summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CREDITS18
-rw-r--r--Documentation/ABI/testing/sysfs-bus-mdio20
-rw-r--r--Documentation/ABI/testing/sysfs-class-net199
-rw-r--r--Documentation/ABI/testing/sysfs-class-net-mesh9
-rw-r--r--Documentation/ABI/testing/sysfs-class-rc111
-rw-r--r--Documentation/ABI/testing/sysfs-firmware-ofw28
-rw-r--r--Documentation/ABI/testing/sysfs-fs-f2fs12
-rw-r--r--Documentation/ABI/testing/sysfs-module1
-rw-r--r--Documentation/ABI/testing/sysfs-ptp20
-rw-r--r--Documentation/DocBook/80211.tmpl2
-rw-r--r--Documentation/DocBook/kernel-hacking.tmpl10
-rw-r--r--Documentation/DocBook/media/dvb/demux.xml23
-rw-r--r--Documentation/DocBook/media/dvb/dvbapi.xml4
-rw-r--r--Documentation/DocBook/media/dvb/dvbproperty.xml2
-rw-r--r--Documentation/DocBook/media/dvb/frontend.xml8
-rw-r--r--Documentation/DocBook/media/v4l/common.xml412
-rw-r--r--Documentation/DocBook/media/v4l/compat.xml15
-rw-r--r--Documentation/DocBook/media/v4l/controls.xml176
-rw-r--r--Documentation/DocBook/media/v4l/dev-osd.xml22
-rw-r--r--Documentation/DocBook/media/v4l/dev-sdr.xml110
-rw-r--r--Documentation/DocBook/media/v4l/io.xml189
-rw-r--r--Documentation/DocBook/media/v4l/pixfmt-nv16m.xml9
-rw-r--r--Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml513
-rw-r--r--Documentation/DocBook/media/v4l/pixfmt-sdr-cu08.xml44
-rw-r--r--Documentation/DocBook/media/v4l/pixfmt-sdr-cu16le.xml46
-rw-r--r--Documentation/DocBook/media/v4l/pixfmt.xml34
-rw-r--r--Documentation/DocBook/media/v4l/remote_controllers.xml143
-rw-r--r--Documentation/DocBook/media/v4l/v4l2.xml26
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml8
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-edid.xml (renamed from Documentation/DocBook/media/v4l/vidioc-subdev-g-edid.xml)36
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml7
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-fmt.xml7
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-frequency.xml5
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-modulator.xml6
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-tuner.xml15
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-querycap.xml6
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml8
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-streamon.xml28
-rw-r--r--Documentation/DocBook/media_api.tmpl90
-rw-r--r--Documentation/SubmittingPatches50
-rw-r--r--Documentation/arm/Marvell/README12
-rw-r--r--Documentation/clk.txt34
-rw-r--r--Documentation/cpu-hotplug.txt45
-rw-r--r--Documentation/device-mapper/era.txt108
-rw-r--r--Documentation/devices.txt8
-rw-r--r--Documentation/devicetree/bindings/arm/armada-375.txt9
-rw-r--r--Documentation/devicetree/bindings/arm/armada-38x.txt10
-rw-r--r--Documentation/devicetree/bindings/arm/bcm/bcm21664.txt15
-rw-r--r--Documentation/devicetree/bindings/arm/bcm/kona-resetmgr.txt14
-rw-r--r--Documentation/devicetree/bindings/arm/bcm4708.txt8
-rw-r--r--Documentation/devicetree/bindings/arm/cpus.txt25
-rw-r--r--Documentation/devicetree/bindings/arm/gic.txt6
-rw-r--r--Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt14
-rw-r--r--Documentation/devicetree/bindings/arm/keystone/keystone.txt10
-rw-r--r--Documentation/devicetree/bindings/arm/mrvl/feroceon.txt16
-rw-r--r--Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt30
-rw-r--r--Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt35
-rw-r--r--Documentation/devicetree/bindings/arm/mvebu-system-controller.txt3
-rw-r--r--Documentation/devicetree/bindings/arm/omap/crossbar.txt27
-rw-r--r--Documentation/devicetree/bindings/arm/omap/dmm.txt22
-rw-r--r--Documentation/devicetree/bindings/arm/omap/omap.txt6
-rw-r--r--Documentation/devicetree/bindings/arm/pmu.txt10
-rw-r--r--Documentation/devicetree/bindings/arm/rockchip/pmu.txt16
-rw-r--r--Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt30
-rw-r--r--Documentation/devicetree/bindings/arm/samsung/pmu.txt15
-rw-r--r--Documentation/devicetree/bindings/ata/exynos-sata-phy.txt14
-rw-r--r--Documentation/devicetree/bindings/ata/exynos-sata.txt31
-rw-r--r--Documentation/devicetree/bindings/bus/imx-weim.txt28
-rw-r--r--Documentation/devicetree/bindings/clock/altr_socfpga.txt5
-rw-r--r--Documentation/devicetree/bindings/clock/arm-integrator.txt34
-rw-r--r--Documentation/devicetree/bindings/clock/axi-clkgen.txt2
-rw-r--r--Documentation/devicetree/bindings/clock/clock-bindings.txt17
-rw-r--r--Documentation/devicetree/bindings/clock/exynos4-clock.txt259
-rw-r--r--Documentation/devicetree/bindings/clock/exynos5250-clock.txt163
-rw-r--r--Documentation/devicetree/bindings/clock/exynos5420-clock.txt184
-rw-r--r--Documentation/devicetree/bindings/clock/exynos5440-clock.txt45
-rw-r--r--Documentation/devicetree/bindings/clock/hi3620-clock.txt1
-rw-r--r--Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt48
-rw-r--r--Documentation/devicetree/bindings/clock/mvebu-core-clock.txt14
-rw-r--r--Documentation/devicetree/bindings/clock/mvebu-corediv-clock.txt5
-rw-r--r--Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt65
-rw-r--r--Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt29
-rw-r--r--Documentation/devicetree/bindings/clock/st/st,clkgen-divmux.txt49
-rw-r--r--Documentation/devicetree/bindings/clock/st/st,clkgen-mux.txt36
-rw-r--r--Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt48
-rw-r--r--Documentation/devicetree/bindings/clock/st/st,clkgen-prediv.txt36
-rw-r--r--Documentation/devicetree/bindings/clock/st/st,clkgen-vcc.txt53
-rw-r--r--Documentation/devicetree/bindings/clock/st/st,clkgen.txt83
-rw-r--r--Documentation/devicetree/bindings/clock/st/st,quadfs.txt45
-rw-r--r--Documentation/devicetree/bindings/clock/sunxi.txt102
-rw-r--r--Documentation/devicetree/bindings/clock/zynq-7000.txt4
-rw-r--r--Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt17
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-davinci.txt25
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-zevio.txt16
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio.txt60
-rw-r--r--Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt60
-rw-r--r--Documentation/devicetree/bindings/i2c/trivial-devices.txt1
-rw-r--r--Documentation/devicetree/bindings/iio/adc/at91_adc.txt (renamed from Documentation/devicetree/bindings/arm/atmel-adc.txt)33
-rw-r--r--Documentation/devicetree/bindings/iio/adc/twl4030-madc.txt24
-rw-r--r--Documentation/devicetree/bindings/input/clps711x-keypad.txt27
-rw-r--r--Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt89
-rw-r--r--Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt46
-rw-r--r--Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt22
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt55
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt30
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt41
-rw-r--r--Documentation/devicetree/bindings/iommu/arm,smmu.txt6
-rw-r--r--Documentation/devicetree/bindings/iommu/ti,omap-iommu.txt26
-rw-r--r--Documentation/devicetree/bindings/media/img-ir-rev1.txt34
-rw-r--r--Documentation/devicetree/bindings/media/samsung-fimc.txt44
-rw-r--r--Documentation/devicetree/bindings/media/samsung-s5c73m3.txt97
-rw-r--r--Documentation/devicetree/bindings/media/samsung-s5k6a3.txt33
-rw-r--r--Documentation/devicetree/bindings/mfd/arizona.txt23
-rw-r--r--Documentation/devicetree/bindings/mfd/bcm590xx.txt37
-rw-r--r--Documentation/devicetree/bindings/mfd/da9055.txt72
-rw-r--r--Documentation/devicetree/bindings/mfd/omap-usb-host.txt23
-rw-r--r--Documentation/devicetree/bindings/mfd/omap-usb-tll.txt10
-rw-r--r--Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt96
-rw-r--r--Documentation/devicetree/bindings/mfd/s2mps11.txt24
-rw-r--r--Documentation/devicetree/bindings/mmc/socfpga-dw-mshc.txt23
-rw-r--r--Documentation/devicetree/bindings/mtd/nand.txt14
-rw-r--r--Documentation/devicetree/bindings/mtd/st-fsm.txt26
-rw-r--r--Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt6
-rw-r--r--Documentation/devicetree/bindings/net/altera_tse.txt114
-rw-r--r--Documentation/devicetree/bindings/net/arc_emac.txt11
-rw-r--r--Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt121
-rw-r--r--Documentation/devicetree/bindings/net/can/sja1000.txt4
-rw-r--r--Documentation/devicetree/bindings/net/cavium-mix.txt7
-rw-r--r--Documentation/devicetree/bindings/net/cavium-pip.txt7
-rw-r--r--Documentation/devicetree/bindings/net/cdns-emac.txt6
-rw-r--r--Documentation/devicetree/bindings/net/cpsw.txt5
-rw-r--r--Documentation/devicetree/bindings/net/davicom-dm9000.txt2
-rw-r--r--Documentation/devicetree/bindings/net/davinci_emac.txt3
-rw-r--r--Documentation/devicetree/bindings/net/ethernet.txt25
-rw-r--r--Documentation/devicetree/bindings/net/fsl-fec.txt5
-rw-r--r--Documentation/devicetree/bindings/net/fsl-tsec-phy.txt13
-rw-r--r--Documentation/devicetree/bindings/net/lpc-eth.txt5
-rw-r--r--Documentation/devicetree/bindings/net/macb.txt6
-rw-r--r--Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt6
-rw-r--r--Documentation/devicetree/bindings/net/marvell-orion-net.txt4
-rw-r--r--Documentation/devicetree/bindings/net/micrel-ks8851.txt1
-rw-r--r--Documentation/devicetree/bindings/net/micrel.txt18
-rw-r--r--Documentation/devicetree/bindings/net/nfc/trf7970a.txt34
-rw-r--r--Documentation/devicetree/bindings/net/phy.txt10
-rw-r--r--Documentation/devicetree/bindings/net/samsung-sxgbe.txt52
-rw-r--r--Documentation/devicetree/bindings/net/sh_eth.txt55
-rw-r--r--Documentation/devicetree/bindings/net/smsc-lan91c111.txt3
-rw-r--r--Documentation/devicetree/bindings/net/smsc911x.txt5
-rw-r--r--Documentation/devicetree/bindings/net/socfpga-dwmac.txt27
-rw-r--r--Documentation/devicetree/bindings/net/stmmac.txt13
-rw-r--r--Documentation/devicetree/bindings/net/wireless/ti,wl1251.txt39
-rw-r--r--Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt8
-rw-r--r--Documentation/devicetree/bindings/phy/samsung-phy.txt40
-rw-r--r--Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt5
-rw-r--r--Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt16
-rw-r--r--Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt35
-rw-r--r--Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt133
-rw-r--r--Documentation/devicetree/bindings/reset/sirf,rstc.txt42
-rw-r--r--Documentation/devicetree/bindings/reset/st,sti-powerdown.txt47
-rw-r--r--Documentation/devicetree/bindings/reset/st,sti-softreset.txt46
-rw-r--r--Documentation/devicetree/bindings/rtc/sunxi-rtc.txt4
-rw-r--r--Documentation/devicetree/bindings/serial/atmel-usart.txt3
-rw-r--r--Documentation/devicetree/bindings/usb/atmel-usb.txt4
-rw-r--r--Documentation/devicetree/bindings/usb/ehci-omap.txt2
-rw-r--r--Documentation/devicetree/bindings/usb/ohci-omap3.txt2
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt24
-rw-r--r--Documentation/devicetree/bindings/video/analog-tv-connector.txt25
-rw-r--r--Documentation/devicetree/bindings/video/dvi-connector.txt35
-rw-r--r--Documentation/devicetree/bindings/video/fsl,imx-fb.txt4
-rw-r--r--Documentation/devicetree/bindings/video/hdmi-connector.txt28
-rw-r--r--Documentation/devicetree/bindings/video/panel-dsi-cm.txt29
-rw-r--r--Documentation/devicetree/bindings/video/sony,acx565akm.txt30
-rw-r--r--Documentation/devicetree/bindings/video/ti,omap-dss.txt211
-rw-r--r--Documentation/devicetree/bindings/video/ti,omap2-dss.txt54
-rw-r--r--Documentation/devicetree/bindings/video/ti,omap3-dss.txt83
-rw-r--r--Documentation/devicetree/bindings/video/ti,omap4-dss.txt111
-rw-r--r--Documentation/devicetree/bindings/video/ti,tfp410.txt41
-rw-r--r--Documentation/devicetree/bindings/video/ti,tpd12s015.txt44
-rw-r--r--Documentation/devicetree/bindings/watchdog/marvel.txt11
-rwxr-xr-xDocumentation/dvb/get_dvb_firmware22
-rw-r--r--Documentation/dvb/it9137.txt9
-rw-r--r--Documentation/edac.txt2
-rw-r--r--Documentation/filesystems/Locking6
-rw-r--r--Documentation/filesystems/autofs4-mount-control.txt2
-rw-r--r--Documentation/filesystems/f2fs.txt29
-rw-r--r--Documentation/filesystems/hfsplus.txt2
-rw-r--r--Documentation/filesystems/nilfs2.txt12
-rw-r--r--Documentation/filesystems/ntfs.txt2
-rw-r--r--Documentation/filesystems/porting6
-rw-r--r--Documentation/filesystems/vfs.txt16
-rw-r--r--Documentation/futex-requeue-pi.txt2
-rw-r--r--Documentation/gpio/consumer.txt1
-rw-r--r--Documentation/gpio/driver.txt35
-rw-r--r--Documentation/hid/hid-transport.txt317
-rw-r--r--Documentation/hid/uhid.txt11
-rw-r--r--Documentation/hwmon/it8710
-rw-r--r--Documentation/hwmon/k10temp6
-rw-r--r--Documentation/input/multi-touch-protocol.txt2
-rw-r--r--Documentation/ja_JP/SubmittingPatches9
-rw-r--r--Documentation/kmemcheck.txt2
-rw-r--r--Documentation/kmemleak.txt23
-rw-r--r--Documentation/memory-barriers.txt2
-rw-r--r--Documentation/module-signing.txt13
-rw-r--r--Documentation/networking/altera_tse.txt263
-rw-r--r--Documentation/networking/bonding.txt96
-rw-r--r--Documentation/networking/can.txt2
-rw-r--r--Documentation/networking/filter.txt125
-rw-r--r--Documentation/networking/gianfar.txt30
-rw-r--r--Documentation/networking/igb.txt48
-rw-r--r--Documentation/networking/phy.txt11
-rw-r--r--Documentation/networking/pktgen.txt24
-rw-r--r--Documentation/networking/rxrpc.txt81
-rw-r--r--Documentation/networking/spider_net.txt2
-rw-r--r--Documentation/networking/tcp.txt2
-rw-r--r--Documentation/networking/timestamping.txt6
-rw-r--r--Documentation/oops-tracing.txt3
-rw-r--r--Documentation/power/devices.txt2
-rw-r--r--Documentation/ptp/testptp.c87
-rw-r--r--Documentation/security/Smack.txt2
-rw-r--r--Documentation/sysctl/kernel.txt2
-rw-r--r--Documentation/sysctl/vm.txt33
-rw-r--r--Documentation/trace/ftrace-design.txt5
-rw-r--r--Documentation/trace/ring-buffer-design.txt2
-rw-r--r--Documentation/usb/WUSB-Design-overview.txt2
-rw-r--r--Documentation/video4linux/CARDLIST.bttv1
-rw-r--r--Documentation/video4linux/CARDLIST.cx238855
-rw-r--r--Documentation/video4linux/CARDLIST.em28xx6
-rw-r--r--Documentation/video4linux/fimc.txt5
-rw-r--r--Documentation/video4linux/gspca.txt1
-rw-r--r--Documentation/video4linux/v4l2-framework.txt5
-rw-r--r--Documentation/video4linux/v4l2-pci-skeleton.c913
-rw-r--r--Documentation/virtual/kvm/api.txt49
-rw-r--r--Documentation/virtual/kvm/devices/s390_flic.txt91
-rw-r--r--Documentation/vm/unevictable-lru.txt2
-rw-r--r--Documentation/zh_CN/SubmittingPatches8
-rw-r--r--MAINTAINERS198
-rw-r--r--Makefile7
-rw-r--r--arch/Kconfig6
-rw-r--r--arch/arc/Kconfig1
-rw-r--r--arch/arm/Kconfig57
-rw-r--r--arch/arm/Kconfig.debug29
-rw-r--r--arch/arm/Makefile10
-rw-r--r--arch/arm/boot/dts/Makefile144
-rw-r--r--arch/arm/boot/dts/am335x-evm.dts60
-rw-r--r--arch/arm/boot/dts/am335x-evmsk.dts56
-rw-r--r--arch/arm/boot/dts/am33xx.dtsi17
-rw-r--r--arch/arm/boot/dts/am3517-craneboard.dts174
-rw-r--r--arch/arm/boot/dts/am4372.dtsi46
-rw-r--r--arch/arm/boot/dts/am437x-gp-evm.dts127
-rw-r--r--arch/arm/boot/dts/am43x-epos-evm.dts183
-rw-r--r--arch/arm/boot/dts/armada-370-db.dts56
-rw-r--r--arch/arm/boot/dts/armada-370-mirabox.dts7
-rw-r--r--arch/arm/boot/dts/armada-370-rd.dts6
-rw-r--r--arch/arm/boot/dts/armada-370-xp.dtsi8
-rw-r--r--arch/arm/boot/dts/armada-370.dtsi33
-rw-r--r--arch/arm/boot/dts/armada-375-db.dts130
-rw-r--r--arch/arm/boot/dts/armada-375.dtsi464
-rw-r--r--arch/arm/boot/dts/armada-380.dtsi117
-rw-r--r--arch/arm/boot/dts/armada-385-db.dts122
-rw-r--r--arch/arm/boot/dts/armada-385-rd.dts94
-rw-r--r--arch/arm/boot/dts/armada-385.dtsi149
-rw-r--r--arch/arm/boot/dts/armada-38x.dtsi376
-rw-r--r--arch/arm/boot/dts/armada-xp-axpwifiap.dts6
-rw-r--r--arch/arm/boot/dts/armada-xp-db.dts13
-rw-r--r--arch/arm/boot/dts/armada-xp-gp.dts22
-rw-r--r--arch/arm/boot/dts/armada-xp-matrix.dts7
-rw-r--r--arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts12
-rw-r--r--arch/arm/boot/dts/armada-xp.dtsi6
-rw-r--r--arch/arm/boot/dts/at91-ariag25.dts1
-rw-r--r--arch/arm/boot/dts/at91-cosino.dtsi1
-rw-r--r--arch/arm/boot/dts/at91-cosino_mega2560.dts1
-rw-r--r--arch/arm/boot/dts/at91sam9260.dtsi11
-rw-r--r--arch/arm/boot/dts/at91sam9261.dtsi735
-rw-r--r--arch/arm/boot/dts/at91sam9261ek.dts211
-rw-r--r--arch/arm/boot/dts/at91sam9g45.dtsi12
-rw-r--r--arch/arm/boot/dts/at91sam9n12.dtsi1
-rw-r--r--arch/arm/boot/dts/at91sam9rl.dtsi802
-rw-r--r--arch/arm/boot/dts/at91sam9rlek.dts157
-rw-r--r--arch/arm/boot/dts/at91sam9x5.dtsi14
-rw-r--r--arch/arm/boot/dts/atlas6.dtsi3
-rw-r--r--arch/arm/boot/dts/bcm11351.dtsi192
-rw-r--r--arch/arm/boot/dts/bcm21664-garnet.dts (renamed from arch/arm/boot/dts/bcm11351-brt.dts)12
-rw-r--r--arch/arm/boot/dts/bcm21664.dtsi292
-rw-r--r--arch/arm/boot/dts/bcm28155-ap.dts51
-rw-r--r--arch/arm/boot/dts/bcm2835.dtsi92
-rw-r--r--arch/arm/boot/dts/bcm4708-netgear-r6250.dts35
-rw-r--r--arch/arm/boot/dts/bcm4708.dtsi34
-rw-r--r--arch/arm/boot/dts/bcm5301x.dtsi95
-rw-r--r--arch/arm/boot/dts/bcm59056.dtsi74
-rw-r--r--arch/arm/boot/dts/dove.dtsi22
-rw-r--r--arch/arm/boot/dts/dra7.dtsi151
-rw-r--r--arch/arm/boot/dts/efm32gg-dk3750.dts2
-rw-r--r--arch/arm/boot/dts/efm32gg.dtsi4
-rw-r--r--arch/arm/boot/dts/exynos4.dtsi78
-rw-r--r--arch/arm/boot/dts/exynos4210-origen.dts2
-rw-r--r--arch/arm/boot/dts/exynos4210-smdkv310.dts2
-rw-r--r--arch/arm/boot/dts/exynos4210-trats.dts2
-rw-r--r--arch/arm/boot/dts/exynos4210-universal_c210.dts2
-rw-r--r--arch/arm/boot/dts/exynos4210.dtsi11
-rw-r--r--arch/arm/boot/dts/exynos4212.dtsi15
-rw-r--r--arch/arm/boot/dts/exynos4412-odroidx.dts4
-rw-r--r--arch/arm/boot/dts/exynos4412-origen.dts6
-rw-r--r--arch/arm/boot/dts/exynos4412-smdk4412.dts2
-rw-r--r--arch/arm/boot/dts/exynos4412-tiny4412.dts2
-rw-r--r--arch/arm/boot/dts/exynos4412-trats2.dts23
-rw-r--r--arch/arm/boot/dts/exynos4412.dtsi16
-rw-r--r--arch/arm/boot/dts/exynos4x12.dtsi60
-rw-r--r--arch/arm/boot/dts/exynos5.dtsi7
-rw-r--r--arch/arm/boot/dts/exynos5250-arndale.dts28
-rw-r--r--arch/arm/boot/dts/exynos5250-smdk5250.dts169
-rw-r--r--arch/arm/boot/dts/exynos5250-snow.dts6
-rw-r--r--arch/arm/boot/dts/exynos5250.dtsi148
-rw-r--r--arch/arm/boot/dts/exynos5420-arndale-octa.dts315
-rw-r--r--arch/arm/boot/dts/exynos5420-smdk5420.dts255
-rw-r--r--arch/arm/boot/dts/exynos5420.dtsi172
-rw-r--r--arch/arm/boot/dts/exynos5440-sd5v1.dts2
-rw-r--r--arch/arm/boot/dts/exynos5440-ssdk5440.dts2
-rw-r--r--arch/arm/boot/dts/exynos5440.dtsi35
-rw-r--r--arch/arm/boot/dts/imx23-evk.dts8
-rw-r--r--arch/arm/boot/dts/imx23-olinuxino.dts5
-rw-r--r--arch/arm/boot/dts/imx23-stmp378x_devb.dts5
-rw-r--r--arch/arm/boot/dts/imx23.dtsi8
-rw-r--r--arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi73
-rw-r--r--arch/arm/boot/dts/imx25-eukrea-mbimxsd25-baseboard.dts174
-rw-r--r--arch/arm/boot/dts/imx25-pinfunc.h494
-rw-r--r--arch/arm/boot/dts/imx25.dtsi18
-rw-r--r--arch/arm/boot/dts/imx27-apf27.dts38
-rw-r--r--arch/arm/boot/dts/imx27-apf27dev.dts149
-rw-r--r--arch/arm/boot/dts/imx27-phytec-phycard-s-rdk.dts77
-rw-r--r--arch/arm/boot/dts/imx27-phytec-phycard-s-som.dts44
-rw-r--r--arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi103
-rw-r--r--arch/arm/boot/dts/imx27-phytec-phycore-rdk.dts178
-rw-r--r--arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi (renamed from arch/arm/boot/dts/imx27-phytec-phycore-som.dts)133
-rw-r--r--arch/arm/boot/dts/imx27-pinfunc.h526
-rw-r--r--arch/arm/boot/dts/imx27.dtsi207
-rw-r--r--arch/arm/boot/dts/imx28-apf28dev.dts29
-rw-r--r--arch/arm/boot/dts/imx28-apx4devkit.dts5
-rw-r--r--arch/arm/boot/dts/imx28-cfa10036.dts2
-rw-r--r--arch/arm/boot/dts/imx28-cfa10037.dts7
-rw-r--r--arch/arm/boot/dts/imx28-cfa10049.dts31
-rw-r--r--arch/arm/boot/dts/imx28-cfa10057.dts7
-rw-r--r--arch/arm/boot/dts/imx28-cfa10058.dts7
-rw-r--r--arch/arm/boot/dts/imx28-duckbill.dts121
-rw-r--r--arch/arm/boot/dts/imx28-eukrea-mbmx283lc.dts71
-rw-r--r--arch/arm/boot/dts/imx28-eukrea-mbmx287lc.dts50
-rw-r--r--arch/arm/boot/dts/imx28-eukrea-mbmx28lc.dtsi326
-rw-r--r--arch/arm/boot/dts/imx28-evk.dts24
-rw-r--r--arch/arm/boot/dts/imx28-m28cu3.dts17
-rw-r--r--arch/arm/boot/dts/imx28-m28evk.dts20
-rw-r--r--arch/arm/boot/dts/imx28-sps1.dts7
-rw-r--r--arch/arm/boot/dts/imx28-tx28.dts24
-rw-r--r--arch/arm/boot/dts/imx28.dtsi65
-rw-r--r--arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi81
-rw-r--r--arch/arm/boot/dts/imx35-eukrea-mbimxsd35-baseboard.dts143
-rw-r--r--arch/arm/boot/dts/imx35.dtsi359
-rw-r--r--arch/arm/boot/dts/imx50-evk.dts119
-rw-r--r--arch/arm/boot/dts/imx50-pinfunc.h923
-rw-r--r--arch/arm/boot/dts/imx50.dtsi478
-rw-r--r--arch/arm/boot/dts/imx51-apf51.dts40
-rw-r--r--arch/arm/boot/dts/imx51-apf51dev.dts102
-rw-r--r--arch/arm/boot/dts/imx51-babbage.dts255
-rw-r--r--arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi93
-rw-r--r--arch/arm/boot/dts/imx51-eukrea-mbimxsd51-baseboard.dts175
-rw-r--r--arch/arm/boot/dts/imx51.dtsi459
-rw-r--r--arch/arm/boot/dts/imx53-ard.dts33
-rw-r--r--arch/arm/boot/dts/imx53-evk.dts126
-rw-r--r--arch/arm/boot/dts/imx53-m53evk.dts232
-rw-r--r--arch/arm/boot/dts/imx53-mba53.dts39
-rw-r--r--arch/arm/boot/dts/imx53-qsb-common.dtsi345
-rw-r--r--arch/arm/boot/dts/imx53-qsb.dts219
-rw-r--r--arch/arm/boot/dts/imx53-qsrb.dts158
-rw-r--r--arch/arm/boot/dts/imx53-smd.dts119
-rw-r--r--arch/arm/boot/dts/imx53-tqma53.dtsi175
-rw-r--r--arch/arm/boot/dts/imx53-tx53-x03x.dts315
-rw-r--r--arch/arm/boot/dts/imx53-tx53-x13x.dts243
-rw-r--r--arch/arm/boot/dts/imx53-tx53.dtsi510
-rw-r--r--arch/arm/boot/dts/imx53-voipac-bsb.dts159
-rw-r--r--arch/arm/boot/dts/imx53-voipac-dmm-668.dtsi277
-rw-r--r--arch/arm/boot/dts/imx53.dtsi663
-rw-r--r--arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts23
-rw-r--r--arch/arm/boot/dts/imx6dl-gw51xx.dts19
-rw-r--r--arch/arm/boot/dts/imx6dl-gw52xx.dts19
-rw-r--r--arch/arm/boot/dts/imx6dl-gw53xx.dts19
-rw-r--r--arch/arm/boot/dts/imx6dl-gw54xx.dts19
-rw-r--r--arch/arm/boot/dts/imx6dl-nitrogen6x.dts21
-rw-r--r--arch/arm/boot/dts/imx6dl-pinfunc.h2
-rw-r--r--arch/arm/boot/dts/imx6dl-sabrelite.dts20
-rw-r--r--arch/arm/boot/dts/imx6dl.dtsi29
-rw-r--r--arch/arm/boot/dts/imx6q-arm2.dts140
-rw-r--r--arch/arm/boot/dts/imx6q-cm-fx6.dts107
-rw-r--r--arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts23
-rw-r--r--arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts372
-rw-r--r--arch/arm/boot/dts/imx6q-gk802.dts171
-rw-r--r--arch/arm/boot/dts/imx6q-gw51xx.dts19
-rw-r--r--arch/arm/boot/dts/imx6q-gw52xx.dts23
-rw-r--r--arch/arm/boot/dts/imx6q-gw53xx.dts23
-rw-r--r--arch/arm/boot/dts/imx6q-gw5400-a.dts546
-rw-r--r--arch/arm/boot/dts/imx6q-gw54xx.dts23
-rw-r--r--arch/arm/boot/dts/imx6q-nitrogen6x.dts25
-rw-r--r--arch/arm/boot/dts/imx6q-phytec-pbab01.dts16
-rw-r--r--arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi167
-rw-r--r--arch/arm/boot/dts/imx6q-pinfunc.h2
-rw-r--r--arch/arm/boot/dts/imx6q-sabrelite.dts178
-rw-r--r--arch/arm/boot/dts/imx6q-sbc6x.dts58
-rw-r--r--arch/arm/boot/dts/imx6q-udoo.dts54
-rw-r--r--arch/arm/boot/dts/imx6q.dtsi23
-rw-r--r--arch/arm/boot/dts/imx6qdl-dfi-fs700-m60.dtsi199
-rw-r--r--arch/arm/boot/dts/imx6qdl-gw51xx.dtsi374
-rw-r--r--arch/arm/boot/dts/imx6qdl-gw52xx.dtsi490
-rw-r--r--arch/arm/boot/dts/imx6qdl-gw53xx.dtsi553
-rw-r--r--arch/arm/boot/dts/imx6qdl-gw54xx.dtsi580
-rw-r--r--arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi422
-rw-r--r--arch/arm/boot/dts/imx6qdl-sabreauto.dtsi378
-rw-r--r--arch/arm/boot/dts/imx6qdl-sabrelite.dtsi423
-rw-r--r--arch/arm/boot/dts/imx6qdl-sabresd.dtsi277
-rw-r--r--arch/arm/boot/dts/imx6qdl-wandboard.dtsi131
-rw-r--r--arch/arm/boot/dts/imx6qdl.dtsi938
-rw-r--r--arch/arm/boot/dts/imx6sl-evk.dts427
-rw-r--r--arch/arm/boot/dts/imx6sl.dtsi385
-rw-r--r--arch/arm/boot/dts/integratorap.dts35
-rw-r--r--arch/arm/boot/dts/integratorcp.dts102
-rw-r--r--arch/arm/boot/dts/k2e-clocks.dtsi78
-rw-r--r--arch/arm/boot/dts/k2e-evm.dts60
-rw-r--r--arch/arm/boot/dts/k2e.dtsi80
-rw-r--r--arch/arm/boot/dts/k2hk-clocks.dtsi426
-rw-r--r--arch/arm/boot/dts/k2hk-evm.dts83
-rw-r--r--arch/arm/boot/dts/k2hk.dtsi46
-rw-r--r--arch/arm/boot/dts/k2l-clocks.dtsi267
-rw-r--r--arch/arm/boot/dts/k2l-evm.dts37
-rw-r--r--arch/arm/boot/dts/k2l.dtsi55
-rw-r--r--arch/arm/boot/dts/keystone-clocks.dtsi427
-rw-r--r--arch/arm/boot/dts/keystone.dtsi102
-rw-r--r--arch/arm/boot/dts/kirkwood-b3.dts204
-rw-r--r--arch/arm/boot/dts/kirkwood-ds109.dts41
-rw-r--r--arch/arm/boot/dts/kirkwood-ds110jv10.dts41
-rw-r--r--arch/arm/boot/dts/kirkwood-ds111.dts44
-rw-r--r--arch/arm/boot/dts/kirkwood-ds112.dts48
-rw-r--r--arch/arm/boot/dts/kirkwood-ds209.dts44
-rw-r--r--arch/arm/boot/dts/kirkwood-ds210.dts46
-rw-r--r--arch/arm/boot/dts/kirkwood-ds212.dts47
-rw-r--r--arch/arm/boot/dts/kirkwood-ds212j.dts41
-rw-r--r--arch/arm/boot/dts/kirkwood-ds409.dts48
-rw-r--r--arch/arm/boot/dts/kirkwood-ds409slim.dts40
-rw-r--r--arch/arm/boot/dts/kirkwood-ds411.dts52
-rw-r--r--arch/arm/boot/dts/kirkwood-ds411j.dts48
-rw-r--r--arch/arm/boot/dts/kirkwood-ds411slim.dts48
-rw-r--r--arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts62
-rw-r--r--arch/arm/boot/dts/kirkwood-rd88f6192.dts112
-rw-r--r--arch/arm/boot/dts/kirkwood-rd88f6281-a0.dts26
-rw-r--r--arch/arm/boot/dts/kirkwood-rd88f6281-a1.dts31
-rw-r--r--arch/arm/boot/dts/kirkwood-rd88f6281.dtsi152
-rw-r--r--arch/arm/boot/dts/kirkwood-rs212.dts48
-rw-r--r--arch/arm/boot/dts/kirkwood-rs409.dts44
-rw-r--r--arch/arm/boot/dts/kirkwood-rs411.dts44
-rw-r--r--arch/arm/boot/dts/kirkwood-synology.dtsi871
-rw-r--r--arch/arm/boot/dts/kirkwood-t5325.dts208
-rw-r--r--arch/arm/boot/dts/kirkwood-ts419-6281.dts20
-rw-r--r--arch/arm/boot/dts/kirkwood-ts419-6282.dts32
-rw-r--r--arch/arm/boot/dts/kirkwood-ts419.dtsi75
-rw-r--r--arch/arm/boot/dts/kirkwood.dtsi24
-rw-r--r--arch/arm/boot/dts/marco.dtsi3
-rw-r--r--arch/arm/boot/dts/omap-gpmc-smsc9221.dtsi58
-rw-r--r--arch/arm/boot/dts/omap2.dtsi31
-rw-r--r--arch/arm/boot/dts/omap2420.dtsi2
-rw-r--r--arch/arm/boot/dts/omap2430.dtsi5
-rw-r--r--arch/arm/boot/dts/omap3-beagle-xm.dts142
-rw-r--r--arch/arm/boot/dts/omap3-beagle.dts139
-rw-r--r--arch/arm/boot/dts/omap3-cm-t3517.dts136
-rw-r--r--arch/arm/boot/dts/omap3-cm-t3530.dts48
-rw-r--r--arch/arm/boot/dts/omap3-cm-t3730.dts57
-rw-r--r--arch/arm/boot/dts/omap3-cm-t3x.dtsi110
-rw-r--r--arch/arm/boot/dts/omap3-cm-t3x30.dtsi74
-rw-r--r--arch/arm/boot/dts/omap3-devkit8000.dts16
-rw-r--r--arch/arm/boot/dts/omap3-gta04.dts51
-rw-r--r--arch/arm/boot/dts/omap3-igep.dtsi1
-rw-r--r--arch/arm/boot/dts/omap3-igep0020.dts58
-rw-r--r--arch/arm/boot/dts/omap3-lilly-a83x.dtsi459
-rw-r--r--arch/arm/boot/dts/omap3-lilly-dbb056.dts170
-rw-r--r--arch/arm/boot/dts/omap3-n900.dts167
-rw-r--r--arch/arm/boot/dts/omap3-overo-alto35-common.dtsi77
-rw-r--r--arch/arm/boot/dts/omap3-overo-alto35.dts22
-rw-r--r--arch/arm/boot/dts/omap3-overo-base.dtsi221
-rw-r--r--arch/arm/boot/dts/omap3-overo-chestnut43-common.dtsi69
-rw-r--r--arch/arm/boot/dts/omap3-overo-chestnut43.dts38
-rw-r--r--arch/arm/boot/dts/omap3-overo-common-peripherals.dtsi94
-rw-r--r--arch/arm/boot/dts/omap3-overo-gallop43-common.dtsi57
-rw-r--r--arch/arm/boot/dts/omap3-overo-gallop43.dts38
-rw-r--r--arch/arm/boot/dts/omap3-overo-palo43-common.dtsi53
-rw-r--r--arch/arm/boot/dts/omap3-overo-palo43.dts38
-rw-r--r--arch/arm/boot/dts/omap3-overo-storm-alto35.dts21
-rw-r--r--arch/arm/boot/dts/omap3-overo-storm-chestnut43.dts38
-rw-r--r--arch/arm/boot/dts/omap3-overo-storm-gallop43.dts38
-rw-r--r--arch/arm/boot/dts/omap3-overo-storm-palo43.dts38
-rw-r--r--arch/arm/boot/dts/omap3-overo-storm-summit.dts30
-rw-r--r--arch/arm/boot/dts/omap3-overo-storm-tobi.dts2
-rw-r--r--arch/arm/boot/dts/omap3-overo-storm.dtsi35
-rw-r--r--arch/arm/boot/dts/omap3-overo-summit-common.dtsi31
-rw-r--r--arch/arm/boot/dts/omap3-overo-summit.dts30
-rw-r--r--arch/arm/boot/dts/omap3-overo-tobi-common.dtsi51
-rw-r--r--arch/arm/boot/dts/omap3-overo-tobi.dts2
-rw-r--r--arch/arm/boot/dts/omap3-overo.dtsi98
-rw-r--r--arch/arm/boot/dts/omap3-sb-t35.dtsi29
-rw-r--r--arch/arm/boot/dts/omap3-sbc-t3517.dts43
-rw-r--r--arch/arm/boot/dts/omap3-sbc-t3530.dts36
-rw-r--r--arch/arm/boot/dts/omap3-sbc-t3730.dts23
-rw-r--r--arch/arm/boot/dts/omap3.dtsi81
-rw-r--r--arch/arm/boot/dts/omap3430-sdp.dts7
-rw-r--r--arch/arm/boot/dts/omap3430es1-clocks.dtsi16
-rw-r--r--arch/arm/boot/dts/omap36xx-am35xx-omap3430es2plus-clocks.dtsi6
-rw-r--r--arch/arm/boot/dts/omap36xx-clocks.dtsi20
-rw-r--r--arch/arm/boot/dts/omap36xx-omap3430es2plus-clocks.dtsi10
-rw-r--r--arch/arm/boot/dts/omap36xx.dtsi28
-rw-r--r--arch/arm/boot/dts/omap3xxx-clocks.dtsi8
-rw-r--r--arch/arm/boot/dts/omap4-duovero-parlor.dts146
-rw-r--r--arch/arm/boot/dts/omap4-duovero.dtsi252
-rw-r--r--arch/arm/boot/dts/omap4-panda-common.dtsi146
-rw-r--r--arch/arm/boot/dts/omap4-sdp.dts146
-rw-r--r--arch/arm/boot/dts/omap4.dtsi151
-rw-r--r--arch/arm/boot/dts/omap443x.dtsi26
-rw-r--r--arch/arm/boot/dts/omap4460.dtsi37
-rw-r--r--arch/arm/boot/dts/omap5-uevm.dts8
-rw-r--r--arch/arm/boot/dts/omap5.dtsi51
-rw-r--r--arch/arm/boot/dts/prima2.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom-msm8660-surf.dts59
-rw-r--r--arch/arm/boot/dts/qcom-msm8660.dtsi87
-rw-r--r--arch/arm/boot/dts/qcom-msm8960-cdp.dts66
-rw-r--r--arch/arm/boot/dts/qcom-msm8960.dtsi135
-rw-r--r--arch/arm/boot/dts/qcom-msm8974.dtsi81
-rw-r--r--arch/arm/boot/dts/r7s72100-genmai-reference.dts13
-rw-r--r--arch/arm/boot/dts/r7s72100.dtsi147
-rw-r--r--arch/arm/boot/dts/r8a7778-bockw-reference.dts4
-rw-r--r--arch/arm/boot/dts/r8a7778.dtsi40
-rw-r--r--arch/arm/boot/dts/r8a7790-lager.dts153
-rw-r--r--arch/arm/boot/dts/r8a7790.dtsi192
-rw-r--r--arch/arm/boot/dts/r8a7791-koelsch-reference.dts115
-rw-r--r--arch/arm/boot/dts/r8a7791-koelsch.dts274
-rw-r--r--arch/arm/boot/dts/r8a7791.dtsi323
-rw-r--r--arch/arm/boot/dts/rk3066a.dtsi13
-rw-r--r--arch/arm/boot/dts/rk3188.dtsi13
-rw-r--r--arch/arm/boot/dts/rk3xxx.dtsi10
-rw-r--r--arch/arm/boot/dts/sama5d3.dtsi28
-rw-r--r--arch/arm/boot/dts/sama5d3xdm.dtsi6
-rw-r--r--arch/arm/boot/dts/socfpga.dtsi41
-rw-r--r--arch/arm/boot/dts/socfpga_arria5.dtsi11
-rw-r--r--arch/arm/boot/dts/socfpga_arria5_socdk.dts21
-rw-r--r--arch/arm/boot/dts/socfpga_cyclone5.dtsi11
-rw-r--r--arch/arm/boot/dts/socfpga_cyclone5_socdk.dts14
-rw-r--r--arch/arm/boot/dts/socfpga_cyclone5_sockit.dts17
-rw-r--r--arch/arm/boot/dts/socfpga_vt.dts16
-rw-r--r--arch/arm/boot/dts/ste-dbx5x0.dtsi12
-rw-r--r--arch/arm/boot/dts/ste-href-ab8500.dtsi428
-rw-r--r--arch/arm/boot/dts/ste-href-ab8505.dtsi240
-rw-r--r--arch/arm/boot/dts/ste-hrefprev60.dtsi1
-rw-r--r--arch/arm/boot/dts/ste-hrefv60plus.dtsi1
-rw-r--r--arch/arm/boot/dts/ste-snowball.dts1
-rw-r--r--arch/arm/boot/dts/ste-u300.dts2
-rw-r--r--arch/arm/boot/dts/stih415-clock.dtsi14
-rw-r--r--arch/arm/boot/dts/stih415-pinctrl.dtsi204
-rw-r--r--arch/arm/boot/dts/stih415.dtsi70
-rw-r--r--arch/arm/boot/dts/stih416-clock.dtsi14
-rw-r--r--arch/arm/boot/dts/stih416-pinctrl.dtsi210
-rw-r--r--arch/arm/boot/dts/stih416.dtsi79
-rw-r--r--arch/arm/boot/dts/stih41x-b2000.dtsi22
-rw-r--r--arch/arm/boot/dts/stih41x-b2020.dtsi14
-rw-r--r--arch/arm/boot/dts/stih41x-b2020x.dtsi28
-rw-r--r--arch/arm/boot/dts/sun4i-a10-a1000.dts55
-rw-r--r--arch/arm/boot/dts/sun4i-a10-cubieboard.dts40
-rw-r--r--arch/arm/boot/dts/sun4i-a10-hackberry.dts56
-rw-r--r--arch/arm/boot/dts/sun4i-a10-inet97fv2.dts69
-rw-r--r--arch/arm/boot/dts/sun4i-a10-mini-xplus.dts31
-rw-r--r--arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts111
-rw-r--r--arch/arm/boot/dts/sun4i-a10-pcduino.dts79
-rw-r--r--arch/arm/boot/dts/sun4i-a10.dtsi213
-rw-r--r--arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts27
-rw-r--r--arch/arm/boot/dts/sun5i-a10s.dtsi158
-rw-r--r--arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts27
-rw-r--r--arch/arm/boot/dts/sun5i-a13-olinuxino.dts27
-rw-r--r--arch/arm/boot/dts/sun5i-a13.dtsi159
-rw-r--r--arch/arm/boot/dts/sun6i-a31-colombus.dts18
-rw-r--r--arch/arm/boot/dts/sun6i-a31.dtsi188
-rw-r--r--arch/arm/boot/dts/sun7i-a20-cubieboard2.dts53
-rw-r--r--arch/arm/boot/dts/sun7i-a20-cubietruck.dts61
-rw-r--r--arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts68
-rw-r--r--arch/arm/boot/dts/sun7i-a20.dtsi302
-rw-r--r--arch/arm/boot/dts/sunxi-common-regulators.dtsi75
-rw-r--r--arch/arm/boot/dts/tegra114-dalmore.dts9
-rw-r--r--arch/arm/boot/dts/tegra114.dtsi8
-rw-r--r--arch/arm/boot/dts/tegra124-venice2.dts312
-rw-r--r--arch/arm/boot/dts/tegra124.dtsi339
-rw-r--r--arch/arm/boot/dts/tegra20-paz00.dts46
-rw-r--r--arch/arm/boot/dts/tegra20-seaboard.dts55
-rw-r--r--arch/arm/boot/dts/tegra20-ventana.dts39
-rw-r--r--arch/arm/boot/dts/tegra20.dtsi4
-rw-r--r--arch/arm/boot/dts/tegra30-cardhu.dtsi7
-rw-r--r--arch/arm/boot/dts/tegra30.dtsi6
-rw-r--r--arch/arm/boot/dts/tps65910.dtsi5
-rw-r--r--arch/arm/boot/dts/twl4030.dtsi7
-rw-r--r--arch/arm/boot/dts/vf610-cosmic.dts29
-rw-r--r--arch/arm/boot/dts/vf610-twr.dts158
-rw-r--r--arch/arm/boot/dts/vf610.dtsi273
-rw-r--r--arch/arm/boot/dts/zynq-7000.dtsi43
-rw-r--r--arch/arm/common/Makefile1
-rw-r--r--arch/arm/common/scoop.c2
-rw-r--r--arch/arm/common/timer-sp.c8
-rw-r--r--arch/arm/configs/ape6evm_defconfig2
-rw-r--r--arch/arm/configs/armadillo800eva_defconfig2
-rw-r--r--arch/arm/configs/at91_dt_defconfig3
-rw-r--r--arch/arm/configs/at91sam9260_9g20_defconfig9
-rw-r--r--arch/arm/configs/at91sam9rl_defconfig10
-rw-r--r--arch/arm/configs/bcm2835_defconfig1
-rw-r--r--arch/arm/configs/bcm_defconfig7
-rw-r--r--arch/arm/configs/bockw_defconfig2
-rw-r--r--arch/arm/configs/clps711x_defconfig3
-rw-r--r--arch/arm/configs/da8xx_omapl_defconfig139
-rw-r--r--arch/arm/configs/davinci_all_defconfig25
-rw-r--r--arch/arm/configs/dove_defconfig3
-rw-r--r--arch/arm/configs/exynos_defconfig2
-rw-r--r--arch/arm/configs/genmai_defconfig8
-rw-r--r--arch/arm/configs/imx_v4_v5_defconfig1
-rw-r--r--arch/arm/configs/imx_v6_v7_defconfig7
-rw-r--r--arch/arm/configs/keystone_defconfig15
-rw-r--r--arch/arm/configs/koelsch_defconfig21
-rw-r--r--arch/arm/configs/kzm9d_defconfig89
-rw-r--r--arch/arm/configs/kzm9g_defconfig2
-rw-r--r--arch/arm/configs/lager_defconfig21
-rw-r--r--arch/arm/configs/mackerel_defconfig2
-rw-r--r--arch/arm/configs/marzen_defconfig2
-rw-r--r--arch/arm/configs/multi_v5_defconfig190
-rw-r--r--arch/arm/configs/multi_v7_defconfig25
-rw-r--r--arch/arm/configs/mvebu_v5_defconfig181
-rw-r--r--arch/arm/configs/mvebu_v7_defconfig (renamed from arch/arm/configs/mvebu_defconfig)10
-rw-r--r--arch/arm/configs/omap2plus_defconfig1
-rw-r--r--arch/arm/configs/shmobile_defconfig129
-rw-r--r--arch/arm/configs/socfpga_defconfig6
-rw-r--r--arch/arm/configs/sunxi_defconfig3
-rw-r--r--arch/arm/configs/tegra_defconfig7
-rw-r--r--arch/arm/firmware/Kconfig3
-rw-r--r--arch/arm/firmware/trusted_foundations.c20
-rw-r--r--arch/arm/include/asm/assembler.h8
-rw-r--r--arch/arm/include/asm/atomic.h44
-rw-r--r--arch/arm/include/asm/cmpxchg.h6
-rw-r--r--arch/arm/include/asm/cputype.h1
-rw-r--r--arch/arm/include/asm/dma-iommu.h12
-rw-r--r--arch/arm/include/asm/firmware.h4
-rw-r--r--arch/arm/include/asm/floppy.h2
-rw-r--r--arch/arm/include/asm/futex.h9
-rw-r--r--arch/arm/include/asm/hardware/cache-feroceon-l2.h (renamed from arch/arm/plat-orion/include/plat/cache-feroceon-l2.h)4
-rw-r--r--arch/arm/include/asm/hw_breakpoint.h1
-rw-r--r--arch/arm/include/asm/hwcap.h3
-rw-r--r--arch/arm/include/asm/jump_label.h1
-rw-r--r--arch/arm/include/asm/kprobes.h17
-rw-r--r--arch/arm/include/asm/kvm_arm.h4
-rw-r--r--arch/arm/include/asm/kvm_asm.h4
-rw-r--r--arch/arm/include/asm/kvm_host.h9
-rw-r--r--arch/arm/include/asm/kvm_mmu.h30
-rw-r--r--arch/arm/include/asm/memory.h41
-rw-r--r--arch/arm/include/asm/pgtable-2level.h1
-rw-r--r--arch/arm/include/asm/pgtable.h7
-rw-r--r--arch/arm/include/asm/pmu.h2
-rw-r--r--arch/arm/include/asm/probes.h43
-rw-r--r--arch/arm/include/asm/ptrace.h14
-rw-r--r--arch/arm/include/asm/smp.h10
-rw-r--r--arch/arm/include/asm/sync_bitops.h1
-rw-r--r--arch/arm/include/asm/system.h7
-rw-r--r--arch/arm/include/asm/thread_info.h5
-rw-r--r--arch/arm/include/asm/timex.h6
-rw-r--r--arch/arm/include/asm/trusted_foundations.h13
-rw-r--r--arch/arm/include/asm/uaccess.h2
-rw-r--r--arch/arm/include/asm/unistd.h1
-rw-r--r--arch/arm/include/asm/uprobes.h45
-rw-r--r--arch/arm/include/asm/xen/page.h15
-rw-r--r--arch/arm/include/debug/samsung.S2
-rw-r--r--arch/arm/include/debug/tegra.S18
-rw-r--r--arch/arm/include/debug/zynq.S3
-rw-r--r--arch/arm/include/uapi/asm/hwcap.h9
-rw-r--r--arch/arm/kernel/Makefile7
-rw-r--r--arch/arm/kernel/armksyms.c2
-rw-r--r--arch/arm/kernel/asm-offsets.c1
-rw-r--r--arch/arm/kernel/bios32.c37
-rw-r--r--arch/arm/kernel/devtree.c40
-rw-r--r--arch/arm/kernel/ftrace.c4
-rw-r--r--arch/arm/kernel/head.S17
-rw-r--r--arch/arm/kernel/hw_breakpoint.c11
-rw-r--r--arch/arm/kernel/kprobes-arm.c806
-rw-r--r--arch/arm/kernel/kprobes-common.c469
-rw-r--r--arch/arm/kernel/kprobes-test-arm.c1
-rw-r--r--arch/arm/kernel/kprobes-test.c12
-rw-r--r--arch/arm/kernel/kprobes-thumb.c1145
-rw-r--r--arch/arm/kernel/kprobes.c25
-rw-r--r--arch/arm/kernel/kprobes.h400
-rw-r--r--arch/arm/kernel/perf_event.c27
-rw-r--r--arch/arm/kernel/perf_event_cpu.c113
-rw-r--r--arch/arm/kernel/perf_event_v7.c717
-rw-r--r--arch/arm/kernel/probes-arm.c734
-rw-r--r--arch/arm/kernel/probes-arm.h73
-rw-r--r--arch/arm/kernel/probes-thumb.c882
-rw-r--r--arch/arm/kernel/probes-thumb.h97
-rw-r--r--arch/arm/kernel/probes.c455
-rw-r--r--arch/arm/kernel/probes.h407
-rw-r--r--arch/arm/kernel/process.c9
-rw-r--r--arch/arm/kernel/setup.c16
-rw-r--r--arch/arm/kernel/signal.c4
-rw-r--r--arch/arm/kernel/sys_oabi-compat.c3
-rw-r--r--arch/arm/kernel/unwind.c137
-rw-r--r--arch/arm/kernel/uprobes-arm.c234
-rw-r--r--arch/arm/kernel/uprobes.c210
-rw-r--r--arch/arm/kernel/uprobes.h35
-rw-r--r--arch/arm/kvm/arm.c7
-rw-r--r--arch/arm/kvm/coproc.c84
-rw-r--r--arch/arm/kvm/coproc.h14
-rw-r--r--arch/arm/kvm/coproc_a15.c2
-rw-r--r--arch/arm/kvm/coproc_a7.c2
-rw-r--r--arch/arm/kvm/guest.c1
-rw-r--r--arch/arm/kvm/interrupts_head.S21
-rw-r--r--arch/arm/kvm/mmu.c110
-rw-r--r--arch/arm/lib/bitops.h5
-rw-r--r--arch/arm/lib/copy_template.S36
-rw-r--r--arch/arm/lib/csumpartialcopygeneric.S96
-rw-r--r--arch/arm/lib/io-readsl.S12
-rw-r--r--arch/arm/lib/io-writesl.S12
-rw-r--r--arch/arm/lib/memmove.S36
-rw-r--r--arch/arm/lib/uaccess.S192
-rw-r--r--arch/arm/mach-at91/Kconfig25
-rw-r--r--arch/arm/mach-at91/Kconfig.non_dt8
-rw-r--r--arch/arm/mach-at91/at91rm9200.c1
-rw-r--r--arch/arm/mach-at91/at91rm9200_devices.c11
-rw-r--r--arch/arm/mach-at91/at91rm9200_time.c1
-rw-r--r--arch/arm/mach-at91/at91sam9260.c1
-rw-r--r--arch/arm/mach-at91/at91sam9260_devices.c14
-rw-r--r--arch/arm/mach-at91/at91sam9261.c26
-rw-r--r--arch/arm/mach-at91/at91sam9261_devices.c5
-rw-r--r--arch/arm/mach-at91/at91sam9263.c2
-rw-r--r--arch/arm/mach-at91/at91sam9263_devices.c5
-rw-r--r--arch/arm/mach-at91/at91sam926x_time.c1
-rw-r--r--arch/arm/mach-at91/at91sam9g45.c2
-rw-r--r--arch/arm/mach-at91/at91sam9g45_devices.c6
-rw-r--r--arch/arm/mach-at91/at91sam9n12.c1
-rw-r--r--arch/arm/mach-at91/at91sam9rl.c25
-rw-r--r--arch/arm/mach-at91/at91sam9rl_devices.c6
-rw-r--r--arch/arm/mach-at91/at91sam9x5.c1
-rw-r--r--arch/arm/mach-at91/at91x40.c2
-rw-r--r--arch/arm/mach-at91/at91x40_time.c1
-rw-r--r--arch/arm/mach-at91/board-dt-sam9.c11
-rw-r--r--arch/arm/mach-at91/board-gsia18s.c1
-rw-r--r--arch/arm/mach-at91/board-pcontrol-g20.c1
-rw-r--r--arch/arm/mach-at91/board-stamp9g20.c1
-rw-r--r--arch/arm/mach-at91/include/mach/at91x40.h2
-rw-r--r--arch/arm/mach-at91/pm.c1
-rw-r--r--arch/arm/mach-at91/sam9_smc.c3
-rw-r--r--arch/arm/mach-at91/setup.c2
-rw-r--r--arch/arm/mach-bcm/Kconfig47
-rw-r--r--arch/arm/mach-bcm/Makefile8
-rw-r--r--arch/arm/mach-bcm/bcm_5301x.c61
-rw-r--r--arch/arm/mach-bcm/board_bcm21664.c78
-rw-r--r--arch/arm/mach-bcm/board_bcm281xx.c83
-rw-r--r--arch/arm/mach-bcm/board_bcm2835.c (renamed from arch/arm/mach-bcm2835/bcm2835.c)0
-rw-r--r--arch/arm/mach-bcm/kona.c64
-rw-r--r--arch/arm/mach-bcm/kona.h7
-rw-r--r--arch/arm/mach-bcm2835/Kconfig15
-rw-r--r--arch/arm/mach-bcm2835/Makefile1
-rw-r--r--arch/arm/mach-berlin/Kconfig4
-rw-r--r--arch/arm/mach-clps711x/Kconfig14
-rw-r--r--arch/arm/mach-clps711x/board-autcpu12.c4
-rw-r--r--arch/arm/mach-clps711x/board-cdb89712.c2
-rw-r--r--arch/arm/mach-clps711x/board-clep7312.c2
-rw-r--r--arch/arm/mach-clps711x/board-edb7211.c2
-rw-r--r--arch/arm/mach-clps711x/board-p720t.c2
-rw-r--r--arch/arm/mach-clps711x/common.c201
-rw-r--r--arch/arm/mach-clps711x/common.h5
-rw-r--r--arch/arm/mach-clps711x/include/mach/clps711x.h16
-rw-r--r--arch/arm/mach-clps711x/include/mach/hardware.h17
-rw-r--r--arch/arm/mach-clps711x/include/mach/timex.h2
-rw-r--r--arch/arm/mach-cns3xxx/Kconfig3
-rw-r--r--arch/arm/mach-cns3xxx/cns3420vb.c1
-rw-r--r--arch/arm/mach-cns3xxx/core.c35
-rw-r--r--arch/arm/mach-cns3xxx/pcie.c105
-rw-r--r--arch/arm/mach-davinci/Kconfig17
-rw-r--r--arch/arm/mach-davinci/Makefile2
-rw-r--r--arch/arm/mach-davinci/Makefile.boot20
-rw-r--r--arch/arm/mach-davinci/aemif.c107
-rw-r--r--arch/arm/mach-davinci/board-da830-evm.c3
-rw-r--r--arch/arm/mach-davinci/board-da850-evm.c3
-rw-r--r--arch/arm/mach-davinci/board-dm644x-evm.c16
-rw-r--r--arch/arm/mach-davinci/board-dm646x-evm.c3
-rw-r--r--arch/arm/mach-davinci/board-mityomapl138.c4
-rw-r--r--arch/arm/mach-davinci/board-tnetv107x-evm.c287
-rw-r--r--arch/arm/mach-davinci/davinci.h2
-rw-r--r--arch/arm/mach-davinci/devices-tnetv107x.c434
-rw-r--r--arch/arm/mach-davinci/devices.c17
-rw-r--r--arch/arm/mach-davinci/dm355.c8
-rw-r--r--arch/arm/mach-davinci/dm365.c8
-rw-r--r--arch/arm/mach-davinci/dm644x.c8
-rw-r--r--arch/arm/mach-davinci/dm646x.c8
-rw-r--r--arch/arm/mach-davinci/include/mach/cputype.h8
-rw-r--r--arch/arm/mach-davinci/include/mach/irqs.h97
-rw-r--r--arch/arm/mach-davinci/include/mach/mux.h269
-rw-r--r--arch/arm/mach-davinci/include/mach/psc.h47
-rw-r--r--arch/arm/mach-davinci/include/mach/serial.h8
-rw-r--r--arch/arm/mach-davinci/include/mach/timex.h22
-rw-r--r--arch/arm/mach-davinci/include/mach/tnetv107x.h61
-rw-r--r--arch/arm/mach-davinci/include/mach/uncompress.h6
-rw-r--r--arch/arm/mach-davinci/tnetv107x.c766
-rw-r--r--arch/arm/mach-dove/Kconfig12
-rw-r--r--arch/arm/mach-dove/Makefile1
-rw-r--r--arch/arm/mach-dove/include/mach/bridge-regs.h1
-rw-r--r--arch/arm/mach-dove/include/mach/timex.h9
-rw-r--r--arch/arm/mach-ebsa110/core.c2
-rw-r--r--arch/arm/mach-ebsa110/include/mach/timex.h19
-rw-r--r--arch/arm/mach-efm32/include/mach/entry-macro.S4
-rw-r--r--arch/arm/mach-efm32/include/mach/timex.h3
-rw-r--r--arch/arm/mach-ep93xx/core.c3
-rw-r--r--arch/arm/mach-ep93xx/include/mach/timex.h5
-rw-r--r--arch/arm/mach-exynos/Kconfig16
-rw-r--r--arch/arm/mach-exynos/Makefile9
-rw-r--r--arch/arm/mach-exynos/common.h17
-rw-r--r--arch/arm/mach-exynos/cpuidle.c4
-rw-r--r--arch/arm/mach-exynos/exynos.c (renamed from arch/arm/mach-exynos/common.c)237
-rw-r--r--arch/arm/mach-exynos/include/mach/hardware.h18
-rw-r--r--arch/arm/mach-exynos/include/mach/pm-core.h75
-rw-r--r--arch/arm/mach-exynos/include/mach/timex.h29
-rw-r--r--arch/arm/mach-exynos/include/mach/uncompress.h48
-rw-r--r--arch/arm/mach-exynos/mach-exynos4-dt.c59
-rw-r--r--arch/arm/mach-exynos/mach-exynos5-dt.c81
-rw-r--r--arch/arm/mach-exynos/mfc.h16
-rw-r--r--arch/arm/mach-exynos/platsmp.c2
-rw-r--r--arch/arm/mach-exynos/pm.c316
-rw-r--r--arch/arm/mach-exynos/pm_domains.c2
-rw-r--r--arch/arm/mach-exynos/regs-pmu.h3
-rw-r--r--arch/arm/mach-exynos/sleep.S85
-rw-r--r--arch/arm/mach-footbridge/Kconfig2
-rw-r--r--arch/arm/mach-footbridge/Makefile3
-rw-r--r--arch/arm/mach-footbridge/cats-hw.c2
-rw-r--r--arch/arm/mach-footbridge/dc21285-timer.c6
-rw-r--r--arch/arm/mach-footbridge/dc21285.c10
-rw-r--r--arch/arm/mach-footbridge/include/mach/timex.h18
-rw-r--r--arch/arm/mach-footbridge/isa-timer.c2
-rw-r--r--arch/arm/mach-gemini/idle.c2
-rw-r--r--arch/arm/mach-gemini/include/mach/timex.h13
-rw-r--r--arch/arm/mach-highbank/Kconfig7
-rw-r--r--arch/arm/mach-hisi/Kconfig6
-rw-r--r--arch/arm/mach-hisi/Makefile3
-rw-r--r--arch/arm/mach-hisi/hotplug.c2
-rw-r--r--arch/arm/mach-imx/Kconfig57
-rw-r--r--arch/arm/mach-imx/Makefile9
-rw-r--r--arch/arm/mach-imx/clk-imx21.c1
-rw-r--r--arch/arm/mach-imx/clk-imx25.c8
-rw-r--r--arch/arm/mach-imx/clk-imx27.c1
-rw-r--r--arch/arm/mach-imx/clk-imx51-imx53.c2
-rw-r--r--arch/arm/mach-imx/clk-imx6q.c7
-rw-r--r--arch/arm/mach-imx/clk-imx6sl.c159
-rw-r--r--arch/arm/mach-imx/clk-vf610.c36
-rw-r--r--arch/arm/mach-imx/common.h17
-rw-r--r--arch/arm/mach-imx/cpuidle-imx6q.c4
-rw-r--r--arch/arm/mach-imx/cpuidle-imx6sl.c57
-rw-r--r--arch/arm/mach-imx/cpuidle.h5
-rw-r--r--arch/arm/mach-imx/devices-imx25.h4
-rw-r--r--arch/arm/mach-imx/devices-imx51.h4
-rw-r--r--arch/arm/mach-imx/devices/Kconfig3
-rw-r--r--arch/arm/mach-imx/devices/Makefile1
-rw-r--r--arch/arm/mach-imx/devices/devices-common.h9
-rw-r--r--arch/arm/mach-imx/devices/platform-mxc_pwm.c69
-rw-r--r--arch/arm/mach-imx/hardware.h4
-rw-r--r--arch/arm/mach-imx/headsmp.S40
-rw-r--r--arch/arm/mach-imx/mach-imx6q.c98
-rw-r--r--arch/arm/mach-imx/mach-imx6sl.c6
-rw-r--r--arch/arm/mach-imx/mach-mx27ads.c55
-rw-r--r--arch/arm/mach-imx/pm-imx6.c551
-rw-r--r--arch/arm/mach-imx/pm-imx6q.c240
-rw-r--r--arch/arm/mach-imx/suspend-imx6.S361
-rw-r--r--arch/arm/mach-imx/time.c12
-rw-r--r--arch/arm/mach-integrator/Kconfig11
-rw-r--r--arch/arm/mach-integrator/core.c4
-rw-r--r--arch/arm/mach-integrator/hardware.h (renamed from arch/arm/mach-integrator/include/mach/platform.h)110
-rw-r--r--arch/arm/mach-integrator/impd1.c85
-rw-r--r--arch/arm/mach-integrator/impd1.h (renamed from arch/arm/mach-integrator/include/mach/impd1.h)4
-rw-r--r--arch/arm/mach-integrator/include/mach/hardware.h45
-rw-r--r--arch/arm/mach-integrator/integrator_ap.c27
-rw-r--r--arch/arm/mach-integrator/integrator_cp.c23
-rw-r--r--arch/arm/mach-integrator/leds.c4
-rw-r--r--arch/arm/mach-integrator/lm.c2
-rw-r--r--arch/arm/mach-integrator/lm.h (renamed from arch/arm/mach-integrator/include/mach/lm.h)0
-rw-r--r--arch/arm/mach-integrator/pci_v3.c4
-rw-r--r--arch/arm/mach-iop13xx/include/mach/timex.h1
-rw-r--r--arch/arm/mach-iop32x/include/mach/timex.h6
-rw-r--r--arch/arm/mach-iop33x/include/mach/timex.h6
-rw-r--r--arch/arm/mach-ixp4xx/common-pci.c39
-rw-r--r--arch/arm/mach-ixp4xx/common.c77
-rw-r--r--arch/arm/mach-ixp4xx/dsmg600-setup.c3
-rw-r--r--arch/arm/mach-ixp4xx/fsg-setup.c6
-rw-r--r--arch/arm/mach-ixp4xx/goramo_mlr.c43
-rw-r--r--arch/arm/mach-ixp4xx/include/mach/io.h3
-rw-r--r--arch/arm/mach-ixp4xx/include/mach/timex.h16
-rw-r--r--arch/arm/mach-ixp4xx/nas100d-setup.c3
-rw-r--r--arch/arm/mach-ixp4xx/nslu2-setup.c6
-rw-r--r--arch/arm/mach-ixp4xx/omixp-setup.c2
-rw-r--r--arch/arm/mach-keystone/Kconfig4
-rw-r--r--arch/arm/mach-keystone/keystone.c2
-rw-r--r--arch/arm/mach-kirkwood/Kconfig7
-rw-r--r--arch/arm/mach-kirkwood/Makefile4
-rw-r--r--arch/arm/mach-kirkwood/board-dt.c113
-rw-r--r--arch/arm/mach-kirkwood/board-mv88f6281gtw_ge.c50
-rw-r--r--arch/arm/mach-kirkwood/common.c3
-rw-r--r--arch/arm/mach-kirkwood/common.h13
-rw-r--r--arch/arm/mach-kirkwood/include/mach/bridge-regs.h3
-rw-r--r--arch/arm/mach-kirkwood/include/mach/timex.h10
-rw-r--r--arch/arm/mach-kirkwood/pm.c9
-rw-r--r--arch/arm/mach-kirkwood/pm.h26
-rw-r--r--arch/arm/mach-ks8695/board-og.c3
-rw-r--r--arch/arm/mach-ks8695/include/mach/timex.h21
-rw-r--r--arch/arm/mach-ks8695/time.c2
-rw-r--r--arch/arm/mach-lpc32xx/common.c1
-rw-r--r--arch/arm/mach-lpc32xx/timer.c2
-rw-r--r--arch/arm/mach-mmp/aspenite.c4
-rw-r--r--arch/arm/mach-mmp/devices.c14
-rw-r--r--arch/arm/mach-mmp/time.c14
-rw-r--r--arch/arm/mach-mmp/ttc_dkb.c18
-rw-r--r--arch/arm/mach-moxart/Kconfig7
-rw-r--r--arch/arm/mach-msm/Kconfig54
-rw-r--r--arch/arm/mach-msm/Makefile8
-rw-r--r--arch/arm/mach-msm/board-dt.c41
-rw-r--r--arch/arm/mach-msm/common.h3
-rw-r--r--arch/arm/mach-msm/dma.c3
-rw-r--r--arch/arm/mach-msm/headsmp.S39
-rw-r--r--arch/arm/mach-msm/hotplug.c74
-rw-r--r--arch/arm/mach-msm/include/mach/timex.h21
-rw-r--r--arch/arm/mach-msm/io.c2
-rw-r--r--arch/arm/mach-msm/platsmp.c161
-rw-r--r--arch/arm/mach-mv78xx0/common.c2
-rw-r--r--arch/arm/mach-mv78xx0/include/mach/bridge-regs.h1
-rw-r--r--arch/arm/mach-mv78xx0/include/mach/timex.h9
-rw-r--r--arch/arm/mach-mvebu/Kconfig86
-rw-r--r--arch/arm/mach-mvebu/Makefile5
-rw-r--r--arch/arm/mach-mvebu/board-t5325.c41
-rw-r--r--arch/arm/mach-mvebu/board-v7.c (renamed from arch/arm/mach-mvebu/armada-370-xp.c)59
-rw-r--r--arch/arm/mach-mvebu/board.h22
-rw-r--r--arch/arm/mach-mvebu/dove.c (renamed from arch/arm/mach-dove/board-dt.c)20
-rw-r--r--arch/arm/mach-mvebu/kirkwood-pm.c76
-rw-r--r--arch/arm/mach-mvebu/kirkwood-pm.h26
-rw-r--r--arch/arm/mach-mvebu/kirkwood.c199
-rw-r--r--arch/arm/mach-mvebu/kirkwood.h22
-rw-r--r--arch/arm/mach-mvebu/mvebu-soc-id.c1
-rw-r--r--arch/arm/mach-mvebu/system-controller.c23
-rw-r--r--arch/arm/mach-mxs/Kconfig4
-rw-r--r--arch/arm/mach-mxs/mach-mxs.c33
-rw-r--r--arch/arm/mach-netx/include/mach/timex.h20
-rw-r--r--arch/arm/mach-netx/time.c13
-rw-r--r--arch/arm/mach-nomadik/Kconfig5
-rw-r--r--arch/arm/mach-nspire/Kconfig5
-rw-r--r--arch/arm/mach-nspire/nspire.c2
-rw-r--r--arch/arm/mach-omap1/board-h2.c3
-rw-r--r--arch/arm/mach-omap1/board-osk.c3
-rw-r--r--arch/arm/mach-omap1/dma.c191
-rw-r--r--arch/arm/mach-omap1/include/mach/timex.h5
-rw-r--r--arch/arm/mach-omap1/pm.c6
-rw-r--r--arch/arm/mach-omap2/Kconfig26
-rw-r--r--arch/arm/mach-omap2/Makefile1
-rw-r--r--arch/arm/mach-omap2/am35xx-emac.c1
-rw-r--r--arch/arm/mach-omap2/board-cm-t35.c16
-rw-r--r--arch/arm/mach-omap2/board-generic.c7
-rw-r--r--arch/arm/mach-omap2/board-omap3pandora.c6
-rw-r--r--arch/arm/mach-omap2/board-rx51-peripherals.c13
-rw-r--r--arch/arm/mach-omap2/cclock3xxx_data.c4
-rw-r--r--arch/arm/mach-omap2/clkt_dpll.c6
-rw-r--r--arch/arm/mach-omap2/clockdomains3xxx_data.c2
-rw-r--r--arch/arm/mach-omap2/cminst44xx.c18
-rw-r--r--arch/arm/mach-omap2/common.h3
-rw-r--r--arch/arm/mach-omap2/devices.c3
-rw-r--r--arch/arm/mach-omap2/display.c167
-rw-r--r--arch/arm/mach-omap2/display.h3
-rw-r--r--arch/arm/mach-omap2/dma.c183
-rw-r--r--arch/arm/mach-omap2/dpll3xxx.c2
-rw-r--r--arch/arm/mach-omap2/dss-common.c224
-rw-r--r--arch/arm/mach-omap2/gpmc-nand.c31
-rw-r--r--arch/arm/mach-omap2/id.c16
-rw-r--r--arch/arm/mach-omap2/include/mach/timex.h5
-rw-r--r--arch/arm/mach-omap2/io.c1
-rw-r--r--arch/arm/mach-omap2/irq.c8
-rw-r--r--arch/arm/mach-omap2/mux.h3
-rw-r--r--arch/arm/mach-omap2/omap-iommu.c5
-rw-r--r--arch/arm/mach-omap2/omap-wakeupgen.c4
-rw-r--r--arch/arm/mach-omap2/omap4-common.c4
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_3xxx_data.c18
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_43xx_data.c1
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_44xx_data.c3
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_54xx_data.c83
-rw-r--r--arch/arm/mach-omap2/pdata-quirks.c120
-rw-r--r--arch/arm/mach-omap2/pm.h2
-rw-r--r--arch/arm/mach-omap2/prminst44xx.c3
-rw-r--r--arch/arm/mach-omap2/soc.h3
-rw-r--r--arch/arm/mach-omap2/timer.c3
-rw-r--r--arch/arm/mach-orion5x/Kconfig1
-rw-r--r--arch/arm/mach-orion5x/dns323-setup.c2
-rw-r--r--arch/arm/mach-orion5x/include/mach/bridge-regs.h1
-rw-r--r--arch/arm/mach-orion5x/include/mach/timex.h11
-rw-r--r--arch/arm/mach-picoxcell/Kconfig5
-rw-r--r--arch/arm/mach-prima2/Kconfig7
-rw-r--r--arch/arm/mach-prima2/common.c11
-rw-r--r--arch/arm/mach-prima2/common.h1
-rw-r--r--arch/arm/mach-prima2/l2x0.c9
-rw-r--r--arch/arm/mach-prima2/platsmp.c6
-rw-r--r--arch/arm/mach-prima2/rstc.c99
-rw-r--r--arch/arm/mach-prima2/rtciobrg.c2
-rw-r--r--arch/arm/mach-pxa/Kconfig23
-rw-r--r--arch/arm/mach-pxa/balloon3.c1
-rw-r--r--arch/arm/mach-pxa/colibri-evalboard.c1
-rw-r--r--arch/arm/mach-pxa/corgi.c40
-rw-r--r--arch/arm/mach-pxa/include/mach/timex.h34
-rw-r--r--arch/arm/mach-qcom/Kconfig33
-rw-r--r--arch/arm/mach-qcom/Makefile5
-rw-r--r--arch/arm/mach-qcom/board.c (renamed from include/linux/mfd/pm8xxx/pm8921.h)26
-rw-r--r--arch/arm/mach-qcom/platsmp.c378
-rw-r--r--arch/arm/mach-qcom/scm-boot.c (renamed from arch/arm/mach-msm/scm-boot.c)0
-rw-r--r--arch/arm/mach-qcom/scm-boot.h (renamed from arch/arm/mach-msm/scm-boot.h)8
-rw-r--r--arch/arm/mach-qcom/scm.c (renamed from arch/arm/mach-msm/scm.c)0
-rw-r--r--arch/arm/mach-qcom/scm.h (renamed from arch/arm/mach-msm/scm.h)0
-rw-r--r--arch/arm/mach-realview/include/mach/memory.h2
-rw-r--r--arch/arm/mach-realview/include/mach/timex.h23
-rw-r--r--arch/arm/mach-rockchip/Kconfig4
-rw-r--r--arch/arm/mach-rockchip/Makefile1
-rw-r--r--arch/arm/mach-rockchip/core.h (renamed from arch/arm/mach-lpc32xx/include/mach/timex.h)20
-rw-r--r--arch/arm/mach-rockchip/headsmp.S (renamed from arch/arm/mach-integrator/include/mach/timex.h)30
-rw-r--r--arch/arm/mach-rockchip/platsmp.c184
-rw-r--r--arch/arm/mach-rockchip/rockchip.c2
-rw-r--r--arch/arm/mach-rpc/dma.c2
-rw-r--r--arch/arm/mach-rpc/include/mach/timex.h17
-rw-r--r--arch/arm/mach-rpc/time.c16
-rw-r--r--arch/arm/mach-s3c24xx/Kconfig6
-rw-r--r--arch/arm/mach-s3c24xx/clock-s3c2410.c3
-rw-r--r--arch/arm/mach-s3c24xx/clock-s3c2412.c3
-rw-r--r--arch/arm/mach-s3c24xx/clock-s3c2440.c2
-rw-r--r--arch/arm/mach-s3c24xx/common.c5
-rw-r--r--arch/arm/mach-s3c24xx/dma-s3c2410.c2
-rw-r--r--arch/arm/mach-s3c24xx/dma-s3c2412.c2
-rw-r--r--arch/arm/mach-s3c24xx/dma-s3c2440.c2
-rw-r--r--arch/arm/mach-s3c24xx/dma-s3c2443.c2
-rw-r--r--arch/arm/mach-s3c24xx/include/mach/debug-macro.S2
-rw-r--r--arch/arm/mach-s3c24xx/include/mach/hardware.h14
-rw-r--r--arch/arm/mach-s3c24xx/include/mach/rtc-core.h (renamed from arch/arm/plat-samsung/include/plat/rtc-core.h)13
-rw-r--r--arch/arm/mach-s3c24xx/include/mach/tick.h15
-rw-r--r--arch/arm/mach-s3c24xx/include/mach/timex.h24
-rw-r--r--arch/arm/mach-s3c24xx/include/mach/uncompress.h57
-rw-r--r--arch/arm/mach-s3c24xx/mach-amlm5900.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-anubis.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-at2440evb.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-bast.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-gta02.c4
-rw-r--r--arch/arm/mach-s3c24xx/mach-h1940.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-jive.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-mini2440.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-n30.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-nexcoder.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-osiris.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-otom.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-qt2410.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-rx1950.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-rx3715.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-s3c2416-dt.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-smdk2410.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-smdk2413.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-smdk2416.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-smdk2440.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-smdk2443.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-tct_hammer.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-vr1000.c2
-rw-r--r--arch/arm/mach-s3c24xx/mach-vstms.c2
-rw-r--r--arch/arm/mach-s3c24xx/pm.c2
-rw-r--r--arch/arm/mach-s3c24xx/s3c2410.c2
-rw-r--r--arch/arm/mach-s3c24xx/s3c2412.c2
-rw-r--r--arch/arm/mach-s3c24xx/s3c2416.c2
-rw-r--r--arch/arm/mach-s3c24xx/s3c2443.c2
-rw-r--r--arch/arm/mach-s3c24xx/s3c244x.c2
-rw-r--r--arch/arm/mach-s3c24xx/sleep-s3c2410.S2
-rw-r--r--arch/arm/mach-s3c24xx/sleep.S2
-rw-r--r--arch/arm/mach-s3c64xx/Kconfig3
-rw-r--r--arch/arm/mach-s3c64xx/common.c2
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/debug-macro.S2
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/pm-core.h2
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/tick.h31
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/timex.h24
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/uncompress.h31
-rw-r--r--arch/arm/mach-s3c64xx/irq-pm.c14
-rw-r--r--arch/arm/mach-s3c64xx/mach-anw6410.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-crag6410-module.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-crag6410.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-hmt.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-mini6410.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-ncp.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-real6410.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-smartq.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-smdk6400.c3
-rw-r--r--arch/arm/mach-s3c64xx/mach-smdk6410.c2
-rw-r--r--arch/arm/mach-s3c64xx/pm.c1
-rw-r--r--arch/arm/mach-s3c64xx/s3c6400.c2
-rw-r--r--arch/arm/mach-s3c64xx/s3c6410.c2
-rw-r--r--arch/arm/mach-s5p64x0/common.c20
-rw-r--r--arch/arm/mach-s5p64x0/common.h5
-rw-r--r--arch/arm/mach-s5p64x0/include/mach/debug-macro.S3
-rw-r--r--arch/arm/mach-s5p64x0/include/mach/pm-core.h2
-rw-r--r--arch/arm/mach-s5p64x0/include/mach/timex.h27
-rw-r--r--arch/arm/mach-s5p64x0/include/mach/uncompress.h34
-rw-r--r--arch/arm/mach-s5p64x0/irq-pm.c8
-rw-r--r--arch/arm/mach-s5p64x0/mach-smdk6440.c2
-rw-r--r--arch/arm/mach-s5p64x0/mach-smdk6450.c2
-rw-r--r--arch/arm/mach-s5p64x0/pm.c1
-rw-r--r--arch/arm/mach-s5pc100/common.c2
-rw-r--r--arch/arm/mach-s5pc100/include/mach/debug-macro.S2
-rw-r--r--arch/arm/mach-s5pc100/include/mach/tick.h31
-rw-r--r--arch/arm/mach-s5pc100/include/mach/timex.h24
-rw-r--r--arch/arm/mach-s5pc100/include/mach/uncompress.h30
-rw-r--r--arch/arm/mach-s5pc100/mach-smdkc100.c2
-rw-r--r--arch/arm/mach-s5pv210/Kconfig1
-rw-r--r--arch/arm/mach-s5pv210/common.c2
-rw-r--r--arch/arm/mach-s5pv210/include/mach/debug-macro.S2
-rw-r--r--arch/arm/mach-s5pv210/include/mach/timex.h29
-rw-r--r--arch/arm/mach-s5pv210/include/mach/uncompress.h28
-rw-r--r--arch/arm/mach-s5pv210/mach-aquila.c2
-rw-r--r--arch/arm/mach-s5pv210/mach-goni.c2
-rw-r--r--arch/arm/mach-s5pv210/mach-smdkc110.c2
-rw-r--r--arch/arm/mach-s5pv210/mach-smdkv210.c2
-rw-r--r--arch/arm/mach-s5pv210/mach-torbreck.c2
-rw-r--r--arch/arm/mach-sa1100/collie.c33
-rw-r--r--arch/arm/mach-sa1100/h3100.c7
-rw-r--r--arch/arm/mach-sa1100/h3600.c7
-rw-r--r--arch/arm/mach-sa1100/h3xxx.c58
-rw-r--r--arch/arm/mach-sa1100/include/mach/collie.h2
-rw-r--r--arch/arm/mach-sa1100/include/mach/h3xxx.h11
-rw-r--r--arch/arm/mach-sa1100/include/mach/timex.h12
-rw-r--r--arch/arm/mach-sa1100/time.c10
-rw-r--r--arch/arm/mach-shmobile/Kconfig26
-rw-r--r--arch/arm/mach-shmobile/Makefile4
-rw-r--r--arch/arm/mach-shmobile/board-armadillo800eva.c2
-rw-r--r--arch/arm/mach-shmobile/board-bockw.c45
-rw-r--r--arch/arm/mach-shmobile/board-genmai.c75
-rw-r--r--arch/arm/mach-shmobile/board-koelsch-reference.c106
-rw-r--r--arch/arm/mach-shmobile/board-koelsch.c305
-rw-r--r--arch/arm/mach-shmobile/board-kzm9d-reference.c48
-rw-r--r--arch/arm/mach-shmobile/board-lager-reference.c109
-rw-r--r--arch/arm/mach-shmobile/board-lager.c492
-rw-r--r--arch/arm/mach-shmobile/clock-r7s72100.c36
-rw-r--r--arch/arm/mach-shmobile/clock-r8a7778.c4
-rw-r--r--arch/arm/mach-shmobile/clock-r8a7779.c27
-rw-r--r--arch/arm/mach-shmobile/clock-r8a7790.c196
-rw-r--r--arch/arm/mach-shmobile/clock-r8a7791.c153
-rw-r--r--arch/arm/mach-shmobile/include/mach/common.h1
-rw-r--r--arch/arm/mach-shmobile/include/mach/head-kzm9g.txt410
-rw-r--r--arch/arm/mach-shmobile/include/mach/pm-rcar.h15
-rw-r--r--arch/arm/mach-shmobile/include/mach/r8a7779.h13
-rw-r--r--arch/arm/mach-shmobile/include/mach/r8a7790.h26
-rw-r--r--arch/arm/mach-shmobile/include/mach/timex.h6
-rw-r--r--arch/arm/mach-shmobile/include/mach/zboot.h3
-rw-r--r--arch/arm/mach-shmobile/include/mach/zboot_macros.h43
-rw-r--r--arch/arm/mach-shmobile/platsmp-apmu.c3
-rw-r--r--arch/arm/mach-shmobile/pm-r8a7779.c131
-rw-r--r--arch/arm/mach-shmobile/pm-r8a7790.c45
-rw-r--r--arch/arm/mach-shmobile/pm-rcar.c141
-rw-r--r--arch/arm/mach-shmobile/setup-emev2.c2
-rw-r--r--arch/arm/mach-shmobile/setup-r8a7790.c90
-rw-r--r--arch/arm/mach-shmobile/setup-rcar-gen2.c2
-rw-r--r--arch/arm/mach-shmobile/smp-r8a7779.c17
-rw-r--r--arch/arm/mach-shmobile/smp-r8a7790.c17
-rw-r--r--arch/arm/mach-socfpga/Kconfig7
-rw-r--r--arch/arm/mach-socfpga/socfpga.c5
-rw-r--r--arch/arm/mach-spear/Kconfig12
-rw-r--r--arch/arm/mach-spear/include/mach/timex.h19
-rw-r--r--arch/arm/mach-spear/time.c2
-rw-r--r--arch/arm/mach-sti/Kconfig7
-rw-r--r--arch/arm/mach-sunxi/Kconfig6
-rw-r--r--arch/arm/mach-sunxi/Makefile2
-rw-r--r--arch/arm/mach-sunxi/headsmp.S9
-rw-r--r--arch/arm/mach-sunxi/platsmp.c2
-rw-r--r--arch/arm/mach-sunxi/sunxi.c4
-rw-r--r--arch/arm/mach-tegra/Kconfig8
-rw-r--r--arch/arm/mach-tegra/Makefile1
-rw-r--r--arch/arm/mach-tegra/cpuidle-tegra114.c7
-rw-r--r--arch/arm/mach-tegra/platsmp.c2
-rw-r--r--arch/arm/mach-tegra/powergate.c2
-rw-r--r--arch/arm/mach-tegra/tegra2_emc.c347
-rw-r--r--arch/arm/mach-tegra/tegra2_emc.h24
-rw-r--r--arch/arm/mach-u300/Kconfig6
-rw-r--r--arch/arm/mach-ux500/Kconfig10
-rw-r--r--arch/arm/mach-ux500/Makefile1
-rw-r--r--arch/arm/mach-ux500/board-mop500-audio.c1
-rw-r--r--arch/arm/mach-ux500/board-mop500-pins.c291
-rw-r--r--arch/arm/mach-ux500/board-mop500.h73
-rw-r--r--arch/arm/mach-ux500/cpu-db8500.c17
-rw-r--r--arch/arm/mach-ux500/cpu.c10
-rw-r--r--arch/arm/mach-ux500/irqs-board-mop500.h55
-rw-r--r--arch/arm/mach-ux500/irqs-db8500.h125
-rw-r--r--arch/arm/mach-ux500/irqs.h49
-rw-r--r--arch/arm/mach-versatile/core.c2
-rw-r--r--arch/arm/mach-versatile/include/mach/timex.h23
-rw-r--r--arch/arm/mach-vexpress/Kconfig5
-rw-r--r--arch/arm/mach-vexpress/Makefile3
-rw-r--r--arch/arm/mach-virt/Kconfig10
-rw-r--r--arch/arm/mach-virt/Makefile5
-rw-r--r--arch/arm/mach-virt/virt.c41
-rw-r--r--arch/arm/mach-vt8500/Kconfig4
-rw-r--r--arch/arm/mach-w90x900/include/mach/timex.h25
-rw-r--r--arch/arm/mach-w90x900/time.c2
-rw-r--r--arch/arm/mach-zynq/Kconfig8
-rw-r--r--arch/arm/mach-zynq/common.c8
-rw-r--r--arch/arm/mach-zynq/common.h2
-rw-r--r--arch/arm/mach-zynq/slcr.c104
-rw-r--r--arch/arm/mm/Kconfig7
-rw-r--r--arch/arm/mm/cache-feroceon-l2.c49
-rw-r--r--arch/arm/mm/cache-tauros2.c29
-rw-r--r--arch/arm/mm/dma-mapping.c147
-rw-r--r--arch/arm/mm/init.c2
-rw-r--r--arch/arm/mm/mmu.c10
-rw-r--r--arch/arm/mm/proc-macros.S19
-rw-r--r--arch/arm/mm/proc-v7-2level.S7
-rw-r--r--arch/arm/mm/proc-v7.S11
-rw-r--r--arch/arm/net/bpf_jit_32.c7
-rw-r--r--arch/arm/plat-iop/time.c2
-rw-r--r--arch/arm/plat-omap/Kconfig3
-rw-r--r--arch/arm/plat-omap/dma.c17
-rw-r--r--arch/arm/plat-omap/include/plat/timex.h33
-rw-r--r--arch/arm/plat-orion/common.c10
-rw-r--r--arch/arm/plat-samsung/Kconfig6
-rw-r--r--arch/arm/plat-samsung/Makefile2
-rw-r--r--arch/arm/plat-samsung/clock.c3
-rw-r--r--arch/arm/plat-samsung/cpu.c7
-rw-r--r--arch/arm/plat-samsung/devs.c14
-rw-r--r--arch/arm/plat-samsung/include/plat/cpu.h11
-rw-r--r--arch/arm/plat-samsung/include/plat/mfc.h3
-rw-r--r--arch/arm/plat-samsung/include/plat/pm-common.h110
-rw-r--r--arch/arm/plat-samsung/include/plat/pm.h80
-rw-r--r--arch/arm/plat-samsung/include/plat/regs-serial.h1
-rw-r--r--arch/arm/plat-samsung/include/plat/uncompress.h175
-rw-r--r--arch/arm/plat-samsung/init.c9
-rw-r--r--arch/arm/plat-samsung/pm-check.c2
-rw-r--r--arch/arm/plat-samsung/pm-common.c75
-rw-r--r--arch/arm/plat-samsung/pm-debug.c97
-rw-r--r--arch/arm/plat-samsung/pm-gpio.c5
-rw-r--r--arch/arm/plat-samsung/pm.c148
-rw-r--r--arch/arm/plat-samsung/s5p-dev-mfc.c17
-rw-r--r--arch/arm/plat-samsung/s5p-dev-uart.c1
-rw-r--r--arch/arm/plat-samsung/s5p-irq-pm.c13
-rw-r--r--arch/arm/plat-samsung/s5p-sleep.S43
-rw-r--r--arch/arm/xen/p2m.c32
-rw-r--r--arch/arm64/Kconfig1
-rw-r--r--arch/arm64/include/asm/kvm_arm.h3
-rw-r--r--arch/arm64/include/asm/kvm_asm.h3
-rw-r--r--arch/arm64/include/asm/kvm_mmu.h22
-rw-r--r--arch/arm64/kernel/debug-monitors.c6
-rw-r--r--arch/arm64/kernel/hw_breakpoint.c7
-rw-r--r--arch/arm64/kvm/sys_regs.c99
-rw-r--r--arch/arm64/kvm/sys_regs.h2
-rw-r--r--arch/arm64/mm/init.c1
-rw-r--r--arch/blackfin/kernel/ftrace.c5
-rw-r--r--arch/cris/arch-v32/drivers/Kconfig2
-rw-r--r--arch/hexagon/Kconfig1
-rw-r--r--arch/hexagon/include/asm/Kbuild2
-rw-r--r--arch/hexagon/include/asm/atomic.h15
-rw-r--r--arch/hexagon/include/asm/delay.h1
-rw-r--r--arch/hexagon/include/asm/dma-mapping.h1
-rw-r--r--arch/hexagon/include/asm/elf.h4
-rw-r--r--arch/hexagon/include/asm/hexagon_vm.h72
-rw-r--r--arch/hexagon/include/asm/io.h2
-rw-r--r--arch/hexagon/include/asm/kgdb.h5
-rw-r--r--arch/hexagon/include/asm/pgalloc.h2
-rw-r--r--arch/hexagon/include/asm/smp.h1
-rw-r--r--arch/hexagon/include/uapi/asm/registers.h4
-rw-r--r--arch/hexagon/include/uapi/asm/setup.h5
-rw-r--r--arch/hexagon/kernel/Makefile2
-rw-r--r--arch/hexagon/kernel/hexagon_ksyms.c24
-rw-r--r--arch/hexagon/kernel/kgdb.c2
-rw-r--r--arch/hexagon/kernel/ptrace.c1
-rw-r--r--arch/hexagon/kernel/reset.c5
-rw-r--r--arch/hexagon/kernel/screen_info.c3
-rw-r--r--arch/hexagon/kernel/smp.c6
-rw-r--r--arch/hexagon/kernel/time.c12
-rw-r--r--arch/ia64/kernel/err_inject.c15
-rw-r--r--arch/ia64/kernel/ftrace.c4
-rw-r--r--arch/ia64/kernel/palinfo.c6
-rw-r--r--arch/ia64/kernel/salinfo.c6
-rw-r--r--arch/ia64/kernel/topology.c6
-rw-r--r--arch/ia64/kvm/kvm-ia64.c1
-rw-r--r--arch/m68k/configs/m5208evb_defconfig1
-rw-r--r--arch/m68k/configs/m5249evb_defconfig1
-rw-r--r--arch/m68k/configs/m5272c3_defconfig1
-rw-r--r--arch/m68k/configs/m5275evb_defconfig1
-rw-r--r--arch/m68k/configs/m5307c3_defconfig1
-rw-r--r--arch/m68k/configs/m5407c3_defconfig1
-rw-r--r--arch/m68k/include/asm/io_no.h6
-rw-r--r--arch/metag/kernel/ftrace.c5
-rw-r--r--arch/microblaze/kernel/ftrace.c5
-rw-r--r--arch/mips/include/asm/kvm_host.h417
-rw-r--r--arch/mips/kernel/ftrace.c5
-rw-r--r--arch/mips/kvm/kvm_mips_emul.c40
-rw-r--r--arch/powerpc/Kconfig1
-rw-r--r--arch/powerpc/include/asm/archrandom.h18
-rw-r--r--arch/powerpc/include/asm/kvm_book3s.h5
-rw-r--r--arch/powerpc/include/asm/kvm_book3s_64.h12
-rw-r--r--arch/powerpc/include/asm/kvm_book3s_asm.h2
-rw-r--r--arch/powerpc/include/asm/kvm_ppc.h2
-rw-r--r--arch/powerpc/include/asm/reg.h1
-rw-r--r--arch/powerpc/include/asm/tm.h4
-rw-r--r--arch/powerpc/kernel/ftrace.c7
-rw-r--r--arch/powerpc/kernel/prom.c3
-rw-r--r--arch/powerpc/kernel/sysfs.c8
-rw-r--r--arch/powerpc/kvm/book3s_64_mmu_hv.c9
-rw-r--r--arch/powerpc/kvm/book3s_64_vio_hv.c28
-rw-r--r--arch/powerpc/kvm/book3s_hv.c159
-rw-r--r--arch/powerpc/kvm/book3s_hv_interrupts.S22
-rw-r--r--arch/powerpc/kvm/book3s_hv_rm_mmu.c6
-rw-r--r--arch/powerpc/kvm/book3s_hv_rmhandlers.S187
-rw-r--r--arch/powerpc/kvm/book3s_rtas.c7
-rw-r--r--arch/powerpc/net/bpf_jit_comp.c7
-rw-r--r--arch/powerpc/platforms/pseries/dlpar.c2
-rw-r--r--arch/powerpc/platforms/pseries/reconfig.c2
-rw-r--r--arch/powerpc/sysdev/msi_bitmap.c2
-rw-r--r--arch/s390/include/asm/cio.h2
-rw-r--r--arch/s390/include/asm/irq.h1
-rw-r--r--arch/s390/include/asm/kvm_host.h98
-rw-r--r--arch/s390/include/asm/pgtable.h2
-rw-r--r--arch/s390/include/asm/processor.h1
-rw-r--r--arch/s390/include/uapi/asm/kvm.h43
-rw-r--r--arch/s390/kernel/cache.c5
-rw-r--r--arch/s390/kernel/ftrace.c3
-rw-r--r--arch/s390/kernel/irq.c1
-rw-r--r--arch/s390/kernel/smp.c13
-rw-r--r--arch/s390/kvm/Kconfig4
-rw-r--r--arch/s390/kvm/Makefile2
-rw-r--r--arch/s390/kvm/diag.c84
-rw-r--r--arch/s390/kvm/interrupt.c704
-rw-r--r--arch/s390/kvm/irq.h22
-rw-r--r--arch/s390/kvm/kvm-s390.c212
-rw-r--r--arch/s390/kvm/kvm-s390.h7
-rw-r--r--arch/s390/kvm/priv.c7
-rw-r--r--arch/s390/kvm/sigp.c157
-rw-r--r--arch/s390/kvm/trace.h46
-rw-r--r--arch/s390/mm/fault.c26
-rw-r--r--arch/s390/net/bpf_jit_comp.c13
-rw-r--r--arch/score/Kconfig6
-rw-r--r--arch/sh/boards/board-sh7757lcr.c2
-rw-r--r--arch/sh/drivers/pci/pcie-sh7786.h3
-rw-r--r--arch/sh/include/asm/syscalls_32.h12
-rw-r--r--arch/sh/include/asm/traps_32.h16
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7757.c2
-rw-r--r--arch/sh/kernel/dumpstack.c2
-rw-r--r--arch/sh/kernel/entry-common.S15
-rw-r--r--arch/sh/kernel/ftrace.c5
-rw-r--r--arch/sh/kernel/signal_32.c12
-rw-r--r--arch/sh/kernel/sys_sh32.c7
-rw-r--r--arch/sh/kernel/traps_32.c23
-rw-r--r--arch/sh/math-emu/math.c18
-rw-r--r--arch/sparc/kernel/ftrace.c6
-rw-r--r--arch/sparc/kernel/leon_pci_grpci2.c1
-rw-r--r--arch/sparc/kernel/sun4m_irq.c2
-rw-r--r--arch/sparc/kernel/sysfs.c6
-rw-r--r--arch/sparc/net/bpf_jit_comp.c5
-rw-r--r--arch/tile/Kconfig6
-rw-r--r--arch/tile/include/asm/perf_event.h22
-rw-r--r--arch/tile/include/asm/pmc.h64
-rw-r--r--arch/tile/kernel/Makefile2
-rw-r--r--arch/tile/kernel/ftrace.c4
-rw-r--r--arch/tile/kernel/intvec_32.S24
-rw-r--r--arch/tile/kernel/intvec_64.S24
-rw-r--r--arch/tile/kernel/irq.c18
-rw-r--r--arch/tile/kernel/messaging.c4
-rw-r--r--arch/tile/kernel/pci.c2
-rw-r--r--arch/tile/kernel/perf_event.c1005
-rw-r--r--arch/tile/kernel/pmc.c121
-rw-r--r--arch/tile/kernel/time.c10
-rw-r--r--arch/tile/kernel/vdso/Makefile2
-rw-r--r--arch/um/drivers/net_kern.c2
-rw-r--r--arch/x86/crypto/Makefile3
-rw-r--r--arch/x86/crypto/blowfish_glue.c3
-rw-r--r--arch/x86/crypto/cast5_avx_glue.c3
-rw-r--r--arch/x86/crypto/ghash-clmulni-intel_asm.S29
-rw-r--r--arch/x86/crypto/ghash-clmulni-intel_glue.c14
-rw-r--r--arch/x86/crypto/sha1_avx2_x86_64_asm.S708
-rw-r--r--arch/x86/crypto/sha1_ssse3_glue.c53
-rw-r--r--arch/x86/include/asm/archrandom.h42
-rw-r--r--arch/x86/include/asm/kvm_host.h18
-rw-r--r--arch/x86/include/asm/vmx.h4
-rw-r--r--arch/x86/include/asm/xen/page.h11
-rw-r--r--arch/x86/include/asm/xsave.h2
-rw-r--r--arch/x86/include/uapi/asm/msr-index.h1
-rw-r--r--arch/x86/kernel/cpu/intel_cacheinfo.c13
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce.c8
-rw-r--r--arch/x86/kernel/cpu/mcheck/therm_throt.c18
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd_ibs.c6
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd_uncore.c7
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_rapl.c9
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_uncore.c6
-rw-r--r--arch/x86/kernel/cpuid.c15
-rw-r--r--arch/x86/kernel/ftrace.c55
-rw-r--r--arch/x86/kernel/hpet.c4
-rw-r--r--arch/x86/kernel/kvm.c1
-rw-r--r--arch/x86/kernel/kvmclock.c2
-rw-r--r--arch/x86/kernel/msr.c16
-rw-r--r--arch/x86/kernel/vsyscall_64.c6
-rw-r--r--arch/x86/kvm/cpuid.c37
-rw-r--r--arch/x86/kvm/emulate.c8
-rw-r--r--arch/x86/kvm/mmu.c2
-rw-r--r--arch/x86/kvm/paging_tmpl.h7
-rw-r--r--arch/x86/kvm/svm.c84
-rw-r--r--arch/x86/kvm/vmx.c334
-rw-r--r--arch/x86/kvm/x86.c152
-rw-r--r--arch/x86/kvm/x86.h5
-rw-r--r--arch/x86/net/bpf_jit_comp.c11
-rw-r--r--arch/x86/oprofile/nmi_int.c15
-rw-r--r--arch/x86/pci/amd_bus.c5
-rw-r--r--arch/x86/pci/xen.c29
-rw-r--r--arch/x86/syscalls/syscall_64.tbl1
-rw-r--r--arch/x86/xen/Kconfig5
-rw-r--r--arch/x86/xen/p2m.c121
-rw-r--r--block/blk-cgroup.c11
-rw-r--r--block/blk-cgroup.h22
-rw-r--r--block/blk-core.c2
-rw-r--r--block/blk-map.c2
-rw-r--r--block/blk-throttle.c8
-rw-r--r--block/cfq-iosched.c7
-rw-r--r--crypto/Kconfig4
-rw-r--r--crypto/Makefile2
-rw-r--r--crypto/ahash.c147
-rw-r--r--crypto/blkcipher.c81
-rw-r--r--crypto/crc32c_generic.c (renamed from crypto/crc32c.c)2
-rw-r--r--crypto/crypto_null.c6
-rw-r--r--crypto/crypto_wq.c2
-rw-r--r--crypto/tcrypt.c8
-rw-r--r--crypto/testmgr.c32
-rw-r--r--crypto/testmgr.h180
-rw-r--r--drivers/amba/bus.c4
-rw-r--r--drivers/amba/tegra-ahb.c2
-rw-r--r--drivers/ata/libata-core.c4
-rw-r--r--drivers/atm/ambassador.c2
-rw-r--r--drivers/atm/firestream.c6
-rw-r--r--drivers/atm/idt77105.c6
-rw-r--r--drivers/atm/nicstar.c24
-rw-r--r--drivers/atm/solos-pci.c2
-rw-r--r--drivers/base/bus.c2
-rw-r--r--drivers/base/topology.c12
-rw-r--r--drivers/bcma/driver_gpio.c9
-rw-r--r--drivers/block/rbd.c87
-rw-r--r--drivers/block/virtio_blk.c20
-rw-r--r--drivers/bluetooth/Kconfig3
-rw-r--r--drivers/bluetooth/ath3k.c97
-rw-r--r--drivers/bluetooth/bfusb.c14
-rw-r--r--drivers/bluetooth/bluecard_cs.c11
-rw-r--r--drivers/bluetooth/bt3c_cs.c7
-rw-r--r--drivers/bluetooth/btmrvl_main.c11
-rw-r--r--drivers/bluetooth/btuart_cs.c6
-rw-r--r--drivers/bluetooth/btusb.c59
-rw-r--r--drivers/bluetooth/dtl1_cs.c9
-rw-r--r--drivers/bluetooth/hci_bcsp.c31
-rw-r--r--drivers/bluetooth/hci_h5.c10
-rw-r--r--drivers/bluetooth/hci_ldisc.c9
-rw-r--r--drivers/bluetooth/hci_vhci.c3
-rw-r--r--drivers/bus/arm-cci.c24
-rw-r--r--drivers/bus/imx-weim.c58
-rw-r--r--drivers/bus/mvebu-mbus.c3
-rw-r--r--drivers/char/hw_random/Kconfig6
-rw-r--r--drivers/char/hw_random/atmel-rng.c23
-rw-r--r--drivers/char/hw_random/core.c17
-rw-r--r--drivers/char/hw_random/nomadik-rng.c13
-rw-r--r--drivers/char/hw_random/omap3-rom-rng.c3
-rw-r--r--drivers/char/hw_random/picoxcell-rng.c27
-rw-r--r--drivers/char/hw_random/timeriomem-rng.c40
-rw-r--r--drivers/char/hw_random/virtio-rng.c3
-rw-r--r--drivers/char/random.c244
-rw-r--r--drivers/clk/Kconfig7
-rw-r--r--drivers/clk/Makefile4
-rw-r--r--drivers/clk/at91/clk-programmable.c202
-rw-r--r--drivers/clk/at91/clk-system.c76
-rw-r--r--drivers/clk/bcm/Kconfig9
-rw-r--r--drivers/clk/bcm/Makefile3
-rw-r--r--drivers/clk/bcm/clk-bcm281xx.c416
-rw-r--r--drivers/clk/bcm/clk-kona-setup.c769
-rw-r--r--drivers/clk/bcm/clk-kona.c1033
-rw-r--r--drivers/clk/bcm/clk-kona.h410
-rw-r--r--drivers/clk/clk-axi-clkgen.c312
-rw-r--r--drivers/clk/clk-divider.c10
-rw-r--r--drivers/clk/clk-moxart.c97
-rw-r--r--drivers/clk/clk-ppc-corenet.c70
-rw-r--r--drivers/clk/clk-s2mps11.c29
-rw-r--r--drivers/clk/clk.c131
-rw-r--r--drivers/clk/clkdev.c2
-rw-r--r--drivers/clk/hisilicon/Makefile5
-rw-r--r--drivers/clk/hisilicon/clk-hi3620.c298
-rw-r--r--drivers/clk/hisilicon/clk-hip04.c58
-rw-r--r--drivers/clk/hisilicon/clk.c62
-rw-r--r--drivers/clk/hisilicon/clk.h17
-rw-r--r--drivers/clk/mmp/clk-frac.c20
-rw-r--r--drivers/clk/mvebu/Kconfig8
-rw-r--r--drivers/clk/mvebu/Makefile2
-rw-r--r--drivers/clk/mvebu/armada-375.c184
-rw-r--r--drivers/clk/mvebu/armada-38x.c167
-rw-r--r--drivers/clk/mvebu/clk-corediv.c154
-rw-r--r--drivers/clk/samsung/clk-exynos-audss.c2
-rw-r--r--drivers/clk/samsung/clk-exynos4.c172
-rw-r--r--drivers/clk/samsung/clk-exynos5250.c49
-rw-r--r--drivers/clk/samsung/clk-exynos5420.c49
-rw-r--r--drivers/clk/samsung/clk-exynos5440.c2
-rw-r--r--drivers/clk/samsung/clk-s3c64xx.c79
-rw-r--r--drivers/clk/samsung/clk.c71
-rw-r--r--drivers/clk/samsung/clk.h14
-rw-r--r--drivers/clk/shmobile/Makefile1
-rw-r--r--drivers/clk/shmobile/clk-div6.c2
-rw-r--r--drivers/clk/shmobile/clk-mstp.c2
-rw-r--r--drivers/clk/shmobile/clk-rcar-gen2.c8
-rw-r--r--drivers/clk/shmobile/clk-rz.c103
-rw-r--r--drivers/clk/sirf/clk-atlas6.c3
-rw-r--r--drivers/clk/sirf/clk-common.c3
-rw-r--r--drivers/clk/sirf/clk-prima2.c3
-rw-r--r--drivers/clk/socfpga/Makefile3
-rw-r--r--drivers/clk/socfpga/clk-gate.c263
-rw-r--r--drivers/clk/socfpga/clk-periph.c94
-rw-r--r--drivers/clk/socfpga/clk-pll.c131
-rw-r--r--drivers/clk/socfpga/clk.c326
-rw-r--r--drivers/clk/socfpga/clk.h57
-rw-r--r--drivers/clk/st/Makefile1
-rw-r--r--drivers/clk/st/clkgen-fsyn.c1039
-rw-r--r--drivers/clk/st/clkgen-mux.c820
-rw-r--r--drivers/clk/st/clkgen-pll.c698
-rw-r--r--drivers/clk/st/clkgen.h48
-rw-r--r--drivers/clk/sunxi/clk-sunxi.c305
-rw-r--r--drivers/clk/tegra/clk-periph.c2
-rw-r--r--drivers/clk/ti/clk-33xx.c1
-rw-r--r--drivers/clk/ti/clk-3xxx.c4
-rw-r--r--drivers/clk/ti/clk-44xx.c1
-rw-r--r--drivers/clk/ti/clk-54xx.c1
-rw-r--r--drivers/clk/ti/clk-7xx.c1
-rw-r--r--drivers/clk/ti/divider.c8
-rw-r--r--drivers/clk/ux500/u8500_of_clk.c3
-rw-r--r--drivers/clk/versatile/clk-icst.c21
-rw-r--r--drivers/clk/versatile/clk-icst.h1
-rw-r--r--drivers/clk/versatile/clk-impd1.c12
-rw-r--r--drivers/clk/versatile/clk-integrator.c83
-rw-r--r--drivers/clk/versatile/clk-realview.c4
-rw-r--r--drivers/clk/zynq/clkc.c93
-rw-r--r--drivers/clk/zynq/pll.c18
-rw-r--r--drivers/clocksource/Kconfig3
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/dummy_timer.c11
-rw-r--r--drivers/clocksource/exynos_mct.c2
-rw-r--r--drivers/clocksource/qcom-timer.c (renamed from arch/arm/mach-msm/timer.c)29
-rw-r--r--drivers/clocksource/timer-marco.c13
-rw-r--r--drivers/clocksource/timer-prima2.c16
-rw-r--r--drivers/clocksource/timer-u300.c2
-rw-r--r--drivers/connector/connector.c1
-rw-r--r--drivers/cpufreq/Kconfig.arm2
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c7
-rw-r--r--drivers/cpuidle/Kconfig.arm2
-rw-r--r--drivers/cpuidle/cpuidle.c107
-rw-r--r--drivers/crypto/Kconfig22
-rw-r--r--drivers/crypto/Makefile2
-rw-r--r--drivers/crypto/bfin_crc.c45
-rw-r--r--drivers/crypto/caam/caamalg.c384
-rw-r--r--drivers/crypto/caam/caamrng.c17
-rw-r--r--drivers/crypto/caam/compat.h1
-rw-r--r--drivers/crypto/caam/ctrl.c61
-rw-r--r--drivers/crypto/caam/ctrl.h2
-rw-r--r--drivers/crypto/caam/desc_constr.h27
-rw-r--r--drivers/crypto/caam/regs.h4
-rw-r--r--drivers/crypto/ccp/ccp-crypto-main.c224
-rw-r--r--drivers/crypto/ccp/ccp-crypto-sha.c130
-rw-r--r--drivers/crypto/ccp/ccp-crypto.h8
-rw-r--r--drivers/crypto/ccp/ccp-dev.c21
-rw-r--r--drivers/crypto/ccp/ccp-ops.c108
-rw-r--r--drivers/crypto/mxs-dcp.c83
-rw-r--r--drivers/crypto/omap-aes.c4
-rw-r--r--drivers/crypto/omap-des.c1216
-rw-r--r--drivers/crypto/omap-sham.c12
-rw-r--r--drivers/crypto/picoxcell_crypto.c16
-rw-r--r--drivers/crypto/s5p-sss.c13
-rw-r--r--drivers/crypto/sahara.c26
-rw-r--r--drivers/crypto/talitos.c4
-rw-r--r--drivers/crypto/tegra-aes.c1087
-rw-r--r--drivers/crypto/tegra-aes.h103
-rw-r--r--drivers/dma/omap-dma.c659
-rw-r--r--drivers/edac/edac_mc_sysfs.c2
-rw-r--r--drivers/edac/ghes_edac.c2
-rw-r--r--drivers/edac/i5400_edac.c4
-rw-r--r--drivers/edac/i7300_edac.c4
-rw-r--r--drivers/edac/i7core_edac.c4
-rw-r--r--drivers/edac/sb_edac.c29
-rw-r--r--drivers/gpio/Kconfig42
-rw-r--r--drivers/gpio/Makefile4
-rw-r--r--drivers/gpio/gpio-adnp.c15
-rw-r--r--drivers/gpio/gpio-adp5588.c16
-rw-r--r--drivers/gpio/gpio-bcm-kona.c109
-rw-r--r--drivers/gpio/gpio-clps711x.c1
-rw-r--r--drivers/gpio/gpio-davinci.c75
-rw-r--r--drivers/gpio/gpio-dwapb.c438
-rw-r--r--drivers/gpio/gpio-em.c14
-rw-r--r--drivers/gpio/gpio-generic.c20
-rw-r--r--drivers/gpio/gpio-ich.c96
-rw-r--r--drivers/gpio/gpio-intel-mid.c22
-rw-r--r--drivers/gpio/gpio-iop.c2
-rw-r--r--drivers/gpio/gpio-lynxpoint.c16
-rw-r--r--drivers/gpio/gpio-max732x.c9
-rw-r--r--drivers/gpio/gpio-mcp23s08.c36
-rw-r--r--drivers/gpio/gpio-moxart.c40
-rw-r--r--drivers/gpio/gpio-mvebu.c7
-rw-r--r--drivers/gpio/gpio-mxs.c3
-rw-r--r--drivers/gpio/gpio-omap.c20
-rw-r--r--drivers/gpio/gpio-pca953x.c10
-rw-r--r--drivers/gpio/gpio-pch.c8
-rw-r--r--drivers/gpio/gpio-pl061.c108
-rw-r--r--drivers/gpio/gpio-rc5t583.c2
-rw-r--r--drivers/gpio/gpio-rcar.c32
-rw-r--r--drivers/gpio/gpio-samsung.c1
-rw-r--r--drivers/gpio/gpio-syscon.c191
-rw-r--r--drivers/gpio/gpio-tnetv107x.c206
-rw-r--r--drivers/gpio/gpio-twl4030.c6
-rw-r--r--drivers/gpio/gpio-tz1090.c28
-rw-r--r--drivers/gpio/gpio-zevio.c220
-rw-r--r--drivers/gpio/gpiolib-acpi.c474
-rw-r--r--drivers/gpio/gpiolib-of.c3
-rw-r--r--drivers/gpio/gpiolib.c417
-rw-r--r--drivers/gpio/gpiolib.h3
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c2
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h2
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_iommu.c6
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_iommu.h1
-rw-r--r--drivers/gpu/drm/msm/Kconfig2
-rw-r--r--drivers/gpu/drm/omapdrm/omap_connector.c6
-rw-r--r--drivers/hid/Kconfig19
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/hid-core.c52
-rw-r--r--drivers/hid/hid-cp2112.c1073
-rw-r--r--drivers/hid/hid-hyperv.c10
-rw-r--r--drivers/hid/hid-ids.h5
-rw-r--r--drivers/hid/hid-input.c21
-rw-r--r--drivers/hid/hid-lg.c6
-rw-r--r--drivers/hid/hid-logitech-dj.c111
-rw-r--r--drivers/hid/hid-magicmouse.c4
-rw-r--r--drivers/hid/hid-microsoft.c74
-rw-r--r--drivers/hid/hid-multitouch.c281
-rw-r--r--drivers/hid/hid-picolcd_cir.c2
-rw-r--r--drivers/hid/hid-sensor-hub.c217
-rw-r--r--drivers/hid/hid-sony.c808
-rw-r--r--drivers/hid/hid-thingm.c4
-rw-r--r--drivers/hid/hid-wacom.c28
-rw-r--r--drivers/hid/hid-wiimote-core.c4
-rw-r--r--drivers/hid/hidraw.c27
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.c139
-rw-r--r--drivers/hid/uhid.c103
-rw-r--r--drivers/hid/usbhid/hid-core.c102
-rw-r--r--drivers/hwmon/Kconfig4
-rw-r--r--drivers/hwmon/adm1021.c70
-rw-r--r--drivers/hwmon/asc7621.c1
-rw-r--r--drivers/hwmon/atxp1.c2
-rw-r--r--drivers/hwmon/coretemp.c14
-rw-r--r--drivers/hwmon/f71805f.c2
-rw-r--r--drivers/hwmon/it87.c9
-rw-r--r--drivers/hwmon/k10temp.c1
-rw-r--r--drivers/hwmon/lm63.c157
-rw-r--r--drivers/hwmon/lm77.c1
-rw-r--r--drivers/hwmon/lm80.c70
-rw-r--r--drivers/hwmon/lm83.c1
-rw-r--r--drivers/hwmon/lm87.c1
-rw-r--r--drivers/hwmon/lm90.c111
-rw-r--r--drivers/hwmon/lm92.c1
-rw-r--r--drivers/hwmon/lm93.c1
-rw-r--r--drivers/hwmon/max1619.c1
-rw-r--r--drivers/hwmon/pc87360.c12
-rw-r--r--drivers/hwmon/via-cputemp.c14
-rw-r--r--drivers/hwmon/w83792d.c1
-rw-r--r--drivers/hwmon/w83l785ts.c4
-rw-r--r--drivers/idle/intel_idle.c12
-rw-r--r--drivers/iio/adc/Kconfig10
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/twl4030-madc.c (renamed from drivers/mfd/twl4030-madc.c)321
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.c39
-rw-r--r--drivers/infiniband/core/cm.c17
-rw-r--r--drivers/infiniband/core/cma.c26
-rw-r--r--drivers/infiniband/core/mad.c14
-rw-r--r--drivers/infiniband/core/umem.c120
-rw-r--r--drivers/infiniband/core/verbs.c47
-rw-r--r--drivers/infiniband/hw/amso1100/c2_provider.c23
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.c19
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c134
-rw-r--r--drivers/infiniband/hw/cxgb4/cq.c31
-rw-r--r--drivers/infiniband/hw/cxgb4/device.c183
-rw-r--r--drivers/infiniband/hw/cxgb4/iw_cxgb4.h11
-rw-r--r--drivers/infiniband/hw/cxgb4/mem.c55
-rw-r--r--drivers/infiniband/hw/cxgb4/provider.c45
-rw-r--r--drivers/infiniband/hw/cxgb4/qp.c148
-rw-r--r--drivers/infiniband/hw/cxgb4/t4.h6
-rw-r--r--drivers/infiniband/hw/cxgb4/user.h5
-rw-r--r--drivers/infiniband/hw/ehca/ehca_classes.h2
-rw-r--r--drivers/infiniband/hw/ehca/ehca_cq.c1
-rw-r--r--drivers/infiniband/hw/ehca/ehca_mrmw.c257
-rw-r--r--drivers/infiniband/hw/ipath/ipath_diag.c66
-rw-r--r--drivers/infiniband/hw/ipath/ipath_dma.c43
-rw-r--r--drivers/infiniband/hw/ipath/ipath_mr.c39
-rw-r--r--drivers/infiniband/hw/mlx4/alias_GUID.c2
-rw-r--r--drivers/infiniband/hw/mlx4/cm.c80
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c42
-rw-r--r--drivers/infiniband/hw/mlx4/doorbell.c4
-rw-r--r--drivers/infiniband/hw/mlx4/mad.c122
-rw-r--r--drivers/infiniband/hw/mlx4/main.c42
-rw-r--r--drivers/infiniband/hw/mlx4/mcg.c5
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h24
-rw-r--r--drivers/infiniband/hw/mlx4/mr.c39
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c321
-rw-r--r--drivers/infiniband/hw/mlx4/sysfs.c5
-rw-r--r--drivers/infiniband/hw/mlx5/cq.c62
-rw-r--r--drivers/infiniband/hw/mlx5/doorbell.c4
-rw-r--r--drivers/infiniband/hw/mlx5/main.c12
-rw-r--r--drivers/infiniband/hw/mlx5/mem.c80
-rw-r--r--drivers/infiniband/hw/mlx5/mlx5_ib.h14
-rw-r--r--drivers/infiniband/hw/mlx5/mr.c157
-rw-r--r--drivers/infiniband/hw/mlx5/qp.c540
-rw-r--r--drivers/infiniband/hw/mthca/mthca_provider.c43
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c121
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.h3
-rw-r--r--drivers/infiniband/hw/nes/nes_user.h5
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c261
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.h1
-rw-r--r--drivers/infiniband/hw/ocrdma/Makefile2
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma.h110
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_abi.h7
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_ah.c2
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_hw.c299
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_hw.h6
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_main.c81
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_sli.h261
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_stats.c623
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_stats.h54
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.c233
-rw-r--r--drivers/infiniband/hw/qib/qib.h12
-rw-r--r--drivers/infiniband/hw/qib/qib_diag.c52
-rw-r--r--drivers/infiniband/hw/qib/qib_dma.c21
-rw-r--r--drivers/infiniband/hw/qib/qib_file_ops.c5
-rw-r--r--drivers/infiniband/hw/qib/qib_fs.c1
-rw-r--r--drivers/infiniband/hw/qib/qib_iba6120.c11
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7220.c12
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7322.c37
-rw-r--r--drivers/infiniband/hw/qib/qib_init.c96
-rw-r--r--drivers/infiniband/hw/qib/qib_mad.c44
-rw-r--r--drivers/infiniband/hw/qib/qib_mr.c14
-rw-r--r--drivers/infiniband/hw/qib/qib_rc.c2
-rw-r--r--drivers/infiniband/hw/qib/qib_ruc.c1
-rw-r--r--drivers/infiniband/hw/qib/qib_ud.c6
-rw-r--r--drivers/infiniband/hw/qib/qib_user_sdma.c136
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.c8
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.h16
-rw-r--r--drivers/infiniband/hw/usnic/usnic_uiom.c2
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c93
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h85
-rw-r--r--drivers/infiniband/ulp/iser/iser_initiator.c154
-rw-r--r--drivers/infiniband/ulp/iser/iser_memory.c470
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c325
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c83
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h1
-rw-r--r--drivers/input/keyboard/Kconfig22
-rw-r--r--drivers/input/keyboard/Makefile2
-rw-r--r--drivers/input/keyboard/clps711x-keypad.c207
-rw-r--r--drivers/input/keyboard/imx_keypad.c4
-rw-r--r--drivers/input/keyboard/pmic8xxx-keypad.c348
-rw-r--r--drivers/input/keyboard/tnetv107x-keypad.c329
-rw-r--r--drivers/input/misc/Kconfig16
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/ims-pcu.c258
-rw-r--r--drivers/input/misc/ixp4xx-beeper.c2
-rw-r--r--drivers/input/misc/pm8xxx-vibrator.c9
-rw-r--r--drivers/input/misc/pmic8xxx-pwrkey.c33
-rw-r--r--drivers/input/misc/sirfsoc-onkey.c111
-rw-r--r--drivers/input/misc/soc_button_array.c218
-rw-r--r--drivers/input/misc/uinput.c97
-rw-r--r--drivers/input/misc/wistron_btns.c19
-rw-r--r--drivers/input/mouse/appletouch.c143
-rw-r--r--drivers/input/serio/Kconfig2
-rw-r--r--drivers/input/serio/hp_sdc.c2
-rw-r--r--drivers/input/sparse-keymap.c2
-rw-r--r--drivers/input/tablet/gtco.c2
-rw-r--r--drivers/input/touchscreen/Kconfig9
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c519
-rw-r--r--drivers/input/touchscreen/tnetv107x-ts.c384
-rw-r--r--drivers/input/touchscreen/zforce_ts.c95
-rw-r--r--drivers/iommu/Kconfig2
-rw-r--r--drivers/iommu/amd_iommu.c8
-rw-r--r--drivers/iommu/amd_iommu_init.c16
-rw-r--r--drivers/iommu/amd_iommu_types.h11
-rw-r--r--drivers/iommu/arm-smmu.c105
-rw-r--r--drivers/iommu/dmar.c513
-rw-r--r--drivers/iommu/intel-iommu.c1610
-rw-r--r--drivers/iommu/intel_irq_remapping.c108
-rw-r--r--drivers/iommu/iova.c64
-rw-r--r--drivers/iommu/omap-iommu.c162
-rw-r--r--drivers/iommu/omap-iommu.h5
-rw-r--r--drivers/iommu/omap-iommu2.c3
-rw-r--r--drivers/iommu/shmobile-iommu.c2
-rw-r--r--drivers/irqchip/Kconfig16
-rw-r--r--drivers/irqchip/Makefile2
-rw-r--r--drivers/irqchip/exynos-combiner.c3
-rw-r--r--drivers/irqchip/irq-clps711x.c243
-rw-r--r--drivers/irqchip/irq-crossbar.c208
-rw-r--r--drivers/irqchip/irq-gic.c86
-rw-r--r--drivers/irqchip/irq-mmp.c2
-rw-r--r--drivers/irqchip/irq-vic.c60
-rw-r--r--drivers/isdn/act2000/module.c2
-rw-r--r--drivers/isdn/divert/divert_procfs.c7
-rw-r--r--drivers/isdn/hisax/elsa.c9
-rw-r--r--drivers/isdn/hisax/elsa_ser.c3
-rw-r--r--drivers/isdn/hysdn/hysdn_proclog.c7
-rw-r--r--drivers/isdn/i4l/isdn_common.c15
-rw-r--r--drivers/isdn/i4l/isdn_ppp.c61
-rw-r--r--drivers/isdn/pcbit/drv.c6
-rw-r--r--drivers/isdn/sc/init.c4
-rw-r--r--drivers/leds/Kconfig4
-rw-r--r--drivers/md/Kconfig11
-rw-r--r--drivers/md/Makefile2
-rw-r--r--drivers/md/dm-cache-block-types.h11
-rw-r--r--drivers/md/dm-cache-metadata.c132
-rw-r--r--drivers/md/dm-cache-metadata.h15
-rw-r--r--drivers/md/dm-cache-target.c131
-rw-r--r--drivers/md/dm-era-target.c1746
-rw-r--r--drivers/md/dm-mpath.c219
-rw-r--r--drivers/md/dm-table.c21
-rw-r--r--drivers/md/dm-thin-metadata.c80
-rw-r--r--drivers/md/dm-thin.c263
-rw-r--r--drivers/md/dm.c24
-rw-r--r--drivers/md/dm.h2
-rw-r--r--drivers/md/persistent-data/dm-bitset.c10
-rw-r--r--drivers/md/persistent-data/dm-bitset.h1
-rw-r--r--drivers/md/persistent-data/dm-block-manager.c15
-rw-r--r--drivers/md/persistent-data/dm-block-manager.h3
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.c5
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.h17
-rw-r--r--drivers/media/common/siano/smsdvb-debugfs.c2
-rw-r--r--drivers/media/common/siano/smsir.c2
-rw-r--r--drivers/media/dvb-core/dvb-usb-ids.h1
-rw-r--r--drivers/media/dvb-core/dvb_frontend.c10
-rw-r--r--drivers/media/dvb-frontends/Kconfig12
-rw-r--r--drivers/media/dvb-frontends/Makefile2
-rw-r--r--drivers/media/dvb-frontends/af9033.c59
-rw-r--r--drivers/media/dvb-frontends/af9033.h34
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/Kconfig7
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/Makefile6
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/bsp_i2c.h139
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx39xxj.h45
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h256
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_driver.h2343
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_driver_version.h72
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.c12400
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.h650
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj_map.h15055
-rw-r--r--drivers/media/dvb-frontends/drxd_hard.c4
-rw-r--r--drivers/media/dvb-frontends/ds3000.c2
-rw-r--r--drivers/media/dvb-frontends/it913x-fe-priv.h1051
-rw-r--r--drivers/media/dvb-frontends/it913x-fe.c1045
-rw-r--r--drivers/media/dvb-frontends/it913x-fe.h237
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c30
-rw-r--r--drivers/media/dvb-frontends/m88rs2000.c19
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.c4
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.h2
-rw-r--r--drivers/media/dvb-frontends/rtl2832.c191
-rw-r--r--drivers/media/dvb-frontends/rtl2832.h34
-rw-r--r--drivers/media/dvb-frontends/rtl2832_priv.h54
-rw-r--r--drivers/media/dvb-frontends/s921.c4
-rw-r--r--drivers/media/dvb-frontends/s921.h2
-rw-r--r--drivers/media/dvb-frontends/stb6100.c2
-rw-r--r--drivers/media/dvb-frontends/stv0900_sw.c2
-rw-r--r--drivers/media/dvb-frontends/tda10071.c68
-rw-r--r--drivers/media/dvb-frontends/tda10071.h2
-rw-r--r--drivers/media/i2c/Kconfig24
-rw-r--r--drivers/media/i2c/Makefile2
-rw-r--r--drivers/media/i2c/ad9389b.c2
-rw-r--r--drivers/media/i2c/adv7180.c118
-rw-r--r--drivers/media/i2c/adv7511.c2
-rw-r--r--drivers/media/i2c/adv7604.c4
-rw-r--r--drivers/media/i2c/adv7842.c153
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c4
-rw-r--r--drivers/media/i2c/lm3560.c22
-rw-r--r--drivers/media/i2c/lm3646.c414
-rw-r--r--drivers/media/i2c/mt9p031.c49
-rw-r--r--drivers/media/i2c/mt9t001.c229
-rw-r--r--drivers/media/i2c/mt9v011.c4
-rw-r--r--drivers/media/i2c/mt9v032.c10
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c207
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-spi.c6
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3.h4
-rw-r--r--drivers/media/i2c/s5k6a3.c389
-rw-r--r--drivers/media/i2c/sr030pc30.c2
-rw-r--r--drivers/media/i2c/ths8200.c26
-rw-r--r--drivers/media/i2c/tvp5150.c8
-rw-r--r--drivers/media/parport/bw-qcam.c8
-rw-r--r--drivers/media/pci/bt8xx/bttv-cards.c17
-rw-r--r--drivers/media/pci/bt8xx/bttv-input.c1
-rw-r--r--drivers/media/pci/bt8xx/bttv.h1
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c1
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.c2
-rw-r--r--drivers/media/pci/cx88/cx88-input.c2
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-core.c6
-rw-r--r--drivers/media/pci/saa7134/saa7134-cards.c4
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.c7
-rw-r--r--drivers/media/pci/ttpci/av7110_hw.c2
-rw-r--r--drivers/media/platform/Kconfig2
-rw-r--r--drivers/media/platform/arv.c6
-rw-r--r--drivers/media/platform/blackfin/bfin_capture.c2
-rw-r--r--drivers/media/platform/coda.c7
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c9
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c9
-rw-r--r--drivers/media/platform/davinci/vpif_display.c9
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-m2m.c12
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig9
-rw-r--r--drivers/media/platform/exynos4-is/Makefile4
-rw-r--r--drivers/media/platform/exynos4-is/fimc-capture.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.h5
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.c16
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.h1
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-sensor.c285
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-sensor.h49
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.c98
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.h9
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp-video.c660
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp-video.h44
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.c29
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.h27
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-m2m.c7
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c363
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.h32
-rw-r--r--drivers/media/platform/m2m-deinterlace.c11
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c3
-rw-r--r--drivers/media/platform/mem2mem_testdev.c95
-rw-r--r--drivers/media/platform/mx2_emmaprp.c13
-rw-r--r--drivers/media/platform/omap/omap_vout.c1
-rw-r--r--drivers/media/platform/omap/omap_vout_vrfb.c3
-rw-r--r--drivers/media/platform/omap3isp/isp.c7
-rw-r--r--drivers/media/platform/omap3isp/isp.h12
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.c10
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.h6
-rw-r--r--drivers/media/platform/omap3isp/ispccp2.c6
-rw-r--r--drivers/media/platform/omap3isp/isphist.c4
-rw-r--r--drivers/media/platform/omap3isp/isppreview.c22
-rw-r--r--drivers/media/platform/omap3isp/ispqueue.c2
-rw-r--r--drivers/media/platform/omap3isp/ispresizer.c6
-rw-r--r--drivers/media/platform/omap3isp/ispresizer.h4
-rw-r--r--drivers/media/platform/omap3isp/ispstat.c4
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.c12
-rw-r--r--drivers/media/platform/s3c-camif/camif-capture.c17
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.c7
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c7
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-regs.h24
-rw-r--r--drivers/media/platform/s5p-mfc/regs-mfc-v6.h1
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c17
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_common.h2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c24
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c8
-rw-r--r--drivers/media/platform/s5p-tv/mixer_video.c6
-rw-r--r--drivers/media/platform/soc_camera/atmel-isi.c2
-rw-r--r--drivers/media/platform/soc_camera/mx2_camera.c2
-rw-r--r--drivers/media/platform/soc_camera/mx3_camera.c2
-rw-r--r--drivers/media/platform/soc_camera/rcar_vin.c11
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c2
-rw-r--r--drivers/media/platform/ti-vpe/vpe.c6
-rw-r--r--drivers/media/platform/vivi.c54
-rw-r--r--drivers/media/platform/vsp1/vsp1.h2
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c2
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.c2
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.h2
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.c2
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.h2
-rw-r--r--drivers/media/platform/vsp1/vsp1_rpf.c2
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.c2
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.h2
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.c2
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.h2
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c4
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.h2
-rw-r--r--drivers/media/platform/vsp1/vsp1_wpf.c2
-rw-r--r--drivers/media/radio/radio-cadet.c46
-rw-r--r--drivers/media/radio/radio-keene.c19
-rw-r--r--drivers/media/radio/si4713/Kconfig6
-rw-r--r--drivers/media/radio/si4713/radio-usb-si4713.c4
-rw-r--r--drivers/media/rc/Kconfig11
-rw-r--r--drivers/media/rc/Makefile2
-rw-r--r--drivers/media/rc/ati_remote.c2
-rw-r--r--drivers/media/rc/ene_ir.c2
-rw-r--r--drivers/media/rc/fintek-cir.c2
-rw-r--r--drivers/media/rc/gpio-ir-recv.c4
-rw-r--r--drivers/media/rc/iguanair.c31
-rw-r--r--drivers/media/rc/img-ir/Kconfig61
-rw-r--r--drivers/media/rc/img-ir/Makefile11
-rw-r--r--drivers/media/rc/img-ir/img-ir-core.c176
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.c1053
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.h269
-rw-r--r--drivers/media/rc/img-ir/img-ir-jvc.c81
-rw-r--r--drivers/media/rc/img-ir/img-ir-nec.c148
-rw-r--r--drivers/media/rc/img-ir/img-ir-raw.c151
-rw-r--r--drivers/media/rc/img-ir/img-ir-raw.h60
-rw-r--r--drivers/media/rc/img-ir/img-ir-sanyo.c122
-rw-r--r--drivers/media/rc/img-ir/img-ir-sharp.c99
-rw-r--r--drivers/media/rc/img-ir/img-ir-sony.c145
-rw-r--r--drivers/media/rc/img-ir/img-ir.h166
-rw-r--r--drivers/media/rc/imon.c7
-rw-r--r--drivers/media/rc/ir-jvc-decoder.c2
-rw-r--r--drivers/media/rc/ir-lirc-codec.c2
-rw-r--r--drivers/media/rc/ir-mce_kbd-decoder.c2
-rw-r--r--drivers/media/rc/ir-nec-decoder.c11
-rw-r--r--drivers/media/rc/ir-raw.c5
-rw-r--r--drivers/media/rc/ir-rc5-decoder.c10
-rw-r--r--drivers/media/rc/ir-rc5-sz-decoder.c4
-rw-r--r--drivers/media/rc/ir-rc6-decoder.c6
-rw-r--r--drivers/media/rc/ir-sanyo-decoder.c6
-rw-r--r--drivers/media/rc/ir-sharp-decoder.c200
-rw-r--r--drivers/media/rc/ir-sony-decoder.c10
-rw-r--r--drivers/media/rc/ite-cir.c2
-rw-r--r--drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c4
-rw-r--r--drivers/media/rc/keymaps/rc-apac-viewcomp.c4
-rw-r--r--drivers/media/rc/keymaps/rc-asus-pc39.c4
-rw-r--r--drivers/media/rc/keymaps/rc-asus-ps3-100.c4
-rw-r--r--drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c4
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-a16d.c4
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-cardbus.c4
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-dvbt.c4
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-m135a.c4
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c2
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia.c4
-rw-r--r--drivers/media/rc/keymaps/rc-avertv-303.c4
-rw-r--r--drivers/media/rc/keymaps/rc-behold-columbus.c4
-rw-r--r--drivers/media/rc/keymaps/rc-behold.c4
-rw-r--r--drivers/media/rc/keymaps/rc-budget-ci-old.c4
-rw-r--r--drivers/media/rc/keymaps/rc-cinergy-1400.c4
-rw-r--r--drivers/media/rc/keymaps/rc-cinergy.c4
-rw-r--r--drivers/media/rc/keymaps/rc-dib0700-nec.c4
-rw-r--r--drivers/media/rc/keymaps/rc-dib0700-rc5.c4
-rw-r--r--drivers/media/rc/keymaps/rc-dm1105-nec.c4
-rw-r--r--drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c4
-rw-r--r--drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c4
-rw-r--r--drivers/media/rc/keymaps/rc-em-terratec.c4
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv-fm53.c4
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv.c4
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv2.c4
-rw-r--r--drivers/media/rc/keymaps/rc-evga-indtube.c4
-rw-r--r--drivers/media/rc/keymaps/rc-eztv.c4
-rw-r--r--drivers/media/rc/keymaps/rc-flydvb.c4
-rw-r--r--drivers/media/rc/keymaps/rc-flyvideo.c4
-rw-r--r--drivers/media/rc/keymaps/rc-fusionhdtv-mce.c4
-rw-r--r--drivers/media/rc/keymaps/rc-gadmei-rm008z.c4
-rw-r--r--drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c4
-rw-r--r--drivers/media/rc/keymaps/rc-gotview7135.c4
-rw-r--r--drivers/media/rc/keymaps/rc-hauppauge.c4
-rw-r--r--drivers/media/rc/keymaps/rc-iodata-bctv7e.c4
-rw-r--r--drivers/media/rc/keymaps/rc-kaiomy.c4
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-315u.c4
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-pc150u.c2
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c4
-rw-r--r--drivers/media/rc/keymaps/rc-manli.c4
-rw-r--r--drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c4
-rw-r--r--drivers/media/rc/keymaps/rc-msi-tvanywhere.c4
-rw-r--r--drivers/media/rc/keymaps/rc-nebula.c4
-rw-r--r--drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c6
-rw-r--r--drivers/media/rc/keymaps/rc-norwood.c4
-rw-r--r--drivers/media/rc/keymaps/rc-npgtech.c4
-rw-r--r--drivers/media/rc/keymaps/rc-pctv-sedna.c4
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-color.c4
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-grey.c4
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c4
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-002t.c4
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-mk12.c4
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-new.c4
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview.c4
-rw-r--r--drivers/media/rc/keymaps/rc-powercolor-real-angel.c4
-rw-r--r--drivers/media/rc/keymaps/rc-proteus-2309.c4
-rw-r--r--drivers/media/rc/keymaps/rc-purpletv.c4
-rw-r--r--drivers/media/rc/keymaps/rc-pv951.c4
-rw-r--r--drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c4
-rw-r--r--drivers/media/rc/keymaps/rc-tbs-nec.c4
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c4
-rw-r--r--drivers/media/rc/keymaps/rc-tevii-nec.c4
-rw-r--r--drivers/media/rc/keymaps/rc-tivo.c86
-rw-r--r--drivers/media/rc/keymaps/rc-tt-1500.c4
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-s350.c4
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-tv-pvr.c4
-rw-r--r--drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c4
-rw-r--r--drivers/media/rc/keymaps/rc-winfast.c4
-rw-r--r--drivers/media/rc/mceusb.c184
-rw-r--r--drivers/media/rc/nuvoton-cir.c12
-rw-r--r--drivers/media/rc/nuvoton-cir.h1
-rw-r--r--drivers/media/rc/rc-core-priv.h15
-rw-r--r--drivers/media/rc/rc-loopback.c2
-rw-r--r--drivers/media/rc/rc-main.c253
-rw-r--r--drivers/media/rc/redrat3.c2
-rw-r--r--drivers/media/rc/st_rc.c2
-rw-r--r--drivers/media/rc/streamzap.c2
-rw-r--r--drivers/media/rc/ttusbir.c2
-rw-r--r--drivers/media/rc/winbond-cir.c2
-rw-r--r--drivers/media/tuners/Kconfig1
-rw-r--r--drivers/media/tuners/e4000.c608
-rw-r--r--drivers/media/tuners/e4000.h21
-rw-r--r--drivers/media/tuners/e4000_priv.h88
-rw-r--r--drivers/media/tuners/mt2063.c4
-rw-r--r--drivers/media/tuners/r820t.c4
-rw-r--r--drivers/media/tuners/tda18212.c12
-rw-r--r--drivers/media/tuners/tda18212.h2
-rw-r--r--drivers/media/tuners/tuner-xc2028.c3
-rw-r--r--drivers/media/usb/au0828/au0828-cards.c23
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-input.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/Kconfig9
-rw-r--r--drivers/media/usb/dvb-usb-v2/Makefile4
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9035.c102
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9035.h2
-rw-r--r--drivers/media/usb/dvb-usb-v2/az6007.c4
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_core.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/it913x.c828
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.c123
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.h2
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-remote.c2
-rw-r--r--drivers/media/usb/em28xx/Kconfig2
-rw-r--r--drivers/media/usb/em28xx/em28xx-audio.c103
-rw-r--r--drivers/media/usb/em28xx/em28xx-camera.c4
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c129
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c54
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c193
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c41
-rw-r--r--drivers/media/usb/em28xx/em28xx-input.c55
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c56
-rw-r--r--drivers/media/usb/em28xx/em28xx.h15
-rw-r--r--drivers/media/usb/gspca/kinect.c7
-rw-r--r--drivers/media/usb/gspca/sn9c20x.c1
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c2
-rw-r--r--drivers/media/usb/gspca/topro.c10
-rw-r--r--drivers/media/usb/pwc/pwc-if.c19
-rw-r--r--drivers/media/usb/s2255/Kconfig2
-rw-r--r--drivers/media/usb/s2255/s2255drv.c1271
-rw-r--r--drivers/media/usb/siano/smsusb.c2
-rw-r--r--drivers/media/usb/stk1160/stk1160-v4l.c2
-rw-r--r--drivers/media/usb/tm6000/tm6000-alsa.c4
-rw-r--r--drivers/media/usb/tm6000/tm6000-dvb.c2
-rw-r--r--drivers/media/usb/tm6000/tm6000-input.c2
-rw-r--r--drivers/media/usb/tm6000/tm6000-stds.c2
-rw-r--r--drivers/media/usb/usbtv/Makefile3
-rw-r--r--drivers/media/usb/usbtv/usbtv-core.c134
-rw-r--r--drivers/media/usb/usbtv/usbtv-video.c (renamed from drivers/media/usb/usbtv/usbtv.c)171
-rw-r--r--drivers/media/usb/usbtv/usbtv.h99
-rw-r--r--drivers/media/usb/usbvision/usbvision.h8
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c24
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c29
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c11
-rw-r--r--drivers/media/usb/uvc/uvc_video.c23
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h16
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c133
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls.c33
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c32
-rw-r--r--drivers/media/v4l2-core/v4l2-dv-timings.c8
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c91
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c18
-rw-r--r--drivers/media/v4l2-core/videobuf2-core.c658
-rw-r--r--drivers/mfd/88pm800.c3
-rw-r--r--drivers/mfd/88pm860x-core.c6
-rw-r--r--drivers/mfd/Kconfig98
-rw-r--r--drivers/mfd/Makefile6
-rw-r--r--drivers/mfd/ab8500-core.c27
-rw-r--r--drivers/mfd/adp5520.c1
-rw-r--r--drivers/mfd/as3722.c1
-rw-r--r--drivers/mfd/bcm590xx.c93
-rw-r--r--drivers/mfd/cs5535-mfd.c1
-rw-r--r--drivers/mfd/da9052-core.c3
-rw-r--r--drivers/mfd/da9052-i2c.c5
-rw-r--r--drivers/mfd/da9052-spi.c1
-rw-r--r--drivers/mfd/da9055-i2c.c8
-rw-r--r--drivers/mfd/da9063-core.c25
-rw-r--r--drivers/mfd/db8500-prcmu.c34
-rw-r--r--drivers/mfd/janz-cmodio.c1
-rw-r--r--drivers/mfd/kempld-core.c31
-rw-r--r--drivers/mfd/lpc_ich.c151
-rw-r--r--drivers/mfd/lpc_sch.c1
-rw-r--r--drivers/mfd/max14577.c6
-rw-r--r--drivers/mfd/max77686.c4
-rw-r--r--drivers/mfd/max77693.c12
-rw-r--r--drivers/mfd/max8925-i2c.c9
-rw-r--r--drivers/mfd/max8997.c18
-rw-r--r--drivers/mfd/max8998.c4
-rw-r--r--drivers/mfd/mc13xxx-spi.c5
-rw-r--r--drivers/mfd/mcp-sa11x0.c1
-rw-r--r--drivers/mfd/omap-usb-host.c189
-rw-r--r--drivers/mfd/omap-usb-tll.c2
-rw-r--r--drivers/mfd/pcf50633-adc.c1
-rw-r--r--drivers/mfd/pm8921-core.c419
-rw-r--r--drivers/mfd/pm8xxx-irq.c371
-rw-r--r--drivers/mfd/rc5t583-irq.c1
-rw-r--r--drivers/mfd/rdc321x-southbridge.c1
-rw-r--r--drivers/mfd/retu-mfd.c1
-rw-r--r--drivers/mfd/rtsx_usb.c760
-rw-r--r--drivers/mfd/sec-core.c17
-rw-r--r--drivers/mfd/smsc-ece1099.c1
-rw-r--r--drivers/mfd/stmpe.c2
-rw-r--r--drivers/mfd/stw481x.c8
-rw-r--r--drivers/mfd/syscon.c9
-rw-r--r--drivers/mfd/tc3589x.c84
-rw-r--r--drivers/mfd/ti-ssp.c465
-rw-r--r--drivers/mfd/ti_am335x_tscadc.c24
-rw-r--r--drivers/mfd/timberdale.c2
-rw-r--r--drivers/mfd/tps65218.c282
-rw-r--r--drivers/mfd/tps65910.c5
-rw-r--r--drivers/mfd/tps65912-core.c1
-rw-r--r--drivers/mfd/tps65912-irq.c1
-rw-r--r--drivers/mfd/twl-core.c10
-rw-r--r--drivers/mfd/twl4030-irq.c1
-rw-r--r--drivers/mfd/twl6030-irq.c1
-rw-r--r--drivers/mfd/twl6040.c6
-rw-r--r--drivers/mfd/ucb1x00-core.c4
-rw-r--r--drivers/mfd/vexpress-config.c3
-rw-r--r--drivers/mfd/vexpress-sysreg.c2
-rw-r--r--drivers/mfd/wm5102-tables.c36
-rw-r--r--drivers/mfd/wm5110-tables.c174
-rw-r--r--drivers/mfd/wm8350-core.c1
-rw-r--r--drivers/mfd/wm8350-irq.c1
-rw-r--r--drivers/mfd/wm8400-core.c22
-rw-r--r--drivers/misc/mic/Kconfig2
-rw-r--r--drivers/mmc/core/host.c2
-rw-r--r--drivers/mmc/host/mmci.h9
-rw-r--r--drivers/mtd/Kconfig2
-rw-r--r--drivers/mtd/bcm47xxpart.c11
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c40
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c9
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0020.c3
-rw-r--r--drivers/mtd/chips/cfi_probe.c4
-rw-r--r--drivers/mtd/chips/cfi_util.c4
-rw-r--r--drivers/mtd/chips/gen_probe.c2
-rw-r--r--drivers/mtd/devices/Kconfig8
-rw-r--r--drivers/mtd/devices/Makefile1
-rw-r--r--drivers/mtd/devices/block2mtd.c19
-rw-r--r--drivers/mtd/devices/elm.c49
-rw-r--r--drivers/mtd/devices/m25p80.c35
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c24
-rw-r--r--drivers/mtd/devices/phram.c41
-rw-r--r--drivers/mtd/devices/pmc551.c7
-rw-r--r--drivers/mtd/devices/serial_flash_cmds.h81
-rw-r--r--drivers/mtd/devices/spear_smi.c2
-rw-r--r--drivers/mtd/devices/sst25l.c1
-rw-r--r--drivers/mtd/devices/st_spi_fsm.c2108
-rw-r--r--drivers/mtd/inftlmount.c1
-rw-r--r--drivers/mtd/lpddr/lpddr_cmds.c4
-rw-r--r--drivers/mtd/lpddr/qinfo_probe.c5
-rw-r--r--drivers/mtd/maps/Kconfig10
-rw-r--r--drivers/mtd/maps/bfin-async-flash.c1
-rw-r--r--drivers/mtd/maps/gpio-addr-flash.c1
-rw-r--r--drivers/mtd/maps/intel_vr_nor.c1
-rw-r--r--drivers/mtd/maps/ixp4xx.c1
-rw-r--r--drivers/mtd/maps/lantiq-flash.c1
-rw-r--r--drivers/mtd/maps/latch-addr-flash.c1
-rw-r--r--drivers/mtd/maps/pci.c1
-rw-r--r--drivers/mtd/maps/physmap_of.c1
-rw-r--r--drivers/mtd/maps/plat-ram.c2
-rw-r--r--drivers/mtd/maps/pxa2xx-flash.c1
-rw-r--r--drivers/mtd/maps/rbtx4939-flash.c1
-rw-r--r--drivers/mtd/maps/scb2_flash.c1
-rw-r--r--drivers/mtd/maps/sun_uflash.c1
-rw-r--r--drivers/mtd/mtd_blkdevs.c1
-rw-r--r--drivers/mtd/mtdchar.c20
-rw-r--r--drivers/mtd/mtdcore.c24
-rw-r--r--drivers/mtd/mtdpart.c14
-rw-r--r--drivers/mtd/nand/Kconfig2
-rw-r--r--drivers/mtd/nand/ams-delta.c1
-rw-r--r--drivers/mtd/nand/atmel_nand.c14
-rw-r--r--drivers/mtd/nand/au1550nd.c4
-rw-r--r--drivers/mtd/nand/bf5xx_nand.c1
-rw-r--r--drivers/mtd/nand/cafe_nand.c68
-rw-r--r--drivers/mtd/nand/davinci_nand.c23
-rw-r--r--drivers/mtd/nand/denali_dt.c39
-rw-r--r--drivers/mtd/nand/diskonchip.c5
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c1
-rw-r--r--drivers/mtd/nand/fsl_ifc_nand.c1
-rw-r--r--drivers/mtd/nand/gpio.c1
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c102
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c1
-rw-r--r--drivers/mtd/nand/mxc_nand.c2
-rw-r--r--drivers/mtd/nand/nand_base.c165
-rw-r--r--drivers/mtd/nand/nand_ids.c3
-rw-r--r--drivers/mtd/nand/nuc900_nand.c6
-rw-r--r--drivers/mtd/nand/omap2.c512
-rw-r--r--drivers/mtd/nand/pasemi_nand.c1
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c3
-rw-r--r--drivers/mtd/nand/s3c2410.c1
-rw-r--r--drivers/mtd/onenand/generic.c1
-rw-r--r--drivers/mtd/onenand/omap2.c1
-rw-r--r--drivers/mtd/onenand/onenand_base.c38
-rw-r--r--drivers/mtd/onenand/samsung.c4
-rw-r--r--drivers/mtd/rfd_ftl.c9
-rw-r--r--drivers/mtd/sm_ftl.c11
-rw-r--r--drivers/mtd/tests/mtd_test.c1
-rw-r--r--drivers/mtd/ubi/Kconfig16
-rw-r--r--drivers/mtd/ubi/Makefile1
-rw-r--r--drivers/mtd/ubi/block.c647
-rw-r--r--drivers/mtd/ubi/build.c11
-rw-r--r--drivers/mtd/ubi/cdev.c20
-rw-r--r--drivers/mtd/ubi/ubi.h21
-rw-r--r--drivers/net/Kconfig5
-rw-r--r--drivers/net/bonding/bond_3ad.c74
-rw-r--r--drivers/net/bonding/bond_3ad.h175
-rw-r--r--drivers/net/bonding/bond_alb.c138
-rw-r--r--drivers/net/bonding/bond_debugfs.c10
-rw-r--r--drivers/net/bonding/bond_main.c466
-rw-r--r--drivers/net/bonding/bond_netlink.c8
-rw-r--r--drivers/net/bonding/bond_options.c328
-rw-r--r--drivers/net/bonding/bond_options.h62
-rw-r--r--drivers/net/bonding/bond_procfs.c14
-rw-r--r--drivers/net/bonding/bond_sysfs.c24
-rw-r--r--drivers/net/bonding/bonding.h43
-rw-r--r--drivers/net/caif/caif_serial.c1
-rw-r--r--drivers/net/caif/caif_spi.c1
-rw-r--r--drivers/net/can/at91_can.c10
-rw-r--r--drivers/net/can/bfin_can.c1
-rw-r--r--drivers/net/can/c_can/c_can.c349
-rw-r--r--drivers/net/can/c_can/c_can.h29
-rw-r--r--drivers/net/can/c_can/c_can_platform.c47
-rw-r--r--drivers/net/can/cc770/cc770.c1
-rw-r--r--drivers/net/can/dev.c183
-rw-r--r--drivers/net/can/flexcan.c11
-rw-r--r--drivers/net/can/grcan.c1
-rw-r--r--drivers/net/can/janz-ican3.c65
-rw-r--r--drivers/net/can/mcp251x.c22
-rw-r--r--drivers/net/can/mscan/mscan.c7
-rw-r--r--drivers/net/can/pch_can.c1
-rw-r--r--drivers/net/can/sja1000/Kconfig13
-rw-r--r--drivers/net/can/sja1000/Makefile1
-rw-r--r--drivers/net/can/sja1000/ems_pci.c1
-rw-r--r--drivers/net/can/sja1000/ems_pcmcia.c1
-rw-r--r--drivers/net/can/sja1000/kvaser_pci.c1
-rw-r--r--drivers/net/can/sja1000/peak_pci.c1
-rw-r--r--drivers/net/can/sja1000/peak_pcmcia.c1
-rw-r--r--drivers/net/can/sja1000/plx_pci.c1
-rw-r--r--drivers/net/can/sja1000/sja1000.c10
-rw-r--r--drivers/net/can/sja1000/sja1000_of_platform.c220
-rw-r--r--drivers/net/can/sja1000/sja1000_platform.c194
-rw-r--r--drivers/net/can/slcan.c6
-rw-r--r--drivers/net/can/softing/softing_main.c2
-rw-r--r--drivers/net/can/ti_hecc.c1
-rw-r--r--drivers/net/can/usb/ems_usb.c1
-rw-r--r--drivers/net/can/usb/esd_usb2.c2
-rw-r--r--drivers/net/can/usb/kvaser_usb.c2
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_core.c2
-rw-r--r--drivers/net/can/usb/usb_8dev.c3
-rw-r--r--drivers/net/dummy.c12
-rw-r--r--drivers/net/ethernet/3com/3c509.c2
-rw-r--r--drivers/net/ethernet/3com/3c589_cs.c1127
-rw-r--r--drivers/net/ethernet/3com/3c59x.c2
-rw-r--r--drivers/net/ethernet/8390/lib8390.c2
-rw-r--r--drivers/net/ethernet/Kconfig2
-rw-r--r--drivers/net/ethernet/Makefile2
-rw-r--r--drivers/net/ethernet/adi/bfin_mac.c9
-rw-r--r--drivers/net/ethernet/aeroflex/greth.c6
-rw-r--r--drivers/net/ethernet/allwinner/sun4i-emac.c2
-rw-r--r--drivers/net/ethernet/altera/Kconfig8
-rw-r--r--drivers/net/ethernet/altera/Makefile7
-rw-r--r--drivers/net/ethernet/altera/altera_msgdma.c202
-rw-r--r--drivers/net/ethernet/altera/altera_msgdma.h34
-rw-r--r--drivers/net/ethernet/altera/altera_msgdmahw.h167
-rw-r--r--drivers/net/ethernet/altera/altera_sgdma.c509
-rw-r--r--drivers/net/ethernet/altera/altera_sgdma.h35
-rw-r--r--drivers/net/ethernet/altera/altera_sgdmahw.h124
-rw-r--r--drivers/net/ethernet/altera/altera_tse.h486
-rw-r--r--drivers/net/ethernet/altera/altera_tse_ethtool.c235
-rw-r--r--drivers/net/ethernet/altera/altera_tse_main.c1543
-rw-r--r--drivers/net/ethernet/altera/altera_utils.c44
-rw-r--r--drivers/net/ethernet/altera/altera_utils.h27
-rw-r--r--drivers/net/ethernet/amd/7990.c2
-rw-r--r--drivers/net/ethernet/amd/am79c961a.c2
-rw-r--r--drivers/net/ethernet/amd/amd8111e.c3
-rw-r--r--drivers/net/ethernet/amd/pcnet32.c124
-rw-r--r--drivers/net/ethernet/atheros/alx/main.c4
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_main.c34
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_main.c14
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl1.c15
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl2.c10
-rw-r--r--drivers/net/ethernet/broadcom/Kconfig11
-rw-r--r--drivers/net/ethernet/broadcom/Makefile1
-rw-r--r--drivers/net/ethernet/broadcom/b44.c8
-rw-r--r--drivers/net/ethernet/broadcom/bcm63xx_enet.c3
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.c24
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h45
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c162
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h48
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c8
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c11
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h1
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h31
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c385
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c155
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h21
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c1876
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h368
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c619
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h22
-rw-r--r--drivers/net/ethernet/broadcom/genet/Makefile2
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c2584
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.h628
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmmii.c464
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c30
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad.c65
-rw-r--r--drivers/net/ethernet/cadence/macb.c8
-rw-r--r--drivers/net/ethernet/calxeda/xgmac.c6
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c26
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/sge.c6
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4.h16
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c230
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c172
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c152
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_msg.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_regs.h9
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c43
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/sge.c6
-rw-r--r--drivers/net/ethernet/cirrus/cs89x0.c2
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c24
-rw-r--r--drivers/net/ethernet/davicom/dm9000.c2
-rw-r--r--drivers/net/ethernet/dec/tulip/dmfe.c4
-rw-r--r--drivers/net/ethernet/dec/tulip/uli526x.c4
-rw-r--r--drivers/net/ethernet/dlink/sundance.c2
-rw-r--r--drivers/net/ethernet/dnet.c6
-rw-r--r--drivers/net/ethernet/emulex/benet/Kconfig8
-rw-r--r--drivers/net/ethernet/emulex/benet/be.h27
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c213
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.h97
-rw-r--r--drivers/net/ethernet/emulex/benet/be_ethtool.c30
-rw-r--r--drivers/net/ethernet/emulex/benet/be_hw.h9
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c467
-rw-r--r--drivers/net/ethernet/emulex/benet/be_roce.c8
-rw-r--r--drivers/net/ethernet/emulex/benet/be_roce.h5
-rw-r--r--drivers/net/ethernet/ethoc.c6
-rw-r--r--drivers/net/ethernet/faraday/ftgmac100.c6
-rw-r--r--drivers/net/ethernet/freescale/Makefile3
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c19
-rw-r--r--drivers/net/ethernet/freescale/fec_ptp.c1
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c3
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-fec.c7
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c1327
-rw-r--r--drivers/net/ethernet/freescale/gianfar.h114
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ethtool.c165
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ptp.c1
-rw-r--r--drivers/net/ethernet/freescale/gianfar_sysfs.c340
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.c2
-rw-r--r--drivers/net/ethernet/i825xx/lib82596.c2
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_main.c6
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.c6
-rw-r--r--drivers/net/ethernet/intel/e100.c4
-rw-r--r--drivers/net/ethernet/intel/e1000e/80003es2lan.c47
-rw-r--r--drivers/net/ethernet/intel/e1000e/80003es2lan.h47
-rw-r--r--drivers/net/ethernet/intel/e1000e/82571.c47
-rw-r--r--drivers/net/ethernet/intel/e1000e/82571.h47
-rw-r--r--drivers/net/ethernet/intel/e1000e/Makefile7
-rw-r--r--drivers/net/ethernet/intel/e1000e/defines.h55
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h52
-rw-r--r--drivers/net/ethernet/intel/e1000e/ethtool.c64
-rw-r--r--drivers/net/ethernet/intel/e1000e/hw.h55
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.c427
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.h72
-rw-r--r--drivers/net/ethernet/intel/e1000e/mac.c47
-rw-r--r--drivers/net/ethernet/intel/e1000e/mac.h47
-rw-r--r--drivers/net/ethernet/intel/e1000e/manage.c47
-rw-r--r--drivers/net/ethernet/intel/e1000e/manage.h47
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c373
-rw-r--r--drivers/net/ethernet/intel/e1000e/nvm.c47
-rw-r--r--drivers/net/ethernet/intel/e1000e/nvm.h47
-rw-r--r--drivers/net/ethernet/intel/e1000e/param.c53
-rw-r--r--drivers/net/ethernet/intel/e1000e/phy.c47
-rw-r--r--drivers/net/ethernet/intel/e1000e/phy.h47
-rw-r--r--drivers/net/ethernet/intel/e1000e/ptp.c53
-rw-r--r--drivers/net/ethernet/intel/e1000e/regs.h48
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e.h50
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq.c5
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_common.c370
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_dcb.c9
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_debugfs.c87
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ethtool.c476
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c546
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_nvm.c117
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_prototype.h7
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.c434
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_type.h12
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c135
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h5
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h2
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_common.c369
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_prototype.h7
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_txrx.c90
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_type.h16
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf.h48
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c13
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_main.c299
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c27
-rw-r--r--drivers/net/ethernet/intel/igb/Makefile5
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.c15
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.h12
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_defines.h75
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_hw.h5
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_i210.c25
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_i210.h14
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mac.c5
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mac.h5
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mbx.c5
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mbx.h5
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_nvm.c5
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_nvm.h5
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.c76
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.h6
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_regs.h36
-rw-r--r--drivers/net/ethernet/intel/igb/igb.h18
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ethtool.c76
-rw-r--r--drivers/net/ethernet/intel/igb/igb_hwmon.c5
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c170
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ptp.c64
-rw-r--r--drivers/net/ethernet/intel/igbvf/netdev.c10
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_main.c6
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe.h9
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c33
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c360
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.c200
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.h14
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c18
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c11
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c17
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c212
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c45
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h8
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c39
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_type.h29
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c6
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ethtool.c141
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf.h12
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c161
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/regs.h12
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/vf.h33
-rw-r--r--drivers/net/ethernet/jme.c16
-rw-r--r--drivers/net/ethernet/marvell/mv643xx_eth.c4
-rw-r--r--drivers/net/ethernet/marvell/mvmdio.c6
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c20
-rw-r--r--drivers/net/ethernet/marvell/skge.c4
-rw-r--r--drivers/net/ethernet/marvell/sky2.c28
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/Kconfig9
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cmd.c179
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_clock.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_main.c45
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c128
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_port.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c28
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_selftest.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_tx.c40
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/eq.c48
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.c108
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c174
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mcg.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4.h14
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h19
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/port.c267
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/resource_tracker.c289
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c18
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mr.c85
-rw-r--r--drivers/net/ethernet/micrel/ksz884x.c2
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge.c40
-rw-r--r--drivers/net/ethernet/neterion/s2io.c14
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-main.c35
-rw-r--r--drivers/net/ethernet/nvidia/forcedeth.c57
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c11
-rw-r--r--drivers/net/ethernet/qlogic/Kconfig10
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c5
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic.h121
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c10
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h11
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c91
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c23
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c22
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h9
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c179
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c85
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c233
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c6
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c102
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_main.c25
-rw-r--r--drivers/net/ethernet/rdc/r6040.c6
-rw-r--r--drivers/net/ethernet/realtek/8139cp.c7
-rw-r--r--drivers/net/ethernet/realtek/8139too.c12
-rw-r--r--drivers/net/ethernet/realtek/r8169.c14
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c271
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.h3
-rw-r--r--drivers/net/ethernet/samsung/Kconfig16
-rw-r--r--drivers/net/ethernet/samsung/Makefile5
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/Kconfig9
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/Makefile4
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h535
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_core.c262
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_desc.c515
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_desc.h298
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_dma.c382
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_dma.h50
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c524
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c2317
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c244
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_mtl.c254
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_mtl.h104
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c259
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_reg.h488
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_xpcs.c91
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_xpcs.h38
-rw-r--r--drivers/net/ethernet/sfc/ef10.c27
-rw-r--r--drivers/net/ethernet/sfc/ef10_regs.h61
-rw-r--r--drivers/net/ethernet/sfc/efx.c33
-rw-r--r--drivers/net/ethernet/sfc/efx.h2
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c39
-rw-r--r--drivers/net/ethernet/sfc/falcon.c6
-rw-r--r--drivers/net/ethernet/sfc/farch.c5
-rw-r--r--drivers/net/ethernet/sfc/filter.h2
-rw-r--r--drivers/net/ethernet/sfc/mcdi.c14
-rw-r--r--drivers/net/ethernet/sfc/mcdi_port.c4
-rw-r--r--drivers/net/ethernet/sfc/net_driver.h1
-rw-r--r--drivers/net/ethernet/sfc/nic.c1
-rw-r--r--drivers/net/ethernet/sfc/ptp.c93
-rw-r--r--drivers/net/ethernet/sfc/selftest.c6
-rw-r--r--drivers/net/ethernet/sfc/siena_sriov.c13
-rw-r--r--drivers/net/ethernet/sfc/tx.c21
-rw-r--r--drivers/net/ethernet/silan/sc92031.c2
-rw-r--r--drivers/net/ethernet/sis/sis900.c2
-rw-r--r--drivers/net/ethernet/smsc/smc911x.c2
-rw-r--r--drivers/net/ethernet/smsc/smc91x.c4
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.c7
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig10
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile1
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c130
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h3
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c3
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c1
-rw-r--r--drivers/net/ethernet/sun/niu.c11
-rw-r--r--drivers/net/ethernet/sun/sungem.c2
-rw-r--r--drivers/net/ethernet/ti/cpsw.c34
-rw-r--r--drivers/net/ethernet/ti/cpts.c11
-rw-r--r--drivers/net/ethernet/tile/tilegx.c4
-rw-r--r--drivers/net/ethernet/tile/tilepro.c11
-rw-r--r--drivers/net/ethernet/toshiba/spider_net.c2
-rw-r--r--drivers/net/ethernet/toshiba/tc35815.c3
-rw-r--r--drivers/net/ethernet/via/via-rhine.c16
-rw-r--r--drivers/net/ethernet/via/via-velocity.c2
-rw-r--r--drivers/net/ethernet/wiznet/w5100.c9
-rw-r--r--drivers/net/ethernet/wiznet/w5300.c9
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_main.c4
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet_main.c2
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_emaclite.c15
-rw-r--r--drivers/net/ethernet/xscale/Kconfig1
-rw-r--r--drivers/net/ethernet/xscale/ixp4xx_eth.c11
-rw-r--r--drivers/net/hamradio/yam.c2
-rw-r--r--drivers/net/hyperv/hyperv_net.h207
-rw-r--r--drivers/net/hyperv/netvsc.c93
-rw-r--r--drivers/net/hyperv/netvsc_drv.c337
-rw-r--r--drivers/net/hyperv/rndis_filter.c187
-rw-r--r--drivers/net/ieee802154/Kconfig34
-rw-r--r--drivers/net/ieee802154/at86rf230.c520
-rw-r--r--drivers/net/ieee802154/fakehard.c22
-rw-r--r--drivers/net/ieee802154/mrf24j40.c17
-rw-r--r--drivers/net/ifb.c8
-rw-r--r--drivers/net/loopback.c16
-rw-r--r--drivers/net/macvlan.c13
-rw-r--r--drivers/net/nlmon.c18
-rw-r--r--drivers/net/phy/Kconfig6
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/at803x.c30
-rw-r--r--drivers/net/phy/bcm7xxx.c359
-rw-r--r--drivers/net/phy/broadcom.c52
-rw-r--r--drivers/net/phy/dp83640.c94
-rw-r--r--drivers/net/phy/mdio-sun4i.c6
-rw-r--r--drivers/net/phy/mdio_bus.c20
-rw-r--r--drivers/net/phy/micrel.c49
-rw-r--r--drivers/net/phy/phy.c51
-rw-r--r--drivers/net/phy/phy_device.c57
-rw-r--r--drivers/net/ppp/ppp_generic.c60
-rw-r--r--drivers/net/team/team.c28
-rw-r--r--drivers/net/team/team_mode_loadbalance.c4
-rw-r--r--drivers/net/tun.c8
-rw-r--r--drivers/net/usb/cdc_ether.c7
-rw-r--r--drivers/net/usb/cdc_ncm.c17
-rw-r--r--drivers/net/usb/lg-vl600.c2
-rw-r--r--drivers/net/usb/qmi_wwan.c7
-rw-r--r--drivers/net/usb/r8152.c1124
-rw-r--r--drivers/net/veth.c23
-rw-r--r--drivers/net/virtio_net.c12
-rw-r--r--drivers/net/vmxnet3/vmxnet3_drv.c112
-rw-r--r--drivers/net/vxlan.c13
-rw-r--r--drivers/net/wimax/i2400m/netdev.c3
-rw-r--r--drivers/net/wireless/Kconfig11
-rw-r--r--drivers/net/wireless/Makefile1
-rw-r--r--drivers/net/wireless/airo.c17
-rw-r--r--drivers/net/wireless/ath/ath.h15
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.c16
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.h9
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c36
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h81
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.h2
-rw-r--r--drivers/net/wireless/ath/ath10k/hif.h25
-rw-r--r--drivers/net/wireless/ath/ath10k/htc.c25
-rw-r--r--drivers/net/wireless/ath/ath10k/htt.h18
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c269
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_tx.c205
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.h6
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c850
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c536
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.h28
-rw-r--r--drivers/net/wireless/ath/ath10k/txrx.c32
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c132
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h34
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c3
-rw-r--r--drivers/net/wireless/ath/ath5k/mac80211-ops.c1
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c27
-rw-r--r--drivers/net/wireless/ath/ath6kl/usb.c6
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/Kconfig12
-rw-r--r--drivers/net/wireless/ath/ath9k/Makefile5
-rw-r--r--drivers/net/wireless/ath/ath9k/ahb.c11
-rw-r--r--drivers/net/wireless/ath/ath9k/ani.c52
-rw-r--r--drivers/net/wireless/ath/ath9k/ani.h4
-rw-r--r--drivers/net/wireless/ath/ath9k/ar5008_phy.c85
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_calib.c235
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.c63
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h35
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c203
-rw-r--r--drivers/net/wireless/ath/ath9k/common-beacon.c180
-rw-r--r--drivers/net/wireless/ath/ath9k/common-beacon.h26
-rw-r--r--drivers/net/wireless/ath/ath9k/common-init.c244
-rw-r--r--drivers/net/wireless/ath/ath9k/common-init.h20
-rw-r--r--drivers/net/wireless/ath/ath9k/common.c244
-rw-r--r--drivers/net/wireless/ath/ath9k/common.h35
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c86
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/dfs_debug.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/hif_usb.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h27
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_beacon.c260
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c240
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c54
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_txrx.c179
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_hst.c36
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_hst.h12
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c247
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c16
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h9
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c69
-rw-r--r--drivers/net/wireless/ath/ath9k/mci.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/rc.c1495
-rw-r--r--drivers/net/wireless/ath/ath9k/rc.h248
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c267
-rw-r--r--drivers/net/wireless/ath/ath9k/tx99.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/wow.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c18
-rw-r--r--drivers/net/wireless/ath/carl9170/rx.c2
-rw-r--r--drivers/net/wireless/ath/regd.c10
-rw-r--r--drivers/net/wireless/ath/wcn36xx/dxe.c10
-rw-r--r--drivers/net/wireless/ath/wcn36xx/dxe.h4
-rw-r--r--drivers/net/wireless/ath/wcn36xx/hal.h4
-rw-r--r--drivers/net/wireless/ath/wcn36xx/main.c72
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.c64
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.h5
-rw-r--r--drivers/net/wireless/ath/wcn36xx/txrx.c7
-rw-r--r--drivers/net/wireless/ath/wcn36xx/wcn36xx.h10
-rw-r--r--drivers/net/wireless/ath/wil6210/Makefile1
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c241
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c181
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c33
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c234
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c5
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c37
-rw-r--r--drivers/net/wireless/ath/wil6210/rx_reorder.c177
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c334
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.h7
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h164
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c136
-rw-r--r--drivers/net/wireless/atmel.c8
-rw-r--r--drivers/net/wireless/b43/Kconfig2
-rw-r--r--drivers/net/wireless/b43/debugfs.h2
-rw-r--r--drivers/net/wireless/b43/main.c2
-rw-r--r--drivers/net/wireless/b43/main.h35
-rw-r--r--drivers/net/wireless/b43/phy_common.c4
-rw-r--r--drivers/net/wireless/b43/pio.c10
-rw-r--r--drivers/net/wireless/b43/sysfs.c2
-rw-r--r--drivers/net/wireless/b43/xmit.c14
-rw-r--r--drivers/net/wireless/b43legacy/main.c4
-rw-r--r--drivers/net/wireless/b43legacy/sysfs.c2
-rw-r--r--drivers/net/wireless/b43legacy/xmit.c2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/Makefile4
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c107
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/chip.c1034
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/chip.h91
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c4
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c982
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwil.c5
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwil.h2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h10
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/p2p.c6
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c972
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h231
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h91
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c283
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h21
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c20
-rw-r--r--drivers/net/wireless/brcm80211/include/brcm_hw_ids.h1
-rw-r--r--drivers/net/wireless/brcm80211/include/brcmu_wifi.h3
-rw-r--r--drivers/net/wireless/cw1200/fwio.c4
-rw-r--r--drivers/net/wireless/hostap/hostap_cs.c2
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2100.c2
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2200.c2
-rw-r--r--drivers/net/wireless/iwlegacy/3945-mac.c13
-rw-r--r--drivers/net/wireless/iwlegacy/3945-rs.c3
-rw-r--r--drivers/net/wireless/iwlegacy/4965-mac.c15
-rw-r--r--drivers/net/wireless/iwlegacy/4965-rs.c3
-rw-r--r--drivers/net/wireless/iwlegacy/commands.h3
-rw-r--r--drivers/net/wireless/iwlegacy/common.c83
-rw-r--r--drivers/net/wireless/iwlegacy/common.h17
-rw-r--r--drivers/net/wireless/iwlwifi/Kconfig14
-rw-r--r--drivers/net/wireless/iwlwifi/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/agn.h4
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/devices.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/mac80211.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/main.c12
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rs.c23
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rs.h2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-7000.c27
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-8000.c132
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-config.h19
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-csr.h66
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debug.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.c58
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.h14
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h5
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-file.h17
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw.h47
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-io.c19
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-io.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-modparams.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-nvm-parse.c270
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-nvm-parse.h3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-op-mode.h35
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-phy-db.c4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-prph.h69
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h40
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/Makefile4
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/coex.c (renamed from drivers/net/wireless/iwlwifi/mvm/bt-coex.c)474
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/constants.h4
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/d3.c226
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c119
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs.c485
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h)21
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h14
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-power.h33
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h3
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h31
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h3
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h134
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h106
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c79
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/led.c2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c78
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c601
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h259
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/nvm.c87
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/offloading.c215
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c481
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c4
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/power.c426
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/power_legacy.c319
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/quota.c100
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.c210
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.h2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rx.c33
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/scan.c255
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c213
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.h62
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/time-event.c8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tt.c7
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tx.c43
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/utils.c138
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/drv.c85
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/internal.h4
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/rx.c52
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c410
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c73
-rw-r--r--drivers/net/wireless/libertas/cfg.c3
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c4
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c113
-rw-r--r--drivers/net/wireless/mac80211_hwsim.h6
-rw-r--r--drivers/net/wireless/mwifiex/11ac.c192
-rw-r--r--drivers/net/wireless/mwifiex/11ac.h2
-rw-r--r--drivers/net/wireless/mwifiex/11h.c4
-rw-r--r--drivers/net/wireless/mwifiex/11n.c106
-rw-r--r--drivers/net/wireless/mwifiex/11n.h58
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.c203
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.h3
-rw-r--r--drivers/net/wireless/mwifiex/Makefile1
-rw-r--r--drivers/net/wireless/mwifiex/README2
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c403
-rw-r--r--drivers/net/wireless/mwifiex/cfp.c205
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c161
-rw-r--r--drivers/net/wireless/mwifiex/debugfs.c8
-rw-r--r--drivers/net/wireless/mwifiex/decl.h23
-rw-r--r--drivers/net/wireless/mwifiex/fw.h202
-rw-r--r--drivers/net/wireless/mwifiex/ie.c6
-rw-r--r--drivers/net/wireless/mwifiex/init.c8
-rw-r--r--drivers/net/wireless/mwifiex/ioctl.h25
-rw-r--r--drivers/net/wireless/mwifiex/join.c66
-rw-r--r--drivers/net/wireless/mwifiex/main.c10
-rw-r--r--drivers/net/wireless/mwifiex/main.h112
-rw-r--r--drivers/net/wireless/mwifiex/pcie.c180
-rw-r--r--drivers/net/wireless/mwifiex/pcie.h5
-rw-r--r--drivers/net/wireless/mwifiex/scan.c615
-rw-r--r--drivers/net/wireless/mwifiex/sdio.c11
-rw-r--r--drivers/net/wireless/mwifiex/sdio.h6
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmd.c471
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmdresp.c130
-rw-r--r--drivers/net/wireless/mwifiex/sta_event.c52
-rw-r--r--drivers/net/wireless/mwifiex/sta_ioctl.c164
-rw-r--r--drivers/net/wireless/mwifiex/sta_rx.c34
-rw-r--r--drivers/net/wireless/mwifiex/sta_tx.c3
-rw-r--r--drivers/net/wireless/mwifiex/tdls.c1044
-rw-r--r--drivers/net/wireless/mwifiex/uap_cmd.c24
-rw-r--r--drivers/net/wireless/mwifiex/uap_event.c130
-rw-r--r--drivers/net/wireless/mwifiex/uap_txrx.c22
-rw-r--r--drivers/net/wireless/mwifiex/usb.c11
-rw-r--r--drivers/net/wireless/mwifiex/util.c118
-rw-r--r--drivers/net/wireless/mwifiex/util.h20
-rw-r--r--drivers/net/wireless/mwifiex/wmm.c131
-rw-r--r--drivers/net/wireless/mwifiex/wmm.h18
-rw-r--r--drivers/net/wireless/mwl8k.c197
-rw-r--r--drivers/net/wireless/orinoco/cfg.c5
-rw-r--r--drivers/net/wireless/orinoco/hw.c2
-rw-r--r--drivers/net/wireless/orinoco/scan.c5
-rw-r--r--drivers/net/wireless/orinoco/wext.c2
-rw-r--r--drivers/net/wireless/p54/p54usb.c6
-rw-r--r--drivers/net/wireless/prism54/isl_ioctl.c2
-rw-r--r--drivers/net/wireless/rndis_wlan.c7
-rw-r--r--drivers/net/wireless/rsi/Kconfig30
-rw-r--r--drivers/net/wireless/rsi/Makefile12
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_core.c342
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_debugfs.c339
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mac80211.c1008
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_main.c295
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mgmt.c1304
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_pkt.c196
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_sdio.c850
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_sdio_ops.c566
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_usb.c575
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_usb_ops.c177
-rw-r--r--drivers/net/wireless/rsi/rsi_boot_params.h126
-rw-r--r--drivers/net/wireless/rsi/rsi_common.h87
-rw-r--r--drivers/net/wireless/rsi/rsi_debugfs.h48
-rw-r--r--drivers/net/wireless/rsi/rsi_main.h218
-rw-r--r--drivers/net/wireless/rsi/rsi_mgmt.h285
-rw-r--r--drivers/net/wireless/rsi/rsi_sdio.h129
-rw-r--r--drivers/net/wireless/rsi/rsi_usb.h68
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.c10
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00debug.c2
-rw-r--r--drivers/net/wireless/rtl818x/Kconfig4
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180/Makefile2
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180/dev.c1009
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180/rtl8180.h76
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180/rtl8225.c23
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180/rtl8225se.c475
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180/rtl8225se.h61
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187/dev.c20
-rw-r--r--drivers/net/wireless/rtl818x/rtl818x.h269
-rw-r--r--drivers/net/wireless/rtlwifi/Kconfig27
-rw-r--r--drivers/net/wireless/rtlwifi/Makefile3
-rw-r--r--drivers/net/wireless/rtlwifi/btcoexist/Makefile7
-rw-r--r--drivers/net/wireless/rtlwifi/btcoexist/halbt_precomp.h75
-rw-r--r--drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.c3698
-rw-r--r--drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.h173
-rw-r--r--drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.c1011
-rw-r--r--drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h559
-rw-r--r--drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.c218
-rw-r--r--drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.h52
-rw-r--r--drivers/net/wireless/rtlwifi/core.c124
-rw-r--r--drivers/net/wireless/rtlwifi/core.h4
-rw-r--r--drivers/net/wireless/rtlwifi/pci.c131
-rw-r--r--drivers/net/wireless/rtlwifi/pci.h14
-rw-r--r--drivers/net/wireless/rtlwifi/ps.c119
-rw-r--r--drivers/net/wireless/rtlwifi/ps.h60
-rw-r--r--drivers/net/wireless/rtlwifi/rc.c3
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/Makefile1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/dm.c11
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/fw.c4
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/hw.c128
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/phy.c63
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/reg.h16
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/sw.c1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/trx.c14
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/trx.h8
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/hw.c34
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/phy.c71
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/reg.h16
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/trx.c7
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/trx.h7
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/hw.c31
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/phy.c71
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/trx.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/trx.h2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/dm.c50
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/hw.c18
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/phy.c429
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/reg.h14
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/rf.c6
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/trx.c5
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/trx.h7
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/hw.c48
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/phy.c87
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/reg.h12
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/rf.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/trx.c7
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/trx.h8
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/Makefile1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/def.h5
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/dm.c47
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/dm.h1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/fw.c260
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/fw.h18
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c8
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/hw.c127
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/phy.c530
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/phy.h21
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/reg.h16
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/sw.c13
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/trx.c11
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/trx.h13
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/Makefile19
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/def.h248
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/dm.c1325
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/dm.h310
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/fw.c620
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/fw.h248
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/hw.c2523
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/hw.h64
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/led.c153
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/led.h35
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/phy.c2156
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/phy.h217
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.c106
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.h304
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.c140
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.h95
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/reg.h2277
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/rf.c504
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/rf.h43
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/sw.c384
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/sw.h35
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/table.c572
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/table.h43
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/trx.c960
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/trx.h617
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723com/Makefile9
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723com/dm_common.c65
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723com/dm_common.h33
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723com/fw_common.c329
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723com/fw_common.h126
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723com/main.c33
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723com/phy_common.c434
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723com/phy_common.h89
-rw-r--r--drivers/net/wireless/rtlwifi/usb.c4
-rw-r--r--drivers/net/wireless/rtlwifi/wifi.h518
-rw-r--r--drivers/net/wireless/ti/wilink_platform_data.c37
-rw-r--r--drivers/net/wireless/ti/wl1251/cmd.c2
-rw-r--r--drivers/net/wireless/ti/wl1251/sdio.c31
-rw-r--r--drivers/net/wireless/ti/wl1251/spi.c71
-rw-r--r--drivers/net/wireless/ti/wl1251/wl1251.h4
-rw-r--r--drivers/net/wireless/ti/wl12xx/main.c67
-rw-r--r--drivers/net/wireless/ti/wl12xx/wl12xx.h53
-rw-r--r--drivers/net/wireless/ti/wl18xx/main.c85
-rw-r--r--drivers/net/wireless/ti/wl18xx/tx.c4
-rw-r--r--drivers/net/wireless/ti/wl18xx/wl18xx.h62
-rw-r--r--drivers/net/wireless/ti/wlcore/acx.c7
-rw-r--r--drivers/net/wireless/ti/wlcore/acx.h6
-rw-r--r--drivers/net/wireless/ti/wlcore/cmd.c24
-rw-r--r--drivers/net/wireless/ti/wlcore/cmd.h9
-rw-r--r--drivers/net/wireless/ti/wlcore/event.c4
-rw-r--r--drivers/net/wireless/ti/wlcore/hw_ops.h9
-rw-r--r--drivers/net/wireless/ti/wlcore/init.c6
-rw-r--r--drivers/net/wireless/ti/wlcore/io.h8
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c200
-rw-r--r--drivers/net/wireless/ti/wlcore/ps.c6
-rw-r--r--drivers/net/wireless/ti/wlcore/rx.c19
-rw-r--r--drivers/net/wireless/ti/wlcore/rx.h2
-rw-r--r--drivers/net/wireless/ti/wlcore/spi.c4
-rw-r--r--drivers/net/wireless/ti/wlcore/sysfs.c2
-rw-r--r--drivers/net/wireless/ti/wlcore/tx.c45
-rw-r--r--drivers/net/wireless/ti/wlcore/tx.h1
-rw-r--r--drivers/net/wireless/ti/wlcore/wlcore.h27
-rw-r--r--drivers/net/wireless/ti/wlcore/wlcore_i.h86
-rw-r--r--drivers/net/wireless/wl3501_cs.c6
-rw-r--r--drivers/net/wireless/zd1201.c9
-rw-r--r--drivers/net/xen-netback/common.h112
-rw-r--r--drivers/net/xen-netback/interface.c141
-rw-r--r--drivers/net/xen-netback/netback.c813
-rw-r--r--drivers/net/xen-netfront.c14
-rw-r--r--drivers/nfc/Kconfig12
-rw-r--r--drivers/nfc/Makefile1
-rw-r--r--drivers/nfc/pn533.c28
-rw-r--r--drivers/nfc/pn544/i2c.c194
-rw-r--r--drivers/nfc/pn544/pn544.c2
-rw-r--r--drivers/nfc/pn544/pn544.h3
-rw-r--r--drivers/nfc/port100.c25
-rw-r--r--drivers/nfc/trf7970a.c1370
-rw-r--r--drivers/of/Kconfig18
-rw-r--r--drivers/of/Makefile1
-rw-r--r--drivers/of/address.c8
-rw-r--r--drivers/of/base.c355
-rw-r--r--drivers/of/fdt.c145
-rw-r--r--drivers/of/of_mdio.c23
-rw-r--r--drivers/of/of_mtd.c34
-rw-r--r--drivers/of/of_net.c33
-rw-r--r--drivers/of/of_reserved_mem.c217
-rw-r--r--drivers/of/pdt.c3
-rw-r--r--drivers/of/selftest.c62
-rw-r--r--drivers/of/testcase-data/tests-phandle.dtsi3
-rw-r--r--drivers/oprofile/nmi_timer_int.c23
-rw-r--r--drivers/pci/slot.c6
-rw-r--r--drivers/phy/Kconfig2
-rw-r--r--drivers/pinctrl/Kconfig4
-rw-r--r--drivers/pinctrl/pinctrl-adi2.c1
-rw-r--r--drivers/pinctrl/pinctrl-baytrail.c14
-rw-r--r--drivers/pinctrl/pinctrl-coh901.c186
-rw-r--r--drivers/pinctrl/pinctrl-msm.c11
-rw-r--r--drivers/pinctrl/pinctrl-nomadik.c159
-rw-r--r--drivers/pinctrl/sirf/pinctrl-sirf.c14
-rw-r--r--drivers/power/reset/Kconfig2
-rw-r--r--drivers/power/reset/qnap-poweroff.c49
-rw-r--r--drivers/powercap/intel_rapl.c10
-rw-r--r--drivers/ptp/Kconfig1
-rw-r--r--drivers/ptp/ptp_chardev.c128
-rw-r--r--drivers/ptp/ptp_clock.c23
-rw-r--r--drivers/ptp/ptp_ixp46x.c1
-rw-r--r--drivers/ptp/ptp_pch.c1
-rw-r--r--drivers/ptp/ptp_private.h8
-rw-r--r--drivers/ptp/ptp_sysfs.c115
-rw-r--r--drivers/pwm/Kconfig30
-rw-r--r--drivers/pwm/Makefile3
-rw-r--r--drivers/pwm/pwm-atmel.c9
-rw-r--r--drivers/pwm/pwm-clps711x.c176
-rw-r--r--drivers/pwm/pwm-fsl-ftm.c495
-rw-r--r--drivers/pwm/pwm-lpss.c183
-rw-r--r--drivers/pwm/pwm-pxa.c4
-rw-r--r--drivers/pwm/pwm-samsung.c5
-rw-r--r--drivers/regulator/core.c2
-rw-r--r--drivers/reset/Kconfig2
-rw-r--r--drivers/reset/Makefile1
-rw-r--r--drivers/reset/core.c71
-rw-r--r--drivers/reset/sti/Kconfig15
-rw-r--r--drivers/reset/sti/Makefile4
-rw-r--r--drivers/reset/sti/reset-stih415.c112
-rw-r--r--drivers/reset/sti/reset-stih416.c143
-rw-r--r--drivers/reset/sti/reset-syscfg.c186
-rw-r--r--drivers/reset/sti/reset-syscfg.h69
-rw-r--r--drivers/rtc/Kconfig12
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/interface.c3
-rw-r--r--drivers/rtc/rtc-as3722.c5
-rw-r--r--drivers/rtc/rtc-at32ap700x.c4
-rw-r--r--drivers/rtc/rtc-at91sam9.c2
-rw-r--r--drivers/rtc/rtc-cmos.c8
-rw-r--r--drivers/rtc/rtc-coh901331.c18
-rw-r--r--drivers/rtc/rtc-da9052.c4
-rw-r--r--drivers/rtc/rtc-da9055.c4
-rw-r--r--drivers/rtc/rtc-davinci.c33
-rw-r--r--drivers/rtc/rtc-ds1305.c10
-rw-r--r--drivers/rtc/rtc-ds1307.c243
-rw-r--r--drivers/rtc/rtc-ds1347.c166
-rw-r--r--drivers/rtc/rtc-ds1390.c5
-rw-r--r--drivers/rtc/rtc-ds1511.c21
-rw-r--r--drivers/rtc/rtc-ds1553.c21
-rw-r--r--drivers/rtc/rtc-ds1672.c11
-rw-r--r--drivers/rtc/rtc-ds1742.c5
-rw-r--r--drivers/rtc/rtc-ds3232.c100
-rw-r--r--drivers/rtc/rtc-imxdi.c4
-rw-r--r--drivers/rtc/rtc-isl12057.c6
-rw-r--r--drivers/rtc/rtc-jz4740.c25
-rw-r--r--drivers/rtc/rtc-lpc32xx.c5
-rw-r--r--drivers/rtc/rtc-mc13xxx.c139
-rw-r--r--drivers/rtc/rtc-moxart.c4
-rw-r--r--drivers/rtc/rtc-mv.c12
-rw-r--r--drivers/rtc/rtc-nuc900.c5
-rw-r--r--drivers/rtc/rtc-palmas.c5
-rw-r--r--drivers/rtc/rtc-pm8xxx.c288
-rw-r--r--drivers/rtc/rtc-pxa.c1
-rw-r--r--drivers/rtc/rtc-rv3029c2.c12
-rw-r--r--drivers/rtc/rtc-rx8025.c1
-rw-r--r--drivers/rtc/rtc-s3c.c4
-rw-r--r--drivers/rtc/rtc-sirfsoc.c68
-rw-r--r--drivers/rtc/rtc-spear.c4
-rw-r--r--drivers/rtc/rtc-stk17ta8.c3
-rw-r--r--drivers/rtc/rtc-sunxi.c2
-rw-r--r--drivers/rtc/rtc-test.c9
-rw-r--r--drivers/rtc/rtc-tx4939.c4
-rw-r--r--drivers/rtc/rtc-vt8500.c28
-rw-r--r--drivers/rtc/rtc-x1205.c2
-rw-r--r--drivers/s390/kvm/virtio_ccw.c323
-rw-r--r--drivers/s390/net/qeth_core.h7
-rw-r--r--drivers/s390/net/qeth_core_main.c4
-rw-r--r--drivers/s390/net/qeth_l2_main.c6
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c12
-rw-r--r--drivers/scsi/bnx2i/bnx2i_init.c12
-rw-r--r--drivers/scsi/fcoe/fcoe.c15
-rw-r--r--drivers/scsi/libiscsi.c32
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c6
-rw-r--r--drivers/scsi/scsi_transport_srp.c1
-rw-r--r--drivers/sh/clk/cpg.c38
-rw-r--r--drivers/sh/intc/Kconfig2
-rw-r--r--drivers/staging/android/Kconfig2
-rw-r--r--drivers/staging/bcm/Kconfig1
-rw-r--r--drivers/staging/frontier/Kconfig1
-rw-r--r--drivers/staging/lustre/lustre/include/linux/lustre_compat25.h4
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_lib.c2
-rw-r--r--drivers/staging/lustre/lustre/lvfs/lvfs_linux.c2
-rw-r--r--drivers/staging/media/Kconfig2
-rw-r--r--drivers/staging/media/Makefile2
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c2
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c6
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_video.c3
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.c5
-rw-r--r--drivers/staging/media/go7007/Kconfig3
-rw-r--r--drivers/staging/media/go7007/go7007-v4l2.c5
-rw-r--r--drivers/staging/media/msi3101/Kconfig7
-rw-r--r--drivers/staging/media/msi3101/Makefile1
-rw-r--r--drivers/staging/media/msi3101/msi001.c500
-rw-r--r--drivers/staging/media/msi3101/sdr-msi3101.c1566
-rw-r--r--drivers/staging/media/omap4iss/iss_video.c2
-rw-r--r--drivers/staging/media/rtl2832u_sdr/Kconfig7
-rw-r--r--drivers/staging/media/rtl2832u_sdr/Makefile6
-rw-r--r--drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c1500
-rw-r--r--drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h54
-rw-r--r--drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c2
-rw-r--r--drivers/staging/media/solo6x10/solo6x10-v4l2.c2
-rw-r--r--drivers/staging/octeon/ethernet-tx.c6
-rw-r--r--drivers/staging/rtl8187se/Kconfig1
-rw-r--r--drivers/staging/rtl8188eu/Kconfig1
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/Kconfig1
-rw-r--r--drivers/staging/rtl8192u/Kconfig1
-rw-r--r--drivers/staging/rtl8712/Kconfig2
-rw-r--r--drivers/staging/rtl8821ae/rc.c1
-rw-r--r--drivers/staging/rtl8821ae/rtl8821ae/trx.c2
-rw-r--r--drivers/staging/speakup/kobjects.c62
-rw-r--r--drivers/staging/speakup/speakup.h2
-rw-r--r--drivers/staging/speakup/speakup_acntpc.c22
-rw-r--r--drivers/staging/speakup/speakup_acntsa.c22
-rw-r--r--drivers/staging/speakup/speakup_apollo.c24
-rw-r--r--drivers/staging/speakup/speakup_audptr.c24
-rw-r--r--drivers/staging/speakup/speakup_bns.c22
-rw-r--r--drivers/staging/speakup/speakup_decext.c24
-rw-r--r--drivers/staging/speakup/speakup_decpc.c24
-rw-r--r--drivers/staging/speakup/speakup_dectlk.c24
-rw-r--r--drivers/staging/speakup/speakup_dtlk.c28
-rw-r--r--drivers/staging/speakup/speakup_dummy.c22
-rw-r--r--drivers/staging/speakup/speakup_keypc.c18
-rw-r--r--drivers/staging/speakup/speakup_ltlk.c28
-rw-r--r--drivers/staging/speakup/speakup_soft.c30
-rw-r--r--drivers/staging/speakup/speakup_spkout.c24
-rw-r--r--drivers/staging/speakup/speakup_txprt.c22
-rw-r--r--drivers/staging/usbip/Kconfig4
-rw-r--r--drivers/staging/wlags49_h2/wl_netdev.c6
-rw-r--r--drivers/thermal/Kconfig2
-rw-r--r--drivers/thermal/x86_pkg_temp_thermal.c14
-rw-r--r--drivers/tty/serial/Kconfig2
-rw-r--r--drivers/tty/serial/atmel_serial.c49
-rw-r--r--drivers/usb/core/Kconfig1
-rw-r--r--drivers/usb/core/message.c2
-rw-r--r--drivers/usb/core/urb.c2
-rw-r--r--drivers/usb/gadget/lpc32xx_udc.c1
-rw-r--r--drivers/usb/host/Kconfig3
-rw-r--r--drivers/vfio/Kconfig1
-rw-r--r--drivers/vfio/vfio.c6
-rw-r--r--drivers/vfio/vfio_iommu_type1.c656
-rw-r--r--drivers/video/Kconfig2
-rw-r--r--drivers/video/atmel_lcdfb.c8
-rw-r--r--drivers/video/aty/atyfb_base.c7
-rw-r--r--drivers/video/aty/mach64_accel.c3
-rw-r--r--drivers/video/aty/mach64_cursor.c22
-rw-r--r--drivers/video/backlight/aat2870_bl.c2
-rw-r--r--drivers/video/backlight/adp8860_bl.c4
-rw-r--r--drivers/video/backlight/adp8870_bl.c4
-rw-r--r--drivers/video/backlight/backlight.c28
-rw-r--r--drivers/video/backlight/corgi_lcd.c4
-rw-r--r--drivers/video/backlight/hx8357.c4
-rw-r--r--drivers/video/backlight/ili922x.c4
-rw-r--r--drivers/video/backlight/ili9320.c4
-rw-r--r--drivers/video/backlight/l4f00242t03.c5
-rw-r--r--drivers/video/backlight/lm3533_bl.c5
-rw-r--r--drivers/video/backlight/lms283gf05.c4
-rw-r--r--drivers/video/backlight/platform_lcd.c4
-rw-r--r--drivers/video/backlight/tps65217_bl.c5
-rw-r--r--drivers/video/cfbcopyarea.c153
-rw-r--r--drivers/video/console/fbcon.c27
-rw-r--r--drivers/video/da8xx-fb.c22
-rw-r--r--drivers/video/efifb.c13
-rw-r--r--drivers/video/exynos/Kconfig2
-rw-r--r--drivers/video/exynos/s6e8ax0.c13
-rw-r--r--drivers/video/fbmem.c3
-rw-r--r--drivers/video/imxfb.c380
-rw-r--r--drivers/video/matrox/matroxfb_accel.c38
-rw-r--r--drivers/video/matrox/matroxfb_base.c3
-rw-r--r--drivers/video/matrox/matroxfb_base.h2
-rw-r--r--drivers/video/omap2/displays-new/connector-analog-tv.c45
-rw-r--r--drivers/video/omap2/displays-new/connector-dvi.c45
-rw-r--r--drivers/video/omap2/displays-new/connector-hdmi.c32
-rw-r--r--drivers/video/omap2/displays-new/encoder-tfp410.c43
-rw-r--r--drivers/video/omap2/displays-new/encoder-tpd12s015.c56
-rw-r--r--drivers/video/omap2/displays-new/panel-dsi-cm.c62
-rw-r--r--drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c2
-rw-r--r--drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c4
-rw-r--r--drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c2
-rw-r--r--drivers/video/omap2/displays-new/panel-sony-acx565akm.c35
-rw-r--r--drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c2
-rw-r--r--drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c2
-rw-r--r--drivers/video/omap2/dss/Makefile2
-rw-r--r--drivers/video/omap2/dss/dispc.c40
-rw-r--r--drivers/video/omap2/dss/display-sysfs.c4
-rw-r--r--drivers/video/omap2/dss/display.c32
-rw-r--r--drivers/video/omap2/dss/dpi.c72
-rw-r--r--drivers/video/omap2/dss/dsi.c163
-rw-r--r--drivers/video/omap2/dss/dss-of.c159
-rw-r--r--drivers/video/omap2/dss/dss.c82
-rw-r--r--drivers/video/omap2/dss/dss.h8
-rw-r--r--drivers/video/omap2/dss/hdmi4.c23
-rw-r--r--drivers/video/omap2/dss/hdmi_common.c74
-rw-r--r--drivers/video/omap2/dss/hdmi_wp.c2
-rw-r--r--drivers/video/omap2/dss/sdi.c60
-rw-r--r--drivers/video/omap2/dss/venc.c70
-rw-r--r--drivers/video/omap2/dss/venc_panel.c2
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c75
-rw-r--r--drivers/video/pxa3xx-gcu.c191
-rw-r--r--drivers/video/sis/init.c1
-rw-r--r--drivers/video/tgafb.c288
-rw-r--r--drivers/video/uvesafb.c15
-rw-r--r--drivers/video/vesafb.c13
-rw-r--r--drivers/video/xilinxfb.c15
-rw-r--r--drivers/virtio/virtio_balloon.c14
-rw-r--r--drivers/virtio/virtio_pci.c6
-rw-r--r--drivers/virtio/virtio_ring.c12
-rw-r--r--drivers/watchdog/Kconfig2
-rw-r--r--drivers/watchdog/iTCO_wdt.c137
-rw-r--r--drivers/watchdog/octeon-wdt-main.c11
-rw-r--r--drivers/watchdog/orion_wdt.c381
-rw-r--r--drivers/xen/balloon.c36
-rw-r--r--drivers/xen/events/events_base.c83
-rw-r--r--drivers/xen/events/events_internal.h1
-rw-r--r--drivers/xen/grant-table.c73
-rw-r--r--drivers/xen/manage.c16
-rw-r--r--drivers/xen/pcpu.c1
-rw-r--r--drivers/xen/platform-pci.c2
-rw-r--r--drivers/xen/xen-acpi-processor.c15
-rw-r--r--drivers/xen/xen-pciback/pciback_ops.c3
-rw-r--r--drivers/xen/xen-selfballoon.c1
-rw-r--r--drivers/xen/xenbus/xenbus_client.c27
-rw-r--r--fs/9p/vfs_inode.c2
-rw-r--r--fs/adfs/super.c1
-rw-r--r--fs/affs/inode.c2
-rw-r--r--fs/affs/super.c1
-rw-r--r--fs/afs/inode.c2
-rw-r--r--fs/befs/Makefile2
-rw-r--r--fs/befs/befs.h3
-rw-r--r--fs/befs/btree.c93
-rw-r--r--fs/befs/datastream.c87
-rw-r--r--fs/befs/debug.c74
-rw-r--r--fs/befs/inode.c10
-rw-r--r--fs/befs/io.c24
-rw-r--r--fs/befs/linuxvfs.c113
-rw-r--r--fs/bfs/inode.c2
-rw-r--r--fs/binfmt_elf.c9
-rw-r--r--fs/binfmt_misc.c1
-rw-r--r--fs/bio.c2
-rw-r--r--fs/block_dev.c6
-rw-r--r--fs/btrfs/async-thread.c848
-rw-r--r--fs/btrfs/async-thread.h121
-rw-r--r--fs/btrfs/backref.c84
-rw-r--r--fs/btrfs/btrfs_inode.h14
-rw-r--r--fs/btrfs/compression.c2
-rw-r--r--fs/btrfs/ctree.c11
-rw-r--r--fs/btrfs/ctree.h73
-rw-r--r--fs/btrfs/delayed-inode.c6
-rw-r--r--fs/btrfs/delayed-ref.c29
-rw-r--r--fs/btrfs/dev-replace.c79
-rw-r--r--fs/btrfs/disk-io.c281
-rw-r--r--fs/btrfs/extent-tree.c58
-rw-r--r--fs/btrfs/extent_io.c15
-rw-r--r--fs/btrfs/extent_map.c56
-rw-r--r--fs/btrfs/extent_map.h10
-rw-r--r--fs/btrfs/file.c160
-rw-r--r--fs/btrfs/inode.c123
-rw-r--r--fs/btrfs/ioctl.c210
-rw-r--r--fs/btrfs/ordered-data.c68
-rw-r--r--fs/btrfs/ordered-data.h6
-rw-r--r--fs/btrfs/qgroup.c15
-rw-r--r--fs/btrfs/raid56.c21
-rw-r--r--fs/btrfs/reada.c4
-rw-r--r--fs/btrfs/relocation.c2
-rw-r--r--fs/btrfs/root-tree.c3
-rw-r--r--fs/btrfs/scrub.c97
-rw-r--r--fs/btrfs/send.c821
-rw-r--r--fs/btrfs/super.c38
-rw-r--r--fs/btrfs/sysfs.c33
-rw-r--r--fs/btrfs/sysfs.h5
-rw-r--r--fs/btrfs/transaction.c39
-rw-r--r--fs/btrfs/tree-log.c236
-rw-r--r--fs/btrfs/tree-log.h18
-rw-r--r--fs/btrfs/volumes.c46
-rw-r--r--fs/btrfs/volumes.h1
-rw-r--r--fs/buffer.c2
-rw-r--r--fs/cachefiles/namei.c4
-rw-r--r--fs/cachefiles/rdwr.c33
-rw-r--r--fs/ceph/cache.c1
-rw-r--r--fs/ceph/cache.h10
-rw-r--r--fs/ceph/caps.c9
-rw-r--r--fs/ceph/debugfs.c5
-rw-r--r--fs/ceph/dir.c53
-rw-r--r--fs/ceph/export.c267
-rw-r--r--fs/ceph/file.c8
-rw-r--r--fs/ceph/inode.c76
-rw-r--r--fs/ceph/ioctl.c5
-rw-r--r--fs/ceph/locks.c98
-rw-r--r--fs/ceph/mds_client.c97
-rw-r--r--fs/ceph/mds_client.h4
-rw-r--r--fs/ceph/strings.c1
-rw-r--r--fs/ceph/super.c1
-rw-r--r--fs/ceph/super.h3
-rw-r--r--fs/ceph/xattr.c48
-rw-r--r--fs/cifs/cifsfs.c5
-rw-r--r--fs/coda/coda_int.h2
-rw-r--r--fs/coda/inode.c5
-rw-r--r--fs/compat.c35
-rw-r--r--fs/cramfs/inode.c4
-rw-r--r--fs/dcache.c50
-rw-r--r--fs/debugfs/inode.c7
-rw-r--r--fs/devpts/inode.c1
-rw-r--r--fs/direct-io.c19
-rw-r--r--fs/dlm/ast.c3
-rw-r--r--fs/dlm/dir.c4
-rw-r--r--fs/dlm/dlm_internal.h2
-rw-r--r--fs/dlm/lock.c7
-rw-r--r--fs/dlm/lockspace.c8
-rw-r--r--fs/dlm/member.c27
-rw-r--r--fs/dlm/recover.c10
-rw-r--r--fs/dlm/recoverd.c34
-rw-r--r--fs/drop_caches.c16
-rw-r--r--fs/ecryptfs/inode.c2
-rw-r--r--fs/ecryptfs/super.c2
-rw-r--r--fs/efs/super.c3
-rw-r--r--fs/exec.c2
-rw-r--r--fs/exofs/inode.c2
-rw-r--r--fs/ext2/inode.c2
-rw-r--r--fs/ext2/super.c1
-rw-r--r--fs/ext3/inode.c2
-rw-r--r--fs/ext3/super.c2
-rw-r--r--fs/ext4/ext4.h11
-rw-r--r--fs/ext4/ext4_jbd2.c10
-rw-r--r--fs/ext4/extents.c818
-rw-r--r--fs/ext4/extents_status.c28
-rw-r--r--fs/ext4/extents_status.h9
-rw-r--r--fs/ext4/file.c2
-rw-r--r--fs/ext4/inode.c124
-rw-r--r--fs/ext4/ioctl.c24
-rw-r--r--fs/ext4/mballoc.c7
-rw-r--r--fs/ext4/mballoc.h4
-rw-r--r--fs/ext4/move_extent.c5
-rw-r--r--fs/ext4/namei.c480
-rw-r--r--fs/ext4/super.c40
-rw-r--r--fs/ext4/xattr.c59
-rw-r--r--fs/ext4/xattr.h6
-rw-r--r--fs/f2fs/acl.c8
-rw-r--r--fs/f2fs/checkpoint.c208
-rw-r--r--fs/f2fs/data.c106
-rw-r--r--fs/f2fs/debug.c12
-rw-r--r--fs/f2fs/dir.c85
-rw-r--r--fs/f2fs/f2fs.h105
-rw-r--r--fs/f2fs/file.c31
-rw-r--r--fs/f2fs/gc.c16
-rw-r--r--fs/f2fs/inline.c4
-rw-r--r--fs/f2fs/inode.c29
-rw-r--r--fs/f2fs/namei.c9
-rw-r--r--fs/f2fs/node.c334
-rw-r--r--fs/f2fs/node.h25
-rw-r--r--fs/f2fs/recovery.c37
-rw-r--r--fs/f2fs/segment.c222
-rw-r--r--fs/f2fs/segment.h75
-rw-r--r--fs/f2fs/super.c99
-rw-r--r--fs/f2fs/xattr.c7
-rw-r--r--fs/fat/inode.c4
-rw-r--r--fs/fcntl.c37
-rw-r--r--fs/file_table.c2
-rw-r--r--fs/filesystems.c2
-rw-r--r--fs/freevxfs/vxfs_inode.c2
-rw-r--r--fs/freevxfs/vxfs_lookup.c2
-rw-r--r--fs/freevxfs/vxfs_super.c1
-rw-r--r--fs/fs-writeback.c33
-rw-r--r--fs/fuse/cuse.c9
-rw-r--r--fs/fuse/dir.c119
-rw-r--r--fs/fuse/file.c286
-rw-r--r--fs/fuse/fuse_i.h22
-rw-r--r--fs/fuse/inode.c32
-rw-r--r--fs/gfs2/acl.c23
-rw-r--r--fs/gfs2/acl.h2
-rw-r--r--fs/gfs2/aops.c132
-rw-r--r--fs/gfs2/bmap.c115
-rw-r--r--fs/gfs2/bmap.h2
-rw-r--r--fs/gfs2/dir.c23
-rw-r--r--fs/gfs2/file.c13
-rw-r--r--fs/gfs2/glock.c28
-rw-r--r--fs/gfs2/glops.c2
-rw-r--r--fs/gfs2/incore.h37
-rw-r--r--fs/gfs2/inode.c75
-rw-r--r--fs/gfs2/lock_dlm.c10
-rw-r--r--fs/gfs2/log.c102
-rw-r--r--fs/gfs2/lops.c85
-rw-r--r--fs/gfs2/lops.h5
-rw-r--r--fs/gfs2/main.c4
-rw-r--r--fs/gfs2/meta_io.c14
-rw-r--r--fs/gfs2/meta_io.h3
-rw-r--r--fs/gfs2/ops_fstype.c89
-rw-r--r--fs/gfs2/quota.c18
-rw-r--r--fs/gfs2/recovery.c16
-rw-r--r--fs/gfs2/recovery.h6
-rw-r--r--fs/gfs2/rgrp.c32
-rw-r--r--fs/gfs2/super.c41
-rw-r--r--fs/gfs2/sys.c7
-rw-r--r--fs/gfs2/trans.c29
-rw-r--r--fs/gfs2/util.c101
-rw-r--r--fs/gfs2/util.h31
-rw-r--r--fs/hfs/inode.c2
-rw-r--r--fs/hfs/super.c1
-rw-r--r--fs/hfsplus/attributes.c2
-rw-r--r--fs/hfsplus/extents.c16
-rw-r--r--fs/hfsplus/hfsplus_fs.h2
-rw-r--r--fs/hfsplus/super.c3
-rw-r--r--fs/hostfs/hostfs_kern.c2
-rw-r--r--fs/hpfs/inode.c2
-rw-r--r--fs/hpfs/super.c2
-rw-r--r--fs/hugetlbfs/inode.c17
-rw-r--r--fs/inode.c60
-rw-r--r--fs/isofs/inode.c1
-rw-r--r--fs/jbd2/commit.c77
-rw-r--r--fs/jbd2/journal.c10
-rw-r--r--fs/jbd2/transaction.c46
-rw-r--r--fs/jffs2/compr_rtime.c4
-rw-r--r--fs/jffs2/fs.c13
-rw-r--r--fs/jffs2/nodelist.h2
-rw-r--r--fs/jffs2/nodemgmt.c14
-rw-r--r--fs/jffs2/super.c1
-rw-r--r--fs/jfs/inode.c4
-rw-r--r--fs/jfs/super.c1
-rw-r--r--fs/kernfs/dir.c1
-rw-r--r--fs/kernfs/inode.c2
-rw-r--r--fs/locks.c389
-rw-r--r--fs/logfs/readwrite.c2
-rw-r--r--fs/mbcache.c540
-rw-r--r--fs/minix/inode.c5
-rw-r--r--fs/namei.c317
-rw-r--r--fs/ncpfs/inode.c3
-rw-r--r--fs/nfs/blocklayout/blocklayout.c2
-rw-r--r--fs/nfs/callback_proc.c19
-rw-r--r--fs/nfs/dir.c62
-rw-r--r--fs/nfs/inode.c36
-rw-r--r--fs/nfs/internal.h8
-rw-r--r--fs/nfs/nfs3proc.c36
-rw-r--r--fs/nfs/nfs4_fs.h11
-rw-r--r--fs/nfs/nfs4client.c7
-rw-r--r--fs/nfs/nfs4proc.c197
-rw-r--r--fs/nfs/nfs4state.c6
-rw-r--r--fs/nfs/nfs4super.c2
-rw-r--r--fs/nfs/nfs4xdr.c3
-rw-r--r--fs/nfs/pnfs.c17
-rw-r--r--fs/nfs/proc.c25
-rw-r--r--fs/nfs/super.c2
-rw-r--r--fs/nfs/unlink.c35
-rw-r--r--fs/nfsd/auth.c5
-rw-r--r--fs/nfsd/vfs.c2
-rw-r--r--fs/nilfs2/cpfile.c12
-rw-r--r--fs/nilfs2/dat.c12
-rw-r--r--fs/nilfs2/inode.c6
-rw-r--r--fs/nilfs2/ioctl.c137
-rw-r--r--fs/nilfs2/sufile.c295
-rw-r--r--fs/nilfs2/sufile.h2
-rw-r--r--fs/nilfs2/super.c1
-rw-r--r--fs/nilfs2/the_nilfs.c10
-rw-r--r--fs/notify/fanotify/fanotify.c63
-rw-r--r--fs/notify/fanotify/fanotify.h34
-rw-r--r--fs/notify/fanotify/fanotify_user.c197
-rw-r--r--fs/ntfs/inode.c2
-rw-r--r--fs/ntfs/super.c2
-rw-r--r--fs/ocfs2/acl.c1
-rw-r--r--fs/ocfs2/alloc.c3
-rw-r--r--fs/ocfs2/aops.c7
-rw-r--r--fs/ocfs2/aops.h5
-rw-r--r--fs/ocfs2/buffer_head_io.c2
-rw-r--r--fs/ocfs2/cluster/sys.c2
-rw-r--r--fs/ocfs2/cluster/tcp.c28
-rw-r--r--fs/ocfs2/dcache.c61
-rw-r--r--fs/ocfs2/dcache.h12
-rw-r--r--fs/ocfs2/dir.c6
-rw-r--r--fs/ocfs2/dlm/dlmdomain.c27
-rw-r--r--fs/ocfs2/dlm/dlmrecovery.c29
-rw-r--r--fs/ocfs2/dlmglue.c44
-rw-r--r--fs/ocfs2/dlmglue.h3
-rw-r--r--fs/ocfs2/file.c69
-rw-r--r--fs/ocfs2/inode.c61
-rw-r--r--fs/ocfs2/inode.h17
-rw-r--r--fs/ocfs2/ioctl.c5
-rw-r--r--fs/ocfs2/journal.c6
-rw-r--r--fs/ocfs2/journal.h11
-rw-r--r--fs/ocfs2/locks.c2
-rw-r--r--fs/ocfs2/move_extents.c7
-rw-r--r--fs/ocfs2/namei.c8
-rw-r--r--fs/ocfs2/ocfs2.h33
-rw-r--r--fs/ocfs2/quota.h2
-rw-r--r--fs/ocfs2/quota_global.c35
-rw-r--r--fs/ocfs2/stackglue.c22
-rw-r--r--fs/ocfs2/suballoc.c29
-rw-r--r--fs/ocfs2/suballoc.h4
-rw-r--r--fs/ocfs2/super.c55
-rw-r--r--fs/ocfs2/sysfile.c3
-rw-r--r--fs/ocfs2/xattr.c35
-rw-r--r--fs/omfs/inode.c2
-rw-r--r--fs/open.c29
-rw-r--r--fs/openpromfs/inode.c1
-rw-r--r--fs/posix_acl.c5
-rw-r--r--fs/proc/Makefile1
-rw-r--r--fs/proc/inode.c2
-rw-r--r--fs/proc/internal.h7
-rw-r--r--fs/proc/proc_devtree.c241
-rw-r--r--fs/proc/root.c5
-rw-r--r--fs/pstore/inode.c1
-rw-r--r--fs/pstore/platform.c1
-rw-r--r--fs/pstore/ram.c19
-rw-r--r--fs/pstore/ram_core.c4
-rw-r--r--fs/qnx4/inode.c1
-rw-r--r--fs/qnx6/inode.c1
-rw-r--r--fs/quota/dquot.c4
-rw-r--r--fs/reiserfs/inode.c2
-rw-r--r--fs/reiserfs/reiserfs.h1
-rw-r--r--fs/reiserfs/super.c4
-rw-r--r--fs/romfs/super.c1
-rw-r--r--fs/squashfs/super.c1
-rw-r--r--fs/super.c2
-rw-r--r--fs/sysv/inode.c3
-rw-r--r--fs/ubifs/super.c3
-rw-r--r--fs/udf/inode.c4
-rw-r--r--fs/udf/super.c1
-rw-r--r--fs/ufs/inode.c2
-rw-r--r--fs/ufs/super.c1
-rw-r--r--fs/xfs/kmem.c21
-rw-r--r--fs/xfs/xfs_acl.c2
-rw-r--r--fs/xfs/xfs_ag.h6
-rw-r--r--fs/xfs/xfs_alloc.c45
-rw-r--r--fs/xfs/xfs_alloc_btree.c16
-rw-r--r--fs/xfs/xfs_aops.c84
-rw-r--r--fs/xfs/xfs_attr_leaf.c17
-rw-r--r--fs/xfs/xfs_attr_remote.c15
-rw-r--r--fs/xfs/xfs_bmap.c193
-rw-r--r--fs/xfs/xfs_bmap.h15
-rw-r--r--fs/xfs/xfs_bmap_btree.c16
-rw-r--r--fs/xfs/xfs_bmap_util.c97
-rw-r--r--fs/xfs/xfs_bmap_util.h2
-rw-r--r--fs/xfs/xfs_btree.c14
-rw-r--r--fs/xfs/xfs_buf.c11
-rw-r--r--fs/xfs/xfs_buf.h14
-rw-r--r--fs/xfs/xfs_buf_item.c19
-rw-r--r--fs/xfs/xfs_da_btree.c19
-rw-r--r--fs/xfs/xfs_dinode.h2
-rw-r--r--fs/xfs/xfs_dir2.c342
-rw-r--r--fs/xfs/xfs_dir2_block.c17
-rw-r--r--fs/xfs/xfs_dir2_data.c20
-rw-r--r--fs/xfs/xfs_dir2_leaf.c17
-rw-r--r--fs/xfs/xfs_dir2_node.c17
-rw-r--r--fs/xfs/xfs_dquot.c2
-rw-r--r--fs/xfs/xfs_dquot_buf.c11
-rw-r--r--fs/xfs/xfs_error.c27
-rw-r--r--fs/xfs/xfs_error.h1
-rw-r--r--fs/xfs/xfs_file.c26
-rw-r--r--fs/xfs/xfs_format.h2
-rw-r--r--fs/xfs/xfs_ialloc.c36
-rw-r--r--fs/xfs/xfs_ialloc_btree.c16
-rw-r--r--fs/xfs/xfs_inode.c123
-rw-r--r--fs/xfs/xfs_inode.h12
-rw-r--r--fs/xfs/xfs_inode_buf.c7
-rw-r--r--fs/xfs/xfs_iomap.c10
-rw-r--r--fs/xfs/xfs_iops.c30
-rw-r--r--fs/xfs/xfs_linux.h2
-rw-r--r--fs/xfs/xfs_log.h2
-rw-r--r--fs/xfs/xfs_log_cil.c55
-rw-r--r--fs/xfs/xfs_mount.c3
-rw-r--r--fs/xfs/xfs_rtalloc.c2
-rw-r--r--fs/xfs/xfs_sb.c17
-rw-r--r--fs/xfs/xfs_sb.h2
-rw-r--r--fs/xfs/xfs_shared.h4
-rw-r--r--fs/xfs/xfs_super.c3
-rw-r--r--fs/xfs/xfs_symlink.c9
-rw-r--r--fs/xfs/xfs_symlink_remote.c16
-rw-r--r--fs/xfs/xfs_trace.h1
-rw-r--r--fs/xfs/xfs_trans.c12
-rw-r--r--fs/xfs/xfs_trans_buf.c11
-rw-r--r--fs/xfs/xfs_trans_resv.c82
-rw-r--r--fs/xfs/xfs_trans_resv.h3
-rw-r--r--include/acpi/actbl2.h15
-rw-r--r--include/asm-generic/gpio.h2
-rw-r--r--include/asm-generic/vmlinux.lds.h21
-rw-r--r--include/crypto/algapi.h9
-rw-r--r--include/crypto/null.h11
-rw-r--r--include/drm/drm_fb_helper.h2
-rw-r--r--include/dt-bindings/clock/bcm281xx.h65
-rw-r--r--include/dt-bindings/clock/exynos-audss-clk.h (renamed from include/dt-bindings/clk/exynos-audss-clk.h)0
-rw-r--r--include/dt-bindings/clock/hi3620-clock.h5
-rw-r--r--include/dt-bindings/clock/hip04-clock.h (renamed from arch/arm/mach-at91/include/mach/timex.h)34
-rw-r--r--include/dt-bindings/clock/r8a7790-clock.h4
-rw-r--r--include/dt-bindings/pinctrl/am43xx.h1
-rw-r--r--include/dt-bindings/reset-controller/stih415-resets.h26
-rw-r--r--include/dt-bindings/reset-controller/stih416-resets.h50
-rw-r--r--include/linux/backing-dev.h2
-rw-r--r--include/linux/backlight.h6
-rw-r--r--include/linux/basic_mmio_gpio.h1
-rw-r--r--include/linux/brcmphy.h60
-rw-r--r--include/linux/can/dev.h9
-rw-r--r--include/linux/ccp.h7
-rw-r--r--include/linux/ceph/ceph_features.h12
-rw-r--r--include/linux/ceph/ceph_fs.h5
-rw-r--r--include/linux/ceph/osd_client.h11
-rw-r--r--include/linux/ceph/osdmap.h50
-rw-r--r--include/linux/ceph/rados.h18
-rw-r--r--include/linux/cgroup.h275
-rw-r--r--include/linux/cgroup_subsys.h30
-rw-r--r--include/linux/clk-provider.h8
-rw-r--r--include/linux/clk.h14
-rw-r--r--include/linux/clk/zynq.h2
-rw-r--r--include/linux/cpu.h47
-rw-r--r--include/linux/cpuidle.h23
-rw-r--r--include/linux/cpuset.h27
-rw-r--r--include/linux/cred.h2
-rw-r--r--include/linux/crush/crush.h7
-rw-r--r--include/linux/dcache.h8
-rw-r--r--include/linux/decompress/inflate.h4
-rw-r--r--include/linux/device-mapper.h8
-rw-r--r--include/linux/dmar.h82
-rw-r--r--include/linux/err.h7
-rw-r--r--include/linux/ethtool.h3
-rw-r--r--include/linux/f2fs_fs.h2
-rw-r--r--include/linux/filter.h118
-rw-r--r--include/linux/fs.h60
-rw-r--r--include/linux/fsnotify_backend.h2
-rw-r--r--include/linux/ftrace.h27
-rw-r--r--include/linux/ftrace_event.h28
-rw-r--r--include/linux/gpio.h2
-rw-r--r--include/linux/gpio/consumer.h15
-rw-r--r--include/linux/gpio/driver.h48
-rw-r--r--include/linux/hid-sensor-hub.h9
-rw-r--r--include/linux/hid-sensor-ids.h16
-rw-r--r--include/linux/hid.h76
-rw-r--r--include/linux/hsi/hsi.h2
-rw-r--r--include/linux/hugetlb.h10
-rw-r--r--include/linux/hugetlb_cgroup.h2
-rw-r--r--include/linux/i2c/adp5588.h4
-rw-r--r--include/linux/i2c/twl.h12
-rw-r--r--include/linux/i2c/twl4030-madc.h2
-rw-r--r--include/linux/idr.h1
-rw-r--r--include/linux/ieee80211.h239
-rw-r--r--include/linux/if_vlan.h9
-rw-r--r--include/linux/input/pmic8xxx-keypad.h52
-rw-r--r--include/linux/input/pmic8xxx-pwrkey.h31
-rw-r--r--include/linux/intel-iommu.h1
-rw-r--r--include/linux/iova.h2
-rw-r--r--include/linux/irqchip/arm-gic.h7
-rw-r--r--include/linux/irqchip/arm-vic.h6
-rw-r--r--include/linux/irqchip/irq-crossbar.h (renamed from arch/arm/mach-mmp/include/mach/timex.h)12
-rw-r--r--include/linux/isapnp.h4
-rw-r--r--include/linux/isdn_ppp.h5
-rw-r--r--include/linux/kernel.h9
-rw-r--r--include/linux/kfifo.h2
-rw-r--r--include/linux/kmemleak.h2
-rw-r--r--include/linux/kobject.h1
-rw-r--r--include/linux/kvm_host.h20
-rw-r--r--include/linux/list_lru.h8
-rw-r--r--include/linux/mbcache.h12
-rw-r--r--include/linux/memblock.h2
-rw-r--r--include/linux/memcontrol.h2
-rw-r--r--include/linux/mfd/abx500/ab8500.h2
-rw-r--r--include/linux/mfd/arizona/registers.h84
-rw-r--r--include/linux/mfd/bcm590xx.h31
-rw-r--r--include/linux/mfd/da9052/da9052.h1
-rw-r--r--include/linux/mfd/da9063/core.h6
-rw-r--r--include/linux/mfd/da9063/registers.h120
-rw-r--r--include/linux/mfd/dbx500-prcmu.h2
-rw-r--r--include/linux/mfd/lpc_ich.h25
-rw-r--r--include/linux/mfd/max14577-private.h8
-rw-r--r--include/linux/mfd/max14577.h5
-rw-r--r--include/linux/mfd/pm8xxx/irq.h59
-rw-r--r--include/linux/mfd/pm8xxx/rtc.h25
-rw-r--r--include/linux/mfd/rtsx_usb.h628
-rw-r--r--include/linux/mfd/syscon/imx6q-iomuxc-gpr.h18
-rw-r--r--include/linux/mfd/tps65218.h284
-rw-r--r--include/linux/miscdevice.h1
-rw-r--r--include/linux/mlx4/cmd.h8
-rw-r--r--include/linux/mlx4/device.h50
-rw-r--r--include/linux/mlx4/driver.h12
-rw-r--r--include/linux/mlx4/qp.h11
-rw-r--r--include/linux/mlx5/cq.h1
-rw-r--r--include/linux/mlx5/device.h43
-rw-r--r--include/linux/mlx5/driver.h41
-rw-r--r--include/linux/mlx5/qp.h67
-rw-r--r--include/linux/mm.h11
-rw-r--r--include/linux/mmc/sdio_ids.h1
-rw-r--r--include/linux/mmzone.h6
-rw-r--r--include/linux/module.h21
-rw-r--r--include/linux/moduleparam.h10
-rw-r--r--include/linux/mpls.h6
-rw-r--r--include/linux/mtd/mtd.h16
-rw-r--r--include/linux/mtd/nand.h135
-rw-r--r--include/linux/netdevice.h157
-rw-r--r--include/linux/netfilter/ipset/ip_set.h15
-rw-r--r--include/linux/netfilter/nfnetlink.h21
-rw-r--r--include/linux/netpoll.h71
-rw-r--r--include/linux/nfs_fs.h2
-rw-r--r--include/linux/nfs_xdr.h3
-rw-r--r--include/linux/nilfs2_fs.h52
-rw-r--r--include/linux/nl802154.h12
-rw-r--r--include/linux/of.h61
-rw-r--r--include/linux/of_fdt.h4
-rw-r--r--include/linux/of_mtd.h12
-rw-r--r--include/linux/of_reserved_mem.h53
-rw-r--r--include/linux/omap-dma.h25
-rw-r--r--include/linux/pagemap.h43
-rw-r--r--include/linux/pagevec.h5
-rw-r--r--include/linux/perf_event.h16
-rw-r--r--include/linux/phy.h56
-rw-r--r--include/linux/pipe_fs_i.h2
-rw-r--r--include/linux/platform_data/ata-samsung_cf.h9
-rw-r--r--include/linux/platform_data/atmel.h1
-rw-r--r--include/linux/platform_data/clk-integrator.h1
-rw-r--r--include/linux/platform_data/elm.h10
-rw-r--r--include/linux/platform_data/gpio-davinci.h4
-rw-r--r--include/linux/platform_data/mtd-davinci-aemif.h5
-rw-r--r--include/linux/platform_data/mtd-nand-s3c2410.h8
-rw-r--r--include/linux/platform_data/touchscreen-s3c2410.h17
-rw-r--r--include/linux/platform_data/video-imxfb.h12
-rw-r--r--include/linux/printk.h16
-rw-r--r--include/linux/ptp_classify.h103
-rw-r--r--include/linux/ptp_clock_kernel.h33
-rw-r--r--include/linux/pwm.h2
-rw-r--r--include/linux/pxa2xx_ssp.h2
-rw-r--r--include/linux/quotaops.h8
-rw-r--r--include/linux/radix-tree.h55
-rw-r--r--include/linux/random.h16
-rw-r--r--include/linux/reset.h65
-rw-r--r--include/linux/seccomp.h1
-rw-r--r--include/linux/security.h12
-rw-r--r--include/linux/serial_s3c.h2
-rw-r--r--include/linux/sh_clk.h19
-rw-r--r--include/linux/shmem_fs.h1
-rw-r--r--include/linux/skbuff.h106
-rw-r--r--include/linux/spi/spi.h8
-rw-r--r--include/linux/ssbi.h20
-rw-r--r--include/linux/sunrpc/bc_xprt.h3
-rw-r--r--include/linux/swap.h36
-rw-r--r--include/linux/sxgbe_platform.h54
-rw-r--r--include/linux/syscalls.h2
-rw-r--r--include/linux/sysfs.h3
-rw-r--r--include/linux/tcp.h8
-rw-r--r--include/linux/tracepoint.h18
-rw-r--r--include/linux/tty.h6
-rw-r--r--include/linux/u64_stats_sync.h16
-rw-r--r--include/linux/uinput.h2
-rw-r--r--include/linux/uprobes.h1
-rw-r--r--include/linux/usb/cdc_ncm.h1
-rw-r--r--include/linux/usb/composite.h2
-rw-r--r--include/linux/vfio.h2
-rw-r--r--include/linux/vm_event_item.h1
-rw-r--r--include/linux/vmstat.h17
-rw-r--r--include/linux/wait.h11
-rw-r--r--include/linux/wl12xx.h24
-rw-r--r--include/linux/xilinxfb.h30
-rw-r--r--include/media/adv7842.h3
-rw-r--r--include/media/lm3646.h87
-rw-r--r--include/media/rc-core.h80
-rw-r--r--include/media/rc-map.h6
-rw-r--r--include/media/v4l2-dev.h3
-rw-r--r--include/media/v4l2-ioctl.h10
-rw-r--r--include/media/v4l2-subdev.h9
-rw-r--r--include/media/videobuf2-core.h117
-rw-r--r--include/net/6lowpan.h (renamed from net/ieee802154/6lowpan.h)115
-rw-r--r--include/net/act_api.h22
-rw-r--r--include/net/addrconf.h6
-rw-r--r--include/net/af_ieee802154.h4
-rw-r--r--include/net/bluetooth/bluetooth.h1
-rw-r--r--include/net/bluetooth/hci.h76
-rw-r--r--include/net/bluetooth/hci_core.h209
-rw-r--r--include/net/bluetooth/l2cap.h8
-rw-r--r--include/net/bluetooth/mgmt.h63
-rw-r--r--include/net/bluetooth/rfcomm.h10
-rw-r--r--include/net/cfg80211.h90
-rw-r--r--include/net/checksum.h23
-rw-r--r--include/net/cls_cgroup.h2
-rw-r--r--include/net/dst.h18
-rw-r--r--include/net/flow.h6
-rw-r--r--include/net/flowcache.h25
-rw-r--r--include/net/ieee80211_radiotap.h4
-rw-r--r--include/net/ieee802154.h28
-rw-r--r--include/net/ieee802154_netdev.h195
-rw-r--r--include/net/ip.h13
-rw-r--r--include/net/ip6_fib.h3
-rw-r--r--include/net/ip6_route.h14
-rw-r--r--include/net/mac80211.h107
-rw-r--r--include/net/mac802154.h41
-rw-r--r--include/net/net_namespace.h4
-rw-r--r--include/net/netfilter/nf_conntrack.h11
-rw-r--r--include/net/netfilter/nf_conntrack_core.h9
-rw-r--r--include/net/netfilter/nf_conntrack_labels.h4
-rw-r--r--include/net/netfilter/nf_tables.h28
-rw-r--r--include/net/netns/conntrack.h13
-rw-r--r--include/net/netns/ieee802154_6lowpan.h22
-rw-r--r--include/net/netns/xfrm.h11
-rw-r--r--include/net/netprio_cgroup.h17
-rw-r--r--include/net/nfc/digital.h7
-rw-r--r--include/net/nfc/nfc.h3
-rw-r--r--include/net/nl802154.h6
-rw-r--r--include/net/regulatory.h21
-rw-r--r--include/net/route.h1
-rw-r--r--include/net/rtnetlink.h2
-rw-r--r--include/net/sock.h39
-rw-r--r--include/net/tc_act/tc_csum.h4
-rw-r--r--include/net/tc_act/tc_defact.h4
-rw-r--r--include/net/tc_act/tc_gact.h4
-rw-r--r--include/net/tc_act/tc_ipt.h4
-rw-r--r--include/net/tc_act/tc_mirred.h4
-rw-r--r--include/net/tc_act/tc_nat.h4
-rw-r--r--include/net/tc_act/tc_pedit.h4
-rw-r--r--include/net/tc_act/tc_skbedit.h4
-rw-r--r--include/net/tcp.h13
-rw-r--r--include/net/wpan-phy.h19
-rw-r--r--include/net/xfrm.h128
-rw-r--r--include/rdma/ib_cm.h1
-rw-r--r--include/rdma/ib_umem.h11
-rw-r--r--include/rdma/ib_verbs.h201
-rw-r--r--include/scsi/libiscsi.h4
-rw-r--r--include/scsi/scsi_transport_iscsi.h1
-rw-r--r--include/scsi/scsi_transport_srp.h1
-rw-r--r--include/trace/events/btrfs.h137
-rw-r--r--include/trace/events/ext4.h102
-rw-r--r--include/trace/events/migrate.h2
-rw-r--r--include/trace/events/module.h4
-rw-r--r--include/trace/events/net.h12
-rw-r--r--include/trace/events/v4l2.h1
-rw-r--r--include/trace/events/writeback.h1
-rw-r--r--include/trace/ftrace.h38
-rw-r--r--include/uapi/asm-generic/fcntl.h19
-rw-r--r--include/uapi/linux/can.h32
-rw-r--r--include/uapi/linux/can/netlink.h3
-rw-r--r--include/uapi/linux/capi.h2
-rw-r--r--include/uapi/linux/ethtool.h439
-rw-r--r--include/uapi/linux/falloc.h35
-rw-r--r--include/uapi/linux/fs.h3
-rw-r--r--include/uapi/linux/fuse.h7
-rw-r--r--include/uapi/linux/gfs2_ondisk.h12
-rw-r--r--include/uapi/linux/if.h134
-rw-r--r--include/uapi/linux/if_ether.h6
-rw-r--r--include/uapi/linux/if_link.h1
-rw-r--r--include/uapi/linux/in.h4
-rw-r--r--include/uapi/linux/in6.h4
-rw-r--r--include/uapi/linux/kvm.h83
-rw-r--r--include/uapi/linux/libc-compat.h9
-rw-r--r--include/uapi/linux/mpls.h34
-rw-r--r--include/uapi/linux/netdevice.h6
-rw-r--r--include/uapi/linux/netfilter/ipset/ip_set.h12
-rw-r--r--include/uapi/linux/netfilter/nf_tables.h6
-rw-r--r--include/uapi/linux/nfc.h9
-rw-r--r--include/uapi/linux/nl80211.h92
-rw-r--r--include/uapi/linux/pfkeyv2.h15
-rw-r--r--include/uapi/linux/ptp_clock.h39
-rw-r--r--include/uapi/linux/snmp.h6
-rw-r--r--include/uapi/linux/tcp.h3
-rw-r--r--include/uapi/linux/tcp_metrics.h7
-rw-r--r--include/uapi/linux/uhid.h23
-rw-r--r--include/uapi/linux/uinput.h13
-rw-r--r--include/uapi/linux/usb/cdc.h12
-rw-r--r--include/uapi/linux/v4l2-common.h8
-rw-r--r--include/uapi/linux/v4l2-controls.h19
-rw-r--r--include/uapi/linux/v4l2-dv-timings.h17
-rw-r--r--include/uapi/linux/v4l2-subdev.h14
-rw-r--r--include/uapi/linux/vfio.h6
-rw-r--r--include/uapi/linux/videodev2.h74
-rw-r--r--include/uapi/linux/xattr.h7
-rw-r--r--include/uapi/linux/xfrm.h10
-rw-r--r--include/uapi/mtd/ubi-user.h22
-rw-r--r--include/video/omapdss.h19
-rw-r--r--include/xen/events.h6
-rw-r--r--include/xen/interface/physdev.h10
-rw-r--r--include/xen/xen-ops.h4
-rw-r--r--include/xen/xenbus.h1
-rw-r--r--init/Kconfig21
-rw-r--r--init/do_mounts.c4
-rw-r--r--kernel/capability.c29
-rw-r--r--kernel/cgroup.c3711
-rw-r--r--kernel/cgroup_freezer.c40
-rw-r--r--kernel/cpu.c38
-rw-r--r--kernel/cpuset.c264
-rw-r--r--kernel/events/core.c25
-rw-r--r--kernel/events/uprobes.c9
-rw-r--r--kernel/exit.c2
-rw-r--r--kernel/fork.c5
-rw-r--r--kernel/groups.c14
-rw-r--r--kernel/hung_task.c3
-rw-r--r--kernel/kexec.c4
-rw-r--r--kernel/kthread.c4
-rw-r--r--kernel/module.c10
-rw-r--r--kernel/panic.c2
-rw-r--r--kernel/pid_namespace.c4
-rw-r--r--kernel/printk/printk.c15
-rw-r--r--kernel/profile.c22
-rw-r--r--kernel/relay.c2
-rw-r--r--kernel/resource.c2
-rw-r--r--kernel/sched/core.c56
-rw-r--r--kernel/sched/cpuacct.c6
-rw-r--r--kernel/sched/debug.c3
-rw-r--r--kernel/sched/idle.c150
-rw-r--r--kernel/sched/stats.c2
-rw-r--r--kernel/seccomp.c121
-rw-r--r--kernel/signal.c2
-rw-r--r--kernel/sys_ni.c2
-rw-r--r--kernel/sysctl.c4
-rw-r--r--kernel/trace/Kconfig1
-rw-r--r--kernel/trace/blktrace.c3
-rw-r--r--kernel/trace/ftrace.c162
-rw-r--r--kernel/trace/ring_buffer.c19
-rw-r--r--kernel/trace/trace.c187
-rw-r--r--kernel/trace/trace.h38
-rw-r--r--kernel/trace/trace_events.c30
-rw-r--r--kernel/trace/trace_functions.c143
-rw-r--r--kernel/trace/trace_functions_graph.c3
-rw-r--r--kernel/trace/trace_irqsoff.c10
-rw-r--r--kernel/trace/trace_kprobe.c17
-rw-r--r--kernel/trace/trace_nop.c5
-rw-r--r--kernel/trace/trace_output.c31
-rw-r--r--kernel/trace/trace_probe.h17
-rw-r--r--kernel/trace/trace_sched_wakeup.c10
-rw-r--r--kernel/trace/trace_stack.c3
-rw-r--r--kernel/trace/trace_uprobe.c191
-rw-r--r--kernel/tracepoint.c256
-rw-r--r--kernel/user.c3
-rw-r--r--kernel/user_namespace.c2
-rw-r--r--kernel/watchdog.c16
-rw-r--r--lib/clz_ctz.c7
-rw-r--r--lib/decompress_inflate.c1
-rw-r--r--lib/devres.c12
-rw-r--r--lib/kobject_uevent.c42
-rw-r--r--lib/nlattr.c10
-rw-r--r--lib/radix-tree.c385
-rw-r--r--lib/random32.c76
-rw-r--r--lib/syscall.c1
-rw-r--r--lib/vsprintf.c45
-rw-r--r--mm/Makefile2
-rw-r--r--mm/backing-dev.c16
-rw-r--r--mm/compaction.c12
-rw-r--r--mm/filemap.c490
-rw-r--r--mm/huge_memory.c79
-rw-r--r--mm/hugetlb.c294
-rw-r--r--mm/hugetlb_cgroup.c11
-rw-r--r--mm/kmemleak.c140
-rw-r--r--mm/list_lru.c16
-rw-r--r--mm/memblock.c5
-rw-r--r--mm/memcontrol.c110
-rw-r--r--mm/memory-failure.c8
-rw-r--r--mm/memory.c466
-rw-r--r--mm/mempolicy.c12
-rw-r--r--mm/mincore.c20
-rw-r--r--mm/mmap.c4
-rw-r--r--mm/nobootmem.c2
-rw-r--r--mm/nommu.c2
-rw-r--r--mm/page_alloc.c8
-rw-r--r--mm/page_cgroup.c12
-rw-r--r--mm/process_vm_access.c2
-rw-r--r--mm/readahead.c10
-rw-r--r--mm/shmem.c124
-rw-r--r--mm/slab.c4
-rw-r--r--mm/slub.c24
-rw-r--r--mm/sparse.c2
-rw-r--r--mm/swap.c53
-rw-r--r--mm/truncate.c148
-rw-r--r--mm/vmscan.c122
-rw-r--r--mm/vmstat.c12
-rw-r--r--mm/workingset.c414
-rw-r--r--mm/zsmalloc.c17
-rw-r--r--mm/zswap.c8
-rw-r--r--net/8021q/vlan.h4
-rw-r--r--net/8021q/vlan_core.c10
-rw-r--r--net/8021q/vlan_dev.c22
-rw-r--r--net/8021q/vlan_netlink.c4
-rw-r--r--net/Kconfig6
-rw-r--r--net/appletalk/aarp.c4
-rw-r--r--net/appletalk/ddp.c100
-rw-r--r--net/atm/mpc.c2
-rw-r--r--net/batman-adv/Kconfig9
-rw-r--r--net/batman-adv/Makefile1
-rw-r--r--net/batman-adv/bat_iv_ogm.c10
-rw-r--r--net/batman-adv/bridge_loop_avoidance.c35
-rw-r--r--net/batman-adv/distributed-arp-table.c9
-rw-r--r--net/batman-adv/distributed-arp-table.h3
-rw-r--r--net/batman-adv/fragmentation.c4
-rw-r--r--net/batman-adv/gateway_client.c10
-rw-r--r--net/batman-adv/icmp_socket.c11
-rw-r--r--net/batman-adv/main.c19
-rw-r--r--net/batman-adv/main.h4
-rw-r--r--net/batman-adv/multicast.c748
-rw-r--r--net/batman-adv/multicast.h80
-rw-r--r--net/batman-adv/network-coding.c26
-rw-r--r--net/batman-adv/originator.c11
-rw-r--r--net/batman-adv/packet.h53
-rw-r--r--net/batman-adv/routing.c17
-rw-r--r--net/batman-adv/send.c31
-rw-r--r--net/batman-adv/send.h7
-rw-r--r--net/batman-adv/soft-interface.c48
-rw-r--r--net/batman-adv/sysfs.c6
-rw-r--r--net/batman-adv/translation-table.c140
-rw-r--r--net/batman-adv/translation-table.h2
-rw-r--r--net/batman-adv/types.h90
-rw-r--r--net/bluetooth/6lowpan.c2
-rw-r--r--net/bluetooth/6lowpan.h21
-rw-r--r--net/bluetooth/Kconfig8
-rw-r--r--net/bluetooth/Makefile3
-rw-r--r--net/bluetooth/a2mp.c20
-rw-r--r--net/bluetooth/af_bluetooth.c2
-rw-r--r--net/bluetooth/hci_conn.c239
-rw-r--r--net/bluetooth/hci_core.c1197
-rw-r--r--net/bluetooth/hci_event.c566
-rw-r--r--net/bluetooth/hci_sock.c17
-rw-r--r--net/bluetooth/hci_sysfs.c18
-rw-r--r--net/bluetooth/hidp/core.c100
-rw-r--r--net/bluetooth/l2cap_core.c706
-rw-r--r--net/bluetooth/l2cap_sock.c69
-rw-r--r--net/bluetooth/mgmt.c974
-rw-r--r--net/bluetooth/rfcomm/core.c96
-rw-r--r--net/bluetooth/rfcomm/sock.c34
-rw-r--r--net/bluetooth/rfcomm/tty.c262
-rw-r--r--net/bluetooth/sco.c10
-rw-r--r--net/bluetooth/smp.c585
-rw-r--r--net/bluetooth/smp.h21
-rw-r--r--net/bridge/br_device.c30
-rw-r--r--net/bridge/br_forward.c9
-rw-r--r--net/bridge/br_if.c2
-rw-r--r--net/bridge/br_multicast.c4
-rw-r--r--net/bridge/br_netfilter.c4
-rw-r--r--net/bridge/br_private.h8
-rw-r--r--net/bridge/br_vlan.c8
-rw-r--r--net/bridge/netfilter/ebt_among.c2
-rw-r--r--net/bridge/netfilter/ebt_dnat.c2
-rw-r--r--net/bridge/netfilter/ebt_redirect.c6
-rw-r--r--net/bridge/netfilter/ebt_snat.c2
-rw-r--r--net/ceph/crush/mapper.c85
-rw-r--r--net/ceph/debugfs.c55
-rw-r--r--net/ceph/messenger.c6
-rw-r--r--net/ceph/osd_client.c43
-rw-r--r--net/ceph/osdmap.c993
-rw-r--r--net/core/Makefile1
-rw-r--r--net/core/dev.c155
-rw-r--r--net/core/filter.c1567
-rw-r--r--net/core/flow.c140
-rw-r--r--net/core/flow_dissector.c24
-rw-r--r--net/core/neighbour.c9
-rw-r--r--net/core/net-sysfs.c24
-rw-r--r--net/core/netclassid_cgroup.c15
-rw-r--r--net/core/netpoll.c587
-rw-r--r--net/core/netprio_cgroup.c41
-rw-r--r--net/core/pktgen.c32
-rw-r--r--net/core/ptp_classifier.c141
-rw-r--r--net/core/request_sock.c1
-rw-r--r--net/core/rtnetlink.c113
-rw-r--r--net/core/skbuff.c166
-rw-r--r--net/core/sock_diag.c23
-rw-r--r--net/core/timestamping.c19
-rw-r--r--net/hsr/hsr_device.c10
-rw-r--r--net/hsr/hsr_framereg.c20
-rw-r--r--net/hsr/hsr_main.c6
-rw-r--r--net/ieee802154/6lowpan_iphc.c3
-rw-r--r--net/ieee802154/6lowpan_rtnl.c (renamed from net/ieee802154/6lowpan.c)388
-rw-r--r--net/ieee802154/Kconfig2
-rw-r--r--net/ieee802154/Makefile6
-rw-r--r--net/ieee802154/af802154.h5
-rw-r--r--net/ieee802154/af_ieee802154.c22
-rw-r--r--net/ieee802154/dgram.c60
-rw-r--r--net/ieee802154/header_ops.c287
-rw-r--r--net/ieee802154/ieee802154.h1
-rw-r--r--net/ieee802154/netlink.c1
-rw-r--r--net/ieee802154/nl-mac.c220
-rw-r--r--net/ieee802154/nl_policy.c10
-rw-r--r--net/ieee802154/raw.c18
-rw-r--r--net/ieee802154/reassembly.c571
-rw-r--r--net/ieee802154/reassembly.h41
-rw-r--r--net/ieee802154/wpan-class.c4
-rw-r--r--net/ipv4/Makefile2
-rw-r--r--net/ipv4/af_inet.c4
-rw-r--r--net/ipv4/ah4.c78
-rw-r--r--net/ipv4/esp4.c26
-rw-r--r--net/ipv4/fib_frontend.c2
-rw-r--r--net/ipv4/ip_forward.c7
-rw-r--r--net/ipv4/ip_output.c12
-rw-r--r--net/ipv4/ip_sockglue.c21
-rw-r--r--net/ipv4/ip_tunnel.c31
-rw-r--r--net/ipv4/ip_tunnel_core.c4
-rw-r--r--net/ipv4/ip_vti.c310
-rw-r--r--net/ipv4/ipcomp.c26
-rw-r--r--net/ipv4/netfilter.c2
-rw-r--r--net/ipv4/ping.c2
-rw-r--r--net/ipv4/proc.c6
-rw-r--r--net/ipv4/raw.c2
-rw-r--r--net/ipv4/route.c12
-rw-r--r--net/ipv4/tcp.c13
-rw-r--r--net/ipv4/tcp_cong.c10
-rw-r--r--net/ipv4/tcp_cubic.c4
-rw-r--r--net/ipv4/tcp_highspeed.c1
-rw-r--r--net/ipv4/tcp_hybla.c13
-rw-r--r--net/ipv4/tcp_illinois.c2
-rw-r--r--net/ipv4/tcp_input.c187
-rw-r--r--net/ipv4/tcp_ipv4.c8
-rw-r--r--net/ipv4/tcp_lp.c2
-rw-r--r--net/ipv4/tcp_memcontrol.c4
-rw-r--r--net/ipv4/tcp_metrics.c83
-rw-r--r--net/ipv4/tcp_minisocks.c4
-rw-r--r--net/ipv4/tcp_output.c71
-rw-r--r--net/ipv4/tcp_probe.c2
-rw-r--r--net/ipv4/tcp_scalable.c1
-rw-r--r--net/ipv4/tcp_timer.c3
-rw-r--r--net/ipv4/tcp_vegas.c2
-rw-r--r--net/ipv4/tcp_veno.c1
-rw-r--r--net/ipv4/tcp_westwood.c1
-rw-r--r--net/ipv4/tcp_yeah.c2
-rw-r--r--net/ipv4/udp.c3
-rw-r--r--net/ipv4/xfrm4_input.c9
-rw-r--r--net/ipv4/xfrm4_mode_tunnel.c68
-rw-r--r--net/ipv4/xfrm4_policy.c1
-rw-r--r--net/ipv4/xfrm4_protocol.c286
-rw-r--r--net/ipv6/Makefile2
-rw-r--r--net/ipv6/addrlabel.c59
-rw-r--r--net/ipv6/ah6.c80
-rw-r--r--net/ipv6/esp6.c26
-rw-r--r--net/ipv6/icmp.c2
-rw-r--r--net/ipv6/ip6_checksum.c4
-rw-r--r--net/ipv6/ip6_fib.c121
-rw-r--r--net/ipv6/ip6_flowlabel.c6
-rw-r--r--net/ipv6/ip6_gre.c9
-rw-r--r--net/ipv6/ip6_output.c19
-rw-r--r--net/ipv6/ip6_tunnel.c13
-rw-r--r--net/ipv6/ip6_vti.c316
-rw-r--r--net/ipv6/ipcomp6.c22
-rw-r--r--net/ipv6/ipv6_sockglue.c2
-rw-r--r--net/ipv6/mcast.c11
-rw-r--r--net/ipv6/output_core.c27
-rw-r--r--net/ipv6/ping.c4
-rw-r--r--net/ipv6/route.c50
-rw-r--r--net/ipv6/sit.c18
-rw-r--r--net/ipv6/tcp_ipv6.c46
-rw-r--r--net/ipv6/xfrm6_mode_tunnel.c63
-rw-r--r--net/ipv6/xfrm6_policy.c7
-rw-r--r--net/ipv6/xfrm6_protocol.c270
-rw-r--r--net/ipx/af_ipx.c28
-rw-r--r--net/iucv/af_iucv.c1
-rw-r--r--net/iucv/iucv.c121
-rw-r--r--net/key/af_key.c39
-rw-r--r--net/l2tp/l2tp_core.c24
-rw-r--r--net/l2tp/l2tp_ppp.c3
-rw-r--r--net/mac80211/agg-tx.c2
-rw-r--r--net/mac80211/cfg.c306
-rw-r--r--net/mac80211/cfg.h2
-rw-r--r--net/mac80211/chan.c2
-rw-r--r--net/mac80211/debugfs_netdev.c13
-rw-r--r--net/mac80211/debugfs_sta.c2
-rw-r--r--net/mac80211/driver-ops.h12
-rw-r--r--net/mac80211/ht.c4
-rw-r--r--net/mac80211/ibss.c45
-rw-r--r--net/mac80211/ieee80211_i.h17
-rw-r--r--net/mac80211/iface.c13
-rw-r--r--net/mac80211/main.c21
-rw-r--r--net/mac80211/mesh.c96
-rw-r--r--net/mac80211/mlme.c179
-rw-r--r--net/mac80211/pm.c14
-rw-r--r--net/mac80211/rate.c46
-rw-r--r--net/mac80211/rate.h2
-rw-r--r--net/mac80211/rc80211_minstrel.c2
-rw-r--r--net/mac80211/rc80211_minstrel.h2
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c7
-rw-r--r--net/mac80211/rc80211_pid_algo.c2
-rw-r--r--net/mac80211/rx.c109
-rw-r--r--net/mac80211/scan.c15
-rw-r--r--net/mac80211/sta_info.h2
-rw-r--r--net/mac80211/status.c3
-rw-r--r--net/mac80211/tx.c35
-rw-r--r--net/mac80211/util.c42
-rw-r--r--net/mac80211/vht.c26
-rw-r--r--net/mac80211/wpa.c9
-rw-r--r--net/mac802154/Makefile2
-rw-r--r--net/mac802154/ieee802154_dev.c85
-rw-r--r--net/mac802154/mac802154.h19
-rw-r--r--net/mac802154/mac_cmd.c5
-rw-r--r--net/mac802154/mib.c26
-rw-r--r--net/mac802154/rx.c1
-rw-r--r--net/mac802154/wpan.c457
-rw-r--r--net/netfilter/ipset/Kconfig9
-rw-r--r--net/netfilter/ipset/Makefile1
-rw-r--r--net/netfilter/ipset/ip_set_core.c54
-rw-r--r--net/netfilter/ipset/ip_set_hash_gen.h43
-rw-r--r--net/netfilter/ipset/ip_set_hash_ip.c3
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipmark.c321
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipport.c3
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportip.c3
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportnet.c3
-rw-r--r--net/netfilter/ipset/ip_set_hash_net.c3
-rw-r--r--net/netfilter/ipset/ip_set_hash_netiface.c3
-rw-r--r--net/netfilter/ipset/ip_set_hash_netnet.c10
-rw-r--r--net/netfilter/ipset/ip_set_hash_netport.c3
-rw-r--r--net/netfilter/ipset/ip_set_hash_netportnet.c3
-rw-r--r--net/netfilter/ipset/pfxlen.c4
-rw-r--r--net/netfilter/ipvs/ip_vs_ctl.c6
-rw-r--r--net/netfilter/ipvs/ip_vs_lblc.c13
-rw-r--r--net/netfilter/nf_conntrack_core.c432
-rw-r--r--net/netfilter/nf_conntrack_expect.c36
-rw-r--r--net/netfilter/nf_conntrack_h323_main.c4
-rw-r--r--net/netfilter/nf_conntrack_helper.c41
-rw-r--r--net/netfilter/nf_conntrack_netlink.c133
-rw-r--r--net/netfilter/nf_conntrack_sip.c8
-rw-r--r--net/netfilter/nf_tables_api.c80
-rw-r--r--net/netfilter/nfnetlink.c8
-rw-r--r--net/netfilter/nfnetlink_log.c8
-rw-r--r--net/netfilter/nft_compat.c4
-rw-r--r--net/netfilter/nft_ct.c36
-rw-r--r--net/netfilter/nft_hash.c261
-rw-r--r--net/netfilter/nft_immediate.c3
-rw-r--r--net/netfilter/nft_log.c3
-rw-r--r--net/netfilter/nft_lookup.c5
-rw-r--r--net/netfilter/nft_nat.c22
-rw-r--r--net/netfilter/xt_AUDIT.c4
-rw-r--r--net/netfilter/xt_connlimit.c311
-rw-r--r--net/netfilter/xt_ipcomp.c2
-rw-r--r--net/netlink/af_netlink.c31
-rw-r--r--net/netlink/af_netlink.h1
-rw-r--r--net/nfc/core.c10
-rw-r--r--net/nfc/digital.h6
-rw-r--r--net/nfc/digital_core.c67
-rw-r--r--net/nfc/digital_technology.c247
-rw-r--r--net/nfc/hci/llc.c4
-rw-r--r--net/nfc/llcp_core.c16
-rw-r--r--net/nfc/nci/core.c3
-rw-r--r--net/nfc/nci/spi.c3
-rw-r--r--net/nfc/netlink.c8
-rw-r--r--net/openvswitch/datapath.c20
-rw-r--r--net/openvswitch/datapath.h2
-rw-r--r--net/openvswitch/vport.c14
-rw-r--r--net/packet/af_packet.c59
-rw-r--r--net/rds/iw.c3
-rw-r--r--net/rfkill/core.c9
-rw-r--r--net/rxrpc/Makefile5
-rw-r--r--net/rxrpc/af_rxrpc.c9
-rw-r--r--net/rxrpc/ar-ack.c61
-rw-r--r--net/rxrpc/ar-call.c213
-rw-r--r--net/rxrpc/ar-connection.c10
-rw-r--r--net/rxrpc/ar-error.c1
-rw-r--r--net/rxrpc/ar-input.c190
-rw-r--r--net/rxrpc/ar-internal.h40
-rw-r--r--net/rxrpc/ar-output.c15
-rw-r--r--net/rxrpc/ar-recvmsg.c25
-rw-r--r--net/rxrpc/ar-skbuff.c7
-rw-r--r--net/rxrpc/ar-transport.c10
-rw-r--r--net/rxrpc/sysctl.c146
-rw-r--r--net/sched/act_api.c142
-rw-r--r--net/sched/act_csum.c31
-rw-r--r--net/sched/act_gact.c34
-rw-r--r--net/sched/act_ipt.c68
-rw-r--r--net/sched/act_mirred.c56
-rw-r--r--net/sched/act_nat.c33
-rw-r--r--net/sched/act_pedit.c45
-rw-r--r--net/sched/act_police.c22
-rw-r--r--net/sched/act_simple.c64
-rw-r--r--net/sched/act_skbedit.c36
-rw-r--r--net/sched/cls_fw.c33
-rw-r--r--net/sched/sch_api.c8
-rw-r--r--net/sched/sch_atm.c3
-rw-r--r--net/sched/sch_cbq.c6
-rw-r--r--net/sched/sch_fq.c3
-rw-r--r--net/sched/sch_fq_codel.c3
-rw-r--r--net/sched/sch_generic.c2
-rw-r--r--net/sched/sch_hfsc.c3
-rw-r--r--net/sched/sch_hhf.c3
-rw-r--r--net/sched/sch_htb.c20
-rw-r--r--net/sched/sch_ingress.c3
-rw-r--r--net/sched/sch_netem.c79
-rw-r--r--net/sched/sch_tbf.c26
-rw-r--r--net/sctp/associola.c3
-rw-r--r--net/sctp/transport.c1
-rw-r--r--net/socket.c18
-rw-r--r--net/sunrpc/backchannel_rqst.c93
-rw-r--r--net/sunrpc/clnt.c23
-rw-r--r--net/sunrpc/sched.c3
-rw-r--r--net/sunrpc/xprtrdma/rpc_rdma.c4
-rw-r--r--net/sunrpc/xprtrdma/transport.c10
-rw-r--r--net/sunrpc/xprtsock.c28
-rw-r--r--net/tipc/addr.h2
-rw-r--r--net/tipc/bcast.c41
-rw-r--r--net/tipc/bcast.h4
-rw-r--r--net/tipc/bearer.c96
-rw-r--r--net/tipc/bearer.h13
-rw-r--r--net/tipc/config.c107
-rw-r--r--net/tipc/config.h5
-rw-r--r--net/tipc/core.c11
-rw-r--r--net/tipc/core.h1
-rw-r--r--net/tipc/discover.c23
-rw-r--r--net/tipc/discover.h5
-rw-r--r--net/tipc/link.c620
-rw-r--r--net/tipc/link.h57
-rw-r--r--net/tipc/name_distr.c22
-rw-r--r--net/tipc/name_distr.h2
-rw-r--r--net/tipc/net.c19
-rw-r--r--net/tipc/node.c119
-rw-r--r--net/tipc/node.h6
-rw-r--r--net/tipc/port.c301
-rw-r--r--net/tipc/port.h130
-rw-r--r--net/tipc/ref.c27
-rw-r--r--net/tipc/ref.h1
-rw-r--r--net/tipc/socket.c414
-rw-r--r--net/tipc/socket.h72
-rw-r--r--net/wireless/ap.c10
-rw-r--r--net/wireless/chan.c87
-rw-r--r--net/wireless/core.c4
-rw-r--r--net/wireless/core.h17
-rw-r--r--net/wireless/genregdb.awk10
-rw-r--r--net/wireless/ibss.c34
-rw-r--r--net/wireless/mesh.c12
-rw-r--r--net/wireless/mlme.c4
-rw-r--r--net/wireless/nl80211.c272
-rw-r--r--net/wireless/nl80211.h2
-rw-r--r--net/wireless/rdev-ops.h9
-rw-r--r--net/wireless/reg.c297
-rw-r--r--net/wireless/reg.h3
-rw-r--r--net/wireless/scan.c31
-rw-r--r--net/wireless/sme.c1
-rw-r--r--net/wireless/trace.h35
-rw-r--r--net/wireless/util.c57
-rw-r--r--net/wireless/wext-sme.c2
-rw-r--r--net/xfrm/xfrm_input.c97
-rw-r--r--net/xfrm/xfrm_policy.c40
-rw-r--r--net/xfrm/xfrm_state.c72
-rw-r--r--net/xfrm/xfrm_user.c37
-rw-r--r--samples/seccomp/Makefile14
-rw-r--r--scripts/Makefile.lib12
-rwxr-xr-xscripts/checkpatch.pl235
-rw-r--r--scripts/dtc/dtc-parser.tab.c_shipped469
-rw-r--r--scripts/dtc/dtc-parser.tab.h_shipped36
-rw-r--r--scripts/dtc/dtc.c119
-rw-r--r--scripts/dtc/dtc.h1
-rw-r--r--scripts/dtc/srcpos.c6
-rwxr-xr-xscripts/dtc/update-dtc-source.sh54
-rw-r--r--scripts/dtc/util.c141
-rw-r--r--scripts/dtc/util.h107
-rw-r--r--scripts/dtc/version_gen.h2
-rw-r--r--scripts/genksyms/keywords.gperf5
-rw-r--r--scripts/genksyms/keywords.hash.c_shipped133
-rw-r--r--scripts/genksyms/lex.l51
-rw-r--r--scripts/genksyms/lex.lex.c_shipped51
-rw-r--r--scripts/genksyms/parse.tab.c_shipped608
-rw-r--r--scripts/genksyms/parse.tab.h_shipped29
-rw-r--r--scripts/genksyms/parse.y5
-rw-r--r--scripts/kallsyms.c74
-rw-r--r--scripts/link-vmlinux.sh4
-rw-r--r--scripts/mod/file2alias.c14
-rw-r--r--security/Makefile12
-rw-r--r--security/apparmor/lsm.c2
-rw-r--r--security/capability.c2
-rw-r--r--security/device_cgroup.c12
-rw-r--r--security/integrity/Makefile4
-rw-r--r--security/integrity/evm/Kconfig6
-rw-r--r--security/integrity/evm/evm.h28
-rw-r--r--security/integrity/evm/evm_crypto.c8
-rw-r--r--security/integrity/evm/evm_main.c6
-rw-r--r--security/integrity/evm/evm_secfs.c6
-rw-r--r--security/integrity/iint.c2
-rw-r--r--security/integrity/ima/ima.h2
-rw-r--r--security/integrity/ima/ima_api.c20
-rw-r--r--security/integrity/ima/ima_appraise.c4
-rw-r--r--security/integrity/ima/ima_crypto.c37
-rw-r--r--security/integrity/ima/ima_fs.c8
-rw-r--r--security/integrity/ima/ima_init.c9
-rw-r--r--security/integrity/ima/ima_main.c11
-rw-r--r--security/integrity/ima/ima_policy.c79
-rw-r--r--security/integrity/ima/ima_queue.c12
-rw-r--r--security/integrity/ima/ima_template.c19
-rw-r--r--security/integrity/ima/ima_template_lib.c29
-rw-r--r--security/integrity/integrity_audit.c7
-rw-r--r--security/keys/encrypted-keys/encrypted.c2
-rw-r--r--security/keys/trusted.c6
-rw-r--r--security/security.c22
-rw-r--r--security/selinux/hooks.c63
-rw-r--r--security/selinux/include/xfrm.h5
-rw-r--r--security/selinux/selinuxfs.c2
-rw-r--r--sound/soc/kirkwood/Kconfig2
-rw-r--r--tools/net/bpf_dbg.c119
-rw-r--r--tools/perf/config/Makefile.arch3
-rw-r--r--tools/perf/perf.h8
-rw-r--r--tools/testing/ktest/examples/kvm.conf4
-rw-r--r--tools/virtio/linux/kmemleak.h3
-rw-r--r--tools/virtio/linux/virtio.h4
-rw-r--r--tools/virtio/virtio_test.c2
-rw-r--r--virt/kvm/Kconfig4
-rw-r--r--virt/kvm/arm/arch_timer.c2
-rw-r--r--virt/kvm/arm/vgic.c2
-rw-r--r--virt/kvm/async_pf.c27
-rw-r--r--virt/kvm/eventfd.c8
-rw-r--r--virt/kvm/ioapic.c108
-rw-r--r--virt/kvm/kvm_main.c12
-rw-r--r--virt/kvm/vfio.c27
4520 files changed, 262112 insertions, 83699 deletions
diff --git a/CREDITS b/CREDITS
index e371c5504a50..c322dcfb926d 100644
--- a/CREDITS
+++ b/CREDITS
@@ -630,6 +630,13 @@ N: Michael Elizabeth Chastain
E: mec@shout.net
D: Configure, Menuconfig, xconfig
+N: Mauro Carvalho Chehab
+E: m.chehab@samsung.org
+E: mchehab@infradead.org
+D: Media subsystem (V4L/DVB) drivers and core
+D: EDAC drivers and EDAC 3.0 core rework
+S: Brazil
+
N: Raymond Chen
E: raymondc@microsoft.com
D: Author of Configure script
@@ -1229,7 +1236,7 @@ E: philip@gladstonefamily.net
D: Kernel / timekeeping stuff
S: Carlisle, MA 01741
S: USA
-
+
N: Jan-Benedict Glaw
E: jbglaw@lug-owl.de
D: SRM environment driver (for Alpha systems)
@@ -2560,10 +2567,14 @@ S: 22 Seaview St
S: Fullarton 5063
S: South Australia
-N. Wolfgang Muees
+N: Wolfgang Muees
E: wolfgang@iksw-muees.de
D: Auerswald USB driver
+N: Paul Mundt
+E: paul.mundt@gmail.com
+D: SuperH maintainer
+
N: Ian A. Murdock
E: imurdock@gnu.ai.mit.edu
D: Creator of Debian distribution
@@ -2707,6 +2718,9 @@ N: Greg Page
E: gpage@sovereign.org
D: IPX development and support
+N: Venkatesh Pallipadi (Venki)
+D: x86/HPET
+
N: David Parsons
E: orc@pell.chi.il.us
D: improved memory detection code.
diff --git a/Documentation/ABI/testing/sysfs-bus-mdio b/Documentation/ABI/testing/sysfs-bus-mdio
index 6349749ebc29..491baaf4285f 100644
--- a/Documentation/ABI/testing/sysfs-bus-mdio
+++ b/Documentation/ABI/testing/sysfs-bus-mdio
@@ -7,3 +7,23 @@ Description:
by the device during bus enumeration, encoded in hexadecimal.
This ID is used to match the device with the appropriate
driver.
+
+What: /sys/bus/mdio_bus/devices/.../phy_interface
+Date: February 2014
+KernelVersion: 3.15
+Contact: netdev@vger.kernel.org
+Description:
+ This attribute contains the PHY interface as configured by the
+ Ethernet driver during bus enumeration, encoded in string.
+ This interface mode is used to configure the Ethernet MAC with the
+ appropriate mode for its data lines to the PHY hardware.
+
+What: /sys/bus/mdio_bus/devices/.../phy_has_fixups
+Date: February 2014
+KernelVersion: 3.15
+Contact: netdev@vger.kernel.org
+Description:
+ This attribute contains the boolean value whether a given PHY
+ device has had any "fixup" workaround running on it, encoded as
+ a boolean. This information is provided to help troubleshooting
+ PHY configurations.
diff --git a/Documentation/ABI/testing/sysfs-class-net b/Documentation/ABI/testing/sysfs-class-net
new file mode 100644
index 000000000000..d922060e455d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-net
@@ -0,0 +1,199 @@
+What: /sys/class/net/<iface>/addr_assign_type
+Date: July 2010
+KernelVersion: 3.2
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the address assignment type. Possible values are:
+ 0: permanent address
+ 1: randomly generated
+ 2: stolen from another device
+ 3: set using dev_set_mac_address
+
+What: /sys/class/net/<iface>/addr_len
+Date: April 2005
+KernelVersion: 2.6.12
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the hardware address size in bytes.
+ Values vary based on the lower-level protocol used by the
+ interface (Ethernet, FDDI, ATM, IEEE 802.15.4...). See
+ include/uapi/linux/if_*.h for actual values.
+
+What: /sys/class/net/<iface>/address
+Date: April 2005
+KernelVersion: 2.6.12
+Contact: netdev@vger.kernel.org
+Description:
+ Hardware address currently assigned to this interface.
+ Format is a string, e.g: 00:11:22:33:44:55 for an Ethernet MAC
+ address.
+
+What: /sys/class/net/<iface>/broadcast
+Date: April 2005
+KernelVersion: 2.6.12
+Contact: netdev@vger.kernel.org
+Description:
+ Hardware broadcast address for this interface. Format is a
+ string, e.g: ff:ff:ff:ff:ff:ff for an Ethernet broadcast MAC
+ address.
+
+What: /sys/class/net/<iface>/carrier
+Date: April 2005
+KernelVersion: 2.6.12
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the current physical link state of the interface.
+ Posssible values are:
+ 0: physical link is down
+ 1: physical link is up
+
+ Note: some special devices, e.g: bonding and team drivers will
+ allow this attribute to be written to force a link state for
+ operating correctly and designating another fallback interface.
+
+What: /sys/class/net/<iface>/dev_id
+Date: April 2008
+KernelVersion: 2.6.26
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the device unique identifier. Format is an hexadecimal
+ value. This is used to disambiguate interfaces which might be
+ stacked (e.g: VLAN interfaces) but still have the same MAC
+ address as their parent device.
+
+What: /sys/class/net/<iface>/dormant
+Date: March 2006
+KernelVersion: 2.6.17
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates whether the interface is in dormant state. Possible
+ values are:
+ 0: interface is not dormant
+ 1: interface is dormant
+
+ This attribute can be used by supplicant software to signal that
+ the device is not usable unless some supplicant-based
+ authentication is performed (e.g: 802.1x). 'link_mode' attribute
+ will also reflect the dormant state.
+
+What: /sys/clas/net/<iface>/duplex
+Date: October 2009
+KernelVersion: 2.6.33
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the interface latest or current duplex value. Possible
+ values are:
+ half: half duplex
+ full: full duplex
+
+ Note: This attribute is only valid for interfaces that implement
+ the ethtool get_settings method (mostly Ethernet).
+
+What: /sys/class/net/<iface>/flags
+Date: April 2005
+KernelVersion: 2.6.12
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the interface flags as a bitmask in hexadecimal. See
+ include/uapi/linux/if.h for a list of all possible values and
+ the flags semantics.
+
+What: /sys/class/net/<iface>/ifalias
+Date: September 2008
+KernelVersion: 2.6.28
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates/stores an interface alias name as a string. This can
+ be used for system management purposes.
+
+What: /sys/class/net/<iface>/ifindex
+Date: April 2005
+KernelVersion: 2.6.12
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the system-wide interface unique index identifier as a
+ decimal number. This attribute is used for mapping an interface
+ identifier to an interface name. It is used throughout the
+ networking stack for specifying the interface specific
+ requests/events.
+
+What: /sys/class/net/<iface>/iflink
+Date: April 2005
+KernelVersion: 2.6.12
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the system-wide interface unique index identifier a
+ the interface is linked to. Format is decimal. This attribute is
+ used to resolve interfaces chaining, linking and stacking.
+ Physical interfaces have the same 'ifindex' and 'iflink' values.
+
+What: /sys/class/net/<iface>/link_mode
+Date: March 2006
+KernelVersion: 2.6.17
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the interface link mode, as a decimal number. This
+ attribute should be used in conjunction with 'dormant' attribute
+ to determine the interface usability. Possible values:
+ 0: default link mode
+ 1: dormant link mode
+
+What: /sys/class/net/<iface>/mtu
+Date: April 2005
+KernelVersion: 2.6.12
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the interface currently configured MTU value, in
+ bytes, and in decimal format. Specific values depends on the
+ lower-level interface protocol used. Ethernet devices will show
+ a 'mtu' attribute value of 1500 unless changed.
+
+What: /sys/calss/net/<iface>/netdev_group
+Date: January 2011
+KernelVersion: 2.6.39
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the interface network device group, as a decimal
+ integer. Default value is 0 which corresponds to the initial
+ network devices group. The group can be changed to affect
+ routing decisions (see: net/ipv4/fib_rules and
+ net/ipv6/fib6_rules.c).
+
+What: /sys/class/net/<iface>/operstate
+Date: March 2006
+KernelVersion: 2.6.17
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the interface RFC2863 operational state as a string.
+ Possible values are:
+ "unknown", "notpresent", "down", "lowerlayerdown", "testing",
+ "dormant", "up".
+
+What: /sys/class/net/<iface>/speed
+Date: October 2009
+KernelVersion: 2.6.33
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the interface latest or current speed value. Value is
+ an integer representing the link speed in Mbits/sec.
+
+ Note: this attribute is only valid for interfaces that implement
+ the ethtool get_settings method (mostly Ethernet ).
+
+What: /sys/class/net/<iface>/tx_queue_len
+Date: April 2005
+KernelVersion: 2.6.12
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the interface transmit queue len in number of packets,
+ as an integer value. Value depend on the type of interface,
+ Ethernet network adapters have a default value of 1000 unless
+ configured otherwise
+
+What: /sys/class/net/<iface>/type
+Date: April 2005
+KernelVersion: 2.6.12
+Contact: netdev@vger.kernel.org
+Description:
+ Indicates the interface protocol type as a decimal value. See
+ include/uapi/linux/if_arp.h for all possible values.
diff --git a/Documentation/ABI/testing/sysfs-class-net-mesh b/Documentation/ABI/testing/sysfs-class-net-mesh
index 4793d3dff6af..c46406296631 100644
--- a/Documentation/ABI/testing/sysfs-class-net-mesh
+++ b/Documentation/ABI/testing/sysfs-class-net-mesh
@@ -76,6 +76,15 @@ Description:
is used to classify clients as "isolated" by the
Extended Isolation feature.
+What: /sys/class/net/<mesh_iface>/mesh/multicast_mode
+Date: Feb 2014
+Contact: Linus Lüssing <linus.luessing@web.de>
+Description:
+ Indicates whether multicast optimizations are enabled
+ or disabled. If set to zero then all nodes in the
+ mesh are going to use classic flooding for any
+ multicast packet with no optimizations.
+
What: /sys/class/net/<mesh_iface>/mesh/network_coding
Date: Nov 2012
Contact: Martin Hundeboll <martin@hundeboll.net>
diff --git a/Documentation/ABI/testing/sysfs-class-rc b/Documentation/ABI/testing/sysfs-class-rc
new file mode 100644
index 000000000000..b65674da43bb
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-rc
@@ -0,0 +1,111 @@
+What: /sys/class/rc/
+Date: Apr 2010
+KernelVersion: 2.6.35
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
+Description:
+ The rc/ class sub-directory belongs to the Remote Controller
+ core and provides a sysfs interface for configuring infrared
+ remote controller receivers.
+
+What: /sys/class/rc/rcN/
+Date: Apr 2010
+KernelVersion: 2.6.35
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
+Description:
+ A /sys/class/rc/rcN directory is created for each remote
+ control receiver device where N is the number of the receiver.
+
+What: /sys/class/rc/rcN/protocols
+Date: Jun 2010
+KernelVersion: 2.6.36
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
+Description:
+ Reading this file returns a list of available protocols,
+ something like:
+ "rc5 [rc6] nec jvc [sony]"
+ Enabled protocols are shown in [] brackets.
+ Writing "+proto" will add a protocol to the list of enabled
+ protocols.
+ Writing "-proto" will remove a protocol from the list of enabled
+ protocols.
+ Writing "proto" will enable only "proto".
+ Writing "none" will disable all protocols.
+ Write fails with EINVAL if an invalid protocol combination or
+ unknown protocol name is used.
+
+What: /sys/class/rc/rcN/filter
+Date: Jan 2014
+KernelVersion: 3.15
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
+Description:
+ Sets the scancode filter expected value.
+ Use in combination with /sys/class/rc/rcN/filter_mask to set the
+ expected value of the bits set in the filter mask.
+ If the hardware supports it then scancodes which do not match
+ the filter will be ignored. Otherwise the write will fail with
+ an error.
+ This value may be reset to 0 if the current protocol is altered.
+
+What: /sys/class/rc/rcN/filter_mask
+Date: Jan 2014
+KernelVersion: 3.15
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
+Description:
+ Sets the scancode filter mask of bits to compare.
+ Use in combination with /sys/class/rc/rcN/filter to set the bits
+ of the scancode which should be compared against the expected
+ value. A value of 0 disables the filter to allow all valid
+ scancodes to be processed.
+ If the hardware supports it then scancodes which do not match
+ the filter will be ignored. Otherwise the write will fail with
+ an error.
+ This value may be reset to 0 if the current protocol is altered.
+
+What: /sys/class/rc/rcN/wakeup_protocols
+Date: Feb 2014
+KernelVersion: 3.15
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
+Description:
+ Reading this file returns a list of available protocols to use
+ for the wakeup filter, something like:
+ "rc5 rc6 nec jvc [sony]"
+ The enabled wakeup protocol is shown in [] brackets.
+ Writing "+proto" will add a protocol to the list of enabled
+ wakeup protocols.
+ Writing "-proto" will remove a protocol from the list of enabled
+ wakeup protocols.
+ Writing "proto" will use "proto" for wakeup events.
+ Writing "none" will disable wakeup.
+ Write fails with EINVAL if an invalid protocol combination or
+ unknown protocol name is used, or if wakeup is not supported by
+ the hardware.
+
+What: /sys/class/rc/rcN/wakeup_filter
+Date: Jan 2014
+KernelVersion: 3.15
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
+Description:
+ Sets the scancode wakeup filter expected value.
+ Use in combination with /sys/class/rc/rcN/wakeup_filter_mask to
+ set the expected value of the bits set in the wakeup filter mask
+ to trigger a system wake event.
+ If the hardware supports it and wakeup_filter_mask is not 0 then
+ scancodes which match the filter will wake the system from e.g.
+ suspend to RAM or power off.
+ Otherwise the write will fail with an error.
+ This value may be reset to 0 if the wakeup protocol is altered.
+
+What: /sys/class/rc/rcN/wakeup_filter_mask
+Date: Jan 2014
+KernelVersion: 3.15
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
+Description:
+ Sets the scancode wakeup filter mask of bits to compare.
+ Use in combination with /sys/class/rc/rcN/wakeup_filter to set
+ the bits of the scancode which should be compared against the
+ expected value to trigger a system wake event.
+ If the hardware supports it and wakeup_filter_mask is not 0 then
+ scancodes which match the filter will wake the system from e.g.
+ suspend to RAM or power off.
+ Otherwise the write will fail with an error.
+ This value may be reset to 0 if the wakeup protocol is altered.
diff --git a/Documentation/ABI/testing/sysfs-firmware-ofw b/Documentation/ABI/testing/sysfs-firmware-ofw
new file mode 100644
index 000000000000..f562b188e71d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-firmware-ofw
@@ -0,0 +1,28 @@
+What: /sys/firmware/devicetree/*
+Date: November 2013
+Contact: Grant Likely <grant.likely@linaro.org>
+Description:
+ When using OpenFirmware or a Flattened Device Tree to enumerate
+ hardware, the device tree structure will be exposed in this
+ directory.
+
+ It is possible for multiple device-tree directories to exist.
+ Some device drivers use a separate detached device tree which
+ have no attachment to the system tree and will appear in a
+ different subdirectory under /sys/firmware/devicetree.
+
+ Userspace must not use the /sys/firmware/devicetree/base
+ path directly, but instead should follow /proc/device-tree
+ symlink. It is possible that the absolute path will change
+ in the future, but the symlink is the stable ABI.
+
+ The /proc/device-tree symlink replaces the devicetree /proc
+ filesystem support, and has largely the same semantics and
+ should be compatible with existing userspace.
+
+ The contents of /sys/firmware/devicetree/ is a
+ hierarchy of directories, one per device tree node. The
+ directory name is the resolved path component name (node
+ name plus address). Properties are represented as files
+ in the directory. The contents of each file is the exact
+ binary data from the device tree.
diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs
index 32b0809203dd..62dd72522d6e 100644
--- a/Documentation/ABI/testing/sysfs-fs-f2fs
+++ b/Documentation/ABI/testing/sysfs-fs-f2fs
@@ -55,3 +55,15 @@ Date: January 2014
Contact: "Jaegeuk Kim" <jaegeuk.kim@samsung.com>
Description:
Controls the number of trials to find a victim segment.
+
+What: /sys/fs/f2fs/<disk>/dir_level
+Date: March 2014
+Contact: "Jaegeuk Kim" <jaegeuk.kim@samsung.com>
+Description:
+ Controls the directory level for large directory.
+
+What: /sys/fs/f2fs/<disk>/ram_thresh
+Date: March 2014
+Contact: "Jaegeuk Kim" <jaegeuk.kim@samsung.com>
+Description:
+ Controls the memory footprint used by f2fs.
diff --git a/Documentation/ABI/testing/sysfs-module b/Documentation/ABI/testing/sysfs-module
index 47064c2b1f79..0aac02e7fb0e 100644
--- a/Documentation/ABI/testing/sysfs-module
+++ b/Documentation/ABI/testing/sysfs-module
@@ -49,3 +49,4 @@ Description: Module taint flags:
O - out-of-tree module
F - force-loaded module
C - staging driver module
+ E - unsigned module
diff --git a/Documentation/ABI/testing/sysfs-ptp b/Documentation/ABI/testing/sysfs-ptp
index 05aeedf17794..44806a678f12 100644
--- a/Documentation/ABI/testing/sysfs-ptp
+++ b/Documentation/ABI/testing/sysfs-ptp
@@ -54,6 +54,26 @@ Description:
This file contains the number of programmable periodic
output channels offered by the PTP hardware clock.
+What: /sys/class/ptp/ptpN/n_pins
+Date: March 2014
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This file contains the number of programmable pins
+ offered by the PTP hardware clock.
+
+What: /sys/class/ptp/ptpN/pins
+Date: March 2014
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This directory contains one file for each programmable
+ pin offered by the PTP hardware clock. The file name
+ is the hardware dependent pin name. Reading from this
+ file produces two numbers, the assigned function (see
+ the PTP_PF_ enumeration values in linux/ptp_clock.h)
+ and the channel number. The function and channel
+ assignment may be changed by two writing numbers into
+ the file.
+
What: /sys/class/ptp/ptpN/pps_avaiable
Date: September 2010
Contact: Richard Cochran <richardcochran@gmail.com>
diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl
index 46ad6faee9ab..044b76436e83 100644
--- a/Documentation/DocBook/80211.tmpl
+++ b/Documentation/DocBook/80211.tmpl
@@ -98,6 +98,8 @@
!Finclude/net/cfg80211.h priv_to_wiphy
!Finclude/net/cfg80211.h set_wiphy_dev
!Finclude/net/cfg80211.h wdev_priv
+!Finclude/net/cfg80211.h ieee80211_iface_limit
+!Finclude/net/cfg80211.h ieee80211_iface_combination
</chapter>
<chapter>
<title>Actions and configuration</title>
diff --git a/Documentation/DocBook/kernel-hacking.tmpl b/Documentation/DocBook/kernel-hacking.tmpl
index d0758b241b23..bd9015d10cff 100644
--- a/Documentation/DocBook/kernel-hacking.tmpl
+++ b/Documentation/DocBook/kernel-hacking.tmpl
@@ -850,16 +850,6 @@ printk(KERN_INFO "my ip: %pI4\n", &amp;ipaddress);
<returnvalue>-ERESTARTSYS</returnvalue> if a signal is received.
The <function>wait_event()</function> version ignores signals.
</para>
- <para>
- Do not use the <function>sleep_on()</function> function family -
- it is very easy to accidentally introduce races; almost certainly
- one of the <function>wait_event()</function> family will do, or a
- loop around <function>schedule_timeout()</function>. If you choose
- to loop around <function>schedule_timeout()</function> remember
- you must set the task state (with
- <function>set_current_state()</function>) on each iteration to avoid
- busy-looping.
- </para>
</sect1>
diff --git a/Documentation/DocBook/media/dvb/demux.xml b/Documentation/DocBook/media/dvb/demux.xml
index 86de89cfbd67..c8683d66f059 100644
--- a/Documentation/DocBook/media/dvb/demux.xml
+++ b/Documentation/DocBook/media/dvb/demux.xml
@@ -1042,7 +1042,14 @@ role="subsection"><title>DMX_ADD_PID</title>
</para>
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
-<para>This ioctl is undocumented. Documentation is welcome.</para>
+<para>This ioctl call allows to add multiple PIDs to a transport stream filter
+previously set up with DMX_SET_PES_FILTER and output equal to DMX_OUT_TSDEMUX_TAP.
+</para></entry></row><row><entry align="char"><para>
+It is used by readers of /dev/dvb/adapterX/demuxY.
+</para></entry></row><row><entry align="char"><para>
+It may be called at any time, i.e. before or after the first filter on the
+shared file descriptor was started. It makes it possible to record multiple
+services without the need to de-multiplex or re-multiplex TS packets.</para>
</entry>
</row></tbody></tgroup></informaltable>
<para>SYNOPSIS
@@ -1075,7 +1082,7 @@ role="subsection"><title>DMX_ADD_PID</title>
</para>
</entry><entry
align="char">
-<para>Undocumented.</para>
+<para>PID number to be filtered.</para>
</entry>
</row></tbody></tgroup></informaltable>
&return-value-dvb;
@@ -1087,7 +1094,15 @@ role="subsection"><title>DMX_REMOVE_PID</title>
</para>
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
-<para>This ioctl is undocumented. Documentation is welcome.</para>
+<para>This ioctl call allows to remove a PID when multiple PIDs are set on a
+transport stream filter, e. g. a filter previously set up with output equal to
+DMX_OUT_TSDEMUX_TAP, created via either DMX_SET_PES_FILTER or DMX_ADD_PID.
+</para></entry></row><row><entry align="char"><para>
+It is used by readers of /dev/dvb/adapterX/demuxY.
+</para></entry></row><row><entry align="char"><para>
+It may be called at any time, i.e. before or after the first filter on the
+shared file descriptor was started. It makes it possible to record multiple
+services without the need to de-multiplex or re-multiplex TS packets.</para>
</entry>
</row></tbody></tgroup></informaltable>
<para>SYNOPSIS
@@ -1120,7 +1135,7 @@ role="subsection"><title>DMX_REMOVE_PID</title>
</para>
</entry><entry
align="char">
-<para>Undocumented.</para>
+<para>PID of the PES filter to be removed.</para>
</entry>
</row></tbody></tgroup></informaltable>
&return-value-dvb;
diff --git a/Documentation/DocBook/media/dvb/dvbapi.xml b/Documentation/DocBook/media/dvb/dvbapi.xml
index 0197bcc7842d..4c15396c67e5 100644
--- a/Documentation/DocBook/media/dvb/dvbapi.xml
+++ b/Documentation/DocBook/media/dvb/dvbapi.xml
@@ -18,7 +18,7 @@
<firstname>Mauro</firstname>
<othername role="mi">Carvalho</othername>
<surname>Chehab</surname>
-<affiliation><address><email>mchehab@redhat.com</email></address></affiliation>
+<affiliation><address><email>m.chehab@samsung.com</email></address></affiliation>
<contrib>Ported document to Docbook XML.</contrib>
</author>
</authorgroup>
@@ -28,7 +28,7 @@
<holder>Convergence GmbH</holder>
</copyright>
<copyright>
- <year>2009-2012</year>
+ <year>2009-2014</year>
<holder>Mauro Carvalho Chehab</holder>
</copyright>
diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml
index a9b15e34c5b2..24c22cabc668 100644
--- a/Documentation/DocBook/media/dvb/dvbproperty.xml
+++ b/Documentation/DocBook/media/dvb/dvbproperty.xml
@@ -196,7 +196,7 @@ get/set up to 64 properties. The actual meaning of each property is described on
<para>1)For satellital delivery systems, it is measured in kHz.
For the other ones, it is measured in Hz.</para>
<para>2)For ISDB-T, the channels are usually transmitted with an offset of 143kHz.
- E.g. a valid frequncy could be 474143 kHz. The stepping is bound to the bandwidth of
+ E.g. a valid frequency could be 474143 kHz. The stepping is bound to the bandwidth of
the channel which is 6MHz.</para>
<para>3)As in ISDB-Tsb the channel consists of only one or three segments the
diff --git a/Documentation/DocBook/media/dvb/frontend.xml b/Documentation/DocBook/media/dvb/frontend.xml
index 0d6e81bd9ed2..8a6a6ff27af5 100644
--- a/Documentation/DocBook/media/dvb/frontend.xml
+++ b/Documentation/DocBook/media/dvb/frontend.xml
@@ -744,7 +744,7 @@ typedef enum fe_hierarchy {
</para>
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
-<para>int ioctl(int fd, int request = <link linkend="FE_READ_SNR">FE_READ_SNR</link>, int16_t
+<para>int ioctl(int fd, int request = <link linkend="FE_READ_SNR">FE_READ_SNR</link>, uint16_t
&#x22C6;snr);</para>
</entry>
</row></tbody></tgroup></informaltable>
@@ -766,7 +766,7 @@ typedef enum fe_hierarchy {
</entry>
</row><row><entry
align="char">
-<para>int16_t *snr</para>
+<para>uint16_t *snr</para>
</entry><entry
align="char">
<para>The signal-to-noise ratio is stored into *snr.</para>
@@ -791,7 +791,7 @@ typedef enum fe_hierarchy {
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
<para>int ioctl( int fd, int request =
- <link linkend="FE_READ_SIGNAL_STRENGTH">FE_READ_SIGNAL_STRENGTH</link>, int16_t &#x22C6;strength);</para>
+ <link linkend="FE_READ_SIGNAL_STRENGTH">FE_READ_SIGNAL_STRENGTH</link>, uint16_t &#x22C6;strength);</para>
</entry>
</row></tbody></tgroup></informaltable>
@@ -814,7 +814,7 @@ typedef enum fe_hierarchy {
</entry>
</row><row><entry
align="char">
-<para>int16_t *strength</para>
+<para>uint16_t *strength</para>
</entry><entry
align="char">
<para>The signal strength value is stored into *strength.</para>
diff --git a/Documentation/DocBook/media/v4l/common.xml b/Documentation/DocBook/media/v4l/common.xml
index 1ddf354aa997..71f6bf9e735e 100644
--- a/Documentation/DocBook/media/v4l/common.xml
+++ b/Documentation/DocBook/media/v4l/common.xml
@@ -38,70 +38,41 @@ the basic concepts applicable to all devices.</para>
<para>V4L2 drivers are implemented as kernel modules, loaded
manually by the system administrator or automatically when a device is
-first opened. The driver modules plug into the "videodev" kernel
+first discovered. The driver modules plug into the "videodev" kernel
module. It provides helper functions and a common application
interface specified in this document.</para>
<para>Each driver thus loaded registers one or more device nodes
-with major number 81 and a minor number between 0 and 255. Assigning
-minor numbers to V4L2 devices is entirely up to the system administrator,
-this is primarily intended to solve conflicts between devices.<footnote>
- <para>Access permissions are associated with character
-device special files, hence we must ensure device numbers cannot
-change with the module load order. To this end minor numbers are no
-longer automatically assigned by the "videodev" module as in V4L but
-requested by the driver. The defaults will suffice for most people
-unless two drivers compete for the same minor numbers.</para>
- </footnote> The module options to select minor numbers are named
-after the device special file with a "_nr" suffix. For example "video_nr"
-for <filename>/dev/video</filename> video capture devices. The number is
-an offset to the base minor number associated with the device type.
-<footnote>
- <para>In earlier versions of the V4L2 API the module options
-where named after the device special file with a "unit_" prefix, expressing
-the minor number itself, not an offset. Rationale for this change is unknown.
-Lastly the naming and semantics are just a convention among driver writers,
-the point to note is that minor numbers are not supposed to be hardcoded
-into drivers.</para>
- </footnote> When the driver supports multiple devices of the same
-type more than one minor number can be assigned, separated by commas:
-<informalexample>
+with major number 81 and a minor number between 0 and 255. Minor numbers
+are allocated dynamically unless the kernel is compiled with the kernel
+option CONFIG_VIDEO_FIXED_MINOR_RANGES. In that case minor numbers are
+allocated in ranges depending on the device node type (video, radio, etc.).</para>
+
+ <para>Many drivers support "video_nr", "radio_nr" or "vbi_nr"
+module options to select specific video/radio/vbi node numbers. This allows
+the user to request that the device node is named e.g. /dev/video5 instead
+of leaving it to chance. When the driver supports multiple devices of the same
+type more than one device node number can be assigned, separated by commas:
+ <informalexample>
<screen>
-&gt; insmod mydriver.o video_nr=0,1 radio_nr=0,1</screen>
+&gt; modprobe mydriver video_nr=0,1 radio_nr=0,1</screen>
</informalexample></para>
<para>In <filename>/etc/modules.conf</filename> this may be
written as: <informalexample>
<screen>
-alias char-major-81-0 mydriver
-alias char-major-81-1 mydriver
-alias char-major-81-64 mydriver <co id="alias" />
-options mydriver video_nr=0,1 radio_nr=0,1 <co id="options" />
+options mydriver video_nr=0,1 radio_nr=0,1
</screen>
- <calloutlist>
- <callout arearefs="alias">
- <para>When an application attempts to open a device
-special file with major number 81 and minor number 0, 1, or 64, load
-"mydriver" (and the "videodev" module it depends upon).</para>
- </callout>
- <callout arearefs="options">
- <para>Register the first two video capture devices with
-minor number 0 and 1 (base number is 0), the first two radio device
-with minor number 64 and 65 (base 64).</para>
- </callout>
- </calloutlist>
- </informalexample> When no minor number is given as module
-option the driver supplies a default. <xref linkend="devices" />
-recommends the base minor numbers to be used for the various device
-types. Obviously minor numbers must be unique. When the number is
-already in use the <emphasis>offending device</emphasis> will not be
-registered. <!-- Blessed by Linus Torvalds on
-linux-kernel@vger.kernel.org, 2002-11-20. --></para>
-
- <para>By convention system administrators create various
-character device special files with these major and minor numbers in
-the <filename>/dev</filename> directory. The names recommended for the
-different V4L2 device types are listed in <xref linkend="devices" />.
+ </informalexample> When no device node number is given as module
+option the driver supplies a default.</para>
+
+ <para>Normally udev will create the device nodes in /dev automatically
+for you. If udev is not installed, then you need to enable the
+CONFIG_VIDEO_FIXED_MINOR_RANGES kernel option in order to be able to correctly
+relate a minor number to a device node number. I.e., you need to be certain
+that minor number 5 maps to device node name video5. With this kernel option
+different device types have different minor number ranges. These ranges are
+listed in <xref linkend="devices" />.
</para>
<para>The creation of character special files (with
@@ -110,85 +81,66 @@ devices cannot be opened by major and minor number. That means
applications cannot <emphasis>reliable</emphasis> scan for loaded or
installed drivers. The user must enter a device name, or the
application can try the conventional device names.</para>
-
- <para>Under the device filesystem (devfs) the minor number
-options are ignored. V4L2 drivers (or by proxy the "videodev" module)
-automatically create the required device files in the
-<filename>/dev/v4l</filename> directory using the conventional device
-names above.</para>
</section>
<section id="related">
<title>Related Devices</title>
- <para>Devices can support several related functions. For example
-video capturing, video overlay and VBI capturing are related because
-these functions share, amongst other, the same video input and tuner
-frequency. V4L and earlier versions of V4L2 used the same device name
-and minor number for video capturing and overlay, but different ones
-for VBI. Experience showed this approach has several problems<footnote>
- <para>Given a device file name one cannot reliable find
-related devices. For once names are arbitrary and in a system with
-multiple devices, where only some support VBI capturing, a
-<filename>/dev/video2</filename> is not necessarily related to
-<filename>/dev/vbi2</filename>. The V4L
-<constant>VIDIOCGUNIT</constant> ioctl would require a search for a
-device file with a particular major and minor number.</para>
- </footnote>, and to make things worse the V4L videodev module
-used to prohibit multiple opens of a device.</para>
-
- <para>As a remedy the present version of the V4L2 API relaxed the
-concept of device types with specific names and minor numbers. For
-compatibility with old applications drivers must still register different
-minor numbers to assign a default function to the device. But if related
-functions are supported by the driver they must be available under all
-registered minor numbers. The desired function can be selected after
-opening the device as described in <xref linkend="devices" />.</para>
-
- <para>Imagine a driver supporting video capturing, video
-overlay, raw VBI capturing, and FM radio reception. It registers three
-devices with minor number 0, 64 and 224 (this numbering scheme is
-inherited from the V4L API). Regardless if
-<filename>/dev/video</filename> (81, 0) or
-<filename>/dev/vbi</filename> (81, 224) is opened the application can
-select any one of the video capturing, overlay or VBI capturing
-functions. Without programming (e.&nbsp;g. reading from the device
-with <application>dd</application> or <application>cat</application>)
-<filename>/dev/video</filename> captures video images, while
-<filename>/dev/vbi</filename> captures raw VBI data.
-<filename>/dev/radio</filename> (81, 64) is invariable a radio device,
-unrelated to the video functions. Being unrelated does not imply the
-devices can be used at the same time, however. The &func-open;
-function may very well return an &EBUSY;.</para>
+ <para>Devices can support several functions. For example
+video capturing, VBI capturing and radio support.</para>
+
+ <para>The V4L2 API creates different nodes for each of these functions.</para>
+
+ <para>The V4L2 API was designed with the idea that one device node could support
+all functions. However, in practice this never worked: this 'feature'
+was never used by applications and many drivers did not support it and if
+they did it was certainly never tested. In addition, switching a device
+node between different functions only works when using the streaming I/O
+API, not with the read()/write() API.</para>
+
+ <para>Today each device node supports just one function.</para>
<para>Besides video input or output the hardware may also
support audio sampling or playback. If so, these functions are
-implemented as OSS or ALSA PCM devices and eventually OSS or ALSA
-audio mixer. The V4L2 API makes no provisions yet to find these
-related devices. If you have an idea please write to the linux-media
-mailing list: &v4l-ml;.</para>
+implemented as ALSA PCM devices with optional ALSA audio mixer
+devices.</para>
+
+ <para>One problem with all these devices is that the V4L2 API
+makes no provisions to find these related devices. Some really
+complex devices use the Media Controller (see <xref linkend="media_controller" />)
+which can be used for this purpose. But most drivers do not use it,
+and while some code exists that uses sysfs to discover related devices
+(see libmedia_dev in the <ulink url="http://git.linuxtv.org/v4l-utils/">v4l-utils</ulink>
+git repository), there is no library yet that can provide a single API towards
+both Media Controller-based devices and devices that do not use the Media Controller.
+If you want to work on this please write to the linux-media mailing list: &v4l-ml;.</para>
</section>
<section>
<title>Multiple Opens</title>
- <para>In general, V4L2 devices can be opened more than once.
+ <para>V4L2 devices can be opened more than once.<footnote><para>
+There are still some old and obscure drivers that have not been updated to
+allow for multiple opens. This implies that for such drivers &func-open; can
+return an &EBUSY; when the device is already in use.</para></footnote>
When this is supported by the driver, users can for example start a
"panel" application to change controls like brightness or audio
volume, while another application captures video and audio. In other words, panel
-applications are comparable to an OSS or ALSA audio mixer application.
-When a device supports multiple functions like capturing and overlay
-<emphasis>simultaneously</emphasis>, multiple opens allow concurrent
-use of the device by forked processes or specialized applications.</para>
-
- <para>Multiple opens are optional, although drivers should
-permit at least concurrent accesses without data exchange, &ie; panel
-applications. This implies &func-open; can return an &EBUSY; when the
-device is already in use, as well as &func-ioctl; functions initiating
-data exchange (namely the &VIDIOC-S-FMT; ioctl), and the &func-read;
-and &func-write; functions.</para>
-
- <para>Mere opening a V4L2 device does not grant exclusive
+applications are comparable to an ALSA audio mixer application.
+Just opening a V4L2 device should not change the state of the device.<footnote>
+<para>Unfortunately, opening a radio device often switches the state of the
+device to radio mode in many drivers. This behavior should be fixed eventually
+as it violates the V4L2 specification.</para></footnote></para>
+
+ <para>Once an application has allocated the memory buffers needed for
+streaming data (by calling the &VIDIOC-REQBUFS; or &VIDIOC-CREATE-BUFS; ioctls,
+or implicitly by calling the &func-read; or &func-write; functions) that
+application (filehandle) becomes the owner of the device. It is no longer
+allowed to make changes that would affect the buffer sizes (e.g. by calling
+the &VIDIOC-S-FMT; ioctl) and other applications are no longer allowed to allocate
+buffers or start or stop streaming. The &EBUSY; will be returned instead.</para>
+
+ <para>Merely opening a V4L2 device does not grant exclusive
access.<footnote>
<para>Drivers could recognize the
<constant>O_EXCL</constant> open flag. Presently this is not required,
@@ -206,12 +158,7 @@ additional access privileges using the priority mechanism described in
<para>V4L2 drivers should not support multiple applications
reading or writing the same data stream on a device by copying
buffers, time multiplexing or similar means. This is better handled by
-a proxy application in user space. When the driver supports stream
-sharing anyway it must be implemented transparently. The V4L2 API does
-not specify how conflicts are solved. <!-- For example O_EXCL when the
-application does not want to be preempted, PROT_READ mmapped buffers
-which can be mapped twice, what happens when image formats do not
-match etc.--></para>
+a proxy application in user space.</para>
</section>
<section>
@@ -240,15 +187,15 @@ methods</link> supported by the device.</para>
<para>Starting with kernel version 3.1, VIDIOC-QUERYCAP will return the
V4L2 API version used by the driver, with generally matches the Kernel version.
-There's no need of using &VIDIOC-QUERYCAP; to check if an specific ioctl is
-supported, the V4L2 core now returns ENOIOCTLCMD if a driver doesn't provide
+There's no need of using &VIDIOC-QUERYCAP; to check if a specific ioctl is
+supported, the V4L2 core now returns ENOTTY if a driver doesn't provide
support for an ioctl.</para>
<para>Other features can be queried
by calling the respective ioctl, for example &VIDIOC-ENUMINPUT;
to learn about the number, types and names of video connectors on the
device. Although abstraction is a major objective of this API, the
-ioctl also allows driver specific applications to reliable identify
+&VIDIOC-QUERYCAP; ioctl also allows driver specific applications to reliably identify
the driver.</para>
<para>All V4L2 drivers must support
@@ -278,9 +225,7 @@ Applications requiring a different priority will usually call
the &VIDIOC-QUERYCAP; ioctl.</para>
<para>Ioctls changing driver properties, such as &VIDIOC-S-INPUT;,
-return an &EBUSY; after another application obtained higher priority.
-An event mechanism to notify applications about asynchronous property
-changes has been proposed but not added yet.</para>
+return an &EBUSY; after another application obtained higher priority.</para>
</section>
<section id="video">
@@ -288,9 +233,9 @@ changes has been proposed but not added yet.</para>
<para>Video inputs and outputs are physical connectors of a
device. These can be for example RF connectors (antenna/cable), CVBS
-a.k.a. Composite Video, S-Video or RGB connectors. Only video and VBI
-capture devices have inputs, output devices have outputs, at least one
-each. Radio devices have no video inputs or outputs.</para>
+a.k.a. Composite Video, S-Video or RGB connectors. Video and VBI
+capture devices have inputs. Video and VBI output devices have outputs,
+at least one each. Radio devices have no video inputs or outputs.</para>
<para>To learn about the number and attributes of the
available inputs and outputs applications can enumerate them with the
@@ -299,30 +244,13 @@ available inputs and outputs applications can enumerate them with the
ioctl also contains signal status information applicable when the
current video input is queried.</para>
- <para>The &VIDIOC-G-INPUT; and &VIDIOC-G-OUTPUT; ioctl return the
+ <para>The &VIDIOC-G-INPUT; and &VIDIOC-G-OUTPUT; ioctls return the
index of the current video input or output. To select a different
input or output applications call the &VIDIOC-S-INPUT; and
-&VIDIOC-S-OUTPUT; ioctl. Drivers must implement all the input ioctls
+&VIDIOC-S-OUTPUT; ioctls. Drivers must implement all the input ioctls
when the device has one or more inputs, all the output ioctls when the
device has one or more outputs.</para>
- <!--
- <figure id=io-tree>
- <title>Input and output enumeration is the root of most device properties.</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="links.pdf" format="ps" />
- </imageobject>
- <imageobject>
- <imagedata fileref="links.gif" format="gif" />
- </imageobject>
- <textobject>
- <phrase>Links between various device property structures.</phrase>
- </textobject>
- </mediaobject>
- </figure>
- -->
-
<example>
<title>Information about the current video input</title>
@@ -330,20 +258,20 @@ device has one or more outputs.</para>
&v4l2-input; input;
int index;
-if (-1 == ioctl (fd, &VIDIOC-G-INPUT;, &amp;index)) {
- perror ("VIDIOC_G_INPUT");
- exit (EXIT_FAILURE);
+if (-1 == ioctl(fd, &VIDIOC-G-INPUT;, &amp;index)) {
+ perror("VIDIOC_G_INPUT");
+ exit(EXIT_FAILURE);
}
-memset (&amp;input, 0, sizeof (input));
+memset(&amp;input, 0, sizeof(input));
input.index = index;
-if (-1 == ioctl (fd, &VIDIOC-ENUMINPUT;, &amp;input)) {
- perror ("VIDIOC_ENUMINPUT");
- exit (EXIT_FAILURE);
+if (-1 == ioctl(fd, &VIDIOC-ENUMINPUT;, &amp;input)) {
+ perror("VIDIOC_ENUMINPUT");
+ exit(EXIT_FAILURE);
}
-printf ("Current input: %s\n", input.name);
+printf("Current input: %s\n", input.name);
</programlisting>
</example>
@@ -355,9 +283,9 @@ int index;
index = 0;
-if (-1 == ioctl (fd, &VIDIOC-S-INPUT;, &amp;index)) {
- perror ("VIDIOC_S_INPUT");
- exit (EXIT_FAILURE);
+if (-1 == ioctl(fd, &VIDIOC-S-INPUT;, &amp;index)) {
+ perror("VIDIOC_S_INPUT");
+ exit(EXIT_FAILURE);
}
</programlisting>
</example>
@@ -397,7 +325,7 @@ available inputs and outputs applications can enumerate them with the
also contains signal status information applicable when the current
audio input is queried.</para>
- <para>The &VIDIOC-G-AUDIO; and &VIDIOC-G-AUDOUT; ioctl report
+ <para>The &VIDIOC-G-AUDIO; and &VIDIOC-G-AUDOUT; ioctls report
the current audio input and output, respectively. Note that, unlike
&VIDIOC-G-INPUT; and &VIDIOC-G-OUTPUT; these ioctls return a structure
as <constant>VIDIOC_ENUMAUDIO</constant> and
@@ -408,11 +336,11 @@ applications call the &VIDIOC-S-AUDIO; ioctl. To select an audio
output (which presently has no changeable properties) applications
call the &VIDIOC-S-AUDOUT; ioctl.</para>
- <para>Drivers must implement all input ioctls when the device
-has one or more inputs, all output ioctls when the device has one
-or more outputs. When the device has any audio inputs or outputs the
-driver must set the <constant>V4L2_CAP_AUDIO</constant> flag in the
-&v4l2-capability; returned by the &VIDIOC-QUERYCAP; ioctl.</para>
+ <para>Drivers must implement all audio input ioctls when the device
+has multiple selectable audio inputs, all audio output ioctls when the
+device has multiple selectable audio outputs. When the device has any
+audio inputs or outputs the driver must set the <constant>V4L2_CAP_AUDIO</constant>
+flag in the &v4l2-capability; returned by the &VIDIOC-QUERYCAP; ioctl.</para>
<example>
<title>Information about the current audio input</title>
@@ -420,14 +348,14 @@ driver must set the <constant>V4L2_CAP_AUDIO</constant> flag in the
<programlisting>
&v4l2-audio; audio;
-memset (&amp;audio, 0, sizeof (audio));
+memset(&amp;audio, 0, sizeof(audio));
-if (-1 == ioctl (fd, &VIDIOC-G-AUDIO;, &amp;audio)) {
- perror ("VIDIOC_G_AUDIO");
- exit (EXIT_FAILURE);
+if (-1 == ioctl(fd, &VIDIOC-G-AUDIO;, &amp;audio)) {
+ perror("VIDIOC_G_AUDIO");
+ exit(EXIT_FAILURE);
}
-printf ("Current input: %s\n", audio.name);
+printf("Current input: %s\n", audio.name);
</programlisting>
</example>
@@ -437,13 +365,13 @@ printf ("Current input: %s\n", audio.name);
<programlisting>
&v4l2-audio; audio;
-memset (&amp;audio, 0, sizeof (audio)); /* clear audio.mode, audio.reserved */
+memset(&amp;audio, 0, sizeof(audio)); /* clear audio.mode, audio.reserved */
audio.index = 0;
-if (-1 == ioctl (fd, &VIDIOC-S-AUDIO;, &amp;audio)) {
- perror ("VIDIOC_S_AUDIO");
- exit (EXIT_FAILURE);
+if (-1 == ioctl(fd, &VIDIOC-S-AUDIO;, &amp;audio)) {
+ perror("VIDIOC_S_AUDIO");
+ exit(EXIT_FAILURE);
}
</programlisting>
</example>
@@ -468,7 +396,7 @@ the tuner.</para>
video inputs.</para>
<para>To query and change tuner properties applications use the
-&VIDIOC-G-TUNER; and &VIDIOC-S-TUNER; ioctl, respectively. The
+&VIDIOC-G-TUNER; and &VIDIOC-S-TUNER; ioctls, respectively. The
&v4l2-tuner; returned by <constant>VIDIOC_G_TUNER</constant> also
contains signal status information applicable when the tuner of the
current video or radio input is queried. Note that
@@ -533,7 +461,7 @@ standards or variations of standards. Each video input and output may
support another set of standards. This set is reported by the
<structfield>std</structfield> field of &v4l2-input; and
&v4l2-output; returned by the &VIDIOC-ENUMINPUT; and
-&VIDIOC-ENUMOUTPUT; ioctl, respectively.</para>
+&VIDIOC-ENUMOUTPUT; ioctls, respectively.</para>
<para>V4L2 defines one bit for each analog video standard
currently in use worldwide, and sets aside bits for driver defined
@@ -564,28 +492,10 @@ automatically.</para>
<para>To query and select the standard used by the current video
input or output applications call the &VIDIOC-G-STD; and
&VIDIOC-S-STD; ioctl, respectively. The <emphasis>received</emphasis>
-standard can be sensed with the &VIDIOC-QUERYSTD; ioctl. Note that the parameter of all these ioctls is a pointer to a &v4l2-std-id; type (a standard set), <emphasis>not</emphasis> an index into the standard enumeration.<footnote>
- <para>An alternative to the current scheme is to use pointers
-to indices as arguments of <constant>VIDIOC_G_STD</constant> and
-<constant>VIDIOC_S_STD</constant>, the &v4l2-input; and
-&v4l2-output; <structfield>std</structfield> field would be a set of
-indices like <structfield>audioset</structfield>.</para>
- <para>Indices are consistent with the rest of the API
-and identify the standard unambiguously. In the present scheme of
-things an enumerated standard is looked up by &v4l2-std-id;. Now the
-standards supported by the inputs of a device can overlap. Just
-assume the tuner and composite input in the example above both
-exist on a device. An enumeration of "PAL-B/G", "PAL-H/I" suggests
-a choice which does not exist. We cannot merge or omit sets, because
-applications would be unable to find the standards reported by
-<constant>VIDIOC_G_STD</constant>. That leaves separate enumerations
-for each input. Also selecting a standard by &v4l2-std-id; can be
-ambiguous. Advantage of this method is that applications need not
-identify the standard indirectly, after enumerating.</para><para>So in
-summary, the lookup itself is unavoidable. The difference is only
-whether the lookup is necessary to find an enumerated standard or to
-switch to a standard by &v4l2-std-id;.</para>
- </footnote> Drivers must implement all video standard ioctls
+standard can be sensed with the &VIDIOC-QUERYSTD; ioctl. Note that the
+parameter of all these ioctls is a pointer to a &v4l2-std-id; type
+(a standard set), <emphasis>not</emphasis> an index into the standard
+enumeration. Drivers must implement all video standard ioctls
when the device has one or more video inputs or outputs.</para>
<para>Special rules apply to devices such as USB cameras where the notion of video
@@ -604,17 +514,10 @@ to zero and the <constant>VIDIOC_G_STD</constant>,
<constant>VIDIOC_S_STD</constant>,
<constant>VIDIOC_QUERYSTD</constant> and
<constant>VIDIOC_ENUMSTD</constant> ioctls shall return the
-&ENOTTY;.<footnote>
- <para>See <xref linkend="buffer" /> for a rationale.</para>
+&ENOTTY; or the &EINVAL;.</para>
<para>Applications can make use of the <xref linkend="input-capabilities" /> and
<xref linkend="output-capabilities"/> flags to determine whether the video standard ioctls
-are available for the device.</para>
-
- <para>See <xref linkend="buffer" /> for a rationale. Probably
-even USB cameras follow some well known video standard. It might have
-been better to explicitly indicate elsewhere if a device cannot live
-up to normal expectations, instead of this exception.</para>
- </footnote></para>
+can be used with the given input or output.</para>
<example>
<title>Information about the current video standard</title>
@@ -623,22 +526,22 @@ up to normal expectations, instead of this exception.</para>
&v4l2-std-id; std_id;
&v4l2-standard; standard;
-if (-1 == ioctl (fd, &VIDIOC-G-STD;, &amp;std_id)) {
+if (-1 == ioctl(fd, &VIDIOC-G-STD;, &amp;std_id)) {
/* Note when VIDIOC_ENUMSTD always returns ENOTTY this
is no video device or it falls under the USB exception,
and VIDIOC_G_STD returning ENOTTY is no error. */
- perror ("VIDIOC_G_STD");
- exit (EXIT_FAILURE);
+ perror("VIDIOC_G_STD");
+ exit(EXIT_FAILURE);
}
-memset (&amp;standard, 0, sizeof (standard));
+memset(&amp;standard, 0, sizeof(standard));
standard.index = 0;
-while (0 == ioctl (fd, &VIDIOC-ENUMSTD;, &amp;standard)) {
+while (0 == ioctl(fd, &VIDIOC-ENUMSTD;, &amp;standard)) {
if (standard.id &amp; std_id) {
- printf ("Current video standard: %s\n", standard.name);
- exit (EXIT_SUCCESS);
+ printf("Current video standard: %s\n", standard.name);
+ exit(EXIT_SUCCESS);
}
standard.index++;
@@ -648,8 +551,8 @@ while (0 == ioctl (fd, &VIDIOC-ENUMSTD;, &amp;standard)) {
empty unless this device falls under the USB exception. */
if (errno == EINVAL || standard.index == 0) {
- perror ("VIDIOC_ENUMSTD");
- exit (EXIT_FAILURE);
+ perror("VIDIOC_ENUMSTD");
+ exit(EXIT_FAILURE);
}
</programlisting>
</example>
@@ -662,26 +565,26 @@ input</title>
&v4l2-input; input;
&v4l2-standard; standard;
-memset (&amp;input, 0, sizeof (input));
+memset(&amp;input, 0, sizeof(input));
-if (-1 == ioctl (fd, &VIDIOC-G-INPUT;, &amp;input.index)) {
- perror ("VIDIOC_G_INPUT");
- exit (EXIT_FAILURE);
+if (-1 == ioctl(fd, &VIDIOC-G-INPUT;, &amp;input.index)) {
+ perror("VIDIOC_G_INPUT");
+ exit(EXIT_FAILURE);
}
-if (-1 == ioctl (fd, &VIDIOC-ENUMINPUT;, &amp;input)) {
- perror ("VIDIOC_ENUM_INPUT");
- exit (EXIT_FAILURE);
+if (-1 == ioctl(fd, &VIDIOC-ENUMINPUT;, &amp;input)) {
+ perror("VIDIOC_ENUM_INPUT");
+ exit(EXIT_FAILURE);
}
-printf ("Current input %s supports:\n", input.name);
+printf("Current input %s supports:\n", input.name);
-memset (&amp;standard, 0, sizeof (standard));
+memset(&amp;standard, 0, sizeof(standard));
standard.index = 0;
-while (0 == ioctl (fd, &VIDIOC-ENUMSTD;, &amp;standard)) {
+while (0 == ioctl(fd, &VIDIOC-ENUMSTD;, &amp;standard)) {
if (standard.id &amp; input.std)
- printf ("%s\n", standard.name);
+ printf("%s\n", standard.name);
standard.index++;
}
@@ -690,8 +593,8 @@ while (0 == ioctl (fd, &VIDIOC-ENUMSTD;, &amp;standard)) {
empty unless this device falls under the USB exception. */
if (errno != EINVAL || standard.index == 0) {
- perror ("VIDIOC_ENUMSTD");
- exit (EXIT_FAILURE);
+ perror("VIDIOC_ENUMSTD");
+ exit(EXIT_FAILURE);
}
</programlisting>
</example>
@@ -703,21 +606,21 @@ if (errno != EINVAL || standard.index == 0) {
&v4l2-input; input;
&v4l2-std-id; std_id;
-memset (&amp;input, 0, sizeof (input));
+memset(&amp;input, 0, sizeof(input));
-if (-1 == ioctl (fd, &VIDIOC-G-INPUT;, &amp;input.index)) {
- perror ("VIDIOC_G_INPUT");
- exit (EXIT_FAILURE);
+if (-1 == ioctl(fd, &VIDIOC-G-INPUT;, &amp;input.index)) {
+ perror("VIDIOC_G_INPUT");
+ exit(EXIT_FAILURE);
}
-if (-1 == ioctl (fd, &VIDIOC-ENUMINPUT;, &amp;input)) {
- perror ("VIDIOC_ENUM_INPUT");
- exit (EXIT_FAILURE);
+if (-1 == ioctl(fd, &VIDIOC-ENUMINPUT;, &amp;input)) {
+ perror("VIDIOC_ENUM_INPUT");
+ exit(EXIT_FAILURE);
}
if (0 == (input.std &amp; V4L2_STD_PAL_BG)) {
- fprintf (stderr, "Oops. B/G PAL is not supported.\n");
- exit (EXIT_FAILURE);
+ fprintf(stderr, "Oops. B/G PAL is not supported.\n");
+ exit(EXIT_FAILURE);
}
/* Note this is also supposed to work when only B
@@ -725,9 +628,9 @@ if (0 == (input.std &amp; V4L2_STD_PAL_BG)) {
std_id = V4L2_STD_PAL_BG;
-if (-1 == ioctl (fd, &VIDIOC-S-STD;, &amp;std_id)) {
- perror ("VIDIOC_S_STD");
- exit (EXIT_FAILURE);
+if (-1 == ioctl(fd, &VIDIOC-S-STD;, &amp;std_id)) {
+ perror("VIDIOC_S_STD");
+ exit(EXIT_FAILURE);
}
</programlisting>
</example>
@@ -740,26 +643,25 @@ corresponding video timings. Today there are many more different hardware interf
such as High Definition TV interfaces (HDMI), VGA, DVI connectors etc., that carry
video signals and there is a need to extend the API to select the video timings
for these interfaces. Since it is not possible to extend the &v4l2-std-id; due to
-the limited bits available, a new set of IOCTLs was added to set/get video timings at
-the input and output: </para><itemizedlist>
- <listitem>
- <para>DV Timings: This will allow applications to define detailed
-video timings for the interface. This includes parameters such as width, height,
-polarities, frontporch, backporch etc. The <filename>linux/v4l2-dv-timings.h</filename>
+the limited bits available, a new set of ioctls was added to set/get video timings at
+the input and output.</para>
+
+ <para>These ioctls deal with the detailed digital video timings that define
+each video format. This includes parameters such as the active video width and height,
+signal polarities, frontporches, backporches, sync widths etc. The <filename>linux/v4l2-dv-timings.h</filename>
header can be used to get the timings of the formats in the <xref linkend="cea861" /> and
<xref linkend="vesadmt" /> standards.
</para>
- </listitem>
- </itemizedlist>
- <para>To enumerate and query the attributes of the DV timings supported by a device,
+
+ <para>To enumerate and query the attributes of the DV timings supported by a device
applications use the &VIDIOC-ENUM-DV-TIMINGS; and &VIDIOC-DV-TIMINGS-CAP; ioctls.
- To set DV timings for the device, applications use the
+ To set DV timings for the device applications use the
&VIDIOC-S-DV-TIMINGS; ioctl and to get current DV timings they use the
&VIDIOC-G-DV-TIMINGS; ioctl. To detect the DV timings as seen by the video receiver applications
use the &VIDIOC-QUERY-DV-TIMINGS; ioctl.</para>
<para>Applications can make use of the <xref linkend="input-capabilities" /> and
-<xref linkend="output-capabilities"/> flags to decide what ioctls are available to set the
-video timings for the device.</para>
+<xref linkend="output-capabilities"/> flags to determine whether the digital video ioctls
+can be used with the given input or output.</para>
</section>
&sub-controls;
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml
index c4cac6dbf9af..eee6f0f4aa43 100644
--- a/Documentation/DocBook/media/v4l/compat.xml
+++ b/Documentation/DocBook/media/v4l/compat.xml
@@ -397,7 +397,7 @@ linkend="control" />.</para>
<para>The <structfield>depth</structfield> (average number of
bits per pixel) of a video image is implied by the selected image
-format. V4L2 does not explicitely provide such information assuming
+format. V4L2 does not explicitly provide such information assuming
applications recognizing the format are aware of the image depth and
others need not know. The <structfield>palette</structfield> field
moved into the &v4l2-pix-format;:<informaltable>
@@ -2535,6 +2535,16 @@ fields changed from _s32 to _u32.
</orderedlist>
</section>
+ <section>
+ <title>V4L2 in Linux 3.15</title>
+ <orderedlist>
+ <listitem>
+ <para>Added Software Defined Radio (SDR) Interface.
+ </para>
+ </listitem>
+ </orderedlist>
+ </section>
+
<section id="other">
<title>Relation of V4L2 to other Linux multimedia APIs</title>
@@ -2651,6 +2661,9 @@ ioctls.</para>
<listitem>
<para>Exporting DMABUF files using &VIDIOC-EXPBUF; ioctl.</para>
</listitem>
+ <listitem>
+ <para>Software Defined Radio (SDR) Interface, <xref linkend="sdr" />.</para>
+ </listitem>
</itemizedlist>
</section>
diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml
index a5a3188e5af7..47198eef75a4 100644
--- a/Documentation/DocBook/media/v4l/controls.xml
+++ b/Documentation/DocBook/media/v4l/controls.xml
@@ -2258,6 +2258,26 @@ Applicable to the MPEG1, MPEG2, MPEG4 encoders.</entry>
VBV buffer control.</entry>
</row>
+ <row><entry></entry></row>
+ <row id="v4l2-mpeg-video-hor-search-range">
+ <entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE</constant>&nbsp;</entry>
+ <entry>integer</entry>
+ </row>
+ <row><entry spanname="descr">Horizontal search range defines maximum horizontal search area in pixels
+to search and match for the present Macroblock (MB) in the reference picture. This V4L2 control macro is used to set
+horizontal search range for motion estimation module in video encoder.</entry>
+ </row>
+
+ <row><entry></entry></row>
+ <row id="v4l2-mpeg-video-vert-search-range">
+ <entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE</constant>&nbsp;</entry>
+ <entry>integer</entry>
+ </row>
+ <row><entry spanname="descr">Vertical search range defines maximum vertical search area in pixels
+to search and match for the present Macroblock (MB) in the reference picture. This V4L2 control macro is used to set
+vertical search range for motion estimation module in video encoder.</entry>
+ </row>
+
<row><entry></entry></row>
<row>
<entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE</constant>&nbsp;</entry>
@@ -4370,6 +4390,24 @@ interface and may change in the future.</para>
<entry>The flash controller has detected a short or open
circuit condition on the indicator LED.</entry>
</row>
+ <row>
+ <entry><constant>V4L2_FLASH_FAULT_UNDER_VOLTAGE</constant></entry>
+ <entry>Flash controller voltage to the flash LED
+ has been below the minimum limit specific to the flash
+ controller.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_FLASH_FAULT_INPUT_VOLTAGE</constant></entry>
+ <entry>The input voltage of the flash controller is below
+ the limit under which strobing the flash at full current
+ will not be possible.The condition persists until this flag
+ is no longer set.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE</constant></entry>
+ <entry>The temperature of the LED has exceeded its
+ allowed upper limit.</entry>
+ </row>
</tbody>
</entrytbl>
</row>
@@ -4971,4 +5009,142 @@ defines possible values for de-emphasis. Here they are:</entry>
</table>
</section>
+
+ <section id="rf-tuner-controls">
+ <title>RF Tuner Control Reference</title>
+
+ <para>
+The RF Tuner (RF_TUNER) class includes controls for common features of devices
+having RF tuner.
+ </para>
+ <para>
+In this context, RF tuner is radio receiver circuit between antenna and
+demodulator. It receives radio frequency (RF) from the antenna and converts that
+received signal to lower intermediate frequency (IF) or baseband frequency (BB).
+Tuners that could do baseband output are often called Zero-IF tuners. Older
+tuners were typically simple PLL tuners inside a metal box, whilst newer ones
+are highly integrated chips without a metal box "silicon tuners". These controls
+are mostly applicable for new feature rich silicon tuners, just because older
+tuners does not have much adjustable features.
+ </para>
+ <para>
+For more information about RF tuners see
+<ulink url="http://en.wikipedia.org/wiki/Tuner_%28radio%29">Tuner (radio)</ulink>
+and
+<ulink url="http://en.wikipedia.org/wiki/RF_front_end">RF front end</ulink>
+from Wikipedia.
+ </para>
+
+ <table pgwide="1" frame="none" id="rf-tuner-control-id">
+ <title>RF_TUNER Control IDs</title>
+
+ <tgroup cols="4">
+ <colspec colname="c1" colwidth="1*" />
+ <colspec colname="c2" colwidth="6*" />
+ <colspec colname="c3" colwidth="2*" />
+ <colspec colname="c4" colwidth="6*" />
+ <spanspec namest="c1" nameend="c2" spanname="id" />
+ <spanspec namest="c2" nameend="c4" spanname="descr" />
+ <thead>
+ <row>
+ <entry spanname="id" align="left">ID</entry>
+ <entry align="left">Type</entry>
+ </row>
+ <row rowsep="1">
+ <entry spanname="descr" align="left">Description</entry>
+ </row>
+ </thead>
+ <tbody valign="top">
+ <row><entry></entry></row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RF_TUNER_CLASS</constant>&nbsp;</entry>
+ <entry>class</entry>
+ </row><row><entry spanname="descr">The RF_TUNER class
+descriptor. Calling &VIDIOC-QUERYCTRL; for this control will return a
+description of this control class.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RF_TUNER_BANDWIDTH_AUTO</constant>&nbsp;</entry>
+ <entry>boolean</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Enables/disables tuner radio channel
+bandwidth configuration. In automatic mode bandwidth configuration is performed
+by the driver.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RF_TUNER_BANDWIDTH</constant>&nbsp;</entry>
+ <entry>integer</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Filter(s) on tuner signal path are used to
+filter signal according to receiving party needs. Driver configures filters to
+fulfill desired bandwidth requirement. Used when V4L2_CID_RF_TUNER_BANDWIDTH_AUTO is not
+set. Unit is in Hz. The range and step are driver-specific.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RF_TUNER_LNA_GAIN_AUTO</constant>&nbsp;</entry>
+ <entry>boolean</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Enables/disables LNA automatic gain control (AGC)</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO</constant>&nbsp;</entry>
+ <entry>boolean</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Enables/disables mixer automatic gain control (AGC)</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RF_TUNER_IF_GAIN_AUTO</constant>&nbsp;</entry>
+ <entry>boolean</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Enables/disables IF automatic gain control (AGC)</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RF_TUNER_LNA_GAIN</constant>&nbsp;</entry>
+ <entry>integer</entry>
+ </row>
+ <row>
+ <entry spanname="descr">LNA (low noise amplifier) gain is first
+gain stage on the RF tuner signal path. It is located very close to tuner
+antenna input. Used when <constant>V4L2_CID_RF_TUNER_LNA_GAIN_AUTO</constant> is not set.
+The range and step are driver-specific.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RF_TUNER_MIXER_GAIN</constant>&nbsp;</entry>
+ <entry>integer</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Mixer gain is second gain stage on the RF
+tuner signal path. It is located inside mixer block, where RF signal is
+down-converted by the mixer. Used when <constant>V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO</constant>
+is not set. The range and step are driver-specific.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RF_TUNER_IF_GAIN</constant>&nbsp;</entry>
+ <entry>integer</entry>
+ </row>
+ <row>
+ <entry spanname="descr">IF gain is last gain stage on the RF tuner
+signal path. It is located on output of RF tuner. It controls signal level of
+intermediate frequency output or baseband output. Used when
+<constant>V4L2_CID_RF_TUNER_IF_GAIN_AUTO</constant> is not set. The range and step are
+driver-specific.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RF_TUNER_PLL_LOCK</constant>&nbsp;</entry>
+ <entry>boolean</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Is synthesizer PLL locked? RF tuner is
+receiving given frequency when that control is set. This is a read-only control.
+</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </section>
</section>
diff --git a/Documentation/DocBook/media/v4l/dev-osd.xml b/Documentation/DocBook/media/v4l/dev-osd.xml
index dd91d6134e8c..54853329140b 100644
--- a/Documentation/DocBook/media/v4l/dev-osd.xml
+++ b/Documentation/DocBook/media/v4l/dev-osd.xml
@@ -56,18 +56,18 @@ framebuffer device.</para>
unsigned int i;
int fb_fd;
-if (-1 == ioctl (fd, VIDIOC_G_FBUF, &amp;fbuf)) {
- perror ("VIDIOC_G_FBUF");
- exit (EXIT_FAILURE);
+if (-1 == ioctl(fd, VIDIOC_G_FBUF, &amp;fbuf)) {
+ perror("VIDIOC_G_FBUF");
+ exit(EXIT_FAILURE);
}
-for (i = 0; i &gt; 30; ++i) {
+for (i = 0; i &lt; 30; i++) {
char dev_name[16];
struct fb_fix_screeninfo si;
- snprintf (dev_name, sizeof (dev_name), "/dev/fb%u", i);
+ snprintf(dev_name, sizeof(dev_name), "/dev/fb%u", i);
- fb_fd = open (dev_name, O_RDWR);
+ fb_fd = open(dev_name, O_RDWR);
if (-1 == fb_fd) {
switch (errno) {
case ENOENT: /* no such file */
@@ -75,19 +75,19 @@ for (i = 0; i &gt; 30; ++i) {
continue;
default:
- perror ("open");
- exit (EXIT_FAILURE);
+ perror("open");
+ exit(EXIT_FAILURE);
}
}
- if (0 == ioctl (fb_fd, FBIOGET_FSCREENINFO, &amp;si)) {
- if (si.smem_start == (unsigned long) fbuf.base)
+ if (0 == ioctl(fb_fd, FBIOGET_FSCREENINFO, &amp;si)) {
+ if (si.smem_start == (unsigned long)fbuf.base)
break;
} else {
/* Apparently not a framebuffer device. */
}
- close (fb_fd);
+ close(fb_fd);
fb_fd = -1;
}
diff --git a/Documentation/DocBook/media/v4l/dev-sdr.xml b/Documentation/DocBook/media/v4l/dev-sdr.xml
new file mode 100644
index 000000000000..dc14804f5436
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/dev-sdr.xml
@@ -0,0 +1,110 @@
+ <title>Software Defined Radio Interface (SDR)</title>
+
+ <note>
+ <title>Experimental</title>
+ <para>This is an <link linkend="experimental"> experimental </link>
+ interface and may change in the future.</para>
+ </note>
+
+ <para>
+SDR is an abbreviation of Software Defined Radio, the radio device
+which uses application software for modulation or demodulation. This interface
+is intended for controlling and data streaming of such devices.
+ </para>
+
+ <para>
+SDR devices are accessed through character device special files named
+<filename>/dev/swradio0</filename> to <filename>/dev/swradio255</filename>
+with major number 81 and dynamically allocated minor numbers 0 to 255.
+ </para>
+
+ <section>
+ <title>Querying Capabilities</title>
+
+ <para>
+Devices supporting the SDR receiver interface set the
+<constant>V4L2_CAP_SDR_CAPTURE</constant> and
+<constant>V4L2_CAP_TUNER</constant> flag in the
+<structfield>capabilities</structfield> field of &v4l2-capability;
+returned by the &VIDIOC-QUERYCAP; ioctl. That flag means the device has an
+Analog to Digital Converter (ADC), which is a mandatory element for the SDR receiver.
+At least one of the read/write, streaming or asynchronous I/O methods must
+be supported.
+ </para>
+ </section>
+
+ <section>
+ <title>Supplemental Functions</title>
+
+ <para>
+SDR devices can support <link linkend="control">controls</link>, and must
+support the <link linkend="tuner">tuner</link> ioctls. Tuner ioctls are used
+for setting the ADC sampling rate (sampling frequency) and the possible RF tuner
+frequency.
+ </para>
+
+ <para>
+The <constant>V4L2_TUNER_ADC</constant> tuner type is used for ADC tuners, and
+the <constant>V4L2_TUNER_RF</constant> tuner type is used for RF tuners. The
+tuner index of the RF tuner (if any) must always follow the ADC tuner index.
+Normally the ADC tuner is #0 and the RF tuner is #1.
+ </para>
+
+ <para>
+The &VIDIOC-S-HW-FREQ-SEEK; ioctl is not supported.
+ </para>
+ </section>
+
+ <section>
+ <title>Data Format Negotiation</title>
+
+ <para>
+The SDR capture device uses the <link linkend="format">format</link> ioctls to
+select the capture format. Both the sampling resolution and the data streaming
+format are bound to that selectable format. In addition to the basic
+<link linkend="format">format</link> ioctls, the &VIDIOC-ENUM-FMT; ioctl
+must be supported as well.
+ </para>
+
+ <para>
+To use the <link linkend="format">format</link> ioctls applications set the
+<structfield>type</structfield> field of a &v4l2-format; to
+<constant>V4L2_BUF_TYPE_SDR_CAPTURE</constant> and use the &v4l2-sdr-format;
+<structfield>sdr</structfield> member of the <structfield>fmt</structfield>
+union as needed per the desired operation.
+Currently only the <structfield>pixelformat</structfield> field of
+&v4l2-sdr-format; is used. The content of that field is the V4L2 fourcc code
+of the data format.
+ </para>
+
+ <table pgwide="1" frame="none" id="v4l2-sdr-format">
+ <title>struct <structname>v4l2_sdr_format</structname></title>
+ <tgroup cols="3">
+ &cs-str;
+ <tbody valign="top">
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>pixelformat</structfield></entry>
+ <entry>
+The data format or type of compression, set by the application. This is a
+little endian <link linkend="v4l2-fourcc">four character code</link>.
+V4L2 defines SDR formats in <xref linkend="sdr-formats" />.
+ </entry>
+ </row>
+ <row>
+ <entry>__u8</entry>
+ <entry><structfield>reserved[28]</structfield></entry>
+ <entry>This array is reserved for future extensions.
+Drivers and applications must set it to zero.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+An SDR device may support <link linkend="rw">read/write</link>
+and/or streaming (<link linkend="mmap">memory mapping</link>
+or <link linkend="userp">user pointer</link>) I/O.
+ </para>
+
+ </section>
diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml
index 2c4c068dde83..97a69bf6f3eb 100644
--- a/Documentation/DocBook/media/v4l/io.xml
+++ b/Documentation/DocBook/media/v4l/io.xml
@@ -339,8 +339,8 @@ returns immediately with an &EAGAIN; when no buffer is available. The
queues as a side effect. Since there is no notion of doing anything
"now" on a multitasking system, if an application needs to synchronize
with another event it should examine the &v4l2-buffer;
-<structfield>timestamp</structfield> of captured buffers, or set the
-field before enqueuing buffers for output.</para>
+<structfield>timestamp</structfield> of captured or outputted buffers.
+</para>
<para>Drivers implementing memory mapping I/O must
support the <constant>VIDIOC_REQBUFS</constant>,
@@ -457,7 +457,7 @@ queues and unlocks all buffers as a side effect. Since there is no
notion of doing anything "now" on a multitasking system, if an
application needs to synchronize with another event it should examine
the &v4l2-buffer; <structfield>timestamp</structfield> of captured
-buffers, or set the field before enqueuing buffers for output.</para>
+or outputted buffers.</para>
<para>Drivers implementing user pointer I/O must
support the <constant>VIDIOC_REQBUFS</constant>,
@@ -620,8 +620,7 @@ returns immediately with an &EAGAIN; when no buffer is available. The
unlocks all buffers as a side effect. Since there is no notion of doing
anything "now" on a multitasking system, if an application needs to synchronize
with another event it should examine the &v4l2-buffer;
-<structfield>timestamp</structfield> of captured buffers, or set the field
-before enqueuing buffers for output.</para>
+<structfield>timestamp</structfield> of captured or outputted buffers.</para>
<para>Drivers implementing DMABUF importing I/O must support the
<constant>VIDIOC_REQBUFS</constant>, <constant>VIDIOC_QBUF</constant>,
@@ -654,38 +653,19 @@ plane, are stored in struct <structname>v4l2_plane</structname> instead.
In that case, struct <structname>v4l2_buffer</structname> contains an array of
plane structures.</para>
- <para>Nominally timestamps refer to the first data byte transmitted.
-In practice however the wide range of hardware covered by the V4L2 API
-limits timestamp accuracy. Often an interrupt routine will
-sample the system clock shortly after the field or frame was stored
-completely in memory. So applications must expect a constant
-difference up to one field or frame period plus a small (few scan
-lines) random error. The delay and error can be much
-larger due to compression or transmission over an external bus when
-the frames are not properly stamped by the sender. This is frequently
-the case with USB cameras. Here timestamps refer to the instant the
-field or frame was received by the driver, not the capture time. These
-devices identify by not enumerating any video standards, see <xref
-linkend="standard" />.</para>
-
- <para>Similar limitations apply to output timestamps. Typically
-the video hardware locks to a clock controlling the video timing, the
-horizontal and vertical synchronization pulses. At some point in the
-line sequence, possibly the vertical blanking, an interrupt routine
-samples the system clock, compares against the timestamp and programs
-the hardware to repeat the previous field or frame, or to display the
-buffer contents.</para>
-
- <para>Apart of limitations of the video device and natural
-inaccuracies of all clocks, it should be noted system time itself is
-not perfectly stable. It can be affected by power saving cycles,
-warped to insert leap seconds, or even turned back or forth by the
-system administrator affecting long term measurements. <footnote>
- <para>Since no other Linux multimedia
-API supports unadjusted time it would be foolish to introduce here. We
-must use a universally supported clock to synchronize different media,
-hence time of day.</para>
- </footnote></para>
+ <para>Dequeued video buffers come with timestamps. The driver
+ decides at which part of the frame and with which clock the
+ timestamp is taken. Please see flags in the masks
+ <constant>V4L2_BUF_FLAG_TIMESTAMP_MASK</constant> and
+ <constant>V4L2_BUF_FLAG_TSTAMP_SRC_MASK</constant> in <xref
+ linkend="buffer-flags" />. These flags are always valid and constant
+ across all buffers during the whole video stream. Changes in these
+ flags may take place as a side effect of &VIDIOC-S-INPUT; or
+ &VIDIOC-S-OUTPUT; however. The
+ <constant>V4L2_BUF_FLAG_TIMESTAMP_COPY</constant> timestamp type
+ which is used by e.g. on mem-to-mem devices is an exception to the
+ rule: the timestamp source flags are copied from the OUTPUT video
+ buffer to the CAPTURE video buffer.</para>
<table frame="none" pgwide="1" id="v4l2-buffer">
<title>struct <structname>v4l2_buffer</structname></title>
@@ -696,10 +676,11 @@ hence time of day.</para>
<entry>__u32</entry>
<entry><structfield>index</structfield></entry>
<entry></entry>
- <entry>Number of the buffer, set by the application. This
-field is only used for <link linkend="mmap">memory mapping</link> I/O
-and can range from zero to the number of buffers allocated
-with the &VIDIOC-REQBUFS; ioctl (&v4l2-requestbuffers; <structfield>count</structfield>) minus one.</entry>
+ <entry>Number of the buffer, set by the application except
+when calling &VIDIOC-DQBUF;, then it is set by the driver.
+This field can range from zero to the number of buffers allocated
+with the &VIDIOC-REQBUFS; ioctl (&v4l2-requestbuffers; <structfield>count</structfield>),
+plus any buffers allocated with &VIDIOC-CREATE-BUFS; minus one.</entry>
</row>
<row>
<entry>__u32</entry>
@@ -718,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 an output stream.</entry>
+refers to an input stream, applications when it refers to an output stream.</entry>
</row>
<row>
<entry>__u32</entry>
@@ -735,7 +716,7 @@ linkend="buffer-flags" />.</entry>
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,
-applications when an output stream.</entry>
+applications when it refers to an output stream.</entry>
</row>
<row>
<entry>struct timeval</entry>
@@ -745,15 +726,13 @@ applications when an output stream.</entry>
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
- <xref linkend="buffer-flags" />. For output streams the data
- will not be displayed before this time, secondary to the nominal
- frame rate determined by the current video standard in enqueued
- order. Applications can for example zero this field to display
- frames as soon as possible. The driver stores the time at which
- the first data byte was actually sent out in the
- <structfield>timestamp</structfield> field. This permits
+ <xref linkend="buffer-flags" />. For output streams the driver
+ stores the time at which the last data byte was actually sent out
+ in the <structfield>timestamp</structfield> field. This permits
applications to monitor the drift between the video and system
- clock.</para></entry>
+ clock. For output streams that use <constant>V4L2_BUF_FLAG_TIMESTAMP_COPY</constant>
+ the application has to fill in the timestamp which will be copied
+ by the driver to the capture stream.</para></entry>
</row>
<row>
<entry>&v4l2-timecode;</entry>
@@ -846,7 +825,8 @@ is the file descriptor associated with a DMABUF buffer.</entry>
<entry><structfield>length</structfield></entry>
<entry></entry>
<entry>Size of the buffer (not the payload) in bytes for the
- single-planar API. For the multi-planar API the application sets
+ single-planar API. This is set by the driver based on the calls to
+ &VIDIOC-REQBUFS; and/or &VIDIOC-CREATE-BUFS;. For the multi-planar API the application sets
this to the number of elements in the <structfield>planes</structfield>
array. The driver will fill in the actual number of valid elements in
that array.
@@ -880,13 +860,15 @@ should set this to 0.</entry>
<entry><structfield>bytesused</structfield></entry>
<entry></entry>
<entry>The number of bytes occupied by data in the plane
- (its payload).</entry>
+ (its payload). Drivers must set this field when <structfield>type</structfield>
+ refers to an input stream, applications when it refers to an output stream.</entry>
</row>
<row>
<entry>__u32</entry>
<entry><structfield>length</structfield></entry>
<entry></entry>
- <entry>Size in bytes of the plane (not its payload).</entry>
+ <entry>Size in bytes of the plane (not its payload). This is set by the driver
+ based on the calls to &VIDIOC-REQBUFS; and/or &VIDIOC-CREATE-BUFS;.</entry>
</row>
<row>
<entry>union</entry>
@@ -925,7 +907,9 @@ should set this to 0.</entry>
<entry>__u32</entry>
<entry><structfield>data_offset</structfield></entry>
<entry></entry>
- <entry>Offset in bytes to video data in the plane, if applicable.
+ <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.
</entry>
</row>
<row>
@@ -1005,6 +989,12 @@ should set this to 0.</entry>
<entry>Buffer for video output overlay (OSD), see <xref
linkend="osd" />.</entry>
</row>
+ <row>
+ <entry><constant>V4L2_BUF_TYPE_SDR_CAPTURE</constant></entry>
+ <entry>11</entry>
+ <entry>Buffer for Software Defined Radio (SDR), see <xref
+ linkend="sdr" />.</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -1016,7 +1006,7 @@ should set this to 0.</entry>
<tbody valign="top">
<row>
<entry><constant>V4L2_BUF_FLAG_MAPPED</constant></entry>
- <entry>0x0001</entry>
+ <entry>0x00000001</entry>
<entry>The buffer resides in device memory and has been mapped
into the application's address space, see <xref linkend="mmap" /> for details.
Drivers set or clear this flag when the
@@ -1026,7 +1016,7 @@ Drivers set or clear this flag when the
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_QUEUED</constant></entry>
- <entry>0x0002</entry>
+ <entry>0x00000002</entry>
<entry>Internally drivers maintain two buffer queues, an
incoming and outgoing queue. When this flag is set, the buffer is
currently on the incoming queue. It automatically moves to the
@@ -1039,7 +1029,7 @@ cleared.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_DONE</constant></entry>
- <entry>0x0004</entry>
+ <entry>0x00000004</entry>
<entry>When this flag is set, the buffer is currently on
the outgoing queue, ready to be dequeued from the driver. Drivers set
or clear this flag when the <constant>VIDIOC_QUERYBUF</constant> ioctl
@@ -1049,11 +1039,11 @@ buffer cannot be on both queues at the same time, the
<constant>V4L2_BUF_FLAG_QUEUED</constant> and
<constant>V4L2_BUF_FLAG_DONE</constant> flag are mutually exclusive.
They can be both cleared however, then the buffer is in "dequeued"
-state, in the application domain to say so.</entry>
+state, in the application domain so to say.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_ERROR</constant></entry>
- <entry>0x0040</entry>
+ <entry>0x00000040</entry>
<entry>When this flag is set, the buffer has been dequeued
successfully, although the data might have been corrupted.
This is recoverable, streaming may continue as normal and
@@ -1063,35 +1053,43 @@ state, in the application domain to say so.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_KEYFRAME</constant></entry>
- <entry>0x0008</entry>
+ <entry>0x00000008</entry>
<entry>Drivers set or clear this flag when calling the
<constant>VIDIOC_DQBUF</constant> ioctl. It may be set by video
capture devices when the buffer contains a compressed image which is a
-key frame (or field), &ie; can be decompressed on its own.</entry>
+key frame (or field), &ie; can be decompressed on its own. Also know as
+an I-frame. Applications can set this bit when <structfield>type</structfield>
+refers to an output stream.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_PFRAME</constant></entry>
- <entry>0x0010</entry>
+ <entry>0x00000010</entry>
<entry>Similar to <constant>V4L2_BUF_FLAG_KEYFRAME</constant>
this flags predicted frames or fields which contain only differences to a
-previous key frame.</entry>
+previous key frame. Applications can set this bit when <structfield>type</structfield>
+refers to an output stream.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_BFRAME</constant></entry>
- <entry>0x0020</entry>
- <entry>Similar to <constant>V4L2_BUF_FLAG_PFRAME</constant>
- this is a bidirectional predicted frame or field. [ooc tbd]</entry>
+ <entry>0x00000020</entry>
+ <entry>Similar to <constant>V4L2_BUF_FLAG_KEYFRAME</constant>
+this flags a bi-directional predicted frame or field which contains only
+the differences between the current frame and both the preceding and following
+key frames to specify its content. Applications can set this bit when
+<structfield>type</structfield> refers to an output stream.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_TIMECODE</constant></entry>
- <entry>0x0100</entry>
+ <entry>0x00000100</entry>
<entry>The <structfield>timecode</structfield> field is valid.
Drivers set or clear this flag when the <constant>VIDIOC_DQBUF</constant>
-ioctl is called.</entry>
+ioctl is called. Applications can set this bit and the corresponding
+<structfield>timecode</structfield> structure when <structfield>type</structfield>
+refers to an output stream.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_PREPARED</constant></entry>
- <entry>0x0400</entry>
+ <entry>0x00000400</entry>
<entry>The buffer has been prepared for I/O and can be queued by the
application. Drivers set or clear this flag when the
<link linkend="vidioc-querybuf">VIDIOC_QUERYBUF</link>, <link
@@ -1101,7 +1099,7 @@ application. Drivers set or clear this flag when the
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_NO_CACHE_INVALIDATE</constant></entry>
- <entry>0x0800</entry>
+ <entry>0x00000800</entry>
<entry>Caches do not have to be invalidated for this buffer.
Typically applications shall use this flag if the data captured in the buffer
is not going to be touched by the CPU, instead the buffer will, probably, be
@@ -1110,7 +1108,7 @@ passed on to a DMA-capable hardware unit for further processing or output.
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_NO_CACHE_CLEAN</constant></entry>
- <entry>0x1000</entry>
+ <entry>0x00001000</entry>
<entry>Caches do not have to be cleaned for this buffer.
Typically applications shall use this flag for output buffers if the data
in this buffer has not been created by the CPU but by some DMA-capable unit,
@@ -1118,7 +1116,7 @@ in which case caches have not been used.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_TIMESTAMP_MASK</constant></entry>
- <entry>0xe000</entry>
+ <entry>0x0000e000</entry>
<entry>Mask for timestamp types below. To test the
timestamp type, mask out bits not belonging to timestamp
type by performing a logical and operation with buffer
@@ -1126,7 +1124,7 @@ in which case caches have not been used.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN</constant></entry>
- <entry>0x0000</entry>
+ <entry>0x00000000</entry>
<entry>Unknown timestamp type. This type is used by
drivers before Linux 3.9 and may be either monotonic (see
below) or realtime (wall clock). Monotonic clock has been
@@ -1139,7 +1137,7 @@ in which case caches have not been used.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC</constant></entry>
- <entry>0x2000</entry>
+ <entry>0x00002000</entry>
<entry>The buffer timestamp has been taken from the
<constant>CLOCK_MONOTONIC</constant> clock. To access the
same clock outside V4L2, use
@@ -1147,10 +1145,42 @@ in which case caches have not been used.</entry>
</row>
<row>
<entry><constant>V4L2_BUF_FLAG_TIMESTAMP_COPY</constant></entry>
- <entry>0x4000</entry>
+ <entry>0x00004000</entry>
<entry>The CAPTURE buffer timestamp has been taken from the
corresponding OUTPUT buffer. This flag applies only to mem2mem devices.</entry>
</row>
+ <row>
+ <entry><constant>V4L2_BUF_FLAG_TSTAMP_SRC_MASK</constant></entry>
+ <entry>0x00070000</entry>
+ <entry>Mask for timestamp sources below. The timestamp source
+ defines the point of time the timestamp is taken in relation to
+ the frame. Logical 'and' operation between the
+ <structfield>flags</structfield> field and
+ <constant>V4L2_BUF_FLAG_TSTAMP_SRC_MASK</constant> produces the
+ value of the timestamp source. Applications must set the timestamp
+ source when <structfield>type</structfield> refers to an output stream
+ and <constant>V4L2_BUF_FLAG_TIMESTAMP_COPY</constant> is set.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_BUF_FLAG_TSTAMP_SRC_EOF</constant></entry>
+ <entry>0x00000000</entry>
+ <entry>End Of Frame. The buffer timestamp has been taken
+ when the last pixel of the frame has been received or the
+ last pixel of the frame has been transmitted. In practice,
+ software generated timestamps will typically be read from
+ the clock a small amount of time after the last pixel has
+ been received or transmitten, depending on the system and
+ other activity in it.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_BUF_FLAG_TSTAMP_SRC_SOE</constant></entry>
+ <entry>0x00010000</entry>
+ <entry>Start Of Exposure. The buffer timestamp has been
+ taken when the exposure of the frame has begun. This is
+ only valid for the
+ <constant>V4L2_BUF_TYPE_VIDEO_CAPTURE</constant> buffer
+ type.</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -1440,10 +1470,9 @@ or application, depending on data direction, must set &v4l2-buffer;
<constant>V4L2_FIELD_BOTTOM</constant>. Any two successive fields pair
to build a frame. If fields are successive, without any dropped fields
between them (fields can drop individually), can be determined from
-the &v4l2-buffer; <structfield>sequence</structfield> field. Image
-sizes refer to the frame, not fields. This format cannot be selected
-when using the read/write I/O method.<!-- Where it's indistinguishable
-from V4L2_FIELD_SEQ_*. --></entry>
+the &v4l2-buffer; <structfield>sequence</structfield> field. This format
+cannot be selected when using the read/write I/O method since there
+is no way to communicate if a field was a top or bottom field.</entry>
</row>
<row>
<entry><constant>V4L2_FIELD_INTERLACED_TB</constant></entry>
diff --git a/Documentation/DocBook/media/v4l/pixfmt-nv16m.xml b/Documentation/DocBook/media/v4l/pixfmt-nv16m.xml
index c51d5a4cda09..fb2b5e35d665 100644
--- a/Documentation/DocBook/media/v4l/pixfmt-nv16m.xml
+++ b/Documentation/DocBook/media/v4l/pixfmt-nv16m.xml
@@ -12,18 +12,17 @@
<refsect1>
<title>Description</title>
- <para>This is a multi-planar, two-plane version of the YUV 4:2:0 format.
+ <para>This is a multi-planar, two-plane version of the YUV 4:2:2 format.
The three components are separated into two sub-images or planes.
<constant>V4L2_PIX_FMT_NV16M</constant> differs from <constant>V4L2_PIX_FMT_NV16
</constant> in that the two planes are non-contiguous in memory, i.e. the chroma
-plane does not necessarily immediately follows the luma plane.
+plane does not necessarily immediately follow the luma plane.
The luminance data occupies the first plane. The Y plane has one byte per pixel.
In the second plane there is chrominance data with alternating chroma samples.
The CbCr plane is the same width and height, in bytes, as the Y plane.
-Each CbCr pair belongs to four pixels. For example,
+Each CbCr pair belongs to two pixels. For example,
Cb<subscript>0</subscript>/Cr<subscript>0</subscript> belongs to
-Y'<subscript>00</subscript>, Y'<subscript>01</subscript>,
-Y'<subscript>10</subscript>, Y'<subscript>11</subscript>.
+Y'<subscript>00</subscript>, Y'<subscript>01</subscript>.
<constant>V4L2_PIX_FMT_NV61M</constant> is the same as <constant>V4L2_PIX_FMT_NV16M</constant>
except the Cb and Cr bytes are swapped, the CrCb plane starts with a Cr byte.</para>
diff --git a/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml b/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml
index 166c8d65e4f7..e1c4f8b4c0b3 100644
--- a/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml
+++ b/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml
@@ -121,14 +121,14 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para>
<entry><constant>V4L2_PIX_FMT_RGB332</constant></entry>
<entry>'RGB1'</entry>
<entry></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
<entry>r<subscript>2</subscript></entry>
<entry>r<subscript>1</subscript></entry>
<entry>r<subscript>0</subscript></entry>
+ <entry>g<subscript>2</subscript></entry>
+ <entry>g<subscript>1</subscript></entry>
+ <entry>g<subscript>0</subscript></entry>
+ <entry>b<subscript>1</subscript></entry>
+ <entry>b<subscript>0</subscript></entry>
</row>
<row id="V4L2-PIX-FMT-RGB444">
<entry><constant>V4L2_PIX_FMT_RGB444</constant></entry>
@@ -159,18 +159,18 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para>
<entry>g<subscript>2</subscript></entry>
<entry>g<subscript>1</subscript></entry>
<entry>g<subscript>0</subscript></entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- <entry></entry>
- <entry>a</entry>
<entry>b<subscript>4</subscript></entry>
<entry>b<subscript>3</subscript></entry>
<entry>b<subscript>2</subscript></entry>
<entry>b<subscript>1</subscript></entry>
<entry>b<subscript>0</subscript></entry>
+ <entry></entry>
+ <entry>a</entry>
+ <entry>r<subscript>4</subscript></entry>
+ <entry>r<subscript>3</subscript></entry>
+ <entry>r<subscript>2</subscript></entry>
+ <entry>r<subscript>1</subscript></entry>
+ <entry>r<subscript>0</subscript></entry>
<entry>g<subscript>4</subscript></entry>
<entry>g<subscript>3</subscript></entry>
</row>
@@ -181,17 +181,17 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para>
<entry>g<subscript>2</subscript></entry>
<entry>g<subscript>1</subscript></entry>
<entry>g<subscript>0</subscript></entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- <entry></entry>
<entry>b<subscript>4</subscript></entry>
<entry>b<subscript>3</subscript></entry>
<entry>b<subscript>2</subscript></entry>
<entry>b<subscript>1</subscript></entry>
<entry>b<subscript>0</subscript></entry>
+ <entry></entry>
+ <entry>r<subscript>4</subscript></entry>
+ <entry>r<subscript>3</subscript></entry>
+ <entry>r<subscript>2</subscript></entry>
+ <entry>r<subscript>1</subscript></entry>
+ <entry>r<subscript>0</subscript></entry>
<entry>g<subscript>5</subscript></entry>
<entry>g<subscript>4</subscript></entry>
<entry>g<subscript>3</subscript></entry>
@@ -201,32 +201,32 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para>
<entry>'RGBQ'</entry>
<entry></entry>
<entry>a</entry>
- <entry>b<subscript>4</subscript></entry>
- <entry>b<subscript>3</subscript></entry>
- <entry>b<subscript>2</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- <entry>g<subscript>4</subscript></entry>
- <entry>g<subscript>3</subscript></entry>
- <entry></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
<entry>r<subscript>4</subscript></entry>
<entry>r<subscript>3</subscript></entry>
<entry>r<subscript>2</subscript></entry>
<entry>r<subscript>1</subscript></entry>
<entry>r<subscript>0</subscript></entry>
- </row>
- <row id="V4L2-PIX-FMT-RGB565X">
- <entry><constant>V4L2_PIX_FMT_RGB565X</constant></entry>
- <entry>'RGBR'</entry>
+ <entry>g<subscript>4</subscript></entry>
+ <entry>g<subscript>3</subscript></entry>
<entry></entry>
+ <entry>g<subscript>2</subscript></entry>
+ <entry>g<subscript>1</subscript></entry>
+ <entry>g<subscript>0</subscript></entry>
<entry>b<subscript>4</subscript></entry>
<entry>b<subscript>3</subscript></entry>
<entry>b<subscript>2</subscript></entry>
<entry>b<subscript>1</subscript></entry>
<entry>b<subscript>0</subscript></entry>
+ </row>
+ <row id="V4L2-PIX-FMT-RGB565X">
+ <entry><constant>V4L2_PIX_FMT_RGB565X</constant></entry>
+ <entry>'RGBR'</entry>
+ <entry></entry>
+ <entry>r<subscript>4</subscript></entry>
+ <entry>r<subscript>3</subscript></entry>
+ <entry>r<subscript>2</subscript></entry>
+ <entry>r<subscript>1</subscript></entry>
+ <entry>r<subscript>0</subscript></entry>
<entry>g<subscript>5</subscript></entry>
<entry>g<subscript>4</subscript></entry>
<entry>g<subscript>3</subscript></entry>
@@ -234,11 +234,11 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para>
<entry>g<subscript>2</subscript></entry>
<entry>g<subscript>1</subscript></entry>
<entry>g<subscript>0</subscript></entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
+ <entry>b<subscript>4</subscript></entry>
+ <entry>b<subscript>3</subscript></entry>
+ <entry>b<subscript>2</subscript></entry>
+ <entry>b<subscript>1</subscript></entry>
+ <entry>b<subscript>0</subscript></entry>
</row>
<row id="V4L2-PIX-FMT-BGR666">
<entry><constant>V4L2_PIX_FMT_BGR666</constant></entry>
@@ -385,6 +385,15 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para>
<entry><constant>V4L2_PIX_FMT_RGB32</constant></entry>
<entry>'RGB4'</entry>
<entry></entry>
+ <entry>a<subscript>7</subscript></entry>
+ <entry>a<subscript>6</subscript></entry>
+ <entry>a<subscript>5</subscript></entry>
+ <entry>a<subscript>4</subscript></entry>
+ <entry>a<subscript>3</subscript></entry>
+ <entry>a<subscript>2</subscript></entry>
+ <entry>a<subscript>1</subscript></entry>
+ <entry>a<subscript>0</subscript></entry>
+ <entry></entry>
<entry>r<subscript>7</subscript></entry>
<entry>r<subscript>6</subscript></entry>
<entry>r<subscript>5</subscript></entry>
@@ -411,25 +420,16 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para>
<entry>b<subscript>2</subscript></entry>
<entry>b<subscript>1</subscript></entry>
<entry>b<subscript>0</subscript></entry>
- <entry></entry>
- <entry>a<subscript>7</subscript></entry>
- <entry>a<subscript>6</subscript></entry>
- <entry>a<subscript>5</subscript></entry>
- <entry>a<subscript>4</subscript></entry>
- <entry>a<subscript>3</subscript></entry>
- <entry>a<subscript>2</subscript></entry>
- <entry>a<subscript>1</subscript></entry>
- <entry>a<subscript>0</subscript></entry>
</row>
</tbody>
</tgroup>
</table>
- <para>Bit 7 is the most significant bit. The value of a = alpha
+ <para>Bit 7 is the most significant bit. The value of the a = alpha
bits is undefined when reading from the driver, ignored when writing
to the driver, except when alpha blending has been negotiated for a
<link linkend="overlay">Video Overlay</link> or <link linkend="osd">
-Video Output Overlay</link> or when alpha component has been configured
+Video Output Overlay</link> or when the alpha component has been configured
for a <link linkend="capture">Video Capture</link> by means of <link
linkend="v4l2-alpha-component"> <constant>V4L2_CID_ALPHA_COMPONENT
</constant> </link> control.</para>
@@ -512,421 +512,6 @@ image</title>
</formalpara>
</example>
- <important>
- <para>Drivers may interpret these formats differently.</para>
- </important>
-
- <para>Some RGB formats above are uncommon and were probably
-defined in error. Drivers may interpret them as in <xref
- linkend="rgb-formats-corrected" />.</para>
-
- <table pgwide="1" frame="none" id="rgb-formats-corrected">
- <title>Packed RGB Image Formats (corrected)</title>
- <tgroup cols="37" align="center">
- <colspec colname="id" align="left" />
- <colspec colname="fourcc" />
- <colspec colname="bit" />
-
- <colspec colnum="4" colname="b07" align="center" />
- <colspec colnum="5" colname="b06" align="center" />
- <colspec colnum="6" colname="b05" align="center" />
- <colspec colnum="7" colname="b04" align="center" />
- <colspec colnum="8" colname="b03" align="center" />
- <colspec colnum="9" colname="b02" align="center" />
- <colspec colnum="10" colname="b01" align="center" />
- <colspec colnum="11" colname="b00" align="center" />
-
- <colspec colnum="13" colname="b17" align="center" />
- <colspec colnum="14" colname="b16" align="center" />
- <colspec colnum="15" colname="b15" align="center" />
- <colspec colnum="16" colname="b14" align="center" />
- <colspec colnum="17" colname="b13" align="center" />
- <colspec colnum="18" colname="b12" align="center" />
- <colspec colnum="19" colname="b11" align="center" />
- <colspec colnum="20" colname="b10" align="center" />
-
- <colspec colnum="22" colname="b27" align="center" />
- <colspec colnum="23" colname="b26" align="center" />
- <colspec colnum="24" colname="b25" align="center" />
- <colspec colnum="25" colname="b24" align="center" />
- <colspec colnum="26" colname="b23" align="center" />
- <colspec colnum="27" colname="b22" align="center" />
- <colspec colnum="28" colname="b21" align="center" />
- <colspec colnum="29" colname="b20" align="center" />
-
- <colspec colnum="31" colname="b37" align="center" />
- <colspec colnum="32" colname="b36" align="center" />
- <colspec colnum="33" colname="b35" align="center" />
- <colspec colnum="34" colname="b34" align="center" />
- <colspec colnum="35" colname="b33" align="center" />
- <colspec colnum="36" colname="b32" align="center" />
- <colspec colnum="37" colname="b31" align="center" />
- <colspec colnum="38" colname="b30" align="center" />
-
- <spanspec namest="b07" nameend="b00" spanname="b0" />
- <spanspec namest="b17" nameend="b10" spanname="b1" />
- <spanspec namest="b27" nameend="b20" spanname="b2" />
- <spanspec namest="b37" nameend="b30" spanname="b3" />
- <thead>
- <row>
- <entry>Identifier</entry>
- <entry>Code</entry>
- <entry>&nbsp;</entry>
- <entry spanname="b0">Byte&nbsp;0 in memory</entry>
- <entry spanname="b1">Byte&nbsp;1</entry>
- <entry spanname="b2">Byte&nbsp;2</entry>
- <entry spanname="b3">Byte&nbsp;3</entry>
- </row>
- <row>
- <entry>&nbsp;</entry>
- <entry>&nbsp;</entry>
- <entry>Bit</entry>
- <entry>7</entry>
- <entry>6</entry>
- <entry>5</entry>
- <entry>4</entry>
- <entry>3</entry>
- <entry>2</entry>
- <entry>1</entry>
- <entry>0</entry>
- <entry>&nbsp;</entry>
- <entry>7</entry>
- <entry>6</entry>
- <entry>5</entry>
- <entry>4</entry>
- <entry>3</entry>
- <entry>2</entry>
- <entry>1</entry>
- <entry>0</entry>
- <entry>&nbsp;</entry>
- <entry>7</entry>
- <entry>6</entry>
- <entry>5</entry>
- <entry>4</entry>
- <entry>3</entry>
- <entry>2</entry>
- <entry>1</entry>
- <entry>0</entry>
- <entry>&nbsp;</entry>
- <entry>7</entry>
- <entry>6</entry>
- <entry>5</entry>
- <entry>4</entry>
- <entry>3</entry>
- <entry>2</entry>
- <entry>1</entry>
- <entry>0</entry>
- </row>
- </thead>
- <tbody valign="top">
- <row><!-- id="V4L2-PIX-FMT-RGB332" -->
- <entry><constant>V4L2_PIX_FMT_RGB332</constant></entry>
- <entry>'RGB1'</entry>
- <entry></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- </row>
- <row><!-- id="V4L2-PIX-FMT-RGB444" -->
- <entry><constant>V4L2_PIX_FMT_RGB444</constant></entry>
- <entry>'R444'</entry>
- <entry></entry>
- <entry>g<subscript>3</subscript></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
- <entry>b<subscript>3</subscript></entry>
- <entry>b<subscript>2</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- <entry></entry>
- <entry>a<subscript>3</subscript></entry>
- <entry>a<subscript>2</subscript></entry>
- <entry>a<subscript>1</subscript></entry>
- <entry>a<subscript>0</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- </row>
- <row><!-- id="V4L2-PIX-FMT-RGB555" -->
- <entry><constant>V4L2_PIX_FMT_RGB555</constant></entry>
- <entry>'RGBO'</entry>
- <entry></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
- <entry>b<subscript>4</subscript></entry>
- <entry>b<subscript>3</subscript></entry>
- <entry>b<subscript>2</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- <entry></entry>
- <entry>a</entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- <entry>g<subscript>4</subscript></entry>
- <entry>g<subscript>3</subscript></entry>
- </row>
- <row><!-- id="V4L2-PIX-FMT-RGB565" -->
- <entry><constant>V4L2_PIX_FMT_RGB565</constant></entry>
- <entry>'RGBP'</entry>
- <entry></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
- <entry>b<subscript>4</subscript></entry>
- <entry>b<subscript>3</subscript></entry>
- <entry>b<subscript>2</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- <entry></entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- <entry>g<subscript>5</subscript></entry>
- <entry>g<subscript>4</subscript></entry>
- <entry>g<subscript>3</subscript></entry>
- </row>
- <row><!-- id="V4L2-PIX-FMT-RGB555X" -->
- <entry><constant>V4L2_PIX_FMT_RGB555X</constant></entry>
- <entry>'RGBQ'</entry>
- <entry></entry>
- <entry>a</entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- <entry>g<subscript>4</subscript></entry>
- <entry>g<subscript>3</subscript></entry>
- <entry></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
- <entry>b<subscript>4</subscript></entry>
- <entry>b<subscript>3</subscript></entry>
- <entry>b<subscript>2</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- </row>
- <row><!-- id="V4L2-PIX-FMT-RGB565X" -->
- <entry><constant>V4L2_PIX_FMT_RGB565X</constant></entry>
- <entry>'RGBR'</entry>
- <entry></entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- <entry>g<subscript>5</subscript></entry>
- <entry>g<subscript>4</subscript></entry>
- <entry>g<subscript>3</subscript></entry>
- <entry></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
- <entry>b<subscript>4</subscript></entry>
- <entry>b<subscript>3</subscript></entry>
- <entry>b<subscript>2</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- </row>
- <row><!-- id="V4L2-PIX-FMT-BGR666" -->
- <entry><constant>V4L2_PIX_FMT_BGR666</constant></entry>
- <entry>'BGRH'</entry>
- <entry></entry>
- <entry>b<subscript>5</subscript></entry>
- <entry>b<subscript>4</subscript></entry>
- <entry>b<subscript>3</subscript></entry>
- <entry>b<subscript>2</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- <entry>g<subscript>5</subscript></entry>
- <entry>g<subscript>4</subscript></entry>
- <entry></entry>
- <entry>g<subscript>3</subscript></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
- <entry>r<subscript>5</subscript></entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- <entry></entry>
- </row>
- <row><!-- id="V4L2-PIX-FMT-BGR24" -->
- <entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
- <entry>'BGR3'</entry>
- <entry></entry>
- <entry>b<subscript>7</subscript></entry>
- <entry>b<subscript>6</subscript></entry>
- <entry>b<subscript>5</subscript></entry>
- <entry>b<subscript>4</subscript></entry>
- <entry>b<subscript>3</subscript></entry>
- <entry>b<subscript>2</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- <entry></entry>
- <entry>g<subscript>7</subscript></entry>
- <entry>g<subscript>6</subscript></entry>
- <entry>g<subscript>5</subscript></entry>
- <entry>g<subscript>4</subscript></entry>
- <entry>g<subscript>3</subscript></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
- <entry></entry>
- <entry>r<subscript>7</subscript></entry>
- <entry>r<subscript>6</subscript></entry>
- <entry>r<subscript>5</subscript></entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- </row>
- <row><!-- id="V4L2-PIX-FMT-RGB24" -->
- <entry><constant>V4L2_PIX_FMT_RGB24</constant></entry>
- <entry>'RGB3'</entry>
- <entry></entry>
- <entry>r<subscript>7</subscript></entry>
- <entry>r<subscript>6</subscript></entry>
- <entry>r<subscript>5</subscript></entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- <entry></entry>
- <entry>g<subscript>7</subscript></entry>
- <entry>g<subscript>6</subscript></entry>
- <entry>g<subscript>5</subscript></entry>
- <entry>g<subscript>4</subscript></entry>
- <entry>g<subscript>3</subscript></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
- <entry></entry>
- <entry>b<subscript>7</subscript></entry>
- <entry>b<subscript>6</subscript></entry>
- <entry>b<subscript>5</subscript></entry>
- <entry>b<subscript>4</subscript></entry>
- <entry>b<subscript>3</subscript></entry>
- <entry>b<subscript>2</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- </row>
- <row><!-- id="V4L2-PIX-FMT-BGR32" -->
- <entry><constant>V4L2_PIX_FMT_BGR32</constant></entry>
- <entry>'BGR4'</entry>
- <entry></entry>
- <entry>b<subscript>7</subscript></entry>
- <entry>b<subscript>6</subscript></entry>
- <entry>b<subscript>5</subscript></entry>
- <entry>b<subscript>4</subscript></entry>
- <entry>b<subscript>3</subscript></entry>
- <entry>b<subscript>2</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- <entry></entry>
- <entry>g<subscript>7</subscript></entry>
- <entry>g<subscript>6</subscript></entry>
- <entry>g<subscript>5</subscript></entry>
- <entry>g<subscript>4</subscript></entry>
- <entry>g<subscript>3</subscript></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
- <entry></entry>
- <entry>r<subscript>7</subscript></entry>
- <entry>r<subscript>6</subscript></entry>
- <entry>r<subscript>5</subscript></entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- <entry></entry>
- <entry>a<subscript>7</subscript></entry>
- <entry>a<subscript>6</subscript></entry>
- <entry>a<subscript>5</subscript></entry>
- <entry>a<subscript>4</subscript></entry>
- <entry>a<subscript>3</subscript></entry>
- <entry>a<subscript>2</subscript></entry>
- <entry>a<subscript>1</subscript></entry>
- <entry>a<subscript>0</subscript></entry>
- </row>
- <row><!-- id="V4L2-PIX-FMT-RGB32" -->
- <entry><constant>V4L2_PIX_FMT_RGB32</constant></entry>
- <entry>'RGB4'</entry>
- <entry></entry>
- <entry>a<subscript>7</subscript></entry>
- <entry>a<subscript>6</subscript></entry>
- <entry>a<subscript>5</subscript></entry>
- <entry>a<subscript>4</subscript></entry>
- <entry>a<subscript>3</subscript></entry>
- <entry>a<subscript>2</subscript></entry>
- <entry>a<subscript>1</subscript></entry>
- <entry>a<subscript>0</subscript></entry>
- <entry></entry>
- <entry>r<subscript>7</subscript></entry>
- <entry>r<subscript>6</subscript></entry>
- <entry>r<subscript>5</subscript></entry>
- <entry>r<subscript>4</subscript></entry>
- <entry>r<subscript>3</subscript></entry>
- <entry>r<subscript>2</subscript></entry>
- <entry>r<subscript>1</subscript></entry>
- <entry>r<subscript>0</subscript></entry>
- <entry></entry>
- <entry>g<subscript>7</subscript></entry>
- <entry>g<subscript>6</subscript></entry>
- <entry>g<subscript>5</subscript></entry>
- <entry>g<subscript>4</subscript></entry>
- <entry>g<subscript>3</subscript></entry>
- <entry>g<subscript>2</subscript></entry>
- <entry>g<subscript>1</subscript></entry>
- <entry>g<subscript>0</subscript></entry>
- <entry></entry>
- <entry>b<subscript>7</subscript></entry>
- <entry>b<subscript>6</subscript></entry>
- <entry>b<subscript>5</subscript></entry>
- <entry>b<subscript>4</subscript></entry>
- <entry>b<subscript>3</subscript></entry>
- <entry>b<subscript>2</subscript></entry>
- <entry>b<subscript>1</subscript></entry>
- <entry>b<subscript>0</subscript></entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
<para>A test utility to determine which RGB formats a driver
actually supports is available from the LinuxTV v4l-dvb repository.
See &v4l-dvb; for access instructions.</para>
diff --git a/Documentation/DocBook/media/v4l/pixfmt-sdr-cu08.xml b/Documentation/DocBook/media/v4l/pixfmt-sdr-cu08.xml
new file mode 100644
index 000000000000..2d80104c178b
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/pixfmt-sdr-cu08.xml
@@ -0,0 +1,44 @@
+<refentry id="V4L2-SDR-FMT-CU08">
+ <refmeta>
+ <refentrytitle>V4L2_SDR_FMT_CU8 ('CU08')</refentrytitle>
+ &manvol;
+ </refmeta>
+ <refnamediv>
+ <refname>
+ <constant>V4L2_SDR_FMT_CU8</constant>
+ </refname>
+ <refpurpose>Complex unsigned 8-bit IQ sample</refpurpose>
+ </refnamediv>
+ <refsect1>
+ <title>Description</title>
+ <para>
+This format contains sequence of complex number samples. Each complex number
+consist two parts, called In-phase and Quadrature (IQ). Both I and Q are
+represented as a 8 bit unsigned number. I value comes first and Q value after
+that.
+ </para>
+ <example>
+ <title><constant>V4L2_SDR_FMT_CU8</constant> 1 sample</title>
+ <formalpara>
+ <title>Byte Order.</title>
+ <para>Each cell is one byte.
+ <informaltable frame="none">
+ <tgroup cols="2" align="center">
+ <colspec align="left" colwidth="2*" />
+ <tbody valign="top">
+ <row>
+ <entry>start&nbsp;+&nbsp;0:</entry>
+ <entry>I'<subscript>0</subscript></entry>
+ </row>
+ <row>
+ <entry>start&nbsp;+&nbsp;1:</entry>
+ <entry>Q'<subscript>0</subscript></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+ </example>
+ </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/pixfmt-sdr-cu16le.xml b/Documentation/DocBook/media/v4l/pixfmt-sdr-cu16le.xml
new file mode 100644
index 000000000000..26288ffa9071
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/pixfmt-sdr-cu16le.xml
@@ -0,0 +1,46 @@
+<refentry id="V4L2-SDR-FMT-CU16LE">
+ <refmeta>
+ <refentrytitle>V4L2_SDR_FMT_CU16LE ('CU16')</refentrytitle>
+ &manvol;
+ </refmeta>
+ <refnamediv>
+ <refname>
+ <constant>V4L2_SDR_FMT_CU16LE</constant>
+ </refname>
+ <refpurpose>Complex unsigned 16-bit little endian IQ sample</refpurpose>
+ </refnamediv>
+ <refsect1>
+ <title>Description</title>
+ <para>
+This format contains sequence of complex number samples. Each complex number
+consist two parts, called In-phase and Quadrature (IQ). Both I and Q are
+represented as a 16 bit unsigned little endian number. I value comes first
+and Q value after that.
+ </para>
+ <example>
+ <title><constant>V4L2_SDR_FMT_CU16LE</constant> 1 sample</title>
+ <formalpara>
+ <title>Byte Order.</title>
+ <para>Each cell is one byte.
+ <informaltable frame="none">
+ <tgroup cols="3" align="center">
+ <colspec align="left" colwidth="2*" />
+ <tbody valign="top">
+ <row>
+ <entry>start&nbsp;+&nbsp;0:</entry>
+ <entry>I'<subscript>0[7:0]</subscript></entry>
+ <entry>I'<subscript>0[15:8]</subscript></entry>
+ </row>
+ <row>
+ <entry>start&nbsp;+&nbsp;2:</entry>
+ <entry>Q'<subscript>0[7:0]</subscript></entry>
+ <entry>Q'<subscript>0[15:8]</subscript></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+ </example>
+ </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml
index 72d72bd67d0a..ea514d6075c5 100644
--- a/Documentation/DocBook/media/v4l/pixfmt.xml
+++ b/Documentation/DocBook/media/v4l/pixfmt.xml
@@ -25,7 +25,12 @@ capturing and output, for overlay frame buffer formats see also
<row>
<entry>__u32</entry>
<entry><structfield>height</structfield></entry>
- <entry>Image height in pixels.</entry>
+ <entry>Image height in pixels. If <structfield>field</structfield> is
+ one of <constant>V4L2_FIELD_TOP</constant>, <constant>V4L2_FIELD_BOTTOM</constant>
+ or <constant>V4L2_FIELD_ALTERNATE</constant> then height refers to the
+ number of lines in the field, otherwise it refers to the number of
+ lines in the frame (which is twice the field height for interlaced
+ formats).</entry>
</row>
<row>
<entry spanname="hspan">Applications set these fields to
@@ -54,7 +59,7 @@ linkend="reserved-formats" /></entry>
can request to capture or output only the top or bottom field, or both
fields interlaced or sequentially stored in one buffer or alternating
in separate buffers. Drivers return the actual field order selected.
-For details see <xref linkend="field-order" />.</entry>
+For more details on fields see <xref linkend="field-order" />.</entry>
</row>
<row>
<entry>__u32</entry>
@@ -81,7 +86,10 @@ plane and is divided by the same factor as the
example the Cb and Cr planes of a YUV 4:2:0 image have half as many
padding bytes following each line as the Y plane. To avoid ambiguities
drivers must return a <structfield>bytesperline</structfield> value
-rounded up to a multiple of the scale factor.</para></entry>
+rounded up to a multiple of the scale factor.</para>
+<para>For compressed formats the <structfield>bytesperline</structfield>
+value makes no sense. Applications and drivers must set this to 0 in
+that case.</para></entry>
</row>
<row>
<entry>__u32</entry>
@@ -97,7 +105,8 @@ hold an image.</entry>
<entry>&v4l2-colorspace;</entry>
<entry><structfield>colorspace</structfield></entry>
<entry>This information supplements the
-<structfield>pixelformat</structfield> and must be set by the driver,
+<structfield>pixelformat</structfield> and must be set by the driver for
+capture streams and by the application for output streams,
see <xref linkend="colorspaces" />.</entry>
</row>
<row>
@@ -135,7 +144,7 @@ set this field to zero.</entry>
<entry>__u16</entry>
<entry><structfield>bytesperline</structfield></entry>
<entry>Distance in bytes between the leftmost pixels in two adjacent
- lines.</entry>
+ lines. See &v4l2-pix-format;.</entry>
</row>
<row>
<entry>__u16</entry>
@@ -154,12 +163,12 @@ set this field to zero.</entry>
<row>
<entry>__u32</entry>
<entry><structfield>width</structfield></entry>
- <entry>Image width in pixels.</entry>
+ <entry>Image width in pixels. See &v4l2-pix-format;.</entry>
</row>
<row>
<entry>__u32</entry>
<entry><structfield>height</structfield></entry>
- <entry>Image height in pixels.</entry>
+ <entry>Image height in pixels. See &v4l2-pix-format;.</entry>
</row>
<row>
<entry>__u32</entry>
@@ -811,6 +820,17 @@ extended control <constant>V4L2_CID_MPEG_STREAM_TYPE</constant>, see
</table>
</section>
+ <section id="sdr-formats">
+ <title>SDR Formats</title>
+
+ <para>These formats are used for <link linkend="sdr">SDR Capture</link>
+interface only.</para>
+
+ &sub-sdr-cu08;
+ &sub-sdr-cu16le;
+
+ </section>
+
<section id="pixfmt-reserved">
<title>Reserved Format Identifiers</title>
diff --git a/Documentation/DocBook/media/v4l/remote_controllers.xml b/Documentation/DocBook/media/v4l/remote_controllers.xml
index 160e464d44b7..5124a6c4daa8 100644
--- a/Documentation/DocBook/media/v4l/remote_controllers.xml
+++ b/Documentation/DocBook/media/v4l/remote_controllers.xml
@@ -1,10 +1,152 @@
+<partinfo>
+<authorgroup>
+<author>
+<firstname>Mauro</firstname>
+<surname>Chehab</surname>
+<othername role="mi">Carvalho</othername>
+<affiliation><address><email>m.chehab@samsung.com</email></address></affiliation>
+<contrib>Initial version.</contrib>
+</author>
+</authorgroup>
+<copyright>
+ <year>2009-2014</year>
+ <holder>Mauro Carvalho Chehab</holder>
+</copyright>
+
+<revhistory>
+<!-- Put document revisions here, newest first. -->
+<revision>
+<revnumber>3.15</revnumber>
+<date>2014-02-06</date>
+<authorinitials>mcc</authorinitials>
+<revremark>Added the interface description and the RC sysfs class description.</revremark>
+</revision>
+<revision>
+<revnumber>1.0</revnumber>
+<date>2009-09-06</date>
+<authorinitials>mcc</authorinitials>
+<revremark>Initial revision</revremark>
+</revision>
+</revhistory>
+</partinfo>
+
+ <title>Remote Controller API</title>
+ <chapter id="remote_controllers">
+
<title>Remote Controllers</title>
+
<section id="Remote_controllers_Intro">
<title>Introduction</title>
<para>Currently, most analog and digital devices have a Infrared input for remote controllers. Each
manufacturer has their own type of control. It is not rare for the same manufacturer to ship different
types of controls, depending on the device.</para>
+<para>A Remote Controller interface is mapped as a normal evdev/input interface, just like a keyboard or a mouse.
+So, it uses all ioctls already defined for any other input devices.</para>
+<para>However, remove controllers are more flexible than a normal input device, as the IR
+receiver (and/or transmitter) can be used in conjunction with a wide variety of different IR remotes.</para>
+<para>In order to allow flexibility, the Remote Controller subsystem allows controlling the
+RC-specific attributes via <link linkend="remote_controllers_sysfs_nodes">the sysfs class nodes</link>.</para>
+</section>
+
+<section id="remote_controllers_sysfs_nodes">
+<title>Remote Controller's sysfs nodes</title>
+<para>As defined at <constant>Documentation/ABI/testing/sysfs-class-rc</constant>, those are the sysfs nodes that control the Remote Controllers:</para>
+
+<section id="sys_class_rc">
+<title>/sys/class/rc/</title>
+<para>The <constant>/sys/class/rc/</constant> class sub-directory belongs to the Remote Controller
+core and provides a sysfs interface for configuring infrared remote controller receivers.
+</para>
+
+</section>
+<section id="sys_class_rc_rcN">
+<title>/sys/class/rc/rcN/</title>
+<para>A <constant>/sys/class/rc/rcN</constant> directory is created for each remote
+ control receiver device where N is the number of the receiver.</para>
+
+</section>
+<section id="sys_class_rc_rcN_protocols">
+<title>/sys/class/rc/rcN/protocols</title>
+<para>Reading this file returns a list of available protocols, something like:</para>
+<para><constant>rc5 [rc6] nec jvc [sony]</constant></para>
+<para>Enabled protocols are shown in [] brackets.</para>
+<para>Writing "+proto" will add a protocol to the list of enabled protocols.</para>
+<para>Writing "-proto" will remove a protocol from the list of enabled protocols.</para>
+<para>Writing "proto" will enable only "proto".</para>
+<para>Writing "none" will disable all protocols.</para>
+<para>Write fails with EINVAL if an invalid protocol combination or unknown protocol name is used.</para>
+
+</section>
+<section id="sys_class_rc_rcN_filter">
+<title>/sys/class/rc/rcN/filter</title>
+<para>Sets the scancode filter expected value.</para>
+<para>Use in combination with <constant>/sys/class/rc/rcN/filter_mask</constant> to set the
+expected value of the bits set in the filter mask.
+If the hardware supports it then scancodes which do not match
+the filter will be ignored. Otherwise the write will fail with
+an error.</para>
+<para>This value may be reset to 0 if the current protocol is altered.</para>
+
+</section>
+<section id="sys_class_rc_rcN_filter_mask">
+<title>/sys/class/rc/rcN/filter_mask</title>
+<para>Sets the scancode filter mask of bits to compare.
+Use in combination with <constant>/sys/class/rc/rcN/filter</constant> to set the bits
+of the scancode which should be compared against the expected
+value. A value of 0 disables the filter to allow all valid
+scancodes to be processed.</para>
+<para>If the hardware supports it then scancodes which do not match
+the filter will be ignored. Otherwise the write will fail with
+an error.</para>
+<para>This value may be reset to 0 if the current protocol is altered.</para>
+
+</section>
+<section id="sys_class_rc_rcN_wakeup_protocols">
+<title>/sys/class/rc/rcN/wakeup_protocols</title>
+<para>Reading this file returns a list of available protocols to use for the
+wakeup filter, something like:</para>
+<para><constant>rc5 rc6 nec jvc [sony]</constant></para>
+<para>The enabled wakeup protocol is shown in [] brackets.</para>
+<para>Writing "+proto" will add a protocol to the list of enabled wakeup
+protocols.</para>
+<para>Writing "-proto" will remove a protocol from the list of enabled wakeup
+protocols.</para>
+<para>Writing "proto" will use "proto" for wakeup events.</para>
+<para>Writing "none" will disable wakeup.</para>
+<para>Write fails with EINVAL if an invalid protocol combination or unknown
+protocol name is used, or if wakeup is not supported by the hardware.</para>
+
+</section>
+<section id="sys_class_rc_rcN_wakeup_filter">
+<title>/sys/class/rc/rcN/wakeup_filter</title>
+<para>Sets the scancode wakeup filter expected value.
+Use in combination with <constant>/sys/class/rc/rcN/wakeup_filter_mask</constant> to
+set the expected value of the bits set in the wakeup filter mask
+to trigger a system wake event.</para>
+<para>If the hardware supports it and wakeup_filter_mask is not 0 then
+scancodes which match the filter will wake the system from e.g.
+suspend to RAM or power off.
+Otherwise the write will fail with an error.</para>
+<para>This value may be reset to 0 if the wakeup protocol is altered.</para>
+
+</section>
+<section id="sys_class_rc_rcN_wakeup_filter_mask">
+<title>/sys/class/rc/rcN/wakeup_filter_mask</title>
+<para>Sets the scancode wakeup filter mask of bits to compare.
+Use in combination with <constant>/sys/class/rc/rcN/wakeup_filter</constant> to set
+the bits of the scancode which should be compared against the
+expected value to trigger a system wake event.</para>
+<para>If the hardware supports it and wakeup_filter_mask is not 0 then
+scancodes which match the filter will wake the system from e.g.
+suspend to RAM or power off.
+Otherwise the write will fail with an error.</para>
+<para>This value may be reset to 0 if the wakeup protocol is altered.</para>
+</section>
+</section>
+
+<section id="Remote_controllers_tables">
+<title>Remote controller tables</title>
<para>Unfortunately, for several years, there was no effort to create uniform IR keycodes for
different devices. This caused the same IR keyname to be mapped completely differently on
different IR devices. This resulted that the same IR keyname to be mapped completely different on
@@ -175,3 +317,4 @@ keymapping.</para>
</section>
&sub-lirc_device_interface;
+</chapter>
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml
index 74b7f27af71a..b445161b912c 100644
--- a/Documentation/DocBook/media/v4l/v4l2.xml
+++ b/Documentation/DocBook/media/v4l/v4l2.xml
@@ -70,7 +70,7 @@ MPEG stream embedded, sliced VBI data format in this specification.
Remote Controller chapter.</contrib>
<affiliation>
<address>
- <email>mchehab@redhat.com</email>
+ <email>m.chehab@samsung.com</email>
</address>
</affiliation>
</author>
@@ -107,6 +107,16 @@ Remote Controller chapter.</contrib>
</address>
</affiliation>
</author>
+ <author>
+ <firstname>Antti</firstname>
+ <surname>Palosaari</surname>
+ <contrib>SDR API.</contrib>
+ <affiliation>
+ <address>
+ <email>crope@iki.fi</email>
+ </address>
+ </affiliation>
+ </author>
</authorgroup>
<copyright>
@@ -125,6 +135,7 @@ Remote Controller chapter.</contrib>
<year>2011</year>
<year>2012</year>
<year>2013</year>
+ <year>2014</year>
<holder>Bill Dirks, Michael H. Schimek, Hans Verkuil, Martin
Rubli, Andy Walls, Muralidharan Karicheri, Mauro Carvalho Chehab,
Pawel Osciak</holder>
@@ -141,6 +152,16 @@ structs, ioctls) must be noted in more detail in the history chapter
applications. -->
<revision>
+ <revnumber>3.15</revnumber>
+ <date>2014-02-03</date>
+ <authorinitials>hv, ap</authorinitials>
+ <revremark>Update several sections of "Common API Elements": "Opening and Closing Devices"
+"Querying Capabilities", "Application Priority", "Video Inputs and Outputs", "Audio Inputs and Outputs"
+"Tuners and Modulators", "Video Standards" and "Digital Video (DV) Timings". Added SDR API.
+ </revremark>
+ </revision>
+
+ <revision>
<revnumber>3.14</revnumber>
<date>2013-11-25</date>
<authorinitials>rr</authorinitials>
@@ -537,6 +558,7 @@ and discussions on the V4L mailing list.</revremark>
<section id="ttx"> &sub-dev-teletext; </section>
<section id="radio"> &sub-dev-radio; </section>
<section id="rds"> &sub-dev-rds; </section>
+ <section id="sdr"> &sub-dev-sdr; </section>
<section id="event"> &sub-dev-event; </section>
<section id="subdev"> &sub-dev-subdev; </section>
</chapter>
@@ -585,6 +607,7 @@ and discussions on the V4L mailing list.</revremark>
&sub-g-crop;
&sub-g-ctrl;
&sub-g-dv-timings;
+ &sub-g-edid;
&sub-g-enc-index;
&sub-g-ext-ctrls;
&sub-g-fbuf;
@@ -616,7 +639,6 @@ and discussions on the V4L mailing list.</revremark>
&sub-subdev-enum-frame-size;
&sub-subdev-enum-mbus-code;
&sub-subdev-g-crop;
- &sub-subdev-g-edid;
&sub-subdev-g-fmt;
&sub-subdev-g-frame-interval;
&sub-subdev-g-selection;
diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml b/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml
index 6541ba0175ed..4e8ea65f7282 100644
--- a/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml
@@ -100,7 +100,7 @@ See <xref linkend="v4l2-tuner-type" /></entry>
<entry><structfield>capability</structfield></entry>
<entry spanname="hspan">The tuner/modulator capability flags for
this frequency band, see <xref linkend="tuner-capability" />. The <constant>V4L2_TUNER_CAP_LOW</constant>
-capability must be the same for all frequency bands of the selected tuner/modulator.
+or <constant>V4L2_TUNER_CAP_1HZ</constant> capability must be the same for all frequency bands of the selected tuner/modulator.
So either all bands have that capability set, or none of them have that capability.</entry>
</row>
<row>
@@ -109,7 +109,8 @@ So either all bands have that capability set, or none of them have that capabili
<entry spanname="hspan">The lowest tunable frequency in
units of 62.5 kHz, or if the <structfield>capability</structfield>
flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
-Hz, for this frequency band.</entry>
+Hz, for this frequency band. A 1 Hz unit is used when the <structfield>capability</structfield> flag
+<constant>V4L2_TUNER_CAP_1HZ</constant> is set.</entry>
</row>
<row>
<entry>__u32</entry>
@@ -117,7 +118,8 @@ Hz, for this frequency band.</entry>
<entry spanname="hspan">The highest tunable frequency in
units of 62.5 kHz, or if the <structfield>capability</structfield>
flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
-Hz, for this frequency band.</entry>
+Hz, for this frequency band. A 1 Hz unit is used when the <structfield>capability</structfield> flag
+<constant>V4L2_TUNER_CAP_1HZ</constant> is set.</entry>
</row>
<row>
<entry>__u32</entry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-edid.xml b/Documentation/DocBook/media/v4l/vidioc-g-edid.xml
index bbd18f0e6ede..ce4563b87131 100644
--- a/Documentation/DocBook/media/v4l/vidioc-subdev-g-edid.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-edid.xml
@@ -1,12 +1,12 @@
-<refentry id="vidioc-subdev-g-edid">
+<refentry id="vidioc-g-edid">
<refmeta>
- <refentrytitle>ioctl VIDIOC_SUBDEV_G_EDID, VIDIOC_SUBDEV_S_EDID</refentrytitle>
+ <refentrytitle>ioctl VIDIOC_G_EDID, VIDIOC_S_EDID</refentrytitle>
&manvol;
</refmeta>
<refnamediv>
- <refname>VIDIOC_SUBDEV_G_EDID</refname>
- <refname>VIDIOC_SUBDEV_S_EDID</refname>
+ <refname>VIDIOC_G_EDID</refname>
+ <refname>VIDIOC_S_EDID</refname>
<refpurpose>Get or set the EDID of a video receiver/transmitter</refpurpose>
</refnamediv>
@@ -16,7 +16,7 @@
<funcdef>int <function>ioctl</function></funcdef>
<paramdef>int <parameter>fd</parameter></paramdef>
<paramdef>int <parameter>request</parameter></paramdef>
- <paramdef>struct v4l2_subdev_edid *<parameter>argp</parameter></paramdef>
+ <paramdef>struct v4l2_edid *<parameter>argp</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<funcsynopsis>
@@ -24,7 +24,7 @@
<funcdef>int <function>ioctl</function></funcdef>
<paramdef>int <parameter>fd</parameter></paramdef>
<paramdef>int <parameter>request</parameter></paramdef>
- <paramdef>const struct v4l2_subdev_edid *<parameter>argp</parameter></paramdef>
+ <paramdef>const struct v4l2_edid *<parameter>argp</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
@@ -42,7 +42,7 @@
<varlistentry>
<term><parameter>request</parameter></term>
<listitem>
- <para>VIDIOC_SUBDEV_G_EDID, VIDIOC_SUBDEV_S_EDID</para>
+ <para>VIDIOC_G_EDID, VIDIOC_S_EDID</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -56,12 +56,20 @@
<refsect1>
<title>Description</title>
- <para>These ioctls can be used to get or set an EDID associated with an input pad
- from a receiver or an output pad of a transmitter subdevice.</para>
+ <para>These ioctls can be used to get or set an EDID associated with an input
+ from a receiver or an output of a transmitter device. They can be
+ used with subdevice nodes (/dev/v4l-subdevX) or with video nodes (/dev/videoX).</para>
+
+ <para>When used with video nodes the <structfield>pad</structfield> field represents the
+ input (for video capture devices) or output (for video output devices) index as
+ is returned by &VIDIOC-ENUMINPUT; and &VIDIOC-ENUMOUTPUT; respectively. When used
+ with subdevice nodes the <structfield>pad</structfield> field represents the
+ input or output pad of the subdevice. If there is no EDID support for the given
+ <structfield>pad</structfield> value, then the &EINVAL; will be returned.</para>
<para>To get the EDID data the application has to fill in the <structfield>pad</structfield>,
<structfield>start_block</structfield>, <structfield>blocks</structfield> and <structfield>edid</structfield>
- fields and call <constant>VIDIOC_SUBDEV_G_EDID</constant>. The current EDID from block
+ fields and call <constant>VIDIOC_G_EDID</constant>. The current EDID from block
<structfield>start_block</structfield> and of size <structfield>blocks</structfield>
will be placed in the memory <structfield>edid</structfield> points to. The <structfield>edid</structfield>
pointer must point to memory at least <structfield>blocks</structfield>&nbsp;*&nbsp;128 bytes
@@ -91,15 +99,17 @@
data in some way. In any case, the end result is the same: the EDID is no longer available.
</para>
- <table pgwide="1" frame="none" id="v4l2-subdev-edid">
- <title>struct <structname>v4l2_subdev_edid</structname></title>
+ <table pgwide="1" frame="none" id="v4l2-edid">
+ <title>struct <structname>v4l2_edid</structname></title>
<tgroup cols="3">
&cs-str;
<tbody valign="top">
<row>
<entry>__u32</entry>
<entry><structfield>pad</structfield></entry>
- <entry>Pad for which to get/set the EDID blocks.</entry>
+ <entry>Pad for which to get/set the EDID blocks. When used with a video device
+ node the pad represents the input or output index as returned by
+ &VIDIOC-ENUMINPUT; and &VIDIOC-ENUMOUTPUT; respectively.</entry>
</row>
<row>
<entry>__u32</entry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml
index b3bb9575b2e0..e9f6735c0823 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml
@@ -327,7 +327,12 @@ These controls are described in <xref
These controls are described in <xref
linkend="fm-rx-controls" />.</entry>
</row>
-
+ <row>
+ <entry><constant>V4L2_CTRL_CLASS_RF_TUNER</constant></entry>
+ <entry>0xa20000</entry>
+ <entry>The class containing RF tuner controls.
+These controls are described in <xref linkend="rf-tuner-controls" />.</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml b/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml
index ee8f56e1bac0..4fe19a7a9a31 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml
@@ -172,6 +172,13 @@ capture and output devices.</entry>
</row>
<row>
<entry></entry>
+ <entry>&v4l2-sdr-format;</entry>
+ <entry><structfield>sdr</structfield></entry>
+ <entry>Definition of a data format, see
+<xref linkend="pixfmt" />, used by SDR capture devices.</entry>
+ </row>
+ <row>
+ <entry></entry>
<entry>__u8</entry>
<entry><structfield>raw_data</structfield>[200]</entry>
<entry>Place holder for future extensions.</entry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml b/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml
index c7a1c462e724..d1034fb61d15 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml
@@ -109,9 +109,10 @@ See <xref linkend="v4l2-tuner-type" /></entry>
<entry>__u32</entry>
<entry><structfield>frequency</structfield></entry>
<entry>Tuning frequency in units of 62.5 kHz, or if the
-&v4l2-tuner; or &v4l2-modulator; <structfield>capabilities</structfield> flag
+&v4l2-tuner; or &v4l2-modulator; <structfield>capability</structfield> flag
<constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
-Hz.</entry>
+Hz. A 1 Hz unit is used when the <structfield>capability</structfield> flag
+<constant>V4L2_TUNER_CAP_1HZ</constant> is set.</entry>
</row>
<row>
<entry>__u32</entry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-modulator.xml b/Documentation/DocBook/media/v4l/vidioc-g-modulator.xml
index 7f4ac7e41fa8..7068b599a00d 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-modulator.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-modulator.xml
@@ -113,7 +113,8 @@ change for example with the current video standard.</entry>
<entry>The lowest tunable frequency in units of 62.5
KHz, or if the <structfield>capability</structfield> flag
<constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
-Hz.</entry>
+Hz, or if the <structfield>capability</structfield> flag
+<constant>V4L2_TUNER_CAP_1HZ</constant> is set, in units of 1 Hz.</entry>
</row>
<row>
<entry>__u32</entry>
@@ -121,7 +122,8 @@ Hz.</entry>
<entry>The highest tunable frequency in units of 62.5
KHz, or if the <structfield>capability</structfield> flag
<constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
-Hz.</entry>
+Hz, or if the <structfield>capability</structfield> flag
+<constant>V4L2_TUNER_CAP_1HZ</constant> is set, in units of 1 Hz.</entry>
</row>
<row>
<entry>__u32</entry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml b/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
index 6cc82010c736..b0d865933da6 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
@@ -134,7 +134,9 @@ the structure refers to a radio tuner the
<entry spanname="hspan">The lowest tunable frequency in
units of 62.5 kHz, or if the <structfield>capability</structfield>
flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
-Hz. If multiple frequency bands are supported, then
+Hz, or if the <structfield>capability</structfield> flag
+<constant>V4L2_TUNER_CAP_1HZ</constant> is set, in units of 1 Hz.
+If multiple frequency bands are supported, then
<structfield>rangelow</structfield> is the lowest frequency
of all the frequency bands.</entry>
</row>
@@ -144,7 +146,9 @@ of all the frequency bands.</entry>
<entry spanname="hspan">The highest tunable frequency in
units of 62.5 kHz, or if the <structfield>capability</structfield>
flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
-Hz. If multiple frequency bands are supported, then
+Hz, or if the <structfield>capability</structfield> flag
+<constant>V4L2_TUNER_CAP_1HZ</constant> is set, in units of 1 Hz.
+If multiple frequency bands are supported, then
<structfield>rangehigh</structfield> is the highest frequency
of all the frequency bands.</entry>
</row>
@@ -270,7 +274,7 @@ applications must set the array to zero.</entry>
<entry><constant>V4L2_TUNER_CAP_LOW</constant></entry>
<entry>0x0001</entry>
<entry>When set, tuning frequencies are expressed in units of
-62.5&nbsp;Hz, otherwise in units of 62.5&nbsp;kHz.</entry>
+62.5 Hz instead of 62.5 kHz.</entry>
</row>
<row>
<entry><constant>V4L2_TUNER_CAP_NORM</constant></entry>
@@ -360,6 +364,11 @@ radio tuners.</entry>
<entry>The range to search when using the hardware seek functionality
is programmable, see &VIDIOC-S-HW-FREQ-SEEK; for details.</entry>
</row>
+ <row>
+ <entry><constant>V4L2_TUNER_CAP_1HZ</constant></entry>
+ <entry>0x1000</entry>
+ <entry>When set, tuning frequencies are expressed in units of 1 Hz instead of 62.5 kHz.</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/Documentation/DocBook/media/v4l/vidioc-querycap.xml b/Documentation/DocBook/media/v4l/vidioc-querycap.xml
index d5a3c97b206a..370d49d6fb64 100644
--- a/Documentation/DocBook/media/v4l/vidioc-querycap.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-querycap.xml
@@ -296,6 +296,12 @@ modulator programming see
<xref linkend="tuner" />.</entry>
</row>
<row>
+ <entry><constant>V4L2_CAP_SDR_CAPTURE</constant></entry>
+ <entry>0x00100000</entry>
+ <entry>The device supports the
+<link linkend="sdr">SDR Capture</link> interface.</entry>
+ </row>
+ <row>
<entry><constant>V4L2_CAP_READWRITE</constant></entry>
<entry>0x01000000</entry>
<entry>The device supports the <link
diff --git a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
index 5b379e752194..a5fc4c4880f3 100644
--- a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
@@ -121,7 +121,9 @@ field and the &v4l2-tuner; <structfield>index</structfield> field.</entry>
<entry>If non-zero, the lowest tunable frequency of the band to
search in units of 62.5 kHz, or if the &v4l2-tuner;
<structfield>capability</structfield> field has the
-<constant>V4L2_TUNER_CAP_LOW</constant> flag set, in units of 62.5 Hz.
+<constant>V4L2_TUNER_CAP_LOW</constant> flag set, in units of 62.5 Hz or if the &v4l2-tuner;
+<structfield>capability</structfield> field has the
+<constant>V4L2_TUNER_CAP_1HZ</constant> flag set, in units of 1 Hz.
If <structfield>rangelow</structfield> is zero a reasonable default value
is used.</entry>
</row>
@@ -131,7 +133,9 @@ is used.</entry>
<entry>If non-zero, the highest tunable frequency of the band to
search in units of 62.5 kHz, or if the &v4l2-tuner;
<structfield>capability</structfield> field has the
-<constant>V4L2_TUNER_CAP_LOW</constant> flag set, in units of 62.5 Hz.
+<constant>V4L2_TUNER_CAP_LOW</constant> flag set, in units of 62.5 Hz or if the &v4l2-tuner;
+<structfield>capability</structfield> field has the
+<constant>V4L2_TUNER_CAP_1HZ</constant> flag set, in units of 1 Hz.
If <structfield>rangehigh</structfield> is zero a reasonable default value
is used.</entry>
</row>
diff --git a/Documentation/DocBook/media/v4l/vidioc-streamon.xml b/Documentation/DocBook/media/v4l/vidioc-streamon.xml
index 65dff55079d7..df2c63d07bac 100644
--- a/Documentation/DocBook/media/v4l/vidioc-streamon.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-streamon.xml
@@ -52,16 +52,24 @@
<para>The <constant>VIDIOC_STREAMON</constant> and
<constant>VIDIOC_STREAMOFF</constant> ioctl start and stop the capture
or output process during streaming (<link linkend="mmap">memory
-mapping</link> or <link linkend="userp">user pointer</link>) I/O.</para>
+mapping</link>, <link linkend="userp">user pointer</link> or
+<link linkend="dmabuf">DMABUF</link>) I/O.</para>
- <para>Specifically the capture hardware is disabled and no input
+ <para>Capture hardware is disabled and no input
buffers are filled (if there are any empty buffers in the incoming
queue) until <constant>VIDIOC_STREAMON</constant> has been called.
-Accordingly the output hardware is disabled, no video signal is
+Output hardware is disabled and no video signal is
produced until <constant>VIDIOC_STREAMON</constant> has been called.
The ioctl will succeed when at least one output buffer is in the
incoming queue.</para>
+ <para>Memory-to-memory devices will not start until
+<constant>VIDIOC_STREAMON</constant> has been called for both the capture
+and output stream types.</para>
+
+ <para>If <constant>VIDIOC_STREAMON</constant> fails then any already
+queued buffers will remain queued.</para>
+
<para>The <constant>VIDIOC_STREAMOFF</constant> ioctl, apart of
aborting or finishing any DMA in progress, unlocks any user pointer
buffers locked in physical memory, and it removes all buffers from the
@@ -70,14 +78,22 @@ dequeued yet will be lost, likewise all images enqueued for output but
not transmitted yet. I/O returns to the same state as after calling
&VIDIOC-REQBUFS; and can be restarted accordingly.</para>
+ <para>If buffers have been queued with &VIDIOC-QBUF; and
+<constant>VIDIOC_STREAMOFF</constant> is called without ever having
+called <constant>VIDIOC_STREAMON</constant>, then those queued buffers
+will also be removed from the incoming queue and all are returned to the
+same state as after calling &VIDIOC-REQBUFS; and can be restarted
+accordingly.</para>
+
<para>Both ioctls take a pointer to an integer, the desired buffer or
stream type. This is the same as &v4l2-requestbuffers;
<structfield>type</structfield>.</para>
<para>If <constant>VIDIOC_STREAMON</constant> is called when streaming
is already in progress, or if <constant>VIDIOC_STREAMOFF</constant> is called
-when streaming is already stopped, then the ioctl does nothing and 0 is
-returned.</para>
+when streaming is already stopped, then 0 is returned. Nothing happens in the
+case of <constant>VIDIOC_STREAMON</constant>, but <constant>VIDIOC_STREAMOFF</constant>
+will return queued buffers to their starting state as mentioned above.</para>
<para>Note that applications can be preempted for unknown periods right
before or after the <constant>VIDIOC_STREAMON</constant> or
@@ -93,7 +109,7 @@ synchronize with other events.</para>
<varlistentry>
<term><errorcode>EINVAL</errorcode></term>
<listitem>
- <para>The buffer<structfield>type</structfield> is not supported,
+ <para>The buffer <structfield>type</structfield> is not supported,
or no buffers have been allocated (memory mapping) or enqueued
(output) yet.</para>
</listitem>
diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl
index 4c8d282545a2..4decb46bfa76 100644
--- a/Documentation/DocBook/media_api.tmpl
+++ b/Documentation/DocBook/media_api.tmpl
@@ -34,22 +34,20 @@
<book id="media_api">
<bookinfo>
-<title>LINUX MEDIA INFRASTRUCTURE API</title>
-
-<copyright>
- <year>2009-2012</year>
- <holder>LinuxTV Developers</holder>
-</copyright>
-
-<legalnotice>
-
-<para>Permission is granted to copy, distribute and/or modify
-this document under the terms of the GNU Free Documentation License,
-Version 1.1 or any later version published by the Free Software
-Foundation. A copy of the license is included in the chapter entitled
-"GNU Free Documentation License"</para>
-</legalnotice>
-
+ <title>LINUX MEDIA INFRASTRUCTURE API</title>
+
+ <copyright>
+ <year>2009-2014</year>
+ <holder>LinuxTV Developers</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>Permission is granted to copy, distribute and/or modify
+ this document under the terms of the GNU Free Documentation License,
+ Version 1.1 or any later version published by the Free Software
+ Foundation. A copy of the license is included in the chapter entitled
+ "GNU Free Documentation License"</para>
+ </legalnotice>
</bookinfo>
<toc></toc> <!-- autogenerated -->
@@ -58,12 +56,13 @@ Foundation. A copy of the license is included in the chapter entitled
<title>Introduction</title>
<para>This document covers the Linux Kernel to Userspace API's used by
- video and radio straming devices, including video cameras,
+ video and radio streaming devices, including video cameras,
analog and digital TV receiver cards, AM/FM receiver cards,
- streaming capture devices.</para>
+ streaming capture and output devices, codec devices and remote
+ controllers.</para>
<para>It is divided into four parts.</para>
- <para>The first part covers radio, capture,
- cameras and analog TV devices.</para>
+ <para>The first part covers radio, video capture and output,
+ cameras, analog TV devices and codecs.</para>
<para>The second part covers the
API used for digital TV and Internet reception via one of the
several digital tv standards. While it is called as DVB API,
@@ -75,55 +74,14 @@ Foundation. A copy of the license is included in the chapter entitled
<para>For additional information and for the latest development code,
see: <ulink url="http://linuxtv.org">http://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>
-<part id="v4l2spec">
-&sub-v4l2;
-</part>
-<part id="dvbapi">
-&sub-dvbapi;
-</part>
-<part id="v4ldvb_common">
-<partinfo>
-<authorgroup>
-<author>
-<firstname>Mauro</firstname>
-<surname>Chehab</surname>
-<othername role="mi">Carvalho</othername>
-<affiliation><address><email>mchehab@redhat.com</email></address></affiliation>
-<contrib>Initial version.</contrib>
-</author>
-</authorgroup>
-<copyright>
- <year>2009-2012</year>
- <holder>Mauro Carvalho Chehab</holder>
-</copyright>
-
-<revhistory>
-<!-- Put document revisions here, newest first. -->
-<revision>
-<revnumber>1.0.0</revnumber>
-<date>2009-09-06</date>
-<authorinitials>mcc</authorinitials>
-<revremark>Initial revision</revremark>
-</revision>
-</revhistory>
-</partinfo>
-
-<title>Remote Controller API</title>
-<chapter id="remote_controllers">
-&sub-remote_controllers;
-</chapter>
-</part>
-<part id="media_common">
-&sub-media-controller;
-</part>
-
-<chapter id="gen_errors">
-&sub-gen-errors;
-</chapter>
+<part id="v4l2spec">&sub-v4l2;</part>
+<part id="dvbapi">&sub-dvbapi;</part>
+<part id="remotes">&sub-remote_controllers;</part>
+<part id="media_common">&sub-media-controller;</part>
+<chapter id="gen_errors">&sub-gen-errors;</chapter>
&sub-fdl-appendix;
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 26b1e31d5a13..2a8e89e13e45 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -14,7 +14,10 @@ Read Documentation/SubmitChecklist for a list of items to check
before submitting code. If you are submitting a driver, also read
Documentation/SubmittingDrivers.
-
+Many of these steps describe the default behavior of the git version
+control system; if you use git to prepare your patches, you'll find much
+of the mechanical work done for you, though you'll still need to prepare
+and document a sensible set of patches.
--------------------------------------------
SECTION 1 - CREATING AND SENDING YOUR CHANGE
@@ -25,7 +28,9 @@ SECTION 1 - CREATING AND SENDING YOUR CHANGE
1) "diff -up"
------------
-Use "diff -up" or "diff -uprN" to create patches.
+Use "diff -up" or "diff -uprN" to create patches. git generates patches
+in this form by default; if you're using git, you can skip this section
+entirely.
All changes to the Linux kernel occur in the form of patches, as
generated by diff(1). When creating your patch, make sure to create it
@@ -66,19 +71,14 @@ Make sure your patch does not include any extra files which do not
belong in a patch submission. Make sure to review your patch -after-
generated it with diff(1), to ensure accuracy.
-If your changes produce a lot of deltas, you may want to look into
-splitting them into individual patches which modify things in
-logical stages. This will facilitate easier reviewing by other
-kernel developers, very important if you want your patch accepted.
-There are a number of scripts which can aid in this:
-
-Quilt:
-http://savannah.nongnu.org/projects/quilt
+If your changes produce a lot of deltas, you need to split them into
+individual patches which modify things in logical stages; see section
+#3. This will facilitate easier reviewing by other kernel developers,
+very important if you want your patch accepted.
-Andrew Morton's patch scripts:
-http://userweb.kernel.org/~akpm/stuff/patch-scripts.tar.gz
-Instead of these scripts, quilt is the recommended patch management
-tool (see above).
+If you're using git, "git rebase -i" can help you with this process. If
+you're not using git, quilt <http://savannah.nongnu.org/projects/quilt>
+is another popular alternative.
@@ -106,8 +106,21 @@ I.e., the patch (series) and its description should be self-contained.
This benefits both the patch merger(s) and reviewers. Some reviewers
probably didn't even receive earlier versions of the patch.
+Describe your changes in imperative mood, e.g. "make xyzzy do frotz"
+instead of "[This patch] makes xyzzy do frotz" or "[I] changed xyzzy
+to do frotz", as if you are giving orders to the codebase to change
+its behaviour.
+
If the patch fixes a logged bug entry, refer to that bug entry by
-number and URL.
+number and URL. If the patch follows from a mailing list discussion,
+give a URL to the mailing list archive; use the https://lkml.kernel.org/
+redirector with a Message-Id, to ensure that the links cannot become
+stale.
+
+However, try to make your explanation understandable without external
+resources. In addition to giving a URL to a mailing list archive or
+bug, summarize the relevant points of the discussion that led to the
+patch as submitted.
If you want to refer to a specific commit, don't just refer to the
SHA-1 ID of the commit. Please also include the oneline summary of
@@ -594,7 +607,8 @@ patch.
If you are going to include a diffstat after the "---" marker, please
use diffstat options "-p 1 -w 70" so that filenames are listed from
the top of the kernel source tree and don't use too much horizontal
-space (easily fit in 80 columns, maybe with some indentation).
+space (easily fit in 80 columns, maybe with some indentation). (git
+generates appropriate diffstats by default.)
See more details on the proper patch format in the following
references.
@@ -725,7 +739,7 @@ SECTION 3 - REFERENCES
----------------------
Andrew Morton, "The perfect patch" (tpp).
- <http://userweb.kernel.org/~akpm/stuff/tpp.txt>
+ <http://www.ozlabs.org/~akpm/stuff/tpp.txt>
Jeff Garzik, "Linux kernel patch submission format".
<http://linux.yyz.us/patch-format.html>
@@ -738,7 +752,7 @@ Greg Kroah-Hartman, "How to piss off a kernel subsystem maintainer".
<http://www.kroah.com/log/linux/maintainer-05.html>
NO!!!! No more huge patch bombs to linux-kernel@vger.kernel.org people!
- <http://marc.theaimsgroup.com/?l=linux-kernel&m=112112749912944&w=2>
+ <https://lkml.org/lkml/2005/7/11/336>
Kernel Documentation/CodingStyle:
<http://users.sosdg.org/~qiyong/lxr/source/Documentation/CodingStyle>
diff --git a/Documentation/arm/Marvell/README b/Documentation/arm/Marvell/README
index 5a930c1528ad..963ec445e15a 100644
--- a/Documentation/arm/Marvell/README
+++ b/Documentation/arm/Marvell/README
@@ -83,14 +83,24 @@ EBU Armada family
88F6710
88F6707
88F6W11
+ Product Brief: http://www.marvell.com/embedded-processors/armada-300/assets/Marvell_ARMADA_370_SoC.pdf
+
+ Armada 375 Flavors:
+ 88F6720
+ Product Brief: http://www.marvell.com/embedded-processors/armada-300/assets/ARMADA_375_SoC-01_product_brief.pdf
+
+ Armada 380/385 Flavors:
+ 88F6810
+ 88F6820
+ 88F6828
Armada XP Flavors:
MV78230
MV78260
MV78460
NOTE: not to be confused with the non-SMP 78xx0 SoCs
+ Product Brief: http://www.marvell.com/embedded-processors/armada-xp/assets/Marvell-ArmadaXP-SoC-product%20brief.pdf
- Product Brief: http://www.marvell.com/embedded-processors/armada-xp/assets/Marvell-ArmadaXP-SoC-product%20brief.pdf
No public datasheet available.
Core: Sheeva ARMv7 compatible
diff --git a/Documentation/clk.txt b/Documentation/clk.txt
index 699ef2a323b1..c9c399af7c08 100644
--- a/Documentation/clk.txt
+++ b/Documentation/clk.txt
@@ -255,3 +255,37 @@ are sorted out.
To bypass this disabling, include "clk_ignore_unused" in the bootargs to the
kernel.
+
+ Part 7 - Locking
+
+The common clock framework uses two global locks, the prepare lock and the
+enable lock.
+
+The enable lock is a spinlock and is held across calls to the .enable,
+.disable and .is_enabled operations. Those operations are thus not allowed to
+sleep, and calls to the clk_enable(), clk_disable() and clk_is_enabled() API
+functions are allowed in atomic context.
+
+The prepare lock is a mutex and is held across calls to all other operations.
+All those operations are allowed to sleep, and calls to the corresponding API
+functions are not allowed in atomic context.
+
+This effectively divides operations in two groups from a locking perspective.
+
+Drivers don't need to manually protect resources shared between the operations
+of one group, regardless of whether those resources are shared by multiple
+clocks or not. However, access to resources that are shared between operations
+of the two groups needs to be protected by the drivers. An example of such a
+resource would be a register that controls both the clock rate and the clock
+enable/disable state.
+
+The clock framework is reentrant, in that a driver is allowed to call clock
+framework functions from within its implementation of clock operations. This
+can for instance cause a .set_rate operation of one clock being called from
+within the .set_rate operation of another clock. This case must be considered
+in the driver implementations, but the code flow is usually controlled by the
+driver in that case.
+
+Note that locking must also be considered when code outside of the common
+clock framework needs to access resources used by the clock operations. This
+is considered out of scope of this document.
diff --git a/Documentation/cpu-hotplug.txt b/Documentation/cpu-hotplug.txt
index be675d2d15a7..a0b005d2bd95 100644
--- a/Documentation/cpu-hotplug.txt
+++ b/Documentation/cpu-hotplug.txt
@@ -312,12 +312,57 @@ things will happen if a notifier in path sent a BAD notify code.
Q: I don't see my action being called for all CPUs already up and running?
A: Yes, CPU notifiers are called only when new CPUs are on-lined or offlined.
If you need to perform some action for each cpu already in the system, then
+ do this:
for_each_online_cpu(i) {
foobar_cpu_callback(&foobar_cpu_notifier, CPU_UP_PREPARE, i);
foobar_cpu_callback(&foobar_cpu_notifier, CPU_ONLINE, i);
}
+ However, if you want to register a hotplug callback, as well as perform
+ some initialization for CPUs that are already online, then do this:
+
+ Version 1: (Correct)
+ ---------
+
+ cpu_notifier_register_begin();
+
+ for_each_online_cpu(i) {
+ foobar_cpu_callback(&foobar_cpu_notifier,
+ CPU_UP_PREPARE, i);
+ foobar_cpu_callback(&foobar_cpu_notifier,
+ CPU_ONLINE, i);
+ }
+
+ /* Note the use of the double underscored version of the API */
+ __register_cpu_notifier(&foobar_cpu_notifier);
+
+ cpu_notifier_register_done();
+
+ Note that the following code is *NOT* the right way to achieve this,
+ because it is prone to an ABBA deadlock between the cpu_add_remove_lock
+ and the cpu_hotplug.lock.
+
+ Version 2: (Wrong!)
+ ---------
+
+ get_online_cpus();
+
+ for_each_online_cpu(i) {
+ foobar_cpu_callback(&foobar_cpu_notifier,
+ CPU_UP_PREPARE, i);
+ foobar_cpu_callback(&foobar_cpu_notifier,
+ CPU_ONLINE, i);
+ }
+
+ register_cpu_notifier(&foobar_cpu_notifier);
+
+ put_online_cpus();
+
+ So always use the first version shown above when you want to register
+ callbacks as well as initialize the already online CPUs.
+
+
Q: If i would like to develop cpu hotplug support for a new architecture,
what do i need at a minimum?
A: The following are what is required for CPU hotplug infrastructure to work
diff --git a/Documentation/device-mapper/era.txt b/Documentation/device-mapper/era.txt
new file mode 100644
index 000000000000..3c6d01be3560
--- /dev/null
+++ b/Documentation/device-mapper/era.txt
@@ -0,0 +1,108 @@
+Introduction
+============
+
+dm-era is a target that behaves similar to the linear target. In
+addition it keeps track of which blocks were written within a user
+defined period of time called an 'era'. Each era target instance
+maintains the current era as a monotonically increasing 32-bit
+counter.
+
+Use cases include tracking changed blocks for backup software, and
+partially invalidating the contents of a cache to restore cache
+coherency after rolling back a vendor snapshot.
+
+Constructor
+===========
+
+ era <metadata dev> <origin dev> <block size>
+
+ metadata dev : fast device holding the persistent metadata
+ origin dev : device holding data blocks that may change
+ block size : block size of origin data device, granularity that is
+ tracked by the target
+
+Messages
+========
+
+None of the dm messages take any arguments.
+
+checkpoint
+----------
+
+Possibly move to a new era. You shouldn't assume the era has
+incremented. After sending this message, you should check the
+current era via the status line.
+
+take_metadata_snap
+------------------
+
+Create a clone of the metadata, to allow a userland process to read it.
+
+drop_metadata_snap
+------------------
+
+Drop the metadata snapshot.
+
+Status
+======
+
+<metadata block size> <#used metadata blocks>/<#total metadata blocks>
+<current era> <held metadata root | '-'>
+
+metadata block size : Fixed block size for each metadata block in
+ sectors
+#used metadata blocks : Number of metadata blocks used
+#total metadata blocks : Total number of metadata blocks
+current era : The current era
+held metadata root : The location, in blocks, of the metadata root
+ that has been 'held' for userspace read
+ access. '-' indicates there is no held root
+
+Detailed use case
+=================
+
+The scenario of invalidating a cache when rolling back a vendor
+snapshot was the primary use case when developing this target:
+
+Taking a vendor snapshot
+------------------------
+
+- Send a checkpoint message to the era target
+- Make a note of the current era in its status line
+- Take vendor snapshot (the era and snapshot should be forever
+ associated now).
+
+Rolling back to an vendor snapshot
+----------------------------------
+
+- Cache enters passthrough mode (see: dm-cache's docs in cache.txt)
+- Rollback vendor storage
+- Take metadata snapshot
+- Ascertain which blocks have been written since the snapshot was taken
+ by checking each block's era
+- Invalidate those blocks in the caching software
+- Cache returns to writeback/writethrough mode
+
+Memory usage
+============
+
+The target uses a bitset to record writes in the current era. It also
+has a spare bitset ready for switching over to a new era. Other than
+that it uses a few 4k blocks for updating metadata.
+
+ (4 * nr_blocks) bytes + buffers
+
+Resilience
+==========
+
+Metadata is updated on disk before a write to a previously unwritten
+block is performed. As such dm-era should not be effected by a hard
+crash such as power failure.
+
+Userland tools
+==============
+
+Userland tools are found in the increasingly poorly named
+thin-provisioning-tools project:
+
+ https://github.com/jthornber/thin-provisioning-tools
diff --git a/Documentation/devices.txt b/Documentation/devices.txt
index 087d2122b204..87b4c5e82d39 100644
--- a/Documentation/devices.txt
+++ b/Documentation/devices.txt
@@ -353,6 +353,7 @@ Your cooperation is appreciated.
133 = /dev/exttrp External device trap
134 = /dev/apm_bios Advanced Power Management BIOS
135 = /dev/rtc Real Time Clock
+ 137 = /dev/vhci Bluetooth virtual HCI driver
139 = /dev/openprom SPARC OpenBoot PROM
140 = /dev/relay8 Berkshire Products Octal relay card
141 = /dev/relay16 Berkshire Products ISO-16 relay card
@@ -1493,10 +1494,17 @@ Your cooperation is appreciated.
64 = /dev/radio0 Radio device
...
127 = /dev/radio63 Radio device
+ 128 = /dev/swradio0 Software Defined Radio device
+ ...
+ 191 = /dev/swradio63 Software Defined Radio device
224 = /dev/vbi0 Vertical blank interrupt
...
255 = /dev/vbi31 Vertical blank interrupt
+ Minor numbers are allocated dynamically unless
+ CONFIG_VIDEO_FIXED_MINOR_RANGES (default n)
+ configuration option is set.
+
81 block I2O hard disk
0 = /dev/i2o/hdq 17th I2O hard disk, whole disk
16 = /dev/i2o/hdr 18th I2O hard disk, whole disk
diff --git a/Documentation/devicetree/bindings/arm/armada-375.txt b/Documentation/devicetree/bindings/arm/armada-375.txt
new file mode 100644
index 000000000000..867d0b80cb8f
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/armada-375.txt
@@ -0,0 +1,9 @@
+Marvell Armada 375 Platforms Device Tree Bindings
+-------------------------------------------------
+
+Boards with a SoC of the Marvell Armada 375 family shall have the
+following property:
+
+Required root node property:
+
+compatible: must contain "marvell,armada375"
diff --git a/Documentation/devicetree/bindings/arm/armada-38x.txt b/Documentation/devicetree/bindings/arm/armada-38x.txt
new file mode 100644
index 000000000000..11f2330a6554
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/armada-38x.txt
@@ -0,0 +1,10 @@
+Marvell Armada 38x Platforms Device Tree Bindings
+-------------------------------------------------
+
+Boards with a SoC of the Marvell Armada 38x family shall have the
+following property:
+
+Required root node property:
+
+ - compatible: must contain either "marvell,armada380" or
+ "marvell,armada385" depending on the variant of the SoC being used.
diff --git a/Documentation/devicetree/bindings/arm/bcm/bcm21664.txt b/Documentation/devicetree/bindings/arm/bcm/bcm21664.txt
new file mode 100644
index 000000000000..e0774255e1a6
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/bcm/bcm21664.txt
@@ -0,0 +1,15 @@
+Broadcom BCM21664 device tree bindings
+--------------------------------------
+
+This document describes the device tree bindings for boards with the BCM21664
+SoC.
+
+Required root node property:
+ - compatible: brcm,bcm21664
+
+Example:
+ / {
+ model = "BCM21664 SoC";
+ compatible = "brcm,bcm21664";
+ [...]
+ }
diff --git a/Documentation/devicetree/bindings/arm/bcm/kona-resetmgr.txt b/Documentation/devicetree/bindings/arm/bcm/kona-resetmgr.txt
new file mode 100644
index 000000000000..93f31ca1ef4b
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/bcm/kona-resetmgr.txt
@@ -0,0 +1,14 @@
+Broadcom Kona Family Reset Manager
+----------------------------------
+
+The reset manager is used on the Broadcom BCM21664 SoC.
+
+Required properties:
+ - compatible: brcm,bcm21664-resetmgr
+ - reg: memory address & range
+
+Example:
+ brcm,resetmgr@35001f00 {
+ compatible = "brcm,bcm21664-resetmgr";
+ reg = <0x35001f00 0x24>;
+ };
diff --git a/Documentation/devicetree/bindings/arm/bcm4708.txt b/Documentation/devicetree/bindings/arm/bcm4708.txt
new file mode 100644
index 000000000000..6b0f49f6f499
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/bcm4708.txt
@@ -0,0 +1,8 @@
+Broadcom BCM4708 device tree bindings
+-------------------------------------------
+
+Boards with the BCM4708 SoC shall have the following properties:
+
+Required root node property:
+
+compatible = "brcm,bcm4708";
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
index 91304353eea4..333f4aea3029 100644
--- a/Documentation/devicetree/bindings/arm/cpus.txt
+++ b/Documentation/devicetree/bindings/arm/cpus.txt
@@ -180,7 +180,11 @@ nodes to be present and contain the properties described below.
be one of:
"spin-table"
"psci"
- # On ARM 32-bit systems this property is optional.
+ # On ARM 32-bit systems this property is optional and
+ can be one of:
+ "qcom,gcc-msm8660"
+ "qcom,kpss-acc-v1"
+ "qcom,kpss-acc-v2"
- cpu-release-addr
Usage: required for systems that have an "enable-method"
@@ -191,6 +195,21 @@ nodes to be present and contain the properties described below.
property identifying a 64-bit zero-initialised
memory location.
+ - qcom,saw
+ Usage: required for systems that have an "enable-method"
+ property value of "qcom,kpss-acc-v1" or
+ "qcom,kpss-acc-v2"
+ Value type: <phandle>
+ Definition: Specifies the SAW[1] node associated with this CPU.
+
+ - qcom,acc
+ Usage: required for systems that have an "enable-method"
+ property value of "qcom,kpss-acc-v1" or
+ "qcom,kpss-acc-v2"
+ Value type: <phandle>
+ Definition: Specifies the ACC[2] node associated with this CPU.
+
+
Example 1 (dual-cluster big.LITTLE system 32-bit):
cpus {
@@ -382,3 +401,7 @@ cpus {
cpu-release-addr = <0 0x20000000>;
};
};
+
+--
+[1] arm/msm/qcom,saw2.txt
+[2] arm/msm/qcom,kpss-acc.txt
diff --git a/Documentation/devicetree/bindings/arm/gic.txt b/Documentation/devicetree/bindings/arm/gic.txt
index bae0d87a38b2..5573c08d3180 100644
--- a/Documentation/devicetree/bindings/arm/gic.txt
+++ b/Documentation/devicetree/bindings/arm/gic.txt
@@ -50,6 +50,11 @@ Optional
regions, used when the GIC doesn't have banked registers. The offset is
cpu-offset * cpu-nr.
+- arm,routable-irqs : Total number of gic irq inputs which are not directly
+ connected from the peripherals, but are routed dynamically
+ by a crossbar/multiplexer preceding the GIC. The GIC irq
+ input line is assigned dynamically when the corresponding
+ peripheral's crossbar line is mapped.
Example:
intc: interrupt-controller@fff11000 {
@@ -57,6 +62,7 @@ Example:
#interrupt-cells = <3>;
#address-cells = <1>;
interrupt-controller;
+ arm,routable-irqs = <160>;
reg = <0xfff11000 0x1000>,
<0xfff10100 0x100>;
};
diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
index 8c7a4653508d..df0a452b8526 100644
--- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
+++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
@@ -30,3 +30,17 @@ Example:
resume-offset = <0x308>;
reboot-offset = <0x4>;
};
+
+PCTRL: Peripheral misc control register
+
+Required Properties:
+- compatible: "hisilicon,pctrl"
+- reg: Address and size of pctrl.
+
+Example:
+
+ /* for Hi3620 */
+ pctrl: pctrl@fca09000 {
+ compatible = "hisilicon,pctrl";
+ reg = <0xfca09000 0x1000>;
+ };
diff --git a/Documentation/devicetree/bindings/arm/keystone/keystone.txt b/Documentation/devicetree/bindings/arm/keystone/keystone.txt
index 63c0e6ae5cf7..59d7a46f85eb 100644
--- a/Documentation/devicetree/bindings/arm/keystone/keystone.txt
+++ b/Documentation/devicetree/bindings/arm/keystone/keystone.txt
@@ -8,3 +8,13 @@ Required properties:
- compatible: All TI specific devices present in Keystone SOC should be in
the form "ti,keystone-*". Generic devices like gic, arch_timers, ns16550
type UART should use the specified compatible for those devices.
+
+Boards:
+- Keystone 2 Hawking/Kepler EVM
+ compatible = "ti,k2hk-evm","ti,keystone"
+
+- Keystone 2 Lamarr EVM
+ compatible = "ti,k2l-evm","ti,keystone"
+
+- Keystone 2 Edison EVM
+ compatible = "ti,k2e-evm","ti,keystone"
diff --git a/Documentation/devicetree/bindings/arm/mrvl/feroceon.txt b/Documentation/devicetree/bindings/arm/mrvl/feroceon.txt
new file mode 100644
index 000000000000..0d244b999d10
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mrvl/feroceon.txt
@@ -0,0 +1,16 @@
+* Marvell Feroceon Cache
+
+Required properties:
+- compatible : Should be either "marvell,feroceon-cache" or
+ "marvell,kirkwood-cache".
+
+Optional properties:
+- reg : Address of the L2 cache control register. Mandatory for
+ "marvell,kirkwood-cache", not used by "marvell,feroceon-cache"
+
+
+Example:
+ l2: l2-cache@20128 {
+ compatible = "marvell,kirkwood-cache";
+ reg = <0x20128 0x4>;
+ };
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
new file mode 100644
index 000000000000..1333db9acfee
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
@@ -0,0 +1,30 @@
+Krait Processor Sub-system (KPSS) Application Clock Controller (ACC)
+
+The KPSS ACC provides clock, power domain, and reset control to a Krait CPU.
+There is one ACC register region per CPU within the KPSS remapped region as
+well as an alias register region that remaps accesses to the ACC associated
+with the CPU accessing the region.
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: should be one of:
+ "qcom,kpss-acc-v1"
+ "qcom,kpss-acc-v2"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: the first element specifies the base address and size of
+ the register region. An optional second element specifies
+ the base address and size of the alias register region.
+
+Example:
+
+ clock-controller@2088000 {
+ compatible = "qcom,kpss-acc-v2";
+ reg = <0x02088000 0x1000>,
+ <0x02008000 0x1000>;
+ };
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt
new file mode 100644
index 000000000000..1505fb8e131a
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt
@@ -0,0 +1,35 @@
+SPM AVS Wrapper 2 (SAW2)
+
+The SAW2 is a wrapper around the Subsystem Power Manager (SPM) and the
+Adaptive Voltage Scaling (AVS) hardware. The SPM is a programmable
+micro-controller that transitions a piece of hardware (like a processor or
+subsystem) into and out of low power modes via a direct connection to
+the PMIC. It can also be wired up to interact with other processors in the
+system, notifying them when a low power state is entered or exited.
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: shall contain "qcom,saw2". A more specific value should be
+ one of:
+ "qcom,saw2-v1"
+ "qcom,saw2-v1.1"
+ "qcom,saw2-v2"
+ "qcom,saw2-v2.1"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: the first element specifies the base address and size of
+ the register region. An optional second element specifies
+ the base address and size of the alias register region.
+
+
+Example:
+
+ regulator@2099000 {
+ compatible = "qcom,saw2";
+ reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
+ };
diff --git a/Documentation/devicetree/bindings/arm/mvebu-system-controller.txt b/Documentation/devicetree/bindings/arm/mvebu-system-controller.txt
index 081c6a786c8a..d24ab2ebf8a7 100644
--- a/Documentation/devicetree/bindings/arm/mvebu-system-controller.txt
+++ b/Documentation/devicetree/bindings/arm/mvebu-system-controller.txt
@@ -1,12 +1,13 @@
MVEBU System Controller
-----------------------
-MVEBU (Marvell SOCs: Armada 370/XP, Dove, mv78xx0, Kirkwood, Orion5x)
+MVEBU (Marvell SOCs: Armada 370/375/XP, Dove, mv78xx0, Kirkwood, Orion5x)
Required properties:
- compatible: one of:
- "marvell,orion-system-controller"
- "marvell,armada-370-xp-system-controller"
+ - "marvell,armada-375-system-controller"
- reg: Should contain system controller registers location and length.
Example:
diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt
new file mode 100644
index 000000000000..fb88585cfb93
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt
@@ -0,0 +1,27 @@
+Some socs have a large number of interrupts requests to service
+the needs of its many peripherals and subsystems. All of the
+interrupt lines from the subsystems are not needed at the same
+time, so they have to be muxed to the irq-controller appropriately.
+In such places a interrupt controllers are preceded by an CROSSBAR
+that provides flexibility in muxing the device requests to the controller
+inputs.
+
+Required properties:
+- compatible : Should be "ti,irq-crossbar"
+- reg: Base address and the size of the crossbar registers.
+- ti,max-irqs: Total number of irqs available at the interrupt controller.
+- ti,reg-size: Size of a individual register in bytes. Every individual
+ register is assumed to be of same size. Valid sizes are 1, 2, 4.
+- ti,irqs-reserved: List of the reserved irq lines that are not muxed using
+ crossbar. These interrupt lines are reserved in the soc,
+ so crossbar bar driver should not consider them as free
+ lines.
+
+Examples:
+ crossbar_mpu: @4a020000 {
+ compatible = "ti,irq-crossbar";
+ reg = <0x4a002a48 0x130>;
+ ti,max-irqs = <160>;
+ ti,reg-size = <2>;
+ ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>;
+ };
diff --git a/Documentation/devicetree/bindings/arm/omap/dmm.txt b/Documentation/devicetree/bindings/arm/omap/dmm.txt
new file mode 100644
index 000000000000..8bd6d0a238a8
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/omap/dmm.txt
@@ -0,0 +1,22 @@
+OMAP Dynamic Memory Manager (DMM) bindings
+
+The dynamic memory manager (DMM) is a module located immediately in front of the
+SDRAM controllers (called EMIFs on OMAP). DMM manages various aspects of memory
+accesses such as priority generation amongst initiators, configuration of SDRAM
+interleaving, optimizing transfer of 2D block objects, and provide MMU-like page
+translation for initiators which need contiguous dma bus addresses.
+
+Required properties:
+- compatible: Should contain "ti,omap4-dmm" for OMAP4 family
+ Should contain "ti,omap5-dmm" for OMAP5 and DRA7x family
+- reg: Contains DMM register address range (base address and length)
+- interrupts: Should contain an interrupt-specifier for DMM_IRQ.
+- ti,hwmods: Name of the hwmod associated to DMM, which is typically "dmm"
+
+Example:
+
+dmm@4e000000 {
+ compatible = "ti,omap4-dmm";
+ reg = <0x4e000000 0x800>;
+ ti,hwmods = "dmm";
+};
diff --git a/Documentation/devicetree/bindings/arm/omap/omap.txt b/Documentation/devicetree/bindings/arm/omap/omap.txt
index af9b4a0d902b..36ede19a1630 100644
--- a/Documentation/devicetree/bindings/arm/omap/omap.txt
+++ b/Documentation/devicetree/bindings/arm/omap/omap.txt
@@ -99,6 +99,9 @@ Boards:
- OMAP4 PandaBoard : Low cost community board
compatible = "ti,omap4-panda", "ti,omap4430"
+- OMAP4 DuoVero with Parlor : Commercial expansion board with daughter board
+ compatible = "gumstix,omap4-duovero-parlor", "gumstix,omap4-duovero", "ti,omap4430", "ti,omap4";
+
- OMAP3 EVM : Software Development Board for OMAP35x, AM/DM37x
compatible = "ti,omap3-evm", "ti,omap3"
@@ -114,5 +117,8 @@ Boards:
- AM43x EPOS EVM
compatible = "ti,am43x-epos-evm", "ti,am4372", "ti,am43"
+- AM437x GP EVM
+ compatible = "ti,am437x-gp-evm", "ti,am4372", "ti,am43"
+
- DRA7 EVM: Software Developement Board for DRA7XX
compatible = "ti,dra7-evm", "ti,dra7"
diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt
index 3e1e498fea96..fe5cef8976cb 100644
--- a/Documentation/devicetree/bindings/arm/pmu.txt
+++ b/Documentation/devicetree/bindings/arm/pmu.txt
@@ -9,6 +9,7 @@ Required properties:
- compatible : should be one of
"arm,armv8-pmuv3"
"arm,cortex-a15-pmu"
+ "arm,cortex-a12-pmu"
"arm,cortex-a9-pmu"
"arm,cortex-a8-pmu"
"arm,cortex-a7-pmu"
@@ -16,7 +17,14 @@ Required properties:
"arm,arm11mpcore-pmu"
"arm,arm1176-pmu"
"arm,arm1136-pmu"
-- interrupts : 1 combined interrupt or 1 per core.
+ "qcom,krait-pmu"
+- interrupts : 1 combined interrupt or 1 per core. If the interrupt is a per-cpu
+ interrupt (PPI) then 1 interrupt should be specified.
+
+Optional properties:
+
+- qcom,no-pc-write : Indicates that this PMU doesn't support the 0xc and 0xd
+ events.
Example:
diff --git a/Documentation/devicetree/bindings/arm/rockchip/pmu.txt b/Documentation/devicetree/bindings/arm/rockchip/pmu.txt
new file mode 100644
index 000000000000..3ee9b428b2f7
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/rockchip/pmu.txt
@@ -0,0 +1,16 @@
+Rockchip power-management-unit:
+-------------------------------
+
+The pmu is used to turn off and on different power domains of the SoCs
+This includes the power to the CPU cores.
+
+Required node properties:
+- compatible value : = "rockchip,rk3066-pmu";
+- reg : physical base address and the size of the registers window
+
+Example:
+
+ pmu@20004000 {
+ compatible = "rockchip,rk3066-pmu";
+ reg = <0x20004000 0x100>;
+ };
diff --git a/Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt b/Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt
new file mode 100644
index 000000000000..d9416fb8db6f
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt
@@ -0,0 +1,30 @@
+Rockchip SRAM for smp bringup:
+------------------------------
+
+Rockchip's smp-capable SoCs use the first part of the sram for the bringup
+of the cores. Once the core gets powered up it executes the code that is
+residing at the very beginning of the sram.
+
+Therefore a reserved section sub-node has to be added to the mmio-sram
+declaration.
+
+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
+
+Example:
+
+ sram: sram@10080000 {
+ compatible = "mmio-sram";
+ reg = <0x10080000 0x10000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ smp-sram@10080000 {
+ compatible = "rockchip,rk3066-smp-sram";
+ reg = <0x10080000 0x50>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.txt b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
new file mode 100644
index 000000000000..f1f155255f28
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
@@ -0,0 +1,15 @@
+SAMSUNG Exynos SoC series PMU Registers
+
+Properties:
+ - compatible : should contain two values. First value must be one from following list:
+ - "samsung,exynos5250-pmu" - for Exynos5250 SoC,
+ - "samsung,exynos5420-pmu" - for Exynos5420 SoC.
+ second value must be always "syscon".
+
+ - reg : offset and length of the register set.
+
+Example :
+pmu_system_controller: system-controller@10040000 {
+ compatible = "samsung,exynos5250-pmu", "syscon";
+ reg = <0x10040000 0x5000>;
+};
diff --git a/Documentation/devicetree/bindings/ata/exynos-sata-phy.txt b/Documentation/devicetree/bindings/ata/exynos-sata-phy.txt
deleted file mode 100644
index 37824fac688e..000000000000
--- a/Documentation/devicetree/bindings/ata/exynos-sata-phy.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-* Samsung SATA PHY Controller
-
-SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
-Each SATA PHY controller should have its own node.
-
-Required properties:
-- compatible : compatible list, contains "samsung,exynos5-sata-phy"
-- reg : <registers mapping>
-
-Example:
- sata@ffe07000 {
- compatible = "samsung,exynos5-sata-phy";
- reg = <0xffe07000 0x1000>;
- };
diff --git a/Documentation/devicetree/bindings/ata/exynos-sata.txt b/Documentation/devicetree/bindings/ata/exynos-sata.txt
index 0849f1025e34..cb48448247ea 100644
--- a/Documentation/devicetree/bindings/ata/exynos-sata.txt
+++ b/Documentation/devicetree/bindings/ata/exynos-sata.txt
@@ -4,14 +4,27 @@ SATA nodes are defined to describe on-chip Serial ATA controllers.
Each SATA controller should have its own node.
Required properties:
-- compatible : compatible list, contains "samsung,exynos5-sata"
-- interrupts : <interrupt mapping for SATA IRQ>
-- reg : <registers mapping>
-- samsung,sata-freq : <frequency in MHz>
+- compatible : compatible list, contains "samsung,exynos5-sata"
+- interrupts : <interrupt mapping for SATA IRQ>
+- reg : <registers mapping>
+- samsung,sata-freq : <frequency in MHz>
+- phys : Must contain exactly one entry as specified
+ in phy-bindings.txt
+- phy-names : Must be "sata-phy"
+
+Optional properties:
+- clocks : Must contain an entry for each entry in clock-names.
+- clock-names : Shall be "sata" for the external SATA bus clock,
+ and "sclk_sata" for the internal controller clock.
Example:
- sata@ffe08000 {
- compatible = "samsung,exynos5-sata";
- reg = <0xffe08000 0x1000>;
- interrupts = <115>;
- };
+ sata@122f0000 {
+ compatible = "snps,dwc-ahci";
+ samsung,sata-freq = <66>;
+ reg = <0x122f0000 0x1ff>;
+ interrupts = <0 115 0>;
+ clocks = <&clock 277>, <&clock 143>;
+ clock-names = "sata", "sclk_sata";
+ phys = <&sata_phy>;
+ phy-names = "sata-phy";
+ };
diff --git a/Documentation/devicetree/bindings/bus/imx-weim.txt b/Documentation/devicetree/bindings/bus/imx-weim.txt
index 0fd76c405208..6630d842c7a3 100644
--- a/Documentation/devicetree/bindings/bus/imx-weim.txt
+++ b/Documentation/devicetree/bindings/bus/imx-weim.txt
@@ -8,7 +8,12 @@ The actual devices are instantiated from the child nodes of a WEIM node.
Required properties:
- - compatible: Should be set to "fsl,<soc>-weim"
+ - compatible: Should contain one of the following:
+ "fsl,imx1-weim"
+ "fsl,imx27-weim"
+ "fsl,imx51-weim"
+ "fsl,imx50-weim"
+ "fsl,imx6q-weim"
- reg: A resource specifier for the register space
(see the example below)
- clocks: the clock, see the example below.
@@ -19,6 +24,26 @@ Required properties:
<cs-number> 0 <physical address of mapping> <size>
+Optional properties:
+
+ - fsl,weim-cs-gpr: For "fsl,imx50-weim" and "fsl,imx6q-weim" type of
+ devices, it should be the phandle to the system General
+ Purpose Register controller that contains WEIM CS GPR
+ register, e.g. IOMUXC_GPR1 on i.MX6Q. IOMUXC_GPR1[11:0]
+ should be set up as one of the following 4 possible
+ values depending on the CS space configuration.
+
+ IOMUXC_GPR1[11:0] CS0 CS1 CS2 CS3
+ ---------------------------------------------
+ 05 128M 0M 0M 0M
+ 033 64M 64M 0M 0M
+ 0113 64M 32M 32M 0M
+ 01111 32M 32M 32M 32M
+
+ In case that the property is absent, the reset value or
+ what bootloader sets up in IOMUXC_GPR1[11:0] will be
+ used.
+
Timing property for child nodes. It is mandatory, not optional.
- fsl,weim-cs-timing: The timing array, contains timing values for the
@@ -43,6 +68,7 @@ Example for an imx6q-sabreauto board, the NOR flash connected to the WEIM:
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x08000000 0x08000000>;
+ fsl,weim-cs-gpr = <&gpr>;
nor@0,0 {
compatible = "cfi-flash";
diff --git a/Documentation/devicetree/bindings/clock/altr_socfpga.txt b/Documentation/devicetree/bindings/clock/altr_socfpga.txt
index 0045433eae1f..5dfd145d3ccf 100644
--- a/Documentation/devicetree/bindings/clock/altr_socfpga.txt
+++ b/Documentation/devicetree/bindings/clock/altr_socfpga.txt
@@ -23,3 +23,8 @@ Optional properties:
and the bit index.
- div-reg : For "socfpga-gate-clk", div-reg contains the divider register, bit shift,
and width.
+- clk-phase : For the sdmmc_clk, contains the value of the clock phase that controls
+ the SDMMC CIU clock. The first value is the clk_sample(smpsel), and the second
+ value is the cclk_in_drv(drvsel). The clk-phase is used to enable the correct
+ hold/delay times that is needed for the SD/MMC CIU clock. The values of both
+ can be 0-315 degrees, in 45 degree increments.
diff --git a/Documentation/devicetree/bindings/clock/arm-integrator.txt b/Documentation/devicetree/bindings/clock/arm-integrator.txt
new file mode 100644
index 000000000000..652914b17b95
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/arm-integrator.txt
@@ -0,0 +1,34 @@
+Clock bindings for ARM Integrator Core Module clocks
+
+Auxilary Oscillator Clock
+
+This is a configurable clock fed from a 24 MHz chrystal,
+used for generating e.g. video clocks. It is located on the
+core module and there is only one of these.
+
+This clock node *must* be a subnode of the core module, since
+it obtains the base address for it's address range from its
+parent node.
+
+
+Required properties:
+- compatible: must be "arm,integrator-cm-auxosc"
+- #clock-cells: must be <0>
+
+Optional properties:
+- clocks: parent clock(s)
+
+Example:
+
+core-module@10000000 {
+ xtal24mhz: xtal24mhz@24M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ };
+ auxosc: cm_aux_osc@25M {
+ #clock-cells = <0>;
+ compatible = "arm,integrator-cm-auxosc";
+ clocks = <&xtal24mhz>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/clock/axi-clkgen.txt b/Documentation/devicetree/bindings/clock/axi-clkgen.txt
index 028b493e97ff..20e1704e7df2 100644
--- a/Documentation/devicetree/bindings/clock/axi-clkgen.txt
+++ b/Documentation/devicetree/bindings/clock/axi-clkgen.txt
@@ -5,7 +5,7 @@ This binding uses the common clock binding[1].
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
Required properties:
-- compatible : shall be "adi,axi-clkgen".
+- compatible : shall be "adi,axi-clkgen-1.00.a" or "adi,axi-clkgen-2.00.a".
- #clock-cells : from common clock binding; Should always be set to 0.
- reg : Address and length of the axi-clkgen register set.
- clocks : Phandle and clock specifier for the parent clock.
diff --git a/Documentation/devicetree/bindings/clock/clock-bindings.txt b/Documentation/devicetree/bindings/clock/clock-bindings.txt
index 7c52c29d99fa..700e7aac3717 100644
--- a/Documentation/devicetree/bindings/clock/clock-bindings.txt
+++ b/Documentation/devicetree/bindings/clock/clock-bindings.txt
@@ -44,6 +44,23 @@ For example:
clocks by index. The names should reflect the clock output signal
names for the device.
+clock-indices: If the identifyng number for the clocks in the node
+ is not linear from zero, then the this mapping allows
+ the mapping of identifiers into the clock-output-names
+ array.
+
+For example, if we have two clocks <&oscillator 1> and <&oscillator 3>:
+
+ oscillator {
+ compatible = "myclocktype";
+ #clock-cells = <1>;
+ clock-indices = <1>, <3>;
+ clock-output-names = "clka", "clkb";
+ }
+
+ This ensures we do not have any empty nodes in clock-output-names
+
+
==Clock consumers==
Required properties:
diff --git a/Documentation/devicetree/bindings/clock/exynos4-clock.txt b/Documentation/devicetree/bindings/clock/exynos4-clock.txt
index a2ac2d9ac71a..f5a5b19ed3b2 100644
--- a/Documentation/devicetree/bindings/clock/exynos4-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos4-clock.txt
@@ -15,259 +15,12 @@ Required Properties:
- #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. Some of the clocks are available only on a particular
-Exynos4 SoC and this is specified where applicable.
-
-
- [Core Clocks]
-
- Clock ID SoC (if specific)
- -----------------------------------------------
-
- xxti 1
- xusbxti 2
- fin_pll 3
- fout_apll 4
- fout_mpll 5
- fout_epll 6
- fout_vpll 7
- sclk_apll 8
- sclk_mpll 9
- sclk_epll 10
- sclk_vpll 11
- arm_clk 12
- aclk200 13
- aclk100 14
- aclk160 15
- aclk133 16
- mout_mpll_user_t 17 Exynos4x12
- mout_mpll_user_c 18 Exynos4x12
- mout_core 19
- mout_apll 20
-
-
- [Clock Gate for Special Clocks]
-
- Clock ID SoC (if specific)
- -----------------------------------------------
-
- sclk_fimc0 128
- sclk_fimc1 129
- sclk_fimc2 130
- sclk_fimc3 131
- sclk_cam0 132
- sclk_cam1 133
- sclk_csis0 134
- sclk_csis1 135
- sclk_hdmi 136
- sclk_mixer 137
- sclk_dac 138
- sclk_pixel 139
- sclk_fimd0 140
- sclk_mdnie0 141 Exynos4412
- sclk_mdnie_pwm0 12 142 Exynos4412
- sclk_mipi0 143
- sclk_audio0 144
- sclk_mmc0 145
- sclk_mmc1 146
- sclk_mmc2 147
- sclk_mmc3 148
- sclk_mmc4 149
- sclk_sata 150 Exynos4210
- sclk_uart0 151
- sclk_uart1 152
- sclk_uart2 153
- sclk_uart3 154
- sclk_uart4 155
- sclk_audio1 156
- sclk_audio2 157
- sclk_spdif 158
- sclk_spi0 159
- sclk_spi1 160
- sclk_spi2 161
- sclk_slimbus 162
- sclk_fimd1 163 Exynos4210
- sclk_mipi1 164 Exynos4210
- sclk_pcm1 165
- sclk_pcm2 166
- sclk_i2s1 167
- sclk_i2s2 168
- sclk_mipihsi 169 Exynos4412
- sclk_mfc 170
- sclk_pcm0 171
- sclk_g3d 172
- sclk_pwm_isp 173 Exynos4x12
- sclk_spi0_isp 174 Exynos4x12
- sclk_spi1_isp 175 Exynos4x12
- sclk_uart_isp 176 Exynos4x12
- sclk_fimg2d 177
-
- [Peripheral Clock Gates]
-
- Clock ID SoC (if specific)
- -----------------------------------------------
-
- fimc0 256
- fimc1 257
- fimc2 258
- fimc3 259
- csis0 260
- csis1 261
- jpeg 262
- smmu_fimc0 263
- smmu_fimc1 264
- smmu_fimc2 265
- smmu_fimc3 266
- smmu_jpeg 267
- vp 268
- mixer 269
- tvenc 270 Exynos4210
- hdmi 271
- smmu_tv 272
- mfc 273
- smmu_mfcl 274
- smmu_mfcr 275
- g3d 276
- g2d 277
- rotator 278 Exynos4210
- mdma 279 Exynos4210
- smmu_g2d 280 Exynos4210
- smmu_rotator 281 Exynos4210
- smmu_mdma 282 Exynos4210
- fimd0 283
- mie0 284
- mdnie0 285 Exynos4412
- dsim0 286
- smmu_fimd0 287
- fimd1 288 Exynos4210
- mie1 289 Exynos4210
- dsim1 290 Exynos4210
- smmu_fimd1 291 Exynos4210
- pdma0 292
- pdma1 293
- pcie_phy 294
- sata_phy 295 Exynos4210
- tsi 296
- sdmmc0 297
- sdmmc1 298
- sdmmc2 299
- sdmmc3 300
- sdmmc4 301
- sata 302 Exynos4210
- sromc 303
- usb_host 304
- usb_device 305
- pcie 306
- onenand 307
- nfcon 308
- smmu_pcie 309
- gps 310
- smmu_gps 311
- uart0 312
- uart1 313
- uart2 314
- uart3 315
- uart4 316
- i2c0 317
- i2c1 318
- i2c2 319
- i2c3 320
- i2c4 321
- i2c5 322
- i2c6 323
- i2c7 324
- i2c_hdmi 325
- tsadc 326
- spi0 327
- spi1 328
- spi2 329
- i2s1 330
- i2s2 331
- pcm0 332
- i2s0 333
- pcm1 334
- pcm2 335
- pwm 336
- slimbus 337
- spdif 338
- ac97 339
- modemif 340
- chipid 341
- sysreg 342
- hdmi_cec 343
- mct 344
- wdt 345
- rtc 346
- keyif 347
- audss 348
- mipi_hsi 349 Exynos4210
- mdma2 350 Exynos4210
- pixelasyncm0 351
- pixelasyncm1 352
- fimc_lite0 353 Exynos4x12
- fimc_lite1 354 Exynos4x12
- ppmuispx 355 Exynos4x12
- ppmuispmx 356 Exynos4x12
- fimc_isp 357 Exynos4x12
- fimc_drc 358 Exynos4x12
- fimc_fd 359 Exynos4x12
- mcuisp 360 Exynos4x12
- gicisp 361 Exynos4x12
- smmu_isp 362 Exynos4x12
- smmu_drc 363 Exynos4x12
- smmu_fd 364 Exynos4x12
- smmu_lite0 365 Exynos4x12
- smmu_lite1 366 Exynos4x12
- mcuctl_isp 367 Exynos4x12
- mpwm_isp 368 Exynos4x12
- i2c0_isp 369 Exynos4x12
- i2c1_isp 370 Exynos4x12
- mtcadc_isp 371 Exynos4x12
- pwm_isp 372 Exynos4x12
- wdt_isp 373 Exynos4x12
- uart_isp 374 Exynos4x12
- asyncaxim 375 Exynos4x12
- smmu_ispcx 376 Exynos4x12
- spi0_isp 377 Exynos4x12
- spi1_isp 378 Exynos4x12
- pwm_isp_sclk 379 Exynos4x12
- spi0_isp_sclk 380 Exynos4x12
- spi1_isp_sclk 381 Exynos4x12
- uart_isp_sclk 382 Exynos4x12
- tmu_apbif 383
-
- [Mux Clocks]
-
- Clock ID SoC (if specific)
- -----------------------------------------------
-
- mout_fimc0 384
- mout_fimc1 385
- mout_fimc2 386
- mout_fimc3 387
- mout_cam0 388
- mout_cam1 389
- mout_csis0 390
- mout_csis1 391
- mout_g3d0 392
- mout_g3d1 393
- mout_g3d 394
- aclk400_mcuisp 395 Exynos4x12
-
- [Div Clocks]
-
- Clock ID SoC (if specific)
- -----------------------------------------------
-
- div_isp0 450 Exynos4x12
- div_isp1 451 Exynos4x12
- div_mcuisp0 452 Exynos4x12
- div_mcuisp1 453 Exynos4x12
- div_aclk200 454 Exynos4x12
- div_aclk400_mcuisp 455 Exynos4x12
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume.
+All available clocks are defined as preprocessor macros in
+dt-bindings/clock/exynos4.h header and can be used in device
+tree sources.
Example 1: An example of a clock controller node is listed below.
@@ -285,6 +38,6 @@ Example 2: UART controller node that consumes the clock generated by the clock
compatible = "samsung,exynos4210-uart";
reg = <0x13820000 0x100>;
interrupts = <0 54 0>;
- clocks = <&clock 314>, <&clock 153>;
+ clocks = <&clock CLK_UART2>, <&clock CLK_SCLK_UART2>;
clock-names = "uart", "clk_uart_baud0";
};
diff --git a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
index 72ce617dea82..536eacd1063f 100644
--- a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
@@ -13,163 +13,12 @@ Required Properties:
- #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.
-
-
- [Core Clocks]
-
- Clock ID
- ----------------------------
-
- fin_pll 1
-
- [Clock Gate for Special Clocks]
-
- Clock ID
- ----------------------------
-
- sclk_cam_bayer 128
- sclk_cam0 129
- sclk_cam1 130
- sclk_gscl_wa 131
- sclk_gscl_wb 132
- sclk_fimd1 133
- sclk_mipi1 134
- sclk_dp 135
- sclk_hdmi 136
- sclk_pixel 137
- sclk_audio0 138
- sclk_mmc0 139
- sclk_mmc1 140
- sclk_mmc2 141
- sclk_mmc3 142
- sclk_sata 143
- sclk_usb3 144
- sclk_jpeg 145
- sclk_uart0 146
- sclk_uart1 147
- sclk_uart2 148
- sclk_uart3 149
- sclk_pwm 150
- sclk_audio1 151
- sclk_audio2 152
- sclk_spdif 153
- sclk_spi0 154
- sclk_spi1 155
- sclk_spi2 156
- div_i2s1 157
- div_i2s2 158
- sclk_hdmiphy 159
- div_pcm0 160
-
-
- [Peripheral Clock Gates]
-
- Clock ID
- ----------------------------
-
- gscl0 256
- gscl1 257
- gscl2 258
- gscl3 259
- gscl_wa 260
- gscl_wb 261
- smmu_gscl0 262
- smmu_gscl1 263
- smmu_gscl2 264
- smmu_gscl3 265
- mfc 266
- smmu_mfcl 267
- smmu_mfcr 268
- rotator 269
- jpeg 270
- mdma1 271
- smmu_rotator 272
- smmu_jpeg 273
- smmu_mdma1 274
- pdma0 275
- pdma1 276
- sata 277
- usbotg 278
- mipi_hsi 279
- sdmmc0 280
- sdmmc1 281
- sdmmc2 282
- sdmmc3 283
- sromc 284
- usb2 285
- usb3 286
- sata_phyctrl 287
- sata_phyi2c 288
- uart0 289
- uart1 290
- uart2 291
- uart3 292
- uart4 293
- i2c0 294
- i2c1 295
- i2c2 296
- i2c3 297
- i2c4 298
- i2c5 299
- i2c6 300
- i2c7 301
- i2c_hdmi 302
- adc 303
- spi0 304
- spi1 305
- spi2 306
- i2s1 307
- i2s2 308
- pcm1 309
- pcm2 310
- pwm 311
- spdif 312
- ac97 313
- hsi2c0 314
- hsi2c1 315
- hs12c2 316
- hs12c3 317
- chipid 318
- sysreg 319
- pmu 320
- cmu_top 321
- cmu_core 322
- cmu_mem 323
- tzpc0 324
- tzpc1 325
- tzpc2 326
- tzpc3 327
- tzpc4 328
- tzpc5 329
- tzpc6 330
- tzpc7 331
- tzpc8 332
- tzpc9 333
- hdmi_cec 334
- mct 335
- wdt 336
- rtc 337
- tmu 338
- fimd1 339
- mie1 340
- dsim0 341
- dp 342
- mixer 343
- hdmi 344
- g2d 345
- mdma0 346
- smmu_mdma0 347
-
-
- [Clock Muxes]
-
- Clock ID
- ----------------------------
- mout_hdmi 1024
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume.
+All available clocks are defined as preprocessor macros in
+dt-bindings/clock/exynos5250.h header and can be used in device
+tree sources.
Example 1: An example of a clock controller node is listed below.
@@ -187,6 +36,6 @@ Example 2: UART controller node that consumes the clock generated by the clock
compatible = "samsung,exynos4210-uart";
reg = <0x13820000 0x100>;
interrupts = <0 54 0>;
- clocks = <&clock 314>, <&clock 153>;
+ clocks = <&clock CLK_UART2>, <&clock CLK_SCLK_UART2>;
clock-names = "uart", "clk_uart_baud0";
};
diff --git a/Documentation/devicetree/bindings/clock/exynos5420-clock.txt b/Documentation/devicetree/bindings/clock/exynos5420-clock.txt
index 458f34789e5d..ca88c97a8562 100644
--- a/Documentation/devicetree/bindings/clock/exynos5420-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos5420-clock.txt
@@ -13,184 +13,12 @@ Required Properties:
- #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.
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume.
-
- [Core Clocks]
-
- Clock ID
- ----------------------------
-
- fin_pll 1
-
- [Clock Gate for Special Clocks]
-
- Clock ID
- ----------------------------
- sclk_uart0 128
- sclk_uart1 129
- sclk_uart2 130
- sclk_uart3 131
- sclk_mmc0 132
- sclk_mmc1 133
- sclk_mmc2 134
- sclk_spi0 135
- sclk_spi1 136
- sclk_spi2 137
- sclk_i2s1 138
- sclk_i2s2 139
- sclk_pcm1 140
- sclk_pcm2 141
- sclk_spdif 142
- sclk_hdmi 143
- sclk_pixel 144
- sclk_dp1 145
- sclk_mipi1 146
- sclk_fimd1 147
- sclk_maudio0 148
- sclk_maupcm0 149
- sclk_usbd300 150
- sclk_usbd301 151
- sclk_usbphy300 152
- sclk_usbphy301 153
- sclk_unipro 154
- sclk_pwm 155
- sclk_gscl_wa 156
- sclk_gscl_wb 157
- sclk_hdmiphy 158
-
- [Peripheral Clock Gates]
-
- Clock ID
- ----------------------------
-
- aclk66_peric 256
- uart0 257
- uart1 258
- uart2 259
- uart3 260
- i2c0 261
- i2c1 262
- i2c2 263
- i2c3 264
- i2c4 265
- i2c5 266
- i2c6 267
- i2c7 268
- i2c_hdmi 269
- tsadc 270
- spi0 271
- spi1 272
- spi2 273
- keyif 274
- i2s1 275
- i2s2 276
- pcm1 277
- pcm2 278
- pwm 279
- spdif 280
- i2c8 281
- i2c9 282
- i2c10 283
- aclk66_psgen 300
- chipid 301
- sysreg 302
- tzpc0 303
- tzpc1 304
- tzpc2 305
- tzpc3 306
- tzpc4 307
- tzpc5 308
- tzpc6 309
- tzpc7 310
- tzpc8 311
- tzpc9 312
- hdmi_cec 313
- seckey 314
- mct 315
- wdt 316
- rtc 317
- tmu 318
- tmu_gpu 319
- pclk66_gpio 330
- aclk200_fsys2 350
- mmc0 351
- mmc1 352
- mmc2 353
- sromc 354
- ufs 355
- aclk200_fsys 360
- tsi 361
- pdma0 362
- pdma1 363
- rtic 364
- usbh20 365
- usbd300 366
- usbd301 377
- aclk400_mscl 380
- mscl0 381
- mscl1 382
- mscl2 383
- smmu_mscl0 384
- smmu_mscl1 385
- smmu_mscl2 386
- aclk333 400
- mfc 401
- smmu_mfcl 402
- smmu_mfcr 403
- aclk200_disp1 410
- dsim1 411
- dp1 412
- hdmi 413
- aclk300_disp1 420
- fimd1 421
- smmu_fimd1 422
- aclk166 430
- mixer 431
- aclk266 440
- rotator 441
- mdma1 442
- smmu_rotator 443
- smmu_mdma1 444
- aclk300_jpeg 450
- jpeg 451
- jpeg2 452
- smmu_jpeg 453
- aclk300_gscl 460
- smmu_gscl0 461
- smmu_gscl1 462
- gscl_wa 463
- gscl_wb 464
- gscl0 465
- gscl1 466
- clk_3aa 467
- aclk266_g2d 470
- sss 471
- slim_sss 472
- mdma0 473
- aclk333_g2d 480
- g2d 481
- aclk333_432_gscl 490
- smmu_3aa 491
- smmu_fimcl0 492
- smmu_fimcl1 493
- smmu_fimcl3 494
- fimc_lite3 495
- aclk_g3d 500
- g3d 501
- smmu_mixer 502
-
- Mux ID
- ----------------------------
-
- mout_hdmi 640
-
- Divider ID
- ----------------------------
-
- dout_pixel 768
+All available clocks are defined as preprocessor macros in
+dt-bindings/clock/exynos5420.h header and can be used in device
+tree sources.
Example 1: An example of a clock controller node is listed below.
@@ -208,6 +36,6 @@ Example 2: UART controller node that consumes the clock generated by the clock
compatible = "samsung,exynos4210-uart";
reg = <0x13820000 0x100>;
interrupts = <0 54 0>;
- clocks = <&clock 259>, <&clock 130>;
+ clocks = <&clock CLK_UART2>, <&clock CLK_SCLK_UART2>;
clock-names = "uart", "clk_uart_baud0";
};
diff --git a/Documentation/devicetree/bindings/clock/exynos5440-clock.txt b/Documentation/devicetree/bindings/clock/exynos5440-clock.txt
index 9955dc9c7d96..5f7005f73058 100644
--- a/Documentation/devicetree/bindings/clock/exynos5440-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos5440-clock.txt
@@ -12,45 +12,12 @@ Required Properties:
- #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.
-
-
- [Core Clocks]
-
- Clock ID
- ----------------------------
-
- xtal 1
- arm_clk 2
-
- [Peripheral Clock Gates]
-
- Clock ID
- ----------------------------
-
- spi_baud 16
- pb0_250 17
- pr0_250 18
- pr1_250 19
- b_250 20
- b_125 21
- b_200 22
- sata 23
- usb 24
- gmac0 25
- cs250 26
- pb0_250_o 27
- pr0_250_o 28
- pr1_250_o 29
- b_250_o 30
- b_125_o 31
- b_200_o 32
- sata_o 33
- usb_o 34
- gmac0_o 35
- cs250_o 36
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume.
+
+All available clocks are defined as preprocessor macros in
+dt-bindings/clock/exynos5440.h header and can be used in device
+tree sources.
Example: An example of a clock controller node is listed below.
diff --git a/Documentation/devicetree/bindings/clock/hi3620-clock.txt b/Documentation/devicetree/bindings/clock/hi3620-clock.txt
index 4b71ab41be53..dad6269f52c5 100644
--- a/Documentation/devicetree/bindings/clock/hi3620-clock.txt
+++ b/Documentation/devicetree/bindings/clock/hi3620-clock.txt
@@ -7,6 +7,7 @@ Required Properties:
- compatible: should be one of the following.
- "hisilicon,hi3620-clock" - controller compatible with Hi3620 SoC.
+ - "hisilicon,hi3620-mmc-clock" - controller specific for Hi3620 mmc.
- reg: physical base address of the controller and length of memory mapped
region.
diff --git a/Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt b/Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt
new file mode 100644
index 000000000000..fedea84314a1
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt
@@ -0,0 +1,48 @@
+Device Tree Clock bindings for arch-moxart
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+MOXA ART SoCs allow to determine PLL output and APB frequencies
+by reading registers holding multiplier and divisor information.
+
+
+PLL:
+
+Required properties:
+- compatible : Must be "moxa,moxart-pll-clock"
+- #clock-cells : Should be 0
+- reg : Should contain registers location and length
+- clocks : Should contain phandle + clock-specifier for the parent clock
+
+Optional properties:
+- clock-output-names : Should contain clock name
+
+
+APB:
+
+Required properties:
+- compatible : Must be "moxa,moxart-apb-clock"
+- #clock-cells : Should be 0
+- reg : Should contain registers location and length
+- clocks : Should contain phandle + clock-specifier for the parent clock
+
+Optional properties:
+- clock-output-names : Should contain clock name
+
+
+For example:
+
+ clk_pll: clk_pll@98100000 {
+ compatible = "moxa,moxart-pll-clock";
+ #clock-cells = <0>;
+ reg = <0x98100000 0x34>;
+ };
+
+ clk_apb: clk_apb@98100000 {
+ compatible = "moxa,moxart-apb-clock";
+ #clock-cells = <0>;
+ reg = <0x98100000 0x34>;
+ clocks = <&clk_pll>;
+ };
diff --git a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt
index 1e662948661e..307a503c5db8 100644
--- a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt
+++ b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt
@@ -11,6 +11,18 @@ The following is a list of provided IDs and clock names on Armada 370/XP:
3 = hclk (DRAM control clock)
4 = dramclk (DDR clock)
+The following is a list of provided IDs and clock names on Armada 375:
+ 0 = tclk (Internal Bus clock)
+ 1 = cpuclk (CPU clock)
+ 2 = l2clk (L2 Cache clock)
+ 3 = ddrclk (DDR clock)
+
+The following is a list of provided IDs and clock names on Armada 380/385:
+ 0 = tclk (Internal Bus clock)
+ 1 = cpuclk (CPU clock)
+ 2 = l2clk (L2 Cache clock)
+ 3 = ddrclk (DDR clock)
+
The following is a list of provided IDs and clock names on Kirkwood and Dove:
0 = tclk (Internal Bus clock)
1 = cpuclk (CPU0 clock)
@@ -20,6 +32,8 @@ The following is a list of provided IDs and clock names on Kirkwood and Dove:
Required properties:
- compatible : shall be one of the following:
"marvell,armada-370-core-clock" - For Armada 370 SoC core clocks
+ "marvell,armada-375-core-clock" - For Armada 375 SoC core clocks
+ "marvell,armada-380-core-clock" - For Armada 380/385 SoC core clocks
"marvell,armada-xp-core-clock" - For Armada XP SoC core clocks
"marvell,dove-core-clock" - for Dove SoC core clocks
"marvell,kirkwood-core-clock" - for Kirkwood SoC (except mv88f6180)
diff --git a/Documentation/devicetree/bindings/clock/mvebu-corediv-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-corediv-clock.txt
index c62391fc0e39..520562a7dc2a 100644
--- a/Documentation/devicetree/bindings/clock/mvebu-corediv-clock.txt
+++ b/Documentation/devicetree/bindings/clock/mvebu-corediv-clock.txt
@@ -4,7 +4,10 @@ The following is a list of provided IDs and clock names on Armada 370/XP:
0 = nand (NAND clock)
Required properties:
-- compatible : must be "marvell,armada-370-corediv-clock"
+- compatible : must be "marvell,armada-370-corediv-clock",
+ "marvell,armada-375-corediv-clock",
+ "marvell,armada-380-corediv-clock",
+
- reg : must be the register address of Core Divider control register
- #clock-cells : from common clock binding; shall be set to 1
- clocks : must be set to the parent's phandle
diff --git a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt
index fc2910fa7e45..76477be742b2 100644
--- a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt
+++ b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt
@@ -1,9 +1,10 @@
* Gated Clock bindings for Marvell EBU SoCs
-Marvell Armada 370/XP, Dove and Kirkwood allow some peripheral clocks to be
-gated to save some power. The clock consumer should specify the desired clock
-by having the clock ID in its "clocks" phandle cell. The clock ID is directly
-mapped to the corresponding clock gating control bit in HW to ease manual clock
+Marvell Armada 370/375/380/385/XP, Dove and Kirkwood allow some
+peripheral clocks to be gated to save some power. The clock consumer
+should specify the desired clock by having the clock ID in its
+"clocks" phandle cell. The clock ID is directly mapped to the
+corresponding clock gating control bit in HW to ease manual clock
lookup in datasheet.
The following is a list of provided IDs for Armada 370:
@@ -22,6 +23,60 @@ ID Clock Peripheral
28 ddr DDR Cntrl
30 sata1 SATA Host 0
+The following is a list of provided IDs for Armada 375:
+ID Clock Peripheral
+-----------------------------------
+2 mu Management Unit
+3 pp Packet Processor
+4 ptp PTP
+5 pex0 PCIe 0 Clock out
+6 pex1 PCIe 1 Clock out
+8 audio Audio Cntrl
+11 nd_clk Nand Flash Cntrl
+14 sata0_link SATA 0 Link
+15 sata0_core SATA 0 Core
+16 usb3 USB3 Host
+17 sdio SDHCI Host
+18 usb USB Host
+19 gop Gigabit Ethernet MAC
+20 sata1_link SATA 1 Link
+21 sata1_core SATA 1 Core
+22 xor0 XOR DMA 0
+23 xor1 XOR DMA 0
+24 copro Coprocessor
+25 tdm Time Division Mplx
+28 crypto0_enc Cryptographic Unit Port 0 Encryption
+29 crypto0_core Cryptographic Unit Port 0 Core
+30 crypto1_enc Cryptographic Unit Port 1 Encryption
+31 crypto1_core Cryptographic Unit Port 1 Core
+
+The following is a list of provided IDs for Armada 380/385:
+ID Clock Peripheral
+-----------------------------------
+0 audio Audio
+2 ge2 Gigabit Ethernet 2
+3 ge1 Gigabit Ethernet 1
+4 ge0 Gigabit Ethernet 0
+5 pex1 PCIe 1
+6 pex2 PCIe 2
+7 pex3 PCIe 3
+8 pex0 PCIe 0
+9 usb3h0 USB3 Host 0
+10 usb3h1 USB3 Host 1
+11 usb3d USB3 Device
+13 bm Buffer Management
+14 crypto0z Cryptographic 0 Z
+15 sata0 SATA 0
+16 crypto1z Cryptographic 1 Z
+17 sdio SDIO
+18 usb2 USB 2
+21 crypto1 Cryptographic 1
+22 xor0 XOR 0
+23 crypto0 Cryptographic 0
+25 tdm Time Division Multiplexing
+28 xor1 XOR 1
+30 sata1 SATA 1
+
The following is a list of provided IDs for Armada XP:
ID Clock Peripheral
-----------------------------------
@@ -95,6 +150,8 @@ ID Clock Peripheral
Required properties:
- compatible : shall be one of the following:
"marvell,armada-370-gating-clock" - for Armada 370 SoC clock gating
+ "marvell,armada-375-gating-clock" - for Armada 375 SoC clock gating
+ "marvell,armada-380-gating-clock" - for Armada 380/385 SoC clock gating
"marvell,armada-xp-gating-clock" - for Armada XP SoC clock gating
"marvell,dove-gating-clock" - for Dove SoC clock gating
"marvell,kirkwood-gating-clock" - for Kirkwood SoC clock gating
diff --git a/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt
new file mode 100644
index 000000000000..98a257492522
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt
@@ -0,0 +1,29 @@
+* Renesas RZ Clock Pulse Generator (CPG)
+
+The CPG generates core clocks for the RZ SoCs. It includes the PLL, variable
+CPU and GPU clocks, and several fixed ratio dividers.
+
+Required Properties:
+
+ - compatible: Must be one of
+ - "renesas,r7s72100-cpg-clocks" for the r7s72100 CPG
+ - "renesas,rz-cpg-clocks" for the generic RZ CPG
+ - reg: Base address and length of the memory resource used by the CPG
+ - clocks: References to possible parent clocks. Order must match clock modes
+ in the datasheet. For the r7s72100, this is extal, usb_x1.
+ - #clock-cells: Must be 1
+ - clock-output-names: The names of the clocks. Supported clocks are "pll",
+ "i", and "g"
+
+
+Example
+-------
+
+ cpg_clocks: cpg_clocks@fcfe0000 {
+ #clock-cells = <1>;
+ compatible = "renesas,r7s72100-cpg-clocks",
+ "renesas,rz-cpg-clocks";
+ reg = <0xfcfe0000 0x18>;
+ clocks = <&extal_clk>, <&usb_x1_clk>;
+ clock-output-names = "pll", "i", "g";
+ };
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-divmux.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-divmux.txt
new file mode 100644
index 000000000000..ae56315fcec5
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/st/st,clkgen-divmux.txt
@@ -0,0 +1,49 @@
+Binding for a ST divider and multiplexer clock driver.
+
+This binding uses the common clock binding[1].
+Base address is located to the parent node. See clock binding[2]
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Documentation/devicetree/bindings/clock/st/st,clkgen.txt
+
+Required properties:
+
+- compatible : shall be:
+ "st,clkgena-divmux-c65-hs", "st,clkgena-divmux"
+ "st,clkgena-divmux-c65-ls", "st,clkgena-divmux"
+ "st,clkgena-divmux-c32-odf0", "st,clkgena-divmux"
+ "st,clkgena-divmux-c32-odf1", "st,clkgena-divmux"
+ "st,clkgena-divmux-c32-odf2", "st,clkgena-divmux"
+ "st,clkgena-divmux-c32-odf3", "st,clkgena-divmux"
+
+- #clock-cells : From common clock binding; shall be set to 1.
+
+- clocks : From common clock binding
+
+- clock-output-names : From common clock binding.
+
+Example:
+
+ clockgenA@fd345000 {
+ reg = <0xfd345000 0xb50>;
+
+ CLK_M_A1_DIV1: CLK_M_A1_DIV1 {
+ #clock-cells = <1>;
+ compatible = "st,clkgena-divmux-c32-odf1",
+ "st,clkgena-divmux";
+
+ clocks = <&CLK_M_A1_OSC_PREDIV>,
+ <&CLK_M_A1_PLL0 1>, /* PLL0 PHI1 */
+ <&CLK_M_A1_PLL1 1>; /* PLL1 PHI1 */
+
+ clock-output-names = "CLK_M_RX_ICN_TS",
+ "CLK_M_RX_ICN_VDP_0",
+ "", /* Unused */
+ "CLK_M_PRV_T1_BUS",
+ "CLK_M_ICN_REG_12",
+ "CLK_M_ICN_REG_10",
+ "", /* Unused */
+ "CLK_M_ICN_ST231";
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-mux.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-mux.txt
new file mode 100644
index 000000000000..943e0808e212
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/st/st,clkgen-mux.txt
@@ -0,0 +1,36 @@
+Binding for a ST multiplexed clock driver.
+
+This binding supports only simple indexed multiplexers, it does not
+support table based parent index to hardware value translations.
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+
+- compatible : shall be:
+ "st,stih416-clkgenc-vcc-hd", "st,clkgen-mux"
+ "st,stih416-clkgenf-vcc-fvdp", "st,clkgen-mux"
+ "st,stih416-clkgenf-vcc-hva", "st,clkgen-mux"
+ "st,stih416-clkgenf-vcc-hd", "st,clkgen-mux"
+ "st,stih416-clkgenf-vcc-sd", "st,clkgen-mux"
+ "st,stih415-clkgen-a9-mux", "st,clkgen-mux"
+ "st,stih416-clkgen-a9-mux", "st,clkgen-mux"
+
+
+- #clock-cells : from common clock binding; shall be set to 0.
+
+- reg : A Base address and length of the register set.
+
+- clocks : from common clock binding
+
+Example:
+
+ CLK_M_HVA: CLK_M_HVA {
+ #clock-cells = <0>;
+ compatible = "st,stih416-clkgenf-vcc-hva", "st,clkgen-mux";
+ reg = <0xfd690868 4>;
+
+ clocks = <&CLOCKGEN_F 1>, <&CLK_M_A1_DIV0 3>;
+ };
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt
new file mode 100644
index 000000000000..81eb3855ab92
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt
@@ -0,0 +1,48 @@
+Binding for a ST pll clock driver.
+
+This binding uses the common clock binding[1].
+Base address is located to the parent node. See clock binding[2]
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Documentation/devicetree/bindings/clock/st/st,clkgen.txt
+
+Required properties:
+
+- compatible : shall be:
+ "st,clkgena-prediv-c65", "st,clkgena-prediv"
+ "st,clkgena-prediv-c32", "st,clkgena-prediv"
+
+ "st,clkgena-plls-c65"
+ "st,plls-c32-a1x-0", "st,clkgen-plls-c32"
+ "st,plls-c32-a1x-1", "st,clkgen-plls-c32"
+ "st,stih415-plls-c32-a9", "st,clkgen-plls-c32"
+ "st,stih415-plls-c32-ddr", "st,clkgen-plls-c32"
+ "st,stih416-plls-c32-a9", "st,clkgen-plls-c32"
+ "st,stih416-plls-c32-ddr", "st,clkgen-plls-c32"
+
+ "st,stih415-gpu-pll-c32", "st,clkgengpu-pll-c32"
+ "st,stih416-gpu-pll-c32", "st,clkgengpu-pll-c32"
+
+
+- #clock-cells : From common clock binding; shall be set to 1.
+
+- clocks : From common clock binding
+
+- clock-output-names : From common clock binding.
+
+Example:
+
+ clockgenA@fee62000 {
+ reg = <0xfee62000 0xb48>;
+
+ CLK_S_A0_PLL: CLK_S_A0_PLL {
+ #clock-cells = <1>;
+ compatible = "st,clkgena-plls-c65";
+
+ clocks = <&CLK_SYSIN>;
+
+ clock-output-names = "CLK_S_A0_PLL0_HS",
+ "CLK_S_A0_PLL0_LS",
+ "CLK_S_A0_PLL1";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-prediv.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-prediv.txt
new file mode 100644
index 000000000000..566c9d79ed32
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/st/st,clkgen-prediv.txt
@@ -0,0 +1,36 @@
+Binding for a ST pre-divider clock driver.
+
+This binding uses the common clock binding[1].
+Base address is located to the parent node. See clock binding[2]
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Documentation/devicetree/bindings/clock/st/st,clkgen.txt
+
+Required properties:
+
+- compatible : shall be:
+ "st,clkgena-prediv-c65", "st,clkgena-prediv"
+ "st,clkgena-prediv-c32", "st,clkgena-prediv"
+
+- #clock-cells : From common clock binding; shall be set to 0.
+
+- clocks : From common clock binding
+
+- clock-output-names : From common clock binding.
+
+Example:
+
+ clockgenA@fd345000 {
+ reg = <0xfd345000 0xb50>;
+
+ CLK_M_A2_OSC_PREDIV: CLK_M_A2_OSC_PREDIV {
+ #clock-cells = <0>;
+ compatible = "st,clkgena-prediv-c32",
+ "st,clkgena-prediv";
+
+ clocks = <&CLK_SYSIN>;
+
+ clock-output-names = "CLK_M_A2_OSC_PREDIV";
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-vcc.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-vcc.txt
new file mode 100644
index 000000000000..4e3ff28b04c3
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/st/st,clkgen-vcc.txt
@@ -0,0 +1,53 @@
+Binding for a type of STMicroelectronics clock crossbar (VCC).
+
+The crossbar can take up to 4 input clocks and control up to 16
+output clocks. Not all inputs or outputs have to be in use in a
+particular instantiation. Each output can be individually enabled,
+select any of the input clocks and apply a divide (by 1,2,4 or 8) to
+that selected clock.
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+
+- compatible : shall be:
+ "st,stih416-clkgenc", "st,vcc"
+ "st,stih416-clkgenf", "st,vcc"
+
+- #clock-cells : from common clock binding; shall be set to 1.
+
+- reg : A Base address and length of the register set.
+
+- clocks : from common clock binding
+
+- clock-output-names : From common clock binding. The block has 16
+ clock outputs but not all of them in a specific instance
+ have to be used in the SoC. If a clock name is left as
+ an empty string then no clock will be created for the
+ output associated with that string index. If fewer than
+ 16 strings are provided then no clocks will be created
+ for the remaining outputs.
+
+Example:
+
+ CLOCKGEN_C_VCC: CLOCKGEN_C_VCC {
+ #clock-cells = <1>;
+ compatible = "st,stih416-clkgenc", "st,clkgen-vcc";
+ reg = <0xfe8308ac 12>;
+
+ clocks = <&CLK_S_VCC_HD>, <&CLOCKGEN_C 1>,
+ <&CLK_S_TMDS_FROMPHY>, <&CLOCKGEN_C 2>;
+
+ clock-output-names =
+ "CLK_S_PIX_HDMI", "CLK_S_PIX_DVO",
+ "CLK_S_OUT_DVO", "CLK_S_PIX_HD",
+ "CLK_S_HDDAC", "CLK_S_DENC",
+ "CLK_S_SDDAC", "CLK_S_PIX_MAIN",
+ "CLK_S_PIX_AUX", "CLK_S_STFE_FRC_0",
+ "CLK_S_REF_MCRU", "CLK_S_SLAVE_MCRU",
+ "CLK_S_TMDS_HDMI", "CLK_S_HDMI_REJECT_PLL",
+ "CLK_S_THSENS";
+ };
+
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen.txt
new file mode 100644
index 000000000000..49ec5ae18b5b
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/st/st,clkgen.txt
@@ -0,0 +1,83 @@
+Binding for a Clockgen hardware block found on
+certain STMicroelectronics consumer electronics SoC devices.
+
+A Clockgen node can contain pll, diviser or multiplexer nodes.
+
+We will find only the base address of the Clockgen, this base
+address is common of all subnode.
+
+ clockgen_node {
+ reg = <>;
+
+ pll_node {
+ ...
+ };
+
+ prediv_node {
+ ...
+ };
+
+ divmux_node {
+ ...
+ };
+
+ quadfs_node {
+ ...
+ };
+ ...
+ };
+
+This binding uses the common clock binding[1].
+Each subnode should use the binding discribe in [2]..[4]
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Documentation/devicetree/bindings/clock/st,quadfs.txt
+[3] Documentation/devicetree/bindings/clock/st,quadfs.txt
+[4] Documentation/devicetree/bindings/clock/st,quadfs.txt
+
+Required properties:
+- reg : A Base address and length of the register set.
+
+Example:
+
+ clockgenA@fee62000 {
+
+ reg = <0xfee62000 0xb48>;
+
+ CLK_S_A0_PLL: CLK_S_A0_PLL {
+ #clock-cells = <1>;
+ compatible = "st,clkgena-plls-c65";
+
+ clocks = <&CLK_SYSIN>;
+
+ clock-output-names = "CLK_S_A0_PLL0_HS",
+ "CLK_S_A0_PLL0_LS",
+ "CLK_S_A0_PLL1";
+ };
+
+ CLK_S_A0_OSC_PREDIV: CLK_S_A0_OSC_PREDIV {
+ #clock-cells = <0>;
+ compatible = "st,clkgena-prediv-c65",
+ "st,clkgena-prediv";
+
+ clocks = <&CLK_SYSIN>;
+
+ clock-output-names = "CLK_S_A0_OSC_PREDIV";
+ };
+
+ CLK_S_A0_HS: CLK_S_A0_HS {
+ #clock-cells = <1>;
+ compatible = "st,clkgena-divmux-c65-hs",
+ "st,clkgena-divmux";
+
+ clocks = <&CLK_S_A0_OSC_PREDIV>,
+ <&CLK_S_A0_PLL 0>, /* PLL0 HS */
+ <&CLK_S_A0_PLL 2>; /* PLL1 */
+
+ clock-output-names = "CLK_S_FDMA_0",
+ "CLK_S_FDMA_1",
+ ""; /* CLK_S_JIT_SENSE */
+ /* Fourth output unused */
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/clock/st/st,quadfs.txt b/Documentation/devicetree/bindings/clock/st/st,quadfs.txt
new file mode 100644
index 000000000000..ec86d62ca283
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/st/st,quadfs.txt
@@ -0,0 +1,45 @@
+Binding for a type of quad channel digital frequency synthesizer found on
+certain STMicroelectronics consumer electronics SoC devices.
+
+This version contains a programmable PLL which can generate up to 216, 432
+or 660MHz (from a 30MHz oscillator input) as the input to the digital
+synthesizers.
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : shall be:
+ "st,stih416-quadfs216", "st,quadfs"
+ "st,stih416-quadfs432", "st,quadfs"
+ "st,stih416-quadfs660-E", "st,quadfs"
+ "st,stih416-quadfs660-F", "st,quadfs"
+
+- #clock-cells : from common clock binding; shall be set to 1.
+
+- reg : A Base address and length of the register set.
+
+- clocks : from common clock binding
+
+- clock-output-names : From common clock binding. The block has 4
+ clock outputs but not all of them in a specific instance
+ have to be used in the SoC. If a clock name is left as
+ an empty string then no clock will be created for the
+ output associated with that string index. If fewer than
+ 4 strings are provided then no clocks will be created
+ for the remaining outputs.
+
+Example:
+
+ CLOCKGEN_E: CLOCKGEN_E {
+ #clock-cells = <1>;
+ compatible = "st,stih416-quadfs660-E", "st,quadfs";
+ reg = <0xfd3208bc 0xB0>;
+
+ clocks = <&CLK_SYSIN>;
+ clock-output-names = "CLK_M_PIX_MDTP_0",
+ "CLK_M_PIX_MDTP_1",
+ "CLK_M_PIX_MDTP_2",
+ "CLK_M_MPELPC";
+ };
diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
index c2cb7621ad2d..a5160d8cbb5f 100644
--- a/Documentation/devicetree/bindings/clock/sunxi.txt
+++ b/Documentation/devicetree/bindings/clock/sunxi.txt
@@ -6,37 +6,41 @@ This binding uses the common clock binding[1].
Required properties:
- compatible : shall be one of the following:
- "allwinner,sun4i-osc-clk" - for a gatable oscillator
- "allwinner,sun4i-pll1-clk" - for the main PLL clock and PLL4
+ "allwinner,sun4i-a10-osc-clk" - for a gatable oscillator
+ "allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4
"allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
- "allwinner,sun4i-pll5-clk" - for the PLL5 clock
- "allwinner,sun4i-pll6-clk" - for the PLL6 clock
- "allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock
- "allwinner,sun4i-axi-clk" - for the AXI clock
- "allwinner,sun4i-axi-gates-clk" - for the AXI gates
- "allwinner,sun4i-ahb-clk" - for the AHB clock
- "allwinner,sun4i-ahb-gates-clk" - for the AHB gates on A10
+ "allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock
+ "allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock
+ "allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31
+ "allwinner,sun4i-a10-cpu-clk" - for the CPU multiplexer clock
+ "allwinner,sun4i-a10-axi-clk" - for the AXI clock
+ "allwinner,sun4i-a10-axi-gates-clk" - for the AXI gates
+ "allwinner,sun4i-a10-ahb-clk" - for the AHB clock
+ "allwinner,sun4i-a10-ahb-gates-clk" - for the AHB gates on A10
"allwinner,sun5i-a13-ahb-gates-clk" - for the AHB gates on A13
"allwinner,sun5i-a10s-ahb-gates-clk" - for the AHB gates on A10s
"allwinner,sun7i-a20-ahb-gates-clk" - for the AHB gates on A20
"allwinner,sun6i-a31-ahb1-mux-clk" - for the AHB1 multiplexer on A31
"allwinner,sun6i-a31-ahb1-gates-clk" - for the AHB1 gates on A31
- "allwinner,sun4i-apb0-clk" - for the APB0 clock
- "allwinner,sun4i-apb0-gates-clk" - for the APB0 gates on A10
+ "allwinner,sun4i-a10-apb0-clk" - for the APB0 clock
+ "allwinner,sun4i-a10-apb0-gates-clk" - for the APB0 gates on A10
"allwinner,sun5i-a13-apb0-gates-clk" - for the APB0 gates on A13
"allwinner,sun5i-a10s-apb0-gates-clk" - for the APB0 gates on A10s
"allwinner,sun7i-a20-apb0-gates-clk" - for the APB0 gates on A20
- "allwinner,sun4i-apb1-clk" - for the APB1 clock
- "allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing
- "allwinner,sun4i-apb1-gates-clk" - for the APB1 gates on A10
+ "allwinner,sun4i-a10-apb1-clk" - for the APB1 clock
+ "allwinner,sun4i-a10-apb1-mux-clk" - for the APB1 clock muxing
+ "allwinner,sun4i-a10-apb1-gates-clk" - for the APB1 gates on A10
"allwinner,sun5i-a13-apb1-gates-clk" - for the APB1 gates on A13
"allwinner,sun5i-a10s-apb1-gates-clk" - for the APB1 gates on A10s
"allwinner,sun6i-a31-apb1-gates-clk" - for the APB1 gates on A31
"allwinner,sun7i-a20-apb1-gates-clk" - for the APB1 gates on A20
"allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31
"allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31
- "allwinner,sun4i-mod0-clk" - for the module 0 family of clocks
+ "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks
"allwinner,sun7i-a20-out-clk" - for the external output clocks
+ "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
+ "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20
+ "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13
Required properties for all clocks:
- reg : shall be the control register address for the clock.
@@ -44,10 +48,17 @@ Required properties for all clocks:
multiplexed clocks, the list order must match the hardware
programming order.
- #clock-cells : from common clock binding; shall be set to 0 except for
- "allwinner,*-gates-clk" where it shall be set to 1
+ "allwinner,*-gates-clk", "allwinner,sun4i-pll5-clk" and
+ "allwinner,sun4i-pll6-clk" where it shall be set to 1
+- clock-output-names : shall be the corresponding names of the outputs.
+ If the clock module only has one output, the name shall be the
+ module name.
-Additionally, "allwinner,*-gates-clk" clocks require:
-- clock-output-names : the corresponding gate names that the clock controls
+And "allwinner,*-usb-clk" clocks also require:
+- reset-cells : shall be set to 1
+
+For "allwinner,sun7i-a20-gmac-clk", the parent clocks shall be fixed rate
+dummy clocks at 25 MHz and 125 MHz, respectively. See example.
Clock consumers should specify the desired clocks they use with a
"clocks" phandle cell. Consumers that are using a gated clock should
@@ -56,23 +67,68 @@ offset of the bit controlling this particular gate in the register.
For example:
-osc24M: osc24M@01c20050 {
+osc24M: clk@01c20050 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-osc-clk";
+ compatible = "allwinner,sun4i-a10-osc-clk";
reg = <0x01c20050 0x4>;
clocks = <&osc24M_fixed>;
+ clock-output-names = "osc24M";
};
-pll1: pll1@01c20000 {
+pll1: clk@01c20000 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-pll1-clk";
+ compatible = "allwinner,sun4i-a10-pll1-clk";
reg = <0x01c20000 0x4>;
clocks = <&osc24M>;
+ clock-output-names = "pll1";
+};
+
+pll5: clk@01c20020 {
+ #clock-cells = <1>;
+ compatible = "allwinner,sun4i-pll5-clk";
+ reg = <0x01c20020 0x4>;
+ clocks = <&osc24M>;
+ clock-output-names = "pll5_ddr", "pll5_other";
};
cpu: cpu@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-cpu-clk";
+ compatible = "allwinner,sun4i-a10-cpu-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll1>;
+ clock-output-names = "cpu";
+};
+
+mmc0_clk: clk@01c20088 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-mod0-clk";
+ reg = <0x01c20088 0x4>;
+ clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
+ clock-output-names = "mmc0";
+};
+
+mii_phy_tx_clk: clk@2 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <25000000>;
+ clock-output-names = "mii_phy_tx";
+};
+
+gmac_int_tx_clk: clk@3 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <125000000>;
+ clock-output-names = "gmac_int_tx";
+};
+
+gmac_clk: clk@01c20164 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun7i-a20-gmac-clk";
+ reg = <0x01c20164 0x4>;
+ /*
+ * The first clock must be fixed at 25MHz;
+ * the second clock must be fixed at 125MHz
+ */
+ clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>;
+ clock-output-names = "gmac";
};
diff --git a/Documentation/devicetree/bindings/clock/zynq-7000.txt b/Documentation/devicetree/bindings/clock/zynq-7000.txt
index 17b4a94916d6..d93746cf2975 100644
--- a/Documentation/devicetree/bindings/clock/zynq-7000.txt
+++ b/Documentation/devicetree/bindings/clock/zynq-7000.txt
@@ -14,6 +14,7 @@ for all clock consumers of PS clocks.
Required properties:
- #clock-cells : Must be 1
- compatible : "xlnx,ps7-clkc"
+ - reg : SLCR offset and size taken via syscon < 0x100 0x100 >
- ps-clk-frequency : Frequency of the oscillator providing ps_clk in HZ
(usually 33 MHz oscillators are used for Zynq platforms)
- clock-output-names : List of strings used to name the clock outputs. Shall be
@@ -87,10 +88,11 @@ Clock outputs:
47: dbg_apb
Example:
- clkc: clkc {
+ clkc: clkc@100 {
#clock-cells = <1>;
compatible = "xlnx,ps7-clkc";
ps-clk-frequency = <33333333>;
+ reg = <0x100 0x100>;
clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x",
"cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x",
"dci", "lqspi", "smc", "pcap", "gem0", "gem1",
diff --git a/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt b/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt
new file mode 100644
index 000000000000..94ae9f82dcf8
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt
@@ -0,0 +1,17 @@
+* ARM Cirrus Logic CLPS711X SYSFLG1 MCTRL GPIOs
+
+Required properties:
+- compatible: Should contain "cirrus,clps711x-mctrl-gpio".
+- gpio-controller: Marks the device node as a gpio controller.
+- #gpio-cells: Should be two. The first cell is the pin number and
+ the second cell is used to specify the gpio polarity:
+ 0 = Active high,
+ 1 = Active low.
+
+Example:
+ sysgpio: sysgpio {
+ compatible = "cirrus,ep7312-mctrl-gpio",
+ "cirrus,clps711x-mctrl-gpio";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
diff --git a/Documentation/devicetree/bindings/gpio/gpio-davinci.txt b/Documentation/devicetree/bindings/gpio/gpio-davinci.txt
index a2e839d6e338..5079ba7d6568 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-davinci.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-davinci.txt
@@ -1,13 +1,17 @@
-Davinci GPIO controller bindings
+Davinci/Keystone GPIO controller bindings
Required Properties:
-- compatible: should be "ti,dm6441-gpio"
+- compatible: should be "ti,dm6441-gpio", "ti,keystone-gpio"
- reg: Physical base address of the controller and the size of memory mapped
registers.
- gpio-controller : Marks the device node as a gpio controller.
+- #gpio-cells : Should be two.
+ - first cell is the pin number
+ - second cell is used to specify optional parameters (unused)
+
- interrupt-parent: phandle of the parent interrupt controller.
- interrupts: Array of GPIO interrupt number. Only banked or unbanked IRQs are
@@ -27,6 +31,7 @@ Example:
gpio: gpio@1e26000 {
compatible = "ti,dm6441-gpio";
gpio-controller;
+ #gpio-cells = <2>;
reg = <0x226000 0x1000>;
interrupt-parent = <&intc>;
interrupts = <42 IRQ_TYPE_EDGE_BOTH 43 IRQ_TYPE_EDGE_BOTH
@@ -39,3 +44,19 @@ gpio: gpio@1e26000 {
interrupt-controller;
#interrupt-cells = <2>;
};
+
+leds {
+ compatible = "gpio-leds";
+
+ led1 {
+ label = "davinci:green:usr1";
+ gpios = <&gpio 10 GPIO_ACTIVE_HIGH>;
+ ...
+ };
+
+ led2 {
+ label = "davinci:red:debug1";
+ gpios = <&gpio 11 GPIO_ACTIVE_HIGH>;
+ ...
+ };
+};
diff --git a/Documentation/devicetree/bindings/gpio/gpio-zevio.txt b/Documentation/devicetree/bindings/gpio/gpio-zevio.txt
new file mode 100644
index 000000000000..a37bd9ae2730
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-zevio.txt
@@ -0,0 +1,16 @@
+Zevio GPIO controller
+
+Required properties:
+- compatible: Should be "lsi,zevio-gpio"
+- reg: Address and length of the register set for the device
+- #gpio-cells: Should be two. The first cell is the pin number and the
+ second cell is used to specify optional parameters (currently unused).
+- gpio-controller: Marks the device node as a GPIO controller.
+
+Example:
+ gpio: gpio@90000000 {
+ compatible = "lsi,zevio-gpio";
+ reg = <0x90000000 0x1000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
index 0c85bb6e3a80..3fb8f53071b8 100644
--- a/Documentation/devicetree/bindings/gpio/gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio.txt
@@ -13,11 +13,11 @@ properties, each containing a 'gpio-list':
gpio-specifier : Array of #gpio-cells specifying specific gpio
(controller specific)
-GPIO properties should be named "[<name>-]gpios". Exact
+GPIO properties should be named "[<name>-]gpios". The exact
meaning of each gpios property must be documented in the device tree
binding for each device.
-For example, the following could be used to describe gpios pins to use
+For example, the following could be used to describe GPIO pins used
as chip select lines; with chip selects 0, 1 and 3 populated, and chip
select 2 left empty:
@@ -44,35 +44,79 @@ whether pin is open-drain and whether pin is logically inverted.
Exact meaning of each specifier cell is controller specific, and must
be documented in the device tree binding for the device.
-Example of the node using GPIOs:
+Example of a node using GPIOs:
node {
gpios = <&qe_pio_e 18 0>;
};
In this example gpio-specifier is "18 0" and encodes GPIO pin number,
-and empty GPIO flags as accepted by the "qe_pio_e" gpio-controller.
+and GPIO flags as accepted by the "qe_pio_e" gpio-controller.
+
+1.1) GPIO specifier best practices
+----------------------------------
+
+A gpio-specifier should contain a flag indicating the GPIO polarity; active-
+high or active-low. If it does, the follow best practices should be followed:
+
+The gpio-specifier's polarity flag should represent the physical level at the
+GPIO controller that achieves (or represents, for inputs) a logically asserted
+value at the device. The exact definition of logically asserted should be
+defined by the binding for the device. If the board inverts the signal between
+the GPIO controller and the device, then the gpio-specifier will represent the
+opposite physical level than the signal at the device's pin.
+
+When the device's signal polarity is configurable, the binding for the
+device must either:
+
+a) Define a single static polarity for the signal, with the expectation that
+any software using that binding would statically program the device to use
+that signal polarity.
+
+The static choice of polarity may be either:
+
+a1) (Preferred) Dictated by a binding-specific DT property.
+
+or:
+
+a2) Defined statically by the DT binding itself.
+
+In particular, the polarity cannot be derived from the gpio-specifier, since
+that would prevent the DT from separately representing the two orthogonal
+concepts of configurable signal polarity in the device, and possible board-
+level signal inversion.
+
+or:
+
+b) Pick a single option for device signal polarity, and document this choice
+in the binding. The gpio-specifier should represent the polarity of the signal
+(at the GPIO controller) assuming that the device is configured for this
+particular signal polarity choice. If software chooses to program the device
+to generate or receive a signal of the opposite polarity, software will be
+responsible for correctly interpreting (inverting) the GPIO signal at the GPIO
+controller.
2) gpio-controller nodes
------------------------
-Every GPIO controller node must both an empty "gpio-controller"
-property, and have #gpio-cells contain the size of the gpio-specifier.
+Every GPIO controller node must contain both an empty "gpio-controller"
+property, and a #gpio-cells integer property, which indicates the number of
+cells in a gpio-specifier.
Example of two SOC GPIO banks defined as gpio-controller nodes:
qe_pio_a: gpio-controller@1400 {
- #gpio-cells = <2>;
compatible = "fsl,qe-pario-bank-a", "fsl,qe-pario-bank";
reg = <0x1400 0x18>;
gpio-controller;
+ #gpio-cells = <2>;
};
qe_pio_e: gpio-controller@1460 {
- #gpio-cells = <2>;
compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
reg = <0x1460 0x18>;
gpio-controller;
+ #gpio-cells = <2>;
};
2.1) gpio- and pin-controller interaction
diff --git a/Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt b/Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt
new file mode 100644
index 000000000000..dd5d2c0394b1
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt
@@ -0,0 +1,60 @@
+* Synopsys DesignWare APB GPIO controller
+
+Required properties:
+- compatible : Should contain "snps,dw-apb-gpio"
+- reg : Address and length of the register set for the device.
+- #address-cells : should be 1 (for addressing port subnodes).
+- #size-cells : should be 0 (port subnodes).
+
+The GPIO controller has a configurable number of ports, each of which are
+represented as child nodes with the following properties:
+
+Required properties:
+- compatible : "snps,dw-apb-gpio-port"
+- gpio-controller : Marks the device node as a gpio controller.
+- #gpio-cells : Should be two. The first cell is the pin number and
+ the second cell is used to specify the gpio polarity:
+ 0 = active high
+ 1 = active low
+- reg : The integer port index of the port, a single cell.
+
+Optional properties:
+- interrupt-controller : The first port may be configured to be an interrupt
+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
+- interrupt-parent : The parent interrupt controller.
+- interrupts : The interrupt to the parent controller raised when GPIOs
+ generate the interrupts.
+- snps,nr-gpios : The number of pins in the port, a single cell.
+
+Example:
+
+gpio: gpio@20000 {
+ compatible = "snps,dw-apb-gpio";
+ reg = <0x20000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ porta: gpio-controller@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ gpio-controller;
+ #gpio-cells = <2>;
+ snps,nr-gpios = <8>;
+ reg = <0>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupt-parent = <&vic1>;
+ interrupts = <0>;
+ };
+
+ portb: gpio-controller@1 {
+ compatible = "snps,dw-apb-gpio-port";
+ gpio-controller;
+ #gpio-cells = <2>;
+ snps,nr-gpios = <8>;
+ reg = <1>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
index f47e56bcf78d..71724d026ffa 100644
--- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
@@ -59,6 +59,7 @@ plx,pex8648 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch
ramtron,24c64 i2c serial eeprom (24cxx)
ricoh,rs5c372a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
+sii,s35390a 2-wire CMOS real-time clock
st-micro,24c256 i2c serial eeprom (24cxx)
stm,m41t00 Serial Access TIMEKEEPER
stm,m41t62 Serial real-time clock (RTC) with alarm
diff --git a/Documentation/devicetree/bindings/arm/atmel-adc.txt b/Documentation/devicetree/bindings/iio/adc/at91_adc.txt
index 9a1175b46f49..0f813dec5e08 100644
--- a/Documentation/devicetree/bindings/arm/atmel-adc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/at91_adc.txt
@@ -8,32 +8,32 @@ Required properties:
- clock-names: tuple listing input clock names.
Required elements: "adc_clk", "adc_op_clk".
- clocks: phandles to input clocks.
- - atmel,adc-channels-used: Bitmask of the channels muxed and enable for this
+ - atmel,adc-channels-used: Bitmask of the channels muxed and enabled for this
device
- atmel,adc-startup-time: Startup Time of the ADC in microseconds as
defined in the datasheet
- atmel,adc-vref: Reference voltage in millivolts for the conversions
- - atmel,adc-res: List of resolution in bits supported by the ADC. List size
+ - atmel,adc-res: List of resolutions in bits supported by the ADC. List size
must be two at least.
- atmel,adc-res-names: Contains one identifier string for each resolution
in atmel,adc-res property. "lowres" and "highres"
identifiers are required.
Optional properties:
- - atmel,adc-use-external: Boolean to enable of external triggers
+ - atmel,adc-use-external-triggers: Boolean to enable the external triggers
- atmel,adc-use-res: String corresponding to an identifier from
atmel,adc-res-names property. If not specified, the highest
resolution will be used.
- atmel,adc-sleep-mode: Boolean to enable sleep mode when no conversion
- atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
- - atmel,adc-ts-wires: Number of touch screen wires. Should be 4 or 5. If this
- value is set, then adc driver will enable touch screen
+ - atmel,adc-ts-wires: Number of touchscreen wires. Should be 4 or 5. If this
+ value is set, then the adc driver will enable touchscreen
support.
- NOTE: when adc touch screen enabled, the adc hardware trigger will be
- disabled. Since touch screen will occupied the trigger register.
+ NOTE: when adc touchscreen is enabled, the adc hardware trigger will be
+ disabled. Since touchscreen will occupy the trigger register.
- atmel,adc-ts-pressure-threshold: a pressure threshold for touchscreen. It
- make touch detect more precision.
-
+ makes touch detection more precise.
+
Optional trigger Nodes:
- Required properties:
* trigger-name: Name of the trigger exposed to the user
@@ -44,42 +44,43 @@ Optional trigger Nodes:
Examples:
adc0: adc@fffb0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
compatible = "atmel,at91sam9260-adc";
reg = <0xfffb0000 0x100>;
- interrupts = <20 4>;
+ interrupts = <20 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&adc_clk>, <&adc_op_clk>;
clock-names = "adc_clk", "adc_op_clk";
- atmel,adc-channel-base = <0x30>;
atmel,adc-channels-used = <0xff>;
- atmel,adc-drdy-mask = <0x10000>;
- atmel,adc-num-channels = <8>;
atmel,adc-startup-time = <40>;
- atmel,adc-status-register = <0x1c>;
- atmel,adc-trigger-register = <0x08>;
- atmel,adc-use-external;
+ atmel,adc-use-external-triggers;
atmel,adc-vref = <3300>;
atmel,adc-res = <8 10>;
atmel,adc-res-names = "lowres", "highres";
atmel,adc-use-res = "lowres";
trigger@0 {
+ reg = <0>;
trigger-name = "external-rising";
trigger-value = <0x1>;
trigger-external;
};
trigger@1 {
+ reg = <1>;
trigger-name = "external-falling";
trigger-value = <0x2>;
trigger-external;
};
trigger@2 {
+ reg = <2>;
trigger-name = "external-any";
trigger-value = <0x3>;
trigger-external;
};
trigger@3 {
+ reg = <3>;
trigger-name = "continuous";
trigger-value = <0x6>;
};
diff --git a/Documentation/devicetree/bindings/iio/adc/twl4030-madc.txt b/Documentation/devicetree/bindings/iio/adc/twl4030-madc.txt
new file mode 100644
index 000000000000..6bdd21404b57
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/twl4030-madc.txt
@@ -0,0 +1,24 @@
+* TWL4030 Monitoring Analog to Digital Converter (MADC)
+
+The MADC subsystem in the TWL4030 consists of a 10-bit ADC
+combined with a 16-input analog multiplexer.
+
+Required properties:
+ - compatible: Should contain "ti,twl4030-madc".
+ - interrupts: IRQ line for the MADC submodule.
+ - #io-channel-cells: Should be set to <1>.
+
+Optional properties:
+ - ti,system-uses-second-madc-irq: boolean, set if the second madc irq register
+ should be used, which is intended to be used
+ by Co-Processors (e.g. a modem).
+
+Example:
+
+&twl {
+ madc {
+ compatible = "ti,twl4030-madc";
+ interrupts = <3>;
+ #io-channel-cells = <1>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/input/clps711x-keypad.txt b/Documentation/devicetree/bindings/input/clps711x-keypad.txt
new file mode 100644
index 000000000000..e68d2bbc6c07
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/clps711x-keypad.txt
@@ -0,0 +1,27 @@
+* Cirrus Logic CLPS711X matrix keypad device tree bindings
+
+Required Properties:
+- compatible: Shall contain "cirrus,clps711x-keypad".
+- row-gpios: List of GPIOs used as row lines.
+- poll-interval: Poll interval time in milliseconds.
+- linux,keymap: The definition can be found at
+ bindings/input/matrix-keymap.txt.
+
+Optional Properties:
+- autorepeat: Enable autorepeat feature.
+
+Example:
+ keypad {
+ compatible = "cirrus,ep7312-keypad", "cirrus,clps711x-keypad";
+ autorepeat;
+ poll-interval = <120>;
+ row-gpios = <&porta 0 0>,
+ <&porta 1 0>;
+
+ linux,keymap = <
+ MATRIX_KEY(0, 0, KEY_UP)
+ MATRIX_KEY(0, 1, KEY_DOWN)
+ MATRIX_KEY(1, 0, KEY_LEFT)
+ MATRIX_KEY(1, 1, KEY_RIGHT)
+ >;
+ };
diff --git a/Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt b/Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt
new file mode 100644
index 000000000000..7d8cb92831d7
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt
@@ -0,0 +1,89 @@
+Qualcomm PM8xxx PMIC Keypad
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,pm8058-keypad"
+ "qcom,pm8921-keypad"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: address of keypad control register
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: the first interrupt specifies the key sense interrupt
+ and the second interrupt specifies the key stuck interrupt.
+ The format of the specifier is defined by the binding
+ document describing the node's interrupt parent.
+
+- linux,keymap:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: the linux keymap. More information can be found in
+ input/matrix-keymap.txt.
+
+- linux,keypad-no-autorepeat:
+ Usage: optional
+ Value type: <bool>
+ Definition: don't enable autorepeat feature.
+
+- linux,keypad-wakeup:
+ Usage: optional
+ Value type: <bool>
+ Definition: use any event on keypad as wakeup event.
+
+- keypad,num-rows:
+ Usage: required
+ Value type: <u32>
+ Definition: number of rows in the keymap. More information can be found
+ in input/matrix-keymap.txt.
+
+- keypad,num-columns:
+ Usage: required
+ Value type: <u32>
+ Definition: number of columns in the keymap. More information can be
+ found in input/matrix-keymap.txt.
+
+- debounce:
+ Usage: optional
+ Value type: <u32>
+ Definition: time in microseconds that key must be pressed or release
+ for key sense interrupt to trigger.
+
+- scan-delay:
+ Usage: optional
+ Value type: <u32>
+ Definition: time in microseconds to pause between successive scans
+ of the matrix array.
+
+- row-hold:
+ Usage: optional
+ Value type: <u32>
+ Definition: time in nanoseconds to pause between scans of each row in
+ the matrix array.
+
+EXAMPLE
+
+ keypad@148 {
+ compatible = "qcom,pm8921-keypad";
+ reg = <0x148>;
+ interrupt-parent = <&pmicintc>;
+ interrupts = <74 1>, <75 1>;
+ linux,keymap = <
+ MATRIX_KEY(0, 0, KEY_VOLUMEUP)
+ MATRIX_KEY(0, 1, KEY_VOLUMEDOWN)
+ MATRIX_KEY(0, 2, KEY_CAMERA_FOCUS)
+ MATRIX_KEY(0, 3, KEY_CAMERA)
+ >;
+ keypad,num-rows = <1>;
+ keypad,num-columns = <5>;
+ debounce = <15>;
+ scan-delay = <32>;
+ row-hold = <91500>;
+ };
diff --git a/Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt b/Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt
new file mode 100644
index 000000000000..588536cc96ed
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt
@@ -0,0 +1,46 @@
+Qualcomm PM8xxx PMIC Power Key
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,pm8058-pwrkey"
+ "qcom,pm8921-pwrkey"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: address of power key control register
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: the first interrupt specifies the key release interrupt
+ and the second interrupt specifies the key press interrupt.
+ The format of the specifier is defined by the binding
+ document describing the node's interrupt parent.
+
+- debounce:
+ Usage: optional
+ Value type: <u32>
+ Definition: time in microseconds that key must be pressed or release
+ for state change interrupt to trigger.
+
+- pull-up:
+ Usage: optional
+ Value type: <empty>
+ Definition: presence of this property indicates that the KPDPWR_N pin
+ should be configured for pull up.
+
+EXAMPLE
+
+ pwrkey@1c {
+ compatible = "qcom,pm8921-pwrkey";
+ reg = <0x1c>;
+ interrupt-parent = <&pmicintc>;
+ interrupts = <50 1>, <51 1>;
+ debounce = <15625>;
+ pull-up;
+ };
diff --git a/Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt b/Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt
new file mode 100644
index 000000000000..4ed467b1e402
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt
@@ -0,0 +1,22 @@
+Qualcomm PM8xxx PMIC Vibrator
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,pm8058-vib"
+ "qcom,pm8921-vib"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: address of vibration control register
+
+EXAMPLE
+
+ vibrator@4a {
+ compatible = "qcom,pm8058-vib";
+ reg = <0x4a>;
+ };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
new file mode 100644
index 000000000000..76db96704a60
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
@@ -0,0 +1,55 @@
+FocalTech EDT-FT5x06 Polytouch driver
+=====================================
+
+There are 3 variants of the chip for various touch panel sizes
+FT5206GE1 2.8" .. 3.8"
+FT5306DE4 4.3" .. 7"
+FT5406EE8 7" .. 8.9"
+
+The software interface is identical for all those chips, so that
+currently there is no need for the driver to distinguish between the
+different chips. Nevertheless distinct compatible strings are used so
+that a distinction can be added if necessary without changing the DT
+bindings.
+
+
+Required properties:
+ - compatible: "edt,edt-ft5206"
+ or: "edt,edt-ft5306"
+ or: "edt,edt-ft5406"
+
+ - reg: I2C slave address of the chip (0x38)
+ - interrupt-parent: a phandle pointing to the interrupt controller
+ serving the interrupt for this chip
+ - interrupts: interrupt specification for the touchdetect
+ interrupt
+
+Optional properties:
+ - reset-gpios: GPIO specification for the RESET input
+ - wake-gpios: GPIO specification for the WAKE input
+
+ - pinctrl-names: should be "default"
+ - pinctrl-0: a phandle pointing to the pin settings for the
+ control gpios
+
+ - threshold: allows setting the "click"-threshold in the range
+ from 20 to 80.
+
+ - gain: allows setting the sensitivity in the range from 0 to
+ 31. Note that lower values indicate higher
+ sensitivity.
+
+ - offset: allows setting the edge compensation in the range from
+ 0 to 31.
+
+Example:
+ polytouch: edt-ft5x06@38 {
+ compatible = "edt,edt-ft5406", "edt,edt-ft5x06";
+ reg = <0x38>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&edt_ft5x06_pins>;
+ interrupt-parent = <&gpio2>;
+ interrupts = <5 0>;
+ reset-gpios = <&gpio2 6 1>;
+ wake-gpios = <&gpio4 9 0>;
+ };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt
new file mode 100644
index 000000000000..2faf1f1fa39e
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt
@@ -0,0 +1,30 @@
+* Neonode infrared touchscreen controller
+
+Required properties:
+- compatible: must be "neonode,zforce"
+- reg: I2C address of the chip
+- interrupts: interrupt to which the chip is connected
+- gpios: gpios the chip is connected to
+ first one is the interrupt gpio and second one the reset gpio
+- x-size: horizontal resolution of touchscreen
+- y-size: vertical resolution of touchscreen
+
+Example:
+
+ i2c@00000000 {
+ /* ... */
+
+ zforce_ts@50 {
+ compatible = "neonode,zforce";
+ reg = <0x50>;
+ interrupts = <2 0>;
+
+ gpios = <&gpio5 6 0>, /* INT */
+ <&gpio5 9 0>; /* RST */
+
+ x-size = <800>;
+ y-size = <600>;
+ };
+
+ /* ... */
+ };
diff --git a/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt
new file mode 100644
index 000000000000..759339c34e4f
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt
@@ -0,0 +1,41 @@
+Cirrus Logic CLPS711X Interrupt Controller
+
+Required properties:
+
+- compatible: Should be "cirrus,clps711x-intc".
+- reg: Specifies base physical address of the registers set.
+- interrupt-controller: Identifies the node as an interrupt controller.
+- #interrupt-cells: Specifies the number of cells needed to encode an
+ interrupt source. The value shall be 1.
+
+The interrupt sources are as follows:
+ID Name Description
+---------------------------
+1: BLINT Battery low (FIQ)
+3: MCINT Media changed (FIQ)
+4: CSINT CODEC sound
+5: EINT1 External 1
+6: EINT2 External 2
+7: EINT3 External 3
+8: TC1OI TC1 under flow
+9: TC2OI TC2 under flow
+10: RTCMI RTC compare match
+11: TINT 64Hz tick
+12: UTXINT1 UART1 transmit FIFO half empty
+13: URXINT1 UART1 receive FIFO half full
+14: UMSINT UART1 modem status changed
+15: SSEOTI SSI1 end of transfer
+16: KBDINT Keyboard
+17: SS2RX SSI2 receive FIFO half or greater full
+18: SS2TX SSI2 transmit FIFO less than half empty
+28: UTXINT2 UART2 transmit FIFO half empty
+29: URXINT2 UART2 receive FIFO half full
+32: DAIINT DAI interface (FIQ)
+
+Example:
+ intc: interrupt-controller {
+ compatible = "cirrus,clps711x-intc";
+ reg = <0x80000000 0x4000>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ };
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
index e34c6cdd8ba8..f284b99402bc 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -48,6 +48,12 @@ conditions.
from the mmu-masters towards memory) node for this
SMMU.
+- calxeda,smmu-secure-config-access : Enable proper handling of buggy
+ implementations that always use secure access to
+ SMMU configuration registers. In this case non-secure
+ aliases of secure registers have to be used during
+ SMMU configuration.
+
Example:
smmu {
diff --git a/Documentation/devicetree/bindings/iommu/ti,omap-iommu.txt b/Documentation/devicetree/bindings/iommu/ti,omap-iommu.txt
new file mode 100644
index 000000000000..42531dc387aa
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/ti,omap-iommu.txt
@@ -0,0 +1,26 @@
+OMAP2+ IOMMU
+
+Required properties:
+- compatible : Should be one of,
+ "ti,omap2-iommu" for OMAP2/OMAP3 IOMMU instances
+ "ti,omap4-iommu" for OMAP4/OMAP5 IOMMU instances
+ "ti,dra7-iommu" for DRA7xx IOMMU instances
+- ti,hwmods : Name of the hwmod associated with the IOMMU instance
+- reg : Address space for the configuration registers
+- interrupts : Interrupt specifier for the IOMMU instance
+
+Optional properties:
+- ti,#tlb-entries : Number of entries in the translation look-aside buffer.
+ Should be either 8 or 32 (default: 32)
+- ti,iommu-bus-err-back : Indicates the IOMMU instance supports throwing
+ back a bus error response on MMU faults.
+
+Example:
+ /* OMAP3 ISP MMU */
+ mmu_isp: mmu@480bd400 {
+ compatible = "ti,omap2-iommu";
+ reg = <0x480bd400 0x80>;
+ interrupts = <24>;
+ ti,hwmods = "mmu_isp";
+ ti,#tlb-entries = <8>;
+ };
diff --git a/Documentation/devicetree/bindings/media/img-ir-rev1.txt b/Documentation/devicetree/bindings/media/img-ir-rev1.txt
new file mode 100644
index 000000000000..5434ce61b925
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/img-ir-rev1.txt
@@ -0,0 +1,34 @@
+* ImgTec Infrared (IR) decoder version 1
+
+This binding is for Imagination Technologies' Infrared decoder block,
+specifically major revision 1.
+
+Required properties:
+- compatible: Should be "img,ir-rev1"
+- reg: Physical base address of the controller and length of
+ memory mapped region.
+- interrupts: The interrupt specifier to the cpu.
+
+Optional properties:
+- clocks: List of clock specifiers as described in standard
+ clock bindings.
+ Up to 3 clocks may be specified in the following order:
+ 1st: Core clock (defaults to 32.768KHz if omitted).
+ 2nd: System side (fast) clock.
+ 3rd: Power modulation clock.
+- clock-names: List of clock names corresponding to the clocks
+ specified in the clocks property.
+ Accepted clock names are:
+ "core": Core clock.
+ "sys": System clock.
+ "mod": Power modulation clock.
+
+Example:
+
+ ir@02006200 {
+ compatible = "img,ir-rev1";
+ reg = <0x02006200 0x100>;
+ interrupts = <29 4>;
+ clocks = <&clk_32khz>;
+ clock-names = "core";
+ };
diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
index 96312f6c4c26..922d6f8e74be 100644
--- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
+++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
@@ -15,11 +15,21 @@ Common 'camera' node
Required properties:
-- compatible : must be "samsung,fimc", "simple-bus"
-- clocks : list of clock specifiers, corresponding to entries in
- the clock-names property;
-- clock-names : must contain "sclk_cam0", "sclk_cam1", "pxl_async0",
- "pxl_async1" entries, matching entries in the clocks property.
+- compatible: must be "samsung,fimc", "simple-bus"
+- clocks: list of clock specifiers, corresponding to entries in
+ the clock-names property;
+- clock-names : must contain "sclk_cam0", "sclk_cam1", "pxl_async0",
+ "pxl_async1" entries, matching entries in the clocks property.
+
+- #clock-cells: from the common clock bindings (../clock/clock-bindings.txt),
+ must be 1. A clock provider is associated with the 'camera' node and it should
+ be referenced by external sensors that use clocks provided by the SoC on
+ CAM_*_CLKOUT pins. The clock specifier cell stores an index of a clock.
+ The indices are 0, 1 for CAM_A_CLKOUT, CAM_B_CLKOUT clocks respectively.
+
+- clock-output-names: from the common clock bindings, should contain names of
+ clocks registered by the camera subsystem corresponding to CAM_A_CLKOUT,
+ CAM_B_CLKOUT output clocks respectively.
The pinctrl bindings defined in ../pinctrl/pinctrl-bindings.txt must be used
to define a required pinctrl state named "default" and optional pinctrl states:
@@ -32,6 +42,7 @@ way around.
The 'camera' node must include at least one 'fimc' child node.
+
'fimc' device nodes
-------------------
@@ -88,8 +99,8 @@ port nodes specifies data input - 0, 1 indicates input A, B respectively.
Optional properties
-- samsung,camclk-out : specifies clock output for remote sensor,
- 0 - CAM_A_CLKOUT, 1 - CAM_B_CLKOUT;
+- samsung,camclk-out (deprecated) : specifies clock output for remote sensor,
+ 0 - CAM_A_CLKOUT, 1 - CAM_B_CLKOUT;
Image sensor nodes
------------------
@@ -97,8 +108,6 @@ Image sensor nodes
The sensor device nodes should be added to their control bus controller (e.g.
I2C0) nodes and linked to a port node in the csis or the parallel-ports node,
using the common video interfaces bindings, defined in video-interfaces.txt.
-The implementation of this bindings requires clock-frequency property to be
-present in the sensor device nodes.
Example:
@@ -114,7 +123,7 @@ Example:
vddio-supply = <...>;
clock-frequency = <24000000>;
- clocks = <...>;
+ clocks = <&camera 1>;
clock-names = "mclk";
port {
@@ -135,7 +144,7 @@ Example:
vddio-supply = <...>;
clock-frequency = <24000000>;
- clocks = <...>;
+ clocks = <&camera 0>;
clock-names = "mclk";
port {
@@ -149,12 +158,17 @@ Example:
camera {
compatible = "samsung,fimc", "simple-bus";
- #address-cells = <1>;
- #size-cells = <1>;
- status = "okay";
-
+ clocks = <&clock 132>, <&clock 133>, <&clock 351>,
+ <&clock 352>;
+ clock-names = "sclk_cam0", "sclk_cam1", "pxl_async0",
+ "pxl_async1";
+ #clock-cells = <1>;
+ clock-output-names = "cam_a_clkout", "cam_b_clkout";
pinctrl-names = "default";
pinctrl-0 = <&cam_port_a_clk_active>;
+ status = "okay";
+ #address-cells = <1>;
+ #size-cells = <1>;
/* parallel camera ports */
parallel-ports {
diff --git a/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt b/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt
new file mode 100644
index 000000000000..2c85c4538a6d
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt
@@ -0,0 +1,97 @@
+Samsung S5C73M3 8Mp camera ISP
+------------------------------
+
+The S5C73M3 camera ISP supports MIPI CSI-2 and parallel (ITU-R BT.656) video
+data busses. The I2C bus is the main control bus and additionally the SPI bus
+is used, mostly for transferring the firmware to and from the device. Two
+slave device nodes corresponding to these control bus interfaces are required
+and should be placed under respective bus controller nodes.
+
+I2C slave device node
+---------------------
+
+Required properties:
+
+- compatible : "samsung,s5c73m3";
+- reg : I2C slave address of the sensor;
+- vdd-int-supply : digital power supply (1.2V);
+- vdda-supply : analog power supply (1.2V);
+- vdd-reg-supply : regulator input power supply (2.8V);
+- vddio-host-supply : host I/O power supply (1.8V to 2.8V);
+- vddio-cis-supply : CIS I/O power supply (1.2V to 1.8V);
+- vdd-af-supply : lens power supply (2.8V);
+- xshutdown-gpios : specifier of GPIO connected to the XSHUTDOWN pin;
+- standby-gpios : specifier of GPIO connected to the STANDBY pin;
+- clocks : should contain list of phandle and clock specifier pairs
+ according to common clock bindings for the clocks described
+ in the clock-names property;
+- clock-names : should contain "cis_extclk" entry for the CIS_EXTCLK clock;
+
+Optional properties:
+
+- clock-frequency : the frequency at which the "cis_extclk" clock should be
+ configured to operate, in Hz; if this property is not
+ specified default 24 MHz value will be used.
+
+The common video interfaces bindings (see video-interfaces.txt) should be used
+to specify link from the S5C73M3 to an external image data receiver. The S5C73M3
+device node should contain one 'port' child node with an 'endpoint' subnode for
+this purpose. The data link from a raw image sensor to the S5C73M3 can be
+similarly specified, but it is optional since the S5C73M3 ISP and a raw image
+sensor are usually inseparable and form a hybrid module.
+
+Following properties are valid for the endpoint node(s):
+
+endpoint subnode
+----------------
+
+- data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in
+ video-interfaces.txt. This sensor doesn't support data lane remapping
+ and physical lane indexes in subsequent elements of the array should
+ be only consecutive ascending values.
+
+SPI device node
+---------------
+
+Required properties:
+
+- compatible : "samsung,s5c73m3";
+
+For more details see description of the SPI busses bindings
+(../spi/spi-bus.txt) and bindings of a specific bus controller.
+
+Example:
+
+i2c@138A000000 {
+ ...
+ s5c73m3@3c {
+ compatible = "samsung,s5c73m3";
+ reg = <0x3c>;
+ vdd-int-supply = <&buck9_reg>;
+ vdda-supply = <&ldo17_reg>;
+ vdd-reg-supply = <&cam_io_reg>;
+ vddio-host-supply = <&ldo18_reg>;
+ vddio-cis-supply = <&ldo9_reg>;
+ vdd-af-supply = <&cam_af_reg>;
+ clock-frequency = <24000000>;
+ clocks = <&clk 0>;
+ clock-names = "cis_extclk";
+ reset-gpios = <&gpf1 3 1>;
+ standby-gpios = <&gpm0 1 1>;
+ port {
+ s5c73m3_ep: endpoint {
+ remote-endpoint = <&csis0_ep>;
+ data-lanes = <1 2 3 4>;
+ };
+ };
+ };
+};
+
+spi@1392000 {
+ ...
+ s5c73m3_spi: s5c73m3@0 {
+ compatible = "samsung,s5c73m3";
+ reg = <0>;
+ ...
+ };
+};
diff --git a/Documentation/devicetree/bindings/media/samsung-s5k6a3.txt b/Documentation/devicetree/bindings/media/samsung-s5k6a3.txt
new file mode 100644
index 000000000000..cce01e82f3e3
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/samsung-s5k6a3.txt
@@ -0,0 +1,33 @@
+Samsung S5K6A3(YX) raw image sensor
+---------------------------------
+
+S5K6A3(YX) is a raw image sensor with MIPI CSI-2 and CCP2 image data interfaces
+and CCI (I2C compatible) control bus.
+
+Required properties:
+
+- compatible : "samsung,s5k6a3";
+- reg : I2C slave address of the sensor;
+- svdda-supply : core voltage supply;
+- svddio-supply : I/O voltage supply;
+- afvdd-supply : AF (actuator) voltage supply;
+- gpios : specifier of a GPIO connected to the RESET pin;
+- clocks : should contain list of phandle and clock specifier pairs
+ according to common clock bindings for the clocks described
+ in the clock-names property;
+- clock-names : should contain "extclk" entry for the sensor's EXTCLK clock;
+
+Optional properties:
+
+- clock-frequency : the frequency at which the "extclk" clock should be
+ configured to operate, in Hz; if this property is not
+ specified default 24 MHz value will be used.
+
+The common video interfaces bindings (see video-interfaces.txt) should be
+used to specify link to the image data receiver. The S5K6A3(YX) device
+node should contain one 'port' child node with an 'endpoint' subnode.
+
+Following properties are valid for the endpoint node:
+
+- data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in
+ video-interfaces.txt. The sensor supports only one data lane.
diff --git a/Documentation/devicetree/bindings/mfd/arizona.txt b/Documentation/devicetree/bindings/mfd/arizona.txt
index 0e295c9d8937..36a0c3d8c726 100644
--- a/Documentation/devicetree/bindings/mfd/arizona.txt
+++ b/Documentation/devicetree/bindings/mfd/arizona.txt
@@ -5,9 +5,10 @@ of analogue I/O.
Required properties:
- - compatible : one of the following chip-specific strings:
- "wlf,wm5102"
- "wlf,wm5110"
+ - compatible : One of the following chip-specific strings:
+ "wlf,wm5102"
+ "wlf,wm5110"
+ "wlf,wm8997"
- reg : I2C slave address when connected using I2C, chip select number when
using SPI.
@@ -25,8 +26,9 @@ Required properties:
- #gpio-cells : Must be 2. The first cell is the pin number and the
second cell is used to specify optional parameters (currently unused).
- - AVDD1-supply, DBVDD1-supply, DBVDD2-supply, DBVDD3-supply, CPVDD-supply,
- SPKVDDL-supply, SPKVDDR-supply : power supplies for the device, as covered
+ - AVDD-supply, DBVDD1-supply, DBVDD2-supply, DBVDD3-supply (wm5102, wm5110),
+ CPVDD-supply, SPKVDDL-supply (wm5102, wm5110), SPKVDDR-supply (wm5102,
+ wm5110), SPKVDD-supply (wm8997) : Power supplies for the device, as covered
in Documentation/devicetree/bindings/regulator/regulator.txt
Optional properties:
@@ -46,6 +48,7 @@ codec: wm5102@1a {
compatible = "wlf,wm5102";
reg = <0x1a>;
interrupts = <347>;
+ interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gic>;
@@ -53,10 +56,10 @@ codec: wm5102@1a {
#gpio-cells = <2>;
wlf,gpio-defaults = <
- 0x00000000, /* AIF1TXLRCLK */
- 0xffffffff,
- 0xffffffff,
- 0xffffffff,
- 0xffffffff,
+ 0x00000000 /* AIF1TXLRCLK */
+ 0xffffffff
+ 0xffffffff
+ 0xffffffff
+ 0xffffffff
>;
};
diff --git a/Documentation/devicetree/bindings/mfd/bcm590xx.txt b/Documentation/devicetree/bindings/mfd/bcm590xx.txt
new file mode 100644
index 000000000000..1fe30e2b10da
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/bcm590xx.txt
@@ -0,0 +1,37 @@
+-------------------------------
+BCM590xx Power Management Units
+-------------------------------
+
+Required properties:
+- compatible: "brcm,bcm59056"
+- reg: I2C slave address
+- interrupts: interrupt for the PMU. Generic interrupt client node bindings
+ are described in interrupt-controller/interrupts.txt
+
+------------------
+Voltage Regulators
+------------------
+
+Optional child nodes:
+- regulators: container node for regulators following the generic
+ regulator binding in regulator/regulator.txt
+
+ The valid regulator node names for BCM59056 are:
+ rfldo, camldo1, camldo2, simldo1, simldo2, sdldo, sdxldo,
+ mmcldo1, mmcldo2, audldo, micldo, usbldo, vibldo,
+ csr, iosr1, iosr2, msr, sdsr1, sdsr2, vsr
+
+Example:
+ pmu: bcm59056@8 {
+ compatible = "brcm,bcm59056";
+ reg = <0x08>;
+ interrupts = <GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>;
+ regulators {
+ rfldo_reg: rfldo {
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ ...
+ };
+ };
diff --git a/Documentation/devicetree/bindings/mfd/da9055.txt b/Documentation/devicetree/bindings/mfd/da9055.txt
new file mode 100644
index 000000000000..6dab34d34fce
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/da9055.txt
@@ -0,0 +1,72 @@
+* Dialog DA9055 Power Management Integrated Circuit (PMIC)
+
+DA9055 consists of a large and varied group of sub-devices (I2C Only):
+
+Device Supply Names Description
+------ ------------ -----------
+da9055-gpio : : GPIOs
+da9055-regulator : : Regulators
+da9055-onkey : : On key
+da9055-rtc : : RTC
+da9055-hwmon : : ADC
+da9055-watchdog : : Watchdog
+
+The CODEC device in DA9055 has a separate, configurable I2C address and so
+is instantiated separately from the PMIC.
+
+For details on accompanying CODEC I2C device, see the following:
+Documentation/devicetree/bindings/sound/da9055.txt
+
+======
+
+Required properties:
+- compatible : Should be "dlg,da9055-pmic"
+- reg: Specifies the I2C slave address (defaults to 0x5a but can be modified)
+- interrupt-parent: Specifies the phandle of the interrupt controller to which
+ the IRQs from da9055 are delivered to.
+- interrupts: IRQ line info for da9055 chip.
+- interrupt-controller: da9055 has internal IRQs (has own IRQ domain).
+- #interrupt-cells: Should be 1, is the local IRQ number for da9055.
+
+Sub-nodes:
+- regulators : Contain the regulator nodes. The DA9055 regulators are
+ bound using their names as listed below:
+
+ buck1 : regulator BUCK1
+ buck2 : regulator BUCK2
+ ldo1 : regulator LDO1
+ ldo2 : regulator LDO2
+ ldo3 : regulator LDO3
+ ldo4 : regulator LDO4
+ ldo5 : regulator LDO5
+ ldo6 : regulator LDO6
+
+ The bindings details of individual regulator device can be found in:
+ Documentation/devicetree/bindings/regulator/regulator.txt
+
+
+Example:
+
+ pmic: da9055-pmic@5a {
+ compatible = "dlg,da9055-pmic";
+ reg = <0x5a>;
+ interrupt-parent = <&intc>;
+ interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ regulators {
+ buck1: BUCK1 {
+ regulator-min-microvolt = <725000>;
+ regulator-max-microvolt = <2075000>;
+ };
+ buck2: BUCK2 {
+ regulator-min-microvolt = <925000>;
+ regulator-max-microvolt = <2500000>;
+ };
+ ldo1: LDO1 {
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/mfd/omap-usb-host.txt b/Documentation/devicetree/bindings/mfd/omap-usb-host.txt
index b381fa696bf9..4721b2d521e4 100644
--- a/Documentation/devicetree/bindings/mfd/omap-usb-host.txt
+++ b/Documentation/devicetree/bindings/mfd/omap-usb-host.txt
@@ -32,6 +32,29 @@ Optional properties:
- single-ulpi-bypass: Must be present if the controller contains a single
ULPI bypass control bit. e.g. OMAP3 silicon <= ES2.1
+- clocks: a list of phandles and clock-specifier pairs, one for each entry in
+ clock-names.
+
+- clock-names: should include:
+ For OMAP3
+ * "usbhost_120m_fck" - 120MHz Functional clock.
+
+ For OMAP4+
+ * "refclk_60m_int" - 60MHz internal reference clock for UTMI clock mux
+ * "refclk_60m_ext_p1" - 60MHz external ref. clock for Port 1's UTMI clock mux.
+ * "refclk_60m_ext_p2" - 60MHz external ref. clock for Port 2's UTMI clock mux
+ * "utmi_p1_gfclk" - Port 1 UTMI clock mux.
+ * "utmi_p2_gfclk" - Port 2 UTMI clock mux.
+ * "usb_host_hs_utmi_p1_clk" - Port 1 UTMI clock gate.
+ * "usb_host_hs_utmi_p2_clk" - Port 2 UTMI clock gate.
+ * "usb_host_hs_utmi_p3_clk" - Port 3 UTMI clock gate.
+ * "usb_host_hs_hsic480m_p1_clk" - Port 1 480MHz HSIC clock gate.
+ * "usb_host_hs_hsic480m_p2_clk" - Port 2 480MHz HSIC clock gate.
+ * "usb_host_hs_hsic480m_p3_clk" - Port 3 480MHz HSIC clock gate.
+ * "usb_host_hs_hsic60m_p1_clk" - Port 1 60MHz HSIC clock gate.
+ * "usb_host_hs_hsic60m_p2_clk" - Port 2 60MHz HSIC clock gate.
+ * "usb_host_hs_hsic60m_p3_clk" - Port 3 60MHz HSIC clock gate.
+
Required properties if child node exists:
- #address-cells: Must be 1
diff --git a/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt b/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt
index 62fe69724e3b..c58d70437fce 100644
--- a/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt
+++ b/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt
@@ -7,6 +7,16 @@ Required properties:
- interrupts : should contain the TLL module's interrupt
- ti,hwmod : must contain "usb_tll_hs"
+Optional properties:
+
+- clocks: a list of phandles and clock-specifier pairs, one for each entry in
+ clock-names.
+
+- clock-names: should include:
+ * "usb_tll_hs_usb_ch0_clk" - USB TLL channel 0 clock
+ * "usb_tll_hs_usb_ch1_clk" - USB TLL channel 1 clock
+ * "usb_tll_hs_usb_ch2_clk" - USB TLL channel 2 clock
+
Example:
usbhstll: usbhstll@4a062000 {
diff --git a/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt b/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt
new file mode 100644
index 000000000000..03518dc8b6bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt
@@ -0,0 +1,96 @@
+Qualcomm PM8xxx PMIC multi-function devices
+
+The PM8xxx family of Power Management ICs are used to provide regulated
+voltages and other various functionality to Qualcomm SoCs.
+
+= PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,pm8058"
+ "qcom,pm8921"
+
+- #address-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: must be 1
+
+- #size-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: must be 0
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: specifies the interrupt that indicates a subdevice
+ has generated an interrupt (summary interrupt). The
+ format of the specifier is defined by the binding document
+ describing the node's interrupt parent.
+
+- #interrupt-cells:
+ Usage: required
+ Value type : <u32>
+ Definition: must be 2. Specifies the number of cells needed to encode
+ an interrupt source. The 1st cell contains the interrupt
+ number. The 2nd cell is the trigger type and level flags
+ encoded as follows:
+
+ 1 = low-to-high edge triggered
+ 2 = high-to-low edge triggered
+ 4 = active high level-sensitive
+ 8 = active low level-sensitive
+
+- interrupt-controller:
+ Usage: required
+ Value type: <empty>
+ Definition: identifies this node as an interrupt controller
+
+= SUBCOMPONENTS
+
+The PMIC contains multiple independent functions, each described in a subnode.
+The below bindings specify the set of valid subnodes.
+
+== Real-Time Clock
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,pm8058-rtc"
+ "qcom,pm8921-rtc"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: single entry specifying the base address of the RTC registers
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: single entry specifying the RTC's alarm interrupt
+
+- allow-set-time:
+ Usage: optional
+ Value type: <empty>
+ Definition: indicates that the setting of RTC time is allowed by
+ the host CPU
+
+= EXAMPLE
+
+ pmicintc: pmic@0 {
+ compatible = "qcom,pm8921";
+ interrupts = <104 8>;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc@11d {
+ compatible = "qcom,pm8921-rtc";
+ reg = <0x11d>;
+ interrupts = <0x27 0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/mfd/s2mps11.txt b/Documentation/devicetree/bindings/mfd/s2mps11.txt
index f69bec294f02..802e839b0829 100644
--- a/Documentation/devicetree/bindings/mfd/s2mps11.txt
+++ b/Documentation/devicetree/bindings/mfd/s2mps11.txt
@@ -16,20 +16,25 @@ Optional properties:
- interrupts: Interrupt specifiers for interrupt sources.
Optional nodes:
-- clocks: s2mps11 provides 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 :
+- clocks: s2mps11 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
- ----------------------
- 32KhzAP 0
- 32KhzCP 1
- 32KhzBT 2
+ Clock ID Devices
+ ----------------------------------------------------------
+ 32KhzAP 0 S2MPS11, S2MPS14, S5M8767
+ 32KhzCP 1 S2MPS11, S5M8767
+ 32KhzBT 2 S2MPS11, S2MPS14, S5M8767
+
+ - compatible: Should be one of: "samsung,s2mps11-clk", "samsung,s2mps14-clk",
+ "samsung,s5m8767-clk"
- regulators: The regulators of s2mps11 that have to be instantiated should be
included in a sub-node named 'regulators'. Regulator nodes included in this
@@ -75,7 +80,8 @@ Example:
compatible = "samsung,s2mps11-pmic";
reg = <0x66>;
- s2m_osc: clocks{
+ s2m_osc: clocks {
+ compatible = "samsung,s2mps11-clk";
#clock-cells = 1;
clock-output-names = "xx", "yy", "zz";
};
diff --git a/Documentation/devicetree/bindings/mmc/socfpga-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/socfpga-dw-mshc.txt
new file mode 100644
index 000000000000..4897bea7e3f8
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/socfpga-dw-mshc.txt
@@ -0,0 +1,23 @@
+* Altera SOCFPGA specific extensions to the Synopsys Designware Mobile
+ Storage Host Controller
+
+The Synopsys designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core Synopsys dw mshc controller properties described
+by synopsys-dw-mshc.txt and the properties used by the Altera SOCFPGA specific
+extensions to the Synopsys Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be
+ - "altr,socfpga-dw-mshc": for Altera's SOCFPGA platform
+
+Example:
+
+ mmc: dwmmc0@ff704000 {
+ compatible = "altr,socfpga-dw-mshc";
+ reg = <0xff704000 0x1000>;
+ interrupts = <0 129 4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt
index 03855c8c492a..b53f92e252d4 100644
--- a/Documentation/devicetree/bindings/mtd/nand.txt
+++ b/Documentation/devicetree/bindings/mtd/nand.txt
@@ -5,3 +5,17 @@
"soft_bch".
- nand-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
+
+- nand-ecc-strength: integer representing the number of bits to correct
+ per ECC step.
+
+- nand-ecc-step-size: integer representing the number of data bytes
+ that are covered by a single ECC step.
+
+The ECC strength and ECC step size properties define the correction capability
+of a controller. Together, they say a controller can correct "{strength} bit
+errors per {size} bytes".
+
+The interpretation of these parameters is implementation-defined, so not all
+implementations must support all possible combinations. However, implementations
+are encouraged to further specify the value(s) they support.
diff --git a/Documentation/devicetree/bindings/mtd/st-fsm.txt b/Documentation/devicetree/bindings/mtd/st-fsm.txt
new file mode 100644
index 000000000000..c2489391c437
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/st-fsm.txt
@@ -0,0 +1,26 @@
+* ST-Microelectronics SPI FSM Serial (NOR) Flash Controller
+
+Required properties:
+ - compatible : Should be "st,spi-fsm"
+ - reg : Contains register's location and length.
+ - reg-names : Should contain the reg names "spi-fsm"
+ - interrupts : The interrupt number
+ - pinctrl-0 : Standard Pinctrl phandle (see: pinctrl/pinctrl-bindings.txt)
+
+Optional properties:
+ - st,syscfg : Phandle to boot-device system configuration registers
+ - st,boot-device-reg : Address of the aforementioned boot-device register(s)
+ - st,boot-device-spi : Expected boot-device value if booted via this device
+
+Example:
+ spifsm: spifsm@fe902000{
+ compatible = "st,spi-fsm";
+ reg = <0xfe902000 0x1000>;
+ reg-names = "spi-fsm";
+ pinctrl-0 = <&pinctrl_fsm>;
+ st,syscfg = <&syscfg_rear>;
+ st,boot-device-reg = <0x958>;
+ st,boot-device-spi = <0x1a>;
+ status = "okay";
+ };
+
diff --git a/Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt b/Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt
index 863d5b8155c7..10640b17c866 100644
--- a/Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt
+++ b/Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt
@@ -5,13 +5,9 @@ Required properties:
"allwinner,sun4i-emac")
- reg: address and length of the register set for the device.
- interrupts: interrupt for the device
-- phy: A phandle to a phy node defining the PHY address (as the reg
- property, a single integer).
+- phy: see ethernet.txt file in the same directory.
- clocks: A phandle to the reference clock for this device
-Optional properties:
-- (local-)mac-address: mac address to be used by this driver
-
Example:
emac: ethernet@01c0b000 {
diff --git a/Documentation/devicetree/bindings/net/altera_tse.txt b/Documentation/devicetree/bindings/net/altera_tse.txt
new file mode 100644
index 000000000000..a706297998e9
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/altera_tse.txt
@@ -0,0 +1,114 @@
+* Altera Triple-Speed Ethernet MAC driver (TSE)
+
+Required properties:
+- compatible: Should be "altr,tse-1.0" for legacy SGDMA based TSE, and should
+ be "altr,tse-msgdma-1.0" for the preferred MSGDMA based TSE.
+ ALTR is supported for legacy device trees, but is deprecated.
+ altr should be used for all new designs.
+- reg: Address and length of the register set for the device. It contains
+ the information of registers in the same order as described by reg-names
+- reg-names: Should contain the reg names
+ "control_port": MAC configuration space region
+ "tx_csr": xDMA Tx dispatcher control and status space region
+ "tx_desc": MSGDMA Tx dispatcher descriptor space region
+ "rx_csr" : xDMA Rx dispatcher control and status space region
+ "rx_desc": MSGDMA Rx dispatcher descriptor space region
+ "rx_resp": MSGDMA Rx dispatcher response space region
+ "s1": SGDMA descriptor memory
+- interrupts: Should contain the TSE interrupts and it's mode.
+- interrupt-names: Should contain the interrupt names
+ "rx_irq": xDMA Rx dispatcher interrupt
+ "tx_irq": xDMA Tx dispatcher interrupt
+- rx-fifo-depth: MAC receive FIFO buffer depth in bytes
+- tx-fifo-depth: MAC transmit FIFO buffer depth in bytes
+- phy-mode: See ethernet.txt in the same directory.
+- phy-handle: See ethernet.txt in the same directory.
+- phy-addr: See ethernet.txt in the same directory. A configuration should
+ include phy-handle or phy-addr.
+- altr,has-supplementary-unicast:
+ If present, TSE supports additional unicast addresses.
+ Otherwise additional unicast addresses are not supported.
+- altr,has-hash-multicast-filter:
+ If present, TSE supports a hash based multicast filter.
+ Otherwise, hash-based multicast filtering is not supported.
+
+- mdio device tree subnode: When the TSE has a phy connected to its local
+ mdio, there must be device tree subnode with the following
+ required properties:
+
+ - compatible: Must be "altr,tse-mdio".
+ - #address-cells: Must be <1>.
+ - #size-cells: Must be <0>.
+
+ For each phy on the mdio bus, there must be a node with the following
+ fields:
+
+ - reg: phy id used to communicate to phy.
+ - device_type: Must be "ethernet-phy".
+
+Optional properties:
+- local-mac-address: See ethernet.txt in the same directory.
+- max-frame-size: See ethernet.txt in the same directory.
+
+Example:
+
+ tse_sub_0_eth_tse_0: ethernet@0x1,00000000 {
+ compatible = "altr,tse-msgdma-1.0";
+ reg = <0x00000001 0x00000000 0x00000400>,
+ <0x00000001 0x00000460 0x00000020>,
+ <0x00000001 0x00000480 0x00000020>,
+ <0x00000001 0x000004A0 0x00000008>,
+ <0x00000001 0x00000400 0x00000020>,
+ <0x00000001 0x00000420 0x00000020>;
+ reg-names = "control_port", "rx_csr", "rx_desc", "rx_resp", "tx_csr", "tx_desc";
+ interrupt-parent = <&hps_0_arm_gic_0>;
+ interrupts = <0 41 4>, <0 40 4>;
+ interrupt-names = "rx_irq", "tx_irq";
+ rx-fifo-depth = <2048>;
+ tx-fifo-depth = <2048>;
+ address-bits = <48>;
+ max-frame-size = <1500>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ phy-mode = "gmii";
+ altr,has-supplementary-unicast;
+ altr,has-hash-multicast-filter;
+ phy-handle = <&phy0>;
+ mdio {
+ compatible = "altr,tse-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ phy0: ethernet-phy@0 {
+ reg = <0x0>;
+ device_type = "ethernet-phy";
+ };
+
+ phy1: ethernet-phy@1 {
+ reg = <0x1>;
+ device_type = "ethernet-phy";
+ };
+
+ };
+ };
+
+ tse_sub_1_eth_tse_0: ethernet@0x1,00001000 {
+ compatible = "altr,tse-msgdma-1.0";
+ reg = <0x00000001 0x00001000 0x00000400>,
+ <0x00000001 0x00001460 0x00000020>,
+ <0x00000001 0x00001480 0x00000020>,
+ <0x00000001 0x000014A0 0x00000008>,
+ <0x00000001 0x00001400 0x00000020>,
+ <0x00000001 0x00001420 0x00000020>;
+ reg-names = "control_port", "rx_csr", "rx_desc", "rx_resp", "tx_csr", "tx_desc";
+ interrupt-parent = <&hps_0_arm_gic_0>;
+ interrupts = <0 43 4>, <0 42 4>;
+ interrupt-names = "rx_irq", "tx_irq";
+ rx-fifo-depth = <2048>;
+ tx-fifo-depth = <2048>;
+ address-bits = <48>;
+ max-frame-size = <1500>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ phy-mode = "gmii";
+ altr,has-supplementary-unicast;
+ altr,has-hash-multicast-filter;
+ phy-handle = <&phy1>;
+ };
diff --git a/Documentation/devicetree/bindings/net/arc_emac.txt b/Documentation/devicetree/bindings/net/arc_emac.txt
index bcbc3f009158..7fbb027218a1 100644
--- a/Documentation/devicetree/bindings/net/arc_emac.txt
+++ b/Documentation/devicetree/bindings/net/arc_emac.txt
@@ -6,19 +6,12 @@ Required properties:
- interrupts: Should contain the EMAC interrupts
- clock-frequency: CPU frequency. It is needed to calculate and set polling
period of EMAC.
-- max-speed: Maximum supported data-rate in Mbit/s. In some HW configurations
-bandwidth of external memory controller might be a limiting factor. That's why
-it's required to specify which data-rate is supported on current SoC or FPGA.
-For example if only 10 Mbit/s is supported (10BASE-T) set "10". If 100 Mbit/s is
-supported (100BASE-TX) set "100".
-- phy: PHY device attached to the EMAC via MDIO bus
+- max-speed: see ethernet.txt file in the same directory.
+- phy: see ethernet.txt file in the same directory.
Child nodes of the driver are the individual PHY devices connected to the
MDIO bus. They must have a "reg" property given the PHY address on the MDIO bus.
-Optional properties:
-- mac-address: 6 bytes, mac address
-
Examples:
ethernet@c0fc2000 {
diff --git a/Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt b/Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt
new file mode 100644
index 000000000000..f2febb94550e
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt
@@ -0,0 +1,121 @@
+* Broadcom BCM7xxx Ethernet Controller (GENET)
+
+Required properties:
+- compatible: should contain one of "brcm,genet-v1", "brcm,genet-v2",
+ "brcm,genet-v3", "brcm,genet-v4".
+- reg: address and length of the register set for the device
+- interrupts: must be two cells, the first cell is the general purpose
+ interrupt line, while the second cell is the interrupt for the ring
+ RX and TX queues operating in ring mode
+- phy-mode: see ethernet.txt file in the same directory
+- #address-cells: should be 1
+- #size-cells: should be 1
+
+Optional properties:
+- clocks: When provided, must be two phandles to the functional clocks nodes
+ of the GENET block. The first phandle is the main GENET clock used during
+ normal operation, while the second phandle is the Wake-on-LAN clock.
+- clock-names: When provided, names of the functional clock phandles, first
+ name should be "enet" and second should be "enet-wol".
+
+- phy-handle: See ethernet.txt file in the same directory; used to describe
+ configurations where a PHY (internal or external) is used.
+
+- fixed-link: When the GENET interface is connected to a MoCA hardware block or
+ when operating in a RGMII to RGMII type of connection, or when the MDIO bus is
+ voluntarily disabled, this property should be used to describe the "fixed link".
+ See Documentation/devicetree/bindings/net/fsl-tsec-phy.txt for information on
+ the property specifics
+
+Required child nodes:
+
+- mdio bus node: this node should always be present regarless of the PHY
+ configuration of the GENET instance
+
+MDIO bus node required properties:
+
+- compatible: should contain one of "brcm,genet-mdio-v1", "brcm,genet-mdio-v2"
+ "brcm,genet-mdio-v3", "brcm,genet-mdio-v4", the version has to match the
+ parent node compatible property (e.g: brcm,genet-v4 pairs with
+ brcm,genet-mdio-v4)
+- reg: address and length relative to the parent node base register address
+- #address-cells: address cell for MDIO bus addressing, should be 1
+- #size-cells: size of the cells for MDIO bus addressing, should be 0
+
+Ethernet PHY node properties:
+
+See Documentation/devicetree/bindings/net/phy.txt for the list of required and
+optional properties.
+
+Internal Gigabit PHY example:
+
+ethernet@f0b60000 {
+ phy-mode = "internal";
+ phy-handle = <&phy1>;
+ mac-address = [ 00 10 18 36 23 1a ];
+ compatible = "brcm,genet-v4";
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ reg = <0xf0b60000 0xfc4c>;
+ interrupts = <0x0 0x14 0x0>, <0x0 0x15 0x0>;
+
+ mdio@e14 {
+ compatible = "brcm,genet-mdio-v4";
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ reg = <0xe14 0x8>;
+
+ phy1: ethernet-phy@1 {
+ max-speed = <1000>;
+ reg = <0x1>;
+ compatible = "brcm,28nm-gphy", "ethernet-phy-ieee802.3-c22";
+ };
+ };
+};
+
+MoCA interface / MAC to MAC example:
+
+ethernet@f0b80000 {
+ phy-mode = "moca";
+ fixed-link = <1 0 1000 0 0>;
+ mac-address = [ 00 10 18 36 24 1a ];
+ compatible = "brcm,genet-v4";
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ reg = <0xf0b80000 0xfc4c>;
+ interrupts = <0x0 0x16 0x0>, <0x0 0x17 0x0>;
+
+ mdio@e14 {
+ compatible = "brcm,genet-mdio-v4";
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ reg = <0xe14 0x8>;
+ };
+};
+
+
+External MDIO-connected Gigabit PHY/switch:
+
+ethernet@f0ba0000 {
+ phy-mode = "rgmii";
+ phy-handle = <&phy0>;
+ mac-address = [ 00 10 18 36 26 1a ];
+ compatible = "brcm,genet-v4";
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ reg = <0xf0ba0000 0xfc4c>;
+ interrupts = <0x0 0x18 0x0>, <0x0 0x19 0x0>;
+
+ mdio@0e14 {
+ compatible = "brcm,genet-mdio-v4";
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ reg = <0xe14 0x8>;
+
+ phy0: ethernet-phy@0 {
+ max-speed = <1000>;
+ reg = <0x0>;
+ compatible = "brcm,bcm53125", "ethernet-phy-ieee802.3-c22";
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/net/can/sja1000.txt b/Documentation/devicetree/bindings/net/can/sja1000.txt
index f2105a47ec87..b4a6d53fb01a 100644
--- a/Documentation/devicetree/bindings/net/can/sja1000.txt
+++ b/Documentation/devicetree/bindings/net/can/sja1000.txt
@@ -12,6 +12,10 @@ Required properties:
Optional properties:
+- reg-io-width : Specify the size (in bytes) of the IO accesses that
+ should be performed on the device. Valid value is 1, 2 or 4.
+ Default to 1 (8 bits).
+
- nxp,external-clock-frequency : Frequency of the external oscillator
clock in Hz. Note that the internal clock frequency used by the
SJA1000 is half of that value. If not specified, a default value
diff --git a/Documentation/devicetree/bindings/net/cavium-mix.txt b/Documentation/devicetree/bindings/net/cavium-mix.txt
index 5da628db68bf..8d7c3096390f 100644
--- a/Documentation/devicetree/bindings/net/cavium-mix.txt
+++ b/Documentation/devicetree/bindings/net/cavium-mix.txt
@@ -18,12 +18,7 @@ Properties:
- interrupts: Two interrupt specifiers. The first is the MIX
interrupt routing and the second the routing for the AGL interrupts.
-- mac-address: Optional, the MAC address to assign to the device.
-
-- local-mac-address: Optional, the MAC address to assign to the device
- if mac-address is not specified.
-
-- phy-handle: Optional, a phandle for the PHY device connected to this device.
+- phy-handle: Optional, see ethernet.txt file in the same directory.
Example:
ethernet@1070000100800 {
diff --git a/Documentation/devicetree/bindings/net/cavium-pip.txt b/Documentation/devicetree/bindings/net/cavium-pip.txt
index d4c53ba04b3b..7dbd158810d2 100644
--- a/Documentation/devicetree/bindings/net/cavium-pip.txt
+++ b/Documentation/devicetree/bindings/net/cavium-pip.txt
@@ -35,12 +35,7 @@ Properties for PIP port which is a child the PIP interface:
- reg: The port number within the interface group.
-- mac-address: Optional, the MAC address to assign to the device.
-
-- local-mac-address: Optional, the MAC address to assign to the device
- if mac-address is not specified.
-
-- phy-handle: Optional, a phandle for the PHY device connected to this device.
+- phy-handle: Optional, see ethernet.txt file in the same directory.
Example:
diff --git a/Documentation/devicetree/bindings/net/cdns-emac.txt b/Documentation/devicetree/bindings/net/cdns-emac.txt
index 09055c2495f0..abd67c13d344 100644
--- a/Documentation/devicetree/bindings/net/cdns-emac.txt
+++ b/Documentation/devicetree/bindings/net/cdns-emac.txt
@@ -6,11 +6,7 @@ Required properties:
or the generic form: "cdns,emac".
- reg: Address and length of the register set for the device
- interrupts: Should contain macb interrupt
-- phy-mode: String, operation mode of the PHY interface.
- Supported values are: "mii", "rmii".
-
-Optional properties:
-- local-mac-address: 6 bytes, mac address
+- phy-mode: see ethernet.txt file in the same directory.
Examples:
diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt
index 05d660e4ac64..ae2b8b7f9c38 100644
--- a/Documentation/devicetree/bindings/net/cpsw.txt
+++ b/Documentation/devicetree/bindings/net/cpsw.txt
@@ -28,9 +28,8 @@ Optional properties:
Slave Properties:
Required properties:
- phy_id : Specifies slave phy id
-- phy-mode : The interface between the SoC and the PHY (a string
- that of_get_phy_mode() can understand)
-- mac-address : Specifies slave MAC address
+- phy-mode : See ethernet.txt file in the same directory
+- mac-address : See ethernet.txt file in the same directory
Optional properties:
- dual_emac_res_vlan : Specifies VID to be used to segregate the ports
diff --git a/Documentation/devicetree/bindings/net/davicom-dm9000.txt b/Documentation/devicetree/bindings/net/davicom-dm9000.txt
index 2d39c990e641..28767ed7c1bd 100644
--- a/Documentation/devicetree/bindings/net/davicom-dm9000.txt
+++ b/Documentation/devicetree/bindings/net/davicom-dm9000.txt
@@ -9,8 +9,6 @@ Required properties:
- interrupts : interrupt specifier specific to interrupt controller
Optional properties:
-- local-mac-address : A bytestring of 6 bytes specifying Ethernet MAC address
- to use (from firmware or bootloader)
- davicom,no-eeprom : Configuration EEPROM is not available
- davicom,ext-phy : Use external PHY
diff --git a/Documentation/devicetree/bindings/net/davinci_emac.txt b/Documentation/devicetree/bindings/net/davinci_emac.txt
index 6e356d15154a..032808843f90 100644
--- a/Documentation/devicetree/bindings/net/davinci_emac.txt
+++ b/Documentation/devicetree/bindings/net/davinci_emac.txt
@@ -17,9 +17,8 @@ Required properties:
Miscellaneous Interrupt>
Optional properties:
-- phy-handle: Contains a phandle to an Ethernet PHY.
+- phy-handle: See ethernet.txt file in the same directory.
If absent, davinci_emac driver defaults to 100/FULL.
-- local-mac-address : 6 bytes, mac address
- ti,davinci-rmii-en: 1 byte, 1 means use RMII
- ti,davinci-no-bd-ram: boolean, does EMAC have BD RAM?
diff --git a/Documentation/devicetree/bindings/net/ethernet.txt b/Documentation/devicetree/bindings/net/ethernet.txt
new file mode 100644
index 000000000000..9ecd43d8792c
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/ethernet.txt
@@ -0,0 +1,25 @@
+The following properties are common to the Ethernet controllers:
+
+- local-mac-address: array of 6 bytes, specifies the MAC address that was
+ assigned to the network device;
+- mac-address: array of 6 bytes, specifies the MAC address that was last used by
+ the boot program; should be used in cases where the MAC address assigned to
+ the device by the boot program is different from the "local-mac-address"
+ property;
+- max-speed: number, specifies maximum speed in Mbit/s supported by the device;
+- max-frame-size: number, maximum transfer unit (IEEE defined MTU), rather than
+ the maximum frame size (there's contradiction in ePAPR).
+- phy-mode: string, operation mode of the PHY interface; supported values are
+ "mii", "gmii", "sgmii", "tbi", "rev-mii", "rmii", "rgmii", "rgmii-id",
+ "rgmii-rxid", "rgmii-txid", "rtbi", "smii", "xgmii"; this is now a de-facto
+ standard property;
+- phy-connection-type: the same as "phy-mode" property but described in ePAPR;
+- phy-handle: phandle, specifies a reference to a node representing a PHY
+ device; this property is described in ePAPR and so preferred;
+- phy: the same as "phy-handle" property, not recommended for new bindings.
+- phy-device: the same as "phy-handle" property, not recommended for new
+ bindings.
+
+Child nodes of the Ethernet controller are typically the individual PHY devices
+connected via the MDIO bus (sometimes the MDIO bus controller is separate).
+They are described in the phy.txt file in this same directory.
diff --git a/Documentation/devicetree/bindings/net/fsl-fec.txt b/Documentation/devicetree/bindings/net/fsl-fec.txt
index 845ff848d895..6bc84adb10c0 100644
--- a/Documentation/devicetree/bindings/net/fsl-fec.txt
+++ b/Documentation/devicetree/bindings/net/fsl-fec.txt
@@ -4,12 +4,9 @@ Required properties:
- compatible : Should be "fsl,<soc>-fec"
- reg : Address and length of the register set for the device
- interrupts : Should contain fec interrupt
-- phy-mode : String, operation mode of the PHY interface.
- Supported values are: "mii", "gmii", "sgmii", "tbi", "rmii",
- "rgmii", "rgmii-id", "rgmii-rxid", "rgmii-txid", "rtbi", "smii".
+- phy-mode : See ethernet.txt file in the same directory
Optional properties:
-- local-mac-address : 6 bytes, mac address
- phy-reset-gpios : Should specify the gpio for phy reset
- phy-reset-duration : Reset duration in milliseconds. Should present
only if property "phy-reset-gpios" is available. Missing the property
diff --git a/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt b/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
index d2ea4605d078..737cdef4f903 100644
--- a/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
+++ b/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
@@ -38,22 +38,17 @@ Properties:
- model : Model of the device. Can be "TSEC", "eTSEC", or "FEC"
- compatible : Should be "gianfar"
- reg : Offset and length of the register set for the device
- - local-mac-address : List of bytes representing the ethernet address of
- this controller
- interrupts : For FEC devices, the first interrupt is the device's
interrupt. For TSEC and eTSEC devices, the first interrupt is
transmit, the second is receive, and the third is error.
- - phy-handle : The phandle for the PHY connected to this ethernet
- controller.
+ - phy-handle : See ethernet.txt file in the same directory.
- fixed-link : <a b c d e> where a is emulated phy id - choose any,
but unique to the all specified fixed-links, b is duplex - 0 half,
1 full, c is link speed - d#10/d#100/d#1000, d is pause - 0 no
pause, 1 pause, e is asym_pause - 0 no asym_pause, 1 asym_pause.
- - phy-connection-type : a string naming the controller/PHY interface type,
- i.e., "mii" (default), "rmii", "gmii", "rgmii", "rgmii-id", "sgmii",
- "tbi", or "rtbi". This property is only really needed if the connection
- is of type "rgmii-id", as all other connection types are detected by
- hardware.
+ - phy-connection-type : See ethernet.txt file in the same directory.
+ This property is only really needed if the connection is of type
+ "rgmii-id", as all other connection types are detected by hardware.
- fsl,magic-packet : If present, indicates that the hardware supports
waking up via magic packet.
- bd-stash : If present, indicates that the hardware supports stashing
diff --git a/Documentation/devicetree/bindings/net/lpc-eth.txt b/Documentation/devicetree/bindings/net/lpc-eth.txt
index 585021acd178..b92e927808b6 100644
--- a/Documentation/devicetree/bindings/net/lpc-eth.txt
+++ b/Documentation/devicetree/bindings/net/lpc-eth.txt
@@ -6,10 +6,9 @@ Required properties:
- interrupts: Should contain ethernet controller interrupt
Optional properties:
-- phy-mode: String, operation mode of the PHY interface.
- Supported values are: "mii", "rmii" (default)
+- phy-mode: See ethernet.txt file in the same directory. If the property is
+ absent, "rmii" is assumed.
- use-iram: Use LPC32xx internal SRAM (IRAM) for DMA buffering
-- local-mac-address : 6 bytes, mac address
Example:
diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt
index 70af2ec12b09..aaa696414f57 100644
--- a/Documentation/devicetree/bindings/net/macb.txt
+++ b/Documentation/devicetree/bindings/net/macb.txt
@@ -8,16 +8,12 @@ Required properties:
the Cadence GEM, or the generic form: "cdns,gem".
- reg: Address and length of the register set for the device
- interrupts: Should contain macb interrupt
-- phy-mode: String, operation mode of the PHY interface.
- Supported values are: "mii", "rmii", "gmii", "rgmii".
+- phy-mode: See ethernet.txt file in the same directory.
- clock-names: Tuple listing input clock names.
Required elements: 'pclk', 'hclk'
Optional elements: 'tx_clk'
- clocks: Phandles to input clocks.
-Optional properties:
-- local-mac-address: 6 bytes, mac address
-
Examples:
macb0: ethernet@fffc4000 {
diff --git a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
index 859a6fa7569c..750d577e8083 100644
--- a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
+++ b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
@@ -4,10 +4,8 @@ Required properties:
- compatible: should be "marvell,armada-370-neta".
- reg: address and length of the register set for the device.
- interrupts: interrupt for the device
-- phy: A phandle to a phy node defining the PHY address (as the reg
- property, a single integer).
-- phy-mode: The interface between the SoC and the PHY (a string that
- of_get_phy_mode() can understand)
+- phy: See ethernet.txt file in the same directory.
+- phy-mode: See ethernet.txt file in the same directory
- clocks: a pointer to the reference clock for this device.
Example:
diff --git a/Documentation/devicetree/bindings/net/marvell-orion-net.txt b/Documentation/devicetree/bindings/net/marvell-orion-net.txt
index c233b6114242..bce52b2ec55e 100644
--- a/Documentation/devicetree/bindings/net/marvell-orion-net.txt
+++ b/Documentation/devicetree/bindings/net/marvell-orion-net.txt
@@ -36,7 +36,7 @@ Required port properties:
"marvell,kirkwood-eth-port".
- reg: port number relative to ethernet controller, shall be 0, 1, or 2.
- interrupts: port interrupt.
- - local-mac-address: 6 bytes MAC address.
+ - local-mac-address: See ethernet.txt file in the same directory.
Optional port properties:
- marvell,tx-queue-size: size of the transmit ring buffer.
@@ -48,7 +48,7 @@ Optional port properties:
and
- - phy-handle: phandle reference to ethernet PHY.
+ - phy-handle: See ethernet.txt file in the same directory.
or
diff --git a/Documentation/devicetree/bindings/net/micrel-ks8851.txt b/Documentation/devicetree/bindings/net/micrel-ks8851.txt
index 4fc392763611..d54d0cc79487 100644
--- a/Documentation/devicetree/bindings/net/micrel-ks8851.txt
+++ b/Documentation/devicetree/bindings/net/micrel-ks8851.txt
@@ -6,5 +6,4 @@ Required properties:
- interrupts : interrupt connection
Optional properties:
-- local-mac-address : Ethernet mac address to use
- vdd-supply: supply for Ethernet mac
diff --git a/Documentation/devicetree/bindings/net/micrel.txt b/Documentation/devicetree/bindings/net/micrel.txt
new file mode 100644
index 000000000000..98a3e61f9ee8
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/micrel.txt
@@ -0,0 +1,18 @@
+Micrel PHY properties.
+
+These properties cover the base properties Micrel PHYs.
+
+Optional properties:
+
+ - micrel,led-mode : LED mode value to set for PHYs with configurable LEDs.
+
+ Configure the LED mode with single value. The list of PHYs and
+ the bits that are currently supported:
+
+ KSZ8001: register 0x1e, bits 15..14
+ KSZ8041: register 0x1e, bits 15..14
+ KSZ8021: register 0x1f, bits 5..4
+ KSZ8031: register 0x1f, bits 5..4
+ KSZ8051: register 0x1f, bits 5..4
+
+ See the respective PHY datasheet for the mode values.
diff --git a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt
new file mode 100644
index 000000000000..8dd3ef7bc56b
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt
@@ -0,0 +1,34 @@
+* Texas Instruments TRF7970A RFID/NFC/15693 Transceiver
+
+Required properties:
+- compatible: Should be "ti,trf7970a".
+- spi-max-frequency: Maximum SPI frequency (<= 2000000).
+- interrupt-parent: phandle of parent interrupt handler.
+- interrupts: A single interrupt specifier.
+- ti,enable-gpios: Two GPIO entries used for 'EN' and 'EN2' pins on the
+ TRF7970A.
+- vin-supply: Regulator for supply voltage to VIN pin
+
+Optional SoC Specific Properties:
+- pinctrl-names: Contains only one value - "default".
+- pintctrl-0: Specifies the pin control groups used for this controller.
+
+Example (for ARM-based BeagleBone with TRF7970A on SPI1):
+
+&spi1 {
+ status = "okay";
+
+ nfc@0 {
+ compatible = "ti,trf7970a";
+ reg = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&trf7970a_default>;
+ spi-max-frequency = <2000000>;
+ interrupt-parent = <&gpio2>;
+ interrupts = <14 0>;
+ ti,enable-gpios = <&gpio2 2 GPIO_ACTIVE_LOW>,
+ <&gpio2 5 GPIO_ACTIVE_LOW>;
+ vin-supply = <&ldo3_reg>;
+ status = "okay";
+ };
+};
diff --git a/Documentation/devicetree/bindings/net/phy.txt b/Documentation/devicetree/bindings/net/phy.txt
index 58307d0931c8..5b8c58903077 100644
--- a/Documentation/devicetree/bindings/net/phy.txt
+++ b/Documentation/devicetree/bindings/net/phy.txt
@@ -21,10 +21,18 @@ Optional Properties:
elements.
- max-speed: Maximum PHY supported speed (10, 100, 1000...)
+ If the phy's identifier is known then the list may contain an entry
+ of the form: "ethernet-phy-idAAAA.BBBB" where
+ AAAA - The value of the 16 bit Phy Identifier 1 register as
+ 4 hex digits. This is the chip vendor OUI bits 3:18
+ BBBB - The value of the 16 bit Phy Identifier 2 register as
+ 4 hex digits. This is the chip vendor OUI bits 19:24,
+ followed by 10 bits of a vendor specific ID.
+
Example:
ethernet-phy@0 {
- compatible = "ethernet-phy-ieee802.3-c22";
+ compatible = "ethernet-phy-id0141.0e90", "ethernet-phy-ieee802.3-c22";
interrupt-parent = <40000>;
interrupts = <35 1>;
reg = <0>;
diff --git a/Documentation/devicetree/bindings/net/samsung-sxgbe.txt b/Documentation/devicetree/bindings/net/samsung-sxgbe.txt
new file mode 100644
index 000000000000..989f6c95cfd5
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/samsung-sxgbe.txt
@@ -0,0 +1,52 @@
+* Samsung 10G Ethernet driver (SXGBE)
+
+Required properties:
+- compatible: Should be "samsung,sxgbe-v2.0a"
+- reg: Address and length of the register set for the device
+- interrupt-parent: Should be the phandle for the interrupt controller
+ that services interrupts for this device
+- interrupts: Should contain the SXGBE interrupts
+ These interrupts are ordered by fixed and follows variable
+ trasmit DMA interrupts, receive DMA interrupts and lpi interrupt.
+ index 0 - this is fixed common interrupt of SXGBE and it is always
+ available.
+ index 1 to 25 - 8 variable trasmit interrupts, variable 16 receive interrupts
+ and 1 optional lpi interrupt.
+- phy-mode: String, operation mode of the PHY interface.
+ Supported values are: "sgmii", "xgmii".
+- samsung,pbl: Integer, Programmable Burst Length.
+ Supported values are 1, 2, 4, 8, 16, or 32.
+- samsung,burst-map: Integer, Program the possible bursts supported by sxgbe
+ This is an interger and represents allowable DMA bursts when fixed burst.
+ Allowable range is 0x01-0x3F. When this field is set fixed burst is enabled.
+ When fixed length is needed for burst mode, it can be set within allowable
+ range.
+
+Optional properties:
+- mac-address: 6 bytes, mac address
+- max-frame-size: Maximum Transfer Unit (IEEE defined MTU), rather
+ than the maximum frame size.
+
+Example:
+
+ aliases {
+ ethernet0 = <&sxgbe0>;
+ };
+
+ sxgbe0: ethernet@1a040000 {
+ compatible = "samsung,sxgbe-v2.0a";
+ reg = <0 0x1a040000 0 0x10000>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 209 4>, <0 185 4>, <0 186 4>, <0 187 4>,
+ <0 188 4>, <0 189 4>, <0 190 4>, <0 191 4>,
+ <0 192 4>, <0 193 4>, <0 194 4>, <0 195 4>,
+ <0 196 4>, <0 197 4>, <0 198 4>, <0 199 4>,
+ <0 200 4>, <0 201 4>, <0 202 4>, <0 203 4>,
+ <0 204 4>, <0 205 4>, <0 206 4>, <0 207 4>,
+ <0 208 4>, <0 210 4>;
+ samsung,pbl = <0x08>
+ samsung,burst-map = <0x20>
+ mac-address = [ 00 11 22 33 44 55 ]; /* Filled in by U-Boot */
+ max-frame-size = <9000>;
+ phy-mode = "xgmii";
+ };
diff --git a/Documentation/devicetree/bindings/net/sh_eth.txt b/Documentation/devicetree/bindings/net/sh_eth.txt
new file mode 100644
index 000000000000..e7106b50dbdc
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/sh_eth.txt
@@ -0,0 +1,55 @@
+* Renesas Electronics SH EtherMAC
+
+This file provides information on what the device node for the SH EtherMAC
+interface contains.
+
+Required properties:
+- compatible: "renesas,gether-r8a7740" if the device is a part of R8A7740 SoC.
+ "renesas,ether-r8a7778" if the device is a part of R8A7778 SoC.
+ "renesas,ether-r8a7779" if the device is a part of R8A7779 SoC.
+ "renesas,ether-r8a7790" if the device is a part of R8A7790 SoC.
+ "renesas,ether-r8a7791" if the device is a part of R8A7791 SoC.
+ "renesas,ether-r7s72100" if the device is a part of R7S72100 SoC.
+- reg: offset and length of (1) the E-DMAC/feLic register block (required),
+ (2) the TSU register block (optional).
+- interrupts: interrupt specifier for the sole interrupt.
+- phy-mode: see ethernet.txt file in the same directory.
+- phy-handle: see ethernet.txt file in the same directory.
+- #address-cells: number of address cells for the MDIO bus, must be equal to 1.
+- #size-cells: number of size cells on the MDIO bus, must be equal to 0.
+- clocks: clock phandle and specifier pair.
+- pinctrl-0: phandle, referring to a default pin configuration node.
+
+Optional properties:
+- interrupt-parent: the phandle for the interrupt controller that services
+ interrupts for this device.
+- pinctrl-names: pin configuration state name ("default").
+- renesas,no-ether-link: boolean, specify when a board does not provide a proper
+ Ether LINK signal.
+- renesas,ether-link-active-low: boolean, specify when the Ether LINK signal is
+ active-low instead of normal active-high.
+
+Example (Lager board):
+
+ ethernet@ee700000 {
+ compatible = "renesas,ether-r8a7790";
+ reg = <0 0xee700000 0 0x400>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 162 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp8_clks R8A7790_CLK_ETHER>;
+ phy-mode = "rmii";
+ phy-handle = <&phy1>;
+ pinctrl-0 = <&ether_pins>;
+ pinctrl-names = "default";
+ renesas,ether-link-active-low;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ interrupt-parent = <&irqc0>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ pinctrl-0 = <&phy1_pins>;
+ pinctrl-names = "default";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/net/smsc-lan91c111.txt b/Documentation/devicetree/bindings/net/smsc-lan91c111.txt
index 5a41a8658daa..0f8487b88822 100644
--- a/Documentation/devicetree/bindings/net/smsc-lan91c111.txt
+++ b/Documentation/devicetree/bindings/net/smsc-lan91c111.txt
@@ -6,8 +6,7 @@ Required properties:
- interrupts : interrupt connection
Optional properties:
-- phy-device : phandle to Ethernet phy
-- local-mac-address : Ethernet mac address to use
+- phy-device : see ethernet.txt file in the same directory
- reg-io-width : Mask of sizes (in bytes) of the IO accesses that
are supported on the device. Valid value for SMSC LAN91c111 are
1, 2 or 4. If it's omitted or invalid, the size would be 2 meaning
diff --git a/Documentation/devicetree/bindings/net/smsc911x.txt b/Documentation/devicetree/bindings/net/smsc911x.txt
index adb5b5744ecd..3fed3c124411 100644
--- a/Documentation/devicetree/bindings/net/smsc911x.txt
+++ b/Documentation/devicetree/bindings/net/smsc911x.txt
@@ -6,9 +6,7 @@ Required properties:
- interrupts : Should contain SMSC LAN interrupt line
- interrupt-parent : Should be the phandle for the interrupt controller
that services interrupts for this device
-- phy-mode : String, operation mode of the PHY interface.
- Supported values are: "mii", "gmii", "sgmii", "tbi", "rmii",
- "rgmii", "rgmii-id", "rgmii-rxid", "rgmii-txid", "rtbi", "smii".
+- phy-mode : See ethernet.txt file in the same directory
Optional properties:
- reg-shift : Specify the quantity to shift the register offsets by
@@ -23,7 +21,6 @@ Optional properties:
external PHY
- smsc,save-mac-address : Indicates that mac address needs to be saved
before resetting the controller
-- local-mac-address : 6 bytes, mac address
Examples:
diff --git a/Documentation/devicetree/bindings/net/socfpga-dwmac.txt b/Documentation/devicetree/bindings/net/socfpga-dwmac.txt
new file mode 100644
index 000000000000..636f0ac4e223
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/socfpga-dwmac.txt
@@ -0,0 +1,27 @@
+Altera SOCFPGA SoC DWMAC controller
+
+This is a variant of the dwmac/stmmac driver an inherits all descriptions
+present in Documentation/devicetree/bindings/net/stmmac.txt.
+
+The device node has additional properties:
+
+Required properties:
+ - compatible : Should contain "altr,socfpga-stmmac" along with
+ "snps,dwmac" and any applicable more detailed
+ 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.
+
+Example:
+
+gmac0: ethernet@ff700000 {
+ compatible = "altr,socfpga-stmmac", "snps,dwmac-3.70a", "snps,dwmac";
+ altr,sysmgr-syscon = <&sysmgr 0x60 0>;
+ status = "disabled";
+ reg = <0xff700000 0x2000>;
+ interrupts = <0 115 4>;
+ interrupt-names = "macirq";
+ mac-address = [00 00 00 00 00 00];/* Filled in by U-Boot */
+ clocks = <&emac_0_clk>;
+ clocks-names = "stmmaceth";
+};
diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt
index 9d92d42140f2..80c1fb8bfbb8 100644
--- a/Documentation/devicetree/bindings/net/stmmac.txt
+++ b/Documentation/devicetree/bindings/net/stmmac.txt
@@ -10,8 +10,7 @@ Required properties:
- interrupt-names: Should contain the interrupt names "macirq"
"eth_wake_irq" if this interrupt is supported in the "interrupts"
property
-- phy-mode: String, operation mode of the PHY interface.
- Supported values are: "mii", "rmii", "gmii", "rgmii".
+- phy-mode: See ethernet.txt file in the same directory.
- snps,reset-gpio gpio number for phy reset.
- snps,reset-active-low boolean flag to indicate if phy reset is active low.
- snps,reset-delays-us is triplet of delays
@@ -28,12 +27,14 @@ Required properties:
ignored if force_thresh_dma_mode is set.
Optional properties:
-- mac-address: 6 bytes, mac address
- resets: Should contain a phandle to the STMMAC reset signal, if any
- reset-names: Should contain the reset signal name "stmmaceth", if a
reset phandle is given
-- max-frame-size: Maximum Transfer Unit (IEEE defined MTU), rather
- than the maximum frame size.
+- max-frame-size: See ethernet.txt file in the same directory
+- clocks: If present, the first clock should be the GMAC main clock,
+ further clocks may be specified in derived bindings.
+- clocks-names: One name for each entry in the clocks property, the
+ first one should be "stmmaceth".
Examples:
@@ -46,4 +47,6 @@ Examples:
mac-address = [000000000000]; /* Filled in by U-Boot */
max-frame-size = <3800>;
phy-mode = "gmii";
+ clocks = <&clock>;
+ clock-names = "stmmaceth">;
};
diff --git a/Documentation/devicetree/bindings/net/wireless/ti,wl1251.txt b/Documentation/devicetree/bindings/net/wireless/ti,wl1251.txt
new file mode 100644
index 000000000000..189ae5cad8f7
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/wireless/ti,wl1251.txt
@@ -0,0 +1,39 @@
+* Texas Instruments wl1251 wireless lan controller
+
+The wl1251 chip can be connected via SPI or via SDIO. This
+document describes the binding for the SPI connected chip.
+
+Required properties:
+- compatible : Should be "ti,wl1251"
+- reg : Chip select address of device
+- spi-max-frequency : Maximum SPI clocking speed of device in Hz
+- interrupts : Should contain interrupt line
+- interrupt-parent : Should be the phandle for the interrupt controller
+ that services interrupts for this device
+- vio-supply : phandle to regulator providing VIO
+- ti,power-gpio : GPIO connected to chip's PMEN pin
+
+Optional properties:
+- ti,wl1251-has-eeprom : boolean, the wl1251 has an eeprom connected, which
+ provides configuration data (calibration, MAC, ...)
+- Please consult Documentation/devicetree/bindings/spi/spi-bus.txt
+ for optional SPI connection related properties,
+
+Examples:
+
+&spi1 {
+ wl1251@0 {
+ compatible = "ti,wl1251";
+
+ reg = <0>;
+ spi-max-frequency = <48000000>;
+ spi-cpol;
+ spi-cpha;
+
+ interrupt-parent = <&gpio2>;
+ interrupts = <10 IRQ_TYPE_NONE>; /* gpio line 42 */
+
+ vio-supply = <&vio>;
+ ti,power-gpio = <&gpio3 23 GPIO_ACTIVE_HIGH>; /* 87 */
+ };
+};
diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
index 24cee06915c9..c300391e8d3e 100644
--- a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
@@ -42,6 +42,10 @@ Required properties:
- 0xc2000000: prefetchable memory region
Please refer to the standard PCI bus binding document for a more detailed
explanation.
+- #interrupt-cells: Size representation for interrupts (must be 1)
+- interrupt-map-mask and interrupt-map: Standard PCI IRQ mapping properties
+ Please refer to the standard PCI bus binding document for a more detailed
+ explanation.
- clocks: Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries:
@@ -86,6 +90,10 @@ SoC DTSI:
0 99 0x04>; /* MSI interrupt */
interrupt-names = "intr", "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt
index 28f9edb8f19c..b422e38946d7 100644
--- a/Documentation/devicetree/bindings/phy/samsung-phy.txt
+++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt
@@ -74,3 +74,43 @@ phy-consumer@12340000 {
Refer to DT bindings documentation of particular PHY consumer devices for more
information about required PHYs and the way of specification.
+
+Samsung SATA PHY Controller
+---------------------------
+
+SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
+Each SATA PHY controller should have its own node.
+
+Required properties:
+- compatible : compatible list, contains "samsung,exynos5250-sata-phy"
+- reg : offset and length of the SATA PHY register set;
+- #phy-cells : must be zero
+- clocks : must be exactly one entry
+- clock-names : must be "sata_phyctrl"
+- samsung,exynos-sataphy-i2c-phandle : a phandle to the I2C device, no arguments
+- samsung,syscon-phandle : a phandle to the PMU system controller, no arguments
+
+Example:
+ sata_phy: sata-phy@12170000 {
+ compatible = "samsung,exynos5250-sata-phy";
+ reg = <0x12170000 0x1ff>;
+ clocks = <&clock 287>;
+ clock-names = "sata_phyctrl";
+ #phy-cells = <0>;
+ samsung,exynos-sataphy-i2c-phandle = <&sata_phy_i2c>;
+ samsung,syscon-phandle = <&pmu_syscon>;
+ };
+
+Device-Tree bindings for sataphy i2c client driver
+--------------------------------------------------
+
+Required properties:
+compatible: Should be "samsung,exynos-sataphy-i2c"
+- reg: I2C address of the sataphy i2c device.
+
+Example:
+
+ sata_phy_i2c:sata-phy@38 {
+ compatible = "samsung,exynos-sataphy-i2c";
+ reg = <0x38>;
+ };
diff --git a/Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt b/Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt
index 0347d8350d94..af25e77c0e0c 100644
--- a/Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt
+++ b/Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt
@@ -6,8 +6,11 @@ Orion5x SoCs. Sending the character 'A', at 19200 baud, tells the
microcontroller to turn the power off. This driver adds a handler to
pm_power_off which is called to turn the power off.
+Synology NAS devices use a similar scheme, but a different baud rate,
+9600, and a different character, '1'.
+
Required Properties:
-- compatible: Should be "qnap,power-off"
+- compatible: Should be "qnap,power-off" or "synology,power-off"
- reg: Address and length of the register set for UART1
- clocks: tclk clock
diff --git a/Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt b/Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt
new file mode 100644
index 000000000000..a183db48f910
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt
@@ -0,0 +1,16 @@
+* Cirris Logic CLPS711X PWM controller
+
+Required properties:
+- compatible: Shall contain "cirrus,clps711x-pwm".
+- reg: Physical base address and length of the controller's registers.
+- clocks: phandle + clock specifier pair of the PWM reference clock.
+- #pwm-cells: Should be 1. The cell specifies the index of the channel.
+
+Example:
+ pwm: pwm@80000400 {
+ compatible = "cirrus,ep7312-pwm",
+ "cirrus,clps711x-pwm";
+ reg = <0x80000400 0x4>;
+ clocks = <&clks 8>;
+ #pwm-cells = <1>;
+ };
diff --git a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt
new file mode 100644
index 000000000000..0bda229a6171
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt
@@ -0,0 +1,35 @@
+Freescale FlexTimer Module (FTM) PWM controller
+
+Required properties:
+- compatible: Should be "fsl,vf610-ftm-pwm".
+- reg: Physical base address and length of the controller's registers
+- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of
+ the cells format.
+- clock-names: Should include the following module clock source entries:
+ "ftm_sys" (module clock, also can be used as counter clock),
+ "ftm_ext" (external counter clock),
+ "ftm_fix" (fixed counter clock),
+ "ftm_cnt_clk_en" (external and fixed counter clock enable/disable).
+- clocks: Must contain a phandle and clock specifier for each entry in
+ clock-names, please see clock/clock-bindings.txt for details of the property
+ values.
+- pinctrl-names: Must contain a "default" entry.
+- pinctrl-NNN: One property must exist for each entry in pinctrl-names.
+ See pinctrl/pinctrl-bindings.txt for details of the property values.
+
+
+Example:
+
+pwm0: pwm@40038000 {
+ compatible = "fsl,vf610-ftm-pwm";
+ reg = <0x40038000 0x1000>;
+ #pwm-cells = <3>;
+ clock-names = "ftm_sys", "ftm_ext",
+ "ftm_fix", "ftm_cnt_clk_en";
+ clocks = <&clks VF610_CLK_FTM0>,
+ <&clks VF610_CLK_FTM0_EXT_SEL>,
+ <&clks VF610_CLK_FTM0_FIX_SEL>,
+ <&clks VF610_CLK_FTM0_EXT_FIX_EN>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm0_1>;
+};
diff --git a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
new file mode 100644
index 000000000000..3da0ebdba8d9
--- /dev/null
+++ b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
@@ -0,0 +1,133 @@
+*** Reserved memory regions ***
+
+Reserved memory is specified as a node under the /reserved-memory node.
+The operating system shall exclude reserved memory from normal usage
+one can create child nodes describing particular reserved (excluded from
+normal use) memory regions. Such memory regions are usually designed for
+the special usage by various device drivers.
+
+Parameters for each memory region can be encoded into the device tree
+with the following nodes:
+
+/reserved-memory node
+---------------------
+#address-cells, #size-cells (required) - standard definition
+ - Should use the same values as the root node
+ranges (required) - standard definition
+ - Should be empty
+
+/reserved-memory/ child nodes
+-----------------------------
+Each child of the reserved-memory node specifies one or more regions of
+reserved memory. Each child node may either use a 'reg' property to
+specify a specific range of reserved memory, or a 'size' property with
+optional constraints to request a dynamically allocated block of memory.
+
+Following the generic-names recommended practice, node names should
+reflect the purpose of the node (ie. "framebuffer" or "dma-pool"). Unit
+address (@<address>) should be appended to the name if the node is a
+static allocation.
+
+Properties:
+Requires either a) or b) below.
+a) static allocation
+ reg (required) - standard definition
+b) dynamic allocation
+ size (required) - length based on parent's #size-cells
+ - Size in bytes of memory to reserve.
+ alignment (optional) - length based on parent's #size-cells
+ - Address boundary for alignment of allocation.
+ alloc-ranges (optional) - prop-encoded-array (address, length pairs).
+ - Specifies regions of memory that are
+ acceptable to allocate from.
+
+If both reg and size are present, then the reg property takes precedence
+and size is ignored.
+
+Additional properties:
+compatible (optional) - standard definition
+ - may contain the following strings:
+ - shared-dma-pool: This indicates a region of memory meant to be
+ used as a shared pool of DMA buffers for a set of devices. It can
+ be used by an operating system to instanciate the necessary pool
+ management subsystem if necessary.
+ - vendor specific string in the form <vendor>,[<device>-]<usage>
+no-map (optional) - empty property
+ - Indicates the operating system must not create a virtual mapping
+ of the region as part of its standard mapping of system memory,
+ nor permit speculative access to it under any circumstances other
+ than under the control of the device driver using the region.
+reusable (optional) - empty property
+ - The operating system can use the memory in this region with the
+ limitation that the device driver(s) owning the region need to be
+ able to reclaim it back. Typically that means that the operating
+ system can use that region to store volatile or cached data that
+ can be otherwise regenerated or migrated elsewhere.
+
+Linux implementation note:
+- If a "linux,cma-default" property is present, then Linux will use the
+ region for the default pool of the contiguous memory allocator.
+
+Device node references to reserved memory
+-----------------------------------------
+Regions in the /reserved-memory node may be referenced by other device
+nodes by adding a memory-region property to the device node.
+
+memory-region (optional) - phandle, specifier pairs to children of /reserved-memory
+
+Example
+-------
+This example defines 3 contiguous regions are defined for Linux kernel:
+one default of all device drivers (named linux,cma@72000000 and 64MiB in size),
+one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB), and
+one for multimedia processing (named multimedia-memory@77000000, 64MiB).
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ memory {
+ reg = <0x40000000 0x40000000>;
+ };
+
+ reserved-memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ /* global autoconfigured region for contiguous allocations */
+ linux,cma {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x4000000>;
+ alignment = <0x2000>;
+ linux,cma-default;
+ };
+
+ display_reserved: framebuffer@78000000 {
+ reg = <0x78000000 0x800000>;
+ };
+
+ multimedia_reserved: multimedia@77000000 {
+ compatible = "acme,multimedia-memory";
+ reg = <0x77000000 0x4000000>;
+ };
+ };
+
+ /* ... */
+
+ fb0: video@12300000 {
+ memory-region = <&display_reserved>;
+ /* ... */
+ };
+
+ scaler: scaler@12500000 {
+ memory-region = <&multimedia_reserved>;
+ /* ... */
+ };
+
+ codec: codec@12600000 {
+ memory-region = <&multimedia_reserved>;
+ /* ... */
+ };
+};
diff --git a/Documentation/devicetree/bindings/reset/sirf,rstc.txt b/Documentation/devicetree/bindings/reset/sirf,rstc.txt
new file mode 100644
index 000000000000..0505de742d30
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/sirf,rstc.txt
@@ -0,0 +1,42 @@
+CSR SiRFSoC Reset Controller
+======================================
+
+Please also refer to reset.txt in this directory for common reset
+controller binding usage.
+
+Required properties:
+- compatible: Should be "sirf,prima2-rstc" or "sirf,marco-rstc"
+- reg: should be register base and length as documented in the
+ datasheet
+- #reset-cells: 1, see below
+
+example:
+
+rstc: reset-controller@88010000 {
+ compatible = "sirf,prima2-rstc";
+ reg = <0x88010000 0x1000>;
+ #reset-cells = <1>;
+};
+
+Specifying reset lines connected to IP modules
+==============================================
+
+The reset controller(rstc) manages various reset sources. This module provides
+reset signals for most blocks in system. Those device nodes should specify the
+reset line on the rstc in their resets property, containing a phandle to the
+rstc device node and a RESET_INDEX specifying which module to reset, as described
+in reset.txt.
+
+For SiRFSoC, RESET_INDEX is just reset_bit defined in SW_RST0 and SW_RST1 registers.
+For modules whose rest_bit is in SW_RST0, its RESET_INDEX is 0~31. For modules whose
+rest_bit is in SW_RST1, its RESET_INDEX is 32~63.
+
+example:
+
+vpp@90020000 {
+ compatible = "sirf,prima2-vpp";
+ reg = <0x90020000 0x10000>;
+ interrupts = <31>;
+ clocks = <&clks 35>;
+ resets = <&rstc 6>;
+};
diff --git a/Documentation/devicetree/bindings/reset/st,sti-powerdown.txt b/Documentation/devicetree/bindings/reset/st,sti-powerdown.txt
new file mode 100644
index 000000000000..5ab26b7e9d35
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/st,sti-powerdown.txt
@@ -0,0 +1,47 @@
+STMicroelectronics STi family Sysconfig Peripheral Powerdown Reset Controller
+=============================================================================
+
+This binding describes a reset controller device that is used to enable and
+disable on-chip peripheral controllers such as USB and SATA, using
+"powerdown" control bits found in the STi family SoC system configuration
+registers. These have been grouped together into a single reset controller
+device for convenience.
+
+The actual action taken when powerdown is asserted is hardware dependent.
+However, when asserted it may not be possible to access the hardware's
+registers and after an assert/deassert sequence the hardware's previous state
+may no longer be valid.
+
+Please refer to reset.txt in this directory for common reset
+controller binding usage.
+
+Required properties:
+- compatible: Should be "st,<chip>-powerdown"
+ ex: "st,stih415-powerdown", "st,stih416-powerdown"
+- #reset-cells: 1, see below
+
+example:
+
+ powerdown: powerdown-controller {
+ #reset-cells = <1>;
+ compatible = "st,stih415-powerdown";
+ };
+
+
+Specifying powerdown control of devices
+=======================================
+
+Device nodes should specify the reset channel required in their "resets"
+property, containing a phandle to the powerdown device node and an
+index specifying which channel to use, as described in reset.txt
+
+example:
+
+ usb1: usb@fe200000 {
+ resets = <&powerdown STIH41X_USB1_POWERDOWN>;
+ };
+
+Macro definitions for the supported reset channels can be found in:
+
+include/dt-bindings/reset-controller/stih415-resets.h
+include/dt-bindings/reset-controller/stih416-resets.h
diff --git a/Documentation/devicetree/bindings/reset/st,sti-softreset.txt b/Documentation/devicetree/bindings/reset/st,sti-softreset.txt
new file mode 100644
index 000000000000..a8d3d3c25ca2
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/st,sti-softreset.txt
@@ -0,0 +1,46 @@
+STMicroelectronics STi family Sysconfig Peripheral SoftReset Controller
+=============================================================================
+
+This binding describes a reset controller device that is used to enable and
+disable on-chip peripheral controllers such as USB and SATA, using
+"softreset" control bits found in the STi family SoC system configuration
+registers.
+
+The actual action taken when softreset is asserted is hardware dependent.
+However, when asserted it may not be possible to access the hardware's
+registers and after an assert/deassert sequence the hardware's previous state
+may no longer be valid.
+
+Please refer to reset.txt in this directory for common reset
+controller binding usage.
+
+Required properties:
+- compatible: Should be "st,<chip>-softreset" example:
+ "st,stih415-softreset" or "st,stih416-softreset";
+- #reset-cells: 1, see below
+
+example:
+
+ softreset: softreset-controller {
+ #reset-cells = <1>;
+ compatible = "st,stih415-softreset";
+ };
+
+
+Specifying softreset control of devices
+=======================================
+
+Device nodes should specify the reset channel required in their "resets"
+property, containing a phandle to the softreset device node and an
+index specifying which channel to use, as described in reset.txt
+
+example:
+
+ ethernet0{
+ resets = <&softreset STIH415_ETH0_SOFTRESET>;
+ };
+
+Macro definitions for the supported reset channels can be found in:
+
+include/dt-bindings/reset-controller/stih415-resets.h
+include/dt-bindings/reset-controller/stih416-resets.h
diff --git a/Documentation/devicetree/bindings/rtc/sunxi-rtc.txt b/Documentation/devicetree/bindings/rtc/sunxi-rtc.txt
index 7cb9dbf34878..6983aad376c3 100644
--- a/Documentation/devicetree/bindings/rtc/sunxi-rtc.txt
+++ b/Documentation/devicetree/bindings/rtc/sunxi-rtc.txt
@@ -3,7 +3,7 @@
RTC controller for the Allwinner A10/A20
Required properties:
-- compatible : Should be "allwinner,sun4i-rtc" or "allwinner,sun7i-a20-rtc"
+- compatible : Should be "allwinner,sun4i-a10-rtc" or "allwinner,sun7i-a20-rtc"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: IRQ line for the RTC.
@@ -11,7 +11,7 @@ Required properties:
Example:
rtc: rtc@01c20d00 {
- compatible = "allwinner,sun4i-rtc";
+ compatible = "allwinner,sun4i-a10-rtc";
reg = <0x01c20d00 0x20>;
interrupts = <24>;
};
diff --git a/Documentation/devicetree/bindings/serial/atmel-usart.txt b/Documentation/devicetree/bindings/serial/atmel-usart.txt
index 9c5d19ac935c..17c1042b2df8 100644
--- a/Documentation/devicetree/bindings/serial/atmel-usart.txt
+++ b/Documentation/devicetree/bindings/serial/atmel-usart.txt
@@ -13,6 +13,8 @@ Required properties:
Optional properties:
- atmel,use-dma-rx: use of PDC or DMA for receiving data
- atmel,use-dma-tx: use of PDC or DMA for transmitting data
+- rts-gpios: specify a GPIO for RTS line. It will use specified PIO instead of the peripheral
+ function pin for the USART RTS feature. If unsure, don't specify this property.
- add dma bindings for dma transfer:
- dmas: DMA specifier, consisting of a phandle to DMA controller node,
memory peripheral interface and USART DMA channel ID, FIFO configuration.
@@ -33,6 +35,7 @@ Example:
clock-names = "usart";
atmel,use-dma-rx;
atmel,use-dma-tx;
+ rts-gpios = <&pioD 15 0>;
};
- use DMA:
diff --git a/Documentation/devicetree/bindings/usb/atmel-usb.txt b/Documentation/devicetree/bindings/usb/atmel-usb.txt
index 55f51af08bc7..bc2222ca3f2a 100644
--- a/Documentation/devicetree/bindings/usb/atmel-usb.txt
+++ b/Documentation/devicetree/bindings/usb/atmel-usb.txt
@@ -57,8 +57,8 @@ Required properties:
- ep childnode: To specify the number of endpoints and their properties.
Optional properties:
- - atmel,vbus-gpio: If present, specifies a gpio that needs to be
- activated for the bus to be powered.
+ - atmel,vbus-gpio: If present, specifies a gpio that allows to detect whether
+ vbus is present (USB is connected).
Required child node properties:
- name: Name of the endpoint.
diff --git a/Documentation/devicetree/bindings/usb/ehci-omap.txt b/Documentation/devicetree/bindings/usb/ehci-omap.txt
index 485a9a1efa7a..3dc231c832b0 100644
--- a/Documentation/devicetree/bindings/usb/ehci-omap.txt
+++ b/Documentation/devicetree/bindings/usb/ehci-omap.txt
@@ -21,7 +21,7 @@ Documentation/devicetree/bindings/mfd/omap-usb-host.txt
Example for OMAP4:
usbhsehci: ehci@4a064c00 {
- compatible = "ti,ehci-omap", "usb-ehci";
+ compatible = "ti,ehci-omap";
reg = <0x4a064c00 0x400>;
interrupts = <0 77 0x4>;
};
diff --git a/Documentation/devicetree/bindings/usb/ohci-omap3.txt b/Documentation/devicetree/bindings/usb/ohci-omap3.txt
index 14ab42812a8e..ce8c47cff6d0 100644
--- a/Documentation/devicetree/bindings/usb/ohci-omap3.txt
+++ b/Documentation/devicetree/bindings/usb/ohci-omap3.txt
@@ -9,7 +9,7 @@ Required properties:
Example for OMAP4:
usbhsohci: ohci@4a064800 {
- compatible = "ti,ohci-omap3", "usb-ohci";
+ compatible = "ti,ohci-omap3";
reg = <0x4a064800 0x400>;
interrupts = <0 76 0x4>;
};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 40ce2df0e0e9..0f01c9bf19c8 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -3,6 +3,7 @@ Device tree binding vendor prefix registry. Keep list in alphabetical order.
This isn't an exhaustive list, but you should add new prefixes to it before
using them to avoid name-space collisions.
+abilis Abilis Systems
active-semi Active-Semi International Inc
ad Avionic Design GmbH
adi Analog Devices, Inc.
@@ -11,14 +12,17 @@ ak Asahi Kasei Corp.
allwinner Allwinner Technology Co., Ltd.
altr Altera Corp.
amcc Applied Micro Circuits Corporation (APM, formally AMCC)
+amd Advanced Micro Devices (AMD), Inc.
amstaos AMS-Taos Inc.
apm Applied Micro Circuits Corporation (APM)
arm ARM Ltd.
+armadeus ARMadeus Systems SARL
atmel Atmel Corporation
auo AU Optronics Corporation
avago Avago Technologies
bosch Bosch Sensortec GmbH
brcm Broadcom Corporation
+calxeda Calxeda
capella Capella Microsystems, Inc
cavium Cavium, Inc.
cdns Cadence Design Systems Inc.
@@ -26,18 +30,24 @@ chrp Common Hardware Reference Platform
chunghwa Chunghwa Picture Tubes Ltd.
cirrus Cirrus Logic, Inc.
cortina Cortina Systems, Inc.
+crystalfontz Crystalfontz America, Inc.
dallas Maxim Integrated Products (formerly Dallas Semiconductor)
davicom DAVICOM Semiconductor, Inc.
+dlink D-Link Systems, Inc.
denx Denx Software Engineering
+dmo Data Modul AG
edt Emerging Display Technologies
emmicro EM Microelectronic
epfl Ecole Polytechnique Fédérale de Lausanne
epson Seiko Epson Corp.
est ESTeem Wireless Modems
+eukrea Eukréa Electromatique
fsl Freescale Semiconductor
GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc.
gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+globalscale Globalscale Technologies, Inc.
gmt Global Mixed-mode Technology, Inc.
+google Google, Inc.
gumstix Gumstix, Inc.
haoyu Haoyu Microelectronic Co. Ltd.
hisilicon Hisilicon Limited.
@@ -46,9 +56,12 @@ hp Hewlett Packard
ibm International Business Machines (IBM)
idt Integrated Device Technologies, Inc.
img Imagination Technologies Ltd.
+intel Intel Corporation
intercontrol Inter Control Group
isl Intersil
karo Ka-Ro electronics GmbH
+lacie LaCie
+lantiq Lantiq Semiconductor
lg LG Corporation
linux Linux-specific binding
lsi LSI Corp. (LSI Logic)
@@ -56,22 +69,28 @@ marvell Marvell Technology Group Ltd.
maxim Maxim Integrated Products
microchip Microchip Technology Inc.
mosaixtech Mosaix Technologies, Inc.
+moxa Moxa
national National Semiconductor
neonode Neonode Inc.
+netgear NETGEAR
nintendo Nintendo
+nokia Nokia
nvidia NVIDIA
nxp NXP Semiconductors
onnn ON Semiconductor Corp.
+opencores OpenCores.org
panasonic Panasonic Corporation
phytec PHYTEC Messtechnik GmbH
picochip Picochip Ltd
powervr PowerVR (deprecated, use img)
qca Qualcomm Atheros, Inc.
qcom Qualcomm Technologies, Inc
+qnap QNAP Systems, Inc.
ralink Mediatek/Ralink Technology Corp.
ramtron Ramtron International
realtek Realtek Semiconductor Corp.
renesas Renesas Electronics Corporation
+ricoh Ricoh Co. Ltd.
rockchip Fuzhou Rockchip Electronics Co., Ltd
samsung Samsung Semiconductor
sbs Smart Battery System
@@ -79,19 +98,24 @@ schindler Schindler
sil Silicon Image
silabs Silicon Laboratories
simtek
+sii Seiko Instruments, Inc.
sirf SiRF Technology, Inc.
+smsc Standard Microsystems Corporation
snps Synopsys, Inc.
spansion Spansion Inc.
st STMicroelectronics
ste ST-Ericsson
stericsson ST-Ericsson
+synology Synology, Inc.
ti Texas Instruments
tlm Trusted Logic Mobility
toshiba Toshiba Corporation
toumaz Toumaz
v3 V3 Semiconductor
via VIA Technologies, Inc.
+voipac Voipac Technologies s.r.o.
winbond Winbond Electronics corp.
wlf Wolfson Microelectronics
wm Wondermedia Technologies, Inc.
+xes Extreme Engineering Solutions (X-ES)
xlnx Xilinx
diff --git a/Documentation/devicetree/bindings/video/analog-tv-connector.txt b/Documentation/devicetree/bindings/video/analog-tv-connector.txt
new file mode 100644
index 000000000000..0218fcdc1299
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/analog-tv-connector.txt
@@ -0,0 +1,25 @@
+Analog TV Connector
+===================
+
+Required properties:
+- compatible: "composite-connector" or "svideo-connector"
+
+Optional properties:
+- label: a symbolic name for the connector
+
+Required nodes:
+- Video port for TV input
+
+Example
+-------
+
+tv: connector {
+ compatible = "composite-connector";
+ label = "tv";
+
+ port {
+ tv_connector_in: endpoint {
+ remote-endpoint = <&venc_out>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/video/dvi-connector.txt b/Documentation/devicetree/bindings/video/dvi-connector.txt
new file mode 100644
index 000000000000..fc53f7c60bc6
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/dvi-connector.txt
@@ -0,0 +1,35 @@
+DVI Connector
+==============
+
+Required properties:
+- compatible: "dvi-connector"
+
+Optional properties:
+- label: a symbolic name for the connector
+- ddc-i2c-bus: phandle to the i2c bus that is connected to DVI DDC
+- analog: the connector has DVI analog pins
+- digital: the connector has DVI digital pins
+- dual-link: the connector has pins for DVI dual-link
+
+Required nodes:
+- Video port for DVI input
+
+Note: One (or both) of 'analog' or 'digital' must be set.
+
+Example
+-------
+
+dvi0: connector@0 {
+ compatible = "dvi-connector";
+ label = "dvi";
+
+ digital;
+
+ ddc-i2c-bus = <&i2c3>;
+
+ port {
+ dvi_connector_in: endpoint {
+ remote-endpoint = <&tfp410_out>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/video/fsl,imx-fb.txt b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
index 46da08db186a..0329f60d431e 100644
--- a/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
+++ b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
@@ -15,8 +15,12 @@ Required nodes:
- fsl,pcr: LCDC PCR value
Optional properties:
+- lcd-supply: Regulator for LCD supply voltage.
- fsl,dmacr: DMA Control Register value. This is optional. By default, the
register is not modified as recommended by the datasheet.
+- fsl,lpccr: Contrast Control Register value. This property provides the
+ default value for the contrast control register.
+ If that property is ommited, the register is zeroed.
- fsl,lscr1: LCDC Sharp Configuration Register value.
Example:
diff --git a/Documentation/devicetree/bindings/video/hdmi-connector.txt b/Documentation/devicetree/bindings/video/hdmi-connector.txt
new file mode 100644
index 000000000000..ccccc19e2573
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/hdmi-connector.txt
@@ -0,0 +1,28 @@
+HDMI Connector
+==============
+
+Required properties:
+- compatible: "hdmi-connector"
+- type: the HDMI connector type: "a", "b", "c", "d" or "e"
+
+Optional properties:
+- label: a symbolic name for the connector
+
+Required nodes:
+- Video port for HDMI input
+
+Example
+-------
+
+hdmi0: connector@1 {
+ compatible = "hdmi-connector";
+ label = "hdmi";
+
+ type = "a";
+
+ port {
+ hdmi_connector_in: endpoint {
+ remote-endpoint = <&tpd12s015_out>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/video/panel-dsi-cm.txt b/Documentation/devicetree/bindings/video/panel-dsi-cm.txt
new file mode 100644
index 000000000000..dce48eb9db57
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/panel-dsi-cm.txt
@@ -0,0 +1,29 @@
+Generic MIPI DSI Command Mode Panel
+===================================
+
+Required properties:
+- compatible: "panel-dsi-cm"
+
+Optional properties:
+- label: a symbolic name for the panel
+- reset-gpios: panel reset gpio
+- te-gpios: panel TE gpio
+
+Required nodes:
+- Video port for DSI input
+
+Example
+-------
+
+lcd0: display {
+ compatible = "tpo,taal", "panel-dsi-cm";
+ label = "lcd0";
+
+ reset-gpios = <&gpio4 6 GPIO_ACTIVE_HIGH>;
+
+ port {
+ lcd0_in: endpoint {
+ remote-endpoint = <&dsi1_out_ep>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/video/sony,acx565akm.txt b/Documentation/devicetree/bindings/video/sony,acx565akm.txt
new file mode 100644
index 000000000000..e12333280749
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/sony,acx565akm.txt
@@ -0,0 +1,30 @@
+Sony ACX565AKM SDI Panel
+========================
+
+Required properties:
+- compatible: "sony,acx565akm"
+
+Optional properties:
+- label: a symbolic name for the panel
+- reset-gpios: panel reset gpio
+
+Required nodes:
+- Video port for SDI input
+
+Example
+-------
+
+acx565akm@2 {
+ compatible = "sony,acx565akm";
+ spi-max-frequency = <6000000>;
+ reg = <2>;
+
+ label = "lcd";
+ reset-gpios = <&gpio3 26 GPIO_ACTIVE_HIGH>; /* 90 */
+
+ port {
+ lcd_in: endpoint {
+ remote-endpoint = <&sdi_out>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/video/ti,omap-dss.txt b/Documentation/devicetree/bindings/video/ti,omap-dss.txt
new file mode 100644
index 000000000000..d5f1a3fe3109
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/ti,omap-dss.txt
@@ -0,0 +1,211 @@
+Texas Instruments OMAP Display Subsystem
+========================================
+
+Generic Description
+-------------------
+
+This document is a generic description of the OMAP Display Subsystem bindings.
+Binding details for each OMAP SoC version are described in respective binding
+documentation.
+
+The OMAP Display Subsystem (DSS) hardware consists of DSS Core, DISPC module and
+a number of encoder modules. All DSS versions contain DSS Core and DISPC, but
+the encoder modules vary.
+
+The DSS Core is the parent of the other DSS modules, and manages clock routing,
+integration to the SoC, etc.
+
+DISPC is the display controller, which reads pixels from the memory and outputs
+a RGB pixel stream to encoders.
+
+The encoder modules encode the received RGB pixel stream to a video output like
+HDMI, MIPI DPI, etc.
+
+Video Ports
+-----------
+
+The DSS Core and the encoders have video port outputs. The structure of the
+video ports is described in Documentation/devicetree/bindings/video/video-
+ports.txt, and the properties for the ports and endpoints for each encoder are
+described in the SoC's DSS binding documentation.
+
+The video ports are used to describe the connections to external hardware, like
+panels or external encoders.
+
+Aliases
+-------
+
+The board dts file may define aliases for displays to assign "displayX" style
+name for each display. If no aliases are defined, a semi-random number is used
+for the display.
+
+Example
+-------
+
+A shortened example of the DSS description for OMAP4, with non-relevant parts
+removed, defined in omap4.dtsi:
+
+dss: dss@58000000 {
+ compatible = "ti,omap4-dss";
+ reg = <0x58000000 0x80>;
+ status = "disabled";
+ ti,hwmods = "dss_core";
+ clocks = <&dss_dss_clk>;
+ clock-names = "fck";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ dispc@58001000 {
+ compatible = "ti,omap4-dispc";
+ reg = <0x58001000 0x1000>;
+ interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "dss_dispc";
+ clocks = <&dss_dss_clk>;
+ clock-names = "fck";
+ };
+
+ hdmi: encoder@58006000 {
+ compatible = "ti,omap4-hdmi";
+ reg = <0x58006000 0x200>,
+ <0x58006200 0x100>,
+ <0x58006300 0x100>,
+ <0x58006400 0x1000>;
+ reg-names = "wp", "pll", "phy", "core";
+ interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ ti,hwmods = "dss_hdmi";
+ clocks = <&dss_48mhz_clk>, <&dss_sys_clk>;
+ clock-names = "fck", "sys_clk";
+ };
+};
+
+A shortened example of the board description for OMAP4 Panda board, defined in
+omap4-panda.dts.
+
+The Panda board has a DVI and a HDMI connector, and the board contains a TFP410
+chip (MIPI DPI to DVI encoder) and a TPD12S015 chip (HDMI ESD protection & level
+shifter). The video pipelines for the connectors are formed as follows:
+
+DSS Core --(MIPI DPI)--> TFP410 --(DVI)--> DVI Connector
+OMAP HDMI --(HDMI)--> TPD12S015 --(HDMI)--> HDMI Connector
+
+/ {
+ aliases {
+ display0 = &dvi0;
+ display1 = &hdmi0;
+ };
+
+ tfp410: encoder@0 {
+ compatible = "ti,tfp410";
+ gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; /* 0, power-down */
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&tfp410_pins>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ tfp410_in: endpoint@0 {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ tfp410_out: endpoint@0 {
+ remote-endpoint = <&dvi_connector_in>;
+ };
+ };
+ };
+ };
+
+ dvi0: connector@0 {
+ compatible = "dvi-connector";
+ label = "dvi";
+
+ i2c-bus = <&i2c3>;
+
+ port {
+ dvi_connector_in: endpoint {
+ remote-endpoint = <&tfp410_out>;
+ };
+ };
+ };
+
+ tpd12s015: encoder@1 {
+ compatible = "ti,tpd12s015";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&tpd12s015_pins>;
+
+ gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>, /* 60, CT CP HPD */
+ <&gpio2 9 GPIO_ACTIVE_HIGH>, /* 41, LS OE */
+ <&gpio2 31 GPIO_ACTIVE_HIGH>; /* 63, HPD */
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ tpd12s015_in: endpoint@0 {
+ remote-endpoint = <&hdmi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ tpd12s015_out: endpoint@0 {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+ };
+ };
+ };
+
+ hdmi0: connector@1 {
+ compatible = "hdmi-connector";
+ label = "hdmi";
+
+ port {
+ hdmi_connector_in: endpoint {
+ remote-endpoint = <&tpd12s015_out>;
+ };
+ };
+ };
+};
+
+&dss {
+ status = "ok";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&dss_dpi_pins>;
+
+ port {
+ dpi_out: endpoint {
+ remote-endpoint = <&tfp410_in>;
+ data-lines = <24>;
+ };
+ };
+};
+
+&hdmi {
+ status = "ok";
+ vdda-supply = <&vdac>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&dss_hdmi_pins>;
+
+ port {
+ hdmi_out: endpoint {
+ remote-endpoint = <&tpd12s015_in>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/video/ti,omap2-dss.txt b/Documentation/devicetree/bindings/video/ti,omap2-dss.txt
new file mode 100644
index 000000000000..fa8bb2ed1170
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/ti,omap2-dss.txt
@@ -0,0 +1,54 @@
+Texas Instruments OMAP2 Display Subsystem
+=========================================
+
+See Documentation/devicetree/bindings/video/ti,omap-dss.txt for generic
+description about OMAP Display Subsystem bindings.
+
+DSS Core
+--------
+
+Required properties:
+- compatible: "ti,omap2-dss"
+- reg: address and length of the register space
+- ti,hwmods: "dss_core"
+
+Optional nodes:
+- Video port for DPI output
+
+DPI Endpoint required properties:
+- data-lines: number of lines used
+
+
+DISPC
+-----
+
+Required properties:
+- compatible: "ti,omap2-dispc"
+- reg: address and length of the register space
+- ti,hwmods: "dss_dispc"
+- interrupts: the DISPC interrupt
+
+
+RFBI
+----
+
+Required properties:
+- compatible: "ti,omap2-rfbi"
+- reg: address and length of the register space
+- ti,hwmods: "dss_rfbi"
+
+
+VENC
+----
+
+Required properties:
+- compatible: "ti,omap2-venc"
+- reg: address and length of the register space
+- ti,hwmods: "dss_venc"
+- vdda-supply: power supply for DAC
+
+VENC Endpoint required properties:
+
+Required properties:
+- ti,invert-polarity: invert the polarity of the video signal
+- ti,channels: 1 for composite, 2 for s-video
diff --git a/Documentation/devicetree/bindings/video/ti,omap3-dss.txt b/Documentation/devicetree/bindings/video/ti,omap3-dss.txt
new file mode 100644
index 000000000000..0023fa4b1328
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/ti,omap3-dss.txt
@@ -0,0 +1,83 @@
+Texas Instruments OMAP3 Display Subsystem
+=========================================
+
+See Documentation/devicetree/bindings/video/ti,omap-dss.txt for generic
+description about OMAP Display Subsystem bindings.
+
+DSS Core
+--------
+
+Required properties:
+- compatible: "ti,omap3-dss"
+- reg: address and length of the register space
+- ti,hwmods: "dss_core"
+- clocks: handle to fclk
+- clock-names: "fck"
+
+Optional nodes:
+- Video ports:
+ - Port 0: DPI output
+ - Port 1: SDI output
+
+DPI Endpoint required properties:
+- data-lines: number of lines used
+
+SDI Endpoint required properties:
+- datapairs: number of datapairs used
+
+
+DISPC
+-----
+
+Required properties:
+- compatible: "ti,omap3-dispc"
+- reg: address and length of the register space
+- ti,hwmods: "dss_dispc"
+- interrupts: the DISPC interrupt
+- clocks: handle to fclk
+- clock-names: "fck"
+
+
+RFBI
+----
+
+Required properties:
+- compatible: "ti,omap3-rfbi"
+- reg: address and length of the register space
+- ti,hwmods: "dss_rfbi"
+- clocks: handles to fclk and iclk
+- clock-names: "fck", "ick"
+
+
+VENC
+----
+
+Required properties:
+- compatible: "ti,omap3-venc"
+- reg: address and length of the register space
+- ti,hwmods: "dss_venc"
+- vdda-supply: power supply for DAC
+- clocks: handle to fclk
+- clock-names: "fck"
+
+VENC Endpoint required properties:
+- ti,invert-polarity: invert the polarity of the video signal
+- ti,channels: 1 for composite, 2 for s-video
+
+
+DSI
+---
+
+Required properties:
+- compatible: "ti,omap3-dsi"
+- reg: addresses and lengths of the register spaces for 'proto', 'phy' and 'pll'
+- reg-names: "proto", "phy", "pll"
+- interrupts: the DSI interrupt line
+- ti,hwmods: "dss_dsi1"
+- vdd-supply: power supply for DSI
+- clocks: handles to fclk and pll clock
+- clock-names: "fck", "sys_clk"
+
+DSI Endpoint required properties:
+- lanes: list of pin numbers for the DSI lanes: CLK+, CLK-, DATA0+, DATA0-,
+ DATA1+, DATA1-, ...
diff --git a/Documentation/devicetree/bindings/video/ti,omap4-dss.txt b/Documentation/devicetree/bindings/video/ti,omap4-dss.txt
new file mode 100644
index 000000000000..f85d6fcfa705
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/ti,omap4-dss.txt
@@ -0,0 +1,111 @@
+Texas Instruments OMAP4 Display Subsystem
+=========================================
+
+See Documentation/devicetree/bindings/video/ti,omap-dss.txt for generic
+description about OMAP Display Subsystem bindings.
+
+DSS Core
+--------
+
+Required properties:
+- compatible: "ti,omap4-dss"
+- reg: address and length of the register space
+- ti,hwmods: "dss_core"
+- clocks: handle to fclk
+- clock-names: "fck"
+
+Required nodes:
+- DISPC
+
+Optional nodes:
+- DSS Submodules: RFBI, VENC, DSI, HDMI
+- Video port for DPI output
+
+DPI Endpoint required properties:
+- data-lines: number of lines used
+
+
+DISPC
+-----
+
+Required properties:
+- compatible: "ti,omap4-dispc"
+- reg: address and length of the register space
+- ti,hwmods: "dss_dispc"
+- interrupts: the DISPC interrupt
+- clocks: handle to fclk
+- clock-names: "fck"
+
+
+RFBI
+----
+
+Required properties:
+- compatible: "ti,omap4-rfbi"
+- reg: address and length of the register space
+- ti,hwmods: "dss_rfbi"
+- clocks: handles to fclk and iclk
+- clock-names: "fck", "ick"
+
+Optional nodes:
+- Video port for RFBI output
+- RFBI controlled peripherals
+
+
+VENC
+----
+
+Required properties:
+- compatible: "ti,omap4-venc"
+- reg: address and length of the register space
+- ti,hwmods: "dss_venc"
+- vdda-supply: power supply for DAC
+- clocks: handle to fclk
+- clock-names: "fck"
+
+Optional nodes:
+- Video port for VENC output
+
+VENC Endpoint required properties:
+- ti,invert-polarity: invert the polarity of the video signal
+- ti,channels: 1 for composite, 2 for s-video
+
+
+DSI
+---
+
+Required properties:
+- compatible: "ti,omap4-dsi"
+- reg: addresses and lengths of the register spaces for 'proto', 'phy' and 'pll'
+- reg-names: "proto", "phy", "pll"
+- interrupts: the DSI interrupt line
+- ti,hwmods: "dss_dsi1" or "dss_dsi2"
+- vdd-supply: power supply for DSI
+- clocks: handles to fclk and pll clock
+- clock-names: "fck", "sys_clk"
+
+Optional nodes:
+- Video port for DSI output
+- DSI controlled peripherals
+
+DSI Endpoint required properties:
+- lanes: list of pin numbers for the DSI lanes: CLK+, CLK-, DATA0+, DATA0-,
+ DATA1+, DATA1-, ...
+
+
+HDMI
+----
+
+Required properties:
+- compatible: "ti,omap4-hdmi"
+- reg: addresses and lengths of the register spaces for 'wp', 'pll', 'phy',
+ 'core'
+- reg-names: "wp", "pll", "phy", "core"
+- interrupts: the HDMI interrupt line
+- ti,hwmods: "dss_hdmi"
+- vdda-supply: vdda power supply
+- clocks: handles to fclk and pll clock
+- clock-names: "fck", "sys_clk"
+
+Optional nodes:
+- Video port for HDMI output
diff --git a/Documentation/devicetree/bindings/video/ti,tfp410.txt b/Documentation/devicetree/bindings/video/ti,tfp410.txt
new file mode 100644
index 000000000000..2cbe32a3d0bb
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/ti,tfp410.txt
@@ -0,0 +1,41 @@
+TFP410 DPI to DVI encoder
+=========================
+
+Required properties:
+- compatible: "ti,tfp410"
+
+Optional properties:
+- powerdown-gpios: power-down gpio
+
+Required nodes:
+- Video port 0 for DPI input
+- Video port 1 for DVI output
+
+Example
+-------
+
+tfp410: encoder@0 {
+ compatible = "ti,tfp410";
+ powerdown-gpios = <&twl_gpio 2 GPIO_ACTIVE_LOW>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ tfp410_in: endpoint@0 {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ tfp410_out: endpoint@0 {
+ remote-endpoint = <&dvi_connector_in>;
+ };
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/video/ti,tpd12s015.txt b/Documentation/devicetree/bindings/video/ti,tpd12s015.txt
new file mode 100644
index 000000000000..26e6d32e3f20
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/ti,tpd12s015.txt
@@ -0,0 +1,44 @@
+TPD12S015 HDMI level shifter and ESD protection chip
+====================================================
+
+Required properties:
+- compatible: "ti,tpd12s015"
+
+Optional properties:
+- gpios: CT CP HPD, LS OE and HPD gpios
+
+Required nodes:
+- Video port 0 for HDMI input
+- Video port 1 for HDMI output
+
+Example
+-------
+
+tpd12s015: encoder@1 {
+ compatible = "ti,tpd12s015";
+
+ gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>, /* 60, CT CP HPD */
+ <&gpio2 9 GPIO_ACTIVE_HIGH>, /* 41, LS OE */
+ <&gpio2 31 GPIO_ACTIVE_HIGH>; /* 63, HPD */
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ tpd12s015_in: endpoint@0 {
+ remote-endpoint = <&hdmi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ tpd12s015_out: endpoint@0 {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/watchdog/marvel.txt b/Documentation/devicetree/bindings/watchdog/marvel.txt
index 5dc8d30061ce..de11eb4c121f 100644
--- a/Documentation/devicetree/bindings/watchdog/marvel.txt
+++ b/Documentation/devicetree/bindings/watchdog/marvel.txt
@@ -3,17 +3,24 @@
Required Properties:
- Compatibility : "marvell,orion-wdt"
-- reg : Address of the timer registers
+ "marvell,armada-370-wdt"
+ "marvell,armada-xp-wdt"
+
+- reg : Should contain two entries: first one with the
+ timer control address, second one with the
+ rstout enable address.
Optional properties:
+- interrupts : Contains the IRQ for watchdog expiration
- timeout-sec : Contains the watchdog timeout in seconds
Example:
wdt@20300 {
compatible = "marvell,orion-wdt";
- reg = <0x20300 0x28>;
+ reg = <0x20300 0x28>, <0x20108 0x4>;
+ interrupts = <3>;
timeout-sec = <10>;
status = "okay";
};
diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware
index 5d5ee4c13fa6..d91b8be80b66 100755
--- a/Documentation/dvb/get_dvb_firmware
+++ b/Documentation/dvb/get_dvb_firmware
@@ -28,8 +28,8 @@ use IO::Handle;
"opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718",
"af9015", "ngene", "az6027", "lme2510_lg", "lme2510c_s7395",
"lme2510c_s7395_old", "drxk", "drxk_terratec_h5",
- "drxk_hauppauge_hvr930c", "tda10071", "it9135", "it9137",
- "drxk_pctv", "drxk_terratec_htc_stick", "sms1xxx_hcw");
+ "drxk_hauppauge_hvr930c", "tda10071", "it9135", "drxk_pctv",
+ "drxk_terratec_htc_stick", "sms1xxx_hcw");
# Check args
syntax() if (scalar(@ARGV) != 1);
@@ -727,24 +727,6 @@ sub it9135 {
"$fwfile1 $fwfile2"
}
-sub it9137 {
- my $url = "http://kworld.server261.com/kworld/CD/ITE_TiVme/V1.00/";
- my $zipfile = "Driver_V10.323.1.0412.100412.zip";
- my $hash = "79b597dc648698ed6820845c0c9d0d37";
- my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 0);
- my $drvfile = "Driver_V10.323.1.0412.100412/Data/x86/IT9135BDA.sys";
- my $fwfile = "dvb-usb-it9137-01.fw";
-
- checkstandard();
-
- wgetfile($zipfile, $url . $zipfile);
- verify($zipfile, $hash);
- unzip($zipfile, $tmpdir);
- extract("$tmpdir/$drvfile", 69632, 5731, "$fwfile");
-
- "$fwfile"
-}
-
sub tda10071 {
my $sourcefile = "PCTV_460e_reference.zip";
my $url = "ftp://ftp.pctvsystems.com/TV/driver/PCTV%2070e%2080e%20100e%20320e%20330e%20800e/";
diff --git a/Documentation/dvb/it9137.txt b/Documentation/dvb/it9137.txt
deleted file mode 100644
index 9e6726eead90..000000000000
--- a/Documentation/dvb/it9137.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-To extract firmware for Kworld UB499-2T (id 1b80:e409) you need to copy the
-following file(s) to this directory.
-
-IT9135BDA.sys Dated Mon 22 Mar 2010 02:20:08 GMT
-
-extract using dd
-dd if=IT9135BDA.sys ibs=1 skip=69632 count=5731 of=dvb-usb-it9137-01.fw
-
-copy to default firmware location.
diff --git a/Documentation/edac.txt b/Documentation/edac.txt
index 56c7e936430f..cb4c2cefd45a 100644
--- a/Documentation/edac.txt
+++ b/Documentation/edac.txt
@@ -6,7 +6,7 @@ Written by Doug Thompson <dougthompson@xmission.com>
7 Dec 2005
17 Jul 2007 Updated
-(c) Mauro Carvalho Chehab <mchehab@redhat.com>
+(c) Mauro Carvalho Chehab
05 Aug 2009 Nehalem interface
EDAC is maintained and written by:
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
index 5b0c083d7c0e..f424e0e5b46b 100644
--- a/Documentation/filesystems/Locking
+++ b/Documentation/filesystems/Locking
@@ -47,6 +47,8 @@ prototypes:
int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
+ int (*rename2) (struct inode *, struct dentry *,
+ struct inode *, struct dentry *, unsigned int);
int (*readlink) (struct dentry *, char __user *,int);
void * (*follow_link) (struct dentry *, struct nameidata *);
void (*put_link) (struct dentry *, struct nameidata *, void *);
@@ -78,6 +80,7 @@ mkdir: yes
unlink: yes (both)
rmdir: yes (both) (see below)
rename: yes (all) (see below)
+rename2: yes (all) (see below)
readlink: no
follow_link: no
put_link: no
@@ -96,7 +99,8 @@ tmpfile: no
Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
victim.
- cross-directory ->rename() has (per-superblock) ->s_vfs_rename_sem.
+ cross-directory ->rename() and rename2() has (per-superblock)
+->s_vfs_rename_sem.
See Documentation/filesystems/directory-locking for more detailed discussion
of the locking scheme for directory operations.
diff --git a/Documentation/filesystems/autofs4-mount-control.txt b/Documentation/filesystems/autofs4-mount-control.txt
index 4c95935cbcf4..aff22113a986 100644
--- a/Documentation/filesystems/autofs4-mount-control.txt
+++ b/Documentation/filesystems/autofs4-mount-control.txt
@@ -255,7 +255,7 @@ AUTOFS_DEV_IOCTL_OPENMOUNT and AUTOFS_DEV_IOCTL_CLOSEMOUNT
Obtain and release a file descriptor for an autofs managed mount point
path. The open call requires an initialized struct autofs_dev_ioctl with
-the the path field set and the size field adjusted appropriately as well
+the path field set and the size field adjusted appropriately as well
as the arg1 field set to the device number of the autofs mount. The
device number can be obtained from the mount options shown in
/proc/mounts. The close call requires an initialized struct
diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt
index b8d284975f0f..25311e113e75 100644
--- a/Documentation/filesystems/f2fs.txt
+++ b/Documentation/filesystems/f2fs.txt
@@ -122,6 +122,10 @@ disable_ext_identify Disable the extension list configured by mkfs, so f2fs
inline_xattr Enable the inline xattrs feature.
inline_data Enable the inline data feature: New created small(<~3.4k)
files can be written into inode block.
+flush_merge Merge concurrent cache_flush commands as much as possible
+ to eliminate redundant command issues. If the underlying
+ device handles the cache_flush command relatively slowly,
+ recommend to enable this option.
================================================================================
DEBUGFS ENTRIES
@@ -169,9 +173,11 @@ Files in /sys/fs/f2fs/<devname>
reclaim_segments This parameter controls the number of prefree
segments to be reclaimed. If the number of prefree
- segments is larger than this number, f2fs tries to
- conduct checkpoint to reclaim the prefree segments
- to free segments. By default, 100 segments, 200MB.
+ segments is larger than the number of segments
+ in the proportion to the percentage over total
+ volume size, f2fs tries to conduct checkpoint to
+ reclaim the prefree segments to free segments.
+ By default, 5% over total # of segments.
max_small_discards This parameter controls the number of discard
commands that consist small blocks less than 2MB.
@@ -195,6 +201,17 @@ Files in /sys/fs/f2fs/<devname>
cleaning operations. The default value is 4096
which covers 8GB block address range.
+ dir_level This parameter controls the directory level to
+ support large directory. If a directory has a
+ number of files, it can reduce the file lookup
+ latency by increasing this dir_level value.
+ Otherwise, it needs to decrease this value to
+ reduce the space overhead. The default value is 0.
+
+ ram_thresh This parameter controls the memory footprint used
+ by free nids and cached nat entries. By default,
+ 10 is set, which indicates 10 MB / 1 GB RAM.
+
================================================================================
USAGE
================================================================================
@@ -444,9 +461,11 @@ The number of blocks and buckets are determined by,
# of blocks in level #n = |
`- 4, Otherwise
- ,- 2^n, if n < MAX_DIR_HASH_DEPTH / 2,
+ ,- 2^ (n + dir_level),
+ | if n < MAX_DIR_HASH_DEPTH / 2,
# of buckets in level #n = |
- `- 2^((MAX_DIR_HASH_DEPTH / 2) - 1), Otherwise
+ `- 2^((MAX_DIR_HASH_DEPTH / 2 + dir_level) - 1),
+ Otherwise
When F2FS finds a file name in a directory, at first a hash value of the file
name is calculated. Then, F2FS scans the hash table in level #0 to find the
diff --git a/Documentation/filesystems/hfsplus.txt b/Documentation/filesystems/hfsplus.txt
index af1628a1061c..59f7569fc9ed 100644
--- a/Documentation/filesystems/hfsplus.txt
+++ b/Documentation/filesystems/hfsplus.txt
@@ -56,4 +56,4 @@ References
kernel source: <file:fs/hfsplus>
-Apple Technote 1150 http://developer.apple.com/technotes/tn/tn1150.html
+Apple Technote 1150 https://developer.apple.com/legacy/library/technotes/tn/tn1150.html
diff --git a/Documentation/filesystems/nilfs2.txt b/Documentation/filesystems/nilfs2.txt
index 06887d46ccf2..41c3d332acc9 100644
--- a/Documentation/filesystems/nilfs2.txt
+++ b/Documentation/filesystems/nilfs2.txt
@@ -25,9 +25,8 @@ available from the following download page. At least "mkfs.nilfs2",
cleaner or garbage collector) are required. Details on the tools are
described in the man pages included in the package.
-Project web page: http://www.nilfs.org/en/
-Download page: http://www.nilfs.org/en/download.html
-Git tree web page: http://www.nilfs.org/git/
+Project web page: http://nilfs.sourceforge.net/
+Download page: http://nilfs.sourceforge.net/en/download.html
List info: http://vger.kernel.org/vger-lists.html#linux-nilfs
Caveats
@@ -111,6 +110,13 @@ Table of NILFS2 specific ioctls
nilfs_resize utilities and by nilfs_cleanerd
daemon.
+ NILFS_IOCTL_SET_SUINFO Modify segment usage info of requested
+ segments. This ioctl is used by
+ nilfs_cleanerd daemon to skip unnecessary
+ cleaning operation of segments and reduce
+ performance penalty or wear of flash device
+ due to redundant move of in-use blocks.
+
NILFS_IOCTL_GET_SUSTAT Return segment usage statistics. This ioctl
is used in lssu, nilfs_resize utilities and
by nilfs_cleanerd daemon.
diff --git a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt
index 791af8dac065..61947facfc07 100644
--- a/Documentation/filesystems/ntfs.txt
+++ b/Documentation/filesystems/ntfs.txt
@@ -455,8 +455,6 @@ not have this problem with odd numbers of sectors.
ChangeLog
=========
-Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
-
2.1.30:
- Fix writev() (it kept writing the first segment over and over again
instead of moving onto subsequent segments).
diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index fe2b7ae6f962..0f3a1390bf00 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -295,9 +295,9 @@ in the beginning of ->setattr unconditionally.
->clear_inode() and ->delete_inode() are gone; ->evict_inode() should
be used instead. It gets called whenever the inode is evicted, whether it has
remaining links or not. Caller does *not* evict the pagecache or inode-associated
-metadata buffers; getting rid of those is responsibility of method, as it had
-been for ->delete_inode(). Caller makes sure async writeback cannot be running
-for the inode while (or after) ->evict_inode() is called.
+metadata buffers; the method has to use truncate_inode_pages_final() to get rid
+of those. Caller makes sure async writeback cannot be running for the inode while
+(or after) ->evict_inode() is called.
->drop_inode() returns int now; it's called on final iput() with
inode->i_lock held and it returns true if filesystems wants the inode to be
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index c53784c119c8..94eb86287bcb 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -347,6 +347,8 @@ struct inode_operations {
int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
+ int (*rename2) (struct inode *, struct dentry *,
+ struct inode *, struct dentry *, unsigned int);
int (*readlink) (struct dentry *, char __user *,int);
void * (*follow_link) (struct dentry *, struct nameidata *);
void (*put_link) (struct dentry *, struct nameidata *, void *);
@@ -414,6 +416,20 @@ otherwise noted.
rename: called by the rename(2) system call to rename the object to
have the parent and name given by the second inode and dentry.
+ rename2: this has an additional flags argument compared to rename.
+ If no flags are supported by the filesystem then this method
+ need not be implemented. If some flags are supported then the
+ filesystem must return -EINVAL for any unsupported or unknown
+ flags. Currently the following flags are implemented:
+ (1) RENAME_NOREPLACE: this flag indicates that if the target
+ of the rename exists the rename should fail with -EEXIST
+ instead of replacing the target. The VFS already checks for
+ existence, so for local filesystems the RENAME_NOREPLACE
+ implementation is equivalent to plain rename.
+ (2) RENAME_EXCHANGE: exchange source and target. Both must
+ exist; this is checked by the VFS. Unlike plain rename,
+ source and target may be of different type.
+
readlink: called by the readlink(2) system call. Only required if
you want to support reading symbolic links
diff --git a/Documentation/futex-requeue-pi.txt b/Documentation/futex-requeue-pi.txt
index 9dc1ff4fd536..31b16610c416 100644
--- a/Documentation/futex-requeue-pi.txt
+++ b/Documentation/futex-requeue-pi.txt
@@ -67,7 +67,7 @@ pthread_cond_wait_pi(cond, mutex)
lock(cond->__data.__lock);
} while(...)
unlock(cond->__data.__lock);
- /* the kernel acquired the the mutex for us */
+ /* the kernel acquired the mutex for us */
}
pthread_cond_broadcast_pi(cond)
diff --git a/Documentation/gpio/consumer.txt b/Documentation/gpio/consumer.txt
index e42f77d8d4ca..09854fe59307 100644
--- a/Documentation/gpio/consumer.txt
+++ b/Documentation/gpio/consumer.txt
@@ -154,6 +154,7 @@ raw line value:
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
+ int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
The active-low state of a GPIO can also be queried using the following call:
diff --git a/Documentation/gpio/driver.txt b/Documentation/gpio/driver.txt
index 9da0bfa74781..f73cc7b5dc85 100644
--- a/Documentation/gpio/driver.txt
+++ b/Documentation/gpio/driver.txt
@@ -62,6 +62,37 @@ Any debugfs dump method should normally ignore signals which haven't been
requested as GPIOs. They can use gpiochip_is_requested(), which returns either
NULL or the label associated with that GPIO when it was requested.
+
+GPIO drivers providing IRQs
+---------------------------
+It is custom that GPIO drivers (GPIO chips) are also providing interrupts,
+most often cascaded off a parent interrupt controller, and in some special
+cases the GPIO logic is melded with a SoC's primary interrupt controller.
+
+The IRQ portions of the GPIO block are implemented using an irqchip, using
+the header <linux/irq.h>. So basically such a driver is utilizing two sub-
+systems simultaneously: gpio and irq.
+
+It is legal for any IRQ consumer to request an IRQ from any irqchip no matter
+if that is a combined GPIO+IRQ driver. The basic premise is that gpio_chip and
+irq_chip are orthogonal, and offering their services independent of each
+other.
+
+gpiod_to_irq() is just a convenience function to figure out the IRQ for a
+certain GPIO line and should not be relied upon to have been called before
+the IRQ is used.
+
+So always prepare the hardware and make it ready for action in respective
+callbacks from the GPIO and irqchip APIs. Do not rely on gpiod_to_irq() having
+been called first.
+
+This orthogonality leads to ambiguities that we need to solve: if there is
+competition inside the subsystem which side is using the resource (a certain
+GPIO line and register for example) it needs to deny certain operations and
+keep track of usage inside of the gpiolib subsystem. This is why the API
+below exists.
+
+
Locking IRQ usage
-----------------
Input GPIOs can be used as IRQ signals. When this happens, a driver is requested
@@ -73,3 +104,7 @@ This will prevent the use of non-irq related GPIO APIs until the GPIO IRQ lock
is released:
void gpiod_unlock_as_irq(struct gpio_desc *desc)
+
+When implementing an irqchip inside a GPIO driver, these two functions should
+typically be called in the .startup() and .shutdown() callbacks from the
+irqchip.
diff --git a/Documentation/hid/hid-transport.txt b/Documentation/hid/hid-transport.txt
new file mode 100644
index 000000000000..3dcba9fd4a3a
--- /dev/null
+++ b/Documentation/hid/hid-transport.txt
@@ -0,0 +1,317 @@
+ HID I/O Transport Drivers
+ ===========================
+
+The HID subsystem is independent of the underlying transport driver. Initially,
+only USB was supported, but other specifications adopted the HID design and
+provided new transport drivers. The kernel includes at least support for USB,
+Bluetooth, I2C and user-space I/O drivers.
+
+1) HID Bus
+==========
+
+The HID subsystem is designed as a bus. Any I/O subsystem may provide HID
+devices and register them with the HID bus. HID core then loads generic device
+drivers on top of it. The transport drivers are responsible of raw data
+transport and device setup/management. HID core is responsible of
+report-parsing, report interpretation and the user-space API. Device specifics
+and quirks are handled by all layers depending on the quirk.
+
+ +-----------+ +-----------+ +-----------+ +-----------+
+ | Device #1 | | Device #i | | Device #j | | Device #k |
+ +-----------+ +-----------+ +-----------+ +-----------+
+ \\ // \\ //
+ +------------+ +------------+
+ | I/O Driver | | I/O Driver |
+ +------------+ +------------+
+ || ||
+ +------------------+ +------------------+
+ | Transport Driver | | Transport Driver |
+ +------------------+ +------------------+
+ \___ ___/
+ \ /
+ +----------------+
+ | HID Core |
+ +----------------+
+ / | | \
+ / | | \
+ ____________/ | | \_________________
+ / | | \
+ / | | \
+ +----------------+ +-----------+ +------------------+ +------------------+
+ | Generic Driver | | MT Driver | | Custom Driver #1 | | Custom Driver #2 |
+ +----------------+ +-----------+ +------------------+ +------------------+
+
+Example Drivers:
+ I/O: USB, I2C, Bluetooth-l2cap
+ Transport: USB-HID, I2C-HID, BT-HIDP
+
+Everything below "HID Core" is simplified in this graph as it is only of
+interest to HID device drivers. Transport drivers do not need to know the
+specifics.
+
+1.1) Device Setup
+-----------------
+
+I/O drivers normally provide hotplug detection or device enumeration APIs to the
+transport drivers. Transport drivers use this to find any suitable HID device.
+They allocate HID device objects and register them with HID core. Transport
+drivers are not required to register themselves with HID core. HID core is never
+aware of which transport drivers are available and is not interested in it. It
+is only interested in devices.
+
+Transport drivers attach a constant "struct hid_ll_driver" object with each
+device. Once a device is registered with HID core, the callbacks provided via
+this struct are used by HID core to communicate with the device.
+
+Transport drivers are responsible of detecting device failures and unplugging.
+HID core will operate a device as long as it is registered regardless of any
+device failures. Once transport drivers detect unplug or failure events, they
+must unregister the device from HID core and HID core will stop using the
+provided callbacks.
+
+1.2) Transport Driver Requirements
+----------------------------------
+
+The terms "asynchronous" and "synchronous" in this document describe the
+transmission behavior regarding acknowledgements. An asynchronous channel must
+not perform any synchronous operations like waiting for acknowledgements or
+verifications. Generally, HID calls operating on asynchronous channels must be
+running in atomic-context just fine.
+On the other hand, synchronous channels can be implemented by the transport
+driver in whatever way they like. They might just be the same as asynchronous
+channels, but they can also provide acknowledgement reports, automatic
+retransmission on failure, etc. in a blocking manner. If such functionality is
+required on asynchronous channels, a transport-driver must implement that via
+its own worker threads.
+
+HID core requires transport drivers to follow a given design. A Transport
+driver must provide two bi-directional I/O channels to each HID device. These
+channels must not necessarily be bi-directional in the hardware itself. A
+transport driver might just provide 4 uni-directional channels. Or it might
+multiplex all four on a single physical channel. However, in this document we
+will describe them as two bi-directional channels as they have several
+properties in common.
+
+ - Interrupt Channel (intr): The intr channel is used for asynchronous data
+ reports. No management commands or data acknowledgements are sent on this
+ channel. Any unrequested incoming or outgoing data report must be sent on
+ this channel and is never acknowledged by the remote side. Devices usually
+ send their input events on this channel. Outgoing events are normally
+ not send via intr, except if high throughput is required.
+ - Control Channel (ctrl): The ctrl channel is used for synchronous requests and
+ device management. Unrequested data input events must not be sent on this
+ channel and are normally ignored. Instead, devices only send management
+ events or answers to host requests on this channel.
+ The control-channel is used for direct blocking queries to the device
+ independent of any events on the intr-channel.
+ Outgoing reports are usually sent on the ctrl channel via synchronous
+ SET_REPORT requests.
+
+Communication between devices and HID core is mostly done via HID reports. A
+report can be of one of three types:
+
+ - INPUT Report: Input reports provide data from device to host. This
+ data may include button events, axis events, battery status or more. This
+ data is generated by the device and sent to the host with or without
+ requiring explicit requests. Devices can choose to send data continuously or
+ only on change.
+ - OUTPUT Report: Output reports change device states. They are sent from host
+ to device and may include LED requests, rumble requests or more. Output
+ reports are never sent from device to host, but a host can retrieve their
+ current state.
+ Hosts may choose to send output reports either continuously or only on
+ change.
+ - FEATURE Report: Feature reports are used for specific static device features
+ and never reported spontaneously. A host can read and/or write them to access
+ data like battery-state or device-settings.
+ Feature reports are never sent without requests. A host must explicitly set
+ or retrieve a feature report. This also means, feature reports are never sent
+ on the intr channel as this channel is asynchronous.
+
+INPUT and OUTPUT reports can be sent as pure data reports on the intr channel.
+For INPUT reports this is the usual operational mode. But for OUTPUT reports,
+this is rarely done as OUTPUT reports are normally quite scarce. But devices are
+free to make excessive use of asynchronous OUTPUT reports (for instance, custom
+HID audio speakers make great use of it).
+
+Plain reports must not be sent on the ctrl channel, though. Instead, the ctrl
+channel provides synchronous GET/SET_REPORT requests. Plain reports are only
+allowed on the intr channel and are the only means of data there.
+
+ - GET_REPORT: A GET_REPORT request has a report ID as payload and is sent
+ from host to device. The device must answer with a data report for the
+ requested report ID on the ctrl channel as a synchronous acknowledgement.
+ Only one GET_REPORT request can be pending for each device. This restriction
+ is enforced by HID core as several transport drivers don't allow multiple
+ simultaneous GET_REPORT requests.
+ Note that data reports which are sent as answer to a GET_REPORT request are
+ not handled as generic device events. That is, if a device does not operate
+ in continuous data reporting mode, an answer to GET_REPORT does not replace
+ the raw data report on the intr channel on state change.
+ GET_REPORT is only used by custom HID device drivers to query device state.
+ Normally, HID core caches any device state so this request is not necessary
+ on devices that follow the HID specs except during device initialization to
+ retrieve the current state.
+ GET_REPORT requests can be sent for any of the 3 report types and shall
+ return the current report state of the device. However, OUTPUT reports as
+ payload may be blocked by the underlying transport driver if the
+ specification does not allow them.
+ - SET_REPORT: A SET_REPORT request has a report ID plus data as payload. It is
+ sent from host to device and a device must update it's current report state
+ according to the given data. Any of the 3 report types can be used. However,
+ INPUT reports as payload might be blocked by the underlying transport driver
+ if the specification does not allow them.
+ A device must answer with a synchronous acknowledgement. However, HID core
+ does not require transport drivers to forward this acknowledgement to HID
+ core.
+ Same as for GET_REPORT, only one SET_REPORT can be pending at a time. This
+ restriction is enforced by HID core as some transport drivers do not support
+ multiple synchronous SET_REPORT requests.
+
+Other ctrl-channel requests are supported by USB-HID but are not available
+(or deprecated) in most other transport level specifications:
+
+ - GET/SET_IDLE: Only used by USB-HID and I2C-HID.
+ - GET/SET_PROTOCOL: Not used by HID core.
+ - RESET: Used by I2C-HID, not hooked up in HID core.
+ - SET_POWER: Used by I2C-HID, not hooked up in HID core.
+
+2) HID API
+==========
+
+2.1) Initialization
+-------------------
+
+Transport drivers normally use the following procedure to register a new device
+with HID core:
+
+ struct hid_device *hid;
+ int ret;
+
+ hid = hid_allocate_device();
+ if (IS_ERR(hid)) {
+ ret = PTR_ERR(hid);
+ goto err_<...>;
+ }
+
+ strlcpy(hid->name, <device-name-src>, 127);
+ strlcpy(hid->phys, <device-phys-src>, 63);
+ strlcpy(hid->uniq, <device-uniq-src>, 63);
+
+ hid->ll_driver = &custom_ll_driver;
+ hid->bus = <device-bus>;
+ hid->vendor = <device-vendor>;
+ hid->product = <device-product>;
+ hid->version = <device-version>;
+ hid->country = <device-country>;
+ hid->dev.parent = <pointer-to-parent-device>;
+ hid->driver_data = <transport-driver-data-field>;
+
+ ret = hid_add_device(hid);
+ if (ret)
+ goto err_<...>;
+
+Once hid_add_device() is entered, HID core might use the callbacks provided in
+"custom_ll_driver". Note that fields like "country" can be ignored by underlying
+transport-drivers if not supported.
+
+To unregister a device, use:
+
+ hid_destroy_device(hid);
+
+Once hid_destroy_device() returns, HID core will no longer make use of any
+driver callbacks.
+
+2.2) hid_ll_driver operations
+-----------------------------
+
+The available HID callbacks are:
+ - int (*start) (struct hid_device *hdev)
+ Called from HID device drivers once they want to use the device. Transport
+ drivers can choose to setup their device in this callback. However, normally
+ devices are already set up before transport drivers register them to HID core
+ so this is mostly only used by USB-HID.
+
+ - void (*stop) (struct hid_device *hdev)
+ Called from HID device drivers once they are done with a device. Transport
+ drivers can free any buffers and deinitialize the device. But note that
+ ->start() might be called again if another HID device driver is loaded on the
+ device.
+ Transport drivers are free to ignore it and deinitialize devices after they
+ destroyed them via hid_destroy_device().
+
+ - int (*open) (struct hid_device *hdev)
+ Called from HID device drivers once they are interested in data reports.
+ Usually, while user-space didn't open any input API/etc., device drivers are
+ not interested in device data and transport drivers can put devices asleep.
+ However, once ->open() is called, transport drivers must be ready for I/O.
+ ->open() calls are nested for each client that opens the HID device.
+
+ - void (*close) (struct hid_device *hdev)
+ Called from HID device drivers after ->open() was called but they are no
+ longer interested in device reports. (Usually if user-space closed any input
+ devices of the driver).
+ Transport drivers can put devices asleep and terminate any I/O of all
+ ->open() calls have been followed by a ->close() call. However, ->start() may
+ be called again if the device driver is interested in input reports again.
+
+ - int (*parse) (struct hid_device *hdev)
+ Called once during device setup after ->start() has been called. Transport
+ drivers must read the HID report-descriptor from the device and tell HID core
+ about it via hid_parse_report().
+
+ - int (*power) (struct hid_device *hdev, int level)
+ Called by HID core to give PM hints to transport drivers. Usually this is
+ analogical to the ->open() and ->close() hints and redundant.
+
+ - void (*request) (struct hid_device *hdev, struct hid_report *report,
+ int reqtype)
+ Send an HID request on the ctrl channel. "report" contains the report that
+ should be sent and "reqtype" the request type. Request-type can be
+ HID_REQ_SET_REPORT or HID_REQ_GET_REPORT.
+ This callback is optional. If not provided, HID core will assemble a raw
+ report following the HID specs and send it via the ->raw_request() callback.
+ The transport driver is free to implement this asynchronously.
+
+ - int (*wait) (struct hid_device *hdev)
+ Used by HID core before calling ->request() again. A transport driver can use
+ it to wait for any pending requests to complete if only one request is
+ allowed at a time.
+
+ - int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
+ __u8 *buf, size_t count, unsigned char rtype,
+ int reqtype)
+ Same as ->request() but provides the report as raw buffer. This request shall
+ be synchronous. A transport driver must not use ->wait() to complete such
+ requests. This request is mandatory and hid core will reject the device if
+ it is missing.
+
+ - int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len)
+ Send raw output report via intr channel. Used by some HID device drivers
+ which require high throughput for outgoing requests on the intr channel. This
+ must not cause SET_REPORT calls! This must be implemented as asynchronous
+ output report on the intr channel!
+
+ - int (*idle) (struct hid_device *hdev, int report, int idle, int reqtype)
+ Perform SET/GET_IDLE request. Only used by USB-HID, do not implement!
+
+2.3) Data Path
+--------------
+
+Transport drivers are responsible of reading data from I/O devices. They must
+handle any I/O-related state-tracking themselves. HID core does not implement
+protocol handshakes or other management commands which can be required by the
+given HID transport specification.
+
+Every raw data packet read from a device must be fed into HID core via
+hid_input_report(). You must specify the channel-type (intr or ctrl) and report
+type (input/output/feature). Under normal conditions, only input reports are
+provided via this API.
+
+Responses to GET_REPORT requests via ->request() must also be provided via this
+API. Responses to ->raw_request() are synchronous and must be intercepted by the
+transport driver and not passed to hid_input_report().
+Acknowledgements to SET_REPORT requests are not of interest to HID core.
+
+----------------------------------------------------
+Written 2013, David Herrmann <dh.herrmann@gmail.com>
diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt
index dc35a2b75eee..ee6593608c8e 100644
--- a/Documentation/hid/uhid.txt
+++ b/Documentation/hid/uhid.txt
@@ -93,6 +93,11 @@ the request was handled successfully.
event to the kernel. The payload is of type struct uhid_create_req and
contains information about your device. You can start I/O now.
+ UHID_CREATE2:
+ Same as UHID_CREATE, but the HID report descriptor data (rd_data) is an array
+ inside struct uhid_create2_req, instead of a pointer to a separate array.
+ Enables use from languages that don't support pointers, e.g. Python.
+
UHID_DESTROY:
This destroys the internal HID device. No further I/O will be accepted. There
may still be pending messages that you can receive with read() but no further
@@ -105,6 +110,12 @@ the request was handled successfully.
contains a data-payload. This is the raw data that you read from your device.
The kernel will parse the HID reports and react on it.
+ UHID_INPUT2:
+ Same as UHID_INPUT, but the data array is the last field of uhid_input2_req.
+ Enables userspace to write only the required bytes to kernel (ev.type +
+ ev.u.input2.size + the part of the data array that matters), instead of
+ the entire struct uhid_input2_req.
+
UHID_FEATURE_ANSWER:
If you receive a UHID_FEATURE request you must answer with this request. You
must copy the "id" field from the request into the answer. Set the "err" field
diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87
index 0c1635082c99..fe80e9adebfa 100644
--- a/Documentation/hwmon/it87
+++ b/Documentation/hwmon/it87
@@ -2,7 +2,7 @@ Kernel driver it87
==================
Supported chips:
- * IT8603E
+ * IT8603E/IT8623E
Prefix: 'it8603'
Addresses scanned: from Super I/O config space (8 I/O ports)
Datasheet: Not publicly available
@@ -94,9 +94,9 @@ motherboard models.
Description
-----------
-This driver implements support for the IT8603E, IT8705F, IT8712F, IT8716F,
-IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, IT8771E, IT8772E,
-IT8782F, IT8783E/F, and SiS950 chips.
+This driver implements support for the IT8603E, IT8623E, IT8705F, IT8712F,
+IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, IT8771E,
+IT8772E, IT8782F, IT8783E/F, and SiS950 chips.
These chips are 'Super I/O chips', supporting floppy disks, infrared ports,
joysticks and other miscellaneous stuff. For hardware monitoring, they
@@ -133,7 +133,7 @@ to userspace applications.
The IT8728F, IT8771E, and IT8772E are considered compatible with the IT8721F,
until a datasheet becomes available (hopefully.)
-The IT8603E is a custom design, hardware monitoring part is similar to
+The IT8603E/IT8623E is a custom design, hardware monitoring part is similar to
IT8728F. It only supports 16-bit fan mode, the full speed mode of the
fan is not supported (value 0 of pwmX_enable).
diff --git a/Documentation/hwmon/k10temp b/Documentation/hwmon/k10temp
index 4dfdc8f83633..ee6d30ec1522 100644
--- a/Documentation/hwmon/k10temp
+++ b/Documentation/hwmon/k10temp
@@ -11,8 +11,8 @@ Supported chips:
Socket S1G2: Athlon (X2), Sempron (X2), Turion X2 (Ultra)
* AMD Family 12h processors: "Llano" (E2/A4/A6/A8-Series)
* AMD Family 14h processors: "Brazos" (C/E/G/Z-Series)
-* AMD Family 15h processors: "Bulldozer" (FX-Series), "Trinity"
-* AMD Family 16h processors: "Kabini"
+* AMD Family 15h processors: "Bulldozer" (FX-Series), "Trinity", "Kaveri"
+* AMD Family 16h processors: "Kabini", "Mullins"
Prefix: 'k10temp'
Addresses scanned: PCI space
@@ -46,7 +46,7 @@ Description
-----------
This driver permits reading of the internal temperature sensor of AMD
-Family 10h/11h/12h/14h/15h processors.
+Family 10h/11h/12h/14h/15h/16h processors.
All these processors have a sensor, but on those for Socket F or AM2+,
the sensor may return inconsistent values (erratum 319). The driver
diff --git a/Documentation/input/multi-touch-protocol.txt b/Documentation/input/multi-touch-protocol.txt
index de139b18184a..7b4f59c09ee2 100644
--- a/Documentation/input/multi-touch-protocol.txt
+++ b/Documentation/input/multi-touch-protocol.txt
@@ -372,7 +372,7 @@ simple scheme, which is compatible with earlier usage, is:
Rationale: We have no information about the orientation of the touching
ellipse, so approximate it with an inscribed circle instead. The tool
-ellipse should align with the the vector (T - C), so the diameter must
+ellipse should align with the vector (T - C), so the diameter must
increase with distance(T, C). Finally, assume that the touch diameter is
equal to the tool thickness, and we arrive at the formulas above.
diff --git a/Documentation/ja_JP/SubmittingPatches b/Documentation/ja_JP/SubmittingPatches
index 97f78dd0c085..5d6ae639bfa0 100644
--- a/Documentation/ja_JP/SubmittingPatches
+++ b/Documentation/ja_JP/SubmittingPatches
@@ -98,11 +98,6 @@ dontdiff ファイルã«ã¯ Linux カーãƒãƒ«ã®ãƒ“ルドプロセスã®éŽç¨‹ã
Quilt:
http://savannah.nongnu.org/projects/quilt
-Andrew Morton's patch scripts:
-http://userweb.kernel.org/~akpm/stuff/patch-scripts.tar.gz
-ã“ã®ãƒªãƒ³ã‚¯ã®å…ˆã®ã‚¹ã‚¯ãƒªãƒ—トã®ä»£ã‚ã‚Šã¨ã—ã¦ã€quilt ãŒãƒ‘ッãƒãƒžãƒã‚¸ãƒ¡ãƒ³ãƒˆ
-ツールã¨ã—ã¦æŽ¨å¥¨ã•ã‚Œã¦ã„ã¾ã™(上ã®ãƒªãƒ³ã‚¯ã‚’見ã¦ãã ã•ã„)。
-
2) パッãƒã«å¯¾ã™ã‚‹èª¬æ˜Ž
パッãƒã®ä¸­ã®å¤‰æ›´ç‚¹ã«å¯¾ã™ã‚‹æŠ€è¡“çš„ãªè©³ç´°ã«ã¤ã„ã¦èª¬æ˜Žã—ã¦ãã ã•ã„。
@@ -695,7 +690,7 @@ gcc ã«ãŠã„ã¦ã¯ã€ãƒžã‚¯ãƒ­ã¨åŒã˜ãらã„軽ã„ã§ã™ã€‚
----------------------
Andrew Morton, "The perfect patch" (tpp).
- <http://userweb.kernel.org/~akpm/stuff/tpp.txt>
+ <http://www.ozlabs.org/~akpm/stuff/tpp.txt>
Jeff Garzik, "Linux kernel patch submission format".
<http://linux.yyz.us/patch-format.html>
@@ -707,7 +702,7 @@ Greg Kroah-Hartman, "How to piss off a kernel subsystem maintainer".
<http://www.kroah.com/log/2006/01/11/>
NO!!!! No more huge patch bombs to linux-kernel@vger.kernel.org people!
- <http://marc.theaimsgroup.com/?l=linux-kernel&m=112112749912944&w=2>
+ <https://lkml.org/lkml/2005/7/11/336>
Kernel Documentation/CodingStyle:
<http://users.sosdg.org/~qiyong/lxr/source/Documentation/CodingStyle>
diff --git a/Documentation/kmemcheck.txt b/Documentation/kmemcheck.txt
index 9398a501fdb9..a41bdebbe87b 100644
--- a/Documentation/kmemcheck.txt
+++ b/Documentation/kmemcheck.txt
@@ -541,7 +541,7 @@ initialized. This is the beginning of the struct:
92 } _sifields;
93 } siginfo_t;
-On 64-bit, the int is 4 bytes long, so it must the the union member that has
+On 64-bit, the int is 4 bytes long, so it must the union member that has
not been initialized. We can verify this using gdb:
$ gdb vmlinux
diff --git a/Documentation/kmemleak.txt b/Documentation/kmemleak.txt
index b6e39739a36d..a7563ec4ea7b 100644
--- a/Documentation/kmemleak.txt
+++ b/Documentation/kmemleak.txt
@@ -11,9 +11,7 @@ with the difference that the orphan objects are not freed but only
reported via /sys/kernel/debug/kmemleak. A similar method is used by the
Valgrind tool (memcheck --leak-check) to detect the memory leaks in
user-space applications.
-
-Please check DEBUG_KMEMLEAK dependencies in lib/Kconfig.debug for supported
-architectures.
+Kmemleak is supported on x86, arm, powerpc, sparc, sh, microblaze, ppc, mips, s390, metag and tile.
Usage
-----
@@ -53,7 +51,8 @@ Memory scanning parameters can be modified at run-time by writing to the
(default 600, 0 to stop the automatic scanning)
scan - trigger a memory scan
clear - clear list of current memory leak suspects, done by
- marking all current reported unreferenced objects grey
+ marking all current reported unreferenced objects grey,
+ or free all kmemleak objects if kmemleak has been disabled.
dump=<addr> - dump information about the object found at <addr>
Kmemleak can also be disabled at boot-time by passing "kmemleak=off" on
@@ -68,7 +67,7 @@ Basic Algorithm
The memory allocations via kmalloc, vmalloc, kmem_cache_alloc and
friends are traced and the pointers, together with additional
-information like size and stack trace, are stored in a prio search tree.
+information like size and stack trace, are stored in a rbtree.
The corresponding freeing function calls are tracked and the pointers
removed from the kmemleak data structures.
@@ -84,7 +83,7 @@ The scanning algorithm steps:
1. mark all objects as white (remaining white objects will later be
considered orphan)
2. scan the memory starting with the data section and stacks, checking
- the values against the addresses stored in the prio search tree. If
+ the values against the addresses stored in the rbtree. If
a pointer to a white object is found, the object is added to the
gray list
3. scan the gray objects for matching addresses (some white objects
@@ -120,6 +119,18 @@ Then as usual to get your report with:
# cat /sys/kernel/debug/kmemleak
+Freeing kmemleak internal objects
+---------------------------------
+
+To allow access to previosuly found memory leaks after kmemleak has been
+disabled by the user or due to an fatal error, internal kmemleak objects
+won't be freed when kmemleak is disabled, and those objects may occupy
+a large part of physical memory.
+
+In this situation, you may reclaim memory with:
+
+ # echo clear > /sys/kernel/debug/kmemleak
+
Kmemleak API
------------
diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt
index 11c1d2049662..556f951f8626 100644
--- a/Documentation/memory-barriers.txt
+++ b/Documentation/memory-barriers.txt
@@ -1401,7 +1401,7 @@ code. Here are some examples of these sorts of optimizations:
process_message(msg);
}
- There is nothing to prevent the the compiler from transforming
+ There is nothing to prevent the compiler from transforming
process_level() to the following, in fact, this might well be a
win for single-threaded code:
diff --git a/Documentation/module-signing.txt b/Documentation/module-signing.txt
index 2b40e04d3c49..09c2382ad055 100644
--- a/Documentation/module-signing.txt
+++ b/Documentation/module-signing.txt
@@ -53,7 +53,8 @@ This has a number of options available:
If this is off (ie. "permissive"), then modules for which the key is not
available and modules that are unsigned are permitted, but the kernel will
- be marked as being tainted.
+ be marked as being tainted, and the concerned modules will be marked as
+ tainted, shown with the character 'E'.
If this is on (ie. "restrictive"), only modules that have a valid
signature that can be verified by a public key in the kernel's possession
@@ -77,11 +78,11 @@ This has a number of options available:
This presents a choice of which hash algorithm the installation phase will
sign the modules with:
- CONFIG_SIG_SHA1 "Sign modules with SHA-1"
- CONFIG_SIG_SHA224 "Sign modules with SHA-224"
- CONFIG_SIG_SHA256 "Sign modules with SHA-256"
- CONFIG_SIG_SHA384 "Sign modules with SHA-384"
- CONFIG_SIG_SHA512 "Sign modules with SHA-512"
+ CONFIG_MODULE_SIG_SHA1 "Sign modules with SHA-1"
+ CONFIG_MODULE_SIG_SHA224 "Sign modules with SHA-224"
+ CONFIG_MODULE_SIG_SHA256 "Sign modules with SHA-256"
+ CONFIG_MODULE_SIG_SHA384 "Sign modules with SHA-384"
+ CONFIG_MODULE_SIG_SHA512 "Sign modules with SHA-512"
The algorithm selected here will also be built into the kernel (rather
than being a module) so that modules signed with that algorithm can have
diff --git a/Documentation/networking/altera_tse.txt b/Documentation/networking/altera_tse.txt
new file mode 100644
index 000000000000..3f24df8c6e65
--- /dev/null
+++ b/Documentation/networking/altera_tse.txt
@@ -0,0 +1,263 @@
+ Altera Triple-Speed Ethernet MAC driver
+
+Copyright (C) 2008-2014 Altera Corporation
+
+This is the driver for the Altera Triple-Speed Ethernet (TSE) controllers
+using the SGDMA and MSGDMA soft DMA IP components. The driver uses the
+platform bus to obtain component resources. The designs used to test this
+driver were built for a Cyclone(R) V SOC FPGA board, a Cyclone(R) V FPGA board,
+and tested with ARM and NIOS processor hosts seperately. The anticipated use
+cases are simple communications between an embedded system and an external peer
+for status and simple configuration of the embedded system.
+
+For more information visit www.altera.com and www.rocketboards.org. Support
+forums for the driver may be found on www.rocketboards.org, and a design used
+to test this driver may be found there as well. Support is also available from
+the maintainer of this driver, found in MAINTAINERS.
+
+The Triple-Speed Ethernet, SGDMA, and MSGDMA components are all soft IP
+components that can be assembled and built into an FPGA using the Altera
+Quartus toolchain. Quartus 13.1 and 14.0 were used to build the design that
+this driver was tested against. The sopc2dts tool is used to create the
+device tree for the driver, and may be found at rocketboards.org.
+
+The driver probe function examines the device tree and determines if the
+Triple-Speed Ethernet instance is using an SGDMA or MSGDMA component. The
+probe function then installs the appropriate set of DMA routines to
+initialize, setup transmits, receives, and interrupt handling primitives for
+the respective configurations.
+
+The SGDMA component is to be deprecated in the near future (over the next 1-2
+years as of this writing in early 2014) in favor of the MSGDMA component.
+SGDMA support is included for existing designs and reference in case a
+developer wishes to support their own soft DMA logic and driver support. Any
+new designs should not use the SGDMA.
+
+The SGDMA supports only a single transmit or receive operation at a time, and
+therefore will not perform as well compared to the MSGDMA soft IP. Please
+visit www.altera.com for known, documented SGDMA errata.
+
+Scatter-gather DMA is not supported by the SGDMA or MSGDMA at this time.
+Scatter-gather DMA will be added to a future maintenance update to this
+driver.
+
+Jumbo frames are not supported at this time.
+
+The driver limits PHY operations to 10/100Mbps, and has not yet been fully
+tested for 1Gbps. This support will be added in a future maintenance update.
+
+1) Kernel Configuration
+The kernel configuration option is ALTERA_TSE:
+ Device Drivers ---> Network device support ---> Ethernet driver support --->
+ Altera Triple-Speed Ethernet MAC support (ALTERA_TSE)
+
+2) Driver parameters list:
+ debug: message level (0: no output, 16: all);
+ dma_rx_num: Number of descriptors in the RX list (default is 64);
+ dma_tx_num: Number of descriptors in the TX list (default is 64).
+
+3) Command line options
+Driver parameters can be also passed in command line by using:
+ altera_tse=dma_rx_num:128,dma_tx_num:512
+
+4) Driver information and notes
+
+4.1) Transmit process
+When the driver's transmit routine is called by the kernel, it sets up a
+transmit descriptor by calling the underlying DMA transmit routine (SGDMA or
+MSGDMA), and initites a transmit operation. Once the transmit is complete, an
+interrupt is driven by the transmit DMA logic. The driver handles the transmit
+completion in the context of the interrupt handling chain by recycling
+resource required to send and track the requested transmit operation.
+
+4.2) Receive process
+The driver will post receive buffers to the receive DMA logic during driver
+intialization. Receive buffers may or may not be queued depending upon the
+underlying DMA logic (MSGDMA is able queue receive buffers, SGDMA is not able
+to queue receive buffers to the SGDMA receive logic). When a packet is
+received, the DMA logic generates an interrupt. The driver handles a receive
+interrupt by obtaining the DMA receive logic status, reaping receive
+completions until no more receive completions are available.
+
+4.3) Interrupt Mitigation
+The driver is able to mitigate the number of its DMA interrupts
+using NAPI for receive operations. Interrupt mitigation is not yet supported
+for transmit operations, but will be added in a future maintenance release.
+
+4.4) Ethtool support
+Ethtool is supported. Driver statistics and internal errors can be taken using:
+ethtool -S ethX command. It is possible to dump registers etc.
+
+4.5) PHY Support
+The driver is compatible with PAL to work with PHY and GPHY devices.
+
+4.7) List of source files:
+ o Kconfig
+ o Makefile
+ o altera_tse_main.c: main network device driver
+ o altera_tse_ethtool.c: ethtool support
+ o altera_tse.h: private driver structure and common definitions
+ o altera_msgdma.h: MSGDMA implementation function definitions
+ o altera_sgdma.h: SGDMA implementation function definitions
+ o altera_msgdma.c: MSGDMA implementation
+ o altera_sgdma.c: SGDMA implementation
+ o altera_sgdmahw.h: SGDMA register and descriptor definitions
+ o altera_msgdmahw.h: MSGDMA register and descriptor definitions
+ o altera_utils.c: Driver utility functions
+ o altera_utils.h: Driver utility function definitions
+
+5) Debug Information
+
+The driver exports debug information such as internal statistics,
+debug information, MAC and DMA registers etc.
+
+A user may use the ethtool support to get statistics:
+e.g. using: ethtool -S ethX (that shows the statistics counters)
+or sees the MAC registers: e.g. using: ethtool -d ethX
+
+The developer can also use the "debug" module parameter to get
+further debug information.
+
+6) Statistics Support
+
+The controller and driver support a mix of IEEE standard defined statistics,
+RFC defined statistics, and driver or Altera defined statistics. The four
+specifications containing the standard definitions for these statistics are
+as follows:
+
+ o IEEE 802.3-2012 - IEEE Standard for Ethernet.
+ o RFC 2863 found at http://www.rfc-editor.org/rfc/rfc2863.txt.
+ o RFC 2819 found at http://www.rfc-editor.org/rfc/rfc2819.txt.
+ o Altera Triple Speed Ethernet User Guide, found at http://www.altera.com
+
+The statistics supported by the TSE and the device driver are as follows:
+
+"tx_packets" is equivalent to aFramesTransmittedOK defined in IEEE 802.3-2012,
+Section 5.2.2.1.2. This statistics is the count of frames that are successfully
+transmitted.
+
+"rx_packets" is equivalent to aFramesReceivedOK defined in IEEE 802.3-2012,
+Section 5.2.2.1.5. This statistic is the count of frames that are successfully
+received. This count does not include any error packets such as CRC errors,
+length errors, or alignment errors.
+
+"rx_crc_errors" is equivalent to aFrameCheckSequenceErrors defined in IEEE
+802.3-2012, Section 5.2.2.1.6. This statistic is the count of frames that are
+an integral number of bytes in length and do not pass the CRC test as the frame
+is received.
+
+"rx_align_errors" is equivalent to aAlignmentErrors defined in IEEE 802.3-2012,
+Section 5.2.2.1.7. This statistic is the count of frames that are not an
+integral number of bytes in length and do not pass the CRC test as the frame is
+received.
+
+"tx_bytes" is equivalent to aOctetsTransmittedOK defined in IEEE 802.3-2012,
+Section 5.2.2.1.8. This statistic is the count of data and pad bytes
+successfully transmitted from the interface.
+
+"rx_bytes" is equivalent to aOctetsReceivedOK defined in IEEE 802.3-2012,
+Section 5.2.2.1.14. This statistic is the count of data and pad bytes
+successfully received by the controller.
+
+"tx_pause" is equivalent to aPAUSEMACCtrlFramesTransmitted defined in IEEE
+802.3-2012, Section 30.3.4.2. This statistic is a count of PAUSE frames
+transmitted from the network controller.
+
+"rx_pause" is equivalent to aPAUSEMACCtrlFramesReceived defined in IEEE
+802.3-2012, Section 30.3.4.3. This statistic is a count of PAUSE frames
+received by the network controller.
+
+"rx_errors" is equivalent to ifInErrors defined in RFC 2863. This statistic is
+a count of the number of packets received containing errors that prevented the
+packet from being delivered to a higher level protocol.
+
+"tx_errors" is equivalent to ifOutErrors defined in RFC 2863. This statistic
+is a count of the number of packets that could not be transmitted due to errors.
+
+"rx_unicast" is equivalent to ifInUcastPkts defined in RFC 2863. This
+statistic is a count of the number of packets received that were not addressed
+to the broadcast address or a multicast group.
+
+"rx_multicast" is equivalent to ifInMulticastPkts defined in RFC 2863. This
+statistic is a count of the number of packets received that were addressed to
+a multicast address group.
+
+"rx_broadcast" is equivalent to ifInBroadcastPkts defined in RFC 2863. This
+statistic is a count of the number of packets received that were addressed to
+the broadcast address.
+
+"tx_discards" is equivalent to ifOutDiscards defined in RFC 2863. This
+statistic is the number of outbound packets not transmitted even though an
+error was not detected. An example of a reason this might occur is to free up
+internal buffer space.
+
+"tx_unicast" is equivalent to ifOutUcastPkts defined in RFC 2863. This
+statistic counts the number of packets transmitted that were not addressed to
+a multicast group or broadcast address.
+
+"tx_multicast" is equivalent to ifOutMulticastPkts defined in RFC 2863. This
+statistic counts the number of packets transmitted that were addressed to a
+multicast group.
+
+"tx_broadcast" is equivalent to ifOutBroadcastPkts defined in RFC 2863. This
+statistic counts the number of packets transmitted that were addressed to a
+broadcast address.
+
+"ether_drops" is equivalent to etherStatsDropEvents defined in RFC 2819.
+This statistic counts the number of packets dropped due to lack of internal
+controller resources.
+
+"rx_total_bytes" is equivalent to etherStatsOctets defined in RFC 2819.
+This statistic counts the total number of bytes received by the controller,
+including error and discarded packets.
+
+"rx_total_packets" is equivalent to etherStatsPkts defined in RFC 2819.
+This statistic counts the total number of packets received by the controller,
+including error, discarded, unicast, multicast, and broadcast packets.
+
+"rx_undersize" is equivalent to etherStatsUndersizePkts defined in RFC 2819.
+This statistic counts the number of correctly formed packets received less
+than 64 bytes long.
+
+"rx_oversize" is equivalent to etherStatsOversizePkts defined in RFC 2819.
+This statistic counts the number of correctly formed packets greater than 1518
+bytes long.
+
+"rx_64_bytes" is equivalent to etherStatsPkts64Octets defined in RFC 2819.
+This statistic counts the total number of packets received that were 64 octets
+in length.
+
+"rx_65_127_bytes" is equivalent to etherStatsPkts65to127Octets defined in RFC
+2819. This statistic counts the total number of packets received that were
+between 65 and 127 octets in length inclusive.
+
+"rx_128_255_bytes" is equivalent to etherStatsPkts128to255Octets defined in
+RFC 2819. This statistic is the total number of packets received that were
+between 128 and 255 octets in length inclusive.
+
+"rx_256_511_bytes" is equivalent to etherStatsPkts256to511Octets defined in
+RFC 2819. This statistic is the total number of packets received that were
+between 256 and 511 octets in length inclusive.
+
+"rx_512_1023_bytes" is equivalent to etherStatsPkts512to1023Octets defined in
+RFC 2819. This statistic is the total number of packets received that were
+between 512 and 1023 octets in length inclusive.
+
+"rx_1024_1518_bytes" is equivalent to etherStatsPkts1024to1518Octets define
+in RFC 2819. This statistic is the total number of packets received that were
+between 1024 and 1518 octets in length inclusive.
+
+"rx_gte_1519_bytes" is a statistic defined specific to the behavior of the
+Altera TSE. This statistics counts the number of received good and errored
+frames between the length of 1519 and the maximum frame length configured
+in the frm_length register. See the Altera TSE User Guide for More details.
+
+"rx_jabbers" is equivalent to etherStatsJabbers defined in RFC 2819. This
+statistic is the total number of packets received that were longer than 1518
+octets, and had either a bad CRC with an integral number of octets (CRC Error)
+or a bad CRC with a non-integral number of octets (Alignment Error).
+
+"rx_runts" is equivalent to etherStatsFragments defined in RFC 2819. This
+statistic is the total number of packets received that were less than 64 octets
+in length and had either a bad CRC with an integral number of octets (CRC
+error) or a bad CRC with a non-integral number of octets (Alignment Error).
diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt
index 5cdb22971d19..a383c00392d0 100644
--- a/Documentation/networking/bonding.txt
+++ b/Documentation/networking/bonding.txt
@@ -270,16 +270,15 @@ arp_ip_target
arp_validate
Specifies whether or not ARP probes and replies should be
- validated in the active-backup mode. This causes the ARP
- monitor to examine the incoming ARP requests and replies, and
- only consider a slave to be up if it is receiving the
- appropriate ARP traffic.
+ validated in any mode that supports arp monitoring, or whether
+ non-ARP traffic should be filtered (disregarded) for link
+ monitoring purposes.
Possible values are:
none or 0
- No validation is performed. This is the default.
+ No validation or filtering is performed.
active or 1
@@ -293,31 +292,68 @@ arp_validate
Validation is performed for all slaves.
- For the active slave, the validation checks ARP replies to
- confirm that they were generated by an arp_ip_target. Since
- backup slaves do not typically receive these replies, the
- validation performed for backup slaves is on the ARP request
- sent out via the active slave. It is possible that some
- switch or network configurations may result in situations
- wherein the backup slaves do not receive the ARP requests; in
- such a situation, validation of backup slaves must be
- disabled.
-
- The validation of ARP requests on backup slaves is mainly
- helping bonding to decide which slaves are more likely to
- work in case of the active slave failure, it doesn't really
- guarantee that the backup slave will work if it's selected
- as the next active slave.
-
- This option is useful in network configurations in which
- multiple bonding hosts are concurrently issuing ARPs to one or
- more targets beyond a common switch. Should the link between
- the switch and target fail (but not the switch itself), the
- probe traffic generated by the multiple bonding instances will
- fool the standard ARP monitor into considering the links as
- still up. Use of the arp_validate option can resolve this, as
- the ARP monitor will only consider ARP requests and replies
- associated with its own instance of bonding.
+ filter or 4
+
+ Filtering is applied to all slaves. No validation is
+ performed.
+
+ filter_active or 5
+
+ Filtering is applied to all slaves, validation is performed
+ only for the active slave.
+
+ filter_backup or 6
+
+ Filtering is applied to all slaves, validation is performed
+ only for backup slaves.
+
+ Validation:
+
+ Enabling validation causes the ARP monitor to examine the incoming
+ ARP requests and replies, and only consider a slave to be up if it
+ is receiving the appropriate ARP traffic.
+
+ For an active slave, the validation checks ARP replies to confirm
+ that they were generated by an arp_ip_target. Since backup slaves
+ do not typically receive these replies, the validation performed
+ for backup slaves is on the broadcast ARP request sent out via the
+ active slave. It is possible that some switch or network
+ configurations may result in situations wherein the backup slaves
+ do not receive the ARP requests; in such a situation, validation
+ of backup slaves must be disabled.
+
+ The validation of ARP requests on backup slaves is mainly helping
+ bonding to decide which slaves are more likely to work in case of
+ the active slave failure, it doesn't really guarantee that the
+ backup slave will work if it's selected as the next active slave.
+
+ Validation is useful in network configurations in which multiple
+ bonding hosts are concurrently issuing ARPs to one or more targets
+ beyond a common switch. Should the link between the switch and
+ target fail (but not the switch itself), the probe traffic
+ generated by the multiple bonding instances will fool the standard
+ ARP monitor into considering the links as still up. Use of
+ validation can resolve this, as the ARP monitor will only consider
+ ARP requests and replies associated with its own instance of
+ bonding.
+
+ Filtering:
+
+ Enabling filtering causes the ARP monitor to only use incoming ARP
+ packets for link availability purposes. Arriving packets that are
+ not ARPs are delivered normally, but do not count when determining
+ if a slave is available.
+
+ Filtering operates by only considering the reception of ARP
+ packets (any ARP packet, regardless of source or destination) when
+ determining if a slave has received traffic for link availability
+ purposes.
+
+ Filtering is useful in network configurations in which significant
+ levels of third party broadcast traffic would fool the standard
+ ARP monitor into considering the links as still up. Use of
+ filtering can resolve this, as only ARP traffic is considered for
+ link availability purposes.
This option was added in bonding version 3.1.0.
diff --git a/Documentation/networking/can.txt b/Documentation/networking/can.txt
index 0cbe6ec22d6f..2fa44cbe81b7 100644
--- a/Documentation/networking/can.txt
+++ b/Documentation/networking/can.txt
@@ -1017,7 +1017,7 @@ solution for a couple of reasons:
in case of a bus-off condition after the specified delay time
in milliseconds. By default it's off.
- "bitrate 125000 sample_point 0.875"
+ "bitrate 125000 sample-point 0.875"
Shows the real bit-rate in bits/sec and the sample-point in the
range 0.000..0.999. If the calculation of bit-timing parameters
is enabled in the kernel (CONFIG_CAN_CALC_BITTIMING=y), the
diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt
index a06b48d2f5cc..81f940f4e884 100644
--- a/Documentation/networking/filter.txt
+++ b/Documentation/networking/filter.txt
@@ -546,6 +546,130 @@ ffffffffa0069c8f + <x>:
For BPF JIT developers, bpf_jit_disasm, bpf_asm and bpf_dbg provides a useful
toolchain for developing and testing the kernel's JIT compiler.
+BPF kernel internals
+--------------------
+Internally, for the kernel interpreter, a different BPF instruction set
+format with similar underlying principles from BPF described in previous
+paragraphs is being used. However, the instruction set format is modelled
+closer to the underlying architecture to mimic native instruction sets, so
+that a better performance can be achieved (more details later).
+
+It is designed to be JITed with one to one mapping, which can also open up
+the possibility for GCC/LLVM compilers to generate optimized BPF code through
+a BPF backend that performs almost as fast as natively compiled code.
+
+The new instruction set was originally designed with the possible goal in
+mind to write programs in "restricted C" and compile into BPF with a optional
+GCC/LLVM backend, so that it can just-in-time map to modern 64-bit CPUs with
+minimal performance overhead over two steps, that is, C -> BPF -> native code.
+
+Currently, the new format is being used for running user BPF programs, which
+includes seccomp BPF, classic socket filters, cls_bpf traffic classifier,
+team driver's classifier for its load-balancing mode, netfilter's xt_bpf
+extension, PTP dissector/classifier, and much more. They are all internally
+converted by the kernel into the new instruction set representation and run
+in the extended interpreter. For in-kernel handlers, this all works
+transparently by using sk_unattached_filter_create() for setting up the
+filter, resp. sk_unattached_filter_destroy() for destroying it. The macro
+SK_RUN_FILTER(filter, ctx) transparently invokes the right BPF function to
+run the filter. 'filter' is a pointer to struct sk_filter that we got from
+sk_unattached_filter_create(), and 'ctx' the given context (e.g. skb pointer).
+All constraints and restrictions from sk_chk_filter() apply before a
+conversion to the new layout is being done behind the scenes!
+
+Currently, for JITing, the user BPF format is being used and current BPF JIT
+compilers reused whenever possible. In other words, we do not (yet!) perform
+a JIT compilation in the new layout, however, future work will successively
+migrate traditional JIT compilers into the new instruction format as well, so
+that they will profit from the very same benefits. Thus, when speaking about
+JIT in the following, a JIT compiler (TBD) for the new instruction format is
+meant in this context.
+
+Some core changes of the new internal format:
+
+- Number of registers increase from 2 to 10:
+
+ The old format had two registers A and X, and a hidden frame pointer. The
+ new layout extends this to be 10 internal registers and a read-only frame
+ pointer. Since 64-bit CPUs are passing arguments to functions via registers
+ the number of args from BPF program to in-kernel function is restricted
+ to 5 and one register is used to accept return value from an in-kernel
+ function. Natively, x86_64 passes first 6 arguments in registers, aarch64/
+ sparcv9/mips64 have 7 - 8 registers for arguments; x86_64 has 6 callee saved
+ registers, and aarch64/sparcv9/mips64 have 11 or more callee saved registers.
+
+ Therefore, BPF calling convention is defined as:
+
+ * R0 - return value from in-kernel function
+ * R1 - R5 - arguments from BPF program to in-kernel function
+ * R6 - R9 - callee saved registers that in-kernel function will preserve
+ * R10 - read-only frame pointer to access stack
+
+ Thus, all BPF registers map one to one to HW registers on x86_64, aarch64,
+ etc, and BPF calling convention maps directly to ABIs used by the kernel on
+ 64-bit architectures.
+
+ On 32-bit architectures JIT may map programs that use only 32-bit arithmetic
+ and may let more complex programs to be interpreted.
+
+ R0 - R5 are scratch registers and BPF program needs spill/fill them if
+ necessary across calls. Note that there is only one BPF program (== one BPF
+ main routine) and it cannot call other BPF functions, it can only call
+ predefined in-kernel functions, though.
+
+- Register width increases from 32-bit to 64-bit:
+
+ Still, the semantics of the original 32-bit ALU operations are preserved
+ via 32-bit subregisters. All BPF registers are 64-bit with 32-bit lower
+ subregisters that zero-extend into 64-bit if they are being written to.
+ That behavior maps directly to x86_64 and arm64 subregister definition, but
+ makes other JITs more difficult.
+
+ 32-bit architectures run 64-bit internal BPF programs via interpreter.
+ Their JITs may convert BPF programs that only use 32-bit subregisters into
+ native instruction set and let the rest being interpreted.
+
+ Operation is 64-bit, because on 64-bit architectures, pointers are also
+ 64-bit wide, and we want to pass 64-bit values in/out of kernel functions,
+ so 32-bit BPF registers would otherwise require to define register-pair
+ ABI, thus, there won't be able to use a direct BPF register to HW register
+ mapping and JIT would need to do combine/split/move operations for every
+ register in and out of the function, which is complex, bug prone and slow.
+ Another reason is the use of atomic 64-bit counters.
+
+- Conditional jt/jf targets replaced with jt/fall-through:
+
+ While the original design has constructs such as "if (cond) jump_true;
+ else jump_false;", they are being replaced into alternative constructs like
+ "if (cond) jump_true; /* else fall-through */".
+
+- Introduces bpf_call insn and register passing convention for zero overhead
+ calls from/to other kernel functions:
+
+ After a kernel function call, R1 - R5 are reset to unreadable and R0 has a
+ return type of the function. Since R6 - R9 are callee saved, their state is
+ preserved across the call.
+
+Also in the new design, BPF is limited to 4096 insns, which means that any
+program will terminate quickly and will only call a fixed number of kernel
+functions. Original BPF and the new format are two operand instructions,
+which helps to do one-to-one mapping between BPF insn and x86 insn during JIT.
+
+The input context pointer for invoking the interpreter function is generic,
+its content is defined by a specific use case. For seccomp register R1 points
+to seccomp_data, for converted BPF filters R1 points to a skb.
+
+A program, that is translated internally consists of the following elements:
+
+ op:16, jt:8, jf:8, k:32 ==> op:8, a_reg:4, x_reg:4, off:16, imm:32
+
+Just like the original BPF, the new format runs within a controlled environment,
+is deterministic and the kernel can easily prove that. The safety of the program
+can be determined in two steps: first step does depth-first-search to disallow
+loops and other CFG validation; second step starts from the first insn and
+descends all possible paths. It simulates execution of every insn and observes
+the state change of registers and stack.
+
Misc
----
@@ -561,3 +685,4 @@ the underlying architecture.
Jay Schulist <jschlst@samba.org>
Daniel Borkmann <dborkman@redhat.com>
+Alexei Starovoitov <ast@plumgrid.com>
diff --git a/Documentation/networking/gianfar.txt b/Documentation/networking/gianfar.txt
index ad474ea07d07..ba1daea7f2e4 100644
--- a/Documentation/networking/gianfar.txt
+++ b/Documentation/networking/gianfar.txt
@@ -1,38 +1,8 @@
The Gianfar Ethernet Driver
-Sysfs File description
Author: Andy Fleming <afleming@freescale.com>
Updated: 2005-07-28
-SYSFS
-
-Several of the features of the gianfar driver are controlled
-through sysfs files. These are:
-
-bd_stash:
-To stash RX Buffer Descriptors in the L2, echo 'on' or '1' to
-bd_stash, echo 'off' or '0' to disable
-
-rx_stash_len:
-To stash the first n bytes of the packet in L2, echo the number
-of bytes to buf_stash_len. echo 0 to disable.
-
-WARNING: You could really screw these up if you set them too low or high!
-fifo_threshold:
-To change the number of bytes the controller needs in the
-fifo before it starts transmission, echo the number of bytes to
-fifo_thresh. Range should be 0-511.
-
-fifo_starve:
-When the FIFO has less than this many bytes during a transmit, it
-enters starve mode, and increases the priority of TX memory
-transactions. To change, echo the number of bytes to
-fifo_starve. Range should be 0-511.
-
-fifo_starve_off:
-Once in starve mode, the FIFO remains there until it has this
-many bytes. To change, echo the number of bytes to
-fifo_starve_off. Range should be 0-511.
CHECKSUM OFFLOADING
diff --git a/Documentation/networking/igb.txt b/Documentation/networking/igb.txt
index 4ebbd659256f..43d3549366a0 100644
--- a/Documentation/networking/igb.txt
+++ b/Documentation/networking/igb.txt
@@ -36,54 +36,6 @@ Default Value: 0
This parameter adds support for SR-IOV. It causes the driver to spawn up to
max_vfs worth of virtual function.
-QueuePairs
-----------
-Valid Range: 0-1
-Default Value: 1 (TX and RX will be paired onto one interrupt vector)
-
-If set to 0, when MSI-X is enabled, the TX and RX will attempt to occupy
-separate vectors.
-
-This option can be overridden to 1 if there are not sufficient interrupts
-available. This can occur if any combination of RSS, VMDQ, and max_vfs
-results in more than 4 queues being used.
-
-Node
-----
-Valid Range: 0-n
-Default Value: -1 (off)
-
- 0 - n: where n is the number of the NUMA node that should be used to
- allocate memory for this adapter port.
- -1: uses the driver default of allocating memory on whichever processor is
- running insmod/modprobe.
-
- The Node parameter will allow you to pick which NUMA node you want to have
- the adapter allocate memory from. All driver structures, in-memory queues,
- and receive buffers will be allocated on the node specified. This parameter
- is only useful when interrupt affinity is specified, otherwise some portion
- of the time the interrupt could run on a different core than the memory is
- allocated on, causing slower memory access and impacting throughput, CPU, or
- both.
-
-EEE
----
-Valid Range: 0-1
-Default Value: 1 (enabled)
-
- A link between two EEE-compliant devices will result in periodic bursts of
- data followed by long periods where in the link is in an idle state. This Low
- Power Idle (LPI) state is supported in both 1Gbps and 100Mbps link speeds.
- NOTE: EEE support requires autonegotiation.
-
-DMAC
-----
-Valid Range: 0-1
-Default Value: 1 (enabled)
- Enables or disables DMA Coalescing feature.
-
-
-
Additional Configurations
=========================
diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt
index ebf270719402..3544c98401fd 100644
--- a/Documentation/networking/phy.txt
+++ b/Documentation/networking/phy.txt
@@ -48,7 +48,7 @@ The MDIO bus
time, so it is safe for them to block, waiting for an interrupt to signal
the operation is complete
- 2) A reset function is necessary. This is used to return the bus to an
+ 2) A reset function is optional. This is used to return the bus to an
initialized state.
3) A probe function is needed. This function should set up anything the bus
@@ -253,16 +253,25 @@ Writing a PHY driver
Each driver consists of a number of function pointers:
+ soft_reset: perform a PHY software reset
config_init: configures PHY into a sane state after a reset.
For instance, a Davicom PHY requires descrambling disabled.
probe: Allocate phy->priv, optionally refuse to bind.
PHY may not have been reset or had fixups run yet.
suspend/resume: power management
config_aneg: Changes the speed/duplex/negotiation settings
+ aneg_done: Determines the auto-negotiation result
read_status: Reads the current speed/duplex/negotiation settings
ack_interrupt: Clear a pending interrupt
+ did_interrupt: Checks if the PHY generated an interrupt
config_intr: Enable or disable interrupts
remove: Does any driver take-down
+ ts_info: Queries about the HW timestamping status
+ hwtstamp: Set the PHY HW timestamping configuration
+ rxtstamp: Requests a receive timestamp at the PHY level for a 'skb'
+ txtsamp: Requests a transmit timestamp at the PHY level for a 'skb'
+ set_wol: Enable Wake-on-LAN at the PHY level
+ get_wol: Get the Wake-on-LAN status at the PHY level
Of these, only config_aneg and read_status are required to be
assigned by the driver code. The rest are optional. Also, it is
diff --git a/Documentation/networking/pktgen.txt b/Documentation/networking/pktgen.txt
index 5a61a240a652..0e30c7845b2b 100644
--- a/Documentation/networking/pktgen.txt
+++ b/Documentation/networking/pktgen.txt
@@ -102,13 +102,18 @@ Examples:
The 'minimum' MAC is what you set with dstmac.
pgset "flag [name]" Set a flag to determine behaviour. Current flags
- are: IPSRC_RND #IP Source is random (between min/max),
- IPDST_RND, UDPSRC_RND,
- UDPDST_RND, MACSRC_RND, MACDST_RND
+ are: IPSRC_RND # IP source is random (between min/max)
+ IPDST_RND # IP destination is random
+ UDPSRC_RND, UDPDST_RND,
+ MACSRC_RND, MACDST_RND
+ TXSIZE_RND, IPV6,
MPLS_RND, VID_RND, SVID_RND
+ FLOW_SEQ,
QUEUE_MAP_RND # queue map random
QUEUE_MAP_CPU # queue map mirrors smp_processor_id()
- IPSEC # Make IPsec encapsulation for packet
+ UDPCSUM,
+ IPSEC # IPsec encapsulation (needs CONFIG_XFRM)
+ NODE_ALLOC # node specific memory allocation
pgset spi SPI_VALUE Set specific SA used to transform packet.
@@ -233,13 +238,22 @@ udp_dst_max
flag
IPSRC_RND
- TXSIZE_RND
IPDST_RND
UDPSRC_RND
UDPDST_RND
MACSRC_RND
MACDST_RND
+ TXSIZE_RND
+ IPV6
+ MPLS_RND
+ VID_RND
+ SVID_RND
+ FLOW_SEQ
+ QUEUE_MAP_RND
+ QUEUE_MAP_CPU
+ UDPCSUM
IPSEC
+ NODE_ALLOC
dst_min
dst_max
diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt
index b89bc82eed46..16a924c486bf 100644
--- a/Documentation/networking/rxrpc.txt
+++ b/Documentation/networking/rxrpc.txt
@@ -27,6 +27,8 @@ Contents of this document:
(*) AF_RXRPC kernel interface.
+ (*) Configurable parameters.
+
========
OVERVIEW
@@ -864,3 +866,82 @@ The kernel interface functions are as follows:
This is used to allocate a null RxRPC key that can be used to indicate
anonymous security for a particular domain.
+
+
+=======================
+CONFIGURABLE PARAMETERS
+=======================
+
+The RxRPC protocol driver has a number of configurable parameters that can be
+adjusted through sysctls in /proc/net/rxrpc/:
+
+ (*) req_ack_delay
+
+ The amount of time in milliseconds after receiving a packet with the
+ request-ack flag set before we honour the flag and actually send the
+ requested ack.
+
+ Usually the other side won't stop sending packets until the advertised
+ reception window is full (to a maximum of 255 packets), so delaying the
+ ACK permits several packets to be ACK'd in one go.
+
+ (*) soft_ack_delay
+
+ The amount of time in milliseconds after receiving a new packet before we
+ generate a soft-ACK to tell the sender that it doesn't need to resend.
+
+ (*) idle_ack_delay
+
+ The amount of time in milliseconds after all the packets currently in the
+ received queue have been consumed before we generate a hard-ACK to tell
+ the sender it can free its buffers, assuming no other reason occurs that
+ we would send an ACK.
+
+ (*) resend_timeout
+
+ The amount of time in milliseconds after transmitting a packet before we
+ transmit it again, assuming no ACK is received from the receiver telling
+ us they got it.
+
+ (*) max_call_lifetime
+
+ The maximum amount of time in seconds that a call may be in progress
+ before we preemptively kill it.
+
+ (*) dead_call_expiry
+
+ The amount of time in seconds before we remove a dead call from the call
+ list. Dead calls are kept around for a little while for the purpose of
+ repeating ACK and ABORT packets.
+
+ (*) connection_expiry
+
+ The amount of time in seconds after a connection was last used before we
+ remove it from the connection list. Whilst a connection is in existence,
+ it serves as a placeholder for negotiated security; when it is deleted,
+ the security must be renegotiated.
+
+ (*) transport_expiry
+
+ The amount of time in seconds after a transport was last used before we
+ remove it from the transport list. Whilst a transport is in existence, it
+ serves to anchor the peer data and keeps the connection ID counter.
+
+ (*) rxrpc_rx_window_size
+
+ The size of the receive window in packets. This is the maximum number of
+ unconsumed received packets we're willing to hold in memory for any
+ particular call.
+
+ (*) rxrpc_rx_mtu
+
+ The maximum packet MTU size that we're willing to receive in bytes. This
+ indicates to the peer whether we're willing to accept jumbo packets.
+
+ (*) rxrpc_rx_jumbo_max
+
+ The maximum number of packets that we're willing to accept in a jumbo
+ packet. Non-terminal packets in a jumbo packet must contain a four byte
+ header plus exactly 1412 bytes of data. The terminal packet must contain
+ a four byte header plus any amount of data. In any event, a jumbo packet
+ may not exceed rxrpc_rx_mtu in size.
diff --git a/Documentation/networking/spider_net.txt b/Documentation/networking/spider_net.txt
index 4b4adb8eb14f..b0b75f8463b3 100644
--- a/Documentation/networking/spider_net.txt
+++ b/Documentation/networking/spider_net.txt
@@ -73,7 +73,7 @@ Thus, in an idle system, the GDACTDPA, tail and head pointers will
all be pointing at the same descr, which should be "empty". All of the
other descrs in the ring should be "empty" as well.
-The show_rx_chain() routine will print out the the locations of the
+The show_rx_chain() routine will print out the locations of the
GDACTDPA, tail and head pointers. It will also summarize the contents
of the ring, starting at the tail pointer, and listing the status
of the descrs that follow.
diff --git a/Documentation/networking/tcp.txt b/Documentation/networking/tcp.txt
index 7d11bb5dc30a..bdc4c0db51e1 100644
--- a/Documentation/networking/tcp.txt
+++ b/Documentation/networking/tcp.txt
@@ -30,7 +30,7 @@ A congestion control mechanism can be registered through functions in
tcp_cong.c. The functions used by the congestion control mechanism are
registered via passing a tcp_congestion_ops struct to
tcp_register_congestion_control. As a minimum name, ssthresh,
-cong_avoid, min_cwnd must be valid.
+cong_avoid must be valid.
Private data for a congestion control mechanism is stored in tp->ca_priv.
tcp_ca(tp) returns a pointer to this space. This is preallocated space - it
diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt
index 048c92b487f6..bc3554124903 100644
--- a/Documentation/networking/timestamping.txt
+++ b/Documentation/networking/timestamping.txt
@@ -202,6 +202,9 @@ Time stamps for outgoing packets are to be generated as follows:
and not free the skb. A driver not supporting hardware time stamping doesn't
do that. A driver must never touch sk_buff::tstamp! It is used to store
software generated time stamps by the network subsystem.
+- Driver should call skb_tx_timestamp() as close to passing sk_buff to hardware
+ as possible. skb_tx_timestamp() provides a software time stamp if requested
+ and hardware timestamping is not possible (SKBTX_IN_PROGRESS not set).
- As soon as the driver has sent the packet and/or obtained a
hardware time stamp for it, it passes the time stamp back by
calling skb_hwtstamp_tx() with the original skb, the raw
@@ -212,6 +215,3 @@ Time stamps for outgoing packets are to be generated as follows:
this would occur at a later time in the processing pipeline than other
software time stamping and therefore could lead to unexpected deltas
between time stamps.
-- If the driver did not set the SKBTX_IN_PROGRESS flag (see above), then
- dev_hard_start_xmit() checks whether software time stamping
- is wanted as fallback and potentially generates the time stamp.
diff --git a/Documentation/oops-tracing.txt b/Documentation/oops-tracing.txt
index 13032c0140d4..e3155995ddd8 100644
--- a/Documentation/oops-tracing.txt
+++ b/Documentation/oops-tracing.txt
@@ -265,6 +265,9 @@ characters, each representing a particular tainted value.
13: 'O' if an externally-built ("out-of-tree") module has been loaded.
+ 14: 'E' if an unsigned module has been loaded in a kernel supporting
+ module signature.
+
The primary reason for the 'Tainted: ' string is to tell kernel
debuggers if this is a clean kernel or if anything unusual has
occurred. Tainting is permanent: even if an offending module is
diff --git a/Documentation/power/devices.txt b/Documentation/power/devices.txt
index a66c9821b5ce..47d46dff70f7 100644
--- a/Documentation/power/devices.txt
+++ b/Documentation/power/devices.txt
@@ -391,7 +391,7 @@ When resuming from freeze, standby or memory sleep, the phases are:
the resume methods. This generally involves undoing the actions of the
preceding suspend_late phase.
- 3 The resume methods should bring the the device back to its operating
+ 3 The resume methods should bring the device back to its operating
state, so that it can perform normal I/O. This generally involves
undoing the actions of the suspend phase.
diff --git a/Documentation/ptp/testptp.c b/Documentation/ptp/testptp.c
index 4aba0436da65..f1ac2dae999e 100644
--- a/Documentation/ptp/testptp.c
+++ b/Documentation/ptp/testptp.c
@@ -19,6 +19,7 @@
*/
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <math.h>
#include <signal.h>
#include <stdio.h>
@@ -120,11 +121,19 @@ static void usage(char *progname)
" -i val index for event/trigger\n"
" -k val measure the time offset between system and phc clock\n"
" for 'val' times (Maximum 25)\n"
+ " -l list the current pin configuration\n"
+ " -L pin,val configure pin index 'pin' with function 'val'\n"
+ " the channel index is taken from the '-i' option\n"
+ " 'val' specifies the auxiliary function:\n"
+ " 0 - none\n"
+ " 1 - external time stamp\n"
+ " 2 - periodic output\n"
" -p val enable output with a period of 'val' nanoseconds\n"
" -P val enable or disable (val=1|0) the system clock PPS\n"
" -s set the ptp clock time from the system time\n"
" -S set the system time from the ptp clock time\n"
- " -t val shift the ptp clock time by 'val' seconds\n",
+ " -t val shift the ptp clock time by 'val' seconds\n"
+ " -T val set the ptp clock time to 'val' seconds\n",
progname);
}
@@ -134,6 +143,7 @@ int main(int argc, char *argv[])
struct ptp_extts_event event;
struct ptp_extts_request extts_request;
struct ptp_perout_request perout_request;
+ struct ptp_pin_desc desc;
struct timespec ts;
struct timex tx;
@@ -156,12 +166,15 @@ int main(int argc, char *argv[])
int extts = 0;
int gettime = 0;
int index = 0;
+ int list_pins = 0;
int oneshot = 0;
int pct_offset = 0;
int n_samples = 0;
int periodic = 0;
int perout = -1;
+ int pin_index = -1, pin_func;
int pps = -1;
+ int seconds = 0;
int settime = 0;
int64_t t1, t2, tp;
@@ -169,7 +182,7 @@ int main(int argc, char *argv[])
progname = strrchr(argv[0], '/');
progname = progname ? 1+progname : argv[0];
- while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghi:k:p:P:sSt:v"))) {
+ while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghi:k:lL:p:P:sSt:T:v"))) {
switch (c) {
case 'a':
oneshot = atoi(optarg);
@@ -199,6 +212,16 @@ int main(int argc, char *argv[])
pct_offset = 1;
n_samples = atoi(optarg);
break;
+ case 'l':
+ list_pins = 1;
+ break;
+ case 'L':
+ cnt = sscanf(optarg, "%d,%d", &pin_index, &pin_func);
+ if (cnt != 2) {
+ usage(progname);
+ return -1;
+ }
+ break;
case 'p':
perout = atoi(optarg);
break;
@@ -214,6 +237,10 @@ int main(int argc, char *argv[])
case 't':
adjtime = atoi(optarg);
break;
+ case 'T':
+ settime = 3;
+ seconds = atoi(optarg);
+ break;
case 'h':
usage(progname);
return 0;
@@ -245,12 +272,14 @@ int main(int argc, char *argv[])
" %d programmable alarms\n"
" %d external time stamp channels\n"
" %d programmable periodic signals\n"
- " %d pulse per second\n",
+ " %d pulse per second\n"
+ " %d programmable pins\n",
caps.max_adj,
caps.n_alarm,
caps.n_ext_ts,
caps.n_per_out,
- caps.pps);
+ caps.pps,
+ caps.n_pins);
}
}
@@ -304,6 +333,16 @@ int main(int argc, char *argv[])
}
}
+ if (settime == 3) {
+ ts.tv_sec = seconds;
+ ts.tv_nsec = 0;
+ if (clock_settime(clkid, &ts)) {
+ perror("clock_settime");
+ } else {
+ puts("set time okay");
+ }
+ }
+
if (extts) {
memset(&extts_request, 0, sizeof(extts_request));
extts_request.index = index;
@@ -331,6 +370,24 @@ int main(int argc, char *argv[])
}
}
+ if (list_pins) {
+ int n_pins = 0;
+ if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
+ perror("PTP_CLOCK_GETCAPS");
+ } else {
+ n_pins = caps.n_pins;
+ }
+ for (i = 0; i < n_pins; i++) {
+ desc.index = i;
+ if (ioctl(fd, PTP_PIN_GETFUNC, &desc)) {
+ perror("PTP_PIN_GETFUNC");
+ break;
+ }
+ printf("name %s index %u func %u chan %u\n",
+ desc.name, desc.index, desc.func, desc.chan);
+ }
+ }
+
if (oneshot) {
install_handler(SIGALRM, handle_alarm);
/* Create a timer. */
@@ -392,6 +449,18 @@ int main(int argc, char *argv[])
}
}
+ if (pin_index >= 0) {
+ memset(&desc, 0, sizeof(desc));
+ desc.index = pin_index;
+ desc.func = pin_func;
+ desc.chan = index;
+ if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) {
+ perror("PTP_PIN_SETFUNC");
+ } else {
+ puts("set pin function okay");
+ }
+ }
+
if (pps != -1) {
int enable = pps ? 1 : 0;
if (ioctl(fd, PTP_ENABLE_PPS, enable)) {
@@ -428,14 +497,14 @@ int main(int argc, char *argv[])
interval = t2 - t1;
offset = (t2 + t1) / 2 - tp;
- printf("system time: %ld.%ld\n",
+ printf("system time: %" PRId64 ".%u\n",
(pct+2*i)->sec, (pct+2*i)->nsec);
- printf("phc time: %ld.%ld\n",
+ printf("phc time: %" PRId64 ".%u\n",
(pct+2*i+1)->sec, (pct+2*i+1)->nsec);
- printf("system time: %ld.%ld\n",
+ printf("system time: %" PRId64 ".%u\n",
(pct+2*i+2)->sec, (pct+2*i+2)->nsec);
- printf("system/phc clock time offset is %ld ns\n"
- "system clock time delay is %ld ns\n",
+ printf("system/phc clock time offset is %" PRId64 " ns\n"
+ "system clock time delay is %" PRId64 " ns\n",
offset, interval);
}
diff --git a/Documentation/security/Smack.txt b/Documentation/security/Smack.txt
index 7a2d30c132e3..5ea996f21d6c 100644
--- a/Documentation/security/Smack.txt
+++ b/Documentation/security/Smack.txt
@@ -3,7 +3,7 @@
"Good for you, you've decided to clean the elevator!"
- The Elevator, from Dark Star
-Smack is the the Simplified Mandatory Access Control Kernel.
+Smack is the Simplified Mandatory Access Control Kernel.
Smack is a kernel based implementation of mandatory access
control that includes simplicity in its primary design goals.
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index ec8be46bf48d..271a09db6629 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -785,6 +785,8 @@ can be ORed together:
1024 - A module from drivers/staging was loaded.
2048 - The system is working around a severe firmware bug.
4096 - An out-of-tree module has been loaded.
+8192 - An unsigned module has been loaded in a kernel supporting module
+ signature.
==============================================================
diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt
index d614a9b6a280..dd9d0e33b443 100644
--- a/Documentation/sysctl/vm.txt
+++ b/Documentation/sysctl/vm.txt
@@ -175,18 +175,39 @@ Setting this to zero disables periodic writeback altogether.
drop_caches
-Writing to this will cause the kernel to drop clean caches, dentries and
-inodes from memory, causing that memory to become free.
+Writing to this will cause the kernel to drop clean caches, as well as
+reclaimable slab objects like dentries and inodes. Once dropped, their
+memory becomes free.
To free pagecache:
echo 1 > /proc/sys/vm/drop_caches
-To free dentries and inodes:
+To free reclaimable slab objects (includes dentries and inodes):
echo 2 > /proc/sys/vm/drop_caches
-To free pagecache, dentries and inodes:
+To free slab objects and pagecache:
echo 3 > /proc/sys/vm/drop_caches
-As this is a non-destructive operation and dirty objects are not freeable, the
-user should run `sync' first.
+This is a non-destructive operation and will not free any dirty objects.
+To increase the number of objects freed by this operation, the user may run
+`sync' prior to writing to /proc/sys/vm/drop_caches. This will minimize the
+number of dirty objects on the system and create more candidates to be
+dropped.
+
+This file is not a means to control the growth of the various kernel caches
+(inodes, dentries, pagecache, etc...) These objects are automatically
+reclaimed by the kernel when memory is needed elsewhere on the system.
+
+Use of this file can cause performance problems. Since it discards cached
+objects, it may cost a significant amount of I/O and CPU to recreate the
+dropped objects, especially if they were under heavy use. Because of this,
+use outside of a testing or debugging environment is not recommended.
+
+You may see informational messages in your kernel log when this file is
+used:
+
+ cat (1234): drop_caches: 3
+
+These are informational only. They do not mean that anything is wrong
+with your system. To disable them, echo 4 (bit 3) into drop_caches.
==============================================================
diff --git a/Documentation/trace/ftrace-design.txt b/Documentation/trace/ftrace-design.txt
index 79fcafc7fd64..3f669b9e8852 100644
--- a/Documentation/trace/ftrace-design.txt
+++ b/Documentation/trace/ftrace-design.txt
@@ -358,11 +358,8 @@ Every arch has an init callback function. If you need to do something early on
to initialize some state, this is the time to do that. Otherwise, this simple
function below should be sufficient for most people:
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- /* return value is done indirectly via data */
- *(unsigned long *)data = 0;
-
return 0;
}
diff --git a/Documentation/trace/ring-buffer-design.txt b/Documentation/trace/ring-buffer-design.txt
index 7d350b496585..ff747b6fa39b 100644
--- a/Documentation/trace/ring-buffer-design.txt
+++ b/Documentation/trace/ring-buffer-design.txt
@@ -683,7 +683,7 @@ against nested writers.
cmpxchg(tail_page, temp_page, next_page)
The above will update the tail page if it is still pointing to the expected
-page. If this fails, a nested write pushed it forward, the the current write
+page. If this fails, a nested write pushed it forward, the current write
does not need to push it.
diff --git a/Documentation/usb/WUSB-Design-overview.txt b/Documentation/usb/WUSB-Design-overview.txt
index 4c5e37939344..1cd07c017cf6 100644
--- a/Documentation/usb/WUSB-Design-overview.txt
+++ b/Documentation/usb/WUSB-Design-overview.txt
@@ -25,7 +25,7 @@ updated content.
* Design-overview.txt-1.8
This code implements a Ultra Wide Band stack for Linux, as well as
-drivers for the the USB based UWB radio controllers defined in the
+drivers for the USB based UWB radio controllers defined in the
Wireless USB 1.0 specification (including Wireless USB host controller
and an Intel WiNET controller).
diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv
index f14475011fea..2f6e93597ce0 100644
--- a/Documentation/video4linux/CARDLIST.bttv
+++ b/Documentation/video4linux/CARDLIST.bttv
@@ -163,3 +163,4 @@
162 -> Adlink MPG24
163 -> Bt848 Capture 14MHz
164 -> CyberVision CV06 (SV)
+165 -> Kworld V-Stream Xpert TV PVR878
diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885
index 9f056d512e35..fc009d0ee7d6 100644
--- a/Documentation/video4linux/CARDLIST.cx23885
+++ b/Documentation/video4linux/CARDLIST.cx23885
@@ -31,10 +31,13 @@
30 -> NetUP Dual DVB-T/C-CI RF [1b55:e2e4]
31 -> Leadtek Winfast PxDVR3200 H XC4000 [107d:6f39]
32 -> MPX-885
- 33 -> Mygica X8507 [14f1:8502]
+ 33 -> Mygica X8502/X8507 ISDB-T [14f1:8502]
34 -> TerraTec Cinergy T PCIe Dual [153b:117e]
35 -> TeVii S471 [d471:9022]
36 -> Hauppauge WinTV-HVR1255 [0070:2259]
37 -> Prof Revolution DVB-S2 8000 [8000:3034]
38 -> Hauppauge WinTV-HVR4400 [0070:c108,0070:c138,0070:c12a,0070:c1f8]
39 -> AVerTV Hybrid Express Slim HC81R [1461:d939]
+ 40 -> TurboSight TBS 6981 [6981:8888]
+ 41 -> TurboSight TBS 6980 [6980:8888]
+ 42 -> Leadtek Winfast PxPVR2200 [107d:6f21]
diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx
index e81864405102..e085b1243b45 100644
--- a/Documentation/video4linux/CARDLIST.em28xx
+++ b/Documentation/video4linux/CARDLIST.em28xx
@@ -57,6 +57,7 @@
56 -> Pinnacle Hybrid Pro (330e) (em2882) [2304:0226]
57 -> Kworld PlusTV HD Hybrid 330 (em2883) [eb1a:a316]
58 -> Compro VideoMate ForYou/Stereo (em2820/em2840) [185b:2041]
+ 59 -> Pinnacle PCTV HD Mini (em2874) [2304:023f]
60 -> Hauppauge WinTV HVR 850 (em2883) [2040:651f]
61 -> Pixelview PlayTV Box 4 USB 2.0 (em2820/em2840)
62 -> Gadmei TVR200 (em2820/em2840)
@@ -86,3 +87,8 @@
86 -> PCTV QuatroStick nano (520e) (em2884) [2013:0251]
87 -> Terratec Cinergy HTC USB XS (em2884) [0ccd:008e,0ccd:00ac]
88 -> C3 Tech Digital Duo HDTV/SDTV USB (em2884) [1b80:e755]
+ 89 -> Delock 61959 (em2874) [1b80:e1cc]
+ 90 -> KWorld USB ATSC TV Stick UB435-Q V2 (em2874) [1b80:e346]
+ 91 -> SpeedLink Vicious And Devine Laplace webcam (em2765) [1ae7:9003,1ae7:9004]
+ 92 -> PCTV DVB-S2 Stick (461e) (em28178)
+ 93 -> KWorld USB ATSC TV Stick UB435-Q V3 (em2874) [1b80:e34c]
diff --git a/Documentation/video4linux/fimc.txt b/Documentation/video4linux/fimc.txt
index e51f1b5b7324..7d6e160724bd 100644
--- a/Documentation/video4linux/fimc.txt
+++ b/Documentation/video4linux/fimc.txt
@@ -151,9 +151,8 @@ CONFIG_S5P_DEV_FIMC1 \
CONFIG_S5P_DEV_FIMC2 | optional
CONFIG_S5P_DEV_FIMC3 |
CONFIG_S5P_SETUP_FIMC /
-CONFIG_S5P_SETUP_MIPIPHY \
-CONFIG_S5P_DEV_CSIS0 | optional for MIPI-CSI interface
-CONFIG_S5P_DEV_CSIS1 /
+CONFIG_S5P_DEV_CSIS0 \ optional for MIPI-CSI interface
+CONFIG_S5P_DEV_CSIS1 /
Except that, relevant s5p_device_fimc? should be registered in the machine code
in addition to a "s5p-fimc-md" platform device to which the media device driver
diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt
index 1e6b6531bbcc..d2ba80bb7af5 100644
--- a/Documentation/video4linux/gspca.txt
+++ b/Documentation/video4linux/gspca.txt
@@ -55,6 +55,7 @@ zc3xx 0458:700f Genius VideoCam Web V2
sonixj 0458:7025 Genius Eye 311Q
sn9c20x 0458:7029 Genius Look 320s
sonixj 0458:702e Genius Slim 310 NB
+sn9c20x 0458:7045 Genius Look 1320 V2
sn9c20x 0458:704a Genius Slim 1320
sn9c20x 0458:704c Genius i-Look 1321
sn9c20x 045e:00f4 LifeCam VX-6000 (SN9C20x + OV9650)
diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt
index 6c4866b49eb5..667a43361706 100644
--- a/Documentation/video4linux/v4l2-framework.txt
+++ b/Documentation/video4linux/v4l2-framework.txt
@@ -34,6 +34,10 @@ So this framework sets up the basic building blocks that all drivers
need and this same framework should make it much easier to refactor
common code into utility functions shared by all drivers.
+A good example to look at as a reference is the v4l2-pci-skeleton.c
+source that is available in this directory. It is a skeleton driver for
+a PCI capture card, and demonstrates how to use the V4L2 driver
+framework. It can be used as a template for real PCI video capture driver.
Structure of a driver
---------------------
@@ -768,6 +772,7 @@ types exist:
VFL_TYPE_GRABBER: videoX for video input/output devices
VFL_TYPE_VBI: vbiX for vertical blank data (i.e. closed captions, teletext)
VFL_TYPE_RADIO: radioX for radio tuners
+VFL_TYPE_SDR: swradioX for Software Defined Radio tuners
The last argument gives you a certain amount of control over the device
device node number used (i.e. the X in videoX). Normally you will pass -1
diff --git a/Documentation/video4linux/v4l2-pci-skeleton.c b/Documentation/video4linux/v4l2-pci-skeleton.c
new file mode 100644
index 000000000000..3a1c0d2dafce
--- /dev/null
+++ b/Documentation/video4linux/v4l2-pci-skeleton.c
@@ -0,0 +1,913 @@
+/*
+ * This is a V4L2 PCI Skeleton Driver. It gives an initial skeleton source
+ * for use with other PCI drivers.
+ *
+ * This skeleton PCI driver assumes that the card has an S-Video connector as
+ * input 0 and an HDMI connector as input 1.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-contig.h>
+
+MODULE_DESCRIPTION("V4L2 PCI Skeleton Driver");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(pci, skeleton_pci_tbl);
+
+/**
+ * struct skeleton - All internal data for one instance of device
+ * @pdev: PCI device
+ * @v4l2_dev: top-level v4l2 device struct
+ * @vdev: video node structure
+ * @ctrl_handler: control handler structure
+ * @lock: ioctl serialization mutex
+ * @std: current SDTV standard
+ * @timings: current HDTV timings
+ * @format: current pix format
+ * @input: current video input (0 = SDTV, 1 = HDTV)
+ * @queue: vb2 video capture queue
+ * @alloc_ctx: vb2 contiguous DMA context
+ * @qlock: spinlock controlling access to buf_list and sequence
+ * @buf_list: list of buffers queued for DMA
+ * @sequence: frame sequence counter
+ */
+struct skeleton {
+ struct pci_dev *pdev;
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct mutex lock;
+ v4l2_std_id std;
+ struct v4l2_dv_timings timings;
+ struct v4l2_pix_format format;
+ unsigned input;
+
+ struct vb2_queue queue;
+ struct vb2_alloc_ctx *alloc_ctx;
+
+ spinlock_t qlock;
+ struct list_head buf_list;
+ unsigned int sequence;
+};
+
+struct skel_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+static inline struct skel_buffer *to_skel_buffer(struct vb2_buffer *vb2)
+{
+ return container_of(vb2, struct skel_buffer, vb);
+}
+
+static const struct pci_device_id skeleton_pci_tbl[] = {
+ /* { PCI_DEVICE(PCI_VENDOR_ID_, PCI_DEVICE_ID_) }, */
+ { 0, }
+};
+
+/*
+ * HDTV: this structure has the capabilities of the HDTV receiver.
+ * It is used to constrain the huge list of possible formats based
+ * upon the hardware capabilities.
+ */
+static const struct v4l2_dv_timings_cap skel_timings_cap = {
+ .type = V4L2_DV_BT_656_1120,
+ /* keep this initialization for compatibility with GCC < 4.4.6 */
+ .reserved = { 0 },
+ V4L2_INIT_BT_TIMINGS(
+ 720, 1920, /* min/max width */
+ 480, 1080, /* min/max height */
+ 27000000, 74250000, /* min/max pixelclock*/
+ V4L2_DV_BT_STD_CEA861, /* Supported standards */
+ /* capabilities */
+ V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE
+ )
+};
+
+/*
+ * Supported SDTV standards. This does the same job as skel_timings_cap, but
+ * for standard TV formats.
+ */
+#define SKEL_TVNORMS V4L2_STD_ALL
+
+/*
+ * Interrupt handler: typically interrupts happen after a new frame has been
+ * captured. It is the job of the handler to remove the new frame from the
+ * internal list and give it back to the vb2 framework, updating the sequence
+ * counter and timestamp at the same time.
+ */
+static irqreturn_t skeleton_irq(int irq, void *dev_id)
+{
+#ifdef TODO
+ struct skeleton *skel = dev_id;
+
+ /* handle interrupt */
+
+ /* Once a new frame has been captured, mark it as done like this: */
+ if (captured_new_frame) {
+ ...
+ spin_lock(&skel->qlock);
+ list_del(&new_buf->list);
+ spin_unlock(&skel->qlock);
+ new_buf->vb.v4l2_buf.sequence = skel->sequence++;
+ v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp);
+ vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE);
+ }
+#endif
+ return IRQ_HANDLED;
+}
+
+/*
+ * Setup the constraints of the queue: besides setting the number of planes
+ * per buffer and the size and allocation context of each plane, it also
+ * checks if sufficient buffers have been allocated. Usually 3 is a good
+ * 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 struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct skeleton *skel = vb2_get_drv_priv(vq);
+
+ if (vq->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - vq->num_buffers;
+
+ if (fmt && fmt->fmt.pix.sizeimage < skel->format.sizeimage)
+ return -EINVAL;
+ *nplanes = 1;
+ sizes[0] = fmt ? fmt->fmt.pix.sizeimage : skel->format.sizeimage;
+ alloc_ctxs[0] = skel->alloc_ctx;
+ return 0;
+}
+
+/*
+ * Prepare the buffer for queueing to the DMA engine: check and set the
+ * payload size and fill in the field. Note: if the format's field is
+ * V4L2_FIELD_ALTERNATE, then vb->v4l2_buf.field should be set in the
+ * interrupt handler since that's usually where you know if the TOP or
+ * BOTTOM field has been captured.
+ */
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct skeleton *skel = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size = skel->format.sizeimage;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(&skel->pdev->dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+ vb->v4l2_buf.field = skel->format.field;
+ return 0;
+}
+
+/*
+ * Queue this buffer to the DMA engine.
+ */
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct skeleton *skel = vb2_get_drv_priv(vb->vb2_queue);
+ struct skel_buffer *buf = to_skel_buffer(vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&skel->qlock, flags);
+ list_add_tail(&buf->list, &skel->buf_list);
+
+ /* TODO: Update any DMA pointers if necessary */
+
+ spin_unlock_irqrestore(&skel->qlock, flags);
+}
+
+static void return_all_buffers(struct skeleton *skel,
+ enum vb2_buffer_state state)
+{
+ struct skel_buffer *buf, *node;
+ unsigned long flags;
+
+ spin_lock_irqsave(&skel->qlock, flags);
+ list_for_each_entry_safe(buf, node, &skel->buf_list, list) {
+ vb2_buffer_done(&buf->vb, state);
+ list_del(&buf->list);
+ }
+ spin_unlock_irqrestore(&skel->qlock, flags);
+}
+
+/*
+ * Start streaming. First check if the minimum number of buffers have been
+ * queued. If not, then return -ENOBUFS and the vb2 framework will call
+ * this function again the next time a buffer has been queued until enough
+ * buffers are available to actually start the DMA engine.
+ */
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct skeleton *skel = vb2_get_drv_priv(vq);
+ int ret = 0;
+
+ skel->sequence = 0;
+
+ /* TODO: start DMA */
+
+ if (ret) {
+ /*
+ * In case of an error, return all active buffers to the
+ * QUEUED state
+ */
+ return_all_buffers(skel, VB2_BUF_STATE_QUEUED);
+ }
+ return ret;
+}
+
+/*
+ * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued
+ * and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct skeleton *skel = vb2_get_drv_priv(vq);
+
+ /* TODO: stop DMA */
+
+ /* Release all active buffers */
+ return_all_buffers(skel, VB2_BUF_STATE_ERROR);
+ return 0;
+}
+
+/*
+ * The vb2 queue ops. Note that since q->lock is set we can use the standard
+ * vb2_ops_wait_prepare/finish helper functions. If q->lock would be NULL,
+ * then this driver would have to provide these ops.
+ */
+static struct vb2_ops skel_qops = {
+ .queue_setup = queue_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+/*
+ * Required ioctl querycap. Note that the version field is prefilled with
+ * the version of the kernel.
+ */
+static int skeleton_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strlcpy(cap->card, "V4L2 PCI Skeleton", sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
+ pci_name(skel->pdev));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+/*
+ * Helper function to check and correct struct v4l2_pix_format. It's used
+ * not only in VIDIOC_TRY/S_FMT, but also elsewhere if changes to the SDTV
+ * standard, HDTV timings or the video input would require updating the
+ * current format.
+ */
+static void skeleton_fill_pix_format(struct skeleton *skel,
+ struct v4l2_pix_format *pix)
+{
+ pix->pixelformat = V4L2_PIX_FMT_YUYV;
+ if (skel->input == 0) {
+ /* S-Video input */
+ pix->width = 720;
+ pix->height = (skel->std & V4L2_STD_525_60) ? 480 : 576;
+ pix->field = V4L2_FIELD_INTERLACED;
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ } else {
+ /* HDMI input */
+ pix->width = skel->timings.bt.width;
+ pix->height = skel->timings.bt.height;
+ if (skel->timings.bt.interlaced)
+ pix->field = V4L2_FIELD_INTERLACED;
+ else
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_REC709;
+ }
+
+ /*
+ * The YUYV format is four bytes for every two pixels, so bytesperline
+ * is width * 2.
+ */
+ pix->bytesperline = pix->width * 2;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->priv = 0;
+}
+
+static int skeleton_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct skeleton *skel = video_drvdata(file);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ /*
+ * Due to historical reasons providing try_fmt with an unsupported
+ * pixelformat will return -EINVAL for video receivers. Webcam drivers,
+ * however, will silently correct the pixelformat. Some video capture
+ * applications rely on this behavior...
+ */
+ if (pix->pixelformat != V4L2_PIX_FMT_YUYV)
+ return -EINVAL;
+ skeleton_fill_pix_format(skel, pix);
+ return 0;
+}
+
+static int skeleton_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct skeleton *skel = video_drvdata(file);
+ int ret;
+
+ ret = skeleton_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ /*
+ * It is not allowed to change the format while buffers for use with
+ * streaming have already been allocated.
+ */
+ if (vb2_is_busy(&skel->queue))
+ return -EBUSY;
+
+ /* TODO: change format */
+ skel->format = f->fmt.pix;
+ return 0;
+}
+
+static int skeleton_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ f->fmt.pix = skel->format;
+ return 0;
+}
+
+static int skeleton_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, "4:2:2, packed, YUYV", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_YUYV;
+ f->flags = 0;
+ return 0;
+}
+
+static int skeleton_s_std(struct file *file, void *priv, v4l2_std_id std)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ /* S_STD is not supported on the HDMI input */
+ if (skel->input)
+ return -ENODATA;
+
+ /*
+ * No change, so just return. Some applications call S_STD again after
+ * the buffers for streaming have been set up, so we have to allow for
+ * this behavior.
+ */
+ if (std == skel->std)
+ return 0;
+
+ /*
+ * Changing the standard implies a format change, which is not allowed
+ * while buffers for use with streaming have already been allocated.
+ */
+ if (vb2_is_busy(&skel->queue))
+ return -EBUSY;
+
+ /* TODO: handle changing std */
+
+ skel->std = std;
+
+ /* Update the internal format */
+ skeleton_fill_pix_format(skel, &skel->format);
+ return 0;
+}
+
+static int skeleton_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ /* G_STD is not supported on the HDMI input */
+ if (skel->input)
+ return -ENODATA;
+
+ *std = skel->std;
+ return 0;
+}
+
+/*
+ * Query the current standard as seen by the hardware. This function shall
+ * never actually change the standard, it just detects and reports.
+ * The framework will initially set *std to tvnorms (i.e. the set of
+ * supported standards by this input), and this function should just AND
+ * this value. If there is no signal, then *std should be set to 0.
+ */
+static int skeleton_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ /* QUERY_STD is not supported on the HDMI input */
+ if (skel->input)
+ return -ENODATA;
+
+#ifdef TODO
+ /*
+ * Query currently seen standard. Initial value of *std is
+ * V4L2_STD_ALL. This function should look something like this:
+ */
+ get_signal_info();
+ if (no_signal) {
+ *std = 0;
+ return 0;
+ }
+ /* Use signal information to reduce the number of possible standards */
+ if (signal_has_525_lines)
+ *std &= V4L2_STD_525_60;
+ else
+ *std &= V4L2_STD_625_50;
+#endif
+ return 0;
+}
+
+static int skeleton_s_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ /* S_DV_TIMINGS is not supported on the S-Video input */
+ if (skel->input == 0)
+ return -ENODATA;
+
+ /* Quick sanity check */
+ if (!v4l2_valid_dv_timings(timings, &skel_timings_cap, NULL, NULL))
+ return -EINVAL;
+
+ /* Check if the timings are part of the CEA-861 timings. */
+ if (!v4l2_find_dv_timings_cap(timings, &skel_timings_cap,
+ 0, NULL, NULL))
+ return -EINVAL;
+
+ /* Return 0 if the new timings are the same as the current timings. */
+ if (v4l2_match_dv_timings(timings, &skel->timings, 0))
+ return 0;
+
+ /*
+ * Changing the timings implies a format change, which is not allowed
+ * while buffers for use with streaming have already been allocated.
+ */
+ if (vb2_is_busy(&skel->queue))
+ return -EBUSY;
+
+ /* TODO: Configure new timings */
+
+ /* Save timings */
+ skel->timings = *timings;
+
+ /* Update the internal format */
+ skeleton_fill_pix_format(skel, &skel->format);
+ return 0;
+}
+
+static int skeleton_g_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ /* G_DV_TIMINGS is not supported on the S-Video input */
+ if (skel->input == 0)
+ return -ENODATA;
+
+ *timings = skel->timings;
+ return 0;
+}
+
+static int skeleton_enum_dv_timings(struct file *file, void *_fh,
+ struct v4l2_enum_dv_timings *timings)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ /* ENUM_DV_TIMINGS is not supported on the S-Video input */
+ if (skel->input == 0)
+ return -ENODATA;
+
+ return v4l2_enum_dv_timings_cap(timings, &skel_timings_cap,
+ NULL, NULL);
+}
+
+/*
+ * Query the current timings as seen by the hardware. This function shall
+ * never actually change the timings, it just detects and reports.
+ * If no signal is detected, then return -ENOLINK. If the hardware cannot
+ * lock to the signal, then return -ENOLCK. If the signal is out of range
+ * of the capabilities of the system (e.g., it is possible that the receiver
+ * can lock but that the DMA engine it is connected to cannot handle
+ * pixelclocks above a certain frequency), then -ERANGE is returned.
+ */
+static int skeleton_query_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ /* QUERY_DV_TIMINGS is not supported on the S-Video input */
+ if (skel->input == 0)
+ return -ENODATA;
+
+#ifdef TODO
+ /*
+ * Query currently seen timings. This function should look
+ * something like this:
+ */
+ detect_timings();
+ if (no_signal)
+ return -ENOLINK;
+ if (cannot_lock_to_signal)
+ return -ENOLCK;
+ if (signal_out_of_range_of_capabilities)
+ return -ERANGE;
+
+ /* Useful for debugging */
+ v4l2_print_dv_timings(skel->v4l2_dev.name, "query_dv_timings:",
+ timings, true);
+#endif
+ return 0;
+}
+
+static int skeleton_dv_timings_cap(struct file *file, void *fh,
+ struct v4l2_dv_timings_cap *cap)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ /* DV_TIMINGS_CAP is not supported on the S-Video input */
+ if (skel->input == 0)
+ return -ENODATA;
+ *cap = skel_timings_cap;
+ return 0;
+}
+
+static int skeleton_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index > 1)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ if (i->index == 0) {
+ i->std = SKEL_TVNORMS;
+ strlcpy(i->name, "S-Video", sizeof(i->name));
+ i->capabilities = V4L2_IN_CAP_STD;
+ } else {
+ i->std = 0;
+ strlcpy(i->name, "HDMI", sizeof(i->name));
+ i->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+ }
+ return 0;
+}
+
+static int skeleton_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ if (i > 1)
+ return -EINVAL;
+
+ /*
+ * Changing the input implies a format change, which is not allowed
+ * while buffers for use with streaming have already been allocated.
+ */
+ if (vb2_is_busy(&skel->queue))
+ return -EBUSY;
+
+ skel->input = i;
+ /*
+ * Update tvnorms. The tvnorms value is used by the core to implement
+ * VIDIOC_ENUMSTD so it has to be correct. If tvnorms == 0, then
+ * ENUMSTD will return -ENODATA.
+ */
+ skel->vdev.tvnorms = i ? 0 : SKEL_TVNORMS;
+
+ /* Update the internal format */
+ skeleton_fill_pix_format(skel, &skel->format);
+ return 0;
+}
+
+static int skeleton_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct skeleton *skel = video_drvdata(file);
+
+ *i = skel->input;
+ return 0;
+}
+
+/* The control handler. */
+static int skeleton_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ /*struct skeleton *skel =
+ container_of(ctrl->handler, struct skeleton, ctrl_handler);*/
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ /* TODO: set brightness to ctrl->val */
+ break;
+ case V4L2_CID_CONTRAST:
+ /* TODO: set contrast to ctrl->val */
+ break;
+ case V4L2_CID_SATURATION:
+ /* TODO: set saturation to ctrl->val */
+ break;
+ case V4L2_CID_HUE:
+ /* TODO: set hue to ctrl->val */
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------
+ File operations for the device
+ ------------------------------------------------------------------*/
+
+static const struct v4l2_ctrl_ops skel_ctrl_ops = {
+ .s_ctrl = skeleton_s_ctrl,
+};
+
+/*
+ * The set of all supported ioctls. Note that all the streaming ioctls
+ * use the vb2 helper functions that take care of all the locking and
+ * that also do ownership tracking (i.e. only the filehandle that requested
+ * the buffers can call the streaming ioctls, all other filehandles will
+ * receive -EBUSY if they attempt to call the same streaming ioctls).
+ *
+ * The last three ioctls also use standard helper functions: these implement
+ * standard behavior for drivers with controls.
+ */
+static const struct v4l2_ioctl_ops skel_ioctl_ops = {
+ .vidioc_querycap = skeleton_querycap,
+ .vidioc_try_fmt_vid_cap = skeleton_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = skeleton_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = skeleton_g_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = skeleton_enum_fmt_vid_cap,
+
+ .vidioc_g_std = skeleton_g_std,
+ .vidioc_s_std = skeleton_s_std,
+ .vidioc_querystd = skeleton_querystd,
+
+ .vidioc_s_dv_timings = skeleton_s_dv_timings,
+ .vidioc_g_dv_timings = skeleton_g_dv_timings,
+ .vidioc_enum_dv_timings = skeleton_enum_dv_timings,
+ .vidioc_query_dv_timings = skeleton_query_dv_timings,
+ .vidioc_dv_timings_cap = skeleton_dv_timings_cap,
+
+ .vidioc_enum_input = skeleton_enum_input,
+ .vidioc_g_input = skeleton_g_input,
+ .vidioc_s_input = skeleton_s_input,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*
+ * The set of file operations. Note that all these ops are standard core
+ * helper functions.
+ */
+static const struct v4l2_file_operations skel_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .unlocked_ioctl = video_ioctl2,
+ .read = vb2_fop_read,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
+};
+
+/*
+ * The initial setup of this device instance. Note that the initial state of
+ * the driver should be complete. So the initial format, standard, timings
+ * and video input should all be initialized to some reasonable value.
+ */
+static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ /* The initial timings are chosen to be 720p60. */
+ static const struct v4l2_dv_timings timings_def =
+ V4L2_DV_BT_CEA_1280X720P60;
+ struct skeleton *skel;
+ struct video_device *vdev;
+ struct v4l2_ctrl_handler *hdl;
+ struct vb2_queue *q;
+ int ret;
+
+ /* Enable PCI */
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(&pdev->dev, "no suitable DMA available.\n");
+ goto disable_pci;
+ }
+
+ /* Allocate a new instance */
+ skel = devm_kzalloc(&pdev->dev, sizeof(struct skeleton), GFP_KERNEL);
+ if (!skel)
+ return -ENOMEM;
+
+ /* Allocate the interrupt */
+ ret = devm_request_irq(&pdev->dev, pdev->irq,
+ skeleton_irq, 0, KBUILD_MODNAME, skel);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ goto disable_pci;
+ }
+ skel->pdev = pdev;
+
+ /* Fill in the initial format-related settings */
+ skel->timings = timings_def;
+ skel->std = V4L2_STD_625_50;
+ skeleton_fill_pix_format(skel, &skel->format);
+
+ /* Initialize the top-level structure */
+ ret = v4l2_device_register(&pdev->dev, &skel->v4l2_dev);
+ if (ret)
+ goto disable_pci;
+
+ mutex_init(&skel->lock);
+
+ /* Add the controls */
+ hdl = &skel->ctrl_handler;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &skel_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+ v4l2_ctrl_new_std(hdl, &skel_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 16);
+ v4l2_ctrl_new_std(hdl, &skel_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 127);
+ v4l2_ctrl_new_std(hdl, &skel_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ if (hdl->error) {
+ ret = hdl->error;
+ goto free_hdl;
+ }
+ skel->v4l2_dev.ctrl_handler = hdl;
+
+ /* Initialize the vb2 queue */
+ q = &skel->queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+ q->drv_priv = skel;
+ q->buf_struct_size = sizeof(struct skel_buffer);
+ q->ops = &skel_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ /*
+ * Assume that this DMA engine needs to have at least two buffers
+ * available before it can be started. The start_streaming() op
+ * won't be called until at least this many buffers are queued up.
+ */
+ q->min_buffers_needed = 2;
+ /*
+ * The serialization lock for the streaming ioctls. This is the same
+ * as the main serialization lock, but if some of the non-streaming
+ * ioctls could take a long time to execute, then you might want to
+ * have a different lock here to prevent VIDIOC_DQBUF from being
+ * blocked while waiting for another action to finish. This is
+ * generally not needed for PCI devices, but USB devices usually do
+ * want a separate lock here.
+ */
+ q->lock = &skel->lock;
+ /*
+ * Since this driver can only do 32-bit DMA we must make sure that
+ * the vb2 core will allocate the buffers in 32-bit DMA memory.
+ */
+ q->gfp_flags = GFP_DMA32;
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto free_hdl;
+
+ skel->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(skel->alloc_ctx)) {
+ dev_err(&pdev->dev, "Can't allocate buffer context");
+ ret = PTR_ERR(skel->alloc_ctx);
+ goto free_hdl;
+ }
+ INIT_LIST_HEAD(&skel->buf_list);
+ spin_lock_init(&skel->qlock);
+
+ /* Initialize the video_device structure */
+ vdev = &skel->vdev;
+ strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
+ /*
+ * There is nothing to clean up, so release is set to an empty release
+ * function. The release callback must be non-NULL.
+ */
+ vdev->release = video_device_release_empty;
+ vdev->fops = &skel_fops,
+ vdev->ioctl_ops = &skel_ioctl_ops,
+ /*
+ * The main serialization lock. All ioctls are serialized by this
+ * lock. Exception: if q->lock is set, then the streaming ioctls
+ * are serialized by that separate lock.
+ */
+ vdev->lock = &skel->lock;
+ vdev->queue = q;
+ vdev->v4l2_dev = &skel->v4l2_dev;
+ /* Supported SDTV standards, if any */
+ vdev->tvnorms = SKEL_TVNORMS;
+ /* If this bit is set, then the v4l2 core will provide the support
+ * for the VIDIOC_G/S_PRIORITY ioctls. This flag will eventually
+ * go away once all drivers have been converted to use struct v4l2_fh.
+ */
+ set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags);
+ video_set_drvdata(vdev, skel);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto free_ctx;
+
+ dev_info(&pdev->dev, "V4L2 PCI Skeleton Driver loaded\n");
+ return 0;
+
+free_ctx:
+ vb2_dma_contig_cleanup_ctx(skel->alloc_ctx);
+free_hdl:
+ v4l2_ctrl_handler_free(&skel->ctrl_handler);
+ v4l2_device_unregister(&skel->v4l2_dev);
+disable_pci:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void skeleton_remove(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
+ struct skeleton *skel = container_of(v4l2_dev, struct skeleton, v4l2_dev);
+
+ video_unregister_device(&skel->vdev);
+ v4l2_ctrl_handler_free(&skel->ctrl_handler);
+ vb2_dma_contig_cleanup_ctx(skel->alloc_ctx);
+ v4l2_device_unregister(&skel->v4l2_dev);
+ pci_disable_device(skel->pdev);
+}
+
+static struct pci_driver skeleton_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = skeleton_probe,
+ .remove = skeleton_remove,
+ .id_table = skeleton_pci_tbl,
+};
+
+module_pci_driver(skeleton_driver);
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 6cd63a9010fb..a9380ba54c8e 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -586,8 +586,8 @@ struct kvm_fpu {
4.24 KVM_CREATE_IRQCHIP
-Capability: KVM_CAP_IRQCHIP
-Architectures: x86, ia64, ARM, arm64
+Capability: KVM_CAP_IRQCHIP, KVM_CAP_S390_IRQCHIP (s390)
+Architectures: x86, ia64, ARM, arm64, s390
Type: vm ioctl
Parameters: none
Returns: 0 on success, -1 on error
@@ -596,7 +596,10 @@ Creates an interrupt controller model in the kernel. On x86, creates a virtual
ioapic, a virtual PIC (two PICs, nested), and sets up future vcpus to have a
local APIC. IRQ routing for GSIs 0-15 is set to both PIC and IOAPIC; GSI 16-23
only go to the IOAPIC. On ia64, a IOSAPIC is created. On ARM/arm64, a GIC is
-created.
+created. On s390, a dummy irq routing table is created.
+
+Note that on s390 the KVM_CAP_S390_IRQCHIP vm capability needs to be enabled
+before KVM_CREATE_IRQCHIP can be used.
4.25 KVM_IRQ_LINE
@@ -612,6 +615,20 @@ On some architectures it is required that an interrupt controller model has
been previously created with KVM_CREATE_IRQCHIP. Note that edge-triggered
interrupts require the level to be set to 1 and then back to 0.
+On real hardware, interrupt pins can be active-low or active-high. This
+does not matter for the level field of struct kvm_irq_level: 1 always
+means active (asserted), 0 means inactive (deasserted).
+
+x86 allows the operating system to program the interrupt polarity
+(active-low/active-high) for level-triggered interrupts, and KVM used
+to consider the polarity. However, due to bitrot in the handling of
+active-low interrupts, the above convention is now valid on x86 too.
+This is signaled by KVM_CAP_X86_IOAPIC_POLARITY_IGNORED. Userspace
+should not present interrupts to the guest as active-low unless this
+capability is present (or unless it is not using the in-kernel irqchip,
+of course).
+
+
ARM/arm64 can signal an interrupt either at the CPU level, or at the
in-kernel irqchip (GIC), and for in-kernel irqchip can tell the GIC to
use PPIs designated for specific cpus. The irq field is interpreted
@@ -628,7 +645,7 @@ The irq_type field has the following values:
(The irq_id field thus corresponds nicely to the IRQ ID in the ARM GIC specs)
-In both cases, level is used to raise/lower the line.
+In both cases, level is used to assert/deassert the line.
struct kvm_irq_level {
union {
@@ -918,9 +935,9 @@ documentation when it pops into existence).
4.37 KVM_ENABLE_CAP
-Capability: KVM_CAP_ENABLE_CAP
+Capability: KVM_CAP_ENABLE_CAP, KVM_CAP_ENABLE_CAP_VM
Architectures: ppc, s390
-Type: vcpu ioctl
+Type: vcpu ioctl, vm ioctl (with KVM_CAP_ENABLE_CAP_VM)
Parameters: struct kvm_enable_cap (in)
Returns: 0 on success; -1 on error
@@ -951,6 +968,8 @@ function properly, this is the place to put them.
__u8 pad[64];
};
+The vcpu ioctl should be used for vcpu-specific capabilities, the vm ioctl
+for vm-wide capabilities.
4.38 KVM_GET_MP_STATE
@@ -1320,7 +1339,7 @@ KVM_ASSIGN_DEV_IRQ. Partial deassignment of host or guest IRQ is allowed.
4.52 KVM_SET_GSI_ROUTING
Capability: KVM_CAP_IRQ_ROUTING
-Architectures: x86 ia64
+Architectures: x86 ia64 s390
Type: vm ioctl
Parameters: struct kvm_irq_routing (in)
Returns: 0 on success, -1 on error
@@ -1343,6 +1362,7 @@ struct kvm_irq_routing_entry {
union {
struct kvm_irq_routing_irqchip irqchip;
struct kvm_irq_routing_msi msi;
+ struct kvm_irq_routing_s390_adapter adapter;
__u32 pad[8];
} u;
};
@@ -1350,6 +1370,7 @@ struct kvm_irq_routing_entry {
/* gsi routing entry types */
#define KVM_IRQ_ROUTING_IRQCHIP 1
#define KVM_IRQ_ROUTING_MSI 2
+#define KVM_IRQ_ROUTING_S390_ADAPTER 3
No flags are specified so far, the corresponding field must be set to zero.
@@ -1365,6 +1386,14 @@ struct kvm_irq_routing_msi {
__u32 pad;
};
+struct kvm_irq_routing_s390_adapter {
+ __u64 ind_addr;
+ __u64 summary_addr;
+ __u64 ind_offset;
+ __u32 summary_offset;
+ __u32 adapter_id;
+};
+
4.53 KVM_ASSIGN_SET_MSIX_NR
@@ -1462,7 +1491,7 @@ struct kvm_lapic_state {
char regs[KVM_APIC_REG_SIZE];
};
-Copies the input argument into the the Local APIC registers. The data format
+Copies the input argument into the Local APIC registers. The data format
and layout are the same as documented in the architecture manual.
@@ -2566,6 +2595,10 @@ executed a memory-mapped I/O instruction which could not be satisfied
by kvm. The 'data' member contains the written data if 'is_write' is
true, and should be filled by application code otherwise.
+The 'data' member contains, in its first 'len' bytes, the value as it would
+appear if the VCPU performed a load or store of the appropriate width directly
+to the byte array.
+
NOTE: For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_DCR,
KVM_EXIT_PAPR and KVM_EXIT_EPR the corresponding
operations are complete (and guest state is consistent) only after userspace
diff --git a/Documentation/virtual/kvm/devices/s390_flic.txt b/Documentation/virtual/kvm/devices/s390_flic.txt
new file mode 100644
index 000000000000..4ceef53164b0
--- /dev/null
+++ b/Documentation/virtual/kvm/devices/s390_flic.txt
@@ -0,0 +1,91 @@
+FLIC (floating interrupt controller)
+====================================
+
+FLIC handles floating (non per-cpu) interrupts, i.e. I/O, service and some
+machine check interruptions. All interrupts are stored in a per-vm list of
+pending interrupts. FLIC performs operations on this list.
+
+Only one FLIC instance may be instantiated.
+
+FLIC provides support to
+- add interrupts (KVM_DEV_FLIC_ENQUEUE)
+- inspect currently pending interrupts (KVM_FLIC_GET_ALL_IRQS)
+- purge all pending floating interrupts (KVM_DEV_FLIC_CLEAR_IRQS)
+- enable/disable for the guest transparent async page faults
+- register and modify adapter interrupt sources (KVM_DEV_FLIC_ADAPTER_*)
+
+Groups:
+ KVM_DEV_FLIC_ENQUEUE
+ Passes a buffer and length into the kernel which are then injected into
+ the list of pending interrupts.
+ attr->addr contains the pointer to the buffer and attr->attr contains
+ the length of the buffer.
+ The format of the data structure kvm_s390_irq as it is copied from userspace
+ is defined in usr/include/linux/kvm.h.
+
+ KVM_DEV_FLIC_GET_ALL_IRQS
+ Copies all floating interrupts into a buffer provided by userspace.
+ When the buffer is too small it returns -ENOMEM, which is the indication
+ for userspace to try again with a bigger buffer.
+ All interrupts remain pending, i.e. are not deleted from the list of
+ currently pending interrupts.
+ attr->addr contains the userspace address of the buffer into which all
+ interrupt data will be copied.
+ attr->attr contains the size of the buffer in bytes.
+
+ KVM_DEV_FLIC_CLEAR_IRQS
+ Simply deletes all elements from the list of currently pending floating
+ interrupts. No interrupts are injected into the guest.
+
+ KVM_DEV_FLIC_APF_ENABLE
+ Enables async page faults for the guest. So in case of a major page fault
+ the host is allowed to handle this async and continues the guest.
+
+ KVM_DEV_FLIC_APF_DISABLE_WAIT
+ Disables async page faults for the guest and waits until already pending
+ async page faults are done. This is necessary to trigger a completion interrupt
+ for every init interrupt before migrating the interrupt list.
+
+ KVM_DEV_FLIC_ADAPTER_REGISTER
+ Register an I/O adapter interrupt source. Takes a kvm_s390_io_adapter
+ describing the adapter to register:
+
+struct kvm_s390_io_adapter {
+ __u32 id;
+ __u8 isc;
+ __u8 maskable;
+ __u8 swap;
+ __u8 pad;
+};
+
+ id contains the unique id for the adapter, isc the I/O interruption subclass
+ to use, maskable whether this adapter may be masked (interrupts turned off)
+ and swap whether the indicators need to be byte swapped.
+
+
+ KVM_DEV_FLIC_ADAPTER_MODIFY
+ Modifies attributes of an existing I/O adapter interrupt source. Takes
+ a kvm_s390_io_adapter_req specifiying the adapter and the operation:
+
+struct kvm_s390_io_adapter_req {
+ __u32 id;
+ __u8 type;
+ __u8 mask;
+ __u16 pad0;
+ __u64 addr;
+};
+
+ id specifies the adapter and type the operation. The supported operations
+ are:
+
+ KVM_S390_IO_ADAPTER_MASK
+ mask or unmask the adapter, as specified in mask
+
+ KVM_S390_IO_ADAPTER_MAP
+ perform a gmap translation for the guest address provided in addr,
+ pin a userspace page for the translated address and add it to the
+ list of mappings
+
+ KVM_S390_IO_ADAPTER_UNMAP
+ release a userspace page for the translated address specified in addr
+ from the list of mappings
diff --git a/Documentation/vm/unevictable-lru.txt b/Documentation/vm/unevictable-lru.txt
index a68db7692ee8..744f82f86c58 100644
--- a/Documentation/vm/unevictable-lru.txt
+++ b/Documentation/vm/unevictable-lru.txt
@@ -453,7 +453,7 @@ putback_lru_page() function to add migrated pages back to the LRU.
mmap(MAP_LOCKED) SYSTEM CALL HANDLING
-------------------------------------
-In addition the the mlock()/mlockall() system calls, an application can request
+In addition the mlock()/mlockall() system calls, an application can request
that a region of memory be mlocked supplying the MAP_LOCKED flag to the mmap()
call. Furthermore, any mmap() call or brk() call that expands the heap by a
task that has previously called mlockall() with the MCL_FUTURE flag will result
diff --git a/Documentation/zh_CN/SubmittingPatches b/Documentation/zh_CN/SubmittingPatches
index be0bd4725062..1d3a10f8746b 100644
--- a/Documentation/zh_CN/SubmittingPatches
+++ b/Documentation/zh_CN/SubmittingPatches
@@ -82,10 +82,6 @@ Documentation/SubmittingDrivers 。
Quilt:
http://savannah.nongnu.org/projects/quilt
-Andrew Morton çš„è¡¥ä¸è„šæœ¬:
-http://userweb.kernel.org/~akpm/stuff/patch-scripts.tar.gz
-作为这些脚本的替代,quilt 是值得推èçš„è¡¥ä¸ç®¡ç†å·¥å…·(看上é¢çš„链接)。
-
2)æ述你的改动。
æ述你的改动包å«çš„技术细节。
@@ -394,7 +390,7 @@ Static inline 函数相比å®æ¥è¯´ï¼Œæ˜¯å¥½å¾—多的选择。Static inline 函æ
----------------
Andrew Morton, "The perfect patch" (tpp).
- <http://userweb.kernel.org/~akpm/stuff/tpp.txt>
+ <http://www.ozlabs.org/~akpm/stuff/tpp.txt>
Jeff Garzik, "Linux kernel patch submission format".
<http://linux.yyz.us/patch-format.html>
@@ -406,7 +402,7 @@ Greg Kroah-Hartman, "How to piss off a kernel subsystem maintainer".
<http://www.kroah.com/log/2006/01/11/>
NO!!!! No more huge patch bombs to linux-kernel@vger.kernel.org people!
- <http://marc.theaimsgroup.com/?l=linux-kernel&m=112112749912944&w=2>
+ <https://lkml.org/lkml/2005/7/11/336>
Kernel Documentation/CodingStyle:
<http://sosdg.org/~coywolf/lxr/source/Documentation/CodingStyle>
diff --git a/MAINTAINERS b/MAINTAINERS
index e2ed69f1c430..bfff89690c3a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -536,6 +536,13 @@ S: Odd Fixes
L: linux-alpha@vger.kernel.org
F: arch/alpha/
+ALTERA TRIPLE SPEED ETHERNET DRIVER
+M: Vince Bridgers <vbridgers2013@gmail.com
+L: netdev@vger.kernel.org
+L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/net/ethernet/altera/
+
ALTERA UART/JTAG UART SERIAL DRIVERS
M: Tobias Klauser <tklauser@distanz.ch>
L: linux-serial@vger.kernel.org
@@ -817,7 +824,7 @@ ARM/CIRRUS LOGIC CLPS711X ARM ARCHITECTURE
M: Alexander Shiyan <shc_work@mail.ru>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Odd Fixes
-F: arch/arm/mach-clps711x/
+N: clps711x
ARM/CIRRUS LOGIC EP93XX ARM ARCHITECTURE
M: Hartley Sweeten <hsweeten@visionengravers.com>
@@ -1168,6 +1175,14 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
W: http://www.arm.linux.org.uk/
S: Maintained
+ARM/QUALCOMM SUPPORT
+M: Kumar Gala <galak@codeaurora.org>
+M: David Brown <davidb@codeaurora.org>
+L: linux-arm-msm@vger.kernel.org
+S: Maintained
+F: arch/arm/mach-qcom/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom.git
+
ARM/RADISYS ENP2611 MACHINE SUPPORT
M: Lennert Buytenhek <kernel@wantstofly.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -1276,13 +1291,21 @@ S: Maintained
F: drivers/clk/socfpga/
ARM/STI ARCHITECTURE
-M: Srinivas Kandagatla <srinivas.kandagatla@st.com>
-M: Stuart Menefy <stuart.menefy@st.com>
+M: Srinivas Kandagatla <srinivas.kandagatla@gmail.com>
+M: Maxime Coquelin <maxime.coquelin@st.com>
+M: Patrice Chotard <patrice.chotard@st.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: kernel@stlinux.com
W: http://www.stlinux.com
S: Maintained
F: arch/arm/mach-sti/
+F: arch/arm/boot/dts/sti*
+F: drivers/clocksource/arm_global_timer.c
+F: drivers/reset/sti/
+F: drivers/pinctrl/pinctrl-st.c
+F: drivers/media/rc/st_rc.c
+F: drivers/i2c/busses/i2c-st.c
+F: drivers/tty/serial/st-asc.c
ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT
M: Lennert Buytenhek <kernel@wantstofly.org>
@@ -1697,6 +1720,8 @@ F: drivers/net/wireless/b43legacy/
BACKLIGHT CLASS/SUBSYSTEM
M: Jingoo Han <jg1.han@samsung.com>
+M: Bryan Wu <cooloney@gmail.com>
+M: Lee Jones <lee.jones@linaro.org>
S: Maintained
F: drivers/video/backlight/
F: include/linux/backlight.h
@@ -1848,6 +1873,12 @@ L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/broadcom/b44.*
+BROADCOM GENET ETHERNET DRIVER
+M: Florian Fainelli <f.fainelli@gmail.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/broadcom/genet/
+
BROADCOM BNX2 GIGABIT ETHERNET DRIVER
M: Michael Chan <mchan@broadcom.com>
L: netdev@vger.kernel.org
@@ -1879,11 +1910,19 @@ M: Stephen Warren <swarren@wwwdotorg.org>
L: linux-rpi-kernel@lists.infradead.org (moderated for non-subscribers)
T: git git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-rpi.git
S: Maintained
-F: arch/arm/mach-bcm2835/
+F: arch/arm/mach-bcm/board_bcm2835.c
F: arch/arm/boot/dts/bcm2835*
F: arch/arm/configs/bcm2835_defconfig
F: drivers/*/*bcm2835*
+BROADCOM BCM5301X ARM ARCHICTURE
+M: Hauke Mehrtens <hauke@hauke-m.de>
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: arch/arm/mach-bcm/bcm_5301x.c
+F: arch/arm/boot/dts/bcm5301x.dtsi
+F: arch/arm/boot/dts/bcm470*
+
BROADCOM TG3 GIGABIT ETHERNET DRIVER
M: Nithin Nayak Sujir <nsujir@broadcom.com>
M: Michael Chan <mchan@broadcom.com>
@@ -1913,6 +1952,13 @@ L: linux-scsi@vger.kernel.org
S: Supported
F: drivers/scsi/bnx2i/
+BROADCOM KONA GPIO DRIVER
+M: Markus Mayer <markus.mayer@linaro.org>
+L: bcm-kernel-feedback-list@broadcom.com
+S: Supported
+F: drivers/gpio/gpio-bcm-kona.c
+F: Documentation/devicetree/bindings/gpio/gpio-bcm-kona.txt
+
BROADCOM SPECIFIC AMBA DRIVER (BCMA)
M: Rafał Miłecki <zajec5@gmail.com>
L: linux-wireless@vger.kernel.org
@@ -2272,7 +2318,7 @@ F: include/uapi/linux/coda*.h
COMMON CLK FRAMEWORK
M: Mike Turquette <mturquette@linaro.org>
-L: linux-arm-kernel@lists.infradead.org (same as CLK API & CLKDEV)
+L: linux-kernel@vger.kernel.org
T: git git://git.linaro.org/people/mturquette/linux.git
S: Maintained
F: drivers/clk/
@@ -2715,6 +2761,31 @@ F: include/linux/device-mapper.h
F: include/linux/dm-*.h
F: include/uapi/linux/dm-*.h
+DIALOG SEMICONDUCTOR DRIVERS
+M: Support Opensource <support.opensource@diasemi.com>
+W: http://www.dialog-semiconductor.com/products
+S: Supported
+F: Documentation/hwmon/da90??
+F: drivers/gpio/gpio-da90??.c
+F: drivers/hwmon/da90??-hwmon.c
+F: drivers/input/misc/da90??_onkey.c
+F: drivers/input/touchscreen/da9052_tsi.c
+F: drivers/leds/leds-da90??.c
+F: drivers/mfd/da903x.c
+F: drivers/mfd/da90??-*.c
+F: drivers/power/da9052-battery.c
+F: drivers/regulator/da903x.c
+F: drivers/regulator/da9???-regulator.[ch]
+F: drivers/rtc/rtc-da90??.c
+F: drivers/video/backlight/da90??_bl.c
+F: drivers/watchdog/da90??_wdt.c
+F: include/linux/mfd/da903x.h
+F: include/linux/mfd/da9052/
+F: include/linux/mfd/da9055/
+F: include/linux/mfd/da9063/
+F: include/sound/da[79]*.h
+F: sound/soc/codecs/da[79]*.[ch]
+
DIGI NEO AND CLASSIC PCI PRODUCTS
M: Lidza Louina <lidza.louina@gmail.com>
L: driverdev-devel@linuxdriverproject.org
@@ -3388,7 +3459,9 @@ F: Documentation/filesystems/ext4.txt
F: fs/ext4/
Extended Verification Module (EVM)
-M: Mimi Zohar <zohar@us.ibm.com>
+M: Mimi Zohar <zohar@linux.vnet.ibm.com>
+L: linux-ima-devel@lists.sourceforge.net
+L: linux-security-module@vger.kernel.org
S: Supported
F: security/integrity/evm/
@@ -3472,7 +3545,8 @@ F: include/scsi/libfcoe.h
F: include/uapi/scsi/fc/
FILE LOCKING (flock() and fcntl()/lockf())
-M: Matthew Wilcox <matthew@wil.cx>
+M: Jeff Layton <jlayton@redhat.com>
+M: J. Bruce Fields <bfields@fieldses.org>
L: linux-fsdevel@vger.kernel.org
S: Maintained
F: include/linux/fcntl.h
@@ -4113,8 +4187,7 @@ F: include/linux/hpet.h
F: include/uapi/linux/hpet.h
HPET: x86
-M: "Venkatesh Pallipadi (Venki)" <venki@google.com>
-S: Maintained
+S: Orphan
F: arch/x86/kernel/hpet.c
F: arch/x86/include/asm/hpet.h
@@ -4410,8 +4483,11 @@ S: Maintained
F: drivers/ipack/
INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
-M: Mimi Zohar <zohar@us.ibm.com>
+M: Mimi Zohar <zohar@linux.vnet.ibm.com>
M: Dmitry Kasatkin <d.kasatkin@samsung.com>
+L: linux-ima-devel@lists.sourceforge.net
+L: linux-ima-user@lists.sourceforge.net
+L: linux-security-module@vger.kernel.org
S: Supported
F: security/integrity/ima/
@@ -4602,7 +4678,7 @@ F: arch/x86/kernel/tboot.c
INTEL WIRELESS WIMAX CONNECTION 2400
M: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
M: linux-wimax@intel.com
-L: wimax@linuxwimax.org
+L: wimax@linuxwimax.org (subscribers-only)
S: Supported
W: http://linuxwimax.org
F: Documentation/wimax/README.i2400m
@@ -4809,22 +4885,6 @@ F: Documentation/hwmon/it87
F: drivers/hwmon/it87.c
IT913X MEDIA DRIVER
-M: Malcolm Priestley <tvboxspy@gmail.com>
-L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
-Q: http://patchwork.linuxtv.org/project/linux-media/list/
-S: Maintained
-F: drivers/media/usb/dvb-usb-v2/it913x*
-
-IT913X FE MEDIA DRIVER
-M: Malcolm Priestley <tvboxspy@gmail.com>
-L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
-Q: http://patchwork.linuxtv.org/project/linux-media/list/
-S: Maintained
-F: drivers/media/dvb-frontends/it913x-fe*
-
-IT913X MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
W: http://linuxtv.org/
@@ -5079,8 +5139,8 @@ F: include/keys/
F: security/keys/
KEYS-TRUSTED
-M: David Safford <safford@watson.ibm.com>
-M: Mimi Zohar <zohar@us.ibm.com>
+M: David Safford <safford@us.ibm.com>
+M: Mimi Zohar <zohar@linux.vnet.ibm.com>
L: linux-security-module@vger.kernel.org
L: keyrings@linux-nfs.org
S: Supported
@@ -5090,8 +5150,8 @@ F: security/keys/trusted.c
F: security/keys/trusted.h
KEYS-ENCRYPTED
-M: Mimi Zohar <zohar@us.ibm.com>
-M: David Safford <safford@watson.ibm.com>
+M: Mimi Zohar <zohar@linux.vnet.ibm.com>
+M: David Safford <safford@us.ibm.com>
L: linux-security-module@vger.kernel.org
L: keyrings@linux-nfs.org
S: Supported
@@ -5716,7 +5776,6 @@ F: fs/imgdafs/
MICROBLAZE ARCHITECTURE
M: Michal Simek <monstr@monstr.eu>
-L: microblaze-uclinux@itee.uq.edu.au (moderated for non-subscribers)
W: http://www.monstr.eu/fdt/
T: git git://git.monstr.eu/linux-2.6-microblaze.git
S: Supported
@@ -5805,6 +5864,26 @@ L: platform-driver-x86@vger.kernel.org
S: Supported
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: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/staging/media/msi3101/msi001*
+
+MSI3101 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://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
+S: Maintained
+F: drivers/staging/media/msi3101/sdr-msi3101*
+
MT9M032 APTINA SENSOR DRIVER
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
L: linux-media@vger.kernel.org
@@ -6043,6 +6122,7 @@ L: netdev@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next.git
S: Maintained
+F: net/core/flow.c
F: net/xfrm/
F: net/key/
F: net/ipv4/xfrm*
@@ -6118,6 +6198,7 @@ F: include/net/nfc/
F: include/uapi/linux/nfc.h
F: drivers/nfc/
F: include/linux/platform_data/pn544.h
+F: Documentation/devicetree/bindings/net/nfc/
NFS, SUNRPC, AND LOCKD CLIENTS
M: Trond Myklebust <trond.myklebust@primarydata.com>
@@ -6136,10 +6217,10 @@ F: include/uapi/linux/nfs*
F: include/uapi/linux/sunrpc/
NILFS2 FILESYSTEM
-M: KONISHI Ryusuke <konishi.ryusuke@lab.ntt.co.jp>
+M: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
L: linux-nilfs@vger.kernel.org
-W: http://www.nilfs.org/en/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/ryusuke/nilfs2.git
+W: http://nilfs.sourceforge.net/
+T: git git://github.com/konis/nilfs2.git
S: Supported
F: Documentation/filesystems/nilfs2.txt
F: fs/nilfs2/
@@ -7077,13 +7158,8 @@ F: Documentation/networking/LICENSE.qla3xxx
F: drivers/net/ethernet/qlogic/qla3xxx.*
QLOGIC QLCNIC (1/10)Gb ETHERNET DRIVER
-M: Himanshu Madhani <himanshu.madhani@qlogic.com>
-M: Rajesh Borundia <rajesh.borundia@qlogic.com>
M: Shahed Shaikh <shahed.shaikh@qlogic.com>
-M: Jitendra Kalsaria <jitendra.kalsaria@qlogic.com>
-M: Sony Chacko <sony.chacko@qlogic.com>
-M: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
-M: linux-driver@qlogic.com
+M: Dept-HSGLinuxNICDev@qlogic.com
L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/qlogic/qlcnic/
@@ -7375,6 +7451,16 @@ T: git git://linuxtv.org/anttip/media_tree.git
S: Maintained
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: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/staging/media/rtl2832u_sdr/rtl2832_sdr*
+
RTL8180 WIRELESS DRIVER
M: "John W. Linville" <linville@tuxdriver.com>
L: linux-wireless@vger.kernel.org
@@ -7579,6 +7665,15 @@ S: Supported
L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
F: drivers/clk/samsung/
+SAMSUNG SXGBE DRIVERS
+M: Byungho An <bh74.an@samsung.com>
+M: Girish K S <ks.giri@samsung.com>
+M: Siva Reddy Kallam <siva.kallam@samsung.com>
+M: Vipul Pandya <vipul.pandya@samsung.com>
+S: Supported
+L: netdev@vger.kernel.org
+F: drivers/net/ethernet/samsung/sxgbe/
+
SERIAL DRIVERS
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
L: linux-serial@vger.kernel.org
@@ -7768,11 +7863,10 @@ M: Security Officers <security@kernel.org>
S: Supported
SELINUX SECURITY MODULE
+M: Paul Moore <paul@paul-moore.com>
M: Stephen Smalley <sds@tycho.nsa.gov>
-M: James Morris <james.l.morris@oracle.com>
M: Eric Paris <eparis@parisplace.org>
-M: Paul Moore <paul@paul-moore.com>
-L: selinux@tycho.nsa.gov (subscribers-only, general discussion)
+L: selinux@tycho.nsa.gov (moderated for non-subscribers)
W: http://selinuxproject.org
T: git git://git.infradead.org/users/pcmoore/selinux
S: Supported
@@ -7899,15 +7993,13 @@ F: drivers/media/usb/siano/
F: drivers/media/mmc/siano/
SH_VEU V4L2 MEM2MEM DRIVER
-M: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
L: linux-media@vger.kernel.org
-S: Maintained
+S: Orphan
F: drivers/media/platform/sh_veu.c
SH_VOU V4L2 OUTPUT DRIVER
-M: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
L: linux-media@vger.kernel.org
-S: Odd Fixes
+S: Orphan
F: drivers/media/platform/sh_vou.c
F: include/media/sh_vou.h
@@ -8453,12 +8545,10 @@ S: Maintained
F: drivers/net/ethernet/dlink/sundance.c
SUPERH
-M: Paul Mundt <lethal@linux-sh.org>
L: linux-sh@vger.kernel.org
W: http://www.linux-sh.org
Q: http://patchwork.kernel.org/project/linux-sh/list/
-T: git git://github.com/pmundt/linux-sh.git sh-latest
-S: Supported
+S: Orphan
F: Documentation/sh/
F: arch/sh/
F: drivers/sh/
@@ -8742,6 +8832,7 @@ M: Max Filippov <jcmvbkbc@gmail.com>
L: linux-xtensa@linux-xtensa.org
S: Maintained
F: arch/xtensa/
+F: drivers/irqchip/irq-xtensa-*
THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
@@ -9432,7 +9523,6 @@ F: include/media/videobuf2-*
VIRTIO CONSOLE DRIVER
M: Amit Shah <amit.shah@redhat.com>
-L: virtio-dev@lists.oasis-open.org
L: virtualization@lists.linux-foundation.org
S: Maintained
F: drivers/char/virtio_console.c
@@ -9442,7 +9532,6 @@ F: include/uapi/linux/virtio_console.h
VIRTIO CORE, NET AND BLOCK DRIVERS
M: Rusty Russell <rusty@rustcorp.com.au>
M: "Michael S. Tsirkin" <mst@redhat.com>
-L: virtio-dev@lists.oasis-open.org
L: virtualization@lists.linux-foundation.org
S: Maintained
F: drivers/virtio/
@@ -9455,7 +9544,6 @@ F: include/uapi/linux/virtio_*.h
VIRTIO HOST (VHOST)
M: "Michael S. Tsirkin" <mst@redhat.com>
L: kvm@vger.kernel.org
-L: virtio-dev@lists.oasis-open.org
L: virtualization@lists.linux-foundation.org
L: netdev@vger.kernel.org
S: Maintained
@@ -9636,7 +9724,7 @@ F: drivers/media/rc/winbond-cir.c
WIMAX STACK
M: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
M: linux-wimax@intel.com
-L: wimax@linuxwimax.org
+L: wimax@linuxwimax.org (subscribers-only)
S: Supported
W: http://linuxwimax.org
F: Documentation/wimax/README.wimax
diff --git a/Makefile b/Makefile
index e5ac8a62e6e5..00a933bb1f41 100644
--- a/Makefile
+++ b/Makefile
@@ -729,6 +729,13 @@ export KBUILD_IMAGE ?= vmlinux
export INSTALL_PATH ?= /boot
#
+# INSTALL_DTBS_PATH specifies a prefix for relocations required by build roots.
+# Like INSTALL_MOD_PATH, it isn't defined in the Makefile, but can be passed as
+# an argument if needed. Otherwise it defaults to the kernel install path
+#
+export INSTALL_DTBS_PATH ?= $(INSTALL_PATH)/dtbs/$(KERNELRELEASE)
+
+#
# INSTALL_MOD_PATH specifies a prefix to MODLIB for module directory
# relocations required by build roots. This is not defined in the
# makefile but the argument can be passed to make if needed.
diff --git a/arch/Kconfig b/arch/Kconfig
index 80bbb8ccd0d1..97ff872c7acc 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -86,9 +86,7 @@ config KPROBES_ON_FTRACE
optimize on top of function tracing.
config UPROBES
- bool "Transparent user-space probes (EXPERIMENTAL)"
- depends on UPROBE_EVENT && PERF_EVENTS
- default n
+ def_bool n
select PERCPU_RWSEM
help
Uprobes is the user-space counterpart to kprobes: they
@@ -101,8 +99,6 @@ config UPROBES
managed by the kernel and kept transparent to the probed
application. )
- If in doubt, say "N".
-
config HAVE_64BIT_ALIGNED_ACCESS
def_bool 64BIT && !HAVE_EFFICIENT_UNALIGNED_ACCESS
help
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 9be30c8cb0c2..75de197a2fef 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -356,7 +356,6 @@ config ARC_CURR_IN_REG
config ARC_MISALIGN_ACCESS
bool "Emulate unaligned memory access (userspace only)"
- default N
select SYSCTL_ARCH_UNALIGN_NO_WARN
select SYSCTL_ARCH_UNALIGN_ALLOW
help
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index b76ae4185a3b..d7a71e3ef55f 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -113,9 +113,6 @@ config ARM_DMA_IOMMU_ALIGNMENT
endif
-config HAVE_PWM
- bool
-
config MIGHT_HAVE_PCI
bool
@@ -207,6 +204,9 @@ config ZONE_DMA
config NEED_DMA_MAP_STATE
def_bool y
+config ARCH_SUPPORTS_UPROBES
+ def_bool y
+
config ARCH_HAS_DMA_SET_COHERENT_MASK
bool
@@ -306,9 +306,12 @@ choice
config ARCH_MULTIPLATFORM
bool "Allow multiple platforms to be selected"
depends on MMU
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select ARM_HAS_SG_CHAIN
select ARM_PATCH_PHYS_VIRT
select AUTO_ZRELADDR
select COMMON_CLK
+ select GENERIC_CLOCKEVENTS
select MULTI_IRQ_HANDLER
select SPARSE_IRQ
select USE_OF
@@ -388,8 +391,6 @@ config ARCH_CLPS711X
select CPU_ARM720T
select GENERIC_CLOCKEVENTS
select MFD_SYSCON
- select MULTI_IRQ_HANDLER
- select SPARSE_IRQ
help
Support for Cirrus Logic 711x/721x/731x based boards.
@@ -420,10 +421,8 @@ config ARCH_EFM32
bool "Energy Micro efm32"
depends on !MMU
select ARCH_REQUIRE_GPIOLIB
+ select AUTO_ZRELADDR
select ARM_NVIC
- # CLKSRC_MMIO is wrong here, but needed until a proper fix is merged,
- # i.e. CLKSRC_EFM32 selecting CLKSRC_MMIO
- select CLKSRC_MMIO
select CLKSRC_OF
select COMMON_CLK
select CPU_V7M
@@ -631,7 +630,6 @@ config ARCH_LPC32XX
select CPU_ARM926T
select GENERIC_CLOCKEVENTS
select HAVE_IDE
- select HAVE_PWM
select USE_OF
help
Support for the NXP LPC32XX family of processors
@@ -655,9 +653,8 @@ config ARCH_PXA
help
Support for Intel/Marvell's PXA2xx/PXA3xx processor line.
-config ARCH_MSM_NODT
- bool "Qualcomm MSM"
- select ARCH_MSM
+config ARCH_MSM
+ bool "Qualcomm MSM (non-multiplatform)"
select ARCH_REQUIRE_GPIOLIB
select COMMON_CLK
select GENERIC_CLOCKEVENTS
@@ -695,6 +692,7 @@ config ARCH_RPC
select ARCH_MAY_HAVE_PC_FDC
select ARCH_SPARSEMEM_ENABLE
select ARCH_USES_GETTIMEOFFSET
+ select CPU_SA110
select FIQ
select HAVE_IDE
select HAVE_PATA_PLATFORM
@@ -729,6 +727,7 @@ config ARCH_S3C24XX
bool "Samsung S3C24XX SoCs"
select ARCH_HAS_CPUFREQ
select ARCH_REQUIRE_GPIOLIB
+ select ATAGS
select CLKDEV_LOOKUP
select CLKSRC_SAMSUNG_PWM
select GENERIC_CLOCKEVENTS
@@ -751,6 +750,7 @@ config ARCH_S3C64XX
select ARCH_REQUIRE_GPIOLIB
select ARM_AMBA
select ARM_VIC
+ select ATAGS
select CLKDEV_LOOKUP
select CLKSRC_SAMSUNG_PWM
select COMMON_CLK
@@ -762,7 +762,7 @@ config ARCH_S3C64XX
select HAVE_TCM
select NO_IOPORT
select PLAT_SAMSUNG
- select PM_GENERIC_DOMAINS
+ select PM_GENERIC_DOMAINS if PM
select S3C_DEV_NAND
select S3C_GPIO_TRACK
select SAMSUNG_ATAGS
@@ -773,6 +773,7 @@ config ARCH_S3C64XX
config ARCH_S5P64X0
bool "Samsung S5P6440 S5P6450"
+ select ATAGS
select CLKDEV_LOOKUP
select CLKSRC_SAMSUNG_PWM
select CPU_V6
@@ -791,6 +792,7 @@ config ARCH_S5P64X0
config ARCH_S5PC100
bool "Samsung S5PC100"
select ARCH_REQUIRE_GPIOLIB
+ select ATAGS
select CLKDEV_LOOKUP
select CLKSRC_SAMSUNG_PWM
select CPU_V7
@@ -810,6 +812,7 @@ config ARCH_S5PV210
select ARCH_HAS_CPUFREQ
select ARCH_HAS_HOLES_MEMORYMODEL
select ARCH_SPARSEMEM_ENABLE
+ select ATAGS
select CLKDEV_LOOKUP
select CLKSRC_SAMSUNG_PWM
select CPU_V7
@@ -883,6 +886,12 @@ menu "Multiple platform selection"
comment "CPU Core family selection"
+config ARCH_MULTI_V4
+ bool "ARMv4 based platforms (FA526)"
+ depends on !ARCH_MULTI_V6_V7
+ select ARCH_MULTI_V4_V5
+ select CPU_FA526
+
config ARCH_MULTI_V4T
bool "ARMv4T based platforms (ARM720T, ARM920T, ...)"
depends on !ARCH_MULTI_V6_V7
@@ -895,7 +904,7 @@ config ARCH_MULTI_V5
bool "ARMv5 based platforms (ARM926T, XSCALE, PJ1, ...)"
depends on !ARCH_MULTI_V6_V7
select ARCH_MULTI_V4_V5
- select CPU_ARM926T if (!CPU_ARM946E || CPU_ARM1020 || \
+ select CPU_ARM926T if !(CPU_ARM946E || CPU_ARM1020 || \
CPU_ARM1020E || CPU_ARM1022 || CPU_ARM1026 || \
CPU_XSCALE || CPU_XSC3 || CPU_MOHAWK || CPU_FEROCEON)
@@ -905,16 +914,18 @@ config ARCH_MULTI_V4_V5
config ARCH_MULTI_V6
bool "ARMv6 based platforms (ARM11)"
select ARCH_MULTI_V6_V7
- select CPU_V6
+ select CPU_V6K
config ARCH_MULTI_V7
bool "ARMv7 based platforms (Cortex-A, PJ4, Scorpion, Krait)"
default y
select ARCH_MULTI_V6_V7
select CPU_V7
+ select HAVE_SMP
config ARCH_MULTI_V6_V7
bool
+ select MIGHT_HAVE_CACHE_L2X0
config ARCH_MULTI_CPU_AUTO
def_bool !(ARCH_MULTI_V4 || ARCH_MULTI_V4T || ARCH_MULTI_V6_V7)
@@ -922,6 +933,13 @@ config ARCH_MULTI_CPU_AUTO
endmenu
+config ARCH_VIRT
+ bool "Dummy Virtual Machine" if ARCH_MULTI_V7
+ select ARM_AMBA
+ select ARM_GIC
+ select ARM_PSCI
+ select HAVE_ARM_ARCH_TIMER
+
#
# This is sorted alphabetically by mach-* pathname. However, plat-*
# Kconfigs may be included either alphabetically (according to the
@@ -933,8 +951,6 @@ source "arch/arm/mach-at91/Kconfig"
source "arch/arm/mach-bcm/Kconfig"
-source "arch/arm/mach-bcm2835/Kconfig"
-
source "arch/arm/mach-berlin/Kconfig"
source "arch/arm/mach-clps711x/Kconfig"
@@ -1002,6 +1018,8 @@ source "arch/arm/plat-pxa/Kconfig"
source "arch/arm/mach-mmp/Kconfig"
+source "arch/arm/mach-qcom/Kconfig"
+
source "arch/arm/mach-realview/Kconfig"
source "arch/arm/mach-rockchip/Kconfig"
@@ -1045,8 +1063,6 @@ source "arch/arm/mach-versatile/Kconfig"
source "arch/arm/mach-vexpress/Kconfig"
source "arch/arm/plat-versatile/Kconfig"
-source "arch/arm/mach-virt/Kconfig"
-
source "arch/arm/mach-vt8500/Kconfig"
source "arch/arm/mach-w90x900/Kconfig"
@@ -1918,6 +1934,7 @@ config USE_OF
select IRQ_DOMAIN
select OF
select OF_EARLY_FLATTREE
+ select OF_RESERVED_MEM
help
Include support for flattened device tree machine descriptions.
@@ -2270,7 +2287,7 @@ source "kernel/power/Kconfig"
config ARCH_SUSPEND_POSSIBLE
depends on !ARCH_S5PC100
depends on CPU_ARM920T || CPU_ARM926T || CPU_FEROCEON || CPU_SA1100 || \
- CPU_V6 || CPU_V6K || CPU_V7 || CPU_XSC3 || CPU_XSCALE || CPU_MOHAWK
+ CPU_V6 || CPU_V6K || CPU_V7 || CPU_V7M || CPU_XSC3 || CPU_XSCALE || CPU_MOHAWK
def_bool y
config ARM_CPU_SUSPEND
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 0531da8e5216..4a2fc0bf6fc9 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -106,9 +106,14 @@ choice
depends on ARCH_BCM2835
select DEBUG_UART_PL01X
+ config DEBUG_BCM_5301X
+ bool "Kernel low-level debugging on BCM5301X UART1"
+ depends on ARCH_BCM_5301X
+ select DEBUG_UART_PL01X
+
config DEBUG_BCM_KONA_UART
bool "Kernel low-level debugging messages via BCM KONA UART"
- depends on ARCH_BCM
+ depends on ARCH_BCM_MOBILE
select DEBUG_UART_8250
help
Say Y here if you want kernel low-level debugging support
@@ -171,15 +176,6 @@ choice
Say Y here if you want the debug print routines to direct
their output to UART0 serial port on DaVinci DMx devices.
- config DEBUG_DAVINCI_TNETV107X_UART1
- bool "Kernel low-level debugging on DaVinci TNETV107x using UART1"
- depends on ARCH_DAVINCI_TNETV107X
- select DEBUG_UART_8250
- help
- Say Y here if you want the debug print routines to direct
- their output to UART1 serial port on DaVinci TNETV107X
- devices.
-
config DEBUG_ZYNQ_UART0
bool "Kernel low-level debugging on Xilinx Zynq using UART0"
depends on ARCH_ZYNQ
@@ -956,7 +952,7 @@ config DEBUG_STI_UART
config DEBUG_MSM_UART
bool
- depends on ARCH_MSM
+ depends on ARCH_MSM || ARCH_QCOM
config DEBUG_LL_INCLUDE
string
@@ -1014,7 +1010,6 @@ config DEBUG_UART_PHYS
default 0x02530c00 if DEBUG_KEYSTONE_UART0
default 0x02531000 if DEBUG_KEYSTONE_UART1
default 0x03010fe0 if ARCH_RPC
- default 0x08108300 if DEBUG_DAVINCI_TNETV107X_UART1
default 0x10009000 if DEBUG_REALVIEW_STD_PORT || DEBUG_CNS3XXX || \
DEBUG_VEXPRESS_UART0_CA9
default 0x1010c000 if DEBUG_REALVIEW_PB1176_PORT
@@ -1023,6 +1018,7 @@ config DEBUG_UART_PHYS
default 0x101f1000 if ARCH_VERSATILE
default 0x101fb000 if DEBUG_NOMADIK_UART
default 0x16000000 if ARCH_INTEGRATOR
+ default 0x18000300 if DEBUG_BCM_5301X
default 0x1c090000 if DEBUG_VEXPRESS_UART0_RS1
default 0x20060000 if DEBUG_RK29_UART0
default 0x20064000 if DEBUG_RK29_UART1 || DEBUG_RK3X_UART2
@@ -1071,6 +1067,7 @@ config DEBUG_UART_VIRT
default 0xf0009000 if DEBUG_CNS3XXX
default 0xf01fb000 if DEBUG_NOMADIK_UART
default 0xf0201000 if DEBUG_BCM2835
+ default 0xf1000300 if DEBUG_BCM_5301X
default 0xf11f1000 if ARCH_VERSATILE
default 0xf1600000 if ARCH_INTEGRATOR
default 0xf1c28000 if DEBUG_SUNXI_UART0
@@ -1110,7 +1107,6 @@ config DEBUG_UART_VIRT
default 0xfed12000 if ARCH_KIRKWOOD
default 0xfedc0000 if ARCH_EP93XX
default 0xfee003f8 if FOOTBRIDGE
- default 0xfee08300 if DEBUG_DAVINCI_TNETV107X_UART1
default 0xfee20000 if DEBUG_NSPIRE_CLASSIC_UART || DEBUG_NSPIRE_CX_UART
default 0xfef36000 if DEBUG_HIGHBANK_UART
default 0xfee82340 if ARCH_IOP13XX
@@ -1135,7 +1131,7 @@ config DEBUG_UART_8250_WORD
default y if DEBUG_PICOXCELL_UART || DEBUG_SOCFPGA_UART || \
ARCH_KEYSTONE || \
DEBUG_DAVINCI_DMx_UART0 || DEBUG_DAVINCI_DA8XX_UART1 || \
- DEBUG_DAVINCI_DA8XX_UART2 || DEBUG_DAVINCI_TNETV107X_UART1 || \
+ DEBUG_DAVINCI_DA8XX_UART2 || \
DEBUG_BCM_KONA_UART
config DEBUG_UART_8250_FLOW_CONTROL
@@ -1145,7 +1141,7 @@ config DEBUG_UART_8250_FLOW_CONTROL
config DEBUG_UNCOMPRESS
bool
- depends on ARCH_MULTIPLATFORM || ARCH_MSM
+ depends on ARCH_MULTIPLATFORM || ARCH_MSM || PLAT_SAMSUNG
default y if DEBUG_LL && !DEBUG_OMAP2PLUS_UART && \
(!DEBUG_TEGRA_UART || !ZBOOT_ROM)
help
@@ -1161,7 +1157,8 @@ config DEBUG_UNCOMPRESS
config UNCOMPRESS_INCLUDE
string
- default "debug/uncompress.h" if ARCH_MULTIPLATFORM || ARCH_MSM
+ default "debug/uncompress.h" if ARCH_MULTIPLATFORM || ARCH_MSM || \
+ PLAT_SAMSUNG || ARCH_EFM32
default "mach/uncompress.h"
config EARLY_PRINTK
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 08a9ef58d9c3..41c1931f0155 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -143,7 +143,6 @@ textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000
# by CONFIG_* macro name.
machine-$(CONFIG_ARCH_AT91) += at91
machine-$(CONFIG_ARCH_BCM) += bcm
-machine-$(CONFIG_ARCH_BCM2835) += bcm2835
machine-$(CONFIG_ARCH_BERLIN) += berlin
machine-$(CONFIG_ARCH_CLPS711X) += clps711x
machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx
@@ -180,6 +179,7 @@ machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2
machine-$(CONFIG_ARCH_ORION5X) += orion5x
machine-$(CONFIG_ARCH_PICOXCELL) += picoxcell
machine-$(CONFIG_ARCH_PXA) += pxa
+machine-$(CONFIG_ARCH_QCOM) += qcom
machine-$(CONFIG_ARCH_REALVIEW) += realview
machine-$(CONFIG_ARCH_ROCKCHIP) += rockchip
machine-$(CONFIG_ARCH_RPC) += rpc
@@ -199,7 +199,6 @@ machine-$(CONFIG_ARCH_U300) += u300
machine-$(CONFIG_ARCH_U8500) += ux500
machine-$(CONFIG_ARCH_VERSATILE) += versatile
machine-$(CONFIG_ARCH_VEXPRESS) += vexpress
-machine-$(CONFIG_ARCH_VIRT) += virt
machine-$(CONFIG_ARCH_VT8500) += vt8500
machine-$(CONFIG_ARCH_W90X900) += w90x900
machine-$(CONFIG_ARCH_ZYNQ) += zynq
@@ -310,9 +309,9 @@ $(INSTALL_TARGETS):
%.dtb: | scripts
$(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $(boot)/dts/$@
-PHONY += dtbs
-dtbs: scripts
- $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) dtbs
+PHONY += dtbs dtbs_install
+dtbs dtbs_install: prepare scripts
+ $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $@
# We use MRPROPER_FILES and CLEAN_FILES now
archclean:
@@ -331,6 +330,7 @@ define archhelp
echo ' bootpImage - Combined zImage and initial RAM disk'
echo ' (supply initrd image via make variable INITRD=<path>)'
echo '* dtbs - Build device tree blobs for enabled boards'
+ echo ' dtbs_install - Install dtbs to $(INSTALL_DTBS_PATH)'
echo ' install - Install uncompressed kernel'
echo ' zinstall - Install compressed kernel'
echo ' uinstall - Install U-Boot wrapped compressed kernel'
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 032030361bef..35c146f31e46 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -12,6 +12,8 @@ dtb-$(CONFIG_ARCH_AT91) += ethernut5.dtb
dtb-$(CONFIG_ARCH_AT91) += evk-pro3.dtb
dtb-$(CONFIG_ARCH_AT91) += tny_a9260.dtb
dtb-$(CONFIG_ARCH_AT91) += usb_a9260.dtb
+# sam9261
+dtb-$(CONFIG_ARCH_AT91) += at91sam9261ek.dtb
# sam9263
dtb-$(CONFIG_ARCH_AT91) += at91sam9263ek.dtb
dtb-$(CONFIG_ARCH_AT91) += tny_a9263.dtb
@@ -29,6 +31,8 @@ dtb-$(CONFIG_ARCH_AT91) += at91sam9m10g45ek.dtb
dtb-$(CONFIG_ARCH_AT91) += pm9g45.dtb
# sam9n12
dtb-$(CONFIG_ARCH_AT91) += at91sam9n12ek.dtb
+# sam9rl
+dtb-$(CONFIG_ARCH_AT91) += at91sam9rlek.dtb
# sam9x5
dtb-$(CONFIG_ARCH_AT91) += at91-ariag25.dtb
dtb-$(CONFIG_ARCH_AT91) += at91-cosino_mega2560.dtb
@@ -47,19 +51,15 @@ dtb-$(CONFIG_ARCH_AT91) += sama5d36ek.dtb
dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.dtb
dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb
-dtb-$(CONFIG_ARCH_BCM_MOBILE) += bcm11351-brt.dtb \
- bcm28155-ap.dtb
+dtb-$(CONFIG_ARCH_BCM_MOBILE) += bcm28155-ap.dtb \
+ bcm21664-garnet.dtb
dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb
+dtb-$(CONFIG_ARCH_BCM_5301X) += bcm4708-netgear-r6250.dtb
dtb-$(CONFIG_ARCH_BERLIN) += \
berlin2-sony-nsz-gs7.dtb \
berlin2cd-google-chromecast.dtb
dtb-$(CONFIG_ARCH_DAVINCI) += da850-enbw-cmc.dtb \
da850-evm.dtb
-dtb-$(CONFIG_ARCH_DOVE) += dove-cm-a510.dtb \
- dove-cubox.dtb \
- dove-d2plug.dtb \
- dove-d3plug.dtb \
- dove-dove-db.dtb
dtb-$(CONFIG_ARCH_EFM32) += efm32gg-dk3750.dtb
dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \
exynos4210-smdkv310.dtb \
@@ -82,14 +82,30 @@ dtb-$(CONFIG_ARCH_HIGHBANK) += highbank.dtb \
ecx-2000.dtb
dtb-$(CONFIG_ARCH_INTEGRATOR) += integratorap.dtb \
integratorcp.dtb
-dtb-$(CONFIG_ARCH_LPC32XX) += ea3250.dtb phy3250.dtb
-dtb-$(CONFIG_ARCH_KIRKWOOD) += kirkwood-cloudbox.dtb \
+dtb-$(CONFIG_ARCH_KEYSTONE) += k2hk-evm.dtb \
+ k2l-evm.dtb \
+ k2e-evm.dtb
+kirkwood := \
+ kirkwood-b3.dtb \
+ kirkwood-cloudbox.dtb \
kirkwood-db-88f6281.dtb \
kirkwood-db-88f6282.dtb \
kirkwood-dns320.dtb \
kirkwood-dns325.dtb \
kirkwood-dockstar.dtb \
kirkwood-dreamplug.dtb \
+ kirkwood-ds109.dtb \
+ kirkwood-ds110jv10.dtb \
+ kirkwood-ds111.dtb \
+ kirkwood-ds209.dtb \
+ kirkwood-ds210.dtb \
+ kirkwood-ds212.dtb \
+ kirkwood-ds212j.dtb \
+ kirkwood-ds409.dtb \
+ kirkwood-ds409slim.dtb \
+ kirkwood-ds411.dtb \
+ kirkwood-ds411j.dtb \
+ kirkwood-ds411slim.dtb \
kirkwood-goflexnet.dtb \
kirkwood-guruplug-server-plus.dtb \
kirkwood-ib62x0.dtb \
@@ -112,54 +128,74 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += kirkwood-cloudbox.dtb \
kirkwood-nsa310a.dtb \
kirkwood-openblocks_a6.dtb \
kirkwood-openblocks_a7.dtb \
+ kirkwood-rd88f6192.dtb \
+ kirkwood-rd88f6281-a0.dtb \
+ kirkwood-rd88f6281-a1.dtb \
+ kirkwood-rs212.dtb \
+ kirkwood-rs409.dtb \
+ kirkwood-rs411.dtb \
kirkwood-sheevaplug.dtb \
kirkwood-sheevaplug-esata.dtb \
+ kirkwood-t5325.dtb \
kirkwood-topkick.dtb \
kirkwood-ts219-6281.dtb \
- kirkwood-ts219-6282.dtb
+ kirkwood-ts219-6282.dtb \
+ kirkwood-ts419-6281.dtb \
+ kirkwood-ts419-6282.dtb
+dtb-$(CONFIG_ARCH_KIRKWOOD) += $(kirkwood)
+dtb-$(CONFIG_MACH_KIRKWOOD) += $(kirkwood)
+dtb-$(CONFIG_ARCH_LPC32XX) += ea3250.dtb phy3250.dtb
dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb
dtb-$(CONFIG_ARCH_MOXART) += moxart-uc7112lx.dtb
-dtb-$(CONFIG_ARCH_MSM) += qcom-msm8660-surf.dtb \
- qcom-msm8960-cdp.dtb \
- qcom-apq8074-dragonboard.dtb
-dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \
- armada-370-mirabox.dtb \
- armada-370-netgear-rn102.dtb \
- armada-370-netgear-rn104.dtb \
- armada-370-rd.dtb \
- armada-xp-axpwifiap.dtb \
- armada-xp-db.dtb \
- armada-xp-gp.dtb \
- armada-xp-netgear-rn2120.dtb \
- armada-xp-matrix.dtb \
- armada-xp-openblocks-ax3-4.dtb
dtb-$(CONFIG_ARCH_MXC) += \
+ imx25-eukrea-mbimxsd25-baseboard.dtb \
imx25-karo-tx25.dtb \
imx25-pdk.dtb \
imx27-apf27.dtb \
imx27-apf27dev.dtb \
imx27-pdk.dtb \
- imx27-phytec-phycore-som.dtb \
imx27-phytec-phycore-rdk.dtb \
- imx27-phytec-phycard-s-som.dtb \
imx27-phytec-phycard-s-rdk.dtb \
imx31-bug.dtb \
+ imx35-eukrea-mbimxsd35-baseboard.dtb \
+ imx50-evk.dtb \
imx51-apf51.dtb \
imx51-apf51dev.dtb \
imx51-babbage.dtb \
+ imx51-eukrea-mbimxsd51-baseboard.dtb \
imx53-ard.dtb \
- imx53-evk.dtb \
imx53-m53evk.dtb \
imx53-mba53.dtb \
imx53-qsb.dtb \
+ imx53-qsrb.dtb \
imx53-smd.dtb \
+ imx53-tx53-x03x.dtb \
+ imx53-tx53-x13x.dtb \
+ imx53-voipac-bsb.dtb \
imx6dl-cubox-i.dtb \
+ imx6dl-dfi-fs700-m60.dtb \
+ imx6dl-gw51xx.dtb \
+ imx6dl-gw52xx.dtb \
+ imx6dl-gw53xx.dtb \
+ imx6dl-gw54xx.dtb \
imx6dl-hummingboard.dtb \
+ imx6dl-nitrogen6x.dtb \
imx6dl-sabreauto.dtb \
+ imx6dl-sabrelite.dtb \
imx6dl-sabresd.dtb \
imx6dl-wandboard.dtb \
imx6q-arm2.dtb \
+ imx6q-cm-fx6.dtb \
imx6q-cubox-i.dtb \
+ imx6q-dfi-fs700-m60.dtb \
+ imx6q-dmo-edmqmx6.dtb \
+ imx6q-gk802.dtb \
+ imx6q-gw51xx.dtb \
+ imx6q-gw52xx.dtb \
+ imx6q-gw53xx.dtb \
+ imx6q-gw5400-a.dtb \
+ imx6q-gw54xx.dtb \
+ imx6q-nitrogen6x.dtb \
imx6q-phytec-pbab01.dtb \
imx6q-sabreauto.dtb \
imx6q-sabrelite.dtb \
@@ -183,6 +219,9 @@ dtb-$(CONFIG_ARCH_MXS) += imx23-evk.dtb \
imx28-cfa10056.dtb \
imx28-cfa10057.dtb \
imx28-cfa10058.dtb \
+ imx28-duckbill.dtb \
+ imx28-eukrea-mbmx283lc.dtb \
+ imx28-eukrea-mbmx287lc.dtb \
imx28-evk.dtb \
imx28-m28cu3.dtb \
imx28-m28evk.dtb \
@@ -199,6 +238,10 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \
omap2420-n810-wimax.dtb \
omap3430-sdp.dtb \
omap3-beagle.dtb \
+ omap3-cm-t3517.dtb \
+ omap3-sbc-t3517.dtb \
+ omap3-cm-t3530.dtb \
+ omap3-sbc-t3530.dtb \
omap3-cm-t3730.dtb \
omap3-sbc-t3730.dtb \
omap3-devkit8000.dtb \
@@ -209,12 +252,24 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \
omap3-n900.dtb \
omap3-n9.dtb \
omap3-n950.dtb \
+ omap3-overo-alto35.dtb \
+ omap3-overo-storm-alto35.dtb \
+ omap3-overo-chestnut43.dtb \
+ omap3-overo-storm-chestnut43.dtb \
+ omap3-overo-gallop43.dtb \
+ omap3-overo-storm-gallop43.dtb \
+ omap3-overo-palo43.dtb \
+ omap3-overo-storm-palo43.dtb \
+ omap3-overo-summit.dtb \
+ omap3-overo-storm-summit.dtb \
omap3-overo-tobi.dtb \
omap3-overo-storm-tobi.dtb \
omap3-gta04.dtb \
omap3-igep0020.dtb \
omap3-igep0030.dtb \
+ omap3-lilly-dbb056.dtb \
omap3-zoom3.dtb \
+ omap4-duovero-parlor.dtb \
omap4-panda.dtb \
omap4-panda-a4.dtb \
omap4-panda-es.dtb \
@@ -228,12 +283,17 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \
am335x-boneblack.dtb \
am335x-nano.dtb \
am335x-base0033.dtb \
+ am3517-craneboard.dtb \
am3517-evm.dtb \
am3517_mt_ventoux.dtb \
am43x-epos-evm.dtb \
+ am437x-gp-evm.dtb \
dra7-evm.dtb
dtb-$(CONFIG_ARCH_ORION5X) += orion5x-lacie-ethernet-disk-mini-v2.dtb
dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb
+dtb-$(CONFIG_ARCH_QCOM) += qcom-msm8660-surf.dtb \
+ qcom-msm8960-cdp.dtb \
+ qcom-apq8074-dragonboard.dtb
dtb-$(CONFIG_ARCH_U8500) += ste-snowball.dtb \
ste-hrefprev60-stuib.dtb \
ste-hrefprev60-tvk.dtb \
@@ -284,6 +344,9 @@ dtb-$(CONFIG_ARCH_SUNXI) += \
sun4i-a10-cubieboard.dtb \
sun4i-a10-mini-xplus.dtb \
sun4i-a10-hackberry.dtb \
+ sun4i-a10-inet97fv2.dtb \
+ sun4i-a10-olinuxino-lime.dtb \
+ sun4i-a10-pcduino.dtb \
sun5i-a10s-olinuxino-micro.dtb \
sun5i-a13-olinuxino.dtb \
sun5i-a13-olinuxino-micro.dtb \
@@ -322,8 +385,31 @@ dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \
dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb \
zynq-zc706.dtb \
zynq-zed.dtb
+dtb-$(CONFIG_MACH_ARMADA_370) += \
+ armada-370-db.dtb \
+ armada-370-mirabox.dtb \
+ armada-370-netgear-rn102.dtb \
+ armada-370-netgear-rn104.dtb \
+ armada-370-rd.dtb
+dtb-$(CONFIG_MACH_ARMADA_375) += \
+ armada-375-db.dtb
+dtb-$(CONFIG_MACH_ARMADA_38X) += \
+ armada-385-db.dtb \
+ armada-385-rd.dtb
+dtb-$(CONFIG_MACH_ARMADA_XP) += \
+ armada-xp-axpwifiap.dtb \
+ armada-xp-db.dtb \
+ armada-xp-gp.dtb \
+ armada-xp-netgear-rn2120.dtb \
+ armada-xp-matrix.dtb \
+ armada-xp-openblocks-ax3-4.dtb
+dtb-$(CONFIG_MACH_DOVE) += dove-cm-a510.dtb \
+ dove-cubox.dtb \
+ dove-d2plug.dtb \
+ dove-d3plug.dtb \
+ dove-dove-db.dtb
-targets += dtbs
+targets += dtbs dtbs_install
targets += $(dtb-y)
endif
@@ -333,3 +419,5 @@ dtbs: $(addprefix $(obj)/, $(dtb-y))
$(Q)rm -f $(obj)/../*.dtb
clean-files := *.dtb
+
+dtbs_install: $(addsuffix _dtbinst_, $(dtb-y))
diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts
index 7e6c64ed966d..28ae040e7c3d 100644
--- a/arch/arm/boot/dts/am335x-evm.dts
+++ b/arch/arm/boot/dts/am335x-evm.dts
@@ -260,6 +260,12 @@
>;
};
+ mmc1_pins: pinmux_mmc1_pins {
+ pinctrl-single,pins = <
+ 0x160 (PIN_INPUT | MUX_MODE7) /* spi0_cs1.gpio0_6 */
+ >;
+ };
+
lcd_pins_s0: lcd_pins_s0 {
pinctrl-single,pins = <
0x20 0x01 /* gpmc_ad8.lcd_data16, OUTPUT | MODE1 */
@@ -434,9 +440,9 @@
ranges = <0 0 0x08000000 0x10000000>; /* CS0: NAND */
nand@0,0 {
reg = <0 0 0>; /* CS0, offset 0 */
- nand-bus-width = <8>;
ti,nand-ecc-opt = "bch8";
- gpmc,device-nand = "true";
+ ti,elm-id = <&elm>;
+ nand-bus-width = <8>;
gpmc,device-width = <1>;
gpmc,sync-clk-ps = <0>;
gpmc,cs-on-ns = <0>;
@@ -460,50 +466,51 @@
gpmc,wait-monitoring-ns = <0>;
gpmc,wr-access-ns = <40>;
gpmc,wr-data-mux-bus-ns = <0>;
-
+ /* MTD partition table */
+ /* All SPL-* partitions are sized to minimal length
+ * which can be independently programmable. For
+ * NAND flash this is equal to size of erase-block */
#address-cells = <1>;
#size-cells = <1>;
- elm_id = <&elm>;
-
- /* MTD partition table */
partition@0 {
- label = "SPL1";
+ label = "NAND.SPL";
reg = <0x00000000 0x000020000>;
};
-
partition@1 {
- label = "SPL2";
+ label = "NAND.SPL.backup1";
reg = <0x00020000 0x00020000>;
};
-
partition@2 {
- label = "SPL3";
+ label = "NAND.SPL.backup2";
reg = <0x00040000 0x00020000>;
};
-
partition@3 {
- label = "SPL4";
+ label = "NAND.SPL.backup3";
reg = <0x00060000 0x00020000>;
};
-
partition@4 {
- label = "U-boot";
- reg = <0x00080000 0x001e0000>;
+ label = "NAND.u-boot-spl";
+ reg = <0x00080000 0x00040000>;
};
-
partition@5 {
- label = "environment";
- reg = <0x00260000 0x00020000>;
+ label = "NAND.u-boot";
+ reg = <0x000C0000 0x00100000>;
};
-
partition@6 {
- label = "Kernel";
- reg = <0x00280000 0x00500000>;
+ label = "NAND.u-boot-env";
+ reg = <0x001C0000 0x00020000>;
};
-
partition@7 {
- label = "File-System";
- reg = <0x00780000 0x0F880000>;
+ label = "NAND.u-boot-env.backup1";
+ reg = <0x001E0000 0x00020000>;
+ };
+ partition@8 {
+ label = "NAND.kernel";
+ reg = <0x00200000 0x00800000>;
+ };
+ partition@9 {
+ label = "NAND.file-system";
+ reg = <0x00A00000 0x0F600000>;
};
};
};
@@ -643,6 +650,9 @@
status = "okay";
vmmc-supply = <&vmmc_reg>;
bus-width = <4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
+ cd-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
};
&sham {
diff --git a/arch/arm/boot/dts/am335x-evmsk.dts b/arch/arm/boot/dts/am335x-evmsk.dts
index 486880b74831..ec08f6f677c3 100644
--- a/arch/arm/boot/dts/am335x-evmsk.dts
+++ b/arch/arm/boot/dts/am335x-evmsk.dts
@@ -45,6 +45,18 @@
regulator-boot-on;
};
+ wl12xx_vmmc: fixedregulator@2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&wl12xx_gpio>;
+ compatible = "regulator-fixed";
+ regulator-name = "vwl1271";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ gpio = <&gpio1 29 0>;
+ startup-delay-us = <70000>;
+ enable-active-high;
+ };
+
leds {
pinctrl-names = "default";
pinctrl-0 = <&user_leds_s0>;
@@ -270,6 +282,24 @@
0x144 (PIN_INPUT_PULLDOWN | MUX_MODE4) /* rmii1_ref_clk.mcasp1_axr3 */
>;
};
+
+ mmc2_pins: pinmux_mmc2_pins {
+ pinctrl-single,pins = <
+ 0x74 (PIN_INPUT_PULLUP | MUX_MODE7) /* gpmc_wpn.gpio0_31 */
+ 0x80 (PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn1.mmc1_clk */
+ 0x84 (PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn2.mmc1_cmd */
+ 0x00 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad0.mmc1_dat0 */
+ 0x04 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad1.mmc1_dat1 */
+ 0x08 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad2.mmc1_dat2 */
+ 0x0c (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad3.mmc1_dat3 */
+ >;
+ };
+
+ wl12xx_gpio: pinmux_wl12xx_gpio {
+ pinctrl-single,pins = <
+ 0x7c (PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_csn0.gpio1_29 */
+ >;
+ };
};
&uart0 {
@@ -342,9 +372,22 @@
status = "okay";
};
+ usb-phy@47401b00 {
+ status = "okay";
+ };
+
usb@47401000 {
status = "okay";
};
+
+ usb@47401800 {
+ status = "okay";
+ dr_mode = "host";
+ };
+
+ dma-controller@07402000 {
+ status = "okay";
+ };
};
&epwmss2 {
@@ -440,6 +483,7 @@
pinctrl-names = "default", "sleep";
pinctrl-0 = <&cpsw_default>;
pinctrl-1 = <&cpsw_sleep>;
+ dual_emac = <1>;
};
&davinci_mdio {
@@ -451,11 +495,13 @@
&cpsw_emac0 {
phy_id = <&davinci_mdio>, <0>;
phy-mode = "rgmii-txid";
+ dual_emac_res_vlan = <1>;
};
&cpsw_emac1 {
phy_id = <&davinci_mdio>, <1>;
phy-mode = "rgmii-txid";
+ dual_emac_res_vlan = <2>;
};
&mmc1 {
@@ -479,6 +525,16 @@
ti,no-reset-on-init;
};
+&mmc2 {
+ status = "okay";
+ vmmc-supply = <&wl12xx_vmmc>;
+ ti,non-removable;
+ bus-width = <4>;
+ cap-power-off-card;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc2_pins>;
+};
+
&mcasp1 {
pinctrl-names = "default";
pinctrl-0 = <&mcasp1_pins>;
diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 6d95d3df33c7..9770e35f2536 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -58,6 +58,10 @@
275000 1125000
>;
voltage-tolerance = <2>; /* 2 percentage */
+
+ clocks = <&dpll_mpu_ck>;
+ clock-names = "cpu";
+
clock-latency = <300000>; /* From omap-cpufreq driver */
};
};
@@ -318,6 +322,7 @@
compatible = "ti,omap4-hwspinlock";
reg = <0x480ca000 0x1000>;
ti,hwmods = "spinlock";
+ #hwlock-cells = <1>;
};
wdt2: wdt@44e35000 {
@@ -399,7 +404,7 @@
ti,timer-pwm;
};
- rtc@44e3e000 {
+ rtc: rtc@44e3e000 {
compatible = "ti,da830-rtc";
reg = <0x44e3e000 0x1000>;
interrupts = <75
@@ -448,7 +453,7 @@
ti,hwmods = "usb_otg_hs";
status = "disabled";
- usb_ctrl_mod: control@44e10000 {
+ usb_ctrl_mod: control@44e10620 {
compatible = "ti,am335x-usb-ctrl-module";
reg = <0x44e10620 0x10
0x44e10648 0x4>;
@@ -551,7 +556,7 @@
"tx14", "tx15";
};
- cppi41dma: dma-controller@07402000 {
+ cppi41dma: dma-controller@47402000 {
compatible = "ti,am3359-cppi41";
reg = <0x47400000 0x1000
0x47402000 0x1000
@@ -582,6 +587,8 @@
compatible = "ti,am33xx-ecap";
#pwm-cells = <3>;
reg = <0x48300100 0x80>;
+ interrupts = <31>;
+ interrupt-names = "ecap0";
ti,hwmods = "ecap0";
status = "disabled";
};
@@ -610,6 +617,8 @@
compatible = "ti,am33xx-ecap";
#pwm-cells = <3>;
reg = <0x48302100 0x80>;
+ interrupts = <47>;
+ interrupt-names = "ecap1";
ti,hwmods = "ecap1";
status = "disabled";
};
@@ -638,6 +647,8 @@
compatible = "ti,am33xx-ecap";
#pwm-cells = <3>;
reg = <0x48304100 0x80>;
+ interrupts = <61>;
+ interrupt-names = "ecap2";
ti,hwmods = "ecap2";
status = "disabled";
};
diff --git a/arch/arm/boot/dts/am3517-craneboard.dts b/arch/arm/boot/dts/am3517-craneboard.dts
new file mode 100644
index 000000000000..2d40b3f241cd
--- /dev/null
+++ b/arch/arm/boot/dts/am3517-craneboard.dts
@@ -0,0 +1,174 @@
+/*
+ * See craneboard.org for more details
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.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.
+ */
+/dts-v1/;
+
+#include "am3517.dtsi"
+
+/ {
+ model = "TI AM3517 CraneBoard (TMDSEVM3517)";
+ compatible = "ti,am3517-craneboard", "ti,am3517", "ti,omap3";
+
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x10000000>; /* 256 MB */
+ };
+
+ vbat: fixedregulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "vbat";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-boot-on;
+ };
+};
+
+&davinci_emac {
+ status = "okay";
+};
+
+&davinci_mdio {
+ status = "okay";
+};
+
+&i2c1 {
+ clock-frequency = <2600000>;
+
+ tps: tps@2d {
+ reg = <0x2d>;
+ };
+};
+
+&i2c2 {
+ clock-frequency = <400000>;
+ /* goes to expansion connector */
+ status = "disabled";
+};
+
+&i2c3 {
+ clock-frequency = <400000>;
+ /* goes to expansion connector */
+ status = "disabled";
+};
+
+&mmc1 {
+ vmmc-supply = <&vdd2_reg>;
+ bus-width = <8>;
+};
+
+&mmc2 {
+ /* goes to expansion connector */
+ status = "disabled";
+};
+
+&mmc3 {
+ /* goes to expansion connector */
+ status = "disabled";
+};
+
+#include "tps65910.dtsi"
+
+&omap3_pmx_core {
+ tps_pins: pinmux_tps_pins {
+ pinctrl-single,pins = <
+ 0x1b0 (PIN_INPUT_PULLUP | MUX_MODE0) /* sys_nirq.sys_nirq */
+ >;
+ };
+};
+
+&tps {
+ pinctrl-names = "default";
+ pinctrl-0 = <&tps_pins>;
+
+ interrupts = <7>; /* SYS_NIRQ cascaded to intc */
+ interrupt-parent = <&intc>;
+
+ ti,en-ck32k-xtal;
+
+ vcc1-supply = <&vbat>;
+ vcc2-supply = <&vbat>;
+ vcc3-supply = <&vbat>;
+ vcc4-supply = <&vbat>;
+ vcc5-supply = <&vbat>;
+ vcc6-supply = <&vbat>;
+ vcc7-supply = <&vbat>;
+ vccio-supply = <&vbat>;
+
+ regulators {
+ vrtc_reg: regulator@0 {
+ regulator-always-on;
+ };
+
+ vio_reg: regulator@1 {
+ regulator-always-on;
+ };
+
+ /*
+ * Unused:
+ * VDIG1=2.7V,300mA max
+ * VDIG2=1.8V,300mA max
+ */
+
+ vpll_reg: regulator@7 {
+ /* VDDS_DPLL_1V8 */
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ vaux1_reg: regulator@9 {
+ /* VDDS_SRAM_1V8 */
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ vaux2_reg: regulator@10 {
+ /* VDDA1P8V_USBPHY */
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ /* VAUX33 unused */
+
+ vdac_reg: regulator@8 {
+ /* VDDA_DAC_1V8 */
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ vmmc_reg: regulator@12 {
+ /* VDDA3P3V_USBPHY */
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vdd1_reg: regulator@2 {
+ /* VDD_CORE */
+ regulator-name = "vdd_core";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vdd2_reg: regulator@3 {
+ /* VDDSHV_3V3 */
+ regulator-name = "vdd_shv";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ /* VDD3 unused */
+ };
+};
diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index c6bd4d986c29..36d523a26831 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -8,6 +8,7 @@
* kind, whether express or implied.
*/
+#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "skeleton.dtsi"
@@ -33,6 +34,11 @@
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <0>;
+
+ clocks = <&dpll_mpu_ck>;
+ clock-names = "cpu";
+
+ clock-latency = <300000>; /* From omap-cpufreq driver */
};
};
@@ -351,6 +357,13 @@
status = "disabled";
};
+ hwspinlock: spinlock@480ca000 {
+ compatible = "ti,omap4-hwspinlock";
+ reg = <0x480ca000 0x1000>;
+ ti,hwmods = "spinlock";
+ #hwlock-cells = <1>;
+ };
+
i2c0: i2c@44e0b000 {
compatible = "ti,am4372-i2c","ti,omap4-i2c";
reg = <0x44e0b000 0x1000>;
@@ -521,6 +534,7 @@
ecap0: ecap@48300100 {
compatible = "ti,am4372-ecap","ti,am33xx-ecap";
+ #pwm-cells = <3>;
reg = <0x48300100 0x80>;
ti,hwmods = "ecap0";
status = "disabled";
@@ -528,6 +542,7 @@
ehrpwm0: ehrpwm@48300200 {
compatible = "ti,am4372-ehrpwm","ti,am33xx-ehrpwm";
+ #pwm-cells = <3>;
reg = <0x48300200 0x80>;
ti,hwmods = "ehrpwm0";
status = "disabled";
@@ -545,6 +560,7 @@
ecap1: ecap@48302100 {
compatible = "ti,am4372-ecap","ti,am33xx-ecap";
+ #pwm-cells = <3>;
reg = <0x48302100 0x80>;
ti,hwmods = "ecap1";
status = "disabled";
@@ -552,6 +568,7 @@
ehrpwm1: ehrpwm@48302200 {
compatible = "ti,am4372-ehrpwm","ti,am33xx-ehrpwm";
+ #pwm-cells = <3>;
reg = <0x48302200 0x80>;
ti,hwmods = "ehrpwm1";
status = "disabled";
@@ -569,6 +586,7 @@
ecap2: ecap@48304100 {
compatible = "ti,am4372-ecap","ti,am33xx-ecap";
+ #pwm-cells = <3>;
reg = <0x48304100 0x80>;
ti,hwmods = "ecap2";
status = "disabled";
@@ -576,6 +594,7 @@
ehrpwm2: ehrpwm@48304200 {
compatible = "ti,am4372-ehrpwm","ti,am33xx-ehrpwm";
+ #pwm-cells = <3>;
reg = <0x48304200 0x80>;
ti,hwmods = "ehrpwm2";
status = "disabled";
@@ -593,6 +612,7 @@
ehrpwm3: ehrpwm@48306200 {
compatible = "ti,am4372-ehrpwm","ti,am33xx-ehrpwm";
+ #pwm-cells = <3>;
reg = <0x48306200 0x80>;
ti,hwmods = "ehrpwm3";
status = "disabled";
@@ -610,6 +630,7 @@
ehrpwm4: ehrpwm@48308200 {
compatible = "ti,am4372-ehrpwm","ti,am33xx-ehrpwm";
+ #pwm-cells = <3>;
reg = <0x48308200 0x80>;
ti,hwmods = "ehrpwm4";
status = "disabled";
@@ -627,6 +648,7 @@
ehrpwm5: ehrpwm@4830a200 {
compatible = "ti,am4372-ehrpwm","ti,am33xx-ehrpwm";
+ #pwm-cells = <3>;
reg = <0x4830a200 0x80>;
ti,hwmods = "ehrpwm5";
status = "disabled";
@@ -689,6 +711,30 @@
<&edma 11>;
dma-names = "tx", "rx";
};
+
+ elm: elm@48080000 {
+ compatible = "ti,am3352-elm";
+ reg = <0x48080000 0x2000>;
+ interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "elm";
+ clocks = <&l4ls_gclk>;
+ clock-names = "fck";
+ status = "disabled";
+ };
+
+ gpmc: gpmc@50000000 {
+ compatible = "ti,am3352-gpmc";
+ ti,hwmods = "gpmc";
+ clocks = <&l3s_gclk>;
+ clock-names = "fck";
+ reg = <0x50000000 0x2000>;
+ interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+ gpmc,num-cs = <7>;
+ gpmc,num-waitpins = <2>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ status = "disabled";
+ };
};
};
diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts
new file mode 100644
index 000000000000..df8798e8bd25
--- /dev/null
+++ b/arch/arm/boot/dts/am437x-gp-evm.dts
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.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.
+ */
+
+/* AM437x GP EVM */
+
+/dts-v1/;
+
+#include "am4372.dtsi"
+#include <dt-bindings/pinctrl/am43xx.h>
+#include <dt-bindings/pwm/pwm.h>
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ model = "TI AM437x GP EVM";
+ compatible = "ti,am437x-gp-evm","ti,am4372","ti,am43";
+
+ vmmcsd_fixed: fixedregulator-sd {
+ compatible = "regulator-fixed";
+ regulator-name = "vmmcsd_fixed";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ enable-active-high;
+ };
+
+ backlight {
+ compatible = "pwm-backlight";
+ pwms = <&ecap0 0 50000 PWM_POLARITY_INVERTED>;
+ brightness-levels = <0 51 53 56 62 75 101 152 255>;
+ default-brightness-level = <8>;
+ };
+
+ matrix_keypad: matrix_keypad@0 {
+ compatible = "gpio-matrix-keypad";
+ debounce-delay-ms = <5>;
+ col-scan-delay-us = <2>;
+
+ row-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH /* Bank3, pin21 */
+ &gpio4 3 GPIO_ACTIVE_HIGH /* Bank4, pin3 */
+ &gpio4 2 GPIO_ACTIVE_HIGH>; /* Bank4, pin2 */
+
+ col-gpios = <&gpio3 19 GPIO_ACTIVE_HIGH /* Bank3, pin19 */
+ &gpio3 20 GPIO_ACTIVE_HIGH>; /* Bank3, pin20 */
+
+ linux,keymap = <0x00000201 /* P1 */
+ 0x00010202 /* P2 */
+ 0x01000067 /* UP */
+ 0x0101006a /* RIGHT */
+ 0x02000069 /* LEFT */
+ 0x0201006c>; /* DOWN */
+ };
+};
+
+&am43xx_pinmux {
+ i2c0_pins: i2c0_pins {
+ pinctrl-single,pins = <
+ 0x188 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0) /* i2c0_sda.i2c0_sda */
+ 0x18c (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0) /* i2c0_scl.i2c0_scl */
+ >;
+ };
+
+ i2c1_pins: i2c1_pins {
+ pinctrl-single,pins = <
+ 0x15c (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE2) /* spi0_cs0.i2c1_scl */
+ 0x158 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE2) /* spi0_d1.i2c1_sda */
+ >;
+ };
+
+ mmc1_pins: pinmux_mmc1_pins {
+ pinctrl-single,pins = <
+ 0x160 (PIN_INPUT | MUX_MODE7) /* spi0_cs1.gpio0_6 */
+ >;
+ };
+
+ ecap0_pins: backlight_pins {
+ pinctrl-single,pins = <
+ 0x164 MUX_MODE0 /* eCAP0_in_PWM0_out.eCAP0_in_PWM0_out MODE0 */
+ >;
+ };
+};
+
+&i2c0 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0_pins>;
+};
+
+&i2c1 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+};
+
+&epwmss0 {
+ status = "okay";
+};
+
+&ecap0 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&ecap0_pins>;
+};
+
+&gpio0 {
+ status = "okay";
+};
+
+&gpio3 {
+ status = "okay";
+};
+
+&gpio4 {
+ status = "okay";
+};
+
+&mmc1 {
+ status = "okay";
+ vmmc-supply = <&vmmcsd_fixed>;
+ bus-width = <4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
+ cd-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
+};
diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts
index fbf9c4c7a94f..167dbc8494de 100644
--- a/arch/arm/boot/dts/am43x-epos-evm.dts
+++ b/arch/arm/boot/dts/am43x-epos-evm.dts
@@ -13,6 +13,7 @@
#include "am4372.dtsi"
#include <dt-bindings/pinctrl/am43xx.h>
#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/pwm/pwm.h>
/ {
model = "TI AM43x EPOS EVM";
@@ -79,6 +80,64 @@
0x18c (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0) /* i2c0_scl.i2c0_scl */
>;
};
+
+ nand_flash_x8: nand_flash_x8 {
+ pinctrl-single,pins = <
+ 0x40 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a0.SELQSPIorNAND/GPIO */
+ 0x0 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* gpmc_ad0.gpmc_ad0 */
+ 0x4 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* gpmc_ad1.gpmc_ad1 */
+ 0x8 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* gpmc_ad2.gpmc_ad2 */
+ 0xc (PIN_INPUT_PULLDOWN | MUX_MODE0) /* gpmc_ad3.gpmc_ad3 */
+ 0x10 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* gpmc_ad4.gpmc_ad4 */
+ 0x14 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* gpmc_ad5.gpmc_ad5 */
+ 0x18 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* gpmc_ad6.gpmc_ad6 */
+ 0x1c (PIN_INPUT_PULLDOWN | MUX_MODE0) /* gpmc_ad7.gpmc_ad7 */
+ 0x70 (PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_wait0.gpmc_wait0 */
+ 0x74 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_wpn.gpmc_wpn */
+ 0x7c (PIN_OUTPUT | MUX_MODE0) /* gpmc_csn0.gpmc_csn0 */
+ 0x90 (PIN_OUTPUT | MUX_MODE0) /* gpmc_advn_ale.gpmc_advn_ale */
+ 0x94 (PIN_OUTPUT | MUX_MODE0) /* gpmc_oen_ren.gpmc_oen_ren */
+ 0x98 (PIN_OUTPUT | MUX_MODE0) /* gpmc_wen.gpmc_wen */
+ 0x9c (PIN_OUTPUT | MUX_MODE0) /* gpmc_be0n_cle.gpmc_be0n_cle */
+ >;
+ };
+
+ ecap0_pins: backlight_pins {
+ pinctrl-single,pins = <
+ 0x164 MUX_MODE0 /* eCAP0_in_PWM0_out.eCAP0_in_PWM0_out MODE0 */
+ >;
+ };
+
+ i2c2_pins: pinmux_i2c2_pins {
+ pinctrl-single,pins = <
+ 0x1c0 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE8) /* i2c2_sda.i2c2_sda */
+ 0x1c4 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE8) /* i2c2_scl.i2c2_scl */
+ >;
+ };
+
+ spi0_pins: pinmux_spi0_pins {
+ pinctrl-single,pins = <
+ 0x150 (PIN_INPUT | MUX_MODE0) /* spi0_clk.spi0_clk */
+ 0x154 (PIN_OUTPUT | MUX_MODE0) /* spi0_d0.spi0_d0 */
+ 0x158 (PIN_INPUT | MUX_MODE0) /* spi0_d1.spi0_d1 */
+ 0x15c (PIN_OUTPUT | MUX_MODE0) /* spi0_cs0.spi0_cs0 */
+ >;
+ };
+
+ spi1_pins: pinmux_spi1_pins {
+ pinctrl-single,pins = <
+ 0x190 (PIN_INPUT | MUX_MODE3) /* mcasp0_aclkx.spi1_clk */
+ 0x194 (PIN_OUTPUT | MUX_MODE3) /* mcasp0_fsx.spi1_d0 */
+ 0x198 (PIN_INPUT | MUX_MODE3) /* mcasp0_axr0.spi1_d1 */
+ 0x19c (PIN_OUTPUT | MUX_MODE3) /* mcasp0_ahclkr.spi1_cs0 */
+ >;
+ };
+
+ mmc1_pins: pinmux_mmc1_pins {
+ pinctrl-single,pins = <
+ 0x160 (PIN_INPUT | MUX_MODE7) /* spi0_cs1.gpio0_6 */
+ >;
+ };
};
matrix_keypad: matrix_keypad@0 {
@@ -113,12 +172,22 @@
0x0203006c /* DOWN */
0x03030069>; /* LEFT */
};
+
+ backlight {
+ compatible = "pwm-backlight";
+ pwms = <&ecap0 0 50000 PWM_POLARITY_INVERTED>;
+ brightness-levels = <0 51 53 56 62 75 101 152 255>;
+ default-brightness-level = <8>;
+ };
};
&mmc1 {
status = "okay";
vmmc-supply = <&vmmcsd_fixed>;
bus-width = <4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
+ cd-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
};
&mac {
@@ -169,6 +238,12 @@
};
};
+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins>;
+ status = "okay";
+};
+
&gpio0 {
status = "okay";
};
@@ -184,3 +259,111 @@
&gpio3 {
status = "okay";
};
+
+&elm {
+ status = "okay";
+};
+
+&gpmc {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&nand_flash_x8>;
+ ranges = <0 0 0x08000000 0x10000000>; /* CS0: NAND */
+ nand@0,0 {
+ reg = <0 0 0>; /* CS0, offset 0 */
+ ti,nand-ecc-opt = "bch8";
+ ti,elm-id = <&elm>;
+ nand-bus-width = <8>;
+ gpmc,device-width = <1>;
+ gpmc,sync-clk-ps = <0>;
+ gpmc,cs-on-ns = <0>;
+ gpmc,cs-rd-off-ns = <40>; /* tCEA + tCHZ + 1 */
+ gpmc,cs-wr-off-ns = <40>;
+ gpmc,adv-on-ns = <0>; /* cs-on-ns */
+ gpmc,adv-rd-off-ns = <25>; /* min( tALH + tALS + 1) */
+ gpmc,adv-wr-off-ns = <25>; /* min( tALH + tALS + 1) */
+ gpmc,we-on-ns = <0>; /* cs-on-ns */
+ gpmc,we-off-ns = <20>; /* we-on-time + tWP + 2 */
+ gpmc,oe-on-ns = <3>; /* cs-on-ns + tRR + 2 */
+ gpmc,oe-off-ns = <30>; /* oe-on-ns + tRP + 2 */
+ gpmc,access-ns = <30>; /* tCEA + 4*/
+ gpmc,rd-cycle-ns = <40>;
+ gpmc,wr-cycle-ns = <40>;
+ gpmc,wait-on-read = "true";
+ gpmc,wait-on-write = "true";
+ gpmc,bus-turnaround-ns = <0>;
+ gpmc,cycle2cycle-delay-ns = <0>;
+ gpmc,clk-activation-ns = <0>;
+ gpmc,wait-monitoring-ns = <0>;
+ gpmc,wr-access-ns = <40>;
+ gpmc,wr-data-mux-bus-ns = <0>;
+ /* MTD partition table */
+ /* All SPL-* partitions are sized to minimal length
+ * which can be independently programmable. For
+ * NAND flash this is equal to size of erase-block */
+ #address-cells = <1>;
+ #size-cells = <1>;
+ partition@0 {
+ label = "NAND.SPL";
+ reg = <0x00000000 0x00040000>;
+ };
+ partition@1 {
+ label = "NAND.SPL.backup1";
+ reg = <0x00040000 0x00040000>;
+ };
+ partition@2 {
+ label = "NAND.SPL.backup2";
+ reg = <0x00080000 0x00040000>;
+ };
+ partition@3 {
+ label = "NAND.SPL.backup3";
+ reg = <0x000C0000 0x00040000>;
+ };
+ partition@4 {
+ label = "NAND.u-boot-spl-os";
+ reg = <0x00100000 0x00080000>;
+ };
+ partition@5 {
+ label = "NAND.u-boot";
+ reg = <0x00180000 0x00100000>;
+ };
+ partition@6 {
+ label = "NAND.u-boot-env";
+ reg = <0x00280000 0x00040000>;
+ };
+ partition@7 {
+ label = "NAND.u-boot-env.backup1";
+ reg = <0x002C0000 0x00040000>;
+ };
+ partition@8 {
+ label = "NAND.kernel";
+ reg = <0x00300000 0x00700000>;
+ };
+ partition@9 {
+ label = "NAND.file-system";
+ reg = <0x00800000 0x1F600000>;
+ };
+ };
+};
+
+&epwmss0 {
+ status = "okay";
+};
+
+&ecap0 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&ecap0_pins>;
+};
+
+&spi0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi0_pins>;
+ status = "okay";
+};
+
+&spi1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi1_pins>;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/armada-370-db.dts b/arch/arm/boot/dts/armada-370-db.dts
index 08a56bcfc724..82f238a9063f 100644
--- a/arch/arm/boot/dts/armada-370-db.dts
+++ b/arch/arm/boot/dts/armada-370-db.dts
@@ -64,6 +64,22 @@
phy-mode = "rgmii-id";
};
+ i2c@11000 {
+ pinctrl-0 = <&i2c0_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+ audio_codec: audio-codec@4a {
+ compatible = "cirrus,cs42l51";
+ reg = <0x4a>;
+ };
+ };
+
+ audio-controller@30000 {
+ pinctrl-0 = <&i2s_pins2>;
+ pinctrl-names = "default";
+ status = "okay";
+ };
+
mvsdio@d4000 {
pinctrl-0 = <&sdio_pins1>;
pinctrl-names = "default";
@@ -80,6 +96,30 @@
broken-cd;
};
+ pinctrl {
+ /*
+ * These pins might be muxed as I2S by
+ * the bootloader, but it conflicts
+ * with the real I2S pins that are
+ * muxed using i2s_pins. We must mux
+ * those pins to a function other than
+ * I2S.
+ */
+ pinctrl-0 = <&hog_pins1 &hog_pins2>;
+ pinctrl-names = "default";
+
+ hog_pins1: hog-pins1 {
+ marvell,pins = "mpp6", "mpp8", "mpp10",
+ "mpp12", "mpp13";
+ marvell,function = "gpio";
+ };
+
+ hog_pins2: hog-pins2 {
+ marvell,pins = "mpp5", "mpp7", "mpp9";
+ marvell,function = "gpo";
+ };
+ };
+
usb@50000 {
status = "okay";
};
@@ -112,10 +152,26 @@
/* Port 0, Lane 0 */
status = "okay";
};
+
pcie@2,0 {
/* Port 1, Lane 0 */
status = "okay";
};
};
};
+
+ sound {
+ compatible = "marvell,a370db-audio";
+ marvell,audio-controller = <&audio_controller>;
+ marvell,audio-codec = <&audio_codec &spdif_out &spdif_in>;
+ status = "okay";
+ };
+
+ spdif_out: spdif-out {
+ compatible = "linux,spdif-dit";
+ };
+
+ spdif_in: spdif-in {
+ compatible = "linux,spdif-dir";
+ };
};
diff --git a/arch/arm/boot/dts/armada-370-mirabox.dts b/arch/arm/boot/dts/armada-370-mirabox.dts
index 944e8785b308..2354fe023ee0 100644
--- a/arch/arm/boot/dts/armada-370-mirabox.dts
+++ b/arch/arm/boot/dts/armada-370-mirabox.dts
@@ -9,6 +9,7 @@
*/
/dts-v1/;
+#include <dt-bindings/gpio/gpio.h>
#include "armada-370.dtsi"
/ {
@@ -73,19 +74,19 @@
green_pwr_led {
label = "mirabox:green:pwr";
- gpios = <&gpio1 31 1>;
+ gpios = <&gpio1 31 GPIO_ACTIVE_LOW>;
default-state = "keep";
};
blue_stat_led {
label = "mirabox:blue:stat";
- gpios = <&gpio2 0 1>;
+ gpios = <&gpio2 0 GPIO_ACTIVE_LOW>;
default-state = "off";
};
green_stat_led {
label = "mirabox:green:stat";
- gpios = <&gpio2 1 1>;
+ gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
default-state = "off";
};
};
diff --git a/arch/arm/boot/dts/armada-370-rd.dts b/arch/arm/boot/dts/armada-370-rd.dts
index abbb807459d2..3e2c857d6000 100644
--- a/arch/arm/boot/dts/armada-370-rd.dts
+++ b/arch/arm/boot/dts/armada-370-rd.dts
@@ -12,6 +12,8 @@
*/
/dts-v1/;
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/gpio/gpio.h>
#include "armada-370.dtsi"
/ {
@@ -100,8 +102,8 @@
#size-cells = <0>;
button@1 {
label = "Software Button";
- linux,code = <116>;
- gpios = <&gpio0 6 1>;
+ linux,code = <KEY_POWER>;
+ gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
};
};
diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi
index 74b5964430ac..bbb40f62037d 100644
--- a/arch/arm/boot/dts/armada-370-xp.dtsi
+++ b/arch/arm/boot/dts/armada-370-xp.dtsi
@@ -44,8 +44,8 @@
#size-cells = <1>;
controller = <&mbusc>;
interrupt-parent = <&mpic>;
- pcie-mem-aperture = <0xe0000000 0x8000000>;
- pcie-io-aperture = <0xe8000000 0x100000>;
+ pcie-mem-aperture = <0xf8000000 0x7e00000>;
+ pcie-io-aperture = <0xffe00000 0x100000>;
devbus-bootcs {
compatible = "marvell,mvebu-devbus";
@@ -199,6 +199,10 @@
interrupts = <37>, <38>, <39>, <40>, <5>, <6>;
};
+ watchdog@20300 {
+ reg = <0x20300 0x34>, <0x20704 0x4>;
+ };
+
usb@50000 {
compatible = "marvell,orion-ehci";
reg = <0x50000 0x500>;
diff --git a/arch/arm/boot/dts/armada-370.dtsi b/arch/arm/boot/dts/armada-370.dtsi
index 0d8530c98cf5..af1f11e9e5a0 100644
--- a/arch/arm/boot/dts/armada-370.dtsi
+++ b/arch/arm/boot/dts/armada-370.dtsi
@@ -132,6 +132,25 @@
"mpp51", "mpp52", "mpp53";
marvell,function = "sd0";
};
+
+ i2c0_pins: i2c0-pins {
+ marvell,pins = "mpp2", "mpp3";
+ marvell,function = "i2c0";
+ };
+
+ i2s_pins1: i2s-pins1 {
+ marvell,pins = "mpp5", "mpp6", "mpp7",
+ "mpp8", "mpp9", "mpp10",
+ "mpp12", "mpp13";
+ marvell,function = "audio";
+ };
+
+ i2s_pins2: i2s-pins2 {
+ marvell,pins = "mpp49", "mpp47", "mpp50",
+ "mpp59", "mpp57", "mpp61",
+ "mpp62", "mpp60", "mpp58";
+ marvell,function = "audio";
+ };
};
gpio0: gpio@18100 {
@@ -196,6 +215,20 @@
clocks = <&coreclk 2>;
};
+ watchdog@20300 {
+ compatible = "marvell,armada-370-wdt";
+ clocks = <&coreclk 2>;
+ };
+
+ audio_controller: audio-controller@30000 {
+ compatible = "marvell,armada370-audio";
+ reg = <0x30000 0x4000>;
+ interrupts = <93>;
+ clocks = <&gateclk 0>;
+ clock-names = "internal";
+ status = "disabled";
+ };
+
usb@50000 {
clocks = <&coreclk 0>;
};
diff --git a/arch/arm/boot/dts/armada-375-db.dts b/arch/arm/boot/dts/armada-375-db.dts
new file mode 100644
index 000000000000..9378d3136b41
--- /dev/null
+++ b/arch/arm/boot/dts/armada-375-db.dts
@@ -0,0 +1,130 @@
+/*
+ * Device Tree file for Marvell Armada 375 evaluation board
+ * (DB-88F6720)
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni@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.
+ */
+
+/dts-v1/;
+#include <dt-bindings/gpio/gpio.h>
+#include "armada-375.dtsi"
+
+/ {
+ model = "Marvell Armada 375 Development Board";
+ compatible = "marvell,a375-db", "marvell,armada375";
+
+ chosen {
+ bootargs = "console=ttyS0,115200 earlyprintk";
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x40000000>; /* 1 GB */
+ };
+
+ soc {
+ ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
+ MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000>;
+
+ internal-regs {
+ spi@10600 {
+ pinctrl-0 = <&spi0_pins>;
+ pinctrl-names = "default";
+ /*
+ * SPI conflicts with NAND, so we disable it
+ * here, and select NAND as the enabled device
+ * by default.
+ */
+ status = "disabled";
+
+ spi-flash@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "n25q128a13";
+ reg = <0>; /* Chip select 0 */
+ spi-max-frequency = <108000000>;
+ };
+ };
+
+ i2c@11000 {
+ status = "okay";
+ clock-frequency = <100000>;
+ pinctrl-0 = <&i2c0_pins>;
+ pinctrl-names = "default";
+ };
+
+ i2c@11100 {
+ status = "okay";
+ clock-frequency = <100000>;
+ pinctrl-0 = <&i2c1_pins>;
+ pinctrl-names = "default";
+ };
+
+ serial@12000 {
+ clock-frequency = <200000000>;
+ status = "okay";
+ };
+
+ pinctrl {
+ sdio_st_pins: sdio-st-pins {
+ marvell,pins = "mpp44", "mpp45";
+ marvell,function = "gpio";
+ };
+ };
+
+ nand: nand@d0000 {
+ pinctrl-0 = <&nand_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+ num-cs = <1>;
+ marvell,nand-keep-config;
+ marvell,nand-enable-arbiter;
+ nand-on-flash-bbt;
+
+ partition@0 {
+ label = "U-Boot";
+ reg = <0 0x800000>;
+ };
+ partition@800000 {
+ label = "Linux";
+ reg = <0x800000 0x800000>;
+ };
+ partition@1000000 {
+ label = "Filesystem";
+ reg = <0x1000000 0x3f000000>;
+ };
+ };
+
+ mvsdio@d4000 {
+ pinctrl-0 = <&sdio_pins &sdio_st_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+ cd-gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>;
+ wp-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ pcie-controller {
+ status = "okay";
+ /*
+ * The two PCIe units are accessible through
+ * standard PCIe slots on the board.
+ */
+ pcie@1,0 {
+ /* Port 0, Lane 0 */
+ status = "okay";
+ };
+ pcie@2,0 {
+ /* Port 1, Lane 0 */
+ status = "okay";
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi
new file mode 100644
index 000000000000..3877693fb2d8
--- /dev/null
+++ b/arch/arm/boot/dts/armada-375.dtsi
@@ -0,0 +1,464 @@
+/*
+ * Device Tree Include file for Marvell Armada 375 family SoC
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni@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 "skeleton.dtsi"
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16))
+
+/ {
+ model = "Marvell Armada 375 family SoC";
+ compatible = "marvell,armada375";
+
+ aliases {
+ gpio0 = &gpio0;
+ gpio1 = &gpio1;
+ gpio2 = &gpio2;
+ };
+
+ clocks {
+ /* 2 GHz fixed main PLL */
+ mainpll: mainpll {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <2000000000>;
+ };
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <0>;
+ };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <1>;
+ };
+ };
+
+ soc {
+ compatible = "marvell,armada375-mbus", "marvell,armada370-mbus", "simple-bus";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ controller = <&mbusc>;
+ interrupt-parent = <&gic>;
+ pcie-mem-aperture = <0xe0000000 0x8000000>;
+ pcie-io-aperture = <0xe8000000 0x100000>;
+
+ bootrom {
+ compatible = "marvell,bootrom";
+ reg = <MBUS_ID(0x01, 0x1d) 0 0x100000>;
+ };
+
+ devbus-bootcs {
+ compatible = "marvell,mvebu-devbus";
+ reg = <MBUS_ID(0xf0, 0x01) 0x10400 0x8>;
+ ranges = <0 MBUS_ID(0x01, 0x2f) 0 0xffffffff>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs0 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <MBUS_ID(0xf0, 0x01) 0x10408 0x8>;
+ ranges = <0 MBUS_ID(0x01, 0x3e) 0 0xffffffff>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs1 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <MBUS_ID(0xf0, 0x01) 0x10410 0x8>;
+ ranges = <0 MBUS_ID(0x01, 0x3d) 0 0xffffffff>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs2 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <MBUS_ID(0xf0, 0x01) 0x10418 0x8>;
+ ranges = <0 MBUS_ID(0x01, 0x3b) 0 0xffffffff>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs3 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <MBUS_ID(0xf0, 0x01) 0x10420 0x8>;
+ ranges = <0 MBUS_ID(0x01, 0x37) 0 0xffffffff>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ internal-regs {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>;
+
+ L2: cache-controller@8000 {
+ compatible = "arm,pl310-cache";
+ reg = <0x8000 0x1000>;
+ cache-unified;
+ cache-level = <2>;
+ };
+
+ timer@c600 {
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = <0xc600 0x20>;
+ interrupts = <GIC_PPI 13 (IRQ_TYPE_EDGE_RISING | GIC_CPU_MASK_SIMPLE(2))>;
+ clocks = <&coreclk 2>;
+ };
+
+ gic: interrupt-controller@d000 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #size-cells = <0>;
+ interrupt-controller;
+ reg = <0xd000 0x1000>,
+ <0xc100 0x100>;
+ };
+
+ spi0: spi@10600 {
+ compatible = "marvell,orion-spi";
+ reg = <0x10600 0x50>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <0>;
+ interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ spi1: spi@10680 {
+ compatible = "marvell,orion-spi";
+ reg = <0x10680 0x50>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <1>;
+ interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ i2c0: i2c@11000 {
+ compatible = "marvell,mv64xxx-i2c";
+ reg = <0x11000 0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+ timeout-ms = <1000>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@11100 {
+ compatible = "marvell,mv64xxx-i2c";
+ reg = <0x11100 0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
+ timeout-ms = <1000>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ serial@12000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x12000 0x100>;
+ reg-shift = <2>;
+ interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+ reg-io-width = <1>;
+ status = "disabled";
+ };
+
+ serial@12100 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x12100 0x100>;
+ reg-shift = <2>;
+ interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
+ reg-io-width = <1>;
+ status = "disabled";
+ };
+
+ pinctrl {
+ compatible = "marvell,mv88f6720-pinctrl";
+ reg = <0x18000 0x24>;
+
+ i2c0_pins: i2c0-pins {
+ marvell,pins = "mpp14", "mpp15";
+ marvell,function = "i2c0";
+ };
+
+ i2c1_pins: i2c1-pins {
+ marvell,pins = "mpp61", "mpp62";
+ marvell,function = "i2c1";
+ };
+
+ nand_pins: nand-pins {
+ marvell,pins = "mpp0", "mpp1", "mpp2",
+ "mpp3", "mpp4", "mpp5",
+ "mpp6", "mpp7", "mpp8",
+ "mpp9", "mpp10", "mpp11",
+ "mpp12", "mpp13";
+ marvell,function = "nand";
+ };
+
+ sdio_pins: sdio-pins {
+ marvell,pins = "mpp24", "mpp25", "mpp26",
+ "mpp27", "mpp28", "mpp29";
+ marvell,function = "sd";
+ };
+
+ spi0_pins: spi0-pins {
+ marvell,pins = "mpp0", "mpp1", "mpp4",
+ "mpp5", "mpp8", "mpp9";
+ marvell,function = "spi0";
+ };
+ };
+
+ gpio0: gpio@18100 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18100 0x40>;
+ ngpios = <32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ gpio1: gpio@18140 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18140 0x40>;
+ ngpios = <32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ gpio2: gpio@18180 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18180 0x40>;
+ ngpios = <3>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ system-controller@18200 {
+ compatible = "marvell,armada-375-system-controller";
+ reg = <0x18200 0x100>;
+ };
+
+ gateclk: clock-gating-control@18220 {
+ compatible = "marvell,armada-375-gating-clock";
+ reg = <0x18220 0x4>;
+ clocks = <&coreclk 0>;
+ #clock-cells = <1>;
+ };
+
+ mbusc: mbus-controller@20000 {
+ compatible = "marvell,mbus-controller";
+ reg = <0x20000 0x100>, <0x20180 0x20>;
+ };
+
+ mpic: interrupt-controller@20000 {
+ compatible = "marvell,mpic";
+ reg = <0x20a00 0x2d0>, <0x21070 0x58>;
+ #interrupt-cells = <1>;
+ #size-cells = <1>;
+ interrupt-controller;
+ msi-controller;
+ interrupts = <GIC_PPI 15 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ timer@20300 {
+ compatible = "marvell,armada-375-timer", "marvell,armada-370-timer";
+ reg = <0x20300 0x30>, <0x21040 0x30>;
+ interrupts-extended = <&gic GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
+ <&gic GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
+ <&gic GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
+ <&gic GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
+ <&mpic 5>,
+ <&mpic 6>;
+ clocks = <&coreclk 0>;
+ };
+
+ xor@60800 {
+ compatible = "marvell,orion-xor";
+ reg = <0x60800 0x100
+ 0x60A00 0x100>;
+ clocks = <&gateclk 22>;
+ status = "okay";
+
+ xor00 {
+ interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
+ dmacap,memcpy;
+ dmacap,xor;
+ };
+ xor01 {
+ interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
+ dmacap,memcpy;
+ dmacap,xor;
+ dmacap,memset;
+ };
+ };
+
+ xor@60900 {
+ compatible = "marvell,orion-xor";
+ reg = <0x60900 0x100
+ 0x60b00 0x100>;
+ clocks = <&gateclk 23>;
+ status = "okay";
+
+ xor10 {
+ interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+ dmacap,memcpy;
+ dmacap,xor;
+ };
+ xor11 {
+ interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
+ dmacap,memcpy;
+ dmacap,xor;
+ dmacap,memset;
+ };
+ };
+
+ sata@a0000 {
+ compatible = "marvell,orion-sata";
+ reg = <0xa0000 0x5000>;
+ interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gateclk 14>, <&gateclk 20>;
+ clock-names = "0", "1";
+ status = "disabled";
+ };
+
+ nand@d0000 {
+ compatible = "marvell,armada370-nand";
+ reg = <0xd0000 0x54>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gateclk 11>;
+ status = "disabled";
+ };
+
+ mvsdio@d4000 {
+ compatible = "marvell,orion-sdio";
+ reg = <0xd4000 0x200>;
+ interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gateclk 17>;
+ bus-width = <4>;
+ cap-sdio-irq;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ status = "disabled";
+ };
+
+ coreclk: mvebu-sar@e8204 {
+ compatible = "marvell,armada-375-core-clock";
+ reg = <0xe8204 0x04>;
+ #clock-cells = <1>;
+ };
+
+ coredivclk: corediv-clock@e8250 {
+ compatible = "marvell,armada-375-corediv-clock";
+ reg = <0xe8250 0xc>;
+ #clock-cells = <1>;
+ clocks = <&mainpll>;
+ clock-output-names = "nand";
+ };
+ };
+
+ pcie-controller {
+ compatible = "marvell,armada-370-pcie";
+ status = "disabled";
+ device_type = "pci";
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ msi-parent = <&mpic>;
+ bus-range = <0x00 0xff>;
+
+ ranges =
+ <0x82000000 0 0x40000 MBUS_ID(0xf0, 0x01) 0x40000 0 0x00002000
+ 0x82000000 0 0x44000 MBUS_ID(0xf0, 0x01) 0x44000 0 0x00002000
+ 0x82000000 0x1 0 MBUS_ID(0x04, 0xe8) 0 1 0 /* Port 0 MEM */
+ 0x81000000 0x1 0 MBUS_ID(0x04, 0xe0) 0 1 0 /* Port 0 IO */
+ 0x82000000 0x2 0 MBUS_ID(0x04, 0xd8) 0 1 0 /* Port 1 MEM */
+ 0x81000000 0x2 0 MBUS_ID(0x04, 0xd0) 0 1 0 /* Port 1 IO */>;
+
+ pcie@1,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x40000 0 0x2000>;
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
+ };
+
+ pcie@2,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x44000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <1>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
+ };
+
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/armada-380.dtsi b/arch/arm/boot/dts/armada-380.dtsi
new file mode 100644
index 000000000000..068031f0f263
--- /dev/null
+++ b/arch/arm/boot/dts/armada-380.dtsi
@@ -0,0 +1,117 @@
+/*
+ * Device Tree Include file for Marvell Armada 380 SoC.
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Lior Amsalem <alior@marvell.com>
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni@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 "armada-38x.dtsi"
+
+/ {
+ model = "Marvell Armada 380 family SoC";
+ compatible = "marvell,armada380", "marvell,armada38x";
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <0>;
+ };
+ };
+
+ soc {
+ internal-regs {
+ pinctrl {
+ compatible = "marvell,mv88f6810-pinctrl";
+ reg = <0x18000 0x20>;
+ };
+ };
+
+ pcie-controller {
+ compatible = "marvell,armada-370-pcie";
+ status = "disabled";
+ device_type = "pci";
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ msi-parent = <&mpic>;
+ bus-range = <0x00 0xff>;
+
+ ranges =
+ <0x82000000 0 0x80000 MBUS_ID(0xf0, 0x01) 0x80000 0 0x00002000
+ 0x82000000 0 0x40000 MBUS_ID(0xf0, 0x01) 0x40000 0 0x00002000
+ 0x82000000 0 0x44000 MBUS_ID(0xf0, 0x01) 0x44000 0 0x00002000
+ 0x82000000 0 0x48000 MBUS_ID(0xf0, 0x01) 0x48000 0 0x00002000
+ 0x82000000 0x1 0 MBUS_ID(0x08, 0xe8) 0 1 0 /* Port 0 MEM */
+ 0x81000000 0x1 0 MBUS_ID(0x08, 0xe0) 0 1 0 /* Port 0 IO */
+ 0x82000000 0x2 0 MBUS_ID(0x04, 0xe8) 0 1 0 /* Port 1 MEM */
+ 0x81000000 0x2 0 MBUS_ID(0x04, 0xe0) 0 1 0 /* Port 1 IO */
+ 0x82000000 0x3 0 MBUS_ID(0x04, 0xd8) 0 1 0 /* Port 2 MEM */
+ 0x81000000 0x3 0 MBUS_ID(0x04, 0xd0) 0 1 0 /* Port 2 IO */>;
+
+ /* x1 port */
+ pcie@1,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x80000 0 0x2000>;
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 8>;
+ status = "disabled";
+ };
+
+ /* x1 port */
+ pcie@2,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x40000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
+ };
+
+ /* x1 port */
+ pcie@3,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x44000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
+ marvell,pcie-port = <2>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/armada-385-db.dts b/arch/arm/boot/dts/armada-385-db.dts
new file mode 100644
index 000000000000..6828d77696a6
--- /dev/null
+++ b/arch/arm/boot/dts/armada-385-db.dts
@@ -0,0 +1,122 @@
+/*
+ * Device Tree file for Marvell Armada 385 evaluation board
+ * (DB-88F6820)
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Thomas Petazzoni <thomas.petazzoni@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.
+ */
+
+/dts-v1/;
+#include "armada-385.dtsi"
+
+/ {
+ model = "Marvell Armada 385 Development Board";
+ compatible = "marvell,a385-db", "marvell,armada385", "marvell,armada38x";
+
+ chosen {
+ bootargs = "console=ttyS0,115200 earlyprintk";
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x10000000>; /* 256 MB */
+ };
+
+ soc {
+ ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
+ MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000>;
+
+ internal-regs {
+ spi@10600 {
+ status = "okay";
+
+ spi-flash@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "w25q32";
+ reg = <0>; /* Chip select 0 */
+ spi-max-frequency = <108000000>;
+ };
+ };
+
+ i2c@11000 {
+ status = "okay";
+ clock-frequency = <100000>;
+ };
+
+ i2c@11100 {
+ status = "okay";
+ clock-frequency = <100000>;
+ };
+
+ serial@12000 {
+ clock-frequency = <200000000>;
+ status = "okay";
+ };
+
+ ethernet@30000 {
+ status = "okay";
+ phy = <&phy1>;
+ phy-mode = "rgmii-id";
+ };
+
+ ethernet@70000 {
+ status = "okay";
+ phy = <&phy0>;
+ phy-mode = "rgmii-id";
+ };
+
+ mdio {
+ phy0: ethernet-phy@0 {
+ reg = <0>;
+ };
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+ };
+
+ flash@d0000 {
+ status = "okay";
+ num-cs = <1>;
+ marvell,nand-keep-config;
+ marvell,nand-enable-arbiter;
+ nand-on-flash-bbt;
+
+ partition@0 {
+ label = "U-Boot";
+ reg = <0 0x800000>;
+ };
+ partition@800000 {
+ label = "Linux";
+ reg = <0x800000 0x800000>;
+ };
+ partition@1000000 {
+ label = "Filesystem";
+ reg = <0x1000000 0x3f000000>;
+ };
+ };
+ };
+
+ pcie-controller {
+ status = "okay";
+ /*
+ * The two PCIe units are accessible through
+ * standard PCIe slots on the board.
+ */
+ pcie@1,0 {
+ /* Port 0, Lane 0 */
+ status = "okay";
+ };
+ pcie@2,0 {
+ /* Port 1, Lane 0 */
+ status = "okay";
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/armada-385-rd.dts b/arch/arm/boot/dts/armada-385-rd.dts
new file mode 100644
index 000000000000..45250c88814b
--- /dev/null
+++ b/arch/arm/boot/dts/armada-385-rd.dts
@@ -0,0 +1,94 @@
+/*
+ * Device Tree file for Marvell Armada 385 Reference Design board
+ * (RD-88F6820-AP)
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni@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.
+ */
+
+/dts-v1/;
+#include "armada-385.dtsi"
+
+/ {
+ model = "Marvell Armada 385 Reference Design";
+ compatible = "marvell,a385-rd", "marvell,armada385", "marvell,armada38x";
+
+ chosen {
+ bootargs = "console=ttyS0,115200 earlyprintk";
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x10000000>; /* 256 MB */
+ };
+
+ soc {
+ ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
+ MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000>;
+
+ internal-regs {
+ spi@10600 {
+ status = "okay";
+
+ spi-flash@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,m25p128";
+ reg = <0>; /* Chip select 0 */
+ spi-max-frequency = <108000000>;
+ };
+ };
+
+ i2c@11000 {
+ status = "okay";
+ clock-frequency = <100000>;
+ };
+
+ serial@12000 {
+ clock-frequency = <200000000>;
+ status = "okay";
+ };
+
+ ethernet@30000 {
+ status = "okay";
+ phy = <&phy0>;
+ phy-mode = "rgmii-id";
+ };
+
+ ethernet@70000 {
+ status = "okay";
+ phy = <&phy1>;
+ phy-mode = "rgmii-id";
+ };
+
+
+ mdio {
+ phy0: ethernet-phy@0 {
+ reg = <0>;
+ };
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+ };
+ };
+
+ pcie-controller {
+ status = "okay";
+ /*
+ * One PCIe units is accessible through
+ * standard PCIe slot on the board.
+ */
+ pcie@1,0 {
+ /* Port 0, Lane 0 */
+ status = "okay";
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/armada-385.dtsi b/arch/arm/boot/dts/armada-385.dtsi
new file mode 100644
index 000000000000..e2919f02e1d4
--- /dev/null
+++ b/arch/arm/boot/dts/armada-385.dtsi
@@ -0,0 +1,149 @@
+/*
+ * Device Tree Include file for Marvell Armada 385 SoC.
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Lior Amsalem <alior@marvell.com>
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni@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 "armada-38x.dtsi"
+
+/ {
+ model = "Marvell Armada 385 family SoC";
+ compatible = "marvell,armada385", "marvell,armada38x";
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <0>;
+ };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <1>;
+ };
+ };
+
+ soc {
+ internal-regs {
+ pinctrl {
+ compatible = "marvell,mv88f6820-pinctrl";
+ reg = <0x18000 0x20>;
+ };
+ };
+
+ pcie-controller {
+ compatible = "marvell,armada-370-pcie";
+ status = "disabled";
+ device_type = "pci";
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ msi-parent = <&mpic>;
+ bus-range = <0x00 0xff>;
+
+ ranges =
+ <0x82000000 0 0x80000 MBUS_ID(0xf0, 0x01) 0x80000 0 0x00002000
+ 0x82000000 0 0x40000 MBUS_ID(0xf0, 0x01) 0x40000 0 0x00002000
+ 0x82000000 0 0x44000 MBUS_ID(0xf0, 0x01) 0x44000 0 0x00002000
+ 0x82000000 0 0x48000 MBUS_ID(0xf0, 0x01) 0x48000 0 0x00002000
+ 0x82000000 0x1 0 MBUS_ID(0x08, 0xe8) 0 1 0 /* Port 0 MEM */
+ 0x81000000 0x1 0 MBUS_ID(0x08, 0xe0) 0 1 0 /* Port 0 IO */
+ 0x82000000 0x2 0 MBUS_ID(0x04, 0xe8) 0 1 0 /* Port 1 MEM */
+ 0x81000000 0x2 0 MBUS_ID(0x04, 0xe0) 0 1 0 /* Port 1 IO */
+ 0x82000000 0x3 0 MBUS_ID(0x04, 0xd8) 0 1 0 /* Port 2 MEM */
+ 0x81000000 0x3 0 MBUS_ID(0x04, 0xd0) 0 1 0 /* Port 2 IO */
+ 0x82000000 0x4 0 MBUS_ID(0x04, 0xb8) 0 1 0 /* Port 3 MEM */
+ 0x81000000 0x4 0 MBUS_ID(0x04, 0xb0) 0 1 0 /* Port 3 IO */>;
+
+ /*
+ * This port can be either x4 or x1. When
+ * configured in x4 by the bootloader, then
+ * pcie@4,0 is not available.
+ */
+ pcie@1,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x80000 0 0x2000>;
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 8>;
+ status = "disabled";
+ };
+
+ /* x1 port */
+ pcie@2,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x40000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
+ };
+
+ /* x1 port */
+ pcie@3,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x44000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
+ marvell,pcie-port = <2>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
+ };
+
+ /*
+ * x1 port only available when pcie@1,0 is
+ * configured as a x1 port
+ */
+ pcie@4,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x48000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0
+ 0x81000000 0 0 0x81000000 0x4 0 1 0>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
+ marvell,pcie-port = <3>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 7>;
+ status = "disabled";
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi
new file mode 100644
index 000000000000..a064f59da02d
--- /dev/null
+++ b/arch/arm/boot/dts/armada-38x.dtsi
@@ -0,0 +1,376 @@
+/*
+ * Device Tree Include file for Marvell Armada 38x family of SoCs.
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Lior Amsalem <alior@marvell.com>
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni@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 "skeleton.dtsi"
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16))
+
+/ {
+ model = "Marvell Armada 38x family SoC";
+ compatible = "marvell,armada38x";
+
+ aliases {
+ gpio0 = &gpio0;
+ gpio1 = &gpio1;
+ eth0 = &eth0;
+ eth1 = &eth1;
+ eth2 = &eth2;
+ };
+
+ soc {
+ compatible = "marvell,armada380-mbus", "marvell,armada370-mbus",
+ "simple-bus";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ controller = <&mbusc>;
+ interrupt-parent = <&gic>;
+ pcie-mem-aperture = <0xe0000000 0x8000000>;
+ pcie-io-aperture = <0xe8000000 0x100000>;
+
+ bootrom {
+ compatible = "marvell,bootrom";
+ reg = <MBUS_ID(0x01, 0x1d) 0 0x200000>;
+ };
+
+ devbus-bootcs {
+ compatible = "marvell,mvebu-devbus";
+ reg = <MBUS_ID(0xf0, 0x01) 0x10400 0x8>;
+ ranges = <0 MBUS_ID(0x01, 0x2f) 0 0xffffffff>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs0 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <MBUS_ID(0xf0, 0x01) 0x10408 0x8>;
+ ranges = <0 MBUS_ID(0x01, 0x3e) 0 0xffffffff>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs1 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <MBUS_ID(0xf0, 0x01) 0x10410 0x8>;
+ ranges = <0 MBUS_ID(0x01, 0x3d) 0 0xffffffff>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs2 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <MBUS_ID(0xf0, 0x01) 0x10418 0x8>;
+ ranges = <0 MBUS_ID(0x01, 0x3b) 0 0xffffffff>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ devbus-cs3 {
+ compatible = "marvell,mvebu-devbus";
+ reg = <MBUS_ID(0xf0, 0x01) 0x10420 0x8>;
+ ranges = <0 MBUS_ID(0x01, 0x37) 0 0xffffffff>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ internal-regs {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>;
+
+ L2: cache-controller@8000 {
+ compatible = "arm,pl310-cache";
+ reg = <0x8000 0x1000>;
+ cache-unified;
+ cache-level = <2>;
+ };
+
+ timer@c600 {
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = <0xc600 0x20>;
+ interrupts = <GIC_PPI 13 (IRQ_TYPE_EDGE_RISING | GIC_CPU_MASK_SIMPLE(2))>;
+ clocks = <&coreclk 2>;
+ };
+
+ gic: interrupt-controller@d000 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #size-cells = <0>;
+ interrupt-controller;
+ reg = <0xd000 0x1000>,
+ <0xc100 0x100>;
+ };
+
+ spi0: spi@10600 {
+ compatible = "marvell,orion-spi";
+ reg = <0x10600 0x50>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <0>;
+ interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ spi1: spi@10680 {
+ compatible = "marvell,orion-spi";
+ reg = <0x10680 0x50>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <1>;
+ interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ i2c0: i2c@11000 {
+ compatible = "marvell,mv64xxx-i2c";
+ reg = <0x11000 0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+ timeout-ms = <1000>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@11100 {
+ compatible = "marvell,mv64xxx-i2c";
+ reg = <0x11100 0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
+ timeout-ms = <1000>;
+ clocks = <&coreclk 0>;
+ status = "disabled";
+ };
+
+ serial@12000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x12000 0x100>;
+ reg-shift = <2>;
+ interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+ reg-io-width = <1>;
+ status = "disabled";
+ };
+
+ serial@12100 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x12100 0x100>;
+ reg-shift = <2>;
+ interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
+ reg-io-width = <1>;
+ status = "disabled";
+ };
+
+ pinctrl {
+ compatible = "marvell,mv88f6820-pinctrl";
+ reg = <0x18000 0x20>;
+ };
+
+ gpio0: gpio@18100 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18100 0x40>;
+ ngpios = <32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ gpio1: gpio@18140 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18140 0x40>;
+ ngpios = <28>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ system-controller@18200 {
+ compatible = "marvell,armada-380-system-controller",
+ "marvell,armada-370-xp-system-controller";
+ reg = <0x18200 0x100>;
+ };
+
+ gateclk: clock-gating-control@18220 {
+ compatible = "marvell,armada-380-gating-clock";
+ reg = <0x18220 0x4>;
+ clocks = <&coreclk 0>;
+ #clock-cells = <1>;
+ };
+
+ coreclk: mvebu-sar@18600 {
+ compatible = "marvell,armada-380-core-clock";
+ reg = <0x18600 0x04>;
+ #clock-cells = <1>;
+ };
+
+ mbusc: mbus-controller@20000 {
+ compatible = "marvell,mbus-controller";
+ reg = <0x20000 0x100>, <0x20180 0x20>;
+ };
+
+ mpic: interrupt-controller@20000 {
+ compatible = "marvell,mpic";
+ reg = <0x20a00 0x2d0>, <0x21070 0x58>;
+ #interrupt-cells = <1>;
+ #size-cells = <1>;
+ interrupt-controller;
+ msi-controller;
+ interrupts = <GIC_PPI 15 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ timer@20300 {
+ compatible = "marvell,armada-380-timer",
+ "marvell,armada-xp-timer";
+ reg = <0x20300 0x30>, <0x21040 0x30>;
+ interrupts-extended = <&gic GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
+ <&gic GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
+ <&gic GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
+ <&gic GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
+ <&mpic 5>,
+ <&mpic 6>;
+ clocks = <&coreclk 2>, <&refclk>;
+ clock-names = "nbclk", "fixed";
+ };
+
+ eth1: ethernet@30000 {
+ compatible = "marvell,armada-370-neta";
+ reg = <0x30000 0x4000>;
+ interrupts-extended = <&mpic 10>;
+ clocks = <&gateclk 3>;
+ status = "disabled";
+ };
+
+ eth2: ethernet@34000 {
+ compatible = "marvell,armada-370-neta";
+ reg = <0x34000 0x4000>;
+ interrupts-extended = <&mpic 12>;
+ clocks = <&gateclk 2>;
+ status = "disabled";
+ };
+
+ xor@60800 {
+ compatible = "marvell,orion-xor";
+ reg = <0x60800 0x100
+ 0x60a00 0x100>;
+ clocks = <&gateclk 22>;
+ status = "okay";
+
+ xor00 {
+ interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
+ dmacap,memcpy;
+ dmacap,xor;
+ };
+ xor01 {
+ interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
+ dmacap,memcpy;
+ dmacap,xor;
+ dmacap,memset;
+ };
+ };
+
+ xor@60900 {
+ compatible = "marvell,orion-xor";
+ reg = <0x60900 0x100
+ 0x60b00 0x100>;
+ clocks = <&gateclk 28>;
+ status = "okay";
+
+ xor10 {
+ interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+ dmacap,memcpy;
+ dmacap,xor;
+ };
+ xor11 {
+ interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
+ dmacap,memcpy;
+ dmacap,xor;
+ dmacap,memset;
+ };
+ };
+
+ eth0: ethernet@70000 {
+ compatible = "marvell,armada-370-neta";
+ reg = <0x70000 0x4000>;
+ interrupts-extended = <&mpic 8>;
+ clocks = <&gateclk 4>;
+ status = "disabled";
+ };
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "marvell,orion-mdio";
+ reg = <0x72004 0x4>;
+ };
+
+ coredivclk: clock@e4250 {
+ compatible = "marvell,armada-380-corediv-clock";
+ reg = <0xe4250 0xc>;
+ #clock-cells = <1>;
+ clocks = <&mainpll>;
+ clock-output-names = "nand";
+ };
+
+ flash@d0000 {
+ compatible = "marvell,armada370-nand";
+ reg = <0xd0000 0x54>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&coredivclk 0>;
+ status = "disabled";
+ };
+ };
+ };
+
+ clocks {
+ /* 2 GHz fixed main PLL */
+ mainpll: mainpll {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <2000000000>;
+ };
+
+ /* 25 MHz reference crystal */
+ refclk: oscillator {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/armada-xp-axpwifiap.dts b/arch/arm/boot/dts/armada-xp-axpwifiap.dts
index c5fe57269f5a..d83d7d69ac01 100644
--- a/arch/arm/boot/dts/armada-xp-axpwifiap.dts
+++ b/arch/arm/boot/dts/armada-xp-axpwifiap.dts
@@ -16,6 +16,8 @@
*/
/dts-v1/;
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
#include "armada-xp-mv78230.dtsi"
/ {
@@ -157,8 +159,8 @@
button@1 {
label = "Factory Reset Button";
- linux,code = <141>; /* KEY_SETUP */
- gpios = <&gpio1 1 1>;
+ linux,code = <KEY_SETUP>;
+ gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
};
};
};
diff --git a/arch/arm/boot/dts/armada-xp-db.dts b/arch/arm/boot/dts/armada-xp-db.dts
index bcf6d79a57ec..448373c4b0e5 100644
--- a/arch/arm/boot/dts/armada-xp-db.dts
+++ b/arch/arm/boot/dts/armada-xp-db.dts
@@ -2,7 +2,7 @@
* Device Tree file for Marvell Armada XP evaluation board
* (DB-78460-BP)
*
- * Copyright (C) 2012 Marvell
+ * Copyright (C) 2012-2014 Marvell
*
* Lior Amsalem <alior@marvell.com>
* Gregory CLEMENT <gregory.clement@free-electrons.com>
@@ -11,6 +11,15 @@
* 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.
+ *
+ * Note: this Device Tree assumes that the bootloader has remapped the
+ * internal registers to 0xf1000000 (instead of the default
+ * 0xd0000000). The 0xf1000000 is the default used by the recent,
+ * DT-capable, U-Boot bootloaders provided by Marvell. Some earlier
+ * boards were delivered with an older version of the bootloader that
+ * left internal registers mapped at 0xd0000000. If you are in this
+ * situation, you should either update your bootloader (preferred
+ * solution) or the below Device Tree should be adjusted.
*/
/dts-v1/;
@@ -30,7 +39,7 @@
};
soc {
- ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xd0000000 0x100000
+ ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x1000000>;
diff --git a/arch/arm/boot/dts/armada-xp-gp.dts b/arch/arm/boot/dts/armada-xp-gp.dts
index 274e2ad5f51c..61bda687f782 100644
--- a/arch/arm/boot/dts/armada-xp-gp.dts
+++ b/arch/arm/boot/dts/armada-xp-gp.dts
@@ -2,7 +2,7 @@
* Device Tree file for Marvell Armada XP development board
* (DB-MV784MP-GP)
*
- * Copyright (C) 2013 Marvell
+ * Copyright (C) 2013-2014 Marvell
*
* Lior Amsalem <alior@marvell.com>
* Gregory CLEMENT <gregory.clement@free-electrons.com>
@@ -11,6 +11,15 @@
* 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.
+ *
+ * Note: this Device Tree assumes that the bootloader has remapped the
+ * internal registers to 0xf1000000 (instead of the default
+ * 0xd0000000). The 0xf1000000 is the default used by the recent,
+ * DT-capable, U-Boot bootloaders provided by Marvell. Some earlier
+ * boards were delivered with an older version of the bootloader that
+ * left internal registers mapped at 0xd0000000. If you are in this
+ * situation, you should either update your bootloader (preferred
+ * solution) or the below Device Tree should be adjusted.
*/
/dts-v1/;
@@ -30,16 +39,17 @@
* 8 GB of plug-in RAM modules by default.The amount
* of memory available can be changed by the
* bootloader according the size of the module
- * actually plugged. Only 7GB are usable because
- * addresses from 0xC0000000 to 0xffffffff are used by
- * the internal registers of the SoC.
+ * actually plugged. However, memory between
+ * 0xF0000000 to 0xFFFFFFFF cannot be used, as it is
+ * the address range used for I/O (internal registers,
+ * MBus windows).
*/
- reg = <0x00000000 0x00000000 0x00000000 0xC0000000>,
+ reg = <0x00000000 0x00000000 0x00000000 0xf0000000>,
<0x00000001 0x00000000 0x00000001 0x00000000>;
};
soc {
- ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xd0000000 0x100000
+ ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x1000000>;
diff --git a/arch/arm/boot/dts/armada-xp-matrix.dts b/arch/arm/boot/dts/armada-xp-matrix.dts
index e47c49ecd55c..c2242745b9b8 100644
--- a/arch/arm/boot/dts/armada-xp-matrix.dts
+++ b/arch/arm/boot/dts/armada-xp-matrix.dts
@@ -23,7 +23,12 @@
memory {
device_type = "memory";
- reg = <0 0x00000000 0 0x80000000>; /* 2 GB */
+ /*
+ * This board has 4 GB of RAM, but the last 256 MB of
+ * RAM are not usable due to the overlap with the MBus
+ * Window address range
+ */
+ reg = <0 0x00000000 0 0xf0000000>;
};
soc {
diff --git a/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts b/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
index 99bcf76e6953..985948ce67b3 100644
--- a/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
+++ b/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
@@ -11,6 +11,8 @@
*/
/dts-v1/;
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
#include "armada-xp-mv78260.dtsi"
/ {
@@ -90,19 +92,19 @@
red_led {
label = "red_led";
- gpios = <&gpio1 17 1>;
+ gpios = <&gpio1 17 GPIO_ACTIVE_LOW>;
default-state = "off";
};
yellow_led {
label = "yellow_led";
- gpios = <&gpio1 19 1>;
+ gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
default-state = "off";
};
green_led {
label = "green_led";
- gpios = <&gpio1 21 1>;
+ gpios = <&gpio1 21 GPIO_ACTIVE_LOW>;
default-state = "keep";
};
};
@@ -114,8 +116,8 @@
button@1 {
label = "Init Button";
- linux,code = <116>;
- gpios = <&gpio1 28 0>;
+ linux,code = <KEY_POWER>;
+ gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
};
};
diff --git a/arch/arm/boot/dts/armada-xp.dtsi b/arch/arm/boot/dts/armada-xp.dtsi
index b8b84a22f0f3..abb9f9dcc525 100644
--- a/arch/arm/boot/dts/armada-xp.dtsi
+++ b/arch/arm/boot/dts/armada-xp.dtsi
@@ -111,6 +111,12 @@
clock-names = "nbclk", "fixed";
};
+ watchdog@20300 {
+ compatible = "marvell,armada-xp-wdt";
+ clocks = <&coreclk 2>, <&refclk>;
+ clock-names = "nbclk", "fixed";
+ };
+
armada-370-xp-pmsu@22000 {
compatible = "marvell,armada-370-xp-pmsu";
reg = <0x22100 0x400>, <0x20800 0x20>;
diff --git a/arch/arm/boot/dts/at91-ariag25.dts b/arch/arm/boot/dts/at91-ariag25.dts
index cce45f5177f9..55ab6180e350 100644
--- a/arch/arm/boot/dts/at91-ariag25.dts
+++ b/arch/arm/boot/dts/at91-ariag25.dts
@@ -129,7 +129,6 @@
adc0: adc@f804c000 {
status = "okay";
atmel,adc-channels-used = <0xf>;
- atmel,adc-num-channels = <4>;
};
dbgu: serial@fffff200 {
diff --git a/arch/arm/boot/dts/at91-cosino.dtsi b/arch/arm/boot/dts/at91-cosino.dtsi
index 2093c4d7cd6a..df4b78695695 100644
--- a/arch/arm/boot/dts/at91-cosino.dtsi
+++ b/arch/arm/boot/dts/at91-cosino.dtsi
@@ -64,7 +64,6 @@
};
adc0: adc@f804c000 {
- atmel,adc-clock-rate = <1000000>;
atmel,adc-ts-wires = <4>;
atmel,adc-ts-pressure-threshold = <10000>;
status = "okay";
diff --git a/arch/arm/boot/dts/at91-cosino_mega2560.dts b/arch/arm/boot/dts/at91-cosino_mega2560.dts
index f9415dd11f17..a542d5837a17 100644
--- a/arch/arm/boot/dts/at91-cosino_mega2560.dts
+++ b/arch/arm/boot/dts/at91-cosino_mega2560.dts
@@ -27,7 +27,6 @@
};
adc0: adc@f804c000 {
- atmel,adc-clock-rate = <1000000>;
atmel,adc-ts-wires = <4>;
atmel,adc-ts-pressure-threshold = <10000>;
status = "okay";
diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi
index 997901f7ed73..366fc2cbcd64 100644
--- a/arch/arm/boot/dts/at91sam9260.dtsi
+++ b/arch/arm/boot/dts/at91sam9260.dtsi
@@ -608,37 +608,38 @@
};
adc0: adc@fffe0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
compatible = "atmel,at91sam9260-adc";
reg = <0xfffe0000 0x100>;
interrupts = <5 IRQ_TYPE_LEVEL_HIGH 0>;
atmel,adc-use-external-triggers;
atmel,adc-channels-used = <0xf>;
atmel,adc-vref = <3300>;
- atmel,adc-num-channels = <4>;
atmel,adc-startup-time = <15>;
- atmel,adc-channel-base = <0x30>;
- atmel,adc-drdy-mask = <0x10000>;
- atmel,adc-status-register = <0x1c>;
- atmel,adc-trigger-register = <0x04>;
atmel,adc-res = <8 10>;
atmel,adc-res-names = "lowres", "highres";
atmel,adc-use-res = "highres";
trigger@0 {
+ reg = <0>;
trigger-name = "timer-counter-0";
trigger-value = <0x1>;
};
trigger@1 {
+ reg = <1>;
trigger-name = "timer-counter-1";
trigger-value = <0x3>;
};
trigger@2 {
+ reg = <2>;
trigger-name = "timer-counter-2";
trigger-value = <0x5>;
};
trigger@3 {
+ reg = <3>;
trigger-name = "external";
trigger-value = <0x13>;
trigger-external;
diff --git a/arch/arm/boot/dts/at91sam9261.dtsi b/arch/arm/boot/dts/at91sam9261.dtsi
new file mode 100644
index 000000000000..e21dda0e8986
--- /dev/null
+++ b/arch/arm/boot/dts/at91sam9261.dtsi
@@ -0,0 +1,735 @@
+/*
+ * at91sam9261.dtsi - Device Tree Include file for AT91SAM9261 SoC
+ *
+ * Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ *
+ * Licensed under GPLv2 only.
+ */
+
+#include "skeleton.dtsi"
+#include <dt-bindings/pinctrl/at91.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/clk/at91.h>
+
+/ {
+ model = "Atmel AT91SAM9261 family SoC";
+ compatible = "atmel,at91sam9261";
+ interrupt-parent = <&aic>;
+
+ aliases {
+ serial0 = &dbgu;
+ serial1 = &usart0;
+ serial2 = &usart1;
+ serial3 = &usart2;
+ gpio0 = &pioA;
+ gpio1 = &pioB;
+ gpio2 = &pioC;
+ tcb0 = &tcb0;
+ i2c0 = &i2c0;
+ ssc0 = &ssc0;
+ ssc1 = &ssc1;
+ };
+
+ cpus {
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ cpu {
+ compatible = "arm,arm926ej-s";
+ device_type = "cpu";
+ };
+ };
+
+ memory {
+ reg = <0x20000000 0x08000000>;
+ };
+
+ ahb {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ usb0: ohci@00500000 {
+ compatible = "atmel,at91rm9200-ohci", "usb-ohci";
+ reg = <0x00500000 0x100000>;
+ interrupts = <20 IRQ_TYPE_LEVEL_HIGH 2>;
+ clocks = <&usb>, <&ohci_clk>, <&hclk0>, <&uhpck>;
+ clock-names = "usb_clk", "ohci_clk", "hclk", "uhpck";
+ status = "disabled";
+ };
+
+ fb0: fb@0x00600000 {
+ compatible = "atmel,at91sam9261-lcdc";
+ reg = <0x00600000 0x1000>;
+ interrupts = <21 IRQ_TYPE_LEVEL_HIGH 3>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fb>;
+ clocks = <&lcd_clk>, <&hclk1>;
+ clock-names = "lcdc_clk", "hclk";
+ status = "disabled";
+ };
+
+ nand0: nand@40000000 {
+ compatible = "atmel,at91rm9200-nand";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x40000000 0x10000000>;
+ atmel,nand-addr-offset = <22>;
+ atmel,nand-cmd-offset = <21>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_nand>;
+
+ gpios = <&pioC 15 GPIO_ACTIVE_HIGH>,
+ <&pioC 14 GPIO_ACTIVE_HIGH>,
+ <0>;
+ status = "disabled";
+ };
+
+ apb {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ tcb0: timer@fffa0000 {
+ compatible = "atmel,at91rm9200-tcb";
+ reg = <0xfffa0000 0x100>;
+ interrupts = <17 IRQ_TYPE_LEVEL_HIGH 0>,
+ <18 IRQ_TYPE_LEVEL_HIGH 0>,
+ <19 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&tc0_clk>, <&tc1_clk>, <&tc2_clk>;
+ clock-names = "t0_clk", "t1_clk", "t2_clk";
+ };
+
+ usb1: gadget@fffa4000 {
+ compatible = "atmel,at91rm9200-udc";
+ reg = <0xfffa4000 0x4000>;
+ interrupts = <10 IRQ_TYPE_LEVEL_HIGH 2>;
+ clocks = <&usb>, <&udc_clk>, <&udpck>;
+ clock-names = "usb_clk", "udc_clk", "udpck";
+ status = "disabled";
+ };
+
+ mmc0: mmc@fffa8000 {
+ compatible = "atmel,hsmci";
+ reg = <0xfffa8000 0x600>;
+ interrupts = <9 IRQ_TYPE_LEVEL_HIGH 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_mmc0_clk>, <&pinctrl_mmc0_slot0_cmd_dat0>, <&pinctrl_mmc0_slot0_dat1_3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&mci0_clk>;
+ clock-names = "mci_clk";
+ status = "disabled";
+ };
+
+ i2c0: i2c@fffac000 {
+ compatible = "atmel,at91sam9261-i2c";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c_twi>;
+ reg = <0xfffac000 0x100>;
+ interrupts = <11 IRQ_TYPE_LEVEL_HIGH 6>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&twi0_clk>;
+ status = "disabled";
+ };
+
+ usart0: serial@fffb0000 {
+ compatible = "atmel,at91sam9260-usart";
+ reg = <0xfffb0000 0x200>;
+ interrupts = <6 IRQ_TYPE_LEVEL_HIGH 5>;
+ atmel,use-dma-rx;
+ atmel,use-dma-tx;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usart0>;
+ clocks = <&usart0_clk>;
+ clock-names = "usart";
+ status = "disabled";
+ };
+
+ usart1: serial@fffb4000 {
+ compatible = "atmel,at91sam9260-usart";
+ reg = <0xfffb4000 0x200>;
+ interrupts = <7 IRQ_TYPE_LEVEL_HIGH 5>;
+ atmel,use-dma-rx;
+ atmel,use-dma-tx;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usart1>;
+ clocks = <&usart1_clk>;
+ clock-names = "usart";
+ status = "disabled";
+ };
+
+ usart2: serial@fffb8000{
+ compatible = "atmel,at91sam9260-usart";
+ reg = <0xfffb8000 0x200>;
+ interrupts = <8 IRQ_TYPE_LEVEL_HIGH 5>;
+ atmel,use-dma-rx;
+ atmel,use-dma-tx;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usart2>;
+ clocks = <&usart2_clk>;
+ clock-names = "usart";
+ status = "disabled";
+ };
+
+ ssc0: ssc@fffbc000 {
+ compatible = "atmel,at91rm9200-ssc";
+ reg = <0xfffbc000 0x4000>;
+ interrupts = <14 IRQ_TYPE_LEVEL_HIGH 5>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
+ status = "disabled";
+ };
+
+ ssc1: ssc@fffc0000 {
+ compatible = "atmel,at91rm9200-ssc";
+ reg = <0xfffc0000 0x4000>;
+ interrupts = <15 IRQ_TYPE_LEVEL_HIGH 5>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ssc1_tx &pinctrl_ssc1_rx>;
+ status = "disabled";
+ };
+
+ spi0: spi@fffc8000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "atmel,at91rm9200-spi";
+ reg = <0xfffc8000 0x200>;
+ cs-gpios = <0>, <0>, <0>, <0>;
+ interrupts = <12 IRQ_TYPE_LEVEL_HIGH 3>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_spi0>;
+ clocks = <&spi0_clk>;
+ clock-names = "spi_clk";
+ status = "disabled";
+ };
+
+ spi1: spi@fffcc000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "atmel,at91rm9200-spi";
+ reg = <0xfffcc000 0x200>;
+ interrupts = <13 IRQ_TYPE_LEVEL_HIGH 3>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_spi1>;
+ clocks = <&spi1_clk>;
+ clock-names = "spi_clk";
+ status = "disabled";
+ };
+
+ ramc: ramc@ffffea00 {
+ compatible = "atmel,at91sam9260-sdramc";
+ reg = <0xffffea00 0x200>;
+ };
+
+ matrix: matrix@ffffee00 {
+ compatible = "atmel,at91sam9260-bus-matrix";
+ reg = <0xffffee00 0x200>;
+ };
+
+ aic: interrupt-controller@fffff000 {
+ #interrupt-cells = <3>;
+ compatible = "atmel,at91rm9200-aic";
+ interrupt-controller;
+ reg = <0xfffff000 0x200>;
+ atmel,external-irqs = <29 30 31>;
+ };
+
+ dbgu: serial@fffff200 {
+ compatible = "atmel,at91sam9260-usart";
+ reg = <0xfffff200 0x200>;
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_dbgu>;
+ clocks = <&mck>;
+ clock-names = "usart";
+ status = "disabled";
+ };
+
+ pinctrl@fffff400 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "atmel,at91rm9200-pinctrl", "simple-bus";
+ ranges = <0xfffff400 0xfffff400 0x600>;
+
+ atmel,mux-mask =
+ /* A B */
+ <0xffffffff 0xfffffff7>, /* pioA */
+ <0xffffffff 0xfffffff4>, /* pioB */
+ <0xffffffff 0xffffff07>; /* pioC */
+
+ /* shared pinctrl settings */
+ dbgu {
+ pinctrl_dbgu: dbgu-0 {
+ atmel,pins =
+ <AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>;
+ };
+ };
+
+ usart0 {
+ pinctrl_usart0: usart0-0 {
+ atmel,pins =
+ <AT91_PIOC 8 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOC 9 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart0_rts: usart0_rts-0 {
+ atmel,pins =
+ <AT91_PIOC 10 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart0_cts: usart0_cts-0 {
+ atmel,pins =
+ <AT91_PIOC 11 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+ };
+
+ usart1 {
+ pinctrl_usart1: usart1-0 {
+ atmel,pins =
+ <AT91_PIOC 12 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOC 13 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart1_rts: usart1_rts-0 {
+ atmel,pins =
+ <AT91_PIOA 12 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart1_cts: usart1_cts-0 {
+ atmel,pins =
+ <AT91_PIOA 13 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+ };
+
+ usart2 {
+ pinctrl_usart2: usart2-0 {
+ atmel,pins =
+ <AT91_PIOC 14 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOC 15 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart2_rts: usart2_rts-0 {
+ atmel,pins =
+ <AT91_PIOA 15 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart2_cts: usart2_cts-0 {
+ atmel,pins =
+ <AT91_PIOA 16 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+ };
+
+ nand {
+ pinctrl_nand: nand-0 {
+ atmel,pins =
+ <AT91_PIOC 15 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOC 14 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
+ };
+ };
+
+ mmc0 {
+ pinctrl_mmc0_clk: mmc0_clk-0 {
+ atmel,pins =
+ <AT91_PIOA 2 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_mmc0_slot0_cmd_dat0: mmc0_slot0_cmd_dat0-0 {
+ atmel,pins =
+ <AT91_PIOA 1 AT91_PERIPH_B AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOA 0 AT91_PERIPH_B AT91_PINCTRL_PULL_UP>;
+ };
+
+ pinctrl_mmc0_slot0_dat1_3: mmc0_slot0_dat1_3-0 {
+ atmel,pins =
+ <AT91_PIOA 4 AT91_PERIPH_B AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOA 5 AT91_PERIPH_B AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOA 6 AT91_PERIPH_B AT91_PINCTRL_PULL_UP>;
+ };
+ };
+
+ ssc0 {
+ pinctrl_ssc0_tx: ssc0_tx-0 {
+ atmel,pins =
+ <AT91_PIOB 21 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 22 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 23 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_ssc0_rx: ssc0_rx-0 {
+ atmel,pins =
+ <AT91_PIOB 24 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 25 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 26 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+ };
+
+ ssc1 {
+ pinctrl_ssc1_tx: ssc1_tx-0 {
+ atmel,pins =
+ <AT91_PIOA 17 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOA 18 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOA 19 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_ssc1_rx: ssc1_rx-0 {
+ atmel,pins =
+ <AT91_PIOA 20 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOA 21 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOA 22 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+ };
+
+ spi0 {
+ pinctrl_spi0: spi0-0 {
+ atmel,pins =
+ <AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+ };
+
+ spi1 {
+ pinctrl_spi1: spi1-0 {
+ atmel,pins =
+ <AT91_PIOB 30 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 31 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 29 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+ };
+
+ tcb0 {
+ pinctrl_tcb0_tclk0: tcb0_tclk0-0 {
+ atmel,pins = <AT91_PIOC 16 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tclk1: tcb0_tclk1-0 {
+ atmel,pins = <AT91_PIOC 17 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tclk2: tcb0_tclk2-0 {
+ atmel,pins = <AT91_PIOC 18 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tioa0: tcb0_tioa0-0 {
+ atmel,pins = <AT91_PIOC 19 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tioa1: tcb0_tioa1-0 {
+ atmel,pins = <AT91_PIOC 21 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tioa2: tcb0_tioa2-0 {
+ atmel,pins = <AT91_PIOC 23 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tiob0: tcb0_tiob0-0 {
+ atmel,pins = <AT91_PIOC 20 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tiob1: tcb0_tiob1-0 {
+ atmel,pins = <AT91_PIOC 22 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tiob2: tcb0_tiob2-0 {
+ atmel,pins = <AT91_PIOC 24 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+ };
+
+ i2c0 {
+ pinctrl_i2c_bitbang: i2c-0-bitbang {
+ atmel,pins =
+ <AT91_PIOA 7 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>,
+ <AT91_PIOA 8 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>;
+ };
+ pinctrl_i2c_twi: i2c-0-twi {
+ atmel,pins =
+ <AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+ };
+
+ fb {
+ pinctrl_fb: fb-0 {
+ atmel,pins =
+ <AT91_PIOB 1 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 2 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 3 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 7 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 8 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 9 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 10 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 11 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 12 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 15 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 16 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 17 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 18 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 20 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 23 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOB 24 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOB 25 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOB 26 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOB 27 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOB 28 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+ };
+
+ pioA: gpio@fffff400 {
+ compatible = "atmel,at91rm9200-gpio";
+ reg = <0xfffff400 0x200>;
+ interrupts = <2 IRQ_TYPE_LEVEL_HIGH 1>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&pioA_clk>;
+ };
+
+ pioB: gpio@fffff600 {
+ compatible = "atmel,at91rm9200-gpio";
+ reg = <0xfffff600 0x200>;
+ interrupts = <3 IRQ_TYPE_LEVEL_HIGH 1>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&pioB_clk>;
+ };
+
+ pioC: gpio@fffff800 {
+ compatible = "atmel,at91rm9200-gpio";
+ reg = <0xfffff800 0x200>;
+ interrupts = <4 IRQ_TYPE_LEVEL_HIGH 1>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&pioC_clk>;
+ };
+ };
+
+ pmc: pmc@fffffc00 {
+ compatible = "atmel,at91rm9200-pmc";
+ reg = <0xfffffc00 0x100>;
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
+ interrupt-controller;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+ clk32k: slck {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <32768>;
+ };
+
+ main: mainck {
+ compatible = "atmel,at91rm9200-clk-main";
+ #clock-cells = <0>;
+ interrupts-extended = <&pmc AT91_PMC_MOSCS>;
+ clocks = <&clk32k>;
+ };
+
+ plla: pllack {
+ compatible = "atmel,at91rm9200-clk-pll";
+ #clock-cells = <0>;
+ interrupts-extended = <&pmc AT91_PMC_LOCKA>;
+ clocks = <&main>;
+ reg = <0>;
+ atmel,clk-input-range = <1000000 32000000>;
+ #atmel,pll-clk-output-range-cells = <4>;
+ atmel,pll-clk-output-ranges = <80000000 200000000 190000000 240000000>;
+ };
+
+ pllb: pllbck {
+ compatible = "atmel,at91rm9200-clk-pll";
+ #clock-cells = <0>;
+ interrupts-extended = <&pmc AT91_PMC_LOCKB>;
+ clocks = <&main>;
+ reg = <1>;
+ atmel,clk-input-range = <1000000 32000000>;
+ #atmel,pll-clk-output-range-cells = <4>;
+ atmel,pll-clk-output-ranges = <80000000 200000000 190000000 240000000>;
+ };
+
+ mck: masterck {
+ compatible = "atmel,at91rm9200-clk-master";
+ #clock-cells = <0>;
+ interrupts-extended = <&pmc AT91_PMC_MCKRDY>;
+ clocks = <&clk32k>, <&main>, <&plla>, <&pllb>;
+ atmel,clk-output-range = <0 94000000>;
+ atmel,clk-divisors = <1 2 4 3>;
+ };
+
+ usb: usbck {
+ compatible = "atmel,at91rm9200-clk-usb";
+ #clock-cells = <0>;
+ atmel,clk-divisors = <1 2 4 3>;
+ clocks = <&pllb>;
+ };
+
+ systemck {
+ compatible = "atmel,at91rm9200-clk-system";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ uhpck: uhpck {
+ #clock-cells = <0>;
+ reg = <6>;
+ clocks = <&usb>;
+ };
+
+ udpck: udpck {
+ #clock-cells = <0>;
+ reg = <7>;
+ clocks = <&usb>;
+ };
+
+ hclk0: hclk0 {
+ #clock-cells = <0>;
+ reg = <16>;
+ clocks = <&mck>;
+ };
+
+ hclk1: hclk1 {
+ #clock-cells = <0>;
+ reg = <17>;
+ clocks = <&mck>;
+ };
+ };
+
+ periphck {
+ compatible = "atmel,at91rm9200-clk-peripheral";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&mck>;
+
+ pioA_clk: pioA_clk {
+ #clock-cells = <0>;
+ reg = <2>;
+ };
+
+ pioB_clk: pioB_clk {
+ #clock-cells = <0>;
+ reg = <3>;
+ };
+
+ pioC_clk: pioC_clk {
+ #clock-cells = <0>;
+ reg = <4>;
+ };
+
+ usart0_clk: usart0_clk {
+ #clock-cells = <0>;
+ reg = <6>;
+ };
+
+ usart1_clk: usart1_clk {
+ #clock-cells = <0>;
+ reg = <7>;
+ };
+
+ usart2_clk: usart2_clk {
+ #clock-cells = <0>;
+ reg = <8>;
+ };
+
+ mci0_clk: mci0_clk {
+ #clock-cells = <0>;
+ reg = <9>;
+ };
+
+ udc_clk: udc_clk {
+ #clock-cells = <0>;
+ reg = <10>;
+ };
+
+ twi0_clk: twi0_clk {
+ reg = <11>;
+ #clock-cells = <0>;
+ };
+
+ spi0_clk: spi0_clk {
+ #clock-cells = <0>;
+ reg = <12>;
+ };
+
+ spi1_clk: spi1_clk {
+ #clock-cells = <0>;
+ reg = <13>;
+ };
+
+ tc0_clk: tc0_clk {
+ #clock-cells = <0>;
+ reg = <17>;
+ };
+
+ tc1_clk: tc1_clk {
+ #clock-cells = <0>;
+ reg = <18>;
+ };
+
+ tc2_clk: tc2_clk {
+ #clock-cells = <0>;
+ reg = <19>;
+ };
+
+ ohci_clk: ohci_clk {
+ #clock-cells = <0>;
+ reg = <20>;
+ };
+
+ lcd_clk: lcd_clk {
+ #clock-cells = <0>;
+ reg = <21>;
+ };
+ };
+ };
+
+ rstc@fffffd00 {
+ compatible = "atmel,at91sam9260-rstc";
+ reg = <0xfffffd00 0x10>;
+ };
+
+ shdwc@fffffd10 {
+ compatible = "atmel,at91sam9260-shdwc";
+ reg = <0xfffffd10 0x10>;
+ };
+
+ pit: timer@fffffd30 {
+ compatible = "atmel,at91sam9260-pit";
+ reg = <0xfffffd30 0xf>;
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
+ clocks = <&mck>;
+ };
+
+ watchdog@fffffd40 {
+ compatible = "atmel,at91sam9260-wdt";
+ reg = <0xfffffd40 0x10>;
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
+ status = "disabled";
+ };
+ };
+ };
+
+ i2c@0 {
+ compatible = "i2c-gpio";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c_bitbang>;
+ gpios = <&pioA 7 GPIO_ACTIVE_HIGH>, /* sda */
+ <&pioA 8 GPIO_ACTIVE_HIGH>; /* scl */
+ i2c-gpio,sda-open-drain;
+ i2c-gpio,scl-open-drain;
+ i2c-gpio,delay-us = <2>; /* ~100 kHz */
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+};
diff --git a/arch/arm/boot/dts/at91sam9261ek.dts b/arch/arm/boot/dts/at91sam9261ek.dts
new file mode 100644
index 000000000000..2ce527e70c7a
--- /dev/null
+++ b/arch/arm/boot/dts/at91sam9261ek.dts
@@ -0,0 +1,211 @@
+/*
+ * at91sam9261ek.dts - Device Tree file for Atmel at91sam9261 reference board
+ *
+ * Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ *
+ * Licensed under GPLv2 only.
+ */
+/dts-v1/;
+#include "at91sam9261.dtsi"
+
+/ {
+ model = "Atmel at91sam9261ek";
+ compatible = "atmel,at91sam9261ek", "atmel,at91sam9261", "atmel,at91sam9";
+
+ chosen {
+ bootargs = "console=ttyS0,115200 rootfstype=ubifs ubi.mtd=5 root=ubi0:rootfs rw";
+ };
+
+ memory {
+ reg = <0x20000000 0x4000000>;
+ };
+
+ clocks {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ main_clock: clock@0 {
+ compatible = "atmel,osc", "fixed-clock";
+ clock-frequency = <18432000>;
+ };
+ };
+
+ ahb {
+ usb0: ohci@00500000 {
+ status = "okay";
+ };
+
+ fb0: fb@0x00600000 {
+ display = <&display0>;
+ atmel,power-control-gpio = <&pioA 12 GPIO_ACTIVE_LOW>;
+ status = "okay";
+
+ display0: display {
+ bits-per-pixel = <16>;
+ atmel,lcdcon-backlight;
+ atmel,dmacon = <0x1>;
+ atmel,lcdcon2 = <0x80008002>;
+ atmel,guard-time = <1>;
+ atmel,lcd-wiring-mode = "BRG";
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing0 {
+ clock-frequency = <4965000>;
+ hactive = <240>;
+ vactive = <320>;
+ hback-porch = <1>;
+ hfront-porch = <33>;
+ vback-porch = <1>;
+ vfront-porch = <0>;
+ hsync-len = <5>;
+ vsync-len = <1>;
+ hsync-active = <1>;
+ vsync-active = <1>;
+ };
+ };
+ };
+ };
+
+ nand0: nand@40000000 {
+ nand-bus-width = <8>;
+ nand-ecc-mode = "soft";
+ nand-on-flash-bbt;
+ status = "okay";
+
+ at91bootstrap@0 {
+ label = "at91bootstrap";
+ reg = <0x0 0x40000>;
+ };
+
+ bootloader@40000 {
+ label = "bootloader";
+ reg = <0x40000 0x80000>;
+ };
+
+ bootloaderenv@c0000 {
+ label = "bootloader env";
+ reg = <0xc0000 0xc0000>;
+ };
+
+ dtb@180000 {
+ label = "device tree";
+ reg = <0x180000 0x80000>;
+ };
+
+ kernel@200000 {
+ label = "kernel";
+ reg = <0x200000 0x600000>;
+ };
+
+ rootfs@800000 {
+ label = "rootfs";
+ reg = <0x800000 0x0f800000>;
+ };
+ };
+
+ apb {
+ usb1: gadget@fffa4000 {
+ atmel,vbus-gpio = <&pioB 29 GPIO_ACTIVE_HIGH>;
+ status = "okay";
+ };
+
+ spi0: spi@fffc8000 {
+ cs-gpios = <&pioA 3 0>, <0>, <&pioA 28 0>, <0>;
+ status = "okay";
+
+ mtd_dataflash@0 {
+ compatible = "atmel,at45", "atmel,dataflash";
+ reg = <0>;
+ spi-max-frequency = <15000000>;
+ };
+
+ tsc2046@0 {
+ reg = <2>;
+ compatible = "ti,ads7843";
+ interrupts-extended = <&pioC 2 IRQ_TYPE_EDGE_BOTH>;
+ spi-max-frequency = <3000000>;
+ pendown-gpio = <&pioC 2 GPIO_ACTIVE_HIGH>;
+
+ ti,x-min = /bits/ 16 <150>;
+ ti,x-max = /bits/ 16 <3830>;
+ ti,y-min = /bits/ 16 <190>;
+ ti,y-max = /bits/ 16 <3830>;
+ ti,vref-delay-usecs = /bits/ 16 <450>;
+ ti,x-plate-ohms = /bits/ 16 <450>;
+ ti,y-plate-ohms = /bits/ 16 <250>;
+ ti,pressure-max = /bits/ 16 <15000>;
+ ti,debounce-rep = /bits/ 16 <0>;
+ ti,debounce-tol = /bits/ 16 <65535>;
+ ti,debounce-max = /bits/ 16 <1>;
+
+ linux,wakeup;
+ };
+ };
+
+ dbgu: serial@fffff200 {
+ status = "okay";
+ };
+
+ watchdog@fffffd40 {
+ status = "okay";
+ };
+
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ ds8 {
+ label = "ds8";
+ gpios = <&pioA 13 GPIO_ACTIVE_LOW>;
+ linux,default-trigger = "none";
+ };
+
+ ds7 {
+ label = "ds7";
+ gpios = <&pioA 14 GPIO_ACTIVE_LOW>;
+ linux,default-trigger = "nand-disk";
+ };
+
+ ds1 {
+ label = "ds1";
+ gpios = <&pioA 23 GPIO_ACTIVE_LOW>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+
+ button_0 {
+ label = "button_0";
+ gpios = <&pioA 27 GPIO_ACTIVE_LOW>;
+ linux,code = <256>;
+ gpio-key,wakeup;
+ };
+
+ button_1 {
+ label = "button_1";
+ gpios = <&pioA 26 GPIO_ACTIVE_LOW>;
+ linux,code = <257>;
+ gpio-key,wakeup;
+ };
+
+ button_2 {
+ label = "button_2";
+ gpios = <&pioA 25 GPIO_ACTIVE_LOW>;
+ linux,code = <258>;
+ gpio-key,wakeup;
+ };
+
+ button_3 {
+ label = "button_3";
+ gpios = <&pioA 24 GPIO_ACTIVE_LOW>;
+ linux,code = <259>;
+ gpio-key,wakeup;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi
index cbcc058b26b4..9cdaecff13b3 100644
--- a/arch/arm/boot/dts/at91sam9g45.dtsi
+++ b/arch/arm/boot/dts/at91sam9g45.dtsi
@@ -632,40 +632,41 @@
};
adc0: adc@fffb0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
compatible = "atmel,at91sam9260-adc";
reg = <0xfffb0000 0x100>;
interrupts = <20 IRQ_TYPE_LEVEL_HIGH 0>;
atmel,adc-use-external-triggers;
atmel,adc-channels-used = <0xff>;
atmel,adc-vref = <3300>;
- atmel,adc-num-channels = <8>;
atmel,adc-startup-time = <40>;
- atmel,adc-channel-base = <0x30>;
- atmel,adc-drdy-mask = <0x10000>;
- atmel,adc-status-register = <0x1c>;
- atmel,adc-trigger-register = <0x08>;
atmel,adc-res = <8 10>;
atmel,adc-res-names = "lowres", "highres";
atmel,adc-use-res = "highres";
trigger@0 {
+ reg = <0>;
trigger-name = "external-rising";
trigger-value = <0x1>;
trigger-external;
};
trigger@1 {
+ reg = <1>;
trigger-name = "external-falling";
trigger-value = <0x2>;
trigger-external;
};
trigger@2 {
+ reg = <2>;
trigger-name = "external-any";
trigger-value = <0x3>;
trigger-external;
};
trigger@3 {
+ reg = <3>;
trigger-name = "continuous";
trigger-value = <0x6>;
};
@@ -817,6 +818,7 @@
>;
atmel,nand-addr-offset = <21>;
atmel,nand-cmd-offset = <22>;
+ atmel,nand-has-dma;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_nand>;
gpios = <&pioC 8 GPIO_ACTIVE_HIGH
diff --git a/arch/arm/boot/dts/at91sam9n12.dtsi b/arch/arm/boot/dts/at91sam9n12.dtsi
index 394e6ce2afb7..9f04808fc697 100644
--- a/arch/arm/boot/dts/at91sam9n12.dtsi
+++ b/arch/arm/boot/dts/at91sam9n12.dtsi
@@ -570,6 +570,7 @@
atmel,pmecc-lookup-table-offset = <0x0 0x8000>;
atmel,nand-addr-offset = <21>;
atmel,nand-cmd-offset = <22>;
+ atmel,nand-has-dma;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_nand>;
gpios = <&pioD 5 GPIO_ACTIVE_HIGH
diff --git a/arch/arm/boot/dts/at91sam9rl.dtsi b/arch/arm/boot/dts/at91sam9rl.dtsi
new file mode 100644
index 000000000000..63e1784d272c
--- /dev/null
+++ b/arch/arm/boot/dts/at91sam9rl.dtsi
@@ -0,0 +1,802 @@
+/*
+ * at91sam9rl.dtsi - Device Tree Include file for AT91SAM9RL family SoC
+ *
+ * Copyright (C) 2014 Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include "skeleton.dtsi"
+#include <dt-bindings/pinctrl/at91.h>
+#include <dt-bindings/clk/at91.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ model = "Atmel AT91SAM9RL family SoC";
+ compatible = "atmel,at91sam9rl", "atmel,at91sam9";
+ interrupt-parent = <&aic>;
+
+ aliases {
+ serial0 = &dbgu;
+ serial1 = &usart0;
+ serial2 = &usart1;
+ serial3 = &usart2;
+ serial4 = &usart3;
+ gpio0 = &pioA;
+ gpio1 = &pioB;
+ gpio2 = &pioC;
+ gpio3 = &pioD;
+ tcb0 = &tcb0;
+ i2c0 = &i2c0;
+ i2c1 = &i2c1;
+ ssc0 = &ssc0;
+ ssc1 = &ssc1;
+ };
+
+ cpus {
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ cpu {
+ compatible = "arm,arm926ej-s";
+ device_type = "cpu";
+ };
+ };
+
+ memory {
+ reg = <0x20000000 0x04000000>;
+ };
+
+ ahb {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ nand0: nand@40000000 {
+ compatible = "atmel,at91rm9200-nand";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x40000000 0x10000000>,
+ <0xffffe800 0x200>;
+ atmel,nand-addr-offset = <21>;
+ atmel,nand-cmd-offset = <22>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_nand>;
+ gpios = <&pioD 17 GPIO_ACTIVE_HIGH>,
+ <&pioB 6 GPIO_ACTIVE_HIGH>,
+ <0>;
+ status = "disabled";
+ };
+
+ apb {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ tcb0: timer@fffa0000 {
+ compatible = "atmel,at91rm9200-tcb";
+ reg = <0xfffa0000 0x100>;
+ interrupts = <16 IRQ_TYPE_LEVEL_HIGH 0>,
+ <17 IRQ_TYPE_LEVEL_HIGH 0>,
+ <18 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&tc0_clk>, <&tc1_clk>, <&tc2_clk>;
+ clock-names = "t0_clk", "t1_clk", "t2_clk";
+ };
+
+ mmc0: mmc@fffa4000 {
+ compatible = "atmel,hsmci";
+ reg = <0xfffa4000 0x600>;
+ interrupts = <10 IRQ_TYPE_LEVEL_HIGH 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-names = "default";
+ clocks = <&mci0_clk>;
+ clock-names = "mci_clk";
+ status = "disabled";
+ };
+
+ i2c0: i2c@fffa8000 {
+ compatible = "atmel,at91sam9260-i2c";
+ reg = <0xfffa8000 0x100>;
+ interrupts = <11 IRQ_TYPE_LEVEL_HIGH 6>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&twi0_clk>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@fffac000 {
+ compatible = "atmel,at91sam9260-i2c";
+ reg = <0xfffac000 0x100>;
+ interrupts = <12 IRQ_TYPE_LEVEL_HIGH 6>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ usart0: serial@fffb0000 {
+ compatible = "atmel,at91sam9260-usart";
+ reg = <0xfffb0000 0x200>;
+ interrupts = <6 IRQ_TYPE_LEVEL_HIGH 5>;
+ atmel,use-dma-rx;
+ atmel,use-dma-tx;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usart0>;
+ clocks = <&usart0_clk>;
+ clock-names = "usart";
+ status = "disabled";
+ };
+
+ usart1: serial@fffb4000 {
+ compatible = "atmel,at91sam9260-usart";
+ reg = <0xfffb4000 0x200>;
+ interrupts = <7 IRQ_TYPE_LEVEL_HIGH 5>;
+ atmel,use-dma-rx;
+ atmel,use-dma-tx;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usart1>;
+ clocks = <&usart1_clk>;
+ clock-names = "usart";
+ status = "disabled";
+ };
+
+ usart2: serial@fffb8000 {
+ compatible = "atmel,at91sam9260-usart";
+ reg = <0xfffb8000 0x200>;
+ interrupts = <8 IRQ_TYPE_LEVEL_HIGH 5>;
+ atmel,use-dma-rx;
+ atmel,use-dma-tx;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usart2>;
+ clocks = <&usart2_clk>;
+ clock-names = "usart";
+ status = "disabled";
+ };
+
+ usart3: serial@fffbc000 {
+ compatible = "atmel,at91sam9260-usart";
+ reg = <0xfffbc000 0x200>;
+ interrupts = <9 IRQ_TYPE_LEVEL_HIGH 5>;
+ atmel,use-dma-rx;
+ atmel,use-dma-tx;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usart3>;
+ clocks = <&usart3_clk>;
+ clock-names = "usart";
+ status = "disabled";
+ };
+
+ ssc0: ssc@fffc0000 {
+ compatible = "atmel,at91rm9200-ssc";
+ reg = <0xfffc0000 0x4000>;
+ interrupts = <14 IRQ_TYPE_LEVEL_HIGH 5>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
+ status = "disabled";
+ };
+
+ ssc1: ssc@fffc4000 {
+ compatible = "atmel,at91rm9200-ssc";
+ reg = <0xfffc4000 0x4000>;
+ interrupts = <15 IRQ_TYPE_LEVEL_HIGH 5>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ssc1_tx &pinctrl_ssc1_rx>;
+ status = "disabled";
+ };
+
+ spi0: spi@fffcc000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "atmel,at91rm9200-spi";
+ reg = <0xfffcc000 0x200>;
+ interrupts = <13 IRQ_TYPE_LEVEL_HIGH 3>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_spi0>;
+ clocks = <&spi0_clk>;
+ clock-names = "spi_clk";
+ status = "disabled";
+ };
+
+ ramc0: ramc@ffffea00 {
+ compatible = "atmel,at91sam9260-sdramc";
+ reg = <0xffffea00 0x200>;
+ };
+
+ aic: interrupt-controller@fffff000 {
+ #interrupt-cells = <3>;
+ compatible = "atmel,at91rm9200-aic";
+ interrupt-controller;
+ reg = <0xfffff000 0x200>;
+ atmel,external-irqs = <31>;
+ };
+
+ dbgu: serial@fffff200 {
+ compatible = "atmel,at91sam9260-usart";
+ reg = <0xfffff200 0x200>;
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_dbgu>;
+ clocks = <&mck>;
+ clock-names = "usart";
+ status = "disabled";
+ };
+
+ pinctrl@fffff400 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "atmel,at91rm9200-pinctrl", "simple-bus";
+ ranges = <0xfffff400 0xfffff400 0x800>;
+
+ atmel,mux-mask =
+ /* A B */
+ <0xffffffff 0xe05c6738>, /* pioA */
+ <0xffffffff 0x0000c780>, /* pioB */
+ <0xffffffff 0xe3ffff0e>, /* pioC */
+ <0x003fffff 0x0001ff3c>; /* pioD */
+
+ /* shared pinctrl settings */
+ dbgu {
+ pinctrl_dbgu: dbgu-0 {
+ atmel,pins =
+ <AT91_PIOA 21 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOA 22 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>;
+ };
+ };
+
+ i2c_gpio0 {
+ pinctrl_i2c_gpio0: i2c_gpio0-0 {
+ atmel,pins =
+ <AT91_PIOA 23 AT91_PERIPH_GPIO AT91_PINCTRL_MULTI_DRIVE>,
+ <AT91_PIOA 24 AT91_PERIPH_GPIO AT91_PINCTRL_MULTI_DRIVE>;
+ };
+ };
+
+ i2c_gpio1 {
+ pinctrl_i2c_gpio1: i2c_gpio1-0 {
+ atmel,pins =
+ <AT91_PIOD 10 AT91_PERIPH_GPIO AT91_PINCTRL_MULTI_DRIVE>,
+ <AT91_PIOD 11 AT91_PERIPH_GPIO AT91_PINCTRL_MULTI_DRIVE>;
+ };
+ };
+
+ mmc0 {
+ pinctrl_mmc0_clk: mmc0_clk-0 {
+ atmel,pins =
+ <AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_mmc0_slot0_cmd_dat0: mmc0_slot0_cmd_dat0-0 {
+ atmel,pins =
+ <AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>;
+ };
+
+ pinctrl_mmc0_slot0_dat1_3: mmc0_slot0_dat1_3-0 {
+ atmel,pins =
+ <AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>;
+ };
+ };
+
+ nand {
+ pinctrl_nand: nand-0 {
+ atmel,pins =
+ <AT91_PIOD 17 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOB 6 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
+ };
+
+ pinctrl_nand0_ale_cle: nand_ale_cle-0 {
+ atmel,pins =
+ <AT91_PIOB 2 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 3 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_nand0_oe_we: nand_oe_we-0 {
+ atmel,pins =
+ <AT91_PIOB 4 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOB 5 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_nand0_cs: nand_cs-0 {
+ atmel,pins =
+ <AT91_PIOB 6 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+ };
+
+ ssc0 {
+ pinctrl_ssc0_tx: ssc0_tx-0 {
+ atmel,pins =
+ <AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOC 0 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOC 1 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_ssc0_rx: ssc0_rx-0 {
+ atmel,pins =
+ <AT91_PIOA 10 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOA 16 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOA 22 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+ };
+
+ ssc1 {
+ pinctrl_ssc1_tx: ssc1_tx-0 {
+ atmel,pins =
+ <AT91_PIOA 13 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOA 29 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOA 30 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_ssc1_rx: ssc1_rx-0 {
+ atmel,pins =
+ <AT91_PIOA 8 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOA 9 AT91_PERIPH_B AT91_PINCTRL_NONE>,
+ <AT91_PIOA 14 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+ };
+
+ spi0 {
+ pinctrl_spi0: spi0-0 {
+ atmel,pins =
+ <AT91_PIOA 25 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOA 26 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOA 27 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+ };
+
+ tcb0 {
+ pinctrl_tcb0_tclk0: tcb0_tclk0-0 {
+ atmel,pins = <AT91_PIOA 3 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tclk1: tcb0_tclk1-0 {
+ atmel,pins = <AT91_PIOC 31 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tclk2: tcb0_tclk2-0 {
+ atmel,pins = <AT91_PIOD 21 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tioa0: tcb0_tioa0-0 {
+ atmel,pins = <AT91_PIOA 4 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tioa1: tcb0_tioa1-0 {
+ atmel,pins = <AT91_PIOC 29 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tioa2: tcb0_tioa2-0 {
+ atmel,pins = <AT91_PIOD 10 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tiob0: tcb0_tiob0-0 {
+ atmel,pins = <AT91_PIOA 5 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tiob1: tcb0_tiob1-0 {
+ atmel,pins = <AT91_PIOC 30 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_tcb0_tiob2: tcb0_tiob2-0 {
+ atmel,pins = <AT91_PIOD 11 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+ };
+
+ usart0 {
+ pinctrl_usart0: usart0-0 {
+ atmel,pins =
+ <AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>;
+ };
+
+ pinctrl_usart0_rts: usart0_rts-0 {
+ atmel,pins =
+ <AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart0_cts: usart0_cts-0 {
+ atmel,pins =
+ <AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart0_dtr_dsr: usart0_dtr_dsr-0 {
+ atmel,pins =
+ <AT91_PIOD 14 AT91_PERIPH_A AT91_PINCTRL_NONE>,
+ <AT91_PIOD 15 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart0_dcd: usart0_dcd-0 {
+ atmel,pins =
+ <AT91_PIOD 16 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart0_ri: usart0_ri-0 {
+ atmel,pins =
+ <AT91_PIOD 17 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart0_sck: usart0_sck-0 {
+ atmel,pins =
+ <AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+ };
+
+ usart1 {
+ pinctrl_usart1: usart1-0 {
+ atmel,pins =
+ <AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart1_rts: usart1_rts-0 {
+ atmel,pins =
+ <AT91_PIOA 18 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart1_cts: usart1_cts-0 {
+ atmel,pins =
+ <AT91_PIOA 19 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart1_sck: usart1_sck-0 {
+ atmel,pins =
+ <AT91_PIOD 2 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+ };
+
+ usart2 {
+ pinctrl_usart2: usart2-0 {
+ atmel,pins =
+ <AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart2_rts: usart2_rts-0 {
+ atmel,pins =
+ <AT91_PIOA 29 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart2_cts: usart2_cts-0 {
+ atmel,pins =
+ <AT91_PIOA 30 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart2_sck: usart2_sck-0 {
+ atmel,pins =
+ <AT91_PIOD 9 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+ };
+
+ usart3 {
+ pinctrl_usart3: usart3-0 {
+ atmel,pins =
+ <AT91_PIOB 0 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>,
+ <AT91_PIOB 1 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart3_rts: usart3_rts-0 {
+ atmel,pins =
+ <AT91_PIOD 4 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart3_cts: usart3_cts-0 {
+ atmel,pins =
+ <AT91_PIOD 3 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+
+ pinctrl_usart3_sck: usart3_sck-0 {
+ atmel,pins =
+ <AT91_PIOA 20 AT91_PERIPH_B AT91_PINCTRL_NONE>;
+ };
+ };
+
+ pioA: gpio@fffff400 {
+ compatible = "atmel,at91rm9200-gpio";
+ reg = <0xfffff400 0x200>;
+ interrupts = <2 IRQ_TYPE_LEVEL_HIGH 1>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&pioA_clk>;
+ };
+
+ pioB: gpio@fffff600 {
+ compatible = "atmel,at91rm9200-gpio";
+ reg = <0xfffff600 0x200>;
+ interrupts = <3 IRQ_TYPE_LEVEL_HIGH 1>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&pioB_clk>;
+ };
+
+ pioC: gpio@fffff800 {
+ compatible = "atmel,at91rm9200-gpio";
+ reg = <0xfffff800 0x200>;
+ interrupts = <4 IRQ_TYPE_LEVEL_HIGH 1>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&pioC_clk>;
+ };
+
+ pioD: gpio@fffffa00 {
+ compatible = "atmel,at91rm9200-gpio";
+ reg = <0xfffffa00 0x200>;
+ interrupts = <5 IRQ_TYPE_LEVEL_HIGH 1>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&pioD_clk>;
+ };
+ };
+
+ pmc: pmc@fffffc00 {
+ compatible = "atmel,at91sam9g45-pmc";
+ reg = <0xfffffc00 0x100>;
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
+ interrupt-controller;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+ clk32k: slck {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <32768>;
+ };
+
+ main: mainck {
+ compatible = "atmel,at91rm9200-clk-main";
+ #clock-cells = <0>;
+ interrupts-extended = <&pmc AT91_PMC_MOSCS>;
+ clocks = <&clk32k>;
+ };
+
+ plla: pllack {
+ compatible = "atmel,at91rm9200-clk-pll";
+ #clock-cells = <0>;
+ interrupts-extended = <&pmc AT91_PMC_LOCKA>;
+ clocks = <&main>;
+ reg = <0>;
+ atmel,clk-input-range = <1000000 32000000>;
+ #atmel,pll-clk-output-range-cells = <4>;
+ atmel,pll-clk-output-ranges = <80000000 200000000 190000000 240000000>;
+ };
+
+ utmi: utmick {
+ compatible = "atmel,at91sam9x5-clk-utmi";
+ #clock-cells = <0>;
+ interrupt-parent = <&pmc>;
+ interrupts = <AT91_PMC_LOCKU>;
+ clocks = <&main>;
+ };
+
+ mck: masterck {
+ compatible = "atmel,at91rm9200-clk-master";
+ #clock-cells = <0>;
+ interrupts-extended = <&pmc AT91_PMC_MCKRDY>;
+ clocks = <&clk32k>, <&main>, <&plla>, <&utmi>;
+ atmel,clk-output-range = <0 94000000>;
+ atmel,clk-divisors = <1 2 4 3>;
+ };
+
+ prog: progck {
+ compatible = "atmel,at91rm9200-clk-programmable";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-parent = <&pmc>;
+ clocks = <&clk32k>, <&main>, <&plla>, <&utmi>, <&mck>;
+
+ prog0: prog0 {
+ #clock-cells = <0>;
+ reg = <0>;
+ interrupts = <AT91_PMC_PCKRDY(0)>;
+ };
+
+ prog1: prog1 {
+ #clock-cells = <0>;
+ reg = <1>;
+ interrupts = <AT91_PMC_PCKRDY(1)>;
+ };
+ };
+
+ systemck {
+ compatible = "atmel,at91rm9200-clk-system";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pck0: pck0 {
+ #clock-cells = <0>;
+ reg = <8>;
+ clocks = <&prog0>;
+ };
+
+ pck1: pck1 {
+ #clock-cells = <0>;
+ reg = <9>;
+ clocks = <&prog1>;
+ };
+
+ };
+
+ periphck {
+ compatible = "atmel,at91rm9200-clk-peripheral";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&mck>;
+
+ pioA_clk: pioA_clk {
+ #clock-cells = <0>;
+ reg = <2>;
+ };
+
+ pioB_clk: pioB_clk {
+ #clock-cells = <0>;
+ reg = <3>;
+ };
+
+ pioC_clk: pioC_clk {
+ #clock-cells = <0>;
+ reg = <4>;
+ };
+
+ pioD_clk: pioD_clk {
+ #clock-cells = <0>;
+ reg = <5>;
+ };
+
+ usart0_clk: usart0_clk {
+ #clock-cells = <0>;
+ reg = <6>;
+ };
+
+ usart1_clk: usart1_clk {
+ #clock-cells = <0>;
+ reg = <7>;
+ };
+
+ usart2_clk: usart2_clk {
+ #clock-cells = <0>;
+ reg = <8>;
+ };
+
+ usart3_clk: usart3_clk {
+ #clock-cells = <0>;
+ reg = <9>;
+ };
+
+ mci0_clk: mci0_clk {
+ #clock-cells = <0>;
+ reg = <10>;
+ };
+
+ twi0_clk: twi0_clk {
+ #clock-cells = <0>;
+ reg = <11>;
+ };
+
+ twi1_clk: twi1_clk {
+ #clock-cells = <0>;
+ reg = <12>;
+ };
+
+ spi0_clk: spi0_clk {
+ #clock-cells = <0>;
+ reg = <13>;
+ };
+
+ ssc0_clk: ssc0_clk {
+ #clock-cells = <0>;
+ reg = <14>;
+ };
+
+ ssc1_clk: ssc1_clk {
+ #clock-cells = <0>;
+ reg = <15>;
+ };
+
+ tc0_clk: tc0_clk {
+ #clock-cells = <0>;
+ reg = <16>;
+ };
+
+ tc1_clk: tc1_clk {
+ #clock-cells = <0>;
+ reg = <17>;
+ };
+
+ tc2_clk: tc2_clk {
+ #clock-cells = <0>;
+ reg = <18>;
+ };
+
+ pwm_clk: pwm_clk {
+ #clock-cells = <0>;
+ reg = <19>;
+ };
+
+ adc_clk: adc_clk {
+ #clock-cells = <0>;
+ reg = <20>;
+ };
+
+ dma0_clk: dma0_clk {
+ #clock-cells = <0>;
+ reg = <21>;
+ };
+
+ udphs_clk: udphs_clk {
+ #clock-cells = <0>;
+ reg = <22>;
+ };
+
+ lcd_clk: lcd_clk {
+ #clock-cells = <0>;
+ reg = <23>;
+ };
+ };
+ };
+
+ rstc@fffffd00 {
+ compatible = "atmel,at91sam9260-rstc";
+ reg = <0xfffffd00 0x10>;
+ };
+
+ shdwc@fffffd10 {
+ compatible = "atmel,at91sam9260-shdwc";
+ reg = <0xfffffd10 0x10>;
+ };
+
+ pit: timer@fffffd30 {
+ compatible = "atmel,at91sam9260-pit";
+ reg = <0xfffffd30 0xf>;
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
+ clocks = <&mck>;
+ };
+
+ watchdog@fffffd40 {
+ compatible = "atmel,at91sam9260-wdt";
+ reg = <0xfffffd40 0x10>;
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
+ status = "disabled";
+ };
+ };
+ };
+
+ i2c@0 {
+ compatible = "i2c-gpio";
+ gpios = <&pioA 23 GPIO_ACTIVE_HIGH>, /* sda */
+ <&pioA 24 GPIO_ACTIVE_HIGH>; /* scl */
+ i2c-gpio,sda-open-drain;
+ i2c-gpio,scl-open-drain;
+ i2c-gpio,delay-us = <2>; /* ~100 kHz */
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c_gpio0>;
+ status = "disabled";
+ };
+
+ i2c@1 {
+ compatible = "i2c-gpio";
+ gpios = <&pioD 10 GPIO_ACTIVE_HIGH>, /* sda */
+ <&pioD 11 GPIO_ACTIVE_HIGH>; /* scl */
+ i2c-gpio,sda-open-drain;
+ i2c-gpio,scl-open-drain;
+ i2c-gpio,delay-us = <2>; /* ~100 kHz */
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c_gpio1>;
+ status = "disabled";
+ };
+};
diff --git a/arch/arm/boot/dts/at91sam9rlek.dts b/arch/arm/boot/dts/at91sam9rlek.dts
new file mode 100644
index 000000000000..cddb37825fad
--- /dev/null
+++ b/arch/arm/boot/dts/at91sam9rlek.dts
@@ -0,0 +1,157 @@
+/*
+ * at91sam9rlek.dts - Device Tree file for Atmel at91sam9rl reference board
+ *
+ * Copyright (C) 2014 Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ *
+ * Licensed under GPLv2 only
+ */
+/dts-v1/;
+#include "at91sam9rl.dtsi"
+
+/ {
+ model = "Atmel at91sam9rlek";
+ compatible = "atmel,at91sam9rlek", "atmel,at91sam9rl", "atmel,at91sam9";
+
+ chosen {
+ bootargs = "console=ttyS0,115200 rootfstype=ubifs root=ubi0:rootfs ubi.mtd=5 rw";
+ };
+
+ memory {
+ reg = <0x20000000 0x4000000>;
+ };
+
+ clocks {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ main_clock: clock {
+ compatible = "atmel,osc", "fixed-clock";
+ clock-frequency = <12000000>;
+ };
+ };
+
+ ahb {
+ nand0: nand@40000000 {
+ nand-bus-width = <8>;
+ nand-ecc-mode = "soft";
+ nand-on-flash-bbt = <1>;
+ status = "okay";
+
+ at91bootstrap@0 {
+ label = "at91bootstrap";
+ reg = <0x0 0x40000>;
+ };
+
+ bootloader@40000 {
+ label = "bootloader";
+ reg = <0x40000 0x80000>;
+ };
+
+ bootloaderenv@c0000 {
+ label = "bootloader env";
+ reg = <0xc0000 0xc0000>;
+ };
+
+ dtb@180000 {
+ label = "device tree";
+ reg = <0x180000 0x80000>;
+ };
+
+ kernel@200000 {
+ label = "kernel";
+ reg = <0x200000 0x600000>;
+ };
+
+ rootfs@800000 {
+ label = "rootfs";
+ reg = <0x800000 0x0f800000>;
+ };
+ };
+
+ apb {
+ mmc0: mmc@fffa4000 {
+ pinctrl-0 = <
+ &pinctrl_board_mmc0
+ &pinctrl_mmc0_clk
+ &pinctrl_mmc0_slot0_cmd_dat0
+ &pinctrl_mmc0_slot0_dat1_3>;
+ status = "okay";
+ slot@0 {
+ reg = <0>;
+ bus-width = <4>;
+ cd-gpios = <&pioA 15 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ usart0: serial@fffb0000 {
+ pinctrl-0 = <
+ &pinctrl_usart0
+ &pinctrl_usart0_rts
+ &pinctrl_usart0_cts>;
+ status = "okay";
+ };
+
+ dbgu: serial@fffff200 {
+ status = "okay";
+ };
+
+ pinctrl@fffff400 {
+ mmc0 {
+ pinctrl_board_mmc0: mmc0-board {
+ atmel,pins =
+ <AT91_PIOA 15 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP_DEGLITCH>;
+ };
+ };
+ };
+
+ pmc: pmc@fffffc00 {
+ main: mainck {
+ clock-frequency = <12000000>;
+ };
+ };
+
+ watchdog@fffffd40 {
+ status = "okay";
+ };
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ ds1 {
+ label = "ds1";
+ gpios = <&pioD 15 GPIO_ACTIVE_LOW>;
+ };
+
+ ds2 {
+ label = "ds2";
+ gpios = <&pioD 16 GPIO_ACTIVE_LOW>;
+ };
+
+ ds3 {
+ label = "ds3";
+ gpios = <&pioD 14 GPIO_ACTIVE_HIGH>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+
+ right_click {
+ label = "right_click";
+ gpios = <&pioB 0 GPIO_ACTIVE_LOW>;
+ linux,code = <273>;
+ gpio-key,wakeup;
+ };
+
+ left_click {
+ label = "left_click";
+ gpios = <&pioB 1 GPIO_ACTIVE_LOW>;
+ linux,code = <272>;
+ gpio-key,wakeup;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi
index 174219de92fa..fc13c9240da8 100644
--- a/arch/arm/boot/dts/at91sam9x5.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5.dtsi
@@ -621,41 +621,42 @@
};
adc0: adc@f804c000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
compatible = "atmel,at91sam9260-adc";
reg = <0xf804c000 0x100>;
interrupts = <19 IRQ_TYPE_LEVEL_HIGH 0>;
- atmel,adc-use-external;
+ atmel,adc-use-external-triggers;
atmel,adc-channels-used = <0xffff>;
atmel,adc-vref = <3300>;
- atmel,adc-num-channels = <12>;
atmel,adc-startup-time = <40>;
- atmel,adc-channel-base = <0x50>;
- atmel,adc-drdy-mask = <0x1000000>;
- atmel,adc-status-register = <0x30>;
- atmel,adc-trigger-register = <0xc0>;
atmel,adc-res = <8 10>;
atmel,adc-res-names = "lowres", "highres";
atmel,adc-use-res = "highres";
trigger@0 {
+ reg = <0>;
trigger-name = "external-rising";
trigger-value = <0x1>;
trigger-external;
};
trigger@1 {
+ reg = <1>;
trigger-name = "external-falling";
trigger-value = <0x2>;
trigger-external;
};
trigger@2 {
+ reg = <2>;
trigger-name = "external-any";
trigger-value = <0x3>;
trigger-external;
};
trigger@3 {
+ reg = <3>;
trigger-name = "continuous";
trigger-value = <0x6>;
};
@@ -790,6 +791,7 @@
atmel,pmecc-lookup-table-offset = <0x0 0x8000>;
atmel,nand-addr-offset = <21>;
atmel,nand-cmd-offset = <22>;
+ atmel,nand-has-dma;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_nand>;
gpios = <&pioD 5 GPIO_ACTIVE_HIGH
diff --git a/arch/arm/boot/dts/atlas6.dtsi b/arch/arm/boot/dts/atlas6.dtsi
index 0c81dc945aed..55d3f79c2ef5 100644
--- a/arch/arm/boot/dts/atlas6.dtsi
+++ b/arch/arm/boot/dts/atlas6.dtsi
@@ -65,9 +65,10 @@
#clock-cells = <1>;
};
- reset-controller@88010000 {
+ rstc: reset-controller@88010000 {
compatible = "sirf,prima2-rstc";
reg = <0x88010000 0x1000>;
+ #reset-cells = <1>;
};
rsc-controller@88020000 {
diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi
index 792fde1b7f75..64d069bcc409 100644
--- a/arch/arm/boot/dts/bcm11351.dtsi
+++ b/arch/arm/boot/dts/bcm11351.dtsi
@@ -14,6 +14,8 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
+#include "dt-bindings/clock/bcm281xx.h"
+
#include "skeleton.dtsi"
/ {
@@ -43,7 +45,7 @@
compatible = "brcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart";
status = "disabled";
reg = <0x3e000000 0x1000>;
- clocks = <&uartb_clk>;
+ clocks = <&slave_ccu BCM281XX_SLAVE_CCU_UARTB>;
interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
@@ -53,7 +55,7 @@
compatible = "brcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart";
status = "disabled";
reg = <0x3e001000 0x1000>;
- clocks = <&uartb2_clk>;
+ clocks = <&slave_ccu BCM281XX_SLAVE_CCU_UARTB2>;
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
@@ -63,7 +65,7 @@
compatible = "brcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart";
status = "disabled";
reg = <0x3e002000 0x1000>;
- clocks = <&uartb3_clk>;
+ clocks = <&slave_ccu BCM281XX_SLAVE_CCU_UARTB3>;
interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
@@ -73,7 +75,7 @@
compatible = "brcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart";
status = "disabled";
reg = <0x3e003000 0x1000>;
- clocks = <&uartb4_clk>;
+ clocks = <&slave_ccu BCM281XX_SLAVE_CCU_UARTB4>;
interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
@@ -95,7 +97,7 @@
compatible = "brcm,kona-timer";
reg = <0x35006000 0x1000>;
interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&hub_timer_clk>;
+ clocks = <&aon_ccu BCM281XX_AON_CCU_HUB_TIMER>;
};
gpio: gpio@35003000 {
@@ -118,7 +120,7 @@
compatible = "brcm,kona-sdhci";
reg = <0x3f180000 0x10000>;
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&sdio1_clk>;
+ clocks = <&master_ccu BCM281XX_MASTER_CCU_SDIO1>;
status = "disabled";
};
@@ -126,7 +128,7 @@
compatible = "brcm,kona-sdhci";
reg = <0x3f190000 0x10000>;
interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&sdio2_clk>;
+ clocks = <&master_ccu BCM281XX_MASTER_CCU_SDIO2>;
status = "disabled";
};
@@ -134,7 +136,7 @@
compatible = "brcm,kona-sdhci";
reg = <0x3f1a0000 0x10000>;
interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&sdio3_clk>;
+ clocks = <&master_ccu BCM281XX_MASTER_CCU_SDIO3>;
status = "disabled";
};
@@ -142,7 +144,7 @@
compatible = "brcm,kona-sdhci";
reg = <0x3f1b0000 0x10000>;
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&sdio4_clk>;
+ clocks = <&master_ccu BCM281XX_MASTER_CCU_SDIO4>;
status = "disabled";
};
@@ -157,7 +159,7 @@
interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&bsc1_clk>;
+ clocks = <&slave_ccu BCM281XX_SLAVE_CCU_BSC1>;
status = "disabled";
};
@@ -167,7 +169,7 @@
interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&bsc2_clk>;
+ clocks = <&slave_ccu BCM281XX_SLAVE_CCU_BSC2>;
status = "disabled";
};
@@ -177,7 +179,7 @@
interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&bsc3_clk>;
+ clocks = <&slave_ccu BCM281XX_SLAVE_CCU_BSC3>;
status = "disabled";
};
@@ -187,105 +189,191 @@
interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&pmu_bsc_clk>;
+ clocks = <&aon_ccu BCM281XX_AON_CCU_PMU_BSC>;
status = "disabled";
};
clocks {
- bsc1_clk: bsc1 {
- compatible = "fixed-clock";
- clock-frequency = <13000000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ root_ccu: root_ccu {
+ compatible = "brcm,bcm11351-root-ccu";
+ reg = <0x35001000 0x0f00>;
+ #clock-cells = <1>;
+ clock-output-names = "frac_1m";
+ };
+
+ hub_ccu: hub_ccu {
+ compatible = "brcm,bcm11351-hub-ccu";
+ reg = <0x34000000 0x0f00>;
+ #clock-cells = <1>;
+ clock-output-names = "tmon_1m";
+ };
+
+ aon_ccu: aon_ccu {
+ compatible = "brcm,bcm11351-aon-ccu";
+ reg = <0x35002000 0x0f00>;
+ #clock-cells = <1>;
+ clock-output-names = "hub_timer",
+ "pmu_bsc",
+ "pmu_bsc_var";
+ };
+
+ master_ccu: master_ccu {
+ compatible = "brcm,bcm11351-master-ccu";
+ reg = <0x3f001000 0x0f00>;
+ #clock-cells = <1>;
+ clock-output-names = "sdio1",
+ "sdio2",
+ "sdio3",
+ "sdio4",
+ "usb_ic",
+ "hsic2_48m",
+ "hsic2_12m";
+ };
+
+ slave_ccu: slave_ccu {
+ compatible = "brcm,bcm11351-slave-ccu";
+ reg = <0x3e011000 0x0f00>;
+ #clock-cells = <1>;
+ clock-output-names = "uartb",
+ "uartb2",
+ "uartb3",
+ "uartb4",
+ "ssp0",
+ "ssp2",
+ "bsc1",
+ "bsc2",
+ "bsc3",
+ "pwm";
+ };
+
+ ref_1m_clk: ref_1m {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <1000000>;
};
- bsc2_clk: bsc2 {
+ ref_32k_clk: ref_32k {
+ #clock-cells = <0>;
compatible = "fixed-clock";
- clock-frequency = <13000000>;
+ clock-frequency = <32768>;
+ };
+
+ bbl_32k_clk: bbl_32k {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
};
- bsc3_clk: bsc3 {
+ ref_13m_clk: ref_13m {
+ #clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <13000000>;
- #clock-cells = <0>;
};
- pmu_bsc_clk: pmu_bsc {
+ var_13m_clk: var_13m {
+ #clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <13000000>;
- #clock-cells = <0>;
};
- hub_timer_clk: hub_timer {
- compatible = "fixed-clock";
- clock-frequency = <32768>;
+ dft_19_5m_clk: dft_19_5m {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <19500000>;
};
- pwm_clk: pwm {
+ ref_crystal_clk: ref_crystal {
+ #clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <26000000>;
- #clock-cells = <0>;
};
- sdio1_clk: sdio1 {
- compatible = "fixed-clock";
- clock-frequency = <48000000>;
+ ref_cx40_clk: ref_cx40 {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <40000000>;
};
- sdio2_clk: sdio2 {
- compatible = "fixed-clock";
- clock-frequency = <48000000>;
+ ref_52m_clk: ref_52m {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <52000000>;
};
- sdio3_clk: sdio3 {
- compatible = "fixed-clock";
- clock-frequency = <48000000>;
+ var_52m_clk: var_52m {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <52000000>;
};
- sdio4_clk: sdio4 {
+ usb_otg_ahb_clk: usb_otg_ahb {
compatible = "fixed-clock";
- clock-frequency = <48000000>;
+ clock-frequency = <52000000>;
#clock-cells = <0>;
};
- tmon_1m_clk: tmon_1m {
- compatible = "fixed-clock";
- clock-frequency = <1000000>;
+ ref_96m_clk: ref_96m {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <96000000>;
};
- uartb_clk: uartb {
- compatible = "fixed-clock";
- clock-frequency = <13000000>;
+ var_96m_clk: var_96m {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <96000000>;
};
- uartb2_clk: uartb2 {
+ ref_104m_clk: ref_104m {
+ #clock-cells = <0>;
compatible = "fixed-clock";
- clock-frequency = <13000000>;
+ clock-frequency = <104000000>;
+ };
+
+ var_104m_clk: var_104m {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <104000000>;
};
- uartb3_clk: uartb3 {
+ ref_156m_clk: ref_156m {
+ #clock-cells = <0>;
compatible = "fixed-clock";
- clock-frequency = <13000000>;
+ clock-frequency = <156000000>;
+ };
+
+ var_156m_clk: var_156m {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <156000000>;
};
- uartb4_clk: uartb4 {
+ ref_208m_clk: ref_208m {
+ #clock-cells = <0>;
compatible = "fixed-clock";
- clock-frequency = <13000000>;
+ clock-frequency = <208000000>;
+ };
+
+ var_208m_clk: var_208m {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <208000000>;
};
- usb_otg_ahb_clk: usb_otg_ahb {
+ ref_312m_clk: ref_312m {
+ #clock-cells = <0>;
compatible = "fixed-clock";
- clock-frequency = <52000000>;
+ clock-frequency = <312000000>;
+ };
+
+ var_312m_clk: var_312m {
#clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <312000000>;
};
};
diff --git a/arch/arm/boot/dts/bcm11351-brt.dts b/arch/arm/boot/dts/bcm21664-garnet.dts
index 396b70459cdc..e87cb26ddf84 100644
--- a/arch/arm/boot/dts/bcm11351-brt.dts
+++ b/arch/arm/boot/dts/bcm21664-garnet.dts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Broadcom Corporation
+ * Copyright (C) 2014 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
@@ -13,11 +13,13 @@
/dts-v1/;
-#include "bcm11351.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+
+#include "bcm21664.dtsi"
/ {
- model = "BCM11351 BRT board";
- compatible = "brcm,bcm11351-brt", "brcm,bcm11351";
+ model = "BCM21664 Garnet board";
+ compatible = "brcm,bcm21664-garnet", "brcm,bcm21664";
memory {
reg = <0x80000000 0x40000000>; /* 1 GB */
@@ -40,7 +42,7 @@
sdio4: sdio@3f1b0000 {
max-frequency = <48000000>;
- cd-gpios = <&gpio 14 0>;
+ cd-gpios = <&gpio 91 GPIO_ACTIVE_LOW>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/bcm21664.dtsi b/arch/arm/boot/dts/bcm21664.dtsi
new file mode 100644
index 000000000000..08a44d41b672
--- /dev/null
+++ b/arch/arm/boot/dts/bcm21664.dtsi
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "skeleton.dtsi"
+
+/ {
+ model = "BCM21664 SoC";
+ compatible = "brcm,bcm21664";
+ interrupt-parent = <&gic>;
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gic: interrupt-controller@3ff00100 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0x3ff01000 0x1000>,
+ <0x3ff00100 0x100>;
+ };
+
+ smc@0x3404e000 {
+ compatible = "brcm,bcm21664-smc", "brcm,kona-smc";
+ reg = <0x3404e000 0x400>; /* 1 KiB in SRAM */
+ };
+
+ uart@3e000000 {
+ compatible = "brcm,bcm21664-dw-apb-uart", "snps,dw-apb-uart";
+ status = "disabled";
+ reg = <0x3e000000 0x118>;
+ clocks = <&uartb_clk>;
+ interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ };
+
+ uart@3e001000 {
+ compatible = "brcm,bcm21664-dw-apb-uart", "snps,dw-apb-uart";
+ status = "disabled";
+ reg = <0x3e001000 0x118>;
+ clocks = <&uartb2_clk>;
+ interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ };
+
+ uart@3e002000 {
+ compatible = "brcm,bcm21664-dw-apb-uart", "snps,dw-apb-uart";
+ status = "disabled";
+ reg = <0x3e002000 0x118>;
+ clocks = <&uartb3_clk>;
+ interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ };
+
+ L2: l2-cache {
+ compatible = "arm,pl310-cache";
+ reg = <0x3ff20000 0x1000>;
+ cache-unified;
+ cache-level = <2>;
+ };
+
+ brcm,resetmgr@35001f00 {
+ compatible = "brcm,bcm21664-resetmgr";
+ reg = <0x35001f00 0x24>;
+ };
+
+ timer@35006000 {
+ compatible = "brcm,kona-timer";
+ reg = <0x35006000 0x1c>;
+ interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&hub_timer_clk>;
+ };
+
+ gpio: gpio@35003000 {
+ compatible = "brcm,bcm21664-gpio", "brcm,kona-gpio";
+ reg = <0x35003000 0x524>;
+ interrupts =
+ <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH
+ GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH
+ GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH
+ GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>;
+ #gpio-cells = <2>;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ };
+
+ sdio1: sdio@3f180000 {
+ compatible = "brcm,kona-sdhci";
+ reg = <0x3f180000 0x801c>;
+ interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&sdio1_clk>;
+ status = "disabled";
+ };
+
+ sdio2: sdio@3f190000 {
+ compatible = "brcm,kona-sdhci";
+ reg = <0x3f190000 0x801c>;
+ interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&sdio2_clk>;
+ status = "disabled";
+ };
+
+ sdio3: sdio@3f1a0000 {
+ compatible = "brcm,kona-sdhci";
+ reg = <0x3f1a0000 0x801c>;
+ interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&sdio3_clk>;
+ status = "disabled";
+ };
+
+ sdio4: sdio@3f1b0000 {
+ compatible = "brcm,kona-sdhci";
+ reg = <0x3f1b0000 0x801c>;
+ interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&sdio4_clk>;
+ status = "disabled";
+ };
+
+ i2c@3e016000 {
+ compatible = "brcm,bcm21664-i2c", "brcm,kona-i2c";
+ reg = <0x3e016000 0x70>;
+ interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&bsc1_clk>;
+ status = "disabled";
+ };
+
+ i2c@3e017000 {
+ compatible = "brcm,bcm21664-i2c", "brcm,kona-i2c";
+ reg = <0x3e017000 0x70>;
+ interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&bsc2_clk>;
+ status = "disabled";
+ };
+
+ i2c@3e018000 {
+ compatible = "brcm,bcm21664-i2c", "brcm,kona-i2c";
+ reg = <0x3e018000 0x70>;
+ interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&bsc3_clk>;
+ status = "disabled";
+ };
+
+ i2c@3e01c000 {
+ compatible = "brcm,bcm21664-i2c", "brcm,kona-i2c";
+ reg = <0x3e01c000 0x70>;
+ interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&bsc4_clk>;
+ status = "disabled";
+ };
+
+ clocks {
+ bsc1_clk: bsc1 {
+ compatible = "fixed-clock";
+ clock-frequency = <13000000>;
+ #clock-cells = <0>;
+ };
+
+ bsc2_clk: bsc2 {
+ compatible = "fixed-clock";
+ clock-frequency = <13000000>;
+ #clock-cells = <0>;
+ };
+
+ bsc3_clk: bsc3 {
+ compatible = "fixed-clock";
+ clock-frequency = <13000000>;
+ #clock-cells = <0>;
+ };
+
+ bsc4_clk: bsc4 {
+ compatible = "fixed-clock";
+ clock-frequency = <13000000>;
+ #clock-cells = <0>;
+ };
+
+ pmu_bsc_clk: pmu_bsc {
+ compatible = "fixed-clock";
+ clock-frequency = <13000000>;
+ #clock-cells = <0>;
+ };
+
+ hub_timer_clk: hub_timer {
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ #clock-cells = <0>;
+ };
+
+ pwm_clk: pwm {
+ compatible = "fixed-clock";
+ clock-frequency = <26000000>;
+ #clock-cells = <0>;
+ };
+
+ sdio1_clk: sdio1 {
+ compatible = "fixed-clock";
+ clock-frequency = <48000000>;
+ #clock-cells = <0>;
+ };
+
+ sdio2_clk: sdio2 {
+ compatible = "fixed-clock";
+ clock-frequency = <48000000>;
+ #clock-cells = <0>;
+ };
+
+ sdio3_clk: sdio3 {
+ compatible = "fixed-clock";
+ clock-frequency = <48000000>;
+ #clock-cells = <0>;
+ };
+
+ sdio4_clk: sdio4 {
+ compatible = "fixed-clock";
+ clock-frequency = <48000000>;
+ #clock-cells = <0>;
+ };
+
+ tmon_1m_clk: tmon_1m {
+ compatible = "fixed-clock";
+ clock-frequency = <1000000>;
+ #clock-cells = <0>;
+ };
+
+ uartb_clk: uartb {
+ compatible = "fixed-clock";
+ clock-frequency = <13000000>;
+ #clock-cells = <0>;
+ };
+
+ uartb2_clk: uartb2 {
+ compatible = "fixed-clock";
+ clock-frequency = <13000000>;
+ #clock-cells = <0>;
+ };
+
+ uartb3_clk: uartb3 {
+ compatible = "fixed-clock";
+ clock-frequency = <13000000>;
+ #clock-cells = <0>;
+ };
+
+ usb_otg_ahb_clk: usb_otg_ahb {
+ compatible = "fixed-clock";
+ clock-frequency = <52000000>;
+ #clock-cells = <0>;
+ };
+ };
+
+ usbotg: usb@3f120000 {
+ compatible = "snps,dwc2";
+ reg = <0x3f120000 0x10000>;
+ interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&usb_otg_ahb_clk>;
+ clock-names = "otg";
+ phys = <&usbphy>;
+ phy-names = "usb2-phy";
+ status = "disabled";
+ };
+
+ usbphy: usb-phy@3f130000 {
+ compatible = "brcm,kona-usb2-phy";
+ reg = <0x3f130000 0x28>;
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+};
diff --git a/arch/arm/boot/dts/bcm28155-ap.dts b/arch/arm/boot/dts/bcm28155-ap.dts
index 5ff2382a49e4..af3da55eef49 100644
--- a/arch/arm/boot/dts/bcm28155-ap.dts
+++ b/arch/arm/boot/dts/bcm28155-ap.dts
@@ -46,27 +46,32 @@
i2c@3500d000 {
status="okay";
- clock-frequency = <400000>;
- };
+ clock-frequency = <100000>;
- sdio1: sdio@3f180000 {
- max-frequency = <48000000>;
- status = "okay";
+ pmu: pmu@8 {
+ reg = <0x08>;
+ };
};
sdio2: sdio@3f190000 {
non-removable;
max-frequency = <48000000>;
+ vmmc-supply = <&camldo1_reg>;
+ vqmmc-supply = <&iosr1_reg>;
status = "okay";
};
sdio4: sdio@3f1b0000 {
max-frequency = <48000000>;
cd-gpios = <&gpio 14 GPIO_ACTIVE_LOW>;
+ vmmc-supply = <&sdldo_reg>;
+ vqmmc-supply = <&sdxldo_reg>;
status = "okay";
};
usbotg: usb@3f120000 {
+ vusb_d-supply = <&usbldo_reg>;
+ vusb_a-supply = <&iosr1_reg>;
status = "okay";
};
@@ -74,3 +79,39 @@
status = "okay";
};
};
+
+#include "bcm59056.dtsi"
+
+&pmu {
+ compatible = "brcm,bcm59056";
+ interrupts = <GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>;
+ regulators {
+ camldo1_reg: camldo1 {
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ sdldo_reg: sdldo {
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ };
+
+ sdxldo_reg: sdxldo {
+ regulator-min-microvolt = <2700000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ usbldo_reg: usbldo {
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ iosr1_reg: iosr1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index b021c96d3ba1..b8473c43e888 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -15,39 +15,52 @@
#size-cells = <1>;
ranges = <0x7e000000 0x20000000 0x02000000>;
- timer {
+ timer@7e003000 {
compatible = "brcm,bcm2835-system-timer";
reg = <0x7e003000 0x1000>;
interrupts = <1 0>, <1 1>, <1 2>, <1 3>;
clock-frequency = <1000000>;
};
- intc: interrupt-controller {
+ dma: dma@7e007000 {
+ compatible = "brcm,bcm2835-dma";
+ reg = <0x7e007000 0xf00>;
+ interrupts = <1 16>,
+ <1 17>,
+ <1 18>,
+ <1 19>,
+ <1 20>,
+ <1 21>,
+ <1 22>,
+ <1 23>,
+ <1 24>,
+ <1 25>,
+ <1 26>,
+ <1 27>,
+ <1 28>;
+
+ #dma-cells = <1>;
+ brcm,dma-channel-mask = <0x7f35>;
+ };
+
+ intc: interrupt-controller@7e00b200 {
compatible = "brcm,bcm2835-armctrl-ic";
reg = <0x7e00b200 0x200>;
interrupt-controller;
#interrupt-cells = <2>;
};
- watchdog {
+ watchdog@7e100000 {
compatible = "brcm,bcm2835-pm-wdt";
reg = <0x7e100000 0x28>;
};
- rng {
+ rng@7e104000 {
compatible = "brcm,bcm2835-rng";
reg = <0x7e104000 0x10>;
};
- uart@20201000 {
- compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell";
- reg = <0x7e201000 0x1000>;
- interrupts = <2 25>;
- clock-frequency = <3000000>;
- arm,primecell-periphid = <0x00241011>;
- };
-
- gpio: gpio {
+ gpio: gpio@7e200000 {
compatible = "brcm,bcm2835-gpio";
reg = <0x7e200000 0xb4>;
/*
@@ -70,7 +83,25 @@
#interrupt-cells = <2>;
};
- spi: spi@20204000 {
+ uart@7e201000 {
+ compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell";
+ reg = <0x7e201000 0x1000>;
+ interrupts = <2 25>;
+ clock-frequency = <3000000>;
+ arm,primecell-periphid = <0x00241011>;
+ };
+
+ i2s: i2s@7e203000 {
+ compatible = "brcm,bcm2835-i2s";
+ reg = <0x7e203000 0x20>,
+ <0x7e101098 0x02>;
+
+ dmas = <&dma 2>,
+ <&dma 3>;
+ dma-names = "tx", "rx";
+ };
+
+ spi: spi@7e204000 {
compatible = "brcm,bcm2835-spi";
reg = <0x7e204000 0x1000>;
interrupts = <2 22>;
@@ -90,7 +121,15 @@
status = "disabled";
};
- i2c1: i2c@20804000 {
+ sdhci: sdhci@7e300000 {
+ compatible = "brcm,bcm2835-sdhci";
+ reg = <0x7e300000 0x100>;
+ interrupts = <2 30>;
+ clocks = <&clk_mmc>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@7e804000 {
compatible = "brcm,bcm2835-i2c";
reg = <0x7e804000 0x1000>;
interrupts = <2 21>;
@@ -100,19 +139,15 @@
status = "disabled";
};
- sdhci: sdhci {
- compatible = "brcm,bcm2835-sdhci";
- reg = <0x7e300000 0x100>;
- interrupts = <2 30>;
- clocks = <&clk_mmc>;
- status = "disabled";
- };
-
- usb {
+ usb@7e980000 {
compatible = "brcm,bcm2835-usb";
reg = <0x7e980000 0x10000>;
interrupts = <1 9>;
};
+
+ arm-pmu {
+ compatible = "arm,arm1176-pmu";
+ };
};
clocks {
@@ -120,24 +155,27 @@
#address-cells = <1>;
#size-cells = <0>;
- clk_mmc: mmc {
+ clk_mmc: clock@0 {
compatible = "fixed-clock";
reg = <0>;
#clock-cells = <0>;
+ clock-output-names = "mmc";
clock-frequency = <100000000>;
};
- clk_i2c: i2c {
+ clk_i2c: clock@1 {
compatible = "fixed-clock";
reg = <1>;
#clock-cells = <0>;
+ clock-output-names = "i2c";
clock-frequency = <250000000>;
};
- clk_spi: spi {
+ clk_spi: clock@2 {
compatible = "fixed-clock";
reg = <2>;
#clock-cells = <0>;
+ clock-output-names = "spi";
clock-frequency = <250000000>;
};
};
diff --git a/arch/arm/boot/dts/bcm4708-netgear-r6250.dts b/arch/arm/boot/dts/bcm4708-netgear-r6250.dts
new file mode 100644
index 000000000000..3b5259de5a38
--- /dev/null
+++ b/arch/arm/boot/dts/bcm4708-netgear-r6250.dts
@@ -0,0 +1,35 @@
+/*
+ * Broadcom BCM470X / BCM5301X arm platform code.
+ * DTS for Netgear R6250 V1
+ *
+ * Copyright 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+/dts-v1/;
+
+#include "bcm4708.dtsi"
+
+/ {
+ compatible = "netgear,r6250v1", "brcm,bcm4708";
+ model = "Netgear R6250 V1 (BCM4708)";
+
+ chosen {
+ bootargs = "console=ttyS0,115200";
+ };
+
+ memory {
+ reg = <0x00000000 0x08000000>;
+ };
+
+ chipcommonA {
+ uart0: serial@0300 {
+ status = "okay";
+ };
+
+ uart1: serial@0400 {
+ status = "okay";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/bcm4708.dtsi b/arch/arm/boot/dts/bcm4708.dtsi
new file mode 100644
index 000000000000..31141e83fedd
--- /dev/null
+++ b/arch/arm/boot/dts/bcm4708.dtsi
@@ -0,0 +1,34 @@
+/*
+ * Broadcom BCM470X / BCM5301X ARM platform code.
+ * DTS for BCM4708 SoC.
+ *
+ * Copyright 2013-2014 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcm5301x.dtsi"
+
+/ {
+ compatible = "brcm,bcm4708";
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ next-level-cache = <&L2>;
+ reg = <0x0>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ next-level-cache = <&L2>;
+ reg = <0x1>;
+ };
+ };
+
+};
diff --git a/arch/arm/boot/dts/bcm5301x.dtsi b/arch/arm/boot/dts/bcm5301x.dtsi
new file mode 100644
index 000000000000..53c624f766b4
--- /dev/null
+++ b/arch/arm/boot/dts/bcm5301x.dtsi
@@ -0,0 +1,95 @@
+/*
+ * Broadcom BCM470X / BCM5301X ARM platform code.
+ * Generic DTS part for all BCM53010, BCM53011, BCM53012, BCM53014, BCM53015,
+ * BCM53016, BCM53017, BCM53018, BCM4707, BCM4708 and BCM4709 SoCs
+ *
+ * Copyright 2013-2014 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "skeleton.dtsi"
+
+/ {
+ interrupt-parent = <&gic>;
+
+ chipcommonA {
+ compatible = "simple-bus";
+ ranges = <0x00000000 0x18000000 0x00001000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ uart0: serial@0300 {
+ compatible = "ns16550";
+ reg = <0x0300 0x100>;
+ interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
+ clock-frequency = <100000000>;
+ status = "disabled";
+ };
+
+ uart1: serial@0400 {
+ compatible = "ns16550";
+ reg = <0x0400 0x100>;
+ interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
+ clock-frequency = <100000000>;
+ status = "disabled";
+ };
+ };
+
+ mpcore {
+ compatible = "simple-bus";
+ ranges = <0x00000000 0x19020000 0x00003000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ scu@0000 {
+ compatible = "arm,cortex-a9-scu";
+ reg = <0x0000 0x100>;
+ };
+
+ timer@0200 {
+ compatible = "arm,cortex-a9-global-timer";
+ reg = <0x0200 0x100>;
+ interrupts = <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_periph>;
+ };
+
+ local-timer@0600 {
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = <0x0600 0x100>;
+ interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_periph>;
+ };
+
+ gic: interrupt-controller@1000 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0x1000 0x1000>,
+ <0x0100 0x100>;
+ };
+
+ L2: cache-controller@2000 {
+ compatible = "arm,pl310-cache";
+ reg = <0x2000 0x1000>;
+ cache-unified;
+ cache-level = <2>;
+ };
+ };
+
+ clocks {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* As long as we do not have a real clock driver us this
+ * fixed clock */
+ clk_periph: periph {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <400000000>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/bcm59056.dtsi b/arch/arm/boot/dts/bcm59056.dtsi
new file mode 100644
index 000000000000..dfadaaa89b05
--- /dev/null
+++ b/arch/arm/boot/dts/bcm59056.dtsi
@@ -0,0 +1,74 @@
+/*
+* Copyright 2014 Linaro Limited
+* Author: Matt Porter <mporter@linaro.org>
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
+
+&pmu {
+ compatible = "brcm,bcm59056";
+ regulators {
+ rfldo_reg: rfldo {
+ };
+
+ camldo1_reg: camldo1 {
+ };
+
+ camldo2_reg: camldo2 {
+ };
+
+ simldo1_reg: simldo1 {
+ };
+
+ simldo2_reg: simldo2 {
+ };
+
+ sdldo_reg: sdldo {
+ };
+
+ sdxldo_reg: sdxldo {
+ };
+
+ mmcldo1_reg: mmcldo1 {
+ };
+
+ mmcldo2_reg: mmcldo2 {
+ };
+
+ audldo_reg: audldo {
+ };
+
+ micldo_reg: micldo {
+ };
+
+ usbldo_reg: usbldo {
+ };
+
+ vibldo_reg: vibldo {
+ };
+
+ csr_reg: csr {
+ };
+
+ iosr1_reg: iosr1 {
+ };
+
+ iosr2_reg: iosr2 {
+ };
+
+ msr_reg: msr {
+ };
+
+ sdsr1_reg: sdsr1 {
+ };
+
+ sdsr2_reg: sdsr2 {
+ };
+
+ vsr_reg: vsr {
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
index 187fd46b7b5e..3b891dd20993 100644
--- a/arch/arm/boot/dts/dove.dtsi
+++ b/arch/arm/boot/dts/dove.dtsi
@@ -186,6 +186,11 @@
reg = <0x20000 0x80>, <0x800100 0x8>;
};
+ sysc: system-ctrl@20000 {
+ compatible = "marvell,orion-system-controller";
+ reg = <0x20000 0x110>;
+ };
+
bridge_intc: bridge-interrupt-ctrl@20110 {
compatible = "marvell,orion-bridge-intc";
interrupt-controller;
@@ -210,6 +215,14 @@
clocks = <&core_clk 0>;
};
+ watchdog@20300 {
+ compatible = "marvell,orion-wdt";
+ reg = <0x20300 0x28>, <0x20108 0x4>;
+ interrupt-parent = <&bridge_intc>;
+ interrupts = <3>;
+ clocks = <&core_clk 0>;
+ };
+
crypto: crypto-engine@30000 {
compatible = "marvell,orion-crypto";
reg = <0x30000 0x10000>,
@@ -381,7 +394,8 @@
pinctrl: pin-ctrl@d0200 {
compatible = "marvell,dove-pinctrl";
- reg = <0xd0200 0x10>;
+ reg = <0xd0200 0x14>,
+ <0xd0440 0x04>;
clocks = <&gate_clk 22>;
pmx_gpio_0: pmx-gpio-0 {
@@ -603,6 +617,12 @@
reg = <0xd8500 0x20>;
};
+ gconf: global-config@e802c {
+ compatible = "marvell,dove-global-config",
+ "syscon";
+ reg = <0xe802c 0x14>;
+ };
+
gpio2: gpio-ctrl@e8400 {
compatible = "marvell,orion-gpio";
#gpio-cells = <2>;
diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index 1fd75aa4639d..9e3caf3d19fb 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -47,6 +47,11 @@
1000000 1060000
1176000 1160000
>;
+
+ clocks = <&dpll_mpu_ck>;
+ clock-names = "cpu";
+
+ clock-latency = <300000>; /* From omap-cpufreq driver */
};
cpu@1 {
device_type = "cpu";
@@ -464,6 +469,20 @@
ti,hwmods = "wd_timer2";
};
+ hwspinlock: spinlock@4a0f6000 {
+ compatible = "ti,omap4-hwspinlock";
+ reg = <0x4a0f6000 0x1000>;
+ ti,hwmods = "spinlock";
+ #hwlock-cells = <1>;
+ };
+
+ dmm@4e000000 {
+ compatible = "ti,omap5-dmm";
+ reg = <0x4e000000 0x800>;
+ interrupts = <0 113 0x4>;
+ ti,hwmods = "dmm";
+ };
+
i2c1: i2c@48070000 {
compatible = "ti,omap4-i2c";
reg = <0x48070000 0x100>;
@@ -559,6 +578,138 @@
status = "disabled";
};
+ abb_mpu: regulator-abb-mpu {
+ compatible = "ti,abb-v3";
+ regulator-name = "abb_mpu";
+ #address-cells = <0>;
+ #size-cells = <0>;
+ clocks = <&sys_clkin1>;
+ ti,settling-time = <50>;
+ ti,clock-cycles = <16>;
+
+ reg = <0x4ae07ddc 0x4>, <0x4ae07de0 0x4>,
+ <0x4ae06014 0x4>, <0x4a003b20 0x8>,
+ <0x4ae0c158 0x4>;
+ reg-names = "setup-address", "control-address",
+ "int-address", "efuse-address",
+ "ldo-address";
+ ti,tranxdone-status-mask = <0x80>;
+ /* LDOVBBMPU_FBB_MUX_CTRL */
+ ti,ldovbb-override-mask = <0x400>;
+ /* LDOVBBMPU_FBB_VSET_OUT */
+ ti,ldovbb-vset-mask = <0x1F>;
+
+ /*
+ * NOTE: only FBB mode used but actual vset will
+ * determine final biasing
+ */
+ ti,abb_info = <
+ /*uV ABB efuse rbb_m fbb_m vset_m*/
+ 1060000 0 0x0 0 0x02000000 0x01F00000
+ 1160000 0 0x4 0 0x02000000 0x01F00000
+ 1210000 0 0x8 0 0x02000000 0x01F00000
+ >;
+ };
+
+ abb_ivahd: regulator-abb-ivahd {
+ compatible = "ti,abb-v3";
+ regulator-name = "abb_ivahd";
+ #address-cells = <0>;
+ #size-cells = <0>;
+ clocks = <&sys_clkin1>;
+ ti,settling-time = <50>;
+ ti,clock-cycles = <16>;
+
+ reg = <0x4ae07e34 0x4>, <0x4ae07e24 0x4>,
+ <0x4ae06010 0x4>, <0x4a0025cc 0x8>,
+ <0x4a002470 0x4>;
+ reg-names = "setup-address", "control-address",
+ "int-address", "efuse-address",
+ "ldo-address";
+ ti,tranxdone-status-mask = <0x40000000>;
+ /* LDOVBBIVA_FBB_MUX_CTRL */
+ ti,ldovbb-override-mask = <0x400>;
+ /* LDOVBBIVA_FBB_VSET_OUT */
+ ti,ldovbb-vset-mask = <0x1F>;
+
+ /*
+ * NOTE: only FBB mode used but actual vset will
+ * determine final biasing
+ */
+ ti,abb_info = <
+ /*uV ABB efuse rbb_m fbb_m vset_m*/
+ 1055000 0 0x0 0 0x02000000 0x01F00000
+ 1150000 0 0x4 0 0x02000000 0x01F00000
+ 1250000 0 0x8 0 0x02000000 0x01F00000
+ >;
+ };
+
+ abb_dspeve: regulator-abb-dspeve {
+ compatible = "ti,abb-v3";
+ regulator-name = "abb_dspeve";
+ #address-cells = <0>;
+ #size-cells = <0>;
+ clocks = <&sys_clkin1>;
+ ti,settling-time = <50>;
+ ti,clock-cycles = <16>;
+
+ reg = <0x4ae07e30 0x4>, <0x4ae07e20 0x4>,
+ <0x4ae06010 0x4>, <0x4a0025e0 0x8>,
+ <0x4a00246c 0x4>;
+ reg-names = "setup-address", "control-address",
+ "int-address", "efuse-address",
+ "ldo-address";
+ ti,tranxdone-status-mask = <0x20000000>;
+ /* LDOVBBDSPEVE_FBB_MUX_CTRL */
+ ti,ldovbb-override-mask = <0x400>;
+ /* LDOVBBDSPEVE_FBB_VSET_OUT */
+ ti,ldovbb-vset-mask = <0x1F>;
+
+ /*
+ * NOTE: only FBB mode used but actual vset will
+ * determine final biasing
+ */
+ ti,abb_info = <
+ /*uV ABB efuse rbb_m fbb_m vset_m*/
+ 1055000 0 0x0 0 0x02000000 0x01F00000
+ 1150000 0 0x4 0 0x02000000 0x01F00000
+ 1250000 0 0x8 0 0x02000000 0x01F00000
+ >;
+ };
+
+ abb_gpu: regulator-abb-gpu {
+ compatible = "ti,abb-v3";
+ regulator-name = "abb_gpu";
+ #address-cells = <0>;
+ #size-cells = <0>;
+ clocks = <&sys_clkin1>;
+ ti,settling-time = <50>;
+ ti,clock-cycles = <16>;
+
+ reg = <0x4ae07de4 0x4>, <0x4ae07de8 0x4>,
+ <0x4ae06010 0x4>, <0x4a003b08 0x8>,
+ <0x4ae0c154 0x4>;
+ reg-names = "setup-address", "control-address",
+ "int-address", "efuse-address",
+ "ldo-address";
+ ti,tranxdone-status-mask = <0x10000000>;
+ /* LDOVBBGPU_FBB_MUX_CTRL */
+ ti,ldovbb-override-mask = <0x400>;
+ /* LDOVBBGPU_FBB_VSET_OUT */
+ ti,ldovbb-vset-mask = <0x1F>;
+
+ /*
+ * NOTE: only FBB mode used but actual vset will
+ * determine final biasing
+ */
+ ti,abb_info = <
+ /*uV ABB efuse rbb_m fbb_m vset_m*/
+ 1090000 0 0x0 0 0x02000000 0x01F00000
+ 1210000 0 0x4 0 0x02000000 0x01F00000
+ 1280000 0 0x8 0 0x02000000 0x01F00000
+ >;
+ };
+
mcspi1: spi@48098000 {
compatible = "ti,omap4-mcspi";
reg = <0x48098000 0x200>;
diff --git a/arch/arm/boot/dts/efm32gg-dk3750.dts b/arch/arm/boot/dts/efm32gg-dk3750.dts
index aa5c0f6363d6..b4031fa4a567 100644
--- a/arch/arm/boot/dts/efm32gg-dk3750.dts
+++ b/arch/arm/boot/dts/efm32gg-dk3750.dts
@@ -26,7 +26,7 @@
};
i2c@4000a000 {
- location = <3>;
+ efm32,location = <3>;
status = "ok";
temp@48 {
diff --git a/arch/arm/boot/dts/efm32gg.dtsi b/arch/arm/boot/dts/efm32gg.dtsi
index a342ab0e6e4f..106d505c5d3d 100644
--- a/arch/arm/boot/dts/efm32gg.dtsi
+++ b/arch/arm/boot/dts/efm32gg.dtsi
@@ -84,7 +84,7 @@
status = "disabled";
};
- spi2: spi@40x4000c800 { /* USART2 */
+ spi2: spi@4000c800 { /* USART2 */
#address-cells = <1>;
#size-cells = <0>;
compatible = "efm32,spi";
@@ -110,7 +110,7 @@
status = "disabled";
};
- uart2: uart@40x4000c800 { /* USART2 */
+ uart2: uart@4000c800 { /* USART2 */
compatible = "efm32,uart";
reg = <0x4000c800 0x400>;
interrupts = <18 19>;
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 08452e183b57..0401f4dba2a2 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -19,6 +19,7 @@
* published by the Free Software Foundation.
*/
+#include <dt-bindings/clock/exynos4.h>
#include "skeleton.dtsi"
/ {
@@ -85,6 +86,11 @@
reg = <0x10023CE0 0x20>;
};
+ pd_gps_alive: gps-alive-power-domain@10023D00 {
+ compatible = "samsung,exynos4210-pd";
+ reg = <0x10023D00 0x20>;
+ };
+
gic: interrupt-controller@10490000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
@@ -119,7 +125,7 @@
compatible = "samsung,exynos4210-fimc";
reg = <0x11800000 0x1000>;
interrupts = <0 84 0>;
- clocks = <&clock 256>, <&clock 128>;
+ clocks = <&clock CLK_FIMC0>, <&clock CLK_SCLK_FIMC0>;
clock-names = "fimc", "sclk_fimc";
samsung,power-domain = <&pd_cam>;
samsung,sysreg = <&sys_reg>;
@@ -130,7 +136,7 @@
compatible = "samsung,exynos4210-fimc";
reg = <0x11810000 0x1000>;
interrupts = <0 85 0>;
- clocks = <&clock 257>, <&clock 129>;
+ clocks = <&clock CLK_FIMC1>, <&clock CLK_SCLK_FIMC1>;
clock-names = "fimc", "sclk_fimc";
samsung,power-domain = <&pd_cam>;
samsung,sysreg = <&sys_reg>;
@@ -141,7 +147,7 @@
compatible = "samsung,exynos4210-fimc";
reg = <0x11820000 0x1000>;
interrupts = <0 86 0>;
- clocks = <&clock 258>, <&clock 130>;
+ clocks = <&clock CLK_FIMC2>, <&clock CLK_SCLK_FIMC2>;
clock-names = "fimc", "sclk_fimc";
samsung,power-domain = <&pd_cam>;
samsung,sysreg = <&sys_reg>;
@@ -152,7 +158,7 @@
compatible = "samsung,exynos4210-fimc";
reg = <0x11830000 0x1000>;
interrupts = <0 87 0>;
- clocks = <&clock 259>, <&clock 131>;
+ clocks = <&clock CLK_FIMC3>, <&clock CLK_SCLK_FIMC3>;
clock-names = "fimc", "sclk_fimc";
samsung,power-domain = <&pd_cam>;
samsung,sysreg = <&sys_reg>;
@@ -163,7 +169,7 @@
compatible = "samsung,exynos4210-csis";
reg = <0x11880000 0x4000>;
interrupts = <0 78 0>;
- clocks = <&clock 260>, <&clock 134>;
+ clocks = <&clock CLK_CSIS0>, <&clock CLK_SCLK_CSIS0>;
clock-names = "csis", "sclk_csis";
bus-width = <4>;
samsung,power-domain = <&pd_cam>;
@@ -178,7 +184,7 @@
compatible = "samsung,exynos4210-csis";
reg = <0x11890000 0x4000>;
interrupts = <0 80 0>;
- clocks = <&clock 261>, <&clock 135>;
+ clocks = <&clock CLK_CSIS1>, <&clock CLK_SCLK_CSIS1>;
clock-names = "csis", "sclk_csis";
bus-width = <2>;
samsung,power-domain = <&pd_cam>;
@@ -194,7 +200,7 @@
compatible = "samsung,s3c2410-wdt";
reg = <0x10060000 0x100>;
interrupts = <0 43 0>;
- clocks = <&clock 345>;
+ clocks = <&clock CLK_WDT>;
clock-names = "watchdog";
status = "disabled";
};
@@ -203,7 +209,7 @@
compatible = "samsung,s3c6410-rtc";
reg = <0x10070000 0x100>;
interrupts = <0 44 0>, <0 45 0>;
- clocks = <&clock 346>;
+ clocks = <&clock CLK_RTC>;
clock-names = "rtc";
status = "disabled";
};
@@ -212,7 +218,7 @@
compatible = "samsung,s5pv210-keypad";
reg = <0x100A0000 0x100>;
interrupts = <0 109 0>;
- clocks = <&clock 347>;
+ clocks = <&clock CLK_KEYIF>;
clock-names = "keypad";
status = "disabled";
};
@@ -221,7 +227,7 @@
compatible = "samsung,exynos4210-sdhci";
reg = <0x12510000 0x100>;
interrupts = <0 73 0>;
- clocks = <&clock 297>, <&clock 145>;
+ clocks = <&clock CLK_SDMMC0>, <&clock CLK_SCLK_MMC0>;
clock-names = "hsmmc", "mmc_busclk.2";
status = "disabled";
};
@@ -230,7 +236,7 @@
compatible = "samsung,exynos4210-sdhci";
reg = <0x12520000 0x100>;
interrupts = <0 74 0>;
- clocks = <&clock 298>, <&clock 146>;
+ clocks = <&clock CLK_SDMMC1>, <&clock CLK_SCLK_MMC1>;
clock-names = "hsmmc", "mmc_busclk.2";
status = "disabled";
};
@@ -239,7 +245,7 @@
compatible = "samsung,exynos4210-sdhci";
reg = <0x12530000 0x100>;
interrupts = <0 75 0>;
- clocks = <&clock 299>, <&clock 147>;
+ clocks = <&clock CLK_SDMMC2>, <&clock CLK_SCLK_MMC2>;
clock-names = "hsmmc", "mmc_busclk.2";
status = "disabled";
};
@@ -248,7 +254,7 @@
compatible = "samsung,exynos4210-sdhci";
reg = <0x12540000 0x100>;
interrupts = <0 76 0>;
- clocks = <&clock 300>, <&clock 148>;
+ clocks = <&clock CLK_SDMMC3>, <&clock CLK_SCLK_MMC3>;
clock-names = "hsmmc", "mmc_busclk.2";
status = "disabled";
};
@@ -257,7 +263,7 @@
compatible = "samsung,exynos4210-ehci";
reg = <0x12580000 0x100>;
interrupts = <0 70 0>;
- clocks = <&clock 304>;
+ clocks = <&clock CLK_USB_HOST>;
clock-names = "usbhost";
status = "disabled";
};
@@ -266,7 +272,7 @@
compatible = "samsung,exynos4210-ohci";
reg = <0x12590000 0x100>;
interrupts = <0 70 0>;
- clocks = <&clock 304>;
+ clocks = <&clock CLK_USB_HOST>;
clock-names = "usbhost";
status = "disabled";
};
@@ -276,7 +282,7 @@
reg = <0x13400000 0x10000>;
interrupts = <0 94 0>;
samsung,power-domain = <&pd_mfc>;
- clocks = <&clock 273>;
+ clocks = <&clock CLK_MFC>;
clock-names = "mfc";
status = "disabled";
};
@@ -285,7 +291,7 @@
compatible = "samsung,exynos4210-uart";
reg = <0x13800000 0x100>;
interrupts = <0 52 0>;
- clocks = <&clock 312>, <&clock 151>;
+ clocks = <&clock CLK_UART0>, <&clock CLK_SCLK_UART0>;
clock-names = "uart", "clk_uart_baud0";
status = "disabled";
};
@@ -294,7 +300,7 @@
compatible = "samsung,exynos4210-uart";
reg = <0x13810000 0x100>;
interrupts = <0 53 0>;
- clocks = <&clock 313>, <&clock 152>;
+ clocks = <&clock CLK_UART1>, <&clock CLK_SCLK_UART1>;
clock-names = "uart", "clk_uart_baud0";
status = "disabled";
};
@@ -303,7 +309,7 @@
compatible = "samsung,exynos4210-uart";
reg = <0x13820000 0x100>;
interrupts = <0 54 0>;
- clocks = <&clock 314>, <&clock 153>;
+ clocks = <&clock CLK_UART2>, <&clock CLK_SCLK_UART2>;
clock-names = "uart", "clk_uart_baud0";
status = "disabled";
};
@@ -312,7 +318,7 @@
compatible = "samsung,exynos4210-uart";
reg = <0x13830000 0x100>;
interrupts = <0 55 0>;
- clocks = <&clock 315>, <&clock 154>;
+ clocks = <&clock CLK_UART3>, <&clock CLK_SCLK_UART3>;
clock-names = "uart", "clk_uart_baud0";
status = "disabled";
};
@@ -323,7 +329,7 @@
compatible = "samsung,s3c2440-i2c";
reg = <0x13860000 0x100>;
interrupts = <0 58 0>;
- clocks = <&clock 317>;
+ clocks = <&clock CLK_I2C0>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c0_bus>;
@@ -336,7 +342,7 @@
compatible = "samsung,s3c2440-i2c";
reg = <0x13870000 0x100>;
interrupts = <0 59 0>;
- clocks = <&clock 318>;
+ clocks = <&clock CLK_I2C1>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c1_bus>;
@@ -349,7 +355,7 @@
compatible = "samsung,s3c2440-i2c";
reg = <0x13880000 0x100>;
interrupts = <0 60 0>;
- clocks = <&clock 319>;
+ clocks = <&clock CLK_I2C2>;
clock-names = "i2c";
status = "disabled";
};
@@ -360,7 +366,7 @@
compatible = "samsung,s3c2440-i2c";
reg = <0x13890000 0x100>;
interrupts = <0 61 0>;
- clocks = <&clock 320>;
+ clocks = <&clock CLK_I2C3>;
clock-names = "i2c";
status = "disabled";
};
@@ -371,7 +377,7 @@
compatible = "samsung,s3c2440-i2c";
reg = <0x138A0000 0x100>;
interrupts = <0 62 0>;
- clocks = <&clock 321>;
+ clocks = <&clock CLK_I2C4>;
clock-names = "i2c";
status = "disabled";
};
@@ -382,7 +388,7 @@
compatible = "samsung,s3c2440-i2c";
reg = <0x138B0000 0x100>;
interrupts = <0 63 0>;
- clocks = <&clock 322>;
+ clocks = <&clock CLK_I2C5>;
clock-names = "i2c";
status = "disabled";
};
@@ -393,7 +399,7 @@
compatible = "samsung,s3c2440-i2c";
reg = <0x138C0000 0x100>;
interrupts = <0 64 0>;
- clocks = <&clock 323>;
+ clocks = <&clock CLK_I2C6>;
clock-names = "i2c";
status = "disabled";
};
@@ -404,7 +410,7 @@
compatible = "samsung,s3c2440-i2c";
reg = <0x138D0000 0x100>;
interrupts = <0 65 0>;
- clocks = <&clock 324>;
+ clocks = <&clock CLK_I2C7>;
clock-names = "i2c";
status = "disabled";
};
@@ -417,7 +423,7 @@
dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 327>, <&clock 159>;
+ clocks = <&clock CLK_SPI0>, <&clock CLK_SCLK_SPI0>;
clock-names = "spi", "spi_busclk0";
pinctrl-names = "default";
pinctrl-0 = <&spi0_bus>;
@@ -432,7 +438,7 @@
dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 328>, <&clock 160>;
+ clocks = <&clock CLK_SPI1>, <&clock CLK_SCLK_SPI1>;
clock-names = "spi", "spi_busclk0";
pinctrl-names = "default";
pinctrl-0 = <&spi1_bus>;
@@ -447,7 +453,7 @@
dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 329>, <&clock 161>;
+ clocks = <&clock CLK_SPI2>, <&clock CLK_SCLK_SPI2>;
clock-names = "spi", "spi_busclk0";
pinctrl-names = "default";
pinctrl-0 = <&spi2_bus>;
@@ -458,7 +464,7 @@
compatible = "samsung,exynos4210-pwm";
reg = <0x139D0000 0x1000>;
interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>;
- clocks = <&clock 336>;
+ clocks = <&clock CLK_PWM>;
clock-names = "timers";
#pwm-cells = <2>;
status = "disabled";
@@ -475,7 +481,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x12680000 0x1000>;
interrupts = <0 35 0>;
- clocks = <&clock 292>;
+ clocks = <&clock CLK_PDMA0>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
@@ -486,7 +492,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x12690000 0x1000>;
interrupts = <0 36 0>;
- clocks = <&clock 293>;
+ clocks = <&clock CLK_PDMA1>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
@@ -497,7 +503,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x12850000 0x1000>;
interrupts = <0 34 0>;
- clocks = <&clock 279>;
+ clocks = <&clock CLK_MDMA>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
@@ -511,7 +517,7 @@
reg = <0x11c00000 0x20000>;
interrupt-names = "fifo", "vsync", "lcd_sys";
interrupts = <11 0>, <11 1>, <11 2>;
- clocks = <&clock 140>, <&clock 283>;
+ clocks = <&clock CLK_SCLK_FIMD0>, <&clock CLK_FIMD0>;
clock-names = "sclk_fimd", "fimd";
samsung,power-domain = <&pd_lcd0>;
status = "disabled";
diff --git a/arch/arm/boot/dts/exynos4210-origen.dts b/arch/arm/boot/dts/exynos4210-origen.dts
index 2aa13cb3bbed..72fb11f7ea21 100644
--- a/arch/arm/boot/dts/exynos4210-origen.dts
+++ b/arch/arm/boot/dts/exynos4210-origen.dts
@@ -19,7 +19,7 @@
/ {
model = "Insignal Origen evaluation board based on Exynos4210";
- compatible = "insignal,origen", "samsung,exynos4210";
+ compatible = "insignal,origen", "samsung,exynos4210", "samsung,exynos4";
memory {
reg = <0x40000000 0x10000000
diff --git a/arch/arm/boot/dts/exynos4210-smdkv310.dts b/arch/arm/boot/dts/exynos4210-smdkv310.dts
index 9c01b718d29d..636d16684750 100644
--- a/arch/arm/boot/dts/exynos4210-smdkv310.dts
+++ b/arch/arm/boot/dts/exynos4210-smdkv310.dts
@@ -19,7 +19,7 @@
/ {
model = "Samsung smdkv310 evaluation board based on Exynos4210";
- compatible = "samsung,smdkv310", "samsung,exynos4210";
+ compatible = "samsung,smdkv310", "samsung,exynos4210", "samsung,exynos4";
memory {
reg = <0x40000000 0x80000000>;
diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts
index 63cc571ca307..361cb58052bf 100644
--- a/arch/arm/boot/dts/exynos4210-trats.dts
+++ b/arch/arm/boot/dts/exynos4210-trats.dts
@@ -17,7 +17,7 @@
/ {
model = "Samsung Trats based on Exynos4210";
- compatible = "samsung,trats", "samsung,exynos4210";
+ compatible = "samsung,trats", "samsung,exynos4210", "samsung,exynos4";
memory {
reg = <0x40000000 0x10000000
diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts b/arch/arm/boot/dts/exynos4210-universal_c210.dts
index d2e3f5f5916d..27d3b70ee9e3 100644
--- a/arch/arm/boot/dts/exynos4210-universal_c210.dts
+++ b/arch/arm/boot/dts/exynos4210-universal_c210.dts
@@ -17,7 +17,7 @@
/ {
model = "Samsung Universal C210 based on Exynos4210 rev0";
- compatible = "samsung,universal_c210", "samsung,exynos4210";
+ compatible = "samsung,universal_c210", "samsung,exynos4210", "samsung,exynos4";
memory {
reg = <0x40000000 0x10000000
diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi
index 48ecd7a755ab..cacf6140dd2f 100644
--- a/arch/arm/boot/dts/exynos4210.dtsi
+++ b/arch/arm/boot/dts/exynos4210.dtsi
@@ -23,7 +23,7 @@
#include "exynos4210-pinctrl.dtsi"
/ {
- compatible = "samsung,exynos4210";
+ compatible = "samsung,exynos4210", "samsung,exynos4";
aliases {
pinctrl0 = &pinctrl_0;
@@ -53,7 +53,7 @@
reg = <0x10050000 0x800>;
interrupt-parent = <&mct_map>;
interrupts = <0>, <1>, <2>, <3>, <4>, <5>;
- clocks = <&clock 3>, <&clock 344>;
+ clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MCT>;
clock-names = "fin_pll", "mct";
mct_map: mct-map {
@@ -109,7 +109,7 @@
interrupt-parent = <&combiner>;
reg = <0x100C0000 0x100>;
interrupts = <2 4>;
- clocks = <&clock 383>;
+ clocks = <&clock CLK_TMU_APBIF>;
clock-names = "tmu_apbif";
status = "disabled";
};
@@ -118,13 +118,14 @@
compatible = "samsung,s5pv210-g2d";
reg = <0x12800000 0x1000>;
interrupts = <0 89 0>;
- clocks = <&clock 177>, <&clock 277>;
+ clocks = <&clock CLK_SCLK_FIMG2D>, <&clock CLK_G2D>;
clock-names = "sclk_fimg2d", "fimg2d";
status = "disabled";
};
camera {
- clocks = <&clock 132>, <&clock 133>, <&clock 351>, <&clock 352>;
+ clocks = <&clock CLK_SCLK_CAM0>, <&clock CLK_SCLK_CAM1>,
+ <&clock CLK_PIXELASYNCM0>, <&clock CLK_PIXELASYNCM1>;
clock-names = "sclk_cam0", "sclk_cam1", "pxl_async0", "pxl_async1";
fimc_0: fimc@11800000 {
diff --git a/arch/arm/boot/dts/exynos4212.dtsi b/arch/arm/boot/dts/exynos4212.dtsi
index 94a43f9a05e2..3c00e6ec9302 100644
--- a/arch/arm/boot/dts/exynos4212.dtsi
+++ b/arch/arm/boot/dts/exynos4212.dtsi
@@ -20,18 +20,13 @@
#include "exynos4x12.dtsi"
/ {
- compatible = "samsung,exynos4212";
+ compatible = "samsung,exynos4212", "samsung,exynos4";
- gic: interrupt-controller@10490000 {
- cpu-offset = <0x8000>;
+ combiner: interrupt-controller@10440000 {
+ samsung,combiner-nr = <18>;
};
- interrupt-controller@10440000 {
- samsung,combiner-nr = <18>;
- interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
- <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
- <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
- <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>,
- <0 107 0>, <0 108 0>;
+ gic: interrupt-controller@10490000 {
+ cpu-offset = <0x8000>;
};
};
diff --git a/arch/arm/boot/dts/exynos4412-odroidx.dts b/arch/arm/boot/dts/exynos4412-odroidx.dts
index 9804fcb71f8c..31db28a4bb33 100644
--- a/arch/arm/boot/dts/exynos4412-odroidx.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidx.dts
@@ -16,7 +16,7 @@
/ {
model = "Hardkernel ODROID-X board based on Exynos4412";
- compatible = "hardkernel,odroid-x", "samsung,exynos4412";
+ compatible = "hardkernel,odroid-x", "samsung,exynos4412", "samsung,exynos4";
memory {
reg = <0x40000000 0x40000000>;
@@ -251,7 +251,7 @@
buck2_reg: BUCK2 {
regulator-name = "vdd_arm";
regulator-min-microvolt = <900000>;
- regulator-max-microvolt = <1300000>;
+ regulator-max-microvolt = <1350000>;
regulator-always-on;
regulator-boot-on;
};
diff --git a/arch/arm/boot/dts/exynos4412-origen.dts b/arch/arm/boot/dts/exynos4412-origen.dts
index 6bc053924e9e..e2c0dcab4d81 100644
--- a/arch/arm/boot/dts/exynos4412-origen.dts
+++ b/arch/arm/boot/dts/exynos4412-origen.dts
@@ -17,7 +17,7 @@
/ {
model = "Insignal Origen evaluation board based on Exynos4412";
- compatible = "insignal,origen4412", "samsung,exynos4412";
+ compatible = "insignal,origen4412", "samsung,exynos4412", "samsung,exynos4";
memory {
reg = <0x40000000 0x40000000>;
@@ -459,8 +459,8 @@
buck2_reg: BUCK2 {
regulator-name = "vdd_arm";
- regulator-min-microvolt = <925000>;
- regulator-max-microvolt = <1300000>;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1350000>;
regulator-always-on;
regulator-boot-on;
op_mode = <1>; /* Normal Mode */
diff --git a/arch/arm/boot/dts/exynos4412-smdk4412.dts b/arch/arm/boot/dts/exynos4412-smdk4412.dts
index ad316a1ee9e0..ded0b70f7644 100644
--- a/arch/arm/boot/dts/exynos4412-smdk4412.dts
+++ b/arch/arm/boot/dts/exynos4412-smdk4412.dts
@@ -17,7 +17,7 @@
/ {
model = "Samsung SMDK evaluation board based on Exynos4412";
- compatible = "samsung,smdk4412", "samsung,exynos4412";
+ compatible = "samsung,smdk4412", "samsung,exynos4412", "samsung,exynos4";
memory {
reg = <0x40000000 0x40000000>;
diff --git a/arch/arm/boot/dts/exynos4412-tiny4412.dts b/arch/arm/boot/dts/exynos4412-tiny4412.dts
index 0a9831256b33..ea6929d9c621 100644
--- a/arch/arm/boot/dts/exynos4412-tiny4412.dts
+++ b/arch/arm/boot/dts/exynos4412-tiny4412.dts
@@ -16,7 +16,7 @@
/ {
model = "FriendlyARM TINY4412 board based on Exynos4412";
- compatible = "friendlyarm,tiny4412", "samsung,exynos4412";
+ compatible = "friendlyarm,tiny4412", "samsung,exynos4412", "samsung,exynos4";
memory {
reg = <0x40000000 0x40000000>;
diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts
index 4f851ccf40eb..c16b3159b813 100644
--- a/arch/arm/boot/dts/exynos4412-trats2.dts
+++ b/arch/arm/boot/dts/exynos4412-trats2.dts
@@ -17,7 +17,7 @@
/ {
model = "Samsung Trats 2 based on Exynos4412";
- compatible = "samsung,trats2", "samsung,exynos4412";
+ compatible = "samsung,trats2", "samsung,exynos4412", "samsung,exynos4";
aliases {
i2c8 = &i2c_ak8975;
@@ -106,6 +106,11 @@
};
};
+ adc: adc@126C0000 {
+ vdd-supply = <&ldo3_reg>;
+ status = "okay";
+ };
+
i2c@13890000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-slave-addr = <0x10>;
@@ -589,4 +594,20 @@
};
};
};
+
+ thermistor-ap@0 {
+ compatible = "ntc,ncp15wb473";
+ pullup-uv = <1800000>; /* VCC_1.8V_AP */
+ pullup-ohm = <100000>; /* 100K */
+ pulldown-ohm = <100000>; /* 100K */
+ io-channels = <&adc 1>; /* AP temperature */
+ };
+
+ thermistor-battery@1 {
+ compatible = "ntc,ncp15wb473";
+ pullup-uv = <1800000>; /* VCC_1.8V_AP */
+ pullup-ohm = <100000>; /* 100K */
+ pulldown-ohm = <100000>; /* 100K */
+ io-channels = <&adc 2>; /* Battery temperature */
+ };
};
diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi
index 87b339c739de..15d3c0ac2f5f 100644
--- a/arch/arm/boot/dts/exynos4412.dtsi
+++ b/arch/arm/boot/dts/exynos4412.dtsi
@@ -20,19 +20,13 @@
#include "exynos4x12.dtsi"
/ {
- compatible = "samsung,exynos4412";
+ compatible = "samsung,exynos4412", "samsung,exynos4";
- gic: interrupt-controller@10490000 {
- cpu-offset = <0x4000>;
- };
-
- interrupt-controller@10440000 {
+ combiner: interrupt-controller@10440000 {
samsung,combiner-nr = <20>;
- interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
- <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
- <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
- <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>,
- <0 107 0>, <0 108 0>, <0 48 0>, <0 42 0>;
};
+ gic: interrupt-controller@10490000 {
+ cpu-offset = <0x4000>;
+ };
};
diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi
index 5c412aa14738..c4a9306f8529 100644
--- a/arch/arm/boot/dts/exynos4x12.dtsi
+++ b/arch/arm/boot/dts/exynos4x12.dtsi
@@ -31,6 +31,12 @@
mshc0 = &mshc_0;
};
+ pmu {
+ compatible = "arm,cortex-a9-pmu";
+ interrupt-parent = <&combiner>;
+ interrupts = <2 2>, <3 2>, <18 2>, <19 2>;
+ };
+
pd_isp: isp-power-domain@10023CA0 {
compatible = "samsung,exynos4210-pd";
reg = <0x10023CA0 0x20>;
@@ -47,7 +53,7 @@
reg = <0x10050000 0x800>;
interrupt-parent = <&mct_map>;
interrupts = <0>, <1>, <2>, <3>, <4>;
- clocks = <&clock 3>, <&clock 344>;
+ clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MCT>;
clock-names = "fin_pll", "mct";
mct_map: mct-map {
@@ -62,6 +68,14 @@
};
};
+ combiner: interrupt-controller@10440000 {
+ interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
+ <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
+ <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
+ <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>,
+ <0 107 0>, <0 108 0>, <0 48 0>, <0 42 0>;
+ };
+
pinctrl_0: pinctrl@11400000 {
compatible = "samsung,exynos4x12-pinctrl";
reg = <0x11400000 0x1000>;
@@ -80,6 +94,18 @@
};
};
+ adc: adc@126C0000 {
+ compatible = "samsung,exynos-adc-v1";
+ reg = <0x126C0000 0x100>, <0x10020718 0x4>;
+ interrupt-parent = <&combiner>;
+ interrupts = <10 3>;
+ clocks = <&clock CLK_TSADC>;
+ clock-names = "adc";
+ #io-channel-cells = <1>;
+ io-channel-ranges;
+ status = "disabled";
+ };
+
pinctrl_2: pinctrl@03860000 {
compatible = "samsung,exynos4x12-pinctrl";
reg = <0x03860000 0x1000>;
@@ -97,13 +123,14 @@
compatible = "samsung,exynos4212-g2d";
reg = <0x10800000 0x1000>;
interrupts = <0 89 0>;
- clocks = <&clock 177>, <&clock 277>;
+ clocks = <&clock CLK_SCLK_FIMG2D>, <&clock CLK_G2D>;
clock-names = "sclk_fimg2d", "fimg2d";
status = "disabled";
};
camera {
- clocks = <&clock 132>, <&clock 133>, <&clock 351>, <&clock 352>;
+ clocks = <&clock CLK_SCLK_CAM0>, <&clock CLK_SCLK_CAM1>,
+ <&clock CLK_PIXELASYNCM0>, <&clock CLK_PIXELASYNCM1>;
clock-names = "sclk_cam0", "sclk_cam1", "pxl_async0", "pxl_async1";
fimc_0: fimc@11800000 {
@@ -145,7 +172,7 @@
reg = <0x12390000 0x1000>;
interrupts = <0 105 0>;
samsung,power-domain = <&pd_isp>;
- clocks = <&clock 353>;
+ clocks = <&clock CLK_FIMC_LITE0>;
clock-names = "flite";
status = "disabled";
};
@@ -155,7 +182,7 @@
reg = <0x123A0000 0x1000>;
interrupts = <0 106 0>;
samsung,power-domain = <&pd_isp>;
- clocks = <&clock 354>;
+ clocks = <&clock CLK_FIMC_LITE1>;
clock-names = "flite";
status = "disabled";
};
@@ -165,12 +192,19 @@
reg = <0x12000000 0x260000>;
interrupts = <0 90 0>, <0 95 0>;
samsung,power-domain = <&pd_isp>;
- clocks = <&clock 353>, <&clock 354>, <&clock 355>,
- <&clock 356>, <&clock 17>, <&clock 357>,
- <&clock 358>, <&clock 359>, <&clock 360>,
- <&clock 450>,<&clock 451>, <&clock 452>,
- <&clock 453>, <&clock 176>, <&clock 13>,
- <&clock 454>, <&clock 395>, <&clock 455>;
+ clocks = <&clock CLK_FIMC_LITE0>,
+ <&clock CLK_FIMC_LITE1>, <&clock CLK_PPMUISPX>,
+ <&clock CLK_PPMUISPMX>,
+ <&clock CLK_MOUT_MPLL_USER_T>,
+ <&clock CLK_FIMC_ISP>, <&clock CLK_FIMC_DRC>,
+ <&clock CLK_FIMC_FD>, <&clock CLK_MCUISP>,
+ <&clock CLK_DIV_ISP0>,<&clock CLK_DIV_ISP1>,
+ <&clock CLK_DIV_MCUISP0>,
+ <&clock CLK_DIV_MCUISP1>,
+ <&clock CLK_SCLK_UART_ISP>,
+ <&clock CLK_ACLK200>, <&clock CLK_DIV_ACLK200>,
+ <&clock CLK_ACLK400_MCUISP>,
+ <&clock CLK_DIV_ACLK400_MCUISP>;
clock-names = "lite0", "lite1", "ppmuispx",
"ppmuispmx", "mpll", "isp",
"drc", "fd", "mcuisp",
@@ -190,7 +224,7 @@
i2c1_isp: i2c-isp@12140000 {
compatible = "samsung,exynos4212-i2c-isp";
reg = <0x12140000 0x100>;
- clocks = <&clock 370>;
+ clocks = <&clock CLK_I2C1_ISP>;
clock-names = "i2c_isp";
#address-cells = <1>;
#size-cells = <0>;
@@ -205,7 +239,7 @@
#address-cells = <1>;
#size-cells = <0>;
fifo-depth = <0x80>;
- clocks = <&clock 301>, <&clock 149>;
+ clocks = <&clock CLK_SDMMC4>, <&clock CLK_SCLK_MMC4>;
clock-names = "biu", "ciu";
status = "disabled";
};
diff --git a/arch/arm/boot/dts/exynos5.dtsi b/arch/arm/boot/dts/exynos5.dtsi
index 258dca441f36..79d0608d6dcc 100644
--- a/arch/arm/boot/dts/exynos5.dtsi
+++ b/arch/arm/boot/dts/exynos5.dtsi
@@ -81,13 +81,6 @@
status = "disabled";
};
- watchdog {
- compatible = "samsung,s3c2410-wdt";
- reg = <0x101D0000 0x100>;
- interrupts = <0 42 0>;
- status = "disabled";
- };
-
fimd@14400000 {
compatible = "samsung,exynos5250-fimd";
interrupt-parent = <&combiner>;
diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts
index b42e658876e5..090f9830b129 100644
--- a/arch/arm/boot/dts/exynos5250-arndale.dts
+++ b/arch/arm/boot/dts/exynos5250-arndale.dts
@@ -15,7 +15,7 @@
/ {
model = "Insignal Arndale evaluation board based on EXYNOS5250";
- compatible = "insignal,arndale", "samsung,exynos5250";
+ compatible = "insignal,arndale", "samsung,exynos5250", "samsung,exynos5";
memory {
reg = <0x40000000 0x80000000>;
@@ -25,6 +25,10 @@
bootargs = "console=ttySAC2,115200";
};
+ rtc@101E0000 {
+ status = "okay";
+ };
+
codec@11000000 {
samsung,mfc-r = <0x43000000 0x800000>;
samsung,mfc-l = <0x51000000 0x800000>;
@@ -287,6 +291,7 @@
regulator-name = "vdd_g3d";
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1000000>;
+ regulator-always-on;
regulator-boot-on;
op_mode = <1>;
};
@@ -370,6 +375,27 @@
};
};
+ i2c@121D0000 {
+ status = "okay";
+ samsung,i2c-sda-delay = <100>;
+ samsung,i2c-max-bus-freq = <40000>;
+ samsung,i2c-slave-addr = <0x38>;
+
+ sata_phy_i2c:sata-phy@38 {
+ compatible = "samsung,exynos-sataphy-i2c";
+ reg = <0x38>;
+ };
+ };
+
+ sata@122F0000 {
+ status = "okay";
+ };
+
+ sata-phy@12170000 {
+ status = "okay";
+ samsung,exynos-sataphy-i2c-phandle = <&sata_phy_i2c>;
+ };
+
mmc_0: mmc@12200000 {
status = "okay";
num-slots = <1>;
diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts
index 3e69837c435c..a794a705d404 100644
--- a/arch/arm/boot/dts/exynos5250-smdk5250.dts
+++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts
@@ -14,7 +14,7 @@
/ {
model = "SAMSUNG SMDK5250 board based on EXYNOS5250";
- compatible = "samsung,smdk5250", "samsung,exynos5250";
+ compatible = "samsung,smdk5250", "samsung,exynos5250", "samsung,exynos5";
aliases {
};
@@ -27,6 +27,10 @@
bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc";
};
+ rtc@101E0000 {
+ status = "okay";
+ };
+
i2c@12C60000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <20000>;
@@ -36,6 +40,148 @@
compatible = "samsung,s524ad0xd1";
reg = <0x50>;
};
+
+ max77686@09 {
+ compatible = "maxim,max77686";
+ reg = <0x09>;
+
+ voltage-regulators {
+ ldo1_reg: LDO1 {
+ regulator-name = "P1.0V_LDO_OUT1";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ ldo2_reg: LDO2 {
+ regulator-name = "P1.2V_LDO_OUT2";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ };
+
+ ldo3_reg: LDO3 {
+ regulator-name = "P1.8V_LDO_OUT3";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ ldo4_reg: LDO4 {
+ regulator-name = "P2.8V_LDO_OUT4";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ };
+
+ ldo5_reg: LDO5 {
+ regulator-name = "P1.8V_LDO_OUT5";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo6_reg: LDO6 {
+ regulator-name = "P1.1V_LDO_OUT6";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ };
+
+ ldo7_reg: LDO7 {
+ regulator-name = "P1.1V_LDO_OUT7";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ };
+
+ ldo8_reg: LDO8 {
+ regulator-name = "P1.0V_LDO_OUT8";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ };
+
+ ldo10_reg: LDO10 {
+ regulator-name = "P1.8V_LDO_OUT10";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo11_reg: LDO11 {
+ regulator-name = "P1.8V_LDO_OUT11";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo12_reg: LDO12 {
+ regulator-name = "P3.0V_LDO_OUT12";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ };
+
+ ldo13_reg: LDO13 {
+ regulator-name = "P1.8V_LDO_OUT13";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo14_reg: LDO14 {
+ regulator-name = "P1.8V_LDO_OUT14";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo15_reg: LDO15 {
+ regulator-name = "P1.0V_LDO_OUT15";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ };
+
+ ldo16_reg: LDO16 {
+ regulator-name = "P1.8V_LDO_OUT16";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ buck1_reg: BUCK1 {
+ regulator-name = "vdd_mif";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1300000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck2_reg: BUCK2 {
+ regulator-name = "vdd_arm";
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck3_reg: BUCK3 {
+ regulator-name = "vdd_int";
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck4_reg: BUCK4 {
+ regulator-name = "vdd_g3d";
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <1300000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck5_reg: BUCK5 {
+ regulator-name = "P1.8V_BUCK_OUT5";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+ };
+ };
};
vdd: fixed-regulator@0 {
@@ -96,16 +242,12 @@
samsung,i2c-slave-addr = <0x38>;
status = "okay";
- sata-phy {
- compatible = "samsung,sata-phy";
+ sata_phy_i2c:sata-phy@38 {
+ compatible = "samsung,exynos-sataphy-i2c";
reg = <0x38>;
};
};
- sata@122F0000 {
- samsung,sata-freq = <66>;
- };
-
i2c@12C80000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <66000>;
@@ -128,6 +270,15 @@
};
};
+ sata@122F0000 {
+ status = "okay";
+ };
+
+ sata-phy@12170000 {
+ status = "okay";
+ samsung,exynos-sataphy-i2c-phandle = <&sata_phy_i2c>;
+ };
+
mmc@12200000 {
status = "okay";
num-slots = <1>;
@@ -164,10 +315,6 @@
};
};
- spi_0: spi@12d20000 {
- status = "disabled";
- };
-
spi_1: spi@12d30000 {
status = "okay";
diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index 7e45eea2d78f..1ce1088a00fb 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -14,12 +14,16 @@
/ {
model = "Google Snow";
- compatible = "google,snow", "samsung,exynos5250";
+ compatible = "google,snow", "samsung,exynos5250", "samsung,exynos5";
aliases {
i2c104 = &i2c_104;
};
+ rtc@101E0000 {
+ status = "okay";
+ };
+
pinctrl@11400000 {
sd3_clk: sd3-clk {
samsung,pin-drv = <0>;
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index b7dec41e32af..37423314a028 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -17,13 +17,14 @@
* published by the Free Software Foundation.
*/
+#include <dt-bindings/clock/exynos5250.h>
#include "exynos5.dtsi"
#include "exynos5250-pinctrl.dtsi"
-#include <dt-bindings/clk/exynos-audss-clk.h>
+#include <dt-bindings/clock/exynos-audss-clk.h>
/ {
- compatible = "samsung,exynos5250";
+ compatible = "samsung,exynos5250", "samsung,exynos5";
aliases {
spi0 = &spi_0;
@@ -46,6 +47,7 @@
i2c6 = &i2c_6;
i2c7 = &i2c_7;
i2c8 = &i2c_8;
+ i2c9 = &i2c_9;
pinctrl0 = &pinctrl_0;
pinctrl1 = &pinctrl_1;
pinctrl2 = &pinctrl_2;
@@ -90,7 +92,8 @@
compatible = "samsung,exynos5250-audss-clock";
reg = <0x03810000 0x0C>;
#clock-cells = <1>;
- clocks = <&clock 1>, <&clock 7>, <&clock 138>, <&clock 160>;
+ clocks = <&clock CLK_FIN_PLL>, <&clock CLK_FOUT_EPLL>,
+ <&clock CLK_SCLK_AUDIO0>, <&clock CLK_DIV_PCM0>;
clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in";
};
@@ -115,7 +118,7 @@
interrupt-parent = <&mct_map>;
interrupts = <0 0>, <1 0>, <2 0>, <3 0>,
<4 0>, <5 0>;
- clocks = <&clock 1>, <&clock 335>;
+ clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MCT>;
clock-names = "fin_pll", "mct";
mct_map: mct-map {
@@ -167,16 +170,25 @@
interrupts = <0 47 0>;
};
- watchdog {
- clocks = <&clock 336>;
+ pmu_system_controller: system-controller@10040000 {
+ compatible = "samsung,exynos5250-pmu", "syscon";
+ reg = <0x10040000 0x5000>;
+ };
+
+ watchdog@101D0000 {
+ compatible = "samsung,exynos5250-wdt";
+ reg = <0x101D0000 0x100>;
+ interrupts = <0 42 0>;
+ clocks = <&clock CLK_WDT>;
clock-names = "watchdog";
+ samsung,syscon-phandle = <&pmu_system_controller>;
};
g2d@10850000 {
compatible = "samsung,exynos5250-g2d";
reg = <0x10850000 0x1000>;
interrupts = <0 91 0>;
- clocks = <&clock 345>;
+ clocks = <&clock CLK_G2D>;
clock-names = "fimg2d";
};
@@ -185,55 +197,64 @@
reg = <0x11000000 0x10000>;
interrupts = <0 96 0>;
samsung,power-domain = <&pd_mfc>;
- clocks = <&clock 266>;
+ clocks = <&clock CLK_MFC>;
clock-names = "mfc";
};
rtc@101E0000 {
- clocks = <&clock 337>;
+ clocks = <&clock CLK_RTC>;
clock-names = "rtc";
- status = "okay";
+ status = "disabled";
};
tmu@10060000 {
compatible = "samsung,exynos5250-tmu";
reg = <0x10060000 0x100>;
interrupts = <0 65 0>;
- clocks = <&clock 338>;
+ clocks = <&clock CLK_TMU>;
clock-names = "tmu_apbif";
};
serial@12C00000 {
- clocks = <&clock 289>, <&clock 146>;
+ clocks = <&clock CLK_UART0>, <&clock CLK_SCLK_UART0>;
clock-names = "uart", "clk_uart_baud0";
};
serial@12C10000 {
- clocks = <&clock 290>, <&clock 147>;
+ clocks = <&clock CLK_UART1>, <&clock CLK_SCLK_UART1>;
clock-names = "uart", "clk_uart_baud0";
};
serial@12C20000 {
- clocks = <&clock 291>, <&clock 148>;
+ clocks = <&clock CLK_UART2>, <&clock CLK_SCLK_UART2>;
clock-names = "uart", "clk_uart_baud0";
};
serial@12C30000 {
- clocks = <&clock 292>, <&clock 149>;
+ clocks = <&clock CLK_UART3>, <&clock CLK_SCLK_UART3>;
clock-names = "uart", "clk_uart_baud0";
};
sata@122F0000 {
- compatible = "samsung,exynos5-sata-ahci";
+ compatible = "snps,dwc-ahci";
+ samsung,sata-freq = <66>;
reg = <0x122F0000 0x1ff>;
interrupts = <0 115 0>;
- clocks = <&clock 277>, <&clock 143>;
+ clocks = <&clock CLK_SATA>, <&clock CLK_SCLK_SATA>;
clock-names = "sata", "sclk_sata";
+ phys = <&sata_phy>;
+ phy-names = "sata-phy";
+ status = "disabled";
};
- sata-phy@12170000 {
- compatible = "samsung,exynos5-sata-phy";
+ sata_phy: sata-phy@12170000 {
+ compatible = "samsung,exynos5250-sata-phy";
reg = <0x12170000 0x1ff>;
+ clocks = <&clock 287>;
+ clock-names = "sata_phyctrl";
+ #phy-cells = <0>;
+ samsung,syscon-phandle = <&pmu_system_controller>;
+ status = "disabled";
};
i2c_0: i2c@12C60000 {
@@ -242,7 +263,7 @@
interrupts = <0 56 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 294>;
+ clocks = <&clock CLK_I2C0>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c0_bus>;
@@ -255,7 +276,7 @@
interrupts = <0 57 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 295>;
+ clocks = <&clock CLK_I2C1>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c1_bus>;
@@ -268,7 +289,7 @@
interrupts = <0 58 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 296>;
+ clocks = <&clock CLK_I2C2>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c2_bus>;
@@ -281,7 +302,7 @@
interrupts = <0 59 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 297>;
+ clocks = <&clock CLK_I2C3>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c3_bus>;
@@ -294,7 +315,7 @@
interrupts = <0 60 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 298>;
+ clocks = <&clock CLK_I2C4>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c4_bus>;
@@ -307,7 +328,7 @@
interrupts = <0 61 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 299>;
+ clocks = <&clock CLK_I2C5>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c5_bus>;
@@ -320,7 +341,7 @@
interrupts = <0 62 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 300>;
+ clocks = <&clock CLK_I2C6>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c6_bus>;
@@ -333,7 +354,7 @@
interrupts = <0 63 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 301>;
+ clocks = <&clock CLK_I2C7>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c7_bus>;
@@ -346,17 +367,17 @@
interrupts = <0 64 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 302>;
+ clocks = <&clock CLK_I2C_HDMI>;
clock-names = "i2c";
status = "disabled";
};
- i2c@121D0000 {
+ i2c_9: i2c@121D0000 {
compatible = "samsung,exynos5-sata-phy-i2c";
reg = <0x121D0000 0x100>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 288>;
+ clocks = <&clock CLK_SATA_PHYI2C>;
clock-names = "i2c";
status = "disabled";
};
@@ -371,7 +392,7 @@
dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 304>, <&clock 154>;
+ clocks = <&clock CLK_SPI0>, <&clock CLK_SCLK_SPI0>;
clock-names = "spi", "spi_busclk0";
pinctrl-names = "default";
pinctrl-0 = <&spi0_bus>;
@@ -387,7 +408,7 @@
dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 305>, <&clock 155>;
+ clocks = <&clock CLK_SPI1>, <&clock CLK_SCLK_SPI1>;
clock-names = "spi", "spi_busclk0";
pinctrl-names = "default";
pinctrl-0 = <&spi1_bus>;
@@ -403,7 +424,7 @@
dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 306>, <&clock 156>;
+ clocks = <&clock CLK_SPI2>, <&clock CLK_SCLK_SPI2>;
clock-names = "spi", "spi_busclk0";
pinctrl-names = "default";
pinctrl-0 = <&spi2_bus>;
@@ -415,7 +436,7 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x12200000 0x1000>;
- clocks = <&clock 280>, <&clock 139>;
+ clocks = <&clock CLK_SDMMC0>, <&clock CLK_SCLK_MMC0>;
clock-names = "biu", "ciu";
fifo-depth = <0x80>;
status = "disabled";
@@ -427,7 +448,7 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x12210000 0x1000>;
- clocks = <&clock 281>, <&clock 140>;
+ clocks = <&clock CLK_SDMMC1>, <&clock CLK_SCLK_MMC1>;
clock-names = "biu", "ciu";
fifo-depth = <0x80>;
status = "disabled";
@@ -439,7 +460,7 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x12220000 0x1000>;
- clocks = <&clock 282>, <&clock 141>;
+ clocks = <&clock CLK_SDMMC2>, <&clock CLK_SCLK_MMC2>;
clock-names = "biu", "ciu";
fifo-depth = <0x80>;
status = "disabled";
@@ -451,7 +472,7 @@
interrupts = <0 78 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 283>, <&clock 142>;
+ clocks = <&clock CLK_SDMMC3>, <&clock CLK_SCLK_MMC3>;
clock-names = "biu", "ciu";
fifo-depth = <0x80>;
status = "disabled";
@@ -481,7 +502,7 @@
dmas = <&pdma1 12
&pdma1 11>;
dma-names = "tx", "rx";
- clocks = <&clock 307>, <&clock 157>;
+ clocks = <&clock CLK_I2S1>, <&clock CLK_DIV_I2S1>;
clock-names = "iis", "i2s_opclk0";
pinctrl-names = "default";
pinctrl-0 = <&i2s1_bus>;
@@ -494,7 +515,7 @@
dmas = <&pdma0 12
&pdma0 11>;
dma-names = "tx", "rx";
- clocks = <&clock 308>, <&clock 158>;
+ clocks = <&clock CLK_I2S2>, <&clock CLK_DIV_I2S2>;
clock-names = "iis", "i2s_opclk0";
pinctrl-names = "default";
pinctrl-0 = <&i2s2_bus>;
@@ -502,7 +523,7 @@
usb@12000000 {
compatible = "samsung,exynos5250-dwusb3";
- clocks = <&clock 286>;
+ clocks = <&clock CLK_USB3>;
clock-names = "usbdrd30";
#address-cells = <1>;
#size-cells = <1>;
@@ -519,7 +540,7 @@
usb3_phy: usbphy@12100000 {
compatible = "samsung,exynos5250-usb3phy";
reg = <0x12100000 0x100>;
- clocks = <&clock 1>, <&clock 286>;
+ clocks = <&clock CLK_FIN_PLL>, <&clock CLK_USB3>;
clock-names = "ext_xtal", "usbdrd30";
#address-cells = <1>;
#size-cells = <1>;
@@ -535,7 +556,7 @@
reg = <0x12110000 0x100>;
interrupts = <0 71 0>;
- clocks = <&clock 285>;
+ clocks = <&clock CLK_USB2>;
clock-names = "usbhost";
};
@@ -544,14 +565,14 @@
reg = <0x12120000 0x100>;
interrupts = <0 71 0>;
- clocks = <&clock 285>;
+ clocks = <&clock CLK_USB2>;
clock-names = "usbhost";
};
usb2_phy: usbphy@12130000 {
compatible = "samsung,exynos5250-usb2phy";
reg = <0x12130000 0x100>;
- clocks = <&clock 1>, <&clock 285>;
+ clocks = <&clock CLK_FIN_PLL>, <&clock CLK_USB2>;
clock-names = "ext_xtal", "usbhost";
#address-cells = <1>;
#size-cells = <1>;
@@ -568,7 +589,7 @@
reg = <0x12dd0000 0x100>;
samsung,pwm-outputs = <0>, <1>, <2>, <3>;
#pwm-cells = <3>;
- clocks = <&clock 311>;
+ clocks = <&clock CLK_PWM>;
clock-names = "timers";
};
@@ -583,7 +604,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x121A0000 0x1000>;
interrupts = <0 34 0>;
- clocks = <&clock 275>;
+ clocks = <&clock CLK_PDMA0>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
@@ -594,7 +615,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x121B0000 0x1000>;
interrupts = <0 35 0>;
- clocks = <&clock 276>;
+ clocks = <&clock CLK_PDMA1>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
@@ -605,7 +626,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x10800000 0x1000>;
interrupts = <0 33 0>;
- clocks = <&clock 346>;
+ clocks = <&clock CLK_MDMA0>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
@@ -616,7 +637,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x11C10000 0x1000>;
interrupts = <0 124 0>;
- clocks = <&clock 271>;
+ clocks = <&clock CLK_MDMA1>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
@@ -629,7 +650,7 @@
reg = <0x13e00000 0x1000>;
interrupts = <0 85 0>;
samsung,power-domain = <&pd_gsc>;
- clocks = <&clock 256>;
+ clocks = <&clock CLK_GSCL0>;
clock-names = "gscl";
};
@@ -638,7 +659,7 @@
reg = <0x13e10000 0x1000>;
interrupts = <0 86 0>;
samsung,power-domain = <&pd_gsc>;
- clocks = <&clock 257>;
+ clocks = <&clock CLK_GSCL1>;
clock-names = "gscl";
};
@@ -647,7 +668,7 @@
reg = <0x13e20000 0x1000>;
interrupts = <0 87 0>;
samsung,power-domain = <&pd_gsc>;
- clocks = <&clock 258>;
+ clocks = <&clock CLK_GSCL2>;
clock-names = "gscl";
};
@@ -656,7 +677,7 @@
reg = <0x13e30000 0x1000>;
interrupts = <0 88 0>;
samsung,power-domain = <&pd_gsc>;
- clocks = <&clock 259>;
+ clocks = <&clock CLK_GSCL3>;
clock-names = "gscl";
};
@@ -664,8 +685,9 @@
compatible = "samsung,exynos4212-hdmi";
reg = <0x14530000 0x70000>;
interrupts = <0 95 0>;
- clocks = <&clock 344>, <&clock 136>, <&clock 137>,
- <&clock 159>, <&clock 1024>;
+ clocks = <&clock CLK_HDMI>, <&clock CLK_SCLK_HDMI>,
+ <&clock CLK_SCLK_PIXEL>, <&clock CLK_SCLK_HDMIPHY>,
+ <&clock CLK_MOUT_HDMI>;
clock-names = "hdmi", "sclk_hdmi", "sclk_pixel",
"sclk_hdmiphy", "mout_hdmi";
};
@@ -674,7 +696,7 @@
compatible = "samsung,exynos5250-mixer";
reg = <0x14450000 0x10000>;
interrupts = <0 94 0>;
- clocks = <&clock 343>, <&clock 136>;
+ clocks = <&clock CLK_MIXER>, <&clock CLK_SCLK_HDMI>;
clock-names = "mixer", "sclk_hdmi";
};
@@ -685,14 +707,14 @@
};
dp-controller@145B0000 {
- clocks = <&clock 342>;
+ clocks = <&clock CLK_DP>;
clock-names = "dp";
phys = <&dp_phy>;
phy-names = "dp";
};
fimd@14400000 {
- clocks = <&clock 133>, <&clock 339>;
+ clocks = <&clock CLK_SCLK_FIMD1>, <&clock CLK_FIMD1>;
clock-names = "sclk_fimd", "fimd";
};
@@ -700,10 +722,18 @@
compatible = "samsung,exynos-adc-v1";
reg = <0x12D10000 0x100>, <0x10040718 0x4>;
interrupts = <0 106 0>;
- clocks = <&clock 303>;
+ clocks = <&clock CLK_ADC>;
clock-names = "adc";
#io-channel-cells = <1>;
io-channel-ranges;
status = "disabled";
};
+
+ sss@10830000 {
+ compatible = "samsung,exynos4210-secss";
+ reg = <0x10830000 0x10000>;
+ interrupts = <0 112 0>;
+ clocks = <&clock 348>;
+ clock-names = "secss";
+ };
};
diff --git a/arch/arm/boot/dts/exynos5420-arndale-octa.dts b/arch/arm/boot/dts/exynos5420-arndale-octa.dts
index 7340745ff979..80a3bf4c5986 100644
--- a/arch/arm/boot/dts/exynos5420-arndale-octa.dts
+++ b/arch/arm/boot/dts/exynos5420-arndale-octa.dts
@@ -11,10 +11,12 @@
/dts-v1/;
#include "exynos5420.dtsi"
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/input/input.h>
/ {
model = "Insignal Arndale Octa evaluation board based on EXYNOS5420";
- compatible = "insignal,arndale-octa", "samsung,exynos5420";
+ compatible = "insignal,arndale-octa", "samsung,exynos5420", "samsung,exynos5";
memory {
reg = <0x20000000 0x80000000>;
@@ -31,6 +33,10 @@
};
};
+ rtc@101E0000 {
+ status = "okay";
+ };
+
mmc@12200000 {
status = "okay";
broken-cd;
@@ -41,6 +47,7 @@
samsung,dw-mshc-ddr-timing = <0 2>;
pinctrl-names = "default";
pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4 &sd0_bus8>;
+ vmmc-supply = <&ldo10_reg>;
slot@0 {
reg = <0>;
@@ -57,10 +64,316 @@
samsung,dw-mshc-ddr-timing = <1 2>;
pinctrl-names = "default";
pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
+ vmmc-supply = <&ldo10_reg>;
slot@0 {
reg = <0>;
bus-width = <4>;
};
};
+
+ hsi2c_4: i2c@12CA0000 {
+ status = "okay";
+
+ s2mps11_pmic@66 {
+ compatible = "samsung,s2mps11-pmic";
+ reg = <0x66>;
+ s2mps11,buck2-ramp-delay = <12>;
+ s2mps11,buck34-ramp-delay = <12>;
+ s2mps11,buck16-ramp-delay = <12>;
+ s2mps11,buck6-ramp-enable = <1>;
+ s2mps11,buck2-ramp-enable = <1>;
+ s2mps11,buck3-ramp-enable = <1>;
+ s2mps11,buck4-ramp-enable = <1>;
+
+ interrupt-parent = <&gpx3>;
+ interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
+
+ s2mps11_osc: clocks {
+ #clock-cells = <1>;
+ clock-output-names = "s2mps11_ap",
+ "s2mps11_cp", "s2mps11_bt";
+ };
+
+ regulators {
+ ldo1_reg: LDO1 {
+ regulator-name = "PVDD_ALIVE_1V0";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ ldo2_reg: LDO2 {
+ regulator-name = "PVDD_APIO_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo3_reg: LDO3 {
+ regulator-name = "PVDD_APIO_MMCON_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ ldo4_reg: LDO4 {
+ regulator-name = "PVDD_ADC_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo5_reg: LDO5 {
+ regulator-name = "PVDD_PLL_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ ldo6_reg: LDO6 {
+ regulator-name = "PVDD_ANAIP_1V0";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ };
+
+ ldo7_reg: LDO7 {
+ regulator-name = "PVDD_ANAIP_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo8_reg: LDO8 {
+ regulator-name = "PVDD_ABB_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo9_reg: LDO9 {
+ regulator-name = "PVDD_USB_3V3";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-always-on;
+ };
+
+ ldo10_reg: LDO10 {
+ regulator-name = "PVDD_PRE_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ ldo11_reg: LDO11 {
+ regulator-name = "PVDD_USB_1V0";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ ldo12_reg: LDO12 {
+ regulator-name = "PVDD_HSIC_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo13_reg: LDO13 {
+ regulator-name = "PVDD_APIO_MMCOFF_2V8";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ };
+
+ ldo15_reg: LDO15 {
+ regulator-name = "PVDD_PERI_2V8";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ ldo16_reg: LDO16 {
+ regulator-name = "PVDD_PERI_3V3";
+ regulator-min-microvolt = <2200000>;
+ regulator-max-microvolt = <2200000>;
+ };
+
+ ldo18_reg: LDO18 {
+ regulator-name = "PVDD_EMMC_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo19_reg: LDO19 {
+ regulator-name = "PVDD_TFLASH_2V8";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ };
+
+ ldo20_reg: LDO20 {
+ regulator-name = "PVDD_BTWIFI_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo21_reg: LDO21 {
+ regulator-name = "PVDD_CAM1IO_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo23_reg: LDO23 {
+ regulator-name = "PVDD_MIFS_1V1";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ };
+
+ ldo24_reg: LDO24 {
+ regulator-name = "PVDD_CAM1_AVDD_2V8";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ };
+
+ ldo26_reg: LDO26 {
+ regulator-name = "PVDD_CAM0_AF_2V8";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ };
+
+ ldo27_reg: LDO27 {
+ regulator-name = "PVDD_G3DS_1V0";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ };
+
+ ldo28_reg: LDO28 {
+ regulator-name = "PVDD_TSP_3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ ldo29_reg: LDO29 {
+ regulator-name = "PVDD_AUDIO_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo31_reg: LDO31 {
+ regulator-name = "PVDD_PERI_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo32_reg: LDO32 {
+ regulator-name = "PVDD_LCD_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo33_reg: LDO33 {
+ regulator-name = "PVDD_CAM0IO_1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ ldo35_reg: LDO35 {
+ regulator-name = "PVDD_CAM0_DVDD_1V2";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ };
+
+ ldo38_reg: LDO38 {
+ regulator-name = "PVDD_CAM0_AVDD_2V8";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ };
+
+ buck1_reg: BUCK1 {
+ regulator-name = "PVDD_MIF_1V1";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ };
+
+ buck2_reg: BUCK2 {
+ regulator-name = "vdd_arm";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ buck3_reg: BUCK3 {
+ regulator-name = "PVDD_INT_1V0";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ buck4_reg: BUCK4 {
+ regulator-name = "PVDD_G3D_1V0";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1000000>;
+ };
+
+ buck5_reg: BUCK5 {
+ regulator-name = "PVDD_LPDDR3_1V2";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ };
+
+ buck6_reg: BUCK6 {
+ regulator-name = "PVDD_KFC_1V0";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ buck7_reg: BUCK7 {
+ regulator-name = "VIN_LLDO_1V4";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-always-on;
+ };
+
+ buck8_reg: BUCK8 {
+ regulator-name = "VIN_MLDO_2V0";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-always-on;
+ };
+
+ buck9_reg: BUCK9 {
+ regulator-name = "VIN_HLDO_3V5";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3500000>;
+ regulator-always-on;
+ };
+
+ buck10_reg: BUCK10 {
+ regulator-name = "PVDD_EMMCF_2V8";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ };
+ };
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+
+ wakeup {
+ label = "SW-TACT1";
+ gpios = <&gpx2 7 1>;
+ linux,code = <KEY_WAKEUP>;
+ gpio-key,wakeup;
+ };
+ };
+
+ amba {
+ mdma1: mdma@11C10000 {
+ /*
+ * MDMA1 can support both secure and non-secure
+ * AXI transactions. When this is enabled in the kernel
+ * for boards that run in secure mode, we are getting
+ * imprecise external aborts causing the kernel to oops.
+ */
+ status = "disabled";
+ };
+ };
};
diff --git a/arch/arm/boot/dts/exynos5420-smdk5420.dts b/arch/arm/boot/dts/exynos5420-smdk5420.dts
index fb5a1e25c632..69104850eb5e 100644
--- a/arch/arm/boot/dts/exynos5420-smdk5420.dts
+++ b/arch/arm/boot/dts/exynos5420-smdk5420.dts
@@ -14,7 +14,7 @@
/ {
model = "Samsung SMDK5420 board based on EXYNOS5420";
- compatible = "samsung,smdk5420", "samsung,exynos5420";
+ compatible = "samsung,smdk5420", "samsung,exynos5420", "samsung,exynos5";
memory {
reg = <0x20000000 0x80000000>;
@@ -31,6 +31,43 @@
};
};
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ vdd: fixed-regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vdd-supply";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ dbvdd: fixed-regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "dbvdd-supply";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ spkvdd: fixed-regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "spkvdd-supply";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+ };
+
+ rtc@101E0000 {
+ status = "okay";
+ };
+
mmc@12200000 {
status = "okay";
broken-cd;
@@ -120,4 +157,220 @@
reg = <0x50>;
};
};
+
+ hsi2c_4: i2c@12CA0000 {
+ status = "okay";
+
+ s2mps11_pmic@66 {
+ compatible = "samsung,s2mps11-pmic";
+ reg = <0x66>;
+ s2mps11,buck2-ramp-delay = <12>;
+ s2mps11,buck34-ramp-delay = <12>;
+ s2mps11,buck16-ramp-delay = <12>;
+ s2mps11,buck6-ramp-enable = <1>;
+ s2mps11,buck2-ramp-enable = <1>;
+ s2mps11,buck3-ramp-enable = <1>;
+ s2mps11,buck4-ramp-enable = <1>;
+
+ s2mps11_osc: clocks {
+ #clock-cells = <1>;
+ clock-output-names = "s2mps11_ap",
+ "s2mps11_cp", "s2mps11_bt";
+ };
+
+ regulators {
+ ldo1_reg: LDO1 {
+ regulator-name = "vdd_ldo1";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ ldo3_reg: LDO3 {
+ regulator-name = "vdd_ldo3";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ ldo5_reg: LDO5 {
+ regulator-name = "vdd_ldo5";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ ldo6_reg: LDO6 {
+ regulator-name = "vdd_ldo6";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ ldo7_reg: LDO7 {
+ regulator-name = "vdd_ldo7";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ ldo8_reg: LDO8 {
+ regulator-name = "vdd_ldo8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ ldo9_reg: LDO9 {
+ regulator-name = "vdd_ldo9";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-always-on;
+ };
+
+ ldo10_reg: LDO10 {
+ regulator-name = "vdd_ldo10";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ ldo11_reg: LDO11 {
+ regulator-name = "vdd_ldo11";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ ldo12_reg: LDO12 {
+ regulator-name = "vdd_ldo12";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ ldo13_reg: LDO13 {
+ regulator-name = "vdd_ldo13";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+ };
+
+ ldo15_reg: LDO15 {
+ regulator-name = "vdd_ldo15";
+ regulator-min-microvolt = <3100000>;
+ regulator-max-microvolt = <3100000>;
+ regulator-always-on;
+ };
+
+ ldo16_reg: LDO16 {
+ regulator-name = "vdd_ldo16";
+ regulator-min-microvolt = <2200000>;
+ regulator-max-microvolt = <2200000>;
+ regulator-always-on;
+ };
+
+ ldo17_reg: LDO17 {
+ regulator-name = "tsp_avdd";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ ldo19_reg: LDO19 {
+ regulator-name = "vdd_sd";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+ };
+
+ ldo24_reg: LDO24 {
+ regulator-name = "tsp_io";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+ };
+
+ buck1_reg: BUCK1 {
+ regulator-name = "vdd_mif";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1300000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck2_reg: BUCK2 {
+ regulator-name = "vdd_arm";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck3_reg: BUCK3 {
+ regulator-name = "vdd_int";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck4_reg: BUCK4 {
+ regulator-name = "vdd_g3d";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck5_reg: BUCK5 {
+ regulator-name = "vdd_mem";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck6_reg: BUCK6 {
+ regulator-name = "vdd_kfc";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck7_reg: BUCK7 {
+ regulator-name = "vdd_1.0v_ldo";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck8_reg: BUCK8 {
+ regulator-name = "vdd_1.8v_ldo";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck9_reg: BUCK9 {
+ regulator-name = "vdd_2.8v_ldo";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3750000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck10_reg: BUCK10 {
+ regulator-name = "vdd_vmem";
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+ };
+ };
+ };
};
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi
index 8db792b26f79..c3a9a66c5767 100644
--- a/arch/arm/boot/dts/exynos5420.dtsi
+++ b/arch/arm/boot/dts/exynos5420.dtsi
@@ -13,13 +13,14 @@
* published by the Free Software Foundation.
*/
+#include <dt-bindings/clock/exynos5420.h>
#include "exynos5.dtsi"
#include "exynos5420-pinctrl.dtsi"
-#include <dt-bindings/clk/exynos-audss-clk.h>
+#include <dt-bindings/clock/exynos-audss-clk.h>
/ {
- compatible = "samsung,exynos5420";
+ compatible = "samsung,exynos5420", "samsung,exynos5";
aliases {
mshc0 = &mmc_0;
@@ -119,7 +120,8 @@
compatible = "samsung,exynos5420-audss-clock";
reg = <0x03810000 0x0C>;
#clock-cells = <1>;
- clocks = <&clock 1>, <&clock 5>, <&clock 148>, <&clock 149>;
+ clocks = <&clock CLK_FIN_PLL>, <&clock CLK_FOUT_EPLL>,
+ <&clock CLK_SCLK_MAUDIO0>, <&clock CLK_SCLK_MAUPCM0>;
clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in";
};
@@ -127,7 +129,7 @@
compatible = "samsung,mfc-v7";
reg = <0x11000000 0x10000>;
interrupts = <0 96 0>;
- clocks = <&clock 401>;
+ clocks = <&clock CLK_MFC>;
clock-names = "mfc";
};
@@ -137,7 +139,7 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x12200000 0x2000>;
- clocks = <&clock 351>, <&clock 132>;
+ clocks = <&clock CLK_MMC0>, <&clock CLK_SCLK_MMC0>;
clock-names = "biu", "ciu";
fifo-depth = <0x40>;
status = "disabled";
@@ -149,7 +151,7 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x12210000 0x2000>;
- clocks = <&clock 352>, <&clock 133>;
+ clocks = <&clock CLK_MMC1>, <&clock CLK_SCLK_MMC1>;
clock-names = "biu", "ciu";
fifo-depth = <0x40>;
status = "disabled";
@@ -161,7 +163,7 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x12220000 0x1000>;
- clocks = <&clock 353>, <&clock 134>;
+ clocks = <&clock CLK_MMC2>, <&clock CLK_SCLK_MMC2>;
clock-names = "biu", "ciu";
fifo-depth = <0x40>;
status = "disabled";
@@ -175,7 +177,7 @@
interrupt-parent = <&mct_map>;
interrupts = <0>, <1>, <2>, <3>, <4>, <5>, <6>, <7>,
<8>, <9>, <10>, <11>;
- clocks = <&clock 1>, <&clock 315>;
+ clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MCT>;
clock-names = "fin_pll", "mct";
mct_map: mct-map {
@@ -269,9 +271,9 @@
};
rtc@101E0000 {
- clocks = <&clock 317>;
+ clocks = <&clock CLK_RTC>;
clock-names = "rtc";
- status = "okay";
+ status = "disabled";
};
amba {
@@ -281,11 +283,22 @@
interrupt-parent = <&gic>;
ranges;
+ adma: adma@03880000 {
+ compatible = "arm,pl330", "arm,primecell";
+ reg = <0x03880000 0x1000>;
+ interrupts = <0 110 0>;
+ clocks = <&clock_audss EXYNOS_ADMA>;
+ clock-names = "apb_pclk";
+ #dma-cells = <1>;
+ #dma-channels = <6>;
+ #dma-requests = <16>;
+ };
+
pdma0: pdma@121A0000 {
compatible = "arm,pl330", "arm,primecell";
reg = <0x121A0000 0x1000>;
interrupts = <0 34 0>;
- clocks = <&clock 362>;
+ clocks = <&clock CLK_PDMA0>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
@@ -296,7 +309,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x121B0000 0x1000>;
interrupts = <0 35 0>;
- clocks = <&clock 363>;
+ clocks = <&clock CLK_PDMA1>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
@@ -307,7 +320,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x10800000 0x1000>;
interrupts = <0 33 0>;
- clocks = <&clock 473>;
+ clocks = <&clock CLK_MDMA0>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
@@ -318,7 +331,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x11C10000 0x1000>;
interrupts = <0 124 0>;
- clocks = <&clock 442>;
+ clocks = <&clock CLK_MDMA1>;
clock-names = "apb_pclk";
#dma-cells = <1>;
#dma-channels = <8>;
@@ -326,6 +339,49 @@
};
};
+ i2s0: i2s@03830000 {
+ compatible = "samsung,exynos5420-i2s";
+ reg = <0x03830000 0x100>;
+ dmas = <&adma 0
+ &adma 2
+ &adma 1>;
+ dma-names = "tx", "rx", "tx-sec";
+ clocks = <&clock_audss EXYNOS_I2S_BUS>,
+ <&clock_audss EXYNOS_I2S_BUS>,
+ <&clock_audss EXYNOS_SCLK_I2S>;
+ clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
+ samsung,idma-addr = <0x03000000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s0_bus>;
+ status = "disabled";
+ };
+
+ i2s1: i2s@12D60000 {
+ compatible = "samsung,exynos5420-i2s";
+ reg = <0x12D60000 0x100>;
+ dmas = <&pdma1 12
+ &pdma1 11>;
+ dma-names = "tx", "rx";
+ clocks = <&clock CLK_I2S1>, <&clock CLK_SCLK_I2S1>;
+ clock-names = "iis", "i2s_opclk0";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s1_bus>;
+ status = "disabled";
+ };
+
+ i2s2: i2s@12D70000 {
+ compatible = "samsung,exynos5420-i2s";
+ reg = <0x12D70000 0x100>;
+ dmas = <&pdma0 12
+ &pdma0 11>;
+ dma-names = "tx", "rx";
+ clocks = <&clock CLK_I2S2>, <&clock CLK_SCLK_I2S2>;
+ clock-names = "iis", "i2s_opclk0";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s2_bus>;
+ status = "disabled";
+ };
+
spi_0: spi@12d20000 {
compatible = "samsung,exynos4210-spi";
reg = <0x12d20000 0x100>;
@@ -337,7 +393,7 @@
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&spi0_bus>;
- clocks = <&clock 271>, <&clock 135>;
+ clocks = <&clock CLK_SPI0>, <&clock CLK_SCLK_SPI0>;
clock-names = "spi", "spi_busclk0";
status = "disabled";
};
@@ -353,7 +409,7 @@
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&spi1_bus>;
- clocks = <&clock 272>, <&clock 136>;
+ clocks = <&clock CLK_SPI1>, <&clock CLK_SCLK_SPI1>;
clock-names = "spi", "spi_busclk0";
status = "disabled";
};
@@ -369,28 +425,28 @@
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&spi2_bus>;
- clocks = <&clock 273>, <&clock 137>;
+ clocks = <&clock CLK_SPI2>, <&clock CLK_SCLK_SPI2>;
clock-names = "spi", "spi_busclk0";
status = "disabled";
};
serial@12C00000 {
- clocks = <&clock 257>, <&clock 128>;
+ clocks = <&clock CLK_UART0>, <&clock CLK_SCLK_UART0>;
clock-names = "uart", "clk_uart_baud0";
};
serial@12C10000 {
- clocks = <&clock 258>, <&clock 129>;
+ clocks = <&clock CLK_UART1>, <&clock CLK_SCLK_UART1>;
clock-names = "uart", "clk_uart_baud0";
};
serial@12C20000 {
- clocks = <&clock 259>, <&clock 130>;
+ clocks = <&clock CLK_UART2>, <&clock CLK_SCLK_UART2>;
clock-names = "uart", "clk_uart_baud0";
};
serial@12C30000 {
- clocks = <&clock 260>, <&clock 131>;
+ clocks = <&clock CLK_UART3>, <&clock CLK_SCLK_UART3>;
clock-names = "uart", "clk_uart_baud0";
};
@@ -399,7 +455,7 @@
reg = <0x12dd0000 0x100>;
samsung,pwm-outputs = <0>, <1>, <2>, <3>;
#pwm-cells = <3>;
- clocks = <&clock 279>;
+ clocks = <&clock CLK_PWM>;
clock-names = "timers";
};
@@ -410,7 +466,7 @@
};
dp-controller@145B0000 {
- clocks = <&clock 412>;
+ clocks = <&clock CLK_DP1>;
clock-names = "dp";
phys = <&dp_phy>;
phy-names = "dp";
@@ -418,7 +474,7 @@
fimd@14400000 {
samsung,power-domain = <&disp_pd>;
- clocks = <&clock 147>, <&clock 421>;
+ clocks = <&clock CLK_SCLK_FIMD1>, <&clock CLK_FIMD1>;
clock-names = "sclk_fimd", "fimd";
};
@@ -426,7 +482,7 @@
compatible = "samsung,exynos-adc-v2";
reg = <0x12D10000 0x100>, <0x10040720 0x4>;
interrupts = <0 106 0>;
- clocks = <&clock 270>;
+ clocks = <&clock CLK_TSADC>;
clock-names = "adc";
#io-channel-cells = <1>;
io-channel-ranges;
@@ -439,7 +495,7 @@
interrupts = <0 56 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 261>;
+ clocks = <&clock CLK_I2C0>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c0_bus>;
@@ -452,7 +508,7 @@
interrupts = <0 57 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 262>;
+ clocks = <&clock CLK_I2C1>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c1_bus>;
@@ -465,7 +521,7 @@
interrupts = <0 58 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 263>;
+ clocks = <&clock CLK_I2C2>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c2_bus>;
@@ -478,7 +534,7 @@
interrupts = <0 59 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 264>;
+ clocks = <&clock CLK_I2C3>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c3_bus>;
@@ -493,7 +549,7 @@
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&i2c4_hs_bus>;
- clocks = <&clock 265>;
+ clocks = <&clock CLK_I2C4>;
clock-names = "hsi2c";
status = "disabled";
};
@@ -506,7 +562,7 @@
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&i2c5_hs_bus>;
- clocks = <&clock 266>;
+ clocks = <&clock CLK_I2C5>;
clock-names = "hsi2c";
status = "disabled";
};
@@ -519,7 +575,7 @@
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&i2c6_hs_bus>;
- clocks = <&clock 267>;
+ clocks = <&clock CLK_I2C6>;
clock-names = "hsi2c";
status = "disabled";
};
@@ -532,7 +588,7 @@
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&i2c7_hs_bus>;
- clocks = <&clock 268>;
+ clocks = <&clock CLK_I2C7>;
clock-names = "hsi2c";
status = "disabled";
};
@@ -545,7 +601,7 @@
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&i2c8_hs_bus>;
- clocks = <&clock 281>;
+ clocks = <&clock CLK_I2C8>;
clock-names = "hsi2c";
status = "disabled";
};
@@ -558,7 +614,7 @@
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&i2c9_hs_bus>;
- clocks = <&clock 282>;
+ clocks = <&clock CLK_I2C9>;
clock-names = "hsi2c";
status = "disabled";
};
@@ -571,7 +627,7 @@
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&i2c10_hs_bus>;
- clocks = <&clock 283>;
+ clocks = <&clock CLK_I2C10>;
clock-names = "hsi2c";
status = "disabled";
};
@@ -580,8 +636,9 @@
compatible = "samsung,exynos4212-hdmi";
reg = <0x14530000 0x70000>;
interrupts = <0 95 0>;
- clocks = <&clock 413>, <&clock 143>, <&clock 768>,
- <&clock 158>, <&clock 640>;
+ clocks = <&clock CLK_HDMI>, <&clock CLK_SCLK_HDMI>,
+ <&clock CLK_DOUT_PIXEL>, <&clock CLK_SCLK_HDMIPHY>,
+ <&clock CLK_MOUT_HDMI>;
clock-names = "hdmi", "sclk_hdmi", "sclk_pixel",
"sclk_hdmiphy", "mout_hdmi";
status = "disabled";
@@ -591,7 +648,7 @@
compatible = "samsung,exynos5420-mixer";
reg = <0x14450000 0x10000>;
interrupts = <0 94 0>;
- clocks = <&clock 431>, <&clock 143>;
+ clocks = <&clock CLK_MIXER>, <&clock CLK_SCLK_HDMI>;
clock-names = "mixer", "sclk_hdmi";
};
@@ -599,7 +656,7 @@
compatible = "samsung,exynos5-gsc";
reg = <0x13e00000 0x1000>;
interrupts = <0 85 0>;
- clocks = <&clock 465>;
+ clocks = <&clock CLK_GSCL0>;
clock-names = "gscl";
samsung,power-domain = <&gsc_pd>;
};
@@ -608,16 +665,21 @@
compatible = "samsung,exynos5-gsc";
reg = <0x13e10000 0x1000>;
interrupts = <0 86 0>;
- clocks = <&clock 466>;
+ clocks = <&clock CLK_GSCL1>;
clock-names = "gscl";
samsung,power-domain = <&gsc_pd>;
};
+ pmu_system_controller: system-controller@10040000 {
+ compatible = "samsung,exynos5420-pmu", "syscon";
+ reg = <0x10040000 0x5000>;
+ };
+
tmu_cpu0: tmu@10060000 {
compatible = "samsung,exynos5420-tmu";
reg = <0x10060000 0x100>;
interrupts = <0 65 0>;
- clocks = <&clock 318>;
+ clocks = <&clock CLK_TMU>;
clock-names = "tmu_apbif";
};
@@ -625,7 +687,7 @@
compatible = "samsung,exynos5420-tmu";
reg = <0x10064000 0x100>;
interrupts = <0 183 0>;
- clocks = <&clock 318>;
+ clocks = <&clock CLK_TMU>;
clock-names = "tmu_apbif";
};
@@ -633,7 +695,7 @@
compatible = "samsung,exynos5420-tmu-ext-triminfo";
reg = <0x10068000 0x100>, <0x1006c000 0x4>;
interrupts = <0 184 0>;
- clocks = <&clock 318>, <&clock 318>;
+ clocks = <&clock CLK_TMU>, <&clock CLK_TMU>;
clock-names = "tmu_apbif", "tmu_triminfo_apbif";
};
@@ -641,7 +703,7 @@
compatible = "samsung,exynos5420-tmu-ext-triminfo";
reg = <0x1006c000 0x100>, <0x100a0000 0x4>;
interrupts = <0 185 0>;
- clocks = <&clock 318>, <&clock 319>;
+ clocks = <&clock CLK_TMU>, <&clock CLK_TMU_GPU>;
clock-names = "tmu_apbif", "tmu_triminfo_apbif";
};
@@ -649,7 +711,25 @@
compatible = "samsung,exynos5420-tmu-ext-triminfo";
reg = <0x100a0000 0x100>, <0x10068000 0x4>;
interrupts = <0 215 0>;
- clocks = <&clock 319>, <&clock 318>;
+ clocks = <&clock CLK_TMU_GPU>, <&clock CLK_TMU>;
clock-names = "tmu_apbif", "tmu_triminfo_apbif";
};
+
+ watchdog@101D0000 {
+ compatible = "samsung,exynos5420-wdt";
+ reg = <0x101D0000 0x100>;
+ interrupts = <0 42 0>;
+ clocks = <&clock CLK_WDT>;
+ clock-names = "watchdog";
+ samsung,syscon-phandle = <&pmu_system_controller>;
+ };
+
+ sss@10830000 {
+ compatible = "samsung,exynos4210-secss";
+ reg = <0x10830000 0x10000>;
+ interrupts = <0 112 0>;
+ clocks = <&clock 471>;
+ clock-names = "secss";
+ samsung,power-domain = <&g2d_pd>;
+ };
};
diff --git a/arch/arm/boot/dts/exynos5440-sd5v1.dts b/arch/arm/boot/dts/exynos5440-sd5v1.dts
index 777fb1c2c70f..268609a42b2c 100644
--- a/arch/arm/boot/dts/exynos5440-sd5v1.dts
+++ b/arch/arm/boot/dts/exynos5440-sd5v1.dts
@@ -14,7 +14,7 @@
/ {
model = "SAMSUNG SD5v1 board based on EXYNOS5440";
- compatible = "samsung,sd5v1", "samsung,exynos5440";
+ compatible = "samsung,sd5v1", "samsung,exynos5440", "samsung,exynos5";
chosen {
bootargs = "root=/dev/sda2 rw rootwait ignore_loglevel earlyprintk no_console_suspend mem=2048M@0x80000000 mem=6144M@0x100000000 console=ttySAC0,115200";
diff --git a/arch/arm/boot/dts/exynos5440-ssdk5440.dts b/arch/arm/boot/dts/exynos5440-ssdk5440.dts
index d58cb787061a..ff55dac6e219 100644
--- a/arch/arm/boot/dts/exynos5440-ssdk5440.dts
+++ b/arch/arm/boot/dts/exynos5440-ssdk5440.dts
@@ -14,7 +14,7 @@
/ {
model = "SAMSUNG SSDK5440 board based on EXYNOS5440";
- compatible = "samsung,ssdk5440", "samsung,exynos5440";
+ compatible = "samsung,ssdk5440", "samsung,exynos5440", "samsung,exynos5";
chosen {
bootargs = "root=/dev/sda2 rw rootwait ignore_loglevel earlyprintk no_console_suspend mem=2048M@0x80000000 mem=6144M@0x100000000 console=ttySAC0,115200";
diff --git a/arch/arm/boot/dts/exynos5440.dtsi b/arch/arm/boot/dts/exynos5440.dtsi
index 02a0a1226cef..84f77c2fe4d4 100644
--- a/arch/arm/boot/dts/exynos5440.dtsi
+++ b/arch/arm/boot/dts/exynos5440.dtsi
@@ -9,10 +9,11 @@
* published by the Free Software Foundation.
*/
+#include <dt-bindings/clock/exynos5440.h>
#include "skeleton.dtsi"
/ {
- compatible = "samsung,exynos5440";
+ compatible = "samsung,exynos5440", "samsung,exynos5";
interrupt-parent = <&gic>;
@@ -105,7 +106,7 @@
compatible = "samsung,exynos4210-uart";
reg = <0xB0000 0x1000>;
interrupts = <0 2 0>;
- clocks = <&clock 21>, <&clock 21>;
+ clocks = <&clock CLK_B_125>, <&clock CLK_B_125>;
clock-names = "uart", "clk_uart_baud0";
};
@@ -113,7 +114,7 @@
compatible = "samsung,exynos4210-uart";
reg = <0xC0000 0x1000>;
interrupts = <0 3 0>;
- clocks = <&clock 21>, <&clock 21>;
+ clocks = <&clock CLK_B_125>, <&clock CLK_B_125>;
clock-names = "uart", "clk_uart_baud0";
};
@@ -125,7 +126,7 @@
#size-cells = <0>;
samsung,spi-src-clk = <0>;
num-cs = <1>;
- clocks = <&clock 21>, <&clock 16>;
+ clocks = <&clock CLK_B_125>, <&clock CLK_SPI_BAUD>;
clock-names = "spi", "spi_busclk0";
};
@@ -161,7 +162,7 @@
interrupts = <0 5 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 21>;
+ clocks = <&clock CLK_B_125>;
clock-names = "i2c";
};
@@ -171,7 +172,7 @@
interrupts = <0 6 0>;
#address-cells = <1>;
#size-cells = <0>;
- clocks = <&clock 21>;
+ clocks = <&clock CLK_B_125>;
clock-names = "i2c";
};
@@ -179,7 +180,7 @@
compatible = "samsung,s3c2410-wdt";
reg = <0x110000 0x1000>;
interrupts = <0 1 0>;
- clocks = <&clock 21>;
+ clocks = <&clock CLK_B_125>;
clock-names = "watchdog";
};
@@ -190,7 +191,7 @@
interrupts = <0 31 4>;
interrupt-names = "macirq";
phy-mode = "sgmii";
- clocks = <&clock 25>;
+ clocks = <&clock CLK_GMAC0>;
clock-names = "stmmaceth";
};
@@ -206,7 +207,7 @@
compatible = "samsung,s3c6410-rtc";
reg = <0x130000 0x1000>;
interrupts = <0 17 0>, <0 16 0>;
- clocks = <&clock 21>;
+ clocks = <&clock CLK_B_125>;
clock-names = "rtc";
};
@@ -214,7 +215,7 @@
compatible = "samsung,exynos5440-tmu";
reg = <0x160118 0x230>, <0x160368 0x10>;
interrupts = <0 58 0>;
- clocks = <&clock 21>;
+ clocks = <&clock CLK_B_125>;
clock-names = "tmu_apbif";
};
@@ -222,7 +223,7 @@
compatible = "samsung,exynos5440-tmu";
reg = <0x16011C 0x230>, <0x160368 0x10>;
interrupts = <0 58 0>;
- clocks = <&clock 21>;
+ clocks = <&clock CLK_B_125>;
clock-names = "tmu_apbif";
};
@@ -230,7 +231,7 @@
compatible = "samsung,exynos5440-tmu";
reg = <0x160120 0x230>, <0x160368 0x10>;
interrupts = <0 58 0>;
- clocks = <&clock 21>;
+ clocks = <&clock CLK_B_125>;
clock-names = "tmu_apbif";
};
@@ -238,7 +239,7 @@
compatible = "snps,exynos5440-ahci";
reg = <0x210000 0x10000>;
interrupts = <0 30 0>;
- clocks = <&clock 23>;
+ clocks = <&clock CLK_SATA>;
clock-names = "sata";
};
@@ -246,7 +247,7 @@
compatible = "samsung,exynos5440-ohci";
reg = <0x220000 0x1000>;
interrupts = <0 29 0>;
- clocks = <&clock 24>;
+ clocks = <&clock CLK_USB>;
clock-names = "usbhost";
};
@@ -254,7 +255,7 @@
compatible = "samsung,exynos5440-ehci";
reg = <0x221000 0x1000>;
interrupts = <0 29 0>;
- clocks = <&clock 24>;
+ clocks = <&clock CLK_USB>;
clock-names = "usbhost";
};
@@ -264,7 +265,7 @@
0x270000 0x1000
0x271000 0x40>;
interrupts = <0 20 0>, <0 21 0>, <0 22 0>;
- clocks = <&clock 28>, <&clock 27>;
+ clocks = <&clock CLK_PR0_250_O>, <&clock CLK_PB0_250_O>;
clock-names = "pcie", "pcie_bus";
#address-cells = <3>;
#size-cells = <2>;
@@ -285,7 +286,7 @@
0x272000 0x1000
0x271040 0x40>;
interrupts = <0 23 0>, <0 24 0>, <0 25 0>;
- clocks = <&clock 29>, <&clock 27>;
+ clocks = <&clock CLK_PR1_250_O>, <&clock CLK_PB0_250_O>;
clock-names = "pcie", "pcie_bus";
#address-cells = <3>;
#size-cells = <2>;
diff --git a/arch/arm/boot/dts/imx23-evk.dts b/arch/arm/boot/dts/imx23-evk.dts
index 1f026adefd45..a33f66c11b73 100644
--- a/arch/arm/boot/dts/imx23-evk.dts
+++ b/arch/arm/boot/dts/imx23-evk.dts
@@ -127,17 +127,21 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_vddio_sd0: vddio-sd0 {
+ reg_vddio_sd0: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "vddio-sd0";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio1 29 0>;
};
- reg_lcd_3v3: lcd-3v3 {
+ reg_lcd_3v3: regulator@1 {
compatible = "regulator-fixed";
+ reg = <1>;
regulator-name = "lcd-3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
diff --git a/arch/arm/boot/dts/imx23-olinuxino.dts b/arch/arm/boot/dts/imx23-olinuxino.dts
index 526bfdbd87f9..7e6eef2488e8 100644
--- a/arch/arm/boot/dts/imx23-olinuxino.dts
+++ b/arch/arm/boot/dts/imx23-olinuxino.dts
@@ -100,9 +100,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_usb0_vbus: usb0_vbus {
+ reg_usb0_vbus: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "usb0_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
diff --git a/arch/arm/boot/dts/imx23-stmp378x_devb.dts b/arch/arm/boot/dts/imx23-stmp378x_devb.dts
index cb64e2b191ea..455169e99d49 100644
--- a/arch/arm/boot/dts/imx23-stmp378x_devb.dts
+++ b/arch/arm/boot/dts/imx23-stmp378x_devb.dts
@@ -66,9 +66,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_vddio_sd0: vddio-sd0 {
+ reg_vddio_sd0: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "vddio-sd0";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
diff --git a/arch/arm/boot/dts/imx23.dtsi b/arch/arm/boot/dts/imx23.dtsi
index 581b75433be6..bbcfb5a19c77 100644
--- a/arch/arm/boot/dts/imx23.dtsi
+++ b/arch/arm/boot/dts/imx23.dtsi
@@ -23,6 +23,7 @@
serial1 = &auart1;
spi0 = &ssp0;
spi1 = &ssp1;
+ usbphy0 = &usbphy0;
};
cpus {
@@ -428,7 +429,7 @@
status = "disabled";
};
- lradc@80050000 {
+ lradc: lradc@80050000 {
compatible = "fsl,imx23-lradc";
reg = <0x80050000 0x2000>;
interrupts = <36 37 38 39 40 41 42 43 44>;
@@ -526,4 +527,9 @@
status = "disabled";
};
};
+
+ iio_hwmon {
+ compatible = "iio-hwmon";
+ io-channels = <&lradc 8>;
+ };
};
diff --git a/arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi b/arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi
new file mode 100644
index 000000000000..d6f27641c0ef
--- /dev/null
+++ b/arch/arm/boot/dts/imx25-eukrea-cpuimx25.dtsi
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2013 Eukréa Electromatique <denis@eukrea.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 "imx25.dtsi"
+
+/ {
+ model = "Eukrea CPUIMX25";
+ compatible = "eukrea,cpuimx25", "fsl,imx25";
+
+ memory {
+ reg = <0x80000000 0x4000000>; /* 64M */
+ };
+};
+
+&fec {
+ phy-mode = "rmii";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fec>;
+ status = "okay";
+};
+
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ pcf8563@51 {
+ compatible = "nxp,pcf8563";
+ reg = <0x51>;
+ };
+};
+
+&iomuxc {
+ imx25-eukrea-cpuimx25 {
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX25_PAD_FEC_MDC__FEC_MDC 0x80000000
+ MX25_PAD_FEC_MDIO__FEC_MDIO 0x400001e0
+ MX25_PAD_FEC_TDATA0__FEC_TDATA0 0x80000000
+ MX25_PAD_FEC_TDATA1__FEC_TDATA1 0x80000000
+ MX25_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000
+ MX25_PAD_FEC_RDATA0__FEC_RDATA0 0x80000000
+ MX25_PAD_FEC_RDATA1__FEC_RDATA1 0x80000000
+ MX25_PAD_FEC_RX_DV__FEC_RX_DV 0x80000000
+ MX25_PAD_FEC_TX_CLK__FEC_TX_CLK 0x1c0
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX25_PAD_I2C1_CLK__I2C1_CLK 0x80000000
+ MX25_PAD_I2C1_DAT__I2C1_DAT 0x80000000
+ >;
+ };
+ };
+};
+
+&nfc {
+ nand-bus-width = <8>;
+ nand-ecc-mode = "hw";
+ nand-on-flash-bbt;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx25-eukrea-mbimxsd25-baseboard.dts b/arch/arm/boot/dts/imx25-eukrea-mbimxsd25-baseboard.dts
new file mode 100644
index 000000000000..62fb3da50bdb
--- /dev/null
+++ b/arch/arm/boot/dts/imx25-eukrea-mbimxsd25-baseboard.dts
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2013 Eukréa Electromatique <denis@eukrea.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.
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+#include "imx25-eukrea-cpuimx25.dtsi"
+
+/ {
+ model = "Eukrea MBIMXSD25";
+ compatible = "eukrea,mbimxsd25-baseboard", "eukrea,cpuimx25", "fsl,imx25";
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpiokeys>;
+
+ bp1 {
+ label = "BP1";
+ gpios = <&gpio3 18 GPIO_ACTIVE_LOW>;
+ linux,code = <BTN_MISC>;
+ gpio-key,wakeup;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpioled>;
+
+ led1 {
+ label = "led1";
+ gpios = <&gpio3 19 GPIO_ACTIVE_LOW>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ sound {
+ compatible = "eukrea,asoc-tlv320";
+ eukrea,model = "imx25-eukrea-tlv320aic23";
+ ssi-controller = <&ssi1>;
+ fsl,mux-int-port = <1>;
+ fsl,mux-ext-port = <5>;
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>;
+ status = "okay";
+};
+
+&esdhc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_esdhc1>;
+ cd-gpios = <&gpio1 20>;
+ status = "okay";
+};
+
+&i2c1 {
+ tlv320aic23: codec@1a {
+ compatible = "ti,tlv320aic23";
+ reg = <0x1a>;
+ };
+};
+
+&iomuxc {
+ imx25-eukrea-mbimxsd25-baseboard {
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX25_PAD_KPP_COL3__AUD5_TXFS 0xe0
+ MX25_PAD_KPP_COL2__AUD5_TXC 0xe0
+ MX25_PAD_KPP_COL1__AUD5_RXD 0xe0
+ MX25_PAD_KPP_COL0__AUD5_TXD 0xe0
+ >;
+ };
+
+ pinctrl_esdhc1: esdhc1grp {
+ fsl,pins = <
+ MX25_PAD_SD1_CMD__SD1_CMD 0x400000c0
+ MX25_PAD_SD1_CLK__SD1_CLK 0x400000c0
+ MX25_PAD_SD1_DATA0__SD1_DATA0 0x400000c0
+ MX25_PAD_SD1_DATA1__SD1_DATA1 0x400000c0
+ MX25_PAD_SD1_DATA2__SD1_DATA2 0x400000c0
+ MX25_PAD_SD1_DATA3__SD1_DATA3 0x400000c0
+ >;
+ };
+
+ pinctrl_gpiokeys: gpiokeysgrp {
+ fsl,pins = <MX25_PAD_VSTBY_ACK__GPIO_3_18 0x80000000>;
+ };
+
+ pinctrl_gpioled: gpioledgrp {
+ fsl,pins = <MX25_PAD_POWER_FAIL__GPIO_3_19 0x80000000>;
+ };
+
+ pinctrl_lcdc: lcdcgrp {
+ fsl,pins = <
+ MX25_PAD_LD0__LD0 0x1
+ MX25_PAD_LD1__LD1 0x1
+ MX25_PAD_LD2__LD2 0x1
+ MX25_PAD_LD3__LD3 0x1
+ MX25_PAD_LD4__LD4 0x1
+ MX25_PAD_LD5__LD5 0x1
+ MX25_PAD_LD6__LD6 0x1
+ MX25_PAD_LD7__LD7 0x1
+ MX25_PAD_LD8__LD8 0x1
+ MX25_PAD_LD9__LD9 0x1
+ MX25_PAD_LD10__LD10 0x1
+ MX25_PAD_LD11__LD11 0x1
+ MX25_PAD_LD12__LD12 0x1
+ MX25_PAD_LD13__LD13 0x1
+ MX25_PAD_LD14__LD14 0x1
+ MX25_PAD_LD15__LD15 0x1
+ MX25_PAD_GPIO_E__LD16 0x1
+ MX25_PAD_GPIO_F__LD17 0x1
+ MX25_PAD_HSYNC__HSYNC 0x80000000
+ MX25_PAD_VSYNC__VSYNC 0x80000000
+ MX25_PAD_LSCLK__LSCLK 0x80000000
+ MX25_PAD_OE_ACD__OE_ACD 0x80000000
+ MX25_PAD_CONTRAST__CONTRAST 0x80000000
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX25_PAD_UART1_RTS__UART1_RTS 0xe0
+ MX25_PAD_UART1_CTS__UART1_CTS 0xe0
+ MX25_PAD_UART1_TXD__UART1_TXD 0x80000000
+ MX25_PAD_UART1_RXD__UART1_RXD 0xc0
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX25_PAD_UART2_RXD__UART2_RXD 0x80000000
+ MX25_PAD_UART2_TXD__UART2_TXD 0x80000000
+ MX25_PAD_UART2_RTS__UART2_RTS 0x80000000
+ MX25_PAD_UART2_CTS__UART2_CTS 0x80000000
+ >;
+ };
+ };
+};
+
+&ssi1 {
+ codec-handle = <&tlv320aic23>;
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ fsl,uart-has-rtscts;
+ status = "okay";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ fsl,uart-has-rtscts;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx25-pinfunc.h b/arch/arm/boot/dts/imx25-pinfunc.h
new file mode 100644
index 000000000000..9238a95d8e62
--- /dev/null
+++ b/arch/arm/boot/dts/imx25-pinfunc.h
@@ -0,0 +1,494 @@
+/*
+ * Copyright 2013 Eukréa Electromatique <denis@eukrea.com>
+ * Based on imx35-pinfunc.h in the same directory Which is:
+ * Copyright 2013 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __DTS_IMX25_PINFUNC_H
+#define __DTS_IMX25_PINFUNC_H
+
+/*
+ * The pin function ID is a tuple of
+ * <mux_reg conf_reg input_reg mux_mode input_val>
+ */
+
+#define MX25_PAD_A10__A10 0x008 0x000 0x000 0x00 0x000
+#define MX25_PAD_A10__GPIO_4_0 0x008 0x000 0x000 0x05 0x000
+
+#define MX25_PAD_A13__A13 0x00c 0x22C 0x000 0x00 0x000
+#define MX25_PAD_A13__GPIO_4_1 0x00c 0x22C 0x000 0x05 0x000
+
+#define MX25_PAD_A14__A14 0x010 0x230 0x000 0x10 0x000
+#define MX25_PAD_A14__GPIO_2_0 0x010 0x230 0x000 0x15 0x000
+
+#define MX25_PAD_A15__A15 0x014 0x234 0x000 0x10 0x000
+#define MX25_PAD_A15__GPIO_2_1 0x014 0x234 0x000 0x15 0x000
+
+#define MX25_PAD_A16__A16 0x018 0x000 0x000 0x10 0x000
+#define MX25_PAD_A16__GPIO_2_2 0x018 0x000 0x000 0x15 0x000
+
+#define MX25_PAD_A17__A17 0x01c 0x238 0x000 0x10 0x000
+#define MX25_PAD_A17__GPIO_2_3 0x01c 0x238 0x000 0x15 0x000
+
+#define MX25_PAD_A18__A18 0x020 0x23c 0x000 0x10 0x000
+#define MX25_PAD_A18__GPIO_2_4 0x020 0x23c 0x000 0x15 0x000
+#define MX25_PAD_A18__FEC_COL 0x020 0x23c 0x504 0x17 0x000
+
+#define MX25_PAD_A19__A19 0x024 0x240 0x000 0x10 0x000
+#define MX25_PAD_A19__FEC_RX_ER 0x024 0x240 0x518 0x17 0x000
+#define MX25_PAD_A19__GPIO_2_5 0x024 0x240 0x000 0x15 0x000
+
+#define MX25_PAD_A20__A20 0x028 0x244 0x000 0x10 0x000
+#define MX25_PAD_A20__GPIO_2_6 0x028 0x244 0x000 0x15 0x000
+#define MX25_PAD_A20__FEC_RDATA2 0x028 0x244 0x50c 0x17 0x000
+
+#define MX25_PAD_A21__A21 0x02c 0x248 0x000 0x10 0x000
+#define MX25_PAD_A21__GPIO_2_7 0x02c 0x248 0x000 0x15 0x000
+#define MX25_PAD_A21__FEC_RDATA3 0x02c 0x248 0x510 0x17 0x000
+
+#define MX25_PAD_A22__A22 0x030 0x000 0x000 0x10 0x000
+#define MX25_PAD_A22__GPIO_2_8 0x030 0x000 0x000 0x15 0x000
+
+#define MX25_PAD_A23__A23 0x034 0x24c 0x000 0x10 0x000
+#define MX25_PAD_A23__GPIO_2_9 0x034 0x24c 0x000 0x15 0x000
+
+#define MX25_PAD_A24__A24 0x038 0x250 0x000 0x10 0x000
+#define MX25_PAD_A24__GPIO_2_10 0x038 0x250 0x000 0x15 0x000
+#define MX25_PAD_A24__FEC_RX_CLK 0x038 0x250 0x514 0x17 0x000
+
+#define MX25_PAD_A25__A25 0x03c 0x254 0x000 0x10 0x000
+#define MX25_PAD_A25__GPIO_2_11 0x03c 0x254 0x000 0x15 0x000
+#define MX25_PAD_A25__FEC_CRS 0x03c 0x254 0x508 0x17 0x000
+
+#define MX25_PAD_EB0__EB0 0x040 0x258 0x000 0x10 0x000
+#define MX25_PAD_EB0__AUD4_TXD 0x040 0x258 0x464 0x14 0x000
+#define MX25_PAD_EB0__GPIO_2_12 0x040 0x258 0x000 0x15 0x000
+
+#define MX25_PAD_EB1__EB1 0x044 0x25c 0x000 0x10 0x000
+#define MX25_PAD_EB1__AUD4_RXD 0x044 0x25c 0x460 0x14 0x000
+#define MX25_PAD_EB1__GPIO_2_13 0x044 0x25c 0x000 0x15 0x000
+
+#define MX25_PAD_OE__OE 0x048 0x260 0x000 0x10 0x000
+#define MX25_PAD_OE__AUD4_TXC 0x048 0x260 0x000 0x14 0x000
+#define MX25_PAD_OE__GPIO_2_14 0x048 0x260 0x000 0x15 0x000
+
+#define MX25_PAD_CS0__CS0 0x04c 0x000 0x000 0x00 0x000
+#define MX25_PAD_CS0__GPIO_4_2 0x04c 0x000 0x000 0x05 0x000
+
+#define MX25_PAD_CS1__CS1 0x050 0x000 0x000 0x00 0x000
+#define MX25_PAD_CS1__NF_CE3 0x050 0x000 0x000 0x01 0x000
+#define MX25_PAD_CS1__GPIO_4_3 0x050 0x000 0x000 0x05 0x000
+
+#define MX25_PAD_CS4__CS4 0x054 0x264 0x000 0x10 0x000
+#define MX25_PAD_CS4__NF_CE1 0x054 0x264 0x000 0x01 0x000
+#define MX25_PAD_CS4__UART5_CTS 0x054 0x264 0x000 0x13 0x000
+#define MX25_PAD_CS4__GPIO_3_20 0x054 0x264 0x000 0x15 0x000
+
+#define MX25_PAD_CS5__CS5 0x058 0x268 0x000 0x10 0x000
+#define MX25_PAD_CS5__NF_CE2 0x058 0x268 0x000 0x01 0x000
+#define MX25_PAD_CS5__UART5_RTS 0x058 0x268 0x574 0x13 0x000
+#define MX25_PAD_CS5__GPIO_3_21 0x058 0x268 0x000 0x15 0x000
+
+#define MX25_PAD_NF_CE0__NF_CE0 0x05c 0x26c 0x000 0x10 0x000
+#define MX25_PAD_NF_CE0__GPIO_3_22 0x05c 0x26c 0x000 0x15 0x000
+
+#define MX25_PAD_ECB__ECB 0x060 0x270 0x000 0x10 0x000
+#define MX25_PAD_ECB__UART5_TXD_MUX 0x060 0x270 0x000 0x13 0x000
+#define MX25_PAD_ECB__GPIO_3_23 0x060 0x270 0x000 0x15 0x000
+
+#define MX25_PAD_LBA__LBA 0x064 0x274 0x000 0x10 0x000
+#define MX25_PAD_LBA__UART5_RXD_MUX 0x064 0x274 0x578 0x13 0x000
+#define MX25_PAD_LBA__GPIO_3_24 0x064 0x274 0x000 0x15 0x000
+
+#define MX25_PAD_BCLK__BCLK 0x068 0x000 0x000 0x00 0x000
+#define MX25_PAD_BCLK__GPIO_4_4 0x068 0x000 0x000 0x05 0x000
+
+#define MX25_PAD_RW__RW 0x06c 0x278 0x000 0x10 0x000
+#define MX25_PAD_RW__AUD4_TXFS 0x06c 0x278 0x474 0x14 0x000
+#define MX25_PAD_RW__GPIO_3_25 0x06c 0x278 0x000 0x15 0x000
+
+#define MX25_PAD_NFWE_B__NFWE_B 0x070 0x000 0x000 0x10 0x000
+#define MX25_PAD_NFWE_B__GPIO_3_26 0x070 0x000 0x000 0x15 0x000
+
+#define MX25_PAD_NFRE_B__NFRE_B 0x074 0x000 0x000 0x10 0x000
+#define MX25_PAD_NFRE_B__GPIO_3_27 0x074 0x000 0x000 0x15 0x000
+
+#define MX25_PAD_NFALE__NFALE 0x078 0x000 0x000 0x10 0x000
+#define MX25_PAD_NFALE__GPIO_3_28 0x078 0x000 0x000 0x15 0x000
+
+#define MX25_PAD_NFCLE__NFCLE 0x07c 0x000 0x000 0x10 0x000
+#define MX25_PAD_NFCLE__GPIO_3_29 0x07c 0x000 0x000 0x15 0x000
+
+#define MX25_PAD_NFWP_B__NFWP_B 0x080 0x000 0x000 0x10 0x000
+#define MX25_PAD_NFWP_B__GPIO_3_30 0x080 0x000 0x000 0x15 0x000
+
+#define MX25_PAD_NFRB__NFRB 0x084 0x27c 0x000 0x10 0x000
+#define MX25_PAD_NFRB__GPIO_3_31 0x084 0x27c 0x000 0x15 0x000
+
+#define MX25_PAD_D15__D15 0x088 0x280 0x000 0x00 0x000
+#define MX25_PAD_D15__LD16 0x088 0x280 0x000 0x01 0x000
+#define MX25_PAD_D15__GPIO_4_5 0x088 0x280 0x000 0x05 0x000
+
+#define MX25_PAD_D14__D14 0x08c 0x284 0x000 0x00 0x000
+#define MX25_PAD_D14__LD17 0x08c 0x284 0x000 0x01 0x000
+#define MX25_PAD_D14__GPIO_4_6 0x08c 0x284 0x000 0x05 0x000
+
+#define MX25_PAD_D13__D13 0x090 0x288 0x000 0x00 0x000
+#define MX25_PAD_D13__LD18 0x090 0x288 0x000 0x01 0x000
+#define MX25_PAD_D13__GPIO_4_7 0x090 0x288 0x000 0x05 0x000
+
+#define MX25_PAD_D12__D12 0x094 0x28c 0x000 0x00 0x000
+#define MX25_PAD_D12__GPIO_4_8 0x094 0x28c 0x000 0x05 0x000
+
+#define MX25_PAD_D11__D11 0x098 0x290 0x000 0x00 0x000
+#define MX25_PAD_D11__GPIO_4_9 0x098 0x290 0x000 0x05 0x000
+
+#define MX25_PAD_D10__D10 0x09c 0x294 0x000 0x00 0x000
+#define MX25_PAD_D10__GPIO_4_10 0x09c 0x294 0x000 0x05 0x000
+#define MX25_PAD_D10__USBOTG_OC 0x09c 0x294 0x57c 0x06 0x000
+
+#define MX25_PAD_D9__D9 0x0a0 0x298 0x000 0x00 0x000
+#define MX25_PAD_D9__GPIO_4_11 0x0a0 0x298 0x000 0x05 0x000
+#define MX25_PAD_D9__USBH2_PWR 0x0a0 0x298 0x000 0x06 0x000
+
+#define MX25_PAD_D8__D8 0x0a4 0x29c 0x000 0x00 0x000
+#define MX25_PAD_D8__GPIO_4_12 0x0a4 0x29c 0x000 0x05 0x000
+#define MX25_PAD_D8__USBH2_OC 0x0a4 0x29c 0x580 0x06 0x000
+
+#define MX25_PAD_D7__D7 0x0a8 0x2a0 0x000 0x00 0x000
+#define MX25_PAD_D7__GPIO_4_13 0x0a8 0x2a0 0x000 0x05 0x000
+
+#define MX25_PAD_D6__D6 0x0ac 0x2a4 0x000 0x00 0x000
+#define MX25_PAD_D6__GPIO_4_14 0x0ac 0x2a4 0x000 0x05 0x000
+
+#define MX25_PAD_D5__D5 0x0b0 0x2a8 0x000 0x00 0x000
+#define MX25_PAD_D5__GPIO_4_15 0x0b0 0x2a8 0x000 0x05 0x000
+
+#define MX25_PAD_D4__D4 0x0b4 0x2ac 0x000 0x00 0x000
+#define MX25_PAD_D4__GPIO_4_16 0x0b4 0x2ac 0x000 0x05 0x000
+
+#define MX25_PAD_D3__D3 0x0b8 0x2b0 0x000 0x00 0x000
+#define MX25_PAD_D3__GPIO_4_17 0x0b8 0x2b0 0x000 0x05 0x000
+
+#define MX25_PAD_D2__D2 0x0bc 0x2b4 0x000 0x00 0x000
+#define MX25_PAD_D2__GPIO_4_18 0x0bc 0x2b4 0x000 0x05 0x000
+
+#define MX25_PAD_D1__D1 0x0c0 0x2b8 0x000 0x00 0x000
+#define MX25_PAD_D1__GPIO_4_19 0x0c0 0x2b8 0x000 0x05 0x000
+
+#define MX25_PAD_D0__D0 0x0c4 0x2bc 0x000 0x00 0x000
+#define MX25_PAD_D0__GPIO_4_20 0x0c4 0x2bc 0x000 0x05 0x000
+
+#define MX25_PAD_LD0__LD0 0x0c8 0x2c0 0x000 0x10 0x000
+#define MX25_PAD_LD0__CSI_D0 0x0c8 0x2c0 0x488 0x12 0x000
+#define MX25_PAD_LD0__GPIO_2_15 0x0c8 0x2c0 0x000 0x15 0x000
+
+#define MX25_PAD_LD1__LD1 0x0cc 0x2c4 0x000 0x10 0x000
+#define MX25_PAD_LD1__CSI_D1 0x0cc 0x2c4 0x48c 0x12 0x000
+#define MX25_PAD_LD1__GPIO_2_16 0x0cc 0x2c4 0x000 0x15 0x000
+
+#define MX25_PAD_LD2__LD2 0x0d0 0x2c8 0x000 0x10 0x000
+#define MX25_PAD_LD2__GPIO_2_17 0x0d0 0x2c8 0x000 0x15 0x000
+
+#define MX25_PAD_LD3__LD3 0x0d4 0x2cc 0x000 0x10 0x000
+#define MX25_PAD_LD3__GPIO_2_18 0x0d4 0x2cc 0x000 0x15 0x000
+
+#define MX25_PAD_LD4__LD4 0x0d8 0x2d0 0x000 0x10 0x000
+#define MX25_PAD_LD4__GPIO_2_19 0x0d8 0x2d0 0x000 0x15 0x000
+
+#define MX25_PAD_LD5__LD5 0x0dc 0x2d4 0x000 0x10 0x000
+#define MX25_PAD_LD5__GPIO_1_19 0x0dc 0x2d4 0x000 0x15 0x000
+
+#define MX25_PAD_LD6__LD6 0x0e0 0x2d8 0x000 0x10 0x000
+#define MX25_PAD_LD6__GPIO_1_20 0x0e0 0x2d8 0x000 0x15 0x000
+
+#define MX25_PAD_LD7__LD7 0x0e4 0x2dc 0x000 0x10 0x000
+#define MX25_PAD_LD7__GPIO_1_21 0x0e4 0x2dc 0x000 0x15 0x000
+
+#define MX25_PAD_LD8__LD8 0x0e8 0x2e0 0x000 0x10 0x000
+#define MX25_PAD_LD8__FEC_TX_ERR 0x0e8 0x2e0 0x000 0x15 0x000
+
+#define MX25_PAD_LD9__LD9 0x0ec 0x2e4 0x000 0x10 0x000
+#define MX25_PAD_LD9__FEC_COL 0x0ec 0x2e4 0x504 0x15 0x001
+
+#define MX25_PAD_LD10__LD10 0x0f0 0x2e8 0x000 0x10 0x000
+#define MX25_PAD_LD10__FEC_RX_ER 0x0f0 0x2e8 0x518 0x15 0x001
+
+#define MX25_PAD_LD11__LD11 0x0f4 0x2ec 0x000 0x10 0x000
+#define MX25_PAD_LD11__FEC_RDATA2 0x0f4 0x2ec 0x50c 0x15 0x001
+
+#define MX25_PAD_LD12__LD12 0x0f8 0x2f0 0x000 0x10 0x000
+#define MX25_PAD_LD12__FEC_RDATA3 0x0f8 0x2f0 0x510 0x15 0x001
+
+#define MX25_PAD_LD13__LD13 0x0fc 0x2f4 0x000 0x10 0x000
+#define MX25_PAD_LD13__FEC_TDATA2 0x0fc 0x2f4 0x000 0x15 0x000
+
+#define MX25_PAD_LD14__LD14 0x100 0x2f8 0x000 0x10 0x000
+#define MX25_PAD_LD14__FEC_TDATA3 0x100 0x2f8 0x000 0x15 0x000
+
+#define MX25_PAD_LD15__LD15 0x104 0x2fc 0x000 0x10 0x000
+#define MX25_PAD_LD15__FEC_RX_CLK 0x104 0x2fc 0x514 0x15 0x001
+
+#define MX25_PAD_HSYNC__HSYNC 0x108 0x300 0x000 0x10 0x000
+#define MX25_PAD_HSYNC__GPIO_1_22 0x108 0x300 0x000 0x15 0x000
+
+#define MX25_PAD_VSYNC__VSYNC 0x10c 0x304 0x000 0x10 0x000
+#define MX25_PAD_VSYNC__GPIO_1_23 0x10c 0x304 0x000 0x15 0x000
+
+#define MX25_PAD_LSCLK__LSCLK 0x110 0x308 0x000 0x10 0x000
+#define MX25_PAD_LSCLK__GPIO_1_24 0x110 0x308 0x000 0x15 0x000
+
+#define MX25_PAD_OE_ACD__OE_ACD 0x114 0x30c 0x000 0x10 0x000
+#define MX25_PAD_OE_ACD__GPIO_1_25 0x114 0x30c 0x000 0x15 0x000
+
+#define MX25_PAD_CONTRAST__CONTRAST 0x118 0x310 0x000 0x10 0x000
+#define MX25_PAD_CONTRAST__PWM4_PWMO 0x118 0x310 0x000 0x14 0x000
+#define MX25_PAD_CONTRAST__FEC_CRS 0x118 0x310 0x508 0x15 0x001
+
+#define MX25_PAD_PWM__PWM 0x11c 0x314 0x000 0x10 0x000
+#define MX25_PAD_PWM__GPIO_1_26 0x11c 0x314 0x000 0x15 0x000
+#define MX25_PAD_PWM__USBH2_OC 0x11c 0x314 0x580 0x16 0x001
+
+#define MX25_PAD_CSI_D2__CSI_D2 0x120 0x318 0x000 0x10 0x000
+#define MX25_PAD_CSI_D2__UART5_RXD_MUX 0x120 0x318 0x578 0x11 0x001
+#define MX25_PAD_CSI_D2__GPIO_1_27 0x120 0x318 0x000 0x15 0x000
+#define MX25_PAD_CSI_D2__CSPI3_MOSI 0x120 0x318 0x000 0x17 0x000
+
+#define MX25_PAD_CSI_D3__CSI_D3 0x124 0x31c 0x000 0x10 0x000
+#define MX25_PAD_CSI_D3__GPIO_1_28 0x124 0x31c 0x000 0x15 0x000
+#define MX25_PAD_CSI_D3__CSPI3_MISO 0x124 0x31c 0x4b4 0x17 0x001
+
+#define MX25_PAD_CSI_D4__CSI_D4 0x128 0x320 0x000 0x10 0x000
+#define MX25_PAD_CSI_D4__UART5_RTS 0x128 0x320 0x574 0x11 0x001
+#define MX25_PAD_CSI_D4__GPIO_1_29 0x128 0x320 0x000 0x15 0x000
+#define MX25_PAD_CSI_D4__CSPI3_SCLK 0x128 0x320 0x000 0x17 0x000
+
+#define MX25_PAD_CSI_D5__CSI_D5 0x12c 0x324 0x000 0x10 0x000
+#define MX25_PAD_CSI_D5__GPIO_1_30 0x12c 0x324 0x000 0x15 0x000
+#define MX25_PAD_CSI_D5__CSPI3_RDY 0x12c 0x324 0x000 0x17 0x000
+
+#define MX25_PAD_CSI_D6__CSI_D6 0x130 0x328 0x000 0x10 0x000
+#define MX25_PAD_CSI_D6__GPIO_1_31 0x130 0x328 0x000 0x15 0x000
+
+#define MX25_PAD_CSI_D7__CSI_D7 0x134 0x32c 0x000 0x10 0x000
+#define MX25_PAD_CSI_D7__GPIO_1_6 0x134 0x32c 0x000 0x15 0x000
+
+#define MX25_PAD_CSI_D8__CSI_D8 0x138 0x330 0x000 0x10 0x000
+#define MX25_PAD_CSI_D8__GPIO_1_7 0x138 0x330 0x000 0x15 0x000
+
+#define MX25_PAD_CSI_D9__CSI_D9 0x13c 0x334 0x000 0x10 0x000
+#define MX25_PAD_CSI_D9__GPIO_4_21 0x13c 0x334 0x000 0x15 0x000
+
+#define MX25_PAD_CSI_MCLK__CSI_MCLK 0x140 0x338 0x000 0x10 0x000
+#define MX25_PAD_CSI_MCLK__GPIO_1_8 0x140 0x338 0x000 0x15 0x000
+
+#define MX25_PAD_CSI_VSYNC__CSI_VSYNC 0x144 0x33c 0x000 0x10 0x000
+#define MX25_PAD_CSI_VSYNC__GPIO_1_9 0x144 0x33c 0x000 0x15 0x000
+
+#define MX25_PAD_CSI_HSYNC__CSI_HSYNC 0x148 0x340 0x000 0x10 0x000
+#define MX25_PAD_CSI_HSYNC__GPIO_1_10 0x148 0x340 0x000 0x15 0x000
+
+#define MX25_PAD_CSI_PIXCLK__CSI_PIXCLK 0x14c 0x344 0x000 0x10 0x000
+#define MX25_PAD_CSI_PIXCLK__GPIO_1_11 0x14c 0x344 0x000 0x15 0x000
+
+#define MX25_PAD_I2C1_CLK__I2C1_CLK 0x150 0x348 0x000 0x10 0x000
+#define MX25_PAD_I2C1_CLK__GPIO_1_12 0x150 0x348 0x000 0x15 0x000
+
+#define MX25_PAD_I2C1_DAT__I2C1_DAT 0x154 0x34c 0x000 0x10 0x000
+#define MX25_PAD_I2C1_DAT__GPIO_1_13 0x154 0x34c 0x000 0x15 0x000
+
+#define MX25_PAD_CSPI1_MOSI__CSPI1_MOSI 0x158 0x350 0x000 0x10 0x000
+#define MX25_PAD_CSPI1_MOSI__GPIO_1_14 0x158 0x350 0x000 0x15 0x000
+
+#define MX25_PAD_CSPI1_MISO__CSPI1_MISO 0x15c 0x354 0x000 0x10 0x000
+#define MX25_PAD_CSPI1_MISO__GPIO_1_15 0x15c 0x354 0x000 0x15 0x000
+
+#define MX25_PAD_CSPI1_SS0__CSPI1_SS0 0x160 0x358 0x000 0x10 0x000
+#define MX25_PAD_CSPI1_SS0__GPIO_1_16 0x160 0x358 0x000 0x15 0x000
+
+#define MX25_PAD_CSPI1_SS1__CSPI1_SS1 0x164 0x35c 0x000 0x10 0x000
+#define MX25_PAD_CSPI1_SS1__GPIO_1_17 0x164 0x35c 0x000 0x15 0x000
+
+#define MX25_PAD_CSPI1_SCLK__CSPI1_SCLK 0x168 0x360 0x000 0x10 0x000
+#define MX25_PAD_CSPI1_SCLK__GPIO_1_18 0x168 0x360 0x000 0x15 0x000
+
+#define MX25_PAD_CSPI1_RDY__CSPI1_RDY 0x16c 0x364 0x000 0x10 0x000
+#define MX25_PAD_CSPI1_RDY__GPIO_2_22 0x16c 0x364 0x000 0x15 0x000
+
+#define MX25_PAD_UART1_RXD__UART1_RXD 0x170 0x368 0x000 0x10 0x000
+#define MX25_PAD_UART1_RXD__GPIO_4_22 0x170 0x368 0x000 0x15 0x000
+
+#define MX25_PAD_UART1_TXD__UART1_TXD 0x174 0x36c 0x000 0x10 0x000
+#define MX25_PAD_UART1_TXD__GPIO_4_23 0x174 0x36c 0x000 0x15 0x000
+
+#define MX25_PAD_UART1_RTS__UART1_RTS 0x178 0x370 0x000 0x10 0x000
+#define MX25_PAD_UART1_RTS__CSI_D0 0x178 0x370 0x488 0x11 0x001
+#define MX25_PAD_UART1_RTS__GPIO_4_24 0x178 0x370 0x000 0x15 0x000
+
+#define MX25_PAD_UART1_CTS__UART1_CTS 0x17c 0x374 0x000 0x10 0x000
+#define MX25_PAD_UART1_CTS__CSI_D1 0x17c 0x374 0x48c 0x11 0x001
+#define MX25_PAD_UART1_CTS__GPIO_4_25 0x17c 0x374 0x000 0x15 0x000
+
+#define MX25_PAD_UART2_RXD__UART2_RXD 0x180 0x378 0x000 0x10 0x000
+#define MX25_PAD_UART2_RXD__GPIO_4_26 0x180 0x378 0x000 0x15 0x000
+
+#define MX25_PAD_UART2_TXD__UART2_TXD 0x184 0x37c 0x000 0x10 0x000
+#define MX25_PAD_UART2_TXD__GPIO_4_27 0x184 0x37c 0x000 0x15 0x000
+
+#define MX25_PAD_UART2_RTS__UART2_RTS 0x188 0x380 0x000 0x10 0x000
+#define MX25_PAD_UART2_RTS__FEC_COL 0x188 0x380 0x504 0x12 0x002
+#define MX25_PAD_UART2_RTS__GPIO_4_28 0x188 0x380 0x000 0x15 0x000
+
+#define MX25_PAD_UART2_CTS__FEC_RX_ER 0x18c 0x384 0x518 0x12 0x002
+#define MX25_PAD_UART2_CTS__UART2_CTS 0x18c 0x384 0x000 0x10 0x000
+#define MX25_PAD_UART2_CTS__GPIO_4_29 0x18c 0x384 0x000 0x15 0x000
+
+#define MX25_PAD_SD1_CMD__SD1_CMD 0x190 0x388 0x000 0x10 0x000
+#define MX25_PAD_SD1_CMD__FEC_RDATA2 0x190 0x388 0x50c 0x12 0x002
+#define MX25_PAD_SD1_CMD__GPIO_2_23 0x190 0x388 0x000 0x15 0x000
+
+#define MX25_PAD_SD1_CLK__SD1_CLK 0x194 0x38c 0x000 0x10 0x000
+#define MX25_PAD_SD1_CLK__FEC_RDATA3 0x194 0x38c 0x510 0x12 0x002
+#define MX25_PAD_SD1_CLK__GPIO_2_24 0x194 0x38c 0x000 0x15 0x000
+
+#define MX25_PAD_SD1_DATA0__SD1_DATA0 0x198 0x390 0x000 0x10 0x000
+#define MX25_PAD_SD1_DATA0__GPIO_2_25 0x198 0x390 0x000 0x15 0x000
+
+#define MX25_PAD_SD1_DATA1__SD1_DATA1 0x19c 0x394 0x000 0x10 0x000
+#define MX25_PAD_SD1_DATA1__AUD7_RXD 0x19c 0x394 0x478 0x13 0x000
+#define MX25_PAD_SD1_DATA1__GPIO_2_26 0x19c 0x394 0x000 0x15 0x000
+
+#define MX25_PAD_SD1_DATA2__SD1_DATA2 0x1a0 0x398 0x000 0x10 0x000
+#define MX25_PAD_SD1_DATA2__FEC_RX_CLK 0x1a0 0x398 0x514 0x15 0x002
+#define MX25_PAD_SD1_DATA2__GPIO_2_27 0x1a0 0x398 0x000 0x15 0x000
+
+#define MX25_PAD_SD1_DATA3__SD1_DATA3 0x1a4 0x39c 0x000 0x10 0x000
+#define MX25_PAD_SD1_DATA3__FEC_CRS 0x1a4 0x39c 0x508 0x10 0x002
+#define MX25_PAD_SD1_DATA3__GPIO_2_28 0x1a4 0x39c 0x000 0x15 0x000
+
+#define MX25_PAD_KPP_ROW0__KPP_ROW0 0x1a8 0x3a0 0x000 0x10 0x000
+#define MX25_PAD_KPP_ROW0__GPIO_2_29 0x1a8 0x3a0 0x000 0x15 0x000
+
+#define MX25_PAD_KPP_ROW1__KPP_ROW1 0x1ac 0x3a4 0x000 0x10 0x000
+#define MX25_PAD_KPP_ROW1__GPIO_2_30 0x1ac 0x3a4 0x000 0x15 0x000
+
+#define MX25_PAD_KPP_ROW2__KPP_ROW2 0x1b0 0x3a8 0x000 0x10 0x000
+#define MX25_PAD_KPP_ROW2__CSI_D0 0x1b0 0x3a8 0x488 0x13 0x002
+#define MX25_PAD_KPP_ROW2__GPIO_2_31 0x1b0 0x3a8 0x000 0x15 0x000
+
+#define MX25_PAD_KPP_ROW3__KPP_ROW3 0x1b4 0x3ac 0x000 0x10 0x000
+#define MX25_PAD_KPP_ROW3__CSI_LD1 0x1b4 0x3ac 0x48c 0x13 0x002
+#define MX25_PAD_KPP_ROW3__GPIO_3_0 0x1b4 0x3ac 0x000 0x15 0x000
+
+#define MX25_PAD_KPP_COL0__KPP_COL0 0x1b8 0x3b0 0x000 0x10 0x000
+#define MX25_PAD_KPP_COL0__UART4_RXD_MUX 0x1b8 0x3b0 0x570 0x11 0x001
+#define MX25_PAD_KPP_COL0__AUD5_TXD 0x1b8 0x3b0 0x000 0x12 0x000
+#define MX25_PAD_KPP_COL0__GPIO_3_1 0x1b8 0x3b0 0x000 0x15 0x000
+
+#define MX25_PAD_KPP_COL1__KPP_COL1 0x1bc 0x3b4 0x000 0x10 0x000
+#define MX25_PAD_KPP_COL1__UART4_TXD_MUX 0x1bc 0x3b4 0x000 0x11 0x000
+#define MX25_PAD_KPP_COL1__AUD5_RXD 0x1bc 0x3b4 0x000 0x12 0x000
+#define MX25_PAD_KPP_COL1__GPIO_3_2 0x1bc 0x3b4 0x000 0x15 0x000
+
+#define MX25_PAD_KPP_COL2__KPP_COL2 0x1c0 0x3b8 0x000 0x10 0x000
+#define MX25_PAD_KPP_COL2__UART4_RTS 0x1c0 0x3b8 0x000 0x11 0x000
+#define MX25_PAD_KPP_COL2__AUD5_TXC 0x1c0 0x3b8 0x000 0x12 0x000
+#define MX25_PAD_KPP_COL2__GPIO_3_3 0x1c0 0x3b8 0x000 0x15 0x000
+
+#define MX25_PAD_KPP_COL3__KPP_COL3 0x1c4 0x3bc 0x000 0x10 0x000
+#define MX25_PAD_KPP_COL3__UART4_CTS 0x1c4 0x3bc 0x000 0x11 0x000
+#define MX25_PAD_KPP_COL3__AUD5_TXFS 0x1c4 0x3bc 0x000 0x12 0x000
+#define MX25_PAD_KPP_COL3__GPIO_3_4 0x1c4 0x3bc 0x000 0x15 0x000
+
+#define MX25_PAD_FEC_MDC__FEC_MDC 0x1c8 0x3c0 0x000 0x10 0x000
+#define MX25_PAD_FEC_MDC__AUD4_TXD 0x1c8 0x3c0 0x464 0x12 0x001
+#define MX25_PAD_FEC_MDC__GPIO_3_5 0x1c8 0x3c0 0x000 0x15 0x000
+
+#define MX25_PAD_FEC_MDIO__FEC_MDIO 0x1cc 0x3c4 0x000 0x10 0x000
+#define MX25_PAD_FEC_MDIO__AUD4_RXD 0x1cc 0x3c4 0x460 0x12 0x001
+#define MX25_PAD_FEC_MDIO__GPIO_3_6 0x1cc 0x3c4 0x000 0x15 0x000
+
+#define MX25_PAD_FEC_TDATA0__FEC_TDATA0 0x1d0 0x3c8 0x000 0x10 0x000
+#define MX25_PAD_FEC_TDATA0__GPIO_3_7 0x1d0 0x3c8 0x000 0x15 0x000
+
+#define MX25_PAD_FEC_TDATA1__FEC_TDATA1 0x1d4 0x3cc 0x000 0x10 0x000
+#define MX25_PAD_FEC_TDATA1__AUD4_TXFS 0x1d4 0x3cc 0x474 0x12 0x001
+#define MX25_PAD_FEC_TDATA1__GPIO_3_8 0x1d4 0x3cc 0x000 0x15 0x000
+
+#define MX25_PAD_FEC_TX_EN__FEC_TX_EN 0x1d8 0x3d0 0x000 0x10 0x000
+#define MX25_PAD_FEC_TX_EN__GPIO_3_9 0x1d8 0x3d0 0x000 0x15 0x000
+
+#define MX25_PAD_FEC_RDATA0__FEC_RDATA0 0x1dc 0x3d4 0x000 0x10 0x000
+#define MX25_PAD_FEC_RDATA0__GPIO_3_10 0x1dc 0x3d4 0x000 0x15 0x000
+
+#define MX25_PAD_FEC_RDATA1__FEC_RDATA1 0x1e0 0x3d8 0x000 0x10 0x000
+#define MX25_PAD_FEC_RDATA1__GPIO_3_11 0x1e0 0x3d8 0x000 0x15 0x000
+
+#define MX25_PAD_FEC_RX_DV__FEC_RX_DV 0x1e4 0x3dc 0x000 0x10 0x000
+#define MX25_PAD_FEC_RX_DV__CAN2_RX 0x1e4 0x3dc 0x484 0x14 0x000
+#define MX25_PAD_FEC_RX_DV__GPIO_3_12 0x1e4 0x3dc 0x000 0x15 0x000
+
+#define MX25_PAD_FEC_TX_CLK__FEC_TX_CLK 0x1e8 0x3e0 0x000 0x10 0x000
+#define MX25_PAD_FEC_TX_CLK__GPIO_3_13 0x1e8 0x3e0 0x000 0x15 0x000
+
+#define MX25_PAD_RTCK__RTCK 0x1ec 0x3e4 0x000 0x10 0x000
+#define MX25_PAD_RTCK__OWIRE 0x1ec 0x3e4 0x000 0x11 0x000
+#define MX25_PAD_RTCK__GPIO_3_14 0x1ec 0x3e4 0x000 0x15 0x000
+
+#define MX25_PAD_DE_B__DE_B 0x1f0 0x3ec 0x000 0x10 0x000
+#define MX25_PAD_DE_B__GPIO_2_20 0x1f0 0x3ec 0x000 0x15 0x000
+
+#define MX25_PAD_TDO__TDO 0x000 0x3e8 0x000 0x00 0x000
+
+#define MX25_PAD_GPIO_A__GPIO_A 0x1f4 0x3f0 0x000 0x10 0x000
+#define MX25_PAD_GPIO_A__CAN1_TX 0x1f4 0x3f0 0x000 0x16 0x000
+#define MX25_PAD_GPIO_A__USBOTG_PWR 0x1f4 0x3f0 0x000 0x12 0x000
+
+#define MX25_PAD_GPIO_B__GPIO_B 0x1f8 0x3f4 0x000 0x10 0x000
+#define MX25_PAD_GPIO_B__CAN1_RX 0x1f8 0x3f4 0x480 0x16 0x001
+#define MX25_PAD_GPIO_B__USBOTG_OC 0x1f8 0x3f4 0x57c 0x12 0x001
+
+#define MX25_PAD_GPIO_C__GPIO_C 0x1fc 0x3f8 0x000 0x10 0x000
+#define MX25_PAD_GPIO_C__CAN2_TX 0x1fc 0x3f8 0x000 0x16 0x000
+
+#define MX25_PAD_GPIO_D__GPIO_D 0x200 0x3fc 0x000 0x10 0x000
+#define MX25_PAD_GPIO_E__LD16 0x204 0x400 0x000 0x02 0x000
+#define MX25_PAD_GPIO_D__CAN2_RX 0x200 0x3fc 0x484 0x16 0x001
+
+#define MX25_PAD_GPIO_E__GPIO_E 0x204 0x400 0x000 0x10 0x000
+#define MX25_PAD_GPIO_F__LD17 0x208 0x404 0x000 0x02 0x000
+#define MX25_PAD_GPIO_E__AUD7_TXD 0x204 0x400 0x000 0x14 0x000
+
+#define MX25_PAD_GPIO_F__GPIO_F 0x208 0x404 0x000 0x10 0x000
+#define MX25_PAD_GPIO_F__AUD7_TXC 0x208 0x404 0x000 0x14 0x000
+
+#define MX25_PAD_EXT_ARMCLK__EXT_ARMCLK 0x20c 0x000 0x000 0x10 0x000
+#define MX25_PAD_EXT_ARMCLK__GPIO_3_15 0x20c 0x000 0x000 0x15 0x000
+
+#define MX25_PAD_UPLL_BYPCLK__UPLL_BYPCLK 0x210 0x000 0x000 0x10 0x000
+#define MX25_PAD_UPLL_BYPCLK__GPIO_3_16 0x210 0x000 0x000 0x15 0x000
+
+#define MX25_PAD_VSTBY_REQ__VSTBY_REQ 0x214 0x408 0x000 0x10 0x000
+#define MX25_PAD_VSTBY_REQ__AUD7_TXFS 0x214 0x408 0x000 0x14 0x000
+#define MX25_PAD_VSTBY_REQ__GPIO_3_17 0x214 0x408 0x000 0x15 0x000
+#define MX25_PAD_VSTBY_ACK__VSTBY_ACK 0x218 0x40c 0x000 0x10 0x000
+#define MX25_PAD_VSTBY_ACK__GPIO_3_18 0x218 0x40c 0x000 0x15 0x000
+
+#define MX25_PAD_POWER_FAIL__POWER_FAIL 0x21c 0x410 0x000 0x10 0x000
+#define MX25_PAD_POWER_FAIL__AUD7_RXD 0x21c 0x410 0x478 0x14 0x001
+#define MX25_PAD_POWER_FAIL__GPIO_3_19 0x21c 0x410 0x000 0x15 0x000
+
+#define MX25_PAD_CLKO__CLKO 0x220 0x414 0x000 0x10 0x000
+#define MX25_PAD_CLKO__GPIO_2_21 0x220 0x414 0x000 0x15 0x000
+
+#define MX25_PAD_BOOT_MODE0__BOOT_MODE0 0x224 0x000 0x000 0x00 0x000
+#define MX25_PAD_BOOT_MODE0__GPIO_4_30 0x224 0x000 0x000 0x05 0x000
+#define MX25_PAD_BOOT_MODE1__BOOT_MODE1 0x228 0x000 0x000 0x00 0x000
+#define MX25_PAD_BOOT_MODE1__GPIO_4_31 0x228 0x000 0x000 0x05 0x000
+
+#endif /* __DTS_IMX25_PINFUNC_H */
diff --git a/arch/arm/boot/dts/imx25.dtsi b/arch/arm/boot/dts/imx25.dtsi
index 737ed5da8f71..32f760e24898 100644
--- a/arch/arm/boot/dts/imx25.dtsi
+++ b/arch/arm/boot/dts/imx25.dtsi
@@ -10,6 +10,7 @@
*/
#include "skeleton.dtsi"
+#include "imx25-pinfunc.h"
/ {
aliases {
@@ -173,12 +174,12 @@
status = "disabled";
};
- iomuxc@43fac000{
+ iomuxc: iomuxc@43fac000 {
compatible = "fsl,imx25-iomuxc";
reg = <0x43fac000 0x4000>;
};
- audmux@43fb0000 {
+ audmux: audmux@43fb0000 {
compatible = "fsl,imx25-audmux", "fsl,imx31-audmux";
reg = <0x43fb0000 0x4000>;
status = "disabled";
@@ -236,6 +237,11 @@
compatible = "fsl,imx25-ssi", "fsl,imx21-ssi";
reg = <0x50014000 0x4000>;
interrupts = <11>;
+ clocks = <&clks 118>;
+ clock-names = "ipg";
+ dmas = <&sdma 24 1 0>,
+ <&sdma 25 1 0>;
+ dma-names = "rx", "tx";
status = "disabled";
};
@@ -266,6 +272,11 @@
compatible = "fsl,imx25-ssi", "fsl,imx21-ssi";
reg = <0x50034000 0x4000>;
interrupts = <12>;
+ clocks = <&clks 117>;
+ clock-names = "ipg";
+ dmas = <&sdma 28 1 0>,
+ <&sdma 29 1 0>;
+ dma-names = "rx", "tx";
status = "disabled";
};
@@ -436,13 +447,14 @@
#interrupt-cells = <2>;
};
- sdma@53fd4000 {
+ sdma: sdma@53fd4000 {
compatible = "fsl,imx25-sdma", "fsl,imx35-sdma";
reg = <0x53fd4000 0x4000>;
clocks = <&clks 112>, <&clks 68>;
clock-names = "ipg", "ahb";
#dma-cells = <3>;
interrupts = <34>;
+ fsl,sdma-ram-script-name = "imx/sdma/sdma-imx25.bin";
};
wdog@53fdc000 {
diff --git a/arch/arm/boot/dts/imx27-apf27.dts b/arch/arm/boot/dts/imx27-apf27.dts
index ba4c6df08ece..09f57b39e3ef 100644
--- a/arch/arm/boot/dts/imx27-apf27.dts
+++ b/arch/arm/boot/dts/imx27-apf27.dts
@@ -34,11 +34,49 @@
};
};
+&iomuxc {
+ imx27-apf27 {
+ pinctrl_fec1: fec1grp {
+ fsl,pins = <
+ MX27_PAD_SD3_CMD__FEC_TXD0 0x0
+ MX27_PAD_SD3_CLK__FEC_TXD1 0x0
+ MX27_PAD_ATA_DATA0__FEC_TXD2 0x0
+ MX27_PAD_ATA_DATA1__FEC_TXD3 0x0
+ MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0
+ MX27_PAD_ATA_DATA3__FEC_RXD1 0x0
+ MX27_PAD_ATA_DATA4__FEC_RXD2 0x0
+ MX27_PAD_ATA_DATA5__FEC_RXD3 0x0
+ MX27_PAD_ATA_DATA6__FEC_MDIO 0x0
+ MX27_PAD_ATA_DATA7__FEC_MDC 0x0
+ MX27_PAD_ATA_DATA8__FEC_CRS 0x0
+ MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0
+ MX27_PAD_ATA_DATA10__FEC_RXD0 0x0
+ MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0
+ MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0
+ MX27_PAD_ATA_DATA13__FEC_COL 0x0
+ MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0
+ MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX27_PAD_UART1_TXD__UART1_TXD 0x0
+ MX27_PAD_UART1_RXD__UART1_RXD 0x0
+ >;
+ };
+ };
+};
+
&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fec1>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx27-apf27dev.dts b/arch/arm/boot/dts/imx27-apf27dev.dts
index 47c8c26012e4..2b6d489dae69 100644
--- a/arch/arm/boot/dts/imx27-apf27dev.dts
+++ b/arch/arm/boot/dts/imx27-apf27dev.dts
@@ -22,10 +22,10 @@
bits-per-pixel = <16>; /* non-standard but required */
fsl,pcr = <0xfae80083>; /* non-standard but required */
display-timings {
- timing0: 640x480 {
+ timing0: 800x480 {
clock-frequency = <33000033>;
hactive = <800>;
- vactive = <640>;
+ vactive = <480>;
hback-porch = <96>;
hfront-porch = <96>;
vback-porch = <20>;
@@ -38,20 +38,24 @@
gpio-keys {
compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpio_keys>;
user-key {
label = "user";
- gpios = <&gpio6 13 0>;
+ gpios = <&gpio6 13 GPIO_ACTIVE_HIGH>;
linux,code = <276>; /* BTN_EXTRA */
};
};
leds {
compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpio_leds>;
user {
label = "Heartbeat";
- gpios = <&gpio6 14 0>;
+ gpios = <&gpio6 14 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
};
};
@@ -59,25 +63,34 @@
&cspi1 {
fsl,spi-num-chipselects = <1>;
- cs-gpios = <&gpio4 28 1>;
+ cs-gpios = <&gpio4 28 GPIO_ACTIVE_LOW>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_cspi1 &pinctrl_cspi1_cs>;
status = "okay";
};
&cspi2 {
fsl,spi-num-chipselects = <3>;
- cs-gpios = <&gpio4 21 1>, <&gpio4 27 1>,
- <&gpio2 17 1>;
+ cs-gpios = <&gpio4 21 GPIO_ACTIVE_LOW>,
+ <&gpio4 27 GPIO_ACTIVE_LOW>,
+ <&gpio2 17 GPIO_ACTIVE_LOW>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_cspi2 &pinctrl_cspi2_cs>;
status = "okay";
};
&fb {
display = <&display>;
fsl,dmacr = <0x00020010>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_imxfb1>;
status = "okay";
};
&i2c1 {
clock-frequency = <400000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
rtc@68 {
@@ -87,5 +100,127 @@
};
&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
};
+
+&iomuxc {
+ imx27-apf27dev {
+ pinctrl_cspi1: cspi1grp {
+ fsl,pins = <
+ MX27_PAD_CSPI1_MISO__CSPI1_MISO 0x0
+ MX27_PAD_CSPI1_MOSI__CSPI1_MOSI 0x0
+ MX27_PAD_CSPI1_SCLK__CSPI1_SCLK 0x0
+ >;
+ };
+
+ pinctrl_cspi1_cs: cspi1csgrp {
+ fsl,pins = <MX27_PAD_CSPI1_SS0__GPIO4_28 0x0>;
+ };
+
+ pinctrl_cspi2: cspi2grp {
+ fsl,pins = <
+ MX27_PAD_CSPI2_MISO__CSPI2_MISO 0x0
+ MX27_PAD_CSPI2_MOSI__CSPI2_MOSI 0x0
+ MX27_PAD_CSPI2_SCLK__CSPI2_SCLK 0x0
+ >;
+ };
+
+ pinctrl_cspi2_cs: cspi2csgrp {
+ fsl,pins = <
+ MX27_PAD_CSI_D5__GPIO2_17 0x0
+ MX27_PAD_CSPI2_SS0__GPIO4_21 0x0
+ MX27_PAD_CSPI1_SS1__GPIO4_27 0x0
+ >;
+ };
+
+ pinctrl_gpio_leds: gpioledsgrp {
+ fsl,pins = <MX27_PAD_PC_VS1__GPIO6_14 0x0>;
+ };
+
+ pinctrl_gpio_keys: gpiokeysgrp {
+ fsl,pins = <MX27_PAD_PC_VS2__GPIO6_13 0x0>;
+ };
+
+ pinctrl_imxfb1: imxfbgrp {
+ fsl,pins = <
+ MX27_PAD_CLS__CLS 0x0
+ MX27_PAD_CONTRAST__CONTRAST 0x0
+ MX27_PAD_LD0__LD0 0x0
+ MX27_PAD_LD1__LD1 0x0
+ MX27_PAD_LD2__LD2 0x0
+ MX27_PAD_LD3__LD3 0x0
+ MX27_PAD_LD4__LD4 0x0
+ MX27_PAD_LD5__LD5 0x0
+ MX27_PAD_LD6__LD6 0x0
+ MX27_PAD_LD7__LD7 0x0
+ MX27_PAD_LD8__LD8 0x0
+ MX27_PAD_LD9__LD9 0x0
+ MX27_PAD_LD10__LD10 0x0
+ MX27_PAD_LD11__LD11 0x0
+ MX27_PAD_LD12__LD12 0x0
+ MX27_PAD_LD13__LD13 0x0
+ MX27_PAD_LD14__LD14 0x0
+ MX27_PAD_LD15__LD15 0x0
+ MX27_PAD_LD16__LD16 0x0
+ MX27_PAD_LD17__LD17 0x0
+ MX27_PAD_LSCLK__LSCLK 0x0
+ MX27_PAD_OE_ACD__OE_ACD 0x0
+ MX27_PAD_PS__PS 0x0
+ MX27_PAD_REV__REV 0x0
+ MX27_PAD_SPL_SPR__SPL_SPR 0x0
+ MX27_PAD_HSYNC__HSYNC 0x0
+ MX27_PAD_VSYNC__VSYNC 0x0
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX27_PAD_I2C_DATA__I2C_DATA 0x0
+ MX27_PAD_I2C_CLK__I2C_CLK 0x0
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX27_PAD_I2C2_SDA__I2C2_SDA 0x0
+ MX27_PAD_I2C2_SCL__I2C2_SCL 0x0
+ >;
+ };
+
+ pinctrl_pwm: pwmgrp {
+ fsl,pins = <
+ MX27_PAD_PWMO__PWMO 0x0
+ >;
+ };
+
+ pinctrl_sdhc2: sdhc2grp {
+ fsl,pins = <
+ MX27_PAD_SD2_CLK__SD2_CLK 0x0
+ MX27_PAD_SD2_CMD__SD2_CMD 0x0
+ MX27_PAD_SD2_D0__SD2_D0 0x0
+ MX27_PAD_SD2_D1__SD2_D1 0x0
+ MX27_PAD_SD2_D2__SD2_D2 0x0
+ MX27_PAD_SD2_D3__SD2_D3 0x0
+ >;
+ };
+
+ pinctrl_sdhc2_cd: sdhc2cdgrp {
+ fsl,pins = <MX27_PAD_TOUT__GPIO3_14 0x0>;
+ };
+ };
+};
+
+&sdhci2 {
+ bus-width = <4>;
+ cd-gpios = <&gpio3 14 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sdhc2 &pinctrl_sdhc2_cd>;
+ status = "okay";
+};
+
+&pwm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm>;
+};
diff --git a/arch/arm/boot/dts/imx27-phytec-phycard-s-rdk.dts b/arch/arm/boot/dts/imx27-phytec-phycard-s-rdk.dts
index 5a31c776513f..3c3964a99637 100644
--- a/arch/arm/boot/dts/imx27-phytec-phycard-s-rdk.dts
+++ b/arch/arm/boot/dts/imx27-phytec-phycard-s-rdk.dts
@@ -9,7 +9,7 @@
* http://www.gnu.org/copyleft/gpl.html
*/
-#include "imx27-phytec-phycard-s-som.dts"
+#include "imx27-phytec-phycard-s-som.dtsi"
/ {
model = "Phytec pca100 rapid development kit";
@@ -37,9 +37,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_3v3: 3v3 {
+ reg_3v3: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "3V3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
@@ -54,6 +57,8 @@
};
&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
rtc@51 {
@@ -68,26 +73,92 @@
};
};
+&iomuxc {
+ imx27-phycard-s-rdk {
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX27_PAD_I2C2_SDA__I2C2_SDA 0x0
+ MX27_PAD_I2C2_SCL__I2C2_SCL 0x0
+ >;
+ };
+
+ pinctrl_owire1: owire1grp {
+ fsl,pins = <
+ MX27_PAD_RTCK__OWIRE 0x0
+ >;
+ };
+
+ pinctrl_sdhc2: sdhc2grp {
+ fsl,pins = <
+ MX27_PAD_SD2_CLK__SD2_CLK 0x0
+ MX27_PAD_SD2_CMD__SD2_CMD 0x0
+ MX27_PAD_SD2_D0__SD2_D0 0x0
+ MX27_PAD_SD2_D1__SD2_D1 0x0
+ MX27_PAD_SD2_D2__SD2_D2 0x0
+ MX27_PAD_SD2_D3__SD2_D3 0x0
+ MX27_PAD_SSI3_RXDAT__GPIO3_29 0x0 /* CD */
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX27_PAD_UART1_TXD__UART1_TXD 0x0
+ MX27_PAD_UART1_RXD__UART1_RXD 0x0
+ MX27_PAD_UART1_CTS__UART1_CTS 0x0
+ MX27_PAD_UART1_RTS__UART1_RTS 0x0
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX27_PAD_UART2_TXD__UART2_TXD 0x0
+ MX27_PAD_UART2_RXD__UART2_RXD 0x0
+ MX27_PAD_UART2_CTS__UART2_CTS 0x0
+ MX27_PAD_UART2_RTS__UART2_RTS 0x0
+ >;
+ };
+
+ pinctrl_uart3: uart3grp {
+ fsl,pins = <
+ MX27_PAD_UART3_TXD__UART3_TXD 0x0
+ MX27_PAD_UART3_RXD__UART3_RXD 0x0
+ MX27_PAD_UART3_CTS__UART3_CTS 0x0
+ MX27_PAD_UART3_RTS__UART3_RTS 0x0
+ >;
+ };
+ };
+};
+
&owire {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_owire1>;
status = "okay";
};
&sdhci2 {
- cd-gpios = <&gpio3 29 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sdhc2>;
+ cd-gpios = <&gpio3 29 GPIO_ACTIVE_HIGH>;
status = "okay";
};
&uart1 {
fsl,uart-has-rtscts;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
&uart2 {
fsl,uart-has-rtscts;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
status = "okay";
};
&uart3 {
fsl,uart-has-rtscts;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart3>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dts b/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dts
deleted file mode 100644
index c8d57d1d0743..000000000000
--- a/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dts
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2012 Sascha Hauer, Uwe Kleine-König, Steffen Trumtrar
- * and Markus Pargmann, Pengutronix
- *
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
-
-/dts-v1/;
-#include "imx27.dtsi"
-
-/ {
- model = "Phytec pca100";
- compatible = "phytec,imx27-pca100", "fsl,imx27";
-
- memory {
- reg = <0xa0000000 0x08000000>; /* 128MB */
- };
-};
-
-&cspi1 {
- fsl,spi-num-chipselects = <2>;
- cs-gpios = <&gpio4 28 0>,
- <&gpio4 27 0>;
- status = "okay";
-};
-
-&fec {
- status = "okay";
-};
-
-&i2c2 {
- status = "okay";
-
- at24@52 {
- compatible = "at,24c32";
- pagesize = <32>;
- reg = <0x52>;
- };
-};
diff --git a/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi b/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi
new file mode 100644
index 000000000000..1b6248079682
--- /dev/null
+++ b/arch/arm/boot/dts/imx27-phytec-phycard-s-som.dtsi
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2012 Sascha Hauer, Uwe Kleine-König, Steffen Trumtrar
+ * and Markus Pargmann, Pengutronix
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx27.dtsi"
+
+/ {
+ model = "Phytec pca100";
+ compatible = "phytec,imx27-pca100", "fsl,imx27";
+
+ memory {
+ reg = <0xa0000000 0x08000000>; /* 128MB */
+ };
+};
+
+&cspi1 {
+ fsl,spi-num-chipselects = <2>;
+ cs-gpios = <&gpio4 28 GPIO_ACTIVE_HIGH>,
+ <&gpio4 27 GPIO_ACTIVE_HIGH>;
+ status = "okay";
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fec1>;
+ status = "okay";
+};
+
+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+
+ at24@52 {
+ compatible = "at,24c32";
+ pagesize = <32>;
+ reg = <0x52>;
+ };
+};
+
+&iomuxc {
+ imx27-phycard-s-som {
+ pinctrl_fec1: fec1grp {
+ fsl,pins = <
+ MX27_PAD_SD3_CMD__FEC_TXD0 0x0
+ MX27_PAD_SD3_CLK__FEC_TXD1 0x0
+ MX27_PAD_ATA_DATA0__FEC_TXD2 0x0
+ MX27_PAD_ATA_DATA1__FEC_TXD3 0x0
+ MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0
+ MX27_PAD_ATA_DATA3__FEC_RXD1 0x0
+ MX27_PAD_ATA_DATA4__FEC_RXD2 0x0
+ MX27_PAD_ATA_DATA5__FEC_RXD3 0x0
+ MX27_PAD_ATA_DATA6__FEC_MDIO 0x0
+ MX27_PAD_ATA_DATA7__FEC_MDC 0x0
+ MX27_PAD_ATA_DATA8__FEC_CRS 0x0
+ MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0
+ MX27_PAD_ATA_DATA10__FEC_RXD0 0x0
+ MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0
+ MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0
+ MX27_PAD_ATA_DATA13__FEC_COL 0x0
+ MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0
+ MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX27_PAD_I2C2_SDA__I2C2_SDA 0x0
+ MX27_PAD_I2C2_SCL__I2C2_SCL 0x0
+ >;
+ };
+
+ pinctrl_nfc: nfcgrp {
+ fsl,pins = <
+ MX27_PAD_NFRB__NFRB 0x0
+ MX27_PAD_NFCLE__NFCLE 0x0
+ MX27_PAD_NFWP_B__NFWP_B 0x0
+ MX27_PAD_NFCE_B__NFCE_B 0x0
+ MX27_PAD_NFALE__NFALE 0x0
+ MX27_PAD_NFRE_B__NFRE_B 0x0
+ MX27_PAD_NFWE_B__NFWE_B 0x0
+ >;
+ };
+ };
+};
+
+&nfc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_nfc>;
+ nand-bus-width = <8>;
+ nand-ecc-mode = "hw";
+ nand-on-flash-bbt;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx27-phytec-phycore-rdk.dts b/arch/arm/boot/dts/imx27-phytec-phycore-rdk.dts
index 0fc6551786c6..df3b2e731835 100644
--- a/arch/arm/boot/dts/imx27-phytec-phycore-rdk.dts
+++ b/arch/arm/boot/dts/imx27-phytec-phycore-rdk.dts
@@ -7,7 +7,7 @@
* http://www.gnu.org/copyleft/gpl.html
*/
-#include "imx27-phytec-phycore-som.dts"
+#include "imx27-phytec-phycore-som.dtsi"
/ {
model = "Phytec pcm970";
@@ -16,32 +16,200 @@
&cspi1 {
fsl,spi-num-chipselects = <2>;
- cs-gpios = <&gpio4 28 0>, <&gpio4 27 0>;
+ cs-gpios = <&gpio4 28 GPIO_ACTIVE_HIGH>,
+ <&gpio4 27 GPIO_ACTIVE_LOW>;
+};
+
+&i2c1 {
+ clock-frequency = <400000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ camgpio: pca9536@41 {
+ compatible = "nxp,pca9536";
+ reg = <0x41>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+};
+
+&iomuxc {
+ imx27_phycore_rdk {
+ pinctrl_i2c1: i2c1grp {
+ /* Add pullup to DATA line */
+ fsl,pins = <
+ MX27_PAD_I2C_DATA__I2C_DATA 0x1
+ MX27_PAD_I2C_CLK__I2C_CLK 0x0
+ >;
+ };
+
+ pinctrl_owire1: owire1grp {
+ fsl,pins = <
+ MX27_PAD_RTCK__OWIRE 0x0
+ >;
+ };
+
+ pinctrl_sdhc2: sdhc2grp {
+ fsl,pins = <
+ MX27_PAD_SD2_CLK__SD2_CLK 0x0
+ MX27_PAD_SD2_CMD__SD2_CMD 0x0
+ MX27_PAD_SD2_D0__SD2_D0 0x0
+ MX27_PAD_SD2_D1__SD2_D1 0x0
+ MX27_PAD_SD2_D2__SD2_D2 0x0
+ MX27_PAD_SD2_D3__SD2_D3 0x0
+ MX27_PAD_SSI3_FS__GPIO3_28 0x0 /* WP */
+ MX27_PAD_SSI3_RXDAT__GPIO3_29 0x0 /* CD */
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX27_PAD_UART1_TXD__UART1_TXD 0x0
+ MX27_PAD_UART1_RXD__UART1_RXD 0x0
+ MX27_PAD_UART1_CTS__UART1_CTS 0x0
+ MX27_PAD_UART1_RTS__UART1_RTS 0x0
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX27_PAD_UART2_TXD__UART2_TXD 0x0
+ MX27_PAD_UART2_RXD__UART2_RXD 0x0
+ MX27_PAD_UART2_CTS__UART2_CTS 0x0
+ MX27_PAD_UART2_RTS__UART2_RTS 0x0
+ >;
+ };
+
+ pinctrl_usbh2: usbh2grp {
+ fsl,pins = <
+ MX27_PAD_USBH2_CLK__USBH2_CLK 0x0
+ MX27_PAD_USBH2_DIR__USBH2_DIR 0x0
+ MX27_PAD_USBH2_NXT__USBH2_NXT 0x0
+ MX27_PAD_USBH2_STP__USBH2_STP 0x0
+ MX27_PAD_CSPI2_SCLK__USBH2_DATA0 0x0
+ MX27_PAD_CSPI2_MOSI__USBH2_DATA1 0x0
+ MX27_PAD_CSPI2_MISO__USBH2_DATA2 0x0
+ MX27_PAD_CSPI2_SS1__USBH2_DATA3 0x0
+ MX27_PAD_CSPI2_SS2__USBH2_DATA4 0x0
+ MX27_PAD_CSPI1_SS2__USBH2_DATA5 0x0
+ MX27_PAD_CSPI2_SS0__USBH2_DATA6 0x0
+ MX27_PAD_USBH2_DATA7__USBH2_DATA7 0x0
+ >;
+ };
+
+ pinctrl_weim: weimgrp {
+ fsl,pins = <
+ MX27_PAD_CS4_B__CS4_B 0x0 /* CS4 */
+ MX27_PAD_SD1_D1__GPIO5_19 0x0 /* CAN IRQ */
+ >;
+ };
+ };
+};
+
+&owire {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_owire1>;
+ status = "okay";
+};
+
+&pmicleds {
+ ledr1: led@3 {
+ reg = <3>;
+ label = "system:red1:user";
+ };
+
+ ledg1: led@4 {
+ reg = <4>;
+ label = "system:green1:user";
+ };
+
+ ledb1: led@5 {
+ reg = <5>;
+ label = "system:blue1:user";
+ };
+
+ ledr2: led@6 {
+ reg = <6>;
+ label = "system:red2:user";
+ };
+
+ ledg2: led@7 {
+ reg = <7>;
+ label = "system:green2:user";
+ };
+
+ ledb2: led@8 {
+ reg = <8>;
+ label = "system:blue2:user";
+ };
+
+ ledr3: led@9 {
+ reg = <9>;
+ label = "system:red3:nand";
+ linux,default-trigger = "nand-disk";
+ };
+
+ ledg3: led@10 {
+ reg = <10>;
+ label = "system:green3:live";
+ linux,default-trigger = "heartbeat";
+ };
+
+ ledb3: led@11 {
+ reg = <11>;
+ label = "system:blue3:cpu";
+ linux,default-trigger = "cpu0";
+ };
};
&sdhci2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sdhc2>;
bus-width = <4>;
- cd-gpios = <&gpio3 29 0>;
- wp-gpios = <&gpio3 28 0>;
+ cd-gpios = <&gpio3 29 GPIO_ACTIVE_HIGH>;
+ wp-gpios = <&gpio3 28 GPIO_ACTIVE_HIGH>;
vmmc-supply = <&vmmc1_reg>;
status = "okay";
};
&uart1 {
fsl,uart-has-rtscts;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
};
&uart2 {
fsl,uart-has-rtscts;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ status = "okay";
+};
+
+&usbh2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbh2>;
+ dr_mode = "host";
+ phy_type = "ulpi";
+ vbus-supply = <&reg_5v0>;
+ disable-over-current;
status = "okay";
};
+&usbphy2 {
+ vcc-supply = <&reg_5v0>;
+};
+
&weim {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_weim>;
+
can@d4000000 {
compatible = "nxp,sja1000";
reg = <4 0x00000000 0x00000100>;
interrupt-parent = <&gpio5>;
- interrupts = <19 0x2>;
+ interrupts = <19 IRQ_TYPE_EDGE_FALLING>;
nxp,external-clock-frequency = <16000000>;
nxp,tx-output-config = <0x16>;
nxp,no-comparator-bypass;
diff --git a/arch/arm/boot/dts/imx27-phytec-phycore-som.dts b/arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi
index 4ec402c38945..cefaa6994623 100644
--- a/arch/arm/boot/dts/imx27-phytec-phycore-som.dts
+++ b/arch/arm/boot/dts/imx27-phytec-phycore-som.dtsi
@@ -19,6 +19,28 @@
memory {
reg = <0xa0000000 0x08000000>;
};
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_3v3: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ reg_5v0: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "5V0";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ };
+ };
};
&audmux {
@@ -37,21 +59,30 @@
};
&cspi1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_cspi1>;
fsl,spi-num-chipselects = <1>;
- cs-gpios = <&gpio4 28 0>;
+ cs-gpios = <&gpio4 28 GPIO_ACTIVE_HIGH>;
status = "okay";
pmic: mc13783@0 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,mc13783";
- spi-max-frequency = <20000000>;
reg = <0>;
+ spi-cs-high;
+ spi-max-frequency = <20000000>;
interrupt-parent = <&gpio2>;
- interrupts = <23 0x4>;
+ interrupts = <23 IRQ_TYPE_LEVEL_HIGH>;
fsl,mc13xxx-uses-adc;
fsl,mc13xxx-uses-rtc;
+ pmicleds: leds {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ led-control = <0x001 0x000 0x000 0x000 0x000 0x000>;
+ };
+
regulators {
/* SW1A and SW1B joined operation */
sw1_reg: sw1a {
@@ -134,12 +165,18 @@
};
&fec {
- phy-reset-gpios = <&gpio3 30 0>;
+ phy-mode = "mii";
+ phy-reset-gpios = <&gpio3 30 GPIO_ACTIVE_HIGH>;
+ phy-supply = <&reg_3v3>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fec1>;
status = "okay";
};
&i2c2 {
clock-frequency = <400000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
at24@52 {
@@ -159,16 +196,102 @@
};
};
+&iomuxc {
+ imx27_phycore_som {
+ pinctrl_cspi1: cspi1grp {
+ fsl,pins = <
+ MX27_PAD_CSPI1_MISO__CSPI1_MISO 0x0
+ MX27_PAD_CSPI1_MOSI__CSPI1_MOSI 0x0
+ MX27_PAD_CSPI1_SCLK__CSPI1_SCLK 0x0
+ MX27_PAD_CSPI1_SS0__GPIO4_28 0x0 /* SPI1 CS0 */
+ MX27_PAD_USB_PWR__GPIO2_23 0x0 /* PMIC IRQ */
+ >;
+ };
+
+ pinctrl_fec1: fec1grp {
+ fsl,pins = <
+ MX27_PAD_SD3_CMD__FEC_TXD0 0x0
+ MX27_PAD_SD3_CLK__FEC_TXD1 0x0
+ MX27_PAD_ATA_DATA0__FEC_TXD2 0x0
+ MX27_PAD_ATA_DATA1__FEC_TXD3 0x0
+ MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0
+ MX27_PAD_ATA_DATA3__FEC_RXD1 0x0
+ MX27_PAD_ATA_DATA4__FEC_RXD2 0x0
+ MX27_PAD_ATA_DATA5__FEC_RXD3 0x0
+ MX27_PAD_ATA_DATA6__FEC_MDIO 0x0
+ MX27_PAD_ATA_DATA7__FEC_MDC 0x0
+ MX27_PAD_ATA_DATA8__FEC_CRS 0x0
+ MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0
+ MX27_PAD_ATA_DATA10__FEC_RXD0 0x0
+ MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0
+ MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0
+ MX27_PAD_ATA_DATA13__FEC_COL 0x0
+ MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0
+ MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0
+ MX27_PAD_SSI3_TXDAT__GPIO3_30 0x0 /* FEC RST */
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX27_PAD_I2C2_SDA__I2C2_SDA 0x0
+ MX27_PAD_I2C2_SCL__I2C2_SCL 0x0
+ >;
+ };
+
+ pinctrl_nfc: nfcgrp {
+ fsl,pins = <
+ MX27_PAD_NFRB__NFRB 0x0
+ MX27_PAD_NFCLE__NFCLE 0x0
+ MX27_PAD_NFWP_B__NFWP_B 0x0
+ MX27_PAD_NFCE_B__NFCE_B 0x0
+ MX27_PAD_NFALE__NFALE 0x0
+ MX27_PAD_NFRE_B__NFRE_B 0x0
+ MX27_PAD_NFWE_B__NFWE_B 0x0
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX27_PAD_USBOTG_CLK__USBOTG_CLK 0x0
+ MX27_PAD_USBOTG_DIR__USBOTG_DIR 0x0
+ MX27_PAD_USBOTG_NXT__USBOTG_NXT 0x0
+ MX27_PAD_USBOTG_STP__USBOTG_STP 0x0
+ MX27_PAD_USBOTG_DATA0__USBOTG_DATA0 0x0
+ MX27_PAD_USBOTG_DATA1__USBOTG_DATA1 0x0
+ MX27_PAD_USBOTG_DATA2__USBOTG_DATA2 0x0
+ MX27_PAD_USBOTG_DATA3__USBOTG_DATA3 0x0
+ MX27_PAD_USBOTG_DATA4__USBOTG_DATA4 0x0
+ MX27_PAD_USBOTG_DATA5__USBOTG_DATA5 0x0
+ MX27_PAD_USBOTG_DATA6__USBOTG_DATA6 0x0
+ MX27_PAD_USBOTG_DATA7__USBOTG_DATA7 0x0
+ >;
+ };
+ };
+};
+
&nfc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_nfc>;
nand-bus-width = <8>;
nand-ecc-mode = "hw";
+ nand-on-flash-bbt;
status = "okay";
};
-&uart1 {
+&usbotg {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ dr_mode = "otg";
+ phy_type = "ulpi";
+ vbus-supply = <&sw3_reg>;
status = "okay";
};
+&usbphy0 {
+ vcc-supply = <&sw3_reg>;
+};
+
&weim {
status = "okay";
diff --git a/arch/arm/boot/dts/imx27-pinfunc.h b/arch/arm/boot/dts/imx27-pinfunc.h
new file mode 100644
index 000000000000..f5387b4de577
--- /dev/null
+++ b/arch/arm/boot/dts/imx27-pinfunc.h
@@ -0,0 +1,526 @@
+/*
+ * Copyright 2013 Markus Pargmann <mpa@pengutronix.de>, Pengutronix
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __DTS_IMX27_PINFUNC_H
+#define __DTS_IMX27_PINFUNC_H
+
+/*
+ * The pin function ID is a tuple of
+ * <pin mux_id>
+ * mux_id consists of
+ * function + (direction << 2) + (gpio_oconf << 4) + (gpio_iconfa << 8) + (gpio_iconfb << 10)
+ *
+ * function: 0 - Primary function
+ * 1 - Alternate function
+ * 2 - GPIO
+ * direction: 0 - Input
+ * 1 - Output
+ * gpio_oconf: 0 - A_IN
+ * 1 - B_IN
+ * 2 - C_IN
+ * 3 - Data Register
+ * gpio_iconfa/b: 0 - GPIO_IN
+ * 1 - Interrupt Status Register
+ * 2 - 0
+ * 3 - 1
+ *
+ * 'pin' is an integer between 0 and 0xbf. imx27 has 6 ports with 32 configurable
+ * configurable pins each. 'pin' is PORT * 32 + PORT_PIN, PORT_PIN is the pin
+ * number on the specific port (between 0 and 31).
+ */
+
+#define MX27_PAD_USBH2_CLK__USBH2_CLK 0x00 0x000
+#define MX27_PAD_USBH2_CLK__GPIO1_0 0x00 0x032
+#define MX27_PAD_USBH2_DIR__USBH2_DIR 0x01 0x000
+#define MX27_PAD_USBH2_DIR__GPIO1_1 0x01 0x032
+#define MX27_PAD_USBH2_DATA7__USBH2_DATA7 0x02 0x004
+#define MX27_PAD_USBH2_DATA7__GPIO1_2 0x02 0x032
+#define MX27_PAD_USBH2_NXT__USBH2_NXT 0x03 0x000
+#define MX27_PAD_USBH2_NXT__GPIO1_3 0x03 0x032
+#define MX27_PAD_USBH2_STP__USBH2_STP 0x04 0x004
+#define MX27_PAD_USBH2_STP__GPIO1_4 0x04 0x032
+#define MX27_PAD_LSCLK__LSCLK 0x05 0x004
+#define MX27_PAD_LSCLK__GPIO1_5 0x05 0x032
+#define MX27_PAD_LD0__LD0 0x06 0x004
+#define MX27_PAD_LD0__GPIO1_6 0x06 0x032
+#define MX27_PAD_LD1__LD1 0x07 0x004
+#define MX27_PAD_LD1__GPIO1_7 0x07 0x032
+#define MX27_PAD_LD2__LD2 0x08 0x004
+#define MX27_PAD_LD2__GPIO1_8 0x08 0x032
+#define MX27_PAD_LD3__LD3 0x09 0x004
+#define MX27_PAD_LD3__GPIO1_9 0x09 0x032
+#define MX27_PAD_LD4__LD4 0x0a 0x004
+#define MX27_PAD_LD4__GPIO1_10 0x0a 0x032
+#define MX27_PAD_LD5__LD5 0x0b 0x004
+#define MX27_PAD_LD5__GPIO1_11 0x0b 0x032
+#define MX27_PAD_LD6__LD6 0x0c 0x004
+#define MX27_PAD_LD6__GPIO1_12 0x0c 0x032
+#define MX27_PAD_LD7__LD7 0x0d 0x004
+#define MX27_PAD_LD7__GPIO1_13 0x0d 0x032
+#define MX27_PAD_LD8__LD8 0x0e 0x004
+#define MX27_PAD_LD8__GPIO1_14 0x0e 0x032
+#define MX27_PAD_LD9__LD9 0x0f 0x004
+#define MX27_PAD_LD9__GPIO1_15 0x0f 0x032
+#define MX27_PAD_LD10__LD10 0x10 0x004
+#define MX27_PAD_LD10__GPIO1_16 0x10 0x032
+#define MX27_PAD_LD11__LD11 0x11 0x004
+#define MX27_PAD_LD11__GPIO1_17 0x11 0x032
+#define MX27_PAD_LD12__LD12 0x12 0x004
+#define MX27_PAD_LD12__GPIO1_18 0x12 0x032
+#define MX27_PAD_LD13__LD13 0x13 0x004
+#define MX27_PAD_LD13__GPIO1_19 0x13 0x032
+#define MX27_PAD_LD14__LD14 0x14 0x004
+#define MX27_PAD_LD14__GPIO1_20 0x14 0x032
+#define MX27_PAD_LD15__LD15 0x15 0x004
+#define MX27_PAD_LD15__GPIO1_21 0x15 0x032
+#define MX27_PAD_LD16__LD16 0x16 0x004
+#define MX27_PAD_LD16__GPIO1_22 0x16 0x032
+#define MX27_PAD_LD17__LD17 0x17 0x004
+#define MX27_PAD_LD17__GPIO1_23 0x17 0x032
+#define MX27_PAD_REV__REV 0x18 0x004
+#define MX27_PAD_REV__GPIO1_24 0x18 0x032
+#define MX27_PAD_CLS__CLS 0x19 0x004
+#define MX27_PAD_CLS__GPIO1_25 0x19 0x032
+#define MX27_PAD_PS__PS 0x1a 0x004
+#define MX27_PAD_PS__GPIO1_26 0x1a 0x032
+#define MX27_PAD_SPL_SPR__SPL_SPR 0x1b 0x004
+#define MX27_PAD_SPL_SPR__GPIO1_27 0x1b 0x032
+#define MX27_PAD_HSYNC__HSYNC 0x1c 0x004
+#define MX27_PAD_HSYNC__GPIO1_28 0x1c 0x032
+#define MX27_PAD_VSYNC__VSYNC 0x1d 0x004
+#define MX27_PAD_VSYNC__GPIO1_29 0x1d 0x032
+#define MX27_PAD_CONTRAST__CONTRAST 0x1e 0x004
+#define MX27_PAD_CONTRAST__GPIO1_30 0x1e 0x032
+#define MX27_PAD_OE_ACD__OE_ACD 0x1f 0x004
+#define MX27_PAD_OE_ACD__GPIO1_31 0x1f 0x032
+#define MX27_PAD_UNUSED0__UNUSED0 0x20 0x004
+#define MX27_PAD_UNUSED0__GPIO2_0 0x20 0x032
+#define MX27_PAD_UNUSED1__UNUSED1 0x21 0x004
+#define MX27_PAD_UNUSED1__GPIO2_1 0x21 0x032
+#define MX27_PAD_UNUSED2__UNUSED2 0x22 0x004
+#define MX27_PAD_UNUSED2__GPIO2_2 0x22 0x032
+#define MX27_PAD_UNUSED3__UNUSED3 0x23 0x004
+#define MX27_PAD_UNUSED3__GPIO2_3 0x23 0x032
+#define MX27_PAD_SD2_D0__SD2_D0 0x24 0x004
+#define MX27_PAD_SD2_D0__MSHC_DATA0 0x24 0x005
+#define MX27_PAD_SD2_D0__GPIO2_4 0x24 0x032
+#define MX27_PAD_SD2_D1__SD2_D1 0x25 0x004
+#define MX27_PAD_SD2_D1__MSHC_DATA1 0x25 0x005
+#define MX27_PAD_SD2_D1__GPIO2_5 0x25 0x032
+#define MX27_PAD_SD2_D2__SD2_D2 0x26 0x004
+#define MX27_PAD_SD2_D2__MSHC_DATA2 0x26 0x005
+#define MX27_PAD_SD2_D2__GPIO2_6 0x26 0x032
+#define MX27_PAD_SD2_D3__SD2_D3 0x27 0x004
+#define MX27_PAD_SD2_D3__MSHC_DATA3 0x27 0x005
+#define MX27_PAD_SD2_D3__GPIO2_7 0x27 0x032
+#define MX27_PAD_SD2_CMD__SD2_CMD 0x28 0x004
+#define MX27_PAD_SD2_CMD__MSHC_BS 0x28 0x005
+#define MX27_PAD_SD2_CMD__GPIO2_8 0x28 0x032
+#define MX27_PAD_SD2_CLK__SD2_CLK 0x29 0x004
+#define MX27_PAD_SD2_CLK__MSHC_SCLK 0x29 0x005
+#define MX27_PAD_SD2_CLK__GPIO2_9 0x29 0x032
+#define MX27_PAD_CSI_D0__CSI_D0 0x2a 0x000
+#define MX27_PAD_CSI_D0__UART6_TXD 0x2a 0x005
+#define MX27_PAD_CSI_D0__GPIO2_10 0x2a 0x032
+#define MX27_PAD_CSI_D1__CSI_D1 0x2b 0x000
+#define MX27_PAD_CSI_D1__UART6_RXD 0x2b 0x001
+#define MX27_PAD_CSI_D1__GPIO2_11 0x2b 0x032
+#define MX27_PAD_CSI_D2__CSI_D2 0x2c 0x000
+#define MX27_PAD_CSI_D2__UART6_CTS 0x2c 0x005
+#define MX27_PAD_CSI_D2__GPIO2_12 0x2c 0x032
+#define MX27_PAD_CSI_D3__CSI_D3 0x2d 0x000
+#define MX27_PAD_CSI_D3__UART6_RTS 0x2d 0x001
+#define MX27_PAD_CSI_D3__GPIO2_13 0x2d 0x032
+#define MX27_PAD_CSI_D4__CSI_D4 0x2e 0x000
+#define MX27_PAD_CSI_D4__GPIO2_14 0x2e 0x032
+#define MX27_PAD_CSI_MCLK__CSI_MCLK 0x2f 0x004
+#define MX27_PAD_CSI_MCLK__GPIO2_15 0x2f 0x032
+#define MX27_PAD_CSI_PIXCLK__CSI_PIXCLK 0x30 0x000
+#define MX27_PAD_CSI_PIXCLK__GPIO2_16 0x30 0x032
+#define MX27_PAD_CSI_D5__CSI_D5 0x31 0x000
+#define MX27_PAD_CSI_D5__GPIO2_17 0x31 0x032
+#define MX27_PAD_CSI_D6__CSI_D6 0x32 0x000
+#define MX27_PAD_CSI_D6__UART5_TXD 0x32 0x005
+#define MX27_PAD_CSI_D6__GPIO2_18 0x32 0x032
+#define MX27_PAD_CSI_D7__CSI_D7 0x33 0x000
+#define MX27_PAD_CSI_D7__UART5_RXD 0x33 0x001
+#define MX27_PAD_CSI_D7__GPIO2_19 0x33 0x032
+#define MX27_PAD_CSI_VSYNC__CSI_VSYNC 0x34 0x000
+#define MX27_PAD_CSI_VSYNC__UART5_CTS 0x34 0x005
+#define MX27_PAD_CSI_VSYNC__GPIO2_20 0x34 0x032
+#define MX27_PAD_CSI_HSYNC__CSI_HSYNC 0x35 0x000
+#define MX27_PAD_CSI_HSYNC__UART5_RTS 0x35 0x001
+#define MX27_PAD_CSI_HSYNC__GPIO2_21 0x35 0x032
+#define MX27_PAD_USBH1_SUSP__USBH1_SUSP 0x36 0x004
+#define MX27_PAD_USBH1_SUSP__GPIO2_22 0x36 0x032
+#define MX27_PAD_USB_PWR__USB_PWR 0x37 0x004
+#define MX27_PAD_USB_PWR__GPIO2_23 0x37 0x032
+#define MX27_PAD_USB_OC_B__USB_OC_B 0x38 0x000
+#define MX27_PAD_USB_OC_B__GPIO2_24 0x38 0x032
+#define MX27_PAD_USBH1_RCV__USBH1_RCV 0x39 0x004
+#define MX27_PAD_USBH1_RCV__GPIO2_25 0x39 0x032
+#define MX27_PAD_USBH1_FS__USBH1_FS 0x3a 0x004
+#define MX27_PAD_USBH1_FS__UART4_RTS 0x3a 0x001
+#define MX27_PAD_USBH1_FS__GPIO2_26 0x3a 0x032
+#define MX27_PAD_USBH1_OE_B__USBH1_OE_B 0x3b 0x004
+#define MX27_PAD_USBH1_OE_B__GPIO2_27 0x3b 0x032
+#define MX27_PAD_USBH1_TXDM__USBH1_TXDM 0x3c 0x004
+#define MX27_PAD_USBH1_TXDM__UART4_TXD 0x3c 0x005
+#define MX27_PAD_USBH1_TXDM__GPIO2_28 0x3c 0x032
+#define MX27_PAD_USBH1_TXDP__USBH1_TXDP 0x3d 0x004
+#define MX27_PAD_USBH1_TXDP__UART4_CTS 0x3d 0x005
+#define MX27_PAD_USBH1_TXDP__GPIO2_29 0x3d 0x032
+#define MX27_PAD_USBH1_RXDM__USBH1_RXDM 0x3e 0x004
+#define MX27_PAD_USBH1_RXDM__GPIO2_30 0x3e 0x032
+#define MX27_PAD_USBH1_RXDP__USBH1_RXDP 0x3f 0x004
+#define MX27_PAD_USBH1_RXDP__UART4_RXD 0x3f 0x001
+#define MX27_PAD_USBH1_RXDP__GPIO2_31 0x3f 0x032
+#define MX27_PAD_UNUSED4__UNUSED4 0x40 0x004
+#define MX27_PAD_UNUSED4__GPIO3_0 0x40 0x032
+#define MX27_PAD_UNUSED5__UNUSED5 0x41 0x004
+#define MX27_PAD_UNUSED5__GPIO3_1 0x41 0x032
+#define MX27_PAD_UNUSED6__UNUSED6 0x42 0x004
+#define MX27_PAD_UNUSED6__GPIO3_2 0x42 0x032
+#define MX27_PAD_UNUSED7__UNUSED7 0x43 0x004
+#define MX27_PAD_UNUSED7__GPIO3_3 0x43 0x032
+#define MX27_PAD_UNUSED8__UNUSED8 0x44 0x004
+#define MX27_PAD_UNUSED8__GPIO3_4 0x44 0x032
+#define MX27_PAD_I2C2_SDA__I2C2_SDA 0x45 0x004
+#define MX27_PAD_I2C2_SDA__GPIO3_5 0x45 0x032
+#define MX27_PAD_I2C2_SCL__I2C2_SCL 0x46 0x004
+#define MX27_PAD_I2C2_SCL__GPIO3_6 0x46 0x032
+#define MX27_PAD_USBOTG_DATA5__USBOTG_DATA5 0x47 0x004
+#define MX27_PAD_USBOTG_DATA5__GPIO3_7 0x47 0x032
+#define MX27_PAD_USBOTG_DATA6__USBOTG_DATA6 0x48 0x004
+#define MX27_PAD_USBOTG_DATA6__GPIO3_8 0x48 0x032
+#define MX27_PAD_USBOTG_DATA0__USBOTG_DATA0 0x49 0x004
+#define MX27_PAD_USBOTG_DATA0__GPIO3_9 0x49 0x032
+#define MX27_PAD_USBOTG_DATA2__USBOTG_DATA2 0x4a 0x004
+#define MX27_PAD_USBOTG_DATA2__GPIO3_10 0x4a 0x032
+#define MX27_PAD_USBOTG_DATA1__USBOTG_DATA1 0x4b 0x004
+#define MX27_PAD_USBOTG_DATA1__GPIO3_11 0x4b 0x032
+#define MX27_PAD_USBOTG_DATA4__USBOTG_DATA4 0x4c 0x004
+#define MX27_PAD_USBOTG_DATA4__GPIO3_12 0x4c 0x032
+#define MX27_PAD_USBOTG_DATA3__USBOTG_DATA3 0x4d 0x004
+#define MX27_PAD_USBOTG_DATA3__GPIO3_13 0x4d 0x032
+#define MX27_PAD_TOUT__TOUT 0x4e 0x004
+#define MX27_PAD_TOUT__GPIO3_14 0x4e 0x032
+#define MX27_PAD_TIN__TIN 0x4f 0x000
+#define MX27_PAD_TIN__GPIO3_15 0x4f 0x032
+#define MX27_PAD_SSI4_FS__SSI4_FS 0x50 0x004
+#define MX27_PAD_SSI4_FS__GPIO3_16 0x50 0x032
+#define MX27_PAD_SSI4_RXDAT__SSI4_RXDAT 0x51 0x004
+#define MX27_PAD_SSI4_RXDAT__GPIO3_17 0x51 0x032
+#define MX27_PAD_SSI4_TXDAT__SSI4_TXDAT 0x52 0x004
+#define MX27_PAD_SSI4_TXDAT__GPIO3_18 0x52 0x032
+#define MX27_PAD_SSI4_CLK__SSI4_CLK 0x53 0x004
+#define MX27_PAD_SSI4_CLK__GPIO3_19 0x53 0x032
+#define MX27_PAD_SSI1_FS__SSI1_FS 0x54 0x004
+#define MX27_PAD_SSI1_FS__GPIO3_20 0x54 0x032
+#define MX27_PAD_SSI1_RXDAT__SSI1_RXDAT 0x55 0x004
+#define MX27_PAD_SSI1_RXDAT__GPIO3_21 0x55 0x032
+#define MX27_PAD_SSI1_TXDAT__SSI1_TXDAT 0x56 0x004
+#define MX27_PAD_SSI1_TXDAT__GPIO3_22 0x56 0x032
+#define MX27_PAD_SSI1_CLK__SSI1_CLK 0x57 0x004
+#define MX27_PAD_SSI1_CLK__GPIO3_23 0x57 0x032
+#define MX27_PAD_SSI2_FS__SSI2_FS 0x58 0x004
+#define MX27_PAD_SSI2_FS__GPT5_TOUT 0x58 0x005
+#define MX27_PAD_SSI2_FS__GPIO3_24 0x58 0x032
+#define MX27_PAD_SSI2_RXDAT__SSI2_RXDAT 0x59 0x004
+#define MX27_PAD_SSI2_RXDAT__GPTS_TIN 0x59 0x001
+#define MX27_PAD_SSI2_RXDAT__GPIO3_25 0x59 0x032
+#define MX27_PAD_SSI2_TXDAT__SSI2_TXDAT 0x5a 0x004
+#define MX27_PAD_SSI2_TXDAT__GPT4_TOUT 0x5a 0x005
+#define MX27_PAD_SSI2_TXDAT__GPIO3_26 0x5a 0x032
+#define MX27_PAD_SSI2_CLK__SSI2_CLK 0x5b 0x004
+#define MX27_PAD_SSI2_CLK__GPT4_TIN 0x5b 0x001
+#define MX27_PAD_SSI2_CLK__GPIO3_27 0x5b 0x032
+#define MX27_PAD_SSI3_FS__SSI3_FS 0x5c 0x004
+#define MX27_PAD_SSI3_FS__SLCDC2_D0 0x5c 0x001
+#define MX27_PAD_SSI3_FS__GPIO3_28 0x5c 0x032
+#define MX27_PAD_SSI3_RXDAT__SSI3_RXDAT 0x5d 0x004
+#define MX27_PAD_SSI3_RXDAT__SLCDC2_RS 0x5d 0x001
+#define MX27_PAD_SSI3_RXDAT__GPIO3_29 0x5d 0x032
+#define MX27_PAD_SSI3_TXDAT__SSI3_TXDAT 0x5e 0x004
+#define MX27_PAD_SSI3_TXDAT__SLCDC2_CS 0x5e 0x001
+#define MX27_PAD_SSI3_TXDAT__GPIO3_30 0x5e 0x032
+#define MX27_PAD_SSI3_CLK__SSI3_CLK 0x5f 0x004
+#define MX27_PAD_SSI3_CLK__SLCDC2_CLK 0x5f 0x001
+#define MX27_PAD_SSI3_CLK__GPIO3_31 0x5f 0x032
+#define MX27_PAD_SD3_CMD__SD3_CMD 0x60 0x004
+#define MX27_PAD_SD3_CMD__FEC_TXD0 0x60 0x006
+#define MX27_PAD_SD3_CMD__GPIO4_0 0x60 0x032
+#define MX27_PAD_SD3_CLK__SD3_CLK 0x61 0x004
+#define MX27_PAD_SD3_CLK__ETMTRACEPKT15 0x61 0x005
+#define MX27_PAD_SD3_CLK__FEC_TXD1 0x61 0x006
+#define MX27_PAD_SD3_CLK__GPIO4_1 0x61 0x032
+#define MX27_PAD_ATA_DATA0__ATA_DATA0 0x62 0x004
+#define MX27_PAD_ATA_DATA0__SD3_D0 0x62 0x005
+#define MX27_PAD_ATA_DATA0__FEC_TXD2 0x62 0x006
+#define MX27_PAD_ATA_DATA0__GPIO4_2 0x62 0x032
+#define MX27_PAD_ATA_DATA1__ATA_DATA1 0x63 0x004
+#define MX27_PAD_ATA_DATA1__SD3_D1 0x63 0x005
+#define MX27_PAD_ATA_DATA1__FEC_TXD3 0x63 0x006
+#define MX27_PAD_ATA_DATA1__GPIO4_3 0x63 0x032
+#define MX27_PAD_ATA_DATA2__ATA_DATA2 0x64 0x004
+#define MX27_PAD_ATA_DATA2__SD3_D2 0x64 0x005
+#define MX27_PAD_ATA_DATA2__FEC_RX_ER 0x64 0x002
+#define MX27_PAD_ATA_DATA2__GPIO4_4 0x64 0x032
+#define MX27_PAD_ATA_DATA3__ATA_DATA3 0x65 0x004
+#define MX27_PAD_ATA_DATA3__SD3_D3 0x65 0x005
+#define MX27_PAD_ATA_DATA3__FEC_RXD1 0x65 0x002
+#define MX27_PAD_ATA_DATA3__GPIO4_5 0x65 0x032
+#define MX27_PAD_ATA_DATA4__ATA_DATA4 0x66 0x004
+#define MX27_PAD_ATA_DATA4__ETMTRACEPKT14 0x66 0x005
+#define MX27_PAD_ATA_DATA4__FEC_RXD2 0x66 0x002
+#define MX27_PAD_ATA_DATA4__GPIO4_6 0x66 0x032
+#define MX27_PAD_ATA_DATA5__ATA_DATA5 0x67 0x004
+#define MX27_PAD_ATA_DATA5__ETMTRACEPKT13 0x67 0x005
+#define MX27_PAD_ATA_DATA5__FEC_RXD3 0x67 0x002
+#define MX27_PAD_ATA_DATA5__GPIO4_7 0x67 0x032
+#define MX27_PAD_ATA_DATA6__ATA_DATA6 0x68 0x004
+#define MX27_PAD_ATA_DATA6__FEC_MDIO 0x68 0x005
+#define MX27_PAD_ATA_DATA6__GPIO4_8 0x68 0x032
+#define MX27_PAD_ATA_DATA7__ATA_DATA7 0x69 0x004
+#define MX27_PAD_ATA_DATA7__ETMTRACEPKT12 0x69 0x005
+#define MX27_PAD_ATA_DATA7__FEC_MDC 0x69 0x006
+#define MX27_PAD_ATA_DATA7__GPIO4_9 0x69 0x032
+#define MX27_PAD_ATA_DATA8__ATA_DATA8 0x6a 0x004
+#define MX27_PAD_ATA_DATA8__ETMTRACEPKT11 0x6a 0x005
+#define MX27_PAD_ATA_DATA8__FEC_CRS 0x6a 0x002
+#define MX27_PAD_ATA_DATA8__GPIO4_10 0x6a 0x032
+#define MX27_PAD_ATA_DATA9__ATA_DATA9 0x6b 0x004
+#define MX27_PAD_ATA_DATA9__ETMTRACEPKT10 0x6b 0x005
+#define MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x6b 0x002
+#define MX27_PAD_ATA_DATA9__GPIO4_11 0x6b 0x032
+#define MX27_PAD_ATA_DATA10__ATA_DATA10 0x6c 0x004
+#define MX27_PAD_ATA_DATA10__ETMTRACEPKT9 0x6c 0x005
+#define MX27_PAD_ATA_DATA10__FEC_RXD0 0x6c 0x002
+#define MX27_PAD_ATA_DATA10__GPIO4_12 0x6c 0x032
+#define MX27_PAD_ATA_DATA11__ATA_DATA11 0x6d 0x004
+#define MX27_PAD_ATA_DATA11__ETMTRACEPKT8 0x6d 0x005
+#define MX27_PAD_ATA_DATA11__FEC_RX_DV 0x6d 0x002
+#define MX27_PAD_ATA_DATA11__GPIO4_13 0x6d 0x032
+#define MX27_PAD_ATA_DATA12__ATA_DATA12 0x6e 0x004
+#define MX27_PAD_ATA_DATA12__ETMTRACEPKT7 0x6e 0x005
+#define MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x6e 0x002
+#define MX27_PAD_ATA_DATA12__GPIO4_14 0x6e 0x032
+#define MX27_PAD_ATA_DATA13__ATA_DATA13 0x6f 0x004
+#define MX27_PAD_ATA_DATA13__ETMTRACEPKT6 0x6f 0x005
+#define MX27_PAD_ATA_DATA13__FEC_COL 0x6f 0x002
+#define MX27_PAD_ATA_DATA13__GPIO4_15 0x6f 0x032
+#define MX27_PAD_ATA_DATA14__ATA_DATA14 0x70 0x004
+#define MX27_PAD_ATA_DATA14__ETMTRACEPKT5 0x70 0x005
+#define MX27_PAD_ATA_DATA14__FEC_TX_ER 0x70 0x006
+#define MX27_PAD_ATA_DATA14__GPIO4_16 0x70 0x032
+#define MX27_PAD_I2C_DATA__I2C_DATA 0x71 0x004
+#define MX27_PAD_I2C_DATA__GPIO4_17 0x71 0x032
+#define MX27_PAD_I2C_CLK__I2C_CLK 0x72 0x004
+#define MX27_PAD_I2C_CLK__GPIO4_18 0x72 0x032
+#define MX27_PAD_CSPI2_SS2__CSPI2_SS2 0x73 0x004
+#define MX27_PAD_CSPI2_SS2__USBH2_DATA4 0x73 0x005
+#define MX27_PAD_CSPI2_SS2__GPIO4_19 0x73 0x032
+#define MX27_PAD_CSPI2_SS1__CSPI2_SS1 0x74 0x004
+#define MX27_PAD_CSPI2_SS1__USBH2_DATA3 0x74 0x005
+#define MX27_PAD_CSPI2_SS1__GPIO4_20 0x74 0x032
+#define MX27_PAD_CSPI2_SS0__CSPI2_SS0 0x75 0x004
+#define MX27_PAD_CSPI2_SS0__USBH2_DATA6 0x75 0x005
+#define MX27_PAD_CSPI2_SS0__GPIO4_21 0x75 0x032
+#define MX27_PAD_CSPI2_SCLK__CSPI2_SCLK 0x76 0x004
+#define MX27_PAD_CSPI2_SCLK__USBH2_DATA0 0x76 0x005
+#define MX27_PAD_CSPI2_SCLK__GPIO4_22 0x76 0x032
+#define MX27_PAD_CSPI2_MISO__CSPI2_MISO 0x77 0x004
+#define MX27_PAD_CSPI2_MISO__USBH2_DATA2 0x77 0x005
+#define MX27_PAD_CSPI2_MISO__GPIO4_23 0x77 0x032
+#define MX27_PAD_CSPI2_MOSI__CSPI2_MOSI 0x78 0x004
+#define MX27_PAD_CSPI2_MOSI__USBH2_DATA1 0x78 0x005
+#define MX27_PAD_CSPI2_MOSI__GPIO4_24 0x78 0x032
+#define MX27_PAD_CSPI1_RDY__CSPI1_RDY 0x79 0x000
+#define MX27_PAD_CSPI1_RDY__GPIO4_25 0x79 0x032
+#define MX27_PAD_CSPI1_SS2__CSPI1_SS2 0x7a 0x004
+#define MX27_PAD_CSPI1_SS2__USBH2_DATA5 0x7a 0x005
+#define MX27_PAD_CSPI1_SS2__GPIO4_26 0x7a 0x032
+#define MX27_PAD_CSPI1_SS1__CSPI1_SS1 0x7b 0x004
+#define MX27_PAD_CSPI1_SS1__GPIO4_27 0x7b 0x032
+#define MX27_PAD_CSPI1_SS0__CSPI1_SS0 0x7c 0x004
+#define MX27_PAD_CSPI1_SS0__GPIO4_28 0x7c 0x032
+#define MX27_PAD_CSPI1_SCLK__CSPI1_SCLK 0x7d 0x004
+#define MX27_PAD_CSPI1_SCLK__GPIO4_29 0x7d 0x032
+#define MX27_PAD_CSPI1_MISO__CSPI1_MISO 0x7e 0x004
+#define MX27_PAD_CSPI1_MISO__GPIO4_30 0x7e 0x032
+#define MX27_PAD_CSPI1_MOSI__CSPI1_MOSI 0x7f 0x004
+#define MX27_PAD_CSPI1_MOSI__GPIO4_31 0x7f 0x032
+#define MX27_PAD_USBOTG_NXT__USBOTG_NXT 0x80 0x000
+#define MX27_PAD_USBOTG_NXT__KP_COL6A 0x80 0x005
+#define MX27_PAD_USBOTG_NXT__GPIO5_0 0x80 0x032
+#define MX27_PAD_USBOTG_STP__USBOTG_STP 0x81 0x004
+#define MX27_PAD_USBOTG_STP__KP_ROW6A 0x81 0x005
+#define MX27_PAD_USBOTG_STP__GPIO5_1 0x81 0x032
+#define MX27_PAD_USBOTG_DIR__USBOTG_DIR 0x82 0x000
+#define MX27_PAD_USBOTG_DIR__KP_ROW7A 0x82 0x005
+#define MX27_PAD_USBOTG_DIR__GPIO5_2 0x82 0x032
+#define MX27_PAD_UART2_CTS__UART2_CTS 0x83 0x004
+#define MX27_PAD_UART2_CTS__KP_COL7 0x83 0x005
+#define MX27_PAD_UART2_CTS__GPIO5_3 0x83 0x032
+#define MX27_PAD_UART2_RTS__UART2_RTS 0x84 0x000
+#define MX27_PAD_UART2_RTS__KP_ROW7 0x84 0x005
+#define MX27_PAD_UART2_RTS__GPIO5_4 0x84 0x032
+#define MX27_PAD_PWMO__PWMO 0x85 0x004
+#define MX27_PAD_PWMO__GPIO5_5 0x85 0x032
+#define MX27_PAD_UART2_TXD__UART2_TXD 0x86 0x004
+#define MX27_PAD_UART2_TXD__KP_COL6 0x86 0x005
+#define MX27_PAD_UART2_TXD__GPIO5_6 0x86 0x032
+#define MX27_PAD_UART2_RXD__UART2_RXD 0x87 0x000
+#define MX27_PAD_UART2_RXD__KP_ROW6 0x87 0x005
+#define MX27_PAD_UART2_RXD__GPIO5_7 0x87 0x032
+#define MX27_PAD_UART3_TXD__UART3_TXD 0x88 0x004
+#define MX27_PAD_UART3_TXD__GPIO5_8 0x88 0x032
+#define MX27_PAD_UART3_RXD__UART3_RXD 0x89 0x000
+#define MX27_PAD_UART3_RXD__GPIO5_9 0x89 0x032
+#define MX27_PAD_UART3_CTS__UART3_CTS 0x8a 0x004
+#define MX27_PAD_UART3_CTS__GPIO5_10 0x8a 0x032
+#define MX27_PAD_UART3_RTS__UART3_RTS 0x8b 0x000
+#define MX27_PAD_UART3_RTS__GPIO5_11 0x8b 0x032
+#define MX27_PAD_UART1_TXD__UART1_TXD 0x8c 0x004
+#define MX27_PAD_UART1_TXD__GPIO5_12 0x8c 0x032
+#define MX27_PAD_UART1_RXD__UART1_RXD 0x8d 0x000
+#define MX27_PAD_UART1_RXD__GPIO5_13 0x8d 0x032
+#define MX27_PAD_UART1_CTS__UART1_CTS 0x8e 0x004
+#define MX27_PAD_UART1_CTS__GPIO5_14 0x8e 0x032
+#define MX27_PAD_UART1_RTS__UART1_RTS 0x8f 0x000
+#define MX27_PAD_UART1_RTS__GPIO5_15 0x8f 0x032
+#define MX27_PAD_RTCK__RTCK 0x90 0x004
+#define MX27_PAD_RTCK__OWIRE 0x90 0x005
+#define MX27_PAD_RTCK__GPIO5_16 0x90 0x032
+#define MX27_PAD_RESET_OUT_B__RESET_OUT_B 0x91 0x004
+#define MX27_PAD_RESET_OUT_B__GPIO5_17 0x91 0x032
+#define MX27_PAD_SD1_D0__SD1_D0 0x92 0x004
+#define MX27_PAD_SD1_D0__CSPI3_MISO 0x92 0x001
+#define MX27_PAD_SD1_D0__GPIO5_18 0x92 0x032
+#define MX27_PAD_SD1_D1__SD1_D1 0x93 0x004
+#define MX27_PAD_SD1_D1__GPIO5_19 0x93 0x032
+#define MX27_PAD_SD1_D2__SD1_D2 0x94 0x004
+#define MX27_PAD_SD1_D2__GPIO5_20 0x94 0x032
+#define MX27_PAD_SD1_D3__SD1_D3 0x95 0x004
+#define MX27_PAD_SD1_D3__CSPI3_SS 0x95 0x005
+#define MX27_PAD_SD1_D3__GPIO5_21 0x95 0x032
+#define MX27_PAD_SD1_CMD__SD1_CMD 0x96 0x004
+#define MX27_PAD_SD1_CMD__CSPI3_MOSI 0x96 0x005
+#define MX27_PAD_SD1_CMD__GPIO5_22 0x96 0x032
+#define MX27_PAD_SD1_CLK__SD1_CLK 0x97 0x004
+#define MX27_PAD_SD1_CLK__CSPI3_SCLK 0x97 0x005
+#define MX27_PAD_SD1_CLK__GPIO5_23 0x97 0x032
+#define MX27_PAD_USBOTG_CLK__USBOTG_CLK 0x98 0x000
+#define MX27_PAD_USBOTG_CLK__GPIO5_24 0x98 0x032
+#define MX27_PAD_USBOTG_DATA7__USBOTG_DATA7 0x99 0x004
+#define MX27_PAD_USBOTG_DATA7__GPIO5_25 0x99 0x032
+#define MX27_PAD_UNUSED9__UNUSED9 0x9a 0x004
+#define MX27_PAD_UNUSED9__GPIO5_26 0x9a 0x032
+#define MX27_PAD_UNUSED10__UNUSED10 0x9b 0x004
+#define MX27_PAD_UNUSED10__GPIO5_27 0x9b 0x032
+#define MX27_PAD_UNUSED11__UNUSED11 0x9c 0x004
+#define MX27_PAD_UNUSED11__GPIO5_28 0x9c 0x032
+#define MX27_PAD_UNUSED12__UNUSED12 0x9d 0x004
+#define MX27_PAD_UNUSED12__GPIO5_29 0x9d 0x032
+#define MX27_PAD_UNUSED13__UNUSED13 0x9e 0x004
+#define MX27_PAD_UNUSED13__GPIO5_30 0x9e 0x032
+#define MX27_PAD_UNUSED14__UNUSED14 0x9f 0x004
+#define MX27_PAD_UNUSED14__GPIO5_31 0x9f 0x032
+#define MX27_PAD_NFRB__NFRB 0xa0 0x000
+#define MX27_PAD_NFRB__ETMTRACEPKT3 0xa0 0x005
+#define MX27_PAD_NFRB__GPIO6_0 0xa0 0x032
+#define MX27_PAD_NFCLE__NFCLE 0xa1 0x004
+#define MX27_PAD_NFCLE__ETMTRACEPKT0 0xa1 0x005
+#define MX27_PAD_NFCLE__GPIO6_1 0xa1 0x032
+#define MX27_PAD_NFWP_B__NFWP_B 0xa2 0x004
+#define MX27_PAD_NFWP_B__ETMTRACEPKT1 0xa2 0x005
+#define MX27_PAD_NFWP_B__GPIO6_2 0xa2 0x032
+#define MX27_PAD_NFCE_B__NFCE_B 0xa3 0x004
+#define MX27_PAD_NFCE_B__ETMTRACEPKT2 0xa3 0x005
+#define MX27_PAD_NFCE_B__GPIO6_3 0xa3 0x032
+#define MX27_PAD_NFALE__NFALE 0xa4 0x004
+#define MX27_PAD_NFALE__ETMPIPESTAT0 0xa4 0x005
+#define MX27_PAD_NFALE__GPIO6_4 0xa4 0x032
+#define MX27_PAD_NFRE_B__NFRE_B 0xa5 0x004
+#define MX27_PAD_NFRE_B__ETMPIPESTAT1 0xa5 0x005
+#define MX27_PAD_NFRE_B__GPIO6_5 0xa5 0x032
+#define MX27_PAD_NFWE_B__NFWE_B 0xa6 0x004
+#define MX27_PAD_NFWE_B__ETMPIPESTAT2 0xa6 0x005
+#define MX27_PAD_NFWE_B__GPIO6_6 0xa6 0x032
+#define MX27_PAD_PC_POE__PC_POE 0xa7 0x004
+#define MX27_PAD_PC_POE__ATA_BUFFER_EN 0xa7 0x005
+#define MX27_PAD_PC_POE__GPIO6_7 0xa7 0x032
+#define MX27_PAD_PC_RW_B__PC_RW_B 0xa8 0x004
+#define MX27_PAD_PC_RW_B__ATA_IORDY 0xa8 0x001
+#define MX27_PAD_PC_RW_B__GPIO6_8 0xa8 0x032
+#define MX27_PAD_IOIS16__IOIS16 0xa9 0x000
+#define MX27_PAD_IOIS16__ATA_INTRQ 0xa9 0x001
+#define MX27_PAD_IOIS16__GPIO6_9 0xa9 0x032
+#define MX27_PAD_PC_RST__PC_RST 0xaa 0x004
+#define MX27_PAD_PC_RST__ATA_RESET_B 0xaa 0x005
+#define MX27_PAD_PC_RST__GPIO6_10 0xaa 0x032
+#define MX27_PAD_PC_BVD2__PC_BVD2 0xab 0x000
+#define MX27_PAD_PC_BVD2__ATA_DMACK 0xab 0x005
+#define MX27_PAD_PC_BVD2__GPIO6_11 0xab 0x032
+#define MX27_PAD_PC_BVD1__PC_BVD1 0xac 0x000
+#define MX27_PAD_PC_BVD1__ATA_DMARQ 0xac 0x001
+#define MX27_PAD_PC_BVD1__GPIO6_12 0xac 0x032
+#define MX27_PAD_PC_VS2__PC_VS2 0xad 0x000
+#define MX27_PAD_PC_VS2__ATA_DA0 0xad 0x005
+#define MX27_PAD_PC_VS2__GPIO6_13 0xad 0x032
+#define MX27_PAD_PC_VS1__PC_VS1 0xae 0x000
+#define MX27_PAD_PC_VS1__ATA_DA1 0xae 0x005
+#define MX27_PAD_PC_VS1__GPIO6_14 0xae 0x032
+#define MX27_PAD_CLKO__CLKO 0xaf 0x004
+#define MX27_PAD_CLKO__GPIO6_15 0xaf 0x032
+#define MX27_PAD_PC_PWRON__PC_PWRON 0xb0 0x000
+#define MX27_PAD_PC_PWRON__ATA_DA2 0xb0 0x005
+#define MX27_PAD_PC_PWRON__GPIO6_16 0xb0 0x032
+#define MX27_PAD_PC_READY__PC_READY 0xb1 0x000
+#define MX27_PAD_PC_READY__ATA_CS0 0xb1 0x005
+#define MX27_PAD_PC_READY__GPIO6_17 0xb1 0x032
+#define MX27_PAD_PC_WAIT_B__PC_WAIT_B 0xb2 0x000
+#define MX27_PAD_PC_WAIT_B__ATA_CS1 0xb2 0x005
+#define MX27_PAD_PC_WAIT_B__GPIO6_18 0xb2 0x032
+#define MX27_PAD_PC_CD2_B__PC_CD2_B 0xb3 0x000
+#define MX27_PAD_PC_CD2_B__ATA_DIOW 0xb3 0x005
+#define MX27_PAD_PC_CD2_B__GPIO6_19 0xb3 0x032
+#define MX27_PAD_PC_CD1_B__PC_CD1_B 0xb4 0x000
+#define MX27_PAD_PC_CD1_B__ATA_DIOR 0xb4 0x005
+#define MX27_PAD_PC_CD1_B__GPIO6_20 0xb4 0x032
+#define MX27_PAD_CS4_B__CS4_B 0xb5 0x004
+#define MX27_PAD_CS4_B__ETMTRACESYNC 0xb5 0x005
+#define MX27_PAD_CS4_B__GPIO6_21 0xb5 0x032
+#define MX27_PAD_CS5_B__CS5_B 0xb6 0x004
+#define MX27_PAD_CS5_B__ETMTRACECLK 0xb6 0x005
+#define MX27_PAD_CS5_B__GPIO6_22 0xb6 0x032
+#define MX27_PAD_ATA_DATA15__ATA_DATA15 0xb7 0x004
+#define MX27_PAD_ATA_DATA15__ETMTRACEPKT4 0xb7 0x005
+#define MX27_PAD_ATA_DATA15__FEC_TX_EN 0xb7 0x006
+#define MX27_PAD_ATA_DATA15__GPIO6_23 0xb7 0x032
+#define MX27_PAD_UNUSED15__UNUSED15 0xb8 0x004
+#define MX27_PAD_UNUSED15__GPIO6_24 0xb8 0x032
+#define MX27_PAD_UNUSED16__UNUSED16 0xb9 0x004
+#define MX27_PAD_UNUSED16__GPIO6_25 0xb9 0x032
+#define MX27_PAD_UNUSED17__UNUSED17 0xba 0x004
+#define MX27_PAD_UNUSED17__GPIO6_26 0xba 0x032
+#define MX27_PAD_UNUSED18__UNUSED18 0xbb 0x004
+#define MX27_PAD_UNUSED18__GPIO6_27 0xbb 0x032
+#define MX27_PAD_UNUSED19__UNUSED19 0xbc 0x004
+#define MX27_PAD_UNUSED19__GPIO6_28 0xbc 0x032
+#define MX27_PAD_UNUSED20__UNUSED20 0xbd 0x004
+#define MX27_PAD_UNUSED20__GPIO6_29 0xbd 0x032
+#define MX27_PAD_UNUSED21__UNUSED21 0xbe 0x004
+#define MX27_PAD_UNUSED21__GPIO6_30 0xbe 0x032
+#define MX27_PAD_UNUSED22__UNUSED22 0xbf 0x004
+#define MX27_PAD_UNUSED22__GPIO6_31 0xbf 0x032
+
+#endif /* __DTS_IMX27_PINFUNC_H */
diff --git a/arch/arm/boot/dts/imx27.dtsi b/arch/arm/boot/dts/imx27.dtsi
index 826231eb4446..6279e0b4f768 100644
--- a/arch/arm/boot/dts/imx27.dtsi
+++ b/arch/arm/boot/dts/imx27.dtsi
@@ -10,6 +10,9 @@
*/
#include "skeleton.dtsi"
+#include "imx27-pinfunc.h"
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/gpio/gpio.h>
/ {
aliases {
@@ -67,6 +70,26 @@
};
};
+ usbphy {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ usbphy0: usbphy@0 {
+ compatible = "usb-nop-xceiv";
+ reg = <0>;
+ clocks = <&clks 75>;
+ clock-names = "main_clk";
+ };
+
+ usbphy2: usbphy@2 {
+ compatible = "usb-nop-xceiv";
+ reg = <2>;
+ clocks = <&clks 75>;
+ clock-names = "main_clk";
+ };
+ };
+
soc {
#address-cells = <1>;
#size-cells = <1>;
@@ -204,6 +227,30 @@
status = "disabled";
};
+ ssi1: ssi@10010000 {
+ #sound-dai-cells = <0>;
+ compatible = "fsl,imx27-ssi", "fsl,imx21-ssi";
+ reg = <0x10010000 0x1000>;
+ interrupts = <14>;
+ clocks = <&clks 26>;
+ dmas = <&dma 12>, <&dma 13>, <&dma 14>, <&dma 15>;
+ dma-names = "rx0", "tx0", "rx1", "tx1";
+ fsl,fifo-depth = <8>;
+ status = "disabled";
+ };
+
+ ssi2: ssi@10011000 {
+ #sound-dai-cells = <0>;
+ compatible = "fsl,imx27-ssi", "fsl,imx21-ssi";
+ reg = <0x10011000 0x1000>;
+ interrupts = <13>;
+ clocks = <&clks 25>;
+ dmas = <&dma 8>, <&dma 9>, <&dma 10>, <&dma 11>;
+ dma-names = "rx0", "tx0", "rx1", "tx1";
+ fsl,fifo-depth = <8>;
+ status = "disabled";
+ };
+
i2c1: i2c@10012000 {
#address-cells = <1>;
#size-cells = <0>;
@@ -236,64 +283,72 @@
status = "disabled";
};
- gpio1: gpio@10015000 {
- compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
- reg = <0x10015000 0x100>;
- interrupts = <8>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupt-cells = <2>;
- };
-
- gpio2: gpio@10015100 {
- compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
- reg = <0x10015100 0x100>;
- interrupts = <8>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupt-cells = <2>;
- };
-
- gpio3: gpio@10015200 {
- compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
- reg = <0x10015200 0x100>;
- interrupts = <8>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupt-cells = <2>;
- };
-
- gpio4: gpio@10015300 {
- compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
- reg = <0x10015300 0x100>;
- interrupts = <8>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupt-cells = <2>;
- };
-
- gpio5: gpio@10015400 {
- compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
- reg = <0x10015400 0x100>;
- interrupts = <8>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupt-cells = <2>;
- };
-
- gpio6: gpio@10015500 {
- compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
- reg = <0x10015500 0x100>;
- interrupts = <8>;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupt-cells = <2>;
+ iomuxc: iomuxc@10015000 {
+ compatible = "fsl,imx27-iomuxc";
+ reg = <0x10015000 0x600>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ gpio1: gpio@10015000 {
+ compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
+ reg = <0x10015000 0x100>;
+ interrupts = <8>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio2: gpio@10015100 {
+ compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
+ reg = <0x10015100 0x100>;
+ interrupts = <8>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio3: gpio@10015200 {
+ compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
+ reg = <0x10015200 0x100>;
+ interrupts = <8>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio4: gpio@10015300 {
+ compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
+ reg = <0x10015300 0x100>;
+ interrupts = <8>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio5: gpio@10015400 {
+ compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
+ reg = <0x10015400 0x100>;
+ interrupts = <8>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio6: gpio@10015500 {
+ compatible = "fsl,imx27-gpio", "fsl,imx21-gpio";
+ reg = <0x10015500 0x100>;
+ interrupts = <8>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
};
audmux: audmux@10016000 {
@@ -404,6 +459,42 @@
iram = <&iram>;
};
+ usbotg: usb@10024000 {
+ compatible = "fsl,imx27-usb";
+ reg = <0x10024000 0x200>;
+ interrupts = <56>;
+ clocks = <&clks 15>;
+ fsl,usbmisc = <&usbmisc 0>;
+ fsl,usbphy = <&usbphy0>;
+ status = "disabled";
+ };
+
+ usbh1: usb@10024200 {
+ compatible = "fsl,imx27-usb";
+ reg = <0x10024200 0x200>;
+ interrupts = <54>;
+ clocks = <&clks 15>;
+ fsl,usbmisc = <&usbmisc 1>;
+ status = "disabled";
+ };
+
+ usbh2: usb@10024400 {
+ compatible = "fsl,imx27-usb";
+ reg = <0x10024400 0x200>;
+ interrupts = <55>;
+ clocks = <&clks 15>;
+ fsl,usbmisc = <&usbmisc 2>;
+ fsl,usbphy = <&usbphy2>;
+ status = "disabled";
+ };
+
+ usbmisc: usbmisc@10024600 {
+ #index-cells = <1>;
+ compatible = "fsl,imx27-usbmisc";
+ reg = <0x10024600 0x200>;
+ clocks = <&clks 62>;
+ };
+
sahara2: sahara@10025000 {
compatible = "fsl,imx27-sahara";
reg = <0x10025000 0x1000>;
diff --git a/arch/arm/boot/dts/imx28-apf28dev.dts b/arch/arm/boot/dts/imx28-apf28dev.dts
index e2efd8d89c4f..221cac4fb2cd 100644
--- a/arch/arm/boot/dts/imx28-apf28dev.dts
+++ b/arch/arm/boot/dts/imx28-apf28dev.dts
@@ -48,6 +48,7 @@
MX28_PAD_LCD_D20__GPIO_1_20
MX28_PAD_LCD_D21__GPIO_1_21
MX28_PAD_LCD_D22__GPIO_1_22
+ MX28_PAD_GPMI_CE1N__GPIO_0_17
>;
fsl,drive-strength = <MXS_DRIVE_4mA>;
fsl,voltage = <MXS_VOLTAGE_HIGH>;
@@ -66,6 +67,16 @@
fsl,voltage = <MXS_VOLTAGE_HIGH>;
fsl,pull-up = <MXS_PULL_DISABLE>;
};
+
+ usb0_otg_apf28dev: otg-apf28dev@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_LCD_D23__GPIO_1_23
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
};
lcdif@80030000 {
@@ -131,6 +142,8 @@
ahb@80080000 {
usb0: usb@80080000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb0_otg_apf28dev>;
vbus-supply = <&reg_usb0_vbus>;
status = "okay";
};
@@ -150,13 +163,17 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_usb0_vbus: usb0_vbus {
+ reg_usb0_vbus: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "usb0_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
gpio = <&gpio1 23 1>;
+ enable-active-high;
};
};
@@ -177,4 +194,14 @@
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <6>;
};
+
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ user-button {
+ label = "User button";
+ gpios = <&gpio0 17 0>;
+ linux,code = <0x100>;
+ };
+ };
};
diff --git a/arch/arm/boot/dts/imx28-apx4devkit.dts b/arch/arm/boot/dts/imx28-apx4devkit.dts
index 6f254ca816cb..e1ce9179db63 100644
--- a/arch/arm/boot/dts/imx28-apx4devkit.dts
+++ b/arch/arm/boot/dts/imx28-apx4devkit.dts
@@ -193,9 +193,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_3p3v: 3p3v {
+ reg_3p3v: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "3P3V";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
diff --git a/arch/arm/boot/dts/imx28-cfa10036.dts b/arch/arm/boot/dts/imx28-cfa10036.dts
index cabb6171a19d..ae7c3390e65a 100644
--- a/arch/arm/boot/dts/imx28-cfa10036.dts
+++ b/arch/arm/boot/dts/imx28-cfa10036.dts
@@ -100,6 +100,8 @@
usb0: usb@80080000 {
pinctrl-names = "default";
pinctrl-0 = <&usb0_otg_cfa10036>;
+ dr_mode = "peripheral";
+ phy_type = "utmi";
status = "okay";
};
};
diff --git a/arch/arm/boot/dts/imx28-cfa10037.dts b/arch/arm/boot/dts/imx28-cfa10037.dts
index f93e9a700e52..e5beaa58bb40 100644
--- a/arch/arm/boot/dts/imx28-cfa10037.dts
+++ b/arch/arm/boot/dts/imx28-cfa10037.dts
@@ -54,7 +54,7 @@
ahb@80080000 {
usb1: usb@80090000 {
vbus-supply = <&reg_usb1_vbus>;
- pinctrl-0 = <&usbphy1_pins_a>;
+ pinctrl-0 = <&usb1_pins_a>;
pinctrl-names = "default";
status = "okay";
};
@@ -72,9 +72,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_usb1_vbus: usb1_vbus {
+ reg_usb1_vbus: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
pinctrl-names = "default";
pinctrl-0 = <&usb_pins_cfa10037>;
regulator-name = "usb1_vbus";
diff --git a/arch/arm/boot/dts/imx28-cfa10049.dts b/arch/arm/boot/dts/imx28-cfa10049.dts
index 7087b4bf6a8f..7d51459de5e8 100644
--- a/arch/arm/boot/dts/imx28-cfa10049.dts
+++ b/arch/arm/boot/dts/imx28-cfa10049.dts
@@ -229,15 +229,39 @@
i2c-parent = <&i2c1>;
i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
reg = <0>;
+
+ adc0: nau7802@2a {
+ compatible = "nuvoton,nau7802";
+ reg = <0x2a>;
+ nuvoton,vldo = <3000>;
+ };
};
i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
reg = <1>;
+
+ adc1: nau7802@2a {
+ compatible = "nuvoton,nau7802";
+ reg = <0x2a>;
+ nuvoton,vldo = <3000>;
+ };
};
i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
reg = <2>;
+
+ adc2: nau7802@2a {
+ compatible = "nuvoton,nau7802";
+ reg = <0x2a>;
+ nuvoton,vldo = <3000>;
+ };
};
i2c@3 {
@@ -274,7 +298,7 @@
ahb@80080000 {
usb1: usb@80090000 {
vbus-supply = <&reg_usb1_vbus>;
- pinctrl-0 = <&usbphy1_pins_a>;
+ pinctrl-0 = <&usb1_pins_a>;
pinctrl-names = "default";
status = "okay";
};
@@ -282,9 +306,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_usb1_vbus: usb1_vbus {
+ reg_usb1_vbus: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
pinctrl-names = "default";
pinctrl-0 = <&usb_pins_cfa10049>;
regulator-name = "usb1_vbus";
diff --git a/arch/arm/boot/dts/imx28-cfa10057.dts b/arch/arm/boot/dts/imx28-cfa10057.dts
index 3c1312885ae0..c4e00ce4b6da 100644
--- a/arch/arm/boot/dts/imx28-cfa10057.dts
+++ b/arch/arm/boot/dts/imx28-cfa10057.dts
@@ -134,7 +134,7 @@
ahb@80080000 {
usb1: usb@80090000 {
vbus-supply = <&reg_usb1_vbus>;
- pinctrl-0 = <&usbphy1_pins_a>;
+ pinctrl-0 = <&usb1_pins_a>;
pinctrl-names = "default";
status = "okay";
};
@@ -142,9 +142,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_usb1_vbus: usb1_vbus {
+ reg_usb1_vbus: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
pinctrl-names = "default";
pinctrl-0 = <&usb_pins_cfa10057>;
regulator-name = "usb1_vbus";
diff --git a/arch/arm/boot/dts/imx28-cfa10058.dts b/arch/arm/boot/dts/imx28-cfa10058.dts
index 2469d34df0ae..7c9cc783f0d1 100644
--- a/arch/arm/boot/dts/imx28-cfa10058.dts
+++ b/arch/arm/boot/dts/imx28-cfa10058.dts
@@ -101,7 +101,7 @@
ahb@80080000 {
usb1: usb@80090000 {
vbus-supply = <&reg_usb1_vbus>;
- pinctrl-0 = <&usbphy1_pins_a>;
+ pinctrl-0 = <&usb1_pins_a>;
pinctrl-names = "default";
status = "okay";
};
@@ -109,11 +109,14 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_usb1_vbus: usb1_vbus {
+ reg_usb1_vbus: regulator@0 {
pinctrl-names = "default";
pinctrl-0 = <&usb_pins_cfa10058>;
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "usb1_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
diff --git a/arch/arm/boot/dts/imx28-duckbill.dts b/arch/arm/boot/dts/imx28-duckbill.dts
new file mode 100644
index 000000000000..5f326c1c1850
--- /dev/null
+++ b/arch/arm/boot/dts/imx28-duckbill.dts
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013 Michael Heimpold <mhei@heimpold.de>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx28.dtsi"
+
+/ {
+ model = "I2SE Duckbill";
+ compatible = "i2se,duckbill", "fsl,imx28";
+
+ memory {
+ reg = <0x40000000 0x08000000>;
+ };
+
+ apb@80000000 {
+ apbh@80000000 {
+ ssp0: ssp@80010000 {
+ compatible = "fsl,imx28-mmc";
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc0_8bit_pins_a
+ &mmc0_cd_cfg &mmc0_sck_cfg>;
+ bus-width = <8>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+ };
+
+ pinctrl@80018000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&hog_pins_a>;
+
+ hog_pins_a: hog@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_ENET0_RX_CLK__GPIO_4_13 /* PHY Reset */
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+
+ led_pins_a: led_gpio@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_AUART1_RX__GPIO_3_4
+ MX28_PAD_AUART1_TX__GPIO_3_5
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+ };
+ };
+
+ apbx@80040000 {
+ duart: serial@80074000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&duart_pins_a>;
+ status = "okay";
+ };
+
+ usbphy0: usbphy@8007c000 {
+ status = "okay";
+ };
+ };
+ };
+
+ ahb@80080000 {
+ usb0: usb@80080000 {
+ status = "okay";
+ };
+
+ mac0: ethernet@800f0000 {
+ phy-mode = "rmii";
+ pinctrl-names = "default";
+ pinctrl-0 = <&mac0_pins_a>;
+ phy-supply = <&reg_3p3v>;
+ phy-reset-gpios = <&gpio4 13 0>;
+ phy-reset-duration = <100>;
+ status = "okay";
+ };
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_3p3v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pins_a>;
+
+ status {
+ label = "duckbill:green:status";
+ gpios = <&gpio3 5 0>;
+ };
+
+ failure {
+ label = "duckbill:red:status";
+ gpios = <&gpio3 4 0>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/imx28-eukrea-mbmx283lc.dts b/arch/arm/boot/dts/imx28-eukrea-mbmx283lc.dts
new file mode 100644
index 000000000000..7c1572c5a4fb
--- /dev/null
+++ b/arch/arm/boot/dts/imx28-eukrea-mbmx283lc.dts
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2013 Eukréa Electromatique <eric@eukrea.com>
+ * Copyright 2013 Eukréa Electromatique <denis@eukrea.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.
+ */
+
+/*
+ * Module contains : i.MX282 + 64MB DDR2 + NAND + Ethernet PHY + RTC
+ */
+
+/dts-v1/;
+#include "imx28-eukrea-mbmx28lc.dtsi"
+
+/ {
+ model = "Eukrea Electromatique MBMX283LC";
+ compatible = "eukrea,mbmx283lc", "eukrea,mbmx28lc", "fsl,imx28";
+
+ memory {
+ reg = <0x40000000 0x04000000>;
+ };
+};
+
+&gpmi {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gpmi_pins_a>;
+ status = "okay";
+};
+
+&i2c0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0_pins_a>;
+ status = "okay";
+
+ pcf8563: rtc@51 {
+ compatible = "nxp,pcf8563";
+ reg = <0x51>;
+ };
+};
+
+
+&mac0 {
+ phy-mode = "rmii";
+ pinctrl-names = "default";
+ pinctrl-0 = <&mac0_pins_a>;
+ phy-reset-gpios = <&gpio4 13 GPIO_ACTIVE_LOW>;
+ status = "okay";
+};
+
+&pinctrl{
+ pinctrl-names = "default";
+ pinctrl-0 = <&hog_pins_cpuimx283>;
+
+ hog_pins_cpuimx283: hog-cpuimx283@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_ENET0_RX_CLK__GPIO_4_13
+ MX28_PAD_ENET0_TX_CLK__GPIO_4_5
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_ENABLE>;
+ };
+};
diff --git a/arch/arm/boot/dts/imx28-eukrea-mbmx287lc.dts b/arch/arm/boot/dts/imx28-eukrea-mbmx287lc.dts
new file mode 100644
index 000000000000..e773144e1e03
--- /dev/null
+++ b/arch/arm/boot/dts/imx28-eukrea-mbmx287lc.dts
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2013 Eukréa Electromatique <eric@eukrea.com>
+ * Copyright 2013 Eukréa Electromatique <denis@eukrea.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.
+ */
+
+/*
+ * Module contains : i.MX287 + 128MB DDR2 + NAND + 2 x Ethernet PHY + RTC
+ */
+
+#include "imx28-eukrea-mbmx283lc.dts"
+
+/ {
+ model = "Eukrea Electromatique MBMX287LC";
+ compatible = "eukrea,mbmx287lc", "eukrea,mbmx283lc", "eukrea,mbmx28lc", "fsl,imx28";
+
+ memory {
+ reg = <0x40000000 0x08000000>;
+ };
+};
+
+&mac1 {
+ phy-mode = "rmii";
+ pinctrl-names = "default";
+ pinctrl-0 = <&mac1_pins_a>;
+ phy-reset-gpios = <&gpio3 27 GPIO_ACTIVE_HIGH>;
+ status = "okay";
+};
+
+&pinctrl {
+ pinctrl-names = "default";
+ pinctrl-0 = <&hog_pins_cpuimx283 &hog_pins_cpuimx287>;
+ hog_pins_cpuimx287: hog-cpuimx287@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_SPDIF__GPIO_3_27
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_ENABLE>;
+ };
+};
diff --git a/arch/arm/boot/dts/imx28-eukrea-mbmx28lc.dtsi b/arch/arm/boot/dts/imx28-eukrea-mbmx28lc.dtsi
new file mode 100644
index 000000000000..927b391d2058
--- /dev/null
+++ b/arch/arm/boot/dts/imx28-eukrea-mbmx28lc.dtsi
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2013 Eukréa Electromatique <eric@eukrea.com>
+ * Copyright 2013 Eukréa Electromatique <denis@eukrea.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 <dt-bindings/input/input.h>
+#include <dt-bindings/gpio/gpio.h>
+#include "imx28.dtsi"
+
+/ {
+ model = "Eukrea Electromatique MBMX28LC";
+ compatible = "eukrea,mbmx28lc", "fsl,imx28";
+
+ backlight {
+ compatible = "pwm-backlight";
+ pwms = <&pwm 4 1000000>;
+ brightness-levels = <0 25 50 75 100 125 150 175 200 225 255>;
+ default-brightness-level = <10>;
+ };
+
+ button-sw3 {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&gpio_button_sw3_pins_mbmx28lc>;
+
+ sw3 {
+ label = "SW3";
+ gpios = <&gpio1 21 GPIO_ACTIVE_LOW>;
+ linux,code = <BTN_MISC>;
+ gpio-key,wakeup;
+ };
+ };
+
+ button-sw4 {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&gpio_button_sw4_pins_mbmx28lc>;
+
+ sw4 {
+ label = "SW4";
+ gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;
+ linux,code = <BTN_MISC>;
+ gpio-key,wakeup;
+ };
+ };
+
+ led-d6 {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_d6_pins_mbmx28lc>;
+
+ led1 {
+ label = "d6";
+ gpios = <&gpio1 23 GPIO_ACTIVE_LOW>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ led-d7 {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_d7_pins_mbmx28lc>;
+
+ led1 {
+ label = "d7";
+ gpios = <&gpio1 22 GPIO_ACTIVE_LOW>;
+ linux,default-trigger = "default-on";
+ };
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_3p3v: regulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_lcd_3v3: regulator@1 {
+ compatible = "regulator-fixed";
+ pinctrl-names = "default";
+ pinctrl-0 = <&reg_lcd_3v3_pins_mbmx28lc>;
+ regulator-name = "lcd-3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio3 30 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ reg_usb0_vbus: regulator@2 {
+ compatible = "regulator-fixed";
+ pinctrl-names = "default";
+ pinctrl-0 = <&reg_usb0_vbus_pins_mbmx28lc>;
+ regulator-name = "usb0_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio1 18 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ reg_usb1_vbus: regulator@3 {
+ compatible = "regulator-fixed";
+ pinctrl-names = "default";
+ pinctrl-0 = <&reg_usb1_vbus_pins_mbmx28lc>;
+ regulator-name = "usb1_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio1 19 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+ };
+
+ sound {
+ compatible = "fsl,imx28-mbmx28lc-sgtl5000",
+ "fsl,mxs-audio-sgtl5000";
+ model = "imx28-mbmx28lc-sgtl5000";
+ saif-controllers = <&saif0 &saif1>;
+ audio-codec = <&sgtl5000>;
+ };
+};
+
+&duart {
+ pinctrl-names = "default";
+ pinctrl-0 = <&duart_4pins_a>;
+ status = "okay";
+};
+
+&i2c0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0_pins_a>;
+ status = "okay";
+
+ sgtl5000: codec@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ VDDA-supply = <&reg_3p3v>;
+ VDDIO-supply = <&reg_3p3v>;
+ clocks = <&saif0>;
+ };
+};
+
+&lcdif {
+ pinctrl-names = "default";
+ pinctrl-0 = <&lcdif_18bit_pins_a &lcdif_pins_mbmx28lc>;
+ lcd-supply = <&reg_lcd_3v3>;
+ display = <&display0>;
+ status = "okay";
+
+ display0: display0 {
+ model = "43WVF1G-0";
+ bits-per-pixel = <16>;
+ bus-width = <18>;
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing0 {
+ clock-frequency = <9072000>;
+ hactive = <480>;
+ vactive = <272>;
+ hback-porch = <10>;
+ hfront-porch = <5>;
+ vback-porch = <8>;
+ vfront-porch = <8>;
+ hsync-len = <40>;
+ vsync-len = <10>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <1>;
+ };
+ };
+ };
+};
+
+&lradc {
+ fsl,lradc-touchscreen-wires = <4>;
+ status = "okay";
+};
+
+&pinctrl {
+ gpio_button_sw3_pins_mbmx28lc: gpio-button-sw3-mbmx28lc@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_LCD_D21__GPIO_1_21
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+
+ gpio_button_sw4_pins_mbmx28lc: gpio-button-sw4-mbmx28lc@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_LCD_D20__GPIO_1_20
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+
+ lcdif_pins_mbmx28lc: lcdif-mbmx28lc@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_LCD_VSYNC__LCD_VSYNC
+ MX28_PAD_LCD_HSYNC__LCD_HSYNC
+ MX28_PAD_LCD_DOTCLK__LCD_DOTCLK
+ MX28_PAD_LCD_ENABLE__LCD_ENABLE
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+
+ led_d6_pins_mbmx28lc: led-d6-mbmx28lc@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_LCD_D23__GPIO_1_23
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+
+ led_d7_pins_mbmx28lc: led-d7-mbmx28lc@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_LCD_D22__GPIO_1_22
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+
+ reg_lcd_3v3_pins_mbmx28lc: lcd-3v3-mbmx28lc@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_LCD_RESET__GPIO_3_30
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+
+ reg_usb0_vbus_pins_mbmx28lc: reg-usb0-vbus-mbmx28lc@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_LCD_D18__GPIO_1_18
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+
+ reg_usb1_vbus_pins_mbmx28lc: reg-usb1-vbus-mbmx28lc@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_LCD_D19__GPIO_1_19
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+};
+
+&pwm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm4_pins_a>;
+ status = "okay";
+};
+
+&saif0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&saif0_pins_a>;
+ status = "okay";
+};
+
+&saif1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&saif1_pins_a>;
+ fsl,saif-master = <&saif0>;
+ status = "okay";
+};
+
+&ssp0 {
+ compatible = "fsl,imx28-mmc";
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc0_4bit_pins_a &mmc0_cd_cfg &mmc0_sck_cfg>;
+ bus-width = <4>;
+ cd-inverted;
+ status = "okay";
+};
+
+&usb0 {
+ disable-over-current;
+ vbus-supply = <&reg_usb0_vbus>;
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb0_id_pins_b>;
+};
+
+&usb1 {
+ vbus-supply = <&reg_usb1_vbus>;
+ status = "okay";
+};
+
+&usbphy0 {
+ status = "okay";
+};
+
+&usbphy1 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx28-evk.dts b/arch/arm/boot/dts/imx28-evk.dts
index 4267c2b05d60..e4cc44c98585 100644
--- a/arch/arm/boot/dts/imx28-evk.dts
+++ b/arch/arm/boot/dts/imx28-evk.dts
@@ -193,6 +193,7 @@
i2c0: i2c@80058000 {
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins_a>;
+ clock-frequency = <400000>;
status = "okay";
sgtl5000: codec@0a {
@@ -278,33 +279,39 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_3p3v: 3p3v {
+ reg_3p3v: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "3P3V";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
- reg_vddio_sd0: vddio-sd0 {
+ reg_vddio_sd0: regulator@1 {
compatible = "regulator-fixed";
+ reg = <1>;
regulator-name = "vddio-sd0";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio3 28 0>;
};
- reg_fec_3v3: fec-3v3 {
+ reg_fec_3v3: regulator@2 {
compatible = "regulator-fixed";
+ reg = <2>;
regulator-name = "fec-3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio2 15 0>;
};
- reg_usb0_vbus: usb0_vbus {
+ reg_usb0_vbus: regulator@3 {
compatible = "regulator-fixed";
+ reg = <3>;
regulator-name = "usb0_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
@@ -312,8 +319,9 @@
enable-active-high;
};
- reg_usb1_vbus: usb1_vbus {
+ reg_usb1_vbus: regulator@4 {
compatible = "regulator-fixed";
+ reg = <4>;
regulator-name = "usb1_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
@@ -321,8 +329,9 @@
enable-active-high;
};
- reg_lcd_3v3: lcd-3v3 {
+ reg_lcd_3v3: regulator@5 {
compatible = "regulator-fixed";
+ reg = <5>;
regulator-name = "lcd-3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
@@ -330,8 +339,9 @@
enable-active-high;
};
- reg_can_3v3: can-3v3 {
+ reg_can_3v3: regulator@6 {
compatible = "regulator-fixed";
+ reg = <6>;
regulator-name = "can-3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
diff --git a/arch/arm/boot/dts/imx28-m28cu3.dts b/arch/arm/boot/dts/imx28-m28cu3.dts
index d3958da60bd7..9348ce59dda4 100644
--- a/arch/arm/boot/dts/imx28-m28cu3.dts
+++ b/arch/arm/boot/dts/imx28-m28cu3.dts
@@ -116,7 +116,6 @@
pinctrl-0 = <&lcdif_24bit_pins_a
&lcdif_pins_m28>;
display = <&display>;
- reset-active-high;
status = "okay";
display: display0 {
@@ -180,7 +179,7 @@
usb1: usb@80090000 {
vbus-supply = <&reg_usb1_vbus>;
pinctrl-names = "default";
- pinctrl-0 = <&usbphy1_pins_a>;
+ pinctrl-0 = <&usb1_pins_a>;
disable-over-current;
status = "okay";
};
@@ -229,33 +228,39 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_3p3v: 3p3v {
+ reg_3p3v: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "3P3V";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
- reg_vddio_sd0: vddio-sd0 {
+ reg_vddio_sd0: regulator@1 {
compatible = "regulator-fixed";
+ reg = <1>;
regulator-name = "vddio-sd0";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio3 29 0>;
};
- reg_vddio_sd1: vddio-sd1 {
+ reg_vddio_sd1: regulator@2 {
compatible = "regulator-fixed";
+ reg = <2>;
regulator-name = "vddio-sd1";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio2 19 0>;
};
- reg_usb1_vbus: usb1_vbus {
+ reg_usb1_vbus: regulator@3 {
compatible = "regulator-fixed";
+ reg = <3>;
regulator-name = "usb1_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
diff --git a/arch/arm/boot/dts/imx28-m28evk.dts b/arch/arm/boot/dts/imx28-m28evk.dts
index 8e2477fbe1d7..f0ad7b9b9d9a 100644
--- a/arch/arm/boot/dts/imx28-m28evk.dts
+++ b/arch/arm/boot/dts/imx28-m28evk.dts
@@ -194,7 +194,7 @@
};
rtc: rtc@68 {
- compatible = "stm,mt41t62";
+ compatible = "stm,m41t62";
reg = <0x68>;
};
};
@@ -248,14 +248,14 @@
usb0: usb@80080000 {
vbus-supply = <&reg_usb0_vbus>;
pinctrl-names = "default";
- pinctrl-0 = <&usbphy0_pins_a>;
+ pinctrl-0 = <&usb0_pins_a>;
status = "okay";
};
usb1: usb@80090000 {
vbus-supply = <&reg_usb1_vbus>;
pinctrl-names = "default";
- pinctrl-0 = <&usbphy1_pins_a>;
+ pinctrl-0 = <&usb1_pins_a>;
status = "okay";
};
@@ -285,33 +285,39 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_3p3v: 3p3v {
+ reg_3p3v: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "3P3V";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
- reg_vddio_sd0: vddio-sd0 {
+ reg_vddio_sd0: regulator@1 {
compatible = "regulator-fixed";
+ reg = <1>;
regulator-name = "vddio-sd0";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio3 28 0>;
};
- reg_usb0_vbus: usb0_vbus {
+ reg_usb0_vbus: regulator@2 {
compatible = "regulator-fixed";
+ reg = <2>;
regulator-name = "usb0_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
gpio = <&gpio3 12 0>;
};
- reg_usb1_vbus: usb1_vbus {
+ reg_usb1_vbus: regulator@3 {
compatible = "regulator-fixed";
+ reg = <3>;
regulator-name = "usb1_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
diff --git a/arch/arm/boot/dts/imx28-sps1.dts b/arch/arm/boot/dts/imx28-sps1.dts
index 4870f07bf56a..0ce3cb8e7914 100644
--- a/arch/arm/boot/dts/imx28-sps1.dts
+++ b/arch/arm/boot/dts/imx28-sps1.dts
@@ -106,7 +106,7 @@
usb0: usb@80080000 {
vbus-supply = <&reg_usb0_vbus>;
pinctrl-names = "default";
- pinctrl-0 = <&usbphy0_pins_b>;
+ pinctrl-0 = <&usb0_pins_b>;
status = "okay";
};
@@ -127,9 +127,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_usb0_vbus: usb0_vbus {
+ reg_usb0_vbus: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "usb0_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
diff --git a/arch/arm/boot/dts/imx28-tx28.dts b/arch/arm/boot/dts/imx28-tx28.dts
index be5a0550d58c..e14bd86f3e99 100644
--- a/arch/arm/boot/dts/imx28-tx28.dts
+++ b/arch/arm/boot/dts/imx28-tx28.dts
@@ -43,9 +43,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_usb0_vbus: usb0_vbus {
+ reg_usb0_vbus: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "usb0_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
@@ -53,8 +56,9 @@
enable-active-high;
};
- reg_usb1_vbus: usb1_vbus {
+ reg_usb1_vbus: regulator@1 {
compatible = "regulator-fixed";
+ reg = <1>;
regulator-name = "usb1_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
@@ -62,35 +66,38 @@
enable-active-high;
};
- reg_2p5v: 2p5v {
+ reg_2p5v: regulator@2 {
compatible = "regulator-fixed";
+ reg = <2>;
regulator-name = "2P5V";
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <2500000>;
regulator-always-on;
};
- reg_3p3v: 3p3v {
+ reg_3p3v: regulator@3 {
compatible = "regulator-fixed";
+ reg = <3>;
regulator-name = "3P3V";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
- reg_can_xcvr: can-xcvr {
+ reg_can_xcvr: regulator@4 {
compatible = "regulator-fixed";
+ reg = <4>;
regulator-name = "CAN XCVR";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio1 0 0>;
- enable-active-low;
pinctrl-names = "default";
pinctrl-0 = <&tx28_flexcan_xcvr_pins>;
};
- reg_lcd: lcd-power {
+ reg_lcd: regulator@5 {
compatible = "regulator-fixed";
+ reg = <5>;
regulator-name = "LCD POWER";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
@@ -98,8 +105,9 @@
enable-active-high;
};
- reg_lcd_reset: lcd-reset {
+ reg_lcd_reset: regulator@6 {
compatible = "regulator-fixed";
+ reg = <6>;
regulator-name = "LCD RESET";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi
index f8e9b20f6982..90a579532b8b 100644
--- a/arch/arm/boot/dts/imx28.dtsi
+++ b/arch/arm/boot/dts/imx28.dtsi
@@ -32,6 +32,8 @@
serial4 = &auart4;
spi0 = &ssp1;
spi1 = &ssp2;
+ usbphy0 = &usbphy0;
+ usbphy1 = &usbphy1;
};
cpus {
@@ -343,6 +345,19 @@
fsl,pull-up = <MXS_PULL_DISABLE>;
};
+ auart2_pins_a: auart2-pins@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_AUART2_RX__AUART2_RX
+ MX28_PAD_AUART2_TX__AUART2_TX
+ MX28_PAD_AUART2_CTS__AUART2_CTS
+ MX28_PAD_AUART2_RTS__AUART2_RTS
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+
auart3_pins_a: auart3@0 {
reg = <0>;
fsl,pinmux-ids = <
@@ -655,6 +670,33 @@
fsl,pull-up = <MXS_PULL_DISABLE>;
};
+ lcdif_18bit_pins_a: lcdif-18bit@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_LCD_D00__LCD_D0
+ MX28_PAD_LCD_D01__LCD_D1
+ MX28_PAD_LCD_D02__LCD_D2
+ MX28_PAD_LCD_D03__LCD_D3
+ MX28_PAD_LCD_D04__LCD_D4
+ MX28_PAD_LCD_D05__LCD_D5
+ MX28_PAD_LCD_D06__LCD_D6
+ MX28_PAD_LCD_D07__LCD_D7
+ MX28_PAD_LCD_D08__LCD_D8
+ MX28_PAD_LCD_D09__LCD_D9
+ MX28_PAD_LCD_D10__LCD_D10
+ MX28_PAD_LCD_D11__LCD_D11
+ MX28_PAD_LCD_D12__LCD_D12
+ MX28_PAD_LCD_D13__LCD_D13
+ MX28_PAD_LCD_D14__LCD_D14
+ MX28_PAD_LCD_D15__LCD_D15
+ MX28_PAD_LCD_D16__LCD_D16
+ MX28_PAD_LCD_D17__LCD_D17
+ >;
+ fsl,drive-strength = <MXS_DRIVE_4mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_DISABLE>;
+ };
+
lcdif_16bit_pins_a: lcdif-16bit@0 {
reg = <0>;
fsl,pinmux-ids = <
@@ -743,7 +785,7 @@
fsl,pull-up = <MXS_PULL_DISABLE>;
};
- usbphy0_pins_a: usbphy0@0 {
+ usb0_pins_a: usb0@0 {
reg = <0>;
fsl,pinmux-ids = <
MX28_PAD_SSP2_SS2__USB0_OVERCURRENT
@@ -753,7 +795,7 @@
fsl,pull-up = <MXS_PULL_DISABLE>;
};
- usbphy0_pins_b: usbphy0@1 {
+ usb0_pins_b: usb0@1 {
reg = <1>;
fsl,pinmux-ids = <
MX28_PAD_AUART1_CTS__USB0_OVERCURRENT
@@ -763,7 +805,7 @@
fsl,pull-up = <MXS_PULL_DISABLE>;
};
- usbphy1_pins_a: usbphy1@0 {
+ usb1_pins_a: usb1@0 {
reg = <0>;
fsl,pinmux-ids = <
MX28_PAD_SSP2_SS1__USB1_OVERCURRENT
@@ -782,6 +824,17 @@
fsl,voltage = <MXS_VOLTAGE_HIGH>;
fsl,pull-up = <MXS_PULL_ENABLE>;
};
+
+ usb0_id_pins_b: usb0id1@0 {
+ reg = <0>;
+ fsl,pinmux-ids = <
+ MX28_PAD_PWM2__USB0_ID
+ >;
+ fsl,drive-strength = <MXS_DRIVE_12mA>;
+ fsl,voltage = <MXS_VOLTAGE_HIGH>;
+ fsl,pull-up = <MXS_PULL_ENABLE>;
+ };
+
};
digctl: digctl@8001c000 {
@@ -946,6 +999,7 @@
20 21 22 23 24 25>;
status = "disabled";
clocks = <&clks 41>;
+ #io-channel-cells = <1>;
};
spdif: spdif@80054000 {
@@ -1130,4 +1184,9 @@
status = "disabled";
};
};
+
+ iio_hwmon {
+ compatible = "iio-hwmon";
+ io-channels = <&lradc 8>;
+ };
};
diff --git a/arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi b/arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi
new file mode 100644
index 000000000000..906ae937b013
--- /dev/null
+++ b/arch/arm/boot/dts/imx35-eukrea-cpuimx35.dtsi
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2013 Eukréa Electromatique <denis@eukrea.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 "imx35.dtsi"
+
+/ {
+ model = "Eukrea CPUIMX35";
+ compatible = "eukrea,cpuimx35", "fsl,imx35";
+
+ memory {
+ reg = <0x80000000 0x8000000>; /* 128M */
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fec>;
+ status = "okay";
+};
+
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ pcf8563@51 {
+ compatible = "nxp,pcf8563";
+ reg = <0x51>;
+ };
+};
+
+&iomuxc {
+ imx35-eukrea {
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX35_PAD_FEC_TX_CLK__FEC_TX_CLK 0x80000000
+ MX35_PAD_FEC_RX_CLK__FEC_RX_CLK 0x80000000
+ MX35_PAD_FEC_RX_DV__FEC_RX_DV 0x80000000
+ MX35_PAD_FEC_COL__FEC_COL 0x80000000
+ MX35_PAD_FEC_RDATA0__FEC_RDATA_0 0x80000000
+ MX35_PAD_FEC_TDATA0__FEC_TDATA_0 0x80000000
+ MX35_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000
+ MX35_PAD_FEC_MDC__FEC_MDC 0x80000000
+ MX35_PAD_FEC_MDIO__FEC_MDIO 0x80000000
+ MX35_PAD_FEC_TX_ERR__FEC_TX_ERR 0x80000000
+ MX35_PAD_FEC_RX_ERR__FEC_RX_ERR 0x80000000
+ MX35_PAD_FEC_CRS__FEC_CRS 0x80000000
+ MX35_PAD_FEC_RDATA1__FEC_RDATA_1 0x80000000
+ MX35_PAD_FEC_TDATA1__FEC_TDATA_1 0x80000000
+ MX35_PAD_FEC_RDATA2__FEC_RDATA_2 0x80000000
+ MX35_PAD_FEC_TDATA2__FEC_TDATA_2 0x80000000
+ MX35_PAD_FEC_RDATA3__FEC_RDATA_3 0x80000000
+ MX35_PAD_FEC_TDATA3__FEC_TDATA_3 0x80000000
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX35_PAD_I2C1_CLK__I2C1_SCL 0x80000000
+ MX35_PAD_I2C1_DAT__I2C1_SDA 0x80000000
+ >;
+ };
+ };
+};
+
+&nfc {
+ nand-bus-width = <8>;
+ nand-ecc-mode = "hw";
+ nand-on-flash-bbt;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx35-eukrea-mbimxsd35-baseboard.dts b/arch/arm/boot/dts/imx35-eukrea-mbimxsd35-baseboard.dts
new file mode 100644
index 000000000000..1bdec21f4533
--- /dev/null
+++ b/arch/arm/boot/dts/imx35-eukrea-mbimxsd35-baseboard.dts
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2013 Eukréa Electromatique <denis@eukrea.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.
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+#include "imx35-eukrea-cpuimx35.dtsi"
+
+/ {
+ model = "Eukrea CPUIMX35";
+ compatible = "eukrea,mbimxsd35-baseboard", "eukrea,cpuimx35", "fsl,imx35";
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_bp1>;
+
+ bp1 {
+ label = "BP1";
+ gpios = <&gpio3 25 GPIO_ACTIVE_LOW>;
+ linux,code = <BTN_MISC>;
+ gpio-key,wakeup;
+ linux,input-type = <1>;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_led1>;
+
+ led1 {
+ label = "led1";
+ gpios = <&gpio3 29 GPIO_ACTIVE_LOW>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>;
+ status = "okay";
+};
+
+&esdhc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_esdhc1>;
+ cd-gpios = <&gpio3 24>;
+ status = "okay";
+};
+
+&i2c1 {
+ tlv320aic23: codec@1a {
+ compatible = "ti,tlv320aic23";
+ reg = <0x1a>;
+ };
+};
+
+&iomuxc {
+ imx35-eukrea {
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX35_PAD_STXFS4__AUDMUX_AUD4_TXFS 0x80000000
+ MX35_PAD_STXD4__AUDMUX_AUD4_TXD 0x80000000
+ MX35_PAD_SRXD4__AUDMUX_AUD4_RXD 0x80000000
+ MX35_PAD_SCK4__AUDMUX_AUD4_TXC 0x80000000
+ >;
+ };
+
+ pinctrl_bp1: bp1grp {
+ fsl,pins = <MX35_PAD_LD19__GPIO3_25 0x80000000>;
+ };
+
+ pinctrl_esdhc1: esdhc1grp {
+ fsl,pins = <
+ MX35_PAD_SD1_CMD__ESDHC1_CMD 0x80000000
+ MX35_PAD_SD1_CLK__ESDHC1_CLK 0x80000000
+ MX35_PAD_SD1_DATA0__ESDHC1_DAT0 0x80000000
+ MX35_PAD_SD1_DATA1__ESDHC1_DAT1 0x80000000
+ MX35_PAD_SD1_DATA2__ESDHC1_DAT2 0x80000000
+ MX35_PAD_SD1_DATA3__ESDHC1_DAT3 0x80000000
+ MX35_PAD_LD18__GPIO3_24 0x80000000 /* CD */
+ >;
+ };
+
+ pinctrl_led1: led1grp {
+ fsl,pins = <MX35_PAD_LD23__GPIO3_29 0x80000000>;
+ };
+
+ pinctrl_reg_lcd_3v3: reg-lcd-3v3 {
+ fsl,pins = <MX35_PAD_D3_CLS__GPIO1_4 0x80000000>;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX35_PAD_TXD1__UART1_TXD_MUX 0x1c5
+ MX35_PAD_RXD1__UART1_RXD_MUX 0x1c5
+ MX35_PAD_CTS1__UART1_CTS 0x1c5
+ MX35_PAD_RTS1__UART1_RTS 0x1c5
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX35_PAD_RXD2__UART2_RXD_MUX 0x1c5
+ MX35_PAD_TXD2__UART2_TXD_MUX 0x1c5
+ MX35_PAD_RTS2__UART2_RTS 0x1c5
+ MX35_PAD_CTS2__UART2_CTS 0x1c5
+ >;
+ };
+ };
+};
+
+&ssi1 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ fsl,uart-has-rtscts;
+ status = "okay";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ fsl,uart-has-rtscts;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx35.dtsi b/arch/arm/boot/dts/imx35.dtsi
new file mode 100644
index 000000000000..88b218f8f810
--- /dev/null
+++ b/arch/arm/boot/dts/imx35.dtsi
@@ -0,0 +1,359 @@
+/*
+ * Copyright 2012 Steffen Trumtrar, Pengutronix
+ *
+ * based on imx27.dtsi
+ *
+ * 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 "skeleton.dtsi"
+#include "imx35-pinfunc.h"
+
+/ {
+ aliases {
+ gpio0 = &gpio1;
+ gpio1 = &gpio2;
+ gpio2 = &gpio3;
+ serial0 = &uart1;
+ serial1 = &uart2;
+ serial2 = &uart3;
+ spi0 = &spi1;
+ spi1 = &spi2;
+ };
+
+ cpus {
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ cpu {
+ compatible = "arm,arm1136";
+ device_type = "cpu";
+ };
+ };
+
+ avic: avic-interrupt-controller@68000000 {
+ compatible = "fsl,imx35-avic", "fsl,avic";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0x68000000 0x10000000>;
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ interrupt-parent = <&avic>;
+ ranges;
+
+ L2: l2-cache@30000000 {
+ compatible = "arm,l210-cache";
+ reg = <0x30000000 0x1000>;
+ cache-unified;
+ cache-level = <2>;
+ };
+
+ aips1: aips@43f00000 {
+ compatible = "fsl,aips", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x43f00000 0x100000>;
+ ranges;
+
+ i2c1: i2c@43f80000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx35-i2c", "fsl,imx1-i2c";
+ reg = <0x43f80000 0x4000>;
+ clocks = <&clks 51>;
+ clock-names = "ipg_per";
+ interrupts = <10>;
+ status = "disabled";
+ };
+
+ i2c3: i2c@43f84000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx35-i2c", "fsl,imx1-i2c";
+ reg = <0x43f84000 0x4000>;
+ clocks = <&clks 53>;
+ clock-names = "ipg_per";
+ interrupts = <3>;
+ status = "disabled";
+ };
+
+ uart1: serial@43f90000 {
+ compatible = "fsl,imx35-uart", "fsl,imx21-uart";
+ reg = <0x43f90000 0x4000>;
+ clocks = <&clks 9>, <&clks 70>;
+ clock-names = "ipg", "per";
+ interrupts = <45>;
+ status = "disabled";
+ };
+
+ uart2: serial@43f94000 {
+ compatible = "fsl,imx35-uart", "fsl,imx21-uart";
+ reg = <0x43f94000 0x4000>;
+ clocks = <&clks 9>, <&clks 71>;
+ clock-names = "ipg", "per";
+ interrupts = <32>;
+ status = "disabled";
+ };
+
+ i2c2: i2c@43f98000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx35-i2c", "fsl,imx1-i2c";
+ reg = <0x43f98000 0x4000>;
+ clocks = <&clks 52>;
+ clock-names = "ipg_per";
+ interrupts = <4>;
+ status = "disabled";
+ };
+
+ ssi1: ssi@43fa0000 {
+ compatible = "fsl,imx35-ssi", "fsl,imx21-ssi";
+ reg = <0x43fa0000 0x4000>;
+ interrupts = <11>;
+ clocks = <&clks 68>;
+ dmas = <&sdma 28 0 0>,
+ <&sdma 29 0 0>;
+ dma-names = "rx", "tx";
+ fsl,fifo-depth = <15>;
+ status = "disabled";
+ };
+
+ spi1: cspi@43fa4000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx35-cspi";
+ reg = <0x43fa4000 0x4000>;
+ clocks = <&clks 35 &clks 35>;
+ clock-names = "ipg", "per";
+ interrupts = <14>;
+ status = "disabled";
+ };
+
+ iomuxc: iomuxc@43fac000 {
+ compatible = "fsl,imx35-iomuxc";
+ reg = <0x43fac000 0x4000>;
+ };
+ };
+
+ spba: spba-bus@50000000 {
+ compatible = "fsl,spba-bus", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x50000000 0x100000>;
+ ranges;
+
+ uart3: serial@5000c000 {
+ compatible = "fsl,imx35-uart", "fsl,imx21-uart";
+ reg = <0x5000c000 0x4000>;
+ clocks = <&clks 9>, <&clks 72>;
+ clock-names = "ipg", "per";
+ interrupts = <18>;
+ status = "disabled";
+ };
+
+ spi2: cspi@50010000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx35-cspi";
+ reg = <0x50010000 0x4000>;
+ interrupts = <13>;
+ clocks = <&clks 36 &clks 36>;
+ clock-names = "ipg", "per";
+ status = "disabled";
+ };
+
+ fec: fec@50038000 {
+ compatible = "fsl,imx35-fec", "fsl,imx27-fec";
+ reg = <0x50038000 0x4000>;
+ clocks = <&clks 46>, <&clks 8>;
+ clock-names = "ipg", "ahb";
+ interrupts = <57>;
+ status = "disabled";
+ };
+ };
+
+ aips2: aips@53f00000 {
+ compatible = "fsl,aips", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x53f00000 0x100000>;
+ ranges;
+
+ clks: ccm@53f80000 {
+ compatible = "fsl,imx35-ccm";
+ reg = <0x53f80000 0x4000>;
+ interrupts = <31>;
+ #clock-cells = <1>;
+ };
+
+ gpio3: gpio@53fa4000 {
+ compatible = "fsl,imx35-gpio", "fsl,imx31-gpio";
+ reg = <0x53fa4000 0x4000>;
+ interrupts = <56>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ esdhc1: esdhc@53fb4000 {
+ compatible = "fsl,imx35-esdhc";
+ reg = <0x53fb4000 0x4000>;
+ interrupts = <7>;
+ clocks = <&clks 9>, <&clks 8>, <&clks 43>;
+ clock-names = "ipg", "ahb", "per";
+ status = "disabled";
+ };
+
+ esdhc2: esdhc@53fb8000 {
+ compatible = "fsl,imx35-esdhc";
+ reg = <0x53fb8000 0x4000>;
+ interrupts = <8>;
+ clocks = <&clks 9>, <&clks 8>, <&clks 44>;
+ clock-names = "ipg", "ahb", "per";
+ status = "disabled";
+ };
+
+ esdhc3: esdhc@53fbc000 {
+ compatible = "fsl,imx35-esdhc";
+ reg = <0x53fbc000 0x4000>;
+ interrupts = <9>;
+ clocks = <&clks 9>, <&clks 8>, <&clks 45>;
+ clock-names = "ipg", "ahb", "per";
+ status = "disabled";
+ };
+
+ audmux: audmux@53fc4000 {
+ compatible = "fsl,imx35-audmux", "fsl,imx31-audmux";
+ reg = <0x53fc4000 0x4000>;
+ status = "disabled";
+ };
+
+ gpio1: gpio@53fcc000 {
+ compatible = "fsl,imx35-gpio", "fsl,imx31-gpio";
+ reg = <0x53fcc000 0x4000>;
+ interrupts = <52>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio2: gpio@53fd0000 {
+ compatible = "fsl,imx35-gpio", "fsl,imx31-gpio";
+ reg = <0x53fd0000 0x4000>;
+ interrupts = <51>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ sdma: sdma@53fd4000 {
+ compatible = "fsl,imx35-sdma";
+ reg = <0x53fd4000 0x4000>;
+ clocks = <&clks 9>, <&clks 65>;
+ clock-names = "ipg", "ahb";
+ #dma-cells = <3>;
+ interrupts = <34>;
+ fsl,sdma-ram-script-name = "imx/sdma/sdma-imx35.bin";
+ };
+
+ wdog: wdog@53fdc000 {
+ compatible = "fsl,imx35-wdt", "fsl,imx21-wdt";
+ reg = <0x53fdc000 0x4000>;
+ clocks = <&clks 74>;
+ clock-names = "";
+ interrupts = <55>;
+ };
+
+ can1: can@53fe4000 {
+ compatible = "fsl,imx35-flexcan", "fsl,p1010-flexcan";
+ reg = <0x53fe4000 0x1000>;
+ clocks = <&clks 33>;
+ clock-names = "ipg";
+ interrupts = <43>;
+ status = "disabled";
+ };
+
+ can2: can@53fe8000 {
+ compatible = "fsl,imx35-flexcan", "fsl,p1010-flexcan";
+ reg = <0x53fe8000 0x1000>;
+ clocks = <&clks 34>;
+ clock-names = "ipg";
+ interrupts = <44>;
+ status = "disabled";
+ };
+
+ usbotg: usb@53ff4000 {
+ compatible = "fsl,imx35-usb", "fsl,imx27-usb";
+ reg = <0x53ff4000 0x0200>;
+ interrupts = <37>;
+ clocks = <&clks 9>, <&clks 73>, <&clks 28>;
+ clock-names = "ipg", "ahb", "per";
+ fsl,usbmisc = <&usbmisc 0>;
+ status = "disabled";
+ };
+
+ usbhost1: usb@53ff4400 {
+ compatible = "fsl,imx35-usb", "fsl,imx27-usb";
+ reg = <0x53ff4400 0x0200>;
+ interrupts = <35>;
+ clocks = <&clks 9>, <&clks 73>, <&clks 28>;
+ clock-names = "ipg", "ahb", "per";
+ fsl,usbmisc = <&usbmisc 1>;
+ status = "disabled";
+ };
+
+ usbmisc: usbmisc@53ff4600 {
+ #index-cells = <1>;
+ compatible = "fsl,imx35-usbmisc";
+ clocks = <&clks 9>, <&clks 73>, <&clks 28>;
+ clock-names = "ipg", "ahb", "per";
+ reg = <0x53ff4600 0x00f>;
+ };
+ };
+
+ emi@80000000 { /* External Memory Interface */
+ compatible = "fsl,emi", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x80000000 0x40000000>;
+ ranges;
+
+ nfc: nand@bb000000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "fsl,imx35-nand", "fsl,imx25-nand";
+ reg = <0xbb000000 0x2000>;
+ clocks = <&clks 29>;
+ clock-names = "";
+ interrupts = <33>;
+ status = "disabled";
+ };
+
+ weim: weim@b8002000 {
+ #address-cells = <2>;
+ #size-cells = <1>;
+ clocks = <&clks 0>;
+ compatible = "fsl,imx35-weim", "fsl,imx27-weim";
+ reg = <0xb8002000 0x1000>;
+ ranges = <
+ 0 0 0xa0000000 0x8000000
+ 1 0 0xa8000000 0x8000000
+ 2 0 0xb0000000 0x2000000
+ 3 0 0xb2000000 0x2000000
+ 4 0 0xb4000000 0x2000000
+ 5 0 0xb6000000 0x2000000
+ >;
+ status = "disabled";
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/imx50-evk.dts b/arch/arm/boot/dts/imx50-evk.dts
new file mode 100644
index 000000000000..1b22512c91bd
--- /dev/null
+++ b/arch/arm/boot/dts/imx50-evk.dts
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2013 Greg Ungerer <gerg@uclinux.org>
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx50.dtsi"
+
+/ {
+ model = "Freescale i.MX50 Evaluation Kit";
+ compatible = "fsl,imx50-evk", "fsl,imx50";
+
+ memory {
+ reg = <0x70000000 0x80000000>;
+ };
+};
+
+&cspi {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_cspi>;
+ fsl,spi-num-chipselects = <2>;
+ cs-gpios = <&gpio4 11 0>, <&gpio4 13 0>;
+ status = "okay";
+
+ flash: m25p32@1 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "m25p32", "m25p80";
+ spi-max-frequency = <25000000>;
+ reg = <1>;
+
+ partition@0 {
+ label = "bootloader";
+ reg = <0x0 0x100000>;
+ read-only;
+ };
+
+ partition@100000 {
+ label = "kernel";
+ reg = <0x100000 0x300000>;
+ };
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fec>;
+ phy-mode = "rmii";
+ phy-reset-gpios = <&gpio4 12 0>;
+ status = "okay";
+};
+
+&iomuxc {
+ imx50-evk {
+ pinctrl_cspi: cspigrp {
+ fsl,pins = <
+ MX50_PAD_CSPI_SCLK__CSPI_SCLK 0x00
+ MX50_PAD_CSPI_MISO__CSPI_MISO 0x00
+ MX50_PAD_CSPI_MOSI__CSPI_MOSI 0x00
+ MX50_PAD_CSPI_SS0__GPIO4_11 0xc4
+ MX50_PAD_ECSPI1_MOSI__CSPI_SS1 0xf4
+ >;
+ };
+
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX50_PAD_SSI_RXFS__FEC_MDC 0x80
+ MX50_PAD_SSI_RXC__FEC_MDIO 0x80
+ MX50_PAD_DISP_D0__FEC_TX_CLK 0x80
+ MX50_PAD_DISP_D1__FEC_RX_ERR 0x80
+ MX50_PAD_DISP_D2__FEC_RX_DV 0x80
+ MX50_PAD_DISP_D3__FEC_RDATA_1 0x80
+ MX50_PAD_DISP_D4__FEC_RDATA_0 0x80
+ MX50_PAD_DISP_D5__FEC_TX_EN 0x80
+ MX50_PAD_DISP_D6__FEC_TDATA_1 0x80
+ MX50_PAD_DISP_D7__FEC_TDATA_0 0x80
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX50_PAD_UART1_TXD__UART1_TXD_MUX 0x1e4
+ MX50_PAD_UART1_RXD__UART1_RXD_MUX 0x1e4
+ MX50_PAD_UART1_RTS__UART1_RTS 0x1e4
+ MX50_PAD_UART1_CTS__UART1_CTS 0x1e4
+ >;
+ };
+ };
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&usbh1 {
+ status = "okay";
+};
+
+&usbh2 {
+ status = "okay";
+};
+
+&usbh3 {
+ status = "okay";
+};
+
+&usbotg {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx50-pinfunc.h b/arch/arm/boot/dts/imx50-pinfunc.h
new file mode 100644
index 000000000000..97e6e7f4ebdd
--- /dev/null
+++ b/arch/arm/boot/dts/imx50-pinfunc.h
@@ -0,0 +1,923 @@
+/*
+ * Copyright 2013 Greg Ungerer <gerg@uclinux.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 __DTS_IMX50_PINFUNC_H
+#define __DTS_IMX50_PINFUNC_H
+
+/*
+ * The pin function ID is a tuple of
+ * <mux_reg conf_reg input_reg mux_mode input_val>
+ */
+#define MX50_PAD_KEY_COL0__KPP_COL_0 0x020 0x2cc 0x000 0x0 0x0
+#define MX50_PAD_KEY_COL0__GPIO4_0 0x020 0x2cc 0x000 0x1 0x0
+#define MX50_PAD_KEY_COL0__EIM_NANDF_CLE 0x020 0x2cc 0x000 0x2 0x0
+#define MX50_PAD_KEY_COL0__CTI_TRIGIN7 0x020 0x2cc 0x000 0x6 0x0
+#define MX50_PAD_KEY_COL0__USBPHY1_TXREADY 0x020 0x2cc 0x000 0x7 0x0
+#define MX50_PAD_KEY_ROW0__KPP_ROW_0 0x024 0x2d0 0x000 0x0 0x0
+#define MX50_PAD_KEY_ROW0__GPIO4_1 0x024 0x2d0 0x000 0x1 0x0
+#define MX50_PAD_KEY_ROW0__EIM_NANDF_ALE 0x024 0x2d0 0x000 0x2 0x0
+#define MX50_PAD_KEY_ROW0__CTI_TRIGIN_ACK7 0x024 0x2d0 0x000 0x6 0x0
+#define MX50_PAD_KEY_ROW0__USBPHY1_RXVALID 0x024 0x2d0 0x000 0x7 0x0
+#define MX50_PAD_KEY_COL1__KPP_COL_1 0x028 0x2d4 0x000 0x0 0x0
+#define MX50_PAD_KEY_COL1__GPIO4_2 0x028 0x2d4 0x000 0x1 0x0
+#define MX50_PAD_KEY_COL1__EIM_NANDF_CEN_0 0x028 0x2d4 0x000 0x2 0x0
+#define MX50_PAD_KEY_COL1__CTI_TRIGOUT_ACK6 0x028 0x2d4 0x000 0x6 0x0
+#define MX50_PAD_KEY_COL1__USBPHY1_RXACTIVE 0x028 0x2d4 0x000 0x7 0x0
+#define MX50_PAD_KEY_ROW1__KPP_ROW_1 0x02c 0x2d8 0x000 0x0 0x0
+#define MX50_PAD_KEY_ROW1__GPIO4_3 0x02c 0x2d8 0x000 0x1 0x0
+#define MX50_PAD_KEY_ROW1__EIM_NANDF_CEN_1 0x02c 0x2d8 0x000 0x2 0x0
+#define MX50_PAD_KEY_ROW1__CTI_TRIGOUT_ACK7 0x02c 0x2d8 0x000 0x6 0x0
+#define MX50_PAD_KEY_ROW1__USBPHY1_RXERROR 0x02c 0x2d8 0x000 0x7 0x0
+#define MX50_PAD_KEY_COL2__KPP_COL_1 0x030 0x2dc 0x000 0x0 0x0
+#define MX50_PAD_KEY_COL2__GPIO4_4 0x030 0x2dc 0x000 0x1 0x0
+#define MX50_PAD_KEY_COL2__EIM_NANDF_CEN_2 0x030 0x2dc 0x000 0x2 0x0
+#define MX50_PAD_KEY_COL2__CTI_TRIGOUT6 0x030 0x2dc 0x000 0x6 0x0
+#define MX50_PAD_KEY_COL2__USBPHY1_SIECLOCK 0x030 0x2dc 0x000 0x7 0x0
+#define MX50_PAD_KEY_ROW2__KPP_ROW_2 0x034 0x2e0 0x000 0x0 0x0
+#define MX50_PAD_KEY_ROW2__GPIO4_5 0x034 0x2e0 0x000 0x1 0x0
+#define MX50_PAD_KEY_ROW2__EIM_NANDF_CEN_3 0x034 0x2e0 0x000 0x2 0x0
+#define MX50_PAD_KEY_ROW2__CTI_TRIGOUT7 0x034 0x2e0 0x000 0x6 0x0
+#define MX50_PAD_KEY_ROW2__USBPHY1_LINESTATE_0 0x034 0x2e0 0x000 0x7 0x0
+#define MX50_PAD_KEY_COL3__KPP_COL_2 0x038 0x2e4 0x000 0x0 0x0
+#define MX50_PAD_KEY_COL3__GPIO4_6 0x038 0x2e4 0x000 0x1 0x0
+#define MX50_PAD_KEY_COL3__EIM_NANDF_READY0 0x038 0x2e4 0x7b4 0x2 0x0
+#define MX50_PAD_KEY_COL3__SDMA_EXT_EVENT_0 0x038 0x2e4 0x7b8 0x6 0x0
+#define MX50_PAD_KEY_COL3__USBPHY1_LINESTATE_1 0x038 0x2e4 0x000 0x7 0x0
+#define MX50_PAD_KEY_ROW3__KPP_ROW_3 0x03c 0x2e8 0x000 0x0 0x0
+#define MX50_PAD_KEY_ROW3__GPIO4_7 0x03c 0x2e8 0x000 0x1 0x0
+#define MX50_PAD_KEY_ROW3__EIM_NANDF_DQS 0x03c 0x2e8 0x7b0 0x2 0x0
+#define MX50_PAD_KEY_ROW3__SDMA_EXT_EVENT_1 0x03c 0x2e8 0x7bc 0x6 0x0
+#define MX50_PAD_KEY_ROW3__USBPHY1_VBUSVALID 0x03c 0x2e8 0x000 0x7 0x0
+#define MX50_PAD_I2C1_SCL__I2C1_SCL 0x040 0x2ec 0x000 0x0 0x0
+#define MX50_PAD_I2C1_SCL__GPIO6_18 0x040 0x2ec 0x000 0x1 0x0
+#define MX50_PAD_I2C1_SCL__UART2_TXD_MUX 0x040 0x2ec 0x7cc 0x2 0x0
+#define MX50_PAD_I2C1_SDA__I2C1_SDA 0x044 0x2f0 0x000 0x0 0x0
+#define MX50_PAD_I2C1_SDA__GPIO6_19 0x044 0x2f0 0x000 0x1 0x0
+#define MX50_PAD_I2C1_SDA__UART2_RXD_MUX 0x044 0x2f0 0x7cc 0x2 0x1
+#define MX50_PAD_I2C2_SCL__I2C2_SCL 0x048 0x2f4 0x000 0x0 0x0
+#define MX50_PAD_I2C2_SCL__GPIO6_20 0x048 0x2f4 0x000 0x1 0x0
+#define MX50_PAD_I2C2_SCL__UART2_CTS 0x048 0x2f4 0x000 0x2 0x0
+#define MX50_PAD_I2C2_SDA__I2C2_SDA 0x04c 0x2f8 0x000 0x0 0x0
+#define MX50_PAD_I2C2_SDA__GPIO6_21 0x04c 0x2f8 0x000 0x1 0x0
+#define MX50_PAD_I2C2_SDA__UART2_RTS 0x04c 0x2f8 0x7c8 0x2 0x1
+#define MX50_PAD_I2C3_SCL__I2C3_SCL 0x050 0x2fc 0x000 0x0 0x0
+#define MX50_PAD_I2C3_SCL__GPIO6_22 0x050 0x2fc 0x000 0x1 0x0
+#define MX50_PAD_I2C3_SCL__FEC_MDC 0x050 0x2fc 0x000 0x2 0x0
+#define MX50_PAD_I2C3_SCL__GPC_PMIC_RDY 0x050 0x2fc 0x000 0x3 0x0
+#define MX50_PAD_I2C3_SCL__GPT_CAPIN1 0x050 0x2fc 0x000 0x5 0x0
+#define MX50_PAD_I2C3_SCL__OBSERVE_MUX_OBSRV_INT_OUT0 0x050 0x2fc 0x000 0x6 0x0
+#define MX50_PAD_I2C3_SCL__USBOH1_USBOTG_OC 0x050 0x2fc 0x7e8 0x7 0x0
+#define MX50_PAD_I2C3_SDA__I2C3_SDA 0x054 0x300 0x000 0x0 0x0
+#define MX50_PAD_I2C3_SDA__GPIO6_23 0x054 0x300 0x000 0x1 0x0
+#define MX50_PAD_I2C3_SDA__FEC_MDIO 0x054 0x300 0x774 0x2 0x0
+#define MX50_PAD_I2C3_SDA__TZIC_PWRFAIL_INT 0x054 0x300 0x000 0x3 0x0
+#define MX50_PAD_I2C3_SDA__SRTC_ALARM_DEB 0x054 0x300 0x000 0x4 0x0
+#define MX50_PAD_I2C3_SDA__GPT_CAPIN2 0x054 0x300 0x000 0x5 0x0
+#define MX50_PAD_I2C3_SDA__OBSERVE_MUX_OBSRV_INT_OUT1 0x054 0x300 0x000 0x6 0x0
+#define MX50_PAD_I2C3_SDA__USBOH1_USBOTG_PWR 0x054 0x300 0x000 0x7 0x0
+#define MX50_PAD_PWM1__PWM1_PWMO 0x058 0x304 0x000 0x0 0x0
+#define MX50_PAD_PWM1__GPIO6_24 0x058 0x304 0x000 0x1 0x0
+#define MX50_PAD_PWM1__USBOH1_USBOTG_OC 0x058 0x304 0x7e8 0x2 0x1
+#define MX50_PAD_PWM1__GPT_CMPOUT1 0x058 0x304 0x000 0x5 0x0
+#define MX50_PAD_PWM1__OBSERVE_MUX_OBSRV_INT_OUT2 0x058 0x304 0x000 0x6 0x0
+#define MX50_PAD_PWM1__SJC_FAIL 0x058 0x304 0x000 0x7 0x0
+#define MX50_PAD_PWM2__PWM2_PWMO 0x05c 0x308 0x000 0x0 0x0
+#define MX50_PAD_PWM2__GPIO6_25 0x05c 0x308 0x000 0x1 0x0
+#define MX50_PAD_PWM2__USBOH1_USBOTG_PWR 0x05c 0x308 0x000 0x2 0x0
+#define MX50_PAD_PWM2__GPT_CMPOUT2 0x05c 0x308 0x000 0x5 0x0
+#define MX50_PAD_PWM2__OBSERVE_MUX_OBSRV_INT_OUT3 0x05c 0x308 0x000 0x6 0x0
+#define MX50_PAD_PWM2__SRC_ANY_PU_RST 0x05c 0x308 0x000 0x7 0x0
+#define MX50_PAD_OWIRE__OWIRE_LINE 0x060 0x30c 0x000 0x0 0x0
+#define MX50_PAD_OWIRE__GPIO6_26 0x060 0x30c 0x000 0x1 0x0
+#define MX50_PAD_OWIRE__USBOH1_USBH1_OC 0x060 0x30c 0x000 0x2 0x0
+#define MX50_PAD_OWIRE__CCM_SSI_EXT1_CLK 0x060 0x30c 0x000 0x3 0x0
+#define MX50_PAD_OWIRE__EPDC_PWRIRQ 0x060 0x30c 0x000 0x4 0x0
+#define MX50_PAD_OWIRE__GPT_CMPOUT3 0x060 0x30c 0x000 0x5 0x0
+#define MX50_PAD_OWIRE__OBSERVE_MUX_OBSRV_INT_OUT4 0x060 0x30c 0x000 0x6 0x0
+#define MX50_PAD_OWIRE__SJC_JTAG_ACT 0x060 0x30c 0x000 0x7 0x0
+#define MX50_PAD_EPITO__EPIT1_EPITO 0x064 0x310 0x000 0x0 0x0
+#define MX50_PAD_EPITO__GPIO6_27 0x064 0x310 0x000 0x1 0x0
+#define MX50_PAD_EPITO__USBOH1_USBH1_PWR 0x064 0x310 0x000 0x2 0x0
+#define MX50_PAD_EPITO__CCM_SSI_EXT2_CLK 0x064 0x310 0x000 0x3 0x0
+#define MX50_PAD_EPITO__DPLLIP1_TOG_EN 0x064 0x310 0x000 0x4 0x0
+#define MX50_PAD_EPITO__GPT_CLK_IN 0x064 0x310 0x000 0x5 0x0
+#define MX50_PAD_EPITO__PMU_IRQ_B 0x064 0x310 0x000 0x6 0x0
+#define MX50_PAD_EPITO__SJC_DE_B 0x064 0x310 0x000 0x7 0x0
+#define MX50_PAD_WDOG__WDOG1_WDOG_B 0x068 0x314 0x000 0x0 0x0
+#define MX50_PAD_WDOG__GPIO6_28 0x068 0x314 0x000 0x1 0x0
+#define MX50_PAD_WDOG__WDOG1_WDOG_RST_B_DEB 0x068 0x314 0x000 0x2 0x0
+#define MX50_PAD_WDOG__CCM_XTAL32K 0x068 0x314 0x000 0x6 0x0
+#define MX50_PAD_WDOG__SJC_DONE 0x068 0x314 0x000 0x7 0x0
+#define MX50_PAD_SSI_TXFS__AUDMUX_AUD3_TXFS 0x06c 0x318 0x000 0x0 0x0
+#define MX50_PAD_SSI_TXFS__GPIO6_0 0x06c 0x318 0x000 0x1 0x0
+#define MX50_PAD_SSI_TXFS__SRC_BT_FUSE_RSV_1 0x06c 0x318 0x000 0x6 0x0
+#define MX50_PAD_SSI_TXFS__USBPHY1_DATAOUT_8 0x06c 0x318 0x000 0x7 0x0
+#define MX50_PAD_SSI_TXC__AUDMUX_AUD3_TXC 0x070 0x31c 0x000 0x0 0x0
+#define MX50_PAD_SSI_TXC__GPIO6_1 0x070 0x31c 0x000 0x1 0x0
+#define MX50_PAD_SSI_TXC__SRC_BT_FUSE_RSV_0 0x070 0x31c 0x000 0x6 0x0
+#define MX50_PAD_SSI_TXC__USBPHY1_DATAOUT_9 0x070 0x31c 0x000 0x7 0x0
+#define MX50_PAD_SSI_TXD__AUDMUX_AUD3_TXD 0x074 0x320 0x000 0x0 0x0
+#define MX50_PAD_SSI_TXD__GPIO6_2 0x074 0x320 0x000 0x1 0x0
+#define MX50_PAD_SSI_TXD__CSPI_RDY 0x074 0x320 0x6e8 0x4 0x0
+#define MX50_PAD_SSI_TXD__USBPHY1_DATAOUT_10 0x074 0x320 0x000 0x7 0x0
+#define MX50_PAD_SSI_RXD__AUDMUX_AUD3_RXD 0x078 0x324 0x000 0x0 0x0
+#define MX50_PAD_SSI_RXD__GPIO6_3 0x078 0x324 0x000 0x1 0x0
+#define MX50_PAD_SSI_RXD__CSPI_SS3 0x078 0x324 0x6f4 0x4 0x0
+#define MX50_PAD_SSI_RXD__USBPHY1_DATAOUT_11 0x078 0x324 0x000 0x7 0x0
+#define MX50_PAD_SSI_RXFS__AUDMUX_AUD3_RXFS 0x07c 0x328 0x000 0x0 0x0
+#define MX50_PAD_SSI_RXFS__GPIO6_4 0x07c 0x328 0x000 0x1 0x0
+#define MX50_PAD_SSI_RXFS__UART5_TXD_MUX 0x07c 0x328 0x7e4 0x2 0x0
+#define MX50_PAD_SSI_RXFS__EIM_WEIM_D_6 0x07c 0x328 0x804 0x3 0x0
+#define MX50_PAD_SSI_RXFS__CSPI_SS2 0x07c 0x328 0x6f0 0x4 0x0
+#define MX50_PAD_SSI_RXFS__FEC_COL 0x07c 0x328 0x770 0x5 0x0
+#define MX50_PAD_SSI_RXFS__FEC_MDC 0x07c 0x328 0x000 0x6 0x0
+#define MX50_PAD_SSI_RXFS__USBPHY1_DATAOUT_12 0x07c 0x328 0x000 0x7 0x0
+#define MX50_PAD_SSI_RXC__AUDMUX_AUD3_RXC 0x080 0x32c 0x000 0x0 0x0
+#define MX50_PAD_SSI_RXC__GPIO6_5 0x080 0x32c 0x000 0x1 0x0
+#define MX50_PAD_SSI_RXC__UART5_RXD_MUX 0x080 0x32c 0x7e4 0x2 0x1
+#define MX50_PAD_SSI_RXC__EIM_WEIM_D_7 0x080 0x32c 0x808 0x3 0x0
+#define MX50_PAD_SSI_RXC__CSPI_SS1 0x080 0x32c 0x6ec 0x4 0x0
+#define MX50_PAD_SSI_RXC__FEC_RX_CLK 0x080 0x32c 0x780 0x5 0x0
+#define MX50_PAD_SSI_RXC__FEC_MDIO 0x080 0x32c 0x774 0x6 0x1
+#define MX50_PAD_SSI_RXC__USBPHY1_DATAOUT_13 0x080 0x32c 0x000 0x7 0x0
+#define MX50_PAD_UART1_TXD__UART1_TXD_MUX 0x084 0x330 0x7c4 0x0 0x0
+#define MX50_PAD_UART1_TXD__GPIO6_6 0x084 0x330 0x000 0x1 0x0
+#define MX50_PAD_UART1_TXD__USBPHY1_DATAOUT_14 0x084 0x330 0x000 0x7 0x0
+#define MX50_PAD_UART1_RXD__UART1_RXD_MUX 0x088 0x334 0x7c4 0x0 0x1
+#define MX50_PAD_UART1_RXD__GPIO6_7 0x088 0x334 0x000 0x1 0x0
+#define MX50_PAD_UART1_RXD__USBPHY1_DATAOUT_15 0x088 0x334 0x000 0x7 0x0
+#define MX50_PAD_UART1_CTS__UART1_CTS 0x08c 0x338 0x000 0x0 0x0
+#define MX50_PAD_UART1_CTS__GPIO6_8 0x08c 0x338 0x000 0x1 0x0
+#define MX50_PAD_UART1_CTS__UART5_TXD_MUX 0x08c 0x338 0x7e4 0x2 0x2
+#define MX50_PAD_UART1_CTS__ESDHC4_DAT4 0x08c 0x338 0x760 0x4 0x0
+#define MX50_PAD_UART1_CTS__ESDHC4_CMD 0x08c 0x338 0x74c 0x5 0x0
+#define MX50_PAD_UART1_CTS__USBPHY2_DATAOUT_8 0x08c 0x338 0x000 0x7 0x0
+#define MX50_PAD_UART1_RTS__UART1_RTS 0x090 0x33c 0x7c0 0x0 0x3
+#define MX50_PAD_UART1_RTS__GPIO6_9 0x090 0x33c 0x000 0x1 0x0
+#define MX50_PAD_UART1_RTS__UART5_RXD_MUX 0x090 0x33c 0x7e4 0x2 0x3
+#define MX50_PAD_UART1_RTS__ESDHC4_DAT5 0x090 0x33c 0x764 0x4 0x0
+#define MX50_PAD_UART1_RTS__ESDHC4_CLK 0x090 0x33c 0x748 0x5 0x0
+#define MX50_PAD_UART1_RTS__USBPHY2_DATAOUT_9 0x090 0x33c 0x000 0x7 0x0
+#define MX50_PAD_UART2_TXD__UART2_TXD_MUX 0x094 0x340 0x7cc 0x0 0x2
+#define MX50_PAD_UART2_TXD__GPIO6_10 0x094 0x340 0x000 0x1 0x0
+#define MX50_PAD_UART2_TXD__ESDHC4_DAT6 0x094 0x340 0x768 0x4 0x0
+#define MX50_PAD_UART2_TXD__ESDHC4_DAT4 0x094 0x340 0x760 0x5 0x1
+#define MX50_PAD_UART2_TXD__USBPHY2_DATAOUT_10 0x094 0x340 0x000 0x7 0x0
+#define MX50_PAD_UART2_RXD__UART2_RXD_MUX 0x098 0x344 0x7cc 0x0 0x3
+#define MX50_PAD_UART2_RXD__GPIO6_11 0x098 0x344 0x000 0x1 0x0
+#define MX50_PAD_UART2_RXD__ESDHC4_DAT7 0x098 0x344 0x76c 0x4 0x0
+#define MX50_PAD_UART2_RXD__ESDHC4_DAT5 0x098 0x344 0x764 0x5 0x1
+#define MX50_PAD_UART2_RXD__USBPHY2_DATAOUT_11 0x098 0x344 0x000 0x7 0x0
+#define MX50_PAD_UART2_CTS__UART2_CTS 0x09c 0x348 0x000 0x0 0x0
+#define MX50_PAD_UART2_CTS__GPIO6_12 0x09c 0x348 0x000 0x1 0x0
+#define MX50_PAD_UART2_CTS__ESDHC4_CMD 0x09c 0x348 0x74c 0x4 0x1
+#define MX50_PAD_UART2_CTS__ESDHC4_DAT6 0x09c 0x348 0x768 0x5 0x1
+#define MX50_PAD_UART2_CTS__USBPHY2_DATAOUT_12 0x09c 0x348 0x000 0x7 0x0
+#define MX50_PAD_UART2_RTS__UART2_RTS 0x0a0 0x34c 0x7c8 0x0 0x2
+#define MX50_PAD_UART2_RTS__GPIO6_13 0x0a0 0x34c 0x000 0x1 0x0
+#define MX50_PAD_UART2_RTS__ESDHC4_CLK 0x0a0 0x34c 0x748 0x4 0x1
+#define MX50_PAD_UART2_RTS__ESDHC4_DAT7 0x0a0 0x34c 0x76c 0x5 0x1
+#define MX50_PAD_UART2_RTS__USBPHY2_DATAOUT_13 0x0a0 0x34c 0x000 0x7 0x0
+#define MX50_PAD_UART3_TXD__UART3_TXD_MUX 0x0a4 0x350 0x7d4 0x0 0x0
+#define MX50_PAD_UART3_TXD__GPIO6_14 0x0a4 0x350 0x000 0x1 0x0
+#define MX50_PAD_UART3_TXD__ESDHC1_DAT4 0x0a4 0x350 0x000 0x3 0x0
+#define MX50_PAD_UART3_TXD__ESDHC4_DAT0 0x0a4 0x350 0x000 0x4 0x0
+#define MX50_PAD_UART3_TXD__ESDHC2_WP 0x0a4 0x350 0x744 0x5 0x0
+#define MX50_PAD_UART3_TXD__EIM_WEIM_D_12 0x0a4 0x350 0x81c 0x6 0x0
+#define MX50_PAD_UART3_TXD__USBPHY2_DATAOUT_14 0x0a4 0x350 0x000 0x7 0x0
+#define MX50_PAD_UART3_RXD__UART3_RXD_MUX 0x0a8 0x354 0x7d4 0x0 0x1
+#define MX50_PAD_UART3_RXD__GPIO6_15 0x0a8 0x354 0x000 0x1 0x0
+#define MX50_PAD_UART3_RXD__ESDHC1_DAT5 0x0a8 0x354 0x000 0x3 0x0
+#define MX50_PAD_UART3_RXD__ESDHC4_DAT1 0x0a8 0x354 0x754 0x4 0x0
+#define MX50_PAD_UART3_RXD__ESDHC2_CD 0x0a8 0x354 0x740 0x5 0x0
+#define MX50_PAD_UART3_RXD__EIM_WEIM_D_13 0x0a8 0x354 0x820 0x6 0x0
+#define MX50_PAD_UART3_RXD__USBPHY2_DATAOUT_15 0x0a8 0x354 0x000 0x7 0x0
+#define MX50_PAD_UART4_TXD__UART4_TXD_MUX 0x0ac 0x358 0x7dc 0x0 0x0
+#define MX50_PAD_UART4_TXD__GPIO6_16 0x0ac 0x358 0x000 0x1 0x0
+#define MX50_PAD_UART4_TXD__UART3_CTS 0x0ac 0x358 0x7d0 0x2 0x0
+#define MX50_PAD_UART4_TXD__ESDHC1_DAT6 0x0ac 0x358 0x000 0x3 0x0
+#define MX50_PAD_UART4_TXD__ESDHC4_DAT2 0x0ac 0x358 0x758 0x4 0x0
+#define MX50_PAD_UART4_TXD__ESDHC2_LCTL 0x0ac 0x358 0x000 0x5 0x0
+#define MX50_PAD_UART4_TXD__EIM_WEIM_D_14 0x0ac 0x358 0x824 0x6 0x0
+#define MX50_PAD_UART4_RXD__UART4_RXD_MUX 0x0b0 0x35c 0x7dc 0x0 0x1
+#define MX50_PAD_UART4_RXD__GPIO6_17 0x0b0 0x35c 0x000 0x1 0x0
+#define MX50_PAD_UART4_RXD__UART3_RTS 0x0b0 0x35c 0x7d0 0x2 0x1
+#define MX50_PAD_UART4_RXD__ESDHC1_DAT7 0x0b0 0x35c 0x000 0x3 0x0
+#define MX50_PAD_UART4_RXD__ESDHC4_DAT3 0x0b0 0x35c 0x75c 0x4 0x0
+#define MX50_PAD_UART4_RXD__ESDHC1_LCTL 0x0b0 0x35c 0x000 0x5 0x0
+#define MX50_PAD_UART4_RXD__EIM_WEIM_D_15 0x0b0 0x35c 0x828 0x6 0x0
+#define MX50_PAD_CSPI_SCLK__CSPI_SCLK 0x0b4 0x360 0x000 0x0 0x0
+#define MX50_PAD_CSPI_SCLK__GPIO4_8 0x0b4 0x360 0x000 0x1 0x0
+#define MX50_PAD_CSPI_MOSI__CSPI_MOSI 0x0b8 0x364 0x000 0x0 0x0
+#define MX50_PAD_CSPI_MOSI__GPIO4_9 0x0b8 0x364 0x000 0x1 0x0
+#define MX50_PAD_CSPI_MISO__CSPI_MISO 0x0bc 0x368 0x000 0x0 0x0
+#define MX50_PAD_CSPI_MISO__GPIO4_10 0x0bc 0x368 0x000 0x1 0x0
+#define MX50_PAD_CSPI_SS0__CSPI_SS0 0x0c0 0x36c 0x000 0x0 0x0
+#define MX50_PAD_CSPI_SS0__GPIO4_11 0x0c0 0x36c 0x000 0x1 0x0
+#define MX50_PAD_ECSPI1_SCLK__ECSPI1_SCLK 0x0c4 0x370 0x000 0x0 0x0
+#define MX50_PAD_ECSPI1_SCLK__GPIO4_12 0x0c4 0x370 0x000 0x1 0x0
+#define MX50_PAD_ECSPI1_SCLK__CSPI_RDY 0x0c4 0x370 0x6e8 0x2 0x1
+#define MX50_PAD_ECSPI1_SCLK__ECSPI2_RDY 0x0c4 0x370 0x000 0x3 0x0
+#define MX50_PAD_ECSPI1_SCLK__UART3_RTS 0x0c4 0x370 0x7d0 0x4 0x2
+#define MX50_PAD_ECSPI1_SCLK__EPDC_SDCE_6 0x0c4 0x370 0x000 0x5 0x0
+#define MX50_PAD_ECSPI1_SCLK__EIM_WEIM_D_8 0x0c4 0x370 0x80c 0x7 0x0
+#define MX50_PAD_ECSPI1_MOSI__ECSPI1_MOSI 0x0c8 0x374 0x000 0x0 0x0
+#define MX50_PAD_ECSPI1_MOSI__GPIO4_13 0x0c8 0x374 0x000 0x1 0x0
+#define MX50_PAD_ECSPI1_MOSI__CSPI_SS1 0x0c8 0x374 0x6ec 0x2 0x1
+#define MX50_PAD_ECSPI1_MOSI__ECSPI2_SS1 0x0c8 0x374 0x000 0x3 0x0
+#define MX50_PAD_ECSPI1_MOSI__UART3_CTS 0x0c8 0x374 0x000 0x4 0x0
+#define MX50_PAD_ECSPI1_MOSI__EPDC_SDCE_7 0x0c8 0x374 0x000 0x5 0x0
+#define MX50_PAD_ECSPI1_MOSI__EIM_WEIM_D_9 0x0c8 0x374 0x810 0x7 0x0
+#define MX50_PAD_ECSPI1_MISO__ECSPI1_MISO 0x0cc 0x378 0x000 0x0 0x0
+#define MX50_PAD_ECSPI1_MISO__GPIO4_14 0x0cc 0x378 0x000 0x1 0x0
+#define MX50_PAD_ECSPI1_MISO__CSPI_SS2 0x0cc 0x378 0x6f0 0x2 0x1
+#define MX50_PAD_ECSPI1_MISO__ECSPI2_SS2 0x0cc 0x378 0x000 0x3 0x0
+#define MX50_PAD_ECSPI1_MISO__UART4_RTS 0x0cc 0x378 0x7d8 0x4 0x0
+#define MX50_PAD_ECSPI1_MISO__EPDC_SDCE_8 0x0cc 0x378 0x000 0x5 0x0
+#define MX50_PAD_ECSPI1_MISO__EIM_WEIM_D_10 0x0cc 0x378 0x814 0x7 0x0
+#define MX50_PAD_ECSPI1_SS0__ECSPI1_SS0 0x0d0 0x37c 0x000 0x0 0x0
+#define MX50_PAD_ECSPI1_SS0__GPIO4_15 0x0d0 0x37c 0x000 0x1 0x0
+#define MX50_PAD_ECSPI1_SS0__CSPI_SS3 0x0d0 0x37c 0x6f4 0x2 0x1
+#define MX50_PAD_ECSPI1_SS0__ECSPI2_SS3 0x0d0 0x37c 0x000 0x3 0x0
+#define MX50_PAD_ECSPI1_SS0__UART4_CTS 0x0d0 0x37c 0x000 0x4 0x0
+#define MX50_PAD_ECSPI1_SS0__EPDC_SDCE_9 0x0d0 0x37c 0x000 0x5 0x0
+#define MX50_PAD_ECSPI1_SS0__EIM_WEIM_D_11 0x0d0 0x37c 0x818 0x7 0x0
+#define MX50_PAD_ECSPI2_SCLK__ECSPI2_SCLK 0x0d4 0x380 0x000 0x0 0x0
+#define MX50_PAD_ECSPI2_SCLK__GPIO4_16 0x0d4 0x380 0x000 0x1 0x0
+#define MX50_PAD_ECSPI2_SCLK__ELCDIF_WR_RWN 0x0d4 0x380 0x000 0x2 0x0
+#define MX50_PAD_ECSPI2_SCLK__ECSPI1_RDY 0x0d4 0x380 0x000 0x3 0x0
+#define MX50_PAD_ECSPI2_SCLK__UART5_RTS 0x0d4 0x380 0x7e0 0x4 0x0
+#define MX50_PAD_ECSPI2_SCLK__ELCDIF_DOTCLK 0x0d4 0x380 0x000 0x5 0x0
+#define MX50_PAD_ECSPI2_SCLK__EIM_NANDF_CEN_4 0x0d4 0x380 0x000 0x6 0x0
+#define MX50_PAD_ECSPI2_SCLK__EIM_WEIM_D_8 0x0d4 0x380 0x80c 0x7 0x1
+#define MX50_PAD_ECSPI2_MOSI__ECSPI2_MOSI 0x0d8 0x384 0x000 0x0 0x0
+#define MX50_PAD_ECSPI2_MOSI__GPIO4_17 0x0d8 0x384 0x000 0x1 0x0
+#define MX50_PAD_ECSPI2_MOSI__ELCDIF_RE_E 0x0d8 0x384 0x000 0x2 0x0
+#define MX50_PAD_ECSPI2_MOSI__ECSPI1_SS1 0x0d8 0x384 0x000 0x3 0x0
+#define MX50_PAD_ECSPI2_MOSI__UART5_CTS 0x0d8 0x384 0x7e0 0x4 0x1
+#define MX50_PAD_ECSPI2_MOSI__ELCDIF_ENABLE 0x0d8 0x384 0x000 0x5 0x0
+#define MX50_PAD_ECSPI2_MOSI__EIM_NANDF_CEN_5 0x0d8 0x384 0x000 0x6 0x0
+#define MX50_PAD_ECSPI2_MOSI__EIM_WEIM_D_9 0x0d8 0x384 0x810 0x7 0x1
+#define MX50_PAD_ECSPI2_MISO__ECSPI2_MISO 0x0dc 0x388 0x000 0x0 0x0
+#define MX50_PAD_ECSPI2_MISO__GPIO4_18 0x0dc 0x388 0x000 0x1 0x0
+#define MX50_PAD_ECSPI2_MISO__ELCDIF_RS 0x0dc 0x388 0x000 0x2 0x0
+#define MX50_PAD_ECSPI2_MISO__ECSPI1_SS2 0x0dc 0x388 0x000 0x3 0x0
+#define MX50_PAD_ECSPI2_MISO__UART5_TXD_MUX 0x0dc 0x388 0x7e4 0x4 0x4
+#define MX50_PAD_ECSPI2_MISO__ELCDIF_VSYNC 0x0dc 0x388 0x73c 0x5 0x0
+#define MX50_PAD_ECSPI2_MISO__EIM_NANDF_CEN_6 0x0dc 0x388 0x000 0x6 0x0
+#define MX50_PAD_ECSPI2_MISO__EIM_WEIM_D_10 0x0dc 0x388 0x814 0x7 0x1
+#define MX50_PAD_ECSPI2_SS0__ECSPI2_SS0 0x0e0 0x38c 0x000 0x0 0x0
+#define MX50_PAD_ECSPI2_SS0__GPIO4_19 0x0e0 0x38c 0x000 0x1 0x0
+#define MX50_PAD_ECSPI2_SS0__ELCDIF_CS 0x0e0 0x38c 0x000 0x2 0x0
+#define MX50_PAD_ECSPI2_SS0__ECSPI2_SS3 0x0e0 0x38c 0x000 0x3 0x0
+#define MX50_PAD_ECSPI2_SS0__UART5_RXD_MUX 0x0e0 0x38c 0x7e4 0x4 0x5
+#define MX50_PAD_ECSPI2_SS0__ELCDIF_HSYNC 0x0e0 0x38c 0x6f8 0x5 0x0
+#define MX50_PAD_ECSPI2_SS0__EIM_NANDF_CEN_7 0x0e0 0x38c 0x000 0x6 0x0
+#define MX50_PAD_ECSPI2_SS0__EIM_WEIM_D_11 0x0e0 0x38c 0x818 0x7 0x1
+#define MX50_PAD_SD1_CLK__ESDHC1_CLK 0x0e4 0x390 0x000 0x0 0x0
+#define MX50_PAD_SD1_CLK__GPIO5_0 0x0e4 0x390 0x000 0x1 0x0
+#define MX50_PAD_SD1_CLK__CCM_CLKO 0x0e4 0x390 0x000 0x7 0x0
+#define MX50_PAD_SD1_CMD__ESDHC1_CMD 0x0e8 0x394 0x000 0x0 0x0
+#define MX50_PAD_SD1_CMD__GPIO5_1 0x0e8 0x394 0x000 0x1 0x0
+#define MX50_PAD_SD1_CMD__CCM_CLKO2 0x0e8 0x394 0x000 0x7 0x0
+#define MX50_PAD_SD1_D0__ESDHC1_DAT0 0x0ec 0x398 0x000 0x0 0x0
+#define MX50_PAD_SD1_D0__GPIO5_2 0x0ec 0x398 0x000 0x1 0x0
+#define MX50_PAD_SD1_D0__CCM_PLL1_BYP 0x0ec 0x398 0x6dc 0x7 0x0
+#define MX50_PAD_SD1_D1__ESDHC1_DAT1 0x0f0 0x39c 0x000 0x0 0x0
+#define MX50_PAD_SD1_D1__GPIO5_3 0x0f0 0x39c 0x000 0x1 0x0
+#define MX50_PAD_SD1_D1__CCM_PLL2_BYP 0x0f0 0x39c 0x000 0x7 0x0
+#define MX50_PAD_SD1_D2__ESDHC1_DAT2 0x0f4 0x3a0 0x000 0x0 0x0
+#define MX50_PAD_SD1_D2__GPIO5_4 0x0f4 0x3a0 0x000 0x1 0x0
+#define MX50_PAD_SD1_D2__CCM_PLL3_BYP 0x0f4 0x3a0 0x6e4 0x7 0x0
+#define MX50_PAD_SD1_D3__ESDHC1_DAT3 0x0f8 0x3a4 0x000 0x0 0x0
+#define MX50_PAD_SD1_D3__GPIO5_5 0x0f8 0x3a4 0x000 0x1 0x0
+#define MX50_PAD_SD2_CLK__ESDHC2_CLK 0x0fc 0x3a8 0x000 0x0 0x0
+#define MX50_PAD_SD2_CLK__GPIO5_6 0x0fc 0x3a8 0x000 0x1 0x0
+#define MX50_PAD_SD2_CLK__MSHC_SCLK 0x0fc 0x3a8 0x000 0x2 0x0
+#define MX50_PAD_SD2_CMD__ESDHC2_CMD 0x100 0x3ac 0x000 0x0 0x0
+#define MX50_PAD_SD2_CMD__GPIO5_7 0x100 0x3ac 0x000 0x1 0x0
+#define MX50_PAD_SD2_CMD__MSHC_BS 0x100 0x3ac 0x000 0x2 0x0
+#define MX50_PAD_SD2_D0__ESDHC2_DAT0 0x104 0x3b0 0x000 0x0 0x0
+#define MX50_PAD_SD2_D0__GPIO5_8 0x104 0x3b0 0x000 0x1 0x0
+#define MX50_PAD_SD2_D0__MSHC_DATA_0 0x104 0x3b0 0x000 0x2 0x0
+#define MX50_PAD_SD2_D0__KPP_COL_4 0x104 0x3b0 0x790 0x3 0x0
+#define MX50_PAD_SD2_D1__ESDHC2_DAT1 0x108 0x3b4 0x000 0x0 0x0
+#define MX50_PAD_SD2_D1__GPIO5_9 0x108 0x3b4 0x000 0x1 0x0
+#define MX50_PAD_SD2_D1__MSHC_DATA_1 0x108 0x3b4 0x000 0x2 0x0
+#define MX50_PAD_SD2_D1__KPP_ROW_4 0x108 0x3b4 0x7a0 0x3 0x0
+#define MX50_PAD_SD2_D2__ESDHC2_DAT2 0x10c 0x3b8 0x000 0x0 0x0
+#define MX50_PAD_SD2_D2__GPIO5_10 0x10c 0x3b8 0x000 0x1 0x0
+#define MX50_PAD_SD2_D2__MSHC_DATA_2 0x10c 0x3b8 0x000 0x2 0x0
+#define MX50_PAD_SD2_D2__KPP_COL_5 0x10c 0x3b8 0x794 0x3 0x0
+#define MX50_PAD_SD2_D3__ESDHC2_DAT3 0x110 0x3bc 0x000 0x0 0x0
+#define MX50_PAD_SD2_D3__GPIO5_11 0x110 0x3bc 0x000 0x1 0x0
+#define MX50_PAD_SD2_D3__MSHC_DATA_3 0x110 0x3bc 0x000 0x2 0x0
+#define MX50_PAD_SD2_D3__KPP_ROW_5 0x110 0x3bc 0x7a4 0x3 0x0
+#define MX50_PAD_SD2_D4__ESDHC2_DAT4 0x114 0x3c0 0x000 0x0 0x0
+#define MX50_PAD_SD2_D4__GPIO5_12 0x114 0x3c0 0x000 0x1 0x0
+#define MX50_PAD_SD2_D4__AUDMUX_AUD4_RXFS 0x114 0x3c0 0x6d0 0x2 0x0
+#define MX50_PAD_SD2_D4__KPP_COL_6 0x114 0x3c0 0x798 0x3 0x0
+#define MX50_PAD_SD2_D4__EIM_WEIM_D_0 0x114 0x3c0 0x7ec 0x4 0x0
+#define MX50_PAD_SD2_D4__CCM_CCM_OUT_0 0x114 0x3c0 0x000 0x7 0x0
+#define MX50_PAD_SD2_D5__ESDHC2_DAT5 0x118 0x3c4 0x000 0x0 0x0
+#define MX50_PAD_SD2_D5__GPIO5_13 0x118 0x3c4 0x000 0x1 0x0
+#define MX50_PAD_SD2_D5__AUDMUX_AUD4_RXC 0x118 0x3c4 0x6cc 0x2 0x0
+#define MX50_PAD_SD2_D5__KPP_ROW_6 0x118 0x3c4 0x7a8 0x3 0x0
+#define MX50_PAD_SD2_D5__EIM_WEIM_D_1 0x118 0x3c4 0x7f0 0x4 0x0
+#define MX50_PAD_SD2_D5__CCM_CCM_OUT_1 0x118 0x3c4 0x000 0x7 0x0
+#define MX50_PAD_SD2_D6__ESDHC2_DAT6 0x11c 0x3c8 0x000 0x0 0x0
+#define MX50_PAD_SD2_D6__GPIO5_14 0x11c 0x3c8 0x000 0x1 0x0
+#define MX50_PAD_SD2_D6__AUDMUX_AUD4_RXD 0x11c 0x3c8 0x6c4 0x2 0x0
+#define MX50_PAD_SD2_D6__KPP_COL_7 0x11c 0x3c8 0x79c 0x3 0x0
+#define MX50_PAD_SD2_D6__EIM_WEIM_D_2 0x11c 0x3c8 0x7f4 0x4 0x0
+#define MX50_PAD_SD2_D6__CCM_CCM_OUT_2 0x11c 0x3c8 0x000 0x7 0x0
+#define MX50_PAD_SD2_D7__ESDHC2_DAT7 0x120 0x3cc 0x000 0x0 0x0
+#define MX50_PAD_SD2_D7__GPIO5_15 0x120 0x3cc 0x000 0x1 0x0
+#define MX50_PAD_SD2_D7__AUDMUX_AUD4_TXFS 0x120 0x3cc 0x6d8 0x2 0x0
+#define MX50_PAD_SD2_D7__KPP_ROW_7 0x120 0x3cc 0x7ac 0x3 0x0
+#define MX50_PAD_SD2_D7__EIM_WEIM_D_3 0x120 0x3cc 0x7f8 0x4 0x0
+#define MX50_PAD_SD2_D7__CCM_STOP 0x120 0x3cc 0x000 0x7 0x0
+#define MX50_PAD_SD2_WP__ESDHC2_WP 0x124 0x3d0 0x744 0x0 0x1
+#define MX50_PAD_SD2_WP__GPIO5_16 0x124 0x3d0 0x000 0x1 0x0
+#define MX50_PAD_SD2_WP__AUDMUX_AUD4_TXD 0x124 0x3d0 0x6c8 0x2 0x0
+#define MX50_PAD_SD2_WP__EIM_WEIM_D_4 0x124 0x3d0 0x7fc 0x4 0x0
+#define MX50_PAD_SD2_WP__CCM_WAIT 0x124 0x3d0 0x000 0x7 0x0
+#define MX50_PAD_SD2_CD__ESDHC2_CD 0x128 0x3d4 0x740 0x0 0x1
+#define MX50_PAD_SD2_CD__GPIO5_17 0x128 0x3d4 0x000 0x1 0x0
+#define MX50_PAD_SD2_CD__AUDMUX_AUD4_TXC 0x128 0x3d4 0x6d4 0x2 0x0
+#define MX50_PAD_SD2_CD__EIM_WEIM_D_5 0x128 0x3d4 0x800 0x4 0x0
+#define MX50_PAD_SD2_CD__CCM_REF_EN_B 0x128 0x3d4 0x000 0x7 0x0
+#define MX50_PAD_DISP_D0__ELCDIF_DAT_0 0x12c 0x40c 0x6fc 0x0 0x0
+#define MX50_PAD_DISP_D0__GPIO2_0 0x12c 0x40c 0x000 0x1 0x0
+#define MX50_PAD_DISP_D0__FEC_TX_CLK 0x12c 0x40c 0x78c 0x2 0x0
+#define MX50_PAD_DISP_D0__EIM_WEIM_A_16 0x12c 0x40c 0x000 0x3 0x0
+#define MX50_PAD_DISP_D0__SDMA_DEBUG_PC_0 0x12c 0x40c 0x000 0x6 0x0
+#define MX50_PAD_DISP_D0__USBPHY1_VSTATUS_0 0x12c 0x40c 0x000 0x7 0x0
+#define MX50_PAD_DISP_D1__ELCDIF_DAT_1 0x130 0x410 0x700 0x0 0x0
+#define MX50_PAD_DISP_D1__GPIO2_1 0x130 0x410 0x000 0x1 0x0
+#define MX50_PAD_DISP_D1__FEC_RX_ERR 0x130 0x410 0x788 0x2 0x0
+#define MX50_PAD_DISP_D1__EIM_WEIM_A_17 0x130 0x410 0x000 0x3 0x0
+#define MX50_PAD_DISP_D1__SDMA_DEBUG_PC_1 0x130 0x410 0x000 0x6 0x0
+#define MX50_PAD_DISP_D1__USBPHY1_VSTATUS_1 0x130 0x410 0x000 0x7 0x0
+#define MX50_PAD_DISP_D2__ELCDIF_DAT_2 0x134 0x414 0x704 0x0 0x0
+#define MX50_PAD_DISP_D2__GPIO2_2 0x134 0x414 0x000 0x1 0x0
+#define MX50_PAD_DISP_D2__FEC_RX_DV 0x134 0x414 0x784 0x2 0x0
+#define MX50_PAD_DISP_D2__EIM_WEIM_A_18 0x134 0x414 0x000 0x3 0x0
+#define MX50_PAD_DISP_D2__SDMA_DEBUG_PC_2 0x134 0x414 0x000 0x6 0x0
+#define MX50_PAD_DISP_D2__USBPHY1_VSTATUS_2 0x134 0x414 0x000 0x7 0x0
+#define MX50_PAD_DISP_D3__ELCDIF_DAT_3 0x138 0x418 0x708 0x0 0x0
+#define MX50_PAD_DISP_D3__GPIO2_3 0x138 0x418 0x000 0x1 0x0
+#define MX50_PAD_DISP_D3__FEC_RDATA_1 0x138 0x418 0x77c 0x2 0x0
+#define MX50_PAD_DISP_D3__EIM_WEIM_A_19 0x138 0x418 0x000 0x3 0x0
+#define MX50_PAD_DISP_D3__FEC_COL 0x138 0x418 0x770 0x4 0x1
+#define MX50_PAD_DISP_D3__SDMA_DEBUG_PC_3 0x138 0x418 0x000 0x6 0x0
+#define MX50_PAD_DISP_D3__USBPHY1_VSTATUS_3 0x138 0x418 0x000 0x7 0x0
+#define MX50_PAD_DISP_D4__ELCDIF_DAT_4 0x13c 0x41c 0x70c 0x0 0x0
+#define MX50_PAD_DISP_D4__GPIO2_4 0x13c 0x41c 0x000 0x1 0x0
+#define MX50_PAD_DISP_D4__FEC_RDATA_0 0x13c 0x41c 0x778 0x2 0x0
+#define MX50_PAD_DISP_D4__EIM_WEIM_A_20 0x13c 0x41c 0x000 0x3 0x0
+#define MX50_PAD_DISP_D4__SDMA_DEBUG_PC_4 0x13c 0x41c 0x000 0x6 0x0
+#define MX50_PAD_DISP_D4__USBPHY1_VSTATUS_4 0x13c 0x41c 0x000 0x7 0x0
+#define MX50_PAD_DISP_D5__ELCDIF_DAT_5 0x140 0x420 0x710 0x0 0x0
+#define MX50_PAD_DISP_D5__GPIO2_5 0x140 0x420 0x000 0x1 0x0
+#define MX50_PAD_DISP_D5__FEC_TX_EN 0x140 0x420 0x000 0x2 0x0
+#define MX50_PAD_DISP_D5__EIM_WEIM_A_21 0x140 0x420 0x000 0x3 0x0
+#define MX50_PAD_DISP_D5__SDMA_DEBUG_PC_5 0x140 0x420 0x000 0x6 0x0
+#define MX50_PAD_DISP_D5__USBPHY1_VSTATUS_5 0x140 0x420 0x000 0x7 0x0
+#define MX50_PAD_DISP_D6__ELCDIF_DAT_6 0x144 0x424 0x714 0x0 0x0
+#define MX50_PAD_DISP_D6__GPIO2_6 0x144 0x424 0x000 0x1 0x0
+#define MX50_PAD_DISP_D6__FEC_TDATA_1 0x144 0x424 0x000 0x2 0x0
+#define MX50_PAD_DISP_D6__EIM_WEIM_A_22 0x144 0x424 0x000 0x3 0x0
+#define MX50_PAD_DISP_D6__FEC_RX_CLK 0x144 0x424 0x780 0x4 0x1
+#define MX50_PAD_DISP_D6__SDMA_DEBUG_PC_6 0x144 0x424 0x000 0x6 0x0
+#define MX50_PAD_DISP_D6__USBPHY1_VSTATUS_6 0x144 0x424 0x000 0x7 0x0
+#define MX50_PAD_DISP_D7__ELCDIF_DAT_7 0x148 0x428 0x718 0x0 0x0
+#define MX50_PAD_DISP_D7__GPIO2_7 0x148 0x428 0x000 0x1 0x0
+#define MX50_PAD_DISP_D7__FEC_TDATA_0 0x148 0x428 0x000 0x2 0x0
+#define MX50_PAD_DISP_D7__EIM_WEIM_A_23 0x148 0x428 0x000 0x3 0x0
+#define MX50_PAD_DISP_D7__SDMA_DEBUG_PC_7 0x148 0x428 0x000 0x6 0x0
+#define MX50_PAD_DISP_D7__USBPHY1_VSTATUS_7 0x148 0x428 0x000 0x7 0x0
+#define MX50_PAD_DISP_WR__ELCDIF_WR_RWN 0x14c 0x42c 0x000 0x0 0x0
+#define MX50_PAD_DISP_WR__GPIO2_16 0x14c 0x42c 0x000 0x1 0x0
+#define MX50_PAD_DISP_WR__ELCDIF_DOTCLK 0x14c 0x42c 0x000 0x2 0x0
+#define MX50_PAD_DISP_WR__EIM_WEIM_A_24 0x14c 0x42c 0x000 0x3 0x0
+#define MX50_PAD_DISP_WR__SDMA_DEBUG_PC_8 0x14c 0x42c 0x000 0x6 0x0
+#define MX50_PAD_DISP_WR__USBPHY1_AVALID 0x14c 0x42c 0x000 0x7 0x0
+#define MX50_PAD_DISP_RD__ELCDIF_RD_E 0x150 0x430 0x000 0x0 0x0
+#define MX50_PAD_DISP_RD__GPIO2_19 0x150 0x430 0x000 0x1 0x0
+#define MX50_PAD_DISP_RD__ELCDIF_ENABLE 0x150 0x430 0x000 0x2 0x0
+#define MX50_PAD_DISP_RD__EIM_WEIM_A_25 0x150 0x430 0x000 0x3 0x0
+#define MX50_PAD_DISP_RD__SDMA_DEBUG_PC_9 0x150 0x430 0x000 0x6 0x0
+#define MX50_PAD_DISP_RD__USBPHY1_BVALID 0x150 0x430 0x000 0x7 0x0
+#define MX50_PAD_DISP_RS__ELCDIF_RS 0x154 0x434 0x000 0x0 0x0
+#define MX50_PAD_DISP_RS__GPIO2_17 0x154 0x434 0x000 0x1 0x0
+#define MX50_PAD_DISP_RS__ELCDIF_VSYNC 0x154 0x434 0x73c 0x2 0x1
+#define MX50_PAD_DISP_RS__EIM_WEIM_A_26 0x154 0x434 0x000 0x3 0x0
+#define MX50_PAD_DISP_RS__SDMA_DEBUG_PC_10 0x154 0x434 0x000 0x6 0x0
+#define MX50_PAD_DISP_RS__USBPHY1_ENDSESSION 0x154 0x434 0x000 0x7 0x0
+#define MX50_PAD_DISP_CS__ELCDIF_CS 0x158 0x438 0x000 0x0 0x0
+#define MX50_PAD_DISP_CS__GPIO2_21 0x158 0x438 0x000 0x1 0x0
+#define MX50_PAD_DISP_CS__ELCDIF_HSYNC 0x158 0x438 0x6f8 0x2 0x1
+#define MX50_PAD_DISP_CS__EIM_WEIM_A_27 0x158 0x438 0x000 0x3 0x0
+#define MX50_PAD_DISP_CS__EIM_WEIM_CS_3 0x158 0x438 0x000 0x4 0x0
+#define MX50_PAD_DISP_CS__SDMA_DEBUG_PC_11 0x158 0x438 0x000 0x6 0x0
+#define MX50_PAD_DISP_CS__USBPHY1_IDDIG 0x158 0x438 0x000 0x7 0x0
+#define MX50_PAD_DISP_BUSY__ELCDIF_BUSY 0x15c 0x43c 0x6f8 0x0 0x2
+#define MX50_PAD_DISP_BUSY__GPIO2_18 0x15c 0x43c 0x000 0x1 0x0
+#define MX50_PAD_DISP_BUSY__EIM_WEIM_CS_3 0x15c 0x43c 0x000 0x4 0x0
+#define MX50_PAD_DISP_BUSY__SDMA_DEBUG_PC_12 0x15c 0x43c 0x000 0x6 0x0
+#define MX50_PAD_DISP_BUSY__USBPHY2_HOSTDISCONNECT 0x15c 0x43c 0x000 0x7 0x0
+#define MX50_PAD_DISP_RESET__ELCDIF_RESET 0x160 0x440 0x000 0x0 0x0
+#define MX50_PAD_DISP_RESET__GPIO2_20 0x160 0x440 0x000 0x1 0x0
+#define MX50_PAD_DISP_RESET__EIM_WEIM_CS_3 0x160 0x440 0x000 0x4 0x0
+#define MX50_PAD_DISP_RESET__SDMA_DEBUG_PC_13 0x160 0x440 0x000 0x6 0x0
+#define MX50_PAD_DISP_RESET__USBPHY2_BISTOK 0x160 0x440 0x000 0x7 0x0
+#define MX50_PAD_SD3_CMD__ESDHC3_CMD 0x164 0x444 0x000 0x0 0x0
+#define MX50_PAD_SD3_CMD__GPIO5_18 0x164 0x444 0x000 0x1 0x0
+#define MX50_PAD_SD3_CMD__EIM_NANDF_WRN 0x164 0x444 0x000 0x2 0x0
+#define MX50_PAD_SD3_CMD__SSP_CMD 0x164 0x444 0x000 0x3 0x0
+#define MX50_PAD_SD3_CLK__ESDHC3_CLK 0x168 0x448 0x000 0x0 0x0
+#define MX50_PAD_SD3_CLK__GPIO5_19 0x168 0x448 0x000 0x1 0x0
+#define MX50_PAD_SD3_CLK__EIM_NANDF_RDN 0x168 0x448 0x000 0x2 0x0
+#define MX50_PAD_SD3_CLK__SSP_CLK 0x168 0x448 0x000 0x3 0x0
+#define MX50_PAD_SD3_D0__ESDHC3_DAT0 0x16c 0x44c 0x000 0x0 0x0
+#define MX50_PAD_SD3_D0__GPIO5_20 0x16c 0x44c 0x000 0x1 0x0
+#define MX50_PAD_SD3_D0__EIM_NANDF_D_4 0x16c 0x44c 0x000 0x2 0x0
+#define MX50_PAD_SD3_D0__SSP_D0 0x16c 0x44c 0x000 0x3 0x0
+#define MX50_PAD_SD3_D0__CCM_PLL1_BYP 0x16c 0x44c 0x6dc 0x7 0x1
+#define MX50_PAD_SD3_D1__ESDHC3_DAT1 0x170 0x450 0x000 0x0 0x0
+#define MX50_PAD_SD3_D1__GPIO5_21 0x170 0x450 0x000 0x1 0x0
+#define MX50_PAD_SD3_D1__EIM_NANDF_D_5 0x170 0x450 0x000 0x2 0x0
+#define MX50_PAD_SD3_D1__SSP_D1 0x170 0x450 0x000 0x3 0x0
+#define MX50_PAD_SD3_D1__CCM_PLL2_BYP 0x170 0x450 0x000 0x7 0x0
+#define MX50_PAD_SD3_D2__ESDHC3_DAT2 0x174 0x454 0x000 0x0 0x0
+#define MX50_PAD_SD3_D2__GPIO5_22 0x174 0x454 0x000 0x1 0x0
+#define MX50_PAD_SD3_D2__EIM_NANDF_D_6 0x174 0x454 0x000 0x2 0x0
+#define MX50_PAD_SD3_D2__SSP_D2 0x174 0x454 0x000 0x3 0x0
+#define MX50_PAD_SD3_D2__CCM_PLL3_BYP 0x174 0x454 0x6e4 0x7 0x1
+#define MX50_PAD_SD3_D3__ESDHC3_DAT3 0x178 0x458 0x000 0x0 0x0
+#define MX50_PAD_SD3_D3__GPIO5_23 0x178 0x458 0x000 0x1 0x0
+#define MX50_PAD_SD3_D3__EIM_NANDF_D_7 0x178 0x458 0x000 0x2 0x0
+#define MX50_PAD_SD3_D3__SSP_D3 0x178 0x458 0x000 0x3 0x0
+#define MX50_PAD_SD3_D4__ESDHC3_DAT4 0x17c 0x45c 0x000 0x0 0x0
+#define MX50_PAD_SD3_D4__GPIO5_24 0x17c 0x45c 0x000 0x1 0x0
+#define MX50_PAD_SD3_D4__EIM_NANDF_D_0 0x17c 0x45c 0x000 0x2 0x0
+#define MX50_PAD_SD3_D4__SSP_D4 0x17c 0x45c 0x000 0x3 0x0
+#define MX50_PAD_SD3_D5__ESDHC3_DAT5 0x180 0x460 0x000 0x0 0x0
+#define MX50_PAD_SD3_D5__GPIO5_25 0x180 0x460 0x000 0x1 0x0
+#define MX50_PAD_SD3_D5__EIM_NANDF_D_1 0x180 0x460 0x000 0x2 0x0
+#define MX50_PAD_SD3_D5__SSP_D5 0x180 0x460 0x000 0x3 0x0
+#define MX50_PAD_SD3_D6__ESDHC3_DAT6 0x184 0x464 0x000 0x0 0x0
+#define MX50_PAD_SD3_D6__GPIO5_26 0x184 0x464 0x000 0x1 0x0
+#define MX50_PAD_SD3_D6__EIM_NANDF_D_2 0x184 0x464 0x000 0x2 0x0
+#define MX50_PAD_SD3_D6__SSP_D6 0x184 0x464 0x000 0x3 0x0
+#define MX50_PAD_SD3_D7__ESDHC3_DAT7 0x188 0x468 0x000 0x0 0x0
+#define MX50_PAD_SD3_D7__GPIO5_27 0x188 0x468 0x000 0x1 0x0
+#define MX50_PAD_SD3_D7__EIM_NANDF_D_3 0x188 0x468 0x000 0x2 0x0
+#define MX50_PAD_SD3_D7__SSP_D7 0x188 0x468 0x000 0x3 0x0
+#define MX50_PAD_SD3_WP__ESDHC3_WP 0x18c 0x46C 0x000 0x0 0x0
+#define MX50_PAD_SD3_WP__GPIO5_28 0x18c 0x46C 0x000 0x1 0x0
+#define MX50_PAD_SD3_WP__EIM_NANDF_RESETN 0x18c 0x46C 0x000 0x2 0x0
+#define MX50_PAD_SD3_WP__SSP_CD 0x18c 0x46C 0x000 0x3 0x0
+#define MX50_PAD_SD3_WP__ESDHC4_LCTL 0x18c 0x46C 0x000 0x4 0x0
+#define MX50_PAD_SD3_WP__EIM_WEIM_CS_3 0x18c 0x46C 0x000 0x5 0x0
+#define MX50_PAD_DISP_D8__ELCDIF_DAT_8 0x190 0x470 0x71c 0x0 0x0
+#define MX50_PAD_DISP_D8__GPIO2_8 0x190 0x470 0x000 0x1 0x0
+#define MX50_PAD_DISP_D8__EIM_NANDF_CLE 0x190 0x470 0x000 0x2 0x0
+#define MX50_PAD_DISP_D8__ESDHC1_LCTL 0x190 0x470 0x000 0x3 0x0
+#define MX50_PAD_DISP_D8__ESDHC4_CMD 0x190 0x470 0x74c 0x4 0x2
+#define MX50_PAD_DISP_D8__KPP_COL_4 0x190 0x470 0x790 0x5 0x1
+#define MX50_PAD_DISP_D8__FEC_TX_CLK 0x190 0x470 0x78c 0x6 0x1
+#define MX50_PAD_DISP_D8__USBPHY1_DATAOUT_0 0x190 0x470 0x000 0x7 0x0
+#define MX50_PAD_DISP_D9__ELCDIF_DAT_9 0x194 0x474 0x720 0x0 0x0
+#define MX50_PAD_DISP_D9__GPIO2_9 0x194 0x474 0x000 0x1 0x0
+#define MX50_PAD_DISP_D9__EIM_NANDF_ALE 0x194 0x474 0x000 0x2 0x0
+#define MX50_PAD_DISP_D9__ESDHC2_LCTL 0x194 0x474 0x000 0x3 0x0
+#define MX50_PAD_DISP_D9__ESDHC4_CLK 0x194 0x474 0x748 0x4 0x2
+#define MX50_PAD_DISP_D9__KPP_ROW_4 0x194 0x474 0x7a0 0x5 0x1
+#define MX50_PAD_DISP_D9__FEC_RX_ER 0x194 0x474 0x788 0x6 0x1
+#define MX50_PAD_DISP_D9__USBPHY1_DATAOUT_1 0x194 0x474 0x000 0x7 0x0
+#define MX50_PAD_DISP_D10__ELCDIF_DAT_10 0x198 0x478 0x724 0x0 0x0
+#define MX50_PAD_DISP_D10__GPIO2_10 0x198 0x478 0x000 0x1 0x0
+#define MX50_PAD_DISP_D10__EIM_NANDF_CEN_0 0x198 0x478 0x000 0x2 0x0
+#define MX50_PAD_DISP_D10__ESDHC3_LCTL 0x198 0x478 0x000 0x3 0x0
+#define MX50_PAD_DISP_D10__ESDHC4_DAT0 0x198 0x478 0x000 0x4 0x0
+#define MX50_PAD_DISP_D10__KPP_COL_5 0x198 0x478 0x794 0x5 0x1
+#define MX50_PAD_DISP_D10__FEC_RX_DV 0x198 0x478 0x784 0x6 0x1
+#define MX50_PAD_DISP_D10__USBPHY1_DATAOUT_2 0x198 0x478 0x000 0x7 0x0
+#define MX50_PAD_DISP_D11__ELCDIF_DAT_11 0x19c 0x47c 0x728 0x0 0x0
+#define MX50_PAD_DISP_D11__GPIO2_11 0x19c 0x47c 0x000 0x1 0x0
+#define MX50_PAD_DISP_D11__EIM_NANDF_CEN_1 0x19c 0x47c 0x000 0x2 0x0
+#define MX50_PAD_DISP_D11__ESDHC4_DAT1 0x19c 0x47c 0x754 0x4 0x1
+#define MX50_PAD_DISP_D11__KPP_ROW_5 0x19c 0x47c 0x7a4 0x5 0x1
+#define MX50_PAD_DISP_D11__FEC_RDATA_1 0x19c 0x47c 0x77c 0x6 0x1
+#define MX50_PAD_DISP_D11__USBPHY1_DATAOUT_3 0x19c 0x47c 0x000 0x7 0x0
+#define MX50_PAD_DISP_D12__ELCDIF_DAT_12 0x1a0 0x480 0x72c 0x0 0x0
+#define MX50_PAD_DISP_D12__GPIO2_12 0x1a0 0x480 0x000 0x1 0x0
+#define MX50_PAD_DISP_D12__EIM_NANDF_CEN_2 0x1a0 0x480 0x000 0x2 0x0
+#define MX50_PAD_DISP_D12__ESDHC1_CD 0x1a0 0x480 0x000 0x3 0x0
+#define MX50_PAD_DISP_D12__ESDHC4_DAT2 0x1a0 0x480 0x758 0x4 0x1
+#define MX50_PAD_DISP_D12__KPP_COL_6 0x1a0 0x480 0x798 0x5 0x1
+#define MX50_PAD_DISP_D12__FEC_RDATA_0 0x1a0 0x480 0x778 0x6 0x1
+#define MX50_PAD_DISP_D12__USBPHY1_DATAOUT_4 0x1a0 0x480 0x000 0x7 0x0
+#define MX50_PAD_DISP_D13__ELCDIF_DAT_13 0x1a4 0x484 0x730 0x0 0x0
+#define MX50_PAD_DISP_D13__GPIO2_13 0x1a4 0x484 0x000 0x1 0x0
+#define MX50_PAD_DISP_D13__EIM_NANDF_CEN_3 0x1a4 0x484 0x000 0x2 0x0
+#define MX50_PAD_DISP_D13__ESDHC3_CD 0x1a4 0x484 0x000 0x3 0x0
+#define MX50_PAD_DISP_D13__ESDHC4_DAT3 0x1a4 0x484 0x75c 0x4 0x1
+#define MX50_PAD_DISP_D13__KPP_ROW_6 0x1a4 0x484 0x7a8 0x5 0x1
+#define MX50_PAD_DISP_D13__FEC_TX_EN 0x1a4 0x484 0x000 0x6 0x0
+#define MX50_PAD_DISP_D13__USBPHY1_DATAOUT_5 0x1a4 0x484 0x000 0x7 0x0
+#define MX50_PAD_DISP_D14__ELCDIF_DAT_14 0x1a8 0x488 0x734 0x0 0x0
+#define MX50_PAD_DISP_D14__GPIO2_14 0x1a8 0x488 0x000 0x1 0x0
+#define MX50_PAD_DISP_D14__EIM_NANDF_READY0 0x1a8 0x488 0x7b4 0x2 0x1
+#define MX50_PAD_DISP_D14__ESDHC1_WP 0x1a8 0x488 0x000 0x3 0x0
+#define MX50_PAD_DISP_D14__ESDHC4_WP 0x1a8 0x488 0x000 0x4 0x0
+#define MX50_PAD_DISP_D14__KPP_COL_7 0x1a8 0x488 0x79c 0x5 0x1
+#define MX50_PAD_DISP_D14__FEC_TDATA_1 0x1a8 0x488 0x000 0x6 0x0
+#define MX50_PAD_DISP_D14__USBPHY1_DATAOUT_6 0x1a8 0x488 0x000 0x7 0x0
+#define MX50_PAD_DISP_D15__ELCDIF_DAT_15 0x1ac 0x48c 0x738 0x0 0x0
+#define MX50_PAD_DISP_D15__GPIO2_15 0x1ac 0x48c 0x000 0x1 0x0
+#define MX50_PAD_DISP_D15__EIM_NANDF_DQS 0x1ac 0x48c 0x7b0 0x2 0x1
+#define MX50_PAD_DISP_D15__ESDHC3_RST 0x1ac 0x48c 0x000 0x3 0x0
+#define MX50_PAD_DISP_D15__ESDHC4_CD 0x1ac 0x48c 0x000 0x4 0x0
+#define MX50_PAD_DISP_D15__KPP_ROW_7 0x1ac 0x48c 0x7ac 0x5 0x1
+#define MX50_PAD_DISP_D15__FEC_TDATA_0 0x1ac 0x48c 0x000 0x6 0x0
+#define MX50_PAD_DISP_D15__USBPHY1_DATAOUT_7 0x1ac 0x48c 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D0__EPDC_SDDO_0 0x1b0 0x54c 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D0__GPIO3_0 0x1b0 0x54c 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D0__EIM_WEIM_D_0 0x1b0 0x54c 0x7ec 0x2 0x1
+#define MX50_PAD_EPDC_D0__ELCDIF_RS 0x1b0 0x54c 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D0__ELCDIF_DOTCLK 0x1b0 0x54c 0x000 0x4 0x0
+#define MX50_PAD_EPDC_D0__SDMA_DEBUG_EVT_CHN_LINES_0 0x1b0 0x54c 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D0__USBPHY2_DATAOUT_0 0x1b0 0x54c 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D1__EPDC_SDDO_1 0x1b4 0x550 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D1__GPIO3_1 0x1b4 0x550 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D1__EIM_WEIM_D_1 0x1b4 0x550 0x7f0 0x2 0x1
+#define MX50_PAD_EPDC_D1__ELCDIF_CS 0x1b4 0x550 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D1__ELCDIF_ENABLE 0x1b4 0x550 0x000 0x4 0x0
+#define MX50_PAD_EPDC_D1__SDMA_DEBUG_EVT_CHN_LINES_1 0x1b4 0x550 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D1__USBPHY2_DATAOUT_1 0x1b4 0x550 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D2__EPDC_SDDO_2 0x1b8 0x554 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D2__GPIO3_2 0x1b8 0x554 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D2__EIM_WEIM_D_2 0x1b8 0x554 0x7f4 0x2 0x1
+#define MX50_PAD_EPDC_D2__ELCDIF_WR_RWN 0x1b8 0x554 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D2__ELCDIF_VSYNC 0x1b8 0x554 0x73c 0x4 0x2
+#define MX50_PAD_EPDC_D2__SDMA_DEBUG_EVT_CHN_LINES_2 0x1b8 0x554 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D2__USBPHY2_DATAOUT_2 0x1b8 0x554 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D3__EPDC_SDDO_3 0x1bc 0x558 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D3__GPIO3_3 0x1bc 0x558 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D3__EIM_WEIM_D_3 0x1bc 0x558 0x7f8 0x2 0x1
+#define MX50_PAD_EPDC_D3__ELCDIF_RD_E 0x1bc 0x558 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D3__ELCDIF_HSYNC 0x1bc 0x558 0x6f8 0x4 0x3
+#define MX50_PAD_EPDC_D3__SDMA_DEBUG_EVT_CHN_LINES_3 0x1bc 0x558 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D3__USBPHY2_DATAOUT_3 0x1bc 0x558 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D4__EPDC_SDDO_4 0x1c0 0x55c 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D4__GPIO3_4 0x1c0 0x55c 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D4__EIM_WEIM_D_4 0x1c0 0x55c 0x7fc 0x2 0x1
+#define MX50_PAD_EPDC_D4__SDMA_DEBUG_EVT_CHN_LINES_4 0x1c0 0x55c 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D4__USBPHY2_DATAOUT_4 0x1c0 0x55c 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D5__EPDC_SDDO_5 0x1c4 0x560 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D5__GPIO3_5 0x1c4 0x560 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D5__EIM_WEIM_D_5 0x1c4 0x560 0x800 0x2 0x1
+#define MX50_PAD_EPDC_D5__SDMA_DEBUG_EVT_CHN_LINES_5 0x1c4 0x560 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D5__USBPHY2_DATAOUT_5 0x1c4 0x560 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D6__EPDC_SDDO_6 0x1c8 0x564 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D6__GPIO3_6 0x1c8 0x564 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D6__EIM_WEIM_D_6 0x1c8 0x564 0x804 0x2 0x1
+#define MX50_PAD_EPDC_D6__SDMA_DEBUG_EVT_CHN_LINES_6 0x1c8 0x564 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D6__USBPHY2_DATAOUT_6 0x1c8 0x564 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D7__EPDC_SDDO_7 0x1cc 0x568 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D7__GPIO3_7 0x1cc 0x568 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D7__EIM_WEIM_D_7 0x1cc 0x568 0x808 0x2 0x1
+#define MX50_PAD_EPDC_D7__SDMA_DEBUG_EVT_CHN_LINES_7 0x1cc 0x568 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D7__USBPHY2_DATAOUT_7 0x1cc 0x568 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D8__EPDC_SDDO_8 0x1d0 0x56c 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D8__GPIO3_8 0x1d0 0x56c 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D8__EIM_WEIM_D_8 0x1d0 0x56c 0x80c 0x2 0x2
+#define MX50_PAD_EPDC_D8__ELCDIF_DAT_24 0x1d0 0x56c 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D8__SDMA_DEBUG_MATCHED_DMBUS 0x1d0 0x56c 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D8__USBPHY2_VSTATUS_0 0x1d0 0x56c 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D9__EPDC_SDDO_9 0x1d4 0x570 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D9__GPIO3_9 0x1d4 0x570 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D9__EIM_WEIM_D_9 0x1d4 0x570 0x810 0x2 0x2
+#define MX50_PAD_EPDC_D9__ELCDIF_DAT_25 0x1d4 0x570 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D9__SDMA_DEBUG_EVENT_CHANNEL_SEL 0x1d4 0x570 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D9__USBPHY2_VSTATUS_1 0x1d4 0x570 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D10__EPDC_SDDO_10 0x1d8 0x574 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D10__GPIO3_10 0x1d8 0x574 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D10__EIM_WEIM_D_10 0x1d8 0x574 0x814 0x2 0x2
+#define MX50_PAD_EPDC_D10__ELCDIF_DAT_26 0x1d8 0x574 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D10__SDMA_DEBUG_EVENT_CHANNEL_0 0x1d8 0x574 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D10__USBPHY2_VSTATUS_2 0x1d8 0x574 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D11__EPDC_SDDO_11 0x1dc 0x578 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D11__GPIO3_11 0x1dc 0x578 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D11__EIM_WEIM_D_11 0x1dc 0x578 0x818 0x2 0x2
+#define MX50_PAD_EPDC_D11__ELCDIF_DAT_27 0x1dc 0x578 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D11__SDMA_DEBUG_EVENT_CHANNEL_1 0x1dc 0x578 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D11__USBPHY2_VSTATUS_3 0x1dc 0x578 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D12__EPDC_SDDO_12 0x1e0 0x57c 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D12__GPIO3_12 0x1e0 0x57c 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D12__EIM_WEIM_D_12 0x1e0 0x57c 0x81c 0x2 0x1
+#define MX50_PAD_EPDC_D12__ELCDIF_DAT_28 0x1e0 0x57c 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D12__SDMA_DEBUG_EVENT_CHANNEL_2 0x1e0 0x57c 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D12__USBPHY2_VSTATUS_4 0x1e0 0x57c 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D13__EPDC_SDDO_13 0x1e4 0x580 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D13__GPIO3_13 0x1e4 0x580 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D13__EIM_WEIM_D_13 0x1e4 0x580 0x820 0x2 0x1
+#define MX50_PAD_EPDC_D13__ELCDIF_DAT_29 0x1e4 0x580 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D13__SDMA_DEBUG_EVENT_CHANNEL_3 0x1e4 0x580 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D13__USBPHY2_VSTATUS_5 0x1e4 0x580 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D14__EPDC_SDDO_14 0x1e8 0x584 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D14__GPIO3_14 0x1e8 0x584 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D14__EIM_WEIM_D_14 0x1e8 0x584 0x824 0x2 0x1
+#define MX50_PAD_EPDC_D14__ELCDIF_DAT_30 0x1e8 0x584 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D14__AUDMUX_AUD6_TXD 0x1e8 0x584 0x000 0x4 0x0
+#define MX50_PAD_EPDC_D14__SDMA_DEBUG_EVENT_CHANNEL_4 0x1e8 0x584 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D14__USBPHY2_VSTATUS_6 0x1e8 0x584 0x000 0x7 0x0
+#define MX50_PAD_EPDC_D15__EPDC_SDDO_15 0x1ec 0x588 0x000 0x0 0x0
+#define MX50_PAD_EPDC_D15__GPIO3_15 0x1ec 0x588 0x000 0x1 0x0
+#define MX50_PAD_EPDC_D15__EIM_WEIM_D_15 0x1ec 0x588 0x828 0x2 0x1
+#define MX50_PAD_EPDC_D15__ELCDIF_DAT_31 0x1ec 0x588 0x000 0x3 0x0
+#define MX50_PAD_EPDC_D15__AUDMUX_AUD6_TXC 0x1ec 0x588 0x000 0x4 0x0
+#define MX50_PAD_EPDC_D15__SDMA_DEBUG_EVENT_CHANNEL_5 0x1ec 0x588 0x000 0x6 0x0
+#define MX50_PAD_EPDC_D15__USBPHY2_VSTATUS_7 0x1ec 0x588 0x000 0x7 0x0
+#define MX50_PAD_EPDC_GDCLK__EPDC_GDCLK 0x1f0 0x58c 0x000 0x0 0x0
+#define MX50_PAD_EPDC_GDCLK__GPIO3_16 0x1f0 0x58c 0x000 0x1 0x0
+#define MX50_PAD_EPDC_GDCLK__EIM_WEIM_D_16 0x1f0 0x58c 0x000 0x2 0x0
+#define MX50_PAD_EPDC_GDCLK__ELCDIF_DAT_16 0x1f0 0x58c 0x000 0x3 0x0
+#define MX50_PAD_EPDC_GDCLK__AUDMUX_AUD6_TXFS 0x1f0 0x58c 0x000 0x4 0x0
+#define MX50_PAD_EPDC_GDCLK__SDMA_DEBUG_CORE_STATE_0 0x1f0 0x58c 0x000 0x6 0x0
+#define MX50_PAD_EPDC_GDCLK__USBPHY2_BISTOK 0x1f0 0x58c 0x000 0x7 0x0
+#define MX50_PAD_EPDC_GDSP__EPCD_GDSP 0x1f4 0x590 0x000 0x0 0x0
+#define MX50_PAD_EPDC_GDSP__GPIO3_17 0x1f4 0x590 0x000 0x1 0x0
+#define MX50_PAD_EPDC_GDSP__EIM_WEIM_D_17 0x1f4 0x590 0x000 0x2 0x0
+#define MX50_PAD_EPDC_GDSP__ELCDIF_DAT_17 0x1f4 0x590 0x000 0x3 0x0
+#define MX50_PAD_EPDC_GDSP__AUDMUX_AUD6_RXD 0x1f4 0x590 0x000 0x4 0x0
+#define MX50_PAD_EPDC_GDSP__SDMA_DEBUG_CORE_STATE_1 0x1f4 0x590 0x000 0x6 0x0
+#define MX50_PAD_EPDC_GDSP__USBPHY2_BVALID 0x1f4 0x590 0x000 0x7 0x0
+#define MX50_PAD_EPDC_GDOE__EPCD_GDOE 0x1f8 0x594 0x000 0x0 0x0
+#define MX50_PAD_EPDC_GDOE__GPIO3_18 0x1f8 0x594 0x000 0x1 0x0
+#define MX50_PAD_EPDC_GDOE__EIM_WEIM_D_18 0x1f8 0x594 0x000 0x2 0x0
+#define MX50_PAD_EPDC_GDOE__ELCDIF_DAT_18 0x1f8 0x594 0x000 0x3 0x0
+#define MX50_PAD_EPDC_GDOE__AUDMUX_AUD6_RXC 0x1f8 0x594 0x000 0x4 0x0
+#define MX50_PAD_EPDC_GDOE__SDMA_DEBUG_CORE_STATE_2 0x1f8 0x594 0x000 0x6 0x0
+#define MX50_PAD_EPDC_GDOE__USBPHY2_ENDSESSION 0x1f8 0x594 0x000 0x7 0x0
+#define MX50_PAD_EPDC_GDRL__EPCD_GDRL 0x1fc 0x598 0x000 0x0 0x0
+#define MX50_PAD_EPDC_GDRL__GPIO3_19 0x1fc 0x598 0x000 0x1 0x0
+#define MX50_PAD_EPDC_GDRL__EIM_WEIM_D_19 0x1f8 0x598 0x000 0x2 0x0
+#define MX50_PAD_EPDC_GDRL__ELCDIF_DAT_19 0x1fc 0x598 0x000 0x3 0x0
+#define MX50_PAD_EPDC_GDRL__AUDMUX_AUD6_RXFS 0x1fc 0x598 0x000 0x4 0x0
+#define MX50_PAD_EPDC_GDRL__SDMA_DEBUG_CORE_STATE_3 0x1fc 0x598 0x000 0x6 0x0
+#define MX50_PAD_EPDC_GDRL__USBPHY2_IDDIG 0x1fc 0x598 0x000 0x7 0x0
+#define MX50_PAD_EPDC_SDCLK__EPCD_SDCLK 0x200 0x59c 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDCLK__GPIO3_20 0x200 0x59c 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDCLK__EIM_WEIM_D_20 0x200 0x59c 0x000 0x2 0x0
+#define MX50_PAD_EPDC_SDCLK__ELCDIF_DAT_20 0x200 0x59c 0x000 0x3 0x0
+#define MX50_PAD_EPDC_SDCLK__AUDMUX_AUD5_TXD 0x200 0x59c 0x000 0x4 0x0
+#define MX50_PAD_EPDC_SDCLK__SDMA_DEBUG_BUS_DEVICE_0 0x200 0x59c 0x000 0x6 0x0
+#define MX50_PAD_EPDC_SDCLK__USBPHY2_HOSTDISCONNECT 0x200 0x59c 0x000 0x7 0x0
+#define MX50_PAD_EPDC_SDOEZ__EPCD_SDOEZ 0x204 0x5a0 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDOEZ__GPIO3_21 0x204 0x5a0 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDOEZ__EIM_WEIM_D_21 0x204 0x5a0 0x000 0x2 0x0
+#define MX50_PAD_EPDC_SDOEZ__ELCDIF_DAT_21 0x204 0x5a0 0x000 0x3 0x0
+#define MX50_PAD_EPDC_SDOEZ__AUDMUX_AUD5_TXC 0x204 0x5a0 0x000 0x4 0x0
+#define MX50_PAD_EPDC_SDOEZ__SDMA_DEBUG_BUS_DEVICE_1 0x204 0x5a0 0x000 0x6 0x0
+#define MX50_PAD_EPDC_SDOEZ__USBPHY2_TXREADY 0x204 0x5a0 0x000 0x7 0x0
+#define MX50_PAD_EPDC_SDOED__EPCD_SDOED 0x208 0x5a4 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDOED__GPIO3_22 0x208 0x5a4 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDOED__EIM_WEIM_D_22 0x208 0x5a4 0x000 0x2 0x0
+#define MX50_PAD_EPDC_SDOED__ELCDIF_DAT_22 0x208 0x5a4 0x000 0x3 0x0
+#define MX50_PAD_EPDC_SDOED__AUDMUX_AUD5_TXFS 0x208 0x5a4 0x000 0x4 0x0
+#define MX50_PAD_EPDC_SDOED__SDMA_DEBUG_BUS_DEVICE_2 0x208 0x5a4 0x000 0x6 0x0
+#define MX50_PAD_EPDC_SDOED__USBPHY2_RXVALID 0x208 0x5a4 0x000 0x7 0x0
+#define MX50_PAD_EPDC_SDOE__EPCD_SDOE 0x20c 0x5a8 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDOE__GPIO3_23 0x20c 0x5a8 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDOE__EIM_WEIM_D_23 0x20c 0x5a8 0x000 0x2 0x0
+#define MX50_PAD_EPDC_SDOE__ELCDIF_DAT_23 0x20c 0x5a8 0x000 0x3 0x0
+#define MX50_PAD_EPDC_SDOE__AUDMUX_AUD5_RXD 0x20c 0x5a8 0x000 0x4 0x0
+#define MX50_PAD_EPDC_SDOE__SDMA_DEBUG_BUS_DEVICE_3 0x20c 0x5a8 0x000 0x6 0x0
+#define MX50_PAD_EPDC_SDOE__USBPHY2_RXACTIVE 0x20c 0x5a8 0x000 0x7 0x0
+#define MX50_PAD_EPDC_SDLE__EPCD_SDLE 0x210 0x5ac 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDLE__GPIO3_24 0x210 0x5ac 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDLE__EIM_WEIM_D_24 0x210 0x5ac 0x000 0x2 0x0
+#define MX50_PAD_EPDC_SDLE__ELCDIF_DAT_8 0x210 0x5ac 0x71c 0x3 0x1
+#define MX50_PAD_EPDC_SDLE__AUDMUX_AUD5_RXC 0x210 0x5ac 0x000 0x4 0x0
+#define MX50_PAD_EPDC_SDLE__SDMA_DEBUG_BUS_DEVICE_4 0x210 0x5ac 0x000 0x6 0x0
+#define MX50_PAD_EPDC_SDLE__USBPHY2_RXERROR 0x210 0x5ac 0x000 0x7 0x0
+#define MX50_PAD_EPDC_SDCLKN__EPCD_SDCLKN 0x214 0x5b0 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDCLKN__GPIO3_25 0x214 0x5b0 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDCLKN__EIM_WEIM_D_25 0x214 0x5b0 0x000 0x2 0x0
+#define MX50_PAD_EPDC_SDCLKN__ELCDIF_DAT_9 0x214 0x5b0 0x720 0x3 0x1
+#define MX50_PAD_EPDC_SDCLKN__AUDMUX_AUD5_RXFS 0x214 0x5b0 0x000 0x4 0x0
+#define MX50_PAD_EPDC_SDCLKN__SDMA_DEBUG_BUS_ERROR 0x214 0x5b0 0x000 0x6 0x0
+#define MX50_PAD_EPDC_SDCLKN__USBPHY2_SIECLOCK 0x214 0x5b0 0x000 0x7 0x0
+#define MX50_PAD_EPDC_SDSHR__EPCD_SDSHR 0x218 0x5b4 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDSHR__GPIO3_26 0x218 0x5b4 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDSHR__EIM_WEIM_D_26 0x218 0x5b4 0x000 0x2 0x0
+#define MX50_PAD_EPDC_SDSHR__ELCDIF_DAT_10 0x218 0x5b4 0x724 0x3 0x1
+#define MX50_PAD_EPDC_SDSHR__AUDMUX_AUD4_TXD 0x218 0x5b4 0x6c8 0x4 0x1
+#define MX50_PAD_EPDC_SDSHR__SDMA_DEBUG_BUS_RWB 0x218 0x5b4 0x000 0x6 0x0
+#define MX50_PAD_EPDC_SDSHR__USBPHY2_LINESTATE_0 0x218 0x5b4 0x000 0x7 0x0
+#define MX50_PAD_EPDC_PWRCOM__EPCD_PWRCOM 0x21c 0x5b8 0x000 0x0 0x0
+#define MX50_PAD_EPDC_PWRCOM__GPIO3_27 0x21c 0x5b8 0x000 0x1 0x0
+#define MX50_PAD_EPDC_PWRCOM__EIM_WEIM_D_27 0x21c 0x5b8 0x000 0x2 0x0
+#define MX50_PAD_EPDC_PWRCOM__ELCDIF_DAT_11 0x21c 0x5b8 0x728 0x3 0x1
+#define MX50_PAD_EPDC_PWRCOM__AUDMUX_AUD4_TXC 0x21c 0x5b8 0x6d4 0x4 0x1
+#define MX50_PAD_EPDC_PWRCOM__SDMA_DEBUG_CORE_RUN 0x21c 0x5b8 0x000 0x6 0x0
+#define MX50_PAD_EPDC_PWRCOM__USBPHY2_LINESTATE_1 0x21c 0x5b8 0x000 0x7 0x0
+#define MX50_PAD_EPDC_PWRSTAT__EPCD_PWRSTAT 0x220 0x5bc 0x000 0x0 0x0
+#define MX50_PAD_EPDC_PWRSTAT__GPIO3_28 0x220 0x5bc 0x000 0x1 0x0
+#define MX50_PAD_EPDC_PWRSTAT__EIM_WEIM_D_28 0x220 0x5bc 0x000 0x2 0x0
+#define MX50_PAD_EPDC_PWRSTAT__ELCDIF_DAT_12 0x220 0x5bc 0x72c 0x3 0x1
+#define MX50_PAD_EPDC_PWRSTAT__AUDMUX_AUD4_TXFS 0x220 0x5bc 0x6d8 0x4 0x1
+#define MX50_PAD_EPDC_PWRSTAT__SDMA_DEBUG_MODE 0x220 0x5bc 0x000 0x6 0x0
+#define MX50_PAD_EPDC_PWRSTAT__USBPHY2_VBUSVALID 0x220 0x5bc 0x000 0x7 0x0
+#define MX50_PAD_EPDC_PWRCTRL0__EPCD_PWRCTRL0 0x224 0x5c0 0x000 0x0 0x0
+#define MX50_PAD_EPDC_PWRCTRL0__GPIO3_29 0x224 0x5c0 0x000 0x1 0x0
+#define MX50_PAD_EPDC_PWRCTRL0__EIM_WEIM_D_29 0x224 0x5c0 0x000 0x2 0x0
+#define MX50_PAD_EPDC_PWRCTRL0__ELCDIF_DAT_13 0x224 0x5c0 0x730 0x3 0x1
+#define MX50_PAD_EPDC_PWRCTRL0__AUDMUX_AUD4_RXD 0x224 0x5c0 0x6c4 0x4 0x1
+#define MX50_PAD_EPDC_PWRCTRL0__SDMA_DEBUG_RTBUFFER_WRITE 0x224 0x5c0 0x000 0x6 0x0
+#define MX50_PAD_EPDC_PWRCTRL0__USBPHY2_AVALID 0x224 0x5c0 0x000 0x7 0x0
+#define MX50_PAD_EPDC_PWRCTRL1__EPCD_PWRCTRL1 0x228 0x5c4 0x000 0x0 0x0
+#define MX50_PAD_EPDC_PWRCTRL1__GPIO3_30 0x228 0x5c4 0x000 0x1 0x0
+#define MX50_PAD_EPDC_PWRCTRL1__EIM_WEIM_D_30 0x228 0x5c4 0x000 0x2 0x0
+#define MX50_PAD_EPDC_PWRCTRL1__ELCDIF_DAT_14 0x228 0x5c4 0x734 0x3 0x1
+#define MX50_PAD_EPDC_PWRCTRL1__AUDMUX_AUD4_RXC 0x228 0x5c4 0x6cc 0x4 0x1
+#define MX50_PAD_EPDC_PWRCTRL1__SDMA_DEBUG_YIELD 0x228 0x5c4 0x000 0x6 0x0
+#define MX50_PAD_EPDC_PWRCTRL1__USBPHY1_ONBIST 0x228 0x5c4 0x000 0x7 0x0
+#define MX50_PAD_EPDC_PWRCTRL2__EPCD_PWRCTRL2 0x22c 0x5c8 0x000 0x0 0x0
+#define MX50_PAD_EPDC_PWRCTRL2__GPIO3_31 0x22c 0x5c8 0x000 0x1 0x0
+#define MX50_PAD_EPDC_PWRCTRL2__EIM_WEIM_D_31 0x22c 0x5c8 0x000 0x2 0x0
+#define MX50_PAD_EPDC_PWRCTRL2__ELCDIF_DAT_15 0x22c 0x5c8 0x738 0x3 0x1
+#define MX50_PAD_EPDC_PWRCTRL2__AUDMUX_AUD4_RXFS 0x22c 0x5c8 0x6d0 0x4 0x1
+#define MX50_PAD_EPDC_PWRCTRL2__SDMA_EXT_EVENT_0 0x22c 0x5c8 0x7b8 0x6 0x1
+#define MX50_PAD_EPDC_PWRCTRL2__USBPHY2_ONBIST 0x22c 0x5c8 0x000 0x7 0x0
+#define MX50_PAD_EPDC_PWRCTRL3__EPCD_PWRCTRL3 0x230 0x5cc 0x000 0x0 0x0
+#define MX50_PAD_EPDC_PWRCTRL3__GPIO4_20 0x230 0x5cc 0x000 0x1 0x0
+#define MX50_PAD_EPDC_PWRCTRL3__EIM_WEIM_EB_2 0x230 0x5cc 0x000 0x2 0x0
+#define MX50_PAD_EPDC_PWRCTRL3__SDMA_EXT_EVENT_1 0x230 0x5cc 0x7bc 0x6 0x1
+#define MX50_PAD_EPDC_PWRCTRL3__USBPHY1_BISTOK 0x230 0x5cc 0x000 0x7 0x0
+#define MX50_PAD_EPDC_VCOM0__EPCD_VCOM_0 0x234 0x5d0 0x000 0x0 0x0
+#define MX50_PAD_EPDC_VCOM0__GPIO4_21 0x234 0x5d0 0x000 0x1 0x0
+#define MX50_PAD_EPDC_VCOM0__EIM_WEIM_EB_3 0x234 0x5d0 0x000 0x2 0x0
+#define MX50_PAD_EPDC_VCOM0__USBPHY2_BISTOK 0x234 0x5d0 0x000 0x7 0x0
+#define MX50_PAD_EPDC_VCOM1__EPCD_VCOM_1 0x238 0x5d4 0x000 0x0 0x0
+#define MX50_PAD_EPDC_VCOM1__GPIO4_22 0x238 0x5d4 0x000 0x1 0x0
+#define MX50_PAD_EPDC_VCOM1__EIM_WEIM_CS_3 0x238 0x5d4 0x000 0x2 0x0
+#define MX50_PAD_EPDC_BDR0__EPCD_BDR_0 0x23c 0x5d8 0x000 0x0 0x0
+#define MX50_PAD_EPDC_BDR0__GPIO4_23 0x23c 0x5d8 0x000 0x1 0x0
+#define MX50_PAD_EPDC_BDR0__ELCDIF_DAT_7 0x23c 0x5d8 0x718 0x3 0x1
+#define MX50_PAD_EPDC_BDR1__EPCD_BDR_1 0x240 0x5dc 0x000 0x0 0x0
+#define MX50_PAD_EPDC_BDR1__GPIO4_24 0x240 0x5dc 0x000 0x1 0x0
+#define MX50_PAD_EPDC_BDR1__ELCDIF_DAT_6 0x240 0x5dc 0x714 0x3 0x1
+#define MX50_PAD_EPDC_SDCE0__EPCD_SDCE_0 0x244 0x5e0 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDCE0__GPIO4_25 0x244 0x5e0 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDCE0__ELCDIF_DAT_5 0x244 0x5e0 0x710 0x3 0x1
+#define MX50_PAD_EPDC_SDCE1__EPCD_SDCE_1 0x248 0x5e4 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDCE1__GPIO4_26 0x248 0x5e4 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDCE1__ELCDIF_DAT_4 0x248 0x5e4 0x70c 0x3 0x0
+#define MX50_PAD_EPDC_SDCE2__EPCD_SDCE_2 0x24c 0x5e8 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDCE2__GPIO4_27 0x24c 0x5e8 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDCE2__ELCDIF_DAT_3 0x24c 0x5e8 0x708 0x3 0x1
+#define MX50_PAD_EPDC_SDCE3__EPCD_SDCE_3 0x250 0x5ec 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDCE3__GPIO4_28 0x250 0x5ec 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDCE3__ELCDIF_DAT_2 0x250 0x5ec 0x704 0x3 0x1
+#define MX50_PAD_EPDC_SDCE4__EPCD_SDCE_4 0x254 0x5f0 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDCE4__GPIO4_29 0x254 0x5f0 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDCE4__ELCDIF_DAT_1 0x254 0x5f0 0x700 0x3 0x1
+#define MX50_PAD_EPDC_SDCE5__EPCD_SDCE_5 0x258 0x5f4 0x000 0x0 0x0
+#define MX50_PAD_EPDC_SDCE5__GPIO4_30 0x258 0x5f4 0x000 0x1 0x0
+#define MX50_PAD_EPDC_SDCE5__ELCDIF_DAT_0 0x258 0x5f4 0x6fc 0x3 0x1
+#define MX50_PAD_EIM_DA0__EIM_WEIM_A_0 0x25c 0x5f8 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA0__GPIO1_0 0x25c 0x5f8 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA0__KPP_COL_4 0x25c 0x5f8 0x790 0x3 0x2
+#define MX50_PAD_EIM_DA0__TPIU_TRACE_0 0x25c 0x5f8 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA0__SRC_BT_CFG1_0 0x25c 0x5f8 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA1__EIM_WEIM_A_1 0x260 0x5fc 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA1__GPIO1_1 0x260 0x5fc 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA1__KPP_ROW_4 0x260 0x5fc 0x7a0 0x3 0x2
+#define MX50_PAD_EIM_DA1__TPIU_TRACE_1 0x260 0x5fc 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA1__SRC_BT_CFG1_1 0x260 0x5fc 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA2__EIM_WEIM_A_2 0x264 0x600 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA2__GPIO1_2 0x264 0x600 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA2__KPP_COL_5 0x264 0x600 0x794 0x3 0x2
+#define MX50_PAD_EIM_DA2__TPIU_TRACE_2 0x264 0x600 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA2__SRC_BT_CFG1_2 0x264 0x600 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA3__EIM_WEIM_A_3 0x268 0x604 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA3__GPIO1_3 0x268 0x604 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA3__KPP_ROW_5 0x268 0x604 0x7a4 0x3 0x2
+#define MX50_PAD_EIM_DA3__TPIU_TRACE_3 0x268 0x604 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA3__SRC_BT_CFG1_3 0x268 0x604 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA4__EIM_WEIM_A_4 0x26c 0x608 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA4__GPIO1_4 0x26c 0x608 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA4__KPP_COL_6 0x26c 0x608 0x798 0x3 0x2
+#define MX50_PAD_EIM_DA4__TPIU_TRACE_4 0x26c 0x608 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA4__SRC_BT_CFG1_4 0x26c 0x608 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA5__EIM_WEIM_A_5 0x270 0x60c 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA5__GPIO1_5 0x270 0x60c 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA5__KPP_ROW_6 0x270 0x60c 0x7a8 0x3 0x2
+#define MX50_PAD_EIM_DA5__TPIU_TRACE_5 0x270 0x60c 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA5__SRC_BT_CFG1_5 0x270 0x60c 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA6__EIM_WEIM_A_6 0x274 0x610 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA6__GPIO1_6 0x274 0x610 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA6__KPP_COL_7 0x274 0x610 0x79c 0x3 0x2
+#define MX50_PAD_EIM_DA6__TPIU_TRACE_6 0x274 0x610 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA6__SRC_BT_CFG1_6 0x274 0x610 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA7__EIM_WEIM_A_7 0x278 0x614 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA7__GPIO1_7 0x278 0x614 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA7__KPP_ROW_7 0x278 0x614 0x7ac 0x3 0x2
+#define MX50_PAD_EIM_DA7__TPIU_TRACE_7 0x278 0x614 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA7__SRC_BT_CFG1_7 0x278 0x614 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA8__EIM_WEIM_A_8 0x27c 0x618 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA8__GPIO1_8 0x27c 0x618 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA8__EIM_NANDF_CLE 0x27c 0x618 0x000 0x2 0x0
+#define MX50_PAD_EIM_DA8__TPIU_TRACE_8 0x27c 0x618 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA8__SRC_BT_CFG2_0 0x27c 0x618 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA9__EIM_WEIM_A_9 0x280 0x61c 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA9__GPIO1_9 0x280 0x61c 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA9__EIM_NANDF_ALE 0x280 0x61c 0x000 0x2 0x0
+#define MX50_PAD_EIM_DA9__TPIU_TRACE_9 0x280 0x61c 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA9__SRC_BT_CFG2_1 0x280 0x61c 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA10__EIM_WEIM_A_10 0x284 0x620 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA10__GPIO1_10 0x284 0x620 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA10__EIM_NANDF_CEN_0 0x284 0x620 0x000 0x2 0x0
+#define MX50_PAD_EIM_DA10__TPIU_TRACE_10 0x284 0x620 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA10__SRC_BT_CFG2_2 0x284 0x620 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA11__EIM_WEIM_A_11 0x288 0x624 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA11__GPIO1_11 0x288 0x624 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA11__EIM_NANDF_CEN_1 0x288 0x624 0x000 0x2 0x0
+#define MX50_PAD_EIM_DA11__TPIU_TRACE_11 0x288 0x624 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA11__SRC_BT_CFG2_3 0x288 0x624 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA12__EIM_WEIM_A_12 0x28c 0x628 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA12__GPIO1_12 0x28c 0x628 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA12__EIM_NANDF_CEN_2 0x28c 0x628 0x000 0x2 0x0
+#define MX50_PAD_EIM_DA12__EPDC_SDCE_6 0x28c 0x628 0x000 0x3 0x0
+#define MX50_PAD_EIM_DA12__TPIU_TRACE_12 0x28c 0x628 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA12__SRC_BT_CFG2_4 0x28c 0x628 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA13__EIM_WEIM_A_13 0x290 0x62c 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA13__GPIO1_13 0x290 0x62c 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA13__EIM_NANDF_CEN_3 0x290 0x62c 0x000 0x2 0x0
+#define MX50_PAD_EIM_DA13__EPDC_SDCE_7 0x290 0x62c 0x000 0x3 0x0
+#define MX50_PAD_EIM_DA13__TPIU_TRACE_13 0x290 0x62c 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA13__SRC_BT_CFG2_5 0x290 0x62c 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA14__EIM_WEIM_A_14 0x294 0x630 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA14__GPIO1_14 0x294 0x630 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA14__EIM_NANDF_READY0 0x294 0x630 0x7b4 0x2 0x2
+#define MX50_PAD_EIM_DA14__EPDC_SDCE_8 0x294 0x630 0x000 0x3 0x0
+#define MX50_PAD_EIM_DA14__TPIU_TRACE_14 0x294 0x630 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA14__SRC_BT_CFG2_6 0x294 0x630 0x000 0x7 0x0
+#define MX50_PAD_EIM_DA15__EIM_WEIM_A_15 0x298 0x634 0x000 0x0 0x0
+#define MX50_PAD_EIM_DA15__GPIO1_15 0x298 0x634 0x000 0x1 0x0
+#define MX50_PAD_EIM_DA15__EIM_NANDF_DQS 0x298 0x634 0x7b0 0x2 0x2
+#define MX50_PAD_EIM_DA15__EPDC_SDCE_9 0x298 0x634 0x000 0x3 0x0
+#define MX50_PAD_EIM_DA15__TPIU_TRACE_15 0x298 0x634 0x000 0x6 0x0
+#define MX50_PAD_EIM_DA15__SRC_BT_CFG2_7 0x298 0x634 0x000 0x7 0x0
+#define MX50_PAD_EIM_CS2__EIM_WEIM_CS_2 0x29c 0x638 0x000 0x0 0x0
+#define MX50_PAD_EIM_CS2__GPIO1_16 0x29c 0x638 0x000 0x1 0x0
+#define MX50_PAD_EIM_CS2__EIM_WEIM_A_27 0x29c 0x638 0x000 0x2 0x0
+#define MX50_PAD_EIM_CS2__TPIU_TRCLK 0x29c 0x638 0x000 0x6 0x0
+#define MX50_PAD_EIM_CS2__SRC_BT_CFG3_0 0x29c 0x638 0x000 0x7 0x0
+#define MX50_PAD_EIM_CS1__EIM_WEIM_CS_1 0x2a0 0x63c 0x000 0x0 0x0
+#define MX50_PAD_EIM_CS1__GPIO1_17 0x2a0 0x63c 0x000 0x1 0x0
+#define MX50_PAD_EIM_CS1__TPIU_TRCTL 0x2a0 0x63c 0x000 0x6 0x0
+#define MX50_PAD_EIM_CS1__SRC_BT_CFG3_1 0x2a0 0x63c 0x000 0x7 0x0
+#define MX50_PAD_EIM_CS0__EIM_WEIM_CS_0 0x2a4 0x640 0x000 0x0 0x0
+#define MX50_PAD_EIM_CS0__GPIO1_18 0x2a4 0x640 0x000 0x1 0x0
+#define MX50_PAD_EIM_CS0__SRC_BT_CFG3_2 0x2a4 0x640 0x000 0x7 0x0
+#define MX50_PAD_EIM_EB0__EIM_WEIM_EB_0 0x2a8 0x644 0x000 0x0 0x0
+#define MX50_PAD_EIM_EB0__GPIO1_19 0x2a8 0x644 0x000 0x1 0x0
+#define MX50_PAD_EIM_EB0__SRC_BT_CFG3_3 0x2a8 0x644 0x000 0x7 0x0
+#define MX50_PAD_EIM_EB1__EIM_WEIM_EB_1 0x2ac 0x648 0x000 0x0 0x0
+#define MX50_PAD_EIM_EB1__GPIO1_20 0x2ac 0x648 0x000 0x1 0x0
+#define MX50_PAD_EIM_EB1__SRC_BT_CFG3_4 0x2ac 0x648 0x000 0x7 0x0
+#define MX50_PAD_EIM_WAIT__EIM_WEIM_WAIT 0x2b0 0x64c 0x000 0x0 0x0
+#define MX50_PAD_EIM_WAIT__GPIO1_21 0x2b0 0x64c 0x000 0x1 0x0
+#define MX50_PAD_EIM_WAIT__EIM_WEIM_DTACK_B 0x2b0 0x64c 0x000 0x2 0x0
+#define MX50_PAD_EIM_WAIT__SRC_BT_CFG3_5 0x2b0 0x64c 0x000 0x7 0x0
+#define MX50_PAD_EIM_BCLK__EIM_WEIM_BCLK 0x2b4 0x650 0x000 0x0 0x0
+#define MX50_PAD_EIM_BCLK__GPIO1_22 0x2b4 0x650 0x000 0x1 0x0
+#define MX50_PAD_EIM_BCLK__SRC_BT_CFG3_6 0x2b4 0x650 0x000 0x7 0x0
+#define MX50_PAD_EIM_RDY__EIM_WEIM_RDY 0x2b8 0x654 0x000 0x0 0x0
+#define MX50_PAD_EIM_RDY__GPIO1_23 0x2b8 0x654 0x000 0x1 0x0
+#define MX50_PAD_EIM_RDY__SRC_BT_CFG3_7 0x2b8 0x654 0x000 0x7 0x0
+#define MX50_PAD_EIM_OE__EIM_WEIM_OE 0x2bc 0x658 0x000 0x0 0x0
+#define MX50_PAD_EIM_OE__GPIO1_24 0x2bc 0x658 0x000 0x1 0x0
+#define MX50_PAD_EIM_OE__INT_BOOT 0x2bc 0x658 0x000 0x7 0x0
+#define MX50_PAD_EIM_RW__EIM_WEIM_RW 0x2c0 0x65c 0x000 0x0 0x0
+#define MX50_PAD_EIM_RW__GPIO1_25 0x2c0 0x65c 0x000 0x1 0x0
+#define MX50_PAD_EIM_RW__SYSTEM_RST 0x2c0 0x65c 0x000 0x7 0x0
+#define MX50_PAD_EIM_LBA__EIM_WEIM_LBA 0x2c4 0x660 0x000 0x0 0x0
+#define MX50_PAD_EIM_LBA__GPIO1_26 0x2c4 0x660 0x000 0x1 0x0
+#define MX50_PAD_EIM_LBA__TESTER_ACK 0x2c4 0x660 0x000 0x7 0x0
+#define MX50_PAD_EIM_CRE__EIM_WEIM_CRE 0x2c8 0x664 0x000 0x0 0x0
+#define MX50_PAD_EIM_CRE__GPIO1_27 0x2c8 0x664 0x000 0x1 0x0
+
+#endif /* __DTS_IMX50_PINFUNC_H */
diff --git a/arch/arm/boot/dts/imx50.dtsi b/arch/arm/boot/dts/imx50.dtsi
new file mode 100644
index 000000000000..0c75fe3deb35
--- /dev/null
+++ b/arch/arm/boot/dts/imx50.dtsi
@@ -0,0 +1,478 @@
+/*
+ * Copyright 2013 Greg Ungerer <gerg@uclinux.org>
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include "skeleton.dtsi"
+#include "imx50-pinfunc.h"
+#include <dt-bindings/clock/imx5-clock.h>
+
+/ {
+ aliases {
+ gpio0 = &gpio1;
+ gpio1 = &gpio2;
+ gpio2 = &gpio3;
+ gpio3 = &gpio4;
+ gpio4 = &gpio5;
+ gpio5 = &gpio6;
+ serial0 = &uart1;
+ serial1 = &uart2;
+ serial2 = &uart3;
+ serial3 = &uart4;
+ serial4 = &uart5;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a8";
+ reg = <0x0>;
+ };
+ };
+
+ tzic: tz-interrupt-controller@0fffc000 {
+ compatible = "fsl,imx50-tzic", "fsl,imx53-tzic", "fsl,tzic";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0x0fffc000 0x4000>;
+ };
+
+ clocks {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ckil {
+ compatible = "fsl,imx-ckil", "fixed-clock";
+ clock-frequency = <32768>;
+ };
+
+ ckih1 {
+ compatible = "fsl,imx-ckih1", "fixed-clock";
+ clock-frequency = <22579200>;
+ };
+
+ ckih2 {
+ compatible = "fsl,imx-ckih2", "fixed-clock";
+ clock-frequency = <0>;
+ };
+
+ osc {
+ compatible = "fsl,imx-osc", "fixed-clock";
+ clock-frequency = <24000000>;
+ };
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ interrupt-parent = <&tzic>;
+ ranges;
+
+ aips@50000000 { /* AIPS1 */
+ compatible = "fsl,aips-bus", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x50000000 0x10000000>;
+ ranges;
+
+ spba@50000000 {
+ compatible = "fsl,spba-bus", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x50000000 0x40000>;
+ ranges;
+
+ esdhc1: esdhc@50004000 {
+ compatible = "fsl,imx50-esdhc";
+ reg = <0x50004000 0x4000>;
+ interrupts = <1>;
+ clocks = <&clks IMX5_CLK_ESDHC1_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC1_PER_GATE>;
+ clock-names = "ipg", "ahb", "per";
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ esdhc2: esdhc@50008000 {
+ compatible = "fsl,imx50-esdhc";
+ reg = <0x50008000 0x4000>;
+ interrupts = <2>;
+ clocks = <&clks IMX5_CLK_ESDHC2_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC2_PER_GATE>;
+ clock-names = "ipg", "ahb", "per";
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ uart3: serial@5000c000 {
+ compatible = "fsl,imx50-uart", "fsl,imx21-uart";
+ reg = <0x5000c000 0x4000>;
+ interrupts = <33>;
+ clocks = <&clks IMX5_CLK_UART3_IPG_GATE>,
+ <&clks IMX5_CLK_UART3_PER_GATE>;
+ clock-names = "ipg", "per";
+ status = "disabled";
+ };
+
+ ecspi1: ecspi@50010000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx50-ecspi", "fsl,imx51-ecspi";
+ reg = <0x50010000 0x4000>;
+ interrupts = <36>;
+ clocks = <&clks IMX5_CLK_ECSPI1_IPG_GATE>,
+ <&clks IMX5_CLK_ECSPI1_PER_GATE>;
+ clock-names = "ipg", "per";
+ status = "disabled";
+ };
+
+ ssi2: ssi@50014000 {
+ compatible = "fsl,imx50-ssi",
+ "fsl,imx51-ssi",
+ "fsl,imx21-ssi";
+ reg = <0x50014000 0x4000>;
+ interrupts = <30>;
+ clocks = <&clks IMX5_CLK_SSI2_IPG_GATE>;
+ fsl,fifo-depth = <15>;
+ fsl,ssi-dma-events = <25 24 23 22>; /* TX0 RX0 TX1 RX1 */
+ status = "disabled";
+ };
+
+ esdhc3: esdhc@50020000 {
+ compatible = "fsl,imx50-esdhc";
+ reg = <0x50020000 0x4000>;
+ interrupts = <3>;
+ clocks = <&clks IMX5_CLK_ESDHC3_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC3_PER_GATE>;
+ clock-names = "ipg", "ahb", "per";
+ bus-width = <4>;
+ status = "disabled";
+ };
+
+ esdhc4: esdhc@50024000 {
+ compatible = "fsl,imx50-esdhc";
+ reg = <0x50024000 0x4000>;
+ interrupts = <4>;
+ clocks = <&clks IMX5_CLK_ESDHC4_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC4_PER_GATE>;
+ clock-names = "ipg", "ahb", "per";
+ bus-width = <4>;
+ status = "disabled";
+ };
+ };
+
+ usbotg: usb@53f80000 {
+ compatible = "fsl,imx50-usb", "fsl,imx27-usb";
+ reg = <0x53f80000 0x0200>;
+ interrupts = <18>;
+ clocks = <&clks IMX5_CLK_USB_PHY1_GATE>;
+ status = "disabled";
+ };
+
+ usbh1: usb@53f80200 {
+ compatible = "fsl,imx50-usb", "fsl,imx27-usb";
+ reg = <0x53f80200 0x0200>;
+ interrupts = <14>;
+ clocks = <&clks IMX5_CLK_USB_PHY2_GATE>;
+ status = "disabled";
+ };
+
+ usbh2: usb@53f80400 {
+ compatible = "fsl,imx50-usb", "fsl,imx27-usb";
+ reg = <0x53f80400 0x0200>;
+ interrupts = <16>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
+ status = "disabled";
+ };
+
+ usbh3: usb@53f80600 {
+ compatible = "fsl,imx50-usb", "fsl,imx27-usb";
+ reg = <0x53f80600 0x0200>;
+ interrupts = <17>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
+ status = "disabled";
+ };
+
+ gpio1: gpio@53f84000 {
+ compatible = "fsl,imx50-gpio", "fsl,imx35-gpio";
+ reg = <0x53f84000 0x4000>;
+ interrupts = <50 51>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio2: gpio@53f88000 {
+ compatible = "fsl,imx50-gpio", "fsl,imx35-gpio";
+ reg = <0x53f88000 0x4000>;
+ interrupts = <52 53>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio3: gpio@53f8c000 {
+ compatible = "fsl,imx50-gpio", "fsl,imx35-gpio";
+ reg = <0x53f8c000 0x4000>;
+ interrupts = <54 55>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio4: gpio@53f90000 {
+ compatible = "fsl,imx50-gpio", "fsl,imx35-gpio";
+ reg = <0x53f90000 0x4000>;
+ interrupts = <56 57>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ wdog1: wdog@53f98000 {
+ compatible = "fsl,imx50-wdt", "fsl,imx21-wdt";
+ reg = <0x53f98000 0x4000>;
+ interrupts = <58>;
+ clocks = <&clks IMX5_CLK_DUMMY>;
+ };
+
+ gpt: timer@53fa0000 {
+ compatible = "fsl,imx50-gpt", "fsl,imx31-gpt";
+ reg = <0x53fa0000 0x4000>;
+ interrupts = <39>;
+ clocks = <&clks IMX5_CLK_GPT_IPG_GATE>,
+ <&clks IMX5_CLK_GPT_HF_GATE>;
+ clock-names = "ipg", "per";
+ };
+
+ iomuxc: iomuxc@53fa8000 {
+ compatible = "fsl,imx50-iomuxc", "fsl,imx53-iomuxc";
+ reg = <0x53fa8000 0x4000>;
+ };
+
+ gpr: iomuxc-gpr@53fa8000 {
+ compatible = "fsl,imx50-iomuxc-gpr", "syscon";
+ reg = <0x53fa8000 0xc>;
+ };
+
+ pwm1: pwm@53fb4000 {
+ #pwm-cells = <2>;
+ compatible = "fsl,imx50-pwm", "fsl,imx27-pwm";
+ reg = <0x53fb4000 0x4000>;
+ clocks = <&clks IMX5_CLK_PWM1_IPG_GATE>,
+ <&clks IMX5_CLK_PWM1_HF_GATE>;
+ clock-names = "ipg", "per";
+ interrupts = <61>;
+ };
+
+ pwm2: pwm@53fb8000 {
+ #pwm-cells = <2>;
+ compatible = "fsl,imx50-pwm", "fsl,imx27-pwm";
+ reg = <0x53fb8000 0x4000>;
+ clocks = <&clks IMX5_CLK_PWM2_IPG_GATE>,
+ <&clks IMX5_CLK_PWM2_HF_GATE>;
+ clock-names = "ipg", "per";
+ interrupts = <94>;
+ };
+
+ uart1: serial@53fbc000 {
+ compatible = "fsl,imx50-uart", "fsl,imx21-uart";
+ reg = <0x53fbc000 0x4000>;
+ interrupts = <31>;
+ clocks = <&clks IMX5_CLK_UART1_IPG_GATE>,
+ <&clks IMX5_CLK_UART1_PER_GATE>;
+ clock-names = "ipg", "per";
+ status = "disabled";
+ };
+
+ uart2: serial@53fc0000 {
+ compatible = "fsl,imx50-uart", "fsl,imx21-uart";
+ reg = <0x53fc0000 0x4000>;
+ interrupts = <32>;
+ clocks = <&clks IMX5_CLK_UART2_IPG_GATE>,
+ <&clks IMX5_CLK_UART2_PER_GATE>;
+ clock-names = "ipg", "per";
+ status = "disabled";
+ };
+
+ src: src@53fd0000 {
+ compatible = "fsl,imx50-src", "fsl,imx51-src";
+ reg = <0x53fd0000 0x4000>;
+ #reset-cells = <1>;
+ };
+
+ clks: ccm@53fd4000{
+ compatible = "fsl,imx50-ccm";
+ reg = <0x53fd4000 0x4000>;
+ interrupts = <0 71 0x04 0 72 0x04>;
+ #clock-cells = <1>;
+ };
+
+ gpio5: gpio@53fdc000 {
+ compatible = "fsl,imx50-gpio", "fsl,imx35-gpio";
+ reg = <0x53fdc000 0x4000>;
+ interrupts = <103 104>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio6: gpio@53fe0000 {
+ compatible = "fsl,imx50-gpio", "fsl,imx35-gpio";
+ reg = <0x53fe0000 0x4000>;
+ interrupts = <105 106>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ i2c3: i2c@53fec000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx50-i2c", "fsl,imx21-i2c";
+ reg = <0x53fec000 0x4000>;
+ interrupts = <64>;
+ clocks = <&clks IMX5_CLK_I2C3_GATE>;
+ status = "disabled";
+ };
+
+ uart4: serial@53ff0000 {
+ compatible = "fsl,imx50-uart", "fsl,imx21-uart";
+ reg = <0x53ff0000 0x4000>;
+ interrupts = <13>;
+ clocks = <&clks IMX5_CLK_UART4_IPG_GATE>,
+ <&clks IMX5_CLK_UART4_PER_GATE>;
+ clock-names = "ipg", "per";
+ status = "disabled";
+ };
+ };
+
+ aips@60000000 { /* AIPS2 */
+ compatible = "fsl,aips-bus", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x60000000 0x10000000>;
+ ranges;
+
+ uart5: serial@63f90000 {
+ compatible = "fsl,imx50-uart", "fsl,imx21-uart";
+ reg = <0x63f90000 0x4000>;
+ interrupts = <86>;
+ clocks = <&clks IMX5_CLK_UART5_IPG_GATE>,
+ <&clks IMX5_CLK_UART5_PER_GATE>;
+ clock-names = "ipg", "per";
+ status = "disabled";
+ };
+
+ owire: owire@63fa4000 {
+ compatible = "fsl,imx50-owire", "fsl,imx21-owire";
+ reg = <0x63fa4000 0x4000>;
+ clocks = <&clks IMX5_CLK_OWIRE_GATE>;
+ status = "disabled";
+ };
+
+ ecspi2: ecspi@63fac000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx50-ecspi", "fsl,imx51-ecspi";
+ reg = <0x63fac000 0x4000>;
+ interrupts = <37>;
+ clocks = <&clks IMX5_CLK_ECSPI2_IPG_GATE>,
+ <&clks IMX5_CLK_ECSPI2_PER_GATE>;
+ clock-names = "ipg", "per";
+ status = "disabled";
+ };
+
+ sdma: sdma@63fb0000 {
+ compatible = "fsl,imx50-sdma", "fsl,imx35-sdma";
+ reg = <0x63fb0000 0x4000>;
+ interrupts = <6>;
+ clocks = <&clks IMX5_CLK_SDMA_GATE>,
+ <&clks IMX5_CLK_SDMA_GATE>;
+ clock-names = "ipg", "ahb";
+ fsl,sdma-ram-script-name = "imx/sdma/sdma-imx50.bin";
+ };
+
+ cspi: cspi@63fc0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx50-cspi", "fsl,imx35-cspi";
+ reg = <0x63fc0000 0x4000>;
+ interrupts = <38>;
+ clocks = <&clks IMX5_CLK_CSPI_IPG_GATE>,
+ <&clks IMX5_CLK_CSPI_IPG_GATE>;
+ clock-names = "ipg", "per";
+ status = "disabled";
+ };
+
+ i2c2: i2c@63fc4000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx50-i2c", "fsl,imx21-i2c";
+ reg = <0x63fc4000 0x4000>;
+ interrupts = <63>;
+ clocks = <&clks IMX5_CLK_I2C2_GATE>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@63fc8000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx50-i2c", "fsl,imx21-i2c";
+ reg = <0x63fc8000 0x4000>;
+ interrupts = <62>;
+ clocks = <&clks IMX5_CLK_I2C1_GATE>;
+ status = "disabled";
+ };
+
+ ssi1: ssi@63fcc000 {
+ compatible = "fsl,imx50-ssi", "fsl,imx51-ssi",
+ "fsl,imx21-ssi";
+ reg = <0x63fcc000 0x4000>;
+ interrupts = <29>;
+ clocks = <&clks IMX5_CLK_SSI1_IPG_GATE>;
+ fsl,fifo-depth = <15>;
+ fsl,ssi-dma-events = <29 28 27 26>; /* TX0 RX0 TX1 RX1 */
+ status = "disabled";
+ };
+
+ audmux: audmux@63fd0000 {
+ compatible = "fsl,imx50-audmux", "fsl,imx31-audmux";
+ reg = <0x63fd0000 0x4000>;
+ status = "disabled";
+ };
+
+ fec: ethernet@63fec000 {
+ compatible = "fsl,imx53-fec", "fsl,imx25-fec";
+ reg = <0x63fec000 0x4000>;
+ interrupts = <87>;
+ clocks = <&clks IMX5_CLK_FEC_GATE>,
+ <&clks IMX5_CLK_FEC_GATE>,
+ <&clks IMX5_CLK_FEC_GATE>;
+ clock-names = "ipg", "ahb", "ptp";
+ status = "disabled";
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/imx51-apf51.dts b/arch/arm/boot/dts/imx51-apf51.dts
index b3606993f2e8..e88b2a6be079 100644
--- a/arch/arm/boot/dts/imx51-apf51.dts
+++ b/arch/arm/boot/dts/imx51-apf51.dts
@@ -34,13 +34,47 @@
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec_2>;
+ pinctrl-0 = <&pinctrl_fec>;
phy-mode = "mii";
- phy-reset-gpios = <&gpio3 0 0>;
+ phy-reset-gpios = <&gpio3 0 GPIO_ACTIVE_HIGH>;
phy-reset-duration = <1>;
status = "okay";
};
+&iomuxc {
+ imx51-apf51 {
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX51_PAD_DI_GP3__FEC_TX_ER 0x80000000
+ MX51_PAD_DI2_PIN4__FEC_CRS 0x80000000
+ MX51_PAD_DI2_PIN2__FEC_MDC 0x80000000
+ MX51_PAD_DI2_PIN3__FEC_MDIO 0x80000000
+ MX51_PAD_DI2_DISP_CLK__FEC_RDATA1 0x80000000
+ MX51_PAD_DI_GP4__FEC_RDATA2 0x80000000
+ MX51_PAD_DISP2_DAT0__FEC_RDATA3 0x80000000
+ MX51_PAD_DISP2_DAT1__FEC_RX_ER 0x80000000
+ MX51_PAD_DISP2_DAT6__FEC_TDATA1 0x80000000
+ MX51_PAD_DISP2_DAT7__FEC_TDATA2 0x80000000
+ MX51_PAD_DISP2_DAT8__FEC_TDATA3 0x80000000
+ MX51_PAD_DISP2_DAT9__FEC_TX_EN 0x80000000
+ MX51_PAD_DISP2_DAT10__FEC_COL 0x80000000
+ MX51_PAD_DISP2_DAT11__FEC_RX_CLK 0x80000000
+ MX51_PAD_DISP2_DAT12__FEC_RX_DV 0x80000000
+ MX51_PAD_DISP2_DAT13__FEC_TX_CLK 0x80000000
+ MX51_PAD_DISP2_DAT14__FEC_RDATA0 0x80000000
+ MX51_PAD_DISP2_DAT15__FEC_TDATA0 0x80000000
+ >;
+ };
+
+ pinctrl_uart3: uart3grp {
+ fsl,pins = <
+ MX51_PAD_UART3_RXD__UART3_RXD 0x1c5
+ MX51_PAD_UART3_TXD__UART3_TXD 0x1c5
+ >;
+ };
+ };
+};
+
&nfc {
nand-bus-width = <8>;
nand-ecc-mode = "hw";
@@ -50,6 +84,6 @@
&uart3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart3_2>;
+ pinctrl-0 = <&pinctrl_uart3>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx51-apf51dev.dts b/arch/arm/boot/dts/imx51-apf51dev.dts
index d3f98141462c..c5a9a24c280a 100644
--- a/arch/arm/boot/dts/imx51-apf51dev.dts
+++ b/arch/arm/boot/dts/imx51-apf51dev.dts
@@ -20,7 +20,7 @@
compatible = "fsl,imx-parallel-display";
interface-pix-fmt = "bgr666";
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ipu_disp1_1>;
+ pinctrl-0 = <&pinctrl_ipu_disp1>;
display-timings {
lw700 {
@@ -53,7 +53,7 @@
user-key {
label = "user";
- gpios = <&gpio1 3 0>;
+ gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;
linux,code = <256>; /* BTN_0 */
};
};
@@ -63,7 +63,7 @@
user {
label = "Heartbeat";
- gpios = <&gpio1 2 0>;
+ gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
};
};
@@ -71,31 +71,33 @@
&ecspi1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi1_1>;
+ pinctrl-0 = <&pinctrl_ecspi1>;
fsl,spi-num-chipselects = <2>;
- cs-gpios = <&gpio4 24 0>, <&gpio4 25 0>;
+ cs-gpios = <&gpio4 24 GPIO_ACTIVE_HIGH>,
+ <&gpio4 25 GPIO_ACTIVE_HIGH>;
status = "okay";
};
&ecspi2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi2_1>;
+ pinctrl-0 = <&pinctrl_ecspi2>;
fsl,spi-num-chipselects = <2>;
- cs-gpios = <&gpio3 28 1>, <&gpio3 27 1>;
+ cs-gpios = <&gpio3 28 GPIO_ACTIVE_LOW>,
+ <&gpio3 27 GPIO_ACTIVE_LOW>;
status = "okay";
};
&esdhc1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc1_1>;
- cd-gpios = <&gpio2 29 0>;
+ pinctrl-0 = <&pinctrl_esdhc1>;
+ cd-gpios = <&gpio2 29 GPIO_ACTIVE_HIGH>;
bus-width = <4>;
status = "okay";
};
&esdhc2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc2_1>;
+ pinctrl-0 = <&pinctrl_esdhc2>;
bus-width = <4>;
non-removable;
status = "okay";
@@ -103,7 +105,7 @@
&i2c2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c2_2>;
+ pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
};
@@ -111,7 +113,7 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- hog {
+ imx51-apf51dev {
pinctrl_hog: hoggrp {
fsl,pins = <
MX51_PAD_EIM_EB2__GPIO2_22 0x0C5
@@ -125,6 +127,82 @@
MX51_PAD_GPIO1_3__GPIO1_3 0x0C5
>;
};
+
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX51_PAD_CSPI1_MISO__ECSPI1_MISO 0x185
+ MX51_PAD_CSPI1_MOSI__ECSPI1_MOSI 0x185
+ MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK 0x185
+ >;
+ };
+
+ pinctrl_ecspi2: ecspi2grp {
+ fsl,pins = <
+ MX51_PAD_NANDF_RB3__ECSPI2_MISO 0x185
+ MX51_PAD_NANDF_D15__ECSPI2_MOSI 0x185
+ MX51_PAD_NANDF_RB2__ECSPI2_SCLK 0x185
+ >;
+ };
+
+ pinctrl_esdhc1: esdhc1grp {
+ fsl,pins = <
+ MX51_PAD_SD1_CMD__SD1_CMD 0x400020d5
+ MX51_PAD_SD1_CLK__SD1_CLK 0x20d5
+ MX51_PAD_SD1_DATA0__SD1_DATA0 0x20d5
+ MX51_PAD_SD1_DATA1__SD1_DATA1 0x20d5
+ MX51_PAD_SD1_DATA2__SD1_DATA2 0x20d5
+ MX51_PAD_SD1_DATA3__SD1_DATA3 0x20d5
+ >;
+ };
+
+ pinctrl_esdhc2: esdhc2grp {
+ fsl,pins = <
+ MX51_PAD_SD2_CMD__SD2_CMD 0x400020d5
+ MX51_PAD_SD2_CLK__SD2_CLK 0x20d5
+ MX51_PAD_SD2_DATA0__SD2_DATA0 0x20d5
+ MX51_PAD_SD2_DATA1__SD2_DATA1 0x20d5
+ MX51_PAD_SD2_DATA2__SD2_DATA2 0x20d5
+ MX51_PAD_SD2_DATA3__SD2_DATA3 0x20d5
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX51_PAD_EIM_D27__I2C2_SCL 0x400001ed
+ MX51_PAD_EIM_D24__I2C2_SDA 0x400001ed
+ >;
+ };
+
+ pinctrl_ipu_disp1: ipudisp1grp {
+ fsl,pins = <
+ MX51_PAD_DISP1_DAT0__DISP1_DAT0 0x5
+ MX51_PAD_DISP1_DAT1__DISP1_DAT1 0x5
+ MX51_PAD_DISP1_DAT2__DISP1_DAT2 0x5
+ MX51_PAD_DISP1_DAT3__DISP1_DAT3 0x5
+ MX51_PAD_DISP1_DAT4__DISP1_DAT4 0x5
+ MX51_PAD_DISP1_DAT5__DISP1_DAT5 0x5
+ MX51_PAD_DISP1_DAT6__DISP1_DAT6 0x5
+ MX51_PAD_DISP1_DAT7__DISP1_DAT7 0x5
+ MX51_PAD_DISP1_DAT8__DISP1_DAT8 0x5
+ MX51_PAD_DISP1_DAT9__DISP1_DAT9 0x5
+ MX51_PAD_DISP1_DAT10__DISP1_DAT10 0x5
+ MX51_PAD_DISP1_DAT11__DISP1_DAT11 0x5
+ MX51_PAD_DISP1_DAT12__DISP1_DAT12 0x5
+ MX51_PAD_DISP1_DAT13__DISP1_DAT13 0x5
+ MX51_PAD_DISP1_DAT14__DISP1_DAT14 0x5
+ MX51_PAD_DISP1_DAT15__DISP1_DAT15 0x5
+ MX51_PAD_DISP1_DAT16__DISP1_DAT16 0x5
+ MX51_PAD_DISP1_DAT17__DISP1_DAT17 0x5
+ MX51_PAD_DISP1_DAT18__DISP1_DAT18 0x5
+ MX51_PAD_DISP1_DAT19__DISP1_DAT19 0x5
+ MX51_PAD_DISP1_DAT20__DISP1_DAT20 0x5
+ MX51_PAD_DISP1_DAT21__DISP1_DAT21 0x5
+ MX51_PAD_DISP1_DAT22__DISP1_DAT22 0x5
+ MX51_PAD_DISP1_DAT23__DISP1_DAT23 0x5
+ MX51_PAD_DI1_PIN2__DI1_PIN2 0x5
+ MX51_PAD_DI1_PIN3__DI1_PIN3 0x5
+ >;
+ };
};
};
diff --git a/arch/arm/boot/dts/imx51-babbage.dts b/arch/arm/boot/dts/imx51-babbage.dts
index 671927145632..9e9deb244b76 100644
--- a/arch/arm/boot/dts/imx51-babbage.dts
+++ b/arch/arm/boot/dts/imx51-babbage.dts
@@ -25,7 +25,7 @@
compatible = "fsl,imx-parallel-display";
interface-pix-fmt = "rgb24";
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ipu_disp1_1>;
+ pinctrl-0 = <&pinctrl_ipu_disp1>;
display-timings {
native-mode = <&timing0>;
timing0: dvi {
@@ -52,7 +52,7 @@
compatible = "fsl,imx-parallel-display";
interface-pix-fmt = "rgb565";
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ipu_disp2_1>;
+ pinctrl-0 = <&pinctrl_ipu_disp2>;
status = "disabled";
display-timings {
native-mode = <&timing1>;
@@ -85,12 +85,23 @@
power {
label = "Power Button";
- gpios = <&gpio2 21 0>;
+ gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>;
linux,code = <116>; /* KEY_POWER */
gpio-key,wakeup;
};
};
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpio_leds>;
+
+ led-diagnostic {
+ label = "diagnostic";
+ gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
sound {
compatible = "fsl,imx51-babbage-sgtl5000",
"fsl,imx-audio-sgtl5000";
@@ -115,14 +126,14 @@
reg=<0>;
#clock-cells = <0>;
clock-frequency = <26000000>;
- gpios = <&gpio4 26 1>;
+ gpios = <&gpio4 26 GPIO_ACTIVE_LOW>;
};
};
};
&esdhc1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc1_1>;
+ pinctrl-0 = <&pinctrl_esdhc1>;
fsl,cd-controller;
fsl,wp-controller;
status = "okay";
@@ -130,24 +141,25 @@
&esdhc2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc2_1>;
- cd-gpios = <&gpio1 6 0>;
- wp-gpios = <&gpio1 5 0>;
+ pinctrl-0 = <&pinctrl_esdhc2>;
+ cd-gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>;
+ wp-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>;
status = "okay";
};
&uart3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart3_1 &pinctrl_uart3_rtscts_1>;
+ pinctrl-0 = <&pinctrl_uart3>;
fsl,uart-has-rtscts;
status = "okay";
};
&ecspi1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi1_1>;
+ pinctrl-0 = <&pinctrl_ecspi1>;
fsl,spi-num-chipselects = <2>;
- cs-gpios = <&gpio4 24 0>, <&gpio4 25 0>;
+ cs-gpios = <&gpio4 24 GPIO_ACTIVE_HIGH>,
+ <&gpio4 25 GPIO_ACTIVE_LOW>;
status = "okay";
pmic: mc13892@0 {
@@ -158,7 +170,7 @@
spi-cs-high;
reg = <0>;
interrupt-parent = <&gpio1>;
- interrupts = <8 0x4>;
+ interrupts = <8 IRQ_TYPE_LEVEL_HIGH>;
regulators {
sw1_reg: sw1 {
@@ -285,7 +297,7 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- hog {
+ imx51-babbage {
pinctrl_hog: hoggrp {
fsl,pins = <
MX51_PAD_GPIO1_0__SD1_CD 0x20d5
@@ -298,25 +310,194 @@
MX51_PAD_CSPI1_RDY__GPIO4_26 0x80000000
>;
};
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX51_PAD_AUD3_BB_TXD__AUD3_TXD 0x80000000
+ MX51_PAD_AUD3_BB_RXD__AUD3_RXD 0x80000000
+ MX51_PAD_AUD3_BB_CK__AUD3_TXC 0x80000000
+ MX51_PAD_AUD3_BB_FS__AUD3_TXFS 0x80000000
+ >;
+ };
+
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX51_PAD_CSPI1_MISO__ECSPI1_MISO 0x185
+ MX51_PAD_CSPI1_MOSI__ECSPI1_MOSI 0x185
+ MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK 0x185
+ >;
+ };
+
+ pinctrl_esdhc1: esdhc1grp {
+ fsl,pins = <
+ MX51_PAD_SD1_CMD__SD1_CMD 0x400020d5
+ MX51_PAD_SD1_CLK__SD1_CLK 0x20d5
+ MX51_PAD_SD1_DATA0__SD1_DATA0 0x20d5
+ MX51_PAD_SD1_DATA1__SD1_DATA1 0x20d5
+ MX51_PAD_SD1_DATA2__SD1_DATA2 0x20d5
+ MX51_PAD_SD1_DATA3__SD1_DATA3 0x20d5
+ >;
+ };
+
+ pinctrl_esdhc2: esdhc2grp {
+ fsl,pins = <
+ MX51_PAD_SD2_CMD__SD2_CMD 0x400020d5
+ MX51_PAD_SD2_CLK__SD2_CLK 0x20d5
+ MX51_PAD_SD2_DATA0__SD2_DATA0 0x20d5
+ MX51_PAD_SD2_DATA1__SD2_DATA1 0x20d5
+ MX51_PAD_SD2_DATA2__SD2_DATA2 0x20d5
+ MX51_PAD_SD2_DATA3__SD2_DATA3 0x20d5
+ >;
+ };
+
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX51_PAD_EIM_EB2__FEC_MDIO 0x80000000
+ MX51_PAD_EIM_EB3__FEC_RDATA1 0x80000000
+ MX51_PAD_EIM_CS2__FEC_RDATA2 0x80000000
+ MX51_PAD_EIM_CS3__FEC_RDATA3 0x80000000
+ MX51_PAD_EIM_CS4__FEC_RX_ER 0x80000000
+ MX51_PAD_EIM_CS5__FEC_CRS 0x80000000
+ MX51_PAD_NANDF_RB2__FEC_COL 0x80000000
+ MX51_PAD_NANDF_RB3__FEC_RX_CLK 0x80000000
+ MX51_PAD_NANDF_D9__FEC_RDATA0 0x80000000
+ MX51_PAD_NANDF_D8__FEC_TDATA0 0x80000000
+ MX51_PAD_NANDF_CS2__FEC_TX_ER 0x80000000
+ MX51_PAD_NANDF_CS3__FEC_MDC 0x80000000
+ MX51_PAD_NANDF_CS4__FEC_TDATA1 0x80000000
+ MX51_PAD_NANDF_CS5__FEC_TDATA2 0x80000000
+ MX51_PAD_NANDF_CS6__FEC_TDATA3 0x80000000
+ MX51_PAD_NANDF_CS7__FEC_TX_EN 0x80000000
+ MX51_PAD_NANDF_RDY_INT__FEC_TX_CLK 0x80000000
+ MX51_PAD_EIM_A20__GPIO2_14 0x85 /* Reset */
+ >;
+ };
+
+ pinctrl_gpio_leds: gpioledsgrp {
+ fsl,pins = <
+ MX51_PAD_EIM_D22__GPIO2_6 0x80000000
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX51_PAD_KEY_COL4__I2C2_SCL 0x400001ed
+ MX51_PAD_KEY_COL5__I2C2_SDA 0x400001ed
+ >;
+ };
+
+ pinctrl_ipu_disp1: ipudisp1grp {
+ fsl,pins = <
+ MX51_PAD_DISP1_DAT0__DISP1_DAT0 0x5
+ MX51_PAD_DISP1_DAT1__DISP1_DAT1 0x5
+ MX51_PAD_DISP1_DAT2__DISP1_DAT2 0x5
+ MX51_PAD_DISP1_DAT3__DISP1_DAT3 0x5
+ MX51_PAD_DISP1_DAT4__DISP1_DAT4 0x5
+ MX51_PAD_DISP1_DAT5__DISP1_DAT5 0x5
+ MX51_PAD_DISP1_DAT6__DISP1_DAT6 0x5
+ MX51_PAD_DISP1_DAT7__DISP1_DAT7 0x5
+ MX51_PAD_DISP1_DAT8__DISP1_DAT8 0x5
+ MX51_PAD_DISP1_DAT9__DISP1_DAT9 0x5
+ MX51_PAD_DISP1_DAT10__DISP1_DAT10 0x5
+ MX51_PAD_DISP1_DAT11__DISP1_DAT11 0x5
+ MX51_PAD_DISP1_DAT12__DISP1_DAT12 0x5
+ MX51_PAD_DISP1_DAT13__DISP1_DAT13 0x5
+ MX51_PAD_DISP1_DAT14__DISP1_DAT14 0x5
+ MX51_PAD_DISP1_DAT15__DISP1_DAT15 0x5
+ MX51_PAD_DISP1_DAT16__DISP1_DAT16 0x5
+ MX51_PAD_DISP1_DAT17__DISP1_DAT17 0x5
+ MX51_PAD_DISP1_DAT18__DISP1_DAT18 0x5
+ MX51_PAD_DISP1_DAT19__DISP1_DAT19 0x5
+ MX51_PAD_DISP1_DAT20__DISP1_DAT20 0x5
+ MX51_PAD_DISP1_DAT21__DISP1_DAT21 0x5
+ MX51_PAD_DISP1_DAT22__DISP1_DAT22 0x5
+ MX51_PAD_DISP1_DAT23__DISP1_DAT23 0x5
+ MX51_PAD_DI1_PIN2__DI1_PIN2 0x5
+ MX51_PAD_DI1_PIN3__DI1_PIN3 0x5
+ >;
+ };
+
+ pinctrl_ipu_disp2: ipudisp2grp {
+ fsl,pins = <
+ MX51_PAD_DISP2_DAT0__DISP2_DAT0 0x5
+ MX51_PAD_DISP2_DAT1__DISP2_DAT1 0x5
+ MX51_PAD_DISP2_DAT2__DISP2_DAT2 0x5
+ MX51_PAD_DISP2_DAT3__DISP2_DAT3 0x5
+ MX51_PAD_DISP2_DAT4__DISP2_DAT4 0x5
+ MX51_PAD_DISP2_DAT5__DISP2_DAT5 0x5
+ MX51_PAD_DISP2_DAT6__DISP2_DAT6 0x5
+ MX51_PAD_DISP2_DAT7__DISP2_DAT7 0x5
+ MX51_PAD_DISP2_DAT8__DISP2_DAT8 0x5
+ MX51_PAD_DISP2_DAT9__DISP2_DAT9 0x5
+ MX51_PAD_DISP2_DAT10__DISP2_DAT10 0x5
+ MX51_PAD_DISP2_DAT11__DISP2_DAT11 0x5
+ MX51_PAD_DISP2_DAT12__DISP2_DAT12 0x5
+ MX51_PAD_DISP2_DAT13__DISP2_DAT13 0x5
+ MX51_PAD_DISP2_DAT14__DISP2_DAT14 0x5
+ MX51_PAD_DISP2_DAT15__DISP2_DAT15 0x5
+ MX51_PAD_DI2_PIN2__DI2_PIN2 0x5
+ MX51_PAD_DI2_PIN3__DI2_PIN3 0x5
+ MX51_PAD_DI2_DISP_CLK__DI2_DISP_CLK 0x5
+ MX51_PAD_DI_GP4__DI2_PIN15 0x5
+ >;
+ };
+
+ pinctrl_kpp: kppgrp {
+ fsl,pins = <
+ MX51_PAD_KEY_ROW0__KEY_ROW0 0xe0
+ MX51_PAD_KEY_ROW1__KEY_ROW1 0xe0
+ MX51_PAD_KEY_ROW2__KEY_ROW2 0xe0
+ MX51_PAD_KEY_ROW3__KEY_ROW3 0xe0
+ MX51_PAD_KEY_COL0__KEY_COL0 0xe8
+ MX51_PAD_KEY_COL1__KEY_COL1 0xe8
+ MX51_PAD_KEY_COL2__KEY_COL2 0xe8
+ MX51_PAD_KEY_COL3__KEY_COL3 0xe8
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX51_PAD_UART1_RXD__UART1_RXD 0x1c5
+ MX51_PAD_UART1_TXD__UART1_TXD 0x1c5
+ MX51_PAD_UART1_RTS__UART1_RTS 0x1c5
+ MX51_PAD_UART1_CTS__UART1_CTS 0x1c5
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX51_PAD_UART2_RXD__UART2_RXD 0x1c5
+ MX51_PAD_UART2_TXD__UART2_TXD 0x1c5
+ >;
+ };
+
+ pinctrl_uart3: uart3grp {
+ fsl,pins = <
+ MX51_PAD_EIM_D25__UART3_RXD 0x1c5
+ MX51_PAD_EIM_D26__UART3_TXD 0x1c5
+ MX51_PAD_EIM_D27__UART3_RTS 0x1c5
+ MX51_PAD_EIM_D24__UART3_CTS 0x1c5
+ >;
+ };
};
};
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_1 &pinctrl_uart1_rtscts_1>;
+ pinctrl-0 = <&pinctrl_uart1>;
fsl,uart-has-rtscts;
status = "okay";
};
&uart2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart2_1>;
+ pinctrl-0 = <&pinctrl_uart2>;
status = "okay";
};
&i2c2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c2_1>;
+ pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
sgtl5000: codec@0a {
@@ -330,35 +511,39 @@
&audmux {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_audmux_1>;
+ pinctrl-0 = <&pinctrl_audmux>;
status = "okay";
};
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec_1>;
+ pinctrl-0 = <&pinctrl_fec>;
phy-mode = "mii";
+ phy-reset-gpios = <&gpio2 14 GPIO_ACTIVE_LOW>;
+ phy-reset-duration = <1>;
status = "okay";
};
&kpp {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_kpp_1>;
- linux,keymap = <0x00000067 /* KEY_UP */
- 0x0001006c /* KEY_DOWN */
- 0x00020072 /* KEY_VOLUMEDOWN */
- 0x00030066 /* KEY_HOME */
- 0x0100006a /* KEY_RIGHT */
- 0x01010069 /* KEY_LEFT */
- 0x0102001c /* KEY_ENTER */
- 0x01030073 /* KEY_VOLUMEUP */
- 0x02000040 /* KEY_F6 */
- 0x02010042 /* KEY_F8 */
- 0x02020043 /* KEY_F9 */
- 0x02030044 /* KEY_F10 */
- 0x0300003b /* KEY_F1 */
- 0x0301003c /* KEY_F2 */
- 0x0302003d /* KEY_F3 */
- 0x03030074>; /* KEY_POWER */
+ pinctrl-0 = <&pinctrl_kpp>;
+ linux,keymap = <
+ MATRIX_KEY(0, 0, KEY_UP)
+ MATRIX_KEY(0, 1, KEY_DOWN)
+ MATRIX_KEY(0, 2, KEY_VOLUMEDOWN)
+ MATRIX_KEY(0, 3, KEY_HOME)
+ MATRIX_KEY(1, 0, KEY_RIGHT)
+ MATRIX_KEY(1, 1, KEY_LEFT)
+ MATRIX_KEY(1, 2, KEY_ENTER)
+ MATRIX_KEY(1, 3, KEY_VOLUMEUP)
+ MATRIX_KEY(2, 0, KEY_F6)
+ MATRIX_KEY(2, 1, KEY_F8)
+ MATRIX_KEY(2, 2, KEY_F9)
+ MATRIX_KEY(2, 3, KEY_F10)
+ MATRIX_KEY(3, 0, KEY_F1)
+ MATRIX_KEY(3, 1, KEY_F2)
+ MATRIX_KEY(3, 2, KEY_F3)
+ MATRIX_KEY(3, 3, KEY_POWER)
+ >;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi b/arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi
new file mode 100644
index 000000000000..9b3acf6e4282
--- /dev/null
+++ b/arch/arm/boot/dts/imx51-eukrea-cpuimx51.dtsi
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2013 Eukréa Electromatique <denis@eukrea.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include "imx51.dtsi"
+
+/ {
+ model = "Eukrea CPUIMX51";
+ compatible = "eukrea,cpuimx51", "fsl,imx51";
+
+ memory {
+ reg = <0x90000000 0x10000000>; /* 256M */
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fec>;
+ status = "okay";
+};
+
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ pcf8563@51 {
+ compatible = "nxp,pcf8563";
+ reg = <0x51>;
+ };
+};
+
+&iomuxc {
+ imx51-eukrea {
+ pinctrl_tsc2007_1: tsc2007grp-1 {
+ fsl,pins = <
+ MX51_PAD_GPIO_NAND__GPIO_NAND 0x1f5
+ MX51_PAD_NANDF_D8__GPIO4_0 0x1f5
+ >;
+ };
+
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX51_PAD_DI_GP3__FEC_TX_ER 0x80000000
+ MX51_PAD_DI2_PIN4__FEC_CRS 0x80000000
+ MX51_PAD_DI2_PIN2__FEC_MDC 0x80000000
+ MX51_PAD_DI2_PIN3__FEC_MDIO 0x80000000
+ MX51_PAD_DI2_DISP_CLK__FEC_RDATA1 0x80000000
+ MX51_PAD_DI_GP4__FEC_RDATA2 0x80000000
+ MX51_PAD_DISP2_DAT0__FEC_RDATA3 0x80000000
+ MX51_PAD_DISP2_DAT1__FEC_RX_ER 0x80000000
+ MX51_PAD_DISP2_DAT6__FEC_TDATA1 0x80000000
+ MX51_PAD_DISP2_DAT7__FEC_TDATA2 0x80000000
+ MX51_PAD_DISP2_DAT8__FEC_TDATA3 0x80000000
+ MX51_PAD_DISP2_DAT9__FEC_TX_EN 0x80000000
+ MX51_PAD_DISP2_DAT10__FEC_COL 0x80000000
+ MX51_PAD_DISP2_DAT11__FEC_RX_CLK 0x80000000
+ MX51_PAD_DISP2_DAT12__FEC_RX_DV 0x80000000
+ MX51_PAD_DISP2_DAT13__FEC_TX_CLK 0x80000000
+ MX51_PAD_DISP2_DAT14__FEC_RDATA0 0x80000000
+ MX51_PAD_DISP2_DAT15__FEC_TDATA0 0x80000000
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX51_PAD_SD2_CMD__I2C1_SCL 0x400001ed
+ MX51_PAD_SD2_CLK__I2C1_SDA 0x400001ed
+ >;
+ };
+ };
+};
+
+&nfc {
+ nand-bus-width = <8>;
+ nand-ecc-mode = "hw";
+ nand-on-flash-bbt;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx51-eukrea-mbimxsd51-baseboard.dts b/arch/arm/boot/dts/imx51-eukrea-mbimxsd51-baseboard.dts
new file mode 100644
index 000000000000..5cec4f322096
--- /dev/null
+++ b/arch/arm/boot/dts/imx51-eukrea-mbimxsd51-baseboard.dts
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2013 Eukréa Electromatique <denis@eukrea.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+/dts-v1/;
+#include "imx51-eukrea-cpuimx51.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ model = "Eukrea CPUIMX51";
+ compatible = "eukrea,mbimxsd51","eukrea,cpuimx51", "fsl,imx51";
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpiokeys_1>;
+
+ button-1 {
+ label = "BP1";
+ gpios = <&gpio3 31 GPIO_ACTIVE_LOW>;
+ linux,code = <256>;
+ gpio-key,wakeup;
+ linux,input-type = <1>;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpioled>;
+
+ led1 {
+ label = "led1";
+ gpios = <&gpio3 30 GPIO_ACTIVE_LOW>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ sound {
+ compatible = "eukrea,asoc-tlv320";
+ eukrea,model = "imx51-eukrea-tlv320aic23";
+ ssi-controller = <&ssi2>;
+ fsl,mux-int-port = <2>;
+ fsl,mux-ext-port = <3>;
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>;
+ status = "okay";
+};
+
+&esdhc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_esdhc1 &pinctrl_esdhc1_cd>;
+ fsl,cd-controller;
+ status = "okay";
+};
+
+&i2c1 {
+ tlv320aic23: codec@1a {
+ compatible = "ti,tlv320aic23";
+ reg = <0x1a>;
+ };
+};
+
+&iomuxc {
+ imx51-eukrea {
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX51_PAD_AUD3_BB_TXD__AUD3_TXD 0x80000000
+ MX51_PAD_AUD3_BB_RXD__AUD3_RXD 0x80000000
+ MX51_PAD_AUD3_BB_CK__AUD3_TXC 0x80000000
+ MX51_PAD_AUD3_BB_FS__AUD3_TXFS 0x80000000
+ >;
+ };
+
+ pinctrl_esdhc1: esdhc1grp {
+ fsl,pins = <
+ MX51_PAD_SD1_CMD__SD1_CMD 0x400020d5
+ MX51_PAD_SD1_CLK__SD1_CLK 0x20d5
+ MX51_PAD_SD1_DATA0__SD1_DATA0 0x20d5
+ MX51_PAD_SD1_DATA1__SD1_DATA1 0x20d5
+ MX51_PAD_SD1_DATA2__SD1_DATA2 0x20d5
+ MX51_PAD_SD1_DATA3__SD1_DATA3 0x20d5
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX51_PAD_UART1_RXD__UART1_RXD 0x1c5
+ MX51_PAD_UART1_TXD__UART1_TXD 0x1c5
+ >;
+ };
+
+ pinctrl_uart3: uart3grp {
+ fsl,pins = <
+ MX51_PAD_UART3_RXD__UART3_RXD 0x1c5
+ MX51_PAD_UART3_TXD__UART3_TXD 0x1c5
+ >;
+ };
+
+ pinctrl_uart3_rtscts: uart3rtsctsgrp {
+ fsl,pins = <
+ MX51_PAD_KEY_COL4__UART3_RTS 0x1c5
+ MX51_PAD_KEY_COL5__UART3_CTS 0x1c5
+ >;
+ };
+
+ pinctrl_backlight_1: backlightgrp-1 {
+ fsl,pins = <
+ MX51_PAD_DI1_D1_CS__GPIO3_4 0x1f5
+ >;
+ };
+
+ pinctrl_esdhc1_cd: esdhc1_cd {
+ fsl,pins = <
+ MX51_PAD_GPIO1_0__SD1_CD 0x20d5
+ >;
+ };
+
+ pinctrl_gpiokeys_1: gpiokeysgrp-1 {
+ fsl,pins = <
+ MX51_PAD_NANDF_D9__GPIO3_31 0x1f5
+ >;
+ };
+
+ pinctrl_gpioled: gpioledgrp-1 {
+ fsl,pins = <
+ MX51_PAD_NANDF_D10__GPIO3_30 0x80000000
+ >;
+ };
+
+ pinctrl_reg_lcd_3v3: reg_lcd_3v3 {
+ fsl,pins = <
+ MX51_PAD_CSI1_D9__GPIO3_13 0x1f5
+ >;
+ };
+ };
+};
+
+&ssi2 {
+ codec-handle = <&tlv320aic23>;
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ fsl,uart-has-rtscts;
+ status = "okay";
+};
+
+&uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart3 &pinctrl_uart3_rtscts>;
+ fsl,uart-has-rtscts;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi
index 28c96aada80b..5f8216d08f6b 100644
--- a/arch/arm/boot/dts/imx51.dtsi
+++ b/arch/arm/boot/dts/imx51.dtsi
@@ -12,6 +12,10 @@
#include "skeleton.dtsi"
#include "imx51-pinfunc.h"
+#include <dt-bindings/clock/imx5-clock.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/interrupt-controller/irq.h>
/ {
aliases {
@@ -21,6 +25,10 @@
gpio3 = &gpio4;
i2c0 = &i2c1;
i2c1 = &i2c2;
+ mmc0 = &esdhc1;
+ mmc1 = &esdhc2;
+ mmc2 = &esdhc3;
+ mmc3 = &esdhc4;
serial0 = &uart1;
serial1 = &uart2;
serial2 = &uart3;
@@ -64,18 +72,32 @@
cpus {
#address-cells = <1>;
#size-cells = <0>;
- cpu@0 {
+ cpu: cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a8";
reg = <0>;
- clock-latency = <61036>; /* two CLK32 periods */
- clocks = <&clks 24>;
+ clock-latency = <62500>;
+ clocks = <&clks IMX5_CLK_CPU_PODF>;
clock-names = "cpu";
operating-points = <
- /* kHz uV (No regulator support) */
- 160000 0
- 800000 0
+ 166000 1000000
+ 600000 1050000
+ 800000 1100000
>;
+ voltage-tolerance = <5>;
+ };
+ };
+
+ usbphy {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "simple-bus";
+
+ usbphy0: usbphy@0 {
+ compatible = "usb-nop-xceiv";
+ reg = <0>;
+ clocks = <&clks IMX5_CLK_USB_PHY_GATE>;
+ clock-names = "main_clk";
};
};
@@ -102,7 +124,9 @@
compatible = "fsl,imx51-ipu";
reg = <0x40000000 0x20000000>;
interrupts = <11 10>;
- clocks = <&clks 59>, <&clks 110>, <&clks 61>;
+ clocks = <&clks IMX5_CLK_IPU_GATE>,
+ <&clks IMX5_CLK_IPU_DI0_GATE>,
+ <&clks IMX5_CLK_IPU_DI1_GATE>;
clock-names = "bus", "di0", "di1";
resets = <&src 2>;
@@ -139,7 +163,9 @@
compatible = "fsl,imx51-esdhc";
reg = <0x70004000 0x4000>;
interrupts = <1>;
- clocks = <&clks 44>, <&clks 0>, <&clks 71>;
+ clocks = <&clks IMX5_CLK_ESDHC1_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC1_PER_GATE>;
clock-names = "ipg", "ahb", "per";
status = "disabled";
};
@@ -148,7 +174,9 @@
compatible = "fsl,imx51-esdhc";
reg = <0x70008000 0x4000>;
interrupts = <2>;
- clocks = <&clks 45>, <&clks 0>, <&clks 72>;
+ clocks = <&clks IMX5_CLK_ESDHC2_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC2_PER_GATE>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
status = "disabled";
@@ -158,7 +186,8 @@
compatible = "fsl,imx51-uart", "fsl,imx21-uart";
reg = <0x7000c000 0x4000>;
interrupts = <33>;
- clocks = <&clks 32>, <&clks 33>;
+ clocks = <&clks IMX5_CLK_UART3_IPG_GATE>,
+ <&clks IMX5_CLK_UART3_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -169,7 +198,8 @@
compatible = "fsl,imx51-ecspi";
reg = <0x70010000 0x4000>;
interrupts = <36>;
- clocks = <&clks 51>, <&clks 52>;
+ clocks = <&clks IMX5_CLK_ECSPI1_IPG_GATE>,
+ <&clks IMX5_CLK_ECSPI1_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -178,7 +208,7 @@
compatible = "fsl,imx51-ssi", "fsl,imx21-ssi";
reg = <0x70014000 0x4000>;
interrupts = <30>;
- clocks = <&clks 49>;
+ clocks = <&clks IMX5_CLK_SSI2_IPG_GATE>;
dmas = <&sdma 24 1 0>,
<&sdma 25 1 0>;
dma-names = "rx", "tx";
@@ -191,7 +221,9 @@
compatible = "fsl,imx51-esdhc";
reg = <0x70020000 0x4000>;
interrupts = <3>;
- clocks = <&clks 46>, <&clks 0>, <&clks 73>;
+ clocks = <&clks IMX5_CLK_ESDHC3_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC3_PER_GATE>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
status = "disabled";
@@ -201,25 +233,20 @@
compatible = "fsl,imx51-esdhc";
reg = <0x70024000 0x4000>;
interrupts = <4>;
- clocks = <&clks 47>, <&clks 0>, <&clks 74>;
+ clocks = <&clks IMX5_CLK_ESDHC4_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC4_PER_GATE>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
status = "disabled";
};
};
- usbphy0: usbphy@0 {
- compatible = "usb-nop-xceiv";
- clocks = <&clks 75>;
- clock-names = "main_clk";
- status = "okay";
- };
-
usbotg: usb@73f80000 {
compatible = "fsl,imx51-usb", "fsl,imx27-usb";
reg = <0x73f80000 0x0200>;
interrupts = <18>;
- clocks = <&clks 108>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
fsl,usbmisc = <&usbmisc 0>;
fsl,usbphy = <&usbphy0>;
status = "disabled";
@@ -229,7 +256,7 @@
compatible = "fsl,imx51-usb", "fsl,imx27-usb";
reg = <0x73f80200 0x0200>;
interrupts = <14>;
- clocks = <&clks 108>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
fsl,usbmisc = <&usbmisc 1>;
status = "disabled";
};
@@ -238,7 +265,7 @@
compatible = "fsl,imx51-usb", "fsl,imx27-usb";
reg = <0x73f80400 0x0200>;
interrupts = <16>;
- clocks = <&clks 108>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
fsl,usbmisc = <&usbmisc 2>;
status = "disabled";
};
@@ -247,7 +274,7 @@
compatible = "fsl,imx51-usb", "fsl,imx27-usb";
reg = <0x73f80600 0x0200>;
interrupts = <17>;
- clocks = <&clks 108>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
fsl,usbmisc = <&usbmisc 3>;
status = "disabled";
};
@@ -256,7 +283,7 @@
#index-cells = <1>;
compatible = "fsl,imx51-usbmisc";
reg = <0x73f80800 0x200>;
- clocks = <&clks 108>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
};
gpio1: gpio@73f84000 {
@@ -303,7 +330,7 @@
compatible = "fsl,imx51-kpp", "fsl,imx21-kpp";
reg = <0x73f94000 0x4000>;
interrupts = <60>;
- clocks = <&clks 0>;
+ clocks = <&clks IMX5_CLK_DUMMY>;
status = "disabled";
};
@@ -311,14 +338,14 @@
compatible = "fsl,imx51-wdt", "fsl,imx21-wdt";
reg = <0x73f98000 0x4000>;
interrupts = <58>;
- clocks = <&clks 0>;
+ clocks = <&clks IMX5_CLK_DUMMY>;
};
wdog2: wdog@73f9c000 {
compatible = "fsl,imx51-wdt", "fsl,imx21-wdt";
reg = <0x73f9c000 0x4000>;
interrupts = <59>;
- clocks = <&clks 0>;
+ clocks = <&clks IMX5_CLK_DUMMY>;
status = "disabled";
};
@@ -326,7 +353,8 @@
compatible = "fsl,imx51-gpt", "fsl,imx31-gpt";
reg = <0x73fa0000 0x4000>;
interrupts = <39>;
- clocks = <&clks 36>, <&clks 41>;
+ clocks = <&clks IMX5_CLK_GPT_IPG_GATE>,
+ <&clks IMX5_CLK_GPT_HF_GATE>;
clock-names = "ipg", "per";
};
@@ -339,7 +367,8 @@
#pwm-cells = <2>;
compatible = "fsl,imx51-pwm", "fsl,imx27-pwm";
reg = <0x73fb4000 0x4000>;
- clocks = <&clks 37>, <&clks 38>;
+ clocks = <&clks IMX5_CLK_PWM1_IPG_GATE>,
+ <&clks IMX5_CLK_PWM1_HF_GATE>;
clock-names = "ipg", "per";
interrupts = <61>;
};
@@ -348,7 +377,8 @@
#pwm-cells = <2>;
compatible = "fsl,imx51-pwm", "fsl,imx27-pwm";
reg = <0x73fb8000 0x4000>;
- clocks = <&clks 39>, <&clks 40>;
+ clocks = <&clks IMX5_CLK_PWM2_IPG_GATE>,
+ <&clks IMX5_CLK_PWM2_HF_GATE>;
clock-names = "ipg", "per";
interrupts = <94>;
};
@@ -357,7 +387,8 @@
compatible = "fsl,imx51-uart", "fsl,imx21-uart";
reg = <0x73fbc000 0x4000>;
interrupts = <31>;
- clocks = <&clks 28>, <&clks 29>;
+ clocks = <&clks IMX5_CLK_UART1_IPG_GATE>,
+ <&clks IMX5_CLK_UART1_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -366,7 +397,8 @@
compatible = "fsl,imx51-uart", "fsl,imx21-uart";
reg = <0x73fc0000 0x4000>;
interrupts = <32>;
- clocks = <&clks 30>, <&clks 31>;
+ clocks = <&clks IMX5_CLK_UART2_IPG_GATE>,
+ <&clks IMX5_CLK_UART2_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -396,14 +428,14 @@
compatible = "fsl,imx51-iim", "fsl,imx27-iim";
reg = <0x83f98000 0x4000>;
interrupts = <69>;
- clocks = <&clks 107>;
+ clocks = <&clks IMX5_CLK_IIM_GATE>;
};
owire: owire@83fa4000 {
compatible = "fsl,imx51-owire", "fsl,imx21-owire";
reg = <0x83fa4000 0x4000>;
interrupts = <88>;
- clocks = <&clks 159>;
+ clocks = <&clks IMX5_CLK_OWIRE_GATE>;
status = "disabled";
};
@@ -413,7 +445,8 @@
compatible = "fsl,imx51-ecspi";
reg = <0x83fac000 0x4000>;
interrupts = <37>;
- clocks = <&clks 53>, <&clks 54>;
+ clocks = <&clks IMX5_CLK_ECSPI2_IPG_GATE>,
+ <&clks IMX5_CLK_ECSPI2_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -422,7 +455,8 @@
compatible = "fsl,imx51-sdma", "fsl,imx35-sdma";
reg = <0x83fb0000 0x4000>;
interrupts = <6>;
- clocks = <&clks 56>, <&clks 56>;
+ clocks = <&clks IMX5_CLK_SDMA_GATE>,
+ <&clks IMX5_CLK_SDMA_GATE>;
clock-names = "ipg", "ahb";
#dma-cells = <3>;
fsl,sdma-ram-script-name = "imx/sdma/sdma-imx51.bin";
@@ -434,7 +468,8 @@
compatible = "fsl,imx51-cspi", "fsl,imx35-cspi";
reg = <0x83fc0000 0x4000>;
interrupts = <38>;
- clocks = <&clks 55>, <&clks 55>;
+ clocks = <&clks IMX5_CLK_CSPI_IPG_GATE>,
+ <&clks IMX5_CLK_CSPI_IPG_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -445,7 +480,7 @@
compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";
reg = <0x83fc4000 0x4000>;
interrupts = <63>;
- clocks = <&clks 35>;
+ clocks = <&clks IMX5_CLK_I2C2_GATE>;
status = "disabled";
};
@@ -455,7 +490,7 @@
compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";
reg = <0x83fc8000 0x4000>;
interrupts = <62>;
- clocks = <&clks 34>;
+ clocks = <&clks IMX5_CLK_I2C1_GATE>;
status = "disabled";
};
@@ -463,7 +498,7 @@
compatible = "fsl,imx51-ssi", "fsl,imx21-ssi";
reg = <0x83fcc000 0x4000>;
interrupts = <29>;
- clocks = <&clks 48>;
+ clocks = <&clks IMX5_CLK_SSI1_IPG_GATE>;
dmas = <&sdma 28 0 0>,
<&sdma 29 0 0>;
dma-names = "rx", "tx";
@@ -475,6 +510,8 @@
audmux: audmux@83fd0000 {
compatible = "fsl,imx51-audmux", "fsl,imx31-audmux";
reg = <0x83fd0000 0x4000>;
+ clocks = <&clks IMX5_CLK_DUMMY>;
+ clock-names = "audmux";
status = "disabled";
};
@@ -483,7 +520,7 @@
#size-cells = <1>;
compatible = "fsl,imx51-weim";
reg = <0x83fda000 0x1000>;
- clocks = <&clks 57>;
+ clocks = <&clks IMX5_CLK_EMI_SLOW_GATE>;
ranges = <
0 0 0xb0000000 0x08000000
1 0 0xb8000000 0x08000000
@@ -499,7 +536,7 @@
compatible = "fsl,imx51-nand";
reg = <0x83fdb000 0x1000 0xcfff0000 0x10000>;
interrupts = <8>;
- clocks = <&clks 60>;
+ clocks = <&clks IMX5_CLK_NFC_GATE>;
status = "disabled";
};
@@ -507,7 +544,7 @@
compatible = "fsl,imx51-pata", "fsl,imx27-pata";
reg = <0x83fe0000 0x4000>;
interrupts = <70>;
- clocks = <&clks 172>;
+ clocks = <&clks IMX5_CLK_PATA_GATE>;
status = "disabled";
};
@@ -515,7 +552,7 @@
compatible = "fsl,imx51-ssi", "fsl,imx21-ssi";
reg = <0x83fe8000 0x4000>;
interrupts = <96>;
- clocks = <&clks 50>;
+ clocks = <&clks IMX5_CLK_SSI3_IPG_GATE>;
dmas = <&sdma 46 0 0>,
<&sdma 47 0 0>;
dma-names = "rx", "tx";
@@ -528,336 +565,12 @@
compatible = "fsl,imx51-fec", "fsl,imx27-fec";
reg = <0x83fec000 0x4000>;
interrupts = <87>;
- clocks = <&clks 42>, <&clks 42>, <&clks 42>;
+ clocks = <&clks IMX5_CLK_FEC_GATE>,
+ <&clks IMX5_CLK_FEC_GATE>,
+ <&clks IMX5_CLK_FEC_GATE>;
clock-names = "ipg", "ahb", "ptp";
status = "disabled";
};
};
};
};
-
-&iomuxc {
- audmux {
- pinctrl_audmux_1: audmuxgrp-1 {
- fsl,pins = <
- MX51_PAD_AUD3_BB_TXD__AUD3_TXD 0x80000000
- MX51_PAD_AUD3_BB_RXD__AUD3_RXD 0x80000000
- MX51_PAD_AUD3_BB_CK__AUD3_TXC 0x80000000
- MX51_PAD_AUD3_BB_FS__AUD3_TXFS 0x80000000
- >;
- };
- };
-
- fec {
- pinctrl_fec_1: fecgrp-1 {
- fsl,pins = <
- MX51_PAD_EIM_EB2__FEC_MDIO 0x80000000
- MX51_PAD_EIM_EB3__FEC_RDATA1 0x80000000
- MX51_PAD_EIM_CS2__FEC_RDATA2 0x80000000
- MX51_PAD_EIM_CS3__FEC_RDATA3 0x80000000
- MX51_PAD_EIM_CS4__FEC_RX_ER 0x80000000
- MX51_PAD_EIM_CS5__FEC_CRS 0x80000000
- MX51_PAD_NANDF_RB2__FEC_COL 0x80000000
- MX51_PAD_NANDF_RB3__FEC_RX_CLK 0x80000000
- MX51_PAD_NANDF_D9__FEC_RDATA0 0x80000000
- MX51_PAD_NANDF_D8__FEC_TDATA0 0x80000000
- MX51_PAD_NANDF_CS2__FEC_TX_ER 0x80000000
- MX51_PAD_NANDF_CS3__FEC_MDC 0x80000000
- MX51_PAD_NANDF_CS4__FEC_TDATA1 0x80000000
- MX51_PAD_NANDF_CS5__FEC_TDATA2 0x80000000
- MX51_PAD_NANDF_CS6__FEC_TDATA3 0x80000000
- MX51_PAD_NANDF_CS7__FEC_TX_EN 0x80000000
- MX51_PAD_NANDF_RDY_INT__FEC_TX_CLK 0x80000000
- >;
- };
-
- pinctrl_fec_2: fecgrp-2 {
- fsl,pins = <
- MX51_PAD_DI_GP3__FEC_TX_ER 0x80000000
- MX51_PAD_DI2_PIN4__FEC_CRS 0x80000000
- MX51_PAD_DI2_PIN2__FEC_MDC 0x80000000
- MX51_PAD_DI2_PIN3__FEC_MDIO 0x80000000
- MX51_PAD_DI2_DISP_CLK__FEC_RDATA1 0x80000000
- MX51_PAD_DI_GP4__FEC_RDATA2 0x80000000
- MX51_PAD_DISP2_DAT0__FEC_RDATA3 0x80000000
- MX51_PAD_DISP2_DAT1__FEC_RX_ER 0x80000000
- MX51_PAD_DISP2_DAT6__FEC_TDATA1 0x80000000
- MX51_PAD_DISP2_DAT7__FEC_TDATA2 0x80000000
- MX51_PAD_DISP2_DAT8__FEC_TDATA3 0x80000000
- MX51_PAD_DISP2_DAT9__FEC_TX_EN 0x80000000
- MX51_PAD_DISP2_DAT10__FEC_COL 0x80000000
- MX51_PAD_DISP2_DAT11__FEC_RX_CLK 0x80000000
- MX51_PAD_DISP2_DAT12__FEC_RX_DV 0x80000000
- MX51_PAD_DISP2_DAT13__FEC_TX_CLK 0x80000000
- MX51_PAD_DISP2_DAT14__FEC_RDATA0 0x80000000
- MX51_PAD_DISP2_DAT15__FEC_TDATA0 0x80000000
- >;
- };
- };
-
- ecspi1 {
- pinctrl_ecspi1_1: ecspi1grp-1 {
- fsl,pins = <
- MX51_PAD_CSPI1_MISO__ECSPI1_MISO 0x185
- MX51_PAD_CSPI1_MOSI__ECSPI1_MOSI 0x185
- MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK 0x185
- >;
- };
- };
-
- ecspi2 {
- pinctrl_ecspi2_1: ecspi2grp-1 {
- fsl,pins = <
- MX51_PAD_NANDF_RB3__ECSPI2_MISO 0x185
- MX51_PAD_NANDF_D15__ECSPI2_MOSI 0x185
- MX51_PAD_NANDF_RB2__ECSPI2_SCLK 0x185
- >;
- };
- };
-
- esdhc1 {
- pinctrl_esdhc1_1: esdhc1grp-1 {
- fsl,pins = <
- MX51_PAD_SD1_CMD__SD1_CMD 0x400020d5
- MX51_PAD_SD1_CLK__SD1_CLK 0x20d5
- MX51_PAD_SD1_DATA0__SD1_DATA0 0x20d5
- MX51_PAD_SD1_DATA1__SD1_DATA1 0x20d5
- MX51_PAD_SD1_DATA2__SD1_DATA2 0x20d5
- MX51_PAD_SD1_DATA3__SD1_DATA3 0x20d5
- >;
- };
- };
-
- esdhc2 {
- pinctrl_esdhc2_1: esdhc2grp-1 {
- fsl,pins = <
- MX51_PAD_SD2_CMD__SD2_CMD 0x400020d5
- MX51_PAD_SD2_CLK__SD2_CLK 0x20d5
- MX51_PAD_SD2_DATA0__SD2_DATA0 0x20d5
- MX51_PAD_SD2_DATA1__SD2_DATA1 0x20d5
- MX51_PAD_SD2_DATA2__SD2_DATA2 0x20d5
- MX51_PAD_SD2_DATA3__SD2_DATA3 0x20d5
- >;
- };
- };
-
- i2c2 {
- pinctrl_i2c2_1: i2c2grp-1 {
- fsl,pins = <
- MX51_PAD_KEY_COL4__I2C2_SCL 0x400001ed
- MX51_PAD_KEY_COL5__I2C2_SDA 0x400001ed
- >;
- };
-
- pinctrl_i2c2_2: i2c2grp-2 {
- fsl,pins = <
- MX51_PAD_EIM_D27__I2C2_SCL 0x400001ed
- MX51_PAD_EIM_D24__I2C2_SDA 0x400001ed
- >;
- };
-
- pinctrl_i2c2_3: i2c2grp-3 {
- fsl,pins = <
- MX51_PAD_GPIO1_2__I2C2_SCL 0x400001ed
- MX51_PAD_GPIO1_3__I2C2_SDA 0x400001ed
- >;
- };
- };
-
- ipu_disp1 {
- pinctrl_ipu_disp1_1: ipudisp1grp-1 {
- fsl,pins = <
- MX51_PAD_DISP1_DAT0__DISP1_DAT0 0x5
- MX51_PAD_DISP1_DAT1__DISP1_DAT1 0x5
- MX51_PAD_DISP1_DAT2__DISP1_DAT2 0x5
- MX51_PAD_DISP1_DAT3__DISP1_DAT3 0x5
- MX51_PAD_DISP1_DAT4__DISP1_DAT4 0x5
- MX51_PAD_DISP1_DAT5__DISP1_DAT5 0x5
- MX51_PAD_DISP1_DAT6__DISP1_DAT6 0x5
- MX51_PAD_DISP1_DAT7__DISP1_DAT7 0x5
- MX51_PAD_DISP1_DAT8__DISP1_DAT8 0x5
- MX51_PAD_DISP1_DAT9__DISP1_DAT9 0x5
- MX51_PAD_DISP1_DAT10__DISP1_DAT10 0x5
- MX51_PAD_DISP1_DAT11__DISP1_DAT11 0x5
- MX51_PAD_DISP1_DAT12__DISP1_DAT12 0x5
- MX51_PAD_DISP1_DAT13__DISP1_DAT13 0x5
- MX51_PAD_DISP1_DAT14__DISP1_DAT14 0x5
- MX51_PAD_DISP1_DAT15__DISP1_DAT15 0x5
- MX51_PAD_DISP1_DAT16__DISP1_DAT16 0x5
- MX51_PAD_DISP1_DAT17__DISP1_DAT17 0x5
- MX51_PAD_DISP1_DAT18__DISP1_DAT18 0x5
- MX51_PAD_DISP1_DAT19__DISP1_DAT19 0x5
- MX51_PAD_DISP1_DAT20__DISP1_DAT20 0x5
- MX51_PAD_DISP1_DAT21__DISP1_DAT21 0x5
- MX51_PAD_DISP1_DAT22__DISP1_DAT22 0x5
- MX51_PAD_DISP1_DAT23__DISP1_DAT23 0x5
- MX51_PAD_DI1_PIN2__DI1_PIN2 0x5 /* hsync */
- MX51_PAD_DI1_PIN3__DI1_PIN3 0x5 /* vsync */
- >;
- };
- };
-
- ipu_disp2 {
- pinctrl_ipu_disp2_1: ipudisp2grp-1 {
- fsl,pins = <
- MX51_PAD_DISP2_DAT0__DISP2_DAT0 0x5
- MX51_PAD_DISP2_DAT1__DISP2_DAT1 0x5
- MX51_PAD_DISP2_DAT2__DISP2_DAT2 0x5
- MX51_PAD_DISP2_DAT3__DISP2_DAT3 0x5
- MX51_PAD_DISP2_DAT4__DISP2_DAT4 0x5
- MX51_PAD_DISP2_DAT5__DISP2_DAT5 0x5
- MX51_PAD_DISP2_DAT6__DISP2_DAT6 0x5
- MX51_PAD_DISP2_DAT7__DISP2_DAT7 0x5
- MX51_PAD_DISP2_DAT8__DISP2_DAT8 0x5
- MX51_PAD_DISP2_DAT9__DISP2_DAT9 0x5
- MX51_PAD_DISP2_DAT10__DISP2_DAT10 0x5
- MX51_PAD_DISP2_DAT11__DISP2_DAT11 0x5
- MX51_PAD_DISP2_DAT12__DISP2_DAT12 0x5
- MX51_PAD_DISP2_DAT13__DISP2_DAT13 0x5
- MX51_PAD_DISP2_DAT14__DISP2_DAT14 0x5
- MX51_PAD_DISP2_DAT15__DISP2_DAT15 0x5
- MX51_PAD_DI2_PIN2__DI2_PIN2 0x5 /* hsync */
- MX51_PAD_DI2_PIN3__DI2_PIN3 0x5 /* vsync */
- MX51_PAD_DI2_DISP_CLK__DI2_DISP_CLK 0x5 /* CLK */
- MX51_PAD_DI_GP4__DI2_PIN15 0x5 /* DE */
- >;
- };
- };
-
- kpp {
- pinctrl_kpp_1: kppgrp-1 {
- fsl,pins = <
- MX51_PAD_KEY_ROW0__KEY_ROW0 0xe0
- MX51_PAD_KEY_ROW1__KEY_ROW1 0xe0
- MX51_PAD_KEY_ROW2__KEY_ROW2 0xe0
- MX51_PAD_KEY_ROW3__KEY_ROW3 0xe0
- MX51_PAD_KEY_COL0__KEY_COL0 0xe8
- MX51_PAD_KEY_COL1__KEY_COL1 0xe8
- MX51_PAD_KEY_COL2__KEY_COL2 0xe8
- MX51_PAD_KEY_COL3__KEY_COL3 0xe8
- >;
- };
- };
-
- pata {
- pinctrl_pata_1: patagrp-1 {
- fsl,pins = <
- MX51_PAD_NANDF_WE_B__PATA_DIOW 0x2004
- MX51_PAD_NANDF_RE_B__PATA_DIOR 0x2004
- MX51_PAD_NANDF_ALE__PATA_BUFFER_EN 0x2004
- MX51_PAD_NANDF_CLE__PATA_RESET_B 0x2004
- MX51_PAD_NANDF_WP_B__PATA_DMACK 0x2004
- MX51_PAD_NANDF_RB0__PATA_DMARQ 0x2004
- MX51_PAD_NANDF_RB1__PATA_IORDY 0x2004
- MX51_PAD_GPIO_NAND__PATA_INTRQ 0x2004
- MX51_PAD_NANDF_CS2__PATA_CS_0 0x2004
- MX51_PAD_NANDF_CS3__PATA_CS_1 0x2004
- MX51_PAD_NANDF_CS4__PATA_DA_0 0x2004
- MX51_PAD_NANDF_CS5__PATA_DA_1 0x2004
- MX51_PAD_NANDF_CS6__PATA_DA_2 0x2004
- MX51_PAD_NANDF_D15__PATA_DATA15 0x2004
- MX51_PAD_NANDF_D14__PATA_DATA14 0x2004
- MX51_PAD_NANDF_D13__PATA_DATA13 0x2004
- MX51_PAD_NANDF_D12__PATA_DATA12 0x2004
- MX51_PAD_NANDF_D11__PATA_DATA11 0x2004
- MX51_PAD_NANDF_D10__PATA_DATA10 0x2004
- MX51_PAD_NANDF_D9__PATA_DATA9 0x2004
- MX51_PAD_NANDF_D8__PATA_DATA8 0x2004
- MX51_PAD_NANDF_D7__PATA_DATA7 0x2004
- MX51_PAD_NANDF_D6__PATA_DATA6 0x2004
- MX51_PAD_NANDF_D5__PATA_DATA5 0x2004
- MX51_PAD_NANDF_D4__PATA_DATA4 0x2004
- MX51_PAD_NANDF_D3__PATA_DATA3 0x2004
- MX51_PAD_NANDF_D2__PATA_DATA2 0x2004
- MX51_PAD_NANDF_D1__PATA_DATA1 0x2004
- MX51_PAD_NANDF_D0__PATA_DATA0 0x2004
- >;
- };
- };
-
- uart1 {
- pinctrl_uart1_1: uart1grp-1 {
- fsl,pins = <
- MX51_PAD_UART1_RXD__UART1_RXD 0x1c5
- MX51_PAD_UART1_TXD__UART1_TXD 0x1c5
- >;
- };
-
- pinctrl_uart1_rtscts_1: uart1rtscts-1 {
- fsl,pins = <
- MX51_PAD_UART1_RTS__UART1_RTS 0x1c5
- MX51_PAD_UART1_CTS__UART1_CTS 0x1c5
- >;
- };
- };
-
- uart2 {
- pinctrl_uart2_1: uart2grp-1 {
- fsl,pins = <
- MX51_PAD_UART2_RXD__UART2_RXD 0x1c5
- MX51_PAD_UART2_TXD__UART2_TXD 0x1c5
- >;
- };
- };
-
- uart3 {
- pinctrl_uart3_1: uart3grp-1 {
- fsl,pins = <
- MX51_PAD_EIM_D25__UART3_RXD 0x1c5
- MX51_PAD_EIM_D26__UART3_TXD 0x1c5
- >;
- };
-
- pinctrl_uart3_rtscts_1: uart3rtscts-1 {
- fsl,pins = <
- MX51_PAD_EIM_D27__UART3_RTS 0x1c5
- MX51_PAD_EIM_D24__UART3_CTS 0x1c5
- >;
- };
-
- pinctrl_uart3_2: uart3grp-2 {
- fsl,pins = <
- MX51_PAD_UART3_RXD__UART3_RXD 0x1c5
- MX51_PAD_UART3_TXD__UART3_TXD 0x1c5
- >;
- };
- };
-
- usbh1 {
- pinctrl_usbh1_1: usbh1grp-1 {
- fsl,pins = <
- MX51_PAD_USBH1_DATA0__USBH1_DATA0 0x1e5
- MX51_PAD_USBH1_DATA1__USBH1_DATA1 0x1e5
- MX51_PAD_USBH1_DATA2__USBH1_DATA2 0x1e5
- MX51_PAD_USBH1_DATA3__USBH1_DATA3 0x1e5
- MX51_PAD_USBH1_DATA4__USBH1_DATA4 0x1e5
- MX51_PAD_USBH1_DATA5__USBH1_DATA5 0x1e5
- MX51_PAD_USBH1_DATA6__USBH1_DATA6 0x1e5
- MX51_PAD_USBH1_DATA7__USBH1_DATA7 0x1e5
- MX51_PAD_USBH1_CLK__USBH1_CLK 0x1e5
- MX51_PAD_USBH1_DIR__USBH1_DIR 0x1e5
- MX51_PAD_USBH1_NXT__USBH1_NXT 0x1e5
- MX51_PAD_USBH1_STP__USBH1_STP 0x1e5
- >;
- };
- };
-
- usbh2 {
- pinctrl_usbh2_1: usbh2grp-1 {
- fsl,pins = <
- MX51_PAD_EIM_D16__USBH2_DATA0 0x1e5
- MX51_PAD_EIM_D17__USBH2_DATA1 0x1e5
- MX51_PAD_EIM_D18__USBH2_DATA2 0x1e5
- MX51_PAD_EIM_D19__USBH2_DATA3 0x1e5
- MX51_PAD_EIM_D20__USBH2_DATA4 0x1e5
- MX51_PAD_EIM_D21__USBH2_DATA5 0x1e5
- MX51_PAD_EIM_D22__USBH2_DATA6 0x1e5
- MX51_PAD_EIM_D23__USBH2_DATA7 0x1e5
- MX51_PAD_EIM_A24__USBH2_CLK 0x1e5
- MX51_PAD_EIM_A25__USBH2_DIR 0x1e5
- MX51_PAD_EIM_A27__USBH2_NXT 0x1e5
- MX51_PAD_EIM_A26__USBH2_STP 0x1e5
- >;
- };
- };
-};
diff --git a/arch/arm/boot/dts/imx53-ard.dts b/arch/arm/boot/dts/imx53-ard.dts
index 174f86938c89..e9337ad52f59 100644
--- a/arch/arm/boot/dts/imx53-ard.dts
+++ b/arch/arm/boot/dts/imx53-ard.dts
@@ -49,9 +49,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_3p3v: 3p3v {
+ reg_3p3v: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "3P3V";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
@@ -99,7 +102,7 @@
&esdhc1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc1_2>;
+ pinctrl-0 = <&pinctrl_esdhc1>;
cd-gpios = <&gpio1 1 0>;
wp-gpios = <&gpio1 9 0>;
status = "okay";
@@ -109,7 +112,7 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- hog {
+ imx53-ard {
pinctrl_hog: hoggrp {
fsl,pins = <
MX53_PAD_GPIO_1__GPIO1_1 0x80000000
@@ -148,11 +151,33 @@
MX53_PAD_EIM_CS1__EMI_WEIM_CS_1 0x80000000
>;
};
+
+ pinctrl_esdhc1: esdhc1grp {
+ fsl,pins = <
+ MX53_PAD_SD1_DATA0__ESDHC1_DAT0 0x1d5
+ MX53_PAD_SD1_DATA1__ESDHC1_DAT1 0x1d5
+ MX53_PAD_SD1_DATA2__ESDHC1_DAT2 0x1d5
+ MX53_PAD_SD1_DATA3__ESDHC1_DAT3 0x1d5
+ MX53_PAD_PATA_DATA8__ESDHC1_DAT4 0x1d5
+ MX53_PAD_PATA_DATA9__ESDHC1_DAT5 0x1d5
+ MX53_PAD_PATA_DATA10__ESDHC1_DAT6 0x1d5
+ MX53_PAD_PATA_DATA11__ESDHC1_DAT7 0x1d5
+ MX53_PAD_SD1_CMD__ESDHC1_CMD 0x1d5
+ MX53_PAD_SD1_CLK__ESDHC1_CLK 0x1d5
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX53_PAD_PATA_DIOW__UART1_TXD_MUX 0x1e4
+ MX53_PAD_PATA_DMACK__UART1_RXD_MUX 0x1e4
+ >;
+ };
};
};
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_2>;
+ pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx53-evk.dts b/arch/arm/boot/dts/imx53-evk.dts
deleted file mode 100644
index 801fda728ed6..000000000000
--- a/arch/arm/boot/dts/imx53-evk.dts
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2011 Freescale Semiconductor, Inc.
- * Copyright 2011 Linaro Ltd.
- *
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
-
-/dts-v1/;
-#include "imx53.dtsi"
-
-/ {
- model = "Freescale i.MX53 Evaluation Kit";
- compatible = "fsl,imx53-evk", "fsl,imx53";
-
- memory {
- reg = <0x70000000 0x80000000>;
- };
-
- leds {
- compatible = "gpio-leds";
-
- green {
- label = "Heartbeat";
- gpios = <&gpio7 7 0>;
- linux,default-trigger = "heartbeat";
- };
- };
-};
-
-&esdhc1 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc1_1>;
- cd-gpios = <&gpio3 13 0>;
- wp-gpios = <&gpio3 14 0>;
- status = "okay";
-};
-
-&ecspi1 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi1_1>;
- fsl,spi-num-chipselects = <2>;
- cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>;
- status = "okay";
-
- flash: at45db321d@1 {
- #address-cells = <1>;
- #size-cells = <1>;
- compatible = "atmel,at45db321d", "atmel,at45", "atmel,dataflash";
- spi-max-frequency = <25000000>;
- reg = <1>;
-
- partition@0 {
- label = "U-Boot";
- reg = <0x0 0x40000>;
- read-only;
- };
-
- partition@40000 {
- label = "Kernel";
- reg = <0x40000 0x3c0000>;
- };
- };
-};
-
-&esdhc3 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc3_1>;
- cd-gpios = <&gpio3 11 0>;
- wp-gpios = <&gpio3 12 0>;
- status = "okay";
-};
-
-&iomuxc {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_hog>;
-
- hog {
- pinctrl_hog: hoggrp {
- fsl,pins = <
- MX53_PAD_EIM_EB2__GPIO2_30 0x80000000
- MX53_PAD_EIM_D19__GPIO3_19 0x80000000
- MX53_PAD_EIM_DA11__GPIO3_11 0x80000000
- MX53_PAD_EIM_DA12__GPIO3_12 0x80000000
- MX53_PAD_EIM_DA13__GPIO3_13 0x80000000
- MX53_PAD_EIM_DA14__GPIO3_14 0x80000000
- MX53_PAD_PATA_DA_0__GPIO7_6 0x80000000
- MX53_PAD_PATA_DA_1__GPIO7_7 0x80000000
- >;
- };
- };
-};
-
-&uart1 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_1>;
- status = "okay";
-};
-
-&i2c2 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c2_1>;
- status = "okay";
-
- pmic: mc13892@08 {
- compatible = "fsl,mc13892", "fsl,mc13xxx";
- reg = <0x08>;
- };
-
- codec: sgtl5000@0a {
- compatible = "fsl,sgtl5000";
- reg = <0x0a>;
- };
-};
-
-&fec {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec_1>;
- phy-mode = "rmii";
- phy-reset-gpios = <&gpio7 6 0>;
- status = "okay";
-};
diff --git a/arch/arm/boot/dts/imx53-m53evk.dts b/arch/arm/boot/dts/imx53-m53evk.dts
index 0298adc73bb7..f6d3ac3e5587 100644
--- a/arch/arm/boot/dts/imx53-m53evk.dts
+++ b/arch/arm/boot/dts/imx53-m53evk.dts
@@ -25,7 +25,7 @@
compatible = "fsl,imx-parallel-display";
interface-pix-fmt = "bgr666";
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ipu_disp2_1>;
+ pinctrl-0 = <&pinctrl_ipu_disp1>;
display-timings {
800x480p60 {
@@ -56,6 +56,7 @@
pwms = <&pwm1 0 3000>;
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <6>;
+ power-supply = <&reg_backlight>;
};
leds {
@@ -78,14 +79,36 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_3p2v: 3p2v {
+ reg_3p2v: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "3P2V";
regulator-min-microvolt = <3200000>;
regulator-max-microvolt = <3200000>;
regulator-always-on;
};
+
+
+ reg_backlight: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "lcd-supply";
+ regulator-min-microvolt = <3200000>;
+ regulator-max-microvolt = <3200000>;
+ regulator-always-on;
+ };
+
+ reg_usbh1_vbus: regulator@3 {
+ compatible = "regulator-fixed";
+ reg = <3>;
+ regulator-name = "vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio1 2 0>;
+ };
};
sound {
@@ -107,25 +130,25 @@
&audmux {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_audmux_2>;
+ pinctrl-0 = <&pinctrl_audmux>;
status = "okay";
};
&can1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_can1_3>;
+ pinctrl-0 = <&pinctrl_can1>;
status = "okay";
};
&can2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_can2_1>;
+ pinctrl-0 = <&pinctrl_can2>;
status = "okay";
};
&esdhc1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc1_1>;
+ pinctrl-0 = <&pinctrl_esdhc1>;
cd-gpios = <&gpio1 1 0>;
wp-gpios = <&gpio1 9 0>;
status = "okay";
@@ -133,14 +156,14 @@
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec_1>;
+ pinctrl-0 = <&pinctrl_fec>;
phy-mode = "rmii";
status = "okay";
};
&i2c1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c1_2>;
+ pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
sgtl5000: codec@0a {
@@ -148,13 +171,13 @@
reg = <0x0a>;
VDDA-supply = <&reg_3p2v>;
VDDIO-supply = <&reg_3p2v>;
- clocks = <&clks 150>;
+ clocks = <&clks IMX5_CLK_SSI_EXT1_GATE>;
};
};
&i2c2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c2_2>;
+ pinctrl-0 = <&pinctrl_i2c2>;
clock-frequency = <400000>;
status = "okay";
@@ -198,7 +221,7 @@
&i2c3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c3_1>;
+ pinctrl-0 = <&pinctrl_i2c3>;
status = "okay";
};
@@ -206,14 +229,14 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- hog {
+ imx53-m53evk {
pinctrl_hog: hoggrp {
fsl,pins = <
MX53_PAD_GPIO_0__CCM_SSI_EXT1_CLK 0x80000000
MX53_PAD_EIM_EB3__GPIO2_31 0x80000000
MX53_PAD_PATA_DA_0__GPIO7_6 0x80000000
- MX53_PAD_DISP0_DAT8__PWM1_PWMO 0x5
-
+ MX53_PAD_GPIO_2__GPIO1_2 0x80000000
+ MX53_PAD_GPIO_3__USBOH3_USBH1_OC 0x80000000
>;
};
@@ -223,6 +246,162 @@
MX53_PAD_PATA_DATA9__GPIO2_9 0x80000000
>;
};
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX53_PAD_SD2_DATA3__AUDMUX_AUD4_TXC 0x80000000
+ MX53_PAD_SD2_DATA2__AUDMUX_AUD4_TXD 0x80000000
+ MX53_PAD_SD2_DATA1__AUDMUX_AUD4_TXFS 0x80000000
+ MX53_PAD_SD2_DATA0__AUDMUX_AUD4_RXD 0x80000000
+ >;
+ };
+
+ pinctrl_can1: can1grp {
+ fsl,pins = <
+ MX53_PAD_GPIO_7__CAN1_TXCAN 0x80000000
+ MX53_PAD_GPIO_8__CAN1_RXCAN 0x80000000
+ >;
+ };
+
+ pinctrl_can2: can2grp {
+ fsl,pins = <
+ MX53_PAD_KEY_COL4__CAN2_TXCAN 0x80000000
+ MX53_PAD_KEY_ROW4__CAN2_RXCAN 0x80000000
+ >;
+ };
+
+ pinctrl_esdhc1: esdhc1grp {
+ fsl,pins = <
+ MX53_PAD_SD1_DATA0__ESDHC1_DAT0 0x1d5
+ MX53_PAD_SD1_DATA1__ESDHC1_DAT1 0x1d5
+ MX53_PAD_SD1_DATA2__ESDHC1_DAT2 0x1d5
+ MX53_PAD_SD1_DATA3__ESDHC1_DAT3 0x1d5
+ MX53_PAD_SD1_CMD__ESDHC1_CMD 0x1d5
+ MX53_PAD_SD1_CLK__ESDHC1_CLK 0x1d5
+ >;
+ };
+
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX53_PAD_FEC_MDC__FEC_MDC 0x80000000
+ MX53_PAD_FEC_MDIO__FEC_MDIO 0x80000000
+ MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x80000000
+ MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x80000000
+ MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x80000000
+ MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x80000000
+ MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x80000000
+ MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000
+ MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x80000000
+ MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x80000000
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX53_PAD_EIM_D21__I2C1_SCL 0xc0000000
+ MX53_PAD_EIM_D28__I2C1_SDA 0xc0000000
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX53_PAD_EIM_D16__I2C2_SDA 0xc0000000
+ MX53_PAD_EIM_EB2__I2C2_SCL 0xc0000000
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX53_PAD_GPIO_6__I2C3_SDA 0xc0000000
+ MX53_PAD_GPIO_5__I2C3_SCL 0xc0000000
+ >;
+ };
+
+ pinctrl_ipu_disp1: ipudisp1grp {
+ fsl,pins = <
+ MX53_PAD_EIM_DA9__IPU_DISP1_DAT_0 0x5
+ MX53_PAD_EIM_DA8__IPU_DISP1_DAT_1 0x5
+ MX53_PAD_EIM_DA7__IPU_DISP1_DAT_2 0x5
+ MX53_PAD_EIM_DA6__IPU_DISP1_DAT_3 0x5
+ MX53_PAD_EIM_DA5__IPU_DISP1_DAT_4 0x5
+ MX53_PAD_EIM_DA4__IPU_DISP1_DAT_5 0x5
+ MX53_PAD_EIM_DA3__IPU_DISP1_DAT_6 0x5
+ MX53_PAD_EIM_DA2__IPU_DISP1_DAT_7 0x5
+ MX53_PAD_EIM_DA1__IPU_DISP1_DAT_8 0x5
+ MX53_PAD_EIM_DA0__IPU_DISP1_DAT_9 0x5
+ MX53_PAD_EIM_EB1__IPU_DISP1_DAT_10 0x5
+ MX53_PAD_EIM_EB0__IPU_DISP1_DAT_11 0x5
+ MX53_PAD_EIM_A17__IPU_DISP1_DAT_12 0x5
+ MX53_PAD_EIM_A18__IPU_DISP1_DAT_13 0x5
+ MX53_PAD_EIM_A19__IPU_DISP1_DAT_14 0x5
+ MX53_PAD_EIM_A20__IPU_DISP1_DAT_15 0x5
+ MX53_PAD_EIM_A21__IPU_DISP1_DAT_16 0x5
+ MX53_PAD_EIM_A22__IPU_DISP1_DAT_17 0x5
+ MX53_PAD_EIM_A23__IPU_DISP1_DAT_18 0x5
+ MX53_PAD_EIM_A24__IPU_DISP1_DAT_19 0x5
+ MX53_PAD_EIM_D31__IPU_DISP1_DAT_20 0x5
+ MX53_PAD_EIM_D30__IPU_DISP1_DAT_21 0x5
+ MX53_PAD_EIM_D26__IPU_DISP1_DAT_22 0x5
+ MX53_PAD_EIM_D27__IPU_DISP1_DAT_23 0x5
+ MX53_PAD_EIM_A16__IPU_DI1_DISP_CLK 0x5
+ MX53_PAD_EIM_DA13__IPU_DI1_D0_CS 0x5
+ MX53_PAD_EIM_DA14__IPU_DI1_D1_CS 0x5
+ MX53_PAD_EIM_DA15__IPU_DI1_PIN1 0x5
+ MX53_PAD_EIM_DA11__IPU_DI1_PIN2 0x5
+ MX53_PAD_EIM_DA12__IPU_DI1_PIN3 0x5
+ MX53_PAD_EIM_A25__IPU_DI1_PIN12 0x5
+ MX53_PAD_EIM_DA10__IPU_DI1_PIN15 0x5
+ >;
+ };
+
+ pinctrl_nand: nandgrp {
+ fsl,pins = <
+ MX53_PAD_NANDF_WE_B__EMI_NANDF_WE_B 0x4
+ MX53_PAD_NANDF_RE_B__EMI_NANDF_RE_B 0x4
+ MX53_PAD_NANDF_CLE__EMI_NANDF_CLE 0x4
+ MX53_PAD_NANDF_ALE__EMI_NANDF_ALE 0x4
+ MX53_PAD_NANDF_WP_B__EMI_NANDF_WP_B 0xe0
+ MX53_PAD_NANDF_RB0__EMI_NANDF_RB_0 0xe0
+ MX53_PAD_NANDF_CS0__EMI_NANDF_CS_0 0x4
+ MX53_PAD_PATA_DATA0__EMI_NANDF_D_0 0xa4
+ MX53_PAD_PATA_DATA1__EMI_NANDF_D_1 0xa4
+ MX53_PAD_PATA_DATA2__EMI_NANDF_D_2 0xa4
+ MX53_PAD_PATA_DATA3__EMI_NANDF_D_3 0xa4
+ MX53_PAD_PATA_DATA4__EMI_NANDF_D_4 0xa4
+ MX53_PAD_PATA_DATA5__EMI_NANDF_D_5 0xa4
+ MX53_PAD_PATA_DATA6__EMI_NANDF_D_6 0xa4
+ MX53_PAD_PATA_DATA7__EMI_NANDF_D_7 0xa4
+ >;
+ };
+
+ pinctrl_pwm1: pwm1grp {
+ fsl,pins = <
+ MX53_PAD_DISP0_DAT8__PWM1_PWMO 0x5
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX53_PAD_PATA_DIOW__UART1_TXD_MUX 0x1e4
+ MX53_PAD_PATA_DMACK__UART1_RXD_MUX 0x1e4
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX53_PAD_PATA_BUFFER_EN__UART2_RXD_MUX 0x1e4
+ MX53_PAD_PATA_DMARQ__UART2_TXD_MUX 0x1e4
+ >;
+ };
+
+ pinctrl_uart3: uart3grp {
+ fsl,pins = <
+ MX53_PAD_PATA_CS_0__UART3_TXD_MUX 0x1e4
+ MX53_PAD_PATA_CS_1__UART3_RXD_MUX 0x1e4
+ MX53_PAD_PATA_DA_1__UART3_CTS 0x1e4
+ MX53_PAD_PATA_DA_2__UART3_RTS 0x1e4
+ >;
+ };
};
};
@@ -232,7 +411,7 @@
&nfc {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_nand_1>;
+ pinctrl-0 = <&pinctrl_nand>;
nand-bus-width = <8>;
nand-ecc-mode = "hw";
status = "okay";
@@ -240,7 +419,11 @@
&pwm1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_pwm1_1>;
+ pinctrl-0 = <&pinctrl_pwm1>;
+ status = "okay";
+};
+
+&sata {
status = "okay";
};
@@ -251,18 +434,29 @@
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_2>;
+ pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
&uart2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart2_1>;
+ pinctrl-0 = <&pinctrl_uart2>;
status = "okay";
};
&uart3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart3_1>;
+ pinctrl-0 = <&pinctrl_uart3>;
+ status = "okay";
+};
+
+&usbh1 {
+ vbus-supply = <&reg_usbh1_vbus>;
+ phy_type = "utmi";
+ status = "okay";
+};
+
+&usbotg {
+ dr_mode = "peripheral";
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx53-mba53.dts b/arch/arm/boot/dts/imx53-mba53.dts
index a5b55c603591..7c8c12969892 100644
--- a/arch/arm/boot/dts/imx53-mba53.dts
+++ b/arch/arm/boot/dts/imx53-mba53.dts
@@ -17,14 +17,6 @@
model = "TQ MBa53 starter kit";
compatible = "tq,mba53", "tq,tqma53", "fsl,imx53";
- reg_backlight: fixed@0 {
- compatible = "regulator-fixed";
- regulator-name = "lcd-supply";
- gpio = <&gpio2 5 0>;
- startup-delay-us = <5000>;
- enable-active-low;
- };
-
backlight {
compatible = "pwm-backlight";
pwms = <&pwm2 0 50000>;
@@ -48,12 +40,27 @@
};
};
- reg_3p2v: 3p2v {
- compatible = "regulator-fixed";
- regulator-name = "3P2V";
- regulator-min-microvolt = <3200000>;
- regulator-max-microvolt = <3200000>;
- regulator-always-on;
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_backlight: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "lcd-supply";
+ gpio = <&gpio2 5 0>;
+ startup-delay-us = <5000>;
+ };
+
+ reg_3p2v: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "3P2V";
+ regulator-min-microvolt = <3200000>;
+ regulator-max-microvolt = <3200000>;
+ regulator-always-on;
+ };
};
sound {
@@ -157,14 +164,14 @@
&audmux {
status = "okay";
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_audmux_1>;
+ pinctrl-0 = <&pinctrl_audmux>;
};
&i2c2 {
codec: sgtl5000@a {
compatible = "fsl,sgtl5000";
reg = <0x0a>;
- clocks = <&clks 150>;
+ clocks = <&clks IMX5_CLK_SSI_EXT1_GATE>;
VDDA-supply = <&reg_3p2v>;
VDDIO-supply = <&reg_3p2v>;
};
diff --git a/arch/arm/boot/dts/imx53-qsb-common.dtsi b/arch/arm/boot/dts/imx53-qsb-common.dtsi
new file mode 100644
index 000000000000..3f825a6813da
--- /dev/null
+++ b/arch/arm/boot/dts/imx53-qsb-common.dtsi
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include "imx53.dtsi"
+
+/ {
+ memory {
+ reg = <0x70000000 0x40000000>;
+ };
+
+ display0: display@di0 {
+ compatible = "fsl,imx-parallel-display";
+ interface-pix-fmt = "rgb565";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ipu_disp0>;
+ status = "disabled";
+ display-timings {
+ claawvga {
+ native-mode;
+ clock-frequency = <27000000>;
+ hactive = <800>;
+ vactive = <480>;
+ hback-porch = <40>;
+ hfront-porch = <60>;
+ vback-porch = <10>;
+ vfront-porch = <10>;
+ hsync-len = <20>;
+ vsync-len = <10>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+ };
+
+ port {
+ display0_in: endpoint {
+ remote-endpoint = <&ipu_di0_disp0>;
+ };
+ };
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ power {
+ label = "Power Button";
+ gpios = <&gpio1 8 0>;
+ linux,code = <116>; /* KEY_POWER */
+ };
+
+ volume-up {
+ label = "Volume Up";
+ gpios = <&gpio2 14 0>;
+ linux,code = <115>; /* KEY_VOLUMEUP */
+ gpio-key,wakeup;
+ };
+
+ volume-down {
+ label = "Volume Down";
+ gpios = <&gpio2 15 0>;
+ linux,code = <114>; /* KEY_VOLUMEDOWN */
+ gpio-key,wakeup;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pin_gpio7_7>;
+
+ user {
+ label = "Heartbeat";
+ gpios = <&gpio7 7 0>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_3p2v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "3P2V";
+ regulator-min-microvolt = <3200000>;
+ regulator-max-microvolt = <3200000>;
+ regulator-always-on;
+ };
+
+ reg_usb_vbus: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "usb_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio7 8 0>;
+ enable-active-high;
+ };
+ };
+
+ sound {
+ compatible = "fsl,imx53-qsb-sgtl5000",
+ "fsl,imx-audio-sgtl5000";
+ model = "imx53-qsb-sgtl5000";
+ ssi-controller = <&ssi2>;
+ audio-codec = <&sgtl5000>;
+ audio-routing =
+ "MIC_IN", "Mic Jack",
+ "Mic Jack", "Mic Bias",
+ "Headphone Jack", "HP_OUT";
+ mux-int-port = <2>;
+ mux-ext-port = <5>;
+ };
+};
+
+&esdhc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_esdhc1>;
+ status = "okay";
+};
+
+&ipu_di0_disp0 {
+ remote-endpoint = <&display0_in>;
+};
+
+&ssi2 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&esdhc3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_esdhc3>;
+ cd-gpios = <&gpio3 11 0>;
+ wp-gpios = <&gpio3 12 0>;
+ bus-width = <8>;
+ status = "okay";
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx53-qsb {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ MX53_PAD_GPIO_0__CCM_SSI_EXT1_CLK 0x80000000
+ MX53_PAD_GPIO_8__GPIO1_8 0x80000000
+ MX53_PAD_PATA_DATA14__GPIO2_14 0x80000000
+ MX53_PAD_PATA_DATA15__GPIO2_15 0x80000000
+ MX53_PAD_EIM_DA11__GPIO3_11 0x80000000
+ MX53_PAD_EIM_DA12__GPIO3_12 0x80000000
+ MX53_PAD_PATA_DA_0__GPIO7_6 0x80000000
+ MX53_PAD_PATA_DA_2__GPIO7_8 0x80000000
+ MX53_PAD_GPIO_16__GPIO7_11 0x80000000
+ >;
+ };
+
+ led_pin_gpio7_7: led_gpio7_7@0 {
+ fsl,pins = <
+ MX53_PAD_PATA_DA_1__GPIO7_7 0x80000000
+ >;
+ };
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX53_PAD_KEY_COL0__AUDMUX_AUD5_TXC 0x80000000
+ MX53_PAD_KEY_ROW0__AUDMUX_AUD5_TXD 0x80000000
+ MX53_PAD_KEY_COL1__AUDMUX_AUD5_TXFS 0x80000000
+ MX53_PAD_KEY_ROW1__AUDMUX_AUD5_RXD 0x80000000
+ >;
+ };
+
+ pinctrl_esdhc1: esdhc1grp {
+ fsl,pins = <
+ MX53_PAD_SD1_DATA0__ESDHC1_DAT0 0x1d5
+ MX53_PAD_SD1_DATA1__ESDHC1_DAT1 0x1d5
+ MX53_PAD_SD1_DATA2__ESDHC1_DAT2 0x1d5
+ MX53_PAD_SD1_DATA3__ESDHC1_DAT3 0x1d5
+ MX53_PAD_SD1_CMD__ESDHC1_CMD 0x1d5
+ MX53_PAD_SD1_CLK__ESDHC1_CLK 0x1d5
+ >;
+ };
+
+ pinctrl_esdhc3: esdhc3grp {
+ fsl,pins = <
+ MX53_PAD_PATA_DATA8__ESDHC3_DAT0 0x1d5
+ MX53_PAD_PATA_DATA9__ESDHC3_DAT1 0x1d5
+ MX53_PAD_PATA_DATA10__ESDHC3_DAT2 0x1d5
+ MX53_PAD_PATA_DATA11__ESDHC3_DAT3 0x1d5
+ MX53_PAD_PATA_DATA0__ESDHC3_DAT4 0x1d5
+ MX53_PAD_PATA_DATA1__ESDHC3_DAT5 0x1d5
+ MX53_PAD_PATA_DATA2__ESDHC3_DAT6 0x1d5
+ MX53_PAD_PATA_DATA3__ESDHC3_DAT7 0x1d5
+ MX53_PAD_PATA_RESET_B__ESDHC3_CMD 0x1d5
+ MX53_PAD_PATA_IORDY__ESDHC3_CLK 0x1d5
+ >;
+ };
+
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX53_PAD_FEC_MDC__FEC_MDC 0x80000000
+ MX53_PAD_FEC_MDIO__FEC_MDIO 0x80000000
+ MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x80000000
+ MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x80000000
+ MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x80000000
+ MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x80000000
+ MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x80000000
+ MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000
+ MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x80000000
+ MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x80000000
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX53_PAD_CSI0_DAT8__I2C1_SDA 0xc0000000
+ MX53_PAD_CSI0_DAT9__I2C1_SCL 0xc0000000
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX53_PAD_KEY_ROW3__I2C2_SDA 0xc0000000
+ MX53_PAD_KEY_COL3__I2C2_SCL 0xc0000000
+ >;
+ };
+
+ pinctrl_ipu_disp0: ipudisp0grp {
+ fsl,pins = <
+ MX53_PAD_DI0_DISP_CLK__IPU_DI0_DISP_CLK 0x5
+ MX53_PAD_DI0_PIN15__IPU_DI0_PIN15 0x5
+ MX53_PAD_DI0_PIN2__IPU_DI0_PIN2 0x5
+ MX53_PAD_DI0_PIN3__IPU_DI0_PIN3 0x5
+ MX53_PAD_DISP0_DAT0__IPU_DISP0_DAT_0 0x5
+ MX53_PAD_DISP0_DAT1__IPU_DISP0_DAT_1 0x5
+ MX53_PAD_DISP0_DAT2__IPU_DISP0_DAT_2 0x5
+ MX53_PAD_DISP0_DAT3__IPU_DISP0_DAT_3 0x5
+ MX53_PAD_DISP0_DAT4__IPU_DISP0_DAT_4 0x5
+ MX53_PAD_DISP0_DAT5__IPU_DISP0_DAT_5 0x5
+ MX53_PAD_DISP0_DAT6__IPU_DISP0_DAT_6 0x5
+ MX53_PAD_DISP0_DAT7__IPU_DISP0_DAT_7 0x5
+ MX53_PAD_DISP0_DAT8__IPU_DISP0_DAT_8 0x5
+ MX53_PAD_DISP0_DAT9__IPU_DISP0_DAT_9 0x5
+ MX53_PAD_DISP0_DAT10__IPU_DISP0_DAT_10 0x5
+ MX53_PAD_DISP0_DAT11__IPU_DISP0_DAT_11 0x5
+ MX53_PAD_DISP0_DAT12__IPU_DISP0_DAT_12 0x5
+ MX53_PAD_DISP0_DAT13__IPU_DISP0_DAT_13 0x5
+ MX53_PAD_DISP0_DAT14__IPU_DISP0_DAT_14 0x5
+ MX53_PAD_DISP0_DAT15__IPU_DISP0_DAT_15 0x5
+ MX53_PAD_DISP0_DAT16__IPU_DISP0_DAT_16 0x5
+ MX53_PAD_DISP0_DAT17__IPU_DISP0_DAT_17 0x5
+ MX53_PAD_DISP0_DAT18__IPU_DISP0_DAT_18 0x5
+ MX53_PAD_DISP0_DAT19__IPU_DISP0_DAT_19 0x5
+ MX53_PAD_DISP0_DAT20__IPU_DISP0_DAT_20 0x5
+ MX53_PAD_DISP0_DAT21__IPU_DISP0_DAT_21 0x5
+ MX53_PAD_DISP0_DAT22__IPU_DISP0_DAT_22 0x5
+ MX53_PAD_DISP0_DAT23__IPU_DISP0_DAT_23 0x5
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX53_PAD_CSI0_DAT10__UART1_TXD_MUX 0x1e4
+ MX53_PAD_CSI0_DAT11__UART1_RXD_MUX 0x1e4
+ >;
+ };
+ };
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+
+ sgtl5000: codec@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ VDDA-supply = <&reg_3p2v>;
+ VDDIO-supply = <&reg_3p2v>;
+ clocks = <&clks IMX5_CLK_SSI_EXT1_GATE>;
+ };
+};
+
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ accelerometer: mma8450@1c {
+ compatible = "fsl,mma8450";
+ reg = <0x1c>;
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>;
+ status = "okay";
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fec>;
+ phy-mode = "rmii";
+ phy-reset-gpios = <&gpio7 6 0>;
+ status = "okay";
+};
+
+&sata {
+ status = "okay";
+};
+
+&vpu {
+ status = "okay";
+};
+
+&usbh1 {
+ vbus-supply = <&reg_usb_vbus>;
+ phy_type = "utmi";
+ status = "okay";
+};
+
+&usbotg {
+ dr_mode = "peripheral";
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx53-qsb.dts b/arch/arm/boot/dts/imx53-qsb.dts
index 8b254289344f..dec4b073ceb1 100644
--- a/arch/arm/boot/dts/imx53-qsb.dts
+++ b/arch/arm/boot/dts/imx53-qsb.dts
@@ -11,202 +11,14 @@
*/
/dts-v1/;
-#include "imx53.dtsi"
+#include "imx53-qsb-common.dtsi"
/ {
model = "Freescale i.MX53 Quick Start Board";
compatible = "fsl,imx53-qsb", "fsl,imx53";
-
- memory {
- reg = <0x70000000 0x40000000>;
- };
-
- display0: display@di0 {
- compatible = "fsl,imx-parallel-display";
- interface-pix-fmt = "rgb565";
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ipu_disp0_1>;
- status = "disabled";
- display-timings {
- claawvga {
- native-mode;
- clock-frequency = <27000000>;
- hactive = <800>;
- vactive = <480>;
- hback-porch = <40>;
- hfront-porch = <60>;
- vback-porch = <10>;
- vfront-porch = <10>;
- hsync-len = <20>;
- vsync-len = <10>;
- hsync-active = <0>;
- vsync-active = <0>;
- de-active = <1>;
- pixelclk-active = <0>;
- };
- };
-
- port {
- display0_in: endpoint {
- remote-endpoint = <&ipu_di0_disp0>;
- };
- };
- };
-
- gpio-keys {
- compatible = "gpio-keys";
-
- power {
- label = "Power Button";
- gpios = <&gpio1 8 0>;
- linux,code = <116>; /* KEY_POWER */
- };
-
- volume-up {
- label = "Volume Up";
- gpios = <&gpio2 14 0>;
- linux,code = <115>; /* KEY_VOLUMEUP */
- gpio-key,wakeup;
- };
-
- volume-down {
- label = "Volume Down";
- gpios = <&gpio2 15 0>;
- linux,code = <114>; /* KEY_VOLUMEDOWN */
- gpio-key,wakeup;
- };
- };
-
- leds {
- compatible = "gpio-leds";
- pinctrl-names = "default";
- pinctrl-0 = <&led_pin_gpio7_7>;
-
- user {
- label = "Heartbeat";
- gpios = <&gpio7 7 0>;
- linux,default-trigger = "heartbeat";
- };
- };
-
- regulators {
- compatible = "simple-bus";
-
- reg_3p2v: 3p2v {
- compatible = "regulator-fixed";
- regulator-name = "3P2V";
- regulator-min-microvolt = <3200000>;
- regulator-max-microvolt = <3200000>;
- regulator-always-on;
- };
-
- reg_usb_vbus: usb_vbus {
- compatible = "regulator-fixed";
- regulator-name = "usb_vbus";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
- gpio = <&gpio7 8 0>;
- enable-active-high;
- };
- };
-
- sound {
- compatible = "fsl,imx53-qsb-sgtl5000",
- "fsl,imx-audio-sgtl5000";
- model = "imx53-qsb-sgtl5000";
- ssi-controller = <&ssi2>;
- audio-codec = <&sgtl5000>;
- audio-routing =
- "MIC_IN", "Mic Jack",
- "Mic Jack", "Mic Bias",
- "Headphone Jack", "HP_OUT";
- mux-int-port = <2>;
- mux-ext-port = <5>;
- };
-};
-
-&esdhc1 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc1_1>;
- status = "okay";
-};
-
-&ipu_di0_disp0 {
- remote-endpoint = <&display0_in>;
-};
-
-&ssi2 {
- fsl,mode = "i2s-slave";
- status = "okay";
-};
-
-&esdhc3 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc3_1>;
- cd-gpios = <&gpio3 11 0>;
- wp-gpios = <&gpio3 12 0>;
- bus-width = <8>;
- status = "okay";
-};
-
-&iomuxc {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_hog>;
-
- hog {
- pinctrl_hog: hoggrp {
- fsl,pins = <
- MX53_PAD_GPIO_0__CCM_SSI_EXT1_CLK 0x80000000
- MX53_PAD_GPIO_8__GPIO1_8 0x80000000
- MX53_PAD_PATA_DATA14__GPIO2_14 0x80000000
- MX53_PAD_PATA_DATA15__GPIO2_15 0x80000000
- MX53_PAD_EIM_DA11__GPIO3_11 0x80000000
- MX53_PAD_EIM_DA12__GPIO3_12 0x80000000
- MX53_PAD_PATA_DA_0__GPIO7_6 0x80000000
- MX53_PAD_PATA_DA_2__GPIO7_8 0x80000000
- MX53_PAD_GPIO_16__GPIO7_11 0x80000000
- >;
- };
-
- led_pin_gpio7_7: led_gpio7_7@0 {
- fsl,pins = <
- MX53_PAD_PATA_DA_1__GPIO7_7 0x80000000
- >;
- };
- };
-
-};
-
-&uart1 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_1>;
- status = "okay";
-};
-
-&i2c2 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c2_1>;
- status = "okay";
-
- sgtl5000: codec@0a {
- compatible = "fsl,sgtl5000";
- reg = <0x0a>;
- VDDA-supply = <&reg_3p2v>;
- VDDIO-supply = <&reg_3p2v>;
- clocks = <&clks 150>;
- };
};
&i2c1 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c1_1>;
- status = "okay";
-
- accelerometer: mma8450@1c {
- compatible = "fsl,mma8450";
- reg = <0x1c>;
- };
-
pmic: dialog@48 {
compatible = "dlg,da9053-aa", "dlg,da9052";
reg = <0x48>;
@@ -301,32 +113,3 @@
};
};
};
-
-&audmux {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_audmux_1>;
- status = "okay";
-};
-
-&fec {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec_1>;
- phy-mode = "rmii";
- phy-reset-gpios = <&gpio7 6 0>;
- status = "okay";
-};
-
-&vpu {
- status = "okay";
-};
-
-&usbh1 {
- vbus-supply = <&reg_usb_vbus>;
- phy_type = "utmi";
- status = "okay";
-};
-
-&usbotg {
- dr_mode = "peripheral";
- status = "okay";
-};
diff --git a/arch/arm/boot/dts/imx53-qsrb.dts b/arch/arm/boot/dts/imx53-qsrb.dts
new file mode 100644
index 000000000000..f1bbf9a32991
--- /dev/null
+++ b/arch/arm/boot/dts/imx53-qsrb.dts
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+
+#include "imx53-qsb-common.dtsi"
+
+/ {
+ model = "Freescale i.MX53 Quick Start-R Board";
+ compatible = "fsl,imx53-qsrb", "fsl,imx53";
+};
+
+&iomuxc {
+ i2c1 {
+ /* open drain */
+ pinctrl_i2c1_qsrb: i2c1grp-1 {
+ fsl,pins = <
+ MX53_PAD_CSI0_DAT8__I2C1_SDA 0x400001ec
+ MX53_PAD_CSI0_DAT9__I2C1_SCL 0x400001ec
+ >;
+ };
+ };
+};
+
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1_qsrb>;
+ status = "okay";
+
+ pmic: mc34708@8 {
+ compatible = "fsl,mc34708";
+ reg = <0x08>;
+ interrupt-parent = <&gpio5>;
+ interrupts = <23 0x8>;
+ regulators {
+ sw1_reg: sw1a {
+ regulator-name = "SW1";
+ regulator-min-microvolt = <650000>;
+ regulator-max-microvolt = <1437500>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw1b_reg: sw1b {
+ regulator-name = "SW1B";
+ regulator-min-microvolt = <650000>;
+ regulator-max-microvolt = <1437500>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw2_reg: sw2 {
+ regulator-name = "SW2";
+ regulator-min-microvolt = <650000>;
+ regulator-max-microvolt = <1437500>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3_reg: sw3 {
+ regulator-name = "SW3";
+ regulator-min-microvolt = <650000>;
+ regulator-max-microvolt = <1425000>;
+ regulator-boot-on;
+ };
+
+ sw4a_reg: sw4a {
+ regulator-name = "SW4A";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw4b_reg: sw4b {
+ regulator-name = "SW4B";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw5_reg: sw5 {
+ regulator-name = "SW5";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ swbst_reg: swbst {
+ regulator-name = "SWBST";
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vpll_reg: vpll {
+ regulator-name = "VPLL";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-boot-on;
+ };
+
+ vrefddr_reg: vrefddr {
+ regulator-name = "VREFDDR";
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vusb_reg: vusb {
+ regulator-name = "VUSB";
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vusb2_reg: vusb2 {
+ regulator-name = "VUSB2";
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vdac_reg: vdac {
+ regulator-name = "VDAC";
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2775000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vgen1_reg: vgen1 {
+ regulator-name = "VGEN1";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1550000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vgen2_reg: vgen2 {
+ regulator-name = "VGEN2";
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/imx53-smd.dts b/arch/arm/boot/dts/imx53-smd.dts
index a9b6e10de0a5..5ec1590ff7bc 100644
--- a/arch/arm/boot/dts/imx53-smd.dts
+++ b/arch/arm/boot/dts/imx53-smd.dts
@@ -40,7 +40,7 @@
&esdhc1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc1_1>;
+ pinctrl-0 = <&pinctrl_esdhc1>;
cd-gpios = <&gpio3 13 0>;
wp-gpios = <&gpio4 11 0>;
status = "okay";
@@ -48,21 +48,21 @@
&esdhc2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc2_1>;
+ pinctrl-0 = <&pinctrl_esdhc2>;
non-removable;
status = "okay";
};
&uart3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart3_1>;
+ pinctrl-0 = <&pinctrl_uart3>;
fsl,uart-has-rtscts;
status = "okay";
};
&ecspi1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi1_1>;
+ pinctrl-0 = <&pinctrl_ecspi1>;
fsl,spi-num-chipselects = <2>;
cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>;
status = "okay";
@@ -95,7 +95,7 @@
&esdhc3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc3_1>;
+ pinctrl-0 = <&pinctrl_esdhc3>;
non-removable;
status = "okay";
};
@@ -104,7 +104,7 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- hog {
+ imx53-smd {
pinctrl_hog: hoggrp {
fsl,pins = <
MX53_PAD_PATA_DATA14__GPIO2_14 0x80000000
@@ -116,24 +116,121 @@
MX53_PAD_PATA_DA_0__GPIO7_6 0x80000000
>;
};
+
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX53_PAD_EIM_D16__ECSPI1_SCLK 0x80000000
+ MX53_PAD_EIM_D17__ECSPI1_MISO 0x80000000
+ MX53_PAD_EIM_D18__ECSPI1_MOSI 0x80000000
+ >;
+ };
+
+ pinctrl_esdhc1: esdhc1grp {
+ fsl,pins = <
+ MX53_PAD_SD1_DATA0__ESDHC1_DAT0 0x1d5
+ MX53_PAD_SD1_DATA1__ESDHC1_DAT1 0x1d5
+ MX53_PAD_SD1_DATA2__ESDHC1_DAT2 0x1d5
+ MX53_PAD_SD1_DATA3__ESDHC1_DAT3 0x1d5
+ MX53_PAD_SD1_CMD__ESDHC1_CMD 0x1d5
+ MX53_PAD_SD1_CLK__ESDHC1_CLK 0x1d5
+ >;
+ };
+
+ pinctrl_esdhc2: esdhc2grp {
+ fsl,pins = <
+ MX53_PAD_SD2_CMD__ESDHC2_CMD 0x1d5
+ MX53_PAD_SD2_CLK__ESDHC2_CLK 0x1d5
+ MX53_PAD_SD2_DATA0__ESDHC2_DAT0 0x1d5
+ MX53_PAD_SD2_DATA1__ESDHC2_DAT1 0x1d5
+ MX53_PAD_SD2_DATA2__ESDHC2_DAT2 0x1d5
+ MX53_PAD_SD2_DATA3__ESDHC2_DAT3 0x1d5
+ >;
+ };
+
+ pinctrl_esdhc3: esdhc3grp {
+ fsl,pins = <
+ MX53_PAD_PATA_DATA8__ESDHC3_DAT0 0x1d5
+ MX53_PAD_PATA_DATA9__ESDHC3_DAT1 0x1d5
+ MX53_PAD_PATA_DATA10__ESDHC3_DAT2 0x1d5
+ MX53_PAD_PATA_DATA11__ESDHC3_DAT3 0x1d5
+ MX53_PAD_PATA_DATA0__ESDHC3_DAT4 0x1d5
+ MX53_PAD_PATA_DATA1__ESDHC3_DAT5 0x1d5
+ MX53_PAD_PATA_DATA2__ESDHC3_DAT6 0x1d5
+ MX53_PAD_PATA_DATA3__ESDHC3_DAT7 0x1d5
+ MX53_PAD_PATA_RESET_B__ESDHC3_CMD 0x1d5
+ MX53_PAD_PATA_IORDY__ESDHC3_CLK 0x1d5
+ >;
+ };
+
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX53_PAD_FEC_MDC__FEC_MDC 0x80000000
+ MX53_PAD_FEC_MDIO__FEC_MDIO 0x80000000
+ MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x80000000
+ MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x80000000
+ MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x80000000
+ MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x80000000
+ MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x80000000
+ MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000
+ MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x80000000
+ MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x80000000
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX53_PAD_CSI0_DAT8__I2C1_SDA 0xc0000000
+ MX53_PAD_CSI0_DAT9__I2C1_SCL 0xc0000000
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX53_PAD_KEY_ROW3__I2C2_SDA 0xc0000000
+ MX53_PAD_KEY_COL3__I2C2_SCL 0xc0000000
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX53_PAD_CSI0_DAT10__UART1_TXD_MUX 0x1e4
+ MX53_PAD_CSI0_DAT11__UART1_RXD_MUX 0x1e4
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX53_PAD_PATA_BUFFER_EN__UART2_RXD_MUX 0x1e4
+ MX53_PAD_PATA_DMARQ__UART2_TXD_MUX 0x1e4
+ >;
+ };
+
+ pinctrl_uart3: uart3grp {
+ fsl,pins = <
+ MX53_PAD_PATA_CS_0__UART3_TXD_MUX 0x1e4
+ MX53_PAD_PATA_CS_1__UART3_RXD_MUX 0x1e4
+ MX53_PAD_PATA_DA_1__UART3_CTS 0x1e4
+ MX53_PAD_PATA_DA_2__UART3_RTS 0x1e4
+ >;
+ };
};
};
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_1>;
+ pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
&uart2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart2_1>;
+ pinctrl-0 = <&pinctrl_uart2>;
status = "okay";
};
&i2c2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c2_1>;
+ pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
codec: sgtl5000@0a {
@@ -154,7 +251,7 @@
&i2c1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c1_1>;
+ pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
accelerometer: mma8450@1c {
@@ -175,7 +272,7 @@
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec_1>;
+ pinctrl-0 = <&pinctrl_fec>;
phy-mode = "rmii";
phy-reset-gpios = <&gpio7 6 0>;
status = "okay";
diff --git a/arch/arm/boot/dts/imx53-tqma53.dtsi b/arch/arm/boot/dts/imx53-tqma53.dtsi
index abd72af545bf..4f1f0e2868bf 100644
--- a/arch/arm/boot/dts/imx53-tqma53.dtsi
+++ b/arch/arm/boot/dts/imx53-tqma53.dtsi
@@ -22,9 +22,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_3p3v: 3p3v {
+ reg_3p3v: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "3P3V";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
@@ -35,8 +38,8 @@
&esdhc2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc2_1>,
- <&pinctrl_tqma53_esdhc2_2>;
+ pinctrl-0 = <&pinctrl_esdhc2>,
+ <&pinctrl_esdhc2_cdwp>;
vmmc-supply = <&reg_3p3v>;
wp-gpios = <&gpio1 2 0>;
cd-gpios = <&gpio1 4 0>;
@@ -45,13 +48,13 @@
&uart3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart3_2>;
+ pinctrl-0 = <&pinctrl_uart3>;
status = "disabled";
};
&ecspi1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi1_1>;
+ pinctrl-0 = <&pinctrl_ecspi1>;
fsl,spi-num-chipselects = <4>;
cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>,
<&gpio3 24 0>, <&gpio3 25 0>;
@@ -60,7 +63,7 @@
&esdhc3 { /* EMMC */
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc3_1>;
+ pinctrl-0 = <&pinctrl_esdhc3>;
vmmc-supply = <&reg_3p3v>;
non-removable;
bus-width = <8>;
@@ -71,27 +74,7 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- esdhc2_2 {
- pinctrl_tqma53_esdhc2_2: esdhc2-tqma53-grp2 {
- fsl,pins = <
- MX53_PAD_GPIO_4__GPIO1_4 0x80000000 /* SD2_CD */
- MX53_PAD_GPIO_2__GPIO1_2 0x80000000 /* SD2_WP */
- >;
- };
- };
-
- i2s {
- pinctrl_i2s_1: i2s-grp1 {
- fsl,pins = <
- MX53_PAD_KEY_COL0__AUDMUX_AUD5_TXC 0x80000000 /* I2S_SCLK */
- MX53_PAD_KEY_ROW0__AUDMUX_AUD5_TXD 0x80000000 /* I2S_DOUT */
- MX53_PAD_KEY_COL1__AUDMUX_AUD5_TXFS 0x80000000 /* I2S_LRCLK */
- MX53_PAD_KEY_ROW1__AUDMUX_AUD5_RXD 0x80000000 /* I2S_DIN */
- >;
- };
- };
-
- hog {
+ imx53-tqma53 {
pinctrl_hog: hoggrp {
fsl,pins = <
MX53_PAD_GPIO_0__CCM_SSI_EXT1_CLK 0x80000000 /* SSI_MCLK */
@@ -107,43 +90,165 @@
MX53_PAD_GPIO_1__PWM2_PWMO 0x80000000 /* LCD_CONTRAST */
>;
};
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX53_PAD_KEY_COL0__AUDMUX_AUD5_TXC 0x80000000
+ MX53_PAD_KEY_ROW0__AUDMUX_AUD5_TXD 0x80000000
+ MX53_PAD_KEY_COL1__AUDMUX_AUD5_TXFS 0x80000000
+ MX53_PAD_KEY_ROW1__AUDMUX_AUD5_RXD 0x80000000
+ >;
+ };
+
+ pinctrl_can1: can1grp {
+ fsl,pins = <
+ MX53_PAD_KEY_COL2__CAN1_TXCAN 0x80000000
+ MX53_PAD_KEY_ROW2__CAN1_RXCAN 0x80000000
+ >;
+ };
+
+ pinctrl_can2: can2grp {
+ fsl,pins = <
+ MX53_PAD_KEY_COL4__CAN2_TXCAN 0x80000000
+ MX53_PAD_KEY_ROW4__CAN2_RXCAN 0x80000000
+ >;
+ };
+
+ pinctrl_cspi: cspigrp {
+ fsl,pins = <
+ MX53_PAD_SD1_DATA0__CSPI_MISO 0x1d5
+ MX53_PAD_SD1_CMD__CSPI_MOSI 0x1d5
+ MX53_PAD_SD1_CLK__CSPI_SCLK 0x1d5
+ >;
+ };
+
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX53_PAD_EIM_D16__ECSPI1_SCLK 0x80000000
+ MX53_PAD_EIM_D17__ECSPI1_MISO 0x80000000
+ MX53_PAD_EIM_D18__ECSPI1_MOSI 0x80000000
+ >;
+ };
+
+ pinctrl_esdhc2: esdhc2grp {
+ fsl,pins = <
+ MX53_PAD_SD2_CMD__ESDHC2_CMD 0x1d5
+ MX53_PAD_SD2_CLK__ESDHC2_CLK 0x1d5
+ MX53_PAD_SD2_DATA0__ESDHC2_DAT0 0x1d5
+ MX53_PAD_SD2_DATA1__ESDHC2_DAT1 0x1d5
+ MX53_PAD_SD2_DATA2__ESDHC2_DAT2 0x1d5
+ MX53_PAD_SD2_DATA3__ESDHC2_DAT3 0x1d5
+ >;
+ };
+
+ pinctrl_esdhc2_cdwp: esdhc2cdwp {
+ fsl,pins = <
+ MX53_PAD_GPIO_4__GPIO1_4 0x80000000 /* SD2_CD */
+ MX53_PAD_GPIO_2__GPIO1_2 0x80000000 /* SD2_WP */
+ >;
+ };
+
+ pinctrl_esdhc3: esdhc3grp {
+ fsl,pins = <
+ MX53_PAD_PATA_DATA8__ESDHC3_DAT0 0x1d5
+ MX53_PAD_PATA_DATA9__ESDHC3_DAT1 0x1d5
+ MX53_PAD_PATA_DATA10__ESDHC3_DAT2 0x1d5
+ MX53_PAD_PATA_DATA11__ESDHC3_DAT3 0x1d5
+ MX53_PAD_PATA_DATA0__ESDHC3_DAT4 0x1d5
+ MX53_PAD_PATA_DATA1__ESDHC3_DAT5 0x1d5
+ MX53_PAD_PATA_DATA2__ESDHC3_DAT6 0x1d5
+ MX53_PAD_PATA_DATA3__ESDHC3_DAT7 0x1d5
+ MX53_PAD_PATA_RESET_B__ESDHC3_CMD 0x1d5
+ MX53_PAD_PATA_IORDY__ESDHC3_CLK 0x1d5
+ >;
+ };
+
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX53_PAD_FEC_MDC__FEC_MDC 0x80000000
+ MX53_PAD_FEC_MDIO__FEC_MDIO 0x80000000
+ MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x80000000
+ MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x80000000
+ MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x80000000
+ MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x80000000
+ MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x80000000
+ MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000
+ MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x80000000
+ MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x80000000
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX53_PAD_KEY_ROW3__I2C2_SDA 0xc0000000
+ MX53_PAD_KEY_COL3__I2C2_SCL 0xc0000000
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX53_PAD_GPIO_6__I2C3_SDA 0xc0000000
+ MX53_PAD_GPIO_5__I2C3_SCL 0xc0000000
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX53_PAD_PATA_DIOW__UART1_TXD_MUX 0x1e4
+ MX53_PAD_PATA_DMACK__UART1_RXD_MUX 0x1e4
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX53_PAD_PATA_BUFFER_EN__UART2_RXD_MUX 0x1e4
+ MX53_PAD_PATA_DMARQ__UART2_TXD_MUX 0x1e4
+ >;
+ };
+
+ pinctrl_uart3: uart3grp {
+ fsl,pins = <
+ MX53_PAD_PATA_CS_0__UART3_TXD_MUX 0x1e4
+ MX53_PAD_PATA_CS_1__UART3_RXD_MUX 0x1e4
+ >;
+ };
};
};
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_2>;
+ pinctrl-0 = <&pinctrl_uart1>;
fsl,uart-has-rtscts;
status = "disabled";
};
&uart2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart2_1>;
+ pinctrl-0 = <&pinctrl_uart2>;
status = "disabled";
};
&can1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_can1_2>;
+ pinctrl-0 = <&pinctrl_can1>;
status = "disabled";
};
&can2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_can2_1>;
+ pinctrl-0 = <&pinctrl_can2>;
status = "disabled";
};
&i2c3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c3_1>;
+ pinctrl-0 = <&pinctrl_i2c3>;
status = "disabled";
};
&cspi {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_cspi_1>;
+ pinctrl-0 = <&pinctrl_cspi>;
fsl,spi-num-chipselects = <3>;
cs-gpios = <&gpio1 18 0>, <&gpio1 19 0>,
<&gpio1 21 0>;
@@ -152,7 +257,7 @@
&i2c2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c2_1>;
+ pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
pmic: mc34708@8 {
@@ -177,7 +282,7 @@
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec_1>;
+ pinctrl-0 = <&pinctrl_fec>;
phy-mode = "rmii";
status = "disabled";
};
diff --git a/arch/arm/boot/dts/imx53-tx53-x03x.dts b/arch/arm/boot/dts/imx53-tx53-x03x.dts
new file mode 100644
index 000000000000..0217dde3b36b
--- /dev/null
+++ b/arch/arm/boot/dts/imx53-tx53-x03x.dts
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2013 Lothar Waßmann <LW@KARO-electronics.de>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx53-tx53.dtsi"
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/pwm/pwm.h>
+
+/ {
+ model = "Ka-Ro electronics TX53 module (LCD)";
+ compatible = "karo,tx53", "fsl,imx53";
+
+ aliases {
+ display = &display;
+ };
+
+ soc {
+ display: display@di0 {
+ compatible = "fsl,imx-parallel-display";
+ crtcs = <&ipu 0>;
+ interface-pix-fmt = "rgb24";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_rgb24_vga1>;
+ status = "okay";
+
+ display-timings {
+ VGA {
+ clock-frequency = <25200000>;
+ hactive = <640>;
+ vactive = <480>;
+ hback-porch = <48>;
+ hsync-len = <96>;
+ hfront-porch = <16>;
+ vback-porch = <31>;
+ vsync-len = <2>;
+ vfront-porch = <12>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+
+ ETV570 {
+ clock-frequency = <25200000>;
+ hactive = <640>;
+ vactive = <480>;
+ hback-porch = <114>;
+ hsync-len = <30>;
+ hfront-porch = <16>;
+ vback-porch = <32>;
+ vsync-len = <3>;
+ vfront-porch = <10>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+
+ ET0350 {
+ clock-frequency = <6413760>;
+ hactive = <320>;
+ vactive = <240>;
+ hback-porch = <34>;
+ hsync-len = <34>;
+ hfront-porch = <20>;
+ vback-porch = <15>;
+ vsync-len = <3>;
+ vfront-porch = <4>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+
+ ET0430 {
+ clock-frequency = <9009000>;
+ hactive = <480>;
+ vactive = <272>;
+ hback-porch = <2>;
+ hsync-len = <41>;
+ hfront-porch = <2>;
+ vback-porch = <2>;
+ vsync-len = <10>;
+ vfront-porch = <2>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <1>;
+ };
+
+ ET0500 {
+ clock-frequency = <33264000>;
+ hactive = <800>;
+ vactive = <480>;
+ hback-porch = <88>;
+ hsync-len = <128>;
+ hfront-porch = <40>;
+ vback-porch = <33>;
+ vsync-len = <2>;
+ vfront-porch = <10>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+
+ ET0700 { /* same as ET0500 */
+ clock-frequency = <33264000>;
+ hactive = <800>;
+ vactive = <480>;
+ hback-porch = <88>;
+ hsync-len = <128>;
+ hfront-porch = <40>;
+ vback-porch = <33>;
+ vsync-len = <2>;
+ vfront-porch = <10>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+
+ ETQ570 {
+ clock-frequency = <6596040>;
+ hactive = <320>;
+ vactive = <240>;
+ hback-porch = <38>;
+ hsync-len = <30>;
+ hfront-porch = <30>;
+ vback-porch = <16>;
+ vsync-len = <3>;
+ vfront-porch = <4>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+ };
+ };
+ };
+
+ backlight: backlight {
+ compatible = "pwm-backlight";
+ pwms = <&pwm2 0 500000 PWM_POLARITY_INVERTED>;
+ power-supply = <&reg_3v3>;
+ brightness-levels = <
+ 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 31 32 33 34 35 36 37 38 39
+ 40 41 42 43 44 45 46 47 48 49
+ 50 51 52 53 54 55 56 57 58 59
+ 60 61 62 63 64 65 66 67 68 69
+ 70 71 72 73 74 75 76 77 78 79
+ 80 81 82 83 84 85 86 87 88 89
+ 90 91 92 93 94 95 96 97 98 99
+ 100
+ >;
+ default-brightness-level = <50>;
+ };
+
+ regulators {
+ reg_lcd_pwr: regulator@5 {
+ compatible = "regulator-fixed";
+ reg = <5>;
+ regulator-name = "LCD POWER";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio2 31 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ regulator-boot-on;
+ };
+
+ reg_lcd_reset: regulator@6 {
+ compatible = "regulator-fixed";
+ reg = <6>;
+ regulator-name = "LCD RESET";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio3 29 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ regulator-boot-on;
+ };
+ };
+};
+
+&i2c3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3>;
+ status = "okay";
+
+ sgtl5000: codec@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ VDDA-supply = <&reg_2v5>;
+ VDDIO-supply = <&reg_3v3>;
+ clocks = <&mclk>;
+ };
+
+ polytouch: edt-ft5x06@38 {
+ compatible = "edt,edt-ft5x06";
+ reg = <0x38>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_edt_ft5x06_1>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <15 0>;
+ reset-gpios = <&gpio2 22 GPIO_ACTIVE_LOW>;
+ wake-gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>;
+ };
+
+ touchscreen: tsc2007@48 {
+ compatible = "ti,tsc2007";
+ reg = <0x48>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_tsc2007>;
+ interrupt-parent = <&gpio3>;
+ interrupts = <26 0>;
+ gpios = <&gpio3 26 GPIO_ACTIVE_LOW>;
+ ti,x-plate-ohms = <660>;
+ linux,wakeup;
+ };
+};
+
+&iomuxc {
+ imx53-tx53-x03x {
+ pinctrl_edt_ft5x06_1: edt-ft5x06grp-1 {
+ fsl,pins = <
+ MX53_PAD_NANDF_CS2__GPIO6_15 0x1f0 /* Interrupt */
+ MX53_PAD_EIM_A16__GPIO2_22 0x04 /* Reset */
+ MX53_PAD_EIM_A17__GPIO2_21 0x04 /* Wake */
+ >;
+ };
+
+ pinctrl_kpp: kppgrp {
+ fsl,pins = <
+ MX53_PAD_GPIO_9__KPP_COL_6 0x1f4
+ MX53_PAD_GPIO_4__KPP_COL_7 0x1f4
+ MX53_PAD_KEY_COL2__KPP_COL_2 0x1f4
+ MX53_PAD_KEY_COL3__KPP_COL_3 0x1f4
+ MX53_PAD_GPIO_2__KPP_ROW_6 0x1f4
+ MX53_PAD_GPIO_5__KPP_ROW_7 0x1f4
+ MX53_PAD_KEY_ROW2__KPP_ROW_2 0x1f4
+ MX53_PAD_KEY_ROW3__KPP_ROW_3 0x1f4
+ >;
+ };
+
+ pinctrl_rgb24_vga1: rgb24-vgagrp1 {
+ fsl,pins = <
+ MX53_PAD_DI0_DISP_CLK__IPU_DI0_DISP_CLK 0x5
+ MX53_PAD_DI0_PIN15__IPU_DI0_PIN15 0x5
+ MX53_PAD_DI0_PIN2__IPU_DI0_PIN2 0x5
+ MX53_PAD_DI0_PIN3__IPU_DI0_PIN3 0x5
+ MX53_PAD_DISP0_DAT0__IPU_DISP0_DAT_0 0x5
+ MX53_PAD_DISP0_DAT1__IPU_DISP0_DAT_1 0x5
+ MX53_PAD_DISP0_DAT2__IPU_DISP0_DAT_2 0x5
+ MX53_PAD_DISP0_DAT3__IPU_DISP0_DAT_3 0x5
+ MX53_PAD_DISP0_DAT4__IPU_DISP0_DAT_4 0x5
+ MX53_PAD_DISP0_DAT5__IPU_DISP0_DAT_5 0x5
+ MX53_PAD_DISP0_DAT6__IPU_DISP0_DAT_6 0x5
+ MX53_PAD_DISP0_DAT7__IPU_DISP0_DAT_7 0x5
+ MX53_PAD_DISP0_DAT8__IPU_DISP0_DAT_8 0x5
+ MX53_PAD_DISP0_DAT9__IPU_DISP0_DAT_9 0x5
+ MX53_PAD_DISP0_DAT10__IPU_DISP0_DAT_10 0x5
+ MX53_PAD_DISP0_DAT11__IPU_DISP0_DAT_11 0x5
+ MX53_PAD_DISP0_DAT12__IPU_DISP0_DAT_12 0x5
+ MX53_PAD_DISP0_DAT13__IPU_DISP0_DAT_13 0x5
+ MX53_PAD_DISP0_DAT14__IPU_DISP0_DAT_14 0x5
+ MX53_PAD_DISP0_DAT15__IPU_DISP0_DAT_15 0x5
+ MX53_PAD_DISP0_DAT16__IPU_DISP0_DAT_16 0x5
+ MX53_PAD_DISP0_DAT17__IPU_DISP0_DAT_17 0x5
+ MX53_PAD_DISP0_DAT18__IPU_DISP0_DAT_18 0x5
+ MX53_PAD_DISP0_DAT19__IPU_DISP0_DAT_19 0x5
+ MX53_PAD_DISP0_DAT20__IPU_DISP0_DAT_20 0x5
+ MX53_PAD_DISP0_DAT21__IPU_DISP0_DAT_21 0x5
+ MX53_PAD_DISP0_DAT22__IPU_DISP0_DAT_22 0x5
+ MX53_PAD_DISP0_DAT23__IPU_DISP0_DAT_23 0x5
+ >;
+ };
+
+ pinctrl_tsc2007: tsc2007grp {
+ fsl,pins = <
+ MX53_PAD_EIM_D26__GPIO3_26 0x1f0 /* Interrupt */
+ >;
+ };
+ };
+};
+
+&kpp {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_kpp>;
+ /* sample keymap */
+ /* row/col 0,1 are mapped to KPP row/col 6,7 */
+ linux,keymap = <
+ MATRIX_KEY(6, 6, KEY_POWER)
+ MATRIX_KEY(6, 7, KEY_KP0)
+ MATRIX_KEY(6, 2, KEY_KP1)
+ MATRIX_KEY(6, 3, KEY_KP2)
+ MATRIX_KEY(7, 6, KEY_KP3)
+ MATRIX_KEY(7, 7, KEY_KP4)
+ MATRIX_KEY(7, 2, KEY_KP5)
+ MATRIX_KEY(7, 3, KEY_KP6)
+ MATRIX_KEY(2, 6, KEY_KP7)
+ MATRIX_KEY(2, 7, KEY_KP8)
+ MATRIX_KEY(2, 2, KEY_KP9)
+ >;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx53-tx53-x13x.dts b/arch/arm/boot/dts/imx53-tx53-x13x.dts
new file mode 100644
index 000000000000..64804719f0f4
--- /dev/null
+++ b/arch/arm/boot/dts/imx53-tx53-x13x.dts
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2013 Lothar Waßmann <LW@KARO-electronics.de>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx53-tx53.dtsi"
+#include <dt-bindings/input/input.h>
+
+/ {
+ model = "Ka-Ro electronics TX53 module (LVDS)";
+ compatible = "karo,tx53", "fsl,imx53";
+
+ aliases {
+ display = &lvds0;
+ lvds0 = &lvds0;
+ lvds1 = &lvds1;
+ };
+
+ backlight0: backlight0 {
+ compatible = "pwm-backlight";
+ pwms = <&pwm2 0 500000 0>;
+ power-supply = <&reg_3v3>;
+ brightness-levels = <
+ 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 31 32 33 34 35 36 37 38 39
+ 40 41 42 43 44 45 46 47 48 49
+ 50 51 52 53 54 55 56 57 58 59
+ 60 61 62 63 64 65 66 67 68 69
+ 70 71 72 73 74 75 76 77 78 79
+ 80 81 82 83 84 85 86 87 88 89
+ 90 91 92 93 94 95 96 97 98 99
+ 100
+ >;
+ default-brightness-level = <50>;
+ };
+
+ backlight1: backlight1 {
+ compatible = "pwm-backlight";
+ pwms = <&pwm1 0 500000 0>;
+ power-supply = <&reg_3v3>;
+ brightness-levels = <
+ 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 31 32 33 34 35 36 37 38 39
+ 40 41 42 43 44 45 46 47 48 49
+ 50 51 52 53 54 55 56 57 58 59
+ 60 61 62 63 64 65 66 67 68 69
+ 70 71 72 73 74 75 76 77 78 79
+ 80 81 82 83 84 85 86 87 88 89
+ 90 91 92 93 94 95 96 97 98 99
+ 100
+ >;
+ default-brightness-level = <50>;
+ };
+
+ regulators {
+ reg_lcd_pwr0: regulator@5 {
+ compatible = "regulator-fixed";
+ reg = <5>;
+ regulator-name = "LVDS0 POWER";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio3 29 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ regulator-boot-on;
+ };
+
+ reg_lcd_pwr1: regulator@6 {
+ compatible = "regulator-fixed";
+ reg = <6>;
+ regulator-name = "LVDS1 POWER";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio2 31 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ regulator-boot-on;
+ };
+ };
+};
+
+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+
+ touchscreen2: eeti@04 {
+ compatible = "eeti,egalax_ts";
+ reg = <0x04>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_eeti2>;
+ interrupt-parent = <&gpio3>;
+ interrupts = <23 0>;
+ wakeup-gpios = <&gpio3 23 GPIO_ACTIVE_HIGH>;
+ linux,wakeup;
+ };
+};
+
+&i2c3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3>;
+ status = "okay";
+
+ sgtl5000: codec@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ VDDA-supply = <&reg_2v5>;
+ VDDIO-supply = <&reg_3v3>;
+ clocks = <&mclk>;
+ };
+
+ touchscreen1: eeti@04 {
+ compatible = "eeti,egalax_ts";
+ reg = <0x04>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_eeti1>;
+ interrupt-parent = <&gpio3>;
+ interrupts = <22 0>;
+ wakeup-gpios = <&gpio3 22 GPIO_ACTIVE_HIGH>;
+ linux,wakeup;
+ };
+};
+
+&iomuxc {
+ imx53-tx53-x13x {
+ pinctrl_i2c2: i2c2-grp1 {
+ fsl,pins = <
+ MX53_PAD_KEY_ROW3__I2C2_SDA 0xc0000000
+ MX53_PAD_KEY_COL3__I2C2_SCL 0xc0000000
+ >;
+ };
+
+ pinctrl_lvds0: lvds0grp {
+ fsl,pins = <
+ MX53_PAD_LVDS0_TX3_P__LDB_LVDS0_TX3 0x80000000
+ MX53_PAD_LVDS0_CLK_P__LDB_LVDS0_CLK 0x80000000
+ MX53_PAD_LVDS0_TX2_P__LDB_LVDS0_TX2 0x80000000
+ MX53_PAD_LVDS0_TX1_P__LDB_LVDS0_TX1 0x80000000
+ MX53_PAD_LVDS0_TX0_P__LDB_LVDS0_TX0 0x80000000
+ >;
+ };
+
+ pinctrl_lvds1: lvds1grp {
+ fsl,pins = <
+ MX53_PAD_LVDS1_TX3_P__LDB_LVDS1_TX3 0x80000000
+ MX53_PAD_LVDS1_TX2_P__LDB_LVDS1_TX2 0x80000000
+ MX53_PAD_LVDS1_CLK_P__LDB_LVDS1_CLK 0x80000000
+ MX53_PAD_LVDS1_TX1_P__LDB_LVDS1_TX1 0x80000000
+ MX53_PAD_LVDS1_TX0_P__LDB_LVDS1_TX0 0x80000000
+ >;
+ };
+
+ pinctrl_pwm1: pwm1grp {
+ fsl,pins = <MX53_PAD_GPIO_9__PWM1_PWMO 0x04>;
+ };
+
+ pinctrl_eeti1: eeti1grp {
+ fsl,pins = <
+ MX53_PAD_EIM_D22__GPIO3_22 0x1f0 /* Interrupt */
+ >;
+ };
+
+ pinctrl_eeti2: eeti2grp {
+ fsl,pins = <
+ MX53_PAD_EIM_D23__GPIO3_23 0x1f0 /* Interrupt */
+ >;
+ };
+ };
+};
+
+&ldb {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_lvds0 &pinctrl_lvds1>;
+ status = "okay";
+
+ lvds0: lvds-channel@0 {
+ fsl,data-mapping = "jeida";
+ fsl,data-width = <24>;
+ status = "okay";
+
+ display-timings {
+ native-mode = <&lvds_timing0>;
+ lvds_timing0: hsd100pxn1 {
+ clock-frequency = <65000000>;
+ hactive = <1024>;
+ vactive = <768>;
+ hback-porch = <220>;
+ hsync-len = <60>;
+ hfront-porch = <40>;
+ vback-porch = <21>;
+ vsync-len = <10>;
+ vfront-porch = <7>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+ };
+ };
+
+ lvds1: lvds-channel@1 {
+ fsl,data-mapping = "jeida";
+ fsl,data-width = <24>;
+ status = "okay";
+
+ display-timings {
+ native-mode = <&lvds_timing1>;
+ lvds_timing1: hsd100pxn1 {
+ clock-frequency = <65000000>;
+ hactive = <1024>;
+ vactive = <768>;
+ hback-porch = <220>;
+ hsync-len = <60>;
+ hfront-porch = <40>;
+ vback-porch = <21>;
+ vsync-len = <10>;
+ vfront-porch = <7>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+ };
+ };
+};
+
+&pwm1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm1>;
+};
+
+&sata {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx53-tx53.dtsi b/arch/arm/boot/dts/imx53-tx53.dtsi
index f494766700a3..e348796ba689 100644
--- a/arch/arm/boot/dts/imx53-tx53.dtsi
+++ b/arch/arm/boot/dts/imx53-tx53.dtsi
@@ -1,122 +1,550 @@
/*
- * Copyright 2013 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ * Copyright 2012 <LW@KARO-electronics.de>
+ * based on imx53-qsb.dts
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
+ * Version 2 at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
-/include/ "imx53.dtsi"
+#include "imx53.dtsi"
+#include <dt-bindings/gpio/gpio.h>
/ {
- model = "Ka-Ro TX53";
+ model = "Ka-Ro electronics TX53 module";
compatible = "karo,tx53", "fsl,imx53";
- memory {
- reg = <0x70000000 0x40000000>; /* Up to 1GiB */
+ aliases {
+ can0 = &can2; /* Make the can interface indices consistent with TX28/TX48 modules */
+ can1 = &can1;
+ ipu = &ipu;
+ reg_can_xcvr = &reg_can_xcvr;
+ usbh1 = &usbh1;
+ usbotg = &usbotg;
+ };
+
+ clocks {
+ ckih1 {
+ clock-frequency = <0>;
+ };
+
+ mclk: clock@0 {
+ compatible = "fixed-clock";
+ reg = <0>;
+ #clock-cells = <0>;
+ clock-frequency = <27000000>;
+ };
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpio_key>;
+
+ power {
+ label = "Power Button";
+ gpios = <&gpio5 2 GPIO_ACTIVE_HIGH>;
+ linux,code = <116>; /* KEY_POWER */
+ gpio-key,wakeup;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_stk5led>;
+
+ user {
+ label = "Heartbeat";
+ gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>;
+ linux,default-trigger = "heartbeat";
+ };
};
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_3p3v: 3p3v {
+ reg_2v5: regulator@0 {
compatible = "regulator-fixed";
- regulator-name = "3P3V";
+ reg = <0>;
+ regulator-name = "2V5";
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+ };
+
+ reg_3v3: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "3V3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
- regulator-always-on;
};
+
+ reg_can_xcvr: regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "CAN XCVR";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_can_xcvr>;
+ gpio = <&gpio4 21 GPIO_ACTIVE_HIGH>;
+ };
+
+ reg_usbh1_vbus: regulator@3 {
+ compatible = "regulator-fixed";
+ reg = <3>;
+ regulator-name = "usbh1_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbh1_vbus>;
+ gpio = <&gpio3 31 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ reg_usbotg_vbus: regulator@4 {
+ compatible = "regulator-fixed";
+ reg = <4>;
+ regulator-name = "usbotg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg_vbus>;
+ gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+ };
+
+ sound {
+ compatible = "karo,tx53-audio-sgtl5000", "fsl,imx-audio-sgtl5000";
+ model = "tx53-audio-sgtl5000";
+ ssi-controller = <&ssi1>;
+ audio-codec = <&sgtl5000>;
+ audio-routing =
+ "MIC_IN", "Mic Jack",
+ "Mic Jack", "Mic Bias",
+ "Headphone Jack", "HP_OUT";
+ /* '1' based port numbers according to datasheet names */
+ mux-int-port = <1>;
+ mux-ext-port = <5>;
};
};
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ssi1>;
+ status = "okay";
+};
+
&can1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_can1_2>;
- status = "disabled";
+ pinctrl-0 = <&pinctrl_can1>;
+ xceiver-supply = <&reg_can_xcvr>;
+ status = "okay";
};
&can2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_can2_1>;
- status = "disabled";
+ pinctrl-0 = <&pinctrl_can2>;
+ xceiver-supply = <&reg_can_xcvr>;
+ status = "okay";
};
&ecspi1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi1_2>;
- status = "disabled";
+ pinctrl-0 = <&pinctrl_ecspi1>;
+ fsl,spi-num-chipselects = <2>;
+ status = "okay";
+
+ cs-gpios = <
+ &gpio2 30 GPIO_ACTIVE_HIGH
+ &gpio3 19 GPIO_ACTIVE_HIGH
+ >;
+
+ spidev0: spi@0 {
+ compatible = "spidev";
+ reg = <0>;
+ spi-max-frequency = <54000000>;
+ };
+
+ spidev1: spi@1 {
+ compatible = "spidev";
+ reg = <1>;
+ spi-max-frequency = <54000000>;
+ };
};
&esdhc1 {
+ cd-gpios = <&gpio3 24 GPIO_ACTIVE_HIGH>;
+ fsl,wp-controller;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc1_2>;
- status = "disabled";
+ pinctrl-0 = <&pinctrl_esdhc1>;
+ status = "okay";
};
&esdhc2 {
+ cd-gpios = <&gpio3 25 GPIO_ACTIVE_HIGH>;
+ fsl,wp-controller;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_esdhc2_1>;
- status = "disabled";
+ pinctrl-0 = <&pinctrl_esdhc2>;
+ status = "okay";
};
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec_1>;
+ pinctrl-0 = <&pinctrl_fec>;
phy-mode = "rmii";
- status = "disabled";
+ phy-reset-gpios = <&gpio7 6 GPIO_ACTIVE_HIGH>;
+ phy-handle = <&phy0>;
+ mac-address = [000000000000]; /* placeholder; will be overwritten by bootloader */
+ status = "okay";
+
+ phy0: ethernet-phy@0 {
+ interrupt-parent = <&gpio2>;
+ interrupts = <4>;
+ device_type = "ethernet-phy";
+ };
};
-&i2c3 {
+&i2c1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c3_2>;
- status = "disabled";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ clock-frequency = <400000>;
+ status = "okay";
+
+ rtc1: ds1339@68 {
+ compatible = "dallas,ds1339";
+ reg = <0x68>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ds1339>;
+ interrupt-parent = <&gpio4>;
+ interrupts = <20 0>;
+ };
};
-&owire {
+&iomuxc {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_owire_1>;
- status = "disabled";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx53-tx53 {
+ pinctrl_hog: hoggrp {
+ /* pins not in use by any device on the Starterkit board series */
+ fsl,pins = <
+ /* CMOS Sensor Interface */
+ MX53_PAD_CSI0_DAT12__GPIO5_30 0x1f4
+ MX53_PAD_CSI0_DAT13__GPIO5_31 0x1f4
+ MX53_PAD_CSI0_DAT14__GPIO6_0 0x1f4
+ MX53_PAD_CSI0_DAT15__GPIO6_1 0x1f4
+ MX53_PAD_CSI0_DAT16__GPIO6_2 0x1f4
+ MX53_PAD_CSI0_DAT17__GPIO6_3 0x1f4
+ MX53_PAD_CSI0_DAT18__GPIO6_4 0x1f4
+ MX53_PAD_CSI0_DAT19__GPIO6_5 0x1f4
+ MX53_PAD_CSI0_MCLK__GPIO5_19 0x1f4
+ MX53_PAD_CSI0_VSYNC__GPIO5_21 0x1f4
+ MX53_PAD_CSI0_PIXCLK__GPIO5_18 0x1f4
+ MX53_PAD_GPIO_0__GPIO1_0 0x1f4
+ /* Module Specific Signal */
+ /* MX53_PAD_NANDF_CS2__GPIO6_15 0x1f4 maybe used by EDT-FT5x06 */
+ /* MX53_PAD_EIM_A16__GPIO2_22 0x1f4 maybe used by EDT-FT5x06 */
+ MX53_PAD_EIM_D29__GPIO3_29 0x1f4
+ MX53_PAD_EIM_EB3__GPIO2_31 0x1f4
+ /* MX53_PAD_EIM_A17__GPIO2_21 0x1f4 maybe used by EDT-FT5x06 */
+ /* MX53_PAD_EIM_A18__GPIO2_20 0x1f4 used by LED */
+ MX53_PAD_EIM_A19__GPIO2_19 0x1f4
+ MX53_PAD_EIM_A20__GPIO2_18 0x1f4
+ MX53_PAD_EIM_A21__GPIO2_17 0x1f4
+ MX53_PAD_EIM_A22__GPIO2_16 0x1f4
+ MX53_PAD_EIM_A23__GPIO6_6 0x1f4
+ MX53_PAD_EIM_A24__GPIO5_4 0x1f4
+ MX53_PAD_CSI0_DAT8__GPIO5_26 0x1f4
+ MX53_PAD_CSI0_DAT9__GPIO5_27 0x1f4
+ MX53_PAD_CSI0_DAT10__GPIO5_28 0x1f4
+ MX53_PAD_CSI0_DAT11__GPIO5_29 0x1f4
+ /* MX53_PAD_EIM_D22__GPIO3_22 0x1f4 maybe used by EETI touchpanel driver */
+ /* MX53_PAD_EIM_D23__GPIO3_23 0x1f4 maybe used by EETI touchpanel driver */
+ MX53_PAD_GPIO_13__GPIO4_3 0x1f4
+ MX53_PAD_EIM_CS0__GPIO2_23 0x1f4
+ MX53_PAD_EIM_CS1__GPIO2_24 0x1f4
+ MX53_PAD_CSI0_DATA_EN__GPIO5_20 0x1f4
+ MX53_PAD_EIM_WAIT__GPIO5_0 0x1f4
+ MX53_PAD_EIM_EB0__GPIO2_28 0x1f4
+ MX53_PAD_EIM_EB1__GPIO2_29 0x1f4
+ MX53_PAD_EIM_OE__GPIO2_25 0x1f4
+ MX53_PAD_EIM_LBA__GPIO2_27 0x1f4
+ MX53_PAD_EIM_RW__GPIO2_26 0x1f4
+ MX53_PAD_EIM_DA8__GPIO3_8 0x1f4
+ MX53_PAD_EIM_DA9__GPIO3_9 0x1f4
+ MX53_PAD_EIM_DA10__GPIO3_10 0x1f4
+ MX53_PAD_EIM_DA11__GPIO3_11 0x1f4
+ MX53_PAD_EIM_DA12__GPIO3_12 0x1f4
+ MX53_PAD_EIM_DA13__GPIO3_13 0x1f4
+ MX53_PAD_EIM_DA14__GPIO3_14 0x1f4
+ MX53_PAD_EIM_DA15__GPIO3_15 0x1f4
+ >;
+ };
+
+ pinctrl_can1: can1grp {
+ fsl,pins = <
+ MX53_PAD_GPIO_7__CAN1_TXCAN 0x80000000
+ MX53_PAD_GPIO_8__CAN1_RXCAN 0x80000000
+ >;
+ };
+
+ pinctrl_can2: can2grp {
+ fsl,pins = <
+ MX53_PAD_KEY_COL4__CAN2_TXCAN 0x80000000
+ MX53_PAD_KEY_ROW4__CAN2_RXCAN 0x80000000
+ >;
+ };
+
+ pinctrl_can_xcvr: can-xcvrgrp {
+ fsl,pins = <MX53_PAD_DISP0_DAT0__GPIO4_21 0xe0>; /* Flexcan XCVR enable */
+ };
+
+ pinctrl_ds1339: ds1339grp {
+ fsl,pins = <MX53_PAD_DI0_PIN4__GPIO4_20 0xe0>;
+ };
+
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX53_PAD_GPIO_19__ECSPI1_RDY 0x80000000
+ MX53_PAD_EIM_EB2__ECSPI1_SS0 0x80000000
+ MX53_PAD_EIM_D16__ECSPI1_SCLK 0x80000000
+ MX53_PAD_EIM_D17__ECSPI1_MISO 0x80000000
+ MX53_PAD_EIM_D18__ECSPI1_MOSI 0x80000000
+ MX53_PAD_EIM_D19__ECSPI1_SS1 0x80000000
+ >;
+ };
+
+ pinctrl_esdhc1: esdhc1grp {
+ fsl,pins = <
+ MX53_PAD_SD1_DATA0__ESDHC1_DAT0 0x1d5
+ MX53_PAD_SD1_DATA1__ESDHC1_DAT1 0x1d5
+ MX53_PAD_SD1_DATA2__ESDHC1_DAT2 0x1d5
+ MX53_PAD_SD1_DATA3__ESDHC1_DAT3 0x1d5
+ MX53_PAD_SD1_CMD__ESDHC1_CMD 0x1d5
+ MX53_PAD_SD1_CLK__ESDHC1_CLK 0x1d5
+ MX53_PAD_EIM_D24__GPIO3_24 0x1f0
+ >;
+ };
+
+ pinctrl_esdhc2: esdhc2grp {
+ fsl,pins = <
+ MX53_PAD_SD2_CMD__ESDHC2_CMD 0x1d5
+ MX53_PAD_SD2_CLK__ESDHC2_CLK 0x1d5
+ MX53_PAD_SD2_DATA0__ESDHC2_DAT0 0x1d5
+ MX53_PAD_SD2_DATA1__ESDHC2_DAT1 0x1d5
+ MX53_PAD_SD2_DATA2__ESDHC2_DAT2 0x1d5
+ MX53_PAD_SD2_DATA3__ESDHC2_DAT3 0x1d5
+ MX53_PAD_EIM_D25__GPIO3_25 0x1f0
+ >;
+ };
+
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX53_PAD_FEC_MDC__FEC_MDC 0x80000000
+ MX53_PAD_FEC_MDIO__FEC_MDIO 0x80000000
+ MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x80000000
+ MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x80000000
+ MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x80000000
+ MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x80000000
+ MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x80000000
+ MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000
+ MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x80000000
+ MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x80000000
+ >;
+ };
+
+ pinctrl_gpio_key: gpio-keygrp {
+ fsl,pins = <MX53_PAD_EIM_A25__GPIO5_2 0x1f4>;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX53_PAD_EIM_D21__I2C1_SCL 0xc0000000
+ MX53_PAD_EIM_D28__I2C1_SDA 0xc0000000
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX53_PAD_GPIO_3__I2C3_SCL 0xc0000000
+ MX53_PAD_GPIO_6__I2C3_SDA 0xc0000000
+ >;
+ };
+
+ pinctrl_nand: nandgrp {
+ fsl,pins = <
+ MX53_PAD_NANDF_WE_B__EMI_NANDF_WE_B 0x4
+ MX53_PAD_NANDF_RE_B__EMI_NANDF_RE_B 0x4
+ MX53_PAD_NANDF_CLE__EMI_NANDF_CLE 0x4
+ MX53_PAD_NANDF_ALE__EMI_NANDF_ALE 0x4
+ MX53_PAD_NANDF_WP_B__EMI_NANDF_WP_B 0xe0
+ MX53_PAD_NANDF_RB0__EMI_NANDF_RB_0 0xe0
+ MX53_PAD_NANDF_CS0__EMI_NANDF_CS_0 0x4
+ MX53_PAD_EIM_DA0__EMI_NAND_WEIM_DA_0 0xa4
+ MX53_PAD_EIM_DA1__EMI_NAND_WEIM_DA_1 0xa4
+ MX53_PAD_EIM_DA2__EMI_NAND_WEIM_DA_2 0xa4
+ MX53_PAD_EIM_DA3__EMI_NAND_WEIM_DA_3 0xa4
+ MX53_PAD_EIM_DA4__EMI_NAND_WEIM_DA_4 0xa4
+ MX53_PAD_EIM_DA5__EMI_NAND_WEIM_DA_5 0xa4
+ MX53_PAD_EIM_DA6__EMI_NAND_WEIM_DA_6 0xa4
+ MX53_PAD_EIM_DA7__EMI_NAND_WEIM_DA_7 0xa4
+ >;
+ };
+
+ pinctrl_pwm2: pwm2grp {
+ fsl,pins = <
+ MX53_PAD_GPIO_1__PWM2_PWMO 0x80000000
+ >;
+ };
+
+ pinctrl_ssi1: ssi1grp {
+ fsl,pins = <
+ MX53_PAD_KEY_COL0__AUDMUX_AUD5_TXC 0x80000000
+ MX53_PAD_KEY_ROW0__AUDMUX_AUD5_TXD 0x80000000
+ MX53_PAD_KEY_COL1__AUDMUX_AUD5_TXFS 0x80000000
+ MX53_PAD_KEY_ROW1__AUDMUX_AUD5_RXD 0x80000000
+ >;
+ };
+
+ pinctrl_ssi2: ssi2grp {
+ fsl,pins = <
+ MX53_PAD_CSI0_DAT4__AUDMUX_AUD3_TXC 0x80000000
+ MX53_PAD_CSI0_DAT5__AUDMUX_AUD3_TXD 0x80000000
+ MX53_PAD_CSI0_DAT6__AUDMUX_AUD3_TXFS 0x80000000
+ MX53_PAD_CSI0_DAT7__AUDMUX_AUD3_RXD 0x80000000
+ MX53_PAD_EIM_D27__GPIO3_27 0x1f0
+ >;
+ };
+
+ pinctrl_stk5led: stk5ledgrp {
+ fsl,pins = <MX53_PAD_EIM_A18__GPIO2_20 0xc0>;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX53_PAD_PATA_DIOW__UART1_TXD_MUX 0x1e4
+ MX53_PAD_PATA_DMACK__UART1_RXD_MUX 0x1e4
+ MX53_PAD_PATA_RESET_B__UART1_CTS 0x1c5
+ MX53_PAD_PATA_IORDY__UART1_RTS 0x1c5
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX53_PAD_PATA_BUFFER_EN__UART2_RXD_MUX 0x1c5
+ MX53_PAD_PATA_DMARQ__UART2_TXD_MUX 0x1c5
+ MX53_PAD_PATA_DIOR__UART2_RTS 0x1c5
+ MX53_PAD_PATA_INTRQ__UART2_CTS 0x1c5
+ >;
+ };
+
+ pinctrl_uart3: uart3grp {
+ fsl,pins = <
+ MX53_PAD_PATA_CS_0__UART3_TXD_MUX 0x1e4
+ MX53_PAD_PATA_CS_1__UART3_RXD_MUX 0x1e4
+ MX53_PAD_PATA_DA_1__UART3_CTS 0x1e4
+ MX53_PAD_PATA_DA_2__UART3_RTS 0x1e4
+ >;
+ };
+
+ pinctrl_usbh1: usbh1grp {
+ fsl,pins = <
+ MX53_PAD_EIM_D30__GPIO3_30 0x100 /* OC */
+ >;
+ };
+
+ pinctrl_usbh1_vbus: usbh1-vbusgrp {
+ fsl,pins = <
+ MX53_PAD_EIM_D31__GPIO3_31 0xe0 /* VBUS ENABLE */
+ >;
+ };
+
+ pinctrl_usbotg_vbus: usbotg-vbusgrp {
+ fsl,pins = <
+ MX53_PAD_GPIO_7__GPIO1_7 0xe0 /* VBUS ENABLE */
+ MX53_PAD_GPIO_8__GPIO1_8 0x100 /* OC */
+ >;
+ };
+ };
+};
+
+&ipu {
+ status = "okay";
+};
+
+&nfc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_nand>;
+ nand-bus-width = <8>;
+ nand-ecc-mode = "hw";
+ nand-on-flash-bbt;
+ status = "okay";
};
&pwm2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_pwm2_1>;
- status = "disabled";
+ pinctrl-0 = <&pinctrl_pwm2>;
+ #pwm-cells = <3>;
+};
+
+&sdma {
+ fsl,sdma-ram-script-name = "sdma-imx53.bin";
};
&ssi1 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_audmux_1>;
- status = "disabled";
+ fsl,mode = "i2s-slave";
+ codec-handle = <&sgtl5000>;
+ status = "okay";
};
&ssi2 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_audmux_2>;
status = "disabled";
};
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_2>,
- <&pinctrl_uart1_3>;
+ pinctrl-0 = <&pinctrl_uart1>;
fsl,uart-has-rtscts;
- status = "disabled";
+ status = "okay";
};
&uart2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart2_2>;
+ pinctrl-0 = <&pinctrl_uart2>;
fsl,uart-has-rtscts;
- status = "disabled";
+ status = "okay";
};
&uart3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart3_1>;
+ pinctrl-0 = <&pinctrl_uart3>;
fsl,uart-has-rtscts;
- status = "disabled";
+ status = "okay";
+};
+
+&usbh1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbh1>;
+ phy_type = "utmi";
+ disable-over-current;
+ vbus-supply = <&reg_usbh1_vbus>;
+ status = "okay";
+};
+
+&usbotg {
+ phy_type = "utmi";
+ dr_mode = "peripheral";
+ disable-over-current;
+ vbus-supply = <&reg_usbotg_vbus>;
+ status = "okay";
};
diff --git a/arch/arm/boot/dts/imx53-voipac-bsb.dts b/arch/arm/boot/dts/imx53-voipac-bsb.dts
new file mode 100644
index 000000000000..7f6711a48615
--- /dev/null
+++ b/arch/arm/boot/dts/imx53-voipac-bsb.dts
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2013 Rostislav Lisovy <lisovy@gmail.com>, PiKRON s.r.o.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx53-voipac-dmm-668.dtsi"
+
+/ {
+ sound {
+ compatible = "fsl,imx53-voipac-sgtl5000",
+ "fsl,imx-audio-sgtl5000";
+ model = "imx53-voipac-sgtl5000";
+ ssi-controller = <&ssi2>;
+ audio-codec = <&sgtl5000>;
+ audio-routing =
+ "Headphone Jack", "HP_OUT";
+ mux-int-port = <2>;
+ mux-ext-port = <5>;
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pin_gpio>;
+
+ led1 {
+ label = "led-red";
+ gpios = <&gpio3 29 0>;
+ default-state = "off";
+ };
+
+ led2 {
+ label = "led-orange";
+ gpios = <&gpio2 31 0>;
+ default-state = "off";
+ };
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx53-voipac {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ /* SD2_CD */
+ MX53_PAD_EIM_D25__GPIO3_25 0x80000000
+ /* SD2_WP */
+ MX53_PAD_EIM_A19__GPIO2_19 0x80000000
+ >;
+ };
+
+ led_pin_gpio: led_gpio {
+ fsl,pins = <
+ MX53_PAD_EIM_D29__GPIO3_29 0x80000000
+ MX53_PAD_EIM_EB3__GPIO2_31 0x80000000
+ >;
+ };
+
+ /* Keyboard controller */
+ pinctrl_kpp_1: kppgrp-1 {
+ fsl,pins = <
+ MX53_PAD_GPIO_9__KPP_COL_6 0xe8
+ MX53_PAD_GPIO_4__KPP_COL_7 0xe8
+ MX53_PAD_KEY_COL2__KPP_COL_2 0xe8
+ MX53_PAD_KEY_COL3__KPP_COL_3 0xe8
+ MX53_PAD_KEY_COL4__KPP_COL_4 0xe8
+ MX53_PAD_GPIO_2__KPP_ROW_6 0xe0
+ MX53_PAD_GPIO_5__KPP_ROW_7 0xe0
+ MX53_PAD_KEY_ROW2__KPP_ROW_2 0xe0
+ MX53_PAD_KEY_ROW3__KPP_ROW_3 0xe0
+ MX53_PAD_KEY_ROW4__KPP_ROW_4 0xe0
+ >;
+ };
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX53_PAD_KEY_COL0__AUDMUX_AUD5_TXC 0x80000000
+ MX53_PAD_KEY_ROW0__AUDMUX_AUD5_TXD 0x80000000
+ MX53_PAD_KEY_COL1__AUDMUX_AUD5_TXFS 0x80000000
+ MX53_PAD_KEY_ROW1__AUDMUX_AUD5_RXD 0x80000000
+ >;
+ };
+
+ pinctrl_esdhc2: esdhc2grp {
+ fsl,pins = <
+ MX53_PAD_SD2_CMD__ESDHC2_CMD 0x1d5
+ MX53_PAD_SD2_CLK__ESDHC2_CLK 0x1d5
+ MX53_PAD_SD2_DATA0__ESDHC2_DAT0 0x1d5
+ MX53_PAD_SD2_DATA1__ESDHC2_DAT1 0x1d5
+ MX53_PAD_SD2_DATA2__ESDHC2_DAT2 0x1d5
+ MX53_PAD_SD2_DATA3__ESDHC2_DAT3 0x1d5
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX53_PAD_GPIO_3__I2C3_SCL 0xc0000000
+ MX53_PAD_GPIO_6__I2C3_SDA 0xc0000000
+ >;
+ };
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>; /* SSI1 */
+ status = "okay";
+};
+
+&esdhc2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_esdhc2>;
+ cd-gpios = <&gpio3 25 0>;
+ wp-gpios = <&gpio2 19 0>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
+
+&i2c3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3>;
+ status = "okay";
+
+ sgtl5000: codec@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ VDDA-supply = <&reg_3p3v>;
+ VDDIO-supply = <&reg_3p3v>;
+ clocks = <&clks 150>;
+ };
+};
+
+&kpp {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_kpp_1>;
+ linux,keymap = <
+ 0x0203003b /* KEY_F1 */
+ 0x0603003c /* KEY_F2 */
+ 0x0207003d /* KEY_F3 */
+ 0x0607003e /* KEY_F4 */
+ >;
+ keypad,num-rows = <8>;
+ keypad,num-columns = <1>;
+ status = "okay";
+};
+
+&ssi2 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx53-voipac-dmm-668.dtsi b/arch/arm/boot/dts/imx53-voipac-dmm-668.dtsi
new file mode 100644
index 000000000000..ba689fbd0e41
--- /dev/null
+++ b/arch/arm/boot/dts/imx53-voipac-dmm-668.dtsi
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2013 Rostislav Lisovy <lisovy@gmail.com>, PiKRON s.r.o.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include "imx53.dtsi"
+
+/ {
+ model = "Voipac i.MX53 X53-DMM-668";
+ compatible = "voipac,imx53-dmm-668", "fsl,imx53";
+
+ memory@70000000 {
+ device_type = "memory";
+ reg = <0x70000000 0x20000000>;
+ };
+
+ memory@b0000000 {
+ device_type = "memory";
+ reg = <0xb0000000 0x20000000>;
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_3p3v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_usb_vbus: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "usb_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio3 31 0>; /* PEN */
+ enable-active-high;
+ };
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx53-voipac {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ /* Make DA9053 regulator functional */
+ MX53_PAD_GPIO_16__GPIO7_11 0x80000000
+ /* FEC Power enable */
+ MX53_PAD_GPIO_11__GPIO4_1 0x80000000
+ /* FEC RST */
+ MX53_PAD_GPIO_12__GPIO4_2 0x80000000
+ >;
+ };
+
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX53_PAD_EIM_D16__ECSPI1_SCLK 0x80000000
+ MX53_PAD_EIM_D17__ECSPI1_MISO 0x80000000
+ MX53_PAD_EIM_D18__ECSPI1_MOSI 0x80000000
+ >;
+ };
+
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX53_PAD_FEC_MDC__FEC_MDC 0x80000000
+ MX53_PAD_FEC_MDIO__FEC_MDIO 0x80000000
+ MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x80000000
+ MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x80000000
+ MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x80000000
+ MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x80000000
+ MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x80000000
+ MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000
+ MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x80000000
+ MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x80000000
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX53_PAD_EIM_D21__I2C1_SCL 0xc0000000
+ MX53_PAD_EIM_D28__I2C1_SDA 0xc0000000
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX53_PAD_PATA_DIOW__UART1_TXD_MUX 0x1e4
+ MX53_PAD_PATA_DMACK__UART1_RXD_MUX 0x1e4
+ >;
+ };
+
+ pinctrl_nand: nandgrp {
+ fsl,pins = <
+ MX53_PAD_NANDF_WE_B__EMI_NANDF_WE_B 0x4
+ MX53_PAD_NANDF_RE_B__EMI_NANDF_RE_B 0x4
+ MX53_PAD_NANDF_CLE__EMI_NANDF_CLE 0x4
+ MX53_PAD_NANDF_ALE__EMI_NANDF_ALE 0x4
+ MX53_PAD_NANDF_WP_B__EMI_NANDF_WP_B 0xe0
+ MX53_PAD_NANDF_RB0__EMI_NANDF_RB_0 0xe0
+ MX53_PAD_NANDF_CS0__EMI_NANDF_CS_0 0x4
+ MX53_PAD_PATA_DATA0__EMI_NANDF_D_0 0xa4
+ MX53_PAD_PATA_DATA1__EMI_NANDF_D_1 0xa4
+ MX53_PAD_PATA_DATA2__EMI_NANDF_D_2 0xa4
+ MX53_PAD_PATA_DATA3__EMI_NANDF_D_3 0xa4
+ MX53_PAD_PATA_DATA4__EMI_NANDF_D_4 0xa4
+ MX53_PAD_PATA_DATA5__EMI_NANDF_D_5 0xa4
+ MX53_PAD_PATA_DATA6__EMI_NANDF_D_6 0xa4
+ MX53_PAD_PATA_DATA7__EMI_NANDF_D_7 0xa4
+ >;
+ };
+ };
+};
+
+&ecspi1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ecspi1>;
+ fsl,spi-num-chipselects = <4>;
+ cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>, <&gpio2 16 0>, <&gpio2 17 0>;
+ status = "okay";
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fec>;
+ phy-mode = "rmii";
+ phy-reset-gpios = <&gpio4 2 0>;
+ status = "okay";
+};
+
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ pmic: dialog@48 {
+ compatible = "dlg,da9053-aa", "dlg,da9052";
+ reg = <0x48>;
+ interrupt-parent = <&gpio7>;
+ interrupts = <11 0x8>; /* low-level active IRQ at GPIO7_11 */
+
+ regulators {
+ buck1_reg: buck1 {
+ regulator-name = "BUCKCORE";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-always-on;
+ };
+
+ buck2_reg: buck2 {
+ regulator-name = "BUCKPRO";
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ };
+
+ buck3_reg: buck3 {
+ regulator-name = "BUCKMEM";
+ regulator-min-microvolt = <1420000>;
+ regulator-max-microvolt = <1580000>;
+ regulator-always-on;
+ };
+
+ buck4_reg: buck4 {
+ regulator-name = "BUCKPERI";
+ regulator-min-microvolt = <2370000>;
+ regulator-max-microvolt = <2630000>;
+ regulator-always-on;
+ };
+
+ ldo1_reg: ldo1 {
+ regulator-name = "ldo1_1v3";
+ regulator-min-microvolt = <1250000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ ldo2_reg: ldo2 {
+ regulator-name = "ldo2_1v3";
+ regulator-min-microvolt = <1250000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ };
+
+ ldo3_reg: ldo3 {
+ regulator-name = "ldo3_3v3";
+ regulator-min-microvolt = <3250000>;
+ regulator-max-microvolt = <3350000>;
+ regulator-always-on;
+ };
+
+ ldo4_reg: ldo4 {
+ regulator-name = "ldo4_2v775";
+ regulator-min-microvolt = <2770000>;
+ regulator-max-microvolt = <2780000>;
+ regulator-always-on;
+ };
+
+ ldo5_reg: ldo5 {
+ regulator-name = "ldo5_3v3";
+ regulator-min-microvolt = <3250000>;
+ regulator-max-microvolt = <3350000>;
+ regulator-always-on;
+ };
+
+ ldo6_reg: ldo6 {
+ regulator-name = "ldo6_1v3";
+ regulator-min-microvolt = <1250000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ };
+
+ ldo7_reg: ldo7 {
+ regulator-name = "ldo7_2v75";
+ regulator-min-microvolt = <2700000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+ };
+
+ ldo8_reg: ldo8 {
+ regulator-name = "ldo8_1v8";
+ regulator-min-microvolt = <1750000>;
+ regulator-max-microvolt = <1850000>;
+ regulator-always-on;
+ };
+
+ ldo9_reg: ldo9 {
+ regulator-name = "ldo9_1v5";
+ regulator-min-microvolt = <1450000>;
+ regulator-max-microvolt = <1550000>;
+ regulator-always-on;
+ };
+
+ ldo10_reg: ldo10 {
+ regulator-name = "ldo10_1v3";
+ regulator-min-microvolt = <1250000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ };
+ };
+ };
+};
+
+&nfc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_nand>;
+ nand-bus-width = <8>;
+ nand-ecc-mode = "hw";
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&usbh1 {
+ vbus-supply = <&reg_usb_vbus>;
+ phy_type = "utmi";
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi
index 04d3127edfe1..b57ab57740f6 100644
--- a/arch/arm/boot/dts/imx53.dtsi
+++ b/arch/arm/boot/dts/imx53.dtsi
@@ -12,6 +12,9 @@
#include "skeleton.dtsi"
#include "imx53-pinfunc.h"
+#include <dt-bindings/clock/imx5-clock.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
/ {
aliases {
@@ -25,6 +28,10 @@
i2c0 = &i2c1;
i2c1 = &i2c2;
i2c2 = &i2c3;
+ mmc0 = &esdhc1;
+ mmc1 = &esdhc2;
+ mmc2 = &esdhc3;
+ mmc3 = &esdhc4;
serial0 = &uart1;
serial1 = &uart2;
serial2 = &uart3;
@@ -89,13 +96,26 @@
interrupt-parent = <&tzic>;
ranges;
+ sata: sata@10000000 {
+ compatible = "fsl,imx53-ahci";
+ reg = <0x10000000 0x1000>;
+ interrupts = <28>;
+ clocks = <&clks IMX5_CLK_SATA_GATE>,
+ <&clks IMX5_CLK_SATA_REF>,
+ <&clks IMX5_CLK_AHB>;
+ clock-names = "sata_gate", "sata_ref", "ahb";
+ status = "disabled";
+ };
+
ipu: ipu@18000000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx53-ipu";
reg = <0x18000000 0x080000000>;
interrupts = <11 10>;
- clocks = <&clks 59>, <&clks 110>, <&clks 61>;
+ clocks = <&clks IMX5_CLK_IPU_GATE>,
+ <&clks IMX5_CLK_IPU_DI0_GATE>,
+ <&clks IMX5_CLK_IPU_DI1_GATE>;
clock-names = "bus", "di0", "di1";
resets = <&src 2>;
@@ -153,7 +173,9 @@
compatible = "fsl,imx53-esdhc";
reg = <0x50004000 0x4000>;
interrupts = <1>;
- clocks = <&clks 44>, <&clks 0>, <&clks 71>;
+ clocks = <&clks IMX5_CLK_ESDHC1_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC1_PER_GATE>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
status = "disabled";
@@ -163,7 +185,9 @@
compatible = "fsl,imx53-esdhc";
reg = <0x50008000 0x4000>;
interrupts = <2>;
- clocks = <&clks 45>, <&clks 0>, <&clks 72>;
+ clocks = <&clks IMX5_CLK_ESDHC2_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC2_PER_GATE>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
status = "disabled";
@@ -173,7 +197,8 @@
compatible = "fsl,imx53-uart", "fsl,imx21-uart";
reg = <0x5000c000 0x4000>;
interrupts = <33>;
- clocks = <&clks 32>, <&clks 33>;
+ clocks = <&clks IMX5_CLK_UART3_IPG_GATE>,
+ <&clks IMX5_CLK_UART3_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -184,16 +209,19 @@
compatible = "fsl,imx53-ecspi", "fsl,imx51-ecspi";
reg = <0x50010000 0x4000>;
interrupts = <36>;
- clocks = <&clks 51>, <&clks 52>;
+ clocks = <&clks IMX5_CLK_ECSPI1_IPG_GATE>,
+ <&clks IMX5_CLK_ECSPI1_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
ssi2: ssi@50014000 {
- compatible = "fsl,imx53-ssi", "fsl,imx21-ssi";
+ compatible = "fsl,imx53-ssi",
+ "fsl,imx51-ssi",
+ "fsl,imx21-ssi";
reg = <0x50014000 0x4000>;
interrupts = <30>;
- clocks = <&clks 49>;
+ clocks = <&clks IMX5_CLK_SSI2_IPG_GATE>;
dmas = <&sdma 24 1 0>,
<&sdma 25 1 0>;
dma-names = "rx", "tx";
@@ -206,7 +234,9 @@
compatible = "fsl,imx53-esdhc";
reg = <0x50020000 0x4000>;
interrupts = <3>;
- clocks = <&clks 46>, <&clks 0>, <&clks 73>;
+ clocks = <&clks IMX5_CLK_ESDHC3_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC3_PER_GATE>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
status = "disabled";
@@ -216,7 +246,9 @@
compatible = "fsl,imx53-esdhc";
reg = <0x50024000 0x4000>;
interrupts = <4>;
- clocks = <&clks 47>, <&clks 0>, <&clks 74>;
+ clocks = <&clks IMX5_CLK_ESDHC4_IPG_GATE>,
+ <&clks IMX5_CLK_DUMMY>,
+ <&clks IMX5_CLK_ESDHC4_PER_GATE>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
status = "disabled";
@@ -225,14 +257,14 @@
usbphy0: usbphy@0 {
compatible = "usb-nop-xceiv";
- clocks = <&clks 124>;
+ clocks = <&clks IMX5_CLK_USB_PHY1_GATE>;
clock-names = "main_clk";
status = "okay";
};
usbphy1: usbphy@1 {
compatible = "usb-nop-xceiv";
- clocks = <&clks 125>;
+ clocks = <&clks IMX5_CLK_USB_PHY2_GATE>;
clock-names = "main_clk";
status = "okay";
};
@@ -241,7 +273,7 @@
compatible = "fsl,imx53-usb", "fsl,imx27-usb";
reg = <0x53f80000 0x0200>;
interrupts = <18>;
- clocks = <&clks 108>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
fsl,usbmisc = <&usbmisc 0>;
fsl,usbphy = <&usbphy0>;
status = "disabled";
@@ -251,7 +283,7 @@
compatible = "fsl,imx53-usb", "fsl,imx27-usb";
reg = <0x53f80200 0x0200>;
interrupts = <14>;
- clocks = <&clks 108>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
fsl,usbmisc = <&usbmisc 1>;
fsl,usbphy = <&usbphy1>;
status = "disabled";
@@ -261,7 +293,7 @@
compatible = "fsl,imx53-usb", "fsl,imx27-usb";
reg = <0x53f80400 0x0200>;
interrupts = <16>;
- clocks = <&clks 108>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
fsl,usbmisc = <&usbmisc 2>;
status = "disabled";
};
@@ -270,7 +302,7 @@
compatible = "fsl,imx53-usb", "fsl,imx27-usb";
reg = <0x53f80600 0x0200>;
interrupts = <17>;
- clocks = <&clks 108>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
fsl,usbmisc = <&usbmisc 3>;
status = "disabled";
};
@@ -279,7 +311,7 @@
#index-cells = <1>;
compatible = "fsl,imx53-usbmisc";
reg = <0x53f80800 0x200>;
- clocks = <&clks 108>;
+ clocks = <&clks IMX5_CLK_USBOH3_GATE>;
};
gpio1: gpio@53f84000 {
@@ -322,18 +354,26 @@
#interrupt-cells = <2>;
};
+ kpp: kpp@53f94000 {
+ compatible = "fsl,imx53-kpp", "fsl,imx21-kpp";
+ reg = <0x53f94000 0x4000>;
+ interrupts = <60>;
+ clocks = <&clks IMX5_CLK_DUMMY>;
+ status = "disabled";
+ };
+
wdog1: wdog@53f98000 {
compatible = "fsl,imx53-wdt", "fsl,imx21-wdt";
reg = <0x53f98000 0x4000>;
interrupts = <58>;
- clocks = <&clks 0>;
+ clocks = <&clks IMX5_CLK_DUMMY>;
};
wdog2: wdog@53f9c000 {
compatible = "fsl,imx53-wdt", "fsl,imx21-wdt";
reg = <0x53f9c000 0x4000>;
interrupts = <59>;
- clocks = <&clks 0>;
+ clocks = <&clks IMX5_CLK_DUMMY>;
status = "disabled";
};
@@ -341,521 +381,14 @@
compatible = "fsl,imx53-gpt", "fsl,imx31-gpt";
reg = <0x53fa0000 0x4000>;
interrupts = <39>;
- clocks = <&clks 36>, <&clks 41>;
+ clocks = <&clks IMX5_CLK_GPT_IPG_GATE>,
+ <&clks IMX5_CLK_GPT_HF_GATE>;
clock-names = "ipg", "per";
};
iomuxc: iomuxc@53fa8000 {
compatible = "fsl,imx53-iomuxc";
reg = <0x53fa8000 0x4000>;
-
- audmux {
- pinctrl_audmux_1: audmuxgrp-1 {
- fsl,pins = <
- MX53_PAD_KEY_COL0__AUDMUX_AUD5_TXC 0x80000000
- MX53_PAD_KEY_ROW0__AUDMUX_AUD5_TXD 0x80000000
- MX53_PAD_KEY_COL1__AUDMUX_AUD5_TXFS 0x80000000
- MX53_PAD_KEY_ROW1__AUDMUX_AUD5_RXD 0x80000000
- >;
- };
-
- pinctrl_audmux_2: audmuxgrp-2 {
- fsl,pins = <
- MX53_PAD_SD2_DATA3__AUDMUX_AUD4_TXC 0x80000000
- MX53_PAD_SD2_DATA2__AUDMUX_AUD4_TXD 0x80000000
- MX53_PAD_SD2_DATA1__AUDMUX_AUD4_TXFS 0x80000000
- MX53_PAD_SD2_DATA0__AUDMUX_AUD4_RXD 0x80000000
- >;
- };
-
- pinctrl_audmux_3: audmuxgrp-3 {
- fsl,pins = <
- MX53_PAD_CSI0_DAT4__AUDMUX_AUD3_TXC 0x80000000
- MX53_PAD_CSI0_DAT5__AUDMUX_AUD3_TXD 0x80000000
- MX53_PAD_CSI0_DAT6__AUDMUX_AUD3_TXFS 0x80000000
- MX53_PAD_CSI0_DAT7__AUDMUX_AUD3_RXD 0x80000000
- >;
- };
- };
-
- fec {
- pinctrl_fec_1: fecgrp-1 {
- fsl,pins = <
- MX53_PAD_FEC_MDC__FEC_MDC 0x80000000
- MX53_PAD_FEC_MDIO__FEC_MDIO 0x80000000
- MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x80000000
- MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x80000000
- MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x80000000
- MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x80000000
- MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x80000000
- MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000
- MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x80000000
- MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x80000000
- >;
- };
-
- pinctrl_fec_2: fecgrp-2 {
- fsl,pins = <
- MX53_PAD_FEC_MDC__FEC_MDC 0x80000000
- MX53_PAD_FEC_MDIO__FEC_MDIO 0x80000000
- MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x80000000
- MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x80000000
- MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x80000000
- MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x80000000
- MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x80000000
- MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000
- MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x80000000
- MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x80000000
- MX53_PAD_KEY_ROW1__FEC_COL 0x80000000
- MX53_PAD_KEY_COL3__FEC_CRS 0x80000000
- MX53_PAD_KEY_COL2__FEC_RDATA_2 0x80000000
- MX53_PAD_KEY_COL0__FEC_RDATA_3 0x80000000
- MX53_PAD_KEY_COL1__FEC_RX_CLK 0x80000000
- MX53_PAD_KEY_ROW2__FEC_TDATA_2 0x80000000
- MX53_PAD_GPIO_19__FEC_TDATA_3 0x80000000
- MX53_PAD_KEY_ROW0__FEC_TX_ER 0x80000000
- >;
- };
- };
-
- csi {
- pinctrl_csi_1: csigrp-1 {
- fsl,pins = <
- MX53_PAD_CSI0_DATA_EN__IPU_CSI0_DATA_EN 0x1d5
- MX53_PAD_CSI0_VSYNC__IPU_CSI0_VSYNC 0x1d5
- MX53_PAD_CSI0_MCLK__IPU_CSI0_HSYNC 0x1d5
- MX53_PAD_CSI0_PIXCLK__IPU_CSI0_PIXCLK 0x1d5
- MX53_PAD_CSI0_DAT19__IPU_CSI0_D_19 0x1d5
- MX53_PAD_CSI0_DAT18__IPU_CSI0_D_18 0x1d5
- MX53_PAD_CSI0_DAT17__IPU_CSI0_D_17 0x1d5
- MX53_PAD_CSI0_DAT16__IPU_CSI0_D_16 0x1d5
- MX53_PAD_CSI0_DAT15__IPU_CSI0_D_15 0x1d5
- MX53_PAD_CSI0_DAT14__IPU_CSI0_D_14 0x1d5
- MX53_PAD_CSI0_DAT13__IPU_CSI0_D_13 0x1d5
- MX53_PAD_CSI0_DAT12__IPU_CSI0_D_12 0x1d5
- MX53_PAD_CSI0_DAT11__IPU_CSI0_D_11 0x1d5
- MX53_PAD_CSI0_DAT10__IPU_CSI0_D_10 0x1d5
- MX53_PAD_CSI0_DAT9__IPU_CSI0_D_9 0x1d5
- MX53_PAD_CSI0_DAT8__IPU_CSI0_D_8 0x1d5
- MX53_PAD_CSI0_DAT7__IPU_CSI0_D_7 0x1d5
- MX53_PAD_CSI0_DAT6__IPU_CSI0_D_6 0x1d5
- MX53_PAD_CSI0_DAT5__IPU_CSI0_D_5 0x1d5
- MX53_PAD_CSI0_DAT4__IPU_CSI0_D_4 0x1d5
- MX53_PAD_CSI0_PIXCLK__IPU_CSI0_PIXCLK 0x1d5
- >;
- };
-
- pinctrl_csi_2: csigrp-2 {
- fsl,pins = <
- MX53_PAD_CSI0_VSYNC__IPU_CSI0_VSYNC 0x1d5
- MX53_PAD_CSI0_MCLK__IPU_CSI0_HSYNC 0x1d5
- MX53_PAD_CSI0_PIXCLK__IPU_CSI0_PIXCLK 0x1d5
- MX53_PAD_CSI0_DAT19__IPU_CSI0_D_19 0x1d5
- MX53_PAD_CSI0_DAT18__IPU_CSI0_D_18 0x1d5
- MX53_PAD_CSI0_DAT17__IPU_CSI0_D_17 0x1d5
- MX53_PAD_CSI0_DAT16__IPU_CSI0_D_16 0x1d5
- MX53_PAD_CSI0_DAT15__IPU_CSI0_D_15 0x1d5
- MX53_PAD_CSI0_DAT14__IPU_CSI0_D_14 0x1d5
- MX53_PAD_CSI0_DAT13__IPU_CSI0_D_13 0x1d5
- MX53_PAD_CSI0_DAT12__IPU_CSI0_D_12 0x1d5
- >;
- };
- };
-
- cspi {
- pinctrl_cspi_1: cspigrp-1 {
- fsl,pins = <
- MX53_PAD_SD1_DATA0__CSPI_MISO 0x1d5
- MX53_PAD_SD1_CMD__CSPI_MOSI 0x1d5
- MX53_PAD_SD1_CLK__CSPI_SCLK 0x1d5
- >;
- };
-
- pinctrl_cspi_2: cspigrp-2 {
- fsl,pins = <
- MX53_PAD_EIM_D22__CSPI_MISO 0x1d5
- MX53_PAD_EIM_D28__CSPI_MOSI 0x1d5
- MX53_PAD_EIM_D21__CSPI_SCLK 0x1d5
- >;
- };
- };
-
- ecspi1 {
- pinctrl_ecspi1_1: ecspi1grp-1 {
- fsl,pins = <
- MX53_PAD_EIM_D16__ECSPI1_SCLK 0x80000000
- MX53_PAD_EIM_D17__ECSPI1_MISO 0x80000000
- MX53_PAD_EIM_D18__ECSPI1_MOSI 0x80000000
- >;
- };
-
- pinctrl_ecspi1_2: ecspi1grp-2 {
- fsl,pins = <
- MX53_PAD_GPIO_19__ECSPI1_RDY 0x80000000
- MX53_PAD_EIM_EB2__ECSPI1_SS0 0x80000000
- MX53_PAD_EIM_D16__ECSPI1_SCLK 0x80000000
- MX53_PAD_EIM_D17__ECSPI1_MISO 0x80000000
- MX53_PAD_EIM_D18__ECSPI1_MOSI 0x80000000
- MX53_PAD_EIM_D19__ECSPI1_SS1 0x80000000
- >;
- };
- };
-
- ecspi2 {
- pinctrl_ecspi2_1: ecspi2grp-1 {
- fsl,pins = <
- MX53_PAD_EIM_OE__ECSPI2_MISO 0x80000000
- MX53_PAD_EIM_CS1__ECSPI2_MOSI 0x80000000
- MX53_PAD_EIM_CS0__ECSPI2_SCLK 0x80000000
- >;
- };
- };
-
- esdhc1 {
- pinctrl_esdhc1_1: esdhc1grp-1 {
- fsl,pins = <
- MX53_PAD_SD1_DATA0__ESDHC1_DAT0 0x1d5
- MX53_PAD_SD1_DATA1__ESDHC1_DAT1 0x1d5
- MX53_PAD_SD1_DATA2__ESDHC1_DAT2 0x1d5
- MX53_PAD_SD1_DATA3__ESDHC1_DAT3 0x1d5
- MX53_PAD_SD1_CMD__ESDHC1_CMD 0x1d5
- MX53_PAD_SD1_CLK__ESDHC1_CLK 0x1d5
- >;
- };
-
- pinctrl_esdhc1_2: esdhc1grp-2 {
- fsl,pins = <
- MX53_PAD_SD1_DATA0__ESDHC1_DAT0 0x1d5
- MX53_PAD_SD1_DATA1__ESDHC1_DAT1 0x1d5
- MX53_PAD_SD1_DATA2__ESDHC1_DAT2 0x1d5
- MX53_PAD_SD1_DATA3__ESDHC1_DAT3 0x1d5
- MX53_PAD_PATA_DATA8__ESDHC1_DAT4 0x1d5
- MX53_PAD_PATA_DATA9__ESDHC1_DAT5 0x1d5
- MX53_PAD_PATA_DATA10__ESDHC1_DAT6 0x1d5
- MX53_PAD_PATA_DATA11__ESDHC1_DAT7 0x1d5
- MX53_PAD_SD1_CMD__ESDHC1_CMD 0x1d5
- MX53_PAD_SD1_CLK__ESDHC1_CLK 0x1d5
- >;
- };
- };
-
- esdhc2 {
- pinctrl_esdhc2_1: esdhc2grp-1 {
- fsl,pins = <
- MX53_PAD_SD2_CMD__ESDHC2_CMD 0x1d5
- MX53_PAD_SD2_CLK__ESDHC2_CLK 0x1d5
- MX53_PAD_SD2_DATA0__ESDHC2_DAT0 0x1d5
- MX53_PAD_SD2_DATA1__ESDHC2_DAT1 0x1d5
- MX53_PAD_SD2_DATA2__ESDHC2_DAT2 0x1d5
- MX53_PAD_SD2_DATA3__ESDHC2_DAT3 0x1d5
- >;
- };
- };
-
- esdhc3 {
- pinctrl_esdhc3_1: esdhc3grp-1 {
- fsl,pins = <
- MX53_PAD_PATA_DATA8__ESDHC3_DAT0 0x1d5
- MX53_PAD_PATA_DATA9__ESDHC3_DAT1 0x1d5
- MX53_PAD_PATA_DATA10__ESDHC3_DAT2 0x1d5
- MX53_PAD_PATA_DATA11__ESDHC3_DAT3 0x1d5
- MX53_PAD_PATA_DATA0__ESDHC3_DAT4 0x1d5
- MX53_PAD_PATA_DATA1__ESDHC3_DAT5 0x1d5
- MX53_PAD_PATA_DATA2__ESDHC3_DAT6 0x1d5
- MX53_PAD_PATA_DATA3__ESDHC3_DAT7 0x1d5
- MX53_PAD_PATA_RESET_B__ESDHC3_CMD 0x1d5
- MX53_PAD_PATA_IORDY__ESDHC3_CLK 0x1d5
- >;
- };
- };
-
- can1 {
- pinctrl_can1_1: can1grp-1 {
- fsl,pins = <
- MX53_PAD_PATA_INTRQ__CAN1_TXCAN 0x80000000
- MX53_PAD_PATA_DIOR__CAN1_RXCAN 0x80000000
- >;
- };
-
- pinctrl_can1_2: can1grp-2 {
- fsl,pins = <
- MX53_PAD_KEY_COL2__CAN1_TXCAN 0x80000000
- MX53_PAD_KEY_ROW2__CAN1_RXCAN 0x80000000
- >;
- };
-
- pinctrl_can1_3: can1grp-3 {
- fsl,pins = <
- MX53_PAD_GPIO_7__CAN1_TXCAN 0x80000000
- MX53_PAD_GPIO_8__CAN1_RXCAN 0x80000000
- >;
- };
- };
-
- can2 {
- pinctrl_can2_1: can2grp-1 {
- fsl,pins = <
- MX53_PAD_KEY_COL4__CAN2_TXCAN 0x80000000
- MX53_PAD_KEY_ROW4__CAN2_RXCAN 0x80000000
- >;
- };
- };
-
- i2c1 {
- pinctrl_i2c1_1: i2c1grp-1 {
- fsl,pins = <
- MX53_PAD_CSI0_DAT8__I2C1_SDA 0xc0000000
- MX53_PAD_CSI0_DAT9__I2C1_SCL 0xc0000000
- >;
- };
-
- pinctrl_i2c1_2: i2c1grp-2 {
- fsl,pins = <
- MX53_PAD_EIM_D21__I2C1_SCL 0xc0000000
- MX53_PAD_EIM_D28__I2C1_SDA 0xc0000000
- >;
- };
- };
-
- i2c2 {
- pinctrl_i2c2_1: i2c2grp-1 {
- fsl,pins = <
- MX53_PAD_KEY_ROW3__I2C2_SDA 0xc0000000
- MX53_PAD_KEY_COL3__I2C2_SCL 0xc0000000
- >;
- };
-
- pinctrl_i2c2_2: i2c2grp-2 {
- fsl,pins = <
- MX53_PAD_EIM_D16__I2C2_SDA 0xc0000000
- MX53_PAD_EIM_EB2__I2C2_SCL 0xc0000000
- >;
- };
- };
-
- i2c3 {
- pinctrl_i2c3_1: i2c3grp-1 {
- fsl,pins = <
- MX53_PAD_GPIO_6__I2C3_SDA 0xc0000000
- MX53_PAD_GPIO_5__I2C3_SCL 0xc0000000
- >;
- };
- };
-
- ipu_disp0 {
- pinctrl_ipu_disp0_1: ipudisp0grp-1 {
- fsl,pins = <
- MX53_PAD_DI0_DISP_CLK__IPU_DI0_DISP_CLK 0x5
- MX53_PAD_DI0_PIN15__IPU_DI0_PIN15 0x5
- MX53_PAD_DI0_PIN2__IPU_DI0_PIN2 0x5
- MX53_PAD_DI0_PIN3__IPU_DI0_PIN3 0x5
- MX53_PAD_DISP0_DAT0__IPU_DISP0_DAT_0 0x5
- MX53_PAD_DISP0_DAT1__IPU_DISP0_DAT_1 0x5
- MX53_PAD_DISP0_DAT2__IPU_DISP0_DAT_2 0x5
- MX53_PAD_DISP0_DAT3__IPU_DISP0_DAT_3 0x5
- MX53_PAD_DISP0_DAT4__IPU_DISP0_DAT_4 0x5
- MX53_PAD_DISP0_DAT5__IPU_DISP0_DAT_5 0x5
- MX53_PAD_DISP0_DAT6__IPU_DISP0_DAT_6 0x5
- MX53_PAD_DISP0_DAT7__IPU_DISP0_DAT_7 0x5
- MX53_PAD_DISP0_DAT8__IPU_DISP0_DAT_8 0x5
- MX53_PAD_DISP0_DAT9__IPU_DISP0_DAT_9 0x5
- MX53_PAD_DISP0_DAT10__IPU_DISP0_DAT_10 0x5
- MX53_PAD_DISP0_DAT11__IPU_DISP0_DAT_11 0x5
- MX53_PAD_DISP0_DAT12__IPU_DISP0_DAT_12 0x5
- MX53_PAD_DISP0_DAT13__IPU_DISP0_DAT_13 0x5
- MX53_PAD_DISP0_DAT14__IPU_DISP0_DAT_14 0x5
- MX53_PAD_DISP0_DAT15__IPU_DISP0_DAT_15 0x5
- MX53_PAD_DISP0_DAT16__IPU_DISP0_DAT_16 0x5
- MX53_PAD_DISP0_DAT17__IPU_DISP0_DAT_17 0x5
- MX53_PAD_DISP0_DAT18__IPU_DISP0_DAT_18 0x5
- MX53_PAD_DISP0_DAT19__IPU_DISP0_DAT_19 0x5
- MX53_PAD_DISP0_DAT20__IPU_DISP0_DAT_20 0x5
- MX53_PAD_DISP0_DAT21__IPU_DISP0_DAT_21 0x5
- MX53_PAD_DISP0_DAT22__IPU_DISP0_DAT_22 0x5
- MX53_PAD_DISP0_DAT23__IPU_DISP0_DAT_23 0x5
- >;
- };
- };
-
- ipu_disp1 {
- pinctrl_ipu_disp1_1: ipudisp1grp-1 {
- fsl,pins = <
- MX53_PAD_EIM_DA9__IPU_DISP1_DAT_0 0x5
- MX53_PAD_EIM_DA8__IPU_DISP1_DAT_1 0x5
- MX53_PAD_EIM_DA7__IPU_DISP1_DAT_2 0x5
- MX53_PAD_EIM_DA6__IPU_DISP1_DAT_3 0x5
- MX53_PAD_EIM_DA5__IPU_DISP1_DAT_4 0x5
- MX53_PAD_EIM_DA4__IPU_DISP1_DAT_5 0x5
- MX53_PAD_EIM_DA3__IPU_DISP1_DAT_6 0x5
- MX53_PAD_EIM_DA2__IPU_DISP1_DAT_7 0x5
- MX53_PAD_EIM_DA1__IPU_DISP1_DAT_8 0x5
- MX53_PAD_EIM_DA0__IPU_DISP1_DAT_9 0x5
- MX53_PAD_EIM_EB1__IPU_DISP1_DAT_10 0x5
- MX53_PAD_EIM_EB0__IPU_DISP1_DAT_11 0x5
- MX53_PAD_EIM_A17__IPU_DISP1_DAT_12 0x5
- MX53_PAD_EIM_A18__IPU_DISP1_DAT_13 0x5
- MX53_PAD_EIM_A19__IPU_DISP1_DAT_14 0x5
- MX53_PAD_EIM_A20__IPU_DISP1_DAT_15 0x5
- MX53_PAD_EIM_A21__IPU_DISP1_DAT_16 0x5
- MX53_PAD_EIM_A22__IPU_DISP1_DAT_17 0x5
- MX53_PAD_EIM_A23__IPU_DISP1_DAT_18 0x5
- MX53_PAD_EIM_A24__IPU_DISP1_DAT_19 0x5
- MX53_PAD_EIM_D31__IPU_DISP1_DAT_20 0x5
- MX53_PAD_EIM_D30__IPU_DISP1_DAT_21 0x5
- MX53_PAD_EIM_D26__IPU_DISP1_DAT_22 0x5
- MX53_PAD_EIM_D27__IPU_DISP1_DAT_23 0x5
- MX53_PAD_EIM_A16__IPU_DI1_DISP_CLK 0x5
- MX53_PAD_EIM_DA13__IPU_DI1_D0_CS 0x5
- MX53_PAD_EIM_DA14__IPU_DI1_D1_CS 0x5
- MX53_PAD_EIM_DA15__IPU_DI1_PIN1 0x5
- MX53_PAD_EIM_DA11__IPU_DI1_PIN2 0x5
- MX53_PAD_EIM_DA12__IPU_DI1_PIN3 0x5
- MX53_PAD_EIM_A25__IPU_DI1_PIN12 0x5
- MX53_PAD_EIM_DA10__IPU_DI1_PIN15 0x5
- >;
- };
- };
-
- ipu_disp2 {
- pinctrl_ipu_disp2_1: ipudisp2grp-1 {
- fsl,pins = <
- MX53_PAD_LVDS0_TX0_P__LDB_LVDS0_TX0 0x80000000
- MX53_PAD_LVDS0_TX1_P__LDB_LVDS0_TX1 0x80000000
- MX53_PAD_LVDS0_TX2_P__LDB_LVDS0_TX2 0x80000000
- MX53_PAD_LVDS0_TX3_P__LDB_LVDS0_TX3 0x80000000
- MX53_PAD_LVDS0_CLK_P__LDB_LVDS0_CLK 0x80000000
- MX53_PAD_LVDS1_TX0_P__LDB_LVDS1_TX0 0x80000000
- MX53_PAD_LVDS1_TX1_P__LDB_LVDS1_TX1 0x80000000
- MX53_PAD_LVDS1_TX2_P__LDB_LVDS1_TX2 0x80000000
- MX53_PAD_LVDS1_TX3_P__LDB_LVDS1_TX3 0x80000000
- MX53_PAD_LVDS1_CLK_P__LDB_LVDS1_CLK 0x80000000
- >;
- };
- };
-
- nand {
- pinctrl_nand_1: nandgrp-1 {
- fsl,pins = <
- MX53_PAD_NANDF_WE_B__EMI_NANDF_WE_B 0x4
- MX53_PAD_NANDF_RE_B__EMI_NANDF_RE_B 0x4
- MX53_PAD_NANDF_CLE__EMI_NANDF_CLE 0x4
- MX53_PAD_NANDF_ALE__EMI_NANDF_ALE 0x4
- MX53_PAD_NANDF_WP_B__EMI_NANDF_WP_B 0xe0
- MX53_PAD_NANDF_RB0__EMI_NANDF_RB_0 0xe0
- MX53_PAD_NANDF_CS0__EMI_NANDF_CS_0 0x4
- MX53_PAD_PATA_DATA0__EMI_NANDF_D_0 0xa4
- MX53_PAD_PATA_DATA1__EMI_NANDF_D_1 0xa4
- MX53_PAD_PATA_DATA2__EMI_NANDF_D_2 0xa4
- MX53_PAD_PATA_DATA3__EMI_NANDF_D_3 0xa4
- MX53_PAD_PATA_DATA4__EMI_NANDF_D_4 0xa4
- MX53_PAD_PATA_DATA5__EMI_NANDF_D_5 0xa4
- MX53_PAD_PATA_DATA6__EMI_NANDF_D_6 0xa4
- MX53_PAD_PATA_DATA7__EMI_NANDF_D_7 0xa4
- >;
- };
- };
-
- owire {
- pinctrl_owire_1: owiregrp-1 {
- fsl,pins = <
- MX53_PAD_GPIO_18__OWIRE_LINE 0x80000000
- >;
- };
- };
-
- pwm1 {
- pinctrl_pwm1_1: pwm1grp-1 {
- fsl,pins = <
- MX53_PAD_DISP0_DAT8__PWM1_PWMO 0x5
- >;
- };
- };
-
- pwm2 {
- pinctrl_pwm2_1: pwm2grp-1 {
- fsl,pins = <
- MX53_PAD_GPIO_1__PWM2_PWMO 0x80000000
- >;
- };
- };
-
- uart1 {
- pinctrl_uart1_1: uart1grp-1 {
- fsl,pins = <
- MX53_PAD_CSI0_DAT10__UART1_TXD_MUX 0x1e4
- MX53_PAD_CSI0_DAT11__UART1_RXD_MUX 0x1e4
- >;
- };
-
- pinctrl_uart1_2: uart1grp-2 {
- fsl,pins = <
- MX53_PAD_PATA_DIOW__UART1_TXD_MUX 0x1e4
- MX53_PAD_PATA_DMACK__UART1_RXD_MUX 0x1e4
- >;
- };
-
- pinctrl_uart1_3: uart1grp-3 {
- fsl,pins = <
- MX53_PAD_PATA_RESET_B__UART1_CTS 0x1c5
- MX53_PAD_PATA_IORDY__UART1_RTS 0x1c5
- >;
- };
- };
-
- uart2 {
- pinctrl_uart2_1: uart2grp-1 {
- fsl,pins = <
- MX53_PAD_PATA_BUFFER_EN__UART2_RXD_MUX 0x1e4
- MX53_PAD_PATA_DMARQ__UART2_TXD_MUX 0x1e4
- >;
- };
-
- pinctrl_uart2_2: uart2grp-2 {
- fsl,pins = <
- MX53_PAD_PATA_BUFFER_EN__UART2_RXD_MUX 0x1c5
- MX53_PAD_PATA_DMARQ__UART2_TXD_MUX 0x1c5
- MX53_PAD_PATA_DIOR__UART2_RTS 0x1c5
- MX53_PAD_PATA_INTRQ__UART2_CTS 0x1c5
- >;
- };
- };
-
- uart3 {
- pinctrl_uart3_1: uart3grp-1 {
- fsl,pins = <
- MX53_PAD_PATA_CS_0__UART3_TXD_MUX 0x1e4
- MX53_PAD_PATA_CS_1__UART3_RXD_MUX 0x1e4
- MX53_PAD_PATA_DA_1__UART3_CTS 0x1e4
- MX53_PAD_PATA_DA_2__UART3_RTS 0x1e4
- >;
- };
-
- pinctrl_uart3_2: uart3grp-2 {
- fsl,pins = <
- MX53_PAD_PATA_CS_0__UART3_TXD_MUX 0x1e4
- MX53_PAD_PATA_CS_1__UART3_RXD_MUX 0x1e4
- >;
- };
-
- };
-
- uart4 {
- pinctrl_uart4_1: uart4grp-1 {
- fsl,pins = <
- MX53_PAD_KEY_COL0__UART4_TXD_MUX 0x1e4
- MX53_PAD_KEY_ROW0__UART4_RXD_MUX 0x1e4
- >;
- };
- };
-
- uart5 {
- pinctrl_uart5_1: uart5grp-1 {
- fsl,pins = <
- MX53_PAD_KEY_COL1__UART5_TXD_MUX 0x1e4
- MX53_PAD_KEY_ROW1__UART5_RXD_MUX 0x1e4
- >;
- };
- };
};
gpr: iomuxc-gpr@53fa8000 {
@@ -869,9 +402,12 @@
compatible = "fsl,imx53-ldb";
reg = <0x53fa8008 0x4>;
gpr = <&gpr>;
- clocks = <&clks 122>, <&clks 120>,
- <&clks 115>, <&clks 116>,
- <&clks 123>, <&clks 85>;
+ clocks = <&clks IMX5_CLK_LDB_DI0_SEL>,
+ <&clks IMX5_CLK_LDB_DI1_SEL>,
+ <&clks IMX5_CLK_IPU_DI0_SEL>,
+ <&clks IMX5_CLK_IPU_DI1_SEL>,
+ <&clks IMX5_CLK_LDB_DI0_GATE>,
+ <&clks IMX5_CLK_LDB_DI1_GATE>;
clock-names = "di0_pll", "di1_pll",
"di0_sel", "di1_sel",
"di0", "di1";
@@ -904,7 +440,8 @@
#pwm-cells = <2>;
compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
reg = <0x53fb4000 0x4000>;
- clocks = <&clks 37>, <&clks 38>;
+ clocks = <&clks IMX5_CLK_PWM1_IPG_GATE>,
+ <&clks IMX5_CLK_PWM1_HF_GATE>;
clock-names = "ipg", "per";
interrupts = <61>;
};
@@ -913,7 +450,8 @@
#pwm-cells = <2>;
compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
reg = <0x53fb8000 0x4000>;
- clocks = <&clks 39>, <&clks 40>;
+ clocks = <&clks IMX5_CLK_PWM2_IPG_GATE>,
+ <&clks IMX5_CLK_PWM2_HF_GATE>;
clock-names = "ipg", "per";
interrupts = <94>;
};
@@ -922,7 +460,8 @@
compatible = "fsl,imx53-uart", "fsl,imx21-uart";
reg = <0x53fbc000 0x4000>;
interrupts = <31>;
- clocks = <&clks 28>, <&clks 29>;
+ clocks = <&clks IMX5_CLK_UART1_IPG_GATE>,
+ <&clks IMX5_CLK_UART1_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -931,7 +470,8 @@
compatible = "fsl,imx53-uart", "fsl,imx21-uart";
reg = <0x53fc0000 0x4000>;
interrupts = <32>;
- clocks = <&clks 30>, <&clks 31>;
+ clocks = <&clks IMX5_CLK_UART2_IPG_GATE>,
+ <&clks IMX5_CLK_UART2_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -940,7 +480,8 @@
compatible = "fsl,imx53-flexcan", "fsl,p1010-flexcan";
reg = <0x53fc8000 0x4000>;
interrupts = <82>;
- clocks = <&clks 158>, <&clks 157>;
+ clocks = <&clks IMX5_CLK_CAN1_IPG_GATE>,
+ <&clks IMX5_CLK_CAN1_SERIAL_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -949,7 +490,8 @@
compatible = "fsl,imx53-flexcan", "fsl,p1010-flexcan";
reg = <0x53fcc000 0x4000>;
interrupts = <83>;
- clocks = <&clks 87>, <&clks 86>;
+ clocks = <&clks IMX5_CLK_CAN2_IPG_GATE>,
+ <&clks IMX5_CLK_CAN2_SERIAL_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -1003,7 +545,7 @@
compatible = "fsl,imx53-i2c", "fsl,imx21-i2c";
reg = <0x53fec000 0x4000>;
interrupts = <64>;
- clocks = <&clks 88>;
+ clocks = <&clks IMX5_CLK_I2C3_GATE>;
status = "disabled";
};
@@ -1011,7 +553,8 @@
compatible = "fsl,imx53-uart", "fsl,imx21-uart";
reg = <0x53ff0000 0x4000>;
interrupts = <13>;
- clocks = <&clks 65>, <&clks 66>;
+ clocks = <&clks IMX5_CLK_UART4_IPG_GATE>,
+ <&clks IMX5_CLK_UART4_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -1028,14 +571,15 @@
compatible = "fsl,imx53-iim", "fsl,imx27-iim";
reg = <0x63f98000 0x4000>;
interrupts = <69>;
- clocks = <&clks 107>;
+ clocks = <&clks IMX5_CLK_IIM_GATE>;
};
uart5: serial@63f90000 {
compatible = "fsl,imx53-uart", "fsl,imx21-uart";
reg = <0x63f90000 0x4000>;
interrupts = <86>;
- clocks = <&clks 67>, <&clks 68>;
+ clocks = <&clks IMX5_CLK_UART5_IPG_GATE>,
+ <&clks IMX5_CLK_UART5_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -1043,7 +587,7 @@
owire: owire@63fa4000 {
compatible = "fsl,imx53-owire", "fsl,imx21-owire";
reg = <0x63fa4000 0x4000>;
- clocks = <&clks 159>;
+ clocks = <&clks IMX5_CLK_OWIRE_GATE>;
status = "disabled";
};
@@ -1053,7 +597,8 @@
compatible = "fsl,imx53-ecspi", "fsl,imx51-ecspi";
reg = <0x63fac000 0x4000>;
interrupts = <37>;
- clocks = <&clks 53>, <&clks 54>;
+ clocks = <&clks IMX5_CLK_ECSPI2_IPG_GATE>,
+ <&clks IMX5_CLK_ECSPI2_PER_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -1062,7 +607,8 @@
compatible = "fsl,imx53-sdma", "fsl,imx35-sdma";
reg = <0x63fb0000 0x4000>;
interrupts = <6>;
- clocks = <&clks 56>, <&clks 56>;
+ clocks = <&clks IMX5_CLK_SDMA_GATE>,
+ <&clks IMX5_CLK_SDMA_GATE>;
clock-names = "ipg", "ahb";
#dma-cells = <3>;
fsl,sdma-ram-script-name = "imx/sdma/sdma-imx53.bin";
@@ -1074,7 +620,8 @@
compatible = "fsl,imx53-cspi", "fsl,imx35-cspi";
reg = <0x63fc0000 0x4000>;
interrupts = <38>;
- clocks = <&clks 55>, <&clks 55>;
+ clocks = <&clks IMX5_CLK_CSPI_IPG_GATE>,
+ <&clks IMX5_CLK_CSPI_IPG_GATE>;
clock-names = "ipg", "per";
status = "disabled";
};
@@ -1085,7 +632,7 @@
compatible = "fsl,imx53-i2c", "fsl,imx21-i2c";
reg = <0x63fc4000 0x4000>;
interrupts = <63>;
- clocks = <&clks 35>;
+ clocks = <&clks IMX5_CLK_I2C2_GATE>;
status = "disabled";
};
@@ -1095,15 +642,16 @@
compatible = "fsl,imx53-i2c", "fsl,imx21-i2c";
reg = <0x63fc8000 0x4000>;
interrupts = <62>;
- clocks = <&clks 34>;
+ clocks = <&clks IMX5_CLK_I2C1_GATE>;
status = "disabled";
};
ssi1: ssi@63fcc000 {
- compatible = "fsl,imx53-ssi", "fsl,imx21-ssi";
+ compatible = "fsl,imx53-ssi", "fsl,imx51-ssi",
+ "fsl,imx21-ssi";
reg = <0x63fcc000 0x4000>;
interrupts = <29>;
- clocks = <&clks 48>;
+ clocks = <&clks IMX5_CLK_SSI1_IPG_GATE>;
dmas = <&sdma 28 0 0>,
<&sdma 29 0 0>;
dma-names = "rx", "tx";
@@ -1122,15 +670,16 @@
compatible = "fsl,imx53-nand";
reg = <0x63fdb000 0x1000 0xf7ff0000 0x10000>;
interrupts = <8>;
- clocks = <&clks 60>;
+ clocks = <&clks IMX5_CLK_NFC_GATE>;
status = "disabled";
};
ssi3: ssi@63fe8000 {
- compatible = "fsl,imx53-ssi", "fsl,imx21-ssi";
+ compatible = "fsl,imx53-ssi", "fsl,imx51-ssi",
+ "fsl,imx21-ssi";
reg = <0x63fe8000 0x4000>;
interrupts = <96>;
- clocks = <&clks 50>;
+ clocks = <&clks IMX5_CLK_SSI3_IPG_GATE>;
dmas = <&sdma 46 0 0>,
<&sdma 47 0 0>;
dma-names = "rx", "tx";
@@ -1143,7 +692,9 @@
compatible = "fsl,imx53-fec", "fsl,imx25-fec";
reg = <0x63fec000 0x4000>;
interrupts = <87>;
- clocks = <&clks 42>, <&clks 42>, <&clks 42>;
+ clocks = <&clks IMX5_CLK_FEC_GATE>,
+ <&clks IMX5_CLK_FEC_GATE>,
+ <&clks IMX5_CLK_FEC_GATE>;
clock-names = "ipg", "ahb", "ptp";
status = "disabled";
};
@@ -1152,7 +703,8 @@
compatible = "fsl,imx53-tve";
reg = <0x63ff0000 0x1000>;
interrupts = <92>;
- clocks = <&clks 69>, <&clks 116>;
+ clocks = <&clks IMX5_CLK_TVE_GATE>,
+ <&clks IMX5_CLK_IPU_DI1_SEL>;
clock-names = "tve", "di_sel";
status = "disabled";
@@ -1167,7 +719,8 @@
compatible = "fsl,imx53-vpu";
reg = <0x63ff4000 0x1000>;
interrupts = <9>;
- clocks = <&clks 63>, <&clks 63>;
+ clocks = <&clks IMX5_CLK_VPU_GATE>,
+ <&clks IMX5_CLK_VPU_GATE>;
clock-names = "per", "ahb";
iram = <&ocram>;
status = "disabled";
@@ -1177,7 +730,7 @@
ocram: sram@f8000000 {
compatible = "mmio-sram";
reg = <0xf8000000 0x20000>;
- clocks = <&clks 186>;
+ clocks = <&clks IMX5_CLK_OCRAM>;
};
};
};
diff --git a/arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts b/arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts
new file mode 100644
index 000000000000..994f96a3fb54
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-dfi-fs700-m60.dts
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __DTS_V1__
+#define __DTS_V1__
+/dts-v1/;
+#endif
+
+#include "imx6dl.dtsi"
+#include "imx6qdl-dfi-fs700-m60.dtsi"
+
+/ {
+ model = "DFI FS700-M60-6DL i.MX6dl Q7 Board";
+ compatible = "dfi,fs700-m60-6dl", "dfi,fs700e-m60", "fsl,imx6dl";
+};
diff --git a/arch/arm/boot/dts/imx6dl-gw51xx.dts b/arch/arm/boot/dts/imx6dl-gw51xx.dts
new file mode 100644
index 000000000000..4bd055f4c930
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-gw51xx.dts
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6dl.dtsi"
+#include "imx6qdl-gw51xx.dtsi"
+
+/ {
+ model = "Gateworks Ventana i.MX6 DualLite GW51XX";
+ compatible = "gw,imx6dl-gw51xx", "gw,ventana", "fsl,imx6dl";
+};
diff --git a/arch/arm/boot/dts/imx6dl-gw52xx.dts b/arch/arm/boot/dts/imx6dl-gw52xx.dts
new file mode 100644
index 000000000000..c9136058f15e
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-gw52xx.dts
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6dl.dtsi"
+#include "imx6qdl-gw52xx.dtsi"
+
+/ {
+ model = "Gateworks Ventana i.MX6 DualLite GW52XX";
+ compatible = "gw,imx6dl-gw52xx", "gw,ventana", "fsl,imx6dl";
+};
diff --git a/arch/arm/boot/dts/imx6dl-gw53xx.dts b/arch/arm/boot/dts/imx6dl-gw53xx.dts
new file mode 100644
index 000000000000..61818a14fde6
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-gw53xx.dts
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6dl.dtsi"
+#include "imx6qdl-gw53xx.dtsi"
+
+/ {
+ model = "Gateworks Ventana i.MX6 DualLite GW53XX";
+ compatible = "gw,imx6dl-gw53xx", "gw,ventana", "fsl,imx6dl";
+};
diff --git a/arch/arm/boot/dts/imx6dl-gw54xx.dts b/arch/arm/boot/dts/imx6dl-gw54xx.dts
new file mode 100644
index 000000000000..ab38b6770a06
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-gw54xx.dts
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6dl.dtsi"
+#include "imx6qdl-gw54xx.dtsi"
+
+/ {
+ model = "Gateworks Ventana i.MX6 DualLite GW54XX";
+ compatible = "gw,imx6dl-gw54xx", "gw,ventana", "fsl,imx6dl";
+};
diff --git a/arch/arm/boot/dts/imx6dl-nitrogen6x.dts b/arch/arm/boot/dts/imx6dl-nitrogen6x.dts
new file mode 100644
index 000000000000..5f4d33ccc4b3
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-nitrogen6x.dts
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2013 Boundary Devices, Inc.
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6dl.dtsi"
+#include "imx6qdl-nitrogen6x.dtsi"
+
+/ {
+ model = "Freescale i.MX6 DualLite Nitrogen6x Board";
+ compatible = "fsl,imx6dl-nitrogen6x", "fsl,imx6dl";
+};
diff --git a/arch/arm/boot/dts/imx6dl-pinfunc.h b/arch/arm/boot/dts/imx6dl-pinfunc.h
index b81a7a4ebab6..0ead323fdbd2 100644
--- a/arch/arm/boot/dts/imx6dl-pinfunc.h
+++ b/arch/arm/boot/dts/imx6dl-pinfunc.h
@@ -755,6 +755,7 @@
#define MX6QDL_PAD_GPIO_5__I2C3_SCL 0x230 0x600 0x878 0x6 0x2
#define MX6QDL_PAD_GPIO_5__ARM_EVENTI 0x230 0x600 0x000 0x7 0x0
#define MX6QDL_PAD_GPIO_6__ESAI_TX_CLK 0x234 0x604 0x840 0x0 0x1
+#define MX6QDL_PAD_GPIO_6__ENET_IRQ 0x234 0x604 0x03c 0x11 0xff000609
#define MX6QDL_PAD_GPIO_6__I2C3_SDA 0x234 0x604 0x87c 0x2 0x2
#define MX6QDL_PAD_GPIO_6__GPIO1_IO06 0x234 0x604 0x000 0x5 0x0
#define MX6QDL_PAD_GPIO_6__SD2_LCTL 0x234 0x604 0x000 0x6 0x0
@@ -950,6 +951,7 @@
#define MX6QDL_PAD_RGMII_TXC__GPIO6_IO19 0x2d8 0x6c0 0x000 0x5 0x0
#define MX6QDL_PAD_RGMII_TXC__XTALOSC_REF_CLK_24M 0x2d8 0x6c0 0x000 0x7 0x0
#define MX6QDL_PAD_SD1_CLK__SD1_CLK 0x2dc 0x6c4 0x928 0x0 0x1
+#define MX6QDL_PAD_SD1_CLK__OSC32K_32K_OUT 0x2dc 0x6c4 0x000 0x2 0x0
#define MX6QDL_PAD_SD1_CLK__GPT_CLKIN 0x2dc 0x6c4 0x000 0x3 0x0
#define MX6QDL_PAD_SD1_CLK__GPIO1_IO20 0x2dc 0x6c4 0x000 0x5 0x0
#define MX6QDL_PAD_SD1_CMD__SD1_CMD 0x2e0 0x6c8 0x000 0x0 0x0
diff --git a/arch/arm/boot/dts/imx6dl-sabrelite.dts b/arch/arm/boot/dts/imx6dl-sabrelite.dts
new file mode 100644
index 000000000000..2de04479dc35
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-sabrelite.dts
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6dl.dtsi"
+#include "imx6qdl-sabrelite.dtsi"
+
+/ {
+ model = "Freescale i.MX6 DualLite SABRE Lite Board";
+ compatible = "fsl,imx6dl-sabrelite", "fsl,imx6dl";
+};
diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi
index 25bbdd6f214b..5c5f574330f9 100644
--- a/arch/arm/boot/dts/imx6dl.dtsi
+++ b/arch/arm/boot/dts/imx6dl.dtsi
@@ -8,6 +8,7 @@
*
*/
+#include <dt-bindings/interrupt-controller/irq.h>
#include "imx6dl-pinfunc.h"
#include "imx6qdl.dtsi"
@@ -21,6 +22,26 @@
device_type = "cpu";
reg = <0>;
next-level-cache = <&L2>;
+ operating-points = <
+ /* kHz uV */
+ 996000 1275000
+ 792000 1175000
+ 396000 1075000
+ >;
+ fsl,soc-operating-points = <
+ /* ARM kHz SOC-PU uV */
+ 996000 1175000
+ 792000 1175000
+ 396000 1175000
+ >;
+ clock-latency = <61036>; /* two CLK32 periods */
+ clocks = <&clks 104>, <&clks 6>, <&clks 16>,
+ <&clks 17>, <&clks 170>;
+ clock-names = "arm", "pll2_pfd2_396m", "step",
+ "pll1_sw", "pll1_sys";
+ arm-supply = <&reg_arm>;
+ pu-supply = <&reg_pu>;
+ soc-supply = <&reg_soc>;
};
cpu@1 {
@@ -45,17 +66,17 @@
pxp: pxp@020f0000 {
reg = <0x020f0000 0x4000>;
- interrupts = <0 98 0x04>;
+ interrupts = <0 98 IRQ_TYPE_LEVEL_HIGH>;
};
epdc: epdc@020f4000 {
reg = <0x020f4000 0x4000>;
- interrupts = <0 97 0x04>;
+ interrupts = <0 97 IRQ_TYPE_LEVEL_HIGH>;
};
lcdif: lcdif@020f8000 {
reg = <0x020f8000 0x4000>;
- interrupts = <0 39 0x04>;
+ interrupts = <0 39 IRQ_TYPE_LEVEL_HIGH>;
};
};
@@ -65,7 +86,7 @@
#size-cells = <0>;
compatible = "fsl,imx1-i2c";
reg = <0x021f8000 0x4000>;
- interrupts = <0 35 0x04>;
+ interrupts = <0 35 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
};
diff --git a/arch/arm/boot/dts/imx6q-arm2.dts b/arch/arm/boot/dts/imx6q-arm2.dts
index edf1bd967164..78df05e9d1ce 100644
--- a/arch/arm/boot/dts/imx6q-arm2.dts
+++ b/arch/arm/boot/dts/imx6q-arm2.dts
@@ -23,14 +23,27 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_3p3v: 3p3v {
+ reg_3p3v: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "3P3V";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
+
+ reg_usb_otg_vbus: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio3 22 0>;
+ enable-active-high;
+ };
};
leds {
@@ -46,7 +59,7 @@
&gpmi {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_gpmi_nand_1>;
+ pinctrl-0 = <&pinctrl_gpmi_nand>;
status = "disabled"; /* gpmi nand conflicts with SD */
};
@@ -54,28 +67,131 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- hog {
+ imx6q-arm2 {
pinctrl_hog: hoggrp {
fsl,pins = <
MX6QDL_PAD_EIM_D25__GPIO3_IO25 0x80000000
>;
};
- };
- arm2 {
- pinctrl_usdhc3_arm2: usdhc3grp-arm2 {
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL1__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_KEY_COL2__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_GPIO_6__ENET_IRQ 0x000b1
+ >;
+ };
+
+ pinctrl_gpmi_nand: gpminandgrp {
+ fsl,pins = <
+ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
+ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1
+ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1
+ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000
+ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1
+ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1
+ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1
+ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1
+ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1
+ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1
+ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1
+ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1
+ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1
+ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1
+ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1
+ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1
+ MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D26__UART2_RX_DATA 0x1b0b1
+ MX6QDL_PAD_EIM_D27__UART2_TX_DATA 0x1b0b1
+ MX6QDL_PAD_EIM_D28__UART2_DTE_CTS_B 0x1b0b1
+ MX6QDL_PAD_EIM_D29__UART2_DTE_RTS_B 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart4: uart4grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1
+ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059
+ MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059
+ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059
+ MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3_cdwp: usdhc3cdwp {
fsl,pins = <
MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x80000000
MX6QDL_PAD_NANDF_CS1__GPIO6_IO14 0x80000000
>;
};
+
+ pinctrl_usdhc4: usdhc4grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059
+ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059
+ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059
+ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059
+ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059
+ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059
+ MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059
+ MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059
+ MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059
+ MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059
+ >;
+ };
};
};
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_enet_2>;
+ pinctrl-0 = <&pinctrl_enet>;
phy-mode = "rgmii";
+ interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>,
+ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>;
+ status = "okay";
+};
+
+&usbotg {
+ vbus-supply = <&reg_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
status = "okay";
};
@@ -84,8 +200,8 @@
wp-gpios = <&gpio6 14 0>;
vmmc-supply = <&reg_3p3v>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc3_1
- &pinctrl_usdhc3_arm2>;
+ pinctrl-0 = <&pinctrl_usdhc3
+ &pinctrl_usdhc3_cdwp>;
status = "okay";
};
@@ -93,13 +209,13 @@
non-removable;
vmmc-supply = <&reg_3p3v>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc4_1>;
+ pinctrl-0 = <&pinctrl_usdhc4>;
status = "okay";
};
&uart2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart2_2>;
+ pinctrl-0 = <&pinctrl_uart2>;
fsl,dte-mode;
fsl,uart-has-rtscts;
status = "okay";
@@ -107,6 +223,6 @@
&uart4 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart4_1>;
+ pinctrl-0 = <&pinctrl_uart4>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts
new file mode 100644
index 000000000000..99b46f8030ad
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013 CompuLab Ltd.
+ *
+ * Author: Valentin Raevsky <valentin@compulab.co.il>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6q.dtsi"
+
+/ {
+ model = "CompuLab CM-FX6";
+ compatible = "compulab,cm-fx6", "fsl,imx6q";
+
+ memory {
+ reg = <0x10000000 0x80000000>;
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ heartbeat-led {
+ label = "Heartbeat";
+ gpios = <&gpio2 31 0>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-mode = "rgmii";
+ status = "okay";
+};
+
+&gpmi {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpmi_nand>;
+ status = "okay";
+};
+
+&iomuxc {
+ imx6q-cm-fx6 {
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ >;
+ };
+
+ pinctrl_gpmi_nand: gpminandgrp {
+ fsl,pins = <
+ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
+ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1
+ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1
+ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000
+ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1
+ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1
+ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1
+ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1
+ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1
+ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1
+ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1
+ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1
+ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1
+ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1
+ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1
+ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1
+ MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1
+ >;
+ };
+
+ pinctrl_uart4: uart4grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1
+ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1
+ >;
+ };
+ };
+};
+
+&uart4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart4>;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts b/arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts
new file mode 100644
index 000000000000..fd0ad9a8866c
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-dfi-fs700-m60.dts
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __DTS_V1__
+#define __DTS_V1__
+/dts-v1/;
+#endif
+
+#include "imx6q.dtsi"
+#include "imx6qdl-dfi-fs700-m60.dtsi"
+
+/ {
+ model = "DFI FS700-M60-6QD i.MX6qd Q7 Board";
+ compatible = "dfi,fs700-m60-6qd", "dfi,fs700e-m60", "fsl,imx6q";
+};
diff --git a/arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts b/arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts
new file mode 100644
index 000000000000..a63bbb3d46bb
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-dmo-edmqmx6.dts
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2013 Data Modul AG
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include "imx6q.dtsi"
+
+/ {
+ model = "Data Modul eDM-QMX6 Board";
+ compatible = "dmo,imx6q-edmqmx6", "fsl,imx6q";
+
+ aliases {
+ gpio7 = &stmpe_gpio;
+ };
+
+ memory {
+ reg = <0x10000000 0x80000000>;
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_3p3v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_usb_otg_vbus: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio7 12 0>;
+ };
+
+ reg_usb_host1: regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "usb_host1_en";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio3 31 0>;
+ enable-active-high;
+ };
+ };
+
+ gpio-leds {
+ compatible = "gpio-leds";
+
+ led-blue {
+ label = "blue";
+ gpios = <&stmpe_gpio 8 GPIO_ACTIVE_HIGH>;
+ linux,default-trigger = "heartbeat";
+ };
+
+ led-green {
+ label = "green";
+ gpios = <&stmpe_gpio 9 GPIO_ACTIVE_HIGH>;
+ };
+
+ led-pink {
+ label = "pink";
+ gpios = <&stmpe_gpio 10 GPIO_ACTIVE_HIGH>;
+ };
+
+ led-red {
+ label = "red";
+ gpios = <&stmpe_gpio 11 GPIO_ACTIVE_HIGH>;
+ };
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-mode = "rgmii";
+ phy-reset-gpios = <&gpio3 23 0>;
+ phy-supply = <&vgen2_1v2_eth>;
+ status = "okay";
+};
+
+&i2c2 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2
+ &pinctrl_stmpe>;
+ status = "okay";
+
+ pmic: pfuze100@08 {
+ compatible = "fsl,pfuze100";
+ reg = <0x08>;
+ interrupt-parent = <&gpio3>;
+ interrupts = <20 8>;
+
+ regulators {
+ sw1a_reg: sw1ab {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw1c_reg: sw1c {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw2_reg: sw2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3a_reg: sw3a {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3b_reg: sw3b {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw4_reg: sw4 {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-always-on;
+ };
+
+ swbst_reg: swbst {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5150000>;
+ regulator-always-on;
+ };
+
+ snvs_reg: vsnvs {
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vref_reg: vrefddr {
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vgen1_reg: vgen1 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen2_1v2_eth: vgen2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vdd_high_in: vgen3 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vgen4_reg: vgen4 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen5_reg: vgen5 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen6_reg: vgen6 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+ };
+
+ stmpe: stmpe1601@40 {
+ compatible = "st,stmpe1601";
+ reg = <0x40>;
+ interrupts = <30 0>;
+ interrupt-parent = <&gpio3>;
+
+ stmpe_gpio: stmpe_gpio {
+ #gpio-cells = <2>;
+ compatible = "st,stmpe-gpio";
+ };
+ };
+
+ temp1: ad7414@4c {
+ compatible = "ad,ad7414";
+ reg = <0x4c>;
+ };
+
+ temp2: ad7414@4d {
+ compatible = "ad,ad7414";
+ reg = <0x4d>;
+ };
+
+ rtc: m41t62@68 {
+ compatible = "stm,m41t62";
+ reg = <0x68>;
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx6q-dmo-edmqmx6 {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_A16__GPIO2_IO22 0x80000000
+ MX6QDL_PAD_EIM_A17__GPIO2_IO21 0x80000000
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_stmpe: stmpegrp {
+ fsl,pins = <MX6QDL_PAD_EIM_D30__GPIO3_IO30 0x80000000>;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1
+ MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
+
+ pinctrl_usdhc4: usdhc4grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059
+ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059
+ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059
+ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059
+ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059
+ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059
+ MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059
+ MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059
+ MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059
+ MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059
+ >;
+ };
+ };
+};
+
+&sata {
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ status = "okay";
+};
+
+&usbh1 {
+ vbus-supply = <&reg_usb_host1>;
+ disable-over-current;
+ status = "okay";
+};
+
+&usbotg {
+ vbus-supply = <&reg_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
+ status = "okay";
+};
+
+&usdhc3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
+
+&usdhc4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc4>;
+ vmmc-supply = <&reg_3p3v>;
+ non-removable;
+ bus-width = <8>;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6q-gk802.dts b/arch/arm/boot/dts/imx6q-gk802.dts
new file mode 100644
index 000000000000..4a9b4dc9afc0
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-gk802.dts
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2013 Philipp Zabel
+ *
+ * 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.
+ */
+
+/dts-v1/;
+#include "imx6q.dtsi"
+
+/ {
+ model = "Zealz GK802";
+ compatible = "zealz,imx6q-gk802", "fsl,imx6q";
+
+ chosen {
+ linux,stdout-path = &uart4;
+ };
+
+ memory {
+ reg = <0x10000000 0x40000000>;
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_3p3v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ recovery-button {
+ label = "recovery";
+ gpios = <&gpio3 16 1>;
+ linux,code = <0x198>; /* KEY_RESTART */
+ gpio-key,wakeup;
+ };
+ };
+};
+
+/* Internal I2C */
+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ clock-frequency = <100000>;
+ status = "okay";
+
+ /* SDMC DM2016 1024 bit EEPROM + 128 bit OTP */
+ eeprom: dm2016@51 {
+ compatible = "sdmc,dm2016";
+ reg = <0x51>;
+ };
+};
+
+/* External I2C via HDMI */
+&i2c3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3>;
+ clock-frequency = <100000>;
+ status = "okay";
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx6q-gk802 {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ /* Recovery button, active-low */
+ MX6QDL_PAD_EIM_D16__GPIO3_IO16 0x100b1
+ /* RTL8192CU enable GPIO, active-low */
+ MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x1b0b0
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1
+ MX6QDL_PAD_GPIO_16__I2C3_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_uart4: uart4grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1
+ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
+
+ pinctrl_usdhc4: usdhc4grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059
+ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059
+ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059
+ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059
+ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059
+ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059
+ >;
+ };
+ };
+};
+
+&uart2 {
+ status = "okay";
+};
+
+&uart4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart4>;
+ status = "okay";
+};
+
+/* External USB-A port (USBOTG) */
+&usbotg {
+ disable-over-current;
+ status = "okay";
+};
+
+/* Internal USB port (USBH1), connected to RTL8192CU */
+&usbh1 {
+ disable-over-current;
+ status = "okay";
+};
+
+/* External microSD */
+&usdhc3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ bus-width = <4>;
+ cd-gpios = <&gpio6 11 0>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
+
+/* Internal microSD */
+&usdhc4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc4>;
+ bus-width = <4>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6q-gw51xx.dts b/arch/arm/boot/dts/imx6q-gw51xx.dts
new file mode 100644
index 000000000000..af4929aee075
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-gw51xx.dts
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6q.dtsi"
+#include "imx6qdl-gw54xx.dtsi"
+
+/ {
+ model = "Gateworks Ventana i.MX6 Quad GW51XX";
+ compatible = "gw,imx6q-gw51xx", "gw,ventana", "fsl,imx6q";
+};
diff --git a/arch/arm/boot/dts/imx6q-gw52xx.dts b/arch/arm/boot/dts/imx6q-gw52xx.dts
new file mode 100644
index 000000000000..5f71ddbc7f05
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-gw52xx.dts
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6q.dtsi"
+#include "imx6qdl-gw52xx.dtsi"
+
+/ {
+ model = "Gateworks Ventana i.MX6 Quad GW52XX";
+ compatible = "gw,imx6q-gw52xx", "gw,ventana", "fsl,imx6q";
+};
+
+&sata {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6q-gw53xx.dts b/arch/arm/boot/dts/imx6q-gw53xx.dts
new file mode 100644
index 000000000000..360c316b4740
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-gw53xx.dts
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6q.dtsi"
+#include "imx6qdl-gw53xx.dtsi"
+
+/ {
+ model = "Gateworks Ventana i.MX6 Quad GW53XX";
+ compatible = "gw,imx6q-gw53xx", "gw,ventana", "fsl,imx6q";
+};
+
+&sata {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6q-gw5400-a.dts b/arch/arm/boot/dts/imx6q-gw5400-a.dts
new file mode 100644
index 000000000000..902f98310481
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-gw5400-a.dts
@@ -0,0 +1,546 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6q.dtsi"
+
+/ {
+ model = "Gateworks Ventana GW5400-A";
+ compatible = "gw,imx6q-gw5400-a", "gw,ventana", "fsl,imx6q";
+
+ /* these are used by bootloader for disabling nodes */
+ aliases {
+ ethernet0 = &fec;
+ ethernet1 = &eth1;
+ i2c0 = &i2c1;
+ i2c1 = &i2c2;
+ i2c2 = &i2c3;
+ led0 = &led0;
+ led1 = &led1;
+ led2 = &led2;
+ sky2 = &eth1;
+ ssi0 = &ssi1;
+ spi0 = &ecspi1;
+ usb0 = &usbh1;
+ usb1 = &usbotg;
+ usdhc2 = &usdhc3;
+ };
+
+ chosen {
+ bootargs = "console=ttymxc1,115200";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led0: user1 {
+ label = "user1";
+ gpios = <&gpio4 6 0>; /* 102 -> MX6_PANLEDG */
+ default-state = "on";
+ linux,default-trigger = "heartbeat";
+ };
+
+ led1: user2 {
+ label = "user2";
+ gpios = <&gpio4 10 0>; /* 106 -> MX6_PANLEDR */
+ default-state = "off";
+ };
+
+ led2: user3 {
+ label = "user3";
+ gpios = <&gpio4 15 1>; /* 111 -> MX6_LOCLED# */
+ default-state = "off";
+ };
+ };
+
+ memory {
+ reg = <0x10000000 0x40000000>;
+ };
+
+ pps {
+ compatible = "pps-gpio";
+ gpios = <&gpio1 5 0>;
+ status = "okay";
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_1p0v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "1P0V";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ reg_3p3v: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_usb_h1_vbus: regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "usb_h1_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ reg_usb_otg_vbus: regulator@3 {
+ compatible = "regulator-fixed";
+ reg = <3>;
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio3 22 0>;
+ enable-active-high;
+ };
+ };
+
+ sound {
+ compatible = "fsl,imx6q-sabrelite-sgtl5000",
+ "fsl,imx-audio-sgtl5000";
+ model = "imx6q-sabrelite-sgtl5000";
+ ssi-controller = <&ssi1>;
+ audio-codec = <&codec>;
+ audio-routing =
+ "MIC_IN", "Mic Jack",
+ "Mic Jack", "Mic Bias",
+ "Headphone Jack", "HP_OUT";
+ mux-int-port = <1>;
+ mux-ext-port = <4>;
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>;
+ status = "okay";
+};
+
+&ecspi1 {
+ fsl,spi-num-chipselects = <1>;
+ cs-gpios = <&gpio3 19 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ecspi1>;
+ status = "okay";
+
+ flash: m25p80@0 {
+ compatible = "sst,w25q256";
+ spi-max-frequency = <30000000>;
+ reg = <0>;
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-mode = "rgmii";
+ phy-reset-gpios = <&gpio1 30 0>;
+ status = "okay";
+};
+
+&i2c1 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ eeprom1: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+ pagesize = <16>;
+ };
+
+ eeprom2: eeprom@51 {
+ compatible = "atmel,24c02";
+ reg = <0x51>;
+ pagesize = <16>;
+ };
+
+ eeprom3: eeprom@52 {
+ compatible = "atmel,24c02";
+ reg = <0x52>;
+ pagesize = <16>;
+ };
+
+ eeprom4: eeprom@53 {
+ compatible = "atmel,24c02";
+ reg = <0x53>;
+ pagesize = <16>;
+ };
+
+ gpio: pca9555@23 {
+ compatible = "nxp,pca9555";
+ reg = <0x23>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ hwmon: gsc@29 {
+ compatible = "gw,gsp";
+ reg = <0x29>;
+ };
+
+ rtc: ds1672@68 {
+ compatible = "dallas,ds1672";
+ reg = <0x68>;
+ };
+};
+
+&i2c2 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+
+ pmic: pfuze100@08 {
+ compatible = "fsl,pfuze100";
+ reg = <0x08>;
+
+ regulators {
+ sw1a_reg: sw1ab {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+ };
+
+ sw1c_reg: sw1c {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+ };
+
+ sw2_reg: sw2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3950000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3a_reg: sw3a {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3b_reg: sw3b {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw4_reg: sw4 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ swbst_reg: swbst {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5150000>;
+ };
+
+ snvs_reg: vsnvs {
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vref_reg: vrefddr {
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vgen1_reg: vgen1 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen2_reg: vgen2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen3_reg: vgen3 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ vgen4_reg: vgen4 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen5_reg: vgen5 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen6_reg: vgen6 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+ };
+
+ pciswitch: pex8609@3f {
+ compatible = "plx,pex8609";
+ reg = <0x3f>;
+ };
+
+ pciclkgen: si52147@6b {
+ compatible = "sil,si52147";
+ reg = <0x6b>;
+ };
+};
+
+&i2c3 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3>;
+ status = "okay";
+
+ accelerometer: mma8450@1c {
+ compatible = "fsl,mma8450";
+ reg = <0x1c>;
+ };
+
+ codec: sgtl5000@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ clocks = <&clks 201>;
+ VDDA-supply = <&sw4_reg>;
+ VDDIO-supply = <&reg_3p3v>;
+ };
+
+ hdmiin: adv7611@4c {
+ compatible = "adi,adv7611";
+ reg = <0x4c>;
+ };
+
+ touchscreen: egalax_ts@04 {
+ compatible = "eeti,egalax_ts";
+ reg = <0x04>;
+ interrupt-parent = <&gpio7>;
+ interrupts = <12 2>; /* gpio7_12 active low */
+ wakeup-gpios = <&gpio7 12 0>;
+ };
+
+ videoout: adv7393@2a {
+ compatible = "adi,adv7393";
+ reg = <0x2a>;
+ };
+
+ videoin: adv7180@20 {
+ compatible = "adi,adv7180";
+ reg = <0x20>;
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx6q-gw5400-a {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 /* OTG_PWR_EN */
+ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x80000000 /* SPINOR_CS0# */
+ MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 /* PCIE IRQ */
+ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 /* PCIE RST */
+ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x000130b0 /* AUD4_MCK */
+ MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x80000000 /* GPS_PPS */
+ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 /* TOUCH_IRQ# */
+ MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x80000000 /* user1 led */
+ MX6QDL_PAD_KEY_COL2__GPIO4_IO10 0x80000000 /* user2 led */
+ MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x80000000 /* user3 led */
+ MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x80000000 /* USBHUB_RST# */
+ MX6QDL_PAD_SD1_DAT3__GPIO1_IO21 0x80000000 /* MIPI_DIO */
+ >;
+ };
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0
+ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0
+ MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0
+ MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0
+ >;
+ };
+
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1
+ MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1
+ MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
+ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1
+ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart5: uart5grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x1b0b1
+ MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
+ };
+};
+
+&ldb {
+ status = "okay";
+ lvds-channel@0 {
+ crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
+ };
+};
+
+&pcie {
+ reset-gpio = <&gpio1 29 0>;
+ status = "okay";
+
+ eth1: sky2@8 { /* MAC/PHY on bus 8 */
+ compatible = "marvell,sky2";
+ };
+};
+
+&ssi1 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ status = "okay";
+};
+
+&uart5 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart5>;
+ status = "okay";
+};
+
+&usbotg {
+ vbus-supply = <&reg_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
+ status = "okay";
+};
+
+&usbh1 {
+ vbus-supply = <&reg_usb_h1_vbus>;
+ status = "okay";
+};
+
+&usdhc3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ cd-gpios = <&gpio7 0 0>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6q-gw54xx.dts b/arch/arm/boot/dts/imx6q-gw54xx.dts
new file mode 100644
index 000000000000..ab518d66a75e
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-gw54xx.dts
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6q.dtsi"
+#include "imx6qdl-gw54xx.dtsi"
+
+/ {
+ model = "Gateworks Ventana i.MX6 Quad GW54XX";
+ compatible = "gw,imx6q-gw54xx", "gw,ventana", "fsl,imx6q";
+};
+
+&sata {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6q-nitrogen6x.dts b/arch/arm/boot/dts/imx6q-nitrogen6x.dts
new file mode 100644
index 000000000000..a57866b2e97e
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-nitrogen6x.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 Boundary Devices, Inc.
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6q.dtsi"
+#include "imx6qdl-nitrogen6x.dtsi"
+
+/ {
+ model = "Freescale i.MX6 Quad Nitrogen6x Board";
+ compatible = "fsl,imx6q-nitrogen6x", "fsl,imx6q";
+};
+
+&sata {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6q-phytec-pbab01.dts b/arch/arm/boot/dts/imx6q-phytec-pbab01.dts
index 7d37ec60d58d..5607c331fca8 100644
--- a/arch/arm/boot/dts/imx6q-phytec-pbab01.dts
+++ b/arch/arm/boot/dts/imx6q-phytec-pbab01.dts
@@ -21,10 +21,26 @@
status = "okay";
};
+&gpmi {
+ status = "okay";
+};
+
+&sata {
+ status = "okay";
+};
+
&uart4 {
status = "okay";
};
+&usbh1 {
+ status = "okay";
+};
+
+&usbotg {
+ status = "okay";
+};
+
&usdhc2 {
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi b/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi
index 1a3b50d4d8fa..324f1550976b 100644
--- a/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi
+++ b/arch/arm/boot/dts/imx6q-phytec-pfla02.dtsi
@@ -18,11 +18,35 @@
memory {
reg = <0x10000000 0x80000000>;
};
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_usb_otg_vbus: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio4 15 0>;
+ };
+
+ reg_usb_h1_vbus: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "usb_h1_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio1 0 0>;
+ };
+ };
};
&ecspi3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi3_1>;
+ pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio4 24 0>;
@@ -36,7 +60,7 @@
&i2c1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c1_1>;
+ pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
eeprom@50 {
@@ -128,7 +152,7 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- hog {
+ imx6q-phytec-pfla02 {
pinctrl_hog: hoggrp {
fsl,pins = <
MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000
@@ -136,10 +160,109 @@
MX6QDL_PAD_DI0_PIN15__GPIO4_IO17 0x80000000 /* PMIC interrupt */
>;
};
- };
- pfla02 {
- pinctrl_usdhc3_pfla02: usdhc3grp-pfla02 {
+ pinctrl_ecspi3: ecspi3grp {
+ fsl,pins = <
+ MX6QDL_PAD_DISP0_DAT2__ECSPI3_MISO 0x100b1
+ MX6QDL_PAD_DISP0_DAT1__ECSPI3_MOSI 0x100b1
+ MX6QDL_PAD_DISP0_DAT0__ECSPI3_SCLK 0x100b1
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x1b0b0
+ >;
+ };
+
+ pinctrl_gpmi_nand: gpminandgrp {
+ fsl,pins = <
+ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
+ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1
+ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1
+ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000
+ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1
+ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1
+ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1
+ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1
+ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1
+ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1
+ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1
+ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1
+ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1
+ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1
+ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1
+ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1
+ MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
+ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_uart4: uart4grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1
+ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbh1: usbh1grp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_0__USB_H1_PWR 0x80000000
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0
+ MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x80000000
+ >;
+ };
+
+ pinctrl_usdhc2: usdhc2grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059
+ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059
+ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059
+ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059
+ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059
+ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3_cdwp: usdhc3cdwp {
fsl,pins = <
MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x80000000
MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000
@@ -150,21 +273,43 @@
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_enet_3>;
+ pinctrl-0 = <&pinctrl_enet>;
phy-mode = "rgmii";
phy-reset-gpios = <&gpio3 23 0>;
status = "disabled";
};
+&gpmi {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpmi_nand>;
+ nand-on-flash-bbt;
+ status = "disabled";
+};
+
&uart4 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart4_1>;
+ pinctrl-0 = <&pinctrl_uart4>;
+ status = "disabled";
+};
+
+&usbh1 {
+ vbus-supply = <&reg_usb_h1_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbh1>;
+ status = "disabled";
+};
+
+&usbotg {
+ vbus-supply = <&reg_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
status = "disabled";
};
&usdhc2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc2_2>;
+ pinctrl-0 = <&pinctrl_usdhc2>;
cd-gpios = <&gpio1 4 0>;
wp-gpios = <&gpio1 2 0>;
status = "disabled";
@@ -172,8 +317,8 @@
&usdhc3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc3_2
- &pinctrl_usdhc3_pfla02>;
+ pinctrl-0 = <&pinctrl_usdhc3
+ &pinctrl_usdhc3_cdwp>;
cd-gpios = <&gpio1 27 0>;
wp-gpios = <&gpio1 29 0>;
status = "disabled";
diff --git a/arch/arm/boot/dts/imx6q-pinfunc.h b/arch/arm/boot/dts/imx6q-pinfunc.h
index 97ed0816a6e0..9fc6120a1853 100644
--- a/arch/arm/boot/dts/imx6q-pinfunc.h
+++ b/arch/arm/boot/dts/imx6q-pinfunc.h
@@ -673,6 +673,7 @@
#define MX6QDL_PAD_GPIO_3__USB_H1_OC 0x22c 0x5fc 0x948 0x6 0x1
#define MX6QDL_PAD_GPIO_3__MLB_CLK 0x22c 0x5fc 0x900 0x7 0x1
#define MX6QDL_PAD_GPIO_6__ESAI_TX_CLK 0x230 0x600 0x870 0x0 0x1
+#define MX6QDL_PAD_GPIO_6__ENET_IRQ 0x230 0x600 0x03c 0x11 0xff000609
#define MX6QDL_PAD_GPIO_6__I2C3_SDA 0x230 0x600 0x8ac 0x2 0x1
#define MX6QDL_PAD_GPIO_6__GPIO1_IO06 0x230 0x600 0x000 0x5 0x0
#define MX6QDL_PAD_GPIO_6__SD2_LCTL 0x230 0x600 0x000 0x6 0x0
@@ -1024,6 +1025,7 @@
#define MX6QDL_PAD_SD1_DAT2__WDOG1_RESET_B_DEB 0x34c 0x734 0x000 0x6 0x0
#define MX6QDL_PAD_SD1_CLK__SD1_CLK 0x350 0x738 0x000 0x0 0x0
#define MX6QDL_PAD_SD1_CLK__ECSPI5_SCLK 0x350 0x738 0x828 0x1 0x0
+#define MX6QDL_PAD_SD1_CLK__OSC32K_32K_OUT 0x350 0x738 0x000 0x2 0x0
#define MX6QDL_PAD_SD1_CLK__GPT_CLKIN 0x350 0x738 0x000 0x3 0x0
#define MX6QDL_PAD_SD1_CLK__GPIO1_IO20 0x350 0x738 0x000 0x5 0x0
#define MX6QDL_PAD_SD2_CLK__SD2_CLK 0x354 0x73c 0x000 0x0 0x0
diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts
index f004913f7d80..96e4688be77c 100644
--- a/arch/arm/boot/dts/imx6q-sabrelite.dts
+++ b/arch/arm/boot/dts/imx6q-sabrelite.dts
@@ -12,189 +12,13 @@
/dts-v1/;
#include "imx6q.dtsi"
+#include "imx6qdl-sabrelite.dtsi"
/ {
model = "Freescale i.MX6 Quad SABRE Lite Board";
compatible = "fsl,imx6q-sabrelite", "fsl,imx6q";
-
- memory {
- reg = <0x10000000 0x40000000>;
- };
-
- regulators {
- compatible = "simple-bus";
-
- reg_2p5v: 2p5v {
- compatible = "regulator-fixed";
- regulator-name = "2P5V";
- regulator-min-microvolt = <2500000>;
- regulator-max-microvolt = <2500000>;
- regulator-always-on;
- };
-
- reg_3p3v: 3p3v {
- compatible = "regulator-fixed";
- regulator-name = "3P3V";
- regulator-min-microvolt = <3300000>;
- regulator-max-microvolt = <3300000>;
- regulator-always-on;
- };
-
- reg_usb_otg_vbus: usb_otg_vbus {
- compatible = "regulator-fixed";
- regulator-name = "usb_otg_vbus";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
- gpio = <&gpio3 22 0>;
- enable-active-high;
- };
- };
-
- sound {
- compatible = "fsl,imx6q-sabrelite-sgtl5000",
- "fsl,imx-audio-sgtl5000";
- model = "imx6q-sabrelite-sgtl5000";
- ssi-controller = <&ssi1>;
- audio-codec = <&codec>;
- audio-routing =
- "MIC_IN", "Mic Jack",
- "Mic Jack", "Mic Bias",
- "Headphone Jack", "HP_OUT";
- mux-int-port = <1>;
- mux-ext-port = <4>;
- };
-};
-
-&audmux {
- status = "okay";
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_audmux_1>;
-};
-
-&ecspi1 {
- fsl,spi-num-chipselects = <1>;
- cs-gpios = <&gpio3 19 0>;
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi1_1>;
- status = "okay";
-
- flash: m25p80@0 {
- compatible = "sst,sst25vf016b";
- spi-max-frequency = <20000000>;
- reg = <0>;
- };
-};
-
-&fec {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_enet_1>;
- phy-mode = "rgmii";
- phy-reset-gpios = <&gpio3 23 0>;
- status = "okay";
-};
-
-&i2c1 {
- status = "okay";
- clock-frequency = <100000>;
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c1_1>;
-
- codec: sgtl5000@0a {
- compatible = "fsl,sgtl5000";
- reg = <0x0a>;
- clocks = <&clks 201>;
- VDDA-supply = <&reg_2p5v>;
- VDDIO-supply = <&reg_3p3v>;
- };
-};
-
-&iomuxc {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_hog>;
-
- hog {
- pinctrl_hog: hoggrp {
- fsl,pins = <
- MX6QDL_PAD_NANDF_D6__GPIO2_IO06 0x80000000
- MX6QDL_PAD_NANDF_D7__GPIO2_IO07 0x80000000
- MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x80000000
- MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000
- MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000
- MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x80000000
- MX6QDL_PAD_SD3_DAT4__GPIO7_IO01 0x1f0b0
- MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x80000000
- MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000
- >;
- };
- };
-};
-
-&ldb {
- status = "okay";
-
- lvds-channel@0 {
- fsl,data-mapping = "spwg";
- fsl,data-width = <18>;
- status = "okay";
-
- display-timings {
- native-mode = <&timing0>;
- timing0: hsd100pxn1 {
- clock-frequency = <65000000>;
- hactive = <1024>;
- vactive = <768>;
- hback-porch = <220>;
- hfront-porch = <40>;
- vback-porch = <21>;
- vfront-porch = <7>;
- hsync-len = <60>;
- vsync-len = <10>;
- };
- };
- };
};
&sata {
status = "okay";
};
-
-&ssi1 {
- fsl,mode = "i2s-slave";
- status = "okay";
-};
-
-&uart2 {
- status = "okay";
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart2_1>;
-};
-
-&usbh1 {
- status = "okay";
-};
-
-&usbotg {
- vbus-supply = <&reg_usb_otg_vbus>;
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usbotg_1>;
- disable-over-current;
- status = "okay";
-};
-
-&usdhc3 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc3_2>;
- cd-gpios = <&gpio7 0 0>;
- wp-gpios = <&gpio7 1 0>;
- vmmc-supply = <&reg_3p3v>;
- status = "okay";
-};
-
-&usdhc4 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc4_2>;
- cd-gpios = <&gpio2 6 0>;
- wp-gpios = <&gpio2 7 0>;
- vmmc-supply = <&reg_3p3v>;
- status = "okay";
-};
diff --git a/arch/arm/boot/dts/imx6q-sbc6x.dts b/arch/arm/boot/dts/imx6q-sbc6x.dts
index ee6addf149af..86cf09364664 100644
--- a/arch/arm/boot/dts/imx6q-sbc6x.dts
+++ b/arch/arm/boot/dts/imx6q-sbc6x.dts
@@ -17,28 +17,78 @@
};
};
+
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_enet_1>;
+ pinctrl-0 = <&pinctrl_enet>;
phy-mode = "rgmii";
status = "okay";
};
+&iomuxc {
+ imx6q-sbc6x {
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
+ };
+};
+
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_1>;
+ pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
&usbotg {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usbotg_1>;
+ pinctrl-0 = <&pinctrl_usbotg>;
disable-over-current;
status = "okay";
};
&usdhc3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc3_2>;
+ pinctrl-0 = <&pinctrl_usdhc3>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6q-udoo.dts b/arch/arm/boot/dts/imx6q-udoo.dts
index 6e1ccdc019a7..ed397d149ab6 100644
--- a/arch/arm/boot/dts/imx6q-udoo.dts
+++ b/arch/arm/boot/dts/imx6q-udoo.dts
@@ -21,19 +21,69 @@
};
};
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-mode = "rgmii";
+ status = "okay";
+};
+
+&iomuxc {
+ imx6q-udoo {
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1
+ MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
+ };
+};
+
&sata {
status = "okay";
};
&uart2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart2_1>;
+ pinctrl-0 = <&pinctrl_uart2>;
status = "okay";
};
&usdhc3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc3_2>;
+ pinctrl-0 = <&pinctrl_usdhc3>;
non-removable;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index 2a8d9de666c9..addd3f881ce2 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -8,10 +8,15 @@
*
*/
+#include <dt-bindings/interrupt-controller/irq.h>
#include "imx6q-pinfunc.h"
#include "imx6qdl.dtsi"
/ {
+ aliases {
+ spi4 = &ecspi5;
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -25,8 +30,17 @@
/* kHz uV */
1200000 1275000
996000 1250000
+ 852000 1250000
792000 1150000
- 396000 950000
+ 396000 975000
+ >;
+ fsl,soc-operating-points = <
+ /* ARM kHz SOC-PU uV */
+ 1200000 1275000
+ 996000 1250000
+ 852000 1250000
+ 792000 1175000
+ 396000 1175000
>;
clock-latency = <61036>; /* two CLK32 periods */
clocks = <&clks 104>, <&clks 6>, <&clks 16>,
@@ -74,7 +88,7 @@
#size-cells = <0>;
compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
reg = <0x02018000 0x4000>;
- interrupts = <0 35 0x04>;
+ interrupts = <0 35 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 116>, <&clks 116>;
clock-names = "ipg", "per";
status = "disabled";
@@ -125,7 +139,7 @@
sata: sata@02200000 {
compatible = "fsl,imx6q-ahci";
reg = <0x02200000 0x4000>;
- interrupts = <0 39 0x04>;
+ interrupts = <0 39 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 154>, <&clks 187>, <&clks 105>;
clock-names = "sata", "sata_ref", "ahb";
status = "disabled";
@@ -136,7 +150,8 @@
#size-cells = <0>;
compatible = "fsl,imx6q-ipu";
reg = <0x02800000 0x400000>;
- interrupts = <0 8 0x4 0 7 0x4>;
+ interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
+ <0 7 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 133>, <&clks 134>, <&clks 137>;
clock-names = "bus", "di0", "di1";
resets = <&src 4>;
diff --git a/arch/arm/boot/dts/imx6qdl-dfi-fs700-m60.dtsi b/arch/arm/boot/dts/imx6qdl-dfi-fs700-m60.dtsi
new file mode 100644
index 000000000000..25cf035dd36e
--- /dev/null
+++ b/arch/arm/boot/dts/imx6qdl-dfi-fs700-m60.dtsi
@@ -0,0 +1,199 @@
+/ {
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dummy_reg: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "dummy-supply";
+ };
+
+ reg_usb_otg_vbus: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio3 22 0>;
+ enable-active-high;
+ };
+ };
+
+ chosen {
+ linux,stdout-path = &uart1;
+ };
+};
+
+&ecspi3 {
+ fsl,spi-num-chipselects = <1>;
+ cs-gpios = <&gpio4 24 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ecspi3>;
+ status = "okay";
+
+ flash: m25p80@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "sst,sst25vf040b", "m25p80";
+ spi-max-frequency = <20000000>;
+ reg = <0>;
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ status = "okay";
+ phy-mode = "rgmii";
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx6qdl-dfi-fs700-m60 {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x80000000
+ MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x80000000 /* PMIC irq */
+ MX6QDL_PAD_EIM_D26__GPIO3_IO26 0x80000000 /* MAX11801 irq */
+ MX6QDL_PAD_NANDF_D5__GPIO2_IO05 0x000030b0 /* Backlight enable */
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_EIM_D16__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc2: usdhc2grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059
+ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059
+ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059
+ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059
+ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059
+ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059
+ MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000 /* card detect */
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
+
+ pinctrl_usdhc4: usdhc4grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059
+ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059
+ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059
+ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059
+ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059
+ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059
+ MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059
+ MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059
+ MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059
+ MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059
+ >;
+ };
+
+ pinctrl_ecspi3: ecspi3grp {
+ fsl,pins = <
+ MX6QDL_PAD_DISP0_DAT2__ECSPI3_MISO 0x100b1
+ MX6QDL_PAD_DISP0_DAT1__ECSPI3_MOSI 0x100b1
+ MX6QDL_PAD_DISP0_DAT0__ECSPI3_SCLK 0x100b1
+ MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24 0x80000000 /* SPI NOR chipselect */
+ >;
+ };
+ };
+};
+
+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&usbh1 {
+ status = "okay";
+};
+
+&usbotg {
+ vbus-supply = <&reg_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
+ dr_mode = "host";
+ status = "okay";
+};
+
+&usdhc2 { /* module slot */
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc2>;
+ cd-gpios = <&gpio2 2 0>;
+ status = "okay";
+};
+
+&usdhc3 { /* baseboard slot */
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc3>;
+};
+
+&usdhc4 { /* eMMC */
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc4>;
+ bus-width = <8>;
+ non-removable;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi
new file mode 100644
index 000000000000..98a422153ce7
--- /dev/null
+++ b/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi
@@ -0,0 +1,374 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/ {
+ /* these are used by bootloader for disabling nodes */
+ aliases {
+ can0 = &can1;
+ ethernet0 = &fec;
+ led0 = &led0;
+ led1 = &led1;
+ nand = &gpmi;
+ usb0 = &usbh1;
+ usb1 = &usbotg;
+ };
+
+ chosen {
+ bootargs = "console=ttymxc1,115200";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led0: user1 {
+ label = "user1";
+ gpios = <&gpio4 6 0>; /* 102 -> MX6_PANLEDG */
+ default-state = "on";
+ linux,default-trigger = "heartbeat";
+ };
+
+ led1: user2 {
+ label = "user2";
+ gpios = <&gpio4 7 0>; /* 103 -> MX6_PANLEDR */
+ default-state = "off";
+ };
+ };
+
+ memory {
+ reg = <0x10000000 0x20000000>;
+ };
+
+ pps {
+ compatible = "pps-gpio";
+ gpios = <&gpio1 26 0>;
+ status = "okay";
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_3p3v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_5p0v: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "5P0V";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ reg_usb_otg_vbus: regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio3 22 0>;
+ enable-active-high;
+ };
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-mode = "rgmii";
+ phy-reset-gpios = <&gpio1 30 0>;
+ status = "okay";
+};
+
+&gpmi {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpmi_nand>;
+ status = "okay";
+};
+
+&i2c1 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ eeprom1: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+ pagesize = <16>;
+ };
+
+ eeprom2: eeprom@51 {
+ compatible = "atmel,24c02";
+ reg = <0x51>;
+ pagesize = <16>;
+ };
+
+ eeprom3: eeprom@52 {
+ compatible = "atmel,24c02";
+ reg = <0x52>;
+ pagesize = <16>;
+ };
+
+ eeprom4: eeprom@53 {
+ compatible = "atmel,24c02";
+ reg = <0x53>;
+ pagesize = <16>;
+ };
+
+ gpio: pca9555@23 {
+ compatible = "nxp,pca9555";
+ reg = <0x23>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ hwmon: gsc@29 {
+ compatible = "gw,gsp";
+ reg = <0x29>;
+ };
+
+ rtc: ds1672@68 {
+ compatible = "dallas,ds1672";
+ reg = <0x68>;
+ };
+};
+
+&i2c2 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+
+ pmic: ltc3676@3c {
+ compatible = "ltc,ltc3676";
+ reg = <0x3c>;
+
+ regulators {
+ sw1_reg: ltc3676__sw1 {
+ regulator-min-microvolt = <1175000>;
+ regulator-max-microvolt = <1175000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw2_reg: ltc3676__sw2 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3_reg: ltc3676__sw3 {
+ regulator-min-microvolt = <1175000>;
+ regulator-max-microvolt = <1175000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw4_reg: ltc3676__sw4 {
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ ldo2_reg: ltc3676__ldo2 {
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ ldo4_reg: ltc3676__ldo4 {
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ };
+ };
+ };
+};
+
+&i2c3 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3>;
+ status = "okay";
+
+ videoin: adv7180@20 {
+ compatible = "adi,adv7180";
+ reg = <0x20>;
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx6qdl-gw51xx {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_A19__GPIO2_IO19 0x80000000 /* MEZZ_DIO0 */
+ MX6QDL_PAD_EIM_A20__GPIO2_IO18 0x80000000 /* MEZZ_DIO1 */
+ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 /* OTG_PWR_EN */
+ MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 /* GPS_PPS */
+ MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x80000000 /* PHY Reset */
+ MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x80000000 /* PCIE_RST# */
+ MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x80000000 /* user1 led */
+ MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x80000000 /* user2 led */
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ >;
+ };
+
+ pinctrl_gpmi_nand: gpminandgrp {
+ fsl,pins = <
+ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
+ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1
+ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1
+ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000
+ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1
+ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1
+ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1
+ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1
+ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1
+ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1
+ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1
+ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1
+ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1
+ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1
+ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1
+ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
+ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1
+ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart3: uart3grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x1b0b1
+ MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart5: uart5grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x1b0b1
+ MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ >;
+ };
+ };
+};
+
+&pcie {
+ reset-gpio = <&gpio1 0 0>;
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ status = "okay";
+};
+
+&uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart3>;
+ status = "okay";
+};
+
+&uart5 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart5>;
+ status = "okay";
+};
+
+&usbotg {
+ vbus-supply = <&reg_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
+ status = "okay";
+};
+
+&usbh1 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi
new file mode 100644
index 000000000000..8e99c9a9bc76
--- /dev/null
+++ b/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi
@@ -0,0 +1,490 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/ {
+ /* these are used by bootloader for disabling nodes */
+ aliases {
+ ethernet0 = &fec;
+ led0 = &led0;
+ led1 = &led1;
+ led2 = &led2;
+ nand = &gpmi;
+ ssi0 = &ssi1;
+ usb0 = &usbh1;
+ usb1 = &usbotg;
+ usdhc2 = &usdhc3;
+ };
+
+ chosen {
+ bootargs = "console=ttymxc1,115200";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led0: user1 {
+ label = "user1";
+ gpios = <&gpio4 6 0>; /* 102 -> MX6_PANLEDG */
+ default-state = "on";
+ linux,default-trigger = "heartbeat";
+ };
+
+ led1: user2 {
+ label = "user2";
+ gpios = <&gpio4 7 0>; /* 103 -> MX6_PANLEDR */
+ default-state = "off";
+ };
+
+ led2: user3 {
+ label = "user3";
+ gpios = <&gpio4 15 1>; /* 111 - MX6_LOCLED# */
+ default-state = "off";
+ };
+ };
+
+ memory {
+ reg = <0x10000000 0x20000000>;
+ };
+
+ pps {
+ compatible = "pps-gpio";
+ gpios = <&gpio1 26 0>;
+ status = "okay";
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_1p0v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "1P0V";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ /* remove this fixed regulator once ltc3676__sw2 driver available */
+ reg_1p8v: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "1P8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ reg_3p3v: regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_5p0v: regulator@3 {
+ compatible = "regulator-fixed";
+ reg = <3>;
+ regulator-name = "5P0V";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ reg_usb_otg_vbus: regulator@4 {
+ compatible = "regulator-fixed";
+ reg = <4>;
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio3 22 0>;
+ enable-active-high;
+ };
+ };
+
+ sound {
+ compatible = "fsl,imx6q-sabrelite-sgtl5000",
+ "fsl,imx-audio-sgtl5000";
+ model = "imx6q-sabrelite-sgtl5000";
+ ssi-controller = <&ssi1>;
+ audio-codec = <&codec>;
+ audio-routing =
+ "MIC_IN", "Mic Jack",
+ "Mic Jack", "Mic Bias",
+ "Headphone Jack", "HP_OUT";
+ mux-int-port = <1>;
+ mux-ext-port = <4>;
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>;
+ status = "okay";
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-mode = "rgmii";
+ phy-reset-gpios = <&gpio1 30 0>;
+ status = "okay";
+};
+
+&gpmi {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpmi_nand>;
+ status = "okay";
+};
+
+&i2c1 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ eeprom1: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+ pagesize = <16>;
+ };
+
+ eeprom2: eeprom@51 {
+ compatible = "atmel,24c02";
+ reg = <0x51>;
+ pagesize = <16>;
+ };
+
+ eeprom3: eeprom@52 {
+ compatible = "atmel,24c02";
+ reg = <0x52>;
+ pagesize = <16>;
+ };
+
+ eeprom4: eeprom@53 {
+ compatible = "atmel,24c02";
+ reg = <0x53>;
+ pagesize = <16>;
+ };
+
+ gpio: pca9555@23 {
+ compatible = "nxp,pca9555";
+ reg = <0x23>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ hwmon: gsc@29 {
+ compatible = "gw,gsp";
+ reg = <0x29>;
+ };
+
+ rtc: ds1672@68 {
+ compatible = "dallas,ds1672";
+ reg = <0x68>;
+ };
+};
+
+&i2c2 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+
+ pciswitch: pex8609@3f {
+ compatible = "plx,pex8609";
+ reg = <0x3f>;
+ };
+
+ pmic: ltc3676@3c {
+ compatible = "ltc,ltc3676";
+ reg = <0x3c>;
+
+ regulators {
+ sw1_reg: ltc3676__sw1 {
+ regulator-min-microvolt = <1175000>;
+ regulator-max-microvolt = <1175000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw2_reg: ltc3676__sw2 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3_reg: ltc3676__sw3 {
+ regulator-min-microvolt = <1175000>;
+ regulator-max-microvolt = <1175000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw4_reg: ltc3676__sw4 {
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ ldo2_reg: ltc3676__ldo2 {
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ ldo3_reg: ltc3676__ldo3 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ ldo4_reg: ltc3676__ldo4 {
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ };
+ };
+ };
+};
+
+&i2c3 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3>;
+ status = "okay";
+
+ accelerometer: fxos8700@1e {
+ compatible = "fsl,fxos8700";
+ reg = <0x13>;
+ };
+
+ codec: sgtl5000@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ clocks = <&clks 169>;
+ VDDA-supply = <&reg_1p8v>;
+ VDDIO-supply = <&reg_3p3v>;
+ };
+
+ touchscreen: egalax_ts@04 {
+ compatible = "eeti,egalax_ts";
+ reg = <0x04>;
+ interrupt-parent = <&gpio7>;
+ interrupts = <12 2>; /* gpio7_12 active low */
+ wakeup-gpios = <&gpio7 12 0>;
+ };
+
+ videoin: adv7180@20 {
+ compatible = "adi,adv7180";
+ reg = <0x20>;
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx6qdl-gw52xx {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_A19__GPIO2_IO19 0x80000000 /* MEZZ_DIO0 */
+ MX6QDL_PAD_EIM_A20__GPIO2_IO18 0x80000000 /* MEZZ_DIO1 */
+ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 /* OTG_PWR_EN */
+ MX6QDL_PAD_EIM_D31__GPIO3_IO31 0x80000000 /* VIDDEC_PDN# */
+ MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x80000000 /* PHY Reset */
+ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 /* PCIE_RST# */
+ MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x80000000 /* GPS_PWDN */
+ MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 /* GPS_PPS */
+ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x000130b0 /* AUD4_MCK */
+ MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 /* USB_SEL_PCI */
+ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 /* TOUCH_IRQ# */
+ MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x80000000 /* user1 led */
+ MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x80000000 /* user2 led */
+ MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x80000000 /* user3 led */
+ MX6QDL_PAD_SD2_CMD__GPIO1_IO11 0x80000000 /* LVDS_TCH# */
+ MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x80000000 /* SD3_CD# */
+ MX6QDL_PAD_SD4_DAT3__GPIO2_IO11 0x80000000 /* UART2_EN# */
+ >;
+ };
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0
+ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0
+ MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0
+ MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ >;
+ };
+
+ pinctrl_gpmi_nand: gpminandgrp {
+ fsl,pins = <
+ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
+ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1
+ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1
+ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000
+ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1
+ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1
+ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1
+ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1
+ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1
+ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1
+ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1
+ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1
+ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1
+ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1
+ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1
+ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
+ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1
+ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart5: uart5grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x1b0b1
+ MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
+ };
+};
+
+&ldb {
+ status = "okay";
+ lvds-channel@0 {
+ crtcs = <&ipu1 0>, <&ipu1 1>;
+ };
+};
+
+&pcie {
+ reset-gpio = <&gpio1 29 0>;
+ status = "okay";
+};
+
+&ssi1 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ status = "okay";
+};
+
+&uart5 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart5>;
+ status = "okay";
+};
+
+&usbotg {
+ vbus-supply = <&reg_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
+ status = "okay";
+};
+
+&usbh1 {
+ status = "okay";
+};
+
+&usdhc3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ cd-gpios = <&gpio7 0 0>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi
new file mode 100644
index 000000000000..c8e5ae06deaf
--- /dev/null
+++ b/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi
@@ -0,0 +1,553 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/ {
+ /* these are used by bootloader for disabling nodes */
+ aliases {
+ can0 = &can1;
+ ethernet0 = &fec;
+ ethernet1 = &eth1;
+ led0 = &led0;
+ led1 = &led1;
+ led2 = &led2;
+ nand = &gpmi;
+ sky2 = &eth1;
+ ssi0 = &ssi1;
+ usb0 = &usbh1;
+ usb1 = &usbotg;
+ usdhc2 = &usdhc3;
+ };
+
+ chosen {
+ bootargs = "console=ttymxc1,115200";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led0: user1 {
+ label = "user1";
+ gpios = <&gpio4 6 0>; /* 102 -> MX6_PANLEDG */
+ default-state = "on";
+ linux,default-trigger = "heartbeat";
+ };
+
+ led1: user2 {
+ label = "user2";
+ gpios = <&gpio4 7 0>; /* 103 -> MX6_PANLEDR */
+ default-state = "off";
+ };
+
+ led2: user3 {
+ label = "user3";
+ gpios = <&gpio4 15 1>; /* 111 -> MX6_LOCLED# */
+ default-state = "off";
+ };
+ };
+
+ memory {
+ reg = <0x10000000 0x40000000>;
+ };
+
+ pps {
+ compatible = "pps-gpio";
+ gpios = <&gpio1 26 0>;
+ status = "okay";
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_1p0v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "1P0V";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ /* remove when pmic 1p8 regulator available */
+ reg_1p8v: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "1P8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ reg_3p3v: regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_usb_h1_vbus: regulator@3 {
+ compatible = "regulator-fixed";
+ reg = <3>;
+ regulator-name = "usb_h1_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ reg_usb_otg_vbus: regulator@4 {
+ compatible = "regulator-fixed";
+ reg = <4>;
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio3 22 0>;
+ enable-active-high;
+ };
+ };
+
+ sound {
+ compatible = "fsl,imx6q-sabrelite-sgtl5000",
+ "fsl,imx-audio-sgtl5000";
+ model = "imx6q-sabrelite-sgtl5000";
+ ssi-controller = <&ssi1>;
+ audio-codec = <&codec>;
+ audio-routing =
+ "MIC_IN", "Mic Jack",
+ "Mic Jack", "Mic Bias",
+ "Headphone Jack", "HP_OUT";
+ mux-int-port = <1>;
+ mux-ext-port = <4>;
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>;
+ status = "okay";
+};
+
+&can1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_flexcan1>;
+ status = "okay";
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-mode = "rgmii";
+ phy-reset-gpios = <&gpio1 30 0>;
+ status = "okay";
+};
+
+&gpmi {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpmi_nand>;
+ status = "okay";
+};
+
+&i2c1 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ eeprom1: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+ pagesize = <16>;
+ };
+
+ eeprom2: eeprom@51 {
+ compatible = "atmel,24c02";
+ reg = <0x51>;
+ pagesize = <16>;
+ };
+
+ eeprom3: eeprom@52 {
+ compatible = "atmel,24c02";
+ reg = <0x52>;
+ pagesize = <16>;
+ };
+
+ eeprom4: eeprom@53 {
+ compatible = "atmel,24c02";
+ reg = <0x53>;
+ pagesize = <16>;
+ };
+
+ gpio: pca9555@23 {
+ compatible = "nxp,pca9555";
+ reg = <0x23>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ hwmon: gsc@29 {
+ compatible = "gw,gsp";
+ reg = <0x29>;
+ };
+
+ rtc: ds1672@68 {
+ compatible = "dallas,ds1672";
+ reg = <0x68>;
+ };
+};
+
+&i2c2 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+
+ pciclkgen: si53156@6b {
+ compatible = "sil,si53156";
+ reg = <0x6b>;
+ };
+
+ pciswitch: pex8606@3f {
+ compatible = "plx,pex8606";
+ reg = <0x3f>;
+ };
+
+ pmic: ltc3676@3c {
+ compatible = "ltc,ltc3676";
+ reg = <0x3c>;
+
+ regulators {
+ /* VDD_SOC */
+ sw1_reg: ltc3676__sw1 {
+ regulator-min-microvolt = <1175000>;
+ regulator-max-microvolt = <1175000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ /* VDD_1P8 */
+ sw2_reg: ltc3676__sw2 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ /* VDD_ARM */
+ sw3_reg: ltc3676__sw3 {
+ regulator-min-microvolt = <1175000>;
+ regulator-max-microvolt = <1175000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ /* VDD_DDR */
+ sw4_reg: ltc3676__sw4 {
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ /* VDD_2P5 */
+ ldo2_reg: ltc3676__ldo2 {
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ /* VDD_1P8 */
+ ldo3_reg: ltc3676__ldo3 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ /* VDD_HIGH */
+ ldo4_reg: ltc3676__ldo4 {
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ };
+ };
+ };
+};
+
+&i2c3 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3>;
+ status = "okay";
+
+ accelerometer: fxos8700@1e {
+ compatible = "fsl,fxos8700";
+ reg = <0x1e>;
+ };
+
+ codec: sgtl5000@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ clocks = <&clks 201>;
+ VDDA-supply = <&reg_1p8v>;
+ VDDIO-supply = <&reg_3p3v>;
+ };
+
+ hdmiin: adv7611@4c {
+ compatible = "adi,adv7611";
+ reg = <0x4c>;
+ };
+
+ touchscreen: egalax_ts@04 {
+ compatible = "eeti,egalax_ts";
+ reg = <0x04>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <11 2>; /* gpio1_11 active low */
+ wakeup-gpios = <&gpio1 11 0>;
+ };
+
+ videoout: adv7393@2a {
+ compatible = "adi,adv7393";
+ reg = <0x2a>;
+ };
+
+ videoin: adv7180@20 {
+ compatible = "adi,adv7180";
+ reg = <0x20>;
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx6qdl-gw53xx {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_A19__GPIO2_IO19 0x80000000 /* PCIE6EXP_DIO0 */
+ MX6QDL_PAD_EIM_A20__GPIO2_IO18 0x80000000 /* PCIE6EXP_DIO1 */
+ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 /* OTG_PWR_EN */
+ MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x80000000 /* GPS_SHDN */
+ MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 /* GPS_PPS */
+ MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 /* PCIE IRQ */
+ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 /* PCIE RST */
+ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x000130b0 /* AUD4_MCK */
+ MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 /* CAN_STBY */
+ MX6QDL_PAD_GPIO_8__GPIO1_IO08 0x80000000 /* PMIC_IRQ# */
+ MX6QDL_PAD_GPIO_9__GPIO1_IO09 0x80000000 /* HUB_RST# */
+ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 /* PCIE_WDIS# */
+ MX6QDL_PAD_GPIO_19__GPIO4_IO05 0x80000000 /* ACCEL_IRQ# */
+ MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x80000000 /* user1 led */
+ MX6QDL_PAD_KEY_COL4__GPIO4_IO14 0x80000000 /* USBOTG_OC# */
+ MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x80000000 /* user2 led */
+ MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x80000000 /* user3 led */
+ MX6QDL_PAD_SD2_CMD__GPIO1_IO11 0x80000000 /* TOUCH_IRQ# */
+ MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x80000000 /* SD3_DET# */
+ >;
+ };
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0
+ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0
+ MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0
+ MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ >;
+ };
+
+ pinctrl_flexcan1: flexcan1grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x80000000
+ MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x80000000
+ >;
+ };
+
+ pinctrl_gpmi_nand: gpminandgrp {
+ fsl,pins = <
+ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
+ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1
+ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1
+ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000
+ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1
+ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1
+ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1
+ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1
+ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1
+ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1
+ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1
+ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1
+ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1
+ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1
+ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1
+ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
+ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1
+ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart5: uart5grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x1b0b1
+ MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
+ };
+};
+
+&ldb {
+ status = "okay";
+
+ lvds-channel@1 {
+ fsl,data-mapping = "spwg";
+ fsl,data-width = <18>;
+ status = "okay";
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: hsd100pxn1 {
+ clock-frequency = <65000000>;
+ hactive = <1024>;
+ vactive = <768>;
+ hback-porch = <220>;
+ hfront-porch = <40>;
+ vback-porch = <21>;
+ vfront-porch = <7>;
+ hsync-len = <60>;
+ vsync-len = <10>;
+ };
+ };
+ };
+};
+
+&pcie {
+ reset-gpio = <&gpio1 29 0>;
+ status = "okay";
+
+ eth1: sky2@8 { /* MAC/PHY on bus 8 */
+ compatible = "marvell,sky2";
+ };
+};
+
+&ssi1 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ status = "okay";
+};
+
+&uart5 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart5>;
+ status = "okay";
+};
+
+&usbotg {
+ vbus-supply = <&reg_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
+ status = "okay";
+};
+
+&usbh1 {
+ vbus-supply = <&reg_usb_h1_vbus>;
+ status = "okay";
+};
+
+&usdhc3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ cd-gpios = <&gpio7 0 0>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi
new file mode 100644
index 000000000000..2795dfc8c926
--- /dev/null
+++ b/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi
@@ -0,0 +1,580 @@
+/*
+ * Copyright 2013 Gateworks Corporation
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/ {
+ /* these are used by bootloader for disabling nodes */
+ aliases {
+ can0 = &can1;
+ ethernet0 = &fec;
+ ethernet1 = &eth1;
+ led0 = &led0;
+ led1 = &led1;
+ led2 = &led2;
+ nand = &gpmi;
+ sky2 = &eth1;
+ ssi0 = &ssi1;
+ usb0 = &usbh1;
+ usb1 = &usbotg;
+ usdhc2 = &usdhc3;
+ };
+
+ chosen {
+ bootargs = "console=ttymxc1,115200";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led0: user1 {
+ label = "user1";
+ gpios = <&gpio4 6 0>; /* 102 -> MX6_PANLEDG */
+ default-state = "on";
+ linux,default-trigger = "heartbeat";
+ };
+
+ led1: user2 {
+ label = "user2";
+ gpios = <&gpio4 7 0>; /* 103 -> MX6_PANLEDR */
+ default-state = "off";
+ };
+
+ led2: user3 {
+ label = "user3";
+ gpios = <&gpio4 15 1>; /* 111 -> MX6_LOCLED# */
+ default-state = "off";
+ };
+ };
+
+ memory {
+ reg = <0x10000000 0x40000000>;
+ };
+
+ pps {
+ compatible = "pps-gpio";
+ gpios = <&gpio1 26 0>;
+ status = "okay";
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_1p0v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "1P0V";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ reg_3p3v: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_usb_h1_vbus: regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "usb_h1_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ reg_usb_otg_vbus: regulator@3 {
+ compatible = "regulator-fixed";
+ reg = <3>;
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio3 22 0>;
+ enable-active-high;
+ };
+ };
+
+ sound {
+ compatible = "fsl,imx6q-sabrelite-sgtl5000",
+ "fsl,imx-audio-sgtl5000";
+ model = "imx6q-sabrelite-sgtl5000";
+ ssi-controller = <&ssi1>;
+ audio-codec = <&codec>;
+ audio-routing =
+ "MIC_IN", "Mic Jack",
+ "Mic Jack", "Mic Bias",
+ "Headphone Jack", "HP_OUT";
+ mux-int-port = <1>;
+ mux-ext-port = <4>;
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>; /* AUD4<->sgtl5000 */
+ status = "okay";
+};
+
+&can1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_flexcan1>;
+ status = "okay";
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-mode = "rgmii";
+ phy-reset-gpios = <&gpio1 30 0>;
+ status = "okay";
+};
+
+&gpmi {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpmi_nand>;
+ status = "okay";
+};
+
+&i2c1 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ eeprom1: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+ pagesize = <16>;
+ };
+
+ eeprom2: eeprom@51 {
+ compatible = "atmel,24c02";
+ reg = <0x51>;
+ pagesize = <16>;
+ };
+
+ eeprom3: eeprom@52 {
+ compatible = "atmel,24c02";
+ reg = <0x52>;
+ pagesize = <16>;
+ };
+
+ eeprom4: eeprom@53 {
+ compatible = "atmel,24c02";
+ reg = <0x53>;
+ pagesize = <16>;
+ };
+
+ gpio: pca9555@23 {
+ compatible = "nxp,pca9555";
+ reg = <0x23>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ hwmon: gsc@29 {
+ compatible = "gw,gsp";
+ reg = <0x29>;
+ };
+
+ rtc: ds1672@68 {
+ compatible = "dallas,ds1672";
+ reg = <0x68>;
+ };
+};
+
+&i2c2 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+
+ pmic: pfuze100@08 {
+ compatible = "fsl,pfuze100";
+ reg = <0x08>;
+
+ regulators {
+ sw1a_reg: sw1ab {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+ };
+
+ sw1c_reg: sw1c {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+ };
+
+ sw2_reg: sw2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3950000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3a_reg: sw3a {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3b_reg: sw3b {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw4_reg: sw4 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ swbst_reg: swbst {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5150000>;
+ };
+
+ snvs_reg: vsnvs {
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vref_reg: vrefddr {
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vgen1_reg: vgen1 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen2_reg: vgen2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen3_reg: vgen3 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ vgen4_reg: vgen4 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen5_reg: vgen5 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen6_reg: vgen6 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+ };
+
+ pciswitch: pex8609@3f {
+ compatible = "plx,pex8609";
+ reg = <0x3f>;
+ };
+
+ pciclkgen: si52147@6b {
+ compatible = "sil,si52147";
+ reg = <0x6b>;
+ };
+};
+
+&i2c3 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3>;
+ status = "okay";
+
+ accelerometer: fxos8700@1e {
+ compatible = "fsl,fxos8700";
+ reg = <0x1e>;
+ };
+
+ codec: sgtl5000@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ clocks = <&clks 201>;
+ VDDA-supply = <&sw4_reg>;
+ VDDIO-supply = <&reg_3p3v>;
+ };
+
+ hdmiin: adv7611@4c {
+ compatible = "adi,adv7611";
+ reg = <0x4c>;
+ };
+
+ touchscreen: egalax_ts@04 {
+ compatible = "eeti,egalax_ts";
+ reg = <0x04>;
+ interrupt-parent = <&gpio7>;
+ interrupts = <12 2>; /* gpio7_12 active low */
+ wakeup-gpios = <&gpio7 12 0>;
+ };
+
+ videoout: adv7393@2a {
+ compatible = "adi,adv7393";
+ reg = <0x2a>;
+ };
+
+ videoin: adv7180@20 {
+ compatible = "adi,adv7180";
+ reg = <0x20>;
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx6qdl-gw54xx {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 /* OTG_PWR_EN */
+ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x80000000 /* SPINOR_CS0# */
+ MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 /* GPS_PPS */
+ MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 /* PCIE IRQ */
+ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 /* PCIE RST */
+ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x000130b0 /* AUD4_MCK */
+ MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 /* CAN_STBY */
+ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 /* TOUCH_IRQ# */
+ MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x80000000 /* user1 led */
+ MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x80000000 /* user2 led */
+ MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x80000000 /* user3 led */
+ MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x80000000 /* USBHUB_RST# */
+ MX6QDL_PAD_SD1_DAT3__GPIO1_IO21 0x80000000 /* MIPI_DIO */
+ >;
+ };
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0
+ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0
+ MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0
+ MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ >;
+ };
+
+ pinctrl_flexcan1: flexcan1grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x80000000
+ MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x80000000
+ >;
+ };
+
+ pinctrl_gpmi_nand: gpminandgrp {
+ fsl,pins = <
+ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
+ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1
+ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1
+ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000
+ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1
+ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1
+ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1
+ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1
+ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1
+ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1
+ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1
+ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1
+ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1
+ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1
+ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1
+ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
+ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1
+ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart5: uart5grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x1b0b1
+ MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
+ };
+};
+
+&ldb {
+ status = "okay";
+
+ lvds-channel@1 {
+ fsl,data-mapping = "spwg";
+ fsl,data-width = <18>;
+ status = "okay";
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: hsd100pxn1 {
+ clock-frequency = <65000000>;
+ hactive = <1024>;
+ vactive = <768>;
+ hback-porch = <220>;
+ hfront-porch = <40>;
+ vback-porch = <21>;
+ vfront-porch = <7>;
+ hsync-len = <60>;
+ vsync-len = <10>;
+ };
+ };
+ };
+};
+
+&pcie {
+ reset-gpio = <&gpio1 29 0>;
+ status = "okay";
+
+ eth1: sky2@8 { /* MAC/PHY on bus 8 */
+ compatible = "marvell,sky2";
+ };
+};
+
+&ssi1 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&ssi2 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ status = "okay";
+};
+
+&uart5 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart5>;
+ status = "okay";
+};
+
+&usbotg {
+ vbus-supply = <&reg_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
+ status = "okay";
+};
+
+&usbh1 {
+ vbus-supply = <&reg_usb_h1_vbus>;
+ status = "okay";
+};
+
+&usdhc3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ cd-gpios = <&gpio7 0 0>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi b/arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi
new file mode 100644
index 000000000000..99be301b5232
--- /dev/null
+++ b/arch/arm/boot/dts/imx6qdl-nitrogen6x.dtsi
@@ -0,0 +1,422 @@
+/*
+ * Copyright 2013 Boundary Devices, Inc.
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+ memory {
+ reg = <0x10000000 0x40000000>;
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_2p5v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "2P5V";
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+ regulator-always-on;
+ };
+
+ reg_3p3v: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_usb_otg_vbus: regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio3 22 0>;
+ enable-active-high;
+ };
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpio_keys>;
+
+ power {
+ label = "Power Button";
+ gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_POWER>;
+ gpio-key,wakeup;
+ };
+
+ menu {
+ label = "Menu";
+ gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_MENU>;
+ };
+
+ home {
+ label = "Home";
+ gpios = <&gpio2 4 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_HOME>;
+ };
+
+ back {
+ label = "Back";
+ gpios = <&gpio2 2 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_BACK>;
+ };
+
+ volume-up {
+ label = "Volume Up";
+ gpios = <&gpio7 13 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_VOLUMEUP>;
+ };
+
+ volume-down {
+ label = "Volume Down";
+ gpios = <&gpio4 5 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_VOLUMEDOWN>;
+ };
+ };
+
+ sound {
+ compatible = "fsl,imx6q-nitrogen6x-sgtl5000",
+ "fsl,imx-audio-sgtl5000";
+ model = "imx6q-nitrogen6x-sgtl5000";
+ ssi-controller = <&ssi1>;
+ audio-codec = <&codec>;
+ audio-routing =
+ "MIC_IN", "Mic Jack",
+ "Mic Jack", "Mic Bias",
+ "Headphone Jack", "HP_OUT";
+ mux-int-port = <1>;
+ mux-ext-port = <3>;
+ };
+
+ backlight_lcd {
+ compatible = "pwm-backlight";
+ pwms = <&pwm1 0 5000000>;
+ brightness-levels = <0 4 8 16 32 64 128 255>;
+ default-brightness-level = <7>;
+ power-supply = <&reg_3p3v>;
+ status = "okay";
+ };
+
+ backlight_lvds {
+ compatible = "pwm-backlight";
+ pwms = <&pwm4 0 5000000>;
+ brightness-levels = <0 4 8 16 32 64 128 255>;
+ default-brightness-level = <7>;
+ power-supply = <&reg_3p3v>;
+ status = "okay";
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>;
+ status = "okay";
+};
+
+&ecspi1 {
+ fsl,spi-num-chipselects = <1>;
+ cs-gpios = <&gpio3 19 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ecspi1>;
+ status = "okay";
+
+ flash: m25p80@0 {
+ compatible = "sst,sst25vf016b";
+ spi-max-frequency = <20000000>;
+ reg = <0>;
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-mode = "rgmii";
+ phy-reset-gpios = <&gpio1 27 0>;
+ txen-skew-ps = <0>;
+ txc-skew-ps = <3000>;
+ rxdv-skew-ps = <0>;
+ rxc-skew-ps = <3000>;
+ rxd0-skew-ps = <0>;
+ rxd1-skew-ps = <0>;
+ rxd2-skew-ps = <0>;
+ rxd3-skew-ps = <0>;
+ txd0-skew-ps = <0>;
+ txd1-skew-ps = <0>;
+ txd2-skew-ps = <0>;
+ txd3-skew-ps = <0>;
+ interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>,
+ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>;
+ status = "okay";
+};
+
+&i2c1 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ codec: sgtl5000@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ clocks = <&clks 201>;
+ VDDA-supply = <&reg_2p5v>;
+ VDDIO-supply = <&reg_3p3v>;
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx6q-nitrogen6x {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ /* SGTL5000 sys_mclk */
+ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x030b0
+ >;
+ };
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0
+ MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0
+ MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0
+ MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0
+ >;
+ };
+
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1
+ MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1
+ MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1
+ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x000b1 /* CS */
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x100b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x100b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x100b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x100b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x100b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x100b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x100b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x100b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x100b0
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ /* Phy reset */
+ MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x000b0
+ MX6QDL_PAD_GPIO_6__ENET_IRQ 0x000b1
+ >;
+ };
+
+ pinctrl_gpio_keys: gpio_keysgrp {
+ fsl,pins = <
+ /* Power Button */
+ MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x1b0b0
+ /* Menu Button */
+ MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x1b0b0
+ /* Home Button */
+ MX6QDL_PAD_NANDF_D4__GPIO2_IO04 0x1b0b0
+ /* Back Button */
+ MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x1b0b0
+ /* Volume Up Button */
+ MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x1b0b0
+ /* Volume Down Button */
+ MX6QDL_PAD_GPIO_19__GPIO4_IO05 0x1b0b0
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
+ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_pwm1: pwm1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1
+ >;
+ };
+
+ pinctrl_pwm3: pwm3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_DAT1__PWM3_OUT 0x1b0b1
+ >;
+ };
+
+ pinctrl_pwm4: pwm4grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_CMD__PWM4_OUT 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1
+ MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0
+ /* power enable, high active */
+ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x000b0
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x1b0b0 /* CD */
+ >;
+ };
+
+ pinctrl_usdhc4: usdhc4grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059
+ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059
+ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059
+ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059
+ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059
+ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059
+ MX6QDL_PAD_NANDF_D6__GPIO2_IO06 0x1b0b0 /* CD */
+ >;
+ };
+ };
+};
+
+&ldb {
+ status = "okay";
+
+ lvds-channel@0 {
+ fsl,data-mapping = "spwg";
+ fsl,data-width = <18>;
+ status = "okay";
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: hsd100pxn1 {
+ clock-frequency = <65000000>;
+ hactive = <1024>;
+ vactive = <768>;
+ hback-porch = <220>;
+ hfront-porch = <40>;
+ vback-porch = <21>;
+ vfront-porch = <7>;
+ hsync-len = <60>;
+ vsync-len = <10>;
+ };
+ };
+ };
+};
+
+&pcie {
+ status = "okay";
+};
+
+&pwm1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm1>;
+ status = "okay";
+};
+
+&pwm3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm3>;
+ status = "okay";
+};
+
+&pwm4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm4>;
+ status = "okay";
+};
+
+&ssi1 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ status = "okay";
+};
+
+&usbh1 {
+ status = "okay";
+};
+
+&usbotg {
+ vbus-supply = <&reg_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
+ status = "okay";
+};
+
+&usdhc3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ cd-gpios = <&gpio7 0 0>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
+
+&usdhc4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc4>;
+ cd-gpios = <&gpio2 6 0>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index ff6f1e8f2dd9..009abd69385d 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -10,17 +10,46 @@
* http://www.gnu.org/copyleft/gpl.html
*/
+#include <dt-bindings/gpio/gpio.h>
+
/ {
memory {
reg = <0x10000000 0x80000000>;
};
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpio_leds>;
+
+ user {
+ label = "debug";
+ gpios = <&gpio5 15 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ sound-spdif {
+ compatible = "fsl,imx-audio-spdif",
+ "fsl,imx-sabreauto-spdif";
+ model = "imx-spdif";
+ spdif-controller = <&spdif>;
+ spdif-in;
+ };
+
+ backlight {
+ compatible = "pwm-backlight";
+ pwms = <&pwm3 0 5000000>;
+ brightness-levels = <0 4 8 16 32 64 128 255>;
+ default-brightness-level = <7>;
+ status = "okay";
+ };
};
&ecspi1 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio3 19 0>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi1_1 &pinctrl_ecspi1_sabreauto>;
+ pinctrl-0 = <&pinctrl_ecspi1 &pinctrl_ecspi1_cs>;
status = "disabled"; /* pin conflict with WEIM NOR */
flash: m25p80@0 {
@@ -34,22 +63,130 @@
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_enet_2>;
+ pinctrl-0 = <&pinctrl_enet>;
phy-mode = "rgmii";
+ interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>,
+ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>;
status = "okay";
};
&gpmi {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_gpmi_nand_1>;
+ pinctrl-0 = <&pinctrl_gpmi_nand>;
+ status = "okay";
+};
+
+&i2c2 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
+
+ pmic: pfuze100@08 {
+ compatible = "fsl,pfuze100";
+ reg = <0x08>;
+
+ regulators {
+ sw1a_reg: sw1ab {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+ };
+
+ sw1c_reg: sw1c {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+ };
+
+ sw2_reg: sw2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3a_reg: sw3a {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3b_reg: sw3b {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw4_reg: sw4 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ swbst_reg: swbst {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5150000>;
+ };
+
+ snvs_reg: vsnvs {
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vref_reg: vrefddr {
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vgen1_reg: vgen1 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen2_reg: vgen2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen3_reg: vgen3 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ vgen4_reg: vgen4 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen5_reg: vgen5 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen6_reg: vgen6 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+ };
};
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- hog {
+ imx6qdl-sabreauto {
pinctrl_hog: hoggrp {
fsl,pins = <
MX6QDL_PAD_NANDF_CS2__GPIO6_IO15 0x80000000
@@ -57,28 +194,245 @@
MX6QDL_PAD_GPIO_18__SD3_VSELECT 0x17059
>;
};
- };
- ecspi1 {
- pinctrl_ecspi1_sabreauto: ecspi1-sabreauto {
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1
+ MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1
+ MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1
+ >;
+ };
+
+ pinctrl_ecspi1_cs: ecspi1cs {
fsl,pins = <
MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x80000000
>;
};
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL1__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_KEY_COL2__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_GPIO_6__ENET_IRQ 0x000b1
+ >;
+ };
+
+ pinctrl_gpio_leds: gpioledsgrp {
+ fsl,pins = <
+ MX6QDL_PAD_DISP0_DAT21__GPIO5_IO15 0x80000000
+ >;
+ };
+
+ pinctrl_gpmi_nand: gpminandgrp {
+ fsl,pins = <
+ MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
+ MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1
+ MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1
+ MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000
+ MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1
+ MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1
+ MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1
+ MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1
+ MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1
+ MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1
+ MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1
+ MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1
+ MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1
+ MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1
+ MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1
+ MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1
+ MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_pwm3: pwm1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1
+ >;
+ };
+
+ pinctrl_spdif: spdifgrp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x1b0b0
+ >;
+ };
+
+ pinctrl_uart4: uart4grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1
+ MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059
+ MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059
+ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059
+ MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3_100mhz: usdhc3grp100mhz {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170b9
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100b9
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170b9
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170b9
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170b9
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170b9
+ MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x170b9
+ MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x170b9
+ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x170b9
+ MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x170b9
+ >;
+ };
+
+ pinctrl_usdhc3_200mhz: usdhc3grp200mhz {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170f9
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100f9
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170f9
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170f9
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170f9
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170f9
+ MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x170f9
+ MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x170f9
+ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x170f9
+ MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x170f9
+ >;
+ };
+
+ pinctrl_weim_cs0: weimcs0grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_CS0__EIM_CS0_B 0xb0b1
+ >;
+ };
+
+ pinctrl_weim_nor: weimnorgrp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_OE__EIM_OE_B 0xb0b1
+ MX6QDL_PAD_EIM_RW__EIM_RW 0xb0b1
+ MX6QDL_PAD_EIM_WAIT__EIM_WAIT_B 0xb060
+ MX6QDL_PAD_EIM_D16__EIM_DATA16 0x1b0b0
+ MX6QDL_PAD_EIM_D17__EIM_DATA17 0x1b0b0
+ MX6QDL_PAD_EIM_D18__EIM_DATA18 0x1b0b0
+ MX6QDL_PAD_EIM_D19__EIM_DATA19 0x1b0b0
+ MX6QDL_PAD_EIM_D20__EIM_DATA20 0x1b0b0
+ MX6QDL_PAD_EIM_D21__EIM_DATA21 0x1b0b0
+ MX6QDL_PAD_EIM_D22__EIM_DATA22 0x1b0b0
+ MX6QDL_PAD_EIM_D23__EIM_DATA23 0x1b0b0
+ MX6QDL_PAD_EIM_D24__EIM_DATA24 0x1b0b0
+ MX6QDL_PAD_EIM_D25__EIM_DATA25 0x1b0b0
+ MX6QDL_PAD_EIM_D26__EIM_DATA26 0x1b0b0
+ MX6QDL_PAD_EIM_D27__EIM_DATA27 0x1b0b0
+ MX6QDL_PAD_EIM_D28__EIM_DATA28 0x1b0b0
+ MX6QDL_PAD_EIM_D29__EIM_DATA29 0x1b0b0
+ MX6QDL_PAD_EIM_D30__EIM_DATA30 0x1b0b0
+ MX6QDL_PAD_EIM_D31__EIM_DATA31 0x1b0b0
+ MX6QDL_PAD_EIM_A23__EIM_ADDR23 0xb0b1
+ MX6QDL_PAD_EIM_A22__EIM_ADDR22 0xb0b1
+ MX6QDL_PAD_EIM_A21__EIM_ADDR21 0xb0b1
+ MX6QDL_PAD_EIM_A20__EIM_ADDR20 0xb0b1
+ MX6QDL_PAD_EIM_A19__EIM_ADDR19 0xb0b1
+ MX6QDL_PAD_EIM_A18__EIM_ADDR18 0xb0b1
+ MX6QDL_PAD_EIM_A17__EIM_ADDR17 0xb0b1
+ MX6QDL_PAD_EIM_A16__EIM_ADDR16 0xb0b1
+ MX6QDL_PAD_EIM_DA15__EIM_AD15 0xb0b1
+ MX6QDL_PAD_EIM_DA14__EIM_AD14 0xb0b1
+ MX6QDL_PAD_EIM_DA13__EIM_AD13 0xb0b1
+ MX6QDL_PAD_EIM_DA12__EIM_AD12 0xb0b1
+ MX6QDL_PAD_EIM_DA11__EIM_AD11 0xb0b1
+ MX6QDL_PAD_EIM_DA10__EIM_AD10 0xb0b1
+ MX6QDL_PAD_EIM_DA9__EIM_AD09 0xb0b1
+ MX6QDL_PAD_EIM_DA8__EIM_AD08 0xb0b1
+ MX6QDL_PAD_EIM_DA7__EIM_AD07 0xb0b1
+ MX6QDL_PAD_EIM_DA6__EIM_AD06 0xb0b1
+ MX6QDL_PAD_EIM_DA5__EIM_AD05 0xb0b1
+ MX6QDL_PAD_EIM_DA4__EIM_AD04 0xb0b1
+ MX6QDL_PAD_EIM_DA3__EIM_AD03 0xb0b1
+ MX6QDL_PAD_EIM_DA2__EIM_AD02 0xb0b1
+ MX6QDL_PAD_EIM_DA1__EIM_AD01 0xb0b1
+ MX6QDL_PAD_EIM_DA0__EIM_AD00 0xb0b1
+ >;
+ };
+ };
+};
+
+&ldb {
+ status = "okay";
+
+ lvds-channel@0 {
+ fsl,data-mapping = "spwg";
+ fsl,data-width = <18>;
+ status = "okay";
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: hsd100pxn1 {
+ clock-frequency = <65000000>;
+ hactive = <1024>;
+ vactive = <768>;
+ hback-porch = <220>;
+ hfront-porch = <40>;
+ vback-porch = <21>;
+ vfront-porch = <7>;
+ hsync-len = <60>;
+ vsync-len = <10>;
+ };
+ };
};
};
+&pwm3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm3>;
+ status = "okay";
+};
+
+&spdif {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_spdif>;
+ status = "okay";
+};
+
&uart4 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart4_1>;
+ pinctrl-0 = <&pinctrl_uart4>;
status = "okay";
};
&usdhc3 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
- pinctrl-0 = <&pinctrl_usdhc3_1>;
- pinctrl-1 = <&pinctrl_usdhc3_1_100mhz>;
- pinctrl-2 = <&pinctrl_usdhc3_1_200mhz>;
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ pinctrl-1 = <&pinctrl_usdhc3_100mhz>;
+ pinctrl-2 = <&pinctrl_usdhc3_200mhz>;
cd-gpios = <&gpio6 15 0>;
wp-gpios = <&gpio1 13 0>;
status = "okay";
@@ -86,7 +440,7 @@
&weim {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_weim_nor_1 &pinctrl_weim_cs0_1>;
+ pinctrl-0 = <&pinctrl_weim_nor &pinctrl_weim_cs0>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x08000000 0x08000000>;
diff --git a/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi b/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi
new file mode 100644
index 000000000000..3bec128c7971
--- /dev/null
+++ b/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+ memory {
+ reg = <0x10000000 0x40000000>;
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_2p5v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "2P5V";
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+ regulator-always-on;
+ };
+
+ reg_3p3v: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_usb_otg_vbus: regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "usb_otg_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio3 22 0>;
+ enable-active-high;
+ };
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpio_keys>;
+
+ power {
+ label = "Power Button";
+ gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_POWER>;
+ gpio-key,wakeup;
+ };
+
+ menu {
+ label = "Menu";
+ gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_MENU>;
+ };
+
+ home {
+ label = "Home";
+ gpios = <&gpio2 4 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_HOME>;
+ };
+
+ back {
+ label = "Back";
+ gpios = <&gpio2 2 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_BACK>;
+ };
+
+ volume-up {
+ label = "Volume Up";
+ gpios = <&gpio7 13 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_VOLUMEUP>;
+ };
+
+ volume-down {
+ label = "Volume Down";
+ gpios = <&gpio4 5 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_VOLUMEDOWN>;
+ };
+ };
+
+ sound {
+ compatible = "fsl,imx6q-sabrelite-sgtl5000",
+ "fsl,imx-audio-sgtl5000";
+ model = "imx6q-sabrelite-sgtl5000";
+ ssi-controller = <&ssi1>;
+ audio-codec = <&codec>;
+ audio-routing =
+ "MIC_IN", "Mic Jack",
+ "Mic Jack", "Mic Bias",
+ "Headphone Jack", "HP_OUT";
+ mux-int-port = <1>;
+ mux-ext-port = <4>;
+ };
+
+ backlight_lcd {
+ compatible = "pwm-backlight";
+ pwms = <&pwm1 0 5000000>;
+ brightness-levels = <0 4 8 16 32 64 128 255>;
+ default-brightness-level = <7>;
+ power-supply = <&reg_3p3v>;
+ status = "okay";
+ };
+
+ backlight_lvds {
+ compatible = "pwm-backlight";
+ pwms = <&pwm4 0 5000000>;
+ brightness-levels = <0 4 8 16 32 64 128 255>;
+ default-brightness-level = <7>;
+ power-supply = <&reg_3p3v>;
+ status = "okay";
+ };
+};
+
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>;
+ status = "okay";
+};
+
+&ecspi1 {
+ fsl,spi-num-chipselects = <1>;
+ cs-gpios = <&gpio3 19 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ecspi1>;
+ status = "okay";
+
+ flash: m25p80@0 {
+ compatible = "sst,sst25vf016b";
+ spi-max-frequency = <20000000>;
+ reg = <0>;
+ };
+};
+
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-mode = "rgmii";
+ phy-reset-gpios = <&gpio3 23 GPIO_ACTIVE_LOW>;
+ txen-skew-ps = <0>;
+ txc-skew-ps = <3000>;
+ rxdv-skew-ps = <0>;
+ rxc-skew-ps = <3000>;
+ rxd0-skew-ps = <0>;
+ rxd1-skew-ps = <0>;
+ rxd2-skew-ps = <0>;
+ rxd3-skew-ps = <0>;
+ txd0-skew-ps = <0>;
+ txd1-skew-ps = <0>;
+ txd2-skew-ps = <0>;
+ txd3-skew-ps = <0>;
+ interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>,
+ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>;
+ status = "okay";
+};
+
+&i2c1 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ codec: sgtl5000@0a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ clocks = <&clks 201>;
+ VDDA-supply = <&reg_2p5v>;
+ VDDIO-supply = <&reg_3p3v>;
+ };
+};
+
+&iomuxc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hog>;
+
+ imx6q-sabrelite {
+ pinctrl_hog: hoggrp {
+ fsl,pins = <
+ /* SGTL5000 sys_mclk */
+ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x030b0
+ >;
+ };
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0
+ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0
+ MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x110b0
+ MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0
+ >;
+ };
+
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1
+ MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1
+ MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1
+ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x000b1 /* CS */
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x100b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x100b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x100b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x100b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x100b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x100b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x100b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x100b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x100b0
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ /* Phy reset */
+ MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x000b0
+ MX6QDL_PAD_GPIO_6__ENET_IRQ 0x000b1
+ >;
+ };
+
+ pinctrl_gpio_keys: gpio_keysgrp {
+ fsl,pins = <
+ /* Power Button */
+ MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x1b0b0
+ /* Menu Button */
+ MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x1b0b0
+ /* Home Button */
+ MX6QDL_PAD_NANDF_D4__GPIO2_IO04 0x1b0b0
+ /* Back Button */
+ MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x1b0b0
+ /* Volume Up Button */
+ MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x1b0b0
+ /* Volume Down Button */
+ MX6QDL_PAD_GPIO_19__GPIO4_IO05 0x1b0b0
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
+ MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_pwm1: pwm1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1
+ >;
+ };
+
+ pinctrl_pwm3: pwm3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_DAT1__PWM3_OUT 0x1b0b1
+ >;
+ };
+
+ pinctrl_pwm4: pwm4grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_CMD__PWM4_OUT 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart2: uart2grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1
+ MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0
+ /* power enable, high active */
+ MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x000b0
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x1b0b0 /* CD */
+ MX6QDL_PAD_SD3_DAT4__GPIO7_IO01 0x1f0b0 /* WP */
+ >;
+ };
+
+ pinctrl_usdhc4: usdhc4grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059
+ MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059
+ MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059
+ MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059
+ MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059
+ MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059
+ MX6QDL_PAD_NANDF_D6__GPIO2_IO06 0x1b0b0 /* CD */
+ >;
+ };
+ };
+};
+
+&ldb {
+ status = "okay";
+
+ lvds-channel@0 {
+ fsl,data-mapping = "spwg";
+ fsl,data-width = <18>;
+ status = "okay";
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: hsd100pxn1 {
+ clock-frequency = <65000000>;
+ hactive = <1024>;
+ vactive = <768>;
+ hback-porch = <220>;
+ hfront-porch = <40>;
+ vback-porch = <21>;
+ vfront-porch = <7>;
+ hsync-len = <60>;
+ vsync-len = <10>;
+ };
+ };
+ };
+};
+
+&pcie {
+ status = "okay";
+};
+
+&pwm1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm1>;
+ status = "okay";
+};
+
+&pwm3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm3>;
+ status = "okay";
+};
+
+&pwm4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pwm4>;
+ status = "okay";
+};
+
+&ssi1 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+ status = "okay";
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart2>;
+ status = "okay";
+};
+
+&usbh1 {
+ status = "okay";
+};
+
+&usbotg {
+ vbus-supply = <&reg_usb_otg_vbus>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usbotg>;
+ disable-over-current;
+ status = "okay";
+};
+
+&usdhc3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ cd-gpios = <&gpio7 0 0>;
+ wp-gpios = <&gpio7 1 0>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
+
+&usdhc4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usdhc4>;
+ cd-gpios = <&gpio2 6 0>;
+ vmmc-supply = <&reg_3p3v>;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
index e75e11b36dff..0d816d3be4b6 100644
--- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
@@ -10,6 +10,9 @@
* http://www.gnu.org/copyleft/gpl.html
*/
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
/ {
memory {
reg = <0x10000000 0x40000000>;
@@ -17,9 +20,12 @@
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_usb_otg_vbus: usb_otg_vbus {
+ reg_usb_otg_vbus: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "usb_otg_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
@@ -27,8 +33,9 @@
enable-active-high;
};
- reg_usb_h1_vbus: usb_h1_vbus {
+ reg_usb_h1_vbus: regulator@1 {
compatible = "regulator-fixed";
+ reg = <1>;
regulator-name = "usb_h1_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
@@ -36,8 +43,9 @@
enable-active-high;
};
- reg_audio: wm8962_supply {
+ reg_audio: regulator@2 {
compatible = "regulator-fixed";
+ reg = <2>;
regulator-name = "wm8962-supply";
gpio = <&gpio4 10 0>;
enable-active-high;
@@ -46,19 +54,28 @@
gpio-keys {
compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpio_keys>;
+
+ power {
+ label = "Power Button";
+ gpios = <&gpio3 29 GPIO_ACTIVE_LOW>;
+ gpio-key,wakeup;
+ linux,code = <KEY_POWER>;
+ };
volume-up {
label = "Volume Up";
- gpios = <&gpio1 4 0>;
+ gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
gpio-key,wakeup;
- linux,code = <115>; /* KEY_VOLUMEUP */
+ linux,code = <KEY_VOLUMEUP>;
};
volume-down {
label = "Volume Down";
- gpios = <&gpio1 5 0>;
+ gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
gpio-key,wakeup;
- linux,code = <114>; /* KEY_VOLUMEDOWN */
+ linux,code = <KEY_VOLUMEDOWN>;
};
};
@@ -92,7 +109,7 @@
&audmux {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_audmux_2>;
+ pinctrl-0 = <&pinctrl_audmux>;
status = "okay";
};
@@ -100,7 +117,7 @@
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio4 9 0>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi1_2>;
+ pinctrl-0 = <&pinctrl_ecspi1>;
status = "okay";
flash: m25p80@0 {
@@ -114,7 +131,7 @@
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_enet_1>;
+ pinctrl-0 = <&pinctrl_enet>;
phy-mode = "rgmii";
phy-reset-gpios = <&gpio1 25 0>;
status = "okay";
@@ -123,7 +140,7 @@
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c1_2>;
+ pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
codec: wm8962@1a {
@@ -149,10 +166,116 @@
};
};
+&i2c2 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+
+ pmic: pfuze100@08 {
+ compatible = "fsl,pfuze100";
+ reg = <0x08>;
+
+ regulators {
+ sw1a_reg: sw1ab {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+ };
+
+ sw1c_reg: sw1c {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+ };
+
+ sw2_reg: sw2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3a_reg: sw3a {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3b_reg: sw3b {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw4_reg: sw4 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ swbst_reg: swbst {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5150000>;
+ };
+
+ snvs_reg: vsnvs {
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vref_reg: vrefddr {
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vgen1_reg: vgen1 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen2_reg: vgen2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen3_reg: vgen3 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ vgen4_reg: vgen4 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen5_reg: vgen5 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen6_reg: vgen6 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+ };
+};
+
&i2c3 {
clock-frequency = <100000>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c3_2>;
+ pinctrl-0 = <&pinctrl_i2c3>;
status = "okay";
egalax_ts@04 {
@@ -168,11 +291,9 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- hog {
+ imx6qdl-sabresd {
pinctrl_hog: hoggrp {
fsl,pins = <
- MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000
- MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x80000000
MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x80000000
MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x80000000
MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000
@@ -184,6 +305,122 @@
MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x80000000
>;
};
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0
+ MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0
+ MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0
+ MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0
+ >;
+ };
+
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x100b1
+ MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x100b1
+ MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x100b1
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ >;
+ };
+
+ pinctrl_gpio_keys: gpio_keysgrp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000
+ MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000
+ MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x80000000
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b8b1
+ MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_i2c3: i2c3grp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1
+ MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_pwm1: pwm1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc2: usdhc2grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059
+ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059
+ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059
+ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059
+ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059
+ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059
+ MX6QDL_PAD_NANDF_D4__SD2_DATA4 0x17059
+ MX6QDL_PAD_NANDF_D5__SD2_DATA5 0x17059
+ MX6QDL_PAD_NANDF_D6__SD2_DATA6 0x17059
+ MX6QDL_PAD_NANDF_D7__SD2_DATA7 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059
+ MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059
+ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059
+ MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059
+ >;
+ };
};
};
@@ -214,7 +451,7 @@
&pwm1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_pwm0_1>;
+ pinctrl-0 = <&pinctrl_pwm1>;
status = "okay";
};
@@ -225,7 +462,7 @@
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_1>;
+ pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
@@ -237,14 +474,14 @@
&usbotg {
vbus-supply = <&reg_usb_otg_vbus>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usbotg_2>;
+ pinctrl-0 = <&pinctrl_usbotg>;
disable-over-current;
status = "okay";
};
&usdhc2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc2_1>;
+ pinctrl-0 = <&pinctrl_usdhc2>;
bus-width = <8>;
cd-gpios = <&gpio2 2 0>;
wp-gpios = <&gpio2 3 0>;
@@ -253,7 +490,7 @@
&usdhc3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc3_1>;
+ pinctrl-0 = <&pinctrl_usdhc3>;
bus-width = <8>;
cd-gpios = <&gpio2 0 0>;
wp-gpios = <&gpio2 1 0>;
diff --git a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
index 35f547929167..bdfdf89d405f 100644
--- a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi
@@ -12,17 +12,21 @@
/ {
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_2p5v: 2p5v {
+ reg_2p5v: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "2P5V";
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <2500000>;
regulator-always-on;
};
- reg_3p3v: 3p3v {
+ reg_3p3v: regulator@1 {
compatible = "regulator-fixed";
+ reg = <1>;
regulator-name = "3P3V";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
@@ -54,14 +58,14 @@
&audmux {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_audmux_2>;
+ pinctrl-0 = <&pinctrl_audmux>;
status = "okay";
};
&i2c2 {
clock-frequency = <100000>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c2_2>;
+ pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
codec: sgtl5000@0a {
@@ -77,7 +81,7 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- hog {
+ imx6qdl-wandboard {
pinctrl_hog: hoggrp {
fsl,pins = <
MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0
@@ -91,20 +95,121 @@
MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000
>;
};
+
+ pinctrl_audmux: audmuxgrp {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0
+ MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0
+ MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0
+ MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0
+ >;
+ };
+
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
+ MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
+ MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
+ MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
+ MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
+ MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
+ MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
+ MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
+ MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
+ MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
+ MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
+ MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+ MX6QDL_PAD_GPIO_6__ENET_IRQ 0x000b1
+ >;
+ };
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
+ MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_spdif: spdifgrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_RXD0__SPDIF_OUT 0x1b0b0
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1
+ MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_uart3: uart3grp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x1b0b1
+ MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1
+ MX6QDL_PAD_EIM_D23__UART3_CTS_B 0x1b0b1
+ MX6QDL_PAD_EIM_EB3__UART3_RTS_B 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg: usbotggrp {
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc1: usdhc1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059
+ MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059
+ MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059
+ MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059
+ MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059
+ MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059
+ >;
+ };
+
+ pinctrl_usdhc2: usdhc2grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059
+ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059
+ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059
+ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059
+ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059
+ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
};
};
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_enet_1>;
+ pinctrl-0 = <&pinctrl_enet>;
phy-mode = "rgmii";
phy-reset-gpios = <&gpio3 29 0>;
+ interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>,
+ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>;
status = "okay";
};
&spdif {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_spdif_3>;
+ pinctrl-0 = <&pinctrl_spdif>;
status = "okay";
};
@@ -115,13 +220,13 @@
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_1>;
+ pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
&uart3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart3_2>;
+ pinctrl-0 = <&pinctrl_uart3>;
fsl,uart-has-rtscts;
status = "okay";
};
@@ -132,7 +237,7 @@
&usbotg {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usbotg_1>;
+ pinctrl-0 = <&pinctrl_usbotg>;
disable-over-current;
dr_mode = "peripheral";
status = "okay";
@@ -140,21 +245,21 @@
&usdhc1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc1_2>;
+ pinctrl-0 = <&pinctrl_usdhc1>;
cd-gpios = <&gpio1 2 0>;
status = "okay";
};
&usdhc2 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc2_2>;
+ pinctrl-0 = <&pinctrl_usdhc2>;
non-removable;
status = "okay";
};
&usdhc3 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usdhc3_2>;
+ pinctrl-0 = <&pinctrl_usdhc3>;
cd-gpios = <&gpio3 9 0>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 64a8cbe9480f..55cb926fa3f7 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -14,6 +14,8 @@
/ {
aliases {
+ can0 = &can1;
+ can1 = &can2;
gpio0 = &gpio1;
gpio1 = &gpio2;
gpio2 = &gpio3;
@@ -24,6 +26,10 @@
i2c0 = &i2c1;
i2c1 = &i2c2;
i2c2 = &i2c3;
+ mmc0 = &usdhc1;
+ mmc1 = &usdhc2;
+ mmc2 = &usdhc3;
+ mmc3 = &usdhc4;
serial0 = &uart1;
serial1 = &uart2;
serial2 = &uart3;
@@ -33,6 +39,8 @@
spi1 = &ecspi2;
spi2 = &ecspi3;
spi3 = &ecspi4;
+ usbphy0 = &usbphy1;
+ usbphy1 = &usbphy2;
};
intc: interrupt-controller@00a01000 {
@@ -75,7 +83,10 @@
dma_apbh: dma-apbh@00110000 {
compatible = "fsl,imx6q-dma-apbh", "fsl,imx28-dma-apbh";
reg = <0x00110000 0x2000>;
- interrupts = <0 13 0x04>, <0 13 0x04>, <0 13 0x04>, <0 13 0x04>;
+ interrupts = <0 13 IRQ_TYPE_LEVEL_HIGH>,
+ <0 13 IRQ_TYPE_LEVEL_HIGH>,
+ <0 13 IRQ_TYPE_LEVEL_HIGH>,
+ <0 13 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "gpmi0", "gpmi1", "gpmi2", "gpmi3";
#dma-cells = <1>;
dma-channels = <4>;
@@ -88,7 +99,7 @@
#size-cells = <1>;
reg = <0x00112000 0x2000>, <0x00114000 0x2000>;
reg-names = "gpmi-nand", "bch";
- interrupts = <0 15 0x04>;
+ interrupts = <0 15 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "bch";
clocks = <&clks 152>, <&clks 153>, <&clks 151>,
<&clks 150>, <&clks 149>;
@@ -109,7 +120,7 @@
L2: l2-cache@00a02000 {
compatible = "arm,pl310-cache";
reg = <0x00a02000 0x1000>;
- interrupts = <0 92 0x04>;
+ interrupts = <0 92 IRQ_TYPE_LEVEL_HIGH>;
cache-unified;
cache-level = <2>;
arm,tag-latency = <4 2 3>;
@@ -126,7 +137,7 @@
0x81000000 0 0 0x01f80000 0 0x00010000 /* downstream I/O */
0x82000000 0 0x01000000 0x01000000 0 0x00f00000>; /* non-prefetchable memory */
num-lanes = <1>;
- interrupts = <0 123 0x04>;
+ interrupts = <0 123 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 189>, <&clks 187>, <&clks 206>, <&clks 144>;
clock-names = "pcie_ref_125m", "sata_ref_100m", "lvds_gate", "pcie_axi";
status = "disabled";
@@ -134,7 +145,7 @@
pmu {
compatible = "arm,cortex-a9-pmu";
- interrupts = <0 94 0x04>;
+ interrupts = <0 94 IRQ_TYPE_LEVEL_HIGH>;
};
aips-bus@02000000 { /* AIPS1 */
@@ -154,7 +165,7 @@
spdif: spdif@02004000 {
compatible = "fsl,imx35-spdif";
reg = <0x02004000 0x4000>;
- interrupts = <0 52 0x04>;
+ interrupts = <0 52 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&sdma 14 18 0>,
<&sdma 15 18 0>;
dma-names = "rx", "tx";
@@ -176,9 +187,11 @@
#size-cells = <0>;
compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
reg = <0x02008000 0x4000>;
- interrupts = <0 31 0x04>;
+ interrupts = <0 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 112>, <&clks 112>;
clock-names = "ipg", "per";
+ dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
+ dma-names = "rx", "tx";
status = "disabled";
};
@@ -187,9 +200,11 @@
#size-cells = <0>;
compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
reg = <0x0200c000 0x4000>;
- interrupts = <0 32 0x04>;
+ interrupts = <0 32 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 113>, <&clks 113>;
clock-names = "ipg", "per";
+ dmas = <&sdma 5 7 1>, <&sdma 6 7 2>;
+ dma-names = "rx", "tx";
status = "disabled";
};
@@ -198,9 +213,11 @@
#size-cells = <0>;
compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
reg = <0x02010000 0x4000>;
- interrupts = <0 33 0x04>;
+ interrupts = <0 33 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 114>, <&clks 114>;
clock-names = "ipg", "per";
+ dmas = <&sdma 7 7 1>, <&sdma 8 7 2>;
+ dma-names = "rx", "tx";
status = "disabled";
};
@@ -209,16 +226,18 @@
#size-cells = <0>;
compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
reg = <0x02014000 0x4000>;
- interrupts = <0 34 0x04>;
+ interrupts = <0 34 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 115>, <&clks 115>;
clock-names = "ipg", "per";
+ dmas = <&sdma 9 7 1>, <&sdma 10 7 2>;
+ dma-names = "rx", "tx";
status = "disabled";
};
uart1: serial@02020000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x02020000 0x4000>;
- interrupts = <0 26 0x04>;
+ interrupts = <0 26 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 160>, <&clks 161>;
clock-names = "ipg", "per";
dmas = <&sdma 25 4 0>, <&sdma 26 4 0>;
@@ -228,13 +247,15 @@
esai: esai@02024000 {
reg = <0x02024000 0x4000>;
- interrupts = <0 51 0x04>;
+ interrupts = <0 51 IRQ_TYPE_LEVEL_HIGH>;
};
ssi1: ssi@02028000 {
- compatible = "fsl,imx6q-ssi","fsl,imx21-ssi";
+ compatible = "fsl,imx6q-ssi",
+ "fsl,imx51-ssi",
+ "fsl,imx21-ssi";
reg = <0x02028000 0x4000>;
- interrupts = <0 46 0x04>;
+ interrupts = <0 46 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 178>;
dmas = <&sdma 37 1 0>,
<&sdma 38 1 0>;
@@ -245,9 +266,11 @@
};
ssi2: ssi@0202c000 {
- compatible = "fsl,imx6q-ssi","fsl,imx21-ssi";
+ compatible = "fsl,imx6q-ssi",
+ "fsl,imx51-ssi",
+ "fsl,imx21-ssi";
reg = <0x0202c000 0x4000>;
- interrupts = <0 47 0x04>;
+ interrupts = <0 47 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 179>;
dmas = <&sdma 41 1 0>,
<&sdma 42 1 0>;
@@ -258,9 +281,11 @@
};
ssi3: ssi@02030000 {
- compatible = "fsl,imx6q-ssi","fsl,imx21-ssi";
+ compatible = "fsl,imx6q-ssi",
+ "fsl,imx51-ssi",
+ "fsl,imx21-ssi";
reg = <0x02030000 0x4000>;
- interrupts = <0 48 0x04>;
+ interrupts = <0 48 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 180>;
dmas = <&sdma 45 1 0>,
<&sdma 46 1 0>;
@@ -272,7 +297,7 @@
asrc: asrc@02034000 {
reg = <0x02034000 0x4000>;
- interrupts = <0 50 0x04>;
+ interrupts = <0 50 IRQ_TYPE_LEVEL_HIGH>;
};
spba@0203c000 {
@@ -282,7 +307,8 @@
vpu: vpu@02040000 {
reg = <0x02040000 0x3c000>;
- interrupts = <0 3 0x04 0 12 0x04>;
+ interrupts = <0 3 IRQ_TYPE_LEVEL_HIGH>,
+ <0 12 IRQ_TYPE_LEVEL_HIGH>;
};
aipstz@0207c000 { /* AIPSTZ1 */
@@ -293,7 +319,7 @@
#pwm-cells = <2>;
compatible = "fsl,imx6q-pwm", "fsl,imx27-pwm";
reg = <0x02080000 0x4000>;
- interrupts = <0 83 0x04>;
+ interrupts = <0 83 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 62>, <&clks 145>;
clock-names = "ipg", "per";
};
@@ -302,7 +328,7 @@
#pwm-cells = <2>;
compatible = "fsl,imx6q-pwm", "fsl,imx27-pwm";
reg = <0x02084000 0x4000>;
- interrupts = <0 84 0x04>;
+ interrupts = <0 84 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 62>, <&clks 146>;
clock-names = "ipg", "per";
};
@@ -311,7 +337,7 @@
#pwm-cells = <2>;
compatible = "fsl,imx6q-pwm", "fsl,imx27-pwm";
reg = <0x02088000 0x4000>;
- interrupts = <0 85 0x04>;
+ interrupts = <0 85 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 62>, <&clks 147>;
clock-names = "ipg", "per";
};
@@ -320,7 +346,7 @@
#pwm-cells = <2>;
compatible = "fsl,imx6q-pwm", "fsl,imx27-pwm";
reg = <0x0208c000 0x4000>;
- interrupts = <0 86 0x04>;
+ interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 62>, <&clks 148>;
clock-names = "ipg", "per";
};
@@ -328,23 +354,25 @@
can1: flexcan@02090000 {
compatible = "fsl,imx6q-flexcan";
reg = <0x02090000 0x4000>;
- interrupts = <0 110 0x04>;
+ interrupts = <0 110 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 108>, <&clks 109>;
clock-names = "ipg", "per";
+ status = "disabled";
};
can2: flexcan@02094000 {
compatible = "fsl,imx6q-flexcan";
reg = <0x02094000 0x4000>;
- interrupts = <0 111 0x04>;
+ interrupts = <0 111 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 110>, <&clks 111>;
clock-names = "ipg", "per";
+ status = "disabled";
};
gpt: gpt@02098000 {
compatible = "fsl,imx6q-gpt", "fsl,imx31-gpt";
reg = <0x02098000 0x4000>;
- interrupts = <0 55 0x04>;
+ interrupts = <0 55 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 119>, <&clks 120>;
clock-names = "ipg", "per";
};
@@ -352,7 +380,8 @@
gpio1: gpio@0209c000 {
compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
reg = <0x0209c000 0x4000>;
- interrupts = <0 66 0x04 0 67 0x04>;
+ interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH>,
+ <0 67 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -362,7 +391,8 @@
gpio2: gpio@020a0000 {
compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
reg = <0x020a0000 0x4000>;
- interrupts = <0 68 0x04 0 69 0x04>;
+ interrupts = <0 68 IRQ_TYPE_LEVEL_HIGH>,
+ <0 69 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -372,7 +402,8 @@
gpio3: gpio@020a4000 {
compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
reg = <0x020a4000 0x4000>;
- interrupts = <0 70 0x04 0 71 0x04>;
+ interrupts = <0 70 IRQ_TYPE_LEVEL_HIGH>,
+ <0 71 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -382,7 +413,8 @@
gpio4: gpio@020a8000 {
compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
reg = <0x020a8000 0x4000>;
- interrupts = <0 72 0x04 0 73 0x04>;
+ interrupts = <0 72 IRQ_TYPE_LEVEL_HIGH>,
+ <0 73 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -392,7 +424,8 @@
gpio5: gpio@020ac000 {
compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
reg = <0x020ac000 0x4000>;
- interrupts = <0 74 0x04 0 75 0x04>;
+ interrupts = <0 74 IRQ_TYPE_LEVEL_HIGH>,
+ <0 75 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -402,7 +435,8 @@
gpio6: gpio@020b0000 {
compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
reg = <0x020b0000 0x4000>;
- interrupts = <0 76 0x04 0 77 0x04>;
+ interrupts = <0 76 IRQ_TYPE_LEVEL_HIGH>,
+ <0 77 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -412,7 +446,8 @@
gpio7: gpio@020b4000 {
compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
reg = <0x020b4000 0x4000>;
- interrupts = <0 78 0x04 0 79 0x04>;
+ interrupts = <0 78 IRQ_TYPE_LEVEL_HIGH>,
+ <0 79 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -421,20 +456,20 @@
kpp: kpp@020b8000 {
reg = <0x020b8000 0x4000>;
- interrupts = <0 82 0x04>;
+ interrupts = <0 82 IRQ_TYPE_LEVEL_HIGH>;
};
wdog1: wdog@020bc000 {
compatible = "fsl,imx6q-wdt", "fsl,imx21-wdt";
reg = <0x020bc000 0x4000>;
- interrupts = <0 80 0x04>;
+ interrupts = <0 80 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 0>;
};
wdog2: wdog@020c0000 {
compatible = "fsl,imx6q-wdt", "fsl,imx21-wdt";
reg = <0x020c0000 0x4000>;
- interrupts = <0 81 0x04>;
+ interrupts = <0 81 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 0>;
status = "disabled";
};
@@ -442,14 +477,17 @@
clks: ccm@020c4000 {
compatible = "fsl,imx6q-ccm";
reg = <0x020c4000 0x4000>;
- interrupts = <0 87 0x04 0 88 0x04>;
+ interrupts = <0 87 IRQ_TYPE_LEVEL_HIGH>,
+ <0 88 IRQ_TYPE_LEVEL_HIGH>;
#clock-cells = <1>;
};
anatop: anatop@020c8000 {
compatible = "fsl,imx6q-anatop", "syscon", "simple-bus";
reg = <0x020c8000 0x1000>;
- interrupts = <0 49 0x04 0 54 0x04 0 127 0x04>;
+ interrupts = <0 49 IRQ_TYPE_LEVEL_HIGH>,
+ <0 54 IRQ_TYPE_LEVEL_HIGH>,
+ <0 127 IRQ_TYPE_LEVEL_HIGH>;
regulator-1p1@110 {
compatible = "fsl,anatop-regulator";
@@ -495,7 +533,7 @@
reg_arm: regulator-vddcore@140 {
compatible = "fsl,anatop-regulator";
- regulator-name = "cpu";
+ regulator-name = "vddarm";
regulator-min-microvolt = <725000>;
regulator-max-microvolt = <1450000>;
regulator-always-on;
@@ -547,23 +585,26 @@
tempmon: tempmon {
compatible = "fsl,imx6q-tempmon";
- interrupts = <0 49 0x04>;
+ interrupts = <0 49 IRQ_TYPE_LEVEL_HIGH>;
fsl,tempmon = <&anatop>;
fsl,tempmon-data = <&ocotp>;
+ clocks = <&clks 172>;
};
usbphy1: usbphy@020c9000 {
compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy";
reg = <0x020c9000 0x1000>;
- interrupts = <0 44 0x04>;
+ interrupts = <0 44 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 182>;
+ fsl,anatop = <&anatop>;
};
usbphy2: usbphy@020ca000 {
compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy";
reg = <0x020ca000 0x1000>;
- interrupts = <0 45 0x04>;
+ interrupts = <0 45 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 183>;
+ fsl,anatop = <&anatop>;
};
snvs@020cc000 {
@@ -575,31 +616,34 @@
snvs-rtc-lp@34 {
compatible = "fsl,sec-v4.0-mon-rtc-lp";
reg = <0x34 0x58>;
- interrupts = <0 19 0x04 0 20 0x04>;
+ interrupts = <0 19 IRQ_TYPE_LEVEL_HIGH>,
+ <0 20 IRQ_TYPE_LEVEL_HIGH>;
};
};
epit1: epit@020d0000 { /* EPIT1 */
reg = <0x020d0000 0x4000>;
- interrupts = <0 56 0x04>;
+ interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
};
epit2: epit@020d4000 { /* EPIT2 */
reg = <0x020d4000 0x4000>;
- interrupts = <0 57 0x04>;
+ interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>;
};
src: src@020d8000 {
compatible = "fsl,imx6q-src", "fsl,imx51-src";
reg = <0x020d8000 0x4000>;
- interrupts = <0 91 0x04 0 96 0x04>;
+ interrupts = <0 91 IRQ_TYPE_LEVEL_HIGH>,
+ <0 96 IRQ_TYPE_LEVEL_HIGH>;
#reset-cells = <1>;
};
gpc: gpc@020dc000 {
compatible = "fsl,imx6q-gpc";
reg = <0x020dc000 0x4000>;
- interrupts = <0 89 0x04 0 90 0x04>;
+ interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>,
+ <0 90 IRQ_TYPE_LEVEL_HIGH>;
};
gpr: iomuxc-gpr@020e0000 {
@@ -610,744 +654,6 @@
iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6dl-iomuxc", "fsl,imx6q-iomuxc";
reg = <0x020e0000 0x4000>;
-
- audmux {
- pinctrl_audmux_1: audmux-1 {
- fsl,pins = <
- MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x80000000
- MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x80000000
- MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x80000000
- MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x80000000
- >;
- };
-
- pinctrl_audmux_2: audmux-2 {
- fsl,pins = <
- MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x80000000
- MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x80000000
- MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x80000000
- MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x80000000
- >;
- };
-
- pinctrl_audmux_3: audmux-3 {
- fsl,pins = <
- MX6QDL_PAD_DISP0_DAT16__AUD5_TXC 0x80000000
- MX6QDL_PAD_DISP0_DAT18__AUD5_TXFS 0x80000000
- MX6QDL_PAD_DISP0_DAT19__AUD5_RXD 0x80000000
- >;
- };
- };
-
- ecspi1 {
- pinctrl_ecspi1_1: ecspi1grp-1 {
- fsl,pins = <
- MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1
- MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1
- MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1
- >;
- };
-
- pinctrl_ecspi1_2: ecspi1grp-2 {
- fsl,pins = <
- MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x100b1
- MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x100b1
- MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x100b1
- >;
- };
- };
-
- ecspi3 {
- pinctrl_ecspi3_1: ecspi3grp-1 {
- fsl,pins = <
- MX6QDL_PAD_DISP0_DAT2__ECSPI3_MISO 0x100b1
- MX6QDL_PAD_DISP0_DAT1__ECSPI3_MOSI 0x100b1
- MX6QDL_PAD_DISP0_DAT0__ECSPI3_SCLK 0x100b1
- >;
- };
- };
-
- enet {
- pinctrl_enet_1: enetgrp-1 {
- fsl,pins = <
- MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
- MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
- MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
- MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
- MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
- MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
- MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
- MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
- MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
- MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
- MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
- MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
- MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
- MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
- MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
- MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
- >;
- };
-
- pinctrl_enet_2: enetgrp-2 {
- fsl,pins = <
- MX6QDL_PAD_KEY_COL1__ENET_MDIO 0x1b0b0
- MX6QDL_PAD_KEY_COL2__ENET_MDC 0x1b0b0
- MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
- MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
- MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
- MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
- MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
- MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
- MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
- MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
- MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
- MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
- MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
- MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
- MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
- >;
- };
-
- pinctrl_enet_3: enetgrp-3 {
- fsl,pins = <
- MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
- MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
- MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
- MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
- MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
- MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
- MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
- MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
- MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
- MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
- MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
- MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
- MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
- MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
- MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
- MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x1b0b0
- >;
- };
- };
-
- esai {
- pinctrl_esai_1: esaigrp-1 {
- fsl,pins = <
- MX6QDL_PAD_ENET_RXD0__ESAI_TX_HF_CLK 0x1b030
- MX6QDL_PAD_ENET_CRS_DV__ESAI_TX_CLK 0x1b030
- MX6QDL_PAD_ENET_RXD1__ESAI_TX_FS 0x1b030
- MX6QDL_PAD_ENET_TX_EN__ESAI_TX3_RX2 0x1b030
- MX6QDL_PAD_ENET_TXD1__ESAI_TX2_RX3 0x1b030
- MX6QDL_PAD_ENET_TXD0__ESAI_TX4_RX1 0x1b030
- MX6QDL_PAD_ENET_MDC__ESAI_TX5_RX0 0x1b030
- MX6QDL_PAD_NANDF_CS2__ESAI_TX0 0x1b030
- MX6QDL_PAD_NANDF_CS3__ESAI_TX1 0x1b030
- >;
- };
-
- pinctrl_esai_2: esaigrp-2 {
- fsl,pins = <
- MX6QDL_PAD_ENET_CRS_DV__ESAI_TX_CLK 0x1b030
- MX6QDL_PAD_ENET_RXD1__ESAI_TX_FS 0x1b030
- MX6QDL_PAD_ENET_TX_EN__ESAI_TX3_RX2 0x1b030
- MX6QDL_PAD_GPIO_5__ESAI_TX2_RX3 0x1b030
- MX6QDL_PAD_ENET_TXD0__ESAI_TX4_RX1 0x1b030
- MX6QDL_PAD_ENET_MDC__ESAI_TX5_RX0 0x1b030
- MX6QDL_PAD_GPIO_17__ESAI_TX0 0x1b030
- MX6QDL_PAD_NANDF_CS3__ESAI_TX1 0x1b030
- MX6QDL_PAD_ENET_MDIO__ESAI_RX_CLK 0x1b030
- MX6QDL_PAD_GPIO_9__ESAI_RX_FS 0x1b030
- >;
- };
- };
-
- flexcan1 {
- pinctrl_flexcan1_1: flexcan1grp-1 {
- fsl,pins = <
- MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x80000000
- MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x80000000
- >;
- };
-
- pinctrl_flexcan1_2: flexcan1grp-2 {
- fsl,pins = <
- MX6QDL_PAD_GPIO_7__FLEXCAN1_TX 0x80000000
- MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x80000000
- >;
- };
- };
-
- flexcan2 {
- pinctrl_flexcan2_1: flexcan2grp-1 {
- fsl,pins = <
- MX6QDL_PAD_KEY_COL4__FLEXCAN2_TX 0x80000000
- MX6QDL_PAD_KEY_ROW4__FLEXCAN2_RX 0x80000000
- >;
- };
- };
-
- gpmi-nand {
- pinctrl_gpmi_nand_1: gpmi-nand-1 {
- fsl,pins = <
- MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
- MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1
- MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1
- MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000
- MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1
- MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1
- MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1
- MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1
- MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1
- MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1
- MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1
- MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1
- MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1
- MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1
- MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1
- MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1
- MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1
- >;
- };
- };
-
- hdmi_hdcp {
- pinctrl_hdmi_hdcp_1: hdmihdcpgrp-1 {
- fsl,pins = <
- MX6QDL_PAD_KEY_COL3__HDMI_TX_DDC_SCL 0x4001b8b1
- MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x4001b8b1
- >;
- };
-
- pinctrl_hdmi_hdcp_2: hdmihdcpgrp-2 {
- fsl,pins = <
- MX6QDL_PAD_EIM_EB2__HDMI_TX_DDC_SCL 0x4001b8b1
- MX6QDL_PAD_EIM_D16__HDMI_TX_DDC_SDA 0x4001b8b1
- >;
- };
-
- pinctrl_hdmi_hdcp_3: hdmihdcpgrp-3 {
- fsl,pins = <
- MX6QDL_PAD_EIM_EB2__HDMI_TX_DDC_SCL 0x4001b8b1
- MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x4001b8b1
- >;
- };
- };
-
- hdmi_cec {
- pinctrl_hdmi_cec_1: hdmicecgrp-1 {
- fsl,pins = <
- MX6QDL_PAD_EIM_A25__HDMI_TX_CEC_LINE 0x1f8b0
- >;
- };
-
- pinctrl_hdmi_cec_2: hdmicecgrp-2 {
- fsl,pins = <
- MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0
- >;
- };
- };
-
- i2c1 {
- pinctrl_i2c1_1: i2c1grp-1 {
- fsl,pins = <
- MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
- MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1
- >;
- };
-
- pinctrl_i2c1_2: i2c1grp-2 {
- fsl,pins = <
- MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b8b1
- MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b8b1
- >;
- };
- };
-
- i2c2 {
- pinctrl_i2c2_1: i2c2grp-1 {
- fsl,pins = <
- MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1
- MX6QDL_PAD_EIM_D16__I2C2_SDA 0x4001b8b1
- >;
- };
-
- pinctrl_i2c2_2: i2c2grp-2 {
- fsl,pins = <
- MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
- MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
- >;
- };
-
- pinctrl_i2c2_3: i2c2grp-3 {
- fsl,pins = <
- MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1
- MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
- >;
- };
- };
-
- i2c3 {
- pinctrl_i2c3_1: i2c3grp-1 {
- fsl,pins = <
- MX6QDL_PAD_EIM_D17__I2C3_SCL 0x4001b8b1
- MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1
- >;
- };
-
- pinctrl_i2c3_2: i2c3grp-2 {
- fsl,pins = <
- MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1
- MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1
- >;
- };
-
- pinctrl_i2c3_3: i2c3grp-3 {
- fsl,pins = <
- MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1
- MX6QDL_PAD_GPIO_16__I2C3_SDA 0x4001b8b1
- >;
- };
-
- pinctrl_i2c3_4: i2c3grp-4 {
- fsl,pins = <
- MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1
- MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1
- >;
- };
- };
-
- ipu1 {
- pinctrl_ipu1_1: ipu1grp-1 {
- fsl,pins = <
- MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x10
- MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x10
- MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x10
- MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x10
- MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x80000000
- MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x10
- MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x10
- MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x10
- MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x10
- MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x10
- MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x10
- MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x10
- MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x10
- MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x10
- MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x10
- MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x10
- MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x10
- MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x10
- MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x10
- MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x10
- MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x10
- MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x10
- MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x10
- MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x10
- MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x10
- MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x10
- MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x10
- MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x10
- MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x10
- >;
- };
-
- pinctrl_ipu1_2: ipu1grp-2 { /* parallel camera */
- fsl,pins = <
- MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x80000000
- MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x80000000
- MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x80000000
- MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x80000000
- MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x80000000
- MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x80000000
- MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x80000000
- MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x80000000
- MX6QDL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x80000000
- MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x80000000
- MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x80000000
- MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x80000000
- >;
- };
-
- pinctrl_ipu1_3: ipu1grp-3 { /* parallel port 16-bit */
- fsl,pins = <
- MX6QDL_PAD_CSI0_DAT4__IPU1_CSI0_DATA04 0x80000000
- MX6QDL_PAD_CSI0_DAT5__IPU1_CSI0_DATA05 0x80000000
- MX6QDL_PAD_CSI0_DAT6__IPU1_CSI0_DATA06 0x80000000
- MX6QDL_PAD_CSI0_DAT7__IPU1_CSI0_DATA07 0x80000000
- MX6QDL_PAD_CSI0_DAT8__IPU1_CSI0_DATA08 0x80000000
- MX6QDL_PAD_CSI0_DAT9__IPU1_CSI0_DATA09 0x80000000
- MX6QDL_PAD_CSI0_DAT10__IPU1_CSI0_DATA10 0x80000000
- MX6QDL_PAD_CSI0_DAT11__IPU1_CSI0_DATA11 0x80000000
- MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x80000000
- MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x80000000
- MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x80000000
- MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x80000000
- MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x80000000
- MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x80000000
- MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x80000000
- MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x80000000
- MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x80000000
- MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x80000000
- MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x80000000
- >;
- };
- };
-
- mlb {
- pinctrl_mlb_1: mlbgrp-1 {
- fsl,pins = <
- MX6QDL_PAD_GPIO_3__MLB_CLK 0x71
- MX6QDL_PAD_GPIO_6__MLB_SIG 0x71
- MX6QDL_PAD_GPIO_2__MLB_DATA 0x71
- >;
- };
-
- pinctrl_mlb_2: mlbgrp-2 {
- fsl,pins = <
- MX6QDL_PAD_ENET_TXD1__MLB_CLK 0x71
- MX6QDL_PAD_GPIO_6__MLB_SIG 0x71
- MX6QDL_PAD_GPIO_2__MLB_DATA 0x71
- >;
- };
- };
-
- pwm0 {
- pinctrl_pwm0_1: pwm0grp-1 {
- fsl,pins = <
- MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1
- >;
- };
- };
-
- pwm3 {
- pinctrl_pwm3_1: pwm3grp-1 {
- fsl,pins = <
- MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1
- >;
- };
- };
-
- spdif {
- pinctrl_spdif_1: spdifgrp-1 {
- fsl,pins = <
- MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x1b0b0
- >;
- };
-
- pinctrl_spdif_2: spdifgrp-2 {
- fsl,pins = <
- MX6QDL_PAD_GPIO_16__SPDIF_IN 0x1b0b0
- MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x1b0b0
- >;
- };
-
- pinctrl_spdif_3: spdifgrp-3 {
- fsl,pins = <
- MX6QDL_PAD_ENET_RXD0__SPDIF_OUT 0x1b0b0
- >;
- };
- };
-
- uart1 {
- pinctrl_uart1_1: uart1grp-1 {
- fsl,pins = <
- MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1
- MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1
- >;
- };
- };
-
- uart2 {
- pinctrl_uart2_1: uart2grp-1 {
- fsl,pins = <
- MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1
- MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1
- >;
- };
-
- pinctrl_uart2_2: uart2grp-2 { /* DTE mode */
- fsl,pins = <
- MX6QDL_PAD_EIM_D26__UART2_RX_DATA 0x1b0b1
- MX6QDL_PAD_EIM_D27__UART2_TX_DATA 0x1b0b1
- MX6QDL_PAD_EIM_D28__UART2_DTE_CTS_B 0x1b0b1
- MX6QDL_PAD_EIM_D29__UART2_DTE_RTS_B 0x1b0b1
- >;
- };
- };
-
- uart3 {
- pinctrl_uart3_1: uart3grp-1 {
- fsl,pins = <
- MX6QDL_PAD_SD4_CLK__UART3_RX_DATA 0x1b0b1
- MX6QDL_PAD_SD4_CMD__UART3_TX_DATA 0x1b0b1
- MX6QDL_PAD_EIM_D30__UART3_CTS_B 0x1b0b1
- MX6QDL_PAD_EIM_EB3__UART3_RTS_B 0x1b0b1
- >;
- };
-
- pinctrl_uart3_2: uart3grp-2 {
- fsl,pins = <
- MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x1b0b1
- MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1
- MX6QDL_PAD_EIM_D23__UART3_CTS_B 0x1b0b1
- MX6QDL_PAD_EIM_EB3__UART3_RTS_B 0x1b0b1
- >;
- };
- };
-
- uart4 {
- pinctrl_uart4_1: uart4grp-1 {
- fsl,pins = <
- MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1
- MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1
- >;
- };
- };
-
- usbotg {
- pinctrl_usbotg_1: usbotggrp-1 {
- fsl,pins = <
- MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
- >;
- };
-
- pinctrl_usbotg_2: usbotggrp-2 {
- fsl,pins = <
- MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059
- >;
- };
- };
-
- usbh2 {
- pinctrl_usbh2_1: usbh2grp-1 {
- fsl,pins = <
- MX6QDL_PAD_RGMII_TXC__USB_H2_DATA 0x40013030
- MX6QDL_PAD_RGMII_TX_CTL__USB_H2_STROBE 0x40013030
- >;
- };
-
- pinctrl_usbh2_2: usbh2grp-2 {
- fsl,pins = <
- MX6QDL_PAD_RGMII_TX_CTL__USB_H2_STROBE 0x40017030
- >;
- };
- };
-
- usbh3 {
- pinctrl_usbh3_1: usbh3grp-1 {
- fsl,pins = <
- MX6QDL_PAD_RGMII_RX_CTL__USB_H3_DATA 0x40013030
- MX6QDL_PAD_RGMII_RXC__USB_H3_STROBE 0x40013030
- >;
- };
-
- pinctrl_usbh3_2: usbh3grp-2 {
- fsl,pins = <
- MX6QDL_PAD_RGMII_RXC__USB_H3_STROBE 0x40017030
- >;
- };
- };
-
- usdhc1 {
- pinctrl_usdhc1_1: usdhc1grp-1 {
- fsl,pins = <
- MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059
- MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059
- MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059
- MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059
- MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059
- MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059
- MX6QDL_PAD_NANDF_D0__SD1_DATA4 0x17059
- MX6QDL_PAD_NANDF_D1__SD1_DATA5 0x17059
- MX6QDL_PAD_NANDF_D2__SD1_DATA6 0x17059
- MX6QDL_PAD_NANDF_D3__SD1_DATA7 0x17059
- >;
- };
-
- pinctrl_usdhc1_2: usdhc1grp-2 {
- fsl,pins = <
- MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059
- MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059
- MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059
- MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059
- MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059
- MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059
- >;
- };
- };
-
- usdhc2 {
- pinctrl_usdhc2_1: usdhc2grp-1 {
- fsl,pins = <
- MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059
- MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059
- MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059
- MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059
- MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059
- MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059
- MX6QDL_PAD_NANDF_D4__SD2_DATA4 0x17059
- MX6QDL_PAD_NANDF_D5__SD2_DATA5 0x17059
- MX6QDL_PAD_NANDF_D6__SD2_DATA6 0x17059
- MX6QDL_PAD_NANDF_D7__SD2_DATA7 0x17059
- >;
- };
-
- pinctrl_usdhc2_2: usdhc2grp-2 {
- fsl,pins = <
- MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059
- MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059
- MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059
- MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059
- MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059
- MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059
- >;
- };
- };
-
- usdhc3 {
- pinctrl_usdhc3_1: usdhc3grp-1 {
- fsl,pins = <
- MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
- MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
- MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
- MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
- MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
- MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
- MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059
- MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059
- MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059
- MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059
- >;
- };
-
- pinctrl_usdhc3_1_100mhz: usdhc3grp-1-100mhz { /* 100Mhz */
- fsl,pins = <
- MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170b9
- MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100b9
- MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170b9
- MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170b9
- MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170b9
- MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170b9
- MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x170b9
- MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x170b9
- MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x170b9
- MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x170b9
- >;
- };
-
- pinctrl_usdhc3_1_200mhz: usdhc3grp-1-200mhz { /* 200Mhz */
- fsl,pins = <
- MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170f9
- MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100f9
- MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170f9
- MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170f9
- MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170f9
- MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170f9
- MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x170f9
- MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x170f9
- MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x170f9
- MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x170f9
- >;
- };
-
- pinctrl_usdhc3_2: usdhc3grp-2 {
- fsl,pins = <
- MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
- MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
- MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
- MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
- MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
- MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
- >;
- };
- };
-
- usdhc4 {
- pinctrl_usdhc4_1: usdhc4grp-1 {
- fsl,pins = <
- MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059
- MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059
- MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059
- MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059
- MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059
- MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059
- MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059
- MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059
- MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059
- MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059
- >;
- };
-
- pinctrl_usdhc4_2: usdhc4grp-2 {
- fsl,pins = <
- MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059
- MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059
- MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059
- MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059
- MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059
- MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059
- >;
- };
- };
-
- weim {
- pinctrl_weim_cs0_1: weim_cs0grp-1 {
- fsl,pins = <
- MX6QDL_PAD_EIM_CS0__EIM_CS0_B 0xb0b1
- >;
- };
-
- pinctrl_weim_nor_1: weim_norgrp-1 {
- fsl,pins = <
- MX6QDL_PAD_EIM_OE__EIM_OE_B 0xb0b1
- MX6QDL_PAD_EIM_RW__EIM_RW 0xb0b1
- MX6QDL_PAD_EIM_WAIT__EIM_WAIT_B 0xb060
- /* data */
- MX6QDL_PAD_EIM_D16__EIM_DATA16 0x1b0b0
- MX6QDL_PAD_EIM_D17__EIM_DATA17 0x1b0b0
- MX6QDL_PAD_EIM_D18__EIM_DATA18 0x1b0b0
- MX6QDL_PAD_EIM_D19__EIM_DATA19 0x1b0b0
- MX6QDL_PAD_EIM_D20__EIM_DATA20 0x1b0b0
- MX6QDL_PAD_EIM_D21__EIM_DATA21 0x1b0b0
- MX6QDL_PAD_EIM_D22__EIM_DATA22 0x1b0b0
- MX6QDL_PAD_EIM_D23__EIM_DATA23 0x1b0b0
- MX6QDL_PAD_EIM_D24__EIM_DATA24 0x1b0b0
- MX6QDL_PAD_EIM_D25__EIM_DATA25 0x1b0b0
- MX6QDL_PAD_EIM_D26__EIM_DATA26 0x1b0b0
- MX6QDL_PAD_EIM_D27__EIM_DATA27 0x1b0b0
- MX6QDL_PAD_EIM_D28__EIM_DATA28 0x1b0b0
- MX6QDL_PAD_EIM_D29__EIM_DATA29 0x1b0b0
- MX6QDL_PAD_EIM_D30__EIM_DATA30 0x1b0b0
- MX6QDL_PAD_EIM_D31__EIM_DATA31 0x1b0b0
- /* address */
- MX6QDL_PAD_EIM_A23__EIM_ADDR23 0xb0b1
- MX6QDL_PAD_EIM_A22__EIM_ADDR22 0xb0b1
- MX6QDL_PAD_EIM_A21__EIM_ADDR21 0xb0b1
- MX6QDL_PAD_EIM_A20__EIM_ADDR20 0xb0b1
- MX6QDL_PAD_EIM_A19__EIM_ADDR19 0xb0b1
- MX6QDL_PAD_EIM_A18__EIM_ADDR18 0xb0b1
- MX6QDL_PAD_EIM_A17__EIM_ADDR17 0xb0b1
- MX6QDL_PAD_EIM_A16__EIM_ADDR16 0xb0b1
- MX6QDL_PAD_EIM_DA15__EIM_AD15 0xb0b1
- MX6QDL_PAD_EIM_DA14__EIM_AD14 0xb0b1
- MX6QDL_PAD_EIM_DA13__EIM_AD13 0xb0b1
- MX6QDL_PAD_EIM_DA12__EIM_AD12 0xb0b1
- MX6QDL_PAD_EIM_DA11__EIM_AD11 0xb0b1
- MX6QDL_PAD_EIM_DA10__EIM_AD10 0xb0b1
- MX6QDL_PAD_EIM_DA9__EIM_AD09 0xb0b1
- MX6QDL_PAD_EIM_DA8__EIM_AD08 0xb0b1
- MX6QDL_PAD_EIM_DA7__EIM_AD07 0xb0b1
- MX6QDL_PAD_EIM_DA6__EIM_AD06 0xb0b1
- MX6QDL_PAD_EIM_DA5__EIM_AD05 0xb0b1
- MX6QDL_PAD_EIM_DA4__EIM_AD04 0xb0b1
- MX6QDL_PAD_EIM_DA3__EIM_AD03 0xb0b1
- MX6QDL_PAD_EIM_DA2__EIM_AD02 0xb0b1
- MX6QDL_PAD_EIM_DA1__EIM_AD01 0xb0b1
- MX6QDL_PAD_EIM_DA0__EIM_AD00 0xb0b1
- >;
- };
- };
};
ldb: ldb@020e0008 {
@@ -1433,18 +739,18 @@
dcic1: dcic@020e4000 {
reg = <0x020e4000 0x4000>;
- interrupts = <0 124 0x04>;
+ interrupts = <0 124 IRQ_TYPE_LEVEL_HIGH>;
};
dcic2: dcic@020e8000 {
reg = <0x020e8000 0x4000>;
- interrupts = <0 125 0x04>;
+ interrupts = <0 125 IRQ_TYPE_LEVEL_HIGH>;
};
sdma: sdma@020ec000 {
compatible = "fsl,imx6q-sdma", "fsl,imx35-sdma";
reg = <0x020ec000 0x4000>;
- interrupts = <0 2 0x04>;
+ interrupts = <0 2 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 155>, <&clks 155>;
clock-names = "ipg", "ahb";
#dma-cells = <3>;
@@ -1461,7 +767,8 @@
caam@02100000 {
reg = <0x02100000 0x40000>;
- interrupts = <0 105 0x04 0 106 0x04>;
+ interrupts = <0 105 IRQ_TYPE_LEVEL_HIGH>,
+ <0 106 IRQ_TYPE_LEVEL_HIGH>;
};
aipstz@0217c000 { /* AIPSTZ2 */
@@ -1471,7 +778,7 @@
usbotg: usb@02184000 {
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
reg = <0x02184000 0x200>;
- interrupts = <0 43 0x04>;
+ interrupts = <0 43 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 162>;
fsl,usbphy = <&usbphy1>;
fsl,usbmisc = <&usbmisc 0>;
@@ -1481,7 +788,7 @@
usbh1: usb@02184200 {
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
reg = <0x02184200 0x200>;
- interrupts = <0 40 0x04>;
+ interrupts = <0 40 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 162>;
fsl,usbphy = <&usbphy2>;
fsl,usbmisc = <&usbmisc 1>;
@@ -1491,7 +798,7 @@
usbh2: usb@02184400 {
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
reg = <0x02184400 0x200>;
- interrupts = <0 41 0x04>;
+ interrupts = <0 41 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 162>;
fsl,usbmisc = <&usbmisc 2>;
status = "disabled";
@@ -1500,7 +807,7 @@
usbh3: usb@02184600 {
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
reg = <0x02184600 0x200>;
- interrupts = <0 42 0x04>;
+ interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 162>;
fsl,usbmisc = <&usbmisc 3>;
status = "disabled";
@@ -1516,7 +823,9 @@
fec: ethernet@02188000 {
compatible = "fsl,imx6q-fec";
reg = <0x02188000 0x4000>;
- interrupts = <0 118 0x04 0 119 0x04>;
+ interrupts-extended =
+ <&intc 0 118 IRQ_TYPE_LEVEL_HIGH>,
+ <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 117>, <&clks 117>, <&clks 190>;
clock-names = "ipg", "ahb", "ptp";
status = "disabled";
@@ -1524,13 +833,15 @@
mlb@0218c000 {
reg = <0x0218c000 0x4000>;
- interrupts = <0 53 0x04 0 117 0x04 0 126 0x04>;
+ interrupts = <0 53 IRQ_TYPE_LEVEL_HIGH>,
+ <0 117 IRQ_TYPE_LEVEL_HIGH>,
+ <0 126 IRQ_TYPE_LEVEL_HIGH>;
};
usdhc1: usdhc@02190000 {
compatible = "fsl,imx6q-usdhc";
reg = <0x02190000 0x4000>;
- interrupts = <0 22 0x04>;
+ interrupts = <0 22 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 163>, <&clks 163>, <&clks 163>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
@@ -1540,7 +851,7 @@
usdhc2: usdhc@02194000 {
compatible = "fsl,imx6q-usdhc";
reg = <0x02194000 0x4000>;
- interrupts = <0 23 0x04>;
+ interrupts = <0 23 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 164>, <&clks 164>, <&clks 164>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
@@ -1550,7 +861,7 @@
usdhc3: usdhc@02198000 {
compatible = "fsl,imx6q-usdhc";
reg = <0x02198000 0x4000>;
- interrupts = <0 24 0x04>;
+ interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 165>, <&clks 165>, <&clks 165>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
@@ -1560,7 +871,7 @@
usdhc4: usdhc@0219c000 {
compatible = "fsl,imx6q-usdhc";
reg = <0x0219c000 0x4000>;
- interrupts = <0 25 0x04>;
+ interrupts = <0 25 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 166>, <&clks 166>, <&clks 166>;
clock-names = "ipg", "ahb", "per";
bus-width = <4>;
@@ -1572,7 +883,7 @@
#size-cells = <0>;
compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
- interrupts = <0 36 0x04>;
+ interrupts = <0 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 125>;
status = "disabled";
};
@@ -1582,7 +893,7 @@
#size-cells = <0>;
compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
reg = <0x021a4000 0x4000>;
- interrupts = <0 37 0x04>;
+ interrupts = <0 37 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 126>;
status = "disabled";
};
@@ -1592,7 +903,7 @@
#size-cells = <0>;
compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
reg = <0x021a8000 0x4000>;
- interrupts = <0 38 0x04>;
+ interrupts = <0 38 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 127>;
status = "disabled";
};
@@ -1613,7 +924,7 @@
weim: weim@021b8000 {
compatible = "fsl,imx6q-weim";
reg = <0x021b8000 0x4000>;
- interrupts = <0 14 0x04>;
+ interrupts = <0 14 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 196>;
};
@@ -1624,12 +935,12 @@
tzasc@021d0000 { /* TZASC1 */
reg = <0x021d0000 0x4000>;
- interrupts = <0 108 0x04>;
+ interrupts = <0 108 IRQ_TYPE_LEVEL_HIGH>;
};
tzasc@021d4000 { /* TZASC2 */
reg = <0x021d4000 0x4000>;
- interrupts = <0 109 0x04>;
+ interrupts = <0 109 IRQ_TYPE_LEVEL_HIGH>;
};
audmux: audmux@021d8000 {
@@ -1638,7 +949,7 @@
status = "disabled";
};
- mipi@021dc000 { /* MIPI-CSI */
+ mipi_csi: mipi@021dc000 {
reg = <0x021dc000 0x4000>;
};
@@ -1667,13 +978,13 @@
vdoa@021e4000 {
reg = <0x021e4000 0x4000>;
- interrupts = <0 18 0x04>;
+ interrupts = <0 18 IRQ_TYPE_LEVEL_HIGH>;
};
uart2: serial@021e8000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x021e8000 0x4000>;
- interrupts = <0 27 0x04>;
+ interrupts = <0 27 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 160>, <&clks 161>;
clock-names = "ipg", "per";
dmas = <&sdma 27 4 0>, <&sdma 28 4 0>;
@@ -1684,7 +995,7 @@
uart3: serial@021ec000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x021ec000 0x4000>;
- interrupts = <0 28 0x04>;
+ interrupts = <0 28 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 160>, <&clks 161>;
clock-names = "ipg", "per";
dmas = <&sdma 29 4 0>, <&sdma 30 4 0>;
@@ -1695,7 +1006,7 @@
uart4: serial@021f0000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x021f0000 0x4000>;
- interrupts = <0 29 0x04>;
+ interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 160>, <&clks 161>;
clock-names = "ipg", "per";
dmas = <&sdma 31 4 0>, <&sdma 32 4 0>;
@@ -1706,7 +1017,7 @@
uart5: serial@021f4000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x021f4000 0x4000>;
- interrupts = <0 30 0x04>;
+ interrupts = <0 30 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 160>, <&clks 161>;
clock-names = "ipg", "per";
dmas = <&sdma 33 4 0>, <&sdma 34 4 0>;
@@ -1720,7 +1031,8 @@
#size-cells = <0>;
compatible = "fsl,imx6q-ipu";
reg = <0x02400000 0x400000>;
- interrupts = <0 6 0x4 0 5 0x4>;
+ interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>,
+ <0 5 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 130>, <&clks 131>, <&clks 132>;
clock-names = "bus", "di0", "di1";
resets = <&src 2>;
diff --git a/arch/arm/boot/dts/imx6sl-evk.dts b/arch/arm/boot/dts/imx6sl-evk.dts
index cc68e19c5163..864d8dfb51ca 100644
--- a/arch/arm/boot/dts/imx6sl-evk.dts
+++ b/arch/arm/boot/dts/imx6sl-evk.dts
@@ -8,6 +8,8 @@
/dts-v1/;
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
#include "imx6sl.dtsi"
/ {
@@ -18,11 +20,26 @@
reg = <0x80000000 0x40000000>;
};
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_led>;
+
+ user {
+ label = "debug";
+ gpios = <&gpio3 20 GPIO_ACTIVE_HIGH>;
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
regulators {
compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
- reg_usb_otg1_vbus: usb_otg1_vbus {
+ reg_usb_otg1_vbus: regulator@0 {
compatible = "regulator-fixed";
+ reg = <0>;
regulator-name = "usb_otg1_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
@@ -30,22 +47,63 @@
enable-active-high;
};
- reg_usb_otg2_vbus: usb_otg2_vbus {
+ reg_usb_otg2_vbus: regulator@1 {
compatible = "regulator-fixed";
+ reg = <1>;
regulator-name = "usb_otg2_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
gpio = <&gpio4 2 0>;
enable-active-high;
};
+
+ reg_aud3v: regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "wm8962-supply-3v15";
+ regulator-min-microvolt = <3150000>;
+ regulator-max-microvolt = <3150000>;
+ regulator-boot-on;
+ };
+
+ reg_aud4v: regulator@3 {
+ compatible = "regulator-fixed";
+ reg = <3>;
+ regulator-name = "wm8962-supply-4v2";
+ regulator-min-microvolt = <4325000>;
+ regulator-max-microvolt = <4325000>;
+ regulator-boot-on;
+ };
+ };
+
+ sound {
+ compatible = "fsl,imx6sl-evk-wm8962", "fsl,imx-audio-wm8962";
+ model = "wm8962-audio";
+ ssi-controller = <&ssi2>;
+ audio-codec = <&codec>;
+ audio-routing =
+ "Headphone Jack", "HPOUTL",
+ "Headphone Jack", "HPOUTR",
+ "Ext Spk", "SPKOUTL",
+ "Ext Spk", "SPKOUTR",
+ "AMIC", "MICBIAS",
+ "IN3R", "AMIC";
+ mux-int-port = <2>;
+ mux-ext-port = <3>;
};
};
+&audmux {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux3>;
+ status = "okay";
+};
+
&ecspi1 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio4 11 0>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ecspi1_1>;
+ pinctrl-0 = <&pinctrl_ecspi1>;
status = "okay";
flash: m25p80@0 {
@@ -59,16 +117,144 @@
&fec {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec_1>;
+ pinctrl-0 = <&pinctrl_fec>;
phy-mode = "rmii";
status = "okay";
};
+&i2c1 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c1>;
+ status = "okay";
+
+ pmic: pfuze100@08 {
+ compatible = "fsl,pfuze100";
+ reg = <0x08>;
+
+ regulators {
+ sw1a_reg: sw1ab {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+ };
+
+ sw1c_reg: sw1c {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+ };
+
+ sw2_reg: sw2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3a_reg: sw3a {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3b_reg: sw3b {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw4_reg: sw4 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ swbst_reg: swbst {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5150000>;
+ };
+
+ snvs_reg: vsnvs {
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vref_reg: vrefddr {
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vgen1_reg: vgen1 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ regulator-always-on;
+ };
+
+ vgen2_reg: vgen2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen3_reg: vgen3 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ vgen4_reg: vgen4 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen5_reg: vgen5 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen6_reg: vgen6 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+ };
+};
+
+&i2c2 {
+ clock-frequency = <100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+ status = "okay";
+
+ codec: wm8962@1a {
+ compatible = "wlf,wm8962";
+ reg = <0x1a>;
+ clocks = <&clks IMX6SL_CLK_EXTERN_AUDIO>;
+ DCVDD-supply = <&vgen3_reg>;
+ DBVDD-supply = <&reg_aud3v>;
+ AVDD-supply = <&vgen3_reg>;
+ CPVDD-supply = <&vgen3_reg>;
+ MICVDD-supply = <&reg_aud3v>;
+ PLLVDD-supply = <&vgen3_reg>;
+ SPKVDD1-supply = <&reg_aud4v>;
+ SPKVDD2-supply = <&reg_aud4v>;
+ };
+};
+
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
- hog {
+ imx6sl-evk {
pinctrl_hog: hoggrp {
fsl,pins = <
MX6SL_PAD_KEY_ROW7__GPIO4_IO07 0x17059
@@ -78,21 +264,230 @@
MX6SL_PAD_REF_CLK_32K__GPIO3_IO22 0x17059
MX6SL_PAD_KEY_COL4__GPIO4_IO00 0x80000000
MX6SL_PAD_KEY_COL5__GPIO4_IO02 0x80000000
+ MX6SL_PAD_AUD_MCLK__AUDIO_CLK_OUT 0x4130b0
+ >;
+ };
+
+ pinctrl_audmux3: audmux3grp {
+ fsl,pins = <
+ MX6SL_PAD_AUD_RXD__AUD3_RXD 0x4130b0
+ MX6SL_PAD_AUD_TXC__AUD3_TXC 0x4130b0
+ MX6SL_PAD_AUD_TXD__AUD3_TXD 0x4110b0
+ MX6SL_PAD_AUD_TXFS__AUD3_TXFS 0x4130b0
+ >;
+ };
+
+ pinctrl_ecspi1: ecspi1grp {
+ fsl,pins = <
+ MX6SL_PAD_ECSPI1_MISO__ECSPI1_MISO 0x100b1
+ MX6SL_PAD_ECSPI1_MOSI__ECSPI1_MOSI 0x100b1
+ MX6SL_PAD_ECSPI1_SCLK__ECSPI1_SCLK 0x100b1
+ >;
+ };
+
+ pinctrl_fec: fecgrp {
+ fsl,pins = <
+ MX6SL_PAD_FEC_MDC__FEC_MDC 0x1b0b0
+ MX6SL_PAD_FEC_MDIO__FEC_MDIO 0x1b0b0
+ MX6SL_PAD_FEC_CRS_DV__FEC_RX_DV 0x1b0b0
+ MX6SL_PAD_FEC_RXD0__FEC_RX_DATA0 0x1b0b0
+ MX6SL_PAD_FEC_RXD1__FEC_RX_DATA1 0x1b0b0
+ MX6SL_PAD_FEC_TX_EN__FEC_TX_EN 0x1b0b0
+ MX6SL_PAD_FEC_TXD0__FEC_TX_DATA0 0x1b0b0
+ MX6SL_PAD_FEC_TXD1__FEC_TX_DATA1 0x1b0b0
+ MX6SL_PAD_FEC_REF_CLK__FEC_REF_OUT 0x4001b0a8
+ >;
+ };
+
+ pinctrl_i2c1: i2c1grp {
+ fsl,pins = <
+ MX6SL_PAD_I2C1_SCL__I2C1_SCL 0x4001b8b1
+ MX6SL_PAD_I2C1_SDA__I2C1_SDA 0x4001b8b1
+ >;
+ };
+
+
+ pinctrl_i2c2: i2c2grp {
+ fsl,pins = <
+ MX6SL_PAD_I2C2_SCL__I2C2_SCL 0x4001b8b1
+ MX6SL_PAD_I2C2_SDA__I2C2_SDA 0x4001b8b1
+ >;
+ };
+
+ pinctrl_led: ledgrp {
+ fsl,pins = <
+ MX6SL_PAD_HSIC_STROBE__GPIO3_IO20 0x17059
+ >;
+ };
+
+ pinctrl_kpp: kppgrp {
+ fsl,pins = <
+ MX6SL_PAD_KEY_ROW0__KEY_ROW0 0x1b010
+ MX6SL_PAD_KEY_ROW1__KEY_ROW1 0x1b010
+ MX6SL_PAD_KEY_ROW2__KEY_ROW2 0x1b0b0
+ MX6SL_PAD_KEY_COL0__KEY_COL0 0x110b0
+ MX6SL_PAD_KEY_COL1__KEY_COL1 0x110b0
+ MX6SL_PAD_KEY_COL2__KEY_COL2 0x110b0
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ MX6SL_PAD_UART1_RXD__UART1_RX_DATA 0x1b0b1
+ MX6SL_PAD_UART1_TXD__UART1_TX_DATA 0x1b0b1
+ >;
+ };
+
+ pinctrl_usbotg1: usbotg1grp {
+ fsl,pins = <
+ MX6SL_PAD_EPDC_PWRCOM__USB_OTG1_ID 0x17059
+ >;
+ };
+
+ pinctrl_usdhc1: usdhc1grp {
+ fsl,pins = <
+ MX6SL_PAD_SD1_CMD__SD1_CMD 0x17059
+ MX6SL_PAD_SD1_CLK__SD1_CLK 0x10059
+ MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x17059
+ MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x17059
+ MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x17059
+ MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x17059
+ MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x17059
+ MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x17059
+ MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x17059
+ MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x17059
+ >;
+ };
+
+ pinctrl_usdhc1_100mhz: usdhc1grp100mhz {
+ fsl,pins = <
+ MX6SL_PAD_SD1_CMD__SD1_CMD 0x170b9
+ MX6SL_PAD_SD1_CLK__SD1_CLK 0x100b9
+ MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x170b9
+ MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x170b9
+ MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x170b9
+ MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x170b9
+ MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x170b9
+ MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x170b9
+ MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x170b9
+ MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x170b9
+ >;
+ };
+
+ pinctrl_usdhc1_200mhz: usdhc1grp200mhz {
+ fsl,pins = <
+ MX6SL_PAD_SD1_CMD__SD1_CMD 0x170f9
+ MX6SL_PAD_SD1_CLK__SD1_CLK 0x100f9
+ MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x170f9
+ MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x170f9
+ MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x170f9
+ MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x170f9
+ MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x170f9
+ MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x170f9
+ MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x170f9
+ MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x170f9
+ >;
+ };
+
+ pinctrl_usdhc2: usdhc2grp {
+ fsl,pins = <
+ MX6SL_PAD_SD2_CMD__SD2_CMD 0x17059
+ MX6SL_PAD_SD2_CLK__SD2_CLK 0x10059
+ MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x17059
+ MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x17059
+ MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x17059
+ MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x17059
+ >;
+ };
+
+ pinctrl_usdhc2_100mhz: usdhc2grp100mhz {
+ fsl,pins = <
+ MX6SL_PAD_SD2_CMD__SD2_CMD 0x170b9
+ MX6SL_PAD_SD2_CLK__SD2_CLK 0x100b9
+ MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170b9
+ MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170b9
+ MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170b9
+ MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170b9
+ >;
+ };
+
+ pinctrl_usdhc2_200mhz: usdhc2grp200mhz {
+ fsl,pins = <
+ MX6SL_PAD_SD2_CMD__SD2_CMD 0x170f9
+ MX6SL_PAD_SD2_CLK__SD2_CLK 0x100f9
+ MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170f9
+ MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170f9
+ MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170f9
+ MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170f9
+ >;
+ };
+
+ pinctrl_usdhc3: usdhc3grp {
+ fsl,pins = <
+ MX6SL_PAD_SD3_CMD__SD3_CMD 0x17059
+ MX6SL_PAD_SD3_CLK__SD3_CLK 0x10059
+ MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+ MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+ MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+ MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+ >;
+ };
+
+ pinctrl_usdhc3_100mhz: usdhc3grp100mhz {
+ fsl,pins = <
+ MX6SL_PAD_SD3_CMD__SD3_CMD 0x170b9
+ MX6SL_PAD_SD3_CLK__SD3_CLK 0x100b9
+ MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x170b9
+ MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x170b9
+ MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x170b9
+ MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x170b9
+ >;
+ };
+
+ pinctrl_usdhc3_200mhz: usdhc3grp200mhz {
+ fsl,pins = <
+ MX6SL_PAD_SD3_CMD__SD3_CMD 0x170f9
+ MX6SL_PAD_SD3_CLK__SD3_CLK 0x100f9
+ MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x170f9
+ MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x170f9
+ MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x170f9
+ MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x170f9
>;
};
};
};
+&kpp {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_kpp>;
+ linux,keymap = <
+ MATRIX_KEY(0x0, 0x0, KEY_UP) /* ROW0, COL0 */
+ MATRIX_KEY(0x0, 0x1, KEY_DOWN) /* ROW0, COL1 */
+ MATRIX_KEY(0x0, 0x2, KEY_ENTER) /* ROW0, COL2 */
+ MATRIX_KEY(0x1, 0x0, KEY_HOME) /* ROW1, COL0 */
+ MATRIX_KEY(0x1, 0x1, KEY_RIGHT) /* ROW1, COL1 */
+ MATRIX_KEY(0x1, 0x2, KEY_LEFT) /* ROW1, COL2 */
+ MATRIX_KEY(0x2, 0x0, KEY_VOLUMEDOWN) /* ROW2, COL0 */
+ MATRIX_KEY(0x2, 0x1, KEY_VOLUMEUP) /* ROW2, COL1 */
+ >;
+ status = "okay";
+};
+
+&ssi2 {
+ fsl,mode = "i2s-slave";
+ status = "okay";
+};
+
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_1>;
+ pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
&usbotg1 {
vbus-supply = <&reg_usb_otg1_vbus>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_usbotg1_1>;
+ pinctrl-0 = <&pinctrl_usbotg1>;
disable-over-current;
status = "okay";
};
@@ -106,9 +501,9 @@
&usdhc1 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
- pinctrl-0 = <&pinctrl_usdhc1_1>;
- pinctrl-1 = <&pinctrl_usdhc1_1_100mhz>;
- pinctrl-2 = <&pinctrl_usdhc1_1_200mhz>;
+ pinctrl-0 = <&pinctrl_usdhc1>;
+ pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
+ pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
bus-width = <8>;
cd-gpios = <&gpio4 7 0>;
wp-gpios = <&gpio4 6 0>;
@@ -117,9 +512,9 @@
&usdhc2 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
- pinctrl-0 = <&pinctrl_usdhc2_1>;
- pinctrl-1 = <&pinctrl_usdhc2_1_100mhz>;
- pinctrl-2 = <&pinctrl_usdhc2_1_200mhz>;
+ pinctrl-0 = <&pinctrl_usdhc2>;
+ pinctrl-1 = <&pinctrl_usdhc2_100mhz>;
+ pinctrl-2 = <&pinctrl_usdhc2_200mhz>;
cd-gpios = <&gpio5 0 0>;
wp-gpios = <&gpio4 29 0>;
status = "okay";
@@ -127,9 +522,9 @@
&usdhc3 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
- pinctrl-0 = <&pinctrl_usdhc3_1>;
- pinctrl-1 = <&pinctrl_usdhc3_1_100mhz>;
- pinctrl-2 = <&pinctrl_usdhc3_1_200mhz>;
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ pinctrl-1 = <&pinctrl_usdhc3_100mhz>;
+ pinctrl-2 = <&pinctrl_usdhc3_200mhz>;
cd-gpios = <&gpio3 22 0>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi
index 28558f1aaf2d..3cb4941afeef 100644
--- a/arch/arm/boot/dts/imx6sl.dtsi
+++ b/arch/arm/boot/dts/imx6sl.dtsi
@@ -7,6 +7,7 @@
*
*/
+#include <dt-bindings/interrupt-controller/irq.h>
#include "skeleton.dtsi"
#include "imx6sl-pinfunc.h"
#include <dt-bindings/clock/imx6sl-clock.h>
@@ -27,6 +28,8 @@
spi1 = &ecspi2;
spi2 = &ecspi3;
spi3 = &ecspi4;
+ usbphy0 = &usbphy1;
+ usbphy1 = &usbphy2;
};
cpus {
@@ -38,6 +41,27 @@
device_type = "cpu";
reg = <0x0>;
next-level-cache = <&L2>;
+ operating-points = <
+ /* kHz uV */
+ 996000 1275000
+ 792000 1175000
+ 396000 975000
+ >;
+ fsl,soc-operating-points = <
+ /* ARM kHz SOC-PU uV */
+ 996000 1225000
+ 792000 1175000
+ 396000 1175000
+ >;
+ clock-latency = <61036>; /* two CLK32 periods */
+ clocks = <&clks IMX6SL_CLK_ARM>, <&clks IMX6SL_CLK_PLL2_PFD2>,
+ <&clks IMX6SL_CLK_STEP>, <&clks IMX6SL_CLK_PLL1_SW>,
+ <&clks IMX6SL_CLK_PLL1_SYS>;
+ clock-names = "arm", "pll2_pfd2_396m", "step",
+ "pll1_sw", "pll1_sys";
+ arm-supply = <&reg_arm>;
+ pu-supply = <&reg_pu>;
+ soc-supply = <&reg_soc>;
};
};
@@ -73,10 +97,16 @@
interrupt-parent = <&intc>;
ranges;
+ ocram: sram@00900000 {
+ compatible = "mmio-sram";
+ reg = <0x00900000 0x20000>;
+ clocks = <&clks IMX6SL_CLK_OCRAM>;
+ };
+
L2: l2-cache@00a02000 {
compatible = "arm,pl310-cache";
reg = <0x00a02000 0x1000>;
- interrupts = <0 92 0x04>;
+ interrupts = <0 92 IRQ_TYPE_LEVEL_HIGH>;
cache-unified;
cache-level = <2>;
arm,tag-latency = <4 2 3>;
@@ -85,7 +115,7 @@
pmu {
compatible = "arm,cortex-a9-pmu";
- interrupts = <0 94 0x04>;
+ interrupts = <0 94 IRQ_TYPE_LEVEL_HIGH>;
};
aips1: aips-bus@02000000 {
@@ -104,7 +134,7 @@
spdif: spdif@02004000 {
reg = <0x02004000 0x4000>;
- interrupts = <0 52 0x04>;
+ interrupts = <0 52 IRQ_TYPE_LEVEL_HIGH>;
};
ecspi1: ecspi@02008000 {
@@ -112,7 +142,7 @@
#size-cells = <0>;
compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi";
reg = <0x02008000 0x4000>;
- interrupts = <0 31 0x04>;
+ interrupts = <0 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_ECSPI1>,
<&clks IMX6SL_CLK_ECSPI1>;
clock-names = "ipg", "per";
@@ -124,7 +154,7 @@
#size-cells = <0>;
compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi";
reg = <0x0200c000 0x4000>;
- interrupts = <0 32 0x04>;
+ interrupts = <0 32 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_ECSPI2>,
<&clks IMX6SL_CLK_ECSPI2>;
clock-names = "ipg", "per";
@@ -136,7 +166,7 @@
#size-cells = <0>;
compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi";
reg = <0x02010000 0x4000>;
- interrupts = <0 33 0x04>;
+ interrupts = <0 33 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_ECSPI3>,
<&clks IMX6SL_CLK_ECSPI3>;
clock-names = "ipg", "per";
@@ -148,7 +178,7 @@
#size-cells = <0>;
compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi";
reg = <0x02014000 0x4000>;
- interrupts = <0 34 0x04>;
+ interrupts = <0 34 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_ECSPI4>,
<&clks IMX6SL_CLK_ECSPI4>;
clock-names = "ipg", "per";
@@ -159,7 +189,7 @@
compatible = "fsl,imx6sl-uart",
"fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x02018000 0x4000>;
- interrupts = <0 30 0x04>;
+ interrupts = <0 30 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_UART>,
<&clks IMX6SL_CLK_UART_SERIAL>;
clock-names = "ipg", "per";
@@ -172,7 +202,7 @@
compatible = "fsl,imx6sl-uart",
"fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x02020000 0x4000>;
- interrupts = <0 26 0x04>;
+ interrupts = <0 26 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_UART>,
<&clks IMX6SL_CLK_UART_SERIAL>;
clock-names = "ipg", "per";
@@ -185,7 +215,7 @@
compatible = "fsl,imx6sl-uart",
"fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x02024000 0x4000>;
- interrupts = <0 27 0x04>;
+ interrupts = <0 27 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_UART>,
<&clks IMX6SL_CLK_UART_SERIAL>;
clock-names = "ipg", "per";
@@ -195,9 +225,11 @@
};
ssi1: ssi@02028000 {
- compatible = "fsl,imx6sl-ssi","fsl,imx21-ssi";
+ compatible = "fsl,imx6sl-ssi",
+ "fsl,imx51-ssi",
+ "fsl,imx21-ssi";
reg = <0x02028000 0x4000>;
- interrupts = <0 46 0x04>;
+ interrupts = <0 46 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_SSI1>;
dmas = <&sdma 37 1 0>,
<&sdma 38 1 0>;
@@ -207,9 +239,11 @@
};
ssi2: ssi@0202c000 {
- compatible = "fsl,imx6sl-ssi","fsl,imx21-ssi";
+ compatible = "fsl,imx6sl-ssi",
+ "fsl,imx51-ssi",
+ "fsl,imx21-ssi";
reg = <0x0202c000 0x4000>;
- interrupts = <0 47 0x04>;
+ interrupts = <0 47 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_SSI2>;
dmas = <&sdma 41 1 0>,
<&sdma 42 1 0>;
@@ -219,9 +253,11 @@
};
ssi3: ssi@02030000 {
- compatible = "fsl,imx6sl-ssi","fsl,imx21-ssi";
+ compatible = "fsl,imx6sl-ssi",
+ "fsl,imx51-ssi",
+ "fsl,imx21-ssi";
reg = <0x02030000 0x4000>;
- interrupts = <0 48 0x04>;
+ interrupts = <0 48 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_SSI3>;
dmas = <&sdma 45 1 0>,
<&sdma 46 1 0>;
@@ -234,7 +270,7 @@
compatible = "fsl,imx6sl-uart",
"fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x02034000 0x4000>;
- interrupts = <0 28 0x04>;
+ interrupts = <0 28 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_UART>,
<&clks IMX6SL_CLK_UART_SERIAL>;
clock-names = "ipg", "per";
@@ -247,7 +283,7 @@
compatible = "fsl,imx6sl-uart",
"fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x02038000 0x4000>;
- interrupts = <0 29 0x04>;
+ interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_UART>,
<&clks IMX6SL_CLK_UART_SERIAL>;
clock-names = "ipg", "per";
@@ -261,7 +297,7 @@
#pwm-cells = <2>;
compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm";
reg = <0x02080000 0x4000>;
- interrupts = <0 83 0x04>;
+ interrupts = <0 83 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_PWM1>,
<&clks IMX6SL_CLK_PWM1>;
clock-names = "ipg", "per";
@@ -271,7 +307,7 @@
#pwm-cells = <2>;
compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm";
reg = <0x02084000 0x4000>;
- interrupts = <0 84 0x04>;
+ interrupts = <0 84 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_PWM2>,
<&clks IMX6SL_CLK_PWM2>;
clock-names = "ipg", "per";
@@ -281,7 +317,7 @@
#pwm-cells = <2>;
compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm";
reg = <0x02088000 0x4000>;
- interrupts = <0 85 0x04>;
+ interrupts = <0 85 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_PWM3>,
<&clks IMX6SL_CLK_PWM3>;
clock-names = "ipg", "per";
@@ -291,7 +327,7 @@
#pwm-cells = <2>;
compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm";
reg = <0x0208c000 0x4000>;
- interrupts = <0 86 0x04>;
+ interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_PWM4>,
<&clks IMX6SL_CLK_PWM4>;
clock-names = "ipg", "per";
@@ -300,7 +336,7 @@
gpt: gpt@02098000 {
compatible = "fsl,imx6sl-gpt";
reg = <0x02098000 0x4000>;
- interrupts = <0 55 0x04>;
+ interrupts = <0 55 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_GPT>,
<&clks IMX6SL_CLK_GPT_SERIAL>;
clock-names = "ipg", "per";
@@ -309,7 +345,8 @@
gpio1: gpio@0209c000 {
compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio";
reg = <0x0209c000 0x4000>;
- interrupts = <0 66 0x04 0 67 0x04>;
+ interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH>,
+ <0 67 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -319,7 +356,8 @@
gpio2: gpio@020a0000 {
compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio";
reg = <0x020a0000 0x4000>;
- interrupts = <0 68 0x04 0 69 0x04>;
+ interrupts = <0 68 IRQ_TYPE_LEVEL_HIGH>,
+ <0 69 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -329,7 +367,8 @@
gpio3: gpio@020a4000 {
compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio";
reg = <0x020a4000 0x4000>;
- interrupts = <0 70 0x04 0 71 0x04>;
+ interrupts = <0 70 IRQ_TYPE_LEVEL_HIGH>,
+ <0 71 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -339,7 +378,8 @@
gpio4: gpio@020a8000 {
compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio";
reg = <0x020a8000 0x4000>;
- interrupts = <0 72 0x04 0 73 0x04>;
+ interrupts = <0 72 IRQ_TYPE_LEVEL_HIGH>,
+ <0 73 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -349,7 +389,8 @@
gpio5: gpio@020ac000 {
compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio";
reg = <0x020ac000 0x4000>;
- interrupts = <0 74 0x04 0 75 0x04>;
+ interrupts = <0 74 IRQ_TYPE_LEVEL_HIGH>,
+ <0 75 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -357,21 +398,23 @@
};
kpp: kpp@020b8000 {
+ compatible = "fsl,imx6sl-kpp", "fsl,imx21-kpp";
reg = <0x020b8000 0x4000>;
- interrupts = <0 82 0x04>;
+ interrupts = <0 82 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX6SL_CLK_DUMMY>;
};
wdog1: wdog@020bc000 {
compatible = "fsl,imx6sl-wdt", "fsl,imx21-wdt";
reg = <0x020bc000 0x4000>;
- interrupts = <0 80 0x04>;
+ interrupts = <0 80 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_DUMMY>;
};
wdog2: wdog@020c0000 {
compatible = "fsl,imx6sl-wdt", "fsl,imx21-wdt";
reg = <0x020c0000 0x4000>;
- interrupts = <0 81 0x04>;
+ interrupts = <0 81 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_DUMMY>;
status = "disabled";
};
@@ -379,7 +422,8 @@
clks: ccm@020c4000 {
compatible = "fsl,imx6sl-ccm";
reg = <0x020c4000 0x4000>;
- interrupts = <0 87 0x04 0 88 0x04>;
+ interrupts = <0 87 IRQ_TYPE_LEVEL_HIGH>,
+ <0 88 IRQ_TYPE_LEVEL_HIGH>;
#clock-cells = <1>;
};
@@ -388,7 +432,9 @@
"fsl,imx6q-anatop",
"syscon", "simple-bus";
reg = <0x020c8000 0x1000>;
- interrupts = <0 49 0x04 0 54 0x04 0 127 0x04>;
+ interrupts = <0 49 IRQ_TYPE_LEVEL_HIGH>,
+ <0 54 IRQ_TYPE_LEVEL_HIGH>,
+ <0 127 IRQ_TYPE_LEVEL_HIGH>;
regulator-1p1@110 {
compatible = "fsl,anatop-regulator";
@@ -434,7 +480,7 @@
reg_arm: regulator-vddcore@140 {
compatible = "fsl,anatop-regulator";
- regulator-name = "cpu";
+ regulator-name = "vddarm";
regulator-min-microvolt = <725000>;
regulator-max-microvolt = <1450000>;
regulator-always-on;
@@ -487,15 +533,17 @@
usbphy1: usbphy@020c9000 {
compatible = "fsl,imx6sl-usbphy", "fsl,imx23-usbphy";
reg = <0x020c9000 0x1000>;
- interrupts = <0 44 0x04>;
+ interrupts = <0 44 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_USBPHY1>;
+ fsl,anatop = <&anatop>;
};
usbphy2: usbphy@020ca000 {
compatible = "fsl,imx6sl-usbphy", "fsl,imx23-usbphy";
reg = <0x020ca000 0x1000>;
- interrupts = <0 45 0x04>;
+ interrupts = <0 45 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_USBPHY2>;
+ fsl,anatop = <&anatop>;
};
snvs@020cc000 {
@@ -507,31 +555,33 @@
snvs-rtc-lp@34 {
compatible = "fsl,sec-v4.0-mon-rtc-lp";
reg = <0x34 0x58>;
- interrupts = <0 19 0x04 0 20 0x04>;
+ interrupts = <0 19 IRQ_TYPE_LEVEL_HIGH>,
+ <0 20 IRQ_TYPE_LEVEL_HIGH>;
};
};
epit1: epit@020d0000 {
reg = <0x020d0000 0x4000>;
- interrupts = <0 56 0x04>;
+ interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
};
epit2: epit@020d4000 {
reg = <0x020d4000 0x4000>;
- interrupts = <0 57 0x04>;
+ interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>;
};
src: src@020d8000 {
compatible = "fsl,imx6sl-src", "fsl,imx51-src";
reg = <0x020d8000 0x4000>;
- interrupts = <0 91 0x04 0 96 0x04>;
+ interrupts = <0 91 IRQ_TYPE_LEVEL_HIGH>,
+ <0 96 IRQ_TYPE_LEVEL_HIGH>;
#reset-cells = <1>;
};
gpc: gpc@020dc000 {
compatible = "fsl,imx6sl-gpc", "fsl,imx6q-gpc";
reg = <0x020dc000 0x4000>;
- interrupts = <0 89 0x04>;
+ interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>;
};
gpr: iomuxc-gpr@020e0000 {
@@ -543,235 +593,22 @@
iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6sl-iomuxc";
reg = <0x020e0000 0x4000>;
-
- ecspi1 {
- pinctrl_ecspi1_1: ecspi1grp-1 {
- fsl,pins = <
- MX6SL_PAD_ECSPI1_MISO__ECSPI1_MISO 0x100b1
- MX6SL_PAD_ECSPI1_MOSI__ECSPI1_MOSI 0x100b1
- MX6SL_PAD_ECSPI1_SCLK__ECSPI1_SCLK 0x100b1
- >;
- };
- };
-
- fec {
- pinctrl_fec_1: fecgrp-1 {
- fsl,pins = <
- MX6SL_PAD_FEC_MDC__FEC_MDC 0x1b0b0
- MX6SL_PAD_FEC_MDIO__FEC_MDIO 0x1b0b0
- MX6SL_PAD_FEC_CRS_DV__FEC_RX_DV 0x1b0b0
- MX6SL_PAD_FEC_RXD0__FEC_RX_DATA0 0x1b0b0
- MX6SL_PAD_FEC_RXD1__FEC_RX_DATA1 0x1b0b0
- MX6SL_PAD_FEC_TX_EN__FEC_TX_EN 0x1b0b0
- MX6SL_PAD_FEC_TXD0__FEC_TX_DATA0 0x1b0b0
- MX6SL_PAD_FEC_TXD1__FEC_TX_DATA1 0x1b0b0
- MX6SL_PAD_FEC_REF_CLK__FEC_REF_OUT 0x4001b0a8
- >;
- };
- };
-
- uart1 {
- pinctrl_uart1_1: uart1grp-1 {
- fsl,pins = <
- MX6SL_PAD_UART1_RXD__UART1_RX_DATA 0x1b0b1
- MX6SL_PAD_UART1_TXD__UART1_TX_DATA 0x1b0b1
- >;
- };
- };
-
- usbotg1 {
- pinctrl_usbotg1_1: usbotg1grp-1 {
- fsl,pins = <
- MX6SL_PAD_EPDC_PWRCOM__USB_OTG1_ID 0x17059
- >;
- };
-
- pinctrl_usbotg1_2: usbotg1grp-2 {
- fsl,pins = <
- MX6SL_PAD_FEC_RXD0__USB_OTG1_ID 0x17059
- >;
- };
-
- pinctrl_usbotg1_3: usbotg1grp-3 {
- fsl,pins = <
- MX6SL_PAD_LCD_DAT1__USB_OTG1_ID 0x17059
- >;
- };
-
- pinctrl_usbotg1_4: usbotg1grp-4 {
- fsl,pins = <
- MX6SL_PAD_REF_CLK_32K__USB_OTG1_ID 0x17059
- >;
- };
-
- pinctrl_usbotg1_5: usbotg1grp-5 {
- fsl,pins = <
- MX6SL_PAD_SD3_DAT0__USB_OTG1_ID 0x17059
- >;
- };
- };
-
- usbotg2 {
- pinctrl_usbotg2_1: usbotg2grp-1 {
- fsl,pins = <
- MX6SL_PAD_ECSPI1_SCLK__USB_OTG2_OC 0x17059
- >;
- };
-
- pinctrl_usbotg2_2: usbotg2grp-2 {
- fsl,pins = <
- MX6SL_PAD_ECSPI2_SCLK__USB_OTG2_OC 0x17059
- >;
- };
-
- pinctrl_usbotg2_3: usbotg2grp-3 {
- fsl,pins = <
- MX6SL_PAD_KEY_ROW5__USB_OTG2_OC 0x17059
- >;
- };
-
- pinctrl_usbotg2_4: usbotg2grp-4 {
- fsl,pins = <
- MX6SL_PAD_SD3_DAT2__USB_OTG2_OC 0x17059
- >;
- };
- };
-
- usdhc1 {
- pinctrl_usdhc1_1: usdhc1grp-1 {
- fsl,pins = <
- MX6SL_PAD_SD1_CMD__SD1_CMD 0x17059
- MX6SL_PAD_SD1_CLK__SD1_CLK 0x10059
- MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x17059
- MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x17059
- MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x17059
- MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x17059
- MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x17059
- MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x17059
- MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x17059
- MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x17059
- >;
- };
-
- pinctrl_usdhc1_1_100mhz: usdhc1grp-1-100mhz {
- fsl,pins = <
- MX6SL_PAD_SD1_CMD__SD1_CMD 0x170b9
- MX6SL_PAD_SD1_CLK__SD1_CLK 0x100b9
- MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x170b9
- MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x170b9
- MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x170b9
- MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x170b9
- MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x170b9
- MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x170b9
- MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x170b9
- MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x170b9
- >;
- };
-
- pinctrl_usdhc1_1_200mhz: usdhc1grp-1-200mhz {
- fsl,pins = <
- MX6SL_PAD_SD1_CMD__SD1_CMD 0x170f9
- MX6SL_PAD_SD1_CLK__SD1_CLK 0x100f9
- MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x170f9
- MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x170f9
- MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x170f9
- MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x170f9
- MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x170f9
- MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x170f9
- MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x170f9
- MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x170f9
- >;
- };
-
-
- };
-
- usdhc2 {
- pinctrl_usdhc2_1: usdhc2grp-1 {
- fsl,pins = <
- MX6SL_PAD_SD2_CMD__SD2_CMD 0x17059
- MX6SL_PAD_SD2_CLK__SD2_CLK 0x10059
- MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x17059
- MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x17059
- MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x17059
- MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x17059
- >;
- };
-
- pinctrl_usdhc2_1_100mhz: usdhc2grp-1-100mhz {
- fsl,pins = <
- MX6SL_PAD_SD2_CMD__SD2_CMD 0x170b9
- MX6SL_PAD_SD2_CLK__SD2_CLK 0x100b9
- MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170b9
- MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170b9
- MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170b9
- MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170b9
- >;
- };
-
- pinctrl_usdhc2_1_200mhz: usdhc2grp-1-200mhz {
- fsl,pins = <
- MX6SL_PAD_SD2_CMD__SD2_CMD 0x170f9
- MX6SL_PAD_SD2_CLK__SD2_CLK 0x100f9
- MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170f9
- MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170f9
- MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170f9
- MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170f9
- >;
- };
-
- };
-
- usdhc3 {
- pinctrl_usdhc3_1: usdhc3grp-1 {
- fsl,pins = <
- MX6SL_PAD_SD3_CMD__SD3_CMD 0x17059
- MX6SL_PAD_SD3_CLK__SD3_CLK 0x10059
- MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x17059
- MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x17059
- MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x17059
- MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x17059
- >;
- };
-
- pinctrl_usdhc3_1_100mhz: usdhc3grp-1-100mhz {
- fsl,pins = <
- MX6SL_PAD_SD3_CMD__SD3_CMD 0x170b9
- MX6SL_PAD_SD3_CLK__SD3_CLK 0x100b9
- MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x170b9
- MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x170b9
- MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x170b9
- MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x170b9
- >;
- };
-
- pinctrl_usdhc3_1_200mhz: usdhc3grp-1-200mhz {
- fsl,pins = <
- MX6SL_PAD_SD3_CMD__SD3_CMD 0x170f9
- MX6SL_PAD_SD3_CLK__SD3_CLK 0x100f9
- MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x170f9
- MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x170f9
- MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x170f9
- MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x170f9
- >;
- };
- };
};
csi: csi@020e4000 {
reg = <0x020e4000 0x4000>;
- interrupts = <0 7 0x04>;
+ interrupts = <0 7 IRQ_TYPE_LEVEL_HIGH>;
};
spdc: spdc@020e8000 {
reg = <0x020e8000 0x4000>;
- interrupts = <0 6 0x04>;
+ interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>;
};
sdma: sdma@020ec000 {
compatible = "fsl,imx6sl-sdma", "fsl,imx35-sdma";
reg = <0x020ec000 0x4000>;
- interrupts = <0 2 0x04>;
+ interrupts = <0 2 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_SDMA>,
<&clks IMX6SL_CLK_SDMA>;
clock-names = "ipg", "ahb";
@@ -782,22 +619,22 @@
pxp: pxp@020f0000 {
reg = <0x020f0000 0x4000>;
- interrupts = <0 98 0x04>;
+ interrupts = <0 98 IRQ_TYPE_LEVEL_HIGH>;
};
epdc: epdc@020f4000 {
reg = <0x020f4000 0x4000>;
- interrupts = <0 97 0x04>;
+ interrupts = <0 97 IRQ_TYPE_LEVEL_HIGH>;
};
lcdif: lcdif@020f8000 {
reg = <0x020f8000 0x4000>;
- interrupts = <0 39 0x04>;
+ interrupts = <0 39 IRQ_TYPE_LEVEL_HIGH>;
};
dcp: dcp@020fc000 {
reg = <0x020fc000 0x4000>;
- interrupts = <0 99 0x04>;
+ interrupts = <0 99 IRQ_TYPE_LEVEL_HIGH>;
};
};
@@ -811,7 +648,7 @@
usbotg1: usb@02184000 {
compatible = "fsl,imx6sl-usb", "fsl,imx27-usb";
reg = <0x02184000 0x200>;
- interrupts = <0 43 0x04>;
+ interrupts = <0 43 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_USBOH3>;
fsl,usbphy = <&usbphy1>;
fsl,usbmisc = <&usbmisc 0>;
@@ -821,7 +658,7 @@
usbotg2: usb@02184200 {
compatible = "fsl,imx6sl-usb", "fsl,imx27-usb";
reg = <0x02184200 0x200>;
- interrupts = <0 42 0x04>;
+ interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_USBOH3>;
fsl,usbphy = <&usbphy2>;
fsl,usbmisc = <&usbmisc 1>;
@@ -831,7 +668,7 @@
usbh: usb@02184400 {
compatible = "fsl,imx6sl-usb", "fsl,imx27-usb";
reg = <0x02184400 0x200>;
- interrupts = <0 40 0x04>;
+ interrupts = <0 40 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_USBOH3>;
fsl,usbmisc = <&usbmisc 2>;
status = "disabled";
@@ -847,7 +684,7 @@
fec: ethernet@02188000 {
compatible = "fsl,imx6sl-fec", "fsl,imx25-fec";
reg = <0x02188000 0x4000>;
- interrupts = <0 114 0x04>;
+ interrupts = <0 114 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_ENET_REF>,
<&clks IMX6SL_CLK_ENET_REF>;
clock-names = "ipg", "ahb";
@@ -857,7 +694,7 @@
usdhc1: usdhc@02190000 {
compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc";
reg = <0x02190000 0x4000>;
- interrupts = <0 22 0x04>;
+ interrupts = <0 22 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_USDHC1>,
<&clks IMX6SL_CLK_USDHC1>,
<&clks IMX6SL_CLK_USDHC1>;
@@ -869,7 +706,7 @@
usdhc2: usdhc@02194000 {
compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc";
reg = <0x02194000 0x4000>;
- interrupts = <0 23 0x04>;
+ interrupts = <0 23 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_USDHC2>,
<&clks IMX6SL_CLK_USDHC2>,
<&clks IMX6SL_CLK_USDHC2>;
@@ -881,7 +718,7 @@
usdhc3: usdhc@02198000 {
compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc";
reg = <0x02198000 0x4000>;
- interrupts = <0 24 0x04>;
+ interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_USDHC3>,
<&clks IMX6SL_CLK_USDHC3>,
<&clks IMX6SL_CLK_USDHC3>;
@@ -893,7 +730,7 @@
usdhc4: usdhc@0219c000 {
compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc";
reg = <0x0219c000 0x4000>;
- interrupts = <0 25 0x04>;
+ interrupts = <0 25 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_USDHC4>,
<&clks IMX6SL_CLK_USDHC4>,
<&clks IMX6SL_CLK_USDHC4>;
@@ -907,7 +744,7 @@
#size-cells = <0>;
compatible = "fsl,imx6sl-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
- interrupts = <0 36 0x04>;
+ interrupts = <0 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_I2C1>;
status = "disabled";
};
@@ -917,7 +754,7 @@
#size-cells = <0>;
compatible = "fsl,imx6sl-i2c", "fsl,imx21-i2c";
reg = <0x021a4000 0x4000>;
- interrupts = <0 37 0x04>;
+ interrupts = <0 37 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_I2C2>;
status = "disabled";
};
@@ -927,7 +764,7 @@
#size-cells = <0>;
compatible = "fsl,imx6sl-i2c", "fsl,imx21-i2c";
reg = <0x021a8000 0x4000>;
- interrupts = <0 38 0x04>;
+ interrupts = <0 38 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_I2C3>;
status = "disabled";
};
@@ -939,12 +776,12 @@
rngb: rngb@021b4000 {
reg = <0x021b4000 0x4000>;
- interrupts = <0 5 0x04>;
+ interrupts = <0 5 IRQ_TYPE_LEVEL_HIGH>;
};
weim: weim@021b8000 {
reg = <0x021b8000 0x4000>;
- interrupts = <0 14 0x04>;
+ interrupts = <0 14 IRQ_TYPE_LEVEL_HIGH>;
};
ocotp: ocotp@021bc000 {
diff --git a/arch/arm/boot/dts/integratorap.dts b/arch/arm/boot/dts/integratorap.dts
index e6be9315ff0a..b10e6351da53 100644
--- a/arch/arm/boot/dts/integratorap.dts
+++ b/arch/arm/boot/dts/integratorap.dts
@@ -18,6 +18,28 @@
bootargs = "root=/dev/ram0 console=ttyAM0,38400n8 earlyprintk";
};
+ /* 24 MHz chrystal on the core module */
+ xtal24mhz: xtal24mhz@24M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ };
+
+ pclk: pclk@0 {
+ #clock-cells = <0>;
+ compatible = "fixed-factor-clock";
+ clock-div = <1>;
+ clock-mult = <1>;
+ clocks = <&xtal24mhz>;
+ };
+
+ /* The UART clock is 14.74 MHz divided by an ICS525 */
+ uartclk: uartclk@14.74M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <14745600>;
+ };
+
syscon {
compatible = "arm,integrator-ap-syscon";
reg = <0x11000000 0x100>;
@@ -28,14 +50,17 @@
timer0: timer@13000000 {
compatible = "arm,integrator-timer";
+ clocks = <&xtal24mhz>;
};
timer1: timer@13000100 {
compatible = "arm,integrator-timer";
+ clocks = <&xtal24mhz>;
};
timer2: timer@13000200 {
compatible = "arm,integrator-timer";
+ clocks = <&xtal24mhz>;
};
pic: pic@14000000 {
@@ -92,26 +117,36 @@
rtc: rtc@15000000 {
compatible = "arm,pl030", "arm,primecell";
arm,primecell-periphid = <0x00041030>;
+ clocks = <&pclk>;
+ clock-names = "apb_pclk";
};
uart0: uart@16000000 {
compatible = "arm,pl010", "arm,primecell";
arm,primecell-periphid = <0x00041010>;
+ clocks = <&uartclk>, <&pclk>;
+ clock-names = "uartclk", "apb_pclk";
};
uart1: uart@17000000 {
compatible = "arm,pl010", "arm,primecell";
arm,primecell-periphid = <0x00041010>;
+ clocks = <&uartclk>, <&pclk>;
+ clock-names = "uartclk", "apb_pclk";
};
kmi0: kmi@18000000 {
compatible = "arm,pl050", "arm,primecell";
arm,primecell-periphid = <0x00041050>;
+ clocks = <&xtal24mhz>, <&pclk>;
+ clock-names = "KMIREFCLK", "apb_pclk";
};
kmi1: kmi@19000000 {
compatible = "arm,pl050", "arm,primecell";
arm,primecell-periphid = <0x00041050>;
+ clocks = <&xtal24mhz>, <&pclk>;
+ clock-names = "KMIREFCLK", "apb_pclk";
};
};
};
diff --git a/arch/arm/boot/dts/integratorcp.dts b/arch/arm/boot/dts/integratorcp.dts
index a21c17de9a5e..d43f15b4f79a 100644
--- a/arch/arm/boot/dts/integratorcp.dts
+++ b/arch/arm/boot/dts/integratorcp.dts
@@ -13,25 +13,107 @@
bootargs = "root=/dev/ram0 console=ttyAMA0,38400n8 earlyprintk";
};
+ /*
+ * The Integrator/CP overall clocking architecture can be found in
+ * ARM DUI 0184B page 7-28 "Integrator/CP922T system clocks" which
+ * appear to illustrate the layout used in most configurations.
+ */
+
+ /* The codec chrystal operates at 24.576 MHz */
+ xtal_codec: xtal24.576@24.576M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24576000>;
+ };
+
+ /* The chrystal is divided by 2 by the codec for the AACI bit clock */
+ aaci_bitclk: aaci_bitclk@12.288M {
+ #clock-cells = <0>;
+ compatible = "fixed-factor-clock";
+ clock-div = <2>;
+ clock-mult = <1>;
+ clocks = <&xtal_codec>;
+ };
+
+ /* This is a 25MHz chrystal on the base board */
+ xtal25mhz: xtal25mhz@25M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <25000000>;
+ };
+
+ /* The UART clock is 14.74 MHz divided from 25MHz by an ICS525 */
+ uartclk: uartclk@14.74M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <14745600>;
+ };
+
+ /* Actually sysclk I think */
+ pclk: pclk@0 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <0>;
+ };
+
+ core-module@10000000 {
+ /* 24 MHz chrystal on the core module */
+ xtal24mhz: xtal24mhz@24M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ };
+
+ /*
+ * External oscillator on the core module, usually used
+ * to drive video circuitry. Driven from the 24MHz clock.
+ */
+ auxosc: cm_aux_osc@25M {
+ #clock-cells = <0>;
+ compatible = "arm,integrator-cm-auxosc";
+ clocks = <&xtal24mhz>;
+ };
+
+ /* The KMI clock is the 24 MHz oscillator divided to 8MHz */
+ kmiclk: kmiclk@1M {
+ #clock-cells = <0>;
+ compatible = "fixed-factor-clock";
+ clock-div = <3>;
+ clock-mult = <1>;
+ clocks = <&xtal24mhz>;
+ };
+
+ /* The timer clock is the 24 MHz oscillator divided to 1MHz */
+ timclk: timclk@1M {
+ #clock-cells = <0>;
+ compatible = "fixed-factor-clock";
+ clock-div = <24>;
+ clock-mult = <1>;
+ clocks = <&xtal24mhz>;
+ };
+ };
+
syscon {
compatible = "arm,integrator-cp-syscon";
reg = <0xcb000000 0x100>;
};
timer0: timer@13000000 {
- /* TIMER0 runs @ 25MHz */
+ /* TIMER0 runs directly on the 25MHz chrystal */
compatible = "arm,integrator-cp-timer";
- status = "disabled";
+ clocks = <&xtal25mhz>;
};
timer1: timer@13000100 {
/* TIMER1 runs @ 1MHz */
compatible = "arm,integrator-cp-timer";
+ clocks = <&timclk>;
};
timer2: timer@13000200 {
/* TIMER2 runs @ 1MHz */
compatible = "arm,integrator-cp-timer";
+ clocks = <&timclk>;
};
pic: pic@14000000 {
@@ -74,22 +156,32 @@
*/
rtc@15000000 {
compatible = "arm,pl031", "arm,primecell";
+ clocks = <&pclk>;
+ clock-names = "apb_pclk";
};
uart@16000000 {
compatible = "arm,pl011", "arm,primecell";
+ clocks = <&uartclk>, <&pclk>;
+ clock-names = "uartclk", "apb_pclk";
};
uart@17000000 {
compatible = "arm,pl011", "arm,primecell";
+ clocks = <&uartclk>, <&pclk>;
+ clock-names = "uartclk", "apb_pclk";
};
kmi@18000000 {
compatible = "arm,pl050", "arm,primecell";
+ clocks = <&kmiclk>, <&pclk>;
+ clock-names = "KMIREFCLK", "apb_pclk";
};
kmi@19000000 {
compatible = "arm,pl050", "arm,primecell";
+ clocks = <&kmiclk>, <&pclk>;
+ clock-names = "KMIREFCLK", "apb_pclk";
};
/*
@@ -100,18 +192,24 @@
reg = <0x1c000000 0x1000>;
interrupts = <23 24>;
max-frequency = <515633>;
+ clocks = <&uartclk>, <&pclk>;
+ clock-names = "mclk", "apb_pclk";
};
aaci@1d000000 {
compatible = "arm,pl041", "arm,primecell";
reg = <0x1d000000 0x1000>;
interrupts = <25>;
+ clocks = <&pclk>;
+ clock-names = "apb_pclk";
};
clcd@c0000000 {
compatible = "arm,pl110", "arm,primecell";
reg = <0xC0000000 0x1000>;
interrupts = <22>;
+ clocks = <&auxosc>, <&pclk>;
+ clock-names = "clcd", "apb_pclk";
};
};
};
diff --git a/arch/arm/boot/dts/k2e-clocks.dtsi b/arch/arm/boot/dts/k2e-clocks.dtsi
new file mode 100644
index 000000000000..90774d604bc1
--- /dev/null
+++ b/arch/arm/boot/dts/k2e-clocks.dtsi
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 Texas Instruments, Inc.
+ *
+ * Keystone 2 Edison SoC specific device tree
+ *
+ * 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.
+ */
+
+clocks {
+ mainpllclk: mainpllclk@2310110 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,main-pll-clock";
+ clocks = <&refclksys>;
+ reg = <0x02620350 4>, <0x02310110 4>;
+ reg-names = "control", "multiplier";
+ fixed-postdiv = <2>;
+ };
+
+ papllclk: papllclk@2620358 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,pll-clock";
+ clocks = <&refclkpass>;
+ clock-output-names = "pa-pll-clk";
+ reg = <0x02620358 4>;
+ reg-names = "control";
+ };
+
+ ddr3apllclk: ddr3apllclk@2620360 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,pll-clock";
+ clocks = <&refclkddr3a>;
+ clock-output-names = "ddr-3a-pll-clk";
+ reg = <0x02620360 4>;
+ reg-names = "control";
+ };
+
+ clkusb1: clkusb1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk16>;
+ clock-output-names = "usb";
+ reg = <0x02350004 0xb00>, <0x02350000 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <0>;
+ };
+
+ clkhyperlink0: clkhyperlink0 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk12>;
+ clock-output-names = "hyperlink-0";
+ reg = <0x02350030 0xb00>, <0x02350014 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <5>;
+ };
+
+ clkpcie1: clkpcie1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk12>;
+ clock-output-names = "pcie";
+ reg = <0x0235006c 0xb00>, <0x02350000 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <18>;
+ };
+
+ clkxge: clkxge {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "xge";
+ reg = <0x023500c8 0xb00>, <0x02350074 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <29>;
+ };
+};
diff --git a/arch/arm/boot/dts/k2e-evm.dts b/arch/arm/boot/dts/k2e-evm.dts
new file mode 100644
index 000000000000..74b3b63e94cf
--- /dev/null
+++ b/arch/arm/boot/dts/k2e-evm.dts
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2013-2014 Texas Instruments, Inc.
+ *
+ * Keystone 2 Edison EVM device tree
+ *
+ * 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.
+ */
+/dts-v1/;
+
+#include "keystone.dtsi"
+#include "k2e.dtsi"
+
+/ {
+ compatible = "ti,k2e-evm","ti,keystone";
+ model = "Texas Instruments Keystone 2 Edison EVM";
+
+ soc {
+
+ clocks {
+ refclksys: refclksys {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <100000000>;
+ clock-output-names = "refclk-sys";
+ };
+
+ refclkpass: refclkpass {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <100000000>;
+ clock-output-names = "refclk-pass";
+ };
+
+ refclkddr3a: refclkddr3a {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <100000000>;
+ clock-output-names = "refclk-ddr3a";
+ };
+ };
+ };
+};
+
+&usb_phy {
+ status = "okay";
+};
+
+&usb {
+ status = "okay";
+};
+
+&usb1_phy {
+ status = "okay";
+};
+
+&usb1 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/k2e.dtsi b/arch/arm/boot/dts/k2e.dtsi
new file mode 100644
index 000000000000..03d01909525b
--- /dev/null
+++ b/arch/arm/boot/dts/k2e.dtsi
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2013-2014 Texas Instruments, Inc.
+ *
+ * Keystone 2 Edison soc device tree
+ *
+ * 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.
+ */
+
+/ {
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupt-parent = <&gic>;
+
+ cpu@0 {
+ compatible = "arm,cortex-a15";
+ device_type = "cpu";
+ reg = <0>;
+ };
+
+ cpu@1 {
+ compatible = "arm,cortex-a15";
+ device_type = "cpu";
+ reg = <1>;
+ };
+
+ cpu@2 {
+ compatible = "arm,cortex-a15";
+ device_type = "cpu";
+ reg = <2>;
+ };
+
+ cpu@3 {
+ compatible = "arm,cortex-a15";
+ device_type = "cpu";
+ reg = <3>;
+ };
+ };
+
+ soc {
+ /include/ "k2e-clocks.dtsi"
+
+ usb: usb@2680000 {
+ interrupts = <GIC_SPI 152 IRQ_TYPE_EDGE_RISING>;
+ dwc3@2690000 {
+ interrupts = <GIC_SPI 152 IRQ_TYPE_EDGE_RISING>;
+ };
+ };
+
+ usb1_phy: usb_phy@2620750 {
+ compatible = "ti,keystone-usbphy";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x2620750 24>;
+ status = "disabled";
+ };
+
+ usb1: usb@25000000 {
+ compatible = "ti,keystone-dwc3";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x25000000 0x10000>;
+ clocks = <&clkusb1>;
+ clock-names = "usb";
+ interrupts = <GIC_SPI 414 IRQ_TYPE_EDGE_RISING>;
+ ranges;
+ status = "disabled";
+
+ dwc3@25010000 {
+ compatible = "synopsys,dwc3";
+ reg = <0x25010000 0x70000>;
+ interrupts = <GIC_SPI 414 IRQ_TYPE_EDGE_RISING>;
+ usb-phy = <&usb1_phy>, <&usb1_phy>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/k2hk-clocks.dtsi b/arch/arm/boot/dts/k2hk-clocks.dtsi
new file mode 100644
index 000000000000..96e65365afe3
--- /dev/null
+++ b/arch/arm/boot/dts/k2hk-clocks.dtsi
@@ -0,0 +1,426 @@
+/*
+ * Copyright 2013-2014 Texas Instruments, Inc.
+ *
+ * Keystone 2 Kepler/Hawking SoC clock nodes
+ *
+ * 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.
+ */
+
+clocks {
+ armpllclk: armpllclk@2620370 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,pll-clock";
+ clocks = <&refclkarm>;
+ clock-output-names = "arm-pll-clk";
+ reg = <0x02620370 4>;
+ reg-names = "control";
+ };
+
+ mainpllclk: mainpllclk@2310110 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,main-pll-clock";
+ clocks = <&refclksys>;
+ reg = <0x02620350 4>, <0x02310110 4>;
+ reg-names = "control", "multiplier";
+ fixed-postdiv = <2>;
+ };
+
+ papllclk: papllclk@2620358 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,pll-clock";
+ clocks = <&refclkpass>;
+ clock-output-names = "pa-pll-clk";
+ reg = <0x02620358 4>;
+ reg-names = "control";
+ };
+
+ ddr3apllclk: ddr3apllclk@2620360 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,pll-clock";
+ clocks = <&refclkddr3a>;
+ clock-output-names = "ddr-3a-pll-clk";
+ reg = <0x02620360 4>;
+ reg-names = "control";
+ };
+
+ ddr3bpllclk: ddr3bpllclk@2620368 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,pll-clock";
+ clocks = <&refclkddr3b>;
+ clock-output-names = "ddr-3b-pll-clk";
+ reg = <0x02620368 4>;
+ reg-names = "control";
+ };
+
+ clktsip: clktsip {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk16>;
+ clock-output-names = "tsip";
+ reg = <0x02350000 0xb00>, <0x02350000 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <0>;
+ };
+
+ clksrio: clksrio {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk1rstiso13>;
+ clock-output-names = "srio";
+ reg = <0x0235002c 0xb00>, <0x02350010 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <4>;
+ };
+
+ clkhyperlink0: clkhyperlink0 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk12>;
+ clock-output-names = "hyperlink-0";
+ reg = <0x02350030 0xb00>, <0x02350014 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <5>;
+ };
+
+ clkgem1: clkgem1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk1>;
+ clock-output-names = "gem1";
+ reg = <0x02350040 0xb00>, <0x02350024 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <9>;
+ };
+
+ clkgem2: clkgem2 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk1>;
+ clock-output-names = "gem2";
+ reg = <0x02350044 0xb00>, <0x02350028 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <10>;
+ };
+
+ clkgem3: clkgem3 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk1>;
+ clock-output-names = "gem3";
+ reg = <0x02350048 0xb00>, <0x0235002c 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <11>;
+ };
+
+ clkgem4: clkgem4 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk1>;
+ clock-output-names = "gem4";
+ reg = <0x0235004c 0xb00>, <0x02350030 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <12>;
+ };
+
+ clkgem5: clkgem5 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk1>;
+ clock-output-names = "gem5";
+ reg = <0x02350050 0xb00>, <0x02350034 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <13>;
+ };
+
+ clkgem6: clkgem6 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk1>;
+ clock-output-names = "gem6";
+ reg = <0x02350054 0xb00>, <0x02350038 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <14>;
+ };
+
+ clkgem7: clkgem7 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk1>;
+ clock-output-names = "gem7";
+ reg = <0x02350058 0xb00>, <0x0235003c 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <15>;
+ };
+
+ clkddr31: clkddr31 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "ddr3-1";
+ reg = <0x02350060 0xb00>, <0x02350040 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <16>;
+ };
+
+ clktac: clktac {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "tac";
+ reg = <0x02350064 0xb00>, <0x02350044 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <17>;
+ };
+
+ clkrac01: clkrac01 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "rac-01";
+ reg = <0x02350068 0xb00>, <0x02350044 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <17>;
+ };
+
+ clkrac23: clkrac23 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "rac-23";
+ reg = <0x0235006c 0xb00>, <0x02350048 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <18>;
+ };
+
+ clkfftc0: clkfftc0 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "fftc-0";
+ reg = <0x02350070 0xb00>, <0x0235004c 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <19>;
+ };
+
+ clkfftc1: clkfftc1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "fftc-1";
+ reg = <0x02350074 0xb00>, <0x0235004c 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <19>;
+ };
+
+ clkfftc2: clkfftc2 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "fftc-2";
+ reg = <0x02350078 0xb00>, <0x02350050 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <20>;
+ };
+
+ clkfftc3: clkfftc3 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "fftc-3";
+ reg = <0x0235007c 0xb00>, <0x02350050 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <20>;
+ };
+
+ clkfftc4: clkfftc4 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "fftc-4";
+ reg = <0x02350080 0xb00>, <0x02350050 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <20>;
+ };
+
+ clkfftc5: clkfftc5 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "fftc-5";
+ reg = <0x02350084 0xb00>, <0x02350050 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <20>;
+ };
+
+ clkaif: clkaif {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "aif";
+ reg = <0x02350088 0xb00>, <0x02350054 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <21>;
+ };
+
+ clktcp3d0: clktcp3d0 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "tcp3d-0";
+ reg = <0x0235008c 0xb00>, <0x02350058 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <22>;
+ };
+
+ clktcp3d1: clktcp3d1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "tcp3d-1";
+ reg = <0x02350090 0xb00>, <0x02350058 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <22>;
+ };
+
+ clktcp3d2: clktcp3d2 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "tcp3d-2";
+ reg = <0x02350094 0xb00>, <0x0235005c 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <23>;
+ };
+
+ clktcp3d3: clktcp3d3 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "tcp3d-3";
+ reg = <0x02350098 0xb00>, <0x0235005c 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <23>;
+ };
+
+ clkvcp0: clkvcp0 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-0";
+ reg = <0x0235009c 0xb00>, <0x02350060 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <24>;
+ };
+
+ clkvcp1: clkvcp1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-1";
+ reg = <0x023500a0 0xb00>, <0x02350060 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <24>;
+ };
+
+ clkvcp2: clkvcp2 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-2";
+ reg = <0x023500a4 0xb00>, <0x02350060 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <24>;
+ };
+
+ clkvcp3: clkvcp3 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-3";
+ reg = <0x023500a8 0xb00>, <0x02350060 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <24>;
+ };
+
+ clkvcp4: clkvcp4 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-4";
+ reg = <0x023500ac 0xb00>, <0x02350064 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <25>;
+ };
+
+ clkvcp5: clkvcp5 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-5";
+ reg = <0x023500b0 0xb00>, <0x02350064 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <25>;
+ };
+
+ clkvcp6: clkvcp6 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-6";
+ reg = <0x023500b4 0xb00>, <0x02350064 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <25>;
+ };
+
+ clkvcp7: clkvcp7 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-7";
+ reg = <0x023500b8 0xb00>, <0x02350064 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <25>;
+ };
+
+ clkbcp: clkbcp {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "bcp";
+ reg = <0x023500bc 0xb00>, <0x02350068 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <26>;
+ };
+
+ clkdxb: clkdxb {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "dxb";
+ reg = <0x023500c0 0xb00>, <0x0235006c 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <27>;
+ };
+
+ clkhyperlink1: clkhyperlink1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk12>;
+ clock-output-names = "hyperlink-1";
+ reg = <0x023500c4 0xb00>, <0x02350070 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <28>;
+ };
+
+ clkxge: clkxge {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "xge";
+ reg = <0x023500c8 0xb00>, <0x02350074 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <29>;
+ };
+};
diff --git a/arch/arm/boot/dts/k2hk-evm.dts b/arch/arm/boot/dts/k2hk-evm.dts
index eaefdfef65c3..c93d06f9f2a8 100644
--- a/arch/arm/boot/dts/k2hk-evm.dts
+++ b/arch/arm/boot/dts/k2hk-evm.dts
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Texas Instruments, Inc.
+ * Copyright 2013-2014 Texas Instruments, Inc.
*
* Keystone 2 Kepler/Hawking EVM device tree
*
@@ -10,12 +10,14 @@
/dts-v1/;
#include "keystone.dtsi"
+#include "k2hk.dtsi"
/ {
- compatible = "ti,keystone-evm";
+ compatible = "ti,k2hk-evm","ti,keystone";
+ model = "Texas Instruments Keystone 2 Kepler/Hawking EVM";
soc {
- clock {
+ clocks {
refclksys: refclksys {
#clock-cells = <0>;
compatible = "fixed-clock";
@@ -52,6 +54,29 @@
};
};
};
+
+ leds {
+ compatible = "gpio-leds";
+ debug1_1 {
+ label = "keystone:green:debug1";
+ gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>; /* 12 */
+ };
+
+ debug1_2 {
+ label = "keystone:red:debug1";
+ gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; /* 13 */
+ };
+
+ debug2 {
+ label = "keystone:blue:debug2";
+ gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; /* 14 */
+ };
+
+ debug3 {
+ label = "keystone:blue:debug3";
+ gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; /* 15 */
+ };
+ };
};
&usb_phy {
@@ -61,3 +86,55 @@
&usb {
status = "okay";
};
+
+&aemif {
+ cs0 {
+ #address-cells = <2>;
+ #size-cells = <1>;
+ clock-ranges;
+ ranges;
+
+ ti,cs-chipselect = <0>;
+ /* all timings in nanoseconds */
+ ti,cs-min-turnaround-ns = <12>;
+ ti,cs-read-hold-ns = <6>;
+ ti,cs-read-strobe-ns = <23>;
+ ti,cs-read-setup-ns = <9>;
+ ti,cs-write-hold-ns = <8>;
+ ti,cs-write-strobe-ns = <23>;
+ ti,cs-write-setup-ns = <8>;
+
+ nand@0,0 {
+ compatible = "ti,keystone-nand","ti,davinci-nand";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0 0 0x4000000
+ 1 0 0x0000100>;
+
+ ti,davinci-chipselect = <0>;
+ ti,davinci-mask-ale = <0x2000>;
+ ti,davinci-mask-cle = <0x4000>;
+ ti,davinci-mask-chipsel = <0>;
+ nand-ecc-mode = "hw";
+ ti,davinci-ecc-bits = <4>;
+ nand-on-flash-bbt;
+
+ partition@0 {
+ label = "u-boot";
+ reg = <0x0 0x100000>;
+ read-only;
+ };
+
+ partition@100000 {
+ label = "params";
+ reg = <0x100000 0x80000>;
+ read-only;
+ };
+
+ partition@180000 {
+ label = "ubifs";
+ reg = <0x180000 0x1fe80000>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/k2hk.dtsi b/arch/arm/boot/dts/k2hk.dtsi
new file mode 100644
index 000000000000..c73899c73118
--- /dev/null
+++ b/arch/arm/boot/dts/k2hk.dtsi
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2013-2014 Texas Instruments, Inc.
+ *
+ * Keystone 2 Kepler/Hawking soc specific device tree
+ *
+ * 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.
+ */
+
+/ {
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupt-parent = <&gic>;
+
+ cpu@0 {
+ compatible = "arm,cortex-a15";
+ device_type = "cpu";
+ reg = <0>;
+ };
+
+ cpu@1 {
+ compatible = "arm,cortex-a15";
+ device_type = "cpu";
+ reg = <1>;
+ };
+
+ cpu@2 {
+ compatible = "arm,cortex-a15";
+ device_type = "cpu";
+ reg = <2>;
+ };
+
+ cpu@3 {
+ compatible = "arm,cortex-a15";
+ device_type = "cpu";
+ reg = <3>;
+ };
+ };
+
+ soc {
+ /include/ "k2hk-clocks.dtsi"
+ };
+};
diff --git a/arch/arm/boot/dts/k2l-clocks.dtsi b/arch/arm/boot/dts/k2l-clocks.dtsi
new file mode 100644
index 000000000000..f584b80200f8
--- /dev/null
+++ b/arch/arm/boot/dts/k2l-clocks.dtsi
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2013-2014 Texas Instruments, Inc.
+ *
+ * Keystone 2 lamarr SoC clock nodes
+ *
+ * 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.
+ */
+
+clocks {
+ armpllclk: armpllclk@2620370 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,pll-clock";
+ clocks = <&refclksys>;
+ clock-output-names = "arm-pll-clk";
+ reg = <0x02620370 4>;
+ reg-names = "control";
+ };
+
+ mainpllclk: mainpllclk@2310110 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,main-pll-clock";
+ clocks = <&refclksys>;
+ reg = <0x02620350 4>, <0x02310110 4>;
+ reg-names = "control", "multiplier";
+ fixed-postdiv = <2>;
+ };
+
+ papllclk: papllclk@2620358 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,pll-clock";
+ clocks = <&refclksys>;
+ clock-output-names = "pa-pll-clk";
+ reg = <0x02620358 4>;
+ reg-names = "control";
+ };
+
+ ddr3apllclk: ddr3apllclk@2620360 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,pll-clock";
+ clocks = <&refclksys>;
+ clock-output-names = "ddr-3a-pll-clk";
+ reg = <0x02620360 4>;
+ reg-names = "control";
+ };
+
+ clkdfeiqnsys: clkdfeiqnsys {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk12>;
+ clock-output-names = "dfe";
+ reg-names = "control", "domain";
+ reg = <0x02350004 0xb00>, <0x02350000 0x400>;
+ domain-id = <0>;
+ };
+
+ clkpcie1: clkpcie1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk12>;
+ clock-output-names = "pcie";
+ reg = <0x0235002c 0xb00>, <0x02350000 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <4>;
+ };
+
+ clkgem1: clkgem1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk1>;
+ clock-output-names = "gem1";
+ reg = <0x02350040 0xb00>, <0x02350024 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <9>;
+ };
+
+ clkgem2: clkgem2 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk1>;
+ clock-output-names = "gem2";
+ reg = <0x02350044 0xb00>, <0x02350028 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <10>;
+ };
+
+ clkgem3: clkgem3 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk1>;
+ clock-output-names = "gem3";
+ reg = <0x02350048 0xb00>, <0x0235002c 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <11>;
+ };
+
+ clktac: clktac {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "tac";
+ reg = <0x02350064 0xb00>, <0x02350044 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <17>;
+ };
+
+ clkrac: clkrac {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "rac";
+ reg = <0x02350068 0xb00>, <0x02350044 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <17>;
+ };
+
+ clkdfepd0: clkdfepd0 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "dfe-pd0";
+ reg = <0x0235006c 0xb00>, <0x02350044 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <18>;
+ };
+
+ clkfftc0: clkfftc0 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "fftc-0";
+ reg = <0x02350070 0xb00>, <0x0235004c 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <19>;
+ };
+
+ clkosr: clkosr {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "osr";
+ reg = <0x02350088 0xb00>, <0x0235004c 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <21>;
+ };
+
+ clktcp3d0: clktcp3d0 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "tcp3d-0";
+ reg = <0x0235008c 0xb00>, <0x02350058 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <22>;
+ };
+
+ clktcp3d1: clktcp3d1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "tcp3d-1";
+ reg = <0x02350094 0xb00>, <0x02350058 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <23>;
+ };
+
+ clkvcp0: clkvcp0 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-0";
+ reg = <0x0235009c 0xb00>, <0x02350060 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <24>;
+ };
+
+ clkvcp1: clkvcp1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-1";
+ reg = <0x023500a0 0xb00>, <0x02350060 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <24>;
+ };
+
+ clkvcp2: clkvcp2 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-2";
+ reg = <0x023500a4 0xb00>, <0x02350060 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <24>;
+ };
+
+ clkvcp3: clkvcp3 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "vcp-3";
+ reg = <0x023500a8 0xb00>, <0x02350060 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <24>;
+ };
+
+ clkbcp: clkbcp {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "bcp";
+ reg = <0x023500bc 0xb00>, <0x02350068 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <26>;
+ };
+
+ clkdfepd1: clkdfepd1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "dfe-pd1";
+ reg = <0x023500c0 0xb00>, <0x02350044 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <27>;
+ };
+
+ clkfftc1: clkfftc1 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "fftc-1";
+ reg = <0x023500c4 0xb00>, <0x023504c0 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <28>;
+ };
+
+ clkiqnail: clkiqnail {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&chipclk13>;
+ clock-output-names = "iqn-ail";
+ reg = <0x023500c8 0xb00>, <0x0235004c 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <29>;
+ };
+
+ clkuart2: clkuart2 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&clkmodrst0>;
+ clock-output-names = "uart2";
+ reg = <0x02350000 0xb00>, <0x02350000 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <0>;
+ };
+
+ clkuart3: clkuart3 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&clkmodrst0>;
+ clock-output-names = "uart3";
+ reg = <0x02350000 0xb00>, <0x02350000 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <0>;
+ };
+};
diff --git a/arch/arm/boot/dts/k2l-evm.dts b/arch/arm/boot/dts/k2l-evm.dts
new file mode 100644
index 000000000000..50a70132ac9e
--- /dev/null
+++ b/arch/arm/boot/dts/k2l-evm.dts
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 Texas Instruments, Inc.
+ *
+ * Keystone 2 Lamarr EVM device tree
+ *
+ * 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.
+ */
+/dts-v1/;
+
+#include "keystone.dtsi"
+#include "k2l.dtsi"
+
+/ {
+ compatible = "ti,k2l-evm","ti,keystone";
+ model = "Texas Instruments Keystone 2 Lamarr EVM";
+
+ soc {
+ clocks {
+ refclksys: refclksys {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <122880000>;
+ clock-output-names = "refclk-sys";
+ };
+ };
+ };
+};
+
+&usb_phy {
+ status = "okay";
+};
+
+&usb {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/k2l.dtsi b/arch/arm/boot/dts/k2l.dtsi
new file mode 100644
index 000000000000..1f7f479589e1
--- /dev/null
+++ b/arch/arm/boot/dts/k2l.dtsi
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 Texas Instruments, Inc.
+ *
+ * Keystone 2 Lamarr SoC specific device tree
+ *
+ * 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.
+ */
+
+/ {
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupt-parent = <&gic>;
+
+ cpu@0 {
+ compatible = "arm,cortex-a15";
+ device_type = "cpu";
+ reg = <0>;
+ };
+
+ cpu@1 {
+ compatible = "arm,cortex-a15";
+ device_type = "cpu";
+ reg = <1>;
+ };
+ };
+
+ soc {
+
+ /include/ "k2l-clocks.dtsi"
+
+ uart2: serial@02348400 {
+ compatible = "ns16550a";
+ current-speed = <115200>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ reg = <0x02348400 0x100>;
+ clocks = <&clkuart2>;
+ interrupts = <GIC_SPI 432 IRQ_TYPE_EDGE_RISING>;
+ };
+
+ uart3: serial@02348800 {
+ compatible = "ns16550a";
+ current-speed = <115200>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ reg = <0x02348800 0x100>;
+ clocks = <&clkuart3>;
+ interrupts = <GIC_SPI 435 IRQ_TYPE_EDGE_RISING>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/keystone-clocks.dtsi b/arch/arm/boot/dts/keystone-clocks.dtsi
index ef58d1c24313..93f82c7010ab 100644
--- a/arch/arm/boot/dts/keystone-clocks.dtsi
+++ b/arch/arm/boot/dts/keystone-clocks.dtsi
@@ -13,51 +13,6 @@ clocks {
#size-cells = <1>;
ranges;
- mainpllclk: mainpllclk@2310110 {
- #clock-cells = <0>;
- compatible = "ti,keystone,main-pll-clock";
- clocks = <&refclksys>;
- reg = <0x02620350 4>, <0x02310110 4>;
- reg-names = "control", "multiplier";
- fixed-postdiv = <2>;
- };
-
- papllclk: papllclk@2620358 {
- #clock-cells = <0>;
- compatible = "ti,keystone,pll-clock";
- clocks = <&refclkpass>;
- clock-output-names = "pa-pll-clk";
- reg = <0x02620358 4>;
- reg-names = "control";
- };
-
- ddr3apllclk: ddr3apllclk@2620360 {
- #clock-cells = <0>;
- compatible = "ti,keystone,pll-clock";
- clocks = <&refclkddr3a>;
- clock-output-names = "ddr-3a-pll-clk";
- reg = <0x02620360 4>;
- reg-names = "control";
- };
-
- ddr3bpllclk: ddr3bpllclk@2620368 {
- #clock-cells = <0>;
- compatible = "ti,keystone,pll-clock";
- clocks = <&refclkddr3b>;
- clock-output-names = "ddr-3b-pll-clk";
- reg = <0x02620368 4>;
- reg-names = "control";
- };
-
- armpllclk: armpllclk@2620370 {
- #clock-cells = <0>;
- compatible = "ti,keystone,pll-clock";
- clocks = <&refclkarm>;
- clock-output-names = "arm-pll-clk";
- reg = <0x02620370 4>;
- reg-names = "control";
- };
-
mainmuxclk: mainmuxclk@2310108 {
#clock-cells = <0>;
compatible = "ti,keystone,pll-mux-clock";
@@ -244,7 +199,7 @@ clocks {
clock-output-names = "debugss-trc";
reg = <0x02350014 0xb00>, <0x02350000 0x400>;
reg-names = "control", "domain";
- domain-id = <0>;
+ domain-id = <1>;
};
clktetbtrc: clktetbtrc {
@@ -297,26 +252,6 @@ clocks {
domain-id = <3>;
};
- clksrio: clksrio {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk1rstiso13>;
- clock-output-names = "srio";
- reg = <0x0235002c 0xb00>, <0x02350010 0x400>;
- reg-names = "control", "domain";
- domain-id = <4>;
- };
-
- clkhyperlink0: clkhyperlink0 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk12>;
- clock-output-names = "hyperlink-0";
- reg = <0x02350030 0xb00>, <0x02350014 0x400>;
- reg-names = "control", "domain";
- domain-id = <5>;
- };
-
clksr: clksr {
#clock-cells = <0>;
compatible = "ti,keystone,psc-clock";
@@ -327,16 +262,6 @@ clocks {
domain-id = <6>;
};
- clkmsmcsram: clkmsmcsram {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk1>;
- clock-output-names = "msmcsram";
- reg = <0x02350038 0xb00>, <0x0235001c 0x400>;
- reg-names = "control", "domain";
- domain-id = <7>;
- };
-
clkgem0: clkgem0 {
#clock-cells = <0>;
compatible = "ti,keystone,psc-clock";
@@ -347,76 +272,6 @@ clocks {
domain-id = <8>;
};
- clkgem1: clkgem1 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk1>;
- clock-output-names = "gem1";
- reg = <0x02350040 0xb00>, <0x02350024 0x400>;
- reg-names = "control", "domain";
- domain-id = <9>;
- };
-
- clkgem2: clkgem2 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk1>;
- clock-output-names = "gem2";
- reg = <0x02350044 0xb00>, <0x02350028 0x400>;
- reg-names = "control", "domain";
- domain-id = <10>;
- };
-
- clkgem3: clkgem3 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk1>;
- clock-output-names = "gem3";
- reg = <0x02350048 0xb00>, <0x0235002c 0x400>;
- reg-names = "control", "domain";
- domain-id = <11>;
- };
-
- clkgem4: clkgem4 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk1>;
- clock-output-names = "gem4";
- reg = <0x0235004c 0xb00>, <0x02350030 0x400>;
- reg-names = "control", "domain";
- domain-id = <12>;
- };
-
- clkgem5: clkgem5 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk1>;
- clock-output-names = "gem5";
- reg = <0x02350050 0xb00>, <0x02350034 0x400>;
- reg-names = "control", "domain";
- domain-id = <13>;
- };
-
- clkgem6: clkgem6 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk1>;
- clock-output-names = "gem6";
- reg = <0x02350054 0xb00>, <0x02350038 0x400>;
- reg-names = "control", "domain";
- domain-id = <14>;
- };
-
- clkgem7: clkgem7 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk1>;
- clock-output-names = "gem7";
- reg = <0x02350058 0xb00>, <0x0235003c 0x400>;
- reg-names = "control", "domain";
- domain-id = <15>;
- };
-
clkddr30: clkddr30 {
#clock-cells = <0>;
compatible = "ti,keystone,psc-clock";
@@ -427,276 +282,6 @@ clocks {
domain-id = <16>;
};
- clkddr31: clkddr31 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "ddr3-1";
- reg = <0x02350060 0xb00>, <0x02350040 0x400>;
- reg-names = "control", "domain";
- domain-id = <16>;
- };
-
- clktac: clktac {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "tac";
- reg = <0x02350064 0xb00>, <0x02350044 0x400>;
- reg-names = "control", "domain";
- domain-id = <17>;
- };
-
- clkrac01: clktac01 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "rac-01";
- reg = <0x02350068 0xb00>, <0x02350044 0x400>;
- reg-names = "control", "domain";
- domain-id = <17>;
- };
-
- clkrac23: clktac23 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "rac-23";
- reg = <0x0235006c 0xb00>, <0x02350048 0x400>;
- reg-names = "control", "domain";
- domain-id = <18>;
- };
-
- clkfftc0: clkfftc0 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "fftc-0";
- reg = <0x02350070 0xb00>, <0x0235004c 0x400>;
- reg-names = "control", "domain";
- domain-id = <19>;
- };
-
- clkfftc1: clkfftc1 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "fftc-1";
- reg = <0x02350074 0xb00>, <0x023504c0 0x400>;
- reg-names = "control", "domain";
- domain-id = <19>;
- };
-
- clkfftc2: clkfftc2 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "fftc-2";
- reg = <0x02350078 0xb00>, <0x02350050 0x400>;
- reg-names = "control", "domain";
- domain-id = <20>;
- };
-
- clkfftc3: clkfftc3 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "fftc-3";
- reg = <0x0235007c 0xb00>, <0x02350050 0x400>;
- reg-names = "control", "domain";
- domain-id = <20>;
- };
-
- clkfftc4: clkfftc4 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "fftc-4";
- reg = <0x02350080 0xb00>, <0x02350050 0x400>;
- reg-names = "control", "domain";
- domain-id = <20>;
- };
-
- clkfftc5: clkfftc5 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "fftc-5";
- reg = <0x02350084 0xb00>, <0x02350050 0x400>;
- reg-names = "control", "domain";
- domain-id = <20>;
- };
-
- clkaif: clkaif {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "aif";
- reg = <0x02350088 0xb00>, <0x02350054 0x400>;
- reg-names = "control", "domain";
- domain-id = <21>;
- };
-
- clktcp3d0: clktcp3d0 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "tcp3d-0";
- reg = <0x0235008c 0xb00>, <0x02350058 0x400>;
- reg-names = "control", "domain";
- domain-id = <22>;
- };
-
- clktcp3d1: clktcp3d1 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "tcp3d-1";
- reg = <0x02350090 0xb00>, <0x02350058 0x400>;
- reg-names = "control", "domain";
- domain-id = <22>;
- };
-
- clktcp3d2: clktcp3d2 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "tcp3d-2";
- reg = <0x02350094 0xb00>, <0x0235005c 0x400>;
- reg-names = "control", "domain";
- domain-id = <23>;
- };
-
- clktcp3d3: clktcp3d3 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "tcp3d-3";
- reg = <0x02350098 0xb00>, <0x0235005c 0x400>;
- reg-names = "control", "domain";
- domain-id = <23>;
- };
-
- clkvcp0: clkvcp0 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "vcp-0";
- reg = <0x0235009c 0xb00>, <0x02350060 0x400>;
- reg-names = "control", "domain";
- domain-id = <24>;
- };
-
- clkvcp1: clkvcp1 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "vcp-1";
- reg = <0x023500a0 0xb00>, <0x02350060 0x400>;
- reg-names = "control", "domain";
- domain-id = <24>;
- };
-
- clkvcp2: clkvcp2 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "vcp-2";
- reg = <0x023500a4 0xb00>, <0x02350060 0x400>;
- reg-names = "control", "domain";
- domain-id = <24>;
- };
-
- clkvcp3: clkvcp3 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "vcp-3";
- reg = <0x023500a8 0xb00>, <0x02350060 0x400>;
- reg-names = "control", "domain";
- domain-id = <24>;
- };
-
- clkvcp4: clkvcp4 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "vcp-4";
- reg = <0x023500ac 0xb00>, <0x02350064 0x400>;
- reg-names = "control", "domain";
- domain-id = <25>;
- };
-
- clkvcp5: clkvcp5 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "vcp-5";
- reg = <0x023500b0 0xb00>, <0x02350064 0x400>;
- reg-names = "control", "domain";
- domain-id = <25>;
- };
-
- clkvcp6: clkvcp6 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "vcp-6";
- reg = <0x023500b4 0xb00>, <0x02350064 0x400>;
- reg-names = "control", "domain";
- domain-id = <25>;
- };
-
- clkvcp7: clkvcp7 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "vcp-7";
- reg = <0x023500b8 0xb00>, <0x02350064 0x400>;
- reg-names = "control", "domain";
- domain-id = <25>;
- };
-
- clkbcp: clkbcp {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "bcp";
- reg = <0x023500bc 0xb00>, <0x02350068 0x400>;
- reg-names = "control", "domain";
- domain-id = <26>;
- };
-
- clkdxb: clkdxb {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "dxb";
- reg = <0x023500c0 0xb00>, <0x0235006c 0x400>;
- reg-names = "control", "domain";
- domain-id = <27>;
- };
-
- clkhyperlink1: clkhyperlink1 {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk12>;
- clock-output-names = "hyperlink-1";
- reg = <0x023500c4 0xb00>, <0x02350070 0x400>;
- reg-names = "control", "domain";
- domain-id = <28>;
- };
-
- clkxge: clkxge {
- #clock-cells = <0>;
- compatible = "ti,keystone,psc-clock";
- clocks = <&chipclk13>;
- clock-output-names = "xge";
- reg = <0x023500c8 0xb00>, <0x02350074 0x400>;
- reg-names = "control", "domain";
- domain-id = <29>;
- };
-
clkwdtimer0: clkwdtimer0 {
#clock-cells = <0>;
compatible = "ti,keystone,psc-clock";
@@ -737,6 +322,16 @@ clocks {
domain-id = <0>;
};
+ clktimer15: clktimer15 {
+ #clock-cells = <0>;
+ compatible = "ti,keystone,psc-clock";
+ clocks = <&clkmodrst0>;
+ clock-output-names = "timer15";
+ reg = <0x02350000 0xb00>, <0x02350000 0x400>;
+ reg-names = "control", "domain";
+ domain-id = <0>;
+ };
+
clkuart0: clkuart0 {
#clock-cells = <0>;
compatible = "ti,keystone,psc-clock";
diff --git a/arch/arm/boot/dts/keystone.dtsi b/arch/arm/boot/dts/keystone.dtsi
index b4202907a27b..90823eb90c1b 100644
--- a/arch/arm/boot/dts/keystone.dtsi
+++ b/arch/arm/boot/dts/keystone.dtsi
@@ -7,6 +7,7 @@
*/
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/gpio/gpio.h>
#include "skeleton.dtsi"
@@ -24,37 +25,6 @@
reg = <0x00000000 0x80000000 0x00000000 0x40000000>;
};
- cpus {
- #address-cells = <1>;
- #size-cells = <0>;
-
- interrupt-parent = <&gic>;
-
- cpu@0 {
- compatible = "arm,cortex-a15";
- device_type = "cpu";
- reg = <0>;
- };
-
- cpu@1 {
- compatible = "arm,cortex-a15";
- device_type = "cpu";
- reg = <1>;
- };
-
- cpu@2 {
- compatible = "arm,cortex-a15";
- device_type = "cpu";
- reg = <2>;
- };
-
- cpu@3 {
- compatible = "arm,cortex-a15";
- device_type = "cpu";
- reg = <3>;
- };
- };
-
gic: interrupt-controller {
compatible = "arm,cortex-a15-gic";
#interrupt-cells = <3>;
@@ -208,5 +178,75 @@
usb-phy = <&usb_phy>, <&usb_phy>;
};
};
+
+ wdt: wdt@022f0080 {
+ compatible = "ti,keystone-wdt","ti,davinci-wdt";
+ reg = <0x022f0080 0x80>;
+ clocks = <&clkwdtimer0>;
+ };
+
+ clock_event: timer@22f0000 {
+ compatible = "ti,keystone-timer";
+ reg = <0x022f0000 0x80>;
+ interrupts = <GIC_SPI 110 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clktimer15>;
+ };
+
+ gpio0: gpio@260bf00 {
+ compatible = "ti,keystone-gpio";
+ reg = <0x0260bf00 0x100>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ /* HW Interrupts mapped to GPIO pins */
+ interrupts = <GIC_SPI 120 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 121 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 122 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 123 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 124 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 125 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 126 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 127 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 128 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 129 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 130 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 131 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 132 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 133 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 134 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 135 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 136 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 137 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 138 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 139 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 140 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 141 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 142 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 143 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 144 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 145 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 146 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 147 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 148 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 149 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 150 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 151 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkgpio>;
+ clock-names = "gpio";
+ ti,ngpio = <32>;
+ ti,davinci-gpio-unbanked = <32>;
+ };
+
+ aemif: aemif@21000A00 {
+ compatible = "ti,keystone-aemif", "ti,davinci-aemif";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ clocks = <&clkaemif>;
+ clock-names = "aemif";
+ clock-ranges;
+
+ reg = <0x21000A00 0x00000100>;
+ ranges = <0 0 0x30000000 0x10000000
+ 1 0 0x21000A00 0x00000100>;
+ };
};
};
diff --git a/arch/arm/boot/dts/kirkwood-b3.dts b/arch/arm/boot/dts/kirkwood-b3.dts
new file mode 100644
index 000000000000..40791053106b
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-b3.dts
@@ -0,0 +1,204 @@
+/*
+ * Device Tree file for Excito Bubba B3
+ *
+ * Copyright (C) 2013, 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.
+ *
+ * Note: This requires a new'ish version of u-boot, which disables the
+ * L2 cache. If your B3 silently fails to boot, u-boot is probably too
+ * old. Either upgrade, or consider the following email:
+ *
+ * http://lists.debian.org/debian-arm/2012/08/msg00128.html
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+
+/ {
+ model = "Excito B3";
+ compatible = "excito,b3", "marvell,kirkwood-88f6281", "marvell,kirkwood";
+ memory { /* 512 MB */
+ device_type = "memory";
+ reg = <0x00000000 0x20000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8 earlyprintk";
+ };
+
+ mbus {
+ pcie-controller {
+ status = "okay";
+
+ /* Wifi model has Atheros chipset on pcie port */
+ pcie@1,0 {
+ status = "okay";
+ };
+ };
+ };
+
+ ocp@f1000000 {
+ pinctrl: pinctrl@10000 {
+ pmx_button_power: pmx-button-power {
+ marvell,pins = "mpp39";
+ marvell,function = "gpio";
+ };
+ pmx_led_green: pmx-led-green {
+ marvell,pins = "mpp38";
+ marvell,function = "gpio";
+ };
+ pmx_led_red: pmx-led-red {
+ marvell,pins = "mpp41";
+ marvell,function = "gpio";
+ };
+ pmx_led_blue: pmx-led-blue {
+ marvell,pins = "mpp42";
+ marvell,function = "gpio";
+ };
+ pmx_beeper: pmx-beeper {
+ marvell,pins = "mpp40";
+ marvell,function = "gpio";
+ };
+ };
+
+ spi@10600 {
+ status = "okay";
+ pinctrl-0 = <&pmx_spi>;
+ pinctrl-names = "default";
+
+ m25p16@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "m25p16";
+ reg = <0>;
+ spi-max-frequency = <40000000>;
+ mode = <0>;
+
+ partition@0 {
+ reg = <0x0 0xc0000>;
+ label = "u-boot";
+ };
+
+ partition@c0000 {
+ reg = <0xc0000 0x20000>;
+ label = "u-boot env";
+ };
+
+ partition@e0000 {
+ reg = <0xe0000 0x120000>;
+ label = "data";
+ };
+ };
+ };
+
+ i2c@11000 {
+ status = "okay";
+ /*
+ * There is something on the bus at address 0x64.
+ * Not yet identified what it is, maybe the eeprom
+ * for the Atheros WiFi chip?
+ */
+ };
+
+
+ serial@12000 {
+ /* Internal on test pins, 3.3v TTL
+ * UART0_RX = Testpoint 65
+ * UART0_TX = Testpoint 66
+ * See the Excito Wiki for more details.
+ */
+ pinctrl-0 = <&pmx_uart0>;
+ pinctrl-names = "default";
+ status = "okay";
+ };
+
+ sata@80000 {
+ /* One internal, the second as eSATA */
+ status = "okay";
+ nr-ports = <2>;
+ };
+ };
+
+ gpio-leds {
+ /*
+ * There is one LED "port" on the front and the colours
+ * mix together giving some interesting combinations.
+ */
+ compatible = "gpio-leds";
+ pinctrl-0 = < &pmx_led_green &pmx_led_red
+ &pmx_led_blue >;
+ pinctrl-names = "default";
+
+ programming_led {
+ label = "bubba3:green:programming";
+ gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+
+ error_led {
+ label = "bubba3:red:error";
+ gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
+ };
+
+ active_led {
+ label = "bubba3:blue:active";
+ gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+ pinctrl-0 = <&pmx_button_power>;
+ pinctrl-names = "default";
+
+ power-button {
+ /* On the back */
+ label = "Power Button";
+ linux,code = <KEY_POWER>;
+ gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ beeper: beeper {
+ /* 4KHz Piezoelectric buzzer */
+ compatible = "gpio-beeper";
+ pinctrl-0 = <&pmx_beeper>;
+ pinctrl-names = "default";
+ gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
+ };
+};
+
+&mdio {
+ status = "okay";
+
+ ethphy0: ethernet-phy@8 {
+ device_type = "ethernet-phy";
+ reg = <8>;
+ };
+
+ ethphy1: ethernet-phy@24 {
+ device_type = "ethernet-phy";
+ reg = <24>;
+ };
+};
+
+&eth0 {
+ status = "okay";
+ ethernet0-port@0 {
+ phy-handle = <&ethphy0>;
+ };
+};
+
+&eth1 {
+ status = "okay";
+ ethernet1-port@0 {
+ phy-handle = <&ethphy1>;
+ };
+};
+
diff --git a/arch/arm/boot/dts/kirkwood-ds109.dts b/arch/arm/boot/dts/kirkwood-ds109.dts
new file mode 100644
index 000000000000..772092c94ca3
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds109.dts
@@ -0,0 +1,41 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS109, DS110, DS110jv20";
+ compatible = "synology,ds109", "synology,ds110jv20",
+ "synology,ds110", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-150-32-35 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-21-1 {
+ status = "okay";
+ };
+};
+
+&rs5c372 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds110jv10.dts b/arch/arm/boot/dts/kirkwood-ds110jv10.dts
new file mode 100644
index 000000000000..aabafbe0da4c
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds110jv10.dts
@@ -0,0 +1,41 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS110j v10 and v30";
+ compatible = "synology,ds110jv10", "synology,ds110jv30",
+ "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-150-32-35 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-21-1 {
+ status = "okay";
+ };
+};
+
+&s35390a {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds111.dts b/arch/arm/boot/dts/kirkwood-ds111.dts
new file mode 100644
index 000000000000..16ec7fbab573
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds111.dts
@@ -0,0 +1,44 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6282.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS111";
+ compatible = "synology,ds111", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-100-15-35-1 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-21-1 {
+ status = "okay";
+ };
+};
+
+&s35390a {
+ status = "okay";
+};
+
+&pcie2 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds112.dts b/arch/arm/boot/dts/kirkwood-ds112.dts
new file mode 100644
index 000000000000..cff1b2388765
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds112.dts
@@ -0,0 +1,48 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6282.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS111";
+ compatible = "synology,ds111", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-100-15-35-1 {
+ status = "okay";
+ };
+
+ gpio-leds-21-2 {
+ status = "okay";
+ };
+
+ regulators-hdd-30 {
+ status = "okay";
+ };
+};
+
+&s35390a {
+ status = "okay";
+};
+
+&pcie2 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds209.dts b/arch/arm/boot/dts/kirkwood-ds209.dts
new file mode 100644
index 000000000000..330411993d38
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds209.dts
@@ -0,0 +1,44 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS209";
+ compatible = "synology,ds209", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-150-32-35 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-21-2 {
+ status = "okay";
+ };
+
+ regulators-hdd-31 {
+ status = "okay";
+ };
+};
+
+&rs5c372 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds210.dts b/arch/arm/boot/dts/kirkwood-ds210.dts
new file mode 100644
index 000000000000..6052eaa37d4f
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds210.dts
@@ -0,0 +1,46 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS210 v10, v20, v30, DS211j";
+ compatible = "synology,ds210jv10", "synology,ds210jv20",
+ "synology,ds210jv30", "synology,ds211j",
+ "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-150-32-35 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-21-2 {
+ status = "okay";
+ };
+
+ regulators-hdd-31 {
+ status = "okay";
+ };
+};
+
+&s35390a {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds212.dts b/arch/arm/boot/dts/kirkwood-ds212.dts
new file mode 100644
index 000000000000..7f76cd30e84e
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds212.dts
@@ -0,0 +1,47 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6282.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS212, DS212p v10, v20, DS213air v10, DS213 v10";
+ compatible = "synology,ds212", "synology,ds212pv10",
+ "synology,ds212pv10", "synology,ds212pv20",
+ "synology,ds213airv10", "synology,ds213v10",
+ "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-100-15-35-1 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-21-2 {
+ status = "okay";
+ };
+};
+
+&s35390a {
+ status = "okay";
+};
+
+&pcie2 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds212j.dts b/arch/arm/boot/dts/kirkwood-ds212j.dts
new file mode 100644
index 000000000000..1f83a00f1f74
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds212j.dts
@@ -0,0 +1,41 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS212j v10, v20";
+ compatible = "synology,ds212jv10", "synology,ds212jv20",
+ "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-100-32-35 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-21-2 {
+ status = "okay";
+ };
+};
+
+&s35390a {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds409.dts b/arch/arm/boot/dts/kirkwood-ds409.dts
new file mode 100644
index 000000000000..0a573add44a2
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds409.dts
@@ -0,0 +1,48 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS409, DS410j";
+ compatible = "synology,ds409", "synology,ds410j", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-150-15-18 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-36 {
+ status = "okay";
+ };
+
+ gpio-leds-alarm-12 {
+ status = "okay";
+ };
+};
+
+&eth1 {
+ status = "okay";
+};
+
+&rs5c372 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds409slim.dts b/arch/arm/boot/dts/kirkwood-ds409slim.dts
new file mode 100644
index 000000000000..1848a6245fd3
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds409slim.dts
@@ -0,0 +1,40 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology 409slim";
+ compatible = "synology,ds409slim", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-150-32-35 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-20 {
+ status = "okay";
+ };
+};
+
+&rs5c372 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds411.dts b/arch/arm/boot/dts/kirkwood-ds411.dts
new file mode 100644
index 000000000000..a1737b4311c6
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds411.dts
@@ -0,0 +1,52 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6282.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS411, DS413jv10";
+ compatible = "synology,ds411", "synology,ds413jv10", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-100-15-35-1 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-36 {
+ status = "okay";
+ };
+
+ regulators-hdd-34 {
+ status = "okay";
+ };
+};
+
+&eth1 {
+ status = "okay";
+};
+
+&s35390a {
+ status = "okay";
+};
+
+&pcie2 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds411j.dts b/arch/arm/boot/dts/kirkwood-ds411j.dts
new file mode 100644
index 000000000000..0cde914eceae
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds411j.dts
@@ -0,0 +1,48 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS411j";
+ compatible = "synology,ds411j", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-150-15-18 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-36 {
+ status = "okay";
+ };
+
+ gpio-leds-alarm-12 {
+ status = "okay";
+ };
+};
+
+&eth1 {
+ status = "okay";
+};
+
+&s35390a {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-ds411slim.dts b/arch/arm/boot/dts/kirkwood-ds411slim.dts
new file mode 100644
index 000000000000..aef0cadc2c78
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ds411slim.dts
@@ -0,0 +1,48 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6282.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology DS411slim";
+ compatible = "synology,ds411slim", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-100-15-35-1 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-36 {
+ status = "okay";
+ };
+};
+
+&eth1 {
+ status = "okay";
+};
+
+&s35390a {
+ status = "okay";
+};
+
+&pcie2 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts b/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts
index dc86429756d7..2cb0dc529165 100644
--- a/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts
+++ b/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts
@@ -122,4 +122,66 @@
gpios = <&gpio1 14 GPIO_ACTIVE_LOW>;
};
};
+
+ dsa@0 {
+ compatible = "marvell,dsa";
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ dsa,ethernet = <&eth0>;
+ dsa,mii-bus = <&ethphy0>;
+
+ switch@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0 0>; /* MDIO address 0, switch 0 in tree */
+
+ port@0 {
+ reg = <0>;
+ label = "lan1";
+ };
+
+ port@1 {
+ reg = <1>;
+ label = "lan2";
+ };
+
+ port@2 {
+ reg = <2>;
+ label = "lan3";
+ };
+
+ port@3 {
+ reg = <3>;
+ label = "lan4";
+ };
+
+ port@4 {
+ reg = <4>;
+ label = "wan";
+ };
+
+ port@5 {
+ reg = <5>;
+ label = "cpu";
+ };
+ };
+ };
+};
+
+&mdio {
+ status = "okay";
+
+ ethphy0: ethernet-phy@ff {
+ reg = <0xff>; /* No phy attached */
+ speed = <1000>;
+ duplex = <1>;
+ };
+};
+
+&eth0 {
+ status = "okay";
+ ethernet0-port@0 {
+ phy-handle = <&ethphy0>;
+ };
};
diff --git a/arch/arm/boot/dts/kirkwood-rd88f6192.dts b/arch/arm/boot/dts/kirkwood-rd88f6192.dts
new file mode 100644
index 000000000000..e9dd85049297
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-rd88f6192.dts
@@ -0,0 +1,112 @@
+/*
+ * Marvell RD88F6192 Board descrition
+ *
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * This file contains the definitions that are common between the three
+ * variants of the Marvell Kirkwood Development Board.
+ */
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6192.dtsi"
+
+/ {
+ model = "Marvell RD88F6192 reference design";
+ compatible = "marvell,rd88f6192", "marvell,kirkwood-88f6192", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x20000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ mbus {
+ pcie-controller {
+ status = "okay";
+
+ pcie@1,0 {
+ status = "okay";
+ };
+ };
+ };
+
+ ocp@f1000000 {
+ pinctrl: pinctrl@10000 {
+ pinctrl-0 = <&pmx_usb_power>;
+ pinctrl-names = "default";
+
+ pmx_usb_power: pmx-usb-power {
+ marvell,pins = "mpp10";
+ marvell,function = "gpo";
+ };
+ };
+
+ serial@12000 {
+ status = "okay";
+
+ };
+
+ spi@10600 {
+ status = "okay";
+ pinctrl-0 = <&pmx_spi>;
+ pinctrl-names = "default";
+
+ m25p128@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,m25p128";
+ reg = <0>;
+ spi-max-frequency = <20000000>;
+ mode = <0>;
+ };
+ };
+
+ sata@80000 {
+ status = "okay";
+ nr-ports = <2>;
+ };
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-0 = <&pmx_usb_power>;
+ pinctrl-names = "default";
+
+ usb_power: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "USB VBUS";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ gpio = <&gpio0 10 GPIO_ACTIVE_HIGH>;
+ };
+ };
+};
+
+&mdio {
+ status = "okay";
+
+ ethphy0: ethernet-phy@8 {
+ reg = <8>;
+ };
+};
+
+&eth0 {
+ status = "okay";
+ ethernet0-port@0 {
+ phy-handle = <&ethphy0>;
+ };
+}; \ No newline at end of file
diff --git a/arch/arm/boot/dts/kirkwood-rd88f6281-a0.dts b/arch/arm/boot/dts/kirkwood-rd88f6281-a0.dts
new file mode 100644
index 000000000000..a803bbb70bc8
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-rd88f6281-a0.dts
@@ -0,0 +1,26 @@
+/*
+ * Marvell RD88F6181 A0 Board descrition
+ *
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * This file contains the definitions for the board with the A0 variant of
+ * the SoC. The ethernet switch does not have a "wan" port.
+ */
+
+/dts-v1/;
+#include "kirkwood-rd88f6281.dtsi"
+
+/ {
+ model = "Marvell RD88f6281 Reference design, with A0 SoC";
+ compatible = "marvell,rd88f6281-a0", "marvell,rd88f6281","marvell,kirkwood-88f6281", "marvell,kirkwood";
+
+ dsa@0 {
+ switch@0 {
+ reg = <10 0>; /* MDIO address 10, switch 0 in tree */
+ };
+ };
+}; \ No newline at end of file
diff --git a/arch/arm/boot/dts/kirkwood-rd88f6281-a1.dts b/arch/arm/boot/dts/kirkwood-rd88f6281-a1.dts
new file mode 100644
index 000000000000..baeebbf1d8c7
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-rd88f6281-a1.dts
@@ -0,0 +1,31 @@
+/*
+ * Marvell RD88F6181 A1 Board descrition
+ *
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * This file contains the definitions for the board with the A1 variant of
+ * the SoC. The ethernet switch has a "wan" port.
+ */
+
+/dts-v1/;
+
+#include "kirkwood-rd88f6281.dtsi"
+
+/ {
+ model = "Marvell RD88f6281 Reference design, with A1 SoC";
+ compatible = "marvell,rd88f6281-a1", "marvell,rd88f6281","marvell,kirkwood-88f6281", "marvell,kirkwood";
+
+ dsa@0 {
+ switch@0 {
+ reg = <0 0>; /* MDIO address 0, switch 0 in tree */
+ port@4 {
+ reg = <4>;
+ label = "wan";
+ };
+ };
+ };
+}; \ No newline at end of file
diff --git a/arch/arm/boot/dts/kirkwood-rd88f6281.dtsi b/arch/arm/boot/dts/kirkwood-rd88f6281.dtsi
new file mode 100644
index 000000000000..d6368c39102e
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-rd88f6281.dtsi
@@ -0,0 +1,152 @@
+/*
+ * Marvell RD88F6181 Common Board descrition
+ *
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * This file contains the definitions that are common between the two
+ * variants of the Marvell Kirkwood Development Board.
+ */
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+
+/ {
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x20000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ mbus {
+ pcie-controller {
+ status = "okay";
+
+ pcie@1,0 {
+ status = "okay";
+ };
+ };
+ };
+
+ ocp@f1000000 {
+ pinctrl: pinctrl@10000 {
+ pinctrl-0 = <&pmx_sdio_cd>;
+ pinctrl-names = "default";
+
+ pmx_sdio_cd: pmx-sdio-cd {
+ marvell,pins = "mpp28";
+ marvell,function = "gpio";
+ };
+ };
+
+ serial@12000 {
+ status = "okay";
+
+ };
+
+ sata@80000 {
+ status = "okay";
+ nr-ports = <2>;
+ };
+ mvsdio@90000 {
+ pinctrl-0 = <&pmx_sdio &pmx_sdio_cd>;
+ pinctrl-names = "default";
+ status = "okay";
+ cd-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>;
+ /* No WP GPIO */
+ };
+ };
+
+ dsa@0 {
+ compatible = "marvell,dsa";
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ dsa,ethernet = <&eth0>;
+ dsa,mii-bus = <&ethphy1>;
+
+ switch@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ label = "lan1";
+ };
+
+ port@1 {
+ reg = <1>;
+ label = "lan2";
+ };
+
+ port@2 {
+ reg = <2>;
+ label = "lan3";
+ };
+
+ port@3 {
+ reg = <3>;
+ label = "lan4";
+ };
+
+ port@5 {
+ reg = <5>;
+ label = "cpu";
+ };
+ };
+ };
+};
+
+&nand {
+ status = "okay";
+
+ partition@0 {
+ label = "u-boot";
+ reg = <0x0000000 0x100000>;
+ read-only;
+ };
+
+ partition@100000 {
+ label = "uImage";
+ reg = <0x0100000 0x200000>;
+ };
+
+ partition@300000 {
+ label = "data";
+ reg = <0x0300000 0x500000>;
+ };
+};
+
+&mdio {
+ status = "okay";
+
+ ethphy0: ethernet-phy@0 {
+ reg = <0>;
+ };
+
+ ethphy1: ethernet-phy@ff {
+ reg = <0xff>; /* No PHY attached */
+ speed = <1000>;
+ duple = <1>;
+ };
+};
+
+&eth0 {
+ status = "okay";
+ ethernet0-port@0 {
+ phy-handle = <&ethphy0>;
+ };
+};
+
+&eth1 {
+ status = "okay";
+ ethernet1-port@0 {
+ phy-handle = <&ethphy1>;
+ };
+};
diff --git a/arch/arm/boot/dts/kirkwood-rs212.dts b/arch/arm/boot/dts/kirkwood-rs212.dts
new file mode 100644
index 000000000000..93ec3d00c6ab
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-rs212.dts
@@ -0,0 +1,48 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6282.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology RS212";
+ compatible = "synology,rs212", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-100-15-35-3 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-38 {
+ status = "okay";
+ };
+
+ regulators-hdd-30-2 {
+ status = "okay";
+ };
+};
+
+&s35390a {
+ status = "okay";
+};
+
+&pcie2 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-rs409.dts b/arch/arm/boot/dts/kirkwood-rs409.dts
new file mode 100644
index 000000000000..311df4e5aa28
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-rs409.dts
@@ -0,0 +1,44 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology RS409";
+ compatible = "synology,rs409", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-150-15-18 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-36 {
+ status = "okay";
+ };
+};
+
+&eth1 {
+ status = "okay";
+};
+
+&rs5c372 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-rs411.dts b/arch/arm/boot/dts/kirkwood-rs411.dts
new file mode 100644
index 000000000000..f90da850bb31
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-rs411.dts
@@ -0,0 +1,44 @@
+/*
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6282.dtsi"
+#include "kirkwood-synology.dtsi"
+
+/ {
+ model = "Synology RS411 RS812";
+ compatible = "synology,rs411", "synology,rs812", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ gpio-fan-100-15-35-3 {
+ status = "okay";
+ };
+
+ gpio-leds-hdd-36 {
+ status = "okay";
+ };
+};
+
+&eth1 {
+ status = "okay";
+};
+
+&s35390a {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/kirkwood-synology.dtsi b/arch/arm/boot/dts/kirkwood-synology.dtsi
new file mode 100644
index 000000000000..4227c974729d
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-synology.dtsi
@@ -0,0 +1,871 @@
+/*
+ * Nodes for Marvell 628x Synology devices
+ *
+ * Andrew Lunn <andrew@lunn.ch>
+ * Ben Peddell <klightspeed@killerwolves.net>
+ *
+ * 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.
+ */
+
+/ {
+ mbus {
+ pcie-controller {
+ status = "okay";
+
+ pcie@1,0 {
+ status = "okay";
+ };
+
+ pcie2: pcie@2,0 {
+ status = "disabled";
+ };
+ };
+ };
+
+ ocp@f1000000 {
+ pinctrl: pinctrl@10000 {
+ pmx_alarmled_12: pmx-alarmled-12 {
+ marvell,pins = "mpp12";
+ marvell,function = "gpio";
+ };
+
+ pmx_fanctrl_15: pmx-fanctrl-15 {
+ marvell,pins = "mpp15";
+ marvell,function = "gpio";
+ };
+
+ pmx_fanctrl_16: pmx-fanctrl-16 {
+ marvell,pins = "mpp16";
+ marvell,function = "gpio";
+ };
+
+ pmx_fanctrl_17: pmx-fanctrl-17 {
+ marvell,pins = "mpp17";
+ marvell,function = "gpio";
+ };
+
+ pmx_fanalarm_18: pmx-fanalarm-18 {
+ marvell,pins = "mpp18";
+ marvell,function = "gpo";
+ };
+
+ pmx_hddled_20: pmx-hddled-20 {
+ marvell,pins = "mpp20";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_21: pmx-hddled-21 {
+ marvell,pins = "mpp21";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_22: pmx-hddled-22 {
+ marvell,pins = "mpp22";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_23: pmx-hddled-23 {
+ marvell,pins = "mpp23";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_24: pmx-hddled-24 {
+ marvell,pins = "mpp24";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_25: pmx-hddled-25 {
+ marvell,pins = "mpp25";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_26: pmx-hddled-26 {
+ marvell,pins = "mpp26";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_27: pmx-hddled-27 {
+ marvell,pins = "mpp27";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_28: pmx-hddled-28 {
+ marvell,pins = "mpp28";
+ marvell,function = "gpio";
+ };
+
+ pmx_hdd1_pwr_29: pmx-hdd1-pwr-29 {
+ marvell,pins = "mpp29";
+ marvell,function = "gpio";
+ };
+
+ pmx_hdd1_pwr_30: pmx-hdd-pwr-30 {
+ marvell,pins = "mpp30";
+ marvell,function = "gpio";
+ };
+
+ pmx_hdd2_pwr_31: pmx-hdd2-pwr-31 {
+ marvell,pins = "mpp31";
+ marvell,function = "gpio";
+ };
+
+ pmx_fanctrl_32: pmx-fanctrl-32 {
+ marvell,pins = "mpp32";
+ marvell,function = "gpio";
+ };
+
+ pmx_fanctrl_33: pmx-fanctrl-33 {
+ marvell,pins = "mpp33";
+ marvell,function = "gpo";
+ };
+
+ pmx_fanctrl_34: pmx-fanctrl-34 {
+ marvell,pins = "mpp34";
+ marvell,function = "gpio";
+ };
+
+ pmx_hdd2_pwr_34: pmx-hdd2-pwr-34 {
+ marvell,pins = "mpp34";
+ marvell,function = "gpio";
+ };
+
+ pmx_fanalarm_35: pmx-fanalarm-35 {
+ marvell,pins = "mpp35";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_36: pmx-hddled-36 {
+ marvell,pins = "mpp36";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_37: pmx-hddled-37 {
+ marvell,pins = "mpp37";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_38: pmx-hddled-38 {
+ marvell,pins = "mpp38";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_39: pmx-hddled-39 {
+ marvell,pins = "mpp39";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_40: pmx-hddled-40 {
+ marvell,pins = "mpp40";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_41: pmx-hddled-41 {
+ marvell,pins = "mpp41";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_42: pmx-hddled-42 {
+ marvell,pins = "mpp42";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_43: pmx-hddled-43 {
+ marvell,pins = "mpp43";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_44: pmx-hddled-44 {
+ marvell,pins = "mpp44";
+ marvell,function = "gpio";
+ };
+
+ pmx_hddled_45: pmx-hddled-45 {
+ marvell,pins = "mpp45";
+ marvell,function = "gpio";
+ };
+
+ pmx_hdd3_pwr_44: pmx-hdd3-pwr-44 {
+ marvell,pins = "mpp44";
+ marvell,function = "gpio";
+ };
+
+ pmx_hdd4_pwr_45: pmx-hdd4-pwr-45 {
+ marvell,pins = "mpp45";
+ marvell,function = "gpio";
+ };
+
+ pmx_fanalarm_44: pmx-fanalarm-44 {
+ marvell,pins = "mpp44";
+ marvell,function = "gpio";
+ };
+
+ pmx_fanalarm_45: pmx-fanalarm-45 {
+ marvell,pins = "mpp45";
+ marvell,function = "gpio";
+ };
+ };
+
+ rtc@10300 {
+ status = "disabled";
+ };
+
+ spi@10600 {
+ status = "okay";
+ pinctrl-0 = <&pmx_spi>;
+ pinctrl-names = "default";
+
+ m25p80@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,m25p80";
+ reg = <0>;
+ spi-max-frequency = <20000000>;
+ mode = <0>;
+
+ partition@00000000 {
+ reg = <0x00000000 0x00080000>;
+ label = "RedBoot";
+ };
+
+ partition@00080000 {
+ reg = <0x00080000 0x00200000>;
+ label = "zImage";
+ };
+
+ partition@00280000 {
+ reg = <0x00280000 0x00140000>;
+ label = "rd.gz";
+ };
+
+ partition@003c0000 {
+ reg = <0x003c0000 0x00010000>;
+ label = "vendor";
+ };
+
+ partition@003d0000 {
+ reg = <0x003d0000 0x00020000>;
+ label = "RedBoot config";
+ };
+
+ partition@003f0000 {
+ reg = <0x003f0000 0x00010000>;
+ label = "FIS directory";
+ };
+ };
+ };
+
+ i2c@11000 {
+ status = "okay";
+ clock-frequency = <400000>;
+ pinctrl-0 = <&pmx_twsi0>;
+ pinctrl-names = "default";
+
+ rs5c372: rs5c372@32 {
+ status = "disabled";
+ compatible = "ricoh,rs5c372";
+ reg = <0x32>;
+ };
+
+ s35390a: s35390a@30 {
+ status = "disabled";
+ compatible = "ssi,s35390a";
+ reg = <0x30>;
+ };
+ };
+
+ serial@12000 {
+ status = "okay";
+ pinctrl-0 = <&pmx_uart0>;
+ pinctrl-names = "default";
+ };
+
+ serial@12100 {
+ status = "okay";
+ pinctrl-0 = <&pmx_uart1>;
+ pinctrl-names = "default";
+ };
+
+ poweroff@12100 {
+ compatible = "synology,power-off";
+ reg = <0x12100 0x100>;
+ clocks = <&gate_clk 7>;
+ };
+
+ sata@80000 {
+ pinctrl-0 = <&pmx_sata0 &pmx_sata1>;
+ pinctrl-names = "default";
+ status = "okay";
+ nr-ports = <2>;
+ };
+ };
+
+ gpio-fan-150-32-35 {
+ status = "disabled";
+ compatible = "gpio-fan";
+ pinctrl-0 = <&pmx_fanctrl_32 &pmx_fanctrl_33 &pmx_fanctrl_34
+ &pmx_fanalarm_35>;
+ pinctrl-names = "default";
+ gpios = <&gpio1 0 GPIO_ACTIVE_HIGH
+ &gpio1 1 GPIO_ACTIVE_HIGH
+ &gpio1 2 GPIO_ACTIVE_HIGH>;
+ gpio-fan,speed-map = < 0 0
+ 2200 1
+ 2500 2
+ 3000 4
+ 3300 3
+ 3700 5
+ 3800 6
+ 4200 7 >;
+ };
+
+ gpio-fan-150-15-18 {
+ status = "disabled";
+ compatible = "gpio-fan";
+ pinctrl-0 = <&pmx_fanctrl_15 &pmx_fanctrl_16 &pmx_fanctrl_17
+ &pmx_fanalarm_18>;
+ pinctrl-names = "default";
+ gpios = <&gpio0 15 GPIO_ACTIVE_HIGH
+ &gpio0 16 GPIO_ACTIVE_HIGH
+ &gpio0 17 GPIO_ACTIVE_HIGH>;
+ alarm-gpios = <&gpio0 18 GPIO_ACTIVE_HIGH>;
+ gpio-fan,speed-map = < 0 0
+ 2200 1
+ 2500 2
+ 3000 4
+ 3300 3
+ 3700 5
+ 3800 6
+ 4200 7 >;
+ };
+
+ gpio-fan-100-32-35 {
+ status = "disabled";
+ compatible = "gpio-fan";
+ pinctrl-0 = <&pmx_fanctrl_32 &pmx_fanctrl_33 &pmx_fanctrl_34
+ &pmx_fanalarm_35>;
+ pinctrl-names = "default";
+ gpios = <&gpio1 0 GPIO_ACTIVE_HIGH
+ &gpio1 1 GPIO_ACTIVE_HIGH
+ &gpio1 2 GPIO_ACTIVE_HIGH>;
+ alarm-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;
+ gpio-fan,speed-map = < 0 0
+ 2500 1
+ 3100 2
+ 3800 3
+ 4600 4
+ 4800 5
+ 4900 6
+ 5000 7 >;
+ };
+
+ gpio-fan-100-15-18 {
+ status = "disabled";
+ compatible = "gpio-fan";
+ pinctrl-0 = <&pmx_fanctrl_15 &pmx_fanctrl_16 &pmx_fanctrl_17
+ &pmx_fanalarm_18>;
+ pinctrl-names = "default";
+ gpios = <&gpio0 15 GPIO_ACTIVE_HIGH
+ &gpio0 16 GPIO_ACTIVE_HIGH
+ &gpio0 17 GPIO_ACTIVE_HIGH>;
+ alarm-gpios = <&gpio0 18 GPIO_ACTIVE_HIGH>;
+ gpio-fan,speed-map = < 0 0
+ 2500 1
+ 3100 2
+ 3800 3
+ 4600 4
+ 4800 5
+ 4900 6
+ 5000 7 >;
+ };
+
+ gpio-fan-100-15-35-1 {
+ status = "disabled";
+ compatible = "gpio-fan";
+ pinctrl-0 = <&pmx_fanctrl_15 &pmx_fanctrl_16 &pmx_fanctrl_17
+ &pmx_fanalarm_35>;
+ pinctrl-names = "default";
+ gpios = <&gpio0 15 GPIO_ACTIVE_HIGH
+ &gpio0 16 GPIO_ACTIVE_HIGH
+ &gpio0 17 GPIO_ACTIVE_HIGH>;
+ alarm-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;
+ gpio-fan,speed-map = < 0 0
+ 2500 1
+ 3100 2
+ 3800 3
+ 4600 4
+ 4800 5
+ 4900 6
+ 5000 7 >;
+ };
+
+ gpio-fan-100-15-35-3 {
+ status = "disabled";
+ compatible = "gpio-fan";
+ pinctrl-0 = <&pmx_fanctrl_15 &pmx_fanctrl_16 &pmx_fanctrl_17
+ &pmx_fanalarm_35 &pmx_fanalarm_44 &pmx_fanalarm_45>;
+ pinctrl-names = "default";
+ gpios = <&gpio0 15 GPIO_ACTIVE_HIGH
+ &gpio0 16 GPIO_ACTIVE_HIGH
+ &gpio0 17 GPIO_ACTIVE_HIGH>;
+ alarm-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH
+ &gpio1 12 GPIO_ACTIVE_HIGH
+ &gpio1 13 GPIO_ACTIVE_HIGH>;
+ gpio-fan,speed-map = < 0 0
+ 2500 1
+ 3100 2
+ 3800 3
+ 4600 4
+ 4800 5
+ 4900 6
+ 5000 7 >;
+ };
+
+ gpio-leds-alarm-12 {
+ status = "disabled";
+ compatible = "gpio-leds";
+ pinctrl-0 = <&pmx_alarmled_12>;
+ pinctrl-names = "default";
+
+ hdd1-green {
+ label = "synology:alarm";
+ gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ gpio-leds-hdd-20 {
+ status = "disabled";
+ compatible = "gpio-leds";
+ pinctrl-0 = <&pmx_hddled_20 &pmx_hddled_21 &pmx_hddled_22
+ &pmx_hddled_23 &pmx_hddled_24 &pmx_hddled_25
+ &pmx_hddled_26 &pmx_hddled_27>;
+ pinctrl-names = "default";
+
+ hdd1-green {
+ label = "synology:green:hdd1";
+ gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd1-amber {
+ label = "synology:amber:hdd1";
+ gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd2-green {
+ label = "synology:green:hdd2";
+ gpios = <&gpio0 22 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd2-amber {
+ label = "synology:amber:hdd2";
+ gpios = <&gpio0 23 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd3-green {
+ label = "synology:green:hdd3";
+ gpios = <&gpio0 24 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd3-amber {
+ label = "synology:amber:hdd3";
+ gpios = <&gpio0 25 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd4-green {
+ label = "synology:green:hdd4";
+ gpios = <&gpio0 26 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd4-amber {
+ label = "synology:amber:hdd4";
+ gpios = <&gpio0 27 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ gpio-leds-hdd-21-1 {
+ status = "disabled";
+ compatible = "gpio-leds";
+ pinctrl-0 = <&pmx_hddled_21 &pmx_hddled_23>;
+ pinctrl-names = "default";
+
+ hdd1-green {
+ label = "synology:green:hdd1";
+ gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd1-amber {
+ label = "synology:amber:hdd1";
+ gpios = <&gpio0 23 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ gpio-leds-hdd-21-2 {
+ status = "disabled";
+ compatible = "gpio-leds";
+ pinctrl-0 = <&pmx_hddled_21 &pmx_hddled_23 &pmx_hddled_20 &pmx_hddled_22>;
+ pinctrl-names = "default";
+
+ hdd1-green {
+ label = "synology:green:hdd1";
+ gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd1-amber {
+ label = "synology:amber:hdd1";
+ gpios = <&gpio0 23 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd2-green {
+ label = "synology:green:hdd2";
+ gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd2-amber {
+ label = "synology:amber:hdd2";
+ gpios = <&gpio0 22 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ gpio-leds-hdd-36 {
+ status = "disabled";
+ compatible = "gpio-leds";
+ pinctrl-0 = <&pmx_hddled_36 &pmx_hddled_37 &pmx_hddled_38
+ &pmx_hddled_39 &pmx_hddled_40 &pmx_hddled_41
+ &pmx_hddled_42 &pmx_hddled_43 &pmx_hddled_44
+ &pmx_hddled_45>;
+ pinctrl-names = "default";
+
+ hdd1-green {
+ label = "synology:green:hdd1";
+ gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd1-amber {
+ label = "synology:amber:hdd1";
+ gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd2-green {
+ label = "synology:green:hdd2";
+ gpios = <&gpio1 6 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd2-amber {
+ label = "synology:amber:hdd2";
+ gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd3-green {
+ label = "synology:green:hdd3";
+ gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd3-amber {
+ label = "synology:amber:hdd3";
+ gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd4-green {
+ label = "synology:green:hdd4";
+ gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd4-amber {
+ label = "synology:amber:hdd4";
+ gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd5-green {
+ label = "synology:green:hdd5";
+ gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd5-amber {
+ label = "synology:amber:hdd5";
+ gpios = <&gpio1 13 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ gpio-leds-hdd-38 {
+ status = "disabled";
+ compatible = "gpio-leds";
+ pinctrl-0 = <&pmx_hddled_38 &pmx_hddled_39 &pmx_hddled_36 &pmx_hddled_37>;
+ pinctrl-names = "default";
+
+ hdd1-green {
+ label = "synology:green:hdd1";
+ gpios = <&gpio1 6 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd1-amber {
+ label = "synology:amber:hdd1";
+ gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd2-green {
+ label = "synology:green:hdd2";
+ gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
+ };
+
+ hdd2-amber {
+ label = "synology:amber:hdd2";
+ gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ regulators-hdd-29 {
+ status = "disabled";
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-0 = <&pmx_hdd1_pwr_29 &pmx_hdd2_pwr_31>;
+ pinctrl-names = "default";
+
+ regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "hdd1power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio0 29 GPIO_ACTIVE_HIGH>;
+ };
+
+ regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "hdd2power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio0 31 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ regulators-hdd-30-1 {
+ status = "disabled";
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-0 = <&pmx_hdd1_pwr_30>;
+ pinctrl-names = "default";
+
+ regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "hdd1power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio0 30 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ regulators-hdd-30-2 {
+ status = "disabled";
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-0 = <&pmx_hdd1_pwr_30 &pmx_hdd2_pwr_34>;
+ pinctrl-names = "default";
+
+ regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "hdd1power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio0 30 GPIO_ACTIVE_HIGH>;
+ };
+
+ regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "hdd2power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio1 2 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ regulators-hdd-30-4 {
+ status = "disabled";
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-0 = <&pmx_hdd1_pwr_30 &pmx_hdd2_pwr_34
+ &pmx_hdd3_pwr_44 &pmx_hdd4_pwr_45>;
+ pinctrl-names = "default";
+
+ regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "hdd1power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio0 30 GPIO_ACTIVE_HIGH>;
+ };
+
+ regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "hdd2power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio1 2 GPIO_ACTIVE_HIGH>;
+ };
+
+ regulator@3 {
+ compatible = "regulator-fixed";
+ reg = <3>;
+ regulator-name = "hdd3power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio1 12 GPIO_ACTIVE_HIGH>;
+ };
+
+ regulator@4 {
+ compatible = "regulator-fixed";
+ reg = <4>;
+ regulator-name = "hdd4power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio1 13 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ regulators-hdd-31 {
+ status = "disabled";
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-0 = <&pmx_hdd2_pwr_31>;
+ pinctrl-names = "default";
+
+ regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "hdd2power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio0 31 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ regulators-hdd-34 {
+ status = "disabled";
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-0 = <&pmx_hdd2_pwr_34 &pmx_hdd3_pwr_44
+ &pmx_hdd4_pwr_45>;
+ pinctrl-names = "default";
+
+ regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <2>;
+ regulator-name = "hdd2power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio1 2 GPIO_ACTIVE_HIGH>;
+ };
+
+ regulator@3 {
+ compatible = "regulator-fixed";
+ reg = <3>;
+ regulator-name = "hdd3power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio1 12 GPIO_ACTIVE_HIGH>;
+ };
+
+ regulator@4 {
+ compatible = "regulator-fixed";
+ reg = <4>;
+ regulator-name = "hdd4power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ startup-delay-us = <5000000>;
+ gpio = <&gpio1 13 GPIO_ACTIVE_HIGH>;
+ };
+ };
+};
+
+&mdio {
+ status = "okay";
+
+ ethphy0: ethernet-phy@0 {
+ device_type = "ethernet-phy";
+ reg = <8>;
+ };
+
+ ethphy1: ethernet-phy@1 {
+ device_type = "ethernet-phy";
+ reg = <9>;
+ };
+};
+
+&eth0 {
+ status = "okay";
+
+ ethernet0-port@0 {
+ phy-handle = <&ethphy0>;
+ };
+};
+
+&eth1 {
+ status = "disabled";
+
+ ethernet1-port@0 {
+ phy-handle = <&ethphy1>;
+ };
+};
diff --git a/arch/arm/boot/dts/kirkwood-t5325.dts b/arch/arm/boot/dts/kirkwood-t5325.dts
new file mode 100644
index 000000000000..7d1c7677a18f
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-t5325.dts
@@ -0,0 +1,208 @@
+/*
+ * Device Tree file for HP t5325 Thin Client"
+ *
+ * Copyright (C) 2014
+ *
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+*/
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+
+/ {
+ model = "HP t5325 Thin Client";
+ compatible = "hp,t5325", "marvell,kirkwood-88f6281", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x20000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ mbus {
+ pcie-controller {
+ status = "okay";
+
+ pcie@1,0 {
+ status = "okay";
+ };
+ };
+ };
+
+ ocp@f1000000 {
+ pinctrl: pinctrl@10000 {
+ pinctrl-0 = <&pmx_i2s &pmx_sysrst>;
+ pinctrl-names = "default";
+
+ pmx_button_power: pmx-button_power {
+ marvell,pins = "mpp45";
+ marvell,function = "gpio";
+ };
+
+ pmx_power_off: pmx-power-off {
+ marvell,pins = "mpp48";
+ marvell,function = "gpio";
+ };
+
+ pmx_led: pmx-led {
+ marvell,pins = "mpp21";
+ marvell,function = "gpio";
+ };
+
+ pmx_usb_sata_power_enable: pmx-usb-sata-power-enable {
+ marvell,pins = "mpp44";
+ marvell,function = "gpio";
+ };
+
+ /*
+ * Redefined from kirkwood-6281.dtsi, because
+ * we don't use SPI CS on MPP0, but on MPP7.
+ */
+ pmx_spi: pmx-spi {
+ marvell,pins = "mpp1", "mpp2", "mpp3", "mpp7";
+ marvell,function = "spi";
+ };
+
+ pmx_sysrst: pmx-sysrst {
+ marvell,pins = "mpp6";
+ marvell,function = "sysrst";
+ };
+
+ pmx_i2s: pmx-i2s {
+ marvell,pins = "mpp39", "mpp40", "mpp41", "mpp42",
+ "mpp43";
+ marvell,function = "audio";
+ };
+ };
+
+ spi@10600 {
+ pinctrl-0 = <&pmx_spi>;
+ pinctrl-names = "default";
+ status = "okay";
+
+ flash@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "st,m25p80";
+ spi-max-frequency = <86000000>;
+ reg = <0>;
+ mode = <0>;
+
+ partition@0 {
+ reg = <0x0 0x80000>;
+ label = "u-boot";
+ };
+
+ partition@1 {
+ reg = <0x80000 0x40000>;
+ label = "SSD firmware";
+ };
+
+ partition@2 {
+ reg = <0xc0000 0x10000>;
+ label = "u-boot env";
+ };
+
+ partition@3 {
+ reg = <0xd0000 0x10000>;
+ label = "permanent u-boot env";
+ };
+
+ partition@4 {
+ reg = <0xd0000 0x10000>;
+ label = "permanent u-boot env";
+ };
+ };
+ };
+
+ i2c@11000 {
+ status = "okay";
+
+ alc5621: alc5621@1a {
+ compatible = "realtek,alc5621";
+ reg = <0x1a>;
+ };
+ };
+
+ serial@12000 {
+ status = "okay";
+ };
+
+ sata@80000 {
+ status = "okay";
+ nr-ports = <2>;
+ };
+
+ audio: audio-controller@a0000 {
+ status = "okay";
+ };
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-0 = <&pmx_usb_sata_power_enable>;
+ pinctrl-names = "default";
+
+ usb_power: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "USB-SATA Power";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ gpio = <&gpio1 12 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-0 = <&pmx_button_power>;
+ pinctrl-names = "default";
+
+ button@1 {
+ label = "Power Button";
+ linux,code = <KEY_POWER>;
+ gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ gpio_poweroff {
+ compatible = "gpio-poweroff";
+ pinctrl-0 = <&pmx_power_off>;
+ pinctrl-names = "default";
+ gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>;
+ };
+
+};
+
+&mdio {
+ status = "okay";
+
+ ethphy0: ethernet-phy {
+ device_type = "ethernet-phy";
+ reg = <8>;
+ };
+};
+
+&eth0 {
+ status = "okay";
+ ethernet0-port@0 {
+ phy-handle = <&ethphy0>;
+ };
+};
diff --git a/arch/arm/boot/dts/kirkwood-ts419-6281.dts b/arch/arm/boot/dts/kirkwood-ts419-6281.dts
new file mode 100644
index 000000000000..aa22aa862857
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ts419-6281.dts
@@ -0,0 +1,20 @@
+/*
+ * Device Tree file for QNAP TS41X with 6281 SoC
+ *
+ * Copyright (C) 2013, 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6281.dtsi"
+#include "kirkwood-ts219.dtsi"
+#include "kirkwood-ts419.dtsi"
+
+&ethphy0 { reg = <8>; };
+&ethphy1 { reg = <0>; };
diff --git a/arch/arm/boot/dts/kirkwood-ts419-6282.dts b/arch/arm/boot/dts/kirkwood-ts419-6282.dts
new file mode 100644
index 000000000000..d7512d4cdced
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ts419-6282.dts
@@ -0,0 +1,32 @@
+/*
+ * Device Tree file for QNAP TS41X with 6282 SoC
+ *
+ * Copyright (C) 2013, 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.
+ */
+
+/dts-v1/;
+
+#include "kirkwood.dtsi"
+#include "kirkwood-6282.dtsi"
+#include "kirkwood-ts219.dtsi"
+#include "kirkwood-ts419.dtsi"
+
+/ {
+ mbus {
+ pcie-controller {
+ status = "okay";
+
+ pcie@2,0 {
+ status = "okay";
+ };
+ };
+ };
+};
+
+&ethphy0 { reg = <0>; };
+&ethphy1 { reg = <1>; };
diff --git a/arch/arm/boot/dts/kirkwood-ts419.dtsi b/arch/arm/boot/dts/kirkwood-ts419.dtsi
new file mode 100644
index 000000000000..1a9c624c7a92
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ts419.dtsi
@@ -0,0 +1,75 @@
+/*
+ * Device Tree include file for QNAP TS41X
+ *
+ * Copyright (C) 2013, 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.
+ */
+
+/ {
+ model = "QNAP TS419 family";
+ compatible = "qnap,ts419", "marvell,kirkwood";
+
+ ocp@f1000000 {
+ pinctrl: pinctrl@10000 {
+ pinctrl-names = "default";
+
+ pmx_USB_copy_button: pmx-USB-copy-button {
+ marvell,pins = "mpp43";
+ marvell,function = "gpio";
+ };
+ pmx_reset_button: pmx-reset-button {
+ marvell,pins = "mpp37";
+ marvell,function = "gpio";
+ };
+ /*
+ * JP1 indicates if an LCD module is installed
+ * on the serial port (0), or if the port is used
+ * as a console (1).
+ */
+ pmx_jumper_jp1: pmx-jumper_jp1 {
+ marvell,pins = "mpp45";
+ marvell,function = "gpio";
+ };
+
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-0 = <&pmx_reset_button &pmx_USB_copy_button>;
+ pinctrl-names = "default";
+
+ button@1 {
+ label = "USB Copy";
+ linux,code = <KEY_COPY>;
+ gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
+ };
+ button@2 {
+ label = "Reset";
+ linux,code = <KEY_RESTART>;
+ gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
+ };
+ };
+};
+
+&mdio {
+ status = "okay";
+
+ ethphy1: ethernet-phy@1 {
+ device_type = "ethernet-phy";
+ /* overwrite reg property in board file */
+ };
+};
+
+&eth1 {
+ status = "okay";
+ ethernet1-port@0 {
+ phy-handle = <&ethphy1>;
+ };
+};
diff --git a/arch/arm/boot/dts/kirkwood.dtsi b/arch/arm/boot/dts/kirkwood.dtsi
index 6abf44d257df..90384587c278 100644
--- a/arch/arm/boot/dts/kirkwood.dtsi
+++ b/arch/arm/boot/dts/kirkwood.dtsi
@@ -24,6 +24,7 @@
aliases {
gpio0 = &gpio0;
gpio1 = &gpio1;
+ i2c0 = &i2c0;
};
mbus {
@@ -111,7 +112,7 @@
clocks = <&gate_clk 7>;
};
- i2c@11000 {
+ i2c0: i2c@11000 {
compatible = "marvell,mv64xxx-i2c";
reg = <0x11000 0x20>;
#address-cells = <1>;
@@ -145,6 +146,11 @@
reg = <0x20000 0x80>, <0x1500 0x20>;
};
+ system-controller@20000 {
+ compatible = "marvell,orion-system-controller";
+ reg = <0x20000 0x120>;
+ };
+
bridge_intc: bridge-interrupt-ctrl@20110 {
compatible = "marvell,orion-bridge-intc";
interrupt-controller;
@@ -161,6 +167,11 @@
#clock-cells = <1>;
};
+ l2: l2-cache@20128 {
+ compatible = "marvell,kirkwood-cache";
+ reg = <0x20128 0x4>;
+ };
+
intc: main-interrupt-ctrl@20200 {
compatible = "marvell,orion-intc";
interrupt-controller;
@@ -178,7 +189,7 @@
wdt: watchdog-timer@20300 {
compatible = "marvell,orion-wdt";
- reg = <0x20300 0x28>;
+ reg = <0x20300 0x28>, <0x20108 0x4>;
interrupt-parent = <&bridge_intc>;
interrupts = <3>;
clocks = <&gate_clk 7>;
@@ -300,5 +311,14 @@
#phy-cells = <0>;
status = "ok";
};
+
+ audio0: audio-controller@a0000 {
+ compatible = "marvell,kirkwood-audio";
+ reg = <0xa0000 0x2210>;
+ interrupts = <24>;
+ clocks = <&gate_clk 9>;
+ clock-names = "internal";
+ status = "disabled";
+ };
};
};
diff --git a/arch/arm/boot/dts/marco.dtsi b/arch/arm/boot/dts/marco.dtsi
index 1579c3491ccd..0c9647d28765 100644
--- a/arch/arm/boot/dts/marco.dtsi
+++ b/arch/arm/boot/dts/marco.dtsi
@@ -58,9 +58,10 @@
#size-cells = <1>;
ranges = <0xc2000000 0xc2000000 0x1000000>;
- reset-controller@c2000000 {
+ rstc: reset-controller@c2000000 {
compatible = "sirf,marco-rstc";
reg = <0xc2000000 0x10000>;
+ #reset-cells = <1>;
};
};
diff --git a/arch/arm/boot/dts/omap-gpmc-smsc9221.dtsi b/arch/arm/boot/dts/omap-gpmc-smsc9221.dtsi
new file mode 100644
index 000000000000..73e272fadc20
--- /dev/null
+++ b/arch/arm/boot/dts/omap-gpmc-smsc9221.dtsi
@@ -0,0 +1,58 @@
+/*
+ * Common file for GPMC connected smsc9221 on omaps
+ *
+ * Compared to smsc911x, smsc9221 (and others like smsc9217
+ * or smsc 9218) has faster timings, leading to higher
+ * bandwidth.
+ *
+ * Note that the board specifc DTS file needs to specify
+ * ranges, pinctrl, reg, interrupt parent and interrupts.
+ */
+
+/ {
+ vddvario: regulator-vddvario {
+ compatible = "regulator-fixed";
+ regulator-name = "vddvario";
+ regulator-always-on;
+ };
+
+ vdd33a: regulator-vdd33a {
+ compatible = "regulator-fixed";
+ regulator-name = "vdd33a";
+ regulator-always-on;
+ };
+};
+
+&gpmc {
+ ethernet@gpmc {
+ compatible = "smsc,lan9221","smsc,lan9115";
+ bank-width = <2>;
+
+ gpmc,mux-add-data;
+ gpmc,cs-on-ns = <0>;
+ gpmc,cs-rd-off-ns = <42>;
+ gpmc,cs-wr-off-ns = <36>;
+ gpmc,adv-on-ns = <6>;
+ gpmc,adv-rd-off-ns = <12>;
+ gpmc,adv-wr-off-ns = <12>;
+ gpmc,oe-on-ns = <0>;
+ gpmc,oe-off-ns = <42>;
+ gpmc,we-on-ns = <0>;
+ gpmc,we-off-ns = <36>;
+ gpmc,rd-cycle-ns = <60>;
+ gpmc,wr-cycle-ns = <54>;
+ gpmc,access-ns = <36>;
+ gpmc,page-burst-access-ns = <0>;
+ gpmc,bus-turnaround-ns = <0>;
+ gpmc,cycle2cycle-delay-ns = <0>;
+ gpmc,wr-data-mux-bus-ns = <18>;
+ gpmc,wr-access-ns = <42>;
+ gpmc,cycle2cycle-samecsen;
+ gpmc,cycle2cycle-diffcsen;
+
+ vddvario-supply = <&vddvario>;
+ vdd33a-supply = <&vdd33a>;
+ reg-io-width = <4>;
+ smsc,save-mac-address;
+ };
+};
diff --git a/arch/arm/boot/dts/omap2.dtsi b/arch/arm/boot/dts/omap2.dtsi
index 5377ddf83bf8..22f35ea142c1 100644
--- a/arch/arm/boot/dts/omap2.dtsi
+++ b/arch/arm/boot/dts/omap2.dtsi
@@ -271,5 +271,36 @@
ti,hwmods = "timer12";
ti,timer-pwm;
};
+
+ dss: dss@48050000 {
+ compatible = "ti,omap2-dss";
+ reg = <0x48050000 0x400>;
+ status = "disabled";
+ ti,hwmods = "dss_core";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ dispc@48050400 {
+ compatible = "ti,omap2-dispc";
+ reg = <0x48050400 0x400>;
+ interrupts = <25>;
+ ti,hwmods = "dss_dispc";
+ };
+
+ rfbi: encoder@48050800 {
+ compatible = "ti,omap2-rfbi";
+ reg = <0x48050800 0x400>;
+ status = "disabled";
+ ti,hwmods = "dss_rfbi";
+ };
+
+ venc: encoder@48050c00 {
+ compatible = "ti,omap2-venc";
+ reg = <0x48050c00 0x400>;
+ status = "disabled";
+ ti,hwmods = "dss_venc";
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/omap2420.dtsi b/arch/arm/boot/dts/omap2420.dtsi
index 60c605de22dd..85b1fb014c43 100644
--- a/arch/arm/boot/dts/omap2420.dtsi
+++ b/arch/arm/boot/dts/omap2420.dtsi
@@ -99,6 +99,7 @@
dmas = <&sdma 31>,
<&sdma 32>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp2: mcbsp@48076000 {
@@ -112,6 +113,7 @@
dmas = <&sdma 33>,
<&sdma 34>;
dma-names = "tx", "rx";
+ status = "disabled";
};
msdi1: mmc@4809c000 {
diff --git a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi
index d624345666f5..9d2f028fd687 100644
--- a/arch/arm/boot/dts/omap2430.dtsi
+++ b/arch/arm/boot/dts/omap2430.dtsi
@@ -113,6 +113,7 @@
dmas = <&sdma 31>,
<&sdma 32>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp2: mcbsp@48076000 {
@@ -128,6 +129,7 @@
dmas = <&sdma 33>,
<&sdma 34>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp3: mcbsp@4808c000 {
@@ -143,6 +145,7 @@
dmas = <&sdma 17>,
<&sdma 18>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp4: mcbsp@4808e000 {
@@ -158,6 +161,7 @@
dmas = <&sdma 19>,
<&sdma 20>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp5: mcbsp@48096000 {
@@ -173,6 +177,7 @@
dmas = <&sdma 21>,
<&sdma 22>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mmc1: mmc@4809c000 {
diff --git a/arch/arm/boot/dts/omap3-beagle-xm.dts b/arch/arm/boot/dts/omap3-beagle-xm.dts
index 447e714d435b..cf0be662297e 100644
--- a/arch/arm/boot/dts/omap3-beagle-xm.dts
+++ b/arch/arm/boot/dts/omap3-beagle-xm.dts
@@ -24,6 +24,11 @@
reg = <0x80000000 0x20000000>; /* 512 MB */
};
+ aliases {
+ display0 = &dvi0;
+ display1 = &tv0;
+ };
+
leds {
compatible = "gpio-leds";
@@ -86,6 +91,60 @@
reset-gpios = <&gpio5 19 GPIO_ACTIVE_LOW>; /* gpio_147 */
vcc-supply = <&hsusb2_power>;
};
+
+ tfp410: encoder@0 {
+ compatible = "ti,tfp410";
+ powerdown-gpios = <&twl_gpio 2 GPIO_ACTIVE_LOW>;
+
+ /* XXX pinctrl from twl */
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ tfp410_in: endpoint@0 {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ tfp410_out: endpoint@0 {
+ remote-endpoint = <&dvi_connector_in>;
+ };
+ };
+ };
+ };
+
+ dvi0: connector@0 {
+ compatible = "dvi-connector";
+ label = "dvi";
+
+ digital;
+
+ ddc-i2c-bus = <&i2c3>;
+
+ port {
+ dvi_connector_in: endpoint {
+ remote-endpoint = <&tfp410_out>;
+ };
+ };
+ };
+
+ tv0: connector@1 {
+ compatible = "svideo-connector";
+ label = "tv";
+
+ port {
+ tv_connector_in: endpoint {
+ remote-endpoint = <&venc_out>;
+ };
+ };
+ };
};
&omap3_pmx_wkup {
@@ -94,6 +153,17 @@
0x0e (PIN_INPUT | PIN_OFF_WAKEUPENABLE | MUX_MODE4) /* sys_boot2.gpio_4 */
>;
};
+
+ dss_dpi_pins2: pinmux_dss_dpi_pins1 {
+ pinctrl-single,pins = <
+ 0x0a (PIN_OUTPUT | MUX_MODE3) /* sys_boot0.dss_data18 */
+ 0x0c (PIN_OUTPUT | MUX_MODE3) /* sys_boot1.dss_data19 */
+ 0x10 (PIN_OUTPUT | MUX_MODE3) /* sys_boot3.dss_data20 */
+ 0x12 (PIN_OUTPUT | MUX_MODE3) /* sys_boot4.dss_data21 */
+ 0x14 (PIN_OUTPUT | MUX_MODE3) /* sys_boot5.dss_data22 */
+ 0x16 (PIN_OUTPUT | MUX_MODE3) /* sys_boot6.dss_data23 */
+ >;
+ };
};
&omap3_pmx_core {
@@ -119,6 +189,35 @@
OMAP3_CORE1_IOPAD(0x21de, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_cs1.hsusb2_data3 */
>;
};
+
+ dss_dpi_pins1: pinmux_dss_dpi_pins2 {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x20d4, PIN_OUTPUT | MUX_MODE0) /* dss_pclk.dss_pclk */
+ OMAP3_CORE1_IOPAD(0x20d6, PIN_OUTPUT | MUX_MODE0) /* dss_hsync.dss_hsync */
+ OMAP3_CORE1_IOPAD(0x20d8, PIN_OUTPUT | MUX_MODE0) /* dss_vsync.dss_vsync */
+ OMAP3_CORE1_IOPAD(0x20da, PIN_OUTPUT | MUX_MODE0) /* dss_acbias.dss_acbias */
+
+ OMAP3_CORE1_IOPAD(0x20e8, PIN_OUTPUT | MUX_MODE0) /* dss_data6.dss_data6 */
+ OMAP3_CORE1_IOPAD(0x20ea, PIN_OUTPUT | MUX_MODE0) /* dss_data7.dss_data7 */
+ OMAP3_CORE1_IOPAD(0x20ec, PIN_OUTPUT | MUX_MODE0) /* dss_data8.dss_data8 */
+ OMAP3_CORE1_IOPAD(0x20ee, PIN_OUTPUT | MUX_MODE0) /* dss_data9.dss_data9 */
+ OMAP3_CORE1_IOPAD(0x20f0, PIN_OUTPUT | MUX_MODE0) /* dss_data10.dss_data10 */
+ OMAP3_CORE1_IOPAD(0x20f2, PIN_OUTPUT | MUX_MODE0) /* dss_data11.dss_data11 */
+ OMAP3_CORE1_IOPAD(0x20f4, PIN_OUTPUT | MUX_MODE0) /* dss_data12.dss_data12 */
+ OMAP3_CORE1_IOPAD(0x20f6, PIN_OUTPUT | MUX_MODE0) /* dss_data13.dss_data13 */
+ OMAP3_CORE1_IOPAD(0x20f8, PIN_OUTPUT | MUX_MODE0) /* dss_data14.dss_data14 */
+ OMAP3_CORE1_IOPAD(0x20fa, PIN_OUTPUT | MUX_MODE0) /* dss_data15.dss_data15 */
+ OMAP3_CORE1_IOPAD(0x20fc, PIN_OUTPUT | MUX_MODE0) /* dss_data16.dss_data16 */
+ OMAP3_CORE1_IOPAD(0x20fe, PIN_OUTPUT | MUX_MODE0) /* dss_data17.dss_data17 */
+
+ OMAP3_CORE1_IOPAD(0x2100, PIN_OUTPUT | MUX_MODE3) /* dss_data18.dss_data0 */
+ OMAP3_CORE1_IOPAD(0x2102, PIN_OUTPUT | MUX_MODE3) /* dss_data19.dss_data1 */
+ OMAP3_CORE1_IOPAD(0x2104, PIN_OUTPUT | MUX_MODE3) /* dss_data20.dss_data2 */
+ OMAP3_CORE1_IOPAD(0x2106, PIN_OUTPUT | MUX_MODE3) /* dss_data21.dss_data3 */
+ OMAP3_CORE1_IOPAD(0x2108, PIN_OUTPUT | MUX_MODE3) /* dss_data22.dss_data4 */
+ OMAP3_CORE1_IOPAD(0x210a, PIN_OUTPUT | MUX_MODE3) /* dss_data23.dss_data5 */
+ >;
+ };
};
&omap3_pmx_core2 {
@@ -164,15 +263,6 @@
&i2c3 {
clock-frequency = <100000>;
-
- /*
- * Display monitor features are burnt in the EEPROM
- * as EDID data.
- */
- eeprom@50 {
- compatible = "ti,eeprom";
- reg = <0x50>;
- };
};
&mmc1 {
@@ -234,3 +324,37 @@
regulator-max-microvolt = <1800000>;
regulator-always-on;
};
+
+&mcbsp2 {
+ status = "okay";
+};
+
+&dss {
+ status = "ok";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &dss_dpi_pins1
+ &dss_dpi_pins2
+ >;
+
+ port {
+ dpi_out: endpoint {
+ remote-endpoint = <&tfp410_in>;
+ data-lines = <24>;
+ };
+ };
+};
+
+&venc {
+ status = "ok";
+
+ vdda-supply = <&vdac>;
+
+ port {
+ venc_out: endpoint {
+ remote-endpoint = <&tv_connector_in>;
+ ti,channels = <2>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts
index 5053766d369b..3c3e6da1deac 100644
--- a/arch/arm/boot/dts/omap3-beagle.dts
+++ b/arch/arm/boot/dts/omap3-beagle.dts
@@ -24,6 +24,11 @@
reg = <0x80000000 0x10000000>; /* 256 MB */
};
+ aliases {
+ display0 = &dvi0;
+ display1 = &tv0;
+ };
+
leds {
compatible = "gpio-leds";
pmu_stat {
@@ -80,6 +85,61 @@
};
};
+
+ tfp410: encoder@0 {
+ compatible = "ti,tfp410";
+ powerdown-gpios = <&gpio6 10 GPIO_ACTIVE_LOW>; /* gpio_170 */
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&tfp410_pins>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ tfp410_in: endpoint@0 {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ tfp410_out: endpoint@0 {
+ remote-endpoint = <&dvi_connector_in>;
+ };
+ };
+ };
+ };
+
+ dvi0: connector@0 {
+ compatible = "dvi-connector";
+ label = "dvi";
+
+ digital;
+
+ ddc-i2c-bus = <&i2c3>;
+
+ port {
+ dvi_connector_in: endpoint {
+ remote-endpoint = <&tfp410_out>;
+ };
+ };
+ };
+
+ tv0: connector@1 {
+ compatible = "svideo-connector";
+ label = "tv";
+
+ port {
+ tv_connector_in: endpoint {
+ remote-endpoint = <&venc_out>;
+ };
+ };
+ };
};
&omap3_pmx_wkup {
@@ -113,6 +173,45 @@
0x170 (PIN_OUTPUT | MUX_MODE0) /* uart3_tx_irtx.uart3_tx_irtx */
>;
};
+
+ tfp410_pins: pinmux_tfp410_pins {
+ pinctrl-single,pins = <
+ 0x194 (PIN_OUTPUT | MUX_MODE4) /* hdq_sio.gpio_170 */
+ >;
+ };
+
+ dss_dpi_pins: pinmux_dss_dpi_pins {
+ pinctrl-single,pins = <
+ 0x0a4 (PIN_OUTPUT | MUX_MODE0) /* dss_pclk.dss_pclk */
+ 0x0a6 (PIN_OUTPUT | MUX_MODE0) /* dss_hsync.dss_hsync */
+ 0x0a8 (PIN_OUTPUT | MUX_MODE0) /* dss_vsync.dss_vsync */
+ 0x0aa (PIN_OUTPUT | MUX_MODE0) /* dss_acbias.dss_acbias */
+ 0x0ac (PIN_OUTPUT | MUX_MODE0) /* dss_data0.dss_data0 */
+ 0x0ae (PIN_OUTPUT | MUX_MODE0) /* dss_data1.dss_data1 */
+ 0x0b0 (PIN_OUTPUT | MUX_MODE0) /* dss_data2.dss_data2 */
+ 0x0b2 (PIN_OUTPUT | MUX_MODE0) /* dss_data3.dss_data3 */
+ 0x0b4 (PIN_OUTPUT | MUX_MODE0) /* dss_data4.dss_data4 */
+ 0x0b6 (PIN_OUTPUT | MUX_MODE0) /* dss_data5.dss_data5 */
+ 0x0b8 (PIN_OUTPUT | MUX_MODE0) /* dss_data6.dss_data6 */
+ 0x0ba (PIN_OUTPUT | MUX_MODE0) /* dss_data7.dss_data7 */
+ 0x0bc (PIN_OUTPUT | MUX_MODE0) /* dss_data8.dss_data8 */
+ 0x0be (PIN_OUTPUT | MUX_MODE0) /* dss_data9.dss_data9 */
+ 0x0c0 (PIN_OUTPUT | MUX_MODE0) /* dss_data10.dss_data10 */
+ 0x0c2 (PIN_OUTPUT | MUX_MODE0) /* dss_data11.dss_data11 */
+ 0x0c4 (PIN_OUTPUT | MUX_MODE0) /* dss_data12.dss_data12 */
+ 0x0c6 (PIN_OUTPUT | MUX_MODE0) /* dss_data13.dss_data13 */
+ 0x0c8 (PIN_OUTPUT | MUX_MODE0) /* dss_data14.dss_data14 */
+ 0x0ca (PIN_OUTPUT | MUX_MODE0) /* dss_data15.dss_data15 */
+ 0x0cc (PIN_OUTPUT | MUX_MODE0) /* dss_data16.dss_data16 */
+ 0x0ce (PIN_OUTPUT | MUX_MODE0) /* dss_data17.dss_data17 */
+ 0x0d0 (PIN_OUTPUT | MUX_MODE0) /* dss_data18.dss_data18 */
+ 0x0d2 (PIN_OUTPUT | MUX_MODE0) /* dss_data19.dss_data19 */
+ 0x0d4 (PIN_OUTPUT | MUX_MODE0) /* dss_data20.dss_data20 */
+ 0x0d6 (PIN_OUTPUT | MUX_MODE0) /* dss_data21.dss_data21 */
+ 0x0d8 (PIN_OUTPUT | MUX_MODE0) /* dss_data22.dss_data22 */
+ 0x0da (PIN_OUTPUT | MUX_MODE0) /* dss_data23.dss_data23 */
+ >;
+ };
};
&omap3_pmx_core2 {
@@ -152,6 +251,10 @@
#include "twl4030.dtsi"
#include "twl4030_omap3.dtsi"
+&i2c3 {
+ clock-frequency = <100000>;
+};
+
&mmc1 {
vmmc-supply = <&vmmc1>;
vmmc_aux-supply = <&vsim>;
@@ -211,3 +314,39 @@
regulator-max-microvolt = <1800000>;
regulator-always-on;
};
+
+&mcbsp2 {
+ status = "okay";
+};
+
+/* Needed to power the DPI pins */
+&vpll2 {
+ regulator-always-on;
+};
+
+&dss {
+ status = "ok";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&dss_dpi_pins>;
+
+ port {
+ dpi_out: endpoint {
+ remote-endpoint = <&tfp410_in>;
+ data-lines = <24>;
+ };
+ };
+};
+
+&venc {
+ status = "ok";
+
+ vdda-supply = <&vdac>;
+
+ port {
+ venc_out: endpoint {
+ remote-endpoint = <&tv_connector_in>;
+ ti,channels = <2>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap3-cm-t3517.dts b/arch/arm/boot/dts/omap3-cm-t3517.dts
new file mode 100644
index 000000000000..d00502f4fd9b
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-cm-t3517.dts
@@ -0,0 +1,136 @@
+/*
+ * Support for CompuLab CM-T3517
+ */
+/dts-v1/;
+
+#include "am3517.dtsi"
+#include "omap3-cm-t3x.dtsi"
+
+/ {
+ model = "CompuLab CM-T3517";
+ compatible = "compulab,omap3-cm-t3517", "ti,am3517", "ti,omap3";
+
+ vmmc: regulator-vmmc {
+ compatible = "regulator-fixed";
+ regulator-name = "vmmc";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ wl12xx_vmmc2: wl12xx_vmmc2 {
+ compatible = "regulator-fixed";
+ regulator-name = "vw1271";
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &wl12xx_wkup_pins
+ &wl12xx_core_pins
+ >;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ gpio = <&gpio1 6 GPIO_ACTIVE_HIGH >; /* gpio6 */
+ startup-delay-us = <20000>;
+ enable-active-high;
+ };
+
+ wl12xx_vaux2: wl12xx_vaux2 {
+ compatible = "regulator-fixed";
+ regulator-name = "vwl1271_vaux2";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+};
+
+&omap3_pmx_wkup {
+
+ wl12xx_wkup_pins: pinmux_wl12xx_wkup_pins {
+ pinctrl-single,pins = <
+ OMAP3_WKUP_IOPAD(0x2a0e, PIN_OUTPUT | MUX_MODE4) /* sys_boot2.gpio_4 */
+ OMAP3_WKUP_IOPAD(0x2a12, PIN_OUTPUT | MUX_MODE4) /* sys_boot4.gpio_6 */
+ >;
+ };
+};
+
+&omap3_pmx_core {
+
+ phy1_reset_pins: pinmux_hsusb1_phy_reset_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2178, PIN_OUTPUT | MUX_MODE4) /* uart2_tx.gpio_146 */
+ >;
+ };
+
+ phy2_reset_pins: pinmux_hsusb2_phy_reset_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x217a, PIN_OUTPUT | MUX_MODE4) /* uart2_rx.gpio_147 */
+ >;
+ };
+
+ otg_drv_vbus: pinmux_otg_drv_vbus {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2210, PIN_INPUT_PULLDOWN | MUX_MODE0) /* rmii_50Mhz_clk.usb0_drvvbus */
+ >;
+ };
+
+ mmc2_pins: pinmux_mmc2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2158, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_clk.sdmmc2_clk */
+ OMAP3_CORE1_IOPAD(0x215a, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_cmd.sdmmc2_cmd */
+ OMAP3_CORE1_IOPAD(0x215c, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat0.sdmmc2_dat0 */
+ OMAP3_CORE1_IOPAD(0x215e, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat1.sdmmc2_dat1 */
+ OMAP3_CORE1_IOPAD(0x2160, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat2.sdmmc2_dat2 */
+ OMAP3_CORE1_IOPAD(0x2162, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat3.sdmmc2_dat3 */
+ >;
+ };
+
+ wl12xx_core_pins: pinmux_wl12xx_core_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x20b8, PIN_OUTPUT | MUX_MODE4) /* gpmc_ncs5.gpio_56 */
+ OMAP3_CORE1_IOPAD(0x2176, PIN_INPUT_PULLUP | MUX_MODE4) /* uart2_rts.gpio_145 */
+ >;
+ };
+
+ usb_hub_pins: pinmux_usb_hub_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2184, PIN_OUTPUT | MUX_MODE4) /* mcbsp4_clkx.gpio_152 - USB HUB RST */
+ >;
+ };
+};
+
+&hsusb1_phy {
+ pinctrl-names = "default";
+ pinctrl-0 = <&phy1_reset_pins>;
+ reset-gpios = <&gpio5 18 GPIO_ACTIVE_LOW>;
+};
+
+&hsusb2_phy {
+ pinctrl-names = "default";
+ pinctrl-0 = <&phy2_reset_pins>;
+ reset-gpios = <&gpio5 19 GPIO_ACTIVE_LOW>;
+};
+
+&davinci_emac {
+ status = "okay";
+};
+
+&davinci_mdio {
+ status = "okay";
+};
+
+&am35x_otg_hs {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&otg_drv_vbus>;
+};
+
+&mmc1 {
+ vmmc-supply = <&vmmc>;
+};
+
+&mmc2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc2_pins>;
+ vmmc-supply = <&wl12xx_vmmc2>;
+ vmmc_aux-supply = <&wl12xx_vaux2>;
+ non-removable;
+ bus-width = <4>;
+ cap-power-off-card;
+};
diff --git a/arch/arm/boot/dts/omap3-cm-t3530.dts b/arch/arm/boot/dts/omap3-cm-t3530.dts
new file mode 100644
index 000000000000..d1458496520e
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-cm-t3530.dts
@@ -0,0 +1,48 @@
+/*
+ * Support for CompuLab CM-T3530
+ */
+/dts-v1/;
+
+#include "omap34xx.dtsi"
+#include "omap3-cm-t3x30.dtsi"
+
+/ {
+ model = "CompuLab CM-T3530";
+ compatible = "compulab,omap3-cm-t3530", "ti,omap34xx", "ti,omap3";
+
+ /* Regulator to trigger the reset signal of the Wifi module */
+ mmc2_sdio_reset: regulator-mmc2-sdio-reset {
+ compatible = "regulator-fixed";
+ regulator-name = "regulator-mmc2-sdio-reset";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&twl_gpio 2 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+};
+
+&omap3_pmx_core {
+ mmc2_pins: pinmux_mmc2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2158, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_clk.sdmmc2_clk */
+ OMAP3_CORE1_IOPAD(0x215a, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_cmd.sdmmc2_cmd */
+ OMAP3_CORE1_IOPAD(0x215c, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat0.sdmmc2_dat0 */
+ OMAP3_CORE1_IOPAD(0x215e, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat1.sdmmc2_dat1 */
+ OMAP3_CORE1_IOPAD(0x2160, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat2.sdmmc2_dat2 */
+ OMAP3_CORE1_IOPAD(0x2162, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat3.sdmmc2_dat3 */
+ OMAP3_CORE1_IOPAD(0x2164, PIN_OUTPUT | MUX_MODE1) /* sdmmc2_dat4.sdmmc2_dir_dat0 */
+ OMAP3_CORE1_IOPAD(0x2166, PIN_OUTPUT | MUX_MODE1) /* sdmmc2_dat5.sdmmc2_dir_dat1 */
+ OMAP3_CORE1_IOPAD(0x2168, PIN_OUTPUT | MUX_MODE1) /* sdmmc2_dat6.sdmmc2_dir_cmd */
+ OMAP3_CORE1_IOPAD(0x216a, PIN_INPUT | MUX_MODE1) /* sdmmc2_dat7.sdmmc2_clkin */
+ >;
+ };
+};
+
+&mmc2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc2_pins>;
+ vmmc-supply = <&mmc2_sdio_reset>;
+ non-removable;
+ bus-width = <4>;
+ cap-power-off-card;
+};
diff --git a/arch/arm/boot/dts/omap3-cm-t3730.dts b/arch/arm/boot/dts/omap3-cm-t3730.dts
index 486f4d6c4219..b3f9a50b3bc8 100644
--- a/arch/arm/boot/dts/omap3-cm-t3730.dts
+++ b/arch/arm/boot/dts/omap3-cm-t3730.dts
@@ -32,57 +32,26 @@
};
&omap3_pmx_core {
- mmc1_pins: pinmux_mmc1_pins {
- pinctrl-single,pins = <
- 0x114 (PIN_OUTPUT_PULLUP | MUX_MODE0) /* sdmmc1_clk.sdmmc1_clk */
- 0x116 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_cmd.sdmmc1_cmd */
- 0x118 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat0.sdmmc1_dat0 */
- 0x11a (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat1.sdmmc1_dat1 */
- 0x11c (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat2.sdmmc1_dat2 */
- 0x11e (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat3.sdmmc1_dat3 */
- >;
- };
mmc2_pins: pinmux_mmc2_pins {
pinctrl-single,pins = <
- 0x128 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_clk.sdmmc2_clk */
- 0x12a (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_cmd.sdmmc2_cmd */
- 0x12c (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat0.sdmmc2_dat0 */
- 0x12e (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat1.sdmmc2_dat1 */
- 0x130 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat2.sdmmc2_dat2 */
- 0x132 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat3.sdmmc2_dat3 */
- >;
- };
-
- smsc1_pins: pinmux_smsc1_pins {
- pinctrl-single,pins = <
- 0x88 (PIN_OUTPUT | MUX_MODE0) /* gpmc_ncs5.gpmc_ncs5 */
- 0x16a (PIN_INPUT_PULLUP | MUX_MODE4) /* uart3_cts_rctx.gpio_163 */
- >;
- };
-
- uart3_pins: pinmux_uart3_pins {
- pinctrl-single,pins = <
- 0x16e (PIN_INPUT | MUX_MODE0) /* uart3_rx_irrx.uart3_rx_irrx */
- 0x170 (PIN_OUTPUT | MUX_MODE0) /* uart3_tx_irtx.uart3_tx_irtx */
+ OMAP3_CORE1_IOPAD(0x2158, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_clk.sdmmc2_clk */
+ OMAP3_CORE1_IOPAD(0x215a, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_cmd.sdmmc2_cmd */
+ OMAP3_CORE1_IOPAD(0x215c, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat0.sdmmc2_dat0 */
+ OMAP3_CORE1_IOPAD(0x215e, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat1.sdmmc2_dat1 */
+ OMAP3_CORE1_IOPAD(0x2160, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat2.sdmmc2_dat2 */
+ OMAP3_CORE1_IOPAD(0x2162, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat3.sdmmc2_dat3 */
>;
};
wl12xx_gpio: pinmux_wl12xx_gpio {
pinctrl-single,pins = <
- 0xb2 (PIN_OUTPUT | MUX_MODE4) /* dss_data3.gpio_73 */
- 0x134 (PIN_INPUT | MUX_MODE4) /* sdmmc2_dat4.gpio_136 */
+ OMAP3_CORE1_IOPAD(0x20e2, PIN_OUTPUT | MUX_MODE4) /* dss_data3.gpio_73 */
+ OMAP3_CORE1_IOPAD(0x2164, PIN_INPUT | MUX_MODE4) /* sdmmc2_dat4.gpio_136 */
>;
};
};
-&mmc1 {
- vmmc-supply = <&vmmc1>;
- bus-width = <4>;
- pinctrl-names = "default";
- pinctrl-0 = <&mmc1_pins>;
-};
-
&mmc2 {
pinctrl-names = "default";
pinctrl-0 = <&mmc2_pins>;
@@ -92,13 +61,3 @@
bus-width = <4>;
cap-power-off-card;
};
-
-&smsc1 {
- pinctrl-names = "default";
- pinctrl-0 = <&smsc1_pins>;
-};
-
-&uart3 {
- pinctrl-names = "default";
- pinctrl-0 = <&uart3_pins>;
-};
diff --git a/arch/arm/boot/dts/omap3-cm-t3x.dtsi b/arch/arm/boot/dts/omap3-cm-t3x.dtsi
new file mode 100644
index 000000000000..c671a2299ea8
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-cm-t3x.dtsi
@@ -0,0 +1,110 @@
+/*
+ * Common support for CompuLab CM-T3x CoMs
+ */
+
+/ {
+
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x10000000>; /* 256 MB */
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&green_led_pins>;
+ ledb {
+ label = "cm-t3x:green";
+ gpios = <&gpio6 26 GPIO_ACTIVE_HIGH>; /* gpio186 */
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ /* HS USB Port 1 Power */
+ hsusb1_power: hsusb1_power_reg {
+ compatible = "regulator-fixed";
+ regulator-name = "hsusb1_vbus";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ startup-delay-us = <70000>;
+ };
+
+ /* HS USB Port 2 Power */
+ hsusb2_power: hsusb2_power_reg {
+ compatible = "regulator-fixed";
+ regulator-name = "hsusb2_vbus";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ startup-delay-us = <70000>;
+ };
+
+ /* HS USB Host PHY on PORT 1 */
+ hsusb1_phy: hsusb1_phy {
+ compatible = "usb-nop-xceiv";
+ vcc-supply = <&hsusb1_power>;
+ };
+
+ /* HS USB Host PHY on PORT 2 */
+ hsusb2_phy: hsusb2_phy {
+ compatible = "usb-nop-xceiv";
+ vcc-supply = <&hsusb2_power>;
+ };
+};
+
+&omap3_pmx_core {
+
+ uart3_pins: pinmux_uart3_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x219e, PIN_INPUT | MUX_MODE0) /* uart3_rx_irrx.uart3_rx_irrx */
+ OMAP3_CORE1_IOPAD(0x21a0, PIN_OUTPUT | MUX_MODE0) /* uart3_tx_irtx.uart3_tx_irtx */
+ >;
+ };
+
+ mmc1_pins: pinmux_mmc1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2144, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_clk.sdmmc1_clk */
+ OMAP3_CORE1_IOPAD(0x2146, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_cmd.sdmmc1_cmd */
+ OMAP3_CORE1_IOPAD(0x2148, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat0.sdmmc1_dat0 */
+ OMAP3_CORE1_IOPAD(0x214a, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat1.sdmmc1_dat1 */
+ OMAP3_CORE1_IOPAD(0x214c, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat2.sdmmc1_dat2 */
+ OMAP3_CORE1_IOPAD(0x214e, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat3.sdmmc1_dat3 */
+ >;
+ };
+
+ green_led_pins: pinmux_green_led_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21e2, PIN_OUTPUT | MUX_MODE4) /* sys_clkout2.gpio_186 */
+ >;
+ };
+};
+
+&uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart3_pins>;
+};
+
+&mmc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
+ bus-width = <4>;
+};
+
+&mmc3 {
+ status = "disabled";
+};
+
+&i2c1 {
+ clock-frequency = <400000>;
+};
+
+&i2c3 {
+ clock-frequency = <400000>;
+};
+&usbhshost {
+ port1-mode = "ehci-phy";
+ port2-mode = "ehci-phy";
+};
+
+&usbhsehci {
+ phys = <&hsusb1_phy &hsusb2_phy>;
+};
diff --git a/arch/arm/boot/dts/omap3-cm-t3x30.dtsi b/arch/arm/boot/dts/omap3-cm-t3x30.dtsi
index 3a9f004d8924..d00055809e31 100644
--- a/arch/arm/boot/dts/omap3-cm-t3x30.dtsi
+++ b/arch/arm/boot/dts/omap3-cm-t3x30.dtsi
@@ -1,28 +1,16 @@
/*
- * Common support for CompuLab CM-T3530 and CM-T3730
+ * Common support for CompuLab CM-T3x30 CoMs
*/
-/ {
- memory {
- device_type = "memory";
- reg = <0x80000000 0x10000000>; /* 256 MB */
- };
+#include "omap3-cm-t3x.dtsi"
+/ {
cpus {
cpu@0 {
cpu0-supply = <&vcc>;
};
};
- leds {
- compatible = "gpio-leds";
- ledb {
- label = "cm-t35:green";
- gpios = <&gpio6 26 GPIO_ACTIVE_HIGH>; /* gpio186 */
- linux,default-trigger = "heartbeat";
- };
- };
-
vddvario: regulator-vddvario {
compatible = "regulator-fixed";
regulator-name = "vddvario";
@@ -36,11 +24,40 @@
};
};
+&omap3_pmx_core {
+
+ smsc1_pins: pinmux_smsc1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x20b8, PIN_OUTPUT | MUX_MODE0) /* gpmc_ncs5.gpmc_ncs5 */
+ OMAP3_CORE1_IOPAD(0x219a, PIN_INPUT_PULLUP | MUX_MODE4) /* uart3_cts_rctx.gpio_163 */
+ >;
+ };
+
+ hsusb0_pins: pinmux_hsusb0_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21a0, PIN_OUTPUT | MUX_MODE0) /* hsusb0_clk.hsusb0_clk */
+ OMAP3_CORE1_IOPAD(0x21a2, PIN_OUTPUT | MUX_MODE0) /* hsusb0_stp.hsusb0_stp */
+ OMAP3_CORE1_IOPAD(0x21a4, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_dir.hsusb0_dir */
+ OMAP3_CORE1_IOPAD(0x21a6, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_nxt.hsusb0_nxt */
+ OMAP3_CORE1_IOPAD(0x21a8, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data0.hsusb2_data0 */
+ OMAP3_CORE1_IOPAD(0x21aa, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data1.hsusb0_data1 */
+ OMAP3_CORE1_IOPAD(0x21ac, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data2.hsusb0_data2 */
+ OMAP3_CORE1_IOPAD(0x21ae, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data3 */
+ OMAP3_CORE1_IOPAD(0x21b0, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data4 */
+ OMAP3_CORE1_IOPAD(0x21b2, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data5 */
+ OMAP3_CORE1_IOPAD(0x21b4, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data6 */
+ OMAP3_CORE1_IOPAD(0x21b6, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data7 */
+ >;
+ };
+};
+
&gpmc {
ranges = <5 0 0x2c000000 0x01000000>;
smsc1: ethernet@5,0 {
compatible = "smsc,lan9221", "smsc,lan9115";
+ pinctrl-names = "default";
+ pinctrl-0 = <&smsc1_pins>;
interrupt-parent = <&gpio6>;
interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
reg = <5 0 0xff>;
@@ -74,8 +91,6 @@
};
&i2c1 {
- clock-frequency = <400000>;
-
twl: twl@48 {
reg = <0x48>;
interrupts = <7>; /* SYS_NIRQ cascaded to intc */
@@ -86,10 +101,31 @@
#include "twl4030.dtsi"
#include "twl4030_omap3.dtsi"
-&i2c3 {
- clock-frequency = <400000>;
+&mmc1 {
+ vmmc-supply = <&vmmc1>;
};
&twl_gpio {
ti,use-leds;
+ /* pullups: BIT(0) */
+ ti,pullups = <0x000001>;
+};
+
+&hsusb1_phy {
+ reset-gpios = <&twl_gpio 6 GPIO_ACTIVE_LOW>;
+};
+
+&hsusb2_phy {
+ reset-gpios = <&twl_gpio 7 GPIO_ACTIVE_LOW>;
+};
+
+&usb_otg_hs {
+ pinctrl-names = "default";
+ pinctrl-0 = <&hsusb0_pins>;
+ interface-type = <0>;
+ usb-phy = <&usb2_phy>;
+ phys = <&usb2_phy>;
+ phy-names = "usb2-phy";
+ mode = <3>;
+ power = <50>;
};
diff --git a/arch/arm/boot/dts/omap3-devkit8000.dts b/arch/arm/boot/dts/omap3-devkit8000.dts
index 4665421bb7bc..bf5a515a3247 100644
--- a/arch/arm/boot/dts/omap3-devkit8000.dts
+++ b/arch/arm/boot/dts/omap3-devkit8000.dts
@@ -101,20 +101,8 @@
status = "disabled";
};
-&mcbsp1 {
- status = "disabled";
-};
-
-&mcbsp3 {
- status = "disabled";
-};
-
-&mcbsp4 {
- status = "disabled";
-};
-
-&mcbsp5 {
- status = "disabled";
+&mcbsp2 {
+ status = "okay";
};
&gpmc {
diff --git a/arch/arm/boot/dts/omap3-gta04.dts b/arch/arm/boot/dts/omap3-gta04.dts
index d3b253bbc885..f8ad125fa46f 100644
--- a/arch/arm/boot/dts/omap3-gta04.dts
+++ b/arch/arm/boot/dts/omap3-gta04.dts
@@ -36,6 +36,14 @@
gpio-key,wakeup;
};
};
+
+ sound {
+ compatible = "ti,omap-twl4030";
+ ti,model = "gta04";
+
+ ti,mcbsp = <&mcbsp2>;
+ ti,codec = <&twl_audio>;
+ };
};
&omap3_pmx_core {
@@ -80,6 +88,12 @@
interrupts = <7>; /* SYS_NIRQ cascaded to intc */
interrupt-parent = <&intc>;
};
+
+ twl_audio: audio {
+ compatible = "ti,twl4030-audio";
+ codec {
+ };
+ };
};
#include "twl4030.dtsi"
@@ -96,6 +110,14 @@
interrupts = <17 IRQ_TYPE_EDGE_RISING>;
};
+ /* accelerometer */
+ bma180@41 {
+ compatible = "bosch,bma180";
+ reg = <0x41>;
+ interrupt-parent = <&gpio3>;
+ interrupts = <19 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
/* leds */
tca6507@45 {
compatible = "ti,tca6507";
@@ -124,6 +146,22 @@
reg = <0x4>;
};
};
+
+ /* compass aka magnetometer */
+ hmc5843@1e {
+ compatible = "honeywell,hmc5843";
+ reg = <0x1e>;
+ };
+
+ /* touchscreen */
+ tsc2007@48 {
+ compatible = "ti,tsc2007";
+ reg = <0x48>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+ gpios = <&gpio6 0 GPIO_ACTIVE_LOW>;
+ ti,x-plate-ohms = <600>;
+ };
};
&i2c3 {
@@ -148,7 +186,9 @@
};
&mmc2 {
- status = "disabled";
+ vmmc-supply = <&vaux4>;
+ bus-width = <4>;
+ ti,non-removable;
};
&mmc3 {
@@ -170,3 +210,12 @@
pinctrl-0 = <&uart3_pins>;
};
+&charger {
+ bb_uvolt = <3200000>;
+ bb_uamp = <150>;
+};
+
+&vaux4 {
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <3150000>;
+};
diff --git a/arch/arm/boot/dts/omap3-igep.dtsi b/arch/arm/boot/dts/omap3-igep.dtsi
index c17009323520..b97736d98a64 100644
--- a/arch/arm/boot/dts/omap3-igep.dtsi
+++ b/arch/arm/boot/dts/omap3-igep.dtsi
@@ -170,6 +170,7 @@
&mcbsp2 {
pinctrl-names = "default";
pinctrl-0 = <&mcbsp2_pins>;
+ status = "okay";
};
&mmc1 {
diff --git a/arch/arm/boot/dts/omap3-igep0020.dts b/arch/arm/boot/dts/omap3-igep0020.dts
index f2779ac75872..7abd64f6ae21 100644
--- a/arch/arm/boot/dts/omap3-igep0020.dts
+++ b/arch/arm/boot/dts/omap3-igep0020.dts
@@ -61,22 +61,63 @@
reset-gpios = <&gpio1 24 GPIO_ACTIVE_LOW>; /* gpio_24 */
vcc-supply = <&hsusb1_power>;
};
+
+ tfp410: encoder@0 {
+ compatible = "ti,tfp410";
+ powerdown-gpios = <&gpio6 10 GPIO_ACTIVE_LOW>; /* gpio_170 */
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ tfp410_in: endpoint@0 {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ tfp410_out: endpoint@0 {
+ remote-endpoint = <&dvi_connector_in>;
+ };
+ };
+ };
+ };
+
+ dvi0: connector@0 {
+ compatible = "dvi-connector";
+ label = "dvi";
+
+ digital;
+
+ ddc-i2c-bus = <&i2c3>;
+
+ port {
+ dvi_connector_in: endpoint {
+ remote-endpoint = <&tfp410_out>;
+ };
+ };
+ };
};
&omap3_pmx_core {
pinctrl-names = "default";
pinctrl-0 = <
&tfp410_pins
- &dss_pins
+ &dss_dpi_pins
>;
- tfp410_pins: tfp410_dvi_pins {
+ tfp410_pins: pinmux_tfp410_pins {
pinctrl-single,pins = <
0x196 (PIN_OUTPUT | MUX_MODE4) /* hdq_sio.gpio_170 */
>;
};
- dss_pins: pinmux_dss_dvi_pins {
+ dss_dpi_pins: pinmux_dss_dpi_pins {
pinctrl-single,pins = <
0x0a4 (PIN_OUTPUT | MUX_MODE0) /* dss_pclk.dss_pclk */
0x0a6 (PIN_OUTPUT | MUX_MODE0) /* dss_hsync.dss_hsync */
@@ -226,3 +267,14 @@
/* Needed for DSS */
regulator-name = "vdds_dsi";
};
+
+&dss {
+ status = "ok";
+
+ port {
+ dpi_out: endpoint {
+ remote-endpoint = <&tfp410_in>;
+ data-lines = <24>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap3-lilly-a83x.dtsi b/arch/arm/boot/dts/omap3-lilly-a83x.dtsi
new file mode 100644
index 000000000000..6369d9f43ca2
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-lilly-a83x.dtsi
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2014 Christoph Fritz <chf.fritzc@googlemail.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 "omap36xx.dtsi"
+
+/ {
+ model = "INCOstartec LILLY-A83X module (DM3730)";
+ compatible = "incostartec,omap3-lilly-a83x", "ti,omap36xx", "ti,omap3";
+
+ chosen {
+ bootargs = "console=ttyO0,115200n8 vt.global_cursor_default=0 consoleblank=0";
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x8000000>; /* 128 MB */
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led1 {
+ label = "lilly-a83x::led1";
+ gpios = <&gpio1 29 GPIO_ACTIVE_LOW>;
+ linux,default-trigger = "default-on";
+ };
+
+ };
+
+ sound {
+ compatible = "ti,omap-twl4030";
+ ti,model = "lilly-a83x";
+
+ ti,mcbsp = <&mcbsp2>;
+ ti,codec = <&twl_audio>;
+ };
+
+ reg_vcc3: vcc3 {
+ compatible = "regulator-fixed";
+ regulator-name = "VCC3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ hsusb1_phy: hsusb1_phy {
+ compatible = "usb-nop-xceiv";
+ vcc-supply = <&reg_vcc3>;
+ };
+};
+
+&omap3_pmx_wkup {
+ pinctrl-names = "default";
+
+ lan9221_pins: pinmux_lan9221_pins {
+ pinctrl-single,pins = <
+ OMAP3_WKUP_IOPAD(0x2a5a, PIN_INPUT | MUX_MODE4) /* reserved.gpio_129 */
+ >;
+ };
+
+ tsc2048_pins: pinmux_tsc2048_pins {
+ pinctrl-single,pins = <
+ OMAP3_WKUP_IOPAD(0x2a16, PIN_INPUT_PULLUP | MUX_MODE4) /* sys_boot6.gpio_8 */
+ >;
+ };
+
+ mmc1cd_pins: pinmux_mmc1cd_pins {
+ pinctrl-single,pins = <
+ OMAP3_WKUP_IOPAD(0x2a56, PIN_INPUT | MUX_MODE4) /* reserved.gpio_126 */
+ >;
+ };
+};
+
+&omap3_pmx_core {
+ pinctrl-names = "default";
+
+ uart1_pins: pinmux_uart1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x217c, PIN_OUTPUT | MUX_MODE0) /* uart1_tx.uart1_tx */
+ OMAP3_CORE1_IOPAD(0x217e, PIN_OUTPUT | MUX_MODE0) /* uart1_rts.uart1_rts */
+ OMAP3_CORE1_IOPAD(0x2180, PIN_INPUT | MUX_MODE0) /* uart1_cts.uart1_cts */
+ OMAP3_CORE1_IOPAD(0x2182, PIN_INPUT | MUX_MODE0) /* uart1_rx.uart1_rx */
+ >;
+ };
+
+ uart2_pins: pinmux_uart2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2170, PIN_OUTPUT | MUX_MODE1) /* mcbsp3_clkx.uart2_tx */
+ OMAP3_CORE1_IOPAD(0x2172, PIN_INPUT | MUX_MODE1) /* mcbsp3_fsx.uart2_rx */
+ >;
+ };
+
+ uart3_pins: pinmux_uart3_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x219e, PIN_INPUT | MUX_MODE0) /* uart3_rx_irrx.uart3_rx_irrx */
+ OMAP3_CORE1_IOPAD(0x21a0, PIN_OUTPUT | MUX_MODE0) /* uart3_tx_irtx.uart3_tx_irtx */
+ >;
+ };
+
+ i2c1_pins: pinmux_i2c1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21ba ,PIN_INPUT_PULLUP | MUX_MODE0) /* i2c1_scl.i2c1_scl */
+ OMAP3_CORE1_IOPAD(0x21bc ,PIN_INPUT_PULLUP | MUX_MODE0) /* i2c1_sda.i2c1_sda */
+ >;
+ };
+
+ i2c2_pins: pinmux_i2c2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21be, PIN_INPUT | MUX_MODE0) /* i2c2_scl.i2c2_scl */
+ OMAP3_CORE1_IOPAD(0x21c0, PIN_INPUT | MUX_MODE0) /* i2c2_sda.i2c2_sda */
+ >;
+ };
+
+ i2c3_pins: pinmux_i2c3_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21c2, PIN_INPUT | MUX_MODE0) /* i2c3_scl.i2c3_scl */
+ OMAP3_CORE1_IOPAD(0x21c4, PIN_INPUT | MUX_MODE0) /* i2c3_sda.i2c3_sda */
+ >;
+ };
+
+ hsusb1_pins: pinmux_hsusb1_pins {
+ pinctrl-single,pins = <
+
+ /* GPIO 182 controls USB-Hub reset. But USB-Phy its
+ * reset can't be controlled. So we clamp this GPIO to
+ * high (PIN_OFF_OUTPUT_HIGH) to always enable USB-Hub.
+ */
+
+ OMAP3_CORE1_IOPAD(0x21de, PIN_OUTPUT_PULLUP | PIN_OFF_OUTPUT_HIGH | MUX_MODE4) /* mcspi2_cs1.gpio_182 */
+ >;
+ };
+
+ hsusb_otg_pins: pinmux_hsusb_otg_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21a2, PIN_INPUT | MUX_MODE0) /* hsusb0_clk.hsusb0_clk */
+ OMAP3_CORE1_IOPAD(0x21a4, PIN_OUTPUT | MUX_MODE0) /* hsusb0_stp.hsusb0_stp */
+ OMAP3_CORE1_IOPAD(0x21a6, PIN_INPUT | MUX_MODE0) /* hsusb0_dir.hsusb0_dir */
+ OMAP3_CORE1_IOPAD(0x21a8, PIN_INPUT | MUX_MODE0) /* hsusb0_nxt.hsusb0_nxt */
+ OMAP3_CORE1_IOPAD(0x21aa, PIN_INPUT | MUX_MODE0) /* hsusb0_data0.hsusb0_data0 */
+ OMAP3_CORE1_IOPAD(0x21ac, PIN_INPUT | MUX_MODE0) /* hsusb0_data1.hsusb0_data1 */
+ OMAP3_CORE1_IOPAD(0x21ae, PIN_INPUT | MUX_MODE0) /* hsusb0_data2.hsusb0_data2 */
+ OMAP3_CORE1_IOPAD(0x21b0, PIN_INPUT | MUX_MODE0) /* hsusb0_data3.hsusb0_data3 */
+ OMAP3_CORE1_IOPAD(0x21b2, PIN_INPUT | MUX_MODE0) /* hsusb0_data4.hsusb0_data4 */
+ OMAP3_CORE1_IOPAD(0x21b4, PIN_INPUT | MUX_MODE0) /* hsusb0_data5.hsusb0_data5 */
+ OMAP3_CORE1_IOPAD(0x21b6, PIN_INPUT | MUX_MODE0) /* hsusb0_data6.hsusb0_data6 */
+ OMAP3_CORE1_IOPAD(0x21b8, PIN_INPUT | MUX_MODE0) /* hsusb0_data7.hsusb0_data7 */
+ >;
+ };
+
+ mmc1_pins: pinmux_mmc1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2144, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_clk.sdmmc1_clk */
+ OMAP3_CORE1_IOPAD(0x2146, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_cmd.sdmmc1_cmd */
+ OMAP3_CORE1_IOPAD(0x2148, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat0.sdmmc1_dat0 */
+ OMAP3_CORE1_IOPAD(0x214a, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat1.sdmmc1_dat1 */
+ OMAP3_CORE1_IOPAD(0x214c, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat2.sdmmc1_dat2 */
+ OMAP3_CORE1_IOPAD(0x214e, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat3.sdmmc1_dat3 */
+ >;
+ };
+
+ spi2_pins: pinmux_spi2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21d6, PIN_INPUT_PULLDOWN | MUX_MODE0) /* mcspi2_clk.mcspi2_clk */
+ OMAP3_CORE1_IOPAD(0x21d8, PIN_INPUT_PULLDOWN | MUX_MODE0) /* mcspi2_simo.mcspi2_simo */
+ OMAP3_CORE1_IOPAD(0x21da, PIN_INPUT_PULLDOWN | MUX_MODE0) /* mcspi2_somi.mcspi2_somi */
+ OMAP3_CORE1_IOPAD(0x21dc, PIN_OUTPUT | MUX_MODE0) /* mcspi2_cs0.mcspi2_cs0 */
+ >;
+ };
+};
+
+&omap3_pmx_core2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &hsusb1_2_pins
+ >;
+
+ hsusb1_2_pins: pinmux_hsusb1_2_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25d8, PIN_OUTPUT | MUX_MODE3) /* etk_clk.hsusb1_stp */
+ OMAP3630_CORE2_IOPAD(0x25da, PIN_INPUT | MUX_MODE3) /* etk_ctl.hsusb1_clk */
+ OMAP3630_CORE2_IOPAD(0x25dc, PIN_INPUT | MUX_MODE3) /* etk_d0.hsusb1_data0 */
+ OMAP3630_CORE2_IOPAD(0x25de, PIN_INPUT | MUX_MODE3) /* etk_d1.hsusb1_data1 */
+ OMAP3630_CORE2_IOPAD(0x25e0, PIN_INPUT | MUX_MODE3) /* etk_d2.hsusb1_data2 */
+ OMAP3630_CORE2_IOPAD(0x25e2, PIN_INPUT | MUX_MODE3) /* etk_d3.hsusb1_data7 */
+ OMAP3630_CORE2_IOPAD(0x25e4, PIN_INPUT | MUX_MODE3) /* etk_d4.hsusb1_data4 */
+ OMAP3630_CORE2_IOPAD(0x25e6, PIN_INPUT | MUX_MODE3) /* etk_d5.hsusb1_data5 */
+ OMAP3630_CORE2_IOPAD(0x25e8, PIN_INPUT | MUX_MODE3) /* etk_d6.hsusb1_data6 */
+ OMAP3630_CORE2_IOPAD(0x25ea, PIN_INPUT | MUX_MODE3) /* etk_d7.hsusb1_data3 */
+ OMAP3630_CORE2_IOPAD(0x25ec, PIN_INPUT | MUX_MODE3) /* etk_d8.hsusb1_dir */
+ OMAP3630_CORE2_IOPAD(0x25ee, PIN_INPUT | MUX_MODE3) /* etk_d9.hsusb1_nxt */
+ >;
+ };
+
+ gpio1_pins: pinmux_gpio1_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25fa, PIN_OUTPUT_PULLDOWN | MUX_MODE4) /* etk_d15.gpio_29 */
+ >;
+ };
+
+};
+
+&gpio1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gpio1_pins>;
+};
+
+&gpio6 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&hsusb1_pins>;
+};
+
+&i2c1 {
+ clock-frequency = <2600000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+
+ twl: twl@48 {
+ reg = <0x48>;
+ interrupts = <7>; /* SYS_NIRQ cascaded to intc */
+ interrupt-parent = <&intc>;
+
+ twl_audio: audio {
+ compatible = "ti,twl4030-audio";
+ codec {
+ };
+ };
+ };
+};
+
+#include "twl4030.dtsi"
+#include "twl4030_omap3.dtsi"
+
+&twl {
+ vmmc1: regulator-vmmc1 {
+ regulator-always-on;
+ };
+
+ vdd1: regulator-vdd1 {
+ regulator-always-on;
+ };
+
+ vdd2: regulator-vdd2 {
+ regulator-always-on;
+ };
+};
+
+&i2c2 {
+ clock-frequency = <2600000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins>;
+};
+
+&i2c3 {
+ clock-frequency = <2600000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3_pins>;
+ gpiom1: gpio@20 {
+ compatible = "mcp,mcp23017";
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x20>;
+ };
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart1_pins>;
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_pins>;
+};
+
+&uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart3_pins>;
+};
+
+&uart4 {
+ status = "disabled";
+};
+
+&mmc1 {
+ cd-gpios = <&gpio4 30 IRQ_TYPE_LEVEL_LOW>;
+ cd-inverted;
+ vmmc-supply = <&vmmc1>;
+ bus-width = <4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins &mmc1cd_pins>;
+ cap-sdio-irq;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+};
+
+&mmc2 {
+ status = "disabled";
+};
+
+&mmc3 {
+ status = "disabled";
+};
+
+&mcspi2 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi2_pins>;
+
+ tsc2046@0 {
+ reg = <0>; /* CS0 */
+ compatible = "ti,tsc2046";
+ interrupt-parent = <&gpio1>;
+ interrupts = <8 0>; /* boot6 / gpio_8 */
+ spi-max-frequency = <1000000>;
+ pendown-gpio = <&gpio1 8 0>;
+ vcc-supply = <&reg_vcc3>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&tsc2048_pins>;
+
+ ti,x-min = <300>;
+ ti,x-max = <3000>;
+ ti,y-min = <600>;
+ ti,y-max = <3600>;
+ ti,x-plate-ohms = <80>;
+ ti,pressure-max = <255>;
+ ti,swap-xy;
+
+ linux,wakeup;
+ };
+};
+
+&usbhsehci {
+ phys = <&hsusb1_phy>;
+};
+
+&usbhshost {
+ pinctrl-names = "default";
+ pinctrl-0 = <&hsusb1_2_pins>;
+ num-ports = <2>;
+ port1-mode = "ehci-phy";
+};
+
+&usb_otg_hs {
+ pinctrl-names = "default";
+ pinctrl-0 = <&hsusb_otg_pins>;
+ interface-type = <0>;
+ usb-phy = <&usb2_phy>;
+ phys = <&usb2_phy>;
+ phy-names = "usb2-phy";
+ mode = <3>;
+ power = <50>;
+};
+
+&gpmc {
+ ranges = <0 0 0x30000000 0x1000000>,
+ <7 0 0x15000000 0x01000000>;
+
+ nand@0,0 {
+ reg = <0 0 0x1000000>;
+ nand-bus-width = <16>;
+ ti,nand-ecc-opt = "bch8";
+ /* no elm on omap3 */
+
+ gpmc,mux-add-data = <0>;
+ gpmc,device-nand;
+ gpmc,device-width = <2>;
+ gpmc,wait-pin = <0>;
+ gpmc,wait-monitoring-ns = <0>;
+ gpmc,burst-length= <4>;
+ gpmc,cs-on-ns = <0>;
+ gpmc,cs-rd-off-ns = <100>;
+ gpmc,cs-wr-off-ns = <100>;
+ gpmc,adv-on-ns = <0>;
+ gpmc,adv-rd-off-ns = <100>;
+ gpmc,adv-wr-off-ns = <100>;
+ gpmc,oe-on-ns = <5>;
+ gpmc,oe-off-ns = <75>;
+ gpmc,we-on-ns = <5>;
+ gpmc,we-off-ns = <75>;
+ gpmc,rd-cycle-ns = <100>;
+ gpmc,wr-cycle-ns = <100>;
+ gpmc,access-ns = <60>;
+ gpmc,page-burst-access-ns = <5>;
+ gpmc,bus-turnaround-ns = <0>;
+ gpmc,cycle2cycle-samecsen;
+ gpmc,cycle2cycle-delay-ns = <50>;
+ gpmc,wr-data-mux-bus-ns = <75>;
+ gpmc,wr-access-ns = <155>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ label = "MLO";
+ reg = <0 0x80000>;
+ };
+
+ partition@0x80000 {
+ label = "u-boot";
+ reg = <0x80000 0x1e0000>;
+ };
+
+ partition@0x260000 {
+ label = "u-boot-environment";
+ reg = <0x260000 0x20000>;
+ };
+
+ partition@0x280000 {
+ label = "kernel";
+ reg = <0x280000 0x500000>;
+ };
+
+ partition@0x780000 {
+ label = "filesystem";
+ reg = <0x780000 0xf880000>;
+ };
+ };
+
+ ethernet@7,0 {
+ compatible = "smsc,lan9221", "smsc,lan9115";
+ bank-width = <2>;
+ gpmc,mux-add-data = <2>;
+ gpmc,cs-on-ns = <10>;
+ gpmc,cs-rd-off-ns = <60>;
+ gpmc,cs-wr-off-ns = <60>;
+ gpmc,adv-on-ns = <0>;
+ gpmc,adv-rd-off-ns = <10>;
+ gpmc,adv-wr-off-ns = <10>;
+ gpmc,oe-on-ns = <10>;
+ gpmc,oe-off-ns = <60>;
+ gpmc,we-on-ns = <10>;
+ gpmc,we-off-ns = <60>;
+ gpmc,rd-cycle-ns = <100>;
+ gpmc,wr-cycle-ns = <100>;
+ gpmc,access-ns = <50>;
+ gpmc,page-burst-access-ns = <5>;
+ gpmc,bus-turnaround-ns = <0>;
+ gpmc,cycle2cycle-delay-ns = <75>;
+ gpmc,wr-data-mux-bus-ns = <15>;
+ gpmc,wr-access-ns = <75>;
+ gpmc,cycle2cycle-samecsen;
+ gpmc,cycle2cycle-diffcsen;
+ vddvario-supply = <&reg_vcc3>;
+ vdd33a-supply = <&reg_vcc3>;
+ reg-io-width = <4>;
+ interrupt-parent = <&gpio5>;
+ interrupts = <1 0x2>;
+ reg = <7 0 0xff>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&lan9221_pins>;
+ phy-mode = "mii";
+ };
+};
diff --git a/arch/arm/boot/dts/omap3-lilly-dbb056.dts b/arch/arm/boot/dts/omap3-lilly-dbb056.dts
new file mode 100644
index 000000000000..834f7c65f62d
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-lilly-dbb056.dts
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2014 Christoph Fritz <chf.fritzc@googlemail.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.
+ */
+/dts-v1/;
+
+#include "omap3-lilly-a83x.dtsi"
+
+/ {
+ model = "INCOstartec LILLY-DBB056 (DM3730)";
+ compatible = "incostartec,omap3-lilly-dbb056", "incostartec,omap3-lilly-a83x", "ti,omap36xx", "ti,omap3";
+};
+
+&twl {
+ vaux2: regulator-vaux2 {
+ compatible = "ti,twl4030-vaux2";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+ };
+};
+
+&omap3_pmx_core {
+ pinctrl-names = "default";
+ pinctrl-0 = <&lcd_pins>;
+
+ lan9117_pins: pinmux_lan9117_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2114, PIN_INPUT | MUX_MODE4) /* cam_fld.gpio_98 */
+ >;
+ };
+
+ gpio4_pins: pinmux_gpio4_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x212e, PIN_INPUT | MUX_MODE4) /* cam_xclkb.gpio_111 -> sja1000 IRQ */
+ >;
+ };
+
+ gpio5_pins: pinmux_gpio5_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x218c, PIN_OUTPUT | PIN_OFF_OUTPUT_HIGH | MUX_MODE4) /* mcbsp1_clk.gpio_156 -> enable DSS */
+ >;
+ };
+
+ lcd_pins: pinmux_lcd_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x20d4, PIN_OUTPUT | MUX_MODE0) /* dss_pclk.dss_pclk */
+ OMAP3_CORE1_IOPAD(0x20d6, PIN_OUTPUT | MUX_MODE0) /* dss_hsync.dss_hsync */
+ OMAP3_CORE1_IOPAD(0x20d8, PIN_OUTPUT | MUX_MODE0) /* dss_vsync.dss_vsync */
+ OMAP3_CORE1_IOPAD(0x20da, PIN_OUTPUT | MUX_MODE0) /* dss_acbias.dss_acbias */
+ OMAP3_CORE1_IOPAD(0x20dc, PIN_OUTPUT | MUX_MODE0) /* dss_data0.dss_data0 */
+ OMAP3_CORE1_IOPAD(0x20de, PIN_OUTPUT | MUX_MODE0) /* dss_data1.dss_data1 */
+ OMAP3_CORE1_IOPAD(0x20e0, PIN_OUTPUT | MUX_MODE0) /* dss_data2.dss_data2 */
+ OMAP3_CORE1_IOPAD(0x20e2, PIN_OUTPUT | MUX_MODE0) /* dss_data3.dss_data3 */
+ OMAP3_CORE1_IOPAD(0x20e4, PIN_OUTPUT | MUX_MODE0) /* dss_data4.dss_data4 */
+ OMAP3_CORE1_IOPAD(0x20e6, PIN_OUTPUT | MUX_MODE0) /* dss_data5.dss_data5 */
+ OMAP3_CORE1_IOPAD(0x20e8, PIN_OUTPUT | MUX_MODE0) /* dss_data6.dss_data6 */
+ OMAP3_CORE1_IOPAD(0x20ea, PIN_OUTPUT | MUX_MODE0) /* dss_data7.dss_data7 */
+ OMAP3_CORE1_IOPAD(0x20ec, PIN_OUTPUT | MUX_MODE0) /* dss_data8.dss_data8 */
+ OMAP3_CORE1_IOPAD(0x20ee, PIN_OUTPUT | MUX_MODE0) /* dss_data9.dss_data9 */
+ OMAP3_CORE1_IOPAD(0x20f0, PIN_OUTPUT | MUX_MODE0) /* dss_data10.dss_data10 */
+ OMAP3_CORE1_IOPAD(0x20f2, PIN_OUTPUT | MUX_MODE0) /* dss_data11.dss_data11 */
+ OMAP3_CORE1_IOPAD(0x20f4, PIN_OUTPUT | MUX_MODE0) /* dss_data12.dss_data12 */
+ OMAP3_CORE1_IOPAD(0x20f6, PIN_OUTPUT | MUX_MODE0) /* dss_data13.dss_data13 */
+ OMAP3_CORE1_IOPAD(0x20f8, PIN_OUTPUT | MUX_MODE0) /* dss_data14.dss_data14 */
+ OMAP3_CORE1_IOPAD(0x20fa, PIN_OUTPUT | MUX_MODE0) /* dss_data15.dss_data15 */
+ OMAP3_CORE1_IOPAD(0x20fc, PIN_OUTPUT | MUX_MODE0) /* dss_data16.dss_data16 */
+ OMAP3_CORE1_IOPAD(0x20fe, PIN_OUTPUT | MUX_MODE0) /* dss_data17.dss_data17 */
+ >;
+ };
+
+ mmc2_pins: pinmux_mmc2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2158, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_clk.sdmmc2_clk */
+ OMAP3_CORE1_IOPAD(0x215a, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_cmd.sdmmc2_cmd */
+ OMAP3_CORE1_IOPAD(0x215c, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat0.sdmmc2_dat0 */
+ OMAP3_CORE1_IOPAD(0x215e, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat1.sdmmc2_dat1 */
+ OMAP3_CORE1_IOPAD(0x2160, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat2.sdmmc2_dat2 */
+ OMAP3_CORE1_IOPAD(0x2162, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat3.sdmmc2_dat3 */
+ OMAP3_CORE1_IOPAD(0x2164, PIN_OUTPUT | MUX_MODE1) /* sdmmc2_dat4.sdmmc2_dir_dat0 */
+ OMAP3_CORE1_IOPAD(0x2166, PIN_OUTPUT | MUX_MODE1) /* sdmmc2_dat5.sdmmc2_dir_dat1 */
+ OMAP3_CORE1_IOPAD(0x2168, PIN_OUTPUT | MUX_MODE1) /* sdmmc2_dat6.sdmmc2_dir_cmd */
+ OMAP3_CORE1_IOPAD(0x216a, PIN_INPUT | MUX_MODE1) /* sdmmc2_dat7.sdmmc2_clkin */
+ OMAP3_CORE1_IOPAD(0x219a, PIN_INPUT_PULLUP | MUX_MODE4) /* uart3_cts_rctx.gpio_163 -> wp */
+ OMAP3_CORE1_IOPAD(0x219c, PIN_INPUT_PULLUP | MUX_MODE4) /* uart3_rts_sd.gpio_164 -> cd */
+ >;
+ };
+
+ spi1_pins: pinmux_spi1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21c8, PIN_INPUT | MUX_MODE0) /* mcspi1_clk.mcspi1_clk */
+ OMAP3_CORE1_IOPAD(0x21ca, PIN_INPUT | MUX_MODE0) /* mcspi1_simo.mcspi1_simo */
+ OMAP3_CORE1_IOPAD(0x21cc, PIN_INPUT | MUX_MODE0) /* mcspi1_somi.mcspi1_somi */
+ OMAP3_CORE1_IOPAD(0x21ce, PIN_INPUT_PULLDOWN | MUX_MODE0) /* mcspi1_cs0.mcspi1_cs0 */
+ >;
+ };
+};
+
+&gpio4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gpio4_pins>;
+};
+
+&gpio5 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gpio5_pins>;
+};
+
+&mmc2 {
+ status = "okay";
+ bus-width = <4>;
+ vmmc-supply = <&vmmc1>;
+ cd-gpios = <&gpio6 4 0>; /* gpio_164 */
+ wp-gpios = <&gpio6 3 0>; /* gpio_163 */
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc2_pins>;
+ ti,dual-volt;
+};
+
+&mcspi1 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi1_pins>;
+};
+
+&gpmc {
+ ranges = <0 0 0x30000000 0x1000000>, /* nand assigned by COM a83x */
+ <4 0 0x20000000 0x01000000>,
+ <7 0 0x15000000 0x01000000>; /* eth assigend by COM a83x */
+
+ ethernet@4,0 {
+ compatible = "smsc,lan9117", "smsc,lan9115";
+ bank-width = <2>;
+ gpmc,mux-add-data = <2>;
+ gpmc,cs-on-ns = <10>;
+ gpmc,cs-rd-off-ns = <65>;
+ gpmc,cs-wr-off-ns = <65>;
+ gpmc,adv-on-ns = <0>;
+ gpmc,adv-rd-off-ns = <10>;
+ gpmc,adv-wr-off-ns = <10>;
+ gpmc,oe-on-ns = <10>;
+ gpmc,oe-off-ns = <65>;
+ gpmc,we-on-ns = <10>;
+ gpmc,we-off-ns = <65>;
+ gpmc,rd-cycle-ns = <100>;
+ gpmc,wr-cycle-ns = <100>;
+ gpmc,access-ns = <60>;
+ gpmc,page-burst-access-ns = <5>;
+ gpmc,bus-turnaround-ns = <0>;
+ gpmc,cycle2cycle-delay-ns = <75>;
+ gpmc,wr-data-mux-bus-ns = <15>;
+ gpmc,wr-access-ns = <75>;
+ gpmc,cycle2cycle-samecsen;
+ gpmc,cycle2cycle-diffcsen;
+ vddvario-supply = <&reg_vcc3>;
+ vdd33a-supply = <&reg_vcc3>;
+ reg-io-width = <4>;
+ interrupt-parent = <&gpio4>;
+ interrupts = <2 0x2>;
+ reg = <4 0 0xff>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&lan9117_pins>;
+ phy-mode = "mii";
+ smsc,force-internal-phy;
+ };
+};
diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts
index 0bf40c90faba..1a57b61f5e24 100644
--- a/arch/arm/boot/dts/omap3-n900.dts
+++ b/arch/arm/boot/dts/omap3-n900.dts
@@ -74,6 +74,22 @@
};
};
+ isp1704: isp1704 {
+ compatible = "nxp,isp1704";
+ nxp,enable-gpio = <&gpio3 3 GPIO_ACTIVE_HIGH>;
+ usb-phy = <&usb2_phy>;
+ };
+
+ tv: connector {
+ compatible = "composite-connector";
+ label = "tv";
+
+ port {
+ tv_connector_in: endpoint {
+ remote-endpoint = <&venc_out>;
+ };
+ };
+ };
};
&omap3_pmx_core {
@@ -140,11 +156,23 @@
>;
};
- display_pins: pinmux_display_pins {
+ acx565akm_pins: pinmux_acx565akm_pins {
pinctrl-single,pins = <
0x0d4 (PIN_OUTPUT | MUX_MODE4) /* RX51_LCD_RESET_GPIO */
>;
};
+
+ dss_sdi_pins: pinmux_dss_sdi_pins {
+ pinctrl-single,pins = <
+ 0x0c0 (PIN_OUTPUT | MUX_MODE1) /* dss_data10.sdi_dat1n */
+ 0x0c2 (PIN_OUTPUT | MUX_MODE1) /* dss_data11.sdi_dat1p */
+ 0x0c4 (PIN_OUTPUT | MUX_MODE1) /* dss_data12.sdi_dat2n */
+ 0x0c6 (PIN_OUTPUT | MUX_MODE1) /* dss_data13.sdi_dat2p */
+
+ 0x0d8 (PIN_OUTPUT | MUX_MODE1) /* dss_data22.sdi_clkp */
+ 0x0da (PIN_OUTPUT | MUX_MODE1) /* dss_data23.sdi_clkn */
+ >;
+ };
};
&i2c1 {
@@ -254,6 +282,61 @@
};
};
+&twl_keypad {
+ linux,keymap = < 0x00000010 /* KEY_Q */
+ 0x00010018 /* KEY_O */
+ 0x00020019 /* KEY_P */
+ 0x00030033 /* KEY_COMMA */
+ 0x0004000e /* KEY_BACKSPACE */
+ 0x0006001e /* KEY_A */
+ 0x0007001f /* KEY_S */
+
+ 0x01000011 /* KEY_W */
+ 0x01010020 /* KEY_D */
+ 0x01020021 /* KEY_F */
+ 0x01030022 /* KEY_G */
+ 0x01040023 /* KEY_H */
+ 0x01050024 /* KEY_J */
+ 0x01060025 /* KEY_K */
+ 0x01070026 /* KEY_L */
+
+ 0x02000012 /* KEY_E */
+ 0x02010034 /* KEY_DOT */
+ 0x02020067 /* KEY_UP */
+ 0x0203001c /* KEY_ENTER */
+ 0x0205002c /* KEY_Z */
+ 0x0206002d /* KEY_X */
+ 0x0207002e /* KEY_C */
+ 0x02080043 /* KEY_F9 */
+
+ 0x03000013 /* KEY_R */
+ 0x0301002f /* KEY_V */
+ 0x03020030 /* KEY_B */
+ 0x03030031 /* KEY_N */
+ 0x03040032 /* KEY_M */
+ 0x03050039 /* KEY_SPACE */
+ 0x03060039 /* KEY_SPACE */
+ 0x03070069 /* KEY_LEFT */
+
+ 0x04000014 /* KEY_T */
+ 0x0401006c /* KEY_DOWN */
+ 0x0402006a /* KEY_RIGHT */
+ 0x0404001d /* KEY_LEFTCTRL */
+ 0x04050064 /* KEY_RIGHTALT */
+ 0x0406002a /* KEY_LEFTSHIFT */
+ 0x04080044 /* KEY_F10 */
+
+ 0x05000015 /* KEY_Y */
+ 0x05080057 /* KEY_F11 */
+
+ 0x06000016 /* KEY_U */
+
+ 0x07000017 /* KEY_I */
+ 0x07010041 /* KEY_F7 */
+ 0x07020042 /* KEY_F8 */
+ >;
+};
+
&twl_gpio {
ti,pullups = <0x0>;
ti,pulldowns = <0x03ff3f>; /* BIT(0..5) | BIT(8..17) */
@@ -291,6 +374,13 @@
DVDD-supply = <&vio>;
};
+ tsl2563: tsl2563@29 {
+ compatible = "amstaos,tsl2563";
+ reg = <0x29>;
+
+ amstaos,cover-comp-gain = <16>;
+ };
+
lp5523: lp5523@32 {
compatible = "national,lp5523";
reg = <0x32>;
@@ -356,6 +446,29 @@
compatible = "ti,bq27200";
reg = <0x55>;
};
+
+ tpa6130a2: tpa6130a2@60 {
+ compatible = "ti,tpa6130a2";
+ reg = <0x60>;
+
+ Vdd-supply = <&vmmc2>;
+
+ power-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>; /* 98 */
+ };
+
+ bq24150a: bq24150a@6b {
+ compatible = "ti,bq24150a";
+ reg = <0x6b>;
+
+ ti,current-limit = <100>;
+ ti,weak-battery-voltage = <3400>;
+ ti,battery-regulation-voltage = <4200>;
+ ti,charge-current = <650>;
+ ti,termination-current = <100>;
+ ti,resistor-sense = <68>;
+
+ ti,usb-charger-detection = <&isp1704>;
+ };
};
&i2c3 {
@@ -471,13 +584,23 @@
spi-max-frequency = <6000000>;
reg = <0>;
};
- mipid@2 {
- compatible = "acx565akm";
+
+ acx565akm@2 {
+ compatible = "sony,acx565akm";
spi-max-frequency = <6000000>;
reg = <2>;
pinctrl-names = "default";
- pinctrl-0 = <&display_pins>;
+ pinctrl-0 = <&acx565akm_pins>;
+
+ label = "lcd";
+ reset-gpios = <&gpio3 26 GPIO_ACTIVE_HIGH>; /* 90 */
+
+ port {
+ lcd_in: endpoint {
+ remote-endpoint = <&sdi_out>;
+ };
+ };
};
};
@@ -503,3 +626,39 @@
pinctrl-names = "default";
pinctrl-0 = <&uart3_pins>;
};
+
+&dss {
+ status = "ok";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&dss_sdi_pins>;
+
+ vdds_sdi-supply = <&vaux1>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@1 {
+ reg = <1>;
+
+ sdi_out: endpoint {
+ remote-endpoint = <&lcd_in>;
+ datapairs = <2>;
+ };
+ };
+ };
+};
+
+&venc {
+ status = "ok";
+
+ vdda-supply = <&vdac>;
+
+ port {
+ venc_out: endpoint {
+ remote-endpoint = <&tv_connector_in>;
+ ti,channels = <1>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap3-overo-alto35-common.dtsi b/arch/arm/boot/dts/omap3-overo-alto35-common.dtsi
new file mode 100644
index 000000000000..19d64864a109
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-alto35-common.dtsi
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Alto35 expansion board is manufactured by Gumstix Inc.
+ */
+
+#include "omap3-overo-common-peripherals.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pins>;
+ gpio148 {
+ label = "overo:red:gpio148";
+ gpios = <&gpio5 20 GPIO_ACTIVE_HIGH>; /* gpio 148 */
+ };
+ gpio150 {
+ label = "overo:yellow:gpio150";
+ gpios = <&gpio5 22 GPIO_ACTIVE_HIGH>; /* gpio 150 */
+ };
+ gpio151 {
+ label = "overo:blue:gpio151";
+ gpios = <&gpio5 23 GPIO_ACTIVE_HIGH>; /* gpio 151 */
+ };
+ gpio170 {
+ label = "overo:green:gpio170";
+ gpios = <&gpio6 10 GPIO_ACTIVE_HIGH>; /* gpio 170 */
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&button_pins>;
+ button0@10 {
+ label = "button0";
+ linux,code = <BTN_0>;
+ gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; /* gpio_10 */
+ gpio-key,wakeup;
+ };
+ };
+};
+
+&omap3_pmx_core {
+ led_pins: pinmux_led_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x217c, PIN_OUTPUT | MUX_MODE4) /* uart1_tx.gpio_148 */
+ OMAP3_CORE1_IOPAD(0x2180, PIN_OUTPUT | MUX_MODE4) /* uart1_cts.gpio_150 */
+ OMAP3_CORE1_IOPAD(0x2182, PIN_OUTPUT | MUX_MODE4) /* uart1_rx.gpio_151 */
+ OMAP3_CORE1_IOPAD(0x21c6, PIN_OUTPUT | MUX_MODE4) /* hdq_sio.gpio_170 */
+ >;
+ };
+};
+
+&omap3_pmx_wkup {
+ button_pins: pinmux_button_pins {
+ pinctrl-single,pins = <
+ OMAP3_WKUP_IOPAD(0x2a18, PIN_INPUT | MUX_MODE4) /* sys_clkout1.gpio_10 */
+ >;
+ };
+};
+
+&usbhshost {
+ status = "disabled";
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-alto35.dts b/arch/arm/boot/dts/omap3-overo-alto35.dts
new file mode 100644
index 000000000000..a3249eb7501d
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-alto35.dts
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Alto35 expansion board is manufactured by Gumstix Inc.
+ */
+
+/dts-v1/;
+
+#include "omap3-overo.dtsi"
+#include "omap3-overo-alto35-common.dtsi"
+
+/ {
+ model = "OMAP35xx Gumstix Overo on Alto35";
+ compatible = "gumstix,omap3-overo-alto35", "gumstix,omap3-overo", "ti,omap3430", "ti,omap3";
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-base.dtsi b/arch/arm/boot/dts/omap3-overo-base.dtsi
new file mode 100644
index 000000000000..d36bf0250a05
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-base.dtsi
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2012 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * The Gumstix Overo must be combined with an expansion board.
+ */
+
+/ {
+ pwmleds {
+ compatible = "pwm-leds";
+
+ overo {
+ label = "overo:blue:COM";
+ pwms = <&twl_pwmled 1 7812500>;
+ max-brightness = <127>;
+ linux,default-trigger = "mmc0";
+ };
+ };
+
+ sound {
+ compatible = "ti,omap-twl4030";
+ ti,model = "overo";
+
+ ti,mcbsp = <&mcbsp2>;
+ ti,codec = <&twl_audio>;
+ };
+
+ /* HS USB Port 2 Power */
+ hsusb2_power: hsusb2_power_reg {
+ compatible = "regulator-fixed";
+ regulator-name = "hsusb2_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio6 8 0>; /* gpio_168: vbus enable */
+ startup-delay-us = <70000>;
+ enable-active-high;
+ };
+
+ /* HS USB Host PHY on PORT 2 */
+ hsusb2_phy: hsusb2_phy {
+ compatible = "usb-nop-xceiv";
+ reset-gpios = <&gpio6 23 GPIO_ACTIVE_LOW>; /* gpio_183 */
+ vcc-supply = <&hsusb2_power>;
+ };
+
+ /* Regulator to trigger the nPoweron signal of the Wifi module */
+ w3cbw003c_npoweron: regulator-w3cbw003c-npoweron {
+ compatible = "regulator-fixed";
+ regulator-name = "regulator-w3cbw003c-npoweron";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio2 22 GPIO_ACTIVE_HIGH>; /* gpio_54: nPoweron */
+ enable-active-high;
+ };
+
+ /* Regulator to trigger the nReset signal of the Wifi module */
+ w3cbw003c_wifi_nreset: regulator-w3cbw003c-wifi-nreset {
+ pinctrl-names = "default";
+ pinctrl-0 = <&w3cbw003c_pins &w3cbw003c_2_pins>;
+ compatible = "regulator-fixed";
+ regulator-name = "regulator-w3cbw003c-wifi-nreset";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio1 16 GPIO_ACTIVE_HIGH>; /* gpio_16: WiFi nReset */
+ startup-delay-us = <10000>;
+ };
+
+ /* Regulator to trigger the nReset signal of the Bluetooth module */
+ w3cbw003c_bt_nreset: regulator-w3cbw003c-bt-nreset {
+ compatible = "regulator-fixed";
+ regulator-name = "regulator-w3cbw003c-bt-nreset";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio6 4 GPIO_ACTIVE_HIGH>; /* gpio_164: BT nReset */
+ startup-delay-us = <10000>;
+ };
+};
+
+&omap3_pmx_core {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &hsusb2_pins
+ >;
+
+ uart2_pins: pinmux_uart2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x216c, PIN_INPUT | MUX_MODE1) /* mcbsp3_dx.uart2_cts */
+ OMAP3_CORE1_IOPAD(0x216e, PIN_OUTPUT | MUX_MODE1) /* mcbsp3_dr.uart2_rts */
+ OMAP3_CORE1_IOPAD(0x2170, PIN_OUTPUT | MUX_MODE1) /* mcbsp3_clk.uart2_tx */
+ OMAP3_CORE1_IOPAD(0x2172, PIN_INPUT | MUX_MODE1) /* mcbsp3_fsx.uart2_rx */
+ >;
+ };
+
+ i2c1_pins: pinmux_i2c1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21ba, PIN_INPUT | MUX_MODE0) /* i2c1_scl.i2c1_scl */
+ OMAP3_CORE1_IOPAD(0x21bc, PIN_INPUT | MUX_MODE0) /* i2c1_sda.i2c1_sda */
+ >;
+ };
+
+ mmc1_pins: pinmux_mmc1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2144, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_clk.sdmmc1_clk */
+ OMAP3_CORE1_IOPAD(0x2146, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_cmd.sdmmc1_cmd */
+ OMAP3_CORE1_IOPAD(0x2148, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat0.sdmmc1_dat0 */
+ OMAP3_CORE1_IOPAD(0x214a, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat1.sdmmc1_dat1 */
+ OMAP3_CORE1_IOPAD(0x214c, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat2.sdmmc1_dat2 */
+ OMAP3_CORE1_IOPAD(0x214e, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat3.sdmmc1_dat3 */
+ >;
+ };
+
+ mmc2_pins: pinmux_mmc2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2158, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_clk.sdmmc2_clk */
+ OMAP3_CORE1_IOPAD(0x215a, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_cmd.sdmmc2_cmd */
+ OMAP3_CORE1_IOPAD(0x215c, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat0.sdmmc2_dat0 */
+ OMAP3_CORE1_IOPAD(0x215e, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat1.sdmmc2_dat1 */
+ OMAP3_CORE1_IOPAD(0x2160, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat2.sdmmc2_dat2 */
+ OMAP3_CORE1_IOPAD(0x2162, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat3.sdmmc2_dat3 */
+ >;
+ };
+
+ /* WiFi/BT combo */
+ w3cbw003c_pins: pinmux_w3cbw003c_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x20b4, PIN_OUTPUT | MUX_MODE4) /* gpmc_ncs3.gpio_54 */
+ OMAP3_CORE1_IOPAD(0x219c, PIN_OUTPUT | MUX_MODE4) /* uart3_rts_sd.gpio_164 */
+ >;
+ };
+
+ hsusb2_pins: pinmux_hsusb2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21d4, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi1_cs3.hsusb2_data2 */
+ OMAP3_CORE1_IOPAD(0x21d6, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_clk.hsusb2_data7 */
+ OMAP3_CORE1_IOPAD(0x21d8, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_simo.hsusb2_data4 */
+ OMAP3_CORE1_IOPAD(0x21da, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_somi.hsusb2_data5 */
+ OMAP3_CORE1_IOPAD(0x21dc, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_cs0.hsusb2_data6 */
+ OMAP3_CORE1_IOPAD(0x21de, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_cs1.hsusb2_data3 */
+ OMAP3_CORE1_IOPAD(0x21be, PIN_OUTPUT | MUX_MODE4) /* i2c2_scl.gpio_168 */
+ OMAP3_CORE1_IOPAD(0x21c0, PIN_OUTPUT | MUX_MODE4) /* i2c2_sda.gpio_183 */
+ >;
+ };
+};
+
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+ clock-frequency = <2600000>;
+
+ twl: twl@48 {
+ reg = <0x48>;
+ interrupts = <7>; /* SYS_NIRQ cascaded to intc */
+ interrupt-parent = <&intc>;
+
+ twl_audio: audio {
+ compatible = "ti,twl4030-audio";
+ codec {
+ };
+ };
+ };
+};
+
+#include "twl4030.dtsi"
+#include "twl4030_omap3.dtsi"
+
+/* i2c2 pins are used for gpio */
+&i2c2 {
+ status = "disabled";
+};
+
+/* on board microSD slot */
+&mmc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
+ vmmc-supply = <&vmmc1>;
+ bus-width = <4>;
+};
+
+/* optional on board WiFi */
+&mmc2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc2_pins>;
+ vmmc-supply = <&w3cbw003c_npoweron>;
+ vqmmc-supply = <&w3cbw003c_bt_nreset>;
+ vmmc_aux-supply = <&w3cbw003c_wifi_nreset>;
+ bus-width = <4>;
+ cap-sdio-irq;
+ non-removable;
+};
+
+&twl_gpio {
+ ti,use-leds;
+};
+
+&usb_otg_hs {
+ interface-type = <0>;
+ usb-phy = <&usb2_phy>;
+ phys = <&usb2_phy>;
+ phy-names = "usb2-phy";
+ mode = <3>;
+ power = <50>;
+};
+
+&usbhshost {
+ port2-mode = "ehci-phy";
+};
+
+&usbhsehci {
+ phys = <0 &hsusb2_phy>;
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_pins>;
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-chestnut43-common.dtsi b/arch/arm/boot/dts/omap3-overo-chestnut43-common.dtsi
new file mode 100644
index 000000000000..19de6ff79686
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-chestnut43-common.dtsi
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Chestnut43 expansion board is manufactured by Gumstix Inc.
+ */
+
+#include "omap3-overo-common-peripherals.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pins>;
+ heartbeat {
+ label = "overo:red:gpio21";
+ gpios = <&gpio1 21 GPIO_ACTIVE_LOW>; /* gpio_21 */
+ linux,default-trigger = "heartbeat";
+ };
+ gpio22 {
+ label = "overo:blue:gpio22";
+ gpios = <&gpio1 22 GPIO_ACTIVE_LOW>; /* gpio_22 */
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&button_pins>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ button0@23 {
+ label = "button0";
+ linux,code = <BTN_0>;
+ gpios = <&gpio1 23 GPIO_ACTIVE_LOW>; /* gpio_23 */
+ gpio-key,wakeup;
+ };
+ button1@14 {
+ label = "button1";
+ linux,code = <BTN_1>;
+ gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; /* gpio_14 */
+ gpio-key,wakeup;
+ };
+ };
+};
+
+#include "omap-gpmc-smsc9221.dtsi"
+
+&gpmc {
+ ranges = <5 0 0x2c000000 0x1000000>; /* CS5 */
+
+ ethernet@gpmc {
+ reg = <5 0 0xff>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <16 IRQ_TYPE_LEVEL_LOW>; /* GPIO 176 */
+ };
+};
+
+&lis33de {
+ status = "disabled";
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-chestnut43.dts b/arch/arm/boot/dts/omap3-overo-chestnut43.dts
new file mode 100644
index 000000000000..fe0824aca3c0
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-chestnut43.dts
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Chestnut43 expansion board is manufactured by Gumstix Inc.
+ */
+
+/dts-v1/;
+
+#include "omap3-overo.dtsi"
+#include "omap3-overo-chestnut43-common.dtsi"
+
+/ {
+ model = "OMAP35xx Gumstix Overo on Chestnut43";
+ compatible = "gumstix,omap3-overo-chestnut43", "gumstix,omap3-overo", "ti,omap3430", "ti,omap3";
+};
+
+&omap3_pmx_core2 {
+ led_pins: pinmux_led_pins {
+ pinctrl-single,pins = <
+ OMAP3430_CORE2_IOPAD(0x25ea, PIN_OUTPUT | MUX_MODE4) /* etk_d7.gpio_21 */
+ OMAP3430_CORE2_IOPAD(0x25ec, PIN_OUTPUT | MUX_MODE4) /* etk_d8.gpio_22 */
+ >;
+ };
+
+ button_pins: pinmux_button_pins {
+ pinctrl-single,pins = <
+ OMAP3430_CORE2_IOPAD(0x25ee, PIN_INPUT | MUX_MODE4) /* etk_d9.gpio_23 */
+ OMAP3430_CORE2_IOPAD(0x25dc, PIN_INPUT | MUX_MODE4) /* etk_d0.gpio_14 */
+ >;
+ };
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-common-peripherals.dtsi b/arch/arm/boot/dts/omap3-overo-common-peripherals.dtsi
new file mode 100644
index 000000000000..5831bcc52966
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-common-peripherals.dtsi
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Peripherals common to all Gumstix Overo boards (Tobi, Summit, Palo43,...)
+ */
+
+/ {
+ lis33_3v3: lis33-3v3-reg {
+ compatible = "regulator-fixed";
+ regulator-name = "lis33-3v3-reg";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ lis33_1v8: lis33-1v8-reg {
+ compatible = "regulator-fixed";
+ regulator-name = "lis33-1v8-reg";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+};
+
+&omap3_pmx_core {
+ i2c3_pins: pinmux_i2c3_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21c2, PIN_INPUT | MUX_MODE0) /* i2c3_scl.i2c3_scl */
+ OMAP3_CORE1_IOPAD(0x21c4, PIN_INPUT | MUX_MODE0) /* i2c3_sda.i2c3_sda */
+ >;
+ };
+
+ uart3_pins: pinmux_uart3_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x219e, PIN_INPUT | PIN_OFF_WAKEUPENABLE | MUX_MODE0) /* uart3_rx_irrx.uart3_rx_irrx */
+ OMAP3_CORE1_IOPAD(0x21a0, PIN_OUTPUT | MUX_MODE0) /* uart3_tx_irtx.uart3_tx_irtx */
+ >;
+ };
+};
+
+&i2c3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3_pins>;
+ clock-frequency = <100000>;
+
+ /* optional 1K EEPROM with revision information */
+ eeprom@51 {
+ compatible = "atmel,24c01";
+ reg = <0x51>;
+ pagesize = <8>;
+ };
+
+ lis33de: lis33de@1d {
+ compatible = "st,lis33de", "st,lis3lv02d";
+ reg = <0x1d>;
+ Vdd-supply = <&lis33_1v8>;
+ Vdd_IO-supply = <&lis33_3v3>;
+
+ st,click-single-x;
+ st,click-single-y;
+ st,click-single-z;
+ st,click-thresh-x = <10>;
+ st,click-thresh-y = <10>;
+ st,click-thresh-z = <10>;
+ st,irq1-click;
+ st,irq2-click;
+ st,wakeup-x-lo;
+ st,wakeup-x-hi;
+ st,wakeup-y-lo;
+ st,wakeup-y-hi;
+ st,wakeup-z-lo;
+ st,wakeup-z-hi;
+ st,min-limit-x = <120>;
+ st,min-limit-y = <120>;
+ st,min-limit-z = <140>;
+ st,max-limit-x = <550>;
+ st,max-limit-y = <550>;
+ st,max-limit-z = <750>;
+ };
+};
+
+&mmc3 {
+ status = "disabled";
+};
+
+&uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart3_pins>;
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-gallop43-common.dtsi b/arch/arm/boot/dts/omap3-overo-gallop43-common.dtsi
new file mode 100644
index 000000000000..5e848c26986b
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-gallop43-common.dtsi
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Gallop43 expansion board is manufactured by Gumstix Inc.
+ */
+
+#include "omap3-overo-common-peripherals.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pins>;
+ heartbeat {
+ label = "overo:red:gpio21";
+ gpios = <&gpio1 21 GPIO_ACTIVE_LOW>; /* gpio_21 */
+ linux,default-trigger = "heartbeat";
+ };
+ gpio22 {
+ label = "overo:blue:gpio22";
+ gpios = <&gpio1 22 GPIO_ACTIVE_LOW>; /* gpio_22 */
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&button_pins>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ button0@23 {
+ label = "button0";
+ linux,code = <BTN_0>;
+ gpios = <&gpio1 23 GPIO_ACTIVE_LOW>; /* gpio_23 */
+ gpio-key,wakeup;
+ };
+ button1@14 {
+ label = "button1";
+ linux,code = <BTN_1>;
+ gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; /* gpio_14 */
+ gpio-key,wakeup;
+ };
+ };
+};
+
+&usbhshost {
+ status = "disabled";
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-gallop43.dts b/arch/arm/boot/dts/omap3-overo-gallop43.dts
new file mode 100644
index 000000000000..241f5c1914e0
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-gallop43.dts
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Gallop43 expansion board is manufactured by Gumstix Inc.
+ */
+
+/dts-v1/;
+
+#include "omap3-overo.dtsi"
+#include "omap3-overo-gallop43-common.dtsi"
+
+/ {
+ model = "OMAP35xx Gumstix Overo on Gallop43";
+ compatible = "gumstix,omap3-overo-gallop43", "gumstix,omap3-overo", "ti,omap3430", "ti,omap3";
+};
+
+&omap3_pmx_core2 {
+ led_pins: pinmux_led_pins {
+ pinctrl-single,pins = <
+ OMAP3430_CORE2_IOPAD(0x25ea, PIN_OUTPUT | MUX_MODE4) /* etk_d7.gpio_21 */
+ OMAP3430_CORE2_IOPAD(0x25ec, PIN_OUTPUT | MUX_MODE4) /* etk_d8.gpio_22 */
+ >;
+ };
+
+ button_pins: pinmux_button_pins {
+ pinctrl-single,pins = <
+ OMAP3430_CORE2_IOPAD(0x25ee, PIN_INPUT | MUX_MODE4) /* etk_d9.gpio_23 */
+ OMAP3430_CORE2_IOPAD(0x25dc, PIN_INPUT | MUX_MODE4) /* etk_d0.gpio_14 */
+ >;
+ };
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-palo43-common.dtsi b/arch/arm/boot/dts/omap3-overo-palo43-common.dtsi
new file mode 100644
index 000000000000..abea232825b9
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-palo43-common.dtsi
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Palo43 expansion board is manufactured by Gumstix Inc.
+ */
+
+#include "omap3-overo-common-peripherals.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pins>;
+ heartbeat {
+ label = "overo:red:gpio21";
+ gpios = <&gpio1 21 GPIO_ACTIVE_LOW>; /* gpio_21 */
+ linux,default-trigger = "heartbeat";
+ };
+ gpio22 {
+ label = "overo:blue:gpio22";
+ gpios = <&gpio1 22 GPIO_ACTIVE_LOW>; /* gpio_22 */
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&button_pins>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ button0@23 {
+ label = "button0";
+ linux,code = <BTN_0>;
+ gpios = <&gpio1 23 GPIO_ACTIVE_LOW>; /* gpio_23 */
+ gpio-key,wakeup;
+ };
+ button1@14 {
+ label = "button1";
+ linux,code = <BTN_1>;
+ gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; /* gpio_14 */
+ gpio-key,wakeup;
+ };
+ };
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-palo43.dts b/arch/arm/boot/dts/omap3-overo-palo43.dts
new file mode 100644
index 000000000000..cedb103b4b66
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-palo43.dts
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Palo43 expansion board is manufactured by Gumstix Inc.
+ */
+
+/dts-v1/;
+
+#include "omap3-overo.dtsi"
+#include "omap3-overo-palo43-common.dtsi"
+
+/ {
+ model = "OMAP35xx Gumstix Overo on Palo43";
+ compatible = "gumstix,omap3-overo-palo43", "gumstix,omap3-overo", "ti,omap3430", "ti,omap3";
+};
+
+&omap3_pmx_core2 {
+ led_pins: pinmux_led_pins {
+ pinctrl-single,pins = <
+ OMAP3430_CORE2_IOPAD(0x25ea, PIN_OUTPUT | MUX_MODE4) /* etk_d7.gpio_21 */
+ OMAP3430_CORE2_IOPAD(0x25ec, PIN_OUTPUT | MUX_MODE4) /* etk_d8.gpio_22 */
+ >;
+ };
+
+ button_pins: pinmux_button_pins {
+ pinctrl-single,pins = <
+ OMAP3430_CORE2_IOPAD(0x25ee, PIN_INPUT | MUX_MODE4) /* etk_d9.gpio_23 */
+ OMAP3430_CORE2_IOPAD(0x25dc, PIN_INPUT | MUX_MODE4) /* etk_d0.gpio_14 */
+ >;
+ };
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-storm-alto35.dts b/arch/arm/boot/dts/omap3-overo-storm-alto35.dts
new file mode 100644
index 000000000000..e9cae52afc25
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-storm-alto35.dts
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Alto35 expansion board is manufactured by Gumstix Inc.
+ */
+
+/dts-v1/;
+
+#include "omap3-overo-storm.dtsi"
+#include "omap3-overo-alto35-common.dtsi"
+
+/ {
+ model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Alto35";
+ compatible = "gumstix,omap3-overo-alto35", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3";
+};
diff --git a/arch/arm/boot/dts/omap3-overo-storm-chestnut43.dts b/arch/arm/boot/dts/omap3-overo-storm-chestnut43.dts
new file mode 100644
index 000000000000..7d82fdfd9909
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-storm-chestnut43.dts
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Chestnut43 expansion board is manufactured by Gumstix Inc.
+ */
+
+/dts-v1/;
+
+#include "omap3-overo-storm.dtsi"
+#include "omap3-overo-chestnut43-common.dtsi"
+
+/ {
+ model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Chestnut43";
+ compatible = "gumstix,omap3-overo-chestnut43", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3";
+};
+
+&omap3_pmx_core2 {
+ led_pins: pinmux_led_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25ea, PIN_OUTPUT | MUX_MODE4) /* etk_d7.gpio_21 */
+ OMAP3630_CORE2_IOPAD(0x25ec, PIN_OUTPUT | MUX_MODE4) /* etk_d8.gpio_22 */
+ >;
+ };
+
+ button_pins: pinmux_button_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25ee, PIN_INPUT | MUX_MODE4) /* etk_d9.gpio_23 */
+ OMAP3630_CORE2_IOPAD(0x25dc, PIN_INPUT | MUX_MODE4) /* etk_d0.gpio_14 */
+ >;
+ };
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-storm-gallop43.dts b/arch/arm/boot/dts/omap3-overo-storm-gallop43.dts
new file mode 100644
index 000000000000..a1b57e0cf37f
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-storm-gallop43.dts
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Gallop43 expansion board is manufactured by Gumstix Inc.
+ */
+
+/dts-v1/;
+
+#include "omap3-overo-storm.dtsi"
+#include "omap3-overo-gallop43-common.dtsi"
+
+/ {
+ model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Gallop43";
+ compatible = "gumstix,omap3-overo-gallop43", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3";
+};
+
+&omap3_pmx_core2 {
+ led_pins: pinmux_led_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25ea, PIN_OUTPUT | MUX_MODE4) /* etk_d7.gpio_21 */
+ OMAP3630_CORE2_IOPAD(0x25ec, PIN_OUTPUT | MUX_MODE4) /* etk_d8.gpio_22 */
+ >;
+ };
+
+ button_pins: pinmux_button_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25ee, PIN_INPUT | MUX_MODE4) /* etk_d9.gpio_23 */
+ OMAP3630_CORE2_IOPAD(0x25dc, PIN_INPUT | MUX_MODE4) /* etk_d0.gpio_14 */
+ >;
+ };
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-storm-palo43.dts b/arch/arm/boot/dts/omap3-overo-storm-palo43.dts
new file mode 100644
index 000000000000..b585d8fbc347
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-storm-palo43.dts
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Palo43 expansion board is manufactured by Gumstix Inc.
+ */
+
+/dts-v1/;
+
+#include "omap3-overo-storm.dtsi"
+#include "omap3-overo-palo43-common.dtsi"
+
+/ {
+ model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Palo43";
+ compatible = "gumstix,omap3-overo-palo43", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3";
+};
+
+&omap3_pmx_core2 {
+ led_pins: pinmux_led_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25ea, PIN_OUTPUT | MUX_MODE4) /* etk_d7.gpio_21 */
+ OMAP3630_CORE2_IOPAD(0x25ec, PIN_OUTPUT | MUX_MODE4) /* etk_d8.gpio_22 */
+ >;
+ };
+
+ button_pins: pinmux_button_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25ee, PIN_INPUT | MUX_MODE4) /* etk_d9.gpio_23 */
+ OMAP3630_CORE2_IOPAD(0x25dc, PIN_INPUT | MUX_MODE4) /* etk_d0.gpio_14 */
+ >;
+ };
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-storm-summit.dts b/arch/arm/boot/dts/omap3-overo-storm-summit.dts
new file mode 100644
index 000000000000..a0d7fd8369d7
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-storm-summit.dts
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Summit expansion board is manufactured by Gumstix Inc.
+ */
+
+/dts-v1/;
+
+#include "omap3-overo-storm.dtsi"
+#include "omap3-overo-summit-common.dtsi"
+
+/ {
+ model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Summit";
+ compatible = "gumstix,omap3-overo-summit", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3";
+};
+
+&omap3_pmx_core2 {
+ led_pins: pinmux_led_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25ea, PIN_OUTPUT | MUX_MODE4) /* etk_d7.gpio_21 */
+ >;
+ };
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-storm-tobi.dts b/arch/arm/boot/dts/omap3-overo-storm-tobi.dts
index 966b5c9cd96a..879383acad87 100644
--- a/arch/arm/boot/dts/omap3-overo-storm-tobi.dts
+++ b/arch/arm/boot/dts/omap3-overo-storm-tobi.dts
@@ -12,7 +12,7 @@
/dts-v1/;
-#include "omap36xx.dtsi"
+#include "omap3-overo-storm.dtsi"
#include "omap3-overo-tobi-common.dtsi"
/ {
diff --git a/arch/arm/boot/dts/omap3-overo-storm.dtsi b/arch/arm/boot/dts/omap3-overo-storm.dtsi
new file mode 100644
index 000000000000..6cb418b4124a
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-storm.dtsi
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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 "omap36xx.dtsi"
+#include "omap3-overo-base.dtsi"
+
+&omap3_pmx_core2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &hsusb2_2_pins
+ >;
+
+ hsusb2_2_pins: pinmux_hsusb2_2_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25f0, PIN_OUTPUT | MUX_MODE3) /* etk_d10.hsusb2_clk */
+ OMAP3630_CORE2_IOPAD(0x25f2, PIN_OUTPUT | MUX_MODE3) /* etk_d11.hsusb2_stp */
+ OMAP3630_CORE2_IOPAD(0x25f4, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d12.hsusb2_dir */
+ OMAP3630_CORE2_IOPAD(0x25f6, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d13.hsusb2_nxt */
+ OMAP3630_CORE2_IOPAD(0x25f8, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d14.hsusb2_data0 */
+ OMAP3630_CORE2_IOPAD(0x25fa, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d15.hsusb2_data1 */
+ >;
+ };
+
+ w3cbw003c_2_pins: pinmux_w3cbw003c_2_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25e0, PIN_OUTPUT | MUX_MODE4) /* etk_d2.gpio_16 */
+ >;
+ };
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-summit-common.dtsi b/arch/arm/boot/dts/omap3-overo-summit-common.dtsi
new file mode 100644
index 000000000000..999d1cd4a09f
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-summit-common.dtsi
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Summit expansion board is manufactured by Gumstix Inc.
+ */
+
+#include "omap3-overo-common-peripherals.dtsi"
+
+/ {
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pins>;
+ heartbeat {
+ label = "overo:red:gpio21";
+ gpios = <&gpio1 21 GPIO_ACTIVE_LOW>; /* gpio_21 */
+ linux,default-trigger = "heartbeat";
+ };
+ };
+};
+
+&lis33de {
+ status = "disabled";
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-summit.dts b/arch/arm/boot/dts/omap3-overo-summit.dts
new file mode 100644
index 000000000000..69765609455a
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-overo-summit.dts
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+
+/*
+ * Summit expansion board is manufactured by Gumstix Inc.
+ */
+
+/dts-v1/;
+
+#include "omap3-overo.dtsi"
+#include "omap3-overo-summit-common.dtsi"
+
+/ {
+ model = "OMAP35xx Gumstix Overo on Summit";
+ compatible = "gumstix,omap3-overo-summit", "gumstix,omap3-overo", "ti,omap3430", "ti,omap3";
+};
+
+&omap3_pmx_core2 {
+ led_pins: pinmux_led_pins {
+ pinctrl-single,pins = <
+ OMAP3430_CORE2_IOPAD(0x25ea, PIN_OUTPUT | MUX_MODE4) /* etk_d7.gpio_21 */
+ >;
+ };
+};
+
diff --git a/arch/arm/boot/dts/omap3-overo-tobi-common.dtsi b/arch/arm/boot/dts/omap3-overo-tobi-common.dtsi
index 4edc013a91c1..13df50b39442 100644
--- a/arch/arm/boot/dts/omap3-overo-tobi-common.dtsi
+++ b/arch/arm/boot/dts/omap3-overo-tobi-common.dtsi
@@ -10,7 +10,7 @@
* Tobi expansion board is manufactured by Gumstix Inc.
*/
-#include "omap3-overo.dtsi"
+#include "omap3-overo-common-peripherals.dtsi"
/ {
leds {
@@ -21,60 +21,21 @@
linux,default-trigger = "heartbeat";
};
};
-
- vddvario: regulator-vddvario {
- compatible = "regulator-fixed";
- regulator-name = "vddvario";
- regulator-always-on;
- };
-
- vdd33a: regulator-vdd33a {
- compatible = "regulator-fixed";
- regulator-name = "vdd33a";
- regulator-always-on;
- };
};
+#include "omap-gpmc-smsc9221.dtsi"
+
&gpmc {
ranges = <5 0 0x2c000000 0x1000000>; /* CS5 */
- ethernet@5,0 {
- compatible = "smsc,lan9221", "smsc,lan9115";
+ ethernet@gpmc {
reg = <5 0 0xff>;
- bank-width = <2>;
-
- gpmc,mux-add-data;
- gpmc,cs-on-ns = <0>;
- gpmc,cs-rd-off-ns = <42>;
- gpmc,cs-wr-off-ns = <36>;
- gpmc,adv-on-ns = <6>;
- gpmc,adv-rd-off-ns = <12>;
- gpmc,adv-wr-off-ns = <12>;
- gpmc,oe-on-ns = <0>;
- gpmc,oe-off-ns = <42>;
- gpmc,we-on-ns = <0>;
- gpmc,we-off-ns = <36>;
- gpmc,rd-cycle-ns = <60>;
- gpmc,wr-cycle-ns = <54>;
- gpmc,access-ns = <36>;
- gpmc,page-burst-access-ns = <0>;
- gpmc,bus-turnaround-ns = <0>;
- gpmc,cycle2cycle-delay-ns = <0>;
- gpmc,wr-data-mux-bus-ns = <18>;
- gpmc,wr-access-ns = <42>;
- gpmc,cycle2cycle-samecsen;
- gpmc,cycle2cycle-diffcsen;
-
interrupt-parent = <&gpio6>;
interrupts = <16 IRQ_TYPE_LEVEL_LOW>; /* GPIO 176 */
- reg-io-width = <4>;
};
};
-&i2c3 {
- clock-frequency = <100000>;
-};
-
-&mmc3 {
+&lis33de {
status = "disabled";
};
+
diff --git a/arch/arm/boot/dts/omap3-overo-tobi.dts b/arch/arm/boot/dts/omap3-overo-tobi.dts
index de5653e1b5ca..fd6400efcdee 100644
--- a/arch/arm/boot/dts/omap3-overo-tobi.dts
+++ b/arch/arm/boot/dts/omap3-overo-tobi.dts
@@ -12,7 +12,7 @@
/dts-v1/;
-#include "omap34xx.dtsi"
+#include "omap3-overo.dtsi"
#include "omap3-overo-tobi-common.dtsi"
/ {
diff --git a/arch/arm/boot/dts/omap3-overo.dtsi b/arch/arm/boot/dts/omap3-overo.dtsi
index 597099907f8e..69ca7c45bca2 100644
--- a/arch/arm/boot/dts/omap3-overo.dtsi
+++ b/arch/arm/boot/dts/omap3-overo.dtsi
@@ -1,94 +1,38 @@
/*
- * Copyright (C) 2012 Florian Vaussard, EPFL Mobots group
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
*
* 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.
*/
-/*
- * The Gumstix Overo must be combined with an expansion board.
- */
+#include "omap34xx.dtsi"
+#include "omap3-overo-base.dtsi"
-/ {
- pwmleds {
- compatible = "pwm-leds";
-
- overo {
- label = "overo:blue:COM";
- pwms = <&twl_pwmled 1 7812500>;
- max-brightness = <127>;
- linux,default-trigger = "mmc0";
- };
- };
-
- sound {
- compatible = "ti,omap-twl4030";
- ti,model = "overo";
-
- ti,mcbsp = <&mcbsp2>;
- ti,codec = <&twl_audio>;
- };
-};
-
-&i2c1 {
- clock-frequency = <2600000>;
-
- twl: twl@48 {
- reg = <0x48>;
- interrupts = <7>; /* SYS_NIRQ cascaded to intc */
- interrupt-parent = <&intc>;
+&omap3_pmx_core2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &hsusb2_2_pins
+ >;
- twl_audio: audio {
- compatible = "ti,twl4030-audio";
- codec {
- };
- };
+ hsusb2_2_pins: pinmux_hsusb2_2_pins {
+ pinctrl-single,pins = <
+ OMAP3430_CORE2_IOPAD(0x25f0, PIN_OUTPUT | MUX_MODE3) /* etk_d10.hsusb2_clk */
+ OMAP3430_CORE2_IOPAD(0x25f2, PIN_OUTPUT | MUX_MODE3) /* etk_d11.hsusb2_stp */
+ OMAP3430_CORE2_IOPAD(0x25f4, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d12.hsusb2_dir */
+ OMAP3430_CORE2_IOPAD(0x25f6, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d13.hsusb2_nxt */
+ OMAP3430_CORE2_IOPAD(0x25f8, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d14.hsusb2_data0 */
+ OMAP3430_CORE2_IOPAD(0x25fa, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d15.hsusb2_data1 */
+ >;
};
-};
-
-#include "twl4030.dtsi"
-#include "twl4030_omap3.dtsi"
-
-/* i2c2 pins are used for gpio */
-&i2c2 {
- status = "disabled";
-};
-/* on board microSD slot */
-&mmc1 {
- vmmc-supply = <&vmmc1>;
- bus-width = <4>;
-};
-
-/* optional on board WiFi */
-&mmc2 {
- bus-width = <4>;
-};
-
-&twl_gpio {
- ti,use-leds;
-};
-
-&usb_otg_hs {
- interface-type = <0>;
- usb-phy = <&usb2_phy>;
- phys = <&usb2_phy>;
- phy-names = "usb2-phy";
- mode = <3>;
- power = <50>;
-};
-
-&omap3_pmx_core {
- uart3_pins: pinmux_uart3_pins {
+ w3cbw003c_2_pins: pinmux_w3cbw003c_2_pins {
pinctrl-single,pins = <
- 0x16e (PIN_INPUT | PIN_OFF_WAKEUPENABLE | MUX_MODE0) /* uart3_rx_irrx.uart3_rx_irrx */
- 0x170 (PIN_OUTPUT | MUX_MODE0) /* uart3_tx_irtx.uart3_tx_irtx */
+ OMAP3430_CORE2_IOPAD(0x25e0, PIN_OUTPUT | MUX_MODE4) /* etk_d2.gpio_16 */
>;
};
};
-&uart3 {
- pinctrl-names = "default";
- pinctrl-0 = <&uart3_pins>;
+&mcbsp2 {
+ status = "okay";
};
diff --git a/arch/arm/boot/dts/omap3-sb-t35.dtsi b/arch/arm/boot/dts/omap3-sb-t35.dtsi
index b9a2fedce7ee..7909c51b05a5 100644
--- a/arch/arm/boot/dts/omap3-sb-t35.dtsi
+++ b/arch/arm/boot/dts/omap3-sb-t35.dtsi
@@ -2,11 +2,36 @@
* Common support for CompuLab SB-T35 used on SBC-T3530, SBC-T3517 and SBC-T3730
*/
+/ {
+ vddvario_sb_t35: regulator-vddvario-sb-t35 {
+ compatible = "regulator-fixed";
+ regulator-name = "vddvario";
+ regulator-always-on;
+ };
+
+ vdd33a_sb_t35: regulator-vdd33a-sb-t35 {
+ compatible = "regulator-fixed";
+ regulator-name = "vdd33a";
+ regulator-always-on;
+ };
+};
+
+&omap3_pmx_core {
+ smsc2_pins: pinmux_smsc2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x20b6, PIN_OUTPUT | MUX_MODE0) /* gpmc_ncs4.gpmc_ncs4 */
+ OMAP3_CORE1_IOPAD(0x20d2, PIN_INPUT_PULLUP | MUX_MODE4) /* gpmc_wait3.gpio_65 */
+ >;
+ };
+};
+
&gpmc {
ranges = <4 0 0x2d000000 0x01000000>;
smsc2: ethernet@4,0 {
compatible = "smsc,lan9221", "smsc,lan9115";
+ pinctrl-names = "default";
+ pinctrl-0 = <&smsc2_pins>;
interrupt-parent = <&gpio3>;
interrupts = <1 IRQ_TYPE_LEVEL_LOW>;
reg = <4 0 0xff>;
@@ -32,8 +57,8 @@
gpmc,wr-access-ns = <186>;
gpmc,cycle2cycle-samecsen;
gpmc,cycle2cycle-diffcsen;
- vddvario-supply = <&vddvario>;
- vdd33a-supply = <&vdd33a>;
+ vddvario-supply = <&vddvario_sb_t35>;
+ vdd33a-supply = <&vdd33a_sb_t35>;
reg-io-width = <4>;
smsc,save-mac-address;
};
diff --git a/arch/arm/boot/dts/omap3-sbc-t3517.dts b/arch/arm/boot/dts/omap3-sbc-t3517.dts
new file mode 100644
index 000000000000..024c9c6c682d
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-sbc-t3517.dts
@@ -0,0 +1,43 @@
+/*
+ * Suppport for CompuLab SBC-T3517 with CM-T3517
+ */
+
+#include "omap3-cm-t3517.dts"
+#include "omap3-sb-t35.dtsi"
+
+/ {
+ model = "CompuLab SBC-T3517 with CM-T3517";
+ compatible = "compulab,omap3-sbc-t3517", "compulab,omap3-cm-t3517", "ti,am3517", "ti,omap3";
+};
+
+&omap3_pmx_core {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &sb_t35_usb_hub_pins
+ &usb_hub_pins
+ >;
+
+ mmc1_aux_pins: pinmux_mmc1_aux_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x20c0, PIN_INPUT_PULLUP | MUX_MODE4) /* gpmc_clk.gpio_59 */
+ OMAP3_CORE1_IOPAD(0x2174, PIN_INPUT_PULLUP | MUX_MODE4) /* uart2_cts.gpio_144 */
+ >;
+ };
+
+ sb_t35_usb_hub_pins: pinmux_sb_t35_usb_hub_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21ec, PIN_OUTPUT | MUX_MODE4) /* ccdc_wen.gpio_98 - SB-T35 USB HUB RST */
+ >;
+ };
+};
+
+&mmc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &mmc1_pins
+ &mmc1_aux_pins
+ >;
+
+ wp-gpios = <&gpio2 27 GPIO_ACTIVE_HIGH>; /* gpio_59 */
+ cd-gpios = <&gpio5 16 GPIO_ACTIVE_HIGH>; /* gpio_144 */
+};
diff --git a/arch/arm/boot/dts/omap3-sbc-t3530.dts b/arch/arm/boot/dts/omap3-sbc-t3530.dts
new file mode 100644
index 000000000000..bbbeea6b1988
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-sbc-t3530.dts
@@ -0,0 +1,36 @@
+/*
+ * Suppport for CompuLab SBC-T3530 with CM-T3530
+ */
+
+#include "omap3-cm-t3530.dts"
+#include "omap3-sb-t35.dtsi"
+
+/ {
+ model = "CompuLab SBC-T3530 with CM-T3530";
+ compatible = "compulab,omap3-sbc-t3530", "compulab,omap3-cm-t3530", "ti,omap34xx", "ti,omap3";
+};
+
+&omap3_pmx_core {
+ pinctrl-names = "default";
+ pinctrl-0 = <&sb_t35_usb_hub_pins>;
+
+ sb_t35_usb_hub_pins: pinmux_sb_t35_usb_hub_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2130, PIN_OUTPUT | MUX_MODE4) /* ccdc_wen.gpio_167 - SB-T35 USB HUB RST */
+ >;
+ };
+};
+
+/*
+ * The following ranges correspond to SMSC9x eth chips on CM-T3530 CoM and
+ * SB-T35 baseboard respectively.
+ * This setting includes both chips in SBC-T3530 board device tree.
+ */
+&gpmc {
+ ranges = <5 0 0x2c000000 0x01000000>,
+ <4 0 0x2d000000 0x01000000>;
+};
+
+&mmc1 {
+ cd-gpios = <&twl_gpio 0 GPIO_ACTIVE_HIGH>;
+};
diff --git a/arch/arm/boot/dts/omap3-sbc-t3730.dts b/arch/arm/boot/dts/omap3-sbc-t3730.dts
index c119bd545053..08e4a7086f22 100644
--- a/arch/arm/boot/dts/omap3-sbc-t3730.dts
+++ b/arch/arm/boot/dts/omap3-sbc-t3730.dts
@@ -10,21 +10,18 @@
compatible = "compulab,omap3-sbc-t3730", "compulab,omap3-cm-t3730", "ti,omap36xx", "ti,omap3";
};
-&gpmc {
- ranges = <5 0 0x2c000000 0x01000000>,
- <4 0 0x2d000000 0x01000000>;
-};
-
-&smsc2 {
+&omap3_pmx_core {
pinctrl-names = "default";
- pinctrl-0 = <&smsc2_pins>;
-};
+ pinctrl-0 = <&sb_t35_usb_hub_pins>;
-&omap3_pmx_core {
- smsc2_pins: pinmux_smsc2_pins {
+ sb_t35_usb_hub_pins: pinmux_sb_t35_usb_hub_pins {
pinctrl-single,pins = <
- 0x86 (PIN_OUTPUT | MUX_MODE0) /* gpmc_ncs4.gpmc_ncs4 */
- 0xa2 (PIN_INPUT_PULLUP | MUX_MODE4) /* gpmc_wait3.gpio_65 */
+ OMAP3_CORE1_IOPAD(0x2130, PIN_OUTPUT | MUX_MODE4) /* ccdc_wen.gpio_167 - SB-T35 USB HUB RST */
>;
};
-}; \ No newline at end of file
+};
+
+&gpmc {
+ ranges = <5 0 0x2c000000 0x01000000>,
+ <4 0 0x2d000000 0x01000000>;
+};
diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi
index a5fc83b9c835..3d05eff67e25 100644
--- a/arch/arm/boot/dts/omap3.dtsi
+++ b/arch/arm/boot/dts/omap3.dtsi
@@ -35,6 +35,11 @@
compatible = "arm,cortex-a8";
device_type = "cpu";
reg = <0x0>;
+
+ clocks = <&dpll1_ck>;
+ clock-names = "cpu";
+
+ clock-latency = <300000>; /* From omap-cpufreq driver */
};
};
@@ -411,10 +416,19 @@
};
mmu_isp: mmu@480bd400 {
- compatible = "ti,omap3-mmu-isp";
- ti,hwmods = "mmu_isp";
+ compatible = "ti,omap2-iommu";
reg = <0x480bd400 0x80>;
- interrupts = <8>;
+ interrupts = <24>;
+ ti,hwmods = "mmu_isp";
+ ti,#tlb-entries = <8>;
+ };
+
+ mmu_iva: mmu@5d000000 {
+ compatible = "ti,omap2-iommu";
+ reg = <0x5d000000 0x80>;
+ interrupts = <28>;
+ ti,hwmods = "mmu_iva";
+ status = "disabled";
};
wdt2: wdt@48314000 {
@@ -436,6 +450,7 @@
dmas = <&sdma 31>,
<&sdma 32>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp2: mcbsp@49022000 {
@@ -453,6 +468,7 @@
dmas = <&sdma 33>,
<&sdma 34>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp3: mcbsp@49024000 {
@@ -470,6 +486,7 @@
dmas = <&sdma 17>,
<&sdma 18>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp4: mcbsp@49026000 {
@@ -485,6 +502,7 @@
dmas = <&sdma 19>,
<&sdma 20>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp5: mcbsp@48096000 {
@@ -500,6 +518,7 @@
dmas = <&sdma 21>,
<&sdma 22>;
dma-names = "tx", "rx";
+ status = "disabled";
};
sham: sham@480c3000 {
@@ -634,14 +653,14 @@
ranges;
usbhsohci: ohci@48064400 {
- compatible = "ti,ohci-omap3", "usb-ohci";
+ compatible = "ti,ohci-omap3";
reg = <0x48064400 0x400>;
interrupt-parent = <&intc>;
interrupts = <76>;
};
usbhsehci: ehci@48064800 {
- compatible = "ti,ehci-omap", "usb-ehci";
+ compatible = "ti,ehci-omap";
reg = <0x48064800 0x400>;
interrupt-parent = <&intc>;
interrupts = <77>;
@@ -669,6 +688,58 @@
num-eps = <16>;
ram-bits = <12>;
};
+
+ dss: dss@48050000 {
+ compatible = "ti,omap3-dss";
+ reg = <0x48050000 0x200>;
+ status = "disabled";
+ ti,hwmods = "dss_core";
+ clocks = <&dss1_alwon_fck>;
+ clock-names = "fck";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ dispc@48050400 {
+ compatible = "ti,omap3-dispc";
+ reg = <0x48050400 0x400>;
+ interrupts = <25>;
+ ti,hwmods = "dss_dispc";
+ clocks = <&dss1_alwon_fck>;
+ clock-names = "fck";
+ };
+
+ dsi: encoder@4804fc00 {
+ compatible = "ti,omap3-dsi";
+ reg = <0x4804fc00 0x200>,
+ <0x4804fe00 0x40>,
+ <0x4804ff00 0x20>;
+ reg-names = "proto", "phy", "pll";
+ interrupts = <25>;
+ status = "disabled";
+ ti,hwmods = "dss_dsi1";
+ clocks = <&dss1_alwon_fck>, <&dss2_alwon_fck>;
+ clock-names = "fck", "sys_clk";
+ };
+
+ rfbi: encoder@48050800 {
+ compatible = "ti,omap3-rfbi";
+ reg = <0x48050800 0x100>;
+ status = "disabled";
+ ti,hwmods = "dss_rfbi";
+ clocks = <&dss1_alwon_fck>, <&dss_ick>;
+ clock-names = "fck", "ick";
+ };
+
+ venc: encoder@48050c00 {
+ compatible = "ti,omap3-venc";
+ reg = <0x48050c00 0x100>;
+ status = "disabled";
+ ti,hwmods = "dss_venc";
+ clocks = <&dss_tv_fck>;
+ clock-names = "fck";
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/omap3430-sdp.dts b/arch/arm/boot/dts/omap3430-sdp.dts
index 281914ed0151..02f69f4a8fd3 100644
--- a/arch/arm/boot/dts/omap3430-sdp.dts
+++ b/arch/arm/boot/dts/omap3430-sdp.dts
@@ -34,6 +34,10 @@
&mmc1 {
vmmc-supply = <&vmmc1>;
vmmc_aux-supply = <&vsim>;
+ /*
+ * S6-3 must be in ON position for 8 bit mode to function
+ * Else, use 4 bit mode
+ */
bus-width = <8>;
};
@@ -103,9 +107,8 @@
#address-cells = <1>;
#size-cells = <1>;
reg = <1 0 0x08000000>;
+ ti,nand-ecc-opt = "ham1";
nand-bus-width = <8>;
-
- ti,nand-ecc-opt = "sw";
gpmc,cs-on-ns = <0>;
gpmc,cs-rd-off-ns = <36>;
gpmc,cs-wr-off-ns = <36>;
diff --git a/arch/arm/boot/dts/omap3430es1-clocks.dtsi b/arch/arm/boot/dts/omap3430es1-clocks.dtsi
index 02f6c7fabbec..4c22f3a7f813 100644
--- a/arch/arm/boot/dts/omap3430es1-clocks.dtsi
+++ b/arch/arm/boot/dts/omap3430es1-clocks.dtsi
@@ -82,16 +82,16 @@
ti,dividers = <0>, <1>, <2>, <3>, <4>, <0>, <6>, <0>, <8>;
};
- ssi_ssr_fck_3430es1: ssi_ssr_fck_3430es1 {
+ ssi_ssr_fck: ssi_ssr_fck_3430es1 {
#clock-cells = <0>;
compatible = "ti,composite-clock";
clocks = <&ssi_ssr_gate_fck_3430es1>, <&ssi_ssr_div_fck_3430es1>;
};
- ssi_sst_fck_3430es1: ssi_sst_fck_3430es1 {
+ ssi_sst_fck: ssi_sst_fck_3430es1 {
#clock-cells = <0>;
compatible = "fixed-factor-clock";
- clocks = <&ssi_ssr_fck_3430es1>;
+ clocks = <&ssi_ssr_fck>;
clock-mult = <1>;
clock-div = <2>;
};
@@ -120,7 +120,7 @@
clock-div = <1>;
};
- ssi_ick_3430es1: ssi_ick_3430es1 {
+ ssi_ick: ssi_ick_3430es1 {
#clock-cells = <0>;
compatible = "ti,omap3-no-wait-interface-clock";
clocks = <&ssi_l4_ick>;
@@ -152,7 +152,7 @@
clocks = <&usb_l4_gate_ick>, <&usb_l4_div_ick>;
};
- dss1_alwon_fck_3430es1: dss1_alwon_fck_3430es1 {
+ dss1_alwon_fck: dss1_alwon_fck_3430es1 {
#clock-cells = <0>;
compatible = "ti,gate-clock";
clocks = <&dpll4_m4x2_ck>;
@@ -161,7 +161,7 @@
ti,set-rate-parent;
};
- dss_ick_3430es1: dss_ick_3430es1 {
+ dss_ick: dss_ick_3430es1 {
#clock-cells = <0>;
compatible = "ti,omap3-no-wait-interface-clock";
clocks = <&l4_ick>;
@@ -184,7 +184,7 @@
dss_clkdm: dss_clkdm {
compatible = "ti,clockdomain";
clocks = <&dss_tv_fck>, <&dss_96m_fck>, <&dss2_alwon_fck>,
- <&dss1_alwon_fck_3430es1>, <&dss_ick_3430es1>;
+ <&dss1_alwon_fck>, <&dss_ick>;
};
d2d_clkdm: d2d_clkdm {
@@ -203,6 +203,6 @@
<&i2c1_ick>, <&uart2_ick>, <&uart1_ick>, <&gpt11_ick>,
<&gpt10_ick>, <&mcbsp5_ick>, <&mcbsp1_ick>,
<&omapctrl_ick>, <&aes2_ick>, <&sha12_ick>,
- <&fshostusb_fck>, <&fac_ick>, <&ssi_ick_3430es1>;
+ <&fshostusb_fck>, <&fac_ick>, <&ssi_ick>;
};
};
diff --git a/arch/arm/boot/dts/omap36xx-am35xx-omap3430es2plus-clocks.dtsi b/arch/arm/boot/dts/omap36xx-am35xx-omap3430es2plus-clocks.dtsi
index af9ae5346bf2..080fb3f4e429 100644
--- a/arch/arm/boot/dts/omap36xx-am35xx-omap3430es2plus-clocks.dtsi
+++ b/arch/arm/boot/dts/omap36xx-am35xx-omap3430es2plus-clocks.dtsi
@@ -160,7 +160,7 @@
ti,bit-shift = <30>;
};
- dss1_alwon_fck_3430es2: dss1_alwon_fck_3430es2 {
+ dss1_alwon_fck: dss1_alwon_fck_3430es2 {
#clock-cells = <0>;
compatible = "ti,dss-gate-clock";
clocks = <&dpll4_m4x2_ck>;
@@ -169,7 +169,7 @@
ti,set-rate-parent;
};
- dss_ick_3430es2: dss_ick_3430es2 {
+ dss_ick: dss_ick_3430es2 {
#clock-cells = <0>;
compatible = "ti,omap3-dss-interface-clock";
clocks = <&l4_ick>;
@@ -216,7 +216,7 @@
dss_clkdm: dss_clkdm {
compatible = "ti,clockdomain";
clocks = <&dss_tv_fck>, <&dss_96m_fck>, <&dss2_alwon_fck>,
- <&dss1_alwon_fck_3430es2>, <&dss_ick_3430es2>;
+ <&dss1_alwon_fck>, <&dss_ick>;
};
core_l4_clkdm: core_l4_clkdm {
diff --git a/arch/arm/boot/dts/omap36xx-clocks.dtsi b/arch/arm/boot/dts/omap36xx-clocks.dtsi
index 2fcf253b677c..6b5280d04a0e 100644
--- a/arch/arm/boot/dts/omap36xx-clocks.dtsi
+++ b/arch/arm/boot/dts/omap36xx-clocks.dtsi
@@ -70,6 +70,26 @@
};
};
+&dpll4_m2x2_mul_ck {
+ clock-mult = <1>;
+};
+
+&dpll4_m3x2_mul_ck {
+ clock-mult = <1>;
+};
+
+&dpll4_m4x2_mul_ck {
+ ti,clock-mult = <1>;
+};
+
+&dpll4_m5x2_mul_ck {
+ clock-mult = <1>;
+};
+
+&dpll4_m6x2_mul_ck {
+ clock-mult = <1>;
+};
+
&cm_clockdomains {
dpll4_clkdm: dpll4_clkdm {
compatible = "ti,clockdomain";
diff --git a/arch/arm/boot/dts/omap36xx-omap3430es2plus-clocks.dtsi b/arch/arm/boot/dts/omap36xx-omap3430es2plus-clocks.dtsi
index 8ed475dd63c9..877318c28364 100644
--- a/arch/arm/boot/dts/omap36xx-omap3430es2plus-clocks.dtsi
+++ b/arch/arm/boot/dts/omap36xx-omap3430es2plus-clocks.dtsi
@@ -25,16 +25,16 @@
ti,dividers = <0>, <1>, <2>, <3>, <4>, <0>, <6>, <0>, <8>;
};
- ssi_ssr_fck_3430es2: ssi_ssr_fck_3430es2 {
+ ssi_ssr_fck: ssi_ssr_fck_3430es2 {
#clock-cells = <0>;
compatible = "ti,composite-clock";
clocks = <&ssi_ssr_gate_fck_3430es2>, <&ssi_ssr_div_fck_3430es2>;
};
- ssi_sst_fck_3430es2: ssi_sst_fck_3430es2 {
+ ssi_sst_fck: ssi_sst_fck_3430es2 {
#clock-cells = <0>;
compatible = "fixed-factor-clock";
- clocks = <&ssi_ssr_fck_3430es2>;
+ clocks = <&ssi_ssr_fck>;
clock-mult = <1>;
clock-div = <2>;
};
@@ -55,7 +55,7 @@
clock-div = <1>;
};
- ssi_ick_3430es2: ssi_ick_3430es2 {
+ ssi_ick: ssi_ick_3430es2 {
#clock-cells = <0>;
compatible = "ti,omap3-ssi-interface-clock";
clocks = <&ssi_l4_ick>;
@@ -193,6 +193,6 @@
<&i2c1_ick>, <&uart2_ick>, <&uart1_ick>, <&gpt11_ick>,
<&gpt10_ick>, <&mcbsp5_ick>, <&mcbsp1_ick>,
<&omapctrl_ick>, <&aes2_ick>, <&sha12_ick>,
- <&ssi_ick_3430es2>;
+ <&ssi_ick>;
};
};
diff --git a/arch/arm/boot/dts/omap36xx.dtsi b/arch/arm/boot/dts/omap36xx.dtsi
index 7e8dee9175d6..22cf4647087e 100644
--- a/arch/arm/boot/dts/omap36xx.dtsi
+++ b/arch/arm/boot/dts/omap36xx.dtsi
@@ -39,6 +39,26 @@
clock-frequency = <48000000>;
};
+ abb_mpu_iva: regulator-abb-mpu {
+ compatible = "ti,abb-v1";
+ regulator-name = "abb_mpu_iva";
+ #address-cell = <0>;
+ #size-cells = <0>;
+ reg = <0x483072f0 0x8>, <0x48306818 0x4>;
+ reg-names = "base-address", "int-address";
+ ti,tranxdone-status-mask = <0x4000000>;
+ clocks = <&sys_ck>;
+ ti,settling-time = <30>;
+ ti,clock-cycles = <8>;
+ ti,abb_info = <
+ /*uV ABB efuse rbb_m fbb_m vset_m*/
+ 1012500 0 0 0 0 0
+ 1200000 0 0 0 0 0
+ 1325000 0 0 0 0 0
+ 1375000 1 0 0 0 0
+ >;
+ };
+
omap3_pmx_core2: pinmux@480025a0 {
compatible = "ti,omap3-padconf", "pinctrl-single";
reg = <0x480025a0 0x5c>;
@@ -52,7 +72,13 @@
};
};
-/include/ "omap36xx-clocks.dtsi"
+/* OMAP3630 needs dss_96m_fck for VENC */
+&venc {
+ clocks = <&dss_tv_fck>, <&dss_96m_fck>;
+ clock-names = "fck", "tv_dac_clk";
+};
+
/include/ "omap34xx-omap36xx-clocks.dtsi"
/include/ "omap36xx-omap3430es2plus-clocks.dtsi"
/include/ "omap36xx-am35xx-omap3430es2plus-clocks.dtsi"
+/include/ "omap36xx-clocks.dtsi"
diff --git a/arch/arm/boot/dts/omap3xxx-clocks.dtsi b/arch/arm/boot/dts/omap3xxx-clocks.dtsi
index cb04d4b37e7f..12be2b35dae9 100644
--- a/arch/arm/boot/dts/omap3xxx-clocks.dtsi
+++ b/arch/arm/boot/dts/omap3xxx-clocks.dtsi
@@ -425,10 +425,11 @@
dpll4_m4x2_mul_ck: dpll4_m4x2_mul_ck {
#clock-cells = <0>;
- compatible = "fixed-factor-clock";
+ compatible = "ti,fixed-factor-clock";
clocks = <&dpll4_m4_ck>;
- clock-mult = <2>;
- clock-div = <1>;
+ ti,clock-mult = <2>;
+ ti,clock-div = <1>;
+ ti,set-rate-parent;
};
dpll4_m4x2_ck: dpll4_m4x2_ck {
@@ -438,6 +439,7 @@
ti,bit-shift = <0x1d>;
reg = <0x0d00>;
ti,set-bit-to-disable;
+ ti,set-rate-parent;
};
dpll4_m5_ck: dpll4_m5_ck {
diff --git a/arch/arm/boot/dts/omap4-duovero-parlor.dts b/arch/arm/boot/dts/omap4-duovero-parlor.dts
new file mode 100644
index 000000000000..96f51d870812
--- /dev/null
+++ b/arch/arm/boot/dts/omap4-duovero-parlor.dts
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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.
+ */
+/dts-v1/;
+
+#include "omap4-duovero.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+ model = "OMAP4430 Gumstix Duovero on Parlor";
+ compatible = "gumstix,omap4-duovero-parlor", "gumstix,omap4-duovero", "ti,omap4430", "ti,omap4";
+
+ leds {
+ compatible = "gpio-leds";
+ led0 {
+ label = "duovero:blue:led0";
+ gpios = <&gpio4 26 GPIO_ACTIVE_HIGH>; /* gpio_122 */
+ linux,default-trigger = "heartbeat";
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ button0@121 {
+ label = "button0";
+ linux,code = <BTN_0>;
+ gpios = <&gpio4 25 GPIO_ACTIVE_LOW>; /* gpio_121 */
+ gpio-key,wakeup;
+ };
+ };
+};
+
+&omap4_pmx_core {
+ pinctrl-0 = <
+ &led_pins
+ &button_pins
+ &smsc_pins
+ >;
+
+ led_pins: pinmux_led_pins {
+ pinctrl-single,pins = <
+ 0xd6 (PIN_OUTPUT | MUX_MODE3) /* abe_dmic_din3.gpio_122 */
+ >;
+ };
+
+ button_pins: pinmux_button_pins {
+ pinctrl-single,pins = <
+ 0xd4 (PIN_INPUT_PULLUP | MUX_MODE3) /* abe_dmic_din2.gpio_121 */
+ >;
+ };
+
+ i2c2_pins: pinmux_i2c2_pins {
+ pinctrl-single,pins = <
+ 0xe6 (PIN_INPUT_PULLUP | MUX_MODE0) /* i2c2_scl */
+ 0xe8 (PIN_INPUT_PULLUP | MUX_MODE0) /* i2c2_sda */
+ >;
+ };
+
+ i2c3_pins: pinmux_i2c3_pins {
+ pinctrl-single,pins = <
+ 0xea (PIN_INPUT_PULLUP | MUX_MODE0) /* i2c3_scl */
+ 0xec (PIN_INPUT_PULLUP | MUX_MODE0) /* i2c3_sda */
+ >;
+ };
+
+ smsc_pins: pinmux_smsc_pins {
+ pinctrl-single,pins = <
+ 0x28 (PIN_INPUT | MUX_MODE3) /* gpmc_a20.gpio_44: IRQ */
+ 0x2a (PIN_INPUT_PULLUP | MUX_MODE3) /* gpmc_a21.gpio_45: nReset */
+ 0x30 (PIN_INPUT_PULLUP | MUX_MODE3) /* gpmc_a24.gpio_48: amdix enabled */
+ >;
+ };
+};
+
+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins>;
+
+ clock-frequency = <400000>;
+};
+
+&i2c3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3_pins>;
+
+ clock-frequency = <100000>;
+
+ /* optional 1K EEPROM with revision information */
+ eeprom@51 {
+ compatible = "atmel,24c01";
+ reg = <0x51>;
+ pagesize = <8>;
+ };
+};
+
+&mmc3 {
+ status = "disabled";
+};
+
+#include "omap-gpmc-smsc911x.dtsi"
+
+&gpmc {
+ ranges = <5 0 0x2c000000 0x1000000>; /* CS5 */
+
+ ethernet@gpmc {
+ reg = <5 0 0xff>;
+ interrupt-parent = <&gpio2>;
+ interrupts = <12 IRQ_TYPE_LEVEL_LOW>; /* gpio_44 */
+
+ phy-mode = "mii";
+
+ gpmc,cs-on-ns = <10>;
+ gpmc,cs-rd-off-ns = <50>;
+ gpmc,cs-wr-off-ns = <50>;
+ gpmc,adv-on-ns = <0>;
+ gpmc,adv-rd-off-ns = <10>;
+ gpmc,adv-wr-off-ns = <10>;
+ gpmc,oe-on-ns = <15>;
+ gpmc,oe-off-ns = <50>;
+ gpmc,we-on-ns = <15>;
+ gpmc,we-off-ns = <50>;
+ gpmc,rd-cycle-ns = <50>;
+ gpmc,wr-cycle-ns = <50>;
+ gpmc,access-ns = <50>;
+ gpmc,page-burst-access-ns = <0>;
+ gpmc,bus-turnaround-ns = <35>;
+ gpmc,cycle2cycle-delay-ns = <35>;
+ gpmc,wr-data-mux-bus-ns = <35>;
+ gpmc,wr-access-ns = <50>;
+
+ gpmc,mux-add-data = <2>;
+ gpmc,sync-read;
+ gpmc,sync-write;
+ gpmc,clk-activation-ns = <5>;
+ gpmc,sync-clk-ps = <20000>;
+ };
+};
+
+
diff --git a/arch/arm/boot/dts/omap4-duovero.dtsi b/arch/arm/boot/dts/omap4-duovero.dtsi
new file mode 100644
index 000000000000..a514791154eb
--- /dev/null
+++ b/arch/arm/boot/dts/omap4-duovero.dtsi
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2014 Florian Vaussard, EPFL Mobots group
+ *
+ * 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 "omap443x.dtsi"
+
+/ {
+ model = "Gumstix Duovero";
+ compatible = "gumstix,omap4-duovero", "ti,omap4430", "ti,omap4";
+
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x40000000>; /* 1 GB */
+ };
+
+ sound {
+ compatible = "ti,abe-twl6040";
+ ti,model = "DuoVero";
+
+ ti,mclk-freq = <38400000>;
+
+ ti,mcpdm = <&mcpdm>;
+
+ ti,twl6040 = <&twl6040>;
+
+ /* Audio routing */
+ ti,audio-routing =
+ "Headset Stereophone", "HSOL",
+ "Headset Stereophone", "HSOR",
+ "HSMIC", "Headset Mic",
+ "Headset Mic", "Headset Mic Bias";
+ };
+
+ /* HS USB Host PHY on PORT 1 */
+ hsusb1_phy: hsusb1_phy {
+ compatible = "usb-nop-xceiv";
+ reset-gpios = <&gpio2 30 GPIO_ACTIVE_LOW>; /* gpio_62 */
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&hsusb1phy_pins>;
+
+ clocks = <&auxclk3_ck>;
+ clock-names = "main_clk";
+ clock-frequency = <19200000>;
+ };
+
+ /* regulator for w2cbw0015 on sdio5 */
+ w2cbw0015_vmmc: w2cbw0015_vmmc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&w2cbw0015_pins>;
+ compatible = "regulator-fixed";
+ regulator-name = "w2cbw0015";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ gpio = <&gpio2 11 GPIO_ACTIVE_LOW>; /* gpio_43 */
+ startup-delay-us = <70000>;
+ enable-active-high;
+ regulator-boot-on;
+ };
+};
+
+&omap4_pmx_core {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &twl6040_pins
+ &mcpdm_pins
+ &mcbsp1_pins
+ &hsusbb1_pins
+ >;
+
+ twl6040_pins: pinmux_twl6040_pins {
+ pinctrl-single,pins = <
+ 0x126 (PIN_OUTPUT | MUX_MODE3) /* usbb2_ulpitll_nxt.gpio_160 */
+ 0x160 (PIN_INPUT | MUX_MODE0) /* sys_nirq2.sys_nirq2 */
+ >;
+ };
+
+ mcpdm_pins: pinmux_mcpdm_pins {
+ pinctrl-single,pins = <
+ 0xc6 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* abe_pdm_ul_data.abe_pdm_ul_data */
+ 0xc8 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* abe_pdm_dl_data.abe_pdm_dl_data */
+ 0xca (PIN_INPUT_PULLUP | MUX_MODE0) /* abe_pdm_frame.abe_pdm_frame */
+ 0xcc (PIN_INPUT_PULLDOWN | MUX_MODE0) /* abe_pdm_lb_clk.abe_pdm_lb_clk */
+ 0xce (PIN_INPUT_PULLDOWN | MUX_MODE0) /* abe_clks.abe_clks */
+ >;
+ };
+
+ mcbsp1_pins: pinmux_mcbsp1_pins {
+ pinctrl-single,pins = <
+ 0xbe (PIN_INPUT | MUX_MODE0) /* abe_mcbsp1_clkx.abe_mcbsp1_clkx */
+ 0xc0 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* abe_mcbsp1_dr.abe_mcbsp1_dr */
+ 0xc2 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* abe_mcbsp1_dx.abe_mcbsp1_dx */
+ 0xc4 (PIN_INPUT | MUX_MODE0) /* abe_mcbsp1_fsx.abe_mcbsp1_fsx */
+ >;
+ };
+
+ hsusbb1_pins: pinmux_hsusbb1_pins {
+ pinctrl-single,pins = <
+ 0x82 (PIN_INPUT_PULLDOWN | MUX_MODE4) /* usbb1_ulpitll_clk.usbb1_ulpiphy_clk */
+ 0x84 (PIN_OUTPUT | MUX_MODE4) /* usbb1_ulpitll_stp.usbb1_ulpiphy_stp */
+ 0x86 (PIN_INPUT_PULLDOWN | MUX_MODE4) /* usbb1_ulpitll_dir.usbb1_ulpiphy_dir */
+ 0x88 (PIN_INPUT_PULLDOWN | MUX_MODE4) /* usbb1_ulpitll_nxt.usbb1_ulpiphy_nxt */
+ 0x8a (PIN_INPUT_PULLDOWN | MUX_MODE4) /* usbb1_ulpitll_dat0.usbb1_ulpiphy_dat0 */
+ 0x8c (PIN_INPUT_PULLDOWN | MUX_MODE4) /* usbb1_ulpitll_dat1.usbb1_ulpiphy_dat1 */
+ 0x8e (PIN_INPUT_PULLDOWN | MUX_MODE4) /* usbb1_ulpitll_dat2.usbb1_ulpiphy_dat2 */
+ 0x90 (PIN_INPUT_PULLDOWN | MUX_MODE4) /* usbb1_ulpitll_dat3.usbb1_ulpiphy_dat3 */
+ 0x92 (PIN_INPUT_PULLDOWN | MUX_MODE4) /* usbb1_ulpitll_dat4.usbb1_ulpiphy_dat4 */
+ 0x94 (PIN_INPUT_PULLDOWN | MUX_MODE4) /* usbb1_ulpitll_dat5.usbb1_ulpiphy_dat5 */
+ 0x96 (PIN_INPUT_PULLDOWN | MUX_MODE4) /* usbb1_ulpitll_dat6.usbb1_ulpiphy_dat6 */
+ 0x98 (PIN_INPUT_PULLDOWN | MUX_MODE4) /* usbb1_ulpitll_dat7.usbb1_ulpiphy_dat7 */
+ >;
+ };
+
+ hsusb1phy_pins: pinmux_hsusb1phy_pins {
+ pinctrl-single,pins = <
+ 0x4c (PIN_OUTPUT | MUX_MODE3) /* gpmc_wait1.gpio_62 */
+ >;
+ };
+
+ w2cbw0015_pins: pinmux_w2cbw0015_pins {
+ pinctrl-single,pins = <
+ 0x26 (PIN_OUTPUT | MUX_MODE3) /* gpmc_a19.gpio_43 */
+ 0x3a (PIN_INPUT | MUX_MODE3) /* gpmc_ncs3.gpio_53 */
+ >;
+ };
+
+ i2c1_pins: pinmux_i2c1_pins {
+ pinctrl-single,pins = <
+ 0xe2 (PIN_INPUT_PULLUP | MUX_MODE0) /* i2c1_scl */
+ 0xe4 (PIN_INPUT_PULLUP | MUX_MODE0) /* i2c1_sda */
+ >;
+ };
+
+ i2c4_pins: pinmux_i2c4_pins {
+ pinctrl-single,pins = <
+ 0xee (PIN_INPUT_PULLUP | MUX_MODE0) /* i2c4_scl */
+ 0xf0 (PIN_INPUT_PULLUP | MUX_MODE0) /* i2c4_sda */
+ >;
+ };
+
+ mmc1_pins: pinmux_mmc1_pins {
+ pinctrl-single,pins = <
+ 0xa2 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_clk */
+ 0xa4 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmcc1_cmd */
+ 0xa6 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmcc1_dat0 */
+ 0xa8 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat1 */
+ 0xaa (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat2 */
+ 0xac (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat3 */
+ >;
+ };
+
+ mmc5_pins: pinmux_mmc5_pins {
+ pinctrl-single,pins = <
+ 0x108 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc5_clk */
+ 0x10a (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmcc5_cmd */
+ 0x10c (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmcc5_dat0 */
+ 0x10e (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc5_dat1 */
+ 0x110 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc5_dat2 */
+ 0x112 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc5_dat3 */
+ >;
+ };
+};
+
+/* PMIC */
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+
+ clock-frequency = <400000>;
+
+ twl: twl@48 {
+ reg = <0x48>;
+ interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>; /* IRQ_SYS_1N cascaded to gic */
+ interrupt-parent = <&gic>;
+ };
+
+ twl6040: twl@4b {
+ compatible = "ti,twl6040";
+ reg = <0x4b>;
+ interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>; /* IRQ_SYS_2N cascaded to gic */
+ interrupt-parent = <&gic>;
+ ti,audpwron-gpio = <&gpio6 0 GPIO_ACTIVE_HIGH>; /* gpio_160 */
+
+ vio-supply = <&v1v8>;
+ v2v1-supply = <&v2v1>;
+ enable-active-high;
+ };
+};
+
+#include "twl6030.dtsi"
+#include "twl6030_omap4.dtsi"
+
+/* on-board bluetooth / WiFi module */
+&i2c4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c4_pins>;
+
+ clock-frequency = <400000>;
+};
+
+&mmc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
+
+ vmmc-supply = <&vmmc>;
+ ti,bus-width = <4>;
+ ti,non-removable; /* FIXME: use PMIC_MMC detect */
+};
+
+&mmc2 {
+ status = "disabled";
+};
+
+/* mmc3 is available to the expansion board */
+
+&mmc4 {
+ status = "disabled";
+};
+
+/* on-board WiFi module */
+&mmc5 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc5_pins>;
+
+ vmmc-supply = <&w2cbw0015_vmmc>;
+ ti,bus-width = <4>;
+ ti,non-removable;
+ cap-power-off-card;
+};
+
+&twl_usb_comparator {
+ usb-supply = <&vusb>;
+};
+
+&usb_otg_hs {
+ interface-type = <1>;
+ mode = <3>;
+ power = <50>;
+};
+
+&usbhshost {
+ port1-mode = "ehci-phy";
+};
+
+&usbhsehci {
+ phys = <&hsusb1_phy>;
+};
+
diff --git a/arch/arm/boot/dts/omap4-panda-common.dtsi b/arch/arm/boot/dts/omap4-panda-common.dtsi
index 88c6a05cab41..d2c45bfaaa2c 100644
--- a/arch/arm/boot/dts/omap4-panda-common.dtsi
+++ b/arch/arm/boot/dts/omap4-panda-common.dtsi
@@ -16,6 +16,11 @@
reg = <0x80000000 0x40000000>; /* 1 GB */
};
+ aliases {
+ display0 = &dvi0;
+ display1 = &hdmi0;
+ };
+
leds: leds {
compatible = "gpio-leds";
pinctrl-names = "default";
@@ -83,12 +88,8 @@
compatible = "usb-nop-xceiv";
reset-gpios = <&gpio2 30 GPIO_ACTIVE_LOW>; /* gpio_62 */
vcc-supply = <&hsusb1_power>;
- /**
- * FIXME:
- * put the right clock phandle here when available
- * clocks = <&auxclk3>;
- * clock-names = "main_clk";
- */
+ clocks = <&auxclk3_ck>;
+ clock-names = "main_clk";
clock-frequency = <19200000>;
};
@@ -104,14 +105,94 @@
startup-delay-us = <70000>;
enable-active-high;
};
+
+ tfp410: encoder@0 {
+ compatible = "ti,tfp410";
+ powerdown-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; /* gpio_0 */
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ tfp410_in: endpoint@0 {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ tfp410_out: endpoint@0 {
+ remote-endpoint = <&dvi_connector_in>;
+ };
+ };
+ };
+ };
+
+ dvi0: connector@0 {
+ compatible = "dvi-connector";
+ label = "dvi";
+
+ digital;
+
+ ddc-i2c-bus = <&i2c3>;
+
+ port {
+ dvi_connector_in: endpoint {
+ remote-endpoint = <&tfp410_out>;
+ };
+ };
+ };
+
+ tpd12s015: encoder@1 {
+ compatible = "ti,tpd12s015";
+
+ gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>, /* 60, CT CP HPD */
+ <&gpio2 9 GPIO_ACTIVE_HIGH>, /* 41, LS OE */
+ <&gpio2 31 GPIO_ACTIVE_HIGH>; /* 63, HPD */
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ tpd12s015_in: endpoint@0 {
+ remote-endpoint = <&hdmi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ tpd12s015_out: endpoint@0 {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+ };
+ };
+ };
+
+ hdmi0: connector@1 {
+ compatible = "hdmi-connector";
+ label = "hdmi";
+
+ type = "a";
+
+ port {
+ hdmi_connector_in: endpoint {
+ remote-endpoint = <&tpd12s015_out>;
+ };
+ };
+ };
};
&omap4_pmx_core {
pinctrl-names = "default";
pinctrl-0 = <
- &twl6040_pins
- &mcpdm_pins
- &mcbsp1_pins
&dss_dpi_pins
&tfp410_pins
&dss_hdmi_pins
@@ -300,6 +381,10 @@
twl6040: twl@4b {
compatible = "ti,twl6040";
reg = <0x4b>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&twl6040_pins>;
+
/* IRQ# = 119 */
interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>; /* IRQ_SYS_2N cascaded to gic */
interrupt-parent = <&gic>;
@@ -380,16 +465,16 @@
device-handle = <&elpida_ECB240ABACN>;
};
-&mcbsp2 {
- status = "disabled";
-};
-
-&mcbsp3 {
- status = "disabled";
+&mcbsp1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mcbsp1_pins>;
+ status = "okay";
};
-&dmic {
- status = "disabled";
+&mcpdm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mcpdm_pins>;
+ status = "okay";
};
&twl_usb_comparator {
@@ -409,3 +494,30 @@
&usbhsehci {
phys = <&hsusb1_phy>;
};
+
+&dss {
+ status = "ok";
+
+ port {
+ dpi_out: endpoint {
+ remote-endpoint = <&tfp410_in>;
+ data-lines = <24>;
+ };
+ };
+};
+
+&dsi2 {
+ status = "ok";
+ vdd-supply = <&vcxio>;
+};
+
+&hdmi {
+ status = "ok";
+ vdda-supply = <&vdac>;
+
+ port {
+ hdmi_out: endpoint {
+ remote-endpoint = <&tpd12s015_in>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts
index dbc81fb6ef03..48983c8d56c2 100644
--- a/arch/arm/boot/dts/omap4-sdp.dts
+++ b/arch/arm/boot/dts/omap4-sdp.dts
@@ -19,6 +19,12 @@
reg = <0x80000000 0x40000000>; /* 1 GB */
};
+ aliases {
+ display0 = &lcd0;
+ display1 = &lcd1;
+ display2 = &hdmi0;
+ };
+
vdd_eth: fixedregulator-vdd-eth {
compatible = "regulator-fixed";
regulator-name = "VDD_ETH";
@@ -153,16 +159,53 @@
startup-delay-us = <70000>;
enable-active-high;
};
+
+ tpd12s015: encoder@0 {
+ compatible = "ti,tpd12s015";
+
+ gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>, /* 60, CT CP HPD */
+ <&gpio2 9 GPIO_ACTIVE_HIGH>, /* 41, LS OE */
+ <&gpio2 31 GPIO_ACTIVE_HIGH>; /* 63, HPD */
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ tpd12s015_in: endpoint@0 {
+ remote-endpoint = <&hdmi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ tpd12s015_out: endpoint@0 {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+ };
+ };
+ };
+
+ hdmi0: connector@0 {
+ compatible = "hdmi-connector";
+ label = "hdmi";
+
+ type = "c";
+
+ port {
+ hdmi_connector_in: endpoint {
+ remote-endpoint = <&tpd12s015_out>;
+ };
+ };
+ };
};
&omap4_pmx_core {
pinctrl-names = "default";
pinctrl-0 = <
- &twl6040_pins
- &mcpdm_pins
- &dmic_pins
- &mcbsp1_pins
- &mcbsp2_pins
&dss_hdmi_pins
&tpd12s015_pins
>;
@@ -326,6 +369,10 @@
twl6040: twl@4b {
compatible = "ti,twl6040";
reg = <0x4b>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&twl6040_pins>;
+
/* SPI = 0, IRQ# = 119, 4 = active high level-sensitive */
interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>; /* IRQ_SYS_2N cascaded to gic */
interrupt-parent = <&gic>;
@@ -537,8 +584,28 @@
pinctrl-0 = <&uart4_pins>;
};
-&mcbsp3 {
- status = "disabled";
+&mcbsp1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mcbsp1_pins>;
+ status = "okay";
+};
+
+&mcbsp2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mcbsp2_pins>;
+ status = "okay";
+};
+
+&dmic {
+ pinctrl-names = "default";
+ pinctrl-0 = <&dmic_pins>;
+ status = "okay";
+};
+
+&mcpdm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mcpdm_pins>;
+ status = "okay";
};
&twl_usb_comparator {
@@ -550,3 +617,68 @@
mode = <3>;
power = <50>;
};
+
+&dss {
+ status = "ok";
+};
+
+&dsi1 {
+ status = "ok";
+ vdd-supply = <&vcxio>;
+
+ port {
+ dsi1_out_ep: endpoint {
+ remote-endpoint = <&lcd0_in>;
+ lanes = <0 1 2 3 4 5>;
+ };
+ };
+
+ lcd0: display {
+ compatible = "tpo,taal", "panel-dsi-cm";
+ label = "lcd0";
+
+ reset-gpios = <&gpio4 6 GPIO_ACTIVE_HIGH>; /* 102 */
+
+ port {
+ lcd0_in: endpoint {
+ remote-endpoint = <&dsi1_out_ep>;
+ };
+ };
+ };
+};
+
+&dsi2 {
+ status = "ok";
+ vdd-supply = <&vcxio>;
+
+ port {
+ dsi2_out_ep: endpoint {
+ remote-endpoint = <&lcd1_in>;
+ lanes = <0 1 2 3 4 5>;
+ };
+ };
+
+ lcd1: display {
+ compatible = "tpo,taal", "panel-dsi-cm";
+ label = "lcd1";
+
+ reset-gpios = <&gpio4 8 GPIO_ACTIVE_HIGH>; /* 104 */
+
+ port {
+ lcd1_in: endpoint {
+ remote-endpoint = <&dsi2_out_ep>;
+ };
+ };
+ };
+};
+
+&hdmi {
+ status = "ok";
+ vdda-supply = <&vdac>;
+
+ port {
+ hdmi_out: endpoint {
+ remote-endpoint = <&tpd12s015_in>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index d3f8a6e8ca20..2b4c1cbbce33 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -36,6 +36,11 @@
device_type = "cpu";
next-level-cache = <&L2>;
reg = <0x0>;
+
+ clocks = <&dpll_mpu_ck>;
+ clock-names = "cpu";
+
+ clock-latency = <300000>; /* From omap-cpufreq driver */
};
cpu@1 {
compatible = "arm,cortex-a9";
@@ -275,6 +280,8 @@
gpmc,num-waitpins = <4>;
ti,hwmods = "gpmc";
ti,no-idle-on-init;
+ clocks = <&l3_div_ck>;
+ clock-names = "fck";
};
uart1: serial@4806a000 {
@@ -313,6 +320,7 @@
compatible = "ti,omap4-hwspinlock";
reg = <0x4a0f6000 0x1000>;
ti,hwmods = "spinlock";
+ #hwlock-cells = <1>;
};
i2c1: i2c@48070000 {
@@ -461,6 +469,21 @@
dma-names = "tx", "rx";
};
+ mmu_dsp: mmu@4a066000 {
+ compatible = "ti,omap4-iommu";
+ reg = <0x4a066000 0x100>;
+ interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "mmu_dsp";
+ };
+
+ mmu_ipu: mmu@55082000 {
+ compatible = "ti,omap4-iommu";
+ reg = <0x55082000 0x100>;
+ interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "mmu_ipu";
+ ti,iommu-bus-err-back;
+ };
+
wdt2: wdt@4a314000 {
compatible = "ti,omap4-wdt", "ti,omap3-wdt";
reg = <0x4a314000 0x80>;
@@ -478,6 +501,7 @@
dmas = <&sdma 65>,
<&sdma 66>;
dma-names = "up_link", "dn_link";
+ status = "disabled";
};
dmic: dmic@4012e000 {
@@ -489,6 +513,7 @@
ti,hwmods = "dmic";
dmas = <&sdma 67>;
dma-names = "up_link";
+ status = "disabled";
};
mcbsp1: mcbsp@40122000 {
@@ -503,6 +528,7 @@
dmas = <&sdma 33>,
<&sdma 34>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp2: mcbsp@40124000 {
@@ -517,6 +543,7 @@
dmas = <&sdma 17>,
<&sdma 18>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp3: mcbsp@40126000 {
@@ -531,6 +558,7 @@
dmas = <&sdma 19>,
<&sdma 20>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp4: mcbsp@48096000 {
@@ -544,6 +572,7 @@
dmas = <&sdma 31>,
<&sdma 32>;
dma-names = "tx", "rx";
+ status = "disabled";
};
keypad: keypad@4a31c000 {
@@ -554,6 +583,13 @@
ti,hwmods = "kbd";
};
+ dmm@4e000000 {
+ compatible = "ti,omap4-dmm";
+ reg = <0x4e000000 0x800>;
+ interrupts = <0 113 0x4>;
+ ti,hwmods = "dmm";
+ };
+
emif1: emif@4c000000 {
compatible = "ti,emif-4d";
reg = <0x4c000000 0x100>;
@@ -697,16 +733,22 @@
#address-cells = <1>;
#size-cells = <1>;
ranges;
+ clocks = <&init_60m_fclk>,
+ <&xclk60mhsp1_ck>,
+ <&xclk60mhsp2_ck>;
+ clock-names = "refclk_60m_int",
+ "refclk_60m_ext_p1",
+ "refclk_60m_ext_p2";
usbhsohci: ohci@4a064800 {
- compatible = "ti,ohci-omap3", "usb-ohci";
+ compatible = "ti,ohci-omap3";
reg = <0x4a064800 0x400>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
};
usbhsehci: ehci@4a064c00 {
- compatible = "ti,ehci-omap", "usb-ehci";
+ compatible = "ti,ehci-omap";
reg = <0x4a064c00 0x400>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
@@ -757,6 +799,111 @@
dmas = <&sdma 117>, <&sdma 116>;
dma-names = "tx", "rx";
};
+
+ abb_mpu: regulator-abb-mpu {
+ compatible = "ti,abb-v2";
+ regulator-name = "abb_mpu";
+ #address-cells = <0>;
+ #size-cells = <0>;
+ ti,tranxdone-status-mask = <0x80>;
+ clocks = <&sys_clkin_ck>;
+ ti,settling-time = <50>;
+ ti,clock-cycles = <16>;
+
+ status = "disabled";
+ };
+
+ abb_iva: regulator-abb-iva {
+ compatible = "ti,abb-v2";
+ regulator-name = "abb_iva";
+ #address-cells = <0>;
+ #size-cells = <0>;
+ ti,tranxdone-status-mask = <0x80000000>;
+ clocks = <&sys_clkin_ck>;
+ ti,settling-time = <50>;
+ ti,clock-cycles = <16>;
+
+ status = "disabled";
+ };
+
+ dss: dss@58000000 {
+ compatible = "ti,omap4-dss";
+ reg = <0x58000000 0x80>;
+ status = "disabled";
+ ti,hwmods = "dss_core";
+ clocks = <&dss_dss_clk>;
+ clock-names = "fck";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ dispc@58001000 {
+ compatible = "ti,omap4-dispc";
+ reg = <0x58001000 0x1000>;
+ interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "dss_dispc";
+ clocks = <&dss_dss_clk>;
+ clock-names = "fck";
+ };
+
+ rfbi: encoder@58002000 {
+ compatible = "ti,omap4-rfbi";
+ reg = <0x58002000 0x1000>;
+ status = "disabled";
+ ti,hwmods = "dss_rfbi";
+ clocks = <&dss_dss_clk>, <&dss_fck>;
+ clock-names = "fck", "ick";
+ };
+
+ venc: encoder@58003000 {
+ compatible = "ti,omap4-venc";
+ reg = <0x58003000 0x1000>;
+ status = "disabled";
+ ti,hwmods = "dss_venc";
+ clocks = <&dss_tv_clk>;
+ clock-names = "fck";
+ };
+
+ dsi1: encoder@58004000 {
+ compatible = "ti,omap4-dsi";
+ reg = <0x58004000 0x200>,
+ <0x58004200 0x40>,
+ <0x58004300 0x20>;
+ reg-names = "proto", "phy", "pll";
+ interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ ti,hwmods = "dss_dsi1";
+ clocks = <&dss_dss_clk>, <&dss_sys_clk>;
+ clock-names = "fck", "sys_clk";
+ };
+
+ dsi2: encoder@58005000 {
+ compatible = "ti,omap4-dsi";
+ reg = <0x58005000 0x200>,
+ <0x58005200 0x40>,
+ <0x58005300 0x20>;
+ reg-names = "proto", "phy", "pll";
+ interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ ti,hwmods = "dss_dsi2";
+ clocks = <&dss_dss_clk>, <&dss_sys_clk>;
+ clock-names = "fck", "sys_clk";
+ };
+
+ hdmi: encoder@58006000 {
+ compatible = "ti,omap4-hdmi";
+ reg = <0x58006000 0x200>,
+ <0x58006200 0x100>,
+ <0x58006300 0x100>,
+ <0x58006400 0x1000>;
+ reg-names = "wp", "pll", "phy", "core";
+ interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ ti,hwmods = "dss_hdmi";
+ clocks = <&dss_48mhz_clk>, <&dss_sys_clk>;
+ clock-names = "fck", "sys_clk";
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi
index 8c1cfad30d60..0adfa1d1ef20 100644
--- a/arch/arm/boot/dts/omap443x.dtsi
+++ b/arch/arm/boot/dts/omap443x.dtsi
@@ -43,6 +43,32 @@
#thermal-sensor-cells = <0>;
};
};
+
+ ocp {
+ abb_mpu: regulator-abb-mpu {
+ status = "okay";
+
+ reg = <0x4a307bd0 0x8>, <0x4a306014 0x4>;
+ reg-names = "base-address", "int-address";
+
+ ti,abb_info = <
+ /*uV ABB efuse rbb_m fbb_m vset_m*/
+ 1025000 0 0 0 0 0
+ 1200000 0 0 0 0 0
+ 1313000 0 0 0 0 0
+ 1375000 1 0 0 0 0
+ 1389000 1 0 0 0 0
+ >;
+ };
+
+ /* Default unused, just provide register info for record */
+ abb_iva: regulator-abb-iva {
+ reg = <0x4a307bd8 0x8>, <0x4a306010 0x4>;
+ reg-names = "base-address", "int-address";
+ };
+
+ };
+
};
/include/ "omap443x-clocks.dtsi"
diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
index 6b32f520741a..194f9ef0a009 100644
--- a/arch/arm/boot/dts/omap4460.dtsi
+++ b/arch/arm/boot/dts/omap4460.dtsi
@@ -50,7 +50,44 @@
#thermal-sensor-cells = <0>;
};
+
+ abb_mpu: regulator-abb-mpu {
+ status = "okay";
+
+ reg = <0x4a307bd0 0x8>, <0x4a306014 0x4>,
+ <0x4A002268 0x4>;
+ reg-names = "base-address", "int-address",
+ "efuse-address";
+
+ ti,abb_info = <
+ /*uV ABB efuse rbb_m fbb_m vset_m*/
+ 1025000 0 0 0 0 0
+ 1200000 0 0 0 0 0
+ 1313000 0 0 0x100000 0x40000 0
+ 1375000 1 0 0 0 0
+ 1389000 1 0 0 0 0
+ >;
+ };
+
+ abb_iva: regulator-abb-iva {
+ status = "okay";
+
+ reg = <0x4a307bd8 0x8>, <0x4a306010 0x4>,
+ <0x4A002268 0x4>;
+ reg-names = "base-address", "int-address",
+ "efuse-address";
+
+ ti,abb_info = <
+ /*uV ABB efuse rbb_m fbb_m vset_m*/
+ 950000 0 0 0 0 0
+ 1140000 0 0 0 0 0
+ 1291000 0 0 0x200000 0 0
+ 1375000 1 0 0 0 0
+ 1376000 1 0 0 0 0
+ >;
+ };
};
+
};
/include/ "omap446x-clocks.dtsi"
diff --git a/arch/arm/boot/dts/omap5-uevm.dts b/arch/arm/boot/dts/omap5-uevm.dts
index 002fa70180a5..3b99ec25b748 100644
--- a/arch/arm/boot/dts/omap5-uevm.dts
+++ b/arch/arm/boot/dts/omap5-uevm.dts
@@ -31,12 +31,8 @@
hsusb2_phy: hsusb2_phy {
compatible = "usb-nop-xceiv";
reset-gpios = <&gpio3 16 GPIO_ACTIVE_LOW>; /* gpio3_80 HUB_NRESET */
- /**
- * FIXME
- * Put the right clock phandle here when available
- * clocks = <&auxclk1>;
- * clock-names = "main_clk";
- */
+ clocks = <&auxclk1_ck>;
+ clock-names = "main_clk";
clock-frequency = <19200000>;
};
diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi
index a72813a9663e..19155bb84835 100644
--- a/arch/arm/boot/dts/omap5.dtsi
+++ b/arch/arm/boot/dts/omap5.dtsi
@@ -49,6 +49,12 @@
1000000 1060000
1500000 1250000
>;
+
+ clocks = <&dpll_mpu_ck>;
+ clock-names = "cpu";
+
+ clock-latency = <300000>; /* From omap-cpufreq driver */
+
/* cooling options */
cooling-min-level = <0>;
cooling-max-level = <2>;
@@ -302,6 +308,8 @@
gpmc,num-cs = <8>;
gpmc,num-waitpins = <4>;
ti,hwmods = "gpmc";
+ clocks = <&l3_iclk_div>;
+ clock-names = "fck";
};
i2c1: i2c@48070000 {
@@ -353,6 +361,7 @@
compatible = "ti,omap4-hwspinlock";
reg = <0x4a0f6000 0x1000>;
ti,hwmods = "spinlock";
+ #hwlock-cells = <1>;
};
mcspi1: spi@48098000 {
@@ -513,6 +522,21 @@
dma-names = "tx", "rx";
};
+ mmu_dsp: mmu@4a066000 {
+ compatible = "ti,omap4-iommu";
+ reg = <0x4a066000 0x100>;
+ interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "mmu_dsp";
+ };
+
+ mmu_ipu: mmu@55082000 {
+ compatible = "ti,omap4-iommu";
+ reg = <0x55082000 0x100>;
+ interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "mmu_ipu";
+ ti,iommu-bus-err-back;
+ };
+
keypad: keypad@4ae1c000 {
compatible = "ti,omap4-keypad";
reg = <0x4ae1c000 0x400>;
@@ -529,6 +553,7 @@
dmas = <&sdma 65>,
<&sdma 66>;
dma-names = "up_link", "dn_link";
+ status = "disabled";
};
dmic: dmic@4012e000 {
@@ -540,6 +565,7 @@
ti,hwmods = "dmic";
dmas = <&sdma 67>;
dma-names = "up_link";
+ status = "disabled";
};
mcbsp1: mcbsp@40122000 {
@@ -554,6 +580,7 @@
dmas = <&sdma 33>,
<&sdma 34>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp2: mcbsp@40124000 {
@@ -568,6 +595,7 @@
dmas = <&sdma 17>,
<&sdma 18>;
dma-names = "tx", "rx";
+ status = "disabled";
};
mcbsp3: mcbsp@40126000 {
@@ -582,6 +610,7 @@
dmas = <&sdma 19>,
<&sdma 20>;
dma-names = "tx", "rx";
+ status = "disabled";
};
timer1: timer@4ae18000 {
@@ -683,6 +712,13 @@
ti,hwmods = "wd_timer2";
};
+ dmm@4e000000 {
+ compatible = "ti,omap5-dmm";
+ reg = <0x4e000000 0x800>;
+ interrupts = <0 113 0x4>;
+ ti,hwmods = "dmm";
+ };
+
emif1: emif@4c000000 {
compatible = "ti,emif-4d5";
ti,hwmods = "emif1";
@@ -732,7 +768,8 @@
compatible = "snps,dwc3";
reg = <0x4a030000 0x10000>;
interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
- usb-phy = <&usb2_phy>, <&usb3_phy>;
+ phys = <&usb2_phy>, <&usb3_phy>;
+ phy-names = "usb2-phy", "usb3-phy";
dr_mode = "peripheral";
tx-fifo-resize;
};
@@ -749,6 +786,7 @@
compatible = "ti,omap-usb2";
reg = <0x4a084000 0x7c>;
ctrl-module = <&omap_control_usb2phy>;
+ #phy-cells = <0>;
};
usb3_phy: usb3phy@4a084400 {
@@ -758,6 +796,7 @@
<0x4a084c00 0x40>;
reg-names = "phy_rx", "phy_tx", "pll_ctrl";
ctrl-module = <&omap_control_usb3phy>;
+ #phy-cells = <0>;
};
};
@@ -775,16 +814,22 @@
#address-cells = <1>;
#size-cells = <1>;
ranges;
+ clocks = <&l3init_60m_fclk>,
+ <&xclk60mhsp1_ck>,
+ <&xclk60mhsp2_ck>;
+ clock-names = "refclk_60m_int",
+ "refclk_60m_ext_p1",
+ "refclk_60m_ext_p2";
usbhsohci: ohci@4a064800 {
- compatible = "ti,ohci-omap3", "usb-ohci";
+ compatible = "ti,ohci-omap3";
reg = <0x4a064800 0x400>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
};
usbhsehci: ehci@4a064c00 {
- compatible = "ti,ehci-omap", "usb-ehci";
+ compatible = "ti,ehci-omap";
reg = <0x4a064c00 0x400>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
diff --git a/arch/arm/boot/dts/prima2.dtsi b/arch/arm/boot/dts/prima2.dtsi
index 8582ae41a583..20145526cd7b 100644
--- a/arch/arm/boot/dts/prima2.dtsi
+++ b/arch/arm/boot/dts/prima2.dtsi
@@ -76,9 +76,10 @@
#clock-cells = <1>;
};
- reset-controller@88010000 {
+ rstc: reset-controller@88010000 {
compatible = "sirf,prima2-rstc";
reg = <0x88010000 0x1000>;
+ #reset-cells = <1>;
};
rsc-controller@88020000 {
diff --git a/arch/arm/boot/dts/qcom-msm8660-surf.dts b/arch/arm/boot/dts/qcom-msm8660-surf.dts
index 68a72f5507b9..169bad90dac9 100644
--- a/arch/arm/boot/dts/qcom-msm8660-surf.dts
+++ b/arch/arm/boot/dts/qcom-msm8660-surf.dts
@@ -1,63 +1,6 @@
-/dts-v1/;
-
-/include/ "skeleton.dtsi"
-
-#include <dt-bindings/clock/qcom,gcc-msm8660.h>
+#include "qcom-msm8660.dtsi"
/ {
model = "Qualcomm MSM8660 SURF";
compatible = "qcom,msm8660-surf", "qcom,msm8660";
- interrupt-parent = <&intc>;
-
- intc: interrupt-controller@2080000 {
- compatible = "qcom,msm-8660-qgic";
- interrupt-controller;
- #interrupt-cells = <3>;
- reg = < 0x02080000 0x1000 >,
- < 0x02081000 0x1000 >;
- };
-
- timer@2000000 {
- compatible = "qcom,scss-timer", "qcom,msm-timer";
- interrupts = <1 0 0x301>,
- <1 1 0x301>,
- <1 2 0x301>;
- reg = <0x02000000 0x100>;
- clock-frequency = <27000000>,
- <32768>;
- cpu-offset = <0x40000>;
- };
-
- msmgpio: gpio@800000 {
- compatible = "qcom,msm-gpio";
- reg = <0x00800000 0x4000>;
- gpio-controller;
- #gpio-cells = <2>;
- ngpio = <173>;
- interrupts = <0 16 0x4>;
- interrupt-controller;
- #interrupt-cells = <2>;
- };
-
- gcc: clock-controller@900000 {
- compatible = "qcom,gcc-msm8660";
- #clock-cells = <1>;
- #reset-cells = <1>;
- reg = <0x900000 0x4000>;
- };
-
- serial@19c40000 {
- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
- reg = <0x19c40000 0x1000>,
- <0x19c00000 0x1000>;
- interrupts = <0 195 0x0>;
- clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>;
- clock-names = "core", "iface";
- };
-
- qcom,ssbi@500000 {
- compatible = "qcom,ssbi";
- reg = <0x500000 0x1000>;
- qcom,controller-type = "pmic-arbiter";
- };
};
diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi
new file mode 100644
index 000000000000..c52a9e964a44
--- /dev/null
+++ b/arch/arm/boot/dts/qcom-msm8660.dtsi
@@ -0,0 +1,87 @@
+/dts-v1/;
+
+/include/ "skeleton.dtsi"
+
+#include <dt-bindings/clock/qcom,gcc-msm8660.h>
+
+/ {
+ model = "Qualcomm MSM8660";
+ compatible = "qcom,msm8660";
+ interrupt-parent = <&intc>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "qcom,scorpion";
+ enable-method = "qcom,gcc-msm8660";
+
+ cpu@0 {
+ device_type = "cpu";
+ reg = <0>;
+ next-level-cache = <&L2>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ reg = <1>;
+ next-level-cache = <&L2>;
+ };
+
+ L2: l2-cache {
+ compatible = "cache";
+ cache-level = <2>;
+ };
+ };
+
+ intc: interrupt-controller@2080000 {
+ compatible = "qcom,msm-8660-qgic";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = < 0x02080000 0x1000 >,
+ < 0x02081000 0x1000 >;
+ };
+
+ timer@2000000 {
+ compatible = "qcom,scss-timer", "qcom,msm-timer";
+ interrupts = <1 0 0x301>,
+ <1 1 0x301>,
+ <1 2 0x301>;
+ reg = <0x02000000 0x100>;
+ clock-frequency = <27000000>,
+ <32768>;
+ cpu-offset = <0x40000>;
+ };
+
+ msmgpio: gpio@800000 {
+ compatible = "qcom,msm-gpio";
+ reg = <0x00800000 0x4000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpio = <173>;
+ interrupts = <0 16 0x4>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gcc: clock-controller@900000 {
+ compatible = "qcom,gcc-msm8660";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ reg = <0x900000 0x4000>;
+ };
+
+ serial@19c40000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x19c40000 0x1000>,
+ <0x19c00000 0x1000>;
+ interrupts = <0 195 0x0>;
+ clocks = <&gcc GSBI12_UART_CLK>, <&gcc GSBI12_H_CLK>;
+ clock-names = "core", "iface";
+ };
+
+ qcom,ssbi@500000 {
+ compatible = "qcom,ssbi";
+ reg = <0x500000 0x1000>;
+ qcom,controller-type = "pmic-arbiter";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom-msm8960-cdp.dts b/arch/arm/boot/dts/qcom-msm8960-cdp.dts
index 7c30de4fa302..a58fb88315f6 100644
--- a/arch/arm/boot/dts/qcom-msm8960-cdp.dts
+++ b/arch/arm/boot/dts/qcom-msm8960-cdp.dts
@@ -1,70 +1,6 @@
-/dts-v1/;
-
-/include/ "skeleton.dtsi"
-
-#include <dt-bindings/clock/qcom,gcc-msm8960.h>
+#include "qcom-msm8960.dtsi"
/ {
model = "Qualcomm MSM8960 CDP";
compatible = "qcom,msm8960-cdp", "qcom,msm8960";
- interrupt-parent = <&intc>;
-
- intc: interrupt-controller@2000000 {
- compatible = "qcom,msm-qgic2";
- interrupt-controller;
- #interrupt-cells = <3>;
- reg = < 0x02000000 0x1000 >,
- < 0x02002000 0x1000 >;
- };
-
- timer@200a000 {
- compatible = "qcom,kpss-timer", "qcom,msm-timer";
- interrupts = <1 1 0x301>,
- <1 2 0x301>,
- <1 3 0x301>;
- reg = <0x0200a000 0x100>;
- clock-frequency = <27000000>,
- <32768>;
- cpu-offset = <0x80000>;
- };
-
- msmgpio: gpio@800000 {
- compatible = "qcom,msm-gpio";
- gpio-controller;
- #gpio-cells = <2>;
- ngpio = <150>;
- interrupts = <0 16 0x4>;
- interrupt-controller;
- #interrupt-cells = <2>;
- reg = <0x800000 0x4000>;
- };
-
- gcc: clock-controller@900000 {
- compatible = "qcom,gcc-msm8960";
- #clock-cells = <1>;
- #reset-cells = <1>;
- reg = <0x900000 0x4000>;
- };
-
- clock-controller@4000000 {
- compatible = "qcom,mmcc-msm8960";
- reg = <0x4000000 0x1000>;
- #clock-cells = <1>;
- #reset-cells = <1>;
- };
-
- serial@16440000 {
- compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
- reg = <0x16440000 0x1000>,
- <0x16400000 0x1000>;
- interrupts = <0 154 0x0>;
- clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>;
- clock-names = "core", "iface";
- };
-
- qcom,ssbi@500000 {
- compatible = "qcom,ssbi";
- reg = <0x500000 0x1000>;
- qcom,controller-type = "pmic-arbiter";
- };
};
diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi
new file mode 100644
index 000000000000..997b7b94e117
--- /dev/null
+++ b/arch/arm/boot/dts/qcom-msm8960.dtsi
@@ -0,0 +1,135 @@
+/dts-v1/;
+
+/include/ "skeleton.dtsi"
+
+#include <dt-bindings/clock/qcom,gcc-msm8960.h>
+
+/ {
+ model = "Qualcomm MSM8960";
+ compatible = "qcom,msm8960";
+ interrupt-parent = <&intc>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <1 14 0x304>;
+ compatible = "qcom,krait";
+ enable-method = "qcom,kpss-acc-v1";
+
+ cpu@0 {
+ device_type = "cpu";
+ reg = <0>;
+ next-level-cache = <&L2>;
+ qcom,acc = <&acc0>;
+ qcom,saw = <&saw0>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ reg = <1>;
+ next-level-cache = <&L2>;
+ qcom,acc = <&acc1>;
+ qcom,saw = <&saw1>;
+ };
+
+ L2: l2-cache {
+ compatible = "cache";
+ cache-level = <2>;
+ interrupts = <0 2 0x4>;
+ };
+ };
+
+ cpu-pmu {
+ compatible = "qcom,krait-pmu";
+ interrupts = <1 10 0x304>;
+ qcom,no-pc-write;
+ };
+
+ intc: interrupt-controller@2000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = < 0x02000000 0x1000 >,
+ < 0x02002000 0x1000 >;
+ };
+
+ timer@200a000 {
+ compatible = "qcom,kpss-timer", "qcom,msm-timer";
+ interrupts = <1 1 0x301>,
+ <1 2 0x301>,
+ <1 3 0x301>;
+ reg = <0x0200a000 0x100>;
+ clock-frequency = <27000000>,
+ <32768>;
+ cpu-offset = <0x80000>;
+ };
+
+ msmgpio: gpio@800000 {
+ compatible = "qcom,msm-gpio";
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpio = <150>;
+ interrupts = <0 16 0x4>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0x800000 0x4000>;
+ };
+
+ gcc: clock-controller@900000 {
+ compatible = "qcom,gcc-msm8960";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ reg = <0x900000 0x4000>;
+ };
+
+ clock-controller@4000000 {
+ compatible = "qcom,mmcc-msm8960";
+ reg = <0x4000000 0x1000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ acc0: clock-controller@2088000 {
+ compatible = "qcom,kpss-acc-v1";
+ reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
+ };
+
+ acc1: clock-controller@2098000 {
+ compatible = "qcom,kpss-acc-v1";
+ reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
+ };
+
+ saw0: regulator@2089000 {
+ compatible = "qcom,saw2";
+ reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
+ regulator;
+ };
+
+ saw1: regulator@2099000 {
+ compatible = "qcom,saw2";
+ reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
+ regulator;
+ };
+
+ serial@16440000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x16440000 0x1000>,
+ <0x16400000 0x1000>;
+ interrupts = <0 154 0x0>;
+ clocks = <&gcc GSBI5_UART_CLK>, <&gcc GSBI5_H_CLK>;
+ clock-names = "core", "iface";
+ };
+
+ qcom,ssbi@500000 {
+ compatible = "qcom,ssbi";
+ reg = <0x500000 0x1000>;
+ qcom,controller-type = "pmic-arbiter";
+ };
+
+ rng@1a500000 {
+ compatible = "qcom,prng";
+ reg = <0x1a500000 0x200>;
+ clocks = <&gcc PRNG_CLK>;
+ clock-names = "core";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
index 9e5dadb101eb..f68723918b3f 100644
--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
@@ -9,6 +9,54 @@
compatible = "qcom,msm8974";
interrupt-parent = <&intc>;
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <1 9 0xf04>;
+ compatible = "qcom,krait";
+ enable-method = "qcom,kpss-acc-v2";
+
+ cpu@0 {
+ device_type = "cpu";
+ reg = <0>;
+ next-level-cache = <&L2>;
+ qcom,acc = <&acc0>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ reg = <1>;
+ next-level-cache = <&L2>;
+ qcom,acc = <&acc1>;
+ };
+
+ cpu@2 {
+ device_type = "cpu";
+ reg = <2>;
+ next-level-cache = <&L2>;
+ qcom,acc = <&acc2>;
+ };
+
+ cpu@3 {
+ device_type = "cpu";
+ reg = <3>;
+ next-level-cache = <&L2>;
+ qcom,acc = <&acc3>;
+ };
+
+ L2: l2-cache {
+ compatible = "cache";
+ cache-level = <2>;
+ interrupts = <0 2 0x4>;
+ qcom,saw = <&saw_l2>;
+ };
+ };
+
+ cpu-pmu {
+ compatible = "qcom,krait-pmu";
+ interrupts = <1 7 0xf04>;
+ };
+
soc: soc {
#address-cells = <1>;
#size-cells = <1>;
@@ -91,6 +139,32 @@
};
};
+ saw_l2: regulator@f9012000 {
+ compatible = "qcom,saw2";
+ reg = <0xf9012000 0x1000>;
+ regulator;
+ };
+
+ acc0: clock-controller@f9088000 {
+ compatible = "qcom,kpss-acc-v2";
+ reg = <0xf9088000 0x1000>, <0xf9008000 0x1000>;
+ };
+
+ acc1: clock-controller@f9098000 {
+ compatible = "qcom,kpss-acc-v2";
+ reg = <0xf9098000 0x1000>, <0xf9008000 0x1000>;
+ };
+
+ acc2: clock-controller@f90a8000 {
+ compatible = "qcom,kpss-acc-v2";
+ reg = <0xf90a8000 0x1000>, <0xf9008000 0x1000>;
+ };
+
+ acc3: clock-controller@f90b8000 {
+ compatible = "qcom,kpss-acc-v2";
+ reg = <0xf90b8000 0x1000>, <0xf9008000 0x1000>;
+ };
+
restart@fc4ab000 {
compatible = "qcom,pshold";
reg = <0xfc4ab000 0x4>;
@@ -117,5 +191,12 @@
clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>;
clock-names = "core", "iface";
};
+
+ rng@f9bff000 {
+ compatible = "qcom,prng";
+ reg = <0xf9bff000 0x200>;
+ clocks = <&gcc GCC_PRNG_AHB_CLK>;
+ clock-names = "core";
+ };
};
};
diff --git a/arch/arm/boot/dts/r7s72100-genmai-reference.dts b/arch/arm/boot/dts/r7s72100-genmai-reference.dts
index da19c70ed82b..e664611a47c8 100644
--- a/arch/arm/boot/dts/r7s72100-genmai-reference.dts
+++ b/arch/arm/boot/dts/r7s72100-genmai-reference.dts
@@ -9,7 +9,7 @@
*/
/dts-v1/;
-/include/ "r7s72100.dtsi"
+#include "r7s72100.dtsi"
/ {
model = "Genmai";
@@ -29,3 +29,14 @@
#size-cells = <1>;
};
};
+
+&i2c2 {
+ status = "okay";
+ clock-frequency = <400000>;
+
+ eeprom@50 {
+ compatible = "renesas,24c128";
+ reg = <0x50>;
+ pagesize = <64>;
+ };
+};
diff --git a/arch/arm/boot/dts/r7s72100.dtsi b/arch/arm/boot/dts/r7s72100.dtsi
index 46b82aa7dc4e..ee700717a34b 100644
--- a/arch/arm/boot/dts/r7s72100.dtsi
+++ b/arch/arm/boot/dts/r7s72100.dtsi
@@ -8,12 +8,26 @@
* kind, whether express or implied.
*/
+#include <dt-bindings/interrupt-controller/irq.h>
+
/ {
compatible = "renesas,r7s72100";
interrupt-parent = <&gic>;
#address-cells = <1>;
#size-cells = <1>;
+ aliases {
+ i2c0 = &i2c0;
+ i2c1 = &i2c1;
+ i2c2 = &i2c2;
+ i2c3 = &i2c3;
+ spi0 = &spi0;
+ spi1 = &spi1;
+ spi2 = &spi2;
+ spi3 = &spi3;
+ spi4 = &spi4;
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -33,4 +47,137 @@
reg = <0xe8201000 0x1000>,
<0xe8202000 0x1000>;
};
+
+ i2c0: i2c@fcfee000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "renesas,riic-r7s72100", "renesas,riic-rz";
+ reg = <0xfcfee000 0x44>;
+ interrupts = <0 157 IRQ_TYPE_LEVEL_HIGH>,
+ <0 158 IRQ_TYPE_EDGE_RISING>,
+ <0 159 IRQ_TYPE_EDGE_RISING>,
+ <0 160 IRQ_TYPE_LEVEL_HIGH>,
+ <0 161 IRQ_TYPE_LEVEL_HIGH>,
+ <0 162 IRQ_TYPE_LEVEL_HIGH>,
+ <0 163 IRQ_TYPE_LEVEL_HIGH>,
+ <0 164 IRQ_TYPE_LEVEL_HIGH>;
+ clock-frequency = <100000>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@fcfee400 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "renesas,riic-r7s72100", "renesas,riic-rz";
+ reg = <0xfcfee400 0x44>;
+ interrupts = <0 165 IRQ_TYPE_LEVEL_HIGH>,
+ <0 166 IRQ_TYPE_EDGE_RISING>,
+ <0 167 IRQ_TYPE_EDGE_RISING>,
+ <0 168 IRQ_TYPE_LEVEL_HIGH>,
+ <0 169 IRQ_TYPE_LEVEL_HIGH>,
+ <0 170 IRQ_TYPE_LEVEL_HIGH>,
+ <0 171 IRQ_TYPE_LEVEL_HIGH>,
+ <0 172 IRQ_TYPE_LEVEL_HIGH>;
+ clock-frequency = <100000>;
+ status = "disabled";
+ };
+
+ i2c2: i2c@fcfee800 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "renesas,riic-r7s72100", "renesas,riic-rz";
+ reg = <0xfcfee800 0x44>;
+ interrupts = <0 173 IRQ_TYPE_LEVEL_HIGH>,
+ <0 174 IRQ_TYPE_EDGE_RISING>,
+ <0 175 IRQ_TYPE_EDGE_RISING>,
+ <0 176 IRQ_TYPE_LEVEL_HIGH>,
+ <0 177 IRQ_TYPE_LEVEL_HIGH>,
+ <0 178 IRQ_TYPE_LEVEL_HIGH>,
+ <0 179 IRQ_TYPE_LEVEL_HIGH>,
+ <0 180 IRQ_TYPE_LEVEL_HIGH>;
+ clock-frequency = <100000>;
+ status = "disabled";
+ };
+
+ i2c3: i2c@fcfeec00 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "renesas,riic-r7s72100", "renesas,riic-rz";
+ reg = <0xfcfeec00 0x44>;
+ interrupts = <0 181 IRQ_TYPE_LEVEL_HIGH>,
+ <0 182 IRQ_TYPE_EDGE_RISING>,
+ <0 183 IRQ_TYPE_EDGE_RISING>,
+ <0 184 IRQ_TYPE_LEVEL_HIGH>,
+ <0 185 IRQ_TYPE_LEVEL_HIGH>,
+ <0 186 IRQ_TYPE_LEVEL_HIGH>,
+ <0 187 IRQ_TYPE_LEVEL_HIGH>,
+ <0 188 IRQ_TYPE_LEVEL_HIGH>;
+ clock-frequency = <100000>;
+ status = "disabled";
+ };
+
+ spi0: spi@e800c800 {
+ compatible = "renesas,rspi-r7s72100", "renesas,rspi-rz";
+ reg = <0xe800c800 0x24>;
+ interrupts = <0 238 IRQ_TYPE_LEVEL_HIGH>,
+ <0 239 IRQ_TYPE_LEVEL_HIGH>,
+ <0 240 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "error", "rx", "tx";
+ num-cs = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ spi1: spi@e800d000 {
+ compatible = "renesas,rspi-r7s72100", "renesas,rspi-rz";
+ reg = <0xe800d000 0x24>;
+ interrupts = <0 241 IRQ_TYPE_LEVEL_HIGH>,
+ <0 242 IRQ_TYPE_LEVEL_HIGH>,
+ <0 243 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "error", "rx", "tx";
+ num-cs = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ spi2: spi@e800d800 {
+ compatible = "renesas,rspi-r7s72100", "renesas,rspi-rz";
+ reg = <0xe800d800 0x24>;
+ interrupts = <0 244 IRQ_TYPE_LEVEL_HIGH>,
+ <0 245 IRQ_TYPE_LEVEL_HIGH>,
+ <0 246 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "error", "rx", "tx";
+ num-cs = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ spi3: spi@e800e000 {
+ compatible = "renesas,rspi-r7s72100", "renesas,rspi-rz";
+ reg = <0xe800e000 0x24>;
+ interrupts = <0 247 IRQ_TYPE_LEVEL_HIGH>,
+ <0 248 IRQ_TYPE_LEVEL_HIGH>,
+ <0 249 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "error", "rx", "tx";
+ num-cs = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ spi4: spi@e800e800 {
+ compatible = "renesas,rspi-r7s72100", "renesas,rspi-rz";
+ reg = <0xe800e800 0x24>;
+ interrupts = <0 250 IRQ_TYPE_LEVEL_HIGH>,
+ <0 251 IRQ_TYPE_LEVEL_HIGH>,
+ <0 252 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "error", "rx", "tx";
+ num-cs = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
};
diff --git a/arch/arm/boot/dts/r8a7778-bockw-reference.dts b/arch/arm/boot/dts/r8a7778-bockw-reference.dts
index bb62c7a906f4..06cda19dac6a 100644
--- a/arch/arm/boot/dts/r8a7778-bockw-reference.dts
+++ b/arch/arm/boot/dts/r8a7778-bockw-reference.dts
@@ -17,6 +17,7 @@
/dts-v1/;
#include "r8a7778.dtsi"
#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/gpio/gpio.h>
/ {
model = "bockw";
@@ -84,7 +85,7 @@
sdhi0_pins: sd0 {
renesas,groups = "sdhi0_data4", "sdhi0_ctrl",
- "sdhi0_cd", "sdhi0_wp";
+ "sdhi0_cd";
renesas,function = "sdhi0";
};
@@ -101,6 +102,7 @@
vmmc-supply = <&fixedregulator3v3>;
bus-width = <4>;
status = "okay";
+ wp-gpios = <&gpio3 18 GPIO_ACTIVE_HIGH>;
};
&hspi0 {
diff --git a/arch/arm/boot/dts/r8a7778.dtsi b/arch/arm/boot/dts/r8a7778.dtsi
index ddb3bd7a8838..85c5b3b99f5e 100644
--- a/arch/arm/boot/dts/r8a7778.dtsi
+++ b/arch/arm/boot/dts/r8a7778.dtsi
@@ -203,46 +203,6 @@
status = "disabled";
};
- i2c0: i2c@ffc70000 {
- #address-cells = <1>;
- #size-cells = <0>;
- compatible = "renesas,i2c-r8a7778";
- reg = <0xffc70000 0x1000>;
- interrupt-parent = <&gic>;
- interrupts = <0 67 IRQ_TYPE_LEVEL_HIGH>;
- status = "disabled";
- };
-
- i2c1: i2c@ffc71000 {
- #address-cells = <1>;
- #size-cells = <0>;
- compatible = "renesas,i2c-r8a7778";
- reg = <0xffc71000 0x1000>;
- interrupt-parent = <&gic>;
- interrupts = <0 78 IRQ_TYPE_LEVEL_HIGH>;
- status = "disabled";
- };
-
- i2c2: i2c@ffc72000 {
- #address-cells = <1>;
- #size-cells = <0>;
- compatible = "renesas,i2c-r8a7778";
- reg = <0xffc72000 0x1000>;
- interrupt-parent = <&gic>;
- interrupts = <0 76 IRQ_TYPE_LEVEL_HIGH>;
- status = "disabled";
- };
-
- i2c3: i2c@ffc73000 {
- #address-cells = <1>;
- #size-cells = <0>;
- compatible = "renesas,i2c-r8a7778";
- reg = <0xffc73000 0x1000>;
- interrupt-parent = <&gic>;
- interrupts = <0 77 IRQ_TYPE_LEVEL_HIGH>;
- status = "disabled";
- };
-
hspi0: spi@fffc7000 {
compatible = "renesas,hspi";
reg = <0xfffc7000 0x18>;
diff --git a/arch/arm/boot/dts/r8a7790-lager.dts b/arch/arm/boot/dts/r8a7790-lager.dts
index 57569cba1528..6e99eb2df076 100644
--- a/arch/arm/boot/dts/r8a7790-lager.dts
+++ b/arch/arm/boot/dts/r8a7790-lager.dts
@@ -1,7 +1,8 @@
/*
* Device Tree Source for the Lager board
*
- * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013-2014 Renesas Solutions Corp.
+ * Copyright (C) 2014 Cogent Embedded, Inc.
*
* 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
@@ -56,6 +57,54 @@
regulator-boot-on;
regulator-always-on;
};
+
+ vcc_sdhi0: regulator@1 {
+ compatible = "regulator-fixed";
+
+ regulator-name = "SDHI0 Vcc";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpio = <&gpio5 24 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ vccq_sdhi0: regulator@2 {
+ compatible = "regulator-gpio";
+
+ regulator-name = "SDHI0 VccQ";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
+ gpios-states = <1>;
+ states = <3300000 1
+ 1800000 0>;
+ };
+
+ vcc_sdhi2: regulator@3 {
+ compatible = "regulator-fixed";
+
+ regulator-name = "SDHI2 Vcc";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpio = <&gpio5 25 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ vccq_sdhi2: regulator@4 {
+ compatible = "regulator-gpio";
+
+ regulator-name = "SDHI2 VccQ";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpios = <&gpio5 30 GPIO_ACTIVE_HIGH>;
+ gpios-states = <1>;
+ states = <3300000 1
+ 1800000 0>;
+ };
};
&extal_clk {
@@ -63,23 +112,68 @@
};
&pfc {
- pinctrl-0 = <&scif0_pins &scif1_pins>;
+ pinctrl-0 = <&du_pins &scif0_pins &scif1_pins>;
pinctrl-names = "default";
+ du_pins: du {
+ renesas,groups = "du_rgb666", "du_sync_1", "du_clk_out_0";
+ renesas,function = "du";
+ };
+
scif0_pins: serial0 {
renesas,groups = "scif0_data";
renesas,function = "scif0";
};
+ ether_pins: ether {
+ renesas,groups = "eth_link", "eth_mdio", "eth_rmii";
+ renesas,function = "eth";
+ };
+
+ phy1_pins: phy1 {
+ renesas,groups = "intc_irq0";
+ renesas,function = "intc";
+ };
+
scif1_pins: serial1 {
renesas,groups = "scif1_data";
renesas,function = "scif1";
};
+ sdhi0_pins: sd0 {
+ renesas,gpios = "sdhi0_data4", "sdhi0_ctrl";
+ renesas,function = "sdhi0";
+ };
+
+ sdhi2_pins: sd2 {
+ renesas,gpios = "sdhi2_data4", "sdhi2_ctrl";
+ renesas,function = "sdhi2";
+ };
+
mmc1_pins: mmc1 {
renesas,groups = "mmc1_data8", "mmc1_ctrl";
renesas,function = "mmc1";
};
+
+ qspi_pins: spi {
+ renesas,groups = "qspi_ctrl", "qspi_data4";
+ renesas,function = "qspi";
+ };
+};
+
+&ether {
+ pinctrl-0 = <&ether_pins &phy1_pins>;
+ pinctrl-names = "default";
+
+ phy-handle = <&phy1>;
+ renesas,ether-link-active-low;
+ status = "ok";
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ interrupt-parent = <&irqc0>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ };
};
&mmcif1 {
@@ -91,3 +185,58 @@
non-removable;
status = "okay";
};
+
+&sata1 {
+ status = "okay";
+};
+
+&spi {
+ pinctrl-0 = <&qspi_pins>;
+ pinctrl-names = "default";
+
+ status = "okay";
+
+ flash: flash@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "spansion,s25fl512s";
+ reg = <0>;
+ spi-max-frequency = <30000000>;
+ m25p,fast-read;
+
+ partition@0 {
+ label = "loader";
+ reg = <0x00000000 0x00040000>;
+ read-only;
+ };
+ partition@40000 {
+ label = "user";
+ reg = <0x00040000 0x00400000>;
+ read-only;
+ };
+ partition@440000 {
+ label = "flash";
+ reg = <0x00440000 0x03bc0000>;
+ };
+ };
+};
+
+&sdhi0 {
+ pinctrl-0 = <&sdhi0_pins>;
+ pinctrl-names = "default";
+
+ vmmc-supply = <&vcc_sdhi0>;
+ vqmmc-supply = <&vccq_sdhi0>;
+ cd-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>;
+ status = "okay";
+};
+
+&sdhi2 {
+ pinctrl-0 = <&sdhi2_pins>;
+ pinctrl-names = "default";
+
+ vmmc-supply = <&vcc_sdhi2>;
+ vqmmc-supply = <&vccq_sdhi2>;
+ cd-gpios = <&gpio3 22 GPIO_ACTIVE_LOW>;
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi
index 71b1251f79c7..618e5b537eaf 100644
--- a/arch/arm/boot/dts/r8a7790.dtsi
+++ b/arch/arm/boot/dts/r8a7790.dtsi
@@ -1,7 +1,8 @@
/*
* Device Tree Source for the r8a7790 SoC
*
- * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013-2014 Renesas Solutions Corp.
+ * Copyright (C) 2014 Cogent Embedded Inc.
*
* 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
@@ -18,6 +19,13 @@
#address-cells = <2>;
#size-cells = <2>;
+ aliases {
+ i2c0 = &i2c0;
+ i2c1 = &i2c1;
+ i2c2 = &i2c2;
+ i2c3 = &i2c3;
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -94,7 +102,6 @@
gpio0: gpio@e6050000 {
compatible = "renesas,gpio-r8a7790", "renesas,gpio-rcar";
reg = <0 0xe6050000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 4 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -106,7 +113,6 @@
gpio1: gpio@e6051000 {
compatible = "renesas,gpio-r8a7790", "renesas,gpio-rcar";
reg = <0 0xe6051000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 5 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -118,7 +124,6 @@
gpio2: gpio@e6052000 {
compatible = "renesas,gpio-r8a7790", "renesas,gpio-rcar";
reg = <0 0xe6052000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -130,7 +135,6 @@
gpio3: gpio@e6053000 {
compatible = "renesas,gpio-r8a7790", "renesas,gpio-rcar";
reg = <0 0xe6053000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 7 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -142,7 +146,6 @@
gpio4: gpio@e6054000 {
compatible = "renesas,gpio-r8a7790", "renesas,gpio-rcar";
reg = <0 0xe6054000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -154,7 +157,6 @@
gpio5: gpio@e6055000 {
compatible = "renesas,gpio-r8a7790", "renesas,gpio-rcar";
reg = <0 0xe6055000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 9 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -166,8 +168,8 @@
thermal@e61f0000 {
compatible = "renesas,thermal-r8a7790", "renesas,rcar-thermal";
reg = <0 0xe61f0000 0 0x14>, <0 0xe61f0100 0 0x38>;
- interrupt-parent = <&gic>;
interrupts = <0 69 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp5_clks R8A7790_CLK_THERMAL>;
};
timer {
@@ -183,7 +185,6 @@
#interrupt-cells = <2>;
interrupt-controller;
reg = <0 0xe61c0000 0 0x200>;
- interrupt-parent = <&gic>;
interrupts = <0 0 IRQ_TYPE_LEVEL_HIGH>,
<0 1 IRQ_TYPE_LEVEL_HIGH>,
<0 2 IRQ_TYPE_LEVEL_HIGH>,
@@ -195,7 +196,6 @@
#size-cells = <0>;
compatible = "renesas,i2c-r8a7790";
reg = <0 0xe6508000 0 0x40>;
- interrupt-parent = <&gic>;
interrupts = <0 287 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp9_clks R8A7790_CLK_I2C0>;
status = "disabled";
@@ -206,7 +206,6 @@
#size-cells = <0>;
compatible = "renesas,i2c-r8a7790";
reg = <0 0xe6518000 0 0x40>;
- interrupt-parent = <&gic>;
interrupts = <0 288 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp9_clks R8A7790_CLK_I2C1>;
status = "disabled";
@@ -217,7 +216,6 @@
#size-cells = <0>;
compatible = "renesas,i2c-r8a7790";
reg = <0 0xe6530000 0 0x40>;
- interrupt-parent = <&gic>;
interrupts = <0 286 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp9_clks R8A7790_CLK_I2C2>;
status = "disabled";
@@ -228,7 +226,6 @@
#size-cells = <0>;
compatible = "renesas,i2c-r8a7790";
reg = <0 0xe6540000 0 0x40>;
- interrupt-parent = <&gic>;
interrupts = <0 290 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp9_clks R8A7790_CLK_I2C3>;
status = "disabled";
@@ -237,7 +234,6 @@
mmcif0: mmcif@ee200000 {
compatible = "renesas,mmcif-r8a7790", "renesas,sh-mmcif";
reg = <0 0xee200000 0 0x80>;
- interrupt-parent = <&gic>;
interrupts = <0 169 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp3_clks R8A7790_CLK_MMCIF0>;
reg-io-width = <4>;
@@ -247,7 +243,6 @@
mmcif1: mmc@ee220000 {
compatible = "renesas,mmcif-r8a7790", "renesas,sh-mmcif";
reg = <0 0xee220000 0 0x80>;
- interrupt-parent = <&gic>;
interrupts = <0 170 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp3_clks R8A7790_CLK_MMCIF1>;
reg-io-width = <4>;
@@ -262,7 +257,6 @@
sdhi0: sd@ee100000 {
compatible = "renesas,sdhi-r8a7790";
reg = <0 0xee100000 0 0x200>;
- interrupt-parent = <&gic>;
interrupts = <0 165 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp3_clks R8A7790_CLK_SDHI0>;
cap-sd-highspeed;
@@ -272,7 +266,6 @@
sdhi1: sd@ee120000 {
compatible = "renesas,sdhi-r8a7790";
reg = <0 0xee120000 0 0x200>;
- interrupt-parent = <&gic>;
interrupts = <0 166 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp3_clks R8A7790_CLK_SDHI1>;
cap-sd-highspeed;
@@ -282,7 +275,6 @@
sdhi2: sd@ee140000 {
compatible = "renesas,sdhi-r8a7790";
reg = <0 0xee140000 0 0x100>;
- interrupt-parent = <&gic>;
interrupts = <0 167 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp3_clks R8A7790_CLK_SDHI2>;
cap-sd-highspeed;
@@ -292,13 +284,129 @@
sdhi3: sd@ee160000 {
compatible = "renesas,sdhi-r8a7790";
reg = <0 0xee160000 0 0x100>;
- interrupt-parent = <&gic>;
interrupts = <0 168 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp3_clks R8A7790_CLK_SDHI3>;
cap-sd-highspeed;
status = "disabled";
};
+ scifa0: serial@e6c40000 {
+ compatible = "renesas,scifa-r8a7790", "renesas,scifa";
+ reg = <0 0xe6c40000 0 64>;
+ interrupts = <0 144 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7790_CLK_SCIFA0>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifa1: serial@e6c50000 {
+ compatible = "renesas,scifa-r8a7790", "renesas,scifa";
+ reg = <0 0xe6c50000 0 64>;
+ interrupts = <0 145 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7790_CLK_SCIFA1>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifa2: serial@e6c60000 {
+ compatible = "renesas,scifa-r8a7790", "renesas,scifa";
+ reg = <0 0xe6c60000 0 64>;
+ interrupts = <0 151 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7790_CLK_SCIFA2>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifb0: serial@e6c20000 {
+ compatible = "renesas,scifb-r8a7790", "renesas,scifb";
+ reg = <0 0xe6c20000 0 64>;
+ interrupts = <0 148 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7790_CLK_SCIFB0>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifb1: serial@e6c30000 {
+ compatible = "renesas,scifb-r8a7790", "renesas,scifb";
+ reg = <0 0xe6c30000 0 64>;
+ interrupts = <0 149 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7790_CLK_SCIFB1>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifb2: serial@e6ce0000 {
+ compatible = "renesas,scifb-r8a7790", "renesas,scifb";
+ reg = <0 0xe6ce0000 0 64>;
+ interrupts = <0 150 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7790_CLK_SCIFB2>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scif0: serial@e6e60000 {
+ compatible = "renesas,scif-r8a7790", "renesas,scif";
+ reg = <0 0xe6e60000 0 64>;
+ interrupts = <0 152 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7790_CLK_SCIF0>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scif1: serial@e6e68000 {
+ compatible = "renesas,scif-r8a7790", "renesas,scif";
+ reg = <0 0xe6e68000 0 64>;
+ interrupts = <0 153 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7790_CLK_SCIF1>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ hscif0: serial@e62c0000 {
+ compatible = "renesas,hscif-r8a7790", "renesas,hscif";
+ reg = <0 0xe62c0000 0 96>;
+ interrupts = <0 154 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7790_CLK_HSCIF0>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ hscif1: serial@e62c8000 {
+ compatible = "renesas,hscif-r8a7790", "renesas,hscif";
+ reg = <0 0xe62c8000 0 96>;
+ interrupts = <0 155 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7790_CLK_HSCIF1>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ ether: ethernet@ee700000 {
+ compatible = "renesas,ether-r8a7790";
+ reg = <0 0xee700000 0 0x400>;
+ interrupts = <0 162 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp8_clks R8A7790_CLK_ETHER>;
+ phy-mode = "rmii";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ sata0: sata@ee300000 {
+ compatible = "renesas,sata-r8a7790";
+ reg = <0 0xee300000 0 0x2000>;
+ interrupts = <0 105 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp8_clks R8A7790_CLK_SATA0>;
+ status = "disabled";
+ };
+
+ sata1: sata@ee500000 {
+ compatible = "renesas,sata-r8a7790";
+ reg = <0 0xee500000 0 0x2000>;
+ interrupts = <0 106 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp8_clks R8A7790_CLK_SATA1>;
+ status = "disabled";
+ };
+
clocks {
#address-cells = <2>;
#size-cells = <2>;
@@ -313,6 +421,29 @@
clock-output-names = "extal";
};
+ /*
+ * The external audio clocks are configured as 0 Hz fixed frequency clocks by
+ * default. Boards that provide audio clocks should override them.
+ */
+ audio_clk_a: audio_clk_a {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <0>;
+ clock-output-names = "audio_clk_a";
+ };
+ audio_clk_b: audio_clk_b {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <0>;
+ clock-output-names = "audio_clk_b";
+ };
+ audio_clk_c: audio_clk_c {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <0>;
+ clock-output-names = "audio_clk_c";
+ };
+
/* Special CPG clocks */
cpg_clocks: cpg_clocks@e6150000 {
compatible = "renesas,r8a7790-cpg-clocks",
@@ -607,10 +738,16 @@
mstp8_clks: mstp8_clks@e6150990 {
compatible = "renesas,r8a7790-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0 0xe6150990 0 4>, <0 0xe61509a0 0 4>;
- clocks = <&p_clk>;
+ clocks = <&zg_clk>, <&zg_clk>, <&zg_clk>, <&zg_clk>, <&p_clk>,
+ <&zs_clk>, <&zs_clk>;
#clock-cells = <1>;
- renesas,clock-indices = <R8A7790_CLK_ETHER>;
- clock-output-names = "ether";
+ renesas,clock-indices = <
+ R8A7790_CLK_VIN3 R8A7790_CLK_VIN2 R8A7790_CLK_VIN1
+ R8A7790_CLK_VIN0 R8A7790_CLK_ETHER R8A7790_CLK_SATA1
+ R8A7790_CLK_SATA0
+ >;
+ clock-output-names =
+ "vin3", "vin2", "vin1", "vin0", "ether", "sata1", "sata0";
};
mstp9_clks: mstp9_clks@e6150994 {
compatible = "renesas,r8a7790-mstp-clocks", "renesas,cpg-mstp-clocks";
@@ -627,4 +764,15 @@
"rcan1", "rcan0", "qspi_mod", "i2c3", "i2c2", "i2c1", "i2c0";
};
};
+
+ spi: spi@e6b10000 {
+ compatible = "renesas,qspi-r8a7790", "renesas,qspi";
+ reg = <0 0xe6b10000 0 0x2c>;
+ interrupts = <0 184 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp9_clks R8A7790_CLK_QSPI_MOD>;
+ num-cs = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
};
diff --git a/arch/arm/boot/dts/r8a7791-koelsch-reference.dts b/arch/arm/boot/dts/r8a7791-koelsch-reference.dts
deleted file mode 100644
index 588ca17ea1f0..000000000000
--- a/arch/arm/boot/dts/r8a7791-koelsch-reference.dts
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Device Tree Source for the Koelsch board
- *
- * Copyright (C) 2013 Renesas Electronics Corporation
- * Copyright (C) 2013 Renesas Solutions Corp.
- *
- * 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.
- */
-
-/dts-v1/;
-#include "r8a7791.dtsi"
-#include <dt-bindings/gpio/gpio.h>
-
-/ {
- model = "Koelsch";
- compatible = "renesas,koelsch-reference", "renesas,r8a7791";
-
- chosen {
- bootargs = "console=ttySC6,115200 ignore_loglevel rw root=/dev/nfs ip=dhcp";
- };
-
- memory@40000000 {
- device_type = "memory";
- reg = <0 0x40000000 0 0x80000000>;
- };
-
- lbsc {
- #address-cells = <1>;
- #size-cells = <1>;
- };
-
- gpio-keys {
- compatible = "gpio-keys";
-
- key-a {
- gpios = <&gpio7 0 GPIO_ACTIVE_LOW>;
- linux,code = <30>;
- label = "SW30";
- gpio-key,wakeup;
- debounce-interval = <20>;
- };
- key-b {
- gpios = <&gpio7 1 GPIO_ACTIVE_LOW>;
- linux,code = <48>;
- label = "SW31";
- gpio-key,wakeup;
- debounce-interval = <20>;
- };
- key-c {
- gpios = <&gpio7 2 GPIO_ACTIVE_LOW>;
- linux,code = <46>;
- label = "SW32";
- gpio-key,wakeup;
- debounce-interval = <20>;
- };
- key-d {
- gpios = <&gpio7 3 GPIO_ACTIVE_LOW>;
- linux,code = <32>;
- label = "SW33";
- gpio-key,wakeup;
- debounce-interval = <20>;
- };
- key-e {
- gpios = <&gpio7 4 GPIO_ACTIVE_LOW>;
- linux,code = <18>;
- label = "SW34";
- gpio-key,wakeup;
- debounce-interval = <20>;
- };
- key-f {
- gpios = <&gpio7 5 GPIO_ACTIVE_LOW>;
- linux,code = <33>;
- label = "SW35";
- gpio-key,wakeup;
- debounce-interval = <20>;
- };
- key-g {
- gpios = <&gpio7 6 GPIO_ACTIVE_LOW>;
- linux,code = <34>;
- label = "SW36";
- gpio-key,wakeup;
- debounce-interval = <20>;
- };
- };
-
- leds {
- compatible = "gpio-leds";
- led6 {
- gpios = <&gpio2 19 GPIO_ACTIVE_HIGH>;
- };
- led7 {
- gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>;
- };
- led8 {
- gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>;
- };
- };
-};
-
-&pfc {
- pinctrl-0 = <&scif0_pins &scif1_pins>;
- pinctrl-names = "default";
-
- scif0_pins: serial0 {
- renesas,groups = "scif0_data_d";
- renesas,function = "scif0";
- };
-
- scif1_pins: serial1 {
- renesas,groups = "scif1_data_d";
- renesas,function = "scif1";
- };
-};
diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts
index fd556c3483e3..bdd73e6657b2 100644
--- a/arch/arm/boot/dts/r8a7791-koelsch.dts
+++ b/arch/arm/boot/dts/r8a7791-koelsch.dts
@@ -2,7 +2,8 @@
* Device Tree Source for the Koelsch board
*
* Copyright (C) 2013 Renesas Electronics Corporation
- * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013-2014 Renesas Solutions Corp.
+ * Copyright (C) 2014 Cogent Embedded, Inc.
*
* 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
@@ -23,7 +24,12 @@
memory@40000000 {
device_type = "memory";
- reg = <0 0x40000000 0 0x80000000>;
+ reg = <0 0x40000000 0 0x40000000>;
+ };
+
+ memory@200000000 {
+ device_type = "memory";
+ reg = <2 0x00000000 0 0x40000000>;
};
lbsc {
@@ -31,6 +37,60 @@
#size-cells = <1>;
};
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ key-a {
+ gpios = <&gpio7 0 GPIO_ACTIVE_LOW>;
+ linux,code = <30>;
+ label = "SW30";
+ gpio-key,wakeup;
+ debounce-interval = <20>;
+ };
+ key-b {
+ gpios = <&gpio7 1 GPIO_ACTIVE_LOW>;
+ linux,code = <48>;
+ label = "SW31";
+ gpio-key,wakeup;
+ debounce-interval = <20>;
+ };
+ key-c {
+ gpios = <&gpio7 2 GPIO_ACTIVE_LOW>;
+ linux,code = <46>;
+ label = "SW32";
+ gpio-key,wakeup;
+ debounce-interval = <20>;
+ };
+ key-d {
+ gpios = <&gpio7 3 GPIO_ACTIVE_LOW>;
+ linux,code = <32>;
+ label = "SW33";
+ gpio-key,wakeup;
+ debounce-interval = <20>;
+ };
+ key-e {
+ gpios = <&gpio7 4 GPIO_ACTIVE_LOW>;
+ linux,code = <18>;
+ label = "SW34";
+ gpio-key,wakeup;
+ debounce-interval = <20>;
+ };
+ key-f {
+ gpios = <&gpio7 5 GPIO_ACTIVE_LOW>;
+ linux,code = <33>;
+ label = "SW35";
+ gpio-key,wakeup;
+ debounce-interval = <20>;
+ };
+ key-g {
+ gpios = <&gpio7 6 GPIO_ACTIVE_LOW>;
+ linux,code = <34>;
+ label = "SW36";
+ gpio-key,wakeup;
+ debounce-interval = <20>;
+ };
+ };
+
leds {
compatible = "gpio-leds";
led6 {
@@ -43,16 +103,112 @@
gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>;
};
};
+
+ vcc_sdhi0: regulator@0 {
+ compatible = "regulator-fixed";
+
+ regulator-name = "SDHI0 Vcc";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpio = <&gpio7 17 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ vccq_sdhi0: regulator@1 {
+ compatible = "regulator-gpio";
+
+ regulator-name = "SDHI0 VccQ";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpios = <&gpio2 12 GPIO_ACTIVE_HIGH>;
+ gpios-states = <1>;
+ states = <3300000 1
+ 1800000 0>;
+ };
+
+ vcc_sdhi1: regulator@2 {
+ compatible = "regulator-fixed";
+
+ regulator-name = "SDHI1 Vcc";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpio = <&gpio7 18 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ vccq_sdhi1: regulator@3 {
+ compatible = "regulator-gpio";
+
+ regulator-name = "SDHI1 VccQ";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpios = <&gpio2 13 GPIO_ACTIVE_HIGH>;
+ gpios-states = <1>;
+ states = <3300000 1
+ 1800000 0>;
+ };
+
+ vcc_sdhi2: regulator@4 {
+ compatible = "regulator-fixed";
+
+ regulator-name = "SDHI2 Vcc";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpio = <&gpio7 19 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ vccq_sdhi2: regulator@5 {
+ compatible = "regulator-gpio";
+
+ regulator-name = "SDHI2 VccQ";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+
+ gpios = <&gpio2 26 GPIO_ACTIVE_HIGH>;
+ gpios-states = <1>;
+ states = <3300000 1
+ 1800000 0>;
+ };
};
&extal_clk {
clock-frequency = <20000000>;
};
+&i2c2 {
+ pinctrl-0 = <&i2c2_pins>;
+ pinctrl-names = "default";
+
+ status = "okay";
+ clock-frequency = <400000>;
+
+ eeprom@50 {
+ compatible = "renesas,24c02";
+ reg = <0x50>;
+ pagesize = <16>;
+ };
+};
+
&pfc {
- pinctrl-0 = <&scif0_pins &scif1_pins>;
+ pinctrl-0 = <&du_pins &scif0_pins &scif1_pins>;
pinctrl-names = "default";
+ i2c2_pins: i2c {
+ renesas,groups = "i2c2";
+ renesas,function = "i2c2";
+ };
+
+ du_pins: du {
+ renesas,groups = "du_rgb666", "du_sync", "du_clk_out_0";
+ renesas,function = "du";
+ };
+
scif0_pins: serial0 {
renesas,groups = "scif0_data_d";
renesas,function = "scif0";
@@ -62,4 +218,116 @@
renesas,groups = "scif1_data_d";
renesas,function = "scif1";
};
+
+ ether_pins: ether {
+ renesas,groups = "eth_link", "eth_mdio", "eth_rmii";
+ renesas,function = "eth";
+ };
+
+ phy1_pins: phy1 {
+ renesas,groups = "intc_irq0";
+ renesas,function = "intc";
+ };
+
+ sdhi0_pins: sd0 {
+ renesas,gpios = "sdhi0_data4", "sdhi0_ctrl";
+ renesas,function = "sdhi0";
+ };
+
+ sdhi1_pins: sd1 {
+ renesas,gpios = "sdhi1_data4", "sdhi1_ctrl";
+ renesas,function = "sdhi1";
+ };
+
+ sdhi2_pins: sd2 {
+ renesas,gpios = "sdhi2_data4", "sdhi2_ctrl";
+ renesas,function = "sdhi2";
+ };
+
+ qspi_pins: spi {
+ renesas,groups = "qspi_ctrl", "qspi_data4";
+ renesas,function = "qspi";
+ };
+};
+
+&ether {
+ pinctrl-0 = <&ether_pins &phy1_pins>;
+ pinctrl-names = "default";
+
+ phy-handle = <&phy1>;
+ renesas,ether-link-active-low;
+ status = "ok";
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ interrupt-parent = <&irqc0>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ };
+};
+
+&sata0 {
+ status = "okay";
+};
+
+&sdhi0 {
+ pinctrl-0 = <&sdhi0_pins>;
+ pinctrl-names = "default";
+
+ vmmc-supply = <&vcc_sdhi0>;
+ vqmmc-supply = <&vccq_sdhi0>;
+ cd-gpios = <&gpio6 6 GPIO_ACTIVE_LOW>;
+ wp-gpios = <&gpio6 7 GPIO_ACTIVE_HIGH>;
+ status = "okay";
+};
+
+&sdhi1 {
+ pinctrl-0 = <&sdhi1_pins>;
+ pinctrl-names = "default";
+
+ vmmc-supply = <&vcc_sdhi1>;
+ vqmmc-supply = <&vccq_sdhi1>;
+ cd-gpios = <&gpio6 14 GPIO_ACTIVE_LOW>;
+ wp-gpios = <&gpio6 15 GPIO_ACTIVE_HIGH>;
+ status = "okay";
+};
+
+&sdhi2 {
+ pinctrl-0 = <&sdhi2_pins>;
+ pinctrl-names = "default";
+
+ vmmc-supply = <&vcc_sdhi2>;
+ vqmmc-supply = <&vccq_sdhi2>;
+ cd-gpios = <&gpio6 22 GPIO_ACTIVE_LOW>;
+ status = "okay";
+};
+
+&spi {
+ pinctrl-0 = <&qspi_pins>;
+ pinctrl-names = "default";
+
+ status = "okay";
+
+ flash: flash@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "spansion,s25fl512s";
+ reg = <0>;
+ spi-max-frequency = <30000000>;
+ m25p,fast-read;
+
+ partition@0 {
+ label = "loader";
+ reg = <0x00000000 0x00080000>;
+ read-only;
+ };
+ partition@80000 {
+ label = "bootenv";
+ reg = <0x00080000 0x00080000>;
+ read-only;
+ };
+ partition@100000 {
+ label = "data";
+ reg = <0x00100000 0x03f00000>;
+ };
+ };
};
diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi
index 3b075dd19b51..46181708e59c 100644
--- a/arch/arm/boot/dts/r8a7791.dtsi
+++ b/arch/arm/boot/dts/r8a7791.dtsi
@@ -2,7 +2,8 @@
* Device Tree Source for the r8a7791 SoC
*
* Copyright (C) 2013 Renesas Electronics Corporation
- * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013-2014 Renesas Solutions Corp.
+ * Copyright (C) 2014 Cogent Embedded Inc.
*
* 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
@@ -19,6 +20,15 @@
#address-cells = <2>;
#size-cells = <2>;
+ aliases {
+ i2c0 = &i2c0;
+ i2c1 = &i2c1;
+ i2c2 = &i2c2;
+ i2c3 = &i2c3;
+ i2c4 = &i2c4;
+ i2c5 = &i2c5;
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -53,7 +63,6 @@
gpio0: gpio@e6050000 {
compatible = "renesas,gpio-r8a7791", "renesas,gpio-rcar";
reg = <0 0xe6050000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 4 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -65,7 +74,6 @@
gpio1: gpio@e6051000 {
compatible = "renesas,gpio-r8a7791", "renesas,gpio-rcar";
reg = <0 0xe6051000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 5 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -77,7 +85,6 @@
gpio2: gpio@e6052000 {
compatible = "renesas,gpio-r8a7791", "renesas,gpio-rcar";
reg = <0 0xe6052000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -89,7 +96,6 @@
gpio3: gpio@e6053000 {
compatible = "renesas,gpio-r8a7791", "renesas,gpio-rcar";
reg = <0 0xe6053000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 7 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -101,7 +107,6 @@
gpio4: gpio@e6054000 {
compatible = "renesas,gpio-r8a7791", "renesas,gpio-rcar";
reg = <0 0xe6054000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -113,7 +118,6 @@
gpio5: gpio@e6055000 {
compatible = "renesas,gpio-r8a7791", "renesas,gpio-rcar";
reg = <0 0xe6055000 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 9 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -125,7 +129,6 @@
gpio6: gpio@e6055400 {
compatible = "renesas,gpio-r8a7791", "renesas,gpio-rcar";
reg = <0 0xe6055400 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 10 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -137,7 +140,6 @@
gpio7: gpio@e6055800 {
compatible = "renesas,gpio-r8a7791", "renesas,gpio-rcar";
reg = <0 0xe6055800 0 0x50>;
- interrupt-parent = <&gic>;
interrupts = <0 11 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
@@ -149,8 +151,8 @@
thermal@e61f0000 {
compatible = "renesas,thermal-r8a7791", "renesas,rcar-thermal";
reg = <0 0xe61f0000 0 0x14>, <0 0xe61f0100 0 0x38>;
- interrupt-parent = <&gic>;
interrupts = <0 69 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp5_clks R8A7791_CLK_THERMAL>;
};
timer {
@@ -166,7 +168,6 @@
#interrupt-cells = <2>;
interrupt-controller;
reg = <0 0xe61c0000 0 0x200>;
- interrupt-parent = <&gic>;
interrupts = <0 0 IRQ_TYPE_LEVEL_HIGH>,
<0 1 IRQ_TYPE_LEVEL_HIGH>,
<0 2 IRQ_TYPE_LEVEL_HIGH>,
@@ -179,12 +180,288 @@
<0 17 IRQ_TYPE_LEVEL_HIGH>;
};
+ i2c0: i2c@e6508000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "renesas,i2c-r8a7791";
+ reg = <0 0xe6508000 0 0x40>;
+ interrupts = <0 287 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp9_clks R8A7791_CLK_I2C0>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@e6518000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "renesas,i2c-r8a7791";
+ reg = <0 0xe6518000 0 0x40>;
+ interrupts = <0 288 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp9_clks R8A7791_CLK_I2C1>;
+ status = "disabled";
+ };
+
+ i2c2: i2c@e6530000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "renesas,i2c-r8a7791";
+ reg = <0 0xe6530000 0 0x40>;
+ interrupts = <0 286 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp9_clks R8A7791_CLK_I2C2>;
+ status = "disabled";
+ };
+
+ i2c3: i2c@e6540000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "renesas,i2c-r8a7791";
+ reg = <0 0xe6540000 0 0x40>;
+ interrupts = <0 290 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp9_clks R8A7791_CLK_I2C3>;
+ status = "disabled";
+ };
+
+ i2c4: i2c@e6520000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "renesas,i2c-r8a7791";
+ reg = <0 0xe6520000 0 0x40>;
+ interrupts = <0 19 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp9_clks R8A7791_CLK_I2C4>;
+ status = "disabled";
+ };
+
+ i2c5: i2c@e6528000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "renesas,i2c-r8a7791";
+ reg = <0 0xe6528000 0 0x40>;
+ interrupts = <0 20 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp9_clks R8A7791_CLK_I2C5>;
+ status = "disabled";
+ };
+
pfc: pfc@e6060000 {
compatible = "renesas,pfc-r8a7791";
reg = <0 0xe6060000 0 0x250>;
#gpio-range-cells = <3>;
};
+ sdhi0: sd@ee100000 {
+ compatible = "renesas,sdhi-r8a7791";
+ reg = <0 0xee100000 0 0x200>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 165 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp3_clks R8A7791_CLK_SDHI0>;
+ status = "disabled";
+ };
+
+ sdhi1: sd@ee140000 {
+ compatible = "renesas,sdhi-r8a7791";
+ reg = <0 0xee140000 0 0x100>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 167 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp3_clks R8A7791_CLK_SDHI1>;
+ status = "disabled";
+ };
+
+ sdhi2: sd@ee160000 {
+ compatible = "renesas,sdhi-r8a7791";
+ reg = <0 0xee160000 0 0x100>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 168 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp3_clks R8A7791_CLK_SDHI2>;
+ status = "disabled";
+ };
+
+ scifa0: serial@e6c40000 {
+ compatible = "renesas,scifa-r8a7791", "renesas,scifa";
+ reg = <0 0xe6c40000 0 64>;
+ interrupts = <0 144 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7791_CLK_SCIFA0>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifa1: serial@e6c50000 {
+ compatible = "renesas,scifa-r8a7791", "renesas,scifa";
+ reg = <0 0xe6c50000 0 64>;
+ interrupts = <0 145 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7791_CLK_SCIFA1>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifa2: serial@e6c60000 {
+ compatible = "renesas,scifa-r8a7791", "renesas,scifa";
+ reg = <0 0xe6c60000 0 64>;
+ interrupts = <0 151 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7791_CLK_SCIFA2>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifa3: serial@e6c70000 {
+ compatible = "renesas,scifa-r8a7791", "renesas,scifa";
+ reg = <0 0xe6c70000 0 64>;
+ interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp11_clks R8A7791_CLK_SCIFA3>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifa4: serial@e6c78000 {
+ compatible = "renesas,scifa-r8a7791", "renesas,scifa";
+ reg = <0 0xe6c78000 0 64>;
+ interrupts = <0 30 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp11_clks R8A7791_CLK_SCIFA4>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifa5: serial@e6c80000 {
+ compatible = "renesas,scifa-r8a7791", "renesas,scifa";
+ reg = <0 0xe6c80000 0 64>;
+ interrupts = <0 31 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp11_clks R8A7791_CLK_SCIFA5>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifb0: serial@e6c20000 {
+ compatible = "renesas,scifb-r8a7791", "renesas,scifb";
+ reg = <0 0xe6c20000 0 64>;
+ interrupts = <0 148 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7791_CLK_SCIFB0>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifb1: serial@e6c30000 {
+ compatible = "renesas,scifb-r8a7791", "renesas,scifb";
+ reg = <0 0xe6c30000 0 64>;
+ interrupts = <0 149 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7791_CLK_SCIFB1>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scifb2: serial@e6ce0000 {
+ compatible = "renesas,scifb-r8a7791", "renesas,scifb";
+ reg = <0 0xe6ce0000 0 64>;
+ interrupts = <0 150 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp2_clks R8A7791_CLK_SCIFB2>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scif0: serial@e6e60000 {
+ compatible = "renesas,scif-r8a7791", "renesas,scif";
+ reg = <0 0xe6e60000 0 64>;
+ interrupts = <0 152 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7791_CLK_SCIF0>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scif1: serial@e6e68000 {
+ compatible = "renesas,scif-r8a7791", "renesas,scif";
+ reg = <0 0xe6e68000 0 64>;
+ interrupts = <0 153 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7791_CLK_SCIF1>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scif2: serial@e6e58000 {
+ compatible = "renesas,scif-r8a7791", "renesas,scif";
+ reg = <0 0xe6e58000 0 64>;
+ interrupts = <0 22 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7791_CLK_SCIF2>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scif3: serial@e6ea8000 {
+ compatible = "renesas,scif-r8a7791", "renesas,scif";
+ reg = <0 0xe6ea8000 0 64>;
+ interrupts = <0 23 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7791_CLK_SCIF3>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scif4: serial@e6ee0000 {
+ compatible = "renesas,scif-r8a7791", "renesas,scif";
+ reg = <0 0xe6ee0000 0 64>;
+ interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7791_CLK_SCIF4>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ scif5: serial@e6ee8000 {
+ compatible = "renesas,scif-r8a7791", "renesas,scif";
+ reg = <0 0xe6ee8000 0 64>;
+ interrupts = <0 25 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7791_CLK_SCIF5>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ hscif0: serial@e62c0000 {
+ compatible = "renesas,hscif-r8a7791", "renesas,hscif";
+ reg = <0 0xe62c0000 0 96>;
+ interrupts = <0 154 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7791_CLK_HSCIF0>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ hscif1: serial@e62c8000 {
+ compatible = "renesas,hscif-r8a7791", "renesas,hscif";
+ reg = <0 0xe62c8000 0 96>;
+ interrupts = <0 155 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7791_CLK_HSCIF1>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ hscif2: serial@e62d0000 {
+ compatible = "renesas,hscif-r8a7791", "renesas,hscif";
+ reg = <0 0xe62d0000 0 96>;
+ interrupts = <0 21 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7791_CLK_HSCIF2>;
+ clock-names = "sci_ick";
+ status = "disabled";
+ };
+
+ ether: ethernet@ee700000 {
+ compatible = "renesas,ether-r8a7791";
+ reg = <0 0xee700000 0 0x400>;
+ interrupts = <0 162 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp8_clks R8A7791_CLK_ETHER>;
+ phy-mode = "rmii";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ sata0: sata@ee300000 {
+ compatible = "renesas,sata-r8a7791";
+ reg = <0 0xee300000 0 0x2000>;
+ interrupts = <0 105 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp8_clks R8A7791_CLK_SATA0>;
+ status = "disabled";
+ };
+
+ sata1: sata@ee500000 {
+ compatible = "renesas,sata-r8a7791";
+ reg = <0 0xee500000 0 0x2000>;
+ interrupts = <0 106 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp8_clks R8A7791_CLK_SATA1>;
+ status = "disabled";
+ };
+
clocks {
#address-cells = <2>;
#size-cells = <2>;
@@ -474,10 +751,15 @@
mstp8_clks: mstp8_clks@e6150990 {
compatible = "renesas,r8a7791-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0 0xe6150990 0 4>, <0 0xe61509a0 0 4>;
- clocks = <&p_clk>;
+ clocks = <&zg_clk>, <&zg_clk>, <&zg_clk>, <&p_clk>, <&zs_clk>,
+ <&zs_clk>;
#clock-cells = <1>;
- renesas,clock-indices = <R8A7791_CLK_ETHER>;
- clock-output-names = "ether";
+ renesas,clock-indices = <
+ R8A7791_CLK_VIN2 R8A7791_CLK_VIN1 R8A7791_CLK_VIN0
+ R8A7791_CLK_ETHER R8A7791_CLK_SATA1 R8A7791_CLK_SATA0
+ >;
+ clock-output-names =
+ "vin2", "vin1", "vin0", "ether", "sata1", "sata0";
};
mstp9_clks: mstp9_clks@e6150994 {
compatible = "renesas,r8a7791-mstp-clocks", "renesas,cpg-mstp-clocks";
@@ -488,7 +770,7 @@
#clock-cells = <1>;
renesas,clock-indices = <
R8A7791_CLK_RCAN1 R8A7791_CLK_RCAN0 R8A7791_CLK_QSPI_MOD
- R8A7791_CLK_I2C4 R8A7791_CLK_I2C4 R8A7791_CLK_I2C3
+ R8A7791_CLK_I2C5 R8A7791_CLK_I2C4 R8A7791_CLK_I2C3
R8A7791_CLK_I2C2 R8A7791_CLK_I2C1 R8A7791_CLK_I2C0
>;
clock-output-names =
@@ -506,4 +788,15 @@
clock-output-names = "scifa3", "scifa4", "scifa5";
};
};
+
+ spi: spi@e6b10000 {
+ compatible = "renesas,qspi-r8a7791", "renesas,qspi";
+ reg = <0 0xe6b10000 0 0x2c>;
+ interrupts = <0 184 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp9_clks R8A7791_CLK_QSPI_MOD>;
+ num-cs = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
};
diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
index be5d2b09a363..4d4dfbb59f4b 100644
--- a/arch/arm/boot/dts/rk3066a.dtsi
+++ b/arch/arm/boot/dts/rk3066a.dtsi
@@ -64,6 +64,19 @@
clock-names = "timer", "pclk";
};
+ sram: sram@10080000 {
+ compatible = "mmio-sram";
+ reg = <0x10080000 0x10000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x10080000 0x10000>;
+
+ smp-sram@0 {
+ compatible = "rockchip,rk3066-smp-sram";
+ reg = <0x0 0x50>;
+ };
+ };
+
pinctrl@20008000 {
compatible = "rockchip,rk3066a-pinctrl";
reg = <0x20008000 0x150>;
diff --git a/arch/arm/boot/dts/rk3188.dtsi b/arch/arm/boot/dts/rk3188.dtsi
index 1a26b03b3649..bb36596ea205 100644
--- a/arch/arm/boot/dts/rk3188.dtsi
+++ b/arch/arm/boot/dts/rk3188.dtsi
@@ -60,6 +60,19 @@
interrupts = <GIC_PPI 13 0xf04>;
};
+ sram: sram@10080000 {
+ compatible = "mmio-sram";
+ reg = <0x10080000 0x8000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x10080000 0x8000>;
+
+ smp-sram@0 {
+ compatible = "rockchip,rk3066-smp-sram";
+ reg = <0x0 0x50>;
+ };
+ };
+
pinctrl@20008000 {
compatible = "rockchip,rk3188-pinctrl";
reg = <0x20008000 0xa0>,
diff --git a/arch/arm/boot/dts/rk3xxx.dtsi b/arch/arm/boot/dts/rk3xxx.dtsi
index 0fcbcfd67de2..26e5a968d49d 100644
--- a/arch/arm/boot/dts/rk3xxx.dtsi
+++ b/arch/arm/boot/dts/rk3xxx.dtsi
@@ -26,6 +26,16 @@
compatible = "simple-bus";
ranges;
+ scu@1013c000 {
+ compatible = "arm,cortex-a9-scu";
+ reg = <0x1013c000 0x100>;
+ };
+
+ pmu@20004000 {
+ compatible = "rockchip,rk3066-pmu";
+ reg = <0x20004000 0x100>;
+ };
+
gic: interrupt-controller@1013d000 {
compatible = "arm,cortex-a9-gic";
interrupt-controller;
diff --git a/arch/arm/boot/dts/sama5d3.dtsi b/arch/arm/boot/dts/sama5d3.dtsi
index 3d5faf85f51b..eabcfdbb403a 100644
--- a/arch/arm/boot/dts/sama5d3.dtsi
+++ b/arch/arm/boot/dts/sama5d3.dtsi
@@ -239,7 +239,9 @@
};
adc0: adc@f8018000 {
- compatible = "atmel,at91sam9260-adc";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "atmel,at91sam9x5-adc";
reg = <0xf8018000 0x100>;
interrupts = <29 IRQ_TYPE_LEVEL_HIGH 5>;
pinctrl-names = "default";
@@ -261,52 +263,39 @@
clocks = <&adc_clk>,
<&adc_op_clk>;
clock-names = "adc_clk", "adc_op_clk";
- atmel,adc-channel-base = <0x50>;
atmel,adc-channels-used = <0xfff>;
- atmel,adc-drdy-mask = <0x1000000>;
- atmel,adc-num-channels = <12>;
atmel,adc-startup-time = <40>;
- atmel,adc-status-register = <0x30>;
- atmel,adc-trigger-register = <0xc0>;
- atmel,adc-use-external;
+ atmel,adc-use-external-triggers;
atmel,adc-vref = <3000>;
atmel,adc-res = <10 12>;
atmel,adc-res-names = "lowres", "highres";
status = "disabled";
trigger@0 {
+ reg = <0>;
trigger-name = "external-rising";
trigger-value = <0x1>;
trigger-external;
};
trigger@1 {
+ reg = <1>;
trigger-name = "external-falling";
trigger-value = <0x2>;
trigger-external;
};
trigger@2 {
+ reg = <2>;
trigger-name = "external-any";
trigger-value = <0x3>;
trigger-external;
};
trigger@3 {
+ reg = <3>;
trigger-name = "continuous";
trigger-value = <0x6>;
};
};
- tsadcc: tsadcc@f8018000 {
- compatible = "atmel,at91sam9x5-tsadcc";
- reg = <0xf8018000 0x4000>;
- interrupts = <29 IRQ_TYPE_LEVEL_HIGH 5>;
- atmel,tsadcc_clock = <300000>;
- atmel,filtering_average = <0x03>;
- atmel,pendet_debounce = <0x08>;
- atmel,pendet_sensitivity = <0x02>;
- atmel,ts_sample_hold_time = <0x0a>;
- status = "disabled";
- };
-
i2c2: i2c@f801c000 {
compatible = "atmel,at91sam9x5-i2c";
reg = <0xf801c000 0x4000>;
@@ -1256,6 +1245,7 @@
interrupts = <5 IRQ_TYPE_LEVEL_HIGH 6>;
atmel,nand-addr-offset = <21>;
atmel,nand-cmd-offset = <22>;
+ atmel,nand-has-dma;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_nand0_ale_cle>;
atmel,pmecc-lookup-table-offset = <0x0 0x8000>;
diff --git a/arch/arm/boot/dts/sama5d3xdm.dtsi b/arch/arm/boot/dts/sama5d3xdm.dtsi
index f9bdde542ced..035ab72b3990 100644
--- a/arch/arm/boot/dts/sama5d3xdm.dtsi
+++ b/arch/arm/boot/dts/sama5d3xdm.dtsi
@@ -23,10 +23,8 @@
};
adc0: adc@f8018000 {
- status = "disabled";
- };
-
- tsadcc: tsadcc@f8018000 {
+ atmel,adc-ts-wires = <4>;
+ atmel,adc-ts-pressure-threshold = <10000>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi
index 537f1a5c07f5..56fc214e6d2c 100644
--- a/arch/arm/boot/dts/socfpga.dtsi
+++ b/arch/arm/boot/dts/socfpga.dtsi
@@ -92,7 +92,12 @@
#address-cells = <1>;
#size-cells = <0>;
- osc: osc1 {
+ osc1: osc1 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ };
+
+ osc2: osc2 {
#clock-cells = <0>;
compatible = "fixed-clock";
};
@@ -100,7 +105,11 @@
f2s_periph_ref_clk: f2s_periph_ref_clk {
#clock-cells = <0>;
compatible = "fixed-clock";
- clock-frequency = <10000000>;
+ };
+
+ f2s_sdram_ref_clk: f2s_sdram_ref_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
};
main_pll: main_pll {
@@ -108,7 +117,7 @@
#size-cells = <0>;
#clock-cells = <0>;
compatible = "altr,socfpga-pll-clock";
- clocks = <&osc>;
+ clocks = <&osc1>;
reg = <0x40>;
mpuclk: mpuclk {
@@ -162,7 +171,7 @@
#size-cells = <0>;
#clock-cells = <0>;
compatible = "altr,socfpga-pll-clock";
- clocks = <&osc>;
+ clocks = <&osc1>, <&osc2>, <&f2s_periph_ref_clk>;
reg = <0x80>;
emac0_clk: emac0_clk {
@@ -213,7 +222,7 @@
#size-cells = <0>;
#clock-cells = <0>;
compatible = "altr,socfpga-pll-clock";
- clocks = <&osc>;
+ clocks = <&osc1>, <&osc2>, <&f2s_sdram_ref_clk>;
reg = <0xC0>;
ddr_dqs_clk: ddr_dqs_clk {
@@ -415,6 +424,7 @@
compatible = "altr,socfpga-gate-clk";
clocks = <&f2s_periph_ref_clk>, <&main_nand_sdmmc_clk>, <&per_nand_mmc_clk>;
clk-gate = <0xa0 8>;
+ clk-phase = <0 135>;
};
nand_x_clk: nand_x_clk {
@@ -443,6 +453,7 @@
gmac0: ethernet@ff700000 {
compatible = "altr,socfpga-stmmac", "snps,dwmac-3.70a", "snps,dwmac";
+ altr,sysmgr-syscon = <&sysmgr 0x60 0>;
reg = <0xff700000 0x2000>;
interrupts = <0 115 4>;
interrupt-names = "macirq";
@@ -454,6 +465,7 @@
gmac1: ethernet@ff702000 {
compatible = "altr,socfpga-stmmac", "snps,dwmac-3.70a", "snps,dwmac";
+ altr,sysmgr-syscon = <&sysmgr 0x60 2>;
reg = <0xff702000 0x2000>;
interrupts = <0 120 4>;
interrupt-names = "macirq";
@@ -473,6 +485,17 @@
arm,data-latency = <2 1 1>;
};
+ mmc: dwmmc0@ff704000 {
+ compatible = "altr,socfpga-dw-mshc";
+ reg = <0xff704000 0x1000>;
+ interrupts = <0 139 4>;
+ fifo-depth = <0x400>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&l4_mp_clk>, <&sdmmc_clk>;
+ clock-names = "biu", "ciu";
+ };
+
/* Local timer */
timer@fffec600 {
compatible = "arm,cortex-a9-twd-timer";
@@ -526,9 +549,9 @@
reg = <0xffd05000 0x1000>;
};
- sysmgr@ffd08000 {
- compatible = "altr,sys-mgr";
- reg = <0xffd08000 0x4000>;
- };
+ sysmgr: sysmgr@ffd08000 {
+ compatible = "altr,sys-mgr", "syscon";
+ reg = <0xffd08000 0x4000>;
+ };
};
};
diff --git a/arch/arm/boot/dts/socfpga_arria5.dtsi b/arch/arm/boot/dts/socfpga_arria5.dtsi
index a85b4043f888..6c87b7070ca7 100644
--- a/arch/arm/boot/dts/socfpga_arria5.dtsi
+++ b/arch/arm/boot/dts/socfpga_arria5.dtsi
@@ -27,6 +27,17 @@
};
};
+ dwmmc0@ff704000 {
+ num-slots = <1>;
+ supports-highspeed;
+ broken-cd;
+
+ slot@0 {
+ reg = <0>;
+ bus-width = <4>;
+ };
+ };
+
serial0@ffc02000 {
clock-frequency = <100000000>;
};
diff --git a/arch/arm/boot/dts/socfpga_arria5_socdk.dts b/arch/arm/boot/dts/socfpga_arria5_socdk.dts
index 5beffb2265f4..a87ee1c07661 100644
--- a/arch/arm/boot/dts/socfpga_arria5_socdk.dts
+++ b/arch/arm/boot/dts/socfpga_arria5_socdk.dts
@@ -37,4 +37,25 @@
*/
ethernet0 = &gmac1;
};
+
+ aliases {
+ /* this allow the ethaddr uboot environmnet variable contents
+ * to be added to the gmac1 device tree blob.
+ */
+ ethernet0 = &gmac1;
+ };
+};
+
+&gmac1 {
+ status = "okay";
+ phy-mode = "rgmii";
+
+ rxd0-skew-ps = <0>;
+ rxd1-skew-ps = <0>;
+ rxd2-skew-ps = <0>;
+ rxd3-skew-ps = <0>;
+ txen-skew-ps = <0>;
+ txc-skew-ps = <2600>;
+ rxdv-skew-ps = <0>;
+ rxc-skew-ps = <2000>;
};
diff --git a/arch/arm/boot/dts/socfpga_cyclone5.dtsi b/arch/arm/boot/dts/socfpga_cyclone5.dtsi
index a8716f6dbe2e..ca41b0ebf461 100644
--- a/arch/arm/boot/dts/socfpga_cyclone5.dtsi
+++ b/arch/arm/boot/dts/socfpga_cyclone5.dtsi
@@ -28,6 +28,17 @@
};
};
+ dwmmc0@ff704000 {
+ num-slots = <1>;
+ supports-highspeed;
+ broken-cd;
+
+ slot@0 {
+ reg = <0>;
+ bus-width = <4>;
+ };
+ };
+
ethernet@ff702000 {
phy-mode = "rgmii";
phy-addr = <0xffffffff>; /* probe for phy addr */
diff --git a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts
index 2ee52ab8cabb..ae16d975196d 100644
--- a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts
+++ b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts
@@ -38,3 +38,17 @@
ethernet0 = &gmac1;
};
};
+
+&gmac1 {
+ status = "okay";
+ phy-mode = "rgmii";
+
+ rxd0-skew-ps = <0>;
+ rxd1-skew-ps = <0>;
+ rxd2-skew-ps = <0>;
+ rxd3-skew-ps = <0>;
+ txen-skew-ps = <0>;
+ txc-skew-ps = <2600>;
+ rxdv-skew-ps = <0>;
+ rxc-skew-ps = <2000>;
+};
diff --git a/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts b/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts
index 50b99a2c12ae..b79e2a2bf175 100644
--- a/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts
+++ b/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts
@@ -30,8 +30,25 @@
device_type = "memory";
reg = <0x0 0x40000000>; /* 1GB */
};
+
+ aliases {
+ /* this allow the ethaddr uboot environmnet variable contents
+ * to be added to the gmac1 device tree blob.
+ */
+ ethernet0 = &gmac1;
+ };
};
&gmac1 {
status = "okay";
+ phy-mode = "rgmii";
+
+ rxd0-skew-ps = <0>;
+ rxd1-skew-ps = <0>;
+ rxd2-skew-ps = <0>;
+ rxd3-skew-ps = <0>;
+ txen-skew-ps = <0>;
+ txc-skew-ps = <2600>;
+ rxdv-skew-ps = <0>;
+ rxc-skew-ps = <2000>;
};
diff --git a/arch/arm/boot/dts/socfpga_vt.dts b/arch/arm/boot/dts/socfpga_vt.dts
index d1ec0cab2dee..87d6f759a9c1 100644
--- a/arch/arm/boot/dts/socfpga_vt.dts
+++ b/arch/arm/boot/dts/socfpga_vt.dts
@@ -41,6 +41,17 @@
};
};
+ dwmmc0@ff704000 {
+ num-slots = <1>;
+ supports-highspeed;
+ broken-cd;
+
+ slot@0 {
+ reg = <0>;
+ bus-width = <4>;
+ };
+ };
+
ethernet@ff700000 {
phy-mode = "gmii";
status = "okay";
@@ -75,3 +86,8 @@
};
};
};
+
+&gmac0 {
+ status = "okay";
+ phy-mode = "gmii";
+};
diff --git a/arch/arm/boot/dts/ste-dbx5x0.dtsi b/arch/arm/boot/dts/ste-dbx5x0.dtsi
index e0853ea02df2..e41eedca3ce3 100644
--- a/arch/arm/boot/dts/ste-dbx5x0.dtsi
+++ b/arch/arm/boot/dts/ste-dbx5x0.dtsi
@@ -705,7 +705,7 @@
#address-cells = <1>;
#size-cells = <0>;
clocks = <&prcc_kclk 3 1>, <&prcc_pclk 3 1>;
- clock-names = "ssp0clk", "apb_pclk";
+ clock-names = "SSPCLK", "apb_pclk";
dmas = <&dma 8 0 0x2>, /* Logical - DevToMem */
<&dma 8 0 0x0>; /* Logical - MemToDev */
dma-names = "rx", "tx";
@@ -718,7 +718,7 @@
#address-cells = <1>;
#size-cells = <0>;
clocks = <&prcc_kclk 3 2>, <&prcc_pclk 3 2>;
- clock-names = "ssp1clk", "apb_pclk";
+ clock-names = "SSPCLK", "apb_pclk";
dmas = <&dma 9 0 0x2>, /* Logical - DevToMem */
<&dma 9 0 0x0>; /* Logical - MemToDev */
dma-names = "rx", "tx";
@@ -732,7 +732,7 @@
#size-cells = <0>;
/* Same clock wired to kernel and pclk */
clocks = <&prcc_pclk 2 8>, <&prcc_pclk 2 8>;
- clock-names = "spi0clk", "apb_pclk";
+ clock-names = "SSPCLK", "apb_pclk";
dmas = <&dma 0 0 0x2>, /* Logical - DevToMem */
<&dma 0 0 0x0>; /* Logical - MemToDev */
dma-names = "rx", "tx";
@@ -746,7 +746,7 @@
#size-cells = <0>;
/* Same clock wired to kernel and pclk */
clocks = <&prcc_pclk 2 2>, <&prcc_pclk 2 2>;
- clock-names = "spi1clk", "apb_pclk";
+ clock-names = "SSPCLK", "apb_pclk";
dmas = <&dma 35 0 0x2>, /* Logical - DevToMem */
<&dma 35 0 0x0>; /* Logical - MemToDev */
dma-names = "rx", "tx";
@@ -760,7 +760,7 @@
#size-cells = <0>;
/* Same clock wired to kernel and pclk */
clocks = <&prcc_pclk 2 1>, <&prcc_pclk 2 1>;
- clock-names = "spi2clk", "apb_pclk";
+ clock-names = "SSPCLK", "apb_pclk";
dmas = <&dma 33 0 0x2>, /* Logical - DevToMem */
<&dma 33 0 0x0>; /* Logical - MemToDev */
dma-names = "rx", "tx";
@@ -774,7 +774,7 @@
#size-cells = <0>;
/* Same clock wired to kernel and pclk */
clocks = <&prcc_pclk 1 7>, <&prcc_pclk 1 7>;
- clock-names = "spi3clk", "apb_pclk";
+ clock-names = "SSPCLK", "apb_pclk";
dmas = <&dma 40 0 0x2>, /* Logical - DevToMem */
<&dma 40 0 0x0>; /* Logical - MemToDev */
dma-names = "rx", "tx";
diff --git a/arch/arm/boot/dts/ste-href-ab8500.dtsi b/arch/arm/boot/dts/ste-href-ab8500.dtsi
new file mode 100644
index 000000000000..30f8601da323
--- /dev/null
+++ b/arch/arm/boot/dts/ste-href-ab8500.dtsi
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2014 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/ {
+ soc {
+ prcmu@80157000 {
+ ab8500 {
+ ab8500-gpio {
+ /* Hog a few default settings */
+ pinctrl-names = "default";
+ pinctrl-0 = <&gpio2_default_mode>,
+ <&gpio4_default_mode>,
+ <&gpio10_default_mode>,
+ <&gpio11_default_mode>,
+ <&gpio12_default_mode>,
+ <&gpio13_default_mode>,
+ <&gpio16_default_mode>,
+ <&gpio24_default_mode>,
+ <&gpio25_default_mode>,
+ <&gpio36_default_mode>,
+ <&gpio37_default_mode>,
+ <&gpio38_default_mode>,
+ <&gpio39_default_mode>,
+ <&gpio42_default_mode>,
+ <&gpio26_default_mode>,
+ <&gpio35_default_mode>,
+ <&ycbcr_default_mode>,
+ <&pwm_default_mode>,
+ <&adi1_default_mode>,
+ <&usbuicc_default_mode>,
+ <&dmic_default_mode>,
+ <&extcpena_default_mode>,
+ <&modsclsda_default_mode>;
+
+ /*
+ * Pins 2, 4, 10, 11, 12, 13, 16, 24, 25, 36, 37, 38, 39 and 42
+ * are muxed in as GPIO, and configured as INPUT PULL DOWN
+ */
+ gpio2 {
+ gpio2_default_mode: gpio2_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio2_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO2_T9";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio4 {
+ gpio4_default_mode: gpio4_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio4_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO4_W2";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio10 {
+ gpio10_default_mode: gpio10_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio10_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO10_U17";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio11 {
+ gpio11_default_mode: gpio11_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio11_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO11_AA18";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio12 {
+ gpio12_default_mode: gpio12_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio12_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO12_U16";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio13 {
+ gpio13_default_mode: gpio13_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio13_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO13_W17";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio16 {
+ gpio16_default_mode: gpio16_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio16_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO16_F15";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio24 {
+ gpio24_default_mode: gpio24_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio24_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO24_T14";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio25 {
+ gpio25_default_mode: gpio25_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio25_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO25_R16";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio36 {
+ gpio36_default_mode: gpio36_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio36_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO36_A17";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio37 {
+ gpio37_default_mode: gpio37_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio37_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO37_E15";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio38 {
+ gpio38_default_mode: gpio38_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio38_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO38_C17";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio39 {
+ gpio39_default_mode: gpio39_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio39_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO39_E16";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio42 {
+ gpio42_default_mode: gpio42_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio42_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO42_U2";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ /*
+ * Pins 26 and 35 muxed in as GPIO, and configured as OUTPUT LOW
+ */
+ gpio26 {
+ gpio26_default_mode: gpio26_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio26_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO26_M16";
+ output-low;
+ };
+ };
+ };
+ gpio35 {
+ gpio35_default_mode: gpio35_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio35_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO35_W15";
+ output-low;
+ };
+ };
+ };
+ /*
+ * This sets up the YCBCR connector pins, i.e. analog video out.
+ * Set as input with no bias.
+ */
+ ycbcr {
+ ycbcr_default_mode: ycbcr_default {
+ default_mux {
+ ste,function = "ycbcr";
+ ste,pins = "ycbcr0123_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO6_Y18",
+ "GPIO7_AA20",
+ "GPIO8_W18",
+ "GPIO9_AA19";
+ input-enable;
+ bias-disable;
+ };
+ };
+ };
+ /* This sets up the PWM pins 14 and 15 */
+ pwm {
+ pwm_default_mode: pwm_default {
+ default_mux {
+ ste,function = "pwmout";
+ ste,pins = "pwmout1_d_1", "pwmout2_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO14_F14",
+ "GPIO15_B17";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ /* This sets up audio interface 1 */
+ adi1 {
+ adi1_default_mode: adi1_default {
+ default_mux {
+ ste,function = "adi1";
+ ste,pins = "adi1_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO17_P5",
+ "GPIO18_R5",
+ "GPIO19_U5",
+ "GPIO20_T5";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ /* This sets up the USB UICC pins */
+ usbuicc {
+ usbuicc_default_mode: usbuicc_default {
+ default_mux {
+ ste,function = "usbuicc";
+ ste,pins = "usbuicc_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO21_H19",
+ "GPIO22_G20",
+ "GPIO23_G19";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ /* This sets up the microphone pins */
+ dmic {
+ dmic_default_mode: dmic_default {
+ default_mux {
+ ste,function = "dmic";
+ ste,pins = "dmic12_d_1",
+ "dmic34_d_1",
+ "dmic56_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO27_J6",
+ "GPIO28_K6",
+ "GPIO29_G6",
+ "GPIO30_H6",
+ "GPIO31_F5",
+ "GPIO32_G5";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ extcpena {
+ extcpena_default_mode: extcpena_default {
+ default_mux {
+ ste,function = "extcpena";
+ ste,pins = "extcpena_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO34_R17";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ /* Modem I2C setup (SCL and SDA pins) */
+ modsclsda {
+ modsclsda_default_mode: modsclsda_default {
+ default_mux {
+ ste,function = "modsclsda";
+ ste,pins = "modsclsda_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO40_T19",
+ "GPIO41_U19";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ /*
+ * Clock output pins associated with regulators.
+ */
+ sysclkreq2 {
+ sysclkreq2_default_mode: sysclkreq2_default {
+ default_mux {
+ ste,function = "sysclkreq";
+ ste,pins = "sysclkreq2_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO1_T10";
+ input-enable;
+ bias-disable;
+ };
+ };
+ sysclkreq2_sleep_mode: sysclkreq2_sleep {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio1_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO1_T10";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ sysclkreq4 {
+ sysclkreq4_default_mode: sysclkreq4_default {
+ default_mux {
+ ste,function = "sysclkreq";
+ ste,pins = "sysclkreq4_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO3_U9";
+ input-enable;
+ bias-disable;
+ };
+ };
+ sysclkreq4_sleep_mode: sysclkreq4_sleep {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio3_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO3_U9";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/ste-href-ab8505.dtsi b/arch/arm/boot/dts/ste-href-ab8505.dtsi
new file mode 100644
index 000000000000..6006d62086a2
--- /dev/null
+++ b/arch/arm/boot/dts/ste-href-ab8505.dtsi
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2014 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/ {
+ soc {
+ prcmu@80157000 {
+ ab8505 {
+ ab8505-gpio {
+ /* Hog a few default settings */
+ pinctrl-names = "default";
+ pinctrl-0 = <&gpio2_default_mode>,
+ <&gpio10_default_mode>,
+ <&gpio11_default_mode>,
+ <&gpio13_default_mode>,
+ <&gpio34_default_mode>,
+ <&gpio50_default_mode>,
+ <&pwm_default_mode>,
+ <&adi2_default_mode>,
+ <&modsclsda_default_mode>,
+ <&resethw_default_mode>,
+ <&service_default_mode>;
+
+ /*
+ * Pins 2, 10, 11, 13, 34 and 50
+ * are muxed in as GPIO, and configured as INPUT PULL DOWN
+ */
+ gpio2 {
+ gpio2_default_mode: gpio2_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio2_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO2_R5";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio10 {
+ gpio10_default_mode: gpio10_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio10_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO10_B16";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio11 {
+ gpio11_default_mode: gpio11_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio11_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO11_B17";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio13 {
+ gpio13_default_mode: gpio13_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio13_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO13_D17";
+ input-enable;
+ bias-disable;
+ };
+ };
+ };
+ gpio34 {
+ gpio34_default_mode: gpio34_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio34_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO34_H14";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ gpio50 {
+ gpio50_default_mode: gpio50_default {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio50_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO50_L4";
+ input-enable;
+ bias-disable;
+ };
+ };
+ };
+ /* This sets up the PWM pin 14 */
+ pwm {
+ pwm_default_mode: pwm_default {
+ default_mux {
+ ste,function = "pwmout";
+ ste,pins = "pwmout1_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO14_C16";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ /* This sets up audio interface 2 */
+ adi2 {
+ adi2_default_mode: adi2_default {
+ default_mux {
+ ste,function = "adi2";
+ ste,pins = "adi2_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO17_P2",
+ "GPIO18_N3",
+ "GPIO19_T1",
+ "GPIO20_P3";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ /* Modem I2C setup (SCL and SDA pins) */
+ modsclsda {
+ modsclsda_default_mode: modsclsda_default {
+ default_mux {
+ ste,function = "modsclsda";
+ ste,pins = "modsclsda_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO40_J15",
+ "GPIO41_J14";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ resethw {
+ resethw_default_mode: resethw_default {
+ default_mux {
+ ste,function = "resethw";
+ ste,pins = "resethw_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO52_D16";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ service {
+ service_default_mode: service_default {
+ default_mux {
+ ste,function = "service";
+ ste,pins = "service_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO53_D15";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ /*
+ * Clock output pins associated with regulators.
+ */
+ sysclkreq2 {
+ sysclkreq2_default_mode: sysclkreq2_default {
+ default_mux {
+ ste,function = "sysclkreq";
+ ste,pins = "sysclkreq2_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO1_N4";
+ input-enable;
+ bias-disable;
+ };
+ };
+ sysclkreq2_sleep_mode: sysclkreq2_sleep {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio1_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO1_N4";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ sysclkreq4 {
+ sysclkreq4_default_mode: sysclkreq4_default {
+ default_mux {
+ ste,function = "sysclkreq";
+ ste,pins = "sysclkreq4_d_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO3_P5";
+ input-enable;
+ bias-disable;
+ };
+ };
+ sysclkreq4_sleep_mode: sysclkreq4_sleep {
+ default_mux {
+ ste,function = "gpio";
+ ste,pins = "gpio3_a_1";
+ };
+ default_cfg {
+ ste,pins = "GPIO3_P5";
+ input-enable;
+ bias-pull-down;
+ };
+ };
+ };
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/ste-hrefprev60.dtsi b/arch/arm/boot/dts/ste-hrefprev60.dtsi
index 40f0ecdf9303..abc762e24fcb 100644
--- a/arch/arm/boot/dts/ste-hrefprev60.dtsi
+++ b/arch/arm/boot/dts/ste-hrefprev60.dtsi
@@ -12,6 +12,7 @@
*/
#include "ste-dbx5x0.dtsi"
+#include "ste-href-ab8500.dtsi"
#include "ste-href.dtsi"
/ {
diff --git a/arch/arm/boot/dts/ste-hrefv60plus.dtsi b/arch/arm/boot/dts/ste-hrefv60plus.dtsi
index 3b6d1181939b..c2341061b943 100644
--- a/arch/arm/boot/dts/ste-hrefv60plus.dtsi
+++ b/arch/arm/boot/dts/ste-hrefv60plus.dtsi
@@ -10,6 +10,7 @@
*/
#include "ste-dbx5x0.dtsi"
+#include "ste-href-ab8500.dtsi"
#include "ste-href.dtsi"
/ {
diff --git a/arch/arm/boot/dts/ste-snowball.dts b/arch/arm/boot/dts/ste-snowball.dts
index 97d5d21b7db7..a2f632d0be2a 100644
--- a/arch/arm/boot/dts/ste-snowball.dts
+++ b/arch/arm/boot/dts/ste-snowball.dts
@@ -11,6 +11,7 @@
/dts-v1/;
#include "ste-dbx5x0.dtsi"
+#include "ste-href-ab8500.dtsi"
#include "ste-href-family-pinctrl.dtsi"
/ {
diff --git a/arch/arm/boot/dts/ste-u300.dts b/arch/arm/boot/dts/ste-u300.dts
index a9da4800daf0..6fe688e9e4da 100644
--- a/arch/arm/boot/dts/ste-u300.dts
+++ b/arch/arm/boot/dts/ste-u300.dts
@@ -457,7 +457,7 @@
interrupt-parent = <&vica>;
interrupts = <23>;
clocks = <&spi_clk>, <&spi_clk>;
- clock-names = "apb_pclk", "spi_clk";
+ clock-names = "SSPCLK", "apb_pclk";
dmas = <&dmac 27 &dmac 28>;
dma-names = "tx", "rx";
num-cs = <3>;
diff --git a/arch/arm/boot/dts/stih415-clock.dtsi b/arch/arm/boot/dts/stih415-clock.dtsi
index 174c799df741..d047dbc28d61 100644
--- a/arch/arm/boot/dts/stih415-clock.dtsi
+++ b/arch/arm/boot/dts/stih415-clock.dtsi
@@ -34,5 +34,19 @@
compatible = "fixed-clock";
clock-frequency = <100000000>;
};
+
+ CLKS_GMAC0_PHY: clockgenA1@7 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <25000000>;
+ clock-output-names = "CLKS_GMAC0_PHY";
+ };
+
+ CLKS_ETH1_PHY: clockgenA0@7 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <25000000>;
+ clock-output-names = "CLKS_ETH1_PHY";
+ };
};
};
diff --git a/arch/arm/boot/dts/stih415-pinctrl.dtsi b/arch/arm/boot/dts/stih415-pinctrl.dtsi
index e56449d41481..f09fb10a3791 100644
--- a/arch/arm/boot/dts/stih415-pinctrl.dtsi
+++ b/arch/arm/boot/dts/stih415-pinctrl.dtsi
@@ -7,6 +7,7 @@
* publishhed by the Free Software Foundation.
*/
#include "st-pincfg.h"
+#include <dt-bindings/interrupt-controller/arm-gic.h>
/ {
aliases {
@@ -45,35 +46,49 @@
#size-cells = <1>;
compatible = "st,stih415-sbc-pinctrl";
st,syscfg = <&syscfg_sbc>;
+ reg = <0xfe61f080 0x4>;
+ reg-names = "irqmux";
+ interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-names = "irqmux";
ranges = <0 0xfe610000 0x5000>;
PIO0: gpio@fe610000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0 0x100>;
st,bank-name = "PIO0";
};
PIO1: gpio@fe611000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x1000 0x100>;
st,bank-name = "PIO1";
};
PIO2: gpio@fe612000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x2000 0x100>;
st,bank-name = "PIO2";
};
PIO3: gpio@fe613000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x3000 0x100>;
st,bank-name = "PIO3";
};
PIO4: gpio@fe614000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x4000 0x100>;
st,bank-name = "PIO4";
};
@@ -104,6 +119,64 @@
};
};
};
+
+ rc{
+ pinctrl_ir: ir0 {
+ st,pins {
+ ir = <&PIO4 0 ALT2 IN>;
+ };
+ };
+ };
+
+ gmac1 {
+ pinctrl_mii1: mii1 {
+ st,pins {
+ txd0 = <&PIO0 0 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txd1 = <&PIO0 1 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txd2 = <&PIO0 2 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txd3 = <&PIO0 3 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txer = <&PIO0 4 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txen = <&PIO0 5 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txclk = <&PIO0 6 ALT1 IN NICLK 0 CLK_A>;
+ col = <&PIO0 7 ALT1 IN BYPASS 1000>;
+ mdio = <&PIO1 0 ALT1 OUT BYPASS 0>;
+ mdc = <&PIO1 1 ALT1 OUT NICLK 0 CLK_A>;
+ crs = <&PIO1 2 ALT1 IN BYPASS 1000>;
+ mdint = <&PIO1 3 ALT1 IN BYPASS 0>;
+ rxd0 = <&PIO1 4 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+ rxd1 = <&PIO1 5 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+ rxd2 = <&PIO1 6 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+ rxd3 = <&PIO1 7 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+ rxdv = <&PIO2 0 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+ rx_er = <&PIO2 1 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+ rxclk = <&PIO2 2 ALT1 IN NICLK 0 CLK_A>;
+ phyclk = <&PIO2 3 ALT1 IN NICLK 1000 CLK_A>;
+ };
+ };
+
+ pinctrl_rgmii1: rgmii1-0 {
+ st,pins {
+ txd0 = <&PIO0 0 ALT1 OUT DE_IO 1000 CLK_A>;
+ txd1 = <&PIO0 1 ALT1 OUT DE_IO 1000 CLK_A>;
+ txd2 = <&PIO0 2 ALT1 OUT DE_IO 1000 CLK_A>;
+ txd3 = <&PIO0 3 ALT1 OUT DE_IO 1000 CLK_A>;
+ txen = <&PIO0 5 ALT1 OUT DE_IO 0 CLK_A>;
+ txclk = <&PIO0 6 ALT1 IN NICLK 0 CLK_A>;
+ mdio = <&PIO1 0 ALT1 OUT BYPASS 0>;
+ mdc = <&PIO1 1 ALT1 OUT NICLK 0 CLK_A>;
+ rxd0 = <&PIO1 4 ALT1 IN DE_IO 0 CLK_A>;
+ rxd1 = <&PIO1 5 ALT1 IN DE_IO 0 CLK_A>;
+ rxd2 = <&PIO1 6 ALT1 IN DE_IO 0 CLK_A>;
+ rxd3 = <&PIO1 7 ALT1 IN DE_IO 0 CLK_A>;
+
+ rxdv = <&PIO2 0 ALT1 IN DE_IO 500 CLK_A>;
+ rxclk = <&PIO2 2 ALT1 IN NICLK 0 CLK_A>;
+ phyclk = <&PIO2 3 ALT4 OUT NICLK 0 CLK_B>;
+
+ clk125= <&PIO3 7 ALT4 IN NICLK 0 CLK_A>;
+ };
+ };
+ };
};
pin-controller-front {
@@ -111,53 +184,73 @@
#size-cells = <1>;
compatible = "st,stih415-front-pinctrl";
st,syscfg = <&syscfg_front>;
+ reg = <0xfee0f080 0x4>;
+ reg-names = "irqmux";
+ interrupts = <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-names = "irqmux";
ranges = <0 0xfee00000 0x8000>;
PIO5: gpio@fee00000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0 0x100>;
st,bank-name = "PIO5";
};
PIO6: gpio@fee01000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x1000 0x100>;
st,bank-name = "PIO6";
};
PIO7: gpio@fee02000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x2000 0x100>;
st,bank-name = "PIO7";
};
PIO8: gpio@fee03000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x3000 0x100>;
st,bank-name = "PIO8";
};
PIO9: gpio@fee04000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x4000 0x100>;
st,bank-name = "PIO9";
};
PIO10: gpio@fee05000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x5000 0x100>;
st,bank-name = "PIO10";
};
PIO11: gpio@fee06000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x6000 0x100>;
st,bank-name = "PIO11";
};
PIO12: gpio@fee07000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x7000 0x100>;
st,bank-name = "PIO12";
};
@@ -186,41 +279,57 @@
#size-cells = <1>;
compatible = "st,stih415-rear-pinctrl";
st,syscfg = <&syscfg_rear>;
+ reg = <0xfe82f080 0x4>;
+ reg-names = "irqmux";
+ interrupts = <GIC_SPI 182 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-names = "irqmux";
ranges = <0 0xfe820000 0x8000>;
PIO13: gpio@fe820000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0 0x100>;
st,bank-name = "PIO13";
};
PIO14: gpio@fe821000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x1000 0x100>;
st,bank-name = "PIO14";
};
PIO15: gpio@fe822000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x2000 0x100>;
st,bank-name = "PIO15";
};
PIO16: gpio@fe823000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x3000 0x100>;
st,bank-name = "PIO16";
};
PIO17: gpio@fe824000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x4000 0x100>;
st,bank-name = "PIO17";
};
PIO18: gpio@fe825000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x5000 0x100>;
st,bank-name = "PIO18";
};
@@ -233,6 +342,77 @@
};
};
};
+
+ gmac0{
+ pinctrl_mii0: mii0 {
+ st,pins {
+ mdint = <&PIO13 6 ALT2 IN BYPASS 0>;
+ txen = <&PIO13 7 ALT2 OUT SE_NICLK_IO 0 CLK_A>;
+
+ txd0 = <&PIO14 0 ALT2 OUT SE_NICLK_IO 0 CLK_A>;
+ txd1 = <&PIO14 1 ALT2 OUT SE_NICLK_IO 0 CLK_A>;
+ txd2 = <&PIO14 2 ALT2 OUT SE_NICLK_IO 0 CLK_B>;
+ txd3 = <&PIO14 3 ALT2 OUT SE_NICLK_IO 0 CLK_B>;
+
+ txclk = <&PIO15 0 ALT2 IN NICLK 0 CLK_A>;
+ txer = <&PIO15 1 ALT2 OUT SE_NICLK_IO 0 CLK_A>;
+ crs = <&PIO15 2 ALT2 IN BYPASS 1000>;
+ col = <&PIO15 3 ALT2 IN BYPASS 1000>;
+ mdio = <&PIO15 4 ALT2 OUT BYPASS 3000>;
+ mdc = <&PIO15 5 ALT2 OUT NICLK 0 CLK_B>;
+
+ rxd0 = <&PIO16 0 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rxd1 = <&PIO16 1 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rxd2 = <&PIO16 2 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rxd3 = <&PIO16 3 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rxdv = <&PIO15 6 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rx_er = <&PIO15 7 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rxclk = <&PIO17 0 ALT2 IN NICLK 0 CLK_A>;
+ phyclk = <&PIO13 5 ALT2 OUT NICLK 1000 CLK_A>;
+
+ };
+ };
+
+ pinctrl_gmii0: gmii0 {
+ st,pins {
+ mdint = <&PIO13 6 ALT2 IN BYPASS 0>;
+ mdio = <&PIO15 4 ALT2 OUT BYPASS 3000>;
+ mdc = <&PIO15 5 ALT2 OUT NICLK 0 CLK_B>;
+ txen = <&PIO13 7 ALT2 OUT SE_NICLK_IO 3000 CLK_A>;
+
+ txd0 = <&PIO14 0 ALT2 OUT SE_NICLK_IO 3000 CLK_A>;
+ txd1 = <&PIO14 1 ALT2 OUT SE_NICLK_IO 3000 CLK_A>;
+ txd2 = <&PIO14 2 ALT2 OUT SE_NICLK_IO 3000 CLK_B>;
+ txd3 = <&PIO14 3 ALT2 OUT SE_NICLK_IO 3000 CLK_B>;
+ txd4 = <&PIO14 4 ALT2 OUT SE_NICLK_IO 3000 CLK_B>;
+ txd5 = <&PIO14 5 ALT2 OUT SE_NICLK_IO 3000 CLK_B>;
+ txd6 = <&PIO14 6 ALT2 OUT SE_NICLK_IO 3000 CLK_B>;
+ txd7 = <&PIO14 7 ALT2 OUT SE_NICLK_IO 3000 CLK_B>;
+
+ txclk = <&PIO15 0 ALT2 IN NICLK 0 CLK_A>;
+ txer = <&PIO15 1 ALT2 OUT SE_NICLK_IO 3000 CLK_A>;
+ crs = <&PIO15 2 ALT2 IN BYPASS 1000>;
+ col = <&PIO15 3 ALT2 IN BYPASS 1000>;
+ rxdv = <&PIO15 6 ALT2 IN SE_NICLK_IO 1500 CLK_A>;
+ rx_er = <&PIO15 7 ALT2 IN SE_NICLK_IO 1500 CLK_A>;
+
+ rxd0 = <&PIO16 0 ALT2 IN SE_NICLK_IO 1500 CLK_A>;
+ rxd1 = <&PIO16 1 ALT2 IN SE_NICLK_IO 1500 CLK_A>;
+ rxd2 = <&PIO16 2 ALT2 IN SE_NICLK_IO 1500 CLK_A>;
+ rxd3 = <&PIO16 3 ALT2 IN SE_NICLK_IO 1500 CLK_A>;
+ rxd4 = <&PIO16 4 ALT2 IN SE_NICLK_IO 1500 CLK_A>;
+ rxd5 = <&PIO16 5 ALT2 IN SE_NICLK_IO 1500 CLK_A>;
+ rxd6 = <&PIO16 6 ALT2 IN SE_NICLK_IO 1500 CLK_A>;
+ rxd7 = <&PIO16 7 ALT2 IN SE_NICLK_IO 1500 CLK_A>;
+
+ rxclk = <&PIO17 0 ALT2 IN NICLK 0 CLK_A>;
+ clk125 = <&PIO17 6 ALT1 IN NICLK 0 CLK_A>;
+ phyclk = <&PIO13 5 ALT4 OUT NICLK 0 CLK_B>;
+
+
+ };
+ };
+ };
};
pin-controller-left {
@@ -240,23 +420,33 @@
#size-cells = <1>;
compatible = "st,stih415-left-pinctrl";
st,syscfg = <&syscfg_left>;
+ reg = <0xfd6bf080 0x4>;
+ reg-names = "irqmux";
+ interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-names = "irqmux";
ranges = <0 0xfd6b0000 0x3000>;
PIO100: gpio@fd6b0000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0 0x100>;
st,bank-name = "PIO100";
};
PIO101: gpio@fd6b1000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x1000 0x100>;
st,bank-name = "PIO101";
};
PIO102: gpio@fd6b2000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x2000 0x100>;
st,bank-name = "PIO102";
};
@@ -267,35 +457,49 @@
#size-cells = <1>;
compatible = "st,stih415-right-pinctrl";
st,syscfg = <&syscfg_right>;
+ reg = <0xfd33f080 0x4>;
+ reg-names = "irqmux";
+ interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-names = "irqmux";
ranges = <0 0xfd330000 0x5000>;
PIO103: gpio@fd330000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0 0x100>;
st,bank-name = "PIO103";
};
PIO104: gpio@fd331000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x1000 0x100>;
st,bank-name = "PIO104";
};
PIO105: gpio@fd332000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x2000 0x100>;
st,bank-name = "PIO105";
};
PIO106: gpio@fd333000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x3000 0x100>;
st,bank-name = "PIO106";
};
PIO107: gpio@fd334000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x4000 0x100>;
st,bank-name = "PIO107";
};
diff --git a/arch/arm/boot/dts/stih415.dtsi b/arch/arm/boot/dts/stih415.dtsi
index d9c7dd1d95a4..d89064c20c8a 100644
--- a/arch/arm/boot/dts/stih415.dtsi
+++ b/arch/arm/boot/dts/stih415.dtsi
@@ -10,6 +10,7 @@
#include "stih415-clock.dtsi"
#include "stih415-pinctrl.dtsi"
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/reset-controller/stih415-resets.h>
/ {
L2: cache-controller {
@@ -28,6 +29,16 @@
ranges;
compatible = "simple-bus";
+ powerdown: powerdown-controller {
+ #reset-cells = <1>;
+ compatible = "st,stih415-powerdown";
+ };
+
+ softreset: softreset-controller {
+ #reset-cells = <1>;
+ compatible = "st,stih415-softreset";
+ };
+
syscfg_sbc: sbc-syscfg@fe600000{
compatible = "st,stih415-sbc-syscfg", "syscon";
reg = <0xfe600000 0xb4>;
@@ -136,5 +147,64 @@
status = "disabled";
};
+
+ ethernet0: dwmac@fe810000 {
+ device_type = "network";
+ compatible = "st,stih415-dwmac", "snps,dwmac", "snps,dwmac-3.610";
+ status = "disabled";
+
+ reg = <0xfe810000 0x8000>, <0x148 0x4>;
+ reg-names = "stmmaceth", "sti-ethconf";
+
+ interrupts = <0 147 0>, <0 148 0>, <0 149 0>;
+ interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
+ resets = <&softreset STIH415_ETH0_SOFTRESET>;
+ reset-names = "stmmaceth";
+
+ snps,pbl = <32>;
+ snps,mixed-burst;
+ snps,force_sf_dma_mode;
+
+ st,syscon = <&syscfg_rear>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_mii0>;
+ clock-names = "stmmaceth";
+ clocks = <&CLKS_GMAC0_PHY>;
+ };
+
+ ethernet1: dwmac@fef08000 {
+ device_type = "network";
+ compatible = "st,stih415-dwmac", "snps,dwmac", "snps,dwmac-3.610";
+ status = "disabled";
+ reg = <0xfef08000 0x8000>, <0x74 0x4>;
+ reg-names = "stmmaceth", "sti-ethconf";
+ interrupts = <0 150 0>, <0 151 0>, <0 152 0>;
+ interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
+
+ snps,pbl = <32>;
+ snps,mixed-burst;
+ snps,force_sf_dma_mode;
+
+ st,syscon = <&syscfg_sbc>;
+
+ resets = <&softreset STIH415_ETH1_SOFTRESET>;
+ reset-names = "stmmaceth";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_mii1>;
+ clock-names = "stmmaceth";
+ clocks = <&CLKS_ETH1_PHY>;
+ };
+
+ rc: rc@fe518000 {
+ compatible = "st,comms-irb";
+ reg = <0xfe518000 0x234>;
+ interrupts = <0 203 0>;
+ clocks = <&CLK_SYSIN>;
+ rx-mode = "infrared";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ir>;
+ resets = <&softreset STIH415_IRB_SOFTRESET>;
+ };
};
};
diff --git a/arch/arm/boot/dts/stih416-clock.dtsi b/arch/arm/boot/dts/stih416-clock.dtsi
index 7026bf1158d8..a6942c75cbbb 100644
--- a/arch/arm/boot/dts/stih416-clock.dtsi
+++ b/arch/arm/boot/dts/stih416-clock.dtsi
@@ -37,5 +37,19 @@
clock-frequency = <100000000>;
clock-output-names = "CLK_S_ICN_REG_0";
};
+
+ CLK_S_GMAC0_PHY: clockgenA1@7 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <25000000>;
+ clock-output-names = "CLK_S_GMAC0_PHY";
+ };
+
+ CLK_S_ETH1_PHY: clockgenA0@7 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <25000000>;
+ clock-output-names = "CLK_S_ETH1_PHY";
+ };
};
};
diff --git a/arch/arm/boot/dts/stih416-pinctrl.dtsi b/arch/arm/boot/dts/stih416-pinctrl.dtsi
index b29ff4ba542c..aeea304086eb 100644
--- a/arch/arm/boot/dts/stih416-pinctrl.dtsi
+++ b/arch/arm/boot/dts/stih416-pinctrl.dtsi
@@ -8,6 +8,7 @@
* publishhed by the Free Software Foundation.
*/
#include "st-pincfg.h"
+#include <dt-bindings/interrupt-controller/arm-gic.h>
/ {
aliases {
@@ -49,46 +50,69 @@
#size-cells = <1>;
compatible = "st,stih416-sbc-pinctrl";
st,syscfg = <&syscfg_sbc>;
+ reg = <0xfe61f080 0x4>;
+ reg-names = "irqmux";
+ interrupts = <GIC_SPI 182 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-names = "irqmux";
ranges = <0 0xfe610000 0x6000>;
PIO0: gpio@fe610000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0 0x100>;
st,bank-name = "PIO0";
};
PIO1: gpio@fe611000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x1000 0x100>;
st,bank-name = "PIO1";
};
PIO2: gpio@fe612000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x2000 0x100>;
st,bank-name = "PIO2";
};
PIO3: gpio@fe613000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x3000 0x100>;
st,bank-name = "PIO3";
};
PIO4: gpio@fe614000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x4000 0x100>;
st,bank-name = "PIO4";
};
PIO40: gpio@fe615000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x5000 0x100>;
st,bank-name = "PIO40";
st,retime-pin-mask = <0x7f>;
};
+ rc{
+ pinctrl_ir: ir0 {
+ st,pins {
+ ir = <&PIO4 0 ALT2 IN>;
+ };
+ };
+ };
sbc_serial1 {
pinctrl_sbc_serial1: sbc_serial1 {
st,pins {
@@ -115,6 +139,58 @@
};
};
};
+
+ gmac1 {
+ pinctrl_mii1: mii1 {
+ st,pins {
+ txd0 = <&PIO0 0 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txd1 = <&PIO0 1 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txd2 = <&PIO0 2 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txd3 = <&PIO0 3 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txer = <&PIO0 4 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txen = <&PIO0 5 ALT1 OUT SE_NICLK_IO 0 CLK_A>;
+ txclk = <&PIO0 6 ALT1 IN NICLK 0 CLK_A>;
+ col = <&PIO0 7 ALT1 IN BYPASS 1000>;
+
+ mdio = <&PIO1 0 ALT1 OUT BYPASS 1500>;
+ mdc = <&PIO1 1 ALT1 OUT NICLK 0 CLK_A>;
+ crs = <&PIO1 2 ALT1 IN BYPASS 1000>;
+ mdint = <&PIO1 3 ALT1 IN BYPASS 0>;
+ rxd0 = <&PIO1 4 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+ rxd1 = <&PIO1 5 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+ rxd2 = <&PIO1 6 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+ rxd3 = <&PIO1 7 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+
+ rxdv = <&PIO2 0 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+ rx_er = <&PIO2 1 ALT1 IN SE_NICLK_IO 0 CLK_A>;
+ rxclk = <&PIO2 2 ALT1 IN NICLK 0 CLK_A>;
+ phyclk = <&PIO2 3 ALT1 OUT NICLK 0 CLK_A>;
+ };
+ };
+ pinctrl_rgmii1: rgmii1-0 {
+ st,pins {
+ txd0 = <&PIO0 0 ALT1 OUT DE_IO 500 CLK_A>;
+ txd1 = <&PIO0 1 ALT1 OUT DE_IO 500 CLK_A>;
+ txd2 = <&PIO0 2 ALT1 OUT DE_IO 500 CLK_A>;
+ txd3 = <&PIO0 3 ALT1 OUT DE_IO 500 CLK_A>;
+ txen = <&PIO0 5 ALT1 OUT DE_IO 0 CLK_A>;
+ txclk = <&PIO0 6 ALT1 IN NICLK 0 CLK_A>;
+
+ mdio = <&PIO1 0 ALT1 OUT BYPASS 0>;
+ mdc = <&PIO1 1 ALT1 OUT NICLK 0 CLK_A>;
+ rxd0 = <&PIO1 4 ALT1 IN DE_IO 500 CLK_A>;
+ rxd1 = <&PIO1 5 ALT1 IN DE_IO 500 CLK_A>;
+ rxd2 = <&PIO1 6 ALT1 IN DE_IO 500 CLK_A>;
+ rxd3 = <&PIO1 7 ALT1 IN DE_IO 500 CLK_A>;
+
+ rxdv = <&PIO2 0 ALT1 IN DE_IO 500 CLK_A>;
+ rxclk = <&PIO2 2 ALT1 IN NICLK 0 CLK_A>;
+ phyclk = <&PIO2 3 ALT4 OUT NICLK 0 CLK_B>;
+
+ clk125= <&PIO3 7 ALT4 IN NICLK 0 CLK_A>;
+ };
+ };
+ };
};
pin-controller-front {
@@ -122,65 +198,89 @@
#size-cells = <1>;
compatible = "st,stih416-front-pinctrl";
st,syscfg = <&syscfg_front>;
+ reg = <0xfee0f080 0x4>;
+ reg-names = "irqmux";
+ interrupts = <GIC_SPI 183 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-names = "irqmux";
ranges = <0 0xfee00000 0x10000>;
PIO5: gpio@fee00000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0 0x100>;
st,bank-name = "PIO5";
};
PIO6: gpio@fee01000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x1000 0x100>;
st,bank-name = "PIO6";
};
PIO7: gpio@fee02000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x2000 0x100>;
st,bank-name = "PIO7";
};
PIO8: gpio@fee03000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x3000 0x100>;
st,bank-name = "PIO8";
};
PIO9: gpio@fee04000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x4000 0x100>;
st,bank-name = "PIO9";
};
PIO10: gpio@fee05000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x5000 0x100>;
st,bank-name = "PIO10";
};
PIO11: gpio@fee06000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x6000 0x100>;
st,bank-name = "PIO11";
};
PIO12: gpio@fee07000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x7000 0x100>;
st,bank-name = "PIO12";
};
PIO30: gpio@fee08000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x8000 0x100>;
st,bank-name = "PIO30";
};
PIO31: gpio@fee09000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x9000 0x100>;
st,bank-name = "PIO31";
};
@@ -210,6 +310,19 @@
};
};
};
+
+ fsm {
+ pinctrl_fsm: fsm {
+ st,pins {
+ spi-fsm-clk = <&PIO12 2 ALT1 OUT>;
+ spi-fsm-cs = <&PIO12 3 ALT1 OUT>;
+ spi-fsm-mosi = <&PIO12 4 ALT1 OUT>;
+ spi-fsm-miso = <&PIO12 5 ALT1 IN>;
+ spi-fsm-hol = <&PIO12 6 ALT1 OUT>;
+ spi-fsm-wp = <&PIO12 7 ALT1 OUT>;
+ };
+ };
+ };
};
pin-controller-rear {
@@ -217,41 +330,57 @@
#size-cells = <1>;
compatible = "st,stih416-rear-pinctrl";
st,syscfg = <&syscfg_rear>;
+ reg = <0xfe82f080 0x4>;
+ reg-names = "irqmux";
+ interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-names = "irqmux";
ranges = <0 0xfe820000 0x6000>;
PIO13: gpio@fe820000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0 0x100>;
st,bank-name = "PIO13";
};
PIO14: gpio@fe821000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x1000 0x100>;
st,bank-name = "PIO14";
};
PIO15: gpio@fe822000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x2000 0x100>;
st,bank-name = "PIO15";
};
PIO16: gpio@fe823000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x3000 0x100>;
st,bank-name = "PIO16";
};
PIO17: gpio@fe824000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x4000 0x100>;
st,bank-name = "PIO17";
};
PIO18: gpio@fe825000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x5000 0x100>;
st,bank-name = "PIO18";
st,retime-pin-mask = <0xf>;
@@ -265,6 +394,63 @@
};
};
};
+
+ gmac0 {
+ pinctrl_mii0: mii0 {
+ st,pins {
+ mdint = <&PIO13 6 ALT2 IN BYPASS 0>;
+ txen = <&PIO13 7 ALT2 OUT SE_NICLK_IO 0 CLK_A>;
+ txd0 = <&PIO14 0 ALT2 OUT SE_NICLK_IO 0 CLK_A>;
+ txd1 = <&PIO14 1 ALT2 OUT SE_NICLK_IO 0 CLK_A>;
+ txd2 = <&PIO14 2 ALT2 OUT SE_NICLK_IO 0 CLK_B>;
+ txd3 = <&PIO14 3 ALT2 OUT SE_NICLK_IO 0 CLK_B>;
+
+ txclk = <&PIO15 0 ALT2 IN NICLK 0 CLK_A>;
+ txer = <&PIO15 1 ALT2 OUT SE_NICLK_IO 0 CLK_A>;
+ crs = <&PIO15 2 ALT2 IN BYPASS 1000>;
+ col = <&PIO15 3 ALT2 IN BYPASS 1000>;
+ mdio= <&PIO15 4 ALT2 OUT BYPASS 1500>;
+ mdc = <&PIO15 5 ALT2 OUT NICLK 0 CLK_B>;
+
+ rxd0 = <&PIO16 0 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rxd1 = <&PIO16 1 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rxd2 = <&PIO16 2 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rxd3 = <&PIO16 3 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rxdv = <&PIO15 6 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rx_er = <&PIO15 7 ALT2 IN SE_NICLK_IO 0 CLK_A>;
+ rxclk = <&PIO17 0 ALT2 IN NICLK 0 CLK_A>;
+ phyclk = <&PIO13 5 ALT2 OUT NICLK 0 CLK_B>;
+ };
+ };
+
+ pinctrl_gmii0: gmii0 {
+ st,pins {
+ };
+ };
+ pinctrl_rgmii0: rgmii0 {
+ st,pins {
+ phyclk = <&PIO13 5 ALT4 OUT NICLK 0 CLK_B>;
+ txen = <&PIO13 7 ALT2 OUT DE_IO 0 CLK_A>;
+ txd0 = <&PIO14 0 ALT2 OUT DE_IO 500 CLK_A>;
+ txd1 = <&PIO14 1 ALT2 OUT DE_IO 500 CLK_A>;
+ txd2 = <&PIO14 2 ALT2 OUT DE_IO 500 CLK_B>;
+ txd3 = <&PIO14 3 ALT2 OUT DE_IO 500 CLK_B>;
+ txclk = <&PIO15 0 ALT2 IN NICLK 0 CLK_A>;
+
+ mdio = <&PIO15 4 ALT2 OUT BYPASS 0>;
+ mdc = <&PIO15 5 ALT2 OUT NICLK 0 CLK_B>;
+
+ rxdv = <&PIO15 6 ALT2 IN DE_IO 500 CLK_A>;
+ rxd0 =<&PIO16 0 ALT2 IN DE_IO 500 CLK_A>;
+ rxd1 =<&PIO16 1 ALT2 IN DE_IO 500 CLK_A>;
+ rxd2 =<&PIO16 2 ALT2 IN DE_IO 500 CLK_A>;
+ rxd3 =<&PIO16 3 ALT2 IN DE_IO 500 CLK_A>;
+ rxclk =<&PIO17 0 ALT2 IN NICLK 0 CLK_A>;
+
+ clk125=<&PIO17 6 ALT1 IN NICLK 0 CLK_A>;
+ };
+ };
+ };
};
pin-controller-fvdp-fe {
@@ -272,23 +458,33 @@
#size-cells = <1>;
compatible = "st,stih416-fvdp-fe-pinctrl";
st,syscfg = <&syscfg_fvdp_fe>;
+ reg = <0xfd6bf080 0x4>;
+ reg-names = "irqmux";
+ interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-names = "irqmux";
ranges = <0 0xfd6b0000 0x3000>;
PIO100: gpio@fd6b0000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0 0x100>;
st,bank-name = "PIO100";
};
PIO101: gpio@fd6b1000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x1000 0x100>;
st,bank-name = "PIO101";
};
PIO102: gpio@fd6b2000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x2000 0x100>;
st,bank-name = "PIO102";
};
@@ -299,29 +495,41 @@
#size-cells = <1>;
compatible = "st,stih416-fvdp-lite-pinctrl";
st,syscfg = <&syscfg_fvdp_lite>;
+ reg = <0xfd33f080 0x4>;
+ reg-names = "irqmux";
+ interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-names = "irqmux";
ranges = <0 0xfd330000 0x5000>;
PIO103: gpio@fd330000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0 0x100>;
st,bank-name = "PIO103";
};
PIO104: gpio@fd331000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x1000 0x100>;
st,bank-name = "PIO104";
};
PIO105: gpio@fd332000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x2000 0x100>;
st,bank-name = "PIO105";
};
PIO106: gpio@fd333000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x3000 0x100>;
st,bank-name = "PIO106";
};
@@ -329,6 +537,8 @@
PIO107: gpio@fd334000 {
gpio-controller;
#gpio-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
reg = <0x4000 0x100>;
st,bank-name = "PIO107";
st,retime-pin-mask = <0xf>;
diff --git a/arch/arm/boot/dts/stih416.dtsi b/arch/arm/boot/dts/stih416.dtsi
index b7ab47b95816..78746d20382e 100644
--- a/arch/arm/boot/dts/stih416.dtsi
+++ b/arch/arm/boot/dts/stih416.dtsi
@@ -10,6 +10,7 @@
#include "stih416-clock.dtsi"
#include "stih416-pinctrl.dtsi"
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/reset-controller/stih416-resets.h>
/ {
L2: cache-controller {
compatible = "arm,pl310-cache";
@@ -27,6 +28,16 @@
ranges;
compatible = "simple-bus";
+ powerdown: powerdown-controller {
+ #reset-cells = <1>;
+ compatible = "st,stih416-powerdown";
+ };
+
+ softreset: softreset-controller {
+ #reset-cells = <1>;
+ compatible = "st,stih416-softreset";
+ };
+
syscfg_sbc:sbc-syscfg@fe600000{
compatible = "st,stih416-sbc-syscfg", "syscon";
reg = <0xfe600000 0x1000>;
@@ -145,5 +156,73 @@
status = "disabled";
};
+
+ ethernet0: dwmac@fe810000 {
+ device_type = "network";
+ compatible = "st,stih416-dwmac", "snps,dwmac", "snps,dwmac-3.710";
+ status = "disabled";
+ reg = <0xfe810000 0x8000>, <0x8bc 0x4>;
+ reg-names = "stmmaceth", "sti-ethconf";
+
+ interrupts = <0 133 0>, <0 134 0>, <0 135 0>;
+ interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
+
+ snps,pbl = <32>;
+ snps,mixed-burst;
+
+ st,syscon = <&syscfg_rear>;
+ resets = <&softreset STIH416_ETH0_SOFTRESET>;
+ reset-names = "stmmaceth";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_mii0>;
+ clock-names = "stmmaceth";
+ clocks = <&CLK_S_GMAC0_PHY>;
+ };
+
+ ethernet1: dwmac@fef08000 {
+ device_type = "network";
+ compatible = "st,stih416-dwmac", "snps,dwmac", "snps,dwmac-3.710";
+ status = "disabled";
+ reg = <0xfef08000 0x8000>, <0x7f0 0x4>;
+ reg-names = "stmmaceth", "sti-ethconf";
+ interrupts = <0 136 0>, <0 137 0>, <0 138 0>;
+ interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
+
+ snps,pbl = <32>;
+ snps,mixed-burst;
+
+ st,syscon = <&syscfg_sbc>;
+
+ resets = <&softreset STIH416_ETH1_SOFTRESET>;
+ reset-names = "stmmaceth";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_mii1>;
+ clock-names = "stmmaceth";
+ clocks = <&CLK_S_ETH1_PHY>;
+ };
+
+ rc: rc@fe518000 {
+ compatible = "st,comms-irb";
+ reg = <0xfe518000 0x234>;
+ interrupts = <0 203 0>;
+ rx-mode = "infrared";
+ clocks = <&CLK_SYSIN>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ir>;
+ resets = <&softreset STIH416_IRB_SOFTRESET>;
+ };
+
+ /* FSM */
+ spifsm: spifsm@fe902000 {
+ compatible = "st,spi-fsm";
+ reg = <0xfe902000 0x1000>;
+ pinctrl-0 = <&pinctrl_fsm>;
+
+ st,syscfg = <&syscfg_rear>;
+ st,boot-device-reg = <0x958>;
+ st,boot-device-spi = <0x1a>;
+
+ status = "disabled";
+ };
};
};
diff --git a/arch/arm/boot/dts/stih41x-b2000.dtsi b/arch/arm/boot/dts/stih41x-b2000.dtsi
index 1e6aa92772f5..bf65c49095af 100644
--- a/arch/arm/boot/dts/stih41x-b2000.dtsi
+++ b/arch/arm/boot/dts/stih41x-b2000.dtsi
@@ -20,6 +20,8 @@
aliases {
ttyAS0 = &serial2;
+ ethernet0 = &ethernet0;
+ ethernet1 = &ethernet1;
};
soc {
@@ -46,5 +48,25 @@
status = "okay";
};
+
+ ethernet0: dwmac@fe810000 {
+ status = "okay";
+ phy-mode = "mii";
+ pinctrl-0 = <&pinctrl_mii0>;
+
+ snps,reset-gpio = <&PIO106 2>;
+ snps,reset-active-low;
+ snps,reset-delays-us = <0 10000 10000>;
+ };
+
+ ethernet1: dwmac@fef08000 {
+ status = "disabled";
+ phy-mode = "mii";
+ st,tx-retime-src = "txclk";
+
+ snps,reset-gpio = <&PIO4 7>;
+ snps,reset-active-low;
+ snps,reset-delays-us = <0 10000 10000>;
+ };
};
};
diff --git a/arch/arm/boot/dts/stih41x-b2020.dtsi b/arch/arm/boot/dts/stih41x-b2020.dtsi
index 0ef0a69df8ea..838513f9ddc0 100644
--- a/arch/arm/boot/dts/stih41x-b2020.dtsi
+++ b/arch/arm/boot/dts/stih41x-b2020.dtsi
@@ -6,6 +6,7 @@
* it under the terms of the GNU General Public License version 2 as
* publishhed by the Free Software Foundation.
*/
+#include "stih41x-b2020x.dtsi"
/ {
memory{
device_type = "memory";
@@ -19,6 +20,7 @@
aliases {
ttyAS0 = &sbc_serial1;
+ ethernet1 = &ethernet1;
};
soc {
sbc_serial1: serial@fe531000 {
@@ -60,5 +62,17 @@
i2c@fe541000 {
status = "okay";
};
+
+ ethernet1: dwmac@fef08000 {
+ status = "okay";
+ phy-mode = "rgmii-id";
+ max-speed = <1000>;
+ st,tx-retime-src = "clk_125";
+ snps,reset-gpio = <&PIO3 0>;
+ snps,reset-active-low;
+ snps,reset-delays-us = <0 10000 10000>;
+
+ pinctrl-0 = <&pinctrl_rgmii1>;
+ };
};
};
diff --git a/arch/arm/boot/dts/stih41x-b2020x.dtsi b/arch/arm/boot/dts/stih41x-b2020x.dtsi
new file mode 100644
index 000000000000..df01c1211b32
--- /dev/null
+++ b/arch/arm/boot/dts/stih41x-b2020x.dtsi
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Lee Jones <lee.jones@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * publishhed by the Free Software Foundation.
+ */
+/ {
+ soc {
+ spifsm: spifsm@fe902000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ status = "okay";
+
+ partition@0 {
+ label = "SerialFlash1";
+ reg = <0x00000000 0x00500000>;
+ };
+
+ partition@500000 {
+ label = "SerialFlash2";
+ reg = <0x00500000 0x00b00000>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/sun4i-a10-a1000.dts b/arch/arm/boot/dts/sun4i-a10-a1000.dts
index d4b081d6a167..fa746aea5e66 100644
--- a/arch/arm/boot/dts/sun4i-a10-a1000.dts
+++ b/arch/arm/boot/dts/sun4i-a10-a1000.dts
@@ -13,6 +13,7 @@
/dts-v1/;
/include/ "sun4i-a10.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
/ {
model = "Mele A1000";
@@ -35,6 +36,32 @@
};
};
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ usb2_vbus-supply = <&reg_usb2_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
+ ahci: sata@01c18000 {
+ status = "okay";
+ };
+
+ ehci1: usb@01c1c000 {
+ status = "okay";
+ };
+
+ ohci1: usb@01c1c400 {
+ status = "okay";
+ };
+
pinctrl@01c20800 {
emac_power_pin_a1000: emac_power_pin@0 {
allwinner,pins = "PH15";
@@ -80,18 +107,22 @@
};
};
- regulators {
- compatible = "simple-bus";
+ reg_emac_3v3: emac-3v3 {
+ compatible = "regulator-fixed";
+ pinctrl-names = "default";
+ pinctrl-0 = <&emac_power_pin_a1000>;
+ regulator-name = "emac-3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ enable-active-high;
+ gpio = <&pio 7 15 0>;
+ };
- reg_emac_3v3: emac-3v3 {
- compatible = "regulator-fixed";
- pinctrl-names = "default";
- pinctrl-0 = <&emac_power_pin_a1000>;
- regulator-name = "emac-3v3";
- regulator-min-microvolt = <3300000>;
- regulator-max-microvolt = <3300000>;
- enable-active-high;
- gpio = <&pio 7 15 0>;
- };
+ reg_usb1_vbus: usb1-vbus {
+ status = "okay";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ status = "okay";
};
};
diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
index b139ee6bcf99..4684cbe6843b 100644
--- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
+++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
@@ -12,6 +12,7 @@
/dts-v1/;
/include/ "sun4i-a10.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
/ {
model = "Cubietech Cubieboard";
@@ -33,6 +34,33 @@
};
};
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ usb2_vbus-supply = <&reg_usb2_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
+ ahci: sata@01c18000 {
+ target-supply = <&reg_ahci_5v>;
+ status = "okay";
+ };
+
+ ehci1: usb@01c1c000 {
+ status = "okay";
+ };
+
+ ohci1: usb@01c1c400 {
+ status = "okay";
+ };
+
pinctrl@01c20800 {
led_pins_cubieboard: led_pins@0 {
allwinner,pins = "PH20", "PH21";
@@ -77,4 +105,16 @@
linux,default-trigger = "heartbeat";
};
};
+
+ reg_ahci_5v: ahci-5v {
+ status = "okay";
+ };
+
+ reg_usb1_vbus: usb1-vbus {
+ status = "okay";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ status = "okay";
+ };
};
diff --git a/arch/arm/boot/dts/sun4i-a10-hackberry.dts b/arch/arm/boot/dts/sun4i-a10-hackberry.dts
index 3a1595f67823..d7c17e46ce23 100644
--- a/arch/arm/boot/dts/sun4i-a10-hackberry.dts
+++ b/arch/arm/boot/dts/sun4i-a10-hackberry.dts
@@ -13,6 +13,7 @@
/dts-v1/;
/include/ "sun4i-a10.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
/ {
model = "Miniand Hackberry";
@@ -35,6 +36,28 @@
};
};
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ usb2_vbus-supply = <&reg_usb2_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
+ ehci1: usb@01c1c000 {
+ status = "okay";
+ };
+
+ ohci1: usb@01c1c400 {
+ status = "okay";
+ };
+
pio: pinctrl@01c20800 {
pinctrl-names = "default";
pinctrl-0 = <&hackberry_hogs>;
@@ -45,6 +68,13 @@
allwinner,drive = <0>;
allwinner,pull = <0>;
};
+
+ usb2_vbus_pin_hackberry: usb2_vbus_pin@0 {
+ allwinner,pins = "PH12";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
uart0: serial@01c28000 {
@@ -54,16 +84,22 @@
};
};
- regulators {
- compatible = "simple-bus";
+ reg_emac_3v3: emac-3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "emac-3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ enable-active-high;
+ gpio = <&pio 7 19 0>;
+ };
- reg_emac_3v3: emac-3v3 {
- compatible = "regulator-fixed";
- regulator-name = "emac-3v3";
- regulator-min-microvolt = <3300000>;
- regulator-max-microvolt = <3300000>;
- enable-active-high;
- gpio = <&pio 7 19 0>;
- };
+ reg_usb1_vbus: usb1-vbus {
+ status = "okay";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ pinctrl-0 = <&usb2_vbus_pin_hackberry>;
+ gpio = <&pio 7 12 0>;
+ status = "okay";
};
};
diff --git a/arch/arm/boot/dts/sun4i-a10-inet97fv2.dts b/arch/arm/boot/dts/sun4i-a10-inet97fv2.dts
new file mode 100644
index 000000000000..fe9272ee55c3
--- /dev/null
+++ b/arch/arm/boot/dts/sun4i-a10-inet97fv2.dts
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014 Open Source Support GmbH
+ *
+ * David Lanzendörfer <david.lanzendoerfer@o2s.ch>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+/include/ "sun4i-a10.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
+
+/ {
+ model = "INet-97F Rev 02";
+ compatible = "primux,inet97fv2", "allwinner,sun4i-a10";
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ soc@01c00000 {
+ uart0: serial@01c28000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pins_a>;
+ status = "okay";
+ };
+
+ i2c0: i2c@01c2ac00 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0_pins_a>;
+ status = "okay";
+ };
+
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ usb2_vbus-supply = <&reg_usb2_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
+ ehci1: usb@01c1c000 {
+ status = "okay";
+ };
+
+ ohci1: usb@01c1c400 {
+ status = "okay";
+ };
+ };
+
+ reg_usb1_vbus: usb1-vbus {
+ status = "okay";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ status = "okay";
+ };
+};
diff --git a/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts b/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts
index 70b3323caf1a..dd84a9e313b3 100644
--- a/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts
+++ b/arch/arm/boot/dts/sun4i-a10-mini-xplus.dts
@@ -13,16 +13,47 @@
/dts-v1/;
/include/ "sun4i-a10.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
/ {
model = "PineRiver Mini X-Plus";
compatible = "pineriver,mini-xplus", "allwinner,sun4i-a10";
soc@01c00000 {
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ usb2_vbus-supply = <&reg_usb2_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
+ ehci1: usb@01c1c000 {
+ status = "okay";
+ };
+
+ ohci1: usb@01c1c400 {
+ status = "okay";
+ };
+
uart0: serial@01c28000 {
pinctrl-names = "default";
pinctrl-0 = <&uart0_pins_a>;
status = "okay";
};
};
+
+ reg_usb1_vbus: usb1-vbus {
+ status = "okay";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ status = "okay";
+ };
};
diff --git a/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts
new file mode 100644
index 000000000000..66cf0c7cf5b7
--- /dev/null
+++ b/arch/arm/boot/dts/sun4i-a10-olinuxino-lime.dts
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2014 - Hans de Goede <hdegoede@redhat.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+/include/ "sun4i-a10.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
+
+/ {
+ model = "Olimex A10-OLinuXino-LIME";
+ compatible = "olimex,a10-olinuxino-lime", "allwinner,sun4i-a10";
+
+ soc@01c00000 {
+ emac: ethernet@01c0b000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&emac_pins_a>;
+ phy = <&phy1>;
+ status = "okay";
+ };
+
+ mdio@01c0b080 {
+ status = "okay";
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+ };
+
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ usb2_vbus-supply = <&reg_usb2_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
+ ahci: sata@01c18000 {
+ target-supply = <&reg_ahci_5v>;
+ status = "okay";
+ };
+
+ ehci1: usb@01c1c000 {
+ status = "okay";
+ };
+
+ ohci1: usb@01c1c400 {
+ status = "okay";
+ };
+
+ pinctrl@01c20800 {
+ ahci_pwr_pin_olinuxinolime: ahci_pwr_pin@1 {
+ allwinner,pins = "PC3";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ led_pins_olinuxinolime: led_pins@0 {
+ allwinner,pins = "PH2";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <1>;
+ allwinner,pull = <0>;
+ };
+ };
+
+ uart0: serial@01c28000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pins_a>;
+ status = "okay";
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pins_olinuxinolime>;
+
+ green {
+ label = "a10-olinuxino-lime:green:usr";
+ gpios = <&pio 7 2 0>;
+ default-state = "on";
+ };
+ };
+
+ reg_ahci_5v: ahci-5v {
+ pinctrl-0 = <&ahci_pwr_pin_olinuxinolime>;
+ gpio = <&pio 2 3 0>;
+ status = "okay";
+ };
+
+ reg_usb1_vbus: usb1-vbus {
+ status = "okay";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ status = "okay";
+ };
+};
diff --git a/arch/arm/boot/dts/sun4i-a10-pcduino.dts b/arch/arm/boot/dts/sun4i-a10-pcduino.dts
new file mode 100644
index 000000000000..255b47e7019c
--- /dev/null
+++ b/arch/arm/boot/dts/sun4i-a10-pcduino.dts
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 Zoltan HERPAI
+ * Zoltan HERPAI <wigyori@uid0.hu>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+/include/ "sun4i-a10.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
+
+/ {
+ model = "LinkSprite pcDuino";
+ compatible = "linksprite,a10-pcduino", "allwinner,sun4i-a10";
+
+ soc@01c00000 {
+ emac: ethernet@01c0b000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&emac_pins_a>;
+ phy = <&phy1>;
+ status = "okay";
+ };
+
+ mdio@01c0b080 {
+ status = "okay";
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+ };
+
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ usb2_vbus-supply = <&reg_usb2_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
+ ehci1: usb@01c1c000 {
+ status = "okay";
+ };
+
+ ohci1: usb@01c1c400 {
+ status = "okay";
+ };
+
+ uart0: serial@01c28000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pins_a>;
+ status = "okay";
+ };
+
+ i2c0: i2c@01c2ac00 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0_pins_a>;
+ status = "okay";
+ };
+ };
+
+ reg_usb1_vbus: usb1-vbus {
+ status = "okay";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ status = "okay";
+ };
+};
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index 7753be0c86d7..9174724571e2 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -19,6 +19,12 @@
ethernet0 = &emac;
serial0 = &uart0;
serial1 = &uart1;
+ serial2 = &uart2;
+ serial3 = &uart3;
+ serial4 = &uart4;
+ serial5 = &uart5;
+ serial6 = &uart6;
+ serial7 = &uart7;
};
cpus {
@@ -52,44 +58,48 @@
clock-frequency = <0>;
};
- osc24M: osc24M@01c20050 {
+ osc24M: clk@01c20050 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-osc-clk";
+ compatible = "allwinner,sun4i-a10-osc-clk";
reg = <0x01c20050 0x4>;
clock-frequency = <24000000>;
+ clock-output-names = "osc24M";
};
- osc32k: osc32k {
+ osc32k: clk@0 {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <32768>;
+ clock-output-names = "osc32k";
};
- pll1: pll1@01c20000 {
+ pll1: clk@01c20000 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-pll1-clk";
+ compatible = "allwinner,sun4i-a10-pll1-clk";
reg = <0x01c20000 0x4>;
clocks = <&osc24M>;
+ clock-output-names = "pll1";
};
- pll4: pll4@01c20018 {
+ pll4: clk@01c20018 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-pll1-clk";
+ compatible = "allwinner,sun4i-a10-pll1-clk";
reg = <0x01c20018 0x4>;
clocks = <&osc24M>;
+ clock-output-names = "pll4";
};
- pll5: pll5@01c20020 {
+ pll5: clk@01c20020 {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-pll5-clk";
+ compatible = "allwinner,sun4i-a10-pll5-clk";
reg = <0x01c20020 0x4>;
clocks = <&osc24M>;
clock-output-names = "pll5_ddr", "pll5_other";
};
- pll6: pll6@01c20028 {
+ pll6: clk@01c20028 {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-pll6-clk";
+ compatible = "allwinner,sun4i-a10-pll6-clk";
reg = <0x01c20028 0x4>;
clocks = <&osc24M>;
clock-output-names = "pll6_sata", "pll6_other", "pll6";
@@ -98,21 +108,23 @@
/* dummy is 200M */
cpu: cpu@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-cpu-clk";
+ compatible = "allwinner,sun4i-a10-cpu-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll1>, <&dummy>;
+ clock-output-names = "cpu";
};
axi: axi@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-axi-clk";
+ compatible = "allwinner,sun4i-a10-axi-clk";
reg = <0x01c20054 0x4>;
clocks = <&cpu>;
+ clock-output-names = "axi";
};
- axi_gates: axi_gates@01c2005c {
+ axi_gates: clk@01c2005c {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-axi-gates-clk";
+ compatible = "allwinner,sun4i-a10-axi-gates-clk";
reg = <0x01c2005c 0x4>;
clocks = <&axi>;
clock-output-names = "axi_dram";
@@ -120,14 +132,15 @@
ahb: ahb@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-ahb-clk";
+ compatible = "allwinner,sun4i-a10-ahb-clk";
reg = <0x01c20054 0x4>;
clocks = <&axi>;
+ clock-output-names = "ahb";
};
- ahb_gates: ahb_gates@01c20060 {
+ ahb_gates: clk@01c20060 {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-ahb-gates-clk";
+ compatible = "allwinner,sun4i-a10-ahb-gates-clk";
reg = <0x01c20060 0x8>;
clocks = <&ahb>;
clock-output-names = "ahb_usb0", "ahb_ehci0",
@@ -145,14 +158,15 @@
apb0: apb0@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb0-clk";
+ compatible = "allwinner,sun4i-a10-apb0-clk";
reg = <0x01c20054 0x4>;
clocks = <&ahb>;
+ clock-output-names = "apb0";
};
- apb0_gates: apb0_gates@01c20068 {
+ apb0_gates: clk@01c20068 {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-apb0-gates-clk";
+ compatible = "allwinner,sun4i-a10-apb0-gates-clk";
reg = <0x01c20068 0x4>;
clocks = <&apb0>;
clock-output-names = "apb0_codec", "apb0_spdif",
@@ -162,21 +176,23 @@
apb1_mux: apb1_mux@01c20058 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb1-mux-clk";
+ compatible = "allwinner,sun4i-a10-apb1-mux-clk";
reg = <0x01c20058 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&osc32k>;
+ clock-output-names = "apb1_mux";
};
apb1: apb1@01c20058 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb1-clk";
+ compatible = "allwinner,sun4i-a10-apb1-clk";
reg = <0x01c20058 0x4>;
clocks = <&apb1_mux>;
+ clock-output-names = "apb1";
};
- apb1_gates: apb1_gates@01c2006c {
+ apb1_gates: clk@01c2006c {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-apb1-gates-clk";
+ compatible = "allwinner,sun4i-a10-apb1-gates-clk";
reg = <0x01c2006c 0x4>;
clocks = <&apb1>;
clock-output-names = "apb1_i2c0", "apb1_i2c1",
@@ -189,7 +205,7 @@
nand_clk: clk@01c20080 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20080 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "nand";
@@ -197,7 +213,7 @@
ms_clk: clk@01c20084 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20084 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ms";
@@ -205,7 +221,7 @@
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
@@ -213,7 +229,7 @@
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
@@ -221,7 +237,7 @@
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
@@ -229,7 +245,7 @@
mmc3_clk: clk@01c20094 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20094 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc3";
@@ -237,7 +253,7 @@
ts_clk: clk@01c20098 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20098 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ts";
@@ -245,7 +261,7 @@
ss_clk: clk@01c2009c {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c2009c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ss";
@@ -253,7 +269,7 @@
spi0_clk: clk@01c200a0 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a0 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi0";
@@ -261,7 +277,7 @@
spi1_clk: clk@01c200a4 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a4 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi1";
@@ -269,7 +285,7 @@
spi2_clk: clk@01c200a8 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a8 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi2";
@@ -277,7 +293,7 @@
pata_clk: clk@01c200ac {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200ac 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "pata";
@@ -285,7 +301,7 @@
ir0_clk: clk@01c200b0 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200b0 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ir0";
@@ -293,15 +309,24 @@
ir1_clk: clk@01c200b4 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200b4 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ir1";
};
+ usb_clk: clk@01c200cc {
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ compatible = "allwinner,sun4i-a10-usb-clk";
+ reg = <0x01c200cc 0x4>;
+ clocks = <&pll6 1>;
+ clock-output-names = "usb_ohci0", "usb_ohci1", "usb_phy";
+ };
+
spi3_clk: clk@01c200d4 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200d4 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi3";
@@ -314,6 +339,28 @@
#size-cells = <1>;
ranges;
+ spi0: spi@01c05000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c05000 0x1000>;
+ interrupts = <10>;
+ clocks = <&ahb_gates 20>, <&spi0_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ spi1: spi@01c06000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c06000 0x1000>;
+ interrupts = <11>;
+ clocks = <&ahb_gates 21>, <&spi1_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
emac: ethernet@01c0b000 {
compatible = "allwinner,sun4i-a10-emac";
reg = <0x01c0b000 0x1000>;
@@ -330,6 +377,88 @@
#size-cells = <0>;
};
+ usbphy: phy@01c13400 {
+ #phy-cells = <1>;
+ compatible = "allwinner,sun4i-a10-usb-phy";
+ reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
+ reg-names = "phy_ctrl", "pmu1", "pmu2";
+ clocks = <&usb_clk 8>;
+ clock-names = "usb_phy";
+ resets = <&usb_clk 1>, <&usb_clk 2>;
+ reset-names = "usb1_reset", "usb2_reset";
+ status = "disabled";
+ };
+
+ ehci0: usb@01c14000 {
+ compatible = "allwinner,sun4i-a10-ehci", "generic-ehci";
+ reg = <0x01c14000 0x100>;
+ interrupts = <39>;
+ clocks = <&ahb_gates 1>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci0: usb@01c14400 {
+ compatible = "allwinner,sun4i-a10-ohci", "generic-ohci";
+ reg = <0x01c14400 0x100>;
+ interrupts = <64>;
+ clocks = <&usb_clk 6>, <&ahb_gates 2>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ spi2: spi@01c17000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c17000 0x1000>;
+ interrupts = <12>;
+ clocks = <&ahb_gates 22>, <&spi2_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ ahci: sata@01c18000 {
+ compatible = "allwinner,sun4i-a10-ahci";
+ reg = <0x01c18000 0x1000>;
+ interrupts = <56>;
+ clocks = <&pll6 0>, <&ahb_gates 25>;
+ status = "disabled";
+ };
+
+ ehci1: usb@01c1c000 {
+ compatible = "allwinner,sun4i-a10-ehci", "generic-ehci";
+ reg = <0x01c1c000 0x100>;
+ interrupts = <40>;
+ clocks = <&ahb_gates 3>;
+ phys = <&usbphy 2>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci1: usb@01c1c400 {
+ compatible = "allwinner,sun4i-a10-ohci", "generic-ohci";
+ reg = <0x01c1c400 0x100>;
+ interrupts = <65>;
+ clocks = <&usb_clk 7>, <&ahb_gates 4>;
+ phys = <&usbphy 2>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ spi3: spi@01c1f000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c1f000 0x1000>;
+ interrupts = <50>;
+ clocks = <&ahb_gates 23>, <&spi3_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
intc: interrupt-controller@01c20400 {
compatible = "allwinner,sun4i-a10-ic";
reg = <0x01c20400 0x400>;
@@ -410,12 +539,12 @@
};
wdt: watchdog@01c20c90 {
- compatible = "allwinner,sun4i-wdt";
+ compatible = "allwinner,sun4i-a10-wdt";
reg = <0x01c20c90 0x10>;
};
rtc: rtc@01c20d00 {
- compatible = "allwinner,sun4i-rtc";
+ compatible = "allwinner,sun4i-a10-rtc";
reg = <0x01c20d00 0x20>;
interrupts = <24>;
};
diff --git a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
index 3c9f8b3cd3e3..23611b71d3aa 100644
--- a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
@@ -13,6 +13,7 @@
/dts-v1/;
/include/ "sun5i-a10s.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
/ {
model = "Olimex A10s-Olinuxino Micro";
@@ -34,6 +35,19 @@
};
};
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
pinctrl@01c20800 {
led_pins_olinuxino: led_pins@0 {
allwinner,pins = "PE3";
@@ -41,6 +55,13 @@
allwinner,drive = <1>;
allwinner,pull = <0>;
};
+
+ usb1_vbus_pin_olinuxino_m: usb1_vbus_pin@0 {
+ allwinner,pins = "PB10";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
uart0: serial@01c28000 {
@@ -98,4 +119,10 @@
default-state = "on";
};
};
+
+ reg_usb1_vbus: usb1-vbus {
+ pinctrl-0 = <&usb1_vbus_pin_olinuxino_m>;
+ gpio = <&pio 1 10 0>;
+ status = "okay";
+ };
};
diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index ee17b1c379e3..79989ed5658d 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -18,6 +18,10 @@
aliases {
ethernet0 = &emac;
+ serial0 = &uart0;
+ serial1 = &uart1;
+ serial2 = &uart2;
+ serial3 = &uart3;
};
cpus {
@@ -47,44 +51,48 @@
clock-frequency = <0>;
};
- osc24M: osc24M@01c20050 {
+ osc24M: clk@01c20050 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-osc-clk";
+ compatible = "allwinner,sun4i-a10-osc-clk";
reg = <0x01c20050 0x4>;
clock-frequency = <24000000>;
+ clock-output-names = "osc24M";
};
- osc32k: osc32k {
+ osc32k: clk@0 {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <32768>;
+ clock-output-names = "osc32k";
};
- pll1: pll1@01c20000 {
+ pll1: clk@01c20000 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-pll1-clk";
+ compatible = "allwinner,sun4i-a10-pll1-clk";
reg = <0x01c20000 0x4>;
clocks = <&osc24M>;
+ clock-output-names = "pll1";
};
- pll4: pll4@01c20018 {
+ pll4: clk@01c20018 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-pll1-clk";
+ compatible = "allwinner,sun4i-a10-pll1-clk";
reg = <0x01c20018 0x4>;
clocks = <&osc24M>;
+ clock-output-names = "pll4";
};
- pll5: pll5@01c20020 {
+ pll5: clk@01c20020 {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-pll5-clk";
+ compatible = "allwinner,sun4i-a10-pll5-clk";
reg = <0x01c20020 0x4>;
clocks = <&osc24M>;
clock-output-names = "pll5_ddr", "pll5_other";
};
- pll6: pll6@01c20028 {
+ pll6: clk@01c20028 {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-pll6-clk";
+ compatible = "allwinner,sun4i-a10-pll6-clk";
reg = <0x01c20028 0x4>;
clocks = <&osc24M>;
clock-output-names = "pll6_sata", "pll6_other", "pll6";
@@ -93,21 +101,23 @@
/* dummy is 200M */
cpu: cpu@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-cpu-clk";
+ compatible = "allwinner,sun4i-a10-cpu-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll1>, <&dummy>;
+ clock-output-names = "cpu";
};
axi: axi@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-axi-clk";
+ compatible = "allwinner,sun4i-a10-axi-clk";
reg = <0x01c20054 0x4>;
clocks = <&cpu>;
+ clock-output-names = "axi";
};
- axi_gates: axi_gates@01c2005c {
+ axi_gates: clk@01c2005c {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-axi-gates-clk";
+ compatible = "allwinner,sun4i-a10-axi-gates-clk";
reg = <0x01c2005c 0x4>;
clocks = <&axi>;
clock-output-names = "axi_dram";
@@ -115,12 +125,13 @@
ahb: ahb@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-ahb-clk";
+ compatible = "allwinner,sun4i-a10-ahb-clk";
reg = <0x01c20054 0x4>;
clocks = <&axi>;
+ clock-output-names = "ahb";
};
- ahb_gates: ahb_gates@01c20060 {
+ ahb_gates: clk@01c20060 {
#clock-cells = <1>;
compatible = "allwinner,sun5i-a10s-ahb-gates-clk";
reg = <0x01c20060 0x8>;
@@ -136,12 +147,13 @@
apb0: apb0@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb0-clk";
+ compatible = "allwinner,sun4i-a10-apb0-clk";
reg = <0x01c20054 0x4>;
clocks = <&ahb>;
+ clock-output-names = "apb0";
};
- apb0_gates: apb0_gates@01c20068 {
+ apb0_gates: clk@01c20068 {
#clock-cells = <1>;
compatible = "allwinner,sun5i-a10s-apb0-gates-clk";
reg = <0x01c20068 0x4>;
@@ -152,19 +164,21 @@
apb1_mux: apb1_mux@01c20058 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb1-mux-clk";
+ compatible = "allwinner,sun4i-a10-apb1-mux-clk";
reg = <0x01c20058 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&osc32k>;
+ clock-output-names = "apb1_mux";
};
apb1: apb1@01c20058 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb1-clk";
+ compatible = "allwinner,sun4i-a10-apb1-clk";
reg = <0x01c20058 0x4>;
clocks = <&apb1_mux>;
+ clock-output-names = "apb1";
};
- apb1_gates: apb1_gates@01c2006c {
+ apb1_gates: clk@01c2006c {
#clock-cells = <1>;
compatible = "allwinner,sun5i-a10s-apb1-gates-clk";
reg = <0x01c2006c 0x4>;
@@ -176,7 +190,7 @@
nand_clk: clk@01c20080 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20080 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "nand";
@@ -184,7 +198,7 @@
ms_clk: clk@01c20084 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20084 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ms";
@@ -192,7 +206,7 @@
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
@@ -200,7 +214,7 @@
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
@@ -208,7 +222,7 @@
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
@@ -216,7 +230,7 @@
ts_clk: clk@01c20098 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20098 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ts";
@@ -224,7 +238,7 @@
ss_clk: clk@01c2009c {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c2009c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ss";
@@ -232,7 +246,7 @@
spi0_clk: clk@01c200a0 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a0 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi0";
@@ -240,7 +254,7 @@
spi1_clk: clk@01c200a4 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a4 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi1";
@@ -248,7 +262,7 @@
spi2_clk: clk@01c200a8 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a8 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi2";
@@ -256,15 +270,24 @@
ir0_clk: clk@01c200b0 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200b0 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ir0";
};
+ usb_clk: clk@01c200cc {
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ compatible = "allwinner,sun5i-a13-usb-clk";
+ reg = <0x01c200cc 0x4>;
+ clocks = <&pll6 1>;
+ clock-output-names = "usb_ohci0", "usb_phy";
+ };
+
mbus_clk: clk@01c2015c {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c2015c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mbus";
@@ -277,6 +300,28 @@
#size-cells = <1>;
ranges;
+ spi0: spi@01c05000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c05000 0x1000>;
+ interrupts = <10>;
+ clocks = <&ahb_gates 20>, <&spi0_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ spi1: spi@01c06000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c06000 0x1000>;
+ interrupts = <11>;
+ clocks = <&ahb_gates 21>, <&spi1_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
emac: ethernet@01c0b000 {
compatible = "allwinner,sun4i-a10-emac";
reg = <0x01c0b000 0x1000>;
@@ -293,6 +338,49 @@
#size-cells = <0>;
};
+ usbphy: phy@01c13400 {
+ #phy-cells = <1>;
+ compatible = "allwinner,sun5i-a13-usb-phy";
+ reg = <0x01c13400 0x10 0x01c14800 0x4>;
+ reg-names = "phy_ctrl", "pmu1";
+ clocks = <&usb_clk 8>;
+ clock-names = "usb_phy";
+ resets = <&usb_clk 1>;
+ reset-names = "usb1_reset";
+ status = "disabled";
+ };
+
+ ehci0: usb@01c14000 {
+ compatible = "allwinner,sun5i-a10s-ehci", "generic-ehci";
+ reg = <0x01c14000 0x100>;
+ interrupts = <39>;
+ clocks = <&ahb_gates 1>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci0: usb@01c14400 {
+ compatible = "allwinner,sun5i-a10s-ohci", "generic-ohci";
+ reg = <0x01c14400 0x100>;
+ interrupts = <40>;
+ clocks = <&usb_clk 6>, <&ahb_gates 2>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ spi2: spi@01c17000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c17000 0x1000>;
+ interrupts = <12>;
+ clocks = <&ahb_gates 22>, <&spi2_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
intc: interrupt-controller@01c20400 {
compatible = "allwinner,sun4i-a10-ic";
reg = <0x01c20400 0x400>;
@@ -373,7 +461,7 @@
};
wdt: watchdog@01c20c90 {
- compatible = "allwinner,sun4i-wdt";
+ compatible = "allwinner,sun4i-a10-wdt";
reg = <0x01c20c90 0x10>;
};
diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts
index fe2ce0acdb06..11169d5b5b86 100644
--- a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts
@@ -14,12 +14,26 @@
/dts-v1/;
/include/ "sun5i-a13.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
/ {
model = "Olimex A13-Olinuxino Micro";
compatible = "olimex,a13-olinuxino-micro", "allwinner,sun5i-a13";
soc@01c00000 {
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
pinctrl@01c20800 {
led_pins_olinuxinom: led_pins@0 {
allwinner,pins = "PG9";
@@ -27,6 +41,13 @@
allwinner,drive = <1>;
allwinner,pull = <0>;
};
+
+ usb1_vbus_pin_olinuxinom: usb1_vbus_pin@0 {
+ allwinner,pins = "PG11";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
uart1: serial@01c28400 {
@@ -65,4 +86,10 @@
default-state = "on";
};
};
+
+ reg_usb1_vbus: usb1-vbus {
+ pinctrl-0 = <&usb1_vbus_pin_olinuxinom>;
+ gpio = <&pio 6 11 0>;
+ status = "okay";
+ };
};
diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
index a4ba5ff010cf..7a9187bbeb28 100644
--- a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
+++ b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
@@ -13,12 +13,26 @@
/dts-v1/;
/include/ "sun5i-a13.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
/ {
model = "Olimex A13-Olinuxino";
compatible = "olimex,a13-olinuxino", "allwinner,sun5i-a13";
soc@01c00000 {
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
pinctrl@01c20800 {
led_pins_olinuxino: led_pins@0 {
allwinner,pins = "PG9";
@@ -26,6 +40,13 @@
allwinner,drive = <1>;
allwinner,pull = <0>;
};
+
+ usb1_vbus_pin_olinuxino: usb1_vbus_pin@0 {
+ allwinner,pins = "PG11";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
uart1: serial@01c28400 {
@@ -63,4 +84,10 @@
default-state = "on";
};
};
+
+ reg_usb1_vbus: usb1-vbus {
+ pinctrl-0 = <&usb1_vbus_pin_olinuxino>;
+ gpio = <&pio 6 11 0>;
+ status = "okay";
+ };
};
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index 3490ef9ec603..f01c315bdc4b 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -16,6 +16,11 @@
/ {
interrupt-parent = <&intc>;
+ aliases {
+ serial0 = &uart1;
+ serial1 = &uart3;
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -47,44 +52,48 @@
clock-frequency = <0>;
};
- osc24M: osc24M@01c20050 {
+ osc24M: clk@01c20050 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-osc-clk";
+ compatible = "allwinner,sun4i-a10-osc-clk";
reg = <0x01c20050 0x4>;
clock-frequency = <24000000>;
+ clock-output-names = "osc24M";
};
- osc32k: osc32k {
+ osc32k: clk@0 {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <32768>;
+ clock-output-names = "osc32k";
};
- pll1: pll1@01c20000 {
+ pll1: clk@01c20000 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-pll1-clk";
+ compatible = "allwinner,sun4i-a10-pll1-clk";
reg = <0x01c20000 0x4>;
clocks = <&osc24M>;
+ clock-output-names = "pll1";
};
- pll4: pll4@01c20018 {
+ pll4: clk@01c20018 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-pll1-clk";
+ compatible = "allwinner,sun4i-a10-pll1-clk";
reg = <0x01c20018 0x4>;
clocks = <&osc24M>;
+ clock-output-names = "pll4";
};
- pll5: pll5@01c20020 {
+ pll5: clk@01c20020 {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-pll5-clk";
+ compatible = "allwinner,sun4i-a10-pll5-clk";
reg = <0x01c20020 0x4>;
clocks = <&osc24M>;
clock-output-names = "pll5_ddr", "pll5_other";
};
- pll6: pll6@01c20028 {
+ pll6: clk@01c20028 {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-pll6-clk";
+ compatible = "allwinner,sun4i-a10-pll6-clk";
reg = <0x01c20028 0x4>;
clocks = <&osc24M>;
clock-output-names = "pll6_sata", "pll6_other", "pll6";
@@ -93,21 +102,23 @@
/* dummy is 200M */
cpu: cpu@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-cpu-clk";
+ compatible = "allwinner,sun4i-a10-cpu-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll1>, <&dummy>;
+ clock-output-names = "cpu";
};
axi: axi@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-axi-clk";
+ compatible = "allwinner,sun4i-a10-axi-clk";
reg = <0x01c20054 0x4>;
clocks = <&cpu>;
+ clock-output-names = "axi";
};
- axi_gates: axi_gates@01c2005c {
+ axi_gates: clk@01c2005c {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-axi-gates-clk";
+ compatible = "allwinner,sun4i-a10-axi-gates-clk";
reg = <0x01c2005c 0x4>;
clocks = <&axi>;
clock-output-names = "axi_dram";
@@ -115,12 +126,13 @@
ahb: ahb@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-ahb-clk";
+ compatible = "allwinner,sun4i-a10-ahb-clk";
reg = <0x01c20054 0x4>;
clocks = <&axi>;
+ clock-output-names = "ahb";
};
- ahb_gates: ahb_gates@01c20060 {
+ ahb_gates: clk@01c20060 {
#clock-cells = <1>;
compatible = "allwinner,sun5i-a13-ahb-gates-clk";
reg = <0x01c20060 0x8>;
@@ -135,12 +147,13 @@
apb0: apb0@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb0-clk";
+ compatible = "allwinner,sun4i-a10-apb0-clk";
reg = <0x01c20054 0x4>;
clocks = <&ahb>;
+ clock-output-names = "apb0";
};
- apb0_gates: apb0_gates@01c20068 {
+ apb0_gates: clk@01c20068 {
#clock-cells = <1>;
compatible = "allwinner,sun5i-a13-apb0-gates-clk";
reg = <0x01c20068 0x4>;
@@ -150,19 +163,21 @@
apb1_mux: apb1_mux@01c20058 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb1-mux-clk";
+ compatible = "allwinner,sun4i-a10-apb1-mux-clk";
reg = <0x01c20058 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&osc32k>;
+ clock-output-names = "apb1_mux";
};
apb1: apb1@01c20058 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb1-clk";
+ compatible = "allwinner,sun4i-a10-apb1-clk";
reg = <0x01c20058 0x4>;
clocks = <&apb1_mux>;
+ clock-output-names = "apb1";
};
- apb1_gates: apb1_gates@01c2006c {
+ apb1_gates: clk@01c2006c {
#clock-cells = <1>;
compatible = "allwinner,sun5i-a13-apb1-gates-clk";
reg = <0x01c2006c 0x4>;
@@ -173,7 +188,7 @@
nand_clk: clk@01c20080 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20080 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "nand";
@@ -181,7 +196,7 @@
ms_clk: clk@01c20084 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20084 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ms";
@@ -189,7 +204,7 @@
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
@@ -197,7 +212,7 @@
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
@@ -205,7 +220,7 @@
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
@@ -213,7 +228,7 @@
ts_clk: clk@01c20098 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20098 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ts";
@@ -221,7 +236,7 @@
ss_clk: clk@01c2009c {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c2009c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ss";
@@ -229,7 +244,7 @@
spi0_clk: clk@01c200a0 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a0 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi0";
@@ -237,7 +252,7 @@
spi1_clk: clk@01c200a4 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a4 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi1";
@@ -245,7 +260,7 @@
spi2_clk: clk@01c200a8 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a8 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi2";
@@ -253,15 +268,24 @@
ir0_clk: clk@01c200b0 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200b0 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ir0";
};
+ usb_clk: clk@01c200cc {
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ compatible = "allwinner,sun5i-a13-usb-clk";
+ reg = <0x01c200cc 0x4>;
+ clocks = <&pll6 1>;
+ clock-output-names = "usb_ohci0", "usb_phy";
+ };
+
mbus_clk: clk@01c2015c {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c2015c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mbus";
@@ -274,6 +298,71 @@
#size-cells = <1>;
ranges;
+ spi0: spi@01c05000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c05000 0x1000>;
+ interrupts = <10>;
+ clocks = <&ahb_gates 20>, <&spi0_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ spi1: spi@01c06000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c06000 0x1000>;
+ interrupts = <11>;
+ clocks = <&ahb_gates 21>, <&spi1_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ usbphy: phy@01c13400 {
+ #phy-cells = <1>;
+ compatible = "allwinner,sun5i-a13-usb-phy";
+ reg = <0x01c13400 0x10 0x01c14800 0x4>;
+ reg-names = "phy_ctrl", "pmu1";
+ clocks = <&usb_clk 8>;
+ clock-names = "usb_phy";
+ resets = <&usb_clk 1>;
+ reset-names = "usb1_reset";
+ status = "disabled";
+ };
+
+ ehci0: usb@01c14000 {
+ compatible = "allwinner,sun5i-a13-ehci", "generic-ehci";
+ reg = <0x01c14000 0x100>;
+ interrupts = <39>;
+ clocks = <&ahb_gates 1>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci0: usb@01c14400 {
+ compatible = "allwinner,sun5i-a13-ohci", "generic-ohci";
+ reg = <0x01c14400 0x100>;
+ interrupts = <40>;
+ clocks = <&usb_clk 6>, <&ahb_gates 2>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ spi2: spi@01c17000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c17000 0x1000>;
+ interrupts = <12>;
+ clocks = <&ahb_gates 22>, <&spi2_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
intc: interrupt-controller@01c20400 {
compatible = "allwinner,sun4i-a10-ic";
reg = <0x01c20400 0x400>;
@@ -336,7 +425,7 @@
};
wdt: watchdog@01c20c90 {
- compatible = "allwinner,sun4i-wdt";
+ compatible = "allwinner,sun4i-a10-wdt";
reg = <0x01c20c90 0x10>;
};
diff --git a/arch/arm/boot/dts/sun6i-a31-colombus.dts b/arch/arm/boot/dts/sun6i-a31-colombus.dts
index e5adae30899b..3898a7bce831 100644
--- a/arch/arm/boot/dts/sun6i-a31-colombus.dts
+++ b/arch/arm/boot/dts/sun6i-a31-colombus.dts
@@ -28,5 +28,23 @@
pinctrl-0 = <&uart0_pins_a>;
status = "okay";
};
+
+ i2c0: i2c@01c2ac00 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0_pins_a>;
+ status = "fail";
+ };
+
+ i2c1: i2c@01c2b000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins_a>;
+ status = "okay";
+ };
+
+ i2c2: i2c@01c2b400 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins_a>;
+ status = "okay";
+ };
};
};
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index 38d43febda4c..d45efa74827c 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -16,6 +16,16 @@
/ {
interrupt-parent = <&gic>;
+ aliases {
+ serial0 = &uart0;
+ serial1 = &uart1;
+ serial2 = &uart2;
+ serial3 = &uart3;
+ serial4 = &uart4;
+ serial5 = &uart5;
+ };
+
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -60,34 +70,32 @@
clock-frequency = <24000000>;
};
- osc32k: osc32k {
+ osc32k: clk@0 {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <32768>;
+ clock-output-names = "osc32k";
};
- pll1: pll1@01c20000 {
+ pll1: clk@01c20000 {
#clock-cells = <0>;
compatible = "allwinner,sun6i-a31-pll1-clk";
reg = <0x01c20000 0x4>;
clocks = <&osc24M>;
+ clock-output-names = "pll1";
};
- /*
- * This is a dummy clock, to be used as placeholder on
- * other mux clocks when a specific parent clock is not
- * yet implemented. It should be dropped when the driver
- * is complete.
- */
- pll6: pll6 {
+ pll6: clk@01c20028 {
#clock-cells = <0>;
- compatible = "fixed-clock";
- clock-frequency = <0>;
+ compatible = "allwinner,sun6i-a31-pll6-clk";
+ reg = <0x01c20028 0x4>;
+ clocks = <&osc24M>;
+ clock-output-names = "pll6";
};
cpu: cpu@01c20050 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-cpu-clk";
+ compatible = "allwinner,sun4i-a10-cpu-clk";
reg = <0x01c20050 0x4>;
/*
@@ -97,13 +105,15 @@
* Allwinner.
*/
clocks = <&osc32k>, <&osc24M>, <&pll1>, <&pll1>;
+ clock-output-names = "cpu";
};
axi: axi@01c20050 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-axi-clk";
+ compatible = "allwinner,sun4i-a10-axi-clk";
reg = <0x01c20050 0x4>;
clocks = <&cpu>;
+ clock-output-names = "axi";
};
ahb1_mux: ahb1_mux@01c20054 {
@@ -111,16 +121,18 @@
compatible = "allwinner,sun6i-a31-ahb1-mux-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6>;
+ clock-output-names = "ahb1_mux";
};
ahb1: ahb1@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-ahb-clk";
+ compatible = "allwinner,sun4i-a10-ahb-clk";
reg = <0x01c20054 0x4>;
clocks = <&ahb1_mux>;
+ clock-output-names = "ahb1";
};
- ahb1_gates: ahb1_gates@01c20060 {
+ ahb1_gates: clk@01c20060 {
#clock-cells = <1>;
compatible = "allwinner,sun6i-a31-ahb1-gates-clk";
reg = <0x01c20060 0x8>;
@@ -143,12 +155,13 @@
apb1: apb1@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb0-clk";
+ compatible = "allwinner,sun4i-a10-apb0-clk";
reg = <0x01c20054 0x4>;
clocks = <&ahb1>;
+ clock-output-names = "apb1";
};
- apb1_gates: apb1_gates@01c20060 {
+ apb1_gates: clk@01c20068 {
#clock-cells = <1>;
compatible = "allwinner,sun6i-a31-apb1-gates-clk";
reg = <0x01c20068 0x4>;
@@ -160,9 +173,10 @@
apb2_mux: apb2_mux@01c20058 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb1-mux-clk";
+ compatible = "allwinner,sun4i-a10-apb1-mux-clk";
reg = <0x01c20058 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll6>, <&pll6>;
+ clock-output-names = "apb2_mux";
};
apb2: apb2@01c20058 {
@@ -170,9 +184,10 @@
compatible = "allwinner,sun6i-a31-apb2-div-clk";
reg = <0x01c20058 0x4>;
clocks = <&apb2_mux>;
+ clock-output-names = "apb2";
};
- apb2_gates: apb2_gates@01c2006c {
+ apb2_gates: clk@01c2006c {
#clock-cells = <1>;
compatible = "allwinner,sun6i-a31-apb2-gates-clk";
reg = <0x01c2006c 0x4>;
@@ -182,6 +197,38 @@
"apb2_uart1", "apb2_uart2", "apb2_uart3",
"apb2_uart4", "apb2_uart5";
};
+
+ spi0_clk: clk@01c200a0 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-mod0-clk";
+ reg = <0x01c200a0 0x4>;
+ clocks = <&osc24M>, <&pll6>;
+ clock-output-names = "spi0";
+ };
+
+ spi1_clk: clk@01c200a4 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-mod0-clk";
+ reg = <0x01c200a4 0x4>;
+ clocks = <&osc24M>, <&pll6>;
+ clock-output-names = "spi1";
+ };
+
+ spi2_clk: clk@01c200a8 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-mod0-clk";
+ reg = <0x01c200a8 0x4>;
+ clocks = <&osc24M>, <&pll6>;
+ clock-output-names = "spi2";
+ };
+
+ spi3_clk: clk@01c200ac {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun4i-a10-mod0-clk";
+ reg = <0x01c200ac 0x4>;
+ clocks = <&osc24M>, <&pll6>;
+ clock-output-names = "spi3";
+ };
};
soc@01c00000 {
@@ -218,6 +265,27 @@
allwinner,drive = <0>;
allwinner,pull = <0>;
};
+
+ i2c0_pins_a: i2c0@0 {
+ allwinner,pins = "PH14", "PH15";
+ allwinner,function = "i2c0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ i2c1_pins_a: i2c1@0 {
+ allwinner,pins = "PH16", "PH17";
+ allwinner,function = "i2c1";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ i2c2_pins_a: i2c2@0 {
+ allwinner,pins = "PH18", "PH19";
+ allwinner,function = "i2c2";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
ahb1_rst: reset@01c202c0 {
@@ -250,7 +318,7 @@
};
wdt1: watchdog@01c20ca0 {
- compatible = "allwinner,sun6i-wdt";
+ compatible = "allwinner,sun6i-a31-wdt";
reg = <0x01c20ca0 0x20>;
};
@@ -320,6 +388,86 @@
status = "disabled";
};
+ i2c0: i2c@01c2ac00 {
+ compatible = "allwinner,sun6i-a31-i2c";
+ reg = <0x01c2ac00 0x400>;
+ interrupts = <0 6 4>;
+ clocks = <&apb2_gates 0>;
+ clock-frequency = <100000>;
+ resets = <&apb2_rst 0>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@01c2b000 {
+ compatible = "allwinner,sun6i-a31-i2c";
+ reg = <0x01c2b000 0x400>;
+ interrupts = <0 7 4>;
+ clocks = <&apb2_gates 1>;
+ clock-frequency = <100000>;
+ resets = <&apb2_rst 1>;
+ status = "disabled";
+ };
+
+ i2c2: i2c@01c2b400 {
+ compatible = "allwinner,sun6i-a31-i2c";
+ reg = <0x01c2b400 0x400>;
+ interrupts = <0 8 4>;
+ clocks = <&apb2_gates 2>;
+ clock-frequency = <100000>;
+ resets = <&apb2_rst 2>;
+ status = "disabled";
+ };
+
+ i2c3: i2c@01c2b800 {
+ compatible = "allwinner,sun6i-a31-i2c";
+ reg = <0x01c2b800 0x400>;
+ interrupts = <0 9 4>;
+ clocks = <&apb2_gates 3>;
+ clock-frequency = <100000>;
+ resets = <&apb2_rst 3>;
+ status = "disabled";
+ };
+
+ spi0: spi@01c68000 {
+ compatible = "allwinner,sun6i-a31-spi";
+ reg = <0x01c68000 0x1000>;
+ interrupts = <0 65 4>;
+ clocks = <&ahb1_gates 20>, <&spi0_clk>;
+ clock-names = "ahb", "mod";
+ resets = <&ahb1_rst 20>;
+ status = "disabled";
+ };
+
+ spi1: spi@01c69000 {
+ compatible = "allwinner,sun6i-a31-spi";
+ reg = <0x01c69000 0x1000>;
+ interrupts = <0 66 4>;
+ clocks = <&ahb1_gates 21>, <&spi1_clk>;
+ clock-names = "ahb", "mod";
+ resets = <&ahb1_rst 21>;
+ status = "disabled";
+ };
+
+ spi2: spi@01c6a000 {
+ compatible = "allwinner,sun6i-a31-spi";
+ reg = <0x01c6a000 0x1000>;
+ interrupts = <0 67 4>;
+ clocks = <&ahb1_gates 22>, <&spi2_clk>;
+ clock-names = "ahb", "mod";
+ resets = <&ahb1_rst 22>;
+ status = "disabled";
+ };
+
+ spi3: spi@01c6b000 {
+ compatible = "allwinner,sun6i-a31-spi";
+ reg = <0x01c6b000 0x1000>;
+ interrupts = <0 68 4>;
+ clocks = <&ahb1_gates 23>, <&spi3_clk>;
+ clock-names = "ahb", "mod";
+ resets = <&ahb1_rst 23>;
+ status = "disabled";
+ };
+
gic: interrupt-controller@01c81000 {
compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic";
reg = <0x01c81000 0x1000>,
diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
index 5c51cb8a98b0..68de89ffbdfa 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
@@ -13,25 +13,38 @@
/dts-v1/;
/include/ "sun7i-a20.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
/ {
model = "Cubietech Cubieboard2";
compatible = "cubietech,cubieboard2", "allwinner,sun7i-a20";
soc@01c00000 {
- emac: ethernet@01c0b000 {
- pinctrl-names = "default";
- pinctrl-0 = <&emac_pins_a>;
- phy = <&phy1>;
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ usb2_vbus-supply = <&reg_usb2_vbus>;
status = "okay";
};
- mdio@01c0b080 {
+ ehci0: usb@01c14000 {
status = "okay";
+ };
- phy1: ethernet-phy@1 {
- reg = <1>;
- };
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
+ ahci: sata@01c18000 {
+ target-supply = <&reg_ahci_5v>;
+ status = "okay";
+ };
+
+ ehci1: usb@01c1c000 {
+ status = "okay";
+ };
+
+ ohci1: usb@01c1c400 {
+ status = "okay";
};
pinctrl@01c20800 {
@@ -60,6 +73,18 @@
pinctrl-0 = <&i2c1_pins_a>;
status = "okay";
};
+
+ gmac: ethernet@01c50000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gmac_pins_mii_a>;
+ phy = <&phy1>;
+ phy-mode = "mii";
+ status = "okay";
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+ };
};
leds {
@@ -77,4 +102,16 @@
gpios = <&pio 7 20 0>;
};
};
+
+ reg_ahci_5v: ahci-5v {
+ status = "okay";
+ };
+
+ reg_usb1_vbus: usb1-vbus {
+ status = "okay";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ status = "okay";
+ };
};
diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
index f9dcb61a5305..cb25d3c8da58 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
@@ -13,13 +13,48 @@
/dts-v1/;
/include/ "sun7i-a20.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
/ {
model = "Cubietech Cubietruck";
compatible = "cubietech,cubietruck", "allwinner,sun7i-a20";
soc@01c00000 {
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ usb2_vbus-supply = <&reg_usb2_vbus>;
+ status = "okay";
+ };
+
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
+ ahci: sata@01c18000 {
+ target-supply = <&reg_ahci_5v>;
+ status = "okay";
+ };
+
+ ehci1: usb@01c1c000 {
+ status = "okay";
+ };
+
+ ohci1: usb@01c1c400 {
+ status = "okay";
+ };
+
pinctrl@01c20800 {
+ ahci_pwr_pin_cubietruck: ahci_pwr_pin@1 {
+ allwinner,pins = "PH12";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
led_pins_cubietruck: led_pins@0 {
allwinner,pins = "PH7", "PH11", "PH20", "PH21";
allwinner,function = "gpio_out";
@@ -51,6 +86,18 @@
pinctrl-0 = <&i2c2_pins_a>;
status = "okay";
};
+
+ gmac: ethernet@01c50000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gmac_pins_rgmii_a>;
+ phy = <&phy1>;
+ phy-mode = "rgmii";
+ status = "okay";
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+ };
};
leds {
@@ -78,4 +125,18 @@
gpios = <&pio 7 7 0>;
};
};
+
+ reg_ahci_5v: ahci-5v {
+ pinctrl-0 = <&ahci_pwr_pin_cubietruck>;
+ gpio = <&pio 7 12 0>;
+ status = "okay";
+ };
+
+ reg_usb1_vbus: usb1-vbus {
+ status = "okay";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ status = "okay";
+ };
};
diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
index ead3013f9aca..eeadf76362fa 100644
--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
@@ -13,25 +13,55 @@
/dts-v1/;
/include/ "sun7i-a20.dtsi"
+/include/ "sunxi-common-regulators.dtsi"
/ {
model = "Olimex A20-Olinuxino Micro";
compatible = "olimex,a20-olinuxino-micro", "allwinner,sun7i-a20";
+ aliases {
+ spi0 = &spi1;
+ spi1 = &spi2;
+ };
+
soc@01c00000 {
- emac: ethernet@01c0b000 {
+ spi1: spi@01c06000 {
pinctrl-names = "default";
- pinctrl-0 = <&emac_pins_a>;
- phy = <&phy1>;
+ pinctrl-0 = <&spi1_pins_a>;
status = "okay";
};
- mdio@01c0b080 {
+ usbphy: phy@01c13400 {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ usb2_vbus-supply = <&reg_usb2_vbus>;
status = "okay";
+ };
- phy1: ethernet-phy@1 {
- reg = <1>;
- };
+ ehci0: usb@01c14000 {
+ status = "okay";
+ };
+
+ ohci0: usb@01c14400 {
+ status = "okay";
+ };
+
+ spi2: spi@01c17000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi2_pins_a>;
+ status = "okay";
+ };
+
+ ahci: sata@01c18000 {
+ target-supply = <&reg_ahci_5v>;
+ status = "okay";
+ };
+
+ ehci1: usb@01c1c000 {
+ status = "okay";
+ };
+
+ ohci1: usb@01c1c400 {
+ status = "okay";
};
pinctrl@01c20800 {
@@ -78,6 +108,18 @@
pinctrl-0 = <&i2c2_pins_a>;
status = "okay";
};
+
+ gmac: ethernet@01c50000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gmac_pins_mii_a>;
+ phy = <&phy1>;
+ phy-mode = "mii";
+ status = "okay";
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+ };
};
leds {
@@ -91,4 +133,16 @@
default-state = "on";
};
};
+
+ reg_ahci_5v: ahci-5v {
+ status = "okay";
+ };
+
+ reg_usb1_vbus: usb1-vbus {
+ status = "okay";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ status = "okay";
+ };
};
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index cadcf2f9881d..32efc105df83 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -17,7 +17,15 @@
interrupt-parent = <&gic>;
aliases {
- ethernet0 = &emac;
+ ethernet0 = &gmac;
+ serial0 = &uart0;
+ serial1 = &uart1;
+ serial2 = &uart2;
+ serial3 = &uart3;
+ serial4 = &uart4;
+ serial5 = &uart5;
+ serial6 = &uart6;
+ serial7 = &uart7;
};
cpus {
@@ -41,16 +49,25 @@
reg = <0x40000000 0x80000000>;
};
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 13 0xf08>,
+ <1 14 0xf08>,
+ <1 11 0xf08>,
+ <1 10 0xf08>;
+ };
+
clocks {
#address-cells = <1>;
#size-cells = <1>;
ranges;
- osc24M: osc24M@01c20050 {
+ osc24M: clk@01c20050 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-osc-clk";
+ compatible = "allwinner,sun4i-a10-osc-clk";
reg = <0x01c20050 0x4>;
clock-frequency = <24000000>;
+ clock-output-names = "osc24M";
};
osc32k: clk@0 {
@@ -60,31 +77,33 @@
clock-output-names = "osc32k";
};
- pll1: pll1@01c20000 {
+ pll1: clk@01c20000 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-pll1-clk";
+ compatible = "allwinner,sun4i-a10-pll1-clk";
reg = <0x01c20000 0x4>;
clocks = <&osc24M>;
+ clock-output-names = "pll1";
};
- pll4: pll4@01c20018 {
+ pll4: clk@01c20018 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-pll1-clk";
+ compatible = "allwinner,sun4i-a10-pll1-clk";
reg = <0x01c20018 0x4>;
clocks = <&osc24M>;
+ clock-output-names = "pll4";
};
- pll5: pll5@01c20020 {
+ pll5: clk@01c20020 {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-pll5-clk";
+ compatible = "allwinner,sun4i-a10-pll5-clk";
reg = <0x01c20020 0x4>;
clocks = <&osc24M>;
clock-output-names = "pll5_ddr", "pll5_other";
};
- pll6: pll6@01c20028 {
+ pll6: clk@01c20028 {
#clock-cells = <1>;
- compatible = "allwinner,sun4i-pll6-clk";
+ compatible = "allwinner,sun4i-a10-pll6-clk";
reg = <0x01c20028 0x4>;
clocks = <&osc24M>;
clock-output-names = "pll6_sata", "pll6_other", "pll6";
@@ -92,26 +111,29 @@
cpu: cpu@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-cpu-clk";
+ compatible = "allwinner,sun4i-a10-cpu-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll1>, <&pll6 1>;
+ clock-output-names = "cpu";
};
axi: axi@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-axi-clk";
+ compatible = "allwinner,sun4i-a10-axi-clk";
reg = <0x01c20054 0x4>;
clocks = <&cpu>;
+ clock-output-names = "axi";
};
ahb: ahb@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-ahb-clk";
+ compatible = "allwinner,sun4i-a10-ahb-clk";
reg = <0x01c20054 0x4>;
clocks = <&axi>;
+ clock-output-names = "ahb";
};
- ahb_gates: ahb_gates@01c20060 {
+ ahb_gates: clk@01c20060 {
#clock-cells = <1>;
compatible = "allwinner,sun7i-a20-ahb-gates-clk";
reg = <0x01c20060 0x8>;
@@ -133,12 +155,13 @@
apb0: apb0@01c20054 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb0-clk";
+ compatible = "allwinner,sun4i-a10-apb0-clk";
reg = <0x01c20054 0x4>;
clocks = <&ahb>;
+ clock-output-names = "apb0";
};
- apb0_gates: apb0_gates@01c20068 {
+ apb0_gates: clk@01c20068 {
#clock-cells = <1>;
compatible = "allwinner,sun7i-a20-apb0-gates-clk";
reg = <0x01c20068 0x4>;
@@ -151,19 +174,21 @@
apb1_mux: apb1_mux@01c20058 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb1-mux-clk";
+ compatible = "allwinner,sun4i-a10-apb1-mux-clk";
reg = <0x01c20058 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&osc32k>;
+ clock-output-names = "apb1_mux";
};
apb1: apb1@01c20058 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-apb1-clk";
+ compatible = "allwinner,sun4i-a10-apb1-clk";
reg = <0x01c20058 0x4>;
clocks = <&apb1_mux>;
+ clock-output-names = "apb1";
};
- apb1_gates: apb1_gates@01c2006c {
+ apb1_gates: clk@01c2006c {
#clock-cells = <1>;
compatible = "allwinner,sun7i-a20-apb1-gates-clk";
reg = <0x01c2006c 0x4>;
@@ -178,7 +203,7 @@
nand_clk: clk@01c20080 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20080 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "nand";
@@ -186,7 +211,7 @@
ms_clk: clk@01c20084 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20084 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ms";
@@ -194,7 +219,7 @@
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
@@ -202,7 +227,7 @@
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
@@ -210,7 +235,7 @@
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
@@ -218,7 +243,7 @@
mmc3_clk: clk@01c20094 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20094 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc3";
@@ -226,7 +251,7 @@
ts_clk: clk@01c20098 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c20098 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ts";
@@ -234,7 +259,7 @@
ss_clk: clk@01c2009c {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c2009c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ss";
@@ -242,7 +267,7 @@
spi0_clk: clk@01c200a0 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a0 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi0";
@@ -250,7 +275,7 @@
spi1_clk: clk@01c200a4 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a4 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi1";
@@ -258,7 +283,7 @@
spi2_clk: clk@01c200a8 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200a8 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi2";
@@ -266,7 +291,7 @@
pata_clk: clk@01c200ac {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200ac 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "pata";
@@ -274,7 +299,7 @@
ir0_clk: clk@01c200b0 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200b0 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ir0";
@@ -282,15 +307,24 @@
ir1_clk: clk@01c200b4 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200b4 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "ir1";
};
+ usb_clk: clk@01c200cc {
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ compatible = "allwinner,sun4i-a10-usb-clk";
+ reg = <0x01c200cc 0x4>;
+ clocks = <&pll6 1>;
+ clock-output-names = "usb_ohci0", "usb_ohci1", "usb_phy";
+ };
+
spi3_clk: clk@01c200d4 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c200d4 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "spi3";
@@ -298,13 +332,41 @@
mbus_clk: clk@01c2015c {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-mod0-clk";
+ compatible = "allwinner,sun4i-a10-mod0-clk";
reg = <0x01c2015c 0x4>;
clocks = <&osc24M>, <&pll6 2>, <&pll5 1>;
clock-output-names = "mbus";
};
/*
+ * The following two are dummy clocks, placeholders used in the gmac_tx
+ * clock. The gmac driver will choose one parent depending on the PHY
+ * interface mode, using clk_set_rate auto-reparenting.
+ * The actual TX clock rate is not controlled by the gmac_tx clock.
+ */
+ mii_phy_tx_clk: clk@2 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <25000000>;
+ clock-output-names = "mii_phy_tx";
+ };
+
+ gmac_int_tx_clk: clk@3 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <125000000>;
+ clock-output-names = "gmac_int_tx";
+ };
+
+ gmac_tx_clk: clk@01c20164 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun7i-a20-gmac-clk";
+ reg = <0x01c20164 0x4>;
+ clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>;
+ clock-output-names = "gmac_tx";
+ };
+
+ /*
* Dummy clock used by output clocks
*/
osc24M_32k: clk@1 {
@@ -347,6 +409,28 @@
interrupts = <0 0 4>;
};
+ spi0: spi@01c05000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c05000 0x1000>;
+ interrupts = <0 10 4>;
+ clocks = <&ahb_gates 20>, <&spi0_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ spi1: spi@01c06000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c06000 0x1000>;
+ interrupts = <0 11 4>;
+ clocks = <&ahb_gates 21>, <&spi1_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
emac: ethernet@01c0b000 {
compatible = "allwinner,sun4i-a10-emac";
reg = <0x01c0b000 0x1000>;
@@ -363,6 +447,88 @@
#size-cells = <0>;
};
+ usbphy: phy@01c13400 {
+ #phy-cells = <1>;
+ compatible = "allwinner,sun7i-a20-usb-phy";
+ reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
+ reg-names = "phy_ctrl", "pmu1", "pmu2";
+ clocks = <&usb_clk 8>;
+ clock-names = "usb_phy";
+ resets = <&usb_clk 1>, <&usb_clk 2>;
+ reset-names = "usb1_reset", "usb2_reset";
+ status = "disabled";
+ };
+
+ ehci0: usb@01c14000 {
+ compatible = "allwinner,sun7i-a20-ehci", "generic-ehci";
+ reg = <0x01c14000 0x100>;
+ interrupts = <0 39 4>;
+ clocks = <&ahb_gates 1>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci0: usb@01c14400 {
+ compatible = "allwinner,sun7i-a20-ohci", "generic-ohci";
+ reg = <0x01c14400 0x100>;
+ interrupts = <0 64 4>;
+ clocks = <&usb_clk 6>, <&ahb_gates 2>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ spi2: spi@01c17000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c17000 0x1000>;
+ interrupts = <0 12 4>;
+ clocks = <&ahb_gates 22>, <&spi2_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ ahci: sata@01c18000 {
+ compatible = "allwinner,sun4i-a10-ahci";
+ reg = <0x01c18000 0x1000>;
+ interrupts = <0 56 4>;
+ clocks = <&pll6 0>, <&ahb_gates 25>;
+ status = "disabled";
+ };
+
+ ehci1: usb@01c1c000 {
+ compatible = "allwinner,sun7i-a20-ehci", "generic-ehci";
+ reg = <0x01c1c000 0x100>;
+ interrupts = <0 40 4>;
+ clocks = <&ahb_gates 3>;
+ phys = <&usbphy 2>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci1: usb@01c1c400 {
+ compatible = "allwinner,sun7i-a20-ohci", "generic-ohci";
+ reg = <0x01c1c400 0x100>;
+ interrupts = <0 65 4>;
+ clocks = <&usb_clk 7>, <&ahb_gates 4>;
+ phys = <&usbphy 2>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ spi3: spi@01c1f000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c1f000 0x1000>;
+ interrupts = <0 50 4>;
+ clocks = <&ahb_gates 23>, <&spi3_clk>;
+ clock-names = "ahb", "mod";
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
pio: pinctrl@01c20800 {
compatible = "allwinner,sun7i-a20-pinctrl";
reg = <0x01c20800 0x400>;
@@ -381,6 +547,13 @@
allwinner,pull = <0>;
};
+ uart2_pins_a: uart2@0 {
+ allwinner,pins = "PI16", "PI17", "PI18", "PI19";
+ allwinner,function = "uart2";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
uart6_pins_a: uart6@0 {
allwinner,pins = "PI12", "PI13";
allwinner,function = "uart6";
@@ -440,6 +613,46 @@
allwinner,drive = <0>;
allwinner,pull = <0>;
};
+
+ gmac_pins_mii_a: gmac_mii@0 {
+ allwinner,pins = "PA0", "PA1", "PA2",
+ "PA3", "PA4", "PA5", "PA6",
+ "PA7", "PA8", "PA9", "PA10",
+ "PA11", "PA12", "PA13", "PA14",
+ "PA15", "PA16";
+ allwinner,function = "gmac";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ gmac_pins_rgmii_a: gmac_rgmii@0 {
+ allwinner,pins = "PA0", "PA1", "PA2",
+ "PA3", "PA4", "PA5", "PA6",
+ "PA7", "PA8", "PA10",
+ "PA11", "PA12", "PA13",
+ "PA15", "PA16";
+ allwinner,function = "gmac";
+ /*
+ * data lines in RGMII mode use DDR mode
+ * and need a higher signal drive strength
+ */
+ allwinner,drive = <3>;
+ allwinner,pull = <0>;
+ };
+
+ spi1_pins_a: spi1@0 {
+ allwinner,pins = "PI16", "PI17", "PI18", "PI19";
+ allwinner,function = "spi1";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ spi2_pins_a: spi2@0 {
+ allwinner,pins = "PC19", "PC20", "PC21", "PC22";
+ allwinner,function = "spi2";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};
timer@01c20c00 {
@@ -455,7 +668,7 @@
};
wdt: watchdog@01c20c90 {
- compatible = "allwinner,sun4i-wdt";
+ compatible = "allwinner,sun4i-a10-wdt";
reg = <0x01c20c90 0x10>;
};
@@ -601,6 +814,21 @@
status = "disabled";
};
+ gmac: ethernet@01c50000 {
+ compatible = "allwinner,sun7i-a20-gmac";
+ reg = <0x01c50000 0x10000>;
+ interrupts = <0 85 4>;
+ interrupt-names = "macirq";
+ clocks = <&ahb_gates 49>, <&gmac_tx_clk>;
+ clock-names = "stmmaceth", "allwinner_gmac_tx";
+ snps,pbl = <2>;
+ snps,fixed-burst;
+ snps,force_sf_dma_mode;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
hstimer@01c60000 {
compatible = "allwinner,sun7i-a20-hstimer";
reg = <0x01c60000 0x1000>;
diff --git a/arch/arm/boot/dts/sunxi-common-regulators.dtsi b/arch/arm/boot/dts/sunxi-common-regulators.dtsi
new file mode 100644
index 000000000000..18eeac0670b9
--- /dev/null
+++ b/arch/arm/boot/dts/sunxi-common-regulators.dtsi
@@ -0,0 +1,75 @@
+/*
+ * sunxi boards common regulator (ahci target power supply, usb-vbus) code
+ *
+ * Copyright 2014 - Hans de Goede <hdegoede@redhat.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/ {
+ soc@01c00000 {
+ pio: pinctrl@01c20800 {
+ ahci_pwr_pin_a: ahci_pwr_pin@0 {
+ allwinner,pins = "PB8";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ usb1_vbus_pin_a: usb1_vbus_pin@0 {
+ allwinner,pins = "PH6";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+
+ usb2_vbus_pin_a: usb2_vbus_pin@0 {
+ allwinner,pins = "PH3";
+ allwinner,function = "gpio_out";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
+ };
+ };
+
+ reg_ahci_5v: ahci-5v {
+ compatible = "regulator-fixed";
+ pinctrl-names = "default";
+ pinctrl-0 = <&ahci_pwr_pin_a>;
+ regulator-name = "ahci-5v";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ gpio = <&pio 1 8 0>;
+ status = "disabled";
+ };
+
+ reg_usb1_vbus: usb1-vbus {
+ compatible = "regulator-fixed";
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb1_vbus_pin_a>;
+ regulator-name = "usb1-vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ gpio = <&pio 7 6 0>;
+ status = "disabled";
+ };
+
+ reg_usb2_vbus: usb2-vbus {
+ compatible = "regulator-fixed";
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb2_vbus_pin_a>;
+ regulator-name = "usb2-vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ gpio = <&pio 7 3 0>;
+ status = "disabled";
+ };
+};
diff --git a/arch/arm/boot/dts/tegra114-dalmore.dts b/arch/arm/boot/dts/tegra114-dalmore.dts
index 73aecfb57ccb..a288a12823ed 100644
--- a/arch/arm/boot/dts/tegra114-dalmore.dts
+++ b/arch/arm/boot/dts/tegra114-dalmore.dts
@@ -1,3 +1,8 @@
+/*
+ * This dts file supports Dalmore A04.
+ * Other board revisions are not supported
+ */
+
/dts-v1/;
#include <dt-bindings/input/input.h>
@@ -715,7 +720,6 @@
nvidia,pins = "drive_sdio1";
nvidia,high-speed-mode = <TEGRA_PIN_ENABLE>;
nvidia,schmitt = <TEGRA_PIN_DISABLE>;
- nvidia,low-power-mode = <TEGRA_PIN_LP_DRIVE_DIV_1>;
nvidia,pull-down-strength = <36>;
nvidia,pull-up-strength = <20>;
nvidia,slew-rate-rising = <TEGRA_PIN_SLEW_RATE_SLOW>;
@@ -725,7 +729,6 @@
nvidia,pins = "drive_sdio3";
nvidia,high-speed-mode = <TEGRA_PIN_ENABLE>;
nvidia,schmitt = <TEGRA_PIN_DISABLE>;
- nvidia,low-power-mode = <TEGRA_PIN_LP_DRIVE_DIV_1>;
nvidia,pull-down-strength = <22>;
nvidia,pull-up-strength = <36>;
nvidia,slew-rate-rising = <TEGRA_PIN_SLEW_RATE_FASTEST>;
@@ -735,12 +738,10 @@
nvidia,pins = "drive_gma";
nvidia,high-speed-mode = <TEGRA_PIN_ENABLE>;
nvidia,schmitt = <TEGRA_PIN_DISABLE>;
- nvidia,low-power-mode = <TEGRA_PIN_LP_DRIVE_DIV_1>;
nvidia,pull-down-strength = <2>;
nvidia,pull-up-strength = <1>;
nvidia,slew-rate-rising = <TEGRA_PIN_SLEW_RATE_FASTEST>;
nvidia,slew-rate-falling = <TEGRA_PIN_SLEW_RATE_FASTEST>;
- nvidia,drive-type = <1>;
};
};
};
diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi
index 44ec401ec366..fdc559ab2db3 100644
--- a/arch/arm/boot/dts/tegra114.dtsi
+++ b/arch/arm/boot/dts/tegra114.dtsi
@@ -604,7 +604,7 @@
clocks = <&tegra_car TEGRA114_CLK_SDMMC1>;
resets = <&tegra_car 14>;
reset-names = "sdhci";
- status = "disable";
+ status = "disabled";
};
sdhci@78000200 {
@@ -614,7 +614,7 @@
clocks = <&tegra_car TEGRA114_CLK_SDMMC2>;
resets = <&tegra_car 9>;
reset-names = "sdhci";
- status = "disable";
+ status = "disabled";
};
sdhci@78000400 {
@@ -624,7 +624,7 @@
clocks = <&tegra_car TEGRA114_CLK_SDMMC3>;
resets = <&tegra_car 69>;
reset-names = "sdhci";
- status = "disable";
+ status = "disabled";
};
sdhci@78000600 {
@@ -634,7 +634,7 @@
clocks = <&tegra_car TEGRA114_CLK_SDMMC4>;
resets = <&tegra_car 15>;
reset-names = "sdhci";
- status = "disable";
+ status = "disabled";
};
usb@7d000000 {
diff --git a/arch/arm/boot/dts/tegra124-venice2.dts b/arch/arm/boot/dts/tegra124-venice2.dts
index c6dcef513e5d..c17283c04598 100644
--- a/arch/arm/boot/dts/tegra124-venice2.dts
+++ b/arch/arm/boot/dts/tegra124-venice2.dts
@@ -8,15 +8,29 @@
compatible = "nvidia,venice2", "nvidia,tegra124";
aliases {
- rtc0 = "/i2c@7000d000/as3722@40";
- rtc1 = "/rtc@7000e000";
+ rtc0 = "/i2c@0,7000d000/pmic@40";
+ rtc1 = "/rtc@0,7000e000";
};
memory {
- reg = <0x80000000 0x80000000>;
+ reg = <0x0 0x80000000 0x0 0x80000000>;
};
- pinmux: pinmux@70000868 {
+ host1x@0,50000000 {
+ sor@0,54540000 {
+ status = "okay";
+
+ nvidia,dpaux = <&dpaux>;
+ nvidia,panel = <&panel>;
+ };
+
+ dpaux: dpaux@0,545c0000 {
+ vdd-supply = <&vdd_3v3_panel>;
+ status = "okay";
+ };
+ };
+
+ pinmux: pinmux@0,70000868 {
pinctrl-names = "default";
pinctrl-0 = <&pinmux_default>;
@@ -138,14 +152,9 @@
nvidia,enable-input = <TEGRA_PIN_ENABLE>;
};
sdmmc1_clk_pz0 {
- nvidia,pins = "sdmmc1_clk_pz0",
- "sdmmc1_cmd_pz1",
- "sdmmc1_dat0_py7",
- "sdmmc1_dat1_py6",
- "sdmmc1_dat2_py5",
- "sdmmc1_dat3_py4";
+ nvidia,pins = "sdmmc1_clk_pz0";
nvidia,function = "sdmmc1";
- nvidia,enable-input = <TEGRA_PIN_ENABLE>;
+ nvidia,enable-input = <TEGRA_PIN_DISABLE>;
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
};
@@ -402,19 +411,11 @@
nvidia,enable-input = <TEGRA_PIN_ENABLE>;
};
usb_vbus_en0_pn4 {
- nvidia,pins = "usb_vbus_en0_pn4";
+ nvidia,pins = "usb_vbus_en0_pn4",
+ "usb_vbus_en1_pn5";
nvidia,function = "usb";
nvidia,enable-input = <TEGRA_PIN_ENABLE>;
- nvidia,pull = <TEGRA_PIN_PULL_UP>;
- nvidia,tristate = <TEGRA_PIN_DISABLE>;
- nvidia,lock = <TEGRA_PIN_DISABLE>;
- nvidia,open-drain = <TEGRA_PIN_ENABLE>;
- };
- usb_vbus_en1_pn5 {
- nvidia,pins = "usb_vbus_en1_pn5";
- nvidia,function = "usb";
- nvidia,enable-input = <TEGRA_PIN_ENABLE>;
- nvidia,pull = <TEGRA_PIN_PULL_UP>;
+ nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
nvidia,lock = <TEGRA_PIN_DISABLE>;
nvidia,open-drain = <TEGRA_PIN_ENABLE>;
@@ -423,7 +424,6 @@
nvidia,pins = "drive_sdio1";
nvidia,high-speed-mode = <TEGRA_PIN_ENABLE>;
nvidia,schmitt = <TEGRA_PIN_DISABLE>;
- nvidia,low-power-mode = <TEGRA_PIN_LP_DRIVE_DIV_1>;
nvidia,pull-down-strength = <32>;
nvidia,pull-up-strength = <42>;
nvidia,slew-rate-rising = <TEGRA_PIN_SLEW_RATE_FASTEST>;
@@ -433,7 +433,6 @@
nvidia,pins = "drive_sdio3";
nvidia,high-speed-mode = <TEGRA_PIN_ENABLE>;
nvidia,schmitt = <TEGRA_PIN_DISABLE>;
- nvidia,low-power-mode = <TEGRA_PIN_LP_DRIVE_DIV_1>;
nvidia,pull-down-strength = <20>;
nvidia,pull-up-strength = <36>;
nvidia,slew-rate-rising = <TEGRA_PIN_SLEW_RATE_FASTEST>;
@@ -572,15 +571,15 @@
};
};
- serial@70006000 {
+ serial@0,70006000 {
status = "okay";
};
- pwm: pwm@7000a000 {
+ pwm: pwm@0,7000a000 {
status = "okay";
};
- i2c@7000c000 {
+ i2c@0,7000c000 {
status = "okay";
clock-frequency = <100000>;
@@ -592,30 +591,32 @@
};
};
- i2c@7000c400 {
+ i2c@0,7000c400 {
status = "okay";
clock-frequency = <100000>;
};
- i2c@7000c500 {
+ i2c@0,7000c500 {
status = "okay";
clock-frequency = <100000>;
};
- i2c@7000c700 {
+ i2c@0,7000c700 {
status = "okay";
clock-frequency = <100000>;
};
- i2c@7000d000 {
+ i2c@0,7000d000 {
status = "okay";
clock-frequency = <400000>;
- as3722: as3722@40 {
+ pmic: pmic@40 {
compatible = "ams,as3722";
reg = <0x40>;
interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>;
+ ams,system-power-controller;
+
#interrupt-cells = <2>;
interrupt-controller;
@@ -650,19 +651,19 @@
};
regulators {
- vsup-sd2-supply = <&vdd_ac_bat_reg>;
- vsup-sd3-supply = <&vdd_ac_bat_reg>;
- vsup-sd4-supply = <&vdd_ac_bat_reg>;
- vsup-sd5-supply = <&vdd_ac_bat_reg>;
- vin-ldo0-supply = <&as3722_sd2>;
- vin-ldo1-6-supply = <&vdd_ac_bat_reg>;
- vin-ldo2-5-7-supply = <&as3722_sd5>;
- vin-ldo3-4-supply = <&vdd_ac_bat_reg>;
- vin-ldo9-10-supply = <&vdd_ac_bat_reg>;
- vin-ldo11-supply = <&vdd_ac_bat_reg>;
+ vsup-sd2-supply = <&vdd_5v0_sys>;
+ vsup-sd3-supply = <&vdd_5v0_sys>;
+ vsup-sd4-supply = <&vdd_5v0_sys>;
+ vsup-sd5-supply = <&vdd_5v0_sys>;
+ vin-ldo0-supply = <&vdd_1v35_lp0>;
+ vin-ldo1-6-supply = <&vdd_3v3_run>;
+ vin-ldo2-5-7-supply = <&vddio_1v8>;
+ vin-ldo3-4-supply = <&vdd_3v3_sys>;
+ vin-ldo9-10-supply = <&vdd_5v0_sys>;
+ vin-ldo11-supply = <&vdd_3v3_run>;
sd0 {
- regulator-name = "vdd-cpu";
+ regulator-name = "+VDD_CPU_AP";
regulator-min-microvolt = <700000>;
regulator-max-microvolt = <1400000>;
regulator-min-microamp = <3500000>;
@@ -673,7 +674,7 @@
};
sd1 {
- regulator-name = "vdd-core";
+ regulator-name = "+VDD_CORE";
regulator-min-microvolt = <700000>;
regulator-max-microvolt = <1350000>;
regulator-min-microamp = <2500000>;
@@ -683,8 +684,8 @@
ams,external-control = <1>;
};
- as3722_sd2: sd2 {
- regulator-name = "vddio-ddr";
+ vdd_1v35_lp0: sd2 {
+ regulator-name = "+1.35V_LP0(sd2)";
regulator-min-microvolt = <1350000>;
regulator-max-microvolt = <1350000>;
regulator-always-on;
@@ -692,7 +693,7 @@
};
sd3 {
- regulator-name = "vddio-ddr-2phase";
+ regulator-name = "+1.35V_LP0(sd3)";
regulator-min-microvolt = <1350000>;
regulator-max-microvolt = <1350000>;
regulator-always-on;
@@ -700,15 +701,13 @@
};
sd4 {
- regulator-name = "avdd-pex-sata";
+ regulator-name = "+1.05V_RUN";
regulator-min-microvolt = <1050000>;
regulator-max-microvolt = <1050000>;
- regulator-boot-on;
- regulator-always-on;
};
- as3722_sd5: sd5 {
- regulator-name = "vddio-sys";
+ vddio_1v8: sd5 {
+ regulator-name = "+1.8V_VDDIO";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-boot-on;
@@ -716,7 +715,7 @@
};
sd6 {
- regulator-name = "vdd-gpu";
+ regulator-name = "+VDD_GPU_AP";
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <1200000>;
regulator-min-microamp = <3500000>;
@@ -726,7 +725,7 @@
};
ldo0 {
- regulator-name = "avdd_pll";
+ regulator-name = "+1.05V_RUN_AVDD";
regulator-min-microvolt = <1050000>;
regulator-max-microvolt = <1050000>;
regulator-boot-on;
@@ -735,13 +734,13 @@
};
ldo1 {
- regulator-name = "run-cam-1.8";
+ regulator-name = "+1.8V_RUN_CAM";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
};
ldo2 {
- regulator-name = "gen-avdd,vddio-hsic";
+ regulator-name = "+1.2V_GEN_AVDD";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
regulator-boot-on;
@@ -749,7 +748,7 @@
};
ldo3 {
- regulator-name = "vdd-rtc";
+ regulator-name = "+1.00V_LP0_VDD_RTC";
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1000000>;
regulator-boot-on;
@@ -757,48 +756,44 @@
ams,enable-tracking;
};
- ldo4 {
- regulator-name = "vdd-cam";
+ vdd_run_cam: ldo4 {
+ regulator-name = "+3.3V_RUN_CAM";
regulator-min-microvolt = <2800000>;
regulator-max-microvolt = <2800000>;
- regulator-boot-on;
- regulator-always-on;
};
ldo5 {
- regulator-name = "vdd-cam-front";
+ regulator-name = "+1.2V_RUN_CAM_FRONT";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
};
- ldo6 {
- regulator-name = "vddio-sdmmc3";
+ vddio_sdmmc3: ldo6 {
+ regulator-name = "+VDDIO_SDMMC3";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
- regulator-boot-on;
- regulator-always-on;
};
ldo7 {
- regulator-name = "vdd-cam-rear";
+ regulator-name = "+1.05V_RUN_CAM_REAR";
regulator-min-microvolt = <1050000>;
regulator-max-microvolt = <1050000>;
};
ldo9 {
- regulator-name = "vdd-touch";
+ regulator-name = "+2.8V_RUN_TOUCH";
regulator-min-microvolt = <2800000>;
regulator-max-microvolt = <2800000>;
};
ldo10 {
- regulator-name = "vdd-cam-af";
+ regulator-name = "+2.8V_RUN_CAM_AF";
regulator-min-microvolt = <2800000>;
regulator-max-microvolt = <2800000>;
};
ldo11 {
- regulator-name = "vpp-fuse";
+ regulator-name = "+1.8V_RUN_VPP_FUSE";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
};
@@ -806,7 +801,7 @@
};
};
- spi@7000d400 {
+ spi@0,7000d400 {
status = "okay";
cros-ec@0 {
@@ -912,7 +907,17 @@
};
};
- pmc@7000e400 {
+ spi@0,7000da00 {
+ status = "okay";
+ spi-max-frequency = <25000000>;
+ spi-flash@0 {
+ compatible = "winbond,w25q32dw";
+ reg = <0>;
+ spi-max-frequency = <20000000>;
+ };
+ };
+
+ pmc@0,7000e400 {
nvidia,invert-interrupt;
nvidia,suspend-mode = <1>;
nvidia,cpu-pwr-good-time = <500>;
@@ -923,24 +928,63 @@
nvidia,sys-clock-req-active-high;
};
- sdhci@700b0400 {
+ sdhci@0,700b0400 {
cd-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_HIGH>;
power-gpios = <&gpio TEGRA_GPIO(R, 0) GPIO_ACTIVE_HIGH>;
status = "okay";
bus-width = <4>;
+ vmmc-supply = <&vddio_sdmmc3>;
};
- sdhci@700b0600 {
+ sdhci@0,700b0600 {
status = "okay";
bus-width = <8>;
};
- ahub@70300000 {
- i2s@70301100 {
+ ahub@0,70300000 {
+ i2s@0,70301100 {
status = "okay";
};
};
+ usb@0,7d000000 {
+ status = "okay";
+ };
+
+ usb-phy@0,7d000000 {
+ status = "okay";
+ vbus-supply = <&vdd_usb1_vbus>;
+ };
+
+ usb@0,7d004000 {
+ status = "okay";
+ };
+
+ usb-phy@0,7d004000 {
+ status = "okay";
+ vbus-supply = <&vdd_run_cam>;
+ };
+
+ usb@0,7d008000 {
+ status = "okay";
+ };
+
+ usb-phy@0,7d008000 {
+ status = "okay";
+ vbus-supply = <&vdd_usb3_vbus>;
+ };
+
+ backlight: backlight {
+ compatible = "pwm-backlight";
+
+ enable-gpios = <&gpio TEGRA_GPIO(H, 2) GPIO_ACTIVE_HIGH>;
+ power-supply = <&vdd_led>;
+ pwms = <&pwm 1 1000000>;
+
+ brightness-levels = <0 4 8 16 32 64 128 255>;
+ default-brightness-level = <6>;
+ };
+
clocks {
compatible = "simple-bus";
#address-cells = <1>;
@@ -948,7 +992,7 @@
clk32k_in: clock@0 {
compatible = "fixed-clock";
- reg=<0>;
+ reg = <0>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
@@ -966,104 +1010,140 @@
};
};
+ panel: panel {
+ compatible = "lg,lp129qe", "simple-panel";
+
+ backlight = <&backlight>;
+ ddc-i2c-bus = <&dpaux>;
+ };
+
regulators {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
- vdd_ac_bat_reg: regulator@0 {
+ vdd_mux: regulator@0 {
compatible = "regulator-fixed";
reg = <0>;
- regulator-name = "vdd_ac_bat";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
+ regulator-name = "+VDD_MUX";
+ regulator-min-microvolt = <12000000>;
+ regulator-max-microvolt = <12000000>;
regulator-always-on;
+ regulator-boot-on;
};
- vdd_3v3_reg: regulator@1 {
+ vdd_5v0_sys: regulator@1 {
compatible = "regulator-fixed";
reg = <1>;
- regulator-name = "vdd_3v3";
- regulator-min-microvolt = <3300000>;
- regulator-max-microvolt = <3300000>;
+ regulator-name = "+5V_SYS";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
regulator-always-on;
regulator-boot-on;
- enable-active-high;
- gpio = <&as3722 1 GPIO_ACTIVE_HIGH>;
+ vin-supply = <&vdd_mux>;
};
- vdd_3v3_modem_reg: regulator@2 {
+ vdd_3v3_sys: regulator@2 {
compatible = "regulator-fixed";
reg = <2>;
- regulator-name = "vdd-modem-3v3";
+ regulator-name = "+3.3V_SYS";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
- enable-active-high;
- gpio = <&as3722 2 GPIO_ACTIVE_HIGH>;
+ regulator-always-on;
+ regulator-boot-on;
+ vin-supply = <&vdd_mux>;
};
- vdd_hdmi_5v0_reg: regulator@3 {
+ vdd_3v3_run: regulator@3 {
compatible = "regulator-fixed";
reg = <3>;
- regulator-name = "vdd-hdmi-5v0";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
+ regulator-name = "+3.3V_RUN";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&pmic 1 GPIO_ACTIVE_HIGH>;
enable-active-high;
- gpio = <&gpio TEGRA_GPIO(K, 6) GPIO_ACTIVE_HIGH>;
+ vin-supply = <&vdd_3v3_sys>;
};
- vdd_bl_reg: regulator@4 {
+ vdd_3v3_hdmi: regulator@4 {
compatible = "regulator-fixed";
reg = <4>;
- regulator-name = "vdd-bl";
+ regulator-name = "+3.3V_AVDD_HDMI_AP_GATED";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
- gpio = <&gpio TEGRA_GPIO(P, 2) GPIO_ACTIVE_LOW>;
+ vin-supply = <&vdd_3v3_run>;
};
- vdd_ts_sw_5v0: regulator@5 {
+ vdd_led: regulator@5 {
compatible = "regulator-fixed";
reg = <5>;
- regulator-name = "vdd_ts_sw";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
+ regulator-name = "+VDD_LED";
+ gpio = <&gpio TEGRA_GPIO(P, 2) GPIO_ACTIVE_HIGH>;
enable-active-high;
- regulator-boot-on;
- gpio = <&gpio TEGRA_GPIO(K, 1) GPIO_ACTIVE_LOW>;
+ vin-supply = <&vdd_mux>;
};
- usb1_vbus_reg: regulator@6 {
+ vdd_5v0_ts: regulator@6 {
compatible = "regulator-fixed";
reg = <6>;
- regulator-name = "usb1_vbus";
+ regulator-name = "+5V_VDD_TS_SW";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
regulator-boot-on;
+ gpio = <&gpio TEGRA_GPIO(K, 1) GPIO_ACTIVE_HIGH>;
enable-active-high;
- gpio = <&gpio TEGRA_GPIO(N, 4) GPIO_ACTIVE_HIGH>;
- gpio-open-drain;
+ vin-supply = <&vdd_5v0_sys>;
};
- usb3_vbus_reg: regulator@7 {
+ vdd_usb1_vbus: regulator@7 {
compatible = "regulator-fixed";
reg = <7>;
- regulator-name = "usb3_vbus";
+ regulator-name = "+5V_USB_HS";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
- regulator-boot-on;
+ gpio = <&gpio TEGRA_GPIO(N, 4) GPIO_ACTIVE_HIGH>;
enable-active-high;
- gpio = <&gpio TEGRA_GPIO(N, 5) GPIO_ACTIVE_HIGH>;
gpio-open-drain;
+ vin-supply = <&vdd_5v0_sys>;
};
- panel_3v3_reg: regulator@8 {
+ vdd_usb3_vbus: regulator@8 {
compatible = "regulator-fixed";
reg = <8>;
- regulator-name = "panel_3v3";
+ regulator-name = "+5V_USB_SS";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpio TEGRA_GPIO(N, 5) GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ gpio-open-drain;
+ vin-supply = <&vdd_5v0_sys>;
+ };
+
+ vdd_3v3_panel: regulator@9 {
+ compatible = "regulator-fixed";
+ reg = <9>;
+ regulator-name = "+3.3V_PANEL";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&pmic 4 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ vin-supply = <&vdd_3v3_run>;
+ };
+
+ vdd_3v3_lp0: regulator@10 {
+ compatible = "regulator-fixed";
+ reg = <10>;
+ regulator-name = "+3.3V_LP0";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
+ /*
+ * TODO: find a way to wire this up with the USB EHCI
+ * controllers so that it can be enabled on demand.
+ */
+ regulator-always-on;
+ gpio = <&pmic 2 GPIO_ACTIVE_HIGH>;
enable-active-high;
- gpio = <&as3722 4 GPIO_ACTIVE_HIGH>;
+ vin-supply = <&vdd_3v3_sys>;
};
};
diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
index ec0698a8354a..cf45a1a39483 100644
--- a/arch/arm/boot/dts/tegra124.dtsi
+++ b/arch/arm/boot/dts/tegra124.dtsi
@@ -8,22 +8,91 @@
/ {
compatible = "nvidia,tegra124";
interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ host1x@0,50000000 {
+ compatible = "nvidia,tegra124-host1x", "simple-bus";
+ reg = <0x0 0x50000000 0x0 0x00034000>;
+ interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>, /* syncpt */
+ <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>; /* general */
+ clocks = <&tegra_car TEGRA124_CLK_HOST1X>;
+ resets = <&tegra_car 28>;
+ reset-names = "host1x";
+
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ ranges = <0 0x54000000 0 0x54000000 0 0x01000000>;
+
+ dc@0,54200000 {
+ compatible = "nvidia,tegra124-dc";
+ reg = <0x0 0x54200000 0x0 0x00040000>;
+ interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&tegra_car TEGRA124_CLK_DISP1>,
+ <&tegra_car TEGRA124_CLK_PLL_P>;
+ clock-names = "dc", "parent";
+ resets = <&tegra_car 27>;
+ reset-names = "dc";
+
+ nvidia,head = <0>;
+ };
+
+ dc@0,54240000 {
+ compatible = "nvidia,tegra124-dc";
+ reg = <0x0 0x54240000 0x0 0x00040000>;
+ interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&tegra_car TEGRA124_CLK_DISP2>,
+ <&tegra_car TEGRA124_CLK_PLL_P>;
+ clock-names = "dc", "parent";
+ resets = <&tegra_car 26>;
+ reset-names = "dc";
+
+ nvidia,head = <1>;
+ };
- gic: interrupt-controller@50041000 {
+ sor@0,54540000 {
+ compatible = "nvidia,tegra124-sor";
+ reg = <0x0 0x54540000 0x0 0x00040000>;
+ interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&tegra_car TEGRA124_CLK_SOR0>,
+ <&tegra_car TEGRA124_CLK_PLL_D_OUT0>,
+ <&tegra_car TEGRA124_CLK_PLL_DP>,
+ <&tegra_car TEGRA124_CLK_CLK_M>;
+ clock-names = "sor", "parent", "dp", "safe";
+ resets = <&tegra_car 182>;
+ reset-names = "sor";
+ status = "disabled";
+ };
+
+ dpaux@0,545c0000 {
+ compatible = "nvidia,tegra124-dpaux";
+ reg = <0x0 0x545c0000 0x0 0x00040000>;
+ interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&tegra_car TEGRA124_CLK_DPAUX>,
+ <&tegra_car TEGRA124_CLK_PLL_DP>;
+ clock-names = "dpaux", "parent";
+ resets = <&tegra_car 181>;
+ reset-names = "dpaux";
+ status = "disabled";
+ };
+ };
+
+ gic: interrupt-controller@0,50041000 {
compatible = "arm,cortex-a15-gic";
#interrupt-cells = <3>;
interrupt-controller;
- reg = <0x50041000 0x1000>,
- <0x50042000 0x1000>,
- <0x50044000 0x2000>,
- <0x50046000 0x2000>;
+ reg = <0x0 0x50041000 0x0 0x1000>,
+ <0x0 0x50042000 0x0 0x1000>,
+ <0x0 0x50044000 0x0 0x2000>,
+ <0x0 0x50046000 0x0 0x2000>;
interrupts = <GIC_PPI 9
(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
};
- timer@60005000 {
+ timer@0,60005000 {
compatible = "nvidia,tegra124-timer", "nvidia,tegra20-timer";
- reg = <0x60005000 0x400>;
+ reg = <0x0 0x60005000 0x0 0x400>;
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>,
@@ -33,16 +102,16 @@
clocks = <&tegra_car TEGRA124_CLK_TIMER>;
};
- tegra_car: clock@60006000 {
+ tegra_car: clock@0,60006000 {
compatible = "nvidia,tegra124-car";
- reg = <0x60006000 0x1000>;
+ reg = <0x0 0x60006000 0x0 0x1000>;
#clock-cells = <1>;
#reset-cells = <1>;
};
- gpio: gpio@6000d000 {
+ gpio: gpio@0,6000d000 {
compatible = "nvidia,tegra124-gpio", "nvidia,tegra30-gpio";
- reg = <0x6000d000 0x1000>;
+ reg = <0x0 0x6000d000 0x0 0x1000>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>,
@@ -57,9 +126,9 @@
interrupt-controller;
};
- apbdma: dma@60020000 {
+ apbdma: dma@0,60020000 {
compatible = "nvidia,tegra124-apbdma", "nvidia,tegra148-apbdma";
- reg = <0x60020000 0x1400>;
+ reg = <0x0 0x60020000 0x0 0x1400>;
interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>,
@@ -98,10 +167,10 @@
#dma-cells = <1>;
};
- pinmux: pinmux@70000868 {
+ pinmux: pinmux@0,70000868 {
compatible = "nvidia,tegra124-pinmux";
- reg = <0x70000868 0x164>, /* Pad control registers */
- <0x70003000 0x434>; /* Mux registers */
+ reg = <0x0 0x70000868 0x0 0x164>, /* Pad control registers */
+ <0x0 0x70003000 0x0 0x434>; /* Mux registers */
};
/*
@@ -112,9 +181,9 @@
* the APB DMA based serial driver, the comptible is
* "nvidia,tegra124-hsuart", "nvidia,tegra30-hsuart".
*/
- serial@70006000 {
+ serial@0,70006000 {
compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
- reg = <0x70006000 0x40>;
+ reg = <0x0 0x70006000 0x0 0x40>;
reg-shift = <2>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_UARTA>;
@@ -125,9 +194,9 @@
status = "disabled";
};
- serial@70006040 {
+ serial@0,70006040 {
compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
- reg = <0x70006040 0x40>;
+ reg = <0x0 0x70006040 0x0 0x40>;
reg-shift = <2>;
interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_UARTB>;
@@ -138,9 +207,9 @@
status = "disabled";
};
- serial@70006200 {
+ serial@0,70006200 {
compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
- reg = <0x70006200 0x40>;
+ reg = <0x0 0x70006200 0x0 0x40>;
reg-shift = <2>;
interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_UARTC>;
@@ -151,9 +220,9 @@
status = "disabled";
};
- serial@70006300 {
+ serial@0,70006300 {
compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
- reg = <0x70006300 0x40>;
+ reg = <0x0 0x70006300 0x0 0x40>;
reg-shift = <2>;
interrupts = <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_UARTD>;
@@ -164,9 +233,9 @@
status = "disabled";
};
- serial@70006400 {
+ serial@0,70006400 {
compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
- reg = <0x70006400 0x40>;
+ reg = <0x0 0x70006400 0x0 0x40>;
reg-shift = <2>;
interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_UARTE>;
@@ -177,9 +246,9 @@
status = "disabled";
};
- pwm@7000a000 {
+ pwm@0,7000a000 {
compatible = "nvidia,tegra124-pwm", "nvidia,tegra20-pwm";
- reg = <0x7000a000 0x100>;
+ reg = <0x0 0x7000a000 0x0 0x100>;
#pwm-cells = <2>;
clocks = <&tegra_car TEGRA124_CLK_PWM>;
resets = <&tegra_car 17>;
@@ -187,9 +256,9 @@
status = "disabled";
};
- i2c@7000c000 {
+ i2c@0,7000c000 {
compatible = "nvidia,tegra124-i2c", "nvidia,tegra114-i2c";
- reg = <0x7000c000 0x100>;
+ reg = <0x0 0x7000c000 0x0 0x100>;
interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -202,9 +271,9 @@
status = "disabled";
};
- i2c@7000c400 {
+ i2c@0,7000c400 {
compatible = "nvidia,tegra124-i2c", "nvidia,tegra114-i2c";
- reg = <0x7000c400 0x100>;
+ reg = <0x0 0x7000c400 0x0 0x100>;
interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -217,9 +286,9 @@
status = "disabled";
};
- i2c@7000c500 {
+ i2c@0,7000c500 {
compatible = "nvidia,tegra124-i2c", "nvidia,tegra114-i2c";
- reg = <0x7000c500 0x100>;
+ reg = <0x0 0x7000c500 0x0 0x100>;
interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -232,9 +301,9 @@
status = "disabled";
};
- i2c@7000c700 {
+ i2c@0,7000c700 {
compatible = "nvidia,tegra124-i2c", "nvidia,tegra114-i2c";
- reg = <0x7000c700 0x100>;
+ reg = <0x0 0x7000c700 0x0 0x100>;
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -247,9 +316,9 @@
status = "disabled";
};
- i2c@7000d000 {
+ i2c@0,7000d000 {
compatible = "nvidia,tegra124-i2c", "nvidia,tegra114-i2c";
- reg = <0x7000d000 0x100>;
+ reg = <0x0 0x7000d000 0x0 0x100>;
interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -262,9 +331,9 @@
status = "disabled";
};
- i2c@7000d100 {
+ i2c@0,7000d100 {
compatible = "nvidia,tegra124-i2c", "nvidia,tegra114-i2c";
- reg = <0x7000d100 0x100>;
+ reg = <0x0 0x7000d100 0x0 0x100>;
interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -277,9 +346,9 @@
status = "disabled";
};
- spi@7000d400 {
+ spi@0,7000d400 {
compatible = "nvidia,tegra124-spi", "nvidia,tegra114-spi";
- reg = <0x7000d400 0x200>;
+ reg = <0x0 0x7000d400 0x0 0x200>;
interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -292,9 +361,9 @@
status = "disabled";
};
- spi@7000d600 {
+ spi@0,7000d600 {
compatible = "nvidia,tegra124-spi", "nvidia,tegra114-spi";
- reg = <0x7000d600 0x200>;
+ reg = <0x0 0x7000d600 0x0 0x200>;
interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -307,9 +376,9 @@
status = "disabled";
};
- spi@7000d800 {
+ spi@0,7000d800 {
compatible = "nvidia,tegra124-spi", "nvidia,tegra114-spi";
- reg = <0x7000d800 0x200>;
+ reg = <0x0 0x7000d800 0x0 0x200>;
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -322,9 +391,9 @@
status = "disabled";
};
- spi@7000da00 {
+ spi@0,7000da00 {
compatible = "nvidia,tegra124-spi", "nvidia,tegra114-spi";
- reg = <0x7000da00 0x200>;
+ reg = <0x0 0x7000da00 0x0 0x200>;
interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -337,9 +406,9 @@
status = "disabled";
};
- spi@7000dc00 {
+ spi@0,7000dc00 {
compatible = "nvidia,tegra124-spi", "nvidia,tegra114-spi";
- reg = <0x7000dc00 0x200>;
+ reg = <0x0 0x7000dc00 0x0 0x200>;
interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -352,9 +421,9 @@
status = "disabled";
};
- spi@7000de00 {
+ spi@0,7000de00 {
compatible = "nvidia,tegra124-spi", "nvidia,tegra114-spi";
- reg = <0x7000de00 0x200>;
+ reg = <0x0 0x7000de00 0x0 0x200>;
interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
@@ -367,65 +436,65 @@
status = "disabled";
};
- rtc@7000e000 {
+ rtc@0,7000e000 {
compatible = "nvidia,tegra124-rtc", "nvidia,tegra20-rtc";
- reg = <0x7000e000 0x100>;
+ reg = <0x0 0x7000e000 0x0 0x100>;
interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_RTC>;
};
- pmc@7000e400 {
+ pmc@0,7000e400 {
compatible = "nvidia,tegra124-pmc";
- reg = <0x7000e400 0x400>;
+ reg = <0x0 0x7000e400 0x0 0x400>;
clocks = <&tegra_car TEGRA124_CLK_PCLK>, <&clk32k_in>;
clock-names = "pclk", "clk32k_in";
};
- sdhci@700b0000 {
+ sdhci@0,700b0000 {
compatible = "nvidia,tegra124-sdhci";
- reg = <0x700b0000 0x200>;
+ reg = <0x0 0x700b0000 0x0 0x200>;
interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_SDMMC1>;
resets = <&tegra_car 14>;
reset-names = "sdhci";
- status = "disable";
+ status = "disabled";
};
- sdhci@700b0200 {
+ sdhci@0,700b0200 {
compatible = "nvidia,tegra124-sdhci";
- reg = <0x700b0200 0x200>;
+ reg = <0x0 0x700b0200 0x0 0x200>;
interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_SDMMC2>;
resets = <&tegra_car 9>;
reset-names = "sdhci";
- status = "disable";
+ status = "disabled";
};
- sdhci@700b0400 {
+ sdhci@0,700b0400 {
compatible = "nvidia,tegra124-sdhci";
- reg = <0x700b0400 0x200>;
+ reg = <0x0 0x700b0400 0x0 0x200>;
interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_SDMMC3>;
resets = <&tegra_car 69>;
reset-names = "sdhci";
- status = "disable";
+ status = "disabled";
};
- sdhci@700b0600 {
+ sdhci@0,700b0600 {
compatible = "nvidia,tegra124-sdhci";
- reg = <0x700b0600 0x200>;
+ reg = <0x0 0x700b0600 0x0 0x200>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_SDMMC4>;
resets = <&tegra_car 15>;
reset-names = "sdhci";
- status = "disable";
+ status = "disabled";
};
- ahub@70300000 {
+ ahub@0,70300000 {
compatible = "nvidia,tegra124-ahub";
- reg = <0x70300000 0x200>,
- <0x70300800 0x800>,
- <0x70300200 0x600>;
+ reg = <0x0 0x70300000 0x0 0x200>,
+ <0x0 0x70300800 0x0 0x800>,
+ <0x0 0x70300200 0x0 0x600>;
interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_D_AUDIO>,
<&tegra_car TEGRA124_CLK_APBIF>;
@@ -470,12 +539,12 @@
"rx6", "tx6", "rx7", "tx7", "rx8", "tx8",
"rx9", "tx9";
ranges;
- #address-cells = <1>;
- #size-cells = <1>;
+ #address-cells = <2>;
+ #size-cells = <2>;
- tegra_i2s0: i2s@70301000 {
+ tegra_i2s0: i2s@0,70301000 {
compatible = "nvidia,tegra124-i2s";
- reg = <0x70301000 0x100>;
+ reg = <0x0 0x70301000 0x0 0x100>;
nvidia,ahub-cif-ids = <4 4>;
clocks = <&tegra_car TEGRA124_CLK_I2S0>;
resets = <&tegra_car 30>;
@@ -483,9 +552,9 @@
status = "disabled";
};
- tegra_i2s1: i2s@70301100 {
+ tegra_i2s1: i2s@0,70301100 {
compatible = "nvidia,tegra124-i2s";
- reg = <0x70301100 0x100>;
+ reg = <0x0 0x70301100 0x0 0x100>;
nvidia,ahub-cif-ids = <5 5>;
clocks = <&tegra_car TEGRA124_CLK_I2S1>;
resets = <&tegra_car 11>;
@@ -493,9 +562,9 @@
status = "disabled";
};
- tegra_i2s2: i2s@70301200 {
+ tegra_i2s2: i2s@0,70301200 {
compatible = "nvidia,tegra124-i2s";
- reg = <0x70301200 0x100>;
+ reg = <0x0 0x70301200 0x0 0x100>;
nvidia,ahub-cif-ids = <6 6>;
clocks = <&tegra_car TEGRA124_CLK_I2S2>;
resets = <&tegra_car 18>;
@@ -503,9 +572,9 @@
status = "disabled";
};
- tegra_i2s3: i2s@70301300 {
+ tegra_i2s3: i2s@0,70301300 {
compatible = "nvidia,tegra124-i2s";
- reg = <0x70301300 0x100>;
+ reg = <0x0 0x70301300 0x0 0x100>;
nvidia,ahub-cif-ids = <7 7>;
clocks = <&tegra_car TEGRA124_CLK_I2S3>;
resets = <&tegra_car 101>;
@@ -513,9 +582,9 @@
status = "disabled";
};
- tegra_i2s4: i2s@70301400 {
+ tegra_i2s4: i2s@0,70301400 {
compatible = "nvidia,tegra124-i2s";
- reg = <0x70301400 0x100>;
+ reg = <0x0 0x70301400 0x0 0x100>;
nvidia,ahub-cif-ids = <8 8>;
clocks = <&tegra_car TEGRA124_CLK_I2S4>;
resets = <&tegra_car 102>;
@@ -524,6 +593,108 @@
};
};
+ usb@0,7d000000 {
+ compatible = "nvidia,tegra124-ehci", "nvidia,tegra30-ehci", "usb-ehci";
+ reg = <0x0 0x7d000000 0x0 0x4000>;
+ interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
+ phy_type = "utmi";
+ clocks = <&tegra_car TEGRA124_CLK_USBD>;
+ resets = <&tegra_car 22>;
+ reset-names = "usb";
+ nvidia,phy = <&phy1>;
+ status = "disabled";
+ };
+
+ phy1: usb-phy@0,7d000000 {
+ compatible = "nvidia,tegra124-usb-phy", "nvidia,tegra30-usb-phy";
+ reg = <0x0 0x7d000000 0x0 0x4000>,
+ <0x0 0x7d000000 0x0 0x4000>;
+ phy_type = "utmi";
+ clocks = <&tegra_car TEGRA124_CLK_USBD>,
+ <&tegra_car TEGRA124_CLK_PLL_U>,
+ <&tegra_car TEGRA124_CLK_USBD>;
+ clock-names = "reg", "pll_u", "utmi-pads";
+ nvidia,hssync-start-delay = <0>;
+ nvidia,idle-wait-delay = <17>;
+ nvidia,elastic-limit = <16>;
+ nvidia,term-range-adj = <6>;
+ nvidia,xcvr-setup = <9>;
+ nvidia,xcvr-lsfslew = <0>;
+ nvidia,xcvr-lsrslew = <3>;
+ nvidia,hssquelch-level = <2>;
+ nvidia,hsdiscon-level = <5>;
+ nvidia,xcvr-hsslew = <12>;
+ status = "disabled";
+ };
+
+ usb@0,7d004000 {
+ compatible = "nvidia,tegra124-ehci", "nvidia,tegra30-ehci", "usb-ehci";
+ reg = <0x0 0x7d004000 0x0 0x4000>;
+ interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
+ phy_type = "utmi";
+ clocks = <&tegra_car TEGRA124_CLK_USB2>;
+ resets = <&tegra_car 58>;
+ reset-names = "usb";
+ nvidia,phy = <&phy2>;
+ status = "disabled";
+ };
+
+ phy2: usb-phy@0,7d004000 {
+ compatible = "nvidia,tegra124-usb-phy", "nvidia,tegra30-usb-phy";
+ reg = <0x0 0x7d004000 0x0 0x4000>,
+ <0x0 0x7d000000 0x0 0x4000>;
+ phy_type = "utmi";
+ clocks = <&tegra_car TEGRA124_CLK_USB2>,
+ <&tegra_car TEGRA124_CLK_PLL_U>,
+ <&tegra_car TEGRA124_CLK_USBD>;
+ clock-names = "reg", "pll_u", "utmi-pads";
+ nvidia,hssync-start-delay = <0>;
+ nvidia,idle-wait-delay = <17>;
+ nvidia,elastic-limit = <16>;
+ nvidia,term-range-adj = <6>;
+ nvidia,xcvr-setup = <9>;
+ nvidia,xcvr-lsfslew = <0>;
+ nvidia,xcvr-lsrslew = <3>;
+ nvidia,hssquelch-level = <2>;
+ nvidia,hsdiscon-level = <5>;
+ nvidia,xcvr-hsslew = <12>;
+ status = "disabled";
+ };
+
+ usb@0,7d008000 {
+ compatible = "nvidia,tegra124-ehci", "nvidia,tegra30-ehci", "usb-ehci";
+ reg = <0x0 0x7d008000 0x0 0x4000>;
+ interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+ phy_type = "utmi";
+ clocks = <&tegra_car TEGRA124_CLK_USB3>;
+ resets = <&tegra_car 59>;
+ reset-names = "usb";
+ nvidia,phy = <&phy3>;
+ status = "disabled";
+ };
+
+ phy3: usb-phy@0,7d008000 {
+ compatible = "nvidia,tegra124-usb-phy", "nvidia,tegra30-usb-phy";
+ reg = <0x0 0x7d008000 0x0 0x4000>,
+ <0x0 0x7d000000 0x0 0x4000>;
+ phy_type = "utmi";
+ clocks = <&tegra_car TEGRA124_CLK_USB3>,
+ <&tegra_car TEGRA124_CLK_PLL_U>,
+ <&tegra_car TEGRA124_CLK_USBD>;
+ clock-names = "reg", "pll_u", "utmi-pads";
+ nvidia,hssync-start-delay = <0>;
+ nvidia,idle-wait-delay = <17>;
+ nvidia,elastic-limit = <16>;
+ nvidia,term-range-adj = <6>;
+ nvidia,xcvr-setup = <9>;
+ nvidia,xcvr-lsfslew = <0>;
+ nvidia,xcvr-lsrslew = <3>;
+ nvidia,hssquelch-level = <2>;
+ nvidia,hsdiscon-level = <5>;
+ nvidia,xcvr-hsslew = <12>;
+ status = "disabled";
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts
index c7cd8e6802d7..9a39a8001f78 100644
--- a/arch/arm/boot/dts/tegra20-paz00.dts
+++ b/arch/arm/boot/dts/tegra20-paz00.dts
@@ -17,6 +17,14 @@
};
host1x@50000000 {
+ dc@54200000 {
+ rgb {
+ status = "okay";
+
+ nvidia,panel = <&panel>;
+ };
+ };
+
hdmi@54280000 {
status = "okay";
@@ -257,7 +265,11 @@
status = "okay";
};
- i2c@7000c000 {
+ pwm: pwm@7000a000 {
+ status = "okay";
+ };
+
+ lvds_ddc: i2c@7000c000 {
status = "okay";
clock-frequency = <400000>;
@@ -475,6 +487,18 @@
non-removable;
};
+ backlight: backlight {
+ compatible = "pwm-backlight";
+
+ enable-gpios = <&gpio TEGRA_GPIO(U, 4) GPIO_ACTIVE_HIGH>;
+ pwms = <&pwm 0 5000000>;
+
+ brightness-levels = <0 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240 255>;
+ default-brightness-level = <10>;
+
+ backlight-boot-off;
+ };
+
clocks {
compatible = "simple-bus";
#address-cells = <1>;
@@ -509,6 +533,16 @@
};
};
+ panel: panel {
+ compatible = "samsung,ltn101nt05", "simple-panel";
+
+ ddc-i2c-bus = <&lvds_ddc>;
+ power-supply = <&vdd_pnl_reg>;
+ enable-gpios = <&gpio TEGRA_GPIO(M, 6) GPIO_ACTIVE_HIGH>;
+
+ backlight = <&backlight>;
+ };
+
regulators {
compatible = "simple-bus";
#address-cells = <1>;
@@ -522,6 +556,16 @@
regulator-max-microvolt = <5000000>;
regulator-always-on;
};
+
+ vdd_pnl_reg: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "+3VS,vdd_pnl";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio TEGRA_GPIO(A, 4) GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
};
sound {
diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts
index a11b6e7b4759..a1d4bf9895d7 100644
--- a/arch/arm/boot/dts/tegra20-seaboard.dts
+++ b/arch/arm/boot/dts/tegra20-seaboard.dts
@@ -17,6 +17,14 @@
};
host1x@50000000 {
+ dc@54200000 {
+ rgb {
+ status = "okay";
+
+ nvidia,panel = <&panel>;
+ };
+ };
+
hdmi@54280000 {
status = "okay";
@@ -312,6 +320,10 @@
status = "okay";
};
+ pwm: pwm@7000a000 {
+ status = "okay";
+ };
+
i2c@7000c000 {
status = "okay";
clock-frequency = <400000>;
@@ -369,7 +381,7 @@
#size-cells = <0>;
};
- i2c@1 {
+ lvds_ddc: i2c@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
@@ -762,6 +774,17 @@
non-removable;
};
+ backlight: backlight {
+ compatible = "pwm-backlight";
+
+ enable-gpios = <&gpio TEGRA_GPIO(D, 4) GPIO_ACTIVE_HIGH>;
+ power-supply = <&vdd_bl_reg>;
+ pwms = <&pwm 2 5000000>;
+
+ brightness-levels = <0 4 8 16 32 64 128 255>;
+ default-brightness-level = <6>;
+ };
+
clocks {
compatible = "simple-bus";
#address-cells = <1>;
@@ -795,6 +818,16 @@
};
};
+ panel: panel {
+ compatible = "chunghwa,claa101wa01a", "simple-panel";
+
+ power-supply = <&vdd_pnl_reg>;
+ enable-gpios = <&gpio TEGRA_GPIO(B, 2) GPIO_ACTIVE_HIGH>;
+
+ backlight = <&backlight>;
+ ddc-i2c-bus = <&lvds_ddc>;
+ };
+
regulators {
compatible = "simple-bus";
#address-cells = <1>;
@@ -839,6 +872,26 @@
regulator-always-on;
regulator-boot-on;
};
+
+ vdd_pnl_reg: regulator@4 {
+ compatible = "regulator-fixed";
+ reg = <4>;
+ regulator-name = "vdd_pnl";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ gpio = <&gpio TEGRA_GPIO(C, 6) GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ vdd_bl_reg: regulator@5 {
+ compatible = "regulator-fixed";
+ reg = <5>;
+ regulator-name = "vdd_bl";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ gpio = <&gpio TEGRA_GPIO(W, 0) GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
};
sound {
diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts
index 571d12e6ac2d..ca8484cccddc 100644
--- a/arch/arm/boot/dts/tegra20-ventana.dts
+++ b/arch/arm/boot/dts/tegra20-ventana.dts
@@ -17,6 +17,14 @@
};
host1x@50000000 {
+ dc@54200000 {
+ rgb {
+ status = "okay";
+
+ nvidia,panel = <&panel>;
+ };
+ };
+
hdmi@54280000 {
status = "okay";
@@ -309,6 +317,10 @@
status = "okay";
};
+ pwm: pwm@7000a000 {
+ status = "okay";
+ };
+
i2c@7000c000 {
status = "okay";
clock-frequency = <400000>;
@@ -359,7 +371,7 @@
#size-cells = <0>;
};
- i2c@1 {
+ lvds_ddc: i2c@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
@@ -557,6 +569,17 @@
non-removable;
};
+ backlight: backlight {
+ compatible = "pwm-backlight";
+
+ enable-gpios = <&gpio TEGRA_GPIO(D, 4) GPIO_ACTIVE_HIGH>;
+ power-supply = <&vdd_bl_reg>;
+ pwms = <&pwm 2 5000000>;
+
+ brightness-levels = <0 4 8 16 32 64 128 255>;
+ default-brightness-level = <6>;
+ };
+
clocks {
compatible = "simple-bus";
#address-cells = <1>;
@@ -581,6 +604,16 @@
};
};
+ panel: panel {
+ compatible = "chunghwa,claa101wa01a", "simple-panel";
+
+ power-supply = <&vdd_pnl_reg>;
+ enable-gpios = <&gpio TEGRA_GPIO(B, 2) GPIO_ACTIVE_HIGH>;
+
+ backlight = <&backlight>;
+ ddc-i2c-bus = <&lvds_ddc>;
+ };
+
regulators {
compatible = "simple-bus";
#address-cells = <1>;
@@ -614,7 +647,7 @@
enable-active-high;
};
- regulator@3 {
+ vdd_pnl_reg: regulator@3 {
compatible = "regulator-fixed";
reg = <3>;
regulator-name = "vdd_pnl";
@@ -624,7 +657,7 @@
enable-active-high;
};
- regulator@4 {
+ vdd_bl_reg: regulator@4 {
compatible = "regulator-fixed";
reg = <4>;
regulator-name = "vdd_bl";
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index 48d2a7f4d0c0..a7ddf70df50b 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -556,6 +556,10 @@
GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
interrupt-names = "intr", "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi
index 1e156d9d0506..0cf0848a82d8 100644
--- a/arch/arm/boot/dts/tegra30-cardhu.dtsi
+++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi
@@ -187,6 +187,13 @@
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(L, 0) IRQ_TYPE_LEVEL_HIGH>;
};
+
+ i2cmux@70 {
+ compatible = "nxp,pca9546";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x70>;
+ };
};
i2c@7000c700 {
diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi
index 19a84e933f4e..dec4fc823901 100644
--- a/arch/arm/boot/dts/tegra30.dtsi
+++ b/arch/arm/boot/dts/tegra30.dtsi
@@ -28,6 +28,10 @@
GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
interrupt-names = "intr", "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
@@ -144,9 +148,9 @@
compatible = "nvidia,tegra30-gr2d";
reg = <0x54140000 0x00040000>;
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&tegra_car TEGRA30_CLK_GR2D>;
resets = <&tegra_car 21>;
reset-names = "2d";
- clocks = <&tegra_car TEGRA30_CLK_GR2D>;
};
gr3d@54180000 {
diff --git a/arch/arm/boot/dts/tps65910.dtsi b/arch/arm/boot/dts/tps65910.dtsi
index 92693a89160e..b0ac6657a170 100644
--- a/arch/arm/boot/dts/tps65910.dtsi
+++ b/arch/arm/boot/dts/tps65910.dtsi
@@ -82,5 +82,10 @@
reg = <12>;
regulator-compatible = "vmmc";
};
+
+ vbb_reg: regulator@13 {
+ reg = <13>;
+ regulator-compatible = "vbb";
+ };
};
};
diff --git a/arch/arm/boot/dts/twl4030.dtsi b/arch/arm/boot/dts/twl4030.dtsi
index 4217096ee677..86cfc7d15ca7 100644
--- a/arch/arm/boot/dts/twl4030.dtsi
+++ b/arch/arm/boot/dts/twl4030.dtsi
@@ -145,4 +145,11 @@
compatible = "ti,twl4030-pwrbutton";
interrupts = <8>;
};
+
+ twl_keypad: keypad {
+ compatible = "ti,twl4030-keypad";
+ interrupts = <1>;
+ keypad,num-rows = <8>;
+ keypad,num-columns = <8>;
+ };
};
diff --git a/arch/arm/boot/dts/vf610-cosmic.dts b/arch/arm/boot/dts/vf610-cosmic.dts
index c42e4f938dcd..3fd1b74e1216 100644
--- a/arch/arm/boot/dts/vf610-cosmic.dts
+++ b/arch/arm/boot/dts/vf610-cosmic.dts
@@ -36,12 +36,37 @@
&fec1 {
phy-mode = "rmii";
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec1_1>;
+ pinctrl-0 = <&pinctrl_fec1>;
status = "okay";
};
+&iomuxc {
+ vf610-cosmic {
+ pinctrl_fec1: fec1grp {
+ fsl,pins = <
+ VF610_PAD_PTC9__ENET_RMII1_MDC 0x30d2
+ VF610_PAD_PTC10__ENET_RMII1_MDIO 0x30d3
+ VF610_PAD_PTC11__ENET_RMII1_CRS 0x30d1
+ VF610_PAD_PTC12__ENET_RMII_RXD1 0x30d1
+ VF610_PAD_PTC13__ENET_RMII1_RXD0 0x30d1
+ VF610_PAD_PTC14__ENET_RMII1_RXER 0x30d1
+ VF610_PAD_PTC15__ENET_RMII1_TXD1 0x30d2
+ VF610_PAD_PTC16__ENET_RMII1_TXD0 0x30d2
+ VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ VF610_PAD_PTB4__UART1_TX 0x21a2
+ VF610_PAD_PTB5__UART1_RX 0x21a1
+ >;
+ };
+ };
+};
+
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_1>;
+ pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/vf610-twr.dts b/arch/arm/boot/dts/vf610-twr.dts
index c8047ca16501..7dd1d6ede525 100644
--- a/arch/arm/boot/dts/vf610-twr.dts
+++ b/arch/arm/boot/dts/vf610-twr.dts
@@ -34,12 +34,70 @@
};
};
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_3p3v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ reg_vcc_3v3_mcu: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <1>;
+ regulator-name = "vcc_3v3_mcu";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+
+ sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,widgets =
+ "Microphone", "Microphone Jack",
+ "Headphone", "Headphone Jack",
+ "Speaker", "Speaker Ext",
+ "Line", "Line In Jack";
+ simple-audio-card,routing =
+ "MIC_IN", "Microphone Jack",
+ "Microphone Jack", "Mic Bias",
+ "LINE_IN", "Line In Jack",
+ "Headphone Jack", "HP_OUT",
+ "Speaker Ext", "LINE_OUT";
+
+ simple-audio-card,cpu {
+ sound-dai = <&sai2>;
+ master-clkdir-out;
+ frame-master;
+ bitclock-master;
+ };
+
+ simple-audio-card,codec {
+ sound-dai = <&codec>;
+ frame-master;
+ bitclock-master;
+ };
+ };
+};
+
+&adc0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_adc0_ad5>;
+ vref-supply = <&reg_vcc_3v3_mcu>;
+ status = "okay";
};
&dspi0 {
bus-num = <0>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_dspi0_1>;
+ pinctrl-0 = <&pinctrl_dspi0>;
status = "okay";
sflash: at26df081a@0 {
@@ -56,26 +114,116 @@
&fec0 {
phy-mode = "rmii";
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec0_1>;
+ pinctrl-0 = <&pinctrl_fec0>;
status = "okay";
};
&fec1 {
phy-mode = "rmii";
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_fec1_1>;
+ pinctrl-0 = <&pinctrl_fec1>;
status = "okay";
};
&i2c0 {
clock-frequency = <100000>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c0_1>;
+ pinctrl-0 = <&pinctrl_i2c0>;
+ status = "okay";
+
+ codec: sgtl5000@0a {
+ #sound-dai-cells = <0>;
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ VDDA-supply = <&reg_3p3v>;
+ VDDIO-supply = <&reg_3p3v>;
+ clocks = <&clks VF610_CLK_SAI2>;
+ };
+};
+
+&iomuxc {
+ vf610-twr {
+ pinctrl_adc0_ad5: adc0ad5grp {
+ fsl,pins = <
+ VF610_PAD_PTC30__ADC0_SE5 0xa1
+ >;
+ };
+
+ pinctrl_dspi0: dspi0grp {
+ fsl,pins = <
+ VF610_PAD_PTB19__DSPI0_CS0 0x1182
+ VF610_PAD_PTB20__DSPI0_SIN 0x1181
+ VF610_PAD_PTB21__DSPI0_SOUT 0x1182
+ VF610_PAD_PTB22__DSPI0_SCK 0x1182
+ >;
+ };
+
+ pinctrl_fec0: fec0grp {
+ fsl,pins = <
+ VF610_PAD_PTA6__RMII_CLKIN 0x30d1
+ VF610_PAD_PTC0__ENET_RMII0_MDC 0x30d3
+ VF610_PAD_PTC1__ENET_RMII0_MDIO 0x30d1
+ VF610_PAD_PTC2__ENET_RMII0_CRS 0x30d1
+ VF610_PAD_PTC3__ENET_RMII0_RXD1 0x30d1
+ VF610_PAD_PTC4__ENET_RMII0_RXD0 0x30d1
+ VF610_PAD_PTC5__ENET_RMII0_RXER 0x30d1
+ VF610_PAD_PTC6__ENET_RMII0_TXD1 0x30d2
+ VF610_PAD_PTC7__ENET_RMII0_TXD0 0x30d2
+ VF610_PAD_PTC8__ENET_RMII0_TXEN 0x30d2
+ >;
+ };
+
+ pinctrl_fec1: fec1grp {
+ fsl,pins = <
+ VF610_PAD_PTC9__ENET_RMII1_MDC 0x30d2
+ VF610_PAD_PTC10__ENET_RMII1_MDIO 0x30d3
+ VF610_PAD_PTC11__ENET_RMII1_CRS 0x30d1
+ VF610_PAD_PTC12__ENET_RMII_RXD1 0x30d1
+ VF610_PAD_PTC13__ENET_RMII1_RXD0 0x30d1
+ VF610_PAD_PTC14__ENET_RMII1_RXER 0x30d1
+ VF610_PAD_PTC15__ENET_RMII1_TXD1 0x30d2
+ VF610_PAD_PTC16__ENET_RMII1_TXD0 0x30d2
+ VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2
+ >;
+ };
+
+ pinctrl_i2c0: i2c0grp {
+ fsl,pins = <
+ VF610_PAD_PTB14__I2C0_SCL 0x30d3
+ VF610_PAD_PTB15__I2C0_SDA 0x30d3
+ >;
+ };
+
+ pinctrl_sai2: sai2grp {
+ fsl,pins = <
+ VF610_PAD_PTA16__SAI2_TX_BCLK 0x02ed
+ VF610_PAD_PTA18__SAI2_TX_DATA 0x02ee
+ VF610_PAD_PTA19__SAI2_TX_SYNC 0x02ed
+ VF610_PAD_PTA21__SAI2_RX_BCLK 0x02ed
+ VF610_PAD_PTA22__SAI2_RX_DATA 0x02ed
+ VF610_PAD_PTA23__SAI2_RX_SYNC 0x02ed
+ VF610_PAD_PTB18__EXT_AUDIO_MCLK 0x02ed
+ >;
+ };
+
+ pinctrl_uart1: uart1grp {
+ fsl,pins = <
+ VF610_PAD_PTB4__UART1_TX 0x21a2
+ VF610_PAD_PTB5__UART1_RX 0x21a1
+ >;
+ };
+ };
+};
+
+&sai2 {
+ #sound-dai-cells = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sai2>;
status = "okay";
};
&uart1 {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart1_1>;
+ pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/vf610.dtsi b/arch/arm/boot/dts/vf610.dtsi
index d31ce1b4a7b0..804873367669 100644
--- a/arch/arm/boot/dts/vf610.dtsi
+++ b/arch/arm/boot/dts/vf610.dtsi
@@ -10,6 +10,7 @@
#include "skeleton.dtsi"
#include "vf610-pinfunc.h"
#include <dt-bindings/clock/vf610-clock.h>
+#include <dt-bindings/interrupt-controller/irq.h>
/ {
aliases {
@@ -87,39 +88,66 @@
arm,tag-latency = <2 2 2>;
};
+ edma0: dma-controller@40018000 {
+ #dma-cells = <2>;
+ compatible = "fsl,vf610-edma";
+ reg = <0x40018000 0x2000>,
+ <0x40024000 0x1000>,
+ <0x40025000 0x1000>;
+ interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
+ <0 9 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "edma-tx", "edma-err";
+ dma-channels = <32>;
+ clock-names = "dmamux0", "dmamux1";
+ clocks = <&clks VF610_CLK_DMAMUX0>,
+ <&clks VF610_CLK_DMAMUX1>;
+ };
+
uart0: serial@40027000 {
compatible = "fsl,vf610-lpuart";
reg = <0x40027000 0x1000>;
- interrupts = <0 61 0x00>;
+ interrupts = <0 61 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_UART0>;
clock-names = "ipg";
+ dmas = <&edma0 0 2>,
+ <&edma0 0 3>;
+ dma-names = "rx","tx";
status = "disabled";
};
uart1: serial@40028000 {
compatible = "fsl,vf610-lpuart";
reg = <0x40028000 0x1000>;
- interrupts = <0 62 0x04>;
+ interrupts = <0 62 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_UART1>;
clock-names = "ipg";
+ dmas = <&edma0 0 4>,
+ <&edma0 0 5>;
+ dma-names = "rx","tx";
status = "disabled";
};
uart2: serial@40029000 {
compatible = "fsl,vf610-lpuart";
reg = <0x40029000 0x1000>;
- interrupts = <0 63 0x04>;
+ interrupts = <0 63 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_UART2>;
clock-names = "ipg";
+ dmas = <&edma0 0 6>,
+ <&edma0 0 7>;
+ dma-names = "rx","tx";
status = "disabled";
};
uart3: serial@4002a000 {
compatible = "fsl,vf610-lpuart";
reg = <0x4002a000 0x1000>;
- interrupts = <0 64 0x04>;
+ interrupts = <0 64 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_UART3>;
clock-names = "ipg";
+ dmas = <&edma0 0 8>,
+ <&edma0 0 9>;
+ dma-names = "rx","tx";
status = "disabled";
};
@@ -128,7 +156,7 @@
#size-cells = <0>;
compatible = "fsl,vf610-dspi";
reg = <0x4002c000 0x1000>;
- interrupts = <0 67 0x04>;
+ interrupts = <0 67 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_DSPI0>;
clock-names = "dspi";
spi-num-chipselects = <5>;
@@ -138,20 +166,32 @@
sai2: sai@40031000 {
compatible = "fsl,vf610-sai";
reg = <0x40031000 0x1000>;
- interrupts = <0 86 0x04>;
+ interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_SAI2>;
clock-names = "sai";
+ dma-names = "tx", "rx";
+ dmas = <&edma0 0 21>,
+ <&edma0 0 20>;
status = "disabled";
};
pit: pit@40037000 {
compatible = "fsl,vf610-pit";
reg = <0x40037000 0x1000>;
- interrupts = <0 39 0x04>;
+ interrupts = <0 39 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_PIT>;
clock-names = "pit";
};
+ adc0: adc@4003b000 {
+ compatible = "fsl,vf610-adc";
+ reg = <0x4003b000 0x1000>;
+ interrupts = <0 53 0x04>;
+ clocks = <&clks VF610_CLK_ADC0>;
+ clock-names = "adc";
+ status = "disabled";
+ };
+
wdog@4003e000 {
compatible = "fsl,vf610-wdt", "fsl,imx21-wdt";
reg = <0x4003e000 0x1000>;
@@ -164,7 +204,7 @@
#size-cells = <0>;
compatible = "fsl,vf610-qspi";
reg = <0x40044000 0x1000>;
- interrupts = <0 24 0x04>;
+ interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_QSPI0_EN>,
<&clks VF610_CLK_QSPI0>;
clock-names = "qspi_en", "qspi";
@@ -175,182 +215,12 @@
compatible = "fsl,vf610-iomuxc";
reg = <0x40048000 0x1000>;
#gpio-range-cells = <3>;
-
- /* functions and groups pins */
-
- dcu0 {
- pinctrl_dcu0_1: dcu0grp_1 {
- fsl,pins = <
- VF610_PAD_PTB8__GPIO_30 0x42
- VF610_PAD_PTE0__DCU0_HSYNC 0x42
- VF610_PAD_PTE1__DCU0_VSYNC 0x42
- VF610_PAD_PTE2__DCU0_PCLK 0x42
- VF610_PAD_PTE4__DCU0_DE 0x42
- VF610_PAD_PTE5__DCU0_R0 0x42
- VF610_PAD_PTE6__DCU0_R1 0x42
- VF610_PAD_PTE7__DCU0_R2 0x42
- VF610_PAD_PTE8__DCU0_R3 0x42
- VF610_PAD_PTE9__DCU0_R4 0x42
- VF610_PAD_PTE10__DCU0_R5 0x42
- VF610_PAD_PTE11__DCU0_R6 0x42
- VF610_PAD_PTE12__DCU0_R7 0x42
- VF610_PAD_PTE13__DCU0_G0 0x42
- VF610_PAD_PTE14__DCU0_G1 0x42
- VF610_PAD_PTE15__DCU0_G2 0x42
- VF610_PAD_PTE16__DCU0_G3 0x42
- VF610_PAD_PTE17__DCU0_G4 0x42
- VF610_PAD_PTE18__DCU0_G5 0x42
- VF610_PAD_PTE19__DCU0_G6 0x42
- VF610_PAD_PTE20__DCU0_G7 0x42
- VF610_PAD_PTE21__DCU0_B0 0x42
- VF610_PAD_PTE22__DCU0_B1 0x42
- VF610_PAD_PTE23__DCU0_B2 0x42
- VF610_PAD_PTE24__DCU0_B3 0x42
- VF610_PAD_PTE25__DCU0_B4 0x42
- VF610_PAD_PTE26__DCU0_B5 0x42
- VF610_PAD_PTE27__DCU0_B6 0x42
- VF610_PAD_PTE28__DCU0_B7 0x42
- >;
- };
- };
-
- dspi0 {
- pinctrl_dspi0_1: dspi0grp_1 {
- fsl,pins = <
- VF610_PAD_PTB19__DSPI0_CS0 0x1182
- VF610_PAD_PTB20__DSPI0_SIN 0x1181
- VF610_PAD_PTB21__DSPI0_SOUT 0x1182
- VF610_PAD_PTB22__DSPI0_SCK 0x1182
- >;
- };
- };
-
- esdhc1 {
- pinctrl_esdhc1_1: esdhc1grp_1 {
- fsl,pins = <
- VF610_PAD_PTA24__ESDHC1_CLK 0x31ef
- VF610_PAD_PTA25__ESDHC1_CMD 0x31ef
- VF610_PAD_PTA26__ESDHC1_DAT0 0x31ef
- VF610_PAD_PTA27__ESDHC1_DAT1 0x31ef
- VF610_PAD_PTA28__ESDHC1_DATA2 0x31ef
- VF610_PAD_PTA29__ESDHC1_DAT3 0x31ef
- VF610_PAD_PTA7__GPIO_134 0x219d
- >;
- };
- };
-
- fec0 {
- pinctrl_fec0_1: fec0grp_1 {
- fsl,pins = <
- VF610_PAD_PTA6__RMII_CLKIN 0x30d1
- VF610_PAD_PTC0__ENET_RMII0_MDC 0x30d3
- VF610_PAD_PTC1__ENET_RMII0_MDIO 0x30d1
- VF610_PAD_PTC2__ENET_RMII0_CRS 0x30d1
- VF610_PAD_PTC3__ENET_RMII0_RXD1 0x30d1
- VF610_PAD_PTC4__ENET_RMII0_RXD0 0x30d1
- VF610_PAD_PTC5__ENET_RMII0_RXER 0x30d1
- VF610_PAD_PTC6__ENET_RMII0_TXD1 0x30d2
- VF610_PAD_PTC7__ENET_RMII0_TXD0 0x30d2
- VF610_PAD_PTC8__ENET_RMII0_TXEN 0x30d2
- >;
- };
- };
-
- fec1 {
- pinctrl_fec1_1: fec1grp_1 {
- fsl,pins = <
- VF610_PAD_PTC9__ENET_RMII1_MDC 0x30d2
- VF610_PAD_PTC10__ENET_RMII1_MDIO 0x30d3
- VF610_PAD_PTC11__ENET_RMII1_CRS 0x30d1
- VF610_PAD_PTC12__ENET_RMII_RXD1 0x30d1
- VF610_PAD_PTC13__ENET_RMII1_RXD0 0x30d1
- VF610_PAD_PTC14__ENET_RMII1_RXER 0x30d1
- VF610_PAD_PTC15__ENET_RMII1_TXD1 0x30d2
- VF610_PAD_PTC16__ENET_RMII1_TXD0 0x30d2
- VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2
- >;
- };
- };
-
- i2c0 {
- pinctrl_i2c0_1: i2c0grp_1 {
- fsl,pins = <
- VF610_PAD_PTB14__I2C0_SCL 0x30d3
- VF610_PAD_PTB15__I2C0_SDA 0x30d3
- >;
- };
- };
-
- pwm0 {
- pinctrl_pwm0_1: pwm0grp_1 {
- fsl,pins = <
- VF610_PAD_PTB0__FTM0_CH0 0x1582
- VF610_PAD_PTB1__FTM0_CH1 0x1582
- VF610_PAD_PTB2__FTM0_CH2 0x1582
- VF610_PAD_PTB3__FTM0_CH3 0x1582
- VF610_PAD_PTB6__FTM0_CH6 0x1582
- VF610_PAD_PTB7__FTM0_CH7 0x1582
- >;
- };
- };
-
- qspi0 {
- pinctrl_qspi0_1: qspi0grp_1 {
- fsl,pins = <
- VF610_PAD_PTD0__QSPI0_A_QSCK 0x307b
- VF610_PAD_PTD1__QSPI0_A_CS0 0x307f
- VF610_PAD_PTD2__QSPI0_A_DATA3 0x3073
- VF610_PAD_PTD3__QSPI0_A_DATA2 0x3073
- VF610_PAD_PTD4__QSPI0_A_DATA1 0x3073
- VF610_PAD_PTD5__QSPI0_A_DATA0 0x307b
- VF610_PAD_PTD7__QSPI0_B_QSCK 0x307b
- VF610_PAD_PTD8__QSPI0_B_CS0 0x307f
- VF610_PAD_PTD9__QSPI0_B_DATA3 0x3073
- VF610_PAD_PTD10__QSPI0_B_DATA2 0x3073
- VF610_PAD_PTD11__QSPI0_B_DATA1 0x3073
- VF610_PAD_PTD12__QSPI0_B_DATA0 0x307b
- >;
- };
- };
-
- sai2 {
- pinctrl_sai2_1: sai2grp_1 {
- fsl,pins = <
- VF610_PAD_PTA16__SAI2_TX_BCLK 0x02ed
- VF610_PAD_PTA18__SAI2_TX_DATA 0x02ee
- VF610_PAD_PTA19__SAI2_TX_SYNC 0x02ed
- VF610_PAD_PTA21__SAI2_RX_BCLK 0x02ed
- VF610_PAD_PTA22__SAI2_RX_DATA 0x02ed
- VF610_PAD_PTA23__SAI2_RX_SYNC 0x02ed
- VF610_PAD_PTB18__EXT_AUDIO_MCLK 0x02ed
- >;
- };
- };
-
- uart1 {
- pinctrl_uart1_1: uart1grp_1 {
- fsl,pins = <
- VF610_PAD_PTB4__UART1_TX 0x21a2
- VF610_PAD_PTB5__UART1_RX 0x21a1
- >;
- };
- };
-
- usbvbus {
- pinctrl_usbvbus_1: usbvbusgrp_1 {
- fsl,pins = <
- VF610_PAD_PTA24__USB1_VBUS_EN 0x219c
- VF610_PAD_PTA16__USB0_VBUS_EN 0x219c
- >;
- };
- };
-
};
gpio1: gpio@40049000 {
compatible = "fsl,vf610-gpio";
reg = <0x40049000 0x1000 0x400ff000 0x40>;
- interrupts = <0 107 0x04>;
+ interrupts = <0 107 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -361,7 +231,7 @@
gpio2: gpio@4004a000 {
compatible = "fsl,vf610-gpio";
reg = <0x4004a000 0x1000 0x400ff040 0x40>;
- interrupts = <0 108 0x04>;
+ interrupts = <0 108 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -372,7 +242,7 @@
gpio3: gpio@4004b000 {
compatible = "fsl,vf610-gpio";
reg = <0x4004b000 0x1000 0x400ff080 0x40>;
- interrupts = <0 109 0x04>;
+ interrupts = <0 109 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -383,7 +253,7 @@
gpio4: gpio@4004c000 {
compatible = "fsl,vf610-gpio";
reg = <0x4004c000 0x1000 0x400ff0c0 0x40>;
- interrupts = <0 110 0x04>;
+ interrupts = <0 110 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -394,7 +264,7 @@
gpio5: gpio@4004d000 {
compatible = "fsl,vf610-gpio";
reg = <0x4004d000 0x1000 0x400ff100 0x40>;
- interrupts = <0 111 0x04>;
+ interrupts = <0 111 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
@@ -412,9 +282,12 @@
#size-cells = <0>;
compatible = "fsl,vf610-i2c";
reg = <0x40066000 0x1000>;
- interrupts =<0 71 0x04>;
+ interrupts =<0 71 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_I2C0>;
clock-names = "ipg";
+ dmas = <&edma0 0 50>,
+ <&edma0 0 51>;
+ dma-names = "rx","tx";
status = "disabled";
};
@@ -432,10 +305,25 @@
reg = <0x40080000 0x80000>;
ranges;
+ edma1: dma-controller@40098000 {
+ #dma-cells = <2>;
+ compatible = "fsl,vf610-edma";
+ reg = <0x40098000 0x2000>,
+ <0x400a1000 0x1000>,
+ <0x400a2000 0x1000>;
+ interrupts = <0 10 IRQ_TYPE_LEVEL_HIGH>,
+ <0 11 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "edma-tx", "edma-err";
+ dma-channels = <32>;
+ clock-names = "dmamux0", "dmamux1";
+ clocks = <&clks VF610_CLK_DMAMUX2>,
+ <&clks VF610_CLK_DMAMUX3>;
+ };
+
uart4: serial@400a9000 {
compatible = "fsl,vf610-lpuart";
reg = <0x400a9000 0x1000>;
- interrupts = <0 65 0x04>;
+ interrupts = <0 65 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_UART4>;
clock-names = "ipg";
status = "disabled";
@@ -444,16 +332,25 @@
uart5: serial@400aa000 {
compatible = "fsl,vf610-lpuart";
reg = <0x400aa000 0x1000>;
- interrupts = <0 66 0x04>;
+ interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_UART5>;
clock-names = "ipg";
status = "disabled";
};
+ adc1: adc@400bb000 {
+ compatible = "fsl,vf610-adc";
+ reg = <0x400bb000 0x1000>;
+ interrupts = <0 54 0x04>;
+ clocks = <&clks VF610_CLK_ADC1>;
+ clock-names = "adc";
+ status = "disabled";
+ };
+
fec0: ethernet@400d0000 {
compatible = "fsl,mvf600-fec";
reg = <0x400d0000 0x1000>;
- interrupts = <0 78 0x04>;
+ interrupts = <0 78 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_ENET0>,
<&clks VF610_CLK_ENET0>,
<&clks VF610_CLK_ENET>;
@@ -464,7 +361,7 @@
fec1: ethernet@400d1000 {
compatible = "fsl,mvf600-fec";
reg = <0x400d1000 0x1000>;
- interrupts = <0 79 0x04>;
+ interrupts = <0 79 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_ENET1>,
<&clks VF610_CLK_ENET1>,
<&clks VF610_CLK_ENET>;
diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
index 789d0bacc110..511180769af5 100644
--- a/arch/arm/boot/dts/zynq-7000.dtsi
+++ b/arch/arm/boot/dts/zynq-7000.dtsi
@@ -129,29 +129,28 @@
} ;
slcr: slcr@f8000000 {
- compatible = "xlnx,zynq-slcr";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "xlnx,zynq-slcr", "syscon";
reg = <0xF8000000 0x1000>;
-
- clocks {
- #address-cells = <1>;
- #size-cells = <0>;
-
- clkc: clkc {
- #clock-cells = <1>;
- compatible = "xlnx,ps7-clkc";
- ps-clk-frequency = <33333333>;
- clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x",
- "cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x",
- "dci", "lqspi", "smc", "pcap", "gem0", "gem1",
- "fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1",
- "sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1",
- "dma", "usb0_aper", "usb1_aper", "gem0_aper",
- "gem1_aper", "sdio0_aper", "sdio1_aper",
- "spi0_aper", "spi1_aper", "can0_aper", "can1_aper",
- "i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper",
- "gpio_aper", "lqspi_aper", "smc_aper", "swdt",
- "dbg_trc", "dbg_apb";
- };
+ ranges;
+ clkc: clkc@100 {
+ #clock-cells = <1>;
+ compatible = "xlnx,ps7-clkc";
+ ps-clk-frequency = <33333333>;
+ fclk-enable = <0>;
+ clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x",
+ "cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x",
+ "dci", "lqspi", "smc", "pcap", "gem0", "gem1",
+ "fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1",
+ "sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1",
+ "dma", "usb0_aper", "usb1_aper", "gem0_aper",
+ "gem1_aper", "sdio0_aper", "sdio1_aper",
+ "spi0_aper", "spi1_aper", "can0_aper", "can1_aper",
+ "i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper",
+ "gpio_aper", "lqspi_aper", "smc_aper", "swdt",
+ "dbg_trc", "dbg_apb";
+ reg = <0x100 0x100>;
};
};
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
index 4bdc41622c36..70b1eff477b3 100644
--- a/arch/arm/common/Makefile
+++ b/arch/arm/common/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_SHARP_SCOOP) += scoop.o
obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o
obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o
obj-$(CONFIG_MCPM) += mcpm_head.o mcpm_entry.o mcpm_platsmp.o vlock.o
+CFLAGS_REMOVE_mcpm_entry.o = -pg
AFLAGS_mcpm_head.o := -march=armv7-a
AFLAGS_vlock.o := -march=armv7-a
obj-$(CONFIG_TI_PRIV_EDMA) += edma.o
diff --git a/arch/arm/common/scoop.c b/arch/arm/common/scoop.c
index a5c3dc38aa18..6ef146edd0cd 100644
--- a/arch/arm/common/scoop.c
+++ b/arch/arm/common/scoop.c
@@ -232,8 +232,6 @@ static int scoop_probe(struct platform_device *pdev)
return 0;
- if (devptr->gpio.base != -1)
- temp = gpiochip_remove(&devptr->gpio);
err_gpio:
platform_set_drvdata(pdev, NULL);
err_ioremap:
diff --git a/arch/arm/common/timer-sp.c b/arch/arm/common/timer-sp.c
index 53c6a26b633d..fd6bff0c5b96 100644
--- a/arch/arm/common/timer-sp.c
+++ b/arch/arm/common/timer-sp.c
@@ -271,10 +271,14 @@ static void __init integrator_cp_of_init(struct device_node *np)
void __iomem *base;
int irq;
const char *name = of_get_property(np, "compatible", NULL);
+ struct clk *clk;
base = of_iomap(np, 0);
if (WARN_ON(!base))
return;
+ clk = of_clk_get(np, 0);
+ if (WARN_ON(IS_ERR(clk)))
+ return;
/* Ensure timer is disabled */
writel(0, base + TIMER_CTRL);
@@ -283,13 +287,13 @@ static void __init integrator_cp_of_init(struct device_node *np)
goto err;
if (!init_count)
- sp804_clocksource_init(base, name);
+ __sp804_clocksource_and_sched_clock_init(base, name, clk, 0);
else {
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0)
goto err;
- sp804_clockevents_init(base, irq, name);
+ __sp804_clockevents_init(base, irq, clk, name);
}
init_count++;
diff --git a/arch/arm/configs/ape6evm_defconfig b/arch/arm/configs/ape6evm_defconfig
index cb26c62dc722..bb396c0e5fda 100644
--- a/arch/arm/configs/ape6evm_defconfig
+++ b/arch/arm/configs/ape6evm_defconfig
@@ -48,6 +48,8 @@ CONFIG_IP_PNP_DHCP=y
# CONFIG_IPV6_SIT is not set
CONFIG_NETFILTER=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FW_LOADER_USER_HELPER is not set
CONFIG_NETDEVICES=y
# CONFIG_NET_CADENCE is not set
diff --git a/arch/arm/configs/armadillo800eva_defconfig b/arch/arm/configs/armadillo800eva_defconfig
index 9287a62de830..065adddeee3e 100644
--- a/arch/arm/configs/armadillo800eva_defconfig
+++ b/arch/arm/configs/armadillo800eva_defconfig
@@ -58,6 +58,8 @@ CONFIG_IP_PNP_DHCP=y
# CONFIG_IPV6 is not set
# CONFIG_WIRELESS is not set
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_MD=y
diff --git a/arch/arm/configs/at91_dt_defconfig b/arch/arm/configs/at91_dt_defconfig
index 0b4e9b5210d8..300ded9acbe9 100644
--- a/arch/arm/configs/at91_dt_defconfig
+++ b/arch/arm/configs/at91_dt_defconfig
@@ -16,10 +16,12 @@ CONFIG_MODULE_UNLOAD=y
CONFIG_ARCH_AT91=y
CONFIG_SOC_AT91RM9200=y
CONFIG_SOC_AT91SAM9260=y
+CONFIG_SOC_AT91SAM9261=y
CONFIG_SOC_AT91SAM9263=y
CONFIG_SOC_AT91SAM9G45=y
CONFIG_SOC_AT91SAM9X5=y
CONFIG_SOC_AT91SAM9N12=y
+CONFIG_SOC_AT91SAM9RL=y
CONFIG_MACH_AT91RM9200_DT=y
CONFIG_MACH_AT91SAM9_DT=y
CONFIG_AT91_TIMER_HZ=128
@@ -119,6 +121,7 @@ CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ADS7846=y
# CONFIG_SERIO is not set
CONFIG_LEGACY_PTY_COUNT=4
CONFIG_SERIAL_ATMEL=y
diff --git a/arch/arm/configs/at91sam9260_9g20_defconfig b/arch/arm/configs/at91sam9260_9g20_defconfig
index 2cd832918e9c..c4c160fc8791 100644
--- a/arch/arm/configs/at91sam9260_9g20_defconfig
+++ b/arch/arm/configs/at91sam9260_9g20_defconfig
@@ -3,6 +3,7 @@
CONFIG_SYSVIPC=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_BLK_DEV_INITRD=y
+CONFIG_EMBEDDED=y
CONFIG_SLAB=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
@@ -30,15 +31,12 @@ CONFIG_MACH_AT91SAM9_DT=y
CONFIG_AT91_SLOW_CLOCK=y
# CONFIG_ARM_THUMB is not set
CONFIG_AEABI=y
-CONFIG_LEDS=y
-CONFIG_LEDS_CPU=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_ARM_APPENDED_DTB=y
CONFIG_ARM_ATAG_DTB_COMPAT=y
CONFIG_CMDLINE="mem=64M console=ttyS0,115200 initrd=0x21100000,3145728 root=/dev/ram0 rw"
CONFIG_AUTO_ZRELADDR=y
-CONFIG_FPE_NWFPE=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
@@ -57,15 +55,14 @@ CONFIG_DEVTMPFS_MOUNT=y
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_OF_PARTS=y
-CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_DATAFLASH=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ATMEL=y
+CONFIG_MTD_UBI=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
-CONFIG_MISC_DEVICES=y
CONFIG_EEPROM_AT25=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
@@ -112,8 +109,6 @@ CONFIG_SND_PCM_OSS=y
CONFIG_SND_SEQUENCER_OSS=y
# CONFIG_SND_VERBOSE_PROCFS is not set
CONFIG_USB=y
-CONFIG_USB_DEVICEFS=y
-# CONFIG_USB_DEVICE_CLASS is not set
CONFIG_USB_MON=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_STORAGE=y
diff --git a/arch/arm/configs/at91sam9rl_defconfig b/arch/arm/configs/at91sam9rl_defconfig
index 7b6f131cecd6..85f846ae9ff2 100644
--- a/arch/arm/configs/at91sam9rl_defconfig
+++ b/arch/arm/configs/at91sam9rl_defconfig
@@ -1,8 +1,8 @@
-CONFIG_EXPERIMENTAL=y
# CONFIG_LOCALVERSION_AUTO is not set
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
CONFIG_LOG_BUF_SHIFT=14
+CONFIG_EMBEDDED=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_SLAB=y
CONFIG_MODULES=y
@@ -14,20 +14,23 @@ CONFIG_ARCH_AT91=y
CONFIG_ARCH_AT91SAM9RL=y
CONFIG_MACH_AT91SAM9RLEK=y
# CONFIG_ARM_THUMB is not set
+CONFIG_AEABI=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_CMDLINE="mem=64M console=ttyS0,115200 initrd=0x21100000,17105363 root=/dev/ram0 rw"
-CONFIG_FPE_NWFPE=y
+CONFIG_AUTO_ZRELADDR=y
CONFIG_NET=y
CONFIG_UNIX=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_DATAFLASH=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ATMEL=y
+CONFIG_MTD_UBI=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=4
@@ -66,6 +69,7 @@ CONFIG_EXT2_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
+CONFIG_UBIFS_FS=y
CONFIG_CRAMFS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_CODEPAGE_850=y
diff --git a/arch/arm/configs/bcm2835_defconfig b/arch/arm/configs/bcm2835_defconfig
index f43392dc2dcf..0302d293fba0 100644
--- a/arch/arm/configs/bcm2835_defconfig
+++ b/arch/arm/configs/bcm2835_defconfig
@@ -31,6 +31,7 @@ CONFIG_OPROFILE=y
CONFIG_JUMP_LABEL=y
CONFIG_ARCH_MULTI_V6=y
# CONFIG_ARCH_MULTI_V7 is not set
+CONFIG_ARCH_BCM=y
CONFIG_ARCH_BCM2835=y
CONFIG_PREEMPT_VOLUNTARY=y
CONFIG_AEABI=y
diff --git a/arch/arm/configs/bcm_defconfig b/arch/arm/configs/bcm_defconfig
index 2519d6de0640..01004640ee4d 100644
--- a/arch/arm/configs/bcm_defconfig
+++ b/arch/arm/configs/bcm_defconfig
@@ -79,6 +79,13 @@ CONFIG_HW_RANDOM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
# CONFIG_HWMON is not set
+CONFIG_MFD_BCM590XX=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y
+CONFIG_REGULATOR_USERSPACE_CONSUMER=y
+CONFIG_REGULATOR_BCM590XX=y
+
CONFIG_VIDEO_OUTPUT_CONTROL=y
CONFIG_FB=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
diff --git a/arch/arm/configs/bockw_defconfig b/arch/arm/configs/bockw_defconfig
index 80cff50beb34..e816140d81c5 100644
--- a/arch/arm/configs/bockw_defconfig
+++ b/arch/arm/configs/bockw_defconfig
@@ -44,6 +44,8 @@ CONFIG_IP_PNP_DHCP=y
# CONFIG_INET_DIAG is not set
# CONFIG_IPV6 is not set
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_STANDALONE is not set
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
# CONFIG_FW_LOADER is not set
diff --git a/arch/arm/configs/clps711x_defconfig b/arch/arm/configs/clps711x_defconfig
index 9e8c8316d6b0..0facf9da047c 100644
--- a/arch/arm/configs/clps711x_defconfig
+++ b/arch/arm/configs/clps711x_defconfig
@@ -15,7 +15,6 @@ CONFIG_ARCH_CDB89712=y
CONFIG_ARCH_CLEP7312=y
CONFIG_ARCH_EDB7211=y
CONFIG_ARCH_P720T=y
-CONFIG_ARCH_FORTUNET=y
CONFIG_AEABI=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
@@ -27,7 +26,6 @@ CONFIG_INET=y
# CONFIG_IPV6 is not set
CONFIG_IRDA=y
CONFIG_IRTTY_SIR=y
-CONFIG_EP7211_DONGLE=y
# CONFIG_WIRELESS is not set
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
@@ -58,6 +56,7 @@ CONFIG_CS89x0_PLATFORM=y
# CONFIG_INPUT is not set
# CONFIG_SERIO is not set
# CONFIG_VT is not set
+CONFIG_SERIAL_CLPS711X=y
CONFIG_SERIAL_CLPS711X_CONSOLE=y
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
diff --git a/arch/arm/configs/da8xx_omapl_defconfig b/arch/arm/configs/da8xx_omapl_defconfig
deleted file mode 100644
index 1571bea48bed..000000000000
--- a/arch/arm/configs/da8xx_omapl_defconfig
+++ /dev/null
@@ -1,139 +0,0 @@
-CONFIG_EXPERIMENTAL=y
-# CONFIG_SWAP is not set
-CONFIG_SYSVIPC=y
-CONFIG_POSIX_MQUEUE=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=14
-CONFIG_CGROUPS=y
-CONFIG_BLK_DEV_INITRD=y
-CONFIG_EXPERT=y
-CONFIG_MODULES=y
-CONFIG_MODULE_UNLOAD=y
-CONFIG_MODULE_FORCE_UNLOAD=y
-CONFIG_MODVERSIONS=y
-# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_IOSCHED_DEADLINE is not set
-# CONFIG_IOSCHED_CFQ is not set
-CONFIG_ARCH_DAVINCI=y
-CONFIG_ARCH_DAVINCI_DA830=y
-CONFIG_ARCH_DAVINCI_DA850=y
-CONFIG_MACH_DA8XX_DT=y
-CONFIG_MACH_MITYOMAPL138=y
-CONFIG_MACH_OMAPL138_HAWKBOARD=y
-CONFIG_DAVINCI_RESET_CLOCKS=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_PREEMPT=y
-CONFIG_AEABI=y
-# CONFIG_OABI_COMPAT is not set
-CONFIG_LEDS=y
-CONFIG_USE_OF=y
-CONFIG_ZBOOT_ROM_TEXT=0x0
-CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CPU_FREQ=y
-CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
-CONFIG_CPU_FREQ_GOV_PERFORMANCE=m
-CONFIG_CPU_FREQ_GOV_POWERSAVE=m
-CONFIG_CPU_FREQ_GOV_ONDEMAND=m
-CONFIG_CPU_IDLE=y
-CONFIG_PM_RUNTIME=y
-CONFIG_NET=y
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_INET=y
-CONFIG_IP_PNP=y
-CONFIG_IP_PNP_DHCP=y
-# CONFIG_INET_LRO is not set
-CONFIG_NETFILTER=y
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-CONFIG_DEVTMPFS=y
-CONFIG_DEVTMPFS_MOUNT=y
-# CONFIG_FW_LOADER is not set
-CONFIG_BLK_DEV_LOOP=m
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_COUNT=1
-CONFIG_BLK_DEV_RAM_SIZE=32768
-CONFIG_EEPROM_AT24=y
-CONFIG_SCSI=m
-CONFIG_BLK_DEV_SD=m
-CONFIG_NETDEVICES=y
-CONFIG_TUN=m
-CONFIG_LXT_PHY=y
-CONFIG_LSI_ET1011C_PHY=y
-CONFIG_NET_ETHERNET=y
-CONFIG_MII=y
-CONFIG_TI_DAVINCI_EMAC=y
-# CONFIG_NETDEV_1000 is not set
-# CONFIG_NETDEV_10000 is not set
-CONFIG_NETCONSOLE=y
-CONFIG_NETPOLL_TRAP=y
-CONFIG_INPUT_MOUSEDEV=m
-CONFIG_INPUT_EVDEV=m
-CONFIG_INPUT_EVBUG=m
-CONFIG_KEYBOARD_ATKBD=m
-CONFIG_KEYBOARD_GPIO=y
-CONFIG_KEYBOARD_XTKBD=m
-# CONFIG_INPUT_MOUSE is not set
-CONFIG_INPUT_TOUCHSCREEN=y
-CONFIG_SERIO_LIBPS2=y
-# CONFIG_VT_CONSOLE is not set
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_SERIAL_8250_NR_UARTS=3
-CONFIG_SERIAL_OF_PLATFORM=y
-CONFIG_I2C=y
-CONFIG_I2C_CHARDEV=y
-CONFIG_I2C_DAVINCI=y
-CONFIG_PINCTRL_SINGLE=y
-# CONFIG_HWMON is not set
-CONFIG_WATCHDOG=y
-CONFIG_REGULATOR=y
-CONFIG_REGULATOR_DUMMY=y
-CONFIG_REGULATOR_TPS6507X=y
-CONFIG_FB=y
-CONFIG_FB_DA8XX=y
-# CONFIG_VGA_CONSOLE is not set
-CONFIG_FRAMEBUFFER_CONSOLE=y
-CONFIG_LOGO=y
-CONFIG_SOUND=m
-CONFIG_SND=m
-CONFIG_SND_SOC=m
-CONFIG_SND_DAVINCI_SOC=m
-# CONFIG_HID_SUPPORT is not set
-# CONFIG_USB_SUPPORT is not set
-CONFIG_DMADEVICES=y
-CONFIG_TI_EDMA=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT3_FS=y
-CONFIG_XFS_FS=m
-CONFIG_INOTIFY=y
-CONFIG_AUTOFS4_FS=m
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
-CONFIG_TMPFS=y
-CONFIG_CRAMFS=y
-CONFIG_MINIX_FS=m
-CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
-CONFIG_ROOT_NFS=y
-CONFIG_NFSD=m
-CONFIG_NFSD_V3=y
-CONFIG_SMB_FS=m
-CONFIG_PARTITION_ADVANCED=y
-CONFIG_NLS_CODEPAGE_437=y
-CONFIG_NLS_ASCII=m
-CONFIG_NLS_ISO8859_1=y
-CONFIG_NLS_UTF8=m
-CONFIG_DEBUG_FS=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_TIMER_STATS=y
-CONFIG_DEBUG_RT_MUTEXES=y
-CONFIG_DEBUG_MUTEXES=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
-CONFIG_DEBUG_USER=y
-CONFIG_DEBUG_ERRORS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
-# CONFIG_CRYPTO_HW is not set
-CONFIG_CRC_CCITT=m
-CONFIG_CRC_T10DIF=m
diff --git a/arch/arm/configs/davinci_all_defconfig b/arch/arm/configs/davinci_all_defconfig
index ab2f7378352c..2a282c051cfd 100644
--- a/arch/arm/configs/davinci_all_defconfig
+++ b/arch/arm/configs/davinci_all_defconfig
@@ -20,9 +20,14 @@ CONFIG_ARCH_DAVINCI_DM644x=y
CONFIG_ARCH_DAVINCI_DM355=y
CONFIG_ARCH_DAVINCI_DM646x=y
CONFIG_ARCH_DAVINCI_DM365=y
+CONFIG_ARCH_DAVINCI_DA830=y
+CONFIG_ARCH_DAVINCI_DA850=y
+CONFIG_MACH_DA8XX_DT=y
CONFIG_MACH_SFFSDR=y
CONFIG_MACH_NEUROS_OSD2=y
CONFIG_MACH_DM355_LEOPARD=y
+CONFIG_MACH_MITYOMAPL138=y
+CONFIG_MACH_OMAPL138_HAWKBOARD=y
CONFIG_DAVINCI_MUX_DEBUG=y
CONFIG_DAVINCI_MUX_WARNINGS=y
CONFIG_DAVINCI_RESET_CLOCKS=y
@@ -32,8 +37,18 @@ CONFIG_PREEMPT=y
CONFIG_AEABI=y
# CONFIG_OABI_COMPAT is not set
CONFIG_LEDS=y
+CONFIG_USE_OF=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_AUTO_ZRELADDR=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=m
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_IDLE=y
CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -57,6 +72,7 @@ CONFIG_MTD_CFI_AMDSTD=m
CONFIG_MTD_PHYSMAP=m
CONFIG_MTD_NAND=m
CONFIG_MTD_NAND_DAVINCI=m
+CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=m
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=1
@@ -71,6 +87,7 @@ CONFIG_TUN=m
CONFIG_LXT_PHY=y
CONFIG_LSI_ET1011C_PHY=y
CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
CONFIG_TI_DAVINCI_EMAC=y
CONFIG_DM9000=y
# CONFIG_NETDEV_1000 is not set
@@ -97,15 +114,21 @@ CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=3
# CONFIG_HW_RANDOM is not set
+CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_DAVINCI=y
+CONFIG_PINCTRL_SINGLE=y
CONFIG_GPIO_PCF857X=y
CONFIG_WATCHDOG=y
CONFIG_DAVINCI_WATCHDOG=m
CONFIG_MFD_DM355EVM_MSP=y
+CONFIG_TPS6507X=y
CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_TPS6507X=y
CONFIG_FB=y
+CONFIG_FB_DA8XX=y
CONFIG_FIRMWARE_EDID=y
# CONFIG_VGA_CONSOLE is not set
CONFIG_FRAMEBUFFER_CONSOLE=y
@@ -198,3 +221,5 @@ CONFIG_DEBUG_ERRORS=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_T10DIF=m
+CONFIG_GPIO_PCA953X=y
+CONFIG_KEYBOARD_GPIO_POLLED=y
diff --git a/arch/arm/configs/dove_defconfig b/arch/arm/configs/dove_defconfig
index 110105476848..f15955144175 100644
--- a/arch/arm/configs/dove_defconfig
+++ b/arch/arm/configs/dove_defconfig
@@ -48,7 +48,6 @@ CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_CFI_STAA=y
CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_M25P80=y
-CONFIG_MTD_UBI=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=1
@@ -80,6 +79,8 @@ CONFIG_SPI_ORION=y
# CONFIG_HWMON is not set
CONFIG_THERMAL=y
CONFIG_DOVE_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_ORION_WATCHDOG=y
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig
index dbe1f1c47bb0..4ce7b70ea901 100644
--- a/arch/arm/configs/exynos_defconfig
+++ b/arch/arm/configs/exynos_defconfig
@@ -94,7 +94,7 @@ CONFIG_FONT_7x14=y
CONFIG_LOGO=y
CONFIG_USB=y
CONFIG_USB_EHCI_HCD=y
-CONFIG_USB_EHCI_S5P=y
+CONFIG_USB_EHCI_EXYNOS=y
CONFIG_USB_STORAGE=y
CONFIG_USB_DWC3=y
CONFIG_USB_PHY=y
diff --git a/arch/arm/configs/genmai_defconfig b/arch/arm/configs/genmai_defconfig
index aa0b704f48af..d238fafb6762 100644
--- a/arch/arm/configs/genmai_defconfig
+++ b/arch/arm/configs/genmai_defconfig
@@ -50,6 +50,9 @@ CONFIG_IP_PNP_DHCP=y
# CONFIG_IPV6 is not set
# CONFIG_WIRELESS is not set
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_EEPROM_AT24=y
CONFIG_NETDEVICES=y
# CONFIG_NET_CORE is not set
# CONFIG_NET_VENDOR_ARC is not set
@@ -78,7 +81,10 @@ CONFIG_SERIAL_SH_SCI=y
CONFIG_SERIAL_SH_SCI_NR_UARTS=10
CONFIG_SERIAL_SH_SCI_CONSOLE=y
# CONFIG_HW_RANDOM is not set
-CONFIG_I2C_SH_MOBILE=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_RIIC=y
+CONFIG_SPI=y
+CONFIG_SPI_RSPI=y
# CONFIG_HWMON is not set
CONFIG_THERMAL=y
CONFIG_RCAR_THERMAL=y
diff --git a/arch/arm/configs/imx_v4_v5_defconfig b/arch/arm/configs/imx_v4_v5_defconfig
index 6309ee52ccfc..f1aeb7d72712 100644
--- a/arch/arm/configs/imx_v4_v5_defconfig
+++ b/arch/arm/configs/imx_v4_v5_defconfig
@@ -154,6 +154,7 @@ CONFIG_USB=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_MXC=y
CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_ESDHC_IMX=y
diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig
index 53e82c2523eb..09e974392fa1 100644
--- a/arch/arm/configs/imx_v6_v7_defconfig
+++ b/arch/arm/configs/imx_v6_v7_defconfig
@@ -39,6 +39,8 @@ CONFIG_SOC_IMX53=y
CONFIG_SOC_IMX6Q=y
CONFIG_SOC_IMX6SL=y
CONFIG_SOC_VF610=y
+CONFIG_PCI=y
+CONFIG_PCI_IMX6=y
CONFIG_SMP=y
CONFIG_VMSPLIT_2G=y
CONFIG_PREEMPT_VOLUNTARY=y
@@ -165,6 +167,7 @@ CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_ANATOP=y
CONFIG_REGULATOR_DA9052=y
+CONFIG_REGULATOR_GPIO=y
CONFIG_REGULATOR_MC13783=y
CONFIG_REGULATOR_MC13892=y
CONFIG_REGULATOR_PFUZE100=y
@@ -186,6 +189,7 @@ CONFIG_LCD_L4F00242T03=y
CONFIG_LCD_PLATFORM=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_PWM=y
+CONFIG_BACKLIGHT_GPIO=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_LOGO=y
CONFIG_SOUND=y
@@ -211,6 +215,7 @@ CONFIG_USB_GADGET=y
CONFIG_USB_ETH=m
CONFIG_USB_MASS_STORAGE=m
CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_ESDHC_IMX=y
@@ -225,6 +230,7 @@ CONFIG_LEDS_TRIGGER_BACKLIGHT=y
CONFIG_LEDS_TRIGGER_GPIO=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_INTF_DEV_UIE_EMUL=y
+CONFIG_RTC_DRV_PCF8563=y
CONFIG_RTC_DRV_MC13XXX=y
CONFIG_RTC_DRV_MXC=y
CONFIG_RTC_DRV_SNVS=y
@@ -277,6 +283,7 @@ CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
CONFIG_NLS_ISO8859_15=m
CONFIG_NLS_UTF8=y
+CONFIG_DEBUG_FS=y
CONFIG_MAGIC_SYSRQ=y
# CONFIG_SCHED_DEBUG is not set
CONFIG_PROVE_LOCKING=y
diff --git a/arch/arm/configs/keystone_defconfig b/arch/arm/configs/keystone_defconfig
index 4582e160feab..ec9a41d50680 100644
--- a/arch/arm/configs/keystone_defconfig
+++ b/arch/arm/configs/keystone_defconfig
@@ -111,6 +111,7 @@ CONFIG_MTD_BLOCK=y
CONFIG_MTD_PLATRAM=y
CONFIG_MTD_M25P80=y
CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_DAVINCI=y
CONFIG_MTD_UBI=y
CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
@@ -131,6 +132,8 @@ CONFIG_SPI_DAVINCI=y
CONFIG_SPI_SPIDEV=y
# CONFIG_HWMON is not set
CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_CORE=y
+CONFIG_DAVINCI_WATCHDOG=y
CONFIG_USB=y
CONFIG_USB_DEBUG=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
@@ -145,6 +148,7 @@ CONFIG_DMADEVICES=y
CONFIG_TI_EDMA=y
CONFIG_COMMON_CLK_DEBUG=y
CONFIG_MEMORY=y
+CONFIG_TI_AEMIF=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_MSDOS_FS=y
@@ -177,3 +181,14 @@ CONFIG_CRYPTO_DES=y
CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_CRYPTO_USER_API_HASH=y
CONFIG_CRYPTO_USER_API_SKCIPHER=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_DAVINCI=y
+CONFIG_LEDS_CLASS=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_GPIO=y
diff --git a/arch/arm/configs/koelsch_defconfig b/arch/arm/configs/koelsch_defconfig
index e248f49d5549..86faab565a96 100644
--- a/arch/arm/configs/koelsch_defconfig
+++ b/arch/arm/configs/koelsch_defconfig
@@ -8,7 +8,6 @@ CONFIG_SYSCTL_SYSCALL=y
CONFIG_EMBEDDED=y
CONFIG_PERF_EVENTS=y
CONFIG_SLAB=y
-# CONFIG_BLOCK is not set
CONFIG_ARCH_SHMOBILE_LEGACY=y
CONFIG_ARCH_R8A7791=y
CONFIG_MACH_KOELSCH=y
@@ -35,7 +34,14 @@ CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_BLK_DEV_SD=y
+CONFIG_ATA=y
+CONFIG_SATA_RCAR=y
+CONFIG_MTD=y
+CONFIG_MTD_M25P80=y
CONFIG_NETDEVICES=y
# CONFIG_NET_VENDOR_ARC is not set
# CONFIG_NET_CADENCE is not set
@@ -53,18 +59,31 @@ CONFIG_SH_ETH=y
# CONFIG_NET_VENDOR_VIA is not set
# CONFIG_NET_VENDOR_WIZNET is not set
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_SH_SCI=y
CONFIG_SERIAL_SH_SCI_NR_UARTS=20
CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_I2C=y
+CONFIG_I2C_RCAR=y
+CONFIG_SPI=y
+CONFIG_SPI_RSPI=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_RCAR=y
# CONFIG_HWMON is not set
CONFIG_THERMAL=y
CONFIG_RCAR_THERMAL=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_GPIO=y
# CONFIG_HID is not set
# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+CONFIG_MMC_SDHI=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
# CONFIG_IOMMU_SUPPORT is not set
# CONFIG_DNOTIFY is not set
CONFIG_TMPFS=y
diff --git a/arch/arm/configs/kzm9d_defconfig b/arch/arm/configs/kzm9d_defconfig
deleted file mode 100644
index e42ce3756af3..000000000000
--- a/arch/arm/configs/kzm9d_defconfig
+++ /dev/null
@@ -1,89 +0,0 @@
-# CONFIG_ARM_PATCH_PHYS_VIRT is not set
-CONFIG_EXPERIMENTAL=y
-CONFIG_SYSVIPC=y
-CONFIG_NO_HZ=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=16
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
-CONFIG_SYSCTL_SYSCALL=y
-CONFIG_EMBEDDED=y
-CONFIG_PERF_EVENTS=y
-CONFIG_SLAB=y
-# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_IOSCHED_DEADLINE is not set
-# CONFIG_IOSCHED_CFQ is not set
-CONFIG_ARCH_SHMOBILE_LEGACY=y
-CONFIG_ARCH_EMEV2=y
-CONFIG_MACH_KZM9D=y
-CONFIG_MEMORY_START=0x40000000
-CONFIG_MEMORY_SIZE=0x10000000
-# CONFIG_SH_TIMER_TMU is not set
-# CONFIG_SWP_EMULATE is not set
-# CONFIG_CACHE_L2X0 is not set
-CONFIG_SMP=y
-CONFIG_NR_CPUS=2
-CONFIG_HOTPLUG_CPU=y
-# CONFIG_LOCAL_TIMERS is not set
-CONFIG_AEABI=y
-# CONFIG_OABI_COMPAT is not set
-# CONFIG_CROSS_MEMORY_ATTACH is not set
-CONFIG_FORCE_MAX_ZONEORDER=13
-CONFIG_ZBOOT_ROM_TEXT=0x0
-CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_ARM_APPENDED_DTB=y
-CONFIG_AUTO_ZRELADDR=y
-CONFIG_VFP=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_PM_RUNTIME=y
-CONFIG_NET=y
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_INET=y
-CONFIG_IP_PNP=y
-CONFIG_IP_PNP_DHCP=y
-# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
-# CONFIG_INET_XFRM_MODE_TUNNEL is not set
-# CONFIG_INET_XFRM_MODE_BEET is not set
-# CONFIG_INET_LRO is not set
-# CONFIG_INET_DIAG is not set
-# CONFIG_IPV6 is not set
-# CONFIG_WIRELESS is not set
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-# CONFIG_BLK_DEV is not set
-CONFIG_NETDEVICES=y
-# CONFIG_NET_VENDOR_BROADCOM is not set
-# CONFIG_NET_VENDOR_CHELSIO is not set
-# CONFIG_NET_VENDOR_CIRRUS is not set
-# CONFIG_NET_VENDOR_FARADAY is not set
-# CONFIG_NET_VENDOR_INTEL is not set
-# CONFIG_NET_VENDOR_MARVELL is not set
-# CONFIG_NET_VENDOR_MICREL is not set
-# CONFIG_NET_VENDOR_NATSEMI is not set
-# CONFIG_NET_VENDOR_SEEQ is not set
-CONFIG_SMSC911X=y
-# CONFIG_NET_VENDOR_STMICRO is not set
-# CONFIG_NET_VENDOR_WIZNET is not set
-# CONFIG_WLAN is not set
-# CONFIG_INPUT_MOUSEDEV is not set
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_SERIO is not set
-# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_SERIAL_8250_EM=y
-# CONFIG_HW_RANDOM is not set
-CONFIG_GPIOLIB=y
-CONFIG_GPIO_EM=y
-# CONFIG_HWMON is not set
-# CONFIG_HID_SUPPORT is not set
-# CONFIG_USB_SUPPORT is not set
-# CONFIG_IOMMU_SUPPORT is not set
-# CONFIG_DNOTIFY is not set
-CONFIG_TMPFS=y
-# CONFIG_MISC_FILESYSTEMS is not set
-CONFIG_NFS_FS=y
-CONFIG_ROOT_NFS=y
-# CONFIG_FTRACE is not set
diff --git a/arch/arm/configs/kzm9g_defconfig b/arch/arm/configs/kzm9g_defconfig
index 9934dbc23d64..12bd1f63c399 100644
--- a/arch/arm/configs/kzm9g_defconfig
+++ b/arch/arm/configs/kzm9g_defconfig
@@ -60,6 +60,8 @@ CONFIG_IRDA=y
CONFIG_SH_IRDA=y
# CONFIG_WIRELESS is not set
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_NETDEVICES=y
diff --git a/arch/arm/configs/lager_defconfig b/arch/arm/configs/lager_defconfig
index 883443f8f4f3..58702440472a 100644
--- a/arch/arm/configs/lager_defconfig
+++ b/arch/arm/configs/lager_defconfig
@@ -49,6 +49,13 @@ CONFIG_IP_PNP_DHCP=y
# CONFIG_IPV6 is not set
# CONFIG_WIRELESS is not set
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=y
+CONFIG_MTD_M25P80=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_ATA=y
+CONFIG_SATA_RCAR=y
CONFIG_NETDEVICES=y
# CONFIG_NET_CORE is not set
# CONFIG_NET_VENDOR_ARC is not set
@@ -81,6 +88,8 @@ CONFIG_SERIAL_SH_SCI_CONSOLE=y
CONFIG_I2C=y
CONFIG_I2C_GPIO=y
CONFIG_I2C_RCAR=y
+CONFIG_SPI=y
+CONFIG_SPI_RSPI=y
CONFIG_GPIO_SH_PFC=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_RCAR=y
@@ -90,8 +99,20 @@ CONFIG_RCAR_THERMAL=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_GPIO=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_SOC_CAMERA=y
+CONFIG_SOC_CAMERA_PLATFORM=y
+CONFIG_VIDEO_RCAR_VIN=y
+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
+CONFIG_VIDEO_ADV7180=y
CONFIG_DRM=y
CONFIG_DRM_RCAR_DU=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_RCAR=y
# CONFIG_USB_SUPPORT is not set
CONFIG_MMC=y
CONFIG_MMC_SDHI=y
diff --git a/arch/arm/configs/mackerel_defconfig b/arch/arm/configs/mackerel_defconfig
index a61e1653fc5e..57ececba2ae6 100644
--- a/arch/arm/configs/mackerel_defconfig
+++ b/arch/arm/configs/mackerel_defconfig
@@ -42,6 +42,8 @@ CONFIG_IP_PNP_DHCP=y
# CONFIG_IPV6 is not set
# CONFIG_WIRELESS is not set
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
CONFIG_MTD=y
CONFIG_MTD_CONCAT=y
diff --git a/arch/arm/configs/marzen_defconfig b/arch/arm/configs/marzen_defconfig
index f21bd405cc2a..92994f7f6fd8 100644
--- a/arch/arm/configs/marzen_defconfig
+++ b/arch/arm/configs/marzen_defconfig
@@ -43,6 +43,8 @@ CONFIG_IP_PNP_DHCP=y
# CONFIG_IPV6 is not set
# CONFIG_WIRELESS is not set
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_STANDALONE is not set
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
# CONFIG_FW_LOADER is not set
diff --git a/arch/arm/configs/multi_v5_defconfig b/arch/arm/configs/multi_v5_defconfig
new file mode 100644
index 000000000000..aa3dfb084fed
--- /dev/null
+++ b/arch/arm/configs/multi_v5_defconfig
@@ -0,0 +1,190 @@
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_LOG_BUF_SHIFT=19
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_KPROBES=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_ARCH_MULTI_V7 is not set
+CONFIG_ARCH_MVEBU=y
+CONFIG_MACH_KIRKWOOD=y
+CONFIG_MACH_T5325=y
+CONFIG_ARCH_MXC=y
+CONFIG_MACH_IMX25_DT=y
+CONFIG_MACH_IMX27_DT=y
+CONFIG_ARCH_U300=y
+CONFIG_PCI_MVEBU=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_IDLE=y
+CONFIG_ARM_KIRKWOOD_CPUIDLE=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IPV6 is not set
+CONFIG_NET_PKTGEN=m
+CONFIG_CFG80211=y
+CONFIG_MAC80211=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_GEOMETRY=y
+# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ORION=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_EEPROM_AT24=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=m
+CONFIG_CHR_DEV_SG=m
+CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_MV=y
+CONFIG_NETDEVICES=y
+CONFIG_NET_DSA_MV88E6123_61_65=y
+CONFIG_MV643XX_ETH=y
+CONFIG_R8169=y
+CONFIG_MARVELL_PHY=y
+CONFIG_LIBERTAS=y
+CONFIG_LIBERTAS_SDIO=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_LEGACY_PTY_COUNT=16
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_OF_PLATFORM=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MV64XXX=y
+CONFIG_I2C_NOMADIK=y
+CONFIG_SPI=y
+CONFIG_SPI_ORION=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_GPIO=y
+CONFIG_POWER_RESET_QNAP=y
+CONFIG_SENSORS_ADT7475=y
+CONFIG_SENSORS_LM63=y
+CONFIG_SENSORS_LM75=y
+CONFIG_SENSORS_LM85=y
+CONFIG_THERMAL=y
+CONFIG_KIRKWOOD_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_ORION_WATCHDOG=y
+CONFIG_FB=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_KIRKWOOD_SOC=y
+CONFIG_SND_KIRKWOOD_SOC_T5325=y
+# CONFIG_ABX500_CORE is not set
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_PRINTER=m
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_MMC=y
+CONFIG_SDIO_UART=y
+CONFIG_MMC_MVSDIO=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_RS5C372=y
+CONFIG_RTC_DRV_PCF8563=y
+CONFIG_RTC_DRV_S35390A=y
+CONFIG_RTC_DRV_MV=y
+CONFIG_DMADEVICES=y
+CONFIG_MV_XOR=y
+CONFIG_STAGING=y
+CONFIG_FB_XGI=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+CONFIG_EXT4_FS=y
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_JFFS2_FS=y
+CONFIG_CRAMFS=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_850=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=y
+CONFIG_NLS_UTF8=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_FTRACE is not set
+CONFIG_DEBUG_USER=y
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_MV_CESA=y
+CONFIG_CRC_CCITT=y
+CONFIG_LIBCRC32C=y
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index ee6982976d66..d4e8a47a2f7c 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -1,4 +1,5 @@
CONFIG_SYSVIPC=y
+CONFIG_FHANDLE=y
CONFIG_IRQ_DOMAIN_DEBUG=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -9,8 +10,12 @@ CONFIG_MODULE_UNLOAD=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_ARCH_MVEBU=y
CONFIG_MACH_ARMADA_370=y
+CONFIG_MACH_ARMADA_375=y
+CONFIG_MACH_ARMADA_38X=y
CONFIG_MACH_ARMADA_XP=y
+CONFIG_MACH_DOVE=y
CONFIG_ARCH_BCM=y
+CONFIG_ARCH_BCM_5301X=y
CONFIG_ARCH_BCM_MOBILE=y
CONFIG_ARCH_BERLIN=y
CONFIG_MACH_BERLIN_BG2=y
@@ -31,6 +36,10 @@ CONFIG_SOC_OMAP5=y
CONFIG_SOC_AM33XX=y
CONFIG_SOC_DRA7XX=y
CONFIG_SOC_AM43XX=y
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_MSM8X60=y
+CONFIG_ARCH_MSM8960=y
+CONFIG_ARCH_MSM8974=y
CONFIG_ARCH_ROCKCHIP=y
CONFIG_ARCH_SOCFPGA=y
CONFIG_PLAT_SPEAR=y
@@ -55,6 +64,7 @@ CONFIG_ARCH_VEXPRESS_CA9X4=y
CONFIG_ARCH_VIRT=y
CONFIG_ARCH_WM8850=y
CONFIG_ARCH_ZYNQ=y
+CONFIG_NEON=y
CONFIG_TRUSTED_FOUNDATIONS=y
CONFIG_PCI=y
CONFIG_PCI_MSI=y
@@ -94,6 +104,7 @@ CONFIG_RFKILL_GPIO=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=64
CONFIG_OMAP_OCP2SCP=y
CONFIG_MTD=y
CONFIG_MTD_M25P80=y
@@ -111,6 +122,7 @@ CONFIG_SATA_MV=y
CONFIG_NETDEVICES=y
CONFIG_SUN4I_EMAC=y
CONFIG_NET_CALXEDA_XGMAC=y
+CONFIG_MV643XX_ETH=y
CONFIG_MVNETA=y
CONFIG_KS8851=y
CONFIG_R8169=y
@@ -146,6 +158,8 @@ CONFIG_SERIAL_SIRFSOC_CONSOLE=y
CONFIG_SERIAL_TEGRA=y
CONFIG_SERIAL_IMX=y
CONFIG_SERIAL_IMX_CONSOLE=y
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
CONFIG_SERIAL_VT8500=y
CONFIG_SERIAL_VT8500_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
@@ -159,6 +173,7 @@ CONFIG_SERIAL_ST_ASC=y
CONFIG_SERIAL_ST_ASC_CONSOLE=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MUX=y
+CONFIG_I2C_MUX_PCA954x=y
CONFIG_I2C_MUX_PINCTRL=y
CONFIG_I2C_DESIGNWARE_PLATFORM=y
CONFIG_I2C_MV64XXX=y
@@ -187,7 +202,10 @@ CONFIG_POWER_RESET_AS3722=y
CONFIG_POWER_RESET_GPIO=y
CONFIG_SENSORS_LM90=y
CONFIG_THERMAL=y
+CONFIG_DOVE_THERMAL=y
CONFIG_ARMADA_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_ORION_WATCHDOG=y
CONFIG_MFD_AS3722=y
CONFIG_MFD_CROS_EC=y
CONFIG_MFD_CROS_EC_SPI=y
@@ -212,6 +230,8 @@ CONFIG_REGULATOR_VEXPRESS=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_USB_GSPCA=y
CONFIG_DRM=y
CONFIG_DRM_TEGRA=y
CONFIG_DRM_PANEL_SIMPLE=y
@@ -254,6 +274,7 @@ CONFIG_MMC_ARMMMCI=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_ESDHC_IMX=y
CONFIG_MMC_SDHCI_TEGRA=y
+CONFIG_MMC_SDHCI_DOVE=y
CONFIG_MMC_SDHCI_SPEAR=y
CONFIG_MMC_SDHCI_BCM_KONA=y
CONFIG_MMC_OMAP=y
@@ -294,6 +315,10 @@ CONFIG_MFD_NVEC=y
CONFIG_KEYBOARD_NVEC=y
CONFIG_SERIO_NVEC_PS2=y
CONFIG_NVEC_POWER=y
+CONFIG_COMMON_CLK_QCOM=y
+CONFIG_MSM_GCC_8660=y
+CONFIG_MSM_MMCC_8960=y
+CONFIG_MSM_MMCC_8974=y
CONFIG_TEGRA_IOMMU_GART=y
CONFIG_TEGRA_IOMMU_SMMU=y
CONFIG_MEMORY=y
diff --git a/arch/arm/configs/mvebu_v5_defconfig b/arch/arm/configs/mvebu_v5_defconfig
new file mode 100644
index 000000000000..36484a37a1ca
--- /dev/null
+++ b/arch/arm/configs/mvebu_v5_defconfig
@@ -0,0 +1,181 @@
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_LOG_BUF_SHIFT=19
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_KPROBES=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_ARCH_MULTI_V7 is not set
+CONFIG_ARCH_MVEBU=y
+CONFIG_MACH_KIRKWOOD=y
+CONFIG_MACH_T5325=y
+# CONFIG_CPU_FEROCEON_OLD_ID is not set
+CONFIG_PCI_MVEBU=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_IDLE=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IPV6 is not set
+CONFIG_NET_PKTGEN=m
+CONFIG_CFG80211=y
+CONFIG_MAC80211=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_GEOMETRY=y
+# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ORION=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_EEPROM_AT24=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=m
+CONFIG_CHR_DEV_SG=m
+CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_MV=y
+CONFIG_NETDEVICES=y
+CONFIG_NET_DSA_MV88E6123_61_65=y
+CONFIG_MV643XX_ETH=y
+CONFIG_R8169=y
+CONFIG_MARVELL_PHY=y
+CONFIG_LIBERTAS=y
+CONFIG_LIBERTAS_SDIO=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_LEGACY_PTY_COUNT=16
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_OF_PLATFORM=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MV64XXX=y
+CONFIG_SPI=y
+CONFIG_SPI_ORION=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_GPIO=y
+CONFIG_POWER_RESET_QNAP=y
+CONFIG_SENSORS_ADT7475=y
+CONFIG_SENSORS_LM63=y
+CONFIG_SENSORS_LM75=y
+CONFIG_SENSORS_LM85=y
+CONFIG_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_ORION_WATCHDOG=y
+CONFIG_FB=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_KIRKWOOD_SOC=y
+CONFIG_SND_KIRKWOOD_SOC_T5325=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_PRINTER=m
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_MMC=y
+CONFIG_SDIO_UART=y
+CONFIG_MMC_MVSDIO=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_RS5C372=y
+CONFIG_RTC_DRV_PCF8563=y
+CONFIG_RTC_DRV_S35390A=y
+CONFIG_RTC_DRV_MV=y
+CONFIG_DMADEVICES=y
+CONFIG_MV_XOR=y
+CONFIG_STAGING=y
+CONFIG_FB_XGI=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+CONFIG_EXT4_FS=y
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_JFFS2_FS=y
+CONFIG_CRAMFS=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_850=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=y
+CONFIG_NLS_UTF8=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_FTRACE is not set
+CONFIG_DEBUG_USER=y
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_MV_CESA=y
+CONFIG_CRC_CCITT=y
+CONFIG_LIBCRC32C=y
diff --git a/arch/arm/configs/mvebu_defconfig b/arch/arm/configs/mvebu_v7_defconfig
index 0f4511d2849f..a34713d8db9f 100644
--- a/arch/arm/configs/mvebu_defconfig
+++ b/arch/arm/configs/mvebu_v7_defconfig
@@ -10,7 +10,10 @@ CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_ARCH_MVEBU=y
CONFIG_MACH_ARMADA_370=y
+CONFIG_MACH_ARMADA_375=y
+CONFIG_MACH_ARMADA_38X=y
CONFIG_MACH_ARMADA_XP=y
+CONFIG_NEON=y
# CONFIG_CACHE_L2X0 is not set
# CONFIG_SWP_EMULATE is not set
CONFIG_PCI=y
@@ -62,6 +65,13 @@ CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_THERMAL=y
CONFIG_ARMADA_THERMAL=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_KIRKWOOD_SOC=y
+CONFIG_SND_KIRKWOOD_SOC_ARMADA370_DB=y
+CONFIG_WATCHDOG=y
+CONFIG_ORION_WATCHDOG=y
CONFIG_USB_SUPPORT=y
CONFIG_USB=y
CONFIG_USB_EHCI_HCD=y
diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig
index 3a0b53d225e7..364ba38e40f3 100644
--- a/arch/arm/configs/omap2plus_defconfig
+++ b/arch/arm/configs/omap2plus_defconfig
@@ -28,6 +28,7 @@ CONFIG_ARCH_OMAP3=y
CONFIG_ARCH_OMAP4=y
CONFIG_SOC_OMAP5=y
CONFIG_SOC_AM33XX=y
+CONFIG_SOC_AM43XX=y
CONFIG_SOC_DRA7XX=y
CONFIG_ARM_THUMBEE=y
CONFIG_ARM_ERRATA_411920=y
diff --git a/arch/arm/configs/shmobile_defconfig b/arch/arm/configs/shmobile_defconfig
new file mode 100644
index 000000000000..83b07258a385
--- /dev/null
+++ b/arch/arm/configs/shmobile_defconfig
@@ -0,0 +1,129 @@
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PERF_EVENTS=y
+CONFIG_SLAB=y
+CONFIG_ARCH_SHMOBILE_MULTI=y
+CONFIG_ARCH_EMEV2=y
+CONFIG_ARCH_R8A7790=y
+CONFIG_ARCH_R8A7791=y
+CONFIG_MACH_KOELSCH=y
+CONFIG_MACH_LAGER=y
+# CONFIG_SWP_EMULATE is not set
+CONFIG_CPU_BPREDICT_DISABLE=y
+CONFIG_PL310_ERRATA_588369=y
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_PCI=y
+CONFIG_PCI_RCAR_GEN2=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_HAVE_ARM_ARCH_TIMER=y
+CONFIG_NR_CPUS=8
+CONFIG_AEABI=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_KEXEC=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=y
+CONFIG_MTD_M25P80=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_ATA=y
+CONFIG_SATA_RCAR=y
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+CONFIG_SH_ETH=y
+# CONFIG_NET_VENDOR_SEEQ is not set
+CONFIG_SMSC911X=y
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_SMSC_PHY=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_EM=y
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=20
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_I2C_GPIO=y
+CONFIG_I2C_RCAR=y
+CONFIG_SPI=y
+CONFIG_SPI_RSPI=y
+CONFIG_GPIO_EM=y
+CONFIG_GPIO_RCAR=y
+# CONFIG_HWMON is not set
+CONFIG_THERMAL=y
+CONFIG_RCAR_THERMAL=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_SOC_CAMERA=y
+CONFIG_SOC_CAMERA_PLATFORM=y
+CONFIG_VIDEO_RCAR_VIN=y
+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
+CONFIG_VIDEO_ADV7180=y
+CONFIG_DRM=y
+CONFIG_DRM_RCAR_DU=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_RCAR=y
+CONFIG_USB_RCAR_GEN2_PHY=y
+CONFIG_MMC=y
+CONFIG_MMC_SDHI=y
+CONFIG_MMC_SH_MMCIF=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_RTC_CLASS=y
+CONFIG_DMADEVICES=y
+CONFIG_SH_DMAE=y
+# CONFIG_IOMMU_SUPPORT is not set
+# CONFIG_DNOTIFY is not set
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_CONFIGFS_FS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+# CONFIG_ARM_UNWIND is not set
diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig
index 4e1ce211d43f..e3a05e8801d8 100644
--- a/arch/arm/configs/socfpga_defconfig
+++ b/arch/arm/configs/socfpga_defconfig
@@ -52,6 +52,7 @@ CONFIG_BLK_DEV_SD=y
# CONFIG_SCSI_LOWLEVEL is not set
CONFIG_NETDEVICES=y
CONFIG_STMMAC_ETH=y
+CONFIG_MICREL_PHY=y
# CONFIG_STMMAC_PHY_ID_ZERO_WORKAROUND is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_SERIO_SERPORT is not set
@@ -66,6 +67,9 @@ CONFIG_SERIAL_8250_DW=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT3_FS=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
# CONFIG_DNOTIFY is not set
# CONFIG_INOTIFY_USER is not set
CONFIG_VFAT_FS=y
@@ -82,3 +86,5 @@ CONFIG_DEBUG_INFO=y
CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_DEBUG_USER=y
CONFIG_XZ_DEC=y
+CONFIG_MMC=y
+CONFIG_MMC_DW=y
diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index 3e2259b60236..b5df4a511b0a 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -24,6 +24,7 @@ CONFIG_IP_PNP_BOOTP=y
# CONFIG_WIRELESS is not set
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_EEPROM_SUNXI_SID=y
CONFIG_NETDEVICES=y
CONFIG_SUN4I_EMAC=y
# CONFIG_NET_CADENCE is not set
@@ -48,6 +49,8 @@ CONFIG_I2C=y
# CONFIG_I2C_COMPAT is not set
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MV64XXX=y
+CONFIG_SPI=y
+CONFIG_SPI_SUN6I=y
CONFIG_GPIO_SYSFS=y
# CONFIG_HWMON is not set
CONFIG_WATCHDOG=y
diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig
index 27d69b558c5d..2926281368ab 100644
--- a/arch/arm/configs/tegra_defconfig
+++ b/arch/arm/configs/tegra_defconfig
@@ -1,4 +1,5 @@
CONFIG_SYSVIPC=y
+CONFIG_FHANDLE=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
@@ -86,6 +87,7 @@ CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=64
CONFIG_MTD=y
CONFIG_MTD_M25P80=y
CONFIG_PROC_DEVICETREE=y
@@ -125,6 +127,7 @@ CONFIG_SERIAL_TEGRA=y
CONFIG_SERIAL_OF_PLATFORM=y
# CONFIG_HW_RANDOM is not set
# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_MUX_PCA954x=y
CONFIG_I2C_MUX_PINCTRL=y
CONFIG_I2C_TEGRA=y
CONFIG_SPI=y
@@ -141,6 +144,7 @@ CONFIG_POWER_SUPPLY=y
CONFIG_BATTERY_SBS=y
CONFIG_CHARGER_TPS65090=y
CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_AS3722=y
CONFIG_POWER_RESET_GPIO=y
CONFIG_SENSORS_LM90=y
CONFIG_MFD_AS3722=y
@@ -166,7 +170,8 @@ CONFIG_REGULATOR_TPS65910=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
CONFIG_MEDIA_USB_SUPPORT=y
-CONFIG_USB_VIDEO_CLASS=m
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_USB_GSPCA=y
CONFIG_DRM=y
CONFIG_DRM_TEGRA=y
CONFIG_DRM_PANEL_SIMPLE=y
diff --git a/arch/arm/firmware/Kconfig b/arch/arm/firmware/Kconfig
index bb00ccf00d66..ad396af68e47 100644
--- a/arch/arm/firmware/Kconfig
+++ b/arch/arm/firmware/Kconfig
@@ -11,6 +11,7 @@ menu "Firmware options"
config TRUSTED_FOUNDATIONS
bool "Trusted Foundations secure monitor support"
depends on ARCH_SUPPORTS_TRUSTED_FOUNDATIONS
+ default y
help
Some devices (including most Tegra-based consumer devices on the
market) are booted with the Trusted Foundations secure monitor
@@ -20,7 +21,7 @@ config TRUSTED_FOUNDATIONS
This option allows the kernel to invoke the secure monitor whenever
required on devices using Trusted Foundations. See
arch/arm/include/asm/trusted_foundations.h or the
- tl,trusted-foundations device tree binding documentation for details
+ tlm,trusted-foundations device tree binding documentation for details
on how to use it.
Say n if you don't know what this is about.
diff --git a/arch/arm/firmware/trusted_foundations.c b/arch/arm/firmware/trusted_foundations.c
index ef1e3d8f4af0..3fb1b5a1dce9 100644
--- a/arch/arm/firmware/trusted_foundations.c
+++ b/arch/arm/firmware/trusted_foundations.c
@@ -22,6 +22,15 @@
#define TF_SET_CPU_BOOT_ADDR_SMC 0xfffff200
+#define TF_CPU_PM 0xfffffffc
+#define TF_CPU_PM_S3 0xffffffe3
+#define TF_CPU_PM_S2 0xffffffe6
+#define TF_CPU_PM_S2_NO_MC_CLK 0xffffffe5
+#define TF_CPU_PM_S1 0xffffffe4
+#define TF_CPU_PM_S1_NOFLUSH_L2 0xffffffe7
+
+static unsigned long cpu_boot_addr;
+
static void __naked tf_generic_smc(u32 type, u32 arg1, u32 arg2)
{
asm volatile(
@@ -41,13 +50,22 @@ static void __naked tf_generic_smc(u32 type, u32 arg1, u32 arg2)
static int tf_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
{
- tf_generic_smc(TF_SET_CPU_BOOT_ADDR_SMC, boot_addr, 0);
+ cpu_boot_addr = boot_addr;
+ tf_generic_smc(TF_SET_CPU_BOOT_ADDR_SMC, cpu_boot_addr, 0);
+
+ return 0;
+}
+
+static int tf_prepare_idle(void)
+{
+ tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S1_NOFLUSH_L2, cpu_boot_addr);
return 0;
}
static const struct firmware_ops trusted_foundations_ops = {
.set_cpu_boot_addr = tf_set_cpu_boot_addr,
+ .prepare_idle = tf_prepare_idle,
};
void register_trusted_foundations(struct trusted_foundations_platform_data *pd)
diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h
index 5c2285160575..380ac4f20000 100644
--- a/arch/arm/include/asm/assembler.h
+++ b/arch/arm/include/asm/assembler.h
@@ -30,8 +30,8 @@
* Endian independent macros for shifting bytes within registers.
*/
#ifndef __ARMEB__
-#define pull lsr
-#define push lsl
+#define lspull lsr
+#define lspush lsl
#define get_byte_0 lsl #0
#define get_byte_1 lsr #8
#define get_byte_2 lsr #16
@@ -41,8 +41,8 @@
#define put_byte_2 lsl #16
#define put_byte_3 lsl #24
#else
-#define pull lsl
-#define push lsr
+#define lspull lsl
+#define lspush lsr
#define get_byte_0 lsr #24
#define get_byte_1 lsr #16
#define get_byte_2 lsr #8
diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h
index 62d2cb53b069..9a92fd7864a8 100644
--- a/arch/arm/include/asm/atomic.h
+++ b/arch/arm/include/asm/atomic.h
@@ -60,6 +60,7 @@ static inline int atomic_add_return(int i, atomic_t *v)
int result;
smp_mb();
+ prefetchw(&v->counter);
__asm__ __volatile__("@ atomic_add_return\n"
"1: ldrex %0, [%3]\n"
@@ -99,6 +100,7 @@ static inline int atomic_sub_return(int i, atomic_t *v)
int result;
smp_mb();
+ prefetchw(&v->counter);
__asm__ __volatile__("@ atomic_sub_return\n"
"1: ldrex %0, [%3]\n"
@@ -121,6 +123,7 @@ static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new)
unsigned long res;
smp_mb();
+ prefetchw(&ptr->counter);
do {
__asm__ __volatile__("@ atomic_cmpxchg\n"
@@ -138,6 +141,33 @@ static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new)
return oldval;
}
+static inline int __atomic_add_unless(atomic_t *v, int a, int u)
+{
+ int oldval, newval;
+ unsigned long tmp;
+
+ smp_mb();
+ prefetchw(&v->counter);
+
+ __asm__ __volatile__ ("@ atomic_add_unless\n"
+"1: ldrex %0, [%4]\n"
+" teq %0, %5\n"
+" beq 2f\n"
+" add %1, %0, %6\n"
+" strex %2, %1, [%4]\n"
+" teq %2, #0\n"
+" bne 1b\n"
+"2:"
+ : "=&r" (oldval), "=&r" (newval), "=&r" (tmp), "+Qo" (v->counter)
+ : "r" (&v->counter), "r" (u), "r" (a)
+ : "cc");
+
+ if (oldval != u)
+ smp_mb();
+
+ return oldval;
+}
+
#else /* ARM_ARCH_6 */
#ifdef CONFIG_SMP
@@ -186,10 +216,6 @@ static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
return ret;
}
-#endif /* __LINUX_ARM_ARCH__ */
-
-#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
-
static inline int __atomic_add_unless(atomic_t *v, int a, int u)
{
int c, old;
@@ -200,6 +226,10 @@ static inline int __atomic_add_unless(atomic_t *v, int a, int u)
return c;
}
+#endif /* __LINUX_ARM_ARCH__ */
+
+#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
+
#define atomic_inc(v) atomic_add(1, v)
#define atomic_dec(v) atomic_sub(1, v)
@@ -299,6 +329,7 @@ static inline long long atomic64_add_return(long long i, atomic64_t *v)
unsigned long tmp;
smp_mb();
+ prefetchw(&v->counter);
__asm__ __volatile__("@ atomic64_add_return\n"
"1: ldrexd %0, %H0, [%3]\n"
@@ -340,6 +371,7 @@ static inline long long atomic64_sub_return(long long i, atomic64_t *v)
unsigned long tmp;
smp_mb();
+ prefetchw(&v->counter);
__asm__ __volatile__("@ atomic64_sub_return\n"
"1: ldrexd %0, %H0, [%3]\n"
@@ -364,6 +396,7 @@ static inline long long atomic64_cmpxchg(atomic64_t *ptr, long long old,
unsigned long res;
smp_mb();
+ prefetchw(&ptr->counter);
do {
__asm__ __volatile__("@ atomic64_cmpxchg\n"
@@ -388,6 +421,7 @@ static inline long long atomic64_xchg(atomic64_t *ptr, long long new)
unsigned long tmp;
smp_mb();
+ prefetchw(&ptr->counter);
__asm__ __volatile__("@ atomic64_xchg\n"
"1: ldrexd %0, %H0, [%3]\n"
@@ -409,6 +443,7 @@ static inline long long atomic64_dec_if_positive(atomic64_t *v)
unsigned long tmp;
smp_mb();
+ prefetchw(&v->counter);
__asm__ __volatile__("@ atomic64_dec_if_positive\n"
"1: ldrexd %0, %H0, [%3]\n"
@@ -436,6 +471,7 @@ static inline int atomic64_add_unless(atomic64_t *v, long long a, long long u)
int ret = 1;
smp_mb();
+ prefetchw(&v->counter);
__asm__ __volatile__("@ atomic64_add_unless\n"
"1: ldrexd %0, %H0, [%4]\n"
diff --git a/arch/arm/include/asm/cmpxchg.h b/arch/arm/include/asm/cmpxchg.h
index df2fbba7efc8..abb2c3769b01 100644
--- a/arch/arm/include/asm/cmpxchg.h
+++ b/arch/arm/include/asm/cmpxchg.h
@@ -2,6 +2,7 @@
#define __ASM_ARM_CMPXCHG_H
#include <linux/irqflags.h>
+#include <linux/prefetch.h>
#include <asm/barrier.h>
#if defined(CONFIG_CPU_SA1100) || defined(CONFIG_CPU_SA110)
@@ -35,6 +36,7 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size
#endif
smp_mb();
+ prefetchw((const void *)ptr);
switch (size) {
#if __LINUX_ARM_ARCH__ >= 6
@@ -138,6 +140,8 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
{
unsigned long oldval, res;
+ prefetchw((const void *)ptr);
+
switch (size) {
#ifndef CONFIG_CPU_V6 /* min ARCH >= ARMv6K */
case 1:
@@ -230,6 +234,8 @@ static inline unsigned long long __cmpxchg64(unsigned long long *ptr,
unsigned long long oldval;
unsigned long res;
+ prefetchw(ptr);
+
__asm__ __volatile__(
"1: ldrexd %1, %H1, [%3]\n"
" teq %1, %4\n"
diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h
index acdde76b39bb..42f0889f0584 100644
--- a/arch/arm/include/asm/cputype.h
+++ b/arch/arm/include/asm/cputype.h
@@ -71,6 +71,7 @@
#define ARM_CPU_PART_CORTEX_A5 0xC050
#define ARM_CPU_PART_CORTEX_A15 0xC0F0
#define ARM_CPU_PART_CORTEX_A7 0xC070
+#define ARM_CPU_PART_CORTEX_A12 0xC0D0
#define ARM_CPU_XSCALE_ARCH_MASK 0xe000
#define ARM_CPU_XSCALE_ARCH_V1 0x2000
diff --git a/arch/arm/include/asm/dma-iommu.h b/arch/arm/include/asm/dma-iommu.h
index a8c56acc8c98..eec0a12c5c1d 100644
--- a/arch/arm/include/asm/dma-iommu.h
+++ b/arch/arm/include/asm/dma-iommu.h
@@ -13,9 +13,12 @@ struct dma_iommu_mapping {
/* iommu specific data */
struct iommu_domain *domain;
- void *bitmap;
- size_t bits;
- unsigned int order;
+ unsigned long **bitmaps; /* array of bitmaps */
+ unsigned int nr_bitmaps; /* nr of elements in array */
+ unsigned int extensions;
+ size_t bitmap_size; /* size of a single bitmap */
+ size_t bits; /* per bitmap */
+ unsigned int size; /* per bitmap */
dma_addr_t base;
spinlock_t lock;
@@ -23,8 +26,7 @@ struct dma_iommu_mapping {
};
struct dma_iommu_mapping *
-arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size,
- int order);
+arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size);
void arm_iommu_release_mapping(struct dma_iommu_mapping *mapping);
diff --git a/arch/arm/include/asm/firmware.h b/arch/arm/include/asm/firmware.h
index 15631300c238..2c9f10df7568 100644
--- a/arch/arm/include/asm/firmware.h
+++ b/arch/arm/include/asm/firmware.h
@@ -22,6 +22,10 @@
*/
struct firmware_ops {
/*
+ * Inform the firmware we intend to enter CPU idle mode
+ */
+ int (*prepare_idle)(void);
+ /*
* Enters CPU idle mode
*/
int (*do_idle)(void);
diff --git a/arch/arm/include/asm/floppy.h b/arch/arm/include/asm/floppy.h
index c9f03eccc9d8..f4882553fbb0 100644
--- a/arch/arm/include/asm/floppy.h
+++ b/arch/arm/include/asm/floppy.h
@@ -25,7 +25,7 @@
#define fd_inb(port) inb((port))
#define fd_request_irq() request_irq(IRQ_FLOPPYDISK,floppy_interrupt,\
- IRQF_DISABLED,"floppy",NULL)
+ 0,"floppy",NULL)
#define fd_free_irq() free_irq(IRQ_FLOPPYDISK,NULL)
#define fd_disable_irq() disable_irq(IRQ_FLOPPYDISK)
#define fd_enable_irq() enable_irq(IRQ_FLOPPYDISK)
diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h
index e42cf597f6e6..53e69dae796f 100644
--- a/arch/arm/include/asm/futex.h
+++ b/arch/arm/include/asm/futex.h
@@ -3,11 +3,6 @@
#ifdef __KERNEL__
-#if defined(CONFIG_CPU_USE_DOMAINS) && defined(CONFIG_SMP)
-/* ARM doesn't provide unprivileged exclusive memory accessors */
-#include <asm-generic/futex.h>
-#else
-
#include <linux/futex.h>
#include <linux/uaccess.h>
#include <asm/errno.h>
@@ -28,6 +23,7 @@
#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
smp_mb(); \
+ prefetchw(uaddr); \
__asm__ __volatile__( \
"1: ldrex %1, [%3]\n" \
" " insn "\n" \
@@ -51,6 +47,8 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
return -EFAULT;
smp_mb();
+ /* Prefetching cannot fault */
+ prefetchw(uaddr);
__asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
"1: ldrex %1, [%4]\n"
" teq %1, %2\n"
@@ -164,6 +162,5 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
return ret;
}
-#endif /* !(CPU_USE_DOMAINS && SMP) */
#endif /* __KERNEL__ */
#endif /* _ASM_ARM_FUTEX_H */
diff --git a/arch/arm/plat-orion/include/plat/cache-feroceon-l2.h b/arch/arm/include/asm/hardware/cache-feroceon-l2.h
index 06f982d55697..12e1588dc4f1 100644
--- a/arch/arm/plat-orion/include/plat/cache-feroceon-l2.h
+++ b/arch/arm/include/asm/hardware/cache-feroceon-l2.h
@@ -1,5 +1,5 @@
/*
- * arch/arm/plat-orion/include/plat/cache-feroceon-l2.h
+ * arch/arm/include/asm/hardware/cache-feroceon-l2.h
*
* Copyright (C) 2008 Marvell Semiconductor
*
@@ -9,3 +9,5 @@
*/
extern void __init feroceon_l2_init(int l2_wt_override);
+extern int __init feroceon_of_init(void);
+
diff --git a/arch/arm/include/asm/hw_breakpoint.h b/arch/arm/include/asm/hw_breakpoint.h
index eef55ea9ef00..8e427c7b4425 100644
--- a/arch/arm/include/asm/hw_breakpoint.h
+++ b/arch/arm/include/asm/hw_breakpoint.h
@@ -51,6 +51,7 @@ static inline void decode_ctrl_reg(u32 reg,
#define ARM_DEBUG_ARCH_V7_ECP14 3
#define ARM_DEBUG_ARCH_V7_MM 4
#define ARM_DEBUG_ARCH_V7_1 5
+#define ARM_DEBUG_ARCH_V8 6
/* Breakpoint */
#define ARM_BREAKPOINT_EXECUTE 0
diff --git a/arch/arm/include/asm/hwcap.h b/arch/arm/include/asm/hwcap.h
index 6ff56eca3f1f..6e183fd269fb 100644
--- a/arch/arm/include/asm/hwcap.h
+++ b/arch/arm/include/asm/hwcap.h
@@ -9,6 +9,7 @@
* instruction set this cpu supports.
*/
#define ELF_HWCAP (elf_hwcap)
-extern unsigned int elf_hwcap;
+#define ELF_HWCAP2 (elf_hwcap2)
+extern unsigned int elf_hwcap, elf_hwcap2;
#endif
#endif
diff --git a/arch/arm/include/asm/jump_label.h b/arch/arm/include/asm/jump_label.h
index 863c892b4aaa..70f9b9bfb1f9 100644
--- a/arch/arm/include/asm/jump_label.h
+++ b/arch/arm/include/asm/jump_label.h
@@ -4,7 +4,6 @@
#ifdef __KERNEL__
#include <linux/types.h>
-#include <asm/system.h>
#define JUMP_LABEL_NOP_SIZE 4
diff --git a/arch/arm/include/asm/kprobes.h b/arch/arm/include/asm/kprobes.h
index f82ec22eeb11..49fa0dfaad33 100644
--- a/arch/arm/include/asm/kprobes.h
+++ b/arch/arm/include/asm/kprobes.h
@@ -18,7 +18,7 @@
#include <linux/types.h>
#include <linux/ptrace.h>
-#include <linux/percpu.h>
+#include <linux/notifier.h>
#define __ARCH_WANT_KPROBES_INSN_SLOT
#define MAX_INSN_SIZE 2
@@ -28,21 +28,10 @@
#define kretprobe_blacklist_size 0
typedef u32 kprobe_opcode_t;
-
struct kprobe;
-typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *);
-typedef unsigned long (kprobe_check_cc)(unsigned long);
-typedef void (kprobe_insn_singlestep_t)(struct kprobe *, struct pt_regs *);
-typedef void (kprobe_insn_fn_t)(void);
+#include <asm/probes.h>
-/* Architecture specific copy of original instruction. */
-struct arch_specific_insn {
- kprobe_opcode_t *insn;
- kprobe_insn_handler_t *insn_handler;
- kprobe_check_cc *insn_check_cc;
- kprobe_insn_singlestep_t *insn_singlestep;
- kprobe_insn_fn_t *insn_fn;
-};
+#define arch_specific_insn arch_probes_insn
struct prev_kprobe {
struct kprobe *kp;
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
index 1d3153c7eb41..816db0bf2dd8 100644
--- a/arch/arm/include/asm/kvm_arm.h
+++ b/arch/arm/include/asm/kvm_arm.h
@@ -55,6 +55,7 @@
* The bits we set in HCR:
* TAC: Trap ACTLR
* TSC: Trap SMC
+ * TVM: Trap VM ops (until MMU and caches are on)
* TSW: Trap cache operations by set/way
* TWI: Trap WFI
* TWE: Trap WFE
@@ -68,8 +69,7 @@
*/
#define HCR_GUEST_MASK (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \
HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \
- HCR_TWE | HCR_SWIO | HCR_TIDCP)
-#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
+ HCR_TVM | HCR_TWE | HCR_SWIO | HCR_TIDCP)
/* System Control Register (SCTLR) bits */
#define SCTLR_TE (1 << 30)
diff --git a/arch/arm/include/asm/kvm_asm.h b/arch/arm/include/asm/kvm_asm.h
index 661da11f76f4..53b3c4a50d5c 100644
--- a/arch/arm/include/asm/kvm_asm.h
+++ b/arch/arm/include/asm/kvm_asm.h
@@ -48,7 +48,9 @@
#define c13_TID_URO 26 /* Thread ID, User R/O */
#define c13_TID_PRIV 27 /* Thread ID, Privileged */
#define c14_CNTKCTL 28 /* Timer Control Register (PL1) */
-#define NR_CP15_REGS 29 /* Number of regs (incl. invalid) */
+#define c10_AMAIR0 29 /* Auxilary Memory Attribute Indirection Reg0 */
+#define c10_AMAIR1 30 /* Auxilary Memory Attribute Indirection Reg1 */
+#define NR_CP15_REGS 31 /* Number of regs (incl. invalid) */
#define ARM_EXCEPTION_RESET 0
#define ARM_EXCEPTION_UNDEFINED 1
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 098f7dd6d564..09af14999c9b 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -101,6 +101,12 @@ struct kvm_vcpu_arch {
/* The CPU type we expose to the VM */
u32 midr;
+ /* HYP trapping configuration */
+ u32 hcr;
+
+ /* Interrupt related fields */
+ u32 irq_lines; /* IRQ and FIQ levels */
+
/* Exception Information */
struct kvm_vcpu_fault_info fault;
@@ -128,9 +134,6 @@ struct kvm_vcpu_arch {
/* IO related fields */
struct kvm_decode mmio_decode;
- /* Interrupt related fields */
- u32 irq_lines; /* IRQ and FIQ levels */
-
/* Cache some mmu pages needed inside spinlock regions */
struct kvm_mmu_memory_cache mmu_page_cache;
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 2d122adcdb22..5c7aa3c1519f 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -114,11 +114,34 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
pmd_val(*pmd) |= L_PMD_S2_RDWR;
}
+/* Open coded p*d_addr_end that can deal with 64bit addresses */
+#define kvm_pgd_addr_end(addr, end) \
+({ u64 __boundary = ((addr) + PGDIR_SIZE) & PGDIR_MASK; \
+ (__boundary - 1 < (end) - 1)? __boundary: (end); \
+})
+
+#define kvm_pud_addr_end(addr,end) (end)
+
+#define kvm_pmd_addr_end(addr, end) \
+({ u64 __boundary = ((addr) + PMD_SIZE) & PMD_MASK; \
+ (__boundary - 1 < (end) - 1)? __boundary: (end); \
+})
+
struct kvm;
-static inline void coherent_icache_guest_page(struct kvm *kvm, hva_t hva,
- unsigned long size)
+#define kvm_flush_dcache_to_poc(a,l) __cpuc_flush_dcache_area((a), (l))
+
+static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu)
{
+ return (vcpu->arch.cp15[c1_SCTLR] & 0b101) == 0b101;
+}
+
+static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
+ unsigned long size)
+{
+ if (!vcpu_has_cache_enabled(vcpu))
+ kvm_flush_dcache_to_poc((void *)hva, size);
+
/*
* If we are going to insert an instruction page and the icache is
* either VIPT or PIPT, there is a potential problem where the host
@@ -139,9 +162,10 @@ static inline void coherent_icache_guest_page(struct kvm *kvm, hva_t hva,
}
}
-#define kvm_flush_dcache_to_poc(a,l) __cpuc_flush_dcache_area((a), (l))
#define kvm_virt_to_phys(x) virt_to_idmap((unsigned long)(x))
+void stage2_flush_vm(struct kvm *kvm);
+
#endif /* !__ASSEMBLY__ */
#endif /* __ARM_KVM_MMU_H__ */
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index 4afb376d9c7c..02fa2558f662 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -166,9 +166,17 @@
* Physical vs virtual RAM address space conversion. These are
* private definitions which should NOT be used outside memory.h
* files. Use virt_to_phys/phys_to_virt/__pa/__va instead.
+ *
+ * PFNs are used to describe any physical page; this means
+ * PFN 0 == physical address 0.
*/
-#ifndef __virt_to_phys
-#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
+#if defined(__virt_to_phys)
+#define PHYS_OFFSET PLAT_PHYS_OFFSET
+#define PHYS_PFN_OFFSET ((unsigned long)(PHYS_OFFSET >> PAGE_SHIFT))
+
+#define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT)
+
+#elif defined(CONFIG_ARM_PATCH_PHYS_VIRT)
/*
* Constants used to force the right instruction encodings and shifts
@@ -177,12 +185,17 @@
#define __PV_BITS_31_24 0x81000000
#define __PV_BITS_7_0 0x81
-extern u64 __pv_phys_offset;
+extern unsigned long __pv_phys_pfn_offset;
extern u64 __pv_offset;
extern void fixup_pv_table(const void *, unsigned long);
extern const void *__pv_table_begin, *__pv_table_end;
-#define PHYS_OFFSET __pv_phys_offset
+#define PHYS_OFFSET ((phys_addr_t)__pv_phys_pfn_offset << PAGE_SHIFT)
+#define PHYS_PFN_OFFSET (__pv_phys_pfn_offset)
+
+#define virt_to_pfn(kaddr) \
+ ((((unsigned long)(kaddr) - PAGE_OFFSET) >> PAGE_SHIFT) + \
+ PHYS_PFN_OFFSET)
#define __pv_stub(from,to,instr,type) \
__asm__("@ __pv_stub\n" \
@@ -243,6 +256,7 @@ static inline unsigned long __phys_to_virt(phys_addr_t x)
#else
#define PHYS_OFFSET PLAT_PHYS_OFFSET
+#define PHYS_PFN_OFFSET ((unsigned long)(PHYS_OFFSET >> PAGE_SHIFT))
static inline phys_addr_t __virt_to_phys(unsigned long x)
{
@@ -254,18 +268,11 @@ static inline unsigned long __phys_to_virt(phys_addr_t x)
return x - PHYS_OFFSET + PAGE_OFFSET;
}
-#endif
-#endif
+#define virt_to_pfn(kaddr) \
+ ((((unsigned long)(kaddr) - PAGE_OFFSET) >> PAGE_SHIFT) + \
+ PHYS_PFN_OFFSET)
-/*
- * PFNs are used to describe any physical page; this means
- * PFN 0 == physical address 0.
- *
- * This is the PFN of the first RAM page in the kernel
- * direct-mapped view. We assume this is the first page
- * of RAM in the mem_map as well.
- */
-#define PHYS_PFN_OFFSET ((unsigned long)(PHYS_OFFSET >> PAGE_SHIFT))
+#endif
/*
* These are *only* valid on the kernel direct mapped RAM memory.
@@ -343,9 +350,9 @@ static inline __deprecated void *bus_to_virt(unsigned long x)
*/
#define ARCH_PFN_OFFSET PHYS_PFN_OFFSET
-#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
+#define virt_to_page(kaddr) pfn_to_page(virt_to_pfn(kaddr))
#define virt_addr_valid(kaddr) (((unsigned long)(kaddr) >= PAGE_OFFSET && (unsigned long)(kaddr) < (unsigned long)high_memory) \
- && pfn_valid(__pa(kaddr) >> PAGE_SHIFT) )
+ && pfn_valid(virt_to_pfn(kaddr)))
#endif
diff --git a/arch/arm/include/asm/pgtable-2level.h b/arch/arm/include/asm/pgtable-2level.h
index dfff709fda3c..219ac88a9542 100644
--- a/arch/arm/include/asm/pgtable-2level.h
+++ b/arch/arm/include/asm/pgtable-2level.h
@@ -140,6 +140,7 @@
#define L_PTE_MT_DEV_NONSHARED (_AT(pteval_t, 0x0c) << 2) /* 1100 */
#define L_PTE_MT_DEV_WC (_AT(pteval_t, 0x09) << 2) /* 1001 */
#define L_PTE_MT_DEV_CACHED (_AT(pteval_t, 0x0b) << 2) /* 1011 */
+#define L_PTE_MT_VECTORS (_AT(pteval_t, 0x0f) << 2) /* 1111 */
#define L_PTE_MT_MASK (_AT(pteval_t, 0x0f) << 2)
#ifndef __ASSEMBLY__
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index 7d59b524f2af..5478e5d6ad89 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -216,13 +216,16 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
#define pte_none(pte) (!pte_val(pte))
#define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT)
+#define pte_valid(pte) (pte_val(pte) & L_PTE_VALID)
+#define pte_accessible(mm, pte) (mm_tlb_flush_pending(mm) ? pte_present(pte) : pte_valid(pte))
#define pte_write(pte) (!(pte_val(pte) & L_PTE_RDONLY))
#define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY)
#define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG)
#define pte_exec(pte) (!(pte_val(pte) & L_PTE_XN))
#define pte_special(pte) (0)
-#define pte_present_user(pte) (pte_present(pte) && (pte_val(pte) & L_PTE_USER))
+#define pte_valid_user(pte) \
+ (pte_valid(pte) && (pte_val(pte) & L_PTE_USER) && pte_young(pte))
#if __LINUX_ARM_ARCH__ < 6
static inline void __sync_icache_dcache(pte_t pteval)
@@ -237,7 +240,7 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
{
unsigned long ext = 0;
- if (addr < TASK_SIZE && pte_present_user(pteval)) {
+ if (addr < TASK_SIZE && pte_valid_user(pteval)) {
__sync_icache_dcache(pteval);
ext |= PTE_EXT_NG;
}
diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h
index f24edad26c70..ae1919be8f98 100644
--- a/arch/arm/include/asm/pmu.h
+++ b/arch/arm/include/asm/pmu.h
@@ -71,6 +71,8 @@ struct arm_pmu {
void (*disable)(struct perf_event *event);
int (*get_event_idx)(struct pmu_hw_events *hw_events,
struct perf_event *event);
+ void (*clear_event_idx)(struct pmu_hw_events *hw_events,
+ struct perf_event *event);
int (*set_event_filter)(struct hw_perf_event *evt,
struct perf_event_attr *attr);
u32 (*read_counter)(struct perf_event *event);
diff --git a/arch/arm/include/asm/probes.h b/arch/arm/include/asm/probes.h
new file mode 100644
index 000000000000..806cfe622a9e
--- /dev/null
+++ b/arch/arm/include/asm/probes.h
@@ -0,0 +1,43 @@
+/*
+ * arch/arm/include/asm/probes.h
+ *
+ * Original contents copied from arch/arm/include/asm/kprobes.h
+ * which contains the following notice...
+ *
+ * Copyright (C) 2006, 2007 Motorola 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.
+ *
+ * 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 _ASM_PROBES_H
+#define _ASM_PROBES_H
+
+typedef u32 probes_opcode_t;
+
+struct arch_probes_insn;
+typedef void (probes_insn_handler_t)(probes_opcode_t,
+ struct arch_probes_insn *,
+ struct pt_regs *);
+typedef unsigned long (probes_check_cc)(unsigned long);
+typedef void (probes_insn_singlestep_t)(probes_opcode_t,
+ struct arch_probes_insn *,
+ struct pt_regs *);
+typedef void (probes_insn_fn_t)(void);
+
+/* Architecture specific copy of original instruction. */
+struct arch_probes_insn {
+ probes_opcode_t *insn;
+ probes_insn_handler_t *insn_handler;
+ probes_check_cc *insn_check_cc;
+ probes_insn_singlestep_t *insn_singlestep;
+ probes_insn_fn_t *insn_fn;
+};
+
+#endif
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
index 04c99f36ff7f..c877654fe3bf 100644
--- a/arch/arm/include/asm/ptrace.h
+++ b/arch/arm/include/asm/ptrace.h
@@ -27,9 +27,13 @@ struct pt_regs {
#define thumb_mode(regs) (0)
#endif
+#ifndef CONFIG_CPU_V7M
#define isa_mode(regs) \
- ((((regs)->ARM_cpsr & PSR_J_BIT) >> 23) | \
- (((regs)->ARM_cpsr & PSR_T_BIT) >> 5))
+ ((((regs)->ARM_cpsr & PSR_J_BIT) >> (__ffs(PSR_J_BIT) - 1)) | \
+ (((regs)->ARM_cpsr & PSR_T_BIT) >> (__ffs(PSR_T_BIT))))
+#else
+#define isa_mode(regs) 1 /* Thumb */
+#endif
#define processor_mode(regs) \
((regs)->ARM_cpsr & MODE_MASK)
@@ -80,6 +84,12 @@ static inline long regs_return_value(struct pt_regs *regs)
#define instruction_pointer(regs) (regs)->ARM_pc
+static inline void instruction_pointer_set(struct pt_regs *regs,
+ unsigned long val)
+{
+ instruction_pointer(regs) = val;
+}
+
#ifdef CONFIG_SMP
extern unsigned long profile_pc(struct pt_regs *regs);
#else
diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h
index 22a3b9b5d4a1..2ec765c39ab4 100644
--- a/arch/arm/include/asm/smp.h
+++ b/arch/arm/include/asm/smp.h
@@ -74,6 +74,7 @@ struct secondary_data {
};
extern struct secondary_data secondary_data;
extern volatile int pen_release;
+extern void secondary_startup(void);
extern int __cpu_disable(void);
@@ -114,6 +115,15 @@ struct smp_operations {
#endif
};
+struct of_cpu_method {
+ const char *method;
+ struct smp_operations *ops;
+};
+
+#define CPU_METHOD_OF_DECLARE(name, _method, _ops) \
+ static const struct of_cpu_method __cpu_method_of_table_##name \
+ __used __section(__cpu_method_of_table) \
+ = { .method = _method, .ops = _ops }
/*
* set platform specific SMP operations
*/
diff --git a/arch/arm/include/asm/sync_bitops.h b/arch/arm/include/asm/sync_bitops.h
index 63479eecbf76..9732b8e11e63 100644
--- a/arch/arm/include/asm/sync_bitops.h
+++ b/arch/arm/include/asm/sync_bitops.h
@@ -2,7 +2,6 @@
#define __ASM_SYNC_BITOPS_H__
#include <asm/bitops.h>
-#include <asm/system.h>
/* sync_bitops functions are equivalent to the SMP implementation of the
* original functions, independently from CONFIG_SMP being defined.
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
deleted file mode 100644
index 368165e33c1c..000000000000
--- a/arch/arm/include/asm/system.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* FILE TO BE DELETED. DO NOT ADD STUFF HERE! */
-#include <asm/barrier.h>
-#include <asm/compiler.h>
-#include <asm/cmpxchg.h>
-#include <asm/switch_to.h>
-#include <asm/system_info.h>
-#include <asm/system_misc.h>
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index 71a06b293489..f989d7c22dc5 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -153,6 +153,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
#define TIF_SIGPENDING 0
#define TIF_NEED_RESCHED 1
#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */
+#define TIF_UPROBE 7
#define TIF_SYSCALL_TRACE 8
#define TIF_SYSCALL_AUDIT 9
#define TIF_SYSCALL_TRACEPOINT 10
@@ -165,6 +166,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
+#define _TIF_UPROBE (1 << TIF_UPROBE)
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT)
@@ -178,7 +180,8 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
/*
* Change these and you break ASM code in entry-common.S
*/
-#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_RESUME)
+#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
+ _TIF_NOTIFY_RESUME | _TIF_UPROBE)
#endif /* __KERNEL__ */
#endif /* __ASM_ARM_THREAD_INFO_H */
diff --git a/arch/arm/include/asm/timex.h b/arch/arm/include/asm/timex.h
index 83f2aa83899c..f6fcc67ef06e 100644
--- a/arch/arm/include/asm/timex.h
+++ b/arch/arm/include/asm/timex.h
@@ -12,12 +12,6 @@
#ifndef _ASMARM_TIMEX_H
#define _ASMARM_TIMEX_H
-#ifdef CONFIG_ARCH_MULTIPLATFORM
-#define CLOCK_TICK_RATE 1000000
-#else
-#include <mach/timex.h>
-#endif
-
typedef unsigned long cycles_t;
#define get_cycles() ({ cycles_t c; read_current_timer(&c) ? 0 : c; })
diff --git a/arch/arm/include/asm/trusted_foundations.h b/arch/arm/include/asm/trusted_foundations.h
index 3bd36e2c5f2e..b5f7705abcb0 100644
--- a/arch/arm/include/asm/trusted_foundations.h
+++ b/arch/arm/include/asm/trusted_foundations.h
@@ -30,6 +30,8 @@
#include <linux/printk.h>
#include <linux/bug.h>
#include <linux/of.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
struct trusted_foundations_platform_data {
unsigned int version_major;
@@ -47,10 +49,13 @@ static inline void register_trusted_foundations(
struct trusted_foundations_platform_data *pd)
{
/*
- * If we try to register TF, this means the system needs it to continue.
- * Its absence if thus a fatal error.
+ * If the system requires TF and we cannot provide it, continue booting
+ * but disable features that cannot be provided.
*/
- panic("No support for Trusted Foundations, stopping...\n");
+ pr_err("No support for Trusted Foundations, continuing in degraded mode.\n");
+ pr_err("Secondary processors as well as CPU PM will be disabled.\n");
+ setup_max_cpus = 0;
+ cpu_idle_poll_ctrl(true);
}
static inline void of_register_trusted_foundations(void)
@@ -59,7 +64,7 @@ static inline void of_register_trusted_foundations(void)
* If we find the target should enable TF but does not support it,
* fail as the system won't be able to do much anyway
*/
- if (of_find_compatible_node(NULL, NULL, "tl,trusted-foundations"))
+ if (of_find_compatible_node(NULL, NULL, "tlm,trusted-foundations"))
register_trusted_foundations(NULL);
}
#endif /* CONFIG_TRUSTED_FOUNDATIONS */
diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
index 72abdc541f38..12c3a5decc60 100644
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -19,7 +19,7 @@
#include <asm/unified.h>
#include <asm/compiler.h>
-#if __LINUX_ARM_ARCH__ < 6
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
#include <asm-generic/uaccess-unaligned.h>
#else
#define __get_user_unaligned __get_user
diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h
index acabef1a75df..43876245fc57 100644
--- a/arch/arm/include/asm/unistd.h
+++ b/arch/arm/include/asm/unistd.h
@@ -48,6 +48,5 @@
*/
#define __IGNORE_fadvise64_64
#define __IGNORE_migrate_pages
-#define __IGNORE_kcmp
#endif /* __ASM_ARM_UNISTD_H */
diff --git a/arch/arm/include/asm/uprobes.h b/arch/arm/include/asm/uprobes.h
new file mode 100644
index 000000000000..9472c20b7d49
--- /dev/null
+++ b/arch/arm/include/asm/uprobes.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
+ *
+ * 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_UPROBES_H
+#define _ASM_UPROBES_H
+
+#include <asm/probes.h>
+#include <asm/opcodes.h>
+
+typedef u32 uprobe_opcode_t;
+
+#define MAX_UINSN_BYTES 4
+#define UPROBE_XOL_SLOT_BYTES 64
+
+#define UPROBE_SWBP_ARM_INSN 0xe7f001f9
+#define UPROBE_SS_ARM_INSN 0xe7f001fa
+#define UPROBE_SWBP_INSN __opcode_to_mem_arm(UPROBE_SWBP_ARM_INSN)
+#define UPROBE_SWBP_INSN_SIZE 4
+
+struct arch_uprobe_task {
+ u32 backup;
+ unsigned long saved_trap_no;
+};
+
+struct arch_uprobe {
+ u8 insn[MAX_UINSN_BYTES];
+ unsigned long ixol[2];
+ uprobe_opcode_t bpinsn;
+ bool simulate;
+ u32 pcreg;
+ void (*prehandler)(struct arch_uprobe *auprobe,
+ struct arch_uprobe_task *autask,
+ struct pt_regs *regs);
+ void (*posthandler)(struct arch_uprobe *auprobe,
+ struct arch_uprobe_task *autask,
+ struct pt_regs *regs);
+ struct arch_probes_insn asi;
+};
+
+#endif
diff --git a/arch/arm/include/asm/xen/page.h b/arch/arm/include/asm/xen/page.h
index e0965abacb7d..cf4f3e867395 100644
--- a/arch/arm/include/asm/xen/page.h
+++ b/arch/arm/include/asm/xen/page.h
@@ -97,16 +97,13 @@ static inline pte_t *lookup_address(unsigned long address, unsigned int *level)
return NULL;
}
-static inline int m2p_add_override(unsigned long mfn, struct page *page,
- struct gnttab_map_grant_ref *kmap_op)
-{
- return 0;
-}
+extern int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
+ struct gnttab_map_grant_ref *kmap_ops,
+ struct page **pages, unsigned int count);
-static inline int m2p_remove_override(struct page *page, bool clear_pte)
-{
- return 0;
-}
+extern int clear_foreign_p2m_mapping(struct gnttab_unmap_grant_ref *unmap_ops,
+ struct gnttab_map_grant_ref *kmap_ops,
+ struct page **pages, unsigned int count);
bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn);
bool __set_phys_to_machine_multi(unsigned long pfn, unsigned long mfn,
diff --git a/arch/arm/include/debug/samsung.S b/arch/arm/include/debug/samsung.S
index f3a9cff6d5d4..8d8d922e5e44 100644
--- a/arch/arm/include/debug/samsung.S
+++ b/arch/arm/include/debug/samsung.S
@@ -9,7 +9,7 @@
* published by the Free Software Foundation.
*/
-#include <plat/regs-serial.h>
+#include <linux/serial_s3c.h>
/* The S5PV210/S5PC110 implementations are as belows. */
diff --git a/arch/arm/include/debug/tegra.S b/arch/arm/include/debug/tegra.S
index f98763f0bc17..3bc80599c022 100644
--- a/arch/arm/include/debug/tegra.S
+++ b/arch/arm/include/debug/tegra.S
@@ -53,8 +53,7 @@
#define checkuart(rp, rv, lhu, bit, uart) \
/* Load address of CLK_RST register */ \
- movw rp, #TEGRA_CLK_RST_DEVICES_##lhu & 0xffff ; \
- movt rp, #TEGRA_CLK_RST_DEVICES_##lhu >> 16 ; \
+ ldr rp, =TEGRA_CLK_RST_DEVICES_##lhu ; \
/* Load value from CLK_RST register */ \
ldr rp, [rp, #0] ; \
/* Test UART's reset bit */ \
@@ -62,8 +61,7 @@
/* If set, can't use UART; jump to save no UART */ \
bne 90f ; \
/* Load address of CLK_OUT_ENB register */ \
- movw rp, #TEGRA_CLK_OUT_ENB_##lhu & 0xffff ; \
- movt rp, #TEGRA_CLK_OUT_ENB_##lhu >> 16 ; \
+ ldr rp, =TEGRA_CLK_OUT_ENB_##lhu ; \
/* Load value from CLK_OUT_ENB register */ \
ldr rp, [rp, #0] ; \
/* Test UART's clock enable bit */ \
@@ -71,8 +69,7 @@
/* If clear, can't use UART; jump to save no UART */ \
beq 90f ; \
/* Passed all tests, load address of UART registers */ \
- movw rp, #TEGRA_UART##uart##_BASE & 0xffff ; \
- movt rp, #TEGRA_UART##uart##_BASE >> 16 ; \
+ ldr rp, =TEGRA_UART##uart##_BASE ; \
/* Jump to save UART address */ \
b 91f
@@ -90,15 +87,16 @@
#ifdef CONFIG_TEGRA_DEBUG_UART_AUTO_ODMDATA
/* Check ODMDATA */
-10: movw \rp, #TEGRA_PMC_SCRATCH20 & 0xffff
- movt \rp, #TEGRA_PMC_SCRATCH20 >> 16
+10: ldr \rp, =TEGRA_PMC_SCRATCH20
ldr \rp, [\rp, #0] @ Load PMC_SCRATCH20
- ubfx \rv, \rp, #18, #2 @ 19:18 are console type
+ lsr \rv, \rp, #18 @ 19:18 are console type
+ and \rv, \rv, #3
cmp \rv, #2 @ 2 and 3 mean DCC, UART
beq 11f @ some boards swap the meaning
cmp \rv, #3 @ so accept either
bne 90f
-11: ubfx \rv, \rp, #15, #3 @ 17:15 are UART ID
+11: lsr \rv, \rp, #15 @ 17:15 are UART ID
+ and \rv, #7
cmp \rv, #0 @ UART 0?
beq 20f
cmp \rv, #1 @ UART 1?
diff --git a/arch/arm/include/debug/zynq.S b/arch/arm/include/debug/zynq.S
index f9aa9740a73f..0b762fafa758 100644
--- a/arch/arm/include/debug/zynq.S
+++ b/arch/arm/include/debug/zynq.S
@@ -42,6 +42,9 @@
.endm
.macro waituart,rd,rx
+1001: ldr \rd, [\rx, #UART_SR_OFFSET]
+ tst \rd, #UART_SR_TXEMPTY
+ beq 1001b
.endm
.macro busyuart,rd,rx
diff --git a/arch/arm/include/uapi/asm/hwcap.h b/arch/arm/include/uapi/asm/hwcap.h
index 7dcc10d67253..20d12f230a2f 100644
--- a/arch/arm/include/uapi/asm/hwcap.h
+++ b/arch/arm/include/uapi/asm/hwcap.h
@@ -28,4 +28,13 @@
#define HWCAP_LPAE (1 << 20)
#define HWCAP_EVTSTRM (1 << 21)
+/*
+ * HWCAP2 flags - for elf_hwcap2 (in kernel) and AT_HWCAP2
+ */
+#define HWCAP2_AES (1 << 0)
+#define HWCAP2_PMULL (1 << 1)
+#define HWCAP2_SHA1 (1 << 2)
+#define HWCAP2_SHA2 (1 << 3)
+#define HWCAP2_CRC32 (1 << 4)
+
#endif /* _UAPI__ASMARM_HWCAP_H */
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index a30fc9be9e9e..a766bcbaf8ad 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -50,11 +50,12 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o
obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
-obj-$(CONFIG_KPROBES) += kprobes.o kprobes-common.o patch.o
+obj-$(CONFIG_UPROBES) += probes.o probes-arm.o uprobes.o uprobes-arm.o
+obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o
ifdef CONFIG_THUMB2_KERNEL
-obj-$(CONFIG_KPROBES) += kprobes-thumb.o
+obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o
else
-obj-$(CONFIG_KPROBES) += kprobes-arm.o
+obj-$(CONFIG_KPROBES) += kprobes-arm.o probes-arm.o
endif
obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o
test-kprobes-objs := kprobes-test.o
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index 85e664b6a5f1..f7b450f97e68 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -158,6 +158,6 @@ EXPORT_SYMBOL(__gnu_mcount_nc);
#endif
#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
-EXPORT_SYMBOL(__pv_phys_offset);
+EXPORT_SYMBOL(__pv_phys_pfn_offset);
EXPORT_SYMBOL(__pv_offset);
#endif
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index ded041711beb..85598b5d1efd 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c
@@ -174,6 +174,7 @@ int main(void)
DEFINE(VCPU_FIQ_REGS, offsetof(struct kvm_vcpu, arch.regs.fiq_regs));
DEFINE(VCPU_PC, offsetof(struct kvm_vcpu, arch.regs.usr_regs.ARM_pc));
DEFINE(VCPU_CPSR, offsetof(struct kvm_vcpu, arch.regs.usr_regs.ARM_cpsr));
+ DEFINE(VCPU_HCR, offsetof(struct kvm_vcpu, arch.hcr));
DEFINE(VCPU_IRQ_LINES, offsetof(struct kvm_vcpu, arch.irq_lines));
DEFINE(VCPU_HSR, offsetof(struct kvm_vcpu, arch.fault.hsr));
DEFINE(VCPU_HxFAR, offsetof(struct kvm_vcpu, arch.fault.hxfar));
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index d0d46786892c..16d43cd45619 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -605,41 +605,10 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
*/
int pcibios_enable_device(struct pci_dev *dev, int mask)
{
- u16 cmd, old_cmd;
- int idx;
- struct resource *r;
-
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
- old_cmd = cmd;
- for (idx = 0; idx < 6; idx++) {
- /* Only set up the requested stuff */
- if (!(mask & (1 << idx)))
- continue;
-
- r = dev->resource + idx;
- if (!r->start && r->end) {
- printk(KERN_ERR "PCI: Device %s not available because"
- " of resource collisions\n", pci_name(dev));
- return -EINVAL;
- }
- if (r->flags & IORESOURCE_IO)
- cmd |= PCI_COMMAND_IO;
- if (r->flags & IORESOURCE_MEM)
- cmd |= PCI_COMMAND_MEMORY;
- }
+ if (pci_has_flag(PCI_PROBE_ONLY))
+ return 0;
- /*
- * Bridges (eg, cardbus bridges) need to be fully enabled
- */
- if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
- cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
-
- if (cmd != old_cmd) {
- printk("PCI: enabling device %s (%04x -> %04x)\n",
- pci_name(dev), old_cmd, cmd);
- pci_write_config_word(dev, PCI_COMMAND, cmd);
- }
- return 0;
+ return pci_enable_resources(dev, mask);
}
int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
index f751714d52c1..c7419a585ddc 100644
--- a/arch/arm/kernel/devtree.c
+++ b/arch/arm/kernel/devtree.c
@@ -18,6 +18,7 @@
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/smp.h>
#include <asm/cputype.h>
#include <asm/setup.h>
@@ -63,6 +64,34 @@ void __init arm_dt_memblock_reserve(void)
}
}
+#ifdef CONFIG_SMP
+extern struct of_cpu_method __cpu_method_of_table_begin[];
+extern struct of_cpu_method __cpu_method_of_table_end[];
+
+static int __init set_smp_ops_by_method(struct device_node *node)
+{
+ const char *method;
+ struct of_cpu_method *m = __cpu_method_of_table_begin;
+
+ if (of_property_read_string(node, "enable-method", &method))
+ return 0;
+
+ for (; m < __cpu_method_of_table_end; m++)
+ if (!strcmp(m->method, method)) {
+ smp_set_ops(m->ops);
+ return 1;
+ }
+
+ return 0;
+}
+#else
+static inline int set_smp_ops_by_method(struct device_node *node)
+{
+ return 1;
+}
+#endif
+
+
/*
* arm_dt_init_cpu_maps - Function retrieves cpu nodes from the device tree
* and builds the cpu logical map array containing MPIDR values related to
@@ -79,6 +108,7 @@ void __init arm_dt_init_cpu_maps(void)
* read as 0.
*/
struct device_node *cpu, *cpus;
+ int found_method = 0;
u32 i, j, cpuidx = 1;
u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0;
@@ -150,8 +180,18 @@ void __init arm_dt_init_cpu_maps(void)
}
tmp_map[i] = hwid;
+
+ if (!found_method)
+ found_method = set_smp_ops_by_method(cpu);
}
+ /*
+ * Fallback to an enable-method in the cpus node if nothing found in
+ * a cpu node.
+ */
+ if (!found_method)
+ set_smp_ops_by_method(cpus);
+
if (!bootcpu_valid) {
pr_warn("DT missing boot CPU MPIDR[23:0], fall back to default cpu_logical_map\n");
return;
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
index 34e56647dcee..c108ddcb9ba4 100644
--- a/arch/arm/kernel/ftrace.c
+++ b/arch/arm/kernel/ftrace.c
@@ -156,10 +156,8 @@ int ftrace_make_nop(struct module *mod,
return ret;
}
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- *(unsigned long *)data = 0;
-
return 0;
}
#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index f5f381d91556..f8c08839edf3 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -584,9 +584,10 @@ __fixup_pv_table:
subs r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET
add r4, r4, r3 @ adjust table start address
add r5, r5, r3 @ adjust table end address
- add r6, r6, r3 @ adjust __pv_phys_offset address
+ add r6, r6, r3 @ adjust __pv_phys_pfn_offset address
add r7, r7, r3 @ adjust __pv_offset address
- str r8, [r6, #LOW_OFFSET] @ save computed PHYS_OFFSET to __pv_phys_offset
+ mov r0, r8, lsr #12 @ convert to PFN
+ str r0, [r6, #LOW_OFFSET] @ save computed PHYS_OFFSET to __pv_phys_pfn_offset
strcc ip, [r7, #HIGH_OFFSET] @ save to __pv_offset high bits
mov r6, r3, lsr #24 @ constant for add/sub instructions
teq r3, r6, lsl #24 @ must be 16MiB aligned
@@ -600,7 +601,7 @@ ENDPROC(__fixup_pv_table)
1: .long .
.long __pv_table_begin
.long __pv_table_end
-2: .long __pv_phys_offset
+2: .long __pv_phys_pfn_offset
.long __pv_offset
.text
@@ -688,11 +689,11 @@ ENTRY(fixup_pv_table)
ENDPROC(fixup_pv_table)
.data
- .globl __pv_phys_offset
- .type __pv_phys_offset, %object
-__pv_phys_offset:
- .quad 0
- .size __pv_phys_offset, . -__pv_phys_offset
+ .globl __pv_phys_pfn_offset
+ .type __pv_phys_pfn_offset, %object
+__pv_phys_pfn_offset:
+ .word 0
+ .size __pv_phys_pfn_offset, . -__pv_phys_pfn_offset
.globl __pv_offset
.type __pv_offset, %object
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index 3d446605cbf8..4d963fb66e3f 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -167,7 +167,7 @@ static int debug_arch_supported(void)
/* Can we determine the watchpoint access type from the fsr? */
static int debug_exception_updates_fsr(void)
{
- return 0;
+ return get_debug_arch() >= ARM_DEBUG_ARCH_V8;
}
/* Determine number of WRP registers available. */
@@ -257,6 +257,7 @@ static int enable_monitor_mode(void)
break;
case ARM_DEBUG_ARCH_V7_ECP14:
case ARM_DEBUG_ARCH_V7_1:
+ case ARM_DEBUG_ARCH_V8:
ARM_DBG_WRITE(c0, c2, 2, (dscr | ARM_DSCR_MDBGEN));
isb();
break;
@@ -1072,6 +1073,8 @@ static int __init arch_hw_breakpoint_init(void)
core_num_brps = get_num_brps();
core_num_wrps = get_num_wrps();
+ cpu_notifier_register_begin();
+
/*
* We need to tread carefully here because DBGSWENABLE may be
* driven low on this core and there isn't an architected way to
@@ -1088,6 +1091,7 @@ static int __init arch_hw_breakpoint_init(void)
if (!cpumask_empty(&debug_err_mask)) {
core_num_brps = 0;
core_num_wrps = 0;
+ cpu_notifier_register_done();
return 0;
}
@@ -1107,7 +1111,10 @@ static int __init arch_hw_breakpoint_init(void)
TRAP_HWBKPT, "breakpoint debug exception");
/* Register hotplug and PM notifiers. */
- register_cpu_notifier(&dbg_reset_nb);
+ __register_cpu_notifier(&dbg_reset_nb);
+
+ cpu_notifier_register_done();
+
pm_init();
return 0;
}
diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c
index 8a30c89da70e..ac300c60d656 100644
--- a/arch/arm/kernel/kprobes-arm.c
+++ b/arch/arm/kernel/kprobes-arm.c
@@ -60,13 +60,10 @@
#include <linux/kernel.h>
#include <linux/kprobes.h>
-#include <linux/module.h>
+#include <linux/ptrace.h>
#include "kprobes.h"
-
-#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
-
-#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+#include "probes-arm.h"
#if __LINUX_ARM_ARCH__ >= 6
#define BLX(reg) "blx "reg" \n\t"
@@ -75,92 +72,11 @@
"mov pc, "reg" \n\t"
#endif
-/*
- * To avoid the complications of mimicing single-stepping on a
- * processor without a Next-PC or a single-step mode, and to
- * avoid having to deal with the side-effects of boosting, we
- * simulate or emulate (almost) all ARM instructions.
- *
- * "Simulation" is where the instruction's behavior is duplicated in
- * C code. "Emulation" is where the original instruction is rewritten
- * and executed, often by altering its registers.
- *
- * By having all behavior of the kprobe'd instruction completed before
- * returning from the kprobe_handler(), all locks (scheduler and
- * interrupt) can safely be released. There is no need for secondary
- * breakpoints, no race with MP or preemptable kernels, nor having to
- * clean up resources counts at a later time impacting overall system
- * performance. By rewriting the instruction, only the minimum registers
- * need to be loaded and saved back optimizing performance.
- *
- * Calling the insnslot_*_rwflags version of a function doesn't hurt
- * anything even when the CPSR flags aren't updated by the
- * instruction. It's just a little slower in return for saving
- * a little space by not having a duplicate function that doesn't
- * update the flags. (The same optimization can be said for
- * instructions that do or don't perform register writeback)
- * Also, instructions can either read the flags, only write the
- * flags, or read and write the flags. To save combinations
- * rather than for sheer performance, flag functions just assume
- * read and write of flags.
- */
-
-static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
-{
- kprobe_opcode_t insn = p->opcode;
- long iaddr = (long)p->addr;
- int disp = branch_displacement(insn);
-
- if (insn & (1 << 24))
- regs->ARM_lr = iaddr + 4;
-
- regs->ARM_pc = iaddr + 8 + disp;
-}
-
-static void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs)
-{
- kprobe_opcode_t insn = p->opcode;
- long iaddr = (long)p->addr;
- int disp = branch_displacement(insn);
-
- regs->ARM_lr = iaddr + 4;
- regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2);
- regs->ARM_cpsr |= PSR_T_BIT;
-}
-
-static void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
-{
- kprobe_opcode_t insn = p->opcode;
- int rm = insn & 0xf;
- long rmv = regs->uregs[rm];
-
- if (insn & (1 << 5))
- regs->ARM_lr = (long)p->addr + 4;
-
- regs->ARM_pc = rmv & ~0x1;
- regs->ARM_cpsr &= ~PSR_T_BIT;
- if (rmv & 0x1)
- regs->ARM_cpsr |= PSR_T_BIT;
-}
-
-static void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
-{
- kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- unsigned long mask = 0xf8ff03df; /* Mask out execution state */
- regs->uregs[rd] = regs->ARM_cpsr & mask;
-}
-
-static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
-{
- regs->uregs[12] = regs->uregs[13];
-}
-
static void __kprobes
-emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
+emulate_ldrdstrd(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = (unsigned long)p->addr + 8;
+ unsigned long pc = regs->ARM_pc + 4;
int rt = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
@@ -175,7 +91,7 @@ emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
BLX("%[fn]")
: "=r" (rtv), "=r" (rt2v), "=r" (rnv)
: "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
- [fn] "r" (p->ainsn.insn_fn)
+ [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -186,10 +102,10 @@ emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-emulate_ldr(struct kprobe *p, struct pt_regs *regs)
+emulate_ldr(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = (unsigned long)p->addr + 8;
+ unsigned long pc = regs->ARM_pc + 4;
int rt = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
@@ -202,7 +118,7 @@ emulate_ldr(struct kprobe *p, struct pt_regs *regs)
__asm__ __volatile__ (
BLX("%[fn]")
: "=r" (rtv), "=r" (rnv)
- : "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
+ : "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -216,11 +132,11 @@ emulate_ldr(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-emulate_str(struct kprobe *p, struct pt_regs *regs)
+emulate_str(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
- unsigned long rnpc = (unsigned long)p->addr + 8;
+ unsigned long rtpc = regs->ARM_pc - 4 + str_pc_offset;
+ unsigned long rnpc = regs->ARM_pc + 4;
int rt = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
@@ -234,7 +150,7 @@ emulate_str(struct kprobe *p, struct pt_regs *regs)
__asm__ __volatile__ (
BLX("%[fn]")
: "=r" (rnv)
- : "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
+ : "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -243,10 +159,10 @@ emulate_str(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
+emulate_rd12rn16rm0rs8_rwflags(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = (unsigned long)p->addr + 8;
+ unsigned long pc = regs->ARM_pc + 4;
int rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
@@ -266,7 +182,7 @@ emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdv), [cpsr] "=r" (cpsr)
: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
- "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+ "1" (cpsr), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -278,9 +194,9 @@ emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
+emulate_rd12rn16rm0_rwflags_nopc(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
@@ -296,7 +212,7 @@ emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdv), [cpsr] "=r" (cpsr)
: "0" (rdv), "r" (rnv), "r" (rmv),
- "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+ "1" (cpsr), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -305,9 +221,10 @@ emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
+emulate_rd16rn12rm0rs8_rwflags_nopc(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 16) & 0xf;
int rn = (insn >> 12) & 0xf;
int rm = insn & 0xf;
@@ -325,7 +242,7 @@ emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdv), [cpsr] "=r" (cpsr)
: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
- "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+ "1" (cpsr), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -334,9 +251,9 @@ emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
+emulate_rd12rm0_noflags_nopc(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 12) & 0xf;
int rm = insn & 0xf;
@@ -346,7 +263,7 @@ emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
__asm__ __volatile__ (
BLX("%[fn]")
: "=r" (rdv)
- : "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
+ : "0" (rdv), "r" (rmv), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -354,9 +271,10 @@ emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
+emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
int rdlo = (insn >> 12) & 0xf;
int rdhi = (insn >> 16) & 0xf;
int rn = insn & 0xf;
@@ -374,7 +292,7 @@ emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
- "2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+ "2" (cpsr), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -383,623 +301,43 @@ emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}
-/*
- * For the instruction masking and comparisons in all the "space_*"
- * functions below, Do _not_ rearrange the order of tests unless
- * you're very, very sure of what you are doing. For the sake of
- * efficiency, the masks for some tests sometimes assume other test
- * have been done prior to them so the number of patterns to test
- * for an instruction set can be as broad as possible to reduce the
- * number of tests needed.
- */
-
-static const union decode_item arm_1111_table[] = {
- /* Unconditional instructions */
-
- /* memory hint 1111 0100 x001 xxxx xxxx xxxx xxxx xxxx */
- /* PLDI (immediate) 1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
- /* PLDW (immediate) 1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
- /* PLD (immediate) 1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
- DECODE_SIMULATE (0xfe300000, 0xf4100000, kprobe_simulate_nop),
-
- /* memory hint 1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
- /* PLDI (register) 1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
- /* PLDW (register) 1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
- /* PLD (register) 1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
- DECODE_SIMULATE (0xfe300010, 0xf6100000, kprobe_simulate_nop),
-
- /* BLX (immediate) 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
- DECODE_SIMULATE (0xfe000000, 0xfa000000, simulate_blx1),
-
- /* CPS 1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */
- /* SETEND 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
- /* SRS 1111 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
- /* RFE 1111 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
-
- /* Coprocessor instructions... */
- /* MCRR2 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx */
- /* MRRC2 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx */
- /* LDC2 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
- /* STC2 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
- /* CDP2 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
- /* MCR2 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
- /* MRC2 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
-
- /* Other unallocated instructions... */
- DECODE_END
-};
-
-static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = {
- /* Miscellaneous instructions */
-
- /* MRS cpsr cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */
- DECODE_SIMULATEX(0x0ff000f0, 0x01000000, simulate_mrs,
- REGS(0, NOPC, 0, 0, 0)),
-
- /* BX cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
- DECODE_SIMULATE (0x0ff000f0, 0x01200010, simulate_blx2bx),
-
- /* BLX (register) cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
- DECODE_SIMULATEX(0x0ff000f0, 0x01200030, simulate_blx2bx,
- REGS(0, 0, 0, 0, NOPC)),
-
- /* CLZ cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */
- DECODE_EMULATEX (0x0ff000f0, 0x01600010, emulate_rd12rm0_noflags_nopc,
- REGS(0, NOPC, 0, 0, NOPC)),
-
- /* QADD cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */
- /* QSUB cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */
- /* QDADD cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */
- /* QDSUB cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */
- DECODE_EMULATEX (0x0f9000f0, 0x01000050, emulate_rd12rn16rm0_rwflags_nopc,
- REGS(NOPC, NOPC, 0, 0, NOPC)),
-
- /* BXJ cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
- /* MSR cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */
- /* MRS spsr cccc 0001 0100 xxxx xxxx xxxx 0000 xxxx */
- /* BKPT 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
- /* SMC cccc 0001 0110 xxxx xxxx xxxx 0111 xxxx */
- /* And unallocated instructions... */
- DECODE_END
-};
-
-static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = {
- /* Halfword multiply and multiply-accumulate */
-
- /* SMLALxy cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */
- DECODE_EMULATEX (0x0ff00090, 0x01400080, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
- REGS(NOPC, NOPC, NOPC, 0, NOPC)),
-
- /* SMULWy cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */
- DECODE_OR (0x0ff000b0, 0x012000a0),
- /* SMULxy cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */
- DECODE_EMULATEX (0x0ff00090, 0x01600080, emulate_rd16rn12rm0rs8_rwflags_nopc,
- REGS(NOPC, 0, NOPC, 0, NOPC)),
-
- /* SMLAxy cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */
- DECODE_OR (0x0ff00090, 0x01000080),
- /* SMLAWy cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */
- DECODE_EMULATEX (0x0ff000b0, 0x01200080, emulate_rd16rn12rm0rs8_rwflags_nopc,
- REGS(NOPC, NOPC, NOPC, 0, NOPC)),
-
- DECODE_END
+const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = {
+ [PROBES_EMULATE_NONE] = {.handler = probes_emulate_none},
+ [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
+ [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
+ [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
+ [PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
+ [PROBES_MRS] = {.handler = simulate_mrs},
+ [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
+ [PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc},
+ [PROBES_SATURATING_ARITHMETIC] = {
+ .handler = emulate_rd12rn16rm0_rwflags_nopc},
+ [PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
+ [PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
+ [PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+ [PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd},
+ [PROBES_LOAD_EXTRA] = {.handler = emulate_ldr},
+ [PROBES_LOAD] = {.handler = emulate_ldr},
+ [PROBES_STORE_EXTRA] = {.handler = emulate_str},
+ [PROBES_STORE] = {.handler = emulate_str},
+ [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
+ [PROBES_DATA_PROCESSING_REG] = {
+ .handler = emulate_rd12rn16rm0rs8_rwflags},
+ [PROBES_DATA_PROCESSING_IMM] = {
+ .handler = emulate_rd12rn16rm0rs8_rwflags},
+ [PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc},
+ [PROBES_SEV] = {.handler = probes_emulate_none},
+ [PROBES_WFE] = {.handler = probes_simulate_nop},
+ [PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+ [PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc},
+ [PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+ [PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+ [PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc},
+ [PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+ [PROBES_MUL_ADD_LONG] = {
+ .handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
+ [PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
+ [PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc},
+ [PROBES_BRANCH] = {.handler = simulate_bbl},
+ [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm}
};
-
-static const union decode_item arm_cccc_0000_____1001_table[] = {
- /* Multiply and multiply-accumulate */
-
- /* MUL cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */
- /* MULS cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */
- DECODE_EMULATEX (0x0fe000f0, 0x00000090, emulate_rd16rn12rm0rs8_rwflags_nopc,
- REGS(NOPC, 0, NOPC, 0, NOPC)),
-
- /* MLA cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */
- /* MLAS cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */
- DECODE_OR (0x0fe000f0, 0x00200090),
- /* MLS cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */
- DECODE_EMULATEX (0x0ff000f0, 0x00600090, emulate_rd16rn12rm0rs8_rwflags_nopc,
- REGS(NOPC, NOPC, NOPC, 0, NOPC)),
-
- /* UMAAL cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */
- DECODE_OR (0x0ff000f0, 0x00400090),
- /* UMULL cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx */
- /* UMULLS cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx */
- /* UMLAL cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx */
- /* UMLALS cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx */
- /* SMULL cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx */
- /* SMULLS cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */
- /* SMLAL cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */
- /* SMLALS cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */
- DECODE_EMULATEX (0x0f8000f0, 0x00800090, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
- REGS(NOPC, NOPC, NOPC, 0, NOPC)),
-
- DECODE_END
-};
-
-static const union decode_item arm_cccc_0001_____1001_table[] = {
- /* Synchronization primitives */
-
-#if __LINUX_ARM_ARCH__ < 6
- /* Deprecated on ARMv6 and may be UNDEFINED on v7 */
- /* SMP/SWPB cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */
- DECODE_EMULATEX (0x0fb000f0, 0x01000090, emulate_rd12rn16rm0_rwflags_nopc,
- REGS(NOPC, NOPC, 0, 0, NOPC)),
-#endif
- /* LDREX/STREX{,D,B,H} cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */
- /* And unallocated instructions... */
- DECODE_END
-};
-
-static const union decode_item arm_cccc_000x_____1xx1_table[] = {
- /* Extra load/store instructions */
-
- /* STRHT cccc 0000 xx10 xxxx xxxx xxxx 1011 xxxx */
- /* ??? cccc 0000 xx10 xxxx xxxx xxxx 11x1 xxxx */
- /* LDRHT cccc 0000 xx11 xxxx xxxx xxxx 1011 xxxx */
- /* LDRSBT cccc 0000 xx11 xxxx xxxx xxxx 1101 xxxx */
- /* LDRSHT cccc 0000 xx11 xxxx xxxx xxxx 1111 xxxx */
- DECODE_REJECT (0x0f200090, 0x00200090),
-
- /* LDRD/STRD lr,pc,{... cccc 000x x0x0 xxxx 111x xxxx 1101 xxxx */
- DECODE_REJECT (0x0e10e0d0, 0x0000e0d0),
-
- /* LDRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */
- /* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */
- DECODE_EMULATEX (0x0e5000d0, 0x000000d0, emulate_ldrdstrd,
- REGS(NOPCWB, NOPCX, 0, 0, NOPC)),
-
- /* LDRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */
- /* STRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */
- DECODE_EMULATEX (0x0e5000d0, 0x004000d0, emulate_ldrdstrd,
- REGS(NOPCWB, NOPCX, 0, 0, 0)),
-
- /* STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */
- DECODE_EMULATEX (0x0e5000f0, 0x000000b0, emulate_str,
- REGS(NOPCWB, NOPC, 0, 0, NOPC)),
-
- /* LDRH (register) cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */
- /* LDRSB (register) cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */
- /* LDRSH (register) cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */
- DECODE_EMULATEX (0x0e500090, 0x00100090, emulate_ldr,
- REGS(NOPCWB, NOPC, 0, 0, NOPC)),
-
- /* STRH (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */
- DECODE_EMULATEX (0x0e5000f0, 0x004000b0, emulate_str,
- REGS(NOPCWB, NOPC, 0, 0, 0)),
-
- /* LDRH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */
- /* LDRSB (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */
- /* LDRSH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */
- DECODE_EMULATEX (0x0e500090, 0x00500090, emulate_ldr,
- REGS(NOPCWB, NOPC, 0, 0, 0)),
-
- DECODE_END
-};
-
-static const union decode_item arm_cccc_000x_table[] = {
- /* Data-processing (register) */
-
- /* <op>S PC, ... cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx */
- DECODE_REJECT (0x0e10f000, 0x0010f000),
-
- /* MOV IP, SP 1110 0001 1010 0000 1100 0000 0000 1101 */
- DECODE_SIMULATE (0xffffffff, 0xe1a0c00d, simulate_mov_ipsp),
-
- /* TST (register) cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */
- /* TEQ (register) cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */
- /* CMP (register) cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */
- /* CMN (register) cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */
- DECODE_EMULATEX (0x0f900010, 0x01100000, emulate_rd12rn16rm0rs8_rwflags,
- REGS(ANY, 0, 0, 0, ANY)),
-
- /* MOV (register) cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */
- /* MVN (register) cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */
- DECODE_EMULATEX (0x0fa00010, 0x01a00000, emulate_rd12rn16rm0rs8_rwflags,
- REGS(0, ANY, 0, 0, ANY)),
-
- /* AND (register) cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */
- /* EOR (register) cccc 0000 001x xxxx xxxx xxxx xxx0 xxxx */
- /* SUB (register) cccc 0000 010x xxxx xxxx xxxx xxx0 xxxx */
- /* RSB (register) cccc 0000 011x xxxx xxxx xxxx xxx0 xxxx */
- /* ADD (register) cccc 0000 100x xxxx xxxx xxxx xxx0 xxxx */
- /* ADC (register) cccc 0000 101x xxxx xxxx xxxx xxx0 xxxx */
- /* SBC (register) cccc 0000 110x xxxx xxxx xxxx xxx0 xxxx */
- /* RSC (register) cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */
- /* ORR (register) cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */
- /* BIC (register) cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */
- DECODE_EMULATEX (0x0e000010, 0x00000000, emulate_rd12rn16rm0rs8_rwflags,
- REGS(ANY, ANY, 0, 0, ANY)),
-
- /* TST (reg-shift reg) cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */
- /* TEQ (reg-shift reg) cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */
- /* CMP (reg-shift reg) cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */
- /* CMN (reg-shift reg) cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */
- DECODE_EMULATEX (0x0f900090, 0x01100010, emulate_rd12rn16rm0rs8_rwflags,
- REGS(ANY, 0, NOPC, 0, ANY)),
-
- /* MOV (reg-shift reg) cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */
- /* MVN (reg-shift reg) cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */
- DECODE_EMULATEX (0x0fa00090, 0x01a00010, emulate_rd12rn16rm0rs8_rwflags,
- REGS(0, ANY, NOPC, 0, ANY)),
-
- /* AND (reg-shift reg) cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */
- /* EOR (reg-shift reg) cccc 0000 001x xxxx xxxx xxxx 0xx1 xxxx */
- /* SUB (reg-shift reg) cccc 0000 010x xxxx xxxx xxxx 0xx1 xxxx */
- /* RSB (reg-shift reg) cccc 0000 011x xxxx xxxx xxxx 0xx1 xxxx */
- /* ADD (reg-shift reg) cccc 0000 100x xxxx xxxx xxxx 0xx1 xxxx */
- /* ADC (reg-shift reg) cccc 0000 101x xxxx xxxx xxxx 0xx1 xxxx */
- /* SBC (reg-shift reg) cccc 0000 110x xxxx xxxx xxxx 0xx1 xxxx */
- /* RSC (reg-shift reg) cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */
- /* ORR (reg-shift reg) cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */
- /* BIC (reg-shift reg) cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */
- DECODE_EMULATEX (0x0e000090, 0x00000010, emulate_rd12rn16rm0rs8_rwflags,
- REGS(ANY, ANY, NOPC, 0, ANY)),
-
- DECODE_END
-};
-
-static const union decode_item arm_cccc_001x_table[] = {
- /* Data-processing (immediate) */
-
- /* MOVW cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */
- /* MOVT cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0x0fb00000, 0x03000000, emulate_rd12rm0_noflags_nopc,
- REGS(0, NOPC, 0, 0, 0)),
-
- /* YIELD cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
- DECODE_OR (0x0fff00ff, 0x03200001),
- /* SEV cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
- DECODE_EMULATE (0x0fff00ff, 0x03200004, kprobe_emulate_none),
- /* NOP cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
- /* WFE cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
- /* WFI cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
- DECODE_SIMULATE (0x0fff00fc, 0x03200000, kprobe_simulate_nop),
- /* DBG cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
- /* unallocated hints cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
- /* MSR (immediate) cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0x0fb00000, 0x03200000),
-
- /* <op>S PC, ... cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx */
- DECODE_REJECT (0x0e10f000, 0x0210f000),
-
- /* TST (immediate) cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx */
- /* TEQ (immediate) cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */
- /* CMP (immediate) cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */
- /* CMN (immediate) cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0x0f900000, 0x03100000, emulate_rd12rn16rm0rs8_rwflags,
- REGS(ANY, 0, 0, 0, 0)),
-
- /* MOV (immediate) cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */
- /* MVN (immediate) cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0x0fa00000, 0x03a00000, emulate_rd12rn16rm0rs8_rwflags,
- REGS(0, ANY, 0, 0, 0)),
-
- /* AND (immediate) cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */
- /* EOR (immediate) cccc 0010 001x xxxx xxxx xxxx xxxx xxxx */
- /* SUB (immediate) cccc 0010 010x xxxx xxxx xxxx xxxx xxxx */
- /* RSB (immediate) cccc 0010 011x xxxx xxxx xxxx xxxx xxxx */
- /* ADD (immediate) cccc 0010 100x xxxx xxxx xxxx xxxx xxxx */
- /* ADC (immediate) cccc 0010 101x xxxx xxxx xxxx xxxx xxxx */
- /* SBC (immediate) cccc 0010 110x xxxx xxxx xxxx xxxx xxxx */
- /* RSC (immediate) cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */
- /* ORR (immediate) cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */
- /* BIC (immediate) cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0x0e000000, 0x02000000, emulate_rd12rn16rm0rs8_rwflags,
- REGS(ANY, ANY, 0, 0, 0)),
-
- DECODE_END
-};
-
-static const union decode_item arm_cccc_0110_____xxx1_table[] = {
- /* Media instructions */
-
- /* SEL cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */
- DECODE_EMULATEX (0x0ff000f0, 0x068000b0, emulate_rd12rn16rm0_rwflags_nopc,
- REGS(NOPC, NOPC, 0, 0, NOPC)),
-
- /* SSAT cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */
- /* USAT cccc 0110 111x xxxx xxxx xxxx xx01 xxxx */
- DECODE_OR(0x0fa00030, 0x06a00010),
- /* SSAT16 cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */
- /* USAT16 cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */
- DECODE_EMULATEX (0x0fb000f0, 0x06a00030, emulate_rd12rn16rm0_rwflags_nopc,
- REGS(0, NOPC, 0, 0, NOPC)),
-
- /* REV cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */
- /* REV16 cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */
- /* RBIT cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */
- /* REVSH cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */
- DECODE_EMULATEX (0x0fb00070, 0x06b00030, emulate_rd12rm0_noflags_nopc,
- REGS(0, NOPC, 0, 0, NOPC)),
-
- /* ??? cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */
- DECODE_REJECT (0x0fb00010, 0x06000010),
- /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1011 xxxx */
- DECODE_REJECT (0x0f8000f0, 0x060000b0),
- /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1101 xxxx */
- DECODE_REJECT (0x0f8000f0, 0x060000d0),
- /* SADD16 cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx */
- /* SADDSUBX cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx */
- /* SSUBADDX cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx */
- /* SSUB16 cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx */
- /* SADD8 cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx */
- /* SSUB8 cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx */
- /* QADD16 cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx */
- /* QADDSUBX cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx */
- /* QSUBADDX cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx */
- /* QSUB16 cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx */
- /* QADD8 cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx */
- /* QSUB8 cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx */
- /* SHADD16 cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx */
- /* SHADDSUBX cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx */
- /* SHSUBADDX cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx */
- /* SHSUB16 cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx */
- /* SHADD8 cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx */
- /* SHSUB8 cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx */
- /* UADD16 cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx */
- /* UADDSUBX cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx */
- /* USUBADDX cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx */
- /* USUB16 cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx */
- /* UADD8 cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx */
- /* USUB8 cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx */
- /* UQADD16 cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx */
- /* UQADDSUBX cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx */
- /* UQSUBADDX cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx */
- /* UQSUB16 cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx */
- /* UQADD8 cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx */
- /* UQSUB8 cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx */
- /* UHADD16 cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx */
- /* UHADDSUBX cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx */
- /* UHSUBADDX cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx */
- /* UHSUB16 cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx */
- /* UHADD8 cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx */
- /* UHSUB8 cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx */
- DECODE_EMULATEX (0x0f800010, 0x06000010, emulate_rd12rn16rm0_rwflags_nopc,
- REGS(NOPC, NOPC, 0, 0, NOPC)),
-
- /* PKHBT cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */
- /* PKHTB cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */
- DECODE_EMULATEX (0x0ff00030, 0x06800010, emulate_rd12rn16rm0_rwflags_nopc,
- REGS(NOPC, NOPC, 0, 0, NOPC)),
-
- /* ??? cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */
- /* ??? cccc 0110 1101 xxxx xxxx xxxx 0111 xxxx */
- DECODE_REJECT (0x0fb000f0, 0x06900070),
-
- /* SXTB16 cccc 0110 1000 1111 xxxx xxxx 0111 xxxx */
- /* SXTB cccc 0110 1010 1111 xxxx xxxx 0111 xxxx */
- /* SXTH cccc 0110 1011 1111 xxxx xxxx 0111 xxxx */
- /* UXTB16 cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */
- /* UXTB cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */
- /* UXTH cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */
- DECODE_EMULATEX (0x0f8f00f0, 0x068f0070, emulate_rd12rm0_noflags_nopc,
- REGS(0, NOPC, 0, 0, NOPC)),
-
- /* SXTAB16 cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx */
- /* SXTAB cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx */
- /* SXTAH cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx */
- /* UXTAB16 cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx */
- /* UXTAB cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx */
- /* UXTAH cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx */
- DECODE_EMULATEX (0x0f8000f0, 0x06800070, emulate_rd12rn16rm0_rwflags_nopc,
- REGS(NOPCX, NOPC, 0, 0, NOPC)),
-
- DECODE_END
-};
-
-static const union decode_item arm_cccc_0111_____xxx1_table[] = {
- /* Media instructions */
-
- /* UNDEFINED cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */
- DECODE_REJECT (0x0ff000f0, 0x07f000f0),
-
- /* SMLALD cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */
- /* SMLSLD cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */
- DECODE_EMULATEX (0x0ff00090, 0x07400010, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
- REGS(NOPC, NOPC, NOPC, 0, NOPC)),
-
- /* SMUAD cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */
- /* SMUSD cccc 0111 0000 xxxx 1111 xxxx 01x1 xxxx */
- DECODE_OR (0x0ff0f090, 0x0700f010),
- /* SMMUL cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */
- DECODE_OR (0x0ff0f0d0, 0x0750f010),
- /* USAD8 cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */
- DECODE_EMULATEX (0x0ff0f0f0, 0x0780f010, emulate_rd16rn12rm0rs8_rwflags_nopc,
- REGS(NOPC, 0, NOPC, 0, NOPC)),
-
- /* SMLAD cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */
- /* SMLSD cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx */
- DECODE_OR (0x0ff00090, 0x07000010),
- /* SMMLA cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */
- DECODE_OR (0x0ff000d0, 0x07500010),
- /* USADA8 cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */
- DECODE_EMULATEX (0x0ff000f0, 0x07800010, emulate_rd16rn12rm0rs8_rwflags_nopc,
- REGS(NOPC, NOPCX, NOPC, 0, NOPC)),
-
- /* SMMLS cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */
- DECODE_EMULATEX (0x0ff000d0, 0x075000d0, emulate_rd16rn12rm0rs8_rwflags_nopc,
- REGS(NOPC, NOPC, NOPC, 0, NOPC)),
-
- /* SBFX cccc 0111 101x xxxx xxxx xxxx x101 xxxx */
- /* UBFX cccc 0111 111x xxxx xxxx xxxx x101 xxxx */
- DECODE_EMULATEX (0x0fa00070, 0x07a00050, emulate_rd12rm0_noflags_nopc,
- REGS(0, NOPC, 0, 0, NOPC)),
-
- /* BFC cccc 0111 110x xxxx xxxx xxxx x001 1111 */
- DECODE_EMULATEX (0x0fe0007f, 0x07c0001f, emulate_rd12rm0_noflags_nopc,
- REGS(0, NOPC, 0, 0, 0)),
-
- /* BFI cccc 0111 110x xxxx xxxx xxxx x001 xxxx */
- DECODE_EMULATEX (0x0fe00070, 0x07c00010, emulate_rd12rm0_noflags_nopc,
- REGS(0, NOPC, 0, 0, NOPCX)),
-
- DECODE_END
-};
-
-static const union decode_item arm_cccc_01xx_table[] = {
- /* Load/store word and unsigned byte */
-
- /* LDRB/STRB pc,[...] cccc 01xx x0xx xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0x0c40f000, 0x0440f000),
-
- /* STRT cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx */
- /* LDRT cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx */
- /* STRBT cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx */
- /* LDRBT cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0x0d200000, 0x04200000),
-
- /* STR (immediate) cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */
- /* STRB (immediate) cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0x0e100000, 0x04000000, emulate_str,
- REGS(NOPCWB, ANY, 0, 0, 0)),
-
- /* LDR (immediate) cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */
- /* LDRB (immediate) cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0x0e100000, 0x04100000, emulate_ldr,
- REGS(NOPCWB, ANY, 0, 0, 0)),
-
- /* STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */
- /* STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0x0e100000, 0x06000000, emulate_str,
- REGS(NOPCWB, ANY, 0, 0, NOPC)),
-
- /* LDR (register) cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */
- /* LDRB (register) cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0x0e100000, 0x06100000, emulate_ldr,
- REGS(NOPCWB, ANY, 0, 0, NOPC)),
-
- DECODE_END
-};
-
-static const union decode_item arm_cccc_100x_table[] = {
- /* Block data transfer instructions */
-
- /* LDM cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
- /* STM cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */
- DECODE_CUSTOM (0x0e400000, 0x08000000, kprobe_decode_ldmstm),
-
- /* STM (user registers) cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
- /* LDM (user registers) cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */
- /* LDM (exception ret) cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx */
- DECODE_END
-};
-
-const union decode_item kprobe_decode_arm_table[] = {
- /*
- * Unconditional instructions
- * 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0xf0000000, 0xf0000000, arm_1111_table),
-
- /*
- * Miscellaneous instructions
- * cccc 0001 0xx0 xxxx xxxx xxxx 0xxx xxxx
- */
- DECODE_TABLE (0x0f900080, 0x01000000, arm_cccc_0001_0xx0____0xxx_table),
-
- /*
- * Halfword multiply and multiply-accumulate
- * cccc 0001 0xx0 xxxx xxxx xxxx 1xx0 xxxx
- */
- DECODE_TABLE (0x0f900090, 0x01000080, arm_cccc_0001_0xx0____1xx0_table),
-
- /*
- * Multiply and multiply-accumulate
- * cccc 0000 xxxx xxxx xxxx xxxx 1001 xxxx
- */
- DECODE_TABLE (0x0f0000f0, 0x00000090, arm_cccc_0000_____1001_table),
-
- /*
- * Synchronization primitives
- * cccc 0001 xxxx xxxx xxxx xxxx 1001 xxxx
- */
- DECODE_TABLE (0x0f0000f0, 0x01000090, arm_cccc_0001_____1001_table),
-
- /*
- * Extra load/store instructions
- * cccc 000x xxxx xxxx xxxx xxxx 1xx1 xxxx
- */
- DECODE_TABLE (0x0e000090, 0x00000090, arm_cccc_000x_____1xx1_table),
-
- /*
- * Data-processing (register)
- * cccc 000x xxxx xxxx xxxx xxxx xxx0 xxxx
- * Data-processing (register-shifted register)
- * cccc 000x xxxx xxxx xxxx xxxx 0xx1 xxxx
- */
- DECODE_TABLE (0x0e000000, 0x00000000, arm_cccc_000x_table),
-
- /*
- * Data-processing (immediate)
- * cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0x0e000000, 0x02000000, arm_cccc_001x_table),
-
- /*
- * Media instructions
- * cccc 011x xxxx xxxx xxxx xxxx xxx1 xxxx
- */
- DECODE_TABLE (0x0f000010, 0x06000010, arm_cccc_0110_____xxx1_table),
- DECODE_TABLE (0x0f000010, 0x07000010, arm_cccc_0111_____xxx1_table),
-
- /*
- * Load/store word and unsigned byte
- * cccc 01xx xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0x0c000000, 0x04000000, arm_cccc_01xx_table),
-
- /*
- * Block data transfer instructions
- * cccc 100x xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0x0e000000, 0x08000000, arm_cccc_100x_table),
-
- /* B cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */
- /* BL cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */
- DECODE_SIMULATE (0x0e000000, 0x0a000000, simulate_bbl),
-
- /*
- * Supervisor Call, and coprocessor instructions
- */
-
- /* MCRR cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx */
- /* MRRC cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx */
- /* LDC cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
- /* STC cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
- /* CDP cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
- /* MCR cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
- /* MRC cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
- /* SVC cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0x0c000000, 0x0c000000),
-
- DECODE_END
-};
-#ifdef CONFIG_ARM_KPROBES_TEST_MODULE
-EXPORT_SYMBOL_GPL(kprobe_decode_arm_table);
-#endif
-
-static void __kprobes arm_singlestep(struct kprobe *p, struct pt_regs *regs)
-{
- regs->ARM_pc += 4;
- p->ainsn.insn_handler(p, regs);
-}
-
-/* Return:
- * INSN_REJECTED If instruction is one not allowed to kprobe,
- * INSN_GOOD If instruction is supported and uses instruction slot,
- * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
- *
- * For instructions we don't want to kprobe (INSN_REJECTED return result):
- * These are generally ones that modify the processor state making
- * them "hard" to simulate such as switches processor modes or
- * make accesses in alternate modes. Any of these could be simulated
- * if the work was put into it, but low return considering they
- * should also be very rare.
- */
-enum kprobe_insn __kprobes
-arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
-{
- asi->insn_singlestep = arm_singlestep;
- asi->insn_check_cc = kprobe_condition_checks[insn>>28];
- return kprobe_decode_insn(insn, asi, kprobe_decode_arm_table, false);
-}
diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
index 18a76282970e..c311ed94ff1c 100644
--- a/arch/arm/kernel/kprobes-common.c
+++ b/arch/arm/kernel/kprobes-common.c
@@ -13,178 +13,14 @@
#include <linux/kernel.h>
#include <linux/kprobes.h>
-#include <asm/system_info.h>
#include "kprobes.h"
-#ifndef find_str_pc_offset
-
-/*
- * For STR and STM instructions, an ARM core may choose to use either
- * a +8 or a +12 displacement from the current instruction's address.
- * Whichever value is chosen for a given core, it must be the same for
- * both instructions and may not change. This function measures it.
- */
-
-int str_pc_offset;
-
-void __init find_str_pc_offset(void)
-{
- int addr, scratch, ret;
-
- __asm__ (
- "sub %[ret], pc, #4 \n\t"
- "str pc, %[addr] \n\t"
- "ldr %[scr], %[addr] \n\t"
- "sub %[ret], %[scr], %[ret] \n\t"
- : [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr));
-
- str_pc_offset = ret;
-}
-
-#endif /* !find_str_pc_offset */
-
-
-#ifndef test_load_write_pc_interworking
-
-bool load_write_pc_interworks;
-
-void __init test_load_write_pc_interworking(void)
-{
- int arch = cpu_architecture();
- BUG_ON(arch == CPU_ARCH_UNKNOWN);
- load_write_pc_interworks = arch >= CPU_ARCH_ARMv5T;
-}
-
-#endif /* !test_load_write_pc_interworking */
-
-
-#ifndef test_alu_write_pc_interworking
-
-bool alu_write_pc_interworks;
-
-void __init test_alu_write_pc_interworking(void)
-{
- int arch = cpu_architecture();
- BUG_ON(arch == CPU_ARCH_UNKNOWN);
- alu_write_pc_interworks = arch >= CPU_ARCH_ARMv7;
-}
-
-#endif /* !test_alu_write_pc_interworking */
-
-
-void __init arm_kprobe_decode_init(void)
-{
- find_str_pc_offset();
- test_load_write_pc_interworking();
- test_alu_write_pc_interworking();
-}
-
-
-static unsigned long __kprobes __check_eq(unsigned long cpsr)
-{
- return cpsr & PSR_Z_BIT;
-}
-
-static unsigned long __kprobes __check_ne(unsigned long cpsr)
-{
- return (~cpsr) & PSR_Z_BIT;
-}
-
-static unsigned long __kprobes __check_cs(unsigned long cpsr)
-{
- return cpsr & PSR_C_BIT;
-}
-
-static unsigned long __kprobes __check_cc(unsigned long cpsr)
-{
- return (~cpsr) & PSR_C_BIT;
-}
-
-static unsigned long __kprobes __check_mi(unsigned long cpsr)
-{
- return cpsr & PSR_N_BIT;
-}
-
-static unsigned long __kprobes __check_pl(unsigned long cpsr)
-{
- return (~cpsr) & PSR_N_BIT;
-}
-
-static unsigned long __kprobes __check_vs(unsigned long cpsr)
-{
- return cpsr & PSR_V_BIT;
-}
-
-static unsigned long __kprobes __check_vc(unsigned long cpsr)
-{
- return (~cpsr) & PSR_V_BIT;
-}
-
-static unsigned long __kprobes __check_hi(unsigned long cpsr)
-{
- cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
- return cpsr & PSR_C_BIT;
-}
-
-static unsigned long __kprobes __check_ls(unsigned long cpsr)
-{
- cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
- return (~cpsr) & PSR_C_BIT;
-}
-
-static unsigned long __kprobes __check_ge(unsigned long cpsr)
-{
- cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
- return (~cpsr) & PSR_N_BIT;
-}
-
-static unsigned long __kprobes __check_lt(unsigned long cpsr)
-{
- cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
- return cpsr & PSR_N_BIT;
-}
-
-static unsigned long __kprobes __check_gt(unsigned long cpsr)
-{
- unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
- temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */
- return (~temp) & PSR_N_BIT;
-}
-
-static unsigned long __kprobes __check_le(unsigned long cpsr)
-{
- unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
- temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */
- return temp & PSR_N_BIT;
-}
-
-static unsigned long __kprobes __check_al(unsigned long cpsr)
-{
- return true;
-}
-
-kprobe_check_cc * const kprobe_condition_checks[16] = {
- &__check_eq, &__check_ne, &__check_cs, &__check_cc,
- &__check_mi, &__check_pl, &__check_vs, &__check_vc,
- &__check_hi, &__check_ls, &__check_ge, &__check_lt,
- &__check_gt, &__check_le, &__check_al, &__check_al
-};
-
-
-void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs)
-{
-}
-
-void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs)
-{
- p->ainsn.insn_fn();
-}
-
-static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
+static void __kprobes simulate_ldm1stm1(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
int rn = (insn >> 16) & 0xf;
int lbit = insn & (1 << 20);
int wbit = insn & (1 << 21);
@@ -223,24 +59,31 @@ static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
}
}
-static void __kprobes simulate_stm1_pc(struct kprobe *p, struct pt_regs *regs)
+static void __kprobes simulate_stm1_pc(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
{
- regs->ARM_pc = (long)p->addr + str_pc_offset;
- simulate_ldm1stm1(p, regs);
- regs->ARM_pc = (long)p->addr + 4;
+ unsigned long addr = regs->ARM_pc - 4;
+
+ regs->ARM_pc = (long)addr + str_pc_offset;
+ simulate_ldm1stm1(insn, asi, regs);
+ regs->ARM_pc = (long)addr + 4;
}
-static void __kprobes simulate_ldm1_pc(struct kprobe *p, struct pt_regs *regs)
+static void __kprobes simulate_ldm1_pc(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
{
- simulate_ldm1stm1(p, regs);
+ simulate_ldm1stm1(insn, asi, regs);
load_write_pc(regs->ARM_pc, regs);
}
static void __kprobes
-emulate_generic_r0_12_noflags(struct kprobe *p, struct pt_regs *regs)
+emulate_generic_r0_12_noflags(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
register void *rregs asm("r1") = regs;
- register void *rfn asm("lr") = p->ainsn.insn_fn;
+ register void *rfn asm("lr") = asi->insn_fn;
__asm__ __volatile__ (
"stmdb sp!, {%[regs], r11} \n\t"
@@ -264,22 +107,27 @@ emulate_generic_r0_12_noflags(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-emulate_generic_r2_14_noflags(struct kprobe *p, struct pt_regs *regs)
+emulate_generic_r2_14_noflags(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- emulate_generic_r0_12_noflags(p, (struct pt_regs *)(regs->uregs+2));
+ emulate_generic_r0_12_noflags(insn, asi,
+ (struct pt_regs *)(regs->uregs+2));
}
static void __kprobes
-emulate_ldm_r3_15(struct kprobe *p, struct pt_regs *regs)
+emulate_ldm_r3_15(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- emulate_generic_r0_12_noflags(p, (struct pt_regs *)(regs->uregs+3));
+ emulate_generic_r0_12_noflags(insn, asi,
+ (struct pt_regs *)(regs->uregs+3));
load_write_pc(regs->ARM_pc, regs);
}
-enum kprobe_insn __kprobes
-kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+enum probes_insn __kprobes
+kprobe_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *h)
{
- kprobe_insn_handler_t *handler = 0;
+ probes_insn_handler_t *handler = 0;
unsigned reglist = insn & 0xffff;
int is_ldm = insn & 0x100000;
int rn = (insn >> 16) & 0xf;
@@ -319,260 +167,3 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
return INSN_GOOD_NO_SLOT;
}
-
-/*
- * Prepare an instruction slot to receive an instruction for emulating.
- * This is done by placing a subroutine return after the location where the
- * instruction will be placed. We also modify ARM instructions to be
- * unconditional as the condition code will already be checked before any
- * emulation handler is called.
- */
-static kprobe_opcode_t __kprobes
-prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
- bool thumb)
-{
-#ifdef CONFIG_THUMB2_KERNEL
- if (thumb) {
- u16 *thumb_insn = (u16 *)asi->insn;
- thumb_insn[1] = 0x4770; /* Thumb bx lr */
- thumb_insn[2] = 0x4770; /* Thumb bx lr */
- return insn;
- }
- asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
-#else
- asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
-#endif
- /* Make an ARM instruction unconditional */
- if (insn < 0xe0000000)
- insn = (insn | 0xe0000000) & ~0x10000000;
- return insn;
-}
-
-/*
- * Write a (probably modified) instruction into the slot previously prepared by
- * prepare_emulated_insn
- */
-static void __kprobes
-set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
- bool thumb)
-{
-#ifdef CONFIG_THUMB2_KERNEL
- if (thumb) {
- u16 *ip = (u16 *)asi->insn;
- if (is_wide_instruction(insn))
- *ip++ = insn >> 16;
- *ip++ = insn;
- return;
- }
-#endif
- asi->insn[0] = insn;
-}
-
-/*
- * When we modify the register numbers encoded in an instruction to be emulated,
- * the new values come from this define. For ARM and 32-bit Thumb instructions
- * this gives...
- *
- * bit position 16 12 8 4 0
- * ---------------+---+---+---+---+---+
- * register r2 r0 r1 -- r3
- */
-#define INSN_NEW_BITS 0x00020103
-
-/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
-#define INSN_SAMEAS16_BITS 0x22222222
-
-/*
- * Validate and modify each of the registers encoded in an instruction.
- *
- * Each nibble in regs contains a value from enum decode_reg_type. For each
- * non-zero value, the corresponding nibble in pinsn is validated and modified
- * according to the type.
- */
-static bool __kprobes decode_regs(kprobe_opcode_t* pinsn, u32 regs)
-{
- kprobe_opcode_t insn = *pinsn;
- kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
-
- for (; regs != 0; regs >>= 4, mask <<= 4) {
-
- kprobe_opcode_t new_bits = INSN_NEW_BITS;
-
- switch (regs & 0xf) {
-
- case REG_TYPE_NONE:
- /* Nibble not a register, skip to next */
- continue;
-
- case REG_TYPE_ANY:
- /* Any register is allowed */
- break;
-
- case REG_TYPE_SAMEAS16:
- /* Replace register with same as at bit position 16 */
- new_bits = INSN_SAMEAS16_BITS;
- break;
-
- case REG_TYPE_SP:
- /* Only allow SP (R13) */
- if ((insn ^ 0xdddddddd) & mask)
- goto reject;
- break;
-
- case REG_TYPE_PC:
- /* Only allow PC (R15) */
- if ((insn ^ 0xffffffff) & mask)
- goto reject;
- break;
-
- case REG_TYPE_NOSP:
- /* Reject SP (R13) */
- if (((insn ^ 0xdddddddd) & mask) == 0)
- goto reject;
- break;
-
- case REG_TYPE_NOSPPC:
- case REG_TYPE_NOSPPCX:
- /* Reject SP and PC (R13 and R15) */
- if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
- goto reject;
- break;
-
- case REG_TYPE_NOPCWB:
- if (!is_writeback(insn))
- break; /* No writeback, so any register is OK */
- /* fall through... */
- case REG_TYPE_NOPC:
- case REG_TYPE_NOPCX:
- /* Reject PC (R15) */
- if (((insn ^ 0xffffffff) & mask) == 0)
- goto reject;
- break;
- }
-
- /* Replace value of nibble with new register number... */
- insn &= ~mask;
- insn |= new_bits & mask;
- }
-
- *pinsn = insn;
- return true;
-
-reject:
- return false;
-}
-
-static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
- [DECODE_TYPE_TABLE] = sizeof(struct decode_table),
- [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom),
- [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate),
- [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate),
- [DECODE_TYPE_OR] = sizeof(struct decode_or),
- [DECODE_TYPE_REJECT] = sizeof(struct decode_reject)
-};
-
-/*
- * kprobe_decode_insn operates on data tables in order to decode an ARM
- * architecture instruction onto which a kprobe has been placed.
- *
- * These instruction decoding tables are a concatenation of entries each
- * of which consist of one of the following structs:
- *
- * decode_table
- * decode_custom
- * decode_simulate
- * decode_emulate
- * decode_or
- * decode_reject
- *
- * Each of these starts with a struct decode_header which has the following
- * fields:
- *
- * type_regs
- * mask
- * value
- *
- * The least significant DECODE_TYPE_BITS of type_regs contains a value
- * from enum decode_type, this indicates which of the decode_* structs
- * the entry contains. The value DECODE_TYPE_END indicates the end of the
- * table.
- *
- * When the table is parsed, each entry is checked in turn to see if it
- * matches the instruction to be decoded using the test:
- *
- * (insn & mask) == value
- *
- * If no match is found before the end of the table is reached then decoding
- * fails with INSN_REJECTED.
- *
- * When a match is found, decode_regs() is called to validate and modify each
- * of the registers encoded in the instruction; the data it uses to do this
- * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
- * to fail with INSN_REJECTED.
- *
- * Once the instruction has passed the above tests, further processing
- * depends on the type of the table entry's decode struct.
- *
- */
-int __kprobes
-kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
- const union decode_item *table, bool thumb)
-{
- const struct decode_header *h = (struct decode_header *)table;
- const struct decode_header *next;
- bool matched = false;
-
- insn = prepare_emulated_insn(insn, asi, thumb);
-
- for (;; h = next) {
- enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
- u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
-
- if (type == DECODE_TYPE_END)
- return INSN_REJECTED;
-
- next = (struct decode_header *)
- ((uintptr_t)h + decode_struct_sizes[type]);
-
- if (!matched && (insn & h->mask.bits) != h->value.bits)
- continue;
-
- if (!decode_regs(&insn, regs))
- return INSN_REJECTED;
-
- switch (type) {
-
- case DECODE_TYPE_TABLE: {
- struct decode_table *d = (struct decode_table *)h;
- next = (struct decode_header *)d->table.table;
- break;
- }
-
- case DECODE_TYPE_CUSTOM: {
- struct decode_custom *d = (struct decode_custom *)h;
- return (*d->decoder.decoder)(insn, asi);
- }
-
- case DECODE_TYPE_SIMULATE: {
- struct decode_simulate *d = (struct decode_simulate *)h;
- asi->insn_handler = d->handler.handler;
- return INSN_GOOD_NO_SLOT;
- }
-
- case DECODE_TYPE_EMULATE: {
- struct decode_emulate *d = (struct decode_emulate *)h;
- asi->insn_handler = d->handler.handler;
- set_emulated_insn(insn, asi, thumb);
- return INSN_GOOD;
- }
-
- case DECODE_TYPE_OR:
- matched = true;
- break;
-
- case DECODE_TYPE_REJECT:
- default:
- return INSN_REJECTED;
- }
- }
- }
diff --git a/arch/arm/kernel/kprobes-test-arm.c b/arch/arm/kernel/kprobes-test-arm.c
index 839312905067..87839de77e5f 100644
--- a/arch/arm/kernel/kprobes-test-arm.c
+++ b/arch/arm/kernel/kprobes-test-arm.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <asm/system_info.h>
#include "kprobes-test.h"
diff --git a/arch/arm/kernel/kprobes-test.c b/arch/arm/kernel/kprobes-test.c
index 0cd63d080c7b..c2fd06b4c389 100644
--- a/arch/arm/kernel/kprobes-test.c
+++ b/arch/arm/kernel/kprobes-test.c
@@ -201,10 +201,14 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kprobes.h>
-
+#include <linux/errno.h>
+#include <linux/stddef.h>
+#include <linux/bug.h>
#include <asm/opcodes.h>
#include "kprobes.h"
+#include "probes-arm.h"
+#include "probes-thumb.h"
#include "kprobes-test.h"
@@ -1608,7 +1612,7 @@ static int __init run_all_tests(void)
goto out;
pr_info("ARM instruction simulation\n");
- ret = run_test_cases(kprobe_arm_test_cases, kprobe_decode_arm_table);
+ ret = run_test_cases(kprobe_arm_test_cases, probes_decode_arm_table);
if (ret)
goto out;
@@ -1631,13 +1635,13 @@ static int __init run_all_tests(void)
pr_info("16-bit Thumb instruction simulation\n");
ret = run_test_cases(kprobe_thumb16_test_cases,
- kprobe_decode_thumb16_table);
+ probes_decode_thumb16_table);
if (ret)
goto out;
pr_info("32-bit Thumb instruction simulation\n");
ret = run_test_cases(kprobe_thumb32_test_cases,
- kprobe_decode_thumb32_table);
+ probes_decode_thumb32_table);
if (ret)
goto out;
#endif
diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
index 6123daf397a7..6619188619ae 100644
--- a/arch/arm/kernel/kprobes-thumb.c
+++ b/arch/arm/kernel/kprobes-thumb.c
@@ -8,41 +8,25 @@
* published by the Free Software Foundation.
*/
+#include <linux/types.h>
#include <linux/kernel.h>
+#include <linux/ptrace.h>
#include <linux/kprobes.h>
-#include <linux/module.h>
#include "kprobes.h"
+#include "probes-thumb.h"
+/* These emulation encodings are functionally equivalent... */
+#define t32_emulate_rd8rn16rm0ra12_noflags \
+ t32_emulate_rdlo12rdhi8rn16rm0_noflags
-/*
- * True if current instruction is in an IT block.
- */
-#define in_it_block(cpsr) ((cpsr & 0x06000c00) != 0x00000000)
-
-/*
- * Return the condition code to check for the currently executing instruction.
- * This is in ITSTATE<7:4> which is in CPSR<15:12> but is only valid if
- * in_it_block returns true.
- */
-#define current_cond(cpsr) ((cpsr >> 12) & 0xf)
-
-/*
- * Return the PC value for a probe in thumb code.
- * This is the address of the probed instruction plus 4.
- * We subtract one because the address will have bit zero set to indicate
- * a pointer to thumb code.
- */
-static inline unsigned long __kprobes thumb_probe_pc(struct kprobe *p)
-{
- return (unsigned long)p->addr - 1 + 4;
-}
+/* t32 thumb actions */
static void __kprobes
-t32_simulate_table_branch(struct kprobe *p, struct pt_regs *regs)
+t32_simulate_table_branch(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = thumb_probe_pc(p);
+ unsigned long pc = regs->ARM_pc;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
@@ -59,19 +43,19 @@ t32_simulate_table_branch(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-t32_simulate_mrs(struct kprobe *p, struct pt_regs *regs)
+t32_simulate_mrs(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 8) & 0xf;
unsigned long mask = 0xf8ff03df; /* Mask out execution state */
regs->uregs[rd] = regs->ARM_cpsr & mask;
}
static void __kprobes
-t32_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
+t32_simulate_cond_branch(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = thumb_probe_pc(p);
+ unsigned long pc = regs->ARM_pc;
long offset = insn & 0x7ff; /* imm11 */
offset += (insn & 0x003f0000) >> 5; /* imm6 */
@@ -82,20 +66,21 @@ t32_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
regs->ARM_pc = pc + (offset * 2);
}
-static enum kprobe_insn __kprobes
-t32_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+static enum probes_insn __kprobes
+t32_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d)
{
int cc = (insn >> 22) & 0xf;
- asi->insn_check_cc = kprobe_condition_checks[cc];
+ asi->insn_check_cc = probes_condition_checks[cc];
asi->insn_handler = t32_simulate_cond_branch;
return INSN_GOOD_NO_SLOT;
}
static void __kprobes
-t32_simulate_branch(struct kprobe *p, struct pt_regs *regs)
+t32_simulate_branch(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = thumb_probe_pc(p);
+ unsigned long pc = regs->ARM_pc;
long offset = insn & 0x7ff; /* imm11 */
offset += (insn & 0x03ff0000) >> 5; /* imm10 */
@@ -108,7 +93,7 @@ t32_simulate_branch(struct kprobe *p, struct pt_regs *regs)
if (insn & (1 << 14)) {
/* BL or BLX */
- regs->ARM_lr = (unsigned long)p->addr + 4;
+ regs->ARM_lr = regs->ARM_pc | 1;
if (!(insn & (1 << 12))) {
/* BLX so switch to ARM mode */
regs->ARM_cpsr &= ~PSR_T_BIT;
@@ -120,10 +105,10 @@ t32_simulate_branch(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-t32_simulate_ldr_literal(struct kprobe *p, struct pt_regs *regs)
+t32_simulate_ldr_literal(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long addr = thumb_probe_pc(p) & ~3;
+ unsigned long addr = regs->ARM_pc & ~3;
int rt = (insn >> 12) & 0xf;
unsigned long rtv;
@@ -157,10 +142,11 @@ t32_simulate_ldr_literal(struct kprobe *p, struct pt_regs *regs)
regs->uregs[rt] = rtv;
}
-static enum kprobe_insn __kprobes
-t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+static enum probes_insn __kprobes
+t32_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d)
{
- enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi);
+ enum probes_insn ret = kprobe_decode_ldmstm(insn, asi, d);
/* Fixup modified instruction to have halfwords in correct order...*/
insn = asi->insn[0];
@@ -171,10 +157,10 @@ t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
}
static void __kprobes
-t32_emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
+t32_emulate_ldrdstrd(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = thumb_probe_pc(p) & ~3;
+ unsigned long pc = regs->ARM_pc & ~3;
int rt1 = (insn >> 12) & 0xf;
int rt2 = (insn >> 8) & 0xf;
int rn = (insn >> 16) & 0xf;
@@ -187,7 +173,7 @@ t32_emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
__asm__ __volatile__ (
"blx %[fn]"
: "=r" (rt1v), "=r" (rt2v), "=r" (rnv)
- : "0" (rt1v), "1" (rt2v), "2" (rnv), [fn] "r" (p->ainsn.insn_fn)
+ : "0" (rt1v), "1" (rt2v), "2" (rnv), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -198,9 +184,9 @@ t32_emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-t32_emulate_ldrstr(struct kprobe *p, struct pt_regs *regs)
+t32_emulate_ldrstr(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
int rt = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
@@ -212,7 +198,7 @@ t32_emulate_ldrstr(struct kprobe *p, struct pt_regs *regs)
__asm__ __volatile__ (
"blx %[fn]"
: "=r" (rtv), "=r" (rnv)
- : "0" (rtv), "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
+ : "0" (rtv), "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -224,9 +210,9 @@ t32_emulate_ldrstr(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-t32_emulate_rd8rn16rm0_rwflags(struct kprobe *p, struct pt_regs *regs)
+t32_emulate_rd8rn16rm0_rwflags(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 8) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
@@ -242,7 +228,7 @@ t32_emulate_rd8rn16rm0_rwflags(struct kprobe *p, struct pt_regs *regs)
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdv), [cpsr] "=r" (cpsr)
: "0" (rdv), "r" (rnv), "r" (rmv),
- "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+ "1" (cpsr), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -251,10 +237,10 @@ t32_emulate_rd8rn16rm0_rwflags(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-t32_emulate_rd8pc16_noflags(struct kprobe *p, struct pt_regs *regs)
+t32_emulate_rd8pc16_noflags(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = thumb_probe_pc(p);
+ unsigned long pc = regs->ARM_pc;
int rd = (insn >> 8) & 0xf;
register unsigned long rdv asm("r1") = regs->uregs[rd];
@@ -263,7 +249,7 @@ t32_emulate_rd8pc16_noflags(struct kprobe *p, struct pt_regs *regs)
__asm__ __volatile__ (
"blx %[fn]"
: "=r" (rdv)
- : "0" (rdv), "r" (rnv), [fn] "r" (p->ainsn.insn_fn)
+ : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -271,9 +257,9 @@ t32_emulate_rd8pc16_noflags(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-t32_emulate_rd8rn16_noflags(struct kprobe *p, struct pt_regs *regs)
+t32_emulate_rd8rn16_noflags(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 8) & 0xf;
int rn = (insn >> 16) & 0xf;
@@ -283,7 +269,7 @@ t32_emulate_rd8rn16_noflags(struct kprobe *p, struct pt_regs *regs)
__asm__ __volatile__ (
"blx %[fn]"
: "=r" (rdv)
- : "0" (rdv), "r" (rnv), [fn] "r" (p->ainsn.insn_fn)
+ : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -291,9 +277,10 @@ t32_emulate_rd8rn16_noflags(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-t32_emulate_rdlo12rdhi8rn16rm0_noflags(struct kprobe *p, struct pt_regs *regs)
+t32_emulate_rdlo12rdhi8rn16rm0_noflags(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
int rdlo = (insn >> 12) & 0xf;
int rdhi = (insn >> 8) & 0xf;
int rn = (insn >> 16) & 0xf;
@@ -308,674 +295,43 @@ t32_emulate_rdlo12rdhi8rn16rm0_noflags(struct kprobe *p, struct pt_regs *regs)
"blx %[fn]"
: "=r" (rdlov), "=r" (rdhiv)
: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
- [fn] "r" (p->ainsn.insn_fn)
+ [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
regs->uregs[rdlo] = rdlov;
regs->uregs[rdhi] = rdhiv;
}
-
-/* These emulation encodings are functionally equivalent... */
-#define t32_emulate_rd8rn16rm0ra12_noflags \
- t32_emulate_rdlo12rdhi8rn16rm0_noflags
-
-static const union decode_item t32_table_1110_100x_x0xx[] = {
- /* Load/store multiple instructions */
-
- /* Rn is PC 1110 100x x0xx 1111 xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0xfe4f0000, 0xe80f0000),
-
- /* SRS 1110 1000 00x0 xxxx xxxx xxxx xxxx xxxx */
- /* RFE 1110 1000 00x1 xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0xffc00000, 0xe8000000),
- /* SRS 1110 1001 10x0 xxxx xxxx xxxx xxxx xxxx */
- /* RFE 1110 1001 10x1 xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0xffc00000, 0xe9800000),
-
- /* STM Rn, {...pc} 1110 100x x0x0 xxxx 1xxx xxxx xxxx xxxx */
- DECODE_REJECT (0xfe508000, 0xe8008000),
- /* LDM Rn, {...lr,pc} 1110 100x x0x1 xxxx 11xx xxxx xxxx xxxx */
- DECODE_REJECT (0xfe50c000, 0xe810c000),
- /* LDM/STM Rn, {...sp} 1110 100x x0xx xxxx xx1x xxxx xxxx xxxx */
- DECODE_REJECT (0xfe402000, 0xe8002000),
-
- /* STMIA 1110 1000 10x0 xxxx xxxx xxxx xxxx xxxx */
- /* LDMIA 1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */
- /* STMDB 1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */
- /* LDMDB 1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */
- DECODE_CUSTOM (0xfe400000, 0xe8000000, t32_decode_ldmstm),
-
- DECODE_END
-};
-
-static const union decode_item t32_table_1110_100x_x1xx[] = {
- /* Load/store dual, load/store exclusive, table branch */
-
- /* STRD (immediate) 1110 1000 x110 xxxx xxxx xxxx xxxx xxxx */
- /* LDRD (immediate) 1110 1000 x111 xxxx xxxx xxxx xxxx xxxx */
- DECODE_OR (0xff600000, 0xe8600000),
- /* STRD (immediate) 1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */
- /* LDRD (immediate) 1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xff400000, 0xe9400000, t32_emulate_ldrdstrd,
- REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)),
-
- /* TBB 1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */
- /* TBH 1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */
- DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, t32_simulate_table_branch,
- REGS(NOSP, 0, 0, 0, NOSPPC)),
-
- /* STREX 1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */
- /* LDREX 1110 1000 0101 xxxx xxxx xxxx xxxx xxxx */
- /* STREXB 1110 1000 1100 xxxx xxxx xxxx 0100 xxxx */
- /* STREXH 1110 1000 1100 xxxx xxxx xxxx 0101 xxxx */
- /* STREXD 1110 1000 1100 xxxx xxxx xxxx 0111 xxxx */
- /* LDREXB 1110 1000 1101 xxxx xxxx xxxx 0100 xxxx */
- /* LDREXH 1110 1000 1101 xxxx xxxx xxxx 0101 xxxx */
- /* LDREXD 1110 1000 1101 xxxx xxxx xxxx 0111 xxxx */
- /* And unallocated instructions... */
- DECODE_END
-};
-
-static const union decode_item t32_table_1110_101x[] = {
- /* Data-processing (shifted register) */
-
- /* TST 1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */
- /* TEQ 1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */
- DECODE_EMULATEX (0xff700f00, 0xea100f00, t32_emulate_rd8rn16rm0_rwflags,
- REGS(NOSPPC, 0, 0, 0, NOSPPC)),
-
- /* CMN 1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */
- DECODE_OR (0xfff00f00, 0xeb100f00),
- /* CMP 1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */
- DECODE_EMULATEX (0xfff00f00, 0xebb00f00, t32_emulate_rd8rn16rm0_rwflags,
- REGS(NOPC, 0, 0, 0, NOSPPC)),
-
- /* MOV 1110 1010 010x 1111 xxxx xxxx xxxx xxxx */
- /* MVN 1110 1010 011x 1111 xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xffcf0000, 0xea4f0000, t32_emulate_rd8rn16rm0_rwflags,
- REGS(0, 0, NOSPPC, 0, NOSPPC)),
-
- /* ??? 1110 1010 101x xxxx xxxx xxxx xxxx xxxx */
- /* ??? 1110 1010 111x xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0xffa00000, 0xeaa00000),
- /* ??? 1110 1011 001x xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0xffe00000, 0xeb200000),
- /* ??? 1110 1011 100x xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0xffe00000, 0xeb800000),
- /* ??? 1110 1011 111x xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0xffe00000, 0xebe00000),
-
- /* ADD/SUB SP, SP, Rm, LSL #0..3 */
- /* 1110 1011 x0xx 1101 x000 1101 xx00 xxxx */
- DECODE_EMULATEX (0xff4f7f30, 0xeb0d0d00, t32_emulate_rd8rn16rm0_rwflags,
- REGS(SP, 0, SP, 0, NOSPPC)),
-
- /* ADD/SUB SP, SP, Rm, shift */
- /* 1110 1011 x0xx 1101 xxxx 1101 xxxx xxxx */
- DECODE_REJECT (0xff4f0f00, 0xeb0d0d00),
-
- /* ADD/SUB Rd, SP, Rm, shift */
- /* 1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xff4f0000, 0xeb0d0000, t32_emulate_rd8rn16rm0_rwflags,
- REGS(SP, 0, NOPC, 0, NOSPPC)),
-
- /* AND 1110 1010 000x xxxx xxxx xxxx xxxx xxxx */
- /* BIC 1110 1010 001x xxxx xxxx xxxx xxxx xxxx */
- /* ORR 1110 1010 010x xxxx xxxx xxxx xxxx xxxx */
- /* ORN 1110 1010 011x xxxx xxxx xxxx xxxx xxxx */
- /* EOR 1110 1010 100x xxxx xxxx xxxx xxxx xxxx */
- /* PKH 1110 1010 110x xxxx xxxx xxxx xxxx xxxx */
- /* ADD 1110 1011 000x xxxx xxxx xxxx xxxx xxxx */
- /* ADC 1110 1011 010x xxxx xxxx xxxx xxxx xxxx */
- /* SBC 1110 1011 011x xxxx xxxx xxxx xxxx xxxx */
- /* SUB 1110 1011 101x xxxx xxxx xxxx xxxx xxxx */
- /* RSB 1110 1011 110x xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfe000000, 0xea000000, t32_emulate_rd8rn16rm0_rwflags,
- REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
-
- DECODE_END
-};
-
-static const union decode_item t32_table_1111_0x0x___0[] = {
- /* Data-processing (modified immediate) */
-
- /* TST 1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */
- /* TEQ 1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */
- DECODE_EMULATEX (0xfb708f00, 0xf0100f00, t32_emulate_rd8rn16rm0_rwflags,
- REGS(NOSPPC, 0, 0, 0, 0)),
-
- /* CMN 1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */
- DECODE_OR (0xfbf08f00, 0xf1100f00),
- /* CMP 1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */
- DECODE_EMULATEX (0xfbf08f00, 0xf1b00f00, t32_emulate_rd8rn16rm0_rwflags,
- REGS(NOPC, 0, 0, 0, 0)),
-
- /* MOV 1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */
- /* MVN 1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfbcf8000, 0xf04f0000, t32_emulate_rd8rn16rm0_rwflags,
- REGS(0, 0, NOSPPC, 0, 0)),
-
- /* ??? 1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */
- DECODE_REJECT (0xfbe08000, 0xf0a00000),
- /* ??? 1111 0x00 110x xxxx 0xxx xxxx xxxx xxxx */
- /* ??? 1111 0x00 111x xxxx 0xxx xxxx xxxx xxxx */
- DECODE_REJECT (0xfbc08000, 0xf0c00000),
- /* ??? 1111 0x01 001x xxxx 0xxx xxxx xxxx xxxx */
- DECODE_REJECT (0xfbe08000, 0xf1200000),
- /* ??? 1111 0x01 100x xxxx 0xxx xxxx xxxx xxxx */
- DECODE_REJECT (0xfbe08000, 0xf1800000),
- /* ??? 1111 0x01 111x xxxx 0xxx xxxx xxxx xxxx */
- DECODE_REJECT (0xfbe08000, 0xf1e00000),
-
- /* ADD Rd, SP, #imm 1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */
- /* SUB Rd, SP, #imm 1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfb4f8000, 0xf10d0000, t32_emulate_rd8rn16rm0_rwflags,
- REGS(SP, 0, NOPC, 0, 0)),
-
- /* AND 1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */
- /* BIC 1111 0x00 001x xxxx 0xxx xxxx xxxx xxxx */
- /* ORR 1111 0x00 010x xxxx 0xxx xxxx xxxx xxxx */
- /* ORN 1111 0x00 011x xxxx 0xxx xxxx xxxx xxxx */
- /* EOR 1111 0x00 100x xxxx 0xxx xxxx xxxx xxxx */
- /* ADD 1111 0x01 000x xxxx 0xxx xxxx xxxx xxxx */
- /* ADC 1111 0x01 010x xxxx 0xxx xxxx xxxx xxxx */
- /* SBC 1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */
- /* SUB 1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */
- /* RSB 1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfa008000, 0xf0000000, t32_emulate_rd8rn16rm0_rwflags,
- REGS(NOSPPC, 0, NOSPPC, 0, 0)),
-
- DECODE_END
-};
-
-static const union decode_item t32_table_1111_0x1x___0[] = {
- /* Data-processing (plain binary immediate) */
-
- /* ADDW Rd, PC, #imm 1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */
- DECODE_OR (0xfbff8000, 0xf20f0000),
- /* SUBW Rd, PC, #imm 1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfbff8000, 0xf2af0000, t32_emulate_rd8pc16_noflags,
- REGS(PC, 0, NOSPPC, 0, 0)),
-
- /* ADDW SP, SP, #imm 1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */
- DECODE_OR (0xfbff8f00, 0xf20d0d00),
- /* SUBW SP, SP, #imm 1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */
- DECODE_EMULATEX (0xfbff8f00, 0xf2ad0d00, t32_emulate_rd8rn16_noflags,
- REGS(SP, 0, SP, 0, 0)),
-
- /* ADDW 1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */
- DECODE_OR (0xfbf08000, 0xf2000000),
- /* SUBW 1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfbf08000, 0xf2a00000, t32_emulate_rd8rn16_noflags,
- REGS(NOPCX, 0, NOSPPC, 0, 0)),
-
- /* MOVW 1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */
- /* MOVT 1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfb708000, 0xf2400000, t32_emulate_rd8rn16_noflags,
- REGS(0, 0, NOSPPC, 0, 0)),
-
- /* SSAT16 1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */
- /* SSAT 1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */
- /* USAT16 1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */
- /* USAT 1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfb508000, 0xf3000000, t32_emulate_rd8rn16rm0_rwflags,
- REGS(NOSPPC, 0, NOSPPC, 0, 0)),
-
- /* SFBX 1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */
- /* UFBX 1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfb708000, 0xf3400000, t32_emulate_rd8rn16_noflags,
- REGS(NOSPPC, 0, NOSPPC, 0, 0)),
-
- /* BFC 1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfbff8000, 0xf36f0000, t32_emulate_rd8rn16_noflags,
- REGS(0, 0, NOSPPC, 0, 0)),
-
- /* BFI 1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfbf08000, 0xf3600000, t32_emulate_rd8rn16_noflags,
- REGS(NOSPPCX, 0, NOSPPC, 0, 0)),
-
- DECODE_END
-};
-
-static const union decode_item t32_table_1111_0xxx___1[] = {
- /* Branches and miscellaneous control */
-
- /* YIELD 1111 0011 1010 xxxx 10x0 x000 0000 0001 */
- DECODE_OR (0xfff0d7ff, 0xf3a08001),
- /* SEV 1111 0011 1010 xxxx 10x0 x000 0000 0100 */
- DECODE_EMULATE (0xfff0d7ff, 0xf3a08004, kprobe_emulate_none),
- /* NOP 1111 0011 1010 xxxx 10x0 x000 0000 0000 */
- /* WFE 1111 0011 1010 xxxx 10x0 x000 0000 0010 */
- /* WFI 1111 0011 1010 xxxx 10x0 x000 0000 0011 */
- DECODE_SIMULATE (0xfff0d7fc, 0xf3a08000, kprobe_simulate_nop),
-
- /* MRS Rd, CPSR 1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
- DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, t32_simulate_mrs,
- REGS(0, 0, NOSPPC, 0, 0)),
-
- /*
- * Unsupported instructions
- * 1111 0x11 1xxx xxxx 10x0 xxxx xxxx xxxx
- *
- * MSR 1111 0011 100x xxxx 10x0 xxxx xxxx xxxx
- * DBG hint 1111 0011 1010 xxxx 10x0 x000 1111 xxxx
- * Unallocated hints 1111 0011 1010 xxxx 10x0 x000 xxxx xxxx
- * CPS 1111 0011 1010 xxxx 10x0 xxxx xxxx xxxx
- * CLREX/DSB/DMB/ISB 1111 0011 1011 xxxx 10x0 xxxx xxxx xxxx
- * BXJ 1111 0011 1100 xxxx 10x0 xxxx xxxx xxxx
- * SUBS PC,LR,#<imm8> 1111 0011 1101 xxxx 10x0 xxxx xxxx xxxx
- * MRS Rd, SPSR 1111 0011 1111 xxxx 10x0 xxxx xxxx xxxx
- * SMC 1111 0111 1111 xxxx 1000 xxxx xxxx xxxx
- * UNDEFINED 1111 0111 1111 xxxx 1010 xxxx xxxx xxxx
- * ??? 1111 0111 1xxx xxxx 1010 xxxx xxxx xxxx
- */
- DECODE_REJECT (0xfb80d000, 0xf3808000),
-
- /* Bcc 1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */
- DECODE_CUSTOM (0xf800d000, 0xf0008000, t32_decode_cond_branch),
-
- /* BLX 1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */
- DECODE_OR (0xf800d001, 0xf000c000),
- /* B 1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */
- /* BL 1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */
- DECODE_SIMULATE (0xf8009000, 0xf0009000, t32_simulate_branch),
-
- DECODE_END
-};
-
-static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
- /* Memory hints */
-
- /* PLD (literal) 1111 1000 x001 1111 1111 xxxx xxxx xxxx */
- /* PLI (literal) 1111 1001 x001 1111 1111 xxxx xxxx xxxx */
- DECODE_SIMULATE (0xfe7ff000, 0xf81ff000, kprobe_simulate_nop),
-
- /* PLD{W} (immediate) 1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
- DECODE_OR (0xffd0f000, 0xf890f000),
- /* PLD{W} (immediate) 1111 1000 00x1 xxxx 1111 1100 xxxx xxxx */
- DECODE_OR (0xffd0ff00, 0xf810fc00),
- /* PLI (immediate) 1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
- DECODE_OR (0xfff0f000, 0xf990f000),
- /* PLI (immediate) 1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
- DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, kprobe_simulate_nop,
- REGS(NOPCX, 0, 0, 0, 0)),
-
- /* PLD{W} (register) 1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
- DECODE_OR (0xffd0ffc0, 0xf810f000),
- /* PLI (register) 1111 1001 0001 xxxx 1111 0000 00xx xxxx */
- DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, kprobe_simulate_nop,
- REGS(NOPCX, 0, 0, 0, NOSPPC)),
-
- /* Other unallocated instructions... */
- DECODE_END
-};
-
-static const union decode_item t32_table_1111_100x[] = {
- /* Store/Load single data item */
-
- /* ??? 1111 100x x11x xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0xfe600000, 0xf8600000),
-
- /* ??? 1111 1001 0101 xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0xfff00000, 0xf9500000),
-
- /* ??? 1111 100x 0xxx xxxx xxxx 10x0 xxxx xxxx */
- DECODE_REJECT (0xfe800d00, 0xf8000800),
-
- /* STRBT 1111 1000 0000 xxxx xxxx 1110 xxxx xxxx */
- /* STRHT 1111 1000 0010 xxxx xxxx 1110 xxxx xxxx */
- /* STRT 1111 1000 0100 xxxx xxxx 1110 xxxx xxxx */
- /* LDRBT 1111 1000 0001 xxxx xxxx 1110 xxxx xxxx */
- /* LDRSBT 1111 1001 0001 xxxx xxxx 1110 xxxx xxxx */
- /* LDRHT 1111 1000 0011 xxxx xxxx 1110 xxxx xxxx */
- /* LDRSHT 1111 1001 0011 xxxx xxxx 1110 xxxx xxxx */
- /* LDRT 1111 1000 0101 xxxx xxxx 1110 xxxx xxxx */
- DECODE_REJECT (0xfe800f00, 0xf8000e00),
-
- /* STR{,B,H} Rn,[PC...] 1111 1000 xxx0 1111 xxxx xxxx xxxx xxxx */
- DECODE_REJECT (0xff1f0000, 0xf80f0000),
-
- /* STR{,B,H} PC,[Rn...] 1111 1000 xxx0 xxxx 1111 xxxx xxxx xxxx */
- DECODE_REJECT (0xff10f000, 0xf800f000),
-
- /* LDR (literal) 1111 1000 x101 1111 xxxx xxxx xxxx xxxx */
- DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, t32_simulate_ldr_literal,
- REGS(PC, ANY, 0, 0, 0)),
-
- /* STR (immediate) 1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */
- /* LDR (immediate) 1111 1000 0101 xxxx xxxx 1xxx xxxx xxxx */
- DECODE_OR (0xffe00800, 0xf8400800),
- /* STR (immediate) 1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */
- /* LDR (immediate) 1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xffe00000, 0xf8c00000, t32_emulate_ldrstr,
- REGS(NOPCX, ANY, 0, 0, 0)),
-
- /* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
- /* LDR (register) 1111 1000 0101 xxxx xxxx 0000 00xx xxxx */
- DECODE_EMULATEX (0xffe00fc0, 0xf8400000, t32_emulate_ldrstr,
- REGS(NOPCX, ANY, 0, 0, NOSPPC)),
-
- /* LDRB (literal) 1111 1000 x001 1111 xxxx xxxx xxxx xxxx */
- /* LDRSB (literal) 1111 1001 x001 1111 xxxx xxxx xxxx xxxx */
- /* LDRH (literal) 1111 1000 x011 1111 xxxx xxxx xxxx xxxx */
- /* LDRSH (literal) 1111 1001 x011 1111 xxxx xxxx xxxx xxxx */
- DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, t32_simulate_ldr_literal,
- REGS(PC, NOSPPCX, 0, 0, 0)),
-
- /* STRB (immediate) 1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */
- /* STRH (immediate) 1111 1000 0010 xxxx xxxx 1xxx xxxx xxxx */
- /* LDRB (immediate) 1111 1000 0001 xxxx xxxx 1xxx xxxx xxxx */
- /* LDRSB (immediate) 1111 1001 0001 xxxx xxxx 1xxx xxxx xxxx */
- /* LDRH (immediate) 1111 1000 0011 xxxx xxxx 1xxx xxxx xxxx */
- /* LDRSH (immediate) 1111 1001 0011 xxxx xxxx 1xxx xxxx xxxx */
- DECODE_OR (0xfec00800, 0xf8000800),
- /* STRB (immediate) 1111 1000 1000 xxxx xxxx xxxx xxxx xxxx */
- /* STRH (immediate) 1111 1000 1010 xxxx xxxx xxxx xxxx xxxx */
- /* LDRB (immediate) 1111 1000 1001 xxxx xxxx xxxx xxxx xxxx */
- /* LDRSB (immediate) 1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */
- /* LDRH (immediate) 1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */
- /* LDRSH (immediate) 1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */
- DECODE_EMULATEX (0xfec00000, 0xf8800000, t32_emulate_ldrstr,
- REGS(NOPCX, NOSPPCX, 0, 0, 0)),
-
- /* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
- /* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */
- /* LDRB (register) 1111 1000 0001 xxxx xxxx 0000 00xx xxxx */
- /* LDRSB (register) 1111 1001 0001 xxxx xxxx 0000 00xx xxxx */
- /* LDRH (register) 1111 1000 0011 xxxx xxxx 0000 00xx xxxx */
- /* LDRSH (register) 1111 1001 0011 xxxx xxxx 0000 00xx xxxx */
- DECODE_EMULATEX (0xfe800fc0, 0xf8000000, t32_emulate_ldrstr,
- REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)),
-
- /* Other unallocated instructions... */
- DECODE_END
-};
-
-static const union decode_item t32_table_1111_1010___1111[] = {
- /* Data-processing (register) */
-
- /* ??? 1111 1010 011x xxxx 1111 xxxx 1xxx xxxx */
- DECODE_REJECT (0xffe0f080, 0xfa60f080),
-
- /* SXTH 1111 1010 0000 1111 1111 xxxx 1xxx xxxx */
- /* UXTH 1111 1010 0001 1111 1111 xxxx 1xxx xxxx */
- /* SXTB16 1111 1010 0010 1111 1111 xxxx 1xxx xxxx */
- /* UXTB16 1111 1010 0011 1111 1111 xxxx 1xxx xxxx */
- /* SXTB 1111 1010 0100 1111 1111 xxxx 1xxx xxxx */
- /* UXTB 1111 1010 0101 1111 1111 xxxx 1xxx xxxx */
- DECODE_EMULATEX (0xff8ff080, 0xfa0ff080, t32_emulate_rd8rn16rm0_rwflags,
- REGS(0, 0, NOSPPC, 0, NOSPPC)),
-
-
- /* ??? 1111 1010 1xxx xxxx 1111 xxxx 0x11 xxxx */
- DECODE_REJECT (0xff80f0b0, 0xfa80f030),
- /* ??? 1111 1010 1x11 xxxx 1111 xxxx 0xxx xxxx */
- DECODE_REJECT (0xffb0f080, 0xfab0f000),
-
- /* SADD16 1111 1010 1001 xxxx 1111 xxxx 0000 xxxx */
- /* SASX 1111 1010 1010 xxxx 1111 xxxx 0000 xxxx */
- /* SSAX 1111 1010 1110 xxxx 1111 xxxx 0000 xxxx */
- /* SSUB16 1111 1010 1101 xxxx 1111 xxxx 0000 xxxx */
- /* SADD8 1111 1010 1000 xxxx 1111 xxxx 0000 xxxx */
- /* SSUB8 1111 1010 1100 xxxx 1111 xxxx 0000 xxxx */
-
- /* QADD16 1111 1010 1001 xxxx 1111 xxxx 0001 xxxx */
- /* QASX 1111 1010 1010 xxxx 1111 xxxx 0001 xxxx */
- /* QSAX 1111 1010 1110 xxxx 1111 xxxx 0001 xxxx */
- /* QSUB16 1111 1010 1101 xxxx 1111 xxxx 0001 xxxx */
- /* QADD8 1111 1010 1000 xxxx 1111 xxxx 0001 xxxx */
- /* QSUB8 1111 1010 1100 xxxx 1111 xxxx 0001 xxxx */
-
- /* SHADD16 1111 1010 1001 xxxx 1111 xxxx 0010 xxxx */
- /* SHASX 1111 1010 1010 xxxx 1111 xxxx 0010 xxxx */
- /* SHSAX 1111 1010 1110 xxxx 1111 xxxx 0010 xxxx */
- /* SHSUB16 1111 1010 1101 xxxx 1111 xxxx 0010 xxxx */
- /* SHADD8 1111 1010 1000 xxxx 1111 xxxx 0010 xxxx */
- /* SHSUB8 1111 1010 1100 xxxx 1111 xxxx 0010 xxxx */
-
- /* UADD16 1111 1010 1001 xxxx 1111 xxxx 0100 xxxx */
- /* UASX 1111 1010 1010 xxxx 1111 xxxx 0100 xxxx */
- /* USAX 1111 1010 1110 xxxx 1111 xxxx 0100 xxxx */
- /* USUB16 1111 1010 1101 xxxx 1111 xxxx 0100 xxxx */
- /* UADD8 1111 1010 1000 xxxx 1111 xxxx 0100 xxxx */
- /* USUB8 1111 1010 1100 xxxx 1111 xxxx 0100 xxxx */
-
- /* UQADD16 1111 1010 1001 xxxx 1111 xxxx 0101 xxxx */
- /* UQASX 1111 1010 1010 xxxx 1111 xxxx 0101 xxxx */
- /* UQSAX 1111 1010 1110 xxxx 1111 xxxx 0101 xxxx */
- /* UQSUB16 1111 1010 1101 xxxx 1111 xxxx 0101 xxxx */
- /* UQADD8 1111 1010 1000 xxxx 1111 xxxx 0101 xxxx */
- /* UQSUB8 1111 1010 1100 xxxx 1111 xxxx 0101 xxxx */
-
- /* UHADD16 1111 1010 1001 xxxx 1111 xxxx 0110 xxxx */
- /* UHASX 1111 1010 1010 xxxx 1111 xxxx 0110 xxxx */
- /* UHSAX 1111 1010 1110 xxxx 1111 xxxx 0110 xxxx */
- /* UHSUB16 1111 1010 1101 xxxx 1111 xxxx 0110 xxxx */
- /* UHADD8 1111 1010 1000 xxxx 1111 xxxx 0110 xxxx */
- /* UHSUB8 1111 1010 1100 xxxx 1111 xxxx 0110 xxxx */
- DECODE_OR (0xff80f080, 0xfa80f000),
-
- /* SXTAH 1111 1010 0000 xxxx 1111 xxxx 1xxx xxxx */
- /* UXTAH 1111 1010 0001 xxxx 1111 xxxx 1xxx xxxx */
- /* SXTAB16 1111 1010 0010 xxxx 1111 xxxx 1xxx xxxx */
- /* UXTAB16 1111 1010 0011 xxxx 1111 xxxx 1xxx xxxx */
- /* SXTAB 1111 1010 0100 xxxx 1111 xxxx 1xxx xxxx */
- /* UXTAB 1111 1010 0101 xxxx 1111 xxxx 1xxx xxxx */
- DECODE_OR (0xff80f080, 0xfa00f080),
-
- /* QADD 1111 1010 1000 xxxx 1111 xxxx 1000 xxxx */
- /* QDADD 1111 1010 1000 xxxx 1111 xxxx 1001 xxxx */
- /* QSUB 1111 1010 1000 xxxx 1111 xxxx 1010 xxxx */
- /* QDSUB 1111 1010 1000 xxxx 1111 xxxx 1011 xxxx */
- DECODE_OR (0xfff0f0c0, 0xfa80f080),
-
- /* SEL 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
- DECODE_OR (0xfff0f0f0, 0xfaa0f080),
-
- /* LSL 1111 1010 000x xxxx 1111 xxxx 0000 xxxx */
- /* LSR 1111 1010 001x xxxx 1111 xxxx 0000 xxxx */
- /* ASR 1111 1010 010x xxxx 1111 xxxx 0000 xxxx */
- /* ROR 1111 1010 011x xxxx 1111 xxxx 0000 xxxx */
- DECODE_EMULATEX (0xff80f0f0, 0xfa00f000, t32_emulate_rd8rn16rm0_rwflags,
- REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
-
- /* CLZ 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
- DECODE_OR (0xfff0f0f0, 0xfab0f080),
-
- /* REV 1111 1010 1001 xxxx 1111 xxxx 1000 xxxx */
- /* REV16 1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */
- /* RBIT 1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */
- /* REVSH 1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */
- DECODE_EMULATEX (0xfff0f0c0, 0xfa90f080, t32_emulate_rd8rn16_noflags,
- REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)),
-
- /* Other unallocated instructions... */
- DECODE_END
-};
-
-static const union decode_item t32_table_1111_1011_0[] = {
- /* Multiply, multiply accumulate, and absolute difference */
-
- /* ??? 1111 1011 0000 xxxx 1111 xxxx 0001 xxxx */
- DECODE_REJECT (0xfff0f0f0, 0xfb00f010),
- /* ??? 1111 1011 0111 xxxx 1111 xxxx 0001 xxxx */
- DECODE_REJECT (0xfff0f0f0, 0xfb70f010),
-
- /* SMULxy 1111 1011 0001 xxxx 1111 xxxx 00xx xxxx */
- DECODE_OR (0xfff0f0c0, 0xfb10f000),
- /* MUL 1111 1011 0000 xxxx 1111 xxxx 0000 xxxx */
- /* SMUAD{X} 1111 1011 0010 xxxx 1111 xxxx 000x xxxx */
- /* SMULWy 1111 1011 0011 xxxx 1111 xxxx 000x xxxx */
- /* SMUSD{X} 1111 1011 0100 xxxx 1111 xxxx 000x xxxx */
- /* SMMUL{R} 1111 1011 0101 xxxx 1111 xxxx 000x xxxx */
- /* USAD8 1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */
- DECODE_EMULATEX (0xff80f0e0, 0xfb00f000, t32_emulate_rd8rn16rm0_rwflags,
- REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
-
- /* ??? 1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */
- DECODE_REJECT (0xfff000f0, 0xfb700010),
-
- /* SMLAxy 1111 1011 0001 xxxx xxxx xxxx 00xx xxxx */
- DECODE_OR (0xfff000c0, 0xfb100000),
- /* MLA 1111 1011 0000 xxxx xxxx xxxx 0000 xxxx */
- /* MLS 1111 1011 0000 xxxx xxxx xxxx 0001 xxxx */
- /* SMLAD{X} 1111 1011 0010 xxxx xxxx xxxx 000x xxxx */
- /* SMLAWy 1111 1011 0011 xxxx xxxx xxxx 000x xxxx */
- /* SMLSD{X} 1111 1011 0100 xxxx xxxx xxxx 000x xxxx */
- /* SMMLA{R} 1111 1011 0101 xxxx xxxx xxxx 000x xxxx */
- /* SMMLS{R} 1111 1011 0110 xxxx xxxx xxxx 000x xxxx */
- /* USADA8 1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */
- DECODE_EMULATEX (0xff8000c0, 0xfb000000, t32_emulate_rd8rn16rm0ra12_noflags,
- REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)),
-
- /* Other unallocated instructions... */
- DECODE_END
-};
-
-static const union decode_item t32_table_1111_1011_1[] = {
- /* Long multiply, long multiply accumulate, and divide */
-
- /* UMAAL 1111 1011 1110 xxxx xxxx xxxx 0110 xxxx */
- DECODE_OR (0xfff000f0, 0xfbe00060),
- /* SMLALxy 1111 1011 1100 xxxx xxxx xxxx 10xx xxxx */
- DECODE_OR (0xfff000c0, 0xfbc00080),
- /* SMLALD{X} 1111 1011 1100 xxxx xxxx xxxx 110x xxxx */
- /* SMLSLD{X} 1111 1011 1101 xxxx xxxx xxxx 110x xxxx */
- DECODE_OR (0xffe000e0, 0xfbc000c0),
- /* SMULL 1111 1011 1000 xxxx xxxx xxxx 0000 xxxx */
- /* UMULL 1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */
- /* SMLAL 1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */
- /* UMLAL 1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */
- DECODE_EMULATEX (0xff9000f0, 0xfb800000, t32_emulate_rdlo12rdhi8rn16rm0_noflags,
- REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)),
-
- /* SDIV 1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */
- /* UDIV 1111 1011 1011 xxxx xxxx xxxx 1111 xxxx */
- /* Other unallocated instructions... */
- DECODE_END
-};
-
-const union decode_item kprobe_decode_thumb32_table[] = {
-
- /*
- * Load/store multiple instructions
- * 1110 100x x0xx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0xfe400000, 0xe8000000, t32_table_1110_100x_x0xx),
-
- /*
- * Load/store dual, load/store exclusive, table branch
- * 1110 100x x1xx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0xfe400000, 0xe8400000, t32_table_1110_100x_x1xx),
-
- /*
- * Data-processing (shifted register)
- * 1110 101x xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0xfe000000, 0xea000000, t32_table_1110_101x),
-
- /*
- * Coprocessor instructions
- * 1110 11xx xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_REJECT (0xfc000000, 0xec000000),
-
- /*
- * Data-processing (modified immediate)
- * 1111 0x0x xxxx xxxx 0xxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0xfa008000, 0xf0000000, t32_table_1111_0x0x___0),
-
- /*
- * Data-processing (plain binary immediate)
- * 1111 0x1x xxxx xxxx 0xxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0xfa008000, 0xf2000000, t32_table_1111_0x1x___0),
-
- /*
- * Branches and miscellaneous control
- * 1111 0xxx xxxx xxxx 1xxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0xf8008000, 0xf0008000, t32_table_1111_0xxx___1),
-
- /*
- * Advanced SIMD element or structure load/store instructions
- * 1111 1001 xxx0 xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_REJECT (0xff100000, 0xf9000000),
-
- /*
- * Memory hints
- * 1111 100x x0x1 xxxx 1111 xxxx xxxx xxxx
- */
- DECODE_TABLE (0xfe50f000, 0xf810f000, t32_table_1111_100x_x0x1__1111),
-
- /*
- * Store single data item
- * 1111 1000 xxx0 xxxx xxxx xxxx xxxx xxxx
- * Load single data items
- * 1111 100x xxx1 xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0xfe000000, 0xf8000000, t32_table_1111_100x),
-
- /*
- * Data-processing (register)
- * 1111 1010 xxxx xxxx 1111 xxxx xxxx xxxx
- */
- DECODE_TABLE (0xff00f000, 0xfa00f000, t32_table_1111_1010___1111),
-
- /*
- * Multiply, multiply accumulate, and absolute difference
- * 1111 1011 0xxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0xff800000, 0xfb000000, t32_table_1111_1011_0),
-
- /*
- * Long multiply, long multiply accumulate, and divide
- * 1111 1011 1xxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE (0xff800000, 0xfb800000, t32_table_1111_1011_1),
-
- /*
- * Coprocessor instructions
- * 1111 11xx xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_END
-};
-#ifdef CONFIG_ARM_KPROBES_TEST_MODULE
-EXPORT_SYMBOL_GPL(kprobe_decode_thumb32_table);
-#endif
+/* t16 thumb actions */
static void __kprobes
-t16_simulate_bxblx(struct kprobe *p, struct pt_regs *regs)
+t16_simulate_bxblx(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = thumb_probe_pc(p);
+ unsigned long pc = regs->ARM_pc + 2;
int rm = (insn >> 3) & 0xf;
unsigned long rmv = (rm == 15) ? pc : regs->uregs[rm];
if (insn & (1 << 7)) /* BLX ? */
- regs->ARM_lr = (unsigned long)p->addr + 2;
+ regs->ARM_lr = regs->ARM_pc | 1;
bx_write_pc(rmv, regs);
}
static void __kprobes
-t16_simulate_ldr_literal(struct kprobe *p, struct pt_regs *regs)
+t16_simulate_ldr_literal(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long* base = (unsigned long *)(thumb_probe_pc(p) & ~3);
+ unsigned long *base = (unsigned long *)((regs->ARM_pc + 2) & ~3);
long index = insn & 0xff;
int rt = (insn >> 8) & 0x7;
regs->uregs[rt] = base[index];
}
static void __kprobes
-t16_simulate_ldrstr_sp_relative(struct kprobe *p, struct pt_regs *regs)
+t16_simulate_ldrstr_sp_relative(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
unsigned long* base = (unsigned long *)regs->ARM_sp;
long index = insn & 0xff;
int rt = (insn >> 8) & 0x7;
@@ -986,20 +342,20 @@ t16_simulate_ldrstr_sp_relative(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-t16_simulate_reladr(struct kprobe *p, struct pt_regs *regs)
+t16_simulate_reladr(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
unsigned long base = (insn & 0x800) ? regs->ARM_sp
- : (thumb_probe_pc(p) & ~3);
+ : ((regs->ARM_pc + 2) & ~3);
long offset = insn & 0xff;
int rt = (insn >> 8) & 0x7;
regs->uregs[rt] = base + offset * 4;
}
static void __kprobes
-t16_simulate_add_sp_imm(struct kprobe *p, struct pt_regs *regs)
+t16_simulate_add_sp_imm(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
long imm = insn & 0x7f;
if (insn & 0x80) /* SUB */
regs->ARM_sp -= imm * 4;
@@ -1008,21 +364,22 @@ t16_simulate_add_sp_imm(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-t16_simulate_cbz(struct kprobe *p, struct pt_regs *regs)
+t16_simulate_cbz(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
int rn = insn & 0x7;
- kprobe_opcode_t nonzero = regs->uregs[rn] ? insn : ~insn;
+ probes_opcode_t nonzero = regs->uregs[rn] ? insn : ~insn;
if (nonzero & 0x800) {
long i = insn & 0x200;
long imm5 = insn & 0xf8;
- unsigned long pc = thumb_probe_pc(p);
+ unsigned long pc = regs->ARM_pc + 2;
regs->ARM_pc = pc + (i >> 3) + (imm5 >> 2);
}
}
static void __kprobes
-t16_simulate_it(struct kprobe *p, struct pt_regs *regs)
+t16_simulate_it(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
/*
* The 8 IT state bits are split into two parts in CPSR:
@@ -1030,7 +387,6 @@ t16_simulate_it(struct kprobe *p, struct pt_regs *regs)
* ITSTATE<7:2> are in CPSR<15:10>
* The new IT state is in the lower byte of insn.
*/
- kprobe_opcode_t insn = p->opcode;
unsigned long cpsr = regs->ARM_cpsr;
cpsr &= ~PSR_IT_MASK;
cpsr |= (insn & 0xfc) << 8;
@@ -1039,50 +395,54 @@ t16_simulate_it(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-t16_singlestep_it(struct kprobe *p, struct pt_regs *regs)
+t16_singlestep_it(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
regs->ARM_pc += 2;
- t16_simulate_it(p, regs);
+ t16_simulate_it(insn, asi, regs);
}
-static enum kprobe_insn __kprobes
-t16_decode_it(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+static enum probes_insn __kprobes
+t16_decode_it(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d)
{
asi->insn_singlestep = t16_singlestep_it;
return INSN_GOOD_NO_SLOT;
}
static void __kprobes
-t16_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
+t16_simulate_cond_branch(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = thumb_probe_pc(p);
+ unsigned long pc = regs->ARM_pc + 2;
long offset = insn & 0x7f;
offset -= insn & 0x80; /* Apply sign bit */
regs->ARM_pc = pc + (offset * 2);
}
-static enum kprobe_insn __kprobes
-t16_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+static enum probes_insn __kprobes
+t16_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d)
{
int cc = (insn >> 8) & 0xf;
- asi->insn_check_cc = kprobe_condition_checks[cc];
+ asi->insn_check_cc = probes_condition_checks[cc];
asi->insn_handler = t16_simulate_cond_branch;
return INSN_GOOD_NO_SLOT;
}
static void __kprobes
-t16_simulate_branch(struct kprobe *p, struct pt_regs *regs)
+t16_simulate_branch(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = thumb_probe_pc(p);
+ unsigned long pc = regs->ARM_pc + 2;
long offset = insn & 0x3ff;
offset -= insn & 0x400; /* Apply sign bit */
regs->ARM_pc = pc + (offset * 2);
}
static unsigned long __kprobes
-t16_emulate_loregs(struct kprobe *p, struct pt_regs *regs)
+t16_emulate_loregs(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
unsigned long oldcpsr = regs->ARM_cpsr;
unsigned long newcpsr;
@@ -1095,7 +455,7 @@ t16_emulate_loregs(struct kprobe *p, struct pt_regs *regs)
"mrs %[newcpsr], cpsr \n\t"
: [newcpsr] "=r" (newcpsr)
: [oldcpsr] "r" (oldcpsr), [regs] "r" (regs),
- [fn] "r" (p->ainsn.insn_fn)
+ [fn] "r" (asi->insn_fn)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"lr", "memory", "cc"
);
@@ -1104,24 +464,26 @@ t16_emulate_loregs(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
-t16_emulate_loregs_rwflags(struct kprobe *p, struct pt_regs *regs)
+t16_emulate_loregs_rwflags(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- regs->ARM_cpsr = t16_emulate_loregs(p, regs);
+ regs->ARM_cpsr = t16_emulate_loregs(insn, asi, regs);
}
static void __kprobes
-t16_emulate_loregs_noitrwflags(struct kprobe *p, struct pt_regs *regs)
+t16_emulate_loregs_noitrwflags(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- unsigned long cpsr = t16_emulate_loregs(p, regs);
+ unsigned long cpsr = t16_emulate_loregs(insn, asi, regs);
if (!in_it_block(cpsr))
regs->ARM_cpsr = cpsr;
}
static void __kprobes
-t16_emulate_hiregs(struct kprobe *p, struct pt_regs *regs)
+t16_emulate_hiregs(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
- kprobe_opcode_t insn = p->opcode;
- unsigned long pc = thumb_probe_pc(p);
+ unsigned long pc = regs->ARM_pc + 2;
int rdn = (insn & 0x7) | ((insn & 0x80) >> 4);
int rm = (insn >> 3) & 0xf;
@@ -1137,7 +499,7 @@ t16_emulate_hiregs(struct kprobe *p, struct pt_regs *regs)
"blx %[fn] \n\t"
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdnv), [cpsr] "=r" (cpsr)
- : "0" (rdnv), "r" (rmv), "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+ : "0" (rdnv), "r" (rmv), "1" (cpsr), [fn] "r" (asi->insn_fn)
: "lr", "memory", "cc"
);
@@ -1148,8 +510,9 @@ t16_emulate_hiregs(struct kprobe *p, struct pt_regs *regs)
regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}
-static enum kprobe_insn __kprobes
-t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+static enum probes_insn __kprobes
+t16_decode_hiregs(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d)
{
insn &= ~0x00ff;
insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */
@@ -1159,7 +522,8 @@ t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi)
}
static void __kprobes
-t16_emulate_push(struct kprobe *p, struct pt_regs *regs)
+t16_emulate_push(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
__asm__ __volatile__ (
"ldr r9, [%[regs], #13*4] \n\t"
@@ -1168,14 +532,15 @@ t16_emulate_push(struct kprobe *p, struct pt_regs *regs)
"blx %[fn] \n\t"
"str r9, [%[regs], #13*4] \n\t"
:
- : [regs] "r" (regs), [fn] "r" (p->ainsn.insn_fn)
+ : [regs] "r" (regs), [fn] "r" (asi->insn_fn)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9",
"lr", "memory", "cc"
);
}
-static enum kprobe_insn __kprobes
-t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+static enum probes_insn __kprobes
+t16_decode_push(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d)
{
/*
* To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}"
@@ -1189,7 +554,8 @@ t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi)
}
static void __kprobes
-t16_emulate_pop_nopc(struct kprobe *p, struct pt_regs *regs)
+t16_emulate_pop_nopc(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
__asm__ __volatile__ (
"ldr r9, [%[regs], #13*4] \n\t"
@@ -1198,14 +564,15 @@ t16_emulate_pop_nopc(struct kprobe *p, struct pt_regs *regs)
"stmia %[regs], {r0-r7} \n\t"
"str r9, [%[regs], #13*4] \n\t"
:
- : [regs] "r" (regs), [fn] "r" (p->ainsn.insn_fn)
+ : [regs] "r" (regs), [fn] "r" (asi->insn_fn)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9",
"lr", "memory", "cc"
);
}
static void __kprobes
-t16_emulate_pop_pc(struct kprobe *p, struct pt_regs *regs)
+t16_emulate_pop_pc(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
{
register unsigned long pc asm("r8");
@@ -1216,7 +583,7 @@ t16_emulate_pop_pc(struct kprobe *p, struct pt_regs *regs)
"stmia %[regs], {r0-r7} \n\t"
"str r9, [%[regs], #13*4] \n\t"
: "=r" (pc)
- : [regs] "r" (regs), [fn] "r" (p->ainsn.insn_fn)
+ : [regs] "r" (regs), [fn] "r" (asi->insn_fn)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9",
"lr", "memory", "cc"
);
@@ -1224,8 +591,9 @@ t16_emulate_pop_pc(struct kprobe *p, struct pt_regs *regs)
bx_write_pc(pc, regs);
}
-static enum kprobe_insn __kprobes
-t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+static enum probes_insn __kprobes
+t16_decode_pop(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d)
{
/*
* To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}"
@@ -1239,231 +607,56 @@ t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi)
return INSN_GOOD;
}
-static const union decode_item t16_table_1011[] = {
- /* Miscellaneous 16-bit instructions */
-
- /* ADD (SP plus immediate) 1011 0000 0xxx xxxx */
- /* SUB (SP minus immediate) 1011 0000 1xxx xxxx */
- DECODE_SIMULATE (0xff00, 0xb000, t16_simulate_add_sp_imm),
-
- /* CBZ 1011 00x1 xxxx xxxx */
- /* CBNZ 1011 10x1 xxxx xxxx */
- DECODE_SIMULATE (0xf500, 0xb100, t16_simulate_cbz),
-
- /* SXTH 1011 0010 00xx xxxx */
- /* SXTB 1011 0010 01xx xxxx */
- /* UXTH 1011 0010 10xx xxxx */
- /* UXTB 1011 0010 11xx xxxx */
- /* REV 1011 1010 00xx xxxx */
- /* REV16 1011 1010 01xx xxxx */
- /* ??? 1011 1010 10xx xxxx */
- /* REVSH 1011 1010 11xx xxxx */
- DECODE_REJECT (0xffc0, 0xba80),
- DECODE_EMULATE (0xf500, 0xb000, t16_emulate_loregs_rwflags),
-
- /* PUSH 1011 010x xxxx xxxx */
- DECODE_CUSTOM (0xfe00, 0xb400, t16_decode_push),
- /* POP 1011 110x xxxx xxxx */
- DECODE_CUSTOM (0xfe00, 0xbc00, t16_decode_pop),
-
- /*
- * If-Then, and hints
- * 1011 1111 xxxx xxxx
- */
-
- /* YIELD 1011 1111 0001 0000 */
- DECODE_OR (0xffff, 0xbf10),
- /* SEV 1011 1111 0100 0000 */
- DECODE_EMULATE (0xffff, 0xbf40, kprobe_emulate_none),
- /* NOP 1011 1111 0000 0000 */
- /* WFE 1011 1111 0010 0000 */
- /* WFI 1011 1111 0011 0000 */
- DECODE_SIMULATE (0xffcf, 0xbf00, kprobe_simulate_nop),
- /* Unassigned hints 1011 1111 xxxx 0000 */
- DECODE_REJECT (0xff0f, 0xbf00),
- /* IT 1011 1111 xxxx xxxx */
- DECODE_CUSTOM (0xff00, 0xbf00, t16_decode_it),
-
- /* SETEND 1011 0110 010x xxxx */
- /* CPS 1011 0110 011x xxxx */
- /* BKPT 1011 1110 xxxx xxxx */
- /* And unallocated instructions... */
- DECODE_END
+const union decode_action kprobes_t16_actions[NUM_PROBES_T16_ACTIONS] = {
+ [PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm},
+ [PROBES_T16_CBZ] = {.handler = t16_simulate_cbz},
+ [PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags},
+ [PROBES_T16_PUSH] = {.decoder = t16_decode_push},
+ [PROBES_T16_POP] = {.decoder = t16_decode_pop},
+ [PROBES_T16_SEV] = {.handler = probes_emulate_none},
+ [PROBES_T16_WFE] = {.handler = probes_simulate_nop},
+ [PROBES_T16_IT] = {.decoder = t16_decode_it},
+ [PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags},
+ [PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags},
+ [PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags},
+ [PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal},
+ [PROBES_T16_BLX] = {.handler = t16_simulate_bxblx},
+ [PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs},
+ [PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags},
+ [PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative},
+ [PROBES_T16_ADR] = {.handler = t16_simulate_reladr},
+ [PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags},
+ [PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch},
+ [PROBES_T16_BRANCH] = {.handler = t16_simulate_branch},
};
-const union decode_item kprobe_decode_thumb16_table[] = {
-
- /*
- * Shift (immediate), add, subtract, move, and compare
- * 00xx xxxx xxxx xxxx
- */
-
- /* CMP (immediate) 0010 1xxx xxxx xxxx */
- DECODE_EMULATE (0xf800, 0x2800, t16_emulate_loregs_rwflags),
-
- /* ADD (register) 0001 100x xxxx xxxx */
- /* SUB (register) 0001 101x xxxx xxxx */
- /* LSL (immediate) 0000 0xxx xxxx xxxx */
- /* LSR (immediate) 0000 1xxx xxxx xxxx */
- /* ASR (immediate) 0001 0xxx xxxx xxxx */
- /* ADD (immediate, Thumb) 0001 110x xxxx xxxx */
- /* SUB (immediate, Thumb) 0001 111x xxxx xxxx */
- /* MOV (immediate) 0010 0xxx xxxx xxxx */
- /* ADD (immediate, Thumb) 0011 0xxx xxxx xxxx */
- /* SUB (immediate, Thumb) 0011 1xxx xxxx xxxx */
- DECODE_EMULATE (0xc000, 0x0000, t16_emulate_loregs_noitrwflags),
-
- /*
- * 16-bit Thumb data-processing instructions
- * 0100 00xx xxxx xxxx
- */
-
- /* TST (register) 0100 0010 00xx xxxx */
- DECODE_EMULATE (0xffc0, 0x4200, t16_emulate_loregs_rwflags),
- /* CMP (register) 0100 0010 10xx xxxx */
- /* CMN (register) 0100 0010 11xx xxxx */
- DECODE_EMULATE (0xff80, 0x4280, t16_emulate_loregs_rwflags),
- /* AND (register) 0100 0000 00xx xxxx */
- /* EOR (register) 0100 0000 01xx xxxx */
- /* LSL (register) 0100 0000 10xx xxxx */
- /* LSR (register) 0100 0000 11xx xxxx */
- /* ASR (register) 0100 0001 00xx xxxx */
- /* ADC (register) 0100 0001 01xx xxxx */
- /* SBC (register) 0100 0001 10xx xxxx */
- /* ROR (register) 0100 0001 11xx xxxx */
- /* RSB (immediate) 0100 0010 01xx xxxx */
- /* ORR (register) 0100 0011 00xx xxxx */
- /* MUL 0100 0011 00xx xxxx */
- /* BIC (register) 0100 0011 10xx xxxx */
- /* MVN (register) 0100 0011 10xx xxxx */
- DECODE_EMULATE (0xfc00, 0x4000, t16_emulate_loregs_noitrwflags),
-
- /*
- * Special data instructions and branch and exchange
- * 0100 01xx xxxx xxxx
- */
-
- /* BLX pc 0100 0111 1111 1xxx */
- DECODE_REJECT (0xfff8, 0x47f8),
-
- /* BX (register) 0100 0111 0xxx xxxx */
- /* BLX (register) 0100 0111 1xxx xxxx */
- DECODE_SIMULATE (0xff00, 0x4700, t16_simulate_bxblx),
-
- /* ADD pc, pc 0100 0100 1111 1111 */
- DECODE_REJECT (0xffff, 0x44ff),
-
- /* ADD (register) 0100 0100 xxxx xxxx */
- /* CMP (register) 0100 0101 xxxx xxxx */
- /* MOV (register) 0100 0110 xxxx xxxx */
- DECODE_CUSTOM (0xfc00, 0x4400, t16_decode_hiregs),
-
- /*
- * Load from Literal Pool
- * LDR (literal) 0100 1xxx xxxx xxxx
- */
- DECODE_SIMULATE (0xf800, 0x4800, t16_simulate_ldr_literal),
-
- /*
- * 16-bit Thumb Load/store instructions
- * 0101 xxxx xxxx xxxx
- * 011x xxxx xxxx xxxx
- * 100x xxxx xxxx xxxx
- */
-
- /* STR (register) 0101 000x xxxx xxxx */
- /* STRH (register) 0101 001x xxxx xxxx */
- /* STRB (register) 0101 010x xxxx xxxx */
- /* LDRSB (register) 0101 011x xxxx xxxx */
- /* LDR (register) 0101 100x xxxx xxxx */
- /* LDRH (register) 0101 101x xxxx xxxx */
- /* LDRB (register) 0101 110x xxxx xxxx */
- /* LDRSH (register) 0101 111x xxxx xxxx */
- /* STR (immediate, Thumb) 0110 0xxx xxxx xxxx */
- /* LDR (immediate, Thumb) 0110 1xxx xxxx xxxx */
- /* STRB (immediate, Thumb) 0111 0xxx xxxx xxxx */
- /* LDRB (immediate, Thumb) 0111 1xxx xxxx xxxx */
- DECODE_EMULATE (0xc000, 0x4000, t16_emulate_loregs_rwflags),
- /* STRH (immediate, Thumb) 1000 0xxx xxxx xxxx */
- /* LDRH (immediate, Thumb) 1000 1xxx xxxx xxxx */
- DECODE_EMULATE (0xf000, 0x8000, t16_emulate_loregs_rwflags),
- /* STR (immediate, Thumb) 1001 0xxx xxxx xxxx */
- /* LDR (immediate, Thumb) 1001 1xxx xxxx xxxx */
- DECODE_SIMULATE (0xf000, 0x9000, t16_simulate_ldrstr_sp_relative),
-
- /*
- * Generate PC-/SP-relative address
- * ADR (literal) 1010 0xxx xxxx xxxx
- * ADD (SP plus immediate) 1010 1xxx xxxx xxxx
- */
- DECODE_SIMULATE (0xf000, 0xa000, t16_simulate_reladr),
-
- /*
- * Miscellaneous 16-bit instructions
- * 1011 xxxx xxxx xxxx
- */
- DECODE_TABLE (0xf000, 0xb000, t16_table_1011),
-
- /* STM 1100 0xxx xxxx xxxx */
- /* LDM 1100 1xxx xxxx xxxx */
- DECODE_EMULATE (0xf000, 0xc000, t16_emulate_loregs_rwflags),
-
- /*
- * Conditional branch, and Supervisor Call
- */
-
- /* Permanently UNDEFINED 1101 1110 xxxx xxxx */
- /* SVC 1101 1111 xxxx xxxx */
- DECODE_REJECT (0xfe00, 0xde00),
-
- /* Conditional branch 1101 xxxx xxxx xxxx */
- DECODE_CUSTOM (0xf000, 0xd000, t16_decode_cond_branch),
-
- /*
- * Unconditional branch
- * B 1110 0xxx xxxx xxxx
- */
- DECODE_SIMULATE (0xf800, 0xe000, t16_simulate_branch),
-
- DECODE_END
+const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = {
+ [PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm},
+ [PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd},
+ [PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch},
+ [PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+ [PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+ [PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+ [PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+ [PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+ [PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,},
+ [PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags},
+ [PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags},
+ [PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+ [PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags},
+ [PROBES_T32_SEV] = {.handler = probes_emulate_none},
+ [PROBES_T32_WFE] = {.handler = probes_simulate_nop},
+ [PROBES_T32_MRS] = {.handler = t32_simulate_mrs},
+ [PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch},
+ [PROBES_T32_BRANCH] = {.handler = t32_simulate_branch},
+ [PROBES_T32_PLDI] = {.handler = probes_simulate_nop},
+ [PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal},
+ [PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr},
+ [PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+ [PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+ [PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags},
+ [PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+ [PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags},
+ [PROBES_T32_MUL_ADD_LONG] = {
+ .handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags},
};
-#ifdef CONFIG_ARM_KPROBES_TEST_MODULE
-EXPORT_SYMBOL_GPL(kprobe_decode_thumb16_table);
-#endif
-
-static unsigned long __kprobes thumb_check_cc(unsigned long cpsr)
-{
- if (unlikely(in_it_block(cpsr)))
- return kprobe_condition_checks[current_cond(cpsr)](cpsr);
- return true;
-}
-
-static void __kprobes thumb16_singlestep(struct kprobe *p, struct pt_regs *regs)
-{
- regs->ARM_pc += 2;
- p->ainsn.insn_handler(p, regs);
- regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
-}
-
-static void __kprobes thumb32_singlestep(struct kprobe *p, struct pt_regs *regs)
-{
- regs->ARM_pc += 4;
- p->ainsn.insn_handler(p, regs);
- regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
-}
-
-enum kprobe_insn __kprobes
-thumb16_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
-{
- asi->insn_singlestep = thumb16_singlestep;
- asi->insn_check_cc = thumb_check_cc;
- return kprobe_decode_insn(insn, asi, kprobe_decode_thumb16_table, true);
-}
-
-enum kprobe_insn __kprobes
-thumb32_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
-{
- asi->insn_singlestep = thumb32_singlestep;
- asi->insn_check_cc = thumb_check_cc;
- return kprobe_decode_insn(insn, asi, kprobe_decode_thumb32_table, true);
-}
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c
index a7b621ece23d..8795f9f819d5 100644
--- a/arch/arm/kernel/kprobes.c
+++ b/arch/arm/kernel/kprobes.c
@@ -27,8 +27,12 @@
#include <linux/stringify.h>
#include <asm/traps.h>
#include <asm/cacheflush.h>
+#include <linux/percpu.h>
+#include <linux/bug.h>
#include "kprobes.h"
+#include "probes-arm.h"
+#include "probes-thumb.h"
#include "patch.h"
#define MIN_STACK_SIZE(addr) \
@@ -54,6 +58,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
unsigned long addr = (unsigned long)p->addr;
bool thumb;
kprobe_decode_insn_t *decode_insn;
+ const union decode_action *actions;
int is;
if (in_exception_text(addr))
@@ -66,21 +71,25 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
if (is_wide_instruction(insn)) {
insn <<= 16;
insn |= ((u16 *)addr)[1];
- decode_insn = thumb32_kprobe_decode_insn;
- } else
- decode_insn = thumb16_kprobe_decode_insn;
+ decode_insn = thumb32_probes_decode_insn;
+ actions = kprobes_t32_actions;
+ } else {
+ decode_insn = thumb16_probes_decode_insn;
+ actions = kprobes_t16_actions;
+ }
#else /* !CONFIG_THUMB2_KERNEL */
thumb = false;
if (addr & 0x3)
return -EINVAL;
insn = *p->addr;
- decode_insn = arm_kprobe_decode_insn;
+ decode_insn = arm_probes_decode_insn;
+ actions = kprobes_arm_actions;
#endif
p->opcode = insn;
p->ainsn.insn = tmp_insn;
- switch ((*decode_insn)(insn, &p->ainsn)) {
+ switch ((*decode_insn)(insn, &p->ainsn, true, actions)) {
case INSN_REJECTED: /* not supported */
return -EINVAL;
@@ -92,7 +101,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
p->ainsn.insn[is] = tmp_insn[is];
flush_insns(p->ainsn.insn,
sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE);
- p->ainsn.insn_fn = (kprobe_insn_fn_t *)
+ p->ainsn.insn_fn = (probes_insn_fn_t *)
((uintptr_t)p->ainsn.insn | thumb);
break;
@@ -197,7 +206,7 @@ singlestep_skip(struct kprobe *p, struct pt_regs *regs)
static inline void __kprobes
singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
{
- p->ainsn.insn_singlestep(p, regs);
+ p->ainsn.insn_singlestep(p->opcode, &p->ainsn, regs);
}
/*
@@ -607,7 +616,7 @@ static struct undef_hook kprobes_arm_break_hook = {
int __init arch_init_kprobes()
{
- arm_kprobe_decode_init();
+ arm_probes_decode_init();
#ifdef CONFIG_THUMB2_KERNEL
register_undef_hook(&kprobes_thumb16_break_hook);
register_undef_hook(&kprobes_thumb32_break_hook);
diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h
index 38945f78f9f1..9a2712ecefc3 100644
--- a/arch/arm/kernel/kprobes.h
+++ b/arch/arm/kernel/kprobes.h
@@ -19,6 +19,8 @@
#ifndef _ARM_KERNEL_KPROBES_H
#define _ARM_KERNEL_KPROBES_H
+#include "probes.h"
+
/*
* These undefined instructions must be unique and
* reserved solely for kprobes' use.
@@ -27,402 +29,24 @@
#define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18
#define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018
+enum probes_insn __kprobes
+kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *h);
-enum kprobe_insn {
- INSN_REJECTED,
- INSN_GOOD,
- INSN_GOOD_NO_SLOT
-};
-
-typedef enum kprobe_insn (kprobe_decode_insn_t)(kprobe_opcode_t,
- struct arch_specific_insn *);
+typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t,
+ struct arch_probes_insn *,
+ bool,
+ const union decode_action *);
#ifdef CONFIG_THUMB2_KERNEL
-enum kprobe_insn thumb16_kprobe_decode_insn(kprobe_opcode_t,
- struct arch_specific_insn *);
-enum kprobe_insn thumb32_kprobe_decode_insn(kprobe_opcode_t,
- struct arch_specific_insn *);
+extern const union decode_action kprobes_t32_actions[];
+extern const union decode_action kprobes_t16_actions[];
#else /* !CONFIG_THUMB2_KERNEL */
-enum kprobe_insn arm_kprobe_decode_insn(kprobe_opcode_t,
- struct arch_specific_insn *);
-#endif
-
-void __init arm_kprobe_decode_init(void);
-
-extern kprobe_check_cc * const kprobe_condition_checks[16];
-
-
-#if __LINUX_ARM_ARCH__ >= 7
-
-/* str_pc_offset is architecturally defined from ARMv7 onwards */
-#define str_pc_offset 8
-#define find_str_pc_offset()
-
-#else /* __LINUX_ARM_ARCH__ < 7 */
-
-/* We need a run-time check to determine str_pc_offset */
-extern int str_pc_offset;
-void __init find_str_pc_offset(void);
+extern const union decode_action kprobes_arm_actions[];
#endif
-
-/*
- * Update ITSTATE after normal execution of an IT block instruction.
- *
- * The 8 IT state bits are split into two parts in CPSR:
- * ITSTATE<1:0> are in CPSR<26:25>
- * ITSTATE<7:2> are in CPSR<15:10>
- */
-static inline unsigned long it_advance(unsigned long cpsr)
- {
- if ((cpsr & 0x06000400) == 0) {
- /* ITSTATE<2:0> == 0 means end of IT block, so clear IT state */
- cpsr &= ~PSR_IT_MASK;
- } else {
- /* We need to shift left ITSTATE<4:0> */
- const unsigned long mask = 0x06001c00; /* Mask ITSTATE<4:0> */
- unsigned long it = cpsr & mask;
- it <<= 1;
- it |= it >> (27 - 10); /* Carry ITSTATE<2> to correct place */
- it &= mask;
- cpsr &= ~mask;
- cpsr |= it;
- }
- return cpsr;
-}
-
-static inline void __kprobes bx_write_pc(long pcv, struct pt_regs *regs)
-{
- long cpsr = regs->ARM_cpsr;
- if (pcv & 0x1) {
- cpsr |= PSR_T_BIT;
- pcv &= ~0x1;
- } else {
- cpsr &= ~PSR_T_BIT;
- pcv &= ~0x2; /* Avoid UNPREDICTABLE address allignment */
- }
- regs->ARM_cpsr = cpsr;
- regs->ARM_pc = pcv;
-}
-
-
-#if __LINUX_ARM_ARCH__ >= 6
-
-/* Kernels built for >= ARMv6 should never run on <= ARMv5 hardware, so... */
-#define load_write_pc_interworks true
-#define test_load_write_pc_interworking()
-
-#else /* __LINUX_ARM_ARCH__ < 6 */
-
-/* We need run-time testing to determine if load_write_pc() should interwork. */
-extern bool load_write_pc_interworks;
-void __init test_load_write_pc_interworking(void);
-
-#endif
-
-static inline void __kprobes load_write_pc(long pcv, struct pt_regs *regs)
-{
- if (load_write_pc_interworks)
- bx_write_pc(pcv, regs);
- else
- regs->ARM_pc = pcv;
-}
-
-
-#if __LINUX_ARM_ARCH__ >= 7
-
-#define alu_write_pc_interworks true
-#define test_alu_write_pc_interworking()
-
-#elif __LINUX_ARM_ARCH__ <= 5
-
-/* Kernels built for <= ARMv5 should never run on >= ARMv6 hardware, so... */
-#define alu_write_pc_interworks false
-#define test_alu_write_pc_interworking()
-
-#else /* __LINUX_ARM_ARCH__ == 6 */
-
-/* We could be an ARMv6 binary on ARMv7 hardware so we need a run-time check. */
-extern bool alu_write_pc_interworks;
-void __init test_alu_write_pc_interworking(void);
-
-#endif /* __LINUX_ARM_ARCH__ == 6 */
-
-static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
-{
- if (alu_write_pc_interworks)
- bx_write_pc(pcv, regs);
- else
- regs->ARM_pc = pcv;
-}
-
-
-void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs);
-void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs);
-
-enum kprobe_insn __kprobes
-kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi);
-
-/*
- * Test if load/store instructions writeback the address register.
- * if P (bit 24) == 0 or W (bit 21) == 1
- */
-#define is_writeback(insn) ((insn ^ 0x01000000) & 0x01200000)
-
-/*
- * The following definitions and macros are used to build instruction
- * decoding tables for use by kprobe_decode_insn.
- *
- * These tables are a concatenation of entries each of which consist of one of
- * the decode_* structs. All of the fields in every type of decode structure
- * are of the union type decode_item, therefore the entire decode table can be
- * viewed as an array of these and declared like:
- *
- * static const union decode_item table_name[] = {};
- *
- * In order to construct each entry in the table, macros are used to
- * initialise a number of sequential decode_item values in a layout which
- * matches the relevant struct. E.g. DECODE_SIMULATE initialise a struct
- * decode_simulate by initialising four decode_item objects like this...
- *
- * {.bits = _type},
- * {.bits = _mask},
- * {.bits = _value},
- * {.handler = _handler},
- *
- * Initialising a specified member of the union means that the compiler
- * will produce a warning if the argument is of an incorrect type.
- *
- * Below is a list of each of the macros used to initialise entries and a
- * description of the action performed when that entry is matched to an
- * instruction. A match is found when (instruction & mask) == value.
- *
- * DECODE_TABLE(mask, value, table)
- * Instruction decoding jumps to parsing the new sub-table 'table'.
- *
- * DECODE_CUSTOM(mask, value, decoder)
- * The custom function 'decoder' is called to the complete decoding
- * of an instruction.
- *
- * DECODE_SIMULATE(mask, value, handler)
- * Set the probes instruction handler to 'handler', this will be used
- * to simulate the instruction when the probe is hit. Decoding returns
- * with INSN_GOOD_NO_SLOT.
- *
- * DECODE_EMULATE(mask, value, handler)
- * Set the probes instruction handler to 'handler', this will be used
- * to emulate the instruction when the probe is hit. The modified
- * instruction (see below) is placed in the probes instruction slot so it
- * may be called by the emulation code. Decoding returns with INSN_GOOD.
- *
- * DECODE_REJECT(mask, value)
- * Instruction decoding fails with INSN_REJECTED
- *
- * DECODE_OR(mask, value)
- * This allows the mask/value test of multiple table entries to be
- * logically ORed. Once an 'or' entry is matched the decoding action to
- * be performed is that of the next entry which isn't an 'or'. E.g.
- *
- * DECODE_OR (mask1, value1)
- * DECODE_OR (mask2, value2)
- * DECODE_SIMULATE (mask3, value3, simulation_handler)
- *
- * This means that if any of the three mask/value pairs match the
- * instruction being decoded, then 'simulation_handler' will be used
- * for it.
- *
- * Both the SIMULATE and EMULATE macros have a second form which take an
- * additional 'regs' argument.
- *
- * DECODE_SIMULATEX(mask, value, handler, regs)
- * DECODE_EMULATEX (mask, value, handler, regs)
- *
- * These are used to specify what kind of CPU register is encoded in each of the
- * least significant 5 nibbles of the instruction being decoded. The regs value
- * is specified using the REGS macro, this takes any of the REG_TYPE_* values
- * from enum decode_reg_type as arguments; only the '*' part of the name is
- * given. E.g.
- *
- * REGS(0, ANY, NOPC, 0, ANY)
- *
- * This indicates an instruction is encoded like:
- *
- * bits 19..16 ignore
- * bits 15..12 any register allowed here
- * bits 11.. 8 any register except PC allowed here
- * bits 7.. 4 ignore
- * bits 3.. 0 any register allowed here
- *
- * This register specification is checked after a decode table entry is found to
- * match an instruction (through the mask/value test). Any invalid register then
- * found in the instruction will cause decoding to fail with INSN_REJECTED. In
- * the above example this would happen if bits 11..8 of the instruction were
- * 1111, indicating R15 or PC.
- *
- * As well as checking for legal combinations of registers, this data is also
- * used to modify the registers encoded in the instructions so that an
- * emulation routines can use it. (See decode_regs() and INSN_NEW_BITS.)
- *
- * Here is a real example which matches ARM instructions of the form
- * "AND <Rd>,<Rn>,<Rm>,<shift> <Rs>"
- *
- * DECODE_EMULATEX (0x0e000090, 0x00000010, emulate_rd12rn16rm0rs8_rwflags,
- * REGS(ANY, ANY, NOPC, 0, ANY)),
- * ^ ^ ^ ^
- * Rn Rd Rs Rm
- *
- * Decoding the instruction "AND R4, R5, R6, ASL R15" will be rejected because
- * Rs == R15
- *
- * Decoding the instruction "AND R4, R5, R6, ASL R7" will be accepted and the
- * instruction will be modified to "AND R0, R2, R3, ASL R1" and then placed into
- * the kprobes instruction slot. This can then be called later by the handler
- * function emulate_rd12rn16rm0rs8_rwflags in order to simulate the instruction.
- */
-
-enum decode_type {
- DECODE_TYPE_END,
- DECODE_TYPE_TABLE,
- DECODE_TYPE_CUSTOM,
- DECODE_TYPE_SIMULATE,
- DECODE_TYPE_EMULATE,
- DECODE_TYPE_OR,
- DECODE_TYPE_REJECT,
- NUM_DECODE_TYPES /* Must be last enum */
-};
-
-#define DECODE_TYPE_BITS 4
-#define DECODE_TYPE_MASK ((1 << DECODE_TYPE_BITS) - 1)
-
-enum decode_reg_type {
- REG_TYPE_NONE = 0, /* Not a register, ignore */
- REG_TYPE_ANY, /* Any register allowed */
- REG_TYPE_SAMEAS16, /* Register should be same as that at bits 19..16 */
- REG_TYPE_SP, /* Register must be SP */
- REG_TYPE_PC, /* Register must be PC */
- REG_TYPE_NOSP, /* Register must not be SP */
- REG_TYPE_NOSPPC, /* Register must not be SP or PC */
- REG_TYPE_NOPC, /* Register must not be PC */
- REG_TYPE_NOPCWB, /* No PC if load/store write-back flag also set */
-
- /* The following types are used when the encoding for PC indicates
- * another instruction form. This distiction only matters for test
- * case coverage checks.
- */
- REG_TYPE_NOPCX, /* Register must not be PC */
- REG_TYPE_NOSPPCX, /* Register must not be SP or PC */
-
- /* Alias to allow '0' arg to be used in REGS macro. */
- REG_TYPE_0 = REG_TYPE_NONE
-};
-
-#define REGS(r16, r12, r8, r4, r0) \
- ((REG_TYPE_##r16) << 16) + \
- ((REG_TYPE_##r12) << 12) + \
- ((REG_TYPE_##r8) << 8) + \
- ((REG_TYPE_##r4) << 4) + \
- (REG_TYPE_##r0)
-
-union decode_item {
- u32 bits;
- const union decode_item *table;
- kprobe_insn_handler_t *handler;
- kprobe_decode_insn_t *decoder;
-};
-
-
-#define DECODE_END \
- {.bits = DECODE_TYPE_END}
-
-
-struct decode_header {
- union decode_item type_regs;
- union decode_item mask;
- union decode_item value;
-};
-
-#define DECODE_HEADER(_type, _mask, _value, _regs) \
- {.bits = (_type) | ((_regs) << DECODE_TYPE_BITS)}, \
- {.bits = (_mask)}, \
- {.bits = (_value)}
-
-
-struct decode_table {
- struct decode_header header;
- union decode_item table;
-};
-
-#define DECODE_TABLE(_mask, _value, _table) \
- DECODE_HEADER(DECODE_TYPE_TABLE, _mask, _value, 0), \
- {.table = (_table)}
-
-
-struct decode_custom {
- struct decode_header header;
- union decode_item decoder;
-};
-
-#define DECODE_CUSTOM(_mask, _value, _decoder) \
- DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0), \
- {.decoder = (_decoder)}
-
-
-struct decode_simulate {
- struct decode_header header;
- union decode_item handler;
-};
-
-#define DECODE_SIMULATEX(_mask, _value, _handler, _regs) \
- DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs), \
- {.handler = (_handler)}
-
-#define DECODE_SIMULATE(_mask, _value, _handler) \
- DECODE_SIMULATEX(_mask, _value, _handler, 0)
-
-
-struct decode_emulate {
- struct decode_header header;
- union decode_item handler;
-};
-
-#define DECODE_EMULATEX(_mask, _value, _handler, _regs) \
- DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs), \
- {.handler = (_handler)}
-
-#define DECODE_EMULATE(_mask, _value, _handler) \
- DECODE_EMULATEX(_mask, _value, _handler, 0)
-
-
-struct decode_or {
- struct decode_header header;
-};
-
-#define DECODE_OR(_mask, _value) \
- DECODE_HEADER(DECODE_TYPE_OR, _mask, _value, 0)
-
-
-struct decode_reject {
- struct decode_header header;
-};
-
-#define DECODE_REJECT(_mask, _value) \
- DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0)
-
-
-#ifdef CONFIG_THUMB2_KERNEL
-extern const union decode_item kprobe_decode_thumb16_table[];
-extern const union decode_item kprobe_decode_thumb32_table[];
-#else
-extern const union decode_item kprobe_decode_arm_table[];
-#endif
-
-
-int kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
- const union decode_item *table, bool thumb16);
-
-
#endif /* _ARM_KERNEL_KPROBES_H */
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 789d846a9184..a6bc431cde70 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -16,6 +16,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/uaccess.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
#include <asm/irq_regs.h>
#include <asm/pmu.h>
@@ -205,6 +207,8 @@ armpmu_del(struct perf_event *event, int flags)
armpmu_stop(event, PERF_EF_UPDATE);
hw_events->events[idx] = NULL;
clear_bit(idx, hw_events->used_mask);
+ if (armpmu->clear_event_idx)
+ armpmu->clear_event_idx(hw_events, event);
perf_event_update_userpage(event);
}
@@ -295,14 +299,27 @@ validate_group(struct perf_event *event)
static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
{
- struct arm_pmu *armpmu = (struct arm_pmu *) dev;
- struct platform_device *plat_device = armpmu->plat_device;
- struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev);
+ struct arm_pmu *armpmu;
+ struct platform_device *plat_device;
+ struct arm_pmu_platdata *plat;
+ int ret;
+ u64 start_clock, finish_clock;
+ if (irq_is_percpu(irq))
+ dev = *(void **)dev;
+ armpmu = dev;
+ plat_device = armpmu->plat_device;
+ plat = dev_get_platdata(&plat_device->dev);
+
+ start_clock = sched_clock();
if (plat && plat->handle_irq)
- return plat->handle_irq(irq, dev, armpmu->handle_irq);
+ ret = plat->handle_irq(irq, dev, armpmu->handle_irq);
else
- return armpmu->handle_irq(irq, dev);
+ ret = armpmu->handle_irq(irq, dev);
+ finish_clock = sched_clock();
+
+ perf_sample_event_took(finish_clock - start_clock);
+ return ret;
}
static void
diff --git a/arch/arm/kernel/perf_event_cpu.c b/arch/arm/kernel/perf_event_cpu.c
index 20d553c9f5e2..51798d7854ac 100644
--- a/arch/arm/kernel/perf_event_cpu.c
+++ b/arch/arm/kernel/perf_event_cpu.c
@@ -25,6 +25,8 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
#include <asm/cputype.h>
#include <asm/irq_regs.h>
@@ -33,6 +35,7 @@
/* Set at runtime when we know what CPU type we are. */
static struct arm_pmu *cpu_pmu;
+static DEFINE_PER_CPU(struct arm_pmu *, percpu_pmu);
static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events);
static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask);
static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events);
@@ -71,6 +74,26 @@ static struct pmu_hw_events *cpu_pmu_get_cpu_events(void)
return this_cpu_ptr(&cpu_hw_events);
}
+static void cpu_pmu_enable_percpu_irq(void *data)
+{
+ struct arm_pmu *cpu_pmu = data;
+ struct platform_device *pmu_device = cpu_pmu->plat_device;
+ int irq = platform_get_irq(pmu_device, 0);
+
+ enable_percpu_irq(irq, IRQ_TYPE_NONE);
+ cpumask_set_cpu(smp_processor_id(), &cpu_pmu->active_irqs);
+}
+
+static void cpu_pmu_disable_percpu_irq(void *data)
+{
+ struct arm_pmu *cpu_pmu = data;
+ struct platform_device *pmu_device = cpu_pmu->plat_device;
+ int irq = platform_get_irq(pmu_device, 0);
+
+ cpumask_clear_cpu(smp_processor_id(), &cpu_pmu->active_irqs);
+ disable_percpu_irq(irq);
+}
+
static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
{
int i, irq, irqs;
@@ -78,12 +101,18 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
irqs = min(pmu_device->num_resources, num_possible_cpus());
- for (i = 0; i < irqs; ++i) {
- if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs))
- continue;
- irq = platform_get_irq(pmu_device, i);
- if (irq >= 0)
- free_irq(irq, cpu_pmu);
+ irq = platform_get_irq(pmu_device, 0);
+ if (irq >= 0 && irq_is_percpu(irq)) {
+ on_each_cpu(cpu_pmu_disable_percpu_irq, cpu_pmu, 1);
+ free_percpu_irq(irq, &percpu_pmu);
+ } else {
+ for (i = 0; i < irqs; ++i) {
+ if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs))
+ continue;
+ irq = platform_get_irq(pmu_device, i);
+ if (irq >= 0)
+ free_irq(irq, cpu_pmu);
+ }
}
}
@@ -101,33 +130,44 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
return -ENODEV;
}
- for (i = 0; i < irqs; ++i) {
- err = 0;
- irq = platform_get_irq(pmu_device, i);
- if (irq < 0)
- continue;
-
- /*
- * If we have a single PMU interrupt that we can't shift,
- * assume that we're running on a uniprocessor machine and
- * continue. Otherwise, continue without this interrupt.
- */
- if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) {
- pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n",
- irq, i);
- continue;
- }
-
- err = request_irq(irq, handler,
- IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu",
- cpu_pmu);
+ irq = platform_get_irq(pmu_device, 0);
+ if (irq >= 0 && irq_is_percpu(irq)) {
+ err = request_percpu_irq(irq, handler, "arm-pmu", &percpu_pmu);
if (err) {
pr_err("unable to request IRQ%d for ARM PMU counters\n",
irq);
return err;
}
-
- cpumask_set_cpu(i, &cpu_pmu->active_irqs);
+ on_each_cpu(cpu_pmu_enable_percpu_irq, cpu_pmu, 1);
+ } else {
+ for (i = 0; i < irqs; ++i) {
+ err = 0;
+ irq = platform_get_irq(pmu_device, i);
+ if (irq < 0)
+ continue;
+
+ /*
+ * If we have a single PMU interrupt that we can't shift,
+ * assume that we're running on a uniprocessor machine and
+ * continue. Otherwise, continue without this interrupt.
+ */
+ if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) {
+ pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n",
+ irq, i);
+ continue;
+ }
+
+ err = request_irq(irq, handler,
+ IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu",
+ cpu_pmu);
+ if (err) {
+ pr_err("unable to request IRQ%d for ARM PMU counters\n",
+ irq);
+ return err;
+ }
+
+ cpumask_set_cpu(i, &cpu_pmu->active_irqs);
+ }
}
return 0;
@@ -141,6 +181,7 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu)
events->events = per_cpu(hw_events, cpu);
events->used_mask = per_cpu(used_mask, cpu);
raw_spin_lock_init(&events->pmu_lock);
+ per_cpu(percpu_pmu, cpu) = cpu_pmu;
}
cpu_pmu->get_hw_events = cpu_pmu_get_cpu_events;
@@ -181,6 +222,7 @@ static struct notifier_block cpu_pmu_hotplug_notifier = {
*/
static struct of_device_id cpu_pmu_of_device_ids[] = {
{.compatible = "arm,cortex-a15-pmu", .data = armv7_a15_pmu_init},
+ {.compatible = "arm,cortex-a12-pmu", .data = armv7_a12_pmu_init},
{.compatible = "arm,cortex-a9-pmu", .data = armv7_a9_pmu_init},
{.compatible = "arm,cortex-a8-pmu", .data = armv7_a8_pmu_init},
{.compatible = "arm,cortex-a7-pmu", .data = armv7_a7_pmu_init},
@@ -188,6 +230,7 @@ static struct of_device_id cpu_pmu_of_device_ids[] = {
{.compatible = "arm,arm11mpcore-pmu", .data = armv6mpcore_pmu_init},
{.compatible = "arm,arm1176-pmu", .data = armv6pmu_init},
{.compatible = "arm,arm1136-pmu", .data = armv6pmu_init},
+ {.compatible = "qcom,krait-pmu", .data = krait_pmu_init},
{},
};
@@ -225,15 +268,6 @@ static int probe_current_pmu(struct arm_pmu *pmu)
case ARM_CPU_PART_CORTEX_A9:
ret = armv7_a9_pmu_init(pmu);
break;
- case ARM_CPU_PART_CORTEX_A5:
- ret = armv7_a5_pmu_init(pmu);
- break;
- case ARM_CPU_PART_CORTEX_A15:
- ret = armv7_a15_pmu_init(pmu);
- break;
- case ARM_CPU_PART_CORTEX_A7:
- ret = armv7_a7_pmu_init(pmu);
- break;
}
/* Intel CPUs [xscale]. */
} else if (implementor == ARM_CPU_IMP_INTEL) {
@@ -270,6 +304,9 @@ static int cpu_pmu_device_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ cpu_pmu = pmu;
+ cpu_pmu->plat_device = pdev;
+
if (node && (of_id = of_match_node(cpu_pmu_of_device_ids, pdev->dev.of_node))) {
init_fn = of_id->data;
ret = init_fn(pmu);
@@ -282,8 +319,6 @@ static int cpu_pmu_device_probe(struct platform_device *pdev)
goto out_free;
}
- cpu_pmu = pmu;
- cpu_pmu->plat_device = pdev;
cpu_pmu_init(cpu_pmu);
ret = armpmu_register(cpu_pmu, PERF_TYPE_RAW);
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 039cffb053a7..f4ef3981ed02 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -18,6 +18,10 @@
#ifdef CONFIG_CPU_V7
+#include <asm/cp15.h>
+#include <asm/vfp.h>
+#include "../vfp/vfpinstr.h"
+
/*
* Common ARMv7 event types
*
@@ -109,6 +113,33 @@ enum armv7_a15_perf_types {
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,
+
+ ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_READ = 0x50,
+ ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_WRITE = 0x51,
+
+ ARMV7_A12_PERFCTR_PC_WRITE_SPEC = 0x76,
+
+ 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,
+
+ KRAIT_PERFCTR_L1_ICACHE_ACCESS = 0x10011,
+ KRAIT_PERFCTR_L1_ICACHE_MISS = 0x10010,
+
+ KRAIT_PERFCTR_L1_ITLB_ACCESS = 0x12222,
+ KRAIT_PERFCTR_L1_DTLB_ACCESS = 0x12210,
+};
+
/*
* Cortex-A8 HW events mapping
*
@@ -732,6 +763,262 @@ static const unsigned armv7_a7_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
};
/*
+ * Cortex-A12 HW events mapping
+ */
+static const unsigned armv7_a12_perf_map[PERF_COUNT_HW_MAX] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES,
+ [PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERFCTR_INSTR_EXECUTED,
+ [PERF_COUNT_HW_CACHE_REFERENCES] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
+ [PERF_COUNT_HW_CACHE_MISSES] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_A12_PERFCTR_PC_WRITE_SPEC,
+ [PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
+ [PERF_COUNT_HW_BUS_CYCLES] = ARMV7_PERFCTR_BUS_CYCLES,
+ [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = HW_OP_UNSUPPORTED,
+ [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = HW_OP_UNSUPPORTED,
+};
+
+static const unsigned armv7_a12_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+ [C(L1D)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = ARMV7_A12_PERFCTR_L1_DCACHE_ACCESS_READ,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = ARMV7_A12_PERFCTR_L1_DCACHE_ACCESS_WRITE,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+ [C(L1I)] = {
+ /*
+ * Not all performance counters differentiate between read
+ * and write accesses/misses so we're not always strictly
+ * correct, but it's the best we can do. Writes and reads get
+ * combined in these cases.
+ */
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_ICACHE_ACCESS,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_L1_ICACHE_REFILL,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+ [C(LL)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_READ,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_L2_CACHE_REFILL,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_WRITE,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_L2_CACHE_REFILL,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+ [C(DTLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_DTLB_REFILL,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_DTLB_REFILL,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = ARMV7_A12_PERFCTR_PF_TLB_REFILL,
+ },
+ },
+ [C(ITLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_ITLB_REFILL,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_ITLB_REFILL,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+ [C(BPU)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = ARMV7_PERFCTR_PC_BRANCH_PRED,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = ARMV7_PERFCTR_PC_BRANCH_PRED,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+ [C(NODE)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+};
+
+/*
+ * Krait HW events mapping
+ */
+static const unsigned krait_perf_map[PERF_COUNT_HW_MAX] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES,
+ [PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERFCTR_INSTR_EXECUTED,
+ [PERF_COUNT_HW_CACHE_REFERENCES] = HW_OP_UNSUPPORTED,
+ [PERF_COUNT_HW_CACHE_MISSES] = HW_OP_UNSUPPORTED,
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE,
+ [PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
+ [PERF_COUNT_HW_BUS_CYCLES] = ARMV7_PERFCTR_CLOCK_CYCLES,
+};
+
+static const unsigned krait_perf_map_no_branch[PERF_COUNT_HW_MAX] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES,
+ [PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERFCTR_INSTR_EXECUTED,
+ [PERF_COUNT_HW_CACHE_REFERENCES] = HW_OP_UNSUPPORTED,
+ [PERF_COUNT_HW_CACHE_MISSES] = HW_OP_UNSUPPORTED,
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = HW_OP_UNSUPPORTED,
+ [PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
+ [PERF_COUNT_HW_BUS_CYCLES] = ARMV7_PERFCTR_CLOCK_CYCLES,
+};
+
+static const unsigned krait_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+ [C(L1D)] = {
+ /*
+ * The performance counters don't differentiate between read
+ * and write accesses/misses so this isn't strictly correct,
+ * but it's the best we can do. Writes and reads get
+ * combined.
+ */
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+ [C(L1I)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = KRAIT_PERFCTR_L1_ICACHE_ACCESS,
+ [C(RESULT_MISS)] = KRAIT_PERFCTR_L1_ICACHE_MISS,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+ [C(LL)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+ [C(DTLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = KRAIT_PERFCTR_L1_DTLB_ACCESS,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = KRAIT_PERFCTR_L1_DTLB_ACCESS,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+ [C(ITLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = KRAIT_PERFCTR_L1_ITLB_ACCESS,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = KRAIT_PERFCTR_L1_ITLB_ACCESS,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+ [C(BPU)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = ARMV7_PERFCTR_PC_BRANCH_PRED,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = ARMV7_PERFCTR_PC_BRANCH_PRED,
+ [C(RESULT_MISS)] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+ [C(NODE)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
+ [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
+ },
+ },
+};
+
+/*
* Perf Events' indices
*/
#define ARMV7_IDX_CYCLE_COUNTER 0
@@ -1212,6 +1499,24 @@ static int armv7_a7_map_event(struct perf_event *event)
&armv7_a7_perf_cache_map, 0xFF);
}
+static int armv7_a12_map_event(struct perf_event *event)
+{
+ return armpmu_map_event(event, &armv7_a12_perf_map,
+ &armv7_a12_perf_cache_map, 0xFF);
+}
+
+static int krait_map_event(struct perf_event *event)
+{
+ return armpmu_map_event(event, &krait_perf_map,
+ &krait_perf_cache_map, 0xFFFFF);
+}
+
+static int krait_map_event_no_branch(struct perf_event *event)
+{
+ return armpmu_map_event(event, &krait_perf_map_no_branch,
+ &krait_perf_cache_map, 0xFFFFF);
+}
+
static void armv7pmu_init(struct arm_pmu *cpu_pmu)
{
cpu_pmu->handle_irq = armv7pmu_handle_irq;
@@ -1283,6 +1588,408 @@ static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
return 0;
}
+
+static int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu)
+{
+ armv7pmu_init(cpu_pmu);
+ cpu_pmu->name = "ARMv7 Cortex-A12";
+ cpu_pmu->map_event = armv7_a12_map_event;
+ cpu_pmu->num_events = armv7_read_num_pmnc_events();
+ cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
+ return 0;
+}
+
+/*
+ * Krait Performance Monitor Region Event Selection Register (PMRESRn)
+ *
+ * 31 30 24 16 8 0
+ * +--------------------------------+
+ * PMRESR0 | EN | CC | CC | CC | CC | N = 1, R = 0
+ * +--------------------------------+
+ * PMRESR1 | EN | CC | CC | CC | CC | N = 1, R = 1
+ * +--------------------------------+
+ * PMRESR2 | EN | CC | CC | CC | CC | N = 1, R = 2
+ * +--------------------------------+
+ * VPMRESR0 | EN | CC | CC | CC | CC | N = 2, R = ?
+ * +--------------------------------+
+ * EN | G=3 | G=2 | G=1 | G=0
+ *
+ * Event Encoding:
+ *
+ * hwc->config_base = 0xNRCCG
+ *
+ * N = prefix, 1 for Krait CPU (PMRESRn), 2 for Venum VFP (VPMRESR)
+ * R = region register
+ * CC = class of events the group G is choosing from
+ * G = group or particular event
+ *
+ * Example: 0x12021 is a Krait CPU event in PMRESR2's group 1 with code 2
+ *
+ * A region (R) corresponds to a piece of the CPU (execution unit, instruction
+ * unit, etc.) while the event code (CC) corresponds to a particular class of
+ * events (interrupts for example). An event code is broken down into
+ * groups (G) that can be mapped into the PMU (irq, fiqs, and irq+fiqs for
+ * example).
+ */
+
+#define KRAIT_EVENT (1 << 16)
+#define VENUM_EVENT (2 << 16)
+#define KRAIT_EVENT_MASK (KRAIT_EVENT | VENUM_EVENT)
+#define PMRESRn_EN BIT(31)
+
+static u32 krait_read_pmresrn(int n)
+{
+ u32 val;
+
+ switch (n) {
+ case 0:
+ asm volatile("mrc p15, 1, %0, c9, c15, 0" : "=r" (val));
+ break;
+ case 1:
+ asm volatile("mrc p15, 1, %0, c9, c15, 1" : "=r" (val));
+ break;
+ case 2:
+ asm volatile("mrc p15, 1, %0, c9, c15, 2" : "=r" (val));
+ break;
+ default:
+ BUG(); /* Should be validated in krait_pmu_get_event_idx() */
+ }
+
+ return val;
+}
+
+static void krait_write_pmresrn(int n, u32 val)
+{
+ switch (n) {
+ case 0:
+ asm volatile("mcr p15, 1, %0, c9, c15, 0" : : "r" (val));
+ break;
+ case 1:
+ asm volatile("mcr p15, 1, %0, c9, c15, 1" : : "r" (val));
+ break;
+ case 2:
+ asm volatile("mcr p15, 1, %0, c9, c15, 2" : : "r" (val));
+ break;
+ default:
+ BUG(); /* Should be validated in krait_pmu_get_event_idx() */
+ }
+}
+
+static u32 krait_read_vpmresr0(void)
+{
+ u32 val;
+ asm volatile("mrc p10, 7, %0, c11, c0, 0" : "=r" (val));
+ return val;
+}
+
+static void krait_write_vpmresr0(u32 val)
+{
+ asm volatile("mcr p10, 7, %0, c11, c0, 0" : : "r" (val));
+}
+
+static void krait_pre_vpmresr0(u32 *venum_orig_val, u32 *fp_orig_val)
+{
+ u32 venum_new_val;
+ u32 fp_new_val;
+
+ BUG_ON(preemptible());
+ /* CPACR Enable CP10 and CP11 access */
+ *venum_orig_val = get_copro_access();
+ venum_new_val = *venum_orig_val | CPACC_SVC(10) | CPACC_SVC(11);
+ set_copro_access(venum_new_val);
+
+ /* Enable FPEXC */
+ *fp_orig_val = fmrx(FPEXC);
+ fp_new_val = *fp_orig_val | FPEXC_EN;
+ fmxr(FPEXC, fp_new_val);
+}
+
+static void krait_post_vpmresr0(u32 venum_orig_val, u32 fp_orig_val)
+{
+ BUG_ON(preemptible());
+ /* Restore FPEXC */
+ fmxr(FPEXC, fp_orig_val);
+ isb();
+ /* Restore CPACR */
+ set_copro_access(venum_orig_val);
+}
+
+static u32 krait_get_pmresrn_event(unsigned int region)
+{
+ static const u32 pmresrn_table[] = { KRAIT_PMRESR0_GROUP0,
+ KRAIT_PMRESR1_GROUP0,
+ KRAIT_PMRESR2_GROUP0 };
+ return pmresrn_table[region];
+}
+
+static void krait_evt_setup(int idx, u32 config_base)
+{
+ u32 val;
+ u32 mask;
+ u32 vval, fval;
+ unsigned int region;
+ unsigned int group;
+ unsigned int code;
+ unsigned int group_shift;
+ bool venum_event;
+
+ venum_event = !!(config_base & VENUM_EVENT);
+ region = (config_base >> 12) & 0xf;
+ code = (config_base >> 4) & 0xff;
+ group = (config_base >> 0) & 0xf;
+
+ group_shift = group * 8;
+ mask = 0xff << group_shift;
+
+ /* Configure evtsel for the region and group */
+ if (venum_event)
+ val = KRAIT_VPMRESR0_GROUP0;
+ else
+ val = krait_get_pmresrn_event(region);
+ val += group;
+ /* Mix in mode-exclusion bits */
+ val |= config_base & (ARMV7_EXCLUDE_USER | ARMV7_EXCLUDE_PL1);
+ armv7_pmnc_write_evtsel(idx, val);
+
+ asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (0));
+
+ if (venum_event) {
+ krait_pre_vpmresr0(&vval, &fval);
+ val = krait_read_vpmresr0();
+ val &= ~mask;
+ val |= code << group_shift;
+ val |= PMRESRn_EN;
+ krait_write_vpmresr0(val);
+ krait_post_vpmresr0(vval, fval);
+ } else {
+ val = krait_read_pmresrn(region);
+ val &= ~mask;
+ val |= code << group_shift;
+ val |= PMRESRn_EN;
+ krait_write_pmresrn(region, val);
+ }
+}
+
+static u32 krait_clear_pmresrn_group(u32 val, int group)
+{
+ u32 mask;
+ int group_shift;
+
+ group_shift = group * 8;
+ mask = 0xff << group_shift;
+ val &= ~mask;
+
+ /* Don't clear enable bit if entire region isn't disabled */
+ if (val & ~PMRESRn_EN)
+ return val |= PMRESRn_EN;
+
+ return 0;
+}
+
+static void krait_clearpmu(u32 config_base)
+{
+ u32 val;
+ u32 vval, fval;
+ unsigned int region;
+ unsigned int group;
+ bool venum_event;
+
+ venum_event = !!(config_base & VENUM_EVENT);
+ region = (config_base >> 12) & 0xf;
+ group = (config_base >> 0) & 0xf;
+
+ if (venum_event) {
+ krait_pre_vpmresr0(&vval, &fval);
+ val = krait_read_vpmresr0();
+ val = krait_clear_pmresrn_group(val, group);
+ krait_write_vpmresr0(val);
+ krait_post_vpmresr0(vval, fval);
+ } else {
+ val = krait_read_pmresrn(region);
+ val = krait_clear_pmresrn_group(val, group);
+ krait_write_pmresrn(region, val);
+ }
+}
+
+static void krait_pmu_disable_event(struct perf_event *event)
+{
+ unsigned long flags;
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+ struct pmu_hw_events *events = cpu_pmu->get_hw_events();
+
+ /* Disable counter and interrupt */
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+ /* Disable counter */
+ armv7_pmnc_disable_counter(idx);
+
+ /*
+ * Clear pmresr code (if destined for PMNx counters)
+ */
+ if (hwc->config_base & KRAIT_EVENT_MASK)
+ krait_clearpmu(hwc->config_base);
+
+ /* Disable interrupt for this counter */
+ armv7_pmnc_disable_intens(idx);
+
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static void krait_pmu_enable_event(struct perf_event *event)
+{
+ unsigned long flags;
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+ struct pmu_hw_events *events = cpu_pmu->get_hw_events();
+
+ /*
+ * Enable counter and interrupt, and set the counter to count
+ * the event that we're interested in.
+ */
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+ /* Disable counter */
+ armv7_pmnc_disable_counter(idx);
+
+ /*
+ * Set event (if destined for PMNx counters)
+ * We set the event for the cycle counter because we
+ * have the ability to perform event filtering.
+ */
+ if (hwc->config_base & KRAIT_EVENT_MASK)
+ krait_evt_setup(idx, hwc->config_base);
+ else
+ armv7_pmnc_write_evtsel(idx, hwc->config_base);
+
+ /* Enable interrupt for this counter */
+ armv7_pmnc_enable_intens(idx);
+
+ /* Enable counter */
+ armv7_pmnc_enable_counter(idx);
+
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static void krait_pmu_reset(void *info)
+{
+ u32 vval, fval;
+
+ armv7pmu_reset(info);
+
+ /* Clear all pmresrs */
+ krait_write_pmresrn(0, 0);
+ krait_write_pmresrn(1, 0);
+ krait_write_pmresrn(2, 0);
+
+ krait_pre_vpmresr0(&vval, &fval);
+ krait_write_vpmresr0(0);
+ krait_post_vpmresr0(vval, fval);
+}
+
+static int krait_event_to_bit(struct perf_event *event, unsigned int region,
+ unsigned int group)
+{
+ int bit;
+ struct hw_perf_event *hwc = &event->hw;
+ struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
+
+ if (hwc->config_base & VENUM_EVENT)
+ bit = KRAIT_VPMRESR0_GROUP0;
+ else
+ bit = krait_get_pmresrn_event(region);
+ bit -= krait_get_pmresrn_event(0);
+ bit += group;
+ /*
+ * Lower bits are reserved for use by the counters (see
+ * armv7pmu_get_event_idx() for more info)
+ */
+ bit += ARMV7_IDX_COUNTER_LAST(cpu_pmu) + 1;
+
+ return bit;
+}
+
+/*
+ * We check for column exclusion constraints here.
+ * Two events cant use the same group within a pmresr register.
+ */
+static int krait_pmu_get_event_idx(struct pmu_hw_events *cpuc,
+ struct perf_event *event)
+{
+ int idx;
+ int bit;
+ unsigned int prefix;
+ unsigned int region;
+ unsigned int code;
+ unsigned int group;
+ bool krait_event;
+ struct hw_perf_event *hwc = &event->hw;
+
+ region = (hwc->config_base >> 12) & 0xf;
+ code = (hwc->config_base >> 4) & 0xff;
+ group = (hwc->config_base >> 0) & 0xf;
+ krait_event = !!(hwc->config_base & KRAIT_EVENT_MASK);
+
+ if (krait_event) {
+ /* Ignore invalid events */
+ if (group > 3 || region > 2)
+ return -EINVAL;
+ prefix = hwc->config_base & KRAIT_EVENT_MASK;
+ if (prefix != KRAIT_EVENT && prefix != VENUM_EVENT)
+ return -EINVAL;
+ if (prefix == VENUM_EVENT && (code & 0xe0))
+ return -EINVAL;
+
+ bit = krait_event_to_bit(event, region, group);
+ if (test_and_set_bit(bit, cpuc->used_mask))
+ return -EAGAIN;
+ }
+
+ idx = armv7pmu_get_event_idx(cpuc, event);
+ if (idx < 0 && krait_event)
+ clear_bit(bit, cpuc->used_mask);
+
+ return idx;
+}
+
+static void krait_pmu_clear_event_idx(struct pmu_hw_events *cpuc,
+ struct perf_event *event)
+{
+ int bit;
+ struct hw_perf_event *hwc = &event->hw;
+ unsigned int region;
+ unsigned int group;
+ bool krait_event;
+
+ region = (hwc->config_base >> 12) & 0xf;
+ group = (hwc->config_base >> 0) & 0xf;
+ krait_event = !!(hwc->config_base & KRAIT_EVENT_MASK);
+
+ if (krait_event) {
+ bit = krait_event_to_bit(event, region, group);
+ clear_bit(bit, cpuc->used_mask);
+ }
+}
+
+static int krait_pmu_init(struct arm_pmu *cpu_pmu)
+{
+ armv7pmu_init(cpu_pmu);
+ cpu_pmu->name = "ARMv7 Krait";
+ /* Some early versions of Krait don't support PC write events */
+ if (of_property_read_bool(cpu_pmu->plat_device->dev.of_node,
+ "qcom,no-pc-write"))
+ cpu_pmu->map_event = krait_map_event_no_branch;
+ else
+ cpu_pmu->map_event = krait_map_event;
+ cpu_pmu->num_events = armv7_read_num_pmnc_events();
+ cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
+ cpu_pmu->reset = krait_pmu_reset;
+ cpu_pmu->enable = krait_pmu_enable_event;
+ cpu_pmu->disable = krait_pmu_disable_event;
+ cpu_pmu->get_event_idx = krait_pmu_get_event_idx;
+ cpu_pmu->clear_event_idx = krait_pmu_clear_event_idx;
+ return 0;
+}
#else
static inline int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu)
{
@@ -1308,4 +2015,14 @@ static inline int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu)
{
return -ENODEV;
}
+
+static inline int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu)
+{
+ return -ENODEV;
+}
+
+static inline int krait_pmu_init(struct arm_pmu *cpu_pmu)
+{
+ return -ENODEV;
+}
#endif /* CONFIG_CPU_V7 */
diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c
new file mode 100644
index 000000000000..51a13a027989
--- /dev/null
+++ b/arch/arm/kernel/probes-arm.c
@@ -0,0 +1,734 @@
+/*
+ * arch/arm/kernel/probes-arm.c
+ *
+ * Some code moved here from arch/arm/kernel/kprobes-arm.c
+ *
+ * Copyright (C) 2006, 2007 Motorola 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.
+ *
+ * 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/stddef.h>
+#include <linux/ptrace.h>
+
+#include "probes.h"
+#include "probes-arm.h"
+
+#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
+
+#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+
+/*
+ * To avoid the complications of mimicing single-stepping on a
+ * processor without a Next-PC or a single-step mode, and to
+ * avoid having to deal with the side-effects of boosting, we
+ * simulate or emulate (almost) all ARM instructions.
+ *
+ * "Simulation" is where the instruction's behavior is duplicated in
+ * C code. "Emulation" is where the original instruction is rewritten
+ * and executed, often by altering its registers.
+ *
+ * By having all behavior of the kprobe'd instruction completed before
+ * returning from the kprobe_handler(), all locks (scheduler and
+ * interrupt) can safely be released. There is no need for secondary
+ * breakpoints, no race with MP or preemptable kernels, nor having to
+ * clean up resources counts at a later time impacting overall system
+ * performance. By rewriting the instruction, only the minimum registers
+ * need to be loaded and saved back optimizing performance.
+ *
+ * Calling the insnslot_*_rwflags version of a function doesn't hurt
+ * anything even when the CPSR flags aren't updated by the
+ * instruction. It's just a little slower in return for saving
+ * a little space by not having a duplicate function that doesn't
+ * update the flags. (The same optimization can be said for
+ * instructions that do or don't perform register writeback)
+ * Also, instructions can either read the flags, only write the
+ * flags, or read and write the flags. To save combinations
+ * rather than for sheer performance, flag functions just assume
+ * read and write of flags.
+ */
+
+void __kprobes simulate_bbl(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
+{
+ long iaddr = (long) regs->ARM_pc - 4;
+ int disp = branch_displacement(insn);
+
+ if (insn & (1 << 24))
+ regs->ARM_lr = iaddr + 4;
+
+ regs->ARM_pc = iaddr + 8 + disp;
+}
+
+void __kprobes simulate_blx1(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
+{
+ long iaddr = (long) regs->ARM_pc - 4;
+ int disp = branch_displacement(insn);
+
+ regs->ARM_lr = iaddr + 4;
+ regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2);
+ regs->ARM_cpsr |= PSR_T_BIT;
+}
+
+void __kprobes simulate_blx2bx(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
+{
+ int rm = insn & 0xf;
+ long rmv = regs->uregs[rm];
+
+ if (insn & (1 << 5))
+ regs->ARM_lr = (long) regs->ARM_pc;
+
+ regs->ARM_pc = rmv & ~0x1;
+ regs->ARM_cpsr &= ~PSR_T_BIT;
+ if (rmv & 0x1)
+ regs->ARM_cpsr |= PSR_T_BIT;
+}
+
+void __kprobes simulate_mrs(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
+{
+ int rd = (insn >> 12) & 0xf;
+ unsigned long mask = 0xf8ff03df; /* Mask out execution state */
+ regs->uregs[rd] = regs->ARM_cpsr & mask;
+}
+
+void __kprobes simulate_mov_ipsp(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
+{
+ regs->uregs[12] = regs->uregs[13];
+}
+
+/*
+ * For the instruction masking and comparisons in all the "space_*"
+ * functions below, Do _not_ rearrange the order of tests unless
+ * you're very, very sure of what you are doing. For the sake of
+ * efficiency, the masks for some tests sometimes assume other test
+ * have been done prior to them so the number of patterns to test
+ * for an instruction set can be as broad as possible to reduce the
+ * number of tests needed.
+ */
+
+static const union decode_item arm_1111_table[] = {
+ /* Unconditional instructions */
+
+ /* memory hint 1111 0100 x001 xxxx xxxx xxxx xxxx xxxx */
+ /* PLDI (immediate) 1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
+ /* PLDW (immediate) 1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
+ /* PLD (immediate) 1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_SIMULATE (0xfe300000, 0xf4100000, PROBES_PRELOAD_IMM),
+
+ /* memory hint 1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
+ /* PLDI (register) 1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
+ /* PLDW (register) 1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
+ /* PLD (register) 1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
+ DECODE_SIMULATE (0xfe300010, 0xf6100000, PROBES_PRELOAD_REG),
+
+ /* BLX (immediate) 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
+ DECODE_SIMULATE (0xfe000000, 0xfa000000, PROBES_BRANCH_IMM),
+
+ /* CPS 1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */
+ /* SETEND 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
+ /* SRS 1111 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
+ /* RFE 1111 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
+
+ /* Coprocessor instructions... */
+ /* MCRR2 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx */
+ /* MRRC2 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx */
+ /* LDC2 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
+ /* STC2 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
+ /* CDP2 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
+ /* MCR2 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
+ /* MRC2 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
+
+ /* Other unallocated instructions... */
+ DECODE_END
+};
+
+static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = {
+ /* Miscellaneous instructions */
+
+ /* MRS cpsr cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */
+ DECODE_SIMULATEX(0x0ff000f0, 0x01000000, PROBES_MRS,
+ REGS(0, NOPC, 0, 0, 0)),
+
+ /* BX cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
+ DECODE_SIMULATE (0x0ff000f0, 0x01200010, PROBES_BRANCH_REG),
+
+ /* BLX (register) cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
+ DECODE_SIMULATEX(0x0ff000f0, 0x01200030, PROBES_BRANCH_REG,
+ REGS(0, 0, 0, 0, NOPC)),
+
+ /* CLZ cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */
+ DECODE_EMULATEX (0x0ff000f0, 0x01600010, PROBES_CLZ,
+ REGS(0, NOPC, 0, 0, NOPC)),
+
+ /* QADD cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */
+ /* QSUB cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */
+ /* QDADD cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */
+ /* QDSUB cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */
+ DECODE_EMULATEX (0x0f9000f0, 0x01000050, PROBES_SATURATING_ARITHMETIC,
+ REGS(NOPC, NOPC, 0, 0, NOPC)),
+
+ /* BXJ cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
+ /* MSR cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */
+ /* MRS spsr cccc 0001 0100 xxxx xxxx xxxx 0000 xxxx */
+ /* BKPT 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
+ /* SMC cccc 0001 0110 xxxx xxxx xxxx 0111 xxxx */
+ /* And unallocated instructions... */
+ DECODE_END
+};
+
+static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = {
+ /* Halfword multiply and multiply-accumulate */
+
+ /* SMLALxy cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */
+ DECODE_EMULATEX (0x0ff00090, 0x01400080, PROBES_MUL1,
+ REGS(NOPC, NOPC, NOPC, 0, NOPC)),
+
+ /* SMULWy cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */
+ DECODE_OR (0x0ff000b0, 0x012000a0),
+ /* SMULxy cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */
+ DECODE_EMULATEX (0x0ff00090, 0x01600080, PROBES_MUL2,
+ REGS(NOPC, 0, NOPC, 0, NOPC)),
+
+ /* SMLAxy cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */
+ DECODE_OR (0x0ff00090, 0x01000080),
+ /* SMLAWy cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */
+ DECODE_EMULATEX (0x0ff000b0, 0x01200080, PROBES_MUL2,
+ REGS(NOPC, NOPC, NOPC, 0, NOPC)),
+
+ DECODE_END
+};
+
+static const union decode_item arm_cccc_0000_____1001_table[] = {
+ /* Multiply and multiply-accumulate */
+
+ /* MUL cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */
+ /* MULS cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */
+ DECODE_EMULATEX (0x0fe000f0, 0x00000090, PROBES_MUL2,
+ REGS(NOPC, 0, NOPC, 0, NOPC)),
+
+ /* MLA cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */
+ /* MLAS cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */
+ DECODE_OR (0x0fe000f0, 0x00200090),
+ /* MLS cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */
+ DECODE_EMULATEX (0x0ff000f0, 0x00600090, PROBES_MUL2,
+ REGS(NOPC, NOPC, NOPC, 0, NOPC)),
+
+ /* UMAAL cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */
+ DECODE_OR (0x0ff000f0, 0x00400090),
+ /* UMULL cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx */
+ /* UMULLS cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx */
+ /* UMLAL cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx */
+ /* UMLALS cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx */
+ /* SMULL cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx */
+ /* SMULLS cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */
+ /* SMLAL cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */
+ /* SMLALS cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */
+ DECODE_EMULATEX (0x0f8000f0, 0x00800090, PROBES_MUL1,
+ REGS(NOPC, NOPC, NOPC, 0, NOPC)),
+
+ DECODE_END
+};
+
+static const union decode_item arm_cccc_0001_____1001_table[] = {
+ /* Synchronization primitives */
+
+#if __LINUX_ARM_ARCH__ < 6
+ /* Deprecated on ARMv6 and may be UNDEFINED on v7 */
+ /* SMP/SWPB cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */
+ DECODE_EMULATEX (0x0fb000f0, 0x01000090, PROBES_SWP,
+ REGS(NOPC, NOPC, 0, 0, NOPC)),
+#endif
+ /* LDREX/STREX{,D,B,H} cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */
+ /* And unallocated instructions... */
+ DECODE_END
+};
+
+static const union decode_item arm_cccc_000x_____1xx1_table[] = {
+ /* Extra load/store instructions */
+
+ /* STRHT cccc 0000 xx10 xxxx xxxx xxxx 1011 xxxx */
+ /* ??? cccc 0000 xx10 xxxx xxxx xxxx 11x1 xxxx */
+ /* LDRHT cccc 0000 xx11 xxxx xxxx xxxx 1011 xxxx */
+ /* LDRSBT cccc 0000 xx11 xxxx xxxx xxxx 1101 xxxx */
+ /* LDRSHT cccc 0000 xx11 xxxx xxxx xxxx 1111 xxxx */
+ DECODE_REJECT (0x0f200090, 0x00200090),
+
+ /* LDRD/STRD lr,pc,{... cccc 000x x0x0 xxxx 111x xxxx 1101 xxxx */
+ DECODE_REJECT (0x0e10e0d0, 0x0000e0d0),
+
+ /* LDRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */
+ /* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */
+ DECODE_EMULATEX (0x0e5000d0, 0x000000d0, PROBES_LDRSTRD,
+ REGS(NOPCWB, NOPCX, 0, 0, NOPC)),
+
+ /* LDRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */
+ /* STRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */
+ DECODE_EMULATEX (0x0e5000d0, 0x004000d0, PROBES_LDRSTRD,
+ REGS(NOPCWB, NOPCX, 0, 0, 0)),
+
+ /* STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */
+ DECODE_EMULATEX (0x0e5000f0, 0x000000b0, PROBES_STORE_EXTRA,
+ REGS(NOPCWB, NOPC, 0, 0, NOPC)),
+
+ /* LDRH (register) cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */
+ /* LDRSB (register) cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */
+ /* LDRSH (register) cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */
+ DECODE_EMULATEX (0x0e500090, 0x00100090, PROBES_LOAD_EXTRA,
+ REGS(NOPCWB, NOPC, 0, 0, NOPC)),
+
+ /* STRH (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */
+ DECODE_EMULATEX (0x0e5000f0, 0x004000b0, PROBES_STORE_EXTRA,
+ REGS(NOPCWB, NOPC, 0, 0, 0)),
+
+ /* LDRH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */
+ /* LDRSB (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */
+ /* LDRSH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */
+ DECODE_EMULATEX (0x0e500090, 0x00500090, PROBES_LOAD_EXTRA,
+ REGS(NOPCWB, NOPC, 0, 0, 0)),
+
+ DECODE_END
+};
+
+static const union decode_item arm_cccc_000x_table[] = {
+ /* Data-processing (register) */
+
+ /* <op>S PC, ... cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx */
+ DECODE_REJECT (0x0e10f000, 0x0010f000),
+
+ /* MOV IP, SP 1110 0001 1010 0000 1100 0000 0000 1101 */
+ DECODE_SIMULATE (0xffffffff, 0xe1a0c00d, PROBES_MOV_IP_SP),
+
+ /* TST (register) cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */
+ /* TEQ (register) cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */
+ /* CMP (register) cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */
+ /* CMN (register) cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */
+ DECODE_EMULATEX (0x0f900010, 0x01100000, PROBES_DATA_PROCESSING_REG,
+ REGS(ANY, 0, 0, 0, ANY)),
+
+ /* MOV (register) cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */
+ /* MVN (register) cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */
+ DECODE_EMULATEX (0x0fa00010, 0x01a00000, PROBES_DATA_PROCESSING_REG,
+ REGS(0, ANY, 0, 0, ANY)),
+
+ /* AND (register) cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */
+ /* EOR (register) cccc 0000 001x xxxx xxxx xxxx xxx0 xxxx */
+ /* SUB (register) cccc 0000 010x xxxx xxxx xxxx xxx0 xxxx */
+ /* RSB (register) cccc 0000 011x xxxx xxxx xxxx xxx0 xxxx */
+ /* ADD (register) cccc 0000 100x xxxx xxxx xxxx xxx0 xxxx */
+ /* ADC (register) cccc 0000 101x xxxx xxxx xxxx xxx0 xxxx */
+ /* SBC (register) cccc 0000 110x xxxx xxxx xxxx xxx0 xxxx */
+ /* RSC (register) cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */
+ /* ORR (register) cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */
+ /* BIC (register) cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */
+ DECODE_EMULATEX (0x0e000010, 0x00000000, PROBES_DATA_PROCESSING_REG,
+ REGS(ANY, ANY, 0, 0, ANY)),
+
+ /* TST (reg-shift reg) cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */
+ /* TEQ (reg-shift reg) cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */
+ /* CMP (reg-shift reg) cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */
+ /* CMN (reg-shift reg) cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */
+ DECODE_EMULATEX (0x0f900090, 0x01100010, PROBES_DATA_PROCESSING_REG,
+ REGS(ANY, 0, NOPC, 0, ANY)),
+
+ /* MOV (reg-shift reg) cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */
+ /* MVN (reg-shift reg) cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */
+ DECODE_EMULATEX (0x0fa00090, 0x01a00010, PROBES_DATA_PROCESSING_REG,
+ REGS(0, ANY, NOPC, 0, ANY)),
+
+ /* AND (reg-shift reg) cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */
+ /* EOR (reg-shift reg) cccc 0000 001x xxxx xxxx xxxx 0xx1 xxxx */
+ /* SUB (reg-shift reg) cccc 0000 010x xxxx xxxx xxxx 0xx1 xxxx */
+ /* RSB (reg-shift reg) cccc 0000 011x xxxx xxxx xxxx 0xx1 xxxx */
+ /* ADD (reg-shift reg) cccc 0000 100x xxxx xxxx xxxx 0xx1 xxxx */
+ /* ADC (reg-shift reg) cccc 0000 101x xxxx xxxx xxxx 0xx1 xxxx */
+ /* SBC (reg-shift reg) cccc 0000 110x xxxx xxxx xxxx 0xx1 xxxx */
+ /* RSC (reg-shift reg) cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */
+ /* ORR (reg-shift reg) cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */
+ /* BIC (reg-shift reg) cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */
+ DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG,
+ REGS(ANY, ANY, NOPC, 0, ANY)),
+
+ DECODE_END
+};
+
+static const union decode_item arm_cccc_001x_table[] = {
+ /* Data-processing (immediate) */
+
+ /* MOVW cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */
+ /* MOVT cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM,
+ REGS(0, NOPC, 0, 0, 0)),
+
+ /* YIELD cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
+ DECODE_OR (0x0fff00ff, 0x03200001),
+ /* SEV cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
+ DECODE_EMULATE (0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE),
+ /* NOP cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
+ /* WFE cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
+ /* WFI cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
+ DECODE_SIMULATE (0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP),
+ /* DBG cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
+ /* unallocated hints cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
+ /* MSR (immediate) cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0x0fb00000, 0x03200000),
+
+ /* <op>S PC, ... cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx */
+ DECODE_REJECT (0x0e10f000, 0x0210f000),
+
+ /* TST (immediate) cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx */
+ /* TEQ (immediate) cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */
+ /* CMP (immediate) cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */
+ /* CMN (immediate) cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0x0f900000, 0x03100000, PROBES_DATA_PROCESSING_IMM,
+ REGS(ANY, 0, 0, 0, 0)),
+
+ /* MOV (immediate) cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */
+ /* MVN (immediate) cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0x0fa00000, 0x03a00000, PROBES_DATA_PROCESSING_IMM,
+ REGS(0, ANY, 0, 0, 0)),
+
+ /* AND (immediate) cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */
+ /* EOR (immediate) cccc 0010 001x xxxx xxxx xxxx xxxx xxxx */
+ /* SUB (immediate) cccc 0010 010x xxxx xxxx xxxx xxxx xxxx */
+ /* RSB (immediate) cccc 0010 011x xxxx xxxx xxxx xxxx xxxx */
+ /* ADD (immediate) cccc 0010 100x xxxx xxxx xxxx xxxx xxxx */
+ /* ADC (immediate) cccc 0010 101x xxxx xxxx xxxx xxxx xxxx */
+ /* SBC (immediate) cccc 0010 110x xxxx xxxx xxxx xxxx xxxx */
+ /* RSC (immediate) cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */
+ /* ORR (immediate) cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */
+ /* BIC (immediate) cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0x0e000000, 0x02000000, PROBES_DATA_PROCESSING_IMM,
+ REGS(ANY, ANY, 0, 0, 0)),
+
+ DECODE_END
+};
+
+static const union decode_item arm_cccc_0110_____xxx1_table[] = {
+ /* Media instructions */
+
+ /* SEL cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */
+ DECODE_EMULATEX (0x0ff000f0, 0x068000b0, PROBES_SATURATE,
+ REGS(NOPC, NOPC, 0, 0, NOPC)),
+
+ /* SSAT cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */
+ /* USAT cccc 0110 111x xxxx xxxx xxxx xx01 xxxx */
+ DECODE_OR(0x0fa00030, 0x06a00010),
+ /* SSAT16 cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */
+ /* USAT16 cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */
+ DECODE_EMULATEX (0x0fb000f0, 0x06a00030, PROBES_SATURATE,
+ REGS(0, NOPC, 0, 0, NOPC)),
+
+ /* REV cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */
+ /* REV16 cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */
+ /* RBIT cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */
+ /* REVSH cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */
+ DECODE_EMULATEX (0x0fb00070, 0x06b00030, PROBES_REV,
+ REGS(0, NOPC, 0, 0, NOPC)),
+
+ /* ??? cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */
+ DECODE_REJECT (0x0fb00010, 0x06000010),
+ /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1011 xxxx */
+ DECODE_REJECT (0x0f8000f0, 0x060000b0),
+ /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1101 xxxx */
+ DECODE_REJECT (0x0f8000f0, 0x060000d0),
+ /* SADD16 cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx */
+ /* SADDSUBX cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx */
+ /* SSUBADDX cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx */
+ /* SSUB16 cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx */
+ /* SADD8 cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx */
+ /* SSUB8 cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx */
+ /* QADD16 cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx */
+ /* QADDSUBX cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx */
+ /* QSUBADDX cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx */
+ /* QSUB16 cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx */
+ /* QADD8 cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx */
+ /* QSUB8 cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx */
+ /* SHADD16 cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx */
+ /* SHADDSUBX cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx */
+ /* SHSUBADDX cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx */
+ /* SHSUB16 cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx */
+ /* SHADD8 cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx */
+ /* SHSUB8 cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx */
+ /* UADD16 cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx */
+ /* UADDSUBX cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx */
+ /* USUBADDX cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx */
+ /* USUB16 cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx */
+ /* UADD8 cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx */
+ /* USUB8 cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx */
+ /* UQADD16 cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx */
+ /* UQADDSUBX cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx */
+ /* UQSUBADDX cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx */
+ /* UQSUB16 cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx */
+ /* UQADD8 cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx */
+ /* UQSUB8 cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx */
+ /* UHADD16 cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx */
+ /* UHADDSUBX cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx */
+ /* UHSUBADDX cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx */
+ /* UHSUB16 cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx */
+ /* UHADD8 cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx */
+ /* UHSUB8 cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx */
+ DECODE_EMULATEX (0x0f800010, 0x06000010, PROBES_MMI,
+ REGS(NOPC, NOPC, 0, 0, NOPC)),
+
+ /* PKHBT cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */
+ /* PKHTB cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */
+ DECODE_EMULATEX (0x0ff00030, 0x06800010, PROBES_PACK,
+ REGS(NOPC, NOPC, 0, 0, NOPC)),
+
+ /* ??? cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */
+ /* ??? cccc 0110 1101 xxxx xxxx xxxx 0111 xxxx */
+ DECODE_REJECT (0x0fb000f0, 0x06900070),
+
+ /* SXTB16 cccc 0110 1000 1111 xxxx xxxx 0111 xxxx */
+ /* SXTB cccc 0110 1010 1111 xxxx xxxx 0111 xxxx */
+ /* SXTH cccc 0110 1011 1111 xxxx xxxx 0111 xxxx */
+ /* UXTB16 cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */
+ /* UXTB cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */
+ /* UXTH cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */
+ DECODE_EMULATEX (0x0f8f00f0, 0x068f0070, PROBES_EXTEND,
+ REGS(0, NOPC, 0, 0, NOPC)),
+
+ /* SXTAB16 cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx */
+ /* SXTAB cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx */
+ /* SXTAH cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx */
+ /* UXTAB16 cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx */
+ /* UXTAB cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx */
+ /* UXTAH cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx */
+ DECODE_EMULATEX (0x0f8000f0, 0x06800070, PROBES_EXTEND_ADD,
+ REGS(NOPCX, NOPC, 0, 0, NOPC)),
+
+ DECODE_END
+};
+
+static const union decode_item arm_cccc_0111_____xxx1_table[] = {
+ /* Media instructions */
+
+ /* UNDEFINED cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */
+ DECODE_REJECT (0x0ff000f0, 0x07f000f0),
+
+ /* SMLALD cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */
+ /* SMLSLD cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */
+ DECODE_EMULATEX (0x0ff00090, 0x07400010, PROBES_MUL_ADD_LONG,
+ REGS(NOPC, NOPC, NOPC, 0, NOPC)),
+
+ /* SMUAD cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */
+ /* SMUSD cccc 0111 0000 xxxx 1111 xxxx 01x1 xxxx */
+ DECODE_OR (0x0ff0f090, 0x0700f010),
+ /* SMMUL cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */
+ DECODE_OR (0x0ff0f0d0, 0x0750f010),
+ /* USAD8 cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */
+ DECODE_EMULATEX (0x0ff0f0f0, 0x0780f010, PROBES_MUL_ADD,
+ REGS(NOPC, 0, NOPC, 0, NOPC)),
+
+ /* SMLAD cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */
+ /* SMLSD cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx */
+ DECODE_OR (0x0ff00090, 0x07000010),
+ /* SMMLA cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */
+ DECODE_OR (0x0ff000d0, 0x07500010),
+ /* USADA8 cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */
+ DECODE_EMULATEX (0x0ff000f0, 0x07800010, PROBES_MUL_ADD,
+ REGS(NOPC, NOPCX, NOPC, 0, NOPC)),
+
+ /* SMMLS cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */
+ DECODE_EMULATEX (0x0ff000d0, 0x075000d0, PROBES_MUL_ADD,
+ REGS(NOPC, NOPC, NOPC, 0, NOPC)),
+
+ /* SBFX cccc 0111 101x xxxx xxxx xxxx x101 xxxx */
+ /* UBFX cccc 0111 111x xxxx xxxx xxxx x101 xxxx */
+ DECODE_EMULATEX (0x0fa00070, 0x07a00050, PROBES_BITFIELD,
+ REGS(0, NOPC, 0, 0, NOPC)),
+
+ /* BFC cccc 0111 110x xxxx xxxx xxxx x001 1111 */
+ DECODE_EMULATEX (0x0fe0007f, 0x07c0001f, PROBES_BITFIELD,
+ REGS(0, NOPC, 0, 0, 0)),
+
+ /* BFI cccc 0111 110x xxxx xxxx xxxx x001 xxxx */
+ DECODE_EMULATEX (0x0fe00070, 0x07c00010, PROBES_BITFIELD,
+ REGS(0, NOPC, 0, 0, NOPCX)),
+
+ DECODE_END
+};
+
+static const union decode_item arm_cccc_01xx_table[] = {
+ /* Load/store word and unsigned byte */
+
+ /* LDRB/STRB pc,[...] cccc 01xx x0xx xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0x0c40f000, 0x0440f000),
+
+ /* STRT cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx */
+ /* LDRT cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx */
+ /* STRBT cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx */
+ /* LDRBT cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0x0d200000, 0x04200000),
+
+ /* STR (immediate) cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */
+ /* STRB (immediate) cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0x0e100000, 0x04000000, PROBES_STORE,
+ REGS(NOPCWB, ANY, 0, 0, 0)),
+
+ /* LDR (immediate) cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */
+ /* LDRB (immediate) cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0x0e100000, 0x04100000, PROBES_LOAD,
+ REGS(NOPCWB, ANY, 0, 0, 0)),
+
+ /* STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */
+ /* STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0x0e100000, 0x06000000, PROBES_STORE,
+ REGS(NOPCWB, ANY, 0, 0, NOPC)),
+
+ /* LDR (register) cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */
+ /* LDRB (register) cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0x0e100000, 0x06100000, PROBES_LOAD,
+ REGS(NOPCWB, ANY, 0, 0, NOPC)),
+
+ DECODE_END
+};
+
+static const union decode_item arm_cccc_100x_table[] = {
+ /* Block data transfer instructions */
+
+ /* LDM cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
+ /* STM cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_CUSTOM (0x0e400000, 0x08000000, PROBES_LDMSTM),
+
+ /* STM (user registers) cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
+ /* LDM (user registers) cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */
+ /* LDM (exception ret) cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx */
+ DECODE_END
+};
+
+const union decode_item probes_decode_arm_table[] = {
+ /*
+ * Unconditional instructions
+ * 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xf0000000, 0xf0000000, arm_1111_table),
+
+ /*
+ * Miscellaneous instructions
+ * cccc 0001 0xx0 xxxx xxxx xxxx 0xxx xxxx
+ */
+ DECODE_TABLE (0x0f900080, 0x01000000, arm_cccc_0001_0xx0____0xxx_table),
+
+ /*
+ * Halfword multiply and multiply-accumulate
+ * cccc 0001 0xx0 xxxx xxxx xxxx 1xx0 xxxx
+ */
+ DECODE_TABLE (0x0f900090, 0x01000080, arm_cccc_0001_0xx0____1xx0_table),
+
+ /*
+ * Multiply and multiply-accumulate
+ * cccc 0000 xxxx xxxx xxxx xxxx 1001 xxxx
+ */
+ DECODE_TABLE (0x0f0000f0, 0x00000090, arm_cccc_0000_____1001_table),
+
+ /*
+ * Synchronization primitives
+ * cccc 0001 xxxx xxxx xxxx xxxx 1001 xxxx
+ */
+ DECODE_TABLE (0x0f0000f0, 0x01000090, arm_cccc_0001_____1001_table),
+
+ /*
+ * Extra load/store instructions
+ * cccc 000x xxxx xxxx xxxx xxxx 1xx1 xxxx
+ */
+ DECODE_TABLE (0x0e000090, 0x00000090, arm_cccc_000x_____1xx1_table),
+
+ /*
+ * Data-processing (register)
+ * cccc 000x xxxx xxxx xxxx xxxx xxx0 xxxx
+ * Data-processing (register-shifted register)
+ * cccc 000x xxxx xxxx xxxx xxxx 0xx1 xxxx
+ */
+ DECODE_TABLE (0x0e000000, 0x00000000, arm_cccc_000x_table),
+
+ /*
+ * Data-processing (immediate)
+ * cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0x0e000000, 0x02000000, arm_cccc_001x_table),
+
+ /*
+ * Media instructions
+ * cccc 011x xxxx xxxx xxxx xxxx xxx1 xxxx
+ */
+ DECODE_TABLE (0x0f000010, 0x06000010, arm_cccc_0110_____xxx1_table),
+ DECODE_TABLE (0x0f000010, 0x07000010, arm_cccc_0111_____xxx1_table),
+
+ /*
+ * Load/store word and unsigned byte
+ * cccc 01xx xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0x0c000000, 0x04000000, arm_cccc_01xx_table),
+
+ /*
+ * Block data transfer instructions
+ * cccc 100x xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0x0e000000, 0x08000000, arm_cccc_100x_table),
+
+ /* B cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */
+ /* BL cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */
+ DECODE_SIMULATE (0x0e000000, 0x0a000000, PROBES_BRANCH),
+
+ /*
+ * Supervisor Call, and coprocessor instructions
+ */
+
+ /* MCRR cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx */
+ /* MRRC cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx */
+ /* LDC cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
+ /* STC cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
+ /* CDP cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
+ /* MCR cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
+ /* MRC cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
+ /* SVC cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0x0c000000, 0x0c000000),
+
+ DECODE_END
+};
+#ifdef CONFIG_ARM_KPROBES_TEST_MODULE
+EXPORT_SYMBOL_GPL(probes_decode_arm_table);
+#endif
+
+static void __kprobes arm_singlestep(probes_opcode_t insn,
+ struct arch_probes_insn *asi, struct pt_regs *regs)
+{
+ regs->ARM_pc += 4;
+ asi->insn_handler(insn, asi, regs);
+}
+
+/* Return:
+ * INSN_REJECTED If instruction is one not allowed to kprobe,
+ * INSN_GOOD If instruction is supported and uses instruction slot,
+ * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
+ *
+ * For instructions we don't want to kprobe (INSN_REJECTED return result):
+ * These are generally ones that modify the processor state making
+ * them "hard" to simulate such as switches processor modes or
+ * make accesses in alternate modes. Any of these could be simulated
+ * if the work was put into it, but low return considering they
+ * should also be very rare.
+ */
+enum probes_insn __kprobes
+arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
+ bool emulate, const union decode_action *actions)
+{
+ asi->insn_singlestep = arm_singlestep;
+ asi->insn_check_cc = probes_condition_checks[insn>>28];
+ return probes_decode_insn(insn, asi, probes_decode_arm_table, false,
+ emulate, actions);
+}
diff --git a/arch/arm/kernel/probes-arm.h b/arch/arm/kernel/probes-arm.h
new file mode 100644
index 000000000000..ace6572f6e26
--- /dev/null
+++ b/arch/arm/kernel/probes-arm.h
@@ -0,0 +1,73 @@
+/*
+ * arch/arm/kernel/probes-arm.h
+ *
+ * Copyright 2013 Linaro Ltd.
+ * Written by: David A. Long
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _ARM_KERNEL_PROBES_ARM_H
+#define _ARM_KERNEL_PROBES_ARM_H
+
+enum probes_arm_action {
+ PROBES_EMULATE_NONE,
+ PROBES_SIMULATE_NOP,
+ PROBES_PRELOAD_IMM,
+ PROBES_PRELOAD_REG,
+ PROBES_BRANCH_IMM,
+ PROBES_BRANCH_REG,
+ PROBES_MRS,
+ PROBES_CLZ,
+ PROBES_SATURATING_ARITHMETIC,
+ PROBES_MUL1,
+ PROBES_MUL2,
+ PROBES_SWP,
+ PROBES_LDRSTRD,
+ PROBES_LOAD,
+ PROBES_STORE,
+ PROBES_LOAD_EXTRA,
+ PROBES_STORE_EXTRA,
+ PROBES_MOV_IP_SP,
+ PROBES_DATA_PROCESSING_REG,
+ PROBES_DATA_PROCESSING_IMM,
+ PROBES_MOV_HALFWORD,
+ PROBES_SEV,
+ PROBES_WFE,
+ PROBES_SATURATE,
+ PROBES_REV,
+ PROBES_MMI,
+ PROBES_PACK,
+ PROBES_EXTEND,
+ PROBES_EXTEND_ADD,
+ PROBES_MUL_ADD_LONG,
+ PROBES_MUL_ADD,
+ PROBES_BITFIELD,
+ PROBES_BRANCH,
+ PROBES_LDMSTM,
+ NUM_PROBES_ARM_ACTIONS
+};
+
+void __kprobes simulate_bbl(probes_opcode_t opcode,
+ struct arch_probes_insn *asi, struct pt_regs *regs);
+void __kprobes simulate_blx1(probes_opcode_t opcode,
+ struct arch_probes_insn *asi, struct pt_regs *regs);
+void __kprobes simulate_blx2bx(probes_opcode_t opcode,
+ struct arch_probes_insn *asi, struct pt_regs *regs);
+void __kprobes simulate_mrs(probes_opcode_t opcode,
+ struct arch_probes_insn *asi, struct pt_regs *regs);
+void __kprobes simulate_mov_ipsp(probes_opcode_t opcode,
+ struct arch_probes_insn *asi, struct pt_regs *regs);
+
+extern const union decode_item probes_decode_arm_table[];
+
+enum probes_insn arm_probes_decode_insn(probes_opcode_t,
+ struct arch_probes_insn *, bool emulate,
+ const union decode_action *actions);
+
+#endif
diff --git a/arch/arm/kernel/probes-thumb.c b/arch/arm/kernel/probes-thumb.c
new file mode 100644
index 000000000000..4131351e812f
--- /dev/null
+++ b/arch/arm/kernel/probes-thumb.c
@@ -0,0 +1,882 @@
+/*
+ * arch/arm/kernel/probes-thumb.c
+ *
+ * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
+ *
+ * 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/stddef.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "probes.h"
+#include "probes-thumb.h"
+
+
+static const union decode_item t32_table_1110_100x_x0xx[] = {
+ /* Load/store multiple instructions */
+
+ /* Rn is PC 1110 100x x0xx 1111 xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xfe4f0000, 0xe80f0000),
+
+ /* SRS 1110 1000 00x0 xxxx xxxx xxxx xxxx xxxx */
+ /* RFE 1110 1000 00x1 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xffc00000, 0xe8000000),
+ /* SRS 1110 1001 10x0 xxxx xxxx xxxx xxxx xxxx */
+ /* RFE 1110 1001 10x1 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xffc00000, 0xe9800000),
+
+ /* STM Rn, {...pc} 1110 100x x0x0 xxxx 1xxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xfe508000, 0xe8008000),
+ /* LDM Rn, {...lr,pc} 1110 100x x0x1 xxxx 11xx xxxx xxxx xxxx */
+ DECODE_REJECT (0xfe50c000, 0xe810c000),
+ /* LDM/STM Rn, {...sp} 1110 100x x0xx xxxx xx1x xxxx xxxx xxxx */
+ DECODE_REJECT (0xfe402000, 0xe8002000),
+
+ /* STMIA 1110 1000 10x0 xxxx xxxx xxxx xxxx xxxx */
+ /* LDMIA 1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */
+ /* STMDB 1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */
+ /* LDMDB 1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_CUSTOM (0xfe400000, 0xe8000000, PROBES_T32_LDMSTM),
+
+ DECODE_END
+};
+
+static const union decode_item t32_table_1110_100x_x1xx[] = {
+ /* Load/store dual, load/store exclusive, table branch */
+
+ /* STRD (immediate) 1110 1000 x110 xxxx xxxx xxxx xxxx xxxx */
+ /* LDRD (immediate) 1110 1000 x111 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_OR (0xff600000, 0xe8600000),
+ /* STRD (immediate) 1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */
+ /* LDRD (immediate) 1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xff400000, 0xe9400000, PROBES_T32_LDRDSTRD,
+ REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)),
+
+ /* TBB 1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */
+ /* TBH 1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */
+ DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, PROBES_T32_TABLE_BRANCH,
+ REGS(NOSP, 0, 0, 0, NOSPPC)),
+
+ /* STREX 1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */
+ /* LDREX 1110 1000 0101 xxxx xxxx xxxx xxxx xxxx */
+ /* STREXB 1110 1000 1100 xxxx xxxx xxxx 0100 xxxx */
+ /* STREXH 1110 1000 1100 xxxx xxxx xxxx 0101 xxxx */
+ /* STREXD 1110 1000 1100 xxxx xxxx xxxx 0111 xxxx */
+ /* LDREXB 1110 1000 1101 xxxx xxxx xxxx 0100 xxxx */
+ /* LDREXH 1110 1000 1101 xxxx xxxx xxxx 0101 xxxx */
+ /* LDREXD 1110 1000 1101 xxxx xxxx xxxx 0111 xxxx */
+ /* And unallocated instructions... */
+ DECODE_END
+};
+
+static const union decode_item t32_table_1110_101x[] = {
+ /* Data-processing (shifted register) */
+
+ /* TST 1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */
+ /* TEQ 1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */
+ DECODE_EMULATEX (0xff700f00, 0xea100f00, PROBES_T32_TST,
+ REGS(NOSPPC, 0, 0, 0, NOSPPC)),
+
+ /* CMN 1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */
+ DECODE_OR (0xfff00f00, 0xeb100f00),
+ /* CMP 1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */
+ DECODE_EMULATEX (0xfff00f00, 0xebb00f00, PROBES_T32_TST,
+ REGS(NOPC, 0, 0, 0, NOSPPC)),
+
+ /* MOV 1110 1010 010x 1111 xxxx xxxx xxxx xxxx */
+ /* MVN 1110 1010 011x 1111 xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xffcf0000, 0xea4f0000, PROBES_T32_MOV,
+ REGS(0, 0, NOSPPC, 0, NOSPPC)),
+
+ /* ??? 1110 1010 101x xxxx xxxx xxxx xxxx xxxx */
+ /* ??? 1110 1010 111x xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xffa00000, 0xeaa00000),
+ /* ??? 1110 1011 001x xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xffe00000, 0xeb200000),
+ /* ??? 1110 1011 100x xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xffe00000, 0xeb800000),
+ /* ??? 1110 1011 111x xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xffe00000, 0xebe00000),
+
+ /* ADD/SUB SP, SP, Rm, LSL #0..3 */
+ /* 1110 1011 x0xx 1101 x000 1101 xx00 xxxx */
+ DECODE_EMULATEX (0xff4f7f30, 0xeb0d0d00, PROBES_T32_ADDSUB,
+ REGS(SP, 0, SP, 0, NOSPPC)),
+
+ /* ADD/SUB SP, SP, Rm, shift */
+ /* 1110 1011 x0xx 1101 xxxx 1101 xxxx xxxx */
+ DECODE_REJECT (0xff4f0f00, 0xeb0d0d00),
+
+ /* ADD/SUB Rd, SP, Rm, shift */
+ /* 1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xff4f0000, 0xeb0d0000, PROBES_T32_ADDSUB,
+ REGS(SP, 0, NOPC, 0, NOSPPC)),
+
+ /* AND 1110 1010 000x xxxx xxxx xxxx xxxx xxxx */
+ /* BIC 1110 1010 001x xxxx xxxx xxxx xxxx xxxx */
+ /* ORR 1110 1010 010x xxxx xxxx xxxx xxxx xxxx */
+ /* ORN 1110 1010 011x xxxx xxxx xxxx xxxx xxxx */
+ /* EOR 1110 1010 100x xxxx xxxx xxxx xxxx xxxx */
+ /* PKH 1110 1010 110x xxxx xxxx xxxx xxxx xxxx */
+ /* ADD 1110 1011 000x xxxx xxxx xxxx xxxx xxxx */
+ /* ADC 1110 1011 010x xxxx xxxx xxxx xxxx xxxx */
+ /* SBC 1110 1011 011x xxxx xxxx xxxx xxxx xxxx */
+ /* SUB 1110 1011 101x xxxx xxxx xxxx xxxx xxxx */
+ /* RSB 1110 1011 110x xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfe000000, 0xea000000, PROBES_T32_LOGICAL,
+ REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
+
+ DECODE_END
+};
+
+static const union decode_item t32_table_1111_0x0x___0[] = {
+ /* Data-processing (modified immediate) */
+
+ /* TST 1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */
+ /* TEQ 1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */
+ DECODE_EMULATEX (0xfb708f00, 0xf0100f00, PROBES_T32_TST,
+ REGS(NOSPPC, 0, 0, 0, 0)),
+
+ /* CMN 1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */
+ DECODE_OR (0xfbf08f00, 0xf1100f00),
+ /* CMP 1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */
+ DECODE_EMULATEX (0xfbf08f00, 0xf1b00f00, PROBES_T32_CMP,
+ REGS(NOPC, 0, 0, 0, 0)),
+
+ /* MOV 1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */
+ /* MVN 1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfbcf8000, 0xf04f0000, PROBES_T32_MOV,
+ REGS(0, 0, NOSPPC, 0, 0)),
+
+ /* ??? 1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xfbe08000, 0xf0a00000),
+ /* ??? 1111 0x00 110x xxxx 0xxx xxxx xxxx xxxx */
+ /* ??? 1111 0x00 111x xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xfbc08000, 0xf0c00000),
+ /* ??? 1111 0x01 001x xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xfbe08000, 0xf1200000),
+ /* ??? 1111 0x01 100x xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xfbe08000, 0xf1800000),
+ /* ??? 1111 0x01 111x xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xfbe08000, 0xf1e00000),
+
+ /* ADD Rd, SP, #imm 1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */
+ /* SUB Rd, SP, #imm 1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfb4f8000, 0xf10d0000, PROBES_T32_ADDSUB,
+ REGS(SP, 0, NOPC, 0, 0)),
+
+ /* AND 1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */
+ /* BIC 1111 0x00 001x xxxx 0xxx xxxx xxxx xxxx */
+ /* ORR 1111 0x00 010x xxxx 0xxx xxxx xxxx xxxx */
+ /* ORN 1111 0x00 011x xxxx 0xxx xxxx xxxx xxxx */
+ /* EOR 1111 0x00 100x xxxx 0xxx xxxx xxxx xxxx */
+ /* ADD 1111 0x01 000x xxxx 0xxx xxxx xxxx xxxx */
+ /* ADC 1111 0x01 010x xxxx 0xxx xxxx xxxx xxxx */
+ /* SBC 1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */
+ /* SUB 1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */
+ /* RSB 1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfa008000, 0xf0000000, PROBES_T32_LOGICAL,
+ REGS(NOSPPC, 0, NOSPPC, 0, 0)),
+
+ DECODE_END
+};
+
+static const union decode_item t32_table_1111_0x1x___0[] = {
+ /* Data-processing (plain binary immediate) */
+
+ /* ADDW Rd, PC, #imm 1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */
+ DECODE_OR (0xfbff8000, 0xf20f0000),
+ /* SUBW Rd, PC, #imm 1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfbff8000, 0xf2af0000, PROBES_T32_ADDWSUBW_PC,
+ REGS(PC, 0, NOSPPC, 0, 0)),
+
+ /* ADDW SP, SP, #imm 1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */
+ DECODE_OR (0xfbff8f00, 0xf20d0d00),
+ /* SUBW SP, SP, #imm 1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */
+ DECODE_EMULATEX (0xfbff8f00, 0xf2ad0d00, PROBES_T32_ADDWSUBW,
+ REGS(SP, 0, SP, 0, 0)),
+
+ /* ADDW 1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_OR (0xfbf08000, 0xf2000000),
+ /* SUBW 1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfbf08000, 0xf2a00000, PROBES_T32_ADDWSUBW,
+ REGS(NOPCX, 0, NOSPPC, 0, 0)),
+
+ /* MOVW 1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */
+ /* MOVT 1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfb708000, 0xf2400000, PROBES_T32_MOVW,
+ REGS(0, 0, NOSPPC, 0, 0)),
+
+ /* SSAT16 1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */
+ /* SSAT 1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */
+ /* USAT16 1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */
+ /* USAT 1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfb508000, 0xf3000000, PROBES_T32_SAT,
+ REGS(NOSPPC, 0, NOSPPC, 0, 0)),
+
+ /* SFBX 1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */
+ /* UFBX 1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfb708000, 0xf3400000, PROBES_T32_BITFIELD,
+ REGS(NOSPPC, 0, NOSPPC, 0, 0)),
+
+ /* BFC 1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfbff8000, 0xf36f0000, PROBES_T32_BITFIELD,
+ REGS(0, 0, NOSPPC, 0, 0)),
+
+ /* BFI 1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfbf08000, 0xf3600000, PROBES_T32_BITFIELD,
+ REGS(NOSPPCX, 0, NOSPPC, 0, 0)),
+
+ DECODE_END
+};
+
+static const union decode_item t32_table_1111_0xxx___1[] = {
+ /* Branches and miscellaneous control */
+
+ /* YIELD 1111 0011 1010 xxxx 10x0 x000 0000 0001 */
+ DECODE_OR (0xfff0d7ff, 0xf3a08001),
+ /* SEV 1111 0011 1010 xxxx 10x0 x000 0000 0100 */
+ DECODE_EMULATE (0xfff0d7ff, 0xf3a08004, PROBES_T32_SEV),
+ /* NOP 1111 0011 1010 xxxx 10x0 x000 0000 0000 */
+ /* WFE 1111 0011 1010 xxxx 10x0 x000 0000 0010 */
+ /* WFI 1111 0011 1010 xxxx 10x0 x000 0000 0011 */
+ DECODE_SIMULATE (0xfff0d7fc, 0xf3a08000, PROBES_T32_WFE),
+
+ /* MRS Rd, CPSR 1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
+ DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, PROBES_T32_MRS,
+ REGS(0, 0, NOSPPC, 0, 0)),
+
+ /*
+ * Unsupported instructions
+ * 1111 0x11 1xxx xxxx 10x0 xxxx xxxx xxxx
+ *
+ * MSR 1111 0011 100x xxxx 10x0 xxxx xxxx xxxx
+ * DBG hint 1111 0011 1010 xxxx 10x0 x000 1111 xxxx
+ * Unallocated hints 1111 0011 1010 xxxx 10x0 x000 xxxx xxxx
+ * CPS 1111 0011 1010 xxxx 10x0 xxxx xxxx xxxx
+ * CLREX/DSB/DMB/ISB 1111 0011 1011 xxxx 10x0 xxxx xxxx xxxx
+ * BXJ 1111 0011 1100 xxxx 10x0 xxxx xxxx xxxx
+ * SUBS PC,LR,#<imm8> 1111 0011 1101 xxxx 10x0 xxxx xxxx xxxx
+ * MRS Rd, SPSR 1111 0011 1111 xxxx 10x0 xxxx xxxx xxxx
+ * SMC 1111 0111 1111 xxxx 1000 xxxx xxxx xxxx
+ * UNDEFINED 1111 0111 1111 xxxx 1010 xxxx xxxx xxxx
+ * ??? 1111 0111 1xxx xxxx 1010 xxxx xxxx xxxx
+ */
+ DECODE_REJECT (0xfb80d000, 0xf3808000),
+
+ /* Bcc 1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */
+ DECODE_CUSTOM (0xf800d000, 0xf0008000, PROBES_T32_BRANCH_COND),
+
+ /* BLX 1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */
+ DECODE_OR (0xf800d001, 0xf000c000),
+ /* B 1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */
+ /* BL 1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */
+ DECODE_SIMULATE (0xf8009000, 0xf0009000, PROBES_T32_BRANCH),
+
+ DECODE_END
+};
+
+static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
+ /* Memory hints */
+
+ /* PLD (literal) 1111 1000 x001 1111 1111 xxxx xxxx xxxx */
+ /* PLI (literal) 1111 1001 x001 1111 1111 xxxx xxxx xxxx */
+ DECODE_SIMULATE (0xfe7ff000, 0xf81ff000, PROBES_T32_PLDI),
+
+ /* PLD{W} (immediate) 1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
+ DECODE_OR (0xffd0f000, 0xf890f000),
+ /* PLD{W} (immediate) 1111 1000 00x1 xxxx 1111 1100 xxxx xxxx */
+ DECODE_OR (0xffd0ff00, 0xf810fc00),
+ /* PLI (immediate) 1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
+ DECODE_OR (0xfff0f000, 0xf990f000),
+ /* PLI (immediate) 1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
+ DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, PROBES_T32_PLDI,
+ REGS(NOPCX, 0, 0, 0, 0)),
+
+ /* PLD{W} (register) 1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
+ DECODE_OR (0xffd0ffc0, 0xf810f000),
+ /* PLI (register) 1111 1001 0001 xxxx 1111 0000 00xx xxxx */
+ DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, PROBES_T32_PLDI,
+ REGS(NOPCX, 0, 0, 0, NOSPPC)),
+
+ /* Other unallocated instructions... */
+ DECODE_END
+};
+
+static const union decode_item t32_table_1111_100x[] = {
+ /* Store/Load single data item */
+
+ /* ??? 1111 100x x11x xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xfe600000, 0xf8600000),
+
+ /* ??? 1111 1001 0101 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xfff00000, 0xf9500000),
+
+ /* ??? 1111 100x 0xxx xxxx xxxx 10x0 xxxx xxxx */
+ DECODE_REJECT (0xfe800d00, 0xf8000800),
+
+ /* STRBT 1111 1000 0000 xxxx xxxx 1110 xxxx xxxx */
+ /* STRHT 1111 1000 0010 xxxx xxxx 1110 xxxx xxxx */
+ /* STRT 1111 1000 0100 xxxx xxxx 1110 xxxx xxxx */
+ /* LDRBT 1111 1000 0001 xxxx xxxx 1110 xxxx xxxx */
+ /* LDRSBT 1111 1001 0001 xxxx xxxx 1110 xxxx xxxx */
+ /* LDRHT 1111 1000 0011 xxxx xxxx 1110 xxxx xxxx */
+ /* LDRSHT 1111 1001 0011 xxxx xxxx 1110 xxxx xxxx */
+ /* LDRT 1111 1000 0101 xxxx xxxx 1110 xxxx xxxx */
+ DECODE_REJECT (0xfe800f00, 0xf8000e00),
+
+ /* STR{,B,H} Rn,[PC...] 1111 1000 xxx0 1111 xxxx xxxx xxxx xxxx */
+ DECODE_REJECT (0xff1f0000, 0xf80f0000),
+
+ /* STR{,B,H} PC,[Rn...] 1111 1000 xxx0 xxxx 1111 xxxx xxxx xxxx */
+ DECODE_REJECT (0xff10f000, 0xf800f000),
+
+ /* LDR (literal) 1111 1000 x101 1111 xxxx xxxx xxxx xxxx */
+ DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, PROBES_T32_LDR_LIT,
+ REGS(PC, ANY, 0, 0, 0)),
+
+ /* STR (immediate) 1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */
+ /* LDR (immediate) 1111 1000 0101 xxxx xxxx 1xxx xxxx xxxx */
+ DECODE_OR (0xffe00800, 0xf8400800),
+ /* STR (immediate) 1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */
+ /* LDR (immediate) 1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xffe00000, 0xf8c00000, PROBES_T32_LDRSTR,
+ REGS(NOPCX, ANY, 0, 0, 0)),
+
+ /* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
+ /* LDR (register) 1111 1000 0101 xxxx xxxx 0000 00xx xxxx */
+ DECODE_EMULATEX (0xffe00fc0, 0xf8400000, PROBES_T32_LDRSTR,
+ REGS(NOPCX, ANY, 0, 0, NOSPPC)),
+
+ /* LDRB (literal) 1111 1000 x001 1111 xxxx xxxx xxxx xxxx */
+ /* LDRSB (literal) 1111 1001 x001 1111 xxxx xxxx xxxx xxxx */
+ /* LDRH (literal) 1111 1000 x011 1111 xxxx xxxx xxxx xxxx */
+ /* LDRSH (literal) 1111 1001 x011 1111 xxxx xxxx xxxx xxxx */
+ DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, PROBES_T32_LDR_LIT,
+ REGS(PC, NOSPPCX, 0, 0, 0)),
+
+ /* STRB (immediate) 1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */
+ /* STRH (immediate) 1111 1000 0010 xxxx xxxx 1xxx xxxx xxxx */
+ /* LDRB (immediate) 1111 1000 0001 xxxx xxxx 1xxx xxxx xxxx */
+ /* LDRSB (immediate) 1111 1001 0001 xxxx xxxx 1xxx xxxx xxxx */
+ /* LDRH (immediate) 1111 1000 0011 xxxx xxxx 1xxx xxxx xxxx */
+ /* LDRSH (immediate) 1111 1001 0011 xxxx xxxx 1xxx xxxx xxxx */
+ DECODE_OR (0xfec00800, 0xf8000800),
+ /* STRB (immediate) 1111 1000 1000 xxxx xxxx xxxx xxxx xxxx */
+ /* STRH (immediate) 1111 1000 1010 xxxx xxxx xxxx xxxx xxxx */
+ /* LDRB (immediate) 1111 1000 1001 xxxx xxxx xxxx xxxx xxxx */
+ /* LDRSB (immediate) 1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */
+ /* LDRH (immediate) 1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */
+ /* LDRSH (immediate) 1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */
+ DECODE_EMULATEX (0xfec00000, 0xf8800000, PROBES_T32_LDRSTR,
+ REGS(NOPCX, NOSPPCX, 0, 0, 0)),
+
+ /* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
+ /* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */
+ /* LDRB (register) 1111 1000 0001 xxxx xxxx 0000 00xx xxxx */
+ /* LDRSB (register) 1111 1001 0001 xxxx xxxx 0000 00xx xxxx */
+ /* LDRH (register) 1111 1000 0011 xxxx xxxx 0000 00xx xxxx */
+ /* LDRSH (register) 1111 1001 0011 xxxx xxxx 0000 00xx xxxx */
+ DECODE_EMULATEX (0xfe800fc0, 0xf8000000, PROBES_T32_LDRSTR,
+ REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)),
+
+ /* Other unallocated instructions... */
+ DECODE_END
+};
+
+static const union decode_item t32_table_1111_1010___1111[] = {
+ /* Data-processing (register) */
+
+ /* ??? 1111 1010 011x xxxx 1111 xxxx 1xxx xxxx */
+ DECODE_REJECT (0xffe0f080, 0xfa60f080),
+
+ /* SXTH 1111 1010 0000 1111 1111 xxxx 1xxx xxxx */
+ /* UXTH 1111 1010 0001 1111 1111 xxxx 1xxx xxxx */
+ /* SXTB16 1111 1010 0010 1111 1111 xxxx 1xxx xxxx */
+ /* UXTB16 1111 1010 0011 1111 1111 xxxx 1xxx xxxx */
+ /* SXTB 1111 1010 0100 1111 1111 xxxx 1xxx xxxx */
+ /* UXTB 1111 1010 0101 1111 1111 xxxx 1xxx xxxx */
+ DECODE_EMULATEX (0xff8ff080, 0xfa0ff080, PROBES_T32_SIGN_EXTEND,
+ REGS(0, 0, NOSPPC, 0, NOSPPC)),
+
+
+ /* ??? 1111 1010 1xxx xxxx 1111 xxxx 0x11 xxxx */
+ DECODE_REJECT (0xff80f0b0, 0xfa80f030),
+ /* ??? 1111 1010 1x11 xxxx 1111 xxxx 0xxx xxxx */
+ DECODE_REJECT (0xffb0f080, 0xfab0f000),
+
+ /* SADD16 1111 1010 1001 xxxx 1111 xxxx 0000 xxxx */
+ /* SASX 1111 1010 1010 xxxx 1111 xxxx 0000 xxxx */
+ /* SSAX 1111 1010 1110 xxxx 1111 xxxx 0000 xxxx */
+ /* SSUB16 1111 1010 1101 xxxx 1111 xxxx 0000 xxxx */
+ /* SADD8 1111 1010 1000 xxxx 1111 xxxx 0000 xxxx */
+ /* SSUB8 1111 1010 1100 xxxx 1111 xxxx 0000 xxxx */
+
+ /* QADD16 1111 1010 1001 xxxx 1111 xxxx 0001 xxxx */
+ /* QASX 1111 1010 1010 xxxx 1111 xxxx 0001 xxxx */
+ /* QSAX 1111 1010 1110 xxxx 1111 xxxx 0001 xxxx */
+ /* QSUB16 1111 1010 1101 xxxx 1111 xxxx 0001 xxxx */
+ /* QADD8 1111 1010 1000 xxxx 1111 xxxx 0001 xxxx */
+ /* QSUB8 1111 1010 1100 xxxx 1111 xxxx 0001 xxxx */
+
+ /* SHADD16 1111 1010 1001 xxxx 1111 xxxx 0010 xxxx */
+ /* SHASX 1111 1010 1010 xxxx 1111 xxxx 0010 xxxx */
+ /* SHSAX 1111 1010 1110 xxxx 1111 xxxx 0010 xxxx */
+ /* SHSUB16 1111 1010 1101 xxxx 1111 xxxx 0010 xxxx */
+ /* SHADD8 1111 1010 1000 xxxx 1111 xxxx 0010 xxxx */
+ /* SHSUB8 1111 1010 1100 xxxx 1111 xxxx 0010 xxxx */
+
+ /* UADD16 1111 1010 1001 xxxx 1111 xxxx 0100 xxxx */
+ /* UASX 1111 1010 1010 xxxx 1111 xxxx 0100 xxxx */
+ /* USAX 1111 1010 1110 xxxx 1111 xxxx 0100 xxxx */
+ /* USUB16 1111 1010 1101 xxxx 1111 xxxx 0100 xxxx */
+ /* UADD8 1111 1010 1000 xxxx 1111 xxxx 0100 xxxx */
+ /* USUB8 1111 1010 1100 xxxx 1111 xxxx 0100 xxxx */
+
+ /* UQADD16 1111 1010 1001 xxxx 1111 xxxx 0101 xxxx */
+ /* UQASX 1111 1010 1010 xxxx 1111 xxxx 0101 xxxx */
+ /* UQSAX 1111 1010 1110 xxxx 1111 xxxx 0101 xxxx */
+ /* UQSUB16 1111 1010 1101 xxxx 1111 xxxx 0101 xxxx */
+ /* UQADD8 1111 1010 1000 xxxx 1111 xxxx 0101 xxxx */
+ /* UQSUB8 1111 1010 1100 xxxx 1111 xxxx 0101 xxxx */
+
+ /* UHADD16 1111 1010 1001 xxxx 1111 xxxx 0110 xxxx */
+ /* UHASX 1111 1010 1010 xxxx 1111 xxxx 0110 xxxx */
+ /* UHSAX 1111 1010 1110 xxxx 1111 xxxx 0110 xxxx */
+ /* UHSUB16 1111 1010 1101 xxxx 1111 xxxx 0110 xxxx */
+ /* UHADD8 1111 1010 1000 xxxx 1111 xxxx 0110 xxxx */
+ /* UHSUB8 1111 1010 1100 xxxx 1111 xxxx 0110 xxxx */
+ DECODE_OR (0xff80f080, 0xfa80f000),
+
+ /* SXTAH 1111 1010 0000 xxxx 1111 xxxx 1xxx xxxx */
+ /* UXTAH 1111 1010 0001 xxxx 1111 xxxx 1xxx xxxx */
+ /* SXTAB16 1111 1010 0010 xxxx 1111 xxxx 1xxx xxxx */
+ /* UXTAB16 1111 1010 0011 xxxx 1111 xxxx 1xxx xxxx */
+ /* SXTAB 1111 1010 0100 xxxx 1111 xxxx 1xxx xxxx */
+ /* UXTAB 1111 1010 0101 xxxx 1111 xxxx 1xxx xxxx */
+ DECODE_OR (0xff80f080, 0xfa00f080),
+
+ /* QADD 1111 1010 1000 xxxx 1111 xxxx 1000 xxxx */
+ /* QDADD 1111 1010 1000 xxxx 1111 xxxx 1001 xxxx */
+ /* QSUB 1111 1010 1000 xxxx 1111 xxxx 1010 xxxx */
+ /* QDSUB 1111 1010 1000 xxxx 1111 xxxx 1011 xxxx */
+ DECODE_OR (0xfff0f0c0, 0xfa80f080),
+
+ /* SEL 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
+ DECODE_OR (0xfff0f0f0, 0xfaa0f080),
+
+ /* LSL 1111 1010 000x xxxx 1111 xxxx 0000 xxxx */
+ /* LSR 1111 1010 001x xxxx 1111 xxxx 0000 xxxx */
+ /* ASR 1111 1010 010x xxxx 1111 xxxx 0000 xxxx */
+ /* ROR 1111 1010 011x xxxx 1111 xxxx 0000 xxxx */
+ DECODE_EMULATEX (0xff80f0f0, 0xfa00f000, PROBES_T32_MEDIA,
+ REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
+
+ /* CLZ 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
+ DECODE_OR (0xfff0f0f0, 0xfab0f080),
+
+ /* REV 1111 1010 1001 xxxx 1111 xxxx 1000 xxxx */
+ /* REV16 1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */
+ /* RBIT 1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */
+ /* REVSH 1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */
+ DECODE_EMULATEX (0xfff0f0c0, 0xfa90f080, PROBES_T32_REVERSE,
+ REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)),
+
+ /* Other unallocated instructions... */
+ DECODE_END
+};
+
+static const union decode_item t32_table_1111_1011_0[] = {
+ /* Multiply, multiply accumulate, and absolute difference */
+
+ /* ??? 1111 1011 0000 xxxx 1111 xxxx 0001 xxxx */
+ DECODE_REJECT (0xfff0f0f0, 0xfb00f010),
+ /* ??? 1111 1011 0111 xxxx 1111 xxxx 0001 xxxx */
+ DECODE_REJECT (0xfff0f0f0, 0xfb70f010),
+
+ /* SMULxy 1111 1011 0001 xxxx 1111 xxxx 00xx xxxx */
+ DECODE_OR (0xfff0f0c0, 0xfb10f000),
+ /* MUL 1111 1011 0000 xxxx 1111 xxxx 0000 xxxx */
+ /* SMUAD{X} 1111 1011 0010 xxxx 1111 xxxx 000x xxxx */
+ /* SMULWy 1111 1011 0011 xxxx 1111 xxxx 000x xxxx */
+ /* SMUSD{X} 1111 1011 0100 xxxx 1111 xxxx 000x xxxx */
+ /* SMMUL{R} 1111 1011 0101 xxxx 1111 xxxx 000x xxxx */
+ /* USAD8 1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */
+ DECODE_EMULATEX (0xff80f0e0, 0xfb00f000, PROBES_T32_MUL_ADD,
+ REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
+
+ /* ??? 1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */
+ DECODE_REJECT (0xfff000f0, 0xfb700010),
+
+ /* SMLAxy 1111 1011 0001 xxxx xxxx xxxx 00xx xxxx */
+ DECODE_OR (0xfff000c0, 0xfb100000),
+ /* MLA 1111 1011 0000 xxxx xxxx xxxx 0000 xxxx */
+ /* MLS 1111 1011 0000 xxxx xxxx xxxx 0001 xxxx */
+ /* SMLAD{X} 1111 1011 0010 xxxx xxxx xxxx 000x xxxx */
+ /* SMLAWy 1111 1011 0011 xxxx xxxx xxxx 000x xxxx */
+ /* SMLSD{X} 1111 1011 0100 xxxx xxxx xxxx 000x xxxx */
+ /* SMMLA{R} 1111 1011 0101 xxxx xxxx xxxx 000x xxxx */
+ /* SMMLS{R} 1111 1011 0110 xxxx xxxx xxxx 000x xxxx */
+ /* USADA8 1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */
+ DECODE_EMULATEX (0xff8000c0, 0xfb000000, PROBES_T32_MUL_ADD2,
+ REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)),
+
+ /* Other unallocated instructions... */
+ DECODE_END
+};
+
+static const union decode_item t32_table_1111_1011_1[] = {
+ /* Long multiply, long multiply accumulate, and divide */
+
+ /* UMAAL 1111 1011 1110 xxxx xxxx xxxx 0110 xxxx */
+ DECODE_OR (0xfff000f0, 0xfbe00060),
+ /* SMLALxy 1111 1011 1100 xxxx xxxx xxxx 10xx xxxx */
+ DECODE_OR (0xfff000c0, 0xfbc00080),
+ /* SMLALD{X} 1111 1011 1100 xxxx xxxx xxxx 110x xxxx */
+ /* SMLSLD{X} 1111 1011 1101 xxxx xxxx xxxx 110x xxxx */
+ DECODE_OR (0xffe000e0, 0xfbc000c0),
+ /* SMULL 1111 1011 1000 xxxx xxxx xxxx 0000 xxxx */
+ /* UMULL 1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */
+ /* SMLAL 1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */
+ /* UMLAL 1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */
+ DECODE_EMULATEX (0xff9000f0, 0xfb800000, PROBES_T32_MUL_ADD_LONG,
+ REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)),
+
+ /* SDIV 1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */
+ /* UDIV 1111 1011 1011 xxxx xxxx xxxx 1111 xxxx */
+ /* Other unallocated instructions... */
+ DECODE_END
+};
+
+const union decode_item probes_decode_thumb32_table[] = {
+
+ /*
+ * Load/store multiple instructions
+ * 1110 100x x0xx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xfe400000, 0xe8000000, t32_table_1110_100x_x0xx),
+
+ /*
+ * Load/store dual, load/store exclusive, table branch
+ * 1110 100x x1xx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xfe400000, 0xe8400000, t32_table_1110_100x_x1xx),
+
+ /*
+ * Data-processing (shifted register)
+ * 1110 101x xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xfe000000, 0xea000000, t32_table_1110_101x),
+
+ /*
+ * Coprocessor instructions
+ * 1110 11xx xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_REJECT (0xfc000000, 0xec000000),
+
+ /*
+ * Data-processing (modified immediate)
+ * 1111 0x0x xxxx xxxx 0xxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xfa008000, 0xf0000000, t32_table_1111_0x0x___0),
+
+ /*
+ * Data-processing (plain binary immediate)
+ * 1111 0x1x xxxx xxxx 0xxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xfa008000, 0xf2000000, t32_table_1111_0x1x___0),
+
+ /*
+ * Branches and miscellaneous control
+ * 1111 0xxx xxxx xxxx 1xxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xf8008000, 0xf0008000, t32_table_1111_0xxx___1),
+
+ /*
+ * Advanced SIMD element or structure load/store instructions
+ * 1111 1001 xxx0 xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_REJECT (0xff100000, 0xf9000000),
+
+ /*
+ * Memory hints
+ * 1111 100x x0x1 xxxx 1111 xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xfe50f000, 0xf810f000, t32_table_1111_100x_x0x1__1111),
+
+ /*
+ * Store single data item
+ * 1111 1000 xxx0 xxxx xxxx xxxx xxxx xxxx
+ * Load single data items
+ * 1111 100x xxx1 xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xfe000000, 0xf8000000, t32_table_1111_100x),
+
+ /*
+ * Data-processing (register)
+ * 1111 1010 xxxx xxxx 1111 xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xff00f000, 0xfa00f000, t32_table_1111_1010___1111),
+
+ /*
+ * Multiply, multiply accumulate, and absolute difference
+ * 1111 1011 0xxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xff800000, 0xfb000000, t32_table_1111_1011_0),
+
+ /*
+ * Long multiply, long multiply accumulate, and divide
+ * 1111 1011 1xxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xff800000, 0xfb800000, t32_table_1111_1011_1),
+
+ /*
+ * Coprocessor instructions
+ * 1111 11xx xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_END
+};
+#ifdef CONFIG_ARM_KPROBES_TEST_MODULE
+EXPORT_SYMBOL_GPL(probes_decode_thumb32_table);
+#endif
+
+static const union decode_item t16_table_1011[] = {
+ /* Miscellaneous 16-bit instructions */
+
+ /* ADD (SP plus immediate) 1011 0000 0xxx xxxx */
+ /* SUB (SP minus immediate) 1011 0000 1xxx xxxx */
+ DECODE_SIMULATE (0xff00, 0xb000, PROBES_T16_ADD_SP),
+
+ /* CBZ 1011 00x1 xxxx xxxx */
+ /* CBNZ 1011 10x1 xxxx xxxx */
+ DECODE_SIMULATE (0xf500, 0xb100, PROBES_T16_CBZ),
+
+ /* SXTH 1011 0010 00xx xxxx */
+ /* SXTB 1011 0010 01xx xxxx */
+ /* UXTH 1011 0010 10xx xxxx */
+ /* UXTB 1011 0010 11xx xxxx */
+ /* REV 1011 1010 00xx xxxx */
+ /* REV16 1011 1010 01xx xxxx */
+ /* ??? 1011 1010 10xx xxxx */
+ /* REVSH 1011 1010 11xx xxxx */
+ DECODE_REJECT (0xffc0, 0xba80),
+ DECODE_EMULATE (0xf500, 0xb000, PROBES_T16_SIGN_EXTEND),
+
+ /* PUSH 1011 010x xxxx xxxx */
+ DECODE_CUSTOM (0xfe00, 0xb400, PROBES_T16_PUSH),
+ /* POP 1011 110x xxxx xxxx */
+ DECODE_CUSTOM (0xfe00, 0xbc00, PROBES_T16_POP),
+
+ /*
+ * If-Then, and hints
+ * 1011 1111 xxxx xxxx
+ */
+
+ /* YIELD 1011 1111 0001 0000 */
+ DECODE_OR (0xffff, 0xbf10),
+ /* SEV 1011 1111 0100 0000 */
+ DECODE_EMULATE (0xffff, 0xbf40, PROBES_T16_SEV),
+ /* NOP 1011 1111 0000 0000 */
+ /* WFE 1011 1111 0010 0000 */
+ /* WFI 1011 1111 0011 0000 */
+ DECODE_SIMULATE (0xffcf, 0xbf00, PROBES_T16_WFE),
+ /* Unassigned hints 1011 1111 xxxx 0000 */
+ DECODE_REJECT (0xff0f, 0xbf00),
+ /* IT 1011 1111 xxxx xxxx */
+ DECODE_CUSTOM (0xff00, 0xbf00, PROBES_T16_IT),
+
+ /* SETEND 1011 0110 010x xxxx */
+ /* CPS 1011 0110 011x xxxx */
+ /* BKPT 1011 1110 xxxx xxxx */
+ /* And unallocated instructions... */
+ DECODE_END
+};
+
+const union decode_item probes_decode_thumb16_table[] = {
+
+ /*
+ * Shift (immediate), add, subtract, move, and compare
+ * 00xx xxxx xxxx xxxx
+ */
+
+ /* CMP (immediate) 0010 1xxx xxxx xxxx */
+ DECODE_EMULATE (0xf800, 0x2800, PROBES_T16_CMP),
+
+ /* ADD (register) 0001 100x xxxx xxxx */
+ /* SUB (register) 0001 101x xxxx xxxx */
+ /* LSL (immediate) 0000 0xxx xxxx xxxx */
+ /* LSR (immediate) 0000 1xxx xxxx xxxx */
+ /* ASR (immediate) 0001 0xxx xxxx xxxx */
+ /* ADD (immediate, Thumb) 0001 110x xxxx xxxx */
+ /* SUB (immediate, Thumb) 0001 111x xxxx xxxx */
+ /* MOV (immediate) 0010 0xxx xxxx xxxx */
+ /* ADD (immediate, Thumb) 0011 0xxx xxxx xxxx */
+ /* SUB (immediate, Thumb) 0011 1xxx xxxx xxxx */
+ DECODE_EMULATE (0xc000, 0x0000, PROBES_T16_ADDSUB),
+
+ /*
+ * 16-bit Thumb data-processing instructions
+ * 0100 00xx xxxx xxxx
+ */
+
+ /* TST (register) 0100 0010 00xx xxxx */
+ DECODE_EMULATE (0xffc0, 0x4200, PROBES_T16_CMP),
+ /* CMP (register) 0100 0010 10xx xxxx */
+ /* CMN (register) 0100 0010 11xx xxxx */
+ DECODE_EMULATE (0xff80, 0x4280, PROBES_T16_CMP),
+ /* AND (register) 0100 0000 00xx xxxx */
+ /* EOR (register) 0100 0000 01xx xxxx */
+ /* LSL (register) 0100 0000 10xx xxxx */
+ /* LSR (register) 0100 0000 11xx xxxx */
+ /* ASR (register) 0100 0001 00xx xxxx */
+ /* ADC (register) 0100 0001 01xx xxxx */
+ /* SBC (register) 0100 0001 10xx xxxx */
+ /* ROR (register) 0100 0001 11xx xxxx */
+ /* RSB (immediate) 0100 0010 01xx xxxx */
+ /* ORR (register) 0100 0011 00xx xxxx */
+ /* MUL 0100 0011 00xx xxxx */
+ /* BIC (register) 0100 0011 10xx xxxx */
+ /* MVN (register) 0100 0011 10xx xxxx */
+ DECODE_EMULATE (0xfc00, 0x4000, PROBES_T16_LOGICAL),
+
+ /*
+ * Special data instructions and branch and exchange
+ * 0100 01xx xxxx xxxx
+ */
+
+ /* BLX pc 0100 0111 1111 1xxx */
+ DECODE_REJECT (0xfff8, 0x47f8),
+
+ /* BX (register) 0100 0111 0xxx xxxx */
+ /* BLX (register) 0100 0111 1xxx xxxx */
+ DECODE_SIMULATE (0xff00, 0x4700, PROBES_T16_BLX),
+
+ /* ADD pc, pc 0100 0100 1111 1111 */
+ DECODE_REJECT (0xffff, 0x44ff),
+
+ /* ADD (register) 0100 0100 xxxx xxxx */
+ /* CMP (register) 0100 0101 xxxx xxxx */
+ /* MOV (register) 0100 0110 xxxx xxxx */
+ DECODE_CUSTOM (0xfc00, 0x4400, PROBES_T16_HIREGOPS),
+
+ /*
+ * Load from Literal Pool
+ * LDR (literal) 0100 1xxx xxxx xxxx
+ */
+ DECODE_SIMULATE (0xf800, 0x4800, PROBES_T16_LDR_LIT),
+
+ /*
+ * 16-bit Thumb Load/store instructions
+ * 0101 xxxx xxxx xxxx
+ * 011x xxxx xxxx xxxx
+ * 100x xxxx xxxx xxxx
+ */
+
+ /* STR (register) 0101 000x xxxx xxxx */
+ /* STRH (register) 0101 001x xxxx xxxx */
+ /* STRB (register) 0101 010x xxxx xxxx */
+ /* LDRSB (register) 0101 011x xxxx xxxx */
+ /* LDR (register) 0101 100x xxxx xxxx */
+ /* LDRH (register) 0101 101x xxxx xxxx */
+ /* LDRB (register) 0101 110x xxxx xxxx */
+ /* LDRSH (register) 0101 111x xxxx xxxx */
+ /* STR (immediate, Thumb) 0110 0xxx xxxx xxxx */
+ /* LDR (immediate, Thumb) 0110 1xxx xxxx xxxx */
+ /* STRB (immediate, Thumb) 0111 0xxx xxxx xxxx */
+ /* LDRB (immediate, Thumb) 0111 1xxx xxxx xxxx */
+ DECODE_EMULATE (0xc000, 0x4000, PROBES_T16_LDRHSTRH),
+ /* STRH (immediate, Thumb) 1000 0xxx xxxx xxxx */
+ /* LDRH (immediate, Thumb) 1000 1xxx xxxx xxxx */
+ DECODE_EMULATE (0xf000, 0x8000, PROBES_T16_LDRHSTRH),
+ /* STR (immediate, Thumb) 1001 0xxx xxxx xxxx */
+ /* LDR (immediate, Thumb) 1001 1xxx xxxx xxxx */
+ DECODE_SIMULATE (0xf000, 0x9000, PROBES_T16_LDRSTR),
+
+ /*
+ * Generate PC-/SP-relative address
+ * ADR (literal) 1010 0xxx xxxx xxxx
+ * ADD (SP plus immediate) 1010 1xxx xxxx xxxx
+ */
+ DECODE_SIMULATE (0xf000, 0xa000, PROBES_T16_ADR),
+
+ /*
+ * Miscellaneous 16-bit instructions
+ * 1011 xxxx xxxx xxxx
+ */
+ DECODE_TABLE (0xf000, 0xb000, t16_table_1011),
+
+ /* STM 1100 0xxx xxxx xxxx */
+ /* LDM 1100 1xxx xxxx xxxx */
+ DECODE_EMULATE (0xf000, 0xc000, PROBES_T16_LDMSTM),
+
+ /*
+ * Conditional branch, and Supervisor Call
+ */
+
+ /* Permanently UNDEFINED 1101 1110 xxxx xxxx */
+ /* SVC 1101 1111 xxxx xxxx */
+ DECODE_REJECT (0xfe00, 0xde00),
+
+ /* Conditional branch 1101 xxxx xxxx xxxx */
+ DECODE_CUSTOM (0xf000, 0xd000, PROBES_T16_BRANCH_COND),
+
+ /*
+ * Unconditional branch
+ * B 1110 0xxx xxxx xxxx
+ */
+ DECODE_SIMULATE (0xf800, 0xe000, PROBES_T16_BRANCH),
+
+ DECODE_END
+};
+#ifdef CONFIG_ARM_KPROBES_TEST_MODULE
+EXPORT_SYMBOL_GPL(probes_decode_thumb16_table);
+#endif
+
+static unsigned long __kprobes thumb_check_cc(unsigned long cpsr)
+{
+ if (unlikely(in_it_block(cpsr)))
+ return probes_condition_checks[current_cond(cpsr)](cpsr);
+ return true;
+}
+
+static void __kprobes thumb16_singlestep(probes_opcode_t opcode,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
+{
+ regs->ARM_pc += 2;
+ asi->insn_handler(opcode, asi, regs);
+ regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
+}
+
+static void __kprobes thumb32_singlestep(probes_opcode_t opcode,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
+{
+ regs->ARM_pc += 4;
+ asi->insn_handler(opcode, asi, regs);
+ regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
+}
+
+enum probes_insn __kprobes
+thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
+ bool emulate, const union decode_action *actions)
+{
+ asi->insn_singlestep = thumb16_singlestep;
+ asi->insn_check_cc = thumb_check_cc;
+ return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true,
+ emulate, actions);
+}
+
+enum probes_insn __kprobes
+thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
+ bool emulate, const union decode_action *actions)
+{
+ asi->insn_singlestep = thumb32_singlestep;
+ asi->insn_check_cc = thumb_check_cc;
+ return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true,
+ emulate, actions);
+}
diff --git a/arch/arm/kernel/probes-thumb.h b/arch/arm/kernel/probes-thumb.h
new file mode 100644
index 000000000000..7c6f6ebe514f
--- /dev/null
+++ b/arch/arm/kernel/probes-thumb.h
@@ -0,0 +1,97 @@
+/*
+ * arch/arm/kernel/probes-thumb.h
+ *
+ * Copyright 2013 Linaro Ltd.
+ * Written by: David A. Long
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _ARM_KERNEL_PROBES_THUMB_H
+#define _ARM_KERNEL_PROBES_THUMB_H
+
+/*
+ * True if current instruction is in an IT block.
+ */
+#define in_it_block(cpsr) ((cpsr & 0x06000c00) != 0x00000000)
+
+/*
+ * Return the condition code to check for the currently executing instruction.
+ * This is in ITSTATE<7:4> which is in CPSR<15:12> but is only valid if
+ * in_it_block returns true.
+ */
+#define current_cond(cpsr) ((cpsr >> 12) & 0xf)
+
+enum probes_t32_action {
+ PROBES_T32_EMULATE_NONE,
+ PROBES_T32_SIMULATE_NOP,
+ PROBES_T32_LDMSTM,
+ PROBES_T32_LDRDSTRD,
+ PROBES_T32_TABLE_BRANCH,
+ PROBES_T32_TST,
+ PROBES_T32_CMP,
+ PROBES_T32_MOV,
+ PROBES_T32_ADDSUB,
+ PROBES_T32_LOGICAL,
+ PROBES_T32_ADDWSUBW_PC,
+ PROBES_T32_ADDWSUBW,
+ PROBES_T32_MOVW,
+ PROBES_T32_SAT,
+ PROBES_T32_BITFIELD,
+ PROBES_T32_SEV,
+ PROBES_T32_WFE,
+ PROBES_T32_MRS,
+ PROBES_T32_BRANCH_COND,
+ PROBES_T32_BRANCH,
+ PROBES_T32_PLDI,
+ PROBES_T32_LDR_LIT,
+ PROBES_T32_LDRSTR,
+ PROBES_T32_SIGN_EXTEND,
+ PROBES_T32_MEDIA,
+ PROBES_T32_REVERSE,
+ PROBES_T32_MUL_ADD,
+ PROBES_T32_MUL_ADD2,
+ PROBES_T32_MUL_ADD_LONG,
+ NUM_PROBES_T32_ACTIONS
+};
+
+enum probes_t16_action {
+ PROBES_T16_ADD_SP,
+ PROBES_T16_CBZ,
+ PROBES_T16_SIGN_EXTEND,
+ PROBES_T16_PUSH,
+ PROBES_T16_POP,
+ PROBES_T16_SEV,
+ PROBES_T16_WFE,
+ PROBES_T16_IT,
+ PROBES_T16_CMP,
+ PROBES_T16_ADDSUB,
+ PROBES_T16_LOGICAL,
+ PROBES_T16_BLX,
+ PROBES_T16_HIREGOPS,
+ PROBES_T16_LDR_LIT,
+ PROBES_T16_LDRHSTRH,
+ PROBES_T16_LDRSTR,
+ PROBES_T16_ADR,
+ PROBES_T16_LDMSTM,
+ PROBES_T16_BRANCH_COND,
+ PROBES_T16_BRANCH,
+ NUM_PROBES_T16_ACTIONS
+};
+
+extern const union decode_item probes_decode_thumb32_table[];
+extern const union decode_item probes_decode_thumb16_table[];
+
+enum probes_insn __kprobes
+thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
+ bool emulate, const union decode_action *actions);
+enum probes_insn __kprobes
+thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
+ bool emulate, const union decode_action *actions);
+
+#endif
diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c
new file mode 100644
index 000000000000..b41873f33e69
--- /dev/null
+++ b/arch/arm/kernel/probes.c
@@ -0,0 +1,455 @@
+/*
+ * arch/arm/kernel/probes.c
+ *
+ * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
+ *
+ * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
+ * Copyright (C) 2006, 2007 Motorola 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/kernel.h>
+#include <linux/types.h>
+#include <asm/system_info.h>
+#include <asm/ptrace.h>
+#include <linux/bug.h>
+
+#include "probes.h"
+
+
+#ifndef find_str_pc_offset
+
+/*
+ * For STR and STM instructions, an ARM core may choose to use either
+ * a +8 or a +12 displacement from the current instruction's address.
+ * Whichever value is chosen for a given core, it must be the same for
+ * both instructions and may not change. This function measures it.
+ */
+
+int str_pc_offset;
+
+void __init find_str_pc_offset(void)
+{
+ int addr, scratch, ret;
+
+ __asm__ (
+ "sub %[ret], pc, #4 \n\t"
+ "str pc, %[addr] \n\t"
+ "ldr %[scr], %[addr] \n\t"
+ "sub %[ret], %[scr], %[ret] \n\t"
+ : [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr));
+
+ str_pc_offset = ret;
+}
+
+#endif /* !find_str_pc_offset */
+
+
+#ifndef test_load_write_pc_interworking
+
+bool load_write_pc_interworks;
+
+void __init test_load_write_pc_interworking(void)
+{
+ int arch = cpu_architecture();
+ BUG_ON(arch == CPU_ARCH_UNKNOWN);
+ load_write_pc_interworks = arch >= CPU_ARCH_ARMv5T;
+}
+
+#endif /* !test_load_write_pc_interworking */
+
+
+#ifndef test_alu_write_pc_interworking
+
+bool alu_write_pc_interworks;
+
+void __init test_alu_write_pc_interworking(void)
+{
+ int arch = cpu_architecture();
+ BUG_ON(arch == CPU_ARCH_UNKNOWN);
+ alu_write_pc_interworks = arch >= CPU_ARCH_ARMv7;
+}
+
+#endif /* !test_alu_write_pc_interworking */
+
+
+void __init arm_probes_decode_init(void)
+{
+ find_str_pc_offset();
+ test_load_write_pc_interworking();
+ test_alu_write_pc_interworking();
+}
+
+
+static unsigned long __kprobes __check_eq(unsigned long cpsr)
+{
+ return cpsr & PSR_Z_BIT;
+}
+
+static unsigned long __kprobes __check_ne(unsigned long cpsr)
+{
+ return (~cpsr) & PSR_Z_BIT;
+}
+
+static unsigned long __kprobes __check_cs(unsigned long cpsr)
+{
+ return cpsr & PSR_C_BIT;
+}
+
+static unsigned long __kprobes __check_cc(unsigned long cpsr)
+{
+ return (~cpsr) & PSR_C_BIT;
+}
+
+static unsigned long __kprobes __check_mi(unsigned long cpsr)
+{
+ return cpsr & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_pl(unsigned long cpsr)
+{
+ return (~cpsr) & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_vs(unsigned long cpsr)
+{
+ return cpsr & PSR_V_BIT;
+}
+
+static unsigned long __kprobes __check_vc(unsigned long cpsr)
+{
+ return (~cpsr) & PSR_V_BIT;
+}
+
+static unsigned long __kprobes __check_hi(unsigned long cpsr)
+{
+ cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
+ return cpsr & PSR_C_BIT;
+}
+
+static unsigned long __kprobes __check_ls(unsigned long cpsr)
+{
+ cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
+ return (~cpsr) & PSR_C_BIT;
+}
+
+static unsigned long __kprobes __check_ge(unsigned long cpsr)
+{
+ cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ return (~cpsr) & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_lt(unsigned long cpsr)
+{
+ cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ return cpsr & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_gt(unsigned long cpsr)
+{
+ unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */
+ return (~temp) & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_le(unsigned long cpsr)
+{
+ unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */
+ return temp & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_al(unsigned long cpsr)
+{
+ return true;
+}
+
+probes_check_cc * const probes_condition_checks[16] = {
+ &__check_eq, &__check_ne, &__check_cs, &__check_cc,
+ &__check_mi, &__check_pl, &__check_vs, &__check_vc,
+ &__check_hi, &__check_ls, &__check_ge, &__check_lt,
+ &__check_gt, &__check_le, &__check_al, &__check_al
+};
+
+
+void __kprobes probes_simulate_nop(probes_opcode_t opcode,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
+{
+}
+
+void __kprobes probes_emulate_none(probes_opcode_t opcode,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
+{
+ asi->insn_fn();
+}
+
+/*
+ * Prepare an instruction slot to receive an instruction for emulating.
+ * This is done by placing a subroutine return after the location where the
+ * instruction will be placed. We also modify ARM instructions to be
+ * unconditional as the condition code will already be checked before any
+ * emulation handler is called.
+ */
+static probes_opcode_t __kprobes
+prepare_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
+ bool thumb)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+ if (thumb) {
+ u16 *thumb_insn = (u16 *)asi->insn;
+ thumb_insn[1] = 0x4770; /* Thumb bx lr */
+ thumb_insn[2] = 0x4770; /* Thumb bx lr */
+ return insn;
+ }
+ asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
+#else
+ asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
+#endif
+ /* Make an ARM instruction unconditional */
+ if (insn < 0xe0000000)
+ insn = (insn | 0xe0000000) & ~0x10000000;
+ return insn;
+}
+
+/*
+ * Write a (probably modified) instruction into the slot previously prepared by
+ * prepare_emulated_insn
+ */
+static void __kprobes
+set_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
+ bool thumb)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+ if (thumb) {
+ u16 *ip = (u16 *)asi->insn;
+ if (is_wide_instruction(insn))
+ *ip++ = insn >> 16;
+ *ip++ = insn;
+ return;
+ }
+#endif
+ asi->insn[0] = insn;
+}
+
+/*
+ * When we modify the register numbers encoded in an instruction to be emulated,
+ * the new values come from this define. For ARM and 32-bit Thumb instructions
+ * this gives...
+ *
+ * bit position 16 12 8 4 0
+ * ---------------+---+---+---+---+---+
+ * register r2 r0 r1 -- r3
+ */
+#define INSN_NEW_BITS 0x00020103
+
+/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
+#define INSN_SAMEAS16_BITS 0x22222222
+
+/*
+ * Validate and modify each of the registers encoded in an instruction.
+ *
+ * Each nibble in regs contains a value from enum decode_reg_type. For each
+ * non-zero value, the corresponding nibble in pinsn is validated and modified
+ * according to the type.
+ */
+static bool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs, bool modify)
+{
+ probes_opcode_t insn = *pinsn;
+ probes_opcode_t mask = 0xf; /* Start at least significant nibble */
+
+ for (; regs != 0; regs >>= 4, mask <<= 4) {
+
+ probes_opcode_t new_bits = INSN_NEW_BITS;
+
+ switch (regs & 0xf) {
+
+ case REG_TYPE_NONE:
+ /* Nibble not a register, skip to next */
+ continue;
+
+ case REG_TYPE_ANY:
+ /* Any register is allowed */
+ break;
+
+ case REG_TYPE_SAMEAS16:
+ /* Replace register with same as at bit position 16 */
+ new_bits = INSN_SAMEAS16_BITS;
+ break;
+
+ case REG_TYPE_SP:
+ /* Only allow SP (R13) */
+ if ((insn ^ 0xdddddddd) & mask)
+ goto reject;
+ break;
+
+ case REG_TYPE_PC:
+ /* Only allow PC (R15) */
+ if ((insn ^ 0xffffffff) & mask)
+ goto reject;
+ break;
+
+ case REG_TYPE_NOSP:
+ /* Reject SP (R13) */
+ if (((insn ^ 0xdddddddd) & mask) == 0)
+ goto reject;
+ break;
+
+ case REG_TYPE_NOSPPC:
+ case REG_TYPE_NOSPPCX:
+ /* Reject SP and PC (R13 and R15) */
+ if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
+ goto reject;
+ break;
+
+ case REG_TYPE_NOPCWB:
+ if (!is_writeback(insn))
+ break; /* No writeback, so any register is OK */
+ /* fall through... */
+ case REG_TYPE_NOPC:
+ case REG_TYPE_NOPCX:
+ /* Reject PC (R15) */
+ if (((insn ^ 0xffffffff) & mask) == 0)
+ goto reject;
+ break;
+ }
+
+ /* Replace value of nibble with new register number... */
+ insn &= ~mask;
+ insn |= new_bits & mask;
+ }
+
+ if (modify)
+ *pinsn = insn;
+
+ return true;
+
+reject:
+ return false;
+}
+
+static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
+ [DECODE_TYPE_TABLE] = sizeof(struct decode_table),
+ [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom),
+ [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate),
+ [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate),
+ [DECODE_TYPE_OR] = sizeof(struct decode_or),
+ [DECODE_TYPE_REJECT] = sizeof(struct decode_reject)
+};
+
+/*
+ * probes_decode_insn operates on data tables in order to decode an ARM
+ * architecture instruction onto which a kprobe has been placed.
+ *
+ * These instruction decoding tables are a concatenation of entries each
+ * of which consist of one of the following structs:
+ *
+ * decode_table
+ * decode_custom
+ * decode_simulate
+ * decode_emulate
+ * decode_or
+ * decode_reject
+ *
+ * Each of these starts with a struct decode_header which has the following
+ * fields:
+ *
+ * type_regs
+ * mask
+ * value
+ *
+ * The least significant DECODE_TYPE_BITS of type_regs contains a value
+ * from enum decode_type, this indicates which of the decode_* structs
+ * the entry contains. The value DECODE_TYPE_END indicates the end of the
+ * table.
+ *
+ * When the table is parsed, each entry is checked in turn to see if it
+ * matches the instruction to be decoded using the test:
+ *
+ * (insn & mask) == value
+ *
+ * If no match is found before the end of the table is reached then decoding
+ * fails with INSN_REJECTED.
+ *
+ * When a match is found, decode_regs() is called to validate and modify each
+ * of the registers encoded in the instruction; the data it uses to do this
+ * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
+ * to fail with INSN_REJECTED.
+ *
+ * Once the instruction has passed the above tests, further processing
+ * depends on the type of the table entry's decode struct.
+ *
+ */
+int __kprobes
+probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const union decode_item *table, bool thumb,
+ bool emulate, const union decode_action *actions)
+{
+ const struct decode_header *h = (struct decode_header *)table;
+ const struct decode_header *next;
+ bool matched = false;
+
+ if (emulate)
+ insn = prepare_emulated_insn(insn, asi, thumb);
+
+ for (;; h = next) {
+ enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
+ u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
+
+ if (type == DECODE_TYPE_END)
+ return INSN_REJECTED;
+
+ next = (struct decode_header *)
+ ((uintptr_t)h + decode_struct_sizes[type]);
+
+ if (!matched && (insn & h->mask.bits) != h->value.bits)
+ continue;
+
+ if (!decode_regs(&insn, regs, emulate))
+ return INSN_REJECTED;
+
+ switch (type) {
+
+ case DECODE_TYPE_TABLE: {
+ struct decode_table *d = (struct decode_table *)h;
+ next = (struct decode_header *)d->table.table;
+ break;
+ }
+
+ case DECODE_TYPE_CUSTOM: {
+ struct decode_custom *d = (struct decode_custom *)h;
+ return actions[d->decoder.action].decoder(insn, asi, h);
+ }
+
+ case DECODE_TYPE_SIMULATE: {
+ struct decode_simulate *d = (struct decode_simulate *)h;
+ asi->insn_handler = actions[d->handler.action].handler;
+ return INSN_GOOD_NO_SLOT;
+ }
+
+ case DECODE_TYPE_EMULATE: {
+ struct decode_emulate *d = (struct decode_emulate *)h;
+
+ if (!emulate)
+ return actions[d->handler.action].decoder(insn,
+ asi, h);
+
+ asi->insn_handler = actions[d->handler.action].handler;
+ set_emulated_insn(insn, asi, thumb);
+ return INSN_GOOD;
+ }
+
+ case DECODE_TYPE_OR:
+ matched = true;
+ break;
+
+ case DECODE_TYPE_REJECT:
+ default:
+ return INSN_REJECTED;
+ }
+ }
+}
diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h
new file mode 100644
index 000000000000..dba9f2466a93
--- /dev/null
+++ b/arch/arm/kernel/probes.h
@@ -0,0 +1,407 @@
+/*
+ * arch/arm/kernel/probes.h
+ *
+ * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
+ *
+ * Some contents moved here from arch/arm/include/asm/kprobes.h which is
+ * Copyright (C) 2006, 2007 Motorola 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.
+ *
+ * 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 _ARM_KERNEL_PROBES_H
+#define _ARM_KERNEL_PROBES_H
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <asm/probes.h>
+
+void __init arm_probes_decode_init(void);
+
+extern probes_check_cc * const probes_condition_checks[16];
+
+#if __LINUX_ARM_ARCH__ >= 7
+
+/* str_pc_offset is architecturally defined from ARMv7 onwards */
+#define str_pc_offset 8
+#define find_str_pc_offset()
+
+#else /* __LINUX_ARM_ARCH__ < 7 */
+
+/* We need a run-time check to determine str_pc_offset */
+extern int str_pc_offset;
+void __init find_str_pc_offset(void);
+
+#endif
+
+
+/*
+ * Update ITSTATE after normal execution of an IT block instruction.
+ *
+ * The 8 IT state bits are split into two parts in CPSR:
+ * ITSTATE<1:0> are in CPSR<26:25>
+ * ITSTATE<7:2> are in CPSR<15:10>
+ */
+static inline unsigned long it_advance(unsigned long cpsr)
+ {
+ if ((cpsr & 0x06000400) == 0) {
+ /* ITSTATE<2:0> == 0 means end of IT block, so clear IT state */
+ cpsr &= ~PSR_IT_MASK;
+ } else {
+ /* We need to shift left ITSTATE<4:0> */
+ const unsigned long mask = 0x06001c00; /* Mask ITSTATE<4:0> */
+ unsigned long it = cpsr & mask;
+ it <<= 1;
+ it |= it >> (27 - 10); /* Carry ITSTATE<2> to correct place */
+ it &= mask;
+ cpsr &= ~mask;
+ cpsr |= it;
+ }
+ return cpsr;
+}
+
+static inline void __kprobes bx_write_pc(long pcv, struct pt_regs *regs)
+{
+ long cpsr = regs->ARM_cpsr;
+ if (pcv & 0x1) {
+ cpsr |= PSR_T_BIT;
+ pcv &= ~0x1;
+ } else {
+ cpsr &= ~PSR_T_BIT;
+ pcv &= ~0x2; /* Avoid UNPREDICTABLE address allignment */
+ }
+ regs->ARM_cpsr = cpsr;
+ regs->ARM_pc = pcv;
+}
+
+
+#if __LINUX_ARM_ARCH__ >= 6
+
+/* Kernels built for >= ARMv6 should never run on <= ARMv5 hardware, so... */
+#define load_write_pc_interworks true
+#define test_load_write_pc_interworking()
+
+#else /* __LINUX_ARM_ARCH__ < 6 */
+
+/* We need run-time testing to determine if load_write_pc() should interwork. */
+extern bool load_write_pc_interworks;
+void __init test_load_write_pc_interworking(void);
+
+#endif
+
+static inline void __kprobes load_write_pc(long pcv, struct pt_regs *regs)
+{
+ if (load_write_pc_interworks)
+ bx_write_pc(pcv, regs);
+ else
+ regs->ARM_pc = pcv;
+}
+
+
+#if __LINUX_ARM_ARCH__ >= 7
+
+#define alu_write_pc_interworks true
+#define test_alu_write_pc_interworking()
+
+#elif __LINUX_ARM_ARCH__ <= 5
+
+/* Kernels built for <= ARMv5 should never run on >= ARMv6 hardware, so... */
+#define alu_write_pc_interworks false
+#define test_alu_write_pc_interworking()
+
+#else /* __LINUX_ARM_ARCH__ == 6 */
+
+/* We could be an ARMv6 binary on ARMv7 hardware so we need a run-time check. */
+extern bool alu_write_pc_interworks;
+void __init test_alu_write_pc_interworking(void);
+
+#endif /* __LINUX_ARM_ARCH__ == 6 */
+
+static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
+{
+ if (alu_write_pc_interworks)
+ bx_write_pc(pcv, regs);
+ else
+ regs->ARM_pc = pcv;
+}
+
+
+/*
+ * Test if load/store instructions writeback the address register.
+ * if P (bit 24) == 0 or W (bit 21) == 1
+ */
+#define is_writeback(insn) ((insn ^ 0x01000000) & 0x01200000)
+
+/*
+ * The following definitions and macros are used to build instruction
+ * decoding tables for use by probes_decode_insn.
+ *
+ * These tables are a concatenation of entries each of which consist of one of
+ * the decode_* structs. All of the fields in every type of decode structure
+ * are of the union type decode_item, therefore the entire decode table can be
+ * viewed as an array of these and declared like:
+ *
+ * static const union decode_item table_name[] = {};
+ *
+ * In order to construct each entry in the table, macros are used to
+ * initialise a number of sequential decode_item values in a layout which
+ * matches the relevant struct. E.g. DECODE_SIMULATE initialise a struct
+ * decode_simulate by initialising four decode_item objects like this...
+ *
+ * {.bits = _type},
+ * {.bits = _mask},
+ * {.bits = _value},
+ * {.action = _handler},
+ *
+ * Initialising a specified member of the union means that the compiler
+ * will produce a warning if the argument is of an incorrect type.
+ *
+ * Below is a list of each of the macros used to initialise entries and a
+ * description of the action performed when that entry is matched to an
+ * instruction. A match is found when (instruction & mask) == value.
+ *
+ * DECODE_TABLE(mask, value, table)
+ * Instruction decoding jumps to parsing the new sub-table 'table'.
+ *
+ * DECODE_CUSTOM(mask, value, decoder)
+ * The value of 'decoder' is used as an index into the array of
+ * action functions, and the retrieved decoder function is invoked
+ * to complete decoding of the instruction.
+ *
+ * DECODE_SIMULATE(mask, value, handler)
+ * The probes instruction handler is set to the value found by
+ * indexing into the action array using the value of 'handler'. This
+ * will be used to simulate the instruction when the probe is hit.
+ * Decoding returns with INSN_GOOD_NO_SLOT.
+ *
+ * DECODE_EMULATE(mask, value, handler)
+ * The probes instruction handler is set to the value found by
+ * indexing into the action array using the value of 'handler'. This
+ * will be used to emulate the instruction when the probe is hit. The
+ * modified instruction (see below) is placed in the probes instruction
+ * slot so it may be called by the emulation code. Decoding returns
+ * with INSN_GOOD.
+ *
+ * DECODE_REJECT(mask, value)
+ * Instruction decoding fails with INSN_REJECTED
+ *
+ * DECODE_OR(mask, value)
+ * This allows the mask/value test of multiple table entries to be
+ * logically ORed. Once an 'or' entry is matched the decoding action to
+ * be performed is that of the next entry which isn't an 'or'. E.g.
+ *
+ * DECODE_OR (mask1, value1)
+ * DECODE_OR (mask2, value2)
+ * DECODE_SIMULATE (mask3, value3, simulation_handler)
+ *
+ * This means that if any of the three mask/value pairs match the
+ * instruction being decoded, then 'simulation_handler' will be used
+ * for it.
+ *
+ * Both the SIMULATE and EMULATE macros have a second form which take an
+ * additional 'regs' argument.
+ *
+ * DECODE_SIMULATEX(mask, value, handler, regs)
+ * DECODE_EMULATEX (mask, value, handler, regs)
+ *
+ * These are used to specify what kind of CPU register is encoded in each of the
+ * least significant 5 nibbles of the instruction being decoded. The regs value
+ * is specified using the REGS macro, this takes any of the REG_TYPE_* values
+ * from enum decode_reg_type as arguments; only the '*' part of the name is
+ * given. E.g.
+ *
+ * REGS(0, ANY, NOPC, 0, ANY)
+ *
+ * This indicates an instruction is encoded like:
+ *
+ * bits 19..16 ignore
+ * bits 15..12 any register allowed here
+ * bits 11.. 8 any register except PC allowed here
+ * bits 7.. 4 ignore
+ * bits 3.. 0 any register allowed here
+ *
+ * This register specification is checked after a decode table entry is found to
+ * match an instruction (through the mask/value test). Any invalid register then
+ * found in the instruction will cause decoding to fail with INSN_REJECTED. In
+ * the above example this would happen if bits 11..8 of the instruction were
+ * 1111, indicating R15 or PC.
+ *
+ * As well as checking for legal combinations of registers, this data is also
+ * used to modify the registers encoded in the instructions so that an
+ * emulation routines can use it. (See decode_regs() and INSN_NEW_BITS.)
+ *
+ * Here is a real example which matches ARM instructions of the form
+ * "AND <Rd>,<Rn>,<Rm>,<shift> <Rs>"
+ *
+ * DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG,
+ * REGS(ANY, ANY, NOPC, 0, ANY)),
+ * ^ ^ ^ ^
+ * Rn Rd Rs Rm
+ *
+ * Decoding the instruction "AND R4, R5, R6, ASL R15" will be rejected because
+ * Rs == R15
+ *
+ * Decoding the instruction "AND R4, R5, R6, ASL R7" will be accepted and the
+ * instruction will be modified to "AND R0, R2, R3, ASL R1" and then placed into
+ * the kprobes instruction slot. This can then be called later by the handler
+ * function emulate_rd12rn16rm0rs8_rwflags (a pointer to which is retrieved from
+ * the indicated slot in the action array), in order to simulate the instruction.
+ */
+
+enum decode_type {
+ DECODE_TYPE_END,
+ DECODE_TYPE_TABLE,
+ DECODE_TYPE_CUSTOM,
+ DECODE_TYPE_SIMULATE,
+ DECODE_TYPE_EMULATE,
+ DECODE_TYPE_OR,
+ DECODE_TYPE_REJECT,
+ NUM_DECODE_TYPES /* Must be last enum */
+};
+
+#define DECODE_TYPE_BITS 4
+#define DECODE_TYPE_MASK ((1 << DECODE_TYPE_BITS) - 1)
+
+enum decode_reg_type {
+ REG_TYPE_NONE = 0, /* Not a register, ignore */
+ REG_TYPE_ANY, /* Any register allowed */
+ REG_TYPE_SAMEAS16, /* Register should be same as that at bits 19..16 */
+ REG_TYPE_SP, /* Register must be SP */
+ REG_TYPE_PC, /* Register must be PC */
+ REG_TYPE_NOSP, /* Register must not be SP */
+ REG_TYPE_NOSPPC, /* Register must not be SP or PC */
+ REG_TYPE_NOPC, /* Register must not be PC */
+ REG_TYPE_NOPCWB, /* No PC if load/store write-back flag also set */
+
+ /* The following types are used when the encoding for PC indicates
+ * another instruction form. This distiction only matters for test
+ * case coverage checks.
+ */
+ REG_TYPE_NOPCX, /* Register must not be PC */
+ REG_TYPE_NOSPPCX, /* Register must not be SP or PC */
+
+ /* Alias to allow '0' arg to be used in REGS macro. */
+ REG_TYPE_0 = REG_TYPE_NONE
+};
+
+#define REGS(r16, r12, r8, r4, r0) \
+ (((REG_TYPE_##r16) << 16) + \
+ ((REG_TYPE_##r12) << 12) + \
+ ((REG_TYPE_##r8) << 8) + \
+ ((REG_TYPE_##r4) << 4) + \
+ (REG_TYPE_##r0))
+
+union decode_item {
+ u32 bits;
+ const union decode_item *table;
+ int action;
+};
+
+struct decode_header;
+typedef enum probes_insn (probes_custom_decode_t)(probes_opcode_t,
+ struct arch_probes_insn *,
+ const struct decode_header *);
+
+union decode_action {
+ probes_insn_handler_t *handler;
+ probes_custom_decode_t *decoder;
+};
+
+#define DECODE_END \
+ {.bits = DECODE_TYPE_END}
+
+
+struct decode_header {
+ union decode_item type_regs;
+ union decode_item mask;
+ union decode_item value;
+};
+
+#define DECODE_HEADER(_type, _mask, _value, _regs) \
+ {.bits = (_type) | ((_regs) << DECODE_TYPE_BITS)}, \
+ {.bits = (_mask)}, \
+ {.bits = (_value)}
+
+
+struct decode_table {
+ struct decode_header header;
+ union decode_item table;
+};
+
+#define DECODE_TABLE(_mask, _value, _table) \
+ DECODE_HEADER(DECODE_TYPE_TABLE, _mask, _value, 0), \
+ {.table = (_table)}
+
+
+struct decode_custom {
+ struct decode_header header;
+ union decode_item decoder;
+};
+
+#define DECODE_CUSTOM(_mask, _value, _decoder) \
+ DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0), \
+ {.action = (_decoder)}
+
+
+struct decode_simulate {
+ struct decode_header header;
+ union decode_item handler;
+};
+
+#define DECODE_SIMULATEX(_mask, _value, _handler, _regs) \
+ DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs), \
+ {.action = (_handler)}
+
+#define DECODE_SIMULATE(_mask, _value, _handler) \
+ DECODE_SIMULATEX(_mask, _value, _handler, 0)
+
+
+struct decode_emulate {
+ struct decode_header header;
+ union decode_item handler;
+};
+
+#define DECODE_EMULATEX(_mask, _value, _handler, _regs) \
+ DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs), \
+ {.action = (_handler)}
+
+#define DECODE_EMULATE(_mask, _value, _handler) \
+ DECODE_EMULATEX(_mask, _value, _handler, 0)
+
+
+struct decode_or {
+ struct decode_header header;
+};
+
+#define DECODE_OR(_mask, _value) \
+ DECODE_HEADER(DECODE_TYPE_OR, _mask, _value, 0)
+
+enum probes_insn {
+ INSN_REJECTED,
+ INSN_GOOD,
+ INSN_GOOD_NO_SLOT
+};
+
+struct decode_reject {
+ struct decode_header header;
+};
+
+#define DECODE_REJECT(_mask, _value) \
+ DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0)
+
+probes_insn_handler_t probes_simulate_nop;
+probes_insn_handler_t probes_emulate_none;
+
+int __kprobes
+probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const union decode_item *table, bool thumb, bool emulate,
+ const union decode_action *actions);
+
+#endif
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index adabeababeb0..806d287e3e53 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -47,14 +47,14 @@ unsigned long __stack_chk_guard __read_mostly;
EXPORT_SYMBOL(__stack_chk_guard);
#endif
-static const char *processor_modes[] = {
+static const char *processor_modes[] __maybe_unused = {
"USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
"UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26",
"USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" ,
"UK8_32" , "UK9_32" , "UK10_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32"
};
-static const char *isa_modes[] = {
+static const char *isa_modes[] __maybe_unused = {
"ARM" , "Thumb" , "Jazelle", "ThumbEE"
};
@@ -270,12 +270,17 @@ void __show_regs(struct pt_regs *regs)
buf[3] = flags & PSR_V_BIT ? 'V' : 'v';
buf[4] = '\0';
+#ifndef CONFIG_CPU_V7M
printk("Flags: %s IRQs o%s FIQs o%s Mode %s ISA %s Segment %s\n",
buf, interrupts_enabled(regs) ? "n" : "ff",
fast_interrupts_enabled(regs) ? "n" : "ff",
processor_modes[processor_mode(regs)],
isa_modes[isa_mode(regs)],
get_fs() == get_ds() ? "kernel" : "user");
+#else
+ printk("xPSR: %08lx\n", regs->ARM_cpsr);
+#endif
+
#ifdef CONFIG_CPU_CP15
{
unsigned int ctrl;
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 1e8b030dbefd..50e198c1e9c8 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -100,6 +100,9 @@ EXPORT_SYMBOL(system_serial_high);
unsigned int elf_hwcap __read_mostly;
EXPORT_SYMBOL(elf_hwcap);
+unsigned int elf_hwcap2 __read_mostly;
+EXPORT_SYMBOL(elf_hwcap2);
+
#ifdef MULTI_CPU
struct processor processor __read_mostly;
@@ -1005,6 +1008,15 @@ static const char *hwcap_str[] = {
NULL
};
+static const char *hwcap2_str[] = {
+ "aes",
+ "pmull",
+ "sha1",
+ "sha2",
+ "crc32",
+ NULL
+};
+
static int c_show(struct seq_file *m, void *v)
{
int i, j;
@@ -1028,6 +1040,10 @@ static int c_show(struct seq_file *m, void *v)
if (elf_hwcap & (1 << j))
seq_printf(m, "%s ", hwcap_str[j]);
+ for (j = 0; hwcap2_str[j]; j++)
+ if (elf_hwcap2 & (1 << j))
+ seq_printf(m, "%s ", hwcap2_str[j]);
+
seq_printf(m, "\nCPU implementer\t: 0x%02x\n", cpuid >> 24);
seq_printf(m, "CPU architecture: %s\n",
proc_arch[cpu_architecture()]);
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index 04d63880037f..bd1983437205 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -13,6 +13,7 @@
#include <linux/personality.h>
#include <linux/uaccess.h>
#include <linux/tracehook.h>
+#include <linux/uprobes.h>
#include <asm/elf.h>
#include <asm/cacheflush.h>
@@ -590,6 +591,9 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
return restart;
}
syscall = 0;
+ } else if (thread_flags & _TIF_UPROBE) {
+ clear_thread_flag(TIF_UPROBE);
+ uprobe_notify_resume(regs);
} else {
clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(regs);
diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c
index 3e94811690ce..702bd329d9d0 100644
--- a/arch/arm/kernel/sys_oabi-compat.c
+++ b/arch/arm/kernel/sys_oabi-compat.c
@@ -203,6 +203,9 @@ asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd,
int ret;
switch (cmd) {
+ case F_GETLKP:
+ case F_SETLKP:
+ case F_SETLKPW:
case F_GETLK64:
case F_SETLK64:
case F_SETLKW64:
diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c
index 00df012c4678..3c217694ebec 100644
--- a/arch/arm/kernel/unwind.c
+++ b/arch/arm/kernel/unwind.c
@@ -68,6 +68,12 @@ EXPORT_SYMBOL(__aeabi_unwind_cpp_pr2);
struct unwind_ctrl_block {
unsigned long vrs[16]; /* virtual register set */
const unsigned long *insn; /* pointer to the current instructions word */
+ unsigned long sp_high; /* highest value of sp allowed */
+ /*
+ * 1 : check for stack overflow for each register pop.
+ * 0 : save overhead if there is plenty of stack remaining.
+ */
+ int check_each_pop;
int entries; /* number of entries left to interpret */
int byte; /* current byte number in the instructions word */
};
@@ -235,12 +241,85 @@ static unsigned long unwind_get_byte(struct unwind_ctrl_block *ctrl)
return ret;
}
+/* Before poping a register check whether it is feasible or not */
+static int unwind_pop_register(struct unwind_ctrl_block *ctrl,
+ unsigned long **vsp, unsigned int reg)
+{
+ if (unlikely(ctrl->check_each_pop))
+ if (*vsp >= (unsigned long *)ctrl->sp_high)
+ return -URC_FAILURE;
+
+ ctrl->vrs[reg] = *(*vsp)++;
+ return URC_OK;
+}
+
+/* Helper functions to execute the instructions */
+static int unwind_exec_pop_subset_r4_to_r13(struct unwind_ctrl_block *ctrl,
+ unsigned long mask)
+{
+ unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
+ int load_sp, reg = 4;
+
+ load_sp = mask & (1 << (13 - 4));
+ while (mask) {
+ if (mask & 1)
+ if (unwind_pop_register(ctrl, &vsp, reg))
+ return -URC_FAILURE;
+ mask >>= 1;
+ reg++;
+ }
+ if (!load_sp)
+ ctrl->vrs[SP] = (unsigned long)vsp;
+
+ return URC_OK;
+}
+
+static int unwind_exec_pop_r4_to_rN(struct unwind_ctrl_block *ctrl,
+ unsigned long insn)
+{
+ unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
+ int reg;
+
+ /* pop R4-R[4+bbb] */
+ for (reg = 4; reg <= 4 + (insn & 7); reg++)
+ if (unwind_pop_register(ctrl, &vsp, reg))
+ return -URC_FAILURE;
+
+ if (insn & 0x80)
+ if (unwind_pop_register(ctrl, &vsp, 14))
+ return -URC_FAILURE;
+
+ ctrl->vrs[SP] = (unsigned long)vsp;
+
+ return URC_OK;
+}
+
+static int unwind_exec_pop_subset_r0_to_r3(struct unwind_ctrl_block *ctrl,
+ unsigned long mask)
+{
+ unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
+ int reg = 0;
+
+ /* pop R0-R3 according to mask */
+ while (mask) {
+ if (mask & 1)
+ if (unwind_pop_register(ctrl, &vsp, reg))
+ return -URC_FAILURE;
+ mask >>= 1;
+ reg++;
+ }
+ ctrl->vrs[SP] = (unsigned long)vsp;
+
+ return URC_OK;
+}
+
/*
* Execute the current unwind instruction.
*/
static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
{
unsigned long insn = unwind_get_byte(ctrl);
+ int ret = URC_OK;
pr_debug("%s: insn = %08lx\n", __func__, insn);
@@ -250,8 +329,6 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4;
else if ((insn & 0xf0) == 0x80) {
unsigned long mask;
- unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
- int load_sp, reg = 4;
insn = (insn << 8) | unwind_get_byte(ctrl);
mask = insn & 0x0fff;
@@ -261,29 +338,16 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
return -URC_FAILURE;
}
- /* pop R4-R15 according to mask */
- load_sp = mask & (1 << (13 - 4));
- while (mask) {
- if (mask & 1)
- ctrl->vrs[reg] = *vsp++;
- mask >>= 1;
- reg++;
- }
- if (!load_sp)
- ctrl->vrs[SP] = (unsigned long)vsp;
+ ret = unwind_exec_pop_subset_r4_to_r13(ctrl, mask);
+ if (ret)
+ goto error;
} else if ((insn & 0xf0) == 0x90 &&
(insn & 0x0d) != 0x0d)
ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f];
else if ((insn & 0xf0) == 0xa0) {
- unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
- int reg;
-
- /* pop R4-R[4+bbb] */
- for (reg = 4; reg <= 4 + (insn & 7); reg++)
- ctrl->vrs[reg] = *vsp++;
- if (insn & 0x80)
- ctrl->vrs[14] = *vsp++;
- ctrl->vrs[SP] = (unsigned long)vsp;
+ ret = unwind_exec_pop_r4_to_rN(ctrl, insn);
+ if (ret)
+ goto error;
} else if (insn == 0xb0) {
if (ctrl->vrs[PC] == 0)
ctrl->vrs[PC] = ctrl->vrs[LR];
@@ -291,8 +355,6 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
ctrl->entries = 0;
} else if (insn == 0xb1) {
unsigned long mask = unwind_get_byte(ctrl);
- unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
- int reg = 0;
if (mask == 0 || mask & 0xf0) {
pr_warning("unwind: Spare encoding %04lx\n",
@@ -300,14 +362,9 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
return -URC_FAILURE;
}
- /* pop R0-R3 according to mask */
- while (mask) {
- if (mask & 1)
- ctrl->vrs[reg] = *vsp++;
- mask >>= 1;
- reg++;
- }
- ctrl->vrs[SP] = (unsigned long)vsp;
+ ret = unwind_exec_pop_subset_r0_to_r3(ctrl, mask);
+ if (ret)
+ goto error;
} else if (insn == 0xb2) {
unsigned long uleb128 = unwind_get_byte(ctrl);
@@ -320,7 +377,8 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
pr_debug("%s: fp = %08lx sp = %08lx lr = %08lx pc = %08lx\n", __func__,
ctrl->vrs[FP], ctrl->vrs[SP], ctrl->vrs[LR], ctrl->vrs[PC]);
- return URC_OK;
+error:
+ return ret;
}
/*
@@ -329,13 +387,13 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
*/
int unwind_frame(struct stackframe *frame)
{
- unsigned long high, low;
+ unsigned long low;
const struct unwind_idx *idx;
struct unwind_ctrl_block ctrl;
- /* only go to a higher address on the stack */
+ /* store the highest address on the stack to avoid crossing it*/
low = frame->sp;
- high = ALIGN(low, THREAD_SIZE);
+ ctrl.sp_high = ALIGN(low, THREAD_SIZE);
pr_debug("%s(pc = %08lx lr = %08lx sp = %08lx)\n", __func__,
frame->pc, frame->lr, frame->sp);
@@ -382,11 +440,16 @@ int unwind_frame(struct stackframe *frame)
return -URC_FAILURE;
}
+ ctrl.check_each_pop = 0;
+
while (ctrl.entries > 0) {
- int urc = unwind_exec_insn(&ctrl);
+ int urc;
+ if ((ctrl.sp_high - ctrl.vrs[SP]) < sizeof(ctrl.vrs))
+ ctrl.check_each_pop = 1;
+ urc = unwind_exec_insn(&ctrl);
if (urc < 0)
return urc;
- if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= high)
+ if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= ctrl.sp_high)
return -URC_FAILURE;
}
diff --git a/arch/arm/kernel/uprobes-arm.c b/arch/arm/kernel/uprobes-arm.c
new file mode 100644
index 000000000000..d3b655ff17da
--- /dev/null
+++ b/arch/arm/kernel/uprobes-arm.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
+ *
+ * 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/types.h>
+#include <linux/stddef.h>
+#include <linux/wait.h>
+#include <linux/uprobes.h>
+#include <linux/module.h>
+
+#include "probes.h"
+#include "probes-arm.h"
+#include "uprobes.h"
+
+static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs)
+{
+ probes_opcode_t insn = __mem_to_opcode_arm(*pinsn);
+ probes_opcode_t temp;
+ probes_opcode_t mask;
+ int freereg;
+ u32 free = 0xffff;
+ u32 regs;
+
+ for (regs = oregs; regs; regs >>= 4, insn >>= 4) {
+ if ((regs & 0xf) == REG_TYPE_NONE)
+ continue;
+
+ free &= ~(1 << (insn & 0xf));
+ }
+
+ /* No PC, no problem */
+ if (free & (1 << 15))
+ return 15;
+
+ if (!free)
+ return -1;
+
+ /*
+ * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would
+ * pick LR instead of R1.
+ */
+ freereg = free = fls(free) - 1;
+
+ temp = __mem_to_opcode_arm(*pinsn);
+ insn = temp;
+ regs = oregs;
+ mask = 0xf;
+
+ for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) {
+ if ((regs & 0xf) == REG_TYPE_NONE)
+ continue;
+
+ if ((temp & 0xf) != 15)
+ continue;
+
+ insn &= ~mask;
+ insn |= free & mask;
+ }
+
+ *pinsn = __opcode_to_mem_arm(insn);
+ return freereg;
+}
+
+static void uprobe_set_pc(struct arch_uprobe *auprobe,
+ struct arch_uprobe_task *autask,
+ struct pt_regs *regs)
+{
+ u32 pcreg = auprobe->pcreg;
+
+ autask->backup = regs->uregs[pcreg];
+ regs->uregs[pcreg] = regs->ARM_pc + 8;
+}
+
+static void uprobe_unset_pc(struct arch_uprobe *auprobe,
+ struct arch_uprobe_task *autask,
+ struct pt_regs *regs)
+{
+ /* PC will be taken care of by common code */
+ regs->uregs[auprobe->pcreg] = autask->backup;
+}
+
+static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
+ struct arch_uprobe_task *autask,
+ struct pt_regs *regs)
+{
+ u32 pcreg = auprobe->pcreg;
+
+ alu_write_pc(regs->uregs[pcreg], regs);
+ regs->uregs[pcreg] = autask->backup;
+}
+
+static void uprobe_write_pc(struct arch_uprobe *auprobe,
+ struct arch_uprobe_task *autask,
+ struct pt_regs *regs)
+{
+ u32 pcreg = auprobe->pcreg;
+
+ load_write_pc(regs->uregs[pcreg], regs);
+ regs->uregs[pcreg] = autask->backup;
+}
+
+enum probes_insn
+decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d)
+{
+ struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
+ asi);
+ struct decode_emulate *decode = (struct decode_emulate *) d;
+ u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS;
+ int reg;
+
+ reg = uprobes_substitute_pc(&auprobe->ixol[0], regs);
+ if (reg == 15)
+ return INSN_GOOD;
+
+ if (reg == -1)
+ return INSN_REJECTED;
+
+ auprobe->pcreg = reg;
+ auprobe->prehandler = uprobe_set_pc;
+ auprobe->posthandler = uprobe_unset_pc;
+
+ return INSN_GOOD;
+}
+
+enum probes_insn
+decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d, bool alu)
+{
+ struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
+ asi);
+ enum probes_insn ret = decode_pc_ro(insn, asi, d);
+
+ if (((insn >> 12) & 0xf) == 15)
+ auprobe->posthandler = alu ? uprobe_aluwrite_pc
+ : uprobe_write_pc;
+
+ return ret;
+}
+
+enum probes_insn
+decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ const struct decode_header *d)
+{
+ return decode_wb_pc(insn, asi, d, true);
+}
+
+enum probes_insn
+decode_ldr(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d)
+{
+ return decode_wb_pc(insn, asi, d, false);
+}
+
+enum probes_insn
+uprobe_decode_ldmstm(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ const struct decode_header *d)
+{
+ struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
+ asi);
+ unsigned reglist = insn & 0xffff;
+ int rn = (insn >> 16) & 0xf;
+ int lbit = insn & (1 << 20);
+ unsigned used = reglist | (1 << rn);
+
+ if (rn == 15)
+ return INSN_REJECTED;
+
+ if (!(used & (1 << 15)))
+ return INSN_GOOD;
+
+ if (used & (1 << 14))
+ return INSN_REJECTED;
+
+ /* Use LR instead of PC */
+ insn ^= 0xc000;
+
+ auprobe->pcreg = 14;
+ auprobe->ixol[0] = __opcode_to_mem_arm(insn);
+
+ auprobe->prehandler = uprobe_set_pc;
+ if (lbit)
+ auprobe->posthandler = uprobe_write_pc;
+ else
+ auprobe->posthandler = uprobe_unset_pc;
+
+ return INSN_GOOD;
+}
+
+const union decode_action uprobes_probes_actions[] = {
+ [PROBES_EMULATE_NONE] = {.handler = probes_simulate_nop},
+ [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
+ [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
+ [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
+ [PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
+ [PROBES_MRS] = {.handler = simulate_mrs},
+ [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
+ [PROBES_CLZ] = {.handler = probes_simulate_nop},
+ [PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop},
+ [PROBES_MUL1] = {.handler = probes_simulate_nop},
+ [PROBES_MUL2] = {.handler = probes_simulate_nop},
+ [PROBES_SWP] = {.handler = probes_simulate_nop},
+ [PROBES_LDRSTRD] = {.decoder = decode_pc_ro},
+ [PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro},
+ [PROBES_LOAD] = {.decoder = decode_ldr},
+ [PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro},
+ [PROBES_STORE] = {.decoder = decode_pc_ro},
+ [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
+ [PROBES_DATA_PROCESSING_REG] = {
+ .decoder = decode_rd12rn16rm0rs8_rwflags},
+ [PROBES_DATA_PROCESSING_IMM] = {
+ .decoder = decode_rd12rn16rm0rs8_rwflags},
+ [PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop},
+ [PROBES_SEV] = {.handler = probes_simulate_nop},
+ [PROBES_WFE] = {.handler = probes_simulate_nop},
+ [PROBES_SATURATE] = {.handler = probes_simulate_nop},
+ [PROBES_REV] = {.handler = probes_simulate_nop},
+ [PROBES_MMI] = {.handler = probes_simulate_nop},
+ [PROBES_PACK] = {.handler = probes_simulate_nop},
+ [PROBES_EXTEND] = {.handler = probes_simulate_nop},
+ [PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop},
+ [PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop},
+ [PROBES_MUL_ADD] = {.handler = probes_simulate_nop},
+ [PROBES_BITFIELD] = {.handler = probes_simulate_nop},
+ [PROBES_BRANCH] = {.handler = simulate_bbl},
+ [PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm}
+};
diff --git a/arch/arm/kernel/uprobes.c b/arch/arm/kernel/uprobes.c
new file mode 100644
index 000000000000..f9bacee973bf
--- /dev/null
+++ b/arch/arm/kernel/uprobes.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
+ *
+ * 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/stddef.h>
+#include <linux/errno.h>
+#include <linux/highmem.h>
+#include <linux/sched.h>
+#include <linux/uprobes.h>
+#include <linux/notifier.h>
+
+#include <asm/opcodes.h>
+#include <asm/traps.h>
+
+#include "probes.h"
+#include "probes-arm.h"
+#include "uprobes.h"
+
+#define UPROBE_TRAP_NR UINT_MAX
+
+bool is_swbp_insn(uprobe_opcode_t *insn)
+{
+ return (__mem_to_opcode_arm(*insn) & 0x0fffffff) ==
+ (UPROBE_SWBP_ARM_INSN & 0x0fffffff);
+}
+
+int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
+ unsigned long vaddr)
+{
+ return uprobe_write_opcode(mm, vaddr,
+ __opcode_to_mem_arm(auprobe->bpinsn));
+}
+
+bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+ if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
+ regs->ARM_pc += 4;
+ return true;
+ }
+
+ return false;
+}
+
+bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+ probes_opcode_t opcode;
+
+ if (!auprobe->simulate)
+ return false;
+
+ opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
+
+ auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs);
+
+ return true;
+}
+
+unsigned long
+arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
+ struct pt_regs *regs)
+{
+ unsigned long orig_ret_vaddr;
+
+ orig_ret_vaddr = regs->ARM_lr;
+ /* Replace the return addr with trampoline addr */
+ regs->ARM_lr = trampoline_vaddr;
+ return orig_ret_vaddr;
+}
+
+int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
+ unsigned long addr)
+{
+ unsigned int insn;
+ unsigned int bpinsn;
+ enum probes_insn ret;
+
+ /* Thumb not yet support */
+ if (addr & 0x3)
+ return -EINVAL;
+
+ insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn);
+ auprobe->ixol[0] = __opcode_to_mem_arm(insn);
+ auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN);
+
+ ret = arm_probes_decode_insn(insn, &auprobe->asi, false,
+ uprobes_probes_actions);
+ switch (ret) {
+ case INSN_REJECTED:
+ return -EINVAL;
+
+ case INSN_GOOD_NO_SLOT:
+ auprobe->simulate = true;
+ break;
+
+ case INSN_GOOD:
+ default:
+ break;
+ }
+
+ bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff;
+ if (insn >= 0xe0000000)
+ bpinsn |= 0xe0000000; /* Unconditional instruction */
+ else
+ bpinsn |= insn & 0xf0000000; /* Copy condition from insn */
+
+ auprobe->bpinsn = bpinsn;
+
+ return 0;
+}
+
+int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+ struct uprobe_task *utask = current->utask;
+
+ if (auprobe->prehandler)
+ auprobe->prehandler(auprobe, &utask->autask, regs);
+
+ utask->autask.saved_trap_no = current->thread.trap_no;
+ current->thread.trap_no = UPROBE_TRAP_NR;
+ regs->ARM_pc = utask->xol_vaddr;
+
+ return 0;
+}
+
+int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+ struct uprobe_task *utask = current->utask;
+
+ WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR);
+
+ current->thread.trap_no = utask->autask.saved_trap_no;
+ regs->ARM_pc = utask->vaddr + 4;
+
+ if (auprobe->posthandler)
+ auprobe->posthandler(auprobe, &utask->autask, regs);
+
+ return 0;
+}
+
+bool arch_uprobe_xol_was_trapped(struct task_struct *t)
+{
+ if (t->thread.trap_no != UPROBE_TRAP_NR)
+ return true;
+
+ return false;
+}
+
+void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+ struct uprobe_task *utask = current->utask;
+
+ current->thread.trap_no = utask->autask.saved_trap_no;
+ instruction_pointer_set(regs, utask->vaddr);
+}
+
+int arch_uprobe_exception_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ return NOTIFY_DONE;
+}
+
+static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ instr &= 0x0fffffff;
+ if (instr == (UPROBE_SWBP_ARM_INSN & 0x0fffffff))
+ uprobe_pre_sstep_notifier(regs);
+ else if (instr == (UPROBE_SS_ARM_INSN & 0x0fffffff))
+ uprobe_post_sstep_notifier(regs);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
+{
+ return instruction_pointer(regs);
+}
+
+static struct undef_hook uprobes_arm_break_hook = {
+ .instr_mask = 0x0fffffff,
+ .instr_val = (UPROBE_SWBP_ARM_INSN & 0x0fffffff),
+ .cpsr_mask = MODE_MASK,
+ .cpsr_val = USR_MODE,
+ .fn = uprobe_trap_handler,
+};
+
+static struct undef_hook uprobes_arm_ss_hook = {
+ .instr_mask = 0x0fffffff,
+ .instr_val = (UPROBE_SS_ARM_INSN & 0x0fffffff),
+ .cpsr_mask = MODE_MASK,
+ .cpsr_val = USR_MODE,
+ .fn = uprobe_trap_handler,
+};
+
+static int arch_uprobes_init(void)
+{
+ register_undef_hook(&uprobes_arm_break_hook);
+ register_undef_hook(&uprobes_arm_ss_hook);
+
+ return 0;
+}
+device_initcall(arch_uprobes_init);
diff --git a/arch/arm/kernel/uprobes.h b/arch/arm/kernel/uprobes.h
new file mode 100644
index 000000000000..1d0c12dfbd03
--- /dev/null
+++ b/arch/arm/kernel/uprobes.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
+ *
+ * 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 __ARM_KERNEL_UPROBES_H
+#define __ARM_KERNEL_UPROBES_H
+
+enum probes_insn uprobe_decode_ldmstm(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ const struct decode_header *d);
+
+enum probes_insn decode_ldr(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ const struct decode_header *d);
+
+enum probes_insn
+decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ const struct decode_header *d);
+
+enum probes_insn
+decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d, bool alu);
+
+enum probes_insn
+decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi,
+ const struct decode_header *d);
+
+extern const union decode_action uprobes_probes_actions[];
+
+#endif
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index bd18bb8b2770..f0e50a0f3a65 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -1051,21 +1051,26 @@ int kvm_arch_init(void *opaque)
}
}
+ cpu_notifier_register_begin();
+
err = init_hyp_mode();
if (err)
goto out_err;
- err = register_cpu_notifier(&hyp_init_cpu_nb);
+ err = __register_cpu_notifier(&hyp_init_cpu_nb);
if (err) {
kvm_err("Cannot register HYP init CPU notifier (%d)\n", err);
goto out_err;
}
+ cpu_notifier_register_done();
+
hyp_cpu_pm_init();
kvm_coproc_table_init();
return 0;
out_err:
+ cpu_notifier_register_done();
return err;
}
diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
index 78c0885d6501..c58a35116f63 100644
--- a/arch/arm/kvm/coproc.c
+++ b/arch/arm/kvm/coproc.c
@@ -23,6 +23,7 @@
#include <asm/kvm_host.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_coproc.h>
+#include <asm/kvm_mmu.h>
#include <asm/cacheflush.h>
#include <asm/cputype.h>
#include <trace/events/kvm.h>
@@ -205,6 +206,44 @@ done:
}
/*
+ * Generic accessor for VM registers. Only called as long as HCR_TVM
+ * is set.
+ */
+static bool access_vm_reg(struct kvm_vcpu *vcpu,
+ const struct coproc_params *p,
+ const struct coproc_reg *r)
+{
+ BUG_ON(!p->is_write);
+
+ vcpu->arch.cp15[r->reg] = *vcpu_reg(vcpu, p->Rt1);
+ if (p->is_64bit)
+ vcpu->arch.cp15[r->reg + 1] = *vcpu_reg(vcpu, p->Rt2);
+
+ return true;
+}
+
+/*
+ * SCTLR accessor. Only called as long as HCR_TVM is set. If the
+ * guest enables the MMU, we stop trapping the VM sys_regs and leave
+ * it in complete control of the caches.
+ *
+ * Used by the cpu-specific code.
+ */
+bool access_sctlr(struct kvm_vcpu *vcpu,
+ const struct coproc_params *p,
+ const struct coproc_reg *r)
+{
+ access_vm_reg(vcpu, p, r);
+
+ if (vcpu_has_cache_enabled(vcpu)) { /* MMU+Caches enabled? */
+ vcpu->arch.hcr &= ~HCR_TVM;
+ stage2_flush_vm(vcpu->kvm);
+ }
+
+ return true;
+}
+
+/*
* We could trap ID_DFR0 and tell the guest we don't support performance
* monitoring. Unfortunately the patch to make the kernel check ID_DFR0 was
* NAKed, so it will read the PMCR anyway.
@@ -261,33 +300,36 @@ static const struct coproc_reg cp15_regs[] = {
{ CRn( 1), CRm( 0), Op1( 0), Op2( 2), is32,
NULL, reset_val, c1_CPACR, 0x00000000 },
- /* TTBR0/TTBR1: swapped by interrupt.S. */
- { CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 },
- { CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 },
-
- /* TTBCR: swapped by interrupt.S. */
+ /* TTBR0/TTBR1/TTBCR: swapped by interrupt.S. */
+ { CRm64( 2), Op1( 0), is64, access_vm_reg, reset_unknown64, c2_TTBR0 },
+ { CRn(2), CRm( 0), Op1( 0), Op2( 0), is32,
+ access_vm_reg, reset_unknown, c2_TTBR0 },
+ { CRn(2), CRm( 0), Op1( 0), Op2( 1), is32,
+ access_vm_reg, reset_unknown, c2_TTBR1 },
{ CRn( 2), CRm( 0), Op1( 0), Op2( 2), is32,
- NULL, reset_val, c2_TTBCR, 0x00000000 },
+ access_vm_reg, reset_val, c2_TTBCR, 0x00000000 },
+ { CRm64( 2), Op1( 1), is64, access_vm_reg, reset_unknown64, c2_TTBR1 },
+
/* DACR: swapped by interrupt.S. */
{ CRn( 3), CRm( 0), Op1( 0), Op2( 0), is32,
- NULL, reset_unknown, c3_DACR },
+ access_vm_reg, reset_unknown, c3_DACR },
/* DFSR/IFSR/ADFSR/AIFSR: swapped by interrupt.S. */
{ CRn( 5), CRm( 0), Op1( 0), Op2( 0), is32,
- NULL, reset_unknown, c5_DFSR },
+ access_vm_reg, reset_unknown, c5_DFSR },
{ CRn( 5), CRm( 0), Op1( 0), Op2( 1), is32,
- NULL, reset_unknown, c5_IFSR },
+ access_vm_reg, reset_unknown, c5_IFSR },
{ CRn( 5), CRm( 1), Op1( 0), Op2( 0), is32,
- NULL, reset_unknown, c5_ADFSR },
+ access_vm_reg, reset_unknown, c5_ADFSR },
{ CRn( 5), CRm( 1), Op1( 0), Op2( 1), is32,
- NULL, reset_unknown, c5_AIFSR },
+ access_vm_reg, reset_unknown, c5_AIFSR },
/* DFAR/IFAR: swapped by interrupt.S. */
{ CRn( 6), CRm( 0), Op1( 0), Op2( 0), is32,
- NULL, reset_unknown, c6_DFAR },
+ access_vm_reg, reset_unknown, c6_DFAR },
{ CRn( 6), CRm( 0), Op1( 0), Op2( 2), is32,
- NULL, reset_unknown, c6_IFAR },
+ access_vm_reg, reset_unknown, c6_IFAR },
/* PAR swapped by interrupt.S */
{ CRm64( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR },
@@ -324,9 +366,15 @@ static const struct coproc_reg cp15_regs[] = {
/* PRRR/NMRR (aka MAIR0/MAIR1): swapped by interrupt.S. */
{ CRn(10), CRm( 2), Op1( 0), Op2( 0), is32,
- NULL, reset_unknown, c10_PRRR},
+ access_vm_reg, reset_unknown, c10_PRRR},
{ CRn(10), CRm( 2), Op1( 0), Op2( 1), is32,
- NULL, reset_unknown, c10_NMRR},
+ access_vm_reg, reset_unknown, c10_NMRR},
+
+ /* AMAIR0/AMAIR1: swapped by interrupt.S. */
+ { CRn(10), CRm( 3), Op1( 0), Op2( 0), is32,
+ access_vm_reg, reset_unknown, c10_AMAIR0},
+ { CRn(10), CRm( 3), Op1( 0), Op2( 1), is32,
+ access_vm_reg, reset_unknown, c10_AMAIR1},
/* VBAR: swapped by interrupt.S. */
{ CRn(12), CRm( 0), Op1( 0), Op2( 0), is32,
@@ -334,7 +382,7 @@ static const struct coproc_reg cp15_regs[] = {
/* CONTEXTIDR/TPIDRURW/TPIDRURO/TPIDRPRW: swapped by interrupt.S. */
{ CRn(13), CRm( 0), Op1( 0), Op2( 1), is32,
- NULL, reset_val, c13_CID, 0x00000000 },
+ access_vm_reg, reset_val, c13_CID, 0x00000000 },
{ CRn(13), CRm( 0), Op1( 0), Op2( 2), is32,
NULL, reset_unknown, c13_TID_URW },
{ CRn(13), CRm( 0), Op1( 0), Op2( 3), is32,
@@ -443,7 +491,7 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
struct coproc_params params;
- params.CRm = (kvm_vcpu_get_hsr(vcpu) >> 1) & 0xf;
+ params.CRn = (kvm_vcpu_get_hsr(vcpu) >> 1) & 0xf;
params.Rt1 = (kvm_vcpu_get_hsr(vcpu) >> 5) & 0xf;
params.is_write = ((kvm_vcpu_get_hsr(vcpu) & 1) == 0);
params.is_64bit = true;
@@ -451,7 +499,7 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
params.Op1 = (kvm_vcpu_get_hsr(vcpu) >> 16) & 0xf;
params.Op2 = 0;
params.Rt2 = (kvm_vcpu_get_hsr(vcpu) >> 10) & 0xf;
- params.CRn = 0;
+ params.CRm = 0;
return emulate_cp15(vcpu, &params);
}
diff --git a/arch/arm/kvm/coproc.h b/arch/arm/kvm/coproc.h
index 0461d5c8d3de..1a44bbe39643 100644
--- a/arch/arm/kvm/coproc.h
+++ b/arch/arm/kvm/coproc.h
@@ -58,8 +58,8 @@ static inline void print_cp_instr(const struct coproc_params *p)
{
/* Look, we even formatted it for you to paste into the table! */
if (p->is_64bit) {
- kvm_pr_unimpl(" { CRm(%2lu), Op1(%2lu), is64, func_%s },\n",
- p->CRm, p->Op1, p->is_write ? "write" : "read");
+ kvm_pr_unimpl(" { CRm64(%2lu), Op1(%2lu), is64, func_%s },\n",
+ p->CRn, p->Op1, p->is_write ? "write" : "read");
} else {
kvm_pr_unimpl(" { CRn(%2lu), CRm(%2lu), Op1(%2lu), Op2(%2lu), is32,"
" func_%s },\n",
@@ -135,13 +135,13 @@ static inline int cmp_reg(const struct coproc_reg *i1,
return -1;
if (i1->CRn != i2->CRn)
return i1->CRn - i2->CRn;
- if (i1->is_64 != i2->is_64)
- return i2->is_64 - i1->is_64;
if (i1->CRm != i2->CRm)
return i1->CRm - i2->CRm;
if (i1->Op1 != i2->Op1)
return i1->Op1 - i2->Op1;
- return i1->Op2 - i2->Op2;
+ if (i1->Op2 != i2->Op2)
+ return i1->Op2 - i2->Op2;
+ return i2->is_64 - i1->is_64;
}
@@ -153,4 +153,8 @@ static inline int cmp_reg(const struct coproc_reg *i1,
#define is64 .is_64 = true
#define is32 .is_64 = false
+bool access_sctlr(struct kvm_vcpu *vcpu,
+ const struct coproc_params *p,
+ const struct coproc_reg *r);
+
#endif /* __ARM_KVM_COPROC_LOCAL_H__ */
diff --git a/arch/arm/kvm/coproc_a15.c b/arch/arm/kvm/coproc_a15.c
index bb0cac1410cc..e6f4ae48bda9 100644
--- a/arch/arm/kvm/coproc_a15.c
+++ b/arch/arm/kvm/coproc_a15.c
@@ -34,7 +34,7 @@
static const struct coproc_reg a15_regs[] = {
/* SCTLR: swapped by interrupt.S. */
{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
- NULL, reset_val, c1_SCTLR, 0x00C50078 },
+ access_sctlr, reset_val, c1_SCTLR, 0x00C50078 },
};
static struct kvm_coproc_target_table a15_target_table = {
diff --git a/arch/arm/kvm/coproc_a7.c b/arch/arm/kvm/coproc_a7.c
index 1df767331588..17fc7cd479d3 100644
--- a/arch/arm/kvm/coproc_a7.c
+++ b/arch/arm/kvm/coproc_a7.c
@@ -37,7 +37,7 @@
static const struct coproc_reg a7_regs[] = {
/* SCTLR: swapped by interrupt.S. */
{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
- NULL, reset_val, c1_SCTLR, 0x00C50878 },
+ access_sctlr, reset_val, c1_SCTLR, 0x00C50878 },
};
static struct kvm_coproc_target_table a7_target_table = {
diff --git a/arch/arm/kvm/guest.c b/arch/arm/kvm/guest.c
index 2786eae10c0d..b23a59c1c522 100644
--- a/arch/arm/kvm/guest.c
+++ b/arch/arm/kvm/guest.c
@@ -38,6 +38,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
{
+ vcpu->arch.hcr = HCR_GUEST_MASK;
return 0;
}
diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S
index 6f18695a09cb..76af93025574 100644
--- a/arch/arm/kvm/interrupts_head.S
+++ b/arch/arm/kvm/interrupts_head.S
@@ -303,13 +303,17 @@ vcpu .req r0 @ vcpu pointer always in r0
mrc p15, 0, r2, c14, c1, 0 @ CNTKCTL
mrrc p15, 0, r4, r5, c7 @ PAR
+ mrc p15, 0, r6, c10, c3, 0 @ AMAIR0
+ mrc p15, 0, r7, c10, c3, 1 @ AMAIR1
.if \store_to_vcpu == 0
- push {r2,r4-r5}
+ push {r2,r4-r7}
.else
str r2, [vcpu, #CP15_OFFSET(c14_CNTKCTL)]
add r12, vcpu, #CP15_OFFSET(c7_PAR)
strd r4, r5, [r12]
+ str r6, [vcpu, #CP15_OFFSET(c10_AMAIR0)]
+ str r7, [vcpu, #CP15_OFFSET(c10_AMAIR1)]
.endif
.endm
@@ -322,15 +326,19 @@ vcpu .req r0 @ vcpu pointer always in r0
*/
.macro write_cp15_state read_from_vcpu
.if \read_from_vcpu == 0
- pop {r2,r4-r5}
+ pop {r2,r4-r7}
.else
ldr r2, [vcpu, #CP15_OFFSET(c14_CNTKCTL)]
add r12, vcpu, #CP15_OFFSET(c7_PAR)
ldrd r4, r5, [r12]
+ ldr r6, [vcpu, #CP15_OFFSET(c10_AMAIR0)]
+ ldr r7, [vcpu, #CP15_OFFSET(c10_AMAIR1)]
.endif
mcr p15, 0, r2, c14, c1, 0 @ CNTKCTL
mcrr p15, 0, r4, r5, c7 @ PAR
+ mcr p15, 0, r6, c10, c3, 0 @ AMAIR0
+ mcr p15, 0, r7, c10, c3, 1 @ AMAIR1
.if \read_from_vcpu == 0
pop {r2-r12}
@@ -597,17 +605,14 @@ vcpu .req r0 @ vcpu pointer always in r0
/* Enable/Disable: stage-2 trans., trap interrupts, trap wfi, trap smc */
.macro configure_hyp_role operation
- mrc p15, 4, r2, c1, c1, 0 @ HCR
- bic r2, r2, #HCR_VIRT_EXCP_MASK
- ldr r3, =HCR_GUEST_MASK
.if \operation == vmentry
- orr r2, r2, r3
+ ldr r2, [vcpu, #VCPU_HCR]
ldr r3, [vcpu, #VCPU_IRQ_LINES]
orr r2, r2, r3
.else
- bic r2, r2, r3
+ mov r2, #0
.endif
- mcr p15, 4, r2, c1, c1, 0
+ mcr p15, 4, r2, c1, c1, 0 @ HCR
.endm
.macro load_vcpu
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 7789857d1470..80bb1e6c2c29 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -144,8 +144,9 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
while (addr < end) {
pgd = pgdp + pgd_index(addr);
pud = pud_offset(pgd, addr);
+ pte = NULL;
if (pud_none(*pud)) {
- addr = pud_addr_end(addr, end);
+ addr = kvm_pud_addr_end(addr, end);
continue;
}
@@ -155,13 +156,13 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
* move on.
*/
clear_pud_entry(kvm, pud, addr);
- addr = pud_addr_end(addr, end);
+ addr = kvm_pud_addr_end(addr, end);
continue;
}
pmd = pmd_offset(pud, addr);
if (pmd_none(*pmd)) {
- addr = pmd_addr_end(addr, end);
+ addr = kvm_pmd_addr_end(addr, end);
continue;
}
@@ -174,12 +175,12 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
/*
* If the pmd entry is to be cleared, walk back up the ladder
*/
- if (kvm_pmd_huge(*pmd) || page_empty(pte)) {
+ if (kvm_pmd_huge(*pmd) || (pte && page_empty(pte))) {
clear_pmd_entry(kvm, pmd, addr);
- next = pmd_addr_end(addr, end);
+ next = kvm_pmd_addr_end(addr, end);
if (page_empty(pmd) && !page_empty(pud)) {
clear_pud_entry(kvm, pud, addr);
- next = pud_addr_end(addr, end);
+ next = kvm_pud_addr_end(addr, end);
}
}
@@ -187,6 +188,99 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
}
}
+static void stage2_flush_ptes(struct kvm *kvm, pmd_t *pmd,
+ phys_addr_t addr, phys_addr_t end)
+{
+ pte_t *pte;
+
+ pte = pte_offset_kernel(pmd, addr);
+ do {
+ if (!pte_none(*pte)) {
+ hva_t hva = gfn_to_hva(kvm, addr >> PAGE_SHIFT);
+ kvm_flush_dcache_to_poc((void*)hva, PAGE_SIZE);
+ }
+ } while (pte++, addr += PAGE_SIZE, addr != end);
+}
+
+static void stage2_flush_pmds(struct kvm *kvm, pud_t *pud,
+ phys_addr_t addr, phys_addr_t end)
+{
+ pmd_t *pmd;
+ phys_addr_t next;
+
+ pmd = pmd_offset(pud, addr);
+ do {
+ next = kvm_pmd_addr_end(addr, end);
+ if (!pmd_none(*pmd)) {
+ if (kvm_pmd_huge(*pmd)) {
+ hva_t hva = gfn_to_hva(kvm, addr >> PAGE_SHIFT);
+ kvm_flush_dcache_to_poc((void*)hva, PMD_SIZE);
+ } else {
+ stage2_flush_ptes(kvm, pmd, addr, next);
+ }
+ }
+ } while (pmd++, addr = next, addr != end);
+}
+
+static void stage2_flush_puds(struct kvm *kvm, pgd_t *pgd,
+ phys_addr_t addr, phys_addr_t end)
+{
+ pud_t *pud;
+ phys_addr_t next;
+
+ pud = pud_offset(pgd, addr);
+ do {
+ next = kvm_pud_addr_end(addr, end);
+ if (!pud_none(*pud)) {
+ if (pud_huge(*pud)) {
+ hva_t hva = gfn_to_hva(kvm, addr >> PAGE_SHIFT);
+ kvm_flush_dcache_to_poc((void*)hva, PUD_SIZE);
+ } else {
+ stage2_flush_pmds(kvm, pud, addr, next);
+ }
+ }
+ } while (pud++, addr = next, addr != end);
+}
+
+static void stage2_flush_memslot(struct kvm *kvm,
+ struct kvm_memory_slot *memslot)
+{
+ phys_addr_t addr = memslot->base_gfn << PAGE_SHIFT;
+ phys_addr_t end = addr + PAGE_SIZE * memslot->npages;
+ phys_addr_t next;
+ pgd_t *pgd;
+
+ pgd = kvm->arch.pgd + pgd_index(addr);
+ do {
+ next = kvm_pgd_addr_end(addr, end);
+ stage2_flush_puds(kvm, pgd, addr, next);
+ } while (pgd++, addr = next, addr != end);
+}
+
+/**
+ * stage2_flush_vm - Invalidate cache for pages mapped in stage 2
+ * @kvm: The struct kvm pointer
+ *
+ * Go through the stage 2 page tables and invalidate any cache lines
+ * backing memory already mapped to the VM.
+ */
+void stage2_flush_vm(struct kvm *kvm)
+{
+ struct kvm_memslots *slots;
+ struct kvm_memory_slot *memslot;
+ int idx;
+
+ idx = srcu_read_lock(&kvm->srcu);
+ spin_lock(&kvm->mmu_lock);
+
+ slots = kvm_memslots(kvm);
+ kvm_for_each_memslot(memslot, slots)
+ stage2_flush_memslot(kvm, memslot);
+
+ spin_unlock(&kvm->mmu_lock);
+ srcu_read_unlock(&kvm->srcu, idx);
+}
+
/**
* free_boot_hyp_pgd - free HYP boot page tables
*
@@ -715,7 +809,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
kvm_set_s2pmd_writable(&new_pmd);
kvm_set_pfn_dirty(pfn);
}
- coherent_icache_guest_page(kvm, hva & PMD_MASK, PMD_SIZE);
+ coherent_cache_guest_page(vcpu, hva & PMD_MASK, PMD_SIZE);
ret = stage2_set_pmd_huge(kvm, memcache, fault_ipa, &new_pmd);
} else {
pte_t new_pte = pfn_pte(pfn, PAGE_S2);
@@ -723,7 +817,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
kvm_set_s2pte_writable(&new_pte);
kvm_set_pfn_dirty(pfn);
}
- coherent_icache_guest_page(kvm, hva, PAGE_SIZE);
+ coherent_cache_guest_page(vcpu, hva, PAGE_SIZE);
ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte, false);
}
diff --git a/arch/arm/lib/bitops.h b/arch/arm/lib/bitops.h
index 52886b89706c..9f12ed1eea86 100644
--- a/arch/arm/lib/bitops.h
+++ b/arch/arm/lib/bitops.h
@@ -37,6 +37,11 @@ UNWIND( .fnstart )
add r1, r1, r0, lsl #2 @ Get word offset
mov r3, r2, lsl r3 @ create mask
smp_dmb
+#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP)
+ .arch_extension mp
+ ALT_SMP(W(pldw) [r1])
+ ALT_UP(W(nop))
+#endif
1: ldrex r2, [r1]
ands r0, r2, r3 @ save old value of bit
\instr r2, r2, r3 @ toggle bit
diff --git a/arch/arm/lib/copy_template.S b/arch/arm/lib/copy_template.S
index 805e3f8fb007..3bc8eb811a73 100644
--- a/arch/arm/lib/copy_template.S
+++ b/arch/arm/lib/copy_template.S
@@ -197,24 +197,24 @@
12: PLD( pld [r1, #124] )
13: ldr4w r1, r4, r5, r6, r7, abort=19f
- mov r3, lr, pull #\pull
+ mov r3, lr, lspull #\pull
subs r2, r2, #32
ldr4w r1, r8, r9, ip, lr, abort=19f
- orr r3, r3, r4, push #\push
- mov r4, r4, pull #\pull
- orr r4, r4, r5, push #\push
- mov r5, r5, pull #\pull
- orr r5, r5, r6, push #\push
- mov r6, r6, pull #\pull
- orr r6, r6, r7, push #\push
- mov r7, r7, pull #\pull
- orr r7, r7, r8, push #\push
- mov r8, r8, pull #\pull
- orr r8, r8, r9, push #\push
- mov r9, r9, pull #\pull
- orr r9, r9, ip, push #\push
- mov ip, ip, pull #\pull
- orr ip, ip, lr, push #\push
+ orr r3, r3, r4, lspush #\push
+ mov r4, r4, lspull #\pull
+ orr r4, r4, r5, lspush #\push
+ mov r5, r5, lspull #\pull
+ orr r5, r5, r6, lspush #\push
+ mov r6, r6, lspull #\pull
+ orr r6, r6, r7, lspush #\push
+ mov r7, r7, lspull #\pull
+ orr r7, r7, r8, lspush #\push
+ mov r8, r8, lspull #\pull
+ orr r8, r8, r9, lspush #\push
+ mov r9, r9, lspull #\pull
+ orr r9, r9, ip, lspush #\push
+ mov ip, ip, lspull #\pull
+ orr ip, ip, lr, lspush #\push
str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f
bge 12b
PLD( cmn r2, #96 )
@@ -225,10 +225,10 @@
14: ands ip, r2, #28
beq 16f
-15: mov r3, lr, pull #\pull
+15: mov r3, lr, lspull #\pull
ldr1w r1, lr, abort=21f
subs ip, ip, #4
- orr r3, r3, lr, push #\push
+ orr r3, r3, lr, lspush #\push
str1w r0, r3, abort=21f
bgt 15b
CALGN( cmp r2, #0 )
diff --git a/arch/arm/lib/csumpartialcopygeneric.S b/arch/arm/lib/csumpartialcopygeneric.S
index d620a5f22a09..d6e742d24007 100644
--- a/arch/arm/lib/csumpartialcopygeneric.S
+++ b/arch/arm/lib/csumpartialcopygeneric.S
@@ -141,7 +141,7 @@ FN_ENTRY
tst len, #2
mov r5, r4, get_byte_0
beq .Lexit
- adcs sum, sum, r4, push #16
+ adcs sum, sum, r4, lspush #16
strb r5, [dst], #1
mov r5, r4, get_byte_1
strb r5, [dst], #1
@@ -171,23 +171,23 @@ FN_ENTRY
cmp ip, #2
beq .Lsrc2_aligned
bhi .Lsrc3_aligned
- mov r4, r5, pull #8 @ C = 0
+ mov r4, r5, lspull #8 @ C = 0
bics ip, len, #15
beq 2f
1: load4l r5, r6, r7, r8
- orr r4, r4, r5, push #24
- mov r5, r5, pull #8
- orr r5, r5, r6, push #24
- mov r6, r6, pull #8
- orr r6, r6, r7, push #24
- mov r7, r7, pull #8
- orr r7, r7, r8, push #24
+ orr r4, r4, r5, lspush #24
+ mov r5, r5, lspull #8
+ orr r5, r5, r6, lspush #24
+ mov r6, r6, lspull #8
+ orr r6, r6, r7, lspush #24
+ mov r7, r7, lspull #8
+ orr r7, r7, r8, lspush #24
stmia dst!, {r4, r5, r6, r7}
adcs sum, sum, r4
adcs sum, sum, r5
adcs sum, sum, r6
adcs sum, sum, r7
- mov r4, r8, pull #8
+ mov r4, r8, lspull #8
sub ip, ip, #16
teq ip, #0
bne 1b
@@ -196,50 +196,50 @@ FN_ENTRY
tst ip, #8
beq 3f
load2l r5, r6
- orr r4, r4, r5, push #24
- mov r5, r5, pull #8
- orr r5, r5, r6, push #24
+ orr r4, r4, r5, lspush #24
+ mov r5, r5, lspull #8
+ orr r5, r5, r6, lspush #24
stmia dst!, {r4, r5}
adcs sum, sum, r4
adcs sum, sum, r5
- mov r4, r6, pull #8
+ mov r4, r6, lspull #8
tst ip, #4
beq 4f
3: load1l r5
- orr r4, r4, r5, push #24
+ orr r4, r4, r5, lspush #24
str r4, [dst], #4
adcs sum, sum, r4
- mov r4, r5, pull #8
+ mov r4, r5, lspull #8
4: ands len, len, #3
beq .Ldone
mov r5, r4, get_byte_0
tst len, #2
beq .Lexit
- adcs sum, sum, r4, push #16
+ adcs sum, sum, r4, lspush #16
strb r5, [dst], #1
mov r5, r4, get_byte_1
strb r5, [dst], #1
mov r5, r4, get_byte_2
b .Lexit
-.Lsrc2_aligned: mov r4, r5, pull #16
+.Lsrc2_aligned: mov r4, r5, lspull #16
adds sum, sum, #0
bics ip, len, #15
beq 2f
1: load4l r5, r6, r7, r8
- orr r4, r4, r5, push #16
- mov r5, r5, pull #16
- orr r5, r5, r6, push #16
- mov r6, r6, pull #16
- orr r6, r6, r7, push #16
- mov r7, r7, pull #16
- orr r7, r7, r8, push #16
+ orr r4, r4, r5, lspush #16
+ mov r5, r5, lspull #16
+ orr r5, r5, r6, lspush #16
+ mov r6, r6, lspull #16
+ orr r6, r6, r7, lspush #16
+ mov r7, r7, lspull #16
+ orr r7, r7, r8, lspush #16
stmia dst!, {r4, r5, r6, r7}
adcs sum, sum, r4
adcs sum, sum, r5
adcs sum, sum, r6
adcs sum, sum, r7
- mov r4, r8, pull #16
+ mov r4, r8, lspull #16
sub ip, ip, #16
teq ip, #0
bne 1b
@@ -248,20 +248,20 @@ FN_ENTRY
tst ip, #8
beq 3f
load2l r5, r6
- orr r4, r4, r5, push #16
- mov r5, r5, pull #16
- orr r5, r5, r6, push #16
+ orr r4, r4, r5, lspush #16
+ mov r5, r5, lspull #16
+ orr r5, r5, r6, lspush #16
stmia dst!, {r4, r5}
adcs sum, sum, r4
adcs sum, sum, r5
- mov r4, r6, pull #16
+ mov r4, r6, lspull #16
tst ip, #4
beq 4f
3: load1l r5
- orr r4, r4, r5, push #16
+ orr r4, r4, r5, lspush #16
str r4, [dst], #4
adcs sum, sum, r4
- mov r4, r5, pull #16
+ mov r4, r5, lspull #16
4: ands len, len, #3
beq .Ldone
mov r5, r4, get_byte_0
@@ -276,24 +276,24 @@ FN_ENTRY
load1b r5
b .Lexit
-.Lsrc3_aligned: mov r4, r5, pull #24
+.Lsrc3_aligned: mov r4, r5, lspull #24
adds sum, sum, #0
bics ip, len, #15
beq 2f
1: load4l r5, r6, r7, r8
- orr r4, r4, r5, push #8
- mov r5, r5, pull #24
- orr r5, r5, r6, push #8
- mov r6, r6, pull #24
- orr r6, r6, r7, push #8
- mov r7, r7, pull #24
- orr r7, r7, r8, push #8
+ orr r4, r4, r5, lspush #8
+ mov r5, r5, lspull #24
+ orr r5, r5, r6, lspush #8
+ mov r6, r6, lspull #24
+ orr r6, r6, r7, lspush #8
+ mov r7, r7, lspull #24
+ orr r7, r7, r8, lspush #8
stmia dst!, {r4, r5, r6, r7}
adcs sum, sum, r4
adcs sum, sum, r5
adcs sum, sum, r6
adcs sum, sum, r7
- mov r4, r8, pull #24
+ mov r4, r8, lspull #24
sub ip, ip, #16
teq ip, #0
bne 1b
@@ -302,20 +302,20 @@ FN_ENTRY
tst ip, #8
beq 3f
load2l r5, r6
- orr r4, r4, r5, push #8
- mov r5, r5, pull #24
- orr r5, r5, r6, push #8
+ orr r4, r4, r5, lspush #8
+ mov r5, r5, lspull #24
+ orr r5, r5, r6, lspush #8
stmia dst!, {r4, r5}
adcs sum, sum, r4
adcs sum, sum, r5
- mov r4, r6, pull #24
+ mov r4, r6, lspull #24
tst ip, #4
beq 4f
3: load1l r5
- orr r4, r4, r5, push #8
+ orr r4, r4, r5, lspush #8
str r4, [dst], #4
adcs sum, sum, r4
- mov r4, r5, pull #24
+ mov r4, r5, lspull #24
4: ands len, len, #3
beq .Ldone
mov r5, r4, get_byte_0
@@ -326,7 +326,7 @@ FN_ENTRY
load1l r4
mov r5, r4, get_byte_0
strb r5, [dst], #1
- adcs sum, sum, r4, push #24
+ adcs sum, sum, r4, lspush #24
mov r5, r4, get_byte_1
b .Lexit
FN_EXIT
diff --git a/arch/arm/lib/io-readsl.S b/arch/arm/lib/io-readsl.S
index 5fb97e7f9f4b..7a7430950c79 100644
--- a/arch/arm/lib/io-readsl.S
+++ b/arch/arm/lib/io-readsl.S
@@ -47,25 +47,25 @@ ENTRY(__raw_readsl)
strb ip, [r1], #1
4: subs r2, r2, #1
- mov ip, r3, pull #24
+ mov ip, r3, lspull #24
ldrne r3, [r0]
- orrne ip, ip, r3, push #8
+ orrne ip, ip, r3, lspush #8
strne ip, [r1], #4
bne 4b
b 8f
5: subs r2, r2, #1
- mov ip, r3, pull #16
+ mov ip, r3, lspull #16
ldrne r3, [r0]
- orrne ip, ip, r3, push #16
+ orrne ip, ip, r3, lspush #16
strne ip, [r1], #4
bne 5b
b 7f
6: subs r2, r2, #1
- mov ip, r3, pull #8
+ mov ip, r3, lspull #8
ldrne r3, [r0]
- orrne ip, ip, r3, push #24
+ orrne ip, ip, r3, lspush #24
strne ip, [r1], #4
bne 6b
diff --git a/arch/arm/lib/io-writesl.S b/arch/arm/lib/io-writesl.S
index 8d3b7813725c..d0d104a0dd11 100644
--- a/arch/arm/lib/io-writesl.S
+++ b/arch/arm/lib/io-writesl.S
@@ -41,26 +41,26 @@ ENTRY(__raw_writesl)
blt 5f
bgt 6f
-4: mov ip, r3, pull #16
+4: mov ip, r3, lspull #16
ldr r3, [r1], #4
subs r2, r2, #1
- orr ip, ip, r3, push #16
+ orr ip, ip, r3, lspush #16
str ip, [r0]
bne 4b
mov pc, lr
-5: mov ip, r3, pull #8
+5: mov ip, r3, lspull #8
ldr r3, [r1], #4
subs r2, r2, #1
- orr ip, ip, r3, push #24
+ orr ip, ip, r3, lspush #24
str ip, [r0]
bne 5b
mov pc, lr
-6: mov ip, r3, pull #24
+6: mov ip, r3, lspull #24
ldr r3, [r1], #4
subs r2, r2, #1
- orr ip, ip, r3, push #8
+ orr ip, ip, r3, lspush #8
str ip, [r0]
bne 6b
mov pc, lr
diff --git a/arch/arm/lib/memmove.S b/arch/arm/lib/memmove.S
index 938fc14f962d..d1fc0c0c342c 100644
--- a/arch/arm/lib/memmove.S
+++ b/arch/arm/lib/memmove.S
@@ -147,24 +147,24 @@ ENTRY(memmove)
12: PLD( pld [r1, #-128] )
13: ldmdb r1!, {r7, r8, r9, ip}
- mov lr, r3, push #\push
+ mov lr, r3, lspush #\push
subs r2, r2, #32
ldmdb r1!, {r3, r4, r5, r6}
- orr lr, lr, ip, pull #\pull
- mov ip, ip, push #\push
- orr ip, ip, r9, pull #\pull
- mov r9, r9, push #\push
- orr r9, r9, r8, pull #\pull
- mov r8, r8, push #\push
- orr r8, r8, r7, pull #\pull
- mov r7, r7, push #\push
- orr r7, r7, r6, pull #\pull
- mov r6, r6, push #\push
- orr r6, r6, r5, pull #\pull
- mov r5, r5, push #\push
- orr r5, r5, r4, pull #\pull
- mov r4, r4, push #\push
- orr r4, r4, r3, pull #\pull
+ orr lr, lr, ip, lspull #\pull
+ mov ip, ip, lspush #\push
+ orr ip, ip, r9, lspull #\pull
+ mov r9, r9, lspush #\push
+ orr r9, r9, r8, lspull #\pull
+ mov r8, r8, lspush #\push
+ orr r8, r8, r7, lspull #\pull
+ mov r7, r7, lspush #\push
+ orr r7, r7, r6, lspull #\pull
+ mov r6, r6, lspush #\push
+ orr r6, r6, r5, lspull #\pull
+ mov r5, r5, lspush #\push
+ orr r5, r5, r4, lspull #\pull
+ mov r4, r4, lspush #\push
+ orr r4, r4, r3, lspull #\pull
stmdb r0!, {r4 - r9, ip, lr}
bge 12b
PLD( cmn r2, #96 )
@@ -175,10 +175,10 @@ ENTRY(memmove)
14: ands ip, r2, #28
beq 16f
-15: mov lr, r3, push #\push
+15: mov lr, r3, lspush #\push
ldr r3, [r1, #-4]!
subs ip, ip, #4
- orr lr, lr, r3, pull #\pull
+ orr lr, lr, r3, lspull #\pull
str lr, [r0, #-4]!
bgt 15b
CALGN( cmp r2, #0 )
diff --git a/arch/arm/lib/uaccess.S b/arch/arm/lib/uaccess.S
index 5c908b1cb8ed..e50520904b76 100644
--- a/arch/arm/lib/uaccess.S
+++ b/arch/arm/lib/uaccess.S
@@ -117,9 +117,9 @@ USER( TUSER( strgtb) r3, [r0], #1) @ May fault
.Lc2u_1fupi: subs r2, r2, #4
addmi ip, r2, #4
bmi .Lc2u_1nowords
- mov r3, r7, pull #8
+ mov r3, r7, lspull #8
ldr r7, [r1], #4
- orr r3, r3, r7, push #24
+ orr r3, r3, r7, lspush #24
USER( TUSER( str) r3, [r0], #4) @ May fault
mov ip, r0, lsl #32 - PAGE_SHIFT
rsb ip, ip, #0
@@ -131,30 +131,30 @@ USER( TUSER( str) r3, [r0], #4) @ May fault
subs ip, ip, #16
blt .Lc2u_1rem8lp
-.Lc2u_1cpy8lp: mov r3, r7, pull #8
+.Lc2u_1cpy8lp: mov r3, r7, lspull #8
ldmia r1!, {r4 - r7}
subs ip, ip, #16
- orr r3, r3, r4, push #24
- mov r4, r4, pull #8
- orr r4, r4, r5, push #24
- mov r5, r5, pull #8
- orr r5, r5, r6, push #24
- mov r6, r6, pull #8
- orr r6, r6, r7, push #24
+ orr r3, r3, r4, lspush #24
+ mov r4, r4, lspull #8
+ orr r4, r4, r5, lspush #24
+ mov r5, r5, lspull #8
+ orr r5, r5, r6, lspush #24
+ mov r6, r6, lspull #8
+ orr r6, r6, r7, lspush #24
stmia r0!, {r3 - r6} @ Shouldnt fault
bpl .Lc2u_1cpy8lp
.Lc2u_1rem8lp: tst ip, #8
- movne r3, r7, pull #8
+ movne r3, r7, lspull #8
ldmneia r1!, {r4, r7}
- orrne r3, r3, r4, push #24
- movne r4, r4, pull #8
- orrne r4, r4, r7, push #24
+ orrne r3, r3, r4, lspush #24
+ movne r4, r4, lspull #8
+ orrne r4, r4, r7, lspush #24
stmneia r0!, {r3 - r4} @ Shouldnt fault
tst ip, #4
- movne r3, r7, pull #8
+ movne r3, r7, lspull #8
ldrne r7, [r1], #4
- orrne r3, r3, r7, push #24
+ orrne r3, r3, r7, lspush #24
TUSER( strne) r3, [r0], #4 @ Shouldnt fault
ands ip, ip, #3
beq .Lc2u_1fupi
@@ -172,9 +172,9 @@ USER( TUSER( strgtb) r3, [r0], #1) @ May fault
.Lc2u_2fupi: subs r2, r2, #4
addmi ip, r2, #4
bmi .Lc2u_2nowords
- mov r3, r7, pull #16
+ mov r3, r7, lspull #16
ldr r7, [r1], #4
- orr r3, r3, r7, push #16
+ orr r3, r3, r7, lspush #16
USER( TUSER( str) r3, [r0], #4) @ May fault
mov ip, r0, lsl #32 - PAGE_SHIFT
rsb ip, ip, #0
@@ -186,30 +186,30 @@ USER( TUSER( str) r3, [r0], #4) @ May fault
subs ip, ip, #16
blt .Lc2u_2rem8lp
-.Lc2u_2cpy8lp: mov r3, r7, pull #16
+.Lc2u_2cpy8lp: mov r3, r7, lspull #16
ldmia r1!, {r4 - r7}
subs ip, ip, #16
- orr r3, r3, r4, push #16
- mov r4, r4, pull #16
- orr r4, r4, r5, push #16
- mov r5, r5, pull #16
- orr r5, r5, r6, push #16
- mov r6, r6, pull #16
- orr r6, r6, r7, push #16
+ orr r3, r3, r4, lspush #16
+ mov r4, r4, lspull #16
+ orr r4, r4, r5, lspush #16
+ mov r5, r5, lspull #16
+ orr r5, r5, r6, lspush #16
+ mov r6, r6, lspull #16
+ orr r6, r6, r7, lspush #16
stmia r0!, {r3 - r6} @ Shouldnt fault
bpl .Lc2u_2cpy8lp
.Lc2u_2rem8lp: tst ip, #8
- movne r3, r7, pull #16
+ movne r3, r7, lspull #16
ldmneia r1!, {r4, r7}
- orrne r3, r3, r4, push #16
- movne r4, r4, pull #16
- orrne r4, r4, r7, push #16
+ orrne r3, r3, r4, lspush #16
+ movne r4, r4, lspull #16
+ orrne r4, r4, r7, lspush #16
stmneia r0!, {r3 - r4} @ Shouldnt fault
tst ip, #4
- movne r3, r7, pull #16
+ movne r3, r7, lspull #16
ldrne r7, [r1], #4
- orrne r3, r3, r7, push #16
+ orrne r3, r3, r7, lspush #16
TUSER( strne) r3, [r0], #4 @ Shouldnt fault
ands ip, ip, #3
beq .Lc2u_2fupi
@@ -227,9 +227,9 @@ USER( TUSER( strgtb) r3, [r0], #1) @ May fault
.Lc2u_3fupi: subs r2, r2, #4
addmi ip, r2, #4
bmi .Lc2u_3nowords
- mov r3, r7, pull #24
+ mov r3, r7, lspull #24
ldr r7, [r1], #4
- orr r3, r3, r7, push #8
+ orr r3, r3, r7, lspush #8
USER( TUSER( str) r3, [r0], #4) @ May fault
mov ip, r0, lsl #32 - PAGE_SHIFT
rsb ip, ip, #0
@@ -241,30 +241,30 @@ USER( TUSER( str) r3, [r0], #4) @ May fault
subs ip, ip, #16
blt .Lc2u_3rem8lp
-.Lc2u_3cpy8lp: mov r3, r7, pull #24
+.Lc2u_3cpy8lp: mov r3, r7, lspull #24
ldmia r1!, {r4 - r7}
subs ip, ip, #16
- orr r3, r3, r4, push #8
- mov r4, r4, pull #24
- orr r4, r4, r5, push #8
- mov r5, r5, pull #24
- orr r5, r5, r6, push #8
- mov r6, r6, pull #24
- orr r6, r6, r7, push #8
+ orr r3, r3, r4, lspush #8
+ mov r4, r4, lspull #24
+ orr r4, r4, r5, lspush #8
+ mov r5, r5, lspull #24
+ orr r5, r5, r6, lspush #8
+ mov r6, r6, lspull #24
+ orr r6, r6, r7, lspush #8
stmia r0!, {r3 - r6} @ Shouldnt fault
bpl .Lc2u_3cpy8lp
.Lc2u_3rem8lp: tst ip, #8
- movne r3, r7, pull #24
+ movne r3, r7, lspull #24
ldmneia r1!, {r4, r7}
- orrne r3, r3, r4, push #8
- movne r4, r4, pull #24
- orrne r4, r4, r7, push #8
+ orrne r3, r3, r4, lspush #8
+ movne r4, r4, lspull #24
+ orrne r4, r4, r7, lspush #8
stmneia r0!, {r3 - r4} @ Shouldnt fault
tst ip, #4
- movne r3, r7, pull #24
+ movne r3, r7, lspull #24
ldrne r7, [r1], #4
- orrne r3, r3, r7, push #8
+ orrne r3, r3, r7, lspush #8
TUSER( strne) r3, [r0], #4 @ Shouldnt fault
ands ip, ip, #3
beq .Lc2u_3fupi
@@ -382,9 +382,9 @@ USER( TUSER( ldr) r7, [r1], #4) @ May fault
.Lcfu_1fupi: subs r2, r2, #4
addmi ip, r2, #4
bmi .Lcfu_1nowords
- mov r3, r7, pull #8
+ mov r3, r7, lspull #8
USER( TUSER( ldr) r7, [r1], #4) @ May fault
- orr r3, r3, r7, push #24
+ orr r3, r3, r7, lspush #24
str r3, [r0], #4
mov ip, r1, lsl #32 - PAGE_SHIFT
rsb ip, ip, #0
@@ -396,30 +396,30 @@ USER( TUSER( ldr) r7, [r1], #4) @ May fault
subs ip, ip, #16
blt .Lcfu_1rem8lp
-.Lcfu_1cpy8lp: mov r3, r7, pull #8
+.Lcfu_1cpy8lp: mov r3, r7, lspull #8
ldmia r1!, {r4 - r7} @ Shouldnt fault
subs ip, ip, #16
- orr r3, r3, r4, push #24
- mov r4, r4, pull #8
- orr r4, r4, r5, push #24
- mov r5, r5, pull #8
- orr r5, r5, r6, push #24
- mov r6, r6, pull #8
- orr r6, r6, r7, push #24
+ orr r3, r3, r4, lspush #24
+ mov r4, r4, lspull #8
+ orr r4, r4, r5, lspush #24
+ mov r5, r5, lspull #8
+ orr r5, r5, r6, lspush #24
+ mov r6, r6, lspull #8
+ orr r6, r6, r7, lspush #24
stmia r0!, {r3 - r6}
bpl .Lcfu_1cpy8lp
.Lcfu_1rem8lp: tst ip, #8
- movne r3, r7, pull #8
+ movne r3, r7, lspull #8
ldmneia r1!, {r4, r7} @ Shouldnt fault
- orrne r3, r3, r4, push #24
- movne r4, r4, pull #8
- orrne r4, r4, r7, push #24
+ orrne r3, r3, r4, lspush #24
+ movne r4, r4, lspull #8
+ orrne r4, r4, r7, lspush #24
stmneia r0!, {r3 - r4}
tst ip, #4
- movne r3, r7, pull #8
+ movne r3, r7, lspull #8
USER( TUSER( ldrne) r7, [r1], #4) @ May fault
- orrne r3, r3, r7, push #24
+ orrne r3, r3, r7, lspush #24
strne r3, [r0], #4
ands ip, ip, #3
beq .Lcfu_1fupi
@@ -437,9 +437,9 @@ USER( TUSER( ldrne) r7, [r1], #4) @ May fault
.Lcfu_2fupi: subs r2, r2, #4
addmi ip, r2, #4
bmi .Lcfu_2nowords
- mov r3, r7, pull #16
+ mov r3, r7, lspull #16
USER( TUSER( ldr) r7, [r1], #4) @ May fault
- orr r3, r3, r7, push #16
+ orr r3, r3, r7, lspush #16
str r3, [r0], #4
mov ip, r1, lsl #32 - PAGE_SHIFT
rsb ip, ip, #0
@@ -452,30 +452,30 @@ USER( TUSER( ldr) r7, [r1], #4) @ May fault
blt .Lcfu_2rem8lp
-.Lcfu_2cpy8lp: mov r3, r7, pull #16
+.Lcfu_2cpy8lp: mov r3, r7, lspull #16
ldmia r1!, {r4 - r7} @ Shouldnt fault
subs ip, ip, #16
- orr r3, r3, r4, push #16
- mov r4, r4, pull #16
- orr r4, r4, r5, push #16
- mov r5, r5, pull #16
- orr r5, r5, r6, push #16
- mov r6, r6, pull #16
- orr r6, r6, r7, push #16
+ orr r3, r3, r4, lspush #16
+ mov r4, r4, lspull #16
+ orr r4, r4, r5, lspush #16
+ mov r5, r5, lspull #16
+ orr r5, r5, r6, lspush #16
+ mov r6, r6, lspull #16
+ orr r6, r6, r7, lspush #16
stmia r0!, {r3 - r6}
bpl .Lcfu_2cpy8lp
.Lcfu_2rem8lp: tst ip, #8
- movne r3, r7, pull #16
+ movne r3, r7, lspull #16
ldmneia r1!, {r4, r7} @ Shouldnt fault
- orrne r3, r3, r4, push #16
- movne r4, r4, pull #16
- orrne r4, r4, r7, push #16
+ orrne r3, r3, r4, lspush #16
+ movne r4, r4, lspull #16
+ orrne r4, r4, r7, lspush #16
stmneia r0!, {r3 - r4}
tst ip, #4
- movne r3, r7, pull #16
+ movne r3, r7, lspull #16
USER( TUSER( ldrne) r7, [r1], #4) @ May fault
- orrne r3, r3, r7, push #16
+ orrne r3, r3, r7, lspush #16
strne r3, [r0], #4
ands ip, ip, #3
beq .Lcfu_2fupi
@@ -493,9 +493,9 @@ USER( TUSER( ldrgtb) r3, [r1], #0) @ May fault
.Lcfu_3fupi: subs r2, r2, #4
addmi ip, r2, #4
bmi .Lcfu_3nowords
- mov r3, r7, pull #24
+ mov r3, r7, lspull #24
USER( TUSER( ldr) r7, [r1], #4) @ May fault
- orr r3, r3, r7, push #8
+ orr r3, r3, r7, lspush #8
str r3, [r0], #4
mov ip, r1, lsl #32 - PAGE_SHIFT
rsb ip, ip, #0
@@ -507,30 +507,30 @@ USER( TUSER( ldr) r7, [r1], #4) @ May fault
subs ip, ip, #16
blt .Lcfu_3rem8lp
-.Lcfu_3cpy8lp: mov r3, r7, pull #24
+.Lcfu_3cpy8lp: mov r3, r7, lspull #24
ldmia r1!, {r4 - r7} @ Shouldnt fault
- orr r3, r3, r4, push #8
- mov r4, r4, pull #24
- orr r4, r4, r5, push #8
- mov r5, r5, pull #24
- orr r5, r5, r6, push #8
- mov r6, r6, pull #24
- orr r6, r6, r7, push #8
+ orr r3, r3, r4, lspush #8
+ mov r4, r4, lspull #24
+ orr r4, r4, r5, lspush #8
+ mov r5, r5, lspull #24
+ orr r5, r5, r6, lspush #8
+ mov r6, r6, lspull #24
+ orr r6, r6, r7, lspush #8
stmia r0!, {r3 - r6}
subs ip, ip, #16
bpl .Lcfu_3cpy8lp
.Lcfu_3rem8lp: tst ip, #8
- movne r3, r7, pull #24
+ movne r3, r7, lspull #24
ldmneia r1!, {r4, r7} @ Shouldnt fault
- orrne r3, r3, r4, push #8
- movne r4, r4, pull #24
- orrne r4, r4, r7, push #8
+ orrne r3, r3, r4, lspush #8
+ movne r4, r4, lspull #24
+ orrne r4, r4, r7, lspush #8
stmneia r0!, {r3 - r4}
tst ip, #4
- movne r3, r7, pull #24
+ movne r3, r7, lspull #24
USER( TUSER( ldrne) r7, [r1], #4) @ May fault
- orrne r3, r3, r7, push #8
+ orrne r3, r3, r7, lspush #8
strne r3, [r0], #4
ands ip, ip, #3
beq .Lcfu_3fupi
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 4f0e800e7e71..b2d2cf4dc052 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -57,6 +57,7 @@ config SOC_SAMA5
select GENERIC_CLOCKEVENTS
select MULTI_IRQ_HANDLER
select SPARSE_IRQ
+ select USE_OF
menu "Atmel AT91 System-on-Chip"
@@ -64,11 +65,22 @@ choice
prompt "Core type"
+config ARCH_AT91X40
+ bool "ARM7 AT91X40"
+ depends on !MMU
+ select CPU_ARM7TDMI
+ select ARCH_USES_GETTIMEOFFSET
+ select MULTI_IRQ_HANDLER
+ select SPARSE_IRQ
+
+ help
+ Select this if you are using one of Atmel's AT91X40 SoC.
+
config SOC_SAM_V4_V5
- bool "ARM7/ARM9"
+ bool "ARM9 AT91SAM9/AT91RM9200"
help
- Select this if you are using one of Atmel's AT91SAM9, AT91RM9200
- or AT91X40 SoC.
+ Select this if you are using one of Atmel's AT91SAM9 or
+ AT91RM9200 SoC.
config SOC_SAM_V7
bool "Cortex A5"
@@ -119,7 +131,6 @@ config SOC_AT91SAM9261
select HAVE_AT91_DBGU0
select HAVE_FB_ATMEL
select SOC_AT91SAM9
- select AT91_USE_OLD_CLK
select HAVE_AT91_USB_CLK
help
Select this if you are using one of Atmel's AT91SAM9261 or AT91SAM9G10 SoC.
@@ -137,7 +148,6 @@ config SOC_AT91SAM9RL
select HAVE_AT91_DBGU0
select HAVE_FB_ATMEL
select SOC_AT91SAM9
- select AT91_USE_OLD_CLK
select HAVE_AT91_UTMI
config SOC_AT91SAM9G45
@@ -179,9 +189,12 @@ config SOC_AT91SAM9N12
Select this if you are using Atmel's AT91SAM9N12 SoC.
# ----------------------------------------------------------
+endif # SOC_SAM_V4_V5
+
+if SOC_SAM_V4_V5 || ARCH_AT91X40
source arch/arm/mach-at91/Kconfig.non_dt
-endif # SOC_SAM_V4_V5
+endif
comment "Generic Board Type"
diff --git a/arch/arm/mach-at91/Kconfig.non_dt b/arch/arm/mach-at91/Kconfig.non_dt
index 1f73e9b527da..44ace320d2e1 100644
--- a/arch/arm/mach-at91/Kconfig.non_dt
+++ b/arch/arm/mach-at91/Kconfig.non_dt
@@ -5,6 +5,7 @@ config HAVE_AT91_DATAFLASH_CARD
choice
prompt "Atmel AT91 Processor Devices for non DT boards"
+ depends on !ARCH_AT91X40
config ARCH_AT91_NONE
bool "None"
@@ -39,13 +40,6 @@ config ARCH_AT91SAM9G45
select SOC_AT91SAM9G45
select AT91_USE_OLD_CLK
-config ARCH_AT91X40
- bool "AT91x40"
- depends on !MMU
- select ARCH_USES_GETTIMEOFFSET
- select MULTI_IRQ_HANDLER
- select SPARSE_IRQ
-
endchoice
config ARCH_AT91SAM9G20
diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c
index e47f5fd232f5..787bb50a4dff 100644
--- a/arch/arm/mach-at91/at91rm9200.c
+++ b/arch/arm/mach-at91/at91rm9200.c
@@ -21,6 +21,7 @@
#include <mach/at91rm9200.h>
#include <mach/at91_st.h>
#include <mach/cpu.h>
+#include <mach/hardware.h>
#include "at91_aic.h"
#include "soc.h"
diff --git a/arch/arm/mach-at91/at91rm9200_devices.c b/arch/arm/mach-at91/at91rm9200_devices.c
index 3ebc9792560c..f3f19f21352a 100644
--- a/arch/arm/mach-at91/at91rm9200_devices.c
+++ b/arch/arm/mach-at91/at91rm9200_devices.c
@@ -21,6 +21,7 @@
#include <mach/at91rm9200.h>
#include <mach/at91rm9200_mc.h>
#include <mach/at91_ramc.h>
+#include <mach/hardware.h>
#include "board.h"
#include "generic.h"
@@ -922,6 +923,7 @@ static struct resource dbgu_resources[] = {
static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0, /* DBGU not capable of receive DMA */
+ .rts_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -960,6 +962,7 @@ static struct resource uart0_resources[] = {
static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -987,9 +990,10 @@ static inline void configure_usart0_pins(unsigned pins)
if (pins & ATMEL_UART_RTS) {
/*
* AT91RM9200 Errata #39 - RTS0 is not internally connected to PA21.
- * We need to drive the pin manually. Default is off (RTS is active low).
+ * We need to drive the pin manually. The serial driver will driver
+ * this to high when initializing.
*/
- at91_set_gpio_output(AT91_PIN_PA21, 1);
+ uart0_data.rts_gpio = AT91_PIN_PA21;
}
}
@@ -1009,6 +1013,7 @@ static struct resource uart1_resources[] = {
static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1060,6 +1065,7 @@ static struct resource uart2_resources[] = {
static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -1103,6 +1109,7 @@ static struct resource uart3_resources[] = {
static struct atmel_uart_data uart3_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart3_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91rm9200_time.c b/arch/arm/mach-at91/at91rm9200_time.c
index bc7b363a3083..7fd13aef9827 100644
--- a/arch/arm/mach-at91/at91rm9200_time.c
+++ b/arch/arm/mach-at91/at91rm9200_time.c
@@ -31,6 +31,7 @@
#include <asm/mach/time.h>
#include <mach/at91_st.h>
+#include <mach/hardware.h>
static unsigned long last_crtr;
static u32 irqmask;
diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c
index 6c821e562159..c3d22be73b7c 100644
--- a/arch/arm/mach-at91/at91sam9260.c
+++ b/arch/arm/mach-at91/at91sam9260.c
@@ -21,6 +21,7 @@
#include <mach/cpu.h>
#include <mach/at91_dbgu.h>
#include <mach/at91sam9260.h>
+#include <mach/hardware.h>
#include "at91_aic.h"
#include "at91_rstc.h"
diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index eda8d1679d40..8b1b0a870025 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -25,6 +25,7 @@
#include <mach/at91_matrix.h>
#include <mach/at91sam9_smc.h>
#include <mach/at91_adc.h>
+#include <mach/hardware.h>
#include "board.h"
#include "generic.h"
@@ -819,6 +820,7 @@ static struct resource dbgu_resources[] = {
static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0, /* DBGU not capable of receive DMA */
+ .rts_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -857,6 +859,7 @@ static struct resource uart0_resources[] = {
static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -908,6 +911,7 @@ static struct resource uart1_resources[] = {
static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -951,6 +955,7 @@ static struct resource uart2_resources[] = {
static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -994,6 +999,7 @@ static struct resource uart3_resources[] = {
static struct atmel_uart_data uart3_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart3_dmamask = DMA_BIT_MASK(32);
@@ -1037,6 +1043,7 @@ static struct resource uart4_resources[] = {
static struct atmel_uart_data uart4_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart4_dmamask = DMA_BIT_MASK(32);
@@ -1075,6 +1082,7 @@ static struct resource uart5_resources[] = {
static struct atmel_uart_data uart5_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart5_dmamask = DMA_BIT_MASK(32);
@@ -1255,12 +1263,8 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
at91_set_A_periph(AT91_PIN_PC10, 0); /* CFRNW */
at91_set_A_periph(AT91_PIN_PC15, 1); /* NWAIT */
- if (data->flags & AT91_CF_TRUE_IDE)
-#if defined(CONFIG_PATA_AT91) || defined(CONFIG_PATA_AT91_MODULE)
+ if (IS_ENABLED(CONFIG_PATA_AT91) && (data->flags & AT91_CF_TRUE_IDE))
pdev->name = "pata_at91";
-#else
-#warning "board requires AT91_CF_TRUE_IDE: enable pata_at91"
-#endif
else
pdev->name = "at91_cf";
diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
index 6276b4c1acfe..fb164a5d04a9 100644
--- a/arch/arm/mach-at91/at91sam9261.c
+++ b/arch/arm/mach-at91/at91sam9261.c
@@ -20,15 +20,18 @@
#include <asm/system_misc.h>
#include <mach/cpu.h>
#include <mach/at91sam9261.h>
+#include <mach/hardware.h>
#include "at91_aic.h"
#include "at91_rstc.h"
#include "soc.h"
#include "generic.h"
-#include "clock.h"
#include "sam9_smc.h"
#include "pm.h"
+#if defined(CONFIG_OLD_CLK_AT91)
+#include "clock.h"
+
/* --------------------------------------------------------------------
* Clocks
* -------------------------------------------------------------------- */
@@ -189,6 +192,23 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_ID("pioA", &pioA_clk),
CLKDEV_CON_ID("pioB", &pioB_clk),
CLKDEV_CON_ID("pioC", &pioC_clk),
+ /* more lookup table for DT entries */
+ CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck),
+ CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk),
+ CLKDEV_CON_DEV_ID("usart", "ffffb400.serial", &usart1_clk),
+ CLKDEV_CON_DEV_ID("usart", "fff94000.serial", &usart2_clk),
+ CLKDEV_CON_DEV_ID("t0_clk", "fffa0000.timer", &tc0_clk),
+ CLKDEV_CON_DEV_ID("t1_clk", "fffa0000.timer", &tc1_clk),
+ CLKDEV_CON_DEV_ID("t2_clk", "fffa0000.timer", &tc2_clk),
+ CLKDEV_CON_DEV_ID("hclk", "500000.ohci", &hck0),
+ CLKDEV_CON_DEV_ID("hclk", "600000.fb", &hck1),
+ CLKDEV_CON_DEV_ID("spi_clk", "fffc8000.spi", &spi0_clk),
+ CLKDEV_CON_DEV_ID("spi_clk", "fffcc000.spi", &spi1_clk),
+ CLKDEV_CON_DEV_ID("mci_clk", "fffa8000.mmc", &mmc_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffac000.i2c", &twi_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffff400.gpio", &pioA_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioB_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioC_clk),
};
static struct clk_lookup usart_clocks_lookups[] = {
@@ -247,7 +267,9 @@ static void __init at91sam9261_register_clocks(void)
clk_register(&hck0);
clk_register(&hck1);
}
-
+#else
+#define at91sam9261_register_clocks NULL
+#endif
/* --------------------------------------------------------------------
* GPIO
* -------------------------------------------------------------------- */
diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c
index b2a34740146a..80e35895d28f 100644
--- a/arch/arm/mach-at91/at91sam9261_devices.c
+++ b/arch/arm/mach-at91/at91sam9261_devices.c
@@ -25,6 +25,7 @@
#include <mach/at91sam9261_matrix.h>
#include <mach/at91_matrix.h>
#include <mach/at91sam9_smc.h>
+#include <mach/hardware.h>
#include "board.h"
#include "generic.h"
@@ -880,6 +881,7 @@ static struct resource dbgu_resources[] = {
static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0, /* DBGU not capable of receive DMA */
+ .rts_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -918,6 +920,7 @@ static struct resource uart0_resources[] = {
static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -961,6 +964,7 @@ static struct resource uart1_resources[] = {
static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1004,6 +1008,7 @@ static struct resource uart2_resources[] = {
static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
index 37b90f4b990c..f30290572293 100644
--- a/arch/arm/mach-at91/at91sam9263.c
+++ b/arch/arm/mach-at91/at91sam9263.c
@@ -19,6 +19,7 @@
#include <asm/mach/map.h>
#include <asm/system_misc.h>
#include <mach/at91sam9263.h>
+#include <mach/hardware.h>
#include "at91_aic.h"
#include "at91_rstc.h"
@@ -223,6 +224,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioCDE_clk),
CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioCDE_clk),
CLKDEV_CON_DEV_ID(NULL, "fffffa00.gpio", &pioCDE_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffb8000.pwm", &pwm_clk),
};
static struct clk_lookup usart_clocks_lookups[] = {
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
index 4aeadddbc181..43d53d6156dd 100644
--- a/arch/arm/mach-at91/at91sam9263_devices.c
+++ b/arch/arm/mach-at91/at91sam9263_devices.c
@@ -24,6 +24,7 @@
#include <mach/at91sam9263_matrix.h>
#include <mach/at91_matrix.h>
#include <mach/at91sam9_smc.h>
+#include <mach/hardware.h>
#include "board.h"
#include "generic.h"
@@ -1324,6 +1325,7 @@ static struct resource dbgu_resources[] = {
static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0, /* DBGU not capable of receive DMA */
+ .rts_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -1362,6 +1364,7 @@ static struct resource uart0_resources[] = {
static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -1405,6 +1408,7 @@ static struct resource uart1_resources[] = {
static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1448,6 +1452,7 @@ static struct resource uart2_resources[] = {
static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c
index 0f04ffe9c5a8..0a9e2fc8f796 100644
--- a/arch/arm/mach-at91/at91sam926x_time.c
+++ b/arch/arm/mach-at91/at91sam926x_time.c
@@ -19,6 +19,7 @@
#include <linux/of_irq.h>
#include <asm/mach/time.h>
+#include <mach/hardware.h>
#define AT91_PIT_MR 0x00 /* Mode Register */
#define AT91_PIT_PITIEN (1 << 25) /* Timer Interrupt Enable */
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index 2f455ce35268..5e6f498db0a8 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -20,6 +20,7 @@
#include <asm/system_misc.h>
#include <mach/at91sam9g45.h>
#include <mach/cpu.h>
+#include <mach/hardware.h>
#include "at91_aic.h"
#include "soc.h"
@@ -284,6 +285,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_ID("pioE", &pioDE_clk),
/* Fake adc clock */
CLKDEV_CON_ID("adc_clk", &tsc_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffb8000.pwm", &pwm_clk),
};
static struct clk_lookup usart_clocks_lookups[] = {
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index cb36fa872d30..77b04c2edd78 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -32,6 +32,7 @@
#include <mach/at91sam9_smc.h>
#include <linux/platform_data/dma-atmel.h>
#include <mach/atmel-mci.h>
+#include <mach/hardware.h>
#include <media/atmel-isi.h>
@@ -1587,6 +1588,7 @@ static struct resource dbgu_resources[] = {
static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0,
+ .rts_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -1625,6 +1627,7 @@ static struct resource uart0_resources[] = {
static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -1668,6 +1671,7 @@ static struct resource uart1_resources[] = {
static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1711,6 +1715,7 @@ static struct resource uart2_resources[] = {
static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -1754,6 +1759,7 @@ static struct resource uart3_resources[] = {
static struct atmel_uart_data uart3_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart3_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9n12.c b/arch/arm/mach-at91/at91sam9n12.c
index 4ef088c62eab..f2ea7b0a02da 100644
--- a/arch/arm/mach-at91/at91sam9n12.c
+++ b/arch/arm/mach-at91/at91sam9n12.c
@@ -182,6 +182,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
/* additional fake clock for macb_hclk */
CLKDEV_CON_DEV_ID("hclk", "500000.ohci", &uhp_clk),
CLKDEV_CON_DEV_ID("ohci_clk", "500000.ohci", &uhp_clk),
+ CLKDEV_CON_DEV_ID(NULL, "f8034000.pwm", &pwm_clk),
};
/*
diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c
index 3651517abedf..57f12d86c0e6 100644
--- a/arch/arm/mach-at91/at91sam9rl.c
+++ b/arch/arm/mach-at91/at91sam9rl.c
@@ -20,18 +20,20 @@
#include <mach/cpu.h>
#include <mach/at91_dbgu.h>
#include <mach/at91sam9rl.h>
+#include <mach/hardware.h>
#include "at91_aic.h"
#include "at91_rstc.h"
#include "soc.h"
#include "generic.h"
-#include "clock.h"
#include "sam9_smc.h"
#include "pm.h"
/* --------------------------------------------------------------------
* Clocks
* -------------------------------------------------------------------- */
+#if defined(CONFIG_OLD_CLK_AT91)
+#include "clock.h"
/*
* The peripheral clocks.
@@ -196,6 +198,24 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_ID("pioB", &pioB_clk),
CLKDEV_CON_ID("pioC", &pioC_clk),
CLKDEV_CON_ID("pioD", &pioD_clk),
+ /* more lookup table for DT entries */
+ CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck),
+ CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk),
+ CLKDEV_CON_DEV_ID("usart", "ffffb400.serial", &usart1_clk),
+ CLKDEV_CON_DEV_ID("usart", "ffffb800.serial", &usart2_clk),
+ CLKDEV_CON_DEV_ID("usart", "ffffbc00.serial", &usart3_clk),
+ CLKDEV_CON_DEV_ID("t0_clk", "fffa0000.timer", &tc0_clk),
+ CLKDEV_CON_DEV_ID("t1_clk", "fffa0000.timer", &tc1_clk),
+ CLKDEV_CON_DEV_ID("t2_clk", "fffa0000.timer", &tc2_clk),
+ CLKDEV_CON_DEV_ID("mci_clk", "fffa4000.mmc", &mmc_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffa8000.i2c", &twi0_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffac000.i2c", &twi1_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffc8000.pwm", &pwm_clk),
+ CLKDEV_CON_DEV_ID(NULL, "ffffc800.pwm", &pwm_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffff400.gpio", &pioA_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioB_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioC_clk),
+ CLKDEV_CON_DEV_ID(NULL, "fffffa00.gpio", &pioD_clk),
};
static struct clk_lookup usart_clocks_lookups[] = {
@@ -238,6 +258,7 @@ static void __init at91sam9rl_register_clocks(void)
clk_register(&pck0);
clk_register(&pck1);
}
+#endif
/* --------------------------------------------------------------------
* GPIO
@@ -350,6 +371,8 @@ AT91_SOC_START(at91sam9rl)
.default_irq_priority = at91sam9rl_default_irq_priority,
.extern_irq = (1 << AT91SAM9RL_ID_IRQ0),
.ioremap_registers = at91sam9rl_ioremap_registers,
+#if defined(CONFIG_OLD_CLK_AT91)
.register_clocks = at91sam9rl_register_clocks,
+#endif
.init = at91sam9rl_initialize,
AT91_SOC_END
diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c
index a698bdab2cce..428fc412aaf1 100644
--- a/arch/arm/mach-at91/at91sam9rl_devices.c
+++ b/arch/arm/mach-at91/at91sam9rl_devices.c
@@ -21,6 +21,7 @@
#include <mach/at91sam9rl_matrix.h>
#include <mach/at91_matrix.h>
#include <mach/at91sam9_smc.h>
+#include <mach/hardware.h>
#include <linux/platform_data/dma-atmel.h>
#include "board.h"
@@ -956,6 +957,7 @@ static struct resource dbgu_resources[] = {
static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0, /* DBGU not capable of receive DMA */
+ .rts_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -994,6 +996,7 @@ static struct resource uart0_resources[] = {
static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -1045,6 +1048,7 @@ static struct resource uart1_resources[] = {
static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1088,6 +1092,7 @@ static struct resource uart2_resources[] = {
static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -1131,6 +1136,7 @@ static struct resource uart3_resources[] = {
static struct atmel_uart_data uart3_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
+ .rts_gpio = -EINVAL,
};
static u64 uart3_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c
index 3e8ec26e39dc..9ad781d5ee7c 100644
--- a/arch/arm/mach-at91/at91sam9x5.c
+++ b/arch/arm/mach-at91/at91sam9x5.c
@@ -253,6 +253,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("ehci_clk", "700000.ehci", &uhphs_clk),
CLKDEV_CON_DEV_ID("hclk", "500000.gadget", &utmi_clk),
CLKDEV_CON_DEV_ID("pclk", "500000.gadget", &udphs_clk),
+ CLKDEV_CON_DEV_ID(NULL, "f8034000.pwm", &pwm_clk),
};
/*
diff --git a/arch/arm/mach-at91/at91x40.c b/arch/arm/mach-at91/at91x40.c
index bad94b84a46f..7523f1cdfe1d 100644
--- a/arch/arm/mach-at91/at91x40.c
+++ b/arch/arm/mach-at91/at91x40.c
@@ -19,7 +19,7 @@
#include <asm/mach/arch.h>
#include <mach/at91x40.h>
#include <mach/at91_st.h>
-#include <mach/timex.h>
+#include <mach/hardware.h>
#include "at91_aic.h"
#include "generic.h"
diff --git a/arch/arm/mach-at91/at91x40_time.c b/arch/arm/mach-at91/at91x40_time.c
index c0e637adf65d..07d0bf2ac2da 100644
--- a/arch/arm/mach-at91/at91x40_time.c
+++ b/arch/arm/mach-at91/at91x40_time.c
@@ -25,6 +25,7 @@
#include <linux/time.h>
#include <linux/io.h>
#include <mach/hardware.h>
+#include <mach/at91x40.h>
#include <asm/mach/time.h>
#include "at91_tc.h"
diff --git a/arch/arm/mach-at91/board-dt-sam9.c b/arch/arm/mach-at91/board-dt-sam9.c
index 3dab868b02fa..575b0be66ca8 100644
--- a/arch/arm/mach-at91/board-dt-sam9.c
+++ b/arch/arm/mach-at91/board-dt-sam9.c
@@ -13,6 +13,7 @@
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_irq.h>
+#include <linux/clk-provider.h>
#include <asm/setup.h>
#include <asm/irq.h>
@@ -25,6 +26,14 @@
#include "generic.h"
+static void __init sam9_dt_timer_init(void)
+{
+#if defined(CONFIG_COMMON_CLK)
+ of_clk_init(NULL);
+#endif
+ at91sam926x_pit_init();
+}
+
static const struct of_device_id irq_of_match[] __initconst = {
{ .compatible = "atmel,at91rm9200-aic", .data = at91_aic_of_init },
@@ -43,7 +52,7 @@ static const char *at91_dt_board_compat[] __initdata = {
DT_MACHINE_START(at91sam_dt, "Atmel AT91SAM (Device Tree)")
/* Maintainer: Atmel */
- .init_time = at91sam926x_pit_init,
+ .init_time = sam9_dt_timer_init,
.map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq,
.init_early = at91_dt_initialize,
diff --git a/arch/arm/mach-at91/board-gsia18s.c b/arch/arm/mach-at91/board-gsia18s.c
index c1d61d247790..416bae8435ee 100644
--- a/arch/arm/mach-at91/board-gsia18s.c
+++ b/arch/arm/mach-at91/board-gsia18s.c
@@ -31,6 +31,7 @@
#include <asm/mach/arch.h>
#include <mach/at91sam9_smc.h>
+#include <mach/hardware.h>
#include "at91_aic.h"
#include "board.h"
diff --git a/arch/arm/mach-at91/board-pcontrol-g20.c b/arch/arm/mach-at91/board-pcontrol-g20.c
index 65c0d6b5ecba..5f25fa54eb93 100644
--- a/arch/arm/mach-at91/board-pcontrol-g20.c
+++ b/arch/arm/mach-at91/board-pcontrol-g20.c
@@ -30,6 +30,7 @@
#include <asm/mach/arch.h>
#include <mach/at91sam9_smc.h>
+#include <mach/hardware.h>
#include "at91_aic.h"
#include "board.h"
diff --git a/arch/arm/mach-at91/board-stamp9g20.c b/arch/arm/mach-at91/board-stamp9g20.c
index 869cbecf00b7..e4a5ac17cdbc 100644
--- a/arch/arm/mach-at91/board-stamp9g20.c
+++ b/arch/arm/mach-at91/board-stamp9g20.c
@@ -26,6 +26,7 @@
#include <asm/mach/arch.h>
#include <mach/at91sam9_smc.h>
+#include <mach/hardware.h>
#include "at91_aic.h"
#include "board.h"
diff --git a/arch/arm/mach-at91/include/mach/at91x40.h b/arch/arm/mach-at91/include/mach/at91x40.h
index 90680217064e..38dca2bb027f 100644
--- a/arch/arm/mach-at91/include/mach/at91x40.h
+++ b/arch/arm/mach-at91/include/mach/at91x40.h
@@ -55,4 +55,6 @@
#define AT91_PS_CR (AT91_PS + 0) /* PS Control register */
#define AT91_PS_CR_CPU (1 << 0) /* CPU clock disable bit */
+#define AT91X40_MASTER_CLOCK 40000000
+
#endif /* AT91X40_H */
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 590b52dea9f7..8bda1cefdf96 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -27,6 +27,7 @@
#include <asm/mach/irq.h>
#include <mach/cpu.h>
+#include <mach/hardware.h>
#include "at91_aic.h"
#include "generic.h"
diff --git a/arch/arm/mach-at91/sam9_smc.c b/arch/arm/mach-at91/sam9_smc.c
index b26156bf15db..826315af6d11 100644
--- a/arch/arm/mach-at91/sam9_smc.c
+++ b/arch/arm/mach-at91/sam9_smc.c
@@ -36,6 +36,7 @@ void sam9_smc_write_mode(int id, int cs,
{
sam9_smc_cs_write_mode(AT91_SMC_CS(id, cs), config);
}
+EXPORT_SYMBOL_GPL(sam9_smc_write_mode);
static void sam9_smc_cs_configure(void __iomem *base,
struct sam9_smc_config *config)
@@ -69,6 +70,7 @@ void sam9_smc_configure(int id, int cs,
{
sam9_smc_cs_configure(AT91_SMC_CS(id, cs), config);
}
+EXPORT_SYMBOL_GPL(sam9_smc_configure);
static void sam9_smc_cs_read_mode(void __iomem *base,
struct sam9_smc_config *config)
@@ -84,6 +86,7 @@ void sam9_smc_read_mode(int id, int cs,
{
sam9_smc_cs_read_mode(AT91_SMC_CS(id, cs), config);
}
+EXPORT_SYMBOL_GPL(sam9_smc_read_mode);
static void sam9_smc_cs_read(void __iomem *base,
struct sam9_smc_config *config)
diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c
index f7ca97b7291e..f7a07a58ebb6 100644
--- a/arch/arm/mach-at91/setup.c
+++ b/arch/arm/mach-at91/setup.c
@@ -351,7 +351,7 @@ void __init at91_ioremap_matrix(u32 base_addr)
panic("Impossible to ioremap at91_matrix_base\n");
}
-#if defined(CONFIG_OF)
+#if defined(CONFIG_OF) && !defined(CONFIG_ARCH_AT91X40)
static struct of_device_id rstc_ids[] = {
{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9_alt_restart },
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index b1aa6a9b3bd1..49c914cd9c7a 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -16,12 +16,7 @@ config ARCH_BCM_MOBILE
select ARM_ERRATA_754322
select ARM_ERRATA_764369 if SMP
select ARM_GIC
- select CPU_V7
- select CLKSRC_OF
- select GENERIC_CLOCKEVENTS
- select GENERIC_TIME
select GPIO_BCM_KONA
- select SPARSE_IRQ
select TICK_ONESHOT
select CACHE_L2X0
select HAVE_ARM_ARCH_TIMER
@@ -32,6 +27,48 @@ config ARCH_BCM_MOBILE
BCM11130, BCM11140, BCM11351, BCM28145 and
BCM28155 variants.
+config ARCH_BCM2835
+ bool "Broadcom BCM2835 family" if ARCH_MULTI_V6
+ select ARCH_REQUIRE_GPIOLIB
+ select ARM_AMBA
+ select ARM_ERRATA_411920
+ select ARM_TIMER_SP804
+ select CLKDEV_LOOKUP
+ select CLKSRC_OF
+ select CPU_V6
+ select GENERIC_CLOCKEVENTS
+ select PINCTRL
+ select PINCTRL_BCM2835
+ help
+ This enables support for the Broadcom BCM2835 SoC. This SoC is
+ used in the Raspberry Pi and Roku 2 devices.
+
+config ARCH_BCM_5301X
+ bool "Broadcom BCM470X / BCM5301X ARM SoC" if ARCH_MULTI_V7
+ depends on MMU
+ select ARM_GIC
+ select CACHE_L2X0
+ select HAVE_ARM_SCU if SMP
+ select HAVE_ARM_TWD if SMP
+ select HAVE_SMP
+ select COMMON_CLK
+ select GENERIC_CLOCKEVENTS
+ select ARM_GLOBAL_TIMER
+ select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
+ select MIGHT_HAVE_PCI
+ help
+ Support for Broadcom BCM470X and BCM5301X SoCs with ARM CPU cores.
+
+ This is a network SoC line mostly used in home routers and
+ wifi access points, it's internal name is Northstar.
+ This inclused the following SoC: BCM53010, BCM53011, BCM53012,
+ BCM53014, BCM53015, BCM53016, BCM53017, BCM53018, BCM4707,
+ BCM4708 and BCM4709.
+
+ Do not confuse this with the BCM4760 which is a totally
+ different SoC or with the older BCM47XX and BCM53XX based
+ network SoC using a MIPS CPU, they are supported by arch/mips/bcm47xx
+
endmenu
endif
diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile
index c2ccd5a0f772..a326b28c4406 100644
--- a/arch/arm/mach-bcm/Makefile
+++ b/arch/arm/mach-bcm/Makefile
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2012-2013 Broadcom Corporation
+# Copyright (C) 2012-2014 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
@@ -10,6 +10,10 @@
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-obj-$(CONFIG_ARCH_BCM_MOBILE) := board_bcm281xx.o bcm_kona_smc.o bcm_kona_smc_asm.o kona.o
+obj-$(CONFIG_ARCH_BCM_MOBILE) := board_bcm281xx.o board_bcm21664.o \
+ bcm_kona_smc.o bcm_kona_smc_asm.o kona.o
+obj-$(CONFIG_ARCH_BCM2835) += board_bcm2835.o
+
plus_sec := $(call as-instr,.arch_extension sec,+sec)
AFLAGS_bcm_kona_smc_asm.o :=-Wa,-march=armv7-a$(plus_sec)
+obj-$(CONFIG_ARCH_BCM_5301X) += bcm_5301x.o
diff --git a/arch/arm/mach-bcm/bcm_5301x.c b/arch/arm/mach-bcm/bcm_5301x.c
new file mode 100644
index 000000000000..edff69761e04
--- /dev/null
+++ b/arch/arm/mach-bcm/bcm_5301x.c
@@ -0,0 +1,61 @@
+/*
+ * Broadcom BCM470X / BCM5301X ARM platform code.
+ *
+ * Copyright 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/of_platform.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include <asm/mach/arch.h>
+#include <asm/siginfo.h>
+#include <asm/signal.h>
+
+
+static bool first_fault = true;
+
+static int bcm5301x_abort_handler(unsigned long addr, unsigned int fsr,
+ struct pt_regs *regs)
+{
+ if (fsr == 0x1c06 && first_fault) {
+ first_fault = false;
+
+ /*
+ * These faults with code 0x1c06 happens for no good reason,
+ * possibly left over from the CFE boot loader.
+ */
+ pr_warn("External imprecise Data abort at addr=%#lx, fsr=%#x ignored.\n",
+ addr, fsr);
+
+ /* Returning non-zero causes fault display and panic */
+ return 0;
+ }
+
+ /* Others should cause a fault */
+ return 1;
+}
+
+static void __init bcm5301x_init_early(void)
+{
+ /* Install our hook */
+ hook_fault_code(16 + 6, bcm5301x_abort_handler, SIGBUS, BUS_OBJERR,
+ "imprecise external abort");
+}
+
+static void __init bcm5301x_dt_init(void)
+{
+ l2x0_of_init(0, ~0UL);
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char __initconst *bcm5301x_dt_compat[] = {
+ "brcm,bcm4708",
+ NULL,
+};
+
+DT_MACHINE_START(BCM5301X, "BCM5301X")
+ .init_early = bcm5301x_init_early,
+ .init_machine = bcm5301x_dt_init,
+ .dt_compat = bcm5301x_dt_compat,
+MACHINE_END
diff --git a/arch/arm/mach-bcm/board_bcm21664.c b/arch/arm/mach-bcm/board_bcm21664.c
new file mode 100644
index 000000000000..acc1573fd005
--- /dev/null
+++ b/arch/arm/mach-bcm/board_bcm21664.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clocksource.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+#include <asm/mach/arch.h>
+
+#include "bcm_kona_smc.h"
+#include "kona.h"
+
+#define RSTMGR_DT_STRING "brcm,bcm21664-resetmgr"
+
+#define RSTMGR_REG_WR_ACCESS_OFFSET 0
+#define RSTMGR_REG_CHIP_SOFT_RST_OFFSET 4
+
+#define RSTMGR_WR_PASSWORD 0xa5a5
+#define RSTMGR_WR_PASSWORD_SHIFT 8
+#define RSTMGR_WR_ACCESS_ENABLE 1
+
+static void bcm21664_restart(enum reboot_mode mode, const char *cmd)
+{
+ void __iomem *base;
+ struct device_node *resetmgr;
+
+ resetmgr = of_find_compatible_node(NULL, NULL, RSTMGR_DT_STRING);
+ if (!resetmgr) {
+ pr_emerg("Couldn't find " RSTMGR_DT_STRING "\n");
+ return;
+ }
+ base = of_iomap(resetmgr, 0);
+ if (!base) {
+ pr_emerg("Couldn't map " RSTMGR_DT_STRING "\n");
+ return;
+ }
+
+ /*
+ * A soft reset is triggered by writing a 0 to bit 0 of the soft reset
+ * register. To write to that register we must first write the password
+ * and the enable bit in the write access enable register.
+ */
+ writel((RSTMGR_WR_PASSWORD << RSTMGR_WR_PASSWORD_SHIFT) |
+ RSTMGR_WR_ACCESS_ENABLE,
+ base + RSTMGR_REG_WR_ACCESS_OFFSET);
+ writel(0, base + RSTMGR_REG_CHIP_SOFT_RST_OFFSET);
+
+ /* Wait for reset */
+ while (1);
+}
+
+static void __init bcm21664_init(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table, NULL,
+ &platform_bus);
+ kona_l2_cache_init();
+}
+
+static const char * const bcm21664_dt_compat[] = {
+ "brcm,bcm21664",
+ NULL,
+};
+
+DT_MACHINE_START(BCM21664_DT, "BCM21664 Broadcom Application Processor")
+ .init_machine = bcm21664_init,
+ .restart = bcm21664_restart,
+ .dt_compat = bcm21664_dt_compat,
+MACHINE_END
diff --git a/arch/arm/mach-bcm/board_bcm281xx.c b/arch/arm/mach-bcm/board_bcm281xx.c
index cb3dc364405c..6be54c10f8cb 100644
--- a/arch/arm/mach-bcm/board_bcm281xx.c
+++ b/arch/arm/mach-bcm/board_bcm281xx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Broadcom Corporation
+ * Copyright (C) 2012-2014 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
@@ -11,64 +11,65 @@
* GNU General Public License for more details.
*/
-#include <linux/of_platform.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
#include <linux/clocksource.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
#include <asm/mach/arch.h>
-#include <asm/mach/time.h>
-#include <asm/hardware/cache-l2x0.h>
-#include "bcm_kona_smc.h"
#include "kona.h"
-static int __init kona_l2_cache_init(void)
+#define SECWDOG_OFFSET 0x00000000
+#define SECWDOG_RESERVED_MASK 0xe2000000
+#define SECWDOG_WD_LOAD_FLAG_MASK 0x10000000
+#define SECWDOG_EN_MASK 0x08000000
+#define SECWDOG_SRSTEN_MASK 0x04000000
+#define SECWDOG_CLKS_SHIFT 20
+#define SECWDOG_COUNT_SHIFT 0
+
+static void bcm281xx_restart(enum reboot_mode mode, const char *cmd)
{
- if (!IS_ENABLED(CONFIG_CACHE_L2X0))
- return 0;
+ uint32_t val;
+ void __iomem *base;
+ struct device_node *np_wdog;
- if (bcm_kona_smc_init() < 0) {
- pr_info("Kona secure API not available. Skipping L2 init\n");
- return 0;
+ np_wdog = of_find_compatible_node(NULL, NULL, "brcm,kona-wdt");
+ if (!np_wdog) {
+ pr_emerg("Couldn't find brcm,kona-wdt\n");
+ return;
+ }
+ base = of_iomap(np_wdog, 0);
+ if (!base) {
+ pr_emerg("Couldn't map brcm,kona-wdt\n");
+ return;
}
- bcm_kona_smc(SSAPI_ENABLE_L2_CACHE, 0, 0, 0, 0);
-
- /*
- * The aux_val and aux_mask have no effect since L2 cache is already
- * enabled. Pass 0s for aux_val and 1s for aux_mask for default value.
- */
- return l2x0_of_init(0, ~0);
-}
-
-static void bcm_board_setup_restart(void)
-{
- struct device_node *np;
+ /* Enable watchdog with short timeout (244us). */
+ val = readl(base + SECWDOG_OFFSET);
+ val &= SECWDOG_RESERVED_MASK | SECWDOG_WD_LOAD_FLAG_MASK;
+ val |= SECWDOG_EN_MASK | SECWDOG_SRSTEN_MASK |
+ (0x15 << SECWDOG_CLKS_SHIFT) |
+ (0x8 << SECWDOG_COUNT_SHIFT);
+ writel(val, base + SECWDOG_OFFSET);
- np = of_find_compatible_node(NULL, NULL, "brcm,bcm11351");
- if (np) {
- if (of_device_is_available(np))
- bcm_kona_setup_restart();
- of_node_put(np);
- }
- /* Restart setup for other boards goes here */
+ /* Wait for reset */
+ while (1);
}
-static void __init board_init(void)
+static void __init bcm281xx_init(void)
{
of_platform_populate(NULL, of_default_bus_match_table, NULL,
&platform_bus);
-
- bcm_board_setup_restart();
kona_l2_cache_init();
}
-static const char * const bcm11351_dt_compat[] = { "brcm,bcm11351", NULL, };
+static const char * const bcm281xx_dt_compat[] = {
+ "brcm,bcm11351", /* Have to use the first number upstreamed */
+ NULL,
+};
-DT_MACHINE_START(BCM11351_DT, "BCM281xx Broadcom Application Processor")
- .init_machine = board_init,
- .restart = bcm_kona_restart,
- .dt_compat = bcm11351_dt_compat,
+DT_MACHINE_START(BCM281XX_DT, "BCM281xx Broadcom Application Processor")
+ .init_machine = bcm281xx_init,
+ .restart = bcm281xx_restart,
+ .dt_compat = bcm281xx_dt_compat,
MACHINE_END
diff --git a/arch/arm/mach-bcm2835/bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c
index 70f2f3925f0e..70f2f3925f0e 100644
--- a/arch/arm/mach-bcm2835/bcm2835.c
+++ b/arch/arm/mach-bcm/board_bcm2835.c
diff --git a/arch/arm/mach-bcm/kona.c b/arch/arm/mach-bcm/kona.c
index 6939d9017f63..768bc2837bf5 100644
--- a/arch/arm/mach-bcm/kona.c
+++ b/arch/arm/mach-bcm/kona.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Broadcom Corporation
+ * Copyright (C) 2012-2014 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
@@ -11,55 +11,33 @@
* GNU General Public License for more details.
*/
-#include <linux/of_address.h>
-#include <asm/io.h>
+#include <linux/of_platform.h>
+#include <asm/hardware/cache-l2x0.h>
+#include "bcm_kona_smc.h"
#include "kona.h"
-static void __iomem *watchdog_base;
-
-void bcm_kona_setup_restart(void)
+void __init kona_l2_cache_init(void)
{
- struct device_node *np_wdog;
+ int ret;
- /*
- * The assumption is that whoever calls bcm_kona_setup_restart()
- * also needs a Kona Watchdog Timer entry in Device Tree, i.e. we
- * report an error if the DT entry is missing.
- */
- np_wdog = of_find_compatible_node(NULL, NULL, "brcm,kona-wdt");
- if (!np_wdog) {
- pr_err("brcm,kona-wdt not found in DT, reboot disabled\n");
+ if (!IS_ENABLED(CONFIG_CACHE_L2X0))
return;
- }
- watchdog_base = of_iomap(np_wdog, 0);
- WARN(!watchdog_base, "failed to map watchdog base");
- of_node_put(np_wdog);
-}
-
-#define SECWDOG_OFFSET 0x00000000
-#define SECWDOG_RESERVED_MASK 0xE2000000
-#define SECWDOG_WD_LOAD_FLAG_MASK 0x10000000
-#define SECWDOG_EN_MASK 0x08000000
-#define SECWDOG_SRSTEN_MASK 0x04000000
-#define SECWDOG_CLKS_SHIFT 20
-#define SECWDOG_LOCK_SHIFT 0
-void bcm_kona_restart(enum reboot_mode mode, const char *cmd)
-{
- uint32_t val;
-
- if (!watchdog_base)
- panic("Watchdog not mapped. Reboot failed.\n");
+ ret = bcm_kona_smc_init();
+ if (ret) {
+ pr_info("Secure API not available (%d). Skipping L2 init.\n",
+ ret);
+ return;
+ }
- /* Enable watchdog2 with very short timeout. */
- val = readl(watchdog_base + SECWDOG_OFFSET);
- val &= SECWDOG_RESERVED_MASK | SECWDOG_WD_LOAD_FLAG_MASK;
- val |= SECWDOG_EN_MASK | SECWDOG_SRSTEN_MASK |
- (0x8 << SECWDOG_CLKS_SHIFT) |
- (0x8 << SECWDOG_LOCK_SHIFT);
- writel(val, watchdog_base + SECWDOG_OFFSET);
+ bcm_kona_smc(SSAPI_ENABLE_L2_CACHE, 0, 0, 0, 0);
- while (1)
- ;
+ /*
+ * The aux_val and aux_mask have no effect since L2 cache is already
+ * enabled. Pass 0s for aux_val and 1s for aux_mask for default value.
+ */
+ ret = l2x0_of_init(0, ~0);
+ if (ret)
+ pr_err("Couldn't enable L2 cache: %d\n", ret);
}
diff --git a/arch/arm/mach-bcm/kona.h b/arch/arm/mach-bcm/kona.h
index 291eca3e06ff..3a7a017c29cd 100644
--- a/arch/arm/mach-bcm/kona.h
+++ b/arch/arm/mach-bcm/kona.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Broadcom Corporation
+ * Copyright (C) 2012-2014 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
@@ -11,7 +11,4 @@
* GNU General Public License for more details.
*/
-#include <linux/reboot.h>
-
-void bcm_kona_setup_restart(void);
-void bcm_kona_restart(enum reboot_mode mode, const char *cmd);
+void __init kona_l2_cache_init(void);
diff --git a/arch/arm/mach-bcm2835/Kconfig b/arch/arm/mach-bcm2835/Kconfig
deleted file mode 100644
index d1f9612f8c15..000000000000
--- a/arch/arm/mach-bcm2835/Kconfig
+++ /dev/null
@@ -1,15 +0,0 @@
-config ARCH_BCM2835
- bool "Broadcom BCM2835 family" if ARCH_MULTI_V6
- select ARCH_REQUIRE_GPIOLIB
- select ARM_AMBA
- select ARM_ERRATA_411920
- select ARM_TIMER_SP804
- select CLKDEV_LOOKUP
- select CLKSRC_OF
- select CPU_V6
- select GENERIC_CLOCKEVENTS
- select PINCTRL
- select PINCTRL_BCM2835
- help
- This enables support for the Broadcom BCM2835 SoC. This SoC is
- used in the Raspberry Pi and Roku 2 devices.
diff --git a/arch/arm/mach-bcm2835/Makefile b/arch/arm/mach-bcm2835/Makefile
deleted file mode 100644
index 4c3892fe02c3..000000000000
--- a/arch/arm/mach-bcm2835/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-obj-y += bcm2835.o
diff --git a/arch/arm/mach-berlin/Kconfig b/arch/arm/mach-berlin/Kconfig
index 7a02d222c378..b0cb0722acd2 100644
--- a/arch/arm/mach-berlin/Kconfig
+++ b/arch/arm/mach-berlin/Kconfig
@@ -1,9 +1,7 @@
config ARCH_BERLIN
bool "Marvell Berlin SoCs" if ARCH_MULTI_V7
select ARM_GIC
- select GENERIC_CLOCKEVENTS
select GENERIC_IRQ_CHIP
- select COMMON_CLK
select DW_APB_ICTL
select DW_APB_TIMER_OF
@@ -16,12 +14,10 @@ config MACH_BERLIN_BG2
select CACHE_L2X0
select CPU_PJ4B
select HAVE_ARM_TWD if SMP
- select HAVE_SMP
config MACH_BERLIN_BG2CD
bool "Marvell Armada 1500-mini (BG2CD)"
select CACHE_L2X0
- select CPU_V7
select HAVE_ARM_TWD if SMP
endmenu
diff --git a/arch/arm/mach-clps711x/Kconfig b/arch/arm/mach-clps711x/Kconfig
index bea6295c8c59..f711498c180c 100644
--- a/arch/arm/mach-clps711x/Kconfig
+++ b/arch/arm/mach-clps711x/Kconfig
@@ -33,20 +33,6 @@ config ARCH_P720T
Say Y here if you intend to run this kernel on the ARM Prospector
720T.
-config EP72XX_ROM_BOOT
- bool "EP721x/EP731x ROM boot"
- help
- If you say Y here, your CLPS711x-based kernel will use the bootstrap
- mode memory map instead of the normal memory map.
-
- Processors derived from the Cirrus CLPS711X core support two boot
- modes. Normal mode boots from the external memory device at CS0.
- Bootstrap mode rearranges parts of the memory map, placing an
- internal 128 byte bootstrap ROM at CS0. This option performs the
- address map changes required to support booting in this mode.
-
- You almost surely want to say N here.
-
endmenu
endif
diff --git a/arch/arm/mach-clps711x/board-autcpu12.c b/arch/arm/mach-clps711x/board-autcpu12.c
index f8d71a89644a..d62ca16d5394 100644
--- a/arch/arm/mach-clps711x/board-autcpu12.c
+++ b/arch/arm/mach-clps711x/board-autcpu12.c
@@ -73,7 +73,7 @@
#define AUTCPU12_SMC_NCE (AUTCPU12_MMGPIO_BASE + 0) /* Bit 0 */
#define AUTCPU12_SMC_RDY CLPS711X_GPIO(1, 2)
#define AUTCPU12_SMC_ALE CLPS711X_GPIO(1, 3)
-#define AUTCPU12_SMC_CLE CLPS711X_GPIO(1, 3)
+#define AUTCPU12_SMC_CLE CLPS711X_GPIO(1, 4)
/* LCD contrast digital potentiometer */
#define AUTCPU12_DPOT_CS CLPS711X_GPIO(4, 0)
@@ -265,14 +265,12 @@ static void __init autcpu12_init_late(void)
MACHINE_START(AUTCPU12, "autronix autcpu12")
/* Maintainer: Thomas Gleixner */
.atag_offset = 0x20000,
- .nr_irqs = CLPS711X_NR_IRQS,
.map_io = clps711x_map_io,
.init_early = clps711x_init_early,
.init_irq = clps711x_init_irq,
.init_time = clps711x_timer_init,
.init_machine = autcpu12_init,
.init_late = autcpu12_init_late,
- .handle_irq = clps711x_handle_irq,
.restart = clps711x_restart,
MACHINE_END
diff --git a/arch/arm/mach-clps711x/board-cdb89712.c b/arch/arm/mach-clps711x/board-cdb89712.c
index a9e38c6bcfb4..e261a47f2aff 100644
--- a/arch/arm/mach-clps711x/board-cdb89712.c
+++ b/arch/arm/mach-clps711x/board-cdb89712.c
@@ -139,12 +139,10 @@ static void __init cdb89712_init(void)
MACHINE_START(CDB89712, "Cirrus-CDB89712")
/* Maintainer: Ray Lehtiniemi */
.atag_offset = 0x100,
- .nr_irqs = CLPS711X_NR_IRQS,
.map_io = clps711x_map_io,
.init_early = clps711x_init_early,
.init_irq = clps711x_init_irq,
.init_time = clps711x_timer_init,
.init_machine = cdb89712_init,
- .handle_irq = clps711x_handle_irq,
.restart = clps711x_restart,
MACHINE_END
diff --git a/arch/arm/mach-clps711x/board-clep7312.c b/arch/arm/mach-clps711x/board-clep7312.c
index b4764246d0f8..221b9de32dd6 100644
--- a/arch/arm/mach-clps711x/board-clep7312.c
+++ b/arch/arm/mach-clps711x/board-clep7312.c
@@ -36,12 +36,10 @@ fixup_clep7312(struct tag *tags, char **cmdline, struct meminfo *mi)
MACHINE_START(CLEP7212, "Cirrus Logic 7212/7312")
/* Maintainer: Nobody */
.atag_offset = 0x0100,
- .nr_irqs = CLPS711X_NR_IRQS,
.fixup = fixup_clep7312,
.map_io = clps711x_map_io,
.init_early = clps711x_init_early,
.init_irq = clps711x_init_irq,
.init_time = clps711x_timer_init,
- .handle_irq = clps711x_handle_irq,
.restart = clps711x_restart,
MACHINE_END
diff --git a/arch/arm/mach-clps711x/board-edb7211.c b/arch/arm/mach-clps711x/board-edb7211.c
index fe6184ead896..077609841f14 100644
--- a/arch/arm/mach-clps711x/board-edb7211.c
+++ b/arch/arm/mach-clps711x/board-edb7211.c
@@ -177,7 +177,6 @@ static void __init edb7211_init_late(void)
MACHINE_START(EDB7211, "CL-EDB7211 (EP7211 eval board)")
/* Maintainer: Jon McClintock */
.atag_offset = VIDEORAM_SIZE + 0x100,
- .nr_irqs = CLPS711X_NR_IRQS,
.fixup = fixup_edb7211,
.reserve = edb7211_reserve,
.map_io = clps711x_map_io,
@@ -186,6 +185,5 @@ MACHINE_START(EDB7211, "CL-EDB7211 (EP7211 eval board)")
.init_time = clps711x_timer_init,
.init_machine = edb7211_init,
.init_late = edb7211_init_late,
- .handle_irq = clps711x_handle_irq,
.restart = clps711x_restart,
MACHINE_END
diff --git a/arch/arm/mach-clps711x/board-p720t.c b/arch/arm/mach-clps711x/board-p720t.c
index dd81b06f68fe..67b733744ed7 100644
--- a/arch/arm/mach-clps711x/board-p720t.c
+++ b/arch/arm/mach-clps711x/board-p720t.c
@@ -363,7 +363,6 @@ static void __init p720t_init_late(void)
MACHINE_START(P720T, "ARM-Prospector720T")
/* Maintainer: ARM Ltd/Deep Blue Solutions Ltd */
.atag_offset = 0x100,
- .nr_irqs = CLPS711X_NR_IRQS,
.fixup = fixup_p720t,
.map_io = clps711x_map_io,
.init_early = clps711x_init_early,
@@ -371,6 +370,5 @@ MACHINE_START(P720T, "ARM-Prospector720T")
.init_time = clps711x_timer_init,
.init_machine = p720t_init,
.init_late = p720t_init_late,
- .handle_irq = clps711x_handle_irq,
.restart = clps711x_restart,
MACHINE_END
diff --git a/arch/arm/mach-clps711x/common.c b/arch/arm/mach-clps711x/common.c
index a1935911e4f1..aee81fa46ccf 100644
--- a/arch/arm/mach-clps711x/common.c
+++ b/arch/arm/mach-clps711x/common.c
@@ -31,14 +31,14 @@
#include <linux/clk-provider.h>
#include <linux/sched_clock.h>
-#include <asm/exception.h>
-#include <asm/mach/irq.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <asm/system_misc.h>
#include <mach/hardware.h>
+#include "common.h"
+
static struct clk *clk_pll, *clk_bus, *clk_uart, *clk_timerl, *clk_timerh,
*clk_tint, *clk_spi;
@@ -59,204 +59,9 @@ void __init clps711x_map_io(void)
iotable_init(clps711x_io_desc, ARRAY_SIZE(clps711x_io_desc));
}
-static void int1_mask(struct irq_data *d)
-{
- u32 intmr1;
-
- intmr1 = clps_readl(INTMR1);
- intmr1 &= ~(1 << d->irq);
- clps_writel(intmr1, INTMR1);
-}
-
-static void int1_eoi(struct irq_data *d)
-{
- switch (d->irq) {
- case IRQ_CSINT: clps_writel(0, COEOI); break;
- case IRQ_TC1OI: clps_writel(0, TC1EOI); break;
- case IRQ_TC2OI: clps_writel(0, TC2EOI); break;
- case IRQ_RTCMI: clps_writel(0, RTCEOI); break;
- case IRQ_TINT: clps_writel(0, TEOI); break;
- case IRQ_UMSINT: clps_writel(0, UMSEOI); break;
- }
-}
-
-static void int1_unmask(struct irq_data *d)
-{
- u32 intmr1;
-
- intmr1 = clps_readl(INTMR1);
- intmr1 |= 1 << d->irq;
- clps_writel(intmr1, INTMR1);
-}
-
-static struct irq_chip int1_chip = {
- .name = "Interrupt Vector 1",
- .irq_eoi = int1_eoi,
- .irq_mask = int1_mask,
- .irq_unmask = int1_unmask,
-};
-
-static void int2_mask(struct irq_data *d)
-{
- u32 intmr2;
-
- intmr2 = clps_readl(INTMR2);
- intmr2 &= ~(1 << (d->irq - 16));
- clps_writel(intmr2, INTMR2);
-}
-
-static void int2_eoi(struct irq_data *d)
-{
- switch (d->irq) {
- case IRQ_KBDINT: clps_writel(0, KBDEOI); break;
- }
-}
-
-static void int2_unmask(struct irq_data *d)
-{
- u32 intmr2;
-
- intmr2 = clps_readl(INTMR2);
- intmr2 |= 1 << (d->irq - 16);
- clps_writel(intmr2, INTMR2);
-}
-
-static struct irq_chip int2_chip = {
- .name = "Interrupt Vector 2",
- .irq_eoi = int2_eoi,
- .irq_mask = int2_mask,
- .irq_unmask = int2_unmask,
-};
-
-static void int3_mask(struct irq_data *d)
-{
- u32 intmr3;
-
- intmr3 = clps_readl(INTMR3);
- intmr3 &= ~(1 << (d->irq - 32));
- clps_writel(intmr3, INTMR3);
-}
-
-static void int3_unmask(struct irq_data *d)
-{
- u32 intmr3;
-
- intmr3 = clps_readl(INTMR3);
- intmr3 |= 1 << (d->irq - 32);
- clps_writel(intmr3, INTMR3);
-}
-
-static struct irq_chip int3_chip = {
- .name = "Interrupt Vector 3",
- .irq_mask = int3_mask,
- .irq_unmask = int3_unmask,
-};
-
-static struct {
- int nr;
- struct irq_chip *chip;
- irq_flow_handler_t handle;
-} clps711x_irqdescs[] __initdata = {
- { IRQ_CSINT, &int1_chip, handle_fasteoi_irq, },
- { IRQ_EINT1, &int1_chip, handle_level_irq, },
- { IRQ_EINT2, &int1_chip, handle_level_irq, },
- { IRQ_EINT3, &int1_chip, handle_level_irq, },
- { IRQ_TC1OI, &int1_chip, handle_fasteoi_irq, },
- { IRQ_TC2OI, &int1_chip, handle_fasteoi_irq, },
- { IRQ_RTCMI, &int1_chip, handle_fasteoi_irq, },
- { IRQ_TINT, &int1_chip, handle_fasteoi_irq, },
- { IRQ_UTXINT1, &int1_chip, handle_level_irq, },
- { IRQ_URXINT1, &int1_chip, handle_level_irq, },
- { IRQ_UMSINT, &int1_chip, handle_fasteoi_irq, },
- { IRQ_SSEOTI, &int1_chip, handle_level_irq, },
- { IRQ_KBDINT, &int2_chip, handle_fasteoi_irq, },
- { IRQ_SS2RX, &int2_chip, handle_level_irq, },
- { IRQ_SS2TX, &int2_chip, handle_level_irq, },
- { IRQ_UTXINT2, &int2_chip, handle_level_irq, },
- { IRQ_URXINT2, &int2_chip, handle_level_irq, },
-};
-
void __init clps711x_init_irq(void)
{
- unsigned int i;
-
- /* Disable interrupts */
- clps_writel(0, INTMR1);
- clps_writel(0, INTMR2);
- clps_writel(0, INTMR3);
-
- /* Clear down any pending interrupts */
- clps_writel(0, BLEOI);
- clps_writel(0, MCEOI);
- clps_writel(0, COEOI);
- clps_writel(0, TC1EOI);
- clps_writel(0, TC2EOI);
- clps_writel(0, RTCEOI);
- clps_writel(0, TEOI);
- clps_writel(0, UMSEOI);
- clps_writel(0, KBDEOI);
- clps_writel(0, SRXEOF);
- clps_writel(0xffffffff, DAISR);
-
- for (i = 0; i < ARRAY_SIZE(clps711x_irqdescs); i++) {
- irq_set_chip_and_handler(clps711x_irqdescs[i].nr,
- clps711x_irqdescs[i].chip,
- clps711x_irqdescs[i].handle);
- set_irq_flags(clps711x_irqdescs[i].nr,
- IRQF_VALID | IRQF_PROBE);
- }
-
- if (IS_ENABLED(CONFIG_FIQ)) {
- init_FIQ(0);
- irq_set_chip_and_handler(IRQ_DAIINT, &int3_chip,
- handle_bad_irq);
- set_irq_flags(IRQ_DAIINT,
- IRQF_VALID | IRQF_PROBE | IRQF_NOAUTOEN);
- }
-}
-
-static inline u32 fls16(u32 x)
-{
- u32 r = 15;
-
- if (!(x & 0xff00)) {
- x <<= 8;
- r -= 8;
- }
- if (!(x & 0xf000)) {
- x <<= 4;
- r -= 4;
- }
- if (!(x & 0xc000)) {
- x <<= 2;
- r -= 2;
- }
- if (!(x & 0x8000))
- r--;
-
- return r;
-}
-
-asmlinkage void __exception_irq_entry clps711x_handle_irq(struct pt_regs *regs)
-{
- do {
- u32 irqstat;
- void __iomem *base = CLPS711X_VIRT_BASE;
-
- irqstat = readw_relaxed(base + INTSR1) &
- readw_relaxed(base + INTMR1);
- if (irqstat)
- handle_IRQ(fls16(irqstat), regs);
-
- irqstat = readw_relaxed(base + INTSR2) &
- readw_relaxed(base + INTMR2);
- if (irqstat) {
- handle_IRQ(fls16(irqstat) + 16, regs);
- continue;
- }
-
- break;
- } while (1);
+ clps711x_intc_init(CLPS711X_PHYS_BASE, SZ_16K);
}
static u64 notrace clps711x_sched_clock_read(void)
diff --git a/arch/arm/mach-clps711x/common.h b/arch/arm/mach-clps711x/common.h
index 9a6767bfdc47..7489139d5d63 100644
--- a/arch/arm/mach-clps711x/common.h
+++ b/arch/arm/mach-clps711x/common.h
@@ -6,13 +6,14 @@
#include <linux/reboot.h>
-#define CLPS711X_NR_IRQS (33)
#define CLPS711X_NR_GPIO (4 * 8 + 3)
#define CLPS711X_GPIO(prt, bit) ((prt) * 8 + (bit))
extern void clps711x_map_io(void);
extern void clps711x_init_irq(void);
extern void clps711x_timer_init(void);
-extern void clps711x_handle_irq(struct pt_regs *regs);
extern void clps711x_restart(enum reboot_mode mode, const char *cmd);
extern void clps711x_init_early(void);
+
+/* drivers/irqchip/irq-clps711x.c */
+void clps711x_intc_init(phys_addr_t, resource_size_t);
diff --git a/arch/arm/mach-clps711x/include/mach/clps711x.h b/arch/arm/mach-clps711x/include/mach/clps711x.h
index 0286f4bf9945..eb052a11aa9d 100644
--- a/arch/arm/mach-clps711x/include/mach/clps711x.h
+++ b/arch/arm/mach-clps711x/include/mach/clps711x.h
@@ -40,8 +40,6 @@
#define MEMCFG1 (0x0180)
#define MEMCFG2 (0x01c0)
#define DRFPR (0x0200)
-#define INTSR1 (0x0240)
-#define INTMR1 (0x0280)
#define LCDCON (0x02c0)
#define TC1D (0x0300)
#define TC2D (0x0340)
@@ -55,28 +53,16 @@
#define PALLSW (0x0540)
#define PALMSW (0x0580)
#define STFCLR (0x05c0)
-#define BLEOI (0x0600)
-#define MCEOI (0x0640)
-#define TEOI (0x0680)
-#define TC1EOI (0x06c0)
-#define TC2EOI (0x0700)
-#define RTCEOI (0x0740)
-#define UMSEOI (0x0780)
-#define COEOI (0x07c0)
#define HALT (0x0800)
#define STDBY (0x0840)
#define FBADDR (0x1000)
#define SYSCON2 (0x1100)
#define SYSFLG2 (0x1140)
-#define INTSR2 (0x1240)
-#define INTMR2 (0x1280)
#define UARTDR2 (0x1480)
#define UBRLCR2 (0x14c0)
#define SS2DR (0x1500)
-#define SRXEOF (0x1600)
#define SS2POP (0x16c0)
-#define KBDEOI (0x1700)
#define DAIR (0x2000)
#define DAIDR0 (0x2040)
@@ -84,8 +70,6 @@
#define DAIDR2 (0x20c0)
#define DAISR (0x2100)
#define SYSCON3 (0x2200)
-#define INTSR3 (0x2240)
-#define INTMR3 (0x2280)
#define LEDFLSH (0x22c0)
#define SDCONF (0x2300)
#define SDRFPR (0x2340)
diff --git a/arch/arm/mach-clps711x/include/mach/hardware.h b/arch/arm/mach-clps711x/include/mach/hardware.h
index c5a8ea6839ef..5d6afda1c0e8 100644
--- a/arch/arm/mach-clps711x/include/mach/hardware.h
+++ b/arch/arm/mach-clps711x/include/mach/hardware.h
@@ -38,13 +38,6 @@
#define clps_writel(val,off) writel(val, CLPS711X_VIRT_BASE + (off))
#endif
-/*
- * The physical addresses that the external chip select signals map to is
- * dependent on the setting of the nMEDCHG signal on EP7211 and EP7212
- * processors. CONFIG_EP72XX_BOOT_ROM is only available if these
- * processors are in use.
- */
-#ifndef CONFIG_EP72XX_ROM_BOOT
#define CS0_PHYS_BASE (0x00000000)
#define CS1_PHYS_BASE (0x10000000)
#define CS2_PHYS_BASE (0x20000000)
@@ -53,16 +46,6 @@
#define CS5_PHYS_BASE (0x50000000)
#define CS6_PHYS_BASE (0x60000000)
#define CS7_PHYS_BASE (0x70000000)
-#else
-#define CS0_PHYS_BASE (0x70000000)
-#define CS1_PHYS_BASE (0x60000000)
-#define CS2_PHYS_BASE (0x50000000)
-#define CS3_PHYS_BASE (0x40000000)
-#define CS4_PHYS_BASE (0x30000000)
-#define CS5_PHYS_BASE (0x20000000)
-#define CS6_PHYS_BASE (0x10000000)
-#define CS7_PHYS_BASE (0x00000000)
-#endif
#define CLPS711X_SRAM_BASE CS6_PHYS_BASE
#define CLPS711X_SRAM_SIZE (48 * 1024)
diff --git a/arch/arm/mach-clps711x/include/mach/timex.h b/arch/arm/mach-clps711x/include/mach/timex.h
deleted file mode 100644
index de6fd192d1c3..000000000000
--- a/arch/arm/mach-clps711x/include/mach/timex.h
+++ /dev/null
@@ -1,2 +0,0 @@
-/* Bogus value */
-#define CLOCK_TICK_RATE 512000
diff --git a/arch/arm/mach-cns3xxx/Kconfig b/arch/arm/mach-cns3xxx/Kconfig
index dbf0df8bb0ac..dce8decd5d46 100644
--- a/arch/arm/mach-cns3xxx/Kconfig
+++ b/arch/arm/mach-cns3xxx/Kconfig
@@ -1,9 +1,6 @@
config ARCH_CNS3XXX
bool "Cavium Networks CNS3XXX family" if ARCH_MULTI_V6
select ARM_GIC
- select CPU_V6K
- select GENERIC_CLOCKEVENTS
- select MIGHT_HAVE_CACHE_L2X0
select MIGHT_HAVE_PCI
select PCI_DOMAINS if PCI
help
diff --git a/arch/arm/mach-cns3xxx/cns3420vb.c b/arch/arm/mach-cns3xxx/cns3420vb.c
index ce096d678aa4..d863d8729edc 100644
--- a/arch/arm/mach-cns3xxx/cns3420vb.c
+++ b/arch/arm/mach-cns3xxx/cns3420vb.c
@@ -246,7 +246,6 @@ static void __init cns3420_map_io(void)
MACHINE_START(CNS3420VB, "Cavium Networks CNS3420 Validation Board")
.atag_offset = 0x100,
- .nr_irqs = NR_IRQS_CNS3XXX,
.map_io = cns3420_map_io,
.init_irq = cns3xxx_init_irq,
.init_time = cns3xxx_timer_init,
diff --git a/arch/arm/mach-cns3xxx/core.c b/arch/arm/mach-cns3xxx/core.c
index e38b279f402c..2ae28a69e3e5 100644
--- a/arch/arm/mach-cns3xxx/core.c
+++ b/arch/arm/mach-cns3xxx/core.c
@@ -47,6 +47,38 @@ static struct map_desc cns3xxx_io_desc[] __initdata = {
.pfn = __phys_to_pfn(CNS3XXX_PM_BASE),
.length = SZ_4K,
.type = MT_DEVICE,
+#ifdef CONFIG_PCI
+ }, {
+ .virtual = CNS3XXX_PCIE0_HOST_BASE_VIRT,
+ .pfn = __phys_to_pfn(CNS3XXX_PCIE0_HOST_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = CNS3XXX_PCIE0_CFG0_BASE_VIRT,
+ .pfn = __phys_to_pfn(CNS3XXX_PCIE0_CFG0_BASE),
+ .length = SZ_64K, /* really 4 KiB at offset 32 KiB */
+ .type = MT_DEVICE,
+ }, {
+ .virtual = CNS3XXX_PCIE0_CFG1_BASE_VIRT,
+ .pfn = __phys_to_pfn(CNS3XXX_PCIE0_CFG1_BASE),
+ .length = SZ_16M,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = CNS3XXX_PCIE1_HOST_BASE_VIRT,
+ .pfn = __phys_to_pfn(CNS3XXX_PCIE1_HOST_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = CNS3XXX_PCIE1_CFG0_BASE_VIRT,
+ .pfn = __phys_to_pfn(CNS3XXX_PCIE1_CFG0_BASE),
+ .length = SZ_64K, /* really 4 KiB at offset 32 KiB */
+ .type = MT_DEVICE,
+ }, {
+ .virtual = CNS3XXX_PCIE1_CFG1_BASE_VIRT,
+ .pfn = __phys_to_pfn(CNS3XXX_PCIE1_CFG1_BASE),
+ .length = SZ_16M,
+ .type = MT_DEVICE,
+#endif
},
};
@@ -155,7 +187,7 @@ static irqreturn_t cns3xxx_timer_interrupt(int irq, void *dev_id)
static struct irqaction cns3xxx_timer_irq = {
.name = "timer",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = cns3xxx_timer_interrupt,
};
@@ -368,7 +400,6 @@ static const char *cns3xxx_dt_compat[] __initdata = {
DT_MACHINE_START(CNS3XXX_DT, "Cavium Networks CNS3xxx")
.dt_compat = cns3xxx_dt_compat,
- .nr_irqs = NR_IRQS_CNS3XXX,
.map_io = cns3xxx_map_io,
.init_irq = cns3xxx_init_irq,
.init_time = cns3xxx_timer_init,
diff --git a/arch/arm/mach-cns3xxx/pcie.c b/arch/arm/mach-cns3xxx/pcie.c
index c7b204bff386..413134c54452 100644
--- a/arch/arm/mach-cns3xxx/pcie.c
+++ b/arch/arm/mach-cns3xxx/pcie.c
@@ -23,15 +23,10 @@
#include "cns3xxx.h"
#include "core.h"
-enum cns3xxx_access_type {
- CNS3XXX_HOST_TYPE = 0,
- CNS3XXX_CFG0_TYPE,
- CNS3XXX_CFG1_TYPE,
- CNS3XXX_NUM_ACCESS_TYPES,
-};
-
struct cns3xxx_pcie {
- struct map_desc cfg_bases[CNS3XXX_NUM_ACCESS_TYPES];
+ void __iomem *host_regs; /* PCI config registers for host bridge */
+ void __iomem *cfg0_regs; /* PCI Type 0 config registers */
+ void __iomem *cfg1_regs; /* PCI Type 1 config registers */
unsigned int irqs[2];
struct resource res_io;
struct resource res_mem;
@@ -66,7 +61,6 @@ static void __iomem *cns3xxx_pci_cfg_base(struct pci_bus *bus,
int busno = bus->number;
int slot = PCI_SLOT(devfn);
int offset;
- enum cns3xxx_access_type type;
void __iomem *base;
/* If there is no link, just show the CNS PCI bridge. */
@@ -78,17 +72,21 @@ static void __iomem *cns3xxx_pci_cfg_base(struct pci_bus *bus,
* we still want to access it. For this to work, we must place
* the first device on the same bus as the CNS PCI bridge.
*/
- if (busno == 0) {
- if (slot > 1)
- return NULL;
- type = slot;
- } else {
- type = CNS3XXX_CFG1_TYPE;
- }
+ if (busno == 0) { /* directly connected PCIe bus */
+ switch (slot) {
+ case 0: /* host bridge device, function 0 only */
+ base = cnspci->host_regs;
+ break;
+ case 1: /* directly connected device */
+ base = cnspci->cfg0_regs;
+ break;
+ default:
+ return NULL; /* no such device */
+ }
+ } else /* remote PCI bus */
+ base = cnspci->cfg1_regs;
- base = (void __iomem *)cnspci->cfg_bases[type].virtual;
offset = ((busno & 0xf) << 20) | (devfn << 12) | (where & 0xffc);
-
return base + offset;
}
@@ -180,36 +178,19 @@ static int cns3xxx_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
static struct cns3xxx_pcie cns3xxx_pcie[] = {
[0] = {
- .cfg_bases = {
- [CNS3XXX_HOST_TYPE] = {
- .virtual = CNS3XXX_PCIE0_HOST_BASE_VIRT,
- .pfn = __phys_to_pfn(CNS3XXX_PCIE0_HOST_BASE),
- .length = SZ_16M,
- .type = MT_DEVICE,
- },
- [CNS3XXX_CFG0_TYPE] = {
- .virtual = CNS3XXX_PCIE0_CFG0_BASE_VIRT,
- .pfn = __phys_to_pfn(CNS3XXX_PCIE0_CFG0_BASE),
- .length = SZ_16M,
- .type = MT_DEVICE,
- },
- [CNS3XXX_CFG1_TYPE] = {
- .virtual = CNS3XXX_PCIE0_CFG1_BASE_VIRT,
- .pfn = __phys_to_pfn(CNS3XXX_PCIE0_CFG1_BASE),
- .length = SZ_16M,
- .type = MT_DEVICE,
- },
- },
+ .host_regs = (void __iomem *)CNS3XXX_PCIE0_HOST_BASE_VIRT,
+ .cfg0_regs = (void __iomem *)CNS3XXX_PCIE0_CFG0_BASE_VIRT,
+ .cfg1_regs = (void __iomem *)CNS3XXX_PCIE0_CFG1_BASE_VIRT,
.res_io = {
.name = "PCIe0 I/O space",
.start = CNS3XXX_PCIE0_IO_BASE,
- .end = CNS3XXX_PCIE0_IO_BASE + SZ_16M - 1,
+ .end = CNS3XXX_PCIE0_CFG0_BASE - 1, /* 16 MiB */
.flags = IORESOURCE_IO,
},
.res_mem = {
.name = "PCIe0 non-prefetchable",
.start = CNS3XXX_PCIE0_MEM_BASE,
- .end = CNS3XXX_PCIE0_MEM_BASE + SZ_16M - 1,
+ .end = CNS3XXX_PCIE0_HOST_BASE - 1, /* 176 MiB */
.flags = IORESOURCE_MEM,
},
.irqs = { IRQ_CNS3XXX_PCIE0_RC, IRQ_CNS3XXX_PCIE0_DEVICE, },
@@ -222,36 +203,19 @@ static struct cns3xxx_pcie cns3xxx_pcie[] = {
},
},
[1] = {
- .cfg_bases = {
- [CNS3XXX_HOST_TYPE] = {
- .virtual = CNS3XXX_PCIE1_HOST_BASE_VIRT,
- .pfn = __phys_to_pfn(CNS3XXX_PCIE1_HOST_BASE),
- .length = SZ_16M,
- .type = MT_DEVICE,
- },
- [CNS3XXX_CFG0_TYPE] = {
- .virtual = CNS3XXX_PCIE1_CFG0_BASE_VIRT,
- .pfn = __phys_to_pfn(CNS3XXX_PCIE1_CFG0_BASE),
- .length = SZ_16M,
- .type = MT_DEVICE,
- },
- [CNS3XXX_CFG1_TYPE] = {
- .virtual = CNS3XXX_PCIE1_CFG1_BASE_VIRT,
- .pfn = __phys_to_pfn(CNS3XXX_PCIE1_CFG1_BASE),
- .length = SZ_16M,
- .type = MT_DEVICE,
- },
- },
+ .host_regs = (void __iomem *)CNS3XXX_PCIE1_HOST_BASE_VIRT,
+ .cfg0_regs = (void __iomem *)CNS3XXX_PCIE1_CFG0_BASE_VIRT,
+ .cfg1_regs = (void __iomem *)CNS3XXX_PCIE1_CFG1_BASE_VIRT,
.res_io = {
.name = "PCIe1 I/O space",
.start = CNS3XXX_PCIE1_IO_BASE,
- .end = CNS3XXX_PCIE1_IO_BASE + SZ_16M - 1,
+ .end = CNS3XXX_PCIE1_CFG0_BASE - 1, /* 16 MiB */
.flags = IORESOURCE_IO,
},
.res_mem = {
.name = "PCIe1 non-prefetchable",
.start = CNS3XXX_PCIE1_MEM_BASE,
- .end = CNS3XXX_PCIE1_MEM_BASE + SZ_16M - 1,
+ .end = CNS3XXX_PCIE1_HOST_BASE - 1, /* 176 MiB */
.flags = IORESOURCE_MEM,
},
.irqs = { IRQ_CNS3XXX_PCIE1_RC, IRQ_CNS3XXX_PCIE1_DEVICE, },
@@ -307,18 +271,15 @@ static void __init cns3xxx_pcie_hw_init(struct cns3xxx_pcie *cnspci)
.ops = &cns3xxx_pcie_ops,
.sysdata = &sd,
};
- u32 io_base = cnspci->res_io.start >> 16;
- u32 mem_base = cnspci->res_mem.start >> 16;
- u32 host_base = cnspci->cfg_bases[CNS3XXX_HOST_TYPE].pfn;
- u32 cfg0_base = cnspci->cfg_bases[CNS3XXX_CFG0_TYPE].pfn;
+ u16 mem_base = cnspci->res_mem.start >> 16;
+ u16 mem_limit = cnspci->res_mem.end >> 16;
+ u16 io_base = cnspci->res_io.start >> 16;
+ u16 io_limit = cnspci->res_io.end >> 16;
u32 devfn = 0;
u8 tmp8;
u16 pos;
u16 dc;
- host_base = (__pfn_to_phys(host_base) - 1) >> 16;
- cfg0_base = (__pfn_to_phys(cfg0_base) - 1) >> 16;
-
pci_bus_write_config_byte(&bus, devfn, PCI_PRIMARY_BUS, 0);
pci_bus_write_config_byte(&bus, devfn, PCI_SECONDARY_BUS, 1);
pci_bus_write_config_byte(&bus, devfn, PCI_SUBORDINATE_BUS, 1);
@@ -328,9 +289,9 @@ static void __init cns3xxx_pcie_hw_init(struct cns3xxx_pcie *cnspci)
pci_bus_read_config_byte(&bus, devfn, PCI_SUBORDINATE_BUS, &tmp8);
pci_bus_write_config_word(&bus, devfn, PCI_MEMORY_BASE, mem_base);
- pci_bus_write_config_word(&bus, devfn, PCI_MEMORY_LIMIT, host_base);
+ pci_bus_write_config_word(&bus, devfn, PCI_MEMORY_LIMIT, mem_limit);
pci_bus_write_config_word(&bus, devfn, PCI_IO_BASE_UPPER16, io_base);
- pci_bus_write_config_word(&bus, devfn, PCI_IO_LIMIT_UPPER16, cfg0_base);
+ pci_bus_write_config_word(&bus, devfn, PCI_IO_LIMIT_UPPER16, io_limit);
if (!cnspci->linked)
return;
@@ -368,8 +329,6 @@ static int __init cns3xxx_pcie_init(void)
"imprecise external abort");
for (i = 0; i < ARRAY_SIZE(cns3xxx_pcie); i++) {
- iotable_init(cns3xxx_pcie[i].cfg_bases,
- ARRAY_SIZE(cns3xxx_pcie[i].cfg_bases));
cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_PCIE(i));
cns3xxx_pwr_soft_rst(0x1 << PM_SOFT_RST_REG_OFFST_PCIE(i));
cns3xxx_pcie_check_link(&cns3xxx_pcie[i]);
diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig
index a075b3e0c5c7..db18ef866593 100644
--- a/arch/arm/mach-davinci/Kconfig
+++ b/arch/arm/mach-davinci/Kconfig
@@ -51,11 +51,6 @@ config ARCH_DAVINCI_DM365
select AINTC
select ARCH_DAVINCI_DMx
-config ARCH_DAVINCI_TNETV107X
- bool "TNETV107X based system"
- select CPU_V6
- select CP_INTC
-
comment "DaVinci Board Type"
config MACH_DA8XX_DT
@@ -214,18 +209,6 @@ config DA850_WL12XX
Say Y if you want to use a wl1271 expansion card connected to the
AM18x EVM.
-config GPIO_PCA953X
- default MACH_DAVINCI_DA850_EVM
-
-config KEYBOARD_GPIO_POLLED
- default MACH_DAVINCI_DA850_EVM
-
-config MACH_TNETV107X
- bool "TI TNETV107X Reference Platform"
- default ARCH_DAVINCI_TNETV107X
- depends on ARCH_DAVINCI_TNETV107X
- help
- Say Y here to select the TI TNETV107X Evaluation Module.
config MACH_MITYOMAPL138
bool "Critical Link MityDSP-L138/MityARM-1808 SoM"
diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile
index 63997a1128e6..2204239ed243 100644
--- a/arch/arm/mach-davinci/Makefile
+++ b/arch/arm/mach-davinci/Makefile
@@ -16,7 +16,6 @@ obj-$(CONFIG_ARCH_DAVINCI_DM646x) += dm646x.o devices.o
obj-$(CONFIG_ARCH_DAVINCI_DM365) += dm365.o devices.o
obj-$(CONFIG_ARCH_DAVINCI_DA830) += da830.o devices-da8xx.o
obj-$(CONFIG_ARCH_DAVINCI_DA850) += da850.o devices-da8xx.o
-obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += tnetv107x.o devices-tnetv107x.o
obj-$(CONFIG_AINTC) += irq.o
obj-$(CONFIG_CP_INTC) += cp_intc.o
@@ -32,7 +31,6 @@ obj-$(CONFIG_MACH_DAVINCI_DM6467_EVM) += board-dm646x-evm.o cdce949.o
obj-$(CONFIG_MACH_DAVINCI_DM365_EVM) += board-dm365-evm.o
obj-$(CONFIG_MACH_DAVINCI_DA830_EVM) += board-da830-evm.o
obj-$(CONFIG_MACH_DAVINCI_DA850_EVM) += board-da850-evm.o
-obj-$(CONFIG_MACH_TNETV107X) += board-tnetv107x-evm.o
obj-$(CONFIG_MACH_MITYOMAPL138) += board-mityomapl138.o
obj-$(CONFIG_MACH_OMAPL138_HAWKBOARD) += board-omapl138-hawk.o
diff --git a/arch/arm/mach-davinci/Makefile.boot b/arch/arm/mach-davinci/Makefile.boot
index 04a6c4e67b14..4b81601754a2 100644
--- a/arch/arm/mach-davinci/Makefile.boot
+++ b/arch/arm/mach-davinci/Makefile.boot
@@ -1,13 +1,7 @@
-ifeq ($(CONFIG_ARCH_DAVINCI_DA8XX),y)
-ifeq ($(CONFIG_ARCH_DAVINCI_DMx),y)
-$(error Cannot enable DaVinci and DA8XX platforms concurrently)
-else
- zreladdr-y += 0xc0008000
-params_phys-y := 0xc0000100
-initrd_phys-y := 0xc0800000
-endif
-else
- zreladdr-y += 0x80008000
-params_phys-y := 0x80000100
-initrd_phys-y := 0x80800000
-endif
+zreladdr-$(CONFIG_ARCH_DAVINCI_DA8XX) += 0xc0008000
+params_phys-$(CONFIG_ARCH_DAVINCI_DA8XX) := 0xc0000100
+initrd_phys-$(CONFIG_ARCH_DAVINCI_DA8XX) := 0xc0800000
+
+zreladdr-$(CONFIG_ARCH_DAVINCI_DMx) += 0x80008000
+params_phys-$(CONFIG_ARCH_DAVINCI_DMx) := 0x80000100
+initrd_phys-$(CONFIG_ARCH_DAVINCI_DMx) := 0x80800000
diff --git a/arch/arm/mach-davinci/aemif.c b/arch/arm/mach-davinci/aemif.c
index f091a9010c2f..ff8b7e76b6e9 100644
--- a/arch/arm/mach-davinci/aemif.c
+++ b/arch/arm/mach-davinci/aemif.c
@@ -16,6 +16,7 @@
#include <linux/time.h>
#include <linux/platform_data/mtd-davinci-aemif.h>
+#include <linux/platform_data/mtd-davinci.h>
/* Timing value configuration */
@@ -43,6 +44,17 @@
WSTROBE(WSTROBE_MAX) | \
WSETUP(WSETUP_MAX))
+static inline unsigned int davinci_aemif_readl(void __iomem *base, int offset)
+{
+ return readl_relaxed(base + offset);
+}
+
+static inline void davinci_aemif_writel(void __iomem *base,
+ int offset, unsigned long value)
+{
+ writel_relaxed(value, base + offset);
+}
+
/*
* aemif_calc_rate - calculate timing data.
* @wanted: The cycle time needed in nanoseconds.
@@ -76,6 +88,7 @@ static int aemif_calc_rate(int wanted, unsigned long clk, int max)
* @t: timing values to be progammed
* @base: The virtual base address of the AEMIF interface
* @cs: chip-select to program the timing values for
+ * @clkrate: the AEMIF clkrate
*
* This function programs the given timing values (in real clock) into the
* AEMIF registers taking the AEMIF clock into account.
@@ -86,24 +99,17 @@ static int aemif_calc_rate(int wanted, unsigned long clk, int max)
*
* Returns 0 on success, else negative errno.
*/
-int davinci_aemif_setup_timing(struct davinci_aemif_timing *t,
- void __iomem *base, unsigned cs)
+static int davinci_aemif_setup_timing(struct davinci_aemif_timing *t,
+ void __iomem *base, unsigned cs,
+ unsigned long clkrate)
{
unsigned set, val;
int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup;
unsigned offset = A1CR_OFFSET + cs * 4;
- struct clk *aemif_clk;
- unsigned long clkrate;
if (!t)
return 0; /* Nothing to do */
- aemif_clk = clk_get(NULL, "aemif");
- if (IS_ERR(aemif_clk))
- return PTR_ERR(aemif_clk);
-
- clkrate = clk_get_rate(aemif_clk);
-
clkrate /= 1000; /* turn clock into kHz for ease of use */
ta = aemif_calc_rate(t->ta, clkrate, TA_MAX);
@@ -130,4 +136,83 @@ int davinci_aemif_setup_timing(struct davinci_aemif_timing *t,
return 0;
}
-EXPORT_SYMBOL(davinci_aemif_setup_timing);
+
+/**
+ * davinci_aemif_setup - setup AEMIF interface by davinci_nand_pdata
+ * @pdev - link to platform device to setup settings for
+ *
+ * This function does not use any locking while programming the AEMIF
+ * because it is expected that there is only one user of a given
+ * chip-select.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+int davinci_aemif_setup(struct platform_device *pdev)
+{
+ struct davinci_nand_pdata *pdata = dev_get_platdata(&pdev->dev);
+ uint32_t val;
+ unsigned long clkrate;
+ struct resource *res;
+ void __iomem *base;
+ struct clk *clk;
+ int ret = 0;
+
+ clk = clk_get(&pdev->dev, "aemif");
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
+ ret);
+ goto err_put;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ dev_err(&pdev->dev, "ioremap failed for resource %pR\n", res);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /*
+ * Setup Async configuration register in case we did not boot
+ * from NAND and so bootloader did not bother to set it up.
+ */
+ val = davinci_aemif_readl(base, A1CR_OFFSET + pdev->id * 4);
+ /*
+ * Extended Wait is not valid and Select Strobe mode is not
+ * used
+ */
+ val &= ~(ACR_ASIZE_MASK | ACR_EW_MASK | ACR_SS_MASK);
+ if (pdata->options & NAND_BUSWIDTH_16)
+ val |= 0x1;
+
+ davinci_aemif_writel(base, A1CR_OFFSET + pdev->id * 4, val);
+
+ clkrate = clk_get_rate(clk);
+
+ if (pdata->timing)
+ ret = davinci_aemif_setup_timing(pdata->timing, base, pdev->id,
+ clkrate);
+
+ if (ret < 0)
+ dev_dbg(&pdev->dev, "NAND timing values setup fail\n");
+
+ iounmap(base);
+err:
+ clk_disable_unprepare(clk);
+err_put:
+ clk_put(clk);
+ return ret;
+}
diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c
index d1f45af7a530..5623131c4f0b 100644
--- a/arch/arm/mach-davinci/board-da830-evm.c
+++ b/arch/arm/mach-davinci/board-da830-evm.c
@@ -419,6 +419,9 @@ static inline void da830_evm_init_nand(int mux_mode)
if (ret)
pr_warning("da830_evm_init: NAND device not registered.\n");
+ if (davinci_aemif_setup(&da830_evm_nand_device))
+ pr_warn("%s: Cannot configure AEMIF.\n", __func__);
+
gpio_direction_output(mux_mode, 1);
}
#else
diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
index e0af0eccde8f..234c5bb091f5 100644
--- a/arch/arm/mach-davinci/board-da850-evm.c
+++ b/arch/arm/mach-davinci/board-da850-evm.c
@@ -358,6 +358,9 @@ static inline void da850_evm_setup_nor_nand(void)
platform_add_devices(da850_evm_devices,
ARRAY_SIZE(da850_evm_devices));
+
+ if (davinci_aemif_setup(&da850_evm_nandflash_device))
+ pr_warn("%s: Cannot configure AEMIF.\n", __func__);
}
}
diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c
index 987605b78556..e583e58b5e1e 100644
--- a/arch/arm/mach-davinci/board-dm644x-evm.c
+++ b/arch/arm/mach-davinci/board-dm644x-evm.c
@@ -778,6 +778,11 @@ static __init void davinci_evm_init(void)
/* only one device will be jumpered and detected */
if (HAS_NAND) {
platform_device_register(&davinci_evm_nandflash_device);
+
+ if (davinci_aemif_setup(&davinci_evm_nandflash_device))
+ pr_warn("%s: Cannot configure AEMIF.\n",
+ __func__);
+
evm_leds[7].default_trigger = "nand-disk";
if (HAS_NOR)
pr_warning("WARNING: both NAND and NOR flash "
@@ -799,11 +804,12 @@ static __init void davinci_evm_init(void)
/* irlml6401 switches over 1A, in under 8 msec */
davinci_setup_usb(1000, 8);
- soc_info->emac_pdata->phy_id = DM644X_EVM_PHY_ID;
- /* Register the fixup for PHY on DaVinci */
- phy_register_fixup_for_uid(LXT971_PHY_ID, LXT971_PHY_MASK,
- davinci_phy_fixup);
-
+ if (IS_BUILTIN(CONFIG_PHYLIB)) {
+ soc_info->emac_pdata->phy_id = DM644X_EVM_PHY_ID;
+ /* Register the fixup for PHY on DaVinci */
+ phy_register_fixup_for_uid(LXT971_PHY_ID, LXT971_PHY_MASK,
+ davinci_phy_fixup);
+ }
}
MACHINE_START(DAVINCI_EVM, "DaVinci DM644x EVM")
diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c
index 13d0801fd6b1..ae129bc49273 100644
--- a/arch/arm/mach-davinci/board-dm646x-evm.c
+++ b/arch/arm/mach-davinci/board-dm646x-evm.c
@@ -805,6 +805,9 @@ static __init void evm_init(void)
platform_device_register(&davinci_nand_device);
+ if (davinci_aemif_setup(&davinci_nand_device))
+ pr_warn("%s: Cannot configure AEMIF.\n", __func__);
+
dm646x_init_edma(dm646x_edma_rsv);
if (HAS_ATA)
diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c
index 7aa105b1fd0f..96fc00a167f5 100644
--- a/arch/arm/mach-davinci/board-mityomapl138.c
+++ b/arch/arm/mach-davinci/board-mityomapl138.c
@@ -27,6 +27,7 @@
#include <mach/cp_intc.h>
#include <mach/da8xx.h>
#include <linux/platform_data/mtd-davinci.h>
+#include <linux/platform_data/mtd-davinci-aemif.h>
#include <mach/mux.h>
#include <linux/platform_data/spi-davinci.h>
@@ -432,6 +433,9 @@ static void __init mityomapl138_setup_nand(void)
{
platform_add_devices(mityomapl138_devices,
ARRAY_SIZE(mityomapl138_devices));
+
+ if (davinci_aemif_setup(&mityomapl138_nandflash_device))
+ pr_warn("%s: Cannot configure AEMIF.\n", __func__);
}
static const short mityomap_mii_pins[] = {
diff --git a/arch/arm/mach-davinci/board-tnetv107x-evm.c b/arch/arm/mach-davinci/board-tnetv107x-evm.c
deleted file mode 100644
index 78ea395d2aca..000000000000
--- a/arch/arm/mach-davinci/board-tnetv107x-evm.c
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Texas Instruments TNETV107X EVM Board Support
- *
- * Copyright (C) 2010 Texas Instruments
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/ratelimit.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/input.h>
-#include <linux/input/matrix_keypad.h>
-#include <linux/spi/spi.h>
-#include <linux/platform_data/edma.h>
-
-#include <asm/mach/arch.h>
-#include <asm/mach-types.h>
-
-#include <mach/irqs.h>
-#include <mach/mux.h>
-#include <mach/cp_intc.h>
-#include <mach/tnetv107x.h>
-
-#define EVM_MMC_WP_GPIO 21
-#define EVM_MMC_CD_GPIO 24
-#define EVM_SPI_CS_GPIO 54
-
-static int initialize_gpio(int gpio, char *desc)
-{
- int ret;
-
- ret = gpio_request(gpio, desc);
- if (ret < 0) {
- pr_err_ratelimited("cannot open %s gpio\n", desc);
- return -ENOSYS;
- }
- gpio_direction_input(gpio);
- return gpio;
-}
-
-static int mmc_get_cd(int index)
-{
- static int gpio;
-
- if (!gpio)
- gpio = initialize_gpio(EVM_MMC_CD_GPIO, "mmc card detect");
-
- if (gpio < 0)
- return gpio;
-
- return gpio_get_value(gpio) ? 0 : 1;
-}
-
-static int mmc_get_ro(int index)
-{
- static int gpio;
-
- if (!gpio)
- gpio = initialize_gpio(EVM_MMC_WP_GPIO, "mmc write protect");
-
- if (gpio < 0)
- return gpio;
-
- return gpio_get_value(gpio) ? 1 : 0;
-}
-
-static struct davinci_mmc_config mmc_config = {
- .get_cd = mmc_get_cd,
- .get_ro = mmc_get_ro,
- .wires = 4,
- .max_freq = 50000000,
- .caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
-};
-
-static const short sdio1_pins[] __initconst = {
- TNETV107X_SDIO1_CLK_1, TNETV107X_SDIO1_CMD_1,
- TNETV107X_SDIO1_DATA0_1, TNETV107X_SDIO1_DATA1_1,
- TNETV107X_SDIO1_DATA2_1, TNETV107X_SDIO1_DATA3_1,
- TNETV107X_GPIO21, TNETV107X_GPIO24,
- -1
-};
-
-static const short uart1_pins[] __initconst = {
- TNETV107X_UART1_RD, TNETV107X_UART1_TD,
- -1
-};
-
-static const short ssp_pins[] __initconst = {
- TNETV107X_SSP0_0, TNETV107X_SSP0_1, TNETV107X_SSP0_2,
- TNETV107X_SSP1_0, TNETV107X_SSP1_1, TNETV107X_SSP1_2,
- TNETV107X_SSP1_3, -1
-};
-
-static struct mtd_partition nand_partitions[] = {
- /* bootloader (U-Boot, etc) in first 12 sectors */
- {
- .name = "bootloader",
- .offset = 0,
- .size = (12*SZ_128K),
- .mask_flags = MTD_WRITEABLE, /* force read-only */
- },
- /* bootloader params in the next sector */
- {
- .name = "params",
- .offset = MTDPART_OFS_NXTBLK,
- .size = SZ_128K,
- .mask_flags = MTD_WRITEABLE, /* force read-only */
- },
- /* kernel */
- {
- .name = "kernel",
- .offset = MTDPART_OFS_NXTBLK,
- .size = SZ_4M,
- .mask_flags = 0,
- },
- /* file system */
- {
- .name = "filesystem",
- .offset = MTDPART_OFS_NXTBLK,
- .size = MTDPART_SIZ_FULL,
- .mask_flags = 0,
- }
-};
-
-static struct davinci_nand_pdata nand_config = {
- .mask_cle = 0x4000,
- .mask_ale = 0x2000,
- .parts = nand_partitions,
- .nr_parts = ARRAY_SIZE(nand_partitions),
- .ecc_mode = NAND_ECC_HW,
- .bbt_options = NAND_BBT_USE_FLASH,
- .ecc_bits = 1,
-};
-
-static struct davinci_uart_config serial_config __initconst = {
- .enabled_uarts = BIT(1),
-};
-
-static const uint32_t keymap[] = {
- KEY(0, 0, KEY_NUMERIC_1),
- KEY(0, 1, KEY_NUMERIC_2),
- KEY(0, 2, KEY_NUMERIC_3),
- KEY(0, 3, KEY_FN_F1),
- KEY(0, 4, KEY_MENU),
-
- KEY(1, 0, KEY_NUMERIC_4),
- KEY(1, 1, KEY_NUMERIC_5),
- KEY(1, 2, KEY_NUMERIC_6),
- KEY(1, 3, KEY_UP),
- KEY(1, 4, KEY_FN_F2),
-
- KEY(2, 0, KEY_NUMERIC_7),
- KEY(2, 1, KEY_NUMERIC_8),
- KEY(2, 2, KEY_NUMERIC_9),
- KEY(2, 3, KEY_LEFT),
- KEY(2, 4, KEY_ENTER),
-
- KEY(3, 0, KEY_NUMERIC_STAR),
- KEY(3, 1, KEY_NUMERIC_0),
- KEY(3, 2, KEY_NUMERIC_POUND),
- KEY(3, 3, KEY_DOWN),
- KEY(3, 4, KEY_RIGHT),
-
- KEY(4, 0, KEY_FN_F3),
- KEY(4, 1, KEY_FN_F4),
- KEY(4, 2, KEY_MUTE),
- KEY(4, 3, KEY_HOME),
- KEY(4, 4, KEY_BACK),
-
- KEY(5, 0, KEY_VOLUMEDOWN),
- KEY(5, 1, KEY_VOLUMEUP),
- KEY(5, 2, KEY_F1),
- KEY(5, 3, KEY_F2),
- KEY(5, 4, KEY_F3),
-};
-
-static const struct matrix_keymap_data keymap_data = {
- .keymap = keymap,
- .keymap_size = ARRAY_SIZE(keymap),
-};
-
-static struct matrix_keypad_platform_data keypad_config = {
- .keymap_data = &keymap_data,
- .num_row_gpios = 6,
- .num_col_gpios = 5,
- .debounce_ms = 0, /* minimum */
- .active_low = 0, /* pull up realization */
- .no_autorepeat = 0,
-};
-
-static void spi_select_device(int cs)
-{
- static int gpio;
-
- if (!gpio) {
- int ret;
- ret = gpio_request(EVM_SPI_CS_GPIO, "spi chipsel");
- if (ret < 0) {
- pr_err("cannot open spi chipsel gpio\n");
- gpio = -ENOSYS;
- return;
- } else {
- gpio = EVM_SPI_CS_GPIO;
- gpio_direction_output(gpio, 0);
- }
- }
-
- if (gpio < 0)
- return;
-
- return gpio_set_value(gpio, cs ? 1 : 0);
-}
-
-static struct ti_ssp_spi_data spi_master_data = {
- .num_cs = 2,
- .select = spi_select_device,
- .iosel = SSP_PIN_SEL(0, SSP_CLOCK) | SSP_PIN_SEL(1, SSP_DATA) |
- SSP_PIN_SEL(2, SSP_CHIPSEL) | SSP_PIN_SEL(3, SSP_IN) |
- SSP_INPUT_SEL(3),
-};
-
-static struct ti_ssp_data ssp_config = {
- .out_clock = 250 * 1000,
- .dev_data = {
- [1] = {
- .dev_name = "ti-ssp-spi",
- .pdata = &spi_master_data,
- .pdata_size = sizeof(spi_master_data),
- },
- },
-};
-
-static struct tnetv107x_device_info evm_device_info __initconst = {
- .serial_config = &serial_config,
- .mmc_config[1] = &mmc_config, /* controller 1 */
- .nand_config[0] = &nand_config, /* chip select 0 */
- .keypad_config = &keypad_config,
- .ssp_config = &ssp_config,
-};
-
-static struct spi_board_info spi_info[] __initconst = {
-};
-
-static __init void tnetv107x_evm_board_init(void)
-{
- davinci_cfg_reg_list(sdio1_pins);
- davinci_cfg_reg_list(uart1_pins);
- davinci_cfg_reg_list(ssp_pins);
-
- tnetv107x_devices_init(&evm_device_info);
-
- spi_register_board_info(spi_info, ARRAY_SIZE(spi_info));
-}
-
-#ifdef CONFIG_SERIAL_8250_CONSOLE
-static int __init tnetv107x_evm_console_init(void)
-{
- return add_preferred_console("ttyS", 0, "115200");
-}
-console_initcall(tnetv107x_evm_console_init);
-#endif
-
-MACHINE_START(TNETV107X, "TNETV107X EVM")
- .atag_offset = 0x100,
- .map_io = tnetv107x_init,
- .init_irq = cp_intc_init,
- .init_time = davinci_timer_init,
- .init_machine = tnetv107x_evm_board_init,
- .init_late = davinci_init_late,
- .dma_zone_size = SZ_128M,
- .restart = tnetv107x_restart,
-MACHINE_END
diff --git a/arch/arm/mach-davinci/davinci.h b/arch/arm/mach-davinci/davinci.h
index 2eebc4338802..4ffc37accce0 100644
--- a/arch/arm/mach-davinci/davinci.h
+++ b/arch/arm/mach-davinci/davinci.h
@@ -79,6 +79,8 @@ int davinci_gpio_register(struct resource *res, int size, void *pdata);
#define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000
#define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000
+int davinci_init_wdt(void);
+
/* DM355 function declarations */
void dm355_init(void);
void dm355_init_spi0(unsigned chipselect_mask,
diff --git a/arch/arm/mach-davinci/devices-tnetv107x.c b/arch/arm/mach-davinci/devices-tnetv107x.c
deleted file mode 100644
index 01d8686e553c..000000000000
--- a/arch/arm/mach-davinci/devices-tnetv107x.c
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * Texas Instruments TNETV107X SoC devices
- *
- * Copyright (C) 2010 Texas Instruments
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/clk.h>
-#include <linux/slab.h>
-#include <linux/platform_data/edma.h>
-
-#include <mach/common.h>
-#include <mach/irqs.h>
-#include <mach/tnetv107x.h>
-
-#include "clock.h"
-
-/* Base addresses for on-chip devices */
-#define TNETV107X_TPCC_BASE 0x01c00000
-#define TNETV107X_TPTC0_BASE 0x01c10000
-#define TNETV107X_TPTC1_BASE 0x01c10400
-#define TNETV107X_WDOG_BASE 0x08086700
-#define TNETV107X_TSC_BASE 0x08088500
-#define TNETV107X_SDIO0_BASE 0x08088700
-#define TNETV107X_SDIO1_BASE 0x08088800
-#define TNETV107X_KEYPAD_BASE 0x08088a00
-#define TNETV107X_SSP_BASE 0x08088c00
-#define TNETV107X_ASYNC_EMIF_CNTRL_BASE 0x08200000
-#define TNETV107X_ASYNC_EMIF_DATA_CE0_BASE 0x30000000
-#define TNETV107X_ASYNC_EMIF_DATA_CE1_BASE 0x40000000
-#define TNETV107X_ASYNC_EMIF_DATA_CE2_BASE 0x44000000
-#define TNETV107X_ASYNC_EMIF_DATA_CE3_BASE 0x48000000
-
-/* TNETV107X specific EDMA3 information */
-#define EDMA_TNETV107X_NUM_DMACH 64
-#define EDMA_TNETV107X_NUM_TCC 64
-#define EDMA_TNETV107X_NUM_PARAMENTRY 128
-#define EDMA_TNETV107X_NUM_EVQUE 2
-#define EDMA_TNETV107X_NUM_TC 2
-#define EDMA_TNETV107X_CHMAP_EXIST 0
-#define EDMA_TNETV107X_NUM_REGIONS 4
-#define TNETV107X_DMACH2EVENT_MAP0 0x3C0CE000u
-#define TNETV107X_DMACH2EVENT_MAP1 0x000FFFFFu
-
-#define TNETV107X_DMACH_SDIO0_RX 26
-#define TNETV107X_DMACH_SDIO0_TX 27
-#define TNETV107X_DMACH_SDIO1_RX 28
-#define TNETV107X_DMACH_SDIO1_TX 29
-
-static s8 edma_tc_mapping[][2] = {
- /* event queue no TC no */
- { 0, 0 },
- { 1, 1 },
- { -1, -1 }
-};
-
-static s8 edma_priority_mapping[][2] = {
- /* event queue no Prio */
- { 0, 3 },
- { 1, 7 },
- { -1, -1 }
-};
-
-static struct edma_soc_info edma_cc0_info = {
- .n_channel = EDMA_TNETV107X_NUM_DMACH,
- .n_region = EDMA_TNETV107X_NUM_REGIONS,
- .n_slot = EDMA_TNETV107X_NUM_PARAMENTRY,
- .n_tc = EDMA_TNETV107X_NUM_TC,
- .n_cc = 1,
- .queue_tc_mapping = edma_tc_mapping,
- .queue_priority_mapping = edma_priority_mapping,
- .default_queue = EVENTQ_1,
-};
-
-static struct edma_soc_info *tnetv107x_edma_info[EDMA_MAX_CC] = {
- &edma_cc0_info,
-};
-
-static struct resource edma_resources[] = {
- {
- .name = "edma_cc0",
- .start = TNETV107X_TPCC_BASE,
- .end = TNETV107X_TPCC_BASE + SZ_32K - 1,
- .flags = IORESOURCE_MEM,
- },
- {
- .name = "edma_tc0",
- .start = TNETV107X_TPTC0_BASE,
- .end = TNETV107X_TPTC0_BASE + SZ_1K - 1,
- .flags = IORESOURCE_MEM,
- },
- {
- .name = "edma_tc1",
- .start = TNETV107X_TPTC1_BASE,
- .end = TNETV107X_TPTC1_BASE + SZ_1K - 1,
- .flags = IORESOURCE_MEM,
- },
- {
- .name = "edma0",
- .start = IRQ_TNETV107X_TPCC,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "edma0_err",
- .start = IRQ_TNETV107X_TPCC_ERR,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device edma_device = {
- .name = "edma",
- .id = -1,
- .num_resources = ARRAY_SIZE(edma_resources),
- .resource = edma_resources,
- .dev.platform_data = tnetv107x_edma_info,
-};
-
-static struct plat_serial8250_port serial0_platform_data[] = {
- {
- .mapbase = TNETV107X_UART0_BASE,
- .irq = IRQ_TNETV107X_UART0,
- .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
- UPF_FIXED_TYPE | UPF_IOREMAP,
- .type = PORT_AR7,
- .iotype = UPIO_MEM32,
- .regshift = 2,
- },
- {
- .flags = 0,
- }
-};
-static struct plat_serial8250_port serial1_platform_data[] = {
- {
- .mapbase = TNETV107X_UART1_BASE,
- .irq = IRQ_TNETV107X_UART1,
- .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
- UPF_FIXED_TYPE | UPF_IOREMAP,
- .type = PORT_AR7,
- .iotype = UPIO_MEM32,
- .regshift = 2,
- },
- {
- .flags = 0,
- }
-};
-static struct plat_serial8250_port serial2_platform_data[] = {
- {
- .mapbase = TNETV107X_UART2_BASE,
- .irq = IRQ_TNETV107X_UART2,
- .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
- UPF_FIXED_TYPE | UPF_IOREMAP,
- .type = PORT_AR7,
- .iotype = UPIO_MEM32,
- .regshift = 2,
- },
- {
- .flags = 0,
- }
-};
-
-
-struct platform_device tnetv107x_serial_device[] = {
- {
- .name = "serial8250",
- .id = PLAT8250_DEV_PLATFORM,
- .dev.platform_data = serial0_platform_data,
- },
- {
- .name = "serial8250",
- .id = PLAT8250_DEV_PLATFORM1,
- .dev.platform_data = serial1_platform_data,
- },
- {
- .name = "serial8250",
- .id = PLAT8250_DEV_PLATFORM2,
- .dev.platform_data = serial2_platform_data,
- },
- {
- }
-};
-
-static struct resource mmc0_resources[] = {
- { /* Memory mapped registers */
- .start = TNETV107X_SDIO0_BASE,
- .end = TNETV107X_SDIO0_BASE + 0x0ff,
- .flags = IORESOURCE_MEM
- },
- { /* MMC interrupt */
- .start = IRQ_TNETV107X_MMC0,
- .flags = IORESOURCE_IRQ
- },
- { /* SDIO interrupt */
- .start = IRQ_TNETV107X_SDIO0,
- .flags = IORESOURCE_IRQ
- },
- { /* DMA RX */
- .start = EDMA_CTLR_CHAN(0, TNETV107X_DMACH_SDIO0_RX),
- .flags = IORESOURCE_DMA
- },
- { /* DMA TX */
- .start = EDMA_CTLR_CHAN(0, TNETV107X_DMACH_SDIO0_TX),
- .flags = IORESOURCE_DMA
- },
-};
-
-static struct resource mmc1_resources[] = {
- { /* Memory mapped registers */
- .start = TNETV107X_SDIO1_BASE,
- .end = TNETV107X_SDIO1_BASE + 0x0ff,
- .flags = IORESOURCE_MEM
- },
- { /* MMC interrupt */
- .start = IRQ_TNETV107X_MMC1,
- .flags = IORESOURCE_IRQ
- },
- { /* SDIO interrupt */
- .start = IRQ_TNETV107X_SDIO1,
- .flags = IORESOURCE_IRQ
- },
- { /* DMA RX */
- .start = EDMA_CTLR_CHAN(0, TNETV107X_DMACH_SDIO1_RX),
- .flags = IORESOURCE_DMA
- },
- { /* DMA TX */
- .start = EDMA_CTLR_CHAN(0, TNETV107X_DMACH_SDIO1_TX),
- .flags = IORESOURCE_DMA
- },
-};
-
-static u64 mmc0_dma_mask = DMA_BIT_MASK(32);
-static u64 mmc1_dma_mask = DMA_BIT_MASK(32);
-
-static struct platform_device mmc_devices[2] = {
- {
- .name = "dm6441-mmc",
- .id = 0,
- .dev = {
- .dma_mask = &mmc0_dma_mask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
- .num_resources = ARRAY_SIZE(mmc0_resources),
- .resource = mmc0_resources
- },
- {
- .name = "dm6441-mmc",
- .id = 1,
- .dev = {
- .dma_mask = &mmc1_dma_mask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
- .num_resources = ARRAY_SIZE(mmc1_resources),
- .resource = mmc1_resources
- },
-};
-
-static const u32 emif_windows[] = {
- TNETV107X_ASYNC_EMIF_DATA_CE0_BASE, TNETV107X_ASYNC_EMIF_DATA_CE1_BASE,
- TNETV107X_ASYNC_EMIF_DATA_CE2_BASE, TNETV107X_ASYNC_EMIF_DATA_CE3_BASE,
-};
-
-static const u32 emif_window_sizes[] = { SZ_256M, SZ_64M, SZ_64M, SZ_64M };
-
-static struct resource wdt_resources[] = {
- {
- .start = TNETV107X_WDOG_BASE,
- .end = TNETV107X_WDOG_BASE + SZ_4K - 1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-struct platform_device tnetv107x_wdt_device = {
- .name = "tnetv107x_wdt",
- .id = 0,
- .num_resources = ARRAY_SIZE(wdt_resources),
- .resource = wdt_resources,
-};
-
-static int __init nand_init(int chipsel, struct davinci_nand_pdata *data)
-{
- struct resource res[2];
- struct platform_device *pdev;
- u32 range;
- int ret;
-
- /* Figure out the resource range from the ale/cle masks */
- range = max(data->mask_cle, data->mask_ale);
- range = PAGE_ALIGN(range + 4) - 1;
-
- if (range >= emif_window_sizes[chipsel])
- return -EINVAL;
-
- pdev = kzalloc(sizeof(*pdev), GFP_KERNEL);
- if (!pdev)
- return -ENOMEM;
-
- pdev->name = "davinci_nand";
- pdev->id = chipsel;
- pdev->dev.platform_data = data;
-
- memset(res, 0, sizeof(res));
-
- res[0].start = emif_windows[chipsel];
- res[0].end = res[0].start + range;
- res[0].flags = IORESOURCE_MEM;
-
- res[1].start = TNETV107X_ASYNC_EMIF_CNTRL_BASE;
- res[1].end = res[1].start + SZ_4K - 1;
- res[1].flags = IORESOURCE_MEM;
-
- ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
- if (ret < 0) {
- kfree(pdev);
- return ret;
- }
-
- return platform_device_register(pdev);
-}
-
-static struct resource keypad_resources[] = {
- {
- .start = TNETV107X_KEYPAD_BASE,
- .end = TNETV107X_KEYPAD_BASE + 0xff,
- .flags = IORESOURCE_MEM,
- },
- {
- .start = IRQ_TNETV107X_KEYPAD,
- .flags = IORESOURCE_IRQ,
- .name = "press",
- },
- {
- .start = IRQ_TNETV107X_KEYPAD_FREE,
- .flags = IORESOURCE_IRQ,
- .name = "release",
- },
-};
-
-static struct platform_device keypad_device = {
- .name = "tnetv107x-keypad",
- .num_resources = ARRAY_SIZE(keypad_resources),
- .resource = keypad_resources,
-};
-
-static struct resource tsc_resources[] = {
- {
- .start = TNETV107X_TSC_BASE,
- .end = TNETV107X_TSC_BASE + 0xff,
- .flags = IORESOURCE_MEM,
- },
- {
- .start = IRQ_TNETV107X_TSC,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device tsc_device = {
- .name = "tnetv107x-ts",
- .num_resources = ARRAY_SIZE(tsc_resources),
- .resource = tsc_resources,
-};
-
-static struct resource ssp_resources[] = {
- {
- .start = TNETV107X_SSP_BASE,
- .end = TNETV107X_SSP_BASE + 0x1ff,
- .flags = IORESOURCE_MEM,
- },
- {
- .start = IRQ_TNETV107X_SSP,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device ssp_device = {
- .name = "ti-ssp",
- .id = -1,
- .num_resources = ARRAY_SIZE(ssp_resources),
- .resource = ssp_resources,
-};
-
-void __init tnetv107x_devices_init(struct tnetv107x_device_info *info)
-{
- int i, error;
- struct clk *tsc_clk;
-
- /*
- * The reset defaults for tnetv107x tsc clock divider is set too high.
- * This forces the clock down to a range that allows the ADC to
- * complete sample conversion in time.
- */
- tsc_clk = clk_get(NULL, "sys_tsc_clk");
- if (!IS_ERR(tsc_clk)) {
- error = clk_set_rate(tsc_clk, 5000000);
- WARN_ON(error < 0);
- clk_put(tsc_clk);
- }
-
- platform_device_register(&edma_device);
- platform_device_register(&tnetv107x_wdt_device);
- platform_device_register(&tsc_device);
-
- if (info->serial_config)
- davinci_serial_init(tnetv107x_serial_device);
-
- for (i = 0; i < 2; i++)
- if (info->mmc_config[i]) {
- mmc_devices[i].dev.platform_data = info->mmc_config[i];
- platform_device_register(&mmc_devices[i]);
- }
-
- for (i = 0; i < 4; i++)
- if (info->nand_config[i])
- nand_init(i, info->nand_config[i]);
-
- if (info->keypad_config) {
- keypad_device.dev.platform_data = info->keypad_config;
- platform_device_register(&keypad_device);
- }
-
- if (info->ssp_config) {
- ssp_device.dev.platform_data = info->ssp_config;
- platform_device_register(&ssp_device);
- }
-}
diff --git a/arch/arm/mach-davinci/devices.c b/arch/arm/mach-davinci/devices.c
index 5cf9a027dcc6..6257aa452568 100644
--- a/arch/arm/mach-davinci/devices.c
+++ b/arch/arm/mach-davinci/devices.c
@@ -313,9 +313,9 @@ void davinci_restart(enum reboot_mode mode, const char *cmd)
davinci_watchdog_reset(&davinci_wdt_device);
}
-static void davinci_init_wdt(void)
+int davinci_init_wdt(void)
{
- platform_device_register(&davinci_wdt_device);
+ return platform_device_register(&davinci_wdt_device);
}
static struct platform_device davinci_gpio_device = {
@@ -348,16 +348,3 @@ struct davinci_timer_instance davinci_timer_instance[2] = {
},
};
-/*-------------------------------------------------------------------------*/
-
-static int __init davinci_init_devices(void)
-{
- /* please keep these calls, and their implementations above,
- * in alphabetical order so they're easier to sort through.
- */
- davinci_init_wdt();
-
- return 0;
-}
-arch_initcall(davinci_init_devices);
-
diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c
index 4668c0e19767..07381d8cea62 100644
--- a/arch/arm/mach-davinci/dm355.c
+++ b/arch/arm/mach-davinci/dm355.c
@@ -1076,12 +1076,18 @@ int __init dm355_init_video(struct vpfe_config *vpfe_cfg,
static int __init dm355_init_devices(void)
{
+ int ret = 0;
+
if (!cpu_is_davinci_dm355())
return 0;
davinci_cfg_reg(DM355_INT_EDMA_CC);
platform_device_register(&dm355_edma_device);
- return 0;
+ ret = davinci_init_wdt();
+ if (ret)
+ pr_warn("%s: watchdog init failed: %d\n", __func__, ret);
+
+ return ret;
}
postcore_initcall(dm355_init_devices);
diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c
index b44b49e2801a..08a61b938333 100644
--- a/arch/arm/mach-davinci/dm365.c
+++ b/arch/arm/mach-davinci/dm365.c
@@ -1436,6 +1436,8 @@ int __init dm365_init_video(struct vpfe_config *vpfe_cfg,
static int __init dm365_init_devices(void)
{
+ int ret = 0;
+
if (!cpu_is_davinci_dm365())
return 0;
@@ -1445,6 +1447,10 @@ static int __init dm365_init_devices(void)
platform_device_register(&dm365_mdio_device);
platform_device_register(&dm365_emac_device);
- return 0;
+ ret = davinci_init_wdt();
+ if (ret)
+ pr_warn("%s: watchdog init failed: %d\n", __func__, ret);
+
+ return ret;
}
postcore_initcall(dm365_init_devices);
diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c
index 5c3e0be95ef3..5debffba4b24 100644
--- a/arch/arm/mach-davinci/dm644x.c
+++ b/arch/arm/mach-davinci/dm644x.c
@@ -964,6 +964,8 @@ int __init dm644x_init_video(struct vpfe_config *vpfe_cfg,
static int __init dm644x_init_devices(void)
{
+ int ret = 0;
+
if (!cpu_is_davinci_dm644x())
return 0;
@@ -972,6 +974,10 @@ static int __init dm644x_init_devices(void)
platform_device_register(&dm644x_mdio_device);
platform_device_register(&dm644x_emac_device);
- return 0;
+ ret = davinci_init_wdt();
+ if (ret)
+ pr_warn("%s: watchdog init failed: %d\n", __func__, ret);
+
+ return ret;
}
postcore_initcall(dm644x_init_devices);
diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c
index 81768dd47096..332d00d24dc2 100644
--- a/arch/arm/mach-davinci/dm646x.c
+++ b/arch/arm/mach-davinci/dm646x.c
@@ -955,12 +955,18 @@ void __init dm646x_init(void)
static int __init dm646x_init_devices(void)
{
+ int ret = 0;
+
if (!cpu_is_davinci_dm646x())
return 0;
platform_device_register(&dm646x_mdio_device);
platform_device_register(&dm646x_emac_device);
- return 0;
+ ret = davinci_init_wdt();
+ if (ret)
+ pr_warn("%s: watchdog init failed: %d\n", __func__, ret);
+
+ return ret;
}
postcore_initcall(dm646x_init_devices);
diff --git a/arch/arm/mach-davinci/include/mach/cputype.h b/arch/arm/mach-davinci/include/mach/cputype.h
index 957fb87e832e..1fc84e21664d 100644
--- a/arch/arm/mach-davinci/include/mach/cputype.h
+++ b/arch/arm/mach-davinci/include/mach/cputype.h
@@ -33,7 +33,6 @@ struct davinci_id {
#define DAVINCI_CPU_ID_DM365 0x03650000
#define DAVINCI_CPU_ID_DA830 0x08300000
#define DAVINCI_CPU_ID_DA850 0x08500000
-#define DAVINCI_CPU_ID_TNETV107X 0x0b8a0000
#define IS_DAVINCI_CPU(type, id) \
static inline int is_davinci_ ##type(void) \
@@ -47,7 +46,6 @@ IS_DAVINCI_CPU(dm355, DAVINCI_CPU_ID_DM355)
IS_DAVINCI_CPU(dm365, DAVINCI_CPU_ID_DM365)
IS_DAVINCI_CPU(da830, DAVINCI_CPU_ID_DA830)
IS_DAVINCI_CPU(da850, DAVINCI_CPU_ID_DA850)
-IS_DAVINCI_CPU(tnetv107x, DAVINCI_CPU_ID_TNETV107X)
#ifdef CONFIG_ARCH_DAVINCI_DM644x
#define cpu_is_davinci_dm644x() is_davinci_dm644x()
@@ -85,10 +83,4 @@ IS_DAVINCI_CPU(tnetv107x, DAVINCI_CPU_ID_TNETV107X)
#define cpu_is_davinci_da850() 0
#endif
-#ifdef CONFIG_ARCH_DAVINCI_TNETV107X
-#define cpu_is_davinci_tnetv107x() is_davinci_tnetv107x()
-#else
-#define cpu_is_davinci_tnetv107x() 0
-#endif
-
#endif
diff --git a/arch/arm/mach-davinci/include/mach/irqs.h b/arch/arm/mach-davinci/include/mach/irqs.h
index ec76c7775c2e..354af71798dc 100644
--- a/arch/arm/mach-davinci/include/mach/irqs.h
+++ b/arch/arm/mach-davinci/include/mach/irqs.h
@@ -401,103 +401,6 @@
#define DA850_N_CP_INTC_IRQ 101
-
-/* TNETV107X specific interrupts */
-#define IRQ_TNETV107X_TDM1_TXDMA 0
-#define IRQ_TNETV107X_EXT_INT_0 1
-#define IRQ_TNETV107X_EXT_INT_1 2
-#define IRQ_TNETV107X_GPIO_INT12 3
-#define IRQ_TNETV107X_GPIO_INT13 4
-#define IRQ_TNETV107X_TIMER_0_TINT12 5
-#define IRQ_TNETV107X_TIMER_1_TINT12 6
-#define IRQ_TNETV107X_UART0 7
-#define IRQ_TNETV107X_TDM1_RXDMA 8
-#define IRQ_TNETV107X_MCDMA_INT0 9
-#define IRQ_TNETV107X_MCDMA_INT1 10
-#define IRQ_TNETV107X_TPCC 11
-#define IRQ_TNETV107X_TPCC_INT0 12
-#define IRQ_TNETV107X_TPCC_INT1 13
-#define IRQ_TNETV107X_TPCC_INT2 14
-#define IRQ_TNETV107X_TPCC_INT3 15
-#define IRQ_TNETV107X_TPTC0 16
-#define IRQ_TNETV107X_TPTC1 17
-#define IRQ_TNETV107X_TIMER_0_TINT34 18
-#define IRQ_TNETV107X_ETHSS 19
-#define IRQ_TNETV107X_TIMER_1_TINT34 20
-#define IRQ_TNETV107X_DSP2ARM_INT0 21
-#define IRQ_TNETV107X_DSP2ARM_INT1 22
-#define IRQ_TNETV107X_ARM_NPMUIRQ 23
-#define IRQ_TNETV107X_USB1 24
-#define IRQ_TNETV107X_VLYNQ 25
-#define IRQ_TNETV107X_UART0_DMATX 26
-#define IRQ_TNETV107X_UART0_DMARX 27
-#define IRQ_TNETV107X_TDM1_TXMCSP 28
-#define IRQ_TNETV107X_SSP 29
-#define IRQ_TNETV107X_MCDMA_INT2 30
-#define IRQ_TNETV107X_MCDMA_INT3 31
-#define IRQ_TNETV107X_TDM_CODECIF_EOT 32
-#define IRQ_TNETV107X_IMCOP_SQR_ARM 33
-#define IRQ_TNETV107X_USB0 34
-#define IRQ_TNETV107X_USB_CDMA 35
-#define IRQ_TNETV107X_LCD 36
-#define IRQ_TNETV107X_KEYPAD 37
-#define IRQ_TNETV107X_KEYPAD_FREE 38
-#define IRQ_TNETV107X_RNG 39
-#define IRQ_TNETV107X_PKA 40
-#define IRQ_TNETV107X_TDM0_TXDMA 41
-#define IRQ_TNETV107X_TDM0_RXDMA 42
-#define IRQ_TNETV107X_TDM0_TXMCSP 43
-#define IRQ_TNETV107X_TDM0_RXMCSP 44
-#define IRQ_TNETV107X_TDM1_RXMCSP 45
-#define IRQ_TNETV107X_SDIO1 46
-#define IRQ_TNETV107X_SDIO0 47
-#define IRQ_TNETV107X_TSC 48
-#define IRQ_TNETV107X_TS 49
-#define IRQ_TNETV107X_UART1 50
-#define IRQ_TNETV107X_MBX_LITE 51
-#define IRQ_TNETV107X_GPIO_INT00 52
-#define IRQ_TNETV107X_GPIO_INT01 53
-#define IRQ_TNETV107X_GPIO_INT02 54
-#define IRQ_TNETV107X_GPIO_INT03 55
-#define IRQ_TNETV107X_UART2 56
-#define IRQ_TNETV107X_UART2_DMATX 57
-#define IRQ_TNETV107X_UART2_DMARX 58
-#define IRQ_TNETV107X_IMCOP_IMX 59
-#define IRQ_TNETV107X_IMCOP_VLCD 60
-#define IRQ_TNETV107X_AES 61
-#define IRQ_TNETV107X_DES 62
-#define IRQ_TNETV107X_SHAMD5 63
-#define IRQ_TNETV107X_TPCC_ERR 68
-#define IRQ_TNETV107X_TPCC_PROT 69
-#define IRQ_TNETV107X_TPTC0_ERR 70
-#define IRQ_TNETV107X_TPTC1_ERR 71
-#define IRQ_TNETV107X_UART0_ERR 72
-#define IRQ_TNETV107X_UART1_ERR 73
-#define IRQ_TNETV107X_AEMIF_ERR 74
-#define IRQ_TNETV107X_DDR_ERR 75
-#define IRQ_TNETV107X_WDTARM_INT0 76
-#define IRQ_TNETV107X_MCDMA_ERR 77
-#define IRQ_TNETV107X_GPIO_ERR 78
-#define IRQ_TNETV107X_MPU_ADDR 79
-#define IRQ_TNETV107X_MPU_PROT 80
-#define IRQ_TNETV107X_IOPU_ADDR 81
-#define IRQ_TNETV107X_IOPU_PROT 82
-#define IRQ_TNETV107X_KEYPAD_ADDR_ERR 83
-#define IRQ_TNETV107X_WDT0_ADDR_ERR 84
-#define IRQ_TNETV107X_WDT1_ADDR_ERR 85
-#define IRQ_TNETV107X_CLKCTL_ADDR_ERR 86
-#define IRQ_TNETV107X_PLL_UNLOCK 87
-#define IRQ_TNETV107X_WDTDSP_INT0 88
-#define IRQ_TNETV107X_SEC_CTRL_VIOLATION 89
-#define IRQ_TNETV107X_KEY_MNG_VIOLATION 90
-#define IRQ_TNETV107X_PBIST_CPU 91
-#define IRQ_TNETV107X_WDTARM 92
-#define IRQ_TNETV107X_PSC 93
-#define IRQ_TNETV107X_MMC0 94
-#define IRQ_TNETV107X_MMC1 95
-
-#define TNETV107X_N_CP_INTC_IRQ 96
-
/* da850 currently has the most gpio pins (144) */
#define DAVINCI_N_GPIO 144
/* da850 currently has the most irqs so use DA850_N_CP_INTC_IRQ */
diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h
index 9e95b8a1edb6..631655e68ae0 100644
--- a/arch/arm/mach-davinci/include/mach/mux.h
+++ b/arch/arm/mach-davinci/include/mach/mux.h
@@ -972,275 +972,6 @@ enum davinci_da850_index {
DA850_VPIF_CLKO3,
};
-enum davinci_tnetv107x_index {
- TNETV107X_ASR_A00,
- TNETV107X_GPIO32,
- TNETV107X_ASR_A01,
- TNETV107X_GPIO33,
- TNETV107X_ASR_A02,
- TNETV107X_GPIO34,
- TNETV107X_ASR_A03,
- TNETV107X_GPIO35,
- TNETV107X_ASR_A04,
- TNETV107X_GPIO36,
- TNETV107X_ASR_A05,
- TNETV107X_GPIO37,
- TNETV107X_ASR_A06,
- TNETV107X_GPIO38,
- TNETV107X_ASR_A07,
- TNETV107X_GPIO39,
- TNETV107X_ASR_A08,
- TNETV107X_GPIO40,
- TNETV107X_ASR_A09,
- TNETV107X_GPIO41,
- TNETV107X_ASR_A10,
- TNETV107X_GPIO42,
- TNETV107X_ASR_A11,
- TNETV107X_BOOT_STRP_0,
- TNETV107X_ASR_A12,
- TNETV107X_BOOT_STRP_1,
- TNETV107X_ASR_A13,
- TNETV107X_GPIO43,
- TNETV107X_ASR_A14,
- TNETV107X_GPIO44,
- TNETV107X_ASR_A15,
- TNETV107X_GPIO45,
- TNETV107X_ASR_A16,
- TNETV107X_GPIO46,
- TNETV107X_ASR_A17,
- TNETV107X_GPIO47,
- TNETV107X_ASR_A18,
- TNETV107X_GPIO48,
- TNETV107X_SDIO1_DATA3_0,
- TNETV107X_ASR_A19,
- TNETV107X_GPIO49,
- TNETV107X_SDIO1_DATA2_0,
- TNETV107X_ASR_A20,
- TNETV107X_GPIO50,
- TNETV107X_SDIO1_DATA1_0,
- TNETV107X_ASR_A21,
- TNETV107X_GPIO51,
- TNETV107X_SDIO1_DATA0_0,
- TNETV107X_ASR_A22,
- TNETV107X_GPIO52,
- TNETV107X_SDIO1_CMD_0,
- TNETV107X_ASR_A23,
- TNETV107X_GPIO53,
- TNETV107X_SDIO1_CLK_0,
- TNETV107X_ASR_BA_1,
- TNETV107X_GPIO54,
- TNETV107X_SYS_PLL_CLK,
- TNETV107X_ASR_CS0,
- TNETV107X_ASR_CS1,
- TNETV107X_ASR_CS2,
- TNETV107X_TDM_PLL_CLK,
- TNETV107X_ASR_CS3,
- TNETV107X_ETH_PHY_CLK,
- TNETV107X_ASR_D00,
- TNETV107X_GPIO55,
- TNETV107X_ASR_D01,
- TNETV107X_GPIO56,
- TNETV107X_ASR_D02,
- TNETV107X_GPIO57,
- TNETV107X_ASR_D03,
- TNETV107X_GPIO58,
- TNETV107X_ASR_D04,
- TNETV107X_GPIO59_0,
- TNETV107X_ASR_D05,
- TNETV107X_GPIO60_0,
- TNETV107X_ASR_D06,
- TNETV107X_GPIO61_0,
- TNETV107X_ASR_D07,
- TNETV107X_GPIO62_0,
- TNETV107X_ASR_D08,
- TNETV107X_GPIO63_0,
- TNETV107X_ASR_D09,
- TNETV107X_GPIO64_0,
- TNETV107X_ASR_D10,
- TNETV107X_SDIO1_DATA3_1,
- TNETV107X_ASR_D11,
- TNETV107X_SDIO1_DATA2_1,
- TNETV107X_ASR_D12,
- TNETV107X_SDIO1_DATA1_1,
- TNETV107X_ASR_D13,
- TNETV107X_SDIO1_DATA0_1,
- TNETV107X_ASR_D14,
- TNETV107X_SDIO1_CMD_1,
- TNETV107X_ASR_D15,
- TNETV107X_SDIO1_CLK_1,
- TNETV107X_ASR_OE,
- TNETV107X_BOOT_STRP_2,
- TNETV107X_ASR_RNW,
- TNETV107X_GPIO29_0,
- TNETV107X_ASR_WAIT,
- TNETV107X_GPIO30_0,
- TNETV107X_ASR_WE,
- TNETV107X_BOOT_STRP_3,
- TNETV107X_ASR_WE_DQM0,
- TNETV107X_GPIO31,
- TNETV107X_LCD_PD17_0,
- TNETV107X_ASR_WE_DQM1,
- TNETV107X_ASR_BA0_0,
- TNETV107X_VLYNQ_CLK,
- TNETV107X_GPIO14,
- TNETV107X_LCD_PD19_0,
- TNETV107X_VLYNQ_RXD0,
- TNETV107X_GPIO15,
- TNETV107X_LCD_PD20_0,
- TNETV107X_VLYNQ_RXD1,
- TNETV107X_GPIO16,
- TNETV107X_LCD_PD21_0,
- TNETV107X_VLYNQ_TXD0,
- TNETV107X_GPIO17,
- TNETV107X_LCD_PD22_0,
- TNETV107X_VLYNQ_TXD1,
- TNETV107X_GPIO18,
- TNETV107X_LCD_PD23_0,
- TNETV107X_SDIO0_CLK,
- TNETV107X_GPIO19,
- TNETV107X_SDIO0_CMD,
- TNETV107X_GPIO20,
- TNETV107X_SDIO0_DATA0,
- TNETV107X_GPIO21,
- TNETV107X_SDIO0_DATA1,
- TNETV107X_GPIO22,
- TNETV107X_SDIO0_DATA2,
- TNETV107X_GPIO23,
- TNETV107X_SDIO0_DATA3,
- TNETV107X_GPIO24,
- TNETV107X_EMU0,
- TNETV107X_EMU1,
- TNETV107X_RTCK,
- TNETV107X_TRST_N,
- TNETV107X_TCK,
- TNETV107X_TDI,
- TNETV107X_TDO,
- TNETV107X_TMS,
- TNETV107X_TDM1_CLK,
- TNETV107X_TDM1_RX,
- TNETV107X_TDM1_TX,
- TNETV107X_TDM1_FS,
- TNETV107X_KEYPAD_R0,
- TNETV107X_KEYPAD_R1,
- TNETV107X_KEYPAD_R2,
- TNETV107X_KEYPAD_R3,
- TNETV107X_KEYPAD_R4,
- TNETV107X_KEYPAD_R5,
- TNETV107X_KEYPAD_R6,
- TNETV107X_GPIO12,
- TNETV107X_KEYPAD_R7,
- TNETV107X_GPIO10,
- TNETV107X_KEYPAD_C0,
- TNETV107X_KEYPAD_C1,
- TNETV107X_KEYPAD_C2,
- TNETV107X_KEYPAD_C3,
- TNETV107X_KEYPAD_C4,
- TNETV107X_KEYPAD_C5,
- TNETV107X_KEYPAD_C6,
- TNETV107X_GPIO13,
- TNETV107X_TEST_CLK_IN,
- TNETV107X_KEYPAD_C7,
- TNETV107X_GPIO11,
- TNETV107X_SSP0_0,
- TNETV107X_SCC_DCLK,
- TNETV107X_LCD_PD20_1,
- TNETV107X_SSP0_1,
- TNETV107X_SCC_CS_N,
- TNETV107X_LCD_PD21_1,
- TNETV107X_SSP0_2,
- TNETV107X_SCC_D,
- TNETV107X_LCD_PD22_1,
- TNETV107X_SSP0_3,
- TNETV107X_SCC_RESETN,
- TNETV107X_LCD_PD23_1,
- TNETV107X_SSP1_0,
- TNETV107X_GPIO25,
- TNETV107X_UART2_CTS,
- TNETV107X_SSP1_1,
- TNETV107X_GPIO26,
- TNETV107X_UART2_RD,
- TNETV107X_SSP1_2,
- TNETV107X_GPIO27,
- TNETV107X_UART2_RTS,
- TNETV107X_SSP1_3,
- TNETV107X_GPIO28,
- TNETV107X_UART2_TD,
- TNETV107X_UART0_CTS,
- TNETV107X_UART0_RD,
- TNETV107X_UART0_RTS,
- TNETV107X_UART0_TD,
- TNETV107X_UART1_RD,
- TNETV107X_UART1_TD,
- TNETV107X_LCD_AC_NCS,
- TNETV107X_LCD_HSYNC_RNW,
- TNETV107X_LCD_VSYNC_A0,
- TNETV107X_LCD_MCLK,
- TNETV107X_LCD_PD16_0,
- TNETV107X_LCD_PCLK_E,
- TNETV107X_LCD_PD00,
- TNETV107X_LCD_PD01,
- TNETV107X_LCD_PD02,
- TNETV107X_LCD_PD03,
- TNETV107X_LCD_PD04,
- TNETV107X_LCD_PD05,
- TNETV107X_LCD_PD06,
- TNETV107X_LCD_PD07,
- TNETV107X_LCD_PD08,
- TNETV107X_GPIO59_1,
- TNETV107X_LCD_PD09,
- TNETV107X_GPIO60_1,
- TNETV107X_LCD_PD10,
- TNETV107X_ASR_BA0_1,
- TNETV107X_GPIO61_1,
- TNETV107X_LCD_PD11,
- TNETV107X_GPIO62_1,
- TNETV107X_LCD_PD12,
- TNETV107X_GPIO63_1,
- TNETV107X_LCD_PD13,
- TNETV107X_GPIO64_1,
- TNETV107X_LCD_PD14,
- TNETV107X_GPIO29_1,
- TNETV107X_LCD_PD15,
- TNETV107X_GPIO30_1,
- TNETV107X_EINT0,
- TNETV107X_GPIO08,
- TNETV107X_EINT1,
- TNETV107X_GPIO09,
- TNETV107X_GPIO00,
- TNETV107X_LCD_PD20_2,
- TNETV107X_TDM_CLK_IN_2,
- TNETV107X_GPIO01,
- TNETV107X_LCD_PD21_2,
- TNETV107X_24M_CLK_OUT_1,
- TNETV107X_GPIO02,
- TNETV107X_LCD_PD22_2,
- TNETV107X_GPIO03,
- TNETV107X_LCD_PD23_2,
- TNETV107X_GPIO04,
- TNETV107X_LCD_PD16_1,
- TNETV107X_USB0_RXERR,
- TNETV107X_GPIO05,
- TNETV107X_LCD_PD17_1,
- TNETV107X_TDM_CLK_IN_1,
- TNETV107X_GPIO06,
- TNETV107X_LCD_PD18,
- TNETV107X_24M_CLK_OUT_2,
- TNETV107X_GPIO07,
- TNETV107X_LCD_PD19_1,
- TNETV107X_USB1_RXERR,
- TNETV107X_ETH_PLL_CLK,
- TNETV107X_MDIO,
- TNETV107X_MDC,
- TNETV107X_AIC_MUTE_STAT_N,
- TNETV107X_TDM0_CLK,
- TNETV107X_AIC_HNS_EN_N,
- TNETV107X_TDM0_FS,
- TNETV107X_AIC_HDS_EN_STAT_N,
- TNETV107X_TDM0_TX,
- TNETV107X_AIC_HNF_EN_STAT_N,
- TNETV107X_TDM0_RX,
-};
-
#define PINMUX(x) (4 * (x))
#ifdef CONFIG_DAVINCI_MUX
diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h
index 0a22710493fd..99d47cfa301f 100644
--- a/arch/arm/mach-davinci/include/mach/psc.h
+++ b/arch/arm/mach-davinci/include/mach/psc.h
@@ -182,53 +182,6 @@
#define DA8XX_LPSC1_CR_P3_SS 26
#define DA8XX_LPSC1_L3_CBA_RAM 31
-/* TNETV107X LPSC Assignments */
-#define TNETV107X_LPSC_ARM 0
-#define TNETV107X_LPSC_GEM 1
-#define TNETV107X_LPSC_DDR2_PHY 2
-#define TNETV107X_LPSC_TPCC 3
-#define TNETV107X_LPSC_TPTC0 4
-#define TNETV107X_LPSC_TPTC1 5
-#define TNETV107X_LPSC_RAM 6
-#define TNETV107X_LPSC_MBX_LITE 7
-#define TNETV107X_LPSC_LCD 8
-#define TNETV107X_LPSC_ETHSS 9
-#define TNETV107X_LPSC_AEMIF 10
-#define TNETV107X_LPSC_CHIP_CFG 11
-#define TNETV107X_LPSC_TSC 12
-#define TNETV107X_LPSC_ROM 13
-#define TNETV107X_LPSC_UART2 14
-#define TNETV107X_LPSC_PKTSEC 15
-#define TNETV107X_LPSC_SECCTL 16
-#define TNETV107X_LPSC_KEYMGR 17
-#define TNETV107X_LPSC_KEYPAD 18
-#define TNETV107X_LPSC_GPIO 19
-#define TNETV107X_LPSC_MDIO 20
-#define TNETV107X_LPSC_SDIO0 21
-#define TNETV107X_LPSC_UART0 22
-#define TNETV107X_LPSC_UART1 23
-#define TNETV107X_LPSC_TIMER0 24
-#define TNETV107X_LPSC_TIMER1 25
-#define TNETV107X_LPSC_WDT_ARM 26
-#define TNETV107X_LPSC_WDT_DSP 27
-#define TNETV107X_LPSC_SSP 28
-#define TNETV107X_LPSC_TDM0 29
-#define TNETV107X_LPSC_VLYNQ 30
-#define TNETV107X_LPSC_MCDMA 31
-#define TNETV107X_LPSC_USB0 32
-#define TNETV107X_LPSC_TDM1 33
-#define TNETV107X_LPSC_DEBUGSS 34
-#define TNETV107X_LPSC_ETHSS_RGMII 35
-#define TNETV107X_LPSC_SYSTEM 36
-#define TNETV107X_LPSC_IMCOP 37
-#define TNETV107X_LPSC_SPARE 38
-#define TNETV107X_LPSC_SDIO1 39
-#define TNETV107X_LPSC_USB1 40
-#define TNETV107X_LPSC_USBSS 41
-#define TNETV107X_LPSC_DDR2_EMIF1_VRST 42
-#define TNETV107X_LPSC_DDR2_EMIF2_VCTL_RST 43
-#define TNETV107X_LPSC_MAX 44
-
/* PSC register offsets */
#define EPCPR 0x070
#define PTCMD 0x120
diff --git a/arch/arm/mach-davinci/include/mach/serial.h b/arch/arm/mach-davinci/include/mach/serial.h
index ce402cd21fa0..d4b4aa87964f 100644
--- a/arch/arm/mach-davinci/include/mach/serial.h
+++ b/arch/arm/mach-davinci/include/mach/serial.h
@@ -23,14 +23,6 @@
#define DA8XX_UART1_BASE (IO_PHYS + 0x10c000)
#define DA8XX_UART2_BASE (IO_PHYS + 0x10d000)
-#define TNETV107X_UART0_BASE 0x08108100
-#define TNETV107X_UART1_BASE 0x08088400
-#define TNETV107X_UART2_BASE 0x08108300
-
-#define TNETV107X_UART0_VIRT IOMEM(0xfee08100)
-#define TNETV107X_UART1_VIRT IOMEM(0xfed88400)
-#define TNETV107X_UART2_VIRT IOMEM(0xfee08300)
-
/* DaVinci UART register offsets */
#define UART_DAVINCI_PWREMU 0x0c
#define UART_DM646X_SCR 0x10
diff --git a/arch/arm/mach-davinci/include/mach/timex.h b/arch/arm/mach-davinci/include/mach/timex.h
deleted file mode 100644
index 9b885298f106..000000000000
--- a/arch/arm/mach-davinci/include/mach/timex.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * DaVinci timer defines
- *
- * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
- *
- * 2007 (c) MontaVista Software, Inc. 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 __ASM_ARCH_TIMEX_H
-#define __ASM_ARCH_TIMEX_H
-
-/*
- * Alert: Not all timers of the DaVinci family run at a frequency of 27MHz,
- * but we should be fine as long as CLOCK_TICK_RATE or LATCH (see include/
- * linux/jiffies.h) are not used directly in code. Currently none of the
- * code relevant to DaVinci platform depends on these values directly.
- */
-#define CLOCK_TICK_RATE 27000000
-
-#endif /* __ASM_ARCH_TIMEX_H__ */
diff --git a/arch/arm/mach-davinci/include/mach/tnetv107x.h b/arch/arm/mach-davinci/include/mach/tnetv107x.h
deleted file mode 100644
index 494fcf5ccfe1..000000000000
--- a/arch/arm/mach-davinci/include/mach/tnetv107x.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Texas Instruments TNETV107X SoC Specific Defines
- *
- * Copyright (C) 2010 Texas Instruments
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#ifndef __ASM_ARCH_DAVINCI_TNETV107X_H
-#define __ASM_ARCH_DAVINCI_TNETV107X_H
-
-#include <asm/sizes.h>
-
-#define TNETV107X_DDR_BASE 0x80000000
-
-/*
- * Fixed mapping for early init starts here. If low-level debug is enabled,
- * this area also gets mapped via io_pg_offset and io_phys by the boot code.
- * To fit in with the io_pg_offset calculation, the io base address selected
- * here _must_ be a multiple of 2^20.
- */
-#define TNETV107X_IO_BASE 0x08000000
-#define TNETV107X_IO_VIRT (IO_VIRT + SZ_1M)
-
-#define TNETV107X_N_GPIO 65
-
-#ifndef __ASSEMBLY__
-
-#include <linux/serial_8250.h>
-#include <linux/input/matrix_keypad.h>
-#include <linux/mfd/ti_ssp.h>
-#include <linux/reboot.h>
-
-#include <linux/platform_data/mmc-davinci.h>
-#include <linux/platform_data/mtd-davinci.h>
-#include <mach/serial.h>
-
-struct tnetv107x_device_info {
- struct davinci_mmc_config *mmc_config[2]; /* 2 controllers */
- struct davinci_nand_pdata *nand_config[4]; /* 4 chipsels */
- struct matrix_keypad_platform_data *keypad_config;
- struct ti_ssp_data *ssp_config;
-};
-
-extern struct platform_device tnetv107x_wdt_device;
-extern struct platform_device tnetv107x_serial_device[];
-
-extern void tnetv107x_init(void);
-extern void tnetv107x_devices_init(struct tnetv107x_device_info *);
-extern void tnetv107x_irq_init(void);
-void tnetv107x_restart(enum reboot_mode mode, const char *cmd);
-
-#endif
-
-#endif /* __ASM_ARCH_DAVINCI_TNETV107X_H */
diff --git a/arch/arm/mach-davinci/include/mach/uncompress.h b/arch/arm/mach-davinci/include/mach/uncompress.h
index f49c2916aa3a..8fb97b93b6bb 100644
--- a/arch/arm/mach-davinci/include/mach/uncompress.h
+++ b/arch/arm/mach-davinci/include/mach/uncompress.h
@@ -68,9 +68,6 @@ static inline void set_uart_info(u32 phys)
#define DEBUG_LL_DA8XX(machine, port) \
_DEBUG_LL_ENTRY(machine, DA8XX_UART##port##_BASE)
-#define DEBUG_LL_TNETV107X(machine, port) \
- _DEBUG_LL_ENTRY(machine, TNETV107X_UART##port##_BASE)
-
static inline void __arch_decomp_setup(unsigned long arch_id)
{
/*
@@ -94,9 +91,6 @@ static inline void __arch_decomp_setup(unsigned long arch_id)
DEBUG_LL_DA8XX(davinci_da850_evm, 2);
DEBUG_LL_DA8XX(mityomapl138, 1);
DEBUG_LL_DA8XX(omapl138_hawkboard, 2);
-
- /* TNETV107x boards */
- DEBUG_LL_TNETV107X(tnetv107x, 1);
} while (0);
}
diff --git a/arch/arm/mach-davinci/tnetv107x.c b/arch/arm/mach-davinci/tnetv107x.c
deleted file mode 100644
index f4d7fbb24b3b..000000000000
--- a/arch/arm/mach-davinci/tnetv107x.c
+++ /dev/null
@@ -1,766 +0,0 @@
-/*
- * Texas Instruments TNETV107X SoC Support
- *
- * Copyright (C) 2010 Texas Instruments
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#include <linux/gpio.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/reboot.h>
-
-#include <asm/mach/map.h>
-
-#include <mach/common.h>
-#include <mach/time.h>
-#include <mach/cputype.h>
-#include <mach/psc.h>
-#include <mach/cp_intc.h>
-#include <mach/irqs.h>
-#include <mach/hardware.h>
-#include <mach/tnetv107x.h>
-#include <mach/gpio-davinci.h>
-
-#include "clock.h"
-#include "mux.h"
-
-/* Base addresses for on-chip devices */
-#define TNETV107X_INTC_BASE 0x03000000
-#define TNETV107X_TIMER0_BASE 0x08086500
-#define TNETV107X_TIMER1_BASE 0x08086600
-#define TNETV107X_CHIP_CFG_BASE 0x08087000
-#define TNETV107X_GPIO_BASE 0x08088000
-#define TNETV107X_CLOCK_CONTROL_BASE 0x0808a000
-#define TNETV107X_PSC_BASE 0x0808b000
-
-/* Reference clock frequencies */
-#define OSC_FREQ_ONCHIP (24000 * 1000)
-#define OSC_FREQ_OFFCHIP_SYS (25000 * 1000)
-#define OSC_FREQ_OFFCHIP_ETH (25000 * 1000)
-#define OSC_FREQ_OFFCHIP_TDM (19200 * 1000)
-
-#define N_PLLS 3
-
-/* Clock Control Registers */
-struct clk_ctrl_regs {
- u32 pll_bypass;
- u32 _reserved0;
- u32 gem_lrst;
- u32 _reserved1;
- u32 pll_unlock_stat;
- u32 sys_unlock;
- u32 eth_unlock;
- u32 tdm_unlock;
-};
-
-/* SSPLL Registers */
-struct sspll_regs {
- u32 modes;
- u32 post_div;
- u32 pre_div;
- u32 mult_factor;
- u32 divider_range;
- u32 bw_divider;
- u32 spr_amount;
- u32 spr_rate_div;
- u32 diag;
-};
-
-/* Watchdog Timer Registers */
-struct wdt_regs {
- u32 kick_lock;
- u32 kick;
- u32 change_lock;
- u32 change ;
- u32 disable_lock;
- u32 disable;
- u32 prescale_lock;
- u32 prescale;
-};
-
-static struct clk_ctrl_regs __iomem *clk_ctrl_regs;
-
-static struct sspll_regs __iomem *sspll_regs[N_PLLS];
-static int sspll_regs_base[N_PLLS] = { 0x40, 0x80, 0xc0 };
-
-/* PLL bypass bit shifts in clk_ctrl_regs->pll_bypass register */
-static u32 bypass_mask[N_PLLS] = { BIT(0), BIT(2), BIT(1) };
-
-/* offchip (external) reference clock frequencies */
-static u32 pll_ext_freq[] = {
- OSC_FREQ_OFFCHIP_SYS,
- OSC_FREQ_OFFCHIP_TDM,
- OSC_FREQ_OFFCHIP_ETH
-};
-
-/* PSC control registers */
-static u32 psc_regs[] = { TNETV107X_PSC_BASE };
-
-/* Host map for interrupt controller */
-static u32 intc_host_map[] = { 0x01010000, 0x01010101, -1 };
-
-static unsigned long clk_sspll_recalc(struct clk *clk);
-
-/* Level 1 - the PLLs */
-#define define_pll_clk(cname, pll, divmask, base) \
- static struct pll_data pll_##cname##_data = { \
- .num = pll, \
- .div_ratio_mask = divmask, \
- .phys_base = base + \
- TNETV107X_CLOCK_CONTROL_BASE, \
- }; \
- static struct clk pll_##cname##_clk = { \
- .name = "pll_" #cname "_clk", \
- .pll_data = &pll_##cname##_data, \
- .flags = CLK_PLL, \
- .recalc = clk_sspll_recalc, \
- }
-
-define_pll_clk(sys, 0, 0x1ff, 0x600);
-define_pll_clk(tdm, 1, 0x0ff, 0x200);
-define_pll_clk(eth, 2, 0x0ff, 0x400);
-
-/* Level 2 - divided outputs from the PLLs */
-#define define_pll_div_clk(pll, cname, div) \
- static struct clk pll##_##cname##_clk = { \
- .name = #pll "_" #cname "_clk", \
- .parent = &pll_##pll##_clk, \
- .flags = CLK_PLL, \
- .div_reg = PLLDIV##div, \
- .set_rate = davinci_set_sysclk_rate, \
- }
-
-define_pll_div_clk(sys, arm1176, 1);
-define_pll_div_clk(sys, dsp, 2);
-define_pll_div_clk(sys, ddr, 3);
-define_pll_div_clk(sys, full, 4);
-define_pll_div_clk(sys, lcd, 5);
-define_pll_div_clk(sys, vlynq_ref, 6);
-define_pll_div_clk(sys, tsc, 7);
-define_pll_div_clk(sys, half, 8);
-
-define_pll_div_clk(eth, 5mhz, 1);
-define_pll_div_clk(eth, 50mhz, 2);
-define_pll_div_clk(eth, 125mhz, 3);
-define_pll_div_clk(eth, 250mhz, 4);
-define_pll_div_clk(eth, 25mhz, 5);
-
-define_pll_div_clk(tdm, 0, 1);
-define_pll_div_clk(tdm, extra, 2);
-define_pll_div_clk(tdm, 1, 3);
-
-
-/* Level 3 - LPSC gated clocks */
-#define __lpsc_clk(cname, _parent, mod, flg) \
- static struct clk clk_##cname = { \
- .name = #cname, \
- .parent = &_parent, \
- .lpsc = TNETV107X_LPSC_##mod,\
- .flags = flg, \
- }
-
-#define lpsc_clk_enabled(cname, parent, mod) \
- __lpsc_clk(cname, parent, mod, ALWAYS_ENABLED)
-
-#define lpsc_clk(cname, parent, mod) \
- __lpsc_clk(cname, parent, mod, 0)
-
-lpsc_clk_enabled(arm, sys_arm1176_clk, ARM);
-lpsc_clk_enabled(gem, sys_dsp_clk, GEM);
-lpsc_clk_enabled(ddr2_phy, sys_ddr_clk, DDR2_PHY);
-lpsc_clk_enabled(tpcc, sys_full_clk, TPCC);
-lpsc_clk_enabled(tptc0, sys_full_clk, TPTC0);
-lpsc_clk_enabled(tptc1, sys_full_clk, TPTC1);
-lpsc_clk_enabled(ram, sys_full_clk, RAM);
-lpsc_clk_enabled(aemif, sys_full_clk, AEMIF);
-lpsc_clk_enabled(chipcfg, sys_half_clk, CHIP_CFG);
-lpsc_clk_enabled(rom, sys_half_clk, ROM);
-lpsc_clk_enabled(secctl, sys_half_clk, SECCTL);
-lpsc_clk_enabled(keymgr, sys_half_clk, KEYMGR);
-lpsc_clk_enabled(gpio, sys_half_clk, GPIO);
-lpsc_clk_enabled(debugss, sys_half_clk, DEBUGSS);
-lpsc_clk_enabled(system, sys_half_clk, SYSTEM);
-lpsc_clk_enabled(ddr2_vrst, sys_ddr_clk, DDR2_EMIF1_VRST);
-lpsc_clk_enabled(ddr2_vctl_rst, sys_ddr_clk, DDR2_EMIF2_VCTL_RST);
-lpsc_clk_enabled(wdt_arm, sys_half_clk, WDT_ARM);
-lpsc_clk_enabled(timer1, sys_half_clk, TIMER1);
-
-lpsc_clk(mbx_lite, sys_arm1176_clk, MBX_LITE);
-lpsc_clk(ethss, eth_125mhz_clk, ETHSS);
-lpsc_clk(tsc, sys_tsc_clk, TSC);
-lpsc_clk(uart0, sys_half_clk, UART0);
-lpsc_clk(uart1, sys_half_clk, UART1);
-lpsc_clk(uart2, sys_half_clk, UART2);
-lpsc_clk(pktsec, sys_half_clk, PKTSEC);
-lpsc_clk(keypad, sys_half_clk, KEYPAD);
-lpsc_clk(mdio, sys_half_clk, MDIO);
-lpsc_clk(sdio0, sys_half_clk, SDIO0);
-lpsc_clk(sdio1, sys_half_clk, SDIO1);
-lpsc_clk(timer0, sys_half_clk, TIMER0);
-lpsc_clk(wdt_dsp, sys_half_clk, WDT_DSP);
-lpsc_clk(ssp, sys_half_clk, SSP);
-lpsc_clk(tdm0, tdm_0_clk, TDM0);
-lpsc_clk(tdm1, tdm_1_clk, TDM1);
-lpsc_clk(vlynq, sys_vlynq_ref_clk, VLYNQ);
-lpsc_clk(mcdma, sys_half_clk, MCDMA);
-lpsc_clk(usbss, sys_half_clk, USBSS);
-lpsc_clk(usb0, clk_usbss, USB0);
-lpsc_clk(usb1, clk_usbss, USB1);
-lpsc_clk(ethss_rgmii, eth_250mhz_clk, ETHSS_RGMII);
-lpsc_clk(imcop, sys_dsp_clk, IMCOP);
-lpsc_clk(spare, sys_half_clk, SPARE);
-
-/* LCD needs a full power down to clear controller state */
-__lpsc_clk(lcd, sys_lcd_clk, LCD, PSC_SWRSTDISABLE);
-
-
-/* Level 4 - leaf clocks for LPSC modules shared across drivers */
-static struct clk clk_rng = { .name = "rng", .parent = &clk_pktsec };
-static struct clk clk_pka = { .name = "pka", .parent = &clk_pktsec };
-
-static struct clk_lookup clks[] = {
- CLK(NULL, "pll_sys_clk", &pll_sys_clk),
- CLK(NULL, "pll_eth_clk", &pll_eth_clk),
- CLK(NULL, "pll_tdm_clk", &pll_tdm_clk),
- CLK(NULL, "sys_arm1176_clk", &sys_arm1176_clk),
- CLK(NULL, "sys_dsp_clk", &sys_dsp_clk),
- CLK(NULL, "sys_ddr_clk", &sys_ddr_clk),
- CLK(NULL, "sys_full_clk", &sys_full_clk),
- CLK(NULL, "sys_lcd_clk", &sys_lcd_clk),
- CLK(NULL, "sys_vlynq_ref_clk", &sys_vlynq_ref_clk),
- CLK(NULL, "sys_tsc_clk", &sys_tsc_clk),
- CLK(NULL, "sys_half_clk", &sys_half_clk),
- CLK(NULL, "eth_5mhz_clk", &eth_5mhz_clk),
- CLK(NULL, "eth_50mhz_clk", &eth_50mhz_clk),
- CLK(NULL, "eth_125mhz_clk", &eth_125mhz_clk),
- CLK(NULL, "eth_250mhz_clk", &eth_250mhz_clk),
- CLK(NULL, "eth_25mhz_clk", &eth_25mhz_clk),
- CLK(NULL, "tdm_0_clk", &tdm_0_clk),
- CLK(NULL, "tdm_extra_clk", &tdm_extra_clk),
- CLK(NULL, "tdm_1_clk", &tdm_1_clk),
- CLK(NULL, "clk_arm", &clk_arm),
- CLK(NULL, "clk_gem", &clk_gem),
- CLK(NULL, "clk_ddr2_phy", &clk_ddr2_phy),
- CLK(NULL, "clk_tpcc", &clk_tpcc),
- CLK(NULL, "clk_tptc0", &clk_tptc0),
- CLK(NULL, "clk_tptc1", &clk_tptc1),
- CLK(NULL, "clk_ram", &clk_ram),
- CLK(NULL, "clk_mbx_lite", &clk_mbx_lite),
- CLK("tnetv107x-fb.0", NULL, &clk_lcd),
- CLK(NULL, "clk_ethss", &clk_ethss),
- CLK(NULL, "aemif", &clk_aemif),
- CLK(NULL, "clk_chipcfg", &clk_chipcfg),
- CLK("tnetv107x-ts.0", NULL, &clk_tsc),
- CLK(NULL, "clk_rom", &clk_rom),
- CLK("serial8250.2", NULL, &clk_uart2),
- CLK(NULL, "clk_pktsec", &clk_pktsec),
- CLK("tnetv107x-rng.0", NULL, &clk_rng),
- CLK("tnetv107x-pka.0", NULL, &clk_pka),
- CLK(NULL, "clk_secctl", &clk_secctl),
- CLK(NULL, "clk_keymgr", &clk_keymgr),
- CLK("tnetv107x-keypad.0", NULL, &clk_keypad),
- CLK(NULL, "clk_gpio", &clk_gpio),
- CLK(NULL, "clk_mdio", &clk_mdio),
- CLK("dm6441-mmc.0", NULL, &clk_sdio0),
- CLK("serial8250.0", NULL, &clk_uart0),
- CLK("serial8250.1", NULL, &clk_uart1),
- CLK(NULL, "timer0", &clk_timer0),
- CLK(NULL, "timer1", &clk_timer1),
- CLK("tnetv107x_wdt.0", NULL, &clk_wdt_arm),
- CLK(NULL, "clk_wdt_dsp", &clk_wdt_dsp),
- CLK("ti-ssp", NULL, &clk_ssp),
- CLK(NULL, "clk_tdm0", &clk_tdm0),
- CLK(NULL, "clk_vlynq", &clk_vlynq),
- CLK(NULL, "clk_mcdma", &clk_mcdma),
- CLK(NULL, "clk_usbss", &clk_usbss),
- CLK(NULL, "clk_usb0", &clk_usb0),
- CLK(NULL, "clk_usb1", &clk_usb1),
- CLK(NULL, "clk_tdm1", &clk_tdm1),
- CLK(NULL, "clk_debugss", &clk_debugss),
- CLK(NULL, "clk_ethss_rgmii", &clk_ethss_rgmii),
- CLK(NULL, "clk_system", &clk_system),
- CLK(NULL, "clk_imcop", &clk_imcop),
- CLK(NULL, "clk_spare", &clk_spare),
- CLK("dm6441-mmc.1", NULL, &clk_sdio1),
- CLK(NULL, "clk_ddr2_vrst", &clk_ddr2_vrst),
- CLK(NULL, "clk_ddr2_vctl_rst", &clk_ddr2_vctl_rst),
- CLK(NULL, NULL, NULL),
-};
-
-static const struct mux_config pins[] = {
-#ifdef CONFIG_DAVINCI_MUX
- MUX_CFG(TNETV107X, ASR_A00, 0, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO32, 0, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A01, 0, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO33, 0, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A02, 0, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO34, 0, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A03, 0, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO35, 0, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A04, 0, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO36, 0, 20, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A05, 0, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO37, 0, 25, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A06, 1, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO38, 1, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A07, 1, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO39, 1, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A08, 1, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO40, 1, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A09, 1, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO41, 1, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A10, 1, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO42, 1, 20, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A11, 1, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, BOOT_STRP_0, 1, 25, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A12, 2, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, BOOT_STRP_1, 2, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A13, 2, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO43, 2, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A14, 2, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO44, 2, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A15, 2, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO45, 2, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A16, 2, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO46, 2, 20, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A17, 2, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO47, 2, 25, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_A18, 3, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO48, 3, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SDIO1_DATA3_0, 3, 0, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_A19, 3, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO49, 3, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SDIO1_DATA2_0, 3, 5, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_A20, 3, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO50, 3, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SDIO1_DATA1_0, 3, 10, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_A21, 3, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO51, 3, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SDIO1_DATA0_0, 3, 15, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_A22, 3, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO52, 3, 20, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SDIO1_CMD_0, 3, 20, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_A23, 3, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO53, 3, 25, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SDIO1_CLK_0, 3, 25, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_BA_1, 4, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO54, 4, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SYS_PLL_CLK, 4, 0, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_CS0, 4, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, ASR_CS1, 4, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, ASR_CS2, 4, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TDM_PLL_CLK, 4, 15, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_CS3, 4, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, ETH_PHY_CLK, 4, 20, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, ASR_D00, 4, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO55, 4, 25, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D01, 5, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO56, 5, 0, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D02, 5, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO57, 5, 5, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D03, 5, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO58, 5, 10, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D04, 5, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO59_0, 5, 15, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D05, 5, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO60_0, 5, 20, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D06, 5, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO61_0, 5, 25, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D07, 6, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO62_0, 6, 0, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D08, 6, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO63_0, 6, 5, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D09, 6, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO64_0, 6, 10, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D10, 6, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, SDIO1_DATA3_1, 6, 15, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D11, 6, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, SDIO1_DATA2_1, 6, 20, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D12, 6, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, SDIO1_DATA1_1, 6, 25, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D13, 7, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, SDIO1_DATA0_1, 7, 0, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D14, 7, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, SDIO1_CMD_1, 7, 5, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_D15, 7, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, SDIO1_CLK_1, 7, 10, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_OE, 7, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, BOOT_STRP_2, 7, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_RNW, 7, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO29_0, 7, 20, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_WAIT, 7, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO30_0, 7, 25, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_WE, 8, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, BOOT_STRP_3, 8, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, ASR_WE_DQM0, 8, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO31, 8, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, LCD_PD17_0, 8, 5, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, ASR_WE_DQM1, 8, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, ASR_BA0_0, 8, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, VLYNQ_CLK, 9, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO14, 9, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, LCD_PD19_0, 9, 0, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, VLYNQ_RXD0, 9, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO15, 9, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, LCD_PD20_0, 9, 5, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, VLYNQ_RXD1, 9, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO16, 9, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, LCD_PD21_0, 9, 10, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, VLYNQ_TXD0, 9, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO17, 9, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, LCD_PD22_0, 9, 15, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, VLYNQ_TXD1, 9, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO18, 9, 20, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, LCD_PD23_0, 9, 20, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, SDIO0_CLK, 10, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO19, 10, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SDIO0_CMD, 10, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO20, 10, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SDIO0_DATA0, 10, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO21, 10, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SDIO0_DATA1, 10, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO22, 10, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SDIO0_DATA2, 10, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO23, 10, 20, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SDIO0_DATA3, 10, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO24, 10, 25, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, EMU0, 11, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, EMU1, 11, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, RTCK, 12, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TRST_N, 12, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TCK, 12, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TDI, 12, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TDO, 12, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TMS, 12, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TDM1_CLK, 13, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TDM1_RX, 13, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TDM1_TX, 13, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TDM1_FS, 13, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_R0, 14, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_R1, 14, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_R2, 14, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_R3, 14, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_R4, 14, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_R5, 14, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_R6, 15, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO12, 15, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, KEYPAD_R7, 15, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO10, 15, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, KEYPAD_C0, 15, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_C1, 15, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_C2, 15, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_C3, 15, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_C4, 16, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_C5, 16, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, KEYPAD_C6, 16, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO13, 16, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, TEST_CLK_IN, 16, 10, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, KEYPAD_C7, 16, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO11, 16, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, SSP0_0, 17, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, SCC_DCLK, 17, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, LCD_PD20_1, 17, 0, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, SSP0_1, 17, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, SCC_CS_N, 17, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, LCD_PD21_1, 17, 5, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, SSP0_2, 17, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, SCC_D, 17, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, LCD_PD22_1, 17, 10, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, SSP0_3, 17, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, SCC_RESETN, 17, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, LCD_PD23_1, 17, 15, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, SSP1_0, 18, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO25, 18, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, UART2_CTS, 18, 0, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, SSP1_1, 18, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO26, 18, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, UART2_RD, 18, 5, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, SSP1_2, 18, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO27, 18, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, UART2_RTS, 18, 10, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, SSP1_3, 18, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO28, 18, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, UART2_TD, 18, 15, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, UART0_CTS, 19, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, UART0_RD, 19, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, UART0_RTS, 19, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, UART0_TD, 19, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, UART1_RD, 19, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, UART1_TD, 19, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_AC_NCS, 20, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_HSYNC_RNW, 20, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_VSYNC_A0, 20, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_MCLK, 20, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD16_0, 20, 15, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, LCD_PCLK_E, 20, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD00, 20, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD01, 21, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD02, 21, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD03, 21, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD04, 21, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD05, 21, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD06, 21, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD07, 22, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD08, 22, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO59_1, 22, 5, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, LCD_PD09, 22, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO60_1, 22, 10, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, LCD_PD10, 22, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, ASR_BA0_1, 22, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, GPIO61_1, 22, 15, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, LCD_PD11, 22, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO62_1, 22, 20, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, LCD_PD12, 22, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO63_1, 22, 25, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, LCD_PD13, 23, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO64_1, 23, 0, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, LCD_PD14, 23, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO29_1, 23, 5, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, LCD_PD15, 23, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO30_1, 23, 10, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, EINT0, 24, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO08, 24, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, EINT1, 24, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, GPIO09, 24, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, GPIO00, 24, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD20_2, 24, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, TDM_CLK_IN_2, 24, 10, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, GPIO01, 24, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD21_2, 24, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, 24M_CLK_OUT_1, 24, 15, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, GPIO02, 24, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD22_2, 24, 20, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, GPIO03, 24, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD23_2, 24, 25, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, GPIO04, 25, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD16_1, 25, 0, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, USB0_RXERR, 25, 0, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, GPIO05, 25, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD17_1, 25, 5, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, TDM_CLK_IN_1, 25, 5, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, GPIO06, 25, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD18, 25, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, 24M_CLK_OUT_2, 25, 10, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, GPIO07, 25, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, LCD_PD19_1, 25, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, USB1_RXERR, 25, 15, 0x1f, 0x0c, false)
- MUX_CFG(TNETV107X, ETH_PLL_CLK, 25, 15, 0x1f, 0x1c, false)
- MUX_CFG(TNETV107X, MDIO, 26, 0, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, MDC, 26, 5, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, AIC_MUTE_STAT_N, 26, 10, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TDM0_CLK, 26, 10, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, AIC_HNS_EN_N, 26, 15, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TDM0_FS, 26, 15, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, AIC_HDS_EN_STAT_N, 26, 20, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TDM0_TX, 26, 20, 0x1f, 0x04, false)
- MUX_CFG(TNETV107X, AIC_HNF_EN_STAT_N, 26, 25, 0x1f, 0x00, false)
- MUX_CFG(TNETV107X, TDM0_RX, 26, 25, 0x1f, 0x04, false)
-#endif
-};
-
-/* FIQ are pri 0-1; otherwise 2-7, with 7 lowest priority */
-static u8 irq_prios[TNETV107X_N_CP_INTC_IRQ] = {
- /* fill in default priority 7 */
- [0 ... (TNETV107X_N_CP_INTC_IRQ - 1)] = 7,
- /* now override as needed, e.g. [xxx] = 5 */
-};
-
-/* Contents of JTAG ID register used to identify exact cpu type */
-static struct davinci_id ids[] = {
- {
- .variant = 0x0,
- .part_no = 0xb8a1,
- .manufacturer = 0x017,
- .cpu_id = DAVINCI_CPU_ID_TNETV107X,
- .name = "tnetv107x rev 1.0",
- },
- {
- .variant = 0x1,
- .part_no = 0xb8a1,
- .manufacturer = 0x017,
- .cpu_id = DAVINCI_CPU_ID_TNETV107X,
- .name = "tnetv107x rev 1.1/1.2",
- },
-};
-
-static struct davinci_timer_instance timer_instance[2] = {
- {
- .base = TNETV107X_TIMER0_BASE,
- .bottom_irq = IRQ_TNETV107X_TIMER_0_TINT12,
- .top_irq = IRQ_TNETV107X_TIMER_0_TINT34,
- },
- {
- .base = TNETV107X_TIMER1_BASE,
- .bottom_irq = IRQ_TNETV107X_TIMER_1_TINT12,
- .top_irq = IRQ_TNETV107X_TIMER_1_TINT34,
- },
-};
-
-static struct davinci_timer_info timer_info = {
- .timers = timer_instance,
- .clockevent_id = T0_BOT,
- .clocksource_id = T0_TOP,
-};
-
-/*
- * TNETV107X platforms do not use the static mappings from Davinci
- * IO_PHYS/IO_VIRT. This SOC's interesting MMRs are at different addresses,
- * and changing IO_PHYS would break away from existing Davinci SOCs.
- *
- * The primary impact of the current model is that IO_ADDRESS() is not to be
- * used to map registers on TNETV107X.
- *
- * 1. The first chunk is for INTC: This needs to be mapped in via iotable
- * because ioremap() does not seem to be operational at the time when
- * irqs are initialized. Without this, consistent dma init bombs.
- *
- * 2. The second chunk maps in register areas that need to be populated into
- * davinci_soc_info. Note that alignment restrictions come into play if
- * low-level debug is enabled (see note in <mach/tnetv107x.h>).
- */
-static struct map_desc io_desc[] = {
- { /* INTC */
- .virtual = IO_VIRT,
- .pfn = __phys_to_pfn(TNETV107X_INTC_BASE),
- .length = SZ_16K,
- .type = MT_DEVICE
- },
- { /* Most of the rest */
- .virtual = TNETV107X_IO_VIRT,
- .pfn = __phys_to_pfn(TNETV107X_IO_BASE),
- .length = IO_SIZE - SZ_1M,
- .type = MT_DEVICE
- },
-};
-
-static unsigned long clk_sspll_recalc(struct clk *clk)
-{
- int pll;
- unsigned long mult = 0, prediv = 1, postdiv = 1;
- unsigned long ref = OSC_FREQ_ONCHIP, ret;
- u32 tmp;
-
- if (WARN_ON(!clk->pll_data))
- return clk->rate;
-
- if (!clk_ctrl_regs) {
- void __iomem *tmp;
-
- tmp = ioremap(TNETV107X_CLOCK_CONTROL_BASE, SZ_4K);
-
- if (WARN(!tmp, "failed ioremap for clock control regs\n"))
- return clk->parent ? clk->parent->rate : 0;
-
- for (pll = 0; pll < N_PLLS; pll++)
- sspll_regs[pll] = tmp + sspll_regs_base[pll];
-
- clk_ctrl_regs = tmp;
- }
-
- pll = clk->pll_data->num;
-
- tmp = __raw_readl(&clk_ctrl_regs->pll_bypass);
- if (!(tmp & bypass_mask[pll])) {
- mult = __raw_readl(&sspll_regs[pll]->mult_factor);
- prediv = __raw_readl(&sspll_regs[pll]->pre_div) + 1;
- postdiv = __raw_readl(&sspll_regs[pll]->post_div) + 1;
- }
-
- tmp = __raw_readl(clk->pll_data->base + PLLCTL);
- if (tmp & PLLCTL_CLKMODE)
- ref = pll_ext_freq[pll];
-
- clk->pll_data->input_rate = ref;
-
- tmp = __raw_readl(clk->pll_data->base + PLLCTL);
- if (!(tmp & PLLCTL_PLLEN))
- return ref;
-
- ret = ref;
- if (mult)
- ret += ((unsigned long long)ref * mult) / 256;
-
- ret /= (prediv * postdiv);
-
- return ret;
-}
-
-static void tnetv107x_watchdog_reset(struct platform_device *pdev)
-{
- struct wdt_regs __iomem *regs;
-
- regs = ioremap(pdev->resource[0].start, SZ_4K);
-
- /* disable watchdog */
- __raw_writel(0x7777, &regs->disable_lock);
- __raw_writel(0xcccc, &regs->disable_lock);
- __raw_writel(0xdddd, &regs->disable_lock);
- __raw_writel(0, &regs->disable);
-
- /* program prescale */
- __raw_writel(0x5a5a, &regs->prescale_lock);
- __raw_writel(0xa5a5, &regs->prescale_lock);
- __raw_writel(0, &regs->prescale);
-
- /* program countdown */
- __raw_writel(0x6666, &regs->change_lock);
- __raw_writel(0xbbbb, &regs->change_lock);
- __raw_writel(1, &regs->change);
-
- /* enable watchdog */
- __raw_writel(0x7777, &regs->disable_lock);
- __raw_writel(0xcccc, &regs->disable_lock);
- __raw_writel(0xdddd, &regs->disable_lock);
- __raw_writel(1, &regs->disable);
-
- /* kick */
- __raw_writel(0x5555, &regs->kick_lock);
- __raw_writel(0xaaaa, &regs->kick_lock);
- __raw_writel(1, &regs->kick);
-}
-
-void tnetv107x_restart(enum reboot_mode mode, const char *cmd)
-{
- tnetv107x_watchdog_reset(&tnetv107x_wdt_device);
-}
-
-static struct davinci_soc_info tnetv107x_soc_info = {
- .io_desc = io_desc,
- .io_desc_num = ARRAY_SIZE(io_desc),
- .ids = ids,
- .ids_num = ARRAY_SIZE(ids),
- .jtag_id_reg = TNETV107X_CHIP_CFG_BASE + 0x018,
- .cpu_clks = clks,
- .psc_bases = psc_regs,
- .psc_bases_num = ARRAY_SIZE(psc_regs),
- .pinmux_base = TNETV107X_CHIP_CFG_BASE + 0x150,
- .pinmux_pins = pins,
- .pinmux_pins_num = ARRAY_SIZE(pins),
- .intc_type = DAVINCI_INTC_TYPE_CP_INTC,
- .intc_base = TNETV107X_INTC_BASE,
- .intc_irq_prios = irq_prios,
- .intc_irq_num = TNETV107X_N_CP_INTC_IRQ,
- .intc_host_map = intc_host_map,
- .gpio_base = TNETV107X_GPIO_BASE,
- .gpio_type = GPIO_TYPE_TNETV107X,
- .gpio_num = TNETV107X_N_GPIO,
- .timer_info = &timer_info,
- .serial_dev = tnetv107x_serial_device,
-};
-
-void __init tnetv107x_init(void)
-{
- davinci_common_init(&tnetv107x_soc_info);
-}
diff --git a/arch/arm/mach-dove/Kconfig b/arch/arm/mach-dove/Kconfig
index 0bc7cdf8cf46..d8c439c89ea9 100644
--- a/arch/arm/mach-dove/Kconfig
+++ b/arch/arm/mach-dove/Kconfig
@@ -20,18 +20,6 @@ config MACH_CM_A510
Say 'Y' here if you want your kernel to support the
CompuLab CM-A510 Board.
-config MACH_DOVE_DT
- bool "Marvell Dove Flattened Device Tree"
- select DOVE_CLK
- select ORION_IRQCHIP
- select ORION_TIMER
- select REGULATOR
- select REGULATOR_FIXED_VOLTAGE
- select USE_OF
- help
- Say 'Y' here if you want your kernel to support the
- Marvell Dove using flattened device tree.
-
endmenu
endif
diff --git a/arch/arm/mach-dove/Makefile b/arch/arm/mach-dove/Makefile
index cbc5c0618788..b608a21919fb 100644
--- a/arch/arm/mach-dove/Makefile
+++ b/arch/arm/mach-dove/Makefile
@@ -2,5 +2,4 @@ obj-y += common.o
obj-$(CONFIG_DOVE_LEGACY) += irq.o mpp.o
obj-$(CONFIG_PCI) += pcie.o
obj-$(CONFIG_MACH_DOVE_DB) += dove-db-setup.o
-obj-$(CONFIG_MACH_DOVE_DT) += board-dt.o
obj-$(CONFIG_MACH_CM_A510) += cm-a510.o
diff --git a/arch/arm/mach-dove/include/mach/bridge-regs.h b/arch/arm/mach-dove/include/mach/bridge-regs.h
index 5362df3df89f..f4a5b34489b7 100644
--- a/arch/arm/mach-dove/include/mach/bridge-regs.h
+++ b/arch/arm/mach-dove/include/mach/bridge-regs.h
@@ -21,6 +21,7 @@
#define CPU_CTRL_PCIE1_LINK 0x00000008
#define RSTOUTn_MASK (BRIDGE_VIRT_BASE + 0x0108)
+#define RSTOUTn_MASK_PHYS (BRIDGE_PHYS_BASE + 0x0108)
#define SOFT_RESET_OUT_EN 0x00000004
#define SYSTEM_SOFT_RESET (BRIDGE_VIRT_BASE + 0x010c)
diff --git a/arch/arm/mach-dove/include/mach/timex.h b/arch/arm/mach-dove/include/mach/timex.h
deleted file mode 100644
index 251d538541db..000000000000
--- a/arch/arm/mach-dove/include/mach/timex.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * arch/arm/mach-dove/include/mach/timex.h
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#define CLOCK_TICK_RATE (100 * HZ)
diff --git a/arch/arm/mach-ebsa110/core.c b/arch/arm/mach-ebsa110/core.c
index 68ac934d4565..8254e716b095 100644
--- a/arch/arm/mach-ebsa110/core.c
+++ b/arch/arm/mach-ebsa110/core.c
@@ -206,7 +206,7 @@ ebsa110_timer_interrupt(int irq, void *dev_id)
static struct irqaction ebsa110_timer_irq = {
.name = "EBSA110 Timer Tick",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = ebsa110_timer_interrupt,
};
diff --git a/arch/arm/mach-ebsa110/include/mach/timex.h b/arch/arm/mach-ebsa110/include/mach/timex.h
deleted file mode 100644
index 4fb43b22a102..000000000000
--- a/arch/arm/mach-ebsa110/include/mach/timex.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * arch/arm/mach-ebsa110/include/mach/timex.h
- *
- * Copyright (C) 1997, 1998 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * EBSA110 architecture timex specifications
- */
-
-/*
- * On the EBSA, the clock ticks at weird rates.
- * This is therefore not used to calculate the
- * divisor.
- */
-#define CLOCK_TICK_RATE 47894000
-
diff --git a/arch/arm/mach-efm32/include/mach/entry-macro.S b/arch/arm/mach-efm32/include/mach/entry-macro.S
deleted file mode 100644
index 322159d5ed91..000000000000
--- a/arch/arm/mach-efm32/include/mach/entry-macro.S
+++ /dev/null
@@ -1,4 +0,0 @@
-/*
- * Empty file waiting for deletion once <mach/entry-macro.S> isn't needed any
- * more. Patch "ARM: v7-M: drop using mach/entry-macro.S" sitting in next.
- */
diff --git a/arch/arm/mach-efm32/include/mach/timex.h b/arch/arm/mach-efm32/include/mach/timex.h
deleted file mode 100644
index 7a8b26da6599..000000000000
--- a/arch/arm/mach-efm32/include/mach/timex.h
+++ /dev/null
@@ -1,3 +0,0 @@
-/*
- * Empty file waiting for deletion once <mach/timex.h> isn't needed any more.
- */
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
index 157ba88433c9..0e571f1749d6 100644
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -117,7 +117,7 @@ void __init ep93xx_map_io(void)
#define EP93XX_TIMER4_CLOCK 983040
#define TIMER1_RELOAD ((EP93XX_TIMER123_CLOCK / HZ) - 1)
-#define TIMER4_TICKS_PER_JIFFY DIV_ROUND_CLOSEST(CLOCK_TICK_RATE, HZ)
+#define TIMER4_TICKS_PER_JIFFY DIV_ROUND_CLOSEST(EP93XX_TIMER4_CLOCK, HZ)
static unsigned int last_jiffy_time;
@@ -242,6 +242,7 @@ unsigned int ep93xx_chip_revision(void)
v >>= EP93XX_SYSCON_SYSCFG_REV_SHIFT;
return v;
}
+EXPORT_SYMBOL_GPL(ep93xx_chip_revision);
/*************************************************************************
* EP93xx GPIO
diff --git a/arch/arm/mach-ep93xx/include/mach/timex.h b/arch/arm/mach-ep93xx/include/mach/timex.h
deleted file mode 100644
index 6b3503b01fa6..000000000000
--- a/arch/arm/mach-ep93xx/include/mach/timex.h
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * arch/arm/mach-ep93xx/include/mach/timex.h
- */
-
-#define CLOCK_TICK_RATE 983040
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index 8d197dcdd2c0..fc8bf18e222d 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -24,7 +24,7 @@ config ARCH_EXYNOS4
select HAVE_SMP
select MIGHT_HAVE_CACHE_L2X0
select PINCTRL
- select PM_GENERIC_DOMAINS if PM
+ select PM_GENERIC_DOMAINS if PM_RUNTIME
select S5P_DEV_MFC
help
Samsung EXYNOS4 SoCs based systems
@@ -46,10 +46,8 @@ config CPU_EXYNOS4210
default y
depends on ARCH_EXYNOS4
select ARCH_HAS_BANDGAP
- select ARM_CPU_SUSPEND if PM
+ select ARM_CPU_SUSPEND if PM_SLEEP
select PINCTRL_EXYNOS
- select S5P_PM if PM
- select S5P_SLEEP if PM
select SAMSUNG_DMADEV
help
Enable EXYNOS4210 CPU support
@@ -60,8 +58,6 @@ config SOC_EXYNOS4212
depends on ARCH_EXYNOS4
select ARCH_HAS_BANDGAP
select PINCTRL_EXYNOS
- select S5P_PM if PM
- select S5P_SLEEP if PM
select SAMSUNG_DMADEV
help
Enable EXYNOS4212 SoC support
@@ -82,9 +78,7 @@ config SOC_EXYNOS5250
depends on ARCH_EXYNOS5
select ARCH_HAS_BANDGAP
select PINCTRL_EXYNOS
- select PM_GENERIC_DOMAINS if PM
- select S5P_PM if PM
- select S5P_SLEEP if PM
+ select PM_GENERIC_DOMAINS if PM_RUNTIME
select S5P_DEV_MFC
select SAMSUNG_DMADEV
help
@@ -94,9 +88,7 @@ config SOC_EXYNOS5420
bool "SAMSUNG EXYNOS5420"
default y
depends on ARCH_EXYNOS5
- select PM_GENERIC_DOMAINS if PM
- select S5P_PM if PM
- select S5P_SLEEP if PM
+ select PM_GENERIC_DOMAINS if PM_RUNTIME
help
Enable EXYNOS5420 SoC support
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
index 8930b66b4abd..a656dbe3b78c 100644
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -12,9 +12,9 @@ obj- :=
# Core
-obj-$(CONFIG_ARCH_EXYNOS) += common.o
+obj-$(CONFIG_ARCH_EXYNOS) += exynos.o
-obj-$(CONFIG_S5P_PM) += pm.o
+obj-$(CONFIG_PM_SLEEP) += pm.o sleep.o
obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
@@ -29,8 +29,3 @@ obj-$(CONFIG_ARCH_EXYNOS) += firmware.o
plus_sec := $(call as-instr,.arch_extension sec,+sec)
AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec)
-
-# machine support
-
-obj-$(CONFIG_ARCH_EXYNOS4) += mach-exynos4-dt.o
-obj-$(CONFIG_ARCH_EXYNOS5) += mach-exynos5-dt.o
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
index f76967b1c551..9ef3f83efaff 100644
--- a/arch/arm/mach-exynos/common.h
+++ b/arch/arm/mach-exynos/common.h
@@ -19,14 +19,27 @@ void mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1);
struct map_desc;
void exynos_init_io(void);
-void exynos4_restart(enum reboot_mode mode, const char *cmd);
-void exynos5_restart(enum reboot_mode mode, const char *cmd);
+void exynos_restart(enum reboot_mode mode, const char *cmd);
void exynos_cpuidle_init(void);
void exynos_cpufreq_init(void);
void exynos_init_late(void);
void exynos_firmware_init(void);
+#ifdef CONFIG_PINCTRL_EXYNOS
+extern u32 exynos_get_eint_wake_mask(void);
+#else
+static inline u32 exynos_get_eint_wake_mask(void) { return 0xffffffff; }
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+extern void __init exynos_pm_init(void);
+#else
+static inline void exynos_pm_init(void) {}
+#endif
+
+extern void exynos_cpu_resume(void);
+
extern struct smp_operations exynos_smp_ops;
extern void exynos_cpu_die(unsigned int cpu);
diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c
index f57cb91f02aa..c57cae0e8779 100644
--- a/arch/arm/mach-exynos/cpuidle.c
+++ b/arch/arm/mach-exynos/cpuidle.c
@@ -14,6 +14,7 @@
#include <linux/cpu_pm.h>
#include <linux/io.h>
#include <linux/export.h>
+#include <linux/module.h>
#include <linux/time.h>
#include <linux/platform_device.h>
@@ -26,7 +27,6 @@
#include <plat/cpu.h>
#include <plat/pm.h>
-#include <mach/pm-core.h>
#include <mach/map.h>
#include "common.h"
@@ -127,7 +127,7 @@ static int exynos4_enter_core0_aftr(struct cpuidle_device *dev,
/* Set value of power down register for aftr mode */
exynos_sys_powerdown_conf(SYS_AFTR);
- __raw_writel(virt_to_phys(s3c_cpu_resume), REG_DIRECTGO_ADDR);
+ __raw_writel(virt_to_phys(exynos_cpu_resume), REG_DIRECTGO_ADDR);
__raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG);
save_cpu_arch_register();
diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/exynos.c
index f18be40e5b21..b32a907d021d 100644
--- a/arch/arm/mach-exynos/common.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -1,105 +1,40 @@
/*
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
+ * SAMSUNG EXYNOS Flattened Device Tree enabled machine
*
- * Common Codes for EXYNOS
+ * Copyright (c) 2010-2014 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 version 2 as
* published by the Free Software Foundation.
*/
-#include <linux/kernel.h>
-#include <linux/bitops.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/irqchip.h>
+#include <linux/init.h>
#include <linux/io.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-#include <clocksource/samsung_pwm.h>
-#include <linux/sched.h>
-#include <linux/serial_core.h>
+#include <linux/kernel.h>
+#include <linux/serial_s3c.h>
#include <linux/of.h>
-#include <linux/of_fdt.h>
-#include <linux/of_irq.h>
-#include <linux/pm_domain.h>
-#include <linux/export.h>
-#include <linux/irqdomain.h>
#include <linux/of_address.h>
-#include <linux/irqchip/arm-gic.h>
-#include <linux/irqchip/chained_irq.h>
+#include <linux/of_fdt.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
-#include <asm/proc-fns.h>
-#include <asm/exception.h>
+#include <asm/cacheflush.h>
#include <asm/hardware/cache-l2x0.h>
+#include <asm/mach/arch.h>
#include <asm/mach/map.h>
-#include <asm/mach/irq.h>
-#include <asm/cacheflush.h>
+#include <asm/memory.h>
#include <plat/cpu.h>
-#include <plat/pm.h>
-#include <plat/regs-serial.h>
#include "common.h"
+#include "mfc.h"
#include "regs-pmu.h"
#define L2_AUX_VAL 0x7C470001
#define L2_AUX_MASK 0xC200ffff
-static const char name_exynos4210[] = "EXYNOS4210";
-static const char name_exynos4212[] = "EXYNOS4212";
-static const char name_exynos4412[] = "EXYNOS4412";
-static const char name_exynos5250[] = "EXYNOS5250";
-static const char name_exynos5420[] = "EXYNOS5420";
-static const char name_exynos5440[] = "EXYNOS5440";
-
-static void exynos4_map_io(void);
-static void exynos5_map_io(void);
-static int exynos_init(void);
-
-static struct cpu_table cpu_ids[] __initdata = {
- {
- .idcode = EXYNOS4210_CPU_ID,
- .idmask = EXYNOS4_CPU_MASK,
- .map_io = exynos4_map_io,
- .init = exynos_init,
- .name = name_exynos4210,
- }, {
- .idcode = EXYNOS4212_CPU_ID,
- .idmask = EXYNOS4_CPU_MASK,
- .map_io = exynos4_map_io,
- .init = exynos_init,
- .name = name_exynos4212,
- }, {
- .idcode = EXYNOS4412_CPU_ID,
- .idmask = EXYNOS4_CPU_MASK,
- .map_io = exynos4_map_io,
- .init = exynos_init,
- .name = name_exynos4412,
- }, {
- .idcode = EXYNOS5250_SOC_ID,
- .idmask = EXYNOS5_SOC_MASK,
- .map_io = exynos5_map_io,
- .init = exynos_init,
- .name = name_exynos5250,
- }, {
- .idcode = EXYNOS5420_SOC_ID,
- .idmask = EXYNOS5_SOC_MASK,
- .map_io = exynos5_map_io,
- .init = exynos_init,
- .name = name_exynos5420,
- }, {
- .idcode = EXYNOS5440_SOC_ID,
- .idmask = EXYNOS5_SOC_MASK,
- .init = exynos_init,
- .name = name_exynos5440,
- },
-};
-
-/* Initial IO mappings */
-
static struct map_desc exynos4_iodesc[] __initdata = {
{
.virtual = (unsigned long)S3C_VA_SYS,
@@ -263,19 +198,11 @@ static struct map_desc exynos5_iodesc[] __initdata = {
},
};
-void exynos4_restart(enum reboot_mode mode, const char *cmd)
-{
- __raw_writel(0x1, S5P_SWRESET);
-}
-
-void exynos5_restart(enum reboot_mode mode, const char *cmd)
+void exynos_restart(enum reboot_mode mode, const char *cmd)
{
struct device_node *np;
- u32 val;
- void __iomem *addr;
-
- val = 0x1;
- addr = EXYNOS_SWRESET;
+ u32 val = 0x1;
+ void __iomem *addr = EXYNOS_SWRESET;
if (of_machine_is_compatible("samsung,exynos5440")) {
u32 status;
@@ -315,6 +242,7 @@ void __init exynos_init_late(void)
return;
pm_genpd_poweroff_unused();
+ exynos_pm_init();
}
static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname,
@@ -345,6 +273,28 @@ static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname,
*
* register the standard cpu IO areas
*/
+static void __init exynos_map_io(void)
+{
+ if (soc_is_exynos4())
+ iotable_init(exynos4_iodesc, ARRAY_SIZE(exynos4_iodesc));
+
+ if (soc_is_exynos5())
+ iotable_init(exynos5_iodesc, ARRAY_SIZE(exynos5_iodesc));
+
+ if (soc_is_exynos4210()) {
+ if (samsung_rev() == EXYNOS4210_REV_0)
+ iotable_init(exynos4_iodesc0,
+ ARRAY_SIZE(exynos4_iodesc0));
+ else
+ iotable_init(exynos4_iodesc1,
+ ARRAY_SIZE(exynos4_iodesc1));
+ iotable_init(exynos4210_iodesc, ARRAY_SIZE(exynos4210_iodesc));
+ }
+ if (soc_is_exynos4212() || soc_is_exynos4412())
+ iotable_init(exynos4x12_iodesc, ARRAY_SIZE(exynos4x12_iodesc));
+ if (soc_is_exynos5250())
+ iotable_init(exynos5250_iodesc, ARRAY_SIZE(exynos5250_iodesc));
+}
void __init exynos_init_io(void)
{
@@ -355,30 +305,7 @@ void __init exynos_init_io(void)
/* detect cpu id and rev. */
s5p_init_cpu(S5P_VA_CHIPID);
- s3c_init_cpu(samsung_cpu_id, cpu_ids, ARRAY_SIZE(cpu_ids));
-}
-
-static void __init exynos4_map_io(void)
-{
- iotable_init(exynos4_iodesc, ARRAY_SIZE(exynos4_iodesc));
-
- if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_0)
- iotable_init(exynos4_iodesc0, ARRAY_SIZE(exynos4_iodesc0));
- else
- iotable_init(exynos4_iodesc1, ARRAY_SIZE(exynos4_iodesc1));
-
- if (soc_is_exynos4210())
- iotable_init(exynos4210_iodesc, ARRAY_SIZE(exynos4210_iodesc));
- if (soc_is_exynos4212() || soc_is_exynos4412())
- iotable_init(exynos4x12_iodesc, ARRAY_SIZE(exynos4x12_iodesc));
-}
-
-static void __init exynos5_map_io(void)
-{
- iotable_init(exynos5_iodesc, ARRAY_SIZE(exynos5_iodesc));
-
- if (soc_is_exynos5250())
- iotable_init(exynos5250_iodesc, ARRAY_SIZE(exynos5250_iodesc));
+ exynos_map_io();
}
struct bus_type exynos_subsys = {
@@ -386,10 +313,6 @@ struct bus_type exynos_subsys = {
.dev_name = "exynos-core",
};
-static struct device exynos4_dev = {
- .bus = &exynos_subsys,
-};
-
static int __init exynos_core_init(void)
{
return subsys_system_register(&exynos_subsys, NULL);
@@ -404,15 +327,85 @@ static int __init exynos4_l2x0_cache_init(void)
if (ret)
return ret;
- l2x0_regs_phys = virt_to_phys(&l2x0_saved_regs);
- clean_dcache_area(&l2x0_regs_phys, sizeof(unsigned long));
+ if (IS_ENABLED(CONFIG_S5P_SLEEP)) {
+ l2x0_regs_phys = virt_to_phys(&l2x0_saved_regs);
+ clean_dcache_area(&l2x0_regs_phys, sizeof(unsigned long));
+ }
return 0;
}
early_initcall(exynos4_l2x0_cache_init);
-static int __init exynos_init(void)
+static void __init exynos_dt_machine_init(void)
{
- printk(KERN_INFO "EXYNOS: Initializing architecture\n");
+ struct device_node *i2c_np;
+ const char *i2c_compat = "samsung,s3c2440-i2c";
+ unsigned int tmp;
+ int id;
+
+ /*
+ * Exynos5's legacy i2c controller and new high speed i2c
+ * controller have muxed interrupt sources. By default the
+ * interrupts for 4-channel HS-I2C controller are enabled.
+ * If node for first four channels of legacy i2c controller
+ * are available then re-configure the interrupts via the
+ * system register.
+ */
+ if (soc_is_exynos5()) {
+ for_each_compatible_node(i2c_np, NULL, i2c_compat) {
+ if (of_device_is_available(i2c_np)) {
+ id = of_alias_get_id(i2c_np, "i2c");
+ if (id < 4) {
+ tmp = readl(EXYNOS5_SYS_I2C_CFG);
+ writel(tmp & ~(0x1 << id),
+ EXYNOS5_SYS_I2C_CFG);
+ }
+ }
+ }
+ }
- return device_register(&exynos4_dev);
+ exynos_cpuidle_init();
+ exynos_cpufreq_init();
+
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
+
+static char const *exynos_dt_compat[] __initconst = {
+ "samsung,exynos4",
+ "samsung,exynos4210",
+ "samsung,exynos4212",
+ "samsung,exynos4412",
+ "samsung,exynos5",
+ "samsung,exynos5250",
+ "samsung,exynos5420",
+ "samsung,exynos5440",
+ NULL
+};
+
+static void __init exynos_reserve(void)
+{
+#ifdef CONFIG_S5P_DEV_MFC
+ int i;
+ char *mfc_mem[] = {
+ "samsung,mfc-v5",
+ "samsung,mfc-v6",
+ "samsung,mfc-v7",
+ };
+
+ for (i = 0; i < ARRAY_SIZE(mfc_mem); i++)
+ if (of_scan_flat_dt(s5p_fdt_alloc_mfc_mem, mfc_mem[i]))
+ break;
+#endif
+}
+
+DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)")
+ /* Maintainer: Thomas Abraham <thomas.abraham@linaro.org> */
+ /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
+ .smp = smp_ops(exynos_smp_ops),
+ .map_io = exynos_init_io,
+ .init_early = exynos_firmware_init,
+ .init_machine = exynos_dt_machine_init,
+ .init_late = exynos_init_late,
+ .dt_compat = exynos_dt_compat,
+ .restart = exynos_restart,
+ .reserve = exynos_reserve,
+MACHINE_END
diff --git a/arch/arm/mach-exynos/include/mach/hardware.h b/arch/arm/mach-exynos/include/mach/hardware.h
deleted file mode 100644
index 5109eb232f23..000000000000
--- a/arch/arm/mach-exynos/include/mach/hardware.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/* linux/arch/arm/mach-exynos4/include/mach/hardware.h
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * EXYNOS4 - Hardware support
- *
- * 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_ARCH_HARDWARE_H
-#define __ASM_ARCH_HARDWARE_H __FILE__
-
-/* currently nothing here, placeholder */
-
-#endif /* __ASM_ARCH_HARDWARE_H */
diff --git a/arch/arm/mach-exynos/include/mach/pm-core.h b/arch/arm/mach-exynos/include/mach/pm-core.h
deleted file mode 100644
index dc0697c2fa92..000000000000
--- a/arch/arm/mach-exynos/include/mach/pm-core.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* linux/arch/arm/mach-exynos4/include/mach/pm-core.h
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Based on arch/arm/mach-s3c2410/include/mach/pm-core.h,
- * Copyright 2008 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- * http://armlinux.simtec.co.uk/
- *
- * EXYNOS4210 - PM core support for arch/arm/plat-s5p/pm.c
- *
- * 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_ARCH_PM_CORE_H
-#define __ASM_ARCH_PM_CORE_H __FILE__
-
-#include <linux/of.h>
-#include <mach/map.h>
-
-#define S5P_EINT_WAKEUP_MASK (S5P_VA_PMU + 0x0604)
-#define S5P_WAKEUP_MASK (S5P_VA_PMU + 0x0608)
-
-#ifdef CONFIG_PINCTRL_EXYNOS
-extern u32 exynos_get_eint_wake_mask(void);
-#else
-static inline u32 exynos_get_eint_wake_mask(void) { return 0xffffffff; }
-#endif
-
-static inline void s3c_pm_debug_init_uart(void)
-{
- /* nothing here yet */
-}
-
-static inline void s3c_pm_arch_prepare_irqs(void)
-{
- __raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK);
- __raw_writel(s3c_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK);
-}
-
-static inline void s3c_pm_arch_stop_clocks(void)
-{
- /* nothing here yet */
-}
-
-static inline void s3c_pm_arch_show_resume_irqs(void)
-{
- /* nothing here yet */
-}
-
-static inline void s3c_pm_arch_update_uart(void __iomem *regs,
- struct pm_uart_save *save)
-{
- /* nothing here yet */
-}
-
-static inline void s3c_pm_restored_gpios(void)
-{
- /* nothing here yet */
-}
-
-static inline void samsung_pm_saved_gpios(void)
-{
- /* nothing here yet */
-}
-
-/* Compatibility definitions to make plat-samsung/pm.c compile */
-#define IRQ_EINT_BIT(x) 1
-#define s3c_irqwake_intallow 0
-#define s3c_irqwake_eintallow 0
-
-#endif /* __ASM_ARCH_PM_CORE_H */
diff --git a/arch/arm/mach-exynos/include/mach/timex.h b/arch/arm/mach-exynos/include/mach/timex.h
deleted file mode 100644
index 6d138750a708..000000000000
--- a/arch/arm/mach-exynos/include/mach/timex.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* linux/arch/arm/mach-exynos4/include/mach/timex.h
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Copyright (c) 2003-2010 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * Based on arch/arm/mach-s5p6442/include/mach/timex.h
- *
- * EXYNOS4 - time parameters
- *
- * 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_ARCH_TIMEX_H
-#define __ASM_ARCH_TIMEX_H __FILE__
-
-/* CLOCK_TICK_RATE needs to be evaluatable by the cpp, so making it
- * a variable is useless. It seems as long as we make our timers an
- * exact multiple of HZ, any value that makes a 1->1 correspondence
- * for the time conversion functions to/from jiffies is acceptable.
-*/
-
-#define CLOCK_TICK_RATE 12000000
-
-#endif /* __ASM_ARCH_TIMEX_H */
diff --git a/arch/arm/mach-exynos/include/mach/uncompress.h b/arch/arm/mach-exynos/include/mach/uncompress.h
deleted file mode 100644
index 5d7ce36be46f..000000000000
--- a/arch/arm/mach-exynos/include/mach/uncompress.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * EXYNOS - uncompress code
- *
- * 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_ARCH_UNCOMPRESS_H
-#define __ASM_ARCH_UNCOMPRESS_H __FILE__
-
-#include <asm/mach-types.h>
-
-#include <mach/map.h>
-#include <plat/uncompress.h>
-
-static unsigned int __raw_readl(unsigned int ptr)
-{
- return *((volatile unsigned int *)ptr);
-}
-
-static void arch_detect_cpu(void)
-{
- u32 chip_id = __raw_readl(EXYNOS_PA_CHIPID);
-
- /*
- * product_id is bits 31:12
- * bits 23:20 describe the exynosX family
- * bits 27:24 describe the exynosX family in exynos5420
- */
- chip_id >>= 20;
-
- if ((chip_id & 0x0f) == 0x5 || (chip_id & 0xf0) == 0x50)
- uart_base = (volatile u8 *)EXYNOS5_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT);
- else
- uart_base = (volatile u8 *)EXYNOS4_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT);
-
- /*
- * For preventing FIFO overrun or infinite loop of UART console,
- * fifo_max should be the minimum fifo size of all of the UART channels
- */
- fifo_mask = S5PV210_UFSTAT_TXMASK;
- fifo_max = 15 << S5PV210_UFSTAT_TXSHIFT;
-}
-#endif /* __ASM_ARCH_UNCOMPRESS_H */
diff --git a/arch/arm/mach-exynos/mach-exynos4-dt.c b/arch/arm/mach-exynos/mach-exynos4-dt.c
deleted file mode 100644
index d3e54b7644d7..000000000000
--- a/arch/arm/mach-exynos/mach-exynos4-dt.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Samsung's EXYNOS4 flattened device tree enabled machine
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- * Copyright (c) 2010-2011 Linaro Ltd.
- * www.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/of_platform.h>
-#include <linux/of_fdt.h>
-
-#include <asm/mach/arch.h>
-#include <plat/mfc.h>
-
-#include "common.h"
-
-static void __init exynos4_dt_machine_init(void)
-{
- exynos_cpuidle_init();
- exynos_cpufreq_init();
-
- of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
-}
-
-static char const *exynos4_dt_compat[] __initdata = {
- "samsung,exynos4210",
- "samsung,exynos4212",
- "samsung,exynos4412",
- NULL
-};
-
-static void __init exynos4_reserve(void)
-{
-#ifdef CONFIG_S5P_DEV_MFC
- struct s5p_mfc_dt_meminfo mfc_mem;
-
- /* Reserve memory for MFC only if it's available */
- mfc_mem.compatible = "samsung,mfc-v5";
- if (of_scan_flat_dt(s5p_fdt_find_mfc_mem, &mfc_mem))
- s5p_mfc_reserve_mem(mfc_mem.roff, mfc_mem.rsize, mfc_mem.loff,
- mfc_mem.lsize);
-#endif
-}
-DT_MACHINE_START(EXYNOS4210_DT, "Samsung Exynos4 (Flattened Device Tree)")
- /* Maintainer: Thomas Abraham <thomas.abraham@linaro.org> */
- .smp = smp_ops(exynos_smp_ops),
- .map_io = exynos_init_io,
- .init_early = exynos_firmware_init,
- .init_machine = exynos4_dt_machine_init,
- .init_late = exynos_init_late,
- .dt_compat = exynos4_dt_compat,
- .restart = exynos4_restart,
- .reserve = exynos4_reserve,
-MACHINE_END
diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c
deleted file mode 100644
index 37ea261f0f6c..000000000000
--- a/arch/arm/mach-exynos/mach-exynos5-dt.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * SAMSUNG EXYNOS5250 Flattened Device Tree enabled machine
- *
- * Copyright (c) 2012 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 version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/of_platform.h>
-#include <linux/of_fdt.h>
-#include <linux/io.h>
-
-#include <asm/mach/arch.h>
-#include <plat/mfc.h>
-
-#include "common.h"
-#include "regs-pmu.h"
-
-static void __init exynos5_dt_machine_init(void)
-{
- struct device_node *i2c_np;
- const char *i2c_compat = "samsung,s3c2440-i2c";
- unsigned int tmp;
-
- /*
- * Exynos5's legacy i2c controller and new high speed i2c
- * controller have muxed interrupt sources. By default the
- * interrupts for 4-channel HS-I2C controller are enabled.
- * If node for first four channels of legacy i2c controller
- * are available then re-configure the interrupts via the
- * system register.
- */
- for_each_compatible_node(i2c_np, NULL, i2c_compat) {
- if (of_device_is_available(i2c_np)) {
- if (of_alias_get_id(i2c_np, "i2c") < 4) {
- tmp = readl(EXYNOS5_SYS_I2C_CFG);
- writel(tmp & ~(0x1 << of_alias_get_id(i2c_np, "i2c")),
- EXYNOS5_SYS_I2C_CFG);
- }
- }
- }
-
- exynos_cpuidle_init();
- exynos_cpufreq_init();
-
- of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
-}
-
-static char const *exynos5_dt_compat[] __initdata = {
- "samsung,exynos5250",
- "samsung,exynos5420",
- "samsung,exynos5440",
- NULL
-};
-
-static void __init exynos5_reserve(void)
-{
-#ifdef CONFIG_S5P_DEV_MFC
- struct s5p_mfc_dt_meminfo mfc_mem;
-
- /* Reserve memory for MFC only if it's available */
- mfc_mem.compatible = "samsung,mfc-v6";
- if (of_scan_flat_dt(s5p_fdt_find_mfc_mem, &mfc_mem))
- s5p_mfc_reserve_mem(mfc_mem.roff, mfc_mem.rsize, mfc_mem.loff,
- mfc_mem.lsize);
-#endif
-}
-
-DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)")
- /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
- .smp = smp_ops(exynos_smp_ops),
- .map_io = exynos_init_io,
- .init_machine = exynos5_dt_machine_init,
- .init_late = exynos_init_late,
- .dt_compat = exynos5_dt_compat,
- .restart = exynos5_restart,
- .reserve = exynos5_reserve,
-MACHINE_END
diff --git a/arch/arm/mach-exynos/mfc.h b/arch/arm/mach-exynos/mfc.h
new file mode 100644
index 000000000000..dec93cd5b3c6
--- /dev/null
+++ b/arch/arm/mach-exynos/mfc.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co.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.
+ */
+
+#ifndef __MACH_EXYNOS_MFC_H
+#define __MACH_EXYNOS_MFC_H __FILE__
+
+int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname,
+ int depth, void *data);
+
+#endif /* __MACH_EXYNOS_MFC_H */
diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c
index 8ea02f63fed9..03e5e9f94705 100644
--- a/arch/arm/mach-exynos/platsmp.c
+++ b/arch/arm/mach-exynos/platsmp.c
@@ -26,8 +26,6 @@
#include <asm/smp_scu.h>
#include <asm/firmware.h>
-#include <mach/hardware.h>
-
#include <plat/cpu.h>
#include "common.h"
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c
index e00025bbbe89..15af0ceb0a66 100644
--- a/arch/arm/mach-exynos/pm.c
+++ b/arch/arm/mach-exynos/pm.c
@@ -17,72 +17,33 @@
#include <linux/suspend.h>
#include <linux/syscore_ops.h>
#include <linux/io.h>
+#include <linux/irqchip/arm-gic.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <asm/cacheflush.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/smp_scu.h>
+#include <asm/suspend.h>
#include <plat/cpu.h>
-#include <plat/pm.h>
+#include <plat/pm-common.h>
#include <plat/pll.h>
#include <plat/regs-srom.h>
#include <mach/map.h>
-#include <mach/pm-core.h>
#include "common.h"
#include "regs-pmu.h"
-#define EXYNOS4_EPLL_LOCK (S5P_VA_CMU + 0x0C010)
-#define EXYNOS4_VPLL_LOCK (S5P_VA_CMU + 0x0C020)
-
-#define EXYNOS4_EPLL_CON0 (S5P_VA_CMU + 0x0C110)
-#define EXYNOS4_EPLL_CON1 (S5P_VA_CMU + 0x0C114)
-#define EXYNOS4_VPLL_CON0 (S5P_VA_CMU + 0x0C120)
-#define EXYNOS4_VPLL_CON1 (S5P_VA_CMU + 0x0C124)
-
-#define EXYNOS4_CLKSRC_MASK_TOP (S5P_VA_CMU + 0x0C310)
-#define EXYNOS4_CLKSRC_MASK_CAM (S5P_VA_CMU + 0x0C320)
-#define EXYNOS4_CLKSRC_MASK_TV (S5P_VA_CMU + 0x0C324)
-#define EXYNOS4_CLKSRC_MASK_LCD0 (S5P_VA_CMU + 0x0C334)
-#define EXYNOS4_CLKSRC_MASK_MAUDIO (S5P_VA_CMU + 0x0C33C)
-#define EXYNOS4_CLKSRC_MASK_FSYS (S5P_VA_CMU + 0x0C340)
-#define EXYNOS4_CLKSRC_MASK_PERIL0 (S5P_VA_CMU + 0x0C350)
-#define EXYNOS4_CLKSRC_MASK_PERIL1 (S5P_VA_CMU + 0x0C354)
-
-#define EXYNOS4_CLKSRC_MASK_DMC (S5P_VA_CMU + 0x10300)
-
-#define EXYNOS4_EPLLCON0_LOCKED_SHIFT (29)
-#define EXYNOS4_VPLLCON0_LOCKED_SHIFT (29)
-
-#define EXYNOS4210_CLKSRC_MASK_LCD1 (S5P_VA_CMU + 0x0C338)
-
-static const struct sleep_save exynos4_set_clksrc[] = {
- { .reg = EXYNOS4_CLKSRC_MASK_TOP , .val = 0x00000001, },
- { .reg = EXYNOS4_CLKSRC_MASK_CAM , .val = 0x11111111, },
- { .reg = EXYNOS4_CLKSRC_MASK_TV , .val = 0x00000111, },
- { .reg = EXYNOS4_CLKSRC_MASK_LCD0 , .val = 0x00001111, },
- { .reg = EXYNOS4_CLKSRC_MASK_MAUDIO , .val = 0x00000001, },
- { .reg = EXYNOS4_CLKSRC_MASK_FSYS , .val = 0x01011111, },
- { .reg = EXYNOS4_CLKSRC_MASK_PERIL0 , .val = 0x01111111, },
- { .reg = EXYNOS4_CLKSRC_MASK_PERIL1 , .val = 0x01110111, },
- { .reg = EXYNOS4_CLKSRC_MASK_DMC , .val = 0x00010000, },
-};
-
-static const struct sleep_save exynos4210_set_clksrc[] = {
- { .reg = EXYNOS4210_CLKSRC_MASK_LCD1 , .val = 0x00001111, },
-};
-
-static struct sleep_save exynos4_epll_save[] = {
- SAVE_ITEM(EXYNOS4_EPLL_CON0),
- SAVE_ITEM(EXYNOS4_EPLL_CON1),
-};
-
-static struct sleep_save exynos4_vpll_save[] = {
- SAVE_ITEM(EXYNOS4_VPLL_CON0),
- SAVE_ITEM(EXYNOS4_VPLL_CON1),
+/**
+ * struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping
+ * @hwirq: Hardware IRQ signal of the GIC
+ * @mask: Mask in PMU wake-up mask register
+ */
+struct exynos_wkup_irq {
+ unsigned int hwirq;
+ u32 mask;
};
static struct sleep_save exynos5_sys_save[] = {
@@ -98,6 +59,46 @@ static struct sleep_save exynos_core_save[] = {
SAVE_ITEM(S5P_SROM_BC3),
};
+/*
+ * GIC wake-up support
+ */
+
+static u32 exynos_irqwake_intmask = 0xffffffff;
+
+static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
+ { 76, BIT(1) }, /* RTC alarm */
+ { 77, BIT(2) }, /* RTC tick */
+ { /* sentinel */ },
+};
+
+static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
+ { 75, BIT(1) }, /* RTC alarm */
+ { 76, BIT(2) }, /* RTC tick */
+ { /* sentinel */ },
+};
+
+static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
+{
+ const struct exynos_wkup_irq *wkup_irq;
+
+ if (soc_is_exynos5250())
+ wkup_irq = exynos5250_wkup_irq;
+ else
+ wkup_irq = exynos4_wkup_irq;
+
+ while (wkup_irq->mask) {
+ if (wkup_irq->hwirq == data->hwirq) {
+ if (!state)
+ exynos_irqwake_intmask |= wkup_irq->mask;
+ else
+ exynos_irqwake_intmask &= ~wkup_irq->mask;
+ return 0;
+ }
+ ++wkup_irq;
+ }
+
+ return -ENOENT;
+}
/* For Cortex-A9 Diagnostic and Power control register */
static unsigned int save_arm_register[2];
@@ -122,12 +123,13 @@ static void exynos_pm_prepare(void)
{
unsigned int tmp;
+ /* Set wake-up mask registers */
+ __raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK);
+ __raw_writel(exynos_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK);
+
s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save));
- if (!soc_is_exynos5250()) {
- s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save));
- s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save));
- } else {
+ if (soc_is_exynos5250()) {
s3c_pm_do_save(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save));
/* Disable USE_RETENTION of JPEG_MEM_OPTION */
tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION);
@@ -142,127 +144,8 @@ static void exynos_pm_prepare(void)
/* ensure at least INFORM0 has the resume address */
- __raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0);
-
- /* Before enter central sequence mode, clock src register have to set */
-
- if (!soc_is_exynos5250())
- s3c_pm_do_restore_core(exynos4_set_clksrc, ARRAY_SIZE(exynos4_set_clksrc));
-
- if (soc_is_exynos4210())
- s3c_pm_do_restore_core(exynos4210_set_clksrc, ARRAY_SIZE(exynos4210_set_clksrc));
-
-}
-
-static int exynos_pm_add(struct device *dev, struct subsys_interface *sif)
-{
- pm_cpu_prep = exynos_pm_prepare;
- pm_cpu_sleep = exynos_cpu_suspend;
-
- return 0;
-}
-
-static unsigned long pll_base_rate;
-
-static void exynos4_restore_pll(void)
-{
- unsigned long pll_con, locktime, lockcnt;
- unsigned long pll_in_rate;
- unsigned int p_div, epll_wait = 0, vpll_wait = 0;
-
- if (pll_base_rate == 0)
- return;
-
- pll_in_rate = pll_base_rate;
-
- /* EPLL */
- pll_con = exynos4_epll_save[0].val;
-
- if (pll_con & (1 << 31)) {
- pll_con &= (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT);
- p_div = (pll_con >> PLL46XX_PDIV_SHIFT);
-
- pll_in_rate /= 1000000;
-
- locktime = (3000 / pll_in_rate) * p_div;
- lockcnt = locktime * 10000 / (10000 / pll_in_rate);
-
- __raw_writel(lockcnt, EXYNOS4_EPLL_LOCK);
-
- s3c_pm_do_restore_core(exynos4_epll_save,
- ARRAY_SIZE(exynos4_epll_save));
- epll_wait = 1;
- }
-
- pll_in_rate = pll_base_rate;
-
- /* VPLL */
- pll_con = exynos4_vpll_save[0].val;
-
- if (pll_con & (1 << 31)) {
- pll_in_rate /= 1000000;
- /* 750us */
- locktime = 750;
- lockcnt = locktime * 10000 / (10000 / pll_in_rate);
-
- __raw_writel(lockcnt, EXYNOS4_VPLL_LOCK);
-
- s3c_pm_do_restore_core(exynos4_vpll_save,
- ARRAY_SIZE(exynos4_vpll_save));
- vpll_wait = 1;
- }
-
- /* Wait PLL locking */
-
- do {
- if (epll_wait) {
- pll_con = __raw_readl(EXYNOS4_EPLL_CON0);
- if (pll_con & (1 << EXYNOS4_EPLLCON0_LOCKED_SHIFT))
- epll_wait = 0;
- }
-
- if (vpll_wait) {
- pll_con = __raw_readl(EXYNOS4_VPLL_CON0);
- if (pll_con & (1 << EXYNOS4_VPLLCON0_LOCKED_SHIFT))
- vpll_wait = 0;
- }
- } while (epll_wait || vpll_wait);
-}
-
-static struct subsys_interface exynos_pm_interface = {
- .name = "exynos_pm",
- .subsys = &exynos_subsys,
- .add_dev = exynos_pm_add,
-};
-
-static __init int exynos_pm_drvinit(void)
-{
- struct clk *pll_base;
- unsigned int tmp;
-
- if (soc_is_exynos5440())
- return 0;
-
- s3c_pm_init();
-
- /* All wakeup disable */
-
- tmp = __raw_readl(S5P_WAKEUP_MASK);
- tmp |= ((0xFF << 8) | (0x1F << 1));
- __raw_writel(tmp, S5P_WAKEUP_MASK);
-
- if (!soc_is_exynos5250()) {
- pll_base = clk_get(NULL, "xtal");
-
- if (!IS_ERR(pll_base)) {
- pll_base_rate = clk_get_rate(pll_base);
- clk_put(pll_base);
- }
- }
-
- return subsys_interface_register(&exynos_pm_interface);
+ __raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0);
}
-arch_initcall(exynos_pm_drvinit);
static int exynos_pm_suspend(void)
{
@@ -343,13 +226,8 @@ static void exynos_pm_resume(void)
s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
- if (!soc_is_exynos5250()) {
- exynos4_restore_pll();
-
-#ifdef CONFIG_SMP
+ if (IS_ENABLED(CONFIG_SMP) && !soc_is_exynos5250())
scu_enable(S5P_VA_SCU);
-#endif
- }
early_wakeup:
@@ -364,12 +242,80 @@ static struct syscore_ops exynos_pm_syscore_ops = {
.resume = exynos_pm_resume,
};
-static __init int exynos_pm_syscore_init(void)
+/*
+ * Suspend Ops
+ */
+
+static int exynos_suspend_enter(suspend_state_t state)
{
- if (soc_is_exynos5440())
- return 0;
+ int ret;
+
+ s3c_pm_debug_init();
+
+ S3C_PMDBG("%s: suspending the system...\n", __func__);
+
+ S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__,
+ exynos_irqwake_intmask, exynos_get_eint_wake_mask());
+
+ if (exynos_irqwake_intmask == -1U
+ && exynos_get_eint_wake_mask() == -1U) {
+ pr_err("%s: No wake-up sources!\n", __func__);
+ pr_err("%s: Aborting sleep\n", __func__);
+ return -EINVAL;
+ }
+
+ s3c_pm_save_uarts();
+ exynos_pm_prepare();
+ flush_cache_all();
+ s3c_pm_check_store();
+
+ ret = cpu_suspend(0, exynos_cpu_suspend);
+ if (ret)
+ return ret;
+
+ s3c_pm_restore_uarts();
+
+ S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
+ __raw_readl(S5P_WAKEUP_STAT));
+
+ s3c_pm_check_restore();
+
+ S3C_PMDBG("%s: resuming the system...\n", __func__);
- register_syscore_ops(&exynos_pm_syscore_ops);
return 0;
}
-arch_initcall(exynos_pm_syscore_init);
+
+static int exynos_suspend_prepare(void)
+{
+ s3c_pm_check_prepare();
+
+ return 0;
+}
+
+static void exynos_suspend_finish(void)
+{
+ s3c_pm_check_cleanup();
+}
+
+static const struct platform_suspend_ops exynos_suspend_ops = {
+ .enter = exynos_suspend_enter,
+ .prepare = exynos_suspend_prepare,
+ .finish = exynos_suspend_finish,
+ .valid = suspend_valid_only_mem,
+};
+
+void __init exynos_pm_init(void)
+{
+ u32 tmp;
+
+ /* Platform-specific GIC callback */
+ gic_arch_extn.irq_set_wake = exynos_irq_set_wake;
+
+ /* All wakeup disable */
+ tmp = __raw_readl(S5P_WAKEUP_MASK);
+ tmp |= ((0xFF << 8) | (0x1F << 1));
+ __raw_writel(tmp, S5P_WAKEUP_MASK);
+
+ register_syscore_ops(&exynos_pm_syscore_ops);
+ suspend_set_ops(&exynos_suspend_ops);
+}
diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c
index 8fd24882f0b1..fe6570ebbdde 100644
--- a/arch/arm/mach-exynos/pm_domains.c
+++ b/arch/arm/mach-exynos/pm_domains.c
@@ -22,8 +22,6 @@
#include <linux/of_platform.h>
#include <linux/sched.h>
-#include <plat/devs.h>
-
#include "regs-pmu.h"
/*
diff --git a/arch/arm/mach-exynos/regs-pmu.h b/arch/arm/mach-exynos/regs-pmu.h
index 7c029ce27711..4f6a2560d022 100644
--- a/arch/arm/mach-exynos/regs-pmu.h
+++ b/arch/arm/mach-exynos/regs-pmu.h
@@ -26,11 +26,12 @@
#define S5P_USE_STANDBY_WFI0 (1 << 16)
#define S5P_USE_STANDBY_WFE0 (1 << 24)
-#define S5P_SWRESET S5P_PMUREG(0x0400)
#define EXYNOS_SWRESET S5P_PMUREG(0x0400)
#define EXYNOS5440_SWRESET S5P_PMUREG(0x00C4)
#define S5P_WAKEUP_STAT S5P_PMUREG(0x0600)
+#define S5P_EINT_WAKEUP_MASK S5P_PMUREG(0x0604)
+#define S5P_WAKEUP_MASK S5P_PMUREG(0x0608)
#define S5P_INFORM0 S5P_PMUREG(0x0800)
#define S5P_INFORM1 S5P_PMUREG(0x0804)
diff --git a/arch/arm/mach-exynos/sleep.S b/arch/arm/mach-exynos/sleep.S
new file mode 100644
index 000000000000..a2613e944e10
--- /dev/null
+++ b/arch/arm/mach-exynos/sleep.S
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Exynos low-level resume code
+ *
+ * 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/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#define CPU_MASK 0xff0ffff0
+#define CPU_CORTEX_A9 0x410fc090
+
+ /*
+ * The following code is located into the .data section. This is to
+ * allow l2x0_regs_phys to be accessed with a relative load while we
+ * can't rely on any MMU translation. We could have put l2x0_regs_phys
+ * in the .text section as well, but some setups might insist on it to
+ * be truly read-only. (Reference from: arch/arm/kernel/sleep.S)
+ */
+ .data
+ .align
+
+ /*
+ * sleep magic, to allow the bootloader to check for an valid
+ * image to resume to. Must be the first word before the
+ * exynos_cpu_resume entry.
+ */
+
+ .word 0x2bedf00d
+
+ /*
+ * exynos_cpu_resume
+ *
+ * resume code entry for bootloader to call
+ */
+
+ENTRY(exynos_cpu_resume)
+#ifdef CONFIG_CACHE_L2X0
+ mrc p15, 0, r0, c0, c0, 0
+ ldr r1, =CPU_MASK
+ and r0, r0, r1
+ ldr r1, =CPU_CORTEX_A9
+ cmp r0, r1
+ bne skip_l2_resume
+ adr r0, l2x0_regs_phys
+ ldr r0, [r0]
+ cmp r0, #0
+ beq skip_l2_resume
+ ldr r1, [r0, #L2X0_R_PHY_BASE]
+ ldr r2, [r1, #L2X0_CTRL]
+ tst r2, #0x1
+ bne skip_l2_resume
+ ldr r2, [r0, #L2X0_R_AUX_CTRL]
+ str r2, [r1, #L2X0_AUX_CTRL]
+ ldr r2, [r0, #L2X0_R_TAG_LATENCY]
+ str r2, [r1, #L2X0_TAG_LATENCY_CTRL]
+ ldr r2, [r0, #L2X0_R_DATA_LATENCY]
+ str r2, [r1, #L2X0_DATA_LATENCY_CTRL]
+ ldr r2, [r0, #L2X0_R_PREFETCH_CTRL]
+ str r2, [r1, #L2X0_PREFETCH_CTRL]
+ ldr r2, [r0, #L2X0_R_PWR_CTRL]
+ str r2, [r1, #L2X0_POWER_CTRL]
+ mov r2, #1
+ str r2, [r1, #L2X0_CTRL]
+skip_l2_resume:
+#endif
+ b cpu_resume
+ENDPROC(exynos_cpu_resume)
+#ifdef CONFIG_CACHE_L2X0
+ .globl l2x0_regs_phys
+l2x0_regs_phys:
+ .long 0
+#endif
diff --git a/arch/arm/mach-footbridge/Kconfig b/arch/arm/mach-footbridge/Kconfig
index fba55fb9f47d..07152d00fc50 100644
--- a/arch/arm/mach-footbridge/Kconfig
+++ b/arch/arm/mach-footbridge/Kconfig
@@ -52,6 +52,7 @@ config ARCH_EBSA285_HOST
select FOOTBRIDGE_HOST
select ISA
select ISA_DMA
+ select ARCH_MAY_HAVE_PC_FDC
select PCI
help
Say Y here if you intend to run this kernel on the EBSA285 card
@@ -94,6 +95,5 @@ config FOOTBRIDGE_ADDIN
# EBSA285 board in either host or addin mode
config ARCH_EBSA285
bool
- select ARCH_MAY_HAVE_PC_FDC
endif
diff --git a/arch/arm/mach-footbridge/Makefile b/arch/arm/mach-footbridge/Makefile
index 0b64dd430d61..c3faa3bc84dd 100644
--- a/arch/arm/mach-footbridge/Makefile
+++ b/arch/arm/mach-footbridge/Makefile
@@ -4,11 +4,12 @@
# Object file lists.
-obj-y := common.o dc21285.o dma.o isa-irq.o
+obj-y := common.o dma.o isa-irq.o
obj-m :=
obj-n :=
obj- :=
+pci-y += dc21285.o
pci-$(CONFIG_ARCH_CATS) += cats-pci.o
pci-$(CONFIG_ARCH_EBSA285_HOST) += ebsa285-pci.o
pci-$(CONFIG_ARCH_NETWINDER) += netwinder-pci.o
diff --git a/arch/arm/mach-footbridge/cats-hw.c b/arch/arm/mach-footbridge/cats-hw.c
index 9669cc0b6318..da0415094856 100644
--- a/arch/arm/mach-footbridge/cats-hw.c
+++ b/arch/arm/mach-footbridge/cats-hw.c
@@ -78,9 +78,11 @@ __initcall(cats_hw_init);
static void __init
fixup_cats(struct tag *tags, char **cmdline, struct meminfo *mi)
{
+#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
screen_info.orig_video_lines = 25;
screen_info.orig_video_points = 16;
screen_info.orig_y = 24;
+#endif
}
MACHINE_START(CATS, "Chalice-CATS")
diff --git a/arch/arm/mach-footbridge/dc21285-timer.c b/arch/arm/mach-footbridge/dc21285-timer.c
index 3971104d32d4..bf7aa7d298e7 100644
--- a/arch/arm/mach-footbridge/dc21285-timer.c
+++ b/arch/arm/mach-footbridge/dc21285-timer.c
@@ -105,7 +105,7 @@ static irqreturn_t timer1_interrupt(int irq, void *dev_id)
static struct irqaction footbridge_timer_irq = {
.name = "dc21285_timer1",
.handler = timer1_interrupt,
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.dev_id = &ckevt_dc21285,
};
@@ -125,7 +125,7 @@ void __init footbridge_timer_init(void)
clockevents_config_and_register(ce, rate, 0x4, 0xffffff);
}
-static u32 notrace footbridge_read_sched_clock(void)
+static u64 notrace footbridge_read_sched_clock(void)
{
return ~*CSR_TIMER3_VALUE;
}
@@ -138,5 +138,5 @@ void __init footbridge_sched_clock(void)
*CSR_TIMER3_CLR = 0;
*CSR_TIMER3_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV16;
- setup_sched_clock(footbridge_read_sched_clock, 24, rate);
+ sched_clock_register(footbridge_read_sched_clock, 24, rate);
}
diff --git a/arch/arm/mach-footbridge/dc21285.c b/arch/arm/mach-footbridge/dc21285.c
index 7c2fdae9a38b..96a3d73ef4bf 100644
--- a/arch/arm/mach-footbridge/dc21285.c
+++ b/arch/arm/mach-footbridge/dc21285.c
@@ -334,15 +334,15 @@ void __init dc21285_preinit(void)
/*
* We don't care if these fail.
*/
- dc21285_request_irq(IRQ_PCI_SERR, dc21285_serr_irq, IRQF_DISABLED,
+ dc21285_request_irq(IRQ_PCI_SERR, dc21285_serr_irq, 0,
"PCI system error", &serr_timer);
- dc21285_request_irq(IRQ_PCI_PERR, dc21285_parity_irq, IRQF_DISABLED,
+ dc21285_request_irq(IRQ_PCI_PERR, dc21285_parity_irq, 0,
"PCI parity error", &perr_timer);
- dc21285_request_irq(IRQ_PCI_ABORT, dc21285_abort_irq, IRQF_DISABLED,
+ dc21285_request_irq(IRQ_PCI_ABORT, dc21285_abort_irq, 0,
"PCI abort", NULL);
- dc21285_request_irq(IRQ_DISCARD_TIMER, dc21285_discard_irq, IRQF_DISABLED,
+ dc21285_request_irq(IRQ_DISCARD_TIMER, dc21285_discard_irq, 0,
"Discard timer", NULL);
- dc21285_request_irq(IRQ_PCI_DPERR, dc21285_dparity_irq, IRQF_DISABLED,
+ dc21285_request_irq(IRQ_PCI_DPERR, dc21285_dparity_irq, 0,
"PCI data parity", NULL);
if (cfn_mode) {
diff --git a/arch/arm/mach-footbridge/include/mach/timex.h b/arch/arm/mach-footbridge/include/mach/timex.h
deleted file mode 100644
index d0fea9d6d4ab..000000000000
--- a/arch/arm/mach-footbridge/include/mach/timex.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * arch/arm/mach-footbridge/include/mach/timex.h
- *
- * Copyright (C) 1998 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * EBSA285 architecture timex specifications
- */
-
-/*
- * We assume a constant here; this satisfies the maths in linux/timex.h
- * and linux/time.h. CLOCK_TICK_RATE is actually system dependent, but
- * this must be a constant.
- */
-#define CLOCK_TICK_RATE (50000000/16)
diff --git a/arch/arm/mach-footbridge/isa-timer.c b/arch/arm/mach-footbridge/isa-timer.c
index d9301dd56354..b73f52e196b9 100644
--- a/arch/arm/mach-footbridge/isa-timer.c
+++ b/arch/arm/mach-footbridge/isa-timer.c
@@ -27,7 +27,7 @@ static irqreturn_t pit_timer_interrupt(int irq, void *dev_id)
static struct irqaction pit_timer_irq = {
.name = "pit",
.handler = pit_timer_interrupt,
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.dev_id = &i8253_clockevent,
};
diff --git a/arch/arm/mach-gemini/idle.c b/arch/arm/mach-gemini/idle.c
index 87dff4f5059e..ddf8ec9d203b 100644
--- a/arch/arm/mach-gemini/idle.c
+++ b/arch/arm/mach-gemini/idle.c
@@ -3,7 +3,7 @@
*/
#include <linux/init.h>
-#include <asm/system.h>
+#include <asm/system_misc.h>
#include <asm/proc-fns.h>
static void gemini_idle(void)
diff --git a/arch/arm/mach-gemini/include/mach/timex.h b/arch/arm/mach-gemini/include/mach/timex.h
deleted file mode 100644
index dc5690ba975c..000000000000
--- a/arch/arm/mach-gemini/include/mach/timex.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Gemini timex specifications
- *
- * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
- *
- * 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.
- */
-
-/* When AHB bus frequency is 150MHz */
-#define CLOCK_TICK_RATE 38000000
diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig
index 0aded64a9ebc..830b76e70250 100644
--- a/arch/arm/mach-highbank/Kconfig
+++ b/arch/arm/mach-highbank/Kconfig
@@ -5,7 +5,6 @@ config ARCH_HIGHBANK
select ARCH_HAS_HOLES_MEMORYMODEL
select ARCH_HAS_OPP
select ARCH_SUPPORTS_BIG_ENDIAN
- select ARCH_WANT_OPTIONAL_GPIOLIB
select ARM_AMBA
select ARM_ERRATA_764369 if SMP
select ARM_ERRATA_775420
@@ -14,14 +13,8 @@ config ARCH_HIGHBANK
select ARM_PSCI
select ARM_TIMER_SP804
select CACHE_L2X0
- select COMMON_CLK
- select CPU_V7
- select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU
select HAVE_ARM_TWD if SMP
- select HAVE_SMP
select MAILBOX
select PL320_MBOX
- select SPARSE_IRQ
- select USE_OF
select ZONE_DMA if ARM_LPAE
diff --git a/arch/arm/mach-hisi/Kconfig b/arch/arm/mach-hisi/Kconfig
index 1abae5f6a418..feee4dbb0760 100644
--- a/arch/arm/mach-hisi/Kconfig
+++ b/arch/arm/mach-hisi/Kconfig
@@ -3,13 +3,9 @@ config ARCH_HI3xxx
select ARM_AMBA
select ARM_GIC
select ARM_TIMER_SP804
- select ARCH_WANT_OPTIONAL_GPIOLIB
select CACHE_L2X0
- select CLKSRC_OF
- select GENERIC_CLOCKEVENTS
- select HAVE_ARM_SCU
+ select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
- select HAVE_SMP
select PINCTRL
select PINCTRL_SINGLE
help
diff --git a/arch/arm/mach-hisi/Makefile b/arch/arm/mach-hisi/Makefile
index 6870058d0a48..2ae1b59267c2 100644
--- a/arch/arm/mach-hisi/Makefile
+++ b/arch/arm/mach-hisi/Makefile
@@ -3,5 +3,4 @@
#
obj-y += hisilicon.o
-obj-$(CONFIG_SMP) += platsmp.o
-obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+obj-$(CONFIG_SMP) += platsmp.o hotplug.o
diff --git a/arch/arm/mach-hisi/hotplug.c b/arch/arm/mach-hisi/hotplug.c
index b909854eee7f..abd441b0c604 100644
--- a/arch/arm/mach-hisi/hotplug.c
+++ b/arch/arm/mach-hisi/hotplug.c
@@ -178,6 +178,7 @@ static inline void cpu_enter_lowpower(void)
: "cc");
}
+#ifdef CONFIG_HOTPLUG_CPU
void hi3xxx_cpu_die(unsigned int cpu)
{
cpu_enter_lowpower();
@@ -198,3 +199,4 @@ int hi3xxx_cpu_kill(unsigned int cpu)
hi3xxx_set_cpu(cpu, false);
return 1;
}
+#endif
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 33567aa5880f..5740296dc429 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -1,19 +1,15 @@
config ARCH_MXC
bool "Freescale i.MX family" if ARCH_MULTI_V4_V5 || ARCH_MULTI_V6_V7
+ select ARCH_HAS_CPUFREQ
+ select ARCH_HAS_OPP
select ARCH_REQUIRE_GPIOLIB
select ARM_CPU_SUSPEND if PM
- select ARM_PATCH_PHYS_VIRT
select CLKSRC_MMIO
- select COMMON_CLK
- select GENERIC_ALLOCATOR
- select GENERIC_CLOCKEVENTS
select GENERIC_IRQ_CHIP
- select MIGHT_HAVE_CACHE_L2X0 if ARCH_MULTI_V6_V7
- select MULTI_IRQ_HANDLER
select PINCTRL
+ select PM_OPP if PM
select SOC_BUS
- select SPARSE_IRQ
- select USE_OF
+ select SRAM
help
Support for Freescale MXC/iMX-based family of processors
@@ -121,18 +117,16 @@ config SOC_IMX31
config SOC_IMX35
bool
select ARCH_MXC_IOMUX_V3
- select CPU_V6K
select HAVE_EPIT
select MXC_AVIC
+ select PINCTRL_IMX35
select SMP_ON_UP if SMP
- select PINCTRL
config SOC_IMX5
bool
select ARCH_HAS_CPUFREQ
select ARCH_HAS_OPP
select ARCH_MXC_IOMUX_V3
- select CPU_V7
select MXC_TZIC
config SOC_IMX51
@@ -777,65 +771,50 @@ config SOC_IMX50
config SOC_IMX53
bool "i.MX53 support"
select HAVE_IMX_SRC
- select IMX_HAVE_PLATFORM_IMX2_WDT
select PINCTRL_IMX53
select SOC_IMX5
help
This enables support for Freescale i.MX53 processor.
-config SOC_IMX6Q
- bool "i.MX6 Quad/DualLite support"
- select ARCH_HAS_CPUFREQ
- select ARCH_HAS_OPP
+config SOC_IMX6
+ bool
select ARM_ERRATA_754322
- select ARM_ERRATA_764369 if SMP
select ARM_ERRATA_775420
select ARM_GIC
- select CPU_V7
- select HAVE_ARM_SCU if SMP
- select HAVE_ARM_TWD if SMP
select HAVE_IMX_ANATOP
select HAVE_IMX_GPC
select HAVE_IMX_MMDC
select HAVE_IMX_SRC
- select HAVE_SMP
select MFD_SYSCON
- select MIGHT_HAVE_PCI
- select PCI_DOMAINS if PCI
- select PINCTRL_IMX6Q
select PL310_ERRATA_588369 if CACHE_PL310
select PL310_ERRATA_727915 if CACHE_PL310
select PL310_ERRATA_769419 if CACHE_PL310
- select PM_OPP if PM
+
+config SOC_IMX6Q
+ bool "i.MX6 Quad/DualLite support"
+ select ARM_ERRATA_764369 if SMP
+ select HAVE_ARM_SCU if SMP
+ select HAVE_ARM_TWD if SMP
+ select MIGHT_HAVE_PCI
+ select PCI_DOMAINS if PCI
+ select PINCTRL_IMX6Q
+ select SOC_IMX6
help
This enables support for Freescale i.MX6 Quad processor.
config SOC_IMX6SL
bool "i.MX6 SoloLite support"
- select ARM_ERRATA_754322
- select ARM_ERRATA_775420
- select ARM_GIC
- select CPU_V7
- select HAVE_IMX_ANATOP
- select HAVE_IMX_GPC
- select HAVE_IMX_MMDC
- select HAVE_IMX_SRC
- select MFD_SYSCON
select PINCTRL_IMX6SL
- select PL310_ERRATA_588369 if CACHE_PL310
- select PL310_ERRATA_727915 if CACHE_PL310
- select PL310_ERRATA_769419 if CACHE_PL310
+ select SOC_IMX6
help
This enables support for Freescale i.MX6 SoloLite processor.
config SOC_VF610
bool "Vybrid Family VF610 support"
- select CPU_V7
select ARM_GIC
- select CLKSRC_OF
select PINCTRL_VF610
select VF_PIT_TIMER
select PL310_ERRATA_588369 if CACHE_PL310
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index ec419649320f..f4ed83032dd0 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o
ifeq ($(CONFIG_CPU_IDLE),y)
obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o
obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o
+obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o
endif
ifdef CONFIG_SND_IMX_SOC
@@ -101,9 +102,11 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o mach-imx6q.o
obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o
-obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o
-# i.MX6SL reuses i.MX6Q code
-obj-$(CONFIG_SOC_IMX6SL) += pm-imx6q.o headsmp.o
+ifeq ($(CONFIG_SUSPEND),y)
+AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
+obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
+endif
+obj-$(CONFIG_SOC_IMX6) += pm-imx6.o
# i.MX5 based machines
obj-$(CONFIG_MACH_MX51_BABBAGE) += mach-mx51_babbage.o
diff --git a/arch/arm/mach-imx/clk-imx21.c b/arch/arm/mach-imx/clk-imx21.c
index d7ed66091a2a..bdc2e4630a08 100644
--- a/arch/arm/mach-imx/clk-imx21.c
+++ b/arch/arm/mach-imx/clk-imx21.c
@@ -149,7 +149,6 @@ int __init mx21_clocks_init(unsigned long lref, unsigned long href)
clk_register_clkdev(clk[per1], "per", "imx-gpt.1");
clk_register_clkdev(clk[gpt3_ipg_gate], "ipg", "imx-gpt.2");
clk_register_clkdev(clk[per1], "per", "imx-gpt.2");
- clk_register_clkdev(clk[pwm_ipg_gate], "pwm", "mxc_pwm.0");
clk_register_clkdev(clk[per2], "per", "imx21-cspi.0");
clk_register_clkdev(clk[cspi1_ipg_gate], "ipg", "imx21-cspi.0");
clk_register_clkdev(clk[per2], "per", "imx21-cspi.1");
diff --git a/arch/arm/mach-imx/clk-imx25.c b/arch/arm/mach-imx/clk-imx25.c
index 69858c78f40d..dc36e6c2f1da 100644
--- a/arch/arm/mach-imx/clk-imx25.c
+++ b/arch/arm/mach-imx/clk-imx25.c
@@ -265,14 +265,6 @@ int __init mx25_clocks_init(void)
clk_register_clkdev(clk[cspi1_ipg], NULL, "imx35-cspi.0");
clk_register_clkdev(clk[cspi2_ipg], NULL, "imx35-cspi.1");
clk_register_clkdev(clk[cspi3_ipg], NULL, "imx35-cspi.2");
- clk_register_clkdev(clk[pwm1_ipg], "ipg", "mxc_pwm.0");
- clk_register_clkdev(clk[per10], "per", "mxc_pwm.0");
- clk_register_clkdev(clk[pwm1_ipg], "ipg", "mxc_pwm.1");
- clk_register_clkdev(clk[per10], "per", "mxc_pwm.1");
- clk_register_clkdev(clk[pwm1_ipg], "ipg", "mxc_pwm.2");
- clk_register_clkdev(clk[per10], "per", "mxc_pwm.2");
- clk_register_clkdev(clk[pwm1_ipg], "ipg", "mxc_pwm.3");
- clk_register_clkdev(clk[per10], "per", "mxc_pwm.3");
clk_register_clkdev(clk[kpp_ipg], NULL, "imx-keypad");
clk_register_clkdev(clk[tsc_ipg], NULL, "mx25-adc");
clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx21-i2c.0");
diff --git a/arch/arm/mach-imx/clk-imx27.c b/arch/arm/mach-imx/clk-imx27.c
index c6b40f386786..d2da8908b268 100644
--- a/arch/arm/mach-imx/clk-imx27.c
+++ b/arch/arm/mach-imx/clk-imx27.c
@@ -231,7 +231,6 @@ int __init mx27_clocks_init(unsigned long fref)
clk_register_clkdev(clk[per1_gate], "per", "imx-gpt.4");
clk_register_clkdev(clk[gpt6_ipg_gate], "ipg", "imx-gpt.5");
clk_register_clkdev(clk[per1_gate], "per", "imx-gpt.5");
- clk_register_clkdev(clk[pwm_ipg_gate], NULL, "mxc_pwm.0");
clk_register_clkdev(clk[per2_gate], "per", "imx21-mmc.0");
clk_register_clkdev(clk[sdhc1_ipg_gate], "ipg", "imx21-mmc.0");
clk_register_clkdev(clk[per2_gate], "per", "imx21-mmc.1");
diff --git a/arch/arm/mach-imx/clk-imx51-imx53.c b/arch/arm/mach-imx/clk-imx51-imx53.c
index 19fca1fdc6fe..568ef0a4de84 100644
--- a/arch/arm/mach-imx/clk-imx51-imx53.c
+++ b/arch/arm/mach-imx/clk-imx51-imx53.c
@@ -266,8 +266,6 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil,
clk_register_clkdev(clk[IMX5_CLK_ECSPI2_PER_GATE], "per", "imx51-ecspi.1");
clk_register_clkdev(clk[IMX5_CLK_ECSPI2_IPG_GATE], "ipg", "imx51-ecspi.1");
clk_register_clkdev(clk[IMX5_CLK_CSPI_IPG_GATE], NULL, "imx35-cspi.2");
- clk_register_clkdev(clk[IMX5_CLK_PWM1_IPG_GATE], "pwm", "mxc_pwm.0");
- clk_register_clkdev(clk[IMX5_CLK_PWM2_IPG_GATE], "pwm", "mxc_pwm.1");
clk_register_clkdev(clk[IMX5_CLK_I2C1_GATE], NULL, "imx21-i2c.0");
clk_register_clkdev(clk[IMX5_CLK_I2C2_GATE], NULL, "imx21-i2c.1");
clk_register_clkdev(clk[IMX5_CLK_USBOH3_PER_GATE], "per", "mxc-ehci.0");
diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index 4d677f442539..b0e7f9d2c245 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -437,12 +437,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk_register_clkdev(clk[gpt_ipg], "ipg", "imx-gpt.0");
clk_register_clkdev(clk[gpt_ipg_per], "per", "imx-gpt.0");
- clk_register_clkdev(clk[cko1_sel], "cko1_sel", NULL);
- clk_register_clkdev(clk[ahb], "ahb", NULL);
- clk_register_clkdev(clk[cko1], "cko1", NULL);
- clk_register_clkdev(clk[arm], NULL, "cpu0");
- clk_register_clkdev(clk[pll4_post_div], "pll4_post_div", NULL);
- clk_register_clkdev(clk[pll4_audio], "pll4_audio", NULL);
+ clk_register_clkdev(clk[enet_ref], "enet_ref", NULL);
if ((imx_get_soc_revision() != IMX_CHIP_REVISION_1_0) ||
cpu_is_imx6dl()) {
diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c
index 4c86f3035205..f7073c0782fb 100644
--- a/arch/arm/mach-imx/clk-imx6sl.c
+++ b/arch/arm/mach-imx/clk-imx6sl.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Freescale Semiconductor, Inc.
+ * Copyright 2013-2014 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 version 2 as
@@ -18,27 +18,43 @@
#include "clk.h"
#include "common.h"
-static const char const *step_sels[] = { "osc", "pll2_pfd2", };
-static const char const *pll1_sw_sels[] = { "pll1_sys", "step", };
-static const char const *ocram_alt_sels[] = { "pll2_pfd2", "pll3_pfd1", };
-static const char const *ocram_sels[] = { "periph", "ocram_alt_sels", };
-static const char const *pre_periph_sels[] = { "pll2_bus", "pll2_pfd2", "pll2_pfd0", "pll2_198m", };
-static const char const *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", };
-static const char const *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", };
-static const char const *periph_sels[] = { "pre_periph_sel", "periph_clk2_podf", };
-static const char const *periph2_sels[] = { "pre_periph2_sel", "periph2_clk2_podf", };
-static const char const *csi_lcdif_sels[] = { "mmdc", "pll2_pfd2", "pll3_120m", "pll3_pfd1", };
-static const char const *usdhc_sels[] = { "pll2_pfd2", "pll2_pfd0", };
-static const char const *ssi_sels[] = { "pll3_pfd2", "pll3_pfd3", "pll4_audio_div", "dummy", };
-static const char const *perclk_sels[] = { "ipg", "osc", };
-static const char const *epdc_pxp_sels[] = { "mmdc", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd2", "pll3_pfd1", };
-static const char const *gpu2d_ovg_sels[] = { "pll3_pfd1", "pll3_usb_otg", "pll2_bus", "pll2_pfd2", };
-static const char const *gpu2d_sels[] = { "pll2_pfd2", "pll3_usb_otg", "pll3_pfd1", "pll2_bus", };
-static const char const *lcdif_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll3_pfd0", "pll3_pfd1", };
-static const char const *epdc_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd1", "pll3_pfd1", };
-static const char const *audio_sels[] = { "pll4_audio_div", "pll3_pfd2", "pll3_pfd3", "pll3_usb_otg", };
-static const char const *ecspi_sels[] = { "pll3_60m", "osc", };
-static const char const *uart_sels[] = { "pll3_80m", "osc", };
+#define CCSR 0xc
+#define BM_CCSR_PLL1_SW_CLK_SEL (1 << 2)
+#define CACRR 0x10
+#define CDHIPR 0x48
+#define BM_CDHIPR_ARM_PODF_BUSY (1 << 16)
+#define ARM_WAIT_DIV_396M 2
+#define ARM_WAIT_DIV_792M 4
+#define ARM_WAIT_DIV_996M 6
+
+#define PLL_ARM 0x0
+#define BM_PLL_ARM_DIV_SELECT (0x7f << 0)
+#define BM_PLL_ARM_POWERDOWN (1 << 12)
+#define BM_PLL_ARM_ENABLE (1 << 13)
+#define BM_PLL_ARM_LOCK (1 << 31)
+#define PLL_ARM_DIV_792M 66
+
+static const char *step_sels[] = { "osc", "pll2_pfd2", };
+static const char *pll1_sw_sels[] = { "pll1_sys", "step", };
+static const char *ocram_alt_sels[] = { "pll2_pfd2", "pll3_pfd1", };
+static const char *ocram_sels[] = { "periph", "ocram_alt_sels", };
+static const char *pre_periph_sels[] = { "pll2_bus", "pll2_pfd2", "pll2_pfd0", "pll2_198m", };
+static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", };
+static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", };
+static const char *periph_sels[] = { "pre_periph_sel", "periph_clk2_podf", };
+static const char *periph2_sels[] = { "pre_periph2_sel", "periph2_clk2_podf", };
+static const char *csi_lcdif_sels[] = { "mmdc", "pll2_pfd2", "pll3_120m", "pll3_pfd1", };
+static const char *usdhc_sels[] = { "pll2_pfd2", "pll2_pfd0", };
+static const char *ssi_sels[] = { "pll3_pfd2", "pll3_pfd3", "pll4_audio_div", "dummy", };
+static const char *perclk_sels[] = { "ipg", "osc", };
+static const char *epdc_pxp_sels[] = { "mmdc", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd2", "pll3_pfd1", };
+static const char *gpu2d_ovg_sels[] = { "pll3_pfd1", "pll3_usb_otg", "pll2_bus", "pll2_pfd2", };
+static const char *gpu2d_sels[] = { "pll2_pfd2", "pll3_usb_otg", "pll3_pfd1", "pll2_bus", };
+static const char *lcdif_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll3_pfd0", "pll3_pfd1", };
+static const char *epdc_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd1", "pll3_pfd1", };
+static const char *audio_sels[] = { "pll4_audio_div", "pll3_pfd2", "pll3_pfd3", "pll3_usb_otg", };
+static const char *ecspi_sels[] = { "pll3_60m", "osc", };
+static const char *uart_sels[] = { "pll3_80m", "osc", };
static struct clk_div_table clk_enet_ref_table[] = {
{ .val = 0, .div = 20, },
@@ -65,6 +81,89 @@ static struct clk_div_table video_div_table[] = {
static struct clk *clks[IMX6SL_CLK_END];
static struct clk_onecell_data clk_data;
+static void __iomem *ccm_base;
+static void __iomem *anatop_base;
+
+static const u32 clks_init_on[] __initconst = {
+ IMX6SL_CLK_IPG, IMX6SL_CLK_ARM, IMX6SL_CLK_MMDC_ROOT,
+};
+
+/*
+ * ERR005311 CCM: After exit from WAIT mode, unwanted interrupt(s) taken
+ * during WAIT mode entry process could cause cache memory
+ * corruption.
+ *
+ * Software workaround:
+ * To prevent this issue from occurring, software should ensure that the
+ * ARM to IPG clock ratio is less than 12:5 (that is < 2.4x), before
+ * entering WAIT mode.
+ *
+ * This function will set the ARM clk to max value within the 12:5 limit.
+ * As IPG clock is fixed at 66MHz(so ARM freq must not exceed 158.4MHz),
+ * ARM freq are one of below setpoints: 396MHz, 792MHz and 996MHz, since
+ * the clk APIs can NOT be called in idle thread(may cause kernel schedule
+ * as there is sleep function in PLL wait function), so here we just slow
+ * down ARM to below freq according to previous freq:
+ *
+ * run mode wait mode
+ * 396MHz -> 132MHz;
+ * 792MHz -> 158.4MHz;
+ * 996MHz -> 142.3MHz;
+ */
+static int imx6sl_get_arm_divider_for_wait(void)
+{
+ if (readl_relaxed(ccm_base + CCSR) & BM_CCSR_PLL1_SW_CLK_SEL) {
+ return ARM_WAIT_DIV_396M;
+ } else {
+ if ((readl_relaxed(anatop_base + PLL_ARM) &
+ BM_PLL_ARM_DIV_SELECT) == PLL_ARM_DIV_792M)
+ return ARM_WAIT_DIV_792M;
+ else
+ return ARM_WAIT_DIV_996M;
+ }
+}
+
+static void imx6sl_enable_pll_arm(bool enable)
+{
+ static u32 saved_pll_arm;
+ u32 val;
+
+ if (enable) {
+ saved_pll_arm = val = readl_relaxed(anatop_base + PLL_ARM);
+ val |= BM_PLL_ARM_ENABLE;
+ val &= ~BM_PLL_ARM_POWERDOWN;
+ writel_relaxed(val, anatop_base + PLL_ARM);
+ while (!(__raw_readl(anatop_base + PLL_ARM) & BM_PLL_ARM_LOCK))
+ ;
+ } else {
+ writel_relaxed(saved_pll_arm, anatop_base + PLL_ARM);
+ }
+}
+
+void imx6sl_set_wait_clk(bool enter)
+{
+ static unsigned long saved_arm_div;
+ int arm_div_for_wait = imx6sl_get_arm_divider_for_wait();
+
+ /*
+ * According to hardware design, arm podf change need
+ * PLL1 clock enabled.
+ */
+ if (arm_div_for_wait == ARM_WAIT_DIV_396M)
+ imx6sl_enable_pll_arm(true);
+
+ if (enter) {
+ saved_arm_div = readl_relaxed(ccm_base + CACRR);
+ writel_relaxed(arm_div_for_wait, ccm_base + CACRR);
+ } else {
+ writel_relaxed(saved_arm_div, ccm_base + CACRR);
+ }
+ while (__raw_readl(ccm_base + CDHIPR) & BM_CDHIPR_ARM_PODF_BUSY)
+ ;
+
+ if (arm_div_for_wait == ARM_WAIT_DIV_396M)
+ imx6sl_enable_pll_arm(false);
+}
static void __init imx6sl_clocks_init(struct device_node *ccm_node)
{
@@ -72,6 +171,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
void __iomem *base;
int irq;
int i;
+ int ret;
clks[IMX6SL_CLK_DUMMY] = imx_clk_fixed("dummy", 0);
clks[IMX6SL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0);
@@ -80,6 +180,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-anatop");
base = of_iomap(np, 0);
WARN_ON(!base);
+ anatop_base = base;
/* type name parent base div_mask */
clks[IMX6SL_CLK_PLL1_SYS] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f);
@@ -127,6 +228,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
np = ccm_node;
base = of_iomap(np, 0);
WARN_ON(!base);
+ ccm_base = base;
/* Reuse imx6q pm code */
imx6q_pm_set_ccm_base(base);
@@ -258,6 +360,19 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
clk_register_clkdev(clks[IMX6SL_CLK_GPT], "ipg", "imx-gpt.0");
clk_register_clkdev(clks[IMX6SL_CLK_GPT_SERIAL], "per", "imx-gpt.0");
+ /* Ensure the AHB clk is at 132MHz. */
+ ret = clk_set_rate(clks[IMX6SL_CLK_AHB], 132000000);
+ if (ret)
+ pr_warn("%s: failed to set AHB clock rate %d!\n",
+ __func__, ret);
+
+ /*
+ * Make sure those always on clocks are enabled to maintain the correct
+ * usecount and enabling/disabling of parent PLLs.
+ */
+ for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
+ clk_prepare_enable(clks[clks_init_on[i]]);
+
if (IS_ENABLED(CONFIG_USB_MXS_PHY)) {
clk_prepare_enable(clks[IMX6SL_CLK_USBPHY1_GATE]);
clk_prepare_enable(clks[IMX6SL_CLK_USBPHY2_GATE]);
diff --git a/arch/arm/mach-imx/clk-vf610.c b/arch/arm/mach-imx/clk-vf610.c
index ecd66d8e20b6..22dc3ee21fd4 100644
--- a/arch/arm/mach-imx/clk-vf610.c
+++ b/arch/arm/mach-imx/clk-vf610.c
@@ -63,25 +63,25 @@ static void __iomem *anatop_base;
static void __iomem *ccm_base;
/* sources for multiplexer clocks, this is used multiple times */
-static const char const *fast_sels[] = { "firc", "fxosc", };
-static const char const *slow_sels[] = { "sirc_32k", "sxosc", };
-static const char const *pll1_sels[] = { "pll1_main", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", };
-static const char const *pll2_sels[] = { "pll2_main", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", };
-static const char const *sys_sels[] = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_main", "pll1_pfd_sel", "pll3_main", };
-static const char const *ddr_sels[] = { "pll2_pfd2", "sys_sel", };
-static const char const *rmii_sels[] = { "enet_ext", "audio_ext", "enet_50m", "enet_25m", };
-static const char const *enet_ts_sels[] = { "enet_ext", "fxosc", "audio_ext", "usb", "enet_ts", "enet_25m", "enet_50m", };
-static const char const *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
-static const char const *sai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
-static const char const *nfc_sels[] = { "platform_bus", "pll1_pfd1", "pll3_pfd1", "pll3_pfd3", };
-static const char const *qspi_sels[] = { "pll3_main", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", };
-static const char const *esdhc_sels[] = { "pll3_main", "pll3_pfd3", "pll1_pfd3", "platform_bus", };
-static const char const *dcu_sels[] = { "pll1_pfd2", "pll3_main", };
-static const char const *gpu_sels[] = { "pll2_pfd2", "pll3_pfd2", };
-static const char const *vadc_sels[] = { "pll6_main_div", "pll3_main_div", "pll3_main", };
+static const char *fast_sels[] = { "firc", "fxosc", };
+static const char *slow_sels[] = { "sirc_32k", "sxosc", };
+static const char *pll1_sels[] = { "pll1_main", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", };
+static const char *pll2_sels[] = { "pll2_main", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", };
+static const char *sys_sels[] = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_main", "pll1_pfd_sel", "pll3_main", };
+static const char *ddr_sels[] = { "pll2_pfd2", "sys_sel", };
+static const char *rmii_sels[] = { "enet_ext", "audio_ext", "enet_50m", "enet_25m", };
+static const char *enet_ts_sels[] = { "enet_ext", "fxosc", "audio_ext", "usb", "enet_ts", "enet_25m", "enet_50m", };
+static const char *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
+static const char *sai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
+static const char *nfc_sels[] = { "platform_bus", "pll1_pfd1", "pll3_pfd1", "pll3_pfd3", };
+static const char *qspi_sels[] = { "pll3_main", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", };
+static const char *esdhc_sels[] = { "pll3_main", "pll3_pfd3", "pll1_pfd3", "platform_bus", };
+static const char *dcu_sels[] = { "pll1_pfd2", "pll3_main", };
+static const char *gpu_sels[] = { "pll2_pfd2", "pll3_pfd2", };
+static const char *vadc_sels[] = { "pll6_main_div", "pll3_main_div", "pll3_main", };
/* FTM counter clock source, not module clock */
-static const char const *ftm_ext_sels[] = {"sirc_128k", "sxosc", "fxosc_half", "audio_ext", };
-static const char const *ftm_fix_sels[] = { "sxosc", "ipg_bus", };
+static const char *ftm_ext_sels[] = {"sirc_128k", "sxosc", "fxosc_half", "audio_ext", };
+static const char *ftm_fix_sels[] = { "sxosc", "ipg_bus", };
static struct clk_div_table pll4_main_div_table[] = {
{ .val = 0, .div = 1 },
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index baf439dc22d8..b5241ea76706 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -116,7 +116,6 @@ void imx_enable_cpu(int cpu, bool enable);
void imx_set_cpu_jump(int cpu, void *jump_addr);
u32 imx_get_cpu_arg(int cpu);
void imx_set_cpu_arg(int cpu, u32 arg);
-void v7_cpu_resume(void);
#ifdef CONFIG_SMP
void v7_secondary_startup(void);
void imx_scu_map_io(void);
@@ -139,13 +138,25 @@ void imx_anatop_init(void);
void imx_anatop_pre_suspend(void);
void imx_anatop_post_resume(void);
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode);
-void imx6q_set_chicken_bit(void);
+void imx6q_set_int_mem_clk_lpm(void);
+void imx6sl_set_wait_clk(bool enter);
void imx_cpu_die(unsigned int cpu);
int imx_cpu_kill(unsigned int cpu);
+#ifdef CONFIG_SUSPEND
+void v7_cpu_resume(void);
+void imx6_suspend(void __iomem *ocram_vbase);
+#else
+static inline void v7_cpu_resume(void) {}
+static inline void imx6_suspend(void __iomem *ocram_vbase) {}
+#endif
+
void imx6q_pm_init(void);
+void imx6dl_pm_init(void);
+void imx6sl_pm_init(void);
void imx6q_pm_set_ccm_base(void __iomem *base);
+
#ifdef CONFIG_PM
void imx5_pm_init(void);
#else
diff --git a/arch/arm/mach-imx/cpuidle-imx6q.c b/arch/arm/mach-imx/cpuidle-imx6q.c
index 23ddfb693b2d..6bcae0479049 100644
--- a/arch/arm/mach-imx/cpuidle-imx6q.c
+++ b/arch/arm/mach-imx/cpuidle-imx6q.c
@@ -68,8 +68,8 @@ int __init imx6q_cpuidle_init(void)
/* Need to enable SCU standby for entering WAIT modes */
imx_scu_standby_enable();
- /* Set chicken bit to get a reliable WAIT mode support */
- imx6q_set_chicken_bit();
+ /* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */
+ imx6q_set_int_mem_clk_lpm();
return cpuidle_register(&imx6q_cpuidle_driver, NULL);
}
diff --git a/arch/arm/mach-imx/cpuidle-imx6sl.c b/arch/arm/mach-imx/cpuidle-imx6sl.c
new file mode 100644
index 000000000000..d4b6b8171fa9
--- /dev/null
+++ b/arch/arm/mach-imx/cpuidle-imx6sl.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/cpuidle.h>
+#include <linux/module.h>
+#include <asm/cpuidle.h>
+#include <asm/proc-fns.h>
+
+#include "common.h"
+#include "cpuidle.h"
+
+static int imx6sl_enter_wait(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
+{
+ imx6q_set_lpm(WAIT_UNCLOCKED);
+ /*
+ * Software workaround for ERR005311, see function
+ * description for details.
+ */
+ imx6sl_set_wait_clk(true);
+ cpu_do_idle();
+ imx6sl_set_wait_clk(false);
+ imx6q_set_lpm(WAIT_CLOCKED);
+
+ return index;
+}
+
+static struct cpuidle_driver imx6sl_cpuidle_driver = {
+ .name = "imx6sl_cpuidle",
+ .owner = THIS_MODULE,
+ .states = {
+ /* WFI */
+ ARM_CPUIDLE_WFI_STATE,
+ /* WAIT */
+ {
+ .exit_latency = 50,
+ .target_residency = 75,
+ .flags = CPUIDLE_FLAG_TIME_VALID |
+ CPUIDLE_FLAG_TIMER_STOP,
+ .enter = imx6sl_enter_wait,
+ .name = "WAIT",
+ .desc = "Clock off",
+ },
+ },
+ .state_count = 2,
+ .safe_state_index = 0,
+};
+
+int __init imx6sl_cpuidle_init(void)
+{
+ return cpuidle_register(&imx6sl_cpuidle_driver, NULL);
+}
diff --git a/arch/arm/mach-imx/cpuidle.h b/arch/arm/mach-imx/cpuidle.h
index 786f98ecc145..24e33670417c 100644
--- a/arch/arm/mach-imx/cpuidle.h
+++ b/arch/arm/mach-imx/cpuidle.h
@@ -13,6 +13,7 @@
#ifdef CONFIG_CPU_IDLE
extern int imx5_cpuidle_init(void);
extern int imx6q_cpuidle_init(void);
+extern int imx6sl_cpuidle_init(void);
#else
static inline int imx5_cpuidle_init(void)
{
@@ -22,4 +23,8 @@ static inline int imx6q_cpuidle_init(void)
{
return 0;
}
+static inline int imx6sl_cpuidle_init(void)
+{
+ return 0;
+}
#endif
diff --git a/arch/arm/mach-imx/devices-imx25.h b/arch/arm/mach-imx/devices-imx25.h
index 769563fdeaa0..61a114cddc39 100644
--- a/arch/arm/mach-imx/devices-imx25.h
+++ b/arch/arm/mach-imx/devices-imx25.h
@@ -83,7 +83,3 @@ extern const struct imx_spi_imx_data imx25_cspi_data[];
#define imx25_add_spi_imx0(pdata) imx25_add_spi_imx(0, pdata)
#define imx25_add_spi_imx1(pdata) imx25_add_spi_imx(1, pdata)
#define imx25_add_spi_imx2(pdata) imx25_add_spi_imx(2, pdata)
-
-extern struct imx_mxc_pwm_data imx25_mxc_pwm_data[];
-#define imx25_add_mxc_pwm(id) \
- imx_add_mxc_pwm(&imx25_mxc_pwm_data[id])
diff --git a/arch/arm/mach-imx/devices-imx51.h b/arch/arm/mach-imx/devices-imx51.h
index deee5baee88c..26389f35a2b2 100644
--- a/arch/arm/mach-imx/devices-imx51.h
+++ b/arch/arm/mach-imx/devices-imx51.h
@@ -57,10 +57,6 @@ extern const struct imx_imx2_wdt_data imx51_imx2_wdt_data[];
#define imx51_add_imx2_wdt(id) \
imx_add_imx2_wdt(&imx51_imx2_wdt_data[id])
-extern const struct imx_mxc_pwm_data imx51_mxc_pwm_data[];
-#define imx51_add_mxc_pwm(id) \
- imx_add_mxc_pwm(&imx51_mxc_pwm_data[id])
-
extern const struct imx_imx_keypad_data imx51_imx_keypad_data;
#define imx51_add_imx_keypad(pdata) \
imx_add_imx_keypad(&imx51_imx_keypad_data, pdata)
diff --git a/arch/arm/mach-imx/devices/Kconfig b/arch/arm/mach-imx/devices/Kconfig
index 68c74fb0373c..2d260a5a307c 100644
--- a/arch/arm/mach-imx/devices/Kconfig
+++ b/arch/arm/mach-imx/devices/Kconfig
@@ -67,9 +67,6 @@ config IMX_HAVE_PLATFORM_MXC_MMC
config IMX_HAVE_PLATFORM_MXC_NAND
bool
-config IMX_HAVE_PLATFORM_MXC_PWM
- bool
-
config IMX_HAVE_PLATFORM_MXC_RNGA
bool
select ARCH_HAS_RNGA
diff --git a/arch/arm/mach-imx/devices/Makefile b/arch/arm/mach-imx/devices/Makefile
index 67416fb1dc69..1cbc14cd80d1 100644
--- a/arch/arm/mach-imx/devices/Makefile
+++ b/arch/arm/mach-imx/devices/Makefile
@@ -23,7 +23,6 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_MX2_CAMERA) += platform-mx2-camera.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_EHCI) += platform-mxc-ehci.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_MMC) += platform-mxc-mmc.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_NAND) += platform-mxc_nand.o
-obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_PWM) += platform-mxc_pwm.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_RNGA) += platform-mxc_rnga.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_RTC) += platform-mxc_rtc.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_W1) += platform-mxc_w1.o
diff --git a/arch/arm/mach-imx/devices/devices-common.h b/arch/arm/mach-imx/devices/devices-common.h
index c13b76b9f6b3..61352a80bb59 100644
--- a/arch/arm/mach-imx/devices/devices-common.h
+++ b/arch/arm/mach-imx/devices/devices-common.h
@@ -290,15 +290,6 @@ struct imx_pata_imx_data {
struct platform_device *__init imx_add_pata_imx(
const struct imx_pata_imx_data *data);
-struct imx_mxc_pwm_data {
- int id;
- resource_size_t iobase;
- resource_size_t iosize;
- resource_size_t irq;
-};
-struct platform_device *__init imx_add_mxc_pwm(
- const struct imx_mxc_pwm_data *data);
-
/* mxc_rtc */
struct imx_mxc_rtc_data {
const char *devid;
diff --git a/arch/arm/mach-imx/devices/platform-mxc_pwm.c b/arch/arm/mach-imx/devices/platform-mxc_pwm.c
deleted file mode 100644
index dcd289777687..000000000000
--- a/arch/arm/mach-imx/devices/platform-mxc_pwm.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2009-2010 Pengutronix
- * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License version 2 as published by the
- * Free Software Foundation.
- */
-#include "../hardware.h"
-#include "devices-common.h"
-
-#define imx_mxc_pwm_data_entry_single(soc, _id, _hwid, _size) \
- { \
- .id = _id, \
- .iobase = soc ## _PWM ## _hwid ## _BASE_ADDR, \
- .iosize = _size, \
- .irq = soc ## _INT_PWM ## _hwid, \
- }
-#define imx_mxc_pwm_data_entry(soc, _id, _hwid, _size) \
- [_id] = imx_mxc_pwm_data_entry_single(soc, _id, _hwid, _size)
-
-#ifdef CONFIG_SOC_IMX21
-const struct imx_mxc_pwm_data imx21_mxc_pwm_data __initconst =
- imx_mxc_pwm_data_entry_single(MX21, 0, , SZ_4K);
-#endif /* ifdef CONFIG_SOC_IMX21 */
-
-#ifdef CONFIG_SOC_IMX25
-const struct imx_mxc_pwm_data imx25_mxc_pwm_data[] __initconst = {
-#define imx25_mxc_pwm_data_entry(_id, _hwid) \
- imx_mxc_pwm_data_entry(MX25, _id, _hwid, SZ_16K)
- imx25_mxc_pwm_data_entry(0, 1),
- imx25_mxc_pwm_data_entry(1, 2),
- imx25_mxc_pwm_data_entry(2, 3),
- imx25_mxc_pwm_data_entry(3, 4),
-};
-#endif /* ifdef CONFIG_SOC_IMX25 */
-
-#ifdef CONFIG_SOC_IMX27
-const struct imx_mxc_pwm_data imx27_mxc_pwm_data __initconst =
- imx_mxc_pwm_data_entry_single(MX27, 0, , SZ_4K);
-#endif /* ifdef CONFIG_SOC_IMX27 */
-
-#ifdef CONFIG_SOC_IMX51
-const struct imx_mxc_pwm_data imx51_mxc_pwm_data[] __initconst = {
-#define imx51_mxc_pwm_data_entry(_id, _hwid) \
- imx_mxc_pwm_data_entry(MX51, _id, _hwid, SZ_16K)
- imx51_mxc_pwm_data_entry(0, 1),
- imx51_mxc_pwm_data_entry(1, 2),
-};
-#endif /* ifdef CONFIG_SOC_IMX51 */
-
-struct platform_device *__init imx_add_mxc_pwm(
- const struct imx_mxc_pwm_data *data)
-{
- struct resource res[] = {
- {
- .start = data->iobase,
- .end = data->iobase + data->iosize - 1,
- .flags = IORESOURCE_MEM,
- }, {
- .start = data->irq,
- .end = data->irq,
- .flags = IORESOURCE_IRQ,
- },
- };
-
- return imx_add_platform_device("mxc_pwm", data->id,
- res, ARRAY_SIZE(res), NULL, 0);
-}
diff --git a/arch/arm/mach-imx/hardware.h b/arch/arm/mach-imx/hardware.h
index a3b0b04b45c9..abf43bb47eca 100644
--- a/arch/arm/mach-imx/hardware.h
+++ b/arch/arm/mach-imx/hardware.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2007, 2014 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
*
* This program is free software; you can redistribute it and/or
@@ -20,7 +20,9 @@
#ifndef __ASM_ARCH_MXC_HARDWARE_H__
#define __ASM_ARCH_MXC_HARDWARE_H__
+#ifndef __ASSEMBLY__
#include <asm/io.h>
+#endif
#include <asm/sizes.h>
#define addr_in_module(addr, mod) \
diff --git a/arch/arm/mach-imx/headsmp.S b/arch/arm/mach-imx/headsmp.S
index 627f16f0e9d1..de5047c8a6c8 100644
--- a/arch/arm/mach-imx/headsmp.S
+++ b/arch/arm/mach-imx/headsmp.S
@@ -12,12 +12,7 @@
#include <linux/linkage.h>
#include <linux/init.h>
-#include <asm/asm-offsets.h>
-#include <asm/hardware/cache-l2x0.h>
- .section ".text.head", "ax"
-
-#ifdef CONFIG_SMP
diag_reg_offset:
.word g_diag_reg - .
@@ -34,38 +29,3 @@ ENTRY(v7_secondary_startup)
set_diag_reg
b secondary_startup
ENDPROC(v7_secondary_startup)
-#endif
-
-#ifdef CONFIG_ARM_CPU_SUSPEND
-/*
- * The following code must assume it is running from physical address
- * where absolute virtual addresses to the data section have to be
- * turned into relative ones.
- */
-
-#ifdef CONFIG_CACHE_L2X0
- .macro pl310_resume
- adr r0, l2x0_saved_regs_offset
- ldr r2, [r0]
- add r2, r2, r0
- ldr r0, [r2, #L2X0_R_PHY_BASE] @ get physical base of l2x0
- ldr r1, [r2, #L2X0_R_AUX_CTRL] @ get aux_ctrl value
- str r1, [r0, #L2X0_AUX_CTRL] @ restore aux_ctrl
- mov r1, #0x1
- str r1, [r0, #L2X0_CTRL] @ re-enable L2
- .endm
-
-l2x0_saved_regs_offset:
- .word l2x0_saved_regs - .
-
-#else
- .macro pl310_resume
- .endm
-#endif
-
-ENTRY(v7_cpu_resume)
- bl v7_invalidate_l1
- pl310_resume
- b cpu_resume
-ENDPROC(v7_cpu_resume)
-#endif
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index 76e5db4fce35..e60456d85c9d 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -182,16 +182,83 @@ static void __init imx6q_enet_phy_init(void)
static void __init imx6q_1588_init(void)
{
+ struct device_node *np;
+ struct clk *ptp_clk;
+ struct clk *enet_ref;
struct regmap *gpr;
+ u32 clksel;
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-fec");
+ if (!np) {
+ pr_warn("%s: failed to find fec node\n", __func__);
+ return;
+ }
+
+ ptp_clk = of_clk_get(np, 2);
+ if (IS_ERR(ptp_clk)) {
+ pr_warn("%s: failed to get ptp clock\n", __func__);
+ goto put_node;
+ }
+
+ enet_ref = clk_get_sys(NULL, "enet_ref");
+ if (IS_ERR(enet_ref)) {
+ pr_warn("%s: failed to get enet clock\n", __func__);
+ goto put_ptp_clk;
+ }
+
+ /*
+ * If enet_ref from ANATOP/CCM is the PTP clock source, we need to
+ * set bit IOMUXC_GPR1[21]. Or the PTP clock must be from pad
+ * (external OSC), and we need to clear the bit.
+ */
+ clksel = ptp_clk == enet_ref ? IMX6Q_GPR1_ENET_CLK_SEL_ANATOP :
+ IMX6Q_GPR1_ENET_CLK_SEL_PAD;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
if (!IS_ERR(gpr))
regmap_update_bits(gpr, IOMUXC_GPR1,
IMX6Q_GPR1_ENET_CLK_SEL_MASK,
- IMX6Q_GPR1_ENET_CLK_SEL_ANATOP);
+ clksel);
else
pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n");
+ clk_put(enet_ref);
+put_ptp_clk:
+ clk_put(ptp_clk);
+put_node:
+ of_node_put(np);
+}
+
+static void __init imx6q_axi_init(void)
+{
+ struct regmap *gpr;
+ unsigned int mask;
+
+ gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
+ if (!IS_ERR(gpr)) {
+ /*
+ * Enable the cacheable attribute of VPU and IPU
+ * AXI transactions.
+ */
+ mask = IMX6Q_GPR4_VPU_WR_CACHE_SEL |
+ IMX6Q_GPR4_VPU_RD_CACHE_SEL |
+ IMX6Q_GPR4_VPU_P_WR_CACHE_VAL |
+ IMX6Q_GPR4_VPU_P_RD_CACHE_VAL_MASK |
+ IMX6Q_GPR4_IPU_WR_CACHE_CTL |
+ IMX6Q_GPR4_IPU_RD_CACHE_CTL;
+ regmap_update_bits(gpr, IOMUXC_GPR4, mask, mask);
+
+ /* Increase IPU read QoS priority */
+ regmap_update_bits(gpr, IOMUXC_GPR6,
+ IMX6Q_GPR6_IPU1_ID00_RD_QOS_MASK |
+ IMX6Q_GPR6_IPU1_ID01_RD_QOS_MASK,
+ (0xf << 16) | (0x7 << 20));
+ regmap_update_bits(gpr, IOMUXC_GPR7,
+ IMX6Q_GPR7_IPU2_ID00_RD_QOS_MASK |
+ IMX6Q_GPR7_IPU2_ID01_RD_QOS_MASK,
+ (0xf << 16) | (0x7 << 20));
+ } else {
+ pr_warn("failed to find fsl,imx6q-iomuxc-gpr regmap\n");
+ }
}
static void __init imx6q_init_machine(void)
@@ -212,15 +279,18 @@ static void __init imx6q_init_machine(void)
of_platform_populate(NULL, of_default_bus_match_table, NULL, parent);
imx_anatop_init();
- imx6q_pm_init();
+ cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init();
imx6q_1588_init();
+ imx6q_axi_init();
}
#define OCOTP_CFG3 0x440
#define OCOTP_CFG3_SPEED_SHIFT 16
#define OCOTP_CFG3_SPEED_1P2GHZ 0x3
+#define OCOTP_CFG3_SPEED_996MHZ 0x2
+#define OCOTP_CFG3_SPEED_852MHZ 0x1
-static void __init imx6q_opp_check_1p2ghz(struct device *cpu_dev)
+static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev)
{
struct device_node *np;
void __iomem *base;
@@ -238,11 +308,29 @@ static void __init imx6q_opp_check_1p2ghz(struct device *cpu_dev)
goto put_node;
}
+ /*
+ * SPEED_GRADING[1:0] defines the max speed of ARM:
+ * 2b'11: 1200000000Hz;
+ * 2b'10: 996000000Hz;
+ * 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz.
+ * 2b'00: 792000000Hz;
+ * We need to set the max speed of ARM according to fuse map.
+ */
val = readl_relaxed(base + OCOTP_CFG3);
val >>= OCOTP_CFG3_SPEED_SHIFT;
- if ((val & 0x3) != OCOTP_CFG3_SPEED_1P2GHZ)
+ val &= 0x3;
+
+ if (val != OCOTP_CFG3_SPEED_1P2GHZ)
if (dev_pm_opp_disable(cpu_dev, 1200000000))
pr_warn("failed to disable 1.2 GHz OPP\n");
+ if (val < OCOTP_CFG3_SPEED_996MHZ)
+ if (dev_pm_opp_disable(cpu_dev, 996000000))
+ pr_warn("failed to disable 996 MHz OPP\n");
+ if (cpu_is_imx6q()) {
+ if (val != OCOTP_CFG3_SPEED_852MHZ)
+ if (dev_pm_opp_disable(cpu_dev, 852000000))
+ pr_warn("failed to disable 852 MHz OPP\n");
+ }
put_node:
of_node_put(np);
@@ -268,7 +356,7 @@ static void __init imx6q_opp_init(void)
goto put_node;
}
- imx6q_opp_check_1p2ghz(cpu_dev);
+ imx6q_opp_check_speed_grading(cpu_dev);
put_node:
of_node_put(np);
diff --git a/arch/arm/mach-imx/mach-imx6sl.c b/arch/arm/mach-imx/mach-imx6sl.c
index 0f4fd4c0ab8e..ad323385115c 100644
--- a/arch/arm/mach-imx/mach-imx6sl.c
+++ b/arch/arm/mach-imx/mach-imx6sl.c
@@ -17,6 +17,7 @@
#include <asm/mach/map.h>
#include "common.h"
+#include "cpuidle.h"
static void __init imx6sl_fec_init(void)
{
@@ -39,6 +40,8 @@ static void __init imx6sl_init_late(void)
/* imx6sl reuses imx6q cpufreq driver */
if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ))
platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0);
+
+ imx6sl_cpuidle_init();
}
static void __init imx6sl_init_machine(void)
@@ -55,8 +58,7 @@ static void __init imx6sl_init_machine(void)
imx6sl_fec_init();
imx_anatop_init();
- /* Reuse imx6q pm code */
- imx6q_pm_init();
+ imx6sl_pm_init();
}
static void __init imx6sl_init_irq(void)
diff --git a/arch/arm/mach-imx/mach-mx27ads.c b/arch/arm/mach-imx/mach-mx27ads.c
index 9821b824dcaf..a7a4a9c67615 100644
--- a/arch/arm/mach-imx/mach-mx27ads.c
+++ b/arch/arm/mach-imx/mach-mx27ads.c
@@ -21,6 +21,10 @@
#include <linux/mtd/physmap.h>
#include <linux/i2c.h>
#include <linux/irq.h>
+
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
+
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
@@ -195,14 +199,58 @@ static const struct imxi2c_platform_data mx27ads_i2c1_data __initconst = {
static struct i2c_board_info mx27ads_i2c_devices[] = {
};
-void lcd_power(int on)
+static void vgpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
- if (on)
+ if (value)
__raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_SET_REG);
else
__raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_CLEAR_REG);
}
+static int vgpio_dir_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+ return 0;
+}
+
+#define MX27ADS_LCD_GPIO (6 * 32)
+
+static struct regulator_consumer_supply mx27ads_lcd_regulator_consumer =
+ REGULATOR_SUPPLY("lcd", "imx-fb.0");
+
+static struct regulator_init_data mx27ads_lcd_regulator_init_data = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+},
+ .consumer_supplies = &mx27ads_lcd_regulator_consumer,
+ .num_consumer_supplies = 1,
+};
+
+static struct fixed_voltage_config mx27ads_lcd_regulator_pdata = {
+ .supply_name = "LCD",
+ .microvolts = 3300000,
+ .gpio = MX27ADS_LCD_GPIO,
+ .init_data = &mx27ads_lcd_regulator_init_data,
+};
+
+static void __init mx27ads_regulator_init(void)
+{
+ struct gpio_chip *vchip;
+
+ vchip = kzalloc(sizeof(*vchip), GFP_KERNEL);
+ vchip->owner = THIS_MODULE;
+ vchip->label = "LCD";
+ vchip->base = MX27ADS_LCD_GPIO;
+ vchip->ngpio = 1;
+ vchip->direction_output = vgpio_dir_out;
+ vchip->set = vgpio_set;
+ gpiochip_add(vchip);
+
+ platform_device_register_data(&platform_bus, "reg-fixed-voltage",
+ PLATFORM_DEVID_AUTO,
+ &mx27ads_lcd_regulator_pdata,
+ sizeof(mx27ads_lcd_regulator_pdata));
+}
+
static struct imx_fb_videomode mx27ads_modes[] = {
{
.mode = {
@@ -239,8 +287,6 @@ static const struct imx_fb_platform_data mx27ads_fb_data __initconst = {
.pwmr = 0x00A903FF,
.lscr1 = 0x00120300,
.dmacr = 0x00020010,
-
- .lcd_power = lcd_power,
};
static int mx27ads_sdhc1_init(struct device *dev, irq_handler_t detect_irq,
@@ -304,6 +350,7 @@ static void __init mx27ads_board_init(void)
i2c_register_board_info(1, mx27ads_i2c_devices,
ARRAY_SIZE(mx27ads_i2c_devices));
imx27_add_imx_i2c(1, &mx27ads_i2c1_data);
+ mx27ads_regulator_init();
imx27_add_imx_fb(&mx27ads_fb_data);
imx27_add_mxc_mmc(0, &sdhc1_pdata);
imx27_add_mxc_mmc(1, &sdhc2_pdata);
diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c
new file mode 100644
index 000000000000..9392a8f4ef24
--- /dev/null
+++ b/arch/arm/mach-imx/pm-imx6.c
@@ -0,0 +1,551 @@
+/*
+ * Copyright 2011-2014 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/genalloc.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/suspend.h>
+#include <asm/cacheflush.h>
+#include <asm/fncpy.h>
+#include <asm/proc-fns.h>
+#include <asm/suspend.h>
+#include <asm/tlb.h>
+
+#include "common.h"
+#include "hardware.h"
+
+#define CCR 0x0
+#define BM_CCR_WB_COUNT (0x7 << 16)
+#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21)
+#define BM_CCR_RBC_EN (0x1 << 27)
+
+#define CLPCR 0x54
+#define BP_CLPCR_LPM 0
+#define BM_CLPCR_LPM (0x3 << 0)
+#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2)
+#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5)
+#define BM_CLPCR_SBYOS (0x1 << 6)
+#define BM_CLPCR_DIS_REF_OSC (0x1 << 7)
+#define BM_CLPCR_VSTBY (0x1 << 8)
+#define BP_CLPCR_STBY_COUNT 9
+#define BM_CLPCR_STBY_COUNT (0x3 << 9)
+#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11)
+#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16)
+#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17)
+#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19)
+#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21)
+#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22)
+#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23)
+#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24)
+#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25)
+#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26)
+#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27)
+
+#define CGPR 0x64
+#define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17)
+
+#define MX6Q_SUSPEND_OCRAM_SIZE 0x1000
+#define MX6_MAX_MMDC_IO_NUM 33
+
+static void __iomem *ccm_base;
+static void __iomem *suspend_ocram_base;
+static void (*imx6_suspend_in_ocram_fn)(void __iomem *ocram_vbase);
+
+/*
+ * suspend ocram space layout:
+ * ======================== high address ======================
+ * .
+ * .
+ * .
+ * ^
+ * ^
+ * ^
+ * imx6_suspend code
+ * PM_INFO structure(imx6_cpu_pm_info)
+ * ======================== low address =======================
+ */
+
+struct imx6_pm_base {
+ phys_addr_t pbase;
+ void __iomem *vbase;
+};
+
+struct imx6_pm_socdata {
+ u32 cpu_type;
+ const char *mmdc_compat;
+ const char *src_compat;
+ const char *iomuxc_compat;
+ const char *gpc_compat;
+ const u32 mmdc_io_num;
+ const u32 *mmdc_io_offset;
+};
+
+static const u32 imx6q_mmdc_io_offset[] __initconst = {
+ 0x5ac, 0x5b4, 0x528, 0x520, /* DQM0 ~ DQM3 */
+ 0x514, 0x510, 0x5bc, 0x5c4, /* DQM4 ~ DQM7 */
+ 0x56c, 0x578, 0x588, 0x594, /* CAS, RAS, SDCLK_0, SDCLK_1 */
+ 0x5a8, 0x5b0, 0x524, 0x51c, /* SDQS0 ~ SDQS3 */
+ 0x518, 0x50c, 0x5b8, 0x5c0, /* SDQS4 ~ SDQS7 */
+ 0x784, 0x788, 0x794, 0x79c, /* GPR_B0DS ~ GPR_B3DS */
+ 0x7a0, 0x7a4, 0x7a8, 0x748, /* GPR_B4DS ~ GPR_B7DS */
+ 0x59c, 0x5a0, 0x750, 0x774, /* SODT0, SODT1, MODE_CTL, MODE */
+ 0x74c, /* GPR_ADDS */
+};
+
+static const u32 imx6dl_mmdc_io_offset[] __initconst = {
+ 0x470, 0x474, 0x478, 0x47c, /* DQM0 ~ DQM3 */
+ 0x480, 0x484, 0x488, 0x48c, /* DQM4 ~ DQM7 */
+ 0x464, 0x490, 0x4ac, 0x4b0, /* CAS, RAS, SDCLK_0, SDCLK_1 */
+ 0x4bc, 0x4c0, 0x4c4, 0x4c8, /* DRAM_SDQS0 ~ DRAM_SDQS3 */
+ 0x4cc, 0x4d0, 0x4d4, 0x4d8, /* DRAM_SDQS4 ~ DRAM_SDQS7 */
+ 0x764, 0x770, 0x778, 0x77c, /* GPR_B0DS ~ GPR_B3DS */
+ 0x780, 0x784, 0x78c, 0x748, /* GPR_B4DS ~ GPR_B7DS */
+ 0x4b4, 0x4b8, 0x750, 0x760, /* SODT0, SODT1, MODE_CTL, MODE */
+ 0x74c, /* GPR_ADDS */
+};
+
+static const u32 imx6sl_mmdc_io_offset[] __initconst = {
+ 0x30c, 0x310, 0x314, 0x318, /* DQM0 ~ DQM3 */
+ 0x5c4, 0x5cc, 0x5d4, 0x5d8, /* GPR_B0DS ~ GPR_B3DS */
+ 0x300, 0x31c, 0x338, 0x5ac, /* CAS, RAS, SDCLK_0, GPR_ADDS */
+ 0x33c, 0x340, 0x5b0, 0x5c0, /* SODT0, SODT1, MODE_CTL, MODE */
+ 0x330, 0x334, 0x320, /* SDCKE0, SDCKE1, RESET */
+};
+
+static const struct imx6_pm_socdata imx6q_pm_data __initconst = {
+ .cpu_type = MXC_CPU_IMX6Q,
+ .mmdc_compat = "fsl,imx6q-mmdc",
+ .src_compat = "fsl,imx6q-src",
+ .iomuxc_compat = "fsl,imx6q-iomuxc",
+ .gpc_compat = "fsl,imx6q-gpc",
+ .mmdc_io_num = ARRAY_SIZE(imx6q_mmdc_io_offset),
+ .mmdc_io_offset = imx6q_mmdc_io_offset,
+};
+
+static const struct imx6_pm_socdata imx6dl_pm_data __initconst = {
+ .cpu_type = MXC_CPU_IMX6DL,
+ .mmdc_compat = "fsl,imx6q-mmdc",
+ .src_compat = "fsl,imx6q-src",
+ .iomuxc_compat = "fsl,imx6dl-iomuxc",
+ .gpc_compat = "fsl,imx6q-gpc",
+ .mmdc_io_num = ARRAY_SIZE(imx6dl_mmdc_io_offset),
+ .mmdc_io_offset = imx6dl_mmdc_io_offset,
+};
+
+static const struct imx6_pm_socdata imx6sl_pm_data __initconst = {
+ .cpu_type = MXC_CPU_IMX6SL,
+ .mmdc_compat = "fsl,imx6sl-mmdc",
+ .src_compat = "fsl,imx6sl-src",
+ .iomuxc_compat = "fsl,imx6sl-iomuxc",
+ .gpc_compat = "fsl,imx6sl-gpc",
+ .mmdc_io_num = ARRAY_SIZE(imx6sl_mmdc_io_offset),
+ .mmdc_io_offset = imx6sl_mmdc_io_offset,
+};
+
+/*
+ * This structure is for passing necessary data for low level ocram
+ * suspend code(arch/arm/mach-imx/suspend-imx6.S), if this struct
+ * definition is changed, the offset definition in
+ * arch/arm/mach-imx/suspend-imx6.S must be also changed accordingly,
+ * otherwise, the suspend to ocram function will be broken!
+ */
+struct imx6_cpu_pm_info {
+ phys_addr_t pbase; /* The physical address of pm_info. */
+ phys_addr_t resume_addr; /* The physical resume address for asm code */
+ u32 cpu_type;
+ u32 pm_info_size; /* Size of pm_info. */
+ struct imx6_pm_base mmdc_base;
+ struct imx6_pm_base src_base;
+ struct imx6_pm_base iomuxc_base;
+ struct imx6_pm_base ccm_base;
+ struct imx6_pm_base gpc_base;
+ struct imx6_pm_base l2_base;
+ u32 mmdc_io_num; /* Number of MMDC IOs which need saved/restored. */
+ u32 mmdc_io_val[MX6_MAX_MMDC_IO_NUM][2]; /* To save offset and value */
+} __aligned(8);
+
+void imx6q_set_int_mem_clk_lpm(void)
+{
+ u32 val = readl_relaxed(ccm_base + CGPR);
+
+ val |= BM_CGPR_INT_MEM_CLK_LPM;
+ writel_relaxed(val, ccm_base + CGPR);
+}
+
+static void imx6q_enable_rbc(bool enable)
+{
+ u32 val;
+
+ /*
+ * need to mask all interrupts in GPC before
+ * operating RBC configurations
+ */
+ imx_gpc_mask_all();
+
+ /* configure RBC enable bit */
+ val = readl_relaxed(ccm_base + CCR);
+ val &= ~BM_CCR_RBC_EN;
+ val |= enable ? BM_CCR_RBC_EN : 0;
+ writel_relaxed(val, ccm_base + CCR);
+
+ /* configure RBC count */
+ val = readl_relaxed(ccm_base + CCR);
+ val &= ~BM_CCR_RBC_BYPASS_COUNT;
+ val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
+ writel(val, ccm_base + CCR);
+
+ /*
+ * need to delay at least 2 cycles of CKIL(32K)
+ * due to hardware design requirement, which is
+ * ~61us, here we use 65us for safe
+ */
+ udelay(65);
+
+ /* restore GPC interrupt mask settings */
+ imx_gpc_restore_all();
+}
+
+static void imx6q_enable_wb(bool enable)
+{
+ u32 val;
+
+ /* configure well bias enable bit */
+ val = readl_relaxed(ccm_base + CLPCR);
+ val &= ~BM_CLPCR_WB_PER_AT_LPM;
+ val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
+ writel_relaxed(val, ccm_base + CLPCR);
+
+ /* configure well bias count */
+ val = readl_relaxed(ccm_base + CCR);
+ val &= ~BM_CCR_WB_COUNT;
+ val |= enable ? BM_CCR_WB_COUNT : 0;
+ writel_relaxed(val, ccm_base + CCR);
+}
+
+int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
+{
+ struct irq_data *iomuxc_irq_data = irq_get_irq_data(32);
+ u32 val = readl_relaxed(ccm_base + CLPCR);
+
+ val &= ~BM_CLPCR_LPM;
+ switch (mode) {
+ case WAIT_CLOCKED:
+ break;
+ case WAIT_UNCLOCKED:
+ val |= 0x1 << BP_CLPCR_LPM;
+ val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
+ break;
+ case STOP_POWER_ON:
+ val |= 0x2 << BP_CLPCR_LPM;
+ break;
+ case WAIT_UNCLOCKED_POWER_OFF:
+ val |= 0x1 << BP_CLPCR_LPM;
+ val &= ~BM_CLPCR_VSTBY;
+ val &= ~BM_CLPCR_SBYOS;
+ break;
+ case STOP_POWER_OFF:
+ val |= 0x2 << BP_CLPCR_LPM;
+ val |= 0x3 << BP_CLPCR_STBY_COUNT;
+ val |= BM_CLPCR_VSTBY;
+ val |= BM_CLPCR_SBYOS;
+ if (cpu_is_imx6sl()) {
+ val |= BM_CLPCR_BYPASS_PMIC_READY;
+ val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
+ } else {
+ val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * ERR007265: CCM: When improper low-power sequence is used,
+ * the SoC enters low power mode before the ARM core executes WFI.
+ *
+ * Software workaround:
+ * 1) Software should trigger IRQ #32 (IOMUX) to be always pending
+ * by setting IOMUX_GPR1_GINT.
+ * 2) Software should then unmask IRQ #32 in GPC before setting CCM
+ * Low-Power mode.
+ * 3) Software should mask IRQ #32 right after CCM Low-Power mode
+ * is set (set bits 0-1 of CCM_CLPCR).
+ */
+ imx_gpc_irq_unmask(iomuxc_irq_data);
+ writel_relaxed(val, ccm_base + CLPCR);
+ imx_gpc_irq_mask(iomuxc_irq_data);
+
+ return 0;
+}
+
+static int imx6q_suspend_finish(unsigned long val)
+{
+ if (!imx6_suspend_in_ocram_fn) {
+ cpu_do_idle();
+ } else {
+ /*
+ * call low level suspend function in ocram,
+ * as we need to float DDR IO.
+ */
+ local_flush_tlb_all();
+ imx6_suspend_in_ocram_fn(suspend_ocram_base);
+ }
+
+ return 0;
+}
+
+static int imx6q_pm_enter(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_MEM:
+ imx6q_set_lpm(STOP_POWER_OFF);
+ imx6q_enable_wb(true);
+ /*
+ * For suspend into ocram, asm code already take care of
+ * RBC setting, so we do NOT need to do that here.
+ */
+ if (!imx6_suspend_in_ocram_fn)
+ imx6q_enable_rbc(true);
+ imx_gpc_pre_suspend();
+ imx_anatop_pre_suspend();
+ imx_set_cpu_jump(0, v7_cpu_resume);
+ /* Zzz ... */
+ cpu_suspend(0, imx6q_suspend_finish);
+ if (cpu_is_imx6q() || cpu_is_imx6dl())
+ imx_smp_prepare();
+ imx_anatop_post_resume();
+ imx_gpc_post_resume();
+ imx6q_enable_rbc(false);
+ imx6q_enable_wb(false);
+ imx6q_set_lpm(WAIT_CLOCKED);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct platform_suspend_ops imx6q_pm_ops = {
+ .enter = imx6q_pm_enter,
+ .valid = suspend_valid_only_mem,
+};
+
+void __init imx6q_pm_set_ccm_base(void __iomem *base)
+{
+ ccm_base = base;
+}
+
+static int __init imx6_pm_get_base(struct imx6_pm_base *base,
+ const char *compat)
+{
+ struct device_node *node;
+ struct resource res;
+ int ret = 0;
+
+ node = of_find_compatible_node(NULL, NULL, compat);
+ if (!node) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret)
+ goto put_node;
+
+ base->pbase = res.start;
+ base->vbase = ioremap(res.start, resource_size(&res));
+ if (!base->vbase)
+ ret = -ENOMEM;
+
+put_node:
+ of_node_put(node);
+out:
+ return ret;
+}
+
+static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
+{
+ phys_addr_t ocram_pbase;
+ struct device_node *node;
+ struct platform_device *pdev;
+ struct imx6_cpu_pm_info *pm_info;
+ struct gen_pool *ocram_pool;
+ unsigned long ocram_base;
+ int i, ret = 0;
+ const u32 *mmdc_offset_array;
+
+ suspend_set_ops(&imx6q_pm_ops);
+
+ if (!socdata) {
+ pr_warn("%s: invalid argument!\n", __func__);
+ return -EINVAL;
+ }
+
+ node = of_find_compatible_node(NULL, NULL, "mmio-sram");
+ if (!node) {
+ pr_warn("%s: failed to find ocram node!\n", __func__);
+ return -ENODEV;
+ }
+
+ pdev = of_find_device_by_node(node);
+ if (!pdev) {
+ pr_warn("%s: failed to find ocram device!\n", __func__);
+ ret = -ENODEV;
+ goto put_node;
+ }
+
+ ocram_pool = dev_get_gen_pool(&pdev->dev);
+ if (!ocram_pool) {
+ pr_warn("%s: ocram pool unavailable!\n", __func__);
+ ret = -ENODEV;
+ goto put_node;
+ }
+
+ ocram_base = gen_pool_alloc(ocram_pool, MX6Q_SUSPEND_OCRAM_SIZE);
+ if (!ocram_base) {
+ pr_warn("%s: unable to alloc ocram!\n", __func__);
+ ret = -ENOMEM;
+ goto put_node;
+ }
+
+ ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base);
+
+ suspend_ocram_base = __arm_ioremap_exec(ocram_pbase,
+ MX6Q_SUSPEND_OCRAM_SIZE, false);
+
+ pm_info = suspend_ocram_base;
+ pm_info->pbase = ocram_pbase;
+ pm_info->resume_addr = virt_to_phys(v7_cpu_resume);
+ pm_info->pm_info_size = sizeof(*pm_info);
+
+ /*
+ * ccm physical address is not used by asm code currently,
+ * so get ccm virtual address directly, as we already have
+ * it from ccm driver.
+ */
+ pm_info->ccm_base.vbase = ccm_base;
+
+ ret = imx6_pm_get_base(&pm_info->mmdc_base, socdata->mmdc_compat);
+ if (ret) {
+ pr_warn("%s: failed to get mmdc base %d!\n", __func__, ret);
+ goto put_node;
+ }
+
+ ret = imx6_pm_get_base(&pm_info->src_base, socdata->src_compat);
+ if (ret) {
+ pr_warn("%s: failed to get src base %d!\n", __func__, ret);
+ goto src_map_failed;
+ }
+
+ ret = imx6_pm_get_base(&pm_info->iomuxc_base, socdata->iomuxc_compat);
+ if (ret) {
+ pr_warn("%s: failed to get iomuxc base %d!\n", __func__, ret);
+ goto iomuxc_map_failed;
+ }
+
+ ret = imx6_pm_get_base(&pm_info->gpc_base, socdata->gpc_compat);
+ if (ret) {
+ pr_warn("%s: failed to get gpc base %d!\n", __func__, ret);
+ goto gpc_map_failed;
+ }
+
+ ret = imx6_pm_get_base(&pm_info->l2_base, "arm,pl310-cache");
+ if (ret) {
+ pr_warn("%s: failed to get pl310-cache base %d!\n",
+ __func__, ret);
+ goto pl310_cache_map_failed;
+ }
+
+ pm_info->cpu_type = socdata->cpu_type;
+ pm_info->mmdc_io_num = socdata->mmdc_io_num;
+ mmdc_offset_array = socdata->mmdc_io_offset;
+
+ for (i = 0; i < pm_info->mmdc_io_num; i++) {
+ pm_info->mmdc_io_val[i][0] =
+ mmdc_offset_array[i];
+ pm_info->mmdc_io_val[i][1] =
+ readl_relaxed(pm_info->iomuxc_base.vbase +
+ mmdc_offset_array[i]);
+ }
+
+ imx6_suspend_in_ocram_fn = fncpy(
+ suspend_ocram_base + sizeof(*pm_info),
+ &imx6_suspend,
+ MX6Q_SUSPEND_OCRAM_SIZE - sizeof(*pm_info));
+
+ goto put_node;
+
+pl310_cache_map_failed:
+ iounmap(&pm_info->gpc_base.vbase);
+gpc_map_failed:
+ iounmap(&pm_info->iomuxc_base.vbase);
+iomuxc_map_failed:
+ iounmap(&pm_info->src_base.vbase);
+src_map_failed:
+ iounmap(&pm_info->mmdc_base.vbase);
+put_node:
+ of_node_put(node);
+
+ return ret;
+}
+
+static void __init imx6_pm_common_init(const struct imx6_pm_socdata
+ *socdata)
+{
+ struct regmap *gpr;
+ int ret;
+
+ WARN_ON(!ccm_base);
+
+ if (IS_ENABLED(CONFIG_SUSPEND)) {
+ ret = imx6q_suspend_init(socdata);
+ if (ret)
+ pr_warn("%s: No DDR LPM support with suspend %d!\n",
+ __func__, ret);
+ }
+
+ /*
+ * This is for SW workaround step #1 of ERR007265, see comments
+ * in imx6q_set_lpm for details of this errata.
+ * Force IOMUXC irq pending, so that the interrupt to GPC can be
+ * used to deassert dsm_request signal when the signal gets
+ * asserted unexpectedly.
+ */
+ gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
+ if (!IS_ERR(gpr))
+ regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT,
+ IMX6Q_GPR1_GINT);
+}
+
+void __init imx6q_pm_init(void)
+{
+ imx6_pm_common_init(&imx6q_pm_data);
+}
+
+void __init imx6dl_pm_init(void)
+{
+ imx6_pm_common_init(&imx6dl_pm_data);
+}
+
+void __init imx6sl_pm_init(void)
+{
+ imx6_pm_common_init(&imx6sl_pm_data);
+}
diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c
deleted file mode 100644
index 29e3fe6a6669..000000000000
--- a/arch/arm/mach-imx/pm-imx6q.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright 2011-2013 Freescale Semiconductor, Inc.
- * Copyright 2011 Linaro Ltd.
- *
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
-
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/regmap.h>
-#include <linux/suspend.h>
-#include <asm/cacheflush.h>
-#include <asm/proc-fns.h>
-#include <asm/suspend.h>
-#include <asm/hardware/cache-l2x0.h>
-
-#include "common.h"
-#include "hardware.h"
-
-#define CCR 0x0
-#define BM_CCR_WB_COUNT (0x7 << 16)
-#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21)
-#define BM_CCR_RBC_EN (0x1 << 27)
-
-#define CLPCR 0x54
-#define BP_CLPCR_LPM 0
-#define BM_CLPCR_LPM (0x3 << 0)
-#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2)
-#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5)
-#define BM_CLPCR_SBYOS (0x1 << 6)
-#define BM_CLPCR_DIS_REF_OSC (0x1 << 7)
-#define BM_CLPCR_VSTBY (0x1 << 8)
-#define BP_CLPCR_STBY_COUNT 9
-#define BM_CLPCR_STBY_COUNT (0x3 << 9)
-#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11)
-#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16)
-#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17)
-#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19)
-#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21)
-#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22)
-#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23)
-#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24)
-#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25)
-#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26)
-#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27)
-
-#define CGPR 0x64
-#define BM_CGPR_CHICKEN_BIT (0x1 << 17)
-
-static void __iomem *ccm_base;
-
-void imx6q_set_chicken_bit(void)
-{
- u32 val = readl_relaxed(ccm_base + CGPR);
-
- val |= BM_CGPR_CHICKEN_BIT;
- writel_relaxed(val, ccm_base + CGPR);
-}
-
-static void imx6q_enable_rbc(bool enable)
-{
- u32 val;
-
- /*
- * need to mask all interrupts in GPC before
- * operating RBC configurations
- */
- imx_gpc_mask_all();
-
- /* configure RBC enable bit */
- val = readl_relaxed(ccm_base + CCR);
- val &= ~BM_CCR_RBC_EN;
- val |= enable ? BM_CCR_RBC_EN : 0;
- writel_relaxed(val, ccm_base + CCR);
-
- /* configure RBC count */
- val = readl_relaxed(ccm_base + CCR);
- val &= ~BM_CCR_RBC_BYPASS_COUNT;
- val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
- writel(val, ccm_base + CCR);
-
- /*
- * need to delay at least 2 cycles of CKIL(32K)
- * due to hardware design requirement, which is
- * ~61us, here we use 65us for safe
- */
- udelay(65);
-
- /* restore GPC interrupt mask settings */
- imx_gpc_restore_all();
-}
-
-static void imx6q_enable_wb(bool enable)
-{
- u32 val;
-
- /* configure well bias enable bit */
- val = readl_relaxed(ccm_base + CLPCR);
- val &= ~BM_CLPCR_WB_PER_AT_LPM;
- val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
- writel_relaxed(val, ccm_base + CLPCR);
-
- /* configure well bias count */
- val = readl_relaxed(ccm_base + CCR);
- val &= ~BM_CCR_WB_COUNT;
- val |= enable ? BM_CCR_WB_COUNT : 0;
- writel_relaxed(val, ccm_base + CCR);
-}
-
-int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
-{
- struct irq_data *iomuxc_irq_data = irq_get_irq_data(32);
- u32 val = readl_relaxed(ccm_base + CLPCR);
-
- val &= ~BM_CLPCR_LPM;
- switch (mode) {
- case WAIT_CLOCKED:
- break;
- case WAIT_UNCLOCKED:
- val |= 0x1 << BP_CLPCR_LPM;
- val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
- break;
- case STOP_POWER_ON:
- val |= 0x2 << BP_CLPCR_LPM;
- break;
- case WAIT_UNCLOCKED_POWER_OFF:
- val |= 0x1 << BP_CLPCR_LPM;
- val &= ~BM_CLPCR_VSTBY;
- val &= ~BM_CLPCR_SBYOS;
- break;
- case STOP_POWER_OFF:
- val |= 0x2 << BP_CLPCR_LPM;
- val |= 0x3 << BP_CLPCR_STBY_COUNT;
- val |= BM_CLPCR_VSTBY;
- val |= BM_CLPCR_SBYOS;
- if (cpu_is_imx6sl()) {
- val |= BM_CLPCR_BYPASS_PMIC_READY;
- val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
- } else {
- val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
- }
- break;
- default:
- return -EINVAL;
- }
-
- /*
- * ERR007265: CCM: When improper low-power sequence is used,
- * the SoC enters low power mode before the ARM core executes WFI.
- *
- * Software workaround:
- * 1) Software should trigger IRQ #32 (IOMUX) to be always pending
- * by setting IOMUX_GPR1_GINT.
- * 2) Software should then unmask IRQ #32 in GPC before setting CCM
- * Low-Power mode.
- * 3) Software should mask IRQ #32 right after CCM Low-Power mode
- * is set (set bits 0-1 of CCM_CLPCR).
- */
- imx_gpc_irq_unmask(iomuxc_irq_data);
- writel_relaxed(val, ccm_base + CLPCR);
- imx_gpc_irq_mask(iomuxc_irq_data);
-
- return 0;
-}
-
-static int imx6q_suspend_finish(unsigned long val)
-{
- cpu_do_idle();
- return 0;
-}
-
-static int imx6q_pm_enter(suspend_state_t state)
-{
- switch (state) {
- case PM_SUSPEND_MEM:
- imx6q_set_lpm(STOP_POWER_OFF);
- imx6q_enable_wb(true);
- imx6q_enable_rbc(true);
- imx_gpc_pre_suspend();
- imx_anatop_pre_suspend();
- imx_set_cpu_jump(0, v7_cpu_resume);
- /* Zzz ... */
- cpu_suspend(0, imx6q_suspend_finish);
- if (cpu_is_imx6q() || cpu_is_imx6dl())
- imx_smp_prepare();
- imx_anatop_post_resume();
- imx_gpc_post_resume();
- imx6q_enable_rbc(false);
- imx6q_enable_wb(false);
- imx6q_set_lpm(WAIT_CLOCKED);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const struct platform_suspend_ops imx6q_pm_ops = {
- .enter = imx6q_pm_enter,
- .valid = suspend_valid_only_mem,
-};
-
-void __init imx6q_pm_set_ccm_base(void __iomem *base)
-{
- ccm_base = base;
-}
-
-void __init imx6q_pm_init(void)
-{
- struct regmap *gpr;
-
- WARN_ON(!ccm_base);
-
- /*
- * This is for SW workaround step #1 of ERR007265, see comments
- * in imx6q_set_lpm for details of this errata.
- * Force IOMUXC irq pending, so that the interrupt to GPC can be
- * used to deassert dsm_request signal when the signal gets
- * asserted unexpectedly.
- */
- gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
- if (!IS_ERR(gpr))
- regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT,
- IMX6Q_GPR1_GINT);
-
-
- suspend_set_ops(&imx6q_pm_ops);
-}
diff --git a/arch/arm/mach-imx/suspend-imx6.S b/arch/arm/mach-imx/suspend-imx6.S
new file mode 100644
index 000000000000..20048ff05739
--- /dev/null
+++ b/arch/arm/mach-imx/suspend-imx6.S
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/hardware/cache-l2x0.h>
+#include "hardware.h"
+
+/*
+ * ==================== low level suspend ====================
+ *
+ * Better to follow below rules to use ARM registers:
+ * r0: pm_info structure address;
+ * r1 ~ r4: for saving pm_info members;
+ * r5 ~ r10: free registers;
+ * r11: io base address.
+ *
+ * suspend ocram space layout:
+ * ======================== high address ======================
+ * .
+ * .
+ * .
+ * ^
+ * ^
+ * ^
+ * imx6_suspend code
+ * PM_INFO structure(imx6_cpu_pm_info)
+ * ======================== low address =======================
+ */
+
+/*
+ * Below offsets are based on struct imx6_cpu_pm_info
+ * which defined in arch/arm/mach-imx/pm-imx6q.c, this
+ * structure contains necessary pm info for low level
+ * suspend related code.
+ */
+#define PM_INFO_PBASE_OFFSET 0x0
+#define PM_INFO_RESUME_ADDR_OFFSET 0x4
+#define PM_INFO_CPU_TYPE_OFFSET 0x8
+#define PM_INFO_PM_INFO_SIZE_OFFSET 0xC
+#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10
+#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14
+#define PM_INFO_MX6Q_SRC_P_OFFSET 0x18
+#define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C
+#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20
+#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24
+#define PM_INFO_MX6Q_CCM_P_OFFSET 0x28
+#define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C
+#define PM_INFO_MX6Q_GPC_P_OFFSET 0x30
+#define PM_INFO_MX6Q_GPC_V_OFFSET 0x34
+#define PM_INFO_MX6Q_L2_P_OFFSET 0x38
+#define PM_INFO_MX6Q_L2_V_OFFSET 0x3C
+#define PM_INFO_MMDC_IO_NUM_OFFSET 0x40
+#define PM_INFO_MMDC_IO_VAL_OFFSET 0x44
+
+#define MX6Q_SRC_GPR1 0x20
+#define MX6Q_SRC_GPR2 0x24
+#define MX6Q_MMDC_MAPSR 0x404
+#define MX6Q_MMDC_MPDGCTRL0 0x83c
+#define MX6Q_GPC_IMR1 0x08
+#define MX6Q_GPC_IMR2 0x0c
+#define MX6Q_GPC_IMR3 0x10
+#define MX6Q_GPC_IMR4 0x14
+#define MX6Q_CCM_CCR 0x0
+
+ .align 3
+
+ .macro sync_l2_cache
+
+ /* sync L2 cache to drain L2's buffers to DRAM. */
+#ifdef CONFIG_CACHE_L2X0
+ ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
+ mov r6, #0x0
+ str r6, [r11, #L2X0_CACHE_SYNC]
+1:
+ ldr r6, [r11, #L2X0_CACHE_SYNC]
+ ands r6, r6, #0x1
+ bne 1b
+#endif
+
+ .endm
+
+ .macro resume_mmdc
+
+ /* restore MMDC IO */
+ cmp r5, #0x0
+ ldreq r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
+ ldrne r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
+
+ ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
+ ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
+ add r7, r7, r0
+1:
+ ldr r8, [r7], #0x4
+ ldr r9, [r7], #0x4
+ str r9, [r11, r8]
+ subs r6, r6, #0x1
+ bne 1b
+
+ cmp r5, #0x0
+ ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
+ ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
+
+ cmp r3, #MXC_CPU_IMX6SL
+ bne 4f
+
+ /* reset read FIFO, RST_RD_FIFO */
+ ldr r7, =MX6Q_MMDC_MPDGCTRL0
+ ldr r6, [r11, r7]
+ orr r6, r6, #(1 << 31)
+ str r6, [r11, r7]
+2:
+ ldr r6, [r11, r7]
+ ands r6, r6, #(1 << 31)
+ bne 2b
+
+ /* reset FIFO a second time */
+ ldr r6, [r11, r7]
+ orr r6, r6, #(1 << 31)
+ str r6, [r11, r7]
+3:
+ ldr r6, [r11, r7]
+ ands r6, r6, #(1 << 31)
+ bne 3b
+4:
+ /* let DDR out of self-refresh */
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ bic r7, r7, #(1 << 21)
+ str r7, [r11, #MX6Q_MMDC_MAPSR]
+5:
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ ands r7, r7, #(1 << 25)
+ bne 5b
+
+ /* enable DDR auto power saving */
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ bic r7, r7, #0x1
+ str r7, [r11, #MX6Q_MMDC_MAPSR]
+
+ .endm
+
+ENTRY(imx6_suspend)
+ ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
+ ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
+ ldr r3, [r0, #PM_INFO_CPU_TYPE_OFFSET]
+ ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
+
+ /*
+ * counting the resume address in iram
+ * to set it in SRC register.
+ */
+ ldr r6, =imx6_suspend
+ ldr r7, =resume
+ sub r7, r7, r6
+ add r8, r1, r4
+ add r9, r8, r7
+
+ /*
+ * make sure TLB contain the addr we want,
+ * as we will access them after MMDC IO floated.
+ */
+
+ ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
+ ldr r6, [r11, #0x0]
+ ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
+ ldr r6, [r11, #0x0]
+
+ /* use r11 to store the IO address */
+ ldr r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
+ /* store physical resume addr and pm_info address. */
+ str r9, [r11, #MX6Q_SRC_GPR1]
+ str r1, [r11, #MX6Q_SRC_GPR2]
+
+ /* need to sync L2 cache before DSM. */
+ sync_l2_cache
+
+ ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
+ /*
+ * put DDR explicitly into self-refresh and
+ * disable automatic power savings.
+ */
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ orr r7, r7, #0x1
+ str r7, [r11, #MX6Q_MMDC_MAPSR]
+
+ /* make the DDR explicitly enter self-refresh. */
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ orr r7, r7, #(1 << 21)
+ str r7, [r11, #MX6Q_MMDC_MAPSR]
+
+poll_dvfs_set:
+ ldr r7, [r11, #MX6Q_MMDC_MAPSR]
+ ands r7, r7, #(1 << 25)
+ beq poll_dvfs_set
+
+ ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
+ ldr r6, =0x0
+ ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
+ ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET
+ add r8, r8, r0
+ /* i.MX6SL's last 3 IOs need special setting */
+ cmp r3, #MXC_CPU_IMX6SL
+ subeq r7, r7, #0x3
+set_mmdc_io_lpm:
+ ldr r9, [r8], #0x8
+ str r6, [r11, r9]
+ subs r7, r7, #0x1
+ bne set_mmdc_io_lpm
+
+ cmp r3, #MXC_CPU_IMX6SL
+ bne set_mmdc_io_lpm_done
+ ldr r6, =0x1000
+ ldr r9, [r8], #0x8
+ str r6, [r11, r9]
+ ldr r9, [r8], #0x8
+ str r6, [r11, r9]
+ ldr r6, =0x80000
+ ldr r9, [r8]
+ str r6, [r11, r9]
+set_mmdc_io_lpm_done:
+
+ /*
+ * mask all GPC interrupts before
+ * enabling the RBC counters to
+ * avoid the counter starting too
+ * early if an interupt is already
+ * pending.
+ */
+ ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
+ ldr r6, [r11, #MX6Q_GPC_IMR1]
+ ldr r7, [r11, #MX6Q_GPC_IMR2]
+ ldr r8, [r11, #MX6Q_GPC_IMR3]
+ ldr r9, [r11, #MX6Q_GPC_IMR4]
+
+ ldr r10, =0xffffffff
+ str r10, [r11, #MX6Q_GPC_IMR1]
+ str r10, [r11, #MX6Q_GPC_IMR2]
+ str r10, [r11, #MX6Q_GPC_IMR3]
+ str r10, [r11, #MX6Q_GPC_IMR4]
+
+ /*
+ * enable the RBC bypass counter here
+ * to hold off the interrupts. RBC counter
+ * = 32 (1ms), Minimum RBC delay should be
+ * 400us for the analog LDOs to power down.
+ */
+ ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
+ ldr r10, [r11, #MX6Q_CCM_CCR]
+ bic r10, r10, #(0x3f << 21)
+ orr r10, r10, #(0x20 << 21)
+ str r10, [r11, #MX6Q_CCM_CCR]
+
+ /* enable the counter. */
+ ldr r10, [r11, #MX6Q_CCM_CCR]
+ orr r10, r10, #(0x1 << 27)
+ str r10, [r11, #MX6Q_CCM_CCR]
+
+ /* unmask all the GPC interrupts. */
+ ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
+ str r6, [r11, #MX6Q_GPC_IMR1]
+ str r7, [r11, #MX6Q_GPC_IMR2]
+ str r8, [r11, #MX6Q_GPC_IMR3]
+ str r9, [r11, #MX6Q_GPC_IMR4]
+
+ /*
+ * now delay for a short while (3usec)
+ * ARM is at 1GHz at this point
+ * so a short loop should be enough.
+ * this delay is required to ensure that
+ * the RBC counter can start counting in
+ * case an interrupt is already pending
+ * or in case an interrupt arrives just
+ * as ARM is about to assert DSM_request.
+ */
+ ldr r6, =2000
+rbc_loop:
+ subs r6, r6, #0x1
+ bne rbc_loop
+
+ /* Zzz, enter stop mode */
+ wfi
+ nop
+ nop
+ nop
+ nop
+
+ /*
+ * run to here means there is pending
+ * wakeup source, system should auto
+ * resume, we need to restore MMDC IO first
+ */
+ mov r5, #0x0
+ resume_mmdc
+
+ /* return to suspend finish */
+ mov pc, lr
+
+resume:
+ /* invalidate L1 I-cache first */
+ mov r6, #0x0
+ mcr p15, 0, r6, c7, c5, 0
+ mcr p15, 0, r6, c7, c5, 6
+ /* enable the Icache and branch prediction */
+ mov r6, #0x1800
+ mcr p15, 0, r6, c1, c0, 0
+ isb
+
+ /* get physical resume address from pm_info. */
+ ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
+ /* clear core0's entry and parameter */
+ ldr r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
+ mov r7, #0x0
+ str r7, [r11, #MX6Q_SRC_GPR1]
+ str r7, [r11, #MX6Q_SRC_GPR2]
+
+ ldr r3, [r0, #PM_INFO_CPU_TYPE_OFFSET]
+ mov r5, #0x1
+ resume_mmdc
+
+ mov pc, lr
+ENDPROC(imx6_suspend)
+
+/*
+ * The following code must assume it is running from physical address
+ * where absolute virtual addresses to the data section have to be
+ * turned into relative ones.
+ */
+
+#ifdef CONFIG_CACHE_L2X0
+ .macro pl310_resume
+ adr r0, l2x0_saved_regs_offset
+ ldr r2, [r0]
+ add r2, r2, r0
+ ldr r0, [r2, #L2X0_R_PHY_BASE] @ get physical base of l2x0
+ ldr r1, [r2, #L2X0_R_AUX_CTRL] @ get aux_ctrl value
+ str r1, [r0, #L2X0_AUX_CTRL] @ restore aux_ctrl
+ mov r1, #0x1
+ str r1, [r0, #L2X0_CTRL] @ re-enable L2
+ .endm
+
+l2x0_saved_regs_offset:
+ .word l2x0_saved_regs - .
+
+#else
+ .macro pl310_resume
+ .endm
+#endif
+
+ENTRY(v7_cpu_resume)
+ bl v7_invalidate_l1
+ pl310_resume
+ b cpu_resume
+ENDPROC(v7_cpu_resume)
diff --git a/arch/arm/mach-imx/time.c b/arch/arm/mach-imx/time.c
index 1a3a5f615770..65222ea0df6d 100644
--- a/arch/arm/mach-imx/time.c
+++ b/arch/arm/mach-imx/time.c
@@ -25,6 +25,7 @@
#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/sched_clock.h>
@@ -116,11 +117,22 @@ static u64 notrace mxc_read_sched_clock(void)
return sched_clock_reg ? __raw_readl(sched_clock_reg) : 0;
}
+static struct delay_timer imx_delay_timer;
+
+static unsigned long imx_read_current_timer(void)
+{
+ return __raw_readl(sched_clock_reg);
+}
+
static int __init mxc_clocksource_init(struct clk *timer_clk)
{
unsigned int c = clk_get_rate(timer_clk);
void __iomem *reg = timer_base + (timer_is_v2() ? V2_TCN : MX1_2_TCN);
+ imx_delay_timer.read_current_timer = &imx_read_current_timer;
+ imx_delay_timer.freq = c;
+ register_current_timer_delay(&imx_delay_timer);
+
sched_clock_reg = reg;
sched_clock_register(mxc_read_sched_clock, 32, c);
diff --git a/arch/arm/mach-integrator/Kconfig b/arch/arm/mach-integrator/Kconfig
index abeff25532ab..ba43321001d8 100644
--- a/arch/arm/mach-integrator/Kconfig
+++ b/arch/arm/mach-integrator/Kconfig
@@ -6,8 +6,8 @@ config ARCH_INTEGRATOR_AP
bool "Support Integrator/AP and Integrator/PP2 platforms"
select CLKSRC_MMIO
select MIGHT_HAVE_PCI
- select SERIAL_AMBA_PL010
- select SERIAL_AMBA_PL010_CONSOLE
+ select SERIAL_AMBA_PL010 if TTY
+ select SERIAL_AMBA_PL010_CONSOLE if TTY
select SOC_BUS
help
Include support for the ARM(R) Integrator/AP and
@@ -18,8 +18,8 @@ config ARCH_INTEGRATOR_CP
select ARCH_CINTEGRATOR
select ARM_TIMER_SP804
select PLAT_VERSATILE_CLCD
- select SERIAL_AMBA_PL011
- select SERIAL_AMBA_PL011_CONSOLE
+ select SERIAL_AMBA_PL011 if TTY
+ select SERIAL_AMBA_PL011_CONSOLE if TTY
select SOC_BUS
help
Include support for the ARM(R) Integrator CP platform.
@@ -30,6 +30,9 @@ config ARCH_CINTEGRATOR
config INTEGRATOR_IMPD1
tristate "Include support for Integrator/IM-PD1"
depends on ARCH_INTEGRATOR_AP
+ select ARCH_REQUIRE_GPIOLIB
+ select ARM_VIC
+ select GPIO_PL061 if GPIOLIB
help
The IM-PD1 is an add-on logic module for the Integrator which
allows ARM(R) Ltd PrimeCells to be developed and evaluated.
diff --git a/arch/arm/mach-integrator/core.c b/arch/arm/mach-integrator/core.c
index 00ddf20ed91b..e3f3aca43efb 100644
--- a/arch/arm/mach-integrator/core.c
+++ b/arch/arm/mach-integrator/core.c
@@ -25,13 +25,11 @@
#include <linux/of.h>
#include <linux/of_address.h>
-#include <mach/hardware.h>
-#include <mach/platform.h>
-
#include <asm/mach-types.h>
#include <asm/mach/time.h>
#include <asm/pgtable.h>
+#include "hardware.h"
#include "cm.h"
#include "common.h"
diff --git a/arch/arm/mach-integrator/include/mach/platform.h b/arch/arm/mach-integrator/hardware.h
index 306d025d9730..857ca5f8b9a6 100644
--- a/arch/arm/mach-integrator/include/mach/platform.h
+++ b/arch/arm/mach-integrator/hardware.h
@@ -1,4 +1,8 @@
/*
+ * This file contains the hardware definitions of the Integrator.
+ *
+ * Copyright (C) 1998-1999 ARM 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
@@ -13,26 +17,28 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-/**************************************************************************
- * * Copyright © ARM Limited 1998. All rights reserved.
- * ***********************************************************************/
-/* ************************************************************************
- *
- * Integrator address map
- *
- * ***********************************************************************/
+#ifndef INTEGRATOR_HARDWARE_H
+#define INTEGRATOR_HARDWARE_H
-#ifndef __address_h
-#define __address_h 1
+/*
+ * Where in virtual memory the IO devices (timers, system controllers
+ * and so on)
+ */
+#define IO_BASE 0xF0000000 // VA of IO
+#define IO_SIZE 0x0B000000 // How much?
+#define IO_START INTEGRATOR_HDR_BASE // PA of IO
+
+/* macro to get at IO space when running virtually */
+#ifdef CONFIG_MMU
+#define IO_ADDRESS(x) (((x) & 0x000fffff) | (((x) >> 4) & 0x0ff00000) | IO_BASE)
+#else
+#define IO_ADDRESS(x) (x)
+#endif
-/* ========================================================================
- * Integrator definitions
- * ========================================================================
- * ------------------------------------------------------------------------
- * Memory definitions
- * ------------------------------------------------------------------------
+#define __io_address(n) ((void __iomem *)IO_ADDRESS(n))
+
+/*
* Integrator memory map
- *
*/
#define INTEGRATOR_BOOT_ROM_LO 0x00000000
#define INTEGRATOR_BOOT_ROM_HI 0x20000000
@@ -40,13 +46,13 @@
#define INTEGRATOR_BOOT_ROM_SIZE SZ_512K
/*
- * New Core Modules have different amounts of SSRAM, the amount of SSRAM
- * fitted can be found in HDR_STAT.
+ * New Core Modules have different amounts of SSRAM, the amount of SSRAM
+ * fitted can be found in HDR_STAT.
*
- * The symbol INTEGRATOR_SSRAM_SIZE is kept, however this now refers to
- * the minimum amount of SSRAM fitted on any core module.
+ * The symbol INTEGRATOR_SSRAM_SIZE is kept, however this now refers to
+ * the minimum amount of SSRAM fitted on any core module.
*
- * New Core Modules also alias the SSRAM.
+ * New Core Modules also alias the SSRAM.
*
*/
#define INTEGRATOR_SSRAM_BASE 0x00000000
@@ -61,7 +67,6 @@
/*
* SDRAM is a SIMM therefore the size is not known.
- *
*/
#define INTEGRATOR_SDRAM_BASE 0x00040000
@@ -81,10 +86,8 @@
#define INTEGRATOR_LOGIC_MODULE2_BASE 0xE0000000
#define INTEGRATOR_LOGIC_MODULE3_BASE 0xF0000000
-/* ------------------------------------------------------------------------
- * Integrator header card registers
- * ------------------------------------------------------------------------
- *
+/*
+ * Integrator header card registers
*/
#define INTEGRATOR_HDR_ID_OFFSET 0x00
#define INTEGRATOR_HDR_PROC_OFFSET 0x04
@@ -173,16 +176,12 @@
#define INTEGRATOR_HDR_SDRAM_SPD_OK (1 << 5)
-
-/* ------------------------------------------------------------------------
- * Integrator system registers
- * ------------------------------------------------------------------------
- *
+/*
+ * Integrator system registers
*/
/*
* System Controller
- *
*/
#define INTEGRATOR_SC_ID_OFFSET 0x00
#define INTEGRATOR_SC_OSC_OFFSET 0x04
@@ -223,7 +222,6 @@
/*
* External Bus Interface
- *
*/
#define INTEGRATOR_EBI_BASE 0x12000000
@@ -272,7 +270,6 @@
/*
* LED's & Switches
- *
*/
#define INTEGRATOR_DBG_ALPHA_OFFSET 0x00
#define INTEGRATOR_DBG_LEDS_OFFSET 0x04
@@ -292,32 +289,25 @@
#define INTEGRATOR_CP_SIC_BASE 0xCA000000 /* SIC */
#define INTEGRATOR_CP_CTL_BASE 0xCB000000 /* CP system control */
-/* ------------------------------------------------------------------------
- * KMI keyboard/mouse definitions
- * ------------------------------------------------------------------------
- */
/* PS2 Keyboard interface */
#define KMI0_BASE INTEGRATOR_KBD_BASE
/* PS2 Mouse interface */
#define KMI1_BASE INTEGRATOR_MOUSE_BASE
-/* KMI definitions are now in include/asm-arm/hardware/amba_kmi.h -- rmk */
-
-/* ------------------------------------------------------------------------
- * Integrator Interrupt Controllers
- * ------------------------------------------------------------------------
+/*
+ * Integrator Interrupt Controllers
*
- * Offsets from interrupt controller base
*
- * System Controller interrupt controller base is
+ * Offsets from interrupt controller base
+ *
+ * System Controller interrupt controller base is
*
* INTEGRATOR_IC_BASE + (header_number << 6)
*
- * Core Module interrupt controller base is
+ * Core Module interrupt controller base is
*
* INTEGRATOR_HDR_IC
- *
*/
#define IRQ_STATUS 0
#define IRQ_RAW_STATUS 0x04
@@ -335,25 +325,8 @@
#define FIQ_ENABLE_CLEAR 0x2C
-/* ------------------------------------------------------------------------
- * Interrupts
- * ------------------------------------------------------------------------
- *
- *
- * Each Core Module has two interrupts controllers, one on the core module
- * itself and one in the system controller on the motherboard. The
- * READ_INT macro in target.s reads both interrupt controllers and returns
- * a 32 bit bitmask, bits 0 to 23 are interrupts from the system controller
- * and bits 24 to 31 are from the core module.
- *
- * The following definitions relate to the bitmask returned by READ_INT.
- *
- */
-
-/* ------------------------------------------------------------------------
- * LED's
- * ------------------------------------------------------------------------
- *
+/*
+ * LED's
*/
#define GREEN_LED 0x01
#define YELLOW_LED 0x02
@@ -371,7 +344,6 @@
*
* Timer 0 runs at bus frequency
*/
-
#define INTEGRATOR_TIMER0_BASE INTEGRATOR_CT_BASE
#define INTEGRATOR_TIMER1_BASE (INTEGRATOR_CT_BASE + 0x100)
#define INTEGRATOR_TIMER2_BASE (INTEGRATOR_CT_BASE + 0x200)
@@ -379,4 +351,4 @@
#define INTEGRATOR_CSR_BASE 0x10000000
#define INTEGRATOR_CSR_SIZE 0x10000000
-#endif
+#endif /* INTEGRATOR_HARDWARE_H */
diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c
index 9f82f9dcbb98..0e870ea818c4 100644
--- a/arch/arm/mach-integrator/impd1.c
+++ b/arch/arm/mach-integrator/impd1.c
@@ -23,10 +23,11 @@
#include <linux/io.h>
#include <linux/platform_data/clk-integrator.h>
#include <linux/slab.h>
+#include <linux/irqchip/arm-vic.h>
-#include <mach/lm.h>
-#include <mach/impd1.h>
#include <asm/sizes.h>
+#include "lm.h"
+#include "impd1.h"
static int module_id;
@@ -35,6 +36,7 @@ MODULE_PARM_DESC(lmid, "logic module stack position");
struct impd1_module {
void __iomem *base;
+ void __iomem *vic_base;
};
void impd1_tweak_control(struct device *dev, u32 mask, u32 val)
@@ -262,9 +264,6 @@ struct impd1_device {
static struct impd1_device impd1_devs[] = {
{
- .offset = 0x03000000,
- .id = 0x00041190,
- }, {
.offset = 0x00100000,
.irq = { 1 },
.id = 0x00141011,
@@ -304,46 +303,72 @@ static struct impd1_device impd1_devs[] = {
}
};
-static int impd1_probe(struct lm_device *dev)
+/*
+ * Valid IRQs: 0 thru 9 and 11, 10 unused.
+ */
+#define IMPD1_VALID_IRQS 0x00000bffU
+
+static int __init impd1_probe(struct lm_device *dev)
{
struct impd1_module *impd1;
- int i, ret;
+ int irq_base;
+ int i;
if (dev->id != module_id)
return -EINVAL;
- if (!request_mem_region(dev->resource.start, SZ_4K, "LM registers"))
+ if (!devm_request_mem_region(&dev->dev, dev->resource.start,
+ SZ_4K, "LM registers"))
return -EBUSY;
- impd1 = kzalloc(sizeof(struct impd1_module), GFP_KERNEL);
- if (!impd1) {
- ret = -ENOMEM;
- goto release_lm;
- }
+ impd1 = devm_kzalloc(&dev->dev, sizeof(struct impd1_module),
+ GFP_KERNEL);
+ if (!impd1)
+ return -ENOMEM;
- impd1->base = ioremap(dev->resource.start, SZ_4K);
- if (!impd1->base) {
- ret = -ENOMEM;
- goto free_impd1;
- }
+ impd1->base = devm_ioremap(&dev->dev, dev->resource.start, SZ_4K);
+ if (!impd1->base)
+ return -ENOMEM;
- lm_set_drvdata(dev, impd1);
+ integrator_impd1_clk_init(impd1->base, dev->id);
- printk("IM-PD1 found at 0x%08lx\n",
- (unsigned long)dev->resource.start);
+ if (!devm_request_mem_region(&dev->dev,
+ dev->resource.start + 0x03000000,
+ SZ_4K, "VIC"))
+ return -EBUSY;
- integrator_impd1_clk_init(impd1->base, dev->id);
+ impd1->vic_base = devm_ioremap(&dev->dev,
+ dev->resource.start + 0x03000000,
+ SZ_4K);
+ if (!impd1->vic_base)
+ return -ENOMEM;
+
+ irq_base = vic_init_cascaded(impd1->vic_base, dev->irq,
+ IMPD1_VALID_IRQS, 0);
+
+ lm_set_drvdata(dev, impd1);
+
+ dev_info(&dev->dev, "IM-PD1 found at 0x%08lx\n",
+ (unsigned long)dev->resource.start);
for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {
struct impd1_device *idev = impd1_devs + i;
struct amba_device *d;
unsigned long pc_base;
char devname[32];
+ int irq1 = idev->irq[0];
+ int irq2 = idev->irq[1];
+
+ /* Translate IRQs to IM-PD1 local numberspace */
+ if (irq1)
+ irq1 += irq_base;
+ if (irq2)
+ irq2 += irq_base;
pc_base = dev->resource.start + idev->offset;
snprintf(devname, 32, "lm%x:%5.5lx", dev->id, idev->offset >> 12);
d = amba_ahb_device_add_res(&dev->dev, devname, pc_base, SZ_4K,
- dev->irq, dev->irq,
+ irq1, irq2,
idev->platform_data, idev->id,
&dev->resource);
if (IS_ERR(d)) {
@@ -353,14 +378,6 @@ static int impd1_probe(struct lm_device *dev)
}
return 0;
-
- free_impd1:
- if (impd1 && impd1->base)
- iounmap(impd1->base);
- kfree(impd1);
- release_lm:
- release_mem_region(dev->resource.start, SZ_4K);
- return ret;
}
static int impd1_remove_one(struct device *dev, void *data)
@@ -371,16 +388,10 @@ static int impd1_remove_one(struct device *dev, void *data)
static void impd1_remove(struct lm_device *dev)
{
- struct impd1_module *impd1 = lm_get_drvdata(dev);
-
device_for_each_child(&dev->dev, NULL, impd1_remove_one);
integrator_impd1_clk_exit(dev->id);
lm_set_drvdata(dev, NULL);
-
- iounmap(impd1->base);
- kfree(impd1);
- release_mem_region(dev->resource.start, SZ_4K);
}
static struct lm_driver impd1_driver = {
diff --git a/arch/arm/mach-integrator/include/mach/impd1.h b/arch/arm/mach-integrator/impd1.h
index d75de4b14237..76de4dc9bee4 100644
--- a/arch/arm/mach-integrator/include/mach/impd1.h
+++ b/arch/arm/mach-integrator/impd1.h
@@ -1,6 +1,3 @@
-#define IMPD1_OSC1 0x00
-#define IMPD1_OSC2 0x04
-#define IMPD1_LOCK 0x08
#define IMPD1_LEDS 0x0c
#define IMPD1_INT 0x10
#define IMPD1_SW 0x14
@@ -15,4 +12,3 @@
struct device;
void impd1_tweak_control(struct device *dev, u32 mask, u32 val);
-
diff --git a/arch/arm/mach-integrator/include/mach/hardware.h b/arch/arm/mach-integrator/include/mach/hardware.h
deleted file mode 100644
index 65fed7c0eb84..000000000000
--- a/arch/arm/mach-integrator/include/mach/hardware.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * arch/arm/mach-integrator/include/mach/hardware.h
- *
- * This file contains the hardware definitions of the Integrator.
- *
- * Copyright (C) 1999 ARM 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __ASM_ARCH_HARDWARE_H
-#define __ASM_ARCH_HARDWARE_H
-
-#include <asm/sizes.h>
-
-/*
- * Where in virtual memory the IO devices (timers, system controllers
- * and so on)
- */
-#define IO_BASE 0xF0000000 // VA of IO
-#define IO_SIZE 0x0B000000 // How much?
-#define IO_START INTEGRATOR_HDR_BASE // PA of IO
-
-/* macro to get at IO space when running virtually */
-#ifdef CONFIG_MMU
-#define IO_ADDRESS(x) (((x) & 0x000fffff) | (((x) >> 4) & 0x0ff00000) | IO_BASE)
-#else
-#define IO_ADDRESS(x) (x)
-#endif
-
-#define __io_address(n) ((void __iomem *)IO_ADDRESS(n))
-
-#endif
-
diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c
index 17c0fe627435..dd0cc677d596 100644
--- a/arch/arm/mach-integrator/integrator_ap.c
+++ b/arch/arm/mach-integrator/integrator_ap.c
@@ -42,24 +42,23 @@
#include <linux/sys_soc.h>
#include <linux/termios.h>
#include <linux/sched_clock.h>
+#include <linux/clk-provider.h>
-#include <mach/hardware.h>
-#include <mach/platform.h>
#include <asm/hardware/arm_timer.h>
#include <asm/setup.h>
#include <asm/param.h> /* HZ */
#include <asm/mach-types.h>
-#include <mach/lm.h>
-
#include <asm/mach/arch.h>
#include <asm/mach/irq.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
+#include "hardware.h"
#include "cm.h"
#include "common.h"
#include "pci_v3.h"
+#include "lm.h"
/* Base address to the AP system controller */
void __iomem *ap_syscon_base;
@@ -358,7 +357,7 @@ static struct clock_event_device integrator_clockevent = {
static struct irqaction integrator_timer_irq = {
.name = "timer",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = integrator_timer_interrupt,
.dev_id = &integrator_clockevent,
};
@@ -402,10 +401,7 @@ static void __init ap_of_timer_init(void)
struct clk *clk;
unsigned long rate;
- clk = clk_get_sys("ap_timer", NULL);
- BUG_ON(IS_ERR(clk));
- clk_prepare_enable(clk);
- rate = clk_get_rate(clk);
+ of_clk_init(NULL);
err = of_property_read_string(of_aliases,
"arm,timer-primary", &path);
@@ -415,6 +411,12 @@ static void __init ap_of_timer_init(void)
base = of_iomap(node, 0);
if (WARN_ON(!base))
return;
+
+ clk = of_clk_get(node, 0);
+ BUG_ON(IS_ERR(clk));
+ clk_prepare_enable(clk);
+ rate = clk_get_rate(clk);
+
writel(0, base + TIMER_CTRL);
integrator_clocksource_init(rate, base);
@@ -427,6 +429,12 @@ static void __init ap_of_timer_init(void)
if (WARN_ON(!base))
return;
irq = irq_of_parse_and_map(node, 0);
+
+ clk = of_clk_get(node, 0);
+ BUG_ON(IS_ERR(clk));
+ clk_prepare_enable(clk);
+ rate = clk_get_rate(clk);
+
writel(0, base + TIMER_CTRL);
integrator_clockevent_init(rate, base, irq);
}
@@ -440,7 +448,6 @@ static void __init ap_init_irq_of(void)
{
cm_init();
of_irq_init(fpga_irq_of_match);
- integrator_clk_init(false);
}
/* For the Device Tree, add in the UART callbacks as AUXDATA */
diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c
index a3ef961e4a93..a938242b0c95 100644
--- a/arch/arm/mach-integrator/integrator_cp.c
+++ b/arch/arm/mach-integrator/integrator_cp.c
@@ -23,31 +23,22 @@
#include <linux/irqchip/versatile-fpga.h>
#include <linux/gfp.h>
#include <linux/mtd/physmap.h>
-#include <linux/platform_data/clk-integrator.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/sys_soc.h>
+#include <linux/sched_clock.h>
-#include <mach/hardware.h>
-#include <mach/platform.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
-#include <asm/hardware/arm_timer.h>
-#include <asm/hardware/icst.h>
-
-#include <mach/lm.h>
-
#include <asm/mach/arch.h>
#include <asm/mach/irq.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
-#include <asm/hardware/timer-sp.h>
-
#include <plat/clcd.h>
-#include <plat/sched_clock.h>
+#include "hardware.h"
#include "cm.h"
#include "common.h"
@@ -234,11 +225,14 @@ static struct clcd_board clcd_data = {
#define REFCOUNTER (__io_address(INTEGRATOR_HDR_BASE) + 0x28)
+static u64 notrace intcp_read_sched_clock(void)
+{
+ return readl(REFCOUNTER);
+}
+
static void __init intcp_init_early(void)
{
-#ifdef CONFIG_PLAT_VERSATILE_SCHED_CLOCK
- versatile_sched_clock_init(REFCOUNTER, 24000000);
-#endif
+ sched_clock_register(intcp_read_sched_clock, 32, 24000000);
}
static const struct of_device_id fpga_irq_of_match[] __initconst = {
@@ -250,7 +244,6 @@ static void __init intcp_init_irq_of(void)
{
cm_init();
of_irq_init(fpga_irq_of_match);
- integrator_clk_init(true);
}
/*
diff --git a/arch/arm/mach-integrator/leds.c b/arch/arm/mach-integrator/leds.c
index cb6ac58f5e07..f1dcb57a59e2 100644
--- a/arch/arm/mach-integrator/leds.c
+++ b/arch/arm/mach-integrator/leds.c
@@ -11,9 +11,7 @@
#include <linux/slab.h>
#include <linux/leds.h>
-#include <mach/hardware.h>
-#include <mach/platform.h>
-
+#include "hardware.h"
#include "cm.h"
#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
diff --git a/arch/arm/mach-integrator/lm.c b/arch/arm/mach-integrator/lm.c
index f52c7af31eaa..3f9e9f043168 100644
--- a/arch/arm/mach-integrator/lm.c
+++ b/arch/arm/mach-integrator/lm.c
@@ -12,7 +12,7 @@
#include <linux/device.h>
#include <linux/slab.h>
-#include <mach/lm.h>
+#include "lm.h"
#define to_lm_device(d) container_of(d, struct lm_device, dev)
#define to_lm_driver(d) container_of(d, struct lm_driver, drv)
diff --git a/arch/arm/mach-integrator/include/mach/lm.h b/arch/arm/mach-integrator/lm.h
index 28186b6f2c09..28186b6f2c09 100644
--- a/arch/arm/mach-integrator/include/mach/lm.h
+++ b/arch/arm/mach-integrator/lm.h
diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c
index c5e01b24d9fb..05e1f73a1e8d 100644
--- a/arch/arm/mach-integrator/pci_v3.c
+++ b/arch/arm/mach-integrator/pci_v3.c
@@ -34,15 +34,13 @@
#include <linux/of_pci.h>
#include <video/vga.h>
-#include <mach/hardware.h>
-#include <mach/platform.h>
-
#include <asm/mach/map.h>
#include <asm/signal.h>
#include <asm/mach/pci.h>
#include <asm/irq_regs.h>
#include "pci_v3.h"
+#include "hardware.h"
/*
* Where in the memory map does PCI live?
diff --git a/arch/arm/mach-iop13xx/include/mach/timex.h b/arch/arm/mach-iop13xx/include/mach/timex.h
deleted file mode 100644
index 45fb2745bb54..000000000000
--- a/arch/arm/mach-iop13xx/include/mach/timex.h
+++ /dev/null
@@ -1 +0,0 @@
-#define CLOCK_TICK_RATE (100 * HZ)
diff --git a/arch/arm/mach-iop32x/include/mach/timex.h b/arch/arm/mach-iop32x/include/mach/timex.h
deleted file mode 100644
index 7262ab81419d..000000000000
--- a/arch/arm/mach-iop32x/include/mach/timex.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
- * arch/arm/mach-iop32x/include/mach/timex.h
- *
- * IOP32x architecture timex specifications
- */
-#define CLOCK_TICK_RATE (100 * HZ)
diff --git a/arch/arm/mach-iop33x/include/mach/timex.h b/arch/arm/mach-iop33x/include/mach/timex.h
deleted file mode 100644
index 54c589091d6e..000000000000
--- a/arch/arm/mach-iop33x/include/mach/timex.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
- * arch/arm/mach-iop33x/include/mach/timex.h
- *
- * IOP3xx architecture timex specifications
- */
-#define CLOCK_TICK_RATE (100 * HZ)
diff --git a/arch/arm/mach-ixp4xx/common-pci.c b/arch/arm/mach-ixp4xx/common-pci.c
index 200970d56f6d..4977296f0c78 100644
--- a/arch/arm/mach-ixp4xx/common-pci.c
+++ b/arch/arm/mach-ixp4xx/common-pci.c
@@ -315,33 +315,6 @@ static int abort_handler(unsigned long addr, unsigned int fsr, struct pt_regs *r
return 0;
}
-
-static int ixp4xx_needs_bounce(struct device *dev, dma_addr_t dma_addr, size_t size)
-{
- return (dma_addr + size) >= SZ_64M;
-}
-
-/*
- * Setup DMA mask to 64MB on PCI devices. Ignore all other devices.
- */
-static int ixp4xx_pci_platform_notify(struct device *dev)
-{
- if (dev_is_pci(dev)) {
- *dev->dma_mask = SZ_64M - 1;
- dev->coherent_dma_mask = SZ_64M - 1;
- dmabounce_register_dev(dev, 2048, 4096, ixp4xx_needs_bounce);
- }
- return 0;
-}
-
-static int ixp4xx_pci_platform_notify_remove(struct device *dev)
-{
- if (dev_is_pci(dev))
- dmabounce_unregister_dev(dev);
-
- return 0;
-}
-
void __init ixp4xx_pci_preinit(void)
{
unsigned long cpuid = read_cpuid_id();
@@ -475,20 +448,8 @@ int ixp4xx_setup(int nr, struct pci_sys_data *sys)
pci_add_resource_offset(&sys->resources, &res[0], sys->io_offset);
pci_add_resource_offset(&sys->resources, &res[1], sys->mem_offset);
- platform_notify = ixp4xx_pci_platform_notify;
- platform_notify_remove = ixp4xx_pci_platform_notify_remove;
-
return 1;
}
-int dma_set_coherent_mask(struct device *dev, u64 mask)
-{
- if (mask >= SZ_64M - 1)
- return 0;
-
- return -EIO;
-}
-
EXPORT_SYMBOL(ixp4xx_pci_read);
EXPORT_SYMBOL(ixp4xx_pci_write);
-EXPORT_SYMBOL(dma_set_coherent_mask);
diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c
index 6d68aed6548a..fc4b7b24265e 100644
--- a/arch/arm/mach-ixp4xx/common.c
+++ b/arch/arm/mach-ixp4xx/common.c
@@ -23,15 +23,14 @@
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/time.h>
-#include <linux/timex.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/io.h>
#include <linux/export.h>
#include <linux/gpio.h>
#include <linux/cpu.h>
+#include <linux/pci.h>
#include <linux/sched_clock.h>
-
#include <mach/udc.h>
#include <mach/hardware.h>
#include <mach/io.h>
@@ -40,11 +39,21 @@
#include <asm/page.h>
#include <asm/irq.h>
#include <asm/system_misc.h>
-
#include <asm/mach/map.h>
#include <asm/mach/irq.h>
#include <asm/mach/time.h>
+#define IXP4XX_TIMER_FREQ 66666000
+
+/*
+ * The timer register doesn't allow to specify the two least significant bits of
+ * the timeout value and assumes them being zero. So make sure IXP4XX_LATCH is
+ * the best value with the two least significant bits unset.
+ */
+#define IXP4XX_LATCH DIV_ROUND_CLOSEST(IXP4XX_TIMER_FREQ, \
+ (IXP4XX_OST_RELOAD_MASK + 1) * HZ) * \
+ (IXP4XX_OST_RELOAD_MASK + 1)
+
static void __init ixp4xx_clocksource_init(void);
static void __init ixp4xx_clockevent_init(void);
static struct clock_event_device clockevent_ixp4xx;
@@ -312,7 +321,7 @@ static irqreturn_t ixp4xx_timer_interrupt(int irq, void *dev_id)
static struct irqaction ixp4xx_timer_irq = {
.name = "timer1",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = ixp4xx_timer_interrupt,
.dev_id = &clockevent_ixp4xx,
};
@@ -520,7 +529,7 @@ static void ixp4xx_set_mode(enum clock_event_mode mode,
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
- osrt = LATCH & ~IXP4XX_OST_RELOAD_MASK;
+ osrt = IXP4XX_LATCH & ~IXP4XX_OST_RELOAD_MASK;
opts = IXP4XX_OST_ENABLE;
break;
case CLOCK_EVT_MODE_ONESHOT:
@@ -578,6 +587,54 @@ void ixp4xx_restart(enum reboot_mode mode, const char *cmd)
}
}
+#ifdef CONFIG_PCI
+static int ixp4xx_needs_bounce(struct device *dev, dma_addr_t dma_addr, size_t size)
+{
+ return (dma_addr + size) > SZ_64M;
+}
+
+static int ixp4xx_platform_notify_remove(struct device *dev)
+{
+ if (dev_is_pci(dev))
+ dmabounce_unregister_dev(dev);
+
+ return 0;
+}
+#endif
+
+/*
+ * Setup DMA mask to 64MB on PCI devices and 4 GB on all other things.
+ */
+static int ixp4xx_platform_notify(struct device *dev)
+{
+ dev->dma_mask = &dev->coherent_dma_mask;
+
+#ifdef CONFIG_PCI
+ if (dev_is_pci(dev)) {
+ dev->coherent_dma_mask = DMA_BIT_MASK(28); /* 64 MB */
+ dmabounce_register_dev(dev, 2048, 4096, ixp4xx_needs_bounce);
+ return 0;
+ }
+#endif
+
+ dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return 0;
+}
+
+int dma_set_coherent_mask(struct device *dev, u64 mask)
+{
+ if (dev_is_pci(dev))
+ mask &= DMA_BIT_MASK(28); /* 64 MB */
+
+ if ((mask & DMA_BIT_MASK(28)) == DMA_BIT_MASK(28)) {
+ dev->coherent_dma_mask = mask;
+ return 0;
+ }
+
+ return -EIO; /* device wanted sub-64MB mask */
+}
+EXPORT_SYMBOL(dma_set_coherent_mask);
+
#ifdef CONFIG_IXP4XX_INDIRECT_PCI
/*
* In the case of using indirect PCI, we simply return the actual PCI
@@ -600,12 +657,16 @@ static void ixp4xx_iounmap(void __iomem *addr)
if (!is_pci_memory((__force u32)addr))
__iounmap(addr);
}
+#endif
void __init ixp4xx_init_early(void)
{
+ platform_notify = ixp4xx_platform_notify;
+#ifdef CONFIG_PCI
+ platform_notify_remove = ixp4xx_platform_notify_remove;
+#endif
+#ifdef CONFIG_IXP4XX_INDIRECT_PCI
arch_ioremap_caller = ixp4xx_ioremap_caller;
arch_iounmap = ixp4xx_iounmap;
-}
-#else
-void __init ixp4xx_init_early(void) {}
#endif
+}
diff --git a/arch/arm/mach-ixp4xx/dsmg600-setup.c b/arch/arm/mach-ixp4xx/dsmg600-setup.c
index 736dc692d540..43ee06d3abe5 100644
--- a/arch/arm/mach-ixp4xx/dsmg600-setup.c
+++ b/arch/arm/mach-ixp4xx/dsmg600-setup.c
@@ -233,8 +233,7 @@ static int __init dsmg600_gpio_init(void)
gpio_request(DSMG600_RB_GPIO, "reset button");
if (request_irq(gpio_to_irq(DSMG600_RB_GPIO), &dsmg600_reset_handler,
- IRQF_DISABLED | IRQF_TRIGGER_LOW,
- "DSM-G600 reset button", NULL) < 0) {
+ IRQF_TRIGGER_LOW, "DSM-G600 reset button", NULL) < 0) {
printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
gpio_to_irq(DSMG600_RB_GPIO));
diff --git a/arch/arm/mach-ixp4xx/fsg-setup.c b/arch/arm/mach-ixp4xx/fsg-setup.c
index 429966b756ed..5c4b0c4a1b37 100644
--- a/arch/arm/mach-ixp4xx/fsg-setup.c
+++ b/arch/arm/mach-ixp4xx/fsg-setup.c
@@ -208,16 +208,14 @@ static void __init fsg_init(void)
platform_add_devices(fsg_devices, ARRAY_SIZE(fsg_devices));
if (request_irq(gpio_to_irq(FSG_RB_GPIO), &fsg_reset_handler,
- IRQF_DISABLED | IRQF_TRIGGER_LOW,
- "FSG reset button", NULL) < 0) {
+ IRQF_TRIGGER_LOW, "FSG reset button", NULL) < 0) {
printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
gpio_to_irq(FSG_RB_GPIO));
}
if (request_irq(gpio_to_irq(FSG_SB_GPIO), &fsg_power_handler,
- IRQF_DISABLED | IRQF_TRIGGER_LOW,
- "FSG power button", NULL) < 0) {
+ IRQF_TRIGGER_LOW, "FSG power button", NULL) < 0) {
printk(KERN_DEBUG "Power Button IRQ %d not available\n",
gpio_to_irq(FSG_SB_GPIO));
diff --git a/arch/arm/mach-ixp4xx/goramo_mlr.c b/arch/arm/mach-ixp4xx/goramo_mlr.c
index e54ff491c105..80bd9d6d04de 100644
--- a/arch/arm/mach-ixp4xx/goramo_mlr.c
+++ b/arch/arm/mach-ixp4xx/goramo_mlr.c
@@ -4,6 +4,7 @@
*/
#include <linux/delay.h>
+#include <linux/gpio.h>
#include <linux/hdlc.h>
#include <linux/i2c-gpio.h>
#include <linux/io.h>
@@ -79,19 +80,19 @@ static u8 control_value;
static void set_scl(u8 value)
{
- gpio_line_set(GPIO_SCL, !!value);
+ gpio_set_value(GPIO_SCL, !!value);
udelay(3);
}
static void set_sda(u8 value)
{
- gpio_line_set(GPIO_SDA, !!value);
+ gpio_set_value(GPIO_SDA, !!value);
udelay(3);
}
static void set_str(u8 value)
{
- gpio_line_set(GPIO_STR, !!value);
+ gpio_set_value(GPIO_STR, !!value);
udelay(3);
}
@@ -108,8 +109,8 @@ static void output_control(void)
{
int i;
- gpio_line_config(GPIO_SCL, IXP4XX_GPIO_OUT);
- gpio_line_config(GPIO_SDA, IXP4XX_GPIO_OUT);
+ gpio_direction_output(GPIO_SCL, 1);
+ gpio_direction_output(GPIO_SDA, 1);
for (i = 0; i < 8; i++) {
set_scl(0);
@@ -151,8 +152,8 @@ static int hss_set_clock(int port, unsigned int clock_type)
static irqreturn_t hss_dcd_irq(int irq, void *pdev)
{
- int i, port = (irq == IXP4XX_GPIO_IRQ(GPIO_HSS1_DCD_N));
- gpio_line_get(port ? GPIO_HSS1_DCD_N : GPIO_HSS0_DCD_N, &i);
+ int port = (irq == IXP4XX_GPIO_IRQ(GPIO_HSS1_DCD_N));
+ int i = gpio_get_value(port ? GPIO_HSS1_DCD_N : GPIO_HSS0_DCD_N);
set_carrier_cb_tab[port](pdev, !i);
return IRQ_HANDLED;
}
@@ -168,7 +169,7 @@ static int hss_open(int port, void *pdev,
else
irq = IXP4XX_GPIO_IRQ(GPIO_HSS1_DCD_N);
- gpio_line_get(port ? GPIO_HSS1_DCD_N : GPIO_HSS0_DCD_N, &i);
+ i = gpio_get_value(port ? GPIO_HSS1_DCD_N : GPIO_HSS0_DCD_N);
set_carrier_cb(pdev, !i);
set_carrier_cb_tab[!!port] = set_carrier_cb;
@@ -181,7 +182,7 @@ static int hss_open(int port, void *pdev,
set_control(port ? CONTROL_HSS1_DTR_N : CONTROL_HSS0_DTR_N, 0);
output_control();
- gpio_line_set(port ? GPIO_HSS1_RTS_N : GPIO_HSS0_RTS_N, 0);
+ gpio_set_value(port ? GPIO_HSS1_RTS_N : GPIO_HSS0_RTS_N, 0);
return 0;
}
@@ -193,7 +194,7 @@ static void hss_close(int port, void *pdev)
set_control(port ? CONTROL_HSS1_DTR_N : CONTROL_HSS0_DTR_N, 1);
output_control();
- gpio_line_set(port ? GPIO_HSS1_RTS_N : GPIO_HSS0_RTS_N, 1);
+ gpio_set_value(port ? GPIO_HSS1_RTS_N : GPIO_HSS0_RTS_N, 1);
}
@@ -413,13 +414,21 @@ static void __init gmlr_init(void)
if (hw_bits & CFG_HW_HAS_EEPROM)
device_tab[devices++] = &device_i2c; /* max index 6 */
- gpio_line_config(GPIO_SCL, IXP4XX_GPIO_OUT);
- gpio_line_config(GPIO_SDA, IXP4XX_GPIO_OUT);
- gpio_line_config(GPIO_STR, IXP4XX_GPIO_OUT);
- gpio_line_config(GPIO_HSS0_RTS_N, IXP4XX_GPIO_OUT);
- gpio_line_config(GPIO_HSS1_RTS_N, IXP4XX_GPIO_OUT);
- gpio_line_config(GPIO_HSS0_DCD_N, IXP4XX_GPIO_IN);
- gpio_line_config(GPIO_HSS1_DCD_N, IXP4XX_GPIO_IN);
+ gpio_request(GPIO_SCL, "SCL/clock");
+ gpio_request(GPIO_SDA, "SDA/data");
+ gpio_request(GPIO_STR, "strobe");
+ gpio_request(GPIO_HSS0_RTS_N, "HSS0 RTS");
+ gpio_request(GPIO_HSS1_RTS_N, "HSS1 RTS");
+ gpio_request(GPIO_HSS0_DCD_N, "HSS0 DCD");
+ gpio_request(GPIO_HSS1_DCD_N, "HSS1 DCD");
+
+ gpio_direction_output(GPIO_SCL, 1);
+ gpio_direction_output(GPIO_SDA, 1);
+ gpio_direction_output(GPIO_STR, 0);
+ gpio_direction_output(GPIO_HSS0_RTS_N, 1);
+ gpio_direction_output(GPIO_HSS1_RTS_N, 1);
+ gpio_direction_input(GPIO_HSS0_DCD_N);
+ gpio_direction_input(GPIO_HSS1_DCD_N);
irq_set_irq_type(IXP4XX_GPIO_IRQ(GPIO_HSS0_DCD_N), IRQ_TYPE_EDGE_BOTH);
irq_set_irq_type(IXP4XX_GPIO_IRQ(GPIO_HSS1_DCD_N), IRQ_TYPE_EDGE_BOTH);
diff --git a/arch/arm/mach-ixp4xx/include/mach/io.h b/arch/arm/mach-ixp4xx/include/mach/io.h
index 5cf30d1b78d2..559c69a47731 100644
--- a/arch/arm/mach-ixp4xx/include/mach/io.h
+++ b/arch/arm/mach-ixp4xx/include/mach/io.h
@@ -48,9 +48,10 @@ extern int ixp4xx_pci_write(u32 addr, u32 cmd, u32 data);
* fallback to the default.
*/
+extern unsigned long pcibios_min_mem;
static inline int is_pci_memory(u32 addr)
{
- return (addr >= PCIBIOS_MIN_MEM) && (addr <= 0x4FFFFFFF);
+ return (addr >= pcibios_min_mem) && (addr <= 0x4FFFFFFF);
}
#define writeb(v, p) __indirect_writeb(v, p)
diff --git a/arch/arm/mach-ixp4xx/include/mach/timex.h b/arch/arm/mach-ixp4xx/include/mach/timex.h
deleted file mode 100644
index 0396d89f947c..000000000000
--- a/arch/arm/mach-ixp4xx/include/mach/timex.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * arch/arm/mach-ixp4xx/include/mach/timex.h
- *
- */
-
-#include <mach/ixp4xx-regs.h>
-
-/*
- * We use IXP425 General purpose timer for our timer needs, it runs at
- * 66.66... MHz. We do a convulted calculation of CLOCK_TICK_RATE b/c the
- * timer register ignores the bottom 2 bits of the LATCH value.
- */
-#define IXP4XX_TIMER_FREQ 66666000
-#define CLOCK_TICK_RATE \
- (((IXP4XX_TIMER_FREQ / HZ & ~IXP4XX_OST_RELOAD_MASK) + 1) * HZ)
-
diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c
index 507cb5233537..4e0f762bc651 100644
--- a/arch/arm/mach-ixp4xx/nas100d-setup.c
+++ b/arch/arm/mach-ixp4xx/nas100d-setup.c
@@ -295,8 +295,7 @@ static void __init nas100d_init(void)
pm_power_off = nas100d_power_off;
if (request_irq(gpio_to_irq(NAS100D_RB_GPIO), &nas100d_reset_handler,
- IRQF_DISABLED | IRQF_TRIGGER_LOW,
- "NAS100D reset button", NULL) < 0) {
+ IRQF_TRIGGER_LOW, "NAS100D reset button", NULL) < 0) {
printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
gpio_to_irq(NAS100D_RB_GPIO));
diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c
index ba5f1cda2a9d..88c025f52d8d 100644
--- a/arch/arm/mach-ixp4xx/nslu2-setup.c
+++ b/arch/arm/mach-ixp4xx/nslu2-setup.c
@@ -265,16 +265,14 @@ static void __init nslu2_init(void)
pm_power_off = nslu2_power_off;
if (request_irq(gpio_to_irq(NSLU2_RB_GPIO), &nslu2_reset_handler,
- IRQF_DISABLED | IRQF_TRIGGER_LOW,
- "NSLU2 reset button", NULL) < 0) {
+ IRQF_TRIGGER_LOW, "NSLU2 reset button", NULL) < 0) {
printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
gpio_to_irq(NSLU2_RB_GPIO));
}
if (request_irq(gpio_to_irq(NSLU2_PB_GPIO), &nslu2_power_handler,
- IRQF_DISABLED | IRQF_TRIGGER_HIGH,
- "NSLU2 power button", NULL) < 0) {
+ IRQF_TRIGGER_HIGH, "NSLU2 power button", NULL) < 0) {
printk(KERN_DEBUG "Power Button IRQ %d not available\n",
gpio_to_irq(NSLU2_PB_GPIO));
diff --git a/arch/arm/mach-ixp4xx/omixp-setup.c b/arch/arm/mach-ixp4xx/omixp-setup.c
index 75ef03dc9964..2d494b454376 100644
--- a/arch/arm/mach-ixp4xx/omixp-setup.c
+++ b/arch/arm/mach-ixp4xx/omixp-setup.c
@@ -17,9 +17,7 @@
#include <linux/serial_8250.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
-#ifdef CONFIG_LEDS_CLASS
#include <linux/leds.h>
-#endif
#include <asm/setup.h>
#include <asm/memory.h>
diff --git a/arch/arm/mach-keystone/Kconfig b/arch/arm/mach-keystone/Kconfig
index 90a708fef541..f50bc936cb84 100644
--- a/arch/arm/mach-keystone/Kconfig
+++ b/arch/arm/mach-keystone/Kconfig
@@ -1,13 +1,9 @@
config ARCH_KEYSTONE
bool "Texas Instruments Keystone Devices"
depends on ARCH_MULTI_V7
- select CPU_V7
select ARM_GIC
select HAVE_ARM_ARCH_TIMER
- select HAVE_SMP
select CLKSRC_MMIO
- select GENERIC_CLOCKEVENTS
- select ARCH_WANT_OPTIONAL_GPIOLIB
select ARM_ERRATA_798181 if SMP
select COMMON_CLK_KEYSTONE
select ARCH_SUPPORTS_BIG_ENDIAN
diff --git a/arch/arm/mach-keystone/keystone.c b/arch/arm/mach-keystone/keystone.c
index 6e6bb7d5ea30..e0b9e1b9cf30 100644
--- a/arch/arm/mach-keystone/keystone.c
+++ b/arch/arm/mach-keystone/keystone.c
@@ -46,7 +46,7 @@ static void __init keystone_init(void)
}
static const char *keystone_match[] __initconst = {
- "ti,keystone-evm",
+ "ti,keystone",
NULL,
};
diff --git a/arch/arm/mach-kirkwood/Kconfig b/arch/arm/mach-kirkwood/Kconfig
index fe8319ad3158..df4b26340ae4 100644
--- a/arch/arm/mach-kirkwood/Kconfig
+++ b/arch/arm/mach-kirkwood/Kconfig
@@ -106,13 +106,6 @@ config ARCH_KIRKWOOD_DT
Say 'Y' here if you want your kernel to support the
Marvell Kirkwood using flattened device tree.
-config MACH_MV88F6281GTW_GE_DT
- bool "Marvell 88F6281 GTW GE Board (Flattened Device Tree)"
- depends on ARCH_KIRKWOOD_DT
- help
- Say 'Y' here if you want your kernel to support the
- Marvell 88F6281 GTW GE Board (Flattened Device Tree).
-
endmenu
endif
diff --git a/arch/arm/mach-kirkwood/Makefile b/arch/arm/mach-kirkwood/Makefile
index 144b51102939..3a72c5c6e747 100644
--- a/arch/arm/mach-kirkwood/Makefile
+++ b/arch/arm/mach-kirkwood/Makefile
@@ -1,5 +1,4 @@
-obj-y += common.o pcie.o
-obj-$(CONFIG_KIRKWOOD_LEGACY) += irq.o mpp.o
+obj-$(CONFIG_KIRKWOOD_LEGACY) += irq.o mpp.o common.o pcie.o
obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_MACH_D2NET_V2) += d2net_v2-setup.o lacie_v2-common.o
@@ -13,4 +12,3 @@ obj-$(CONFIG_MACH_TS219) += ts219-setup.o tsx1x-common.o
obj-$(CONFIG_MACH_TS41X) += ts41x-setup.o tsx1x-common.o
obj-$(CONFIG_ARCH_KIRKWOOD_DT) += board-dt.o
-obj-$(CONFIG_MACH_MV88F6281GTW_GE_DT) += board-mv88f6281gtw_ge.o
diff --git a/arch/arm/mach-kirkwood/board-dt.c b/arch/arm/mach-kirkwood/board-dt.c
index 78188159484d..2801da49e2a3 100644
--- a/arch/arm/mach-kirkwood/board-dt.c
+++ b/arch/arm/mach-kirkwood/board-dt.c
@@ -19,11 +19,84 @@
#include <linux/of_platform.h>
#include <linux/dma-mapping.h>
#include <linux/irqchip.h>
-#include <linux/kexec.h>
+#include <asm/hardware/cache-feroceon-l2.h>
#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
#include <mach/bridge-regs.h>
#include <plat/common.h>
-#include "common.h"
+#include <plat/pcie.h>
+#include "pm.h"
+
+static struct map_desc kirkwood_io_desc[] __initdata = {
+ {
+ .virtual = (unsigned long) KIRKWOOD_REGS_VIRT_BASE,
+ .pfn = __phys_to_pfn(KIRKWOOD_REGS_PHYS_BASE),
+ .length = KIRKWOOD_REGS_SIZE,
+ .type = MT_DEVICE,
+ },
+};
+
+static void __init kirkwood_map_io(void)
+{
+ iotable_init(kirkwood_io_desc, ARRAY_SIZE(kirkwood_io_desc));
+}
+
+static struct resource kirkwood_cpufreq_resources[] = {
+ [0] = {
+ .start = CPU_CONTROL_PHYS,
+ .end = CPU_CONTROL_PHYS + 3,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device kirkwood_cpufreq_device = {
+ .name = "kirkwood-cpufreq",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(kirkwood_cpufreq_resources),
+ .resource = kirkwood_cpufreq_resources,
+};
+
+static void __init kirkwood_cpufreq_init(void)
+{
+ platform_device_register(&kirkwood_cpufreq_device);
+}
+
+static struct resource kirkwood_cpuidle_resource[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ .start = DDR_OPERATION_BASE,
+ .end = DDR_OPERATION_BASE + 3,
+ },
+};
+
+static struct platform_device kirkwood_cpuidle = {
+ .name = "kirkwood_cpuidle",
+ .id = -1,
+ .resource = kirkwood_cpuidle_resource,
+ .num_resources = 1,
+};
+
+static void __init kirkwood_cpuidle_init(void)
+{
+ platform_device_register(&kirkwood_cpuidle);
+}
+
+/* Temporary here since mach-mvebu has a function we can use */
+static void kirkwood_restart(enum reboot_mode mode, const char *cmd)
+{
+ /*
+ * Enable soft reset to assert RSTOUTn.
+ */
+ writel(SOFT_RESET_OUT_EN, RSTOUTn_MASK);
+
+ /*
+ * Assert soft reset.
+ */
+ writel(SOFT_RESET, SYSTEM_SOFT_RESET);
+
+ while (1)
+ ;
+}
#define MV643XX_ETH_MAC_ADDR_LOW 0x0414
#define MV643XX_ETH_MAC_ADDR_HIGH 0x0418
@@ -104,35 +177,35 @@ eth_fixup_skip:
}
}
-static void __init kirkwood_dt_init(void)
+/*
+ * Disable propagation of mbus errors to the CPU local bus, as this
+ * causes mbus errors (which can occur for example for PCI aborts) to
+ * throw CPU aborts, which we're not set up to deal with.
+ */
+static void __init kirkwood_disable_mbus_error_propagation(void)
{
- pr_info("Kirkwood: %s.\n", kirkwood_id());
+ void __iomem *cpu_config;
- /*
- * Disable propagation of mbus errors to the CPU local bus,
- * as this causes mbus errors (which can occur for example
- * for PCI aborts) to throw CPU aborts, which we're not set
- * up to deal with.
- */
- writel(readl(CPU_CONFIG) & ~CPU_CONFIG_ERROR_PROP, CPU_CONFIG);
+ cpu_config = ioremap(CPU_CONFIG_PHYS, 4);
+ writel(readl(cpu_config) & ~CPU_CONFIG_ERROR_PROP, cpu_config);
+ iounmap(cpu_config);
+}
- BUG_ON(mvebu_mbus_dt_init());
+static void __init kirkwood_dt_init(void)
+{
+ kirkwood_disable_mbus_error_propagation();
- kirkwood_l2_init();
+ BUG_ON(mvebu_mbus_dt_init());
+#ifdef CONFIG_CACHE_FEROCEON_L2
+ feroceon_of_init();
+#endif
kirkwood_cpufreq_init();
kirkwood_cpuidle_init();
kirkwood_pm_init();
kirkwood_dt_eth_fixup();
-#ifdef CONFIG_KEXEC
- kexec_reinit = kirkwood_enable_pcie;
-#endif
-
- if (of_machine_is_compatible("marvell,mv88f6281gtw-ge"))
- mv88f6281gtw_ge_init();
-
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
diff --git a/arch/arm/mach-kirkwood/board-mv88f6281gtw_ge.c b/arch/arm/mach-kirkwood/board-mv88f6281gtw_ge.c
deleted file mode 100644
index ee5eea678c11..000000000000
--- a/arch/arm/mach-kirkwood/board-mv88f6281gtw_ge.c
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * arch/arm/mach-kirkwood/board-mv88f6281gtw_ge.c
- *
- * Marvell 88F6281 GTW GE Board Setup
- *
- * 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/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/irq.h>
-#include <linux/timer.h>
-#include <linux/mv643xx_eth.h>
-#include <linux/ethtool.h>
-#include <linux/gpio.h>
-#include <net/dsa.h>
-#include <asm/mach-types.h>
-#include <asm/mach/arch.h>
-#include <asm/mach/pci.h>
-#include <mach/kirkwood.h>
-#include "common.h"
-
-static struct mv643xx_eth_platform_data mv88f6281gtw_ge_ge00_data = {
- .phy_addr = MV643XX_ETH_PHY_NONE,
- .speed = SPEED_1000,
- .duplex = DUPLEX_FULL,
-};
-
-static struct dsa_chip_data mv88f6281gtw_ge_switch_chip_data = {
- .port_names[0] = "lan1",
- .port_names[1] = "lan2",
- .port_names[2] = "lan3",
- .port_names[3] = "lan4",
- .port_names[4] = "wan",
- .port_names[5] = "cpu",
-};
-
-static struct dsa_platform_data mv88f6281gtw_ge_switch_plat_data = {
- .nr_chips = 1,
- .chip = &mv88f6281gtw_ge_switch_chip_data,
-};
-
-void __init mv88f6281gtw_ge_init(void)
-{
- kirkwood_ge00_init(&mv88f6281gtw_ge_ge00_data);
- kirkwood_ge00_switch_init(&mv88f6281gtw_ge_switch_plat_data, NO_IRQ);
-}
diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c
index f3407a5db216..255f33a3903c 100644
--- a/arch/arm/mach-kirkwood/common.c
+++ b/arch/arm/mach-kirkwood/common.c
@@ -25,10 +25,10 @@
#include <asm/page.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
+#include <asm/hardware/cache-feroceon-l2.h>
#include <mach/kirkwood.h>
#include <mach/bridge-regs.h>
#include <linux/platform_data/asoc-kirkwood.h>
-#include <plat/cache-feroceon-l2.h>
#include <linux/platform_data/mmc-mvsdio.h>
#include <linux/platform_data/mtd-orion_nand.h>
#include <linux/platform_data/usb-ehci-orion.h>
@@ -36,6 +36,7 @@
#include <plat/time.h>
#include <linux/platform_data/dma-mv_xor.h>
#include "common.h"
+#include "pm.h"
/* These can go away once Kirkwood uses the mvebu-mbus DT binding */
#define KIRKWOOD_MBUS_NAND_TARGET 0x01
diff --git a/arch/arm/mach-kirkwood/common.h b/arch/arm/mach-kirkwood/common.h
index 05fd648df543..832a4e2ab8d7 100644
--- a/arch/arm/mach-kirkwood/common.h
+++ b/arch/arm/mach-kirkwood/common.h
@@ -58,19 +58,6 @@ void kirkwood_cpufreq_init(void);
void kirkwood_restart(enum reboot_mode, const char *);
void kirkwood_clk_init(void);
-#ifdef CONFIG_PM
-void kirkwood_pm_init(void);
-#else
-static inline void kirkwood_pm_init(void) {};
-#endif
-
-/* board init functions for boards not fully converted to fdt */
-#ifdef CONFIG_MACH_MV88F6281GTW_GE_DT
-void mv88f6281gtw_ge_init(void);
-#else
-static inline void mv88f6281gtw_ge_init(void) {};
-#endif
-
/* early init functions not converted to fdt yet */
char *kirkwood_id(void);
void kirkwood_l2_init(void);
diff --git a/arch/arm/mach-kirkwood/include/mach/bridge-regs.h b/arch/arm/mach-kirkwood/include/mach/bridge-regs.h
index 8b9d1c9ff199..1c37082c8b39 100644
--- a/arch/arm/mach-kirkwood/include/mach/bridge-regs.h
+++ b/arch/arm/mach-kirkwood/include/mach/bridge-regs.h
@@ -14,6 +14,7 @@
#include <mach/kirkwood.h>
#define CPU_CONFIG (BRIDGE_VIRT_BASE + 0x0100)
+#define CPU_CONFIG_PHYS (BRIDGE_PHYS_BASE + 0x0100)
#define CPU_CONFIG_ERROR_PROP 0x00000004
#define CPU_CONTROL (BRIDGE_VIRT_BASE + 0x0104)
@@ -21,6 +22,7 @@
#define CPU_RESET 0x00000002
#define RSTOUTn_MASK (BRIDGE_VIRT_BASE + 0x0108)
+#define RSTOUTn_MASK_PHYS (BRIDGE_PHYS_BASE + 0x0108)
#define SOFT_RESET_OUT_EN 0x00000004
#define SYSTEM_SOFT_RESET (BRIDGE_VIRT_BASE + 0x010c)
@@ -79,5 +81,6 @@
#define CGC_RESERVED (0x6 << 21)
#define MEMORY_PM_CTRL (BRIDGE_VIRT_BASE + 0x118)
+#define MEMORY_PM_CTRL_PHYS (BRIDGE_PHYS_BASE + 0x118)
#endif
diff --git a/arch/arm/mach-kirkwood/include/mach/timex.h b/arch/arm/mach-kirkwood/include/mach/timex.h
deleted file mode 100644
index c923cd169b9c..000000000000
--- a/arch/arm/mach-kirkwood/include/mach/timex.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * arch/arm/mach-kirkwood/include/mach/timex.h
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#define CLOCK_TICK_RATE (100 * HZ)
-
diff --git a/arch/arm/mach-kirkwood/pm.c b/arch/arm/mach-kirkwood/pm.c
index c6ab8d9303a5..8e5e0329d04c 100644
--- a/arch/arm/mach-kirkwood/pm.c
+++ b/arch/arm/mach-kirkwood/pm.c
@@ -21,15 +21,16 @@
#include "common.h"
static void __iomem *ddr_operation_base;
+static void __iomem *memory_pm_ctrl;
static void kirkwood_low_power(void)
{
u32 mem_pm_ctrl;
- mem_pm_ctrl = readl(MEMORY_PM_CTRL);
+ mem_pm_ctrl = readl(memory_pm_ctrl);
/* Set peripherals to low-power mode */
- writel_relaxed(~0, MEMORY_PM_CTRL);
+ writel_relaxed(~0, memory_pm_ctrl);
/* Set DDR in self-refresh */
writel_relaxed(0x7, ddr_operation_base);
@@ -41,7 +42,7 @@ static void kirkwood_low_power(void)
*/
cpu_do_idle();
- writel_relaxed(mem_pm_ctrl, MEMORY_PM_CTRL);
+ writel_relaxed(mem_pm_ctrl, memory_pm_ctrl);
}
static int kirkwood_suspend_enter(suspend_state_t state)
@@ -69,5 +70,7 @@ static const struct platform_suspend_ops kirkwood_suspend_ops = {
void __init kirkwood_pm_init(void)
{
ddr_operation_base = ioremap(DDR_OPERATION_BASE, 4);
+ memory_pm_ctrl = ioremap(MEMORY_PM_CTRL_PHYS, 4);
+
suspend_set_ops(&kirkwood_suspend_ops);
}
diff --git a/arch/arm/mach-kirkwood/pm.h b/arch/arm/mach-kirkwood/pm.h
new file mode 100644
index 000000000000..21e7530f368b
--- /dev/null
+++ b/arch/arm/mach-kirkwood/pm.h
@@ -0,0 +1,26 @@
+/*
+ * Power Management driver for Marvell Kirkwood SoCs
+ *
+ * Copyright (C) 2013 Ezequiel Garcia <ezequiel@free-electrons.com>
+ * Copyright (C) 2010 Simon Guinot <sguinot@lacie.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License,
+ * 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.
+ */
+
+#ifndef __ARCH_KIRKWOOD_PM_H
+#define __ARCH_KIRKWOOD_PM_H
+
+#ifdef CONFIG_PM
+void kirkwood_pm_init(void);
+#else
+static inline void kirkwood_pm_init(void) {};
+#endif
+
+#endif
diff --git a/arch/arm/mach-ks8695/board-og.c b/arch/arm/mach-ks8695/board-og.c
index 002bc619bb68..f2658168eeff 100644
--- a/arch/arm/mach-ks8695/board-og.c
+++ b/arch/arm/mach-ks8695/board-og.c
@@ -44,7 +44,8 @@ static void __init og_register_pci(void)
if (machine_is_im4004())
ks8695_gpio_interrupt(KS8695_GPIO_1, IRQ_TYPE_LEVEL_LOW);
- ks8695_init_pci(&og_pci);
+ if (IS_ENABLED(CONFIG_PCI))
+ ks8695_init_pci(&og_pci);
}
/*
diff --git a/arch/arm/mach-ks8695/include/mach/timex.h b/arch/arm/mach-ks8695/include/mach/timex.h
deleted file mode 100644
index 10f716371bd3..000000000000
--- a/arch/arm/mach-ks8695/include/mach/timex.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * arch/arm/mach-ks8695/include/mach/timex.h
- *
- * Copyright (C) 2006 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * KS8695 - Time Parameters
- *
- * 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_ARCH_TIMEX_H
-#define __ASM_ARCH_TIMEX_H
-
-#include <mach/hardware.h>
-
-#define CLOCK_TICK_RATE KS8695_CLOCK_RATE
-
-#endif
diff --git a/arch/arm/mach-ks8695/time.c b/arch/arm/mach-ks8695/time.c
index 426c97662f5b..a197874bf382 100644
--- a/arch/arm/mach-ks8695/time.c
+++ b/arch/arm/mach-ks8695/time.c
@@ -122,7 +122,7 @@ static irqreturn_t ks8695_timer_interrupt(int irq, void *dev_id)
static struct irqaction ks8695_timer_irq = {
.name = "ks8695_tick",
- .flags = IRQF_DISABLED | IRQF_TIMER,
+ .flags = IRQF_TIMER,
.handler = ks8695_timer_interrupt,
};
diff --git a/arch/arm/mach-lpc32xx/common.c b/arch/arm/mach-lpc32xx/common.c
index d7aa54c25c59..de03620d7fa7 100644
--- a/arch/arm/mach-lpc32xx/common.c
+++ b/arch/arm/mach-lpc32xx/common.c
@@ -99,6 +99,7 @@ u32 lpc32xx_return_iram_size(void)
return iram_size;
}
+EXPORT_SYMBOL_GPL(lpc32xx_return_iram_size);
/*
* Computes PLL rate from PLL register and input clock
diff --git a/arch/arm/mach-lpc32xx/timer.c b/arch/arm/mach-lpc32xx/timer.c
index 20eab63d10ba..4e5837299c04 100644
--- a/arch/arm/mach-lpc32xx/timer.c
+++ b/arch/arm/mach-lpc32xx/timer.c
@@ -90,7 +90,7 @@ static irqreturn_t lpc32xx_timer_interrupt(int irq, void *dev_id)
static struct irqaction lpc32xx_timer_irq = {
.name = "LPC32XX Timer Tick",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = lpc32xx_timer_interrupt,
};
diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 0c002099c3a3..7e0248582efd 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -231,7 +231,7 @@ static struct pxa27x_keypad_platform_data aspenite_keypad_info __initdata = {
.debounce_interval = 30,
};
-#if defined(CONFIG_USB_EHCI_MV)
+#if IS_ENABLED(CONFIG_USB_EHCI_MV)
static struct mv_usb_platform_data pxa168_sph_pdata = {
.mode = MV_USB_MODE_HOST,
.phy_init = pxa_usb_phy_init,
@@ -258,7 +258,7 @@ static void __init common_init(void)
/* off-chip devices */
platform_device_register(&smc91x_device);
-#if defined(CONFIG_USB_EHCI_MV)
+#if IS_ENABLED(CONFIG_USB_EHCI_MV)
pxa168_add_usb_host(&pxa168_sph_pdata);
#endif
}
diff --git a/arch/arm/mach-mmp/devices.c b/arch/arm/mach-mmp/devices.c
index dd2d8b103cc8..2bcb766af05d 100644
--- a/arch/arm/mach-mmp/devices.c
+++ b/arch/arm/mach-mmp/devices.c
@@ -72,7 +72,7 @@ int __init pxa_register_device(struct pxa_device_desc *desc,
return platform_device_add(pdev);
}
-#if defined(CONFIG_USB) || defined(CONFIG_USB_GADGET)
+#if IS_ENABLED(CONFIG_USB) || IS_ENABLED(CONFIG_USB_GADGET)
/*****************************************************************************
* The registers read/write routines
@@ -112,9 +112,9 @@ static void u2o_write(void __iomem *base, unsigned int offset,
readl_relaxed(base + offset);
}
-#if defined(CONFIG_USB_MV_UDC) || defined(CONFIG_USB_EHCI_MV)
+#if IS_ENABLED(CONFIG_USB_MV_UDC) || IS_ENABLED(CONFIG_USB_EHCI_MV)
-#if defined(CONFIG_CPU_PXA910) || defined(CONFIG_CPU_PXA168)
+#if IS_ENABLED(CONFIG_CPU_PXA910) || IS_ENABLED(CONFIG_CPU_PXA168)
static DEFINE_MUTEX(phy_lock);
static int phy_init_cnt;
@@ -238,10 +238,10 @@ void pxa_usb_phy_deinit(void __iomem *phy_reg)
#endif
#endif
-#ifdef CONFIG_USB_SUPPORT
+#if IS_ENABLED(CONFIG_USB_SUPPORT)
static u64 usb_dma_mask = ~(u32)0;
-#ifdef CONFIG_USB_MV_UDC
+#if IS_ENABLED(CONFIG_USB_MV_UDC)
struct resource pxa168_u2o_resources[] = {
/* regbase */
[0] = {
@@ -276,7 +276,7 @@ struct platform_device pxa168_device_u2o = {
};
#endif /* CONFIG_USB_MV_UDC */
-#ifdef CONFIG_USB_EHCI_MV_U2O
+#if IS_ENABLED(CONFIG_USB_EHCI_MV_U2O)
struct resource pxa168_u2oehci_resources[] = {
/* regbase */
[0] = {
@@ -312,7 +312,7 @@ struct platform_device pxa168_device_u2oehci = {
};
#endif
-#if defined(CONFIG_USB_MV_OTG)
+#if IS_ENABLED(CONFIG_USB_MV_OTG)
struct resource pxa168_u2ootg_resources[] = {
/* regbase */
[0] = {
diff --git a/arch/arm/mach-mmp/time.c b/arch/arm/mach-mmp/time.c
index 024022d91fe3..2756351dbb35 100644
--- a/arch/arm/mach-mmp/time.c
+++ b/arch/arm/mach-mmp/time.c
@@ -39,6 +39,12 @@
#include "clock.h"
+#ifdef CONFIG_CPU_MMP2
+#define MMP_CLOCK_FREQ 6500000
+#else
+#define MMP_CLOCK_FREQ 3250000
+#endif
+
#define TIMERS_VIRT_BASE TIMERS1_VIRT_BASE
#define MAX_DELTA (0xfffffffe)
@@ -186,7 +192,7 @@ static void __init timer_config(void)
static struct irqaction timer_irq = {
.name = "timer",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = timer_interrupt,
.dev_id = &ckevt,
};
@@ -195,14 +201,14 @@ void __init timer_init(int irq)
{
timer_config();
- sched_clock_register(mmp_read_sched_clock, 32, CLOCK_TICK_RATE);
+ sched_clock_register(mmp_read_sched_clock, 32, MMP_CLOCK_FREQ);
ckevt.cpumask = cpumask_of(0);
setup_irq(irq, &timer_irq);
- clocksource_register_hz(&cksrc, CLOCK_TICK_RATE);
- clockevents_config_and_register(&ckevt, CLOCK_TICK_RATE,
+ clocksource_register_hz(&cksrc, MMP_CLOCK_FREQ);
+ clockevents_config_and_register(&ckevt, MMP_CLOCK_FREQ,
MIN_DELTA, MAX_DELTA);
}
diff --git a/arch/arm/mach-mmp/ttc_dkb.c b/arch/arm/mach-mmp/ttc_dkb.c
index cfadd974f5ce..ac4af81de3ea 100644
--- a/arch/arm/mach-mmp/ttc_dkb.c
+++ b/arch/arm/mach-mmp/ttc_dkb.c
@@ -164,8 +164,8 @@ static struct i2c_board_info ttc_dkb_i2c_info[] = {
},
};
-#ifdef CONFIG_USB_SUPPORT
-#if defined(CONFIG_USB_MV_UDC) || defined(CONFIG_USB_EHCI_MV_U2O)
+#if IS_ENABLED(CONFIG_USB_SUPPORT)
+#if IS_ENABLED(CONFIG_USB_MV_UDC) || IS_ENABLED(CONFIG_USB_EHCI_MV_U2O)
static struct mv_usb_platform_data ttc_usb_pdata = {
.vbus = NULL,
@@ -178,14 +178,14 @@ static struct mv_usb_platform_data ttc_usb_pdata = {
#endif
#endif
-#ifdef CONFIG_MTD_NAND_PXA3xx
+#if IS_ENABLED(CONFIG_MTD_NAND_PXA3xx)
static struct pxa3xx_nand_platform_data dkb_nand_info = {
.enable_arbiter = 1,
.num_cs = 1,
};
#endif
-#ifdef CONFIG_MMP_DISP
+#if IS_ENABLED(CONFIG_MMP_DISP)
/* path config */
#define CFG_IOPADMODE(iopad) (iopad) /* 0x0 ~ 0xd */
#define SCLK_SOURCE_SELECT(x) (x << 30) /* 0x0 ~ 0x3 */
@@ -275,7 +275,7 @@ static void __init ttc_dkb_init(void)
/* on-chip devices */
pxa910_add_uart(1);
-#ifdef CONFIG_MTD_NAND_PXA3xx
+#if IS_ENABLED(CONFIG_MTD_NAND_PXA3xx)
pxa910_add_nand(&dkb_nand_info);
#endif
@@ -285,22 +285,22 @@ static void __init ttc_dkb_init(void)
sizeof(struct pxa_gpio_platform_data));
platform_add_devices(ARRAY_AND_SIZE(ttc_dkb_devices));
-#ifdef CONFIG_USB_MV_UDC
+#if IS_ENABLED(CONFIG_USB_MV_UDC)
pxa168_device_u2o.dev.platform_data = &ttc_usb_pdata;
platform_device_register(&pxa168_device_u2o);
#endif
-#ifdef CONFIG_USB_EHCI_MV_U2O
+#if IS_ENABLED(CONFIG_USB_EHCI_MV_U2O)
pxa168_device_u2oehci.dev.platform_data = &ttc_usb_pdata;
platform_device_register(&pxa168_device_u2oehci);
#endif
-#ifdef CONFIG_USB_MV_OTG
+#if IS_ENABLED(CONFIG_USB_MV_OTG)
pxa168_device_u2ootg.dev.platform_data = &ttc_usb_pdata;
platform_device_register(&pxa168_device_u2ootg);
#endif
-#ifdef CONFIG_MMP_DISP
+#if IS_ENABLED(CONFIG_MMP_DISP)
add_disp();
#endif
}
diff --git a/arch/arm/mach-moxart/Kconfig b/arch/arm/mach-moxart/Kconfig
index 3795ae28a613..82a4ba8578a2 100644
--- a/arch/arm/mach-moxart/Kconfig
+++ b/arch/arm/mach-moxart/Kconfig
@@ -1,15 +1,10 @@
config ARCH_MOXART
- bool "MOXA ART SoC" if ARCH_MULTI_V4T
+ bool "MOXA ART SoC" if ARCH_MULTI_V4
select CPU_FA526
select ARM_DMA_MEM_BUFFERABLE
- select USE_OF
- select CLKSRC_OF
select CLKSRC_MMIO
- select HAVE_CLK
- select COMMON_CLK
select GENERIC_IRQ_CHIP
select ARCH_REQUIRE_GPIOLIB
- select GENERIC_CLOCKEVENTS
select PHYLIB if NETDEVICES
help
Say Y here if you want to run your kernel on hardware with a
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 9625cf378931..a7f959e58c3d 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -1,50 +1,9 @@
-config ARCH_MSM
- bool
-
-config ARCH_MSM_DT
- bool "Qualcomm MSM DT Support" if ARCH_MULTI_V7
- select ARCH_MSM
- select ARCH_REQUIRE_GPIOLIB
- select CLKSRC_OF
- select GENERIC_CLOCKEVENTS
- help
- Support for Qualcomm's devicetree based MSM systems.
-
if ARCH_MSM
-menu "Qualcomm MSM SoC Selection"
- depends on ARCH_MSM_DT
-
-config ARCH_MSM8X60
- bool "Enable support for MSM8X60"
- select ARM_GIC
- select CPU_V7
- select HAVE_SMP
- select MSM_SCM if SMP
- select MSM_TIMER
-
-config ARCH_MSM8960
- bool "Enable support for MSM8960"
- select ARM_GIC
- select CPU_V7
- select HAVE_SMP
- select MSM_SCM if SMP
- select MSM_TIMER
-
-config ARCH_MSM8974
- bool "Enable support for MSM8974"
- select ARM_GIC
- select CPU_V7
- select HAVE_ARM_ARCH_TIMER
- select HAVE_SMP
- select MSM_SCM if SMP
-
-endmenu
-
choice
prompt "Qualcomm MSM SoC Type"
default ARCH_MSM7X00A
- depends on ARCH_MSM_NODT
+ depends on ARCH_MSM
config ARCH_MSM7X00A
bool "MSM7x00A / MSM7x01A"
@@ -54,7 +13,7 @@ config ARCH_MSM7X00A
select MACH_TROUT if !MACH_HALIBUT
select MSM_PROC_COMM
select MSM_SMD
- select MSM_TIMER
+ select CLKSRC_QCOM
select MSM_SMD_PKG3
config ARCH_MSM7X30
@@ -66,7 +25,7 @@ config ARCH_MSM7X30
select MSM_GPIOMUX
select MSM_PROC_COMM
select MSM_SMD
- select MSM_TIMER
+ select CLKSRC_QCOM
select MSM_VIC
config ARCH_QSD8X50
@@ -78,7 +37,7 @@ config ARCH_QSD8X50
select MSM_GPIOMUX
select MSM_PROC_COMM
select MSM_SMD
- select MSM_TIMER
+ select CLKSRC_QCOM
select MSM_VIC
endchoice
@@ -99,7 +58,7 @@ config MSM_VIC
bool
menu "Qualcomm MSM Board Type"
- depends on ARCH_MSM_NODT
+ depends on ARCH_MSM
config MACH_HALIBUT
depends on ARCH_MSM
@@ -153,7 +112,4 @@ config MSM_GPIOMUX
config MSM_SCM
bool
-config MSM_TIMER
- bool
-
endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 8e307a10d3c3..27c078a568df 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -1,4 +1,3 @@
-obj-$(CONFIG_MSM_TIMER) += timer.o
obj-$(CONFIG_MSM_PROC_COMM) += clock.o
obj-$(CONFIG_MSM_VIC) += irq-vic.o
@@ -14,18 +13,11 @@ obj-$(CONFIG_ARCH_QSD8X50) += dma.o io.o
obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o
obj-$(CONFIG_MSM_SMD) += last_radio_log.o
-obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o
-
-CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
-
-obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
-obj-$(CONFIG_SMP) += headsmp.o platsmp.o
obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o
obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o
obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o
obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o
-obj-$(CONFIG_ARCH_MSM_DT) += board-dt.o
obj-$(CONFIG_MSM_GPIOMUX) += gpiomux.o
obj-$(CONFIG_ARCH_QSD8X50) += gpiomux-8x50.o
diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c
deleted file mode 100644
index 1f11d93e700e..000000000000
--- a/arch/arm/mach-msm/board-dt.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright (c) 2010-2012,2013 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/init.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-
-#include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-
-#include "common.h"
-
-static const char * const msm_dt_match[] __initconst = {
- "qcom,msm8660-fluid",
- "qcom,msm8660-surf",
- "qcom,msm8960-cdp",
- NULL
-};
-
-static const char * const apq8074_dt_match[] __initconst = {
- "qcom,apq8074-dragonboard",
- NULL
-};
-
-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
- .smp = smp_ops(msm_smp_ops),
- .dt_compat = msm_dt_match,
-MACHINE_END
-
-DT_MACHINE_START(APQ_DT, "Qualcomm MSM (Flattened Device Tree)")
- .dt_compat = apq8074_dt_match,
-MACHINE_END
diff --git a/arch/arm/mach-msm/common.h b/arch/arm/mach-msm/common.h
index 33c7725adae2..572479a3c7be 100644
--- a/arch/arm/mach-msm/common.h
+++ b/arch/arm/mach-msm/common.h
@@ -23,9 +23,6 @@ extern void msm_map_qsd8x50_io(void);
extern void __iomem *__msm_ioremap_caller(phys_addr_t phys_addr, size_t size,
unsigned int mtype, void *caller);
-extern struct smp_operations msm_smp_ops;
-extern void msm_cpu_die(unsigned int cpu);
-
struct msm_mmc_platform_data;
extern void msm_add_devices(void);
diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c
index f8f6adfa07c6..fb9762464718 100644
--- a/arch/arm/mach-msm/dma.c
+++ b/arch/arm/mach-msm/dma.c
@@ -18,6 +18,7 @@
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
+#include <linux/module.h>
#include <mach/dma.h>
#include <mach/msm_iomap.h>
@@ -77,6 +78,7 @@ void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful)
{
writel((graceful << 31), DMOV_FLUSH0(id));
}
+EXPORT_SYMBOL_GPL(msm_dmov_stop_cmd);
void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd)
{
@@ -115,6 +117,7 @@ void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd)
}
spin_unlock_irqrestore(&msm_dmov_lock, irq_flags);
}
+EXPORT_SYMBOL_GPL(msm_dmov_enqueue_cmd);
struct msm_dmov_exec_cmdptr_cmd {
struct msm_dmov_cmd dmov_cmd;
diff --git a/arch/arm/mach-msm/headsmp.S b/arch/arm/mach-msm/headsmp.S
deleted file mode 100644
index 6c62c3f82fe6..000000000000
--- a/arch/arm/mach-msm/headsmp.S
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * linux/arch/arm/mach-realview/headsmp.S
- *
- * Copyright (c) 2003 ARM Limited
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <linux/linkage.h>
-#include <linux/init.h>
-
-/*
- * MSM specific entry point for secondary CPUs. This provides
- * a "holding pen" into which all secondary cores are held until we're
- * ready for them to initialise.
- */
-ENTRY(msm_secondary_startup)
- mrc p15, 0, r0, c0, c0, 5
- and r0, r0, #15
- adr r4, 1f
- ldmia r4, {r5, r6}
- sub r4, r4, r5
- add r6, r6, r4
-pen: ldr r7, [r6]
- cmp r7, r0
- bne pen
-
- /*
- * we've been released from the holding pen: secondary_stack
- * should now contain the SVC stack for this core
- */
- b secondary_startup
-ENDPROC(msm_secondary_startup)
-
- .align
-1: .long .
- .long pen_release
diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c
deleted file mode 100644
index 326a87261f9a..000000000000
--- a/arch/arm/mach-msm/hotplug.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2002 ARM Ltd.
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/smp.h>
-
-#include <asm/smp_plat.h>
-
-#include "common.h"
-
-static inline void cpu_enter_lowpower(void)
-{
-}
-
-static inline void cpu_leave_lowpower(void)
-{
-}
-
-static inline void platform_do_lowpower(unsigned int cpu)
-{
- /* Just enter wfi for now. TODO: Properly shut off the cpu. */
- for (;;) {
- /*
- * here's the WFI
- */
- asm("wfi"
- :
- :
- : "memory", "cc");
-
- if (pen_release == cpu_logical_map(cpu)) {
- /*
- * OK, proper wakeup, we're done
- */
- break;
- }
-
- /*
- * getting here, means that we have come out of WFI without
- * having been woken up - this shouldn't happen
- *
- * The trouble is, letting people know about this is not really
- * possible, since we are currently running incoherently, and
- * therefore cannot safely call printk() or anything else
- */
- pr_debug("CPU%u: spurious wakeup call\n", cpu);
- }
-}
-
-/*
- * platform-specific code to shutdown a CPU
- *
- * Called with IRQs disabled
- */
-void __ref msm_cpu_die(unsigned int cpu)
-{
- /*
- * we're ready for shutdown now, so do it
- */
- cpu_enter_lowpower();
- platform_do_lowpower(cpu);
-
- /*
- * bring this CPU back into the world of cache
- * coherency, and then restore interrupts
- */
- cpu_leave_lowpower();
-}
diff --git a/arch/arm/mach-msm/include/mach/timex.h b/arch/arm/mach-msm/include/mach/timex.h
deleted file mode 100644
index a62e6b215aec..000000000000
--- a/arch/arm/mach-msm/include/mach/timex.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* arch/arm/mach-msm/include/mach/timex.h
- *
- * Copyright (C) 2007 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __ASM_ARCH_MSM_TIMEX_H
-#define __ASM_ARCH_MSM_TIMEX_H
-
-#define CLOCK_TICK_RATE 1000000
-
-#endif
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index adc8971c7266..34e09474636d 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -78,8 +78,10 @@ void __init msm_map_common_io(void)
asm("mcr p15, 0, %0, c15, c2, 4" : : "r" (0));
#if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) || \
defined(CONFIG_DEBUG_MSM_UART3)
+#ifdef CONFIG_MMU
debug_ll_addr(&msm_io_desc[size - 1].pfn,
&msm_io_desc[size - 1].virtual);
+#endif
msm_io_desc[size - 1].pfn = __phys_to_pfn(msm_io_desc[size - 1].pfn);
#endif
iotable_init(msm_io_desc, size);
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
deleted file mode 100644
index f10a1f58fde9..000000000000
--- a/arch/arm/mach-msm/platsmp.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2002 ARM Ltd.
- * All Rights Reserved
- * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/jiffies.h>
-#include <linux/smp.h>
-#include <linux/io.h>
-
-#include <asm/cacheflush.h>
-#include <asm/cputype.h>
-#include <asm/mach-types.h>
-#include <asm/smp_plat.h>
-
-#include "scm-boot.h"
-#include "common.h"
-
-#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0
-#define SCSS_CPU1CORE_RESET 0xD80
-#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64
-
-extern void msm_secondary_startup(void);
-
-static DEFINE_SPINLOCK(boot_lock);
-
-static inline int get_core_count(void)
-{
- /* 1 + the PART[1:0] field of MIDR */
- return ((read_cpuid_id() >> 4) & 3) + 1;
-}
-
-static void msm_secondary_init(unsigned int cpu)
-{
- /*
- * let the primary processor know we're out of the
- * pen, then head off into the C entry point
- */
- pen_release = -1;
- smp_wmb();
-
- /*
- * Synchronise with the boot thread.
- */
- spin_lock(&boot_lock);
- spin_unlock(&boot_lock);
-}
-
-static void prepare_cold_cpu(unsigned int cpu)
-{
- int ret;
- ret = scm_set_boot_addr(virt_to_phys(msm_secondary_startup),
- SCM_FLAG_COLDBOOT_CPU1);
- if (ret == 0) {
- void __iomem *sc1_base_ptr;
- sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2);
- if (sc1_base_ptr) {
- writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
- writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET);
- writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP);
- iounmap(sc1_base_ptr);
- }
- } else
- printk(KERN_DEBUG "Failed to set secondary core boot "
- "address\n");
-}
-
-static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle)
-{
- unsigned long timeout;
- static int cold_boot_done;
-
- /* Only need to bring cpu out of reset this way once */
- if (cold_boot_done == false) {
- prepare_cold_cpu(cpu);
- cold_boot_done = true;
- }
-
- /*
- * set synchronisation state between this boot processor
- * and the secondary one
- */
- spin_lock(&boot_lock);
-
- /*
- * The secondary processor is waiting to be released from
- * the holding pen - release it, then wait for it to flag
- * that it has been released by resetting pen_release.
- *
- * Note that "pen_release" is the hardware CPU ID, whereas
- * "cpu" is Linux's internal ID.
- */
- pen_release = cpu_logical_map(cpu);
- sync_cache_w(&pen_release);
-
- /*
- * Send the secondary CPU a soft interrupt, thereby causing
- * the boot monitor to read the system wide flags register,
- * and branch to the address found there.
- */
- arch_send_wakeup_ipi_mask(cpumask_of(cpu));
-
- timeout = jiffies + (1 * HZ);
- while (time_before(jiffies, timeout)) {
- smp_rmb();
- if (pen_release == -1)
- break;
-
- udelay(10);
- }
-
- /*
- * now the secondary core is starting up let it run its
- * calibrations, then wait for it to finish
- */
- spin_unlock(&boot_lock);
-
- return pen_release != -1 ? -ENOSYS : 0;
-}
-
-/*
- * Initialise the CPU possible map early - this describes the CPUs
- * which may be present or become present in the system. The msm8x60
- * does not support the ARM SCU, so just set the possible cpu mask to
- * NR_CPUS.
- */
-static void __init msm_smp_init_cpus(void)
-{
- unsigned int i, ncores = get_core_count();
-
- if (ncores > nr_cpu_ids) {
- pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
- ncores, nr_cpu_ids);
- ncores = nr_cpu_ids;
- }
-
- for (i = 0; i < ncores; i++)
- set_cpu_possible(i, true);
-}
-
-static void __init msm_smp_prepare_cpus(unsigned int max_cpus)
-{
-}
-
-struct smp_operations msm_smp_ops __initdata = {
- .smp_init_cpus = msm_smp_init_cpus,
- .smp_prepare_cpus = msm_smp_prepare_cpus,
- .smp_secondary_init = msm_secondary_init,
- .smp_boot_secondary = msm_boot_secondary,
-#ifdef CONFIG_HOTPLUG_CPU
- .cpu_die = msm_cpu_die,
-#endif
-};
diff --git a/arch/arm/mach-mv78xx0/common.c b/arch/arm/mach-mv78xx0/common.c
index 75062eff2494..e6ac679bece9 100644
--- a/arch/arm/mach-mv78xx0/common.c
+++ b/arch/arm/mach-mv78xx0/common.c
@@ -15,11 +15,11 @@
#include <linux/ata_platform.h>
#include <linux/clk-provider.h>
#include <linux/ethtool.h>
+#include <asm/hardware/cache-feroceon-l2.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <mach/mv78xx0.h>
#include <mach/bridge-regs.h>
-#include <plat/cache-feroceon-l2.h>
#include <linux/platform_data/usb-ehci-orion.h>
#include <linux/platform_data/mtd-orion_nand.h>
#include <plat/time.h>
diff --git a/arch/arm/mach-mv78xx0/include/mach/bridge-regs.h b/arch/arm/mach-mv78xx0/include/mach/bridge-regs.h
index 5f03484584d4..e20d6da234a6 100644
--- a/arch/arm/mach-mv78xx0/include/mach/bridge-regs.h
+++ b/arch/arm/mach-mv78xx0/include/mach/bridge-regs.h
@@ -15,6 +15,7 @@
#define L2_WRITETHROUGH 0x00020000
#define RSTOUTn_MASK (BRIDGE_VIRT_BASE + 0x0108)
+#define RSTOUTn_MASK_PHYS (BRIDGE_PHYS_BASE + 0x0108)
#define SOFT_RESET_OUT_EN 0x00000004
#define SYSTEM_SOFT_RESET (BRIDGE_VIRT_BASE + 0x010c)
diff --git a/arch/arm/mach-mv78xx0/include/mach/timex.h b/arch/arm/mach-mv78xx0/include/mach/timex.h
deleted file mode 100644
index 0e8c443c723a..000000000000
--- a/arch/arm/mach-mv78xx0/include/mach/timex.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * arch/arm/mach-mv78xx0/include/mach/timex.h
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#define CLOCK_TICK_RATE (100 * HZ)
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index 5e269d7263ce..3f73eecbcfb0 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -1,51 +1,109 @@
config ARCH_MVEBU
- bool "Marvell SOCs with Device Tree support" if ARCH_MULTI_V7
+ bool "Marvell Engineering Business Unit (MVEBU) SoCs" if (ARCH_MULTI_V7 || ARCH_MULTI_V5)
select ARCH_SUPPORTS_BIG_ENDIAN
select CLKSRC_MMIO
- select COMMON_CLK
- select GENERIC_CLOCKEVENTS
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
- select MULTI_IRQ_HANDLER
select PINCTRL
select PLAT_ORION
- select SPARSE_IRQ
- select CLKDEV_LOOKUP
select MVEBU_MBUS
select ZONE_DMA if ARM_LPAE
select ARCH_REQUIRE_GPIOLIB
select MIGHT_HAVE_PCI
select PCI_QUIRKS if PCI
+ select OF_ADDRESS_PCI
if ARCH_MVEBU
-menu "Marvell SOC with device tree"
+menu "Marvell EBU SoC variants"
-config MACH_ARMADA_370_XP
+config MACH_MVEBU_V7
bool
select ARMADA_370_XP_TIMER
- select HAVE_SMP
select CACHE_L2X0
- select CPU_PJ4B
config MACH_ARMADA_370
- bool "Marvell Armada 370 boards"
+ bool "Marvell Armada 370 boards" if ARCH_MULTI_V7
select ARMADA_370_CLK
- select MACH_ARMADA_370_XP
+ select CPU_PJ4B
+ select MACH_MVEBU_V7
select PINCTRL_ARMADA_370
help
Say 'Y' here if you want your kernel to support boards based
on the Marvell Armada 370 SoC with device tree.
+config MACH_ARMADA_375
+ bool "Marvell Armada 375 boards" if ARCH_MULTI_V7
+ select ARM_ERRATA_720789
+ select ARM_ERRATA_753970
+ select ARM_GIC
+ select ARMADA_375_CLK
+ select CPU_V7
+ select MACH_MVEBU_V7
+ select PINCTRL_ARMADA_375
+ help
+ Say 'Y' here if you want your kernel to support boards based
+ on the Marvell Armada 375 SoC with device tree.
+
+config MACH_ARMADA_38X
+ bool "Marvell Armada 380/385 boards" if ARCH_MULTI_V7
+ select ARM_ERRATA_720789
+ select ARM_ERRATA_753970
+ select ARM_GIC
+ select ARMADA_38X_CLK
+ select CPU_V7
+ select MACH_MVEBU_V7
+ select PINCTRL_ARMADA_38X
+ help
+ Say 'Y' here if you want your kernel to support boards based
+ on the Marvell Armada 380/385 SoC with device tree.
+
config MACH_ARMADA_XP
- bool "Marvell Armada XP boards"
+ bool "Marvell Armada XP boards" if ARCH_MULTI_V7
select ARMADA_XP_CLK
- select MACH_ARMADA_370_XP
+ select CPU_PJ4B
+ select MACH_MVEBU_V7
select PINCTRL_ARMADA_XP
help
Say 'Y' here if you want your kernel to support boards based
on the Marvell Armada XP SoC with device tree.
+config MACH_DOVE
+ bool "Marvell Dove boards" if ARCH_MULTI_V7
+ select CACHE_L2X0
+ select CPU_PJ4
+ select DOVE_CLK
+ select ORION_IRQCHIP
+ select ORION_TIMER
+ select PINCTRL_DOVE
+ help
+ Say 'Y' here if you want your kernel to support the
+ Marvell Dove using flattened device tree.
+
+config MACH_KIRKWOOD
+ bool "Marvell Kirkwood boards" if ARCH_MULTI_V5
+ select ARCH_HAS_CPUFREQ
+ select ARCH_REQUIRE_GPIOLIB
+ select CPU_FEROCEON
+ select KIRKWOOD_CLK
+ select OF_IRQ
+ select ORION_IRQCHIP
+ select ORION_TIMER
+ select PCI
+ select PCI_QUIRKS
+ select PINCTRL_KIRKWOOD
+ select USE_OF
+ help
+ Say 'Y' here if you want your kernel to support boards based
+ on the Marvell Kirkwood device tree.
+
+config MACH_T5325
+ bool "HP T5325 thin client"
+ depends on MACH_KIRKWOOD
+ help
+ Say 'Y' here if you want your kernel to support the
+ HP T5325 Thin client
+
endmenu
endif
diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile
index 878aebe98dcc..a63e43b6b451 100644
--- a/arch/arm/mach-mvebu/Makefile
+++ b/arch/arm/mach-mvebu/Makefile
@@ -4,7 +4,10 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \
AFLAGS_coherency_ll.o := -Wa,-march=armv7-a
obj-y += system-controller.o mvebu-soc-id.o
-obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o
+obj-$(CONFIG_MACH_MVEBU_V7) += board-v7.o
+obj-$(CONFIG_MACH_DOVE) += dove.o
obj-$(CONFIG_ARCH_MVEBU) += coherency.o coherency_ll.o pmsu.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o
+obj-$(CONFIG_MACH_T5325) += board-t5325.o
diff --git a/arch/arm/mach-mvebu/board-t5325.c b/arch/arm/mach-mvebu/board-t5325.c
new file mode 100644
index 000000000000..65ace6db9f28
--- /dev/null
+++ b/arch/arm/mach-mvebu/board-t5325.c
@@ -0,0 +1,41 @@
+/*
+ * HP T5325 Board Setup
+ *
+ * Copyright (C) 2014
+ *
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <sound/alc5623.h>
+#include "board.h"
+
+static struct platform_device hp_t5325_audio_device = {
+ .name = "t5325-audio",
+ .id = -1,
+};
+
+static struct alc5623_platform_data alc5621_data = {
+ .add_ctrl = 0x3700,
+ .jack_det_ctrl = 0x4810,
+};
+
+static struct i2c_board_info i2c_board_info[] __initdata = {
+ {
+ I2C_BOARD_INFO("alc5621", 0x1a),
+ .platform_data = &alc5621_data,
+ },
+};
+
+void __init t5325_init(void)
+{
+ i2c_register_board_info(0, i2c_board_info, ARRAY_SIZE(i2c_board_info));
+ platform_device_register(&hp_t5325_audio_device);
+}
diff --git a/arch/arm/mach-mvebu/armada-370-xp.c b/arch/arm/mach-mvebu/board-v7.c
index f6c9d1d85c14..333fca8fdc41 100644
--- a/arch/arm/mach-mvebu/armada-370-xp.c
+++ b/arch/arm/mach-mvebu/board-v7.c
@@ -21,6 +21,7 @@
#include <linux/clocksource.h>
#include <linux/dma-mapping.h>
#include <linux/mbus.h>
+#include <linux/signal.h>
#include <linux/slab.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/arch.h>
@@ -31,12 +32,28 @@
#include "coherency.h"
#include "mvebu-soc-id.h"
-static void __init armada_370_xp_map_io(void)
+/*
+ * Early versions of Armada 375 SoC have a bug where the BootROM
+ * leaves an external data abort pending. The kernel is hit by this
+ * data abort as soon as it enters userspace, because it unmasks the
+ * data aborts at this moment. We register a custom abort handler
+ * below to ignore the first data abort to work around this
+ * problem.
+ */
+static int armada_375_external_abort_wa(unsigned long addr, unsigned int fsr,
+ struct pt_regs *regs)
{
- debug_ll_io_init();
+ static int ignore_first;
+
+ if (!ignore_first && fsr == 0x1406) {
+ ignore_first = 1;
+ return 0;
+ }
+
+ return 1;
}
-static void __init armada_370_xp_timer_and_clk_init(void)
+static void __init mvebu_timer_and_clk_init(void)
{
of_clk_init(NULL);
clocksource_of_init();
@@ -45,6 +62,10 @@ static void __init armada_370_xp_timer_and_clk_init(void)
#ifdef CONFIG_CACHE_L2X0
l2x0_of_init(0, ~0UL);
#endif
+
+ if (of_machine_is_compatible("marvell,armada375"))
+ hook_fault_code(16 + 6, armada_375_external_abort_wa, SIGBUS, 0,
+ "imprecise external abort");
}
static void __init i2c_quirk(void)
@@ -75,7 +96,7 @@ static void __init i2c_quirk(void)
return;
}
-static void __init armada_370_xp_dt_init(void)
+static void __init mvebu_dt_init(void)
{
if (of_machine_is_compatible("plathome,openblocks-ax3-4"))
i2c_quirk();
@@ -87,11 +108,33 @@ static const char * const armada_370_xp_dt_compat[] = {
NULL,
};
-DT_MACHINE_START(ARMADA_XP_DT, "Marvell Armada 370/XP (Device Tree)")
+DT_MACHINE_START(ARMADA_370_XP_DT, "Marvell Armada 370/XP (Device Tree)")
.smp = smp_ops(armada_xp_smp_ops),
- .init_machine = armada_370_xp_dt_init,
- .map_io = armada_370_xp_map_io,
- .init_time = armada_370_xp_timer_and_clk_init,
+ .init_machine = mvebu_dt_init,
+ .init_time = mvebu_timer_and_clk_init,
.restart = mvebu_restart,
.dt_compat = armada_370_xp_dt_compat,
MACHINE_END
+
+static const char * const armada_375_dt_compat[] = {
+ "marvell,armada375",
+ NULL,
+};
+
+DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)")
+ .init_time = mvebu_timer_and_clk_init,
+ .restart = mvebu_restart,
+ .dt_compat = armada_375_dt_compat,
+MACHINE_END
+
+static const char * const armada_38x_dt_compat[] = {
+ "marvell,armada380",
+ "marvell,armada385",
+ NULL,
+};
+
+DT_MACHINE_START(ARMADA_38X_DT, "Marvell Armada 380/385 (Device Tree)")
+ .init_time = mvebu_timer_and_clk_init,
+ .restart = mvebu_restart,
+ .dt_compat = armada_38x_dt_compat,
+MACHINE_END
diff --git a/arch/arm/mach-mvebu/board.h b/arch/arm/mach-mvebu/board.h
new file mode 100644
index 000000000000..de7f0a191394
--- /dev/null
+++ b/arch/arm/mach-mvebu/board.h
@@ -0,0 +1,22 @@
+/*
+ * Board functions for Marvell System On Chip
+ *
+ * Copyright (C) 2014
+ *
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __ARCH_MVEBU_BOARD_H
+#define __ARCH_MVEBU_BOARD_H
+
+#ifdef CONFIG_MACH_T5325
+void t5325_init(void);
+#else
+static inline void t5325_init(void) {};
+#endif
+
+#endif
diff --git a/arch/arm/mach-dove/board-dt.c b/arch/arm/mach-mvebu/dove.c
index 49fa9abd09da..5e5a43624237 100644
--- a/arch/arm/mach-dove/board-dt.c
+++ b/arch/arm/mach-mvebu/dove.c
@@ -1,5 +1,5 @@
/*
- * arch/arm/mach-dove/board-dt.c
+ * arch/arm/mach-mvebu/dove.c
*
* Marvell Dove 88AP510 System On Chip FDT Board
*
@@ -9,17 +9,14 @@
*/
#include <linux/init.h>
-#include <linux/clk-provider.h>
+#include <linux/mbus.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <asm/hardware/cache-tauros2.h>
#include <asm/mach/arch.h>
-#include <mach/dove.h>
-#include <mach/pm.h>
-#include <plat/common.h>
#include "common.h"
-static void __init dove_dt_init(void)
+static void __init dove_init(void)
{
pr_info("Dove 88AP510 SoC\n");
@@ -30,14 +27,13 @@ static void __init dove_dt_init(void)
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
-static const char * const dove_dt_board_compat[] = {
+static const char * const dove_dt_compat[] = {
"marvell,dove",
NULL
};
-DT_MACHINE_START(DOVE_DT, "Marvell Dove (Flattened Device Tree)")
- .map_io = dove_map_io,
- .init_machine = dove_dt_init,
- .restart = dove_restart,
- .dt_compat = dove_dt_board_compat,
+DT_MACHINE_START(DOVE_DT, "Marvell Dove")
+ .init_machine = dove_init,
+ .restart = mvebu_restart,
+ .dt_compat = dove_dt_compat,
MACHINE_END
diff --git a/arch/arm/mach-mvebu/kirkwood-pm.c b/arch/arm/mach-mvebu/kirkwood-pm.c
new file mode 100644
index 000000000000..cbb816f2120c
--- /dev/null
+++ b/arch/arm/mach-mvebu/kirkwood-pm.c
@@ -0,0 +1,76 @@
+/*
+ * Power Management driver for Marvell Kirkwood SoCs
+ *
+ * Copyright (C) 2013 Ezequiel Garcia <ezequiel@free-electrons.com>
+ * Copyright (C) 2010 Simon Guinot <sguinot@lacie.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License,
+ * version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/suspend.h>
+#include <linux/io.h>
+#include "kirkwood.h"
+
+static void __iomem *ddr_operation_base;
+static void __iomem *memory_pm_ctrl;
+
+static void kirkwood_low_power(void)
+{
+ u32 mem_pm_ctrl;
+
+ mem_pm_ctrl = readl(memory_pm_ctrl);
+
+ /* Set peripherals to low-power mode */
+ writel_relaxed(~0, memory_pm_ctrl);
+
+ /* Set DDR in self-refresh */
+ writel_relaxed(0x7, ddr_operation_base);
+
+ /*
+ * Set CPU in wait-for-interrupt state.
+ * This disables the CPU core clocks,
+ * the array clocks, and also the L2 controller.
+ */
+ cpu_do_idle();
+
+ writel_relaxed(mem_pm_ctrl, memory_pm_ctrl);
+}
+
+static int kirkwood_suspend_enter(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ kirkwood_low_power();
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int kirkwood_pm_valid_standby(suspend_state_t state)
+{
+ return state == PM_SUSPEND_STANDBY;
+}
+
+static const struct platform_suspend_ops kirkwood_suspend_ops = {
+ .enter = kirkwood_suspend_enter,
+ .valid = kirkwood_pm_valid_standby,
+};
+
+int __init kirkwood_pm_init(void)
+{
+ ddr_operation_base = ioremap(DDR_OPERATION_BASE, 4);
+ memory_pm_ctrl = ioremap(MEMORY_PM_CTRL_PHYS, 4);
+
+ suspend_set_ops(&kirkwood_suspend_ops);
+ return 0;
+}
diff --git a/arch/arm/mach-mvebu/kirkwood-pm.h b/arch/arm/mach-mvebu/kirkwood-pm.h
new file mode 100644
index 000000000000..21e7530f368b
--- /dev/null
+++ b/arch/arm/mach-mvebu/kirkwood-pm.h
@@ -0,0 +1,26 @@
+/*
+ * Power Management driver for Marvell Kirkwood SoCs
+ *
+ * Copyright (C) 2013 Ezequiel Garcia <ezequiel@free-electrons.com>
+ * Copyright (C) 2010 Simon Guinot <sguinot@lacie.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License,
+ * 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.
+ */
+
+#ifndef __ARCH_KIRKWOOD_PM_H
+#define __ARCH_KIRKWOOD_PM_H
+
+#ifdef CONFIG_PM
+void kirkwood_pm_init(void);
+#else
+static inline void kirkwood_pm_init(void) {};
+#endif
+
+#endif
diff --git a/arch/arm/mach-mvebu/kirkwood.c b/arch/arm/mach-mvebu/kirkwood.c
new file mode 100644
index 000000000000..120207fc36f1
--- /dev/null
+++ b/arch/arm/mach-mvebu/kirkwood.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2012 (C), Jason Cooper <jason@lakedaemon.net>
+ *
+ * arch/arm/mach-mvebu/kirkwood.c
+ *
+ * Flattened Device Tree board initialization
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mbus.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <asm/hardware/cache-feroceon-l2.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include "kirkwood.h"
+#include "kirkwood-pm.h"
+#include "common.h"
+#include "board.h"
+
+static struct resource kirkwood_cpufreq_resources[] = {
+ [0] = {
+ .start = CPU_CONTROL_PHYS,
+ .end = CPU_CONTROL_PHYS + 3,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device kirkwood_cpufreq_device = {
+ .name = "kirkwood-cpufreq",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(kirkwood_cpufreq_resources),
+ .resource = kirkwood_cpufreq_resources,
+};
+
+static void __init kirkwood_cpufreq_init(void)
+{
+ platform_device_register(&kirkwood_cpufreq_device);
+}
+
+static struct resource kirkwood_cpuidle_resource[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ .start = DDR_OPERATION_BASE,
+ .end = DDR_OPERATION_BASE + 3,
+ },
+};
+
+static struct platform_device kirkwood_cpuidle = {
+ .name = "kirkwood_cpuidle",
+ .id = -1,
+ .resource = kirkwood_cpuidle_resource,
+ .num_resources = 1,
+};
+
+static void __init kirkwood_cpuidle_init(void)
+{
+ platform_device_register(&kirkwood_cpuidle);
+}
+
+#define MV643XX_ETH_MAC_ADDR_LOW 0x0414
+#define MV643XX_ETH_MAC_ADDR_HIGH 0x0418
+
+static void __init kirkwood_dt_eth_fixup(void)
+{
+ struct device_node *np;
+
+ /*
+ * The ethernet interfaces forget the MAC address assigned by u-boot
+ * if the clocks are turned off. Usually, u-boot on kirkwood boards
+ * has no DT support to properly set local-mac-address property.
+ * As a workaround, we get the MAC address from mv643xx_eth registers
+ * and update the port device node if no valid MAC address is set.
+ */
+ for_each_compatible_node(np, NULL, "marvell,kirkwood-eth-port") {
+ struct device_node *pnp = of_get_parent(np);
+ struct clk *clk;
+ struct property *pmac;
+ void __iomem *io;
+ u8 *macaddr;
+ u32 reg;
+
+ if (!pnp)
+ continue;
+
+ /* skip disabled nodes or nodes with valid MAC address*/
+ if (!of_device_is_available(pnp) || of_get_mac_address(np))
+ goto eth_fixup_skip;
+
+ clk = of_clk_get(pnp, 0);
+ if (IS_ERR(clk))
+ goto eth_fixup_skip;
+
+ io = of_iomap(pnp, 0);
+ if (!io)
+ goto eth_fixup_no_map;
+
+ /* ensure port clock is not gated to not hang CPU */
+ clk_prepare_enable(clk);
+
+ /* store MAC address register contents in local-mac-address */
+ pr_err(FW_INFO "%s: local-mac-address is not set\n",
+ np->full_name);
+
+ pmac = kzalloc(sizeof(*pmac) + 6, GFP_KERNEL);
+ if (!pmac)
+ goto eth_fixup_no_mem;
+
+ pmac->value = pmac + 1;
+ pmac->length = 6;
+ pmac->name = kstrdup("local-mac-address", GFP_KERNEL);
+ if (!pmac->name) {
+ kfree(pmac);
+ goto eth_fixup_no_mem;
+ }
+
+ macaddr = pmac->value;
+ reg = readl(io + MV643XX_ETH_MAC_ADDR_HIGH);
+ macaddr[0] = (reg >> 24) & 0xff;
+ macaddr[1] = (reg >> 16) & 0xff;
+ macaddr[2] = (reg >> 8) & 0xff;
+ macaddr[3] = reg & 0xff;
+
+ reg = readl(io + MV643XX_ETH_MAC_ADDR_LOW);
+ macaddr[4] = (reg >> 8) & 0xff;
+ macaddr[5] = reg & 0xff;
+
+ of_update_property(np, pmac);
+
+eth_fixup_no_mem:
+ iounmap(io);
+ clk_disable_unprepare(clk);
+eth_fixup_no_map:
+ clk_put(clk);
+eth_fixup_skip:
+ of_node_put(pnp);
+ }
+}
+
+/*
+ * Disable propagation of mbus errors to the CPU local bus, as this
+ * causes mbus errors (which can occur for example for PCI aborts) to
+ * throw CPU aborts, which we're not set up to deal with.
+ */
+void kirkwood_disable_mbus_error_propagation(void)
+{
+ void __iomem *cpu_config;
+
+ cpu_config = ioremap(CPU_CONFIG_PHYS, 4);
+ writel(readl(cpu_config) & ~CPU_CONFIG_ERROR_PROP, cpu_config);
+}
+
+static struct of_dev_auxdata auxdata[] __initdata = {
+ OF_DEV_AUXDATA("marvell,kirkwood-audio", 0xf10a0000,
+ "mvebu-audio", NULL),
+ { /* sentinel */ }
+};
+
+static void __init kirkwood_dt_init(void)
+{
+ kirkwood_disable_mbus_error_propagation();
+
+ BUG_ON(mvebu_mbus_dt_init());
+
+#ifdef CONFIG_CACHE_FEROCEON_L2
+ feroceon_of_init();
+#endif
+ kirkwood_cpufreq_init();
+ kirkwood_cpuidle_init();
+
+ kirkwood_pm_init();
+ kirkwood_dt_eth_fixup();
+
+ if (of_machine_is_compatible("hp,t5325"))
+ t5325_init();
+
+ of_platform_populate(NULL, of_default_bus_match_table, auxdata, NULL);
+}
+
+static const char * const kirkwood_dt_board_compat[] = {
+ "marvell,kirkwood",
+ NULL
+};
+
+DT_MACHINE_START(KIRKWOOD_DT, "Marvell Kirkwood (Flattened Device Tree)")
+ /* Maintainer: Jason Cooper <jason@lakedaemon.net> */
+ .init_machine = kirkwood_dt_init,
+ .restart = mvebu_restart,
+ .dt_compat = kirkwood_dt_board_compat,
+MACHINE_END
diff --git a/arch/arm/mach-mvebu/kirkwood.h b/arch/arm/mach-mvebu/kirkwood.h
new file mode 100644
index 000000000000..89f3d1f51643
--- /dev/null
+++ b/arch/arm/mach-mvebu/kirkwood.h
@@ -0,0 +1,22 @@
+/*
+ * arch/arm/mach-mvebu/kirkwood.h
+ *
+ * Generic definitions for Marvell Kirkwood SoC flavors:
+ * 88F6180, 88F6192 and 88F6281.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#define KIRKWOOD_REGS_PHYS_BASE 0xf1000000
+#define DDR_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE + 0x00000)
+#define BRIDGE_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE + 0x20000)
+
+#define DDR_OPERATION_BASE (DDR_PHYS_BASE + 0x1418)
+
+#define CPU_CONFIG_PHYS (BRIDGE_PHYS_BASE + 0x0100)
+#define CPU_CONFIG_ERROR_PROP 0x00000004
+
+#define CPU_CONTROL_PHYS (BRIDGE_PHYS_BASE + 0x0104)
+#define MEMORY_PM_CTRL_PHYS (BRIDGE_PHYS_BASE + 0x0118)
diff --git a/arch/arm/mach-mvebu/mvebu-soc-id.c b/arch/arm/mach-mvebu/mvebu-soc-id.c
index f3b325f6cbd4..f3d4cf53f746 100644
--- a/arch/arm/mach-mvebu/mvebu-soc-id.c
+++ b/arch/arm/mach-mvebu/mvebu-soc-id.c
@@ -38,6 +38,7 @@ static bool is_id_valid;
static const struct of_device_id mvebu_pcie_of_match_table[] = {
{ .compatible = "marvell,armada-xp-pcie", },
{ .compatible = "marvell,armada-370-pcie", },
+ { .compatible = "marvell,kirkwood-pcie" },
{},
};
diff --git a/arch/arm/mach-mvebu/system-controller.c b/arch/arm/mach-mvebu/system-controller.c
index a7fb89a5b5d9..614ba6832ff3 100644
--- a/arch/arm/mach-mvebu/system-controller.c
+++ b/arch/arm/mach-mvebu/system-controller.c
@@ -1,5 +1,5 @@
/*
- * System controller support for Armada 370 and XP platforms.
+ * System controller support for Armada 370, 375 and XP platforms.
*
* Copyright (C) 2012 Marvell
*
@@ -11,7 +11,7 @@
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
- * The Armada 370 and Armada XP SoCs both have a range of
+ * The Armada 370, 375 and Armada XP SoCs have a range of
* miscellaneous registers, that do not belong to a particular device,
* but rather provide system-level features. This basic
* system-controller driver provides a device tree binding for those
@@ -47,6 +47,13 @@ static const struct mvebu_system_controller armada_370_xp_system_controller = {
.system_soft_reset = 0x1,
};
+static const struct mvebu_system_controller armada_375_system_controller = {
+ .rstoutn_mask_offset = 0x54,
+ .system_soft_reset_offset = 0x58,
+ .rstoutn_mask_reset_out_en = 0x1,
+ .system_soft_reset = 0x1,
+};
+
static const struct mvebu_system_controller orion_system_controller = {
.rstoutn_mask_offset = 0x108,
.system_soft_reset_offset = 0x10c,
@@ -54,13 +61,16 @@ static const struct mvebu_system_controller orion_system_controller = {
.system_soft_reset = 0x1,
};
-static struct of_device_id of_system_controller_table[] = {
+static const struct of_device_id of_system_controller_table[] = {
{
.compatible = "marvell,orion-system-controller",
.data = (void *) &orion_system_controller,
}, {
.compatible = "marvell,armada-370-xp-system-controller",
.data = (void *) &armada_370_xp_system_controller,
+ }, {
+ .compatible = "marvell,armada-375-system-controller",
+ .data = (void *) &armada_375_system_controller,
},
{ /* end of list */ },
};
@@ -90,13 +100,12 @@ void mvebu_restart(enum reboot_mode mode, const char *cmd)
static int __init mvebu_system_controller_init(void)
{
+ const struct of_device_id *match;
struct device_node *np;
- np = of_find_matching_node(NULL, of_system_controller_table);
+ np = of_find_matching_node_and_match(NULL, of_system_controller_table,
+ &match);
if (np) {
- const struct of_device_id *match =
- of_match_node(of_system_controller_table, np);
- BUG_ON(!match);
system_controller_base = of_iomap(np, 0);
mvebu_sc = (struct mvebu_system_controller *)match->data;
of_node_put(np);
diff --git a/arch/arm/mach-mxs/Kconfig b/arch/arm/mach-mxs/Kconfig
index 8cde9e05b5d6..84794137b175 100644
--- a/arch/arm/mach-mxs/Kconfig
+++ b/arch/arm/mach-mxs/Kconfig
@@ -16,11 +16,7 @@ config ARCH_MXS
bool "Freescale MXS (i.MX23, i.MX28) support"
depends on ARCH_MULTI_V5
select ARCH_REQUIRE_GPIOLIB
- select CLKDEV_LOOKUP
select CLKSRC_MMIO
- select CLKSRC_OF
- select GENERIC_CLOCKEVENTS
- select HAVE_CLK_PREPARE
select PINCTRL
select SOC_BUS
select SOC_IMX23
diff --git a/arch/arm/mach-mxs/mach-mxs.c b/arch/arm/mach-mxs/mach-mxs.c
index 1dc5acd4fc99..2e7cec86e50e 100644
--- a/arch/arm/mach-mxs/mach-mxs.c
+++ b/arch/arm/mach-mxs/mach-mxs.c
@@ -157,6 +157,8 @@ enum mac_oui {
OUI_FSL,
OUI_DENX,
OUI_CRYSTALFONTZ,
+ OUI_I2SE,
+ OUI_ARMADEUS,
};
static void __init update_fec_mac_prop(enum mac_oui oui)
@@ -211,6 +213,16 @@ static void __init update_fec_mac_prop(enum mac_oui oui)
macaddr[1] = 0xb9;
macaddr[2] = 0xe1;
break;
+ case OUI_I2SE:
+ macaddr[0] = 0x00;
+ macaddr[1] = 0x01;
+ macaddr[2] = 0x87;
+ break;
+ case OUI_ARMADEUS:
+ macaddr[0] = 0x00;
+ macaddr[1] = 0x1e;
+ macaddr[2] = 0xac;
+ break;
}
val = ocotp[i];
macaddr[3] = (val >> 16) & 0xff;
@@ -236,6 +248,11 @@ static void __init imx28_evk_init(void)
mxs_saif_clkmux_select(MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0);
}
+static void __init imx28_apf28_init(void)
+{
+ update_fec_mac_prop(OUI_ARMADEUS);
+}
+
static int apx4devkit_phy_fixup(struct phy_device *phy)
{
phy->dev_flags |= MICREL_PHY_50MHZ_CLK;
@@ -330,6 +347,11 @@ static void __init crystalfontz_init(void)
update_fec_mac_prop(OUI_CRYSTALFONTZ);
}
+static void __init duckbill_init(void)
+{
+ update_fec_mac_prop(OUI_I2SE);
+}
+
static void __init m28cu3_init(void)
{
update_fec_mac_prop(OUI_DENX);
@@ -426,6 +448,11 @@ static int __init mxs_restart_init(void)
return 0;
}
+static void __init eukrea_mbmx283lc_init(void)
+{
+ mxs_saif_clkmux_select(MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0);
+}
+
static void __init mxs_machine_init(void)
{
struct device_node *root;
@@ -458,10 +485,16 @@ static void __init mxs_machine_init(void)
if (of_machine_is_compatible("fsl,imx28-evk"))
imx28_evk_init();
+ if (of_machine_is_compatible("armadeus,imx28-apf28"))
+ imx28_apf28_init();
else if (of_machine_is_compatible("bluegiga,apx4devkit"))
apx4devkit_init();
else if (of_machine_is_compatible("crystalfontz,cfa10036"))
crystalfontz_init();
+ else if (of_machine_is_compatible("eukrea,mbmx283lc"))
+ eukrea_mbmx283lc_init();
+ else if (of_machine_is_compatible("i2se,duckbill"))
+ duckbill_init();
else if (of_machine_is_compatible("msr,m28cu3"))
m28cu3_init();
diff --git a/arch/arm/mach-netx/include/mach/timex.h b/arch/arm/mach-netx/include/mach/timex.h
deleted file mode 100644
index 1120dd0ba393..000000000000
--- a/arch/arm/mach-netx/include/mach/timex.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * arch/arm/mach-netx/include/mach/timex.h
- *
- * Copyright (C) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#define CLOCK_TICK_RATE 100000000
diff --git a/arch/arm/mach-netx/time.c b/arch/arm/mach-netx/time.c
index 6df42e643031..5fb2a590ec17 100644
--- a/arch/arm/mach-netx/time.c
+++ b/arch/arm/mach-netx/time.c
@@ -28,6 +28,9 @@
#include <asm/mach/time.h>
#include <mach/netx-regs.h>
+#define NETX_CLOCK_FREQ 100000000
+#define NETX_LATCH DIV_ROUND_CLOSEST(NETX_CLOCK_FREQ, HZ)
+
#define TIMER_CLOCKEVENT 0
#define TIMER_CLOCKSOURCE 1
@@ -41,7 +44,7 @@ static void netx_set_mode(enum clock_event_mode mode,
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
- writel(LATCH, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKEVENT));
+ writel(NETX_LATCH, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKEVENT));
tmode = NETX_GPIO_COUNTER_CTRL_RST_EN |
NETX_GPIO_COUNTER_CTRL_IRQ_EN |
NETX_GPIO_COUNTER_CTRL_RUN;
@@ -99,7 +102,7 @@ netx_timer_interrupt(int irq, void *dev_id)
static struct irqaction netx_timer_irq = {
.name = "NetX Timer Tick",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = netx_timer_interrupt,
};
@@ -114,7 +117,7 @@ void __init netx_timer_init(void)
/* Reset the timer value to zero */
writel(0, NETX_GPIO_COUNTER_CURRENT(0));
- writel(LATCH, NETX_GPIO_COUNTER_MAX(0));
+ writel(NETX_LATCH, NETX_GPIO_COUNTER_MAX(0));
/* acknowledge interrupt */
writel(COUNTER_BIT(0), NETX_GPIO_IRQ);
@@ -137,11 +140,11 @@ void __init netx_timer_init(void)
NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKSOURCE));
clocksource_mmio_init(NETX_GPIO_COUNTER_CURRENT(TIMER_CLOCKSOURCE),
- "netx_timer", CLOCK_TICK_RATE, 200, 32, clocksource_mmio_readl_up);
+ "netx_timer", NETX_CLOCK_FREQ, 200, 32, clocksource_mmio_readl_up);
/* with max_delta_ns >= delta2ns(0x800) the system currently runs fine.
* Adding some safety ... */
netx_clockevent.cpumask = cpumask_of(0);
- clockevents_config_and_register(&netx_clockevent, CLOCK_TICK_RATE,
+ clockevents_config_and_register(&netx_clockevent, NETX_CLOCK_FREQ,
0xa00, 0xfffffffe);
}
diff --git a/arch/arm/mach-nomadik/Kconfig b/arch/arm/mach-nomadik/Kconfig
index 4d42da49753c..486d301f43fd 100644
--- a/arch/arm/mach-nomadik/Kconfig
+++ b/arch/arm/mach-nomadik/Kconfig
@@ -6,16 +6,11 @@ config ARCH_NOMADIK
select ARM_VIC
select CLKSRC_NOMADIK_MTU
select CLKSRC_NOMADIK_MTU_SCHED_CLOCK
- select CLKSRC_OF
- select COMMON_CLK
select CPU_ARM926T
- select GENERIC_CLOCKEVENTS
select MIGHT_HAVE_CACHE_L2X0
select PINCTRL
select PINCTRL_NOMADIK
select PINCTRL_STN8815
- select SPARSE_IRQ
- select USE_OF
help
Support for the Nomadik platform by ST-Ericsson
diff --git a/arch/arm/mach-nspire/Kconfig b/arch/arm/mach-nspire/Kconfig
index 59d8f0a70919..bc41f26c1a12 100644
--- a/arch/arm/mach-nspire/Kconfig
+++ b/arch/arm/mach-nspire/Kconfig
@@ -3,14 +3,9 @@ config ARCH_NSPIRE
depends on ARCH_MULTI_V4_V5
depends on MMU
select CPU_ARM926T
- select COMMON_CLK
- select GENERIC_CLOCKEVENTS
select GENERIC_IRQ_CHIP
- select SPARSE_IRQ
select ARM_AMBA
select ARM_VIC
select ARM_TIMER_SP804
- select USE_OF
- select CLKSRC_OF
help
This enables support for systems using the TI-NSPIRE CPU
diff --git a/arch/arm/mach-nspire/nspire.c b/arch/arm/mach-nspire/nspire.c
index 4b2ed2e8352f..3d24ebf12095 100644
--- a/arch/arm/mach-nspire/nspire.c
+++ b/arch/arm/mach-nspire/nspire.c
@@ -63,7 +63,7 @@ static void __init nspire_init(void)
nspire_auxdata, NULL);
}
-static void nspire_restart(char mode, const char *cmd)
+static void nspire_restart(enum reboot_mode mode, const char *cmd)
{
void __iomem *base = ioremap(NSPIRE_MISC_PHYS_BASE, SZ_4K);
if (!base)
diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c
index fd90cafc2e36..65d2acb31498 100644
--- a/arch/arm/mach-omap1/board-h2.c
+++ b/arch/arm/mach-omap1/board-h2.c
@@ -318,6 +318,9 @@ static void __init h2_init_smc91x(void)
static int tps_setup(struct i2c_client *client, void *context)
{
+ if (!IS_BUILTIN(CONFIG_TPS65010))
+ return -ENOSYS;
+
tps65010_config_vregs1(TPS_LDO2_ENABLE | TPS_VLDO2_3_0V |
TPS_LDO1_ENABLE | TPS_VLDO1_3_0V);
diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c
index d68909b095f1..3a0262156e93 100644
--- a/arch/arm/mach-omap1/board-osk.c
+++ b/arch/arm/mach-omap1/board-osk.c
@@ -191,6 +191,9 @@ static struct platform_device osk5912_tps_leds = {
static int osk_tps_setup(struct i2c_client *client, void *context)
{
+ if (!IS_BUILTIN(CONFIG_TPS65010))
+ return -ENOSYS;
+
/* Set GPIO 1 HIGH to disable VBUS power supply;
* OHCI driver powers it up/down as needed.
*/
diff --git a/arch/arm/mach-omap1/dma.c b/arch/arm/mach-omap1/dma.c
index 5bb8ce86d54b..4be601b638d7 100644
--- a/arch/arm/mach-omap1/dma.c
+++ b/arch/arm/mach-omap1/dma.c
@@ -32,55 +32,51 @@
#define OMAP1_DMA_BASE (0xfffed800)
#define OMAP1_LOGICAL_DMA_CH_COUNT 17
-#define OMAP1_DMA_STRIDE 0x40
-static u32 errata;
static u32 enable_1510_mode;
-static u8 dma_stride;
-static enum omap_reg_offsets dma_common_ch_start, dma_common_ch_end;
-
-static u16 reg_map[] = {
- [GCR] = 0x400,
- [GSCR] = 0x404,
- [GRST1] = 0x408,
- [HW_ID] = 0x442,
- [PCH2_ID] = 0x444,
- [PCH0_ID] = 0x446,
- [PCH1_ID] = 0x448,
- [PCHG_ID] = 0x44a,
- [PCHD_ID] = 0x44c,
- [CAPS_0] = 0x44e,
- [CAPS_1] = 0x452,
- [CAPS_2] = 0x456,
- [CAPS_3] = 0x458,
- [CAPS_4] = 0x45a,
- [PCH2_SR] = 0x460,
- [PCH0_SR] = 0x480,
- [PCH1_SR] = 0x482,
- [PCHD_SR] = 0x4c0,
+
+static const struct omap_dma_reg reg_map[] = {
+ [GCR] = { 0x0400, 0x00, OMAP_DMA_REG_16BIT },
+ [GSCR] = { 0x0404, 0x00, OMAP_DMA_REG_16BIT },
+ [GRST1] = { 0x0408, 0x00, OMAP_DMA_REG_16BIT },
+ [HW_ID] = { 0x0442, 0x00, OMAP_DMA_REG_16BIT },
+ [PCH2_ID] = { 0x0444, 0x00, OMAP_DMA_REG_16BIT },
+ [PCH0_ID] = { 0x0446, 0x00, OMAP_DMA_REG_16BIT },
+ [PCH1_ID] = { 0x0448, 0x00, OMAP_DMA_REG_16BIT },
+ [PCHG_ID] = { 0x044a, 0x00, OMAP_DMA_REG_16BIT },
+ [PCHD_ID] = { 0x044c, 0x00, OMAP_DMA_REG_16BIT },
+ [CAPS_0] = { 0x044e, 0x00, OMAP_DMA_REG_2X16BIT },
+ [CAPS_1] = { 0x0452, 0x00, OMAP_DMA_REG_2X16BIT },
+ [CAPS_2] = { 0x0456, 0x00, OMAP_DMA_REG_16BIT },
+ [CAPS_3] = { 0x0458, 0x00, OMAP_DMA_REG_16BIT },
+ [CAPS_4] = { 0x045a, 0x00, OMAP_DMA_REG_16BIT },
+ [PCH2_SR] = { 0x0460, 0x00, OMAP_DMA_REG_16BIT },
+ [PCH0_SR] = { 0x0480, 0x00, OMAP_DMA_REG_16BIT },
+ [PCH1_SR] = { 0x0482, 0x00, OMAP_DMA_REG_16BIT },
+ [PCHD_SR] = { 0x04c0, 0x00, OMAP_DMA_REG_16BIT },
/* Common Registers */
- [CSDP] = 0x00,
- [CCR] = 0x02,
- [CICR] = 0x04,
- [CSR] = 0x06,
- [CEN] = 0x10,
- [CFN] = 0x12,
- [CSFI] = 0x14,
- [CSEI] = 0x16,
- [CPC] = 0x18, /* 15xx only */
- [CSAC] = 0x18,
- [CDAC] = 0x1a,
- [CDEI] = 0x1c,
- [CDFI] = 0x1e,
- [CLNK_CTRL] = 0x28,
+ [CSDP] = { 0x0000, 0x40, OMAP_DMA_REG_16BIT },
+ [CCR] = { 0x0002, 0x40, OMAP_DMA_REG_16BIT },
+ [CICR] = { 0x0004, 0x40, OMAP_DMA_REG_16BIT },
+ [CSR] = { 0x0006, 0x40, OMAP_DMA_REG_16BIT },
+ [CEN] = { 0x0010, 0x40, OMAP_DMA_REG_16BIT },
+ [CFN] = { 0x0012, 0x40, OMAP_DMA_REG_16BIT },
+ [CSFI] = { 0x0014, 0x40, OMAP_DMA_REG_16BIT },
+ [CSEI] = { 0x0016, 0x40, OMAP_DMA_REG_16BIT },
+ [CPC] = { 0x0018, 0x40, OMAP_DMA_REG_16BIT }, /* 15xx only */
+ [CSAC] = { 0x0018, 0x40, OMAP_DMA_REG_16BIT },
+ [CDAC] = { 0x001a, 0x40, OMAP_DMA_REG_16BIT },
+ [CDEI] = { 0x001c, 0x40, OMAP_DMA_REG_16BIT },
+ [CDFI] = { 0x001e, 0x40, OMAP_DMA_REG_16BIT },
+ [CLNK_CTRL] = { 0x0028, 0x40, OMAP_DMA_REG_16BIT },
/* Channel specific register offsets */
- [CSSA] = 0x08,
- [CDSA] = 0x0c,
- [COLOR] = 0x20,
- [CCR2] = 0x24,
- [LCH_CTRL] = 0x2a,
+ [CSSA] = { 0x0008, 0x40, OMAP_DMA_REG_2X16BIT },
+ [CDSA] = { 0x000c, 0x40, OMAP_DMA_REG_2X16BIT },
+ [COLOR] = { 0x0020, 0x40, OMAP_DMA_REG_2X16BIT },
+ [CCR2] = { 0x0024, 0x40, OMAP_DMA_REG_16BIT },
+ [LCH_CTRL] = { 0x002a, 0x40, OMAP_DMA_REG_16BIT },
};
static struct resource res[] __initdata = {
@@ -181,44 +177,36 @@ static struct resource res[] __initdata = {
static void __iomem *dma_base;
static inline void dma_write(u32 val, int reg, int lch)
{
- u8 stride;
- u32 offset;
+ void __iomem *addr = dma_base;
- stride = (reg >= dma_common_ch_start) ? dma_stride : 0;
- offset = reg_map[reg] + (stride * lch);
+ addr += reg_map[reg].offset;
+ addr += reg_map[reg].stride * lch;
- __raw_writew(val, dma_base + offset);
- if ((reg > CLNK_CTRL && reg < CCEN) ||
- (reg > PCHD_ID && reg < CAPS_2)) {
- u32 offset2 = reg_map[reg] + 2 + (stride * lch);
- __raw_writew(val >> 16, dma_base + offset2);
- }
+ __raw_writew(val, addr);
+ if (reg_map[reg].type == OMAP_DMA_REG_2X16BIT)
+ __raw_writew(val >> 16, addr + 2);
}
static inline u32 dma_read(int reg, int lch)
{
- u8 stride;
- u32 offset, val;
-
- stride = (reg >= dma_common_ch_start) ? dma_stride : 0;
- offset = reg_map[reg] + (stride * lch);
-
- val = __raw_readw(dma_base + offset);
- if ((reg > CLNK_CTRL && reg < CCEN) ||
- (reg > PCHD_ID && reg < CAPS_2)) {
- u16 upper;
- u32 offset2 = reg_map[reg] + 2 + (stride * lch);
- upper = __raw_readw(dma_base + offset2);
- val |= (upper << 16);
- }
+ void __iomem *addr = dma_base;
+ uint32_t val;
+
+ addr += reg_map[reg].offset;
+ addr += reg_map[reg].stride * lch;
+
+ val = __raw_readw(addr);
+ if (reg_map[reg].type == OMAP_DMA_REG_2X16BIT)
+ val |= __raw_readw(addr + 2) << 16;
+
return val;
}
static void omap1_clear_lch_regs(int lch)
{
- int i = dma_common_ch_start;
+ int i;
- for (; i <= dma_common_ch_end; i += 1)
+ for (i = CPC; i <= COLOR; i += 1)
dma_write(0, i, lch);
}
@@ -255,8 +243,9 @@ static void omap1_show_dma_caps(void)
return;
}
-static u32 configure_dma_errata(void)
+static unsigned configure_dma_errata(void)
{
+ unsigned errata = 0;
/*
* Erratum 3.2/3.3: sometimes 0 is returned if CSAC/CDAC is
@@ -272,11 +261,23 @@ static const struct platform_device_info omap_dma_dev_info = {
.name = "omap-dma-engine",
.id = -1,
.dma_mask = DMA_BIT_MASK(32),
+ .res = res,
+ .num_res = 1,
+};
+
+static struct omap_system_dma_plat_info dma_plat_info __initdata = {
+ .reg_map = reg_map,
+ .channel_stride = 0x40,
+ .show_dma_caps = omap1_show_dma_caps,
+ .clear_lch_regs = omap1_clear_lch_regs,
+ .clear_dma = omap1_clear_dma,
+ .dma_write = dma_write,
+ .dma_read = dma_read,
};
static int __init omap1_system_dma_init(void)
{
- struct omap_system_dma_plat_info *p;
+ struct omap_system_dma_plat_info p;
struct omap_dma_dev_attr *d;
struct platform_device *pdev, *dma_pdev;
int ret;
@@ -302,20 +303,12 @@ static int __init omap1_system_dma_init(void)
goto exit_iounmap;
}
- p = kzalloc(sizeof(struct omap_system_dma_plat_info), GFP_KERNEL);
- if (!p) {
- dev_err(&pdev->dev, "%s: Unable to allocate 'p' for %s\n",
- __func__, pdev->name);
- ret = -ENOMEM;
- goto exit_iounmap;
- }
-
d = kzalloc(sizeof(struct omap_dma_dev_attr), GFP_KERNEL);
if (!d) {
dev_err(&pdev->dev, "%s: Unable to allocate 'd' for %s\n",
__func__, pdev->name);
ret = -ENOMEM;
- goto exit_release_p;
+ goto exit_iounmap;
}
d->lch_count = OMAP1_LOGICAL_DMA_CH_COUNT;
@@ -336,17 +329,6 @@ static int __init omap1_system_dma_init(void)
d->dev_caps |= CLEAR_CSR_ON_READ;
d->dev_caps |= IS_WORD_16;
-
- d->chan = kzalloc(sizeof(struct omap_dma_lch) *
- (d->lch_count), GFP_KERNEL);
- if (!d->chan) {
- dev_err(&pdev->dev,
- "%s: Memory allocation failed for d->chan!\n",
- __func__);
- ret = -ENOMEM;
- goto exit_release_d;
- }
-
if (cpu_is_omap15xx())
d->chan_count = 9;
else if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
@@ -356,35 +338,24 @@ static int __init omap1_system_dma_init(void)
d->chan_count = 9;
}
- p->dma_attr = d;
-
- p->show_dma_caps = omap1_show_dma_caps;
- p->clear_lch_regs = omap1_clear_lch_regs;
- p->clear_dma = omap1_clear_dma;
- p->dma_write = dma_write;
- p->dma_read = dma_read;
- p->disable_irq_lch = NULL;
-
- p->errata = configure_dma_errata();
+ p = dma_plat_info;
+ p.dma_attr = d;
+ p.errata = configure_dma_errata();
- ret = platform_device_add_data(pdev, p, sizeof(*p));
+ ret = platform_device_add_data(pdev, &p, sizeof(p));
if (ret) {
dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n",
__func__, pdev->name, pdev->id);
- goto exit_release_chan;
+ goto exit_release_d;
}
ret = platform_device_add(pdev);
if (ret) {
dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n",
__func__, pdev->name, pdev->id);
- goto exit_release_chan;
+ goto exit_release_d;
}
- dma_stride = OMAP1_DMA_STRIDE;
- dma_common_ch_start = CPC;
- dma_common_ch_end = COLOR;
-
dma_pdev = platform_device_register_full(&omap_dma_dev_info);
if (IS_ERR(dma_pdev)) {
ret = PTR_ERR(dma_pdev);
@@ -395,12 +366,8 @@ static int __init omap1_system_dma_init(void)
exit_release_pdev:
platform_device_del(pdev);
-exit_release_chan:
- kfree(d->chan);
exit_release_d:
kfree(d);
-exit_release_p:
- kfree(p);
exit_iounmap:
iounmap(dma_base);
exit_device_put:
diff --git a/arch/arm/mach-omap1/include/mach/timex.h b/arch/arm/mach-omap1/include/mach/timex.h
deleted file mode 100644
index 4793790d53cc..000000000000
--- a/arch/arm/mach-omap1/include/mach/timex.h
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * arch/arm/mach-omap1/include/mach/timex.h
- */
-
-#include <plat/timex.h>
diff --git a/arch/arm/mach-omap1/pm.c b/arch/arm/mach-omap1/pm.c
index 40a1ae319610..dbee729e3b6d 100644
--- a/arch/arm/mach-omap1/pm.c
+++ b/arch/arm/mach-omap1/pm.c
@@ -71,7 +71,11 @@ static unsigned int mpui7xx_sleep_save[MPUI7XX_SLEEP_SAVE_SIZE];
static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE];
static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE];
-#ifdef CONFIG_OMAP_32K_TIMER
+#ifndef CONFIG_OMAP_32K_TIMER
+
+static unsigned short enable_dyn_sleep = 0;
+
+#else
static unsigned short enable_dyn_sleep = 1;
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 6fb91fb604a0..cb31d4390d52 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -6,7 +6,6 @@ config ARCH_OMAP2
depends on ARCH_MULTI_V6
select ARCH_OMAP2PLUS
select CPU_V6
- select MULTI_IRQ_HANDLER
select SOC_HAS_OMAP2_SDRC
config ARCH_OMAP3
@@ -15,8 +14,6 @@ config ARCH_OMAP3
select ARCH_OMAP2PLUS
select ARCH_HAS_OPP
select ARM_CPU_SUSPEND if PM
- select CPU_V7
- select MULTI_IRQ_HANDLER
select OMAP_INTERCONNECT
select PM_OPP if PM
select PM_RUNTIME if CPU_IDLE
@@ -32,10 +29,8 @@ config ARCH_OMAP4
select ARM_ERRATA_720789
select ARM_GIC
select CACHE_L2X0
- select CPU_V7
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
- select HAVE_SMP
select OMAP_INTERCONNECT
select PL310_ERRATA_588369
select PL310_ERRATA_727915
@@ -51,10 +46,8 @@ config SOC_OMAP5
select ARCH_HAS_OPP
select ARM_CPU_SUSPEND if PM
select ARM_GIC
- select CPU_V7
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
- select HAVE_SMP
select HAVE_ARM_ARCH_TIMER
select ARM_ERRATA_798181 if SMP
@@ -64,16 +57,12 @@ config SOC_AM33XX
select ARCH_OMAP2PLUS
select ARCH_HAS_OPP
select ARM_CPU_SUSPEND if PM
- select CPU_V7
- select MULTI_IRQ_HANDLER
config SOC_AM43XX
bool "TI AM43x"
depends on ARCH_MULTI_V7
- select CPU_V7
select ARCH_OMAP2PLUS
select ARCH_HAS_OPP
- select MULTI_IRQ_HANDLER
select ARM_GIC
select MACH_OMAP_GENERIC
@@ -84,9 +73,8 @@ config SOC_DRA7XX
select ARCH_HAS_OPP
select ARM_CPU_SUSPEND if PM
select ARM_GIC
- select CPU_V7
- select HAVE_SMP
select HAVE_ARM_ARCH_TIMER
+ select IRQ_CROSSBAR
config ARCH_OMAP2PLUS
bool
@@ -96,17 +84,12 @@ config ARCH_OMAP2PLUS
select ARCH_OMAP
select ARCH_REQUIRE_GPIOLIB
select CLKSRC_MMIO
- select COMMON_CLK
- select GENERIC_CLOCKEVENTS
select GENERIC_IRQ_CHIP
select MACH_OMAP_GENERIC
select OMAP_DM_TIMER
select PINCTRL
- select PROC_DEVICETREE if PROC_FS
select SOC_BUS
- select SPARSE_IRQ
select TI_PRIV_EDMA
- select USE_OF
help
Systems based on OMAP2, OMAP3, OMAP4 or OMAP5
@@ -167,12 +150,6 @@ config SOC_TI81XX
depends on ARCH_OMAP3
default y
-config OMAP_PACKAGE_ZAF
- bool
-
-config OMAP_PACKAGE_ZAC
- bool
-
config OMAP_PACKAGE_CBC
bool
@@ -282,7 +259,6 @@ config MACH_NOKIA_N8X0
default y
select MACH_NOKIA_N810
select MACH_NOKIA_N810_WIMAX
- select OMAP_PACKAGE_ZAC
config MACH_NOKIA_RX51
bool "Nokia N900 (RX-51) phone"
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index e6eec6f72fd3..8421f38cf445 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -60,6 +60,7 @@ AFLAGS_sram34xx.o :=-Wa,-march=armv7-a
obj-$(CONFIG_SOC_OMAP2420) += omap2-restart.o
obj-$(CONFIG_SOC_OMAP2430) += omap2-restart.o
obj-$(CONFIG_SOC_AM33XX) += am33xx-restart.o
+obj-$(CONFIG_SOC_AM43XX) += omap4-restart.o
obj-$(CONFIG_ARCH_OMAP3) += omap3-restart.o
obj-$(CONFIG_ARCH_OMAP4) += omap4-restart.o
obj-$(CONFIG_SOC_OMAP5) += omap4-restart.o
diff --git a/arch/arm/mach-omap2/am35xx-emac.c b/arch/arm/mach-omap2/am35xx-emac.c
index 25b79a297365..6a6935caac1e 100644
--- a/arch/arm/mach-omap2/am35xx-emac.c
+++ b/arch/arm/mach-omap2/am35xx-emac.c
@@ -17,7 +17,6 @@
#include <linux/err.h>
#include <linux/davinci_emac.h>
-#include <asm/system.h>
#include "omap_device.h"
#include "am35xx.h"
#include "control.h"
diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c
index 8dd0ec858cf1..018353d88b96 100644
--- a/arch/arm/mach-omap2/board-cm-t35.c
+++ b/arch/arm/mach-omap2/board-cm-t35.c
@@ -16,6 +16,8 @@
*
*/
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
@@ -542,8 +544,22 @@ static struct isp_platform_data cm_t35_isp_pdata = {
.subdevs = cm_t35_isp_subdevs,
};
+static struct regulator_consumer_supply cm_t35_camera_supplies[] = {
+ REGULATOR_SUPPLY("vaa", "3-005d"),
+ REGULATOR_SUPPLY("vdd", "3-005d"),
+};
+
static void __init cm_t35_init_camera(void)
{
+ struct clk *clk;
+
+ clk = clk_register_fixed_rate(NULL, "mt9t001-clkin", NULL, CLK_IS_ROOT,
+ 48000000);
+ clk_register_clkdev(clk, NULL, "3-005d");
+
+ regulator_register_fixed(2, cm_t35_camera_supplies,
+ ARRAY_SIZE(cm_t35_camera_supplies));
+
if (omap3_init_camera(&cm_t35_isp_pdata) < 0)
pr_warn("CM-T3x: Failed registering camera device!\n");
}
diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index 8e3daa11602b..b8920b6bc104 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -35,7 +35,11 @@ static struct of_device_id omap_dt_match_table[] __initdata = {
static void __init omap_generic_init(void)
{
+ omapdss_early_init_of();
+
pdata_quirks_init(omap_dt_match_table);
+
+ omapdss_init_of();
}
#ifdef CONFIG_SOC_OMAP2420
@@ -229,8 +233,9 @@ DT_MACHINE_START(AM43_DT, "Generic AM43 (Flattened Device Tree)")
.init_late = am43xx_init_late,
.init_irq = omap_gic_of_init,
.init_machine = omap_generic_init,
- .init_time = omap3_sync32k_timer_init,
+ .init_time = omap3_gptimer_timer_init,
.dt_compat = am43_boards_compat,
+ .restart = omap44xx_restart,
MACHINE_END
#endif
diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c
index de1bc6bbe585..cf18340eb3bb 100644
--- a/arch/arm/mach-omap2/board-omap3pandora.c
+++ b/arch/arm/mach-omap2/board-omap3pandora.c
@@ -536,11 +536,13 @@ static struct spi_board_info omap3pandora_spi_board_info[] __initdata = {
static void __init pandora_wl1251_init(void)
{
- struct wl12xx_platform_data pandora_wl1251_pdata;
+ struct wl1251_platform_data pandora_wl1251_pdata;
int ret;
memset(&pandora_wl1251_pdata, 0, sizeof(pandora_wl1251_pdata));
+ pandora_wl1251_pdata.power_gpio = -1;
+
ret = gpio_request_one(PANDORA_WIFI_IRQ_GPIO, GPIOF_IN, "wl1251 irq");
if (ret < 0)
goto fail;
@@ -550,7 +552,7 @@ static void __init pandora_wl1251_init(void)
goto fail_irq;
pandora_wl1251_pdata.use_eeprom = true;
- ret = wl12xx_set_platform_data(&pandora_wl1251_pdata);
+ ret = wl1251_set_platform_data(&pandora_wl1251_pdata);
if (ret < 0)
goto fail_irq;
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index 8760bbe3baab..ddfc8df83c6a 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -84,7 +84,7 @@ enum {
RX51_SPI_MIPID, /* LCD panel */
};
-static struct wl12xx_platform_data wl1251_pdata;
+static struct wl1251_platform_data wl1251_pdata;
static struct tsc2005_platform_data tsc2005_pdata;
#if defined(CONFIG_SENSORS_LIS3_I2C) || defined(CONFIG_SENSORS_LIS3_I2C_MODULE)
@@ -1173,13 +1173,7 @@ static inline void board_smc91x_init(void)
#endif
-static void rx51_wl1251_set_power(bool enable)
-{
- gpio_set_value(RX51_WL1251_POWER_GPIO, enable);
-}
-
static struct gpio rx51_wl1251_gpios[] __initdata = {
- { RX51_WL1251_POWER_GPIO, GPIOF_OUT_INIT_LOW, "wl1251 power" },
{ RX51_WL1251_IRQ_GPIO, GPIOF_IN, "wl1251 irq" },
};
@@ -1196,17 +1190,16 @@ static void __init rx51_init_wl1251(void)
if (irq < 0)
goto err_irq;
- wl1251_pdata.set_power = rx51_wl1251_set_power;
+ wl1251_pdata.power_gpio = RX51_WL1251_POWER_GPIO;
rx51_peripherals_spi_board_info[RX51_SPI_WL1251].irq = irq;
return;
err_irq:
gpio_free(RX51_WL1251_IRQ_GPIO);
- gpio_free(RX51_WL1251_POWER_GPIO);
error:
printk(KERN_ERR "wl1251 board initialisation failed\n");
- wl1251_pdata.set_power = NULL;
+ wl1251_pdata.power_gpio = -1;
/*
* Now rx51_peripherals_spi_board_info[1].irq is zero and
diff --git a/arch/arm/mach-omap2/cclock3xxx_data.c b/arch/arm/mach-omap2/cclock3xxx_data.c
index 11ed9152e665..8f5121b89688 100644
--- a/arch/arm/mach-omap2/cclock3xxx_data.c
+++ b/arch/arm/mach-omap2/cclock3xxx_data.c
@@ -3497,10 +3497,6 @@ static struct omap_clk omap3xxx_clks[] = {
CLK(NULL, "dss_tv_fck", &dss_tv_fck),
CLK(NULL, "dss_96m_fck", &dss_96m_fck),
CLK(NULL, "dss2_alwon_fck", &dss2_alwon_fck),
- CLK(NULL, "utmi_p1_gfclk", &dummy_ck),
- CLK(NULL, "utmi_p2_gfclk", &dummy_ck),
- CLK(NULL, "xclk60mhsp1_ck", &dummy_ck),
- CLK(NULL, "xclk60mhsp2_ck", &dummy_ck),
CLK(NULL, "init_60m_fclk", &dummy_ck),
CLK(NULL, "gpt1_fck", &gpt1_fck),
CLK(NULL, "aes2_ick", &aes2_ick),
diff --git a/arch/arm/mach-omap2/clkt_dpll.c b/arch/arm/mach-omap2/clkt_dpll.c
index 47f9562ca7aa..2649ce445845 100644
--- a/arch/arm/mach-omap2/clkt_dpll.c
+++ b/arch/arm/mach-omap2/clkt_dpll.c
@@ -306,7 +306,7 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
ref_rate = __clk_get_rate(dd->clk_ref);
clk_name = __clk_get_name(hw->clk);
- pr_debug("clock: %s: starting DPLL round_rate, target rate %ld\n",
+ pr_debug("clock: %s: starting DPLL round_rate, target rate %lu\n",
clk_name, target_rate);
scaled_rt_rp = target_rate / (ref_rate / DPLL_SCALE_FACTOR);
@@ -342,7 +342,7 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
if (r == DPLL_MULT_UNDERFLOW)
continue;
- pr_debug("clock: %s: m = %d: n = %d: new_rate = %ld\n",
+ pr_debug("clock: %s: m = %d: n = %d: new_rate = %lu\n",
clk_name, m, n, new_rate);
if (target_rate == new_rate) {
@@ -354,7 +354,7 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
}
if (target_rate != new_rate) {
- pr_debug("clock: %s: cannot round to rate %ld\n",
+ pr_debug("clock: %s: cannot round to rate %lu\n",
clk_name, target_rate);
return ~0;
}
diff --git a/arch/arm/mach-omap2/clockdomains3xxx_data.c b/arch/arm/mach-omap2/clockdomains3xxx_data.c
index e6b91e552d3d..f03dc97921ad 100644
--- a/arch/arm/mach-omap2/clockdomains3xxx_data.c
+++ b/arch/arm/mach-omap2/clockdomains3xxx_data.c
@@ -247,7 +247,7 @@ static struct clockdomain neon_clkdm = {
static struct clockdomain iva2_clkdm = {
.name = "iva2_clkdm",
.pwrdm = { .name = "iva2_pwrdm" },
- .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .flags = CLKDM_CAN_SWSUP,
.dep_bit = OMAP3430_PM_WKDEP_MPU_EN_IVA2_SHIFT,
.wkdep_srcs = iva2_wkdeps,
.clktrctrl_mask = OMAP3430_CLKTRCTRL_IVA2_MASK,
diff --git a/arch/arm/mach-omap2/cminst44xx.c b/arch/arm/mach-omap2/cminst44xx.c
index 731ca134348c..f5c4731b6f06 100644
--- a/arch/arm/mach-omap2/cminst44xx.c
+++ b/arch/arm/mach-omap2/cminst44xx.c
@@ -254,6 +254,11 @@ void omap4_cminst_clkdm_force_wakeup(u8 part, u16 inst, u16 cdoffs)
*
*/
+void omap4_cminst_clkdm_force_sleep(u8 part, u16 inst, u16 cdoffs)
+{
+ _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, part, inst, cdoffs);
+}
+
/**
* omap4_cminst_wait_module_ready - wait for a module to be in 'func' state
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
@@ -404,8 +409,17 @@ static int omap4_clkdm_clear_all_wkup_sleep_deps(struct clockdomain *clkdm)
static int omap4_clkdm_sleep(struct clockdomain *clkdm)
{
- omap4_cminst_clkdm_enable_hwsup(clkdm->prcm_partition,
- clkdm->cm_inst, clkdm->clkdm_offs);
+ if (clkdm->flags & CLKDM_CAN_HWSUP)
+ omap4_cminst_clkdm_enable_hwsup(clkdm->prcm_partition,
+ clkdm->cm_inst,
+ clkdm->clkdm_offs);
+ else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
+ omap4_cminst_clkdm_force_sleep(clkdm->prcm_partition,
+ clkdm->cm_inst,
+ clkdm->clkdm_offs);
+ else
+ return -EINVAL;
+
return 0;
}
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index a6aae300542c..d88aff7baff8 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -315,5 +315,8 @@ extern int omap_dss_reset(struct omap_hwmod *);
/* SoC specific clock initializer */
int omap_clk_init(void);
+int __init omapdss_init_of(void);
+void __init omapdss_early_init_of(void);
+
#endif /* __ASSEMBLER__ */
#endif /* __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H */
diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index 0dd6398bade4..e58609b312c7 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -229,6 +229,9 @@ static struct omap_iommu_arch_data omap3_isp_iommu = {
int omap3_init_camera(struct isp_platform_data *pdata)
{
+ if (of_have_populated_dt())
+ omap3_isp_iommu.name = "480bd400.mmu";
+
omap3isp_device.dev.platform_data = pdata;
omap3isp_device.dev.archdata.iommu = &omap3_isp_iommu;
diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c
index 4cf165502b35..16d33d831287 100644
--- a/arch/arm/mach-omap2/display.c
+++ b/arch/arm/mach-omap2/display.c
@@ -23,6 +23,9 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
#include <video/omapdss.h>
#include "omap_hwmod.h"
@@ -301,7 +304,6 @@ int __init omap_display_init(struct omap_dss_board_info *board_data)
board_data->version = ver;
board_data->dsi_enable_pads = omap_dsi_enable_pads;
board_data->dsi_disable_pads = omap_dsi_disable_pads;
- board_data->get_context_loss_count = omap_pm_get_dev_context_loss_count;
board_data->set_min_bus_tput = omap_dss_set_min_bus_tput;
omap_display_device.dev.platform_data = board_data;
@@ -552,3 +554,166 @@ int omap_dss_reset(struct omap_hwmod *oh)
return r;
}
+
+/* list of 'compatible' nodes to convert to omapdss specific */
+static const char * const dss_compat_conv_list[] __initconst = {
+ "composite-connector",
+ "dvi-connector",
+ "hdmi-connector",
+ "panel-dpi",
+ "panel-dsi-cm",
+ "sony,acx565akm",
+ "svideo-connector",
+ "ti,tfp410",
+ "ti,tpd12s015",
+};
+
+/* prepend compatible string with "omapdss," */
+static __init void omapdss_omapify_node(struct device_node *node,
+ const char *compat)
+{
+ char *new_compat;
+ struct property *prop;
+
+ new_compat = kasprintf(GFP_KERNEL, "omapdss,%s", compat);
+
+ prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+
+ if (!prop) {
+ pr_err("omapdss_omapify_node: kzalloc failed\n");
+ return;
+ }
+
+ prop->name = "compatible";
+ prop->value = new_compat;
+ prop->length = strlen(new_compat) + 1;
+
+ of_update_property(node, prop);
+}
+
+/*
+ * As omapdss panel drivers are omapdss specific, but we want to define the
+ * DT-data in generic manner, we convert the compatible strings of the panel
+ * nodes from "panel-foo" to "omapdss,panel-foo". This way we can have both
+ * correct DT data and omapdss specific drivers.
+ *
+ * When we get generic panel drivers to the kernel, this will be removed.
+ */
+void __init omapdss_early_init_of(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dss_compat_conv_list); ++i) {
+ const char *compat = dss_compat_conv_list[i];
+ struct device_node *node = NULL;
+
+ while ((node = of_find_compatible_node(node, NULL, compat))) {
+ if (!of_device_is_available(node))
+ continue;
+
+ omapdss_omapify_node(node, compat);
+ }
+ }
+}
+
+struct device_node * __init omapdss_find_dss_of_node(void)
+{
+ struct device_node *node;
+
+ node = of_find_compatible_node(NULL, NULL, "ti,omap2-dss");
+ if (node)
+ return node;
+
+ node = of_find_compatible_node(NULL, NULL, "ti,omap3-dss");
+ if (node)
+ return node;
+
+ node = of_find_compatible_node(NULL, NULL, "ti,omap4-dss");
+ if (node)
+ return node;
+
+ return NULL;
+}
+
+int __init omapdss_init_of(void)
+{
+ int r;
+ enum omapdss_version ver;
+ struct device_node *node;
+ struct platform_device *pdev;
+
+ static struct omap_dss_board_info board_data = {
+ .dsi_enable_pads = omap_dsi_enable_pads,
+ .dsi_disable_pads = omap_dsi_disable_pads,
+ .set_min_bus_tput = omap_dss_set_min_bus_tput,
+ };
+
+ /* only create dss helper devices if dss is enabled in the .dts */
+
+ node = omapdss_find_dss_of_node();
+ if (!node)
+ return 0;
+
+ if (!of_device_is_available(node))
+ return 0;
+
+ ver = omap_display_get_version();
+
+ if (ver == OMAPDSS_VER_UNKNOWN) {
+ pr_err("DSS not supported on this SoC\n");
+ return -ENODEV;
+ }
+
+ pdev = of_find_device_by_node(node);
+
+ if (!pdev) {
+ pr_err("Unable to find DSS platform device\n");
+ return -ENODEV;
+ }
+
+ r = of_platform_populate(node, NULL, NULL, &pdev->dev);
+ if (r) {
+ pr_err("Unable to populate DSS submodule devices\n");
+ return r;
+ }
+
+ board_data.version = ver;
+
+ omap_display_device.dev.platform_data = &board_data;
+
+ r = platform_device_register(&omap_display_device);
+ if (r < 0) {
+ pr_err("Unable to register omapdss device\n");
+ return r;
+ }
+
+ /* create DRM device */
+ r = omap_init_drm();
+ if (r < 0) {
+ pr_err("Unable to register omapdrm device\n");
+ return r;
+ }
+
+ /* create vrfb device */
+ r = omap_init_vrfb();
+ if (r < 0) {
+ pr_err("Unable to register omapvrfb device\n");
+ return r;
+ }
+
+ /* create FB device */
+ r = omap_init_fb();
+ if (r < 0) {
+ pr_err("Unable to register omapfb device\n");
+ return r;
+ }
+
+ /* create V4L2 display device */
+ r = omap_init_vout();
+ if (r < 0) {
+ pr_err("Unable to register omap_vout device\n");
+ return r;
+ }
+
+ return 0;
+}
diff --git a/arch/arm/mach-omap2/display.h b/arch/arm/mach-omap2/display.h
index f3d2ce4bc262..7375854b16c7 100644
--- a/arch/arm/mach-omap2/display.h
+++ b/arch/arm/mach-omap2/display.h
@@ -30,4 +30,7 @@ int omap_init_drm(void);
int omap_init_vrfb(void);
int omap_init_fb(void);
int omap_init_vout(void);
+
+struct device_node * __init omapdss_find_dss_of_node(void);
+
#endif
diff --git a/arch/arm/mach-omap2/dma.c b/arch/arm/mach-omap2/dma.c
index 49fd0d501c9b..5689c88d986d 100644
--- a/arch/arm/mach-omap2/dma.c
+++ b/arch/arm/mach-omap2/dma.c
@@ -35,97 +35,80 @@
#include "omap_hwmod.h"
#include "omap_device.h"
-#define OMAP2_DMA_STRIDE 0x60
-
-static u32 errata;
-static u8 dma_stride;
-
-static struct omap_dma_dev_attr *d;
-
-static enum omap_reg_offsets dma_common_ch_start, dma_common_ch_end;
-
-static u16 reg_map[] = {
- [REVISION] = 0x00,
- [GCR] = 0x78,
- [IRQSTATUS_L0] = 0x08,
- [IRQSTATUS_L1] = 0x0c,
- [IRQSTATUS_L2] = 0x10,
- [IRQSTATUS_L3] = 0x14,
- [IRQENABLE_L0] = 0x18,
- [IRQENABLE_L1] = 0x1c,
- [IRQENABLE_L2] = 0x20,
- [IRQENABLE_L3] = 0x24,
- [SYSSTATUS] = 0x28,
- [OCP_SYSCONFIG] = 0x2c,
- [CAPS_0] = 0x64,
- [CAPS_2] = 0x6c,
- [CAPS_3] = 0x70,
- [CAPS_4] = 0x74,
+static enum omap_reg_offsets dma_common_ch_end;
+
+static const struct omap_dma_reg reg_map[] = {
+ [REVISION] = { 0x0000, 0x00, OMAP_DMA_REG_32BIT },
+ [GCR] = { 0x0078, 0x00, OMAP_DMA_REG_32BIT },
+ [IRQSTATUS_L0] = { 0x0008, 0x00, OMAP_DMA_REG_32BIT },
+ [IRQSTATUS_L1] = { 0x000c, 0x00, OMAP_DMA_REG_32BIT },
+ [IRQSTATUS_L2] = { 0x0010, 0x00, OMAP_DMA_REG_32BIT },
+ [IRQSTATUS_L3] = { 0x0014, 0x00, OMAP_DMA_REG_32BIT },
+ [IRQENABLE_L0] = { 0x0018, 0x00, OMAP_DMA_REG_32BIT },
+ [IRQENABLE_L1] = { 0x001c, 0x00, OMAP_DMA_REG_32BIT },
+ [IRQENABLE_L2] = { 0x0020, 0x00, OMAP_DMA_REG_32BIT },
+ [IRQENABLE_L3] = { 0x0024, 0x00, OMAP_DMA_REG_32BIT },
+ [SYSSTATUS] = { 0x0028, 0x00, OMAP_DMA_REG_32BIT },
+ [OCP_SYSCONFIG] = { 0x002c, 0x00, OMAP_DMA_REG_32BIT },
+ [CAPS_0] = { 0x0064, 0x00, OMAP_DMA_REG_32BIT },
+ [CAPS_2] = { 0x006c, 0x00, OMAP_DMA_REG_32BIT },
+ [CAPS_3] = { 0x0070, 0x00, OMAP_DMA_REG_32BIT },
+ [CAPS_4] = { 0x0074, 0x00, OMAP_DMA_REG_32BIT },
/* Common register offsets */
- [CCR] = 0x80,
- [CLNK_CTRL] = 0x84,
- [CICR] = 0x88,
- [CSR] = 0x8c,
- [CSDP] = 0x90,
- [CEN] = 0x94,
- [CFN] = 0x98,
- [CSEI] = 0xa4,
- [CSFI] = 0xa8,
- [CDEI] = 0xac,
- [CDFI] = 0xb0,
- [CSAC] = 0xb4,
- [CDAC] = 0xb8,
+ [CCR] = { 0x0080, 0x60, OMAP_DMA_REG_32BIT },
+ [CLNK_CTRL] = { 0x0084, 0x60, OMAP_DMA_REG_32BIT },
+ [CICR] = { 0x0088, 0x60, OMAP_DMA_REG_32BIT },
+ [CSR] = { 0x008c, 0x60, OMAP_DMA_REG_32BIT },
+ [CSDP] = { 0x0090, 0x60, OMAP_DMA_REG_32BIT },
+ [CEN] = { 0x0094, 0x60, OMAP_DMA_REG_32BIT },
+ [CFN] = { 0x0098, 0x60, OMAP_DMA_REG_32BIT },
+ [CSEI] = { 0x00a4, 0x60, OMAP_DMA_REG_32BIT },
+ [CSFI] = { 0x00a8, 0x60, OMAP_DMA_REG_32BIT },
+ [CDEI] = { 0x00ac, 0x60, OMAP_DMA_REG_32BIT },
+ [CDFI] = { 0x00b0, 0x60, OMAP_DMA_REG_32BIT },
+ [CSAC] = { 0x00b4, 0x60, OMAP_DMA_REG_32BIT },
+ [CDAC] = { 0x00b8, 0x60, OMAP_DMA_REG_32BIT },
/* Channel specific register offsets */
- [CSSA] = 0x9c,
- [CDSA] = 0xa0,
- [CCEN] = 0xbc,
- [CCFN] = 0xc0,
- [COLOR] = 0xc4,
+ [CSSA] = { 0x009c, 0x60, OMAP_DMA_REG_32BIT },
+ [CDSA] = { 0x00a0, 0x60, OMAP_DMA_REG_32BIT },
+ [CCEN] = { 0x00bc, 0x60, OMAP_DMA_REG_32BIT },
+ [CCFN] = { 0x00c0, 0x60, OMAP_DMA_REG_32BIT },
+ [COLOR] = { 0x00c4, 0x60, OMAP_DMA_REG_32BIT },
/* OMAP4 specific registers */
- [CDP] = 0xd0,
- [CNDP] = 0xd4,
- [CCDN] = 0xd8,
+ [CDP] = { 0x00d0, 0x60, OMAP_DMA_REG_32BIT },
+ [CNDP] = { 0x00d4, 0x60, OMAP_DMA_REG_32BIT },
+ [CCDN] = { 0x00d8, 0x60, OMAP_DMA_REG_32BIT },
};
static void __iomem *dma_base;
static inline void dma_write(u32 val, int reg, int lch)
{
- u8 stride;
- u32 offset;
+ void __iomem *addr = dma_base;
- stride = (reg >= dma_common_ch_start) ? dma_stride : 0;
- offset = reg_map[reg] + (stride * lch);
- __raw_writel(val, dma_base + offset);
+ addr += reg_map[reg].offset;
+ addr += reg_map[reg].stride * lch;
+
+ __raw_writel(val, addr);
}
static inline u32 dma_read(int reg, int lch)
{
- u8 stride;
- u32 offset, val;
-
- stride = (reg >= dma_common_ch_start) ? dma_stride : 0;
- offset = reg_map[reg] + (stride * lch);
- val = __raw_readl(dma_base + offset);
- return val;
-}
+ void __iomem *addr = dma_base;
-static inline void omap2_disable_irq_lch(int lch)
-{
- u32 val;
+ addr += reg_map[reg].offset;
+ addr += reg_map[reg].stride * lch;
- val = dma_read(IRQENABLE_L0, lch);
- val &= ~(1 << lch);
- dma_write(val, IRQENABLE_L0, lch);
+ return __raw_readl(addr);
}
static void omap2_clear_dma(int lch)
{
- int i = dma_common_ch_start;
+ int i;
- for (; i <= dma_common_ch_end; i += 1)
+ for (i = CSDP; i <= dma_common_ch_end; i += 1)
dma_write(0, i, lch);
}
@@ -137,8 +120,9 @@ static void omap2_show_dma_caps(void)
return;
}
-static u32 configure_dma_errata(void)
+static unsigned configure_dma_errata(void)
{
+ unsigned errata = 0;
/*
* Errata applicable for OMAP2430ES1.0 and all omap2420
@@ -220,48 +204,50 @@ static u32 configure_dma_errata(void)
return errata;
}
+static struct omap_system_dma_plat_info dma_plat_info __initdata = {
+ .reg_map = reg_map,
+ .channel_stride = 0x60,
+ .show_dma_caps = omap2_show_dma_caps,
+ .clear_dma = omap2_clear_dma,
+ .dma_write = dma_write,
+ .dma_read = dma_read,
+};
+
+static struct platform_device_info omap_dma_dev_info = {
+ .name = "omap-dma-engine",
+ .id = -1,
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
/* One time initializations */
static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *unused)
{
struct platform_device *pdev;
- struct omap_system_dma_plat_info *p;
+ struct omap_system_dma_plat_info p;
+ struct omap_dma_dev_attr *d;
struct resource *mem;
char *name = "omap_dma_system";
- dma_stride = OMAP2_DMA_STRIDE;
- dma_common_ch_start = CSDP;
-
- p = kzalloc(sizeof(struct omap_system_dma_plat_info), GFP_KERNEL);
- if (!p) {
- pr_err("%s: Unable to allocate pdata for %s:%s\n",
- __func__, name, oh->name);
- return -ENOMEM;
- }
-
- p->dma_attr = (struct omap_dma_dev_attr *)oh->dev_attr;
- p->disable_irq_lch = omap2_disable_irq_lch;
- p->show_dma_caps = omap2_show_dma_caps;
- p->clear_dma = omap2_clear_dma;
- p->dma_write = dma_write;
- p->dma_read = dma_read;
-
- p->clear_lch_regs = NULL;
-
- p->errata = configure_dma_errata();
+ p = dma_plat_info;
+ p.dma_attr = (struct omap_dma_dev_attr *)oh->dev_attr;
+ p.errata = configure_dma_errata();
- pdev = omap_device_build(name, 0, oh, p, sizeof(*p));
- kfree(p);
+ pdev = omap_device_build(name, 0, oh, &p, sizeof(p));
if (IS_ERR(pdev)) {
pr_err("%s: Can't build omap_device for %s:%s.\n",
__func__, name, oh->name);
return PTR_ERR(pdev);
}
+ omap_dma_dev_info.res = pdev->resource;
+ omap_dma_dev_info.num_res = pdev->num_resources;
+
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(&pdev->dev, "%s: no mem resource\n", __func__);
return -EINVAL;
}
+
dma_base = ioremap(mem->start, resource_size(mem));
if (!dma_base) {
dev_err(&pdev->dev, "%s: ioremap fail\n", __func__);
@@ -269,13 +255,6 @@ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *unused)
}
d = oh->dev_attr;
- d->chan = kzalloc(sizeof(struct omap_dma_lch) *
- (d->lch_count), GFP_KERNEL);
-
- if (!d->chan) {
- dev_err(&pdev->dev, "%s: kzalloc fail\n", __func__);
- return -ENOMEM;
- }
if (cpu_is_omap34xx() && (omap_type() != OMAP2_DEVICE_TYPE_GP))
d->dev_caps |= HS_CHANNELS_RESERVED;
@@ -289,12 +268,6 @@ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *unused)
return 0;
}
-static const struct platform_device_info omap_dma_dev_info = {
- .name = "omap-dma-engine",
- .id = -1,
- .dma_mask = DMA_BIT_MASK(32),
-};
-
static int __init omap2_system_dma_init(void)
{
struct platform_device *pdev;
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c
index 3c418ea54bbe..fcd8036af910 100644
--- a/arch/arm/mach-omap2/dpll3xxx.c
+++ b/arch/arm/mach-omap2/dpll3xxx.c
@@ -525,7 +525,7 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
* stuff is inherited for free
*/
- if (!ret)
+ if (!ret && clk_get_parent(hw->clk) != new_parent)
__clk_reparent(hw->clk, new_parent);
return 0;
diff --git a/arch/arm/mach-omap2/dss-common.c b/arch/arm/mach-omap2/dss-common.c
index dadccc91488c..ea2be0f5953b 100644
--- a/arch/arm/mach-omap2/dss-common.c
+++ b/arch/arm/mach-omap2/dss-common.c
@@ -33,227 +33,5 @@
#include "soc.h"
#include "dss-common.h"
#include "mux.h"
+#include "display.h"
-#define HDMI_GPIO_CT_CP_HPD 60 /* HPD mode enable/disable */
-#define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */
-#define HDMI_GPIO_HPD 63 /* Hotplug detect */
-
-#define PANDA_DVI_TFP410_POWER_DOWN_GPIO 0
-
-/* DVI Connector */
-static struct connector_dvi_platform_data omap4_panda_dvi_connector_pdata = {
- .name = "dvi",
- .source = "tfp410.0",
- .i2c_bus_num = 2,
-};
-
-static struct platform_device omap4_panda_dvi_connector_device = {
- .name = "connector-dvi",
- .id = 0,
- .dev.platform_data = &omap4_panda_dvi_connector_pdata,
-};
-
-/* TFP410 DPI-to-DVI chip */
-static struct encoder_tfp410_platform_data omap4_panda_tfp410_pdata = {
- .name = "tfp410.0",
- .source = "dpi.0",
- .data_lines = 24,
- .power_down_gpio = PANDA_DVI_TFP410_POWER_DOWN_GPIO,
-};
-
-static struct platform_device omap4_panda_tfp410_device = {
- .name = "tfp410",
- .id = 0,
- .dev.platform_data = &omap4_panda_tfp410_pdata,
-};
-
-/* HDMI Connector */
-static struct connector_hdmi_platform_data omap4_panda_hdmi_connector_pdata = {
- .name = "hdmi",
- .source = "tpd12s015.0",
-};
-
-static struct platform_device omap4_panda_hdmi_connector_device = {
- .name = "connector-hdmi",
- .id = 0,
- .dev.platform_data = &omap4_panda_hdmi_connector_pdata,
-};
-
-/* TPD12S015 HDMI ESD protection & level shifter chip */
-static struct encoder_tpd12s015_platform_data omap4_panda_tpd_pdata = {
- .name = "tpd12s015.0",
- .source = "hdmi.0",
-
- .ct_cp_hpd_gpio = HDMI_GPIO_CT_CP_HPD,
- .ls_oe_gpio = HDMI_GPIO_LS_OE,
- .hpd_gpio = HDMI_GPIO_HPD,
-};
-
-static struct platform_device omap4_panda_tpd_device = {
- .name = "tpd12s015",
- .id = 0,
- .dev.platform_data = &omap4_panda_tpd_pdata,
-};
-
-static struct omap_dss_board_info omap4_panda_dss_data = {
- .default_display_name = "dvi",
-};
-
-void __init omap4_panda_display_init_of(void)
-{
- omap_display_init(&omap4_panda_dss_data);
-
- platform_device_register(&omap4_panda_tfp410_device);
- platform_device_register(&omap4_panda_dvi_connector_device);
-
- platform_device_register(&omap4_panda_tpd_device);
- platform_device_register(&omap4_panda_hdmi_connector_device);
-}
-
-
-/* OMAP4 Blaze display data */
-
-#define DISPLAY_SEL_GPIO 59 /* LCD2/PicoDLP switch */
-#define DLP_POWER_ON_GPIO 40
-
-static struct panel_dsicm_platform_data dsi1_panel = {
- .name = "lcd",
- .source = "dsi.0",
- .reset_gpio = 102,
- .use_ext_te = false,
- .ext_te_gpio = 101,
- .pin_config = {
- .num_pins = 6,
- .pins = { 0, 1, 2, 3, 4, 5 },
- },
-};
-
-static struct platform_device sdp4430_lcd_device = {
- .name = "panel-dsi-cm",
- .id = 0,
- .dev.platform_data = &dsi1_panel,
-};
-
-static struct panel_dsicm_platform_data dsi2_panel = {
- .name = "lcd2",
- .source = "dsi.1",
- .reset_gpio = 104,
- .use_ext_te = false,
- .ext_te_gpio = 103,
- .pin_config = {
- .num_pins = 6,
- .pins = { 0, 1, 2, 3, 4, 5 },
- },
-};
-
-static struct platform_device sdp4430_lcd2_device = {
- .name = "panel-dsi-cm",
- .id = 1,
- .dev.platform_data = &dsi2_panel,
-};
-
-/* HDMI Connector */
-static struct connector_hdmi_platform_data sdp4430_hdmi_connector_pdata = {
- .name = "hdmi",
- .source = "tpd12s015.0",
-};
-
-static struct platform_device sdp4430_hdmi_connector_device = {
- .name = "connector-hdmi",
- .id = 0,
- .dev.platform_data = &sdp4430_hdmi_connector_pdata,
-};
-
-/* TPD12S015 HDMI ESD protection & level shifter chip */
-static struct encoder_tpd12s015_platform_data sdp4430_tpd_pdata = {
- .name = "tpd12s015.0",
- .source = "hdmi.0",
-
- .ct_cp_hpd_gpio = HDMI_GPIO_CT_CP_HPD,
- .ls_oe_gpio = HDMI_GPIO_LS_OE,
- .hpd_gpio = HDMI_GPIO_HPD,
-};
-
-static struct platform_device sdp4430_tpd_device = {
- .name = "tpd12s015",
- .id = 0,
- .dev.platform_data = &sdp4430_tpd_pdata,
-};
-
-
-static struct omap_dss_board_info sdp4430_dss_data = {
- .default_display_name = "lcd",
-};
-
-/*
- * we select LCD2 by default (instead of Pico DLP) by setting DISPLAY_SEL_GPIO.
- * Setting DLP_POWER_ON gpio enables the VDLP_2V5 VDLP_1V8 and VDLP_1V0 rails
- * used by picodlp on the 4430sdp platform. Keep this gpio disabled as LCD2 is
- * selected by default
- */
-void __init omap_4430sdp_display_init_of(void)
-{
- int r;
-
- r = gpio_request_one(DISPLAY_SEL_GPIO, GPIOF_OUT_INIT_HIGH,
- "display_sel");
- if (r)
- pr_err("%s: Could not get display_sel GPIO\n", __func__);
-
- r = gpio_request_one(DLP_POWER_ON_GPIO, GPIOF_OUT_INIT_LOW,
- "DLP POWER ON");
- if (r)
- pr_err("%s: Could not get DLP POWER ON GPIO\n", __func__);
-
- omap_display_init(&sdp4430_dss_data);
-
- platform_device_register(&sdp4430_lcd_device);
- platform_device_register(&sdp4430_lcd2_device);
-
- platform_device_register(&sdp4430_tpd_device);
- platform_device_register(&sdp4430_hdmi_connector_device);
-}
-
-
-/* OMAP3 IGEPv2 data */
-
-#define IGEP2_DVI_TFP410_POWER_DOWN_GPIO 170
-
-/* DVI Connector */
-static struct connector_dvi_platform_data omap3_igep2_dvi_connector_pdata = {
- .name = "dvi",
- .source = "tfp410.0",
- .i2c_bus_num = 2,
-};
-
-static struct platform_device omap3_igep2_dvi_connector_device = {
- .name = "connector-dvi",
- .id = 0,
- .dev.platform_data = &omap3_igep2_dvi_connector_pdata,
-};
-
-/* TFP410 DPI-to-DVI chip */
-static struct encoder_tfp410_platform_data omap3_igep2_tfp410_pdata = {
- .name = "tfp410.0",
- .source = "dpi.0",
- .data_lines = 24,
- .power_down_gpio = IGEP2_DVI_TFP410_POWER_DOWN_GPIO,
-};
-
-static struct platform_device omap3_igep2_tfp410_device = {
- .name = "tfp410",
- .id = 0,
- .dev.platform_data = &omap3_igep2_tfp410_pdata,
-};
-
-static struct omap_dss_board_info igep2_dss_data = {
- .default_display_name = "dvi",
-};
-
-void __init omap3_igep2_display_init_of(void)
-{
- omap_display_init(&igep2_dss_data);
-
- platform_device_register(&omap3_igep2_tfp410_device);
- platform_device_register(&omap3_igep2_dvi_connector_device);
-}
diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c
index 174caecc3186..4349e82debfe 100644
--- a/arch/arm/mach-omap2/gpmc-nand.c
+++ b/arch/arm/mach-omap2/gpmc-nand.c
@@ -45,24 +45,31 @@ static struct platform_device gpmc_nand_device = {
static bool gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt)
{
- /* support only OMAP3 class */
- if (!cpu_is_omap34xx() && !soc_is_am33xx()) {
- pr_err("BCH ecc is not supported on this CPU\n");
+ /* platforms which support all ECC schemes */
+ if (soc_is_am33xx() || cpu_is_omap44xx() ||
+ soc_is_omap54xx() || soc_is_dra7xx())
+ return 1;
+
+ /* OMAP3xxx do not have ELM engine, so cannot support ECC schemes
+ * which require H/W based ECC error detection */
+ if ((cpu_is_omap34xx() || cpu_is_omap3630()) &&
+ ((ecc_opt == OMAP_ECC_BCH4_CODE_HW) ||
+ (ecc_opt == OMAP_ECC_BCH8_CODE_HW)))
return 0;
- }
/*
* For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1
* and AM33xx derivates. Other chips may be added if confirmed to work.
*/
- if ((ecc_opt == OMAP_ECC_BCH4_CODE_HW) &&
- (!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0)) &&
- (!soc_is_am33xx())) {
- pr_err("BCH 4-bit mode is not supported on this CPU\n");
+ if ((ecc_opt == OMAP_ECC_BCH4_CODE_HW_DETECTION_SW) &&
+ (!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0)))
return 0;
- }
- return 1;
+ /* legacy platforms support only HAM1 (1-bit Hamming) ECC scheme */
+ if (ecc_opt == OMAP_ECC_HAM1_CODE_HW)
+ return 1;
+ else
+ return 0;
}
/* This function will go away once the device-tree convertion is complete */
@@ -133,8 +140,10 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
gpmc_update_nand_reg(&gpmc_nand_data->reg, gpmc_nand_data->cs);
- if (!gpmc_hwecc_bch_capable(gpmc_nand_data->ecc_opt))
+ if (!gpmc_hwecc_bch_capable(gpmc_nand_data->ecc_opt)) {
+ dev_err(dev, "Unsupported NAND ECC scheme selected\n");
return -EINVAL;
+ }
err = platform_device_register(&gpmc_nand_device);
if (err < 0) {
diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c
index 9428c5f9d4f2..157412e4273a 100644
--- a/arch/arm/mach-omap2/id.c
+++ b/arch/arm/mach-omap2/id.c
@@ -465,8 +465,18 @@ void __init omap3xxx_check_revision(void)
}
break;
case 0xb98c:
- omap_revision = AM437X_REV_ES1_0;
- cpu_rev = "1.0";
+ switch (rev) {
+ case 0:
+ omap_revision = AM437X_REV_ES1_0;
+ cpu_rev = "1.0";
+ break;
+ case 1:
+ /* FALLTHROUGH */
+ default:
+ omap_revision = AM437X_REV_ES1_1;
+ cpu_rev = "1.1";
+ break;
+ }
break;
case 0xb8f2:
switch (rev) {
@@ -657,6 +667,8 @@ static const char * __init omap_get_family(void)
return kasprintf(GFP_KERNEL, "OMAP4");
else if (soc_is_omap54xx())
return kasprintf(GFP_KERNEL, "OMAP5");
+ else if (soc_is_am43xx())
+ return kasprintf(GFP_KERNEL, "AM43xx");
else
return kasprintf(GFP_KERNEL, "Unknown");
}
diff --git a/arch/arm/mach-omap2/include/mach/timex.h b/arch/arm/mach-omap2/include/mach/timex.h
deleted file mode 100644
index de9f8fc40e7c..000000000000
--- a/arch/arm/mach-omap2/include/mach/timex.h
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * arch/arm/mach-omap2/include/mach/timex.h
- */
-
-#include <plat/timex.h>
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index af432b191255..f14f9ac2dca1 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -604,6 +604,7 @@ void __init am43xx_init_early(void)
omap_prm_base_init();
omap_cm_base_init();
omap3xxx_check_revision();
+ am33xx_check_features();
am43xx_powerdomains_init();
am43xx_clockdomains_init();
am43xx_hwmod_init();
diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
index e022a869bff2..6037a9a01ed5 100644
--- a/arch/arm/mach-omap2/irq.c
+++ b/arch/arm/mach-omap2/irq.c
@@ -222,6 +222,7 @@ void __init ti81xx_init_irq(void)
static inline void omap_intc_handle_irq(void __iomem *base_addr, struct pt_regs *regs)
{
u32 irqnr;
+ int handled_irq = 0;
do {
irqnr = readl_relaxed(base_addr + 0x98);
@@ -249,8 +250,15 @@ out:
if (irqnr) {
irqnr = irq_find_mapping(domain, irqnr);
handle_IRQ(irqnr, regs);
+ handled_irq = 1;
}
} while (irqnr);
+
+ /* If an irq is masked or deasserted while active, we will
+ * keep ending up here with no irq handled. So remove it from
+ * the INTC with an ack.*/
+ if (!handled_irq)
+ omap_ack_irq(NULL);
}
asmlinkage void __exception_irq_entry omap2_intc_handle_irq(struct pt_regs *regs)
diff --git a/arch/arm/mach-omap2/mux.h b/arch/arm/mach-omap2/mux.h
index a722330d4d53..d121fb6df4e6 100644
--- a/arch/arm/mach-omap2/mux.h
+++ b/arch/arm/mach-omap2/mux.h
@@ -63,9 +63,6 @@
#define OMAP_PACKAGE_CUS 5 /* 423-pin 0.65 */
#define OMAP_PACKAGE_CBB 4 /* 515-pin 0.40 0.50 */
#define OMAP_PACKAGE_CBC 3 /* 515-pin 0.50 0.65 */
-#define OMAP_PACKAGE_ZAC 2 /* 24xx 447-pin POP */
-#define OMAP_PACKAGE_ZAF 1 /* 2420 447-pin SIP */
-
#define OMAP_MUX_NR_MODES 8 /* Available modes */
#define OMAP_MUX_NR_SIDES 2 /* Bottom & top */
diff --git a/arch/arm/mach-omap2/omap-iommu.c b/arch/arm/mach-omap2/omap-iommu.c
index f6daae821ebb..f1fab5684a24 100644
--- a/arch/arm/mach-omap2/omap-iommu.c
+++ b/arch/arm/mach-omap2/omap-iommu.c
@@ -10,6 +10,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/of.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
@@ -58,6 +59,10 @@ static int __init omap_iommu_dev_init(struct omap_hwmod *oh, void *unused)
static int __init omap_iommu_init(void)
{
+ /* If dtb is there, the devices will be created dynamically */
+ if (of_have_populated_dt())
+ return -ENODEV;
+
return omap_hwmod_for_each_by_class("mmu", omap_iommu_dev_init, NULL);
}
/* must be ready before omap3isp is probed */
diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c
index 3664562f9148..693fe486e917 100644
--- a/arch/arm/mach-omap2/omap-wakeupgen.c
+++ b/arch/arm/mach-omap2/omap-wakeupgen.c
@@ -138,7 +138,7 @@ static void wakeupgen_mask(struct irq_data *d)
unsigned long flags;
raw_spin_lock_irqsave(&wakeupgen_lock, flags);
- _wakeupgen_clear(d->irq, irq_target_cpu[d->irq]);
+ _wakeupgen_clear(d->hwirq, irq_target_cpu[d->hwirq]);
raw_spin_unlock_irqrestore(&wakeupgen_lock, flags);
}
@@ -150,7 +150,7 @@ static void wakeupgen_unmask(struct irq_data *d)
unsigned long flags;
raw_spin_lock_irqsave(&wakeupgen_lock, flags);
- _wakeupgen_set(d->irq, irq_target_cpu[d->irq]);
+ _wakeupgen_set(d->hwirq, irq_target_cpu[d->hwirq]);
raw_spin_unlock_irqrestore(&wakeupgen_lock, flags);
}
diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c
index 6cd3f3772ecf..95e171a055f3 100644
--- a/arch/arm/mach-omap2/omap4-common.c
+++ b/arch/arm/mach-omap2/omap4-common.c
@@ -22,6 +22,7 @@
#include <linux/of_platform.h>
#include <linux/export.h>
#include <linux/irqchip/arm-gic.h>
+#include <linux/irqchip/irq-crossbar.h>
#include <linux/of_address.h>
#include <linux/reboot.h>
@@ -288,5 +289,8 @@ void __init omap_gic_of_init(void)
skip_errata_init:
omap_wakeupgen_init();
+#ifdef CONFIG_IRQ_CROSSBAR
+ irqcrossbar_init();
+#endif
irqchip_init();
}
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 4c3b1e6df508..a123ff0070bd 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -1955,10 +1955,6 @@ static struct omap_hwmod_class omap3xxx_usb_host_hs_hwmod_class = {
.sysc = &omap3xxx_usb_host_hs_sysc,
};
-static struct omap_hwmod_opt_clk omap3xxx_usb_host_hs_opt_clks[] = {
- { .role = "ehci_logic_fck", .clk = "usbhost_120m_fck", },
-};
-
static struct omap_hwmod_irq_info omap3xxx_usb_host_hs_irqs[] = {
{ .name = "ohci-irq", .irq = 76 + OMAP_INTC_START, },
{ .name = "ehci-irq", .irq = 77 + OMAP_INTC_START, },
@@ -1981,8 +1977,6 @@ static struct omap_hwmod omap3xxx_usb_host_hs_hwmod = {
.idlest_stdby_bit = OMAP3430ES2_ST_USBHOST_STDBY_SHIFT,
},
},
- .opt_clks = omap3xxx_usb_host_hs_opt_clks,
- .opt_clks_cnt = ARRAY_SIZE(omap3xxx_usb_host_hs_opt_clks),
/*
* Errata: USBHOST Configured In Smart-Idle Can Lead To a Deadlock
@@ -3029,8 +3023,6 @@ static struct omap_hwmod omap3xxx_mmu_isp_hwmod = {
.flags = HWMOD_NO_IDLEST,
};
-#ifdef CONFIG_OMAP_IOMMU_IVA2
-
/* mmu iva */
static struct omap_mmu_dev_attr mmu_iva_dev_attr = {
@@ -3070,20 +3062,22 @@ static struct omap_hwmod omap3xxx_mmu_iva_hwmod = {
.name = "mmu_iva",
.class = &omap3xxx_mmu_hwmod_class,
.mpu_irqs = omap3xxx_mmu_iva_irqs,
+ .clkdm_name = "iva2_clkdm",
.rst_lines = omap3xxx_mmu_iva_resets,
.rst_lines_cnt = ARRAY_SIZE(omap3xxx_mmu_iva_resets),
.main_clk = "iva2_ck",
.prcm = {
.omap2 = {
.module_offs = OMAP3430_IVA2_MOD,
+ .module_bit = OMAP3430_CM_FCLKEN_IVA2_EN_IVA2_SHIFT,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_IVA2_SHIFT,
},
},
.dev_attr = &mmu_iva_dev_attr,
.flags = HWMOD_NO_IDLEST,
};
-#endif
-
/* l4_per -> gpio4 */
static struct omap_hwmod_addr_space omap3xxx_gpio4_addrs[] = {
{
@@ -3855,9 +3849,7 @@ static struct omap_hwmod_ocp_if *omap34xx_hwmod_ocp_ifs[] __initdata = {
&omap3xxx_l4_core__hdq1w,
&omap3xxx_sad2d__l3,
&omap3xxx_l4_core__mmu_isp,
-#ifdef CONFIG_OMAP_IOMMU_IVA2
&omap3xxx_l3_main__mmu_iva,
-#endif
&omap34xx_l4_core__ssi,
NULL
};
@@ -3881,9 +3873,7 @@ static struct omap_hwmod_ocp_if *omap36xx_hwmod_ocp_ifs[] __initdata = {
&omap3xxx_l4_core__hdq1w,
&omap3xxx_sad2d__l3,
&omap3xxx_l4_core__mmu_isp,
-#ifdef CONFIG_OMAP_IOMMU_IVA2
&omap3xxx_l3_main__mmu_iva,
-#endif
NULL
};
diff --git a/arch/arm/mach-omap2/omap_hwmod_43xx_data.c b/arch/arm/mach-omap2/omap_hwmod_43xx_data.c
index 9002fca76699..5c2cc8083fdd 100644
--- a/arch/arm/mach-omap2/omap_hwmod_43xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_43xx_data.c
@@ -719,6 +719,7 @@ static struct omap_hwmod_ocp_if *am43xx_hwmod_ocp_ifs[] __initdata = {
&am33xx_l4_ls__uart4,
&am33xx_l4_ls__uart5,
&am33xx_l4_ls__uart6,
+ &am33xx_l4_ls__spinlock,
&am33xx_l4_ls__elm,
&am33xx_l4_ls__epwmss0,
&am33xx_epwmss0__ecap0,
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index 3318cae96e7d..1219280bb976 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -2541,8 +2541,7 @@ static struct omap_hwmod_class_sysconfig omap44xx_spinlock_sysc = {
.sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE |
SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
- .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
- SIDLE_SMART_WKUP),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
.sysc_fields = &omap_hwmod_sysc_type1,
};
diff --git a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c
index e297d6231c3a..892317294fdc 100644
--- a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c
@@ -1122,6 +1122,71 @@ static struct omap_hwmod omap54xx_mmc5_hwmod = {
};
/*
+ * 'mmu' class
+ * The memory management unit performs virtual to physical address translation
+ * for its requestors.
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_mmu_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+ SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET |
+ SYSS_HAS_RESET_STATUS),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_mmu_hwmod_class = {
+ .name = "mmu",
+ .sysc = &omap54xx_mmu_sysc,
+};
+
+static struct omap_hwmod_rst_info omap54xx_mmu_dsp_resets[] = {
+ { .name = "mmu_cache", .rst_shift = 1 },
+};
+
+static struct omap_hwmod omap54xx_mmu_dsp_hwmod = {
+ .name = "mmu_dsp",
+ .class = &omap54xx_mmu_hwmod_class,
+ .clkdm_name = "dsp_clkdm",
+ .rst_lines = omap54xx_mmu_dsp_resets,
+ .rst_lines_cnt = ARRAY_SIZE(omap54xx_mmu_dsp_resets),
+ .main_clk = "dpll_iva_h11x2_ck",
+ .prcm = {
+ .omap4 = {
+ .clkctrl_offs = OMAP54XX_CM_DSP_DSP_CLKCTRL_OFFSET,
+ .rstctrl_offs = OMAP54XX_RM_DSP_RSTCTRL_OFFSET,
+ .context_offs = OMAP54XX_RM_DSP_DSP_CONTEXT_OFFSET,
+ .modulemode = MODULEMODE_HWCTRL,
+ },
+ },
+};
+
+/* mmu ipu */
+static struct omap_hwmod_rst_info omap54xx_mmu_ipu_resets[] = {
+ { .name = "mmu_cache", .rst_shift = 2 },
+};
+
+static struct omap_hwmod omap54xx_mmu_ipu_hwmod = {
+ .name = "mmu_ipu",
+ .class = &omap54xx_mmu_hwmod_class,
+ .clkdm_name = "ipu_clkdm",
+ .rst_lines = omap54xx_mmu_ipu_resets,
+ .rst_lines_cnt = ARRAY_SIZE(omap54xx_mmu_ipu_resets),
+ .main_clk = "dpll_core_h22x2_ck",
+ .prcm = {
+ .omap4 = {
+ .clkctrl_offs = OMAP54XX_CM_IPU_IPU_CLKCTRL_OFFSET,
+ .rstctrl_offs = OMAP54XX_RM_IPU_RSTCTRL_OFFSET,
+ .context_offs = OMAP54XX_RM_IPU_IPU_CONTEXT_OFFSET,
+ .modulemode = MODULEMODE_HWCTRL,
+ },
+ },
+};
+
+/*
* 'mpu' class
* mpu sub-system
*/
@@ -1763,6 +1828,14 @@ static struct omap_hwmod_ocp_if omap54xx_l4_cfg__l3_main_1 = {
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
+/* l4_cfg -> mmu_dsp */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__mmu_dsp = {
+ .master = &omap54xx_l4_cfg_hwmod,
+ .slave = &omap54xx_mmu_dsp_hwmod,
+ .clk = "l4_root_clk_div",
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
/* mpu -> l3_main_1 */
static struct omap_hwmod_ocp_if omap54xx_mpu__l3_main_1 = {
.master = &omap54xx_mpu_hwmod,
@@ -1787,6 +1860,14 @@ static struct omap_hwmod_ocp_if omap54xx_l4_cfg__l3_main_2 = {
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
+/* l3_main_2 -> mmu_ipu */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__mmu_ipu = {
+ .master = &omap54xx_l3_main_2_hwmod,
+ .slave = &omap54xx_mmu_ipu_hwmod,
+ .clk = "l3_iclk_div",
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
/* l3_main_1 -> l3_main_3 */
static struct omap_hwmod_ocp_if omap54xx_l3_main_1__l3_main_3 = {
.master = &omap54xx_l3_main_1_hwmod,
@@ -2345,6 +2426,7 @@ static struct omap_hwmod_ocp_if *omap54xx_hwmod_ocp_ifs[] __initdata = {
&omap54xx_l4_wkup__counter_32k,
&omap54xx_l4_cfg__dma_system,
&omap54xx_l4_abe__dmic,
+ &omap54xx_l4_cfg__mmu_dsp,
&omap54xx_mpu__emif1,
&omap54xx_mpu__emif2,
&omap54xx_l4_wkup__gpio1,
@@ -2360,6 +2442,7 @@ static struct omap_hwmod_ocp_if *omap54xx_hwmod_ocp_ifs[] __initdata = {
&omap54xx_l4_per__i2c3,
&omap54xx_l4_per__i2c4,
&omap54xx_l4_per__i2c5,
+ &omap54xx_l3_main_2__mmu_ipu,
&omap54xx_l4_wkup__kbd,
&omap54xx_l4_cfg__mailbox,
&omap54xx_l4_abe__mcbsp1,
diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c
index c33e07e2f0d4..c3b73351cb7a 100644
--- a/arch/arm/mach-omap2/pdata-quirks.c
+++ b/arch/arm/mach-omap2/pdata-quirks.c
@@ -16,12 +16,14 @@
#include <linux/wl12xx.h>
#include <linux/platform_data/pinctrl-single.h>
+#include <linux/platform_data/iommu-omap.h>
#include "am35xx.h"
#include "common.h"
#include "common-board-devices.h"
#include "dss-common.h"
#include "control.h"
+#include "omap_device.h"
#include "omap-secure.h"
#include "soc.h"
@@ -33,20 +35,6 @@ struct pdata_init {
struct of_dev_auxdata omap_auxdata_lookup[];
static struct twl4030_gpio_platform_data twl_gpio_auxdata;
-/*
- * Create alias for USB host PHY clock.
- * Remove this when clock phandle can be provided via DT
- */
-static void __init __used legacy_init_ehci_clk(char *clkname)
-{
- int ret;
-
- ret = clk_add_alias("main_clk", NULL, clkname, NULL);
- if (ret)
- pr_err("%s:Failed to add main_clk alias to %s :%d\n",
- __func__, clkname, ret);
-}
-
#if IS_ENABLED(CONFIG_WL12XX)
static struct wl12xx_platform_data wl12xx __initdata;
@@ -94,6 +82,12 @@ static void __init hsmmc2_internal_input_clk(void)
omap_ctrl_writel(reg, OMAP343X_CONTROL_DEVCONF1);
}
+static struct iommu_platform_data omap3_iommu_pdata = {
+ .reset_name = "mmu",
+ .assert_reset = omap_device_assert_hardreset,
+ .deassert_reset = omap_device_deassert_hardreset,
+};
+
static int omap3_sbc_t3730_twl_callback(struct device *dev,
unsigned gpio,
unsigned ngpio)
@@ -101,7 +95,7 @@ static int omap3_sbc_t3730_twl_callback(struct device *dev,
int res;
res = gpio_request_one(gpio + 2, GPIOF_OUT_INIT_HIGH,
- "wlan rst");
+ "wlan pwr");
if (res)
return res;
@@ -110,6 +104,23 @@ static int omap3_sbc_t3730_twl_callback(struct device *dev,
return 0;
}
+static void __init omap3_sbc_t3x_usb_hub_init(int gpio, char *hub_name)
+{
+ int err = gpio_request_one(gpio, GPIOF_OUT_INIT_LOW, hub_name);
+
+ if (err) {
+ pr_err("SBC-T3x: %s reset gpio request failed: %d\n",
+ hub_name, err);
+ return;
+ }
+
+ gpio_export(gpio, 0);
+
+ udelay(10);
+ gpio_set_value(gpio, 1);
+ msleep(1);
+}
+
static void __init omap3_sbc_t3730_twl_init(void)
{
twl_gpio_auxdata.setup = omap3_sbc_t3730_twl_callback;
@@ -117,13 +128,19 @@ static void __init omap3_sbc_t3730_twl_init(void)
static void __init omap3_sbc_t3730_legacy_init(void)
{
+ omap3_sbc_t3x_usb_hub_init(167, "sb-t35 usb hub");
legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 136);
omap_ads7846_init(1, 57, 0, NULL);
}
+static void __init omap3_sbc_t3530_legacy_init(void)
+{
+ omap3_sbc_t3x_usb_hub_init(167, "sb-t35 usb hub");
+ omap_ads7846_init(1, 57, 0, NULL);
+}
+
static void __init omap3_igep0020_legacy_init(void)
{
- omap3_igep2_display_init_of();
}
static void __init omap3_evm_legacy_init(void)
@@ -162,7 +179,7 @@ static struct emac_platform_data am35xx_emac_pdata = {
.interrupt_disable = am35xx_disable_emac_int,
};
-static void __init am3517_evm_legacy_init(void)
+static void __init am35xx_emac_reset(void)
{
u32 v;
@@ -172,6 +189,43 @@ static void __init am3517_evm_legacy_init(void)
omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET); /* OCP barrier */
}
+static struct gpio cm_t3517_wlan_gpios[] __initdata = {
+ { 56, GPIOF_OUT_INIT_HIGH, "wlan pwr" },
+ { 4, GPIOF_OUT_INIT_HIGH, "xcvr noe" },
+};
+
+static void __init omap3_sbc_t3517_wifi_init(void)
+{
+ int err = gpio_request_array(cm_t3517_wlan_gpios,
+ ARRAY_SIZE(cm_t3517_wlan_gpios));
+ if (err) {
+ pr_err("SBC-T3517: wl12xx gpios request failed: %d\n", err);
+ return;
+ }
+
+ gpio_export(cm_t3517_wlan_gpios[0].gpio, 0);
+ gpio_export(cm_t3517_wlan_gpios[1].gpio, 0);
+
+ msleep(100);
+ gpio_set_value(cm_t3517_wlan_gpios[1].gpio, 0);
+}
+
+static void __init omap3_sbc_t3517_legacy_init(void)
+{
+ omap3_sbc_t3x_usb_hub_init(152, "cm-t3517 usb hub");
+ omap3_sbc_t3x_usb_hub_init(98, "sb-t35 usb hub");
+ am35xx_emac_reset();
+ hsmmc2_internal_input_clk();
+ omap3_sbc_t3517_wifi_init();
+ legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 145);
+ omap_ads7846_init(1, 57, 0, NULL);
+}
+
+static void __init am3517_evm_legacy_init(void)
+{
+ am35xx_emac_reset();
+}
+
static void __init nokia_n900_legacy_init(void)
{
hsmmc2_internal_input_clk();
@@ -192,23 +246,34 @@ static void __init nokia_n900_legacy_init(void)
#ifdef CONFIG_ARCH_OMAP4
static void __init omap4_sdp_legacy_init(void)
{
- omap_4430sdp_display_init_of();
legacy_init_wl12xx(WL12XX_REFCLOCK_26,
WL12XX_TCXOCLOCK_26, 53);
}
static void __init omap4_panda_legacy_init(void)
{
- omap4_panda_display_init_of();
- legacy_init_ehci_clk("auxclk3_ck");
legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 53);
}
#endif
+#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5)
+static struct iommu_platform_data omap4_iommu_pdata = {
+ .reset_name = "mmu_cache",
+ .assert_reset = omap_device_assert_hardreset,
+ .deassert_reset = omap_device_deassert_hardreset,
+};
+#endif
+
+#ifdef CONFIG_SOC_AM33XX
+static void __init am335x_evmsk_legacy_init(void)
+{
+ legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 31);
+}
+#endif
+
#ifdef CONFIG_SOC_OMAP5
static void __init omap5_uevm_legacy_init(void)
{
- legacy_init_ehci_clk("auxclk1_ck");
}
#endif
@@ -259,6 +324,8 @@ struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("ti,omap3-padconf", 0x48002030, "48002030.pinmux", &pcs_pdata),
OF_DEV_AUXDATA("ti,omap3-padconf", 0x480025a0, "480025a0.pinmux", &pcs_pdata),
OF_DEV_AUXDATA("ti,omap3-padconf", 0x48002a00, "48002a00.pinmux", &pcs_pdata),
+ OF_DEV_AUXDATA("ti,omap2-iommu", 0x5d000000, "5d000000.mmu",
+ &omap3_iommu_pdata),
/* Only on am3517 */
OF_DEV_AUXDATA("ti,davinci_mdio", 0x5c030000, "davinci_mdio.0", NULL),
OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0",
@@ -268,6 +335,12 @@ struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a100040, "4a100040.pinmux", &pcs_pdata),
OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a31e040, "4a31e040.pinmux", &pcs_pdata),
#endif
+#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5)
+ OF_DEV_AUXDATA("ti,omap4-iommu", 0x4a066000, "4a066000.mmu",
+ &omap4_iommu_pdata),
+ OF_DEV_AUXDATA("ti,omap4-iommu", 0x55082000, "55082000.mmu",
+ &omap4_iommu_pdata),
+#endif
{ /* sentinel */ },
};
@@ -277,6 +350,8 @@ struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
*/
static struct pdata_init pdata_quirks[] __initdata = {
#ifdef CONFIG_ARCH_OMAP3
+ { "compulab,omap3-sbc-t3517", omap3_sbc_t3517_legacy_init, },
+ { "compulab,omap3-sbc-t3530", omap3_sbc_t3530_legacy_init, },
{ "compulab,omap3-sbc-t3730", omap3_sbc_t3730_legacy_init, },
{ "nokia,omap3-n900", nokia_n900_legacy_init, },
{ "nokia,omap3-n9", hsmmc2_internal_input_clk, },
@@ -290,6 +365,9 @@ static struct pdata_init pdata_quirks[] __initdata = {
{ "ti,omap4-sdp", omap4_sdp_legacy_init, },
{ "ti,omap4-panda", omap4_panda_legacy_init, },
#endif
+#ifdef CONFIG_SOC_AM33XX
+ { "ti,am335x-evmsk", am335x_evmsk_legacy_init, },
+#endif
#ifdef CONFIG_SOC_OMAP5
{ "ti,omap5-uevm", omap5_uevm_legacy_init, },
#endif
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index 7bdd22afce69..d4d0fce325c7 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -103,7 +103,7 @@ static inline void enable_omap3630_toggle_l2_on_restore(void) { }
#define PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD (1 << 0)
-#if defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP4)
extern u16 pm44xx_errata;
#define IS_PM44XX_ERRATUM(id) (pm44xx_errata & (id))
#else
diff --git a/arch/arm/mach-omap2/prminst44xx.c b/arch/arm/mach-omap2/prminst44xx.c
index 280f3c58abe5..05fcf6de44ee 100644
--- a/arch/arm/mach-omap2/prminst44xx.c
+++ b/arch/arm/mach-omap2/prminst44xx.c
@@ -25,6 +25,7 @@
#include "prminst44xx.h"
#include "prm-regbits-44xx.h"
#include "prcm44xx.h"
+#include "prcm43xx.h"
#include "prcm_mpu44xx.h"
#include "soc.h"
@@ -176,6 +177,8 @@ void omap4_prminst_global_warm_sw_reset(void)
dev_inst = OMAP54XX_PRM_DEVICE_INST;
else if (soc_is_dra7xx())
dev_inst = DRA7XX_PRM_DEVICE_INST;
+ else if (soc_is_am43xx())
+ dev_inst = AM43XX_PRM_DEVICE_INST;
else
return;
diff --git a/arch/arm/mach-omap2/soc.h b/arch/arm/mach-omap2/soc.h
index 076bd90a6ce0..30abcc8b20e0 100644
--- a/arch/arm/mach-omap2/soc.h
+++ b/arch/arm/mach-omap2/soc.h
@@ -438,7 +438,8 @@ IS_OMAP_TYPE(3430, 0x3430)
#define AM335X_REV_ES2_1 (AM335X_CLASS | (0x2 << 8))
#define AM437X_CLASS 0x43700000
-#define AM437X_REV_ES1_0 AM437X_CLASS
+#define AM437X_REV_ES1_0 (AM437X_CLASS | (0x10 << 8))
+#define AM437X_REV_ES1_1 (AM437X_CLASS | (0x11 << 8))
#define OMAP443X_CLASS 0x44300044
#define OMAP4430_REV_ES1_0 (OMAP443X_CLASS | (0x10 << 8))
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index 74044aaf438b..b62de9f9d05c 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -604,7 +604,8 @@ OMAP_SYS_32K_TIMER_INIT(3_secure, 12, "secure_32k_fck", "ti,timer-secure",
2, "timer_sys_ck", NULL);
#endif /* CONFIG_ARCH_OMAP3 */
-#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_AM33XX)
+#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_AM33XX) || \
+ defined(CONFIG_SOC_AM43XX)
OMAP_SYS_GP_TIMER_INIT(3, 2, "timer_sys_ck", NULL,
1, "timer_sys_ck", "ti,timer-alwon");
#endif
diff --git a/arch/arm/mach-orion5x/Kconfig b/arch/arm/mach-orion5x/Kconfig
index 2cb2f06c20f5..14f2cae4109c 100644
--- a/arch/arm/mach-orion5x/Kconfig
+++ b/arch/arm/mach-orion5x/Kconfig
@@ -33,7 +33,6 @@ config MACH_KUROBOX_PRO
config MACH_DNS323
bool "D-Link DNS-323"
select I2C_BOARDINFO
- select PHYLIB
help
Say 'Y' here if you want your kernel to support the
D-Link DNS-323 platform.
diff --git a/arch/arm/mach-orion5x/dns323-setup.c b/arch/arm/mach-orion5x/dns323-setup.c
index 70974732cbf0..56edeab17b68 100644
--- a/arch/arm/mach-orion5x/dns323-setup.c
+++ b/arch/arm/mach-orion5x/dns323-setup.c
@@ -642,6 +642,8 @@ static void __init dns323_init(void)
platform_device_register_simple("dns323c-fan", 0, NULL, 0);
/* Register fixup for the PHY LEDs */
+ if (!IS_BUILTIN(CONFIG_PHYLIB))
+ break;
phy_register_fixup_for_uid(MARVELL_PHY_ID_88E1118,
MARVELL_PHY_ID_MASK,
dns323c_phy_fixup);
diff --git a/arch/arm/mach-orion5x/include/mach/bridge-regs.h b/arch/arm/mach-orion5x/include/mach/bridge-regs.h
index f727d03f1688..5766e3fbff69 100644
--- a/arch/arm/mach-orion5x/include/mach/bridge-regs.h
+++ b/arch/arm/mach-orion5x/include/mach/bridge-regs.h
@@ -18,6 +18,7 @@
#define CPU_CTRL (ORION5X_BRIDGE_VIRT_BASE + 0x104)
#define RSTOUTn_MASK (ORION5X_BRIDGE_VIRT_BASE + 0x108)
+#define RSTOUTn_MASK_PHYS (ORION5X_BRIDGE_PHYS_BASE + 0x108)
#define CPU_SOFT_RESET (ORION5X_BRIDGE_VIRT_BASE + 0x10c)
diff --git a/arch/arm/mach-orion5x/include/mach/timex.h b/arch/arm/mach-orion5x/include/mach/timex.h
deleted file mode 100644
index 4c69820e0810..000000000000
--- a/arch/arm/mach-orion5x/include/mach/timex.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * arch/arm/mach-orion5x/include/mach/timex.h
- *
- * Tzachi Perelstein <tzachi@marvell.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.
- */
-
-#define CLOCK_TICK_RATE (100 * HZ)
diff --git a/arch/arm/mach-picoxcell/Kconfig b/arch/arm/mach-picoxcell/Kconfig
index b1022f4315f7..eca9eb1c5931 100644
--- a/arch/arm/mach-picoxcell/Kconfig
+++ b/arch/arm/mach-picoxcell/Kconfig
@@ -1,12 +1,7 @@
config ARCH_PICOXCELL
bool "Picochip PicoXcell" if ARCH_MULTI_V6
select ARCH_REQUIRE_GPIOLIB
- select ARM_PATCH_PHYS_VIRT
select ARM_VIC
- select CPU_V6K
select DW_APB_TIMER_OF
- select GENERIC_CLOCKEVENTS
select HAVE_TCM
select NO_IOPORT
- select SPARSE_IRQ
- select USE_OF
diff --git a/arch/arm/mach-prima2/Kconfig b/arch/arm/mach-prima2/Kconfig
index 6988b117fc17..3e8189186a5b 100644
--- a/arch/arm/mach-prima2/Kconfig
+++ b/arch/arm/mach-prima2/Kconfig
@@ -1,9 +1,8 @@
config ARCH_SIRF
bool "CSR SiRF" if ARCH_MULTI_V7
+ select ARCH_HAS_RESET_CONTROLLER
select ARCH_REQUIRE_GPIOLIB
- select GENERIC_CLOCKEVENTS
select GENERIC_IRQ_CHIP
- select MIGHT_HAVE_CACHE_L2X0
select NO_IOPORT
select PINCTRL
select PINCTRL_SIRF
@@ -17,7 +16,6 @@ menu "CSR SiRF atlas6/primaII/Marco/Polo Specific Features"
config ARCH_ATLAS6
bool "CSR SiRFSoC ATLAS6 ARM Cortex A9 Platform"
default y
- select CPU_V7
select SIRF_IRQ
help
Support for CSR SiRFSoC ARM Cortex A9 Platform
@@ -25,7 +23,6 @@ config ARCH_ATLAS6
config ARCH_PRIMA2
bool "CSR SiRFSoC PRIMA2 ARM Cortex A9 Platform"
default y
- select CPU_V7
select SIRF_IRQ
select ZONE_DMA
help
@@ -35,9 +32,7 @@ config ARCH_MARCO
bool "CSR SiRFSoC MARCO ARM Cortex A9 Platform"
default y
select ARM_GIC
- select CPU_V7
select HAVE_ARM_SCU if SMP
- select HAVE_SMP
select SMP_ON_UP if SMP
help
Support for CSR SiRFSoC ARM Cortex A9 Platform
diff --git a/arch/arm/mach-prima2/common.c b/arch/arm/mach-prima2/common.c
index d49aff74de98..47c7819edb9b 100644
--- a/arch/arm/mach-prima2/common.c
+++ b/arch/arm/mach-prima2/common.c
@@ -15,7 +15,7 @@
#include <linux/of_platform.h>
#include "common.h"
-void __init sirfsoc_init_late(void)
+static void __init sirfsoc_init_late(void)
{
sirfsoc_pm_init();
}
@@ -27,7 +27,7 @@ static __init void sirfsoc_map_io(void)
}
#ifdef CONFIG_ARCH_ATLAS6
-static const char *atlas6_dt_match[] __initdata = {
+static const char *atlas6_dt_match[] __initconst = {
"sirf,atlas6",
NULL
};
@@ -37,12 +37,11 @@ DT_MACHINE_START(ATLAS6_DT, "Generic ATLAS6 (Flattened Device Tree)")
.map_io = sirfsoc_map_io,
.init_late = sirfsoc_init_late,
.dt_compat = atlas6_dt_match,
- .restart = sirfsoc_restart,
MACHINE_END
#endif
#ifdef CONFIG_ARCH_PRIMA2
-static const char *prima2_dt_match[] __initdata = {
+static const char *prima2_dt_match[] __initconst = {
"sirf,prima2",
NULL
};
@@ -53,12 +52,11 @@ DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)")
.dma_zone_size = SZ_256M,
.init_late = sirfsoc_init_late,
.dt_compat = prima2_dt_match,
- .restart = sirfsoc_restart,
MACHINE_END
#endif
#ifdef CONFIG_ARCH_MARCO
-static const char *marco_dt_match[] __initdata = {
+static const char *marco_dt_match[] __initconst = {
"sirf,marco",
NULL
};
@@ -69,6 +67,5 @@ DT_MACHINE_START(MARCO_DT, "Generic MARCO (Flattened Device Tree)")
.map_io = sirfsoc_map_io,
.init_late = sirfsoc_init_late,
.dt_compat = marco_dt_match,
- .restart = sirfsoc_restart,
MACHINE_END
#endif
diff --git a/arch/arm/mach-prima2/common.h b/arch/arm/mach-prima2/common.h
index 4b768060a858..07d3e5ed9264 100644
--- a/arch/arm/mach-prima2/common.h
+++ b/arch/arm/mach-prima2/common.h
@@ -23,7 +23,6 @@ extern void sirfsoc_secondary_startup(void);
extern void sirfsoc_cpu_die(unsigned int cpu);
extern void __init sirfsoc_of_irq_init(void);
-extern void sirfsoc_restart(enum reboot_mode, const char *);
extern asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs);
#ifndef CONFIG_DEBUG_LL
diff --git a/arch/arm/mach-prima2/l2x0.c b/arch/arm/mach-prima2/l2x0.c
index cbcbe9cb094c..c7102539c0b0 100644
--- a/arch/arm/mach-prima2/l2x0.c
+++ b/arch/arm/mach-prima2/l2x0.c
@@ -11,24 +11,23 @@
#include <linux/of.h>
#include <asm/hardware/cache-l2x0.h>
-struct l2x0_aux
-{
+struct l2x0_aux {
u32 val;
u32 mask;
};
-static struct l2x0_aux prima2_l2x0_aux __initconst = {
+static const struct l2x0_aux prima2_l2x0_aux __initconst = {
.val = 2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT,
.mask = 0,
};
-static struct l2x0_aux marco_l2x0_aux __initconst = {
+static const struct l2x0_aux marco_l2x0_aux __initconst = {
.val = (2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) |
(1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT),
.mask = L2X0_AUX_CTRL_MASK,
};
-static struct of_device_id sirf_l2x0_ids[] __initconst = {
+static const struct of_device_id sirf_l2x0_ids[] __initconst = {
{ .compatible = "sirf,prima2-pl310-cache", .data = &prima2_l2x0_aux, },
{ .compatible = "sirf,marco-pl310-cache", .data = &marco_l2x0_aux, },
{},
diff --git a/arch/arm/mach-prima2/platsmp.c b/arch/arm/mach-prima2/platsmp.c
index e358b0736dea..335c12e92262 100644
--- a/arch/arm/mach-prima2/platsmp.c
+++ b/arch/arm/mach-prima2/platsmp.c
@@ -138,9 +138,9 @@ static void __init sirfsoc_smp_prepare_cpus(unsigned int max_cpus)
}
struct smp_operations sirfsoc_smp_ops __initdata = {
- .smp_prepare_cpus = sirfsoc_smp_prepare_cpus,
- .smp_secondary_init = sirfsoc_secondary_init,
- .smp_boot_secondary = sirfsoc_boot_secondary,
+ .smp_prepare_cpus = sirfsoc_smp_prepare_cpus,
+ .smp_secondary_init = sirfsoc_secondary_init,
+ .smp_boot_secondary = sirfsoc_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = sirfsoc_cpu_die,
#endif
diff --git a/arch/arm/mach-prima2/rstc.c b/arch/arm/mach-prima2/rstc.c
index ccb53391147a..4887a2a4c698 100644
--- a/arch/arm/mach-prima2/rstc.c
+++ b/arch/arm/mach-prima2/rstc.c
@@ -13,57 +13,38 @@
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/platform_device.h>
#include <linux/reboot.h>
+#include <linux/reset-controller.h>
-void __iomem *sirfsoc_rstc_base;
-static DEFINE_MUTEX(rstc_lock);
-
-static struct of_device_id rstc_ids[] = {
- { .compatible = "sirf,prima2-rstc" },
- { .compatible = "sirf,marco-rstc" },
- {},
-};
+#include <asm/system_misc.h>
-static int __init sirfsoc_of_rstc_init(void)
-{
- struct device_node *np;
+#define SIRFSOC_RSTBIT_NUM 64
- np = of_find_matching_node(NULL, rstc_ids);
- if (!np) {
- pr_err("unable to find compatible sirf rstc node in dtb\n");
- return -ENOENT;
- }
-
- sirfsoc_rstc_base = of_iomap(np, 0);
- if (!sirfsoc_rstc_base)
- panic("unable to map rstc cpu registers\n");
-
- of_node_put(np);
-
- return 0;
-}
-early_initcall(sirfsoc_of_rstc_init);
+static void __iomem *sirfsoc_rstc_base;
+static DEFINE_MUTEX(rstc_lock);
-int sirfsoc_reset_device(struct device *dev)
+static int sirfsoc_reset_module(struct reset_controller_dev *rcdev,
+ unsigned long sw_reset_idx)
{
- u32 reset_bit;
+ u32 reset_bit = sw_reset_idx;
- if (of_property_read_u32(dev->of_node, "reset-bit", &reset_bit))
+ if (reset_bit >= SIRFSOC_RSTBIT_NUM)
return -EINVAL;
mutex_lock(&rstc_lock);
- if (of_device_is_compatible(dev->of_node, "sirf,prima2-rstc")) {
+ if (of_device_is_compatible(rcdev->of_node, "sirf,prima2-rstc")) {
/*
* Writing 1 to this bit resets corresponding block. Writing 0 to this
* bit de-asserts reset signal of the corresponding block.
* datasheet doesn't require explicit delay between the set and clear
* of reset bit. it could be shorter if tests pass.
*/
- writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) | reset_bit,
+ writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) | (1 << reset_bit),
sirfsoc_rstc_base + (reset_bit / 32) * 4);
msleep(10);
- writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) & ~reset_bit,
+ writel(readl(sirfsoc_rstc_base + (reset_bit / 32) * 4) & ~(1 << reset_bit),
sirfsoc_rstc_base + (reset_bit / 32) * 4);
} else {
/*
@@ -73,9 +54,9 @@ int sirfsoc_reset_device(struct device *dev)
* datasheet doesn't require explicit delay between the set and clear
* of reset bit. it could be shorter if tests pass.
*/
- writel(reset_bit, sirfsoc_rstc_base + (reset_bit / 32) * 8);
+ writel(1 << reset_bit, sirfsoc_rstc_base + (reset_bit / 32) * 8);
msleep(10);
- writel(reset_bit, sirfsoc_rstc_base + (reset_bit / 32) * 8 + 4);
+ writel(1 << reset_bit, sirfsoc_rstc_base + (reset_bit / 32) * 8 + 4);
}
mutex_unlock(&rstc_lock);
@@ -83,9 +64,57 @@ int sirfsoc_reset_device(struct device *dev)
return 0;
}
+static struct reset_control_ops sirfsoc_rstc_ops = {
+ .reset = sirfsoc_reset_module,
+};
+
+static struct reset_controller_dev sirfsoc_reset_controller = {
+ .ops = &sirfsoc_rstc_ops,
+ .nr_resets = SIRFSOC_RSTBIT_NUM,
+};
+
#define SIRFSOC_SYS_RST_BIT BIT(31)
-void sirfsoc_restart(enum reboot_mode mode, const char *cmd)
+static void sirfsoc_restart(enum reboot_mode mode, const char *cmd)
{
writel(SIRFSOC_SYS_RST_BIT, sirfsoc_rstc_base);
}
+
+static int sirfsoc_rstc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ sirfsoc_rstc_base = of_iomap(np, 0);
+ if (!sirfsoc_rstc_base) {
+ dev_err(&pdev->dev, "unable to map rstc cpu registers\n");
+ return -ENOMEM;
+ }
+
+ sirfsoc_reset_controller.of_node = np;
+ arm_pm_restart = sirfsoc_restart;
+
+ if (IS_ENABLED(CONFIG_RESET_CONTROLLER))
+ reset_controller_register(&sirfsoc_reset_controller);
+
+ return 0;
+}
+
+static const struct of_device_id rstc_ids[] = {
+ { .compatible = "sirf,prima2-rstc" },
+ { .compatible = "sirf,marco-rstc" },
+ {},
+};
+
+static struct platform_driver sirfsoc_rstc_driver = {
+ .probe = sirfsoc_rstc_probe,
+ .driver = {
+ .name = "sirfsoc_rstc",
+ .owner = THIS_MODULE,
+ .of_match_table = rstc_ids,
+ },
+};
+
+static int __init sirfsoc_rstc_init(void)
+{
+ return platform_driver_register(&sirfsoc_rstc_driver);
+}
+subsys_initcall(sirfsoc_rstc_init);
diff --git a/arch/arm/mach-prima2/rtciobrg.c b/arch/arm/mach-prima2/rtciobrg.c
index 9f2da2eec4dc..a17c88b74fa1 100644
--- a/arch/arm/mach-prima2/rtciobrg.c
+++ b/arch/arm/mach-prima2/rtciobrg.c
@@ -137,4 +137,4 @@ postcore_initcall(sirfsoc_rtciobrg_init);
MODULE_AUTHOR("Zhiwu Song <zhiwu.song@csr.com>, "
"Barry Song <baohua.song@csr.com>");
MODULE_DESCRIPTION("CSR SiRFprimaII rtc io bridge");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index 96100dbf5a2e..e6690a44917d 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -7,7 +7,6 @@ comment "Intel/Marvell Dev Platforms (sorted by hardware release time)"
config MACH_PXA3XX_DT
bool "Support PXA3xx platforms from device tree"
select CPU_PXA300
- select HAVE_PWM
select POWER_SUPPLY
select PXA3xx
select USE_OF
@@ -23,12 +22,10 @@ config ARCH_LUBBOCK
config MACH_MAINSTONE
bool "Intel HCDDBBVA0 Development Platform (aka Mainstone)"
- select HAVE_PWM
select PXA27x
config MACH_ZYLONITE
bool
- select HAVE_PWM
select PXA3xx
config MACH_ZYLONITE300
@@ -53,12 +50,16 @@ config MACH_TAVOREVB
select CPU_PXA930
select CPU_PXA935
select PXA3xx
+ select FB
+ select FB_PXA
config MACH_SAAR
bool "PXA930 Handheld Platform (aka SAAR)"
select CPU_PXA930
select CPU_PXA935
select PXA3xx
+ select FB
+ select FB_PXA
comment "Third Party Dev Platforms (sorted by vendor name)"
@@ -69,8 +70,7 @@ config ARCH_PXA_IDP
config ARCH_VIPER
bool "Arcom/Eurotech VIPER SBC"
select ARCOM_PCMCIA
- select HAVE_PWM
- select I2C_GPIO
+ select I2C_GPIO if I2C=y
select ISA
select PXA25x
select PXA_HAVE_ISA_IRQS
@@ -120,7 +120,6 @@ config MACH_CM_X300
bool "CompuLab CM-X300 modules"
select CPU_PXA300
select CPU_PXA310
- select HAVE_PWM
select PXA3xx
config MACH_CAPC7117
@@ -164,7 +163,6 @@ config MACH_XCEP
select MTD_CFI_INTELEXT
select MTD_PHYSMAP
select PXA25x
- select SMC91X
help
PXA255 based Single Board Computer with SMC 91C111 ethernet chip and 64 MB of flash.
Tuned for usage in Libera instruments for particle accelerators.
@@ -181,6 +179,7 @@ config MACH_TRIZEPS4
config MACH_TRIZEPS4WL
bool "Keith und Koep Trizeps4-WL DIMM-Module"
depends on TRIZEPS_PXA
+ select MACH_TRIZEPS4
select PXA27x
select TRIZEPS_PCMCIA
@@ -211,7 +210,6 @@ config TRIZEPS_PCMCIA
config MACH_LOGICPD_PXA270
bool "LogicPD PXA270 Card Engine Development Platform"
- select HAVE_PWM
select PXA27x
config MACH_PCM027
@@ -222,7 +220,6 @@ config MACH_PCM027
config MACH_PCM990_BASEBOARD
bool "PHYTEC PCM-990 development board"
depends on MACH_PCM027
- select HAVE_PWM
choice
prompt "display on pcm990"
@@ -246,7 +243,6 @@ config MACH_COLIBRI
config MACH_COLIBRI_PXA270_INCOME
bool "Income s.r.o. PXA270 SBC"
depends on MACH_COLIBRI
- select HAVE_PWM
select PXA27x
config MACH_COLIBRI300
@@ -275,7 +271,6 @@ comment "End-user Products (sorted by vendor name)"
config MACH_H4700
bool "HP iPAQ hx4700"
- select HAVE_PWM
select IWMMXT
select PXA27x
@@ -289,14 +284,12 @@ config MACH_HIMALAYA
config MACH_MAGICIAN
bool "Enable HTC Magician Support"
- select HAVE_PWM
select IWMMXT
select PXA27x
config MACH_MIOA701
bool "Mitac Mio A701 Support"
select GPIO_SYSFS
- select HAVE_PWM
select IWMMXT
select PXA27x
help
@@ -306,7 +299,6 @@ config MACH_MIOA701
config PXA_EZX
bool "Motorola EZX Platform"
- select HAVE_PWM
select IWMMXT
select PXA27x
@@ -346,7 +338,6 @@ config MACH_MP900C
config ARCH_PXA_PALM
bool "PXA based Palm PDAs"
- select HAVE_PWM
config MACH_PALM27X
bool
@@ -444,7 +435,6 @@ config MACH_TREO680
config MACH_RAUMFELD_RC
bool "Raumfeld Controller"
select CPU_PXA300
- select HAVE_PWM
select POWER_SUPPLY
select PXA3xx
@@ -608,7 +598,6 @@ config MACH_E800
config MACH_ZIPIT2
bool "Zipit Z2 Handheld"
- select HAVE_PWM
select PXA27x
endmenu
diff --git a/arch/arm/mach-pxa/balloon3.c b/arch/arm/mach-pxa/balloon3.c
index 2f71b3fbd319..43596e0ed051 100644
--- a/arch/arm/mach-pxa/balloon3.c
+++ b/arch/arm/mach-pxa/balloon3.c
@@ -331,7 +331,6 @@ static struct pxa2xx_udc_mach_info balloon3_udc_info __initdata = {
static void __init balloon3_udc_init(void)
{
pxa_set_udc_info(&balloon3_udc_info);
- platform_device_register(&balloon3_gpio_vbus);
}
#else
static inline void balloon3_udc_init(void) {}
diff --git a/arch/arm/mach-pxa/colibri-evalboard.c b/arch/arm/mach-pxa/colibri-evalboard.c
index 8404b24240ea..638b0bb88426 100644
--- a/arch/arm/mach-pxa/colibri-evalboard.c
+++ b/arch/arm/mach-pxa/colibri-evalboard.c
@@ -20,6 +20,7 @@
#include <asm/mach/arch.h>
#include <linux/i2c.h>
#include <linux/i2c/pxa-i2c.h>
+#include <asm/io.h>
#include <mach/pxa27x.h>
#include <mach/colibri.h>
diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c
index f162f1b77cd2..57d60542f982 100644
--- a/arch/arm/mach-pxa/corgi.c
+++ b/arch/arm/mach-pxa/corgi.c
@@ -32,6 +32,7 @@
#include <linux/spi/pxa2xx_spi.h>
#include <linux/mtd/sharpsl.h>
#include <linux/input/matrix_keypad.h>
+#include <linux/gpio_keys.h>
#include <linux/module.h>
#include <video/w100fb.h>
@@ -405,6 +406,44 @@ static struct platform_device corgikbd_device = {
},
};
+static struct gpio_keys_button corgi_gpio_keys[] = {
+ {
+ .type = EV_SW,
+ .code = SW_LID,
+ .gpio = CORGI_GPIO_SWA,
+ .desc = "Lid close switch",
+ .debounce_interval = 500,
+ },
+ {
+ .type = EV_SW,
+ .code = SW_TABLET_MODE,
+ .gpio = CORGI_GPIO_SWB,
+ .desc = "Tablet mode switch",
+ .debounce_interval = 500,
+ },
+ {
+ .type = EV_SW,
+ .code = SW_HEADPHONE_INSERT,
+ .gpio = CORGI_GPIO_AK_INT,
+ .desc = "HeadPhone insert",
+ .debounce_interval = 500,
+ },
+};
+
+static struct gpio_keys_platform_data corgi_gpio_keys_platform_data = {
+ .buttons = corgi_gpio_keys,
+ .nbuttons = ARRAY_SIZE(corgi_gpio_keys),
+ .poll_interval = 250,
+};
+
+static struct platform_device corgi_gpio_keys_device = {
+ .name = "gpio-keys-polled",
+ .id = -1,
+ .dev = {
+ .platform_data = &corgi_gpio_keys_platform_data,
+ },
+};
+
/*
* Corgi LEDs
*/
@@ -646,6 +685,7 @@ static struct platform_device sharpsl_rom_device = {
static struct platform_device *devices[] __initdata = {
&corgiscoop_device,
&corgifb_device,
+ &corgi_gpio_keys_device,
&corgikbd_device,
&corgiled_device,
&corgi_audio_device,
diff --git a/arch/arm/mach-pxa/include/mach/timex.h b/arch/arm/mach-pxa/include/mach/timex.h
deleted file mode 100644
index af6760a50e1a..000000000000
--- a/arch/arm/mach-pxa/include/mach/timex.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * arch/arm/mach-pxa/include/mach/timex.h
- *
- * Author: Nicolas Pitre
- * Created: Jun 15, 2001
- * Copyright: MontaVista Software 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.
- */
-
-/* Various drivers are still using the constant of CLOCK_TICK_RATE, for
- * those drivers to at least work, the definition is provided here.
- *
- * NOTE: this is no longer accurate when multiple processors and boards
- * are selected, newer drivers should not depend on this any more. Use
- * either the clocksource/clockevent or get this at run-time by calling
- * get_clock_tick_rate() (as defined in generic.c).
- */
-
-#if defined(CONFIG_PXA25x)
-/* PXA250/210 timer base */
-#define CLOCK_TICK_RATE 3686400
-#elif defined(CONFIG_PXA27x)
-/* PXA27x timer base */
-#ifdef CONFIG_MACH_MAINSTONE
-#define CLOCK_TICK_RATE 3249600
-#else
-#define CLOCK_TICK_RATE 3250000
-#endif
-#else
-#define CLOCK_TICK_RATE 3250000
-#endif
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
new file mode 100644
index 000000000000..a028be234334
--- /dev/null
+++ b/arch/arm/mach-qcom/Kconfig
@@ -0,0 +1,33 @@
+config ARCH_QCOM
+ bool "Qualcomm Support" if ARCH_MULTI_V7
+ select ARCH_REQUIRE_GPIOLIB
+ select ARM_GIC
+ select CLKSRC_OF
+ select GENERIC_CLOCKEVENTS
+ select HAVE_SMP
+ select QCOM_SCM if SMP
+ help
+ Support for Qualcomm's devicetree based systems.
+
+if ARCH_QCOM
+
+menu "Qualcomm SoC Selection"
+
+config ARCH_MSM8X60
+ bool "Enable support for MSM8X60"
+ select CLKSRC_QCOM
+
+config ARCH_MSM8960
+ bool "Enable support for MSM8960"
+ select CLKSRC_QCOM
+
+config ARCH_MSM8974
+ bool "Enable support for MSM8974"
+ select HAVE_ARM_ARCH_TIMER
+
+endmenu
+
+config QCOM_SCM
+ bool
+
+endif
diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
new file mode 100644
index 000000000000..8f756ae1ae31
--- /dev/null
+++ b/arch/arm/mach-qcom/Makefile
@@ -0,0 +1,5 @@
+obj-y := board.o
+obj-$(CONFIG_SMP) += platsmp.o
+obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
+
+CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
diff --git a/include/linux/mfd/pm8xxx/pm8921.h b/arch/arm/mach-qcom/board.c
index 00fa3de7659d..bae617ef0b31 100644
--- a/include/linux/mfd/pm8xxx/pm8921.h
+++ b/arch/arm/mach-qcom/board.c
@@ -1,5 +1,4 @@
-/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -10,21 +9,18 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-/*
- * Qualcomm PMIC 8921 driver header file
- *
- */
-
-#ifndef __MFD_PM8921_H
-#define __MFD_PM8921_H
-#include <linux/mfd/pm8xxx/irq.h>
+#include <linux/init.h>
-#define PM8921_NR_IRQS 256
+#include <asm/mach/arch.h>
-struct pm8921_platform_data {
- int irq_base;
- struct pm8xxx_irq_platform_data *irq_pdata;
+static const char * const qcom_dt_match[] __initconst = {
+ "qcom,msm8660-surf",
+ "qcom,msm8960-cdp",
+ "qcom,apq8074-dragonboard",
+ NULL
};
-#endif
+DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)")
+ .dt_compat = qcom_dt_match,
+MACHINE_END
diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c
new file mode 100644
index 000000000000..d6908569ecaf
--- /dev/null
+++ b/arch/arm/mach-qcom/platsmp.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2002 ARM Ltd.
+ * All Rights Reserved
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 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 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+
+#include <asm/smp_plat.h>
+
+#include "scm-boot.h"
+
+#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0
+#define SCSS_CPU1CORE_RESET 0x2d80
+#define SCSS_DBG_STATUS_CORE_PWRDUP 0x2e64
+
+#define APCS_CPU_PWR_CTL 0x04
+#define PLL_CLAMP BIT(8)
+#define CORE_PWRD_UP BIT(7)
+#define COREPOR_RST BIT(5)
+#define CORE_RST BIT(4)
+#define L2DT_SLP BIT(3)
+#define CLAMP BIT(0)
+
+#define APC_PWR_GATE_CTL 0x14
+#define BHS_CNT_SHIFT 24
+#define LDO_PWR_DWN_SHIFT 16
+#define LDO_BYP_SHIFT 8
+#define BHS_SEG_SHIFT 1
+#define BHS_EN BIT(0)
+
+#define APCS_SAW2_VCTL 0x14
+#define APCS_SAW2_2_VCTL 0x1c
+
+extern void secondary_startup(void);
+
+static DEFINE_SPINLOCK(boot_lock);
+
+#ifdef CONFIG_HOTPLUG_CPU
+static void __ref qcom_cpu_die(unsigned int cpu)
+{
+ wfi();
+}
+#endif
+
+static void qcom_secondary_init(unsigned int cpu)
+{
+ /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+}
+
+static int scss_release_secondary(unsigned int cpu)
+{
+ struct device_node *node;
+ void __iomem *base;
+
+ node = of_find_compatible_node(NULL, NULL, "qcom,gcc-msm8660");
+ if (!node) {
+ pr_err("%s: can't find node\n", __func__);
+ return -ENXIO;
+ }
+
+ base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!base)
+ return -ENOMEM;
+
+ writel_relaxed(0, base + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
+ writel_relaxed(0, base + SCSS_CPU1CORE_RESET);
+ writel_relaxed(3, base + SCSS_DBG_STATUS_CORE_PWRDUP);
+ mb();
+ iounmap(base);
+
+ return 0;
+}
+
+static int kpssv1_release_secondary(unsigned int cpu)
+{
+ int ret = 0;
+ void __iomem *reg, *saw_reg;
+ struct device_node *cpu_node, *acc_node, *saw_node;
+ u32 val;
+
+ cpu_node = of_get_cpu_node(cpu, NULL);
+ if (!cpu_node)
+ return -ENODEV;
+
+ acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
+ if (!acc_node) {
+ ret = -ENODEV;
+ goto out_acc;
+ }
+
+ saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
+ if (!saw_node) {
+ ret = -ENODEV;
+ goto out_saw;
+ }
+
+ reg = of_iomap(acc_node, 0);
+ if (!reg) {
+ ret = -ENOMEM;
+ goto out_acc_map;
+ }
+
+ saw_reg = of_iomap(saw_node, 0);
+ if (!saw_reg) {
+ ret = -ENOMEM;
+ goto out_saw_map;
+ }
+
+ /* Turn on CPU rail */
+ writel_relaxed(0xA4, saw_reg + APCS_SAW2_VCTL);
+ mb();
+ udelay(512);
+
+ /* Krait bring-up sequence */
+ val = PLL_CLAMP | L2DT_SLP | CLAMP;
+ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
+ val &= ~L2DT_SLP;
+ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
+ mb();
+ ndelay(300);
+
+ val |= COREPOR_RST;
+ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
+ mb();
+ udelay(2);
+
+ val &= ~CLAMP;
+ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
+ mb();
+ udelay(2);
+
+ val &= ~COREPOR_RST;
+ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
+ mb();
+ udelay(100);
+
+ val |= CORE_PWRD_UP;
+ writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
+ mb();
+
+ iounmap(saw_reg);
+out_saw_map:
+ iounmap(reg);
+out_acc_map:
+ of_node_put(saw_node);
+out_saw:
+ of_node_put(acc_node);
+out_acc:
+ of_node_put(cpu_node);
+ return ret;
+}
+
+static int kpssv2_release_secondary(unsigned int cpu)
+{
+ void __iomem *reg;
+ struct device_node *cpu_node, *l2_node, *acc_node, *saw_node;
+ void __iomem *l2_saw_base;
+ unsigned reg_val;
+ int ret;
+
+ cpu_node = of_get_cpu_node(cpu, NULL);
+ if (!cpu_node)
+ return -ENODEV;
+
+ acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
+ if (!acc_node) {
+ ret = -ENODEV;
+ goto out_acc;
+ }
+
+ l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0);
+ if (!l2_node) {
+ ret = -ENODEV;
+ goto out_l2;
+ }
+
+ saw_node = of_parse_phandle(l2_node, "qcom,saw", 0);
+ if (!saw_node) {
+ ret = -ENODEV;
+ goto out_saw;
+ }
+
+ reg = of_iomap(acc_node, 0);
+ if (!reg) {
+ ret = -ENOMEM;
+ goto out_map;
+ }
+
+ l2_saw_base = of_iomap(saw_node, 0);
+ if (!l2_saw_base) {
+ ret = -ENOMEM;
+ goto out_saw_map;
+ }
+
+ /* Turn on the BHS, turn off LDO Bypass and power down LDO */
+ reg_val = (64 << BHS_CNT_SHIFT) | (0x3f << LDO_PWR_DWN_SHIFT) | BHS_EN;
+ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
+ mb();
+ /* wait for the BHS to settle */
+ udelay(1);
+
+ /* Turn on BHS segments */
+ reg_val |= 0x3f << BHS_SEG_SHIFT;
+ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
+ mb();
+ /* wait for the BHS to settle */
+ udelay(1);
+
+ /* Finally turn on the bypass so that BHS supplies power */
+ reg_val |= 0x3f << LDO_BYP_SHIFT;
+ writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
+
+ /* enable max phases */
+ writel_relaxed(0x10003, l2_saw_base + APCS_SAW2_2_VCTL);
+ mb();
+ udelay(50);
+
+ reg_val = COREPOR_RST | CLAMP;
+ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
+ mb();
+ udelay(2);
+
+ reg_val &= ~CLAMP;
+ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
+ mb();
+ udelay(2);
+
+ reg_val &= ~COREPOR_RST;
+ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
+ mb();
+
+ reg_val |= CORE_PWRD_UP;
+ writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
+ mb();
+
+ ret = 0;
+
+ iounmap(l2_saw_base);
+out_saw_map:
+ iounmap(reg);
+out_map:
+ of_node_put(saw_node);
+out_saw:
+ of_node_put(l2_node);
+out_l2:
+ of_node_put(acc_node);
+out_acc:
+ of_node_put(cpu_node);
+
+ return ret;
+}
+
+static DEFINE_PER_CPU(int, cold_boot_done);
+
+static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int))
+{
+ int ret = 0;
+
+ if (!per_cpu(cold_boot_done, cpu)) {
+ ret = func(cpu);
+ if (!ret)
+ per_cpu(cold_boot_done, cpu) = true;
+ }
+
+ /*
+ * set synchronisation state between this boot processor
+ * and the secondary one
+ */
+ spin_lock(&boot_lock);
+
+ /*
+ * Send the secondary CPU a soft interrupt, thereby causing
+ * the boot monitor to read the system wide flags register,
+ * and branch to the address found there.
+ */
+ arch_send_wakeup_ipi_mask(cpumask_of(cpu));
+
+ /*
+ * now the secondary core is starting up let it run its
+ * calibrations, then wait for it to finish
+ */
+ spin_unlock(&boot_lock);
+
+ return ret;
+}
+
+static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ return qcom_boot_secondary(cpu, scss_release_secondary);
+}
+
+static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ return qcom_boot_secondary(cpu, kpssv1_release_secondary);
+}
+
+static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ return qcom_boot_secondary(cpu, kpssv2_release_secondary);
+}
+
+static void __init qcom_smp_prepare_cpus(unsigned int max_cpus)
+{
+ int cpu, map;
+ unsigned int flags = 0;
+ static const int cold_boot_flags[] = {
+ 0,
+ SCM_FLAG_COLDBOOT_CPU1,
+ SCM_FLAG_COLDBOOT_CPU2,
+ SCM_FLAG_COLDBOOT_CPU3,
+ };
+
+ for_each_present_cpu(cpu) {
+ map = cpu_logical_map(cpu);
+ if (WARN_ON(map >= ARRAY_SIZE(cold_boot_flags))) {
+ set_cpu_present(cpu, false);
+ continue;
+ }
+ flags |= cold_boot_flags[map];
+ }
+
+ if (scm_set_boot_addr(virt_to_phys(secondary_startup), flags)) {
+ for_each_present_cpu(cpu) {
+ if (cpu == smp_processor_id())
+ continue;
+ set_cpu_present(cpu, false);
+ }
+ pr_warn("Failed to set CPU boot address, disabling SMP\n");
+ }
+}
+
+static struct smp_operations smp_msm8660_ops __initdata = {
+ .smp_prepare_cpus = qcom_smp_prepare_cpus,
+ .smp_secondary_init = qcom_secondary_init,
+ .smp_boot_secondary = msm8660_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = qcom_cpu_die,
+#endif
+};
+CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops);
+
+static struct smp_operations qcom_smp_kpssv1_ops __initdata = {
+ .smp_prepare_cpus = qcom_smp_prepare_cpus,
+ .smp_secondary_init = qcom_secondary_init,
+ .smp_boot_secondary = kpssv1_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = qcom_cpu_die,
+#endif
+};
+CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops);
+
+static struct smp_operations qcom_smp_kpssv2_ops __initdata = {
+ .smp_prepare_cpus = qcom_smp_prepare_cpus,
+ .smp_secondary_init = qcom_secondary_init,
+ .smp_boot_secondary = kpssv2_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = qcom_cpu_die,
+#endif
+};
+CPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops);
diff --git a/arch/arm/mach-msm/scm-boot.c b/arch/arm/mach-qcom/scm-boot.c
index 45cee3e469a5..45cee3e469a5 100644
--- a/arch/arm/mach-msm/scm-boot.c
+++ b/arch/arm/mach-qcom/scm-boot.c
diff --git a/arch/arm/mach-msm/scm-boot.h b/arch/arm/mach-qcom/scm-boot.h
index 7be32ff5d687..6aabb2428176 100644
--- a/arch/arm/mach-msm/scm-boot.h
+++ b/arch/arm/mach-qcom/scm-boot.h
@@ -13,9 +13,11 @@
#define __MACH_SCM_BOOT_H
#define SCM_BOOT_ADDR 0x1
-#define SCM_FLAG_COLDBOOT_CPU1 0x1
-#define SCM_FLAG_WARMBOOT_CPU1 0x2
-#define SCM_FLAG_WARMBOOT_CPU0 0x4
+#define SCM_FLAG_COLDBOOT_CPU1 0x01
+#define SCM_FLAG_COLDBOOT_CPU2 0x08
+#define SCM_FLAG_COLDBOOT_CPU3 0x20
+#define SCM_FLAG_WARMBOOT_CPU0 0x04
+#define SCM_FLAG_WARMBOOT_CPU1 0x02
int scm_set_boot_addr(phys_addr_t addr, int flags);
diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-qcom/scm.c
index c536fd6bf827..c536fd6bf827 100644
--- a/arch/arm/mach-msm/scm.c
+++ b/arch/arm/mach-qcom/scm.c
diff --git a/arch/arm/mach-msm/scm.h b/arch/arm/mach-qcom/scm.h
index 00b31ea58f29..00b31ea58f29 100644
--- a/arch/arm/mach-msm/scm.h
+++ b/arch/arm/mach-qcom/scm.h
diff --git a/arch/arm/mach-realview/include/mach/memory.h b/arch/arm/mach-realview/include/mach/memory.h
index 2022e092f0ca..db09170e3832 100644
--- a/arch/arm/mach-realview/include/mach/memory.h
+++ b/arch/arm/mach-realview/include/mach/memory.h
@@ -56,6 +56,8 @@
#define PAGE_OFFSET1 (PAGE_OFFSET + 0x10000000)
#define PAGE_OFFSET2 (PAGE_OFFSET + 0x30000000)
+#define PHYS_OFFSET PLAT_PHYS_OFFSET
+
#define __phys_to_virt(phys) \
((phys) >= 0x80000000 ? (phys) - 0x80000000 + PAGE_OFFSET2 : \
(phys) >= 0x20000000 ? (phys) - 0x20000000 + PAGE_OFFSET1 : \
diff --git a/arch/arm/mach-realview/include/mach/timex.h b/arch/arm/mach-realview/include/mach/timex.h
deleted file mode 100644
index 4eeb069373c2..000000000000
--- a/arch/arm/mach-realview/include/mach/timex.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * arch/arm/mach-realview/include/mach/timex.h
- *
- * RealView architecture timex specifications
- *
- * Copyright (C) 2003 ARM 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.
- *
- * 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
- */
-
-#define CLOCK_TICK_RATE (50000000 / 16)
diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
index cf073dea5784..1caee6d548b8 100644
--- a/arch/arm/mach-rockchip/Kconfig
+++ b/arch/arm/mach-rockchip/Kconfig
@@ -5,10 +5,8 @@ config ARCH_ROCKCHIP
select ARCH_REQUIRE_GPIOLIB
select ARM_GIC
select CACHE_L2X0
+ select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
- select HAVE_SMP
- select COMMON_CLK
- select GENERIC_CLOCKEVENTS
select DW_APB_TIMER_OF
select ARM_GLOBAL_TIMER
select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
index 1547d4fc920a..4377a1436a98 100644
--- a/arch/arm/mach-rockchip/Makefile
+++ b/arch/arm/mach-rockchip/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o
+obj-$(CONFIG_SMP) += headsmp.o platsmp.o
diff --git a/arch/arm/mach-lpc32xx/include/mach/timex.h b/arch/arm/mach-rockchip/core.h
index 8d4066b16b3f..e2e7c9dbb200 100644
--- a/arch/arm/mach-lpc32xx/include/mach/timex.h
+++ b/arch/arm/mach-rockchip/core.h
@@ -1,9 +1,6 @@
/*
- * arch/arm/mach-lpc32xx/include/mach/timex.h
- *
- * Author: Kevin Wells <kevin.wells@nxp.com>
- *
- * Copyright (C) 2010 NXP Semiconductors
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,13 +13,10 @@
* GNU General Public License for more details.
*/
-#ifndef __ASM_ARCH_TIMEX_H
-#define __ASM_ARCH_TIMEX_H
+extern char rockchip_secondary_trampoline;
+extern char rockchip_secondary_trampoline_end;
-/*
- * Rate in Hz of the main system oscillator. This value should match
- * the value 'MAIN_OSC_FREQ' in platform.h
- */
-#define CLOCK_TICK_RATE 13000000
+extern unsigned long rockchip_boot_fn;
+extern void rockchip_secondary_startup(void);
-#endif
+extern struct smp_operations rockchip_smp_ops;
diff --git a/arch/arm/mach-integrator/include/mach/timex.h b/arch/arm/mach-rockchip/headsmp.S
index 1dcb42028c82..73206e360e31 100644
--- a/arch/arm/mach-integrator/include/mach/timex.h
+++ b/arch/arm/mach-rockchip/headsmp.S
@@ -1,9 +1,6 @@
/*
- * arch/arm/mach-integrator/include/mach/timex.h
- *
- * Integrator architecture timex specifications
- *
- * Copyright (C) 1999 ARM Limited
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,13 +11,20 @@
* 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/linkage.h>
+#include <linux/init.h>
-/*
- * ??
- */
-#define CLOCK_TICK_RATE (50000000 / 16)
+ENTRY(rockchip_secondary_startup)
+ bl v7_invalidate_l1
+ b secondary_startup
+ENDPROC(rockchip_secondary_startup)
+
+ENTRY(rockchip_secondary_trampoline)
+ ldr pc, 1f
+ENDPROC(rockchip_secondary_trampoline)
+ .globl rockchip_boot_fn
+rockchip_boot_fn:
+1: .space 4
+
+ENTRY(rockchip_secondary_trampoline_end)
diff --git a/arch/arm/mach-rockchip/platsmp.c b/arch/arm/mach-rockchip/platsmp.c
new file mode 100644
index 000000000000..dbfa5a26cfff
--- /dev/null
+++ b/arch/arm/mach-rockchip/platsmp.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/init.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_scu.h>
+#include <asm/smp_plat.h>
+#include <asm/mach/map.h>
+
+#include "core.h"
+
+static void __iomem *scu_base_addr;
+static void __iomem *sram_base_addr;
+static int ncores;
+
+#define PMU_PWRDN_CON 0x08
+#define PMU_PWRDN_ST 0x0c
+
+#define PMU_PWRDN_SCU 4
+
+static void __iomem *pmu_base_addr;
+
+static inline bool pmu_power_domain_is_on(int pd)
+{
+ return !(readl_relaxed(pmu_base_addr + PMU_PWRDN_ST) & BIT(pd));
+}
+
+static void pmu_set_power_domain(int pd, bool on)
+{
+ u32 val = readl_relaxed(pmu_base_addr + PMU_PWRDN_CON);
+ if (on)
+ val &= ~BIT(pd);
+ else
+ val |= BIT(pd);
+ writel(val, pmu_base_addr + PMU_PWRDN_CON);
+
+ while (pmu_power_domain_is_on(pd) != on) { }
+}
+
+/*
+ * Handling of CPU cores
+ */
+
+static int __cpuinit rockchip_boot_secondary(unsigned int cpu,
+ struct task_struct *idle)
+{
+ if (!sram_base_addr || !pmu_base_addr) {
+ pr_err("%s: sram or pmu missing for cpu boot\n", __func__);
+ return -ENXIO;
+ }
+
+ if (cpu >= ncores) {
+ pr_err("%s: cpu %d outside maximum number of cpus %d\n",
+ __func__, cpu, ncores);
+ return -ENXIO;
+ }
+
+ /* start the core */
+ pmu_set_power_domain(0 + cpu, true);
+
+ return 0;
+}
+
+/**
+ * rockchip_smp_prepare_sram - populate necessary sram block
+ * Starting cores execute the code residing at the start of the on-chip sram
+ * after power-on. Therefore make sure, this sram region is reserved and
+ * big enough. After this check, copy the trampoline code that directs the
+ * core to the real startup code in ram into the sram-region.
+ * @node: mmio-sram device node
+ */
+static int __init rockchip_smp_prepare_sram(struct device_node *node)
+{
+ unsigned int trampoline_sz = &rockchip_secondary_trampoline_end -
+ &rockchip_secondary_trampoline;
+ struct resource res;
+ unsigned int rsize;
+ int ret;
+
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret < 0) {
+ pr_err("%s: could not get address for node %s\n",
+ __func__, node->full_name);
+ return ret;
+ }
+
+ rsize = resource_size(&res);
+ if (rsize < trampoline_sz) {
+ pr_err("%s: reserved block with size 0x%x is to small for trampoline size 0x%x\n",
+ __func__, rsize, trampoline_sz);
+ return -EINVAL;
+ }
+
+ sram_base_addr = of_iomap(node, 0);
+
+ /* set the boot function for the sram code */
+ rockchip_boot_fn = virt_to_phys(rockchip_secondary_startup);
+
+ /* copy the trampoline to sram, that runs during startup of the core */
+ memcpy(sram_base_addr, &rockchip_secondary_trampoline, trampoline_sz);
+ flush_cache_all();
+ outer_clean_range(0, trampoline_sz);
+
+ dsb_sev();
+
+ return 0;
+}
+
+static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus)
+{
+ struct device_node *node;
+ unsigned int i;
+
+ node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
+ if (!node) {
+ pr_err("%s: missing scu\n", __func__);
+ return;
+ }
+
+ scu_base_addr = of_iomap(node, 0);
+ if (!scu_base_addr) {
+ pr_err("%s: could not map scu registers\n", __func__);
+ return;
+ }
+
+ node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-smp-sram");
+ if (!node) {
+ pr_err("%s: could not find sram dt node\n", __func__);
+ return;
+ }
+
+ if (rockchip_smp_prepare_sram(node))
+ return;
+
+ node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-pmu");
+ if (!node) {
+ pr_err("%s: could not find sram dt node\n", __func__);
+ return;
+ }
+
+ pmu_base_addr = of_iomap(node, 0);
+ if (!pmu_base_addr) {
+ pr_err("%s: could not map pmu registers\n", __func__);
+ return;
+ }
+
+ /* enable the SCU power domain */
+ pmu_set_power_domain(PMU_PWRDN_SCU, true);
+
+ /*
+ * While the number of cpus is gathered from dt, also get the number
+ * of cores from the scu to verify this value when booting the cores.
+ */
+ ncores = scu_get_core_count(scu_base_addr);
+
+ scu_enable(scu_base_addr);
+
+ /* Make sure that all cores except the first are really off */
+ for (i = 1; i < ncores; i++)
+ pmu_set_power_domain(0 + i, false);
+}
+
+struct smp_operations rockchip_smp_ops __initdata = {
+ .smp_prepare_cpus = rockchip_smp_prepare_cpus,
+ .smp_boot_secondary = rockchip_boot_secondary,
+};
diff --git a/arch/arm/mach-rockchip/rockchip.c b/arch/arm/mach-rockchip/rockchip.c
index 82c0b0709712..d211d6fa0d98 100644
--- a/arch/arm/mach-rockchip/rockchip.c
+++ b/arch/arm/mach-rockchip/rockchip.c
@@ -22,6 +22,7 @@
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/hardware/cache-l2x0.h>
+#include "core.h"
static void __init rockchip_dt_init(void)
{
@@ -38,6 +39,7 @@ static const char * const rockchip_board_dt_compat[] = {
};
DT_MACHINE_START(ROCKCHIP_DT, "Rockchip Cortex-A9 (Device Tree)")
+ .smp = smp_ops(rockchip_smp_ops),
.init_machine = rockchip_dt_init,
.dt_compat = rockchip_board_dt_compat,
MACHINE_END
diff --git a/arch/arm/mach-rpc/dma.c b/arch/arm/mach-rpc/dma.c
index 85883b2e0e49..6d3517dc4772 100644
--- a/arch/arm/mach-rpc/dma.c
+++ b/arch/arm/mach-rpc/dma.c
@@ -141,7 +141,7 @@ static int iomd_request_dma(unsigned int chan, dma_t *dma)
struct iomd_dma *idma = container_of(dma, struct iomd_dma, dma);
return request_irq(idma->irq, iomd_dma_handle,
- IRQF_DISABLED, idma->dma.device_id, idma);
+ 0, idma->dma.device_id, idma);
}
static void iomd_free_dma(unsigned int chan, dma_t *dma)
diff --git a/arch/arm/mach-rpc/include/mach/timex.h b/arch/arm/mach-rpc/include/mach/timex.h
deleted file mode 100644
index dd75e7387bbe..000000000000
--- a/arch/arm/mach-rpc/include/mach/timex.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * arch/arm/mach-rpc/include/mach/timex.h
- *
- * Copyright (C) 1997, 1998 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * RiscPC architecture timex specifications
- */
-
-/*
- * On the RiscPC, the clock ticks at 2MHz.
- */
-#define CLOCK_TICK_RATE 2000000
-
diff --git a/arch/arm/mach-rpc/time.c b/arch/arm/mach-rpc/time.c
index 9a6def14df01..2689771c1d38 100644
--- a/arch/arm/mach-rpc/time.c
+++ b/arch/arm/mach-rpc/time.c
@@ -24,6 +24,9 @@
#include <asm/mach/time.h>
+#define RPC_CLOCK_FREQ 2000000
+#define RPC_LATCH DIV_ROUND_CLOSEST(RPC_CLOCK_FREQ, HZ)
+
static u32 ioc_timer_gettimeoffset(void)
{
unsigned int count1, count2, status;
@@ -46,23 +49,23 @@ static u32 ioc_timer_gettimeoffset(void)
* and count2.
*/
if (status & (1 << 5))
- offset -= LATCH;
+ offset -= RPC_LATCH;
} else if (count2 > count1) {
/*
* We have just had another interrupt between reading
* count1 and count2.
*/
- offset -= LATCH;
+ offset -= RPC_LATCH;
}
- offset = (LATCH - offset) * (tick_nsec / 1000);
- return ((offset + LATCH/2) / LATCH) * 1000;
+ offset = (RPC_LATCH - offset) * (tick_nsec / 1000);
+ return DIV_ROUND_CLOSEST(offset, RPC_LATCH) * 1000;
}
void __init ioctime_init(void)
{
- ioc_writeb(LATCH & 255, IOC_T0LTCHL);
- ioc_writeb(LATCH >> 8, IOC_T0LTCHH);
+ ioc_writeb(RPC_LATCH & 255, IOC_T0LTCHL);
+ ioc_writeb(RPC_LATCH >> 8, IOC_T0LTCHH);
ioc_writeb(0, IOC_T0GO);
}
@@ -75,7 +78,6 @@ ioc_timer_interrupt(int irq, void *dev_id)
static struct irqaction ioc_timer_irq = {
.name = "timer",
- .flags = IRQF_DISABLED,
.handler = ioc_timer_interrupt
};
diff --git a/arch/arm/mach-s3c24xx/Kconfig b/arch/arm/mach-s3c24xx/Kconfig
index d876431d64c0..ba1cc6246778 100644
--- a/arch/arm/mach-s3c24xx/Kconfig
+++ b/arch/arm/mach-s3c24xx/Kconfig
@@ -521,7 +521,6 @@ config MACH_ANUBIS
select HAVE_PATA_PLATFORM
select S3C2440_XTAL_12000000
select S3C24XX_DCLK
- select S3C24XX_GPIO_EXTRA64
select S3C24XX_SIMTEC_PM if PM
select S3C_DEV_USB_HOST
help
@@ -537,7 +536,7 @@ config MACH_AT2440EVB
config MACH_MINI2440
bool "MINI2440 development board"
- select EEPROM_AT24
+ select EEPROM_AT24 if I2C
select LEDS_CLASS
select LEDS_TRIGGERS
select LEDS_TRIGGER_BACKLIGHT
@@ -562,7 +561,6 @@ config MACH_OSIRIS
select S3C2410_IOTIMING if ARM_S3C2440_CPUFREQ
select S3C2440_XTAL_12000000
select S3C24XX_DCLK
- select S3C24XX_GPIO_EXTRA128
select S3C24XX_SIMTEC_PM if PM
select S3C_DEV_NAND
select S3C_DEV_USB_HOST
@@ -573,7 +571,7 @@ config MACH_OSIRIS
config MACH_OSIRIS_DVS
tristate "Simtec IM2440D20 (OSIRIS) Dynamic Voltage Scaling driver"
depends on MACH_OSIRIS
- select TPS65010
+ depends on TPS65010
help
Say Y/M here if you want to have dynamic voltage scaling support
on the Simtec IM2440D20 (OSIRIS) module via the TPS65011.
diff --git a/arch/arm/mach-s3c24xx/clock-s3c2410.c b/arch/arm/mach-s3c24xx/clock-s3c2410.c
index d39d3c787580..d1afcf9252d1 100644
--- a/arch/arm/mach-s3c24xx/clock-s3c2410.c
+++ b/arch/arm/mach-s3c24xx/clock-s3c2410.c
@@ -30,13 +30,12 @@
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/io.h>
#include <asm/mach/map.h>
#include <mach/hardware.h>
-
-#include <plat/regs-serial.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
diff --git a/arch/arm/mach-s3c24xx/clock-s3c2412.c b/arch/arm/mach-s3c24xx/clock-s3c2412.c
index 11b3b28457bb..192a5b2550b0 100644
--- a/arch/arm/mach-s3c24xx/clock-s3c2412.c
+++ b/arch/arm/mach-s3c24xx/clock-s3c2412.c
@@ -31,13 +31,12 @@
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/io.h>
#include <asm/mach/map.h>
#include <mach/hardware.h>
-
-#include <plat/regs-serial.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
diff --git a/arch/arm/mach-s3c24xx/clock-s3c2440.c b/arch/arm/mach-s3c24xx/clock-s3c2440.c
index aaf006d1d6dc..5527226fd61f 100644
--- a/arch/arm/mach-s3c24xx/clock-s3c2440.c
+++ b/arch/arm/mach-s3c24xx/clock-s3c2440.c
@@ -34,6 +34,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <mach/hardware.h>
#include <linux/atomic.h>
@@ -43,7 +44,6 @@
#include <plat/clock.h>
#include <plat/cpu.h>
-#include <plat/regs-serial.h>
/* S3C2440 extended clock support */
diff --git a/arch/arm/mach-s3c24xx/common.c b/arch/arm/mach-s3c24xx/common.c
index 4adaa4b43ffe..1bc8e73c94f9 100644
--- a/arch/arm/mach-s3c24xx/common.c
+++ b/arch/arm/mach-s3c24xx/common.c
@@ -27,6 +27,7 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <clocksource/samsung_pwm.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
@@ -44,7 +45,6 @@
#include <asm/mach/map.h>
#include <mach/regs-gpio.h>
-#include <plat/regs-serial.h>
#include <mach/dma.h>
#include <plat/cpu.h>
@@ -240,7 +240,6 @@ void __init s3c24xx_init_io(struct map_desc *mach_desc, int size)
} else {
samsung_cpu_id = s3c24xx_read_idcode_v4();
}
- s3c24xx_init_cpu();
s3c_init_cpu(samsung_cpu_id, cpu_ids, ARRAY_SIZE(cpu_ids));
@@ -484,7 +483,7 @@ struct platform_device s3c2440_device_dma = {
};
#endif
-#if defined(CONFIG_CPUS_3C2443) || defined(CONFIG_CPU_S3C2416)
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2416)
static struct resource s3c2443_dma_resource[] = {
[0] = DEFINE_RES_MEM(S3C24XX_PA_DMA, S3C24XX_SZ_DMA),
[1] = DEFINE_RES_IRQ(IRQ_S3C2443_DMA0),
diff --git a/arch/arm/mach-s3c24xx/dma-s3c2410.c b/arch/arm/mach-s3c24xx/dma-s3c2410.c
index 30aa53ff07a6..09aa12da1789 100644
--- a/arch/arm/mach-s3c24xx/dma-s3c2410.c
+++ b/arch/arm/mach-s3c24xx/dma-s3c2410.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <mach/map.h>
#include <mach/dma.h>
@@ -23,7 +24,6 @@
#include <plat/cpu.h>
#include <plat/dma-s3c24xx.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <plat/regs-dma.h>
#include <mach/regs-lcd.h>
diff --git a/arch/arm/mach-s3c24xx/dma-s3c2412.c b/arch/arm/mach-s3c24xx/dma-s3c2412.c
index b7e094671522..0c0106d1a4d1 100644
--- a/arch/arm/mach-s3c24xx/dma-s3c2412.c
+++ b/arch/arm/mach-s3c24xx/dma-s3c2412.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/io.h>
#include <mach/dma.h>
@@ -23,7 +24,6 @@
#include <plat/dma-s3c24xx.h>
#include <plat/cpu.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <plat/regs-dma.h>
#include <mach/regs-lcd.h>
diff --git a/arch/arm/mach-s3c24xx/dma-s3c2440.c b/arch/arm/mach-s3c24xx/dma-s3c2440.c
index cd25de28804c..2f8e8a3017df 100644
--- a/arch/arm/mach-s3c24xx/dma-s3c2440.c
+++ b/arch/arm/mach-s3c24xx/dma-s3c2440.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <mach/map.h>
#include <mach/dma.h>
@@ -23,7 +24,6 @@
#include <plat/dma-s3c24xx.h>
#include <plat/cpu.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <plat/regs-dma.h>
#include <mach/regs-lcd.h>
diff --git a/arch/arm/mach-s3c24xx/dma-s3c2443.c b/arch/arm/mach-s3c24xx/dma-s3c2443.c
index 95b9f759fe97..f4096ec0700a 100644
--- a/arch/arm/mach-s3c24xx/dma-s3c2443.c
+++ b/arch/arm/mach-s3c24xx/dma-s3c2443.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/io.h>
#include <mach/dma.h>
@@ -23,7 +24,6 @@
#include <plat/dma-s3c24xx.h>
#include <plat/cpu.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <plat/regs-dma.h>
#include <mach/regs-lcd.h>
diff --git a/arch/arm/mach-s3c24xx/include/mach/debug-macro.S b/arch/arm/mach-s3c24xx/include/mach/debug-macro.S
index 2558952e3147..2f39737544c0 100644
--- a/arch/arm/mach-s3c24xx/include/mach/debug-macro.S
+++ b/arch/arm/mach-s3c24xx/include/mach/debug-macro.S
@@ -14,7 +14,7 @@
#include <mach/map.h>
#include <mach/regs-gpio.h>
-#include <plat/regs-serial.h>
+#include <linux/serial_s3c.h>
#define S3C2410_UART1_OFF (0x4000)
#define SHIFT_2440TXF (14-9)
diff --git a/arch/arm/mach-s3c24xx/include/mach/hardware.h b/arch/arm/mach-s3c24xx/include/mach/hardware.h
index a6cc14a092fc..dedd3837c193 100644
--- a/arch/arm/mach-s3c24xx/include/mach/hardware.h
+++ b/arch/arm/mach-s3c24xx/include/mach/hardware.h
@@ -1,5 +1,4 @@
-/* arch/arm/mach-s3c2410/include/mach/hardware.h
- *
+/*
* Copyright (c) 2003 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
@@ -17,20 +16,9 @@
extern unsigned int s3c2410_modify_misccr(unsigned int clr, unsigned int chg);
-#ifdef CONFIG_CPU_S3C2440
-
-extern int s3c2440_set_dsc(unsigned int pin, unsigned int value);
-
-#endif /* CONFIG_CPU_S3C2440 */
-
#endif /* __ASSEMBLY__ */
#include <asm/sizes.h>
#include <mach/map.h>
-/* machine specific hardware definitions should go after this */
-
-/* currently here until moved into config (todo) */
-#define CONFIG_NO_MULTIWORD_IO
-
#endif /* __ASM_ARCH_HARDWARE_H */
diff --git a/arch/arm/plat-samsung/include/plat/rtc-core.h b/arch/arm/mach-s3c24xx/include/mach/rtc-core.h
index 7b542f7b7938..4d5f5768f700 100644
--- a/arch/arm/plat-samsung/include/plat/rtc-core.h
+++ b/arch/arm/mach-s3c24xx/include/mach/rtc-core.h
@@ -1,5 +1,4 @@
-/* linux/arch/arm/plat-samsung/include/plat/rtc-core.h
- *
+/*
* Copyright (c) 2011 Heiko Stuebner <heiko@sntech.de>
*
* Samsung RTC Controller core functions
@@ -9,19 +8,19 @@
* published by the Free Software Foundation.
*/
-#ifndef __ASM_PLAT_RTC_CORE_H
-#define __ASM_PLAT_RTC_CORE_H __FILE__
+#ifndef __RTC_CORE_H
+#define __RTC_CORE_H __FILE__
/* These functions are only for use with the core support code, such as
* the cpu specific initialisation code
*/
+extern struct platform_device s3c_device_rtc;
+
/* re-define device name depending on support. */
static inline void s3c_rtc_setname(char *name)
{
-#if defined(CONFIG_S3C_DEV_RTC) || defined(CONFIG_PLAT_S3C24XX)
s3c_device_rtc.name = name;
-#endif
}
-#endif /* __ASM_PLAT_RTC_CORE_H */
+#endif /* __RTC_CORE_H */
diff --git a/arch/arm/mach-s3c24xx/include/mach/tick.h b/arch/arm/mach-s3c24xx/include/mach/tick.h
deleted file mode 100644
index 544da41979db..000000000000
--- a/arch/arm/mach-s3c24xx/include/mach/tick.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* linux/arch/arm/mach-s3c2410/include/mach/tick.h
- *
- * Copyright 2008 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- * http://armlinux.simtec.co.uk/
- *
- * S3C2410 - timer tick support
- */
-
-#define SRCPND_TIMER4 (1<<(IRQ_TIMER4 - IRQ_EINT0))
-
-static inline int s3c24xx_ostimer_pending(void)
-{
- return __raw_readl(S3C2410_SRCPND) & SRCPND_TIMER4;
-}
diff --git a/arch/arm/mach-s3c24xx/include/mach/timex.h b/arch/arm/mach-s3c24xx/include/mach/timex.h
deleted file mode 100644
index fe9ca1ffd51b..000000000000
--- a/arch/arm/mach-s3c24xx/include/mach/timex.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* arch/arm/mach-s3c2410/include/mach/timex.h
- *
- * Copyright (c) 2003-2005 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C2410 - time parameters
- *
- * 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_ARCH_TIMEX_H
-#define __ASM_ARCH_TIMEX_H
-
-/* CLOCK_TICK_RATE needs to be evaluatable by the cpp, so making it
- * a variable is useless. It seems as long as we make our timers an
- * exact multiple of HZ, any value that makes a 1->1 correspondence
- * for the time conversion functions to/from jiffies is acceptable.
-*/
-
-#define CLOCK_TICK_RATE 12000000
-
-#endif /* __ASM_ARCH_TIMEX_H */
diff --git a/arch/arm/mach-s3c24xx/include/mach/uncompress.h b/arch/arm/mach-s3c24xx/include/mach/uncompress.h
deleted file mode 100644
index 7d2ce205dce8..000000000000
--- a/arch/arm/mach-s3c24xx/include/mach/uncompress.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* arch/arm/mach-s3c2410/include/mach/uncompress.h
- *
- * Copyright (c) 2003-2007 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C2410 - uncompress code
- *
- * 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_ARCH_UNCOMPRESS_H
-#define __ASM_ARCH_UNCOMPRESS_H
-
-#include <mach/regs-gpio.h>
-#include <mach/map.h>
-
-/* working in physical space... */
-#undef S3C2410_GPIOREG
-#define S3C2410_GPIOREG(x) ((S3C24XX_PA_GPIO + (x)))
-
-#include <plat/uncompress.h>
-
-static inline int is_arm926(void)
-{
- unsigned int cpuid;
-
- asm volatile ("mrc p15, 0, %0, c1, c0, 0" : "=r" (cpuid));
-
- return ((cpuid & 0xff0) == 0x260);
-}
-
-static void arch_detect_cpu(void)
-{
- unsigned int cpuid;
-
- cpuid = *((volatile unsigned int *)S3C2410_GSTATUS1);
- cpuid &= S3C2410_GSTATUS1_IDMASK;
-
- if (is_arm926() || cpuid == S3C2410_GSTATUS1_2440 ||
- cpuid == S3C2410_GSTATUS1_2442 ||
- cpuid == S3C2410_GSTATUS1_2416 ||
- cpuid == S3C2410_GSTATUS1_2450) {
- fifo_mask = S3C2440_UFSTAT_TXMASK;
- fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT;
- } else {
- fifo_mask = S3C2410_UFSTAT_TXMASK;
- fifo_max = 15 << S3C2410_UFSTAT_TXSHIFT;
- }
-
- uart_base = (volatile u8 *) S3C_PA_UART +
- (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT);
-}
-
-#endif /* __ASM_ARCH_UNCOMPRESS_H */
diff --git a/arch/arm/mach-s3c24xx/mach-amlm5900.c b/arch/arm/mach-s3c24xx/mach-amlm5900.c
index 284ea1f44205..8ac9554aa996 100644
--- a/arch/arm/mach-s3c24xx/mach-amlm5900.c
+++ b/arch/arm/mach-s3c24xx/mach-amlm5900.c
@@ -37,6 +37,7 @@
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/io.h>
#include <asm/mach/arch.h>
@@ -49,7 +50,6 @@
#include <asm/mach-types.h>
#include <mach/fb.h>
-#include <plat/regs-serial.h>
#include <mach/regs-lcd.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-samsung.h>
diff --git a/arch/arm/mach-s3c24xx/mach-anubis.c b/arch/arm/mach-s3c24xx/mach-anubis.c
index 2a16f8fb3584..81a270af2336 100644
--- a/arch/arm/mach-s3c24xx/mach-anubis.c
+++ b/arch/arm/mach-s3c24xx/mach-anubis.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/ata_platform.h>
#include <linux/i2c.h>
@@ -32,7 +33,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <mach/regs-lcd.h>
#include <mach/gpio-samsung.h>
diff --git a/arch/arm/mach-s3c24xx/mach-at2440evb.c b/arch/arm/mach-s3c24xx/mach-at2440evb.c
index 6beab674c147..d8f6bb1096cb 100644
--- a/arch/arm/mach-s3c24xx/mach-at2440evb.c
+++ b/arch/arm/mach-s3c24xx/mach-at2440evb.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/dm9000.h>
#include <linux/platform_device.h>
@@ -33,7 +34,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <mach/regs-lcd.h>
#include <mach/gpio-samsung.h>
diff --git a/arch/arm/mach-s3c24xx/mach-bast.c b/arch/arm/mach-s3c24xx/mach-bast.c
index 981ba1eb9fdc..e371ff53a408 100644
--- a/arch/arm/mach-s3c24xx/mach-bast.c
+++ b/arch/arm/mach-s3c24xx/mach-bast.c
@@ -19,6 +19,7 @@
#include <linux/gpio.h>
#include <linux/syscore_ops.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/dm9000.h>
#include <linux/ata_platform.h>
@@ -55,7 +56,6 @@
#include <plat/cpu-freq.h>
#include <plat/devs.h>
#include <plat/gpio-cfg.h>
-#include <plat/regs-serial.h>
#include <plat/samsung-time.h>
#include "bast.h"
diff --git a/arch/arm/mach-s3c24xx/mach-gta02.c b/arch/arm/mach-s3c24xx/mach-gta02.c
index d9170e9f8ccd..dc4db849f0fd 100644
--- a/arch/arm/mach-s3c24xx/mach-gta02.c
+++ b/arch/arm/mach-s3c24xx/mach-gta02.c
@@ -35,6 +35,7 @@
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/input.h>
#include <linux/io.h>
#include <linux/i2c.h>
@@ -81,7 +82,6 @@
#include <plat/devs.h>
#include <plat/gpio-cfg.h>
#include <plat/pm.h>
-#include <plat/regs-serial.h>
#include <plat/samsung-time.h>
#include "common.h"
@@ -196,7 +196,7 @@ static void gta02_charger_worker(struct work_struct *work)
* If the PCF50633 ADC is disabled we fallback to a
* 100mA limit for safety.
*/
- pcf50633_mbc_usb_curlim_set(pcf, 100);
+ pcf50633_mbc_usb_curlim_set(gta02_pcf, 100);
#endif
}
diff --git a/arch/arm/mach-s3c24xx/mach-h1940.c b/arch/arm/mach-s3c24xx/mach-h1940.c
index de0832181d8c..e453acd92cbf 100644
--- a/arch/arm/mach-s3c24xx/mach-h1940.c
+++ b/arch/arm/mach-s3c24xx/mach-h1940.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/gpio.h>
@@ -62,7 +63,6 @@
#include <plat/gpio-cfg.h>
#include <plat/pll.h>
#include <plat/pm.h>
-#include <plat/regs-serial.h>
#include <plat/samsung-time.h>
#include "common.h"
diff --git a/arch/arm/mach-s3c24xx/mach-jive.c b/arch/arm/mach-s3c24xx/mach-jive.c
index 67cb8e948b7e..5faa7239e7d6 100644
--- a/arch/arm/mach-s3c24xx/mach-jive.c
+++ b/arch/arm/mach-s3c24xx/mach-jive.c
@@ -19,6 +19,7 @@
#include <linux/gpio.h>
#include <linux/syscore_ops.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
@@ -31,7 +32,6 @@
#include <asm/mach/map.h>
#include <asm/mach/irq.h>
-#include <plat/regs-serial.h>
#include <linux/platform_data/mtd-nand-s3c2410.h>
#include <linux/platform_data/i2c-s3c2410.h>
diff --git a/arch/arm/mach-s3c24xx/mach-mini2440.c b/arch/arm/mach-s3c24xx/mach-mini2440.c
index 1f1559713d8b..9e57fd9f4f3b 100644
--- a/arch/arm/mach-s3c24xx/mach-mini2440.c
+++ b/arch/arm/mach-s3c24xx/mach-mini2440.c
@@ -23,6 +23,7 @@
#include <linux/input.h>
#include <linux/io.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/dm9000.h>
#include <linux/platform_data/at24.h>
#include <linux/platform_device.h>
@@ -37,7 +38,6 @@
#include <mach/fb.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <linux/platform_data/leds-s3c24xx.h>
#include <mach/regs-lcd.h>
diff --git a/arch/arm/mach-s3c24xx/mach-n30.c b/arch/arm/mach-s3c24xx/mach-n30.c
index 997684f17930..4cccaad34847 100644
--- a/arch/arm/mach-s3c24xx/mach-n30.c
+++ b/arch/arm/mach-s3c24xx/mach-n30.c
@@ -24,6 +24,7 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/timer.h>
#include <linux/io.h>
#include <linux/mmc/host.h>
@@ -43,7 +44,6 @@
#include <asm/mach/map.h>
#include <linux/platform_data/i2c-s3c2410.h>
-#include <plat/regs-serial.h>
#include <plat/clock.h>
#include <plat/cpu.h>
diff --git a/arch/arm/mach-s3c24xx/mach-nexcoder.c b/arch/arm/mach-s3c24xx/mach-nexcoder.c
index 575d28c9e6c6..3066851f584d 100644
--- a/arch/arm/mach-s3c24xx/mach-nexcoder.c
+++ b/arch/arm/mach-s3c24xx/mach-nexcoder.c
@@ -21,6 +21,7 @@
#include <linux/gpio.h>
#include <linux/string.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -38,7 +39,6 @@
//#include <asm/debug-ll.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-samsung.h>
-#include <plat/regs-serial.h>
#include <linux/platform_data/i2c-s3c2410.h>
#include <plat/gpio-cfg.h>
diff --git a/arch/arm/mach-s3c24xx/mach-osiris.c b/arch/arm/mach-s3c24xx/mach-osiris.c
index f84f2a4c0c6d..a4ae4bb3666d 100644
--- a/arch/arm/mach-s3c24xx/mach-osiris.c
+++ b/arch/arm/mach-s3c24xx/mach-osiris.c
@@ -18,6 +18,7 @@
#include <linux/device.h>
#include <linux/syscore_ops.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/io.h>
@@ -44,7 +45,6 @@
#include <plat/cpu-freq.h>
#include <plat/devs.h>
#include <plat/gpio-cfg.h>
-#include <plat/regs-serial.h>
#include <plat/samsung-time.h>
#include <mach/hardware.h>
diff --git a/arch/arm/mach-s3c24xx/mach-otom.c b/arch/arm/mach-s3c24xx/mach-otom.c
index 7e16b0740ec1..bdb3faac2d9b 100644
--- a/arch/arm/mach-s3c24xx/mach-otom.c
+++ b/arch/arm/mach-s3c24xx/mach-otom.c
@@ -15,6 +15,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -32,7 +33,6 @@
#include <plat/clock.h>
#include <plat/cpu.h>
#include <plat/devs.h>
-#include <plat/regs-serial.h>
#include <plat/samsung-time.h>
#include "common.h"
diff --git a/arch/arm/mach-s3c24xx/mach-qt2410.c b/arch/arm/mach-s3c24xx/mach-qt2410.c
index b534b76812e3..8c12787a8fd3 100644
--- a/arch/arm/mach-s3c24xx/mach-qt2410.c
+++ b/arch/arm/mach-s3c24xx/mach-qt2410.c
@@ -31,6 +31,7 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_gpio.h>
#include <linux/io.h>
@@ -49,7 +50,6 @@
#include <linux/platform_data/leds-s3c24xx.h>
#include <mach/regs-lcd.h>
-#include <plat/regs-serial.h>
#include <mach/fb.h>
#include <linux/platform_data/mtd-nand-s3c2410.h>
#include <linux/platform_data/usb-s3c2410_udc.h>
diff --git a/arch/arm/mach-s3c24xx/mach-rx1950.c b/arch/arm/mach-s3c24xx/mach-rx1950.c
index 0a5456cda1bc..afb784e934c8 100644
--- a/arch/arm/mach-s3c24xx/mach-rx1950.c
+++ b/arch/arm/mach-s3c24xx/mach-rx1950.c
@@ -21,6 +21,7 @@
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/device.h>
@@ -57,7 +58,6 @@
#include <plat/cpu.h>
#include <plat/devs.h>
#include <plat/pm.h>
-#include <plat/regs-serial.h>
#include <plat/samsung-time.h>
#include <plat/gpio-cfg.h>
diff --git a/arch/arm/mach-s3c24xx/mach-rx3715.c b/arch/arm/mach-s3c24xx/mach-rx3715.c
index b36edce8b2b8..e6535ce1bc5c 100644
--- a/arch/arm/mach-s3c24xx/mach-rx3715.c
+++ b/arch/arm/mach-s3c24xx/mach-rx3715.c
@@ -23,6 +23,7 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/serial.h>
#include <linux/io.h>
#include <linux/mtd/mtd.h>
@@ -49,7 +50,6 @@
#include <plat/cpu.h>
#include <plat/devs.h>
#include <plat/pm.h>
-#include <plat/regs-serial.h>
#include <plat/samsung-time.h>
#include "common.h"
diff --git a/arch/arm/mach-s3c24xx/mach-s3c2416-dt.c b/arch/arm/mach-s3c24xx/mach-s3c2416-dt.c
index f50454a34f72..70f0900d4bca 100644
--- a/arch/arm/mach-s3c24xx/mach-s3c2416-dt.c
+++ b/arch/arm/mach-s3c24xx/mach-s3c2416-dt.c
@@ -19,13 +19,13 @@
#include <linux/irqchip.h>
#include <linux/of_platform.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <asm/mach/arch.h>
#include <mach/map.h>
#include <plat/cpu.h>
#include <plat/pm.h>
-#include <plat/regs-serial.h>
#include "common.h"
diff --git a/arch/arm/mach-s3c24xx/mach-smdk2410.c b/arch/arm/mach-s3c24xx/mach-smdk2410.c
index a773789e4f38..f32924ee0e9f 100644
--- a/arch/arm/mach-s3c24xx/mach-smdk2410.c
+++ b/arch/arm/mach-s3c24xx/mach-smdk2410.c
@@ -35,6 +35,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -46,7 +47,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <linux/platform_data/i2c-s3c2410.h>
#include <plat/devs.h>
diff --git a/arch/arm/mach-s3c24xx/mach-smdk2413.c b/arch/arm/mach-s3c24xx/mach-smdk2413.c
index f5bc721217e3..233fe52d2015 100644
--- a/arch/arm/mach-s3c24xx/mach-smdk2413.c
+++ b/arch/arm/mach-s3c24xx/mach-smdk2413.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -33,7 +34,6 @@
#include <asm/mach-types.h>
//#include <asm/debug-ll.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <mach/regs-lcd.h>
diff --git a/arch/arm/mach-s3c24xx/mach-smdk2416.c b/arch/arm/mach-s3c24xx/mach-smdk2416.c
index 12023cae4378..b3b54d8e1410 100644
--- a/arch/arm/mach-s3c24xx/mach-smdk2416.c
+++ b/arch/arm/mach-s3c24xx/mach-smdk2416.c
@@ -18,6 +18,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/mtd/partitions.h>
@@ -34,7 +35,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <mach/regs-lcd.h>
#include <mach/regs-s3c2443-clock.h>
diff --git a/arch/arm/mach-s3c24xx/mach-smdk2440.c b/arch/arm/mach-s3c24xx/mach-smdk2440.c
index de2e5d39a847..d071dcfea548 100644
--- a/arch/arm/mach-s3c24xx/mach-smdk2440.c
+++ b/arch/arm/mach-s3c24xx/mach-smdk2440.c
@@ -20,6 +20,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -31,7 +32,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <mach/regs-lcd.h>
diff --git a/arch/arm/mach-s3c24xx/mach-smdk2443.c b/arch/arm/mach-s3c24xx/mach-smdk2443.c
index d9933fcc6cc8..06c4d77de3a5 100644
--- a/arch/arm/mach-s3c24xx/mach-smdk2443.c
+++ b/arch/arm/mach-s3c24xx/mach-smdk2443.c
@@ -20,6 +20,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -31,7 +32,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <mach/regs-lcd.h>
diff --git a/arch/arm/mach-s3c24xx/mach-tct_hammer.c b/arch/arm/mach-s3c24xx/mach-tct_hammer.c
index 7fad8f055cab..4108b2f0cede 100644
--- a/arch/arm/mach-s3c24xx/mach-tct_hammer.c
+++ b/arch/arm/mach-s3c24xx/mach-tct_hammer.c
@@ -33,6 +33,7 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/io.h>
#include <asm/mach/arch.h>
@@ -44,7 +45,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <linux/platform_data/i2c-s3c2410.h>
#include <plat/devs.h>
#include <plat/cpu.h>
diff --git a/arch/arm/mach-s3c24xx/mach-vr1000.c b/arch/arm/mach-s3c24xx/mach-vr1000.c
index 755df489a45f..1cc5b1bd51cd 100644
--- a/arch/arm/mach-s3c24xx/mach-vr1000.c
+++ b/arch/arm/mach-s3c24xx/mach-vr1000.c
@@ -25,6 +25,7 @@
#include <linux/tty.h>
#include <linux/serial_8250.h>
#include <linux/serial_reg.h>
+#include <linux/serial_s3c.h>
#include <linux/io.h>
#include <asm/mach/arch.h>
@@ -45,7 +46,6 @@
#include <plat/clock.h>
#include <plat/cpu.h>
#include <plat/devs.h>
-#include <plat/regs-serial.h>
#include <plat/samsung-time.h>
#include "bast.h"
diff --git a/arch/arm/mach-s3c24xx/mach-vstms.c b/arch/arm/mach-s3c24xx/mach-vstms.c
index f7ec9c550787..40868c0e0a68 100644
--- a/arch/arm/mach-s3c24xx/mach-vstms.c
+++ b/arch/arm/mach-s3c24xx/mach-vstms.c
@@ -16,6 +16,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/mtd/mtd.h>
@@ -32,7 +33,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <mach/regs-lcd.h>
diff --git a/arch/arm/mach-s3c24xx/pm.c b/arch/arm/mach-s3c24xx/pm.c
index 052ca23393a7..68ea5b7e5dc7 100644
--- a/arch/arm/mach-s3c24xx/pm.c
+++ b/arch/arm/mach-s3c24xx/pm.c
@@ -33,9 +33,9 @@
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/io.h>
-#include <plat/regs-serial.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <mach/regs-irq.h>
diff --git a/arch/arm/mach-s3c24xx/s3c2410.c b/arch/arm/mach-s3c24xx/s3c2410.c
index ffb92cbca08c..04b58cb49888 100644
--- a/arch/arm/mach-s3c24xx/s3c2410.c
+++ b/arch/arm/mach-s3c24xx/s3c2410.c
@@ -21,6 +21,7 @@
#include <linux/device.h>
#include <linux/syscore_ops.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/io.h>
@@ -37,7 +38,6 @@
#include <plat/cpu-freq.h>
#include <mach/regs-clock.h>
-#include <plat/regs-serial.h>
#include <plat/cpu.h>
#include <plat/devs.h>
diff --git a/arch/arm/mach-s3c24xx/s3c2412.c b/arch/arm/mach-s3c24xx/s3c2412.c
index 0251650cbf80..657cbaca80ac 100644
--- a/arch/arm/mach-s3c24xx/s3c2412.c
+++ b/arch/arm/mach-s3c24xx/s3c2412.c
@@ -20,6 +20,7 @@
#include <linux/device.h>
#include <linux/syscore_ops.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/reboot.h>
@@ -43,7 +44,6 @@
#include <plat/nand-core.h>
#include <plat/pll.h>
#include <plat/pm.h>
-#include <plat/regs-serial.h>
#include <plat/regs-spi.h>
#include "common.h"
diff --git a/arch/arm/mach-s3c24xx/s3c2416.c b/arch/arm/mach-s3c24xx/s3c2416.c
index 8e01b4f2df35..9fe260ae11e1 100644
--- a/arch/arm/mach-s3c24xx/s3c2416.c
+++ b/arch/arm/mach-s3c24xx/s3c2416.c
@@ -48,6 +48,7 @@
#include <asm/system_misc.h>
#include <mach/regs-s3c2443-clock.h>
+#include <mach/rtc-core.h>
#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>
@@ -61,7 +62,6 @@
#include <plat/fb-core.h>
#include <plat/nand-core.h>
#include <plat/adc-core.h>
-#include <plat/rtc-core.h>
#include <plat/spi-core.h>
#include "common.h"
diff --git a/arch/arm/mach-s3c24xx/s3c2443.c b/arch/arm/mach-s3c24xx/s3c2443.c
index 886c2147062b..c7a804d0348e 100644
--- a/arch/arm/mach-s3c24xx/s3c2443.c
+++ b/arch/arm/mach-s3c24xx/s3c2443.c
@@ -34,6 +34,7 @@
#include <asm/system_misc.h>
#include <mach/regs-s3c2443-clock.h>
+#include <mach/rtc-core.h>
#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>
@@ -43,7 +44,6 @@
#include <plat/fb-core.h>
#include <plat/nand-core.h>
#include <plat/adc-core.h>
-#include <plat/rtc-core.h>
#include <plat/spi-core.h>
static struct map_desc s3c2443_iodesc[] __initdata = {
diff --git a/arch/arm/mach-s3c24xx/s3c244x.c b/arch/arm/mach-s3c24xx/s3c244x.c
index 911b555029fc..fe30ebb234d2 100644
--- a/arch/arm/mach-s3c24xx/s3c244x.c
+++ b/arch/arm/mach-s3c24xx/s3c244x.c
@@ -17,6 +17,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/device.h>
@@ -35,7 +36,6 @@
#include <plat/cpu-freq.h>
#include <mach/regs-clock.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <plat/clock.h>
diff --git a/arch/arm/mach-s3c24xx/sleep-s3c2410.S b/arch/arm/mach-s3c24xx/sleep-s3c2410.S
index dd47c8fa07fa..c9b91223697c 100644
--- a/arch/arm/mach-s3c24xx/sleep-s3c2410.S
+++ b/arch/arm/mach-s3c24xx/sleep-s3c2410.S
@@ -25,13 +25,13 @@
*/
#include <linux/linkage.h>
+#include <linux/serial_s3c.h>
#include <asm/assembler.h>
#include <mach/hardware.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
-#include <plat/regs-serial.h>
#include "regs-mem.h"
diff --git a/arch/arm/mach-s3c24xx/sleep.S b/arch/arm/mach-s3c24xx/sleep.S
index 7f378b662da6..d833d616bd2e 100644
--- a/arch/arm/mach-s3c24xx/sleep.S
+++ b/arch/arm/mach-s3c24xx/sleep.S
@@ -25,13 +25,13 @@
*/
#include <linux/linkage.h>
+#include <linux/serial_s3c.h>
#include <asm/assembler.h>
#include <mach/hardware.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
-#include <plat/regs-serial.h>
/* CONFIG_DEBUG_RESUME is dangerous if your bootloader does not
* reset the UART configuration, only enable if you really need this!
diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig
index 64f04e6f9c31..3136d86b0d6e 100644
--- a/arch/arm/mach-s3c64xx/Kconfig
+++ b/arch/arm/mach-s3c64xx/Kconfig
@@ -86,8 +86,7 @@ config MACH_SMDK6400
bool "SMDK6400"
select CPU_S3C6400
select S3C64XX_SETUP_SDHCI
- select S3C_DEV_HSMMC
- select S3C_DEV_NAND
+ select S3C_DEV_HSMMC1
help
Machine support for the Samsung SMDK6400
diff --git a/arch/arm/mach-s3c64xx/common.c b/arch/arm/mach-s3c64xx/common.c
index 76ab595d849b..5c45aae675b6 100644
--- a/arch/arm/mach-s3c64xx/common.c
+++ b/arch/arm/mach-s3c64xx/common.c
@@ -25,6 +25,7 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/io.h>
@@ -50,7 +51,6 @@
#include <plat/irq-uart.h>
#include <plat/pwm-core.h>
#include <plat/regs-irqtype.h>
-#include <plat/regs-serial.h>
#include <plat/watchdog-reset.h>
#include "common.h"
diff --git a/arch/arm/mach-s3c64xx/include/mach/debug-macro.S b/arch/arm/mach-s3c64xx/include/mach/debug-macro.S
index dd9ccca5de1f..c9b95325b672 100644
--- a/arch/arm/mach-s3c64xx/include/mach/debug-macro.S
+++ b/arch/arm/mach-s3c64xx/include/mach/debug-macro.S
@@ -12,8 +12,8 @@
/* pull in the relevant register and map files. */
+#include <linux/serial_s3c.h>
#include <mach/map.h>
-#include <plat/regs-serial.h>
/* note, for the boot process to work we have to keep the UART
* virtual address aligned to an 1MiB boundary for the L1
diff --git a/arch/arm/mach-s3c64xx/include/mach/pm-core.h b/arch/arm/mach-s3c64xx/include/mach/pm-core.h
index c0537f40a3d8..a30a1e3ffc6a 100644
--- a/arch/arm/mach-s3c64xx/include/mach/pm-core.h
+++ b/arch/arm/mach-s3c64xx/include/mach/pm-core.h
@@ -15,6 +15,8 @@
#ifndef __MACH_S3C64XX_PM_CORE_H
#define __MACH_S3C64XX_PM_CORE_H __FILE__
+#include <linux/serial_s3c.h>
+
#include <mach/regs-gpio.h>
static inline void s3c_pm_debug_init_uart(void)
diff --git a/arch/arm/mach-s3c64xx/include/mach/tick.h b/arch/arm/mach-s3c64xx/include/mach/tick.h
deleted file mode 100644
index db9c1b1d56a4..000000000000
--- a/arch/arm/mach-s3c64xx/include/mach/tick.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* linux/arch/arm/mach-s3c6400/include/mach/tick.h
- *
- * Copyright 2008 Openmoko, Inc.
- * Copyright 2008 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C64XX - Timer tick support definitions
- *
- * 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_ARCH_TICK_H
-#define __ASM_ARCH_TICK_H __FILE__
-
-#include <linux/irqchip/arm-vic.h>
-
-/* note, the timer interrutps turn up in 2 places, the vic and then
- * the timer block. We take the VIC as the base at the moment.
- */
-static inline u32 s3c24xx_ostimer_pending(void)
-{
- u32 pend = __raw_readl(VA_VIC0 + VIC_RAW_STATUS);
- return pend & 1 << (IRQ_TIMER4_VIC - S3C64XX_IRQ_VIC0(0));
-}
-
-#define TICK_MAX (0xffffffff)
-
-#endif /* __ASM_ARCH_6400_TICK_H */
diff --git a/arch/arm/mach-s3c64xx/include/mach/timex.h b/arch/arm/mach-s3c64xx/include/mach/timex.h
deleted file mode 100644
index fb2e8cd40829..000000000000
--- a/arch/arm/mach-s3c64xx/include/mach/timex.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* arch/arm/mach-s3c64xx/include/mach/timex.h
- *
- * Copyright (c) 2003-2005 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C6400 - time parameters
- *
- * 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_ARCH_TIMEX_H
-#define __ASM_ARCH_TIMEX_H
-
-/* CLOCK_TICK_RATE needs to be evaluatable by the cpp, so making it
- * a variable is useless. It seems as long as we make our timers an
- * exact multiple of HZ, any value that makes a 1->1 correspondence
- * for the time conversion functions to/from jiffies is acceptable.
-*/
-
-#define CLOCK_TICK_RATE 12000000
-
-#endif /* __ASM_ARCH_TIMEX_H */
diff --git a/arch/arm/mach-s3c64xx/include/mach/uncompress.h b/arch/arm/mach-s3c64xx/include/mach/uncompress.h
deleted file mode 100644
index 1c956738b42d..000000000000
--- a/arch/arm/mach-s3c64xx/include/mach/uncompress.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* arch/arm/mach-s3c6400/include/mach/uncompress.h
- *
- * Copyright 2008 Openmoko, Inc.
- * Copyright 2008 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C6400 - uncompress code
- *
- * 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_ARCH_UNCOMPRESS_H
-#define __ASM_ARCH_UNCOMPRESS_H
-
-#include <mach/map.h>
-#include <plat/uncompress.h>
-
-static void arch_detect_cpu(void)
-{
- /* we do not need to do any cpu detection here at the moment. */
- fifo_mask = S3C2440_UFSTAT_TXMASK;
- fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT;
-
- uart_base = (volatile u8 *)S3C_PA_UART +
- (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT);
-}
-
-#endif /* __ASM_ARCH_UNCOMPRESS_H */
diff --git a/arch/arm/mach-s3c64xx/irq-pm.c b/arch/arm/mach-s3c64xx/irq-pm.c
index 1649c0d1c1b8..ae4ea7601f60 100644
--- a/arch/arm/mach-s3c64xx/irq-pm.c
+++ b/arch/arm/mach-s3c64xx/irq-pm.c
@@ -20,13 +20,13 @@
#include <linux/syscore_ops.h>
#include <linux/interrupt.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/of.h>
#include <mach/map.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <plat/cpu.h>
#include <plat/pm.h>
@@ -55,7 +55,13 @@ static struct irq_grp_save {
u32 mask;
} eint_grp_save[5];
-static u32 irq_uart_mask[CONFIG_SERIAL_SAMSUNG_UARTS];
+#ifndef CONFIG_SERIAL_SAMSUNG_UARTS
+#define SERIAL_SAMSUNG_UARTS 0
+#else
+#define SERIAL_SAMSUNG_UARTS CONFIG_SERIAL_SAMSUNG_UARTS
+#endif
+
+static u32 irq_uart_mask[SERIAL_SAMSUNG_UARTS];
static int s3c64xx_irq_pm_suspend(void)
{
@@ -66,7 +72,7 @@ static int s3c64xx_irq_pm_suspend(void)
s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
- for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++)
+ for (i = 0; i < SERIAL_SAMSUNG_UARTS; i++)
irq_uart_mask[i] = __raw_readl(S3C_VA_UARTx(i) + S3C64XX_UINTM);
for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
@@ -87,7 +93,7 @@ static void s3c64xx_irq_pm_resume(void)
s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
- for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++)
+ for (i = 0; i < SERIAL_SAMSUNG_UARTS; i++)
__raw_writel(irq_uart_mask[i], S3C_VA_UARTx(i) + S3C64XX_UINTM);
for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
diff --git a/arch/arm/mach-s3c64xx/mach-anw6410.c b/arch/arm/mach-s3c64xx/mach-anw6410.c
index ddeb0e51a962..55eb6a69655b 100644
--- a/arch/arm/mach-s3c64xx/mach-anw6410.c
+++ b/arch/arm/mach-s3c64xx/mach-anw6410.c
@@ -20,6 +20,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/i2c.h>
@@ -41,7 +42,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <linux/platform_data/i2c-s3c2410.h>
#include <plat/fb.h>
diff --git a/arch/arm/mach-s3c64xx/mach-crag6410-module.c b/arch/arm/mach-s3c64xx/mach-crag6410-module.c
index 7ccfef227c77..9c00d83f7151 100644
--- a/arch/arm/mach-s3c64xx/mach-crag6410-module.c
+++ b/arch/arm/mach-s3c64xx/mach-crag6410-module.c
@@ -401,4 +401,4 @@ static int __init wlf_gf_module_register(void)
{
return i2c_add_driver(&wlf_gf_module_driver);
}
-module_init(wlf_gf_module_register);
+device_initcall(wlf_gf_module_register);
diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c
index 3df3c372ee1f..4b0199fff9f5 100644
--- a/arch/arm/mach-s3c64xx/mach-crag6410.c
+++ b/arch/arm/mach-s3c64xx/mach-crag6410.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/io.h>
@@ -51,7 +52,6 @@
#include <mach/regs-gpio.h>
#include <mach/gpio-samsung.h>
-#include <plat/regs-serial.h>
#include <plat/fb.h>
#include <plat/sdhci.h>
#include <plat/gpio-cfg.h>
diff --git a/arch/arm/mach-s3c64xx/mach-hmt.c b/arch/arm/mach-s3c64xx/mach-hmt.c
index 0431016925b9..72cee08c8bf5 100644
--- a/arch/arm/mach-s3c64xx/mach-hmt.c
+++ b/arch/arm/mach-s3c64xx/mach-hmt.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/i2c.h>
@@ -33,7 +34,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <linux/platform_data/i2c-s3c2410.h>
#include <mach/gpio-samsung.h>
#include <plat/fb.h>
diff --git a/arch/arm/mach-s3c64xx/mach-mini6410.c b/arch/arm/mach-s3c64xx/mach-mini6410.c
index 8d553a418e1c..9cbc07602ef3 100644
--- a/arch/arm/mach-s3c64xx/mach-mini6410.c
+++ b/arch/arm/mach-s3c64xx/mach-mini6410.c
@@ -22,6 +22,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/types.h>
#include <asm/mach-types.h>
@@ -38,7 +39,6 @@
#include <plat/fb.h>
#include <linux/platform_data/mtd-nand-s3c2410.h>
#include <linux/platform_data/mmc-sdhci-s3c.h>
-#include <plat/regs-serial.h>
#include <plat/sdhci.h>
#include <linux/platform_data/touchscreen-s3c2410.h>
diff --git a/arch/arm/mach-s3c64xx/mach-ncp.c b/arch/arm/mach-s3c64xx/mach-ncp.c
index 2067b0bf55b4..67f06a9ae656 100644
--- a/arch/arm/mach-s3c64xx/mach-ncp.c
+++ b/arch/arm/mach-s3c64xx/mach-ncp.c
@@ -16,6 +16,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/i2c.h>
@@ -36,7 +37,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <linux/platform_data/i2c-s3c2410.h>
#include <plat/fb.h>
diff --git a/arch/arm/mach-s3c64xx/mach-real6410.c b/arch/arm/mach-s3c64xx/mach-real6410.c
index 5152026f0e19..fbad2af1ef16 100644
--- a/arch/arm/mach-s3c64xx/mach-real6410.c
+++ b/arch/arm/mach-s3c64xx/mach-real6410.c
@@ -23,6 +23,7 @@
#include <linux/mtd/partitions.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/types.h>
#include <asm/mach-types.h>
@@ -38,7 +39,6 @@
#include <plat/devs.h>
#include <plat/fb.h>
#include <linux/platform_data/mtd-nand-s3c2410.h>
-#include <plat/regs-serial.h>
#include <linux/platform_data/touchscreen-s3c2410.h>
#include <video/platform_lcd.h>
diff --git a/arch/arm/mach-s3c64xx/mach-smartq.c b/arch/arm/mach-s3c64xx/mach-smartq.c
index 6e72bd5c1d0c..78dd6f73c072 100644
--- a/arch/arm/mach-s3c64xx/mach-smartq.c
+++ b/arch/arm/mach-s3c64xx/mach-smartq.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/pwm_backlight.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/spi/spi_gpio.h>
#include <linux/usb/gpio_vbus.h>
#include <linux/platform_data/s3c-hsotg.h>
@@ -33,7 +34,6 @@
#include <linux/platform_data/i2c-s3c2410.h>
#include <plat/gpio-cfg.h>
#include <linux/platform_data/hwmon-s3c.h>
-#include <plat/regs-serial.h>
#include <linux/platform_data/usb-ohci-s3c2410.h>
#include <plat/sdhci.h>
#include <linux/platform_data/touchscreen-s3c2410.h>
diff --git a/arch/arm/mach-s3c64xx/mach-smdk6400.c b/arch/arm/mach-s3c64xx/mach-smdk6400.c
index 150f55fb9e33..c85d1cbe769f 100644
--- a/arch/arm/mach-s3c64xx/mach-smdk6400.c
+++ b/arch/arm/mach-s3c64xx/mach-smdk6400.c
@@ -16,6 +16,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/io.h>
@@ -29,8 +30,6 @@
#include <mach/hardware.h>
#include <mach/map.h>
-#include <plat/regs-serial.h>
-
#include <plat/clock.h>
#include <plat/devs.h>
#include <plat/cpu.h>
diff --git a/arch/arm/mach-s3c64xx/mach-smdk6410.c b/arch/arm/mach-s3c64xx/mach-smdk6410.c
index 43261d24a0a5..c6a8b2ab0240 100644
--- a/arch/arm/mach-s3c64xx/mach-smdk6410.c
+++ b/arch/arm/mach-s3c64xx/mach-smdk6410.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/i2c.h>
@@ -55,7 +56,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-samsung.h>
#include <linux/platform_data/ata-samsung_cf.h>
diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c
index b5a66986a529..6b37694fa335 100644
--- a/arch/arm/mach-s3c64xx/pm.c
+++ b/arch/arm/mach-s3c64xx/pm.c
@@ -332,7 +332,6 @@ static __init int s3c64xx_pm_initcall(void)
{
pm_cpu_prep = s3c64xx_pm_prepare;
pm_cpu_sleep = s3c64xx_cpu_suspend;
- pm_uart_udivslot = 1;
#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
gpio_request(S3C64XX_GPN(12), "DEBUG_LED0");
diff --git a/arch/arm/mach-s3c64xx/s3c6400.c b/arch/arm/mach-s3c64xx/s3c6400.c
index 3db0c98222f7..8c42807bf579 100644
--- a/arch/arm/mach-s3c64xx/s3c6400.c
+++ b/arch/arm/mach-s3c64xx/s3c6400.c
@@ -23,6 +23,7 @@
#include <linux/io.h>
#include <linux/device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/of.h>
@@ -34,7 +35,6 @@
#include <asm/irq.h>
#include <plat/cpu-freq.h>
-#include <plat/regs-serial.h>
#include <mach/regs-clock.h>
#include <plat/cpu.h>
diff --git a/arch/arm/mach-s3c64xx/s3c6410.c b/arch/arm/mach-s3c64xx/s3c6410.c
index 72b2278953a8..5be3f09bac92 100644
--- a/arch/arm/mach-s3c64xx/s3c6410.c
+++ b/arch/arm/mach-s3c64xx/s3c6410.c
@@ -24,6 +24,7 @@
#include <linux/io.h>
#include <linux/device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/of.h>
@@ -35,7 +36,6 @@
#include <asm/irq.h>
#include <plat/cpu-freq.h>
-#include <plat/regs-serial.h>
#include <mach/regs-clock.h>
#include <plat/cpu.h>
diff --git a/arch/arm/mach-s5p64x0/common.c b/arch/arm/mach-s5p64x0/common.c
index 42e14f2e7ca7..9a43be002d78 100644
--- a/arch/arm/mach-s5p64x0/common.c
+++ b/arch/arm/mach-s5p64x0/common.c
@@ -19,6 +19,7 @@
#include <linux/io.h>
#include <linux/device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <clocksource/samsung_pwm.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
@@ -50,7 +51,6 @@
#include <plat/gpio-cfg.h>
#include <plat/pwm-core.h>
#include <plat/regs-irqtype.h>
-#include <plat/regs-serial.h>
#include <plat/watchdog-reset.h>
#include "common.h"
@@ -205,6 +205,7 @@ void __init s5p64x0_init_io(struct map_desc *mach_desc, int size)
samsung_pwm_set_platdata(&s5p64x0_pwm_variant);
}
+#ifdef CONFIG_CPU_S5P6440
void __init s5p6440_map_io(void)
{
/* initialize any device information early */
@@ -218,7 +219,9 @@ void __init s5p6440_map_io(void)
iotable_init(s5p6440_iodesc, ARRAY_SIZE(s5p6440_iodesc));
}
+#endif
+#ifdef CONFIG_CPU_S5P6450
void __init s5p6450_map_io(void)
{
/* initialize any device information early */
@@ -232,13 +235,14 @@ void __init s5p6450_map_io(void)
iotable_init(s5p6450_iodesc, ARRAY_SIZE(s5p6450_iodesc));
}
+#endif
/*
* s5p64x0_init_clocks
*
* register and setup the CPU clocks
*/
-
+#ifdef CONFIG_CPU_S5P6440
void __init s5p6440_init_clocks(int xtal)
{
printk(KERN_DEBUG "%s: initializing clocks\n", __func__);
@@ -248,7 +252,9 @@ void __init s5p6440_init_clocks(int xtal)
s5p6440_register_clocks();
s5p6440_setup_clocks();
}
+#endif
+#ifdef CONFIG_CPU_S5P6450
void __init s5p6450_init_clocks(int xtal)
{
printk(KERN_DEBUG "%s: initializing clocks\n", __func__);
@@ -258,13 +264,14 @@ void __init s5p6450_init_clocks(int xtal)
s5p6450_register_clocks();
s5p6450_setup_clocks();
}
+#endif
/*
* s5p64x0_init_irq
*
* register the CPU interrupts
*/
-
+#ifdef CONFIG_CPU_S5P6440
void __init s5p6440_init_irq(void)
{
/* S5P6440 supports 2 VIC */
@@ -279,7 +286,9 @@ void __init s5p6440_init_irq(void)
s5p_init_irq(vic, ARRAY_SIZE(vic));
}
+#endif
+#ifdef CONFIG_CPU_S5P6450
void __init s5p6450_init_irq(void)
{
/* S5P6450 supports only 2 VIC */
@@ -294,6 +303,7 @@ void __init s5p6450_init_irq(void)
s5p_init_irq(vic, ARRAY_SIZE(vic));
}
+#endif
struct bus_type s5p64x0_subsys = {
.name = "s5p64x0-core",
@@ -321,6 +331,7 @@ int __init s5p64x0_init(void)
}
/* uart registration process */
+#ifdef CONFIG_CPU_S5P6440
void __init s5p6440_init_uarts(struct s3c2410_uartcfg *cfg, int no)
{
int uart;
@@ -332,11 +343,14 @@ void __init s5p6440_init_uarts(struct s3c2410_uartcfg *cfg, int no)
s3c24xx_init_uartdevs("s3c6400-uart", s5p_uart_resources, cfg, no);
}
+#endif
+#ifdef CONFIG_CPU_S5P6450
void __init s5p6450_init_uarts(struct s3c2410_uartcfg *cfg, int no)
{
s3c24xx_init_uartdevs("s3c6400-uart", s5p_uart_resources, cfg, no);
}
+#endif
#define eint_offset(irq) ((irq) - IRQ_EINT(0))
diff --git a/arch/arm/mach-s5p64x0/common.h b/arch/arm/mach-s5p64x0/common.h
index f3a9b43cba4a..cbe7f3d731d0 100644
--- a/arch/arm/mach-s5p64x0/common.h
+++ b/arch/arm/mach-s5p64x0/common.h
@@ -25,10 +25,10 @@ void s5p6450_register_clocks(void);
void s5p6450_setup_clocks(void);
void s5p64x0_restart(enum reboot_mode mode, const char *cmd);
+extern int s5p64x0_init(void);
#ifdef CONFIG_CPU_S5P6440
-extern int s5p64x0_init(void);
extern void s5p6440_map_io(void);
extern void s5p6440_init_clocks(int xtal);
@@ -38,12 +38,10 @@ extern void s5p6440_init_uarts(struct s3c2410_uartcfg *cfg, int no);
#define s5p6440_init_clocks NULL
#define s5p6440_init_uarts NULL
#define s5p6440_map_io NULL
-#define s5p64x0_init NULL
#endif
#ifdef CONFIG_CPU_S5P6450
-extern int s5p64x0_init(void);
extern void s5p6450_map_io(void);
extern void s5p6450_init_clocks(int xtal);
@@ -53,7 +51,6 @@ extern void s5p6450_init_uarts(struct s3c2410_uartcfg *cfg, int no);
#define s5p6450_init_clocks NULL
#define s5p6450_init_uarts NULL
#define s5p6450_map_io NULL
-#define s5p64x0_init NULL
#endif
#endif /* __ARCH_ARM_MACH_S5P64X0_COMMON_H */
diff --git a/arch/arm/mach-s5p64x0/include/mach/debug-macro.S b/arch/arm/mach-s5p64x0/include/mach/debug-macro.S
index 5e2916fb19a9..8759e7882bcb 100644
--- a/arch/arm/mach-s5p64x0/include/mach/debug-macro.S
+++ b/arch/arm/mach-s5p64x0/include/mach/debug-macro.S
@@ -10,11 +10,10 @@
/* pull in the relevant register and map files. */
+#include <linux/serial_s3c.h>
#include <plat/map-base.h>
#include <plat/map-s5p.h>
-#include <plat/regs-serial.h>
-
.macro addruart, rp, rv, tmp
mov \rp, #0xE0000000
orr \rp, \rp, #0x00100000
diff --git a/arch/arm/mach-s5p64x0/include/mach/pm-core.h b/arch/arm/mach-s5p64x0/include/mach/pm-core.h
index e52f7545d3aa..1e0eb65b2b82 100644
--- a/arch/arm/mach-s5p64x0/include/mach/pm-core.h
+++ b/arch/arm/mach-s5p64x0/include/mach/pm-core.h
@@ -12,6 +12,8 @@
* published by the Free Software Foundation.
*/
+#include <linux/serial_s3c.h>
+
#include <mach/regs-gpio.h>
static inline void s3c_pm_debug_init_uart(void)
diff --git a/arch/arm/mach-s5p64x0/include/mach/timex.h b/arch/arm/mach-s5p64x0/include/mach/timex.h
deleted file mode 100644
index 4b91faa195a8..000000000000
--- a/arch/arm/mach-s5p64x0/include/mach/timex.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* linux/arch/arm/mach-s5p64x0/include/mach/timex.h
- *
- * Copyright (c) 2010 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Copyright (c) 2003-2005 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S5P64X0 - time parameters
- *
- * 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_ARCH_TIMEX_H
-#define __ASM_ARCH_TIMEX_H
-
-/* CLOCK_TICK_RATE needs to be evaluatable by the cpp, so making it
- * a variable is useless. It seems as long as we make our timers an
- * exact multiple of HZ, any value that makes a 1->1 correspondence
- * for the time conversion functions to/from jiffies is acceptable.
-*/
-
-#define CLOCK_TICK_RATE 12000000
-
-#endif /* __ASM_ARCH_TIMEX_H */
diff --git a/arch/arm/mach-s5p64x0/include/mach/uncompress.h b/arch/arm/mach-s5p64x0/include/mach/uncompress.h
deleted file mode 100644
index bbcc3f669ee3..000000000000
--- a/arch/arm/mach-s5p64x0/include/mach/uncompress.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* linux/arch/arm/mach-s5p64x0/include/mach/uncompress.h
- *
- * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * S5P64X0 - uncompress code
- *
- * 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_ARCH_UNCOMPRESS_H
-#define __ASM_ARCH_UNCOMPRESS_H
-
-#include <mach/map.h>
-#include <plat/uncompress.h>
-
-static void arch_detect_cpu(void)
-{
- unsigned int chipid;
-
- chipid = *(const volatile unsigned int __force *) 0xE0100118;
-
- if ((chipid & 0xff000) == 0x50000)
- uart_base = (volatile u8 *)S5P6450_PA_UART(CONFIG_S3C_LOWLEVEL_UART_PORT);
- else
- uart_base = (volatile u8 *)S5P6440_PA_UART(CONFIG_S3C_LOWLEVEL_UART_PORT);
-
- fifo_mask = S3C2440_UFSTAT_TXMASK;
- fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT;
-}
-
-#endif /* __ASM_ARCH_UNCOMPRESS_H */
diff --git a/arch/arm/mach-s5p64x0/irq-pm.c b/arch/arm/mach-s5p64x0/irq-pm.c
index 3e6f2456ee9d..2ed921e095dc 100644
--- a/arch/arm/mach-s5p64x0/irq-pm.c
+++ b/arch/arm/mach-s5p64x0/irq-pm.c
@@ -14,9 +14,9 @@
#include <linux/syscore_ops.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/io.h>
-#include <plat/regs-serial.h>
#include <plat/pm.h>
#include <mach/regs-gpio.h>
@@ -34,7 +34,9 @@ static struct irq_grp_save {
u32 mask;
} eint_grp_save[4];
+#ifdef CONFIG_SERIAL_SAMSUNG
static u32 irq_uart_mask[CONFIG_SERIAL_SAMSUNG_UARTS];
+#endif
static int s5p64x0_irq_pm_suspend(void)
{
@@ -45,8 +47,10 @@ static int s5p64x0_irq_pm_suspend(void)
s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
+#ifdef CONFIG_SERIAL_SAMSUNG
for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++)
irq_uart_mask[i] = __raw_readl(S3C_VA_UARTx(i) + S3C64XX_UINTM);
+#endif
for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
grp->con = __raw_readl(S5P64X0_EINT12CON + (i * 4));
@@ -66,8 +70,10 @@ static void s5p64x0_irq_pm_resume(void)
s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
+#ifdef CONFIG_SERIAL_SAMSUNG
for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++)
__raw_writel(irq_uart_mask[i], S3C_VA_UARTx(i) + S3C64XX_UINTM);
+#endif
for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
__raw_writel(grp->con, S5P64X0_EINT12CON + (i * 4));
diff --git a/arch/arm/mach-s5p64x0/mach-smdk6440.c b/arch/arm/mach-s5p64x0/mach-smdk6440.c
index 9efdcc03df3b..6840e197cb2d 100644
--- a/arch/arm/mach-s5p64x0/mach-smdk6440.c
+++ b/arch/arm/mach-s5p64x0/mach-smdk6440.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -39,7 +40,6 @@
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
-#include <plat/regs-serial.h>
#include <plat/gpio-cfg.h>
#include <plat/clock.h>
#include <plat/devs.h>
diff --git a/arch/arm/mach-s5p64x0/mach-smdk6450.c b/arch/arm/mach-s5p64x0/mach-smdk6450.c
index c3cacc067efe..fa1341c074ca 100644
--- a/arch/arm/mach-s5p64x0/mach-smdk6450.c
+++ b/arch/arm/mach-s5p64x0/mach-smdk6450.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -39,7 +40,6 @@
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
-#include <plat/regs-serial.h>
#include <plat/gpio-cfg.h>
#include <plat/clock.h>
#include <plat/devs.h>
diff --git a/arch/arm/mach-s5p64x0/pm.c b/arch/arm/mach-s5p64x0/pm.c
index 861e15cea691..ec8229cee716 100644
--- a/arch/arm/mach-s5p64x0/pm.c
+++ b/arch/arm/mach-s5p64x0/pm.c
@@ -161,7 +161,6 @@ static int s5p64x0_pm_add(struct device *dev, struct subsys_interface *sif)
{
pm_cpu_prep = s5p64x0_pm_prepare;
pm_cpu_sleep = s5p64x0_cpu_suspend;
- pm_uart_udivslot = 1;
return 0;
}
diff --git a/arch/arm/mach-s5pc100/common.c b/arch/arm/mach-s5pc100/common.c
index c5a8eeacf81c..6a41bf7dacf6 100644
--- a/arch/arm/mach-s5pc100/common.c
+++ b/arch/arm/mach-s5pc100/common.c
@@ -22,6 +22,7 @@
#include <linux/io.h>
#include <linux/device.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <clocksource/samsung_pwm.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
@@ -49,7 +50,6 @@
#include <plat/onenand-core.h>
#include <plat/pwm-core.h>
#include <plat/spi-core.h>
-#include <plat/regs-serial.h>
#include <plat/watchdog-reset.h>
#include "common.h"
diff --git a/arch/arm/mach-s5pc100/include/mach/debug-macro.S b/arch/arm/mach-s5pc100/include/mach/debug-macro.S
index 66cb7f16bf2a..22c23859e45e 100644
--- a/arch/arm/mach-s5pc100/include/mach/debug-macro.S
+++ b/arch/arm/mach-s5pc100/include/mach/debug-macro.S
@@ -13,8 +13,8 @@
/* pull in the relevant register and map files. */
+#include <linux/serial_s3c.h>
#include <mach/map.h>
-#include <plat/regs-serial.h>
/* note, for the boot process to work we have to keep the UART
* virtual address aligned to an 1MiB boundary for the L1
diff --git a/arch/arm/mach-s5pc100/include/mach/tick.h b/arch/arm/mach-s5pc100/include/mach/tick.h
deleted file mode 100644
index 0af8e41230ed..000000000000
--- a/arch/arm/mach-s5pc100/include/mach/tick.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* linux/arch/arm/mach-s5pc100/include/mach/tick.h
- *
- * Copyright 2009 Samsung Electronics Co.
- * Byungho Min <bhmin@samsung.com>
- *
- * S3C64XX - Timer tick support definitions
- *
- * Based on mach-s3c6400/include/mach/tick.h
- *
- * 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_ARCH_TICK_H
-#define __ASM_ARCH_TICK_H __FILE__
-
-#include <linux/irqchip/arm-vic.h>
-
-/* note, the timer interrutps turn up in 2 places, the vic and then
- * the timer block. We take the VIC as the base at the moment.
- */
-static inline u32 s3c24xx_ostimer_pending(void)
-{
- u32 pend = __raw_readl(VA_VIC0 + VIC_RAW_STATUS);
- return pend & (1 << (IRQ_TIMER4_VIC - S5P_IRQ_VIC0(0)));
-}
-
-#define TICK_MAX (0xffffffff)
-
-#endif /* __ASM_ARCH_TICK_H */
diff --git a/arch/arm/mach-s5pc100/include/mach/timex.h b/arch/arm/mach-s5pc100/include/mach/timex.h
deleted file mode 100644
index 47ffb17aff96..000000000000
--- a/arch/arm/mach-s5pc100/include/mach/timex.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* arch/arm/mach-s5pc100/include/mach/timex.h
- *
- * Copyright (c) 2003-2005 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C6400 - time parameters
- *
- * 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_ARCH_TIMEX_H
-#define __ASM_ARCH_TIMEX_H
-
-/* CLOCK_TICK_RATE needs to be evaluatable by the cpp, so making it
- * a variable is useless. It seems as long as we make our timers an
- * exact multiple of HZ, any value that makes a 1->1 correspondence
- * for the time conversion functions to/from jiffies is acceptable.
-*/
-
-#define CLOCK_TICK_RATE 12000000
-
-#endif /* __ASM_ARCH_TIMEX_H */
diff --git a/arch/arm/mach-s5pc100/include/mach/uncompress.h b/arch/arm/mach-s5pc100/include/mach/uncompress.h
deleted file mode 100644
index 720e1339425c..000000000000
--- a/arch/arm/mach-s5pc100/include/mach/uncompress.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* arch/arm/mach-s5pc100/include/mach/uncompress.h
- *
- * Copyright 2009 Samsung Electronics Co.
- * Byungho Min <bhmin@samsung.com>
- *
- * S5PC100 - uncompress code
- *
- * Based on mach-s3c6400/include/mach/uncompress.h
- *
- * 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_ARCH_UNCOMPRESS_H
-#define __ASM_ARCH_UNCOMPRESS_H
-
-#include <mach/map.h>
-#include <plat/uncompress.h>
-
-static void arch_detect_cpu(void)
-{
- /* we do not need to do any cpu detection here at the moment. */
- fifo_mask = S3C2440_UFSTAT_TXMASK;
- fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT;
-
- uart_base = (volatile u8 *)S5P_PA_UART(CONFIG_S3C_LOWLEVEL_UART_PORT);
-}
-
-#endif /* __ASM_ARCH_UNCOMPRESS_H */
diff --git a/arch/arm/mach-s5pc100/mach-smdkc100.c b/arch/arm/mach-s5pc100/mach-smdkc100.c
index 9e256b9fc930..668af3ac31f3 100644
--- a/arch/arm/mach-s5pc100/mach-smdkc100.c
+++ b/arch/arm/mach-s5pc100/mach-smdkc100.c
@@ -16,6 +16,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/gpio.h>
@@ -37,7 +38,6 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <plat/regs-serial.h>
#include <plat/gpio-cfg.h>
#include <plat/clock.h>
diff --git a/arch/arm/mach-s5pv210/Kconfig b/arch/arm/mach-s5pv210/Kconfig
index caaedafbbf5f..8c3abe521757 100644
--- a/arch/arm/mach-s5pv210/Kconfig
+++ b/arch/arm/mach-s5pv210/Kconfig
@@ -189,6 +189,7 @@ config MACH_TORBRECK
select S5PV210_SETUP_I2C1
select S5PV210_SETUP_I2C2
select S5PV210_SETUP_SDHCI
+ select SAMSUNG_DEV_IDE
help
Machine support for aESOP Torbreck
diff --git a/arch/arm/mach-s5pv210/common.c b/arch/arm/mach-s5pv210/common.c
index 26027a29b8a1..7024dcd0e40a 100644
--- a/arch/arm/mach-s5pv210/common.c
+++ b/arch/arm/mach-s5pv210/common.c
@@ -24,6 +24,7 @@
#include <linux/sched.h>
#include <linux/dma-mapping.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <asm/proc-fns.h>
#include <asm/mach/arch.h>
@@ -46,7 +47,6 @@
#include <plat/pwm-core.h>
#include <plat/tv-core.h>
#include <plat/spi-core.h>
-#include <plat/regs-serial.h>
#include "common.h"
diff --git a/arch/arm/mach-s5pv210/include/mach/debug-macro.S b/arch/arm/mach-s5pv210/include/mach/debug-macro.S
index 80c21996c943..30b511a580aa 100644
--- a/arch/arm/mach-s5pv210/include/mach/debug-macro.S
+++ b/arch/arm/mach-s5pv210/include/mach/debug-macro.S
@@ -12,8 +12,8 @@
/* pull in the relevant register and map files. */
+#include <linux/serial_s3c.h>
#include <mach/map.h>
-#include <plat/regs-serial.h>
/* note, for the boot process to work we have to keep the UART
* virtual address aligned to an 1MiB boundary for the L1
diff --git a/arch/arm/mach-s5pv210/include/mach/timex.h b/arch/arm/mach-s5pv210/include/mach/timex.h
deleted file mode 100644
index 73dc85496a83..000000000000
--- a/arch/arm/mach-s5pv210/include/mach/timex.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* linux/arch/arm/mach-s5pv210/include/mach/timex.h
- *
- * Copyright (c) 2003-2010 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * Copyright (c) 2010 Samsung Electronics Co., Ltd.
- * http://www.samsung.com/
- *
- * Based on arch/arm/mach-s5p6442/include/mach/timex.h
- *
- * S5PV210 - time parameters
- *
- * 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_ARCH_TIMEX_H
-#define __ASM_ARCH_TIMEX_H __FILE__
-
-/* CLOCK_TICK_RATE needs to be evaluatable by the cpp, so making it
- * a variable is useless. It seems as long as we make our timers an
- * exact multiple of HZ, any value that makes a 1->1 correspondence
- * for the time conversion functions to/from jiffies is acceptable.
-*/
-
-#define CLOCK_TICK_RATE 12000000
-
-#endif /* __ASM_ARCH_TIMEX_H */
diff --git a/arch/arm/mach-s5pv210/include/mach/uncompress.h b/arch/arm/mach-s5pv210/include/mach/uncompress.h
deleted file mode 100644
index 231cb07de058..000000000000
--- a/arch/arm/mach-s5pv210/include/mach/uncompress.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* linux/arch/arm/mach-s5pv210/include/mach/uncompress.h
- *
- * Copyright (c) 2010 Samsung Electronics Co., Ltd.
- * http://www.samsung.com/
- *
- * S5PV210 - uncompress code
- *
- * 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_ARCH_UNCOMPRESS_H
-#define __ASM_ARCH_UNCOMPRESS_H
-
-#include <mach/map.h>
-#include <plat/uncompress.h>
-
-static void arch_detect_cpu(void)
-{
- /* we do not need to do any cpu detection here at the moment. */
- fifo_mask = S5PV210_UFSTAT_TXMASK;
- fifo_max = 63 << S5PV210_UFSTAT_TXSHIFT;
-
- uart_base = (volatile u8 *)S5P_PA_UART(CONFIG_S3C_LOWLEVEL_UART_PORT);
-}
-
-#endif /* __ASM_ARCH_UNCOMPRESS_H */
diff --git a/arch/arm/mach-s5pv210/mach-aquila.c b/arch/arm/mach-s5pv210/mach-aquila.c
index ad40ab0f5dbd..cc37edacda26 100644
--- a/arch/arm/mach-s5pv210/mach-aquila.c
+++ b/arch/arm/mach-s5pv210/mach-aquila.c
@@ -12,6 +12,7 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/fb.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h>
@@ -32,7 +33,6 @@
#include <mach/regs-clock.h>
#include <plat/gpio-cfg.h>
-#include <plat/regs-serial.h>
#include <plat/devs.h>
#include <plat/cpu.h>
#include <plat/fb.h>
diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c
index e5cd9fbf19e9..b41a38a75844 100644
--- a/arch/arm/mach-s5pv210/mach-goni.c
+++ b/arch/arm/mach-s5pv210/mach-goni.c
@@ -12,6 +12,7 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/fb.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h>
@@ -39,7 +40,6 @@
#include <mach/regs-clock.h>
#include <plat/gpio-cfg.h>
-#include <plat/regs-serial.h>
#include <plat/devs.h>
#include <plat/cpu.h>
#include <plat/fb.h>
diff --git a/arch/arm/mach-s5pv210/mach-smdkc110.c b/arch/arm/mach-s5pv210/mach-smdkc110.c
index 7c0ed07a78a3..448e1d2eeed6 100644
--- a/arch/arm/mach-s5pv210/mach-smdkc110.c
+++ b/arch/arm/mach-s5pv210/mach-smdkc110.c
@@ -12,6 +12,7 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/i2c.h>
#include <linux/device.h>
@@ -23,7 +24,6 @@
#include <mach/map.h>
#include <mach/regs-clock.h>
-#include <plat/regs-serial.h>
#include <plat/devs.h>
#include <plat/cpu.h>
#include <linux/platform_data/ata-samsung_cf.h>
diff --git a/arch/arm/mach-s5pv210/mach-smdkv210.c b/arch/arm/mach-s5pv210/mach-smdkv210.c
index f52cc15c2d85..2a6655fb63e7 100644
--- a/arch/arm/mach-s5pv210/mach-smdkv210.c
+++ b/arch/arm/mach-s5pv210/mach-smdkv210.c
@@ -13,6 +13,7 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/device.h>
#include <linux/dm9000.h>
#include <linux/fb.h>
@@ -32,7 +33,6 @@
#include <mach/map.h>
#include <mach/regs-clock.h>
-#include <plat/regs-serial.h>
#include <plat/regs-srom.h>
#include <plat/gpio-cfg.h>
#include <plat/devs.h>
diff --git a/arch/arm/mach-s5pv210/mach-torbreck.c b/arch/arm/mach-s5pv210/mach-torbreck.c
index 579afe89842a..157805529f26 100644
--- a/arch/arm/mach-s5pv210/mach-torbreck.c
+++ b/arch/arm/mach-s5pv210/mach-torbreck.c
@@ -13,6 +13,7 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
@@ -22,7 +23,6 @@
#include <mach/map.h>
#include <mach/regs-clock.h>
-#include <plat/regs-serial.h>
#include <plat/devs.h>
#include <plat/cpu.h>
#include <linux/platform_data/i2c-s3c2410.h>
diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c
index 831a15824ec8..f9874ba60cc8 100644
--- a/arch/arm/mach-sa1100/collie.c
+++ b/arch/arm/mach-sa1100/collie.c
@@ -43,6 +43,7 @@
#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
#include <asm/mach/map.h>
+#include <asm/mach/irda.h>
#include <asm/hardware/scoop.h>
#include <asm/mach/sharpsl_param.h>
@@ -96,6 +97,37 @@ static struct mcp_plat_data collie_mcp_data = {
.codec_pdata = &collie_ucb1x00_data,
};
+static int collie_ir_startup(struct device *dev)
+{
+ int rc = gpio_request(COLLIE_GPIO_IR_ON, "IrDA");
+ if (rc)
+ return rc;
+ rc = gpio_direction_output(COLLIE_GPIO_IR_ON, 1);
+
+ if (!rc)
+ return 0;
+
+ gpio_free(COLLIE_GPIO_IR_ON);
+ return rc;
+}
+
+static void collie_ir_shutdown(struct device *dev)
+{
+ gpio_free(COLLIE_GPIO_IR_ON);
+}
+
+static int collie_ir_set_power(struct device *dev, unsigned int state)
+{
+ gpio_set_value(COLLIE_GPIO_IR_ON, !state);
+ return 0;
+}
+
+static struct irda_platform_data collie_ir_data = {
+ .startup = collie_ir_startup,
+ .shutdown = collie_ir_shutdown,
+ .set_power = collie_ir_set_power,
+};
+
/*
* Collie AC IN
*/
@@ -400,6 +432,7 @@ static void __init collie_init(void)
sa11x0_register_mtd(&collie_flash_data, collie_flash_resources,
ARRAY_SIZE(collie_flash_resources));
sa11x0_register_mcp(&collie_mcp_data);
+ sa11x0_register_irda(&collie_ir_data);
sharpsl_save_param();
}
diff --git a/arch/arm/mach-sa1100/h3100.c b/arch/arm/mach-sa1100/h3100.c
index daa27c474c13..3c43219bc881 100644
--- a/arch/arm/mach-sa1100/h3100.c
+++ b/arch/arm/mach-sa1100/h3100.c
@@ -122,15 +122,8 @@ static struct irda_platform_data h3100_irda_data = {
.shutdown = h3100_irda_shutdown,
};
-static struct gpio_default_state h3100_default_gpio[] = {
- { H3XXX_GPIO_COM_DCD, GPIO_MODE_IN, "COM DCD" },
- { H3XXX_GPIO_COM_CTS, GPIO_MODE_IN, "COM CTS" },
- { H3XXX_GPIO_COM_RTS, GPIO_MODE_OUT0, "COM RTS" },
-};
-
static void __init h3100_mach_init(void)
{
- h3xxx_init_gpio(h3100_default_gpio, ARRAY_SIZE(h3100_default_gpio));
h3xxx_mach_init();
sa11x0_register_lcd(&h3100_lcd_info);
diff --git a/arch/arm/mach-sa1100/h3600.c b/arch/arm/mach-sa1100/h3600.c
index a663e7230141..5be54c214c7c 100644
--- a/arch/arm/mach-sa1100/h3600.c
+++ b/arch/arm/mach-sa1100/h3600.c
@@ -130,15 +130,8 @@ static struct irda_platform_data h3600_irda_data = {
.shutdown = h3600_irda_shutdown,
};
-static struct gpio_default_state h3600_default_gpio[] = {
- { H3XXX_GPIO_COM_DCD, GPIO_MODE_IN, "COM DCD" },
- { H3XXX_GPIO_COM_CTS, GPIO_MODE_IN, "COM CTS" },
- { H3XXX_GPIO_COM_RTS, GPIO_MODE_OUT0, "COM RTS" },
-};
-
static void __init h3600_mach_init(void)
{
- h3xxx_init_gpio(h3600_default_gpio, ARRAY_SIZE(h3600_default_gpio));
h3xxx_mach_init();
sa11x0_register_lcd(&h3600_lcd_info);
diff --git a/arch/arm/mach-sa1100/h3xxx.c b/arch/arm/mach-sa1100/h3xxx.c
index f17e7382242a..c79bf467fb7f 100644
--- a/arch/arm/mach-sa1100/h3xxx.c
+++ b/arch/arm/mach-sa1100/h3xxx.c
@@ -28,37 +28,6 @@
#include "generic.h"
-void h3xxx_init_gpio(struct gpio_default_state *s, size_t n)
-{
- while (n--) {
- const char *name = s->name;
- int err;
-
- if (!name)
- name = "[init]";
- err = gpio_request(s->gpio, name);
- if (err) {
- printk(KERN_ERR "gpio%u: unable to request: %d\n",
- s->gpio, err);
- continue;
- }
- if (s->mode >= 0) {
- err = gpio_direction_output(s->gpio, s->mode);
- } else {
- err = gpio_direction_input(s->gpio);
- }
- if (err) {
- printk(KERN_ERR "gpio%u: unable to set direction: %d\n",
- s->gpio, err);
- continue;
- }
- if (!s->name)
- gpio_free(s->gpio);
- s++;
- }
-}
-
-
/*
* H3xxx flash support
*/
@@ -116,9 +85,34 @@ static struct resource h3xxx_flash_resource =
/*
* H3xxx uart support
*/
+static struct gpio h3xxx_uart_gpio[] = {
+ { H3XXX_GPIO_COM_DCD, GPIOF_IN, "COM DCD" },
+ { H3XXX_GPIO_COM_CTS, GPIOF_IN, "COM CTS" },
+ { H3XXX_GPIO_COM_RTS, GPIOF_OUT_INIT_LOW, "COM RTS" },
+};
+
+static bool h3xxx_uart_request_gpios(void)
+{
+ static bool h3xxx_uart_gpio_ok;
+ int rc;
+
+ if (h3xxx_uart_gpio_ok)
+ return true;
+
+ rc = gpio_request_array(h3xxx_uart_gpio, ARRAY_SIZE(h3xxx_uart_gpio));
+ if (rc)
+ pr_err("h3xxx_uart_request_gpios: error %d\n", rc);
+ else
+ h3xxx_uart_gpio_ok = true;
+
+ return h3xxx_uart_gpio_ok;
+}
+
static void h3xxx_uart_set_mctrl(struct uart_port *port, u_int mctrl)
{
if (port->mapbase == _Ser3UTCR0) {
+ if (!h3xxx_uart_request_gpios())
+ return;
gpio_set_value(H3XXX_GPIO_COM_RTS, !(mctrl & TIOCM_RTS));
}
}
@@ -128,6 +122,8 @@ static u_int h3xxx_uart_get_mctrl(struct uart_port *port)
u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
if (port->mapbase == _Ser3UTCR0) {
+ if (!h3xxx_uart_request_gpios())
+ return ret;
/*
* DCD and CTS bits are inverted in GPLR by RS232 transceiver
*/
diff --git a/arch/arm/mach-sa1100/include/mach/collie.h b/arch/arm/mach-sa1100/include/mach/collie.h
index 50e1d850ee2e..b478ca180c19 100644
--- a/arch/arm/mach-sa1100/include/mach/collie.h
+++ b/arch/arm/mach-sa1100/include/mach/collie.h
@@ -80,7 +80,7 @@ extern void locomolcd_power(int on);
#define COLLIE_TC35143_GPIO_VERSION0 UCB_IO_0
#define COLLIE_TC35143_GPIO_TBL_CHK UCB_IO_1
#define COLLIE_TC35143_GPIO_VPEN_ON UCB_IO_2
-#define COLLIE_TC35143_GPIO_IR_ON UCB_IO_3
+#define COLLIE_GPIO_IR_ON (COLLIE_TC35143_GPIO_BASE + 3)
#define COLLIE_TC35143_GPIO_AMP_ON UCB_IO_4
#define COLLIE_TC35143_GPIO_VERSION1 UCB_IO_5
#define COLLIE_TC35143_GPIO_FS8KLPF UCB_IO_5
diff --git a/arch/arm/mach-sa1100/include/mach/h3xxx.h b/arch/arm/mach-sa1100/include/mach/h3xxx.h
index c810620db53d..603d4343f7f6 100644
--- a/arch/arm/mach-sa1100/include/mach/h3xxx.h
+++ b/arch/arm/mach-sa1100/include/mach/h3xxx.h
@@ -79,17 +79,6 @@
#define H3600_EGPIO_LCD_5V_ON (H3XXX_EGPIO_BASE + 14) /* enable 5V to LCD. active high. */
#define H3600_EGPIO_LVDD_ON (H3XXX_EGPIO_BASE + 15) /* enable 9V and -6.5V to LCD. */
-struct gpio_default_state {
- int gpio;
- int mode;
- const char *name;
-};
-
-#define GPIO_MODE_IN -1
-#define GPIO_MODE_OUT0 0
-#define GPIO_MODE_OUT1 1
-
-void h3xxx_init_gpio(struct gpio_default_state *s, size_t n);
void __init h3xxx_map_io(void);
void __init h3xxx_mach_init(void);
diff --git a/arch/arm/mach-sa1100/include/mach/timex.h b/arch/arm/mach-sa1100/include/mach/timex.h
deleted file mode 100644
index 7a5d017b58b3..000000000000
--- a/arch/arm/mach-sa1100/include/mach/timex.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * arch/arm/mach-sa1100/include/mach/timex.h
- *
- * SA1100 architecture timex specifications
- *
- * Copyright (C) 1998
- */
-
-/*
- * SA1100 timer
- */
-#define CLOCK_TICK_RATE 3686400
diff --git a/arch/arm/mach-sa1100/time.c b/arch/arm/mach-sa1100/time.c
index 6fd4acb8f187..1dea6cfafb31 100644
--- a/arch/arm/mach-sa1100/time.c
+++ b/arch/arm/mach-sa1100/time.c
@@ -9,6 +9,7 @@
*
*/
#include <linux/init.h>
+#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -20,6 +21,9 @@
#include <mach/hardware.h>
#include <mach/irqs.h>
+#define SA1100_CLOCK_FREQ 3686400
+#define SA1100_LATCH DIV_ROUND_CLOSEST(SA1100_CLOCK_FREQ, HZ)
+
static u64 notrace sa1100_read_sched_clock(void)
{
return readl_relaxed(OSCR);
@@ -93,7 +97,7 @@ static void sa1100_timer_resume(struct clock_event_device *cedev)
/*
* OSMR0 is the system timer: make sure OSCR is sufficiently behind
*/
- writel_relaxed(OSMR0 - LATCH, OSCR);
+ writel_relaxed(OSMR0 - SA1100_LATCH, OSCR);
}
#else
#define sa1100_timer_suspend NULL
@@ -112,7 +116,7 @@ static struct clock_event_device ckevt_sa1100_osmr0 = {
static struct irqaction sa1100_timer_irq = {
.name = "ost0",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = sa1100_ost0_interrupt,
.dev_id = &ckevt_sa1100_osmr0,
};
@@ -128,7 +132,7 @@ void __init sa1100_timer_init(void)
setup_irq(IRQ_OST0, &sa1100_timer_irq);
- clocksource_mmio_init(OSCR, "oscr", CLOCK_TICK_RATE, 200, 32,
+ clocksource_mmio_init(OSCR, "oscr", SA1100_CLOCK_FREQ, 200, 32,
clocksource_mmio_readl_up);
clockevents_config_and_register(&ckevt_sa1100_osmr0, 3686400,
MIN_OSCR_DELTA * 2, 0x7fffffff);
diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
index 3b8c87461d67..a182008e3aeb 100644
--- a/arch/arm/mach-shmobile/Kconfig
+++ b/arch/arm/mach-shmobile/Kconfig
@@ -5,18 +5,14 @@ config ARCH_SHMOBILE_MULTI
bool "Renesas ARM SoCs" if ARCH_MULTI_V7
depends on MMU
select ARCH_SHMOBILE
- select CPU_V7
- select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
- select HAVE_SMP
select ARM_GIC
- select MIGHT_HAVE_CACHE_L2X0
select MIGHT_HAVE_PCI
+ select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
select NO_IOPORT
select PINCTRL
select ARCH_REQUIRE_GPIOLIB
- select CLKDEV_LOOKUP
if ARCH_SHMOBILE_MULTI
@@ -49,15 +45,12 @@ config MACH_GENMAI
config MACH_KOELSCH
bool "Koelsch board"
depends on ARCH_R8A7791
-
-config MACH_KZM9D
- bool "KZM9D board"
- depends on ARCH_EMEV2
- select REGULATOR_FIXED_VOLTAGE if REGULATOR
+ select MICREL_PHY if SH_ETH
config MACH_LAGER
bool "Lager board"
depends on ARCH_R8A7790
+ select MICREL_PHY if SH_ETH
comment "Renesas ARM SoCs System Configuration"
endif
@@ -134,6 +127,7 @@ config ARCH_R8A7790
select SH_CLK_CPG
select RENESAS_IRQC
select SYS_SUPPORTS_SH_CMT
+ select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
config ARCH_R8A7791
bool "R-Car M2 (R8A77910)"
@@ -144,6 +138,7 @@ config ARCH_R8A7791
select SH_CLK_CPG
select RENESAS_IRQC
select SYS_SUPPORTS_SH_CMT
+ select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
config ARCH_EMEV2
bool "Emma Mobile EV2"
@@ -168,11 +163,13 @@ comment "Renesas ARM SoCs Board Type"
config MACH_APE6EVM
bool "APE6EVM board"
depends on ARCH_R8A73A4
+ select SMSC_PHY if SMSC911X
select USE_OF
config MACH_APE6EVM_REFERENCE
bool "APE6EVM board - Reference Device Tree Implementation"
depends on ARCH_R8A73A4
+ select SMSC_PHY if SMSC911X
select USE_OF
---help---
Use reference implementation of APE6EVM board support
@@ -186,6 +183,7 @@ config MACH_MACKEREL
depends on ARCH_SH7372
select ARCH_REQUIRE_GPIOLIB
select REGULATOR_FIXED_VOLTAGE if REGULATOR
+ select SMSC_PHY if SMSC911X
select SND_SOC_AK4642 if SND_SIMPLE_CARD
select USE_OF
@@ -194,6 +192,7 @@ config MACH_ARMADILLO800EVA
depends on ARCH_R8A7740
select ARCH_REQUIRE_GPIOLIB
select REGULATOR_FIXED_VOLTAGE if REGULATOR
+ select SMSC_PHY if SH_ETH
select SND_SOC_WM8978 if SND_SIMPLE_CARD
select USE_OF
@@ -202,6 +201,7 @@ config MACH_ARMADILLO800EVA_REFERENCE
depends on ARCH_R8A7740
select ARCH_REQUIRE_GPIOLIB
select REGULATOR_FIXED_VOLTAGE if REGULATOR
+ select SMSC_PHY if SH_ETH
select SND_SOC_WM8978 if SND_SIMPLE_CARD
select USE_OF
---help---
@@ -215,11 +215,11 @@ config MACH_BOCKW
bool "BOCK-W platform"
depends on ARCH_R8A7778
select ARCH_REQUIRE_GPIOLIB
- select RENESAS_INTC_IRQPIN
select REGULATOR_FIXED_VOLTAGE if REGULATOR
- select USE_OF
+ select RENESAS_INTC_IRQPIN
select SND_SOC_AK4554 if SND_SIMPLE_CARD
select SND_SOC_AK4642 if SND_SIMPLE_CARD
+ select USE_OF
config MACH_BOCKW_REFERENCE
bool "BOCK-W - Reference Device Tree Implementation"
@@ -275,6 +275,8 @@ config MACH_LAGER
bool "Lager board"
depends on ARCH_R8A7790
select USE_OF
+ select MICREL_PHY if SH_ETH
+ select SND_SOC_AK4642 if SND_SIMPLE_CARD
config MACH_KOELSCH
bool "Koelsch board"
diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
index fe7d4ff706e4..4caffc912a81 100644
--- a/arch/arm/mach-shmobile/Makefile
+++ b/arch/arm/mach-shmobile/Makefile
@@ -52,13 +52,13 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_ARCH_SH7372) += pm-sh7372.o sleep-sh7372.o pm-rmobile.o
obj-$(CONFIG_ARCH_SH73A0) += pm-sh73a0.o
obj-$(CONFIG_ARCH_R8A7740) += pm-r8a7740.o pm-rmobile.o
-obj-$(CONFIG_ARCH_R8A7779) += pm-r8a7779.o
+obj-$(CONFIG_ARCH_R8A7779) += pm-r8a7779.o pm-rcar.o
+obj-$(CONFIG_ARCH_R8A7790) += pm-r8a7790.o pm-rcar.o
# Board objects
ifdef CONFIG_ARCH_SHMOBILE_MULTI
obj-$(CONFIG_MACH_GENMAI) += board-genmai-reference.o
obj-$(CONFIG_MACH_KOELSCH) += board-koelsch-reference.o
-obj-$(CONFIG_MACH_KZM9D) += board-kzm9d-reference.o
obj-$(CONFIG_MACH_LAGER) += board-lager-reference.o
else
obj-$(CONFIG_MACH_APE6EVM) += board-ape6evm.o
diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c
index 9323854242ca..2858f380beae 100644
--- a/arch/arm/mach-shmobile/board-armadillo800eva.c
+++ b/arch/arm/mach-shmobile/board-armadillo800eva.c
@@ -383,6 +383,8 @@ static struct platform_device sh_eth_device = {
.id = -1,
.dev = {
.platform_data = &sh_eth_platdata,
+ .dma_mask = &sh_eth_device.dev.coherent_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
},
.resource = sh_eth_resources,
.num_resources = ARRAY_SIZE(sh_eth_resources),
diff --git a/arch/arm/mach-shmobile/board-bockw.c b/arch/arm/mach-shmobile/board-bockw.c
index 74c27d9d6900..b4122f8cb8d9 100644
--- a/arch/arm/mach-shmobile/board-bockw.c
+++ b/arch/arm/mach-shmobile/board-bockw.c
@@ -1,9 +1,9 @@
/*
* Bock-W board support
*
- * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013-2014 Renesas Solutions Corp.
* Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- * Copyright (C) 2013 Cogent Embedded, Inc.
+ * Copyright (C) 2013-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 as published by
@@ -168,6 +168,8 @@ static struct renesas_usbhs_platform_info usbhs_info __initdata = {
},
.driver_param = {
.buswait_bwait = 4,
+ .d0_tx_id = HPBDMA_SLAVE_USBFUNC_TX,
+ .d1_rx_id = HPBDMA_SLAVE_USBFUNC_RX,
},
};
@@ -233,6 +235,17 @@ static struct sh_eth_plat_data ether_platform_data __initdata = {
.no_ether_link = 1,
};
+static struct platform_device_info ether_info __initdata = {
+ .parent = &platform_bus,
+ .name = "r8a777x-ether",
+ .id = -1,
+ .res = ether_resources,
+ .num_res = ARRAY_SIZE(ether_resources),
+ .data = &ether_platform_data,
+ .size_data = sizeof(ether_platform_data),
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
/* I2C */
static struct i2c_board_info i2c0_devices[] = {
{
@@ -332,16 +345,24 @@ static struct rsnd_ssi_platform_info rsnd_ssi[] = {
RSND_SSI_UNUSED, /* SSI 0 */
RSND_SSI_UNUSED, /* SSI 1 */
RSND_SSI_UNUSED, /* SSI 2 */
- RSND_SSI_SET(1, 0, gic_iid(0x85), RSND_SSI_PLAY),
- RSND_SSI_SET(2, 0, gic_iid(0x85), RSND_SSI_CLK_PIN_SHARE),
- RSND_SSI_SET(0, 0, gic_iid(0x86), RSND_SSI_PLAY),
- RSND_SSI_SET(0, 0, gic_iid(0x86), 0),
- RSND_SSI_SET(3, 0, gic_iid(0x86), RSND_SSI_PLAY),
- RSND_SSI_SET(4, 0, gic_iid(0x86), RSND_SSI_CLK_PIN_SHARE),
+ RSND_SSI_SET(1, HPBDMA_SLAVE_HPBIF3_TX, gic_iid(0x85), RSND_SSI_PLAY),
+ RSND_SSI_SET(2, HPBDMA_SLAVE_HPBIF4_RX, gic_iid(0x85), RSND_SSI_CLK_PIN_SHARE),
+ RSND_SSI_SET(0, HPBDMA_SLAVE_HPBIF5_TX, gic_iid(0x86), RSND_SSI_PLAY),
+ RSND_SSI_SET(0, HPBDMA_SLAVE_HPBIF6_RX, gic_iid(0x86), 0),
+ RSND_SSI_SET(3, HPBDMA_SLAVE_HPBIF7_TX, gic_iid(0x86), RSND_SSI_PLAY),
+ RSND_SSI_SET(4, HPBDMA_SLAVE_HPBIF8_RX, gic_iid(0x86), RSND_SSI_CLK_PIN_SHARE),
};
static struct rsnd_scu_platform_info rsnd_scu[9] = {
- /* no member at this point */
+ { .flags = 0, }, /* SRU 0 */
+ { .flags = 0, }, /* SRU 1 */
+ { .flags = 0, }, /* SRU 2 */
+ { .flags = RSND_SCU_USE_HPBIF, },
+ { .flags = RSND_SCU_USE_HPBIF, },
+ { .flags = RSND_SCU_USE_HPBIF, },
+ { .flags = RSND_SCU_USE_HPBIF, },
+ { .flags = RSND_SCU_USE_HPBIF, },
+ { .flags = RSND_SCU_USE_HPBIF, },
};
enum {
@@ -576,11 +597,7 @@ static void __init bockw_init(void)
r8a7778_init_irq_extpin(1);
r8a7778_add_standard_devices();
- platform_device_register_resndata(&platform_bus, "r8a777x-ether", -1,
- ether_resources,
- ARRAY_SIZE(ether_resources),
- &ether_platform_data,
- sizeof(ether_platform_data));
+ platform_device_register_full(&ether_info);
platform_device_register_full(&vin0_info);
/* VIN1 has a pin conflict with Ether */
diff --git a/arch/arm/mach-shmobile/board-genmai.c b/arch/arm/mach-shmobile/board-genmai.c
index 3e92e3c62d4c..6c328d63b819 100644
--- a/arch/arm/mach-shmobile/board-genmai.c
+++ b/arch/arm/mach-shmobile/board-genmai.c
@@ -1,8 +1,9 @@
/*
* Genmai board support
*
- * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013-2014 Renesas Solutions Corp.
* Copyright (C) 2013 Magnus Damm
+ * 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 as published by
@@ -20,15 +21,87 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include <linux/sh_eth.h>
+#include <linux/spi/rspi.h>
+#include <linux/spi/spi.h>
#include <mach/common.h>
+#include <mach/irqs.h>
#include <mach/r7s72100.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
+/* Ether */
+static const struct sh_eth_plat_data ether_pdata __initconst = {
+ .phy = 0x00, /* PD60610 */
+ .edmac_endian = EDMAC_LITTLE_ENDIAN,
+ .phy_interface = PHY_INTERFACE_MODE_MII,
+ .no_ether_link = 1
+};
+
+static const struct resource ether_resources[] __initconst = {
+ DEFINE_RES_MEM(0xe8203000, 0x800),
+ DEFINE_RES_MEM(0xe8204800, 0x200),
+ DEFINE_RES_IRQ(gic_iid(359)),
+};
+
+static const struct platform_device_info ether_info __initconst = {
+ .parent = &platform_bus,
+ .name = "r7s72100-ether",
+ .id = -1,
+ .res = ether_resources,
+ .num_res = ARRAY_SIZE(ether_resources),
+ .data = &ether_pdata,
+ .size_data = sizeof(ether_pdata),
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
+/* RSPI */
+#define RSPI_RESOURCE(idx, baseaddr, irq) \
+static const struct resource rspi##idx##_resources[] __initconst = { \
+ DEFINE_RES_MEM(baseaddr, 0x24), \
+ DEFINE_RES_IRQ_NAMED(irq, "error"), \
+ DEFINE_RES_IRQ_NAMED(irq + 1, "rx"), \
+ DEFINE_RES_IRQ_NAMED(irq + 2, "tx"), \
+}
+
+RSPI_RESOURCE(0, 0xe800c800, gic_iid(270));
+RSPI_RESOURCE(1, 0xe800d000, gic_iid(273));
+RSPI_RESOURCE(2, 0xe800d800, gic_iid(276));
+RSPI_RESOURCE(3, 0xe800e000, gic_iid(279));
+RSPI_RESOURCE(4, 0xe800e800, gic_iid(282));
+
+static const struct rspi_plat_data rspi_pdata __initconst = {
+ .num_chipselect = 1,
+};
+
+#define r7s72100_register_rspi(idx) \
+ platform_device_register_resndata(&platform_bus, "rspi-rz", idx, \
+ rspi##idx##_resources, \
+ ARRAY_SIZE(rspi##idx##_resources), \
+ &rspi_pdata, sizeof(rspi_pdata))
+
+static const struct spi_board_info spi_info[] __initconst = {
+ {
+ .modalias = "wm8978",
+ .max_speed_hz = 5000000,
+ .bus_num = 4,
+ .chip_select = 0,
+ },
+};
+
static void __init genmai_add_standard_devices(void)
{
r7s72100_clock_init();
r7s72100_add_dt_devices();
+
+ platform_device_register_full(&ether_info);
+
+ r7s72100_register_rspi(0);
+ r7s72100_register_rspi(1);
+ r7s72100_register_rspi(2);
+ r7s72100_register_rspi(3);
+ r7s72100_register_rspi(4);
+ spi_register_board_info(spi_info, ARRAY_SIZE(spi_info));
}
static const char * const genmai_boards_compat_dt[] __initconst = {
diff --git a/arch/arm/mach-shmobile/board-koelsch-reference.c b/arch/arm/mach-shmobile/board-koelsch-reference.c
index 652b59268416..a3fd30242bd8 100644
--- a/arch/arm/mach-shmobile/board-koelsch-reference.c
+++ b/arch/arm/mach-shmobile/board-koelsch-reference.c
@@ -21,46 +21,114 @@
#include <linux/clk.h>
#include <linux/clkdev.h>
+#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/of_platform.h>
+#include <linux/platform_data/rcar-du.h>
#include <mach/common.h>
+#include <mach/irqs.h>
#include <mach/rcar-gen2.h>
#include <mach/r8a7791.h>
#include <asm/mach/arch.h>
+/* DU */
+static struct rcar_du_encoder_data koelsch_du_encoders[] = {
+ {
+ .type = RCAR_DU_ENCODER_NONE,
+ .output = RCAR_DU_OUTPUT_LVDS0,
+ .connector.lvds.panel = {
+ .width_mm = 210,
+ .height_mm = 158,
+ .mode = {
+ .clock = 65000,
+ .hdisplay = 1024,
+ .hsync_start = 1048,
+ .hsync_end = 1184,
+ .htotal = 1344,
+ .vdisplay = 768,
+ .vsync_start = 771,
+ .vsync_end = 777,
+ .vtotal = 806,
+ .flags = 0,
+ },
+ },
+ },
+};
+
+static struct rcar_du_platform_data koelsch_du_pdata = {
+ .encoders = koelsch_du_encoders,
+ .num_encoders = ARRAY_SIZE(koelsch_du_encoders),
+};
+
+static const struct resource du_resources[] __initconst = {
+ DEFINE_RES_MEM(0xfeb00000, 0x40000),
+ DEFINE_RES_MEM_NAMED(0xfeb90000, 0x1c, "lvds.0"),
+ DEFINE_RES_IRQ(gic_spi(256)),
+ DEFINE_RES_IRQ(gic_spi(268)),
+};
+
+static void __init koelsch_add_du_device(void)
+{
+ struct platform_device_info info = {
+ .name = "rcar-du-r8a7791",
+ .id = -1,
+ .res = du_resources,
+ .num_res = ARRAY_SIZE(du_resources),
+ .data = &koelsch_du_pdata,
+ .size_data = sizeof(koelsch_du_pdata),
+ .dma_mask = DMA_BIT_MASK(32),
+ };
+
+ platform_device_register_full(&info);
+}
+
static void __init koelsch_add_standard_devices(void)
{
-#ifdef CONFIG_COMMON_CLK
/*
- * This is a really crude hack to provide clkdev support to the SCIF
- * and CMT devices until they get moved to DT.
+ * This is a really crude hack to provide clkdev support to the CMT and
+ * DU devices until they get moved to DT.
*/
- static const char * const scif_names[] = {
- "scifa0", "scifa1", "scifb0", "scifb1", "scifb2", "scifa2",
- "scif0", "scif1", "scif2", "scif3", "scif4", "scif5", "scifa3",
- "scifa4", "scifa5",
+ static const struct clk_name {
+ const char *clk;
+ const char *con_id;
+ const char *dev_id;
+ } clk_names[] = {
+ { "cmt0", NULL, "sh_cmt.0" },
+ { "scifa0", NULL, "sh-sci.0" },
+ { "scifa1", NULL, "sh-sci.1" },
+ { "scifb0", NULL, "sh-sci.2" },
+ { "scifb1", NULL, "sh-sci.3" },
+ { "scifb2", NULL, "sh-sci.4" },
+ { "scifa2", NULL, "sh-sci.5" },
+ { "scif0", NULL, "sh-sci.6" },
+ { "scif1", NULL, "sh-sci.7" },
+ { "scif2", NULL, "sh-sci.8" },
+ { "scif3", NULL, "sh-sci.9" },
+ { "scif4", NULL, "sh-sci.10" },
+ { "scif5", NULL, "sh-sci.11" },
+ { "scifa3", NULL, "sh-sci.12" },
+ { "scifa4", NULL, "sh-sci.13" },
+ { "scifa5", NULL, "sh-sci.14" },
+ { "du0", "du.0", "rcar-du-r8a7791" },
+ { "du1", "du.1", "rcar-du-r8a7791" },
+ { "lvds0", "lvds.0", "rcar-du-r8a7791" },
};
struct clk *clk;
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(scif_names); ++i) {
- clk = clk_get(NULL, scif_names[i]);
- if (clk) {
- clk_register_clkdev(clk, NULL, "sh-sci.%u", i);
+ for (i = 0; i < ARRAY_SIZE(clk_names); ++i) {
+ clk = clk_get(NULL, clk_names[i].clk);
+ if (!IS_ERR(clk)) {
+ clk_register_clkdev(clk, clk_names[i].con_id,
+ clk_names[i].dev_id);
clk_put(clk);
}
}
- clk = clk_get(NULL, "cmt0");
- if (clk) {
- clk_register_clkdev(clk, NULL, "sh_cmt.0");
- clk_put(clk);
- }
-#else
- r8a7791_clock_init();
-#endif
r8a7791_add_dt_devices();
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+
+ koelsch_add_du_device();
}
static const char * const koelsch_boards_compat_dt[] __initconst = {
diff --git a/arch/arm/mach-shmobile/board-koelsch.c b/arch/arm/mach-shmobile/board-koelsch.c
index de7cc64b1f37..5a034ff405d0 100644
--- a/arch/arm/mach-shmobile/board-koelsch.c
+++ b/arch/arm/mach-shmobile/board-koelsch.c
@@ -2,8 +2,9 @@
* Koelsch board support
*
* Copyright (C) 2013 Renesas Electronics Corporation
- * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013-2014 Renesas Solutions Corp.
* Copyright (C) 2013 Magnus Damm
+ * 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 as published by
@@ -23,14 +24,27 @@
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
+#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/leds.h>
+#include <linux/mfd/tmio.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sh_mobile_sdhi.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
#include <linux/phy.h>
#include <linux/pinctrl/machine.h>
#include <linux/platform_data/gpio-rcar.h>
#include <linux/platform_data/rcar-du.h>
#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/gpio-regulator.h>
+#include <linux/regulator/machine.h>
#include <linux/sh_eth.h>
+#include <linux/spi/flash.h>
+#include <linux/spi/rspi.h>
+#include <linux/spi/spi.h>
#include <mach/common.h>
#include <mach/irqs.h>
#include <mach/r8a7791.h>
@@ -92,6 +106,7 @@ static void __init koelsch_add_du_device(void)
/* Ether */
static const struct sh_eth_plat_data ether_pdata __initconst = {
.phy = 0x1,
+ .phy_irq = irq_pin(0),
.edmac_endian = EDMAC_LITTLE_ENDIAN,
.phy_interface = PHY_INTERFACE_MODE_RMII,
.ether_link_active_low = 1,
@@ -102,6 +117,17 @@ static const struct resource ether_resources[] __initconst = {
DEFINE_RES_IRQ(gic_spi(162)),
};
+static const struct platform_device_info ether_info __initconst = {
+ .parent = &platform_bus,
+ .name = "r8a7791-ether",
+ .id = -1,
+ .res = ether_resources,
+ .num_res = ARRAY_SIZE(ether_resources),
+ .data = &ether_pdata,
+ .size_data = sizeof(ether_pdata),
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
/* LEDS */
static struct gpio_led koelsch_leds[] = {
{
@@ -148,6 +174,199 @@ static const struct gpio_keys_platform_data koelsch_keys_pdata __initconst = {
.nbuttons = ARRAY_SIZE(gpio_buttons),
};
+/* QSPI */
+static const struct resource qspi_resources[] __initconst = {
+ DEFINE_RES_MEM(0xe6b10000, 0x1000),
+ DEFINE_RES_IRQ_NAMED(gic_spi(184), "mux"),
+};
+
+static const struct rspi_plat_data qspi_pdata __initconst = {
+ .num_chipselect = 1,
+};
+
+/* SPI Flash memory (Spansion S25FL512SAGMFIG11 64 MiB) */
+static struct mtd_partition spi_flash_part[] = {
+ {
+ .name = "loader",
+ .offset = 0x00000000,
+ .size = 512 * 1024,
+ .mask_flags = MTD_WRITEABLE,
+ },
+ {
+ .name = "bootenv",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 512 * 1024,
+ .mask_flags = MTD_WRITEABLE,
+ },
+ {
+ .name = "data",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static const struct flash_platform_data spi_flash_data = {
+ .name = "m25p80",
+ .parts = spi_flash_part,
+ .nr_parts = ARRAY_SIZE(spi_flash_part),
+ .type = "s25fl512s",
+};
+
+static const struct spi_board_info spi_info[] __initconst = {
+ {
+ .modalias = "m25p80",
+ .platform_data = &spi_flash_data,
+ .mode = SPI_MODE_0,
+ .max_speed_hz = 30000000,
+ .bus_num = 0,
+ .chip_select = 0,
+ },
+};
+
+/* SATA0 */
+static const struct resource sata0_resources[] __initconst = {
+ DEFINE_RES_MEM(0xee300000, 0x2000),
+ DEFINE_RES_IRQ(gic_spi(105)),
+};
+
+static const struct platform_device_info sata0_info __initconst = {
+ .parent = &platform_bus,
+ .name = "sata-r8a7791",
+ .id = 0,
+ .res = sata0_resources,
+ .num_res = ARRAY_SIZE(sata0_resources),
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
+/* I2C */
+static const struct resource i2c_resources[] __initconst = {
+ /* I2C0 */
+ DEFINE_RES_MEM(0xE6508000, 0x40),
+ DEFINE_RES_IRQ(gic_spi(287)),
+ /* I2C1 */
+ DEFINE_RES_MEM(0xE6518000, 0x40),
+ DEFINE_RES_IRQ(gic_spi(288)),
+ /* I2C2 */
+ DEFINE_RES_MEM(0xE6530000, 0x40),
+ DEFINE_RES_IRQ(gic_spi(286)),
+ /* I2C3 */
+ DEFINE_RES_MEM(0xE6540000, 0x40),
+ DEFINE_RES_IRQ(gic_spi(290)),
+ /* I2C4 */
+ DEFINE_RES_MEM(0xE6520000, 0x40),
+ DEFINE_RES_IRQ(gic_spi(19)),
+ /* I2C5 */
+ DEFINE_RES_MEM(0xE6528000, 0x40),
+ DEFINE_RES_IRQ(gic_spi(20)),
+};
+
+static void __init koelsch_add_i2c(unsigned idx)
+{
+ unsigned res_idx = idx * 2;
+
+ BUG_ON(res_idx >= ARRAY_SIZE(i2c_resources));
+
+ platform_device_register_simple("i2c-rcar_gen2", idx,
+ i2c_resources + res_idx, 2);
+}
+
+#define SDHI_REGULATOR(idx, vdd_pin, vccq_pin) \
+static struct regulator_consumer_supply vcc_sdhi##idx##_consumer = \
+ REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi." #idx); \
+ \
+static struct regulator_init_data vcc_sdhi##idx##_init_data = { \
+ .constraints = { \
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS, \
+ }, \
+ .consumer_supplies = &vcc_sdhi##idx##_consumer, \
+ .num_consumer_supplies = 1, \
+}; \
+ \
+static const struct fixed_voltage_config vcc_sdhi##idx##_info __initconst = {\
+ .supply_name = "SDHI" #idx "Vcc", \
+ .microvolts = 3300000, \
+ .gpio = vdd_pin, \
+ .enable_high = 1, \
+ .init_data = &vcc_sdhi##idx##_init_data, \
+}; \
+ \
+static struct regulator_consumer_supply vccq_sdhi##idx##_consumer = \
+ REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi." #idx); \
+ \
+static struct regulator_init_data vccq_sdhi##idx##_init_data = { \
+ .constraints = { \
+ .input_uV = 3300000, \
+ .min_uV = 1800000, \
+ .max_uV = 3300000, \
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | \
+ REGULATOR_CHANGE_STATUS, \
+ }, \
+ .consumer_supplies = &vccq_sdhi##idx##_consumer, \
+ .num_consumer_supplies = 1, \
+}; \
+ \
+static struct gpio vccq_sdhi##idx##_gpio = \
+ { vccq_pin, GPIOF_OUT_INIT_HIGH, "vccq-sdhi" #idx }; \
+ \
+static struct gpio_regulator_state vccq_sdhi##idx##_states[] = { \
+ { .value = 1800000, .gpios = 0 }, \
+ { .value = 3300000, .gpios = 1 }, \
+}; \
+ \
+static const struct gpio_regulator_config vccq_sdhi##idx##_info __initconst = {\
+ .supply_name = "vqmmc", \
+ .gpios = &vccq_sdhi##idx##_gpio, \
+ .nr_gpios = 1, \
+ .states = vccq_sdhi##idx##_states, \
+ .nr_states = ARRAY_SIZE(vccq_sdhi##idx##_states), \
+ .type = REGULATOR_VOLTAGE, \
+ .init_data = &vccq_sdhi##idx##_init_data, \
+};
+
+SDHI_REGULATOR(0, RCAR_GP_PIN(7, 17), RCAR_GP_PIN(2, 12));
+SDHI_REGULATOR(1, RCAR_GP_PIN(7, 18), RCAR_GP_PIN(2, 13));
+SDHI_REGULATOR(2, RCAR_GP_PIN(7, 19), RCAR_GP_PIN(2, 26));
+
+/* SDHI0 */
+static struct sh_mobile_sdhi_info sdhi0_info __initdata = {
+ .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
+ MMC_CAP_POWER_OFF_CARD,
+ .tmio_caps2 = MMC_CAP2_NO_MULTI_READ,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
+};
+
+static struct resource sdhi0_resources[] __initdata = {
+ DEFINE_RES_MEM(0xee100000, 0x200),
+ DEFINE_RES_IRQ(gic_spi(165)),
+};
+
+/* SDHI1 */
+static struct sh_mobile_sdhi_info sdhi1_info __initdata = {
+ .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
+ MMC_CAP_POWER_OFF_CARD,
+ .tmio_caps2 = MMC_CAP2_NO_MULTI_READ,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
+};
+
+static struct resource sdhi1_resources[] __initdata = {
+ DEFINE_RES_MEM(0xee140000, 0x100),
+ DEFINE_RES_IRQ(gic_spi(167)),
+};
+
+/* SDHI2 */
+static struct sh_mobile_sdhi_info sdhi2_info __initdata = {
+ .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
+ MMC_CAP_POWER_OFF_CARD,
+ .tmio_caps2 = MMC_CAP2_NO_MULTI_READ,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT |
+ TMIO_MMC_WRPROTECT_DISABLE,
+};
+
+static struct resource sdhi2_resources[] __initdata = {
+ DEFINE_RES_MEM(0xee160000, 0x100),
+ DEFINE_RES_IRQ(gic_spi(168)),
+};
+
static const struct pinctrl_map koelsch_pinctrl_map[] = {
/* DU */
PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7791", "pfc-r8a7791",
@@ -165,12 +384,51 @@ static const struct pinctrl_map koelsch_pinctrl_map[] = {
"eth_rmii", "eth"),
PIN_MAP_MUX_GROUP_DEFAULT("r8a7791-ether", "pfc-r8a7791",
"intc_irq0", "intc"),
+ /* QSPI */
+ PIN_MAP_MUX_GROUP_DEFAULT("qspi.0", "pfc-r8a7791",
+ "qspi_ctrl", "qspi"),
+ PIN_MAP_MUX_GROUP_DEFAULT("qspi.0", "pfc-r8a7791",
+ "qspi_data4", "qspi"),
/* SCIF0 (CN19: DEBUG SERIAL0) */
PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.6", "pfc-r8a7791",
"scif0_data_d", "scif0"),
/* SCIF1 (CN20: DEBUG SERIAL1) */
PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.7", "pfc-r8a7791",
"scif1_data_d", "scif1"),
+ /* I2C1 */
+ PIN_MAP_MUX_GROUP_DEFAULT("i2c-rcar_gen2.1", "pfc-r8a7791",
+ "i2c1_e", "i2c1"),
+ /* I2C2 */
+ PIN_MAP_MUX_GROUP_DEFAULT("i2c-rcar_gen2.2", "pfc-r8a7791",
+ "i2c2", "i2c2"),
+ /* I2C4 */
+ PIN_MAP_MUX_GROUP_DEFAULT("i2c-rcar_gen2.4", "pfc-r8a7791",
+ "i2c4_c", "i2c4"),
+ /* SDHI0 */
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7791",
+ "sdhi0_data4", "sdhi0"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7791",
+ "sdhi0_ctrl", "sdhi0"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7791",
+ "sdhi0_cd", "sdhi0"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7791",
+ "sdhi0_wp", "sdhi0"),
+ /* SDHI2 */
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.1", "pfc-r8a7791",
+ "sdhi1_data4", "sdhi1"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.1", "pfc-r8a7791",
+ "sdhi1_ctrl", "sdhi1"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.1", "pfc-r8a7791",
+ "sdhi1_cd", "sdhi1"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.1", "pfc-r8a7791",
+ "sdhi1_wp", "sdhi1"),
+ /* SDHI2 */
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.2", "pfc-r8a7791",
+ "sdhi2_data4", "sdhi2"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.2", "pfc-r8a7791",
+ "sdhi2_ctrl", "sdhi2"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.2", "pfc-r8a7791",
+ "sdhi2_cd", "sdhi2"),
};
static void __init koelsch_add_standard_devices(void)
@@ -180,18 +438,53 @@ static void __init koelsch_add_standard_devices(void)
ARRAY_SIZE(koelsch_pinctrl_map));
r8a7791_pinmux_init();
r8a7791_add_standard_devices();
- platform_device_register_resndata(&platform_bus, "r8a7791-ether", -1,
- ether_resources,
- ARRAY_SIZE(ether_resources),
- &ether_pdata, sizeof(ether_pdata));
+ platform_device_register_full(&ether_info);
platform_device_register_data(&platform_bus, "leds-gpio", -1,
&koelsch_leds_pdata,
sizeof(koelsch_leds_pdata));
platform_device_register_data(&platform_bus, "gpio-keys", -1,
&koelsch_keys_pdata,
sizeof(koelsch_keys_pdata));
+ platform_device_register_resndata(&platform_bus, "qspi", 0,
+ qspi_resources,
+ ARRAY_SIZE(qspi_resources),
+ &qspi_pdata, sizeof(qspi_pdata));
+ spi_register_board_info(spi_info, ARRAY_SIZE(spi_info));
koelsch_add_du_device();
+
+ platform_device_register_full(&sata0_info);
+
+ koelsch_add_i2c(1);
+ koelsch_add_i2c(2);
+ koelsch_add_i2c(4);
+ koelsch_add_i2c(5);
+
+ platform_device_register_data(&platform_bus, "reg-fixed-voltage", 0,
+ &vcc_sdhi0_info, sizeof(struct fixed_voltage_config));
+ platform_device_register_data(&platform_bus, "reg-fixed-voltage", 1,
+ &vcc_sdhi1_info, sizeof(struct fixed_voltage_config));
+ platform_device_register_data(&platform_bus, "reg-fixed-voltage", 2,
+ &vcc_sdhi2_info, sizeof(struct fixed_voltage_config));
+ platform_device_register_data(&platform_bus, "gpio-regulator", 0,
+ &vccq_sdhi0_info, sizeof(struct gpio_regulator_config));
+ platform_device_register_data(&platform_bus, "gpio-regulator", 1,
+ &vccq_sdhi1_info, sizeof(struct gpio_regulator_config));
+ platform_device_register_data(&platform_bus, "gpio-regulator", 2,
+ &vccq_sdhi2_info, sizeof(struct gpio_regulator_config));
+
+ platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 0,
+ sdhi0_resources, ARRAY_SIZE(sdhi0_resources),
+ &sdhi0_info, sizeof(struct sh_mobile_sdhi_info));
+
+ platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 1,
+ sdhi1_resources, ARRAY_SIZE(sdhi1_resources),
+ &sdhi1_info, sizeof(struct sh_mobile_sdhi_info));
+
+ platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 2,
+ sdhi2_resources, ARRAY_SIZE(sdhi2_resources),
+ &sdhi2_info, sizeof(struct sh_mobile_sdhi_info));
+
}
/*
@@ -215,6 +508,8 @@ static void __init koelsch_init(void)
{
koelsch_add_standard_devices();
+ irq_set_irq_type(irq_pin(0), IRQ_TYPE_LEVEL_LOW);
+
if (IS_ENABLED(CONFIG_PHYLIB))
phy_register_fixup_for_id("r8a7791-ether-ff:01",
koelsch_ksz8041_fixup);
diff --git a/arch/arm/mach-shmobile/board-kzm9d-reference.c b/arch/arm/mach-shmobile/board-kzm9d-reference.c
deleted file mode 100644
index 054d8d5c8fc1..000000000000
--- a/arch/arm/mach-shmobile/board-kzm9d-reference.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * kzm9d board support - Reference DT implementation
- *
- * Copyright (C) 2013 Renesas Solutions Corp.
- * 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; 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <linux/init.h>
-#include <linux/of_platform.h>
-#include <mach/emev2.h>
-#include <mach/common.h>
-#include <asm/mach/arch.h>
-
-static void __init kzm9d_add_standard_devices(void)
-{
- if (!IS_ENABLED(CONFIG_COMMON_CLK))
- emev2_clock_init();
-
- of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
-}
-
-static const char *kzm9d_boards_compat_dt[] __initdata = {
- "renesas,kzm9d",
- "renesas,kzm9d-reference",
- NULL,
-};
-
-DT_MACHINE_START(KZM9D_DT, "kzm9d")
- .smp = smp_ops(emev2_smp_ops),
- .map_io = emev2_map_io,
- .init_early = emev2_init_delay,
- .init_machine = kzm9d_add_standard_devices,
- .init_late = shmobile_init_late,
- .dt_compat = kzm9d_boards_compat_dt,
-MACHINE_END
diff --git a/arch/arm/mach-shmobile/board-lager-reference.c b/arch/arm/mach-shmobile/board-lager-reference.c
index a6e271d92af0..440aac36d693 100644
--- a/arch/arm/mach-shmobile/board-lager-reference.c
+++ b/arch/arm/mach-shmobile/board-lager-reference.c
@@ -20,47 +20,116 @@
#include <linux/clk.h>
#include <linux/clkdev.h>
+#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/of_platform.h>
+#include <linux/platform_data/rcar-du.h>
#include <mach/common.h>
+#include <mach/irqs.h>
#include <mach/rcar-gen2.h>
#include <mach/r8a7790.h>
#include <asm/mach/arch.h>
+/* DU */
+static struct rcar_du_encoder_data lager_du_encoders[] = {
+ {
+ .type = RCAR_DU_ENCODER_VGA,
+ .output = RCAR_DU_OUTPUT_DPAD0,
+ }, {
+ .type = RCAR_DU_ENCODER_NONE,
+ .output = RCAR_DU_OUTPUT_LVDS1,
+ .connector.lvds.panel = {
+ .width_mm = 210,
+ .height_mm = 158,
+ .mode = {
+ .clock = 65000,
+ .hdisplay = 1024,
+ .hsync_start = 1048,
+ .hsync_end = 1184,
+ .htotal = 1344,
+ .vdisplay = 768,
+ .vsync_start = 771,
+ .vsync_end = 777,
+ .vtotal = 806,
+ .flags = 0,
+ },
+ },
+ },
+};
+
+static struct rcar_du_platform_data lager_du_pdata = {
+ .encoders = lager_du_encoders,
+ .num_encoders = ARRAY_SIZE(lager_du_encoders),
+};
+
+static const struct resource du_resources[] __initconst = {
+ DEFINE_RES_MEM(0xfeb00000, 0x70000),
+ DEFINE_RES_MEM_NAMED(0xfeb90000, 0x1c, "lvds.0"),
+ DEFINE_RES_MEM_NAMED(0xfeb94000, 0x1c, "lvds.1"),
+ DEFINE_RES_IRQ(gic_spi(256)),
+ DEFINE_RES_IRQ(gic_spi(268)),
+ DEFINE_RES_IRQ(gic_spi(269)),
+};
+
+static void __init lager_add_du_device(void)
+{
+ struct platform_device_info info = {
+ .name = "rcar-du-r8a7790",
+ .id = -1,
+ .res = du_resources,
+ .num_res = ARRAY_SIZE(du_resources),
+ .data = &lager_du_pdata,
+ .size_data = sizeof(lager_du_pdata),
+ .dma_mask = DMA_BIT_MASK(32),
+ };
+
+ platform_device_register_full(&info);
+}
+
static void __init lager_add_standard_devices(void)
{
-#ifdef CONFIG_COMMON_CLK
/*
- * This is a really crude hack to provide clkdev support to the SCIF
- * and CMT devices until they get moved to DT.
+ * This is a really crude hack to provide clkdev support to platform
+ * devices until they get moved to DT.
*/
- static const char * const scif_names[] = {
- "scifa0", "scifa1", "scifb0", "scifb1",
- "scifb2", "scifa2", "scif0", "scif1",
- "hscif0", "hscif1",
+ static const struct clk_name {
+ const char *clk;
+ const char *con_id;
+ const char *dev_id;
+ } clk_names[] = {
+ { "cmt0", NULL, "sh_cmt.0" },
+ { "scifa0", NULL, "sh-sci.0" },
+ { "scifa1", NULL, "sh-sci.1" },
+ { "scifb0", NULL, "sh-sci.2" },
+ { "scifb1", NULL, "sh-sci.3" },
+ { "scifb2", NULL, "sh-sci.4" },
+ { "scifa2", NULL, "sh-sci.5" },
+ { "scif0", NULL, "sh-sci.6" },
+ { "scif1", NULL, "sh-sci.7" },
+ { "hscif0", NULL, "sh-sci.8" },
+ { "hscif1", NULL, "sh-sci.9" },
+ { "du0", "du.0", "rcar-du-r8a7790" },
+ { "du1", "du.1", "rcar-du-r8a7790" },
+ { "du2", "du.2", "rcar-du-r8a7790" },
+ { "lvds0", "lvds.0", "rcar-du-r8a7790" },
+ { "lvds1", "lvds.1", "rcar-du-r8a7790" },
};
struct clk *clk;
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(scif_names); ++i) {
- clk = clk_get(NULL, scif_names[i]);
- if (clk) {
- clk_register_clkdev(clk, NULL, "sh-sci.%u", i);
+ for (i = 0; i < ARRAY_SIZE(clk_names); ++i) {
+ clk = clk_get(NULL, clk_names[i].clk);
+ if (!IS_ERR(clk)) {
+ clk_register_clkdev(clk, clk_names[i].con_id,
+ clk_names[i].dev_id);
clk_put(clk);
}
}
- clk = clk_get(NULL, "cmt0");
- if (clk) {
- clk_register_clkdev(clk, NULL, "sh_cmt.0");
- clk_put(clk);
- }
-#else
- r8a7790_clock_init();
-#endif
-
r8a7790_add_dt_devices();
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+
+ lager_add_du_device();
}
static const char *lager_boards_compat_dt[] __initdata = {
diff --git a/arch/arm/mach-shmobile/board-lager.c b/arch/arm/mach-shmobile/board-lager.c
index f20c10a18543..f0104bfe544e 100644
--- a/arch/arm/mach-shmobile/board-lager.c
+++ b/arch/arm/mach-shmobile/board-lager.c
@@ -1,8 +1,9 @@
/*
* Lager board support
*
- * Copyright (C) 2013 Renesas Solutions Corp.
+ * Copyright (C) 2013-2014 Renesas Solutions Corp.
* Copyright (C) 2013 Magnus Damm
+ * 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 as published by
@@ -20,15 +21,21 @@
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
+#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/leds.h>
+#include <linux/mfd/tmio.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sh_mmcif.h>
+#include <linux/mmc/sh_mobile_sdhi.h>
#include <linux/pinctrl/machine.h>
+#include <linux/platform_data/camera-rcar.h>
#include <linux/platform_data/gpio-rcar.h>
#include <linux/platform_data/rcar-du.h>
+#include <linux/platform_data/usb-rcar-gen2-phy.h>
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <linux/regulator/driver.h>
@@ -36,9 +43,12 @@
#include <linux/regulator/gpio-regulator.h>
#include <linux/regulator/machine.h>
#include <linux/sh_eth.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/renesas_usbhs.h>
#include <mach/common.h>
#include <mach/irqs.h>
#include <mach/r8a7790.h>
+#include <media/soc_camera.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <linux/mtd/partitions.h>
@@ -46,6 +56,33 @@
#include <linux/spi/flash.h>
#include <linux/spi/rspi.h>
#include <linux/spi/spi.h>
+#include <sound/rcar_snd.h>
+#include <sound/simple_card.h>
+
+/*
+ * SSI-AK4643
+ *
+ * SW1: 1: AK4643
+ * 2: CN22
+ * 3: ADV7511
+ *
+ * this command is required when playback.
+ *
+ * # amixer set "LINEOUT Mixer DACL" on
+ */
+
+/*
+ * SDHI0 (CN8)
+ *
+ * JP3: pin1
+ * SW20: pin1
+
+ * GP5_24: 1: VDD 3.3V (defult)
+ * 0: VDD 0.0V
+ * GP5_29: 1: VccQ 3.3V (defult)
+ * 0: VccQ 1.8V
+ *
+ */
/* DU */
static struct rcar_du_encoder_data lager_du_encoders[] = {
@@ -228,6 +265,7 @@ static const struct resource mmcif1_resources[] __initconst = {
/* Ether */
static const struct sh_eth_plat_data ether_pdata __initconst = {
.phy = 0x1,
+ .phy_irq = irq_pin(0),
.edmac_endian = EDMAC_LITTLE_ENDIAN,
.phy_interface = PHY_INTERFACE_MODE_RMII,
.ether_link_active_low = 1,
@@ -238,6 +276,17 @@ static const struct resource ether_resources[] __initconst = {
DEFINE_RES_IRQ(gic_spi(162)),
};
+static const struct platform_device_info ether_info __initconst = {
+ .parent = &platform_bus,
+ .name = "r8a7790-ether",
+ .id = -1,
+ .res = ether_resources,
+ .num_res = ARRAY_SIZE(ether_resources),
+ .data = &ether_pdata,
+ .size_data = sizeof(ether_pdata),
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
/* SPI Flash memory (Spansion S25FL512SAGMFIG11 64Mb) */
static struct mtd_partition spi_flash_part[] = {
/* Reserved for user loader program, read-only */
@@ -263,7 +312,7 @@ static struct mtd_partition spi_flash_part[] = {
},
};
-static struct flash_platform_data spi_flash_data = {
+static const struct flash_platform_data spi_flash_data = {
.name = "m25p80",
.parts = spi_flash_part,
.nr_parts = ARRAY_SIZE(spi_flash_part),
@@ -288,9 +337,361 @@ static const struct spi_board_info spi_info[] __initconst = {
/* QSPI resource */
static const struct resource qspi_resources[] __initconst = {
DEFINE_RES_MEM(0xe6b10000, 0x1000),
- DEFINE_RES_IRQ(gic_spi(184)),
+ DEFINE_RES_IRQ_NAMED(gic_spi(184), "mux"),
+};
+
+/* VIN */
+static const struct resource vin_resources[] __initconst = {
+ /* VIN0 */
+ DEFINE_RES_MEM(0xe6ef0000, 0x1000),
+ DEFINE_RES_IRQ(gic_spi(188)),
+ /* VIN1 */
+ DEFINE_RES_MEM(0xe6ef1000, 0x1000),
+ DEFINE_RES_IRQ(gic_spi(189)),
+};
+
+static void __init lager_add_vin_device(unsigned idx,
+ struct rcar_vin_platform_data *pdata)
+{
+ struct platform_device_info vin_info = {
+ .parent = &platform_bus,
+ .name = "r8a7790-vin",
+ .id = idx,
+ .res = &vin_resources[idx * 2],
+ .num_res = 2,
+ .dma_mask = DMA_BIT_MASK(32),
+ .data = pdata,
+ .size_data = sizeof(*pdata),
+ };
+
+ BUG_ON(idx > 1);
+
+ platform_device_register_full(&vin_info);
+}
+
+#define LAGER_CAMERA(idx, name, addr, pdata, flag) \
+static struct i2c_board_info i2c_cam##idx##_device = { \
+ I2C_BOARD_INFO(name, addr), \
+}; \
+ \
+static struct rcar_vin_platform_data vin##idx##_pdata = { \
+ .flags = flag, \
+}; \
+ \
+static struct soc_camera_link cam##idx##_link = { \
+ .bus_id = idx, \
+ .board_info = &i2c_cam##idx##_device, \
+ .i2c_adapter_id = 2, \
+ .module_name = name, \
+ .priv = pdata, \
+}
+
+/* Camera 0 is not currently supported due to adv7612 support missing */
+LAGER_CAMERA(1, "adv7180", 0x20, NULL, RCAR_VIN_BT656);
+
+static void __init lager_add_camera1_device(void)
+{
+ platform_device_register_data(&platform_bus, "soc-camera-pdrv", 1,
+ &cam1_link, sizeof(cam1_link));
+ lager_add_vin_device(1, &vin1_pdata);
+}
+
+/* SATA1 */
+static const struct resource sata1_resources[] __initconst = {
+ DEFINE_RES_MEM(0xee500000, 0x2000),
+ DEFINE_RES_IRQ(gic_spi(106)),
+};
+
+static const struct platform_device_info sata1_info __initconst = {
+ .parent = &platform_bus,
+ .name = "sata-r8a7790",
+ .id = 1,
+ .res = sata1_resources,
+ .num_res = ARRAY_SIZE(sata1_resources),
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
+/* USBHS */
+static const struct resource usbhs_resources[] __initconst = {
+ DEFINE_RES_MEM(0xe6590000, 0x100),
+ DEFINE_RES_IRQ(gic_spi(107)),
+};
+
+struct usbhs_private {
+ struct renesas_usbhs_platform_info info;
+ struct usb_phy *phy;
+};
+
+#define usbhs_get_priv(pdev) \
+ container_of(renesas_usbhs_get_info(pdev), struct usbhs_private, info)
+
+static int usbhs_power_ctrl(struct platform_device *pdev,
+ void __iomem *base, int enable)
+{
+ struct usbhs_private *priv = usbhs_get_priv(pdev);
+
+ if (!priv->phy)
+ return -ENODEV;
+
+ if (enable) {
+ int retval = usb_phy_init(priv->phy);
+
+ if (!retval)
+ retval = usb_phy_set_suspend(priv->phy, 0);
+ return retval;
+ }
+
+ usb_phy_set_suspend(priv->phy, 1);
+ usb_phy_shutdown(priv->phy);
+ return 0;
+}
+
+static int usbhs_hardware_init(struct platform_device *pdev)
+{
+ struct usbhs_private *priv = usbhs_get_priv(pdev);
+ struct usb_phy *phy;
+ int ret;
+
+ /* USB0 Function - use PWEN as GPIO input to detect DIP Switch SW5
+ * setting to avoid VBUS short circuit due to wrong cable.
+ * PWEN should be pulled up high if USB Function is selected by SW5
+ */
+ gpio_request_one(RCAR_GP_PIN(5, 18), GPIOF_IN, NULL); /* USB0_PWEN */
+ if (!gpio_get_value(RCAR_GP_PIN(5, 18))) {
+ pr_warn("Error: USB Function not selected - check SW5 + SW6\n");
+ ret = -ENOTSUPP;
+ goto error;
+ }
+
+ phy = usb_get_phy_dev(&pdev->dev, 0);
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ goto error;
+ }
+
+ priv->phy = phy;
+ return 0;
+ error:
+ gpio_free(RCAR_GP_PIN(5, 18));
+ return ret;
+}
+
+static int usbhs_hardware_exit(struct platform_device *pdev)
+{
+ struct usbhs_private *priv = usbhs_get_priv(pdev);
+
+ if (!priv->phy)
+ return 0;
+
+ usb_put_phy(priv->phy);
+ priv->phy = NULL;
+
+ gpio_free(RCAR_GP_PIN(5, 18));
+ return 0;
+}
+
+static int usbhs_get_id(struct platform_device *pdev)
+{
+ return USBHS_GADGET;
+}
+
+static u32 lager_usbhs_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 usbhs_private usbhs_priv __initdata = {
+ .info = {
+ .platform_callback = {
+ .power_ctrl = usbhs_power_ctrl,
+ .hardware_init = usbhs_hardware_init,
+ .hardware_exit = usbhs_hardware_exit,
+ .get_id = usbhs_get_id,
+ },
+ .driver_param = {
+ .buswait_bwait = 4,
+ .pipe_type = lager_usbhs_pipe_type,
+ .pipe_size = ARRAY_SIZE(lager_usbhs_pipe_type),
+ },
+ }
+};
+
+static void __init lager_register_usbhs(void)
+{
+ usb_bind_phy("renesas_usbhs", 0, "usb_phy_rcar_gen2");
+ platform_device_register_resndata(&platform_bus,
+ "renesas_usbhs", -1,
+ usbhs_resources,
+ ARRAY_SIZE(usbhs_resources),
+ &usbhs_priv.info,
+ sizeof(usbhs_priv.info));
+}
+
+/* USBHS PHY */
+static const struct rcar_gen2_phy_platform_data usbhs_phy_pdata __initconst = {
+ .chan0_pci = 0, /* Channel 0 is USBHS */
+ .chan2_pci = 1, /* Channel 2 is PCI USB */
+};
+
+static const struct resource usbhs_phy_resources[] __initconst = {
+ DEFINE_RES_MEM(0xe6590100, 0x100),
+};
+
+/* I2C */
+static struct i2c_board_info i2c2_devices[] = {
+ {
+ I2C_BOARD_INFO("ak4643", 0x12),
+ }
+};
+
+/* Sound */
+static struct resource rsnd_resources[] __initdata = {
+ [RSND_GEN2_SCU] = DEFINE_RES_MEM(0xec500000, 0x1000),
+ [RSND_GEN2_ADG] = DEFINE_RES_MEM(0xec5a0000, 0x100),
+ [RSND_GEN2_SSIU] = DEFINE_RES_MEM(0xec540000, 0x1000),
+ [RSND_GEN2_SSI] = DEFINE_RES_MEM(0xec541000, 0x1280),
+};
+
+static struct rsnd_ssi_platform_info rsnd_ssi[] = {
+ RSND_SSI_SET(0, 0, gic_spi(370), RSND_SSI_PLAY),
+ RSND_SSI_SET(0, 0, gic_spi(371), RSND_SSI_CLK_PIN_SHARE),
+};
+
+static struct rsnd_scu_platform_info rsnd_scu[2] = {
+ /* no member at this point */
+};
+
+static struct rcar_snd_info rsnd_info = {
+ .flags = RSND_GEN2,
+ .ssi_info = rsnd_ssi,
+ .ssi_info_nr = ARRAY_SIZE(rsnd_ssi),
+ .scu_info = rsnd_scu,
+ .scu_info_nr = ARRAY_SIZE(rsnd_scu),
+};
+
+static struct asoc_simple_card_info rsnd_card_info = {
+ .name = "AK4643",
+ .card = "SSI01-AK4643",
+ .codec = "ak4642-codec.2-0012",
+ .platform = "rcar_sound",
+ .daifmt = SND_SOC_DAIFMT_LEFT_J,
+ .cpu_dai = {
+ .name = "rcar_sound",
+ .fmt = SND_SOC_DAIFMT_CBS_CFS,
+ },
+ .codec_dai = {
+ .name = "ak4642-hifi",
+ .fmt = SND_SOC_DAIFMT_CBM_CFM,
+ .sysclk = 11289600,
+ },
+};
+
+static void __init lager_add_rsnd_device(void)
+{
+ struct platform_device_info cardinfo = {
+ .parent = &platform_bus,
+ .name = "asoc-simple-card",
+ .id = -1,
+ .data = &rsnd_card_info,
+ .size_data = sizeof(struct asoc_simple_card_info),
+ .dma_mask = DMA_BIT_MASK(32),
+ };
+
+ i2c_register_board_info(2, i2c2_devices,
+ ARRAY_SIZE(i2c2_devices));
+
+ platform_device_register_resndata(
+ &platform_bus, "rcar_sound", -1,
+ rsnd_resources, ARRAY_SIZE(rsnd_resources),
+ &rsnd_info, sizeof(rsnd_info));
+
+ platform_device_register_full(&cardinfo);
+}
+
+/* SDHI0 */
+static struct sh_mobile_sdhi_info sdhi0_info __initdata = {
+ .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
+ MMC_CAP_POWER_OFF_CARD,
+ .tmio_caps2 = MMC_CAP2_NO_MULTI_READ,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT |
+ TMIO_MMC_WRPROTECT_DISABLE,
+};
+
+static struct resource sdhi0_resources[] __initdata = {
+ DEFINE_RES_MEM(0xee100000, 0x200),
+ DEFINE_RES_IRQ(gic_spi(165)),
+};
+
+/* SDHI2 */
+static struct sh_mobile_sdhi_info sdhi2_info __initdata = {
+ .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
+ MMC_CAP_POWER_OFF_CARD,
+ .tmio_caps2 = MMC_CAP2_NO_MULTI_READ,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT |
+ TMIO_MMC_WRPROTECT_DISABLE,
+};
+
+static struct resource sdhi2_resources[] __initdata = {
+ DEFINE_RES_MEM(0xee140000, 0x100),
+ DEFINE_RES_IRQ(gic_spi(167)),
+};
+
+/* Internal PCI1 */
+static const struct resource pci1_resources[] __initconst = {
+ DEFINE_RES_MEM(0xee0b0000, 0x10000), /* CFG */
+ DEFINE_RES_MEM(0xee0a0000, 0x10000), /* MEM */
+ DEFINE_RES_IRQ(gic_spi(112)),
+};
+
+static const struct platform_device_info pci1_info __initconst = {
+ .parent = &platform_bus,
+ .name = "pci-rcar-gen2",
+ .id = 1,
+ .res = pci1_resources,
+ .num_res = ARRAY_SIZE(pci1_resources),
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
+static void __init lager_add_usb1_device(void)
+{
+ platform_device_register_full(&pci1_info);
+}
+
+/* Internal PCI2 */
+static const struct resource pci2_resources[] __initconst = {
+ DEFINE_RES_MEM(0xee0d0000, 0x10000), /* CFG */
+ DEFINE_RES_MEM(0xee0c0000, 0x10000), /* MEM */
+ DEFINE_RES_IRQ(gic_spi(113)),
+};
+
+static const struct platform_device_info pci2_info __initconst = {
+ .parent = &platform_bus,
+ .name = "pci-rcar-gen2",
+ .id = 2,
+ .res = pci2_resources,
+ .num_res = ARRAY_SIZE(pci2_resources),
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
+static void __init lager_add_usb2_device(void)
+{
+ platform_device_register_full(&pci2_info);
+}
+
static const struct pinctrl_map lager_pinctrl_map[] = {
/* DU (CN10: ARGB0, CN13: LVDS) */
PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7790", "pfc-r8a7790",
@@ -299,12 +700,43 @@ static const struct pinctrl_map lager_pinctrl_map[] = {
"du_sync_1", "du"),
PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7790", "pfc-r8a7790",
"du_clk_out_0", "du"),
+ /* I2C2 */
+ PIN_MAP_MUX_GROUP_DEFAULT("i2c-rcar.2", "pfc-r8a7790",
+ "i2c2", "i2c2"),
+ /* QSPI */
+ PIN_MAP_MUX_GROUP_DEFAULT("qspi.0", "pfc-r8a7790",
+ "qspi_ctrl", "qspi"),
+ PIN_MAP_MUX_GROUP_DEFAULT("qspi.0", "pfc-r8a7790",
+ "qspi_data4", "qspi"),
/* SCIF0 (CN19: DEBUG SERIAL0) */
PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.6", "pfc-r8a7790",
"scif0_data", "scif0"),
/* SCIF1 (CN20: DEBUG SERIAL1) */
PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.7", "pfc-r8a7790",
"scif1_data", "scif1"),
+ /* SDHI0 */
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7790",
+ "sdhi0_data4", "sdhi0"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7790",
+ "sdhi0_ctrl", "sdhi0"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.0", "pfc-r8a7790",
+ "sdhi0_cd", "sdhi0"),
+ /* SDHI2 */
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.2", "pfc-r8a7790",
+ "sdhi2_data4", "sdhi2"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.2", "pfc-r8a7790",
+ "sdhi2_ctrl", "sdhi2"),
+ PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_sdhi.2", "pfc-r8a7790",
+ "sdhi2_cd", "sdhi2"),
+ /* SSI (CN17: sound) */
+ PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7790",
+ "ssi0129_ctrl", "ssi"),
+ PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7790",
+ "ssi0_data", "ssi"),
+ PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7790",
+ "ssi1_data", "ssi"),
+ PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7790",
+ "audio_clk_a", "audio_clk"),
/* MMCIF1 */
PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif.1", "pfc-r8a7790",
"mmc1_data8", "mmc1"),
@@ -319,6 +751,31 @@ static const struct pinctrl_map lager_pinctrl_map[] = {
"eth_rmii", "eth"),
PIN_MAP_MUX_GROUP_DEFAULT("r8a7790-ether", "pfc-r8a7790",
"intc_irq0", "intc"),
+ /* VIN0 */
+ PIN_MAP_MUX_GROUP_DEFAULT("r8a7790-vin.0", "pfc-r8a7790",
+ "vin0_data24", "vin0"),
+ PIN_MAP_MUX_GROUP_DEFAULT("r8a7790-vin.0", "pfc-r8a7790",
+ "vin0_sync", "vin0"),
+ PIN_MAP_MUX_GROUP_DEFAULT("r8a7790-vin.0", "pfc-r8a7790",
+ "vin0_field", "vin0"),
+ PIN_MAP_MUX_GROUP_DEFAULT("r8a7790-vin.0", "pfc-r8a7790",
+ "vin0_clkenb", "vin0"),
+ PIN_MAP_MUX_GROUP_DEFAULT("r8a7790-vin.0", "pfc-r8a7790",
+ "vin0_clk", "vin0"),
+ /* VIN1 */
+ PIN_MAP_MUX_GROUP_DEFAULT("r8a7790-vin.1", "pfc-r8a7790",
+ "vin1_data8", "vin1"),
+ PIN_MAP_MUX_GROUP_DEFAULT("r8a7790-vin.1", "pfc-r8a7790",
+ "vin1_clk", "vin1"),
+ /* USB0 */
+ PIN_MAP_MUX_GROUP_DEFAULT("renesas_usbhs", "pfc-r8a7790",
+ "usb0_ovc_vbus", "usb0"),
+ /* USB1 */
+ PIN_MAP_MUX_GROUP_DEFAULT("pci-rcar-gen2.1", "pfc-r8a7790",
+ "usb1", "usb1"),
+ /* USB2 */
+ PIN_MAP_MUX_GROUP_DEFAULT("pci-rcar-gen2.2", "pfc-r8a7790",
+ "usb2", "usb2"),
};
static void __init lager_add_standard_devices(void)
@@ -346,10 +803,7 @@ static void __init lager_add_standard_devices(void)
mmcif1_resources, ARRAY_SIZE(mmcif1_resources),
&mmcif1_pdata, sizeof(mmcif1_pdata));
- platform_device_register_resndata(&platform_bus, "r8a7790-ether", -1,
- ether_resources,
- ARRAY_SIZE(ether_resources),
- &ether_pdata, sizeof(ether_pdata));
+ platform_device_register_full(&ether_info);
lager_add_du_device();
@@ -368,6 +822,28 @@ static void __init lager_add_standard_devices(void)
&vccq_sdhi0_info, sizeof(struct gpio_regulator_config));
platform_device_register_data(&platform_bus, "gpio-regulator", gpio_regulator_idx++,
&vccq_sdhi2_info, sizeof(struct gpio_regulator_config));
+
+ lager_add_camera1_device();
+
+ platform_device_register_full(&sata1_info);
+
+ platform_device_register_resndata(&platform_bus, "usb_phy_rcar_gen2",
+ -1, usbhs_phy_resources,
+ ARRAY_SIZE(usbhs_phy_resources),
+ &usbhs_phy_pdata,
+ sizeof(usbhs_phy_pdata));
+ lager_register_usbhs();
+ lager_add_usb1_device();
+ lager_add_usb2_device();
+
+ lager_add_rsnd_device();
+
+ platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 0,
+ sdhi0_resources, ARRAY_SIZE(sdhi0_resources),
+ &sdhi0_info, sizeof(struct sh_mobile_sdhi_info));
+ platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 2,
+ sdhi2_resources, ARRAY_SIZE(sdhi2_resources),
+ &sdhi2_info, sizeof(struct sh_mobile_sdhi_info));
}
/*
@@ -391,6 +867,8 @@ static void __init lager_init(void)
{
lager_add_standard_devices();
+ irq_set_irq_type(irq_pin(0), IRQ_TYPE_LEVEL_LOW);
+
if (IS_ENABLED(CONFIG_PHYLIB))
phy_register_fixup_for_id("r8a7790-ether-ff:01",
lager_ksz8041_fixup);
diff --git a/arch/arm/mach-shmobile/clock-r7s72100.c b/arch/arm/mach-shmobile/clock-r7s72100.c
index e6ab0cd5b286..bee0073c9b64 100644
--- a/arch/arm/mach-shmobile/clock-r7s72100.c
+++ b/arch/arm/mach-shmobile/clock-r7s72100.c
@@ -22,12 +22,15 @@
#include <mach/common.h>
#include <mach/r7s72100.h>
-/* registers */
+/* Frequency Control Registers */
#define FRQCR 0xfcfe0010
#define FRQCR2 0xfcfe0014
+/* Standby Control Registers */
#define STBCR3 0xfcfe0420
#define STBCR4 0xfcfe0424
+#define STBCR7 0xfcfe0430
#define STBCR9 0xfcfe0438
+#define STBCR10 0xfcfe043c
#define PLL_RATE 30
@@ -67,7 +70,7 @@ static struct clk pll_clk = {
static unsigned long bus_recalc(struct clk *clk)
{
- return clk->parent->rate * 2 / 3;
+ return clk->parent->rate / 3;
}
static struct sh_clk_ops bus_clk_ops = {
@@ -145,15 +148,25 @@ struct clk div4_clks[DIV4_NR] = {
| CLK_ENABLE_ON_INIT),
};
-enum { MSTP97, MSTP96, MSTP95, MSTP94,
+enum {
+ MSTP107, MSTP106, MSTP105, MSTP104, MSTP103,
+ MSTP97, MSTP96, MSTP95, MSTP94,
+ MSTP74,
MSTP47, MSTP46, MSTP45, MSTP44, MSTP43, MSTP42, MSTP41, MSTP40,
- MSTP33, MSTP_NR };
+ MSTP33, MSTP_NR
+};
static struct clk mstp_clks[MSTP_NR] = {
+ [MSTP107] = SH_CLK_MSTP8(&peripheral1_clk, STBCR10, 7, 0), /* RSPI0 */
+ [MSTP106] = SH_CLK_MSTP8(&peripheral1_clk, STBCR10, 6, 0), /* RSPI1 */
+ [MSTP105] = SH_CLK_MSTP8(&peripheral1_clk, STBCR10, 5, 0), /* RSPI2 */
+ [MSTP104] = SH_CLK_MSTP8(&peripheral1_clk, STBCR10, 4, 0), /* RSPI3 */
+ [MSTP103] = SH_CLK_MSTP8(&peripheral1_clk, STBCR10, 3, 0), /* RSPI4 */
[MSTP97] = SH_CLK_MSTP8(&peripheral0_clk, STBCR9, 7, 0), /* RIIC0 */
[MSTP96] = SH_CLK_MSTP8(&peripheral0_clk, STBCR9, 6, 0), /* RIIC1 */
[MSTP95] = SH_CLK_MSTP8(&peripheral0_clk, STBCR9, 5, 0), /* RIIC2 */
[MSTP94] = SH_CLK_MSTP8(&peripheral0_clk, STBCR9, 4, 0), /* RIIC3 */
+ [MSTP74] = SH_CLK_MSTP8(&peripheral1_clk, STBCR7, 4, 0), /* Ether */
[MSTP47] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 7, 0), /* SCIF0 */
[MSTP46] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 6, 0), /* SCIF1 */
[MSTP45] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 5, 0), /* SCIF2 */
@@ -176,6 +189,21 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]),
/* MSTP clocks */
+ CLKDEV_DEV_ID("rspi-rz.0", &mstp_clks[MSTP107]),
+ CLKDEV_DEV_ID("rspi-rz.1", &mstp_clks[MSTP106]),
+ CLKDEV_DEV_ID("rspi-rz.2", &mstp_clks[MSTP105]),
+ CLKDEV_DEV_ID("rspi-rz.3", &mstp_clks[MSTP104]),
+ CLKDEV_DEV_ID("rspi-rz.4", &mstp_clks[MSTP103]),
+ CLKDEV_DEV_ID("e800c800.spi", &mstp_clks[MSTP107]),
+ CLKDEV_DEV_ID("e800d000.spi", &mstp_clks[MSTP106]),
+ CLKDEV_DEV_ID("e800d800.spi", &mstp_clks[MSTP105]),
+ CLKDEV_DEV_ID("e800e000.spi", &mstp_clks[MSTP104]),
+ CLKDEV_DEV_ID("e800e800.spi", &mstp_clks[MSTP103]),
+ CLKDEV_DEV_ID("fcfee000.i2c", &mstp_clks[MSTP97]),
+ CLKDEV_DEV_ID("fcfee400.i2c", &mstp_clks[MSTP96]),
+ CLKDEV_DEV_ID("fcfee800.i2c", &mstp_clks[MSTP95]),
+ CLKDEV_DEV_ID("fcfeec00.i2c", &mstp_clks[MSTP94]),
+ CLKDEV_DEV_ID("r7s72100-ether", &mstp_clks[MSTP74]),
CLKDEV_CON_ID("mtu2_fck", &mstp_clks[MSTP33]),
/* ICK */
diff --git a/arch/arm/mach-shmobile/clock-r8a7778.c b/arch/arm/mach-shmobile/clock-r8a7778.c
index 9783945f8bc7..2009a9bc6356 100644
--- a/arch/arm/mach-shmobile/clock-r8a7778.c
+++ b/arch/arm/mach-shmobile/clock-r8a7778.c
@@ -221,6 +221,10 @@ static struct clk_lookup lookups[] = {
CLKDEV_DEV_ID("fffc6000.spi", &mstp_clks[MSTP007]), /* HSPI2 */
CLKDEV_DEV_ID("rcar_sound", &mstp_clks[MSTP008]), /* SRU */
+ CLKDEV_ICK_ID("clk_a", "rcar_sound", &audio_clk_a),
+ CLKDEV_ICK_ID("clk_b", "rcar_sound", &audio_clk_b),
+ CLKDEV_ICK_ID("clk_c", "rcar_sound", &audio_clk_c),
+ CLKDEV_ICK_ID("clk_i", "rcar_sound", &s1_clk),
CLKDEV_ICK_ID("ssi.0", "rcar_sound", &mstp_clks[MSTP012]),
CLKDEV_ICK_ID("ssi.1", "rcar_sound", &mstp_clks[MSTP011]),
CLKDEV_ICK_ID("ssi.2", "rcar_sound", &mstp_clks[MSTP010]),
diff --git a/arch/arm/mach-shmobile/clock-r8a7779.c b/arch/arm/mach-shmobile/clock-r8a7779.c
index f1fb89b76786..8e403ae0c7b2 100644
--- a/arch/arm/mach-shmobile/clock-r8a7779.c
+++ b/arch/arm/mach-shmobile/clock-r8a7779.c
@@ -47,17 +47,10 @@
#define MD(nr) BIT(nr)
-#define FRQMR IOMEM(0xffc80014)
#define MSTPCR0 IOMEM(0xffc80030)
#define MSTPCR1 IOMEM(0xffc80034)
#define MSTPCR3 IOMEM(0xffc8003c)
#define MSTPSR1 IOMEM(0xffc80044)
-#define MSTPSR4 IOMEM(0xffc80048)
-#define MSTPSR6 IOMEM(0xffc8004c)
-#define MSTPCR4 IOMEM(0xffc80050)
-#define MSTPCR5 IOMEM(0xffc80054)
-#define MSTPCR6 IOMEM(0xffc80058)
-#define MSTPCR7 IOMEM(0xffc80040)
#define MODEMR 0xffcc0020
@@ -127,16 +120,16 @@ static struct clk mstp_clks[MSTP_NR] = {
[MSTP322] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 22, 0), /* SDHI1 */
[MSTP321] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 21, 0), /* SDHI2 */
[MSTP320] = SH_CLK_MSTP32(&clkp_clk, MSTPCR3, 20, 0), /* SDHI3 */
- [MSTP120] = SH_CLK_MSTP32(&clks_clk, MSTPCR1, 20, 0), /* VIN3 */
- [MSTP116] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 16, 0), /* PCIe */
- [MSTP115] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 15, 0), /* SATA */
- [MSTP114] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 14, 0), /* Ether */
- [MSTP110] = SH_CLK_MSTP32(&clks_clk, MSTPCR1, 10, 0), /* VIN0 */
- [MSTP109] = SH_CLK_MSTP32(&clks_clk, MSTPCR1, 9, 0), /* VIN1 */
- [MSTP108] = SH_CLK_MSTP32(&clks_clk, MSTPCR1, 8, 0), /* VIN2 */
- [MSTP103] = SH_CLK_MSTP32(&clks_clk, MSTPCR1, 3, 0), /* DU */
- [MSTP101] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 1, 0), /* USB2 */
- [MSTP100] = SH_CLK_MSTP32(&clkp_clk, MSTPCR1, 0, 0), /* USB0/1 */
+ [MSTP120] = SH_CLK_MSTP32_STS(&clks_clk, MSTPCR1, 20, MSTPSR1, 0), /* VIN3 */
+ [MSTP116] = SH_CLK_MSTP32_STS(&clkp_clk, MSTPCR1, 16, MSTPSR1, 0), /* PCIe */
+ [MSTP115] = SH_CLK_MSTP32_STS(&clkp_clk, MSTPCR1, 15, MSTPSR1, 0), /* SATA */
+ [MSTP114] = SH_CLK_MSTP32_STS(&clkp_clk, MSTPCR1, 14, MSTPSR1, 0), /* Ether */
+ [MSTP110] = SH_CLK_MSTP32_STS(&clks_clk, MSTPCR1, 10, MSTPSR1, 0), /* VIN0 */
+ [MSTP109] = SH_CLK_MSTP32_STS(&clks_clk, MSTPCR1, 9, MSTPSR1, 0), /* VIN1 */
+ [MSTP108] = SH_CLK_MSTP32_STS(&clks_clk, MSTPCR1, 8, MSTPSR1, 0), /* VIN2 */
+ [MSTP103] = SH_CLK_MSTP32_STS(&clks_clk, MSTPCR1, 3, MSTPSR1, 0), /* DU */
+ [MSTP101] = SH_CLK_MSTP32_STS(&clkp_clk, MSTPCR1, 1, MSTPSR1, 0), /* USB2 */
+ [MSTP100] = SH_CLK_MSTP32_STS(&clkp_clk, MSTPCR1, 0, MSTPSR1, 0), /* USB0/1 */
[MSTP030] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 30, 0), /* I2C0 */
[MSTP029] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 29, 0), /* I2C1 */
[MSTP028] = SH_CLK_MSTP32(&clkp_clk, MSTPCR0, 28, 0), /* I2C2 */
diff --git a/arch/arm/mach-shmobile/clock-r8a7790.c b/arch/arm/mach-shmobile/clock-r8a7790.c
index f44987a92ad4..3f93503f5b96 100644
--- a/arch/arm/mach-shmobile/clock-r8a7790.c
+++ b/arch/arm/mach-shmobile/clock-r8a7790.c
@@ -43,17 +43,26 @@
* see "p1 / 2" on R8A7790_CLOCK_ROOT() below
*/
-#define CPG_BASE 0xe6150000
-#define CPG_LEN 0x1000
-
-#define SMSTPCR1 0xe6150134
-#define SMSTPCR2 0xe6150138
-#define SMSTPCR3 0xe615013c
-#define SMSTPCR5 0xe6150144
-#define SMSTPCR7 0xe615014c
-#define SMSTPCR8 0xe6150990
-#define SMSTPCR9 0xe6150994
-#define SMSTPCR10 0xe6150998
+#define CPG_BASE 0xe6150000
+#define CPG_LEN 0x1000
+
+#define SMSTPCR1 0xe6150134
+#define SMSTPCR2 0xe6150138
+#define SMSTPCR3 0xe615013c
+#define SMSTPCR5 0xe6150144
+#define SMSTPCR7 0xe615014c
+#define SMSTPCR8 0xe6150990
+#define SMSTPCR9 0xe6150994
+#define SMSTPCR10 0xe6150998
+
+#define MSTPSR1 IOMEM(0xe6150038)
+#define MSTPSR2 IOMEM(0xe6150040)
+#define MSTPSR3 IOMEM(0xe6150048)
+#define MSTPSR5 IOMEM(0xe615003c)
+#define MSTPSR7 IOMEM(0xe61501c4)
+#define MSTPSR8 IOMEM(0xe61509a0)
+#define MSTPSR9 IOMEM(0xe61509a4)
+#define MSTPSR10 IOMEM(0xe61509a8)
#define SDCKCR 0xE6150074
#define SD2CKCR 0xE6150078
@@ -82,6 +91,15 @@ static struct clk main_clk = {
.ops = &followparent_clk_ops,
};
+static struct clk audio_clk_a = {
+};
+
+static struct clk audio_clk_b = {
+};
+
+static struct clk audio_clk_c = {
+};
+
/*
* clock ratio of these clock will be updated
* on r8a7790_clock_init()
@@ -115,6 +133,9 @@ SH_FIXED_RATIO_CLK_SET(ddr_clk, pll3_clk, 1, 8);
SH_FIXED_RATIO_CLK_SET(mp_clk, pll1_div2_clk, 1, 15);
static struct clk *main_clks[] = {
+ &audio_clk_a,
+ &audio_clk_b,
+ &audio_clk_c,
&extal_clk,
&extal_div2_clk,
&main_clk,
@@ -183,15 +204,22 @@ static struct clk div6_clks[DIV6_NR] = {
/* MSTP */
enum {
+ MSTP1017, /* parent of SCU */
+
+ MSTP1031, MSTP1030,
+ MSTP1029, MSTP1028, MSTP1027, MSTP1026, MSTP1025, MSTP1024, MSTP1023, MSTP1022,
MSTP1015, MSTP1014, MSTP1013, MSTP1012, MSTP1011, MSTP1010,
MSTP1009, MSTP1008, MSTP1007, MSTP1006, MSTP1005,
MSTP931, MSTP930, MSTP929, MSTP928,
MSTP917,
+ MSTP815, MSTP814,
MSTP813,
+ MSTP811, MSTP810, MSTP809, MSTP808,
MSTP726, MSTP725, MSTP724, MSTP723, MSTP722, MSTP721, MSTP720,
MSTP717, MSTP716,
- MSTP704,
+ MSTP704, MSTP703,
MSTP522,
+ MSTP502, MSTP501,
MSTP315, MSTP314, MSTP313, MSTP312, MSTP311, MSTP305, MSTP304,
MSTP216, MSTP207, MSTP206, MSTP204, MSTP203, MSTP202,
MSTP124,
@@ -199,53 +227,77 @@ enum {
};
static struct clk mstp_clks[MSTP_NR] = {
- [MSTP1015] = SH_CLK_MSTP32(&p_clk, SMSTPCR10, 15, 0), /* SSI0 */
- [MSTP1014] = SH_CLK_MSTP32(&p_clk, SMSTPCR10, 14, 0), /* SSI1 */
- [MSTP1013] = SH_CLK_MSTP32(&p_clk, SMSTPCR10, 13, 0), /* SSI2 */
- [MSTP1012] = SH_CLK_MSTP32(&p_clk, SMSTPCR10, 12, 0), /* SSI3 */
- [MSTP1011] = SH_CLK_MSTP32(&p_clk, SMSTPCR10, 11, 0), /* SSI4 */
- [MSTP1010] = SH_CLK_MSTP32(&p_clk, SMSTPCR10, 10, 0), /* SSI5 */
- [MSTP1009] = SH_CLK_MSTP32(&p_clk, SMSTPCR10, 9, 0), /* SSI6 */
- [MSTP1008] = SH_CLK_MSTP32(&p_clk, SMSTPCR10, 8, 0), /* SSI7 */
- [MSTP1007] = SH_CLK_MSTP32(&p_clk, SMSTPCR10, 7, 0), /* SSI8 */
- [MSTP1006] = SH_CLK_MSTP32(&p_clk, SMSTPCR10, 6, 0), /* SSI9 */
- [MSTP1005] = SH_CLK_MSTP32(&p_clk, SMSTPCR10, 5, 0), /* SSI ALL */
- [MSTP931] = SH_CLK_MSTP32(&p_clk, SMSTPCR9, 31, 0), /* I2C0 */
- [MSTP930] = SH_CLK_MSTP32(&p_clk, SMSTPCR9, 30, 0), /* I2C1 */
- [MSTP929] = SH_CLK_MSTP32(&p_clk, SMSTPCR9, 29, 0), /* I2C2 */
- [MSTP928] = SH_CLK_MSTP32(&p_clk, SMSTPCR9, 28, 0), /* I2C3 */
- [MSTP917] = SH_CLK_MSTP32(&qspi_clk, SMSTPCR9, 17, 0), /* QSPI */
- [MSTP813] = SH_CLK_MSTP32(&p_clk, SMSTPCR8, 13, 0), /* Ether */
- [MSTP726] = SH_CLK_MSTP32(&zx_clk, SMSTPCR7, 26, 0), /* LVDS0 */
- [MSTP725] = SH_CLK_MSTP32(&zx_clk, SMSTPCR7, 25, 0), /* LVDS1 */
- [MSTP724] = SH_CLK_MSTP32(&zx_clk, SMSTPCR7, 24, 0), /* DU0 */
- [MSTP723] = SH_CLK_MSTP32(&zx_clk, SMSTPCR7, 23, 0), /* DU1 */
- [MSTP722] = SH_CLK_MSTP32(&zx_clk, SMSTPCR7, 22, 0), /* DU2 */
- [MSTP721] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 21, 0), /* SCIF0 */
- [MSTP720] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 20, 0), /* SCIF1 */
- [MSTP717] = SH_CLK_MSTP32(&zs_clk, SMSTPCR7, 17, 0), /* HSCIF0 */
- [MSTP716] = SH_CLK_MSTP32(&zs_clk, SMSTPCR7, 16, 0), /* HSCIF1 */
- [MSTP704] = SH_CLK_MSTP32(&mp_clk, SMSTPCR7, 4, 0), /* HSUSB */
- [MSTP522] = SH_CLK_MSTP32(&extal_clk, SMSTPCR5, 22, 0), /* Thermal */
- [MSTP315] = SH_CLK_MSTP32(&div6_clks[DIV6_MMC0], SMSTPCR3, 15, 0), /* MMC0 */
- [MSTP314] = SH_CLK_MSTP32(&div4_clks[DIV4_SD0], SMSTPCR3, 14, 0), /* SDHI0 */
- [MSTP313] = SH_CLK_MSTP32(&div4_clks[DIV4_SD1], SMSTPCR3, 13, 0), /* SDHI1 */
- [MSTP312] = SH_CLK_MSTP32(&div6_clks[DIV6_SD2], SMSTPCR3, 12, 0), /* SDHI2 */
- [MSTP311] = SH_CLK_MSTP32(&div6_clks[DIV6_SD3], SMSTPCR3, 11, 0), /* SDHI3 */
- [MSTP305] = SH_CLK_MSTP32(&div6_clks[DIV6_MMC1], SMSTPCR3, 5, 0), /* MMC1 */
- [MSTP304] = SH_CLK_MSTP32(&cp_clk, SMSTPCR3, 4, 0), /* TPU0 */
- [MSTP216] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 16, 0), /* SCIFB2 */
- [MSTP207] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 7, 0), /* SCIFB1 */
- [MSTP206] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 6, 0), /* SCIFB0 */
- [MSTP204] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 4, 0), /* SCIFA0 */
- [MSTP203] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 3, 0), /* SCIFA1 */
- [MSTP202] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 2, 0), /* SCIFA2 */
- [MSTP124] = SH_CLK_MSTP32(&rclk_clk, SMSTPCR1, 24, 0), /* CMT0 */
+ [MSTP1031] = SH_CLK_MSTP32_STS(&mstp_clks[MSTP1017], SMSTPCR10, 31, MSTPSR10, 0), /* SCU0 */
+ [MSTP1030] = SH_CLK_MSTP32_STS(&mstp_clks[MSTP1017], SMSTPCR10, 30, MSTPSR10, 0), /* SCU1 */
+ [MSTP1029] = SH_CLK_MSTP32_STS(&mstp_clks[MSTP1017], SMSTPCR10, 29, MSTPSR10, 0), /* SCU2 */
+ [MSTP1028] = SH_CLK_MSTP32_STS(&mstp_clks[MSTP1017], SMSTPCR10, 28, MSTPSR10, 0), /* SCU3 */
+ [MSTP1027] = SH_CLK_MSTP32_STS(&mstp_clks[MSTP1017], SMSTPCR10, 27, MSTPSR10, 0), /* SCU4 */
+ [MSTP1026] = SH_CLK_MSTP32_STS(&mstp_clks[MSTP1017], SMSTPCR10, 26, MSTPSR10, 0), /* SCU5 */
+ [MSTP1025] = SH_CLK_MSTP32_STS(&mstp_clks[MSTP1017], SMSTPCR10, 25, MSTPSR10, 0), /* SCU6 */
+ [MSTP1024] = SH_CLK_MSTP32_STS(&mstp_clks[MSTP1017], SMSTPCR10, 24, MSTPSR10, 0), /* SCU7 */
+ [MSTP1023] = SH_CLK_MSTP32_STS(&mstp_clks[MSTP1017], SMSTPCR10, 23, MSTPSR10, 0), /* SCU8 */
+ [MSTP1022] = SH_CLK_MSTP32_STS(&mstp_clks[MSTP1017], SMSTPCR10, 22, MSTPSR10, 0), /* SCU9 */
+ [MSTP1017] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 17, MSTPSR10, 0), /* SCU */
+ [MSTP1015] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 15, MSTPSR10, 0), /* SSI0 */
+ [MSTP1014] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 14, MSTPSR10, 0), /* SSI1 */
+ [MSTP1013] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 13, MSTPSR10, 0), /* SSI2 */
+ [MSTP1012] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 12, MSTPSR10, 0), /* SSI3 */
+ [MSTP1011] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 11, MSTPSR10, 0), /* SSI4 */
+ [MSTP1010] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 10, MSTPSR10, 0), /* SSI5 */
+ [MSTP1009] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 9, MSTPSR10, 0), /* SSI6 */
+ [MSTP1008] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 8, MSTPSR10, 0), /* SSI7 */
+ [MSTP1007] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 7, MSTPSR10, 0), /* SSI8 */
+ [MSTP1006] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 6, MSTPSR10, 0), /* SSI9 */
+ [MSTP1005] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR10, 5, MSTPSR10, 0), /* SSI ALL */
+ [MSTP931] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR9, 31, MSTPSR9, 0), /* I2C0 */
+ [MSTP930] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR9, 30, MSTPSR9, 0), /* I2C1 */
+ [MSTP929] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR9, 29, MSTPSR9, 0), /* I2C2 */
+ [MSTP928] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR9, 28, MSTPSR9, 0), /* I2C3 */
+ [MSTP917] = SH_CLK_MSTP32_STS(&qspi_clk, SMSTPCR9, 17, MSTPSR9, 0), /* QSPI */
+ [MSTP815] = SH_CLK_MSTP32_STS(&zs_clk, SMSTPCR8, 15, MSTPSR8, 0), /* SATA0 */
+ [MSTP814] = SH_CLK_MSTP32_STS(&zs_clk, SMSTPCR8, 14, MSTPSR8, 0), /* SATA1 */
+ [MSTP813] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR8, 13, MSTPSR8, 0), /* Ether */
+ [MSTP811] = SH_CLK_MSTP32_STS(&zg_clk, SMSTPCR8, 11, MSTPSR8, 0), /* VIN0 */
+ [MSTP810] = SH_CLK_MSTP32_STS(&zg_clk, SMSTPCR8, 10, MSTPSR8, 0), /* VIN1 */
+ [MSTP809] = SH_CLK_MSTP32_STS(&zg_clk, SMSTPCR8, 9, MSTPSR8, 0), /* VIN2 */
+ [MSTP808] = SH_CLK_MSTP32_STS(&zg_clk, SMSTPCR8, 8, MSTPSR8, 0), /* VIN3 */
+ [MSTP726] = SH_CLK_MSTP32_STS(&zx_clk, SMSTPCR7, 26, MSTPSR7, 0), /* LVDS0 */
+ [MSTP725] = SH_CLK_MSTP32_STS(&zx_clk, SMSTPCR7, 25, MSTPSR7, 0), /* LVDS1 */
+ [MSTP724] = SH_CLK_MSTP32_STS(&zx_clk, SMSTPCR7, 24, MSTPSR7, 0), /* DU0 */
+ [MSTP723] = SH_CLK_MSTP32_STS(&zx_clk, SMSTPCR7, 23, MSTPSR7, 0), /* DU1 */
+ [MSTP722] = SH_CLK_MSTP32_STS(&zx_clk, SMSTPCR7, 22, MSTPSR7, 0), /* DU2 */
+ [MSTP721] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR7, 21, MSTPSR7, 0), /* SCIF0 */
+ [MSTP720] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR7, 20, MSTPSR7, 0), /* SCIF1 */
+ [MSTP717] = SH_CLK_MSTP32_STS(&zs_clk, SMSTPCR7, 17, MSTPSR7, 0), /* HSCIF0 */
+ [MSTP716] = SH_CLK_MSTP32_STS(&zs_clk, SMSTPCR7, 16, MSTPSR7, 0), /* HSCIF1 */
+ [MSTP704] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR7, 4, MSTPSR7, 0), /* HSUSB */
+ [MSTP703] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR7, 3, MSTPSR7, 0), /* EHCI */
+ [MSTP522] = SH_CLK_MSTP32_STS(&extal_clk, SMSTPCR5, 22, MSTPSR5, 0), /* Thermal */
+ [MSTP502] = SH_CLK_MSTP32_STS(&zs_clk, SMSTPCR5, 2, MSTPSR5, 0), /* Audio-DMAC low */
+ [MSTP501] = SH_CLK_MSTP32_STS(&zs_clk, SMSTPCR5, 1, MSTPSR5, 0), /* Audio-DMAC hi */
+ [MSTP315] = SH_CLK_MSTP32_STS(&div6_clks[DIV6_MMC0], SMSTPCR3, 15, MSTPSR3, 0), /* MMC0 */
+ [MSTP314] = SH_CLK_MSTP32_STS(&div4_clks[DIV4_SD0], SMSTPCR3, 14, MSTPSR3, 0), /* SDHI0 */
+ [MSTP313] = SH_CLK_MSTP32_STS(&div4_clks[DIV4_SD1], SMSTPCR3, 13, MSTPSR3, 0), /* SDHI1 */
+ [MSTP312] = SH_CLK_MSTP32_STS(&div6_clks[DIV6_SD2], SMSTPCR3, 12, MSTPSR3, 0), /* SDHI2 */
+ [MSTP311] = SH_CLK_MSTP32_STS(&div6_clks[DIV6_SD3], SMSTPCR3, 11, MSTPSR3, 0), /* SDHI3 */
+ [MSTP305] = SH_CLK_MSTP32_STS(&div6_clks[DIV6_MMC1], SMSTPCR3, 5, MSTPSR3, 0), /* MMC1 */
+ [MSTP304] = SH_CLK_MSTP32_STS(&cp_clk, SMSTPCR3, 4, MSTPSR3, 0), /* TPU0 */
+ [MSTP216] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 16, MSTPSR2, 0), /* SCIFB2 */
+ [MSTP207] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 7, MSTPSR2, 0), /* SCIFB1 */
+ [MSTP206] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 6, MSTPSR2, 0), /* SCIFB0 */
+ [MSTP204] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 4, MSTPSR2, 0), /* SCIFA0 */
+ [MSTP203] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 3, MSTPSR2, 0), /* SCIFA1 */
+ [MSTP202] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 2, MSTPSR2, 0), /* SCIFA2 */
+ [MSTP124] = SH_CLK_MSTP32_STS(&rclk_clk, SMSTPCR1, 24, MSTPSR1, 0), /* CMT0 */
};
static struct clk_lookup lookups[] = {
/* main clocks */
+ CLKDEV_CON_ID("audio_clk_a", &audio_clk_a),
+ CLKDEV_CON_ID("audio_clk_b", &audio_clk_b),
+ CLKDEV_CON_ID("audio_clk_c", &audio_clk_c),
+ CLKDEV_CON_ID("audio_clk_internal", &m2_clk),
CLKDEV_CON_ID("extal", &extal_clk),
CLKDEV_CON_ID("extal_div2", &extal_div2_clk),
CLKDEV_CON_ID("main", &main_clk),
@@ -291,32 +343,32 @@ static struct clk_lookup lookups[] = {
CLKDEV_DEV_ID("sh-sci.7", &mstp_clks[MSTP720]),
CLKDEV_DEV_ID("sh-sci.8", &mstp_clks[MSTP717]),
CLKDEV_DEV_ID("sh-sci.9", &mstp_clks[MSTP716]),
- CLKDEV_DEV_ID("e6508000.i2c", &mstp_clks[MSTP931]),
CLKDEV_DEV_ID("i2c-rcar_gen2.0", &mstp_clks[MSTP931]),
- CLKDEV_DEV_ID("e6518000.i2c", &mstp_clks[MSTP930]),
CLKDEV_DEV_ID("i2c-rcar_gen2.1", &mstp_clks[MSTP930]),
- CLKDEV_DEV_ID("e6530000.i2c", &mstp_clks[MSTP929]),
CLKDEV_DEV_ID("i2c-rcar_gen2.2", &mstp_clks[MSTP929]),
- CLKDEV_DEV_ID("e6540000.i2c", &mstp_clks[MSTP928]),
CLKDEV_DEV_ID("i2c-rcar_gen2.3", &mstp_clks[MSTP928]),
CLKDEV_DEV_ID("r8a7790-ether", &mstp_clks[MSTP813]),
- CLKDEV_DEV_ID("e61f0000.thermal", &mstp_clks[MSTP522]),
+ CLKDEV_DEV_ID("r8a7790-vin.0", &mstp_clks[MSTP811]),
+ CLKDEV_DEV_ID("r8a7790-vin.1", &mstp_clks[MSTP810]),
+ CLKDEV_DEV_ID("r8a7790-vin.2", &mstp_clks[MSTP809]),
+ CLKDEV_DEV_ID("r8a7790-vin.3", &mstp_clks[MSTP808]),
CLKDEV_DEV_ID("rcar_thermal", &mstp_clks[MSTP522]),
- CLKDEV_DEV_ID("ee200000.mmc", &mstp_clks[MSTP315]),
+ CLKDEV_DEV_ID("sh-dma-engine.0", &mstp_clks[MSTP502]),
+ CLKDEV_DEV_ID("sh-dma-engine.1", &mstp_clks[MSTP501]),
CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[MSTP315]),
- CLKDEV_DEV_ID("ee100000.sd", &mstp_clks[MSTP314]),
CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP314]),
- CLKDEV_DEV_ID("ee120000.sd", &mstp_clks[MSTP313]),
CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]),
- CLKDEV_DEV_ID("ee140000.sd", &mstp_clks[MSTP312]),
CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP312]),
- CLKDEV_DEV_ID("ee160000.sd", &mstp_clks[MSTP311]),
CLKDEV_DEV_ID("sh_mobile_sdhi.3", &mstp_clks[MSTP311]),
- CLKDEV_DEV_ID("ee220000.mmc", &mstp_clks[MSTP305]),
CLKDEV_DEV_ID("sh_mmcif.1", &mstp_clks[MSTP305]),
CLKDEV_DEV_ID("sh_cmt.0", &mstp_clks[MSTP124]),
CLKDEV_DEV_ID("qspi.0", &mstp_clks[MSTP917]),
CLKDEV_DEV_ID("renesas_usbhs", &mstp_clks[MSTP704]),
+ CLKDEV_DEV_ID("pci-rcar-gen2.0", &mstp_clks[MSTP703]),
+ CLKDEV_DEV_ID("pci-rcar-gen2.1", &mstp_clks[MSTP703]),
+ CLKDEV_DEV_ID("pci-rcar-gen2.2", &mstp_clks[MSTP703]),
+ CLKDEV_DEV_ID("sata-r8a7790.0", &mstp_clks[MSTP815]),
+ CLKDEV_DEV_ID("sata-r8a7790.1", &mstp_clks[MSTP814]),
/* ICK */
CLKDEV_ICK_ID("usbhs", "usb_phy_rcar_gen2", &mstp_clks[MSTP704]),
@@ -325,6 +377,20 @@ static struct clk_lookup lookups[] = {
CLKDEV_ICK_ID("du.0", "rcar-du-r8a7790", &mstp_clks[MSTP724]),
CLKDEV_ICK_ID("du.1", "rcar-du-r8a7790", &mstp_clks[MSTP723]),
CLKDEV_ICK_ID("du.2", "rcar-du-r8a7790", &mstp_clks[MSTP722]),
+ CLKDEV_ICK_ID("clk_a", "rcar_sound", &audio_clk_a),
+ CLKDEV_ICK_ID("clk_b", "rcar_sound", &audio_clk_b),
+ CLKDEV_ICK_ID("clk_c", "rcar_sound", &audio_clk_c),
+ CLKDEV_ICK_ID("clk_i", "rcar_sound", &m2_clk),
+ CLKDEV_ICK_ID("scu.0", "rcar_sound", &mstp_clks[MSTP1031]),
+ CLKDEV_ICK_ID("scu.1", "rcar_sound", &mstp_clks[MSTP1030]),
+ CLKDEV_ICK_ID("scu.2", "rcar_sound", &mstp_clks[MSTP1029]),
+ CLKDEV_ICK_ID("scu.3", "rcar_sound", &mstp_clks[MSTP1028]),
+ CLKDEV_ICK_ID("scu.4", "rcar_sound", &mstp_clks[MSTP1027]),
+ CLKDEV_ICK_ID("scu.5", "rcar_sound", &mstp_clks[MSTP1026]),
+ CLKDEV_ICK_ID("scu.6", "rcar_sound", &mstp_clks[MSTP1025]),
+ CLKDEV_ICK_ID("scu.7", "rcar_sound", &mstp_clks[MSTP1024]),
+ CLKDEV_ICK_ID("scu.8", "rcar_sound", &mstp_clks[MSTP1023]),
+ CLKDEV_ICK_ID("scu.9", "rcar_sound", &mstp_clks[MSTP1022]),
CLKDEV_ICK_ID("ssi.0", "rcar_sound", &mstp_clks[MSTP1015]),
CLKDEV_ICK_ID("ssi.1", "rcar_sound", &mstp_clks[MSTP1014]),
CLKDEV_ICK_ID("ssi.2", "rcar_sound", &mstp_clks[MSTP1013]),
diff --git a/arch/arm/mach-shmobile/clock-r8a7791.c b/arch/arm/mach-shmobile/clock-r8a7791.c
index f5461262ee25..701383fe3267 100644
--- a/arch/arm/mach-shmobile/clock-r8a7791.c
+++ b/arch/arm/mach-shmobile/clock-r8a7791.c
@@ -59,10 +59,19 @@
#define SMSTPCR10 0xE6150998
#define SMSTPCR11 0xE615099C
+#define MSTPSR1 IOMEM(0xe6150038)
+#define MSTPSR2 IOMEM(0xe6150040)
+#define MSTPSR3 IOMEM(0xe6150048)
+#define MSTPSR5 IOMEM(0xe615003c)
+#define MSTPSR7 IOMEM(0xe61501c4)
+#define MSTPSR8 IOMEM(0xe61509a0)
+#define MSTPSR9 IOMEM(0xe61509a4)
+#define MSTPSR11 IOMEM(0xe61509ac)
+
#define MODEMR 0xE6160060
#define SDCKCR 0xE6150074
-#define SD2CKCR 0xE6150078
-#define SD3CKCR 0xE615007C
+#define SD1CKCR 0xE6150078
+#define SD2CKCR 0xE615026c
#define MMC0CKCR 0xE6150240
#define MMC1CKCR 0xE6150244
#define SSPCKCR 0xE6150248
@@ -93,6 +102,7 @@ static struct clk main_clk = {
*/
SH_FIXED_RATIO_CLK_SET(pll1_clk, main_clk, 1, 1);
SH_FIXED_RATIO_CLK_SET(pll3_clk, main_clk, 1, 1);
+SH_FIXED_RATIO_CLK_SET(qspi_clk, pll1_clk, 1, 1);
/* fixed ratio clock */
SH_FIXED_RATIO_CLK_SET(extal_div2_clk, extal_clk, 1, 2);
@@ -103,7 +113,9 @@ SH_FIXED_RATIO_CLK_SET(hp_clk, pll1_clk, 1, 12);
SH_FIXED_RATIO_CLK_SET(p_clk, pll1_clk, 1, 24);
SH_FIXED_RATIO_CLK_SET(rclk_clk, pll1_clk, 1, (48 * 1024));
SH_FIXED_RATIO_CLK_SET(mp_clk, pll1_div2_clk, 1, 15);
+SH_FIXED_RATIO_CLK_SET(zg_clk, pll1_clk, 1, 3);
SH_FIXED_RATIO_CLK_SET(zx_clk, pll1_clk, 1, 3);
+SH_FIXED_RATIO_CLK_SET(zs_clk, pll1_clk, 1, 6);
static struct clk *main_clks[] = {
&extal_clk,
@@ -114,46 +126,103 @@ static struct clk *main_clks[] = {
&pll3_clk,
&hp_clk,
&p_clk,
+ &qspi_clk,
&rclk_clk,
&mp_clk,
&cp_clk,
+ &zg_clk,
&zx_clk,
+ &zs_clk,
+};
+
+/* SDHI (DIV4) clock */
+static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 18, 24, 0, 36, 48, 10 };
+
+static struct clk_div_mult_table div4_div_mult_table = {
+ .divisors = divisors,
+ .nr_divisors = ARRAY_SIZE(divisors),
+};
+
+static struct clk_div4_table div4_table = {
+ .div_mult_table = &div4_div_mult_table,
+};
+
+enum {
+ DIV4_SDH, DIV4_SD0,
+ DIV4_NR
+};
+
+static struct clk div4_clks[DIV4_NR] = {
+ [DIV4_SDH] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 8, 0x0dff, CLK_ENABLE_ON_INIT),
+ [DIV4_SD0] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 4, 0x1de0, CLK_ENABLE_ON_INIT),
+};
+
+/* DIV6 clocks */
+enum {
+ DIV6_SD1, DIV6_SD2,
+ DIV6_NR
+};
+
+static struct clk div6_clks[DIV6_NR] = {
+ [DIV6_SD1] = SH_CLK_DIV6(&pll1_div2_clk, SD1CKCR, 0),
+ [DIV6_SD2] = SH_CLK_DIV6(&pll1_div2_clk, SD2CKCR, 0),
};
/* MSTP */
enum {
+ MSTP1108, MSTP1107, MSTP1106,
+ MSTP931, MSTP930, MSTP929, MSTP928, MSTP927, MSTP925,
+ MSTP917,
+ MSTP815, MSTP814,
MSTP813,
+ MSTP811, MSTP810, MSTP809,
MSTP726, MSTP724, MSTP723, MSTP721, MSTP720,
MSTP719, MSTP718, MSTP715, MSTP714,
MSTP522,
+ MSTP314, MSTP312, MSTP311,
MSTP216, MSTP207, MSTP206,
- MSTP204, MSTP203, MSTP202, MSTP1105, MSTP1106, MSTP1107,
+ MSTP204, MSTP203, MSTP202,
MSTP124,
MSTP_NR
};
static struct clk mstp_clks[MSTP_NR] = {
- [MSTP813] = SH_CLK_MSTP32(&p_clk, SMSTPCR8, 13, 0), /* Ether */
- [MSTP726] = SH_CLK_MSTP32(&zx_clk, SMSTPCR7, 26, 0), /* LVDS0 */
- [MSTP724] = SH_CLK_MSTP32(&zx_clk, SMSTPCR7, 24, 0), /* DU0 */
- [MSTP723] = SH_CLK_MSTP32(&zx_clk, SMSTPCR7, 23, 0), /* DU1 */
- [MSTP721] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 21, 0), /* SCIF0 */
- [MSTP720] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 20, 0), /* SCIF1 */
- [MSTP719] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 19, 0), /* SCIF2 */
- [MSTP718] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 18, 0), /* SCIF3 */
- [MSTP715] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 15, 0), /* SCIF4 */
- [MSTP714] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 14, 0), /* SCIF5 */
- [MSTP522] = SH_CLK_MSTP32(&extal_clk, SMSTPCR5, 22, 0), /* Thermal */
- [MSTP216] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 16, 0), /* SCIFB2 */
- [MSTP207] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 7, 0), /* SCIFB1 */
- [MSTP206] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 6, 0), /* SCIFB0 */
- [MSTP204] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 4, 0), /* SCIFA0 */
- [MSTP203] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 3, 0), /* SCIFA1 */
- [MSTP202] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 2, 0), /* SCIFA2 */
- [MSTP1105] = SH_CLK_MSTP32(&mp_clk, SMSTPCR11, 5, 0), /* SCIFA3 */
- [MSTP1106] = SH_CLK_MSTP32(&mp_clk, SMSTPCR11, 6, 0), /* SCIFA4 */
- [MSTP1107] = SH_CLK_MSTP32(&mp_clk, SMSTPCR11, 7, 0), /* SCIFA5 */
- [MSTP124] = SH_CLK_MSTP32(&rclk_clk, SMSTPCR1, 24, 0), /* CMT0 */
+ [MSTP1108] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR11, 8, MSTPSR11, 0), /* SCIFA5 */
+ [MSTP1107] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR11, 7, MSTPSR11, 0), /* SCIFA4 */
+ [MSTP1106] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR11, 6, MSTPSR11, 0), /* SCIFA3 */
+ [MSTP931] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR9, 31, MSTPSR9, 0), /* I2C0 */
+ [MSTP930] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR9, 30, MSTPSR9, 0), /* I2C1 */
+ [MSTP929] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR9, 29, MSTPSR9, 0), /* I2C2 */
+ [MSTP928] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR9, 28, MSTPSR9, 0), /* I2C3 */
+ [MSTP927] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR9, 27, MSTPSR9, 0), /* I2C4 */
+ [MSTP925] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR9, 25, MSTPSR9, 0), /* I2C5 */
+ [MSTP917] = SH_CLK_MSTP32_STS(&qspi_clk, SMSTPCR9, 17, MSTPSR9, 0), /* QSPI */
+ [MSTP815] = SH_CLK_MSTP32_STS(&zs_clk, SMSTPCR8, 15, MSTPSR8, 0), /* SATA0 */
+ [MSTP814] = SH_CLK_MSTP32_STS(&zs_clk, SMSTPCR8, 14, MSTPSR8, 0), /* SATA1 */
+ [MSTP813] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR8, 13, MSTPSR8, 0), /* Ether */
+ [MSTP811] = SH_CLK_MSTP32_STS(&zg_clk, SMSTPCR8, 11, MSTPSR8, 0), /* VIN0 */
+ [MSTP810] = SH_CLK_MSTP32_STS(&zg_clk, SMSTPCR8, 10, MSTPSR8, 0), /* VIN1 */
+ [MSTP809] = SH_CLK_MSTP32_STS(&zg_clk, SMSTPCR8, 9, MSTPSR8, 0), /* VIN2 */
+ [MSTP726] = SH_CLK_MSTP32_STS(&zx_clk, SMSTPCR7, 26, MSTPSR7, 0), /* LVDS0 */
+ [MSTP724] = SH_CLK_MSTP32_STS(&zx_clk, SMSTPCR7, 24, MSTPSR7, 0), /* DU0 */
+ [MSTP723] = SH_CLK_MSTP32_STS(&zx_clk, SMSTPCR7, 23, MSTPSR7, 0), /* DU1 */
+ [MSTP721] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR7, 21, MSTPSR7, 0), /* SCIF0 */
+ [MSTP720] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR7, 20, MSTPSR7, 0), /* SCIF1 */
+ [MSTP719] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR7, 19, MSTPSR7, 0), /* SCIF2 */
+ [MSTP718] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR7, 18, MSTPSR7, 0), /* SCIF3 */
+ [MSTP715] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR7, 15, MSTPSR7, 0), /* SCIF4 */
+ [MSTP714] = SH_CLK_MSTP32_STS(&p_clk, SMSTPCR7, 14, MSTPSR7, 0), /* SCIF5 */
+ [MSTP522] = SH_CLK_MSTP32_STS(&extal_clk, SMSTPCR5, 22, MSTPSR5, 0), /* Thermal */
+ [MSTP314] = SH_CLK_MSTP32_STS(&div4_clks[DIV4_SD0], SMSTPCR3, 14, MSTPSR3, 0), /* SDHI0 */
+ [MSTP312] = SH_CLK_MSTP32_STS(&div6_clks[DIV6_SD1], SMSTPCR3, 12, MSTPSR3, 0), /* SDHI1 */
+ [MSTP311] = SH_CLK_MSTP32_STS(&div6_clks[DIV6_SD2], SMSTPCR3, 11, MSTPSR3, 0), /* SDHI2 */
+ [MSTP216] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 16, MSTPSR2, 0), /* SCIFB2 */
+ [MSTP207] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 7, MSTPSR2, 0), /* SCIFB1 */
+ [MSTP206] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 6, MSTPSR2, 0), /* SCIFB0 */
+ [MSTP204] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 4, MSTPSR2, 0), /* SCIFA0 */
+ [MSTP203] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 3, MSTPSR2, 0), /* SCIFA1 */
+ [MSTP202] = SH_CLK_MSTP32_STS(&mp_clk, SMSTPCR2, 2, MSTPSR2, 0), /* SCIFA2 */
+ [MSTP124] = SH_CLK_MSTP32_STS(&rclk_clk, SMSTPCR1, 24, MSTPSR1, 0), /* CMT0 */
};
static struct clk_lookup lookups[] = {
@@ -165,8 +234,11 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("pll1", &pll1_clk),
CLKDEV_CON_ID("pll1_div2", &pll1_div2_clk),
CLKDEV_CON_ID("pll3", &pll3_clk),
+ CLKDEV_CON_ID("zg", &zg_clk),
+ CLKDEV_CON_ID("zs", &zs_clk),
CLKDEV_CON_ID("hp", &hp_clk),
CLKDEV_CON_ID("p", &p_clk),
+ CLKDEV_CON_ID("qspi", &qspi_clk),
CLKDEV_CON_ID("rclk", &rclk_clk),
CLKDEV_CON_ID("mp", &mp_clk),
CLKDEV_CON_ID("cp", &cp_clk),
@@ -188,13 +260,27 @@ static struct clk_lookup lookups[] = {
CLKDEV_DEV_ID("sh-sci.9", &mstp_clks[MSTP718]), /* SCIF3 */
CLKDEV_DEV_ID("sh-sci.10", &mstp_clks[MSTP715]), /* SCIF4 */
CLKDEV_DEV_ID("sh-sci.11", &mstp_clks[MSTP714]), /* SCIF5 */
- CLKDEV_DEV_ID("sh-sci.12", &mstp_clks[MSTP1105]), /* SCIFA3 */
- CLKDEV_DEV_ID("sh-sci.13", &mstp_clks[MSTP1106]), /* SCIFA4 */
- CLKDEV_DEV_ID("sh-sci.14", &mstp_clks[MSTP1107]), /* SCIFA5 */
+ CLKDEV_DEV_ID("sh-sci.12", &mstp_clks[MSTP1106]), /* SCIFA3 */
+ CLKDEV_DEV_ID("sh-sci.13", &mstp_clks[MSTP1107]), /* SCIFA4 */
+ CLKDEV_DEV_ID("sh-sci.14", &mstp_clks[MSTP1108]), /* SCIFA5 */
+ CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP314]),
+ CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP312]),
+ CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP311]),
CLKDEV_DEV_ID("sh_cmt.0", &mstp_clks[MSTP124]),
- CLKDEV_DEV_ID("e61f0000.thermal", &mstp_clks[MSTP522]),
+ CLKDEV_DEV_ID("qspi.0", &mstp_clks[MSTP917]),
CLKDEV_DEV_ID("rcar_thermal", &mstp_clks[MSTP522]),
+ CLKDEV_DEV_ID("i2c-rcar_gen2.0", &mstp_clks[MSTP931]),
+ CLKDEV_DEV_ID("i2c-rcar_gen2.1", &mstp_clks[MSTP930]),
+ CLKDEV_DEV_ID("i2c-rcar_gen2.2", &mstp_clks[MSTP929]),
+ CLKDEV_DEV_ID("i2c-rcar_gen2.3", &mstp_clks[MSTP928]),
+ CLKDEV_DEV_ID("i2c-rcar_gen2.4", &mstp_clks[MSTP927]),
+ CLKDEV_DEV_ID("i2c-rcar_gen2.5", &mstp_clks[MSTP925]),
CLKDEV_DEV_ID("r8a7791-ether", &mstp_clks[MSTP813]), /* Ether */
+ CLKDEV_DEV_ID("r8a7791-vin.0", &mstp_clks[MSTP811]),
+ CLKDEV_DEV_ID("r8a7791-vin.1", &mstp_clks[MSTP810]),
+ CLKDEV_DEV_ID("r8a7791-vin.2", &mstp_clks[MSTP809]),
+ CLKDEV_DEV_ID("sata-r8a7791.0", &mstp_clks[MSTP815]),
+ CLKDEV_DEV_ID("sata-r8a7791.1", &mstp_clks[MSTP814]),
};
#define R8A7791_CLOCK_ROOT(e, m, p0, p1, p30, p31) \
@@ -232,10 +318,21 @@ void __init r8a7791_clock_init(void)
break;
}
+ if ((mode & (MD(3) | MD(2) | MD(1))) == MD(2))
+ SH_CLK_SET_RATIO(&qspi_clk_ratio, 1, 16);
+ else
+ SH_CLK_SET_RATIO(&qspi_clk_ratio, 1, 20);
+
for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
ret = clk_register(main_clks[k]);
if (!ret)
+ ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table);
+
+ if (!ret)
+ ret = sh_clk_div6_register(div6_clks, DIV6_NR);
+
+ if (!ret)
ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
diff --git a/arch/arm/mach-shmobile/include/mach/common.h b/arch/arm/mach-shmobile/include/mach/common.h
index e31980590eb4..cb8e32deb2a3 100644
--- a/arch/arm/mach-shmobile/include/mach/common.h
+++ b/arch/arm/mach-shmobile/include/mach/common.h
@@ -25,7 +25,6 @@ extern int shmobile_smp_apmu_boot_secondary(unsigned int cpu,
struct task_struct *idle);
extern void shmobile_smp_apmu_cpu_die(unsigned int cpu);
extern int shmobile_smp_apmu_cpu_kill(unsigned int cpu);
-extern void shmobile_invalidate_start(void);
struct clk;
extern int shmobile_clk_init(void);
extern void shmobile_handle_irq_intc(struct pt_regs *);
diff --git a/arch/arm/mach-shmobile/include/mach/head-kzm9g.txt b/arch/arm/mach-shmobile/include/mach/head-kzm9g.txt
new file mode 100644
index 000000000000..9531f46a822a
--- /dev/null
+++ b/arch/arm/mach-shmobile/include/mach/head-kzm9g.txt
@@ -0,0 +1,410 @@
+LIST "KZM9G low-level initialization routine."
+LIST "Adapted from u-boot KZM9G support code."
+
+LIST "Copyright (C) 2013 Ulrich Hecht"
+
+LIST "This program is free software; you can redistribute it and/or modify"
+LIST "it under the terms of the GNU General Public License version 2 as"
+LIST "published by the Free Software Foundation."
+
+LIST "This program is distributed in the hope that it will be useful,"
+LIST "but WITHOUT ANY WARRANTY; without even the implied warranty of"
+LIST "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"
+LIST "GNU General Public License for more details."
+
+
+LIST "Register definitions:"
+
+LIST "Secure control register"
+#define LIFEC_SEC_SRC (0xE6110008)
+
+LIST "RWDT"
+#define RWDT_BASE (0xE6020000)
+#define RWTCSRA0 (RWDT_BASE + 0x04)
+
+LIST "HPB Semaphore Control Registers"
+#define HPBSCR_BASE (0xE6000000)
+#define HPBCTRL6 (HPBSCR_BASE + 0x1030)
+
+#define SBSC1_BASE (0xFE400000)
+#define SDCR0A (SBSC1_BASE + 0x0008)
+#define SDCR1A (SBSC1_BASE + 0x000C)
+#define SDPCRA (SBSC1_BASE + 0x0010)
+#define SDCR0SA (SBSC1_BASE + 0x0018)
+#define SDCR1SA (SBSC1_BASE + 0x001C)
+#define RTCSRA (SBSC1_BASE + 0x0020)
+#define RTCORA (SBSC1_BASE + 0x0028)
+#define RTCORHA (SBSC1_BASE + 0x002C)
+#define SDWCRC0A (SBSC1_BASE + 0x0040)
+#define SDWCRC1A (SBSC1_BASE + 0x0044)
+#define SDWCR00A (SBSC1_BASE + 0x0048)
+#define SDWCR01A (SBSC1_BASE + 0x004C)
+#define SDWCR10A (SBSC1_BASE + 0x0050)
+#define SDWCR11A (SBSC1_BASE + 0x0054)
+#define SDWCR2A (SBSC1_BASE + 0x0060)
+#define SDWCRC2A (SBSC1_BASE + 0x0064)
+#define ZQCCRA (SBSC1_BASE + 0x0068)
+#define SDMRACR0A (SBSC1_BASE + 0x0084)
+#define SDMRTMPCRA (SBSC1_BASE + 0x008C)
+#define SDMRTMPMSKA (SBSC1_BASE + 0x0094)
+#define SDGENCNTA (SBSC1_BASE + 0x009C)
+#define SDDRVCR0A (SBSC1_BASE + 0x00B4)
+#define DLLCNT0A (SBSC1_BASE + 0x0354)
+
+#define SDMRA1 (0xFE500000)
+#define SDMRA2 (0xFE5C0000)
+#define SDMRA3 (0xFE504000)
+
+#define SBSC2_BASE (0xFB400000)
+#define SDCR0B (SBSC2_BASE + 0x0008)
+#define SDCR1B (SBSC2_BASE + 0x000C)
+#define SDPCRB (SBSC2_BASE + 0x0010)
+#define SDCR0SB (SBSC2_BASE + 0x0018)
+#define SDCR1SB (SBSC2_BASE + 0x001C)
+#define RTCSRB (SBSC2_BASE + 0x0020)
+#define RTCORB (SBSC2_BASE + 0x0028)
+#define RTCORHB (SBSC2_BASE + 0x002C)
+#define SDWCRC0B (SBSC2_BASE + 0x0040)
+#define SDWCRC1B (SBSC2_BASE + 0x0044)
+#define SDWCR00B (SBSC2_BASE + 0x0048)
+#define SDWCR01B (SBSC2_BASE + 0x004C)
+#define SDWCR10B (SBSC2_BASE + 0x0050)
+#define SDWCR11B (SBSC2_BASE + 0x0054)
+#define SDPDCR0B (SBSC2_BASE + 0x0058)
+#define SDWCR2B (SBSC2_BASE + 0x0060)
+#define SDWCRC2B (SBSC2_BASE + 0x0064)
+#define ZQCCRB (SBSC2_BASE + 0x0068)
+#define SDMRACR0B (SBSC2_BASE + 0x0084)
+#define SDMRTMPCRB (SBSC2_BASE + 0x008C)
+#define SDMRTMPMSKB (SBSC2_BASE + 0x0094)
+#define SDGENCNTB (SBSC2_BASE + 0x009C)
+#define DPHYCNT0B (SBSC2_BASE + 0x00A0)
+#define DPHYCNT1B (SBSC2_BASE + 0x00A4)
+#define DPHYCNT2B (SBSC2_BASE + 0x00A8)
+#define SDDRVCR0B (SBSC2_BASE + 0x00B4)
+#define DLLCNT0B (SBSC2_BASE + 0x0354)
+
+#define SDMRB1 (0xFB500000)
+#define SDMRB2 (0xFB5C0000)
+#define SDMRB3 (0xFB504000)
+
+#define CPG_BASE (0xE6150000)
+#define FRQCRA (CPG_BASE + 0x0000)
+#define FRQCRB (CPG_BASE + 0x0004)
+#define FRQCRD (CPG_BASE + 0x00E4)
+#define VCLKCR1 (CPG_BASE + 0x0008)
+#define VCLKCR2 (CPG_BASE + 0x000C)
+#define VCLKCR3 (CPG_BASE + 0x001C)
+#define ZBCKCR (CPG_BASE + 0x0010)
+#define FLCKCR (CPG_BASE + 0x0014)
+#define SD0CKCR (CPG_BASE + 0x0074)
+#define SD1CKCR (CPG_BASE + 0x0078)
+#define SD2CKCR (CPG_BASE + 0x007C)
+#define FSIACKCR (CPG_BASE + 0x0018)
+#define SUBCKCR (CPG_BASE + 0x0080)
+#define SPUACKCR (CPG_BASE + 0x0084)
+#define SPUVCKCR (CPG_BASE + 0x0094)
+#define MSUCKCR (CPG_BASE + 0x0088)
+#define HSICKCR (CPG_BASE + 0x008C)
+#define FSIBCKCR (CPG_BASE + 0x0090)
+#define MFCK1CR (CPG_BASE + 0x0098)
+#define MFCK2CR (CPG_BASE + 0x009C)
+#define DSITCKCR (CPG_BASE + 0x0060)
+#define DSI0PCKCR (CPG_BASE + 0x0064)
+#define DSI1PCKCR (CPG_BASE + 0x0068)
+#define DSI0PHYCR (CPG_BASE + 0x006C)
+#define DVFSCR3 (CPG_BASE + 0x0174)
+#define DVFSCR4 (CPG_BASE + 0x0178)
+#define DVFSCR5 (CPG_BASE + 0x017C)
+#define MPMODE (CPG_BASE + 0x00CC)
+
+#define PLLECR (CPG_BASE + 0x00D0)
+#define PLL0CR (CPG_BASE + 0x00D8)
+#define PLL1CR (CPG_BASE + 0x0028)
+#define PLL2CR (CPG_BASE + 0x002C)
+#define PLL3CR (CPG_BASE + 0x00DC)
+#define PLL0STPCR (CPG_BASE + 0x00F0)
+#define PLL1STPCR (CPG_BASE + 0x00C8)
+#define PLL2STPCR (CPG_BASE + 0x00F8)
+#define PLL3STPCR (CPG_BASE + 0x00FC)
+#define RMSTPCR0 (CPG_BASE + 0x0110)
+#define RMSTPCR1 (CPG_BASE + 0x0114)
+#define RMSTPCR2 (CPG_BASE + 0x0118)
+#define RMSTPCR3 (CPG_BASE + 0x011C)
+#define RMSTPCR4 (CPG_BASE + 0x0120)
+#define RMSTPCR5 (CPG_BASE + 0x0124)
+#define SMSTPCR0 (CPG_BASE + 0x0130)
+#define SMSTPCR2 (CPG_BASE + 0x0138)
+#define SMSTPCR3 (CPG_BASE + 0x013C)
+#define CPGXXCR4 (CPG_BASE + 0x0150)
+#define SRCR0 (CPG_BASE + 0x80A0)
+#define SRCR2 (CPG_BASE + 0x80B0)
+#define SRCR3 (CPG_BASE + 0x80A8)
+#define VREFCR (CPG_BASE + 0x00EC)
+#define PCLKCR (CPG_BASE + 0x1020)
+
+#define PORT32CR (0xE6051020)
+#define PORT33CR (0xE6051021)
+#define PORT34CR (0xE6051022)
+#define PORT35CR (0xE6051023)
+
+LIST "DRAM initialization code:"
+
+EW RWTCSRA0, 0xA507
+
+ED_AND LIFEC_SEC_SRC, 0xFFFF7FFF
+
+ED_AND SMSTPCR3,0xFFFF7FFF
+ED_AND SRCR3, 0xFFFF7FFF
+ED_AND SMSTPCR2,0xFFFBFFFF
+ED_AND SRCR2, 0xFFFBFFFF
+ED PLLECR, 0x00000000
+
+WAIT_MASK PLLECR, 0x00000F00, 0x00000000
+WAIT_MASK FRQCRB, 0x80000000, 0x00000000
+
+ED PLL0CR, 0x2D000000
+ED PLL1CR, 0x17100000
+ED FRQCRB, 0x96235880
+WAIT_MASK FRQCRB, 0x80000000, 0x00000000
+
+ED FLCKCR, 0x0000000B
+ED_AND SMSTPCR0, 0xFFFFFFFD
+
+ED_AND SRCR0, 0xFFFFFFFD
+ED 0xE6001628, 0x514
+ED 0xE6001648, 0x514
+ED 0xE6001658, 0x514
+ED 0xE6001678, 0x514
+
+ED DVFSCR4, 0x00092000
+ED DVFSCR5, 0x000000DC
+ED PLLECR, 0x00000000
+WAIT_MASK PLLECR, 0x00000F00, 0x00000000
+
+ED FRQCRA, 0x0012453C
+ED FRQCRB, 0x80431350
+WAIT_MASK FRQCRB, 0x80000000, 0x00000000
+ED FRQCRD, 0x00000B0B
+WAIT_MASK FRQCRD, 0x80000000, 0x00000000
+
+ED PCLKCR, 0x00000003
+ED VCLKCR1, 0x0000012F
+ED VCLKCR2, 0x00000119
+ED VCLKCR3, 0x00000119
+ED ZBCKCR, 0x00000002
+ED FLCKCR, 0x00000005
+ED SD0CKCR, 0x00000080
+ED SD1CKCR, 0x00000080
+ED SD2CKCR, 0x00000080
+ED FSIACKCR, 0x0000003F
+ED FSIBCKCR, 0x0000003F
+ED SUBCKCR, 0x00000080
+ED SPUACKCR, 0x0000000B
+ED SPUVCKCR, 0x0000000B
+ED MSUCKCR, 0x0000013F
+ED HSICKCR, 0x00000080
+ED MFCK1CR, 0x0000003F
+ED MFCK2CR, 0x0000003F
+ED DSITCKCR, 0x00000107
+ED DSI0PCKCR, 0x00000313
+ED DSI1PCKCR, 0x0000130D
+ED DSI0PHYCR, 0x2A800E0E
+ED PLL0CR, 0x1E000000
+ED PLL0CR, 0x2D000000
+ED PLL1CR, 0x17100000
+ED PLL2CR, 0x27000080
+ED PLL3CR, 0x1D000000
+ED PLL0STPCR, 0x00080000
+ED PLL1STPCR, 0x000120C0
+ED PLL2STPCR, 0x00012000
+ED PLL3STPCR, 0x00000030
+ED PLLECR, 0x0000000B
+WAIT_MASK PLLECR, 0x00000B00, 0x00000B00
+
+ED DVFSCR3, 0x000120F0
+ED MPMODE, 0x00000020
+ED VREFCR, 0x0000028A
+ED RMSTPCR0, 0xE4628087
+ED RMSTPCR1, 0xFFFFFFFF
+ED RMSTPCR2, 0x53FFFFFF
+ED RMSTPCR3, 0xFFFFFFFF
+ED RMSTPCR4, 0x00800D3D
+ED RMSTPCR5, 0xFFFFF3FF
+ED SMSTPCR2, 0x00000000
+ED SRCR2, 0x00040000
+ED_AND PLLECR, 0xFFFFFFF7
+WAIT_MASK PLLECR, 0x00000800, 0x00000000
+
+LIST "set SBSC operational"
+ED HPBCTRL6, 0x00000001
+WAIT_MASK HPBCTRL6, 0x00000001, 0x00000001
+
+LIST "set SBSC operating frequency"
+ED FRQCRD, 0x00001414
+WAIT_MASK FRQCRD, 0x80000000, 0x00000000
+ED PLL3CR, 0x1D000000
+ED_OR PLLECR, 0x00000008
+WAIT_MASK PLLECR, 0x00000800, 0x00000800
+
+LIST "enable DLL oscillation in DDRPHY"
+ED_OR DLLCNT0A, 0x00000002
+
+LIST "wait >= 100 ns"
+ED SDGENCNTA, 0x00000005
+WAIT_MASK SDGENCNTA, 0xFFFFFFFF, 0x00000000
+
+LIST "target LPDDR2 device settings"
+ED SDCR0A, 0xACC90159
+ED SDCR1A, 0x00010059
+ED SDWCRC0A, 0x50874114
+ED SDWCRC1A, 0x33199B37
+ED SDWCRC2A, 0x008F2313
+ED SDWCR00A, 0x31020707
+ED SDWCR01A, 0x0017040A
+ED SDWCR10A, 0x31020707
+ED SDWCR11A, 0x0017040A
+
+ED SDDRVCR0A, 0x055557ff
+
+ED SDWCR2A, 0x30000000
+
+LIST "drive CKE high"
+ED_OR SDPCRA, 0x00000080
+WAIT_MASK SDPCRA, 0x00000080, 0x00000080
+
+LIST "wait >= 200 us"
+ED SDGENCNTA, 0x00002710
+WAIT_MASK SDGENCNTA, 0xFFFFFFFF, 0x00000000
+
+LIST "issue reset command to LPDDR2 device"
+ED SDMRACR0A, 0x0000003F
+ED SDMRA1, 0x00000000
+
+LIST "wait >= 10 (or 1) us (docs inconsistent)"
+ED SDGENCNTA, 0x000001F4
+WAIT_MASK SDGENCNTA, 0xFFFFFFFF, 0x00000000
+
+LIST "MRW ZS initialization calibration command"
+ED SDMRACR0A, 0x0000FF0A
+ED SDMRA3, 0x00000000
+
+LIST "wait >= 1 us"
+ED SDGENCNTA, 0x00000032
+WAIT_MASK SDGENCNTA, 0xFFFFFFFF, 0x00000000
+
+LIST "specify operating mode in LPDDR2"
+ED SDMRACR0A, 0x00002201
+ED SDMRA1, 0x00000000
+ED SDMRACR0A, 0x00000402
+ED SDMRA1, 0x00000000
+ED SDMRACR0A, 0x00000203
+ED SDMRA1, 0x00000000
+
+LIST "initialize DDR interface"
+ED SDMRA2, 0x00000000
+
+LIST "temperature sensor control"
+ED SDMRTMPCRA, 0x88800004
+ED SDMRTMPMSKA,0x00000004
+
+LIST "auto-refreshing control"
+ED RTCORA, 0xA55A0032
+ED RTCORHA, 0xA55A000C
+ED RTCSRA, 0xA55A2048
+
+ED_OR SDCR0A, 0x00000800
+ED_OR SDCR1A, 0x00000400
+
+LIST "auto ZQ calibration control"
+ED ZQCCRA, 0xFFF20000
+
+ED_OR DLLCNT0B, 0x00000002
+ED SDGENCNTB, 0x00000005
+WAIT_MASK SDGENCNTB, 0xFFFFFFFF, 0x00000000
+
+ED SDCR0B, 0xACC90159
+ED SDCR1B, 0x00010059
+ED SDWCRC0B, 0x50874114
+ED SDWCRC1B, 0x33199B37
+ED SDWCRC2B, 0x008F2313
+ED SDWCR00B, 0x31020707
+ED SDWCR01B, 0x0017040A
+ED SDWCR10B, 0x31020707
+ED SDWCR11B, 0x0017040A
+ED SDDRVCR0B, 0x055557ff
+ED SDWCR2B, 0x30000000
+ED_OR SDPCRB, 0x00000080
+WAIT_MASK SDPCRB, 0x00000080, 0x00000080
+
+ED SDGENCNTB, 0x00002710
+WAIT_MASK SDGENCNTB, 0xFFFFFFFF, 0x00000000
+ED SDMRACR0B, 0x0000003F
+
+LIST "upstream u-boot writes to SDMRA1A for both SBSC 1 and 2, which does"
+LIST "not seem to make a lot of sense..."
+ED SDMRB1, 0x00000000
+
+ED SDGENCNTB, 0x000001F4
+WAIT_MASK SDGENCNTB, 0xFFFFFFFF, 0x00000000
+
+ED SDMRACR0B, 0x0000FF0A
+ED SDMRB3, 0x00000000
+ED SDGENCNTB, 0x00000032
+WAIT_MASK SDGENCNTB, 0xFFFFFFFF, 0x00000000
+
+ED SDMRACR0B, 0x00002201
+ED SDMRB1, 0x00000000
+ED SDMRACR0B, 0x00000402
+ED SDMRB1, 0x00000000
+ED SDMRACR0B, 0x00000203
+ED SDMRB1, 0x00000000
+ED SDMRB2, 0x00000000
+ED SDMRTMPCRB, 0x88800004
+ED SDMRTMPMSKB, 0x00000004
+ED RTCORB, 0xA55A0032
+ED RTCORHB, 0xA55A000C
+ED RTCSRB, 0xA55A2048
+ED_OR SDCR0B, 0x00000800
+ED_OR SDCR1B, 0x00000400
+ED ZQCCRB, 0xFFF20000
+ED_OR SDPDCR0B, 0x00030000
+ED DPHYCNT1B, 0xA5390000
+ED DPHYCNT0B, 0x00001200
+ED DPHYCNT1B, 0x07CE0000
+ED DPHYCNT0B, 0x00001247
+WAIT_MASK DPHYCNT2B, 0xFFFFFFFF, 0x07CE0000
+
+ED_AND SDPDCR0B, 0xFFFCFFFF
+
+ED FRQCRD, 0x00000B0B
+WAIT_MASK FRQCRD, 0x80000000, 0x00000000
+
+ED CPGXXCR4, 0xfffffffc
+
+LIST "Setup SCIF4 / workaround"
+EB PORT32CR, 0x12
+EB PORT33CR, 0x22
+EB PORT34CR, 0x12
+EB PORT35CR, 0x22
+
+EW 0xE6C80000, 0
+EB 0xE6C80004, 0x19
+EW 0xE6C80008, 0x0030
+EW 0xE6C80018, 0
+EW 0xE6C80030, 0x0014
+
+LIST "Magic to avoid hangs and corruption on DRAM writes."
+
+LIST "It has been observed that the system would most often hang while"
+LIST "decompressing the kernel, and if it didn't it would always write"
+LIST "a corrupt image to DRAM."
+LIST "This problem does not occur in u-boot, and the reason is that"
+LIST "u-boot performs an additional cache invalidation after setting up"
+LIST "the DRAM controller. Such an invalidation should not be necessary at"
+LIST "this point, and attempts at removing parts of the routine to arrive"
+LIST "at the minimal snippet of code necessary to avoid the DRAM stability"
+LIST "problem yielded the following:"
+
+MRC p15, 0, r0, c1, c0, 0
+MCR p15, 0, r0, c1, c0, 0
diff --git a/arch/arm/mach-shmobile/include/mach/pm-rcar.h b/arch/arm/mach-shmobile/include/mach/pm-rcar.h
new file mode 100644
index 000000000000..ef3a1ef628f1
--- /dev/null
+++ b/arch/arm/mach-shmobile/include/mach/pm-rcar.h
@@ -0,0 +1,15 @@
+#ifndef PM_RCAR_H
+#define PM_RCAR_H
+
+struct rcar_sysc_ch {
+ unsigned long chan_offs;
+ unsigned int chan_bit;
+ unsigned int isr_bit;
+};
+
+int rcar_sysc_power_down(struct rcar_sysc_ch *sysc_ch);
+int rcar_sysc_power_up(struct rcar_sysc_ch *sysc_ch);
+bool rcar_sysc_power_is_off(struct rcar_sysc_ch *sysc_ch);
+void __iomem *rcar_sysc_init(phys_addr_t base);
+
+#endif /* PM_RCAR_H */
diff --git a/arch/arm/mach-shmobile/include/mach/r8a7779.h b/arch/arm/mach-shmobile/include/mach/r8a7779.h
index b40e13631f6a..88eeceaf1088 100644
--- a/arch/arm/mach-shmobile/include/mach/r8a7779.h
+++ b/arch/arm/mach-shmobile/include/mach/r8a7779.h
@@ -3,6 +3,7 @@
#include <linux/sh_clk.h>
#include <linux/pm_domain.h>
+#include <mach/pm-rcar.h>
/* HPB-DMA slave IDs */
enum {
@@ -11,18 +12,12 @@ enum {
HPBDMA_SLAVE_SDHI0_RX,
};
-struct r8a7779_pm_ch {
- unsigned long chan_offs;
- unsigned int chan_bit;
- unsigned int isr_bit;
-};
-
struct r8a7779_pm_domain {
struct generic_pm_domain genpd;
- struct r8a7779_pm_ch ch;
+ struct rcar_sysc_ch ch;
};
-static inline struct r8a7779_pm_ch *to_r8a7779_ch(struct generic_pm_domain *d)
+static inline struct rcar_sysc_ch *to_r8a7779_ch(struct generic_pm_domain *d)
{
return &container_of(d, struct r8a7779_pm_domain, genpd)->ch;
}
@@ -41,8 +36,6 @@ extern void r8a7779_clock_init(void);
extern void r8a7779_pinmux_init(void);
extern void r8a7779_pm_init(void);
extern void r8a7779_register_twd(void);
-extern int r8a7779_sysc_power_down(struct r8a7779_pm_ch *r8a7779_ch);
-extern int r8a7779_sysc_power_up(struct r8a7779_pm_ch *r8a7779_ch);
#ifdef CONFIG_PM
extern void __init r8a7779_init_pm_domains(void);
diff --git a/arch/arm/mach-shmobile/include/mach/r8a7790.h b/arch/arm/mach-shmobile/include/mach/r8a7790.h
index 5fbfa28b40b6..0b95babe84ba 100644
--- a/arch/arm/mach-shmobile/include/mach/r8a7790.h
+++ b/arch/arm/mach-shmobile/include/mach/r8a7790.h
@@ -3,10 +3,36 @@
#include <mach/rcar-gen2.h>
+/* DMA slave IDs */
+enum {
+ RCAR_DMA_SLAVE_INVALID,
+ AUDIO_DMAC_SLAVE_SSI0_TX,
+ AUDIO_DMAC_SLAVE_SSI0_RX,
+ AUDIO_DMAC_SLAVE_SSI1_TX,
+ AUDIO_DMAC_SLAVE_SSI1_RX,
+ AUDIO_DMAC_SLAVE_SSI2_TX,
+ AUDIO_DMAC_SLAVE_SSI2_RX,
+ AUDIO_DMAC_SLAVE_SSI3_TX,
+ AUDIO_DMAC_SLAVE_SSI3_RX,
+ AUDIO_DMAC_SLAVE_SSI4_TX,
+ AUDIO_DMAC_SLAVE_SSI4_RX,
+ AUDIO_DMAC_SLAVE_SSI5_TX,
+ AUDIO_DMAC_SLAVE_SSI5_RX,
+ AUDIO_DMAC_SLAVE_SSI6_TX,
+ AUDIO_DMAC_SLAVE_SSI6_RX,
+ AUDIO_DMAC_SLAVE_SSI7_TX,
+ AUDIO_DMAC_SLAVE_SSI7_RX,
+ AUDIO_DMAC_SLAVE_SSI8_TX,
+ AUDIO_DMAC_SLAVE_SSI8_RX,
+ AUDIO_DMAC_SLAVE_SSI9_TX,
+ AUDIO_DMAC_SLAVE_SSI9_RX,
+};
+
void r8a7790_add_standard_devices(void);
void r8a7790_add_dt_devices(void);
void r8a7790_clock_init(void);
void r8a7790_pinmux_init(void);
+void r8a7790_pm_init(void);
void r8a7790_init_early(void);
extern struct smp_operations r8a7790_smp_ops;
diff --git a/arch/arm/mach-shmobile/include/mach/timex.h b/arch/arm/mach-shmobile/include/mach/timex.h
deleted file mode 100644
index ae0d8d825c23..000000000000
--- a/arch/arm/mach-shmobile/include/mach/timex.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_MACH_TIMEX_H
-#define __ASM_MACH_TIMEX_H
-
-#define CLOCK_TICK_RATE 1193180 /* unused i8253 PIT value */
-
-#endif /* __ASM_MACH_TIMEX_H */
diff --git a/arch/arm/mach-shmobile/include/mach/zboot.h b/arch/arm/mach-shmobile/include/mach/zboot.h
index c3c4669a2d72..727cc78ac8ec 100644
--- a/arch/arm/mach-shmobile/include/mach/zboot.h
+++ b/arch/arm/mach-shmobile/include/mach/zboot.h
@@ -12,6 +12,9 @@
#ifdef CONFIG_MACH_MACKEREL
#define MEMORY_START 0x40000000
#include "mach/head-mackerel.txt"
+#elif defined(CONFIG_MACH_KZM9G) || defined(CONFIG_MACH_KZM9G_REFERENCE)
+#define MEMORY_START 0x43000000
+#include "mach/head-kzm9g.txt"
#else
#error "unsupported board."
#endif
diff --git a/arch/arm/mach-shmobile/include/mach/zboot_macros.h b/arch/arm/mach-shmobile/include/mach/zboot_macros.h
index aa6111fbc989..14fd3d538e9a 100644
--- a/arch/arm/mach-shmobile/include/mach/zboot_macros.h
+++ b/arch/arm/mach-shmobile/include/mach/zboot_macros.h
@@ -62,4 +62,47 @@
2 :
.endm
+/* loop until a given value has been read (with mask) */
+.macro WAIT_MASK, addr, data, cmp
+ LDR r0, 2f
+ LDR r1, 3f
+ LDR r2, 4f
+1:
+ LDR r3, [r0, #0]
+ AND r3, r1, r3
+ CMP r2, r3
+ BNE 1b
+ B 5f
+2: .long \addr
+3: .long \data
+4: .long \cmp
+5:
+.endm
+
+/* read 32-bit value from addr, "or" an immediate and write back */
+.macro ED_OR, addr, data
+ LDR r4, 1f
+ LDR r5, 2f
+ LDR r6, [r4]
+ ORR r5, r6, r5
+ STR r5, [r4]
+ B 3f
+1: .long \addr
+2: .long \data
+3:
+.endm
+
+/* read 32-bit value from addr, "and" an immediate and write back */
+.macro ED_AND, addr, data
+ LDR r4, 1f
+ LDR r5, 2f
+ LDR r6, [r4]
+ AND r5, r6, r5
+ STR r5, [r4]
+ B 3f
+1: .long \addr
+2: .long \data
+3:
+.endm
+
#endif /* __ZBOOT_MACRO_H */
diff --git a/arch/arm/mach-shmobile/platsmp-apmu.c b/arch/arm/mach-shmobile/platsmp-apmu.c
index 1da5a72d9642..8cb641c00fdb 100644
--- a/arch/arm/mach-shmobile/platsmp-apmu.c
+++ b/arch/arm/mach-shmobile/platsmp-apmu.c
@@ -75,8 +75,7 @@ static void apmu_init_cpu(struct resource *res, int cpu, int bit)
apmu_cpus[cpu].iomem = ioremap_nocache(res->start, resource_size(res));
apmu_cpus[cpu].bit = bit;
- pr_debug("apmu ioremap %d %d 0x%08x 0x%08x\n", cpu, bit,
- res->start, resource_size(res));
+ pr_debug("apmu ioremap %d %d %pr\n", cpu, bit, res);
}
static struct {
diff --git a/arch/arm/mach-shmobile/pm-r8a7779.c b/arch/arm/mach-shmobile/pm-r8a7779.c
index d50a8e9b94a4..d6fe189b2df6 100644
--- a/arch/arm/mach-shmobile/pm-r8a7779.c
+++ b/arch/arm/mach-shmobile/pm-r8a7779.c
@@ -20,132 +20,22 @@
#include <linux/console.h>
#include <asm/io.h>
#include <mach/common.h>
+#include <mach/pm-rcar.h>
#include <mach/r8a7779.h>
-static void __iomem *r8a7779_sysc_base;
-
/* SYSC */
-#define SYSCSR 0x00
-#define SYSCISR 0x04
-#define SYSCISCR 0x08
#define SYSCIER 0x0c
#define SYSCIMR 0x10
-#define PWRSR0 0x40
-#define PWRSR1 0x80
-#define PWRSR2 0xc0
-#define PWRSR3 0x100
-#define PWRSR4 0x140
-
-#define PWRSR_OFFS 0x00
-#define PWROFFCR_OFFS 0x04
-#define PWRONCR_OFFS 0x0c
-#define PWRER_OFFS 0x14
-
-#define SYSCSR_RETRIES 100
-#define SYSCSR_DELAY_US 1
-
-#define SYSCISR_RETRIES 1000
-#define SYSCISR_DELAY_US 1
#if defined(CONFIG_PM) || defined(CONFIG_SMP)
-static DEFINE_SPINLOCK(r8a7779_sysc_lock); /* SMP CPUs + I/O devices */
-
-static int r8a7779_sysc_pwr_on_off(struct r8a7779_pm_ch *r8a7779_ch,
- int sr_bit, int reg_offs)
-{
- int k;
-
- for (k = 0; k < SYSCSR_RETRIES; k++) {
- if (ioread32(r8a7779_sysc_base + SYSCSR) & (1 << sr_bit))
- break;
- udelay(SYSCSR_DELAY_US);
- }
-
- if (k == SYSCSR_RETRIES)
- return -EAGAIN;
-
- iowrite32(1 << r8a7779_ch->chan_bit,
- r8a7779_sysc_base + r8a7779_ch->chan_offs + reg_offs);
-
- return 0;
-}
-
-static int r8a7779_sysc_pwr_off(struct r8a7779_pm_ch *r8a7779_ch)
-{
- return r8a7779_sysc_pwr_on_off(r8a7779_ch, 0, PWROFFCR_OFFS);
-}
-
-static int r8a7779_sysc_pwr_on(struct r8a7779_pm_ch *r8a7779_ch)
-{
- return r8a7779_sysc_pwr_on_off(r8a7779_ch, 1, PWRONCR_OFFS);
-}
-
-static int r8a7779_sysc_update(struct r8a7779_pm_ch *r8a7779_ch,
- int (*on_off_fn)(struct r8a7779_pm_ch *))
-{
- unsigned int isr_mask = 1 << r8a7779_ch->isr_bit;
- unsigned int chan_mask = 1 << r8a7779_ch->chan_bit;
- unsigned int status;
- unsigned long flags;
- int ret = 0;
- int k;
-
- spin_lock_irqsave(&r8a7779_sysc_lock, flags);
-
- iowrite32(isr_mask, r8a7779_sysc_base + SYSCISCR);
-
- do {
- ret = on_off_fn(r8a7779_ch);
- if (ret)
- goto out;
-
- status = ioread32(r8a7779_sysc_base +
- r8a7779_ch->chan_offs + PWRER_OFFS);
- } while (status & chan_mask);
-
- for (k = 0; k < SYSCISR_RETRIES; k++) {
- if (ioread32(r8a7779_sysc_base + SYSCISR) & isr_mask)
- break;
- udelay(SYSCISR_DELAY_US);
- }
-
- if (k == SYSCISR_RETRIES)
- ret = -EIO;
-
- iowrite32(isr_mask, r8a7779_sysc_base + SYSCISCR);
-
- out:
- spin_unlock_irqrestore(&r8a7779_sysc_lock, flags);
-
- pr_debug("r8a7779 power domain %d: %02x %02x %02x %02x %02x -> %d\n",
- r8a7779_ch->isr_bit, ioread32(r8a7779_sysc_base + PWRSR0),
- ioread32(r8a7779_sysc_base + PWRSR1),
- ioread32(r8a7779_sysc_base + PWRSR2),
- ioread32(r8a7779_sysc_base + PWRSR3),
- ioread32(r8a7779_sysc_base + PWRSR4), ret);
- return ret;
-}
-
-int r8a7779_sysc_power_down(struct r8a7779_pm_ch *r8a7779_ch)
-{
- return r8a7779_sysc_update(r8a7779_ch, r8a7779_sysc_pwr_off);
-}
-
-int r8a7779_sysc_power_up(struct r8a7779_pm_ch *r8a7779_ch)
-{
- return r8a7779_sysc_update(r8a7779_ch, r8a7779_sysc_pwr_on);
-}
-
static void __init r8a7779_sysc_init(void)
{
- r8a7779_sysc_base = ioremap_nocache(0xffd85000, PAGE_SIZE);
- if (!r8a7779_sysc_base)
- panic("unable to ioremap r8a7779 SYSC hardware block\n");
+ void __iomem *base = rcar_sysc_init(0xffd85000);
/* enable all interrupt sources, but do not use interrupt handler */
- iowrite32(0x0131000e, r8a7779_sysc_base + SYSCIER);
- iowrite32(0, r8a7779_sysc_base + SYSCIMR);
+ iowrite32(0x0131000e, base + SYSCIER);
+ iowrite32(0, base + SYSCIMR);
}
#else /* CONFIG_PM || CONFIG_SMP */
@@ -158,24 +48,17 @@ static inline void r8a7779_sysc_init(void) {}
static int pd_power_down(struct generic_pm_domain *genpd)
{
- return r8a7779_sysc_power_down(to_r8a7779_ch(genpd));
+ return rcar_sysc_power_down(to_r8a7779_ch(genpd));
}
static int pd_power_up(struct generic_pm_domain *genpd)
{
- return r8a7779_sysc_power_up(to_r8a7779_ch(genpd));
+ return rcar_sysc_power_up(to_r8a7779_ch(genpd));
}
static bool pd_is_off(struct generic_pm_domain *genpd)
{
- struct r8a7779_pm_ch *r8a7779_ch = to_r8a7779_ch(genpd);
- unsigned int st;
-
- st = ioread32(r8a7779_sysc_base + r8a7779_ch->chan_offs + PWRSR_OFFS);
- if (st & (1 << r8a7779_ch->chan_bit))
- return true;
-
- return false;
+ return rcar_sysc_power_is_off(to_r8a7779_ch(genpd));
}
static bool pd_active_wakeup(struct device *dev)
diff --git a/arch/arm/mach-shmobile/pm-r8a7790.c b/arch/arm/mach-shmobile/pm-r8a7790.c
new file mode 100644
index 000000000000..fc82839e2c2a
--- /dev/null
+++ b/arch/arm/mach-shmobile/pm-r8a7790.c
@@ -0,0 +1,45 @@
+/*
+ * r8a7790 Power management support
+ *
+ * Copyright (C) 2013 Renesas Electronics Corporation
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Copyright (C) 2011 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <mach/pm-rcar.h>
+#include <mach/r8a7790.h>
+
+/* SYSC */
+#define SYSCIER 0x0c
+#define SYSCIMR 0x10
+
+#if defined(CONFIG_SMP)
+
+static void __init r8a7790_sysc_init(void)
+{
+ void __iomem *base = rcar_sysc_init(0xe6180000);
+
+ /* enable all interrupt sources, but do not use interrupt handler */
+ iowrite32(0x0131000e, base + SYSCIER);
+ iowrite32(0, base + SYSCIMR);
+}
+
+#else /* CONFIG_SMP */
+
+static inline void r8a7790_sysc_init(void) {}
+
+#endif /* CONFIG_SMP */
+
+void __init r8a7790_pm_init(void)
+{
+ static int once;
+
+ if (!once++)
+ r8a7790_sysc_init();
+}
diff --git a/arch/arm/mach-shmobile/pm-rcar.c b/arch/arm/mach-shmobile/pm-rcar.c
new file mode 100644
index 000000000000..1f465a12d1b1
--- /dev/null
+++ b/arch/arm/mach-shmobile/pm-rcar.c
@@ -0,0 +1,141 @@
+/*
+ * R-Car SYSC Power management support
+ *
+ * Copyright (C) 2014 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <mach/pm-rcar.h>
+
+/* SYSC */
+#define SYSCSR 0x00
+#define SYSCISR 0x04
+#define SYSCISCR 0x08
+
+#define PWRSR_OFFS 0x00
+#define PWROFFCR_OFFS 0x04
+#define PWRONCR_OFFS 0x0c
+#define PWRER_OFFS 0x14
+
+#define SYSCSR_RETRIES 100
+#define SYSCSR_DELAY_US 1
+
+#define SYSCISR_RETRIES 1000
+#define SYSCISR_DELAY_US 1
+
+#if defined(CONFIG_PM) || defined(CONFIG_SMP)
+
+static void __iomem *rcar_sysc_base;
+static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */
+
+static int rcar_sysc_pwr_on_off(struct rcar_sysc_ch *sysc_ch,
+ int sr_bit, int reg_offs)
+{
+ int k;
+
+ for (k = 0; k < SYSCSR_RETRIES; k++) {
+ if (ioread32(rcar_sysc_base + SYSCSR) & (1 << sr_bit))
+ break;
+ udelay(SYSCSR_DELAY_US);
+ }
+
+ if (k == SYSCSR_RETRIES)
+ return -EAGAIN;
+
+ iowrite32(1 << sysc_ch->chan_bit,
+ rcar_sysc_base + sysc_ch->chan_offs + reg_offs);
+
+ return 0;
+}
+
+static int rcar_sysc_pwr_off(struct rcar_sysc_ch *sysc_ch)
+{
+ return rcar_sysc_pwr_on_off(sysc_ch, 0, PWROFFCR_OFFS);
+}
+
+static int rcar_sysc_pwr_on(struct rcar_sysc_ch *sysc_ch)
+{
+ return rcar_sysc_pwr_on_off(sysc_ch, 1, PWRONCR_OFFS);
+}
+
+static int rcar_sysc_update(struct rcar_sysc_ch *sysc_ch,
+ int (*on_off_fn)(struct rcar_sysc_ch *))
+{
+ unsigned int isr_mask = 1 << sysc_ch->isr_bit;
+ unsigned int chan_mask = 1 << sysc_ch->chan_bit;
+ unsigned int status;
+ unsigned long flags;
+ int ret = 0;
+ int k;
+
+ spin_lock_irqsave(&rcar_sysc_lock, flags);
+
+ iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);
+
+ do {
+ ret = on_off_fn(sysc_ch);
+ if (ret)
+ goto out;
+
+ status = ioread32(rcar_sysc_base +
+ sysc_ch->chan_offs + PWRER_OFFS);
+ } while (status & chan_mask);
+
+ for (k = 0; k < SYSCISR_RETRIES; k++) {
+ if (ioread32(rcar_sysc_base + SYSCISR) & isr_mask)
+ break;
+ udelay(SYSCISR_DELAY_US);
+ }
+
+ if (k == SYSCISR_RETRIES)
+ ret = -EIO;
+
+ iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);
+
+ out:
+ spin_unlock_irqrestore(&rcar_sysc_lock, flags);
+
+ pr_debug("sysc power domain %d: %08x -> %d\n",
+ sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret);
+ return ret;
+}
+
+int rcar_sysc_power_down(struct rcar_sysc_ch *sysc_ch)
+{
+ return rcar_sysc_update(sysc_ch, rcar_sysc_pwr_off);
+}
+
+int rcar_sysc_power_up(struct rcar_sysc_ch *sysc_ch)
+{
+ return rcar_sysc_update(sysc_ch, rcar_sysc_pwr_on);
+}
+
+bool rcar_sysc_power_is_off(struct rcar_sysc_ch *sysc_ch)
+{
+ unsigned int st;
+
+ st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS);
+ if (st & (1 << sysc_ch->chan_bit))
+ return true;
+
+ return false;
+}
+
+void __iomem *rcar_sysc_init(phys_addr_t base)
+{
+ rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE);
+ if (!rcar_sysc_base)
+ panic("unable to ioremap R-Car SYSC hardware block\n");
+
+ return rcar_sysc_base;
+}
+
+#endif /* CONFIG_PM || CONFIG_SMP */
diff --git a/arch/arm/mach-shmobile/setup-emev2.c b/arch/arm/mach-shmobile/setup-emev2.c
index c8f2a1a69a52..c71d667007b8 100644
--- a/arch/arm/mach-shmobile/setup-emev2.c
+++ b/arch/arm/mach-shmobile/setup-emev2.c
@@ -58,7 +58,7 @@ static void __init emev2_add_standard_devices_dt(void)
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
-static const char *emev2_boards_compat_dt[] __initdata = {
+static const char *emev2_boards_compat_dt[] __initconst = {
"renesas,emev2",
NULL,
};
diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c b/arch/arm/mach-shmobile/setup-r8a7790.c
index 6ab37aa1e919..c4616f0698c6 100644
--- a/arch/arm/mach-shmobile/setup-r8a7790.c
+++ b/arch/arm/mach-shmobile/setup-r8a7790.c
@@ -24,12 +24,100 @@
#include <linux/platform_data/gpio-rcar.h>
#include <linux/platform_data/irq-renesas-irqc.h>
#include <linux/serial_sci.h>
+#include <linux/sh_dma.h>
#include <linux/sh_timer.h>
#include <mach/common.h>
+#include <mach/dma-register.h>
#include <mach/irqs.h>
#include <mach/r8a7790.h>
#include <asm/mach/arch.h>
+/* Audio-DMAC */
+#define AUDIO_DMAC_SLAVE(_id, _addr, t, r) \
+{ \
+ .slave_id = AUDIO_DMAC_SLAVE_## _id ##_TX, \
+ .addr = _addr + 0x8, \
+ .chcr = CHCR_TX(XMIT_SZ_32BIT), \
+ .mid_rid = t, \
+}, { \
+ .slave_id = AUDIO_DMAC_SLAVE_## _id ##_RX, \
+ .addr = _addr + 0xc, \
+ .chcr = CHCR_RX(XMIT_SZ_32BIT), \
+ .mid_rid = r, \
+}
+
+static const struct sh_dmae_slave_config r8a7790_audio_dmac_slaves[] = {
+ AUDIO_DMAC_SLAVE(SSI0, 0xec241000, 0x01, 0x02),
+ AUDIO_DMAC_SLAVE(SSI1, 0xec241040, 0x03, 0x04),
+ AUDIO_DMAC_SLAVE(SSI2, 0xec241080, 0x05, 0x06),
+ AUDIO_DMAC_SLAVE(SSI3, 0xec2410c0, 0x07, 0x08),
+ AUDIO_DMAC_SLAVE(SSI4, 0xec241100, 0x09, 0x0a),
+ AUDIO_DMAC_SLAVE(SSI5, 0xec241140, 0x0b, 0x0c),
+ AUDIO_DMAC_SLAVE(SSI6, 0xec241180, 0x0d, 0x0e),
+ AUDIO_DMAC_SLAVE(SSI7, 0xec2411c0, 0x0f, 0x10),
+ AUDIO_DMAC_SLAVE(SSI8, 0xec241200, 0x11, 0x12),
+ AUDIO_DMAC_SLAVE(SSI9, 0xec241240, 0x13, 0x14),
+};
+
+#define DMAE_CHANNEL(a, b) \
+{ \
+ .offset = (a) - 0x20, \
+ .dmars = (a) - 0x20 + 0x40, \
+ .chclr_bit = (b), \
+ .chclr_offset = 0x80 - 0x20, \
+}
+
+static const struct sh_dmae_channel r8a7790_audio_dmac_channels[] = {
+ DMAE_CHANNEL(0x8000, 0),
+ DMAE_CHANNEL(0x8080, 1),
+ DMAE_CHANNEL(0x8100, 2),
+ DMAE_CHANNEL(0x8180, 3),
+ DMAE_CHANNEL(0x8200, 4),
+ DMAE_CHANNEL(0x8280, 5),
+ DMAE_CHANNEL(0x8300, 6),
+ DMAE_CHANNEL(0x8380, 7),
+ DMAE_CHANNEL(0x8400, 8),
+ DMAE_CHANNEL(0x8480, 9),
+ DMAE_CHANNEL(0x8500, 10),
+ DMAE_CHANNEL(0x8580, 11),
+ DMAE_CHANNEL(0x8600, 12),
+};
+
+static struct sh_dmae_pdata r8a7790_audio_dmac_platform_data = {
+ .slave = r8a7790_audio_dmac_slaves,
+ .slave_num = ARRAY_SIZE(r8a7790_audio_dmac_slaves),
+ .channel = r8a7790_audio_dmac_channels,
+ .channel_num = ARRAY_SIZE(r8a7790_audio_dmac_channels),
+ .ts_low_shift = TS_LOW_SHIFT,
+ .ts_low_mask = TS_LOW_BIT << TS_LOW_SHIFT,
+ .ts_high_shift = TS_HI_SHIFT,
+ .ts_high_mask = TS_HI_BIT << TS_HI_SHIFT,
+ .ts_shift = dma_ts_shift,
+ .ts_shift_num = ARRAY_SIZE(dma_ts_shift),
+ .dmaor_init = DMAOR_DME,
+ .chclr_present = 1,
+ .chclr_bitwise = 1,
+};
+
+static struct resource r8a7790_audio_dmac_resources[] = {
+ /* Channel registers and DMAOR for low */
+ DEFINE_RES_MEM(0xec700020, 0x8663 - 0x20),
+ DEFINE_RES_IRQ(gic_spi(346)),
+ DEFINE_RES_NAMED(gic_spi(320), 13, NULL, IORESOURCE_IRQ),
+
+ /* Channel registers and DMAOR for hi */
+ DEFINE_RES_MEM(0xec720020, 0x8663 - 0x20), /* hi */
+ DEFINE_RES_IRQ(gic_spi(347)),
+ DEFINE_RES_NAMED(gic_spi(333), 13, NULL, IORESOURCE_IRQ),
+};
+
+#define r8a7790_register_audio_dmac(id) \
+ platform_device_register_resndata( \
+ &platform_bus, "sh-dma-engine", id, \
+ &r8a7790_audio_dmac_resources[id * 3], 3, \
+ &r8a7790_audio_dmac_platform_data, \
+ sizeof(r8a7790_audio_dmac_platform_data))
+
static const struct resource pfc_resources[] __initconst = {
DEFINE_RES_MEM(0xe6060000, 0x250),
};
@@ -101,6 +189,8 @@ void __init r8a7790_pinmux_init(void)
r8a7790_register_i2c(1);
r8a7790_register_i2c(2);
r8a7790_register_i2c(3);
+ r8a7790_register_audio_dmac(0);
+ r8a7790_register_audio_dmac(1);
}
#define __R8A7790_SCIF(scif_type, _scscr, index, baseaddr, irq) \
diff --git a/arch/arm/mach-shmobile/setup-rcar-gen2.c b/arch/arm/mach-shmobile/setup-rcar-gen2.c
index 69ccc6c6fd33..10604480f325 100644
--- a/arch/arm/mach-shmobile/setup-rcar-gen2.c
+++ b/arch/arm/mach-shmobile/setup-rcar-gen2.c
@@ -28,7 +28,7 @@
#define MODEMR 0xe6160060
-u32 __init rcar_gen2_read_mode_pins(void)
+u32 rcar_gen2_read_mode_pins(void)
{
void __iomem *modemr = ioremap_nocache(MODEMR, 4);
u32 mode;
diff --git a/arch/arm/mach-shmobile/smp-r8a7779.c b/arch/arm/mach-shmobile/smp-r8a7779.c
index 627c1f0d9478..e7a3201473d0 100644
--- a/arch/arm/mach-shmobile/smp-r8a7779.c
+++ b/arch/arm/mach-shmobile/smp-r8a7779.c
@@ -24,6 +24,7 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <mach/common.h>
+#include <mach/pm-rcar.h>
#include <mach/r8a7779.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
@@ -33,25 +34,25 @@
#define AVECR IOMEM(0xfe700040)
#define R8A7779_SCU_BASE 0xf0000000
-static struct r8a7779_pm_ch r8a7779_ch_cpu1 = {
+static struct rcar_sysc_ch r8a7779_ch_cpu1 = {
.chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
.chan_bit = 1, /* ARM1 */
.isr_bit = 1, /* ARM1 */
};
-static struct r8a7779_pm_ch r8a7779_ch_cpu2 = {
+static struct rcar_sysc_ch r8a7779_ch_cpu2 = {
.chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
.chan_bit = 2, /* ARM2 */
.isr_bit = 2, /* ARM2 */
};
-static struct r8a7779_pm_ch r8a7779_ch_cpu3 = {
+static struct rcar_sysc_ch r8a7779_ch_cpu3 = {
.chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
.chan_bit = 3, /* ARM3 */
.isr_bit = 3, /* ARM3 */
};
-static struct r8a7779_pm_ch *r8a7779_ch_cpu[4] = {
+static struct rcar_sysc_ch *r8a7779_ch_cpu[4] = {
[1] = &r8a7779_ch_cpu1,
[2] = &r8a7779_ch_cpu2,
[3] = &r8a7779_ch_cpu3,
@@ -67,7 +68,7 @@ void __init r8a7779_register_twd(void)
static int r8a7779_platform_cpu_kill(unsigned int cpu)
{
- struct r8a7779_pm_ch *ch = NULL;
+ struct rcar_sysc_ch *ch = NULL;
int ret = -EIO;
cpu = cpu_logical_map(cpu);
@@ -76,14 +77,14 @@ static int r8a7779_platform_cpu_kill(unsigned int cpu)
ch = r8a7779_ch_cpu[cpu];
if (ch)
- ret = r8a7779_sysc_power_down(ch);
+ ret = rcar_sysc_power_down(ch);
return ret ? ret : 1;
}
static int r8a7779_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
- struct r8a7779_pm_ch *ch = NULL;
+ struct rcar_sysc_ch *ch = NULL;
unsigned int lcpu = cpu_logical_map(cpu);
int ret;
@@ -91,7 +92,7 @@ static int r8a7779_boot_secondary(unsigned int cpu, struct task_struct *idle)
ch = r8a7779_ch_cpu[lcpu];
if (ch)
- ret = r8a7779_sysc_power_up(ch);
+ ret = rcar_sysc_power_up(ch);
else
ret = -EIO;
diff --git a/arch/arm/mach-shmobile/smp-r8a7790.c b/arch/arm/mach-shmobile/smp-r8a7790.c
index 015e2753de1f..591052799e8f 100644
--- a/arch/arm/mach-shmobile/smp-r8a7790.c
+++ b/arch/arm/mach-shmobile/smp-r8a7790.c
@@ -19,6 +19,8 @@
#include <linux/io.h>
#include <asm/smp_plat.h>
#include <mach/common.h>
+#include <mach/pm-rcar.h>
+#include <mach/r8a7790.h>
#define RST 0xe6160000
#define CA15BAR 0x0020
@@ -27,6 +29,16 @@
#define CA7RESCNT 0x0044
#define MERAM 0xe8080000
+static struct rcar_sysc_ch r8a7790_ca15_scu = {
+ .chan_offs = 0x180, /* PWRSR5 .. PWRER5 */
+ .isr_bit = 12, /* CA15-SCU */
+};
+
+static struct rcar_sysc_ch r8a7790_ca7_scu = {
+ .chan_offs = 0x100, /* PWRSR3 .. PWRER3 */
+ .isr_bit = 21, /* CA7-SCU */
+};
+
static void __init r8a7790_smp_prepare_cpus(unsigned int max_cpus)
{
void __iomem *p;
@@ -54,6 +66,11 @@ static void __init r8a7790_smp_prepare_cpus(unsigned int max_cpus)
writel_relaxed((readl_relaxed(p + CA7RESCNT) & ~0x0f) | 0x5a5a0000,
p + CA7RESCNT);
iounmap(p);
+
+ /* turn on power to SCU */
+ r8a7790_pm_init();
+ rcar_sysc_power_up(&r8a7790_ca15_scu);
+ rcar_sysc_power_up(&r8a7790_ca7_scu);
}
struct smp_operations r8a7790_smp_ops __initdata = {
diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig
index aee77f06f887..b5f8d75d51a0 100644
--- a/arch/arm/mach-socfpga/Kconfig
+++ b/arch/arm/mach-socfpga/Kconfig
@@ -1,17 +1,10 @@
config ARCH_SOCFPGA
bool "Altera SOCFPGA family" if ARCH_MULTI_V7
- select ARCH_WANT_OPTIONAL_GPIOLIB
select ARM_AMBA
select ARM_GIC
select CACHE_L2X0
- select COMMON_CLK
- select CPU_V7
select DW_APB_TIMER_OF
- select GENERIC_CLOCKEVENTS
select GPIO_PL061 if GPIOLIB
select HAVE_ARM_SCU
select HAVE_ARM_TWD if SMP
- select HAVE_SMP
select MFD_SYSCON
- select SPARSE_IRQ
- select USE_OF
diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c
index dd0d49cdbe09..d86231e11b34 100644
--- a/arch/arm/mach-socfpga/socfpga.c
+++ b/arch/arm/mach-socfpga/socfpga.c
@@ -29,7 +29,6 @@
void __iomem *socfpga_scu_base_addr = ((void __iomem *)(SOCFPGA_SCU_VIRT_BASE));
void __iomem *sys_manager_base_addr;
void __iomem *rst_manager_base_addr;
-void __iomem *clk_mgr_base_addr;
unsigned long cpu1start_addr;
static struct map_desc scu_io_desc __initdata = {
@@ -78,9 +77,6 @@ void __init socfpga_sysmgr_init(void)
np = of_find_compatible_node(NULL, NULL, "altr,rst-mgr");
rst_manager_base_addr = of_iomap(np, 0);
-
- np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr");
- clk_mgr_base_addr = of_iomap(np, 0);
}
static void __init socfpga_init_irq(void)
@@ -106,7 +102,6 @@ static void __init socfpga_cyclone5_init(void)
{
l2x0_of_init(0, ~0UL);
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
- socfpga_init_clocks();
}
static const char *altera_dt_match[] = {
diff --git a/arch/arm/mach-spear/Kconfig b/arch/arm/mach-spear/Kconfig
index ac1710e64d9a..0786249b2832 100644
--- a/arch/arm/mach-spear/Kconfig
+++ b/arch/arm/mach-spear/Kconfig
@@ -8,8 +8,6 @@ menuconfig PLAT_SPEAR
select ARCH_REQUIRE_GPIOLIB
select ARM_AMBA
select CLKSRC_MMIO
- select COMMON_CLK
- select GENERIC_CLOCKEVENTS
if PLAT_SPEAR
@@ -18,14 +16,10 @@ config ARCH_SPEAR13XX
depends on ARCH_MULTI_V7 || PLAT_SPEAR_SINGLE
select ARCH_HAS_CPUFREQ
select ARM_GIC
- select CPU_V7
select GPIO_SPEAR_SPICS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
- select HAVE_SMP
- select MIGHT_HAVE_CACHE_L2X0
select PINCTRL
- select USE_OF
help
Supports for ARM's SPEAR13XX family
@@ -50,9 +44,7 @@ config ARCH_SPEAR3XX
depends on ARCH_MULTI_V5 || PLAT_SPEAR_SINGLE
depends on !ARCH_SPEAR13XX
select ARM_VIC
- select CPU_ARM926T
select PINCTRL
- select USE_OF
help
Supports for ARM's SPEAR3XX family
@@ -83,16 +75,14 @@ config ARCH_SPEAR6XX
depends on ARCH_MULTI_V5 || PLAT_SPEAR_SINGLE
depends on !ARCH_SPEAR13XX
select ARM_VIC
- select CPU_ARM926T
help
Supports for ARM's SPEAR6XX family
config MACH_SPEAR600
def_bool y
depends on ARCH_SPEAR6XX
- select USE_OF
help
- Supports ST SPEAr600 boards configured via the device-treesource "arch/arm/mach-spear6xx/Kconfig"
+ Supports ST SPEAr600 boards configured via the device-tree
config ARCH_SPEAR_AUTO
def_bool PLAT_SPEAR_SINGLE
diff --git a/arch/arm/mach-spear/include/mach/timex.h b/arch/arm/mach-spear/include/mach/timex.h
deleted file mode 100644
index ef95e5b780bd..000000000000
--- a/arch/arm/mach-spear/include/mach/timex.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * arch/arm/plat-spear/include/plat/timex.h
- *
- * SPEAr platform specific timex definitions
- *
- * Copyright (C) 2009 ST Microelectronics
- * Viresh Kumar <viresh.linux@gmail.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.
- */
-
-#ifndef __PLAT_TIMEX_H
-#define __PLAT_TIMEX_H
-
-#define CLOCK_TICK_RATE 48000000
-
-#endif /* __PLAT_TIMEX_H */
diff --git a/arch/arm/mach-spear/time.c b/arch/arm/mach-spear/time.c
index d449673e40f7..218ba5b67d92 100644
--- a/arch/arm/mach-spear/time.c
+++ b/arch/arm/mach-spear/time.c
@@ -172,7 +172,7 @@ static irqreturn_t spear_timer_interrupt(int irq, void *dev_id)
static struct irqaction spear_timer_irq = {
.name = "timer",
- .flags = IRQF_DISABLED | IRQF_TIMER,
+ .flags = IRQF_TIMER,
.handler = spear_timer_interrupt
};
diff --git a/arch/arm/mach-sti/Kconfig b/arch/arm/mach-sti/Kconfig
index d71654bc8d54..abf9ee9bbc3f 100644
--- a/arch/arm/mach-sti/Kconfig
+++ b/arch/arm/mach-sti/Kconfig
@@ -1,14 +1,11 @@
menuconfig ARCH_STI
bool "STMicroelectronics Consumer Electronics SOCs with Device Trees" if ARCH_MULTI_V7
- select GENERIC_CLOCKEVENTS
- select CLKDEV_LOOKUP
select ARM_GIC
select ARM_GLOBAL_TIMER
select PINCTRL
select PINCTRL_ST
select MFD_SYSCON
- select MIGHT_HAVE_CACHE_L2X0
- select HAVE_SMP
+ select ARCH_HAS_RESET_CONTROLLER
select HAVE_ARM_SCU if SMP
select ARCH_REQUIRE_GPIOLIB
select ARM_ERRATA_754322
@@ -28,6 +25,7 @@ if ARCH_STI
config SOC_STIH415
bool "STiH415 STMicroelectronics Consumer Electronics family"
default y
+ select STIH415_RESET
help
This enables support for STMicroelectronics Digital Consumer
Electronics family StiH415 parts, primarily targeted at set-top-box
@@ -37,6 +35,7 @@ config SOC_STIH415
config SOC_STIH416
bool "STiH416 STMicroelectronics Consumer Electronics family"
default y
+ select STIH416_RESET
help
This enables support for STMicroelectronics Digital Consumer
Electronics family StiH416 parts, primarily targeted at set-top-box
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index b9d6cad8669b..b57d7d53b9d3 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -5,14 +5,10 @@ config ARCH_SUNXI
select ARM_GIC
select ARM_PSCI
select CLKSRC_MMIO
- select CLKSRC_OF
- select COMMON_CLK
- select GENERIC_CLOCKEVENTS
select GENERIC_IRQ_CHIP
- select HAVE_SMP
+ select HAVE_ARM_ARCH_TIMER
select PINCTRL
select PINCTRL_SUNXI
select RESET_CONTROLLER
- select SPARSE_IRQ
select SUN4I_TIMER
select SUN5I_HSTIMER
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index d9397202d6ec..27b168f121a1 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -1,2 +1,2 @@
obj-$(CONFIG_ARCH_SUNXI) += sunxi.o
-obj-$(CONFIG_SMP) += platsmp.o headsmp.o
+obj-$(CONFIG_SMP) += platsmp.o
diff --git a/arch/arm/mach-sunxi/headsmp.S b/arch/arm/mach-sunxi/headsmp.S
deleted file mode 100644
index a10d494fb37b..000000000000
--- a/arch/arm/mach-sunxi/headsmp.S
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <linux/linkage.h>
-#include <linux/init.h>
-
- .section ".text.head", "ax"
-
-ENTRY(sun6i_secondary_startup)
- msr cpsr_fsxc, #0xd3
- b secondary_startup
-ENDPROC(sun6i_secondary_startup)
diff --git a/arch/arm/mach-sunxi/platsmp.c b/arch/arm/mach-sunxi/platsmp.c
index 7b141d8342a1..0c7dbce033cc 100644
--- a/arch/arm/mach-sunxi/platsmp.c
+++ b/arch/arm/mach-sunxi/platsmp.c
@@ -82,7 +82,7 @@ static int sun6i_smp_boot_secondary(unsigned int cpu,
spin_lock(&cpu_lock);
/* Set CPU boot address */
- writel(virt_to_phys(sun6i_secondary_startup),
+ writel(virt_to_phys(secondary_startup),
cpucfg_membase + CPUCFG_PRIVATE0_REG);
/* Assert the CPU core in reset */
diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c
index aeea6ceea725..460b5a4962ef 100644
--- a/arch/arm/mach-sunxi/sunxi.c
+++ b/arch/arm/mach-sunxi/sunxi.c
@@ -94,8 +94,8 @@ static void sun6i_restart(enum reboot_mode mode, const char *cmd)
}
static struct of_device_id sunxi_restart_ids[] = {
- { .compatible = "allwinner,sun4i-wdt" },
- { .compatible = "allwinner,sun6i-wdt" },
+ { .compatible = "allwinner,sun4i-a10-wdt" },
+ { .compatible = "allwinner,sun6i-a31-wdt" },
{ /*sentinel*/ }
};
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index 4926bd11f190..92d660f9610f 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -5,23 +5,15 @@ config ARCH_TEGRA
select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS
select ARM_GIC
select CLKSRC_MMIO
- select CLKSRC_OF
- select COMMON_CLK
- select CPU_V7
- select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
- select HAVE_SMP
- select MIGHT_HAVE_CACHE_L2X0
select MIGHT_HAVE_PCI
select PINCTRL
select ARCH_HAS_RESET_CONTROLLER
select RESET_CONTROLLER
select SOC_BUS
- select SPARSE_IRQ
select USB_ULPI if USB_PHY
select USB_ULPI_VIEWPORT if USB_PHY
- select USE_OF
help
This enables support for NVIDIA Tegra based systems.
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 019bb1758662..6fbfbb77dcd9 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -14,7 +14,6 @@ obj-y += sleep.o
obj-y += tegra.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_speedo.o
-obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pm-tegra20.o
ifeq ($(CONFIG_CPU_IDLE),y)
diff --git a/arch/arm/mach-tegra/cpuidle-tegra114.c b/arch/arm/mach-tegra/cpuidle-tegra114.c
index e0b87300243d..b5fb7c110c64 100644
--- a/arch/arm/mach-tegra/cpuidle-tegra114.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra114.c
@@ -19,6 +19,7 @@
#include <linux/cpuidle.h>
#include <linux/cpu_pm.h>
#include <linux/clockchips.h>
+#include <asm/firmware.h>
#include <asm/cpuidle.h>
#include <asm/suspend.h>
@@ -45,7 +46,11 @@ static int tegra114_idle_power_down(struct cpuidle_device *dev,
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
- cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
+ call_firmware_op(prepare_idle);
+
+ /* Do suspend by ourselves if the firmware does not implement it */
+ if (call_firmware_op(do_idle) == -ENOSYS)
+ cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c
index eb72ae709124..929d1046e2b4 100644
--- a/arch/arm/mach-tegra/platsmp.c
+++ b/arch/arm/mach-tegra/platsmp.c
@@ -114,7 +114,7 @@ static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle)
/* Wait for the power to come up. */
timeout = jiffies + msecs_to_jiffies(100);
- while (tegra_pmc_cpu_is_powered(cpu)) {
+ while (!tegra_pmc_cpu_is_powered(cpu)) {
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
udelay(10);
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c
index 3d0c537d9b94..4cefc5cd6bed 100644
--- a/arch/arm/mach-tegra/powergate.c
+++ b/arch/arm/mach-tegra/powergate.c
@@ -484,6 +484,7 @@ int tegra_io_rail_power_on(int id)
return 0;
}
+EXPORT_SYMBOL(tegra_io_rail_power_on);
int tegra_io_rail_power_off(int id)
{
@@ -511,3 +512,4 @@ int tegra_io_rail_power_off(int id)
return 0;
}
+EXPORT_SYMBOL(tegra_io_rail_power_off);
diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c
deleted file mode 100644
index 3ae4a7f1a2fb..000000000000
--- a/arch/arm/mach-tegra/tegra2_emc.c
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Copyright (C) 2011 Google, Inc.
- *
- * Author:
- * Colin Cross <ccross@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/tegra_emc.h>
-
-#include "tegra2_emc.h"
-#include "fuse.h"
-
-#ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE
-static bool emc_enable = true;
-#else
-static bool emc_enable;
-#endif
-module_param(emc_enable, bool, 0644);
-
-static struct platform_device *emc_pdev;
-static void __iomem *emc_regbase;
-
-static inline void emc_writel(u32 val, unsigned long addr)
-{
- writel(val, emc_regbase + addr);
-}
-
-static inline u32 emc_readl(unsigned long addr)
-{
- return readl(emc_regbase + addr);
-}
-
-static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = {
- 0x2c, /* RC */
- 0x30, /* RFC */
- 0x34, /* RAS */
- 0x38, /* RP */
- 0x3c, /* R2W */
- 0x40, /* W2R */
- 0x44, /* R2P */
- 0x48, /* W2P */
- 0x4c, /* RD_RCD */
- 0x50, /* WR_RCD */
- 0x54, /* RRD */
- 0x58, /* REXT */
- 0x5c, /* WDV */
- 0x60, /* QUSE */
- 0x64, /* QRST */
- 0x68, /* QSAFE */
- 0x6c, /* RDV */
- 0x70, /* REFRESH */
- 0x74, /* BURST_REFRESH_NUM */
- 0x78, /* PDEX2WR */
- 0x7c, /* PDEX2RD */
- 0x80, /* PCHG2PDEN */
- 0x84, /* ACT2PDEN */
- 0x88, /* AR2PDEN */
- 0x8c, /* RW2PDEN */
- 0x90, /* TXSR */
- 0x94, /* TCKE */
- 0x98, /* TFAW */
- 0x9c, /* TRPAB */
- 0xa0, /* TCLKSTABLE */
- 0xa4, /* TCLKSTOP */
- 0xa8, /* TREFBW */
- 0xac, /* QUSE_EXTRA */
- 0x114, /* FBIO_CFG6 */
- 0xb0, /* ODT_WRITE */
- 0xb4, /* ODT_READ */
- 0x104, /* FBIO_CFG5 */
- 0x2bc, /* CFG_DIG_DLL */
- 0x2c0, /* DLL_XFORM_DQS */
- 0x2c4, /* DLL_XFORM_QUSE */
- 0x2e0, /* ZCAL_REF_CNT */
- 0x2e4, /* ZCAL_WAIT_CNT */
- 0x2a8, /* AUTO_CAL_INTERVAL */
- 0x2d0, /* CFG_CLKTRIM_0 */
- 0x2d4, /* CFG_CLKTRIM_1 */
- 0x2d8, /* CFG_CLKTRIM_2 */
-};
-
-/* Select the closest EMC rate that is higher than the requested rate */
-long tegra_emc_round_rate(unsigned long rate)
-{
- struct tegra_emc_pdata *pdata;
- int i;
- int best = -1;
- unsigned long distance = ULONG_MAX;
-
- if (!emc_pdev)
- return -EINVAL;
-
- pdata = emc_pdev->dev.platform_data;
-
- pr_debug("%s: %lu\n", __func__, rate);
-
- /*
- * The EMC clock rate is twice the bus rate, and the bus rate is
- * measured in kHz
- */
- rate = rate / 2 / 1000;
-
- for (i = 0; i < pdata->num_tables; i++) {
- if (pdata->tables[i].rate >= rate &&
- (pdata->tables[i].rate - rate) < distance) {
- distance = pdata->tables[i].rate - rate;
- best = i;
- }
- }
-
- if (best < 0)
- return -EINVAL;
-
- pr_debug("%s: using %lu\n", __func__, pdata->tables[best].rate);
-
- return pdata->tables[best].rate * 2 * 1000;
-}
-
-/*
- * The EMC registers have shadow registers. When the EMC clock is updated
- * in the clock controller, the shadow registers are copied to the active
- * registers, allowing glitchless memory bus frequency changes.
- * This function updates the shadow registers for a new clock frequency,
- * and relies on the clock lock on the emc clock to avoid races between
- * multiple frequency changes
- */
-int tegra_emc_set_rate(unsigned long rate)
-{
- struct tegra_emc_pdata *pdata;
- int i;
- int j;
-
- if (!emc_pdev)
- return -EINVAL;
-
- pdata = emc_pdev->dev.platform_data;
-
- /*
- * The EMC clock rate is twice the bus rate, and the bus rate is
- * measured in kHz
- */
- rate = rate / 2 / 1000;
-
- for (i = 0; i < pdata->num_tables; i++)
- if (pdata->tables[i].rate == rate)
- break;
-
- if (i >= pdata->num_tables)
- return -EINVAL;
-
- pr_debug("%s: setting to %lu\n", __func__, rate);
-
- for (j = 0; j < TEGRA_EMC_NUM_REGS; j++)
- emc_writel(pdata->tables[i].regs[j], emc_reg_addr[j]);
-
- emc_readl(pdata->tables[i].regs[TEGRA_EMC_NUM_REGS - 1]);
-
- return 0;
-}
-
-#ifdef CONFIG_OF
-static struct device_node *tegra_emc_ramcode_devnode(struct device_node *np)
-{
- struct device_node *iter;
- u32 reg;
-
- for_each_child_of_node(np, iter) {
- if (of_property_read_u32(iter, "nvidia,ram-code", &reg))
- continue;
- if (reg == tegra_bct_strapping)
- return of_node_get(iter);
- }
-
- return NULL;
-}
-
-static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata(
- struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct device_node *tnp, *iter;
- struct tegra_emc_pdata *pdata;
- int ret, i, num_tables;
-
- if (!np)
- return NULL;
-
- if (of_find_property(np, "nvidia,use-ram-code", NULL)) {
- tnp = tegra_emc_ramcode_devnode(np);
- if (!tnp)
- dev_warn(&pdev->dev,
- "can't find emc table for ram-code 0x%02x\n",
- tegra_bct_strapping);
- } else
- tnp = of_node_get(np);
-
- if (!tnp)
- return NULL;
-
- num_tables = 0;
- for_each_child_of_node(tnp, iter)
- if (of_device_is_compatible(iter, "nvidia,tegra20-emc-table"))
- num_tables++;
-
- if (!num_tables) {
- pdata = NULL;
- goto out;
- }
-
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- pdata->tables = devm_kzalloc(&pdev->dev,
- sizeof(*pdata->tables) * num_tables,
- GFP_KERNEL);
-
- i = 0;
- for_each_child_of_node(tnp, iter) {
- u32 prop;
-
- ret = of_property_read_u32(iter, "clock-frequency", &prop);
- if (ret) {
- dev_err(&pdev->dev, "no clock-frequency in %s\n",
- iter->full_name);
- continue;
- }
- pdata->tables[i].rate = prop;
-
- ret = of_property_read_u32_array(iter, "nvidia,emc-registers",
- pdata->tables[i].regs,
- TEGRA_EMC_NUM_REGS);
- if (ret) {
- dev_err(&pdev->dev,
- "malformed emc-registers property in %s\n",
- iter->full_name);
- continue;
- }
-
- i++;
- }
- pdata->num_tables = i;
-
-out:
- of_node_put(tnp);
- return pdata;
-}
-#else
-static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata(
- struct platform_device *pdev)
-{
- return NULL;
-}
-#endif
-
-static struct tegra_emc_pdata *tegra_emc_fill_pdata(struct platform_device *pdev)
-{
- struct clk *c = clk_get_sys(NULL, "emc");
- struct tegra_emc_pdata *pdata;
- unsigned long khz;
- int i;
-
- WARN_ON(pdev->dev.platform_data);
- BUG_ON(IS_ERR(c));
-
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- pdata->tables = devm_kzalloc(&pdev->dev, sizeof(*pdata->tables),
- GFP_KERNEL);
-
- pdata->tables[0].rate = clk_get_rate(c) / 2 / 1000;
-
- for (i = 0; i < TEGRA_EMC_NUM_REGS; i++)
- pdata->tables[0].regs[i] = emc_readl(emc_reg_addr[i]);
-
- pdata->num_tables = 1;
-
- khz = pdata->tables[0].rate;
- dev_info(&pdev->dev, "no tables provided, using %ld kHz emc, "
- "%ld kHz mem\n", khz * 2, khz);
-
- return pdata;
-}
-
-static int tegra_emc_probe(struct platform_device *pdev)
-{
- struct tegra_emc_pdata *pdata;
- struct resource *res;
-
- if (!emc_enable) {
- dev_err(&pdev->dev, "disabled per module parameter\n");
- return -ENODEV;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- emc_regbase = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(emc_regbase))
- return PTR_ERR(emc_regbase);
-
- pdata = pdev->dev.platform_data;
-
- if (!pdata)
- pdata = tegra_emc_dt_parse_pdata(pdev);
-
- if (!pdata)
- pdata = tegra_emc_fill_pdata(pdev);
-
- pdev->dev.platform_data = pdata;
-
- emc_pdev = pdev;
-
- return 0;
-}
-
-static struct of_device_id tegra_emc_of_match[] = {
- { .compatible = "nvidia,tegra20-emc", },
- { },
-};
-
-static struct platform_driver tegra_emc_driver = {
- .driver = {
- .name = "tegra-emc",
- .owner = THIS_MODULE,
- .of_match_table = tegra_emc_of_match,
- },
- .probe = tegra_emc_probe,
-};
-
-static int __init tegra_emc_init(void)
-{
- return platform_driver_register(&tegra_emc_driver);
-}
-device_initcall(tegra_emc_init);
diff --git a/arch/arm/mach-tegra/tegra2_emc.h b/arch/arm/mach-tegra/tegra2_emc.h
deleted file mode 100644
index f61409b54cb7..000000000000
--- a/arch/arm/mach-tegra/tegra2_emc.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2011 Google, Inc.
- *
- * Author:
- * Colin Cross <ccross@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __MACH_TEGRA_TEGRA2_EMC_H_
-#define __MACH_TEGRA_TEGRA2_EMC_H
-
-int tegra_emc_set_rate(unsigned long rate);
-long tegra_emc_round_rate(unsigned long rate);
-
-#endif
diff --git a/arch/arm/mach-u300/Kconfig b/arch/arm/mach-u300/Kconfig
index 8e23071bd1b3..e3a96d7302e9 100644
--- a/arch/arm/mach-u300/Kconfig
+++ b/arch/arm/mach-u300/Kconfig
@@ -3,20 +3,14 @@ config ARCH_U300
depends on MMU
select ARCH_REQUIRE_GPIOLIB
select ARM_AMBA
- select ARM_PATCH_PHYS_VIRT
select ARM_VIC
select CLKSRC_MMIO
- select CLKSRC_OF
- select COMMON_CLK
select CPU_ARM926T
- select GENERIC_CLOCKEVENTS
select HAVE_TCM
select PINCTRL
select PINCTRL_COH901
select PINCTRL_U300
- select SPARSE_IRQ
select MFD_SYSCON
- select USE_OF
help
Support for ST-Ericsson U300 series mobile platforms.
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index 0034d2cd6973..b41a42da1505 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -11,13 +11,8 @@ config ARCH_U8500
select ARM_GIC
select CACHE_L2X0
select CLKSRC_NOMADIK_MTU
- select COMMON_CLK
- select CPU_V7
- select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
- select HAVE_SMP
- select MIGHT_HAVE_CACHE_L2X0
select PINCTRL
select PINCTRL_ABX500
select PINCTRL_NOMADIK
@@ -73,11 +68,6 @@ config UX500_AUTO_PLATFORM
a working kernel. If everything else is disabled, this
automatically enables MACH_MOP500.
-config MACH_UX500_DT
- bool "Generic U8500 support using device tree"
- depends on MACH_MOP500
- select USE_OF
-
endmenu
config UX500_DEBUG_UART
diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile
index d05ba759da30..de544aabf292 100644
--- a/arch/arm/mach-ux500/Makefile
+++ b/arch/arm/mach-ux500/Makefile
@@ -7,7 +7,6 @@ obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o
obj-$(CONFIG_MACH_MOP500) += board-mop500-sdi.o \
board-mop500-regulators.o \
- board-mop500-pins.o \
board-mop500-audio.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-ux500/board-mop500-audio.c b/arch/arm/mach-ux500/board-mop500-audio.c
index 9309ad4cbd09..b2a0899e7453 100644
--- a/arch/arm/mach-ux500/board-mop500-audio.c
+++ b/arch/arm/mach-ux500/board-mop500-audio.c
@@ -9,7 +9,6 @@
#include <linux/gpio.h>
#include <linux/platform_data/dma-ste-dma40.h>
-#include "irqs.h"
#include <linux/platform_data/asoc-ux500-msp.h>
#include "ste-dma40-db8500.h"
diff --git a/arch/arm/mach-ux500/board-mop500-pins.c b/arch/arm/mach-ux500/board-mop500-pins.c
deleted file mode 100644
index f63619b69113..000000000000
--- a/arch/arm/mach-ux500/board-mop500-pins.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/bug.h>
-#include <linux/string.h>
-#include <linux/pinctrl/machine.h>
-#include <linux/pinctrl/pinconf-generic.h>
-
-#include <asm/mach-types.h>
-
-#include "board-mop500.h"
-
-/* These simply sets bias for pins */
-#define BIAS(a,b) static unsigned long a[] = { b }
-
-BIAS(abx500_out_lo, PIN_CONF_PACKED(PIN_CONFIG_OUTPUT, 0));
-BIAS(abx500_in_pd, PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_DOWN, 1));
-BIAS(abx500_in_nopull, PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_DOWN, 0));
-
-#define AB8500_MUX_HOG(group, func) \
- PIN_MAP_MUX_GROUP_HOG_DEFAULT("pinctrl-ab8500.0", group, func)
-#define AB8500_PIN_HOG(pin, conf) \
- PIN_MAP_CONFIGS_PIN_HOG_DEFAULT("pinctrl-ab8500.0", pin, abx500_##conf)
-
-#define AB8500_MUX_STATE(group, func, dev, state) \
- PIN_MAP_MUX_GROUP(dev, state, "pinctrl-ab8500.0", group, func)
-#define AB8500_PIN_STATE(pin, conf, dev, state) \
- PIN_MAP_CONFIGS_PIN(dev, state, "pinctrl-ab8500.0", pin, abx500_##conf)
-
-#define AB8505_MUX_HOG(group, func) \
- PIN_MAP_MUX_GROUP_HOG_DEFAULT("pinctrl-ab8505.0", group, func)
-#define AB8505_PIN_HOG(pin, conf) \
- PIN_MAP_CONFIGS_PIN_HOG_DEFAULT("pinctrl-ab8505.0", pin, abx500_##conf)
-
-#define AB8505_MUX_STATE(group, func, dev, state) \
- PIN_MAP_MUX_GROUP(dev, state, "pinctrl-ab8505.0", group, func)
-#define AB8505_PIN_STATE(pin, conf, dev, state) \
- PIN_MAP_CONFIGS_PIN(dev, state, "pinctrl-ab8505.0", pin, abx500_##conf)
-
-static struct pinctrl_map __initdata ab8500_pinmap[] = {
- /* Sysclkreq2 */
- AB8500_MUX_STATE("sysclkreq2_d_1", "sysclkreq", "regulator.35", PINCTRL_STATE_DEFAULT),
- AB8500_PIN_STATE("GPIO1_T10", in_nopull, "regulator.35", PINCTRL_STATE_DEFAULT),
- /* sysclkreq2 disable, mux in gpio configured in input pulldown */
- AB8500_MUX_STATE("gpio1_a_1", "gpio", "regulator.35", PINCTRL_STATE_SLEEP),
- AB8500_PIN_STATE("GPIO1_T10", in_pd, "regulator.35", PINCTRL_STATE_SLEEP),
-
- /* pins 2 is muxed in GPIO, configured in INPUT PULL DOWN */
- AB8500_MUX_HOG("gpio2_a_1", "gpio"),
- AB8500_PIN_HOG("GPIO2_T9", in_pd),
-
- /* Sysclkreq4 */
- AB8500_MUX_STATE("sysclkreq4_d_1", "sysclkreq", "regulator.36", PINCTRL_STATE_DEFAULT),
- AB8500_PIN_STATE("GPIO3_U9", in_nopull, "regulator.36", PINCTRL_STATE_DEFAULT),
- /* sysclkreq4 disable, mux in gpio configured in input pulldown */
- AB8500_MUX_STATE("gpio3_a_1", "gpio", "regulator.36", PINCTRL_STATE_SLEEP),
- AB8500_PIN_STATE("GPIO3_U9", in_pd, "regulator.36", PINCTRL_STATE_SLEEP),
-
- /* pins 4 is muxed in GPIO, configured in INPUT PULL DOWN */
- AB8500_MUX_HOG("gpio4_a_1", "gpio"),
- AB8500_PIN_HOG("GPIO4_W2", in_pd),
-
- /*
- * pins 6,7,8 and 9 are muxed in YCBCR0123
- * configured in INPUT PULL UP
- */
- AB8500_MUX_HOG("ycbcr0123_d_1", "ycbcr"),
- AB8500_PIN_HOG("GPIO6_Y18", in_nopull),
- AB8500_PIN_HOG("GPIO7_AA20", in_nopull),
- AB8500_PIN_HOG("GPIO8_W18", in_nopull),
- AB8500_PIN_HOG("GPIO9_AA19", in_nopull),
-
- /*
- * pins 10,11,12 and 13 are muxed in GPIO
- * configured in INPUT PULL DOWN
- */
- AB8500_MUX_HOG("gpio10_d_1", "gpio"),
- AB8500_PIN_HOG("GPIO10_U17", in_pd),
-
- AB8500_MUX_HOG("gpio11_d_1", "gpio"),
- AB8500_PIN_HOG("GPIO11_AA18", in_pd),
-
- AB8500_MUX_HOG("gpio12_d_1", "gpio"),
- AB8500_PIN_HOG("GPIO12_U16", in_pd),
-
- AB8500_MUX_HOG("gpio13_d_1", "gpio"),
- AB8500_PIN_HOG("GPIO13_W17", in_pd),
-
- /*
- * pins 14,15 are muxed in PWM1 and PWM2
- * configured in INPUT PULL DOWN
- */
- AB8500_MUX_HOG("pwmout1_d_1", "pwmout"),
- AB8500_PIN_HOG("GPIO14_F14", in_pd),
-
- AB8500_MUX_HOG("pwmout2_d_1", "pwmout"),
- AB8500_PIN_HOG("GPIO15_B17", in_pd),
-
- /*
- * pins 16 is muxed in GPIO
- * configured in INPUT PULL DOWN
- */
- AB8500_MUX_HOG("gpio16_a_1", "gpio"),
- AB8500_PIN_HOG("GPIO14_F14", in_pd),
-
- /*
- * pins 17,18,19 and 20 are muxed in AUDIO interface 1
- * configured in INPUT PULL DOWN
- */
- AB8500_MUX_HOG("adi1_d_1", "adi1"),
- AB8500_PIN_HOG("GPIO17_P5", in_pd),
- AB8500_PIN_HOG("GPIO18_R5", in_pd),
- AB8500_PIN_HOG("GPIO19_U5", in_pd),
- AB8500_PIN_HOG("GPIO20_T5", in_pd),
-
- /*
- * pins 21,22 and 23 are muxed in USB UICC
- * configured in INPUT PULL DOWN
- */
- AB8500_MUX_HOG("usbuicc_d_1", "usbuicc"),
- AB8500_PIN_HOG("GPIO21_H19", in_pd),
- AB8500_PIN_HOG("GPIO22_G20", in_pd),
- AB8500_PIN_HOG("GPIO23_G19", in_pd),
-
- /*
- * pins 24,25 are muxed in GPIO
- * configured in INPUT PULL DOWN
- */
- AB8500_MUX_HOG("gpio24_a_1", "gpio"),
- AB8500_PIN_HOG("GPIO24_T14", in_pd),
-
- AB8500_MUX_HOG("gpio25_a_1", "gpio"),
- AB8500_PIN_HOG("GPIO25_R16", in_pd),
-
- /*
- * pins 26 is muxed in GPIO
- * configured in OUTPUT LOW
- */
- AB8500_MUX_HOG("gpio26_d_1", "gpio"),
- AB8500_PIN_HOG("GPIO26_M16", out_lo),
-
- /*
- * pins 27,28 are muxed in DMIC12
- * configured in INPUT PULL DOWN
- */
- AB8500_MUX_HOG("dmic12_d_1", "dmic"),
- AB8500_PIN_HOG("GPIO27_J6", in_pd),
- AB8500_PIN_HOG("GPIO28_K6", in_pd),
-
- /*
- * pins 29,30 are muxed in DMIC34
- * configured in INPUT PULL DOWN
- */
- AB8500_MUX_HOG("dmic34_d_1", "dmic"),
- AB8500_PIN_HOG("GPIO29_G6", in_pd),
- AB8500_PIN_HOG("GPIO30_H6", in_pd),
-
- /*
- * pins 31,32 are muxed in DMIC56
- * configured in INPUT PULL DOWN
- */
- AB8500_MUX_HOG("dmic56_d_1", "dmic"),
- AB8500_PIN_HOG("GPIO31_F5", in_pd),
- AB8500_PIN_HOG("GPIO32_G5", in_pd),
-
- /*
- * pins 34 is muxed in EXTCPENA
- * configured INPUT PULL DOWN
- */
- AB8500_MUX_HOG("extcpena_d_1", "extcpena"),
- AB8500_PIN_HOG("GPIO34_R17", in_pd),
-
- /*
- * pins 35 is muxed in GPIO
- * configured in OUTPUT LOW
- */
- AB8500_MUX_HOG("gpio35_d_1", "gpio"),
- AB8500_PIN_HOG("GPIO35_W15", in_pd),
-
- /*
- * pins 36,37,38 and 39 are muxed in GPIO
- * configured in INPUT PULL DOWN
- */
- AB8500_MUX_HOG("gpio36_a_1", "gpio"),
- AB8500_PIN_HOG("GPIO36_A17", in_pd),
-
- AB8500_MUX_HOG("gpio37_a_1", "gpio"),
- AB8500_PIN_HOG("GPIO37_E15", in_pd),
-
- AB8500_MUX_HOG("gpio38_a_1", "gpio"),
- AB8500_PIN_HOG("GPIO38_C17", in_pd),
-
- AB8500_MUX_HOG("gpio39_a_1", "gpio"),
- AB8500_PIN_HOG("GPIO39_E16", in_pd),
-
- /*
- * pins 40 and 41 are muxed in MODCSLSDA
- * configured INPUT PULL DOWN
- */
- AB8500_MUX_HOG("modsclsda_d_1", "modsclsda"),
- AB8500_PIN_HOG("GPIO40_T19", in_pd),
- AB8500_PIN_HOG("GPIO41_U19", in_pd),
-
- /*
- * pins 42 is muxed in GPIO
- * configured INPUT PULL DOWN
- */
- AB8500_MUX_HOG("gpio42_a_1", "gpio"),
- AB8500_PIN_HOG("GPIO42_U2", in_pd),
-};
-
-static struct pinctrl_map __initdata ab8505_pinmap[] = {
- /* Sysclkreq2 */
- AB8505_MUX_STATE("sysclkreq2_d_1", "sysclkreq", "regulator.36", PINCTRL_STATE_DEFAULT),
- AB8505_PIN_STATE("GPIO1_N4", in_nopull, "regulator.36", PINCTRL_STATE_DEFAULT),
- /* sysclkreq2 disable, mux in gpio configured in input pulldown */
- AB8505_MUX_STATE("gpio1_a_1", "gpio", "regulator.36", PINCTRL_STATE_SLEEP),
- AB8505_PIN_STATE("GPIO1_N4", in_pd, "regulator.36", PINCTRL_STATE_SLEEP),
-
- /* pins 2 is muxed in GPIO, configured in INPUT PULL DOWN */
- AB8505_MUX_HOG("gpio2_a_1", "gpio"),
- AB8505_PIN_HOG("GPIO2_R5", in_pd),
-
- /* Sysclkreq4 */
- AB8505_MUX_STATE("sysclkreq4_d_1", "sysclkreq", "regulator.37", PINCTRL_STATE_DEFAULT),
- AB8505_PIN_STATE("GPIO3_P5", in_nopull, "regulator.37", PINCTRL_STATE_DEFAULT),
- /* sysclkreq4 disable, mux in gpio configured in input pulldown */
- AB8505_MUX_STATE("gpio3_a_1", "gpio", "regulator.37", PINCTRL_STATE_SLEEP),
- AB8505_PIN_STATE("GPIO3_P5", in_pd, "regulator.37", PINCTRL_STATE_SLEEP),
-
- AB8505_MUX_HOG("gpio10_d_1", "gpio"),
- AB8505_PIN_HOG("GPIO10_B16", in_pd),
-
- AB8505_MUX_HOG("gpio11_d_1", "gpio"),
- AB8505_PIN_HOG("GPIO11_B17", in_pd),
-
- AB8505_MUX_HOG("gpio13_d_1", "gpio"),
- AB8505_PIN_HOG("GPIO13_D17", in_nopull),
-
- AB8505_MUX_HOG("pwmout1_d_1", "pwmout"),
- AB8505_PIN_HOG("GPIO14_C16", in_pd),
-
- AB8505_MUX_HOG("adi2_d_1", "adi2"),
- AB8505_PIN_HOG("GPIO17_P2", in_pd),
- AB8505_PIN_HOG("GPIO18_N3", in_pd),
- AB8505_PIN_HOG("GPIO19_T1", in_pd),
- AB8505_PIN_HOG("GPIO20_P3", in_pd),
-
- AB8505_MUX_HOG("gpio34_a_1", "gpio"),
- AB8505_PIN_HOG("GPIO34_H14", in_pd),
-
- AB8505_MUX_HOG("modsclsda_d_1", "modsclsda"),
- AB8505_PIN_HOG("GPIO40_J15", in_pd),
- AB8505_PIN_HOG("GPIO41_J14", in_pd),
-
- AB8505_MUX_HOG("gpio50_d_1", "gpio"),
- AB8505_PIN_HOG("GPIO50_L4", in_nopull),
-
- AB8505_MUX_HOG("resethw_d_1", "resethw"),
- AB8505_PIN_HOG("GPIO52_D16", in_pd),
-
- AB8505_MUX_HOG("service_d_1", "service"),
- AB8505_PIN_HOG("GPIO53_D15", in_pd),
-};
-
-void __init mop500_pinmaps_init(void)
-{
- if (machine_is_u8520())
- pinctrl_register_mappings(ab8505_pinmap,
- ARRAY_SIZE(ab8505_pinmap));
- else
- pinctrl_register_mappings(ab8500_pinmap,
- ARRAY_SIZE(ab8500_pinmap));
-}
-
-void __init snowball_pinmaps_init(void)
-{
- pinctrl_register_mappings(ab8500_pinmap,
- ARRAY_SIZE(ab8500_pinmap));
-}
-
-void __init hrefv60_pinmaps_init(void)
-{
- pinctrl_register_mappings(ab8500_pinmap,
- ARRAY_SIZE(ab8500_pinmap));
-}
diff --git a/arch/arm/mach-ux500/board-mop500.h b/arch/arm/mach-ux500/board-mop500.h
index d48e8662c676..32cc0d8d8a0e 100644
--- a/arch/arm/mach-ux500/board-mop500.h
+++ b/arch/arm/mach-ux500/board-mop500.h
@@ -7,78 +7,9 @@
#ifndef __BOARD_MOP500_H
#define __BOARD_MOP500_H
-/* For NOMADIK_NR_GPIO */
-#include "irqs.h"
#include <linux/platform_data/asoc-ux500-msp.h>
#include <linux/amba/mmci.h>
-/* Snowball specific GPIO assignments, this board has no GPIO expander */
-#define SNOWBALL_ACCEL_INT1_GPIO 163
-#define SNOWBALL_ACCEL_INT2_GPIO 164
-#define SNOWBALL_MAGNET_DRDY_GPIO 165
-#define SNOWBALL_SDMMC_EN_GPIO 217
-#define SNOWBALL_SDMMC_1V8_3V_GPIO 228
-#define SNOWBALL_SDMMC_CD_GPIO 218
-
-/* HREFv60-specific GPIO assignments, this board has no GPIO expander */
-#define HREFV60_SDMMC_1V8_3V_GPIO 5
-#define HREFV60_CAMERA_FLASH_ENABLE 21
-#define HREFV60_MAGNET_DRDY_GPIO 32
-#define HREFV60_DISP1_RST_GPIO 65
-#define HREFV60_DISP2_RST_GPIO 66
-#define HREFV60_ACCEL_INT1_GPIO 82
-#define HREFV60_ACCEL_INT2_GPIO 83
-#define HREFV60_SDMMC_CD_GPIO 95
-#define HREFV60_XSHUTDOWN_SECONDARY_SENSOR 140
-#define HREFV60_TOUCH_RST_GPIO 143
-#define HREFV60_HAL_SW_GPIO 145
-#define HREFV60_SDMMC_EN_GPIO 169
-#define HREFV60_MMIO_XENON_CHARGE 170
-#define HREFV60_PROX_SENSE_GPIO 217
-
-/* MOP500 generic GPIOs */
-#define CAMERA_FLASH_INT_PIN 7
-#define CYPRESS_TOUCH_INT_PIN 84
-#define XSHUTDOWN_PRIMARY_SENSOR 141
-#define XSHUTDOWN_SECONDARY_SENSOR 142
-#define CYPRESS_TOUCH_RST_GPIO 143
-#define MOP500_HDMI_RST_GPIO 196
-#define CYPRESS_SLAVE_SELECT_GPIO 216
-
-/* GPIOs on the TC35892 expander */
-#define MOP500_EGPIO(x) (NOMADIK_NR_GPIO + (x))
-#define GPIO_MAGNET_DRDY MOP500_EGPIO(1)
-#define GPIO_SDMMC_CD MOP500_EGPIO(3)
-#define GPIO_CAMERA_FLASH_ENABLE MOP500_EGPIO(4)
-#define GPIO_MMIO_XENON_CHARGE MOP500_EGPIO(5)
-#define GPIO_PROX_SENSOR MOP500_EGPIO(7)
-#define GPIO_HAL_SENSOR MOP500_EGPIO(8)
-#define GPIO_ACCEL_INT1 MOP500_EGPIO(10)
-#define GPIO_ACCEL_INT2 MOP500_EGPIO(11)
-#define GPIO_BU21013_CS MOP500_EGPIO(13)
-#define MOP500_DISP2_RST_GPIO MOP500_EGPIO(14)
-#define MOP500_DISP1_RST_GPIO MOP500_EGPIO(15)
-#define GPIO_SDMMC_EN MOP500_EGPIO(17)
-#define GPIO_SDMMC_1V8_3V_SEL MOP500_EGPIO(18)
-#define MOP500_EGPIO_END MOP500_EGPIO(24)
-
-/*
- * GPIOs on the AB8500 mixed-signals circuit
- * Notice that we subtract 1 from the number passed into the macro, this is
- * because the AB8500 GPIO pins are enumbered starting from 1, so the value in
- * parens matches the GPIO pin number in the data sheet.
- */
-#define MOP500_AB8500_PIN_GPIO(x) (MOP500_EGPIO_END + (x) - 1)
-/*Snowball AB8500 GPIO */
-#define SNOWBALL_VSMPS2_1V8_GPIO MOP500_AB8500_PIN_GPIO(1) /* SYSCLKREQ2/GPIO1 */
-#define SNOWBALL_PM_GPIO1_GPIO MOP500_AB8500_PIN_GPIO(2) /* SYSCLKREQ3/GPIO2 */
-#define SNOWBALL_WLAN_CLK_REQ_GPIO MOP500_AB8500_PIN_GPIO(3) /* SYSCLKREQ4/GPIO3 */
-#define SNOWBALL_PM_GPIO4_GPIO MOP500_AB8500_PIN_GPIO(4) /* SYSCLKREQ6/GPIO4 */
-#define SNOWBALL_EN_3V6_GPIO MOP500_AB8500_PIN_GPIO(16) /* PWMOUT3/GPIO16 */
-#define SNOWBALL_PME_ETH_GPIO MOP500_AB8500_PIN_GPIO(24) /* SYSCLKREQ7/GPIO24 */
-#define SNOWBALL_EN_3V3_ETH_GPIO MOP500_AB8500_PIN_GPIO(26) /* GPIO26 */
-
-struct device;
extern struct mmci_platform_data mop500_sdi0_data;
extern struct mmci_platform_data mop500_sdi1_data;
extern struct mmci_platform_data mop500_sdi2_data;
@@ -88,8 +19,4 @@ extern struct msp_i2s_platform_data msp1_platform_data;
extern struct msp_i2s_platform_data msp2_platform_data;
extern struct msp_i2s_platform_data msp3_platform_data;
-void __init mop500_pinmaps_init(void);
-void __init snowball_pinmaps_init(void);
-void __init hrefv60_pinmaps_init(void);
-
#endif
diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c
index bc8a6183560d..8820f602fcd2 100644
--- a/arch/arm/mach-ux500/cpu-db8500.c
+++ b/arch/arm/mach-ux500/cpu-db8500.c
@@ -27,7 +27,6 @@
#include <asm/mach/map.h>
#include "setup.h"
-#include "irqs.h"
#include "board-mop500-regulators.h"
#include "board-mop500.h"
@@ -35,14 +34,11 @@
#include "id.h"
struct ab8500_platform_data ab8500_platdata = {
- .irq_base = MOP500_AB8500_IRQ_BASE,
.regulator = &ab8500_regulator_plat_data,
};
struct prcmu_pdata db8500_prcmu_pdata = {
.ab_platdata = &ab8500_platdata,
- .ab_irq = IRQ_DB8500_AB8500,
- .irq_base = IRQ_PRCMU_BASE,
.version_offset = DB8500_PRCMU_FW_VERSION_OFFSET,
.legacy_offset = DB8500_PRCMU_LEGACY_OFFSET,
};
@@ -146,7 +142,6 @@ static struct device * __init db8500_soc_device_init(void)
return ux500_soc_device_init(soc_id);
}
-#ifdef CONFIG_MACH_UX500_DT
static struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = {
/* Requires call-back bindings. */
OF_DEV_AUXDATA("arm,cortex-a9-pmu", 0, "arm-pmu", &db8500_pmu_platdata),
@@ -191,16 +186,6 @@ static void __init u8500_init_machine(void)
{
struct device *parent = db8500_soc_device_init();
- /* Pinmaps must be in place before devices register */
- if (of_machine_is_compatible("st-ericsson,mop500"))
- mop500_pinmaps_init();
- else if (of_machine_is_compatible("calaosystems,snowball-a9500")) {
- snowball_pinmaps_init();
- } else if (of_machine_is_compatible("st-ericsson,hrefv60+"))
- hrefv60_pinmaps_init();
- else if (of_machine_is_compatible("st-ericsson,ccu9540")) {}
- /* TODO: Add pinmaps for ccu9540 board. */
-
/* automatically probe child nodes of dbx5x0 devices */
if (of_machine_is_compatible("st-ericsson,u8540"))
of_platform_populate(NULL, u8500_local_bus_nodes,
@@ -229,5 +214,3 @@ DT_MACHINE_START(U8500_DT, "ST-Ericsson Ux5x0 platform (Device Tree Support)")
.dt_compat = stericsson_dt_platform_compat,
.restart = ux500_restart,
MACHINE_END
-
-#endif
diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c
index d11ac4bf336c..db16b5a04ad5 100644
--- a/arch/arm/mach-ux500/cpu.c
+++ b/arch/arm/mach-ux500/cpu.c
@@ -52,17 +52,7 @@ void ux500_restart(enum reboot_mode mode, const char *cmd)
*/
void __init ux500_init_irq(void)
{
- void __iomem *dist_base;
- void __iomem *cpu_base;
-
gic_arch_extn.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND;
-
- if (cpu_is_u8500_family() || cpu_is_ux540_family()) {
- dist_base = __io_address(U8500_GIC_DIST_BASE);
- cpu_base = __io_address(U8500_GIC_CPU_BASE);
- } else
- ux500_unknown_soc();
-
irqchip_init();
/*
diff --git a/arch/arm/mach-ux500/irqs-board-mop500.h b/arch/arm/mach-ux500/irqs-board-mop500.h
deleted file mode 100644
index d526dd8e87d3..000000000000
--- a/arch/arm/mach-ux500/irqs-board-mop500.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Rabin Vincent <rabin.vincent@stericsson.com>
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#ifndef __MACH_IRQS_BOARD_MOP500_H
-#define __MACH_IRQS_BOARD_MOP500_H
-
-/* Number of AB8500 irqs is taken from header file */
-#include <linux/mfd/abx500/ab8500.h>
-
-#define MOP500_AB8500_IRQ_BASE IRQ_BOARD_START
-#define MOP500_AB8500_IRQ_END (MOP500_AB8500_IRQ_BASE \
- + AB8500_MAX_NR_IRQS)
-
-/* TC35892 */
-#define TC35892_NR_INTERNAL_IRQS 8
-#define TC35892_INT_GPIO(x) (TC35892_NR_INTERNAL_IRQS + (x))
-#define TC35892_NR_GPIOS 24
-#define TC35892_NR_IRQS TC35892_INT_GPIO(TC35892_NR_GPIOS)
-
-#define MOP500_EGPIO_NR_IRQS TC35892_NR_IRQS
-
-#define MOP500_EGPIO_IRQ_BASE MOP500_AB8500_IRQ_END
-#define MOP500_EGPIO_IRQ_END (MOP500_EGPIO_IRQ_BASE \
- + MOP500_EGPIO_NR_IRQS)
-/* STMPE1601 irqs */
-#define STMPE_NR_INTERNAL_IRQS 9
-#define STMPE_INT_GPIO(x) (STMPE_NR_INTERNAL_IRQS + (x))
-#define STMPE_NR_GPIOS 24
-#define STMPE_NR_IRQS STMPE_INT_GPIO(STMPE_NR_GPIOS)
-
-#define MOP500_STMPE1601_IRQBASE MOP500_EGPIO_IRQ_END
-#define MOP500_STMPE1601_IRQ(x) (MOP500_STMPE1601_IRQBASE + (x))
-
-#define MOP500_STMPE1601_IRQ_END \
- MOP500_STMPE1601_IRQ(STMPE_NR_INTERNAL_IRQS)
-
-#define MOP500_NR_IRQS MOP500_STMPE1601_IRQ_END
-
-#define MOP500_IRQ_END MOP500_NR_IRQS
-
-/*
- * We may have several boards, but only one will run at a
- * time, so the one with most IRQs will bump this ahead,
- * but the IRQ_BOARD_START remains the same for either board.
- */
-#if MOP500_IRQ_END > IRQ_BOARD_END
-#undef IRQ_BOARD_END
-#define IRQ_BOARD_END MOP500_IRQ_END
-#endif
-
-#endif
diff --git a/arch/arm/mach-ux500/irqs-db8500.h b/arch/arm/mach-ux500/irqs-db8500.h
deleted file mode 100644
index f3a9d5947ef3..000000000000
--- a/arch/arm/mach-ux500/irqs-db8500.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Rabin Vincent <rabin.vincent@stericsson.com>
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#ifndef __MACH_IRQS_DB8500_H
-#define __MACH_IRQS_DB8500_H
-
-#define IRQ_DB8500_MTU0 (IRQ_SHPI_START + 4)
-#define IRQ_DB8500_SPI2 (IRQ_SHPI_START + 6)
-#define IRQ_DB8500_PMU (IRQ_SHPI_START + 7)
-#define IRQ_DB8500_SPI0 (IRQ_SHPI_START + 8)
-#define IRQ_DB8500_RTT (IRQ_SHPI_START + 9)
-#define IRQ_DB8500_PKA (IRQ_SHPI_START + 10)
-#define IRQ_DB8500_UART0 (IRQ_SHPI_START + 11)
-#define IRQ_DB8500_I2C3 (IRQ_SHPI_START + 12)
-#define IRQ_DB8500_L2CC (IRQ_SHPI_START + 13)
-#define IRQ_DB8500_SSP0 (IRQ_SHPI_START + 14)
-#define IRQ_DB8500_CRYP1 (IRQ_SHPI_START + 15)
-#define IRQ_DB8500_MSP1_RX (IRQ_SHPI_START + 16)
-#define IRQ_DB8500_MTU1 (IRQ_SHPI_START + 17)
-#define IRQ_DB8500_RTC (IRQ_SHPI_START + 18)
-#define IRQ_DB8500_UART1 (IRQ_SHPI_START + 19)
-#define IRQ_DB8500_USB_WAKEUP (IRQ_SHPI_START + 20)
-#define IRQ_DB8500_I2C0 (IRQ_SHPI_START + 21)
-#define IRQ_DB8500_I2C1 (IRQ_SHPI_START + 22)
-#define IRQ_DB8500_USBOTG (IRQ_SHPI_START + 23)
-#define IRQ_DB8500_DMA_SECURE (IRQ_SHPI_START + 24)
-#define IRQ_DB8500_DMA (IRQ_SHPI_START + 25)
-#define IRQ_DB8500_UART2 (IRQ_SHPI_START + 26)
-#define IRQ_DB8500_ICN_PMU1 (IRQ_SHPI_START + 27)
-#define IRQ_DB8500_ICN_PMU2 (IRQ_SHPI_START + 28)
-#define IRQ_DB8500_HSIR_EXCEP (IRQ_SHPI_START + 29)
-#define IRQ_DB8500_MSP0 (IRQ_SHPI_START + 31)
-#define IRQ_DB8500_HSIR_CH0_OVRRUN (IRQ_SHPI_START + 32)
-#define IRQ_DB8500_HSIR_CH1_OVRRUN (IRQ_SHPI_START + 33)
-#define IRQ_DB8500_HSIR_CH2_OVRRUN (IRQ_SHPI_START + 34)
-#define IRQ_DB8500_HSIR_CH3_OVRRUN (IRQ_SHPI_START + 35)
-#define IRQ_DB8500_HSIR_CH4_OVRRUN (IRQ_SHPI_START + 36)
-#define IRQ_DB8500_HSIR_CH5_OVRRUN (IRQ_SHPI_START + 37)
-#define IRQ_DB8500_HSIR_CH6_OVRRUN (IRQ_SHPI_START + 38)
-#define IRQ_DB8500_HSIR_CH7_OVRRUN (IRQ_SHPI_START + 39)
-#define IRQ_DB8500_AB8500 (IRQ_SHPI_START + 40)
-#define IRQ_DB8500_SDMMC2 (IRQ_SHPI_START + 41)
-#define IRQ_DB8500_SIA (IRQ_SHPI_START + 42)
-#define IRQ_DB8500_SIA2 (IRQ_SHPI_START + 43)
-#define IRQ_DB8500_SVA (IRQ_SHPI_START + 44)
-#define IRQ_DB8500_SVA2 (IRQ_SHPI_START + 45)
-#define IRQ_DB8500_PRCMU0 (IRQ_SHPI_START + 46)
-#define IRQ_DB8500_PRCMU1 (IRQ_SHPI_START + 47)
-#define IRQ_DB8500_DISP (IRQ_SHPI_START + 48)
-#define IRQ_DB8500_SPI3 (IRQ_SHPI_START + 49)
-#define IRQ_DB8500_SDMMC1 (IRQ_SHPI_START + 50)
-#define IRQ_DB8500_I2C4 (IRQ_SHPI_START + 51)
-#define IRQ_DB8500_SSP1 (IRQ_SHPI_START + 52)
-#define IRQ_DB8500_SKE (IRQ_SHPI_START + 53)
-#define IRQ_DB8500_KB (IRQ_SHPI_START + 54)
-#define IRQ_DB8500_I2C2 (IRQ_SHPI_START + 55)
-#define IRQ_DB8500_B2R2 (IRQ_SHPI_START + 56)
-#define IRQ_DB8500_CRYP0 (IRQ_SHPI_START + 57)
-#define IRQ_DB8500_SDMMC3 (IRQ_SHPI_START + 59)
-#define IRQ_DB8500_SDMMC0 (IRQ_SHPI_START + 60)
-#define IRQ_DB8500_HSEM (IRQ_SHPI_START + 61)
-#define IRQ_DB8500_MSP1 (IRQ_SHPI_START + 62)
-#define IRQ_DB8500_SBAG (IRQ_SHPI_START + 63)
-#define IRQ_DB8500_SPI1 (IRQ_SHPI_START + 96)
-#define IRQ_DB8500_SRPTIMER (IRQ_SHPI_START + 97)
-#define IRQ_DB8500_MSP2 (IRQ_SHPI_START + 98)
-#define IRQ_DB8500_SDMMC4 (IRQ_SHPI_START + 99)
-#define IRQ_DB8500_SDMMC5 (IRQ_SHPI_START + 100)
-#define IRQ_DB8500_HSIRD0 (IRQ_SHPI_START + 104)
-#define IRQ_DB8500_HSIRD1 (IRQ_SHPI_START + 105)
-#define IRQ_DB8500_HSITD0 (IRQ_SHPI_START + 106)
-#define IRQ_DB8500_HSITD1 (IRQ_SHPI_START + 107)
-#define IRQ_DB8500_CTI0 (IRQ_SHPI_START + 108)
-#define IRQ_DB8500_CTI1 (IRQ_SHPI_START + 109)
-#define IRQ_DB8500_ICN_ERR (IRQ_SHPI_START + 110)
-#define IRQ_DB8500_MALI_PPMMU (IRQ_SHPI_START + 112)
-#define IRQ_DB8500_MALI_PP (IRQ_SHPI_START + 113)
-#define IRQ_DB8500_MALI_GPMMU (IRQ_SHPI_START + 114)
-#define IRQ_DB8500_MALI_GP (IRQ_SHPI_START + 115)
-#define IRQ_DB8500_MALI (IRQ_SHPI_START + 116)
-#define IRQ_DB8500_PRCMU_SEM (IRQ_SHPI_START + 118)
-#define IRQ_DB8500_GPIO0 (IRQ_SHPI_START + 119)
-#define IRQ_DB8500_GPIO1 (IRQ_SHPI_START + 120)
-#define IRQ_DB8500_GPIO2 (IRQ_SHPI_START + 121)
-#define IRQ_DB8500_GPIO3 (IRQ_SHPI_START + 122)
-#define IRQ_DB8500_GPIO4 (IRQ_SHPI_START + 123)
-#define IRQ_DB8500_GPIO5 (IRQ_SHPI_START + 124)
-#define IRQ_DB8500_GPIO6 (IRQ_SHPI_START + 125)
-#define IRQ_DB8500_GPIO7 (IRQ_SHPI_START + 126)
-#define IRQ_DB8500_GPIO8 (IRQ_SHPI_START + 127)
-
-#define IRQ_CA_WAKE_REQ_ED (IRQ_SHPI_START + 71)
-#define IRQ_AC_READ_NOTIFICATION_0_ED (IRQ_SHPI_START + 66)
-#define IRQ_AC_READ_NOTIFICATION_1_ED (IRQ_SHPI_START + 64)
-#define IRQ_CA_MSG_PEND_NOTIFICATION_0_ED (IRQ_SHPI_START + 67)
-#define IRQ_CA_MSG_PEND_NOTIFICATION_1_ED (IRQ_SHPI_START + 65)
-
-#define IRQ_CA_WAKE_REQ_V1 (IRQ_SHPI_START + 83)
-#define IRQ_AC_READ_NOTIFICATION_0_V1 (IRQ_SHPI_START + 78)
-#define IRQ_AC_READ_NOTIFICATION_1_V1 (IRQ_SHPI_START + 76)
-#define IRQ_CA_MSG_PEND_NOTIFICATION_0_V1 (IRQ_SHPI_START + 79)
-#define IRQ_CA_MSG_PEND_NOTIFICATION_1_V1 (IRQ_SHPI_START + 77)
-
-#ifdef CONFIG_UX500_SOC_DB8500
-
-/* Virtual interrupts corresponding to the PRCMU wakeups. */
-#define IRQ_PRCMU_BASE IRQ_SOC_START
-#define IRQ_PRCMU_END (IRQ_PRCMU_BASE + 23)
-
-/*
- * We may have several SoCs, but only one will run at a
- * time, so the one with most IRQs will bump this ahead,
- * but the IRQ_SOC_START remains the same for either SoC.
- */
-#if IRQ_SOC_END < IRQ_PRCMU_END
-#undef IRQ_SOC_END
-#define IRQ_SOC_END IRQ_PRCMU_END
-#endif
-
-#endif /* CONFIG_UX500_SOC_DB8500 */
-#endif
diff --git a/arch/arm/mach-ux500/irqs.h b/arch/arm/mach-ux500/irqs.h
deleted file mode 100644
index 15b2af698ed7..000000000000
--- a/arch/arm/mach-ux500/irqs.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2008 STMicroelectronics
- * Copyright (C) 2009 ST-Ericsson.
- *
- * 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 ASM_ARCH_IRQS_H
-#define ASM_ARCH_IRQS_H
-
-#define IRQ_LOCALTIMER 29
-#define IRQ_LOCALWDOG 30
-
-/* Shared Peripheral Interrupt (SHPI) */
-#define IRQ_SHPI_START 32
-
-/*
- * MTU0 preserved for now until plat-nomadik is taught not to use it. Don't
- * add any other IRQs here, use the irqs-dbx500.h files.
- */
-#define IRQ_MTU0 (IRQ_SHPI_START + 4)
-
-#define DBX500_NR_INTERNAL_IRQS 166
-
-/* After chip-specific IRQ numbers we have the GPIO ones */
-#define NOMADIK_NR_GPIO 288
-#define NOMADIK_GPIO_TO_IRQ(gpio) ((gpio) + DBX500_NR_INTERNAL_IRQS)
-#define NOMADIK_IRQ_TO_GPIO(irq) ((irq) - DBX500_NR_INTERNAL_IRQS)
-#define IRQ_GPIO_END NOMADIK_GPIO_TO_IRQ(NOMADIK_NR_GPIO)
-
-#define IRQ_SOC_START IRQ_GPIO_END
-/* This will be overridden by SoC-specific irq headers */
-#define IRQ_SOC_END IRQ_SOC_START
-
-#include "irqs-db8500.h"
-
-#define IRQ_BOARD_START IRQ_SOC_END
-/* This will be overridden by board-specific irq headers */
-#define IRQ_BOARD_END IRQ_BOARD_START
-
-#ifdef CONFIG_MACH_MOP500
-#include "irqs-board-mop500.h"
-#endif
-
-#define UX500_NR_IRQS IRQ_BOARD_END
-
-#endif /* ASM_ARCH_IRQS_H */
diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c
index a335126ae18f..f2c89fb8fca9 100644
--- a/arch/arm/mach-versatile/core.c
+++ b/arch/arm/mach-versatile/core.c
@@ -108,7 +108,7 @@ void __init versatile_init_irq(void)
np = of_find_matching_node_by_address(NULL, vic_of_match,
VERSATILE_VIC_BASE);
- __vic_init(VA_VIC_BASE, IRQ_VIC_START, ~0, 0, np);
+ __vic_init(VA_VIC_BASE, 0, IRQ_VIC_START, ~0, 0, np);
writel(~0, VA_SIC_BASE + SIC_IRQ_ENABLE_CLEAR);
diff --git a/arch/arm/mach-versatile/include/mach/timex.h b/arch/arm/mach-versatile/include/mach/timex.h
deleted file mode 100644
index 426199b1add5..000000000000
--- a/arch/arm/mach-versatile/include/mach/timex.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * arch/arm/mach-versatile/include/mach/timex.h
- *
- * Versatile architecture timex specifications
- *
- * Copyright (C) 2003 ARM 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.
- *
- * 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
- */
-
-#define CLOCK_TICK_RATE (50000000 / 16)
diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig
index 4a70be485ff8..80b4be36f10a 100644
--- a/arch/arm/mach-vexpress/Kconfig
+++ b/arch/arm/mach-vexpress/Kconfig
@@ -5,16 +5,11 @@ config ARCH_VEXPRESS
select ARM_AMBA
select ARM_GIC
select ARM_TIMER_SP804
- select COMMON_CLK
select COMMON_CLK_VERSATILE
- select CPU_V7
- select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
select HAVE_PATA_PLATFORM
- select HAVE_SMP
select ICST
- select MIGHT_HAVE_CACHE_L2X0
select NO_IOPORT
select PLAT_VERSATILE
select PLAT_VERSATILE_CLCD
diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile
index 0997e0b7494c..fc649bc09d0c 100644
--- a/arch/arm/mach-vexpress/Makefile
+++ b/arch/arm/mach-vexpress/Makefile
@@ -8,8 +8,11 @@ obj-y := v2m.o
obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o
obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o
CFLAGS_dcscb.o += -march=armv7-a
+CFLAGS_REMOVE_dcscb.o = -pg
obj-$(CONFIG_ARCH_VEXPRESS_SPC) += spc.o
+CFLAGS_REMOVE_spc.o = -pg
obj-$(CONFIG_ARCH_VEXPRESS_TC2_PM) += tc2_pm.o
CFLAGS_tc2_pm.o += -march=armv7-a
+CFLAGS_REMOVE_tc2_pm.o = -pg
obj-$(CONFIG_SMP) += platsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-virt/Kconfig b/arch/arm/mach-virt/Kconfig
deleted file mode 100644
index 081d46929436..000000000000
--- a/arch/arm/mach-virt/Kconfig
+++ /dev/null
@@ -1,10 +0,0 @@
-config ARCH_VIRT
- bool "Dummy Virtual Machine" if ARCH_MULTI_V7
- select ARCH_WANT_OPTIONAL_GPIOLIB
- select ARM_GIC
- select HAVE_ARM_ARCH_TIMER
- select ARM_PSCI
- select HAVE_SMP
- select CPU_V7
- select SPARSE_IRQ
- select USE_OF
diff --git a/arch/arm/mach-virt/Makefile b/arch/arm/mach-virt/Makefile
deleted file mode 100644
index 7ddbfa60227f..000000000000
--- a/arch/arm/mach-virt/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-#
-# Makefile for the linux kernel.
-#
-
-obj-y := virt.o
diff --git a/arch/arm/mach-virt/virt.c b/arch/arm/mach-virt/virt.c
deleted file mode 100644
index b184e57d1854..000000000000
--- a/arch/arm/mach-virt/virt.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Dummy Virtual Machine - does what it says on the tin.
- *
- * Copyright (C) 2012 ARM Ltd
- * Authors: Will Deacon <will.deacon@arm.com>,
- * 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/of_irq.h>
-#include <linux/of_platform.h>
-#include <linux/smp.h>
-
-#include <asm/mach/arch.h>
-
-static void __init virt_init(void)
-{
- of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
-}
-
-static const char *virt_dt_match[] = {
- "linux,dummy-virt",
- "xen,xenvm",
- NULL
-};
-
-DT_MACHINE_START(VIRT, "Dummy Virtual Machine")
- .init_machine = virt_init,
- .dt_compat = virt_dt_match,
-MACHINE_END
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
index 927be93b692e..08f56a41cb55 100644
--- a/arch/arm/mach-vt8500/Kconfig
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -3,8 +3,6 @@ config ARCH_VT8500
select ARCH_HAS_CPUFREQ
select ARCH_REQUIRE_GPIOLIB
select CLKDEV_LOOKUP
- select CLKSRC_OF
- select GENERIC_CLOCKEVENTS
select VT8500_TIMER
select PINCTRL
help
@@ -21,7 +19,6 @@ config ARCH_WM8750
bool "WonderMedia WM8750"
depends on ARCH_MULTI_V6
select ARCH_VT8500
- select CPU_V6
help
Support for WonderMedia WM8750 System-on-Chip.
@@ -29,6 +26,5 @@ config ARCH_WM8850
bool "WonderMedia WM8850"
depends on ARCH_MULTI_V7
select ARCH_VT8500
- select CPU_V7
help
Support for WonderMedia WM8850 System-on-Chip.
diff --git a/arch/arm/mach-w90x900/include/mach/timex.h b/arch/arm/mach-w90x900/include/mach/timex.h
deleted file mode 100644
index 164dce0b64db..000000000000
--- a/arch/arm/mach-w90x900/include/mach/timex.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * arch/arm/mach-w90x900/include/mach/timex.h
- *
- * Copyright (c) 2008 Nuvoton technology corporation
- * All rights reserved.
- *
- * Wan ZongShun <mcuos.com@gmail.com>
- *
- * Based on arch/arm/mach-s3c2410/include/mach/timex.h
- *
- * 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 __ASM_ARCH_TIMEX_H
-#define __ASM_ARCH_TIMEX_H
-
-/* CLOCK_TICK_RATE Now, I don't use it. */
-
-#define CLOCK_TICK_RATE 15000000
-
-#endif /* __ASM_ARCH_TIMEX_H */
diff --git a/arch/arm/mach-w90x900/time.c b/arch/arm/mach-w90x900/time.c
index 30fbca844575..9230d3725599 100644
--- a/arch/arm/mach-w90x900/time.c
+++ b/arch/arm/mach-w90x900/time.c
@@ -111,7 +111,7 @@ static irqreturn_t nuc900_timer0_interrupt(int irq, void *dev_id)
static struct irqaction nuc900_timer0_irq = {
.name = "nuc900-timer0",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = nuc900_timer0_interrupt,
};
diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig
index f03e75bd0b2b..58c2b844e0a3 100644
--- a/arch/arm/mach-zynq/Kconfig
+++ b/arch/arm/mach-zynq/Kconfig
@@ -4,17 +4,11 @@ config ARCH_ZYNQ
select ARM_GIC
select ARCH_HAS_CPUFREQ
select ARCH_HAS_OPP
- select COMMON_CLK
- select CPU_V7
- select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
select ICST
- select MIGHT_HAVE_CACHE_L2X0
- select USE_OF
- select HAVE_SMP
- select SPARSE_IRQ
select CADENCE_TTC_TIMER
select ARM_GLOBAL_TIMER if !CPU_FREQ
+ select MFD_SYSCON
help
Support for Xilinx Zynq ARM Cortex A9 Platform
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index a39be8e80856..6fcc584c1a11 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -19,6 +19,7 @@
#include <linux/cpumask.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/clk/zynq.h>
#include <linux/clocksource.h>
#include <linux/of_address.h>
@@ -75,11 +76,16 @@ static void __init zynq_init_machine(void)
platform_device_register(&zynq_cpuidle_device);
platform_device_register_full(&devinfo);
+
+ zynq_slcr_init();
}
static void __init zynq_timer_init(void)
{
- zynq_slcr_init();
+ zynq_early_slcr_init();
+
+ zynq_clock_init();
+ of_clk_init(NULL);
clocksource_of_init();
}
diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h
index c22c92cea8cb..b097844d3175 100644
--- a/arch/arm/mach-zynq/common.h
+++ b/arch/arm/mach-zynq/common.h
@@ -20,6 +20,7 @@
void zynq_secondary_startup(void);
extern int zynq_slcr_init(void);
+extern int zynq_early_slcr_init(void);
extern void zynq_slcr_system_reset(void);
extern void zynq_slcr_cpu_stop(int cpu);
extern void zynq_slcr_cpu_start(int cpu);
@@ -33,7 +34,6 @@ extern int zynq_cpun_start(u32 address, int cpu);
extern struct smp_operations zynq_smp_ops __initdata;
#endif
-extern void __iomem *zynq_slcr_base;
extern void __iomem *zynq_scu_base;
/* Hotplug */
diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c
index 1836d5a34606..a37d49a6e657 100644
--- a/arch/arm/mach-zynq/slcr.c
+++ b/arch/arm/mach-zynq/slcr.c
@@ -15,7 +15,9 @@
*/
#include <linux/io.h>
+#include <linux/mfd/syscon.h>
#include <linux/of_address.h>
+#include <linux/regmap.h>
#include <linux/clk/zynq.h>
#include "common.h"
@@ -29,7 +31,56 @@
#define SLCR_A9_CPU_CLKSTOP 0x10
#define SLCR_A9_CPU_RST 0x1
-void __iomem *zynq_slcr_base;
+static void __iomem *zynq_slcr_base;
+static struct regmap *zynq_slcr_regmap;
+
+/**
+ * zynq_slcr_write - Write to a register in SLCR block
+ *
+ * @val: Value to write to the register
+ * @offset: Register offset in SLCR block
+ *
+ * Return: a negative value on error, 0 on success
+ */
+static int zynq_slcr_write(u32 val, u32 offset)
+{
+ if (!zynq_slcr_regmap) {
+ writel(val, zynq_slcr_base + offset);
+ return 0;
+ }
+
+ return regmap_write(zynq_slcr_regmap, offset, val);
+}
+
+/**
+ * zynq_slcr_read - Read a register in SLCR block
+ *
+ * @val: Pointer to value to be read from SLCR
+ * @offset: Register offset in SLCR block
+ *
+ * Return: a negative value on error, 0 on success
+ */
+static int zynq_slcr_read(u32 *val, u32 offset)
+{
+ if (zynq_slcr_regmap)
+ return regmap_read(zynq_slcr_regmap, offset, val);
+
+ *val = readl(zynq_slcr_base + offset);
+
+ return 0;
+}
+
+/**
+ * zynq_slcr_unlock - Unlock SLCR registers
+ *
+ * Return: a negative value on error, 0 on success
+ */
+static inline int zynq_slcr_unlock(void)
+{
+ zynq_slcr_write(SLCR_UNLOCK_MAGIC, SLCR_UNLOCK_OFFSET);
+
+ return 0;
+}
/**
* zynq_slcr_system_reset - Reset the entire system.
@@ -43,16 +94,16 @@ void zynq_slcr_system_reset(void)
* Note that this seems to require raw i/o
* functions or there's a lockup?
*/
- writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK_OFFSET);
+ zynq_slcr_unlock();
/*
* Clear 0x0F000000 bits of reboot status register to workaround
* the FSBL not loading the bitstream after soft-reboot
* This is a temporary solution until we know more.
*/
- reboot = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
- writel(reboot & 0xF0FFFFFF, zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
- writel(1, zynq_slcr_base + SLCR_PS_RST_CTRL_OFFSET);
+ zynq_slcr_read(&reboot, SLCR_REBOOT_STATUS_OFFSET);
+ zynq_slcr_write(reboot & 0xF0FFFFFF, SLCR_REBOOT_STATUS_OFFSET);
+ zynq_slcr_write(1, SLCR_PS_RST_CTRL_OFFSET);
}
/**
@@ -61,11 +112,13 @@ void zynq_slcr_system_reset(void)
*/
void zynq_slcr_cpu_start(int cpu)
{
- u32 reg = readl(zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET);
+ u32 reg;
+
+ zynq_slcr_read(&reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
reg &= ~(SLCR_A9_CPU_RST << cpu);
- writel(reg, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET);
+ zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu);
- writel(reg, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET);
+ zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
}
/**
@@ -74,19 +127,40 @@ void zynq_slcr_cpu_start(int cpu)
*/
void zynq_slcr_cpu_stop(int cpu)
{
- u32 reg = readl(zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET);
+ u32 reg;
+
+ zynq_slcr_read(&reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
reg |= (SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu;
- writel(reg, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET);
+ zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
}
/**
- * zynq_slcr_init
- * Returns 0 on success, negative errno otherwise.
+ * zynq_slcr_init - Regular slcr driver init
+ *
+ * Return: 0 on success, negative errno otherwise.
*
* Called early during boot from platform code to remap SLCR area.
*/
int __init zynq_slcr_init(void)
{
+ zynq_slcr_regmap = syscon_regmap_lookup_by_compatible("xlnx,zynq-slcr");
+ if (IS_ERR(zynq_slcr_regmap)) {
+ pr_err("%s: failed to find zynq-slcr\n", __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
+ * zynq_early_slcr_init - Early slcr init function
+ *
+ * Return: 0 on success, negative errno otherwise.
+ *
+ * Called very early during boot from platform code to unlock SLCR.
+ */
+int __init zynq_early_slcr_init(void)
+{
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
@@ -101,13 +175,13 @@ int __init zynq_slcr_init(void)
BUG();
}
+ np->data = (__force void *)zynq_slcr_base;
+
/* unlock the SLCR so that registers can be changed */
- writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK_OFFSET);
+ zynq_slcr_unlock();
pr_info("%s mapped to %p\n", np->name, zynq_slcr_base);
- zynq_clock_init(zynq_slcr_base);
-
of_node_put(np);
return 0;
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 1f8fed94c2a4..f5ad9ee70426 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -264,7 +264,7 @@ config CPU_ARM1026
# SA110
config CPU_SA110
- bool "Support StrongARM(R) SA-110 processor" if ARCH_RPC
+ bool
select CPU_32v3 if ARCH_RPC
select CPU_32v4 if !ARCH_RPC
select CPU_ABRT_EV4
@@ -446,7 +446,6 @@ config CPU_32v5
config CPU_32v6
bool
- select CPU_USE_DOMAINS if CPU_V6 && MMU
select TLS_REG_EMUL if !CPU_32v6K && !MMU
config CPU_32v6K
@@ -671,7 +670,7 @@ config ARM_VIRT_EXT
config SWP_EMULATE
bool "Emulate SWP/SWPB instructions"
- depends on !CPU_USE_DOMAINS && CPU_V7
+ depends on CPU_V7
default y if SMP
select HAVE_PROC_CPU if PROC_FS
help
@@ -855,7 +854,7 @@ config OUTER_CACHE_SYNC
config CACHE_FEROCEON_L2
bool "Enable the Feroceon L2 cache controller"
- depends on ARCH_KIRKWOOD || ARCH_MV78XX0
+ depends on ARCH_KIRKWOOD || ARCH_MV78XX0 || ARCH_MVEBU
default y
select OUTER_CACHE
help
diff --git a/arch/arm/mm/cache-feroceon-l2.c b/arch/arm/mm/cache-feroceon-l2.c
index 48bc3c0a87ce..dc814a548056 100644
--- a/arch/arm/mm/cache-feroceon-l2.c
+++ b/arch/arm/mm/cache-feroceon-l2.c
@@ -13,10 +13,15 @@
*/
#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/highmem.h>
+#include <linux/io.h>
#include <asm/cacheflush.h>
#include <asm/cp15.h>
-#include <plat/cache-feroceon-l2.h>
+#include <asm/hardware/cache-feroceon-l2.h>
+
+#define L2_WRITETHROUGH_KIRKWOOD BIT(4)
/*
* Low-level cache maintenance operations.
@@ -331,7 +336,9 @@ static void __init enable_l2(void)
enable_icache();
if (d)
enable_dcache();
- }
+ } else
+ pr_err(FW_BUG
+ "Feroceon L2: bootloader left the L2 cache on!\n");
}
void __init feroceon_l2_init(int __l2_wt_override)
@@ -350,3 +357,41 @@ void __init feroceon_l2_init(int __l2_wt_override)
printk(KERN_INFO "Feroceon L2: Cache support initialised%s.\n",
l2_wt_override ? ", in WT override mode" : "");
}
+#ifdef CONFIG_OF
+static const struct of_device_id feroceon_ids[] __initconst = {
+ { .compatible = "marvell,kirkwood-cache"},
+ { .compatible = "marvell,feroceon-cache"},
+ {}
+};
+
+int __init feroceon_of_init(void)
+{
+ struct device_node *node;
+ void __iomem *base;
+ bool l2_wt_override = false;
+ struct resource res;
+
+#if defined(CONFIG_CACHE_FEROCEON_L2_WRITETHROUGH)
+ l2_wt_override = true;
+#endif
+
+ node = of_find_matching_node(NULL, feroceon_ids);
+ if (node && of_device_is_compatible(node, "marvell,kirkwood-cache")) {
+ if (of_address_to_resource(node, 0, &res))
+ return -ENODEV;
+
+ base = ioremap(res.start, resource_size(&res));
+ if (!base)
+ return -ENOMEM;
+
+ if (l2_wt_override)
+ writel(readl(base) | L2_WRITETHROUGH_KIRKWOOD, base);
+ else
+ writel(readl(base) & ~L2_WRITETHROUGH_KIRKWOOD, base);
+ }
+
+ feroceon_l2_init(l2_wt_override);
+
+ return 0;
+}
+#endif
diff --git a/arch/arm/mm/cache-tauros2.c b/arch/arm/mm/cache-tauros2.c
index 1be0f4e5e6eb..b273739e6359 100644
--- a/arch/arm/mm/cache-tauros2.c
+++ b/arch/arm/mm/cache-tauros2.c
@@ -33,7 +33,7 @@
* outer cache operations into the kernel image if the kernel has been
* configured to support a pre-v7 CPU.
*/
-#if __LINUX_ARM_ARCH__ < 7
+#ifdef CONFIG_CPU_32v5
/*
* Low-level cache maintenance operations.
*/
@@ -229,33 +229,6 @@ static void __init tauros2_internal_init(unsigned int features)
}
#endif
-#ifdef CONFIG_CPU_32v6
- /*
- * Check whether this CPU lacks support for the v7 hierarchical
- * cache ops. (PJ4 is in its v6 personality mode if the MMFR3
- * register indicates no support for the v7 hierarchical cache
- * ops.)
- */
- if (cpuid_scheme() && (read_mmfr3() & 0xf) == 0) {
- /*
- * When Tauros2 is used in an ARMv6 system, the L2
- * enable bit is in the ARMv6 ARM-mandated position
- * (bit [26] of the System Control Register).
- */
- if (!(get_cr() & 0x04000000)) {
- printk(KERN_INFO "Tauros2: Enabling L2 cache.\n");
- adjust_cr(0x04000000, 0x04000000);
- }
-
- mode = "ARMv6";
- outer_cache.inv_range = tauros2_inv_range;
- outer_cache.clean_range = tauros2_clean_range;
- outer_cache.flush_range = tauros2_flush_range;
- outer_cache.disable = tauros2_disable;
- outer_cache.resume = tauros2_resume;
- }
-#endif
-
#ifdef CONFIG_CPU_32v7
/*
* Check whether this CPU has support for the v7 hierarchical
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 11b3914660d2..f62aa0677e5c 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -284,9 +284,6 @@ static void __dma_free_buffer(struct page *page, size_t size)
}
#ifdef CONFIG_MMU
-#ifdef CONFIG_HUGETLB_PAGE
-#warning ARM Coherent DMA allocator does not (yet) support huge TLB
-#endif
static void *__alloc_from_contiguous(struct device *dev, size_t size,
pgprot_t prot, struct page **ret_page,
@@ -1069,6 +1066,8 @@ fs_initcall(dma_debug_do_init);
/* IOMMU */
+static int extend_iommu_mapping(struct dma_iommu_mapping *mapping);
+
static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping,
size_t size)
{
@@ -1076,41 +1075,87 @@ static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping,
unsigned int align = 0;
unsigned int count, start;
unsigned long flags;
+ dma_addr_t iova;
+ int i;
if (order > CONFIG_ARM_DMA_IOMMU_ALIGNMENT)
order = CONFIG_ARM_DMA_IOMMU_ALIGNMENT;
- count = ((PAGE_ALIGN(size) >> PAGE_SHIFT) +
- (1 << mapping->order) - 1) >> mapping->order;
-
- if (order > mapping->order)
- align = (1 << (order - mapping->order)) - 1;
+ count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ align = (1 << order) - 1;
spin_lock_irqsave(&mapping->lock, flags);
- start = bitmap_find_next_zero_area(mapping->bitmap, mapping->bits, 0,
- count, align);
- if (start > mapping->bits) {
- spin_unlock_irqrestore(&mapping->lock, flags);
- return DMA_ERROR_CODE;
+ for (i = 0; i < mapping->nr_bitmaps; i++) {
+ start = bitmap_find_next_zero_area(mapping->bitmaps[i],
+ mapping->bits, 0, count, align);
+
+ if (start > mapping->bits)
+ continue;
+
+ bitmap_set(mapping->bitmaps[i], start, count);
+ break;
}
- bitmap_set(mapping->bitmap, start, count);
+ /*
+ * No unused range found. Try to extend the existing mapping
+ * and perform a second attempt to reserve an IO virtual
+ * address range of size bytes.
+ */
+ if (i == mapping->nr_bitmaps) {
+ if (extend_iommu_mapping(mapping)) {
+ spin_unlock_irqrestore(&mapping->lock, flags);
+ return DMA_ERROR_CODE;
+ }
+
+ start = bitmap_find_next_zero_area(mapping->bitmaps[i],
+ mapping->bits, 0, count, align);
+
+ if (start > mapping->bits) {
+ spin_unlock_irqrestore(&mapping->lock, flags);
+ return DMA_ERROR_CODE;
+ }
+
+ bitmap_set(mapping->bitmaps[i], start, count);
+ }
spin_unlock_irqrestore(&mapping->lock, flags);
- return mapping->base + (start << (mapping->order + PAGE_SHIFT));
+ iova = mapping->base + (mapping->size * i);
+ iova += start << PAGE_SHIFT;
+
+ return iova;
}
static inline void __free_iova(struct dma_iommu_mapping *mapping,
dma_addr_t addr, size_t size)
{
- unsigned int start = (addr - mapping->base) >>
- (mapping->order + PAGE_SHIFT);
- unsigned int count = ((size >> PAGE_SHIFT) +
- (1 << mapping->order) - 1) >> mapping->order;
+ unsigned int start, count;
unsigned long flags;
+ dma_addr_t bitmap_base;
+ u32 bitmap_index;
+
+ if (!size)
+ return;
+
+ bitmap_index = (u32) (addr - mapping->base) / (u32) mapping->size;
+ BUG_ON(addr < mapping->base || bitmap_index > mapping->extensions);
+
+ bitmap_base = mapping->base + mapping->size * bitmap_index;
+
+ start = (addr - bitmap_base) >> PAGE_SHIFT;
+
+ if (addr + size > bitmap_base + mapping->size) {
+ /*
+ * The address range to be freed reaches into the iova
+ * range of the next bitmap. This should not happen as
+ * we don't allow this in __alloc_iova (at the
+ * moment).
+ */
+ BUG();
+ } else
+ count = size >> PAGE_SHIFT;
spin_lock_irqsave(&mapping->lock, flags);
- bitmap_clear(mapping->bitmap, start, count);
+ bitmap_clear(mapping->bitmaps[bitmap_index], start, count);
spin_unlock_irqrestore(&mapping->lock, flags);
}
@@ -1875,8 +1920,7 @@ struct dma_map_ops iommu_coherent_ops = {
* arm_iommu_create_mapping
* @bus: pointer to the bus holding the client device (for IOMMU calls)
* @base: start address of the valid IO address space
- * @size: size of the valid IO address space
- * @order: accuracy of the IO addresses allocations
+ * @size: maximum size of the valid IO address space
*
* Creates a mapping structure which holds information about used/unused
* IO address ranges, which is required to perform memory allocation and
@@ -1886,38 +1930,54 @@ struct dma_map_ops iommu_coherent_ops = {
* arm_iommu_attach_device function.
*/
struct dma_iommu_mapping *
-arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size,
- int order)
+arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size)
{
- unsigned int count = size >> (PAGE_SHIFT + order);
- unsigned int bitmap_size = BITS_TO_LONGS(count) * sizeof(long);
+ unsigned int bits = size >> PAGE_SHIFT;
+ unsigned int bitmap_size = BITS_TO_LONGS(bits) * sizeof(long);
struct dma_iommu_mapping *mapping;
+ int extensions = 1;
int err = -ENOMEM;
- if (!count)
+ if (!bitmap_size)
return ERR_PTR(-EINVAL);
+ if (bitmap_size > PAGE_SIZE) {
+ extensions = bitmap_size / PAGE_SIZE;
+ bitmap_size = PAGE_SIZE;
+ }
+
mapping = kzalloc(sizeof(struct dma_iommu_mapping), GFP_KERNEL);
if (!mapping)
goto err;
- mapping->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
- if (!mapping->bitmap)
+ mapping->bitmap_size = bitmap_size;
+ mapping->bitmaps = kzalloc(extensions * sizeof(unsigned long *),
+ GFP_KERNEL);
+ if (!mapping->bitmaps)
goto err2;
+ mapping->bitmaps[0] = kzalloc(bitmap_size, GFP_KERNEL);
+ if (!mapping->bitmaps[0])
+ goto err3;
+
+ mapping->nr_bitmaps = 1;
+ mapping->extensions = extensions;
mapping->base = base;
+ mapping->size = bitmap_size << PAGE_SHIFT;
mapping->bits = BITS_PER_BYTE * bitmap_size;
- mapping->order = order;
+
spin_lock_init(&mapping->lock);
mapping->domain = iommu_domain_alloc(bus);
if (!mapping->domain)
- goto err3;
+ goto err4;
kref_init(&mapping->kref);
return mapping;
+err4:
+ kfree(mapping->bitmaps[0]);
err3:
- kfree(mapping->bitmap);
+ kfree(mapping->bitmaps);
err2:
kfree(mapping);
err:
@@ -1927,14 +1987,35 @@ EXPORT_SYMBOL_GPL(arm_iommu_create_mapping);
static void release_iommu_mapping(struct kref *kref)
{
+ int i;
struct dma_iommu_mapping *mapping =
container_of(kref, struct dma_iommu_mapping, kref);
iommu_domain_free(mapping->domain);
- kfree(mapping->bitmap);
+ for (i = 0; i < mapping->nr_bitmaps; i++)
+ kfree(mapping->bitmaps[i]);
+ kfree(mapping->bitmaps);
kfree(mapping);
}
+static int extend_iommu_mapping(struct dma_iommu_mapping *mapping)
+{
+ int next_bitmap;
+
+ if (mapping->nr_bitmaps > mapping->extensions)
+ return -EINVAL;
+
+ next_bitmap = mapping->nr_bitmaps;
+ mapping->bitmaps[next_bitmap] = kzalloc(mapping->bitmap_size,
+ GFP_ATOMIC);
+ if (!mapping->bitmaps[next_bitmap])
+ return -ENOMEM;
+
+ mapping->nr_bitmaps++;
+
+ return 0;
+}
+
void arm_iommu_release_mapping(struct dma_iommu_mapping *mapping)
{
if (mapping)
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 804d61566a53..2a77ba8796ae 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -323,6 +323,8 @@ void __init arm_memblock_init(struct meminfo *mi,
if (mdesc->reserve)
mdesc->reserve();
+ early_init_fdt_scan_reserved_mem();
+
/*
* reserve memory for DMA contigouos allocations,
* must come from DMA area inside low memory
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index a623cb3ad012..b68c6b22e1c8 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -516,6 +516,16 @@ static void __init build_mem_type_table(void)
s2_device_pgprot = mem_types[MT_DEVICE].prot_pte_s2;
/*
+ * We don't use domains on ARMv6 (since this causes problems with
+ * v6/v7 kernels), so we must use a separate memory type for user
+ * r/o, kernel r/w to map the vectors page.
+ */
+#ifndef CONFIG_ARM_LPAE
+ if (cpu_arch == CPU_ARCH_ARMv6)
+ vecs_pgprot |= L_PTE_MT_VECTORS;
+#endif
+
+ /*
* ARMv6 and above have extended page tables.
*/
if (cpu_arch >= CPU_ARCH_ARMv6 && (cr & CR_XP)) {
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S
index e3c48a3fe063..ee1d80593958 100644
--- a/arch/arm/mm/proc-macros.S
+++ b/arch/arm/mm/proc-macros.S
@@ -112,13 +112,9 @@
* 100x 1 0 1 r/o no acc
* 10x0 1 0 1 r/o no acc
* 1011 0 0 1 r/w no acc
- * 110x 0 1 0 r/w r/o
- * 11x0 0 1 0 r/w r/o
- * 1111 0 1 1 r/w r/w
- *
- * If !CONFIG_CPU_USE_DOMAINS, the following permissions are changed:
* 110x 1 1 1 r/o r/o
* 11x0 1 1 1 r/o r/o
+ * 1111 0 1 1 r/w r/w
*/
.macro armv6_mt_table pfx
\pfx\()_mt_table:
@@ -137,7 +133,7 @@
.long PTE_EXT_TEX(2) @ L_PTE_MT_DEV_NONSHARED
.long 0x00 @ unused
.long 0x00 @ unused
- .long 0x00 @ unused
+ .long PTE_CACHEABLE | PTE_BUFFERABLE | PTE_EXT_APX @ L_PTE_MT_VECTORS
.endm
.macro armv6_set_pte_ext pfx
@@ -158,24 +154,21 @@
tst r1, #L_PTE_USER
orrne r3, r3, #PTE_EXT_AP1
-#ifdef CONFIG_CPU_USE_DOMAINS
- @ allow kernel read/write access to read-only user pages
tstne r3, #PTE_EXT_APX
- bicne r3, r3, #PTE_EXT_APX | PTE_EXT_AP0
-#endif
+
+ @ user read-only -> kernel read-only
+ bicne r3, r3, #PTE_EXT_AP0
tst r1, #L_PTE_XN
orrne r3, r3, #PTE_EXT_XN
- orr r3, r3, r2
+ eor r3, r3, r2
tst r1, #L_PTE_YOUNG
tstne r1, #L_PTE_PRESENT
moveq r3, #0
-#ifndef CONFIG_CPU_USE_DOMAINS
tstne r1, #L_PTE_NONE
movne r3, #0
-#endif
str r3, [r0]
mcr p15, 0, r0, c7, c10, 1 @ flush_pte
diff --git a/arch/arm/mm/proc-v7-2level.S b/arch/arm/mm/proc-v7-2level.S
index bdd3be4be77a..1f52915f2b28 100644
--- a/arch/arm/mm/proc-v7-2level.S
+++ b/arch/arm/mm/proc-v7-2level.S
@@ -90,21 +90,14 @@ ENTRY(cpu_v7_set_pte_ext)
tst r1, #L_PTE_USER
orrne r3, r3, #PTE_EXT_AP1
-#ifdef CONFIG_CPU_USE_DOMAINS
- @ allow kernel read/write access to read-only user pages
- tstne r3, #PTE_EXT_APX
- bicne r3, r3, #PTE_EXT_APX | PTE_EXT_AP0
-#endif
tst r1, #L_PTE_XN
orrne r3, r3, #PTE_EXT_XN
tst r1, #L_PTE_YOUNG
tstne r1, #L_PTE_VALID
-#ifndef CONFIG_CPU_USE_DOMAINS
eorne r1, r1, #L_PTE_NONE
tstne r1, #L_PTE_NONE
-#endif
moveq r3, #0
ARM( str r3, [r0, #2048]! )
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index 74f6033e76dd..195731d3813b 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -192,6 +192,7 @@ __v7_cr7mp_setup:
mov r10, #(1 << 0) @ Cache/TLB ops broadcasting
b 1f
__v7_ca7mp_setup:
+__v7_ca12mp_setup:
__v7_ca15mp_setup:
mov r10, #0
1:
@@ -484,6 +485,16 @@ __v7_ca7mp_proc_info:
.size __v7_ca7mp_proc_info, . - __v7_ca7mp_proc_info
/*
+ * ARM Ltd. Cortex A12 processor.
+ */
+ .type __v7_ca12mp_proc_info, #object
+__v7_ca12mp_proc_info:
+ .long 0x410fc0d0
+ .long 0xff0ffff0
+ __v7_proc __v7_ca12mp_setup
+ .size __v7_ca12mp_proc_info, . - __v7_ca12mp_proc_info
+
+ /*
* ARM Ltd. Cortex A15 processor.
*/
.type __v7_ca15mp_proc_info, #object
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c
index 271b5e971568..6f879c319a9d 100644
--- a/arch/arm/net/bpf_jit_32.c
+++ b/arch/arm/net/bpf_jit_32.c
@@ -825,8 +825,8 @@ b_epilogue:
break;
case BPF_S_ANC_RXHASH:
ctx->seen |= SEEN_SKB;
- BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, rxhash) != 4);
- off = offsetof(struct sk_buff, rxhash);
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
+ off = offsetof(struct sk_buff, hash);
emit(ARM_LDR_I(r_A, r_skb, off), ctx);
break;
case BPF_S_ANC_VLAN_TAG:
@@ -925,6 +925,7 @@ void bpf_jit_compile(struct sk_filter *fp)
bpf_jit_dump(fp->len, alloc_size, 2, ctx.target);
fp->bpf_func = (void *)ctx.target;
+ fp->jited = 1;
out:
kfree(ctx.offsets);
return;
@@ -932,7 +933,7 @@ out:
void bpf_jit_free(struct sk_filter *fp)
{
- if (fp->bpf_func != sk_run_filter)
+ if (fp->jited)
module_free(NULL, fp->bpf_func);
kfree(fp);
}
diff --git a/arch/arm/plat-iop/time.c b/arch/arm/plat-iop/time.c
index d70b73364a3f..6ad65d8ae237 100644
--- a/arch/arm/plat-iop/time.c
+++ b/arch/arm/plat-iop/time.c
@@ -127,7 +127,7 @@ iop_timer_interrupt(int irq, void *dev_id)
static struct irqaction iop_timer_irq = {
.name = "IOP Timer Tick",
.handler = iop_timer_interrupt,
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.dev_id = &iop_clockevent,
};
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index 436ea97074cd..02fc10d2d63b 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -86,9 +86,6 @@ config OMAP_MUX_WARNINGS
to change the pin multiplexing setup. When there are no warnings
printed, it's safe to deselect OMAP_MUX for your product.
-config OMAP_IOMMU_IVA2
- bool
-
config OMAP_MPU_TIMER
bool "Use mpu timer"
depends on ARCH_OMAP1
diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
index 01619c2910e3..5f5b975887fc 100644
--- a/arch/arm/plat-omap/dma.c
+++ b/arch/arm/plat-omap/dma.c
@@ -2000,6 +2000,12 @@ void omap_dma_global_context_restore(void)
omap_clear_dma(ch);
}
+struct omap_system_dma_plat_info *omap_get_plat_info(void)
+{
+ return p;
+}
+EXPORT_SYMBOL_GPL(omap_get_plat_info);
+
static int omap_system_dma_probe(struct platform_device *pdev)
{
int ch, ret = 0;
@@ -2024,9 +2030,16 @@ static int omap_system_dma_probe(struct platform_device *pdev)
dma_lch_count = d->lch_count;
dma_chan_count = dma_lch_count;
- dma_chan = d->chan;
enable_1510_mode = d->dev_caps & ENABLE_1510_MODE;
+ dma_chan = devm_kcalloc(&pdev->dev, dma_lch_count,
+ sizeof(struct omap_dma_lch), GFP_KERNEL);
+ if (!dma_chan) {
+ dev_err(&pdev->dev, "%s: kzalloc fail\n", __func__);
+ return -ENOMEM;
+ }
+
+
if (dma_omap2plus()) {
dma_linked_lch = kzalloc(sizeof(struct dma_link_info) *
dma_lch_count, GFP_KERNEL);
@@ -2111,7 +2124,6 @@ exit_dma_irq_fail:
}
exit_dma_lch_fail:
- kfree(dma_chan);
return ret;
}
@@ -2131,7 +2143,6 @@ static int omap_system_dma_remove(struct platform_device *pdev)
free_irq(dma_irq, (void *)(irq_rel + 1));
}
}
- kfree(dma_chan);
return 0;
}
diff --git a/arch/arm/plat-omap/include/plat/timex.h b/arch/arm/plat-omap/include/plat/timex.h
deleted file mode 100644
index e27d2daa7790..000000000000
--- a/arch/arm/plat-omap/include/plat/timex.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * arch/arm/plat-omap/include/mach/timex.h
- *
- * Copyright (C) 2000 RidgeRun, Inc.
- * Author: Greg Lonnon <glonnon@ridgerun.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#if !defined(__ASM_ARCH_OMAP_TIMEX_H)
-#define __ASM_ARCH_OMAP_TIMEX_H
-
-#define CLOCK_TICK_RATE (HZ * 100000UL)
-
-#endif /* __ASM_ARCH_OMAP_TIMEX_H */
diff --git a/arch/arm/plat-orion/common.c b/arch/arm/plat-orion/common.c
index 830ff07f3385..3ec6e8e8d368 100644
--- a/arch/arm/plat-orion/common.c
+++ b/arch/arm/plat-orion/common.c
@@ -595,14 +595,16 @@ void __init orion_spi_1_init(unsigned long mapbase)
/*****************************************************************************
* Watchdog
****************************************************************************/
-static struct resource orion_wdt_resource =
- DEFINE_RES_MEM(TIMER_PHYS_BASE, 0x28);
+static struct resource orion_wdt_resource[] = {
+ DEFINE_RES_MEM(TIMER_PHYS_BASE, 0x04),
+ DEFINE_RES_MEM(RSTOUTn_MASK_PHYS, 0x04),
+};
static struct platform_device orion_wdt_device = {
.name = "orion_wdt",
.id = -1,
- .num_resources = 1,
- .resource = &orion_wdt_resource,
+ .num_resources = ARRAY_SIZE(orion_wdt_resource),
+ .resource = orion_wdt_resource,
};
void __init orion_wdt_init(void)
diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig
index 58645a58d0d8..b57e922f1614 100644
--- a/arch/arm/plat-samsung/Kconfig
+++ b/arch/arm/plat-samsung/Kconfig
@@ -427,8 +427,7 @@ comment "Power management"
config SAMSUNG_PM_DEBUG
bool "S3C2410 PM Suspend debug"
- depends on PM
- select DEBUG_LL
+ depends on PM && DEBUG_KERNEL && DEBUG_S3C_UART
help
Say Y here if you want verbose debugging from the PM Suspend and
Resume code. See <file:Documentation/arm/Samsung-S3C24XX/Suspend.txt>
@@ -445,7 +444,8 @@ config S3C_PM_DEBUG_LED_SMDK
config SAMSUNG_PM_CHECK
bool "S3C2410 PM Suspend Memory CRC"
- depends on PM && CRC32
+ depends on PM
+ select CRC32
help
Enable the PM code's memory area checksum over sleep. This option
will generate CRCs of all blocks of memory, and store them before
diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile
index 9267d29549b4..25c826ed3b65 100644
--- a/arch/arm/plat-samsung/Makefile
+++ b/arch/arm/plat-samsung/Makefile
@@ -47,9 +47,11 @@ obj-$(CONFIG_SAMSUNG_DMADEV) += dma-ops.o
# PM support
+obj-$(CONFIG_PM_SLEEP) += pm-common.o
obj-$(CONFIG_SAMSUNG_PM) += pm.o
obj-$(CONFIG_SAMSUNG_PM_GPIO) += pm-gpio.o
obj-$(CONFIG_SAMSUNG_PM_CHECK) += pm-check.o
+obj-$(CONFIG_SAMSUNG_PM_DEBUG) += pm-debug.o
obj-$(CONFIG_SAMSUNG_WAKEMASK) += wakeup-mask.o
obj-$(CONFIG_SAMSUNG_WDT_RESET) += watchdog-reset.o
diff --git a/arch/arm/plat-samsung/clock.c b/arch/arm/plat-samsung/clock.c
index 47c9fad43f00..d103ac1a52af 100644
--- a/arch/arm/plat-samsung/clock.c
+++ b/arch/arm/plat-samsung/clock.c
@@ -43,7 +43,6 @@
#include <linux/debugfs.h>
#endif
-#include <mach/hardware.h>
#include <asm/irq.h>
#include <plat/cpu-freq.h>
@@ -52,7 +51,7 @@
#include <plat/cpu.h>
#include <linux/serial_core.h>
-#include <plat/regs-serial.h> /* for s3c24xx_uart_devs */
+#include <linux/serial_s3c.h> /* for s3c24xx_uart_devs */
/* clock information */
diff --git a/arch/arm/plat-samsung/cpu.c b/arch/arm/plat-samsung/cpu.c
index 46b426e8aff5..364963a0a344 100644
--- a/arch/arm/plat-samsung/cpu.c
+++ b/arch/arm/plat-samsung/cpu.c
@@ -28,13 +28,6 @@ unsigned int samsung_rev(void)
}
EXPORT_SYMBOL(samsung_rev);
-void __init s3c24xx_init_cpu(void)
-{
- /* nothing here yet */
-
- samsung_cpu_rev = 0;
-}
-
void __init s3c64xx_init_cpu(void)
{
samsung_cpu_id = __raw_readl(S3C_VA_SYS + 0x118);
diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c
index ac07e871f6a7..ead4f1c94058 100644
--- a/arch/arm/plat-samsung/devs.c
+++ b/arch/arm/plat-samsung/devs.c
@@ -18,6 +18,7 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -30,6 +31,7 @@
#include <linux/mtd/partitions.h>
#include <linux/mmc/host.h>
#include <linux/ioport.h>
+#include <linux/sizes.h>
#include <linux/platform_data/s3c-hsudc.h>
#include <linux/platform_data/s3c-hsotg.h>
#include <linux/platform_data/dma-s3c24xx.h>
@@ -41,7 +43,6 @@
#include <asm/mach/map.h>
#include <asm/mach/irq.h>
-#include <mach/hardware.h>
#include <mach/dma.h>
#include <mach/irqs.h>
#include <mach/map.h>
@@ -64,7 +65,6 @@
#include <linux/platform_data/usb-s3c2410_udc.h>
#include <linux/platform_data/usb-ohci-s3c2410.h>
#include <plat/usb-phy.h>
-#include <plat/regs-serial.h>
#include <plat/regs-spi.h>
#include <linux/platform_data/spi-s3c64xx.h>
@@ -744,10 +744,7 @@ void __init s5p_i2c_hdmiphy_set_platdata(struct s3c2410_platform_i2c *pd)
if (!pd) {
pd = &default_i2c_data;
- if (soc_is_exynos4210() ||
- soc_is_exynos4212() || soc_is_exynos4412())
- pd->bus_num = 8;
- else if (soc_is_s5pv210())
+ if (soc_is_s5pv210())
pd->bus_num = 3;
else
pd->bus_num = 0;
@@ -764,10 +761,7 @@ void __init s5p_hdmi_set_platdata(struct i2c_board_info *hdmiphy_info,
{
struct s5p_hdmi_platform_data *pd = &s5p_hdmi_def_platdata;
- if (soc_is_exynos4210() ||
- soc_is_exynos4212() || soc_is_exynos4412())
- pd->hdmiphy_bus = 8;
- else if (soc_is_s5pv210())
+ if (soc_is_s5pv210())
pd->hdmiphy_bus = 3;
else
pd->hdmiphy_bus = 0;
diff --git a/arch/arm/plat-samsung/include/plat/cpu.h b/arch/arm/plat-samsung/include/plat/cpu.h
index 335beb341355..5992b8dd9b89 100644
--- a/arch/arm/plat-samsung/include/plat/cpu.h
+++ b/arch/arm/plat-samsung/include/plat/cpu.h
@@ -20,6 +20,9 @@
extern unsigned long samsung_cpu_id;
+#define S3C2410_CPU_ID 0x32410000
+#define S3C2410_CPU_MASK 0xFFFFFFFF
+
#define S3C24XX_CPU_ID 0x32400000
#define S3C24XX_CPU_MASK 0xFFF00000
@@ -56,6 +59,7 @@ static inline int is_samsung_##name(void) \
return ((samsung_cpu_id & mask) == (id & mask)); \
}
+IS_SAMSUNG_CPU(s3c2410, S3C2410_CPU_ID, S3C2410_CPU_MASK)
IS_SAMSUNG_CPU(s3c24xx, S3C24XX_CPU_ID, S3C24XX_CPU_MASK)
IS_SAMSUNG_CPU(s3c2412, S3C2412_CPU_ID, S3C2412_CPU_MASK)
IS_SAMSUNG_CPU(s3c6400, S3C6400_CPU_ID, S3C64XX_CPU_MASK)
@@ -76,8 +80,10 @@ IS_SAMSUNG_CPU(exynos5440, EXYNOS5440_SOC_ID, EXYNOS5_SOC_MASK)
defined(CONFIG_CPU_S3C2442) || defined(CONFIG_CPU_S3C244X) || \
defined(CONFIG_CPU_S3C2443)
# define soc_is_s3c24xx() is_samsung_s3c24xx()
+# define soc_is_s3c2410() is_samsung_s3c2410()
#else
# define soc_is_s3c24xx() 0
+# define soc_is_s3c2410() 0
#endif
#if defined(CONFIG_CPU_S3C2412)
@@ -160,6 +166,10 @@ IS_SAMSUNG_CPU(exynos5440, EXYNOS5440_SOC_ID, EXYNOS5_SOC_MASK)
# define soc_is_exynos5440() 0
#endif
+#define soc_is_exynos4() (soc_is_exynos4210() || soc_is_exynos4212() || \
+ soc_is_exynos4412())
+#define soc_is_exynos5() (soc_is_exynos5250() || soc_is_exynos5420())
+
#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, __phys_to_pfn(S3C24XX_PA_##x), S3C24XX_SZ_##x, MT_DEVICE }
#ifndef KHZ
@@ -199,7 +209,6 @@ extern void s5p_init_irq(u32 *vic, u32 num_vic);
extern void s3c24xx_init_io(struct map_desc *mach_desc, int size);
-extern void s3c24xx_init_cpu(void);
extern void s3c64xx_init_cpu(void);
extern void s5p_init_cpu(void __iomem *cpuid_addr);
diff --git a/arch/arm/plat-samsung/include/plat/mfc.h b/arch/arm/plat-samsung/include/plat/mfc.h
index e6d7c42d68b6..033654e91e22 100644
--- a/arch/arm/plat-samsung/include/plat/mfc.h
+++ b/arch/arm/plat-samsung/include/plat/mfc.h
@@ -32,7 +32,4 @@ struct s5p_mfc_dt_meminfo {
void __init s5p_mfc_reserve_mem(phys_addr_t rbase, unsigned int rsize,
phys_addr_t lbase, unsigned int lsize);
-int __init s5p_fdt_find_mfc_mem(unsigned long node, const char *uname,
- int depth, void *data);
-
#endif /* __PLAT_SAMSUNG_MFC_H */
diff --git a/arch/arm/plat-samsung/include/plat/pm-common.h b/arch/arm/plat-samsung/include/plat/pm-common.h
new file mode 100644
index 000000000000..8705f9e0e288
--- /dev/null
+++ b/arch/arm/plat-samsung/include/plat/pm-common.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Tomasz Figa <t.figa@samsung.com>
+ * Copyright (c) 2004 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Written by Ben Dooks, <ben@simtec.co.uk>
+ *
+ * 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 __PLAT_SAMSUNG_PM_COMMON_H
+#define __PLAT_SAMSUNG_PM_COMMON_H __FILE__
+
+#include <linux/irq.h>
+
+/* sleep save info */
+
+/**
+ * struct sleep_save - save information for shared peripherals.
+ * @reg: Pointer to the register to save.
+ * @val: Holder for the value saved from reg.
+ *
+ * This describes a list of registers which is used by the pm core and
+ * other subsystem to save and restore register values over suspend.
+ */
+struct sleep_save {
+ void __iomem *reg;
+ unsigned long val;
+};
+
+#define SAVE_ITEM(x) \
+ { .reg = (x) }
+
+/* helper functions to save/restore lists of registers. */
+
+extern void s3c_pm_do_save(struct sleep_save *ptr, int count);
+extern void s3c_pm_do_restore(const struct sleep_save *ptr, int count);
+extern void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count);
+
+/* PM debug functions */
+
+/**
+ * struct pm_uart_save - save block for core UART
+ * @ulcon: Save value for S3C2410_ULCON
+ * @ucon: Save value for S3C2410_UCON
+ * @ufcon: Save value for S3C2410_UFCON
+ * @umcon: Save value for S3C2410_UMCON
+ * @ubrdiv: Save value for S3C2410_UBRDIV
+ *
+ * Save block for UART registers to be held over sleep and restored if they
+ * are needed (say by debug).
+*/
+struct pm_uart_save {
+ u32 ulcon;
+ u32 ucon;
+ u32 ufcon;
+ u32 umcon;
+ u32 ubrdiv;
+ u32 udivslot;
+};
+
+#ifdef CONFIG_SAMSUNG_PM_DEBUG
+/**
+ * s3c_pm_dbg() - low level debug function for use in suspend/resume.
+ * @msg: The message to print.
+ *
+ * This function is used mainly to debug the resume process before the system
+ * can rely on printk/console output. It uses the low-level debugging output
+ * routine printascii() to do its work.
+ */
+extern void s3c_pm_dbg(const char *msg, ...);
+
+/**
+ * s3c_pm_debug_init() - suspend/resume low level debug initialization.
+ * @base: Virtual base of UART to use for suspend/resume debugging.
+ *
+ * This function needs to be called before S3C_PMDBG() can be used, to set up
+ * UART port base address and configuration.
+ */
+extern void s3c_pm_debug_init(void);
+
+#define S3C_PMDBG(fmt...) s3c_pm_dbg(fmt)
+
+extern void s3c_pm_save_uarts(void);
+extern void s3c_pm_restore_uarts(void);
+#else
+#define S3C_PMDBG(fmt...) pr_debug(fmt)
+#define s3c_pm_debug_init() do { } while (0)
+
+static inline void s3c_pm_save_uarts(void) { }
+static inline void s3c_pm_restore_uarts(void) { }
+#endif
+
+/* suspend memory checking */
+
+#ifdef CONFIG_SAMSUNG_PM_CHECK
+extern void s3c_pm_check_prepare(void);
+extern void s3c_pm_check_restore(void);
+extern void s3c_pm_check_cleanup(void);
+extern void s3c_pm_check_store(void);
+#else
+#define s3c_pm_check_prepare() do { } while (0)
+#define s3c_pm_check_restore() do { } while (0)
+#define s3c_pm_check_cleanup() do { } while (0)
+#define s3c_pm_check_store() do { } while (0)
+#endif
+
+#endif
diff --git a/arch/arm/plat-samsung/include/plat/pm.h b/arch/arm/plat-samsung/include/plat/pm.h
index ff6063f0d5ea..e17d871b934c 100644
--- a/arch/arm/plat-samsung/include/plat/pm.h
+++ b/arch/arm/plat-samsung/include/plat/pm.h
@@ -15,7 +15,7 @@
* management
*/
-#include <linux/irq.h>
+#include <plat/pm-common.h>
struct device;
@@ -54,56 +54,10 @@ extern int (*pm_cpu_sleep)(unsigned long);
extern unsigned long s3c_pm_flags;
-extern unsigned char pm_uart_udivslot; /* true to save UART UDIVSLOT */
-
/* from sleep.S */
extern int s3c2410_cpu_suspend(unsigned long);
-/* sleep save info */
-
-/**
- * struct sleep_save - save information for shared peripherals.
- * @reg: Pointer to the register to save.
- * @val: Holder for the value saved from reg.
- *
- * This describes a list of registers which is used by the pm core and
- * other subsystem to save and restore register values over suspend.
- */
-struct sleep_save {
- void __iomem *reg;
- unsigned long val;
-};
-
-#define SAVE_ITEM(x) \
- { .reg = (x) }
-
-/**
- * struct pm_uart_save - save block for core UART
- * @ulcon: Save value for S3C2410_ULCON
- * @ucon: Save value for S3C2410_UCON
- * @ufcon: Save value for S3C2410_UFCON
- * @umcon: Save value for S3C2410_UMCON
- * @ubrdiv: Save value for S3C2410_UBRDIV
- *
- * Save block for UART registers to be held over sleep and restored if they
- * are needed (say by debug).
-*/
-struct pm_uart_save {
- u32 ulcon;
- u32 ucon;
- u32 ufcon;
- u32 umcon;
- u32 ubrdiv;
- u32 udivslot;
-};
-
-/* helper functions to save/restore lists of registers. */
-
-extern void s3c_pm_do_save(struct sleep_save *ptr, int count);
-extern void s3c_pm_do_restore(const struct sleep_save *ptr, int count);
-extern void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count);
-
#ifdef CONFIG_SAMSUNG_PM
extern int s3c_irq_wake(struct irq_data *data, unsigned int state);
extern int s3c_irqext_wake(struct irq_data *data, unsigned int state);
@@ -114,24 +68,6 @@ extern void s3c_cpu_resume(void);
#define s3c_cpu_resume NULL
#endif
-/* PM debug functions */
-
-#ifdef CONFIG_SAMSUNG_PM_DEBUG
-/**
- * s3c_pm_dbg() - low level debug function for use in suspend/resume.
- * @msg: The message to print.
- *
- * This function is used mainly to debug the resume process before the system
- * can rely on printk/console output. It uses the low-level debugging output
- * routine printascii() to do its work.
- */
-extern void s3c_pm_dbg(const char *msg, ...);
-
-#define S3C_PMDBG(fmt...) s3c_pm_dbg(fmt)
-#else
-#define S3C_PMDBG(fmt...) printk(KERN_DEBUG fmt)
-#endif
-
#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
/**
* s3c_pm_debug_smdkled() - Debug PM suspend/resume via SMDK Board LEDs
@@ -144,20 +80,6 @@ extern void s3c_pm_debug_smdkled(u32 set, u32 clear);
static inline void s3c_pm_debug_smdkled(u32 set, u32 clear) { }
#endif /* CONFIG_S3C_PM_DEBUG_LED_SMDK */
-/* suspend memory checking */
-
-#ifdef CONFIG_SAMSUNG_PM_CHECK
-extern void s3c_pm_check_prepare(void);
-extern void s3c_pm_check_restore(void);
-extern void s3c_pm_check_cleanup(void);
-extern void s3c_pm_check_store(void);
-#else
-#define s3c_pm_check_prepare() do { } while(0)
-#define s3c_pm_check_restore() do { } while(0)
-#define s3c_pm_check_cleanup() do { } while(0)
-#define s3c_pm_check_store() do { } while(0)
-#endif
-
/**
* s3c_pm_configure_extint() - ensure pins are correctly set for IRQ
*
diff --git a/arch/arm/plat-samsung/include/plat/regs-serial.h b/arch/arm/plat-samsung/include/plat/regs-serial.h
deleted file mode 100644
index f05f2afa440d..000000000000
--- a/arch/arm/plat-samsung/include/plat/regs-serial.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <linux/serial_s3c.h>
diff --git a/arch/arm/plat-samsung/include/plat/uncompress.h b/arch/arm/plat-samsung/include/plat/uncompress.h
deleted file mode 100644
index f48dc0a4736c..000000000000
--- a/arch/arm/plat-samsung/include/plat/uncompress.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/* arch/arm/plat-samsung/include/plat/uncompress.h
- *
- * Copyright 2003, 2007 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C - uncompress code
- *
- * 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_PLAT_UNCOMPRESS_H
-#define __ASM_PLAT_UNCOMPRESS_H
-
-typedef unsigned int upf_t; /* cannot include linux/serial_core.h */
-
-/* uart setup */
-
-unsigned int fifo_mask;
-unsigned int fifo_max;
-
-volatile u8 *uart_base;
-
-/* forward declerations */
-
-static void arch_detect_cpu(void);
-
-/* defines for UART registers */
-
-#include <plat/regs-serial.h>
-
-/* working in physical space... */
-#define S3C_WDOGREG(x) ((S3C_PA_WDT + (x)))
-
-#define S3C2410_WTCON S3C_WDOGREG(0x00)
-#define S3C2410_WTDAT S3C_WDOGREG(0x04)
-#define S3C2410_WTCNT S3C_WDOGREG(0x08)
-
-#define S3C2410_WTCON_RSTEN (1 << 0)
-#define S3C2410_WTCON_ENABLE (1 << 5)
-
-#define S3C2410_WTCON_DIV128 (3 << 3)
-
-#define S3C2410_WTCON_PRESCALE(x) ((x) << 8)
-
-/* how many bytes we allow into the FIFO at a time in FIFO mode */
-#define FIFO_MAX (14)
-
-static __inline__ void
-uart_wr(unsigned int reg, unsigned int val)
-{
- volatile unsigned int *ptr;
-
- ptr = (volatile unsigned int *)(reg + uart_base);
- *ptr = val;
-}
-
-static __inline__ unsigned int
-uart_rd(unsigned int reg)
-{
- volatile unsigned int *ptr;
-
- ptr = (volatile unsigned int *)(reg + uart_base);
- return *ptr;
-}
-
-/* we can deal with the case the UARTs are being run
- * in FIFO mode, so that we don't hold up our execution
- * waiting for tx to happen...
-*/
-
-static void putc(int ch)
-{
- if (!config_enabled(CONFIG_DEBUG_LL))
- return;
-
- if (uart_rd(S3C2410_UFCON) & S3C2410_UFCON_FIFOMODE) {
- int level;
-
- while (1) {
- level = uart_rd(S3C2410_UFSTAT);
- level &= fifo_mask;
-
- if (level < fifo_max)
- break;
- }
-
- } else {
- /* not using fifos */
-
- while ((uart_rd(S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE) != S3C2410_UTRSTAT_TXE)
- barrier();
- }
-
- /* write byte to transmission register */
- uart_wr(S3C2410_UTXH, ch);
-}
-
-static inline void flush(void)
-{
-}
-
-#define __raw_writel(d, ad) \
- do { \
- *((volatile unsigned int __force *)(ad)) = (d); \
- } while (0)
-
-#ifdef CONFIG_S3C_BOOT_ERROR_RESET
-
-static void arch_decomp_error(const char *x)
-{
- putstr("\n\n");
- putstr(x);
- putstr("\n\n -- System resetting\n");
-
- __raw_writel(0x4000, S3C2410_WTDAT);
- __raw_writel(0x4000, S3C2410_WTCNT);
- __raw_writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128 | S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x40), S3C2410_WTCON);
-
- while(1);
-}
-
-#define arch_error arch_decomp_error
-#endif
-
-#ifdef CONFIG_S3C_BOOT_UART_FORCE_FIFO
-static inline void arch_enable_uart_fifo(void)
-{
- u32 fifocon;
-
- if (!config_enabled(CONFIG_DEBUG_LL))
- return;
-
- fifocon = uart_rd(S3C2410_UFCON);
-
- if (!(fifocon & S3C2410_UFCON_FIFOMODE)) {
- fifocon |= S3C2410_UFCON_RESETBOTH;
- uart_wr(S3C2410_UFCON, fifocon);
-
- /* wait for fifo reset to complete */
- while (1) {
- fifocon = uart_rd(S3C2410_UFCON);
- if (!(fifocon & S3C2410_UFCON_RESETBOTH))
- break;
- }
-
- uart_wr(S3C2410_UFCON, S3C2410_UFCON_FIFOMODE);
- }
-}
-#else
-#define arch_enable_uart_fifo() do { } while(0)
-#endif
-
-
-static void
-arch_decomp_setup(void)
-{
- /* we may need to setup the uart(s) here if we are not running
- * on an BAST... the BAST will have left the uarts configured
- * after calling linux.
- */
-
- arch_detect_cpu();
-
- /* Enable the UART FIFOs if they where not enabled and our
- * configuration says we should turn them on.
- */
-
- arch_enable_uart_fifo();
-}
-
-
-#endif /* __ASM_PLAT_UNCOMPRESS_H */
diff --git a/arch/arm/plat-samsung/init.c b/arch/arm/plat-samsung/init.c
index aa9511b6914a..a1f925f3121f 100644
--- a/arch/arm/plat-samsung/init.c
+++ b/arch/arm/plat-samsung/init.c
@@ -21,11 +21,10 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/of.h>
-#include <mach/hardware.h>
-
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
@@ -33,8 +32,6 @@
#include <plat/devs.h>
#include <plat/clock.h>
-#include <plat/regs-serial.h>
-
static struct cpu_table *cpu;
static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode,
@@ -97,7 +94,9 @@ void __init s3c24xx_init_clocks(int xtal)
#if IS_ENABLED(CONFIG_SAMSUNG_ATAGS)
static int nr_uarts __initdata = 0;
+#ifdef CONFIG_SERIAL_SAMSUNG_UARTS
static struct s3c2410_uartcfg uart_cfgs[CONFIG_SERIAL_SAMSUNG_UARTS];
+#endif
/* s3c24xx_init_uartdevs
*
@@ -112,6 +111,7 @@ void __init s3c24xx_init_uartdevs(char *name,
struct s3c24xx_uart_resources *res,
struct s3c2410_uartcfg *cfg, int no)
{
+#ifdef CONFIG_SERIAL_SAMSUNG_UARTS
struct platform_device *platdev;
struct s3c2410_uartcfg *cfgptr = uart_cfgs;
struct s3c24xx_uart_resources *resp;
@@ -134,6 +134,7 @@ void __init s3c24xx_init_uartdevs(char *name,
}
nr_uarts = no;
+#endif
}
void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no)
diff --git a/arch/arm/plat-samsung/pm-check.c b/arch/arm/plat-samsung/pm-check.c
index 3cbd62666b1e..04aff2c31b46 100644
--- a/arch/arm/plat-samsung/pm-check.c
+++ b/arch/arm/plat-samsung/pm-check.c
@@ -19,7 +19,7 @@
#include <linux/ioport.h>
#include <linux/slab.h>
-#include <plat/pm.h>
+#include <plat/pm-common.h>
#if CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE < 1
#error CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE must be a positive non-zero value
diff --git a/arch/arm/plat-samsung/pm-common.c b/arch/arm/plat-samsung/pm-common.c
new file mode 100644
index 000000000000..515cd53372bd
--- /dev/null
+++ b/arch/arm/plat-samsung/pm-common.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Tomasz Figa <t.figa@samsung.com>
+ * Copyright (C) 2008 Openmoko, Inc.
+ * Copyright (C) 2004-2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * Samsung common power management helper functions.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+
+#include <plat/pm-common.h>
+
+/* helper functions to save and restore register state */
+
+/**
+ * s3c_pm_do_save() - save a set of registers for restoration on resume.
+ * @ptr: Pointer to an array of registers.
+ * @count: Size of the ptr array.
+ *
+ * Run through the list of registers given, saving their contents in the
+ * array for later restoration when we wakeup.
+ */
+void s3c_pm_do_save(struct sleep_save *ptr, int count)
+{
+ for (; count > 0; count--, ptr++) {
+ ptr->val = __raw_readl(ptr->reg);
+ S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val);
+ }
+}
+
+/**
+ * s3c_pm_do_restore() - restore register values from the save list.
+ * @ptr: Pointer to an array of registers.
+ * @count: Size of the ptr array.
+ *
+ * Restore the register values saved from s3c_pm_do_save().
+ *
+ * Note, we do not use S3C_PMDBG() in here, as the system may not have
+ * restore the UARTs state yet
+*/
+
+void s3c_pm_do_restore(const struct sleep_save *ptr, int count)
+{
+ for (; count > 0; count--, ptr++) {
+ pr_debug("restore %p (restore %08lx, was %08x)\n",
+ ptr->reg, ptr->val, __raw_readl(ptr->reg));
+
+ __raw_writel(ptr->val, ptr->reg);
+ }
+}
+
+/**
+ * s3c_pm_do_restore_core() - early restore register values from save list.
+ *
+ * This is similar to s3c_pm_do_restore() except we try and minimise the
+ * side effects of the function in case registers that hardware might need
+ * to work has been restored.
+ *
+ * WARNING: Do not put any debug in here that may effect memory or use
+ * peripherals, as things may be changing!
+*/
+
+void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count)
+{
+ for (; count > 0; count--, ptr++)
+ __raw_writel(ptr->val, ptr->reg);
+}
diff --git a/arch/arm/plat-samsung/pm-debug.c b/arch/arm/plat-samsung/pm-debug.c
new file mode 100644
index 000000000000..8f19f66388dd
--- /dev/null
+++ b/arch/arm/plat-samsung/pm-debug.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Tomasz Figa <t.figa@samsung.com>
+ * Copyright (C) 2008 Openmoko, Inc.
+ * Copyright (C) 2004-2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * Samsung common power management (suspend to RAM) debug support
+ *
+ * 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/serial_core.h>
+#include <linux/io.h>
+
+#include <asm/mach/map.h>
+
+#include <plat/cpu.h>
+#include <plat/pm-common.h>
+
+#ifdef CONFIG_SAMSUNG_ATAGS
+#include <mach/pm-core.h>
+#else
+static inline void s3c_pm_debug_init_uart(void) {}
+static inline void s3c_pm_arch_update_uart(void __iomem *regs,
+ struct pm_uart_save *save) {}
+#endif
+
+static struct pm_uart_save uart_save;
+
+extern void printascii(const char *);
+
+void s3c_pm_dbg(const char *fmt, ...)
+{
+ va_list va;
+ char buff[256];
+
+ va_start(va, fmt);
+ vsnprintf(buff, sizeof(buff), fmt, va);
+ va_end(va);
+
+ printascii(buff);
+}
+
+void s3c_pm_debug_init(void)
+{
+ /* restart uart clocks so we can use them to output */
+ s3c_pm_debug_init_uart();
+}
+
+static inline void __iomem *s3c_pm_uart_base(void)
+{
+ unsigned long paddr;
+ unsigned long vaddr;
+
+ debug_ll_addr(&paddr, &vaddr);
+
+ return (void __iomem *)vaddr;
+}
+
+void s3c_pm_save_uarts(void)
+{
+ void __iomem *regs = s3c_pm_uart_base();
+ struct pm_uart_save *save = &uart_save;
+
+ save->ulcon = __raw_readl(regs + S3C2410_ULCON);
+ save->ucon = __raw_readl(regs + S3C2410_UCON);
+ save->ufcon = __raw_readl(regs + S3C2410_UFCON);
+ save->umcon = __raw_readl(regs + S3C2410_UMCON);
+ save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV);
+
+ if (!soc_is_s3c2410())
+ save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT);
+
+ S3C_PMDBG("UART[%p]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n",
+ regs, save->ulcon, save->ucon, save->ufcon, save->ubrdiv);
+}
+
+void s3c_pm_restore_uarts(void)
+{
+ void __iomem *regs = s3c_pm_uart_base();
+ struct pm_uart_save *save = &uart_save;
+
+ s3c_pm_arch_update_uart(regs, save);
+
+ __raw_writel(save->ulcon, regs + S3C2410_ULCON);
+ __raw_writel(save->ucon, regs + S3C2410_UCON);
+ __raw_writel(save->ufcon, regs + S3C2410_UFCON);
+ __raw_writel(save->umcon, regs + S3C2410_UMCON);
+ __raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV);
+
+ if (!soc_is_s3c2410())
+ __raw_writel(save->udivslot, regs + S3C2443_DIVSLOT);
+}
diff --git a/arch/arm/plat-samsung/pm-gpio.c b/arch/arm/plat-samsung/pm-gpio.c
index dd4c15d0d68f..da268813901b 100644
--- a/arch/arm/plat-samsung/pm-gpio.c
+++ b/arch/arm/plat-samsung/pm-gpio.c
@@ -196,8 +196,7 @@ struct samsung_gpio_pm samsung_gpio_pm_2bit = {
.resume = samsung_gpio_pm_2bit_resume,
};
-#if defined(CONFIG_ARCH_S3C64XX) || defined(CONFIG_PLAT_S5P) \
- || defined(CONFIG_ARCH_EXYNOS)
+#if defined(CONFIG_ARCH_S3C64XX) || defined(CONFIG_PLAT_S5P)
static void samsung_gpio_pm_4bit_save(struct samsung_gpio_chip *chip)
{
chip->pm_save[1] = __raw_readl(chip->base + OFFS_CON);
@@ -307,7 +306,7 @@ struct samsung_gpio_pm samsung_gpio_pm_4bit = {
.save = samsung_gpio_pm_4bit_save,
.resume = samsung_gpio_pm_4bit_resume,
};
-#endif /* CONFIG_ARCH_S3C64XX || CONFIG_PLAT_S5P || CONFIG_ARCH_EXYNOS */
+#endif /* CONFIG_ARCH_S3C64XX || CONFIG_PLAT_S5P */
/**
* samsung_pm_save_gpio() - save gpio chip data for suspend
diff --git a/arch/arm/plat-samsung/pm.c b/arch/arm/plat-samsung/pm.c
index e5b0f2c2d884..f8c0f9797dcf 100644
--- a/arch/arm/plat-samsung/pm.c
+++ b/arch/arm/plat-samsung/pm.c
@@ -17,16 +17,13 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/of.h>
-#include <linux/serial_core.h>
+#include <linux/serial_s3c.h>
#include <linux/io.h>
#include <asm/cacheflush.h>
#include <asm/suspend.h>
-#include <plat/regs-serial.h>
-
#ifdef CONFIG_SAMSUNG_ATAGS
-#include <mach/hardware.h>
#include <mach/map.h>
#ifndef CONFIG_ARCH_EXYNOS
#include <mach/regs-clock.h>
@@ -44,93 +41,6 @@
unsigned long s3c_pm_flags;
-/* Debug code:
- *
- * This code supports debug output to the low level UARTs for use on
- * resume before the console layer is available.
-*/
-
-#ifdef CONFIG_SAMSUNG_PM_DEBUG
-extern void printascii(const char *);
-
-void s3c_pm_dbg(const char *fmt, ...)
-{
- va_list va;
- char buff[256];
-
- va_start(va, fmt);
- vsnprintf(buff, sizeof(buff), fmt, va);
- va_end(va);
-
- printascii(buff);
-}
-
-static inline void s3c_pm_debug_init(void)
-{
- /* restart uart clocks so we can use them to output */
- s3c_pm_debug_init_uart();
-}
-
-#else
-#define s3c_pm_debug_init() do { } while(0)
-
-#endif /* CONFIG_SAMSUNG_PM_DEBUG */
-
-/* Save the UART configurations if we are configured for debug. */
-
-unsigned char pm_uart_udivslot;
-
-#ifdef CONFIG_SAMSUNG_PM_DEBUG
-
-static struct pm_uart_save uart_save;
-
-static void s3c_pm_save_uart(unsigned int uart, struct pm_uart_save *save)
-{
- void __iomem *regs = S3C_VA_UARTx(uart);
-
- save->ulcon = __raw_readl(regs + S3C2410_ULCON);
- save->ucon = __raw_readl(regs + S3C2410_UCON);
- save->ufcon = __raw_readl(regs + S3C2410_UFCON);
- save->umcon = __raw_readl(regs + S3C2410_UMCON);
- save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV);
-
- if (pm_uart_udivslot)
- save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT);
-
- S3C_PMDBG("UART[%d]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n",
- uart, save->ulcon, save->ucon, save->ufcon, save->ubrdiv);
-}
-
-static void s3c_pm_save_uarts(void)
-{
- s3c_pm_save_uart(CONFIG_DEBUG_S3C_UART, &uart_save);
-}
-
-static void s3c_pm_restore_uart(unsigned int uart, struct pm_uart_save *save)
-{
- void __iomem *regs = S3C_VA_UARTx(uart);
-
- s3c_pm_arch_update_uart(regs, save);
-
- __raw_writel(save->ulcon, regs + S3C2410_ULCON);
- __raw_writel(save->ucon, regs + S3C2410_UCON);
- __raw_writel(save->ufcon, regs + S3C2410_UFCON);
- __raw_writel(save->umcon, regs + S3C2410_UMCON);
- __raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV);
-
- if (pm_uart_udivslot)
- __raw_writel(save->udivslot, regs + S3C2443_DIVSLOT);
-}
-
-static void s3c_pm_restore_uarts(void)
-{
- s3c_pm_restore_uart(CONFIG_DEBUG_S3C_UART, &uart_save);
-}
-#else
-static void s3c_pm_save_uarts(void) { }
-static void s3c_pm_restore_uarts(void) { }
-#endif
-
/* The IRQ ext-int code goes here, it is too small to currently bother
* with its own file. */
@@ -155,62 +65,6 @@ int s3c_irqext_wake(struct irq_data *data, unsigned int state)
return 0;
}
-/* helper functions to save and restore register state */
-
-/**
- * s3c_pm_do_save() - save a set of registers for restoration on resume.
- * @ptr: Pointer to an array of registers.
- * @count: Size of the ptr array.
- *
- * Run through the list of registers given, saving their contents in the
- * array for later restoration when we wakeup.
- */
-void s3c_pm_do_save(struct sleep_save *ptr, int count)
-{
- for (; count > 0; count--, ptr++) {
- ptr->val = __raw_readl(ptr->reg);
- S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val);
- }
-}
-
-/**
- * s3c_pm_do_restore() - restore register values from the save list.
- * @ptr: Pointer to an array of registers.
- * @count: Size of the ptr array.
- *
- * Restore the register values saved from s3c_pm_do_save().
- *
- * Note, we do not use S3C_PMDBG() in here, as the system may not have
- * restore the UARTs state yet
-*/
-
-void s3c_pm_do_restore(const struct sleep_save *ptr, int count)
-{
- for (; count > 0; count--, ptr++) {
- printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n",
- ptr->reg, ptr->val, __raw_readl(ptr->reg));
-
- __raw_writel(ptr->val, ptr->reg);
- }
-}
-
-/**
- * s3c_pm_do_restore_core() - early restore register values from save list.
- *
- * This is similar to s3c_pm_do_restore() except we try and minimise the
- * side effects of the function in case registers that hardware might need
- * to work has been restored.
- *
- * WARNING: Do not put any debug in here that may effect memory or use
- * peripherals, as things may be changing!
-*/
-
-void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count)
-{
- for (; count > 0; count--, ptr++)
- __raw_writel(ptr->val, ptr->reg);
-}
-
/* s3c2410_pm_show_resume_irqs
*
* print any IRQs asserted at resume time (ie, we woke from)
diff --git a/arch/arm/plat-samsung/s5p-dev-mfc.c b/arch/arm/plat-samsung/s5p-dev-mfc.c
index ad51f85fbd01..98087b655df0 100644
--- a/arch/arm/plat-samsung/s5p-dev-mfc.c
+++ b/arch/arm/plat-samsung/s5p-dev-mfc.c
@@ -122,32 +122,35 @@ device_initcall(s5p_mfc_memory_init);
#endif
#ifdef CONFIG_OF
-int __init s5p_fdt_find_mfc_mem(unsigned long node, const char *uname,
+int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname,
int depth, void *data)
{
__be32 *prop;
unsigned long len;
- struct s5p_mfc_dt_meminfo *mfc_mem = data;
+ struct s5p_mfc_dt_meminfo mfc_mem;
if (!data)
return 0;
- if (!of_flat_dt_is_compatible(node, mfc_mem->compatible))
+ if (!of_flat_dt_is_compatible(node, data))
return 0;
prop = of_get_flat_dt_prop(node, "samsung,mfc-l", &len);
if (!prop || (len != 2 * sizeof(unsigned long)))
return 0;
- mfc_mem->loff = be32_to_cpu(prop[0]);
- mfc_mem->lsize = be32_to_cpu(prop[1]);
+ mfc_mem.loff = be32_to_cpu(prop[0]);
+ mfc_mem.lsize = be32_to_cpu(prop[1]);
prop = of_get_flat_dt_prop(node, "samsung,mfc-r", &len);
if (!prop || (len != 2 * sizeof(unsigned long)))
return 0;
- mfc_mem->roff = be32_to_cpu(prop[0]);
- mfc_mem->rsize = be32_to_cpu(prop[1]);
+ mfc_mem.roff = be32_to_cpu(prop[0]);
+ mfc_mem.rsize = be32_to_cpu(prop[1]);
+
+ s5p_mfc_reserve_mem(mfc_mem.roff, mfc_mem.rsize,
+ mfc_mem.loff, mfc_mem.lsize);
return 1;
}
diff --git a/arch/arm/plat-samsung/s5p-dev-uart.c b/arch/arm/plat-samsung/s5p-dev-uart.c
index cafa3deddcc1..8c4487af98c8 100644
--- a/arch/arm/plat-samsung/s5p-dev-uart.c
+++ b/arch/arm/plat-samsung/s5p-dev-uart.c
@@ -18,7 +18,6 @@
#include <asm/mach/arch.h>
#include <asm/mach/irq.h>
-#include <mach/hardware.h>
#include <mach/map.h>
#include <plat/devs.h>
diff --git a/arch/arm/plat-samsung/s5p-irq-pm.c b/arch/arm/plat-samsung/s5p-irq-pm.c
index 591498035916..52b16943617e 100644
--- a/arch/arm/plat-samsung/s5p-irq-pm.c
+++ b/arch/arm/plat-samsung/s5p-irq-pm.c
@@ -22,10 +22,7 @@
#include <mach/map.h>
#include <mach/regs-gpio.h>
-
-#ifndef CONFIG_ARCH_EXYNOS
#include <mach/regs-irq.h>
-#endif
/* state for IRQs over sleep */
@@ -43,18 +40,8 @@ int s3c_irq_wake(struct irq_data *data, unsigned int state)
unsigned long irqbit;
unsigned int irq_rtc_tic, irq_rtc_alarm;
-#ifdef CONFIG_ARCH_EXYNOS
- if (soc_is_exynos5250()) {
- irq_rtc_tic = EXYNOS5_IRQ_RTC_TIC;
- irq_rtc_alarm = EXYNOS5_IRQ_RTC_ALARM;
- } else {
- irq_rtc_tic = EXYNOS4_IRQ_RTC_TIC;
- irq_rtc_alarm = EXYNOS4_IRQ_RTC_ALARM;
- }
-#else
irq_rtc_tic = IRQ_RTC_TIC;
irq_rtc_alarm = IRQ_RTC_ALARM;
-#endif
if (data->irq == irq_rtc_tic || data->irq == irq_rtc_alarm) {
irqbit = 1 << (data->irq + 1 - irq_rtc_alarm);
diff --git a/arch/arm/plat-samsung/s5p-sleep.S b/arch/arm/plat-samsung/s5p-sleep.S
index a030e7301da8..c5001659bdf8 100644
--- a/arch/arm/plat-samsung/s5p-sleep.S
+++ b/arch/arm/plat-samsung/s5p-sleep.S
@@ -23,18 +23,7 @@
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
-#include <asm/hardware/cache-l2x0.h>
-#define CPU_MASK 0xff0ffff0
-#define CPU_CORTEX_A9 0x410fc090
-
-/*
- * The following code is located into the .data section. This is to
- * allow l2x0_regs_phys to be accessed with a relative load while we
- * can't rely on any MMU translation. We could have put l2x0_regs_phys
- * in the .text section as well, but some setups might insist on it to
- * be truly read-only. (Reference from: arch/arm/kernel/sleep.S)
- */
.data
.align
@@ -53,37 +42,5 @@
*/
ENTRY(s3c_cpu_resume)
-#ifdef CONFIG_CACHE_L2X0
- mrc p15, 0, r0, c0, c0, 0
- ldr r1, =CPU_MASK
- and r0, r0, r1
- ldr r1, =CPU_CORTEX_A9
- cmp r0, r1
- bne resume_l2on
- adr r0, l2x0_regs_phys
- ldr r0, [r0]
- ldr r1, [r0, #L2X0_R_PHY_BASE]
- ldr r2, [r1, #L2X0_CTRL]
- tst r2, #0x1
- bne resume_l2on
- ldr r2, [r0, #L2X0_R_AUX_CTRL]
- str r2, [r1, #L2X0_AUX_CTRL]
- ldr r2, [r0, #L2X0_R_TAG_LATENCY]
- str r2, [r1, #L2X0_TAG_LATENCY_CTRL]
- ldr r2, [r0, #L2X0_R_DATA_LATENCY]
- str r2, [r1, #L2X0_DATA_LATENCY_CTRL]
- ldr r2, [r0, #L2X0_R_PREFETCH_CTRL]
- str r2, [r1, #L2X0_PREFETCH_CTRL]
- ldr r2, [r0, #L2X0_R_PWR_CTRL]
- str r2, [r1, #L2X0_POWER_CTRL]
- mov r2, #1
- str r2, [r1, #L2X0_CTRL]
-resume_l2on:
-#endif
b cpu_resume
ENDPROC(s3c_cpu_resume)
-#ifdef CONFIG_CACHE_L2X0
- .globl l2x0_regs_phys
-l2x0_regs_phys:
- .long 0
-#endif
diff --git a/arch/arm/xen/p2m.c b/arch/arm/xen/p2m.c
index b31ee1b275b0..97baf4427817 100644
--- a/arch/arm/xen/p2m.c
+++ b/arch/arm/xen/p2m.c
@@ -146,6 +146,38 @@ unsigned long __mfn_to_pfn(unsigned long mfn)
}
EXPORT_SYMBOL_GPL(__mfn_to_pfn);
+int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
+ struct gnttab_map_grant_ref *kmap_ops,
+ struct page **pages, unsigned int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (map_ops[i].status)
+ continue;
+ set_phys_to_machine(map_ops[i].host_addr >> PAGE_SHIFT,
+ map_ops[i].dev_bus_addr >> PAGE_SHIFT);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(set_foreign_p2m_mapping);
+
+int clear_foreign_p2m_mapping(struct gnttab_unmap_grant_ref *unmap_ops,
+ struct gnttab_map_grant_ref *kmap_ops,
+ struct page **pages, unsigned int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ set_phys_to_machine(unmap_ops[i].host_addr >> PAGE_SHIFT,
+ INVALID_P2M_ENTRY);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clear_foreign_p2m_mapping);
+
bool __set_phys_to_machine_multi(unsigned long pfn,
unsigned long mfn, unsigned long nr_pages)
{
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 8033b9b8a2df..9711a5fd948d 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -47,6 +47,7 @@ config ARM64
select NO_BOOTMEM
select OF
select OF_EARLY_FLATTREE
+ select OF_RESERVED_MEM
select PERF_USE_VMALLOC
select POWER_RESET
select POWER_SUPPLY
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 21ef48d32ff2..3d6903006a8a 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -62,6 +62,7 @@
* RW: 64bit by default, can be overriden for 32bit VMs
* TAC: Trap ACTLR
* TSC: Trap SMC
+ * TVM: Trap VM ops (until M+C set in SCTLR_EL1)
* TSW: Trap cache operations by set/way
* TWE: Trap WFE
* TWI: Trap WFI
@@ -74,7 +75,7 @@
* SWIO: Turn set/way invalidates into set/way clean+invalidate
*/
#define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWE | HCR_TWI | HCR_VM | \
- HCR_BSU_IS | HCR_FB | HCR_TAC | \
+ HCR_TVM | HCR_BSU_IS | HCR_FB | HCR_TAC | \
HCR_AMO | HCR_IMO | HCR_FMO | \
HCR_SWIO | HCR_TIDCP | HCR_RW)
#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index b25763bc0ec4..9fcd54b1e16d 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -79,7 +79,8 @@
#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_AMAIR (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
+#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 NR_CP15_REGS (NR_SYS_REGS * 2)
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 7f1f9408ff66..7d29847a893b 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -106,7 +106,6 @@ static inline bool kvm_is_write_fault(unsigned long esr)
return true;
}
-static inline void kvm_clean_dcache_area(void *addr, size_t size) {}
static inline void kvm_clean_pgd(pgd_t *pgd) {}
static inline void kvm_clean_pmd_entry(pmd_t *pmd) {}
static inline void kvm_clean_pte(pte_t *pte) {}
@@ -122,11 +121,25 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
pmd_val(*pmd) |= PMD_S2_RDWR;
}
+#define kvm_pgd_addr_end(addr, end) pgd_addr_end(addr, end)
+#define kvm_pud_addr_end(addr, end) pud_addr_end(addr, end)
+#define kvm_pmd_addr_end(addr, end) pmd_addr_end(addr, end)
+
struct kvm;
-static inline void coherent_icache_guest_page(struct kvm *kvm, hva_t hva,
- unsigned long size)
+#define kvm_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l))
+
+static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu)
{
+ return (vcpu_sys_reg(vcpu, SCTLR_EL1) & 0b101) == 0b101;
+}
+
+static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
+ unsigned long size)
+{
+ if (!vcpu_has_cache_enabled(vcpu))
+ kvm_flush_dcache_to_poc((void *)hva, size);
+
if (!icache_is_aliasing()) { /* PIPT */
flush_icache_range(hva, hva + size);
} else if (!icache_is_aivivt()) { /* non ASID-tagged VIVT */
@@ -135,8 +148,9 @@ static inline void coherent_icache_guest_page(struct kvm *kvm, hva_t hva,
}
}
-#define kvm_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l))
#define kvm_virt_to_phys(x) __virt_to_phys((unsigned long)(x))
+void stage2_flush_vm(struct kvm *kvm);
+
#endif /* __ASSEMBLY__ */
#endif /* __ARM64_KVM_MMU_H__ */
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
index 14ba23c61153..ed3955a95747 100644
--- a/arch/arm64/kernel/debug-monitors.c
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -154,13 +154,17 @@ static struct notifier_block os_lock_nb = {
static int debug_monitors_init(void)
{
+ cpu_notifier_register_begin();
+
/* Clear the OS lock. */
on_each_cpu(clear_os_lock, NULL, 1);
isb();
local_dbg_enable();
/* Register hotplug handler. */
- register_cpu_notifier(&os_lock_nb);
+ __register_cpu_notifier(&os_lock_nb);
+
+ cpu_notifier_register_done();
return 0;
}
postcore_initcall(debug_monitors_init);
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index f17f581116fc..bee789757806 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -913,6 +913,8 @@ static int __init arch_hw_breakpoint_init(void)
pr_info("found %d breakpoint and %d watchpoint registers.\n",
core_num_brps, core_num_wrps);
+ cpu_notifier_register_begin();
+
/*
* Reset the breakpoint resources. We assume that a halting
* debugger will leave the world in a nice state for us.
@@ -927,7 +929,10 @@ static int __init arch_hw_breakpoint_init(void)
TRAP_HWBKPT, "hw-watchpoint handler");
/* Register hotplug notifier. */
- register_cpu_notifier(&hw_breakpoint_reset_nb);
+ __register_cpu_notifier(&hw_breakpoint_reset_nb);
+
+ cpu_notifier_register_done();
+
/* Register cpu_suspend hw breakpoint restore hook */
cpu_suspend_set_dbg_restorer(hw_breakpoint_reset);
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 02e9d09e1d80..03244582bc55 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -27,6 +27,7 @@
#include <asm/kvm_host.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_coproc.h>
+#include <asm/kvm_mmu.h>
#include <asm/cacheflush.h>
#include <asm/cputype.h>
#include <trace/events/kvm.h>
@@ -121,6 +122,48 @@ done:
}
/*
+ * Generic accessor for VM registers. Only called as long as HCR_TVM
+ * is set.
+ */
+static bool access_vm_reg(struct kvm_vcpu *vcpu,
+ const struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ unsigned long val;
+
+ BUG_ON(!p->is_write);
+
+ val = *vcpu_reg(vcpu, p->Rt);
+ if (!p->is_aarch32) {
+ vcpu_sys_reg(vcpu, r->reg) = val;
+ } else {
+ vcpu_cp15(vcpu, r->reg) = val & 0xffffffffUL;
+ if (!p->is_32bit)
+ vcpu_cp15(vcpu, r->reg + 1) = val >> 32;
+ }
+ return true;
+}
+
+/*
+ * SCTLR_EL1 accessor. Only called as long as HCR_TVM is set. If the
+ * guest enables the MMU, we stop trapping the VM sys_regs and leave
+ * it in complete control of the caches.
+ */
+static bool access_sctlr(struct kvm_vcpu *vcpu,
+ const struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ access_vm_reg(vcpu, p, r);
+
+ if (vcpu_has_cache_enabled(vcpu)) { /* MMU+Caches enabled? */
+ vcpu->arch.hcr_el2 &= ~HCR_TVM;
+ stage2_flush_vm(vcpu->kvm);
+ }
+
+ return true;
+}
+
+/*
* We could trap ID_DFR0 and tell the guest we don't support performance
* monitoring. Unfortunately the patch to make the kernel check ID_DFR0 was
* NAKed, so it will read the PMCR anyway.
@@ -185,32 +228,32 @@ static const struct sys_reg_desc sys_reg_descs[] = {
NULL, reset_mpidr, MPIDR_EL1 },
/* SCTLR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b000),
- NULL, reset_val, SCTLR_EL1, 0x00C50078 },
+ access_sctlr, reset_val, SCTLR_EL1, 0x00C50078 },
/* CPACR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b010),
NULL, reset_val, CPACR_EL1, 0 },
/* TTBR0_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b000),
- NULL, reset_unknown, TTBR0_EL1 },
+ access_vm_reg, reset_unknown, TTBR0_EL1 },
/* TTBR1_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b001),
- NULL, reset_unknown, TTBR1_EL1 },
+ access_vm_reg, reset_unknown, TTBR1_EL1 },
/* TCR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b010),
- NULL, reset_val, TCR_EL1, 0 },
+ access_vm_reg, reset_val, TCR_EL1, 0 },
/* AFSR0_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0001), Op2(0b000),
- NULL, reset_unknown, AFSR0_EL1 },
+ access_vm_reg, reset_unknown, AFSR0_EL1 },
/* AFSR1_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0001), Op2(0b001),
- NULL, reset_unknown, AFSR1_EL1 },
+ access_vm_reg, reset_unknown, AFSR1_EL1 },
/* ESR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0010), Op2(0b000),
- NULL, reset_unknown, ESR_EL1 },
+ access_vm_reg, reset_unknown, ESR_EL1 },
/* FAR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0110), CRm(0b0000), Op2(0b000),
- NULL, reset_unknown, FAR_EL1 },
+ access_vm_reg, reset_unknown, FAR_EL1 },
/* PAR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0111), CRm(0b0100), Op2(0b000),
NULL, reset_unknown, PAR_EL1 },
@@ -224,17 +267,17 @@ static const struct sys_reg_desc sys_reg_descs[] = {
/* MAIR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0010), Op2(0b000),
- NULL, reset_unknown, MAIR_EL1 },
+ access_vm_reg, reset_unknown, MAIR_EL1 },
/* AMAIR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0011), Op2(0b000),
- NULL, reset_amair_el1, AMAIR_EL1 },
+ access_vm_reg, reset_amair_el1, AMAIR_EL1 },
/* VBAR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1100), CRm(0b0000), Op2(0b000),
NULL, reset_val, VBAR_EL1, 0 },
/* CONTEXTIDR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b001),
- NULL, reset_val, CONTEXTIDR_EL1, 0 },
+ access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 },
/* TPIDR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b100),
NULL, reset_unknown, TPIDR_EL1 },
@@ -305,14 +348,32 @@ static const struct sys_reg_desc sys_reg_descs[] = {
NULL, reset_val, FPEXC32_EL2, 0x70 },
};
-/* Trapped cp15 registers */
+/*
+ * Trapped cp15 registers. TTBR0/TTBR1 get a double encoding,
+ * depending on the way they are accessed (as a 32bit or a 64bit
+ * register).
+ */
static const struct sys_reg_desc cp15_regs[] = {
+ { Op1( 0), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
+ { Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_sctlr, NULL, c1_SCTLR },
+ { Op1( 0), CRn( 2), CRm( 0), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
+ { Op1( 0), CRn( 2), CRm( 0), Op2( 1), access_vm_reg, NULL, c2_TTBR1 },
+ { Op1( 0), CRn( 2), CRm( 0), Op2( 2), access_vm_reg, NULL, c2_TTBCR },
+ { Op1( 0), CRn( 3), CRm( 0), Op2( 0), access_vm_reg, NULL, c3_DACR },
+ { Op1( 0), CRn( 5), CRm( 0), Op2( 0), access_vm_reg, NULL, c5_DFSR },
+ { Op1( 0), CRn( 5), CRm( 0), Op2( 1), access_vm_reg, NULL, c5_IFSR },
+ { Op1( 0), CRn( 5), CRm( 1), Op2( 0), access_vm_reg, NULL, c5_ADFSR },
+ { Op1( 0), CRn( 5), CRm( 1), Op2( 1), access_vm_reg, NULL, c5_AIFSR },
+ { Op1( 0), CRn( 6), CRm( 0), Op2( 0), access_vm_reg, NULL, c6_DFAR },
+ { Op1( 0), CRn( 6), CRm( 0), Op2( 2), access_vm_reg, NULL, c6_IFAR },
+
/*
* DC{C,I,CI}SW operations:
*/
{ Op1( 0), CRn( 7), CRm( 6), Op2( 2), access_dcsw },
{ Op1( 0), CRn( 7), CRm(10), Op2( 2), access_dcsw },
{ Op1( 0), CRn( 7), CRm(14), Op2( 2), access_dcsw },
+
{ Op1( 0), CRn( 9), CRm(12), Op2( 0), pm_fake },
{ Op1( 0), CRn( 9), CRm(12), Op2( 1), pm_fake },
{ Op1( 0), CRn( 9), CRm(12), Op2( 2), pm_fake },
@@ -326,6 +387,14 @@ static const struct sys_reg_desc cp15_regs[] = {
{ Op1( 0), CRn( 9), CRm(14), Op2( 0), pm_fake },
{ Op1( 0), CRn( 9), CRm(14), Op2( 1), pm_fake },
{ Op1( 0), CRn( 9), CRm(14), Op2( 2), pm_fake },
+
+ { Op1( 0), CRn(10), CRm( 2), Op2( 0), access_vm_reg, NULL, c10_PRRR },
+ { Op1( 0), CRn(10), CRm( 2), Op2( 1), access_vm_reg, NULL, c10_NMRR },
+ { Op1( 0), CRn(10), CRm( 3), Op2( 0), access_vm_reg, NULL, c10_AMAIR0 },
+ { Op1( 0), CRn(10), CRm( 3), Op2( 1), access_vm_reg, NULL, c10_AMAIR1 },
+ { Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID },
+
+ { Op1( 1), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR1 },
};
/* Target specific emulation tables */
@@ -437,6 +506,8 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
u32 hsr = kvm_vcpu_get_hsr(vcpu);
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);
@@ -480,6 +551,8 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
struct sys_reg_params params;
u32 hsr = kvm_vcpu_get_hsr(vcpu);
+ params.is_aarch32 = true;
+ params.is_32bit = true;
params.CRm = (hsr >> 1) & 0xf;
params.Rt = (hsr >> 5) & 0xf;
params.is_write = ((hsr & 1) == 0);
@@ -549,6 +622,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);
+ params.is_aarch32 = false;
+ params.is_32bit = false;
params.Op0 = (esr >> 20) & 3;
params.Op1 = (esr >> 14) & 0x7;
params.CRn = (esr >> 10) & 0xf;
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index d50d3722998e..d411e251412c 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -30,6 +30,8 @@ struct sys_reg_params {
u8 Op2;
u8 Rt;
bool is_write;
+ bool is_aarch32;
+ bool is_32bit; /* Only valid if is_aarch32 is true */
};
struct sys_reg_desc {
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 88627c450a6c..51d5352e6ad5 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -161,6 +161,7 @@ void __init arm64_memblock_init(void)
memblock_reserve(base, size);
}
+ early_init_fdt_scan_reserved_mem();
dma_contiguous_reserve(0);
memblock_allow_resize();
diff --git a/arch/blackfin/kernel/ftrace.c b/arch/blackfin/kernel/ftrace.c
index 9277905b82cf..095de0fa044d 100644
--- a/arch/blackfin/kernel/ftrace.c
+++ b/arch/blackfin/kernel/ftrace.c
@@ -65,11 +65,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return ftrace_modify_code(ip, call, sizeof(call));
}
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- /* return value is done indirectly via data */
- *(unsigned long *)data = 0;
-
return 0;
}
diff --git a/arch/cris/arch-v32/drivers/Kconfig b/arch/cris/arch-v32/drivers/Kconfig
index 6792503aaf79..15a9ed1d579c 100644
--- a/arch/cris/arch-v32/drivers/Kconfig
+++ b/arch/cris/arch-v32/drivers/Kconfig
@@ -11,7 +11,6 @@ config ETRAX_ETHERNET
config ETRAX_NO_PHY
bool "PHY not present"
depends on ETRAX_ETHERNET
- default N
help
This option disables all MDIO communication with an ethernet
transceiver connected to the MII interface. This option shall
@@ -116,7 +115,6 @@ config ETRAX_AXISFLASHMAP
config ETRAX_AXISFLASHMAP_MTD0WHOLE
bool "MTD0 is whole boot flash device"
depends on ETRAX_AXISFLASHMAP
- default N
help
When this option is not set, mtd0 refers to the first partition
on the boot flash device. When set, mtd0 refers to the whole
diff --git a/arch/hexagon/Kconfig b/arch/hexagon/Kconfig
index 09df2608f40a..fbc5c78c9ac7 100644
--- a/arch/hexagon/Kconfig
+++ b/arch/hexagon/Kconfig
@@ -28,6 +28,7 @@ config HEXAGON
select GENERIC_CLOCKEVENTS_BROADCAST
select MODULES_USE_ELF_RELA
select GENERIC_CPU_DEVICES
+ select HAVE_DMA_ATTRS
---help---
Qualcomm Hexagon is a processor architecture designed for high
performance and low power across a wide variety of applications.
diff --git a/arch/hexagon/include/asm/Kbuild b/arch/hexagon/include/asm/Kbuild
index eadcc118f950..0e69796b58c7 100644
--- a/arch/hexagon/include/asm/Kbuild
+++ b/arch/hexagon/include/asm/Kbuild
@@ -41,6 +41,7 @@ generic-y += scatterlist.h
generic-y += sections.h
generic-y += segment.h
generic-y += sembuf.h
+generic-y += serial.h
generic-y += shmbuf.h
generic-y += shmparam.h
generic-y += siginfo.h
@@ -56,4 +57,5 @@ generic-y += trace_clock.h
generic-y += types.h
generic-y += ucontext.h
generic-y += unaligned.h
+generic-y += vga.h
generic-y += xor.h
diff --git a/arch/hexagon/include/asm/atomic.h b/arch/hexagon/include/asm/atomic.h
index 7aae4cb2a29a..17dc63780c06 100644
--- a/arch/hexagon/include/asm/atomic.h
+++ b/arch/hexagon/include/asm/atomic.h
@@ -26,7 +26,20 @@
#include <asm/cmpxchg.h>
#define ATOMIC_INIT(i) { (i) }
-#define atomic_set(v, i) ((v)->counter = (i))
+
+/* Normal writes in our arch don't clear lock reservations */
+
+static inline void atomic_set(atomic_t *v, int new)
+{
+ asm volatile(
+ "1: r6 = memw_locked(%0);\n"
+ " memw_locked(%0,p0) = %1;\n"
+ " if (!P0) jump 1b;\n"
+ :
+ : "r" (&v->counter), "r" (new)
+ : "memory", "p0", "r6"
+ );
+}
/**
* atomic_read - reads a word, atomically
diff --git a/arch/hexagon/include/asm/delay.h b/arch/hexagon/include/asm/delay.h
index 53079719d667..8933b9b1a3bf 100644
--- a/arch/hexagon/include/asm/delay.h
+++ b/arch/hexagon/include/asm/delay.h
@@ -21,6 +21,7 @@
#include <asm/param.h>
+extern void __delay(unsigned long cycles);
extern void __udelay(unsigned long usecs);
#define udelay(usecs) __udelay((usecs))
diff --git a/arch/hexagon/include/asm/dma-mapping.h b/arch/hexagon/include/asm/dma-mapping.h
index 85e9935660cb..16965427f6b4 100644
--- a/arch/hexagon/include/asm/dma-mapping.h
+++ b/arch/hexagon/include/asm/dma-mapping.h
@@ -25,7 +25,6 @@
#include <linux/cache.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
-#include <linux/dma-mapping.h>
#include <linux/dma-debug.h>
#include <linux/dma-attrs.h>
#include <asm/io.h>
diff --git a/arch/hexagon/include/asm/elf.h b/arch/hexagon/include/asm/elf.h
index e1b933a0e121..80311e7b8ca6 100644
--- a/arch/hexagon/include/asm/elf.h
+++ b/arch/hexagon/include/asm/elf.h
@@ -1,7 +1,7 @@
/*
* ELF definitions for the Hexagon architecture
*
- * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2013, 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
@@ -202,7 +202,7 @@ do { \
#define CORE_DUMP_USE_REGSET
/* Hrm is this going to cause problems for changing PAGE_SIZE? */
-#define ELF_EXEC_PAGESIZE 4096
+#define ELF_EXEC_PAGESIZE PAGE_SIZE
/*
* This is the location that an ET_DYN program is loaded if exec'ed. Typical
diff --git a/arch/hexagon/include/asm/hexagon_vm.h b/arch/hexagon/include/asm/hexagon_vm.h
index 67bb6d6f3337..1f6918b428de 100644
--- a/arch/hexagon/include/asm/hexagon_vm.h
+++ b/arch/hexagon/include/asm/hexagon_vm.h
@@ -55,27 +55,27 @@
#ifndef __ASSEMBLY__
enum VM_CACHE_OPS {
- ickill,
- dckill,
- l2kill,
- dccleaninva,
- icinva,
- idsync,
- fetch_cfg
+ hvmc_ickill,
+ hvmc_dckill,
+ hvmc_l2kill,
+ hvmc_dccleaninva,
+ hvmc_icinva,
+ hvmc_idsync,
+ hvmc_fetch_cfg
};
enum VM_INT_OPS {
- nop,
- globen,
- globdis,
- locen,
- locdis,
- affinity,
- get,
- peek,
- status,
- post,
- clear
+ hvmi_nop,
+ hvmi_globen,
+ hvmi_globdis,
+ hvmi_locen,
+ hvmi_locdis,
+ hvmi_affinity,
+ hvmi_get,
+ hvmi_peek,
+ hvmi_status,
+ hvmi_post,
+ hvmi_clear
};
extern void _K_VM_event_vector(void);
@@ -98,95 +98,95 @@ long __vmvpid(void);
static inline long __vmcache_ickill(void)
{
- return __vmcache(ickill, 0, 0);
+ return __vmcache(hvmc_ickill, 0, 0);
}
static inline long __vmcache_dckill(void)
{
- return __vmcache(dckill, 0, 0);
+ return __vmcache(hvmc_dckill, 0, 0);
}
static inline long __vmcache_l2kill(void)
{
- return __vmcache(l2kill, 0, 0);
+ return __vmcache(hvmc_l2kill, 0, 0);
}
static inline long __vmcache_dccleaninva(unsigned long addr, unsigned long len)
{
- return __vmcache(dccleaninva, addr, len);
+ return __vmcache(hvmc_dccleaninva, addr, len);
}
static inline long __vmcache_icinva(unsigned long addr, unsigned long len)
{
- return __vmcache(icinva, addr, len);
+ return __vmcache(hvmc_icinva, addr, len);
}
static inline long __vmcache_idsync(unsigned long addr,
unsigned long len)
{
- return __vmcache(idsync, addr, len);
+ return __vmcache(hvmc_idsync, addr, len);
}
static inline long __vmcache_fetch_cfg(unsigned long val)
{
- return __vmcache(fetch_cfg, val, 0);
+ return __vmcache(hvmc_fetch_cfg, val, 0);
}
/* interrupt operations */
static inline long __vmintop_nop(void)
{
- return __vmintop(nop, 0, 0, 0, 0);
+ return __vmintop(hvmi_nop, 0, 0, 0, 0);
}
static inline long __vmintop_globen(long i)
{
- return __vmintop(globen, i, 0, 0, 0);
+ return __vmintop(hvmi_globen, i, 0, 0, 0);
}
static inline long __vmintop_globdis(long i)
{
- return __vmintop(globdis, i, 0, 0, 0);
+ return __vmintop(hvmi_globdis, i, 0, 0, 0);
}
static inline long __vmintop_locen(long i)
{
- return __vmintop(locen, i, 0, 0, 0);
+ return __vmintop(hvmi_locen, i, 0, 0, 0);
}
static inline long __vmintop_locdis(long i)
{
- return __vmintop(locdis, i, 0, 0, 0);
+ return __vmintop(hvmi_locdis, i, 0, 0, 0);
}
static inline long __vmintop_affinity(long i, long cpu)
{
- return __vmintop(locdis, i, cpu, 0, 0);
+ return __vmintop(hvmi_affinity, i, cpu, 0, 0);
}
static inline long __vmintop_get(void)
{
- return __vmintop(get, 0, 0, 0, 0);
+ return __vmintop(hvmi_get, 0, 0, 0, 0);
}
static inline long __vmintop_peek(void)
{
- return __vmintop(peek, 0, 0, 0, 0);
+ return __vmintop(hvmi_peek, 0, 0, 0, 0);
}
static inline long __vmintop_status(long i)
{
- return __vmintop(status, i, 0, 0, 0);
+ return __vmintop(hvmi_status, i, 0, 0, 0);
}
static inline long __vmintop_post(long i)
{
- return __vmintop(post, i, 0, 0, 0);
+ return __vmintop(hvmi_post, i, 0, 0, 0);
}
static inline long __vmintop_clear(long i)
{
- return __vmintop(clear, i, 0, 0, 0);
+ return __vmintop(hvmi_clear, i, 0, 0, 0);
}
#else /* Only assembly code should reference these */
diff --git a/arch/hexagon/include/asm/io.h b/arch/hexagon/include/asm/io.h
index 1b7698e19139..70298996e9b2 100644
--- a/arch/hexagon/include/asm/io.h
+++ b/arch/hexagon/include/asm/io.h
@@ -189,6 +189,8 @@ static inline void writel(u32 data, volatile void __iomem *addr)
#define writew_relaxed __raw_writew
#define writel_relaxed __raw_writel
+#define mmiowb()
+
/*
* Need an mtype somewhere in here, for cache type deals?
* This is probably too long for an inline.
diff --git a/arch/hexagon/include/asm/kgdb.h b/arch/hexagon/include/asm/kgdb.h
index 32a6fb66944a..ccd3ac336b24 100644
--- a/arch/hexagon/include/asm/kgdb.h
+++ b/arch/hexagon/include/asm/kgdb.h
@@ -34,10 +34,11 @@ static inline void arch_kgdb_breakpoint(void)
* 32 gpr + sa0/1 + lc0/1 + m0/1 + gp + ugp + pred + pc = 42 total.
* vm regs = psp+elr+est+badva = 4
* syscall+restart = 2 more
- * so 48 = 42 +4 + 2
+ * also add cs0/1 = 2
+ * so 48 = 42 + 4 + 2 + 2
*/
#define DBG_USER_REGS 42
-#define DBG_MAX_REG_NUM (DBG_USER_REGS + 6)
+#define DBG_MAX_REG_NUM (DBG_USER_REGS + 8)
#define NUMREGBYTES (DBG_MAX_REG_NUM*4)
#endif /* __HEXAGON_KGDB_H__ */
diff --git a/arch/hexagon/include/asm/pgalloc.h b/arch/hexagon/include/asm/pgalloc.h
index 4c9d382d7798..77da3b0ae3c2 100644
--- a/arch/hexagon/include/asm/pgalloc.h
+++ b/arch/hexagon/include/asm/pgalloc.h
@@ -45,7 +45,7 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm)
* map with a copy of the kernel's persistent map.
*/
- memcpy(pgd, swapper_pg_dir, PTRS_PER_PGD*sizeof(pgd_t *));
+ memcpy(pgd, swapper_pg_dir, PTRS_PER_PGD*sizeof(pgd_t));
mm->context.generation = kmap_generation;
/* Physical version is what is passed to virtual machine on switch */
diff --git a/arch/hexagon/include/asm/smp.h b/arch/hexagon/include/asm/smp.h
index 2b9b974e0952..ca171c13891d 100644
--- a/arch/hexagon/include/asm/smp.h
+++ b/arch/hexagon/include/asm/smp.h
@@ -29,7 +29,6 @@ enum ipi_message_type {
IPI_NOP = 0,
IPI_RESCHEDULE = 1,
IPI_CALL_FUNC,
- IPI_CALL_FUNC_SINGLE,
IPI_CPU_STOP,
IPI_TIMER,
};
diff --git a/arch/hexagon/include/uapi/asm/registers.h b/arch/hexagon/include/uapi/asm/registers.h
index 487d6ceca5e7..e7be31840a90 100644
--- a/arch/hexagon/include/uapi/asm/registers.h
+++ b/arch/hexagon/include/uapi/asm/registers.h
@@ -6,8 +6,6 @@
#ifndef _ASM_REGISTERS_H
#define _ASM_REGISTERS_H
-#define SP r29
-
#ifndef __ASSEMBLY__
/* See kernel/entry.S for further documentation. */
@@ -215,7 +213,7 @@ struct pt_regs {
#define pt_clr_singlestep(regs) ((regs)->hvmer.vmest &= ~(1<<HVM_VMEST_SS_SFT))
#define pt_set_rte_sp(regs, sp) do {\
- pt_psp(regs) = (regs)->SP = (sp);\
+ pt_psp(regs) = (regs)->r29 = (sp);\
} while (0)
#define pt_set_kmode(regs) \
diff --git a/arch/hexagon/include/uapi/asm/setup.h b/arch/hexagon/include/uapi/asm/setup.h
index e48285e4af96..7e3952d6221c 100644
--- a/arch/hexagon/include/uapi/asm/setup.h
+++ b/arch/hexagon/include/uapi/asm/setup.h
@@ -19,7 +19,12 @@
#ifndef _ASM_SETUP_H
#define _ASM_SETUP_H
+#ifdef __KERNEL__
#include <linux/init.h>
+#else
+#define __init
+#endif
+
#include <asm-generic/setup.h>
extern char external_cmdline_buffer;
diff --git a/arch/hexagon/kernel/Makefile b/arch/hexagon/kernel/Makefile
index 29fc933a7722..009228b8611c 100644
--- a/arch/hexagon/kernel/Makefile
+++ b/arch/hexagon/kernel/Makefile
@@ -15,3 +15,5 @@ obj-y += vm_vectors.o
obj-$(CONFIG_HAS_DMA) += dma.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
+
+obj-$(CONFIG_VGA_CONSOLE) += screen_info.o
diff --git a/arch/hexagon/kernel/hexagon_ksyms.c b/arch/hexagon/kernel/hexagon_ksyms.c
index 32b1379d6877..c041d8ecb1e2 100644
--- a/arch/hexagon/kernel/hexagon_ksyms.c
+++ b/arch/hexagon/kernel/hexagon_ksyms.c
@@ -18,23 +18,39 @@
* 02110-1301, USA.
*/
+#include <linux/dma-mapping.h>
#include <asm/hexagon_vm.h>
+#include <asm/io.h>
#include <asm/uaccess.h>
+/* Additional functions */
+EXPORT_SYMBOL(__clear_user_hexagon);
EXPORT_SYMBOL(__copy_from_user_hexagon);
EXPORT_SYMBOL(__copy_to_user_hexagon);
+EXPORT_SYMBOL(__iounmap);
+EXPORT_SYMBOL(__strnlen_user);
EXPORT_SYMBOL(__vmgetie);
EXPORT_SYMBOL(__vmsetie);
+EXPORT_SYMBOL(__vmyield);
+EXPORT_SYMBOL(empty_zero_page);
+EXPORT_SYMBOL(ioremap_nocache);
EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memset);
+/* Additional variables */
+EXPORT_SYMBOL(__phys_offset);
+EXPORT_SYMBOL(_dflt_cache_att);
+EXPORT_SYMBOL(bad_dma_address);
+
#define DECLARE_EXPORT(name) \
extern void name(void); EXPORT_SYMBOL(name)
/* Symbols found in libgcc that assorted kernel modules need */
DECLARE_EXPORT(__hexagon_memcpy_likely_aligned_min32bytes_mult8bytes);
-DECLARE_EXPORT(__hexagon_divsi3);
-DECLARE_EXPORT(__hexagon_modsi3);
-DECLARE_EXPORT(__hexagon_udivsi3);
-DECLARE_EXPORT(__hexagon_umodsi3);
+/* Additional functions */
+DECLARE_EXPORT(__divsi3);
+DECLARE_EXPORT(__modsi3);
+DECLARE_EXPORT(__udivsi3);
+DECLARE_EXPORT(__umodsi3);
+DECLARE_EXPORT(csum_tcpudp_magic);
diff --git a/arch/hexagon/kernel/kgdb.c b/arch/hexagon/kernel/kgdb.c
index 82d5c2593323..038580cc5abf 100644
--- a/arch/hexagon/kernel/kgdb.c
+++ b/arch/hexagon/kernel/kgdb.c
@@ -18,6 +18,8 @@
* 02110-1301, USA.
*/
+#include <linux/irq.h>
+#include <linux/sched.h>
#include <linux/kdebug.h>
#include <linux/kgdb.h>
diff --git a/arch/hexagon/kernel/ptrace.c b/arch/hexagon/kernel/ptrace.c
index de829eb7f185..390a9ad14ca1 100644
--- a/arch/hexagon/kernel/ptrace.c
+++ b/arch/hexagon/kernel/ptrace.c
@@ -183,6 +183,7 @@ static const struct user_regset_view hexagon_user_view = {
.e_machine = ELF_ARCH,
.ei_osabi = ELF_OSABI,
.regsets = hexagon_regsets,
+ .e_flags = ELF_CORE_EFLAGS,
.n = ARRAY_SIZE(hexagon_regsets)
};
diff --git a/arch/hexagon/kernel/reset.c b/arch/hexagon/kernel/reset.c
index 6aeabc962b3b..76483c10130d 100644
--- a/arch/hexagon/kernel/reset.c
+++ b/arch/hexagon/kernel/reset.c
@@ -33,6 +33,5 @@ void machine_restart(char *cmd)
{
}
-void pm_power_off(void)
-{
-}
+void (*pm_power_off)(void) = NULL;
+EXPORT_SYMBOL(pm_power_off);
diff --git a/arch/hexagon/kernel/screen_info.c b/arch/hexagon/kernel/screen_info.c
new file mode 100644
index 000000000000..1e1ceb18bafe
--- /dev/null
+++ b/arch/hexagon/kernel/screen_info.c
@@ -0,0 +1,3 @@
+#include <linux/screen_info.h>
+
+struct screen_info screen_info;
diff --git a/arch/hexagon/kernel/smp.c b/arch/hexagon/kernel/smp.c
index 9faaa940452b..ff759f26b96a 100644
--- a/arch/hexagon/kernel/smp.c
+++ b/arch/hexagon/kernel/smp.c
@@ -64,10 +64,6 @@ static inline void __handle_ipi(unsigned long *ops, struct ipi_data *ipi,
generic_smp_call_function_interrupt();
break;
- case IPI_CALL_FUNC_SINGLE:
- generic_smp_call_function_single_interrupt();
- break;
-
case IPI_CPU_STOP:
/*
* call vmstop()
@@ -248,7 +244,7 @@ void smp_send_stop(void)
void arch_send_call_function_single_ipi(int cpu)
{
- send_ipi(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE);
+ send_ipi(cpumask_of(cpu), IPI_CALL_FUNC);
}
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
diff --git a/arch/hexagon/kernel/time.c b/arch/hexagon/kernel/time.c
index 9903fad997f3..17fbf45bf150 100644
--- a/arch/hexagon/kernel/time.c
+++ b/arch/hexagon/kernel/time.c
@@ -191,9 +191,6 @@ void __init time_init_deferred(void)
{
struct resource *resource = NULL;
struct clock_event_device *ce_dev = &hexagon_clockevent_dev;
- struct device_node *dn;
- struct resource r;
- int err;
ce_dev->cpumask = cpu_all_mask;
@@ -232,6 +229,15 @@ void __init time_init(void)
late_time_init = time_init_deferred;
}
+void __delay(unsigned long cycles)
+{
+ unsigned long long start = __vmgettime();
+
+ while ((__vmgettime() - start) < cycles)
+ cpu_relax();
+}
+EXPORT_SYMBOL(__delay);
+
/*
* This could become parametric or perhaps even computed at run-time,
* but for now we take the observed simulator jitter.
diff --git a/arch/ia64/kernel/err_inject.c b/arch/ia64/kernel/err_inject.c
index f59c0b844e88..0c161ed6d18e 100644
--- a/arch/ia64/kernel/err_inject.c
+++ b/arch/ia64/kernel/err_inject.c
@@ -269,12 +269,17 @@ err_inject_init(void)
#ifdef ERR_INJ_DEBUG
printk(KERN_INFO "Enter error injection driver.\n");
#endif
+
+ cpu_notifier_register_begin();
+
for_each_online_cpu(i) {
err_inject_cpu_callback(&err_inject_cpu_notifier, CPU_ONLINE,
(void *)(long)i);
}
- register_hotcpu_notifier(&err_inject_cpu_notifier);
+ __register_hotcpu_notifier(&err_inject_cpu_notifier);
+
+ cpu_notifier_register_done();
return 0;
}
@@ -288,11 +293,17 @@ err_inject_exit(void)
#ifdef ERR_INJ_DEBUG
printk(KERN_INFO "Exit error injection driver.\n");
#endif
+
+ cpu_notifier_register_begin();
+
for_each_online_cpu(i) {
sys_dev = get_cpu_device(i);
sysfs_remove_group(&sys_dev->kobj, &err_inject_attr_group);
}
- unregister_hotcpu_notifier(&err_inject_cpu_notifier);
+
+ __unregister_hotcpu_notifier(&err_inject_cpu_notifier);
+
+ cpu_notifier_register_done();
}
module_init(err_inject_init);
diff --git a/arch/ia64/kernel/ftrace.c b/arch/ia64/kernel/ftrace.c
index 7fc8c961b1f7..3b0c2aa07857 100644
--- a/arch/ia64/kernel/ftrace.c
+++ b/arch/ia64/kernel/ftrace.c
@@ -198,9 +198,7 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
}
/* run from kstop_machine */
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- *(unsigned long *)data = 0;
-
return 0;
}
diff --git a/arch/ia64/kernel/palinfo.c b/arch/ia64/kernel/palinfo.c
index ab333284f4b2..c39c3cd3ac34 100644
--- a/arch/ia64/kernel/palinfo.c
+++ b/arch/ia64/kernel/palinfo.c
@@ -996,13 +996,17 @@ palinfo_init(void)
if (!palinfo_dir)
return -ENOMEM;
+ cpu_notifier_register_begin();
+
/* Create palinfo dirs in /proc for all online cpus */
for_each_online_cpu(i) {
create_palinfo_proc_entries(i);
}
/* Register for future delivery via notify registration */
- register_hotcpu_notifier(&palinfo_cpu_notifier);
+ __register_hotcpu_notifier(&palinfo_cpu_notifier);
+
+ cpu_notifier_register_done();
return 0;
}
diff --git a/arch/ia64/kernel/salinfo.c b/arch/ia64/kernel/salinfo.c
index 960a396f5929..ee9719eebb1e 100644
--- a/arch/ia64/kernel/salinfo.c
+++ b/arch/ia64/kernel/salinfo.c
@@ -635,6 +635,8 @@ salinfo_init(void)
(void *)salinfo_entries[i].feature);
}
+ cpu_notifier_register_begin();
+
for (i = 0; i < ARRAY_SIZE(salinfo_log_name); i++) {
data = salinfo_data + i;
data->type = i;
@@ -669,7 +671,9 @@ salinfo_init(void)
salinfo_timer.function = &salinfo_timeout;
add_timer(&salinfo_timer);
- register_hotcpu_notifier(&salinfo_cpu_notifier);
+ __register_hotcpu_notifier(&salinfo_cpu_notifier);
+
+ cpu_notifier_register_done();
return 0;
}
diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c
index ca69a5a96dcc..f295f9abba4b 100644
--- a/arch/ia64/kernel/topology.c
+++ b/arch/ia64/kernel/topology.c
@@ -454,12 +454,16 @@ static int __init cache_sysfs_init(void)
{
int i;
+ cpu_notifier_register_begin();
+
for_each_online_cpu(i) {
struct device *sys_dev = get_cpu_device((unsigned int)i);
cache_add_dev(sys_dev);
}
- register_hotcpu_notifier(&cache_cpu_notifier);
+ __register_hotcpu_notifier(&cache_cpu_notifier);
+
+ cpu_notifier_register_done();
return 0;
}
diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c
index 53f44bee9ebb..6a4309bb821a 100644
--- a/arch/ia64/kvm/kvm-ia64.c
+++ b/arch/ia64/kvm/kvm-ia64.c
@@ -199,6 +199,7 @@ int kvm_dev_ioctl_check_extension(long ext)
case KVM_CAP_IRQCHIP:
case KVM_CAP_MP_STATE:
case KVM_CAP_IRQ_INJECT_STATUS:
+ case KVM_CAP_IOAPIC_POLARITY_IGNORED:
r = 1;
break;
case KVM_CAP_COALESCED_MMIO:
diff --git a/arch/m68k/configs/m5208evb_defconfig b/arch/m68k/configs/m5208evb_defconfig
index c1616824e201..e7292f460af4 100644
--- a/arch/m68k/configs/m5208evb_defconfig
+++ b/arch/m68k/configs/m5208evb_defconfig
@@ -40,7 +40,6 @@ CONFIG_INET=y
# CONFIG_IPV6 is not set
# CONFIG_FW_LOADER is not set
CONFIG_MTD=y
-CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_RAM=y
diff --git a/arch/m68k/configs/m5249evb_defconfig b/arch/m68k/configs/m5249evb_defconfig
index a6599e42facf..0cd4b39f325b 100644
--- a/arch/m68k/configs/m5249evb_defconfig
+++ b/arch/m68k/configs/m5249evb_defconfig
@@ -38,7 +38,6 @@ CONFIG_INET=y
# CONFIG_IPV6 is not set
# CONFIG_FW_LOADER is not set
CONFIG_MTD=y
-CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_RAM=y
diff --git a/arch/m68k/configs/m5272c3_defconfig b/arch/m68k/configs/m5272c3_defconfig
index 3fa60a57a0f9..a60cb3509135 100644
--- a/arch/m68k/configs/m5272c3_defconfig
+++ b/arch/m68k/configs/m5272c3_defconfig
@@ -36,7 +36,6 @@ CONFIG_INET=y
# CONFIG_IPV6 is not set
# CONFIG_FW_LOADER is not set
CONFIG_MTD=y
-CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_RAM=y
diff --git a/arch/m68k/configs/m5275evb_defconfig b/arch/m68k/configs/m5275evb_defconfig
index a1230e82bb1e..e6502ab7cb2f 100644
--- a/arch/m68k/configs/m5275evb_defconfig
+++ b/arch/m68k/configs/m5275evb_defconfig
@@ -39,7 +39,6 @@ CONFIG_INET=y
# CONFIG_IPV6 is not set
# CONFIG_FW_LOADER is not set
CONFIG_MTD=y
-CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_RAM=y
diff --git a/arch/m68k/configs/m5307c3_defconfig b/arch/m68k/configs/m5307c3_defconfig
index 43795f41f7c7..023812abd2e6 100644
--- a/arch/m68k/configs/m5307c3_defconfig
+++ b/arch/m68k/configs/m5307c3_defconfig
@@ -38,7 +38,6 @@ CONFIG_INET=y
# CONFIG_IPV6 is not set
# CONFIG_FW_LOADER is not set
CONFIG_MTD=y
-CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_RAM=y
diff --git a/arch/m68k/configs/m5407c3_defconfig b/arch/m68k/configs/m5407c3_defconfig
index 72746c57a571..557b39f3be90 100644
--- a/arch/m68k/configs/m5407c3_defconfig
+++ b/arch/m68k/configs/m5407c3_defconfig
@@ -38,7 +38,6 @@ CONFIG_INET=y
# CONFIG_IPV6 is not set
# CONFIG_FW_LOADER is not set
CONFIG_MTD=y
-CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_RAM=y
diff --git a/arch/m68k/include/asm/io_no.h b/arch/m68k/include/asm/io_no.h
index e1534783e94e..52f7e8499172 100644
--- a/arch/m68k/include/asm/io_no.h
+++ b/arch/m68k/include/asm/io_no.h
@@ -55,7 +55,7 @@ static inline unsigned int _swapl(volatile unsigned long v)
#define __raw_writew writew
#define __raw_writel writel
-static inline void io_outsb(unsigned int addr, void *buf, int len)
+static inline void io_outsb(unsigned int addr, const void *buf, int len)
{
volatile unsigned char *ap = (volatile unsigned char *) addr;
unsigned char *bp = (unsigned char *) buf;
@@ -63,7 +63,7 @@ static inline void io_outsb(unsigned int addr, void *buf, int len)
*ap = *bp++;
}
-static inline void io_outsw(unsigned int addr, void *buf, int len)
+static inline void io_outsw(unsigned int addr, const void *buf, int len)
{
volatile unsigned short *ap = (volatile unsigned short *) addr;
unsigned short *bp = (unsigned short *) buf;
@@ -71,7 +71,7 @@ static inline void io_outsw(unsigned int addr, void *buf, int len)
*ap = _swapw(*bp++);
}
-static inline void io_outsl(unsigned int addr, void *buf, int len)
+static inline void io_outsl(unsigned int addr, const void *buf, int len)
{
volatile unsigned int *ap = (volatile unsigned int *) addr;
unsigned int *bp = (unsigned int *) buf;
diff --git a/arch/metag/kernel/ftrace.c b/arch/metag/kernel/ftrace.c
index a774f321643f..ed1d685157c2 100644
--- a/arch/metag/kernel/ftrace.c
+++ b/arch/metag/kernel/ftrace.c
@@ -117,10 +117,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
}
/* run from kstop_machine */
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- /* The return code is returned via data */
- writel(0, data);
-
return 0;
}
diff --git a/arch/microblaze/kernel/ftrace.c b/arch/microblaze/kernel/ftrace.c
index e8a5e9cf4ed1..bbcd2533766c 100644
--- a/arch/microblaze/kernel/ftrace.c
+++ b/arch/microblaze/kernel/ftrace.c
@@ -171,11 +171,8 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
return ret;
}
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- /* The return code is retured via data */
- *(unsigned long *)data = 0;
-
return 0;
}
diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h
index a995fce87791..060aaa6348d7 100644
--- a/arch/mips/include/asm/kvm_host.h
+++ b/arch/mips/include/asm/kvm_host.h
@@ -30,16 +30,16 @@
/* Special address that contains the comm page, used for reducing # of traps */
-#define KVM_GUEST_COMMPAGE_ADDR 0x0
+#define KVM_GUEST_COMMPAGE_ADDR 0x0
#define KVM_GUEST_KERNEL_MODE(vcpu) ((kvm_read_c0_guest_status(vcpu->arch.cop0) & (ST0_EXL | ST0_ERL)) || \
((kvm_read_c0_guest_status(vcpu->arch.cop0) & KSU_USER) == 0))
-#define KVM_GUEST_KUSEG 0x00000000UL
-#define KVM_GUEST_KSEG0 0x40000000UL
-#define KVM_GUEST_KSEG23 0x60000000UL
-#define KVM_GUEST_KSEGX(a) ((_ACAST32_(a)) & 0x60000000)
-#define KVM_GUEST_CPHYSADDR(a) ((_ACAST32_(a)) & 0x1fffffff)
+#define KVM_GUEST_KUSEG 0x00000000UL
+#define KVM_GUEST_KSEG0 0x40000000UL
+#define KVM_GUEST_KSEG23 0x60000000UL
+#define KVM_GUEST_KSEGX(a) ((_ACAST32_(a)) & 0x60000000)
+#define KVM_GUEST_CPHYSADDR(a) ((_ACAST32_(a)) & 0x1fffffff)
#define KVM_GUEST_CKSEG0ADDR(a) (KVM_GUEST_CPHYSADDR(a) | KVM_GUEST_KSEG0)
#define KVM_GUEST_CKSEG1ADDR(a) (KVM_GUEST_CPHYSADDR(a) | KVM_GUEST_KSEG1)
@@ -52,17 +52,17 @@
#define KVM_GUEST_KSEG1ADDR(a) (KVM_GUEST_CPHYSADDR(a) | KVM_GUEST_KSEG1)
#define KVM_GUEST_KSEG23ADDR(a) (KVM_GUEST_CPHYSADDR(a) | KVM_GUEST_KSEG23)
-#define KVM_INVALID_PAGE 0xdeadbeef
-#define KVM_INVALID_INST 0xdeadbeef
-#define KVM_INVALID_ADDR 0xdeadbeef
+#define KVM_INVALID_PAGE 0xdeadbeef
+#define KVM_INVALID_INST 0xdeadbeef
+#define KVM_INVALID_ADDR 0xdeadbeef
-#define KVM_MALTA_GUEST_RTC_ADDR 0xb8000070UL
+#define KVM_MALTA_GUEST_RTC_ADDR 0xb8000070UL
-#define GUEST_TICKS_PER_JIFFY (40000000/HZ)
-#define MS_TO_NS(x) (x * 1E6L)
+#define GUEST_TICKS_PER_JIFFY (40000000/HZ)
+#define MS_TO_NS(x) (x * 1E6L)
-#define CAUSEB_DC 27
-#define CAUSEF_DC (_ULCAST_(1) << 27)
+#define CAUSEB_DC 27
+#define CAUSEF_DC (_ULCAST_(1) << 27)
struct kvm;
struct kvm_run;
@@ -126,8 +126,8 @@ struct kvm_arch {
int commpage_tlb;
};
-#define N_MIPS_COPROC_REGS 32
-#define N_MIPS_COPROC_SEL 8
+#define N_MIPS_COPROC_REGS 32
+#define N_MIPS_COPROC_SEL 8
struct mips_coproc {
unsigned long reg[N_MIPS_COPROC_REGS][N_MIPS_COPROC_SEL];
@@ -139,124 +139,124 @@ struct mips_coproc {
/*
* Coprocessor 0 register names
*/
-#define MIPS_CP0_TLB_INDEX 0
-#define MIPS_CP0_TLB_RANDOM 1
-#define MIPS_CP0_TLB_LOW 2
-#define MIPS_CP0_TLB_LO0 2
-#define MIPS_CP0_TLB_LO1 3
-#define MIPS_CP0_TLB_CONTEXT 4
-#define MIPS_CP0_TLB_PG_MASK 5
-#define MIPS_CP0_TLB_WIRED 6
-#define MIPS_CP0_HWRENA 7
-#define MIPS_CP0_BAD_VADDR 8
-#define MIPS_CP0_COUNT 9
-#define MIPS_CP0_TLB_HI 10
-#define MIPS_CP0_COMPARE 11
-#define MIPS_CP0_STATUS 12
-#define MIPS_CP0_CAUSE 13
-#define MIPS_CP0_EXC_PC 14
-#define MIPS_CP0_PRID 15
-#define MIPS_CP0_CONFIG 16
-#define MIPS_CP0_LLADDR 17
-#define MIPS_CP0_WATCH_LO 18
-#define MIPS_CP0_WATCH_HI 19
-#define MIPS_CP0_TLB_XCONTEXT 20
-#define MIPS_CP0_ECC 26
-#define MIPS_CP0_CACHE_ERR 27
-#define MIPS_CP0_TAG_LO 28
-#define MIPS_CP0_TAG_HI 29
-#define MIPS_CP0_ERROR_PC 30
-#define MIPS_CP0_DEBUG 23
-#define MIPS_CP0_DEPC 24
-#define MIPS_CP0_PERFCNT 25
-#define MIPS_CP0_ERRCTL 26
-#define MIPS_CP0_DATA_LO 28
-#define MIPS_CP0_DATA_HI 29
-#define MIPS_CP0_DESAVE 31
-
-#define MIPS_CP0_CONFIG_SEL 0
-#define MIPS_CP0_CONFIG1_SEL 1
-#define MIPS_CP0_CONFIG2_SEL 2
-#define MIPS_CP0_CONFIG3_SEL 3
+#define MIPS_CP0_TLB_INDEX 0
+#define MIPS_CP0_TLB_RANDOM 1
+#define MIPS_CP0_TLB_LOW 2
+#define MIPS_CP0_TLB_LO0 2
+#define MIPS_CP0_TLB_LO1 3
+#define MIPS_CP0_TLB_CONTEXT 4
+#define MIPS_CP0_TLB_PG_MASK 5
+#define MIPS_CP0_TLB_WIRED 6
+#define MIPS_CP0_HWRENA 7
+#define MIPS_CP0_BAD_VADDR 8
+#define MIPS_CP0_COUNT 9
+#define MIPS_CP0_TLB_HI 10
+#define MIPS_CP0_COMPARE 11
+#define MIPS_CP0_STATUS 12
+#define MIPS_CP0_CAUSE 13
+#define MIPS_CP0_EXC_PC 14
+#define MIPS_CP0_PRID 15
+#define MIPS_CP0_CONFIG 16
+#define MIPS_CP0_LLADDR 17
+#define MIPS_CP0_WATCH_LO 18
+#define MIPS_CP0_WATCH_HI 19
+#define MIPS_CP0_TLB_XCONTEXT 20
+#define MIPS_CP0_ECC 26
+#define MIPS_CP0_CACHE_ERR 27
+#define MIPS_CP0_TAG_LO 28
+#define MIPS_CP0_TAG_HI 29
+#define MIPS_CP0_ERROR_PC 30
+#define MIPS_CP0_DEBUG 23
+#define MIPS_CP0_DEPC 24
+#define MIPS_CP0_PERFCNT 25
+#define MIPS_CP0_ERRCTL 26
+#define MIPS_CP0_DATA_LO 28
+#define MIPS_CP0_DATA_HI 29
+#define MIPS_CP0_DESAVE 31
+
+#define MIPS_CP0_CONFIG_SEL 0
+#define MIPS_CP0_CONFIG1_SEL 1
+#define MIPS_CP0_CONFIG2_SEL 2
+#define MIPS_CP0_CONFIG3_SEL 3
/* Config0 register bits */
-#define CP0C0_M 31
-#define CP0C0_K23 28
-#define CP0C0_KU 25
-#define CP0C0_MDU 20
-#define CP0C0_MM 17
-#define CP0C0_BM 16
-#define CP0C0_BE 15
-#define CP0C0_AT 13
-#define CP0C0_AR 10
-#define CP0C0_MT 7
-#define CP0C0_VI 3
-#define CP0C0_K0 0
+#define CP0C0_M 31
+#define CP0C0_K23 28
+#define CP0C0_KU 25
+#define CP0C0_MDU 20
+#define CP0C0_MM 17
+#define CP0C0_BM 16
+#define CP0C0_BE 15
+#define CP0C0_AT 13
+#define CP0C0_AR 10
+#define CP0C0_MT 7
+#define CP0C0_VI 3
+#define CP0C0_K0 0
/* Config1 register bits */
-#define CP0C1_M 31
-#define CP0C1_MMU 25
-#define CP0C1_IS 22
-#define CP0C1_IL 19
-#define CP0C1_IA 16
-#define CP0C1_DS 13
-#define CP0C1_DL 10
-#define CP0C1_DA 7
-#define CP0C1_C2 6
-#define CP0C1_MD 5
-#define CP0C1_PC 4
-#define CP0C1_WR 3
-#define CP0C1_CA 2
-#define CP0C1_EP 1
-#define CP0C1_FP 0
+#define CP0C1_M 31
+#define CP0C1_MMU 25
+#define CP0C1_IS 22
+#define CP0C1_IL 19
+#define CP0C1_IA 16
+#define CP0C1_DS 13
+#define CP0C1_DL 10
+#define CP0C1_DA 7
+#define CP0C1_C2 6
+#define CP0C1_MD 5
+#define CP0C1_PC 4
+#define CP0C1_WR 3
+#define CP0C1_CA 2
+#define CP0C1_EP 1
+#define CP0C1_FP 0
/* Config2 Register bits */
-#define CP0C2_M 31
-#define CP0C2_TU 28
-#define CP0C2_TS 24
-#define CP0C2_TL 20
-#define CP0C2_TA 16
-#define CP0C2_SU 12
-#define CP0C2_SS 8
-#define CP0C2_SL 4
-#define CP0C2_SA 0
+#define CP0C2_M 31
+#define CP0C2_TU 28
+#define CP0C2_TS 24
+#define CP0C2_TL 20
+#define CP0C2_TA 16
+#define CP0C2_SU 12
+#define CP0C2_SS 8
+#define CP0C2_SL 4
+#define CP0C2_SA 0
/* Config3 Register bits */
-#define CP0C3_M 31
-#define CP0C3_ISA_ON_EXC 16
-#define CP0C3_ULRI 13
-#define CP0C3_DSPP 10
-#define CP0C3_LPA 7
-#define CP0C3_VEIC 6
-#define CP0C3_VInt 5
-#define CP0C3_SP 4
-#define CP0C3_MT 2
-#define CP0C3_SM 1
-#define CP0C3_TL 0
+#define CP0C3_M 31
+#define CP0C3_ISA_ON_EXC 16
+#define CP0C3_ULRI 13
+#define CP0C3_DSPP 10
+#define CP0C3_LPA 7
+#define CP0C3_VEIC 6
+#define CP0C3_VInt 5
+#define CP0C3_SP 4
+#define CP0C3_MT 2
+#define CP0C3_SM 1
+#define CP0C3_TL 0
/* Have config1, Cacheable, noncoherent, write-back, write allocate*/
-#define MIPS_CONFIG0 \
+#define MIPS_CONFIG0 \
((1 << CP0C0_M) | (0x3 << CP0C0_K0))
/* Have config2, no coprocessor2 attached, no MDMX support attached,
no performance counters, watch registers present,
no code compression, EJTAG present, no FPU, no watch registers */
-#define MIPS_CONFIG1 \
-((1 << CP0C1_M) | \
- (0 << CP0C1_C2) | (0 << CP0C1_MD) | (0 << CP0C1_PC) | \
- (0 << CP0C1_WR) | (0 << CP0C1_CA) | (1 << CP0C1_EP) | \
+#define MIPS_CONFIG1 \
+((1 << CP0C1_M) | \
+ (0 << CP0C1_C2) | (0 << CP0C1_MD) | (0 << CP0C1_PC) | \
+ (0 << CP0C1_WR) | (0 << CP0C1_CA) | (1 << CP0C1_EP) | \
(0 << CP0C1_FP))
/* Have config3, no tertiary/secondary caches implemented */
-#define MIPS_CONFIG2 \
+#define MIPS_CONFIG2 \
((1 << CP0C2_M))
/* No config4, no DSP ASE, no large physaddr (PABITS),
no external interrupt controller, no vectored interrupts,
no 1kb pages, no SmartMIPS ASE, no trace logic */
-#define MIPS_CONFIG3 \
-((0 << CP0C3_M) | (0 << CP0C3_DSPP) | (0 << CP0C3_LPA) | \
- (0 << CP0C3_VEIC) | (0 << CP0C3_VInt) | (0 << CP0C3_SP) | \
+#define MIPS_CONFIG3 \
+((0 << CP0C3_M) | (0 << CP0C3_DSPP) | (0 << CP0C3_LPA) | \
+ (0 << CP0C3_VEIC) | (0 << CP0C3_VInt) | (0 << CP0C3_SP) | \
(0 << CP0C3_SM) | (0 << CP0C3_TL))
/* MMU types, the first four entries have the same layout as the
@@ -274,36 +274,36 @@ enum mips_mmu_types {
/*
* Trap codes
*/
-#define T_INT 0 /* Interrupt pending */
-#define T_TLB_MOD 1 /* TLB modified fault */
-#define T_TLB_LD_MISS 2 /* TLB miss on load or ifetch */
-#define T_TLB_ST_MISS 3 /* TLB miss on a store */
-#define T_ADDR_ERR_LD 4 /* Address error on a load or ifetch */
-#define T_ADDR_ERR_ST 5 /* Address error on a store */
-#define T_BUS_ERR_IFETCH 6 /* Bus error on an ifetch */
-#define T_BUS_ERR_LD_ST 7 /* Bus error on a load or store */
-#define T_SYSCALL 8 /* System call */
-#define T_BREAK 9 /* Breakpoint */
-#define T_RES_INST 10 /* Reserved instruction exception */
-#define T_COP_UNUSABLE 11 /* Coprocessor unusable */
-#define T_OVFLOW 12 /* Arithmetic overflow */
+#define T_INT 0 /* Interrupt pending */
+#define T_TLB_MOD 1 /* TLB modified fault */
+#define T_TLB_LD_MISS 2 /* TLB miss on load or ifetch */
+#define T_TLB_ST_MISS 3 /* TLB miss on a store */
+#define T_ADDR_ERR_LD 4 /* Address error on a load or ifetch */
+#define T_ADDR_ERR_ST 5 /* Address error on a store */
+#define T_BUS_ERR_IFETCH 6 /* Bus error on an ifetch */
+#define T_BUS_ERR_LD_ST 7 /* Bus error on a load or store */
+#define T_SYSCALL 8 /* System call */
+#define T_BREAK 9 /* Breakpoint */
+#define T_RES_INST 10 /* Reserved instruction exception */
+#define T_COP_UNUSABLE 11 /* Coprocessor unusable */
+#define T_OVFLOW 12 /* Arithmetic overflow */
/*
* Trap definitions added for r4000 port.
*/
-#define T_TRAP 13 /* Trap instruction */
-#define T_VCEI 14 /* Virtual coherency exception */
-#define T_FPE 15 /* Floating point exception */
-#define T_WATCH 23 /* Watch address reference */
-#define T_VCED 31 /* Virtual coherency data */
+#define T_TRAP 13 /* Trap instruction */
+#define T_VCEI 14 /* Virtual coherency exception */
+#define T_FPE 15 /* Floating point exception */
+#define T_WATCH 23 /* Watch address reference */
+#define T_VCED 31 /* Virtual coherency data */
/* Resume Flags */
-#define RESUME_FLAG_DR (1<<0) /* Reload guest nonvolatile state? */
-#define RESUME_FLAG_HOST (1<<1) /* Resume host? */
+#define RESUME_FLAG_DR (1<<0) /* Reload guest nonvolatile state? */
+#define RESUME_FLAG_HOST (1<<1) /* Resume host? */
-#define RESUME_GUEST 0
-#define RESUME_GUEST_DR RESUME_FLAG_DR
-#define RESUME_HOST RESUME_FLAG_HOST
+#define RESUME_GUEST 0
+#define RESUME_GUEST_DR RESUME_FLAG_DR
+#define RESUME_HOST RESUME_FLAG_HOST
enum emulation_result {
EMULATE_DONE, /* no further processing */
@@ -313,24 +313,27 @@ enum emulation_result {
EMULATE_PRIV_FAIL,
};
-#define MIPS3_PG_G 0x00000001 /* Global; ignore ASID if in lo0 & lo1 */
-#define MIPS3_PG_V 0x00000002 /* Valid */
-#define MIPS3_PG_NV 0x00000000
-#define MIPS3_PG_D 0x00000004 /* Dirty */
+#define MIPS3_PG_G 0x00000001 /* Global; ignore ASID if in lo0 & lo1 */
+#define MIPS3_PG_V 0x00000002 /* Valid */
+#define MIPS3_PG_NV 0x00000000
+#define MIPS3_PG_D 0x00000004 /* Dirty */
#define mips3_paddr_to_tlbpfn(x) \
- (((unsigned long)(x) >> MIPS3_PG_SHIFT) & MIPS3_PG_FRAME)
+ (((unsigned long)(x) >> MIPS3_PG_SHIFT) & MIPS3_PG_FRAME)
#define mips3_tlbpfn_to_paddr(x) \
- ((unsigned long)((x) & MIPS3_PG_FRAME) << MIPS3_PG_SHIFT)
+ ((unsigned long)((x) & MIPS3_PG_FRAME) << MIPS3_PG_SHIFT)
-#define MIPS3_PG_SHIFT 6
-#define MIPS3_PG_FRAME 0x3fffffc0
+#define MIPS3_PG_SHIFT 6
+#define MIPS3_PG_FRAME 0x3fffffc0
-#define VPN2_MASK 0xffffe000
-#define TLB_IS_GLOBAL(x) (((x).tlb_lo0 & MIPS3_PG_G) && ((x).tlb_lo1 & MIPS3_PG_G))
-#define TLB_VPN2(x) ((x).tlb_hi & VPN2_MASK)
-#define TLB_ASID(x) ((x).tlb_hi & ASID_MASK)
-#define TLB_IS_VALID(x, va) (((va) & (1 << PAGE_SHIFT)) ? ((x).tlb_lo1 & MIPS3_PG_V) : ((x).tlb_lo0 & MIPS3_PG_V))
+#define VPN2_MASK 0xffffe000
+#define TLB_IS_GLOBAL(x) (((x).tlb_lo0 & MIPS3_PG_G) && \
+ ((x).tlb_lo1 & MIPS3_PG_G))
+#define TLB_VPN2(x) ((x).tlb_hi & VPN2_MASK)
+#define TLB_ASID(x) ((x).tlb_hi & ASID_MASK)
+#define TLB_IS_VALID(x, va) (((va) & (1 << PAGE_SHIFT)) \
+ ? ((x).tlb_lo1 & MIPS3_PG_V) \
+ : ((x).tlb_lo0 & MIPS3_PG_V))
struct kvm_mips_tlb {
long tlb_mask;
@@ -339,7 +342,7 @@ struct kvm_mips_tlb {
long tlb_lo1;
};
-#define KVM_MIPS_GUEST_TLB_SIZE 64
+#define KVM_MIPS_GUEST_TLB_SIZE 64
struct kvm_vcpu_arch {
void *host_ebase, *guest_ebase;
unsigned long host_stack;
@@ -400,65 +403,67 @@ struct kvm_vcpu_arch {
};
-#define kvm_read_c0_guest_index(cop0) (cop0->reg[MIPS_CP0_TLB_INDEX][0])
-#define kvm_write_c0_guest_index(cop0, val) (cop0->reg[MIPS_CP0_TLB_INDEX][0] = val)
-#define kvm_read_c0_guest_entrylo0(cop0) (cop0->reg[MIPS_CP0_TLB_LO0][0])
-#define kvm_read_c0_guest_entrylo1(cop0) (cop0->reg[MIPS_CP0_TLB_LO1][0])
-#define kvm_read_c0_guest_context(cop0) (cop0->reg[MIPS_CP0_TLB_CONTEXT][0])
-#define kvm_write_c0_guest_context(cop0, val) (cop0->reg[MIPS_CP0_TLB_CONTEXT][0] = (val))
-#define kvm_read_c0_guest_userlocal(cop0) (cop0->reg[MIPS_CP0_TLB_CONTEXT][2])
-#define kvm_read_c0_guest_pagemask(cop0) (cop0->reg[MIPS_CP0_TLB_PG_MASK][0])
-#define kvm_write_c0_guest_pagemask(cop0, val) (cop0->reg[MIPS_CP0_TLB_PG_MASK][0] = (val))
-#define kvm_read_c0_guest_wired(cop0) (cop0->reg[MIPS_CP0_TLB_WIRED][0])
-#define kvm_write_c0_guest_wired(cop0, val) (cop0->reg[MIPS_CP0_TLB_WIRED][0] = (val))
-#define kvm_read_c0_guest_badvaddr(cop0) (cop0->reg[MIPS_CP0_BAD_VADDR][0])
-#define kvm_write_c0_guest_badvaddr(cop0, val) (cop0->reg[MIPS_CP0_BAD_VADDR][0] = (val))
-#define kvm_read_c0_guest_count(cop0) (cop0->reg[MIPS_CP0_COUNT][0])
-#define kvm_write_c0_guest_count(cop0, val) (cop0->reg[MIPS_CP0_COUNT][0] = (val))
-#define kvm_read_c0_guest_entryhi(cop0) (cop0->reg[MIPS_CP0_TLB_HI][0])
-#define kvm_write_c0_guest_entryhi(cop0, val) (cop0->reg[MIPS_CP0_TLB_HI][0] = (val))
-#define kvm_read_c0_guest_compare(cop0) (cop0->reg[MIPS_CP0_COMPARE][0])
-#define kvm_write_c0_guest_compare(cop0, val) (cop0->reg[MIPS_CP0_COMPARE][0] = (val))
-#define kvm_read_c0_guest_status(cop0) (cop0->reg[MIPS_CP0_STATUS][0])
-#define kvm_write_c0_guest_status(cop0, val) (cop0->reg[MIPS_CP0_STATUS][0] = (val))
-#define kvm_read_c0_guest_intctl(cop0) (cop0->reg[MIPS_CP0_STATUS][1])
-#define kvm_write_c0_guest_intctl(cop0, val) (cop0->reg[MIPS_CP0_STATUS][1] = (val))
-#define kvm_read_c0_guest_cause(cop0) (cop0->reg[MIPS_CP0_CAUSE][0])
-#define kvm_write_c0_guest_cause(cop0, val) (cop0->reg[MIPS_CP0_CAUSE][0] = (val))
-#define kvm_read_c0_guest_epc(cop0) (cop0->reg[MIPS_CP0_EXC_PC][0])
-#define kvm_write_c0_guest_epc(cop0, val) (cop0->reg[MIPS_CP0_EXC_PC][0] = (val))
-#define kvm_read_c0_guest_prid(cop0) (cop0->reg[MIPS_CP0_PRID][0])
-#define kvm_write_c0_guest_prid(cop0, val) (cop0->reg[MIPS_CP0_PRID][0] = (val))
-#define kvm_read_c0_guest_ebase(cop0) (cop0->reg[MIPS_CP0_PRID][1])
-#define kvm_write_c0_guest_ebase(cop0, val) (cop0->reg[MIPS_CP0_PRID][1] = (val))
-#define kvm_read_c0_guest_config(cop0) (cop0->reg[MIPS_CP0_CONFIG][0])
-#define kvm_read_c0_guest_config1(cop0) (cop0->reg[MIPS_CP0_CONFIG][1])
-#define kvm_read_c0_guest_config2(cop0) (cop0->reg[MIPS_CP0_CONFIG][2])
-#define kvm_read_c0_guest_config3(cop0) (cop0->reg[MIPS_CP0_CONFIG][3])
-#define kvm_read_c0_guest_config7(cop0) (cop0->reg[MIPS_CP0_CONFIG][7])
-#define kvm_write_c0_guest_config(cop0, val) (cop0->reg[MIPS_CP0_CONFIG][0] = (val))
-#define kvm_write_c0_guest_config1(cop0, val) (cop0->reg[MIPS_CP0_CONFIG][1] = (val))
-#define kvm_write_c0_guest_config2(cop0, val) (cop0->reg[MIPS_CP0_CONFIG][2] = (val))
-#define kvm_write_c0_guest_config3(cop0, val) (cop0->reg[MIPS_CP0_CONFIG][3] = (val))
-#define kvm_write_c0_guest_config7(cop0, val) (cop0->reg[MIPS_CP0_CONFIG][7] = (val))
-#define kvm_read_c0_guest_errorepc(cop0) (cop0->reg[MIPS_CP0_ERROR_PC][0])
-#define kvm_write_c0_guest_errorepc(cop0, val) (cop0->reg[MIPS_CP0_ERROR_PC][0] = (val))
-
-#define kvm_set_c0_guest_status(cop0, val) (cop0->reg[MIPS_CP0_STATUS][0] |= (val))
-#define kvm_clear_c0_guest_status(cop0, val) (cop0->reg[MIPS_CP0_STATUS][0] &= ~(val))
-#define kvm_set_c0_guest_cause(cop0, val) (cop0->reg[MIPS_CP0_CAUSE][0] |= (val))
-#define kvm_clear_c0_guest_cause(cop0, val) (cop0->reg[MIPS_CP0_CAUSE][0] &= ~(val))
-#define kvm_change_c0_guest_cause(cop0, change, val) \
-{ \
- kvm_clear_c0_guest_cause(cop0, change); \
- kvm_set_c0_guest_cause(cop0, ((val) & (change))); \
+#define kvm_read_c0_guest_index(cop0) (cop0->reg[MIPS_CP0_TLB_INDEX][0])
+#define kvm_write_c0_guest_index(cop0, val) (cop0->reg[MIPS_CP0_TLB_INDEX][0] = val)
+#define kvm_read_c0_guest_entrylo0(cop0) (cop0->reg[MIPS_CP0_TLB_LO0][0])
+#define kvm_read_c0_guest_entrylo1(cop0) (cop0->reg[MIPS_CP0_TLB_LO1][0])
+#define kvm_read_c0_guest_context(cop0) (cop0->reg[MIPS_CP0_TLB_CONTEXT][0])
+#define kvm_write_c0_guest_context(cop0, val) (cop0->reg[MIPS_CP0_TLB_CONTEXT][0] = (val))
+#define kvm_read_c0_guest_userlocal(cop0) (cop0->reg[MIPS_CP0_TLB_CONTEXT][2])
+#define kvm_read_c0_guest_pagemask(cop0) (cop0->reg[MIPS_CP0_TLB_PG_MASK][0])
+#define kvm_write_c0_guest_pagemask(cop0, val) (cop0->reg[MIPS_CP0_TLB_PG_MASK][0] = (val))
+#define kvm_read_c0_guest_wired(cop0) (cop0->reg[MIPS_CP0_TLB_WIRED][0])
+#define kvm_write_c0_guest_wired(cop0, val) (cop0->reg[MIPS_CP0_TLB_WIRED][0] = (val))
+#define kvm_read_c0_guest_hwrena(cop0) (cop0->reg[MIPS_CP0_HWRENA][0])
+#define kvm_write_c0_guest_hwrena(cop0, val) (cop0->reg[MIPS_CP0_HWRENA][0] = (val))
+#define kvm_read_c0_guest_badvaddr(cop0) (cop0->reg[MIPS_CP0_BAD_VADDR][0])
+#define kvm_write_c0_guest_badvaddr(cop0, val) (cop0->reg[MIPS_CP0_BAD_VADDR][0] = (val))
+#define kvm_read_c0_guest_count(cop0) (cop0->reg[MIPS_CP0_COUNT][0])
+#define kvm_write_c0_guest_count(cop0, val) (cop0->reg[MIPS_CP0_COUNT][0] = (val))
+#define kvm_read_c0_guest_entryhi(cop0) (cop0->reg[MIPS_CP0_TLB_HI][0])
+#define kvm_write_c0_guest_entryhi(cop0, val) (cop0->reg[MIPS_CP0_TLB_HI][0] = (val))
+#define kvm_read_c0_guest_compare(cop0) (cop0->reg[MIPS_CP0_COMPARE][0])
+#define kvm_write_c0_guest_compare(cop0, val) (cop0->reg[MIPS_CP0_COMPARE][0] = (val))
+#define kvm_read_c0_guest_status(cop0) (cop0->reg[MIPS_CP0_STATUS][0])
+#define kvm_write_c0_guest_status(cop0, val) (cop0->reg[MIPS_CP0_STATUS][0] = (val))
+#define kvm_read_c0_guest_intctl(cop0) (cop0->reg[MIPS_CP0_STATUS][1])
+#define kvm_write_c0_guest_intctl(cop0, val) (cop0->reg[MIPS_CP0_STATUS][1] = (val))
+#define kvm_read_c0_guest_cause(cop0) (cop0->reg[MIPS_CP0_CAUSE][0])
+#define kvm_write_c0_guest_cause(cop0, val) (cop0->reg[MIPS_CP0_CAUSE][0] = (val))
+#define kvm_read_c0_guest_epc(cop0) (cop0->reg[MIPS_CP0_EXC_PC][0])
+#define kvm_write_c0_guest_epc(cop0, val) (cop0->reg[MIPS_CP0_EXC_PC][0] = (val))
+#define kvm_read_c0_guest_prid(cop0) (cop0->reg[MIPS_CP0_PRID][0])
+#define kvm_write_c0_guest_prid(cop0, val) (cop0->reg[MIPS_CP0_PRID][0] = (val))
+#define kvm_read_c0_guest_ebase(cop0) (cop0->reg[MIPS_CP0_PRID][1])
+#define kvm_write_c0_guest_ebase(cop0, val) (cop0->reg[MIPS_CP0_PRID][1] = (val))
+#define kvm_read_c0_guest_config(cop0) (cop0->reg[MIPS_CP0_CONFIG][0])
+#define kvm_read_c0_guest_config1(cop0) (cop0->reg[MIPS_CP0_CONFIG][1])
+#define kvm_read_c0_guest_config2(cop0) (cop0->reg[MIPS_CP0_CONFIG][2])
+#define kvm_read_c0_guest_config3(cop0) (cop0->reg[MIPS_CP0_CONFIG][3])
+#define kvm_read_c0_guest_config7(cop0) (cop0->reg[MIPS_CP0_CONFIG][7])
+#define kvm_write_c0_guest_config(cop0, val) (cop0->reg[MIPS_CP0_CONFIG][0] = (val))
+#define kvm_write_c0_guest_config1(cop0, val) (cop0->reg[MIPS_CP0_CONFIG][1] = (val))
+#define kvm_write_c0_guest_config2(cop0, val) (cop0->reg[MIPS_CP0_CONFIG][2] = (val))
+#define kvm_write_c0_guest_config3(cop0, val) (cop0->reg[MIPS_CP0_CONFIG][3] = (val))
+#define kvm_write_c0_guest_config7(cop0, val) (cop0->reg[MIPS_CP0_CONFIG][7] = (val))
+#define kvm_read_c0_guest_errorepc(cop0) (cop0->reg[MIPS_CP0_ERROR_PC][0])
+#define kvm_write_c0_guest_errorepc(cop0, val) (cop0->reg[MIPS_CP0_ERROR_PC][0] = (val))
+
+#define kvm_set_c0_guest_status(cop0, val) (cop0->reg[MIPS_CP0_STATUS][0] |= (val))
+#define kvm_clear_c0_guest_status(cop0, val) (cop0->reg[MIPS_CP0_STATUS][0] &= ~(val))
+#define kvm_set_c0_guest_cause(cop0, val) (cop0->reg[MIPS_CP0_CAUSE][0] |= (val))
+#define kvm_clear_c0_guest_cause(cop0, val) (cop0->reg[MIPS_CP0_CAUSE][0] &= ~(val))
+#define kvm_change_c0_guest_cause(cop0, change, val) \
+{ \
+ kvm_clear_c0_guest_cause(cop0, change); \
+ kvm_set_c0_guest_cause(cop0, ((val) & (change))); \
}
-#define kvm_set_c0_guest_ebase(cop0, val) (cop0->reg[MIPS_CP0_PRID][1] |= (val))
-#define kvm_clear_c0_guest_ebase(cop0, val) (cop0->reg[MIPS_CP0_PRID][1] &= ~(val))
-#define kvm_change_c0_guest_ebase(cop0, change, val) \
-{ \
- kvm_clear_c0_guest_ebase(cop0, change); \
- kvm_set_c0_guest_ebase(cop0, ((val) & (change))); \
+#define kvm_set_c0_guest_ebase(cop0, val) (cop0->reg[MIPS_CP0_PRID][1] |= (val))
+#define kvm_clear_c0_guest_ebase(cop0, val) (cop0->reg[MIPS_CP0_PRID][1] &= ~(val))
+#define kvm_change_c0_guest_ebase(cop0, change, val) \
+{ \
+ kvm_clear_c0_guest_ebase(cop0, change); \
+ kvm_set_c0_guest_ebase(cop0, ((val) & (change))); \
}
diff --git a/arch/mips/kernel/ftrace.c b/arch/mips/kernel/ftrace.c
index 74fe73506d8f..60e7e5e45af1 100644
--- a/arch/mips/kernel/ftrace.c
+++ b/arch/mips/kernel/ftrace.c
@@ -201,7 +201,7 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return ftrace_modify_code(FTRACE_CALL_IP, new);
}
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
/* Encode the instructions when booting */
ftrace_dyn_arch_init_insns();
@@ -209,9 +209,6 @@ int __init ftrace_dyn_arch_init(void *data)
/* Remove "b ftrace_stub" to ensure ftrace_caller() is executed */
ftrace_modify_code(MCOUNT_ADDR, INSN_NOP);
- /* The return code is retured via data */
- *(unsigned long *)data = 0;
-
return 0;
}
#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/mips/kvm/kvm_mips_emul.c b/arch/mips/kvm/kvm_mips_emul.c
index 4b6274b47f33..e3fec99941a7 100644
--- a/arch/mips/kvm/kvm_mips_emul.c
+++ b/arch/mips/kvm/kvm_mips_emul.c
@@ -436,13 +436,6 @@ kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc, uint32_t cause,
sel = inst & 0x7;
co_bit = (inst >> 25) & 1;
- /* Verify that the register is valid */
- if (rd > MIPS_CP0_DESAVE) {
- printk("Invalid rd: %d\n", rd);
- er = EMULATE_FAIL;
- goto done;
- }
-
if (co_bit) {
op = (inst) & 0xff;
@@ -1542,8 +1535,15 @@ kvm_mips_handle_ri(unsigned long cause, uint32_t *opc,
}
if ((inst & OPCODE) == SPEC3 && (inst & FUNC) == RDHWR) {
+ int usermode = !KVM_GUEST_KERNEL_MODE(vcpu);
int rd = (inst & RD) >> 11;
int rt = (inst & RT) >> 16;
+ /* If usermode, check RDHWR rd is allowed by guest HWREna */
+ if (usermode && !(kvm_read_c0_guest_hwrena(cop0) & BIT(rd))) {
+ kvm_debug("RDHWR %#x disallowed by HWREna @ %p\n",
+ rd, opc);
+ goto emulate_ri;
+ }
switch (rd) {
case 0: /* CPU number */
arch->gprs[rt] = 0;
@@ -1567,31 +1567,27 @@ kvm_mips_handle_ri(unsigned long cause, uint32_t *opc,
}
break;
case 29:
-#if 1
arch->gprs[rt] = kvm_read_c0_guest_userlocal(cop0);
-#else
- /* UserLocal not implemented */
- er = kvm_mips_emulate_ri_exc(cause, opc, run, vcpu);
-#endif
break;
default:
- printk("RDHWR not supported\n");
- er = EMULATE_FAIL;
- break;
+ kvm_debug("RDHWR %#x not supported @ %p\n", rd, opc);
+ goto emulate_ri;
}
} else {
- printk("Emulate RI not supported @ %p: %#x\n", opc, inst);
- er = EMULATE_FAIL;
+ kvm_debug("Emulate RI not supported @ %p: %#x\n", opc, inst);
+ goto emulate_ri;
}
+ return EMULATE_DONE;
+
+emulate_ri:
/*
- * Rollback PC only if emulation was unsuccessful
+ * Rollback PC (if in branch delay slot then the PC already points to
+ * branch target), and pass the RI exception to the guest OS.
*/
- if (er == EMULATE_FAIL) {
- vcpu->arch.pc = curr_pc;
- }
- return er;
+ vcpu->arch.pc = curr_pc;
+ return kvm_mips_emulate_ri_exc(cause, opc, run, vcpu);
}
enum emulation_result
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index f3d7846bc9b2..6c03a94991ad 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -90,6 +90,7 @@ config PPC
select BINFMT_ELF
select OF
select OF_EARLY_FLATTREE
+ select OF_RESERVED_MEM
select HAVE_FTRACE_MCOUNT_RECORD
select HAVE_DYNAMIC_FTRACE
select HAVE_FUNCTION_TRACER
diff --git a/arch/powerpc/include/asm/archrandom.h b/arch/powerpc/include/asm/archrandom.h
index d853d163ba47..bde531103638 100644
--- a/arch/powerpc/include/asm/archrandom.h
+++ b/arch/powerpc/include/asm/archrandom.h
@@ -25,8 +25,26 @@ static inline int arch_get_random_int(unsigned int *v)
return rc;
}
+static inline int arch_has_random(void)
+{
+ return !!ppc_md.get_random_long;
+}
+
int powernv_get_random_long(unsigned long *v);
+static inline int arch_get_random_seed_long(unsigned long *v)
+{
+ return 0;
+}
+static inline int arch_get_random_seed_int(unsigned int *v)
+{
+ return 0;
+}
+static inline int arch_has_random_seed(void)
+{
+ return 0;
+}
+
#endif /* CONFIG_ARCH_RANDOM */
#endif /* _ASM_POWERPC_ARCHRANDOM_H */
diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h
index 83851aabfdc8..bb1e38a23ac7 100644
--- a/arch/powerpc/include/asm/kvm_book3s.h
+++ b/arch/powerpc/include/asm/kvm_book3s.h
@@ -304,6 +304,11 @@ static inline ulong kvmppc_get_fault_dar(struct kvm_vcpu *vcpu)
return vcpu->arch.fault_dar;
}
+static inline bool is_kvmppc_resume_guest(int r)
+{
+ return (r == RESUME_GUEST || r == RESUME_GUEST_NV);
+}
+
/* Magic register values loaded into r3 and r4 before the 'sc' assembly
* instruction for the OSI hypercalls */
#define OSI_SC_MAGIC_R3 0x113724FA
diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h
index bf0fa8b0a883..51388befeddb 100644
--- a/arch/powerpc/include/asm/kvm_book3s_64.h
+++ b/arch/powerpc/include/asm/kvm_book3s_64.h
@@ -289,6 +289,18 @@ static inline void note_hpte_modification(struct kvm *kvm,
if (atomic_read(&kvm->arch.hpte_mod_interest))
rev->guest_rpte |= HPTE_GR_MODIFIED;
}
+
+/*
+ * Like kvm_memslots(), but for use in real mode when we can't do
+ * any RCU stuff (since the secondary threads are offline from the
+ * kernel's point of view), and we can't print anything.
+ * Thus we use rcu_dereference_raw() rather than rcu_dereference_check().
+ */
+static inline struct kvm_memslots *kvm_memslots_raw(struct kvm *kvm)
+{
+ return rcu_dereference_raw_notrace(kvm->memslots);
+}
+
#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
#endif /* __ASM_KVM_BOOK3S_64_H__ */
diff --git a/arch/powerpc/include/asm/kvm_book3s_asm.h b/arch/powerpc/include/asm/kvm_book3s_asm.h
index f3a91dc02c98..821725c1bf46 100644
--- a/arch/powerpc/include/asm/kvm_book3s_asm.h
+++ b/arch/powerpc/include/asm/kvm_book3s_asm.h
@@ -94,7 +94,7 @@ struct kvmppc_host_state {
unsigned long xics_phys;
u32 saved_xirr;
u64 dabr;
- u64 host_mmcr[3];
+ u64 host_mmcr[7]; /* MMCR 0,1,A, SIAR, SDAR, MMCR2, SIER */
u32 host_pmc[8];
u64 host_purr;
u64 host_spurr;
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index fcd53f0d34ba..4096f16502a9 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -129,6 +129,8 @@ extern long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
struct kvm_create_spapr_tce *args);
extern long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
unsigned long ioba, unsigned long tce);
+extern long kvmppc_h_get_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
+ unsigned long ioba);
extern struct kvm_rma_info *kvm_alloc_rma(void);
extern void kvm_release_rma(struct kvm_rma_info *ri);
extern struct page *kvm_alloc_hpt(unsigned long nr_pages);
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 1a36b8ede417..0dcc48af25a3 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -213,6 +213,7 @@
#define SPRN_ACOP 0x1F /* Available Coprocessor Register */
#define SPRN_TFIAR 0x81 /* Transaction Failure Inst Addr */
#define SPRN_TEXASR 0x82 /* Transaction EXception & Summary */
+#define TEXASR_FS __MASK(63-36) /* Transaction Failure Summary */
#define SPRN_TEXASRU 0x83 /* '' '' '' Upper 32 */
#define SPRN_TFHAR 0x80 /* Transaction Failure Handler Addr */
#define SPRN_CTRLF 0x088
diff --git a/arch/powerpc/include/asm/tm.h b/arch/powerpc/include/asm/tm.h
index 0c9f8b74dd97..c22d704b6d41 100644
--- a/arch/powerpc/include/asm/tm.h
+++ b/arch/powerpc/include/asm/tm.h
@@ -7,6 +7,8 @@
#include <uapi/asm/tm.h>
+#ifndef __ASSEMBLY__
+
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
extern void do_load_up_transact_fpu(struct thread_struct *thread);
extern void do_load_up_transact_altivec(struct thread_struct *thread);
@@ -21,3 +23,5 @@ extern void tm_recheckpoint(struct thread_struct *thread,
extern void tm_abort(uint8_t cause);
extern void tm_save_sprs(struct thread_struct *thread);
extern void tm_restore_sprs(struct thread_struct *thread);
+
+#endif /* __ASSEMBLY__ */
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c
index b0ded97ee4e1..6a014c763cc7 100644
--- a/arch/powerpc/kernel/ftrace.c
+++ b/arch/powerpc/kernel/ftrace.c
@@ -532,13 +532,8 @@ void arch_ftrace_update_code(int command)
ftrace_disable_ftrace_graph_caller();
}
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- /* caller expects data to be zero */
- unsigned long *p = data;
-
- *p = 0;
-
return 0;
}
#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index d711b7eb05aa..dd72bebd708a 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -33,6 +33,7 @@
#include <linux/irq.h>
#include <linux/memblock.h>
#include <linux/of.h>
+#include <linux/of_fdt.h>
#include <asm/prom.h>
#include <asm/rtas.h>
@@ -588,6 +589,8 @@ static void __init early_reserve_mem_dt(void)
memblock_reserve(base, size);
}
}
+
+ early_init_fdt_scan_reserved_mem();
}
static void __init early_reserve_mem(void)
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index 97e1dc917683..d90d4b7810d6 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -975,7 +975,8 @@ static int __init topology_init(void)
int cpu;
register_nodes();
- register_cpu_notifier(&sysfs_cpu_nb);
+
+ cpu_notifier_register_begin();
for_each_possible_cpu(cpu) {
struct cpu *c = &per_cpu(cpu_devices, cpu);
@@ -999,6 +1000,11 @@ static int __init topology_init(void)
if (cpu_online(cpu))
register_cpu_online(cpu);
}
+
+ __register_cpu_notifier(&sysfs_cpu_nb);
+
+ cpu_notifier_register_done();
+
#ifdef CONFIG_PPC64
sysfs_create_dscr_default();
#endif /* CONFIG_PPC64 */
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index 303ece75b8e4..fb25ebc0af0c 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -262,7 +262,14 @@ int kvmppc_mmu_hv_init(void)
static void kvmppc_mmu_book3s_64_hv_reset_msr(struct kvm_vcpu *vcpu)
{
- kvmppc_set_msr(vcpu, vcpu->arch.intr_msr);
+ unsigned long msr = vcpu->arch.intr_msr;
+
+ /* If transactional, change to suspend mode on IRQ delivery */
+ if (MSR_TM_TRANSACTIONAL(vcpu->arch.shregs.msr))
+ msr |= MSR_TS_S;
+ else
+ msr |= vcpu->arch.shregs.msr & MSR_TS_MASK;
+ kvmppc_set_msr(vcpu, msr);
}
/*
diff --git a/arch/powerpc/kvm/book3s_64_vio_hv.c b/arch/powerpc/kvm/book3s_64_vio_hv.c
index 2c25f5412bdb..89e96b3e0039 100644
--- a/arch/powerpc/kvm/book3s_64_vio_hv.c
+++ b/arch/powerpc/kvm/book3s_64_vio_hv.c
@@ -75,3 +75,31 @@ long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
return H_TOO_HARD;
}
EXPORT_SYMBOL_GPL(kvmppc_h_put_tce);
+
+long kvmppc_h_get_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
+ unsigned long ioba)
+{
+ struct kvm *kvm = vcpu->kvm;
+ struct kvmppc_spapr_tce_table *stt;
+
+ list_for_each_entry(stt, &kvm->arch.spapr_tce_tables, list) {
+ if (stt->liobn == liobn) {
+ unsigned long idx = ioba >> SPAPR_TCE_SHIFT;
+ struct page *page;
+ u64 *tbl;
+
+ if (ioba >= stt->window_size)
+ return H_PARAMETER;
+
+ page = stt->pages[idx / TCES_PER_PAGE];
+ tbl = (u64 *)page_address(page);
+
+ vcpu->arch.gpr[4] = tbl[idx % TCES_PER_PAGE];
+ return H_SUCCESS;
+ }
+ }
+
+ /* Didn't find the liobn, punt it to userspace */
+ return H_TOO_HARD;
+}
+EXPORT_SYMBOL_GPL(kvmppc_h_get_tce);
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 17fc9496b6ac..8227dba5af0f 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -86,7 +86,7 @@ static void kvmppc_fast_vcpu_kick_hv(struct kvm_vcpu *vcpu)
/* CPU points to the first thread of the core */
if (cpu != me && cpu >= 0 && cpu < nr_cpu_ids) {
-#ifdef CONFIG_KVM_XICS
+#ifdef CONFIG_PPC_ICP_NATIVE
int real_cpu = cpu + vcpu->arch.ptid;
if (paca[real_cpu].kvm_hstate.xics_phys)
xics_wake_cpu(real_cpu);
@@ -879,17 +879,6 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
case KVM_REG_PPC_IAMR:
*val = get_reg_val(id, vcpu->arch.iamr);
break;
-#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
- case KVM_REG_PPC_TFHAR:
- *val = get_reg_val(id, vcpu->arch.tfhar);
- break;
- case KVM_REG_PPC_TFIAR:
- *val = get_reg_val(id, vcpu->arch.tfiar);
- break;
- case KVM_REG_PPC_TEXASR:
- *val = get_reg_val(id, vcpu->arch.texasr);
- break;
-#endif
case KVM_REG_PPC_FSCR:
*val = get_reg_val(id, vcpu->arch.fscr);
break;
@@ -970,6 +959,69 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
case KVM_REG_PPC_PPR:
*val = get_reg_val(id, vcpu->arch.ppr);
break;
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ case KVM_REG_PPC_TFHAR:
+ *val = get_reg_val(id, vcpu->arch.tfhar);
+ break;
+ case KVM_REG_PPC_TFIAR:
+ *val = get_reg_val(id, vcpu->arch.tfiar);
+ break;
+ case KVM_REG_PPC_TEXASR:
+ *val = get_reg_val(id, vcpu->arch.texasr);
+ break;
+ case KVM_REG_PPC_TM_GPR0 ... KVM_REG_PPC_TM_GPR31:
+ i = id - KVM_REG_PPC_TM_GPR0;
+ *val = get_reg_val(id, vcpu->arch.gpr_tm[i]);
+ break;
+ case KVM_REG_PPC_TM_VSR0 ... KVM_REG_PPC_TM_VSR63:
+ {
+ int j;
+ i = id - KVM_REG_PPC_TM_VSR0;
+ if (i < 32)
+ for (j = 0; j < TS_FPRWIDTH; j++)
+ val->vsxval[j] = vcpu->arch.fp_tm.fpr[i][j];
+ else {
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ val->vval = vcpu->arch.vr_tm.vr[i-32];
+ else
+ r = -ENXIO;
+ }
+ break;
+ }
+ case KVM_REG_PPC_TM_CR:
+ *val = get_reg_val(id, vcpu->arch.cr_tm);
+ break;
+ case KVM_REG_PPC_TM_LR:
+ *val = get_reg_val(id, vcpu->arch.lr_tm);
+ break;
+ case KVM_REG_PPC_TM_CTR:
+ *val = get_reg_val(id, vcpu->arch.ctr_tm);
+ break;
+ case KVM_REG_PPC_TM_FPSCR:
+ *val = get_reg_val(id, vcpu->arch.fp_tm.fpscr);
+ break;
+ case KVM_REG_PPC_TM_AMR:
+ *val = get_reg_val(id, vcpu->arch.amr_tm);
+ break;
+ case KVM_REG_PPC_TM_PPR:
+ *val = get_reg_val(id, vcpu->arch.ppr_tm);
+ break;
+ case KVM_REG_PPC_TM_VRSAVE:
+ *val = get_reg_val(id, vcpu->arch.vrsave_tm);
+ break;
+ case KVM_REG_PPC_TM_VSCR:
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ *val = get_reg_val(id, vcpu->arch.vr_tm.vscr.u[3]);
+ else
+ r = -ENXIO;
+ break;
+ case KVM_REG_PPC_TM_DSCR:
+ *val = get_reg_val(id, vcpu->arch.dscr_tm);
+ break;
+ case KVM_REG_PPC_TM_TAR:
+ *val = get_reg_val(id, vcpu->arch.tar_tm);
+ break;
+#endif
case KVM_REG_PPC_ARCH_COMPAT:
*val = get_reg_val(id, vcpu->arch.vcore->arch_compat);
break;
@@ -1039,17 +1091,6 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
case KVM_REG_PPC_IAMR:
vcpu->arch.iamr = set_reg_val(id, *val);
break;
-#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
- case KVM_REG_PPC_TFHAR:
- vcpu->arch.tfhar = set_reg_val(id, *val);
- break;
- case KVM_REG_PPC_TFIAR:
- vcpu->arch.tfiar = set_reg_val(id, *val);
- break;
- case KVM_REG_PPC_TEXASR:
- vcpu->arch.texasr = set_reg_val(id, *val);
- break;
-#endif
case KVM_REG_PPC_FSCR:
vcpu->arch.fscr = set_reg_val(id, *val);
break;
@@ -1144,6 +1185,68 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
case KVM_REG_PPC_PPR:
vcpu->arch.ppr = set_reg_val(id, *val);
break;
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ case KVM_REG_PPC_TFHAR:
+ vcpu->arch.tfhar = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TFIAR:
+ vcpu->arch.tfiar = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TEXASR:
+ vcpu->arch.texasr = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_GPR0 ... KVM_REG_PPC_TM_GPR31:
+ i = id - KVM_REG_PPC_TM_GPR0;
+ vcpu->arch.gpr_tm[i] = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_VSR0 ... KVM_REG_PPC_TM_VSR63:
+ {
+ int j;
+ i = id - KVM_REG_PPC_TM_VSR0;
+ if (i < 32)
+ for (j = 0; j < TS_FPRWIDTH; j++)
+ vcpu->arch.fp_tm.fpr[i][j] = val->vsxval[j];
+ else
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ vcpu->arch.vr_tm.vr[i-32] = val->vval;
+ else
+ r = -ENXIO;
+ break;
+ }
+ case KVM_REG_PPC_TM_CR:
+ vcpu->arch.cr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_LR:
+ vcpu->arch.lr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_CTR:
+ vcpu->arch.ctr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_FPSCR:
+ vcpu->arch.fp_tm.fpscr = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_AMR:
+ vcpu->arch.amr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_PPR:
+ vcpu->arch.ppr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_VRSAVE:
+ vcpu->arch.vrsave_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_VSCR:
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ vcpu->arch.vr.vscr.u[3] = set_reg_val(id, *val);
+ else
+ r = - ENXIO;
+ break;
+ case KVM_REG_PPC_TM_DSCR:
+ vcpu->arch.dscr_tm = set_reg_val(id, *val);
+ break;
+ case KVM_REG_PPC_TM_TAR:
+ vcpu->arch.tar_tm = set_reg_val(id, *val);
+ break;
+#endif
case KVM_REG_PPC_ARCH_COMPAT:
r = kvmppc_set_arch_compat(vcpu, set_reg_val(id, *val));
break;
@@ -1360,9 +1463,7 @@ static void kvmppc_start_thread(struct kvm_vcpu *vcpu)
smp_wmb();
#if defined(CONFIG_PPC_ICP_NATIVE) && defined(CONFIG_SMP)
if (cpu != smp_processor_id()) {
-#ifdef CONFIG_KVM_XICS
xics_wake_cpu(cpu);
-#endif
if (vcpu->arch.ptid)
++vc->n_woken;
}
@@ -1530,7 +1631,7 @@ static void kvmppc_run_core(struct kvmppc_vcore *vc)
vcpu->arch.trap = 0;
if (vcpu->arch.ceded) {
- if (ret != RESUME_GUEST)
+ if (!is_kvmppc_resume_guest(ret))
kvmppc_end_cede(vcpu);
else
kvmppc_set_timer(vcpu);
@@ -1541,7 +1642,7 @@ static void kvmppc_run_core(struct kvmppc_vcore *vc)
vc->vcore_state = VCORE_INACTIVE;
list_for_each_entry_safe(vcpu, vnext, &vc->runnable_threads,
arch.run_list) {
- if (vcpu->arch.ret != RESUME_GUEST) {
+ if (!is_kvmppc_resume_guest(vcpu->arch.ret)) {
kvmppc_remove_runnable(vc, vcpu);
wake_up(&vcpu->arch.cpu_run);
}
@@ -1731,7 +1832,7 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu)
vcpu->arch.fault_dar, vcpu->arch.fault_dsisr);
srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx);
}
- } while (r == RESUME_GUEST);
+ } while (is_kvmppc_resume_guest(r));
out:
vcpu->arch.state = KVMPPC_VCPU_NOTREADY;
@@ -2366,7 +2467,7 @@ static int kvmppc_book3s_init_hv(void)
*/
r = kvmppc_core_check_processor_compat_hv();
if (r < 0)
- return r;
+ return -ENODEV;
kvm_ops_hv.owner = THIS_MODULE;
kvmppc_hv_ops = &kvm_ops_hv;
diff --git a/arch/powerpc/kvm/book3s_hv_interrupts.S b/arch/powerpc/kvm/book3s_hv_interrupts.S
index e873796b1a29..e18e3cfc32de 100644
--- a/arch/powerpc/kvm/book3s_hv_interrupts.S
+++ b/arch/powerpc/kvm/book3s_hv_interrupts.S
@@ -71,6 +71,14 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
mtmsrd r10,1
/* Save host PMU registers */
+BEGIN_FTR_SECTION
+ /* Work around P8 PMAE bug */
+ li r3, -1
+ clrrdi r3, r3, 10
+ mfspr r8, SPRN_MMCR2
+ mtspr SPRN_MMCR2, r3 /* freeze all counters using MMCR2 */
+ isync
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
li r3, 1
sldi r3, r3, 31 /* MMCR0_FC (freeze counters) bit */
mfspr r7, SPRN_MMCR0 /* save MMCR0 */
@@ -87,9 +95,18 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
cmpwi r5, 0
beq 31f /* skip if not */
mfspr r5, SPRN_MMCR1
+ mfspr r9, SPRN_SIAR
+ mfspr r10, SPRN_SDAR
std r7, HSTATE_MMCR(r13)
std r5, HSTATE_MMCR + 8(r13)
std r6, HSTATE_MMCR + 16(r13)
+ std r9, HSTATE_MMCR + 24(r13)
+ std r10, HSTATE_MMCR + 32(r13)
+BEGIN_FTR_SECTION
+ mfspr r9, SPRN_SIER
+ std r8, HSTATE_MMCR + 40(r13)
+ std r9, HSTATE_MMCR + 48(r13)
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
mfspr r3, SPRN_PMC1
mfspr r5, SPRN_PMC2
mfspr r6, SPRN_PMC3
@@ -110,6 +127,11 @@ BEGIN_FTR_SECTION
stw r10, HSTATE_PMC + 24(r13)
stw r11, HSTATE_PMC + 28(r13)
END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
+BEGIN_FTR_SECTION
+ mfspr r9, SPRN_SIER
+ std r8, HSTATE_MMCR + 40(r13)
+ std r9, HSTATE_MMCR + 48(r13)
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
31:
/*
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index 37fb3caa4c80..1d6c56ad5b60 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -111,7 +111,7 @@ static void remove_revmap_chain(struct kvm *kvm, long pte_index,
rcbits = hpte_r & (HPTE_R_R | HPTE_R_C);
ptel = rev->guest_rpte |= rcbits;
gfn = hpte_rpn(ptel, hpte_page_size(hpte_v, ptel));
- memslot = __gfn_to_memslot(kvm_memslots(kvm), gfn);
+ memslot = __gfn_to_memslot(kvm_memslots_raw(kvm), gfn);
if (!memslot)
return;
@@ -192,7 +192,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
/* Find the memslot (if any) for this address */
gpa = (ptel & HPTE_R_RPN) & ~(psize - 1);
gfn = gpa >> PAGE_SHIFT;
- memslot = __gfn_to_memslot(kvm_memslots(kvm), gfn);
+ memslot = __gfn_to_memslot(kvm_memslots_raw(kvm), gfn);
pa = 0;
is_io = ~0ul;
rmap = NULL;
@@ -670,7 +670,7 @@ long kvmppc_h_protect(struct kvm_vcpu *vcpu, unsigned long flags,
psize = hpte_page_size(v, r);
gfn = ((r & HPTE_R_RPN) & ~(psize - 1)) >> PAGE_SHIFT;
- memslot = __gfn_to_memslot(kvm_memslots(kvm), gfn);
+ memslot = __gfn_to_memslot(kvm_memslots_raw(kvm), gfn);
if (memslot) {
hva = __gfn_to_hva_memslot(memslot, gfn);
pte = lookup_linux_pte_and_update(pgdir, hva,
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index 53d647f8e741..ffbb871c2bd8 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -28,6 +28,9 @@
#include <asm/exception-64s.h>
#include <asm/kvm_book3s_asm.h>
#include <asm/mmu-hash64.h>
+#include <asm/tm.h>
+
+#define VCPU_GPRS_TM(reg) (((reg) * ULONG_SIZE) + VCPU_GPR_TM)
#ifdef __LITTLE_ENDIAN__
#error Need to fix lppaca and SLB shadow accesses in little endian mode
@@ -106,8 +109,18 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
ld r3, HSTATE_MMCR(r13)
ld r4, HSTATE_MMCR + 8(r13)
ld r5, HSTATE_MMCR + 16(r13)
+ ld r6, HSTATE_MMCR + 24(r13)
+ ld r7, HSTATE_MMCR + 32(r13)
mtspr SPRN_MMCR1, r4
mtspr SPRN_MMCRA, r5
+ mtspr SPRN_SIAR, r6
+ mtspr SPRN_SDAR, r7
+BEGIN_FTR_SECTION
+ ld r8, HSTATE_MMCR + 40(r13)
+ ld r9, HSTATE_MMCR + 48(r13)
+ mtspr SPRN_MMCR2, r8
+ mtspr SPRN_SIER, r9
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
mtspr SPRN_MMCR0, r3
isync
23:
@@ -597,6 +610,116 @@ BEGIN_FTR_SECTION
END_FTR_SECTION_NESTED(CPU_FTR_ARCH_206, CPU_FTR_ARCH_206, 89)
END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+BEGIN_FTR_SECTION
+ b skip_tm
+END_FTR_SECTION_IFCLR(CPU_FTR_TM)
+
+ /* Turn on TM/FP/VSX/VMX so we can restore them. */
+ mfmsr r5
+ li r6, MSR_TM >> 32
+ sldi r6, r6, 32
+ or r5, r5, r6
+ ori r5, r5, MSR_FP
+ oris r5, r5, (MSR_VEC | MSR_VSX)@h
+ mtmsrd r5
+
+ /*
+ * The user may change these outside of a transaction, so they must
+ * always be context switched.
+ */
+ ld r5, VCPU_TFHAR(r4)
+ ld r6, VCPU_TFIAR(r4)
+ ld r7, VCPU_TEXASR(r4)
+ mtspr SPRN_TFHAR, r5
+ mtspr SPRN_TFIAR, r6
+ mtspr SPRN_TEXASR, r7
+
+ ld r5, VCPU_MSR(r4)
+ rldicl. r5, r5, 64 - MSR_TS_S_LG, 62
+ beq skip_tm /* TM not active in guest */
+
+ /* Make sure the failure summary is set, otherwise we'll program check
+ * when we trechkpt. It's possible that this might have been not set
+ * on a kvmppc_set_one_reg() call but we shouldn't let this crash the
+ * host.
+ */
+ oris r7, r7, (TEXASR_FS)@h
+ mtspr SPRN_TEXASR, r7
+
+ /*
+ * We need to load up the checkpointed state for the guest.
+ * We need to do this early as it will blow away any GPRs, VSRs and
+ * some SPRs.
+ */
+
+ mr r31, r4
+ addi r3, r31, VCPU_FPRS_TM
+ bl .load_fp_state
+ addi r3, r31, VCPU_VRS_TM
+ bl .load_vr_state
+ mr r4, r31
+ lwz r7, VCPU_VRSAVE_TM(r4)
+ mtspr SPRN_VRSAVE, r7
+
+ ld r5, VCPU_LR_TM(r4)
+ lwz r6, VCPU_CR_TM(r4)
+ ld r7, VCPU_CTR_TM(r4)
+ ld r8, VCPU_AMR_TM(r4)
+ ld r9, VCPU_TAR_TM(r4)
+ mtlr r5
+ mtcr r6
+ mtctr r7
+ mtspr SPRN_AMR, r8
+ mtspr SPRN_TAR, r9
+
+ /*
+ * Load up PPR and DSCR values but don't put them in the actual SPRs
+ * till the last moment to avoid running with userspace PPR and DSCR for
+ * too long.
+ */
+ ld r29, VCPU_DSCR_TM(r4)
+ ld r30, VCPU_PPR_TM(r4)
+
+ std r2, PACATMSCRATCH(r13) /* Save TOC */
+
+ /* Clear the MSR RI since r1, r13 are all going to be foobar. */
+ li r5, 0
+ mtmsrd r5, 1
+
+ /* Load GPRs r0-r28 */
+ reg = 0
+ .rept 29
+ ld reg, VCPU_GPRS_TM(reg)(r31)
+ reg = reg + 1
+ .endr
+
+ mtspr SPRN_DSCR, r29
+ mtspr SPRN_PPR, r30
+
+ /* Load final GPRs */
+ ld 29, VCPU_GPRS_TM(29)(r31)
+ ld 30, VCPU_GPRS_TM(30)(r31)
+ ld 31, VCPU_GPRS_TM(31)(r31)
+
+ /* TM checkpointed state is now setup. All GPRs are now volatile. */
+ TRECHKPT
+
+ /* Now let's get back the state we need. */
+ HMT_MEDIUM
+ GET_PACA(r13)
+ ld r29, HSTATE_DSCR(r13)
+ mtspr SPRN_DSCR, r29
+ ld r4, HSTATE_KVM_VCPU(r13)
+ ld r1, HSTATE_HOST_R1(r13)
+ ld r2, PACATMSCRATCH(r13)
+
+ /* Set the MSR RI since we have our registers back. */
+ li r5, MSR_RI
+ mtmsrd r5, 1
+skip_tm:
+#endif
+
/* Load guest PMU registers */
/* R4 is live here (vcpu pointer) */
li r3, 1
@@ -704,14 +827,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
ld r6, VCPU_VTB(r4)
mtspr SPRN_IC, r5
mtspr SPRN_VTB, r6
-#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
- ld r5, VCPU_TFHAR(r4)
- ld r6, VCPU_TFIAR(r4)
- ld r7, VCPU_TEXASR(r4)
- mtspr SPRN_TFHAR, r5
- mtspr SPRN_TFIAR, r6
- mtspr SPRN_TEXASR, r7
-#endif
ld r8, VCPU_EBBHR(r4)
mtspr SPRN_EBBHR, r8
ld r5, VCPU_EBBRR(r4)
@@ -736,6 +851,10 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
* Set the decrementer to the guest decrementer.
*/
ld r8,VCPU_DEC_EXPIRES(r4)
+ /* r8 is a host timebase value here, convert to guest TB */
+ ld r5,HSTATE_KVM_VCORE(r13)
+ ld r6,VCORE_TB_OFFSET(r5)
+ add r8,r8,r6
mftb r7
subf r3,r7,r8
mtspr SPRN_DEC,r3
@@ -817,7 +936,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
12: mtspr SPRN_SRR0, r10
mr r10,r0
mtspr SPRN_SRR1, r11
- ld r11, VCPU_INTR_MSR(r4)
+ mr r9, r4
+ bl kvmppc_msr_interrupt
5:
/*
@@ -1098,17 +1218,15 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_201)
mftb r6
extsw r5,r5
add r5,r5,r6
+ /* r5 is a guest timebase value here, convert to host TB */
+ ld r3,HSTATE_KVM_VCORE(r13)
+ ld r4,VCORE_TB_OFFSET(r3)
+ subf r5,r4,r5
std r5,VCPU_DEC_EXPIRES(r9)
BEGIN_FTR_SECTION
b 8f
END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
- /* Turn on TM so we can access TFHAR/TFIAR/TEXASR */
- mfmsr r8
- li r0, 1
- rldimi r8, r0, MSR_TM_LG, 63-MSR_TM_LG
- mtmsrd r8
-
/* Save POWER8-specific registers */
mfspr r5, SPRN_IAMR
mfspr r6, SPRN_PSPB
@@ -1122,14 +1240,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
std r5, VCPU_IC(r9)
std r6, VCPU_VTB(r9)
std r7, VCPU_TAR(r9)
-#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
- mfspr r5, SPRN_TFHAR
- mfspr r6, SPRN_TFIAR
- mfspr r7, SPRN_TEXASR
- std r5, VCPU_TFHAR(r9)
- std r6, VCPU_TFIAR(r9)
- std r7, VCPU_TEXASR(r9)
-#endif
mfspr r8, SPRN_EBBHR
std r8, VCPU_EBBHR(r9)
mfspr r5, SPRN_EBBRR
@@ -1387,7 +1497,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
ld r8,VCORE_TB_OFFSET(r5)
cmpdi r8,0
beq 17f
- mftb r6 /* current host timebase */
+ mftb r6 /* current guest timebase */
subf r8,r8,r6
mtspr SPRN_TBU40,r8 /* update upper 40 bits */
mftb r7 /* check if lower 24 bits overflowed */
@@ -1557,7 +1667,7 @@ kvmppc_hdsi:
mtspr SPRN_SRR0, r10
mtspr SPRN_SRR1, r11
li r10, BOOK3S_INTERRUPT_DATA_STORAGE
- ld r11, VCPU_INTR_MSR(r9)
+ bl kvmppc_msr_interrupt
fast_interrupt_c_return:
6: ld r7, VCPU_CTR(r9)
lwz r8, VCPU_XER(r9)
@@ -1626,7 +1736,7 @@ kvmppc_hisi:
1: mtspr SPRN_SRR0, r10
mtspr SPRN_SRR1, r11
li r10, BOOK3S_INTERRUPT_INST_STORAGE
- ld r11, VCPU_INTR_MSR(r9)
+ bl kvmppc_msr_interrupt
b fast_interrupt_c_return
3: ld r6, VCPU_KVM(r9) /* not relocated, use VRMA */
@@ -1669,7 +1779,7 @@ sc_1_fast_return:
mtspr SPRN_SRR0,r10
mtspr SPRN_SRR1,r11
li r10, BOOK3S_INTERRUPT_SYSCALL
- ld r11, VCPU_INTR_MSR(r9)
+ bl kvmppc_msr_interrupt
mr r4,r9
b fast_guest_return
@@ -1691,7 +1801,7 @@ hcall_real_table:
.long 0 /* 0x10 - H_CLEAR_MOD */
.long 0 /* 0x14 - H_CLEAR_REF */
.long .kvmppc_h_protect - hcall_real_table
- .long 0 /* 0x1c - H_GET_TCE */
+ .long .kvmppc_h_get_tce - hcall_real_table
.long .kvmppc_h_put_tce - hcall_real_table
.long 0 /* 0x24 - H_SET_SPRG0 */
.long .kvmppc_h_set_dabr - hcall_real_table
@@ -1997,7 +2107,7 @@ machine_check_realmode:
beq mc_cont
/* If not, deliver a machine check. SRR0/1 are already set */
li r10, BOOK3S_INTERRUPT_MACHINE_CHECK
- ld r11, VCPU_INTR_MSR(r9)
+ bl kvmppc_msr_interrupt
b fast_interrupt_c_return
/*
@@ -2138,8 +2248,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
mfspr r6,SPRN_VRSAVE
stw r6,VCPU_VRSAVE(r31)
mtlr r30
- mtmsrd r5
- isync
blr
/*
@@ -2186,3 +2294,20 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
*/
kvmppc_bad_host_intr:
b .
+
+/*
+ * This mimics the MSR transition on IRQ delivery. The new guest MSR is taken
+ * from VCPU_INTR_MSR and is modified based on the required TM state changes.
+ * r11 has the guest MSR value (in/out)
+ * r9 has a vcpu pointer (in)
+ * r0 is used as a scratch register
+ */
+kvmppc_msr_interrupt:
+ rldicl r0, r11, 64 - MSR_TS_S_LG, 62
+ cmpwi r0, 2 /* Check if we are in transactional state.. */
+ ld r11, VCPU_INTR_MSR(r9)
+ bne 1f
+ /* ... if transactional, change to suspended */
+ li r0, 1
+1: rldimi r11, r0, MSR_TS_S_LG, 63 - MSR_TS_T_LG
+ blr
diff --git a/arch/powerpc/kvm/book3s_rtas.c b/arch/powerpc/kvm/book3s_rtas.c
index cf95cdef73c9..7a053157483b 100644
--- a/arch/powerpc/kvm/book3s_rtas.c
+++ b/arch/powerpc/kvm/book3s_rtas.c
@@ -213,8 +213,11 @@ int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu)
gpa_t args_phys;
int rc;
- /* r4 contains the guest physical address of the RTAS args */
- args_phys = kvmppc_get_gpr(vcpu, 4);
+ /*
+ * r4 contains the guest physical address of the RTAS args
+ * Mask off the top 4 bits since this is a guest real address
+ */
+ args_phys = kvmppc_get_gpr(vcpu, 4) & KVM_PAM;
rc = kvm_read_guest(vcpu->kvm, args_phys, &args, sizeof(args));
if (rc)
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 555034f8505e..808ce1cae21a 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -390,9 +390,9 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
mark));
break;
case BPF_S_ANC_RXHASH:
- BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, rxhash) != 4);
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff,
- rxhash));
+ hash));
break;
case BPF_S_ANC_VLAN_TAG:
case BPF_S_ANC_VLAN_TAG_PRESENT:
@@ -689,6 +689,7 @@ void bpf_jit_compile(struct sk_filter *fp)
((u64 *)image)[0] = (u64)code_base;
((u64 *)image)[1] = local_paca->kernel_toc;
fp->bpf_func = (void *)image;
+ fp->jited = 1;
}
out:
kfree(addrs);
@@ -697,7 +698,7 @@ out:
void bpf_jit_free(struct sk_filter *fp)
{
- if (fp->bpf_func != sk_run_filter)
+ if (fp->jited)
module_free(NULL, fp->bpf_func);
kfree(fp);
}
diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c
index a8fe5aa3d34f..022b38e6a80b 100644
--- a/arch/powerpc/platforms/pseries/dlpar.c
+++ b/arch/powerpc/platforms/pseries/dlpar.c
@@ -11,7 +11,6 @@
*/
#include <linux/kernel.h>
-#include <linux/kref.h>
#include <linux/notifier.h>
#include <linux/spinlock.h>
#include <linux/cpu.h>
@@ -87,7 +86,6 @@ static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa,
}
of_node_set_flag(dn, OF_DYNAMIC);
- kref_init(&dn->kref);
return dn;
}
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c
index f93cdf55628c..0435bb65d0aa 100644
--- a/arch/powerpc/platforms/pseries/reconfig.c
+++ b/arch/powerpc/platforms/pseries/reconfig.c
@@ -12,7 +12,6 @@
*/
#include <linux/kernel.h>
-#include <linux/kref.h>
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
@@ -70,7 +69,6 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist
np->properties = proplist;
of_node_set_flag(np, OF_DYNAMIC);
- kref_init(&np->kref);
np->parent = derive_parent(path);
if (IS_ERR(np->parent)) {
diff --git a/arch/powerpc/sysdev/msi_bitmap.c b/arch/powerpc/sysdev/msi_bitmap.c
index 0968b66b4cf9..8ba60424be95 100644
--- a/arch/powerpc/sysdev/msi_bitmap.c
+++ b/arch/powerpc/sysdev/msi_bitmap.c
@@ -202,7 +202,7 @@ void __init test_of_node(void)
/* There should really be a struct device_node allocator */
memset(&of_node, 0, sizeof(of_node));
- kref_init(&of_node.kref);
+ kref_init(&of_node.kobj.kref);
of_node.full_name = node_name;
check(0 == msi_bitmap_alloc(&bmp, size, &of_node));
diff --git a/arch/s390/include/asm/cio.h b/arch/s390/include/asm/cio.h
index d42625053c37..096339207764 100644
--- a/arch/s390/include/asm/cio.h
+++ b/arch/s390/include/asm/cio.h
@@ -199,7 +199,7 @@ struct esw_eadm {
/**
* struct irb - interruption response block
* @scsw: subchannel status word
- * @esw: extened status word
+ * @esw: extended status word
* @ecw: extended control word
*
* The irb that is handed to the device driver when an interrupt occurs. For
diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h
index 5f8bcc5fe423..35f0faab5361 100644
--- a/arch/s390/include/asm/irq.h
+++ b/arch/s390/include/asm/irq.h
@@ -53,6 +53,7 @@ enum interruption_class {
IRQIO_PCI,
IRQIO_MSI,
IRQIO_VIR,
+ IRQIO_VAI,
NMI_NMI,
CPU_RST,
NR_ARCH_IRQS
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index 9bf95bb30f1a..154b60089be9 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -16,12 +16,22 @@
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/kvm_host.h>
+#include <linux/kvm.h>
#include <asm/debug.h>
#include <asm/cpu.h>
+#include <asm/isc.h>
#define KVM_MAX_VCPUS 64
#define KVM_USER_MEM_SLOTS 32
+/*
+ * These seem to be used for allocating ->chip in the routing table,
+ * which we don't use. 4096 is an out-of-thin-air value. If we need
+ * to look at ->chip later on, we'll need to revisit this.
+ */
+#define KVM_NR_IRQCHIPS 1
+#define KVM_IRQCHIP_NUM_PINS 4096
+
struct sca_entry {
atomic_t scn;
__u32 reserved;
@@ -108,7 +118,9 @@ struct kvm_s390_sie_block {
__u32 fac; /* 0x01a0 */
__u8 reserved1a4[20]; /* 0x01a4 */
__u64 cbrlo; /* 0x01b8 */
- __u8 reserved1c0[40]; /* 0x01c0 */
+ __u8 reserved1c0[30]; /* 0x01c0 */
+ __u64 pp; /* 0x01de */
+ __u8 reserved1e6[2]; /* 0x01e6 */
__u64 itdba; /* 0x01e8 */
__u8 reserved1f0[16]; /* 0x01f0 */
} __attribute__((packed));
@@ -171,18 +183,6 @@ struct kvm_vcpu_stat {
u32 diagnose_9c;
};
-struct kvm_s390_io_info {
- __u16 subchannel_id; /* 0x0b8 */
- __u16 subchannel_nr; /* 0x0ba */
- __u32 io_int_parm; /* 0x0bc */
- __u32 io_int_word; /* 0x0c0 */
-};
-
-struct kvm_s390_ext_info {
- __u32 ext_params;
- __u64 ext_params2;
-};
-
#define PGM_OPERATION 0x01
#define PGM_PRIVILEGED_OP 0x02
#define PGM_EXECUTE 0x03
@@ -191,27 +191,6 @@ struct kvm_s390_ext_info {
#define PGM_SPECIFICATION 0x06
#define PGM_DATA 0x07
-struct kvm_s390_pgm_info {
- __u16 code;
-};
-
-struct kvm_s390_prefix_info {
- __u32 address;
-};
-
-struct kvm_s390_extcall_info {
- __u16 code;
-};
-
-struct kvm_s390_emerg_info {
- __u16 code;
-};
-
-struct kvm_s390_mchk_info {
- __u64 cr14;
- __u64 mcic;
-};
-
struct kvm_s390_interrupt_info {
struct list_head list;
u64 type;
@@ -246,9 +225,8 @@ struct kvm_s390_float_interrupt {
struct list_head list;
atomic_t active;
int next_rr_cpu;
- unsigned long idle_mask[(KVM_MAX_VCPUS + sizeof(long) - 1)
- / sizeof(long)];
- struct kvm_s390_local_interrupt *local_int[KVM_MAX_VCPUS];
+ unsigned long idle_mask[BITS_TO_LONGS(KVM_MAX_VCPUS)];
+ unsigned int irq_count;
};
@@ -265,6 +243,10 @@ struct kvm_vcpu_arch {
u64 stidp_data;
};
struct gmap *gmap;
+#define KVM_S390_PFAULT_TOKEN_INVALID (-1UL)
+ unsigned long pfault_token;
+ unsigned long pfault_select;
+ unsigned long pfault_compare;
};
struct kvm_vm_stat {
@@ -274,12 +256,36 @@ struct kvm_vm_stat {
struct kvm_arch_memory_slot {
};
+struct s390_map_info {
+ struct list_head list;
+ __u64 guest_addr;
+ __u64 addr;
+ struct page *page;
+};
+
+struct s390_io_adapter {
+ unsigned int id;
+ int isc;
+ bool maskable;
+ bool masked;
+ bool swap;
+ struct rw_semaphore maps_lock;
+ struct list_head maps;
+ atomic_t nr_maps;
+};
+
+#define MAX_S390_IO_ADAPTERS ((MAX_ISC + 1) * 8)
+#define MAX_S390_ADAPTER_MAPS 256
+
struct kvm_arch{
struct sca_block *sca;
debug_info_t *dbf;
struct kvm_s390_float_interrupt float_int;
+ struct kvm_device *flic;
struct gmap *gmap;
int css_support;
+ int use_irqchip;
+ struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS];
};
#define KVM_HVA_ERR_BAD (-1UL)
@@ -290,6 +296,24 @@ static inline bool kvm_is_error_hva(unsigned long addr)
return IS_ERR_VALUE(addr);
}
+#define ASYNC_PF_PER_VCPU 64
+struct kvm_vcpu;
+struct kvm_async_pf;
+struct kvm_arch_async_pf {
+ unsigned long pfault_token;
+};
+
+bool kvm_arch_can_inject_async_page_present(struct kvm_vcpu *vcpu);
+
+void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu,
+ struct kvm_async_pf *work);
+
+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,
+ struct kvm_async_pf *work);
+
extern int sie64a(struct kvm_s390_sie_block *, u64 *);
extern char sie_exit;
#endif
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index 1ab75eaacbd4..50a75d96f939 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -782,6 +782,7 @@ static inline void pgste_set_pte(pte_t *ptep, pte_t entry)
* @table: pointer to the page directory
* @asce: address space control element for gmap page table
* @crst_list: list of all crst tables used in the guest address space
+ * @pfault_enabled: defines if pfaults are applicable for the guest
*/
struct gmap {
struct list_head list;
@@ -790,6 +791,7 @@ struct gmap {
unsigned long asce;
void *private;
struct list_head crst_list;
+ bool pfault_enabled;
};
/**
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index 0a876bc543d3..dc5fc4f90e52 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -79,6 +79,7 @@ struct thread_struct {
unsigned long ksp; /* kernel stack pointer */
mm_segment_t mm_segment;
unsigned long gmap_addr; /* address of last gmap fault. */
+ unsigned int gmap_pfault; /* signal of a pending guest pfault */
struct per_regs per_user; /* User specified PER registers */
struct per_event per_event; /* Cause of the last PER trap */
unsigned long per_flags; /* Flags to control debug behavior */
diff --git a/arch/s390/include/uapi/asm/kvm.h b/arch/s390/include/uapi/asm/kvm.h
index d25da598ec62..c003c6a73b1e 100644
--- a/arch/s390/include/uapi/asm/kvm.h
+++ b/arch/s390/include/uapi/asm/kvm.h
@@ -16,6 +16,44 @@
#define __KVM_S390
+/* Device control API: s390-specific devices */
+#define KVM_DEV_FLIC_GET_ALL_IRQS 1
+#define KVM_DEV_FLIC_ENQUEUE 2
+#define KVM_DEV_FLIC_CLEAR_IRQS 3
+#define KVM_DEV_FLIC_APF_ENABLE 4
+#define KVM_DEV_FLIC_APF_DISABLE_WAIT 5
+#define KVM_DEV_FLIC_ADAPTER_REGISTER 6
+#define KVM_DEV_FLIC_ADAPTER_MODIFY 7
+/*
+ * We can have up to 4*64k pending subchannels + 8 adapter interrupts,
+ * as well as up to ASYNC_PF_PER_VCPU*KVM_MAX_VCPUS pfault done interrupts.
+ * There are also sclp and machine checks. This gives us
+ * sizeof(kvm_s390_irq)*(4*65536+8+64*64+1+1) = 72 * 266250 = 19170000
+ * Lets round up to 8192 pages.
+ */
+#define KVM_S390_MAX_FLOAT_IRQS 266250
+#define KVM_S390_FLIC_MAX_BUFFER 0x2000000
+
+struct kvm_s390_io_adapter {
+ __u32 id;
+ __u8 isc;
+ __u8 maskable;
+ __u8 swap;
+ __u8 pad;
+};
+
+#define KVM_S390_IO_ADAPTER_MASK 1
+#define KVM_S390_IO_ADAPTER_MAP 2
+#define KVM_S390_IO_ADAPTER_UNMAP 3
+
+struct kvm_s390_io_adapter_req {
+ __u32 id;
+ __u8 type;
+ __u8 mask;
+ __u16 pad0;
+ __u64 addr;
+};
+
/* for KVM_GET_REGS and KVM_SET_REGS */
struct kvm_regs {
/* general purpose regs for s390 */
@@ -57,4 +95,9 @@ struct kvm_sync_regs {
#define KVM_REG_S390_EPOCHDIFF (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x2)
#define KVM_REG_S390_CPU_TIMER (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x3)
#define KVM_REG_S390_CLOCK_COMP (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x4)
+#define KVM_REG_S390_PFTOKEN (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x5)
+#define KVM_REG_S390_PFCOMPARE (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x6)
+#define KVM_REG_S390_PFSELECT (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x7)
+#define KVM_REG_S390_PP (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x8)
+#define KVM_REG_S390_GBEA (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x9)
#endif
diff --git a/arch/s390/kernel/cache.c b/arch/s390/kernel/cache.c
index 3a414c0f93ed..c0b03c28d157 100644
--- a/arch/s390/kernel/cache.c
+++ b/arch/s390/kernel/cache.c
@@ -378,9 +378,12 @@ static int __init cache_init(void)
if (!test_facility(34))
return 0;
cache_build_info();
+
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
cache_add_cpu(cpu);
- hotcpu_notifier(cache_hotplug, 0);
+ __hotcpu_notifier(cache_hotplug, 0);
+ cpu_notifier_register_done();
return 0;
}
device_initcall(cache_init);
diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c
index 224db03e9518..54d6493c4a56 100644
--- a/arch/s390/kernel/ftrace.c
+++ b/arch/s390/kernel/ftrace.c
@@ -130,9 +130,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return 0;
}
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- *(unsigned long *) data = 0;
return 0;
}
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
index a770be97db4d..d42b14cc72a4 100644
--- a/arch/s390/kernel/irq.c
+++ b/arch/s390/kernel/irq.c
@@ -85,6 +85,7 @@ static const struct irq_class irqclass_sub_desc[NR_ARCH_IRQS] = {
[IRQIO_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" },
[IRQIO_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" },
[IRQIO_VIR] = {.name = "VIR", .desc = "[I/O] Virtual I/O Devices"},
+ [IRQIO_VAI] = {.name = "VAI", .desc = "[I/O] Virtual I/O Devices AI"},
[NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"},
[CPU_RST] = {.name = "RST", .desc = "[CPU] CPU Restart"},
};
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 8827883310dd..5a640b395bd4 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -1057,19 +1057,24 @@ static DEVICE_ATTR(rescan, 0200, NULL, rescan_store);
static int __init s390_smp_init(void)
{
- int cpu, rc;
+ int cpu, rc = 0;
- hotcpu_notifier(smp_cpu_notify, 0);
#ifdef CONFIG_HOTPLUG_CPU
rc = device_create_file(cpu_subsys.dev_root, &dev_attr_rescan);
if (rc)
return rc;
#endif
+ cpu_notifier_register_begin();
for_each_present_cpu(cpu) {
rc = smp_add_present_cpu(cpu);
if (rc)
- return rc;
+ goto out;
}
- return 0;
+
+ __hotcpu_notifier(smp_cpu_notify, 0);
+
+out:
+ cpu_notifier_register_done();
+ return rc;
}
subsys_initcall(s390_smp_init);
diff --git a/arch/s390/kvm/Kconfig b/arch/s390/kvm/Kconfig
index 70b46eacf8e1..10d529ac9821 100644
--- a/arch/s390/kvm/Kconfig
+++ b/arch/s390/kvm/Kconfig
@@ -23,6 +23,10 @@ config KVM
select ANON_INODES
select HAVE_KVM_CPU_RELAX_INTERCEPT
select HAVE_KVM_EVENTFD
+ select KVM_ASYNC_PF
+ select KVM_ASYNC_PF_SYNC
+ select HAVE_KVM_IRQCHIP
+ select HAVE_KVM_IRQ_ROUTING
---help---
Support hosting paravirtualized guest machines using the SIE
virtualization capability on the mainframe. This should work
diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile
index 40b4c6470f88..d3adb37e93a4 100644
--- a/arch/s390/kvm/Makefile
+++ b/arch/s390/kvm/Makefile
@@ -7,7 +7,7 @@
# as published by the Free Software Foundation.
KVM := ../../../virt/kvm
-common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o
+common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/async_pf.o $(KVM)/irqchip.o
ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
diff --git a/arch/s390/kvm/diag.c b/arch/s390/kvm/diag.c
index 6f9cfa500372..03a05ffb662f 100644
--- a/arch/s390/kvm/diag.c
+++ b/arch/s390/kvm/diag.c
@@ -18,6 +18,7 @@
#include "kvm-s390.h"
#include "trace.h"
#include "trace-s390.h"
+#include "gaccess.h"
static int diag_release_pages(struct kvm_vcpu *vcpu)
{
@@ -47,6 +48,87 @@ static int diag_release_pages(struct kvm_vcpu *vcpu)
return 0;
}
+static int __diag_page_ref_service(struct kvm_vcpu *vcpu)
+{
+ struct prs_parm {
+ u16 code;
+ u16 subcode;
+ u16 parm_len;
+ u16 parm_version;
+ u64 token_addr;
+ u64 select_mask;
+ u64 compare_mask;
+ u64 zarch;
+ };
+ struct prs_parm parm;
+ int rc;
+ u16 rx = (vcpu->arch.sie_block->ipa & 0xf0) >> 4;
+ u16 ry = (vcpu->arch.sie_block->ipa & 0x0f);
+ unsigned long hva_token = KVM_HVA_ERR_BAD;
+
+ if (vcpu->run->s.regs.gprs[rx] & 7)
+ return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+ if (copy_from_guest(vcpu, &parm, vcpu->run->s.regs.gprs[rx], sizeof(parm)))
+ return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+ if (parm.parm_version != 2 || parm.parm_len < 5 || parm.code != 0x258)
+ return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+ switch (parm.subcode) {
+ case 0: /* TOKEN */
+ if (vcpu->arch.pfault_token != KVM_S390_PFAULT_TOKEN_INVALID) {
+ /*
+ * If the pagefault handshake is already activated,
+ * the token must not be changed. We have to return
+ * decimal 8 instead, as mandated in SC24-6084.
+ */
+ vcpu->run->s.regs.gprs[ry] = 8;
+ return 0;
+ }
+
+ if ((parm.compare_mask & parm.select_mask) != parm.compare_mask ||
+ parm.token_addr & 7 || parm.zarch != 0x8000000000000000ULL)
+ return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+ hva_token = gfn_to_hva(vcpu->kvm, gpa_to_gfn(parm.token_addr));
+ if (kvm_is_error_hva(hva_token))
+ return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+
+ vcpu->arch.pfault_token = parm.token_addr;
+ vcpu->arch.pfault_select = parm.select_mask;
+ vcpu->arch.pfault_compare = parm.compare_mask;
+ vcpu->run->s.regs.gprs[ry] = 0;
+ rc = 0;
+ break;
+ case 1: /*
+ * CANCEL
+ * Specification allows to let already pending tokens survive
+ * the cancel, therefore to reduce code complexity, we assume
+ * all outstanding tokens are already pending.
+ */
+ if (parm.token_addr || parm.select_mask ||
+ parm.compare_mask || parm.zarch)
+ return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+ vcpu->run->s.regs.gprs[ry] = 0;
+ /*
+ * If the pfault handling was not established or is already
+ * canceled SC24-6084 requests to return decimal 4.
+ */
+ if (vcpu->arch.pfault_token == KVM_S390_PFAULT_TOKEN_INVALID)
+ vcpu->run->s.regs.gprs[ry] = 4;
+ else
+ vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
+
+ rc = 0;
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ break;
+ }
+
+ return rc;
+}
+
static int __diag_time_slice_end(struct kvm_vcpu *vcpu)
{
VCPU_EVENT(vcpu, 5, "%s", "diag time slice end");
@@ -153,6 +235,8 @@ int kvm_s390_handle_diag(struct kvm_vcpu *vcpu)
return __diag_time_slice_end(vcpu);
case 0x9c:
return __diag_time_slice_end_directed(vcpu);
+ case 0x258:
+ return __diag_page_ref_service(vcpu);
case 0x308:
return __diag_ipl_functions(vcpu);
case 0x500:
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 5f79d2d79ca7..200a8f9390b6 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -1,7 +1,7 @@
/*
* handling kvm guest interrupts
*
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008,2014
*
* 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)
@@ -13,6 +13,7 @@
#include <linux/interrupt.h>
#include <linux/kvm_host.h>
#include <linux/hrtimer.h>
+#include <linux/mmu_context.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <asm/asm-offsets.h>
@@ -31,7 +32,7 @@ static int is_ioint(u64 type)
return ((type & 0xfffe0000u) != 0xfffe0000u);
}
-static int psw_extint_disabled(struct kvm_vcpu *vcpu)
+int psw_extint_disabled(struct kvm_vcpu *vcpu)
{
return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT);
}
@@ -78,11 +79,8 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
return 1;
return 0;
case KVM_S390_INT_SERVICE:
- if (psw_extint_disabled(vcpu))
- return 0;
- if (vcpu->arch.sie_block->gcr[0] & 0x200ul)
- return 1;
- return 0;
+ case KVM_S390_INT_PFAULT_INIT:
+ case KVM_S390_INT_PFAULT_DONE:
case KVM_S390_INT_VIRTIO:
if (psw_extint_disabled(vcpu))
return 0;
@@ -117,14 +115,12 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
static void __set_cpu_idle(struct kvm_vcpu *vcpu)
{
- BUG_ON(vcpu->vcpu_id > KVM_MAX_VCPUS - 1);
atomic_set_mask(CPUSTAT_WAIT, &vcpu->arch.sie_block->cpuflags);
set_bit(vcpu->vcpu_id, vcpu->arch.local_int.float_int->idle_mask);
}
static void __unset_cpu_idle(struct kvm_vcpu *vcpu)
{
- BUG_ON(vcpu->vcpu_id > KVM_MAX_VCPUS - 1);
atomic_clear_mask(CPUSTAT_WAIT, &vcpu->arch.sie_block->cpuflags);
clear_bit(vcpu->vcpu_id, vcpu->arch.local_int.float_int->idle_mask);
}
@@ -150,6 +146,8 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
case KVM_S390_INT_EXTERNAL_CALL:
case KVM_S390_INT_EMERGENCY:
case KVM_S390_INT_SERVICE:
+ case KVM_S390_INT_PFAULT_INIT:
+ case KVM_S390_INT_PFAULT_DONE:
case KVM_S390_INT_VIRTIO:
if (psw_extint_disabled(vcpu))
__set_cpuflag(vcpu, CPUSTAT_EXT_INT);
@@ -223,6 +221,30 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
rc |= put_guest(vcpu, inti->ext.ext_params,
(u32 __user *)__LC_EXT_PARAMS);
break;
+ case KVM_S390_INT_PFAULT_INIT:
+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, 0,
+ inti->ext.ext_params2);
+ rc = put_guest(vcpu, 0x2603, (u16 __user *) __LC_EXT_INT_CODE);
+ rc |= put_guest(vcpu, 0x0600, (u16 __user *) __LC_EXT_CPU_ADDR);
+ rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW,
+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+ rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
+ __LC_EXT_NEW_PSW, sizeof(psw_t));
+ rc |= put_guest(vcpu, inti->ext.ext_params2,
+ (u64 __user *) __LC_EXT_PARAMS2);
+ break;
+ case KVM_S390_INT_PFAULT_DONE:
+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, 0,
+ inti->ext.ext_params2);
+ rc = put_guest(vcpu, 0x2603, (u16 __user *) __LC_EXT_INT_CODE);
+ rc |= put_guest(vcpu, 0x0680, (u16 __user *) __LC_EXT_CPU_ADDR);
+ rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW,
+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+ rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
+ __LC_EXT_NEW_PSW, sizeof(psw_t));
+ rc |= put_guest(vcpu, inti->ext.ext_params2,
+ (u64 __user *) __LC_EXT_PARAMS2);
+ break;
case KVM_S390_INT_VIRTIO:
VCPU_EVENT(vcpu, 4, "interrupt: virtio parm:%x,parm64:%llx",
inti->ext.ext_params, inti->ext.ext_params2);
@@ -357,7 +379,7 @@ static int __try_deliver_ckc_interrupt(struct kvm_vcpu *vcpu)
return 1;
}
-static int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
+int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
{
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
struct kvm_s390_float_interrupt *fi = vcpu->arch.local_int.float_int;
@@ -482,11 +504,26 @@ enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer)
struct kvm_vcpu *vcpu;
vcpu = container_of(timer, struct kvm_vcpu, arch.ckc_timer);
+ vcpu->preempted = true;
tasklet_schedule(&vcpu->arch.tasklet);
return HRTIMER_NORESTART;
}
+void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu)
+{
+ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
+ struct kvm_s390_interrupt_info *n, *inti = NULL;
+
+ spin_lock_bh(&li->lock);
+ list_for_each_entry_safe(inti, n, &li->list, list) {
+ list_del(&inti->list);
+ kfree(inti);
+ }
+ atomic_set(&li->active, 0);
+ spin_unlock_bh(&li->lock);
+}
+
void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
{
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
@@ -528,6 +565,7 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
list_for_each_entry_safe(inti, n, &fi->list, list) {
if (__interrupt_is_deliverable(vcpu, inti)) {
list_del(&inti->list);
+ fi->irq_count--;
deliver = 1;
break;
}
@@ -583,6 +621,7 @@ void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu)
if ((inti->type == KVM_S390_MCHK) &&
__interrupt_is_deliverable(vcpu, inti)) {
list_del(&inti->list);
+ fi->irq_count--;
deliver = 1;
break;
}
@@ -650,8 +689,10 @@ struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
inti = iter;
break;
}
- if (inti)
+ if (inti) {
list_del_init(&inti->list);
+ fi->irq_count--;
+ }
if (list_empty(&fi->list))
atomic_set(&fi->active, 0);
spin_unlock(&fi->lock);
@@ -659,53 +700,101 @@ struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
return inti;
}
-int kvm_s390_inject_vm(struct kvm *kvm,
- struct kvm_s390_interrupt *s390int)
+static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
{
struct kvm_s390_local_interrupt *li;
struct kvm_s390_float_interrupt *fi;
- struct kvm_s390_interrupt_info *inti, *iter;
+ struct kvm_s390_interrupt_info *iter;
+ struct kvm_vcpu *dst_vcpu = NULL;
int sigcpu;
+ int rc = 0;
+
+ mutex_lock(&kvm->lock);
+ fi = &kvm->arch.float_int;
+ spin_lock(&fi->lock);
+ if (fi->irq_count >= KVM_S390_MAX_FLOAT_IRQS) {
+ rc = -EINVAL;
+ goto unlock_fi;
+ }
+ fi->irq_count++;
+ if (!is_ioint(inti->type)) {
+ list_add_tail(&inti->list, &fi->list);
+ } else {
+ u64 isc_bits = int_word_to_isc_bits(inti->io.io_int_word);
+
+ /* Keep I/O interrupts sorted in isc order. */
+ list_for_each_entry(iter, &fi->list, list) {
+ if (!is_ioint(iter->type))
+ continue;
+ if (int_word_to_isc_bits(iter->io.io_int_word)
+ <= isc_bits)
+ continue;
+ break;
+ }
+ list_add_tail(&inti->list, &iter->list);
+ }
+ atomic_set(&fi->active, 1);
+ sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);
+ if (sigcpu == KVM_MAX_VCPUS) {
+ do {
+ sigcpu = fi->next_rr_cpu++;
+ if (sigcpu == KVM_MAX_VCPUS)
+ sigcpu = fi->next_rr_cpu = 0;
+ } while (kvm_get_vcpu(kvm, sigcpu) == NULL);
+ }
+ dst_vcpu = kvm_get_vcpu(kvm, sigcpu);
+ li = &dst_vcpu->arch.local_int;
+ spin_lock_bh(&li->lock);
+ atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
+ if (waitqueue_active(li->wq))
+ wake_up_interruptible(li->wq);
+ kvm_get_vcpu(kvm, sigcpu)->preempted = true;
+ spin_unlock_bh(&li->lock);
+unlock_fi:
+ spin_unlock(&fi->lock);
+ mutex_unlock(&kvm->lock);
+ return rc;
+}
+
+int kvm_s390_inject_vm(struct kvm *kvm,
+ struct kvm_s390_interrupt *s390int)
+{
+ struct kvm_s390_interrupt_info *inti;
inti = kzalloc(sizeof(*inti), GFP_KERNEL);
if (!inti)
return -ENOMEM;
- switch (s390int->type) {
+ inti->type = s390int->type;
+ switch (inti->type) {
case KVM_S390_INT_VIRTIO:
VM_EVENT(kvm, 5, "inject: virtio parm:%x,parm64:%llx",
s390int->parm, s390int->parm64);
- inti->type = s390int->type;
inti->ext.ext_params = s390int->parm;
inti->ext.ext_params2 = s390int->parm64;
break;
case KVM_S390_INT_SERVICE:
VM_EVENT(kvm, 5, "inject: sclp parm:%x", s390int->parm);
- inti->type = s390int->type;
inti->ext.ext_params = s390int->parm;
break;
- case KVM_S390_PROGRAM_INT:
- case KVM_S390_SIGP_STOP:
- case KVM_S390_INT_EXTERNAL_CALL:
- case KVM_S390_INT_EMERGENCY:
- kfree(inti);
- return -EINVAL;
+ case KVM_S390_INT_PFAULT_DONE:
+ inti->type = s390int->type;
+ inti->ext.ext_params2 = s390int->parm64;
+ break;
case KVM_S390_MCHK:
VM_EVENT(kvm, 5, "inject: machine check parm64:%llx",
s390int->parm64);
- inti->type = s390int->type;
inti->mchk.cr14 = s390int->parm; /* upper bits are not used */
inti->mchk.mcic = s390int->parm64;
break;
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
- if (s390int->type & IOINT_AI_MASK)
+ if (inti->type & IOINT_AI_MASK)
VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)");
else
VM_EVENT(kvm, 5, "inject: I/O css %x ss %x schid %04x",
s390int->type & IOINT_CSSID_MASK,
s390int->type & IOINT_SSID_MASK,
s390int->type & IOINT_SCHID_MASK);
- inti->type = s390int->type;
inti->io.subchannel_id = s390int->parm >> 16;
inti->io.subchannel_nr = s390int->parm & 0x0000ffffu;
inti->io.io_int_parm = s390int->parm64 >> 32;
@@ -718,43 +807,7 @@ int kvm_s390_inject_vm(struct kvm *kvm,
trace_kvm_s390_inject_vm(s390int->type, s390int->parm, s390int->parm64,
2);
- mutex_lock(&kvm->lock);
- fi = &kvm->arch.float_int;
- spin_lock(&fi->lock);
- if (!is_ioint(inti->type))
- list_add_tail(&inti->list, &fi->list);
- else {
- u64 isc_bits = int_word_to_isc_bits(inti->io.io_int_word);
-
- /* Keep I/O interrupts sorted in isc order. */
- list_for_each_entry(iter, &fi->list, list) {
- if (!is_ioint(iter->type))
- continue;
- if (int_word_to_isc_bits(iter->io.io_int_word)
- <= isc_bits)
- continue;
- break;
- }
- list_add_tail(&inti->list, &iter->list);
- }
- atomic_set(&fi->active, 1);
- sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);
- if (sigcpu == KVM_MAX_VCPUS) {
- do {
- sigcpu = fi->next_rr_cpu++;
- if (sigcpu == KVM_MAX_VCPUS)
- sigcpu = fi->next_rr_cpu = 0;
- } while (fi->local_int[sigcpu] == NULL);
- }
- li = fi->local_int[sigcpu];
- spin_lock_bh(&li->lock);
- atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
- if (waitqueue_active(li->wq))
- wake_up_interruptible(li->wq);
- spin_unlock_bh(&li->lock);
- spin_unlock(&fi->lock);
- mutex_unlock(&kvm->lock);
- return 0;
+ return __inject_vm(kvm, inti);
}
int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
@@ -814,6 +867,10 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
inti->type = s390int->type;
inti->mchk.mcic = s390int->parm64;
break;
+ case KVM_S390_INT_PFAULT_INIT:
+ inti->type = s390int->type;
+ inti->ext.ext_params2 = s390int->parm64;
+ break;
case KVM_S390_INT_VIRTIO:
case KVM_S390_INT_SERVICE:
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
@@ -837,7 +894,528 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
if (waitqueue_active(&vcpu->wq))
wake_up_interruptible(&vcpu->wq);
+ vcpu->preempted = true;
spin_unlock_bh(&li->lock);
mutex_unlock(&vcpu->kvm->lock);
return 0;
}
+
+static void clear_floating_interrupts(struct kvm *kvm)
+{
+ struct kvm_s390_float_interrupt *fi;
+ struct kvm_s390_interrupt_info *n, *inti = NULL;
+
+ mutex_lock(&kvm->lock);
+ fi = &kvm->arch.float_int;
+ spin_lock(&fi->lock);
+ list_for_each_entry_safe(inti, n, &fi->list, list) {
+ list_del(&inti->list);
+ kfree(inti);
+ }
+ fi->irq_count = 0;
+ atomic_set(&fi->active, 0);
+ spin_unlock(&fi->lock);
+ mutex_unlock(&kvm->lock);
+}
+
+static inline int copy_irq_to_user(struct kvm_s390_interrupt_info *inti,
+ u8 *addr)
+{
+ struct kvm_s390_irq __user *uptr = (struct kvm_s390_irq __user *) addr;
+ struct kvm_s390_irq irq = {0};
+
+ irq.type = inti->type;
+ switch (inti->type) {
+ case KVM_S390_INT_PFAULT_INIT:
+ case KVM_S390_INT_PFAULT_DONE:
+ case KVM_S390_INT_VIRTIO:
+ case KVM_S390_INT_SERVICE:
+ irq.u.ext = inti->ext;
+ break;
+ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
+ irq.u.io = inti->io;
+ break;
+ case KVM_S390_MCHK:
+ irq.u.mchk = inti->mchk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (copy_to_user(uptr, &irq, sizeof(irq)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int get_all_floating_irqs(struct kvm *kvm, __u8 *buf, __u64 len)
+{
+ struct kvm_s390_interrupt_info *inti;
+ struct kvm_s390_float_interrupt *fi;
+ int ret = 0;
+ int n = 0;
+
+ mutex_lock(&kvm->lock);
+ fi = &kvm->arch.float_int;
+ spin_lock(&fi->lock);
+
+ list_for_each_entry(inti, &fi->list, list) {
+ if (len < sizeof(struct kvm_s390_irq)) {
+ /* signal userspace to try again */
+ ret = -ENOMEM;
+ break;
+ }
+ ret = copy_irq_to_user(inti, buf);
+ if (ret)
+ break;
+ buf += sizeof(struct kvm_s390_irq);
+ len -= sizeof(struct kvm_s390_irq);
+ n++;
+ }
+
+ spin_unlock(&fi->lock);
+ mutex_unlock(&kvm->lock);
+
+ return ret < 0 ? ret : n;
+}
+
+static int flic_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
+{
+ int r;
+
+ switch (attr->group) {
+ case KVM_DEV_FLIC_GET_ALL_IRQS:
+ r = get_all_floating_irqs(dev->kvm, (u8 *) attr->addr,
+ attr->attr);
+ break;
+ default:
+ r = -EINVAL;
+ }
+
+ return r;
+}
+
+static inline int copy_irq_from_user(struct kvm_s390_interrupt_info *inti,
+ u64 addr)
+{
+ struct kvm_s390_irq __user *uptr = (struct kvm_s390_irq __user *) addr;
+ void *target = NULL;
+ void __user *source;
+ u64 size;
+
+ if (get_user(inti->type, (u64 __user *)addr))
+ return -EFAULT;
+
+ switch (inti->type) {
+ case KVM_S390_INT_PFAULT_INIT:
+ case KVM_S390_INT_PFAULT_DONE:
+ case KVM_S390_INT_VIRTIO:
+ case KVM_S390_INT_SERVICE:
+ target = (void *) &inti->ext;
+ source = &uptr->u.ext;
+ size = sizeof(inti->ext);
+ break;
+ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
+ target = (void *) &inti->io;
+ source = &uptr->u.io;
+ size = sizeof(inti->io);
+ break;
+ case KVM_S390_MCHK:
+ target = (void *) &inti->mchk;
+ source = &uptr->u.mchk;
+ size = sizeof(inti->mchk);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (copy_from_user(target, source, size))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int enqueue_floating_irq(struct kvm_device *dev,
+ struct kvm_device_attr *attr)
+{
+ struct kvm_s390_interrupt_info *inti = NULL;
+ int r = 0;
+ int len = attr->attr;
+
+ if (len % sizeof(struct kvm_s390_irq) != 0)
+ return -EINVAL;
+ else if (len > KVM_S390_FLIC_MAX_BUFFER)
+ return -EINVAL;
+
+ while (len >= sizeof(struct kvm_s390_irq)) {
+ inti = kzalloc(sizeof(*inti), GFP_KERNEL);
+ if (!inti)
+ return -ENOMEM;
+
+ r = copy_irq_from_user(inti, attr->addr);
+ if (r) {
+ kfree(inti);
+ return r;
+ }
+ r = __inject_vm(dev->kvm, inti);
+ if (r) {
+ kfree(inti);
+ return r;
+ }
+ len -= sizeof(struct kvm_s390_irq);
+ attr->addr += sizeof(struct kvm_s390_irq);
+ }
+
+ return r;
+}
+
+static struct s390_io_adapter *get_io_adapter(struct kvm *kvm, unsigned int id)
+{
+ if (id >= MAX_S390_IO_ADAPTERS)
+ return NULL;
+ return kvm->arch.adapters[id];
+}
+
+static int register_io_adapter(struct kvm_device *dev,
+ struct kvm_device_attr *attr)
+{
+ struct s390_io_adapter *adapter;
+ struct kvm_s390_io_adapter adapter_info;
+
+ if (copy_from_user(&adapter_info,
+ (void __user *)attr->addr, sizeof(adapter_info)))
+ return -EFAULT;
+
+ if ((adapter_info.id >= MAX_S390_IO_ADAPTERS) ||
+ (dev->kvm->arch.adapters[adapter_info.id] != NULL))
+ return -EINVAL;
+
+ adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
+ if (!adapter)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&adapter->maps);
+ init_rwsem(&adapter->maps_lock);
+ atomic_set(&adapter->nr_maps, 0);
+ adapter->id = adapter_info.id;
+ adapter->isc = adapter_info.isc;
+ adapter->maskable = adapter_info.maskable;
+ adapter->masked = false;
+ adapter->swap = adapter_info.swap;
+ dev->kvm->arch.adapters[adapter->id] = adapter;
+
+ return 0;
+}
+
+int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked)
+{
+ int ret;
+ struct s390_io_adapter *adapter = get_io_adapter(kvm, id);
+
+ if (!adapter || !adapter->maskable)
+ return -EINVAL;
+ ret = adapter->masked;
+ adapter->masked = masked;
+ return ret;
+}
+
+static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr)
+{
+ struct s390_io_adapter *adapter = get_io_adapter(kvm, id);
+ struct s390_map_info *map;
+ int ret;
+
+ if (!adapter || !addr)
+ return -EINVAL;
+
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (!map) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ INIT_LIST_HEAD(&map->list);
+ map->guest_addr = addr;
+ map->addr = gmap_translate(addr, kvm->arch.gmap);
+ if (map->addr == -EFAULT) {
+ ret = -EFAULT;
+ goto out;
+ }
+ ret = get_user_pages_fast(map->addr, 1, 1, &map->page);
+ if (ret < 0)
+ goto out;
+ BUG_ON(ret != 1);
+ down_write(&adapter->maps_lock);
+ if (atomic_inc_return(&adapter->nr_maps) < MAX_S390_ADAPTER_MAPS) {
+ list_add_tail(&map->list, &adapter->maps);
+ ret = 0;
+ } else {
+ put_page(map->page);
+ ret = -EINVAL;
+ }
+ up_write(&adapter->maps_lock);
+out:
+ if (ret)
+ kfree(map);
+ return ret;
+}
+
+static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr)
+{
+ struct s390_io_adapter *adapter = get_io_adapter(kvm, id);
+ struct s390_map_info *map, *tmp;
+ int found = 0;
+
+ if (!adapter || !addr)
+ return -EINVAL;
+
+ down_write(&adapter->maps_lock);
+ list_for_each_entry_safe(map, tmp, &adapter->maps, list) {
+ if (map->guest_addr == addr) {
+ found = 1;
+ atomic_dec(&adapter->nr_maps);
+ list_del(&map->list);
+ put_page(map->page);
+ kfree(map);
+ break;
+ }
+ }
+ up_write(&adapter->maps_lock);
+
+ return found ? 0 : -EINVAL;
+}
+
+void kvm_s390_destroy_adapters(struct kvm *kvm)
+{
+ int i;
+ struct s390_map_info *map, *tmp;
+
+ for (i = 0; i < MAX_S390_IO_ADAPTERS; i++) {
+ if (!kvm->arch.adapters[i])
+ continue;
+ list_for_each_entry_safe(map, tmp,
+ &kvm->arch.adapters[i]->maps, list) {
+ list_del(&map->list);
+ put_page(map->page);
+ kfree(map);
+ }
+ kfree(kvm->arch.adapters[i]);
+ }
+}
+
+static int modify_io_adapter(struct kvm_device *dev,
+ struct kvm_device_attr *attr)
+{
+ struct kvm_s390_io_adapter_req req;
+ struct s390_io_adapter *adapter;
+ int ret;
+
+ if (copy_from_user(&req, (void __user *)attr->addr, sizeof(req)))
+ return -EFAULT;
+
+ adapter = get_io_adapter(dev->kvm, req.id);
+ if (!adapter)
+ return -EINVAL;
+ switch (req.type) {
+ case KVM_S390_IO_ADAPTER_MASK:
+ ret = kvm_s390_mask_adapter(dev->kvm, req.id, req.mask);
+ if (ret > 0)
+ ret = 0;
+ break;
+ case KVM_S390_IO_ADAPTER_MAP:
+ ret = kvm_s390_adapter_map(dev->kvm, req.id, req.addr);
+ break;
+ case KVM_S390_IO_ADAPTER_UNMAP:
+ ret = kvm_s390_adapter_unmap(dev->kvm, req.id, req.addr);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
+{
+ int r = 0;
+ unsigned int i;
+ struct kvm_vcpu *vcpu;
+
+ switch (attr->group) {
+ case KVM_DEV_FLIC_ENQUEUE:
+ r = enqueue_floating_irq(dev, attr);
+ break;
+ case KVM_DEV_FLIC_CLEAR_IRQS:
+ r = 0;
+ clear_floating_interrupts(dev->kvm);
+ break;
+ case KVM_DEV_FLIC_APF_ENABLE:
+ dev->kvm->arch.gmap->pfault_enabled = 1;
+ break;
+ case KVM_DEV_FLIC_APF_DISABLE_WAIT:
+ dev->kvm->arch.gmap->pfault_enabled = 0;
+ /*
+ * Make sure no async faults are in transition when
+ * clearing the queues. So we don't need to worry
+ * about late coming workers.
+ */
+ synchronize_srcu(&dev->kvm->srcu);
+ kvm_for_each_vcpu(i, vcpu, dev->kvm)
+ kvm_clear_async_pf_completion_queue(vcpu);
+ break;
+ case KVM_DEV_FLIC_ADAPTER_REGISTER:
+ r = register_io_adapter(dev, attr);
+ break;
+ case KVM_DEV_FLIC_ADAPTER_MODIFY:
+ r = modify_io_adapter(dev, attr);
+ break;
+ default:
+ r = -EINVAL;
+ }
+
+ return r;
+}
+
+static int flic_create(struct kvm_device *dev, u32 type)
+{
+ if (!dev)
+ return -EINVAL;
+ if (dev->kvm->arch.flic)
+ return -EINVAL;
+ dev->kvm->arch.flic = dev;
+ return 0;
+}
+
+static void flic_destroy(struct kvm_device *dev)
+{
+ dev->kvm->arch.flic = NULL;
+ kfree(dev);
+}
+
+/* s390 floating irq controller (flic) */
+struct kvm_device_ops kvm_flic_ops = {
+ .name = "kvm-flic",
+ .get_attr = flic_get_attr,
+ .set_attr = flic_set_attr,
+ .create = flic_create,
+ .destroy = flic_destroy,
+};
+
+static unsigned long get_ind_bit(__u64 addr, unsigned long bit_nr, bool swap)
+{
+ unsigned long bit;
+
+ bit = bit_nr + (addr % PAGE_SIZE) * 8;
+
+ return swap ? (bit ^ (BITS_PER_LONG - 1)) : bit;
+}
+
+static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter,
+ u64 addr)
+{
+ struct s390_map_info *map;
+
+ if (!adapter)
+ return NULL;
+
+ list_for_each_entry(map, &adapter->maps, list) {
+ if (map->guest_addr == addr)
+ return map;
+ }
+ return NULL;
+}
+
+static int adapter_indicators_set(struct kvm *kvm,
+ struct s390_io_adapter *adapter,
+ struct kvm_s390_adapter_int *adapter_int)
+{
+ unsigned long bit;
+ int summary_set, idx;
+ struct s390_map_info *info;
+ void *map;
+
+ info = get_map_info(adapter, adapter_int->ind_addr);
+ if (!info)
+ return -1;
+ map = page_address(info->page);
+ bit = get_ind_bit(info->addr, adapter_int->ind_offset, adapter->swap);
+ set_bit(bit, map);
+ idx = srcu_read_lock(&kvm->srcu);
+ mark_page_dirty(kvm, info->guest_addr >> PAGE_SHIFT);
+ set_page_dirty_lock(info->page);
+ info = get_map_info(adapter, adapter_int->summary_addr);
+ if (!info) {
+ srcu_read_unlock(&kvm->srcu, idx);
+ return -1;
+ }
+ map = page_address(info->page);
+ bit = get_ind_bit(info->addr, adapter_int->summary_offset,
+ adapter->swap);
+ summary_set = test_and_set_bit(bit, map);
+ mark_page_dirty(kvm, info->guest_addr >> PAGE_SHIFT);
+ set_page_dirty_lock(info->page);
+ srcu_read_unlock(&kvm->srcu, idx);
+ return summary_set ? 0 : 1;
+}
+
+/*
+ * < 0 - not injected due to error
+ * = 0 - coalesced, summary indicator already active
+ * > 0 - injected interrupt
+ */
+static int set_adapter_int(struct kvm_kernel_irq_routing_entry *e,
+ struct kvm *kvm, int irq_source_id, int level,
+ bool line_status)
+{
+ int ret;
+ struct s390_io_adapter *adapter;
+
+ /* We're only interested in the 0->1 transition. */
+ if (!level)
+ return 0;
+ adapter = get_io_adapter(kvm, e->adapter.adapter_id);
+ if (!adapter)
+ return -1;
+ down_read(&adapter->maps_lock);
+ ret = adapter_indicators_set(kvm, adapter, &e->adapter);
+ up_read(&adapter->maps_lock);
+ if ((ret > 0) && !adapter->masked) {
+ struct kvm_s390_interrupt s390int = {
+ .type = KVM_S390_INT_IO(1, 0, 0, 0),
+ .parm = 0,
+ .parm64 = (adapter->isc << 27) | 0x80000000,
+ };
+ ret = kvm_s390_inject_vm(kvm, &s390int);
+ if (ret == 0)
+ ret = 1;
+ }
+ return ret;
+}
+
+int kvm_set_routing_entry(struct kvm_irq_routing_table *rt,
+ struct kvm_kernel_irq_routing_entry *e,
+ const struct kvm_irq_routing_entry *ue)
+{
+ int ret;
+
+ switch (ue->type) {
+ case KVM_IRQ_ROUTING_S390_ADAPTER:
+ e->set = set_adapter_int;
+ e->adapter.summary_addr = ue->u.adapter.summary_addr;
+ e->adapter.ind_addr = ue->u.adapter.ind_addr;
+ e->adapter.summary_offset = ue->u.adapter.summary_offset;
+ e->adapter.ind_offset = ue->u.adapter.ind_offset;
+ e->adapter.adapter_id = ue->u.adapter.adapter_id;
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
+ int irq_source_id, int level, bool line_status)
+{
+ return -EINVAL;
+}
diff --git a/arch/s390/kvm/irq.h b/arch/s390/kvm/irq.h
new file mode 100644
index 000000000000..d98e4159643d
--- /dev/null
+++ b/arch/s390/kvm/irq.h
@@ -0,0 +1,22 @@
+/*
+ * s390 irqchip routines
+ *
+ * Copyright IBM Corp. 2014
+ *
+ * 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.
+ *
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ */
+#ifndef __KVM_IRQ_H
+#define __KVM_IRQ_H
+
+#include <linux/kvm_host.h>
+
+static inline int irqchip_in_kernel(struct kvm *kvm)
+{
+ return 1;
+}
+
+#endif
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 10b5db3c9bc4..b3ecb8f5b6ce 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -153,11 +153,14 @@ int kvm_dev_ioctl_check_extension(long ext)
#ifdef CONFIG_KVM_S390_UCONTROL
case KVM_CAP_S390_UCONTROL:
#endif
+ case KVM_CAP_ASYNC_PF:
case KVM_CAP_SYNC_REGS:
case KVM_CAP_ONE_REG:
case KVM_CAP_ENABLE_CAP:
case KVM_CAP_S390_CSS_SUPPORT:
case KVM_CAP_IOEVENTFD:
+ case KVM_CAP_DEVICE_CTRL:
+ case KVM_CAP_ENABLE_CAP_VM:
r = 1;
break;
case KVM_CAP_NR_VCPUS:
@@ -186,6 +189,25 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
return 0;
}
+static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
+{
+ int r;
+
+ if (cap->flags)
+ return -EINVAL;
+
+ switch (cap->cap) {
+ case KVM_CAP_S390_IRQCHIP:
+ kvm->arch.use_irqchip = 1;
+ r = 0;
+ break;
+ default:
+ r = -EINVAL;
+ break;
+ }
+ return r;
+}
+
long kvm_arch_vm_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
@@ -203,6 +225,26 @@ long kvm_arch_vm_ioctl(struct file *filp,
r = kvm_s390_inject_vm(kvm, &s390int);
break;
}
+ case KVM_ENABLE_CAP: {
+ struct kvm_enable_cap cap;
+ r = -EFAULT;
+ if (copy_from_user(&cap, argp, sizeof(cap)))
+ break;
+ r = kvm_vm_ioctl_enable_cap(kvm, &cap);
+ break;
+ }
+ case KVM_CREATE_IRQCHIP: {
+ struct kvm_irq_routing_entry routing;
+
+ r = -EINVAL;
+ if (kvm->arch.use_irqchip) {
+ /* Set up dummy routing. */
+ memset(&routing, 0, sizeof(routing));
+ kvm_set_irq_routing(kvm, &routing, 0, 0);
+ r = 0;
+ }
+ break;
+ }
default:
r = -ENOTTY;
}
@@ -214,6 +256,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
{
int rc;
char debug_name[16];
+ static unsigned long sca_offset;
rc = -EINVAL;
#ifdef CONFIG_KVM_S390_UCONTROL
@@ -235,6 +278,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
kvm->arch.sca = (struct sca_block *) get_zeroed_page(GFP_KERNEL);
if (!kvm->arch.sca)
goto out_err;
+ spin_lock(&kvm_lock);
+ sca_offset = (sca_offset + 16) & 0x7f0;
+ kvm->arch.sca = (struct sca_block *) ((char *) kvm->arch.sca + sca_offset);
+ spin_unlock(&kvm_lock);
sprintf(debug_name, "kvm-%u", current->pid);
@@ -255,9 +302,11 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
if (!kvm->arch.gmap)
goto out_nogmap;
kvm->arch.gmap->private = kvm;
+ kvm->arch.gmap->pfault_enabled = 0;
}
kvm->arch.css_support = 0;
+ kvm->arch.use_irqchip = 0;
return 0;
out_nogmap:
@@ -272,6 +321,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
{
VCPU_EVENT(vcpu, 3, "%s", "free cpu");
trace_kvm_s390_destroy_vcpu(vcpu->vcpu_id);
+ 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);
@@ -320,11 +370,14 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
debug_unregister(kvm->arch.dbf);
if (!kvm_is_ucontrol(kvm))
gmap_free(kvm->arch.gmap);
+ kvm_s390_destroy_adapters(kvm);
}
/* Section: vcpu related */
int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
{
+ vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
+ kvm_clear_async_pf_completion_queue(vcpu);
if (kvm_is_ucontrol(vcpu->kvm)) {
vcpu->arch.gmap = gmap_alloc(current->mm);
if (!vcpu->arch.gmap)
@@ -385,7 +438,11 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu)
vcpu->arch.guest_fpregs.fpc = 0;
asm volatile("lfpc %0" : : "Q" (vcpu->arch.guest_fpregs.fpc));
vcpu->arch.sie_block->gbea = 1;
+ vcpu->arch.sie_block->pp = 0;
+ vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
+ kvm_clear_async_pf_completion_queue(vcpu);
atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
+ kvm_s390_clear_local_irqs(vcpu);
}
int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
@@ -466,11 +523,8 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
spin_lock_init(&vcpu->arch.local_int.lock);
INIT_LIST_HEAD(&vcpu->arch.local_int.list);
vcpu->arch.local_int.float_int = &kvm->arch.float_int;
- spin_lock(&kvm->arch.float_int.lock);
- kvm->arch.float_int.local_int[id] = &vcpu->arch.local_int;
vcpu->arch.local_int.wq = &vcpu->wq;
vcpu->arch.local_int.cpuflags = &vcpu->arch.sie_block->cpuflags;
- spin_unlock(&kvm->arch.float_int.lock);
rc = kvm_vcpu_init(vcpu, kvm, id);
if (rc)
@@ -490,9 +544,7 @@ out:
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
{
- /* kvm common code refers to this, but never calls it */
- BUG();
- return 0;
+ return kvm_cpu_has_interrupt(vcpu);
}
void s390_vcpu_block(struct kvm_vcpu *vcpu)
@@ -568,6 +620,26 @@ static int kvm_arch_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu,
r = put_user(vcpu->arch.sie_block->ckc,
(u64 __user *)reg->addr);
break;
+ case KVM_REG_S390_PFTOKEN:
+ r = put_user(vcpu->arch.pfault_token,
+ (u64 __user *)reg->addr);
+ break;
+ case KVM_REG_S390_PFCOMPARE:
+ r = put_user(vcpu->arch.pfault_compare,
+ (u64 __user *)reg->addr);
+ break;
+ case KVM_REG_S390_PFSELECT:
+ r = put_user(vcpu->arch.pfault_select,
+ (u64 __user *)reg->addr);
+ break;
+ case KVM_REG_S390_PP:
+ r = put_user(vcpu->arch.sie_block->pp,
+ (u64 __user *)reg->addr);
+ break;
+ case KVM_REG_S390_GBEA:
+ r = put_user(vcpu->arch.sie_block->gbea,
+ (u64 __user *)reg->addr);
+ break;
default:
break;
}
@@ -597,6 +669,26 @@ static int kvm_arch_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu,
r = get_user(vcpu->arch.sie_block->ckc,
(u64 __user *)reg->addr);
break;
+ case KVM_REG_S390_PFTOKEN:
+ r = get_user(vcpu->arch.pfault_token,
+ (u64 __user *)reg->addr);
+ break;
+ case KVM_REG_S390_PFCOMPARE:
+ r = get_user(vcpu->arch.pfault_compare,
+ (u64 __user *)reg->addr);
+ break;
+ case KVM_REG_S390_PFSELECT:
+ r = get_user(vcpu->arch.pfault_select,
+ (u64 __user *)reg->addr);
+ break;
+ case KVM_REG_S390_PP:
+ r = get_user(vcpu->arch.sie_block->pp,
+ (u64 __user *)reg->addr);
+ break;
+ case KVM_REG_S390_GBEA:
+ r = get_user(vcpu->arch.sie_block->gbea,
+ (u64 __user *)reg->addr);
+ break;
default:
break;
}
@@ -715,10 +807,100 @@ static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu)
return 0;
}
+static long kvm_arch_fault_in_sync(struct kvm_vcpu *vcpu)
+{
+ long rc;
+ hva_t fault = gmap_fault(current->thread.gmap_addr, vcpu->arch.gmap);
+ struct mm_struct *mm = current->mm;
+ down_read(&mm->mmap_sem);
+ rc = get_user_pages(current, mm, fault, 1, 1, 0, NULL, NULL);
+ up_read(&mm->mmap_sem);
+ return rc;
+}
+
+static void __kvm_inject_pfault_token(struct kvm_vcpu *vcpu, bool start_token,
+ unsigned long token)
+{
+ struct kvm_s390_interrupt inti;
+ inti.parm64 = token;
+
+ if (start_token) {
+ inti.type = KVM_S390_INT_PFAULT_INIT;
+ WARN_ON_ONCE(kvm_s390_inject_vcpu(vcpu, &inti));
+ } else {
+ inti.type = KVM_S390_INT_PFAULT_DONE;
+ WARN_ON_ONCE(kvm_s390_inject_vm(vcpu->kvm, &inti));
+ }
+}
+
+void kvm_arch_async_page_not_present(struct kvm_vcpu *vcpu,
+ struct kvm_async_pf *work)
+{
+ trace_kvm_s390_pfault_init(vcpu, work->arch.pfault_token);
+ __kvm_inject_pfault_token(vcpu, true, work->arch.pfault_token);
+}
+
+void kvm_arch_async_page_present(struct kvm_vcpu *vcpu,
+ struct kvm_async_pf *work)
+{
+ trace_kvm_s390_pfault_done(vcpu, work->arch.pfault_token);
+ __kvm_inject_pfault_token(vcpu, false, work->arch.pfault_token);
+}
+
+void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu,
+ struct kvm_async_pf *work)
+{
+ /* s390 will always inject the page directly */
+}
+
+bool kvm_arch_can_inject_async_page_present(struct kvm_vcpu *vcpu)
+{
+ /*
+ * s390 will always inject the page directly,
+ * but we still want check_async_completion to cleanup
+ */
+ return true;
+}
+
+static int kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu)
+{
+ hva_t hva;
+ struct kvm_arch_async_pf arch;
+ int rc;
+
+ if (vcpu->arch.pfault_token == KVM_S390_PFAULT_TOKEN_INVALID)
+ return 0;
+ if ((vcpu->arch.sie_block->gpsw.mask & vcpu->arch.pfault_select) !=
+ vcpu->arch.pfault_compare)
+ return 0;
+ if (psw_extint_disabled(vcpu))
+ return 0;
+ if (kvm_cpu_has_interrupt(vcpu))
+ return 0;
+ if (!(vcpu->arch.sie_block->gcr[0] & 0x200ul))
+ return 0;
+ if (!vcpu->arch.gmap->pfault_enabled)
+ return 0;
+
+ hva = gmap_fault(current->thread.gmap_addr, vcpu->arch.gmap);
+ if (copy_from_guest(vcpu, &arch.pfault_token, vcpu->arch.pfault_token, 8))
+ return 0;
+
+ rc = kvm_setup_async_pf(vcpu, current->thread.gmap_addr, hva, &arch);
+ return rc;
+}
+
static int vcpu_pre_run(struct kvm_vcpu *vcpu)
{
int rc, cpuflags;
+ /*
+ * On s390 notifications for arriving pages will be delivered directly
+ * to the guest but the house keeping for completed pfaults is
+ * handled outside the worker.
+ */
+ kvm_check_async_pf_completion(vcpu);
+
memcpy(&vcpu->arch.sie_block->gg14, &vcpu->run->s.regs.gprs[14], 16);
if (need_resched())
@@ -744,7 +926,7 @@ static int vcpu_pre_run(struct kvm_vcpu *vcpu)
static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
{
- int rc;
+ int rc = -1;
VCPU_EVENT(vcpu, 6, "exit sie icptcode %d",
vcpu->arch.sie_block->icptcode);
@@ -758,7 +940,16 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
current->thread.gmap_addr;
vcpu->run->s390_ucontrol.pgm_code = 0x10;
rc = -EREMOTE;
- } else {
+
+ } 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) ||
+ (kvm_arch_fault_in_sync(vcpu) >= 0))
+ rc = 0;
+ }
+
+ if (rc == -1) {
VCPU_EVENT(vcpu, 3, "%s", "fault in sie instruction");
trace_kvm_s390_sie_fault(vcpu);
rc = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
@@ -768,7 +959,8 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
if (rc == 0) {
if (kvm_is_ucontrol(vcpu->kvm))
- rc = -EOPNOTSUPP;
+ /* Don't exit for host interrupts. */
+ rc = vcpu->arch.sie_block->icptcode ? -EOPNOTSUPP : 0;
else
rc = kvm_handle_sie_intercept(vcpu);
}
@@ -831,8 +1023,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
atomic_clear_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
- BUG_ON(vcpu->kvm->arch.float_int.local_int[vcpu->vcpu_id] == NULL);
-
switch (kvm_run->exit_reason) {
case KVM_EXIT_S390_SIEIC:
case KVM_EXIT_UNKNOWN:
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index 564514f410f4..3c1e2274d9ea 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -129,6 +129,7 @@ enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer);
void kvm_s390_tasklet(unsigned long parm);
void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu);
void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu);
+void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu);
int __must_check kvm_s390_inject_vm(struct kvm *kvm,
struct kvm_s390_interrupt *s390int);
int __must_check kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
@@ -136,6 +137,7 @@ int __must_check kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
int __must_check kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code);
struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
u64 cr6, u64 schid);
+int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked);
/* implemented in priv.c */
int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
@@ -161,4 +163,9 @@ bool kvm_enabled_cmma(void);
/* implemented in diag.c */
int kvm_s390_handle_diag(struct kvm_vcpu *vcpu);
+/* implemented in interrupt.c */
+int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu);
+int psw_extint_disabled(struct kvm_vcpu *vcpu);
+void kvm_s390_destroy_adapters(struct kvm *kvm);
+
#endif
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index aacb6b129914..476e9e218f43 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -396,15 +396,10 @@ static int handle_stidp(struct kvm_vcpu *vcpu)
static void handle_stsi_3_2_2(struct kvm_vcpu *vcpu, struct sysinfo_3_2_2 *mem)
{
- struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
int cpus = 0;
int n;
- spin_lock(&fi->lock);
- for (n = 0; n < KVM_MAX_VCPUS; n++)
- if (fi->local_int[n])
- cpus++;
- spin_unlock(&fi->lock);
+ cpus = atomic_read(&vcpu->kvm->online_vcpus);
/* deal with other level 3 hypervisors */
if (stsi(mem, 3, 2, 2))
diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c
index 87c2b3a3bd3e..26caeb530a78 100644
--- a/arch/s390/kvm/sigp.c
+++ b/arch/s390/kvm/sigp.c
@@ -23,29 +23,30 @@
static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr,
u64 *reg)
{
- struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
+ struct kvm_s390_local_interrupt *li;
+ struct kvm_vcpu *dst_vcpu = NULL;
+ int cpuflags;
int rc;
if (cpu_addr >= KVM_MAX_VCPUS)
return SIGP_CC_NOT_OPERATIONAL;
- spin_lock(&fi->lock);
- if (fi->local_int[cpu_addr] == NULL)
- rc = SIGP_CC_NOT_OPERATIONAL;
- else if (!(atomic_read(fi->local_int[cpu_addr]->cpuflags)
- & (CPUSTAT_ECALL_PEND | CPUSTAT_STOPPED)))
+ dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+ if (!dst_vcpu)
+ return SIGP_CC_NOT_OPERATIONAL;
+ li = &dst_vcpu->arch.local_int;
+
+ cpuflags = atomic_read(li->cpuflags);
+ if (!(cpuflags & (CPUSTAT_ECALL_PEND | CPUSTAT_STOPPED)))
rc = SIGP_CC_ORDER_CODE_ACCEPTED;
else {
*reg &= 0xffffffff00000000UL;
- if (atomic_read(fi->local_int[cpu_addr]->cpuflags)
- & CPUSTAT_ECALL_PEND)
+ if (cpuflags & CPUSTAT_ECALL_PEND)
*reg |= SIGP_STATUS_EXT_CALL_PENDING;
- if (atomic_read(fi->local_int[cpu_addr]->cpuflags)
- & CPUSTAT_STOPPED)
+ if (cpuflags & CPUSTAT_STOPPED)
*reg |= SIGP_STATUS_STOPPED;
rc = SIGP_CC_STATUS_STORED;
}
- spin_unlock(&fi->lock);
VCPU_EVENT(vcpu, 4, "sensed status of cpu %x rc %x", cpu_addr, rc);
return rc;
@@ -53,12 +54,13 @@ static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr,
static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr)
{
- struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
struct kvm_s390_local_interrupt *li;
struct kvm_s390_interrupt_info *inti;
- int rc;
+ struct kvm_vcpu *dst_vcpu = NULL;
- if (cpu_addr >= KVM_MAX_VCPUS)
+ if (cpu_addr < KVM_MAX_VCPUS)
+ dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+ if (!dst_vcpu)
return SIGP_CC_NOT_OPERATIONAL;
inti = kzalloc(sizeof(*inti), GFP_KERNEL);
@@ -68,13 +70,7 @@ static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr)
inti->type = KVM_S390_INT_EMERGENCY;
inti->emerg.code = vcpu->vcpu_id;
- spin_lock(&fi->lock);
- li = fi->local_int[cpu_addr];
- if (li == NULL) {
- rc = SIGP_CC_NOT_OPERATIONAL;
- kfree(inti);
- goto unlock;
- }
+ li = &dst_vcpu->arch.local_int;
spin_lock_bh(&li->lock);
list_add_tail(&inti->list, &li->list);
atomic_set(&li->active, 1);
@@ -82,11 +78,9 @@ static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr)
if (waitqueue_active(li->wq))
wake_up_interruptible(li->wq);
spin_unlock_bh(&li->lock);
- rc = SIGP_CC_ORDER_CODE_ACCEPTED;
VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr);
-unlock:
- spin_unlock(&fi->lock);
- return rc;
+
+ return SIGP_CC_ORDER_CODE_ACCEPTED;
}
static int __sigp_conditional_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr,
@@ -122,12 +116,13 @@ static int __sigp_conditional_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr,
static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr)
{
- struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
struct kvm_s390_local_interrupt *li;
struct kvm_s390_interrupt_info *inti;
- int rc;
+ struct kvm_vcpu *dst_vcpu = NULL;
- if (cpu_addr >= KVM_MAX_VCPUS)
+ if (cpu_addr < KVM_MAX_VCPUS)
+ dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+ if (!dst_vcpu)
return SIGP_CC_NOT_OPERATIONAL;
inti = kzalloc(sizeof(*inti), GFP_KERNEL);
@@ -137,13 +132,7 @@ static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr)
inti->type = KVM_S390_INT_EXTERNAL_CALL;
inti->extcall.code = vcpu->vcpu_id;
- spin_lock(&fi->lock);
- li = fi->local_int[cpu_addr];
- if (li == NULL) {
- rc = SIGP_CC_NOT_OPERATIONAL;
- kfree(inti);
- goto unlock;
- }
+ li = &dst_vcpu->arch.local_int;
spin_lock_bh(&li->lock);
list_add_tail(&inti->list, &li->list);
atomic_set(&li->active, 1);
@@ -151,11 +140,9 @@ static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr)
if (waitqueue_active(li->wq))
wake_up_interruptible(li->wq);
spin_unlock_bh(&li->lock);
- rc = SIGP_CC_ORDER_CODE_ACCEPTED;
VCPU_EVENT(vcpu, 4, "sent sigp ext call to cpu %x", cpu_addr);
-unlock:
- spin_unlock(&fi->lock);
- return rc;
+
+ return SIGP_CC_ORDER_CODE_ACCEPTED;
}
static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action)
@@ -189,31 +176,26 @@ out:
static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action)
{
- struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
struct kvm_s390_local_interrupt *li;
+ struct kvm_vcpu *dst_vcpu = NULL;
int rc;
if (cpu_addr >= KVM_MAX_VCPUS)
return SIGP_CC_NOT_OPERATIONAL;
- spin_lock(&fi->lock);
- li = fi->local_int[cpu_addr];
- if (li == NULL) {
- rc = SIGP_CC_NOT_OPERATIONAL;
- goto unlock;
- }
+ dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+ if (!dst_vcpu)
+ return SIGP_CC_NOT_OPERATIONAL;
+ li = &dst_vcpu->arch.local_int;
rc = __inject_sigp_stop(li, action);
-unlock:
- spin_unlock(&fi->lock);
VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr);
if ((action & ACTION_STORE_ON_STOP) != 0 && rc == -ESHUTDOWN) {
/* If the CPU has already been stopped, we still have
* to save the status when doing stop-and-store. This
* has to be done after unlocking all spinlocks. */
- struct kvm_vcpu *dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
rc = kvm_s390_store_status_unloaded(dst_vcpu,
KVM_S390_STORE_STATUS_NOADDR);
}
@@ -224,6 +206,8 @@ unlock:
static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter)
{
int rc;
+ unsigned int i;
+ struct kvm_vcpu *v;
switch (parameter & 0xff) {
case 0:
@@ -231,6 +215,11 @@ static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter)
break;
case 1:
case 2:
+ kvm_for_each_vcpu(i, v, vcpu->kvm) {
+ v->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
+ kvm_clear_async_pf_completion_queue(v);
+ }
+
rc = SIGP_CC_ORDER_CODE_ACCEPTED;
break;
default:
@@ -242,12 +231,18 @@ static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter)
static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
u64 *reg)
{
- struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
- struct kvm_s390_local_interrupt *li = NULL;
+ struct kvm_s390_local_interrupt *li;
+ struct kvm_vcpu *dst_vcpu = NULL;
struct kvm_s390_interrupt_info *inti;
int rc;
u8 tmp;
+ if (cpu_addr < KVM_MAX_VCPUS)
+ dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+ if (!dst_vcpu)
+ return SIGP_CC_NOT_OPERATIONAL;
+ li = &dst_vcpu->arch.local_int;
+
/* make sure that the new value is valid memory */
address = address & 0x7fffe000u;
if (copy_from_guest_absolute(vcpu, &tmp, address, 1) ||
@@ -261,18 +256,6 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
if (!inti)
return SIGP_CC_BUSY;
- spin_lock(&fi->lock);
- if (cpu_addr < KVM_MAX_VCPUS)
- li = fi->local_int[cpu_addr];
-
- if (li == NULL) {
- *reg &= 0xffffffff00000000UL;
- *reg |= SIGP_STATUS_INCORRECT_STATE;
- rc = SIGP_CC_STATUS_STORED;
- kfree(inti);
- goto out_fi;
- }
-
spin_lock_bh(&li->lock);
/* cpu must be in stopped state */
if (!(atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) {
@@ -295,8 +278,6 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
VCPU_EVENT(vcpu, 4, "set prefix of cpu %02x to %x", cpu_addr, address);
out_li:
spin_unlock_bh(&li->lock);
-out_fi:
- spin_unlock(&fi->lock);
return rc;
}
@@ -334,28 +315,26 @@ static int __sigp_store_status_at_addr(struct kvm_vcpu *vcpu, u16 cpu_id,
static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr,
u64 *reg)
{
+ struct kvm_s390_local_interrupt *li;
+ struct kvm_vcpu *dst_vcpu = NULL;
int rc;
- struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
if (cpu_addr >= KVM_MAX_VCPUS)
return SIGP_CC_NOT_OPERATIONAL;
- spin_lock(&fi->lock);
- if (fi->local_int[cpu_addr] == NULL)
- rc = SIGP_CC_NOT_OPERATIONAL;
- else {
- if (atomic_read(fi->local_int[cpu_addr]->cpuflags)
- & CPUSTAT_RUNNING) {
- /* running */
- rc = SIGP_CC_ORDER_CODE_ACCEPTED;
- } else {
- /* not running */
- *reg &= 0xffffffff00000000UL;
- *reg |= SIGP_STATUS_NOT_RUNNING;
- rc = SIGP_CC_STATUS_STORED;
- }
+ dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+ if (!dst_vcpu)
+ return SIGP_CC_NOT_OPERATIONAL;
+ li = &dst_vcpu->arch.local_int;
+ if (atomic_read(li->cpuflags) & CPUSTAT_RUNNING) {
+ /* running */
+ rc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ } else {
+ /* not running */
+ *reg &= 0xffffffff00000000UL;
+ *reg |= SIGP_STATUS_NOT_RUNNING;
+ rc = SIGP_CC_STATUS_STORED;
}
- spin_unlock(&fi->lock);
VCPU_EVENT(vcpu, 4, "sensed running status of cpu %x rc %x", cpu_addr,
rc);
@@ -366,26 +345,22 @@ static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr,
/* Test whether the destination CPU is available and not busy */
static int sigp_check_callable(struct kvm_vcpu *vcpu, u16 cpu_addr)
{
- struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
struct kvm_s390_local_interrupt *li;
int rc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ struct kvm_vcpu *dst_vcpu = NULL;
if (cpu_addr >= KVM_MAX_VCPUS)
return SIGP_CC_NOT_OPERATIONAL;
- spin_lock(&fi->lock);
- li = fi->local_int[cpu_addr];
- if (li == NULL) {
- rc = SIGP_CC_NOT_OPERATIONAL;
- goto out;
- }
-
+ dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+ if (!dst_vcpu)
+ return SIGP_CC_NOT_OPERATIONAL;
+ li = &dst_vcpu->arch.local_int;
spin_lock_bh(&li->lock);
if (li->action_bits & ACTION_STOP_ON_STOP)
rc = SIGP_CC_BUSY;
spin_unlock_bh(&li->lock);
-out:
- spin_unlock(&fi->lock);
+
return rc;
}
diff --git a/arch/s390/kvm/trace.h b/arch/s390/kvm/trace.h
index 3db76b2daed7..e8e7213d4cc5 100644
--- a/arch/s390/kvm/trace.h
+++ b/arch/s390/kvm/trace.h
@@ -30,6 +30,52 @@
TP_printk("%02d[%016lx-%016lx]: " p_str, __entry->id, \
__entry->pswmask, __entry->pswaddr, p_args)
+TRACE_EVENT(kvm_s390_major_guest_pfault,
+ TP_PROTO(VCPU_PROTO_COMMON),
+ TP_ARGS(VCPU_ARGS_COMMON),
+
+ TP_STRUCT__entry(
+ VCPU_FIELD_COMMON
+ ),
+
+ TP_fast_assign(
+ VCPU_ASSIGN_COMMON
+ ),
+ VCPU_TP_PRINTK("%s", "major fault, maybe applicable for pfault")
+ );
+
+TRACE_EVENT(kvm_s390_pfault_init,
+ TP_PROTO(VCPU_PROTO_COMMON, long pfault_token),
+ TP_ARGS(VCPU_ARGS_COMMON, pfault_token),
+
+ TP_STRUCT__entry(
+ VCPU_FIELD_COMMON
+ __field(long, pfault_token)
+ ),
+
+ TP_fast_assign(
+ VCPU_ASSIGN_COMMON
+ __entry->pfault_token = pfault_token;
+ ),
+ VCPU_TP_PRINTK("init pfault token %ld", __entry->pfault_token)
+ );
+
+TRACE_EVENT(kvm_s390_pfault_done,
+ TP_PROTO(VCPU_PROTO_COMMON, long pfault_token),
+ TP_ARGS(VCPU_ARGS_COMMON, pfault_token),
+
+ TP_STRUCT__entry(
+ VCPU_FIELD_COMMON
+ __field(long, pfault_token)
+ ),
+
+ TP_fast_assign(
+ VCPU_ASSIGN_COMMON
+ __entry->pfault_token = pfault_token;
+ ),
+ VCPU_TP_PRINTK("done pfault token %ld", __entry->pfault_token)
+ );
+
/*
* Tracepoints for SIE entry and exit.
*/
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
index d95265b2719f..88cef505453b 100644
--- a/arch/s390/mm/fault.c
+++ b/arch/s390/mm/fault.c
@@ -50,6 +50,7 @@
#define VM_FAULT_BADMAP 0x020000
#define VM_FAULT_BADACCESS 0x040000
#define VM_FAULT_SIGNAL 0x080000
+#define VM_FAULT_PFAULT 0x100000
static unsigned long store_indication __read_mostly;
@@ -227,6 +228,7 @@ static noinline void do_fault_error(struct pt_regs *regs, int fault)
return;
}
case VM_FAULT_BADCONTEXT:
+ case VM_FAULT_PFAULT:
do_no_context(regs);
break;
case VM_FAULT_SIGNAL:
@@ -264,6 +266,9 @@ static noinline void do_fault_error(struct pt_regs *regs, int fault)
*/
static inline int do_exception(struct pt_regs *regs, int access)
{
+#ifdef CONFIG_PGSTE
+ struct gmap *gmap;
+#endif
struct task_struct *tsk;
struct mm_struct *mm;
struct vm_area_struct *vma;
@@ -304,9 +309,10 @@ static inline int do_exception(struct pt_regs *regs, int access)
down_read(&mm->mmap_sem);
#ifdef CONFIG_PGSTE
- if ((current->flags & PF_VCPU) && S390_lowcore.gmap) {
- address = __gmap_fault(address,
- (struct gmap *) S390_lowcore.gmap);
+ gmap = (struct gmap *)
+ ((current->flags & PF_VCPU) ? S390_lowcore.gmap : 0);
+ if (gmap) {
+ address = __gmap_fault(address, gmap);
if (address == -EFAULT) {
fault = VM_FAULT_BADMAP;
goto out_up;
@@ -315,6 +321,8 @@ static inline int do_exception(struct pt_regs *regs, int access)
fault = VM_FAULT_OOM;
goto out_up;
}
+ if (gmap->pfault_enabled)
+ flags |= FAULT_FLAG_RETRY_NOWAIT;
}
#endif
@@ -371,9 +379,19 @@ retry:
regs, address);
}
if (fault & VM_FAULT_RETRY) {
+#ifdef CONFIG_PGSTE
+ if (gmap && (flags & FAULT_FLAG_RETRY_NOWAIT)) {
+ /* FAULT_FLAG_RETRY_NOWAIT has been set,
+ * mmap_sem has not been released */
+ current->thread.gmap_pfault = 1;
+ fault = VM_FAULT_PFAULT;
+ goto out_up;
+ }
+#endif
/* Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk
* of starvation. */
- flags &= ~FAULT_FLAG_ALLOW_RETRY;
+ flags &= ~(FAULT_FLAG_ALLOW_RETRY |
+ FAULT_FLAG_RETRY_NOWAIT);
flags |= FAULT_FLAG_TRIED;
down_read(&mm->mmap_sem);
goto retry;
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index 708d60e40066..9c36dc398f90 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -737,10 +737,10 @@ call_fn: /* lg %r1,<d(function)>(%r13) */
/* icm %r5,3,<d(type)>(%r1) */
EMIT4_DISP(0xbf531000, offsetof(struct net_device, type));
break;
- case BPF_S_ANC_RXHASH: /* A = skb->rxhash */
- BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, rxhash) != 4);
- /* l %r5,<d(rxhash)>(%r2) */
- EMIT4_DISP(0x58502000, offsetof(struct sk_buff, rxhash));
+ case BPF_S_ANC_RXHASH: /* A = skb->hash */
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
+ /* l %r5,<d(hash)>(%r2) */
+ EMIT4_DISP(0x58502000, offsetof(struct sk_buff, hash));
break;
case BPF_S_ANC_VLAN_TAG:
case BPF_S_ANC_VLAN_TAG_PRESENT:
@@ -877,6 +877,7 @@ void bpf_jit_compile(struct sk_filter *fp)
if (jit.start) {
set_memory_ro((unsigned long)header, header->pages);
fp->bpf_func = (void *) jit.start;
+ fp->jited = 1;
}
out:
kfree(addrs);
@@ -887,10 +888,12 @@ void bpf_jit_free(struct sk_filter *fp)
unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
struct bpf_binary_header *header = (void *)addr;
- if (fp->bpf_func == sk_run_filter)
+ if (!fp->jited)
goto free_filter;
+
set_memory_rw(addr, header->pages);
module_free(NULL, header);
+
free_filter:
kfree(fp);
}
diff --git a/arch/score/Kconfig b/arch/score/Kconfig
index c75d06aa27c3..4ac8cae5727c 100644
--- a/arch/score/Kconfig
+++ b/arch/score/Kconfig
@@ -22,27 +22,21 @@ choice
config ARCH_SCORE7
bool "SCORE7 processor"
select SYS_SUPPORTS_32BIT_KERNEL
- select CPU_SCORE7
select GENERIC_HAS_IOMAP
config MACH_SPCT6600
bool "SPCT6600 series based machines"
select SYS_SUPPORTS_32BIT_KERNEL
- select CPU_SCORE7
select GENERIC_HAS_IOMAP
config SCORE_SIM
bool "Score simulator"
select SYS_SUPPORTS_32BIT_KERNEL
- select CPU_SCORE7
select GENERIC_HAS_IOMAP
endchoice
endmenu
-config CPU_SCORE7
- bool
-
config NO_DMA
bool
default y
diff --git a/arch/sh/boards/board-sh7757lcr.c b/arch/sh/boards/board-sh7757lcr.c
index 25c5a932f9fe..669df51a82e3 100644
--- a/arch/sh/boards/board-sh7757lcr.c
+++ b/arch/sh/boards/board-sh7757lcr.c
@@ -252,7 +252,7 @@ static struct sh_mobile_sdhi_info sdhi_info = {
static struct resource sdhi_resources[] = {
[0] = {
.start = 0xffe50000,
- .end = 0xffe501ff,
+ .end = 0xffe500ff,
.flags = IORESOURCE_MEM,
},
[1] = {
diff --git a/arch/sh/drivers/pci/pcie-sh7786.h b/arch/sh/drivers/pci/pcie-sh7786.h
index 1ee054e47eae..4a6ff55f759b 100644
--- a/arch/sh/drivers/pci/pcie-sh7786.h
+++ b/arch/sh/drivers/pci/pcie-sh7786.h
@@ -145,9 +145,6 @@
/* PCIERMSGIER */
#define SH4A_PCIERMSGIER (0x004040) /* R/W - 0x0000 0000 32 */
-/* PCIEPHYCTLR */
-#define SH4A_PCIEPHYCTLR (0x010000) /* R/W - 0x0000 0000 32 */
-
/* PCIEPHYADRR */
#define SH4A_PCIEPHYADRR (0x010004) /* R/W - 0x0000 0000 32 */
#define BITS_ACK (24) // Rev1.171
diff --git a/arch/sh/include/asm/syscalls_32.h b/arch/sh/include/asm/syscalls_32.h
index 4f97df87d7d5..4f643aa718e3 100644
--- a/arch/sh/include/asm/syscalls_32.h
+++ b/arch/sh/include/asm/syscalls_32.h
@@ -9,15 +9,9 @@
struct pt_regs;
-asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs);
-asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs);
-asmlinkage int sys_sh_pipe(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs);
+asmlinkage int sys_sigreturn(void);
+asmlinkage int sys_rt_sigreturn(void);
+asmlinkage int sys_sh_pipe(void);
asmlinkage ssize_t sys_pread_wrapper(unsigned int fd, char __user *buf,
size_t count, long dummy, loff_t pos);
asmlinkage ssize_t sys_pwrite_wrapper(unsigned int fd, const char __user *buf,
diff --git a/arch/sh/include/asm/traps_32.h b/arch/sh/include/asm/traps_32.h
index cfd55ff9dff2..17e129fe459c 100644
--- a/arch/sh/include/asm/traps_32.h
+++ b/arch/sh/include/asm/traps_32.h
@@ -42,18 +42,10 @@ static inline void trigger_address_error(void)
asmlinkage void do_address_error(struct pt_regs *regs,
unsigned long writeaccess,
unsigned long address);
-asmlinkage void do_divide_error(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs);
-asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs);
-asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs);
-asmlinkage void do_exception_error(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs);
+asmlinkage void do_divide_error(unsigned long r4);
+asmlinkage void do_reserved_inst(void);
+asmlinkage void do_illegal_slot_inst(void);
+asmlinkage void do_exception_error(void);
#define BUILD_TRAP_HANDLER(name) \
asmlinkage void name##_trap_handler(unsigned long r4, unsigned long r5, \
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7757.c b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c
index e84a43229b9c..5c0e3c335161 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7757.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c
@@ -132,7 +132,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("usb_fck", &mstp_clks[MSTP103]),
CLKDEV_DEV_ID("renesas_usbhs.0", &mstp_clks[MSTP102]),
CLKDEV_CON_ID("mmc0", &mstp_clks[MSTP220]),
- CLKDEV_CON_ID("rspi2", &mstp_clks[MSTP127]),
+ CLKDEV_DEV_ID("rspi.2", &mstp_clks[MSTP127]),
};
int __init arch_clk_init(void)
diff --git a/arch/sh/kernel/dumpstack.c b/arch/sh/kernel/dumpstack.c
index b959f5592604..8dfe645bcc4b 100644
--- a/arch/sh/kernel/dumpstack.c
+++ b/arch/sh/kernel/dumpstack.c
@@ -115,7 +115,7 @@ static int print_trace_stack(void *data, char *name)
*/
static void print_trace_address(void *data, unsigned long addr, int reliable)
{
- printk(data);
+ printk("%s", (char *)data);
printk_address(addr, reliable);
}
diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S
index ca46834294b7..13047a4facd2 100644
--- a/arch/sh/kernel/entry-common.S
+++ b/arch/sh/kernel/entry-common.S
@@ -193,10 +193,10 @@ syscall_trace_entry:
! Reload R0-R4 from kernel stack, where the
! parent may have modified them using
! ptrace(POKEUSR). (Note that R0-R2 are
- ! used by the system call handler directly
- ! from the kernel stack anyway, so don't need
- ! to be reloaded here.) This allows the parent
- ! to rewrite system calls and args on the fly.
+ ! reloaded from the kernel stack by syscall_call
+ ! below, so don't need to be reloaded here.)
+ ! This allows the parent to rewrite system calls
+ ! and args on the fly.
mov.l @(OFF_R4,r15), r4 ! arg0
mov.l @(OFF_R5,r15), r5
mov.l @(OFF_R6,r15), r6
@@ -357,8 +357,15 @@ syscall_call:
mov.l 3f, r8 ! Load the address of sys_call_table
add r8, r3
mov.l @r3, r8
+ mov.l @(OFF_R2,r15), r2
+ mov.l @(OFF_R1,r15), r1
+ mov.l @(OFF_R0,r15), r0
+ mov.l r2, @-r15
+ mov.l r1, @-r15
+ mov.l r0, @-r15
jsr @r8 ! jump to specific syscall handler
nop
+ add #12, r15
mov.l @(OFF_R0,r15), r12 ! save r0
mov.l r0, @(OFF_R0,r15) ! save the return value
!
diff --git a/arch/sh/kernel/ftrace.c b/arch/sh/kernel/ftrace.c
index 30e13196d35b..3c74f53db6db 100644
--- a/arch/sh/kernel/ftrace.c
+++ b/arch/sh/kernel/ftrace.c
@@ -272,11 +272,8 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
return ftrace_modify_code(rec->ip, old, new);
}
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- /* The return code is retured via data */
- __raw_writel(0, (unsigned long)data);
-
return 0;
}
#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c
index 6af6e7c5cac8..594cd371aa28 100644
--- a/arch/sh/kernel/signal_32.c
+++ b/arch/sh/kernel/signal_32.c
@@ -148,11 +148,9 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, int *r0_p
return err;
}
-asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs)
+asmlinkage int sys_sigreturn(void)
{
- struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
+ struct pt_regs *regs = current_pt_regs();
struct sigframe __user *frame = (struct sigframe __user *)regs->regs[15];
sigset_t set;
int r0;
@@ -180,11 +178,9 @@ badframe:
return 0;
}
-asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs)
+asmlinkage int sys_rt_sigreturn(void)
{
- struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
+ struct pt_regs *regs = current_pt_regs();
struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs->regs[15];
sigset_t set;
int r0;
diff --git a/arch/sh/kernel/sys_sh32.c b/arch/sh/kernel/sys_sh32.c
index 497bab3a0401..b66d1c62eb19 100644
--- a/arch/sh/kernel/sys_sh32.c
+++ b/arch/sh/kernel/sys_sh32.c
@@ -21,17 +21,14 @@
* sys_pipe() is the normal C calling standard for creating
* a pipe. It's not the way Unix traditionally does this, though.
*/
-asmlinkage int sys_sh_pipe(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs)
+asmlinkage int sys_sh_pipe(void)
{
- struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
int fd[2];
int error;
error = do_pipe_flags(fd, 0);
if (!error) {
- regs->regs[1] = fd[1];
+ current_pt_regs()->regs[1] = fd[1];
return fd[0];
}
return error;
diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c
index 68e99f09171d..ff639342a8be 100644
--- a/arch/sh/kernel/traps_32.c
+++ b/arch/sh/kernel/traps_32.c
@@ -594,9 +594,7 @@ int is_dsp_inst(struct pt_regs *regs)
#endif /* CONFIG_SH_DSP */
#ifdef CONFIG_CPU_SH2A
-asmlinkage void do_divide_error(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs)
+asmlinkage void do_divide_error(unsigned long r4)
{
siginfo_t info;
@@ -613,11 +611,9 @@ asmlinkage void do_divide_error(unsigned long r4, unsigned long r5,
}
#endif
-asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs)
+asmlinkage void do_reserved_inst(void)
{
- struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
+ struct pt_regs *regs = current_pt_regs();
unsigned long error_code;
struct task_struct *tsk = current;
@@ -701,11 +697,9 @@ static int emulate_branch(unsigned short inst, struct pt_regs *regs)
}
#endif
-asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs)
+asmlinkage void do_illegal_slot_inst(void)
{
- struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
+ struct pt_regs *regs = current_pt_regs();
unsigned long inst;
struct task_struct *tsk = current;
@@ -730,15 +724,12 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5,
die_if_no_fixup("illegal slot instruction", regs, inst);
}
-asmlinkage void do_exception_error(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs)
+asmlinkage void do_exception_error(void)
{
- struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
long ex;
ex = lookup_exception_vector();
- die_if_kernel("exception", regs, ex);
+ die_if_kernel("exception", current_pt_regs(), ex);
}
void per_cpu_trap_init(void)
diff --git a/arch/sh/math-emu/math.c b/arch/sh/math-emu/math.c
index b876780c1e1c..04aa55fa8c75 100644
--- a/arch/sh/math-emu/math.c
+++ b/arch/sh/math-emu/math.c
@@ -574,24 +574,6 @@ static int ieee_fpe_handler(struct pt_regs *regs)
return 0;
}
-asmlinkage void do_fpu_error(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs regs)
-{
- struct task_struct *tsk = current;
- siginfo_t info;
-
- if (ieee_fpe_handler (&regs))
- return;
-
- regs.pc += 2;
- info.si_signo = SIGFPE;
- info.si_errno = 0;
- info.si_code = FPE_FLTINV;
- info.si_addr = (void __user *)regs.pc;
- force_sig_info(SIGFPE, &info, tsk);
-}
-
/**
* fpu_init - Initialize FPU registers
* @fpu: Pointer to software emulated FPU registers.
diff --git a/arch/sparc/kernel/ftrace.c b/arch/sparc/kernel/ftrace.c
index 03ab022e51c5..0a2d2ddff543 100644
--- a/arch/sparc/kernel/ftrace.c
+++ b/arch/sparc/kernel/ftrace.c
@@ -82,12 +82,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return ftrace_modify_code(ip, old, new);
}
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- unsigned long *p = data;
-
- *p = 0;
-
return 0;
}
#endif
diff --git a/arch/sparc/kernel/leon_pci_grpci2.c b/arch/sparc/kernel/leon_pci_grpci2.c
index 5f0402aab7fb..24d6a4446349 100644
--- a/arch/sparc/kernel/leon_pci_grpci2.c
+++ b/arch/sparc/kernel/leon_pci_grpci2.c
@@ -8,6 +8,7 @@
#include <linux/of_device.h>
#include <linux/kernel.h>
#include <linux/pci.h>
+#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <asm/io.h>
diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c
index c5ade9d27a1d..8bb3b3fddea7 100644
--- a/arch/sparc/kernel/sun4m_irq.c
+++ b/arch/sparc/kernel/sun4m_irq.c
@@ -9,6 +9,8 @@
* Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
*/
+#include <linux/slab.h>
+
#include <asm/timer.h>
#include <asm/traps.h>
#include <asm/pgalloc.h>
diff --git a/arch/sparc/kernel/sysfs.c b/arch/sparc/kernel/sysfs.c
index c21c673e5f7c..a364000ca1aa 100644
--- a/arch/sparc/kernel/sysfs.c
+++ b/arch/sparc/kernel/sysfs.c
@@ -300,7 +300,7 @@ static int __init topology_init(void)
check_mmu_stats();
- register_cpu_notifier(&sysfs_cpu_nb);
+ cpu_notifier_register_begin();
for_each_possible_cpu(cpu) {
struct cpu *c = &per_cpu(cpu_devices, cpu);
@@ -310,6 +310,10 @@ static int __init topology_init(void)
register_cpu_online(cpu);
}
+ __register_cpu_notifier(&sysfs_cpu_nb);
+
+ cpu_notifier_register_done();
+
return 0;
}
diff --git a/arch/sparc/net/bpf_jit_comp.c b/arch/sparc/net/bpf_jit_comp.c
index 01fe9946d388..a82c6b2a9780 100644
--- a/arch/sparc/net/bpf_jit_comp.c
+++ b/arch/sparc/net/bpf_jit_comp.c
@@ -618,7 +618,7 @@ void bpf_jit_compile(struct sk_filter *fp)
emit_load16(r_A, struct net_device, type, r_A);
break;
case BPF_S_ANC_RXHASH:
- emit_skb_load32(rxhash, r_A);
+ emit_skb_load32(hash, r_A);
break;
case BPF_S_ANC_VLAN_TAG:
case BPF_S_ANC_VLAN_TAG_PRESENT:
@@ -809,6 +809,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf];
if (image) {
bpf_flush_icache(image, image + proglen);
fp->bpf_func = (void *)image;
+ fp->jited = 1;
}
out:
kfree(addrs);
@@ -817,7 +818,7 @@ out:
void bpf_jit_free(struct sk_filter *fp)
{
- if (fp->bpf_func != sk_run_filter)
+ if (fp->jited)
module_free(NULL, fp->bpf_func);
kfree(fp);
}
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig
index b3692ce78f90..31c8c6223995 100644
--- a/arch/tile/Kconfig
+++ b/arch/tile/Kconfig
@@ -3,6 +3,8 @@
config TILE
def_bool y
+ select HAVE_PERF_EVENTS
+ select USE_PMC if PERF_EVENTS
select HAVE_DMA_ATTRS
select HAVE_DMA_API_DEBUG
select HAVE_KVM if !TILEGX
@@ -66,6 +68,10 @@ config HUGETLB_SUPER_PAGES
config GENERIC_TIME_VSYSCALL
def_bool y
+# Enable PMC if PERF_EVENTS, OPROFILE, or WATCHPOINTS are enabled.
+config USE_PMC
+ bool
+
# FIXME: tilegx can implement a more efficient rwsem.
config RWSEM_GENERIC_SPINLOCK
def_bool y
diff --git a/arch/tile/include/asm/perf_event.h b/arch/tile/include/asm/perf_event.h
new file mode 100644
index 000000000000..59c5b164e5b6
--- /dev/null
+++ b/arch/tile/include/asm/perf_event.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 Tilera Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _ASM_TILE_PERF_EVENT_H
+#define _ASM_TILE_PERF_EVENT_H
+
+#include <linux/percpu.h>
+DECLARE_PER_CPU(u64, perf_irqs);
+
+unsigned long handle_syscall_link_address(void);
+#endif /* _ASM_TILE_PERF_EVENT_H */
diff --git a/arch/tile/include/asm/pmc.h b/arch/tile/include/asm/pmc.h
new file mode 100644
index 000000000000..7ae3956d9008
--- /dev/null
+++ b/arch/tile/include/asm/pmc.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 Tilera Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _ASM_TILE_PMC_H
+#define _ASM_TILE_PMC_H
+
+#include <linux/ptrace.h>
+
+#define TILE_BASE_COUNTERS 2
+
+/* Bitfields below are derived from SPR PERF_COUNT_CTL*/
+#ifndef __tilegx__
+/* PERF_COUNT_CTL on TILEPro */
+#define TILE_CTL_EXCL_USER (1 << 7) /* exclude user level */
+#define TILE_CTL_EXCL_KERNEL (1 << 8) /* exclude kernel level */
+#define TILE_CTL_EXCL_HV (1 << 9) /* exclude hypervisor level */
+
+#define TILE_SEL_MASK 0x7f /* 7 bits for event SEL,
+ COUNT_0_SEL */
+#define TILE_PLM_MASK 0x780 /* 4 bits priv level msks,
+ COUNT_0_MASK*/
+#define TILE_EVENT_MASK (TILE_SEL_MASK | TILE_PLM_MASK)
+
+#else /* __tilegx__*/
+/* PERF_COUNT_CTL on TILEGx*/
+#define TILE_CTL_EXCL_USER (1 << 10) /* exclude user level */
+#define TILE_CTL_EXCL_KERNEL (1 << 11) /* exclude kernel level */
+#define TILE_CTL_EXCL_HV (1 << 12) /* exclude hypervisor level */
+
+#define TILE_SEL_MASK 0x3f /* 6 bits for event SEL,
+ COUNT_0_SEL*/
+#define TILE_BOX_MASK 0x1c0 /* 3 bits box msks,
+ COUNT_0_BOX */
+#define TILE_PLM_MASK 0x3c00 /* 4 bits priv level msks,
+ COUNT_0_MASK */
+#define TILE_EVENT_MASK (TILE_SEL_MASK | TILE_BOX_MASK | TILE_PLM_MASK)
+#endif /* __tilegx__*/
+
+/* Takes register and fault number. Returns error to disable the interrupt. */
+typedef int (*perf_irq_t)(struct pt_regs *, int);
+
+int userspace_perf_handler(struct pt_regs *regs, int fault);
+
+perf_irq_t reserve_pmc_hardware(perf_irq_t new_perf_irq);
+void release_pmc_hardware(void);
+
+unsigned long pmc_get_overflow(void);
+void pmc_ack_overflow(unsigned long status);
+
+void unmask_pmc_interrupts(void);
+void mask_pmc_interrupts(void);
+
+#endif /* _ASM_TILE_PMC_H */
diff --git a/arch/tile/kernel/Makefile b/arch/tile/kernel/Makefile
index 27a2bf39dae8..21f77bf68c69 100644
--- a/arch/tile/kernel/Makefile
+++ b/arch/tile/kernel/Makefile
@@ -25,6 +25,8 @@ obj-$(CONFIG_PCI) += pci_gx.o
else
obj-$(CONFIG_PCI) += pci.o
endif
+obj-$(CONFIG_PERF_EVENTS) += perf_event.o
+obj-$(CONFIG_USE_PMC) += pmc.o
obj-$(CONFIG_TILE_USB) += usb.o
obj-$(CONFIG_TILE_HVGLUE_TRACE) += hvglue_trace.o
obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount_64.o
diff --git a/arch/tile/kernel/ftrace.c b/arch/tile/kernel/ftrace.c
index f1c452092eeb..8d52d83cc516 100644
--- a/arch/tile/kernel/ftrace.c
+++ b/arch/tile/kernel/ftrace.c
@@ -167,10 +167,8 @@ int ftrace_make_nop(struct module *mod,
return ret;
}
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- *(unsigned long *)data = 0;
-
return 0;
}
#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/tile/kernel/intvec_32.S b/arch/tile/kernel/intvec_32.S
index 2cbe6d5dd6b0..cdbda45a4e4b 100644
--- a/arch/tile/kernel/intvec_32.S
+++ b/arch/tile/kernel/intvec_32.S
@@ -313,13 +313,13 @@ intvec_\vecname:
movei r3, 0
}
.else
- .ifc \c_routine, op_handle_perf_interrupt
+ .ifc \c_routine, handle_perf_interrupt
{
mfspr r2, PERF_COUNT_STS
movei r3, -1 /* not used, but set for consistency */
}
.else
- .ifc \c_routine, op_handle_aux_perf_interrupt
+ .ifc \c_routine, handle_perf_interrupt
{
mfspr r2, AUX_PERF_COUNT_STS
movei r3, -1 /* not used, but set for consistency */
@@ -946,6 +946,13 @@ STD_ENTRY(interrupt_return)
bzt r30, .Lrestore_regs
3:
+ /* We are relying on INT_PERF_COUNT at 33, and AUX_PERF_COUNT at 48 */
+ {
+ moveli r0, lo16(1 << (INT_PERF_COUNT - 32))
+ bz r31, .Lrestore_regs
+ }
+ auli r0, r0, ha16(1 << (INT_AUX_PERF_COUNT - 32))
+ mtspr SPR_INTERRUPT_MASK_RESET_K_1, r0
/*
* We now commit to returning from this interrupt, since we will be
@@ -1171,6 +1178,10 @@ handle_nmi:
PTREGS_PTR(r0, PTREGS_OFFSET_BASE)
}
FEEDBACK_REENTER(handle_nmi)
+ {
+ movei r30, 1
+ seq r31, r0, zero
+ }
j interrupt_return
STD_ENDPROC(handle_nmi)
@@ -1835,8 +1846,9 @@ int_unalign:
/* Include .intrpt array of interrupt vectors */
.section ".intrpt", "ax"
-#define op_handle_perf_interrupt bad_intr
-#define op_handle_aux_perf_interrupt bad_intr
+#ifndef CONFIG_USE_PMC
+#define handle_perf_interrupt bad_intr
+#endif
#ifndef CONFIG_HARDWALL
#define do_hardwall_trap bad_intr
@@ -1877,7 +1889,7 @@ int_unalign:
int_hand INT_IDN_AVAIL, IDN_AVAIL, bad_intr
int_hand INT_UDN_AVAIL, UDN_AVAIL, bad_intr
int_hand INT_PERF_COUNT, PERF_COUNT, \
- op_handle_perf_interrupt, handle_nmi
+ handle_perf_interrupt, handle_nmi
int_hand INT_INTCTRL_3, INTCTRL_3, bad_intr
#if CONFIG_KERNEL_PL == 2
dc_dispatch INT_INTCTRL_2, INTCTRL_2
@@ -1902,7 +1914,7 @@ int_unalign:
int_hand INT_SN_CPL, SN_CPL, bad_intr
int_hand INT_DOUBLE_FAULT, DOUBLE_FAULT, do_trap
int_hand INT_AUX_PERF_COUNT, AUX_PERF_COUNT, \
- op_handle_aux_perf_interrupt, handle_nmi
+ handle_perf_interrupt, handle_nmi
/* Synthetic interrupt delivered only by the simulator */
int_hand INT_BREAKPOINT, BREAKPOINT, do_breakpoint
diff --git a/arch/tile/kernel/intvec_64.S b/arch/tile/kernel/intvec_64.S
index b8fc497f2437..5b67efcecabd 100644
--- a/arch/tile/kernel/intvec_64.S
+++ b/arch/tile/kernel/intvec_64.S
@@ -509,10 +509,10 @@ intvec_\vecname:
.ifc \c_routine, do_trap
mfspr r2, GPV_REASON
.else
- .ifc \c_routine, op_handle_perf_interrupt
+ .ifc \c_routine, handle_perf_interrupt
mfspr r2, PERF_COUNT_STS
.else
- .ifc \c_routine, op_handle_aux_perf_interrupt
+ .ifc \c_routine, handle_perf_interrupt
mfspr r2, AUX_PERF_COUNT_STS
.endif
.endif
@@ -971,6 +971,15 @@ STD_ENTRY(interrupt_return)
beqzt r30, .Lrestore_regs
3:
+#if INT_PERF_COUNT + 1 != INT_AUX_PERF_COUNT
+# error Bad interrupt assumption
+#endif
+ {
+ movei r0, 3 /* two adjacent bits for the PERF_COUNT mask */
+ beqz r31, .Lrestore_regs
+ }
+ shli r0, r0, INT_PERF_COUNT
+ mtspr SPR_INTERRUPT_MASK_RESET_K, r0
/*
* We now commit to returning from this interrupt, since we will be
@@ -1187,7 +1196,7 @@ handle_nmi:
FEEDBACK_REENTER(handle_nmi)
{
movei r30, 1
- move r31, r0
+ cmpeq r31, r0, zero
}
j interrupt_return
STD_ENDPROC(handle_nmi)
@@ -1491,8 +1500,9 @@ STD_ENTRY(fill_ra_stack)
.global intrpt_start
intrpt_start:
-#define op_handle_perf_interrupt bad_intr
-#define op_handle_aux_perf_interrupt bad_intr
+#ifndef CONFIG_USE_PMC
+#define handle_perf_interrupt bad_intr
+#endif
#ifndef CONFIG_HARDWALL
#define do_hardwall_trap bad_intr
@@ -1540,9 +1550,9 @@ intrpt_start:
#endif
int_hand INT_IPI_0, IPI_0, bad_intr
int_hand INT_PERF_COUNT, PERF_COUNT, \
- op_handle_perf_interrupt, handle_nmi
+ handle_perf_interrupt, handle_nmi
int_hand INT_AUX_PERF_COUNT, AUX_PERF_COUNT, \
- op_handle_perf_interrupt, handle_nmi
+ handle_perf_interrupt, handle_nmi
int_hand INT_INTCTRL_3, INTCTRL_3, bad_intr
#if CONFIG_KERNEL_PL == 2
dc_dispatch INT_INTCTRL_2, INTCTRL_2
diff --git a/arch/tile/kernel/irq.c b/arch/tile/kernel/irq.c
index 0586fdb9352d..906a76bdb31d 100644
--- a/arch/tile/kernel/irq.c
+++ b/arch/tile/kernel/irq.c
@@ -21,6 +21,7 @@
#include <hv/drv_pcie_rc_intf.h>
#include <arch/spr_def.h>
#include <asm/traps.h>
+#include <linux/perf_event.h>
/* Bit-flag stored in irq_desc->chip_data to indicate HW-cleared irqs. */
#define IS_HW_CLEARED 1
@@ -261,6 +262,23 @@ void ack_bad_irq(unsigned int irq)
}
/*
+ * /proc/interrupts printing:
+ */
+int arch_show_interrupts(struct seq_file *p, int prec)
+{
+#ifdef CONFIG_PERF_EVENTS
+ int i;
+
+ seq_printf(p, "%*s: ", prec, "PMI");
+
+ for_each_online_cpu(i)
+ seq_printf(p, "%10llu ", per_cpu(perf_irqs, i));
+ seq_puts(p, " perf_events\n");
+#endif
+ return 0;
+}
+
+/*
* Generic, controller-independent functions:
*/
diff --git a/arch/tile/kernel/messaging.c b/arch/tile/kernel/messaging.c
index 00331af9525d..7867266f9716 100644
--- a/arch/tile/kernel/messaging.c
+++ b/arch/tile/kernel/messaging.c
@@ -68,8 +68,8 @@ void hv_message_intr(struct pt_regs *regs, int intnum)
#endif
while (1) {
- rmi = hv_receive_message(__get_cpu_var(msg_state),
- (HV_VirtAddr) message,
+ HV_MsgState *state = this_cpu_ptr(&msg_state);
+ rmi = hv_receive_message(*state, (HV_VirtAddr) message,
sizeof(message));
if (rmi.msglen == 0)
break;
diff --git a/arch/tile/kernel/pci.c b/arch/tile/kernel/pci.c
index c45593db7718..1f80a88c75a6 100644
--- a/arch/tile/kernel/pci.c
+++ b/arch/tile/kernel/pci.c
@@ -250,8 +250,6 @@ static void fixup_read_and_payload_sizes(void)
/* Scan for the smallest maximum payload size. */
for_each_pci_dev(dev) {
- u32 devcap;
-
if (!pci_is_pcie(dev))
continue;
diff --git a/arch/tile/kernel/perf_event.c b/arch/tile/kernel/perf_event.c
new file mode 100644
index 000000000000..2bf6c9c135c1
--- /dev/null
+++ b/arch/tile/kernel/perf_event.c
@@ -0,0 +1,1005 @@
+/*
+ * Copyright 2014 Tilera Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ *
+ *
+ * Perf_events support for Tile processor.
+ *
+ * This code is based upon the x86 perf event
+ * code, which is:
+ *
+ * Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
+ * 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) 2009 Intel Corporation, <markus.t.metzger@intel.com>
+ * Copyright (C) 2009 Google, Inc., Stephane Eranian
+ */
+
+#include <linux/kprobes.h>
+#include <linux/kernel.h>
+#include <linux/kdebug.h>
+#include <linux/mutex.h>
+#include <linux/bitmap.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/perf_event.h>
+#include <linux/atomic.h>
+#include <asm/traps.h>
+#include <asm/stack.h>
+#include <asm/pmc.h>
+#include <hv/hypervisor.h>
+
+#define TILE_MAX_COUNTERS 4
+
+#define PERF_COUNT_0_IDX 0
+#define PERF_COUNT_1_IDX 1
+#define AUX_PERF_COUNT_0_IDX 2
+#define AUX_PERF_COUNT_1_IDX 3
+
+struct cpu_hw_events {
+ int n_events;
+ struct perf_event *events[TILE_MAX_COUNTERS]; /* counter order */
+ struct perf_event *event_list[TILE_MAX_COUNTERS]; /* enabled
+ order */
+ int assign[TILE_MAX_COUNTERS];
+ unsigned long active_mask[BITS_TO_LONGS(TILE_MAX_COUNTERS)];
+ unsigned long used_mask;
+};
+
+/* TILE arch specific performance monitor unit */
+struct tile_pmu {
+ const char *name;
+ int version;
+ const int *hw_events; /* generic hw events table */
+ /* generic hw cache events table */
+ const int (*cache_events)[PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX];
+ int (*map_hw_event)(u64); /*method used to map
+ hw events */
+ int (*map_cache_event)(u64); /*method used to map
+ cache events */
+
+ u64 max_period; /* max sampling period */
+ u64 cntval_mask; /* counter width mask */
+ int cntval_bits; /* counter width */
+ int max_events; /* max generic hw events
+ in map */
+ int num_counters; /* number base + aux counters */
+ int num_base_counters; /* number base counters */
+};
+
+DEFINE_PER_CPU(u64, perf_irqs);
+static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
+
+#define TILE_OP_UNSUPP (-1)
+
+#ifndef __tilegx__
+/* TILEPro hardware events map */
+static const int tile_hw_event_map[] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = 0x01, /* ONE */
+ [PERF_COUNT_HW_INSTRUCTIONS] = 0x06, /* MP_BUNDLE_RETIRED */
+ [PERF_COUNT_HW_CACHE_REFERENCES] = TILE_OP_UNSUPP,
+ [PERF_COUNT_HW_CACHE_MISSES] = TILE_OP_UNSUPP,
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x16, /*
+ MP_CONDITIONAL_BRANCH_ISSUED */
+ [PERF_COUNT_HW_BRANCH_MISSES] = 0x14, /*
+ MP_CONDITIONAL_BRANCH_MISSPREDICT */
+ [PERF_COUNT_HW_BUS_CYCLES] = TILE_OP_UNSUPP,
+};
+#else
+/* TILEGx hardware events map */
+static const int tile_hw_event_map[] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = 0x181, /* ONE */
+ [PERF_COUNT_HW_INSTRUCTIONS] = 0xdb, /* INSTRUCTION_BUNDLE */
+ [PERF_COUNT_HW_CACHE_REFERENCES] = TILE_OP_UNSUPP,
+ [PERF_COUNT_HW_CACHE_MISSES] = TILE_OP_UNSUPP,
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0xd9, /*
+ COND_BRANCH_PRED_CORRECT */
+ [PERF_COUNT_HW_BRANCH_MISSES] = 0xda, /*
+ COND_BRANCH_PRED_INCORRECT */
+ [PERF_COUNT_HW_BUS_CYCLES] = TILE_OP_UNSUPP,
+};
+#endif
+
+#define C(x) PERF_COUNT_HW_CACHE_##x
+
+/*
+ * Generalized hw caching related hw_event table, filled
+ * in on a per model basis. A value of -1 means
+ * 'not supported', any other value means the
+ * raw hw_event ID.
+ */
+#ifndef __tilegx__
+/* TILEPro hardware cache event map */
+static const int tile_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+[C(L1D)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = 0x21, /* RD_MISS */
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = 0x22, /* WR_MISS */
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+[C(L1I)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = 0x12, /* MP_ICACHE_HIT_ISSUED */
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+[C(LL)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+[C(DTLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = 0x1d, /* TLB_CNT */
+ [C(RESULT_MISS)] = 0x20, /* TLB_EXCEPTION */
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+[C(ITLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = 0x13, /* MP_ITLB_HIT_ISSUED */
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+[C(BPU)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+};
+#else
+/* TILEGx hardware events map */
+static const int tile_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+[C(L1D)] = {
+ /*
+ * Like some other architectures (e.g. ARM), the performance
+ * counters don't differentiate between read and write
+ * accesses/misses, so this isn't strictly correct, but it's the
+ * best we can do. Writes and reads get combined.
+ */
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = 0x44, /* RD_MISS */
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = 0x45, /* WR_MISS */
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+[C(L1I)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+[C(LL)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+[C(DTLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = 0x40, /* TLB_CNT */
+ [C(RESULT_MISS)] = 0x43, /* TLB_EXCEPTION */
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = 0x40, /* TLB_CNT */
+ [C(RESULT_MISS)] = 0x43, /* TLB_EXCEPTION */
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+[C(ITLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = 0xd4, /* ITLB_MISS_INT */
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = 0xd4, /* ITLB_MISS_INT */
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+[C(BPU)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = TILE_OP_UNSUPP,
+ [C(RESULT_MISS)] = TILE_OP_UNSUPP,
+ },
+},
+};
+#endif
+
+static atomic_t tile_active_events;
+static DEFINE_MUTEX(perf_intr_reserve_mutex);
+
+static int tile_map_hw_event(u64 config);
+static int tile_map_cache_event(u64 config);
+
+static int tile_pmu_handle_irq(struct pt_regs *regs, int fault);
+
+/*
+ * To avoid new_raw_count getting larger then pre_raw_count
+ * in tile_perf_event_update(), we limit the value of max_period to 2^31 - 1.
+ */
+static const struct tile_pmu tilepmu = {
+#ifndef __tilegx__
+ .name = "tilepro",
+#else
+ .name = "tilegx",
+#endif
+ .max_events = ARRAY_SIZE(tile_hw_event_map),
+ .map_hw_event = tile_map_hw_event,
+ .hw_events = tile_hw_event_map,
+ .map_cache_event = tile_map_cache_event,
+ .cache_events = &tile_cache_event_map,
+ .cntval_bits = 32,
+ .cntval_mask = (1ULL << 32) - 1,
+ .max_period = (1ULL << 31) - 1,
+ .num_counters = TILE_MAX_COUNTERS,
+ .num_base_counters = TILE_BASE_COUNTERS,
+};
+
+static const struct tile_pmu *tile_pmu __read_mostly;
+
+/*
+ * Check whether perf event is enabled.
+ */
+int tile_perf_enabled(void)
+{
+ return atomic_read(&tile_active_events) != 0;
+}
+
+/*
+ * Read Performance Counters.
+ */
+static inline u64 read_counter(int idx)
+{
+ u64 val = 0;
+
+ /* __insn_mfspr() only takes an immediate argument */
+ switch (idx) {
+ case PERF_COUNT_0_IDX:
+ val = __insn_mfspr(SPR_PERF_COUNT_0);
+ break;
+ case PERF_COUNT_1_IDX:
+ val = __insn_mfspr(SPR_PERF_COUNT_1);
+ break;
+ case AUX_PERF_COUNT_0_IDX:
+ val = __insn_mfspr(SPR_AUX_PERF_COUNT_0);
+ break;
+ case AUX_PERF_COUNT_1_IDX:
+ val = __insn_mfspr(SPR_AUX_PERF_COUNT_1);
+ break;
+ default:
+ WARN_ON_ONCE(idx > AUX_PERF_COUNT_1_IDX ||
+ idx < PERF_COUNT_0_IDX);
+ }
+
+ return val;
+}
+
+/*
+ * Write Performance Counters.
+ */
+static inline void write_counter(int idx, u64 value)
+{
+ /* __insn_mtspr() only takes an immediate argument */
+ switch (idx) {
+ case PERF_COUNT_0_IDX:
+ __insn_mtspr(SPR_PERF_COUNT_0, value);
+ break;
+ case PERF_COUNT_1_IDX:
+ __insn_mtspr(SPR_PERF_COUNT_1, value);
+ break;
+ case AUX_PERF_COUNT_0_IDX:
+ __insn_mtspr(SPR_AUX_PERF_COUNT_0, value);
+ break;
+ case AUX_PERF_COUNT_1_IDX:
+ __insn_mtspr(SPR_AUX_PERF_COUNT_1, value);
+ break;
+ default:
+ WARN_ON_ONCE(idx > AUX_PERF_COUNT_1_IDX ||
+ idx < PERF_COUNT_0_IDX);
+ }
+}
+
+/*
+ * Enable performance event by setting
+ * Performance Counter Control registers.
+ */
+static inline void tile_pmu_enable_event(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ unsigned long cfg, mask;
+ int shift, idx = hwc->idx;
+
+ /*
+ * prevent early activation from tile_pmu_start() in hw_perf_enable
+ */
+
+ if (WARN_ON_ONCE(idx == -1))
+ return;
+
+ if (idx < tile_pmu->num_base_counters)
+ cfg = __insn_mfspr(SPR_PERF_COUNT_CTL);
+ else
+ cfg = __insn_mfspr(SPR_AUX_PERF_COUNT_CTL);
+
+ switch (idx) {
+ case PERF_COUNT_0_IDX:
+ case AUX_PERF_COUNT_0_IDX:
+ mask = TILE_EVENT_MASK;
+ shift = 0;
+ break;
+ case PERF_COUNT_1_IDX:
+ case AUX_PERF_COUNT_1_IDX:
+ mask = TILE_EVENT_MASK << 16;
+ shift = 16;
+ break;
+ default:
+ WARN_ON_ONCE(idx < PERF_COUNT_0_IDX ||
+ idx > AUX_PERF_COUNT_1_IDX);
+ return;
+ }
+
+ /* Clear mask bits to enable the event. */
+ cfg &= ~mask;
+ cfg |= hwc->config << shift;
+
+ if (idx < tile_pmu->num_base_counters)
+ __insn_mtspr(SPR_PERF_COUNT_CTL, cfg);
+ else
+ __insn_mtspr(SPR_AUX_PERF_COUNT_CTL, cfg);
+}
+
+/*
+ * Disable performance event by clearing
+ * Performance Counter Control registers.
+ */
+static inline void tile_pmu_disable_event(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ unsigned long cfg, mask;
+ int idx = hwc->idx;
+
+ if (idx == -1)
+ return;
+
+ if (idx < tile_pmu->num_base_counters)
+ cfg = __insn_mfspr(SPR_PERF_COUNT_CTL);
+ else
+ cfg = __insn_mfspr(SPR_AUX_PERF_COUNT_CTL);
+
+ switch (idx) {
+ case PERF_COUNT_0_IDX:
+ case AUX_PERF_COUNT_0_IDX:
+ mask = TILE_PLM_MASK;
+ break;
+ case PERF_COUNT_1_IDX:
+ case AUX_PERF_COUNT_1_IDX:
+ mask = TILE_PLM_MASK << 16;
+ break;
+ default:
+ WARN_ON_ONCE(idx < PERF_COUNT_0_IDX ||
+ idx > AUX_PERF_COUNT_1_IDX);
+ return;
+ }
+
+ /* Set mask bits to disable the event. */
+ cfg |= mask;
+
+ if (idx < tile_pmu->num_base_counters)
+ __insn_mtspr(SPR_PERF_COUNT_CTL, cfg);
+ else
+ __insn_mtspr(SPR_AUX_PERF_COUNT_CTL, cfg);
+}
+
+/*
+ * Propagate event elapsed time into the generic event.
+ * Can only be executed on the CPU where the event is active.
+ * Returns the delta events processed.
+ */
+static u64 tile_perf_event_update(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int shift = 64 - tile_pmu->cntval_bits;
+ u64 prev_raw_count, new_raw_count;
+ u64 oldval;
+ int idx = hwc->idx;
+ u64 delta;
+
+ /*
+ * Careful: an NMI might modify the previous event value.
+ *
+ * Our tactic to handle this is to first atomically read and
+ * exchange a new raw count - then add that new-prev delta
+ * count to the generic event atomically:
+ */
+again:
+ prev_raw_count = local64_read(&hwc->prev_count);
+ new_raw_count = read_counter(idx);
+
+ oldval = local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+ new_raw_count);
+ if (oldval != prev_raw_count)
+ goto again;
+
+ /*
+ * Now we have the new raw value and have updated the prev
+ * timestamp already. We can now calculate the elapsed delta
+ * (event-)time and add that to the generic event.
+ *
+ * Careful, not all hw sign-extends above the physical width
+ * of the count.
+ */
+ delta = (new_raw_count << shift) - (prev_raw_count << shift);
+ delta >>= shift;
+
+ local64_add(delta, &event->count);
+ local64_sub(delta, &hwc->period_left);
+
+ return new_raw_count;
+}
+
+/*
+ * Set the next IRQ period, based on the hwc->period_left value.
+ * To be called with the event disabled in hw:
+ */
+static int tile_event_set_period(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+ s64 left = local64_read(&hwc->period_left);
+ s64 period = hwc->sample_period;
+ int ret = 0;
+
+ /*
+ * If we are way outside a reasonable range then just skip forward:
+ */
+ if (unlikely(left <= -period)) {
+ left = period;
+ local64_set(&hwc->period_left, left);
+ hwc->last_period = period;
+ ret = 1;
+ }
+
+ if (unlikely(left <= 0)) {
+ left += period;
+ local64_set(&hwc->period_left, left);
+ hwc->last_period = period;
+ ret = 1;
+ }
+ if (left > tile_pmu->max_period)
+ left = tile_pmu->max_period;
+
+ /*
+ * The hw event starts counting from this event offset,
+ * mark it to be able to extra future deltas:
+ */
+ local64_set(&hwc->prev_count, (u64)-left);
+
+ write_counter(idx, (u64)(-left) & tile_pmu->cntval_mask);
+
+ perf_event_update_userpage(event);
+
+ return ret;
+}
+
+/*
+ * Stop the event but do not release the PMU counter
+ */
+static void tile_pmu_stop(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ if (__test_and_clear_bit(idx, cpuc->active_mask)) {
+ tile_pmu_disable_event(event);
+ cpuc->events[hwc->idx] = NULL;
+ WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
+ hwc->state |= PERF_HES_STOPPED;
+ }
+
+ if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
+ /*
+ * Drain the remaining delta count out of a event
+ * that we are disabling:
+ */
+ tile_perf_event_update(event);
+ hwc->state |= PERF_HES_UPTODATE;
+ }
+}
+
+/*
+ * Start an event (without re-assigning counter)
+ */
+static void tile_pmu_start(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ int idx = event->hw.idx;
+
+ if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
+ return;
+
+ if (WARN_ON_ONCE(idx == -1))
+ return;
+
+ if (flags & PERF_EF_RELOAD) {
+ WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE));
+ tile_event_set_period(event);
+ }
+
+ event->hw.state = 0;
+
+ cpuc->events[idx] = event;
+ __set_bit(idx, cpuc->active_mask);
+
+ unmask_pmc_interrupts();
+
+ tile_pmu_enable_event(event);
+
+ perf_event_update_userpage(event);
+}
+
+/*
+ * Add a single event to the PMU.
+ *
+ * The event is added to the group of enabled events
+ * but only if it can be scehduled with existing events.
+ */
+static int tile_pmu_add(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ struct hw_perf_event *hwc;
+ unsigned long mask;
+ int b, max_cnt;
+
+ hwc = &event->hw;
+
+ /*
+ * We are full.
+ */
+ if (cpuc->n_events == tile_pmu->num_counters)
+ return -ENOSPC;
+
+ cpuc->event_list[cpuc->n_events] = event;
+ cpuc->n_events++;
+
+ hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+ if (!(flags & PERF_EF_START))
+ hwc->state |= PERF_HES_ARCH;
+
+ /*
+ * Find first empty counter.
+ */
+ max_cnt = tile_pmu->num_counters;
+ mask = ~cpuc->used_mask;
+
+ /* Find next free counter. */
+ b = find_next_bit(&mask, max_cnt, 0);
+
+ /* Should not happen. */
+ if (WARN_ON_ONCE(b == max_cnt))
+ return -ENOSPC;
+
+ /*
+ * Assign counter to event.
+ */
+ event->hw.idx = b;
+ __set_bit(b, &cpuc->used_mask);
+
+ /*
+ * Start if requested.
+ */
+ if (flags & PERF_EF_START)
+ tile_pmu_start(event, PERF_EF_RELOAD);
+
+ return 0;
+}
+
+/*
+ * Delete a single event from the PMU.
+ *
+ * The event is deleted from the group of enabled events.
+ * If it is the last event, disable PMU interrupt.
+ */
+static void tile_pmu_del(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ int i;
+
+ /*
+ * Remove event from list, compact list if necessary.
+ */
+ for (i = 0; i < cpuc->n_events; i++) {
+ if (cpuc->event_list[i] == event) {
+ while (++i < cpuc->n_events)
+ cpuc->event_list[i-1] = cpuc->event_list[i];
+ --cpuc->n_events;
+ cpuc->events[event->hw.idx] = NULL;
+ __clear_bit(event->hw.idx, &cpuc->used_mask);
+ tile_pmu_stop(event, PERF_EF_UPDATE);
+ break;
+ }
+ }
+ /*
+ * If there are no events left, then mask PMU interrupt.
+ */
+ if (cpuc->n_events == 0)
+ mask_pmc_interrupts();
+ perf_event_update_userpage(event);
+}
+
+/*
+ * Propagate event elapsed time into the event.
+ */
+static inline void tile_pmu_read(struct perf_event *event)
+{
+ tile_perf_event_update(event);
+}
+
+/*
+ * Map generic events to Tile PMU.
+ */
+static int tile_map_hw_event(u64 config)
+{
+ if (config >= tile_pmu->max_events)
+ return -EINVAL;
+ return tile_pmu->hw_events[config];
+}
+
+/*
+ * Map generic hardware cache events to Tile PMU.
+ */
+static int tile_map_cache_event(u64 config)
+{
+ unsigned int cache_type, cache_op, cache_result;
+ int code;
+
+ if (!tile_pmu->cache_events)
+ return -ENOENT;
+
+ cache_type = (config >> 0) & 0xff;
+ if (cache_type >= PERF_COUNT_HW_CACHE_MAX)
+ return -EINVAL;
+
+ cache_op = (config >> 8) & 0xff;
+ if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX)
+ return -EINVAL;
+
+ cache_result = (config >> 16) & 0xff;
+ if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
+ return -EINVAL;
+
+ code = (*tile_pmu->cache_events)[cache_type][cache_op][cache_result];
+ if (code == TILE_OP_UNSUPP)
+ return -EINVAL;
+
+ return code;
+}
+
+static void tile_event_destroy(struct perf_event *event)
+{
+ if (atomic_dec_return(&tile_active_events) == 0)
+ release_pmc_hardware();
+}
+
+static int __tile_event_init(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+ struct hw_perf_event *hwc = &event->hw;
+ int code;
+
+ switch (attr->type) {
+ case PERF_TYPE_HARDWARE:
+ code = tile_pmu->map_hw_event(attr->config);
+ break;
+ case PERF_TYPE_HW_CACHE:
+ code = tile_pmu->map_cache_event(attr->config);
+ break;
+ case PERF_TYPE_RAW:
+ code = attr->config & TILE_EVENT_MASK;
+ break;
+ default:
+ /* Should not happen. */
+ return -EOPNOTSUPP;
+ }
+
+ if (code < 0)
+ return code;
+
+ hwc->config = code;
+ hwc->idx = -1;
+
+ if (attr->exclude_user)
+ hwc->config |= TILE_CTL_EXCL_USER;
+
+ if (attr->exclude_kernel)
+ hwc->config |= TILE_CTL_EXCL_KERNEL;
+
+ if (attr->exclude_hv)
+ hwc->config |= TILE_CTL_EXCL_HV;
+
+ if (!hwc->sample_period) {
+ hwc->sample_period = tile_pmu->max_period;
+ hwc->last_period = hwc->sample_period;
+ local64_set(&hwc->period_left, hwc->sample_period);
+ }
+ event->destroy = tile_event_destroy;
+ return 0;
+}
+
+static int tile_event_init(struct perf_event *event)
+{
+ int err = 0;
+ perf_irq_t old_irq_handler = NULL;
+
+ if (atomic_inc_return(&tile_active_events) == 1)
+ old_irq_handler = reserve_pmc_hardware(tile_pmu_handle_irq);
+
+ if (old_irq_handler) {
+ pr_warn("PMC hardware busy (reserved by oprofile)\n");
+
+ atomic_dec(&tile_active_events);
+ return -EBUSY;
+ }
+
+ switch (event->attr.type) {
+ case PERF_TYPE_RAW:
+ case PERF_TYPE_HARDWARE:
+ case PERF_TYPE_HW_CACHE:
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ err = __tile_event_init(event);
+ if (err) {
+ if (event->destroy)
+ event->destroy(event);
+ }
+ return err;
+}
+
+static struct pmu tilera_pmu = {
+ .event_init = tile_event_init,
+ .add = tile_pmu_add,
+ .del = tile_pmu_del,
+
+ .start = tile_pmu_start,
+ .stop = tile_pmu_stop,
+
+ .read = tile_pmu_read,
+};
+
+/*
+ * PMU's IRQ handler, PMU has 2 interrupts, they share the same handler.
+ */
+int tile_pmu_handle_irq(struct pt_regs *regs, int fault)
+{
+ struct perf_sample_data data;
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ struct perf_event *event;
+ struct hw_perf_event *hwc;
+ u64 val;
+ unsigned long status;
+ int bit;
+
+ __get_cpu_var(perf_irqs)++;
+
+ if (!atomic_read(&tile_active_events))
+ return 0;
+
+ status = pmc_get_overflow();
+ pmc_ack_overflow(status);
+
+ for_each_set_bit(bit, &status, tile_pmu->num_counters) {
+
+ event = cpuc->events[bit];
+
+ if (!event)
+ continue;
+
+ if (!test_bit(bit, cpuc->active_mask))
+ continue;
+
+ hwc = &event->hw;
+
+ val = tile_perf_event_update(event);
+ if (val & (1ULL << (tile_pmu->cntval_bits - 1)))
+ continue;
+
+ perf_sample_data_init(&data, 0, event->hw.last_period);
+ if (!tile_event_set_period(event))
+ continue;
+
+ if (perf_event_overflow(event, &data, regs))
+ tile_pmu_stop(event, 0);
+ }
+
+ return 0;
+}
+
+static bool __init supported_pmu(void)
+{
+ tile_pmu = &tilepmu;
+ return true;
+}
+
+int __init init_hw_perf_events(void)
+{
+ supported_pmu();
+ perf_pmu_register(&tilera_pmu, "cpu", PERF_TYPE_RAW);
+ return 0;
+}
+arch_initcall(init_hw_perf_events);
+
+/* Callchain handling code. */
+
+/*
+ * Tile specific backtracing code for perf_events.
+ */
+static inline void perf_callchain(struct perf_callchain_entry *entry,
+ struct pt_regs *regs)
+{
+ struct KBacktraceIterator kbt;
+ unsigned int i;
+
+ /*
+ * Get the address just after the "jalr" instruction that
+ * jumps to the handler for a syscall. When we find this
+ * address in a backtrace, we silently ignore it, which gives
+ * us a one-step backtrace connection from the sys_xxx()
+ * function in the kernel to the xxx() function in libc.
+ * Otherwise, we lose the ability to properly attribute time
+ * from the libc calls to the kernel implementations, since
+ * oprofile only considers PCs from backtraces a pair at a time.
+ */
+ unsigned long handle_syscall_pc = handle_syscall_link_address();
+
+ KBacktraceIterator_init(&kbt, NULL, regs);
+ kbt.profile = 1;
+
+ /*
+ * The sample for the pc is already recorded. Now we are adding the
+ * address of the callsites on the stack. Our iterator starts
+ * with the frame of the (already sampled) call site. If our
+ * iterator contained a "return address" field, we could have just
+ * used it and wouldn't have needed to skip the first
+ * frame. That's in effect what the arm and x86 versions do.
+ * Instead we peel off the first iteration to get the equivalent
+ * behavior.
+ */
+
+ if (KBacktraceIterator_end(&kbt))
+ return;
+ KBacktraceIterator_next(&kbt);
+
+ /*
+ * Set stack depth to 16 for user and kernel space respectively, that
+ * is, total 32 stack frames.
+ */
+ for (i = 0; i < 16; ++i) {
+ unsigned long pc;
+ if (KBacktraceIterator_end(&kbt))
+ break;
+ pc = kbt.it.pc;
+ if (pc != handle_syscall_pc)
+ perf_callchain_store(entry, pc);
+ KBacktraceIterator_next(&kbt);
+ }
+}
+
+void perf_callchain_user(struct perf_callchain_entry *entry,
+ struct pt_regs *regs)
+{
+ perf_callchain(entry, regs);
+}
+
+void perf_callchain_kernel(struct perf_callchain_entry *entry,
+ struct pt_regs *regs)
+{
+ perf_callchain(entry, regs);
+}
diff --git a/arch/tile/kernel/pmc.c b/arch/tile/kernel/pmc.c
new file mode 100644
index 000000000000..db62cc34b955
--- /dev/null
+++ b/arch/tile/kernel/pmc.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 Tilera Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/atomic.h>
+#include <linux/interrupt.h>
+
+#include <asm/processor.h>
+#include <asm/pmc.h>
+
+perf_irq_t perf_irq = NULL;
+int handle_perf_interrupt(struct pt_regs *regs, int fault)
+{
+ int retval;
+
+ if (!perf_irq)
+ panic("Unexpected PERF_COUNT interrupt %d\n", fault);
+
+ nmi_enter();
+ retval = perf_irq(regs, fault);
+ nmi_exit();
+ return retval;
+}
+
+/* Reserve PMC hardware if it is available. */
+perf_irq_t reserve_pmc_hardware(perf_irq_t new_perf_irq)
+{
+ return cmpxchg(&perf_irq, NULL, new_perf_irq);
+}
+EXPORT_SYMBOL(reserve_pmc_hardware);
+
+/* Release PMC hardware. */
+void release_pmc_hardware(void)
+{
+ perf_irq = NULL;
+}
+EXPORT_SYMBOL(release_pmc_hardware);
+
+
+/*
+ * Get current overflow status of each performance counter,
+ * and auxiliary performance counter.
+ */
+unsigned long
+pmc_get_overflow(void)
+{
+ unsigned long status;
+
+ /*
+ * merge base+aux into a single vector
+ */
+ status = __insn_mfspr(SPR_PERF_COUNT_STS);
+ status |= __insn_mfspr(SPR_AUX_PERF_COUNT_STS) << TILE_BASE_COUNTERS;
+ return status;
+}
+
+/*
+ * Clear the status bit for the corresponding counter, if written
+ * with a one.
+ */
+void
+pmc_ack_overflow(unsigned long status)
+{
+ /*
+ * clear overflow status by writing ones
+ */
+ __insn_mtspr(SPR_PERF_COUNT_STS, status);
+ __insn_mtspr(SPR_AUX_PERF_COUNT_STS, status >> TILE_BASE_COUNTERS);
+}
+
+/*
+ * The perf count interrupts are masked and unmasked explicitly,
+ * and only here. The normal irq_enable() does not enable them,
+ * and irq_disable() does not disable them. That lets these
+ * routines drive the perf count interrupts orthogonally.
+ *
+ * We also mask the perf count interrupts on entry to the perf count
+ * interrupt handler in assembly code, and by default unmask them
+ * again (with interrupt critical section protection) just before
+ * returning from the interrupt. If the perf count handler returns
+ * a non-zero error code, then we don't re-enable them before returning.
+ *
+ * For Pro, we rely on both interrupts being in the same word to update
+ * them atomically so we never have one enabled and one disabled.
+ */
+
+#if CHIP_HAS_SPLIT_INTR_MASK()
+# if INT_PERF_COUNT < 32 || INT_AUX_PERF_COUNT < 32
+# error Fix assumptions about which word PERF_COUNT interrupts are in
+# endif
+#endif
+
+static inline unsigned long long pmc_mask(void)
+{
+ unsigned long long mask = 1ULL << INT_PERF_COUNT;
+ mask |= 1ULL << INT_AUX_PERF_COUNT;
+ return mask;
+}
+
+void unmask_pmc_interrupts(void)
+{
+ interrupt_mask_reset_mask(pmc_mask());
+}
+
+void mask_pmc_interrupts(void)
+{
+ interrupt_mask_set_mask(pmc_mask());
+}
diff --git a/arch/tile/kernel/time.c b/arch/tile/kernel/time.c
index 5d10642db63e..462dcd0c1700 100644
--- a/arch/tile/kernel/time.c
+++ b/arch/tile/kernel/time.c
@@ -236,7 +236,15 @@ cycles_t ns2cycles(unsigned long nsecs)
* clock frequency.
*/
struct clock_event_device *dev = &__raw_get_cpu_var(tile_timer);
- return ((u64)nsecs * dev->mult) >> dev->shift;
+
+ /*
+ * as in clocksource.h and x86's timer.h, we split the calculation
+ * into 2 parts to avoid unecessary overflow of the intermediate
+ * value. This will not lead to any loss of precision.
+ */
+ u64 quot = (u64)nsecs >> dev->shift;
+ u64 rem = (u64)nsecs & ((1ULL << dev->shift) - 1);
+ return quot * dev->mult + ((rem * dev->mult) >> dev->shift);
}
void update_vsyscall_tz(void)
diff --git a/arch/tile/kernel/vdso/Makefile b/arch/tile/kernel/vdso/Makefile
index e2b7a2f4ee41..a025f63d54cd 100644
--- a/arch/tile/kernel/vdso/Makefile
+++ b/arch/tile/kernel/vdso/Makefile
@@ -104,7 +104,7 @@ $(obj-vdso32:%=%): KBUILD_AFLAGS = $(KBUILD_AFLAGS_32)
$(obj-vdso32:%=%): KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
$(obj)/vgettimeofday32.o: $(obj)/vgettimeofday.c
- $(call if_changed,cc_o_c)
+ $(call if_changed_rule,cc_o_c)
$(obj)/vrt_sigreturn32.o: $(obj)/vrt_sigreturn.S
$(call if_changed,as_o_S)
diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c
index 39f186252e02..7d26d9c0b2fb 100644
--- a/arch/um/drivers/net_kern.c
+++ b/arch/um/drivers/net_kern.c
@@ -240,7 +240,7 @@ static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
spin_unlock_irqrestore(&lp->lock, flags);
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/arch/x86/crypto/Makefile b/arch/x86/crypto/Makefile
index 6ba54d640383..61d6e281898b 100644
--- a/arch/x86/crypto/Makefile
+++ b/arch/x86/crypto/Makefile
@@ -79,6 +79,9 @@ aesni-intel-y := aesni-intel_asm.o aesni-intel_glue.o fpu.o
aesni-intel-$(CONFIG_64BIT) += aesni-intel_avx-x86_64.o
ghash-clmulni-intel-y := ghash-clmulni-intel_asm.o ghash-clmulni-intel_glue.o
sha1-ssse3-y := sha1_ssse3_asm.o sha1_ssse3_glue.o
+ifeq ($(avx2_supported),yes)
+sha1-ssse3-y += sha1_avx2_x86_64_asm.o
+endif
crc32c-intel-y := crc32c-intel_glue.o
crc32c-intel-$(CONFIG_64BIT) += crc32c-pcl-intel-asm_64.o
crc32-pclmul-y := crc32-pclmul_asm.o crc32-pclmul_glue.o
diff --git a/arch/x86/crypto/blowfish_glue.c b/arch/x86/crypto/blowfish_glue.c
index 50ec333b70e6..8af519ed73d1 100644
--- a/arch/x86/crypto/blowfish_glue.c
+++ b/arch/x86/crypto/blowfish_glue.c
@@ -223,9 +223,6 @@ static unsigned int __cbc_decrypt(struct blkcipher_desc *desc,
src -= 1;
dst -= 1;
} while (nbytes >= bsize * 4);
-
- if (nbytes < bsize)
- goto done;
}
/* Handle leftovers */
diff --git a/arch/x86/crypto/cast5_avx_glue.c b/arch/x86/crypto/cast5_avx_glue.c
index e6a3700489b9..e57e20ab5e0b 100644
--- a/arch/x86/crypto/cast5_avx_glue.c
+++ b/arch/x86/crypto/cast5_avx_glue.c
@@ -203,9 +203,6 @@ static unsigned int __cbc_decrypt(struct blkcipher_desc *desc,
src -= 1;
dst -= 1;
} while (nbytes >= bsize * CAST5_PARALLEL_BLOCKS);
-
- if (nbytes < bsize)
- goto done;
}
/* Handle leftovers */
diff --git a/arch/x86/crypto/ghash-clmulni-intel_asm.S b/arch/x86/crypto/ghash-clmulni-intel_asm.S
index 586f41aac361..185fad49d86f 100644
--- a/arch/x86/crypto/ghash-clmulni-intel_asm.S
+++ b/arch/x86/crypto/ghash-clmulni-intel_asm.S
@@ -24,10 +24,6 @@
.align 16
.Lbswap_mask:
.octa 0x000102030405060708090a0b0c0d0e0f
-.Lpoly:
- .octa 0xc2000000000000000000000000000001
-.Ltwo_one:
- .octa 0x00000001000000000000000000000001
#define DATA %xmm0
#define SHASH %xmm1
@@ -134,28 +130,3 @@ ENTRY(clmul_ghash_update)
.Lupdate_just_ret:
ret
ENDPROC(clmul_ghash_update)
-
-/*
- * void clmul_ghash_setkey(be128 *shash, const u8 *key);
- *
- * Calculate hash_key << 1 mod poly
- */
-ENTRY(clmul_ghash_setkey)
- movaps .Lbswap_mask, BSWAP
- movups (%rsi), %xmm0
- PSHUFB_XMM BSWAP %xmm0
- movaps %xmm0, %xmm1
- psllq $1, %xmm0
- psrlq $63, %xmm1
- movaps %xmm1, %xmm2
- pslldq $8, %xmm1
- psrldq $8, %xmm2
- por %xmm1, %xmm0
- # reduction
- pshufd $0b00100100, %xmm2, %xmm1
- pcmpeqd .Ltwo_one, %xmm1
- pand .Lpoly, %xmm1
- pxor %xmm1, %xmm0
- movups %xmm0, (%rdi)
- ret
-ENDPROC(clmul_ghash_setkey)
diff --git a/arch/x86/crypto/ghash-clmulni-intel_glue.c b/arch/x86/crypto/ghash-clmulni-intel_glue.c
index 6759dd1135be..d785cf2c529c 100644
--- a/arch/x86/crypto/ghash-clmulni-intel_glue.c
+++ b/arch/x86/crypto/ghash-clmulni-intel_glue.c
@@ -30,8 +30,6 @@ void clmul_ghash_mul(char *dst, const be128 *shash);
void clmul_ghash_update(char *dst, const char *src, unsigned int srclen,
const be128 *shash);
-void clmul_ghash_setkey(be128 *shash, const u8 *key);
-
struct ghash_async_ctx {
struct cryptd_ahash *cryptd_tfm;
};
@@ -58,13 +56,23 @@ static int ghash_setkey(struct crypto_shash *tfm,
const u8 *key, unsigned int keylen)
{
struct ghash_ctx *ctx = crypto_shash_ctx(tfm);
+ be128 *x = (be128 *)key;
+ u64 a, b;
if (keylen != GHASH_BLOCK_SIZE) {
crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
return -EINVAL;
}
- clmul_ghash_setkey(&ctx->shash, key);
+ /* perform multiplication by 'x' in GF(2^128) */
+ a = be64_to_cpu(x->a);
+ b = be64_to_cpu(x->b);
+
+ ctx->shash.a = (__be64)((b << 1) | (a >> 63));
+ ctx->shash.b = (__be64)((a << 1) | (b >> 63));
+
+ if (a >> 63)
+ ctx->shash.b ^= cpu_to_be64(0xc2);
return 0;
}
diff --git a/arch/x86/crypto/sha1_avx2_x86_64_asm.S b/arch/x86/crypto/sha1_avx2_x86_64_asm.S
new file mode 100644
index 000000000000..1cd792db15ef
--- /dev/null
+++ b/arch/x86/crypto/sha1_avx2_x86_64_asm.S
@@ -0,0 +1,708 @@
+/*
+ * Implement fast SHA-1 with AVX2 instructions. (x86_64)
+ *
+ * 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:
+ * Ilya Albrekht <ilya.albrekht@intel.com>
+ * Maxim Locktyukhin <maxim.locktyukhin@intel.com>
+ * Ronen Zohar <ronen.zohar@intel.com>
+ * Chandramouli Narayanan <mouli@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.
+ *
+ */
+
+/*
+ * SHA-1 implementation with Intel(R) AVX2 instruction set extensions.
+ *
+ *This implementation is based on the previous SSSE3 release:
+ *Visit http://software.intel.com/en-us/articles/
+ *and refer to improving-the-performance-of-the-secure-hash-algorithm-1/
+ *
+ *Updates 20-byte SHA-1 record in 'hash' for even number of
+ *'num_blocks' consecutive 64-byte blocks
+ *
+ *extern "C" void sha1_transform_avx2(
+ * int *hash, const char* input, size_t num_blocks );
+ */
+
+#include <linux/linkage.h>
+
+#define CTX %rdi /* arg1 */
+#define BUF %rsi /* arg2 */
+#define CNT %rdx /* arg3 */
+
+#define REG_A %ecx
+#define REG_B %esi
+#define REG_C %edi
+#define REG_D %eax
+#define REG_E %edx
+#define REG_TB %ebx
+#define REG_TA %r12d
+#define REG_RA %rcx
+#define REG_RB %rsi
+#define REG_RC %rdi
+#define REG_RD %rax
+#define REG_RE %rdx
+#define REG_RTA %r12
+#define REG_RTB %rbx
+#define REG_T1 %ebp
+#define xmm_mov vmovups
+#define avx2_zeroupper vzeroupper
+#define RND_F1 1
+#define RND_F2 2
+#define RND_F3 3
+
+.macro REGALLOC
+ .set A, REG_A
+ .set B, REG_B
+ .set C, REG_C
+ .set D, REG_D
+ .set E, REG_E
+ .set TB, REG_TB
+ .set TA, REG_TA
+
+ .set RA, REG_RA
+ .set RB, REG_RB
+ .set RC, REG_RC
+ .set RD, REG_RD
+ .set RE, REG_RE
+
+ .set RTA, REG_RTA
+ .set RTB, REG_RTB
+
+ .set T1, REG_T1
+.endm
+
+#define K_BASE %r8
+#define HASH_PTR %r9
+#define BUFFER_PTR %r10
+#define BUFFER_PTR2 %r13
+#define BUFFER_END %r11
+
+#define PRECALC_BUF %r14
+#define WK_BUF %r15
+
+#define W_TMP %xmm0
+#define WY_TMP %ymm0
+#define WY_TMP2 %ymm9
+
+# AVX2 variables
+#define WY0 %ymm3
+#define WY4 %ymm5
+#define WY08 %ymm7
+#define WY12 %ymm8
+#define WY16 %ymm12
+#define WY20 %ymm13
+#define WY24 %ymm14
+#define WY28 %ymm15
+
+#define YMM_SHUFB_BSWAP %ymm10
+
+/*
+ * Keep 2 iterations precalculated at a time:
+ * - 80 DWORDs per iteration * 2
+ */
+#define W_SIZE (80*2*2 +16)
+
+#define WK(t) ((((t) % 80) / 4)*32 + ( (t) % 4)*4 + ((t)/80)*16 )(WK_BUF)
+#define PRECALC_WK(t) ((t)*2*2)(PRECALC_BUF)
+
+
+.macro UPDATE_HASH hash, val
+ add \hash, \val
+ mov \val, \hash
+.endm
+
+.macro PRECALC_RESET_WY
+ .set WY_00, WY0
+ .set WY_04, WY4
+ .set WY_08, WY08
+ .set WY_12, WY12
+ .set WY_16, WY16
+ .set WY_20, WY20
+ .set WY_24, WY24
+ .set WY_28, WY28
+ .set WY_32, WY_00
+.endm
+
+.macro PRECALC_ROTATE_WY
+ /* Rotate macros */
+ .set WY_32, WY_28
+ .set WY_28, WY_24
+ .set WY_24, WY_20
+ .set WY_20, WY_16
+ .set WY_16, WY_12
+ .set WY_12, WY_08
+ .set WY_08, WY_04
+ .set WY_04, WY_00
+ .set WY_00, WY_32
+
+ /* Define register aliases */
+ .set WY, WY_00
+ .set WY_minus_04, WY_04
+ .set WY_minus_08, WY_08
+ .set WY_minus_12, WY_12
+ .set WY_minus_16, WY_16
+ .set WY_minus_20, WY_20
+ .set WY_minus_24, WY_24
+ .set WY_minus_28, WY_28
+ .set WY_minus_32, WY
+.endm
+
+.macro PRECALC_00_15
+ .if (i == 0) # Initialize and rotate registers
+ PRECALC_RESET_WY
+ PRECALC_ROTATE_WY
+ .endif
+
+ /* message scheduling pre-compute for rounds 0-15 */
+ .if ((i & 7) == 0)
+ /*
+ * blended AVX2 and ALU instruction scheduling
+ * 1 vector iteration per 8 rounds
+ */
+ vmovdqu ((i * 2) + PRECALC_OFFSET)(BUFFER_PTR), W_TMP
+ .elseif ((i & 7) == 1)
+ vinsertf128 $1, (((i-1) * 2)+PRECALC_OFFSET)(BUFFER_PTR2),\
+ WY_TMP, WY_TMP
+ .elseif ((i & 7) == 2)
+ vpshufb YMM_SHUFB_BSWAP, WY_TMP, WY
+ .elseif ((i & 7) == 4)
+ vpaddd K_XMM(K_BASE), WY, WY_TMP
+ .elseif ((i & 7) == 7)
+ vmovdqu WY_TMP, PRECALC_WK(i&~7)
+
+ PRECALC_ROTATE_WY
+ .endif
+.endm
+
+.macro PRECALC_16_31
+ /*
+ * message scheduling pre-compute for rounds 16-31
+ * calculating last 32 w[i] values in 8 XMM registers
+ * pre-calculate K+w[i] values and store to mem
+ * for later load by ALU add instruction
+ *
+ * "brute force" vectorization for rounds 16-31 only
+ * due to w[i]->w[i-3] dependency
+ */
+ .if ((i & 7) == 0)
+ /*
+ * blended AVX2 and ALU instruction scheduling
+ * 1 vector iteration per 8 rounds
+ */
+ /* w[i-14] */
+ vpalignr $8, WY_minus_16, WY_minus_12, WY
+ vpsrldq $4, WY_minus_04, WY_TMP /* w[i-3] */
+ .elseif ((i & 7) == 1)
+ vpxor WY_minus_08, WY, WY
+ vpxor WY_minus_16, WY_TMP, WY_TMP
+ .elseif ((i & 7) == 2)
+ vpxor WY_TMP, WY, WY
+ vpslldq $12, WY, WY_TMP2
+ .elseif ((i & 7) == 3)
+ vpslld $1, WY, WY_TMP
+ vpsrld $31, WY, WY
+ .elseif ((i & 7) == 4)
+ vpor WY, WY_TMP, WY_TMP
+ vpslld $2, WY_TMP2, WY
+ .elseif ((i & 7) == 5)
+ vpsrld $30, WY_TMP2, WY_TMP2
+ vpxor WY, WY_TMP, WY_TMP
+ .elseif ((i & 7) == 7)
+ vpxor WY_TMP2, WY_TMP, WY
+ vpaddd K_XMM(K_BASE), WY, WY_TMP
+ vmovdqu WY_TMP, PRECALC_WK(i&~7)
+
+ PRECALC_ROTATE_WY
+ .endif
+.endm
+
+.macro PRECALC_32_79
+ /*
+ * in SHA-1 specification:
+ * w[i] = (w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]) rol 1
+ * instead we do equal:
+ * w[i] = (w[i-6] ^ w[i-16] ^ w[i-28] ^ w[i-32]) rol 2
+ * allows more efficient vectorization
+ * since w[i]=>w[i-3] dependency is broken
+ */
+
+ .if ((i & 7) == 0)
+ /*
+ * blended AVX2 and ALU instruction scheduling
+ * 1 vector iteration per 8 rounds
+ */
+ vpalignr $8, WY_minus_08, WY_minus_04, WY_TMP
+ .elseif ((i & 7) == 1)
+ /* W is W_minus_32 before xor */
+ vpxor WY_minus_28, WY, WY
+ .elseif ((i & 7) == 2)
+ vpxor WY_minus_16, WY_TMP, WY_TMP
+ .elseif ((i & 7) == 3)
+ vpxor WY_TMP, WY, WY
+ .elseif ((i & 7) == 4)
+ vpslld $2, WY, WY_TMP
+ .elseif ((i & 7) == 5)
+ vpsrld $30, WY, WY
+ vpor WY, WY_TMP, WY
+ .elseif ((i & 7) == 7)
+ vpaddd K_XMM(K_BASE), WY, WY_TMP
+ vmovdqu WY_TMP, PRECALC_WK(i&~7)
+
+ PRECALC_ROTATE_WY
+ .endif
+.endm
+
+.macro PRECALC r, s
+ .set i, \r
+
+ .if (i < 40)
+ .set K_XMM, 32*0
+ .elseif (i < 80)
+ .set K_XMM, 32*1
+ .elseif (i < 120)
+ .set K_XMM, 32*2
+ .else
+ .set K_XMM, 32*3
+ .endif
+
+ .if (i<32)
+ PRECALC_00_15 \s
+ .elseif (i<64)
+ PRECALC_16_31 \s
+ .elseif (i < 160)
+ PRECALC_32_79 \s
+ .endif
+.endm
+
+.macro ROTATE_STATE
+ .set T_REG, E
+ .set E, D
+ .set D, C
+ .set C, B
+ .set B, TB
+ .set TB, A
+ .set A, T_REG
+
+ .set T_REG, RE
+ .set RE, RD
+ .set RD, RC
+ .set RC, RB
+ .set RB, RTB
+ .set RTB, RA
+ .set RA, T_REG
+.endm
+
+/* Macro relies on saved ROUND_Fx */
+
+.macro RND_FUN f, r
+ .if (\f == RND_F1)
+ ROUND_F1 \r
+ .elseif (\f == RND_F2)
+ ROUND_F2 \r
+ .elseif (\f == RND_F3)
+ ROUND_F3 \r
+ .endif
+.endm
+
+.macro RR r
+ .set round_id, (\r % 80)
+
+ .if (round_id == 0) /* Precalculate F for first round */
+ .set ROUND_FUNC, RND_F1
+ mov B, TB
+
+ rorx $(32-30), B, B /* b>>>2 */
+ andn D, TB, T1
+ and C, TB
+ xor T1, TB
+ .endif
+
+ RND_FUN ROUND_FUNC, \r
+ ROTATE_STATE
+
+ .if (round_id == 18)
+ .set ROUND_FUNC, RND_F2
+ .elseif (round_id == 38)
+ .set ROUND_FUNC, RND_F3
+ .elseif (round_id == 58)
+ .set ROUND_FUNC, RND_F2
+ .endif
+
+ .set round_id, ( (\r+1) % 80)
+
+ RND_FUN ROUND_FUNC, (\r+1)
+ ROTATE_STATE
+.endm
+
+.macro ROUND_F1 r
+ add WK(\r), E
+
+ andn C, A, T1 /* ~b&d */
+ lea (RE,RTB), E /* Add F from the previous round */
+
+ rorx $(32-5), A, TA /* T2 = A >>> 5 */
+ rorx $(32-30),A, TB /* b>>>2 for next round */
+
+ PRECALC (\r) /* msg scheduling for next 2 blocks */
+
+ /*
+ * Calculate F for the next round
+ * (b & c) ^ andn[b, d]
+ */
+ and B, A /* b&c */
+ xor T1, A /* F1 = (b&c) ^ (~b&d) */
+
+ lea (RE,RTA), E /* E += A >>> 5 */
+.endm
+
+.macro ROUND_F2 r
+ add WK(\r), E
+ lea (RE,RTB), E /* Add F from the previous round */
+
+ /* Calculate F for the next round */
+ rorx $(32-5), A, TA /* T2 = A >>> 5 */
+ .if ((round_id) < 79)
+ rorx $(32-30), A, TB /* b>>>2 for next round */
+ .endif
+ PRECALC (\r) /* msg scheduling for next 2 blocks */
+
+ .if ((round_id) < 79)
+ xor B, A
+ .endif
+
+ add TA, E /* E += A >>> 5 */
+
+ .if ((round_id) < 79)
+ xor C, A
+ .endif
+.endm
+
+.macro ROUND_F3 r
+ add WK(\r), E
+ PRECALC (\r) /* msg scheduling for next 2 blocks */
+
+ lea (RE,RTB), E /* Add F from the previous round */
+
+ mov B, T1
+ or A, T1
+
+ rorx $(32-5), A, TA /* T2 = A >>> 5 */
+ rorx $(32-30), A, TB /* b>>>2 for next round */
+
+ /* Calculate F for the next round
+ * (b and c) or (d and (b or c))
+ */
+ and C, T1
+ and B, A
+ or T1, A
+
+ add TA, E /* E += A >>> 5 */
+
+.endm
+
+/*
+ * macro implements 80 rounds of SHA-1, for multiple blocks with s/w pipelining
+ */
+.macro SHA1_PIPELINED_MAIN_BODY
+
+ REGALLOC
+
+ mov (HASH_PTR), A
+ mov 4(HASH_PTR), B
+ mov 8(HASH_PTR), C
+ mov 12(HASH_PTR), D
+ mov 16(HASH_PTR), E
+
+ mov %rsp, PRECALC_BUF
+ lea (2*4*80+32)(%rsp), WK_BUF
+
+ # Precalc WK for first 2 blocks
+ PRECALC_OFFSET = 0
+ .set i, 0
+ .rept 160
+ PRECALC i
+ .set i, i + 1
+ .endr
+ PRECALC_OFFSET = 128
+ xchg WK_BUF, PRECALC_BUF
+
+ .align 32
+_loop:
+ /*
+ * code loops through more than one block
+ * we use K_BASE value as a signal of a last block,
+ * it is set below by: cmovae BUFFER_PTR, K_BASE
+ */
+ cmp K_BASE, BUFFER_PTR
+ jne _begin
+ .align 32
+ jmp _end
+ .align 32
+_begin:
+
+ /*
+ * Do first block
+ * rounds: 0,2,4,6,8
+ */
+ .set j, 0
+ .rept 5
+ RR j
+ .set j, j+2
+ .endr
+
+ jmp _loop0
+_loop0:
+
+ /*
+ * rounds:
+ * 10,12,14,16,18
+ * 20,22,24,26,28
+ * 30,32,34,36,38
+ * 40,42,44,46,48
+ * 50,52,54,56,58
+ */
+ .rept 25
+ RR j
+ .set j, j+2
+ .endr
+
+ add $(2*64), BUFFER_PTR /* move to next odd-64-byte block */
+ cmp BUFFER_END, BUFFER_PTR /* is current block the last one? */
+ cmovae K_BASE, BUFFER_PTR /* signal the last iteration smartly */
+
+ /*
+ * rounds
+ * 60,62,64,66,68
+ * 70,72,74,76,78
+ */
+ .rept 10
+ RR j
+ .set j, j+2
+ .endr
+
+ UPDATE_HASH (HASH_PTR), A
+ UPDATE_HASH 4(HASH_PTR), TB
+ UPDATE_HASH 8(HASH_PTR), C
+ UPDATE_HASH 12(HASH_PTR), D
+ UPDATE_HASH 16(HASH_PTR), E
+
+ cmp K_BASE, BUFFER_PTR /* is current block the last one? */
+ je _loop
+
+ mov TB, B
+
+ /* Process second block */
+ /*
+ * rounds
+ * 0+80, 2+80, 4+80, 6+80, 8+80
+ * 10+80,12+80,14+80,16+80,18+80
+ */
+
+ .set j, 0
+ .rept 10
+ RR j+80
+ .set j, j+2
+ .endr
+
+ jmp _loop1
+_loop1:
+ /*
+ * rounds
+ * 20+80,22+80,24+80,26+80,28+80
+ * 30+80,32+80,34+80,36+80,38+80
+ */
+ .rept 10
+ RR j+80
+ .set j, j+2
+ .endr
+
+ jmp _loop2
+_loop2:
+
+ /*
+ * rounds
+ * 40+80,42+80,44+80,46+80,48+80
+ * 50+80,52+80,54+80,56+80,58+80
+ */
+ .rept 10
+ RR j+80
+ .set j, j+2
+ .endr
+
+ add $(2*64), BUFFER_PTR2 /* move to next even-64-byte block */
+
+ cmp BUFFER_END, BUFFER_PTR2 /* is current block the last one */
+ cmovae K_BASE, BUFFER_PTR /* signal the last iteration smartly */
+
+ jmp _loop3
+_loop3:
+
+ /*
+ * rounds
+ * 60+80,62+80,64+80,66+80,68+80
+ * 70+80,72+80,74+80,76+80,78+80
+ */
+ .rept 10
+ RR j+80
+ .set j, j+2
+ .endr
+
+ UPDATE_HASH (HASH_PTR), A
+ UPDATE_HASH 4(HASH_PTR), TB
+ UPDATE_HASH 8(HASH_PTR), C
+ UPDATE_HASH 12(HASH_PTR), D
+ UPDATE_HASH 16(HASH_PTR), E
+
+ /* Reset state for AVX2 reg permutation */
+ mov A, TA
+ mov TB, A
+ mov C, TB
+ mov E, C
+ mov D, B
+ mov TA, D
+
+ REGALLOC
+
+ xchg WK_BUF, PRECALC_BUF
+
+ jmp _loop
+
+ .align 32
+ _end:
+
+.endm
+/*
+ * macro implements SHA-1 function's body for several 64-byte blocks
+ * param: function's name
+ */
+.macro SHA1_VECTOR_ASM name
+ ENTRY(\name)
+
+ push %rbx
+ push %rbp
+ push %r12
+ push %r13
+ push %r14
+ push %r15
+
+ RESERVE_STACK = (W_SIZE*4 + 8+24)
+
+ /* Align stack */
+ mov %rsp, %rbx
+ and $~(0x20-1), %rsp
+ push %rbx
+ sub $RESERVE_STACK, %rsp
+
+ avx2_zeroupper
+
+ lea K_XMM_AR(%rip), K_BASE
+
+ mov CTX, HASH_PTR
+ mov BUF, BUFFER_PTR
+ lea 64(BUF), BUFFER_PTR2
+
+ shl $6, CNT /* mul by 64 */
+ add BUF, CNT
+ add $64, CNT
+ mov CNT, BUFFER_END
+
+ cmp BUFFER_END, BUFFER_PTR2
+ cmovae K_BASE, BUFFER_PTR2
+
+ xmm_mov BSWAP_SHUFB_CTL(%rip), YMM_SHUFB_BSWAP
+
+ SHA1_PIPELINED_MAIN_BODY
+
+ avx2_zeroupper
+
+ add $RESERVE_STACK, %rsp
+ pop %rsp
+
+ pop %r15
+ pop %r14
+ pop %r13
+ pop %r12
+ pop %rbp
+ pop %rbx
+
+ ret
+
+ ENDPROC(\name)
+.endm
+
+.section .rodata
+
+#define K1 0x5a827999
+#define K2 0x6ed9eba1
+#define K3 0x8f1bbcdc
+#define K4 0xca62c1d6
+
+.align 128
+K_XMM_AR:
+ .long K1, K1, K1, K1
+ .long K1, K1, K1, K1
+ .long K2, K2, K2, K2
+ .long K2, K2, K2, K2
+ .long K3, K3, K3, K3
+ .long K3, K3, K3, K3
+ .long K4, K4, K4, K4
+ .long K4, K4, K4, K4
+
+BSWAP_SHUFB_CTL:
+ .long 0x00010203
+ .long 0x04050607
+ .long 0x08090a0b
+ .long 0x0c0d0e0f
+ .long 0x00010203
+ .long 0x04050607
+ .long 0x08090a0b
+ .long 0x0c0d0e0f
+.text
+
+SHA1_VECTOR_ASM sha1_transform_avx2
diff --git a/arch/x86/crypto/sha1_ssse3_glue.c b/arch/x86/crypto/sha1_ssse3_glue.c
index 4a11a9d72451..74d16ef707c7 100644
--- a/arch/x86/crypto/sha1_ssse3_glue.c
+++ b/arch/x86/crypto/sha1_ssse3_glue.c
@@ -10,6 +10,7 @@
* Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk>
* Copyright (c) Jean-Francois Dive <jef@linuxbe.org>
* Copyright (c) Mathias Krause <minipli@googlemail.com>
+ * Copyright (c) Chandramouli Narayanan <mouli@linux.intel.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
@@ -39,6 +40,12 @@ asmlinkage void sha1_transform_ssse3(u32 *digest, const char *data,
asmlinkage void sha1_transform_avx(u32 *digest, const char *data,
unsigned int rounds);
#endif
+#ifdef CONFIG_AS_AVX2
+#define SHA1_AVX2_BLOCK_OPTSIZE 4 /* optimal 4*64 bytes of SHA1 blocks */
+
+asmlinkage void sha1_transform_avx2(u32 *digest, const char *data,
+ unsigned int rounds);
+#endif
static asmlinkage void (*sha1_transform_asm)(u32 *, const char *, unsigned int);
@@ -165,6 +172,18 @@ static int sha1_ssse3_import(struct shash_desc *desc, const void *in)
return 0;
}
+#ifdef CONFIG_AS_AVX2
+static void sha1_apply_transform_avx2(u32 *digest, const char *data,
+ unsigned int rounds)
+{
+ /* Select the optimal transform based on data block size */
+ if (rounds >= SHA1_AVX2_BLOCK_OPTSIZE)
+ sha1_transform_avx2(digest, data, rounds);
+ else
+ sha1_transform_avx(digest, data, rounds);
+}
+#endif
+
static struct shash_alg alg = {
.digestsize = SHA1_DIGEST_SIZE,
.init = sha1_ssse3_init,
@@ -201,27 +220,49 @@ static bool __init avx_usable(void)
return true;
}
+
+#ifdef CONFIG_AS_AVX2
+static bool __init avx2_usable(void)
+{
+ if (avx_usable() && cpu_has_avx2 && boot_cpu_has(X86_FEATURE_BMI1) &&
+ boot_cpu_has(X86_FEATURE_BMI2))
+ return true;
+
+ return false;
+}
+#endif
#endif
static int __init sha1_ssse3_mod_init(void)
{
+ char *algo_name;
+
/* test for SSSE3 first */
- if (cpu_has_ssse3)
+ if (cpu_has_ssse3) {
sha1_transform_asm = sha1_transform_ssse3;
+ algo_name = "SSSE3";
+ }
#ifdef CONFIG_AS_AVX
/* allow AVX to override SSSE3, it's a little faster */
- if (avx_usable())
+ if (avx_usable()) {
sha1_transform_asm = sha1_transform_avx;
+ algo_name = "AVX";
+#ifdef CONFIG_AS_AVX2
+ /* allow AVX2 to override AVX, it's a little faster */
+ if (avx2_usable()) {
+ sha1_transform_asm = sha1_apply_transform_avx2;
+ algo_name = "AVX2";
+ }
+#endif
+ }
#endif
if (sha1_transform_asm) {
- pr_info("Using %s optimized SHA-1 implementation\n",
- sha1_transform_asm == sha1_transform_ssse3 ? "SSSE3"
- : "AVX");
+ pr_info("Using %s optimized SHA-1 implementation\n", algo_name);
return crypto_register_shash(&alg);
}
- pr_info("Neither AVX nor SSSE3 is available/usable.\n");
+ pr_info("Neither AVX nor AVX2 nor SSSE3 is available/usable.\n");
return -ENODEV;
}
diff --git a/arch/x86/include/asm/archrandom.h b/arch/x86/include/asm/archrandom.h
index e6a92455740e..69f1366f1aa3 100644
--- a/arch/x86/include/asm/archrandom.h
+++ b/arch/x86/include/asm/archrandom.h
@@ -1,7 +1,7 @@
/*
* This file is part of the Linux kernel.
*
- * Copyright (c) 2011, Intel Corporation
+ * Copyright (c) 2011-2014, Intel Corporation
* Authors: Fenghua Yu <fenghua.yu@intel.com>,
* H. Peter Anvin <hpa@linux.intel.com>
*
@@ -31,10 +31,13 @@
#define RDRAND_RETRY_LOOPS 10
#define RDRAND_INT ".byte 0x0f,0xc7,0xf0"
+#define RDSEED_INT ".byte 0x0f,0xc7,0xf8"
#ifdef CONFIG_X86_64
# define RDRAND_LONG ".byte 0x48,0x0f,0xc7,0xf0"
+# define RDSEED_LONG ".byte 0x48,0x0f,0xc7,0xf8"
#else
# define RDRAND_LONG RDRAND_INT
+# define RDSEED_LONG RDSEED_INT
#endif
#ifdef CONFIG_ARCH_RANDOM
@@ -53,6 +56,16 @@ static inline int rdrand_long(unsigned long *v)
return ok;
}
+/* A single attempt at RDSEED */
+static inline bool rdseed_long(unsigned long *v)
+{
+ unsigned char ok;
+ asm volatile(RDSEED_LONG "\n\t"
+ "setc %0"
+ : "=qm" (ok), "=a" (*v));
+ return ok;
+}
+
#define GET_RANDOM(name, type, rdrand, nop) \
static inline int name(type *v) \
{ \
@@ -70,18 +83,40 @@ static inline int name(type *v) \
return ok; \
}
+#define GET_SEED(name, type, rdseed, nop) \
+static inline int name(type *v) \
+{ \
+ unsigned char ok; \
+ alternative_io("movb $0, %0\n\t" \
+ nop, \
+ rdseed "\n\t" \
+ "setc %0", \
+ X86_FEATURE_RDSEED, \
+ ASM_OUTPUT2("=q" (ok), "=a" (*v))); \
+ return ok; \
+}
+
#ifdef CONFIG_X86_64
GET_RANDOM(arch_get_random_long, unsigned long, RDRAND_LONG, ASM_NOP5);
GET_RANDOM(arch_get_random_int, unsigned int, RDRAND_INT, ASM_NOP4);
+GET_SEED(arch_get_random_seed_long, unsigned long, RDSEED_LONG, ASM_NOP5);
+GET_SEED(arch_get_random_seed_int, unsigned int, RDSEED_INT, ASM_NOP4);
+
#else
GET_RANDOM(arch_get_random_long, unsigned long, RDRAND_LONG, ASM_NOP3);
GET_RANDOM(arch_get_random_int, unsigned int, RDRAND_INT, ASM_NOP3);
+GET_SEED(arch_get_random_seed_long, unsigned long, RDSEED_LONG, ASM_NOP4);
+GET_SEED(arch_get_random_seed_int, unsigned int, RDSEED_INT, ASM_NOP4);
+
#endif /* CONFIG_X86_64 */
+#define arch_has_random() static_cpu_has(X86_FEATURE_RDRAND)
+#define arch_has_random_seed() static_cpu_has(X86_FEATURE_RDSEED)
+
#else
static inline int rdrand_long(unsigned long *v)
@@ -89,6 +124,11 @@ static inline int rdrand_long(unsigned long *v)
return 0;
}
+static inline bool rdseed_long(unsigned long *v)
+{
+ return 0;
+}
+
#endif /* CONFIG_ARCH_RANDOM */
extern void x86_init_rdrand(struct cpuinfo_x86 *c);
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index fdf83afbb7d9..fcaf9c961265 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -337,6 +337,11 @@ struct kvm_pmu {
u64 reprogram_pmi;
};
+enum {
+ KVM_DEBUGREG_BP_ENABLED = 1,
+ KVM_DEBUGREG_WONT_EXIT = 2,
+};
+
struct kvm_vcpu_arch {
/*
* rip and regs accesses must go through
@@ -444,7 +449,6 @@ struct kvm_vcpu_arch {
} st;
u64 last_guest_tsc;
- u64 last_kernel_ns;
u64 last_host_tsc;
u64 tsc_offset_adjustment;
u64 this_tsc_nsec;
@@ -464,7 +468,7 @@ struct kvm_vcpu_arch {
struct mtrr_state_type mtrr_state;
u32 pat;
- int switch_db_regs;
+ unsigned switch_db_regs;
unsigned long db[KVM_NR_DB_REGS];
unsigned long dr6;
unsigned long dr7;
@@ -599,6 +603,8 @@ struct kvm_arch {
bool use_master_clock;
u64 master_kernel_ns;
cycle_t master_cycle_now;
+ struct delayed_work kvmclock_update_work;
+ struct delayed_work kvmclock_sync_work;
struct kvm_xen_hvm_config xen_hvm_config;
@@ -702,6 +708,7 @@ struct kvm_x86_ops {
void (*set_gdt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt);
u64 (*get_dr6)(struct kvm_vcpu *vcpu);
void (*set_dr6)(struct kvm_vcpu *vcpu, unsigned long value);
+ void (*sync_dirty_debug_regs)(struct kvm_vcpu *vcpu);
void (*set_dr7)(struct kvm_vcpu *vcpu, unsigned long value);
void (*cache_reg)(struct kvm_vcpu *vcpu, enum kvm_reg reg);
unsigned long (*get_rflags)(struct kvm_vcpu *vcpu);
@@ -728,8 +735,8 @@ struct kvm_x86_ops {
int (*nmi_allowed)(struct kvm_vcpu *vcpu);
bool (*get_nmi_mask)(struct kvm_vcpu *vcpu);
void (*set_nmi_mask)(struct kvm_vcpu *vcpu, bool masked);
- int (*enable_nmi_window)(struct kvm_vcpu *vcpu);
- int (*enable_irq_window)(struct kvm_vcpu *vcpu);
+ 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 (*vm_has_apicv)(struct kvm *kvm);
void (*hwapic_irr_update)(struct kvm_vcpu *vcpu, int max_irr);
@@ -765,6 +772,9 @@ struct kvm_x86_ops {
struct x86_instruction_info *info,
enum x86_intercept_stage stage);
void (*handle_external_intr)(struct kvm_vcpu *vcpu);
+ bool (*mpx_supported)(void);
+
+ int (*check_nested_events)(struct kvm_vcpu *vcpu, bool external_intr);
};
struct kvm_arch_async_pf {
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 2067264fb7f5..7004d21e6219 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -85,6 +85,7 @@
#define VM_EXIT_SAVE_IA32_EFER 0x00100000
#define VM_EXIT_LOAD_IA32_EFER 0x00200000
#define VM_EXIT_SAVE_VMX_PREEMPTION_TIMER 0x00400000
+#define VM_EXIT_CLEAR_BNDCFGS 0x00800000
#define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff
@@ -95,6 +96,7 @@
#define VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL 0x00002000
#define VM_ENTRY_LOAD_IA32_PAT 0x00004000
#define VM_ENTRY_LOAD_IA32_EFER 0x00008000
+#define VM_ENTRY_LOAD_BNDCFGS 0x00010000
#define VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR 0x000011ff
@@ -174,6 +176,8 @@ enum vmcs_field {
GUEST_PDPTR2_HIGH = 0x0000280f,
GUEST_PDPTR3 = 0x00002810,
GUEST_PDPTR3_HIGH = 0x00002811,
+ GUEST_BNDCFGS = 0x00002812,
+ GUEST_BNDCFGS_HIGH = 0x00002813,
HOST_IA32_PAT = 0x00002c00,
HOST_IA32_PAT_HIGH = 0x00002c01,
HOST_IA32_EFER = 0x00002c02,
diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h
index 3e276eb23d1b..c949923a5668 100644
--- a/arch/x86/include/asm/xen/page.h
+++ b/arch/x86/include/asm/xen/page.h
@@ -49,10 +49,17 @@ extern bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn);
extern unsigned long set_phys_range_identity(unsigned long pfn_s,
unsigned long pfn_e);
+extern int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
+ struct gnttab_map_grant_ref *kmap_ops,
+ struct page **pages, unsigned int count);
extern int m2p_add_override(unsigned long mfn, struct page *page,
struct gnttab_map_grant_ref *kmap_op);
+extern int clear_foreign_p2m_mapping(struct gnttab_unmap_grant_ref *unmap_ops,
+ struct gnttab_map_grant_ref *kmap_ops,
+ struct page **pages, unsigned int count);
extern int m2p_remove_override(struct page *page,
- struct gnttab_map_grant_ref *kmap_op);
+ struct gnttab_map_grant_ref *kmap_op,
+ unsigned long mfn);
extern struct page *m2p_find_override(unsigned long mfn);
extern unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn);
@@ -121,7 +128,7 @@ static inline unsigned long mfn_to_pfn(unsigned long mfn)
pfn = m2p_find_override_pfn(mfn, ~0);
}
- /*
+ /*
* pfn is ~0 if there are no entries in the m2p for mfn or if the
* entry doesn't map back to the mfn and m2p_override doesn't have a
* valid entry for it.
diff --git a/arch/x86/include/asm/xsave.h b/arch/x86/include/asm/xsave.h
index 6c1d7411eb00..d949ef28c48b 100644
--- a/arch/x86/include/asm/xsave.h
+++ b/arch/x86/include/asm/xsave.h
@@ -16,6 +16,8 @@
#define XSTATE_Hi16_ZMM 0x80
#define XSTATE_FPSSE (XSTATE_FP | XSTATE_SSE)
+/* Bit 63 of XCR0 is reserved for future expansion */
+#define XSTATE_EXTEND_MASK (~(XSTATE_FPSSE | (1ULL << 63)))
#define FXSAVE_SIZE 512
diff --git a/arch/x86/include/uapi/asm/msr-index.h b/arch/x86/include/uapi/asm/msr-index.h
index 4924f4be2b99..c827ace3121b 100644
--- a/arch/x86/include/uapi/asm/msr-index.h
+++ b/arch/x86/include/uapi/asm/msr-index.h
@@ -295,6 +295,7 @@
#define MSR_SMI_COUNT 0x00000034
#define MSR_IA32_FEATURE_CONTROL 0x0000003a
#define MSR_IA32_TSC_ADJUST 0x0000003b
+#define MSR_IA32_BNDCFGS 0x00000d90
#define FEATURE_CONTROL_LOCKED (1<<0)
#define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX (1<<1)
diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c
index 0641113e2965..a952e9c85b6f 100644
--- a/arch/x86/kernel/cpu/intel_cacheinfo.c
+++ b/arch/x86/kernel/cpu/intel_cacheinfo.c
@@ -1225,21 +1225,24 @@ static struct notifier_block cacheinfo_cpu_notifier = {
static int __init cache_sysfs_init(void)
{
- int i;
+ int i, err = 0;
if (num_cache_leaves == 0)
return 0;
+ cpu_notifier_register_begin();
for_each_online_cpu(i) {
- int err;
struct device *dev = get_cpu_device(i);
err = cache_add_dev(dev);
if (err)
- return err;
+ goto out;
}
- register_hotcpu_notifier(&cacheinfo_cpu_notifier);
- return 0;
+ __register_hotcpu_notifier(&cacheinfo_cpu_notifier);
+
+out:
+ cpu_notifier_register_done();
+ return err;
}
device_initcall(cache_sysfs_init);
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 4d5419b249da..9b7734b1f975 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -2434,14 +2434,18 @@ static __init int mcheck_init_device(void)
if (err)
return err;
+ cpu_notifier_register_begin();
for_each_online_cpu(i) {
err = mce_device_create(i);
- if (err)
+ if (err) {
+ cpu_notifier_register_done();
return err;
+ }
}
register_syscore_ops(&mce_syscore_ops);
- register_hotcpu_notifier(&mce_cpu_notifier);
+ __register_hotcpu_notifier(&mce_cpu_notifier);
+ cpu_notifier_register_done();
/* register character device /dev/mcelog */
misc_register(&mce_chrdev_device);
diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c
index 3eec7de76efb..d921b7ee6595 100644
--- a/arch/x86/kernel/cpu/mcheck/therm_throt.c
+++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c
@@ -271,9 +271,6 @@ static void thermal_throttle_remove_dev(struct device *dev)
sysfs_remove_group(&dev->kobj, &thermal_attr_group);
}
-/* Mutex protecting device creation against CPU hotplug: */
-static DEFINE_MUTEX(therm_cpu_lock);
-
/* Get notified when a cpu comes on/off. Be hotplug friendly. */
static int
thermal_throttle_cpu_callback(struct notifier_block *nfb,
@@ -289,18 +286,14 @@ thermal_throttle_cpu_callback(struct notifier_block *nfb,
switch (action) {
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
- mutex_lock(&therm_cpu_lock);
err = thermal_throttle_add_dev(dev, cpu);
- mutex_unlock(&therm_cpu_lock);
WARN_ON(err);
break;
case CPU_UP_CANCELED:
case CPU_UP_CANCELED_FROZEN:
case CPU_DEAD:
case CPU_DEAD_FROZEN:
- mutex_lock(&therm_cpu_lock);
thermal_throttle_remove_dev(dev);
- mutex_unlock(&therm_cpu_lock);
break;
}
return notifier_from_errno(err);
@@ -319,19 +312,16 @@ static __init int thermal_throttle_init_device(void)
if (!atomic_read(&therm_throt_en))
return 0;
- register_hotcpu_notifier(&thermal_throttle_cpu_notifier);
+ cpu_notifier_register_begin();
-#ifdef CONFIG_HOTPLUG_CPU
- mutex_lock(&therm_cpu_lock);
-#endif
/* connect live CPUs to sysfs */
for_each_online_cpu(cpu) {
err = thermal_throttle_add_dev(get_cpu_device(cpu), cpu);
WARN_ON(err);
}
-#ifdef CONFIG_HOTPLUG_CPU
- mutex_unlock(&therm_cpu_lock);
-#endif
+
+ __register_hotcpu_notifier(&thermal_throttle_cpu_notifier);
+ cpu_notifier_register_done();
return 0;
}
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
index 4b8e4d3cd6ea..4c36bbe3173a 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
@@ -926,13 +926,13 @@ static __init int amd_ibs_init(void)
goto out;
perf_ibs_pm_init();
- get_online_cpus();
+ cpu_notifier_register_begin();
ibs_caps = caps;
/* make ibs_caps visible to other cpus: */
smp_mb();
- perf_cpu_notifier(perf_ibs_cpu_notifier);
smp_call_function(setup_APIC_ibs, NULL, 1);
- put_online_cpus();
+ __perf_cpu_notifier(perf_ibs_cpu_notifier);
+ cpu_notifier_register_done();
ret = perf_event_ibs_init();
out:
diff --git a/arch/x86/kernel/cpu/perf_event_amd_uncore.c b/arch/x86/kernel/cpu/perf_event_amd_uncore.c
index 754291adec33..3bbdf4cd38b9 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_uncore.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_uncore.c
@@ -531,15 +531,16 @@ static int __init amd_uncore_init(void)
if (ret)
return -ENODEV;
- get_online_cpus();
+ cpu_notifier_register_begin();
+
/* init cpus already online before registering for hotplug notifier */
for_each_online_cpu(cpu) {
amd_uncore_cpu_up_prepare(cpu);
smp_call_function_single(cpu, init_cpu_already_online, NULL, 1);
}
- register_cpu_notifier(&amd_uncore_cpu_notifier_block);
- put_online_cpus();
+ __register_cpu_notifier(&amd_uncore_cpu_notifier_block);
+ cpu_notifier_register_done();
return 0;
}
diff --git a/arch/x86/kernel/cpu/perf_event_intel_rapl.c b/arch/x86/kernel/cpu/perf_event_intel_rapl.c
index 5ad35ad94d0f..059218ed5208 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_rapl.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_rapl.c
@@ -646,19 +646,20 @@ static int __init rapl_pmu_init(void)
/* unsupported */
return 0;
}
- get_online_cpus();
+
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu) {
rapl_cpu_prepare(cpu);
rapl_cpu_init(cpu);
}
- perf_cpu_notifier(rapl_cpu_notifier);
+ __perf_cpu_notifier(rapl_cpu_notifier);
ret = perf_pmu_register(&rapl_pmu_class, "power", -1);
if (WARN_ON(ret)) {
pr_info("RAPL PMU detected, registration failed (%d), RAPL PMU disabled\n", ret);
- put_online_cpus();
+ cpu_notifier_register_done();
return -1;
}
@@ -672,7 +673,7 @@ static int __init rapl_pmu_init(void)
hweight32(rapl_cntr_mask),
ktime_to_ms(pmu->timer_interval));
- put_online_cpus();
+ cpu_notifier_register_done();
return 0;
}
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
index bd2253d40cff..65bbbea38b9c 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
@@ -4244,7 +4244,7 @@ static void __init uncore_cpumask_init(void)
if (!cpumask_empty(&uncore_cpu_mask))
return;
- get_online_cpus();
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu) {
int i, phys_id = topology_physical_package_id(cpu);
@@ -4263,9 +4263,9 @@ static void __init uncore_cpumask_init(void)
}
on_each_cpu(uncore_cpu_setup, NULL, 1);
- register_cpu_notifier(&uncore_cpu_nb);
+ __register_cpu_notifier(&uncore_cpu_nb);
- put_online_cpus();
+ cpu_notifier_register_done();
}
diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c
index 7d9481c743f8..3225ae6c5180 100644
--- a/arch/x86/kernel/cpuid.c
+++ b/arch/x86/kernel/cpuid.c
@@ -198,14 +198,15 @@ static int __init cpuid_init(void)
goto out_chrdev;
}
cpuid_class->devnode = cpuid_devnode;
- get_online_cpus();
+
+ cpu_notifier_register_begin();
for_each_online_cpu(i) {
err = cpuid_device_create(i);
if (err != 0)
goto out_class;
}
- register_hotcpu_notifier(&cpuid_class_cpu_notifier);
- put_online_cpus();
+ __register_hotcpu_notifier(&cpuid_class_cpu_notifier);
+ cpu_notifier_register_done();
err = 0;
goto out;
@@ -215,7 +216,7 @@ out_class:
for_each_online_cpu(i) {
cpuid_device_destroy(i);
}
- put_online_cpus();
+ cpu_notifier_register_done();
class_destroy(cpuid_class);
out_chrdev:
__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
@@ -227,13 +228,13 @@ static void __exit cpuid_exit(void)
{
int cpu = 0;
- get_online_cpus();
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
cpuid_device_destroy(cpu);
class_destroy(cpuid_class);
__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
- unregister_hotcpu_notifier(&cpuid_class_cpu_notifier);
- put_online_cpus();
+ __unregister_hotcpu_notifier(&cpuid_class_cpu_notifier);
+ cpu_notifier_register_done();
}
module_init(cpuid_init);
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index e6253195a301..52819e816f87 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -308,7 +308,10 @@ static int ftrace_write(unsigned long ip, const char *val, int size)
if (within(ip, (unsigned long)_text, (unsigned long)_etext))
ip = (unsigned long)__va(__pa_symbol(ip));
- return probe_kernel_write((void *)ip, val, size);
+ if (probe_kernel_write((void *)ip, val, size))
+ return -EPERM;
+
+ return 0;
}
static int add_break(unsigned long ip, const char *old)
@@ -323,10 +326,7 @@ static int add_break(unsigned long ip, const char *old)
if (memcmp(replaced, old, MCOUNT_INSN_SIZE) != 0)
return -EINVAL;
- if (ftrace_write(ip, &brk, 1))
- return -EPERM;
-
- return 0;
+ return ftrace_write(ip, &brk, 1);
}
static int add_brk_on_call(struct dyn_ftrace *rec, unsigned long addr)
@@ -425,7 +425,7 @@ static int remove_breakpoint(struct dyn_ftrace *rec)
/* If this does not have a breakpoint, we are done */
if (ins[0] != brk)
- return -1;
+ return 0;
nop = ftrace_nop_replace();
@@ -455,7 +455,7 @@ static int remove_breakpoint(struct dyn_ftrace *rec)
}
update:
- return probe_kernel_write((void *)ip, &nop[0], 1);
+ return ftrace_write(ip, nop, 1);
}
static int add_update_code(unsigned long ip, unsigned const char *new)
@@ -463,9 +463,7 @@ static int add_update_code(unsigned long ip, unsigned const char *new)
/* skip breakpoint */
ip++;
new++;
- if (ftrace_write(ip, new, MCOUNT_INSN_SIZE - 1))
- return -EPERM;
- return 0;
+ return ftrace_write(ip, new, MCOUNT_INSN_SIZE - 1);
}
static int add_update_call(struct dyn_ftrace *rec, unsigned long addr)
@@ -520,10 +518,7 @@ static int finish_update_call(struct dyn_ftrace *rec, unsigned long addr)
new = ftrace_call_replace(ip, addr);
- if (ftrace_write(ip, new, 1))
- return -EPERM;
-
- return 0;
+ return ftrace_write(ip, new, 1);
}
static int finish_update_nop(struct dyn_ftrace *rec)
@@ -533,9 +528,7 @@ static int finish_update_nop(struct dyn_ftrace *rec)
new = ftrace_nop_replace();
- if (ftrace_write(ip, new, 1))
- return -EPERM;
- return 0;
+ return ftrace_write(ip, new, 1);
}
static int finish_update(struct dyn_ftrace *rec, int enable)
@@ -632,8 +625,14 @@ void ftrace_replace_code(int enable)
printk(KERN_WARNING "Failed on %s (%d):\n", report, count);
for_ftrace_rec_iter(iter) {
rec = ftrace_rec_iter_record(iter);
- remove_breakpoint(rec);
+ /*
+ * Breakpoints are handled only when this function is in
+ * progress. The system could not work with them.
+ */
+ if (remove_breakpoint(rec))
+ BUG();
}
+ run_sync();
}
static int
@@ -655,16 +654,19 @@ ftrace_modify_code(unsigned long ip, unsigned const char *old_code,
run_sync();
ret = ftrace_write(ip, new_code, 1);
- if (ret) {
- ret = -EPERM;
- goto out;
- }
- run_sync();
+ /*
+ * The breakpoint is handled only when this function is in progress.
+ * The system could not work if we could not remove it.
+ */
+ BUG_ON(ret);
out:
+ run_sync();
return ret;
fail_update:
- probe_kernel_write((void *)ip, &old_code[0], 1);
+ /* Also here the system could not work with the breakpoint */
+ if (ftrace_write(ip, old_code, 1))
+ BUG();
goto out;
}
@@ -678,11 +680,8 @@ void arch_ftrace_update_code(int command)
atomic_dec(&modifying_ftrace_code);
}
-int __init ftrace_dyn_arch_init(void *data)
+int __init ftrace_dyn_arch_init(void)
{
- /* The return code is retured via data */
- *(unsigned long *)data = 0;
-
return 0;
}
#endif
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index 93eed15a8fd4..8d80ae011603 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -941,12 +941,14 @@ static __init int hpet_late_init(void)
if (boot_cpu_has(X86_FEATURE_ARAT))
return 0;
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu) {
hpet_cpuhp_notify(NULL, CPU_ONLINE, (void *)(long)cpu);
}
/* This notifier should be called after workqueue is ready */
- hotcpu_notifier(hpet_cpuhp_notify, -20);
+ __hotcpu_notifier(hpet_cpuhp_notify, -20);
+ cpu_notifier_register_done();
return 0;
}
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index 713f1b3bad52..0331cb389d68 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -417,7 +417,6 @@ void kvm_disable_steal_time(void)
#ifdef CONFIG_SMP
static void __init kvm_smp_prepare_boot_cpu(void)
{
- WARN_ON(kvm_register_clock("primary cpu clock"));
kvm_guest_cpu_init();
native_smp_prepare_boot_cpu();
kvm_spinlock_init();
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index e6041094ff26..d9156ceecdff 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -242,7 +242,7 @@ void __init kvmclock_init(void)
hv_clock = __va(mem);
memset(hv_clock, 0, size);
- if (kvm_register_clock("boot clock")) {
+ if (kvm_register_clock("primary cpu clock")) {
hv_clock = NULL;
memblock_free(mem, size);
return;
diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c
index 05266b5aae22..c9603ac80de5 100644
--- a/arch/x86/kernel/msr.c
+++ b/arch/x86/kernel/msr.c
@@ -259,14 +259,15 @@ static int __init msr_init(void)
goto out_chrdev;
}
msr_class->devnode = msr_devnode;
- get_online_cpus();
+
+ cpu_notifier_register_begin();
for_each_online_cpu(i) {
err = msr_device_create(i);
if (err != 0)
goto out_class;
}
- register_hotcpu_notifier(&msr_class_cpu_notifier);
- put_online_cpus();
+ __register_hotcpu_notifier(&msr_class_cpu_notifier);
+ cpu_notifier_register_done();
err = 0;
goto out;
@@ -275,7 +276,7 @@ out_class:
i = 0;
for_each_online_cpu(i)
msr_device_destroy(i);
- put_online_cpus();
+ cpu_notifier_register_done();
class_destroy(msr_class);
out_chrdev:
__unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr");
@@ -286,13 +287,14 @@ out:
static void __exit msr_exit(void)
{
int cpu = 0;
- get_online_cpus();
+
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
msr_device_destroy(cpu);
class_destroy(msr_class);
__unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr");
- unregister_hotcpu_notifier(&msr_class_cpu_notifier);
- put_online_cpus();
+ __unregister_hotcpu_notifier(&msr_class_cpu_notifier);
+ cpu_notifier_register_done();
}
module_init(msr_init);
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c
index 9ea287666c65..8b3b3eb3cead 100644
--- a/arch/x86/kernel/vsyscall_64.c
+++ b/arch/x86/kernel/vsyscall_64.c
@@ -348,9 +348,13 @@ static int __init vsyscall_init(void)
{
BUG_ON(VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE));
+ cpu_notifier_register_begin();
+
on_each_cpu(cpu_vsyscall_init, NULL, 1);
/* notifier priority > KVM */
- hotcpu_notifier(cpu_vsyscall_notifier, 30);
+ __hotcpu_notifier(cpu_vsyscall_notifier, 30);
+
+ cpu_notifier_register_done();
return 0;
}
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index e5503d8aec1d..bea60671ef8a 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -28,7 +28,7 @@ static u32 xstate_required_size(u64 xstate_bv)
int feature_bit = 0;
u32 ret = XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET;
- xstate_bv &= ~XSTATE_FPSSE;
+ xstate_bv &= XSTATE_EXTEND_MASK;
while (xstate_bv) {
if (xstate_bv & 0x1) {
u32 eax, ebx, ecx, edx;
@@ -43,6 +43,16 @@ static u32 xstate_required_size(u64 xstate_bv)
return ret;
}
+u64 kvm_supported_xcr0(void)
+{
+ u64 xcr0 = KVM_SUPPORTED_XCR0 & host_xcr0;
+
+ if (!kvm_x86_ops->mpx_supported())
+ xcr0 &= ~(XSTATE_BNDREGS | XSTATE_BNDCSR);
+
+ return xcr0;
+}
+
void kvm_update_cpuid(struct kvm_vcpu *vcpu)
{
struct kvm_cpuid_entry2 *best;
@@ -73,9 +83,9 @@ void kvm_update_cpuid(struct kvm_vcpu *vcpu)
} else {
vcpu->arch.guest_supported_xcr0 =
(best->eax | ((u64)best->edx << 32)) &
- host_xcr0 & KVM_SUPPORTED_XCR0;
- vcpu->arch.guest_xstate_size =
- xstate_required_size(vcpu->arch.guest_supported_xcr0);
+ kvm_supported_xcr0();
+ vcpu->arch.guest_xstate_size = best->ebx =
+ xstate_required_size(vcpu->arch.xcr0);
}
kvm_pmu_cpuid_update(vcpu);
@@ -210,13 +220,6 @@ static void do_cpuid_1_ent(struct kvm_cpuid_entry2 *entry, u32 function,
entry->flags = 0;
}
-static bool supported_xcr0_bit(unsigned bit)
-{
- u64 mask = ((u64)1 << bit);
-
- return mask & KVM_SUPPORTED_XCR0 & host_xcr0;
-}
-
#define F(x) bit(X86_FEATURE_##x)
static int __do_cpuid_ent_emulated(struct kvm_cpuid_entry2 *entry,
@@ -256,6 +259,7 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
#endif
unsigned f_rdtscp = kvm_x86_ops->rdtscp_supported() ? F(RDTSCP) : 0;
unsigned f_invpcid = kvm_x86_ops->invpcid_supported() ? F(INVPCID) : 0;
+ unsigned f_mpx = kvm_x86_ops->mpx_supported() ? F(MPX) : 0;
/* cpuid 1.edx */
const u32 kvm_supported_word0_x86_features =
@@ -303,7 +307,8 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
/* cpuid 7.0.ebx */
const u32 kvm_supported_word9_x86_features =
F(FSGSBASE) | F(BMI1) | F(HLE) | F(AVX2) | F(SMEP) |
- F(BMI2) | F(ERMS) | f_invpcid | F(RTM);
+ F(BMI2) | F(ERMS) | f_invpcid | F(RTM) | f_mpx | F(RDSEED) |
+ F(ADX);
/* all calls to cpuid_count() should be made on the same cpu */
get_cpu();
@@ -436,16 +441,18 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
}
case 0xd: {
int idx, i;
+ u64 supported = kvm_supported_xcr0();
- entry->eax &= host_xcr0 & KVM_SUPPORTED_XCR0;
- entry->edx &= (host_xcr0 & KVM_SUPPORTED_XCR0) >> 32;
+ entry->eax &= supported;
+ entry->edx &= supported >> 32;
entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
for (idx = 1, i = 1; idx < 64; ++idx) {
+ u64 mask = ((u64)1 << idx);
if (*nent >= maxnent)
goto out;
do_cpuid_1_ent(&entry[i], function, idx);
- if (entry[i].eax == 0 || !supported_xcr0_bit(idx))
+ if (entry[i].eax == 0 || !(supported & mask))
continue;
entry[i].flags |=
KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 07ffca0a89e9..205b17eed93c 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -3668,6 +3668,10 @@ static const struct gprefix pfx_vmovntpx = {
I(0, em_mov), N, N, N,
};
+static const struct gprefix pfx_0f_28_0f_29 = {
+ I(Aligned, em_mov), I(Aligned, em_mov), N, N,
+};
+
static const struct escape escape_d9 = { {
N, N, N, N, N, N, N, I(DstMem, em_fnstcw),
}, {
@@ -3870,7 +3874,9 @@ static const struct opcode twobyte_table[256] = {
IIP(ModRM | SrcMem | Priv | Op3264, em_cr_write, cr_write, check_cr_write),
IIP(ModRM | SrcMem | Priv | Op3264, em_dr_write, dr_write, check_dr_write),
N, N, N, N,
- N, N, N, GP(ModRM | DstMem | SrcReg | Sse | Mov | Aligned, &pfx_vmovntpx),
+ GP(ModRM | DstReg | SrcMem | Mov | Sse, &pfx_0f_28_0f_29),
+ GP(ModRM | DstMem | SrcReg | Mov | Sse, &pfx_0f_28_0f_29),
+ N, GP(ModRM | DstMem | SrcReg | Sse | Mov | Aligned, &pfx_vmovntpx),
N, N, N, N,
/* 0x30 - 0x3F */
II(ImplicitOps | Priv, em_wrmsr, wrmsr),
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 9b531351a587..f5704d9e5ddc 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -3329,7 +3329,7 @@ static int kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn)
arch.direct_map = vcpu->arch.mmu.direct_map;
arch.cr3 = vcpu->arch.mmu.get_cr3(vcpu);
- return kvm_setup_async_pf(vcpu, gva, gfn, &arch);
+ return kvm_setup_async_pf(vcpu, gva, gfn_to_hva(vcpu->kvm, gfn), &arch);
}
static bool can_do_async_pf(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
index cba218a2f08d..b1e6c1bf68d3 100644
--- a/arch/x86/kvm/paging_tmpl.h
+++ b/arch/x86/kvm/paging_tmpl.h
@@ -913,7 +913,8 @@ static gpa_t FNAME(gva_to_gpa_nested)(struct kvm_vcpu *vcpu, gva_t vaddr,
* and kvm_mmu_notifier_invalidate_range_start detect the mapping page isn't
* used by guest then tlbs are not flushed, so guest is allowed to access the
* freed pages.
- * And we increase kvm->tlbs_dirty to delay tlbs flush in this case.
+ * We set tlbs_dirty to let the notifier know this change and delay the flush
+ * until such a case actually happens.
*/
static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
{
@@ -942,7 +943,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
return -EINVAL;
if (FNAME(prefetch_invalid_gpte)(vcpu, sp, &sp->spt[i], gpte)) {
- vcpu->kvm->tlbs_dirty++;
+ vcpu->kvm->tlbs_dirty = true;
continue;
}
@@ -957,7 +958,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
if (gfn != sp->gfns[i]) {
drop_spte(vcpu->kvm, &sp->spt[i]);
- vcpu->kvm->tlbs_dirty++;
+ vcpu->kvm->tlbs_dirty = true;
continue;
}
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 2de1bc09a8d4..7f4f9c2badae 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -34,6 +34,7 @@
#include <asm/perf_event.h>
#include <asm/tlbflush.h>
#include <asm/desc.h>
+#include <asm/debugreg.h>
#include <asm/kvm_para.h>
#include <asm/virtext.h>
@@ -303,20 +304,35 @@ static inline bool is_cr_intercept(struct vcpu_svm *svm, int bit)
return vmcb->control.intercept_cr & (1U << bit);
}
-static inline void set_dr_intercept(struct vcpu_svm *svm, int bit)
+static inline void set_dr_intercepts(struct vcpu_svm *svm)
{
struct vmcb *vmcb = get_host_vmcb(svm);
- vmcb->control.intercept_dr |= (1U << bit);
+ vmcb->control.intercept_dr = (1 << INTERCEPT_DR0_READ)
+ | (1 << INTERCEPT_DR1_READ)
+ | (1 << INTERCEPT_DR2_READ)
+ | (1 << INTERCEPT_DR3_READ)
+ | (1 << INTERCEPT_DR4_READ)
+ | (1 << INTERCEPT_DR5_READ)
+ | (1 << INTERCEPT_DR6_READ)
+ | (1 << INTERCEPT_DR7_READ)
+ | (1 << INTERCEPT_DR0_WRITE)
+ | (1 << INTERCEPT_DR1_WRITE)
+ | (1 << INTERCEPT_DR2_WRITE)
+ | (1 << INTERCEPT_DR3_WRITE)
+ | (1 << INTERCEPT_DR4_WRITE)
+ | (1 << INTERCEPT_DR5_WRITE)
+ | (1 << INTERCEPT_DR6_WRITE)
+ | (1 << INTERCEPT_DR7_WRITE);
recalc_intercepts(svm);
}
-static inline void clr_dr_intercept(struct vcpu_svm *svm, int bit)
+static inline void clr_dr_intercepts(struct vcpu_svm *svm)
{
struct vmcb *vmcb = get_host_vmcb(svm);
- vmcb->control.intercept_dr &= ~(1U << bit);
+ vmcb->control.intercept_dr = 0;
recalc_intercepts(svm);
}
@@ -1080,23 +1096,7 @@ static void init_vmcb(struct vcpu_svm *svm)
set_cr_intercept(svm, INTERCEPT_CR4_WRITE);
set_cr_intercept(svm, INTERCEPT_CR8_WRITE);
- set_dr_intercept(svm, INTERCEPT_DR0_READ);
- set_dr_intercept(svm, INTERCEPT_DR1_READ);
- set_dr_intercept(svm, INTERCEPT_DR2_READ);
- set_dr_intercept(svm, INTERCEPT_DR3_READ);
- set_dr_intercept(svm, INTERCEPT_DR4_READ);
- set_dr_intercept(svm, INTERCEPT_DR5_READ);
- set_dr_intercept(svm, INTERCEPT_DR6_READ);
- set_dr_intercept(svm, INTERCEPT_DR7_READ);
-
- set_dr_intercept(svm, INTERCEPT_DR0_WRITE);
- set_dr_intercept(svm, INTERCEPT_DR1_WRITE);
- set_dr_intercept(svm, INTERCEPT_DR2_WRITE);
- set_dr_intercept(svm, INTERCEPT_DR3_WRITE);
- set_dr_intercept(svm, INTERCEPT_DR4_WRITE);
- set_dr_intercept(svm, INTERCEPT_DR5_WRITE);
- set_dr_intercept(svm, INTERCEPT_DR6_WRITE);
- set_dr_intercept(svm, INTERCEPT_DR7_WRITE);
+ set_dr_intercepts(svm);
set_exception_intercept(svm, PF_VECTOR);
set_exception_intercept(svm, UD_VECTOR);
@@ -1684,6 +1684,21 @@ static void svm_set_dr6(struct kvm_vcpu *vcpu, unsigned long value)
mark_dirty(svm->vmcb, VMCB_DR);
}
+static void svm_sync_dirty_debug_regs(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+
+ get_debugreg(vcpu->arch.db[0], 0);
+ get_debugreg(vcpu->arch.db[1], 1);
+ get_debugreg(vcpu->arch.db[2], 2);
+ get_debugreg(vcpu->arch.db[3], 3);
+ vcpu->arch.dr6 = svm_get_dr6(vcpu);
+ vcpu->arch.dr7 = svm->vmcb->save.dr7;
+
+ vcpu->arch.switch_db_regs &= ~KVM_DEBUGREG_WONT_EXIT;
+ set_dr_intercepts(svm);
+}
+
static void svm_set_dr7(struct kvm_vcpu *vcpu, unsigned long value)
{
struct vcpu_svm *svm = to_svm(vcpu);
@@ -2842,6 +2857,7 @@ static int iret_interception(struct vcpu_svm *svm)
clr_intercept(svm, INTERCEPT_IRET);
svm->vcpu.arch.hflags |= HF_IRET_MASK;
svm->nmi_iret_rip = kvm_rip_read(&svm->vcpu);
+ kvm_make_request(KVM_REQ_EVENT, &svm->vcpu);
return 1;
}
@@ -2974,6 +2990,17 @@ static int dr_interception(struct vcpu_svm *svm)
unsigned long val;
int err;
+ if (svm->vcpu.guest_debug == 0) {
+ /*
+ * No more DR vmexits; force a reload of the debug registers
+ * and reenter on this instruction. The next vmexit will
+ * retrieve the full state of the debug registers.
+ */
+ clr_dr_intercepts(svm);
+ svm->vcpu.arch.switch_db_regs |= KVM_DEBUGREG_WONT_EXIT;
+ return 1;
+ }
+
if (!boot_cpu_has(X86_FEATURE_DECODEASSISTS))
return emulate_on_interception(svm);
@@ -3649,7 +3676,7 @@ static int svm_interrupt_allowed(struct kvm_vcpu *vcpu)
return ret;
}
-static int enable_irq_window(struct kvm_vcpu *vcpu)
+static void enable_irq_window(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
@@ -3663,16 +3690,15 @@ static int enable_irq_window(struct kvm_vcpu *vcpu)
svm_set_vintr(svm);
svm_inject_irq(svm, 0x0);
}
- return 0;
}
-static int enable_nmi_window(struct kvm_vcpu *vcpu)
+static void enable_nmi_window(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
if ((svm->vcpu.arch.hflags & (HF_NMI_MASK | HF_IRET_MASK))
== HF_NMI_MASK)
- return 0; /* IRET will cause a vm exit */
+ return; /* IRET will cause a vm exit */
/*
* Something prevents NMI from been injected. Single step over possible
@@ -3681,7 +3707,6 @@ static int enable_nmi_window(struct kvm_vcpu *vcpu)
svm->nmi_singlestep = true;
svm->vmcb->save.rflags |= (X86_EFLAGS_TF | X86_EFLAGS_RF);
update_db_bp_intercept(vcpu);
- return 0;
}
static int svm_set_tss_addr(struct kvm *kvm, unsigned int addr)
@@ -4064,6 +4089,11 @@ static bool svm_invpcid_supported(void)
return false;
}
+static bool svm_mpx_supported(void)
+{
+ return false;
+}
+
static bool svm_has_wbinvd_exit(void)
{
return true;
@@ -4302,6 +4332,7 @@ static struct kvm_x86_ops svm_x86_ops = {
.get_dr6 = svm_get_dr6,
.set_dr6 = svm_set_dr6,
.set_dr7 = svm_set_dr7,
+ .sync_dirty_debug_regs = svm_sync_dirty_debug_regs,
.cache_reg = svm_cache_reg,
.get_rflags = svm_get_rflags,
.set_rflags = svm_set_rflags,
@@ -4345,6 +4376,7 @@ static struct kvm_x86_ops svm_x86_ops = {
.rdtscp_supported = svm_rdtscp_supported,
.invpcid_supported = svm_invpcid_supported,
+ .mpx_supported = svm_mpx_supported,
.set_supported_cpuid = svm_set_supported_cpuid,
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 392752834751..1320e0f8e611 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -31,6 +31,7 @@
#include <linux/ftrace_event.h>
#include <linux/slab.h>
#include <linux/tboot.h>
+#include <linux/hrtimer.h>
#include "kvm_cache_regs.h"
#include "x86.h"
@@ -42,6 +43,7 @@
#include <asm/i387.h>
#include <asm/xcr.h>
#include <asm/perf_event.h>
+#include <asm/debugreg.h>
#include <asm/kexec.h>
#include "trace.h"
@@ -110,6 +112,8 @@ module_param(nested, bool, S_IRUGO);
#define RMODE_GUEST_OWNED_EFLAGS_BITS (~(X86_EFLAGS_IOPL | X86_EFLAGS_VM))
+#define VMX_MISC_EMULATED_PREEMPTION_TIMER_RATE 5
+
/*
* These 2 parameters are used to config the controls for Pause-Loop Exiting:
* ple_gap: upper bound on the amount of time between two successive
@@ -202,6 +206,7 @@ struct __packed vmcs12 {
u64 guest_pdptr1;
u64 guest_pdptr2;
u64 guest_pdptr3;
+ u64 guest_bndcfgs;
u64 host_ia32_pat;
u64 host_ia32_efer;
u64 host_ia32_perf_global_ctrl;
@@ -374,6 +379,9 @@ struct nested_vmx {
*/
struct page *apic_access_page;
u64 msr_ia32_feature_control;
+
+ struct hrtimer preemption_timer;
+ bool preemption_timer_expired;
};
#define POSTED_INTR_ON 0
@@ -441,6 +449,7 @@ struct vcpu_vmx {
#endif
int gs_ldt_reload_needed;
int fs_reload_needed;
+ u64 msr_host_bndcfgs;
} host_state;
struct {
int vm86_active;
@@ -533,6 +542,7 @@ static const unsigned long shadow_read_write_fields[] = {
GUEST_CS_LIMIT,
GUEST_CS_BASE,
GUEST_ES_BASE,
+ GUEST_BNDCFGS,
CR0_GUEST_HOST_MASK,
CR0_READ_SHADOW,
CR4_READ_SHADOW,
@@ -588,6 +598,7 @@ static const unsigned short vmcs_field_to_offset_table[] = {
FIELD64(GUEST_PDPTR1, guest_pdptr1),
FIELD64(GUEST_PDPTR2, guest_pdptr2),
FIELD64(GUEST_PDPTR3, guest_pdptr3),
+ FIELD64(GUEST_BNDCFGS, guest_bndcfgs),
FIELD64(HOST_IA32_PAT, host_ia32_pat),
FIELD64(HOST_IA32_EFER, host_ia32_efer),
FIELD64(HOST_IA32_PERF_GLOBAL_CTRL, host_ia32_perf_global_ctrl),
@@ -718,6 +729,7 @@ static unsigned long nested_ept_get_cr3(struct kvm_vcpu *vcpu);
static u64 construct_eptp(unsigned long root_hpa);
static void kvm_cpu_vmxon(u64 addr);
static void kvm_cpu_vmxoff(void);
+static bool vmx_mpx_supported(void);
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);
@@ -728,6 +740,7 @@ 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 bool vmx_mpx_supported(void);
static DEFINE_PER_CPU(struct vmcs *, vmxarea);
static DEFINE_PER_CPU(struct vmcs *, current_vmcs);
@@ -1047,6 +1060,12 @@ static inline bool nested_cpu_has_virtual_nmis(struct vmcs12 *vmcs12)
return vmcs12->pin_based_vm_exec_control & PIN_BASED_VIRTUAL_NMIS;
}
+static inline bool nested_cpu_has_preemption_timer(struct vmcs12 *vmcs12)
+{
+ return vmcs12->pin_based_vm_exec_control &
+ PIN_BASED_VMX_PREEMPTION_TIMER;
+}
+
static inline int nested_cpu_has_ept(struct vmcs12 *vmcs12)
{
return nested_cpu_has2(vmcs12, SECONDARY_EXEC_ENABLE_EPT);
@@ -1710,6 +1729,8 @@ static void vmx_save_host_state(struct kvm_vcpu *vcpu)
if (is_long_mode(&vmx->vcpu))
wrmsrl(MSR_KERNEL_GS_BASE, vmx->msr_guest_kernel_gs_base);
#endif
+ if (boot_cpu_has(X86_FEATURE_MPX))
+ rdmsrl(MSR_IA32_BNDCFGS, vmx->host_state.msr_host_bndcfgs);
for (i = 0; i < vmx->save_nmsrs; ++i)
kvm_set_shared_msr(vmx->guest_msrs[i].index,
vmx->guest_msrs[i].data,
@@ -1747,6 +1768,8 @@ static void __vmx_load_host_state(struct vcpu_vmx *vmx)
#ifdef CONFIG_X86_64
wrmsrl(MSR_KERNEL_GS_BASE, vmx->msr_host_kernel_gs_base);
#endif
+ if (vmx->host_state.msr_host_bndcfgs)
+ wrmsrl(MSR_IA32_BNDCFGS, vmx->host_state.msr_host_bndcfgs);
/*
* If the FPU is not active (through the host task or
* the guest vcpu), then restore the cr0.TS bit.
@@ -2248,9 +2271,9 @@ static __init void nested_vmx_setup_ctls_msrs(void)
*/
nested_vmx_pinbased_ctls_low |= PIN_BASED_ALWAYSON_WITHOUT_TRUE_MSR;
nested_vmx_pinbased_ctls_high &= PIN_BASED_EXT_INTR_MASK |
- PIN_BASED_NMI_EXITING | PIN_BASED_VIRTUAL_NMIS |
+ PIN_BASED_NMI_EXITING | PIN_BASED_VIRTUAL_NMIS;
+ nested_vmx_pinbased_ctls_high |= PIN_BASED_ALWAYSON_WITHOUT_TRUE_MSR |
PIN_BASED_VMX_PREEMPTION_TIMER;
- nested_vmx_pinbased_ctls_high |= PIN_BASED_ALWAYSON_WITHOUT_TRUE_MSR;
/*
* Exit controls
@@ -2265,15 +2288,12 @@ static __init void nested_vmx_setup_ctls_msrs(void)
#ifdef CONFIG_X86_64
VM_EXIT_HOST_ADDR_SPACE_SIZE |
#endif
- VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT |
+ VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT;
+ nested_vmx_exit_ctls_high |= VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR |
+ VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER |
VM_EXIT_SAVE_VMX_PREEMPTION_TIMER;
- if (!(nested_vmx_pinbased_ctls_high & PIN_BASED_VMX_PREEMPTION_TIMER) ||
- !(nested_vmx_exit_ctls_high & VM_EXIT_SAVE_VMX_PREEMPTION_TIMER)) {
- nested_vmx_exit_ctls_high &= ~VM_EXIT_SAVE_VMX_PREEMPTION_TIMER;
- nested_vmx_pinbased_ctls_high &= ~PIN_BASED_VMX_PREEMPTION_TIMER;
- }
- nested_vmx_exit_ctls_high |= (VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR |
- VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER);
+ if (vmx_mpx_supported())
+ nested_vmx_exit_ctls_high |= VM_EXIT_CLEAR_BNDCFGS;
/* entry controls */
rdmsr(MSR_IA32_VMX_ENTRY_CTLS,
@@ -2287,6 +2307,8 @@ static __init void nested_vmx_setup_ctls_msrs(void)
VM_ENTRY_LOAD_IA32_PAT;
nested_vmx_entry_ctls_high |= (VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR |
VM_ENTRY_LOAD_IA32_EFER);
+ if (vmx_mpx_supported())
+ nested_vmx_entry_ctls_high |= VM_ENTRY_LOAD_BNDCFGS;
/* cpu-based controls */
rdmsr(MSR_IA32_VMX_PROCBASED_CTLS,
@@ -2342,9 +2364,9 @@ static __init void nested_vmx_setup_ctls_msrs(void)
/* miscellaneous data */
rdmsr(MSR_IA32_VMX_MISC, nested_vmx_misc_low, nested_vmx_misc_high);
- nested_vmx_misc_low &= VMX_MISC_PREEMPTION_TIMER_RATE_MASK |
- VMX_MISC_SAVE_EFER_LMA;
- nested_vmx_misc_low |= VMX_MISC_ACTIVITY_HLT;
+ nested_vmx_misc_low &= VMX_MISC_SAVE_EFER_LMA;
+ nested_vmx_misc_low |= VMX_MISC_EMULATED_PREEMPTION_TIMER_RATE |
+ VMX_MISC_ACTIVITY_HLT;
nested_vmx_misc_high = 0;
}
@@ -2479,6 +2501,11 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
case MSR_IA32_SYSENTER_ESP:
data = vmcs_readl(GUEST_SYSENTER_ESP);
break;
+ case MSR_IA32_BNDCFGS:
+ if (!vmx_mpx_supported())
+ return 1;
+ data = vmcs_read64(GUEST_BNDCFGS);
+ break;
case MSR_IA32_FEATURE_CONTROL:
if (!nested_vmx_allowed(vcpu))
return 1;
@@ -2547,6 +2574,11 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
case MSR_IA32_SYSENTER_ESP:
vmcs_writel(GUEST_SYSENTER_ESP, data);
break;
+ case MSR_IA32_BNDCFGS:
+ if (!vmx_mpx_supported())
+ return 1;
+ vmcs_write64(GUEST_BNDCFGS, data);
+ break;
case MSR_IA32_TSC:
kvm_write_tsc(vcpu, msr_info);
break;
@@ -2832,12 +2864,12 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
vmx_capability.ept, vmx_capability.vpid);
}
- min = 0;
+ min = VM_EXIT_SAVE_DEBUG_CONTROLS;
#ifdef CONFIG_X86_64
min |= VM_EXIT_HOST_ADDR_SPACE_SIZE;
#endif
opt = VM_EXIT_SAVE_IA32_PAT | VM_EXIT_LOAD_IA32_PAT |
- VM_EXIT_ACK_INTR_ON_EXIT;
+ VM_EXIT_ACK_INTR_ON_EXIT | VM_EXIT_CLEAR_BNDCFGS;
if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_EXIT_CTLS,
&_vmexit_control) < 0)
return -EIO;
@@ -2853,8 +2885,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
!(_vmexit_control & VM_EXIT_ACK_INTR_ON_EXIT))
_pin_based_exec_control &= ~PIN_BASED_POSTED_INTR;
- min = 0;
- opt = VM_ENTRY_LOAD_IA32_PAT;
+ min = VM_ENTRY_LOAD_DEBUG_CONTROLS;
+ opt = VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_BNDCFGS;
if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_ENTRY_CTLS,
&_vmentry_control) < 0)
return -EIO;
@@ -4223,6 +4255,10 @@ static u32 vmx_pin_based_exec_ctrl(struct vcpu_vmx *vmx)
static u32 vmx_exec_control(struct vcpu_vmx *vmx)
{
u32 exec_control = vmcs_config.cpu_based_exec_ctrl;
+
+ if (vmx->vcpu.arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT)
+ exec_control &= ~CPU_BASED_MOV_DR_EXITING;
+
if (!vm_need_tpr_shadow(vmx->vcpu.kvm)) {
exec_control &= ~CPU_BASED_TPR_SHADOW;
#ifdef CONFIG_X86_64
@@ -4496,39 +4532,28 @@ static bool nested_exit_on_nmi(struct kvm_vcpu *vcpu)
PIN_BASED_NMI_EXITING;
}
-static int enable_irq_window(struct kvm_vcpu *vcpu)
+static void enable_irq_window(struct kvm_vcpu *vcpu)
{
u32 cpu_based_vm_exec_control;
- if (is_guest_mode(vcpu) && nested_exit_on_intr(vcpu))
- /*
- * We get here if vmx_interrupt_allowed() said we can't
- * inject to L1 now because L2 must run. The caller will have
- * to make L2 exit right after entry, so we can inject to L1
- * more promptly.
- */
- return -EBUSY;
-
cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
cpu_based_vm_exec_control |= CPU_BASED_VIRTUAL_INTR_PENDING;
vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);
- return 0;
}
-static int enable_nmi_window(struct kvm_vcpu *vcpu)
+static void enable_nmi_window(struct kvm_vcpu *vcpu)
{
u32 cpu_based_vm_exec_control;
- if (!cpu_has_virtual_nmis())
- return enable_irq_window(vcpu);
-
- if (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & GUEST_INTR_STATE_STI)
- return enable_irq_window(vcpu);
+ if (!cpu_has_virtual_nmis() ||
+ vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & GUEST_INTR_STATE_STI) {
+ enable_irq_window(vcpu);
+ return;
+ }
cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
cpu_based_vm_exec_control |= CPU_BASED_VIRTUAL_NMI_PENDING;
vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);
- return 0;
}
static void vmx_inject_irq(struct kvm_vcpu *vcpu)
@@ -4620,22 +4645,8 @@ static void vmx_set_nmi_mask(struct kvm_vcpu *vcpu, bool masked)
static int vmx_nmi_allowed(struct kvm_vcpu *vcpu)
{
- if (is_guest_mode(vcpu)) {
- if (to_vmx(vcpu)->nested.nested_run_pending)
- return 0;
- if (nested_exit_on_nmi(vcpu)) {
- nested_vmx_vmexit(vcpu, EXIT_REASON_EXCEPTION_NMI,
- NMI_VECTOR | INTR_TYPE_NMI_INTR |
- INTR_INFO_VALID_MASK, 0);
- /*
- * The NMI-triggered VM exit counts as injection:
- * clear this one and block further NMIs.
- */
- vcpu->arch.nmi_pending = 0;
- vmx_set_nmi_mask(vcpu, true);
- return 0;
- }
- }
+ if (to_vmx(vcpu)->nested.nested_run_pending)
+ return 0;
if (!cpu_has_virtual_nmis() && to_vmx(vcpu)->soft_vnmi_blocked)
return 0;
@@ -4647,19 +4658,8 @@ static int vmx_nmi_allowed(struct kvm_vcpu *vcpu)
static int vmx_interrupt_allowed(struct kvm_vcpu *vcpu)
{
- if (is_guest_mode(vcpu)) {
- if (to_vmx(vcpu)->nested.nested_run_pending)
- return 0;
- if (nested_exit_on_intr(vcpu)) {
- nested_vmx_vmexit(vcpu, EXIT_REASON_EXTERNAL_INTERRUPT,
- 0, 0);
- /*
- * fall through to normal code, but now in L1, not L2
- */
- }
- }
-
- return (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) &&
+ return (!to_vmx(vcpu)->nested.nested_run_pending &&
+ vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) &&
!(vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) &
(GUEST_INTR_STATE_STI | GUEST_INTR_STATE_MOV_SS));
}
@@ -5102,6 +5102,22 @@ static int handle_dr(struct kvm_vcpu *vcpu)
}
}
+ if (vcpu->guest_debug == 0) {
+ u32 cpu_based_vm_exec_control;
+
+ cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
+ cpu_based_vm_exec_control &= ~CPU_BASED_MOV_DR_EXITING;
+ vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);
+
+ /*
+ * No more DR vmexits; force a reload of the debug registers
+ * and reenter on this instruction. The next vmexit will
+ * retrieve the full state of the debug registers.
+ */
+ vcpu->arch.switch_db_regs |= KVM_DEBUGREG_WONT_EXIT;
+ return 1;
+ }
+
exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
dr = exit_qualification & DEBUG_REG_ACCESS_NUM;
reg = DEBUG_REG_ACCESS_REG(exit_qualification);
@@ -5128,6 +5144,24 @@ static void vmx_set_dr6(struct kvm_vcpu *vcpu, unsigned long val)
{
}
+static void vmx_sync_dirty_debug_regs(struct kvm_vcpu *vcpu)
+{
+ u32 cpu_based_vm_exec_control;
+
+ get_debugreg(vcpu->arch.db[0], 0);
+ get_debugreg(vcpu->arch.db[1], 1);
+ get_debugreg(vcpu->arch.db[2], 2);
+ get_debugreg(vcpu->arch.db[3], 3);
+ get_debugreg(vcpu->arch.dr6, 6);
+ vcpu->arch.dr7 = vmcs_readl(GUEST_DR7);
+
+ vcpu->arch.switch_db_regs &= ~KVM_DEBUGREG_WONT_EXIT;
+
+ cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
+ cpu_based_vm_exec_control |= CPU_BASED_MOV_DR_EXITING;
+ vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);
+}
+
static void vmx_set_dr7(struct kvm_vcpu *vcpu, unsigned long val)
{
vmcs_writel(GUEST_DR7, val);
@@ -5727,6 +5761,18 @@ static void nested_vmx_failValid(struct kvm_vcpu *vcpu,
*/
}
+static enum hrtimer_restart vmx_preemption_timer_fn(struct hrtimer *timer)
+{
+ struct vcpu_vmx *vmx =
+ container_of(timer, struct vcpu_vmx, nested.preemption_timer);
+
+ vmx->nested.preemption_timer_expired = true;
+ kvm_make_request(KVM_REQ_EVENT, &vmx->vcpu);
+ kvm_vcpu_kick(&vmx->vcpu);
+
+ return HRTIMER_NORESTART;
+}
+
/*
* Emulate the VMXON instruction.
* Currently, we just remember that VMX is active, and do not save or even
@@ -5791,6 +5837,10 @@ static int handle_vmon(struct kvm_vcpu *vcpu)
INIT_LIST_HEAD(&(vmx->nested.vmcs02_pool));
vmx->nested.vmcs02_num = 0;
+ hrtimer_init(&vmx->nested.preemption_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ vmx->nested.preemption_timer.function = vmx_preemption_timer_fn;
+
vmx->nested.vmxon = true;
skip_emulated_instruction(vcpu);
@@ -6767,9 +6817,6 @@ static bool nested_vmx_exit_handled(struct kvm_vcpu *vcpu)
* table is L0's fault.
*/
return 0;
- case EXIT_REASON_PREEMPTION_TIMER:
- return vmcs12->pin_based_vm_exec_control &
- PIN_BASED_VMX_PREEMPTION_TIMER;
case EXIT_REASON_WBINVD:
return nested_cpu_has2(vmcs12, SECONDARY_EXEC_WBINVD_EXITING);
case EXIT_REASON_XSETBV:
@@ -6785,27 +6832,6 @@ static void vmx_get_exit_info(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2)
*info2 = vmcs_read32(VM_EXIT_INTR_INFO);
}
-static void nested_adjust_preemption_timer(struct kvm_vcpu *vcpu)
-{
- u64 delta_tsc_l1;
- u32 preempt_val_l1, preempt_val_l2, preempt_scale;
-
- if (!(get_vmcs12(vcpu)->pin_based_vm_exec_control &
- PIN_BASED_VMX_PREEMPTION_TIMER))
- return;
- preempt_scale = native_read_msr(MSR_IA32_VMX_MISC) &
- MSR_IA32_VMX_MISC_PREEMPTION_TIMER_SCALE;
- preempt_val_l2 = vmcs_read32(VMX_PREEMPTION_TIMER_VALUE);
- delta_tsc_l1 = vmx_read_l1_tsc(vcpu, native_read_tsc())
- - vcpu->arch.last_guest_tsc;
- preempt_val_l1 = delta_tsc_l1 >> preempt_scale;
- if (preempt_val_l2 <= preempt_val_l1)
- preempt_val_l2 = 0;
- else
- preempt_val_l2 -= preempt_val_l1;
- vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, preempt_val_l2);
-}
-
/*
* The guest has exited. See if we can fix it or if we need userspace
* assistance.
@@ -7052,6 +7078,12 @@ static void vmx_handle_external_intr(struct kvm_vcpu *vcpu)
local_irq_enable();
}
+static bool vmx_mpx_supported(void)
+{
+ return (vmcs_config.vmexit_ctrl & VM_EXIT_CLEAR_BNDCFGS) &&
+ (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_BNDCFGS);
+}
+
static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx)
{
u32 exit_intr_info;
@@ -7218,8 +7250,6 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
atomic_switch_perf_msrs(vmx);
debugctlmsr = get_debugctlmsr();
- if (is_guest_mode(vcpu) && !vmx->nested.nested_run_pending)
- nested_adjust_preemption_timer(vcpu);
vmx->__launched = vmx->loaded_vmcs->launched;
asm(
/* Store host registers */
@@ -7616,6 +7646,28 @@ static void vmx_inject_page_fault_nested(struct kvm_vcpu *vcpu,
kvm_inject_page_fault(vcpu, fault);
}
+static void vmx_start_preemption_timer(struct kvm_vcpu *vcpu)
+{
+ u64 preemption_timeout = get_vmcs12(vcpu)->vmx_preemption_timer_value;
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
+
+ if (vcpu->arch.virtual_tsc_khz == 0)
+ return;
+
+ /* Make sure short timeouts reliably trigger an immediate vmexit.
+ * hrtimer_start does not guarantee this. */
+ if (preemption_timeout <= 1) {
+ vmx_preemption_timer_fn(&vmx->nested.preemption_timer);
+ return;
+ }
+
+ preemption_timeout <<= VMX_MISC_EMULATED_PREEMPTION_TIMER_RATE;
+ preemption_timeout *= 1000000;
+ do_div(preemption_timeout, vcpu->arch.virtual_tsc_khz);
+ hrtimer_start(&vmx->nested.preemption_timer,
+ ns_to_ktime(preemption_timeout), HRTIMER_MODE_REL);
+}
+
/*
* prepare_vmcs02 is called when the L1 guest hypervisor runs its nested
* L2 guest. L1 has a vmcs for L2 (vmcs12), and this function "merges" it
@@ -7629,7 +7681,6 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
u32 exec_control;
- u32 exit_control;
vmcs_write16(GUEST_ES_SELECTOR, vmcs12->guest_es_selector);
vmcs_write16(GUEST_CS_SELECTOR, vmcs12->guest_cs_selector);
@@ -7687,13 +7738,14 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
vmcs_write64(VMCS_LINK_POINTER, -1ull);
- vmcs_write32(PIN_BASED_VM_EXEC_CONTROL,
- (vmcs_config.pin_based_exec_ctrl |
- vmcs12->pin_based_vm_exec_control));
+ exec_control = vmcs12->pin_based_vm_exec_control;
+ exec_control |= vmcs_config.pin_based_exec_ctrl;
+ exec_control &= ~PIN_BASED_VMX_PREEMPTION_TIMER;
+ vmcs_write32(PIN_BASED_VM_EXEC_CONTROL, exec_control);
- if (vmcs12->pin_based_vm_exec_control & PIN_BASED_VMX_PREEMPTION_TIMER)
- vmcs_write32(VMX_PREEMPTION_TIMER_VALUE,
- vmcs12->vmx_preemption_timer_value);
+ vmx->nested.preemption_timer_expired = false;
+ if (nested_cpu_has_preemption_timer(vmcs12))
+ vmx_start_preemption_timer(vcpu);
/*
* Whether page-faults are trapped is determined by a combination of
@@ -7721,7 +7773,7 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
enable_ept ? vmcs12->page_fault_error_code_match : 0);
if (cpu_has_secondary_exec_ctrls()) {
- u32 exec_control = vmx_secondary_exec_control(vmx);
+ exec_control = vmx_secondary_exec_control(vmx);
if (!vmx->rdtscp_enabled)
exec_control &= ~SECONDARY_EXEC_RDTSCP;
/* Take the following fields only from vmcs12 */
@@ -7808,10 +7860,7 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
* we should use its exit controls. Note that VM_EXIT_LOAD_IA32_EFER
* bits are further modified by vmx_set_efer() below.
*/
- exit_control = vmcs_config.vmexit_ctrl;
- if (vmcs12->pin_based_vm_exec_control & PIN_BASED_VMX_PREEMPTION_TIMER)
- exit_control |= VM_EXIT_SAVE_VMX_PREEMPTION_TIMER;
- vm_exit_controls_init(vmx, exit_control);
+ vmcs_write32(VM_EXIT_CONTROLS, vmcs_config.vmexit_ctrl);
/* vmcs12's VM_ENTRY_LOAD_IA32_EFER and VM_ENTRY_IA32E_MODE are
* emulated by vmx_set_efer(), below.
@@ -7830,6 +7879,9 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
set_cr4_guest_host_mask(vmx);
+ if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS)
+ vmcs_write64(GUEST_BNDCFGS, vmcs12->guest_bndcfgs);
+
if (vmcs12->cpu_based_vm_exec_control & CPU_BASED_USE_TSC_OFFSETING)
vmcs_write64(TSC_OFFSET,
vmx->nested.vmcs01_tsc_offset + vmcs12->tsc_offset);
@@ -8155,6 +8207,58 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu,
}
}
+static int vmx_check_nested_events(struct kvm_vcpu *vcpu, bool external_intr)
+{
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
+
+ if (nested_cpu_has_preemption_timer(get_vmcs12(vcpu)) &&
+ vmx->nested.preemption_timer_expired) {
+ if (vmx->nested.nested_run_pending)
+ return -EBUSY;
+ nested_vmx_vmexit(vcpu, EXIT_REASON_PREEMPTION_TIMER, 0, 0);
+ return 0;
+ }
+
+ if (vcpu->arch.nmi_pending && nested_exit_on_nmi(vcpu)) {
+ if (vmx->nested.nested_run_pending ||
+ vcpu->arch.interrupt.pending)
+ return -EBUSY;
+ nested_vmx_vmexit(vcpu, EXIT_REASON_EXCEPTION_NMI,
+ NMI_VECTOR | INTR_TYPE_NMI_INTR |
+ INTR_INFO_VALID_MASK, 0);
+ /*
+ * The NMI-triggered VM exit counts as injection:
+ * clear this one and block further NMIs.
+ */
+ vcpu->arch.nmi_pending = 0;
+ vmx_set_nmi_mask(vcpu, true);
+ return 0;
+ }
+
+ if ((kvm_cpu_has_interrupt(vcpu) || external_intr) &&
+ nested_exit_on_intr(vcpu)) {
+ if (vmx->nested.nested_run_pending)
+ return -EBUSY;
+ nested_vmx_vmexit(vcpu, EXIT_REASON_EXTERNAL_INTERRUPT, 0, 0);
+ }
+
+ return 0;
+}
+
+static u32 vmx_get_preemption_timer_value(struct kvm_vcpu *vcpu)
+{
+ ktime_t remaining =
+ hrtimer_get_remaining(&to_vmx(vcpu)->nested.preemption_timer);
+ u64 value;
+
+ if (ktime_to_ns(remaining) <= 0)
+ return 0;
+
+ value = ktime_to_ns(remaining) * vcpu->arch.virtual_tsc_khz;
+ do_div(value, 1000000);
+ return value >> VMX_MISC_EMULATED_PREEMPTION_TIMER_RATE;
+}
+
/*
* prepare_vmcs12 is part of what we need to do when the nested L2 guest exits
* and we want to prepare to run its L1 parent. L1 keeps a vmcs for L2 (vmcs12),
@@ -8225,10 +8329,13 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
else
vmcs12->guest_activity_state = GUEST_ACTIVITY_ACTIVE;
- if ((vmcs12->pin_based_vm_exec_control & PIN_BASED_VMX_PREEMPTION_TIMER) &&
- (vmcs12->vm_exit_controls & VM_EXIT_SAVE_VMX_PREEMPTION_TIMER))
- vmcs12->vmx_preemption_timer_value =
- vmcs_read32(VMX_PREEMPTION_TIMER_VALUE);
+ if (nested_cpu_has_preemption_timer(vmcs12)) {
+ if (vmcs12->vm_exit_controls &
+ VM_EXIT_SAVE_VMX_PREEMPTION_TIMER)
+ vmcs12->vmx_preemption_timer_value =
+ vmx_get_preemption_timer_value(vcpu);
+ hrtimer_cancel(&to_vmx(vcpu)->nested.preemption_timer);
+ }
/*
* In some cases (usually, nested EPT), L2 is allowed to change its
@@ -8260,6 +8367,8 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
vmcs12->guest_sysenter_cs = vmcs_read32(GUEST_SYSENTER_CS);
vmcs12->guest_sysenter_esp = vmcs_readl(GUEST_SYSENTER_ESP);
vmcs12->guest_sysenter_eip = vmcs_readl(GUEST_SYSENTER_EIP);
+ if (vmx_mpx_supported())
+ vmcs12->guest_bndcfgs = vmcs_read64(GUEST_BNDCFGS);
/* update exit information fields: */
@@ -8369,6 +8478,10 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu,
vmcs_writel(GUEST_IDTR_BASE, vmcs12->host_idtr_base);
vmcs_writel(GUEST_GDTR_BASE, vmcs12->host_gdtr_base);
+ /* If not VM_EXIT_CLEAR_BNDCFGS, the L2 value propagates to L1. */
+ if (vmcs12->vm_exit_controls & VM_EXIT_CLEAR_BNDCFGS)
+ vmcs_write64(GUEST_BNDCFGS, 0);
+
if (vmcs12->vm_exit_controls & VM_EXIT_LOAD_IA32_PAT) {
vmcs_write64(GUEST_IA32_PAT, vmcs12->host_ia32_pat);
vcpu->arch.pat = vmcs12->host_ia32_pat;
@@ -8495,6 +8608,9 @@ static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason,
nested_vmx_succeed(vcpu);
if (enable_shadow_vmcs)
vmx->nested.sync_shadow_vmcs = true;
+
+ /* in case we halted in L2 */
+ vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
}
/*
@@ -8573,6 +8689,7 @@ static struct kvm_x86_ops vmx_x86_ops = {
.get_dr6 = vmx_get_dr6,
.set_dr6 = vmx_set_dr6,
.set_dr7 = vmx_set_dr7,
+ .sync_dirty_debug_regs = vmx_sync_dirty_debug_regs,
.cache_reg = vmx_cache_reg,
.get_rflags = vmx_get_rflags,
.set_rflags = vmx_set_rflags,
@@ -8634,6 +8751,9 @@ static struct kvm_x86_ops vmx_x86_ops = {
.check_intercept = vmx_check_intercept,
.handle_external_intr = vmx_handle_external_intr,
+ .mpx_supported = vmx_mpx_supported,
+
+ .check_nested_events = vmx_check_nested_events,
};
static int __init vmx_init(void)
@@ -8721,6 +8841,8 @@ static int __init vmx_init(void)
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_CS, false);
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_ESP, false);
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_EIP, false);
+ vmx_disable_intercept_for_msr(MSR_IA32_BNDCFGS, true);
+
memcpy(vmx_msr_bitmap_legacy_x2apic,
vmx_msr_bitmap_legacy, PAGE_SIZE);
memcpy(vmx_msr_bitmap_longmode_x2apic,
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 2b8578432d5b..9d1b5cd4d34c 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -595,13 +595,13 @@ static void kvm_put_guest_xcr0(struct kvm_vcpu *vcpu)
int __kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr)
{
- u64 xcr0;
+ u64 xcr0 = xcr;
+ u64 old_xcr0 = vcpu->arch.xcr0;
u64 valid_bits;
/* Only support XCR_XFEATURE_ENABLED_MASK(xcr0) now */
if (index != XCR_XFEATURE_ENABLED_MASK)
return 1;
- xcr0 = xcr;
if (!(xcr0 & XSTATE_FP))
return 1;
if ((xcr0 & XSTATE_YMM) && !(xcr0 & XSTATE_SSE))
@@ -616,8 +616,14 @@ int __kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr)
if (xcr0 & ~valid_bits)
return 1;
+ if ((!(xcr0 & XSTATE_BNDREGS)) != (!(xcr0 & XSTATE_BNDCSR)))
+ return 1;
+
kvm_put_guest_xcr0(vcpu);
vcpu->arch.xcr0 = xcr0;
+
+ if ((xcr0 ^ old_xcr0) & XSTATE_EXTEND_MASK)
+ kvm_update_cpuid(vcpu);
return 0;
}
@@ -753,7 +759,9 @@ static void kvm_update_dr7(struct kvm_vcpu *vcpu)
else
dr7 = vcpu->arch.dr7;
kvm_x86_ops->set_dr7(vcpu, dr7);
- vcpu->arch.switch_db_regs = (dr7 & DR7_BP_EN_MASK);
+ vcpu->arch.switch_db_regs &= ~KVM_DEBUGREG_BP_ENABLED;
+ if (dr7 & DR7_BP_EN_MASK)
+ vcpu->arch.switch_db_regs |= KVM_DEBUGREG_BP_ENABLED;
}
static int __kvm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long val)
@@ -879,7 +887,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_FEATURE_CONTROL, MSR_IA32_BNDCFGS
};
static unsigned num_msrs_to_save;
@@ -1581,7 +1589,6 @@ static int kvm_guest_time_update(struct kvm_vcpu *v)
/* With all the info we got, fill in the values */
vcpu->hv_clock.tsc_timestamp = tsc_timestamp;
vcpu->hv_clock.system_time = kernel_ns + v->kvm->arch.kvmclock_offset;
- vcpu->last_kernel_ns = kernel_ns;
vcpu->last_guest_tsc = tsc_timestamp;
/*
@@ -1623,14 +1630,21 @@ static int kvm_guest_time_update(struct kvm_vcpu *v)
* the others.
*
* So in those cases, request a kvmclock update for all vcpus.
- * The worst case for a remote vcpu to update its kvmclock
- * is then bounded by maximum nohz sleep latency.
+ * We need to rate-limit these requests though, as they can
+ * considerably slow guests that have a large number of vcpus.
+ * The time for a remote vcpu to update its kvmclock is bound
+ * by the delay we use to rate-limit the updates.
*/
-static void kvm_gen_kvmclock_update(struct kvm_vcpu *v)
+#define KVMCLOCK_UPDATE_DELAY msecs_to_jiffies(100)
+
+static void kvmclock_update_fn(struct work_struct *work)
{
int i;
- struct kvm *kvm = v->kvm;
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct kvm_arch *ka = container_of(dwork, struct kvm_arch,
+ kvmclock_update_work);
+ struct kvm *kvm = container_of(ka, struct kvm, arch);
struct kvm_vcpu *vcpu;
kvm_for_each_vcpu(i, vcpu, kvm) {
@@ -1639,6 +1653,29 @@ static void kvm_gen_kvmclock_update(struct kvm_vcpu *v)
}
}
+static void kvm_gen_kvmclock_update(struct kvm_vcpu *v)
+{
+ struct kvm *kvm = v->kvm;
+
+ set_bit(KVM_REQ_CLOCK_UPDATE, &v->requests);
+ schedule_delayed_work(&kvm->arch.kvmclock_update_work,
+ KVMCLOCK_UPDATE_DELAY);
+}
+
+#define KVMCLOCK_SYNC_PERIOD (300 * HZ)
+
+static void kvmclock_sync_fn(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct kvm_arch *ka = container_of(dwork, struct kvm_arch,
+ kvmclock_sync_work);
+ struct kvm *kvm = container_of(ka, struct kvm, arch);
+
+ schedule_delayed_work(&kvm->arch.kvmclock_update_work, 0);
+ schedule_delayed_work(&kvm->arch.kvmclock_sync_work,
+ KVMCLOCK_SYNC_PERIOD);
+}
+
static bool msr_mtrr_valid(unsigned msr)
{
switch (msr) {
@@ -2323,9 +2360,12 @@ static int get_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
case HV_X64_MSR_VP_INDEX: {
int r;
struct kvm_vcpu *v;
- kvm_for_each_vcpu(r, v, vcpu->kvm)
- if (v == vcpu)
+ kvm_for_each_vcpu(r, v, vcpu->kvm) {
+ if (v == vcpu) {
data = r;
+ break;
+ }
+ }
break;
}
case HV_X64_MSR_EOI:
@@ -2617,6 +2657,7 @@ int kvm_dev_ioctl_check_extension(long ext)
case KVM_CAP_KVMCLOCK_CTRL:
case KVM_CAP_READONLY_MEM:
case KVM_CAP_HYPERV_TIME:
+ case KVM_CAP_IOAPIC_POLARITY_IGNORED:
#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
case KVM_CAP_ASSIGN_DEV_IRQ:
case KVM_CAP_PCI_2_3:
@@ -3043,9 +3084,7 @@ static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu,
* CPUID leaf 0xD, index 0, EDX:EAX. This is for compatibility
* with old userspace.
*/
- if (xstate_bv & ~KVM_SUPPORTED_XCR0)
- return -EINVAL;
- if (xstate_bv & ~host_xcr0)
+ if (xstate_bv & ~kvm_supported_xcr0())
return -EINVAL;
memcpy(&vcpu->arch.guest_fpu.state->xsave,
guest_xsave->region, vcpu->arch.guest_xstate_size);
@@ -3898,6 +3937,23 @@ static void kvm_init_msr_list(void)
for (i = j = KVM_SAVE_MSRS_BEGIN; i < ARRAY_SIZE(msrs_to_save); i++) {
if (rdmsr_safe(msrs_to_save[i], &dummy[0], &dummy[1]) < 0)
continue;
+
+ /*
+ * 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.
+ */
+ switch (msrs_to_save[i]) {
+ case MSR_IA32_BNDCFGS:
+ if (!kvm_x86_ops->mpx_supported())
+ continue;
+ break;
+ default:
+ break;
+ }
+
if (j < i)
msrs_to_save[j] = msrs_to_save[i];
j++;
@@ -4394,6 +4450,7 @@ static int emulator_cmpxchg_emulated(struct x86_emulate_ctxt *ctxt,
if (!exchanged)
return X86EMUL_CMPXCHG_FAILED;
+ mark_page_dirty(vcpu->kvm, gpa >> PAGE_SHIFT);
kvm_mmu_pte_write(vcpu, gpa, new, bytes);
return X86EMUL_CONTINUE;
@@ -5365,7 +5422,8 @@ static void kvm_timer_init(void)
int cpu;
max_tsc_khz = tsc_khz;
- register_hotcpu_notifier(&kvmclock_cpu_notifier_block);
+
+ cpu_notifier_register_begin();
if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) {
#ifdef CONFIG_CPU_FREQ
struct cpufreq_policy policy;
@@ -5382,6 +5440,10 @@ static void kvm_timer_init(void)
pr_debug("kvm: max_tsc_khz = %ld\n", max_tsc_khz);
for_each_online_cpu(cpu)
smp_call_function_single(cpu, tsc_khz_changed, NULL, 1);
+
+ __register_hotcpu_notifier(&kvmclock_cpu_notifier_block);
+ cpu_notifier_register_done();
+
}
static DEFINE_PER_CPU(struct kvm_vcpu *, current_vcpu);
@@ -5537,9 +5599,10 @@ int kvm_arch_init(void *opaque)
goto out_free_percpu;
kvm_set_mmio_spte_mask();
- kvm_init_msr_list();
kvm_x86_ops = ops;
+ kvm_init_msr_list();
+
kvm_mmu_set_mask_ptes(PT_USER_MASK, PT_ACCESSED_MASK,
PT_DIRTY_MASK, PT64_NX_MASK, 0);
@@ -5782,8 +5845,10 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu)
kvm_x86_ops->update_cr8_intercept(vcpu, tpr, max_irr);
}
-static void inject_pending_event(struct kvm_vcpu *vcpu)
+static int inject_pending_event(struct kvm_vcpu *vcpu, bool req_int_win)
{
+ int r;
+
/* try to reinject previous events if any */
if (vcpu->arch.exception.pending) {
trace_kvm_inj_exception(vcpu->arch.exception.nr,
@@ -5793,17 +5858,23 @@ static void inject_pending_event(struct kvm_vcpu *vcpu)
vcpu->arch.exception.has_error_code,
vcpu->arch.exception.error_code,
vcpu->arch.exception.reinject);
- return;
+ return 0;
}
if (vcpu->arch.nmi_injected) {
kvm_x86_ops->set_nmi(vcpu);
- return;
+ return 0;
}
if (vcpu->arch.interrupt.pending) {
kvm_x86_ops->set_irq(vcpu);
- return;
+ return 0;
+ }
+
+ if (is_guest_mode(vcpu) && kvm_x86_ops->check_nested_events) {
+ r = kvm_x86_ops->check_nested_events(vcpu, req_int_win);
+ if (r != 0)
+ return r;
}
/* try to inject new event if pending */
@@ -5820,6 +5891,7 @@ static void inject_pending_event(struct kvm_vcpu *vcpu)
kvm_x86_ops->set_irq(vcpu);
}
}
+ return 0;
}
static void process_nmi(struct kvm_vcpu *vcpu)
@@ -5924,15 +5996,13 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
goto out;
}
- inject_pending_event(vcpu);
-
+ if (inject_pending_event(vcpu, req_int_win) != 0)
+ req_immediate_exit = true;
/* enable NMI/IRQ window open exits if needed */
- if (vcpu->arch.nmi_pending)
- req_immediate_exit =
- kvm_x86_ops->enable_nmi_window(vcpu) != 0;
+ else if (vcpu->arch.nmi_pending)
+ kvm_x86_ops->enable_nmi_window(vcpu);
else if (kvm_cpu_has_injectable_intr(vcpu) || req_int_win)
- req_immediate_exit =
- kvm_x86_ops->enable_irq_window(vcpu) != 0;
+ kvm_x86_ops->enable_irq_window(vcpu);
if (kvm_lapic_enabled(vcpu)) {
/*
@@ -5992,12 +6062,28 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
set_debugreg(vcpu->arch.eff_db[1], 1);
set_debugreg(vcpu->arch.eff_db[2], 2);
set_debugreg(vcpu->arch.eff_db[3], 3);
+ set_debugreg(vcpu->arch.dr6, 6);
}
trace_kvm_entry(vcpu->vcpu_id);
kvm_x86_ops->run(vcpu);
/*
+ * Do this here before restoring debug registers on the host. And
+ * since we do this before handling the vmexit, a DR access vmexit
+ * can (a) read the correct value of the debug registers, (b) set
+ * KVM_DEBUGREG_WONT_EXIT again.
+ */
+ if (unlikely(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT)) {
+ int i;
+
+ WARN_ON(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP);
+ kvm_x86_ops->sync_dirty_debug_regs(vcpu);
+ for (i = 0; i < KVM_NR_DB_REGS; i++)
+ vcpu->arch.eff_db[i] = vcpu->arch.db[i];
+ }
+
+ /*
* If the guest has used debug registers, at least dr7
* will be disabled while returning to the host.
* If we don't have active breakpoints in the host, we don't
@@ -6711,6 +6797,7 @@ int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
{
int r;
struct msr_data msr;
+ struct kvm *kvm = vcpu->kvm;
r = vcpu_load(vcpu);
if (r)
@@ -6721,6 +6808,9 @@ int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
kvm_write_tsc(vcpu, &msr);
vcpu_put(vcpu);
+ schedule_delayed_work(&kvm->arch.kvmclock_sync_work,
+ KVMCLOCK_SYNC_PERIOD);
+
return r;
}
@@ -7013,6 +7103,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
pvclock_update_vm_gtod_copy(kvm);
+ INIT_DELAYED_WORK(&kvm->arch.kvmclock_update_work, kvmclock_update_fn);
+ INIT_DELAYED_WORK(&kvm->arch.kvmclock_sync_work, kvmclock_sync_fn);
+
return 0;
}
@@ -7050,6 +7143,8 @@ static void kvm_free_vcpus(struct kvm *kvm)
void kvm_arch_sync_events(struct kvm *kvm)
{
+ cancel_delayed_work_sync(&kvm->arch.kvmclock_sync_work);
+ cancel_delayed_work_sync(&kvm->arch.kvmclock_update_work);
kvm_free_all_assigned_devices(kvm);
kvm_free_pit(kvm);
}
@@ -7248,6 +7343,9 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
{
+ if (is_guest_mode(vcpu) && kvm_x86_ops->check_nested_events)
+ kvm_x86_ops->check_nested_events(vcpu, false);
+
return (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE &&
!vcpu->arch.apf.halted)
|| !list_empty_careful(&vcpu->async_pf.done)
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index 8da5823bcde6..8c97bac9a895 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -122,9 +122,12 @@ int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt,
gva_t addr, void *val, unsigned int bytes,
struct x86_exception *exception);
-#define KVM_SUPPORTED_XCR0 (XSTATE_FP | XSTATE_SSE | XSTATE_YMM)
+#define KVM_SUPPORTED_XCR0 (XSTATE_FP | XSTATE_SSE | XSTATE_YMM \
+ | XSTATE_BNDREGS | XSTATE_BNDCSR)
extern u64 host_xcr0;
+extern u64 kvm_supported_xcr0(void);
+
extern unsigned int min_timer_period_us;
extern struct static_key kvm_no_apic_vcpu;
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 4ed75dd81d05..dc017735bb91 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -553,13 +553,13 @@ void bpf_jit_compile(struct sk_filter *fp)
}
break;
case BPF_S_ANC_RXHASH:
- BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, rxhash) != 4);
- if (is_imm8(offsetof(struct sk_buff, rxhash))) {
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
+ if (is_imm8(offsetof(struct sk_buff, hash))) {
/* mov off8(%rdi),%eax */
- EMIT3(0x8b, 0x47, offsetof(struct sk_buff, rxhash));
+ EMIT3(0x8b, 0x47, offsetof(struct sk_buff, hash));
} else {
EMIT2(0x8b, 0x87);
- EMIT(offsetof(struct sk_buff, rxhash), 4);
+ EMIT(offsetof(struct sk_buff, hash), 4);
}
break;
case BPF_S_ANC_QUEUE:
@@ -772,6 +772,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i];
bpf_flush_icache(header, image + proglen);
set_memory_ro((unsigned long)header, header->pages);
fp->bpf_func = (void *)image;
+ fp->jited = 1;
}
out:
kfree(addrs);
@@ -791,7 +792,7 @@ static void bpf_jit_free_deferred(struct work_struct *work)
void bpf_jit_free(struct sk_filter *fp)
{
- if (fp->bpf_func != sk_run_filter) {
+ if (fp->jited) {
INIT_WORK(&fp->work, bpf_jit_free_deferred);
schedule_work(&fp->work);
} else {
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c
index 6890d8498e0b..379e8bd0deea 100644
--- a/arch/x86/oprofile/nmi_int.c
+++ b/arch/x86/oprofile/nmi_int.c
@@ -494,14 +494,19 @@ static int nmi_setup(void)
if (err)
goto fail;
+ cpu_notifier_register_begin();
+
+ /* Use get/put_online_cpus() to protect 'nmi_enabled' */
get_online_cpus();
- register_cpu_notifier(&oprofile_cpu_nb);
nmi_enabled = 1;
/* make nmi_enabled visible to the nmi handler: */
smp_mb();
on_each_cpu(nmi_cpu_setup, NULL, 1);
+ __register_cpu_notifier(&oprofile_cpu_nb);
put_online_cpus();
+ cpu_notifier_register_done();
+
return 0;
fail:
free_msrs();
@@ -512,12 +517,18 @@ static void nmi_shutdown(void)
{
struct op_msrs *msrs;
+ cpu_notifier_register_begin();
+
+ /* Use get/put_online_cpus() to protect 'nmi_enabled' & 'ctr_running' */
get_online_cpus();
- unregister_cpu_notifier(&oprofile_cpu_nb);
on_each_cpu(nmi_cpu_shutdown, NULL, 1);
nmi_enabled = 0;
ctr_running = 0;
+ __unregister_cpu_notifier(&oprofile_cpu_nb);
put_online_cpus();
+
+ cpu_notifier_register_done();
+
/* make variables visible to the nmi handler: */
smp_mb();
unregister_nmi_handler(NMI_LOCAL, "oprofile");
diff --git a/arch/x86/pci/amd_bus.c b/arch/x86/pci/amd_bus.c
index a313a7fb6b86..e88f4c53d7f6 100644
--- a/arch/x86/pci/amd_bus.c
+++ b/arch/x86/pci/amd_bus.c
@@ -370,10 +370,13 @@ static int __init pci_io_ecs_init(void)
if (early_pci_allowed())
pci_enable_pci_io_ecs();
- register_cpu_notifier(&amd_cpu_notifier);
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
amd_cpu_notify(&amd_cpu_notifier, (unsigned long)CPU_ONLINE,
(void *)(long)cpu);
+ __register_cpu_notifier(&amd_cpu_notifier);
+ cpu_notifier_register_done();
+
pci_probe |= PCI_HAS_IO_ECS;
return 0;
diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c
index 103e702ec5a7..905956f16465 100644
--- a/arch/x86/pci/xen.c
+++ b/arch/x86/pci/xen.c
@@ -178,6 +178,7 @@ static int xen_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
i = 0;
list_for_each_entry(msidesc, &dev->msi_list, list) {
irq = xen_bind_pirq_msi_to_irq(dev, msidesc, v[i],
+ (type == PCI_CAP_ID_MSI) ? nvec : 1,
(type == PCI_CAP_ID_MSIX) ?
"pcifront-msi-x" :
"pcifront-msi",
@@ -245,6 +246,7 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
"xen: msi already bound to pirq=%d\n", pirq);
}
irq = xen_bind_pirq_msi_to_irq(dev, msidesc, pirq,
+ (type == PCI_CAP_ID_MSI) ? nvec : 1,
(type == PCI_CAP_ID_MSIX) ?
"msi-x" : "msi",
DOMID_SELF);
@@ -269,9 +271,6 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
int ret = 0;
struct msi_desc *msidesc;
- if (type == PCI_CAP_ID_MSI && nvec > 1)
- return 1;
-
list_for_each_entry(msidesc, &dev->msi_list, list) {
struct physdev_map_pirq map_irq;
domid_t domid;
@@ -291,7 +290,10 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
(pci_domain_nr(dev->bus) << 16);
map_irq.devfn = dev->devfn;
- if (type == PCI_CAP_ID_MSIX) {
+ if (type == PCI_CAP_ID_MSI && nvec > 1) {
+ map_irq.type = MAP_PIRQ_TYPE_MULTI_MSI;
+ map_irq.entry_nr = nvec;
+ } else if (type == PCI_CAP_ID_MSIX) {
int pos;
u32 table_offset, bir;
@@ -308,6 +310,16 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
if (pci_seg_supported)
ret = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq,
&map_irq);
+ if (type == PCI_CAP_ID_MSI && nvec > 1 && ret) {
+ /*
+ * If MAP_PIRQ_TYPE_MULTI_MSI is not available
+ * there's nothing else we can do in this case.
+ * Just set ret > 0 so driver can retry with
+ * single MSI.
+ */
+ ret = 1;
+ goto out;
+ }
if (ret == -EINVAL && !pci_domain_nr(dev->bus)) {
map_irq.type = MAP_PIRQ_TYPE_MSI;
map_irq.index = -1;
@@ -324,11 +336,10 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
goto out;
}
- ret = xen_bind_pirq_msi_to_irq(dev, msidesc,
- map_irq.pirq,
- (type == PCI_CAP_ID_MSIX) ?
- "msi-x" : "msi",
- domid);
+ ret = xen_bind_pirq_msi_to_irq(dev, msidesc, map_irq.pirq,
+ (type == PCI_CAP_ID_MSI) ? nvec : 1,
+ (type == PCI_CAP_ID_MSIX) ? "msi-x" : "msi",
+ domid);
if (ret < 0)
goto out;
}
diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl
index a12bddc7ccea..04376ac3d9ef 100644
--- a/arch/x86/syscalls/syscall_64.tbl
+++ b/arch/x86/syscalls/syscall_64.tbl
@@ -322,6 +322,7 @@
313 common finit_module sys_finit_module
314 common sched_setattr sys_sched_setattr
315 common sched_getattr sys_sched_getattr
+316 common renameat2 sys_renameat2
#
# x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/arch/x86/xen/Kconfig b/arch/x86/xen/Kconfig
index 9c50cc2e403b..e88fda867a33 100644
--- a/arch/x86/xen/Kconfig
+++ b/arch/x86/xen/Kconfig
@@ -19,11 +19,6 @@ config XEN_DOM0
depends on XEN && PCI_XEN && SWIOTLB_XEN
depends on X86_LOCAL_APIC && X86_IO_APIC && ACPI && PCI
-# Dummy symbol since people have come to rely on the PRIVILEGED_GUEST
-# name in tools.
-config XEN_PRIVILEGED_GUEST
- def_bool XEN_DOM0
-
config XEN_PVHVM
def_bool y
depends on XEN && PCI && X86_LOCAL_APIC
diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c
index 696c694986d0..85e5d78c9874 100644
--- a/arch/x86/xen/p2m.c
+++ b/arch/x86/xen/p2m.c
@@ -881,6 +881,65 @@ static unsigned long mfn_hash(unsigned long mfn)
return hash_long(mfn, M2P_OVERRIDE_HASH_SHIFT);
}
+int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
+ struct gnttab_map_grant_ref *kmap_ops,
+ struct page **pages, unsigned int count)
+{
+ int i, ret = 0;
+ bool lazy = false;
+ pte_t *pte;
+
+ if (xen_feature(XENFEAT_auto_translated_physmap))
+ return 0;
+
+ if (kmap_ops &&
+ !in_interrupt() &&
+ paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) {
+ arch_enter_lazy_mmu_mode();
+ lazy = true;
+ }
+
+ for (i = 0; i < count; i++) {
+ unsigned long mfn, pfn;
+
+ /* Do not add to override if the map failed. */
+ if (map_ops[i].status)
+ continue;
+
+ if (map_ops[i].flags & GNTMAP_contains_pte) {
+ pte = (pte_t *) (mfn_to_virt(PFN_DOWN(map_ops[i].host_addr)) +
+ (map_ops[i].host_addr & ~PAGE_MASK));
+ mfn = pte_mfn(*pte);
+ } else {
+ mfn = PFN_DOWN(map_ops[i].dev_bus_addr);
+ }
+ pfn = page_to_pfn(pages[i]);
+
+ WARN_ON(PagePrivate(pages[i]));
+ SetPagePrivate(pages[i]);
+ set_page_private(pages[i], mfn);
+ pages[i]->index = pfn_to_mfn(pfn);
+
+ if (unlikely(!set_phys_to_machine(pfn, FOREIGN_FRAME(mfn)))) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (kmap_ops) {
+ ret = m2p_add_override(mfn, pages[i], &kmap_ops[i]);
+ if (ret)
+ goto out;
+ }
+ }
+
+out:
+ if (lazy)
+ arch_leave_lazy_mmu_mode();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(set_foreign_p2m_mapping);
+
/* Add an MFN override for a particular page */
int m2p_add_override(unsigned long mfn, struct page *page,
struct gnttab_map_grant_ref *kmap_op)
@@ -899,13 +958,6 @@ int m2p_add_override(unsigned long mfn, struct page *page,
"m2p_add_override: pfn %lx not mapped", pfn))
return -EINVAL;
}
- WARN_ON(PagePrivate(page));
- SetPagePrivate(page);
- set_page_private(page, mfn);
- page->index = pfn_to_mfn(pfn);
-
- if (unlikely(!set_phys_to_machine(pfn, FOREIGN_FRAME(mfn))))
- return -ENOMEM;
if (kmap_op != NULL) {
if (!PageHighMem(page)) {
@@ -943,20 +995,62 @@ int m2p_add_override(unsigned long mfn, struct page *page,
return 0;
}
EXPORT_SYMBOL_GPL(m2p_add_override);
+
+int clear_foreign_p2m_mapping(struct gnttab_unmap_grant_ref *unmap_ops,
+ struct gnttab_map_grant_ref *kmap_ops,
+ struct page **pages, unsigned int count)
+{
+ int i, ret = 0;
+ bool lazy = false;
+
+ if (xen_feature(XENFEAT_auto_translated_physmap))
+ return 0;
+
+ if (kmap_ops &&
+ !in_interrupt() &&
+ paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) {
+ arch_enter_lazy_mmu_mode();
+ lazy = true;
+ }
+
+ for (i = 0; i < count; i++) {
+ unsigned long mfn = get_phys_to_machine(page_to_pfn(pages[i]));
+ unsigned long pfn = page_to_pfn(pages[i]);
+
+ if (mfn == INVALID_P2M_ENTRY || !(mfn & FOREIGN_FRAME_BIT)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ set_page_private(pages[i], INVALID_P2M_ENTRY);
+ WARN_ON(!PagePrivate(pages[i]));
+ ClearPagePrivate(pages[i]);
+ set_phys_to_machine(pfn, pages[i]->index);
+
+ if (kmap_ops)
+ ret = m2p_remove_override(pages[i], &kmap_ops[i], mfn);
+ if (ret)
+ goto out;
+ }
+
+out:
+ if (lazy)
+ arch_leave_lazy_mmu_mode();
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clear_foreign_p2m_mapping);
+
int m2p_remove_override(struct page *page,
- struct gnttab_map_grant_ref *kmap_op)
+ struct gnttab_map_grant_ref *kmap_op,
+ unsigned long mfn)
{
unsigned long flags;
- unsigned long mfn;
unsigned long pfn;
unsigned long uninitialized_var(address);
unsigned level;
pte_t *ptep = NULL;
pfn = page_to_pfn(page);
- mfn = get_phys_to_machine(pfn);
- if (mfn == INVALID_P2M_ENTRY || !(mfn & FOREIGN_FRAME_BIT))
- return -EINVAL;
if (!PageHighMem(page)) {
address = (unsigned long)__va(pfn << PAGE_SHIFT);
@@ -970,10 +1064,7 @@ int m2p_remove_override(struct page *page,
spin_lock_irqsave(&m2p_override_lock, flags);
list_del(&page->lru);
spin_unlock_irqrestore(&m2p_override_lock, flags);
- WARN_ON(!PagePrivate(page));
- ClearPagePrivate(page);
- set_phys_to_machine(pfn, page->index);
if (kmap_op != NULL) {
if (!PageHighMem(page)) {
struct multicall_space mcs;
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index b6e95b5e262f..e4a4145926f6 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -894,7 +894,7 @@ static int blkcg_can_attach(struct cgroup_subsys_state *css,
int ret = 0;
/* task_lock() is needed to avoid races with exit_io_context() */
- cgroup_taskset_for_each(task, css, tset) {
+ cgroup_taskset_for_each(task, tset) {
task_lock(task);
ioc = task->io_context;
if (ioc && atomic_read(&ioc->nr_tasks) > 1)
@@ -906,17 +906,14 @@ static int blkcg_can_attach(struct cgroup_subsys_state *css,
return ret;
}
-struct cgroup_subsys blkio_subsys = {
- .name = "blkio",
+struct cgroup_subsys blkio_cgrp_subsys = {
.css_alloc = blkcg_css_alloc,
.css_offline = blkcg_css_offline,
.css_free = blkcg_css_free,
.can_attach = blkcg_can_attach,
- .subsys_id = blkio_subsys_id,
.base_cftypes = blkcg_files,
- .module = THIS_MODULE,
};
-EXPORT_SYMBOL_GPL(blkio_subsys);
+EXPORT_SYMBOL_GPL(blkio_cgrp_subsys);
/**
* blkcg_activate_policy - activate a blkcg policy on a request_queue
@@ -1106,7 +1103,7 @@ int blkcg_policy_register(struct blkcg_policy *pol)
/* everything is in place, add intf files for the new policy */
if (pol->cftypes)
- WARN_ON(cgroup_add_cftypes(&blkio_subsys, pol->cftypes));
+ WARN_ON(cgroup_add_cftypes(&blkio_cgrp_subsys, pol->cftypes));
ret = 0;
out_unlock:
mutex_unlock(&blkcg_pol_mutex);
diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h
index 86154eab9523..371fe8e92ab5 100644
--- a/block/blk-cgroup.h
+++ b/block/blk-cgroup.h
@@ -186,7 +186,7 @@ static inline struct blkcg *css_to_blkcg(struct cgroup_subsys_state *css)
static inline struct blkcg *task_blkcg(struct task_struct *tsk)
{
- return css_to_blkcg(task_css(tsk, blkio_subsys_id));
+ return css_to_blkcg(task_css(tsk, blkio_cgrp_id));
}
static inline struct blkcg *bio_blkcg(struct bio *bio)
@@ -241,12 +241,16 @@ static inline struct blkcg_gq *pd_to_blkg(struct blkg_policy_data *pd)
*/
static inline int blkg_path(struct blkcg_gq *blkg, char *buf, int buflen)
{
- int ret;
+ char *p;
- ret = cgroup_path(blkg->blkcg->css.cgroup, buf, buflen);
- if (ret)
+ p = cgroup_path(blkg->blkcg->css.cgroup, buf, buflen);
+ if (!p) {
strncpy(buf, "<unavailable>", buflen);
- return ret;
+ return -ENAMETOOLONG;
+ }
+
+ memmove(buf, p, buf + buflen - p);
+ return 0;
}
/**
@@ -435,9 +439,9 @@ static inline uint64_t blkg_stat_read(struct blkg_stat *stat)
uint64_t v;
do {
- start = u64_stats_fetch_begin_bh(&stat->syncp);
+ start = u64_stats_fetch_begin_irq(&stat->syncp);
v = stat->cnt;
- } while (u64_stats_fetch_retry_bh(&stat->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&stat->syncp, start));
return v;
}
@@ -508,9 +512,9 @@ static inline struct blkg_rwstat blkg_rwstat_read(struct blkg_rwstat *rwstat)
struct blkg_rwstat tmp;
do {
- start = u64_stats_fetch_begin_bh(&rwstat->syncp);
+ start = u64_stats_fetch_begin_irq(&rwstat->syncp);
tmp = *rwstat;
- } while (u64_stats_fetch_retry_bh(&rwstat->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&rwstat->syncp, start));
return tmp;
}
diff --git a/block/blk-core.c b/block/blk-core.c
index e45b321cf6a0..34d7c196338b 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1928,7 +1928,7 @@ EXPORT_SYMBOL(submit_bio);
* 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 agaist
+ * 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.
diff --git a/block/blk-map.c b/block/blk-map.c
index ae4ae1047fd9..cca6356d216d 100644
--- a/block/blk-map.c
+++ b/block/blk-map.c
@@ -285,7 +285,7 @@ EXPORT_SYMBOL(blk_rq_unmap_user);
*
* Description:
* Data will be mapped directly if possible. Otherwise a bounce
- * buffer is used. Can be called multple times to append multple
+ * buffer is used. Can be called multiple times to append multiple
* buffers.
*/
int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf,
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index 1474c3ab7e72..033745cd7fba 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -1408,13 +1408,13 @@ static int tg_set_conf(struct cgroup_subsys_state *css, struct cftype *cft,
}
static int tg_set_conf_u64(struct cgroup_subsys_state *css, struct cftype *cft,
- const char *buf)
+ char *buf)
{
return tg_set_conf(css, cft, buf, true);
}
static int tg_set_conf_uint(struct cgroup_subsys_state *css, struct cftype *cft,
- const char *buf)
+ char *buf)
{
return tg_set_conf(css, cft, buf, false);
}
@@ -1425,28 +1425,24 @@ static struct cftype throtl_files[] = {
.private = offsetof(struct throtl_grp, bps[READ]),
.seq_show = tg_print_conf_u64,
.write_string = tg_set_conf_u64,
- .max_write_len = 256,
},
{
.name = "throttle.write_bps_device",
.private = offsetof(struct throtl_grp, bps[WRITE]),
.seq_show = tg_print_conf_u64,
.write_string = tg_set_conf_u64,
- .max_write_len = 256,
},
{
.name = "throttle.read_iops_device",
.private = offsetof(struct throtl_grp, iops[READ]),
.seq_show = tg_print_conf_uint,
.write_string = tg_set_conf_uint,
- .max_write_len = 256,
},
{
.name = "throttle.write_iops_device",
.private = offsetof(struct throtl_grp, iops[WRITE]),
.seq_show = tg_print_conf_uint,
.write_string = tg_set_conf_uint,
- .max_write_len = 256,
},
{
.name = "throttle.io_service_bytes",
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 5873e4ada9eb..e0985f1955e7 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -1701,13 +1701,13 @@ static int __cfqg_set_weight_device(struct cgroup_subsys_state *css,
}
static int cfqg_set_weight_device(struct cgroup_subsys_state *css,
- struct cftype *cft, const char *buf)
+ struct cftype *cft, char *buf)
{
return __cfqg_set_weight_device(css, cft, buf, false);
}
static int cfqg_set_leaf_weight_device(struct cgroup_subsys_state *css,
- struct cftype *cft, const char *buf)
+ struct cftype *cft, char *buf)
{
return __cfqg_set_weight_device(css, cft, buf, true);
}
@@ -1838,7 +1838,6 @@ static struct cftype cfq_blkcg_files[] = {
.flags = CFTYPE_ONLY_ON_ROOT,
.seq_show = cfqg_print_leaf_weight_device,
.write_string = cfqg_set_leaf_weight_device,
- .max_write_len = 256,
},
{
.name = "weight",
@@ -1853,7 +1852,6 @@ static struct cftype cfq_blkcg_files[] = {
.flags = CFTYPE_NOT_ON_ROOT,
.seq_show = cfqg_print_weight_device,
.write_string = cfqg_set_weight_device,
- .max_write_len = 256,
},
{
.name = "weight",
@@ -1866,7 +1864,6 @@ static struct cftype cfq_blkcg_files[] = {
.name = "leaf_weight_device",
.seq_show = cfqg_print_leaf_weight_device,
.write_string = cfqg_set_leaf_weight_device,
- .max_write_len = 256,
},
{
.name = "leaf_weight",
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 7bcb70d216e1..ce4012a58781 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -491,14 +491,14 @@ config CRYPTO_SHA1
SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2).
config CRYPTO_SHA1_SSSE3
- tristate "SHA1 digest algorithm (SSSE3/AVX)"
+ tristate "SHA1 digest algorithm (SSSE3/AVX/AVX2)"
depends on X86 && 64BIT
select CRYPTO_SHA1
select CRYPTO_HASH
help
SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2) implemented
using Supplemental SSE3 (SSSE3) instructions or Advanced Vector
- Extensions (AVX), when available.
+ Extensions (AVX/AVX2), when available.
config CRYPTO_SHA256_SSSE3
tristate "SHA256 digest algorithm (SSSE3/AVX/AVX2)"
diff --git a/crypto/Makefile b/crypto/Makefile
index b29402a7b9b5..38e64231dcd3 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -81,7 +81,7 @@ obj-$(CONFIG_CRYPTO_SALSA20) += salsa20_generic.o
obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o
obj-$(CONFIG_CRYPTO_ZLIB) += zlib.o
obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o
-obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o
+obj-$(CONFIG_CRYPTO_CRC32C) += crc32c_generic.o
obj-$(CONFIG_CRYPTO_CRC32) += crc32.o
obj-$(CONFIG_CRYPTO_CRCT10DIF) += crct10dif_common.o crct10dif_generic.o
obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o authencesn.o
diff --git a/crypto/ahash.c b/crypto/ahash.c
index a92dc382f781..6e7223392e80 100644
--- a/crypto/ahash.c
+++ b/crypto/ahash.c
@@ -190,6 +190,75 @@ static inline unsigned int ahash_align_buffer_size(unsigned len,
return len + (mask & ~(crypto_tfm_ctx_alignment() - 1));
}
+static int ahash_save_req(struct ahash_request *req, crypto_completion_t cplt)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ unsigned long alignmask = crypto_ahash_alignmask(tfm);
+ unsigned int ds = crypto_ahash_digestsize(tfm);
+ struct ahash_request_priv *priv;
+
+ priv = kmalloc(sizeof(*priv) + ahash_align_buffer_size(ds, alignmask),
+ (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+ GFP_KERNEL : GFP_ATOMIC);
+ if (!priv)
+ return -ENOMEM;
+
+ /*
+ * WARNING: Voodoo programming below!
+ *
+ * The code below is obscure and hard to understand, thus explanation
+ * is necessary. See include/crypto/hash.h and include/linux/crypto.h
+ * to understand the layout of structures used here!
+ *
+ * The code here will replace portions of the ORIGINAL request with
+ * pointers to new code and buffers so the hashing operation can store
+ * the result in aligned buffer. We will call the modified request
+ * an ADJUSTED request.
+ *
+ * The newly mangled request will look as such:
+ *
+ * req {
+ * .result = ADJUSTED[new aligned buffer]
+ * .base.complete = ADJUSTED[pointer to completion function]
+ * .base.data = ADJUSTED[*req (pointer to self)]
+ * .priv = ADJUSTED[new priv] {
+ * .result = ORIGINAL(result)
+ * .complete = ORIGINAL(base.complete)
+ * .data = ORIGINAL(base.data)
+ * }
+ */
+
+ priv->result = req->result;
+ priv->complete = req->base.complete;
+ priv->data = req->base.data;
+ /*
+ * WARNING: We do not backup req->priv here! The req->priv
+ * is for internal use of the Crypto API and the
+ * user must _NOT_ _EVER_ depend on it's content!
+ */
+
+ req->result = PTR_ALIGN((u8 *)priv->ubuf, alignmask + 1);
+ req->base.complete = cplt;
+ req->base.data = req;
+ req->priv = priv;
+
+ return 0;
+}
+
+static void ahash_restore_req(struct ahash_request *req)
+{
+ struct ahash_request_priv *priv = req->priv;
+
+ /* Restore the original crypto request. */
+ req->result = priv->result;
+ req->base.complete = priv->complete;
+ req->base.data = priv->data;
+ req->priv = NULL;
+
+ /* Free the req->priv.priv from the ADJUSTED request. */
+ kzfree(priv);
+}
+
static void ahash_op_unaligned_finish(struct ahash_request *req, int err)
{
struct ahash_request_priv *priv = req->priv;
@@ -201,47 +270,37 @@ static void ahash_op_unaligned_finish(struct ahash_request *req, int err)
memcpy(priv->result, req->result,
crypto_ahash_digestsize(crypto_ahash_reqtfm(req)));
- kzfree(priv);
+ ahash_restore_req(req);
}
static void ahash_op_unaligned_done(struct crypto_async_request *req, int err)
{
struct ahash_request *areq = req->data;
- struct ahash_request_priv *priv = areq->priv;
- crypto_completion_t complete = priv->complete;
- void *data = priv->data;
- ahash_op_unaligned_finish(areq, err);
+ /*
+ * Restore the original request, see ahash_op_unaligned() for what
+ * goes where.
+ *
+ * The "struct ahash_request *req" here is in fact the "req.base"
+ * from the ADJUSTED request from ahash_op_unaligned(), thus as it
+ * is a pointer to self, it is also the ADJUSTED "req" .
+ */
- areq->base.complete = complete;
- areq->base.data = data;
+ /* First copy req->result into req->priv.result */
+ ahash_op_unaligned_finish(areq, err);
- complete(&areq->base, err);
+ /* Complete the ORIGINAL request. */
+ areq->base.complete(&areq->base, err);
}
static int ahash_op_unaligned(struct ahash_request *req,
int (*op)(struct ahash_request *))
{
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- unsigned long alignmask = crypto_ahash_alignmask(tfm);
- unsigned int ds = crypto_ahash_digestsize(tfm);
- struct ahash_request_priv *priv;
int err;
- priv = kmalloc(sizeof(*priv) + ahash_align_buffer_size(ds, alignmask),
- (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
- GFP_KERNEL : GFP_ATOMIC);
- if (!priv)
- return -ENOMEM;
-
- priv->result = req->result;
- priv->complete = req->base.complete;
- priv->data = req->base.data;
-
- req->result = PTR_ALIGN((u8 *)priv->ubuf, alignmask + 1);
- req->base.complete = ahash_op_unaligned_done;
- req->base.data = req;
- req->priv = priv;
+ err = ahash_save_req(req, ahash_op_unaligned_done);
+ if (err)
+ return err;
err = op(req);
ahash_op_unaligned_finish(req, err);
@@ -290,19 +349,16 @@ static void ahash_def_finup_finish2(struct ahash_request *req, int err)
memcpy(priv->result, req->result,
crypto_ahash_digestsize(crypto_ahash_reqtfm(req)));
- kzfree(priv);
+ ahash_restore_req(req);
}
static void ahash_def_finup_done2(struct crypto_async_request *req, int err)
{
struct ahash_request *areq = req->data;
- struct ahash_request_priv *priv = areq->priv;
- crypto_completion_t complete = priv->complete;
- void *data = priv->data;
ahash_def_finup_finish2(areq, err);
- complete(data, err);
+ areq->base.complete(&areq->base, err);
}
static int ahash_def_finup_finish1(struct ahash_request *req, int err)
@@ -322,38 +378,23 @@ out:
static void ahash_def_finup_done1(struct crypto_async_request *req, int err)
{
struct ahash_request *areq = req->data;
- struct ahash_request_priv *priv = areq->priv;
- crypto_completion_t complete = priv->complete;
- void *data = priv->data;
err = ahash_def_finup_finish1(areq, err);
- complete(data, err);
+ areq->base.complete(&areq->base, err);
}
static int ahash_def_finup(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- unsigned long alignmask = crypto_ahash_alignmask(tfm);
- unsigned int ds = crypto_ahash_digestsize(tfm);
- struct ahash_request_priv *priv;
-
- priv = kmalloc(sizeof(*priv) + ahash_align_buffer_size(ds, alignmask),
- (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
- GFP_KERNEL : GFP_ATOMIC);
- if (!priv)
- return -ENOMEM;
-
- priv->result = req->result;
- priv->complete = req->base.complete;
- priv->data = req->base.data;
+ int err;
- req->result = PTR_ALIGN((u8 *)priv->ubuf, alignmask + 1);
- req->base.complete = ahash_def_finup_done1;
- req->base.data = req;
- req->priv = priv;
+ err = ahash_save_req(req, ahash_def_finup_done1);
+ if (err)
+ return err;
- return ahash_def_finup_finish1(req, tfm->update(req));
+ err = tfm->update(req);
+ return ahash_def_finup_finish1(req, err);
}
static int ahash_no_export(struct ahash_request *req, void *out)
diff --git a/crypto/blkcipher.c b/crypto/blkcipher.c
index a79e7e9ab86e..0122bec38564 100644
--- a/crypto/blkcipher.c
+++ b/crypto/blkcipher.c
@@ -70,14 +70,12 @@ static inline u8 *blkcipher_get_spot(u8 *start, unsigned int len)
return max(start, end_page);
}
-static inline unsigned int blkcipher_done_slow(struct crypto_blkcipher *tfm,
- struct blkcipher_walk *walk,
+static inline unsigned int blkcipher_done_slow(struct blkcipher_walk *walk,
unsigned int bsize)
{
u8 *addr;
- unsigned int alignmask = crypto_blkcipher_alignmask(tfm);
- addr = (u8 *)ALIGN((unsigned long)walk->buffer, alignmask + 1);
+ addr = (u8 *)ALIGN((unsigned long)walk->buffer, walk->alignmask + 1);
addr = blkcipher_get_spot(addr, bsize);
scatterwalk_copychunks(addr, &walk->out, bsize, 1);
return bsize;
@@ -105,7 +103,6 @@ static inline unsigned int blkcipher_done_fast(struct blkcipher_walk *walk,
int blkcipher_walk_done(struct blkcipher_desc *desc,
struct blkcipher_walk *walk, int err)
{
- struct crypto_blkcipher *tfm = desc->tfm;
unsigned int nbytes = 0;
if (likely(err >= 0)) {
@@ -117,7 +114,7 @@ int blkcipher_walk_done(struct blkcipher_desc *desc,
err = -EINVAL;
goto err;
} else
- n = blkcipher_done_slow(tfm, walk, n);
+ n = blkcipher_done_slow(walk, n);
nbytes = walk->total - n;
err = 0;
@@ -136,7 +133,7 @@ err:
}
if (walk->iv != desc->info)
- memcpy(desc->info, walk->iv, crypto_blkcipher_ivsize(tfm));
+ memcpy(desc->info, walk->iv, walk->ivsize);
if (walk->buffer != walk->page)
kfree(walk->buffer);
if (walk->page)
@@ -226,22 +223,20 @@ static inline int blkcipher_next_fast(struct blkcipher_desc *desc,
static int blkcipher_walk_next(struct blkcipher_desc *desc,
struct blkcipher_walk *walk)
{
- struct crypto_blkcipher *tfm = desc->tfm;
- unsigned int alignmask = crypto_blkcipher_alignmask(tfm);
unsigned int bsize;
unsigned int n;
int err;
n = walk->total;
- if (unlikely(n < crypto_blkcipher_blocksize(tfm))) {
+ if (unlikely(n < walk->cipher_blocksize)) {
desc->flags |= CRYPTO_TFM_RES_BAD_BLOCK_LEN;
return blkcipher_walk_done(desc, walk, -EINVAL);
}
walk->flags &= ~(BLKCIPHER_WALK_SLOW | BLKCIPHER_WALK_COPY |
BLKCIPHER_WALK_DIFF);
- if (!scatterwalk_aligned(&walk->in, alignmask) ||
- !scatterwalk_aligned(&walk->out, alignmask)) {
+ if (!scatterwalk_aligned(&walk->in, walk->alignmask) ||
+ !scatterwalk_aligned(&walk->out, walk->alignmask)) {
walk->flags |= BLKCIPHER_WALK_COPY;
if (!walk->page) {
walk->page = (void *)__get_free_page(GFP_ATOMIC);
@@ -250,12 +245,12 @@ static int blkcipher_walk_next(struct blkcipher_desc *desc,
}
}
- bsize = min(walk->blocksize, n);
+ bsize = min(walk->walk_blocksize, n);
n = scatterwalk_clamp(&walk->in, n);
n = scatterwalk_clamp(&walk->out, n);
if (unlikely(n < bsize)) {
- err = blkcipher_next_slow(desc, walk, bsize, alignmask);
+ err = blkcipher_next_slow(desc, walk, bsize, walk->alignmask);
goto set_phys_lowmem;
}
@@ -277,28 +272,26 @@ set_phys_lowmem:
return err;
}
-static inline int blkcipher_copy_iv(struct blkcipher_walk *walk,
- struct crypto_blkcipher *tfm,
- unsigned int alignmask)
+static inline int blkcipher_copy_iv(struct blkcipher_walk *walk)
{
- unsigned bs = walk->blocksize;
- unsigned int ivsize = crypto_blkcipher_ivsize(tfm);
- unsigned aligned_bs = ALIGN(bs, alignmask + 1);
- unsigned int size = aligned_bs * 2 + ivsize + max(aligned_bs, ivsize) -
- (alignmask + 1);
+ unsigned bs = walk->walk_blocksize;
+ unsigned aligned_bs = ALIGN(bs, walk->alignmask + 1);
+ unsigned int size = aligned_bs * 2 +
+ walk->ivsize + max(aligned_bs, walk->ivsize) -
+ (walk->alignmask + 1);
u8 *iv;
- size += alignmask & ~(crypto_tfm_ctx_alignment() - 1);
+ size += walk->alignmask & ~(crypto_tfm_ctx_alignment() - 1);
walk->buffer = kmalloc(size, GFP_ATOMIC);
if (!walk->buffer)
return -ENOMEM;
- iv = (u8 *)ALIGN((unsigned long)walk->buffer, alignmask + 1);
+ iv = (u8 *)ALIGN((unsigned long)walk->buffer, walk->alignmask + 1);
iv = blkcipher_get_spot(iv, bs) + aligned_bs;
iv = blkcipher_get_spot(iv, bs) + aligned_bs;
- iv = blkcipher_get_spot(iv, ivsize);
+ iv = blkcipher_get_spot(iv, walk->ivsize);
- walk->iv = memcpy(iv, walk->iv, ivsize);
+ walk->iv = memcpy(iv, walk->iv, walk->ivsize);
return 0;
}
@@ -306,7 +299,10 @@ int blkcipher_walk_virt(struct blkcipher_desc *desc,
struct blkcipher_walk *walk)
{
walk->flags &= ~BLKCIPHER_WALK_PHYS;
- walk->blocksize = crypto_blkcipher_blocksize(desc->tfm);
+ walk->walk_blocksize = crypto_blkcipher_blocksize(desc->tfm);
+ walk->cipher_blocksize = walk->walk_blocksize;
+ walk->ivsize = crypto_blkcipher_ivsize(desc->tfm);
+ walk->alignmask = crypto_blkcipher_alignmask(desc->tfm);
return blkcipher_walk_first(desc, walk);
}
EXPORT_SYMBOL_GPL(blkcipher_walk_virt);
@@ -315,7 +311,10 @@ int blkcipher_walk_phys(struct blkcipher_desc *desc,
struct blkcipher_walk *walk)
{
walk->flags |= BLKCIPHER_WALK_PHYS;
- walk->blocksize = crypto_blkcipher_blocksize(desc->tfm);
+ walk->walk_blocksize = crypto_blkcipher_blocksize(desc->tfm);
+ walk->cipher_blocksize = walk->walk_blocksize;
+ walk->ivsize = crypto_blkcipher_ivsize(desc->tfm);
+ walk->alignmask = crypto_blkcipher_alignmask(desc->tfm);
return blkcipher_walk_first(desc, walk);
}
EXPORT_SYMBOL_GPL(blkcipher_walk_phys);
@@ -323,9 +322,6 @@ EXPORT_SYMBOL_GPL(blkcipher_walk_phys);
static int blkcipher_walk_first(struct blkcipher_desc *desc,
struct blkcipher_walk *walk)
{
- struct crypto_blkcipher *tfm = desc->tfm;
- unsigned int alignmask = crypto_blkcipher_alignmask(tfm);
-
if (WARN_ON_ONCE(in_irq()))
return -EDEADLK;
@@ -335,8 +331,8 @@ static int blkcipher_walk_first(struct blkcipher_desc *desc,
walk->buffer = NULL;
walk->iv = desc->info;
- if (unlikely(((unsigned long)walk->iv & alignmask))) {
- int err = blkcipher_copy_iv(walk, tfm, alignmask);
+ if (unlikely(((unsigned long)walk->iv & walk->alignmask))) {
+ int err = blkcipher_copy_iv(walk);
if (err)
return err;
}
@@ -353,11 +349,28 @@ int blkcipher_walk_virt_block(struct blkcipher_desc *desc,
unsigned int blocksize)
{
walk->flags &= ~BLKCIPHER_WALK_PHYS;
- walk->blocksize = blocksize;
+ walk->walk_blocksize = blocksize;
+ walk->cipher_blocksize = crypto_blkcipher_blocksize(desc->tfm);
+ walk->ivsize = crypto_blkcipher_ivsize(desc->tfm);
+ walk->alignmask = crypto_blkcipher_alignmask(desc->tfm);
return blkcipher_walk_first(desc, walk);
}
EXPORT_SYMBOL_GPL(blkcipher_walk_virt_block);
+int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc,
+ struct blkcipher_walk *walk,
+ struct crypto_aead *tfm,
+ unsigned int blocksize)
+{
+ walk->flags &= ~BLKCIPHER_WALK_PHYS;
+ walk->walk_blocksize = blocksize;
+ walk->cipher_blocksize = crypto_aead_blocksize(tfm);
+ walk->ivsize = crypto_aead_ivsize(tfm);
+ walk->alignmask = crypto_aead_alignmask(tfm);
+ return blkcipher_walk_first(desc, walk);
+}
+EXPORT_SYMBOL_GPL(blkcipher_aead_walk_virt_block);
+
static int setkey_unaligned(struct crypto_tfm *tfm, const u8 *key,
unsigned int keylen)
{
diff --git a/crypto/crc32c.c b/crypto/crc32c_generic.c
index 06f7018c9d95..d9c7beba8e50 100644
--- a/crypto/crc32c.c
+++ b/crypto/crc32c_generic.c
@@ -170,3 +170,5 @@ module_exit(crc32c_mod_fini);
MODULE_AUTHOR("Clay Haapala <chaapala@cisco.com>");
MODULE_DESCRIPTION("CRC32c (Castagnoli) calculations wrapper for lib/crc32c");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("crc32c");
+MODULE_SOFTDEP("pre: crc32c");
diff --git a/crypto/crypto_null.c b/crypto/crypto_null.c
index fee7265cd35d..1dc54bb95a87 100644
--- a/crypto/crypto_null.c
+++ b/crypto/crypto_null.c
@@ -17,6 +17,7 @@
*
*/
+#include <crypto/null.h>
#include <crypto/internal/hash.h>
#include <crypto/internal/skcipher.h>
#include <linux/init.h>
@@ -24,11 +25,6 @@
#include <linux/mm.h>
#include <linux/string.h>
-#define NULL_KEY_SIZE 0
-#define NULL_BLOCK_SIZE 1
-#define NULL_DIGEST_SIZE 0
-#define NULL_IV_SIZE 0
-
static int null_compress(struct crypto_tfm *tfm, const u8 *src,
unsigned int slen, u8 *dst, unsigned int *dlen)
{
diff --git a/crypto/crypto_wq.c b/crypto/crypto_wq.c
index adad92a44ba2..2f1b8d12952a 100644
--- a/crypto/crypto_wq.c
+++ b/crypto/crypto_wq.c
@@ -33,7 +33,7 @@ static void __exit crypto_wq_exit(void)
destroy_workqueue(kcrypto_wq);
}
-module_init(crypto_wq_init);
+subsys_initcall(crypto_wq_init);
module_exit(crypto_wq_exit);
MODULE_LICENSE("GPL");
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index 0d9003ae8c61..870be7b4dc05 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -1511,6 +1511,14 @@ static int do_test(int m)
ret += tcrypt_test("authenc(hmac(sha1),cbc(aes))");
break;
+ case 156:
+ ret += tcrypt_test("authenc(hmac(md5),ecb(cipher_null))");
+ break;
+
+ case 157:
+ ret += tcrypt_test("authenc(hmac(sha1),ecb(cipher_null))");
+ break;
+
case 200:
test_cipher_speed("ecb(aes)", ENCRYPT, sec, NULL, 0,
speed_template_16_24_32);
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 77955507f6f1..dc3cf3535ef0 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -1809,6 +1809,22 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}
}, {
+ .alg = "authenc(hmac(md5),ecb(cipher_null))",
+ .test = alg_test_aead,
+ .fips_allowed = 1,
+ .suite = {
+ .aead = {
+ .enc = {
+ .vecs = hmac_md5_ecb_cipher_null_enc_tv_template,
+ .count = HMAC_MD5_ECB_CIPHER_NULL_ENC_TEST_VECTORS
+ },
+ .dec = {
+ .vecs = hmac_md5_ecb_cipher_null_dec_tv_template,
+ .count = HMAC_MD5_ECB_CIPHER_NULL_DEC_TEST_VECTORS
+ }
+ }
+ }
+ }, {
.alg = "authenc(hmac(sha1),cbc(aes))",
.test = alg_test_aead,
.fips_allowed = 1,
@@ -1821,6 +1837,22 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}
}, {
+ .alg = "authenc(hmac(sha1),ecb(cipher_null))",
+ .test = alg_test_aead,
+ .fips_allowed = 1,
+ .suite = {
+ .aead = {
+ .enc = {
+ .vecs = hmac_sha1_ecb_cipher_null_enc_tv_template,
+ .count = HMAC_SHA1_ECB_CIPHER_NULL_ENC_TEST_VECTORS
+ },
+ .dec = {
+ .vecs = hmac_sha1_ecb_cipher_null_dec_tv_template,
+ .count = HMAC_SHA1_ECB_CIPHER_NULL_DEC_TEST_VECTORS
+ }
+ }
+ }
+ }, {
.alg = "authenc(hmac(sha256),cbc(aes))",
.test = alg_test_aead,
.fips_allowed = 1,
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 7d44aa3d6b44..3db83dbba1d9 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -12821,6 +12821,10 @@ static struct cipher_testvec cast6_xts_dec_tv_template[] = {
#define AES_DEC_TEST_VECTORS 4
#define AES_CBC_ENC_TEST_VECTORS 5
#define AES_CBC_DEC_TEST_VECTORS 5
+#define HMAC_MD5_ECB_CIPHER_NULL_ENC_TEST_VECTORS 2
+#define HMAC_MD5_ECB_CIPHER_NULL_DEC_TEST_VECTORS 2
+#define HMAC_SHA1_ECB_CIPHER_NULL_ENC_TEST_VECTORS 2
+#define HMAC_SHA1_ECB_CIPHER_NULL_DEC_TEST_VECTORS 2
#define HMAC_SHA1_AES_CBC_ENC_TEST_VECTORS 7
#define HMAC_SHA256_AES_CBC_ENC_TEST_VECTORS 7
#define HMAC_SHA512_AES_CBC_ENC_TEST_VECTORS 7
@@ -13627,6 +13631,90 @@ static struct cipher_testvec aes_cbc_dec_tv_template[] = {
},
};
+static struct aead_testvec hmac_md5_ecb_cipher_null_enc_tv_template[] = {
+ { /* Input data from RFC 2410 Case 1 */
+#ifdef __LITTLE_ENDIAN
+ .key = "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+#else
+ .key = "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+#endif
+ "\x00\x00\x00\x00" /* enc key length */
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .klen = 8 + 16 + 0,
+ .iv = "",
+ .input = "\x01\x23\x45\x67\x89\xab\xcd\xef",
+ .ilen = 8,
+ .result = "\x01\x23\x45\x67\x89\xab\xcd\xef"
+ "\xaa\x42\xfe\x43\x8d\xea\xa3\x5a"
+ "\xb9\x3d\x9f\xb1\xa3\x8e\x9b\xae",
+ .rlen = 8 + 16,
+ }, { /* Input data from RFC 2410 Case 2 */
+#ifdef __LITTLE_ENDIAN
+ .key = "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+#else
+ .key = "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+#endif
+ "\x00\x00\x00\x00" /* enc key length */
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .klen = 8 + 16 + 0,
+ .iv = "",
+ .input = "Network Security People Have A Strange Sense Of Humor",
+ .ilen = 53,
+ .result = "Network Security People Have A Strange Sense Of Humor"
+ "\x73\xa5\x3e\x1c\x08\x0e\x8a\x8a"
+ "\x8e\xb5\x5f\x90\x8e\xfe\x13\x23",
+ .rlen = 53 + 16,
+ },
+};
+
+static struct aead_testvec hmac_md5_ecb_cipher_null_dec_tv_template[] = {
+ {
+#ifdef __LITTLE_ENDIAN
+ .key = "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+#else
+ .key = "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+#endif
+ "\x00\x00\x00\x00" /* enc key length */
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .klen = 8 + 16 + 0,
+ .iv = "",
+ .input = "\x01\x23\x45\x67\x89\xab\xcd\xef"
+ "\xaa\x42\xfe\x43\x8d\xea\xa3\x5a"
+ "\xb9\x3d\x9f\xb1\xa3\x8e\x9b\xae",
+ .ilen = 8 + 16,
+ .result = "\x01\x23\x45\x67\x89\xab\xcd\xef",
+ .rlen = 8,
+ }, {
+#ifdef __LITTLE_ENDIAN
+ .key = "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+#else
+ .key = "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+#endif
+ "\x00\x00\x00\x00" /* enc key length */
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .klen = 8 + 16 + 0,
+ .iv = "",
+ .input = "Network Security People Have A Strange Sense Of Humor"
+ "\x73\xa5\x3e\x1c\x08\x0e\x8a\x8a"
+ "\x8e\xb5\x5f\x90\x8e\xfe\x13\x23",
+ .ilen = 53 + 16,
+ .result = "Network Security People Have A Strange Sense Of Humor",
+ .rlen = 53,
+ },
+};
+
static struct aead_testvec hmac_sha1_aes_cbc_enc_tv_template[] = {
{ /* RFC 3602 Case 1 */
#ifdef __LITTLE_ENDIAN
@@ -13876,6 +13964,98 @@ static struct aead_testvec hmac_sha1_aes_cbc_enc_tv_template[] = {
},
};
+static struct aead_testvec hmac_sha1_ecb_cipher_null_enc_tv_template[] = {
+ { /* Input data from RFC 2410 Case 1 */
+#ifdef __LITTLE_ENDIAN
+ .key = "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+#else
+ .key = "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+#endif
+ "\x00\x00\x00\x00" /* enc key length */
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00",
+ .klen = 8 + 20 + 0,
+ .iv = "",
+ .input = "\x01\x23\x45\x67\x89\xab\xcd\xef",
+ .ilen = 8,
+ .result = "\x01\x23\x45\x67\x89\xab\xcd\xef"
+ "\x40\xc3\x0a\xa1\xc9\xa0\x28\xab"
+ "\x99\x5e\x19\x04\xd1\x72\xef\xb8"
+ "\x8c\x5e\xe4\x08",
+ .rlen = 8 + 20,
+ }, { /* Input data from RFC 2410 Case 2 */
+#ifdef __LITTLE_ENDIAN
+ .key = "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+#else
+ .key = "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+#endif
+ "\x00\x00\x00\x00" /* enc key length */
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00",
+ .klen = 8 + 20 + 0,
+ .iv = "",
+ .input = "Network Security People Have A Strange Sense Of Humor",
+ .ilen = 53,
+ .result = "Network Security People Have A Strange Sense Of Humor"
+ "\x75\x6f\x42\x1e\xf8\x50\x21\xd2"
+ "\x65\x47\xee\x8e\x1a\xef\x16\xf6"
+ "\x91\x56\xe4\xd6",
+ .rlen = 53 + 20,
+ },
+};
+
+static struct aead_testvec hmac_sha1_ecb_cipher_null_dec_tv_template[] = {
+ {
+#ifdef __LITTLE_ENDIAN
+ .key = "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+#else
+ .key = "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+#endif
+ "\x00\x00\x00\x00" /* enc key length */
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00",
+ .klen = 8 + 20 + 0,
+ .iv = "",
+ .input = "\x01\x23\x45\x67\x89\xab\xcd\xef"
+ "\x40\xc3\x0a\xa1\xc9\xa0\x28\xab"
+ "\x99\x5e\x19\x04\xd1\x72\xef\xb8"
+ "\x8c\x5e\xe4\x08",
+ .ilen = 8 + 20,
+ .result = "\x01\x23\x45\x67\x89\xab\xcd\xef",
+ .rlen = 8,
+ }, {
+#ifdef __LITTLE_ENDIAN
+ .key = "\x08\x00" /* rta length */
+ "\x01\x00" /* rta type */
+#else
+ .key = "\x00\x08" /* rta length */
+ "\x00\x01" /* rta type */
+#endif
+ "\x00\x00\x00\x00" /* enc key length */
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00",
+ .klen = 8 + 20 + 0,
+ .iv = "",
+ .input = "Network Security People Have A Strange Sense Of Humor"
+ "\x75\x6f\x42\x1e\xf8\x50\x21\xd2"
+ "\x65\x47\xee\x8e\x1a\xef\x16\xf6"
+ "\x91\x56\xe4\xd6",
+ .ilen = 53 + 20,
+ .result = "Network Security People Have A Strange Sense Of Humor",
+ .rlen = 53,
+ },
+};
+
static struct aead_testvec hmac_sha256_aes_cbc_enc_tv_template[] = {
{ /* RFC 3602 Case 1 */
#ifdef __LITTLE_ENDIAN
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 9e6029105607..3cf61a127ee5 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -83,7 +83,7 @@ static struct device_attribute amba_dev_attrs[] = {
__ATTR_NULL,
};
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
/*
* Hooks to provide runtime PM of the pclk (bus clock). It is safe to
* enable/disable the bus clock at runtime PM suspend/resume as this
@@ -123,7 +123,7 @@ static const struct dev_pm_ops amba_pm = {
.thaw = pm_generic_thaw,
.poweroff = pm_generic_poweroff,
.restore = pm_generic_restore,
- SET_RUNTIME_PM_OPS(
+ SET_PM_RUNTIME_PM_OPS(
amba_pm_runtime_suspend,
amba_pm_runtime_resume,
NULL
diff --git a/drivers/amba/tegra-ahb.c b/drivers/amba/tegra-ahb.c
index 1f44e56cc65d..558a239954e8 100644
--- a/drivers/amba/tegra-ahb.c
+++ b/drivers/amba/tegra-ahb.c
@@ -256,8 +256,6 @@ static int tegra_ahb_probe(struct platform_device *pdev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
ahb->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ahb->regs))
return PTR_ERR(ahb->regs);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 34406f7fdd7a..c19734d96d7e 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -1524,7 +1524,7 @@ static void ata_qc_complete_internal(struct ata_queued_cmd *qc)
* @dev: Device to which the command is sent
* @tf: Taskfile registers for the command and the result
* @cdb: CDB for packet command
- * @dma_dir: Data tranfer direction of the command
+ * @dma_dir: Data transfer direction of the command
* @sgl: sg list for the data buffer of the command
* @n_elem: Number of sg entries
* @timeout: Timeout in msecs (0 for default)
@@ -1712,7 +1712,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
* @dev: Device to which the command is sent
* @tf: Taskfile registers for the command and the result
* @cdb: CDB for packet command
- * @dma_dir: Data tranfer direction of the command
+ * @dma_dir: Data transfer direction of the command
* @buf: Data buffer of the command
* @buflen: Length of data buffer
* @timeout: Timeout in msecs (0 for default)
diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c
index 62a76076b548..f1a9198dfe5a 100644
--- a/drivers/atm/ambassador.c
+++ b/drivers/atm/ambassador.c
@@ -1925,7 +1925,7 @@ static int ucode_init(loader_block *lb, amb_dev *dev)
const struct firmware *fw;
unsigned long start_address;
const struct ihex_binrec *rec;
- const char *errmsg = 0;
+ const char *errmsg = NULL;
int res;
res = request_ihex_firmware(&fw, "atmsar11.fw", &dev->pci_dev->dev);
diff --git a/drivers/atm/firestream.c b/drivers/atm/firestream.c
index b41c9481b67b..82f2ae0d7cc4 100644
--- a/drivers/atm/firestream.c
+++ b/drivers/atm/firestream.c
@@ -736,8 +736,8 @@ static void process_txdone_queue (struct fs_dev *dev, struct queue *q)
skb = td->skb;
if (skb == FS_VCC (ATM_SKB(skb)->vcc)->last_skb) {
- wake_up_interruptible (& FS_VCC (ATM_SKB(skb)->vcc)->close_wait);
FS_VCC (ATM_SKB(skb)->vcc)->last_skb = NULL;
+ wake_up_interruptible (& FS_VCC (ATM_SKB(skb)->vcc)->close_wait);
}
td->dev->ntxpckts--;
@@ -1123,7 +1123,7 @@ static void fs_close(struct atm_vcc *atm_vcc)
this sleep_on, we'll lose any reference to these packets. Memory leak!
On the other hand, it's awfully convenient that we can abort a "close" that
is taking too long. Maybe just use non-interruptible sleep on? -- REW */
- interruptible_sleep_on (& vcc->close_wait);
+ wait_event_interruptible(vcc->close_wait, !vcc->last_skb);
}
txtp = &atm_vcc->qos.txtp;
@@ -2000,7 +2000,7 @@ static void firestream_remove_one(struct pci_dev *pdev)
fs_dprintk (FS_DEBUG_CLEANUP, "Freeing irq%d.\n", dev->irq);
free_irq (dev->irq, dev);
- del_timer (&dev->timer);
+ del_timer_sync (&dev->timer);
atm_dev_deregister(dev->atm_dev);
free_queue (dev, &dev->hp_txq);
diff --git a/drivers/atm/idt77105.c b/drivers/atm/idt77105.c
index 45d506363aba..909c95bd7be2 100644
--- a/drivers/atm/idt77105.c
+++ b/drivers/atm/idt77105.c
@@ -368,9 +368,9 @@ EXPORT_SYMBOL(idt77105_init);
static void __exit idt77105_exit(void)
{
- /* turn off timers */
- del_timer(&stats_timer);
- del_timer(&restart_timer);
+ /* turn off timers */
+ del_timer_sync(&stats_timer);
+ del_timer_sync(&restart_timer);
}
module_exit(idt77105_exit);
diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c
index 9587e959ce1a..9988ac98b6d8 100644
--- a/drivers/atm/nicstar.c
+++ b/drivers/atm/nicstar.c
@@ -639,9 +639,9 @@ static int ns_init_card(int i, struct pci_dev *pcidev)
card->hbnr.init = NUM_HB;
card->hbnr.max = MAX_HB;
- card->sm_handle = 0x00000000;
+ card->sm_handle = NULL;
card->sm_addr = 0x00000000;
- card->lg_handle = 0x00000000;
+ card->lg_handle = NULL;
card->lg_addr = 0x00000000;
card->efbie = 1; /* To prevent push_rxbufs from enabling the interrupt */
@@ -979,7 +979,7 @@ static void push_rxbufs(ns_dev * card, struct sk_buff *skb)
addr2 = card->sm_addr;
handle2 = card->sm_handle;
card->sm_addr = 0x00000000;
- card->sm_handle = 0x00000000;
+ card->sm_handle = NULL;
} else { /* (!sm_addr) */
card->sm_addr = addr1;
@@ -993,7 +993,7 @@ static void push_rxbufs(ns_dev * card, struct sk_buff *skb)
addr2 = card->lg_addr;
handle2 = card->lg_handle;
card->lg_addr = 0x00000000;
- card->lg_handle = 0x00000000;
+ card->lg_handle = NULL;
} else { /* (!lg_addr) */
card->lg_addr = addr1;
@@ -1739,10 +1739,10 @@ static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd,
}
scq->full = 1;
- spin_unlock_irqrestore(&scq->lock, flags);
- interruptible_sleep_on_timeout(&scq->scqfull_waitq,
- SCQFULL_TIMEOUT);
- spin_lock_irqsave(&scq->lock, flags);
+ wait_event_interruptible_lock_irq_timeout(scq->scqfull_waitq,
+ scq->tail != scq->next,
+ scq->lock,
+ SCQFULL_TIMEOUT);
if (scq->full) {
spin_unlock_irqrestore(&scq->lock, flags);
@@ -1789,10 +1789,10 @@ static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd,
scq->full = 1;
if (has_run++)
break;
- spin_unlock_irqrestore(&scq->lock, flags);
- interruptible_sleep_on_timeout(&scq->scqfull_waitq,
- SCQFULL_TIMEOUT);
- spin_lock_irqsave(&scq->lock, flags);
+ wait_event_interruptible_lock_irq_timeout(scq->scqfull_waitq,
+ scq->tail != scq->next,
+ scq->lock,
+ SCQFULL_TIMEOUT);
}
if (!scq->full) {
diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c
index e3fb496c7163..943cf0d6abaf 100644
--- a/drivers/atm/solos-pci.c
+++ b/drivers/atm/solos-pci.c
@@ -760,7 +760,7 @@ static irqreturn_t solos_irq(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-void solos_bh(unsigned long card_arg)
+static void solos_bh(unsigned long card_arg)
{
struct solos_card *card = (void *)card_arg;
uint32_t card_flags;
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 59dc8086e4fa..83e910a57563 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -1218,7 +1218,7 @@ err_dev:
* with the name of the subsystem. The root device can carry subsystem-
* wide attributes. All registered devices are below this single root
* device and are named after the subsystem with a simple enumeration
- * number appended. The registered devices are not explicitely named;
+ * number appended. The registered devices are not explicitly named;
* only 'id' in the device needs to be set.
*
* Do not use this interface for anything new, it exists for compatibility
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index ad9d17762664..bbcbd3c43926 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -160,16 +160,20 @@ static int topology_cpu_callback(struct notifier_block *nfb,
static int topology_sysfs_init(void)
{
int cpu;
- int rc;
+ int rc = 0;
+
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu) {
rc = topology_add_dev(cpu);
if (rc)
- return rc;
+ goto out;
}
- hotcpu_notifier(topology_cpu_callback, 0);
+ __hotcpu_notifier(topology_cpu_callback, 0);
- return 0;
+out:
+ cpu_notifier_register_done();
+ return rc;
}
device_initcall(topology_sysfs_init);
diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c
index 25f9887a35d0..d7f81ad56b8a 100644
--- a/drivers/bcma/driver_gpio.c
+++ b/drivers/bcma/driver_gpio.c
@@ -218,7 +218,14 @@ int bcma_gpio_init(struct bcma_drv_cc *cc)
#if IS_BUILTIN(CONFIG_BCMA_HOST_SOC)
chip->to_irq = bcma_gpio_to_irq;
#endif
- chip->ngpio = 16;
+ switch (cc->core->bus->chipinfo.id) {
+ case BCMA_CHIP_ID_BCM5357:
+ chip->ngpio = 32;
+ break;
+ default:
+ chip->ngpio = 16;
+ }
+
/* There is just one SoC in one device and its GPIO addresses should be
* deterministic to address them more easily. The other buses could get
* a random base number. */
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 34898d53395b..4c95b503b09e 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -1654,7 +1654,7 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req,
if (osd_req->r_result < 0)
obj_request->result = osd_req->r_result;
- BUG_ON(osd_req->r_num_ops > 2);
+ rbd_assert(osd_req->r_num_ops <= CEPH_OSD_MAX_OP);
/*
* We support a 64-bit length, but ultimately it has to be
@@ -1662,11 +1662,15 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req,
*/
obj_request->xferred = osd_req->r_reply_op_len[0];
rbd_assert(obj_request->xferred < (u64)UINT_MAX);
+
opcode = osd_req->r_ops[0].op;
switch (opcode) {
case CEPH_OSD_OP_READ:
rbd_osd_read_callback(obj_request);
break;
+ case CEPH_OSD_OP_SETALLOCHINT:
+ rbd_assert(osd_req->r_ops[1].op == CEPH_OSD_OP_WRITE);
+ /* fall through */
case CEPH_OSD_OP_WRITE:
rbd_osd_write_callback(obj_request);
break;
@@ -1715,9 +1719,16 @@ static void rbd_osd_req_format_write(struct rbd_obj_request *obj_request)
snapc, CEPH_NOSNAP, &mtime);
}
+/*
+ * Create an osd request. A read request has one osd op (read).
+ * A write request has either one (watch) or two (hint+write) osd ops.
+ * (All rbd data writes are prefixed with an allocation hint op, but
+ * technically osd watch is a write request, hence this distinction.)
+ */
static struct ceph_osd_request *rbd_osd_req_create(
struct rbd_device *rbd_dev,
bool write_request,
+ unsigned int num_ops,
struct rbd_obj_request *obj_request)
{
struct ceph_snap_context *snapc = NULL;
@@ -1733,10 +1744,13 @@ static struct ceph_osd_request *rbd_osd_req_create(
snapc = img_request->snapc;
}
- /* Allocate and initialize the request, for the single op */
+ rbd_assert(num_ops == 1 || (write_request && num_ops == 2));
+
+ /* Allocate and initialize the request, for the num_ops ops */
osdc = &rbd_dev->rbd_client->client->osdc;
- osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_ATOMIC);
+ osd_req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false,
+ GFP_ATOMIC);
if (!osd_req)
return NULL; /* ENOMEM */
@@ -1756,8 +1770,8 @@ static struct ceph_osd_request *rbd_osd_req_create(
/*
* Create a copyup osd request based on the information in the
- * object request supplied. A copyup request has two osd ops,
- * a copyup method call, and a "normal" write request.
+ * object request supplied. A copyup request has three osd ops,
+ * a copyup method call, a hint op, and a write op.
*/
static struct ceph_osd_request *
rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request)
@@ -1773,12 +1787,12 @@ rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request)
rbd_assert(img_request);
rbd_assert(img_request_write_test(img_request));
- /* Allocate and initialize the request, for the two ops */
+ /* Allocate and initialize the request, for the three ops */
snapc = img_request->snapc;
rbd_dev = img_request->rbd_dev;
osdc = &rbd_dev->rbd_client->client->osdc;
- osd_req = ceph_osdc_alloc_request(osdc, snapc, 2, false, GFP_ATOMIC);
+ osd_req = ceph_osdc_alloc_request(osdc, snapc, 3, false, GFP_ATOMIC);
if (!osd_req)
return NULL; /* ENOMEM */
@@ -2178,6 +2192,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
const char *object_name;
u64 offset;
u64 length;
+ unsigned int which = 0;
object_name = rbd_segment_name(rbd_dev, img_offset);
if (!object_name)
@@ -2190,6 +2205,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
rbd_segment_name_free(object_name);
if (!obj_request)
goto out_unwind;
+
/*
* set obj_request->img_request before creating the
* osd_request so that it gets the right snapc
@@ -2207,7 +2223,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
clone_size,
GFP_ATOMIC);
if (!obj_request->bio_list)
- goto out_partial;
+ goto out_unwind;
} else {
unsigned int page_count;
@@ -2220,19 +2236,27 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
}
osd_req = rbd_osd_req_create(rbd_dev, write_request,
- obj_request);
+ (write_request ? 2 : 1),
+ obj_request);
if (!osd_req)
- goto out_partial;
+ goto out_unwind;
obj_request->osd_req = osd_req;
obj_request->callback = rbd_img_obj_callback;
- osd_req_op_extent_init(osd_req, 0, opcode, offset, length,
- 0, 0);
+ if (write_request) {
+ osd_req_op_alloc_hint_init(osd_req, which,
+ rbd_obj_bytes(&rbd_dev->header),
+ rbd_obj_bytes(&rbd_dev->header));
+ which++;
+ }
+
+ osd_req_op_extent_init(osd_req, which, opcode, offset, length,
+ 0, 0);
if (type == OBJ_REQUEST_BIO)
- osd_req_op_extent_osd_data_bio(osd_req, 0,
+ osd_req_op_extent_osd_data_bio(osd_req, which,
obj_request->bio_list, length);
else
- osd_req_op_extent_osd_data_pages(osd_req, 0,
+ osd_req_op_extent_osd_data_pages(osd_req, which,
obj_request->pages, length,
offset & ~PAGE_MASK, false, false);
@@ -2249,11 +2273,9 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
return 0;
-out_partial:
- rbd_obj_request_put(obj_request);
out_unwind:
for_each_obj_request_safe(img_request, obj_request, next_obj_request)
- rbd_obj_request_put(obj_request);
+ rbd_img_obj_request_del(img_request, obj_request);
return -ENOMEM;
}
@@ -2353,7 +2375,7 @@ rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request)
/*
* The original osd request is of no use to use any more.
- * We need a new one that can hold the two ops in a copyup
+ * We need a new one that can hold the three ops in a copyup
* request. Allocate the new copyup osd request for the
* original request, and release the old one.
*/
@@ -2372,17 +2394,22 @@ rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request)
osd_req_op_cls_request_data_pages(osd_req, 0, pages, parent_length, 0,
false, false);
- /* Then the original write request op */
+ /* Then the hint op */
+
+ osd_req_op_alloc_hint_init(osd_req, 1, rbd_obj_bytes(&rbd_dev->header),
+ rbd_obj_bytes(&rbd_dev->header));
+
+ /* And the original write request op */
offset = orig_request->offset;
length = orig_request->length;
- osd_req_op_extent_init(osd_req, 1, CEPH_OSD_OP_WRITE,
+ osd_req_op_extent_init(osd_req, 2, CEPH_OSD_OP_WRITE,
offset, length, 0, 0);
if (orig_request->type == OBJ_REQUEST_BIO)
- osd_req_op_extent_osd_data_bio(osd_req, 1,
+ osd_req_op_extent_osd_data_bio(osd_req, 2,
orig_request->bio_list, length);
else
- osd_req_op_extent_osd_data_pages(osd_req, 1,
+ osd_req_op_extent_osd_data_pages(osd_req, 2,
orig_request->pages, length,
offset & ~PAGE_MASK, false, false);
@@ -2603,8 +2630,8 @@ static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request)
rbd_assert(obj_request->img_request);
rbd_dev = obj_request->img_request->rbd_dev;
- stat_request->osd_req = rbd_osd_req_create(rbd_dev, false,
- stat_request);
+ stat_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+ stat_request);
if (!stat_request->osd_req)
goto out;
stat_request->callback = rbd_img_obj_exists_callback;
@@ -2807,7 +2834,8 @@ static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, u64 notify_id)
return -ENOMEM;
ret = -ENOMEM;
- obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, obj_request);
+ obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+ obj_request);
if (!obj_request->osd_req)
goto out;
@@ -2870,7 +2898,8 @@ static int __rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, bool start)
if (!obj_request)
goto out_cancel;
- obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, obj_request);
+ obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, 1,
+ obj_request);
if (!obj_request->osd_req)
goto out_cancel;
@@ -2978,7 +3007,8 @@ static int rbd_obj_method_sync(struct rbd_device *rbd_dev,
obj_request->pages = pages;
obj_request->page_count = page_count;
- obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, obj_request);
+ obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+ obj_request);
if (!obj_request->osd_req)
goto out;
@@ -3211,7 +3241,8 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev,
obj_request->pages = pages;
obj_request->page_count = page_count;
- obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, obj_request);
+ obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+ obj_request);
if (!obj_request->osd_req)
goto out;
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 0eace43cea11..6d8a87f252de 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -158,6 +158,7 @@ static int virtio_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *req)
unsigned long flags;
unsigned int num;
const bool last = (req->cmd_flags & REQ_END) != 0;
+ int err;
BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems);
@@ -198,11 +199,16 @@ static int virtio_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *req)
}
spin_lock_irqsave(&vblk->vq_lock, flags);
- if (__virtblk_add_req(vblk->vq, vbr, vbr->sg, num) < 0) {
+ err = __virtblk_add_req(vblk->vq, vbr, vbr->sg, num);
+ if (err) {
virtqueue_kick(vblk->vq);
spin_unlock_irqrestore(&vblk->vq_lock, flags);
blk_mq_stop_hw_queue(hctx);
- return BLK_MQ_RQ_QUEUE_BUSY;
+ /* Out of mem doesn't actually happen, since we fall back
+ * to direct descriptors */
+ if (err == -ENOMEM || err == -ENOSPC)
+ return BLK_MQ_RQ_QUEUE_BUSY;
+ return BLK_MQ_RQ_QUEUE_ERROR;
}
if (last)
@@ -485,10 +491,11 @@ static struct blk_mq_ops virtio_mq_ops = {
static struct blk_mq_reg virtio_mq_reg = {
.ops = &virtio_mq_ops,
.nr_hw_queues = 1,
- .queue_depth = 64,
+ .queue_depth = 0, /* Set in virtblk_probe */
.numa_node = NUMA_NO_NODE,
.flags = BLK_MQ_F_SHOULD_MERGE,
};
+module_param_named(queue_depth, virtio_mq_reg.queue_depth, uint, 0444);
static int virtblk_init_vbr(void *data, struct blk_mq_hw_ctx *hctx,
struct request *rq, unsigned int nr)
@@ -553,6 +560,13 @@ static int virtblk_probe(struct virtio_device *vdev)
goto out_free_vq;
}
+ /* Default queue sizing is to fill the ring. */
+ if (!virtio_mq_reg.queue_depth) {
+ virtio_mq_reg.queue_depth = vblk->vq->num_free;
+ /* ... but without indirect descs, we use 2 descs per req */
+ if (!virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC))
+ virtio_mq_reg.queue_depth /= 2;
+ }
virtio_mq_reg.cmd_size =
sizeof(struct virtblk_req) +
sizeof(struct scatterlist) * sg_elems;
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 11a6104a1e4f..f5ce64e03fd7 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -241,5 +241,6 @@ config BT_WILINK
core driver to communicate with the BT core of the combo chip.
Say Y here to compile support for Texas Instrument's WiLink7 driver
- into the kernel or say M to compile it as module.
+ into the kernel or say M to compile it as module (btwilink).
+
endmenu
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 106d1d8e16ad..be571fef185d 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -62,50 +62,54 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x0CF3, 0x3000) },
/* Atheros AR3011 with sflash firmware*/
+ { USB_DEVICE(0x0489, 0xE027) },
+ { USB_DEVICE(0x0489, 0xE03D) },
+ { USB_DEVICE(0x0930, 0x0215) },
{ USB_DEVICE(0x0CF3, 0x3002) },
{ USB_DEVICE(0x0CF3, 0xE019) },
{ USB_DEVICE(0x13d3, 0x3304) },
- { USB_DEVICE(0x0930, 0x0215) },
- { USB_DEVICE(0x0489, 0xE03D) },
- { USB_DEVICE(0x0489, 0xE027) },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03F0, 0x311D) },
/* Atheros AR3012 with sflash firmware*/
- { USB_DEVICE(0x0CF3, 0x0036) },
- { USB_DEVICE(0x0CF3, 0x3004) },
- { USB_DEVICE(0x0CF3, 0x3008) },
- { USB_DEVICE(0x0CF3, 0x311D) },
- { USB_DEVICE(0x0CF3, 0x817a) },
- { USB_DEVICE(0x13d3, 0x3375) },
+ { USB_DEVICE(0x0489, 0xe04d) },
+ { USB_DEVICE(0x0489, 0xe04e) },
+ { USB_DEVICE(0x0489, 0xe057) },
+ { USB_DEVICE(0x0489, 0xe056) },
+ { USB_DEVICE(0x0489, 0xe05f) },
+ { USB_DEVICE(0x04c5, 0x1330) },
{ USB_DEVICE(0x04CA, 0x3004) },
{ USB_DEVICE(0x04CA, 0x3005) },
{ USB_DEVICE(0x04CA, 0x3006) },
{ USB_DEVICE(0x04CA, 0x3008) },
{ USB_DEVICE(0x04CA, 0x300b) },
- { USB_DEVICE(0x13d3, 0x3362) },
- { USB_DEVICE(0x0CF3, 0xE004) },
- { USB_DEVICE(0x0CF3, 0xE005) },
{ USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0930, 0x0220) },
- { USB_DEVICE(0x0489, 0xe057) },
- { USB_DEVICE(0x13d3, 0x3393) },
- { USB_DEVICE(0x0489, 0xe04e) },
- { USB_DEVICE(0x0489, 0xe056) },
- { USB_DEVICE(0x0489, 0xe04d) },
- { USB_DEVICE(0x04c5, 0x1330) },
- { USB_DEVICE(0x13d3, 0x3402) },
+ { USB_DEVICE(0x0b05, 0x17d0) },
+ { USB_DEVICE(0x0CF3, 0x0036) },
+ { USB_DEVICE(0x0CF3, 0x3004) },
+ { USB_DEVICE(0x0CF3, 0x3005) },
+ { USB_DEVICE(0x0CF3, 0x3008) },
+ { USB_DEVICE(0x0CF3, 0x311D) },
+ { USB_DEVICE(0x0CF3, 0x311E) },
+ { USB_DEVICE(0x0CF3, 0x311F) },
{ USB_DEVICE(0x0cf3, 0x3121) },
+ { USB_DEVICE(0x0CF3, 0x817a) },
{ USB_DEVICE(0x0cf3, 0xe003) },
- { USB_DEVICE(0x0489, 0xe05f) },
+ { USB_DEVICE(0x0CF3, 0xE004) },
+ { USB_DEVICE(0x0CF3, 0xE005) },
+ { USB_DEVICE(0x13d3, 0x3362) },
+ { USB_DEVICE(0x13d3, 0x3375) },
+ { USB_DEVICE(0x13d3, 0x3393) },
+ { USB_DEVICE(0x13d3, 0x3402) },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE02C) },
/* Atheros AR5BBU22 with sflash firmware */
- { USB_DEVICE(0x0489, 0xE03C) },
{ USB_DEVICE(0x0489, 0xE036) },
+ { USB_DEVICE(0x0489, 0xE03C) },
{ } /* Terminating entry */
};
@@ -118,36 +122,40 @@ MODULE_DEVICE_TABLE(usb, ath3k_table);
static const struct usb_device_id ath3k_blist_tbl[] = {
/* Atheros AR3012 with sflash firmware*/
- { USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x3005), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x311E), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x311F), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
- { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
{ } /* Terminating entry */
};
@@ -174,10 +182,9 @@ static int ath3k_load_firmware(struct usb_device *udev,
}
memcpy(send_buf, firmware->data, 20);
- if ((err = usb_control_msg(udev, pipe,
- USB_REQ_DFU_DNLOAD,
- USB_TYPE_VENDOR, 0, 0,
- send_buf, 20, USB_CTRL_SET_TIMEOUT)) < 0) {
+ err = usb_control_msg(udev, pipe, USB_REQ_DFU_DNLOAD, USB_TYPE_VENDOR,
+ 0, 0, send_buf, 20, USB_CTRL_SET_TIMEOUT);
+ if (err < 0) {
BT_ERR("Can't change to loading configuration err");
goto error;
}
@@ -360,7 +367,7 @@ static int ath3k_load_patch(struct usb_device *udev)
}
snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu",
- fw_version.rom_version);
+ le32_to_cpu(fw_version.rom_version));
ret = request_firmware(&firmware, filename, &udev->dev);
if (ret < 0) {
@@ -422,7 +429,7 @@ static int ath3k_load_syscfg(struct usb_device *udev)
}
snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s",
- fw_version.rom_version, clk_value, ".dfu");
+ le32_to_cpu(fw_version.rom_version), clk_value, ".dfu");
ret = request_firmware(&firmware, filename, &udev->dev);
if (ret < 0) {
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index 31386998c9a7..b2e7e94a6771 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -131,8 +131,11 @@ static int bfusb_send_bulk(struct bfusb_data *data, struct sk_buff *skb)
BT_DBG("bfusb %p skb %p len %d", data, skb, skb->len);
- if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
- return -ENOMEM;
+ if (!urb) {
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+ }
pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
@@ -218,8 +221,11 @@ static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb)
BT_DBG("bfusb %p urb %p", data, urb);
- if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
- return -ENOMEM;
+ if (!urb) {
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+ }
skb = bt_skb_alloc(size, GFP_ATOMIC);
if (!skb) {
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index 57427de864a6..dfa5043e68ba 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -257,7 +257,8 @@ static void bluecard_write_wakeup(bluecard_info_t *info)
ready_bit = XMIT_BUF_ONE_READY;
}
- if (!(skb = skb_dequeue(&(info->txq))))
+ skb = skb_dequeue(&(info->txq));
+ if (!skb)
break;
if (bt_cb(skb)->pkt_type & 0x80) {
@@ -391,7 +392,8 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
if (info->rx_skb == NULL) {
info->rx_state = RECV_WAIT_PACKET_TYPE;
info->rx_count = 0;
- if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+ if (!info->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
return;
}
@@ -566,7 +568,8 @@ static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
/* Ericsson baud rate command */
unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
- if (!(skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+ if (!skb) {
BT_ERR("Can't allocate mem for new packet");
return -1;
}
@@ -898,7 +901,7 @@ static void bluecard_release(struct pcmcia_device *link)
bluecard_close(info);
- del_timer(&(info->timer));
+ del_timer_sync(&(info->timer));
pcmcia_disable_device(link);
}
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 73d87994d028..1d82721cf9c6 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -193,8 +193,8 @@ static void bt3c_write_wakeup(bt3c_info_t *info)
if (!pcmcia_dev_present(info->p_dev))
break;
-
- if (!(skb = skb_dequeue(&(info->txq)))) {
+ skb = skb_dequeue(&(info->txq));
+ if (!skb) {
clear_bit(XMIT_SENDING, &(info->tx_state));
break;
}
@@ -238,7 +238,8 @@ static void bt3c_receive(bt3c_info_t *info)
if (info->rx_skb == NULL) {
info->rx_state = RECV_WAIT_PACKET_TYPE;
info->rx_count = 0;
- if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+ if (!info->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
return;
}
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index 1e0320af00c6..2c4997ce2484 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -59,12 +59,13 @@ bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
priv->btmrvl_dev.sendcmdflag = false;
priv->adapter->cmd_complete = true;
wake_up_interruptible(&priv->adapter->cmd_wait_q);
- }
- if (hci_opcode_ogf(opcode) == 0x3F) {
- BT_DBG("vendor event skipped: opcode=%#4.4x", opcode);
- kfree_skb(skb);
- return false;
+ if (hci_opcode_ogf(opcode) == 0x3F) {
+ BT_DBG("vendor event skipped: opcode=%#4.4x",
+ opcode);
+ kfree_skb(skb);
+ return false;
+ }
}
}
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index a03ecc22a561..fb948f02eda5 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -149,7 +149,8 @@ static void btuart_write_wakeup(btuart_info_t *info)
if (!pcmcia_dev_present(info->p_dev))
return;
- if (!(skb = skb_dequeue(&(info->txq))))
+ skb = skb_dequeue(&(info->txq));
+ if (!skb)
break;
/* Send frame */
@@ -190,7 +191,8 @@ static void btuart_receive(btuart_info_t *info)
if (info->rx_skb == NULL) {
info->rx_state = RECV_WAIT_PACKET_TYPE;
info->rx_count = 0;
- if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+ if (!info->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
return;
}
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index baeaaed299e4..f338b0c5a8de 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -101,21 +101,24 @@ static const struct usb_device_id btusb_table[] = {
{ USB_DEVICE(0x0c10, 0x0000) },
/* Broadcom BCM20702A0 */
+ { USB_DEVICE(0x0489, 0xe042) },
+ { USB_DEVICE(0x04ca, 0x2003) },
{ USB_DEVICE(0x0b05, 0x17b5) },
{ USB_DEVICE(0x0b05, 0x17cb) },
- { USB_DEVICE(0x04ca, 0x2003) },
- { USB_DEVICE(0x0489, 0xe042) },
{ USB_DEVICE(0x413c, 0x8197) },
/* Foxconn - Hon Hai */
{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
- /*Broadcom devices with vendor specific id */
+ /* Broadcom devices with vendor specific id */
{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
/* Belkin F8065bf - Broadcom based */
{ USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
+ /* IMC Networks - Broadcom based */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) },
+
{ } /* Terminating entry */
};
@@ -129,55 +132,59 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
/* Atheros 3011 with sflash firmware */
+ { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
+ { USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
+ { USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0cf3, 0xe019), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
- { USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
- { USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
- { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
/* Atheros 3012 with sflash firmware */
- { USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x3005), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x311e), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x311f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
- { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
/* Atheros AR5BBU12 with sflash firmware */
- { USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe036), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
/* Broadcom BCM2035 */
- { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
- { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
+ { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
/* Broadcom BCM2045 */
{ USB_DEVICE(0x0a5c, 0x2039), .driver_info = BTUSB_WRONG_SCO_MTU },
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index 52eed1f3565d..2bd8fad17206 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -153,7 +153,8 @@ static void dtl1_write_wakeup(dtl1_info_t *info)
if (!pcmcia_dev_present(info->p_dev))
return;
- if (!(skb = skb_dequeue(&(info->txq))))
+ skb = skb_dequeue(&(info->txq));
+ if (!skb)
break;
/* Send frame */
@@ -215,13 +216,15 @@ static void dtl1_receive(dtl1_info_t *info)
info->hdev->stat.byte_rx++;
/* Allocate packet */
- if (info->rx_skb == NULL)
- if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ if (info->rx_skb == NULL) {
+ info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+ if (!info->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
info->rx_state = RECV_WAIT_NSH;
info->rx_count = NSHL;
return;
}
+ }
*skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
nsh = (nsh_t *)info->rx_skb->data;
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index 0bc87f7abd95..21cc45b34f13 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -291,7 +291,8 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
/* First of all, check for unreliable messages in the queue,
since they have priority */
- if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) {
+ 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);
if (nskb) {
kfree_skb(skb);
@@ -308,16 +309,20 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING);
- if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) {
- struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type);
- if (nskb) {
- __skb_queue_tail(&bcsp->unack, skb);
- mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
- spin_unlock_irqrestore(&bcsp->unack.lock, flags);
- return nskb;
- } else {
- skb_queue_head(&bcsp->rel, skb);
- BT_ERR("Could not dequeue pkt because alloc_skb failed");
+ 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);
+ if (nskb) {
+ __skb_queue_tail(&bcsp->unack, skb);
+ mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
+ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+ return nskb;
+ } else {
+ skb_queue_head(&bcsp->rel, skb);
+ BT_ERR("Could not dequeue pkt because alloc_skb failed");
+ }
}
}
@@ -715,6 +720,9 @@ static int bcsp_open(struct hci_uart *hu)
static int bcsp_close(struct hci_uart *hu)
{
struct bcsp_struct *bcsp = hu->priv;
+
+ del_timer_sync(&bcsp->tbcsp);
+
hu->priv = NULL;
BT_DBG("hu %p", hu);
@@ -722,7 +730,6 @@ static int bcsp_close(struct hci_uart *hu)
skb_queue_purge(&bcsp->unack);
skb_queue_purge(&bcsp->rel);
skb_queue_purge(&bcsp->unrel);
- del_timer(&bcsp->tbcsp);
kfree(bcsp);
return 0;
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index f6f497450560..04680ead9275 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -206,12 +206,12 @@ static int h5_close(struct hci_uart *hu)
{
struct h5 *h5 = hu->priv;
+ del_timer_sync(&h5->timer);
+
skb_queue_purge(&h5->unack);
skb_queue_purge(&h5->rel);
skb_queue_purge(&h5->unrel);
- del_timer(&h5->timer);
-
kfree(h5);
return 0;
@@ -673,7 +673,8 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
return h5_prepare_pkt(hu, HCI_3WIRE_LINK_PKT, wakeup_req, 2);
}
- if ((skb = skb_dequeue(&h5->unrel)) != NULL) {
+ skb = skb_dequeue(&h5->unrel);
+ if (skb != NULL) {
nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
skb->data, skb->len);
if (nskb) {
@@ -690,7 +691,8 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
if (h5->unack.qlen >= h5->tx_win)
goto unlock;
- if ((skb = skb_dequeue(&h5->rel)) != NULL) {
+ skb = skb_dequeue(&h5->rel);
+ if (skb != NULL) {
nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
skb->data, skb->len);
if (nskb) {
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 6e06f6f69152..f1fbf4f1e5be 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -271,7 +271,8 @@ static int hci_uart_tty_open(struct tty_struct *tty)
if (tty->ops->write == NULL)
return -EOPNOTSUPP;
- if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
+ hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL);
+ if (!hu) {
BT_ERR("Can't allocate control structure");
return -ENFILE;
}
@@ -569,7 +570,8 @@ static int __init hci_uart_init(void)
hci_uart_ldisc.write_wakeup = hci_uart_tty_wakeup;
hci_uart_ldisc.owner = THIS_MODULE;
- if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) {
+ err = tty_register_ldisc(N_HCI, &hci_uart_ldisc);
+ if (err) {
BT_ERR("HCI line discipline registration failed. (%d)", err);
return err;
}
@@ -614,7 +616,8 @@ static void __exit hci_uart_exit(void)
#endif
/* Release tty registration of line discipline */
- if ((err = tty_unregister_ldisc(N_HCI)))
+ err = tty_unregister_ldisc(N_HCI);
+ if (err)
BT_ERR("Can't unregister HCI line discipline (%d)", err);
}
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index 1ef6990a5c7e..add1c6a72063 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -359,7 +359,7 @@ static const struct file_operations vhci_fops = {
static struct miscdevice vhci_miscdev= {
.name = "vhci",
.fops = &vhci_fops,
- .minor = MISC_DYNAMIC_MINOR,
+ .minor = VHCI_MINOR,
};
static int __init vhci_init(void)
@@ -385,3 +385,4 @@ MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("devname:vhci");
+MODULE_ALIAS_MISCDEV(VHCI_MINOR);
diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c
index 962fd35cbd8d..5a86da97a70b 100644
--- a/drivers/bus/arm-cci.c
+++ b/drivers/bus/arm-cci.c
@@ -31,7 +31,6 @@
#define DRIVER_NAME "CCI-400"
#define DRIVER_NAME_PMU DRIVER_NAME " PMU"
-#define PMU_NAME "CCI_400"
#define CCI_PORT_CTRL 0x0
#define CCI_CTRL_STATUS 0xc
@@ -88,8 +87,7 @@ static unsigned long cci_ctrl_phys;
#define CCI_REV_R0 0
#define CCI_REV_R1 1
-#define CCI_REV_R0_P4 4
-#define CCI_REV_R1_P2 6
+#define CCI_REV_R1_PX 5
#define CCI_PMU_EVT_SEL 0x000
#define CCI_PMU_CNTR 0x004
@@ -163,6 +161,15 @@ static struct pmu_port_event_ranges port_event_range[] = {
},
};
+/*
+ * Export different PMU names for the different revisions so userspace knows
+ * because the event ids are different
+ */
+static char *const pmu_names[] = {
+ [CCI_REV_R0] = "CCI_400",
+ [CCI_REV_R1] = "CCI_400_r1",
+};
+
struct cci_pmu_drv_data {
void __iomem *base;
struct arm_pmu *cci_pmu;
@@ -193,21 +200,16 @@ static int probe_cci_revision(void)
rev = readl_relaxed(cci_ctrl_base + CCI_PID2) & CCI_PID2_REV_MASK;
rev >>= CCI_PID2_REV_SHIFT;
- if (rev <= CCI_REV_R0_P4)
+ if (rev < CCI_REV_R1_PX)
return CCI_REV_R0;
- else if (rev <= CCI_REV_R1_P2)
+ else
return CCI_REV_R1;
-
- return -ENOENT;
}
static struct pmu_port_event_ranges *port_range_by_rev(void)
{
int rev = probe_cci_revision();
- if (rev < 0)
- return NULL;
-
return &port_event_range[rev];
}
@@ -526,7 +528,7 @@ static void pmu_write_counter(struct perf_event *event, u32 value)
static int cci_pmu_init(struct arm_pmu *cci_pmu, struct platform_device *pdev)
{
*cci_pmu = (struct arm_pmu){
- .name = PMU_NAME,
+ .name = pmu_names[probe_cci_revision()],
.max_period = (1LLU << 32) - 1,
.get_hw_events = pmu_get_hw_events,
.get_event_idx = pmu_get_event_idx,
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
index 3ef58c8dbf11..f8ee13c7bf7b 100644
--- a/drivers/bus/imx-weim.c
+++ b/drivers/bus/imx-weim.c
@@ -11,6 +11,9 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/regmap.h>
struct imx_weim_devtype {
unsigned int cs_count;
@@ -56,6 +59,55 @@ static const struct of_device_id weim_id_table[] = {
};
MODULE_DEVICE_TABLE(of, weim_id_table);
+static int __init imx_weim_gpr_setup(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct property *prop;
+ const __be32 *p;
+ struct regmap *gpr;
+ u32 gprvals[4] = {
+ 05, /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */
+ 033, /* CS0(64M) CS1(64M) CS2(0M) CS3(0M) */
+ 0113, /* CS0(64M) CS1(32M) CS2(32M) CS3(0M) */
+ 01111, /* CS0(32M) CS1(32M) CS2(32M) CS3(32M) */
+ };
+ u32 gprval = 0;
+ u32 val;
+ int cs = 0;
+ int i = 0;
+
+ gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
+ if (IS_ERR(gpr)) {
+ dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
+ return 0;
+ }
+
+ of_property_for_each_u32(np, "ranges", prop, p, val) {
+ if (i % 4 == 0) {
+ cs = val;
+ } else if (i % 4 == 3 && val) {
+ val = (val / SZ_32M) | 1;
+ gprval |= val << cs * 3;
+ }
+ i++;
+ }
+
+ if (i == 0 || i % 4)
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
+ if (gprval == gprvals[i]) {
+ /* Found it. Set up IOMUXC_GPR1[11:0] with it. */
+ regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval);
+ return 0;
+ }
+ }
+
+err:
+ dev_err(&pdev->dev, "Invalid 'ranges' configuration\n");
+ return -EINVAL;
+}
+
/* Parse and set the timing for this device. */
static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
const struct imx_weim_devtype *devtype)
@@ -92,6 +144,12 @@ static int __init weim_parse_dt(struct platform_device *pdev,
struct device_node *child;
int ret;
+ if (devtype == &imx50_weim_devtype) {
+ ret = imx_weim_gpr_setup(pdev);
+ if (ret)
+ return ret;
+ }
+
for_each_child_of_node(pdev->dev.of_node, child) {
if (!child->name)
continue;
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index 2ac754e18bcf..293e2e0a0a87 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -890,13 +890,12 @@ int __init mvebu_mbus_dt_init(void)
const __be32 *prop;
int ret;
- np = of_find_matching_node(NULL, of_mvebu_mbus_ids);
+ np = of_find_matching_node_and_match(NULL, of_mvebu_mbus_ids, &of_id);
if (!np) {
pr_err("could not find a matching SoC family\n");
return -ENODEV;
}
- of_id = of_match_node(of_mvebu_mbus_ids, np);
mbus_state.soc = of_id->data;
prop = of_get_property(np, "controller", NULL);
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 2f2b08457c67..244759bbd7b7 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -342,11 +342,11 @@ config HW_RANDOM_TPM
If unsure, say Y.
config HW_RANDOM_MSM
- tristate "Qualcomm MSM Random Number Generator support"
- depends on HW_RANDOM && ARCH_MSM
+ tristate "Qualcomm SoCs Random Number Generator support"
+ depends on HW_RANDOM && ARCH_QCOM
---help---
This driver provides kernel-side support for the Random Number
- Generator hardware found on Qualcomm MSM SoCs.
+ Generator hardware found on Qualcomm SoCs.
To compile this driver as a module, choose M here. the
module will be called msm-rng.
diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c
index bf9fc6b79328..851bc7e20ad2 100644
--- a/drivers/char/hw_random/atmel-rng.c
+++ b/drivers/char/hw_random/atmel-rng.c
@@ -54,29 +54,22 @@ static int atmel_trng_probe(struct platform_device *pdev)
struct resource *res;
int ret;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
-
trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
if (!trng)
return -ENOMEM;
- if (!devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), pdev->name))
- return -EBUSY;
-
- trng->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!trng->base)
- return -EBUSY;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ trng->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(trng->base))
+ return PTR_ERR(trng->base);
- trng->clk = clk_get(&pdev->dev, NULL);
+ trng->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(trng->clk))
return PTR_ERR(trng->clk);
ret = clk_enable(trng->clk);
if (ret)
- goto err_enable;
+ return ret;
writel(TRNG_KEY | 1, trng->base + TRNG_CR);
trng->rng.name = pdev->name;
@@ -92,9 +85,6 @@ static int atmel_trng_probe(struct platform_device *pdev)
err_register:
clk_disable(trng->clk);
-err_enable:
- clk_put(trng->clk);
-
return ret;
}
@@ -106,7 +96,6 @@ static int atmel_trng_remove(struct platform_device *pdev)
writel(TRNG_KEY, trng->base + TRNG_CR);
clk_disable(trng->clk);
- clk_put(trng->clk);
return 0;
}
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index b9495a8c05c6..334601cc81cf 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -40,6 +40,7 @@
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/random.h>
#include <asm/uaccess.h>
@@ -301,9 +302,10 @@ err_misc_dereg:
int hwrng_register(struct hwrng *rng)
{
- int must_register_misc;
int err = -EINVAL;
struct hwrng *old_rng, *tmp;
+ unsigned char bytes[16];
+ int bytes_read;
if (rng->name == NULL ||
(rng->data_read == NULL && rng->read == NULL))
@@ -326,7 +328,6 @@ int hwrng_register(struct hwrng *rng)
goto out_unlock;
}
- must_register_misc = (current_rng == NULL);
old_rng = current_rng;
if (!old_rng) {
err = hwrng_init(rng);
@@ -335,18 +336,20 @@ int hwrng_register(struct hwrng *rng)
current_rng = rng;
}
err = 0;
- if (must_register_misc) {
+ if (!old_rng) {
err = register_miscdev();
if (err) {
- if (!old_rng) {
- hwrng_cleanup(rng);
- current_rng = NULL;
- }
+ hwrng_cleanup(rng);
+ current_rng = NULL;
goto out_unlock;
}
}
INIT_LIST_HEAD(&rng->list);
list_add_tail(&rng->list, &rng_list);
+
+ bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1);
+ if (bytes_read > 0)
+ add_device_randomness(bytes, bytes_read);
out_unlock:
mutex_unlock(&rng_mutex);
out:
diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c
index 00e9d2d46634..9c8581577246 100644
--- a/drivers/char/hw_random/nomadik-rng.c
+++ b/drivers/char/hw_random/nomadik-rng.c
@@ -43,7 +43,7 @@ static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id)
void __iomem *base;
int ret;
- rng_clk = clk_get(&dev->dev, NULL);
+ rng_clk = devm_clk_get(&dev->dev, NULL);
if (IS_ERR(rng_clk)) {
dev_err(&dev->dev, "could not get rng clock\n");
ret = PTR_ERR(rng_clk);
@@ -56,33 +56,28 @@ static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id)
if (ret)
goto out_clk;
ret = -ENOMEM;
- base = ioremap(dev->res.start, resource_size(&dev->res));
+ base = devm_ioremap(&dev->dev, dev->res.start,
+ resource_size(&dev->res));
if (!base)
goto out_release;
nmk_rng.priv = (unsigned long)base;
ret = hwrng_register(&nmk_rng);
if (ret)
- goto out_unmap;
+ goto out_release;
return 0;
-out_unmap:
- iounmap(base);
out_release:
amba_release_regions(dev);
out_clk:
clk_disable(rng_clk);
- clk_put(rng_clk);
return ret;
}
static int nmk_rng_remove(struct amba_device *dev)
{
- void __iomem *base = (void __iomem *)nmk_rng.priv;
hwrng_unregister(&nmk_rng);
- iounmap(base);
amba_release_regions(dev);
clk_disable(rng_clk);
- clk_put(rng_clk);
return 0;
}
diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c
index c853e9e68573..6f2eaffed623 100644
--- a/drivers/char/hw_random/omap3-rom-rng.c
+++ b/drivers/char/hw_random/omap3-rom-rng.c
@@ -103,7 +103,7 @@ static int omap3_rom_rng_probe(struct platform_device *pdev)
}
setup_timer(&idle_timer, omap3_rom_rng_idle, 0);
- rng_clk = clk_get(&pdev->dev, "ick");
+ rng_clk = devm_clk_get(&pdev->dev, "ick");
if (IS_ERR(rng_clk)) {
pr_err("unable to get RNG clock\n");
return PTR_ERR(rng_clk);
@@ -120,7 +120,6 @@ static int omap3_rom_rng_remove(struct platform_device *pdev)
{
hwrng_unregister(&omap3_rom_rng_ops);
clk_disable_unprepare(rng_clk);
- clk_put(rng_clk);
return 0;
}
diff --git a/drivers/char/hw_random/picoxcell-rng.c b/drivers/char/hw_random/picoxcell-rng.c
index 3d4c2293c6f5..eab5448ad56f 100644
--- a/drivers/char/hw_random/picoxcell-rng.c
+++ b/drivers/char/hw_random/picoxcell-rng.c
@@ -104,24 +104,11 @@ static int picoxcell_trng_probe(struct platform_device *pdev)
int ret;
struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_warn(&pdev->dev, "no memory resource\n");
- return -ENOMEM;
- }
-
- if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
- "picoxcell_trng")) {
- dev_warn(&pdev->dev, "unable to request io mem\n");
- return -EBUSY;
- }
+ rng_base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(rng_base))
+ return PTR_ERR(rng_base);
- rng_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- if (!rng_base) {
- dev_warn(&pdev->dev, "unable to remap io mem\n");
- return -ENOMEM;
- }
-
- rng_clk = clk_get(&pdev->dev, NULL);
+ rng_clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(rng_clk)) {
dev_warn(&pdev->dev, "no clk\n");
return PTR_ERR(rng_clk);
@@ -130,7 +117,7 @@ static int picoxcell_trng_probe(struct platform_device *pdev)
ret = clk_enable(rng_clk);
if (ret) {
dev_warn(&pdev->dev, "unable to enable clk\n");
- goto err_enable;
+ return ret;
}
picoxcell_trng_start();
@@ -145,9 +132,6 @@ static int picoxcell_trng_probe(struct platform_device *pdev)
err_register:
clk_disable(rng_clk);
-err_enable:
- clk_put(rng_clk);
-
return ret;
}
@@ -155,7 +139,6 @@ static int picoxcell_trng_remove(struct platform_device *pdev)
{
hwrng_unregister(&picoxcell_trng);
clk_disable(rng_clk);
- clk_put(rng_clk);
return 0;
}
diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c
index 73ce739f8e19..439ff8b28c43 100644
--- a/drivers/char/hw_random/timeriomem-rng.c
+++ b/drivers/char/hw_random/timeriomem-rng.c
@@ -118,7 +118,8 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
}
/* Allocate memory for the device structure (and zero it) */
- priv = kzalloc(sizeof(struct timeriomem_rng_private_data), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct timeriomem_rng_private_data), GFP_KERNEL);
if (!priv) {
dev_err(&pdev->dev, "failed to allocate device structure.\n");
return -ENOMEM;
@@ -134,17 +135,16 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
period = i;
else {
dev_err(&pdev->dev, "missing period\n");
- err = -EINVAL;
- goto out_free;
+ return -EINVAL;
}
- } else
+ } else {
period = pdata->period;
+ }
priv->period = usecs_to_jiffies(period);
if (priv->period < 1) {
dev_err(&pdev->dev, "period is less than one jiffy\n");
- err = -EINVAL;
- goto out_free;
+ return -EINVAL;
}
priv->expires = jiffies;
@@ -160,24 +160,16 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
priv->timeriomem_rng_ops.data_read = timeriomem_rng_data_read;
priv->timeriomem_rng_ops.priv = (unsigned long)priv;
- if (!request_mem_region(res->start, resource_size(res),
- dev_name(&pdev->dev))) {
- dev_err(&pdev->dev, "request_mem_region failed\n");
- err = -EBUSY;
+ priv->io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->io_base)) {
+ err = PTR_ERR(priv->io_base);
goto out_timer;
}
- priv->io_base = ioremap(res->start, resource_size(res));
- if (priv->io_base == NULL) {
- dev_err(&pdev->dev, "ioremap failed\n");
- err = -EIO;
- goto out_release_io;
- }
-
err = hwrng_register(&priv->timeriomem_rng_ops);
if (err) {
dev_err(&pdev->dev, "problem registering\n");
- goto out;
+ goto out_timer;
}
dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",
@@ -185,30 +177,18 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
return 0;
-out:
- iounmap(priv->io_base);
-out_release_io:
- release_mem_region(res->start, resource_size(res));
out_timer:
del_timer_sync(&priv->timer);
-out_free:
- kfree(priv);
return err;
}
static int timeriomem_rng_remove(struct platform_device *pdev)
{
struct timeriomem_rng_private_data *priv = platform_get_drvdata(pdev);
- struct resource *res;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hwrng_unregister(&priv->timeriomem_rng_ops);
del_timer_sync(&priv->timer);
- iounmap(priv->io_base);
- release_mem_region(res->start, resource_size(res));
- kfree(priv);
return 0;
}
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index c12398d1517c..2ce0e225e58c 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -47,8 +47,7 @@ static void register_buffer(u8 *buf, size_t size)
sg_init_one(&sg, buf, size);
/* There should always be room for one buffer. */
- if (virtqueue_add_inbuf(vq, &sg, 1, buf, GFP_KERNEL) < 0)
- BUG();
+ virtqueue_add_inbuf(vq, &sg, 1, buf, GFP_KERNEL);
virtqueue_kick(vq);
}
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 429b75bb60e8..6b75713d953a 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -295,17 +295,17 @@
* The minimum number of bits of entropy before we wake up a read on
* /dev/random. Should be enough to do a significant reseed.
*/
-static int random_read_wakeup_thresh = 64;
+static int random_read_wakeup_bits = 64;
/*
* If the entropy count falls under this number of bits, then we
* should wake up processes which are selecting or polling on write
* access to /dev/random.
*/
-static int random_write_wakeup_thresh = 28 * OUTPUT_POOL_WORDS;
+static int random_write_wakeup_bits = 28 * OUTPUT_POOL_WORDS;
/*
- * The minimum number of seconds between urandom pool resending. We
+ * The minimum number of seconds between urandom pool reseeding. We
* do this to limit the amount of entropy that can be drained from the
* input pool even if there are heavy demands on /dev/urandom.
*/
@@ -322,7 +322,7 @@ static int random_min_urandom_seed = 60;
* Register. (See M. Matsumoto & Y. Kurita, 1992. Twisted GFSR
* generators. ACM Transactions on Modeling and Computer Simulation
* 2(3):179-194. Also see M. Matsumoto & Y. Kurita, 1994. Twisted
- * GFSR generators II. ACM Transactions on Mdeling and Computer
+ * GFSR generators II. ACM Transactions on Modeling and Computer
* Simulation 4:254-266)
*
* Thanks to Colin Plumb for suggesting this.
@@ -666,10 +666,10 @@ retry:
r->entropy_total, _RET_IP_);
if (r == &input_pool) {
- int entropy_bytes = entropy_count >> ENTROPY_SHIFT;
+ int entropy_bits = entropy_count >> ENTROPY_SHIFT;
/* should we wake readers? */
- if (entropy_bytes >= random_read_wakeup_thresh) {
+ if (entropy_bits >= random_read_wakeup_bits) {
wake_up_interruptible(&random_read_wait);
kill_fasync(&fasync, SIGIO, POLL_IN);
}
@@ -678,9 +678,9 @@ retry:
* forth between them, until the output pools are 75%
* full.
*/
- if (entropy_bytes > random_write_wakeup_thresh &&
+ if (entropy_bits > random_write_wakeup_bits &&
r->initialized &&
- r->entropy_total >= 2*random_read_wakeup_thresh) {
+ r->entropy_total >= 2*random_read_wakeup_bits) {
static struct entropy_store *last = &blocking_pool;
struct entropy_store *other = &blocking_pool;
@@ -844,6 +844,8 @@ void add_interrupt_randomness(int irq, int irq_flags)
cycles_t cycles = random_get_entropy();
__u32 input[4], c_high, j_high;
__u64 ip;
+ unsigned long seed;
+ int credit;
c_high = (sizeof(cycles) > 4) ? cycles >> 32 : 0;
j_high = (sizeof(now) > 4) ? now >> 32 : 0;
@@ -862,20 +864,33 @@ void add_interrupt_randomness(int irq, int irq_flags)
r = nonblocking_pool.initialized ? &input_pool : &nonblocking_pool;
__mix_pool_bytes(r, &fast_pool->pool, sizeof(fast_pool->pool), NULL);
+
/*
* If we don't have a valid cycle counter, and we see
* back-to-back timer interrupts, then skip giving credit for
- * any entropy.
+ * any entropy, otherwise credit 1 bit.
*/
+ credit = 1;
if (cycles == 0) {
if (irq_flags & __IRQF_TIMER) {
if (fast_pool->last_timer_intr)
- return;
+ credit = 0;
fast_pool->last_timer_intr = 1;
} else
fast_pool->last_timer_intr = 0;
}
- credit_entropy_bits(r, 1);
+
+ /*
+ * If we have architectural seed generator, produce a seed and
+ * add it to the pool. For the sake of paranoia count it as
+ * 50% entropic.
+ */
+ if (arch_get_random_seed_long(&seed)) {
+ __mix_pool_bytes(r, &seed, sizeof(seed), NULL);
+ credit += sizeof(seed) * 4;
+ }
+
+ credit_entropy_bits(r, credit);
}
#ifdef CONFIG_BLOCK
@@ -924,19 +939,19 @@ static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
{
__u32 tmp[OUTPUT_POOL_WORDS];
- /* For /dev/random's pool, always leave two wakeup worth's BITS */
- int rsvd = r->limit ? 0 : random_read_wakeup_thresh/4;
+ /* For /dev/random's pool, always leave two wakeups' worth */
+ int rsvd_bytes = r->limit ? 0 : random_read_wakeup_bits / 4;
int bytes = nbytes;
- /* pull at least as many as BYTES as wakeup BITS */
- bytes = max_t(int, bytes, random_read_wakeup_thresh / 8);
+ /* pull at least as much as a wakeup */
+ bytes = max_t(int, bytes, random_read_wakeup_bits / 8);
/* but never more than the buffer size */
bytes = min_t(int, bytes, sizeof(tmp));
trace_xfer_secondary_pool(r->name, bytes * 8, nbytes * 8,
ENTROPY_BITS(r), ENTROPY_BITS(r->pull));
bytes = extract_entropy(r->pull, tmp, bytes,
- random_read_wakeup_thresh / 8, rsvd);
+ random_read_wakeup_bits / 8, rsvd_bytes);
mix_pool_bytes(r, tmp, bytes, NULL);
credit_entropy_bits(r, bytes*8);
}
@@ -952,35 +967,22 @@ static void push_to_pool(struct work_struct *work)
struct entropy_store *r = container_of(work, struct entropy_store,
push_work);
BUG_ON(!r);
- _xfer_secondary_pool(r, random_read_wakeup_thresh/8);
+ _xfer_secondary_pool(r, random_read_wakeup_bits/8);
trace_push_to_pool(r->name, r->entropy_count >> ENTROPY_SHIFT,
r->pull->entropy_count >> ENTROPY_SHIFT);
}
/*
- * These functions extracts randomness from the "entropy pool", and
- * returns it in a buffer.
- *
- * The min parameter specifies the minimum amount we can pull before
- * failing to avoid races that defeat catastrophic reseeding while the
- * reserved parameter indicates how much entropy we must leave in the
- * pool after each pull to avoid starving other readers.
- *
- * Note: extract_entropy() assumes that .poolwords is a multiple of 16 words.
+ * This function decides how many bytes to actually take from the
+ * given pool, and also debits the entropy count accordingly.
*/
-
static size_t account(struct entropy_store *r, size_t nbytes, int min,
int reserved)
{
- unsigned long flags;
- int wakeup_write = 0;
int have_bytes;
int entropy_count, orig;
size_t ibytes;
- /* Hold lock while accounting */
- spin_lock_irqsave(&r->lock, flags);
-
BUG_ON(r->entropy_count > r->poolinfo->poolfracbits);
/* Can we pull enough? */
@@ -988,29 +990,19 @@ retry:
entropy_count = orig = ACCESS_ONCE(r->entropy_count);
have_bytes = entropy_count >> (ENTROPY_SHIFT + 3);
ibytes = nbytes;
- if (have_bytes < min + reserved) {
+ /* If limited, never pull more than available */
+ if (r->limit)
+ ibytes = min_t(size_t, ibytes, have_bytes - reserved);
+ if (ibytes < min)
ibytes = 0;
- } else {
- /* If limited, never pull more than available */
- if (r->limit && ibytes + reserved >= have_bytes)
- ibytes = have_bytes - reserved;
-
- if (have_bytes >= ibytes + reserved)
- entropy_count -= ibytes << (ENTROPY_SHIFT + 3);
- else
- entropy_count = reserved << (ENTROPY_SHIFT + 3);
-
- if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
- goto retry;
-
- if ((r->entropy_count >> ENTROPY_SHIFT)
- < random_write_wakeup_thresh)
- wakeup_write = 1;
- }
- spin_unlock_irqrestore(&r->lock, flags);
+ entropy_count = max_t(int, 0,
+ entropy_count - (ibytes << (ENTROPY_SHIFT + 3)));
+ if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
+ goto retry;
trace_debit_entropy(r->name, 8 * ibytes);
- if (wakeup_write) {
+ if (ibytes &&
+ (r->entropy_count >> ENTROPY_SHIFT) < random_write_wakeup_bits) {
wake_up_interruptible(&random_write_wait);
kill_fasync(&fasync, SIGIO, POLL_OUT);
}
@@ -1018,6 +1010,12 @@ retry:
return ibytes;
}
+/*
+ * This function does the actual extraction for extract_entropy and
+ * extract_entropy_user.
+ *
+ * Note: we assume that .poolwords is a multiple of 16 words.
+ */
static void extract_buf(struct entropy_store *r, __u8 *out)
{
int i;
@@ -1029,23 +1027,23 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
__u8 extract[64];
unsigned long flags;
- /* Generate a hash across the pool, 16 words (512 bits) at a time */
- sha_init(hash.w);
- spin_lock_irqsave(&r->lock, flags);
- for (i = 0; i < r->poolinfo->poolwords; i += 16)
- sha_transform(hash.w, (__u8 *)(r->pool + i), workspace);
-
/*
- * If we have a architectural hardware random number
- * generator, mix that in, too.
+ * If we have an architectural hardware random number
+ * generator, use it for SHA's initial vector
*/
+ sha_init(hash.w);
for (i = 0; i < LONGS(20); i++) {
unsigned long v;
if (!arch_get_random_long(&v))
break;
- hash.l[i] ^= v;
+ hash.l[i] = v;
}
+ /* Generate a hash across the pool, 16 words (512 bits) at a time */
+ spin_lock_irqsave(&r->lock, flags);
+ for (i = 0; i < r->poolinfo->poolwords; i += 16)
+ sha_transform(hash.w, (__u8 *)(r->pool + i), workspace);
+
/*
* We mix the hash back into the pool to prevent backtracking
* attacks (where the attacker knows the state of the pool
@@ -1079,6 +1077,15 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
memset(&hash, 0, sizeof(hash));
}
+/*
+ * This function extracts randomness from the "entropy pool", and
+ * returns it in a buffer.
+ *
+ * The min parameter specifies the minimum amount we can pull before
+ * failing to avoid races that defeat catastrophic reseeding while the
+ * reserved parameter indicates how much entropy we must leave in the
+ * pool after each pull to avoid starving other readers.
+ */
static ssize_t extract_entropy(struct entropy_store *r, void *buf,
size_t nbytes, int min, int reserved)
{
@@ -1129,6 +1136,10 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
return ret;
}
+/*
+ * This function extracts randomness from the "entropy pool", and
+ * returns it in a userspace buffer.
+ */
static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
size_t nbytes)
{
@@ -1170,8 +1181,9 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
/*
* This function is the exported kernel interface. It returns some
* number of good random numbers, suitable for key generation, seeding
- * TCP sequence numbers, etc. It does not use the hw random number
- * generator, if available; use get_random_bytes_arch() for that.
+ * TCP sequence numbers, etc. It does not rely on the hardware random
+ * number generator. For random bytes direct from the hardware RNG
+ * (when available), use get_random_bytes_arch().
*/
void get_random_bytes(void *buf, int nbytes)
{
@@ -1238,7 +1250,8 @@ static void init_std_data(struct entropy_store *r)
r->last_pulled = jiffies;
mix_pool_bytes(r, &now, sizeof(now), NULL);
for (i = r->poolinfo->poolbytes; i > 0; i -= sizeof(rv)) {
- if (!arch_get_random_long(&rv))
+ if (!arch_get_random_seed_long(&rv) &&
+ !arch_get_random_long(&rv))
rv = random_get_entropy();
mix_pool_bytes(r, &rv, sizeof(rv), NULL);
}
@@ -1281,56 +1294,71 @@ void rand_initialize_disk(struct gendisk *disk)
}
#endif
-static ssize_t
-random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+/*
+ * Attempt an emergency refill using arch_get_random_seed_long().
+ *
+ * As with add_interrupt_randomness() be paranoid and only
+ * credit the output as 50% entropic.
+ */
+static int arch_random_refill(void)
{
- ssize_t n, retval = 0, count = 0;
+ const unsigned int nlongs = 64; /* Arbitrary number */
+ unsigned int n = 0;
+ unsigned int i;
+ unsigned long buf[nlongs];
- if (nbytes == 0)
+ if (!arch_has_random_seed())
return 0;
- while (nbytes > 0) {
- n = nbytes;
- if (n > SEC_XFER_SIZE)
- n = SEC_XFER_SIZE;
+ for (i = 0; i < nlongs; i++) {
+ if (arch_get_random_seed_long(&buf[n]))
+ n++;
+ }
- n = extract_entropy_user(&blocking_pool, buf, n);
+ if (n) {
+ unsigned int rand_bytes = n * sizeof(unsigned long);
- if (n < 0) {
- retval = n;
- break;
- }
+ mix_pool_bytes(&input_pool, buf, rand_bytes, NULL);
+ credit_entropy_bits(&input_pool, rand_bytes*4);
+ }
+ return n;
+}
+
+static ssize_t
+random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+{
+ ssize_t n;
+
+ if (nbytes == 0)
+ return 0;
+
+ nbytes = min_t(size_t, nbytes, SEC_XFER_SIZE);
+ while (1) {
+ n = extract_entropy_user(&blocking_pool, buf, nbytes);
+ if (n < 0)
+ return n;
trace_random_read(n*8, (nbytes-n)*8,
ENTROPY_BITS(&blocking_pool),
ENTROPY_BITS(&input_pool));
+ if (n > 0)
+ return n;
- if (n == 0) {
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- break;
- }
-
- wait_event_interruptible(random_read_wait,
- ENTROPY_BITS(&input_pool) >=
- random_read_wakeup_thresh);
-
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
+ /* Pool is (near) empty. Maybe wait and retry. */
+ /* First try an emergency refill */
+ if (arch_random_refill())
continue;
- }
- count += n;
- buf += n;
- nbytes -= n;
- break; /* This break makes the device work */
- /* like a named pipe */
- }
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
- return (count ? count : retval);
+ wait_event_interruptible(random_read_wait,
+ ENTROPY_BITS(&input_pool) >=
+ random_read_wakeup_bits);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ }
}
static ssize_t
@@ -1358,9 +1386,9 @@ random_poll(struct file *file, poll_table * wait)
poll_wait(file, &random_read_wait, wait);
poll_wait(file, &random_write_wait, wait);
mask = 0;
- if (ENTROPY_BITS(&input_pool) >= random_read_wakeup_thresh)
+ if (ENTROPY_BITS(&input_pool) >= random_read_wakeup_bits)
mask |= POLLIN | POLLRDNORM;
- if (ENTROPY_BITS(&input_pool) < random_write_wakeup_thresh)
+ if (ENTROPY_BITS(&input_pool) < random_write_wakeup_bits)
mask |= POLLOUT | POLLWRNORM;
return mask;
}
@@ -1507,18 +1535,18 @@ EXPORT_SYMBOL(generate_random_uuid);
#include <linux/sysctl.h>
static int min_read_thresh = 8, min_write_thresh;
-static int max_read_thresh = INPUT_POOL_WORDS * 32;
+static int max_read_thresh = OUTPUT_POOL_WORDS * 32;
static int max_write_thresh = INPUT_POOL_WORDS * 32;
static char sysctl_bootid[16];
/*
- * These functions is used to return both the bootid UUID, and random
+ * This function is used to return both the bootid UUID, and random
* UUID. The difference is in whether table->data is NULL; if it is,
* then a new UUID is generated and returned to the user.
*
- * If the user accesses this via the proc interface, it will be returned
- * as an ASCII string in the standard UUID format. If accesses via the
- * sysctl system call, it is returned as 16 bytes of binary data.
+ * If the user accesses this via the proc interface, the UUID will be
+ * returned as an ASCII string in the standard UUID format; if via the
+ * sysctl system call, as 16 bytes of binary data.
*/
static int proc_do_uuid(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
@@ -1583,7 +1611,7 @@ struct ctl_table random_table[] = {
},
{
.procname = "read_wakeup_threshold",
- .data = &random_read_wakeup_thresh,
+ .data = &random_read_wakeup_bits,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
@@ -1592,7 +1620,7 @@ struct ctl_table random_table[] = {
},
{
.procname = "write_wakeup_threshold",
- .data = &random_write_wakeup_thresh,
+ .data = &random_write_wakeup_bits,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 7641965d208d..6f56d3a4f010 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -65,10 +65,12 @@ config COMMON_CLK_SI570
clock generators.
config COMMON_CLK_S2MPS11
- tristate "Clock driver for S2MPS11 MFD"
+ tristate "Clock driver for S2MPS11/S5M8767 MFD"
depends on MFD_SEC_CORE
---help---
- This driver supports S2MPS11 crystal oscillator clock.
+ This driver supports S2MPS11/S5M8767 crystal oscillator clock. These
+ multi-function devices have 3 fixed-rate oscillators, clocked at
+ 32KHz each.
config CLK_TWL6040
tristate "External McPDM functional clock from twl6040"
@@ -111,4 +113,5 @@ source "drivers/clk/qcom/Kconfig"
endmenu
+source "drivers/clk/bcm/Kconfig"
source "drivers/clk/mvebu/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index a367a9831717..5f8a28735c96 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
+obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o
@@ -29,7 +30,9 @@ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
+obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/
obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/
+obj-$(CONFIG_ARCH_HIP04) += hisilicon/
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
@@ -43,6 +46,7 @@ obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += shmobile/
obj-$(CONFIG_ARCH_SIRF) += sirf/
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
obj-$(CONFIG_PLAT_SPEAR) += spear/
+obj-$(CONFIG_ARCH_STI) += st/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_ARCH_OMAP2PLUS) += ti/
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index fd792b203eaf..62e2509f9df1 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -13,12 +13,9 @@
#include <linux/clk/at91_pmc.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_irq.h>
#include <linux/io.h>
#include <linux/wait.h>
#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
#include "pmc.h"
@@ -38,104 +35,59 @@ struct clk_programmable_layout {
struct clk_programmable {
struct clk_hw hw;
struct at91_pmc *pmc;
- unsigned int irq;
- wait_queue_head_t wait;
u8 id;
- u8 css;
- u8 pres;
- u8 slckmck;
const struct clk_programmable_layout *layout;
};
#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw)
-
-static irqreturn_t clk_programmable_irq_handler(int irq, void *dev_id)
-{
- struct clk_programmable *prog = (struct clk_programmable *)dev_id;
-
- wake_up(&prog->wait);
-
- return IRQ_HANDLED;
-}
-
-static int clk_programmable_prepare(struct clk_hw *hw)
+static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
{
- u32 tmp;
+ u32 pres;
struct clk_programmable *prog = to_clk_programmable(hw);
struct at91_pmc *pmc = prog->pmc;
const struct clk_programmable_layout *layout = prog->layout;
- u8 id = prog->id;
- u32 mask = PROG_STATUS_MASK(id);
-
- tmp = prog->css | (prog->pres << layout->pres_shift);
- if (layout->have_slck_mck && prog->slckmck)
- tmp |= AT91_PMC_CSSMCK_MCK;
-
- pmc_write(pmc, AT91_PMC_PCKR(id), tmp);
-
- while (!(pmc_read(pmc, AT91_PMC_SR) & mask))
- wait_event(prog->wait, pmc_read(pmc, AT91_PMC_SR) & mask);
- return 0;
+ pres = (pmc_read(pmc, AT91_PMC_PCKR(prog->id)) >> layout->pres_shift) &
+ PROG_PRES_MASK;
+ return parent_rate >> pres;
}
-static int clk_programmable_is_ready(struct clk_hw *hw)
+static long clk_programmable_determine_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *best_parent_rate,
+ struct clk **best_parent_clk)
{
- struct clk_programmable *prog = to_clk_programmable(hw);
- struct at91_pmc *pmc = prog->pmc;
-
- return !!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_PCKR(prog->id));
-}
+ struct clk *parent = NULL;
+ long best_rate = -EINVAL;
+ unsigned long parent_rate;
+ unsigned long tmp_rate;
+ int shift;
+ int i;
-static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- u32 tmp;
- struct clk_programmable *prog = to_clk_programmable(hw);
- struct at91_pmc *pmc = prog->pmc;
- const struct clk_programmable_layout *layout = prog->layout;
+ for (i = 0; i < __clk_get_num_parents(hw->clk); i++) {
+ parent = clk_get_parent_by_index(hw->clk, i);
+ if (!parent)
+ continue;
- tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id));
- prog->pres = (tmp >> layout->pres_shift) & PROG_PRES_MASK;
+ parent_rate = __clk_get_rate(parent);
+ for (shift = 0; shift < PROG_PRES_MASK; shift++) {
+ tmp_rate = parent_rate >> shift;
+ if (tmp_rate <= rate)
+ break;
+ }
- return parent_rate >> prog->pres;
-}
+ if (tmp_rate > rate)
+ continue;
-static long clk_programmable_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
-{
- unsigned long best_rate = *parent_rate;
- unsigned long best_diff;
- unsigned long new_diff;
- unsigned long cur_rate;
- int shift = shift;
-
- if (rate > *parent_rate)
- return *parent_rate;
- else
- best_diff = *parent_rate - rate;
-
- if (!best_diff)
- return best_rate;
-
- for (shift = 1; shift < PROG_PRES_MASK; shift++) {
- cur_rate = *parent_rate >> shift;
-
- if (cur_rate > rate)
- new_diff = cur_rate - rate;
- else
- new_diff = rate - cur_rate;
-
- if (!new_diff)
- return cur_rate;
-
- if (new_diff < best_diff) {
- best_diff = new_diff;
- best_rate = cur_rate;
+ if (best_rate < 0 || (rate - tmp_rate) < (rate - best_rate)) {
+ best_rate = tmp_rate;
+ *best_parent_rate = parent_rate;
+ *best_parent_clk = parent;
}
- if (rate > cur_rate)
+ if (!best_rate)
break;
}
@@ -146,17 +98,22 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
+ struct at91_pmc *pmc = prog->pmc;
+ u32 tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)) & ~layout->css_mask;
+
+ if (layout->have_slck_mck)
+ tmp &= AT91_PMC_CSSMCK_MCK;
+
if (index > layout->css_mask) {
if (index > PROG_MAX_RM9200_CSS && layout->have_slck_mck) {
- prog->css = 0;
- prog->slckmck = 1;
+ tmp |= AT91_PMC_CSSMCK_MCK;
return 0;
} else {
return -EINVAL;
}
}
- prog->css = index;
+ pmc_write(pmc, AT91_PMC_PCKR(prog->id), tmp | index);
return 0;
}
@@ -169,13 +126,9 @@ static u8 clk_programmable_get_parent(struct clk_hw *hw)
const struct clk_programmable_layout *layout = prog->layout;
tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id));
- prog->css = tmp & layout->css_mask;
- ret = prog->css;
- if (layout->have_slck_mck) {
- prog->slckmck = !!(tmp & AT91_PMC_CSSMCK_MCK);
- if (prog->slckmck && !ret)
- ret = PROG_MAX_RM9200_CSS + 1;
- }
+ ret = tmp & layout->css_mask;
+ if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !ret)
+ ret = PROG_MAX_RM9200_CSS + 1;
return ret;
}
@@ -184,67 +137,47 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_programmable *prog = to_clk_programmable(hw);
- unsigned long best_rate = parent_rate;
- unsigned long best_diff;
- unsigned long new_diff;
- unsigned long cur_rate;
+ struct at91_pmc *pmc = prog->pmc;
+ const struct clk_programmable_layout *layout = prog->layout;
+ unsigned long div = parent_rate / rate;
int shift = 0;
+ u32 tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)) &
+ ~(PROG_PRES_MASK << layout->pres_shift);
- if (rate > parent_rate)
- return parent_rate;
- else
- best_diff = parent_rate - rate;
-
- if (!best_diff) {
- prog->pres = shift;
- return 0;
- }
+ if (!div)
+ return -EINVAL;
- for (shift = 1; shift < PROG_PRES_MASK; shift++) {
- cur_rate = parent_rate >> shift;
+ shift = fls(div) - 1;
- if (cur_rate > rate)
- new_diff = cur_rate - rate;
- else
- new_diff = rate - cur_rate;
+ if (div != (1<<shift))
+ return -EINVAL;
- if (!new_diff)
- break;
+ if (shift >= PROG_PRES_MASK)
+ return -EINVAL;
- if (new_diff < best_diff) {
- best_diff = new_diff;
- best_rate = cur_rate;
- }
+ pmc_write(pmc, AT91_PMC_PCKR(prog->id),
+ tmp | (shift << layout->pres_shift));
- if (rate > cur_rate)
- break;
- }
-
- prog->pres = shift;
return 0;
}
static const struct clk_ops programmable_ops = {
- .prepare = clk_programmable_prepare,
- .is_prepared = clk_programmable_is_ready,
.recalc_rate = clk_programmable_recalc_rate,
- .round_rate = clk_programmable_round_rate,
+ .determine_rate = clk_programmable_determine_rate,
.get_parent = clk_programmable_get_parent,
.set_parent = clk_programmable_set_parent,
.set_rate = clk_programmable_set_rate,
};
static struct clk * __init
-at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq,
+at91_clk_register_programmable(struct at91_pmc *pmc,
const char *name, const char **parent_names,
u8 num_parents, u8 id,
const struct clk_programmable_layout *layout)
{
- int ret;
struct clk_programmable *prog;
struct clk *clk = NULL;
struct clk_init_data init;
- char irq_name[11];
if (id > PROG_ID_MAX)
return ERR_PTR(-EINVAL);
@@ -263,14 +196,6 @@ at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq,
prog->layout = layout;
prog->hw.init = &init;
prog->pmc = pmc;
- prog->irq = irq;
- init_waitqueue_head(&prog->wait);
- irq_set_status_flags(prog->irq, IRQ_NOAUTOEN);
- snprintf(irq_name, sizeof(irq_name), "clk-prog%d", id);
- ret = request_irq(prog->irq, clk_programmable_irq_handler,
- IRQF_TRIGGER_HIGH, irq_name, prog);
- if (ret)
- return ERR_PTR(ret);
clk = clk_register(NULL, &prog->hw);
if (IS_ERR(clk))
@@ -304,7 +229,6 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc,
int num;
u32 id;
int i;
- unsigned int irq;
struct clk *clk;
int num_parents;
const char *parent_names[PROG_SOURCE_MAX];
@@ -332,11 +256,7 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc,
if (of_property_read_string(np, "clock-output-names", &name))
name = progclknp->name;
- irq = irq_of_parse_and_map(progclknp, 0);
- if (!irq)
- continue;
-
- clk = at91_clk_register_programmable(pmc, irq, name,
+ clk = at91_clk_register_programmable(pmc, name,
parent_names, num_parents,
id, layout);
if (IS_ERR(clk))
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
index 8f7c0434a09f..8c96307d7363 100644
--- a/drivers/clk/at91/clk-system.c
+++ b/drivers/clk/at91/clk-system.c
@@ -14,6 +14,11 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
#include "pmc.h"
@@ -25,19 +30,48 @@
struct clk_system {
struct clk_hw hw;
struct at91_pmc *pmc;
+ unsigned int irq;
+ wait_queue_head_t wait;
u8 id;
};
-static int clk_system_enable(struct clk_hw *hw)
+static inline int is_pck(int id)
+{
+ return (id >= 8) && (id <= 15);
+}
+static irqreturn_t clk_system_irq_handler(int irq, void *dev_id)
+{
+ struct clk_system *sys = (struct clk_system *)dev_id;
+
+ wake_up(&sys->wait);
+ disable_irq_nosync(sys->irq);
+
+ return IRQ_HANDLED;
+}
+
+static int clk_system_prepare(struct clk_hw *hw)
{
struct clk_system *sys = to_clk_system(hw);
struct at91_pmc *pmc = sys->pmc;
+ u32 mask = 1 << sys->id;
- pmc_write(pmc, AT91_PMC_SCER, 1 << sys->id);
+ pmc_write(pmc, AT91_PMC_SCER, mask);
+
+ if (!is_pck(sys->id))
+ return 0;
+
+ while (!(pmc_read(pmc, AT91_PMC_SR) & mask)) {
+ if (sys->irq) {
+ enable_irq(sys->irq);
+ wait_event(sys->wait,
+ pmc_read(pmc, AT91_PMC_SR) & mask);
+ } else
+ cpu_relax();
+ }
return 0;
}
-static void clk_system_disable(struct clk_hw *hw)
+static void clk_system_unprepare(struct clk_hw *hw)
{
struct clk_system *sys = to_clk_system(hw);
struct at91_pmc *pmc = sys->pmc;
@@ -45,27 +79,34 @@ static void clk_system_disable(struct clk_hw *hw)
pmc_write(pmc, AT91_PMC_SCDR, 1 << sys->id);
}
-static int clk_system_is_enabled(struct clk_hw *hw)
+static int clk_system_is_prepared(struct clk_hw *hw)
{
struct clk_system *sys = to_clk_system(hw);
struct at91_pmc *pmc = sys->pmc;
- return !!(pmc_read(pmc, AT91_PMC_SCSR) & (1 << sys->id));
+ if (!(pmc_read(pmc, AT91_PMC_SCSR) & (1 << sys->id)))
+ return 0;
+
+ if (!is_pck(sys->id))
+ return 1;
+
+ return !!(pmc_read(pmc, AT91_PMC_SR) & (1 << sys->id));
}
static const struct clk_ops system_ops = {
- .enable = clk_system_enable,
- .disable = clk_system_disable,
- .is_enabled = clk_system_is_enabled,
+ .prepare = clk_system_prepare,
+ .unprepare = clk_system_unprepare,
+ .is_prepared = clk_system_is_prepared,
};
static struct clk * __init
at91_clk_register_system(struct at91_pmc *pmc, const char *name,
- const char *parent_name, u8 id)
+ const char *parent_name, u8 id, int irq)
{
struct clk_system *sys;
struct clk *clk = NULL;
struct clk_init_data init;
+ int ret;
if (!parent_name || id > SYSTEM_MAX_ID)
return ERR_PTR(-EINVAL);
@@ -84,11 +125,20 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name,
* (see drivers/memory) which would request and enable the ddrck clock.
* When this is done we will be able to remove CLK_IGNORE_UNUSED flag.
*/
- init.flags = CLK_IGNORE_UNUSED;
+ init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED;
sys->id = id;
sys->hw.init = &init;
sys->pmc = pmc;
+ sys->irq = irq;
+ if (irq) {
+ init_waitqueue_head(&sys->wait);
+ irq_set_status_flags(sys->irq, IRQ_NOAUTOEN);
+ ret = request_irq(sys->irq, clk_system_irq_handler,
+ IRQF_TRIGGER_HIGH, name, sys);
+ if (ret)
+ return ERR_PTR(ret);
+ }
clk = clk_register(NULL, &sys->hw);
if (IS_ERR(clk))
@@ -101,6 +151,7 @@ static void __init
of_at91_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc)
{
int num;
+ int irq = 0;
u32 id;
struct clk *clk;
const char *name;
@@ -118,9 +169,12 @@ of_at91_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc)
if (of_property_read_string(np, "clock-output-names", &name))
name = sysclknp->name;
+ if (is_pck(id))
+ irq = irq_of_parse_and_map(sysclknp, 0);
+
parent_name = of_clk_get_parent_name(sysclknp, 0);
- clk = at91_clk_register_system(pmc, name, parent_name, id);
+ clk = at91_clk_register_system(pmc, name, parent_name, id, irq);
if (IS_ERR(clk))
continue;
diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig
new file mode 100644
index 000000000000..a7262fb8ce55
--- /dev/null
+++ b/drivers/clk/bcm/Kconfig
@@ -0,0 +1,9 @@
+config CLK_BCM_KONA
+ bool "Broadcom Kona CCU clock support"
+ depends on ARCH_BCM_MOBILE
+ depends on COMMON_CLK
+ default y
+ help
+ Enable common clock framework support for Broadcom SoCs
+ using "Kona" style clock control units, including those
+ in the BCM281xx family.
diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile
new file mode 100644
index 000000000000..cf93359aa862
--- /dev/null
+++ b/drivers/clk/bcm/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o
+obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o
+obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o
diff --git a/drivers/clk/bcm/clk-bcm281xx.c b/drivers/clk/bcm/clk-bcm281xx.c
new file mode 100644
index 000000000000..3c66de696aeb
--- /dev/null
+++ b/drivers/clk/bcm/clk-bcm281xx.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ * Copyright 2013 Linaro 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "clk-kona.h"
+#include "dt-bindings/clock/bcm281xx.h"
+
+/* bcm11351 CCU device tree "compatible" strings */
+#define BCM11351_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu"
+#define BCM11351_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu"
+#define BCM11351_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu"
+#define BCM11351_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu"
+#define BCM11351_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu"
+
+/* Root CCU clocks */
+
+static struct peri_clk_data frac_1m_data = {
+ .gate = HW_SW_GATE(0x214, 16, 0, 1),
+ .trig = TRIGGER(0x0e04, 0),
+ .div = FRAC_DIVIDER(0x0e00, 0, 22, 16),
+ .clocks = CLOCKS("ref_crystal"),
+};
+
+/* AON CCU clocks */
+
+static struct peri_clk_data hub_timer_data = {
+ .gate = HW_SW_GATE(0x0414, 16, 0, 1),
+ .clocks = CLOCKS("bbl_32k",
+ "frac_1m",
+ "dft_19_5m"),
+ .sel = SELECTOR(0x0a10, 0, 2),
+ .trig = TRIGGER(0x0a40, 4),
+};
+
+static struct peri_clk_data pmu_bsc_data = {
+ .gate = HW_SW_GATE(0x0418, 16, 0, 1),
+ .clocks = CLOCKS("ref_crystal",
+ "pmu_bsc_var",
+ "bbl_32k"),
+ .sel = SELECTOR(0x0a04, 0, 2),
+ .div = DIVIDER(0x0a04, 3, 4),
+ .trig = TRIGGER(0x0a40, 0),
+};
+
+static struct peri_clk_data pmu_bsc_var_data = {
+ .clocks = CLOCKS("var_312m",
+ "ref_312m"),
+ .sel = SELECTOR(0x0a00, 0, 2),
+ .div = DIVIDER(0x0a00, 4, 5),
+ .trig = TRIGGER(0x0a40, 2),
+};
+
+/* Hub CCU clocks */
+
+static struct peri_clk_data tmon_1m_data = {
+ .gate = HW_SW_GATE(0x04a4, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "frac_1m"),
+ .sel = SELECTOR(0x0e74, 0, 2),
+ .trig = TRIGGER(0x0e84, 1),
+};
+
+/* Master CCU clocks */
+
+static struct peri_clk_data sdio1_data = {
+ .gate = HW_SW_GATE(0x0358, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_52m",
+ "ref_52m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a28, 0, 3),
+ .div = DIVIDER(0x0a28, 4, 14),
+ .trig = TRIGGER(0x0afc, 9),
+};
+
+static struct peri_clk_data sdio2_data = {
+ .gate = HW_SW_GATE(0x035c, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_52m",
+ "ref_52m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a2c, 0, 3),
+ .div = DIVIDER(0x0a2c, 4, 14),
+ .trig = TRIGGER(0x0afc, 10),
+};
+
+static struct peri_clk_data sdio3_data = {
+ .gate = HW_SW_GATE(0x0364, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_52m",
+ "ref_52m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a34, 0, 3),
+ .div = DIVIDER(0x0a34, 4, 14),
+ .trig = TRIGGER(0x0afc, 12),
+};
+
+static struct peri_clk_data sdio4_data = {
+ .gate = HW_SW_GATE(0x0360, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_52m",
+ "ref_52m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a30, 0, 3),
+ .div = DIVIDER(0x0a30, 4, 14),
+ .trig = TRIGGER(0x0afc, 11),
+};
+
+static struct peri_clk_data usb_ic_data = {
+ .gate = HW_SW_GATE(0x0354, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_96m",
+ "ref_96m"),
+ .div = FIXED_DIVIDER(2),
+ .sel = SELECTOR(0x0a24, 0, 2),
+ .trig = TRIGGER(0x0afc, 7),
+};
+
+/* also called usbh_48m */
+static struct peri_clk_data hsic2_48m_data = {
+ .gate = HW_SW_GATE(0x0370, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a38, 0, 2),
+ .div = FIXED_DIVIDER(2),
+ .trig = TRIGGER(0x0afc, 5),
+};
+
+/* also called usbh_12m */
+static struct peri_clk_data hsic2_12m_data = {
+ .gate = HW_SW_GATE(0x0370, 20, 4, 5),
+ .div = DIVIDER(0x0a38, 12, 2),
+ .clocks = CLOCKS("ref_crystal",
+ "var_96m",
+ "ref_96m"),
+ .pre_div = FIXED_DIVIDER(2),
+ .sel = SELECTOR(0x0a38, 0, 2),
+ .trig = TRIGGER(0x0afc, 5),
+};
+
+/* Slave CCU clocks */
+
+static struct peri_clk_data uartb_data = {
+ .gate = HW_SW_GATE(0x0400, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_156m",
+ "ref_156m"),
+ .sel = SELECTOR(0x0a10, 0, 2),
+ .div = FRAC_DIVIDER(0x0a10, 4, 12, 8),
+ .trig = TRIGGER(0x0afc, 2),
+};
+
+static struct peri_clk_data uartb2_data = {
+ .gate = HW_SW_GATE(0x0404, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_156m",
+ "ref_156m"),
+ .sel = SELECTOR(0x0a14, 0, 2),
+ .div = FRAC_DIVIDER(0x0a14, 4, 12, 8),
+ .trig = TRIGGER(0x0afc, 3),
+};
+
+static struct peri_clk_data uartb3_data = {
+ .gate = HW_SW_GATE(0x0408, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_156m",
+ "ref_156m"),
+ .sel = SELECTOR(0x0a18, 0, 2),
+ .div = FRAC_DIVIDER(0x0a18, 4, 12, 8),
+ .trig = TRIGGER(0x0afc, 4),
+};
+
+static struct peri_clk_data uartb4_data = {
+ .gate = HW_SW_GATE(0x0408, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_156m",
+ "ref_156m"),
+ .sel = SELECTOR(0x0a1c, 0, 2),
+ .div = FRAC_DIVIDER(0x0a1c, 4, 12, 8),
+ .trig = TRIGGER(0x0afc, 5),
+};
+
+static struct peri_clk_data ssp0_data = {
+ .gate = HW_SW_GATE(0x0410, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m",
+ "ref_104m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a20, 0, 3),
+ .div = DIVIDER(0x0a20, 4, 14),
+ .trig = TRIGGER(0x0afc, 6),
+};
+
+static struct peri_clk_data ssp2_data = {
+ .gate = HW_SW_GATE(0x0418, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m",
+ "ref_104m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a28, 0, 3),
+ .div = DIVIDER(0x0a28, 4, 14),
+ .trig = TRIGGER(0x0afc, 8),
+};
+
+static struct peri_clk_data bsc1_data = {
+ .gate = HW_SW_GATE(0x0458, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m",
+ "ref_104m",
+ "var_13m",
+ "ref_13m"),
+ .sel = SELECTOR(0x0a64, 0, 3),
+ .trig = TRIGGER(0x0afc, 23),
+};
+
+static struct peri_clk_data bsc2_data = {
+ .gate = HW_SW_GATE(0x045c, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m",
+ "ref_104m",
+ "var_13m",
+ "ref_13m"),
+ .sel = SELECTOR(0x0a68, 0, 3),
+ .trig = TRIGGER(0x0afc, 24),
+};
+
+static struct peri_clk_data bsc3_data = {
+ .gate = HW_SW_GATE(0x0484, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m",
+ "ref_104m",
+ "var_13m",
+ "ref_13m"),
+ .sel = SELECTOR(0x0a84, 0, 3),
+ .trig = TRIGGER(0x0b00, 2),
+};
+
+static struct peri_clk_data pwm_data = {
+ .gate = HW_SW_GATE(0x0468, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m"),
+ .sel = SELECTOR(0x0a70, 0, 2),
+ .div = DIVIDER(0x0a70, 4, 3),
+ .trig = TRIGGER(0x0afc, 15),
+};
+
+/*
+ * CCU setup routines
+ *
+ * These are called from kona_dt_ccu_setup() to initialize the array
+ * of clocks provided by the CCU. Once allocated, the entries in
+ * the array are initialized by calling kona_clk_setup() with the
+ * initialization data for each clock. They return 0 if successful
+ * or an error code otherwise.
+ */
+static int __init bcm281xx_root_ccu_clks_setup(struct ccu_data *ccu)
+{
+ struct clk **clks;
+ size_t count = BCM281XX_ROOT_CCU_CLOCK_COUNT;
+
+ clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
+ if (!clks) {
+ pr_err("%s: failed to allocate root clocks\n", __func__);
+ return -ENOMEM;
+ }
+ ccu->data.clks = clks;
+ ccu->data.clk_num = count;
+
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_ROOT_CCU_FRAC_1M, frac_1m);
+
+ return 0;
+}
+
+static int __init bcm281xx_aon_ccu_clks_setup(struct ccu_data *ccu)
+{
+ struct clk **clks;
+ size_t count = BCM281XX_AON_CCU_CLOCK_COUNT;
+
+ clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
+ if (!clks) {
+ pr_err("%s: failed to allocate aon clocks\n", __func__);
+ return -ENOMEM;
+ }
+ ccu->data.clks = clks;
+ ccu->data.clk_num = count;
+
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_HUB_TIMER, hub_timer);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_PMU_BSC, pmu_bsc);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_PMU_BSC_VAR, pmu_bsc_var);
+
+ return 0;
+}
+
+static int __init bcm281xx_hub_ccu_clks_setup(struct ccu_data *ccu)
+{
+ struct clk **clks;
+ size_t count = BCM281XX_HUB_CCU_CLOCK_COUNT;
+
+ clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
+ if (!clks) {
+ pr_err("%s: failed to allocate hub clocks\n", __func__);
+ return -ENOMEM;
+ }
+ ccu->data.clks = clks;
+ ccu->data.clk_num = count;
+
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_HUB_CCU_TMON_1M, tmon_1m);
+
+ return 0;
+}
+
+static int __init bcm281xx_master_ccu_clks_setup(struct ccu_data *ccu)
+{
+ struct clk **clks;
+ size_t count = BCM281XX_MASTER_CCU_CLOCK_COUNT;
+
+ clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
+ if (!clks) {
+ pr_err("%s: failed to allocate master clocks\n", __func__);
+ return -ENOMEM;
+ }
+ ccu->data.clks = clks;
+ ccu->data.clk_num = count;
+
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO1, sdio1);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO2, sdio2);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO3, sdio3);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO4, sdio4);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_USB_IC, usb_ic);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_HSIC2_48M, hsic2_48m);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_HSIC2_12M, hsic2_12m);
+
+ return 0;
+}
+
+static int __init bcm281xx_slave_ccu_clks_setup(struct ccu_data *ccu)
+{
+ struct clk **clks;
+ size_t count = BCM281XX_SLAVE_CCU_CLOCK_COUNT;
+
+ clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
+ if (!clks) {
+ pr_err("%s: failed to allocate slave clocks\n", __func__);
+ return -ENOMEM;
+ }
+ ccu->data.clks = clks;
+ ccu->data.clk_num = count;
+
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB, uartb);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB2, uartb2);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB3, uartb3);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB4, uartb4);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_SSP0, ssp0);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_SSP2, ssp2);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC1, bsc1);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC2, bsc2);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC3, bsc3);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_PWM, pwm);
+
+ return 0;
+}
+
+/* Device tree match table callback functions */
+
+static void __init kona_dt_root_ccu_setup(struct device_node *node)
+{
+ kona_dt_ccu_setup(node, bcm281xx_root_ccu_clks_setup);
+}
+
+static void __init kona_dt_aon_ccu_setup(struct device_node *node)
+{
+ kona_dt_ccu_setup(node, bcm281xx_aon_ccu_clks_setup);
+}
+
+static void __init kona_dt_hub_ccu_setup(struct device_node *node)
+{
+ kona_dt_ccu_setup(node, bcm281xx_hub_ccu_clks_setup);
+}
+
+static void __init kona_dt_master_ccu_setup(struct device_node *node)
+{
+ kona_dt_ccu_setup(node, bcm281xx_master_ccu_clks_setup);
+}
+
+static void __init kona_dt_slave_ccu_setup(struct device_node *node)
+{
+ kona_dt_ccu_setup(node, bcm281xx_slave_ccu_clks_setup);
+}
+
+CLK_OF_DECLARE(bcm11351_root_ccu, BCM11351_DT_ROOT_CCU_COMPAT,
+ kona_dt_root_ccu_setup);
+CLK_OF_DECLARE(bcm11351_aon_ccu, BCM11351_DT_AON_CCU_COMPAT,
+ kona_dt_aon_ccu_setup);
+CLK_OF_DECLARE(bcm11351_hub_ccu, BCM11351_DT_HUB_CCU_COMPAT,
+ kona_dt_hub_ccu_setup);
+CLK_OF_DECLARE(bcm11351_master_ccu, BCM11351_DT_MASTER_CCU_COMPAT,
+ kona_dt_master_ccu_setup);
+CLK_OF_DECLARE(bcm11351_slave_ccu, BCM11351_DT_SLAVE_CCU_COMPAT,
+ kona_dt_slave_ccu_setup);
diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c
new file mode 100644
index 000000000000..c7607feb18dd
--- /dev/null
+++ b/drivers/clk/bcm/clk-kona-setup.c
@@ -0,0 +1,769 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ * Copyright 2013 Linaro 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/of_address.h>
+
+#include "clk-kona.h"
+
+/* These are used when a selector or trigger is found to be unneeded */
+#define selector_clear_exists(sel) ((sel)->width = 0)
+#define trigger_clear_exists(trig) FLAG_CLEAR(trig, TRIG, EXISTS)
+
+LIST_HEAD(ccu_list); /* The list of set up CCUs */
+
+/* Validity checking */
+
+static bool clk_requires_trigger(struct kona_clk *bcm_clk)
+{
+ struct peri_clk_data *peri = bcm_clk->peri;
+ struct bcm_clk_sel *sel;
+ struct bcm_clk_div *div;
+
+ if (bcm_clk->type != bcm_clk_peri)
+ return false;
+
+ sel = &peri->sel;
+ if (sel->parent_count && selector_exists(sel))
+ return true;
+
+ div = &peri->div;
+ if (!divider_exists(div))
+ return false;
+
+ /* Fixed dividers don't need triggers */
+ if (!divider_is_fixed(div))
+ return true;
+
+ div = &peri->pre_div;
+
+ return divider_exists(div) && !divider_is_fixed(div);
+}
+
+static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
+{
+ struct peri_clk_data *peri;
+ struct bcm_clk_gate *gate;
+ struct bcm_clk_div *div;
+ struct bcm_clk_sel *sel;
+ struct bcm_clk_trig *trig;
+ const char *name;
+ u32 range;
+ u32 limit;
+
+ BUG_ON(bcm_clk->type != bcm_clk_peri);
+ peri = bcm_clk->peri;
+ name = bcm_clk->name;
+ range = bcm_clk->ccu->range;
+
+ limit = range - sizeof(u32);
+ limit = round_down(limit, sizeof(u32));
+
+ gate = &peri->gate;
+ if (gate_exists(gate)) {
+ if (gate->offset > limit) {
+ pr_err("%s: bad gate offset for %s (%u > %u)\n",
+ __func__, name, gate->offset, limit);
+ return false;
+ }
+ }
+
+ div = &peri->div;
+ if (divider_exists(div)) {
+ if (div->offset > limit) {
+ pr_err("%s: bad divider offset for %s (%u > %u)\n",
+ __func__, name, div->offset, limit);
+ return false;
+ }
+ }
+
+ div = &peri->pre_div;
+ if (divider_exists(div)) {
+ if (div->offset > limit) {
+ pr_err("%s: bad pre-divider offset for %s "
+ "(%u > %u)\n",
+ __func__, name, div->offset, limit);
+ return false;
+ }
+ }
+
+ sel = &peri->sel;
+ if (selector_exists(sel)) {
+ if (sel->offset > limit) {
+ pr_err("%s: bad selector offset for %s (%u > %u)\n",
+ __func__, name, sel->offset, limit);
+ return false;
+ }
+ }
+
+ trig = &peri->trig;
+ if (trigger_exists(trig)) {
+ if (trig->offset > limit) {
+ pr_err("%s: bad trigger offset for %s (%u > %u)\n",
+ __func__, name, trig->offset, limit);
+ return false;
+ }
+ }
+
+ trig = &peri->pre_trig;
+ if (trigger_exists(trig)) {
+ if (trig->offset > limit) {
+ pr_err("%s: bad pre-trigger offset for %s (%u > %u)\n",
+ __func__, name, trig->offset, limit);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* A bit position must be less than the number of bits in a 32-bit register. */
+static bool bit_posn_valid(u32 bit_posn, const char *field_name,
+ const char *clock_name)
+{
+ u32 limit = BITS_PER_BYTE * sizeof(u32) - 1;
+
+ if (bit_posn > limit) {
+ pr_err("%s: bad %s bit for %s (%u > %u)\n", __func__,
+ field_name, clock_name, bit_posn, limit);
+ return false;
+ }
+ return true;
+}
+
+/*
+ * A bitfield must be at least 1 bit wide. Both the low-order and
+ * high-order bits must lie within a 32-bit register. We require
+ * fields to be less than 32 bits wide, mainly because we use
+ * shifting to produce field masks, and shifting a full word width
+ * is not well-defined by the C standard.
+ */
+static bool bitfield_valid(u32 shift, u32 width, const char *field_name,
+ const char *clock_name)
+{
+ u32 limit = BITS_PER_BYTE * sizeof(u32);
+
+ if (!width) {
+ pr_err("%s: bad %s field width 0 for %s\n", __func__,
+ field_name, clock_name);
+ return false;
+ }
+ if (shift + width > limit) {
+ pr_err("%s: bad %s for %s (%u + %u > %u)\n", __func__,
+ field_name, clock_name, shift, width, limit);
+ return false;
+ }
+ return true;
+}
+
+/*
+ * All gates, if defined, have a status bit, and for hardware-only
+ * gates, that's it. Gates that can be software controlled also
+ * have an enable bit. And a gate that can be hardware or software
+ * controlled will have a hardware/software select bit.
+ */
+static bool gate_valid(struct bcm_clk_gate *gate, const char *field_name,
+ const char *clock_name)
+{
+ if (!bit_posn_valid(gate->status_bit, "gate status", clock_name))
+ return false;
+
+ if (gate_is_sw_controllable(gate)) {
+ if (!bit_posn_valid(gate->en_bit, "gate enable", clock_name))
+ return false;
+
+ if (gate_is_hw_controllable(gate)) {
+ if (!bit_posn_valid(gate->hw_sw_sel_bit,
+ "gate hw/sw select",
+ clock_name))
+ return false;
+ }
+ } else {
+ BUG_ON(!gate_is_hw_controllable(gate));
+ }
+
+ return true;
+}
+
+/*
+ * A selector bitfield must be valid. Its parent_sel array must
+ * also be reasonable for the field.
+ */
+static bool sel_valid(struct bcm_clk_sel *sel, const char *field_name,
+ const char *clock_name)
+{
+ if (!bitfield_valid(sel->shift, sel->width, field_name, clock_name))
+ return false;
+
+ if (sel->parent_count) {
+ u32 max_sel;
+ u32 limit;
+
+ /*
+ * Make sure the selector field can hold all the
+ * selector values we expect to be able to use. A
+ * clock only needs to have a selector defined if it
+ * has more than one parent. And in that case the
+ * highest selector value will be in the last entry
+ * in the array.
+ */
+ max_sel = sel->parent_sel[sel->parent_count - 1];
+ limit = (1 << sel->width) - 1;
+ if (max_sel > limit) {
+ pr_err("%s: bad selector for %s "
+ "(%u needs > %u bits)\n",
+ __func__, clock_name, max_sel,
+ sel->width);
+ return false;
+ }
+ } else {
+ pr_warn("%s: ignoring selector for %s (no parents)\n",
+ __func__, clock_name);
+ selector_clear_exists(sel);
+ kfree(sel->parent_sel);
+ sel->parent_sel = NULL;
+ }
+
+ return true;
+}
+
+/*
+ * A fixed divider just needs to be non-zero. A variable divider
+ * has to have a valid divider bitfield, and if it has a fraction,
+ * the width of the fraction must not be no more than the width of
+ * the divider as a whole.
+ */
+static bool div_valid(struct bcm_clk_div *div, const char *field_name,
+ const char *clock_name)
+{
+ if (divider_is_fixed(div)) {
+ /* Any fixed divider value but 0 is OK */
+ if (div->fixed == 0) {
+ pr_err("%s: bad %s fixed value 0 for %s\n", __func__,
+ field_name, clock_name);
+ return false;
+ }
+ return true;
+ }
+ if (!bitfield_valid(div->shift, div->width, field_name, clock_name))
+ return false;
+
+ if (divider_has_fraction(div))
+ if (div->frac_width > div->width) {
+ pr_warn("%s: bad %s fraction width for %s (%u > %u)\n",
+ __func__, field_name, clock_name,
+ div->frac_width, div->width);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * If a clock has two dividers, the combined number of fractional
+ * bits must be representable in a 32-bit unsigned value. This
+ * is because we scale up a dividend using both dividers before
+ * dividing to improve accuracy, and we need to avoid overflow.
+ */
+static bool kona_dividers_valid(struct kona_clk *bcm_clk)
+{
+ struct peri_clk_data *peri = bcm_clk->peri;
+ struct bcm_clk_div *div;
+ struct bcm_clk_div *pre_div;
+ u32 limit;
+
+ BUG_ON(bcm_clk->type != bcm_clk_peri);
+
+ if (!divider_exists(&peri->div) || !divider_exists(&peri->pre_div))
+ return true;
+
+ div = &peri->div;
+ pre_div = &peri->pre_div;
+ if (divider_is_fixed(div) || divider_is_fixed(pre_div))
+ return true;
+
+ limit = BITS_PER_BYTE * sizeof(u32);
+
+ return div->frac_width + pre_div->frac_width <= limit;
+}
+
+
+/* A trigger just needs to represent a valid bit position */
+static bool trig_valid(struct bcm_clk_trig *trig, const char *field_name,
+ const char *clock_name)
+{
+ return bit_posn_valid(trig->bit, field_name, clock_name);
+}
+
+/* Determine whether the set of peripheral clock registers are valid. */
+static bool
+peri_clk_data_valid(struct kona_clk *bcm_clk)
+{
+ struct peri_clk_data *peri;
+ struct bcm_clk_gate *gate;
+ struct bcm_clk_sel *sel;
+ struct bcm_clk_div *div;
+ struct bcm_clk_div *pre_div;
+ struct bcm_clk_trig *trig;
+ const char *name;
+
+ BUG_ON(bcm_clk->type != bcm_clk_peri);
+
+ /*
+ * First validate register offsets. This is the only place
+ * where we need something from the ccu, so we do these
+ * together.
+ */
+ if (!peri_clk_data_offsets_valid(bcm_clk))
+ return false;
+
+ peri = bcm_clk->peri;
+ name = bcm_clk->name;
+ gate = &peri->gate;
+ if (gate_exists(gate) && !gate_valid(gate, "gate", name))
+ return false;
+
+ sel = &peri->sel;
+ if (selector_exists(sel)) {
+ if (!sel_valid(sel, "selector", name))
+ return false;
+
+ } else if (sel->parent_count > 1) {
+ pr_err("%s: multiple parents but no selector for %s\n",
+ __func__, name);
+
+ return false;
+ }
+
+ div = &peri->div;
+ pre_div = &peri->pre_div;
+ if (divider_exists(div)) {
+ if (!div_valid(div, "divider", name))
+ return false;
+
+ if (divider_exists(pre_div))
+ if (!div_valid(pre_div, "pre-divider", name))
+ return false;
+ } else if (divider_exists(pre_div)) {
+ pr_err("%s: pre-divider but no divider for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ trig = &peri->trig;
+ if (trigger_exists(trig)) {
+ if (!trig_valid(trig, "trigger", name))
+ return false;
+
+ if (trigger_exists(&peri->pre_trig)) {
+ if (!trig_valid(trig, "pre-trigger", name)) {
+ return false;
+ }
+ }
+ if (!clk_requires_trigger(bcm_clk)) {
+ pr_warn("%s: ignoring trigger for %s (not needed)\n",
+ __func__, name);
+ trigger_clear_exists(trig);
+ }
+ } else if (trigger_exists(&peri->pre_trig)) {
+ pr_err("%s: pre-trigger but no trigger for %s\n", __func__,
+ name);
+ return false;
+ } else if (clk_requires_trigger(bcm_clk)) {
+ pr_err("%s: required trigger missing for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ return kona_dividers_valid(bcm_clk);
+}
+
+static bool kona_clk_valid(struct kona_clk *bcm_clk)
+{
+ switch (bcm_clk->type) {
+ case bcm_clk_peri:
+ if (!peri_clk_data_valid(bcm_clk))
+ return false;
+ break;
+ default:
+ pr_err("%s: unrecognized clock type (%d)\n", __func__,
+ (int)bcm_clk->type);
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Scan an array of parent clock names to determine whether there
+ * are any entries containing BAD_CLK_NAME. Such entries are
+ * placeholders for non-supported clocks. Keep track of the
+ * position of each clock name in the original array.
+ *
+ * Allocates an array of pointers to to hold the names of all
+ * non-null entries in the original array, and returns a pointer to
+ * that array in *names. This will be used for registering the
+ * clock with the common clock code. On successful return,
+ * *count indicates how many entries are in that names array.
+ *
+ * If there is more than one entry in the resulting names array,
+ * another array is allocated to record the parent selector value
+ * for each (defined) parent clock. This is the value that
+ * represents this parent clock in the clock's source selector
+ * register. The position of the clock in the original parent array
+ * defines that selector value. The number of entries in this array
+ * is the same as the number of entries in the parent names array.
+ *
+ * The array of selector values is returned. If the clock has no
+ * parents, no selector is required and a null pointer is returned.
+ *
+ * Returns a null pointer if the clock names array supplied was
+ * null. (This is not an error.)
+ *
+ * Returns a pointer-coded error if an error occurs.
+ */
+static u32 *parent_process(const char *clocks[],
+ u32 *count, const char ***names)
+{
+ static const char **parent_names;
+ static u32 *parent_sel;
+ const char **clock;
+ u32 parent_count;
+ u32 bad_count = 0;
+ u32 orig_count;
+ u32 i;
+ u32 j;
+
+ *count = 0; /* In case of early return */
+ *names = NULL;
+ if (!clocks)
+ return NULL;
+
+ /*
+ * Count the number of names in the null-terminated array,
+ * and find out how many of those are actually clock names.
+ */
+ for (clock = clocks; *clock; clock++)
+ if (*clock == BAD_CLK_NAME)
+ bad_count++;
+ orig_count = (u32)(clock - clocks);
+ parent_count = orig_count - bad_count;
+
+ /* If all clocks are unsupported, we treat it as no clock */
+ if (!parent_count)
+ return NULL;
+
+ /* Avoid exceeding our parent clock limit */
+ if (parent_count > PARENT_COUNT_MAX) {
+ pr_err("%s: too many parents (%u > %u)\n", __func__,
+ parent_count, PARENT_COUNT_MAX);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /*
+ * There is one parent name for each defined parent clock.
+ * We also maintain an array containing the selector value
+ * for each defined clock. If there's only one clock, the
+ * selector is not required, but we allocate space for the
+ * array anyway to keep things simple.
+ */
+ parent_names = kmalloc(parent_count * sizeof(parent_names), GFP_KERNEL);
+ if (!parent_names) {
+ pr_err("%s: error allocating %u parent names\n", __func__,
+ parent_count);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* There is at least one parent, so allocate a selector array */
+
+ parent_sel = kmalloc(parent_count * sizeof(*parent_sel), GFP_KERNEL);
+ if (!parent_sel) {
+ pr_err("%s: error allocating %u parent selectors\n", __func__,
+ parent_count);
+ kfree(parent_names);
+
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Now fill in the parent names and selector arrays */
+ for (i = 0, j = 0; i < orig_count; i++) {
+ if (clocks[i] != BAD_CLK_NAME) {
+ parent_names[j] = clocks[i];
+ parent_sel[j] = i;
+ j++;
+ }
+ }
+ *names = parent_names;
+ *count = parent_count;
+
+ return parent_sel;
+}
+
+static int
+clk_sel_setup(const char **clocks, struct bcm_clk_sel *sel,
+ struct clk_init_data *init_data)
+{
+ const char **parent_names = NULL;
+ u32 parent_count = 0;
+ u32 *parent_sel;
+
+ /*
+ * If a peripheral clock has multiple parents, the value
+ * used by the hardware to select that parent is represented
+ * by the parent clock's position in the "clocks" list. Some
+ * values don't have defined or supported clocks; these will
+ * have BAD_CLK_NAME entries in the parents[] array. The
+ * list is terminated by a NULL entry.
+ *
+ * We need to supply (only) the names of defined parent
+ * clocks when registering a clock though, so we use an
+ * array of parent selector values to map between the
+ * indexes the common clock code uses and the selector
+ * values we need.
+ */
+ parent_sel = parent_process(clocks, &parent_count, &parent_names);
+ if (IS_ERR(parent_sel)) {
+ int ret = PTR_ERR(parent_sel);
+
+ pr_err("%s: error processing parent clocks for %s (%d)\n",
+ __func__, init_data->name, ret);
+
+ return ret;
+ }
+
+ init_data->parent_names = parent_names;
+ init_data->num_parents = parent_count;
+
+ sel->parent_count = parent_count;
+ sel->parent_sel = parent_sel;
+
+ return 0;
+}
+
+static void clk_sel_teardown(struct bcm_clk_sel *sel,
+ struct clk_init_data *init_data)
+{
+ kfree(sel->parent_sel);
+ sel->parent_sel = NULL;
+ sel->parent_count = 0;
+
+ init_data->num_parents = 0;
+ kfree(init_data->parent_names);
+ init_data->parent_names = NULL;
+}
+
+static void peri_clk_teardown(struct peri_clk_data *data,
+ struct clk_init_data *init_data)
+{
+ clk_sel_teardown(&data->sel, init_data);
+ init_data->ops = NULL;
+}
+
+/*
+ * Caller is responsible for freeing the parent_names[] and
+ * parent_sel[] arrays in the peripheral clock's "data" structure
+ * that can be assigned if the clock has one or more parent clocks
+ * associated with it.
+ */
+static int peri_clk_setup(struct ccu_data *ccu, struct peri_clk_data *data,
+ struct clk_init_data *init_data)
+{
+ init_data->ops = &kona_peri_clk_ops;
+ init_data->flags = CLK_IGNORE_UNUSED;
+
+ return clk_sel_setup(data->clocks, &data->sel, init_data);
+}
+
+static void bcm_clk_teardown(struct kona_clk *bcm_clk)
+{
+ switch (bcm_clk->type) {
+ case bcm_clk_peri:
+ peri_clk_teardown(bcm_clk->data, &bcm_clk->init_data);
+ break;
+ default:
+ break;
+ }
+ bcm_clk->data = NULL;
+ bcm_clk->type = bcm_clk_none;
+}
+
+static void kona_clk_teardown(struct clk *clk)
+{
+ struct clk_hw *hw;
+ struct kona_clk *bcm_clk;
+
+ if (!clk)
+ return;
+
+ hw = __clk_get_hw(clk);
+ if (!hw) {
+ pr_err("%s: clk %p has null hw pointer\n", __func__, clk);
+ return;
+ }
+ clk_unregister(clk);
+
+ bcm_clk = to_kona_clk(hw);
+ bcm_clk_teardown(bcm_clk);
+}
+
+struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name,
+ enum bcm_clk_type type, void *data)
+{
+ struct kona_clk *bcm_clk;
+ struct clk_init_data *init_data;
+ struct clk *clk = NULL;
+
+ bcm_clk = kzalloc(sizeof(*bcm_clk), GFP_KERNEL);
+ if (!bcm_clk) {
+ pr_err("%s: failed to allocate bcm_clk for %s\n", __func__,
+ name);
+ return NULL;
+ }
+ bcm_clk->ccu = ccu;
+ bcm_clk->name = name;
+
+ init_data = &bcm_clk->init_data;
+ init_data->name = name;
+ switch (type) {
+ case bcm_clk_peri:
+ if (peri_clk_setup(ccu, data, init_data))
+ goto out_free;
+ break;
+ default:
+ data = NULL;
+ break;
+ }
+ bcm_clk->type = type;
+ bcm_clk->data = data;
+
+ /* Make sure everything makes sense before we set it up */
+ if (!kona_clk_valid(bcm_clk)) {
+ pr_err("%s: clock data invalid for %s\n", __func__, name);
+ goto out_teardown;
+ }
+
+ bcm_clk->hw.init = init_data;
+ clk = clk_register(NULL, &bcm_clk->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: error registering clock %s (%ld)\n", __func__,
+ name, PTR_ERR(clk));
+ goto out_teardown;
+ }
+ BUG_ON(!clk);
+
+ return clk;
+out_teardown:
+ bcm_clk_teardown(bcm_clk);
+out_free:
+ kfree(bcm_clk);
+
+ return NULL;
+}
+
+static void ccu_clks_teardown(struct ccu_data *ccu)
+{
+ u32 i;
+
+ for (i = 0; i < ccu->data.clk_num; i++)
+ kona_clk_teardown(ccu->data.clks[i]);
+ kfree(ccu->data.clks);
+}
+
+static void kona_ccu_teardown(struct ccu_data *ccu)
+{
+ if (!ccu)
+ return;
+
+ if (!ccu->base)
+ goto done;
+
+ of_clk_del_provider(ccu->node); /* safe if never added */
+ ccu_clks_teardown(ccu);
+ list_del(&ccu->links);
+ of_node_put(ccu->node);
+ iounmap(ccu->base);
+done:
+ kfree(ccu->name);
+ kfree(ccu);
+}
+
+/*
+ * Set up a CCU. Call the provided ccu_clks_setup callback to
+ * initialize the array of clocks provided by the CCU.
+ */
+void __init kona_dt_ccu_setup(struct device_node *node,
+ int (*ccu_clks_setup)(struct ccu_data *))
+{
+ struct ccu_data *ccu;
+ struct resource res = { 0 };
+ resource_size_t range;
+ int ret;
+
+ ccu = kzalloc(sizeof(*ccu), GFP_KERNEL);
+ if (ccu)
+ ccu->name = kstrdup(node->name, GFP_KERNEL);
+ if (!ccu || !ccu->name) {
+ pr_err("%s: unable to allocate CCU struct for %s\n",
+ __func__, node->name);
+ kfree(ccu);
+
+ return;
+ }
+
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret) {
+ pr_err("%s: no valid CCU registers found for %s\n", __func__,
+ node->name);
+ goto out_err;
+ }
+
+ range = resource_size(&res);
+ if (range > (resource_size_t)U32_MAX) {
+ pr_err("%s: address range too large for %s\n", __func__,
+ node->name);
+ goto out_err;
+ }
+
+ ccu->range = (u32)range;
+ ccu->base = ioremap(res.start, ccu->range);
+ if (!ccu->base) {
+ pr_err("%s: unable to map CCU registers for %s\n", __func__,
+ node->name);
+ goto out_err;
+ }
+
+ spin_lock_init(&ccu->lock);
+ INIT_LIST_HEAD(&ccu->links);
+ ccu->node = of_node_get(node);
+
+ list_add_tail(&ccu->links, &ccu_list);
+
+ /* Set up clocks array (in ccu->data) */
+ if (ccu_clks_setup(ccu))
+ goto out_err;
+
+ ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->data);
+ if (ret) {
+ pr_err("%s: error adding ccu %s as provider (%d)\n", __func__,
+ node->name, ret);
+ goto out_err;
+ }
+
+ if (!kona_ccu_init(ccu))
+ pr_err("Broadcom %s initialization had errors\n", node->name);
+
+ return;
+out_err:
+ kona_ccu_teardown(ccu);
+ pr_err("Broadcom %s setup aborted\n", node->name);
+}
diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c
new file mode 100644
index 000000000000..e3d339e08309
--- /dev/null
+++ b/drivers/clk/bcm/clk-kona.c
@@ -0,0 +1,1033 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ * Copyright 2013 Linaro 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "clk-kona.h"
+
+#include <linux/delay.h>
+
+#define CCU_ACCESS_PASSWORD 0xA5A500
+#define CLK_GATE_DELAY_LOOP 2000
+
+/* Bitfield operations */
+
+/* Produces a mask of set bits covering a range of a 32-bit value */
+static inline u32 bitfield_mask(u32 shift, u32 width)
+{
+ return ((1 << width) - 1) << shift;
+}
+
+/* Extract the value of a bitfield found within a given register value */
+static inline u32 bitfield_extract(u32 reg_val, u32 shift, u32 width)
+{
+ return (reg_val & bitfield_mask(shift, width)) >> shift;
+}
+
+/* Replace the value of a bitfield found within a given register value */
+static inline u32 bitfield_replace(u32 reg_val, u32 shift, u32 width, u32 val)
+{
+ u32 mask = bitfield_mask(shift, width);
+
+ return (reg_val & ~mask) | (val << shift);
+}
+
+/* Divider and scaling helpers */
+
+/*
+ * Implement DIV_ROUND_CLOSEST() for 64-bit dividend and both values
+ * unsigned. Note that unlike do_div(), the remainder is discarded
+ * and the return value is the quotient (not the remainder).
+ */
+u64 do_div_round_closest(u64 dividend, unsigned long divisor)
+{
+ u64 result;
+
+ result = dividend + ((u64)divisor >> 1);
+ (void)do_div(result, divisor);
+
+ return result;
+}
+
+/* Convert a divider into the scaled divisor value it represents. */
+static inline u64 scaled_div_value(struct bcm_clk_div *div, u32 reg_div)
+{
+ return (u64)reg_div + ((u64)1 << div->frac_width);
+}
+
+/*
+ * Build a scaled divider value as close as possible to the
+ * given whole part (div_value) and fractional part (expressed
+ * in billionths).
+ */
+u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths)
+{
+ u64 combined;
+
+ BUG_ON(!div_value);
+ BUG_ON(billionths >= BILLION);
+
+ combined = (u64)div_value * BILLION + billionths;
+ combined <<= div->frac_width;
+
+ return do_div_round_closest(combined, BILLION);
+}
+
+/* The scaled minimum divisor representable by a divider */
+static inline u64
+scaled_div_min(struct bcm_clk_div *div)
+{
+ if (divider_is_fixed(div))
+ return (u64)div->fixed;
+
+ return scaled_div_value(div, 0);
+}
+
+/* The scaled maximum divisor representable by a divider */
+u64 scaled_div_max(struct bcm_clk_div *div)
+{
+ u32 reg_div;
+
+ if (divider_is_fixed(div))
+ return (u64)div->fixed;
+
+ reg_div = ((u32)1 << div->width) - 1;
+
+ return scaled_div_value(div, reg_div);
+}
+
+/*
+ * Convert a scaled divisor into its divider representation as
+ * stored in a divider register field.
+ */
+static inline u32
+divider(struct bcm_clk_div *div, u64 scaled_div)
+{
+ BUG_ON(scaled_div < scaled_div_min(div));
+ BUG_ON(scaled_div > scaled_div_max(div));
+
+ return (u32)(scaled_div - ((u64)1 << div->frac_width));
+}
+
+/* Return a rate scaled for use when dividing by a scaled divisor. */
+static inline u64
+scale_rate(struct bcm_clk_div *div, u32 rate)
+{
+ if (divider_is_fixed(div))
+ return (u64)rate;
+
+ return (u64)rate << div->frac_width;
+}
+
+/* CCU access */
+
+/* Read a 32-bit register value from a CCU's address space. */
+static inline u32 __ccu_read(struct ccu_data *ccu, u32 reg_offset)
+{
+ return readl(ccu->base + reg_offset);
+}
+
+/* Write a 32-bit register value into a CCU's address space. */
+static inline void
+__ccu_write(struct ccu_data *ccu, u32 reg_offset, u32 reg_val)
+{
+ writel(reg_val, ccu->base + reg_offset);
+}
+
+static inline unsigned long ccu_lock(struct ccu_data *ccu)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ccu->lock, flags);
+
+ return flags;
+}
+static inline void ccu_unlock(struct ccu_data *ccu, unsigned long flags)
+{
+ spin_unlock_irqrestore(&ccu->lock, flags);
+}
+
+/*
+ * Enable/disable write access to CCU protected registers. The
+ * WR_ACCESS register for all CCUs is at offset 0.
+ */
+static inline void __ccu_write_enable(struct ccu_data *ccu)
+{
+ if (ccu->write_enabled) {
+ pr_err("%s: access already enabled for %s\n", __func__,
+ ccu->name);
+ return;
+ }
+ ccu->write_enabled = true;
+ __ccu_write(ccu, 0, CCU_ACCESS_PASSWORD | 1);
+}
+
+static inline void __ccu_write_disable(struct ccu_data *ccu)
+{
+ if (!ccu->write_enabled) {
+ pr_err("%s: access wasn't enabled for %s\n", __func__,
+ ccu->name);
+ return;
+ }
+
+ __ccu_write(ccu, 0, CCU_ACCESS_PASSWORD);
+ ccu->write_enabled = false;
+}
+
+/*
+ * Poll a register in a CCU's address space, returning when the
+ * specified bit in that register's value is set (or clear). Delay
+ * a microsecond after each read of the register. Returns true if
+ * successful, or false if we gave up trying.
+ *
+ * Caller must ensure the CCU lock is held.
+ */
+static inline bool
+__ccu_wait_bit(struct ccu_data *ccu, u32 reg_offset, u32 bit, bool want)
+{
+ unsigned int tries;
+ u32 bit_mask = 1 << bit;
+
+ for (tries = 0; tries < CLK_GATE_DELAY_LOOP; tries++) {
+ u32 val;
+ bool bit_val;
+
+ val = __ccu_read(ccu, reg_offset);
+ bit_val = (val & bit_mask) != 0;
+ if (bit_val == want)
+ return true;
+ udelay(1);
+ }
+ return false;
+}
+
+/* Gate operations */
+
+/* Determine whether a clock is gated. CCU lock must be held. */
+static bool
+__is_clk_gate_enabled(struct ccu_data *ccu, struct bcm_clk_gate *gate)
+{
+ u32 bit_mask;
+ u32 reg_val;
+
+ /* If there is no gate we can assume it's enabled. */
+ if (!gate_exists(gate))
+ return true;
+
+ bit_mask = 1 << gate->status_bit;
+ reg_val = __ccu_read(ccu, gate->offset);
+
+ return (reg_val & bit_mask) != 0;
+}
+
+/* Determine whether a clock is gated. */
+static bool
+is_clk_gate_enabled(struct ccu_data *ccu, struct bcm_clk_gate *gate)
+{
+ long flags;
+ bool ret;
+
+ /* Avoid taking the lock if we can */
+ if (!gate_exists(gate))
+ return true;
+
+ flags = ccu_lock(ccu);
+ ret = __is_clk_gate_enabled(ccu, gate);
+ ccu_unlock(ccu, flags);
+
+ return ret;
+}
+
+/*
+ * Commit our desired gate state to the hardware.
+ * Returns true if successful, false otherwise.
+ */
+static bool
+__gate_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate)
+{
+ u32 reg_val;
+ u32 mask;
+ bool enabled = false;
+
+ BUG_ON(!gate_exists(gate));
+ if (!gate_is_sw_controllable(gate))
+ return true; /* Nothing we can change */
+
+ reg_val = __ccu_read(ccu, gate->offset);
+
+ /* For a hardware/software gate, set which is in control */
+ if (gate_is_hw_controllable(gate)) {
+ mask = (u32)1 << gate->hw_sw_sel_bit;
+ if (gate_is_sw_managed(gate))
+ reg_val |= mask;
+ else
+ reg_val &= ~mask;
+ }
+
+ /*
+ * If software is in control, enable or disable the gate.
+ * If hardware is, clear the enabled bit for good measure.
+ * If a software controlled gate can't be disabled, we're
+ * required to write a 0 into the enable bit (but the gate
+ * will be enabled).
+ */
+ mask = (u32)1 << gate->en_bit;
+ if (gate_is_sw_managed(gate) && (enabled = gate_is_enabled(gate)) &&
+ !gate_is_no_disable(gate))
+ reg_val |= mask;
+ else
+ reg_val &= ~mask;
+
+ __ccu_write(ccu, gate->offset, reg_val);
+
+ /* For a hardware controlled gate, we're done */
+ if (!gate_is_sw_managed(gate))
+ return true;
+
+ /* Otherwise wait for the gate to be in desired state */
+ return __ccu_wait_bit(ccu, gate->offset, gate->status_bit, enabled);
+}
+
+/*
+ * Initialize a gate. Our desired state (hardware/software select,
+ * and if software, its enable state) is committed to hardware
+ * without the usual checks to see if it's already set up that way.
+ * Returns true if successful, false otherwise.
+ */
+static bool gate_init(struct ccu_data *ccu, struct bcm_clk_gate *gate)
+{
+ if (!gate_exists(gate))
+ return true;
+ return __gate_commit(ccu, gate);
+}
+
+/*
+ * Set a gate to enabled or disabled state. Does nothing if the
+ * gate is not currently under software control, or if it is already
+ * in the requested state. Returns true if successful, false
+ * otherwise. CCU lock must be held.
+ */
+static bool
+__clk_gate(struct ccu_data *ccu, struct bcm_clk_gate *gate, bool enable)
+{
+ bool ret;
+
+ if (!gate_exists(gate) || !gate_is_sw_managed(gate))
+ return true; /* Nothing to do */
+
+ if (!enable && gate_is_no_disable(gate)) {
+ pr_warn("%s: invalid gate disable request (ignoring)\n",
+ __func__);
+ return true;
+ }
+
+ if (enable == gate_is_enabled(gate))
+ return true; /* No change */
+
+ gate_flip_enabled(gate);
+ ret = __gate_commit(ccu, gate);
+ if (!ret)
+ gate_flip_enabled(gate); /* Revert the change */
+
+ return ret;
+}
+
+/* Enable or disable a gate. Returns 0 if successful, -EIO otherwise */
+static int clk_gate(struct ccu_data *ccu, const char *name,
+ struct bcm_clk_gate *gate, bool enable)
+{
+ unsigned long flags;
+ bool success;
+
+ /*
+ * Avoid taking the lock if we can. We quietly ignore
+ * requests to change state that don't make sense.
+ */
+ if (!gate_exists(gate) || !gate_is_sw_managed(gate))
+ return 0;
+ if (!enable && gate_is_no_disable(gate))
+ return 0;
+
+ flags = ccu_lock(ccu);
+ __ccu_write_enable(ccu);
+
+ success = __clk_gate(ccu, gate, enable);
+
+ __ccu_write_disable(ccu);
+ ccu_unlock(ccu, flags);
+
+ if (success)
+ return 0;
+
+ pr_err("%s: failed to %s gate for %s\n", __func__,
+ enable ? "enable" : "disable", name);
+
+ return -EIO;
+}
+
+/* Trigger operations */
+
+/*
+ * Caller must ensure CCU lock is held and access is enabled.
+ * Returns true if successful, false otherwise.
+ */
+static bool __clk_trigger(struct ccu_data *ccu, struct bcm_clk_trig *trig)
+{
+ /* Trigger the clock and wait for it to finish */
+ __ccu_write(ccu, trig->offset, 1 << trig->bit);
+
+ return __ccu_wait_bit(ccu, trig->offset, trig->bit, false);
+}
+
+/* Divider operations */
+
+/* Read a divider value and return the scaled divisor it represents. */
+static u64 divider_read_scaled(struct ccu_data *ccu, struct bcm_clk_div *div)
+{
+ unsigned long flags;
+ u32 reg_val;
+ u32 reg_div;
+
+ if (divider_is_fixed(div))
+ return (u64)div->fixed;
+
+ flags = ccu_lock(ccu);
+ reg_val = __ccu_read(ccu, div->offset);
+ ccu_unlock(ccu, flags);
+
+ /* Extract the full divider field from the register value */
+ reg_div = bitfield_extract(reg_val, div->shift, div->width);
+
+ /* Return the scaled divisor value it represents */
+ return scaled_div_value(div, reg_div);
+}
+
+/*
+ * Convert a divider's scaled divisor value into its recorded form
+ * and commit it into the hardware divider register.
+ *
+ * Returns 0 on success. Returns -EINVAL for invalid arguments.
+ * Returns -ENXIO if gating failed, and -EIO if a trigger failed.
+ */
+static int __div_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_div *div, struct bcm_clk_trig *trig)
+{
+ bool enabled;
+ u32 reg_div;
+ u32 reg_val;
+ int ret = 0;
+
+ BUG_ON(divider_is_fixed(div));
+
+ /*
+ * If we're just initializing the divider, and no initial
+ * state was defined in the device tree, we just find out
+ * what its current value is rather than updating it.
+ */
+ if (div->scaled_div == BAD_SCALED_DIV_VALUE) {
+ reg_val = __ccu_read(ccu, div->offset);
+ reg_div = bitfield_extract(reg_val, div->shift, div->width);
+ div->scaled_div = scaled_div_value(div, reg_div);
+
+ return 0;
+ }
+
+ /* Convert the scaled divisor to the value we need to record */
+ reg_div = divider(div, div->scaled_div);
+
+ /* Clock needs to be enabled before changing the rate */
+ enabled = __is_clk_gate_enabled(ccu, gate);
+ if (!enabled && !__clk_gate(ccu, gate, true)) {
+ ret = -ENXIO;
+ goto out;
+ }
+
+ /* Replace the divider value and record the result */
+ reg_val = __ccu_read(ccu, div->offset);
+ reg_val = bitfield_replace(reg_val, div->shift, div->width, reg_div);
+ __ccu_write(ccu, div->offset, reg_val);
+
+ /* If the trigger fails we still want to disable the gate */
+ if (!__clk_trigger(ccu, trig))
+ ret = -EIO;
+
+ /* Disable the clock again if it was disabled to begin with */
+ if (!enabled && !__clk_gate(ccu, gate, false))
+ ret = ret ? ret : -ENXIO; /* return first error */
+out:
+ return ret;
+}
+
+/*
+ * Initialize a divider by committing our desired state to hardware
+ * without the usual checks to see if it's already set up that way.
+ * Returns true if successful, false otherwise.
+ */
+static bool div_init(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_div *div, struct bcm_clk_trig *trig)
+{
+ if (!divider_exists(div) || divider_is_fixed(div))
+ return true;
+ return !__div_commit(ccu, gate, div, trig);
+}
+
+static int divider_write(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_div *div, struct bcm_clk_trig *trig,
+ u64 scaled_div)
+{
+ unsigned long flags;
+ u64 previous;
+ int ret;
+
+ BUG_ON(divider_is_fixed(div));
+
+ previous = div->scaled_div;
+ if (previous == scaled_div)
+ return 0; /* No change */
+
+ div->scaled_div = scaled_div;
+
+ flags = ccu_lock(ccu);
+ __ccu_write_enable(ccu);
+
+ ret = __div_commit(ccu, gate, div, trig);
+
+ __ccu_write_disable(ccu);
+ ccu_unlock(ccu, flags);
+
+ if (ret)
+ div->scaled_div = previous; /* Revert the change */
+
+ return ret;
+
+}
+
+/* Common clock rate helpers */
+
+/*
+ * Implement the common clock framework recalc_rate method, taking
+ * into account a divider and an optional pre-divider. The
+ * pre-divider register pointer may be NULL.
+ */
+static unsigned long clk_recalc_rate(struct ccu_data *ccu,
+ struct bcm_clk_div *div, struct bcm_clk_div *pre_div,
+ unsigned long parent_rate)
+{
+ u64 scaled_parent_rate;
+ u64 scaled_div;
+ u64 result;
+
+ if (!divider_exists(div))
+ return parent_rate;
+
+ if (parent_rate > (unsigned long)LONG_MAX)
+ return 0; /* actually this would be a caller bug */
+
+ /*
+ * If there is a pre-divider, divide the scaled parent rate
+ * by the pre-divider value first. In this case--to improve
+ * accuracy--scale the parent rate by *both* the pre-divider
+ * value and the divider before actually computing the
+ * result of the pre-divider.
+ *
+ * If there's only one divider, just scale the parent rate.
+ */
+ if (pre_div && divider_exists(pre_div)) {
+ u64 scaled_rate;
+
+ scaled_rate = scale_rate(pre_div, parent_rate);
+ scaled_rate = scale_rate(div, scaled_rate);
+ scaled_div = divider_read_scaled(ccu, pre_div);
+ scaled_parent_rate = do_div_round_closest(scaled_rate,
+ scaled_div);
+ } else {
+ scaled_parent_rate = scale_rate(div, parent_rate);
+ }
+
+ /*
+ * Get the scaled divisor value, and divide the scaled
+ * parent rate by that to determine this clock's resulting
+ * rate.
+ */
+ scaled_div = divider_read_scaled(ccu, div);
+ result = do_div_round_closest(scaled_parent_rate, scaled_div);
+
+ return (unsigned long)result;
+}
+
+/*
+ * Compute the output rate produced when a given parent rate is fed
+ * into two dividers. The pre-divider can be NULL, and even if it's
+ * non-null it may be nonexistent. It's also OK for the divider to
+ * be nonexistent, and in that case the pre-divider is also ignored.
+ *
+ * If scaled_div is non-null, it is used to return the scaled divisor
+ * value used by the (downstream) divider to produce that rate.
+ */
+static long round_rate(struct ccu_data *ccu, struct bcm_clk_div *div,
+ struct bcm_clk_div *pre_div,
+ unsigned long rate, unsigned long parent_rate,
+ u64 *scaled_div)
+{
+ u64 scaled_parent_rate;
+ u64 min_scaled_div;
+ u64 max_scaled_div;
+ u64 best_scaled_div;
+ u64 result;
+
+ BUG_ON(!divider_exists(div));
+ BUG_ON(!rate);
+ BUG_ON(parent_rate > (u64)LONG_MAX);
+
+ /*
+ * If there is a pre-divider, divide the scaled parent rate
+ * by the pre-divider value first. In this case--to improve
+ * accuracy--scale the parent rate by *both* the pre-divider
+ * value and the divider before actually computing the
+ * result of the pre-divider.
+ *
+ * If there's only one divider, just scale the parent rate.
+ *
+ * For simplicity we treat the pre-divider as fixed (for now).
+ */
+ if (divider_exists(pre_div)) {
+ u64 scaled_rate;
+ u64 scaled_pre_div;
+
+ scaled_rate = scale_rate(pre_div, parent_rate);
+ scaled_rate = scale_rate(div, scaled_rate);
+ scaled_pre_div = divider_read_scaled(ccu, pre_div);
+ scaled_parent_rate = do_div_round_closest(scaled_rate,
+ scaled_pre_div);
+ } else {
+ scaled_parent_rate = scale_rate(div, parent_rate);
+ }
+
+ /*
+ * Compute the best possible divider and ensure it is in
+ * range. A fixed divider can't be changed, so just report
+ * the best we can do.
+ */
+ if (!divider_is_fixed(div)) {
+ best_scaled_div = do_div_round_closest(scaled_parent_rate,
+ rate);
+ min_scaled_div = scaled_div_min(div);
+ max_scaled_div = scaled_div_max(div);
+ if (best_scaled_div > max_scaled_div)
+ best_scaled_div = max_scaled_div;
+ else if (best_scaled_div < min_scaled_div)
+ best_scaled_div = min_scaled_div;
+ } else {
+ best_scaled_div = divider_read_scaled(ccu, div);
+ }
+
+ /* OK, figure out the resulting rate */
+ result = do_div_round_closest(scaled_parent_rate, best_scaled_div);
+
+ if (scaled_div)
+ *scaled_div = best_scaled_div;
+
+ return (long)result;
+}
+
+/* Common clock parent helpers */
+
+/*
+ * For a given parent selector (register field) value, find the
+ * index into a selector's parent_sel array that contains it.
+ * Returns the index, or BAD_CLK_INDEX if it's not found.
+ */
+static u8 parent_index(struct bcm_clk_sel *sel, u8 parent_sel)
+{
+ u8 i;
+
+ BUG_ON(sel->parent_count > (u32)U8_MAX);
+ for (i = 0; i < sel->parent_count; i++)
+ if (sel->parent_sel[i] == parent_sel)
+ return i;
+ return BAD_CLK_INDEX;
+}
+
+/*
+ * Fetch the current value of the selector, and translate that into
+ * its corresponding index in the parent array we registered with
+ * the clock framework.
+ *
+ * Returns parent array index that corresponds with the value found,
+ * or BAD_CLK_INDEX if the found value is out of range.
+ */
+static u8 selector_read_index(struct ccu_data *ccu, struct bcm_clk_sel *sel)
+{
+ unsigned long flags;
+ u32 reg_val;
+ u32 parent_sel;
+ u8 index;
+
+ /* If there's no selector, there's only one parent */
+ if (!selector_exists(sel))
+ return 0;
+
+ /* Get the value in the selector register */
+ flags = ccu_lock(ccu);
+ reg_val = __ccu_read(ccu, sel->offset);
+ ccu_unlock(ccu, flags);
+
+ parent_sel = bitfield_extract(reg_val, sel->shift, sel->width);
+
+ /* Look up that selector's parent array index and return it */
+ index = parent_index(sel, parent_sel);
+ if (index == BAD_CLK_INDEX)
+ pr_err("%s: out-of-range parent selector %u (%s 0x%04x)\n",
+ __func__, parent_sel, ccu->name, sel->offset);
+
+ return index;
+}
+
+/*
+ * Commit our desired selector value to the hardware.
+ *
+ * Returns 0 on success. Returns -EINVAL for invalid arguments.
+ * Returns -ENXIO if gating failed, and -EIO if a trigger failed.
+ */
+static int
+__sel_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_sel *sel, struct bcm_clk_trig *trig)
+{
+ u32 parent_sel;
+ u32 reg_val;
+ bool enabled;
+ int ret = 0;
+
+ BUG_ON(!selector_exists(sel));
+
+ /*
+ * If we're just initializing the selector, and no initial
+ * state was defined in the device tree, we just find out
+ * what its current value is rather than updating it.
+ */
+ if (sel->clk_index == BAD_CLK_INDEX) {
+ u8 index;
+
+ reg_val = __ccu_read(ccu, sel->offset);
+ parent_sel = bitfield_extract(reg_val, sel->shift, sel->width);
+ index = parent_index(sel, parent_sel);
+ if (index == BAD_CLK_INDEX)
+ return -EINVAL;
+ sel->clk_index = index;
+
+ return 0;
+ }
+
+ BUG_ON((u32)sel->clk_index >= sel->parent_count);
+ parent_sel = sel->parent_sel[sel->clk_index];
+
+ /* Clock needs to be enabled before changing the parent */
+ enabled = __is_clk_gate_enabled(ccu, gate);
+ if (!enabled && !__clk_gate(ccu, gate, true))
+ return -ENXIO;
+
+ /* Replace the selector value and record the result */
+ reg_val = __ccu_read(ccu, sel->offset);
+ reg_val = bitfield_replace(reg_val, sel->shift, sel->width, parent_sel);
+ __ccu_write(ccu, sel->offset, reg_val);
+
+ /* If the trigger fails we still want to disable the gate */
+ if (!__clk_trigger(ccu, trig))
+ ret = -EIO;
+
+ /* Disable the clock again if it was disabled to begin with */
+ if (!enabled && !__clk_gate(ccu, gate, false))
+ ret = ret ? ret : -ENXIO; /* return first error */
+
+ return ret;
+}
+
+/*
+ * Initialize a selector by committing our desired state to hardware
+ * without the usual checks to see if it's already set up that way.
+ * Returns true if successful, false otherwise.
+ */
+static bool sel_init(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_sel *sel, struct bcm_clk_trig *trig)
+{
+ if (!selector_exists(sel))
+ return true;
+ return !__sel_commit(ccu, gate, sel, trig);
+}
+
+/*
+ * Write a new value into a selector register to switch to a
+ * different parent clock. Returns 0 on success, or an error code
+ * (from __sel_commit()) otherwise.
+ */
+static int selector_write(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_sel *sel, struct bcm_clk_trig *trig,
+ u8 index)
+{
+ unsigned long flags;
+ u8 previous;
+ int ret;
+
+ previous = sel->clk_index;
+ if (previous == index)
+ return 0; /* No change */
+
+ sel->clk_index = index;
+
+ flags = ccu_lock(ccu);
+ __ccu_write_enable(ccu);
+
+ ret = __sel_commit(ccu, gate, sel, trig);
+
+ __ccu_write_disable(ccu);
+ ccu_unlock(ccu, flags);
+
+ if (ret)
+ sel->clk_index = previous; /* Revert the change */
+
+ return ret;
+}
+
+/* Clock operations */
+
+static int kona_peri_clk_enable(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->peri->gate;
+
+ return clk_gate(bcm_clk->ccu, bcm_clk->name, gate, true);
+}
+
+static void kona_peri_clk_disable(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->peri->gate;
+
+ (void)clk_gate(bcm_clk->ccu, bcm_clk->name, gate, false);
+}
+
+static int kona_peri_clk_is_enabled(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->peri->gate;
+
+ return is_clk_gate_enabled(bcm_clk->ccu, gate) ? 1 : 0;
+}
+
+static unsigned long kona_peri_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct peri_clk_data *data = bcm_clk->peri;
+
+ return clk_recalc_rate(bcm_clk->ccu, &data->div, &data->pre_div,
+ parent_rate);
+}
+
+static long kona_peri_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_div *div = &bcm_clk->peri->div;
+
+ if (!divider_exists(div))
+ return __clk_get_rate(hw->clk);
+
+ /* Quietly avoid a zero rate */
+ return round_rate(bcm_clk->ccu, div, &bcm_clk->peri->pre_div,
+ rate ? rate : 1, *parent_rate, NULL);
+}
+
+static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct peri_clk_data *data = bcm_clk->peri;
+ struct bcm_clk_sel *sel = &data->sel;
+ struct bcm_clk_trig *trig;
+ int ret;
+
+ BUG_ON(index >= sel->parent_count);
+
+ /* If there's only one parent we don't require a selector */
+ if (!selector_exists(sel))
+ return 0;
+
+ /*
+ * The regular trigger is used by default, but if there's a
+ * pre-trigger we want to use that instead.
+ */
+ trig = trigger_exists(&data->pre_trig) ? &data->pre_trig
+ : &data->trig;
+
+ ret = selector_write(bcm_clk->ccu, &data->gate, sel, trig, index);
+ if (ret == -ENXIO) {
+ pr_err("%s: gating failure for %s\n", __func__, bcm_clk->name);
+ ret = -EIO; /* Don't proliferate weird errors */
+ } else if (ret == -EIO) {
+ pr_err("%s: %strigger failed for %s\n", __func__,
+ trig == &data->pre_trig ? "pre-" : "",
+ bcm_clk->name);
+ }
+
+ return ret;
+}
+
+static u8 kona_peri_clk_get_parent(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct peri_clk_data *data = bcm_clk->peri;
+ u8 index;
+
+ index = selector_read_index(bcm_clk->ccu, &data->sel);
+
+ /* Not all callers would handle an out-of-range value gracefully */
+ return index == BAD_CLK_INDEX ? 0 : index;
+}
+
+static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct peri_clk_data *data = bcm_clk->peri;
+ struct bcm_clk_div *div = &data->div;
+ u64 scaled_div = 0;
+ int ret;
+
+ if (parent_rate > (unsigned long)LONG_MAX)
+ return -EINVAL;
+
+ if (rate == __clk_get_rate(hw->clk))
+ return 0;
+
+ if (!divider_exists(div))
+ return rate == parent_rate ? 0 : -EINVAL;
+
+ /*
+ * A fixed divider can't be changed. (Nor can a fixed
+ * pre-divider be, but for now we never actually try to
+ * change that.) Tolerate a request for a no-op change.
+ */
+ if (divider_is_fixed(&data->div))
+ return rate == parent_rate ? 0 : -EINVAL;
+
+ /*
+ * Get the scaled divisor value needed to achieve a clock
+ * rate as close as possible to what was requested, given
+ * the parent clock rate supplied.
+ */
+ (void)round_rate(bcm_clk->ccu, div, &data->pre_div,
+ rate ? rate : 1, parent_rate, &scaled_div);
+
+ /*
+ * We aren't updating any pre-divider at this point, so
+ * we'll use the regular trigger.
+ */
+ ret = divider_write(bcm_clk->ccu, &data->gate, &data->div,
+ &data->trig, scaled_div);
+ if (ret == -ENXIO) {
+ pr_err("%s: gating failure for %s\n", __func__, bcm_clk->name);
+ ret = -EIO; /* Don't proliferate weird errors */
+ } else if (ret == -EIO) {
+ pr_err("%s: trigger failed for %s\n", __func__, bcm_clk->name);
+ }
+
+ return ret;
+}
+
+struct clk_ops kona_peri_clk_ops = {
+ .enable = kona_peri_clk_enable,
+ .disable = kona_peri_clk_disable,
+ .is_enabled = kona_peri_clk_is_enabled,
+ .recalc_rate = kona_peri_clk_recalc_rate,
+ .round_rate = kona_peri_clk_round_rate,
+ .set_parent = kona_peri_clk_set_parent,
+ .get_parent = kona_peri_clk_get_parent,
+ .set_rate = kona_peri_clk_set_rate,
+};
+
+/* Put a peripheral clock into its initial state */
+static bool __peri_clk_init(struct kona_clk *bcm_clk)
+{
+ struct ccu_data *ccu = bcm_clk->ccu;
+ struct peri_clk_data *peri = bcm_clk->peri;
+ const char *name = bcm_clk->name;
+ struct bcm_clk_trig *trig;
+
+ BUG_ON(bcm_clk->type != bcm_clk_peri);
+
+ if (!gate_init(ccu, &peri->gate)) {
+ pr_err("%s: error initializing gate for %s\n", __func__, name);
+ return false;
+ }
+ if (!div_init(ccu, &peri->gate, &peri->div, &peri->trig)) {
+ pr_err("%s: error initializing divider for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ /*
+ * For the pre-divider and selector, the pre-trigger is used
+ * if it's present, otherwise we just use the regular trigger.
+ */
+ trig = trigger_exists(&peri->pre_trig) ? &peri->pre_trig
+ : &peri->trig;
+
+ if (!div_init(ccu, &peri->gate, &peri->pre_div, trig)) {
+ pr_err("%s: error initializing pre-divider for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ if (!sel_init(ccu, &peri->gate, &peri->sel, trig)) {
+ pr_err("%s: error initializing selector for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ return true;
+}
+
+static bool __kona_clk_init(struct kona_clk *bcm_clk)
+{
+ switch (bcm_clk->type) {
+ case bcm_clk_peri:
+ return __peri_clk_init(bcm_clk);
+ default:
+ BUG();
+ }
+ return -EINVAL;
+}
+
+/* Set a CCU and all its clocks into their desired initial state */
+bool __init kona_ccu_init(struct ccu_data *ccu)
+{
+ unsigned long flags;
+ unsigned int which;
+ struct clk **clks = ccu->data.clks;
+ bool success = true;
+
+ flags = ccu_lock(ccu);
+ __ccu_write_enable(ccu);
+
+ for (which = 0; which < ccu->data.clk_num; which++) {
+ struct kona_clk *bcm_clk;
+
+ if (!clks[which])
+ continue;
+ bcm_clk = to_kona_clk(__clk_get_hw(clks[which]));
+ success &= __kona_clk_init(bcm_clk);
+ }
+
+ __ccu_write_disable(ccu);
+ ccu_unlock(ccu, flags);
+ return success;
+}
diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h
new file mode 100644
index 000000000000..5e139adc3dc5
--- /dev/null
+++ b/drivers/clk/bcm/clk-kona.h
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ * Copyright 2013 Linaro 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CLK_KONA_H
+#define _CLK_KONA_H
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/clk-provider.h>
+
+#define BILLION 1000000000
+
+/* The common clock framework uses u8 to represent a parent index */
+#define PARENT_COUNT_MAX ((u32)U8_MAX)
+
+#define BAD_CLK_INDEX U8_MAX /* Can't ever be valid */
+#define BAD_CLK_NAME ((const char *)-1)
+
+#define BAD_SCALED_DIV_VALUE U64_MAX
+
+/*
+ * Utility macros for object flag management. If possible, flags
+ * should be defined such that 0 is the desired default value.
+ */
+#define FLAG(type, flag) BCM_CLK_ ## type ## _FLAGS_ ## flag
+#define FLAG_SET(obj, type, flag) ((obj)->flags |= FLAG(type, flag))
+#define FLAG_CLEAR(obj, type, flag) ((obj)->flags &= ~(FLAG(type, flag)))
+#define FLAG_FLIP(obj, type, flag) ((obj)->flags ^= FLAG(type, flag))
+#define FLAG_TEST(obj, type, flag) (!!((obj)->flags & FLAG(type, flag)))
+
+/* Clock field state tests */
+
+#define gate_exists(gate) FLAG_TEST(gate, GATE, EXISTS)
+#define gate_is_enabled(gate) FLAG_TEST(gate, GATE, ENABLED)
+#define gate_is_hw_controllable(gate) FLAG_TEST(gate, GATE, HW)
+#define gate_is_sw_controllable(gate) FLAG_TEST(gate, GATE, SW)
+#define gate_is_sw_managed(gate) FLAG_TEST(gate, GATE, SW_MANAGED)
+#define gate_is_no_disable(gate) FLAG_TEST(gate, GATE, NO_DISABLE)
+
+#define gate_flip_enabled(gate) FLAG_FLIP(gate, GATE, ENABLED)
+
+#define divider_exists(div) FLAG_TEST(div, DIV, EXISTS)
+#define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED)
+#define divider_has_fraction(div) (!divider_is_fixed(div) && \
+ (div)->frac_width > 0)
+
+#define selector_exists(sel) ((sel)->width != 0)
+#define trigger_exists(trig) FLAG_TEST(trig, TRIG, EXISTS)
+
+/* Clock type, used to tell common block what it's part of */
+enum bcm_clk_type {
+ bcm_clk_none, /* undefined clock type */
+ bcm_clk_bus,
+ bcm_clk_core,
+ bcm_clk_peri
+};
+
+/*
+ * Each CCU defines a mapped area of memory containing registers
+ * used to manage clocks implemented by the CCU. Access to memory
+ * within the CCU's space is serialized by a spinlock. Before any
+ * (other) address can be written, a special access "password" value
+ * must be written to its WR_ACCESS register (located at the base
+ * address of the range). We keep track of the name of each CCU as
+ * it is set up, and maintain them in a list.
+ */
+struct ccu_data {
+ void __iomem *base; /* base of mapped address space */
+ spinlock_t lock; /* serialization lock */
+ bool write_enabled; /* write access is currently enabled */
+ struct list_head links; /* for ccu_list */
+ struct device_node *node;
+ struct clk_onecell_data data;
+ const char *name;
+ u32 range; /* byte range of address space */
+};
+
+/*
+ * Gating control and status is managed by a 32-bit gate register.
+ *
+ * There are several types of gating available:
+ * - (no gate)
+ * A clock with no gate is assumed to be always enabled.
+ * - hardware-only gating (auto-gating)
+ * Enabling or disabling clocks with this type of gate is
+ * managed automatically by the hardware. Such clocks can be
+ * considered by the software to be enabled. The current status
+ * of auto-gated clocks can be read from the gate status bit.
+ * - software-only gating
+ * Auto-gating is not available for this type of clock.
+ * Instead, software manages whether it's enabled by setting or
+ * clearing the enable bit. The current gate status of a gate
+ * under software control can be read from the gate status bit.
+ * To ensure a change to the gating status is complete, the
+ * status bit can be polled to verify that the gate has entered
+ * the desired state.
+ * - selectable hardware or software gating
+ * Gating for this type of clock can be configured to be either
+ * under software or hardware control. Which type is in use is
+ * determined by the hw_sw_sel bit of the gate register.
+ */
+struct bcm_clk_gate {
+ u32 offset; /* gate register offset */
+ u32 status_bit; /* 0: gate is disabled; 0: gatge is enabled */
+ u32 en_bit; /* 0: disable; 1: enable */
+ u32 hw_sw_sel_bit; /* 0: hardware gating; 1: software gating */
+ u32 flags; /* BCM_CLK_GATE_FLAGS_* below */
+};
+
+/*
+ * Gate flags:
+ * HW means this gate can be auto-gated
+ * SW means the state of this gate can be software controlled
+ * NO_DISABLE means this gate is (only) enabled if under software control
+ * SW_MANAGED means the status of this gate is under software control
+ * ENABLED means this software-managed gate is *supposed* to be enabled
+ */
+#define BCM_CLK_GATE_FLAGS_EXISTS ((u32)1 << 0) /* Gate is valid */
+#define BCM_CLK_GATE_FLAGS_HW ((u32)1 << 1) /* Can auto-gate */
+#define BCM_CLK_GATE_FLAGS_SW ((u32)1 << 2) /* Software control */
+#define BCM_CLK_GATE_FLAGS_NO_DISABLE ((u32)1 << 3) /* HW or enabled */
+#define BCM_CLK_GATE_FLAGS_SW_MANAGED ((u32)1 << 4) /* SW now in control */
+#define BCM_CLK_GATE_FLAGS_ENABLED ((u32)1 << 5) /* If SW_MANAGED */
+
+/*
+ * Gate initialization macros.
+ *
+ * Any gate initially under software control will be enabled.
+ */
+
+/* A hardware/software gate initially under software control */
+#define HW_SW_GATE(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
+ { \
+ .offset = (_offset), \
+ .status_bit = (_status_bit), \
+ .en_bit = (_en_bit), \
+ .hw_sw_sel_bit = (_hw_sw_sel_bit), \
+ .flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
+ FLAG(GATE, SW_MANAGED)|FLAG(GATE, ENABLED)| \
+ FLAG(GATE, EXISTS), \
+ }
+
+/* A hardware/software gate initially under hardware control */
+#define HW_SW_GATE_AUTO(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
+ { \
+ .offset = (_offset), \
+ .status_bit = (_status_bit), \
+ .en_bit = (_en_bit), \
+ .hw_sw_sel_bit = (_hw_sw_sel_bit), \
+ .flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
+ FLAG(GATE, EXISTS), \
+ }
+
+/* A hardware-or-enabled gate (enabled if not under hardware control) */
+#define HW_ENABLE_GATE(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
+ { \
+ .offset = (_offset), \
+ .status_bit = (_status_bit), \
+ .en_bit = (_en_bit), \
+ .hw_sw_sel_bit = (_hw_sw_sel_bit), \
+ .flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
+ FLAG(GATE, NO_DISABLE)|FLAG(GATE, EXISTS), \
+ }
+
+/* A software-only gate */
+#define SW_ONLY_GATE(_offset, _status_bit, _en_bit) \
+ { \
+ .offset = (_offset), \
+ .status_bit = (_status_bit), \
+ .en_bit = (_en_bit), \
+ .flags = FLAG(GATE, SW)|FLAG(GATE, SW_MANAGED)| \
+ FLAG(GATE, ENABLED)|FLAG(GATE, EXISTS), \
+ }
+
+/* A hardware-only gate */
+#define HW_ONLY_GATE(_offset, _status_bit) \
+ { \
+ .offset = (_offset), \
+ .status_bit = (_status_bit), \
+ .flags = FLAG(GATE, HW)|FLAG(GATE, EXISTS), \
+ }
+
+/*
+ * Each clock can have zero, one, or two dividers which change the
+ * output rate of the clock. Each divider can be either fixed or
+ * variable. If there are two dividers, they are the "pre-divider"
+ * and the "regular" or "downstream" divider. If there is only one,
+ * there is no pre-divider.
+ *
+ * A fixed divider is any non-zero (positive) value, and it
+ * indicates how the input rate is affected by the divider.
+ *
+ * The value of a variable divider is maintained in a sub-field of a
+ * 32-bit divider register. The position of the field in the
+ * register is defined by its offset and width. The value recorded
+ * in this field is always 1 less than the value it represents.
+ *
+ * In addition, a variable divider can indicate that some subset
+ * of its bits represent a "fractional" part of the divider. Such
+ * bits comprise the low-order portion of the divider field, and can
+ * be viewed as representing the portion of the divider that lies to
+ * the right of the decimal point. Most variable dividers have zero
+ * fractional bits. Variable dividers with non-zero fraction width
+ * still record a value 1 less than the value they represent; the
+ * added 1 does *not* affect the low-order bit in this case, it
+ * affects the bits above the fractional part only. (Often in this
+ * code a divider field value is distinguished from the value it
+ * represents by referring to the latter as a "divisor".)
+ *
+ * In order to avoid dealing with fractions, divider arithmetic is
+ * performed using "scaled" values. A scaled value is one that's
+ * been left-shifted by the fractional width of a divider. Dividing
+ * a scaled value by a scaled divisor produces the desired quotient
+ * without loss of precision and without any other special handling
+ * for fractions.
+ *
+ * The recorded value of a variable divider can be modified. To
+ * modify either divider (or both), a clock must be enabled (i.e.,
+ * using its gate). In addition, a trigger register (described
+ * below) must be used to commit the change, and polled to verify
+ * the change is complete.
+ */
+struct bcm_clk_div {
+ union {
+ struct { /* variable divider */
+ u32 offset; /* divider register offset */
+ u32 shift; /* field shift */
+ u32 width; /* field width */
+ u32 frac_width; /* field fraction width */
+
+ u64 scaled_div; /* scaled divider value */
+ };
+ u32 fixed; /* non-zero fixed divider value */
+ };
+ u32 flags; /* BCM_CLK_DIV_FLAGS_* below */
+};
+
+/*
+ * Divider flags:
+ * EXISTS means this divider exists
+ * FIXED means it is a fixed-rate divider
+ */
+#define BCM_CLK_DIV_FLAGS_EXISTS ((u32)1 << 0) /* Divider is valid */
+#define BCM_CLK_DIV_FLAGS_FIXED ((u32)1 << 1) /* Fixed-value */
+
+/* Divider initialization macros */
+
+/* A fixed (non-zero) divider */
+#define FIXED_DIVIDER(_value) \
+ { \
+ .fixed = (_value), \
+ .flags = FLAG(DIV, EXISTS)|FLAG(DIV, FIXED), \
+ }
+
+/* A divider with an integral divisor */
+#define DIVIDER(_offset, _shift, _width) \
+ { \
+ .offset = (_offset), \
+ .shift = (_shift), \
+ .width = (_width), \
+ .scaled_div = BAD_SCALED_DIV_VALUE, \
+ .flags = FLAG(DIV, EXISTS), \
+ }
+
+/* A divider whose divisor has an integer and fractional part */
+#define FRAC_DIVIDER(_offset, _shift, _width, _frac_width) \
+ { \
+ .offset = (_offset), \
+ .shift = (_shift), \
+ .width = (_width), \
+ .frac_width = (_frac_width), \
+ .scaled_div = BAD_SCALED_DIV_VALUE, \
+ .flags = FLAG(DIV, EXISTS), \
+ }
+
+/*
+ * Clocks may have multiple "parent" clocks. If there is more than
+ * one, a selector must be specified to define which of the parent
+ * clocks is currently in use. The selected clock is indicated in a
+ * sub-field of a 32-bit selector register. The range of
+ * representable selector values typically exceeds the number of
+ * available parent clocks. Occasionally the reset value of a
+ * selector field is explicitly set to a (specific) value that does
+ * not correspond to a defined input clock.
+ *
+ * We register all known parent clocks with the common clock code
+ * using a packed array (i.e., no empty slots) of (parent) clock
+ * names, and refer to them later using indexes into that array.
+ * We maintain an array of selector values indexed by common clock
+ * index values in order to map between these common clock indexes
+ * and the selector values used by the hardware.
+ *
+ * Like dividers, a selector can be modified, but to do so a clock
+ * must be enabled, and a trigger must be used to commit the change.
+ */
+struct bcm_clk_sel {
+ u32 offset; /* selector register offset */
+ u32 shift; /* field shift */
+ u32 width; /* field width */
+
+ u32 parent_count; /* number of entries in parent_sel[] */
+ u32 *parent_sel; /* array of parent selector values */
+ u8 clk_index; /* current selected index in parent_sel[] */
+};
+
+/* Selector initialization macro */
+#define SELECTOR(_offset, _shift, _width) \
+ { \
+ .offset = (_offset), \
+ .shift = (_shift), \
+ .width = (_width), \
+ .clk_index = BAD_CLK_INDEX, \
+ }
+
+/*
+ * Making changes to a variable divider or a selector for a clock
+ * requires the use of a trigger. A trigger is defined by a single
+ * bit within a register. To signal a change, a 1 is written into
+ * that bit. To determine when the change has been completed, that
+ * trigger bit is polled; the read value will be 1 while the change
+ * is in progress, and 0 when it is complete.
+ *
+ * Occasionally a clock will have more than one trigger. In this
+ * case, the "pre-trigger" will be used when changing a clock's
+ * selector and/or its pre-divider.
+ */
+struct bcm_clk_trig {
+ u32 offset; /* trigger register offset */
+ u32 bit; /* trigger bit */
+ u32 flags; /* BCM_CLK_TRIG_FLAGS_* below */
+};
+
+/*
+ * Trigger flags:
+ * EXISTS means this trigger exists
+ */
+#define BCM_CLK_TRIG_FLAGS_EXISTS ((u32)1 << 0) /* Trigger is valid */
+
+/* Trigger initialization macro */
+#define TRIGGER(_offset, _bit) \
+ { \
+ .offset = (_offset), \
+ .bit = (_bit), \
+ .flags = FLAG(TRIG, EXISTS), \
+ }
+
+struct peri_clk_data {
+ struct bcm_clk_gate gate;
+ struct bcm_clk_trig pre_trig;
+ struct bcm_clk_div pre_div;
+ struct bcm_clk_trig trig;
+ struct bcm_clk_div div;
+ struct bcm_clk_sel sel;
+ const char *clocks[]; /* must be last; use CLOCKS() to declare */
+};
+#define CLOCKS(...) { __VA_ARGS__, NULL, }
+#define NO_CLOCKS { NULL, } /* Must use of no parent clocks */
+
+struct kona_clk {
+ struct clk_hw hw;
+ struct clk_init_data init_data;
+ const char *name; /* name of this clock */
+ struct ccu_data *ccu; /* ccu this clock is associated with */
+ enum bcm_clk_type type;
+ union {
+ void *data;
+ struct peri_clk_data *peri;
+ };
+};
+#define to_kona_clk(_hw) \
+ container_of(_hw, struct kona_clk, hw)
+
+/* Exported globals */
+
+extern struct clk_ops kona_peri_clk_ops;
+
+/* Help functions */
+
+#define PERI_CLK_SETUP(clks, ccu, id, name) \
+ clks[id] = kona_clk_setup(ccu, #name, bcm_clk_peri, &name ## _data)
+
+/* Externally visible functions */
+
+extern u64 do_div_round_closest(u64 dividend, unsigned long divisor);
+extern u64 scaled_div_max(struct bcm_clk_div *div);
+extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value,
+ u32 billionths);
+
+extern struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name,
+ enum bcm_clk_type type, void *data);
+extern void __init kona_dt_ccu_setup(struct device_node *node,
+ int (*ccu_clks_setup)(struct ccu_data *));
+extern bool __init kona_ccu_init(struct ccu_data *ccu);
+
+#endif /* _CLK_KONA_H */
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 8137327847c3..1127ee46b802 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -17,23 +17,75 @@
#include <linux/module.h>
#include <linux/err.h>
-#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04
-#define AXI_CLKGEN_REG_CLK_OUT1 0x08
-#define AXI_CLKGEN_REG_CLK_OUT2 0x0c
-#define AXI_CLKGEN_REG_CLK_DIV 0x10
-#define AXI_CLKGEN_REG_CLK_FB1 0x14
-#define AXI_CLKGEN_REG_CLK_FB2 0x18
-#define AXI_CLKGEN_REG_LOCK1 0x1c
-#define AXI_CLKGEN_REG_LOCK2 0x20
-#define AXI_CLKGEN_REG_LOCK3 0x24
-#define AXI_CLKGEN_REG_FILTER1 0x28
-#define AXI_CLKGEN_REG_FILTER2 0x2c
+#define AXI_CLKGEN_V1_REG_UPDATE_ENABLE 0x04
+#define AXI_CLKGEN_V1_REG_CLK_OUT1 0x08
+#define AXI_CLKGEN_V1_REG_CLK_OUT2 0x0c
+#define AXI_CLKGEN_V1_REG_CLK_DIV 0x10
+#define AXI_CLKGEN_V1_REG_CLK_FB1 0x14
+#define AXI_CLKGEN_V1_REG_CLK_FB2 0x18
+#define AXI_CLKGEN_V1_REG_LOCK1 0x1c
+#define AXI_CLKGEN_V1_REG_LOCK2 0x20
+#define AXI_CLKGEN_V1_REG_LOCK3 0x24
+#define AXI_CLKGEN_V1_REG_FILTER1 0x28
+#define AXI_CLKGEN_V1_REG_FILTER2 0x2c
+
+#define AXI_CLKGEN_V2_REG_RESET 0x40
+#define AXI_CLKGEN_V2_REG_DRP_CNTRL 0x70
+#define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74
+
+#define AXI_CLKGEN_V2_RESET_MMCM_ENABLE BIT(1)
+#define AXI_CLKGEN_V2_RESET_ENABLE BIT(0)
+
+#define AXI_CLKGEN_V2_DRP_CNTRL_SEL BIT(29)
+#define AXI_CLKGEN_V2_DRP_CNTRL_READ BIT(28)
+
+#define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16)
+
+#define MMCM_REG_CLKOUT0_1 0x08
+#define MMCM_REG_CLKOUT0_2 0x09
+#define MMCM_REG_CLK_FB1 0x14
+#define MMCM_REG_CLK_FB2 0x15
+#define MMCM_REG_CLK_DIV 0x16
+#define MMCM_REG_LOCK1 0x18
+#define MMCM_REG_LOCK2 0x19
+#define MMCM_REG_LOCK3 0x1a
+#define MMCM_REG_FILTER1 0x4e
+#define MMCM_REG_FILTER2 0x4f
+
+struct axi_clkgen;
+
+struct axi_clkgen_mmcm_ops {
+ void (*enable)(struct axi_clkgen *axi_clkgen, bool enable);
+ int (*write)(struct axi_clkgen *axi_clkgen, unsigned int reg,
+ unsigned int val, unsigned int mask);
+ int (*read)(struct axi_clkgen *axi_clkgen, unsigned int reg,
+ unsigned int *val);
+};
struct axi_clkgen {
void __iomem *base;
+ const struct axi_clkgen_mmcm_ops *mmcm_ops;
struct clk_hw clk_hw;
};
+static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen,
+ bool enable)
+{
+ axi_clkgen->mmcm_ops->enable(axi_clkgen, enable);
+}
+
+static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int val, unsigned int mask)
+{
+ return axi_clkgen->mmcm_ops->write(axi_clkgen, reg, val, mask);
+}
+
+static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int *val)
+{
+ return axi_clkgen->mmcm_ops->read(axi_clkgen, reg, val);
+}
+
static uint32_t axi_clkgen_lookup_filter(unsigned int m)
{
switch (m) {
@@ -156,6 +208,148 @@ static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
*val = readl(axi_clkgen->base + reg);
}
+static unsigned int axi_clkgen_v1_map_mmcm_reg(unsigned int reg)
+{
+ switch (reg) {
+ case MMCM_REG_CLKOUT0_1:
+ return AXI_CLKGEN_V1_REG_CLK_OUT1;
+ case MMCM_REG_CLKOUT0_2:
+ return AXI_CLKGEN_V1_REG_CLK_OUT2;
+ case MMCM_REG_CLK_FB1:
+ return AXI_CLKGEN_V1_REG_CLK_FB1;
+ case MMCM_REG_CLK_FB2:
+ return AXI_CLKGEN_V1_REG_CLK_FB2;
+ case MMCM_REG_CLK_DIV:
+ return AXI_CLKGEN_V1_REG_CLK_DIV;
+ case MMCM_REG_LOCK1:
+ return AXI_CLKGEN_V1_REG_LOCK1;
+ case MMCM_REG_LOCK2:
+ return AXI_CLKGEN_V1_REG_LOCK2;
+ case MMCM_REG_LOCK3:
+ return AXI_CLKGEN_V1_REG_LOCK3;
+ case MMCM_REG_FILTER1:
+ return AXI_CLKGEN_V1_REG_FILTER1;
+ case MMCM_REG_FILTER2:
+ return AXI_CLKGEN_V1_REG_FILTER2;
+ default:
+ return 0;
+ }
+}
+
+static int axi_clkgen_v1_mmcm_write(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int val, unsigned int mask)
+{
+ reg = axi_clkgen_v1_map_mmcm_reg(reg);
+ if (reg == 0)
+ return -EINVAL;
+
+ axi_clkgen_write(axi_clkgen, reg, val);
+
+ return 0;
+}
+
+static int axi_clkgen_v1_mmcm_read(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int *val)
+{
+ reg = axi_clkgen_v1_map_mmcm_reg(reg);
+ if (reg == 0)
+ return -EINVAL;
+
+ axi_clkgen_read(axi_clkgen, reg, val);
+
+ return 0;
+}
+
+static void axi_clkgen_v1_mmcm_enable(struct axi_clkgen *axi_clkgen,
+ bool enable)
+{
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V1_REG_UPDATE_ENABLE, enable);
+}
+
+static const struct axi_clkgen_mmcm_ops axi_clkgen_v1_mmcm_ops = {
+ .write = axi_clkgen_v1_mmcm_write,
+ .read = axi_clkgen_v1_mmcm_read,
+ .enable = axi_clkgen_v1_mmcm_enable,
+};
+
+static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
+{
+ unsigned int timeout = 10000;
+ unsigned int val;
+
+ do {
+ axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val);
+ } while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout);
+
+ if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY)
+ return -EIO;
+
+ return val & 0xffff;
+}
+
+static int axi_clkgen_v2_mmcm_read(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int *val)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = axi_clkgen_wait_non_busy(axi_clkgen);
+ if (ret < 0)
+ return ret;
+
+ reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ;
+ reg_val |= (reg << 16);
+
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
+
+ ret = axi_clkgen_wait_non_busy(axi_clkgen);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int val, unsigned int mask)
+{
+ unsigned int reg_val = 0;
+ int ret;
+
+ ret = axi_clkgen_wait_non_busy(axi_clkgen);
+ if (ret < 0)
+ return ret;
+
+ if (mask != 0xffff) {
+ axi_clkgen_v2_mmcm_read(axi_clkgen, reg, &reg_val);
+ reg_val &= ~mask;
+ }
+
+ reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask);
+
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
+
+ return 0;
+}
+
+static void axi_clkgen_v2_mmcm_enable(struct axi_clkgen *axi_clkgen,
+ bool enable)
+{
+ unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE;
+
+ if (enable)
+ val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE;
+
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val);
+}
+
+static const struct axi_clkgen_mmcm_ops axi_clkgen_v2_mmcm_ops = {
+ .write = axi_clkgen_v2_mmcm_write,
+ .read = axi_clkgen_v2_mmcm_read,
+ .enable = axi_clkgen_v2_mmcm_enable,
+};
+
static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
{
return container_of(clk_hw, struct axi_clkgen, clk_hw);
@@ -184,33 +378,29 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
filter = axi_clkgen_lookup_filter(m - 1);
lock = axi_clkgen_lookup_lock(m - 1);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0);
-
axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1,
- (high << 6) | low);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2,
- (edge << 7) | (nocount << 6));
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1,
+ (high << 6) | low, 0xefff);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2,
+ (edge << 7) | (nocount << 6), 0x03ff);
axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV,
- (edge << 13) | (nocount << 12) | (high << 6) | low);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
+ (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff);
axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1,
- (high << 6) | low);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2,
- (edge << 7) | (nocount << 6));
-
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2,
- (((lock >> 16) & 0x1f) << 10) | 0x1);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3,
- (((lock >> 24) & 0x1f) << 10) | 0x3e9);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter);
-
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1,
+ (high << 6) | low, 0xefff);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
+ (edge << 7) | (nocount << 6), 0x03ff);
+
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
+ (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3,
+ (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900);
return 0;
}
@@ -236,11 +426,11 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
unsigned int reg;
unsigned long long tmp;
- axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, &reg);
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, &reg);
dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
- axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, &reg);
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &reg);
d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
- axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, &reg);
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
if (d == 0 || dout == 0)
@@ -255,14 +445,45 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
return tmp;
}
+static int axi_clkgen_enable(struct clk_hw *clk_hw)
+{
+ struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
+
+ axi_clkgen_mmcm_enable(axi_clkgen, true);
+
+ return 0;
+}
+
+static void axi_clkgen_disable(struct clk_hw *clk_hw)
+{
+ struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
+
+ axi_clkgen_mmcm_enable(axi_clkgen, false);
+}
+
static const struct clk_ops axi_clkgen_ops = {
.recalc_rate = axi_clkgen_recalc_rate,
.round_rate = axi_clkgen_round_rate,
.set_rate = axi_clkgen_set_rate,
+ .enable = axi_clkgen_enable,
+ .disable = axi_clkgen_disable,
};
+static const struct of_device_id axi_clkgen_ids[] = {
+ {
+ .compatible = "adi,axi-clkgen-1.00.a",
+ .data = &axi_clkgen_v1_mmcm_ops
+ }, {
+ .compatible = "adi,axi-clkgen-2.00.a",
+ .data = &axi_clkgen_v2_mmcm_ops,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
+
static int axi_clkgen_probe(struct platform_device *pdev)
{
+ const struct of_device_id *id;
struct axi_clkgen *axi_clkgen;
struct clk_init_data init;
const char *parent_name;
@@ -270,10 +491,19 @@ static int axi_clkgen_probe(struct platform_device *pdev)
struct resource *mem;
struct clk *clk;
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ id = of_match_node(axi_clkgen_ids, pdev->dev.of_node);
+ if (!id)
+ return -ENODEV;
+
axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
if (!axi_clkgen)
return -ENOMEM;
+ axi_clkgen->mmcm_ops = id->data;
+
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(axi_clkgen->base))
@@ -289,10 +519,12 @@ static int axi_clkgen_probe(struct platform_device *pdev)
init.name = clk_name;
init.ops = &axi_clkgen_ops;
- init.flags = 0;
+ init.flags = CLK_SET_RATE_GATE;
init.parent_names = &parent_name;
init.num_parents = 1;
+ axi_clkgen_mmcm_enable(axi_clkgen, false);
+
axi_clkgen->clk_hw.init = &init;
clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
if (IS_ERR(clk))
@@ -309,12 +541,6 @@ static int axi_clkgen_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id axi_clkgen_ids[] = {
- { .compatible = "adi,axi-clkgen-1.00.a" },
- { },
-};
-MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
-
static struct platform_driver axi_clkgen_driver = {
.driver = {
.name = "adi-axi-clkgen",
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 5543b7df8e16..ec22112e569f 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -24,7 +24,7 @@
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
- * rate - rate is adjustable. clk->rate = parent->rate / divisor
+ * rate - rate is adjustable. clk->rate = DIV_ROUND_UP(parent->rate / divisor)
* parent - fixed parent. No clk_set_parent support
*/
@@ -115,7 +115,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
- return parent_rate / div;
+ return DIV_ROUND_UP(parent_rate, div);
}
/*
@@ -185,7 +185,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
}
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
MULT_ROUND_UP(rate, i));
- now = parent_rate / i;
+ now = DIV_ROUND_UP(parent_rate, i);
if (now <= rate && now > best) {
bestdiv = i;
best = now;
@@ -207,7 +207,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
int div;
div = clk_divider_bestdiv(hw, rate, prate);
- return *prate / div;
+ return DIV_ROUND_UP(*prate, div);
}
static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -218,7 +218,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long flags = 0;
u32 val;
- div = parent_rate / rate;
+ div = DIV_ROUND_UP(parent_rate, rate);
value = _get_val(divider, div);
if (value > div_mask(divider))
diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c
new file mode 100644
index 000000000000..30a3b6999e10
--- /dev/null
+++ b/drivers/clk/clk-moxart.c
@@ -0,0 +1,97 @@
+/*
+ * MOXA ART SoCs clock driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <jonas.jensen@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/clkdev.h>
+
+void __init moxart_of_pll_clk_init(struct device_node *node)
+{
+ static void __iomem *base;
+ struct clk *clk, *ref_clk;
+ unsigned int mul;
+ const char *name = node->name;
+ const char *parent_name;
+
+ of_property_read_string(node, "clock-output-names", &name);
+ parent_name = of_clk_get_parent_name(node, 0);
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s: of_iomap failed\n", node->full_name);
+ return;
+ }
+
+ mul = readl(base + 0x30) >> 3 & 0x3f;
+ iounmap(base);
+
+ ref_clk = of_clk_get(node, 0);
+ if (IS_ERR(ref_clk)) {
+ pr_err("%s: of_clk_get failed\n", node->full_name);
+ return;
+ }
+
+ clk = clk_register_fixed_factor(NULL, name, parent_name, 0, mul, 1);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock\n", node->full_name);
+ return;
+ }
+
+ clk_register_clkdev(clk, NULL, name);
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock",
+ moxart_of_pll_clk_init);
+
+void __init moxart_of_apb_clk_init(struct device_node *node)
+{
+ static void __iomem *base;
+ struct clk *clk, *pll_clk;
+ unsigned int div, val;
+ unsigned int div_idx[] = { 2, 3, 4, 6, 8};
+ const char *name = node->name;
+ const char *parent_name;
+
+ of_property_read_string(node, "clock-output-names", &name);
+ parent_name = of_clk_get_parent_name(node, 0);
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s: of_iomap failed\n", node->full_name);
+ return;
+ }
+
+ val = readl(base + 0xc) >> 4 & 0x7;
+ iounmap(base);
+
+ if (val > 4)
+ val = 0;
+ div = div_idx[val] * 2;
+
+ pll_clk = of_clk_get(node, 0);
+ if (IS_ERR(pll_clk)) {
+ pr_err("%s: of_clk_get failed\n", node->full_name);
+ return;
+ }
+
+ clk = clk_register_fixed_factor(NULL, name, parent_name, 0, 1, div);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock\n", node->full_name);
+ return;
+ }
+
+ clk_register_clkdev(clk, NULL, name);
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(moxart_apb_clock, "moxa,moxart-apb-clock",
+ moxart_of_apb_clk_init);
diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c
index c4f76ed914b0..8b284be4efa4 100644
--- a/drivers/clk/clk-ppc-corenet.c
+++ b/drivers/clk/clk-ppc-corenet.c
@@ -27,7 +27,6 @@ struct cmux_clk {
#define CLKSEL_ADJUST BIT(0)
#define to_cmux_clk(p) container_of(p, struct cmux_clk, hw)
-static void __iomem *base;
static unsigned int clocks_per_pll;
static int cmux_set_parent(struct clk_hw *hw, u8 idx)
@@ -100,7 +99,11 @@ static void __init core_mux_init(struct device_node *np)
pr_err("%s: could not allocate cmux_clk\n", __func__);
goto err_name;
}
- cmux_clk->reg = base + offset;
+ cmux_clk->reg = of_iomap(np, 0);
+ if (!cmux_clk->reg) {
+ pr_err("%s: could not map register\n", __func__);
+ goto err_clk;
+ }
node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen");
if (node && (offset >= 0x80))
@@ -143,38 +146,39 @@ err_name:
static void __init core_pll_init(struct device_node *np)
{
- u32 offset, mult;
+ u32 mult;
int i, rc, count;
const char *clk_name, *parent_name;
struct clk_onecell_data *onecell_data;
struct clk **subclks;
+ void __iomem *base;
- rc = of_property_read_u32(np, "reg", &offset);
- if (rc) {
- pr_err("%s: could not get reg property\n", np->name);
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("clk-ppc: iomap error\n");
return;
}
/* get the multiple of PLL */
- mult = ioread32be(base + offset);
+ mult = ioread32be(base);
/* check if this PLL is disabled */
if (mult & PLL_KILL) {
pr_debug("PLL:%s is disabled\n", np->name);
- return;
+ goto err_map;
}
mult = (mult >> 1) & 0x3f;
parent_name = of_clk_get_parent_name(np, 0);
if (!parent_name) {
pr_err("PLL: %s must have a parent\n", np->name);
- return;
+ goto err_map;
}
count = of_property_count_strings(np, "clock-output-names");
if (count < 0 || count > 4) {
pr_err("%s: clock is not supported\n", np->name);
- return;
+ goto err_map;
}
/* output clock number per PLL */
@@ -183,7 +187,7 @@ static void __init core_pll_init(struct device_node *np)
subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL);
if (!subclks) {
pr_err("%s: could not allocate subclks\n", __func__);
- return;
+ goto err_map;
}
onecell_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
@@ -230,30 +234,52 @@ static void __init core_pll_init(struct device_node *np)
goto err_cell;
}
+ iounmap(base);
return;
err_cell:
kfree(onecell_data);
err_clks:
kfree(subclks);
+err_map:
+ iounmap(base);
+}
+
+static void __init sysclk_init(struct device_node *node)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ struct device_node *np = of_get_parent(node);
+ u32 rate;
+
+ if (!np) {
+ pr_err("ppc-clk: could not get parent node\n");
+ return;
+ }
+
+ if (of_property_read_u32(np, "clock-frequency", &rate)) {
+ of_node_put(node);
+ return;
+ }
+
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, rate);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
}
static const struct of_device_id clk_match[] __initconst = {
- { .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
- { .compatible = "fsl,core-pll-clock", .data = core_pll_init, },
- { .compatible = "fsl,core-mux-clock", .data = core_mux_init, },
+ { .compatible = "fsl,qoriq-sysclk-1.0", .data = sysclk_init, },
+ { .compatible = "fsl,qoriq-sysclk-2.0", .data = sysclk_init, },
+ { .compatible = "fsl,qoriq-core-pll-1.0", .data = core_pll_init, },
+ { .compatible = "fsl,qoriq-core-pll-2.0", .data = core_pll_init, },
+ { .compatible = "fsl,qoriq-core-mux-1.0", .data = core_mux_init, },
+ { .compatible = "fsl,qoriq-core-mux-2.0", .data = core_mux_init, },
{}
};
static int __init ppc_corenet_clk_probe(struct platform_device *pdev)
{
- struct device_node *np;
-
- np = pdev->dev.of_node;
- base = of_iomap(np, 0);
- if (!base) {
- dev_err(&pdev->dev, "iomap error\n");
- return -ENOMEM;
- }
of_clk_init(clk_match);
return 0;
diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c
index 00a3abe103a5..f2f62a1bf61a 100644
--- a/drivers/clk/clk-s2mps11.c
+++ b/drivers/clk/clk-s2mps11.c
@@ -27,6 +27,7 @@
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s5m8767.h>
#include <linux/mfd/samsung/core.h>
#define s2mps11_name(a) (a->hw.init->name)
@@ -48,6 +49,7 @@ struct s2mps11_clk {
struct clk_lookup *lookup;
u32 mask;
bool enabled;
+ unsigned int reg;
};
static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw)
@@ -61,7 +63,7 @@ static int s2mps11_clk_prepare(struct clk_hw *hw)
int ret;
ret = regmap_update_bits(s2mps11->iodev->regmap_pmic,
- S2MPS11_REG_RTC_CTRL,
+ s2mps11->reg,
s2mps11->mask, s2mps11->mask);
if (!ret)
s2mps11->enabled = true;
@@ -74,7 +76,7 @@ static void s2mps11_clk_unprepare(struct clk_hw *hw)
struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
int ret;
- ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, S2MPS11_REG_RTC_CTRL,
+ ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg,
s2mps11->mask, ~s2mps11->mask);
if (!ret)
@@ -130,9 +132,9 @@ static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev)
int i;
if (!iodev->dev->of_node)
- return NULL;
+ return ERR_PTR(-EINVAL);
- clk_np = of_find_node_by_name(iodev->dev->of_node, "clocks");
+ clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks");
if (!clk_np) {
dev_err(&pdev->dev, "could not find clock sub-node\n");
return ERR_PTR(-EINVAL);
@@ -155,6 +157,7 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct s2mps11_clk *s2mps11_clks, *s2mps11_clk;
struct device_node *clk_np = NULL;
+ unsigned int s2mps11_reg;
int i, ret = 0;
u32 val;
@@ -169,13 +172,26 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
if (IS_ERR(clk_np))
return PTR_ERR(clk_np);
+ switch(platform_get_device_id(pdev)->driver_data) {
+ case S2MPS11X:
+ s2mps11_reg = S2MPS11_REG_RTC_CTRL;
+ break;
+ case S5M8767X:
+ s2mps11_reg = S5M8767_REG_CTRL1;
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid device type\n");
+ return -EINVAL;
+ };
+
for (i = 0; i < S2MPS11_CLKS_NUM; i++, s2mps11_clk++) {
s2mps11_clk->iodev = iodev;
s2mps11_clk->hw.init = &s2mps11_clks_init[i];
s2mps11_clk->mask = 1 << i;
+ s2mps11_clk->reg = s2mps11_reg;
ret = regmap_read(s2mps11_clk->iodev->regmap_pmic,
- S2MPS11_REG_RTC_CTRL, &val);
+ s2mps11_clk->reg, &val);
if (ret < 0)
goto err_reg;
@@ -241,7 +257,8 @@ static int s2mps11_clk_remove(struct platform_device *pdev)
}
static const struct platform_device_id s2mps11_clk_id[] = {
- { "s2mps11-clk", 0},
+ { "s2mps11-clk", S2MPS11X},
+ { "s5m8767-clk", S5M8767X},
{ },
};
MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index c42e608af6bb..dff0373f53c1 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -277,6 +277,10 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
if (!d)
goto err_out;
+ if (clk->ops->debug_init)
+ if (clk->ops->debug_init(clk->hw, clk->dentry))
+ goto err_out;
+
ret = 0;
goto out;
@@ -1339,8 +1343,11 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate)
if (clk->notifier_count)
ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate);
- if (ret & NOTIFY_STOP_MASK)
+ if (ret & NOTIFY_STOP_MASK) {
+ pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n",
+ __func__, clk->name, ret);
goto out;
+ }
hlist_for_each_entry(child, &clk->children, child_node) {
ret = __clk_speculate_rates(child, new_rate);
@@ -1588,7 +1595,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
/* notify that we are about to change rates */
fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
if (fail_clk) {
- pr_warn("%s: failed to set %s rate\n", __func__,
+ pr_debug("%s: failed to set %s rate\n", __func__,
fail_clk->name);
clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
ret = -EBUSY;
@@ -2260,20 +2267,11 @@ void __clk_put(struct clk *clk)
* re-enter into the clk framework by calling any top-level clk APIs;
* this will cause a nested prepare_lock mutex.
*
- * Pre-change notifier callbacks will be passed the current, pre-change
- * rate of the clk via struct clk_notifier_data.old_rate. The new,
- * post-change rate of the clk is passed via struct
- * clk_notifier_data.new_rate.
- *
- * Post-change notifiers will pass the now-current, post-change rate of
- * the clk in both struct clk_notifier_data.old_rate and struct
+ * In all notification cases cases (pre, post and abort rate change) the
+ * original clock rate is passed to the callback via struct
+ * clk_notifier_data.old_rate and the new frequency is passed via struct
* clk_notifier_data.new_rate.
*
- * Abort-change notifiers are effectively the opposite of pre-change
- * notifiers: the original pre-change clk rate is passed in via struct
- * clk_notifier_data.new_rate and the failed post-change rate is passed
- * in via struct clk_notifier_data.old_rate.
- *
* clk_notifier_register() must be called from non-atomic context.
* Returns -EINVAL if called with null arguments, -ENOMEM upon
* allocation failure; otherwise, passes along the return value of
@@ -2473,7 +2471,7 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider);
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec)
{
struct of_clk_provider *provider;
- struct clk *clk = ERR_PTR(-ENOENT);
+ struct clk *clk = ERR_PTR(-EPROBE_DEFER);
/* Check if we have such a provider in our array */
list_for_each_entry(provider, &of_clk_providers, link) {
@@ -2506,8 +2504,12 @@ EXPORT_SYMBOL_GPL(of_clk_get_parent_count);
const char *of_clk_get_parent_name(struct device_node *np, int index)
{
struct of_phandle_args clkspec;
+ struct property *prop;
const char *clk_name;
+ const __be32 *vp;
+ u32 pv;
int rc;
+ int count;
if (index < 0)
return NULL;
@@ -2517,8 +2519,22 @@ const char *of_clk_get_parent_name(struct device_node *np, int index)
if (rc)
return NULL;
+ index = clkspec.args_count ? clkspec.args[0] : 0;
+ count = 0;
+
+ /* if there is an indices property, use it to transfer the index
+ * specified into an array offset for the clock-output-names property.
+ */
+ of_property_for_each_u32(clkspec.np, "clock-indices", prop, vp, pv) {
+ if (index == pv) {
+ index = count;
+ break;
+ }
+ count++;
+ }
+
if (of_property_read_string_index(clkspec.np, "clock-output-names",
- clkspec.args_count ? clkspec.args[0] : 0,
+ index,
&clk_name) < 0)
clk_name = clkspec.np->name;
@@ -2527,24 +2543,99 @@ const char *of_clk_get_parent_name(struct device_node *np, int index)
}
EXPORT_SYMBOL_GPL(of_clk_get_parent_name);
+struct clock_provider {
+ of_clk_init_cb_t clk_init_cb;
+ struct device_node *np;
+ struct list_head node;
+};
+
+static LIST_HEAD(clk_provider_list);
+
+/*
+ * This function looks for a parent clock. If there is one, then it
+ * checks that the provider for this parent clock was initialized, in
+ * this case the parent clock will be ready.
+ */
+static int parent_ready(struct device_node *np)
+{
+ int i = 0;
+
+ while (true) {
+ struct clk *clk = of_clk_get(np, i);
+
+ /* this parent is ready we can check the next one */
+ if (!IS_ERR(clk)) {
+ clk_put(clk);
+ i++;
+ continue;
+ }
+
+ /* at least one parent is not ready, we exit now */
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return 0;
+
+ /*
+ * Here we make assumption that the device tree is
+ * written correctly. So an error means that there is
+ * no more parent. As we didn't exit yet, then the
+ * previous parent are ready. If there is no clock
+ * parent, no need to wait for them, then we can
+ * consider their absence as being ready
+ */
+ return 1;
+ }
+}
+
/**
* of_clk_init() - Scan and init clock providers from the DT
* @matches: array of compatible values and init functions for providers.
*
- * This function scans the device tree for matching clock providers and
- * calls their initialization functions
+ * This function scans the device tree for matching clock providers
+ * and calls their initialization functions. It also does it by trying
+ * to follow the dependencies.
*/
void __init of_clk_init(const struct of_device_id *matches)
{
const struct of_device_id *match;
struct device_node *np;
+ struct clock_provider *clk_provider, *next;
+ bool is_init_done;
+ bool force = false;
if (!matches)
matches = &__clk_of_table;
+ /* First prepare the list of the clocks providers */
for_each_matching_node_and_match(np, matches, &match) {
- of_clk_init_cb_t clk_init_cb = match->data;
- clk_init_cb(np);
+ struct clock_provider *parent =
+ kzalloc(sizeof(struct clock_provider), GFP_KERNEL);
+
+ parent->clk_init_cb = match->data;
+ parent->np = np;
+ list_add_tail(&parent->node, &clk_provider_list);
+ }
+
+ while (!list_empty(&clk_provider_list)) {
+ is_init_done = false;
+ list_for_each_entry_safe(clk_provider, next,
+ &clk_provider_list, node) {
+ if (force || parent_ready(clk_provider->np)) {
+ clk_provider->clk_init_cb(clk_provider->np);
+ list_del(&clk_provider->node);
+ kfree(clk_provider);
+ is_init_done = true;
+ }
+ }
+
+ /*
+ * We didn't manage to initialize any of the
+ * remaining providers during the last loop, so now we
+ * initialize all the remaining ones unconditionally
+ * in case the clock parent was not mandatory
+ */
+ if (!is_init_done)
+ force = true;
+
}
}
#endif
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 48f67218247c..a360b2eca5cb 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -167,6 +167,8 @@ struct clk *clk_get(struct device *dev, const char *con_id)
clk = of_clk_get_by_name(dev->of_node, con_id);
if (!IS_ERR(clk))
return clk;
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return clk;
}
return clk_get_sys(dev_id, con_id);
diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile
index a049108341fc..40b33c6a8257 100644
--- a/drivers/clk/hisilicon/Makefile
+++ b/drivers/clk/hisilicon/Makefile
@@ -2,4 +2,7 @@
# Hisilicon Clock specific Makefile
#
-obj-y += clk.o clkgate-separated.o clk-hi3620.o
+obj-y += clk.o clkgate-separated.o
+
+obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o
+obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o
diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c
index f24ad6a3a797..339945d2503b 100644
--- a/drivers/clk/hisilicon/clk-hi3620.c
+++ b/drivers/clk/hisilicon/clk-hi3620.c
@@ -210,33 +210,297 @@ static struct hisi_gate_clock hi3620_seperated_gate_clks[] __initdata = {
static void __init hi3620_clk_init(struct device_node *np)
{
- void __iomem *base;
+ struct hisi_clock_data *clk_data;
- if (np) {
- base = of_iomap(np, 0);
- if (!base) {
- pr_err("failed to map Hi3620 clock registers\n");
- return;
- }
- } else {
- pr_err("failed to find Hi3620 clock node in DTS\n");
+ clk_data = hisi_clk_init(np, HI3620_NR_CLKS);
+ if (!clk_data)
return;
- }
-
- hisi_clk_init(np, HI3620_NR_CLKS);
hisi_clk_register_fixed_rate(hi3620_fixed_rate_clks,
ARRAY_SIZE(hi3620_fixed_rate_clks),
- base);
+ clk_data);
hisi_clk_register_fixed_factor(hi3620_fixed_factor_clks,
ARRAY_SIZE(hi3620_fixed_factor_clks),
- base);
+ clk_data);
hisi_clk_register_mux(hi3620_mux_clks, ARRAY_SIZE(hi3620_mux_clks),
- base);
+ clk_data);
hisi_clk_register_divider(hi3620_div_clks, ARRAY_SIZE(hi3620_div_clks),
- base);
+ clk_data);
hisi_clk_register_gate_sep(hi3620_seperated_gate_clks,
ARRAY_SIZE(hi3620_seperated_gate_clks),
- base);
+ clk_data);
}
CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init);
+
+struct hisi_mmc_clock {
+ unsigned int id;
+ const char *name;
+ const char *parent_name;
+ unsigned long flags;
+ u32 clken_reg;
+ u32 clken_bit;
+ u32 div_reg;
+ u32 div_off;
+ u32 div_bits;
+ u32 drv_reg;
+ u32 drv_off;
+ u32 drv_bits;
+ u32 sam_reg;
+ u32 sam_off;
+ u32 sam_bits;
+};
+
+struct clk_mmc {
+ struct clk_hw hw;
+ u32 id;
+ void __iomem *clken_reg;
+ u32 clken_bit;
+ void __iomem *div_reg;
+ u32 div_off;
+ u32 div_bits;
+ void __iomem *drv_reg;
+ u32 drv_off;
+ u32 drv_bits;
+ void __iomem *sam_reg;
+ u32 sam_off;
+ u32 sam_bits;
+};
+
+#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw)
+
+static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = {
+ { HI3620_SD_CIUCLK, "sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4, 4, 0x1f8, 8, 4},
+ { HI3620_MMC_CIUCLK1, "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3, 0x1f8, 16, 4, 0x1f8, 20, 4},
+ { HI3620_MMC_CIUCLK2, "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3, 0x1f8, 28, 4, 0x1fc, 0, 4},
+ { HI3620_MMC_CIUCLK3, "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3, 0x1fc, 8, 4, 0x1fc, 12, 4},
+};
+
+static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ switch (parent_rate) {
+ case 26000000:
+ return 13000000;
+ case 180000000:
+ return 25000000;
+ case 360000000:
+ return 50000000;
+ case 720000000:
+ return 100000000;
+ case 1440000000:
+ return 180000000;
+ default:
+ return parent_rate;
+ }
+}
+
+static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate,
+ struct clk **best_parent_p)
+{
+ struct clk_mmc *mclk = to_mmc(hw);
+ unsigned long best = 0;
+
+ if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) {
+ rate = 13000000;
+ best = 26000000;
+ } else if (rate <= 26000000) {
+ rate = 25000000;
+ best = 180000000;
+ } else if (rate <= 52000000) {
+ rate = 50000000;
+ best = 360000000;
+ } else if (rate <= 100000000) {
+ rate = 100000000;
+ best = 720000000;
+ } else {
+ /* max is 180M */
+ rate = 180000000;
+ best = 1440000000;
+ }
+ *best_parent_rate = best;
+ return rate;
+}
+
+static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len)
+{
+ u32 i;
+
+ for (i = 0; i < len; i++) {
+ if (para % 2)
+ val |= 1 << (off + i);
+ else
+ val &= ~(1 << (off + i));
+ para = para >> 1;
+ }
+
+ return val;
+}
+
+static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate)
+{
+ struct clk_mmc *mclk = to_mmc(hw);
+ unsigned long flags;
+ u32 sam, drv, div, val;
+ static DEFINE_SPINLOCK(mmc_clk_lock);
+
+ switch (rate) {
+ case 13000000:
+ sam = 3;
+ drv = 1;
+ div = 1;
+ break;
+ case 25000000:
+ sam = 13;
+ drv = 6;
+ div = 6;
+ break;
+ case 50000000:
+ sam = 3;
+ drv = 6;
+ div = 6;
+ break;
+ case 100000000:
+ sam = 6;
+ drv = 4;
+ div = 6;
+ break;
+ case 180000000:
+ sam = 6;
+ drv = 4;
+ div = 7;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&mmc_clk_lock, flags);
+
+ val = readl_relaxed(mclk->clken_reg);
+ val &= ~(1 << mclk->clken_bit);
+ writel_relaxed(val, mclk->clken_reg);
+
+ val = readl_relaxed(mclk->sam_reg);
+ val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits);
+ writel_relaxed(val, mclk->sam_reg);
+
+ val = readl_relaxed(mclk->drv_reg);
+ val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits);
+ writel_relaxed(val, mclk->drv_reg);
+
+ val = readl_relaxed(mclk->div_reg);
+ val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits);
+ writel_relaxed(val, mclk->div_reg);
+
+ val = readl_relaxed(mclk->clken_reg);
+ val |= 1 << mclk->clken_bit;
+ writel_relaxed(val, mclk->clken_reg);
+
+ spin_unlock_irqrestore(&mmc_clk_lock, flags);
+
+ return 0;
+}
+
+static int mmc_clk_prepare(struct clk_hw *hw)
+{
+ struct clk_mmc *mclk = to_mmc(hw);
+ unsigned long rate;
+
+ if (mclk->id == HI3620_MMC_CIUCLK1)
+ rate = 13000000;
+ else
+ rate = 25000000;
+
+ return mmc_clk_set_timing(hw, rate);
+}
+
+static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ return mmc_clk_set_timing(hw, rate);
+}
+
+static struct clk_ops clk_mmc_ops = {
+ .prepare = mmc_clk_prepare,
+ .determine_rate = mmc_clk_determine_rate,
+ .set_rate = mmc_clk_set_rate,
+ .recalc_rate = mmc_clk_recalc_rate,
+};
+
+static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk,
+ void __iomem *base, struct device_node *np)
+{
+ struct clk_mmc *mclk;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ mclk = kzalloc(sizeof(*mclk), GFP_KERNEL);
+ if (!mclk) {
+ pr_err("%s: fail to allocate mmc clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = mmc_clk->name;
+ init.ops = &clk_mmc_ops;
+ init.flags = mmc_clk->flags | CLK_IS_BASIC;
+ init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL);
+ init.num_parents = (mmc_clk->parent_name ? 1 : 0);
+ mclk->hw.init = &init;
+
+ mclk->id = mmc_clk->id;
+ mclk->clken_reg = base + mmc_clk->clken_reg;
+ mclk->clken_bit = mmc_clk->clken_bit;
+ mclk->div_reg = base + mmc_clk->div_reg;
+ mclk->div_off = mmc_clk->div_off;
+ mclk->div_bits = mmc_clk->div_bits;
+ mclk->drv_reg = base + mmc_clk->drv_reg;
+ mclk->drv_off = mmc_clk->drv_off;
+ mclk->drv_bits = mmc_clk->drv_bits;
+ mclk->sam_reg = base + mmc_clk->sam_reg;
+ mclk->sam_off = mmc_clk->sam_off;
+ mclk->sam_bits = mmc_clk->sam_bits;
+
+ clk = clk_register(NULL, &mclk->hw);
+ if (WARN_ON(IS_ERR(clk)))
+ kfree(mclk);
+ return clk;
+}
+
+static void __init hi3620_mmc_clk_init(struct device_node *node)
+{
+ void __iomem *base;
+ int i, num = ARRAY_SIZE(hi3620_mmc_clks);
+ struct clk_onecell_data *clk_data;
+
+ if (!node) {
+ pr_err("failed to find pctrl node in DTS\n");
+ return;
+ }
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("failed to map pctrl\n");
+ return;
+ }
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (WARN_ON(!clk_data))
+ return;
+
+ clk_data->clks = kzalloc(sizeof(struct clk *) * num, GFP_KERNEL);
+ if (!clk_data->clks) {
+ pr_err("%s: fail to allocate mmc clk\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < num; i++) {
+ struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i];
+ clk_data->clks[mmc_clk->id] =
+ hisi_register_clk_mmc(mmc_clk, base, node);
+ }
+
+ clk_data->clk_num = num;
+ of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+
+CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init);
diff --git a/drivers/clk/hisilicon/clk-hip04.c b/drivers/clk/hisilicon/clk-hip04.c
new file mode 100644
index 000000000000..132b57a0ce09
--- /dev/null
+++ b/drivers/clk/hisilicon/clk-hip04.c
@@ -0,0 +1,58 @@
+/*
+ * Hisilicon HiP04 clock driver
+ *
+ * Copyright (c) 2013-2014 Hisilicon Limited.
+ * Copyright (c) 2013-2014 Linaro Limited.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+
+#include <dt-bindings/clock/hip04-clock.h>
+
+#include "clk.h"
+
+/* fixed rate clocks */
+static struct hisi_fixed_rate_clock hip04_fixed_rate_clks[] __initdata = {
+ { HIP04_OSC50M, "osc50m", NULL, CLK_IS_ROOT, 50000000, },
+ { HIP04_CLK_50M, "clk50m", NULL, CLK_IS_ROOT, 50000000, },
+ { HIP04_CLK_168M, "clk168m", NULL, CLK_IS_ROOT, 168750000, },
+};
+
+static void __init hip04_clk_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+
+ clk_data = hisi_clk_init(np, HIP04_NR_CLKS);
+ if (!clk_data)
+ return;
+
+ hisi_clk_register_fixed_rate(hip04_fixed_rate_clks,
+ ARRAY_SIZE(hip04_fixed_rate_clks),
+ clk_data);
+}
+CLK_OF_DECLARE(hip04_clk, "hisilicon,hip04-clock", hip04_clk_init);
diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c
index a3a7152c92d9..276f672e7b1a 100644
--- a/drivers/clk/hisilicon/clk.c
+++ b/drivers/clk/hisilicon/clk.c
@@ -37,23 +37,49 @@
#include "clk.h"
static DEFINE_SPINLOCK(hisi_clk_lock);
-static struct clk **clk_table;
-static struct clk_onecell_data clk_data;
-void __init hisi_clk_init(struct device_node *np, int nr_clks)
+struct hisi_clock_data __init *hisi_clk_init(struct device_node *np,
+ int nr_clks)
{
+ struct hisi_clock_data *clk_data;
+ struct clk **clk_table;
+ void __iomem *base;
+
+ if (np) {
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("failed to map Hisilicon clock registers\n");
+ goto err;
+ }
+ } else {
+ pr_err("failed to find Hisilicon clock node in DTS\n");
+ goto err;
+ }
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data) {
+ pr_err("%s: could not allocate clock data\n", __func__);
+ goto err;
+ }
+ clk_data->base = base;
+
clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
if (!clk_table) {
pr_err("%s: could not allocate clock lookup table\n", __func__);
- return;
+ goto err_data;
}
- clk_data.clks = clk_table;
- clk_data.clk_num = nr_clks;
- of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+ clk_data->clk_data.clks = clk_table;
+ clk_data->clk_data.clk_num = nr_clks;
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data->clk_data);
+ return clk_data;
+err_data:
+ kfree(clk_data);
+err:
+ return NULL;
}
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks,
- int nums, void __iomem *base)
+ int nums, struct hisi_clock_data *data)
{
struct clk *clk;
int i;
@@ -68,11 +94,13 @@ void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks,
__func__, clks[i].name);
continue;
}
+ data->clk_data.clks[clks[i].id] = clk;
}
}
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks,
- int nums, void __iomem *base)
+ int nums,
+ struct hisi_clock_data *data)
{
struct clk *clk;
int i;
@@ -87,13 +115,15 @@ void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks,
__func__, clks[i].name);
continue;
}
+ data->clk_data.clks[clks[i].id] = clk;
}
}
void __init hisi_clk_register_mux(struct hisi_mux_clock *clks,
- int nums, void __iomem *base)
+ int nums, struct hisi_clock_data *data)
{
struct clk *clk;
+ void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
@@ -111,14 +141,15 @@ void __init hisi_clk_register_mux(struct hisi_mux_clock *clks,
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
- clk_table[clks[i].id] = clk;
+ data->clk_data.clks[clks[i].id] = clk;
}
}
void __init hisi_clk_register_divider(struct hisi_divider_clock *clks,
- int nums, void __iomem *base)
+ int nums, struct hisi_clock_data *data)
{
struct clk *clk;
+ void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
@@ -139,14 +170,15 @@ void __init hisi_clk_register_divider(struct hisi_divider_clock *clks,
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
- clk_table[clks[i].id] = clk;
+ data->clk_data.clks[clks[i].id] = clk;
}
}
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks,
- int nums, void __iomem *base)
+ int nums, struct hisi_clock_data *data)
{
struct clk *clk;
+ void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
@@ -166,6 +198,6 @@ void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks,
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
- clk_table[clks[i].id] = clk;
+ data->clk_data.clks[clks[i].id] = clk;
}
}
diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h
index 4a6beebefb7a..43fa5da88f02 100644
--- a/drivers/clk/hisilicon/clk.h
+++ b/drivers/clk/hisilicon/clk.h
@@ -30,6 +30,11 @@
#include <linux/io.h>
#include <linux/spinlock.h>
+struct hisi_clock_data {
+ struct clk_onecell_data clk_data;
+ void __iomem *base;
+};
+
struct hisi_fixed_rate_clock {
unsigned int id;
char *name;
@@ -89,15 +94,15 @@ struct clk *hisi_register_clkgate_sep(struct device *, const char *,
void __iomem *, u8,
u8, spinlock_t *);
-void __init hisi_clk_init(struct device_node *, int);
+struct hisi_clock_data __init *hisi_clk_init(struct device_node *, int);
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *,
- int, void __iomem *);
+ int, struct hisi_clock_data *);
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *,
- int, void __iomem *);
+ int, struct hisi_clock_data *);
void __init hisi_clk_register_mux(struct hisi_mux_clock *, int,
- void __iomem *);
+ struct hisi_clock_data *);
void __init hisi_clk_register_divider(struct hisi_divider_clock *,
- int, void __iomem *);
+ int, struct hisi_clock_data *);
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *,
- int, void __iomem *);
+ int, struct hisi_clock_data *);
#endif /* __HISI_CLK_H */
diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c
index 80c1dd15d15c..23a56f561812 100644
--- a/drivers/clk/mmp/clk-frac.c
+++ b/drivers/clk/mmp/clk-frac.c
@@ -40,15 +40,19 @@ static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate,
for (i = 0; i < factor->ftbl_cnt; i++) {
prev_rate = rate;
- rate = (((*prate / 10000) * factor->ftbl[i].num) /
- (factor->ftbl[i].den * factor->masks->factor)) * 10000;
+ rate = (((*prate / 10000) * factor->ftbl[i].den) /
+ (factor->ftbl[i].num * factor->masks->factor)) * 10000;
if (rate > drate)
break;
}
- if (i == 0)
+ if ((i == 0) || (i == factor->ftbl_cnt)) {
return rate;
- else
- return prev_rate;
+ } else {
+ if ((drate - prev_rate) > (rate - drate))
+ return rate;
+ else
+ return prev_rate;
+ }
}
static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
@@ -64,7 +68,7 @@ static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
num = (val >> masks->num_shift) & masks->num_mask;
/* calculate denominator */
- den = (val >> masks->den_shift) & masks->num_mask;
+ den = (val >> masks->den_shift) & masks->den_mask;
if (!den)
return 0;
@@ -85,8 +89,8 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate,
for (i = 0; i < factor->ftbl_cnt; i++) {
prev_rate = rate;
- rate = (((prate / 10000) * factor->ftbl[i].num) /
- (factor->ftbl[i].den * factor->masks->factor)) * 10000;
+ rate = (((prate / 10000) * factor->ftbl[i].den) /
+ (factor->ftbl[i].num * factor->masks->factor)) * 10000;
if (rate > drate)
break;
}
diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig
index c339b829d3e3..693f7be129f1 100644
--- a/drivers/clk/mvebu/Kconfig
+++ b/drivers/clk/mvebu/Kconfig
@@ -13,6 +13,14 @@ config ARMADA_370_CLK
select MVEBU_CLK_CPU
select MVEBU_CLK_COREDIV
+config ARMADA_375_CLK
+ bool
+ select MVEBU_CLK_COMMON
+
+config ARMADA_38X_CLK
+ bool
+ select MVEBU_CLK_COMMON
+
config ARMADA_XP_CLK
bool
select MVEBU_CLK_COMMON
diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile
index 21bbfb4a9f42..4c66162fb0b4 100644
--- a/drivers/clk/mvebu/Makefile
+++ b/drivers/clk/mvebu/Makefile
@@ -3,6 +3,8 @@ obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o
obj-$(CONFIG_MVEBU_CLK_COREDIV) += clk-corediv.o
obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o
+obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o
+obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o
obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o
obj-$(CONFIG_DOVE_CLK) += dove.o
obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o
diff --git a/drivers/clk/mvebu/armada-375.c b/drivers/clk/mvebu/armada-375.c
new file mode 100644
index 000000000000..c991a4d95e10
--- /dev/null
+++ b/drivers/clk/mvebu/armada-375.c
@@ -0,0 +1,184 @@
+/*
+ * Marvell Armada 375 SoC clocks
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include "common.h"
+
+/*
+ * Core Clocks
+ */
+
+/*
+ * For the Armada 375 SoCs, the CPU, DDR and L2 clocks frequencies are
+ * all modified at the same time, and not separately as for the Armada
+ * 370 or the Armada XP SoCs.
+ *
+ * SAR0[21:17] : CPU frequency DDR frequency L2 frequency
+ * 6 = 400 MHz 400 MHz 200 MHz
+ * 15 = 600 MHz 600 MHz 300 MHz
+ * 21 = 800 MHz 534 MHz 400 MHz
+ * 25 = 1000 MHz 500 MHz 500 MHz
+ * others reserved.
+ *
+ * SAR0[22] : TCLK frequency
+ * 0 = 166 MHz
+ * 1 = 200 MHz
+ */
+
+#define SAR1_A375_TCLK_FREQ_OPT 22
+#define SAR1_A375_TCLK_FREQ_OPT_MASK 0x1
+#define SAR1_A375_CPU_DDR_L2_FREQ_OPT 17
+#define SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK 0x1F
+
+static const u32 armada_375_tclk_frequencies[] __initconst = {
+ 166000000,
+ 200000000,
+};
+
+static u32 __init armada_375_get_tclk_freq(void __iomem *sar)
+{
+ u8 tclk_freq_select;
+
+ tclk_freq_select = ((readl(sar) >> SAR1_A375_TCLK_FREQ_OPT) &
+ SAR1_A375_TCLK_FREQ_OPT_MASK);
+ return armada_375_tclk_frequencies[tclk_freq_select];
+}
+
+
+static const u32 armada_375_cpu_frequencies[] __initconst = {
+ 0, 0, 0, 0, 0, 0,
+ 400000000,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 600000000,
+ 0, 0, 0, 0, 0,
+ 800000000,
+ 0, 0, 0,
+ 1000000000,
+};
+
+static u32 __init armada_375_get_cpu_freq(void __iomem *sar)
+{
+ u8 cpu_freq_select;
+
+ cpu_freq_select = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) &
+ SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK);
+ if (cpu_freq_select >= ARRAY_SIZE(armada_375_cpu_frequencies)) {
+ pr_err("Selected CPU frequency (%d) unsupported\n",
+ cpu_freq_select);
+ return 0;
+ } else
+ return armada_375_cpu_frequencies[cpu_freq_select];
+}
+
+enum { A375_CPU_TO_DDR, A375_CPU_TO_L2 };
+
+static const struct coreclk_ratio armada_375_coreclk_ratios[] __initconst = {
+ { .id = A375_CPU_TO_L2, .name = "l2clk" },
+ { .id = A375_CPU_TO_DDR, .name = "ddrclk" },
+};
+
+static const int armada_375_cpu_l2_ratios[32][2] __initconst = {
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {1, 2}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {1, 2},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {1, 2}, {0, 1}, {0, 1},
+ {0, 1}, {1, 2}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static const int armada_375_cpu_ddr_ratios[32][2] __initconst = {
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {1, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {2, 3},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {2, 3}, {0, 1}, {0, 1},
+ {0, 1}, {1, 2}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static void __init armada_375_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ u32 opt = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) &
+ SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK);
+
+ switch (id) {
+ case A375_CPU_TO_L2:
+ *mult = armada_375_cpu_l2_ratios[opt][0];
+ *div = armada_375_cpu_l2_ratios[opt][1];
+ break;
+ case A375_CPU_TO_DDR:
+ *mult = armada_375_cpu_ddr_ratios[opt][0];
+ *div = armada_375_cpu_ddr_ratios[opt][1];
+ break;
+ }
+}
+
+static const struct coreclk_soc_desc armada_375_coreclks = {
+ .get_tclk_freq = armada_375_get_tclk_freq,
+ .get_cpu_freq = armada_375_get_cpu_freq,
+ .get_clk_ratio = armada_375_get_clk_ratio,
+ .ratios = armada_375_coreclk_ratios,
+ .num_ratios = ARRAY_SIZE(armada_375_coreclk_ratios),
+};
+
+static void __init armada_375_coreclk_init(struct device_node *np)
+{
+ mvebu_coreclk_setup(np, &armada_375_coreclks);
+}
+CLK_OF_DECLARE(armada_375_core_clk, "marvell,armada-375-core-clock",
+ armada_375_coreclk_init);
+
+/*
+ * Clock Gating Control
+ */
+static const struct clk_gating_soc_desc armada_375_gating_desc[] __initconst = {
+ { "mu", NULL, 2 },
+ { "pp", NULL, 3 },
+ { "ptp", NULL, 4 },
+ { "pex0", NULL, 5 },
+ { "pex1", NULL, 6 },
+ { "audio", NULL, 8 },
+ { "nd_clk", "nand", 11 },
+ { "sata0_link", "sata0_core", 14 },
+ { "sata0_core", NULL, 15 },
+ { "usb3", NULL, 16 },
+ { "sdio", NULL, 17 },
+ { "usb", NULL, 18 },
+ { "gop", NULL, 19 },
+ { "sata1_link", "sata1_core", 20 },
+ { "sata1_core", NULL, 21 },
+ { "xor0", NULL, 22 },
+ { "xor1", NULL, 23 },
+ { "copro", NULL, 24 },
+ { "tdm", NULL, 25 },
+ { "crypto0_enc", NULL, 28 },
+ { "crypto0_core", NULL, 29 },
+ { "crypto1_enc", NULL, 30 },
+ { "crypto1_core", NULL, 31 },
+ { }
+};
+
+static void __init armada_375_clk_gating_init(struct device_node *np)
+{
+ mvebu_clk_gating_setup(np, armada_375_gating_desc);
+}
+CLK_OF_DECLARE(armada_375_clk_gating, "marvell,armada-375-gating-clock",
+ armada_375_clk_gating_init);
diff --git a/drivers/clk/mvebu/armada-38x.c b/drivers/clk/mvebu/armada-38x.c
new file mode 100644
index 000000000000..8bccf4ecdab6
--- /dev/null
+++ b/drivers/clk/mvebu/armada-38x.c
@@ -0,0 +1,167 @@
+/*
+ * Marvell Armada 380/385 SoC clocks
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include "common.h"
+
+/*
+ * SAR[14:10] : Ratios between PCLK0, NBCLK, HCLK and DRAM clocks
+ *
+ * SAR[15] : TCLK frequency
+ * 0 = 250 MHz
+ * 1 = 200 MHz
+ */
+
+#define SAR_A380_TCLK_FREQ_OPT 15
+#define SAR_A380_TCLK_FREQ_OPT_MASK 0x1
+#define SAR_A380_CPU_DDR_L2_FREQ_OPT 10
+#define SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK 0x1F
+
+static const u32 armada_38x_tclk_frequencies[] __initconst = {
+ 250000000,
+ 200000000,
+};
+
+static u32 __init armada_38x_get_tclk_freq(void __iomem *sar)
+{
+ u8 tclk_freq_select;
+
+ tclk_freq_select = ((readl(sar) >> SAR_A380_TCLK_FREQ_OPT) &
+ SAR_A380_TCLK_FREQ_OPT_MASK);
+ return armada_38x_tclk_frequencies[tclk_freq_select];
+}
+
+static const u32 armada_38x_cpu_frequencies[] __initconst = {
+ 0, 0, 0, 0,
+ 1066 * 1000 * 1000, 0, 0, 0,
+ 1332 * 1000 * 1000, 0, 0, 0,
+ 1600 * 1000 * 1000,
+};
+
+static u32 __init armada_38x_get_cpu_freq(void __iomem *sar)
+{
+ u8 cpu_freq_select;
+
+ cpu_freq_select = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) &
+ SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK);
+ if (cpu_freq_select >= ARRAY_SIZE(armada_38x_cpu_frequencies)) {
+ pr_err("Selected CPU frequency (%d) unsupported\n",
+ cpu_freq_select);
+ return 0;
+ }
+
+ return armada_38x_cpu_frequencies[cpu_freq_select];
+}
+
+enum { A380_CPU_TO_DDR, A380_CPU_TO_L2 };
+
+static const struct coreclk_ratio armada_38x_coreclk_ratios[] __initconst = {
+ { .id = A380_CPU_TO_L2, .name = "l2clk" },
+ { .id = A380_CPU_TO_DDR, .name = "ddrclk" },
+};
+
+static const int armada_38x_cpu_l2_ratios[32][2] __initconst = {
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static const int armada_38x_cpu_ddr_ratios[32][2] __initconst = {
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static void __init armada_38x_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ u32 opt = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) &
+ SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK);
+
+ switch (id) {
+ case A380_CPU_TO_L2:
+ *mult = armada_38x_cpu_l2_ratios[opt][0];
+ *div = armada_38x_cpu_l2_ratios[opt][1];
+ break;
+ case A380_CPU_TO_DDR:
+ *mult = armada_38x_cpu_ddr_ratios[opt][0];
+ *div = armada_38x_cpu_ddr_ratios[opt][1];
+ break;
+ }
+}
+
+static const struct coreclk_soc_desc armada_38x_coreclks = {
+ .get_tclk_freq = armada_38x_get_tclk_freq,
+ .get_cpu_freq = armada_38x_get_cpu_freq,
+ .get_clk_ratio = armada_38x_get_clk_ratio,
+ .ratios = armada_38x_coreclk_ratios,
+ .num_ratios = ARRAY_SIZE(armada_38x_coreclk_ratios),
+};
+
+static void __init armada_38x_coreclk_init(struct device_node *np)
+{
+ mvebu_coreclk_setup(np, &armada_38x_coreclks);
+}
+CLK_OF_DECLARE(armada_38x_core_clk, "marvell,armada-380-core-clock",
+ armada_38x_coreclk_init);
+
+/*
+ * Clock Gating Control
+ */
+static const struct clk_gating_soc_desc armada_38x_gating_desc[] __initconst = {
+ { "audio", NULL, 0 },
+ { "ge2", NULL, 2 },
+ { "ge1", NULL, 3 },
+ { "ge0", NULL, 4 },
+ { "pex1", NULL, 5 },
+ { "pex2", NULL, 6 },
+ { "pex3", NULL, 7 },
+ { "pex0", NULL, 8 },
+ { "usb3h0", NULL, 9 },
+ { "usb3h1", NULL, 10 },
+ { "usb3d", NULL, 11 },
+ { "bm", NULL, 13 },
+ { "crypto0z", NULL, 14 },
+ { "sata0", NULL, 15 },
+ { "crypto1z", NULL, 16 },
+ { "sdio", NULL, 17 },
+ { "usb2", NULL, 18 },
+ { "crypto1", NULL, 21 },
+ { "xor0", NULL, 22 },
+ { "crypto0", NULL, 23 },
+ { "tdm", NULL, 25 },
+ { "xor1", NULL, 28 },
+ { "sata1", NULL, 30 },
+ { }
+};
+
+static void __init armada_38x_clk_gating_init(struct device_node *np)
+{
+ mvebu_clk_gating_setup(np, armada_38x_gating_desc);
+}
+CLK_OF_DECLARE(armada_38x_clk_gating, "marvell,armada-380-gating-clock",
+ armada_38x_clk_gating_init);
diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c
index 7162615bcdcd..d1e5863d3375 100644
--- a/drivers/clk/mvebu/clk-corediv.c
+++ b/drivers/clk/mvebu/clk-corediv.c
@@ -18,26 +18,56 @@
#include "common.h"
#define CORE_CLK_DIV_RATIO_MASK 0xff
-#define CORE_CLK_DIV_RATIO_RELOAD BIT(8)
-#define CORE_CLK_DIV_ENABLE_OFFSET 24
-#define CORE_CLK_DIV_RATIO_OFFSET 0x8
+/*
+ * This structure describes the hardware details (bit offset and mask)
+ * to configure one particular core divider clock. Those hardware
+ * details may differ from one SoC to another. This structure is
+ * therefore typically instantiated statically to describe the
+ * hardware details.
+ */
struct clk_corediv_desc {
unsigned int mask;
unsigned int offset;
unsigned int fieldbit;
};
+/*
+ * This structure describes the hardware details to configure the core
+ * divider clocks on a given SoC. Amongst others, it points to the
+ * array of core divider clock descriptors for this SoC, as well as
+ * the corresponding operations to manipulate them.
+ */
+struct clk_corediv_soc_desc {
+ const struct clk_corediv_desc *descs;
+ unsigned int ndescs;
+ const struct clk_ops ops;
+ u32 ratio_reload;
+ u32 enable_bit_offset;
+ u32 ratio_offset;
+};
+
+/*
+ * This structure represents one core divider clock for the clock
+ * framework, and is dynamically allocated for each core divider clock
+ * existing in the current SoC.
+ */
struct clk_corediv {
struct clk_hw hw;
void __iomem *reg;
- struct clk_corediv_desc desc;
+ const struct clk_corediv_desc *desc;
+ const struct clk_corediv_soc_desc *soc_desc;
spinlock_t lock;
};
static struct clk_onecell_data clk_data;
-static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = {
+/*
+ * Description of the core divider clocks available. For now, we
+ * support only NAND, and it is available at the same register
+ * locations regardless of the SoC.
+ */
+static const struct clk_corediv_desc mvebu_corediv_desc[] = {
{ .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */
};
@@ -46,8 +76,9 @@ static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = {
static int clk_corediv_is_enabled(struct clk_hw *hwclk)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
- struct clk_corediv_desc *desc = &corediv->desc;
- u32 enable_mask = BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET;
+ const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
+ const struct clk_corediv_desc *desc = corediv->desc;
+ u32 enable_mask = BIT(desc->fieldbit) << soc_desc->enable_bit_offset;
return !!(readl(corediv->reg) & enable_mask);
}
@@ -55,14 +86,15 @@ static int clk_corediv_is_enabled(struct clk_hw *hwclk)
static int clk_corediv_enable(struct clk_hw *hwclk)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
- struct clk_corediv_desc *desc = &corediv->desc;
+ const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
+ const struct clk_corediv_desc *desc = corediv->desc;
unsigned long flags = 0;
u32 reg;
spin_lock_irqsave(&corediv->lock, flags);
reg = readl(corediv->reg);
- reg |= (BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET);
+ reg |= (BIT(desc->fieldbit) << soc_desc->enable_bit_offset);
writel(reg, corediv->reg);
spin_unlock_irqrestore(&corediv->lock, flags);
@@ -73,14 +105,15 @@ static int clk_corediv_enable(struct clk_hw *hwclk)
static void clk_corediv_disable(struct clk_hw *hwclk)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
- struct clk_corediv_desc *desc = &corediv->desc;
+ const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
+ const struct clk_corediv_desc *desc = corediv->desc;
unsigned long flags = 0;
u32 reg;
spin_lock_irqsave(&corediv->lock, flags);
reg = readl(corediv->reg);
- reg &= ~(BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET);
+ reg &= ~(BIT(desc->fieldbit) << soc_desc->enable_bit_offset);
writel(reg, corediv->reg);
spin_unlock_irqrestore(&corediv->lock, flags);
@@ -90,10 +123,11 @@ static unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
- struct clk_corediv_desc *desc = &corediv->desc;
+ const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
+ const struct clk_corediv_desc *desc = corediv->desc;
u32 reg, div;
- reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
+ reg = readl(corediv->reg + soc_desc->ratio_offset);
div = (reg >> desc->offset) & desc->mask;
return parent_rate / div;
}
@@ -117,7 +151,8 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
unsigned long parent_rate)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
- struct clk_corediv_desc *desc = &corediv->desc;
+ const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
+ const struct clk_corediv_desc *desc = corediv->desc;
unsigned long flags = 0;
u32 reg, div;
@@ -126,17 +161,17 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
spin_lock_irqsave(&corediv->lock, flags);
/* Write new divider to the divider ratio register */
- reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
+ reg = readl(corediv->reg + soc_desc->ratio_offset);
reg &= ~(desc->mask << desc->offset);
reg |= (div & desc->mask) << desc->offset;
- writel(reg, corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
+ writel(reg, corediv->reg + soc_desc->ratio_offset);
/* Set reload-force for this clock */
reg = readl(corediv->reg) | BIT(desc->fieldbit);
writel(reg, corediv->reg);
/* Now trigger the clock update */
- reg = readl(corediv->reg) | CORE_CLK_DIV_RATIO_RELOAD;
+ reg = readl(corediv->reg) | soc_desc->ratio_reload;
writel(reg, corediv->reg);
/*
@@ -144,7 +179,7 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
* ratios request and the reload request.
*/
udelay(1000);
- reg &= ~(CORE_CLK_DIV_RATIO_MASK | CORE_CLK_DIV_RATIO_RELOAD);
+ reg &= ~(CORE_CLK_DIV_RATIO_MASK | soc_desc->ratio_reload);
writel(reg, corediv->reg);
udelay(1000);
@@ -153,16 +188,53 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
return 0;
}
-static const struct clk_ops corediv_ops = {
- .enable = clk_corediv_enable,
- .disable = clk_corediv_disable,
- .is_enabled = clk_corediv_is_enabled,
- .recalc_rate = clk_corediv_recalc_rate,
- .round_rate = clk_corediv_round_rate,
- .set_rate = clk_corediv_set_rate,
+static const struct clk_corediv_soc_desc armada370_corediv_soc = {
+ .descs = mvebu_corediv_desc,
+ .ndescs = ARRAY_SIZE(mvebu_corediv_desc),
+ .ops = {
+ .enable = clk_corediv_enable,
+ .disable = clk_corediv_disable,
+ .is_enabled = clk_corediv_is_enabled,
+ .recalc_rate = clk_corediv_recalc_rate,
+ .round_rate = clk_corediv_round_rate,
+ .set_rate = clk_corediv_set_rate,
+ },
+ .ratio_reload = BIT(8),
+ .enable_bit_offset = 24,
+ .ratio_offset = 0x8,
+};
+
+static const struct clk_corediv_soc_desc armada380_corediv_soc = {
+ .descs = mvebu_corediv_desc,
+ .ndescs = ARRAY_SIZE(mvebu_corediv_desc),
+ .ops = {
+ .enable = clk_corediv_enable,
+ .disable = clk_corediv_disable,
+ .is_enabled = clk_corediv_is_enabled,
+ .recalc_rate = clk_corediv_recalc_rate,
+ .round_rate = clk_corediv_round_rate,
+ .set_rate = clk_corediv_set_rate,
+ },
+ .ratio_reload = BIT(8),
+ .enable_bit_offset = 16,
+ .ratio_offset = 0x4,
};
-static void __init mvebu_corediv_clk_init(struct device_node *node)
+static const struct clk_corediv_soc_desc armada375_corediv_soc = {
+ .descs = mvebu_corediv_desc,
+ .ndescs = ARRAY_SIZE(mvebu_corediv_desc),
+ .ops = {
+ .recalc_rate = clk_corediv_recalc_rate,
+ .round_rate = clk_corediv_round_rate,
+ .set_rate = clk_corediv_set_rate,
+ },
+ .ratio_reload = BIT(8),
+ .ratio_offset = 0x4,
+};
+
+static void __init
+mvebu_corediv_clk_init(struct device_node *node,
+ const struct clk_corediv_soc_desc *soc_desc)
{
struct clk_init_data init;
struct clk_corediv *corediv;
@@ -178,7 +250,7 @@ static void __init mvebu_corediv_clk_init(struct device_node *node)
parent_name = of_clk_get_parent_name(node, 0);
- clk_data.clk_num = ARRAY_SIZE(mvebu_corediv_desc);
+ clk_data.clk_num = soc_desc->ndescs;
/* clks holds the clock array */
clks = kcalloc(clk_data.clk_num, sizeof(struct clk *),
@@ -199,10 +271,11 @@ static void __init mvebu_corediv_clk_init(struct device_node *node)
init.num_parents = 1;
init.parent_names = &parent_name;
init.name = clk_name;
- init.ops = &corediv_ops;
+ init.ops = &soc_desc->ops;
init.flags = 0;
- corediv[i].desc = mvebu_corediv_desc[i];
+ corediv[i].soc_desc = soc_desc;
+ corediv[i].desc = soc_desc->descs + i;
corediv[i].reg = base;
corediv[i].hw.init = &init;
@@ -219,5 +292,24 @@ err_free_clks:
err_unmap:
iounmap(base);
}
-CLK_OF_DECLARE(mvebu_corediv_clk, "marvell,armada-370-corediv-clock",
- mvebu_corediv_clk_init);
+
+static void __init armada370_corediv_clk_init(struct device_node *node)
+{
+ return mvebu_corediv_clk_init(node, &armada370_corediv_soc);
+}
+CLK_OF_DECLARE(armada370_corediv_clk, "marvell,armada-370-corediv-clock",
+ armada370_corediv_clk_init);
+
+static void __init armada375_corediv_clk_init(struct device_node *node)
+{
+ return mvebu_corediv_clk_init(node, &armada375_corediv_soc);
+}
+CLK_OF_DECLARE(armada375_corediv_clk, "marvell,armada-375-corediv-clock",
+ armada375_corediv_clk_init);
+
+static void __init armada380_corediv_clk_init(struct device_node *node)
+{
+ return mvebu_corediv_clk_init(node, &armada380_corediv_soc);
+}
+CLK_OF_DECLARE(armada380_corediv_clk, "marvell,armada-380-corediv-clock",
+ armada380_corediv_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
index 884187fbfe00..13eae14c2cc2 100644
--- a/drivers/clk/samsung/clk-exynos-audss.c
+++ b/drivers/clk/samsung/clk-exynos-audss.c
@@ -17,7 +17,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <dt-bindings/clk/exynos-audss-clk.h>
+#include <dt-bindings/clock/exynos-audss-clk.h>
enum exynos_audss_clk_type {
TYPE_EXYNOS4210,
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c
index 010f071af883..b4f967210175 100644
--- a/drivers/clk/samsung/clk-exynos4.c
+++ b/drivers/clk/samsung/clk-exynos4.c
@@ -16,6 +16,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
#include "clk.h"
@@ -130,6 +131,17 @@ enum exynos4_plls {
nr_plls /* number of PLLs */
};
+static void __iomem *reg_base;
+static enum exynos4_soc exynos4_soc;
+
+/*
+ * Support for CMU save/restore across system suspends
+ */
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *exynos4_save_common;
+static struct samsung_clk_reg_dump *exynos4_save_soc;
+static struct samsung_clk_reg_dump *exynos4_save_pll;
+
/*
* list of controller registers to be saved and restored during a
* suspend/resume cycle.
@@ -154,6 +166,17 @@ static unsigned long exynos4x12_clk_save[] __initdata = {
E4X12_MPLL_CON0,
};
+static unsigned long exynos4_clk_pll_regs[] __initdata = {
+ EPLL_LOCK,
+ VPLL_LOCK,
+ EPLL_CON0,
+ EPLL_CON1,
+ EPLL_CON2,
+ VPLL_CON0,
+ VPLL_CON1,
+ VPLL_CON2,
+};
+
static unsigned long exynos4_clk_regs[] __initdata = {
SRC_LEFTBUS,
DIV_LEFTBUS,
@@ -161,12 +184,6 @@ static unsigned long exynos4_clk_regs[] __initdata = {
SRC_RIGHTBUS,
DIV_RIGHTBUS,
GATE_IP_RIGHTBUS,
- EPLL_CON0,
- EPLL_CON1,
- EPLL_CON2,
- VPLL_CON0,
- VPLL_CON1,
- VPLL_CON2,
SRC_TOP0,
SRC_TOP1,
SRC_CAM,
@@ -227,6 +244,124 @@ static unsigned long exynos4_clk_regs[] __initdata = {
GATE_IP_CPU,
};
+static const struct samsung_clk_reg_dump src_mask_suspend[] = {
+ { .offset = SRC_MASK_TOP, .value = 0x00000001, },
+ { .offset = SRC_MASK_CAM, .value = 0x11111111, },
+ { .offset = SRC_MASK_TV, .value = 0x00000111, },
+ { .offset = SRC_MASK_LCD0, .value = 0x00001111, },
+ { .offset = SRC_MASK_MAUDIO, .value = 0x00000001, },
+ { .offset = SRC_MASK_FSYS, .value = 0x01011111, },
+ { .offset = SRC_MASK_PERIL0, .value = 0x01111111, },
+ { .offset = SRC_MASK_PERIL1, .value = 0x01110111, },
+ { .offset = SRC_MASK_DMC, .value = 0x00010000, },
+};
+
+static const struct samsung_clk_reg_dump src_mask_suspend_e4210[] = {
+ { .offset = E4210_SRC_MASK_LCD1, .value = 0x00001111, },
+};
+
+#define PLL_ENABLED (1 << 31)
+#define PLL_LOCKED (1 << 29)
+
+static void exynos4_clk_wait_for_pll(u32 reg)
+{
+ u32 pll_con;
+
+ pll_con = readl(reg_base + reg);
+ if (!(pll_con & PLL_ENABLED))
+ return;
+
+ while (!(pll_con & PLL_LOCKED)) {
+ cpu_relax();
+ pll_con = readl(reg_base + reg);
+ }
+}
+
+static int exynos4_clk_suspend(void)
+{
+ samsung_clk_save(reg_base, exynos4_save_common,
+ ARRAY_SIZE(exynos4_clk_regs));
+ samsung_clk_save(reg_base, exynos4_save_pll,
+ ARRAY_SIZE(exynos4_clk_pll_regs));
+
+ if (exynos4_soc == EXYNOS4210) {
+ samsung_clk_save(reg_base, exynos4_save_soc,
+ ARRAY_SIZE(exynos4210_clk_save));
+ samsung_clk_restore(reg_base, src_mask_suspend_e4210,
+ ARRAY_SIZE(src_mask_suspend_e4210));
+ } else {
+ samsung_clk_save(reg_base, exynos4_save_soc,
+ ARRAY_SIZE(exynos4x12_clk_save));
+ }
+
+ samsung_clk_restore(reg_base, src_mask_suspend,
+ ARRAY_SIZE(src_mask_suspend));
+
+ return 0;
+}
+
+static void exynos4_clk_resume(void)
+{
+ samsung_clk_restore(reg_base, exynos4_save_pll,
+ ARRAY_SIZE(exynos4_clk_pll_regs));
+
+ exynos4_clk_wait_for_pll(EPLL_CON0);
+ exynos4_clk_wait_for_pll(VPLL_CON0);
+
+ samsung_clk_restore(reg_base, exynos4_save_common,
+ ARRAY_SIZE(exynos4_clk_regs));
+
+ if (exynos4_soc == EXYNOS4210)
+ samsung_clk_restore(reg_base, exynos4_save_soc,
+ ARRAY_SIZE(exynos4210_clk_save));
+ else
+ samsung_clk_restore(reg_base, exynos4_save_soc,
+ ARRAY_SIZE(exynos4x12_clk_save));
+}
+
+static struct syscore_ops exynos4_clk_syscore_ops = {
+ .suspend = exynos4_clk_suspend,
+ .resume = exynos4_clk_resume,
+};
+
+static void exynos4_clk_sleep_init(void)
+{
+ exynos4_save_common = samsung_clk_alloc_reg_dump(exynos4_clk_regs,
+ ARRAY_SIZE(exynos4_clk_regs));
+ if (!exynos4_save_common)
+ goto err_warn;
+
+ if (exynos4_soc == EXYNOS4210)
+ exynos4_save_soc = samsung_clk_alloc_reg_dump(
+ exynos4210_clk_save,
+ ARRAY_SIZE(exynos4210_clk_save));
+ else
+ exynos4_save_soc = samsung_clk_alloc_reg_dump(
+ exynos4x12_clk_save,
+ ARRAY_SIZE(exynos4x12_clk_save));
+ if (!exynos4_save_soc)
+ goto err_common;
+
+ exynos4_save_pll = samsung_clk_alloc_reg_dump(exynos4_clk_pll_regs,
+ ARRAY_SIZE(exynos4_clk_pll_regs));
+ if (!exynos4_save_pll)
+ goto err_soc;
+
+ register_syscore_ops(&exynos4_clk_syscore_ops);
+ return;
+
+err_soc:
+ kfree(exynos4_save_soc);
+err_common:
+ kfree(exynos4_save_common);
+err_warn:
+ pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+ __func__);
+}
+#else
+static void exynos4_clk_sleep_init(void) {}
+#endif
+
/* list of all parent clock list */
PNAME(mout_apll_p) = { "fin_pll", "fout_apll", };
PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", };
@@ -908,12 +1043,13 @@ static unsigned long exynos4_get_xom(void)
return xom;
}
-static void __init exynos4_clk_register_finpll(unsigned long xom)
+static void __init exynos4_clk_register_finpll(void)
{
struct samsung_fixed_rate_clock fclk;
struct clk *clk;
unsigned long finpll_f = 24000000;
char *parent_name;
+ unsigned int xom = exynos4_get_xom();
parent_name = xom & 1 ? "xusbxti" : "xxti";
clk = clk_get(NULL, parent_name);
@@ -1038,27 +1174,21 @@ static struct samsung_pll_clock exynos4x12_plls[nr_plls] __initdata = {
/* register exynos4 clocks */
static void __init exynos4_clk_init(struct device_node *np,
- enum exynos4_soc exynos4_soc,
- void __iomem *reg_base, unsigned long xom)
+ enum exynos4_soc soc)
{
+ exynos4_soc = soc;
+
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
- if (exynos4_soc == EXYNOS4210)
- samsung_clk_init(np, reg_base, CLK_NR_CLKS,
- exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs),
- exynos4210_clk_save, ARRAY_SIZE(exynos4210_clk_save));
- else
- samsung_clk_init(np, reg_base, CLK_NR_CLKS,
- exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs),
- exynos4x12_clk_save, ARRAY_SIZE(exynos4x12_clk_save));
+ samsung_clk_init(np, reg_base, CLK_NR_CLKS);
samsung_clk_of_register_fixed_ext(exynos4_fixed_rate_ext_clks,
ARRAY_SIZE(exynos4_fixed_rate_ext_clks),
ext_clk_match);
- exynos4_clk_register_finpll(xom);
+ exynos4_clk_register_finpll();
if (exynos4_soc == EXYNOS4210) {
samsung_clk_register_mux(exynos4210_mux_early,
@@ -1125,6 +1255,8 @@ static void __init exynos4_clk_init(struct device_node *np,
samsung_clk_register_alias(exynos4_aliases,
ARRAY_SIZE(exynos4_aliases));
+ exynos4_clk_sleep_init();
+
pr_info("%s clocks: sclk_apll = %ld, sclk_mpll = %ld\n"
"\tsclk_epll = %ld, sclk_vpll = %ld, arm_clk = %ld\n",
exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12",
@@ -1136,12 +1268,12 @@ static void __init exynos4_clk_init(struct device_node *np,
static void __init exynos4210_clk_init(struct device_node *np)
{
- exynos4_clk_init(np, EXYNOS4210, NULL, exynos4_get_xom());
+ exynos4_clk_init(np, EXYNOS4210);
}
CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init);
static void __init exynos4412_clk_init(struct device_node *np)
{
- exynos4_clk_init(np, EXYNOS4X12, NULL, exynos4_get_xom());
+ exynos4_clk_init(np, EXYNOS4X12);
}
CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4412_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index ff4beebe1f0b..e7ee4420da81 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -16,6 +16,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
#include "clk.h"
@@ -85,6 +86,11 @@ enum exynos5250_plls {
nr_plls /* number of PLLs */
};
+static void __iomem *reg_base;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *exynos5250_save;
+
/*
* list of controller registers to be saved and restored during a
* suspend/resume cycle.
@@ -137,6 +143,41 @@ static unsigned long exynos5250_clk_regs[] __initdata = {
GATE_IP_ACP,
};
+static int exynos5250_clk_suspend(void)
+{
+ samsung_clk_save(reg_base, exynos5250_save,
+ ARRAY_SIZE(exynos5250_clk_regs));
+
+ return 0;
+}
+
+static void exynos5250_clk_resume(void)
+{
+ samsung_clk_restore(reg_base, exynos5250_save,
+ ARRAY_SIZE(exynos5250_clk_regs));
+}
+
+static struct syscore_ops exynos5250_clk_syscore_ops = {
+ .suspend = exynos5250_clk_suspend,
+ .resume = exynos5250_clk_resume,
+};
+
+static void exynos5250_clk_sleep_init(void)
+{
+ exynos5250_save = samsung_clk_alloc_reg_dump(exynos5250_clk_regs,
+ ARRAY_SIZE(exynos5250_clk_regs));
+ if (!exynos5250_save) {
+ pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+ __func__);
+ return;
+ }
+
+ register_syscore_ops(&exynos5250_clk_syscore_ops);
+}
+#else
+static void exynos5250_clk_sleep_init(void) {}
+#endif
+
/* list of all parent clock list */
PNAME(mout_apll_p) = { "fin_pll", "fout_apll", };
PNAME(mout_cpu_p) = { "mout_apll", "mout_mpll", };
@@ -645,8 +686,6 @@ static struct of_device_id ext_clk_match[] __initdata = {
/* register exynox5250 clocks */
static void __init exynos5250_clk_init(struct device_node *np)
{
- void __iomem *reg_base;
-
if (np) {
reg_base = of_iomap(np, 0);
if (!reg_base)
@@ -655,9 +694,7 @@ static void __init exynos5250_clk_init(struct device_node *np)
panic("%s: unable to determine soc\n", __func__);
}
- samsung_clk_init(np, reg_base, CLK_NR_CLKS,
- exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs),
- NULL, 0);
+ samsung_clk_init(np, reg_base, CLK_NR_CLKS);
samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks,
ARRAY_SIZE(exynos5250_fixed_rate_ext_clks),
ext_clk_match);
@@ -685,6 +722,8 @@ static void __init exynos5250_clk_init(struct device_node *np)
samsung_clk_register_gate(exynos5250_gate_clks,
ARRAY_SIZE(exynos5250_gate_clks));
+ exynos5250_clk_sleep_init();
+
pr_info("Exynos5250: clock setup completed, armclk=%ld\n",
_get_rate("div_arm2"));
}
diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index ab4f2f7d88ef..60b26819bed5 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -16,6 +16,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
#include "clk.h"
@@ -108,6 +109,11 @@ enum exynos5420_plls {
nr_plls /* number of PLLs */
};
+static void __iomem *reg_base;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *exynos5420_save;
+
/*
* list of controller registers to be saved and restored during a
* suspend/resume cycle.
@@ -174,6 +180,41 @@ static unsigned long exynos5420_clk_regs[] __initdata = {
DIV_KFC0,
};
+static int exynos5420_clk_suspend(void)
+{
+ samsung_clk_save(reg_base, exynos5420_save,
+ ARRAY_SIZE(exynos5420_clk_regs));
+
+ return 0;
+}
+
+static void exynos5420_clk_resume(void)
+{
+ samsung_clk_restore(reg_base, exynos5420_save,
+ ARRAY_SIZE(exynos5420_clk_regs));
+}
+
+static struct syscore_ops exynos5420_clk_syscore_ops = {
+ .suspend = exynos5420_clk_suspend,
+ .resume = exynos5420_clk_resume,
+};
+
+static void exynos5420_clk_sleep_init(void)
+{
+ exynos5420_save = samsung_clk_alloc_reg_dump(exynos5420_clk_regs,
+ ARRAY_SIZE(exynos5420_clk_regs));
+ if (!exynos5420_save) {
+ pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+ __func__);
+ return;
+ }
+
+ register_syscore_ops(&exynos5420_clk_syscore_ops);
+}
+#else
+static void exynos5420_clk_sleep_init(void) {}
+#endif
+
/* list of all parent clocks */
PNAME(mspll_cpu_p) = { "sclk_cpll", "sclk_dpll",
"sclk_mpll", "sclk_spll" };
@@ -737,8 +778,6 @@ static struct of_device_id ext_clk_match[] __initdata = {
/* register exynos5420 clocks */
static void __init exynos5420_clk_init(struct device_node *np)
{
- void __iomem *reg_base;
-
if (np) {
reg_base = of_iomap(np, 0);
if (!reg_base)
@@ -747,9 +786,7 @@ static void __init exynos5420_clk_init(struct device_node *np)
panic("%s: unable to determine soc\n", __func__);
}
- samsung_clk_init(np, reg_base, CLK_NR_CLKS,
- exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
- NULL, 0);
+ samsung_clk_init(np, reg_base, CLK_NR_CLKS);
samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
ARRAY_SIZE(exynos5420_fixed_rate_ext_clks),
ext_clk_match);
@@ -765,5 +802,7 @@ static void __init exynos5420_clk_init(struct device_node *np)
ARRAY_SIZE(exynos5420_div_clks));
samsung_clk_register_gate(exynos5420_gate_clks,
ARRAY_SIZE(exynos5420_gate_clks));
+
+ exynos5420_clk_sleep_init();
}
CLK_OF_DECLARE(exynos5420_clk, "samsung,exynos5420-clock", exynos5420_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c
index cbc15b56891d..2bfad5a993d0 100644
--- a/drivers/clk/samsung/clk-exynos5440.c
+++ b/drivers/clk/samsung/clk-exynos5440.c
@@ -101,7 +101,7 @@ static void __init exynos5440_clk_init(struct device_node *np)
return;
}
- samsung_clk_init(np, reg_base, CLK_NR_CLKS, NULL, 0, NULL, 0);
+ samsung_clk_init(np, reg_base, CLK_NR_CLKS);
samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks,
ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match);
diff --git a/drivers/clk/samsung/clk-s3c64xx.c b/drivers/clk/samsung/clk-s3c64xx.c
index 8e27aee6887e..8bda658137a8 100644
--- a/drivers/clk/samsung/clk-s3c64xx.c
+++ b/drivers/clk/samsung/clk-s3c64xx.c
@@ -13,6 +13,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
#include <dt-bindings/clock/samsung,s3c64xx-clock.h>
@@ -61,6 +62,13 @@ enum s3c64xx_plls {
apll, mpll, epll,
};
+static void __iomem *reg_base;
+static bool is_s3c6400;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *s3c64xx_save_common;
+static struct samsung_clk_reg_dump *s3c64xx_save_soc;
+
/*
* List of controller registers to be saved and restored during
* a suspend/resume cycle.
@@ -87,6 +95,60 @@ static unsigned long s3c6410_clk_regs[] __initdata = {
MEM0_GATE,
};
+static int s3c64xx_clk_suspend(void)
+{
+ samsung_clk_save(reg_base, s3c64xx_save_common,
+ ARRAY_SIZE(s3c64xx_clk_regs));
+
+ if (!is_s3c6400)
+ samsung_clk_save(reg_base, s3c64xx_save_soc,
+ ARRAY_SIZE(s3c6410_clk_regs));
+
+ return 0;
+}
+
+static void s3c64xx_clk_resume(void)
+{
+ samsung_clk_restore(reg_base, s3c64xx_save_common,
+ ARRAY_SIZE(s3c64xx_clk_regs));
+
+ if (!is_s3c6400)
+ samsung_clk_restore(reg_base, s3c64xx_save_soc,
+ ARRAY_SIZE(s3c6410_clk_regs));
+}
+
+static struct syscore_ops s3c64xx_clk_syscore_ops = {
+ .suspend = s3c64xx_clk_suspend,
+ .resume = s3c64xx_clk_resume,
+};
+
+static void s3c64xx_clk_sleep_init(void)
+{
+ s3c64xx_save_common = samsung_clk_alloc_reg_dump(s3c64xx_clk_regs,
+ ARRAY_SIZE(s3c64xx_clk_regs));
+ if (!s3c64xx_save_common)
+ goto err_warn;
+
+ if (!is_s3c6400) {
+ s3c64xx_save_soc = samsung_clk_alloc_reg_dump(s3c6410_clk_regs,
+ ARRAY_SIZE(s3c6410_clk_regs));
+ if (!s3c64xx_save_soc)
+ goto err_soc;
+ }
+
+ register_syscore_ops(&s3c64xx_clk_syscore_ops);
+ return;
+
+err_soc:
+ kfree(s3c64xx_save_common);
+err_warn:
+ pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+ __func__);
+}
+#else
+static void s3c64xx_clk_sleep_init(void) {}
+#endif
+
/* List of parent clocks common for all S3C64xx SoCs. */
PNAME(spi_mmc_p) = { "mout_epll", "dout_mpll", "fin_pll", "clk27m" };
PNAME(uart_p) = { "mout_epll", "dout_mpll" };
@@ -391,11 +453,11 @@ static void __init s3c64xx_clk_register_fixed_ext(unsigned long fin_pll_f,
/* Register s3c64xx clocks. */
void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
- unsigned long xusbxti_f, bool is_s3c6400,
- void __iomem *reg_base)
+ unsigned long xusbxti_f, bool s3c6400,
+ void __iomem *base)
{
- unsigned long *soc_regs = NULL;
- unsigned long nr_soc_regs = 0;
+ reg_base = base;
+ is_s3c6400 = s3c6400;
if (np) {
reg_base = of_iomap(np, 0);
@@ -403,13 +465,7 @@ void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
panic("%s: failed to map registers\n", __func__);
}
- if (!is_s3c6400) {
- soc_regs = s3c6410_clk_regs;
- nr_soc_regs = ARRAY_SIZE(s3c6410_clk_regs);
- }
-
- samsung_clk_init(np, reg_base, NR_CLKS, s3c64xx_clk_regs,
- ARRAY_SIZE(s3c64xx_clk_regs), soc_regs, nr_soc_regs);
+ samsung_clk_init(np, reg_base, NR_CLKS);
/* Register external clocks. */
if (!np)
@@ -452,6 +508,7 @@ void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
samsung_clk_register_alias(s3c64xx_clock_aliases,
ARRAY_SIZE(s3c64xx_clock_aliases));
+ s3c64xx_clk_sleep_init();
pr_info("%s clocks: apll = %lu, mpll = %lu\n"
"\tepll = %lu, arm_clk = %lu\n",
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
index f503f32e2f80..91bec3ebdc8f 100644
--- a/drivers/clk/samsung/clk.c
+++ b/drivers/clk/samsung/clk.c
@@ -21,64 +21,45 @@ static void __iomem *reg_base;
static struct clk_onecell_data clk_data;
#endif
-#ifdef CONFIG_PM_SLEEP
-static struct samsung_clk_reg_dump *reg_dump;
-static unsigned long nr_reg_dump;
-
-static int samsung_clk_suspend(void)
+void samsung_clk_save(void __iomem *base,
+ struct samsung_clk_reg_dump *rd,
+ unsigned int num_regs)
{
- struct samsung_clk_reg_dump *rd = reg_dump;
- unsigned long i;
-
- for (i = 0; i < nr_reg_dump; i++, rd++)
- rd->value = __raw_readl(reg_base + rd->offset);
+ for (; num_regs > 0; --num_regs, ++rd)
+ rd->value = readl(base + rd->offset);
+}
- return 0;
+void samsung_clk_restore(void __iomem *base,
+ const struct samsung_clk_reg_dump *rd,
+ unsigned int num_regs)
+{
+ for (; num_regs > 0; --num_regs, ++rd)
+ writel(rd->value, base + rd->offset);
}
-static void samsung_clk_resume(void)
+struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
+ const unsigned long *rdump,
+ unsigned long nr_rdump)
{
- struct samsung_clk_reg_dump *rd = reg_dump;
- unsigned long i;
+ struct samsung_clk_reg_dump *rd;
+ unsigned int i;
- for (i = 0; i < nr_reg_dump; i++, rd++)
- __raw_writel(rd->value, reg_base + rd->offset);
-}
+ rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);
+ if (!rd)
+ return NULL;
+
+ for (i = 0; i < nr_rdump; ++i)
+ rd[i].offset = rdump[i];
-static struct syscore_ops samsung_clk_syscore_ops = {
- .suspend = samsung_clk_suspend,
- .resume = samsung_clk_resume,
-};
-#endif /* CONFIG_PM_SLEEP */
+ return rd;
+}
/* setup the essentials required to support clock lookup using ccf */
void __init samsung_clk_init(struct device_node *np, void __iomem *base,
- unsigned long nr_clks, unsigned long *rdump,
- unsigned long nr_rdump, unsigned long *soc_rdump,
- unsigned long nr_soc_rdump)
+ unsigned long nr_clks)
{
reg_base = base;
-#ifdef CONFIG_PM_SLEEP
- if (rdump && nr_rdump) {
- unsigned int idx;
- reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump)
- * (nr_rdump + nr_soc_rdump), GFP_KERNEL);
- if (!reg_dump) {
- pr_err("%s: memory alloc for register dump failed\n",
- __func__);
- return;
- }
-
- for (idx = 0; idx < nr_rdump; idx++)
- reg_dump[idx].offset = rdump[idx];
- for (idx = 0; idx < nr_soc_rdump; idx++)
- reg_dump[nr_rdump + idx].offset = soc_rdump[idx];
- nr_reg_dump = nr_rdump + nr_soc_rdump;
- register_syscore_ops(&samsung_clk_syscore_ops);
- }
-#endif
-
clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
if (!clk_table)
panic("could not allocate clock lookup table\n");
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index 31b4174e7a5b..c7141ba826e0 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -313,9 +313,7 @@ struct samsung_pll_clock {
_lock, _con, _rtable, _alias)
extern void __init samsung_clk_init(struct device_node *np, void __iomem *base,
- unsigned long nr_clks, unsigned long *rdump,
- unsigned long nr_rdump, unsigned long *soc_rdump,
- unsigned long nr_soc_rdump);
+ unsigned long nr_clks);
extern void __init samsung_clk_of_register_fixed_ext(
struct samsung_fixed_rate_clock *fixed_rate_clk,
unsigned int nr_fixed_rate_clk,
@@ -340,4 +338,14 @@ extern void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list,
extern unsigned long _get_rate(const char *clk_name);
+extern void samsung_clk_save(void __iomem *base,
+ struct samsung_clk_reg_dump *rd,
+ unsigned int num_regs);
+extern void samsung_clk_restore(void __iomem *base,
+ const struct samsung_clk_reg_dump *rd,
+ unsigned int num_regs);
+extern struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
+ const unsigned long *rdump,
+ unsigned long nr_rdump);
+
#endif /* __SAMSUNG_CLK_H */
diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile
index 9ecef140dba7..5404cb931ebf 100644
--- a/drivers/clk/shmobile/Makefile
+++ b/drivers/clk/shmobile/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o
+obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o
obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o
diff --git a/drivers/clk/shmobile/clk-div6.c b/drivers/clk/shmobile/clk-div6.c
index aac4756ec52e..f065f694cb65 100644
--- a/drivers/clk/shmobile/clk-div6.c
+++ b/drivers/clk/shmobile/clk-div6.c
@@ -23,7 +23,7 @@
#define CPG_DIV6_DIV_MASK 0x3f
/**
- * struct div6_clock - MSTP gating clock
+ * struct div6_clock - CPG 6 bit divider clock
* @hw: handle between common and hardware-specific interfaces
* @reg: IO-remapped register
* @div: divisor value (1-64)
diff --git a/drivers/clk/shmobile/clk-mstp.c b/drivers/clk/shmobile/clk-mstp.c
index 42d5912b1d25..2e5810c88d11 100644
--- a/drivers/clk/shmobile/clk-mstp.c
+++ b/drivers/clk/shmobile/clk-mstp.c
@@ -137,7 +137,7 @@ cpg_mstp_clock_register(const char *name, const char *parent_name,
init.name = name;
init.ops = &cpg_mstp_clock_ops;
- init.flags = CLK_IS_BASIC;
+ init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
init.parent_names = &parent_name;
init.num_parents = 1;
diff --git a/drivers/clk/shmobile/clk-rcar-gen2.c b/drivers/clk/shmobile/clk-rcar-gen2.c
index 99c27b1c625b..dff7f79a19b9 100644
--- a/drivers/clk/shmobile/clk-rcar-gen2.c
+++ b/drivers/clk/shmobile/clk-rcar-gen2.c
@@ -242,22 +242,22 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg,
parent_name = "main";
mult = config->pll3_mult;
} else if (!strcmp(name, "lb")) {
- parent_name = "pll1_div2";
+ parent_name = "pll1";
div = cpg_mode & BIT(18) ? 36 : 24;
} else if (!strcmp(name, "qspi")) {
parent_name = "pll1_div2";
div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2)
? 8 : 10;
} else if (!strcmp(name, "sdh")) {
- parent_name = "pll1_div2";
+ parent_name = "pll1";
table = cpg_sdh_div_table;
shift = 8;
} else if (!strcmp(name, "sd0")) {
- parent_name = "pll1_div2";
+ parent_name = "pll1";
table = cpg_sd01_div_table;
shift = 4;
} else if (!strcmp(name, "sd1")) {
- parent_name = "pll1_div2";
+ parent_name = "pll1";
table = cpg_sd01_div_table;
shift = 0;
} else if (!strcmp(name, "z")) {
diff --git a/drivers/clk/shmobile/clk-rz.c b/drivers/clk/shmobile/clk-rz.c
new file mode 100644
index 000000000000..7e68e8630962
--- /dev/null
+++ b/drivers/clk/shmobile/clk-rz.c
@@ -0,0 +1,103 @@
+/*
+ * rz Core CPG Clocks
+ *
+ * Copyright (C) 2013 Ideas On Board SPRL
+ * Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.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 <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+struct rz_cpg {
+ struct clk_onecell_data data;
+ void __iomem *reg;
+};
+
+#define CPG_FRQCR 0x10
+#define CPG_FRQCR2 0x14
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
+static struct clk * __init
+rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name)
+{
+ u32 val;
+ unsigned mult;
+ static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 };
+
+ if (strcmp(name, "pll") == 0) {
+ /* FIXME: cpg_mode should be read from GPIO. But no GPIO support yet */
+ unsigned cpg_mode = 0; /* hardcoded to EXTAL for now */
+ const char *parent_name = of_clk_get_parent_name(np, cpg_mode);
+
+ mult = cpg_mode ? (32 / 4) : 30;
+
+ return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, 1);
+ }
+
+ /* If mapping regs failed, skip non-pll clocks. System will boot anyhow */
+ if (!cpg->reg)
+ return ERR_PTR(-ENXIO);
+
+ /* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3)
+ * and the constraint that always g <= i. To get the rz platform started,
+ * let them run at fixed current speed and implement the details later.
+ */
+ if (strcmp(name, "i") == 0)
+ val = (clk_readl(cpg->reg + CPG_FRQCR) >> 8) & 3;
+ else if (strcmp(name, "g") == 0)
+ val = clk_readl(cpg->reg + CPG_FRQCR2) & 3;
+ else
+ return ERR_PTR(-EINVAL);
+
+ mult = frqcr_tab[val];
+ return clk_register_fixed_factor(NULL, name, "pll", 0, mult, 3);
+}
+
+static void __init rz_cpg_clocks_init(struct device_node *np)
+{
+ struct rz_cpg *cpg;
+ struct clk **clks;
+ unsigned i;
+ int num_clks;
+
+ num_clks = of_property_count_strings(np, "clock-output-names");
+ if (WARN(num_clks <= 0, "can't count CPG clocks\n"))
+ return;
+
+ cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
+ clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL);
+ BUG_ON(!cpg || !clks);
+
+ cpg->data.clks = clks;
+ cpg->data.clk_num = num_clks;
+
+ cpg->reg = of_iomap(np, 0);
+
+ for (i = 0; i < num_clks; ++i) {
+ const char *name;
+ struct clk *clk;
+
+ of_property_read_string_index(np, "clock-output-names", i, &name);
+
+ clk = rz_cpg_register_clock(np, cpg, name);
+ if (IS_ERR(clk))
+ pr_err("%s: failed to register %s %s clock (%ld)\n",
+ __func__, np->name, name, PTR_ERR(clk));
+ else
+ cpg->data.clks[i] = clk;
+ }
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
+}
+CLK_OF_DECLARE(rz_cpg_clks, "renesas,rz-cpg-clocks", rz_cpg_clocks_init);
diff --git a/drivers/clk/sirf/clk-atlas6.c b/drivers/clk/sirf/clk-atlas6.c
index f9f4a15a64ab..d63b76ca60c3 100644
--- a/drivers/clk/sirf/clk-atlas6.c
+++ b/drivers/clk/sirf/clk-atlas6.c
@@ -1,7 +1,8 @@
/*
* Clock tree for CSR SiRFatlasVI
*
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
+ * company.
*
* Licensed under GPLv2 or later.
*/
diff --git a/drivers/clk/sirf/clk-common.c b/drivers/clk/sirf/clk-common.c
index 7dde6a82f514..37af51c5f213 100644
--- a/drivers/clk/sirf/clk-common.c
+++ b/drivers/clk/sirf/clk-common.c
@@ -1,7 +1,8 @@
/*
* common clks module for all SiRF SoCs
*
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
+ * company.
*
* Licensed under GPLv2 or later.
*/
diff --git a/drivers/clk/sirf/clk-prima2.c b/drivers/clk/sirf/clk-prima2.c
index 7adc5c70c7ff..6968e2ebcd8a 100644
--- a/drivers/clk/sirf/clk-prima2.c
+++ b/drivers/clk/sirf/clk-prima2.c
@@ -1,7 +1,8 @@
/*
* Clock tree for CSR SiRFprimaII
*
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
+ * company.
*
* Licensed under GPLv2 or later.
*/
diff --git a/drivers/clk/socfpga/Makefile b/drivers/clk/socfpga/Makefile
index 0303c0b99cd0..7e2d15a0c7b8 100644
--- a/drivers/clk/socfpga/Makefile
+++ b/drivers/clk/socfpga/Makefile
@@ -1 +1,4 @@
obj-y += clk.o
+obj-y += clk-gate.o
+obj-y += clk-pll.o
+obj-y += clk-periph.o
diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c
new file mode 100644
index 000000000000..501d513bf890
--- /dev/null
+++ b/drivers/clk/socfpga/clk-gate.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ * Copyright (C) 2012-2013 Altera Corporation <www.altera.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.
+ *
+ * Based from clk-highbank.c
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include "clk.h"
+
+#define SOCFPGA_L4_MP_CLK "l4_mp_clk"
+#define SOCFPGA_L4_SP_CLK "l4_sp_clk"
+#define SOCFPGA_NAND_CLK "nand_clk"
+#define SOCFPGA_NAND_X_CLK "nand_x_clk"
+#define SOCFPGA_MMC_CLK "sdmmc_clk"
+#define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8
+
+#define div_mask(width) ((1 << (width)) - 1)
+#define streq(a, b) (strcmp((a), (b)) == 0)
+
+#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
+
+/* SDMMC Group for System Manager defines */
+#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108
+#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \
+ ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0))
+
+static u8 socfpga_clk_get_parent(struct clk_hw *hwclk)
+{
+ u32 l4_src;
+ u32 perpll_src;
+
+ if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
+ l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
+ return l4_src &= 0x1;
+ }
+ if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
+ l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
+ return !!(l4_src & 2);
+ }
+
+ perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
+ if (streq(hwclk->init->name, SOCFPGA_MMC_CLK))
+ return perpll_src &= 0x3;
+ if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
+ streq(hwclk->init->name, SOCFPGA_NAND_X_CLK))
+ return (perpll_src >> 2) & 3;
+
+ /* QSPI clock */
+ return (perpll_src >> 4) & 3;
+
+}
+
+static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent)
+{
+ u32 src_reg;
+
+ if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
+ src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
+ src_reg &= ~0x1;
+ src_reg |= parent;
+ writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
+ } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
+ src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
+ src_reg &= ~0x2;
+ src_reg |= (parent << 1);
+ writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
+ } else {
+ src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
+ if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) {
+ src_reg &= ~0x3;
+ src_reg |= parent;
+ } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
+ streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) {
+ src_reg &= ~0xC;
+ src_reg |= (parent << 2);
+ } else {/* QSPI clock */
+ src_reg &= ~0x30;
+ src_reg |= (parent << 4);
+ }
+ writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
+ }
+
+ return 0;
+}
+
+static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
+ u32 div = 1, val;
+
+ if (socfpgaclk->fixed_div)
+ div = socfpgaclk->fixed_div;
+ else if (socfpgaclk->div_reg) {
+ val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
+ val &= div_mask(socfpgaclk->width);
+ /* Check for GPIO_DB_CLK by its offset */
+ if ((int) socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET)
+ div = val + 1;
+ else
+ div = (1 << val);
+ }
+
+ return parent_rate / div;
+}
+
+static int socfpga_clk_prepare(struct clk_hw *hwclk)
+{
+ struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
+ struct regmap *sys_mgr_base_addr;
+ int i;
+ u32 hs_timing;
+ u32 clk_phase[2];
+
+ if (socfpgaclk->clk_phase[0] || socfpgaclk->clk_phase[1]) {
+ sys_mgr_base_addr = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
+ if (IS_ERR(sys_mgr_base_addr)) {
+ pr_err("%s: failed to find altr,sys-mgr regmap!\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ switch (socfpgaclk->clk_phase[i]) {
+ case 0:
+ clk_phase[i] = 0;
+ break;
+ case 45:
+ clk_phase[i] = 1;
+ break;
+ case 90:
+ clk_phase[i] = 2;
+ break;
+ case 135:
+ clk_phase[i] = 3;
+ break;
+ case 180:
+ clk_phase[i] = 4;
+ break;
+ case 225:
+ clk_phase[i] = 5;
+ break;
+ case 270:
+ clk_phase[i] = 6;
+ break;
+ case 315:
+ clk_phase[i] = 7;
+ break;
+ default:
+ clk_phase[i] = 0;
+ break;
+ }
+ }
+ hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1]);
+ regmap_write(sys_mgr_base_addr, SYSMGR_SDMMCGRP_CTRL_OFFSET,
+ hs_timing);
+ }
+ return 0;
+}
+
+static struct clk_ops gateclk_ops = {
+ .prepare = socfpga_clk_prepare,
+ .recalc_rate = socfpga_clk_recalc_rate,
+ .get_parent = socfpga_clk_get_parent,
+ .set_parent = socfpga_clk_set_parent,
+};
+
+static void __init __socfpga_gate_init(struct device_node *node,
+ const struct clk_ops *ops)
+{
+ u32 clk_gate[2];
+ u32 div_reg[3];
+ u32 clk_phase[2];
+ u32 fixed_div;
+ struct clk *clk;
+ struct socfpga_gate_clk *socfpga_clk;
+ const char *clk_name = node->name;
+ const char *parent_name[SOCFPGA_MAX_PARENTS];
+ struct clk_init_data init;
+ int rc;
+ int i = 0;
+
+ socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
+ if (WARN_ON(!socfpga_clk))
+ return;
+
+ rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
+ if (rc)
+ clk_gate[0] = 0;
+
+ if (clk_gate[0]) {
+ socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0];
+ socfpga_clk->hw.bit_idx = clk_gate[1];
+
+ gateclk_ops.enable = clk_gate_ops.enable;
+ gateclk_ops.disable = clk_gate_ops.disable;
+ }
+
+ rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
+ if (rc)
+ socfpga_clk->fixed_div = 0;
+ else
+ socfpga_clk->fixed_div = fixed_div;
+
+ rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
+ if (!rc) {
+ socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0];
+ socfpga_clk->shift = div_reg[1];
+ socfpga_clk->width = div_reg[2];
+ } else {
+ socfpga_clk->div_reg = 0;
+ }
+
+ rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2);
+ if (!rc) {
+ socfpga_clk->clk_phase[0] = clk_phase[0];
+ socfpga_clk->clk_phase[1] = clk_phase[1];
+ }
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = ops;
+ init.flags = 0;
+ while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] =
+ of_clk_get_parent_name(node, i)) != NULL)
+ i++;
+
+ init.parent_names = parent_name;
+ init.num_parents = i;
+ socfpga_clk->hw.hw.init = &init;
+
+ clk = clk_register(NULL, &socfpga_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(socfpga_clk);
+ return;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ if (WARN_ON(rc))
+ return;
+}
+
+void __init socfpga_gate_init(struct device_node *node)
+{
+ __socfpga_gate_init(node, &gateclk_ops);
+}
diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c
new file mode 100644
index 000000000000..81623a3736f9
--- /dev/null
+++ b/drivers/clk/socfpga/clk-periph.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ * Copyright (C) 2012-2013 Altera Corporation <www.altera.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.
+ *
+ * Based from clk-highbank.c
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include "clk.h"
+
+#define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
+
+static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk);
+ u32 div;
+
+ if (socfpgaclk->fixed_div)
+ div = socfpgaclk->fixed_div;
+ else
+ div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1);
+
+ return parent_rate / div;
+}
+
+static const struct clk_ops periclk_ops = {
+ .recalc_rate = clk_periclk_recalc_rate,
+};
+
+static __init void __socfpga_periph_init(struct device_node *node,
+ const struct clk_ops *ops)
+{
+ u32 reg;
+ struct clk *clk;
+ struct socfpga_periph_clk *periph_clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init;
+ int rc;
+ u32 fixed_div;
+
+ of_property_read_u32(node, "reg", &reg);
+
+ periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
+ if (WARN_ON(!periph_clk))
+ return;
+
+ periph_clk->hw.reg = clk_mgr_base_addr + reg;
+
+ rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
+ if (rc)
+ periph_clk->fixed_div = 0;
+ else
+ periph_clk->fixed_div = fixed_div;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = ops;
+ init.flags = 0;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ periph_clk->hw.hw.init = &init;
+
+ clk = clk_register(NULL, &periph_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(periph_clk);
+ return;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+
+void __init socfpga_periph_init(struct device_node *node)
+{
+ __socfpga_periph_init(node, &periclk_ops);
+}
diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c
new file mode 100644
index 000000000000..88dafb5e9627
--- /dev/null
+++ b/drivers/clk/socfpga/clk-pll.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ * Copyright (C) 2012-2013 Altera Corporation <www.altera.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.
+ *
+ * Based from clk-highbank.c
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include "clk.h"
+
+/* Clock bypass bits */
+#define MAINPLL_BYPASS (1<<0)
+#define SDRAMPLL_BYPASS (1<<1)
+#define SDRAMPLL_SRC_BYPASS (1<<2)
+#define PERPLL_BYPASS (1<<3)
+#define PERPLL_SRC_BYPASS (1<<4)
+
+#define SOCFPGA_PLL_BG_PWRDWN 0
+#define SOCFPGA_PLL_EXT_ENA 1
+#define SOCFPGA_PLL_PWR_DOWN 2
+#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
+#define SOCFPGA_PLL_DIVF_SHIFT 3
+#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
+#define SOCFPGA_PLL_DIVQ_SHIFT 16
+
+#define CLK_MGR_PLL_CLK_SRC_SHIFT 22
+#define CLK_MGR_PLL_CLK_SRC_MASK 0x3
+
+#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
+ unsigned long divf, divq, reg;
+ unsigned long long vco_freq;
+ unsigned long bypass;
+
+ reg = readl(socfpgaclk->hw.reg);
+ bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS);
+ if (bypass & MAINPLL_BYPASS)
+ return parent_rate;
+
+ divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
+ divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
+ vco_freq = (unsigned long long)parent_rate * (divf + 1);
+ do_div(vco_freq, (1 + divq));
+ return (unsigned long)vco_freq;
+}
+
+static u8 clk_pll_get_parent(struct clk_hw *hwclk)
+{
+ u32 pll_src;
+ struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
+
+ pll_src = readl(socfpgaclk->hw.reg);
+ return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) &
+ CLK_MGR_PLL_CLK_SRC_MASK;
+}
+
+static struct clk_ops clk_pll_ops = {
+ .recalc_rate = clk_pll_recalc_rate,
+ .get_parent = clk_pll_get_parent,
+};
+
+static __init struct clk *__socfpga_pll_init(struct device_node *node,
+ const struct clk_ops *ops)
+{
+ u32 reg;
+ struct clk *clk;
+ struct socfpga_pll *pll_clk;
+ const char *clk_name = node->name;
+ const char *parent_name[SOCFPGA_MAX_PARENTS];
+ struct clk_init_data init;
+ int rc;
+ int i = 0;
+
+ of_property_read_u32(node, "reg", &reg);
+
+ pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
+ if (WARN_ON(!pll_clk))
+ return NULL;
+
+ pll_clk->hw.reg = clk_mgr_base_addr + reg;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = ops;
+ init.flags = 0;
+
+ while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] =
+ of_clk_get_parent_name(node, i)) != NULL)
+ i++;
+
+ init.num_parents = i;
+ init.parent_names = parent_name;
+ pll_clk->hw.hw.init = &init;
+
+ pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
+ clk_pll_ops.enable = clk_gate_ops.enable;
+ clk_pll_ops.disable = clk_gate_ops.disable;
+
+ clk = clk_register(NULL, &pll_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(pll_clk);
+ return NULL;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ return clk;
+}
+
+void __init socfpga_pll_init(struct device_node *node)
+{
+ __socfpga_pll_init(node, &clk_pll_ops);
+}
diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c
index 5983a26a8c5f..35a960a993f9 100644
--- a/drivers/clk/socfpga/clk.c
+++ b/drivers/clk/socfpga/clk.c
@@ -22,325 +22,23 @@
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_address.h>
-/* Clock Manager offsets */
-#define CLKMGR_CTRL 0x0
-#define CLKMGR_BYPASS 0x4
-#define CLKMGR_L4SRC 0x70
-#define CLKMGR_PERPLL_SRC 0xAC
+#include "clk.h"
-/* Clock bypass bits */
-#define MAINPLL_BYPASS (1<<0)
-#define SDRAMPLL_BYPASS (1<<1)
-#define SDRAMPLL_SRC_BYPASS (1<<2)
-#define PERPLL_BYPASS (1<<3)
-#define PERPLL_SRC_BYPASS (1<<4)
+void __iomem *clk_mgr_base_addr;
-#define SOCFPGA_PLL_BG_PWRDWN 0
-#define SOCFPGA_PLL_EXT_ENA 1
-#define SOCFPGA_PLL_PWR_DOWN 2
-#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
-#define SOCFPGA_PLL_DIVF_SHIFT 3
-#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
-#define SOCFPGA_PLL_DIVQ_SHIFT 16
-#define SOCFGPA_MAX_PARENTS 3
-
-#define SOCFPGA_L4_MP_CLK "l4_mp_clk"
-#define SOCFPGA_L4_SP_CLK "l4_sp_clk"
-#define SOCFPGA_NAND_CLK "nand_clk"
-#define SOCFPGA_NAND_X_CLK "nand_x_clk"
-#define SOCFPGA_MMC_CLK "sdmmc_clk"
-#define SOCFPGA_DB_CLK "gpio_db_clk"
-
-#define div_mask(width) ((1 << (width)) - 1)
-#define streq(a, b) (strcmp((a), (b)) == 0)
-
-extern void __iomem *clk_mgr_base_addr;
-
-struct socfpga_clk {
- struct clk_gate hw;
- char *parent_name;
- char *clk_name;
- u32 fixed_div;
- void __iomem *div_reg;
- u32 width; /* only valid if div_reg != 0 */
- u32 shift; /* only valid if div_reg != 0 */
-};
-#define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw)
-
-static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
- unsigned long parent_rate)
-{
- struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
- unsigned long divf, divq, vco_freq, reg;
- unsigned long bypass;
-
- reg = readl(socfpgaclk->hw.reg);
- bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS);
- if (bypass & MAINPLL_BYPASS)
- return parent_rate;
-
- divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
- divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
- vco_freq = parent_rate * (divf + 1);
- return vco_freq / (1 + divq);
-}
-
-
-static struct clk_ops clk_pll_ops = {
- .recalc_rate = clk_pll_recalc_rate,
-};
-
-static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
- unsigned long parent_rate)
-{
- struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
- u32 div;
-
- if (socfpgaclk->fixed_div)
- div = socfpgaclk->fixed_div;
- else
- div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1);
-
- return parent_rate / div;
-}
-
-static const struct clk_ops periclk_ops = {
- .recalc_rate = clk_periclk_recalc_rate,
-};
-
-static __init struct clk *socfpga_clk_init(struct device_node *node,
- const struct clk_ops *ops)
-{
- u32 reg;
- struct clk *clk;
- struct socfpga_clk *socfpga_clk;
- const char *clk_name = node->name;
- const char *parent_name;
- struct clk_init_data init;
- int rc;
- u32 fixed_div;
-
- of_property_read_u32(node, "reg", &reg);
-
- socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
- if (WARN_ON(!socfpga_clk))
- return NULL;
-
- socfpga_clk->hw.reg = clk_mgr_base_addr + reg;
-
- rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
- if (rc)
- socfpga_clk->fixed_div = 0;
- else
- socfpga_clk->fixed_div = fixed_div;
-
- of_property_read_string(node, "clock-output-names", &clk_name);
-
- init.name = clk_name;
- init.ops = ops;
- init.flags = 0;
- parent_name = of_clk_get_parent_name(node, 0);
- init.parent_names = &parent_name;
- init.num_parents = 1;
-
- socfpga_clk->hw.hw.init = &init;
-
- if (streq(clk_name, "main_pll") ||
- streq(clk_name, "periph_pll") ||
- streq(clk_name, "sdram_pll")) {
- socfpga_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
- clk_pll_ops.enable = clk_gate_ops.enable;
- clk_pll_ops.disable = clk_gate_ops.disable;
- }
-
- clk = clk_register(NULL, &socfpga_clk->hw.hw);
- if (WARN_ON(IS_ERR(clk))) {
- kfree(socfpga_clk);
- return NULL;
- }
- rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
- return clk;
-}
-
-static u8 socfpga_clk_get_parent(struct clk_hw *hwclk)
-{
- u32 l4_src;
- u32 perpll_src;
-
- if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
- l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
- return l4_src &= 0x1;
- }
- if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
- l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
- return !!(l4_src & 2);
- }
-
- perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
- if (streq(hwclk->init->name, SOCFPGA_MMC_CLK))
- return perpll_src &= 0x3;
- if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
- streq(hwclk->init->name, SOCFPGA_NAND_X_CLK))
- return (perpll_src >> 2) & 3;
-
- /* QSPI clock */
- return (perpll_src >> 4) & 3;
-
-}
-
-static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent)
-{
- u32 src_reg;
-
- if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
- src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
- src_reg &= ~0x1;
- src_reg |= parent;
- writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
- } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
- src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
- src_reg &= ~0x2;
- src_reg |= (parent << 1);
- writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
- } else {
- src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
- if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) {
- src_reg &= ~0x3;
- src_reg |= parent;
- } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
- streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) {
- src_reg &= ~0xC;
- src_reg |= (parent << 2);
- } else {/* QSPI clock */
- src_reg &= ~0x30;
- src_reg |= (parent << 4);
- }
- writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
- }
-
- return 0;
-}
-
-static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk,
- unsigned long parent_rate)
-{
- struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
- u32 div = 1, val;
-
- if (socfpgaclk->fixed_div)
- div = socfpgaclk->fixed_div;
- else if (socfpgaclk->div_reg) {
- val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
- val &= div_mask(socfpgaclk->width);
- if (streq(hwclk->init->name, SOCFPGA_DB_CLK))
- div = val + 1;
- else
- div = (1 << val);
- }
-
- return parent_rate / div;
-}
-
-static struct clk_ops gateclk_ops = {
- .recalc_rate = socfpga_clk_recalc_rate,
- .get_parent = socfpga_clk_get_parent,
- .set_parent = socfpga_clk_set_parent,
+static const struct of_device_id socfpga_child_clocks[] __initconst = {
+ { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, },
+ { .compatible = "altr,socfpga-perip-clk", socfpga_periph_init, },
+ { .compatible = "altr,socfpga-gate-clk", socfpga_gate_init, },
+ {},
};
-static void __init socfpga_gate_clk_init(struct device_node *node,
- const struct clk_ops *ops)
-{
- u32 clk_gate[2];
- u32 div_reg[3];
- u32 fixed_div;
- struct clk *clk;
- struct socfpga_clk *socfpga_clk;
- const char *clk_name = node->name;
- const char *parent_name[SOCFGPA_MAX_PARENTS];
- struct clk_init_data init;
- int rc;
- int i = 0;
-
- socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
- if (WARN_ON(!socfpga_clk))
- return;
-
- rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
- if (rc)
- clk_gate[0] = 0;
-
- if (clk_gate[0]) {
- socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0];
- socfpga_clk->hw.bit_idx = clk_gate[1];
-
- gateclk_ops.enable = clk_gate_ops.enable;
- gateclk_ops.disable = clk_gate_ops.disable;
- }
-
- rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
- if (rc)
- socfpga_clk->fixed_div = 0;
- else
- socfpga_clk->fixed_div = fixed_div;
-
- rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
- if (!rc) {
- socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0];
- socfpga_clk->shift = div_reg[1];
- socfpga_clk->width = div_reg[2];
- } else {
- socfpga_clk->div_reg = NULL;
- }
-
- of_property_read_string(node, "clock-output-names", &clk_name);
-
- init.name = clk_name;
- init.ops = ops;
- init.flags = 0;
- while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] =
- of_clk_get_parent_name(node, i)) != NULL)
- i++;
-
- init.parent_names = parent_name;
- init.num_parents = i;
- socfpga_clk->hw.hw.init = &init;
-
- clk = clk_register(NULL, &socfpga_clk->hw.hw);
- if (WARN_ON(IS_ERR(clk))) {
- kfree(socfpga_clk);
- return;
- }
- rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
- if (WARN_ON(rc))
- return;
-}
-
-static void __init socfpga_pll_init(struct device_node *node)
+static void __init socfpga_clkmgr_init(struct device_node *node)
{
- socfpga_clk_init(node, &clk_pll_ops);
+ clk_mgr_base_addr = of_iomap(node, 0);
+ of_clk_init(socfpga_child_clocks);
}
-CLK_OF_DECLARE(socfpga_pll, "altr,socfpga-pll-clock", socfpga_pll_init);
+CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_init);
-static void __init socfpga_periph_init(struct device_node *node)
-{
- socfpga_clk_init(node, &periclk_ops);
-}
-CLK_OF_DECLARE(socfpga_periph, "altr,socfpga-perip-clk", socfpga_periph_init);
-
-static void __init socfpga_gate_init(struct device_node *node)
-{
- socfpga_gate_clk_init(node, &gateclk_ops);
-}
-CLK_OF_DECLARE(socfpga_gate, "altr,socfpga-gate-clk", socfpga_gate_init);
-
-void __init socfpga_init_clocks(void)
-{
- struct clk *clk;
- int ret;
-
- clk = clk_register_fixed_factor(NULL, "smp_twd", "mpuclk", 0, 1, 4);
- ret = clk_register_clkdev(clk, NULL, "smp_twd");
- if (ret)
- pr_err("smp_twd alias not registered\n");
-}
diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h
new file mode 100644
index 000000000000..d2e54019c94f
--- /dev/null
+++ b/drivers/clk/socfpga/clk.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013, Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * based on drivers/clk/tegra/clk.h
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __SOCFPGA_CLK_H
+#define __SOCFPGA_CLK_H
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+/* Clock Manager offsets */
+#define CLKMGR_CTRL 0x0
+#define CLKMGR_BYPASS 0x4
+#define CLKMGR_L4SRC 0x70
+#define CLKMGR_PERPLL_SRC 0xAC
+
+#define SOCFPGA_MAX_PARENTS 3
+
+extern void __iomem *clk_mgr_base_addr;
+
+void __init socfpga_pll_init(struct device_node *node);
+void __init socfpga_periph_init(struct device_node *node);
+void __init socfpga_gate_init(struct device_node *node);
+
+struct socfpga_pll {
+ struct clk_gate hw;
+};
+
+struct socfpga_gate_clk {
+ struct clk_gate hw;
+ char *parent_name;
+ u32 fixed_div;
+ void __iomem *div_reg;
+ u32 width; /* only valid if div_reg != 0 */
+ u32 shift; /* only valid if div_reg != 0 */
+ u32 clk_phase[2];
+};
+
+struct socfpga_periph_clk {
+ struct clk_gate hw;
+ char *parent_name;
+ u32 fixed_div;
+};
+
+#endif /* SOCFPGA_CLK_H */
diff --git a/drivers/clk/st/Makefile b/drivers/clk/st/Makefile
new file mode 100644
index 000000000000..c7455ffdbdf7
--- /dev/null
+++ b/drivers/clk/st/Makefile
@@ -0,0 +1 @@
+obj-y += clkgen-mux.o clkgen-pll.o clkgen-fsyn.o
diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c
new file mode 100644
index 000000000000..4f53ee0778d9
--- /dev/null
+++ b/drivers/clk/st/clkgen-fsyn.c
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics R&D 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.
+ *
+ */
+
+/*
+ * Authors:
+ * Stephen Gallimore <stephen.gallimore@st.com>,
+ * Pankaj Dev <pankaj.dev@st.com>.
+ */
+
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/clk-provider.h>
+
+#include "clkgen.h"
+
+/*
+ * Maximum input clock to the PLL before we divide it down by 2
+ * although in reality in actual systems this has never been seen to
+ * be used.
+ */
+#define QUADFS_NDIV_THRESHOLD 30000000
+
+#define PLL_BW_GOODREF (0L)
+#define PLL_BW_VBADREF (1L)
+#define PLL_BW_BADREF (2L)
+#define PLL_BW_VGOODREF (3L)
+
+#define QUADFS_MAX_CHAN 4
+
+struct stm_fs {
+ unsigned long ndiv;
+ unsigned long mdiv;
+ unsigned long pe;
+ unsigned long sdiv;
+ unsigned long nsdiv;
+};
+
+static struct stm_fs fs216c65_rtbl[] = {
+ { .mdiv = 0x1f, .pe = 0x0, .sdiv = 0x7, .nsdiv = 0 }, /* 312.5 Khz */
+ { .mdiv = 0x17, .pe = 0x25ed, .sdiv = 0x1, .nsdiv = 0 }, /* 27 MHz */
+ { .mdiv = 0x1a, .pe = 0x7b36, .sdiv = 0x2, .nsdiv = 1 }, /* 36.87 MHz */
+ { .mdiv = 0x13, .pe = 0x0, .sdiv = 0x2, .nsdiv = 1 }, /* 48 MHz */
+ { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x1, .nsdiv = 1 }, /* 108 MHz */
+};
+
+static struct stm_fs fs432c65_rtbl[] = {
+ { .mdiv = 0x1f, .pe = 0x0, .sdiv = 0x7, .nsdiv = 0 }, /* 625 Khz */
+ { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x2, .nsdiv = 1 }, /* 108 MHz */
+ { .mdiv = 0x19, .pe = 0x121a, .sdiv = 0x0, .nsdiv = 1 }, /* 297 MHz */
+};
+
+static struct stm_fs fs660c32_rtbl[] = {
+ { .mdiv = 0x01, .pe = 0x2aaa, .sdiv = 0x8, .nsdiv = 0 }, /* 600 KHz */
+ { .mdiv = 0x02, .pe = 0x3d33, .sdiv = 0x0, .nsdiv = 0 }, /* 148.5 Mhz */
+ { .mdiv = 0x13, .pe = 0x5bcc, .sdiv = 0x0, .nsdiv = 1 }, /* 297 Mhz */
+ { .mdiv = 0x0e, .pe = 0x1025, .sdiv = 0x0, .nsdiv = 1 }, /* 333 Mhz */
+ { .mdiv = 0x0b, .pe = 0x715f, .sdiv = 0x0, .nsdiv = 1 }, /* 350 Mhz */
+};
+
+struct clkgen_quadfs_data {
+ bool reset_present;
+ bool bwfilter_present;
+ bool lockstatus_present;
+ bool nsdiv_present;
+ struct clkgen_field ndiv;
+ struct clkgen_field ref_bw;
+ struct clkgen_field nreset;
+ struct clkgen_field npda;
+ struct clkgen_field lock_status;
+
+ struct clkgen_field nsb[QUADFS_MAX_CHAN];
+ struct clkgen_field en[QUADFS_MAX_CHAN];
+ struct clkgen_field mdiv[QUADFS_MAX_CHAN];
+ struct clkgen_field pe[QUADFS_MAX_CHAN];
+ struct clkgen_field sdiv[QUADFS_MAX_CHAN];
+ struct clkgen_field nsdiv[QUADFS_MAX_CHAN];
+
+ const struct clk_ops *pll_ops;
+ struct stm_fs *rtbl;
+ u8 rtbl_cnt;
+ int (*get_rate)(unsigned long , struct stm_fs *,
+ unsigned long *);
+};
+
+static const struct clk_ops st_quadfs_pll_c65_ops;
+static const struct clk_ops st_quadfs_pll_c32_ops;
+static const struct clk_ops st_quadfs_fs216c65_ops;
+static const struct clk_ops st_quadfs_fs432c65_ops;
+static const struct clk_ops st_quadfs_fs660c32_ops;
+
+static int clk_fs216c65_get_rate(unsigned long, struct stm_fs *,
+ unsigned long *);
+static int clk_fs432c65_get_rate(unsigned long, struct stm_fs *,
+ unsigned long *);
+static int clk_fs660c32_dig_get_rate(unsigned long, struct stm_fs *,
+ unsigned long *);
+/*
+ * Values for all of the standalone instances of this clock
+ * generator found in STiH415 and STiH416 SYSCFG register banks. Note
+ * that the individual channel standby control bits (nsb) are in the
+ * first register along with the PLL control bits.
+ */
+static struct clkgen_quadfs_data st_fs216c65_416 = {
+ /* 416 specific */
+ .npda = CLKGEN_FIELD(0x0, 0x1, 14),
+ .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
+ CLKGEN_FIELD(0x0, 0x1, 11),
+ CLKGEN_FIELD(0x0, 0x1, 12),
+ CLKGEN_FIELD(0x0, 0x1, 13) },
+ .nsdiv_present = true,
+ .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
+ CLKGEN_FIELD(0x0, 0x1, 19),
+ CLKGEN_FIELD(0x0, 0x1, 20),
+ CLKGEN_FIELD(0x0, 0x1, 21) },
+ .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
+ CLKGEN_FIELD(0x14, 0x1f, 0),
+ CLKGEN_FIELD(0x24, 0x1f, 0),
+ CLKGEN_FIELD(0x34, 0x1f, 0) },
+ .en = { CLKGEN_FIELD(0x10, 0x1, 0),
+ CLKGEN_FIELD(0x20, 0x1, 0),
+ CLKGEN_FIELD(0x30, 0x1, 0),
+ CLKGEN_FIELD(0x40, 0x1, 0) },
+ .ndiv = CLKGEN_FIELD(0x0, 0x1, 15),
+ .bwfilter_present = true,
+ .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16),
+ .pe = { CLKGEN_FIELD(0x8, 0xffff, 0),
+ CLKGEN_FIELD(0x18, 0xffff, 0),
+ CLKGEN_FIELD(0x28, 0xffff, 0),
+ CLKGEN_FIELD(0x38, 0xffff, 0) },
+ .sdiv = { CLKGEN_FIELD(0xC, 0x7, 0),
+ CLKGEN_FIELD(0x1C, 0x7, 0),
+ CLKGEN_FIELD(0x2C, 0x7, 0),
+ CLKGEN_FIELD(0x3C, 0x7, 0) },
+ .pll_ops = &st_quadfs_pll_c65_ops,
+ .rtbl = fs216c65_rtbl,
+ .rtbl_cnt = ARRAY_SIZE(fs216c65_rtbl),
+ .get_rate = clk_fs216c65_get_rate,
+};
+
+static struct clkgen_quadfs_data st_fs432c65_416 = {
+ .npda = CLKGEN_FIELD(0x0, 0x1, 14),
+ .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
+ CLKGEN_FIELD(0x0, 0x1, 11),
+ CLKGEN_FIELD(0x0, 0x1, 12),
+ CLKGEN_FIELD(0x0, 0x1, 13) },
+ .nsdiv_present = true,
+ .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
+ CLKGEN_FIELD(0x0, 0x1, 19),
+ CLKGEN_FIELD(0x0, 0x1, 20),
+ CLKGEN_FIELD(0x0, 0x1, 21) },
+ .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
+ CLKGEN_FIELD(0x14, 0x1f, 0),
+ CLKGEN_FIELD(0x24, 0x1f, 0),
+ CLKGEN_FIELD(0x34, 0x1f, 0) },
+ .en = { CLKGEN_FIELD(0x10, 0x1, 0),
+ CLKGEN_FIELD(0x20, 0x1, 0),
+ CLKGEN_FIELD(0x30, 0x1, 0),
+ CLKGEN_FIELD(0x40, 0x1, 0) },
+ .ndiv = CLKGEN_FIELD(0x0, 0x1, 15),
+ .bwfilter_present = true,
+ .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16),
+ .pe = { CLKGEN_FIELD(0x8, 0xffff, 0),
+ CLKGEN_FIELD(0x18, 0xffff, 0),
+ CLKGEN_FIELD(0x28, 0xffff, 0),
+ CLKGEN_FIELD(0x38, 0xffff, 0) },
+ .sdiv = { CLKGEN_FIELD(0xC, 0x7, 0),
+ CLKGEN_FIELD(0x1C, 0x7, 0),
+ CLKGEN_FIELD(0x2C, 0x7, 0),
+ CLKGEN_FIELD(0x3C, 0x7, 0) },
+ .pll_ops = &st_quadfs_pll_c65_ops,
+ .rtbl = fs432c65_rtbl,
+ .rtbl_cnt = ARRAY_SIZE(fs432c65_rtbl),
+ .get_rate = clk_fs432c65_get_rate,
+};
+
+static struct clkgen_quadfs_data st_fs660c32_E_416 = {
+ .npda = CLKGEN_FIELD(0x0, 0x1, 14),
+ .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
+ CLKGEN_FIELD(0x0, 0x1, 11),
+ CLKGEN_FIELD(0x0, 0x1, 12),
+ CLKGEN_FIELD(0x0, 0x1, 13) },
+ .nsdiv_present = true,
+ .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
+ CLKGEN_FIELD(0x0, 0x1, 19),
+ CLKGEN_FIELD(0x0, 0x1, 20),
+ CLKGEN_FIELD(0x0, 0x1, 21) },
+ .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
+ CLKGEN_FIELD(0x14, 0x1f, 0),
+ CLKGEN_FIELD(0x24, 0x1f, 0),
+ CLKGEN_FIELD(0x34, 0x1f, 0) },
+ .en = { CLKGEN_FIELD(0x10, 0x1, 0),
+ CLKGEN_FIELD(0x20, 0x1, 0),
+ CLKGEN_FIELD(0x30, 0x1, 0),
+ CLKGEN_FIELD(0x40, 0x1, 0) },
+ .ndiv = CLKGEN_FIELD(0x0, 0x7, 15),
+ .pe = { CLKGEN_FIELD(0x8, 0x7fff, 0),
+ CLKGEN_FIELD(0x18, 0x7fff, 0),
+ CLKGEN_FIELD(0x28, 0x7fff, 0),
+ CLKGEN_FIELD(0x38, 0x7fff, 0) },
+ .sdiv = { CLKGEN_FIELD(0xC, 0xf, 0),
+ CLKGEN_FIELD(0x1C, 0xf, 0),
+ CLKGEN_FIELD(0x2C, 0xf, 0),
+ CLKGEN_FIELD(0x3C, 0xf, 0) },
+ .lockstatus_present = true,
+ .lock_status = CLKGEN_FIELD(0xAC, 0x1, 0),
+ .pll_ops = &st_quadfs_pll_c32_ops,
+ .rtbl = fs660c32_rtbl,
+ .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl),
+ .get_rate = clk_fs660c32_dig_get_rate,
+};
+
+static struct clkgen_quadfs_data st_fs660c32_F_416 = {
+ .npda = CLKGEN_FIELD(0x0, 0x1, 14),
+ .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
+ CLKGEN_FIELD(0x0, 0x1, 11),
+ CLKGEN_FIELD(0x0, 0x1, 12),
+ CLKGEN_FIELD(0x0, 0x1, 13) },
+ .nsdiv_present = true,
+ .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
+ CLKGEN_FIELD(0x0, 0x1, 19),
+ CLKGEN_FIELD(0x0, 0x1, 20),
+ CLKGEN_FIELD(0x0, 0x1, 21) },
+ .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
+ CLKGEN_FIELD(0x14, 0x1f, 0),
+ CLKGEN_FIELD(0x24, 0x1f, 0),
+ CLKGEN_FIELD(0x34, 0x1f, 0) },
+ .en = { CLKGEN_FIELD(0x10, 0x1, 0),
+ CLKGEN_FIELD(0x20, 0x1, 0),
+ CLKGEN_FIELD(0x30, 0x1, 0),
+ CLKGEN_FIELD(0x40, 0x1, 0) },
+ .ndiv = CLKGEN_FIELD(0x0, 0x7, 15),
+ .pe = { CLKGEN_FIELD(0x8, 0x7fff, 0),
+ CLKGEN_FIELD(0x18, 0x7fff, 0),
+ CLKGEN_FIELD(0x28, 0x7fff, 0),
+ CLKGEN_FIELD(0x38, 0x7fff, 0) },
+ .sdiv = { CLKGEN_FIELD(0xC, 0xf, 0),
+ CLKGEN_FIELD(0x1C, 0xf, 0),
+ CLKGEN_FIELD(0x2C, 0xf, 0),
+ CLKGEN_FIELD(0x3C, 0xf, 0) },
+ .lockstatus_present = true,
+ .lock_status = CLKGEN_FIELD(0xEC, 0x1, 0),
+ .pll_ops = &st_quadfs_pll_c32_ops,
+ .rtbl = fs660c32_rtbl,
+ .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl),
+ .get_rate = clk_fs660c32_dig_get_rate,
+};
+
+/**
+ * DOC: A Frequency Synthesizer that multiples its input clock by a fixed factor
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control the Fsyn
+ * rate - inherits rate from parent. set_rate/round_rate/recalc_rate
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+/**
+ * struct st_clk_quadfs_pll - A pll which outputs a fixed multiplier of
+ * its parent clock, found inside a type of
+ * ST quad channel frequency synthesizer block
+ *
+ * @hw: handle between common and hardware-specific interfaces.
+ * @ndiv: regmap field for the ndiv control.
+ * @regs_base: base address of the configuration registers.
+ * @lock: spinlock.
+ *
+ */
+struct st_clk_quadfs_pll {
+ struct clk_hw hw;
+ void __iomem *regs_base;
+ spinlock_t *lock;
+ struct clkgen_quadfs_data *data;
+ u32 ndiv;
+};
+
+#define to_quadfs_pll(_hw) container_of(_hw, struct st_clk_quadfs_pll, hw)
+
+static int quadfs_pll_enable(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+ unsigned long flags = 0, timeout = jiffies + msecs_to_jiffies(10);
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ /*
+ * Bring block out of reset if we have reset control.
+ */
+ if (pll->data->reset_present)
+ CLKGEN_WRITE(pll, nreset, 1);
+
+ /*
+ * Use a fixed input clock noise bandwidth filter for the moment
+ */
+ if (pll->data->bwfilter_present)
+ CLKGEN_WRITE(pll, ref_bw, PLL_BW_GOODREF);
+
+
+ CLKGEN_WRITE(pll, ndiv, pll->ndiv);
+
+ /*
+ * Power up the PLL
+ */
+ CLKGEN_WRITE(pll, npda, 1);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ if (pll->data->lockstatus_present)
+ while (!CLKGEN_READ(pll, lock_status)) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static void quadfs_pll_disable(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+ unsigned long flags = 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ /*
+ * Powerdown the PLL and then put block into soft reset if we have
+ * reset control.
+ */
+ CLKGEN_WRITE(pll, npda, 0);
+
+ if (pll->data->reset_present)
+ CLKGEN_WRITE(pll, nreset, 0);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int quadfs_pll_is_enabled(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+ u32 npda = CLKGEN_READ(pll, npda);
+
+ return !!npda;
+}
+
+int clk_fs660c32_vco_get_rate(unsigned long input, struct stm_fs *fs,
+ unsigned long *rate)
+{
+ unsigned long nd = fs->ndiv + 16; /* ndiv value */
+
+ *rate = input * nd;
+
+ return 0;
+}
+
+static unsigned long quadfs_pll_fs660c32_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+ unsigned long rate = 0;
+ struct stm_fs params;
+
+ params.ndiv = CLKGEN_READ(pll, ndiv);
+ if (clk_fs660c32_vco_get_rate(parent_rate, &params, &rate))
+ pr_err("%s:%s error calculating rate\n",
+ __clk_get_name(hw->clk), __func__);
+
+ pll->ndiv = params.ndiv;
+
+ return rate;
+}
+
+int clk_fs660c32_vco_get_params(unsigned long input,
+ unsigned long output, struct stm_fs *fs)
+{
+/* Formula
+ VCO frequency = (fin x ndiv) / pdiv
+ ndiv = VCOfreq * pdiv / fin
+ */
+ unsigned long pdiv = 1, n;
+
+ /* Output clock range: 384Mhz to 660Mhz */
+ if (output < 384000000 || output > 660000000)
+ return -EINVAL;
+
+ if (input > 40000000)
+ /* This means that PDIV would be 2 instead of 1.
+ Not supported today. */
+ return -EINVAL;
+
+ input /= 1000;
+ output /= 1000;
+
+ n = output * pdiv / input;
+ if (n < 16)
+ n = 16;
+ fs->ndiv = n - 16; /* Converting formula value to reg value */
+
+ return 0;
+}
+
+static long quadfs_pll_fs660c32_round_rate(struct clk_hw *hw, unsigned long rate
+ , unsigned long *prate)
+{
+ struct stm_fs params;
+
+ if (!clk_fs660c32_vco_get_params(*prate, rate, &params))
+ clk_fs660c32_vco_get_rate(*prate, &params, &rate);
+
+ pr_debug("%s: %s new rate %ld [sdiv=0x%x,md=0x%x,pe=0x%x,nsdiv3=%u]\n",
+ __func__, __clk_get_name(hw->clk),
+ rate, (unsigned int)params.sdiv,
+ (unsigned int)params.mdiv,
+ (unsigned int)params.pe, (unsigned int)params.nsdiv);
+
+ return rate;
+}
+
+static int quadfs_pll_fs660c32_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+ struct stm_fs params;
+ long hwrate = 0;
+ unsigned long flags = 0;
+
+ if (!rate || !parent_rate)
+ return -EINVAL;
+
+ if (!clk_fs660c32_vco_get_params(parent_rate, rate, &params))
+ clk_fs660c32_vco_get_rate(parent_rate, &params, &hwrate);
+
+ pr_debug("%s: %s new rate %ld [ndiv=0x%x]\n",
+ __func__, __clk_get_name(hw->clk),
+ hwrate, (unsigned int)params.ndiv);
+
+ if (!hwrate)
+ return -EINVAL;
+
+ pll->ndiv = params.ndiv;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ CLKGEN_WRITE(pll, ndiv, pll->ndiv);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops st_quadfs_pll_c65_ops = {
+ .enable = quadfs_pll_enable,
+ .disable = quadfs_pll_disable,
+ .is_enabled = quadfs_pll_is_enabled,
+};
+
+static const struct clk_ops st_quadfs_pll_c32_ops = {
+ .enable = quadfs_pll_enable,
+ .disable = quadfs_pll_disable,
+ .is_enabled = quadfs_pll_is_enabled,
+ .recalc_rate = quadfs_pll_fs660c32_recalc_rate,
+ .round_rate = quadfs_pll_fs660c32_round_rate,
+ .set_rate = quadfs_pll_fs660c32_set_rate,
+};
+
+static struct clk * __init st_clk_register_quadfs_pll(
+ const char *name, const char *parent_name,
+ struct clkgen_quadfs_data *quadfs, void __iomem *reg,
+ spinlock_t *lock)
+{
+ struct st_clk_quadfs_pll *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /*
+ * Sanity check required pointers.
+ */
+ if (WARN_ON(!name || !parent_name))
+ return ERR_PTR(-EINVAL);
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = quadfs->pll_ops;
+ init.flags = CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ pll->data = quadfs;
+ pll->regs_base = reg;
+ pll->lock = lock;
+ pll->hw.init = &init;
+
+ clk = clk_register(NULL, &pll->hw);
+
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+/**
+ * DOC: A digital frequency synthesizer
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional
+ * rate - set rate is functional
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+/**
+ * struct st_clk_quadfs_fsynth - One clock output from a four channel digital
+ * frequency synthesizer (fsynth) block.
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ *
+ * @nsb: regmap field in the output control register for the digital
+ * standby of this fsynth channel. This control is active low so
+ * the channel is in standby when the control bit is cleared.
+ *
+ * @nsdiv: regmap field in the output control register for
+ * for the optional divide by 3 of this fsynth channel. This control
+ * is active low so the divide by 3 is active when the control bit is
+ * cleared and the divide is bypassed when the bit is set.
+ */
+struct st_clk_quadfs_fsynth {
+ struct clk_hw hw;
+ void __iomem *regs_base;
+ spinlock_t *lock;
+ struct clkgen_quadfs_data *data;
+
+ u32 chan;
+ /*
+ * Cached hardware values from set_rate so we can program the
+ * hardware in enable. There are two reasons for this:
+ *
+ * 1. The registers may not be writable until the parent has been
+ * enabled.
+ *
+ * 2. It restores the clock rate when a driver does an enable
+ * on PM restore, after a suspend to RAM has lost the hardware
+ * setup.
+ */
+ u32 md;
+ u32 pe;
+ u32 sdiv;
+ u32 nsdiv;
+};
+
+#define to_quadfs_fsynth(_hw) \
+ container_of(_hw, struct st_clk_quadfs_fsynth, hw)
+
+static void quadfs_fsynth_program_enable(struct st_clk_quadfs_fsynth *fs)
+{
+ /*
+ * Pulse the program enable register lsb to make the hardware take
+ * notice of the new md/pe values with a glitchless transition.
+ */
+ CLKGEN_WRITE(fs, en[fs->chan], 1);
+ CLKGEN_WRITE(fs, en[fs->chan], 0);
+}
+
+static void quadfs_fsynth_program_rate(struct st_clk_quadfs_fsynth *fs)
+{
+ unsigned long flags = 0;
+
+ /*
+ * Ensure the md/pe parameters are ignored while we are
+ * reprogramming them so we can get a glitchless change
+ * when fine tuning the speed of a running clock.
+ */
+ CLKGEN_WRITE(fs, en[fs->chan], 0);
+
+ CLKGEN_WRITE(fs, mdiv[fs->chan], fs->md);
+ CLKGEN_WRITE(fs, pe[fs->chan], fs->pe);
+ CLKGEN_WRITE(fs, sdiv[fs->chan], fs->sdiv);
+
+ if (fs->lock)
+ spin_lock_irqsave(fs->lock, flags);
+
+ if (fs->data->nsdiv_present)
+ CLKGEN_WRITE(fs, nsdiv[fs->chan], fs->nsdiv);
+
+ if (fs->lock)
+ spin_unlock_irqrestore(fs->lock, flags);
+}
+
+static int quadfs_fsynth_enable(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ unsigned long flags = 0;
+
+ pr_debug("%s: %s\n", __func__, __clk_get_name(hw->clk));
+
+ quadfs_fsynth_program_rate(fs);
+
+ if (fs->lock)
+ spin_lock_irqsave(fs->lock, flags);
+
+ CLKGEN_WRITE(fs, nsb[fs->chan], 1);
+
+ if (fs->lock)
+ spin_unlock_irqrestore(fs->lock, flags);
+
+ quadfs_fsynth_program_enable(fs);
+
+ return 0;
+}
+
+static void quadfs_fsynth_disable(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ unsigned long flags = 0;
+
+ pr_debug("%s: %s\n", __func__, __clk_get_name(hw->clk));
+
+ if (fs->lock)
+ spin_lock_irqsave(fs->lock, flags);
+
+ CLKGEN_WRITE(fs, nsb[fs->chan], 0);
+
+ if (fs->lock)
+ spin_unlock_irqrestore(fs->lock, flags);
+}
+
+static int quadfs_fsynth_is_enabled(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ u32 nsb = CLKGEN_READ(fs, nsb[fs->chan]);
+
+ pr_debug("%s: %s enable bit = 0x%x\n",
+ __func__, __clk_get_name(hw->clk), nsb);
+
+ return !!nsb;
+}
+
+#define P15 (uint64_t)(1 << 15)
+
+static int clk_fs216c65_get_rate(unsigned long input, struct stm_fs *fs,
+ unsigned long *rate)
+{
+ uint64_t res;
+ unsigned long ns;
+ unsigned long nd = 8; /* ndiv stuck at 0 => val = 8 */
+ unsigned long s;
+ long m;
+
+ m = fs->mdiv - 32;
+ s = 1 << (fs->sdiv + 1);
+ ns = (fs->nsdiv ? 1 : 3);
+
+ res = (uint64_t)(s * ns * P15 * (uint64_t)(m + 33));
+ res = res - (s * ns * fs->pe);
+ *rate = div64_u64(P15 * nd * input * 32, res);
+
+ return 0;
+}
+
+static int clk_fs432c65_get_rate(unsigned long input, struct stm_fs *fs,
+ unsigned long *rate)
+{
+ uint64_t res;
+ unsigned long nd = 16; /* ndiv value; stuck at 0 (30Mhz input) */
+ long m;
+ unsigned long sd;
+ unsigned long ns;
+
+ m = fs->mdiv - 32;
+ sd = 1 << (fs->sdiv + 1);
+ ns = (fs->nsdiv ? 1 : 3);
+
+ res = (uint64_t)(sd * ns * P15 * (uint64_t)(m + 33));
+ res = res - (sd * ns * fs->pe);
+ *rate = div64_u64(P15 * nd * input * 32, res);
+
+ return 0;
+}
+
+#define P20 (uint64_t)(1 << 20)
+
+static int clk_fs660c32_dig_get_rate(unsigned long input,
+ struct stm_fs *fs, unsigned long *rate)
+{
+ unsigned long s = (1 << fs->sdiv);
+ unsigned long ns;
+ uint64_t res;
+
+ /*
+ * 'nsdiv' is a register value ('BIN') which is translated
+ * to a decimal value according to following rules.
+ *
+ * nsdiv ns.dec
+ * 0 3
+ * 1 1
+ */
+ ns = (fs->nsdiv == 1) ? 1 : 3;
+
+ res = (P20 * (32 + fs->mdiv) + 32 * fs->pe) * s * ns;
+ *rate = (unsigned long)div64_u64(input * P20 * 32, res);
+
+ return 0;
+}
+
+static int quadfs_fsynt_get_hw_value_for_recalc(struct st_clk_quadfs_fsynth *fs,
+ struct stm_fs *params)
+{
+ /*
+ * Get the initial hardware values for recalc_rate
+ */
+ params->mdiv = CLKGEN_READ(fs, mdiv[fs->chan]);
+ params->pe = CLKGEN_READ(fs, pe[fs->chan]);
+ params->sdiv = CLKGEN_READ(fs, sdiv[fs->chan]);
+
+ if (fs->data->nsdiv_present)
+ params->nsdiv = CLKGEN_READ(fs, nsdiv[fs->chan]);
+ else
+ params->nsdiv = 1;
+
+ /*
+ * If All are NULL then assume no clock rate is programmed.
+ */
+ if (!params->mdiv && !params->pe && !params->sdiv)
+ return 1;
+
+ fs->md = params->mdiv;
+ fs->pe = params->pe;
+ fs->sdiv = params->sdiv;
+ fs->nsdiv = params->nsdiv;
+
+ return 0;
+}
+
+static long quadfs_find_best_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate, struct stm_fs *params)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ int (*clk_fs_get_rate)(unsigned long ,
+ struct stm_fs *, unsigned long *);
+ struct stm_fs prev_params;
+ unsigned long prev_rate, rate = 0;
+ unsigned long diff_rate, prev_diff_rate = ~0;
+ int index;
+
+ clk_fs_get_rate = fs->data->get_rate;
+
+ for (index = 0; index < fs->data->rtbl_cnt; index++) {
+ prev_rate = rate;
+
+ *params = fs->data->rtbl[index];
+ prev_params = *params;
+
+ clk_fs_get_rate(prate, &fs->data->rtbl[index], &rate);
+
+ diff_rate = abs(drate - rate);
+
+ if (diff_rate > prev_diff_rate) {
+ rate = prev_rate;
+ *params = prev_params;
+ break;
+ }
+
+ prev_diff_rate = diff_rate;
+
+ if (drate == rate)
+ return rate;
+ }
+
+
+ if (index == fs->data->rtbl_cnt)
+ *params = prev_params;
+
+ return rate;
+}
+
+static unsigned long quadfs_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ unsigned long rate = 0;
+ struct stm_fs params;
+ int (*clk_fs_get_rate)(unsigned long ,
+ struct stm_fs *, unsigned long *);
+
+ clk_fs_get_rate = fs->data->get_rate;
+
+ if (quadfs_fsynt_get_hw_value_for_recalc(fs, &params))
+ return 0;
+
+ if (clk_fs_get_rate(parent_rate, &params, &rate)) {
+ pr_err("%s:%s error calculating rate\n",
+ __clk_get_name(hw->clk), __func__);
+ }
+
+ pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
+
+ return rate;
+}
+
+static long quadfs_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct stm_fs params;
+
+ rate = quadfs_find_best_rate(hw, rate, *prate, &params);
+
+ pr_debug("%s: %s new rate %ld [sdiv=0x%x,md=0x%x,pe=0x%x,nsdiv3=%u]\n",
+ __func__, __clk_get_name(hw->clk),
+ rate, (unsigned int)params.sdiv, (unsigned int)params.mdiv,
+ (unsigned int)params.pe, (unsigned int)params.nsdiv);
+
+ return rate;
+}
+
+
+static void quadfs_program_and_enable(struct st_clk_quadfs_fsynth *fs,
+ struct stm_fs *params)
+{
+ fs->md = params->mdiv;
+ fs->pe = params->pe;
+ fs->sdiv = params->sdiv;
+ fs->nsdiv = params->nsdiv;
+
+ /*
+ * In some integrations you can only change the fsynth programming when
+ * the parent entity containing it is enabled.
+ */
+ quadfs_fsynth_program_rate(fs);
+ quadfs_fsynth_program_enable(fs);
+}
+
+static int quadfs_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ struct stm_fs params;
+ long hwrate;
+ int uninitialized_var(i);
+
+ if (!rate || !parent_rate)
+ return -EINVAL;
+
+ memset(&params, 0, sizeof(struct stm_fs));
+
+ hwrate = quadfs_find_best_rate(hw, rate, parent_rate, &params);
+ if (!hwrate)
+ return -EINVAL;
+
+ quadfs_program_and_enable(fs, &params);
+
+ return 0;
+}
+
+
+
+static const struct clk_ops st_quadfs_ops = {
+ .enable = quadfs_fsynth_enable,
+ .disable = quadfs_fsynth_disable,
+ .is_enabled = quadfs_fsynth_is_enabled,
+ .round_rate = quadfs_round_rate,
+ .set_rate = quadfs_set_rate,
+ .recalc_rate = quadfs_recalc_rate,
+};
+
+static struct clk * __init st_clk_register_quadfs_fsynth(
+ const char *name, const char *parent_name,
+ struct clkgen_quadfs_data *quadfs, void __iomem *reg, u32 chan,
+ spinlock_t *lock)
+{
+ struct st_clk_quadfs_fsynth *fs;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /*
+ * Sanity check required pointers, note that nsdiv3 is optional.
+ */
+ if (WARN_ON(!name || !parent_name))
+ return ERR_PTR(-EINVAL);
+
+ fs = kzalloc(sizeof(*fs), GFP_KERNEL);
+ if (!fs)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &st_quadfs_ops;
+ init.flags = CLK_GET_RATE_NOCACHE | CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ fs->data = quadfs;
+ fs->regs_base = reg;
+ fs->chan = chan;
+ fs->lock = lock;
+ fs->hw.init = &init;
+
+ clk = clk_register(NULL, &fs->hw);
+
+ if (IS_ERR(clk))
+ kfree(fs);
+
+ return clk;
+}
+
+static struct of_device_id quadfs_of_match[] = {
+ {
+ .compatible = "st,stih416-quadfs216",
+ .data = (void *)&st_fs216c65_416
+ },
+ {
+ .compatible = "st,stih416-quadfs432",
+ .data = (void *)&st_fs432c65_416
+ },
+ {
+ .compatible = "st,stih416-quadfs660-E",
+ .data = (void *)&st_fs660c32_E_416
+ },
+ {
+ .compatible = "st,stih416-quadfs660-F",
+ .data = (void *)&st_fs660c32_F_416
+ },
+ {}
+};
+
+static void __init st_of_create_quadfs_fsynths(
+ struct device_node *np, const char *pll_name,
+ struct clkgen_quadfs_data *quadfs, void __iomem *reg,
+ spinlock_t *lock)
+{
+ struct clk_onecell_data *clk_data;
+ int fschan;
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->clk_num = QUADFS_MAX_CHAN;
+ clk_data->clks = kzalloc(QUADFS_MAX_CHAN * sizeof(struct clk *),
+ GFP_KERNEL);
+
+ if (!clk_data->clks) {
+ kfree(clk_data);
+ return;
+ }
+
+ for (fschan = 0; fschan < QUADFS_MAX_CHAN; fschan++) {
+ struct clk *clk;
+ const char *clk_name;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ fschan, &clk_name)) {
+ break;
+ }
+
+ /*
+ * If we read an empty clock name then the channel is unused
+ */
+ if (*clk_name == '\0')
+ continue;
+
+ clk = st_clk_register_quadfs_fsynth(clk_name, pll_name,
+ quadfs, reg, fschan, lock);
+
+ /*
+ * If there was an error registering this clock output, clean
+ * up and move on to the next one.
+ */
+ if (!IS_ERR(clk)) {
+ clk_data->clks[fschan] = clk;
+ pr_debug("%s: parent %s rate %u\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ (unsigned int)clk_get_rate(clk));
+ }
+ }
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+}
+
+static void __init st_of_quadfs_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ struct clk *clk;
+ const char *pll_name, *clk_parent_name;
+ void __iomem *reg;
+ spinlock_t *lock;
+
+ match = of_match_node(quadfs_of_match, np);
+ if (WARN_ON(!match))
+ return;
+
+ reg = of_iomap(np, 0);
+ if (!reg)
+ return;
+
+ clk_parent_name = of_clk_get_parent_name(np, 0);
+ if (!clk_parent_name)
+ return;
+
+ pll_name = kasprintf(GFP_KERNEL, "%s.pll", np->name);
+ if (!pll_name)
+ return;
+
+ lock = kzalloc(sizeof(*lock), GFP_KERNEL);
+ if (!lock)
+ goto err_exit;
+
+ spin_lock_init(lock);
+
+ clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name,
+ (struct clkgen_quadfs_data *) match->data, reg, lock);
+ if (IS_ERR(clk))
+ goto err_exit;
+ else
+ pr_debug("%s: parent %s rate %u\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ (unsigned int)clk_get_rate(clk));
+
+ st_of_create_quadfs_fsynths(np, pll_name,
+ (struct clkgen_quadfs_data *)match->data,
+ reg, lock);
+
+err_exit:
+ kfree(pll_name); /* No longer need local copy of the PLL name */
+}
+CLK_OF_DECLARE(quadfs, "st,quadfs", st_of_quadfs_setup);
diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c
new file mode 100644
index 000000000000..a329906d1e81
--- /dev/null
+++ b/drivers/clk/st/clkgen-mux.c
@@ -0,0 +1,820 @@
+/*
+ * clkgen-mux.c: ST GEN-MUX Clock driver
+ *
+ * Copyright (C) 2014 STMicroelectronics (R&D) Limited
+ *
+ * Authors: Stephen Gallimore <stephen.gallimore@st.com>
+ * Pankaj Dev <pankaj.dev@st.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/slab.h>
+#include <linux/of_address.h>
+#include <linux/clk-provider.h>
+
+static DEFINE_SPINLOCK(clkgena_divmux_lock);
+static DEFINE_SPINLOCK(clkgenf_lock);
+
+static const char ** __init clkgen_mux_get_parents(struct device_node *np,
+ int *num_parents)
+{
+ const char **parents;
+ int nparents, i;
+
+ nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
+ if (WARN_ON(nparents <= 0))
+ return ERR_PTR(-EINVAL);
+
+ parents = kzalloc(nparents * sizeof(const char *), GFP_KERNEL);
+ if (!parents)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < nparents; i++)
+ parents[i] = of_clk_get_parent_name(np, i);
+
+ *num_parents = nparents;
+ return parents;
+}
+
+/**
+ * DOC: Clock mux with a programmable divider on each of its three inputs.
+ * The mux has an input setting which effectively gates its output.
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gating
+ * rate - set rate is supported
+ * parent - set/get parent
+ */
+
+#define NUM_INPUTS 3
+
+struct clkgena_divmux {
+ struct clk_hw hw;
+ /* Subclassed mux and divider structures */
+ struct clk_mux mux;
+ struct clk_divider div[NUM_INPUTS];
+ /* Enable/running feedback register bits for each input */
+ void __iomem *feedback_reg[NUM_INPUTS];
+ int feedback_bit_idx;
+
+ u8 muxsel;
+};
+
+#define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw)
+
+struct clkgena_divmux_data {
+ int num_outputs;
+ int mux_offset;
+ int mux_offset2;
+ int mux_start_bit;
+ int div_offsets[NUM_INPUTS];
+ int fb_offsets[NUM_INPUTS];
+ int fb_start_bit_idx;
+};
+
+#define CKGAX_CLKOPSRC_SWITCH_OFF 0x3
+
+static int clkgena_divmux_is_running(struct clkgena_divmux *mux)
+{
+ u32 regval = readl(mux->feedback_reg[mux->muxsel]);
+ u32 running = regval & BIT(mux->feedback_bit_idx);
+ return !!running;
+}
+
+static int clkgena_divmux_enable(struct clk_hw *hw)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *mux_hw = &genamux->mux.hw;
+ unsigned long timeout;
+ int ret = 0;
+
+ mux_hw->clk = hw->clk;
+
+ ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel);
+ if (ret)
+ return ret;
+
+ timeout = jiffies + msecs_to_jiffies(10);
+
+ while (!clkgena_divmux_is_running(genamux)) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static void clkgena_divmux_disable(struct clk_hw *hw)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *mux_hw = &genamux->mux.hw;
+
+ mux_hw->clk = hw->clk;
+
+ clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF);
+}
+
+static int clkgena_divmux_is_enabled(struct clk_hw *hw)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *mux_hw = &genamux->mux.hw;
+
+ mux_hw->clk = hw->clk;
+
+ return (s8)clk_mux_ops.get_parent(mux_hw) > 0;
+}
+
+u8 clkgena_divmux_get_parent(struct clk_hw *hw)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *mux_hw = &genamux->mux.hw;
+
+ mux_hw->clk = hw->clk;
+
+ genamux->muxsel = clk_mux_ops.get_parent(mux_hw);
+ if ((s8)genamux->muxsel < 0) {
+ pr_debug("%s: %s: Invalid parent, setting to default.\n",
+ __func__, __clk_get_name(hw->clk));
+ genamux->muxsel = 0;
+ }
+
+ return genamux->muxsel;
+}
+
+static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+
+ if (index >= CKGAX_CLKOPSRC_SWITCH_OFF)
+ return -EINVAL;
+
+ genamux->muxsel = index;
+
+ /*
+ * If the mux is already enabled, call enable directly to set the
+ * new mux position and wait for it to start running again. Otherwise
+ * do nothing.
+ */
+ if (clkgena_divmux_is_enabled(hw))
+ clkgena_divmux_enable(hw);
+
+ return 0;
+}
+
+unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
+
+ div_hw->clk = hw->clk;
+
+ return clk_divider_ops.recalc_rate(div_hw, parent_rate);
+}
+
+static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
+
+ div_hw->clk = hw->clk;
+
+ return clk_divider_ops.set_rate(div_hw, rate, parent_rate);
+}
+
+static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
+
+ div_hw->clk = hw->clk;
+
+ return clk_divider_ops.round_rate(div_hw, rate, prate);
+}
+
+static const struct clk_ops clkgena_divmux_ops = {
+ .enable = clkgena_divmux_enable,
+ .disable = clkgena_divmux_disable,
+ .is_enabled = clkgena_divmux_is_enabled,
+ .get_parent = clkgena_divmux_get_parent,
+ .set_parent = clkgena_divmux_set_parent,
+ .round_rate = clkgena_divmux_round_rate,
+ .recalc_rate = clkgena_divmux_recalc_rate,
+ .set_rate = clkgena_divmux_set_rate,
+};
+
+/**
+ * clk_register_genamux - register a genamux clock with the clock framework
+ */
+struct clk *clk_register_genamux(const char *name,
+ const char **parent_names, u8 num_parents,
+ void __iomem *reg,
+ const struct clkgena_divmux_data *muxdata,
+ u32 idx)
+{
+ /*
+ * Fixed constants across all ClockgenA variants
+ */
+ const int mux_width = 2;
+ const int divider_width = 5;
+ struct clkgena_divmux *genamux;
+ struct clk *clk;
+ struct clk_init_data init;
+ int i;
+
+ genamux = kzalloc(sizeof(*genamux), GFP_KERNEL);
+ if (!genamux)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clkgena_divmux_ops;
+ init.flags = CLK_IS_BASIC;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ genamux->mux.lock = &clkgena_divmux_lock;
+ genamux->mux.mask = BIT(mux_width) - 1;
+ genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width);
+ if (genamux->mux.shift > 31) {
+ /*
+ * We have spilled into the second mux register so
+ * adjust the register address and the bit shift accordingly
+ */
+ genamux->mux.reg = reg + muxdata->mux_offset2;
+ genamux->mux.shift -= 32;
+ } else {
+ genamux->mux.reg = reg + muxdata->mux_offset;
+ }
+
+ for (i = 0; i < NUM_INPUTS; i++) {
+ /*
+ * Divider config for each input
+ */
+ void __iomem *divbase = reg + muxdata->div_offsets[i];
+ genamux->div[i].width = divider_width;
+ genamux->div[i].reg = divbase + (idx * sizeof(u32));
+
+ /*
+ * Mux enabled/running feedback register for each input.
+ */
+ genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i];
+ }
+
+ genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx;
+ genamux->hw.init = &init;
+
+ clk = clk_register(NULL, &genamux->hw);
+ if (IS_ERR(clk)) {
+ kfree(genamux);
+ goto err;
+ }
+
+ pr_debug("%s: parent %s rate %lu\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ clk_get_rate(clk));
+err:
+ return clk;
+}
+
+static struct clkgena_divmux_data st_divmux_c65hs = {
+ .num_outputs = 4,
+ .mux_offset = 0x14,
+ .mux_start_bit = 0,
+ .div_offsets = { 0x800, 0x900, 0xb00 },
+ .fb_offsets = { 0x18, 0x1c, 0x20 },
+ .fb_start_bit_idx = 0,
+};
+
+static struct clkgena_divmux_data st_divmux_c65ls = {
+ .num_outputs = 14,
+ .mux_offset = 0x14,
+ .mux_offset2 = 0x24,
+ .mux_start_bit = 8,
+ .div_offsets = { 0x810, 0xa10, 0xb10 },
+ .fb_offsets = { 0x18, 0x1c, 0x20 },
+ .fb_start_bit_idx = 4,
+};
+
+static struct clkgena_divmux_data st_divmux_c32odf0 = {
+ .num_outputs = 8,
+ .mux_offset = 0x1c,
+ .mux_start_bit = 0,
+ .div_offsets = { 0x800, 0x900, 0xa60 },
+ .fb_offsets = { 0x2c, 0x24, 0x28 },
+ .fb_start_bit_idx = 0,
+};
+
+static struct clkgena_divmux_data st_divmux_c32odf1 = {
+ .num_outputs = 8,
+ .mux_offset = 0x1c,
+ .mux_start_bit = 16,
+ .div_offsets = { 0x820, 0x980, 0xa80 },
+ .fb_offsets = { 0x2c, 0x24, 0x28 },
+ .fb_start_bit_idx = 8,
+};
+
+static struct clkgena_divmux_data st_divmux_c32odf2 = {
+ .num_outputs = 8,
+ .mux_offset = 0x20,
+ .mux_start_bit = 0,
+ .div_offsets = { 0x840, 0xa20, 0xb10 },
+ .fb_offsets = { 0x2c, 0x24, 0x28 },
+ .fb_start_bit_idx = 16,
+};
+
+static struct clkgena_divmux_data st_divmux_c32odf3 = {
+ .num_outputs = 8,
+ .mux_offset = 0x20,
+ .mux_start_bit = 16,
+ .div_offsets = { 0x860, 0xa40, 0xb30 },
+ .fb_offsets = { 0x2c, 0x24, 0x28 },
+ .fb_start_bit_idx = 24,
+};
+
+static struct of_device_id clkgena_divmux_of_match[] = {
+ {
+ .compatible = "st,clkgena-divmux-c65-hs",
+ .data = &st_divmux_c65hs,
+ },
+ {
+ .compatible = "st,clkgena-divmux-c65-ls",
+ .data = &st_divmux_c65ls,
+ },
+ {
+ .compatible = "st,clkgena-divmux-c32-odf0",
+ .data = &st_divmux_c32odf0,
+ },
+ {
+ .compatible = "st,clkgena-divmux-c32-odf1",
+ .data = &st_divmux_c32odf1,
+ },
+ {
+ .compatible = "st,clkgena-divmux-c32-odf2",
+ .data = &st_divmux_c32odf2,
+ },
+ {
+ .compatible = "st,clkgena-divmux-c32-odf3",
+ .data = &st_divmux_c32odf3,
+ },
+ {}
+};
+
+static void __iomem * __init clkgen_get_register_base(
+ struct device_node *np)
+{
+ struct device_node *pnode;
+ void __iomem *reg = NULL;
+
+ pnode = of_get_parent(np);
+ if (!pnode)
+ return NULL;
+
+ reg = of_iomap(pnode, 0);
+
+ of_node_put(pnode);
+ return reg;
+}
+
+void __init st_of_clkgena_divmux_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ const struct clkgena_divmux_data *data;
+ struct clk_onecell_data *clk_data;
+ void __iomem *reg;
+ const char **parents;
+ int num_parents = 0, i;
+
+ match = of_match_node(clkgena_divmux_of_match, np);
+ if (WARN_ON(!match))
+ return;
+
+ data = (struct clkgena_divmux_data *)match->data;
+
+ reg = clkgen_get_register_base(np);
+ if (!reg)
+ return;
+
+ parents = clkgen_mux_get_parents(np, &num_parents);
+ if (IS_ERR(parents))
+ return;
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ goto err;
+
+ clk_data->clk_num = data->num_outputs;
+ clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+
+ if (!clk_data->clks)
+ goto err;
+
+ for (i = 0; i < clk_data->clk_num; i++) {
+ struct clk *clk;
+ const char *clk_name;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ i, &clk_name))
+ break;
+
+ /*
+ * If we read an empty clock name then the output is unused
+ */
+ if (*clk_name == '\0')
+ continue;
+
+ clk = clk_register_genamux(clk_name, parents, num_parents,
+ reg, data, i);
+
+ if (IS_ERR(clk))
+ goto err;
+
+ clk_data->clks[i] = clk;
+ }
+
+ kfree(parents);
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ return;
+err:
+ if (clk_data)
+ kfree(clk_data->clks);
+
+ kfree(clk_data);
+ kfree(parents);
+}
+CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup);
+
+struct clkgena_prediv_data {
+ u32 offset;
+ u8 shift;
+ struct clk_div_table *table;
+};
+
+static struct clk_div_table prediv_table16[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 16 },
+ { .div = 0 },
+};
+
+static struct clkgena_prediv_data prediv_c65_data = {
+ .offset = 0x4c,
+ .shift = 31,
+ .table = prediv_table16,
+};
+
+static struct clkgena_prediv_data prediv_c32_data = {
+ .offset = 0x50,
+ .shift = 1,
+ .table = prediv_table16,
+};
+
+static struct of_device_id clkgena_prediv_of_match[] = {
+ { .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data },
+ { .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data },
+ {}
+};
+
+void __init st_of_clkgena_prediv_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ void __iomem *reg;
+ const char *parent_name, *clk_name;
+ struct clk *clk;
+ struct clkgena_prediv_data *data;
+
+ match = of_match_node(clkgena_prediv_of_match, np);
+ if (!match) {
+ pr_err("%s: No matching data\n", __func__);
+ return;
+ }
+
+ data = (struct clkgena_prediv_data *)match->data;
+
+ reg = clkgen_get_register_base(np);
+ if (!reg)
+ return;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ 0, &clk_name))
+ return;
+
+ clk = clk_register_divider_table(NULL, clk_name, parent_name, 0,
+ reg + data->offset, data->shift, 1,
+ 0, data->table, NULL);
+ if (IS_ERR(clk))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ pr_debug("%s: parent %s rate %u\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ (unsigned int)clk_get_rate(clk));
+
+ return;
+}
+CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup);
+
+struct clkgen_mux_data {
+ u32 offset;
+ u8 shift;
+ u8 width;
+ spinlock_t *lock;
+ unsigned long clk_flags;
+ u8 mux_flags;
+};
+
+static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = {
+ .offset = 0,
+ .shift = 0,
+ .width = 1,
+};
+
+static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = {
+ .offset = 0,
+ .shift = 0,
+ .width = 1,
+};
+
+static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = {
+ .offset = 0,
+ .shift = 0,
+ .width = 1,
+};
+
+static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = {
+ .offset = 0,
+ .shift = 16,
+ .width = 1,
+ .lock = &clkgenf_lock,
+};
+
+static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = {
+ .offset = 0,
+ .shift = 17,
+ .width = 1,
+ .lock = &clkgenf_lock,
+};
+
+static struct clkgen_mux_data stih415_a9_mux_data = {
+ .offset = 0,
+ .shift = 1,
+ .width = 2,
+};
+static struct clkgen_mux_data stih416_a9_mux_data = {
+ .offset = 0,
+ .shift = 0,
+ .width = 2,
+};
+
+static struct of_device_id mux_of_match[] = {
+ {
+ .compatible = "st,stih416-clkgenc-vcc-hd",
+ .data = &clkgen_mux_c_vcc_hd_416,
+ },
+ {
+ .compatible = "st,stih416-clkgenf-vcc-fvdp",
+ .data = &clkgen_mux_f_vcc_fvdp_416,
+ },
+ {
+ .compatible = "st,stih416-clkgenf-vcc-hva",
+ .data = &clkgen_mux_f_vcc_hva_416,
+ },
+ {
+ .compatible = "st,stih416-clkgenf-vcc-hd",
+ .data = &clkgen_mux_f_vcc_hd_416,
+ },
+ {
+ .compatible = "st,stih416-clkgenf-vcc-sd",
+ .data = &clkgen_mux_c_vcc_sd_416,
+ },
+ {
+ .compatible = "st,stih415-clkgen-a9-mux",
+ .data = &stih415_a9_mux_data,
+ },
+ {
+ .compatible = "st,stih416-clkgen-a9-mux",
+ .data = &stih416_a9_mux_data,
+ },
+ {}
+};
+
+void __init st_of_clkgen_mux_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ struct clk *clk;
+ void __iomem *reg;
+ const char **parents;
+ int num_parents;
+ struct clkgen_mux_data *data;
+
+ match = of_match_node(mux_of_match, np);
+ if (!match) {
+ pr_err("%s: No matching data\n", __func__);
+ return;
+ }
+
+ data = (struct clkgen_mux_data *)match->data;
+
+ reg = of_iomap(np, 0);
+ if (!reg) {
+ pr_err("%s: Failed to get base address\n", __func__);
+ return;
+ }
+
+ parents = clkgen_mux_get_parents(np, &num_parents);
+ if (IS_ERR(parents)) {
+ pr_err("%s: Failed to get parents (%ld)\n",
+ __func__, PTR_ERR(parents));
+ return;
+ }
+
+ clk = clk_register_mux(NULL, np->name, parents, num_parents,
+ data->clk_flags | CLK_SET_RATE_PARENT,
+ reg + data->offset,
+ data->shift, data->width, data->mux_flags,
+ data->lock);
+ if (IS_ERR(clk))
+ goto err;
+
+ pr_debug("%s: parent %s rate %u\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ (unsigned int)clk_get_rate(clk));
+
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+err:
+ kfree(parents);
+
+ return;
+}
+CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup);
+
+#define VCC_MAX_CHANNELS 16
+
+#define VCC_GATE_OFFSET 0x0
+#define VCC_MUX_OFFSET 0x4
+#define VCC_DIV_OFFSET 0x8
+
+struct clkgen_vcc_data {
+ spinlock_t *lock;
+ unsigned long clk_flags;
+};
+
+static struct clkgen_vcc_data st_clkgenc_vcc_416 = {
+ .clk_flags = CLK_SET_RATE_PARENT,
+};
+
+static struct clkgen_vcc_data st_clkgenf_vcc_416 = {
+ .lock = &clkgenf_lock,
+};
+
+static struct of_device_id vcc_of_match[] = {
+ { .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 },
+ { .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 },
+ {}
+};
+
+void __init st_of_clkgen_vcc_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ void __iomem *reg;
+ const char **parents;
+ int num_parents, i;
+ struct clk_onecell_data *clk_data;
+ struct clkgen_vcc_data *data;
+
+ match = of_match_node(vcc_of_match, np);
+ if (WARN_ON(!match))
+ return;
+ data = (struct clkgen_vcc_data *)match->data;
+
+ reg = of_iomap(np, 0);
+ if (!reg)
+ return;
+
+ parents = clkgen_mux_get_parents(np, &num_parents);
+ if (IS_ERR(parents))
+ return;
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ goto err;
+
+ clk_data->clk_num = VCC_MAX_CHANNELS;
+ clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+
+ if (!clk_data->clks)
+ goto err;
+
+ for (i = 0; i < clk_data->clk_num; i++) {
+ struct clk *clk;
+ const char *clk_name;
+ struct clk_gate *gate;
+ struct clk_divider *div;
+ struct clk_mux *mux;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ i, &clk_name))
+ break;
+
+ /*
+ * If we read an empty clock name then the output is unused
+ */
+ if (*clk_name == '\0')
+ continue;
+
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+ if (!gate)
+ break;
+
+ div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
+ if (!div) {
+ kfree(gate);
+ break;
+ }
+
+ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+ if (!mux) {
+ kfree(gate);
+ kfree(div);
+ break;
+ }
+
+ gate->reg = reg + VCC_GATE_OFFSET;
+ gate->bit_idx = i;
+ gate->flags = CLK_GATE_SET_TO_DISABLE;
+ gate->lock = data->lock;
+
+ div->reg = reg + VCC_DIV_OFFSET;
+ div->shift = 2 * i;
+ div->width = 2;
+ div->flags = CLK_DIVIDER_POWER_OF_TWO;
+
+ mux->reg = reg + VCC_MUX_OFFSET;
+ mux->shift = 2 * i;
+ mux->mask = 0x3;
+
+ clk = clk_register_composite(NULL, clk_name, parents,
+ num_parents,
+ &mux->hw, &clk_mux_ops,
+ &div->hw, &clk_divider_ops,
+ &gate->hw, &clk_gate_ops,
+ data->clk_flags);
+ if (IS_ERR(clk)) {
+ kfree(gate);
+ kfree(div);
+ kfree(mux);
+ goto err;
+ }
+
+ pr_debug("%s: parent %s rate %u\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ (unsigned int)clk_get_rate(clk));
+
+ clk_data->clks[i] = clk;
+ }
+
+ kfree(parents);
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ return;
+
+err:
+ for (i = 0; i < clk_data->clk_num; i++) {
+ struct clk_composite *composite;
+
+ if (!clk_data->clks[i])
+ continue;
+
+ composite = container_of(__clk_get_hw(clk_data->clks[i]),
+ struct clk_composite, hw);
+ kfree(container_of(composite->gate_hw, struct clk_gate, hw));
+ kfree(container_of(composite->rate_hw, struct clk_divider, hw));
+ kfree(container_of(composite->mux_hw, struct clk_mux, hw));
+ }
+
+ if (clk_data)
+ kfree(clk_data->clks);
+
+ kfree(clk_data);
+ kfree(parents);
+}
+CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup);
diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c
new file mode 100644
index 000000000000..bca0a0badbfa
--- /dev/null
+++ b/drivers/clk/st/clkgen-pll.c
@@ -0,0 +1,698 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics (R&D) Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+/*
+ * Authors:
+ * Stephen Gallimore <stephen.gallimore@st.com>,
+ * Pankaj Dev <pankaj.dev@st.com>.
+ */
+
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/clk-provider.h>
+
+#include "clkgen.h"
+
+static DEFINE_SPINLOCK(clkgena_c32_odf_lock);
+
+/*
+ * Common PLL configuration register bits for PLL800 and PLL1600 C65
+ */
+#define C65_MDIV_PLL800_MASK (0xff)
+#define C65_MDIV_PLL1600_MASK (0x7)
+#define C65_NDIV_MASK (0xff)
+#define C65_PDIV_MASK (0x7)
+
+/*
+ * PLL configuration register bits for PLL3200 C32
+ */
+#define C32_NDIV_MASK (0xff)
+#define C32_IDF_MASK (0x7)
+#define C32_ODF_MASK (0x3f)
+#define C32_LDF_MASK (0x7f)
+
+#define C32_MAX_ODFS (4)
+
+struct clkgen_pll_data {
+ struct clkgen_field pdn_status;
+ struct clkgen_field locked_status;
+ struct clkgen_field mdiv;
+ struct clkgen_field ndiv;
+ struct clkgen_field pdiv;
+ struct clkgen_field idf;
+ struct clkgen_field ldf;
+ unsigned int num_odfs;
+ struct clkgen_field odf[C32_MAX_ODFS];
+ struct clkgen_field odf_gate[C32_MAX_ODFS];
+ const struct clk_ops *ops;
+};
+
+static const struct clk_ops st_pll1600c65_ops;
+static const struct clk_ops st_pll800c65_ops;
+static const struct clk_ops stm_pll3200c32_ops;
+static const struct clk_ops st_pll1200c32_ops;
+
+static struct clkgen_pll_data st_pll1600c65_ax = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 19),
+ .locked_status = CLKGEN_FIELD(0x0, 0x1, 31),
+ .mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL1600_MASK, 0),
+ .ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8),
+ .ops = &st_pll1600c65_ops
+};
+
+static struct clkgen_pll_data st_pll800c65_ax = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 19),
+ .locked_status = CLKGEN_FIELD(0x0, 0x1, 31),
+ .mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL800_MASK, 0),
+ .ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8),
+ .pdiv = CLKGEN_FIELD(0x0, C65_PDIV_MASK, 16),
+ .ops = &st_pll800c65_ops
+};
+
+static struct clkgen_pll_data st_pll3200c32_a1x_0 = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 31),
+ .locked_status = CLKGEN_FIELD(0x4, 0x1, 31),
+ .ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 0x0),
+ .idf = CLKGEN_FIELD(0x4, C32_IDF_MASK, 0x0),
+ .num_odfs = 4,
+ .odf = { CLKGEN_FIELD(0x54, C32_ODF_MASK, 4),
+ CLKGEN_FIELD(0x54, C32_ODF_MASK, 10),
+ CLKGEN_FIELD(0x54, C32_ODF_MASK, 16),
+ CLKGEN_FIELD(0x54, C32_ODF_MASK, 22) },
+ .odf_gate = { CLKGEN_FIELD(0x54, 0x1, 0),
+ CLKGEN_FIELD(0x54, 0x1, 1),
+ CLKGEN_FIELD(0x54, 0x1, 2),
+ CLKGEN_FIELD(0x54, 0x1, 3) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+static struct clkgen_pll_data st_pll3200c32_a1x_1 = {
+ .pdn_status = CLKGEN_FIELD(0xC, 0x1, 31),
+ .locked_status = CLKGEN_FIELD(0x10, 0x1, 31),
+ .ndiv = CLKGEN_FIELD(0xC, C32_NDIV_MASK, 0x0),
+ .idf = CLKGEN_FIELD(0x10, C32_IDF_MASK, 0x0),
+ .num_odfs = 4,
+ .odf = { CLKGEN_FIELD(0x58, C32_ODF_MASK, 4),
+ CLKGEN_FIELD(0x58, C32_ODF_MASK, 10),
+ CLKGEN_FIELD(0x58, C32_ODF_MASK, 16),
+ CLKGEN_FIELD(0x58, C32_ODF_MASK, 22) },
+ .odf_gate = { CLKGEN_FIELD(0x58, 0x1, 0),
+ CLKGEN_FIELD(0x58, 0x1, 1),
+ CLKGEN_FIELD(0x58, 0x1, 2),
+ CLKGEN_FIELD(0x58, 0x1, 3) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+/* 415 specific */
+static struct clkgen_pll_data st_pll3200c32_a9_415 = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
+ .locked_status = CLKGEN_FIELD(0x6C, 0x1, 0),
+ .ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 9),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 22),
+ .num_odfs = 1,
+ .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 3) },
+ .odf_gate = { CLKGEN_FIELD(0x0, 0x1, 28) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+static struct clkgen_pll_data st_pll3200c32_ddr_415 = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
+ .locked_status = CLKGEN_FIELD(0x100, 0x1, 0),
+ .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
+ .num_odfs = 2,
+ .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8),
+ CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) },
+ .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28),
+ CLKGEN_FIELD(0x4, 0x1, 29) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+static struct clkgen_pll_data st_pll1200c32_gpu_415 = {
+ .pdn_status = CLKGEN_FIELD(0x144, 0x1, 3),
+ .locked_status = CLKGEN_FIELD(0x168, 0x1, 0),
+ .ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0),
+ .num_odfs = 0,
+ .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) },
+ .ops = &st_pll1200c32_ops,
+};
+
+/* 416 specific */
+static struct clkgen_pll_data st_pll3200c32_a9_416 = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
+ .locked_status = CLKGEN_FIELD(0x6C, 0x1, 0),
+ .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
+ .num_odfs = 1,
+ .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8) },
+ .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+static struct clkgen_pll_data st_pll3200c32_ddr_416 = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
+ .locked_status = CLKGEN_FIELD(0x10C, 0x1, 0),
+ .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
+ .num_odfs = 2,
+ .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8),
+ CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) },
+ .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28),
+ CLKGEN_FIELD(0x4, 0x1, 29) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+static struct clkgen_pll_data st_pll1200c32_gpu_416 = {
+ .pdn_status = CLKGEN_FIELD(0x8E4, 0x1, 3),
+ .locked_status = CLKGEN_FIELD(0x90C, 0x1, 0),
+ .ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0),
+ .num_odfs = 0,
+ .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) },
+ .ops = &st_pll1200c32_ops,
+};
+
+/**
+ * DOC: Clock Generated by PLL, rate set and enabled by bootloader
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable/disable only ensures parent is enabled
+ * rate - rate is fixed. No clk_set_rate support
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+/**
+ * PLL clock that is integrated in the ClockGenA instances on the STiH415
+ * and STiH416.
+ *
+ * @hw: handle between common and hardware-specific interfaces.
+ * @type: PLL instance type.
+ * @regs_base: base of the PLL configuration register(s).
+ *
+ */
+struct clkgen_pll {
+ struct clk_hw hw;
+ struct clkgen_pll_data *data;
+ void __iomem *regs_base;
+};
+
+#define to_clkgen_pll(_hw) container_of(_hw, struct clkgen_pll, hw)
+
+static int clkgen_pll_is_locked(struct clk_hw *hw)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ u32 locked = CLKGEN_READ(pll, locked_status);
+
+ return !!locked;
+}
+
+static int clkgen_pll_is_enabled(struct clk_hw *hw)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ u32 poweroff = CLKGEN_READ(pll, pdn_status);
+ return !poweroff;
+}
+
+unsigned long recalc_stm_pll800c65(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ unsigned long mdiv, ndiv, pdiv;
+ unsigned long rate;
+ uint64_t res;
+
+ if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
+ return 0;
+
+ pdiv = CLKGEN_READ(pll, pdiv);
+ mdiv = CLKGEN_READ(pll, mdiv);
+ ndiv = CLKGEN_READ(pll, ndiv);
+
+ if (!mdiv)
+ mdiv++; /* mdiv=0 or 1 => MDIV=1 */
+
+ res = (uint64_t)2 * (uint64_t)parent_rate * (uint64_t)ndiv;
+ rate = (unsigned long)div64_u64(res, mdiv * (1 << pdiv));
+
+ pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
+
+ return rate;
+
+}
+
+unsigned long recalc_stm_pll1600c65(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ unsigned long mdiv, ndiv;
+ unsigned long rate;
+
+ if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
+ return 0;
+
+ mdiv = CLKGEN_READ(pll, mdiv);
+ ndiv = CLKGEN_READ(pll, ndiv);
+
+ if (!mdiv)
+ mdiv = 1;
+
+ /* Note: input is divided by 1000 to avoid overflow */
+ rate = ((2 * (parent_rate / 1000) * ndiv) / mdiv) * 1000;
+
+ pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
+
+ return rate;
+}
+
+unsigned long recalc_stm_pll3200c32(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ unsigned long ndiv, idf;
+ unsigned long rate = 0;
+
+ if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
+ return 0;
+
+ ndiv = CLKGEN_READ(pll, ndiv);
+ idf = CLKGEN_READ(pll, idf);
+
+ if (idf)
+ /* Note: input is divided to avoid overflow */
+ rate = ((2 * (parent_rate/1000) * ndiv) / idf) * 1000;
+
+ pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
+
+ return rate;
+}
+
+unsigned long recalc_stm_pll1200c32(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ unsigned long odf, ldf, idf;
+ unsigned long rate;
+
+ if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
+ return 0;
+
+ odf = CLKGEN_READ(pll, odf[0]);
+ ldf = CLKGEN_READ(pll, ldf);
+ idf = CLKGEN_READ(pll, idf);
+
+ if (!idf) /* idf==0 means 1 */
+ idf = 1;
+ if (!odf) /* odf==0 means 1 */
+ odf = 1;
+
+ /* Note: input is divided by 1000 to avoid overflow */
+ rate = (((parent_rate / 1000) * ldf) / (odf * idf)) * 1000;
+
+ pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
+
+ return rate;
+}
+
+static const struct clk_ops st_pll1600c65_ops = {
+ .is_enabled = clkgen_pll_is_enabled,
+ .recalc_rate = recalc_stm_pll1600c65,
+};
+
+static const struct clk_ops st_pll800c65_ops = {
+ .is_enabled = clkgen_pll_is_enabled,
+ .recalc_rate = recalc_stm_pll800c65,
+};
+
+static const struct clk_ops stm_pll3200c32_ops = {
+ .is_enabled = clkgen_pll_is_enabled,
+ .recalc_rate = recalc_stm_pll3200c32,
+};
+
+static const struct clk_ops st_pll1200c32_ops = {
+ .is_enabled = clkgen_pll_is_enabled,
+ .recalc_rate = recalc_stm_pll1200c32,
+};
+
+static struct clk * __init clkgen_pll_register(const char *parent_name,
+ struct clkgen_pll_data *pll_data,
+ void __iomem *reg,
+ const char *clk_name)
+{
+ struct clkgen_pll *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = clk_name;
+ init.ops = pll_data->ops;
+
+ init.flags = CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ pll->data = pll_data;
+ pll->regs_base = reg;
+ pll->hw.init = &init;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ kfree(pll);
+ return clk;
+ }
+
+ pr_debug("%s: parent %s rate %lu\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ clk_get_rate(clk));
+
+ return clk;
+}
+
+static struct clk * __init clkgen_c65_lsdiv_register(const char *parent_name,
+ const char *clk_name)
+{
+ struct clk *clk;
+
+ clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, 1, 2);
+ if (IS_ERR(clk))
+ return clk;
+
+ pr_debug("%s: parent %s rate %lu\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ clk_get_rate(clk));
+ return clk;
+}
+
+static void __iomem * __init clkgen_get_register_base(
+ struct device_node *np)
+{
+ struct device_node *pnode;
+ void __iomem *reg = NULL;
+
+ pnode = of_get_parent(np);
+ if (!pnode)
+ return NULL;
+
+ reg = of_iomap(pnode, 0);
+
+ of_node_put(pnode);
+ return reg;
+}
+
+#define CLKGENAx_PLL0_OFFSET 0x0
+#define CLKGENAx_PLL1_OFFSET 0x4
+
+static void __init clkgena_c65_pll_setup(struct device_node *np)
+{
+ const int num_pll_outputs = 3;
+ struct clk_onecell_data *clk_data;
+ const char *parent_name;
+ void __iomem *reg;
+ const char *clk_name;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return;
+
+ reg = clkgen_get_register_base(np);
+ if (!reg)
+ return;
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->clk_num = num_pll_outputs;
+ clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+
+ if (!clk_data->clks)
+ goto err;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ 0, &clk_name))
+ goto err;
+
+ /*
+ * PLL0 HS (high speed) output
+ */
+ clk_data->clks[0] = clkgen_pll_register(parent_name,
+ &st_pll1600c65_ax,
+ reg + CLKGENAx_PLL0_OFFSET,
+ clk_name);
+
+ if (IS_ERR(clk_data->clks[0]))
+ goto err;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ 1, &clk_name))
+ goto err;
+
+ /*
+ * PLL0 LS (low speed) output, which is a fixed divide by 2 of the
+ * high speed output.
+ */
+ clk_data->clks[1] = clkgen_c65_lsdiv_register(__clk_get_name
+ (clk_data->clks[0]),
+ clk_name);
+
+ if (IS_ERR(clk_data->clks[1]))
+ goto err;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ 2, &clk_name))
+ goto err;
+
+ /*
+ * PLL1 output
+ */
+ clk_data->clks[2] = clkgen_pll_register(parent_name,
+ &st_pll800c65_ax,
+ reg + CLKGENAx_PLL1_OFFSET,
+ clk_name);
+
+ if (IS_ERR(clk_data->clks[2]))
+ goto err;
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ return;
+
+err:
+ kfree(clk_data->clks);
+ kfree(clk_data);
+}
+CLK_OF_DECLARE(clkgena_c65_plls,
+ "st,clkgena-plls-c65", clkgena_c65_pll_setup);
+
+static struct clk * __init clkgen_odf_register(const char *parent_name,
+ void * __iomem reg,
+ struct clkgen_pll_data *pll_data,
+ int odf,
+ spinlock_t *odf_lock,
+ const char *odf_name)
+{
+ struct clk *clk;
+ unsigned long flags;
+ struct clk_gate *gate;
+ struct clk_divider *div;
+
+ flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->flags = CLK_GATE_SET_TO_DISABLE;
+ gate->reg = reg + pll_data->odf_gate[odf].offset;
+ gate->bit_idx = pll_data->odf_gate[odf].shift;
+ gate->lock = odf_lock;
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ div->flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO;
+ div->reg = reg + pll_data->odf[odf].offset;
+ div->shift = pll_data->odf[odf].shift;
+ div->width = fls(pll_data->odf[odf].mask);
+ div->lock = odf_lock;
+
+ clk = clk_register_composite(NULL, odf_name, &parent_name, 1,
+ NULL, NULL,
+ &div->hw, &clk_divider_ops,
+ &gate->hw, &clk_gate_ops,
+ flags);
+ if (IS_ERR(clk))
+ return clk;
+
+ pr_debug("%s: parent %s rate %lu\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ clk_get_rate(clk));
+ return clk;
+}
+
+static struct of_device_id c32_pll_of_match[] = {
+ {
+ .compatible = "st,plls-c32-a1x-0",
+ .data = &st_pll3200c32_a1x_0,
+ },
+ {
+ .compatible = "st,plls-c32-a1x-1",
+ .data = &st_pll3200c32_a1x_1,
+ },
+ {
+ .compatible = "st,stih415-plls-c32-a9",
+ .data = &st_pll3200c32_a9_415,
+ },
+ {
+ .compatible = "st,stih415-plls-c32-ddr",
+ .data = &st_pll3200c32_ddr_415,
+ },
+ {
+ .compatible = "st,stih416-plls-c32-a9",
+ .data = &st_pll3200c32_a9_416,
+ },
+ {
+ .compatible = "st,stih416-plls-c32-ddr",
+ .data = &st_pll3200c32_ddr_416,
+ },
+ {}
+};
+
+static void __init clkgen_c32_pll_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ struct clk *clk;
+ const char *parent_name, *pll_name;
+ void __iomem *pll_base;
+ int num_odfs, odf;
+ struct clk_onecell_data *clk_data;
+ struct clkgen_pll_data *data;
+
+ match = of_match_node(c32_pll_of_match, np);
+ if (!match) {
+ pr_err("%s: No matching data\n", __func__);
+ return;
+ }
+
+ data = (struct clkgen_pll_data *) match->data;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return;
+
+ pll_base = clkgen_get_register_base(np);
+ if (!pll_base)
+ return;
+
+ clk = clkgen_pll_register(parent_name, data, pll_base, np->name);
+ if (IS_ERR(clk))
+ return;
+
+ pll_name = __clk_get_name(clk);
+
+ num_odfs = data->num_odfs;
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->clk_num = num_odfs;
+ clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+
+ if (!clk_data->clks)
+ goto err;
+
+ for (odf = 0; odf < num_odfs; odf++) {
+ struct clk *clk;
+ const char *clk_name;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ odf, &clk_name))
+ return;
+
+ clk = clkgen_odf_register(pll_name, pll_base, data,
+ odf, &clkgena_c32_odf_lock, clk_name);
+ if (IS_ERR(clk))
+ goto err;
+
+ clk_data->clks[odf] = clk;
+ }
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ return;
+
+err:
+ kfree(pll_name);
+ kfree(clk_data->clks);
+ kfree(clk_data);
+}
+CLK_OF_DECLARE(clkgen_c32_pll, "st,clkgen-plls-c32", clkgen_c32_pll_setup);
+
+static struct of_device_id c32_gpu_pll_of_match[] = {
+ {
+ .compatible = "st,stih415-gpu-pll-c32",
+ .data = &st_pll1200c32_gpu_415,
+ },
+ {
+ .compatible = "st,stih416-gpu-pll-c32",
+ .data = &st_pll1200c32_gpu_416,
+ },
+};
+
+static void __init clkgengpu_c32_pll_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ struct clk *clk;
+ const char *parent_name;
+ void __iomem *reg;
+ const char *clk_name;
+ struct clkgen_pll_data *data;
+
+ match = of_match_node(c32_gpu_pll_of_match, np);
+ if (!match) {
+ pr_err("%s: No matching data\n", __func__);
+ return;
+ }
+
+ data = (struct clkgen_pll_data *)match->data;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return;
+
+ reg = clkgen_get_register_base(np);
+ if (!reg)
+ return;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ 0, &clk_name))
+ return;
+
+ /*
+ * PLL 1200MHz output
+ */
+ clk = clkgen_pll_register(parent_name, data, reg, clk_name);
+
+ if (!IS_ERR(clk))
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+ return;
+}
+CLK_OF_DECLARE(clkgengpu_c32_pll,
+ "st,clkgengpu-pll-c32", clkgengpu_c32_pll_setup);
diff --git a/drivers/clk/st/clkgen.h b/drivers/clk/st/clkgen.h
new file mode 100644
index 000000000000..35c863295268
--- /dev/null
+++ b/drivers/clk/st/clkgen.h
@@ -0,0 +1,48 @@
+/************************************************************************
+File : Clock H/w specific Information
+
+Author: Pankaj Dev <pankaj.dev@st.com>
+
+Copyright (C) 2014 STMicroelectronics
+************************************************************************/
+
+#ifndef __CLKGEN_INFO_H
+#define __CLKGEN_INFO_H
+
+struct clkgen_field {
+ unsigned int offset;
+ unsigned int mask;
+ unsigned int shift;
+};
+
+static inline unsigned long clkgen_read(void __iomem *base,
+ struct clkgen_field *field)
+{
+ return (readl(base + field->offset) >> field->shift) & field->mask;
+}
+
+
+static inline void clkgen_write(void __iomem *base, struct clkgen_field *field,
+ unsigned long val)
+{
+ writel((readl(base + field->offset) &
+ ~(field->mask << field->shift)) | (val << field->shift),
+ base + field->offset);
+
+ return;
+}
+
+#define CLKGEN_FIELD(_offset, _mask, _shift) { \
+ .offset = _offset, \
+ .mask = _mask, \
+ .shift = _shift, \
+ }
+
+#define CLKGEN_READ(pll, field) clkgen_read(pll->regs_base, \
+ &pll->data->field)
+
+#define CLKGEN_WRITE(pll, field, val) clkgen_write(pll->regs_base, \
+ &pll->data->field, val)
+
+#endif /*__CLKGEN_INFO_H*/
+
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index abb6c5ac8a10..bd7dc733c1ca 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -18,6 +18,7 @@
#include <linux/clkdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/reset-controller.h>
#include "clk-factors.h"
@@ -51,6 +52,8 @@ static void __init sun4i_osc_clk_setup(struct device_node *node)
if (!gate)
goto err_free_fixed;
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
/* set up gate and fixed rate properties */
gate->reg = of_iomap(node, 0);
gate->bit_idx = SUNXI_OSC24M_GATE;
@@ -77,7 +80,7 @@ err_free_gate:
err_free_fixed:
kfree(fixed);
}
-CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-osc-clk", sun4i_osc_clk_setup);
+CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-a10-osc-clk", sun4i_osc_clk_setup);
@@ -249,7 +252,38 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate,
*n = DIV_ROUND_UP(div, (*k+1));
}
+/**
+ * sun6i_a31_get_pll6_factors() - calculates n, k factors for A31 PLL6
+ * PLL6 rate is calculated as follows
+ * rate = parent_rate * n * (k + 1) / 2
+ * parent_rate is always 24Mhz
+ */
+
+static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate,
+ u8 *n, u8 *k, u8 *m, u8 *p)
+{
+ u8 div;
+
+ /*
+ * We always have 24MHz / 2, so we can just say that our
+ * parent clock is 12MHz.
+ */
+ parent_rate = parent_rate / 2;
+ /* Normalize value to a parent_rate multiple (24M / 2) */
+ div = *freq / parent_rate;
+ *freq = parent_rate * div;
+
+ /* we were called to round the frequency, we can now return */
+ if (n == NULL)
+ return;
+
+ *k = div / 32;
+ if (*k > 3)
+ *k = 3;
+
+ *n = DIV_ROUND_UP(div, (*k+1));
+}
/**
* sun4i_get_apb1_factors() - calculates m, p factors for APB1
@@ -265,7 +299,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
if (parent_rate < *freq)
*freq = parent_rate;
- parent_rate = (parent_rate + (*freq - 1)) / *freq;
+ parent_rate = DIV_ROUND_UP(parent_rate, *freq);
/* Invalid rate! */
if (parent_rate > 32)
@@ -296,7 +330,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
/**
* sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks
- * MMC rate is calculated as follows
+ * MOD0 rate is calculated as follows
* rate = (parent_rate >> p) / (m + 1);
*/
@@ -310,7 +344,7 @@ static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate,
if (*freq > parent_rate)
*freq = parent_rate;
- div = parent_rate / *freq;
+ div = DIV_ROUND_UP(parent_rate, *freq);
if (div < 16)
calcp = 0;
@@ -351,7 +385,7 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
if (*freq > parent_rate)
*freq = parent_rate;
- div = parent_rate / *freq;
+ div = DIV_ROUND_UP(parent_rate, *freq);
if (div < 32)
calcp = 0;
@@ -377,6 +411,102 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
/**
+ * sun7i_a20_gmac_clk_setup - Setup function for A20/A31 GMAC clock module
+ *
+ * This clock looks something like this
+ * ________________________
+ * MII TX clock from PHY >-----|___________ _________|----> to GMAC core
+ * GMAC Int. RGMII TX clk >----|___________\__/__gate---|----> to PHY
+ * Ext. 125MHz RGMII TX clk >--|__divider__/ |
+ * |________________________|
+ *
+ * The external 125 MHz reference is optional, i.e. GMAC can use its
+ * internal TX clock just fine. The A31 GMAC clock module does not have
+ * the divider controls for the external reference.
+ *
+ * To keep it simple, let the GMAC use either the MII TX clock for MII mode,
+ * and its internal TX clock for GMII and RGMII modes. The GMAC driver should
+ * select the appropriate source and gate/ungate the output to the PHY.
+ *
+ * Only the GMAC should use this clock. Altering the clock so that it doesn't
+ * match the GMAC's operation parameters will result in the GMAC not being
+ * able to send traffic out. The GMAC driver should set the clock rate and
+ * enable/disable this clock to configure the required state. The clock
+ * driver then responds by auto-reparenting the clock.
+ */
+
+#define SUN7I_A20_GMAC_GPIT 2
+#define SUN7I_A20_GMAC_MASK 0x3
+#define SUN7I_A20_GMAC_PARENTS 2
+
+static void __init sun7i_a20_gmac_clk_setup(struct device_node *node)
+{
+ struct clk *clk;
+ struct clk_mux *mux;
+ struct clk_gate *gate;
+ const char *clk_name = node->name;
+ const char *parents[SUN7I_A20_GMAC_PARENTS];
+ void *reg;
+
+ if (of_property_read_string(node, "clock-output-names", &clk_name))
+ return;
+
+ /* allocate mux and gate clock structs */
+ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+ if (!mux)
+ return;
+
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+ if (!gate)
+ goto free_mux;
+
+ /* gmac clock requires exactly 2 parents */
+ parents[0] = of_clk_get_parent_name(node, 0);
+ parents[1] = of_clk_get_parent_name(node, 1);
+ if (!parents[0] || !parents[1])
+ goto free_gate;
+
+ reg = of_iomap(node, 0);
+ if (!reg)
+ goto free_gate;
+
+ /* set up gate and fixed rate properties */
+ gate->reg = reg;
+ gate->bit_idx = SUN7I_A20_GMAC_GPIT;
+ gate->lock = &clk_lock;
+ mux->reg = reg;
+ mux->mask = SUN7I_A20_GMAC_MASK;
+ mux->flags = CLK_MUX_INDEX_BIT;
+ mux->lock = &clk_lock;
+
+ clk = clk_register_composite(NULL, clk_name,
+ parents, SUN7I_A20_GMAC_PARENTS,
+ &mux->hw, &clk_mux_ops,
+ NULL, NULL,
+ &gate->hw, &clk_gate_ops,
+ 0);
+
+ if (IS_ERR(clk))
+ goto iounmap_reg;
+
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+
+ return;
+
+iounmap_reg:
+ iounmap(reg);
+free_gate:
+ kfree(gate);
+free_mux:
+ kfree(mux);
+}
+CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk",
+ sun7i_a20_gmac_clk_setup);
+
+
+
+/**
* sunxi_factors_clk_setup() - Setup function for factor clocks
*/
@@ -387,6 +517,7 @@ struct factors_data {
int mux;
struct clk_factors_config *table;
void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
+ const char *name;
};
static struct clk_factors_config sun4i_pll1_config = {
@@ -416,6 +547,13 @@ static struct clk_factors_config sun4i_pll5_config = {
.kwidth = 2,
};
+static struct clk_factors_config sun6i_a31_pll6_config = {
+ .nshift = 8,
+ .nwidth = 5,
+ .kshift = 4,
+ .kwidth = 2,
+};
+
static struct clk_factors_config sun4i_apb1_config = {
.mshift = 0,
.mwidth = 5,
@@ -451,10 +589,30 @@ static const struct factors_data sun6i_a31_pll1_data __initconst = {
.getter = sun6i_a31_get_pll1_factors,
};
+static const struct factors_data sun7i_a20_pll4_data __initconst = {
+ .enable = 31,
+ .table = &sun4i_pll5_config,
+ .getter = sun4i_get_pll5_factors,
+};
+
static const struct factors_data sun4i_pll5_data __initconst = {
.enable = 31,
.table = &sun4i_pll5_config,
.getter = sun4i_get_pll5_factors,
+ .name = "pll5",
+};
+
+static const struct factors_data sun4i_pll6_data __initconst = {
+ .enable = 31,
+ .table = &sun4i_pll5_config,
+ .getter = sun4i_get_pll5_factors,
+ .name = "pll6",
+};
+
+static const struct factors_data sun6i_a31_pll6_data __initconst = {
+ .enable = 31,
+ .table = &sun6i_a31_pll6_config,
+ .getter = sun6i_a31_get_pll6_factors,
};
static const struct factors_data sun4i_apb1_data __initconst = {
@@ -497,14 +655,14 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
i++;
- /* Nodes should be providing the name via clock-output-names
- * but originally our dts didn't, and so we used node->name.
- * The new, better nodes look like clk@deadbeef, so we pull the
- * name just in this case */
- if (!strcmp("clk", clk_name)) {
- of_property_read_string_index(node, "clock-output-names",
- 0, &clk_name);
- }
+ /*
+ * some factor clocks, such as pll5 and pll6, may have multiple
+ * outputs, and have their name designated in factors_data
+ */
+ if (data->name)
+ clk_name = data->name;
+ else
+ of_property_read_string(node, "clock-output-names", &clk_name);
factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
if (!factors)
@@ -601,6 +759,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node,
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
i++;
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
clk = clk_register_mux(NULL, clk_name, parents, i,
CLK_SET_RATE_NO_REPARENT, reg,
data->shift, SUNXI_MUX_GATE_WIDTH,
@@ -660,6 +820,8 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
clk_parent = of_clk_get_parent_name(node, 0);
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
reg, data->shift, data->width,
data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0,
@@ -673,6 +835,59 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
/**
+ * sunxi_gates_reset... - reset bits in leaf gate clk registers handling
+ */
+
+struct gates_reset_data {
+ void __iomem *reg;
+ spinlock_t *lock;
+ struct reset_controller_dev rcdev;
+};
+
+static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct gates_reset_data *data = container_of(rcdev,
+ struct gates_reset_data,
+ rcdev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(data->lock, flags);
+
+ reg = readl(data->reg);
+ writel(reg & ~BIT(id), data->reg);
+
+ spin_unlock_irqrestore(data->lock, flags);
+
+ return 0;
+}
+
+static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct gates_reset_data *data = container_of(rcdev,
+ struct gates_reset_data,
+ rcdev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(data->lock, flags);
+
+ reg = readl(data->reg);
+ writel(reg | BIT(id), data->reg);
+
+ spin_unlock_irqrestore(data->lock, flags);
+
+ return 0;
+}
+
+static struct reset_control_ops sunxi_gates_reset_ops = {
+ .assert = sunxi_gates_reset_assert,
+ .deassert = sunxi_gates_reset_deassert,
+};
+
+/**
* sunxi_gates_clk_setup() - Setup function for leaf gates on clocks
*/
@@ -680,6 +895,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
struct gates_data {
DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE);
+ u32 reset_mask;
};
static const struct gates_data sun4i_axi_gates_data __initconst = {
@@ -746,10 +962,21 @@ static const struct gates_data sun7i_a20_apb1_gates_data __initconst = {
.mask = { 0xff80ff },
};
+static const struct gates_data sun4i_a10_usb_gates_data __initconst = {
+ .mask = {0x1C0},
+ .reset_mask = 0x07,
+};
+
+static const struct gates_data sun5i_a13_usb_gates_data __initconst = {
+ .mask = {0x140},
+ .reset_mask = 0x03,
+};
+
static void __init sunxi_gates_clk_setup(struct device_node *node,
struct gates_data *data)
{
struct clk_onecell_data *clk_data;
+ struct gates_reset_data *reset_data;
const char *clk_parent;
const char *clk_name;
void *reg;
@@ -793,6 +1020,21 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
clk_data->clk_num = i;
of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ /* Register a reset controler for gates with reset bits */
+ if (data->reset_mask == 0)
+ return;
+
+ reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
+ if (!reset_data)
+ return;
+
+ reset_data->reg = reg;
+ reset_data->lock = &clk_lock;
+ reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1;
+ reset_data->rcdev.ops = &sunxi_gates_reset_ops;
+ reset_data->rcdev.of_node = node;
+ reset_controller_register(&reset_data->rcdev);
}
@@ -832,7 +1074,7 @@ static const struct divs_data pll5_divs_data __initconst = {
};
static const struct divs_data pll6_divs_data __initconst = {
- .factors = &sun4i_pll5_data,
+ .factors = &sun4i_pll6_data,
.div = {
{ .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */
{ .fixed = 2 }, /* P, other */
@@ -854,7 +1096,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
struct divs_data *data)
{
struct clk_onecell_data *clk_data;
- const char *parent = node->name;
+ const char *parent;
const char *clk_name;
struct clk **clks, *pclk;
struct clk_hw *gate_hw, *rate_hw;
@@ -868,6 +1110,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
/* Set up factor clock that we will be dividing */
pclk = sunxi_factors_clk_setup(node, data->factors);
+ parent = __clk_get_name(pclk);
reg = of_iomap(node, 0);
@@ -970,56 +1213,60 @@ free_clkdata:
/* Matches for factors clocks */
static const struct of_device_id clk_factors_match[] __initconst = {
- {.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,},
+ {.compatible = "allwinner,sun4i-a10-pll1-clk", .data = &sun4i_pll1_data,},
{.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,},
- {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,},
- {.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,},
+ {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,},
+ {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,},
+ {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,},
+ {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,},
{.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,},
{}
};
/* Matches for divider clocks */
static const struct of_device_id clk_div_match[] __initconst = {
- {.compatible = "allwinner,sun4i-axi-clk", .data = &sun4i_axi_data,},
- {.compatible = "allwinner,sun4i-ahb-clk", .data = &sun4i_ahb_data,},
- {.compatible = "allwinner,sun4i-apb0-clk", .data = &sun4i_apb0_data,},
+ {.compatible = "allwinner,sun4i-a10-axi-clk", .data = &sun4i_axi_data,},
+ {.compatible = "allwinner,sun4i-a10-ahb-clk", .data = &sun4i_ahb_data,},
+ {.compatible = "allwinner,sun4i-a10-apb0-clk", .data = &sun4i_apb0_data,},
{.compatible = "allwinner,sun6i-a31-apb2-div-clk", .data = &sun6i_a31_apb2_div_data,},
{}
};
/* Matches for divided outputs */
static const struct of_device_id clk_divs_match[] __initconst = {
- {.compatible = "allwinner,sun4i-pll5-clk", .data = &pll5_divs_data,},
- {.compatible = "allwinner,sun4i-pll6-clk", .data = &pll6_divs_data,},
+ {.compatible = "allwinner,sun4i-a10-pll5-clk", .data = &pll5_divs_data,},
+ {.compatible = "allwinner,sun4i-a10-pll6-clk", .data = &pll6_divs_data,},
{}
};
/* Matches for mux clocks */
static const struct of_device_id clk_mux_match[] __initconst = {
- {.compatible = "allwinner,sun4i-cpu-clk", .data = &sun4i_cpu_mux_data,},
- {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &sun4i_apb1_mux_data,},
+ {.compatible = "allwinner,sun4i-a10-cpu-clk", .data = &sun4i_cpu_mux_data,},
+ {.compatible = "allwinner,sun4i-a10-apb1-mux-clk", .data = &sun4i_apb1_mux_data,},
{.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_mux_data,},
{}
};
/* Matches for gate clocks */
static const struct of_device_id clk_gates_match[] __initconst = {
- {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &sun4i_axi_gates_data,},
- {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &sun4i_ahb_gates_data,},
+ {.compatible = "allwinner,sun4i-a10-axi-gates-clk", .data = &sun4i_axi_gates_data,},
+ {.compatible = "allwinner,sun4i-a10-ahb-gates-clk", .data = &sun4i_ahb_gates_data,},
{.compatible = "allwinner,sun5i-a10s-ahb-gates-clk", .data = &sun5i_a10s_ahb_gates_data,},
{.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,},
{.compatible = "allwinner,sun6i-a31-ahb1-gates-clk", .data = &sun6i_a31_ahb1_gates_data,},
{.compatible = "allwinner,sun7i-a20-ahb-gates-clk", .data = &sun7i_a20_ahb_gates_data,},
- {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &sun4i_apb0_gates_data,},
+ {.compatible = "allwinner,sun4i-a10-apb0-gates-clk", .data = &sun4i_apb0_gates_data,},
{.compatible = "allwinner,sun5i-a10s-apb0-gates-clk", .data = &sun5i_a10s_apb0_gates_data,},
{.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,},
{.compatible = "allwinner,sun7i-a20-apb0-gates-clk", .data = &sun7i_a20_apb0_gates_data,},
- {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &sun4i_apb1_gates_data,},
+ {.compatible = "allwinner,sun4i-a10-apb1-gates-clk", .data = &sun4i_apb1_gates_data,},
{.compatible = "allwinner,sun5i-a10s-apb1-gates-clk", .data = &sun5i_a10s_apb1_gates_data,},
{.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,},
{.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,},
{.compatible = "allwinner,sun7i-a20-apb1-gates-clk", .data = &sun7i_a20_apb1_gates_data,},
{.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,},
+ {.compatible = "allwinner,sun4i-a10-usb-clk", .data = &sun4i_a10_usb_gates_data,},
+ {.compatible = "allwinner,sun5i-a13-usb-clk", .data = &sun5i_a13_usb_gates_data,},
{}
};
diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
index 356e9b804421..9e899c18af86 100644
--- a/drivers/clk/tegra/clk-periph.c
+++ b/drivers/clk/tegra/clk-periph.c
@@ -130,7 +130,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = {
.disable = clk_periph_disable,
};
-const struct clk_ops tegra_clk_periph_no_gate_ops = {
+static const struct clk_ops tegra_clk_periph_no_gate_ops = {
.get_parent = clk_periph_get_parent,
.set_parent = clk_periph_set_parent,
.recalc_rate = clk_periph_recalc_rate,
diff --git a/drivers/clk/ti/clk-33xx.c b/drivers/clk/ti/clk-33xx.c
index 776ee4594bd4..028b33783d38 100644
--- a/drivers/clk/ti/clk-33xx.c
+++ b/drivers/clk/ti/clk-33xx.c
@@ -34,7 +34,6 @@ static struct ti_dt_clk am33xx_clks[] = {
DT_CLK(NULL, "dpll_core_m5_ck", "dpll_core_m5_ck"),
DT_CLK(NULL, "dpll_core_m6_ck", "dpll_core_m6_ck"),
DT_CLK(NULL, "dpll_mpu_ck", "dpll_mpu_ck"),
- DT_CLK("cpu0", NULL, "dpll_mpu_ck"),
DT_CLK(NULL, "dpll_mpu_m2_ck", "dpll_mpu_m2_ck"),
DT_CLK(NULL, "dpll_ddr_ck", "dpll_ddr_ck"),
DT_CLK(NULL, "dpll_ddr_m2_ck", "dpll_ddr_m2_ck"),
diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c
index d3230234f07b..0d1750a8aea4 100644
--- a/drivers/clk/ti/clk-3xxx.c
+++ b/drivers/clk/ti/clk-3xxx.c
@@ -130,10 +130,6 @@ static struct ti_dt_clk omap3xxx_clks[] = {
DT_CLK(NULL, "dss_tv_fck", "dss_tv_fck"),
DT_CLK(NULL, "dss_96m_fck", "dss_96m_fck"),
DT_CLK(NULL, "dss2_alwon_fck", "dss2_alwon_fck"),
- DT_CLK(NULL, "utmi_p1_gfclk", "dummy_ck"),
- DT_CLK(NULL, "utmi_p2_gfclk", "dummy_ck"),
- DT_CLK(NULL, "xclk60mhsp1_ck", "dummy_ck"),
- DT_CLK(NULL, "xclk60mhsp2_ck", "dummy_ck"),
DT_CLK(NULL, "init_60m_fclk", "dummy_ck"),
DT_CLK(NULL, "gpt1_fck", "gpt1_fck"),
DT_CLK(NULL, "aes2_ick", "aes2_ick"),
diff --git a/drivers/clk/ti/clk-44xx.c b/drivers/clk/ti/clk-44xx.c
index ae00218b5da3..02517a8206bd 100644
--- a/drivers/clk/ti/clk-44xx.c
+++ b/drivers/clk/ti/clk-44xx.c
@@ -222,7 +222,6 @@ static struct ti_dt_clk omap44xx_clks[] = {
DT_CLK(NULL, "auxclk5_src_ck", "auxclk5_src_ck"),
DT_CLK(NULL, "auxclk5_ck", "auxclk5_ck"),
DT_CLK(NULL, "auxclkreq5_ck", "auxclkreq5_ck"),
- DT_CLK("50000000.gpmc", "fck", "dummy_ck"),
DT_CLK("omap_i2c.1", "ick", "dummy_ck"),
DT_CLK("omap_i2c.2", "ick", "dummy_ck"),
DT_CLK("omap_i2c.3", "ick", "dummy_ck"),
diff --git a/drivers/clk/ti/clk-54xx.c b/drivers/clk/ti/clk-54xx.c
index 0ef9f581286b..08f3d1b915b3 100644
--- a/drivers/clk/ti/clk-54xx.c
+++ b/drivers/clk/ti/clk-54xx.c
@@ -182,7 +182,6 @@ static struct ti_dt_clk omap54xx_clks[] = {
DT_CLK(NULL, "auxclk3_src_ck", "auxclk3_src_ck"),
DT_CLK(NULL, "auxclk3_ck", "auxclk3_ck"),
DT_CLK(NULL, "auxclkreq3_ck", "auxclkreq3_ck"),
- DT_CLK(NULL, "gpmc_ck", "dummy_ck"),
DT_CLK("omap_i2c.1", "ick", "dummy_ck"),
DT_CLK("omap_i2c.2", "ick", "dummy_ck"),
DT_CLK("omap_i2c.3", "ick", "dummy_ck"),
diff --git a/drivers/clk/ti/clk-7xx.c b/drivers/clk/ti/clk-7xx.c
index 9977653f2d63..f7e40734c819 100644
--- a/drivers/clk/ti/clk-7xx.c
+++ b/drivers/clk/ti/clk-7xx.c
@@ -262,7 +262,6 @@ static struct ti_dt_clk dra7xx_clks[] = {
DT_CLK(NULL, "vip1_gclk_mux", "vip1_gclk_mux"),
DT_CLK(NULL, "vip2_gclk_mux", "vip2_gclk_mux"),
DT_CLK(NULL, "vip3_gclk_mux", "vip3_gclk_mux"),
- DT_CLK(NULL, "gpmc_ck", "dummy_ck"),
DT_CLK("omap_i2c.1", "ick", "dummy_ck"),
DT_CLK("omap_i2c.2", "ick", "dummy_ck"),
DT_CLK("omap_i2c.3", "ick", "dummy_ck"),
diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c
index a15e445570b2..e6aa10db7bba 100644
--- a/drivers/clk/ti/divider.c
+++ b/drivers/clk/ti/divider.c
@@ -112,7 +112,7 @@ static unsigned long ti_clk_divider_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
- return parent_rate / div;
+ return DIV_ROUND_UP(parent_rate, div);
}
/*
@@ -182,7 +182,7 @@ static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
}
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
MULT_ROUND_UP(rate, i));
- now = parent_rate / i;
+ now = DIV_ROUND_UP(parent_rate, i);
if (now <= rate && now > best) {
bestdiv = i;
best = now;
@@ -205,7 +205,7 @@ static long ti_clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
int div;
div = ti_clk_divider_bestdiv(hw, rate, prate);
- return *prate / div;
+ return DIV_ROUND_UP(*prate, div);
}
static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -216,7 +216,7 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long flags = 0;
u32 val;
- div = parent_rate / rate;
+ div = DIV_ROUND_UP(parent_rate, rate);
value = _get_val(divider, div);
if (value > div_mask(divider))
diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c
index cdeff299de26..7b55ef89baa5 100644
--- a/drivers/clk/ux500/u8500_of_clk.c
+++ b/drivers/clk/ux500/u8500_of_clk.c
@@ -29,7 +29,8 @@ static struct clk *prcc_kclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_C
#define PRCC_KCLK_STORE(clk, base, bit) \
prcc_kclk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit] = clk
-struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, void *data)
+static struct clk *ux500_twocell_get(struct of_phandle_args *clkspec,
+ void *data)
{
struct clk **clk_data = data;
unsigned int base, bit;
diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c
index 8cbfcf88fae3..a820b0cfcf57 100644
--- a/drivers/clk/versatile/clk-icst.c
+++ b/drivers/clk/versatile/clk-icst.c
@@ -33,7 +33,7 @@ struct clk_icst {
struct clk_hw hw;
void __iomem *vcoreg;
void __iomem *lockreg;
- const struct icst_params *params;
+ struct icst_params *params;
unsigned long rate;
};
@@ -84,6 +84,8 @@ static unsigned long icst_recalc_rate(struct clk_hw *hw,
struct clk_icst *icst = to_icst(hw);
struct icst_vco vco;
+ if (parent_rate)
+ icst->params->ref = parent_rate;
vco = vco_get(icst->vcoreg);
icst->rate = icst_hz(icst->params, vco);
return icst->rate;
@@ -105,6 +107,8 @@ static int icst_set_rate(struct clk_hw *hw, unsigned long rate,
struct clk_icst *icst = to_icst(hw);
struct icst_vco vco;
+ if (parent_rate)
+ icst->params->ref = parent_rate;
vco = icst_hz_to_vco(icst->params, rate);
icst->rate = icst_hz(icst->params, vco);
vco_set(icst->lockreg, icst->vcoreg, vco);
@@ -120,24 +124,33 @@ static const struct clk_ops icst_ops = {
struct clk *icst_clk_register(struct device *dev,
const struct clk_icst_desc *desc,
const char *name,
+ const char *parent_name,
void __iomem *base)
{
struct clk *clk;
struct clk_icst *icst;
struct clk_init_data init;
+ struct icst_params *pclone;
icst = kzalloc(sizeof(struct clk_icst), GFP_KERNEL);
if (!icst) {
pr_err("could not allocate ICST clock!\n");
return ERR_PTR(-ENOMEM);
}
+
+ pclone = kmemdup(desc->params, sizeof(*pclone), GFP_KERNEL);
+ if (!pclone) {
+ pr_err("could not clone ICST params\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
init.name = name;
init.ops = &icst_ops;
init.flags = CLK_IS_ROOT;
- init.parent_names = NULL;
- init.num_parents = 0;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
icst->hw.init = &init;
- icst->params = desc->params;
+ icst->params = pclone;
icst->vcoreg = base + desc->vco_offset;
icst->lockreg = base + desc->lock_offset;
diff --git a/drivers/clk/versatile/clk-icst.h b/drivers/clk/versatile/clk-icst.h
index be99dd0da785..04e6f0aef588 100644
--- a/drivers/clk/versatile/clk-icst.h
+++ b/drivers/clk/versatile/clk-icst.h
@@ -16,4 +16,5 @@ struct clk_icst_desc {
struct clk *icst_clk_register(struct device *dev,
const struct clk_icst_desc *desc,
const char *name,
+ const char *parent_name,
void __iomem *base);
diff --git a/drivers/clk/versatile/clk-impd1.c b/drivers/clk/versatile/clk-impd1.c
index 844f8d711a12..31b44f025f9e 100644
--- a/drivers/clk/versatile/clk-impd1.c
+++ b/drivers/clk/versatile/clk-impd1.c
@@ -13,10 +13,12 @@
#include <linux/io.h>
#include <linux/platform_data/clk-integrator.h>
-#include <mach/impd1.h>
-
#include "clk-icst.h"
+#define IMPD1_OSC1 0x00
+#define IMPD1_OSC2 0x04
+#define IMPD1_LOCK 0x08
+
struct impd1_clk {
char *vco1name;
struct clk *vco1clk;
@@ -93,13 +95,15 @@ void integrator_impd1_clk_init(void __iomem *base, unsigned int id)
imc = &impd1_clks[id];
imc->vco1name = kasprintf(GFP_KERNEL, "lm%x-vco1", id);
- clk = icst_clk_register(NULL, &impd1_icst1_desc, imc->vco1name, base);
+ clk = icst_clk_register(NULL, &impd1_icst1_desc, imc->vco1name, NULL,
+ base);
imc->vco1clk = clk;
imc->clks[0] = clkdev_alloc(clk, NULL, "lm%x:01000", id);
/* VCO2 is also called "CLK2" */
imc->vco2name = kasprintf(GFP_KERNEL, "lm%x-vco2", id);
- clk = icst_clk_register(NULL, &impd1_icst2_desc, imc->vco2name, base);
+ clk = icst_clk_register(NULL, &impd1_icst2_desc, imc->vco2name, NULL,
+ base);
imc->vco2clk = clk;
/* MMCI uses CLK2 right off */
diff --git a/drivers/clk/versatile/clk-integrator.c b/drivers/clk/versatile/clk-integrator.c
index bda8967e09c2..734c4b8fe6ab 100644
--- a/drivers/clk/versatile/clk-integrator.c
+++ b/drivers/clk/versatile/clk-integrator.c
@@ -10,21 +10,17 @@
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <linux/platform_data/clk-integrator.h>
-
-#include <mach/hardware.h>
-#include <mach/platform.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
#include "clk-icst.h"
-/*
- * Implementation of the ARM Integrator/AP and Integrator/CP clock tree.
- * Inspired by portions of:
- * plat-versatile/clock.c and plat-versatile/include/plat/clock.h
- */
+#define INTEGRATOR_HDR_LOCK_OFFSET 0x14
-static const struct icst_params cp_auxvco_params = {
- .ref = 24000000,
+/* Base offset for the core module */
+static void __iomem *cm_base;
+
+static const struct icst_params cp_auxosc_params = {
.vco_max = ICST525_VCO_MAX_5V,
.vco_min = ICST525_VCO_MIN,
.vd_min = 8,
@@ -35,50 +31,39 @@ static const struct icst_params cp_auxvco_params = {
.idx2s = icst525_idx2s,
};
-static const struct clk_icst_desc __initdata cp_icst_desc = {
- .params = &cp_auxvco_params,
+static const struct clk_icst_desc __initdata cm_auxosc_desc = {
+ .params = &cp_auxosc_params,
.vco_offset = 0x1c,
.lock_offset = INTEGRATOR_HDR_LOCK_OFFSET,
};
-/*
- * integrator_clk_init() - set up the integrator clock tree
- * @is_cp: pass true if it's the Integrator/CP else AP is assumed
- */
-void __init integrator_clk_init(bool is_cp)
+static void __init of_integrator_cm_osc_setup(struct device_node *np)
{
- struct clk *clk;
-
- /* APB clock dummy */
- clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0);
- clk_register_clkdev(clk, "apb_pclk", NULL);
-
- /* UART reference clock */
- clk = clk_register_fixed_rate(NULL, "uartclk", NULL, CLK_IS_ROOT,
- 14745600);
- clk_register_clkdev(clk, NULL, "uart0");
- clk_register_clkdev(clk, NULL, "uart1");
- if (is_cp)
- clk_register_clkdev(clk, NULL, "mmci");
-
- /* 24 MHz clock */
- clk = clk_register_fixed_rate(NULL, "clk24mhz", NULL, CLK_IS_ROOT,
- 24000000);
- clk_register_clkdev(clk, NULL, "kmi0");
- clk_register_clkdev(clk, NULL, "kmi1");
- if (!is_cp)
- clk_register_clkdev(clk, NULL, "ap_timer");
+ struct clk *clk = ERR_PTR(-EINVAL);
+ const char *clk_name = np->name;
+ const struct clk_icst_desc *desc = &cm_auxosc_desc;
+ const char *parent_name;
- if (!is_cp)
- return;
+ if (!cm_base) {
+ /* Remap the core module base if not done yet */
+ struct device_node *parent;
- /* 1 MHz clock */
- clk = clk_register_fixed_rate(NULL, "clk1mhz", NULL, CLK_IS_ROOT,
- 1000000);
- clk_register_clkdev(clk, NULL, "sp804");
+ parent = of_get_parent(np);
+ if (!np) {
+ pr_err("no parent on core module clock\n");
+ return;
+ }
+ cm_base = of_iomap(parent, 0);
+ if (!cm_base) {
+ pr_err("could not remap core module base\n");
+ return;
+ }
+ }
- /* ICST VCO clock used on the Integrator/CP CLCD */
- clk = icst_clk_register(NULL, &cp_icst_desc, "icst",
- __io_address(INTEGRATOR_HDR_BASE));
- clk_register_clkdev(clk, NULL, "clcd");
+ parent_name = of_clk_get_parent_name(np, 0);
+ clk = icst_clk_register(NULL, desc, clk_name, parent_name, cm_base);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
}
+CLK_OF_DECLARE(integrator_cm_auxosc_clk,
+ "arm,integrator-cm-auxosc", of_integrator_cm_osc_setup);
diff --git a/drivers/clk/versatile/clk-realview.c b/drivers/clk/versatile/clk-realview.c
index 747e7b31117c..c8b523117fb7 100644
--- a/drivers/clk/versatile/clk-realview.c
+++ b/drivers/clk/versatile/clk-realview.c
@@ -85,10 +85,10 @@ void __init realview_clk_init(void __iomem *sysbase, bool is_pb1176)
/* ICST VCO clock */
if (is_pb1176)
clk = icst_clk_register(NULL, &realview_osc0_desc,
- "osc0", sysbase);
+ "osc0", NULL, sysbase);
else
clk = icst_clk_register(NULL, &realview_osc4_desc,
- "osc4", sysbase);
+ "osc4", NULL, sysbase);
clk_register_clkdev(clk, NULL, "dev:clcd");
clk_register_clkdev(clk, NULL, "issp:clcd");
diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c
index 09dd0173ea0a..52c09afdcfb7 100644
--- a/drivers/clk/zynq/clkc.c
+++ b/drivers/clk/zynq/clkc.c
@@ -21,34 +21,35 @@
#include <linux/clk/zynq.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/io.h>
-static void __iomem *zynq_slcr_base_priv;
-
-#define SLCR_ARMPLL_CTRL (zynq_slcr_base_priv + 0x100)
-#define SLCR_DDRPLL_CTRL (zynq_slcr_base_priv + 0x104)
-#define SLCR_IOPLL_CTRL (zynq_slcr_base_priv + 0x108)
-#define SLCR_PLL_STATUS (zynq_slcr_base_priv + 0x10c)
-#define SLCR_ARM_CLK_CTRL (zynq_slcr_base_priv + 0x120)
-#define SLCR_DDR_CLK_CTRL (zynq_slcr_base_priv + 0x124)
-#define SLCR_DCI_CLK_CTRL (zynq_slcr_base_priv + 0x128)
-#define SLCR_APER_CLK_CTRL (zynq_slcr_base_priv + 0x12c)
-#define SLCR_GEM0_CLK_CTRL (zynq_slcr_base_priv + 0x140)
-#define SLCR_GEM1_CLK_CTRL (zynq_slcr_base_priv + 0x144)
-#define SLCR_SMC_CLK_CTRL (zynq_slcr_base_priv + 0x148)
-#define SLCR_LQSPI_CLK_CTRL (zynq_slcr_base_priv + 0x14c)
-#define SLCR_SDIO_CLK_CTRL (zynq_slcr_base_priv + 0x150)
-#define SLCR_UART_CLK_CTRL (zynq_slcr_base_priv + 0x154)
-#define SLCR_SPI_CLK_CTRL (zynq_slcr_base_priv + 0x158)
-#define SLCR_CAN_CLK_CTRL (zynq_slcr_base_priv + 0x15c)
-#define SLCR_CAN_MIOCLK_CTRL (zynq_slcr_base_priv + 0x160)
-#define SLCR_DBG_CLK_CTRL (zynq_slcr_base_priv + 0x164)
-#define SLCR_PCAP_CLK_CTRL (zynq_slcr_base_priv + 0x168)
-#define SLCR_FPGA0_CLK_CTRL (zynq_slcr_base_priv + 0x170)
-#define SLCR_621_TRUE (zynq_slcr_base_priv + 0x1c4)
-#define SLCR_SWDT_CLK_SEL (zynq_slcr_base_priv + 0x304)
+static void __iomem *zynq_clkc_base;
+
+#define SLCR_ARMPLL_CTRL (zynq_clkc_base + 0x00)
+#define SLCR_DDRPLL_CTRL (zynq_clkc_base + 0x04)
+#define SLCR_IOPLL_CTRL (zynq_clkc_base + 0x08)
+#define SLCR_PLL_STATUS (zynq_clkc_base + 0x0c)
+#define SLCR_ARM_CLK_CTRL (zynq_clkc_base + 0x20)
+#define SLCR_DDR_CLK_CTRL (zynq_clkc_base + 0x24)
+#define SLCR_DCI_CLK_CTRL (zynq_clkc_base + 0x28)
+#define SLCR_APER_CLK_CTRL (zynq_clkc_base + 0x2c)
+#define SLCR_GEM0_CLK_CTRL (zynq_clkc_base + 0x40)
+#define SLCR_GEM1_CLK_CTRL (zynq_clkc_base + 0x44)
+#define SLCR_SMC_CLK_CTRL (zynq_clkc_base + 0x48)
+#define SLCR_LQSPI_CLK_CTRL (zynq_clkc_base + 0x4c)
+#define SLCR_SDIO_CLK_CTRL (zynq_clkc_base + 0x50)
+#define SLCR_UART_CLK_CTRL (zynq_clkc_base + 0x54)
+#define SLCR_SPI_CLK_CTRL (zynq_clkc_base + 0x58)
+#define SLCR_CAN_CLK_CTRL (zynq_clkc_base + 0x5c)
+#define SLCR_CAN_MIOCLK_CTRL (zynq_clkc_base + 0x60)
+#define SLCR_DBG_CLK_CTRL (zynq_clkc_base + 0x64)
+#define SLCR_PCAP_CLK_CTRL (zynq_clkc_base + 0x68)
+#define SLCR_FPGA0_CLK_CTRL (zynq_clkc_base + 0x70)
+#define SLCR_621_TRUE (zynq_clkc_base + 0xc4)
+#define SLCR_SWDT_CLK_SEL (zynq_clkc_base + 0x204)
#define NUM_MIO_PINS 54
@@ -148,7 +149,7 @@ static void __init zynq_clk_register_fclk(enum zynq_clk fclk,
clks[fclk] = clk_register_gate(NULL, clk_name,
div1_name, CLK_SET_RATE_PARENT, fclk_gate_reg,
0, CLK_GATE_SET_TO_DISABLE, fclk_gate_lock);
- enable_reg = readl(fclk_gate_reg) & 1;
+ enable_reg = clk_readl(fclk_gate_reg) & 1;
if (enable && !enable_reg) {
if (clk_prepare_enable(clks[fclk]))
pr_warn("%s: FCLK%u enable failed\n", __func__,
@@ -277,7 +278,7 @@ static void __init zynq_clk_setup(struct device_node *np)
SLCR_IOPLL_CTRL, 4, 1, 0, &iopll_lock);
/* CPU clocks */
- tmp = readl(SLCR_621_TRUE) & 1;
+ tmp = clk_readl(SLCR_621_TRUE) & 1;
clk = clk_register_mux(NULL, "cpu_mux", cpu_parents, 4,
CLK_SET_RATE_NO_REPARENT, SLCR_ARM_CLK_CTRL, 4, 2, 0,
&armclk_lock);
@@ -569,8 +570,42 @@ static void __init zynq_clk_setup(struct device_node *np)
CLK_OF_DECLARE(zynq_clkc, "xlnx,ps7-clkc", zynq_clk_setup);
-void __init zynq_clock_init(void __iomem *slcr_base)
+void __init zynq_clock_init(void)
{
- zynq_slcr_base_priv = slcr_base;
- of_clk_init(NULL);
+ struct device_node *np;
+ struct device_node *slcr;
+ struct resource res;
+
+ np = of_find_compatible_node(NULL, NULL, "xlnx,ps7-clkc");
+ if (!np) {
+ pr_err("%s: clkc node not found\n", __func__);
+ goto np_err;
+ }
+
+ if (of_address_to_resource(np, 0, &res)) {
+ pr_err("%s: failed to get resource\n", np->name);
+ goto np_err;
+ }
+
+ slcr = of_get_parent(np);
+
+ if (slcr->data) {
+ zynq_clkc_base = (__force void __iomem *)slcr->data + res.start;
+ } else {
+ pr_err("%s: Unable to get I/O memory\n", np->name);
+ of_node_put(slcr);
+ goto np_err;
+ }
+
+ pr_info("%s: clkc starts at %p\n", __func__, zynq_clkc_base);
+
+ of_node_put(slcr);
+ of_node_put(np);
+
+ return;
+
+np_err:
+ of_node_put(np);
+ BUG();
+ return;
}
diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c
index 3226f54fa595..cec97596fe65 100644
--- a/drivers/clk/zynq/pll.c
+++ b/drivers/clk/zynq/pll.c
@@ -90,7 +90,7 @@ static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw,
* makes probably sense to redundantly save fbdiv in the struct
* zynq_pll to save the IO access.
*/
- fbdiv = (readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >>
+ fbdiv = (clk_readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >>
PLLCTRL_FBDIV_SHIFT;
return parent_rate * fbdiv;
@@ -112,7 +112,7 @@ static int zynq_pll_is_enabled(struct clk_hw *hw)
spin_lock_irqsave(clk->lock, flags);
- reg = readl(clk->pll_ctrl);
+ reg = clk_readl(clk->pll_ctrl);
spin_unlock_irqrestore(clk->lock, flags);
@@ -138,10 +138,10 @@ static int zynq_pll_enable(struct clk_hw *hw)
/* Power up PLL and wait for lock */
spin_lock_irqsave(clk->lock, flags);
- reg = readl(clk->pll_ctrl);
+ reg = clk_readl(clk->pll_ctrl);
reg &= ~(PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK);
- writel(reg, clk->pll_ctrl);
- while (!(readl(clk->pll_status) & (1 << clk->lockbit)))
+ clk_writel(reg, clk->pll_ctrl);
+ while (!(clk_readl(clk->pll_status) & (1 << clk->lockbit)))
;
spin_unlock_irqrestore(clk->lock, flags);
@@ -168,9 +168,9 @@ static void zynq_pll_disable(struct clk_hw *hw)
/* shut down PLL */
spin_lock_irqsave(clk->lock, flags);
- reg = readl(clk->pll_ctrl);
+ reg = clk_readl(clk->pll_ctrl);
reg |= PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK;
- writel(reg, clk->pll_ctrl);
+ clk_writel(reg, clk->pll_ctrl);
spin_unlock_irqrestore(clk->lock, flags);
}
@@ -225,9 +225,9 @@ struct clk *clk_register_zynq_pll(const char *name, const char *parent,
spin_lock_irqsave(pll->lock, flags);
- reg = readl(pll->pll_ctrl);
+ reg = clk_readl(pll->pll_ctrl);
reg &= ~PLLCTRL_BPQUAL_MASK;
- writel(reg, pll->pll_ctrl);
+ clk_writel(reg, pll->pll_ctrl);
spin_unlock_irqrestore(pll->lock, flags);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 52e9329e3c51..96918e1f26a3 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -188,3 +188,6 @@ config EM_TIMER_STI
This enables build of a clocksource and clockevent driver for
the 48-bit System Timer (STI) hardware available on a SoCs
such as EMEV2 from former NEC Electronics.
+
+config CLKSRC_QCOM
+ bool
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index f3fe4cb4974b..98cb6c51aa87 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
+obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
diff --git a/drivers/clocksource/dummy_timer.c b/drivers/clocksource/dummy_timer.c
index b3eb582d6a6f..ad3572541728 100644
--- a/drivers/clocksource/dummy_timer.c
+++ b/drivers/clocksource/dummy_timer.c
@@ -56,14 +56,19 @@ static struct notifier_block dummy_timer_cpu_nb = {
static int __init dummy_timer_register(void)
{
- int err = register_cpu_notifier(&dummy_timer_cpu_nb);
+ int err = 0;
+
+ cpu_notifier_register_begin();
+ err = __register_cpu_notifier(&dummy_timer_cpu_nb);
if (err)
- return err;
+ goto out;
/* We won't get a call on the boot CPU, so register immediately */
if (num_possible_cpus() > 1)
dummy_timer_setup();
- return 0;
+out:
+ cpu_notifier_register_done();
+ return err;
}
early_initcall(dummy_timer_register);
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index c2e390efbdca..a6ee6d7cd63f 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -25,8 +25,6 @@
#include <linux/of_address.h>
#include <linux/clocksource.h>
-#include <asm/mach/time.h>
-
#define EXYNOS4_MCTREG(x) (x)
#define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100)
#define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104)
diff --git a/arch/arm/mach-msm/timer.c b/drivers/clocksource/qcom-timer.c
index fd1644987534..e807acf4c665 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/drivers/clocksource/qcom-timer.c
@@ -1,7 +1,7 @@
/*
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2012,2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -26,10 +26,6 @@
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
-#include <asm/mach/time.h>
-
-#include "common.h"
-
#define TIMER_MATCH_VAL 0x0000
#define TIMER_COUNT_VAL 0x0004
#define TIMER_ENABLE 0x0008
@@ -110,15 +106,6 @@ static notrace cycle_t msm_read_timer_count(struct clocksource *cs)
return readl_relaxed(source_base + TIMER_COUNT_VAL);
}
-static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
-{
- /*
- * Shift timer count down by a constant due to unreliable lower bits
- * on some targets.
- */
- return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
-}
-
static struct clocksource msm_clocksource = {
.name = "dg_timer",
.rating = 300,
@@ -232,7 +219,7 @@ err:
sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz);
}
-#ifdef CONFIG_OF
+#ifdef CONFIG_ARCH_QCOM
static void __init msm_dt_timer_init(struct device_node *np)
{
u32 freq;
@@ -285,7 +272,7 @@ static void __init msm_dt_timer_init(struct device_node *np)
}
CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
-#endif
+#else
static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
u32 sts)
@@ -305,6 +292,15 @@ static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
return 0;
}
+static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
+{
+ /*
+ * Shift timer count down by a constant due to unreliable lower bits
+ * on some targets.
+ */
+ return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
+}
+
void __init msm7x01_timer_init(void)
{
struct clocksource *cs = &msm_clocksource;
@@ -331,3 +327,4 @@ void __init qsd8x50_timer_init(void)
return;
msm_timer_init(19200000 / 4, 32, 7, false);
}
+#endif
diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c
index 09a17d9a6594..b52e1c078b99 100644
--- a/drivers/clocksource/timer-marco.c
+++ b/drivers/clocksource/timer-marco.c
@@ -19,7 +19,8 @@
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/sched_clock.h>
-#include <asm/mach/time.h>
+
+#define MARCO_CLOCK_FREQ 1000000
#define SIRFSOC_TIMER_32COUNTER_0_CTRL 0x0000
#define SIRFSOC_TIMER_32COUNTER_1_CTRL 0x0004
@@ -191,7 +192,7 @@ static int sirfsoc_local_timer_setup(struct clock_event_device *ce)
ce->rating = 200;
ce->set_mode = sirfsoc_timer_set_mode;
ce->set_next_event = sirfsoc_timer_set_next_event;
- clockevents_calc_mult_shift(ce, CLOCK_TICK_RATE, 60);
+ clockevents_calc_mult_shift(ce, MARCO_CLOCK_FREQ, 60);
ce->max_delta_ns = clockevent_delta2ns(-2, ce);
ce->min_delta_ns = clockevent_delta2ns(2, ce);
ce->cpumask = cpumask_of(cpu);
@@ -263,11 +264,11 @@ static void __init sirfsoc_marco_timer_init(void)
BUG_ON(IS_ERR(clk));
rate = clk_get_rate(clk);
- BUG_ON(rate < CLOCK_TICK_RATE);
- BUG_ON(rate % CLOCK_TICK_RATE);
+ BUG_ON(rate < MARCO_CLOCK_FREQ);
+ BUG_ON(rate % MARCO_CLOCK_FREQ);
/* Initialize the timer dividers */
- timer_div = rate / CLOCK_TICK_RATE - 1;
+ timer_div = rate / MARCO_CLOCK_FREQ - 1;
writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL);
writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL);
@@ -283,7 +284,7 @@ static void __init sirfsoc_marco_timer_init(void)
/* Clear all interrupts */
writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
- BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
+ BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, MARCO_CLOCK_FREQ));
sirfsoc_clockevent_init();
}
diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c
index 8a492d34ff9f..1a6b2d6356d6 100644
--- a/drivers/clocksource/timer-prima2.c
+++ b/drivers/clocksource/timer-prima2.c
@@ -21,6 +21,8 @@
#include <linux/sched_clock.h>
#include <asm/mach/time.h>
+#define PRIMA2_CLOCK_FREQ 1000000
+
#define SIRFSOC_TIMER_COUNTER_LO 0x0000
#define SIRFSOC_TIMER_COUNTER_HI 0x0004
#define SIRFSOC_TIMER_MATCH_0 0x0008
@@ -173,7 +175,7 @@ static u64 notrace sirfsoc_read_sched_clock(void)
static void __init sirfsoc_clockevent_init(void)
{
sirfsoc_clockevent.cpumask = cpumask_of(0);
- clockevents_config_and_register(&sirfsoc_clockevent, CLOCK_TICK_RATE,
+ clockevents_config_and_register(&sirfsoc_clockevent, PRIMA2_CLOCK_FREQ,
2, -2);
}
@@ -190,8 +192,8 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np)
rate = clk_get_rate(clk);
- BUG_ON(rate < CLOCK_TICK_RATE);
- BUG_ON(rate % CLOCK_TICK_RATE);
+ BUG_ON(rate < PRIMA2_CLOCK_FREQ);
+ BUG_ON(rate % PRIMA2_CLOCK_FREQ);
sirfsoc_timer_base = of_iomap(np, 0);
if (!sirfsoc_timer_base)
@@ -199,14 +201,16 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np)
sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
- writel_relaxed(rate / CLOCK_TICK_RATE / 2 - 1, sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
+ writel_relaxed(rate / PRIMA2_CLOCK_FREQ / 2 - 1,
+ sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
- BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
+ BUG_ON(clocksource_register_hz(&sirfsoc_clocksource,
+ PRIMA2_CLOCK_FREQ));
- sched_clock_register(sirfsoc_read_sched_clock, 64, CLOCK_TICK_RATE);
+ sched_clock_register(sirfsoc_read_sched_clock, 64, PRIMA2_CLOCK_FREQ);
BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
diff --git a/drivers/clocksource/timer-u300.c b/drivers/clocksource/timer-u300.c
index e63d469661fd..5dcf756970e7 100644
--- a/drivers/clocksource/timer-u300.c
+++ b/drivers/clocksource/timer-u300.c
@@ -333,7 +333,7 @@ static irqreturn_t u300_timer_interrupt(int irq, void *dev_id)
static struct irqaction u300_timer_irq = {
.name = "U300 Timer Tick",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = u300_timer_interrupt,
};
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index 77afe7487d34..b14f1d36f897 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -145,7 +145,6 @@ static int cn_call_callback(struct sk_buff *skb)
spin_unlock_bh(&dev->cbdev->queue_lock);
if (cbq != NULL) {
- err = 0;
cbq->callback(msg, nsp);
kfree_skb(skb);
cn_queue_release_callback(cbq);
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 9fb627046e17..1e2b9db563ec 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -122,7 +122,7 @@ config ARM_INTEGRATOR
If in doubt, say Y.
config ARM_KIRKWOOD_CPUFREQ
- def_bool ARCH_KIRKWOOD && OF
+ def_bool MACH_KIRKWOOD
help
This adds the CPUFreq driver for Marvell Kirkwood
SoCs.
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index 822ca03a87f7..d5eaedbe464f 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -906,15 +906,16 @@ static void __init acpi_cpufreq_boost_init(void)
acpi_cpufreq_driver.boost_supported = true;
acpi_cpufreq_driver.boost_enabled = boost_state(0);
- get_online_cpus();
+
+ cpu_notifier_register_begin();
/* Force all MSRs to the same value */
boost_set_msrs(acpi_cpufreq_driver.boost_enabled,
cpu_online_mask);
- register_cpu_notifier(&boost_nb);
+ __register_cpu_notifier(&boost_nb);
- put_online_cpus();
+ cpu_notifier_register_done();
}
}
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
index d988948a89a0..97ccc31dbdd8 100644
--- a/drivers/cpuidle/Kconfig.arm
+++ b/drivers/cpuidle/Kconfig.arm
@@ -22,7 +22,7 @@ config ARM_HIGHBANK_CPUIDLE
config ARM_KIRKWOOD_CPUIDLE
bool "CPU Idle Driver for Marvell Kirkwood SoCs"
- depends on ARCH_KIRKWOOD
+ depends on ARCH_KIRKWOOD || MACH_KIRKWOOD
help
This adds the CPU Idle driver for Marvell Kirkwood SoCs.
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index cb20fd915be8..8236746e46bb 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -65,6 +65,26 @@ int cpuidle_play_dead(void)
}
/**
+ * cpuidle_enabled - check if the cpuidle framework is ready
+ * @dev: cpuidle device for this cpu
+ * @drv: cpuidle driver for this cpu
+ *
+ * Return 0 on success, otherwise:
+ * -NODEV : the cpuidle framework is not available
+ * -EBUSY : the cpuidle framework is not initialized
+ */
+int cpuidle_enabled(struct cpuidle_driver *drv, struct cpuidle_device *dev)
+{
+ if (off || !initialized)
+ return -ENODEV;
+
+ if (!drv || !dev || !dev->enabled)
+ return -EBUSY;
+
+ return 0;
+}
+
+/**
* cpuidle_enter_state - enter the state and update stats
* @dev: cpuidle device for this cpu
* @drv: cpuidle driver for this cpu
@@ -109,63 +129,48 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
}
/**
- * cpuidle_idle_call - the main idle loop
+ * cpuidle_select - ask the cpuidle framework to choose an idle state
+ *
+ * @drv: the cpuidle driver
+ * @dev: the cpuidle device
*
- * NOTE: no locks or semaphores should be used here
- * return non-zero on failure
+ * Returns the index of the idle state.
*/
-int cpuidle_idle_call(void)
+int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
- struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
- struct cpuidle_driver *drv;
- int next_state, entered_state;
- bool broadcast;
-
- if (off || !initialized)
- return -ENODEV;
-
- /* check if the device is ready */
- if (!dev || !dev->enabled)
- return -EBUSY;
-
- drv = cpuidle_get_cpu_driver(dev);
-
- /* ask the governor for the next state */
- next_state = cpuidle_curr_governor->select(drv, dev);
- if (need_resched()) {
- dev->last_residency = 0;
- /* give the governor an opportunity to reflect on the outcome */
- if (cpuidle_curr_governor->reflect)
- cpuidle_curr_governor->reflect(dev, next_state);
- local_irq_enable();
- return 0;
- }
-
- broadcast = !!(drv->states[next_state].flags & CPUIDLE_FLAG_TIMER_STOP);
-
- if (broadcast &&
- clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu))
- return -EBUSY;
-
-
- trace_cpu_idle_rcuidle(next_state, dev->cpu);
-
- if (cpuidle_state_is_coupled(dev, drv, next_state))
- entered_state = cpuidle_enter_state_coupled(dev, drv,
- next_state);
- else
- entered_state = cpuidle_enter_state(dev, drv, next_state);
-
- trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
+ return cpuidle_curr_governor->select(drv, dev);
+}
- if (broadcast)
- clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
+/**
+ * cpuidle_enter - enter into the specified idle state
+ *
+ * @drv: the cpuidle driver tied with the cpu
+ * @dev: the cpuidle device
+ * @index: the index in the idle state table
+ *
+ * Returns the index in the idle state, < 0 in case of error.
+ * The error code depends on the backend driver
+ */
+int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev,
+ int index)
+{
+ if (cpuidle_state_is_coupled(dev, drv, index))
+ return cpuidle_enter_state_coupled(dev, drv, index);
+ return cpuidle_enter_state(dev, drv, index);
+}
- /* give the governor an opportunity to reflect on the outcome */
+/**
+ * cpuidle_reflect - tell the underlying governor what was the state
+ * we were in
+ *
+ * @dev : the cpuidle device
+ * @index: the index in the idle state table
+ *
+ */
+void cpuidle_reflect(struct cpuidle_device *dev, int index)
+{
if (cpuidle_curr_governor->reflect)
- cpuidle_curr_governor->reflect(dev, entered_state);
-
- return 0;
+ cpuidle_curr_governor->reflect(dev, index);
}
/**
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 13857f5d28f7..03ccdb0ccf9e 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -262,6 +262,17 @@ config CRYPTO_DEV_OMAP_AES
OMAP processors have AES module accelerator. Select this if you
want to use the OMAP module for AES algorithms.
+config CRYPTO_DEV_OMAP_DES
+ tristate "Support for OMAP DES3DES hw engine"
+ depends on ARCH_OMAP2PLUS
+ select CRYPTO_DES
+ select CRYPTO_BLKCIPHER2
+ help
+ OMAP processors have DES/3DES module accelerator. Select this if you
+ want to use the OMAP module for DES and 3DES algorithms. Currently
+ the ECB and CBC modes of operation supported by the driver. Also
+ accesses made on unaligned boundaries are also supported.
+
config CRYPTO_DEV_PICOXCELL
tristate "Support for picoXcell IPSEC and Layer2 crypto engines"
depends on ARCH_PICOXCELL && HAVE_CLK
@@ -300,17 +311,6 @@ config CRYPTO_DEV_S5P
Select this to offload Samsung S5PV210 or S5PC110 from AES
algorithms execution.
-config CRYPTO_DEV_TEGRA_AES
- tristate "Support for TEGRA AES hw engine"
- depends on ARCH_TEGRA
- select CRYPTO_AES
- help
- TEGRA processors have AES module accelerator. Select this if you
- want to use the TEGRA module for AES algorithms.
-
- To compile this driver as a module, choose M here: the module
- will be called tegra-aes.
-
config CRYPTO_DEV_NX
bool "Support for IBM Power7+ in-Nest cryptographic acceleration"
depends on PPC64 && IBMVIO
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 0bc6aa0a54d7..482f090d16d0 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_CRYPTO_DEV_NIAGARA2) += n2_crypto.o
n2_crypto-y := n2_core.o n2_asm.o
obj-$(CONFIG_CRYPTO_DEV_NX) += nx/
obj-$(CONFIG_CRYPTO_DEV_OMAP_AES) += omap-aes.o
+obj-$(CONFIG_CRYPTO_DEV_OMAP_DES) += omap-des.o
obj-$(CONFIG_CRYPTO_DEV_OMAP_SHAM) += omap-sham.o
obj-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o
obj-$(CONFIG_CRYPTO_DEV_PADLOCK_SHA) += padlock-sha.o
@@ -21,5 +22,4 @@ obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/
obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o
obj-$(CONFIG_CRYPTO_DEV_SAHARA) += sahara.o
obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
-obj-$(CONFIG_CRYPTO_DEV_TEGRA_AES) += tegra-aes.o
obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/
diff --git a/drivers/crypto/bfin_crc.c b/drivers/crypto/bfin_crc.c
index d797f31f5d85..c9ff298e6d26 100644
--- a/drivers/crypto/bfin_crc.c
+++ b/drivers/crypto/bfin_crc.c
@@ -139,7 +139,6 @@ static int bfin_crypto_crc_init_hw(struct bfin_crypto_crc *crc, u32 key)
/* setup CRC interrupts */
crc->regs->status = CMPERRI | DCNTEXPI;
crc->regs->intrenset = CMPERRI | DCNTEXPI;
- SSYNC();
return 0;
}
@@ -285,17 +284,12 @@ static void bfin_crypto_crc_config_dma(struct bfin_crypto_crc *crc)
if (i == 0)
return;
- flush_dcache_range((unsigned int)crc->sg_cpu,
- (unsigned int)crc->sg_cpu +
- i * sizeof(struct dma_desc_array));
-
/* Set the last descriptor to stop mode */
crc->sg_cpu[i - 1].cfg &= ~(DMAFLOW | NDSIZE);
crc->sg_cpu[i - 1].cfg |= DI_EN;
set_dma_curr_desc_addr(crc->dma_ch, (unsigned long *)crc->sg_dma);
set_dma_x_count(crc->dma_ch, 0);
set_dma_x_modify(crc->dma_ch, 0);
- SSYNC();
set_dma_config(crc->dma_ch, dma_config);
}
@@ -415,7 +409,6 @@ finish_update:
/* finally kick off CRC operation */
crc->regs->control |= BLKEN;
- SSYNC();
return -EINPROGRESS;
}
@@ -539,7 +532,6 @@ static irqreturn_t bfin_crypto_crc_handler(int irq, void *dev_id)
if (crc->regs->status & DCNTEXP) {
crc->regs->status = DCNTEXP;
- SSYNC();
/* prepare results */
put_unaligned_le32(crc->regs->result, crc->req->result);
@@ -594,7 +586,7 @@ static int bfin_crypto_crc_probe(struct platform_device *pdev)
unsigned int timeout = 100000;
int ret;
- crc = kzalloc(sizeof(*crc), GFP_KERNEL);
+ crc = devm_kzalloc(dev, sizeof(*crc), GFP_KERNEL);
if (!crc) {
dev_err(&pdev->dev, "fail to malloc bfin_crypto_crc\n");
return -ENOMEM;
@@ -610,42 +602,39 @@ static int bfin_crypto_crc_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
- ret = -ENOENT;
- goto out_error_free_mem;
+ return -ENOENT;
}
- crc->regs = ioremap(res->start, resource_size(res));
- if (!crc->regs) {
+ crc->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR((void *)crc->regs)) {
dev_err(&pdev->dev, "Cannot map CRC IO\n");
- ret = -ENXIO;
- goto out_error_free_mem;
+ return PTR_ERR((void *)crc->regs);
}
crc->irq = platform_get_irq(pdev, 0);
if (crc->irq < 0) {
dev_err(&pdev->dev, "No CRC DCNTEXP IRQ specified\n");
- ret = -ENOENT;
- goto out_error_unmap;
+ return -ENOENT;
}
- ret = request_irq(crc->irq, bfin_crypto_crc_handler, IRQF_SHARED, dev_name(dev), crc);
+ ret = devm_request_irq(dev, crc->irq, bfin_crypto_crc_handler,
+ IRQF_SHARED, dev_name(dev), crc);
if (ret) {
dev_err(&pdev->dev, "Unable to request blackfin crc irq\n");
- goto out_error_unmap;
+ return ret;
}
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (res == NULL) {
dev_err(&pdev->dev, "No CRC DMA channel specified\n");
- ret = -ENOENT;
- goto out_error_irq;
+ return -ENOENT;
}
crc->dma_ch = res->start;
ret = request_dma(crc->dma_ch, dev_name(dev));
if (ret) {
dev_err(&pdev->dev, "Unable to attach Blackfin CRC DMA channel\n");
- goto out_error_irq;
+ return ret;
}
crc->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &crc->sg_dma, GFP_KERNEL);
@@ -660,9 +649,7 @@ static int bfin_crypto_crc_probe(struct platform_device *pdev)
crc->sg_mid_buf = (u8 *)(crc->sg_cpu + ((CRC_MAX_DMA_DESC + 1) << 1));
crc->regs->control = 0;
- SSYNC();
crc->regs->poly = crc->poly = (u32)pdev->dev.platform_data;
- SSYNC();
while (!(crc->regs->status & LUTDONE) && (--timeout) > 0)
cpu_relax();
@@ -693,12 +680,6 @@ out_error_dma:
if (crc->sg_cpu)
dma_free_coherent(&pdev->dev, PAGE_SIZE, crc->sg_cpu, crc->sg_dma);
free_dma(crc->dma_ch);
-out_error_irq:
- free_irq(crc->irq, crc);
-out_error_unmap:
- iounmap((void *)crc->regs);
-out_error_free_mem:
- kfree(crc);
return ret;
}
@@ -721,10 +702,6 @@ static int bfin_crypto_crc_remove(struct platform_device *pdev)
crypto_unregister_ahash(&algs);
tasklet_kill(&crc->done_task);
free_dma(crc->dma_ch);
- if (crc->irq > 0)
- free_irq(crc->irq, crc);
- iounmap((void *)crc->regs);
- kfree(crc);
return 0;
}
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index b71f2fd749df..5f891254db73 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -66,10 +66,14 @@
/* length of descriptors text */
#define DESC_AEAD_BASE (4 * CAAM_CMD_SZ)
-#define DESC_AEAD_ENC_LEN (DESC_AEAD_BASE + 16 * CAAM_CMD_SZ)
-#define DESC_AEAD_DEC_LEN (DESC_AEAD_BASE + 21 * CAAM_CMD_SZ)
+#define DESC_AEAD_ENC_LEN (DESC_AEAD_BASE + 15 * CAAM_CMD_SZ)
+#define DESC_AEAD_DEC_LEN (DESC_AEAD_BASE + 18 * CAAM_CMD_SZ)
#define DESC_AEAD_GIVENC_LEN (DESC_AEAD_ENC_LEN + 7 * CAAM_CMD_SZ)
+#define DESC_AEAD_NULL_BASE (3 * CAAM_CMD_SZ)
+#define DESC_AEAD_NULL_ENC_LEN (DESC_AEAD_NULL_BASE + 14 * CAAM_CMD_SZ)
+#define DESC_AEAD_NULL_DEC_LEN (DESC_AEAD_NULL_BASE + 17 * CAAM_CMD_SZ)
+
#define DESC_ABLKCIPHER_BASE (3 * CAAM_CMD_SZ)
#define DESC_ABLKCIPHER_ENC_LEN (DESC_ABLKCIPHER_BASE + \
20 * CAAM_CMD_SZ)
@@ -104,27 +108,14 @@ static inline void append_dec_op1(u32 *desc, u32 type)
}
/*
- * Wait for completion of class 1 key loading before allowing
- * error propagation
- */
-static inline void append_dec_shr_done(u32 *desc)
-{
- u32 *jump_cmd;
-
- jump_cmd = append_jump(desc, JUMP_CLASS_CLASS1 | JUMP_TEST_ALL);
- set_jump_tgt_here(desc, jump_cmd);
- append_cmd(desc, SET_OK_NO_PROP_ERRORS | CMD_LOAD);
-}
-
-/*
* For aead functions, read payload and write payload,
* both of which are specified in req->src and req->dst
*/
static inline void aead_append_src_dst(u32 *desc, u32 msg_type)
{
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF);
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH |
KEY_VLF | msg_type | FIFOLD_TYPE_LASTBOTH);
- append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF);
}
/*
@@ -211,9 +202,196 @@ static void init_sh_desc_key_aead(u32 *desc, struct caam_ctx *ctx,
append_key_aead(desc, ctx, keys_fit_inline);
set_jump_tgt_here(desc, key_jump_cmd);
+}
+
+static int aead_null_set_sh_desc(struct crypto_aead *aead)
+{
+ struct aead_tfm *tfm = &aead->base.crt_aead;
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ struct device *jrdev = ctx->jrdev;
+ bool keys_fit_inline = false;
+ u32 *key_jump_cmd, *jump_cmd, *read_move_cmd, *write_move_cmd;
+ u32 *desc;
+
+ /*
+ * Job Descriptor and Shared Descriptors
+ * must all fit into the 64-word Descriptor h/w Buffer
+ */
+ if (DESC_AEAD_NULL_ENC_LEN + DESC_JOB_IO_LEN +
+ ctx->split_key_pad_len <= CAAM_DESC_BYTES_MAX)
+ keys_fit_inline = true;
+
+ /* aead_encrypt shared descriptor */
+ desc = ctx->sh_desc_enc;
+
+ init_sh_desc(desc, HDR_SHARE_SERIAL);
+
+ /* Skip if already shared */
+ key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_SHRD);
+ if (keys_fit_inline)
+ append_key_as_imm(desc, ctx->key, ctx->split_key_pad_len,
+ ctx->split_key_len, CLASS_2 |
+ KEY_DEST_MDHA_SPLIT | KEY_ENC);
+ else
+ append_key(desc, ctx->key_dma, ctx->split_key_len, CLASS_2 |
+ KEY_DEST_MDHA_SPLIT | KEY_ENC);
+ set_jump_tgt_here(desc, key_jump_cmd);
+
+ /* cryptlen = seqoutlen - authsize */
+ append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
+
+ /*
+ * NULL encryption; IV is zero
+ * assoclen = (assoclen + cryptlen) - cryptlen
+ */
+ append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG3, CAAM_CMD_SZ);
+
+ /* read assoc before reading payload */
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
+ KEY_VLF);
+
+ /* Prepare to read and write cryptlen bytes */
+ append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+ append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
+
+ /*
+ * MOVE_LEN opcode is not available in all SEC HW revisions,
+ * thus need to do some magic, i.e. self-patch the descriptor
+ * buffer.
+ */
+ read_move_cmd = append_move(desc, MOVE_SRC_DESCBUF |
+ MOVE_DEST_MATH3 |
+ (0x6 << MOVE_LEN_SHIFT));
+ write_move_cmd = append_move(desc, MOVE_SRC_MATH3 |
+ MOVE_DEST_DESCBUF |
+ MOVE_WAITCOMP |
+ (0x8 << MOVE_LEN_SHIFT));
+
+ /* Class 2 operation */
+ append_operation(desc, ctx->class2_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
+
+ /* Read and write cryptlen bytes */
+ aead_append_src_dst(desc, FIFOLD_TYPE_MSG | FIFOLD_TYPE_FLUSH1);
+
+ set_move_tgt_here(desc, read_move_cmd);
+ set_move_tgt_here(desc, write_move_cmd);
+ append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
+ append_move(desc, MOVE_SRC_INFIFO_CL | MOVE_DEST_OUTFIFO |
+ MOVE_AUX_LS);
+
+ /* Write ICV */
+ append_seq_store(desc, ctx->authsize, LDST_CLASS_2_CCB |
+ LDST_SRCDST_BYTE_CONTEXT);
+
+ ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR,
+ "aead null enc shdesc@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+
+ /*
+ * Job Descriptor and Shared Descriptors
+ * must all fit into the 64-word Descriptor h/w Buffer
+ */
+ if (DESC_AEAD_NULL_DEC_LEN + DESC_JOB_IO_LEN +
+ ctx->split_key_pad_len <= CAAM_DESC_BYTES_MAX)
+ keys_fit_inline = true;
+
+ desc = ctx->sh_desc_dec;
+
+ /* aead_decrypt shared descriptor */
+ init_sh_desc(desc, HDR_SHARE_SERIAL);
+
+ /* Skip if already shared */
+ key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_SHRD);
+ if (keys_fit_inline)
+ append_key_as_imm(desc, ctx->key, ctx->split_key_pad_len,
+ ctx->split_key_len, CLASS_2 |
+ KEY_DEST_MDHA_SPLIT | KEY_ENC);
+ else
+ append_key(desc, ctx->key_dma, ctx->split_key_len, CLASS_2 |
+ KEY_DEST_MDHA_SPLIT | KEY_ENC);
+ set_jump_tgt_here(desc, key_jump_cmd);
+
+ /* Class 2 operation */
+ append_operation(desc, ctx->class2_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT | OP_ALG_ICV_ON);
+
+ /* assoclen + cryptlen = seqinlen - ivsize - authsize */
+ append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
+ ctx->authsize + tfm->ivsize);
+ /* assoclen = (assoclen + cryptlen) - cryptlen */
+ append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
+ append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
+
+ /* read assoc before reading payload */
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
+ KEY_VLF);
+
+ /* Prepare to read and write cryptlen bytes */
+ append_math_add(desc, VARSEQINLEN, ZERO, REG2, CAAM_CMD_SZ);
+ append_math_add(desc, VARSEQOUTLEN, ZERO, REG2, CAAM_CMD_SZ);
+
+ /*
+ * MOVE_LEN opcode is not available in all SEC HW revisions,
+ * thus need to do some magic, i.e. self-patch the descriptor
+ * buffer.
+ */
+ read_move_cmd = append_move(desc, MOVE_SRC_DESCBUF |
+ MOVE_DEST_MATH2 |
+ (0x6 << MOVE_LEN_SHIFT));
+ write_move_cmd = append_move(desc, MOVE_SRC_MATH2 |
+ MOVE_DEST_DESCBUF |
+ MOVE_WAITCOMP |
+ (0x8 << MOVE_LEN_SHIFT));
+
+ /* Read and write cryptlen bytes */
+ aead_append_src_dst(desc, FIFOLD_TYPE_MSG | FIFOLD_TYPE_FLUSH1);
+
+ /*
+ * Insert a NOP here, since we need at least 4 instructions between
+ * code patching the descriptor buffer and the location being patched.
+ */
+ jump_cmd = append_jump(desc, JUMP_TEST_ALL);
+ set_jump_tgt_here(desc, jump_cmd);
- /* Propagate errors from shared to job descriptor */
- append_cmd(desc, SET_OK_NO_PROP_ERRORS | CMD_LOAD);
+ set_move_tgt_here(desc, read_move_cmd);
+ set_move_tgt_here(desc, write_move_cmd);
+ append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
+ append_move(desc, MOVE_SRC_INFIFO_CL | MOVE_DEST_OUTFIFO |
+ MOVE_AUX_LS);
+ append_cmd(desc, CMD_LOAD | ENABLE_AUTO_INFO_FIFO);
+
+ /* Load ICV */
+ append_seq_fifo_load(desc, ctx->authsize, FIFOLD_CLASS_CLASS2 |
+ FIFOLD_TYPE_LAST2 | FIFOLD_TYPE_ICV);
+
+ ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR,
+ "aead null dec shdesc@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+
+ return 0;
}
static int aead_set_sh_desc(struct crypto_aead *aead)
@@ -222,13 +400,16 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
bool keys_fit_inline = false;
- u32 *key_jump_cmd, *jump_cmd;
u32 geniv, moveiv;
u32 *desc;
- if (!ctx->enckeylen || !ctx->authsize)
+ if (!ctx->authsize)
return 0;
+ /* NULL encryption / decryption */
+ if (!ctx->enckeylen)
+ return aead_null_set_sh_desc(aead);
+
/*
* Job Descriptor and Shared Descriptors
* must all fit into the 64-word Descriptor h/w Buffer
@@ -253,7 +434,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
/* assoclen + cryptlen = seqinlen - ivsize */
append_math_sub_imm_u32(desc, REG2, SEQINLEN, IMM, tfm->ivsize);
- /* assoclen + cryptlen = (assoclen + cryptlen) - cryptlen */
+ /* assoclen = (assoclen + cryptlen) - cryptlen */
append_math_sub(desc, VARSEQINLEN, REG2, REG3, CAAM_CMD_SZ);
/* read assoc before reading payload */
@@ -296,30 +477,18 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
CAAM_DESC_BYTES_MAX)
keys_fit_inline = true;
- desc = ctx->sh_desc_dec;
-
/* aead_decrypt shared descriptor */
- init_sh_desc(desc, HDR_SHARE_SERIAL);
-
- /* Skip if already shared */
- key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
- JUMP_COND_SHRD);
-
- append_key_aead(desc, ctx, keys_fit_inline);
+ desc = ctx->sh_desc_dec;
- /* Only propagate error immediately if shared */
- jump_cmd = append_jump(desc, JUMP_TEST_ALL);
- set_jump_tgt_here(desc, key_jump_cmd);
- append_cmd(desc, SET_OK_NO_PROP_ERRORS | CMD_LOAD);
- set_jump_tgt_here(desc, jump_cmd);
+ init_sh_desc_key_aead(desc, ctx, keys_fit_inline);
/* Class 2 operation */
append_operation(desc, ctx->class2_alg_type |
OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT | OP_ALG_ICV_ON);
- /* assoclen + cryptlen = seqinlen - ivsize */
+ /* assoclen + cryptlen = seqinlen - ivsize - authsize */
append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
- ctx->authsize + tfm->ivsize)
+ ctx->authsize + tfm->ivsize);
/* assoclen = (assoclen + cryptlen) - cryptlen */
append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
@@ -340,7 +509,6 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
/* Load ICV */
append_seq_fifo_load(desc, ctx->authsize, FIFOLD_CLASS_CLASS2 |
FIFOLD_TYPE_LAST2 | FIFOLD_TYPE_ICV);
- append_dec_shr_done(desc);
ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc,
desc_bytes(desc),
@@ -532,7 +700,7 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher,
struct ablkcipher_tfm *tfm = &ablkcipher->base.crt_ablkcipher;
struct device *jrdev = ctx->jrdev;
int ret = 0;
- u32 *key_jump_cmd, *jump_cmd;
+ u32 *key_jump_cmd;
u32 *desc;
#ifdef DEBUG
@@ -563,9 +731,6 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher,
set_jump_tgt_here(desc, key_jump_cmd);
- /* Propagate errors from shared to job descriptor */
- append_cmd(desc, SET_OK_NO_PROP_ERRORS | CMD_LOAD);
-
/* Load iv */
append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT |
LDST_CLASS_1_CCB | tfm->ivsize);
@@ -603,11 +768,7 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher,
ctx->enckeylen, CLASS_1 |
KEY_DEST_CLASS_REG);
- /* For aead, only propagate error immediately if shared */
- jump_cmd = append_jump(desc, JUMP_TEST_ALL);
set_jump_tgt_here(desc, key_jump_cmd);
- append_cmd(desc, SET_OK_NO_PROP_ERRORS | CMD_LOAD);
- set_jump_tgt_here(desc, jump_cmd);
/* load IV */
append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT |
@@ -619,9 +780,6 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher,
/* Perform operation */
ablkcipher_append_src_dst(desc);
- /* Wait for key to load before allowing propagating error */
- append_dec_shr_done(desc);
-
ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc,
desc_bytes(desc),
DMA_TO_DEVICE);
@@ -1459,6 +1617,11 @@ static int aead_givencrypt(struct aead_givcrypt_request *areq)
return ret;
}
+static int aead_null_givencrypt(struct aead_givcrypt_request *areq)
+{
+ return aead_encrypt(&areq->areq);
+}
+
/*
* allocate and map the ablkcipher extended descriptor for ablkcipher
*/
@@ -1648,6 +1811,124 @@ struct caam_alg_template {
static struct caam_alg_template driver_algs[] = {
/* single-pass ipsec_esp descriptor */
{
+ .name = "authenc(hmac(md5),ecb(cipher_null))",
+ .driver_name = "authenc-hmac-md5-ecb-cipher_null-caam",
+ .blocksize = NULL_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_null_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = NULL_IV_SIZE,
+ .maxauthsize = MD5_DIGEST_SIZE,
+ },
+ .class1_alg_type = 0,
+ .class2_alg_type = OP_ALG_ALGSEL_MD5 | OP_ALG_AAI_HMAC_PRECOMP,
+ .alg_op = OP_ALG_ALGSEL_MD5 | OP_ALG_AAI_HMAC,
+ },
+ {
+ .name = "authenc(hmac(sha1),ecb(cipher_null))",
+ .driver_name = "authenc-hmac-sha1-ecb-cipher_null-caam",
+ .blocksize = NULL_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_null_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = NULL_IV_SIZE,
+ .maxauthsize = SHA1_DIGEST_SIZE,
+ },
+ .class1_alg_type = 0,
+ .class2_alg_type = OP_ALG_ALGSEL_SHA1 | OP_ALG_AAI_HMAC_PRECOMP,
+ .alg_op = OP_ALG_ALGSEL_SHA1 | OP_ALG_AAI_HMAC,
+ },
+ {
+ .name = "authenc(hmac(sha224),ecb(cipher_null))",
+ .driver_name = "authenc-hmac-sha224-ecb-cipher_null-caam",
+ .blocksize = NULL_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_null_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = NULL_IV_SIZE,
+ .maxauthsize = SHA224_DIGEST_SIZE,
+ },
+ .class1_alg_type = 0,
+ .class2_alg_type = OP_ALG_ALGSEL_SHA224 |
+ OP_ALG_AAI_HMAC_PRECOMP,
+ .alg_op = OP_ALG_ALGSEL_SHA224 | OP_ALG_AAI_HMAC,
+ },
+ {
+ .name = "authenc(hmac(sha256),ecb(cipher_null))",
+ .driver_name = "authenc-hmac-sha256-ecb-cipher_null-caam",
+ .blocksize = NULL_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_null_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = NULL_IV_SIZE,
+ .maxauthsize = SHA256_DIGEST_SIZE,
+ },
+ .class1_alg_type = 0,
+ .class2_alg_type = OP_ALG_ALGSEL_SHA256 |
+ OP_ALG_AAI_HMAC_PRECOMP,
+ .alg_op = OP_ALG_ALGSEL_SHA256 | OP_ALG_AAI_HMAC,
+ },
+ {
+ .name = "authenc(hmac(sha384),ecb(cipher_null))",
+ .driver_name = "authenc-hmac-sha384-ecb-cipher_null-caam",
+ .blocksize = NULL_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_null_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = NULL_IV_SIZE,
+ .maxauthsize = SHA384_DIGEST_SIZE,
+ },
+ .class1_alg_type = 0,
+ .class2_alg_type = OP_ALG_ALGSEL_SHA384 |
+ OP_ALG_AAI_HMAC_PRECOMP,
+ .alg_op = OP_ALG_ALGSEL_SHA384 | OP_ALG_AAI_HMAC,
+ },
+ {
+ .name = "authenc(hmac(sha512),ecb(cipher_null))",
+ .driver_name = "authenc-hmac-sha512-ecb-cipher_null-caam",
+ .blocksize = NULL_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_null_givencrypt,
+ .geniv = "<built-in>",
+ .ivsize = NULL_IV_SIZE,
+ .maxauthsize = SHA512_DIGEST_SIZE,
+ },
+ .class1_alg_type = 0,
+ .class2_alg_type = OP_ALG_ALGSEL_SHA512 |
+ OP_ALG_AAI_HMAC_PRECOMP,
+ .alg_op = OP_ALG_ALGSEL_SHA512 | OP_ALG_AAI_HMAC,
+ },
+ {
.name = "authenc(hmac(md5),cbc(aes))",
.driver_name = "authenc-hmac-md5-cbc-aes-caam",
.blocksize = AES_BLOCK_SIZE,
@@ -2099,6 +2380,11 @@ static void caam_cra_exit(struct crypto_tfm *tfm)
dma_unmap_single(ctx->jrdev, ctx->sh_desc_givenc_dma,
desc_bytes(ctx->sh_desc_givenc),
DMA_TO_DEVICE);
+ if (ctx->key_dma &&
+ !dma_mapping_error(ctx->jrdev, ctx->key_dma))
+ dma_unmap_single(ctx->jrdev, ctx->key_dma,
+ ctx->enckeylen + ctx->split_key_pad_len,
+ DMA_TO_DEVICE);
caam_jr_free(ctx->jrdev);
}
diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c
index 28486b19fc36..3529b54048c9 100644
--- a/drivers/crypto/caam/caamrng.c
+++ b/drivers/crypto/caam/caamrng.c
@@ -76,7 +76,7 @@ struct caam_rng_ctx {
struct buf_data bufs[2];
};
-static struct caam_rng_ctx rng_ctx;
+static struct caam_rng_ctx *rng_ctx;
static inline void rng_unmap_buf(struct device *jrdev, struct buf_data *bd)
{
@@ -137,7 +137,7 @@ static inline int submit_job(struct caam_rng_ctx *ctx, int to_current)
static int caam_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
- struct caam_rng_ctx *ctx = &rng_ctx;
+ struct caam_rng_ctx *ctx = rng_ctx;
struct buf_data *bd = &ctx->bufs[ctx->current_buf];
int next_buf_idx, copied_idx;
int err;
@@ -237,12 +237,12 @@ static void caam_cleanup(struct hwrng *rng)
struct buf_data *bd;
for (i = 0; i < 2; i++) {
- bd = &rng_ctx.bufs[i];
+ bd = &rng_ctx->bufs[i];
if (atomic_read(&bd->empty) == BUF_PENDING)
wait_for_completion(&bd->filled);
}
- rng_unmap_ctx(&rng_ctx);
+ rng_unmap_ctx(rng_ctx);
}
static void caam_init_buf(struct caam_rng_ctx *ctx, int buf_id)
@@ -273,8 +273,9 @@ static struct hwrng caam_rng = {
static void __exit caam_rng_exit(void)
{
- caam_jr_free(rng_ctx.jrdev);
+ caam_jr_free(rng_ctx->jrdev);
hwrng_unregister(&caam_rng);
+ kfree(rng_ctx);
}
static int __init caam_rng_init(void)
@@ -286,8 +287,10 @@ static int __init caam_rng_init(void)
pr_err("Job Ring Device allocation for transform failed\n");
return PTR_ERR(dev);
}
-
- caam_init_rng(&rng_ctx, dev);
+ rng_ctx = kmalloc(sizeof(struct caam_rng_ctx), GFP_DMA);
+ if (!rng_ctx)
+ return -ENOMEM;
+ caam_init_rng(rng_ctx, dev);
dev_info(dev, "registering rng-caam\n");
return hwrng_register(&caam_rng);
diff --git a/drivers/crypto/caam/compat.h b/drivers/crypto/caam/compat.h
index 762aeff626ac..f227922cea38 100644
--- a/drivers/crypto/caam/compat.h
+++ b/drivers/crypto/caam/compat.h
@@ -26,6 +26,7 @@
#include <net/xfrm.h>
#include <crypto/algapi.h>
+#include <crypto/null.h>
#include <crypto/aes.h>
#include <crypto/des.h>
#include <crypto/sha.h>
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index 63fb1af2c431..1c38f86bf63a 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -14,7 +14,6 @@
#include "jr.h"
#include "desc_constr.h"
#include "error.h"
-#include "ctrl.h"
/*
* Descriptor to instantiate RNG State Handle 0 in normal mode and
@@ -352,32 +351,17 @@ static void kick_trng(struct platform_device *pdev, int ent_delay)
/**
* caam_get_era() - Return the ERA of the SEC on SoC, based
- * on the SEC_VID register.
- * Returns the ERA number (1..4) or -ENOTSUPP if the ERA is unknown.
- * @caam_id - the value of the SEC_VID register
+ * on "sec-era" propery in the DTS. This property is updated by u-boot.
**/
-int caam_get_era(u64 caam_id)
+int caam_get_era(void)
{
- struct sec_vid *sec_vid = (struct sec_vid *)&caam_id;
- static const struct {
- u16 ip_id;
- u8 maj_rev;
- u8 era;
- } caam_eras[] = {
- {0x0A10, 1, 1},
- {0x0A10, 2, 2},
- {0x0A12, 1, 3},
- {0x0A14, 1, 3},
- {0x0A14, 2, 4},
- {0x0A16, 1, 4},
- {0x0A11, 1, 4}
- };
- int i;
-
- for (i = 0; i < ARRAY_SIZE(caam_eras); i++)
- if (caam_eras[i].ip_id == sec_vid->ip_id &&
- caam_eras[i].maj_rev == sec_vid->maj_rev)
- return caam_eras[i].era;
+ struct device_node *caam_node;
+ for_each_compatible_node(caam_node, NULL, "fsl,sec-v4.0") {
+ const uint32_t *prop = (uint32_t *)of_get_property(caam_node,
+ "fsl,sec-era",
+ NULL);
+ return prop ? *prop : -ENOTSUPP;
+ }
return -ENOTSUPP;
}
@@ -443,13 +427,10 @@ static int caam_probe(struct platform_device *pdev)
* for all, then go probe each one.
*/
rspec = 0;
- for_each_compatible_node(np, NULL, "fsl,sec-v4.0-job-ring")
- rspec++;
- if (!rspec) {
- /* for backward compatible with device trees */
- for_each_compatible_node(np, NULL, "fsl,sec4.0-job-ring")
+ for_each_available_child_of_node(nprop, np)
+ if (of_device_is_compatible(np, "fsl,sec-v4.0-job-ring") ||
+ of_device_is_compatible(np, "fsl,sec4.0-job-ring"))
rspec++;
- }
ctrlpriv->jrpdev = kzalloc(sizeof(struct platform_device *) * rspec,
GFP_KERNEL);
@@ -460,18 +441,9 @@ static int caam_probe(struct platform_device *pdev)
ring = 0;
ctrlpriv->total_jobrs = 0;
- for_each_compatible_node(np, NULL, "fsl,sec-v4.0-job-ring") {
- ctrlpriv->jrpdev[ring] =
- of_platform_device_create(np, NULL, dev);
- if (!ctrlpriv->jrpdev[ring]) {
- pr_warn("JR%d Platform device creation error\n", ring);
- continue;
- }
- ctrlpriv->total_jobrs++;
- ring++;
- }
- if (!ring) {
- for_each_compatible_node(np, NULL, "fsl,sec4.0-job-ring") {
+ for_each_available_child_of_node(nprop, np)
+ if (of_device_is_compatible(np, "fsl,sec-v4.0-job-ring") ||
+ of_device_is_compatible(np, "fsl,sec4.0-job-ring")) {
ctrlpriv->jrpdev[ring] =
of_platform_device_create(np, NULL, dev);
if (!ctrlpriv->jrpdev[ring]) {
@@ -482,7 +454,6 @@ static int caam_probe(struct platform_device *pdev)
ctrlpriv->total_jobrs++;
ring++;
}
- }
/* Check to see if QI present. If so, enable */
ctrlpriv->qi_present = !!(rd_reg64(&topregs->ctrl.perfmon.comp_parms) &
@@ -564,7 +535,7 @@ static int caam_probe(struct platform_device *pdev)
/* Report "alive" for developer to see */
dev_info(dev, "device ID = 0x%016llx (Era %d)\n", caam_id,
- caam_get_era(caam_id));
+ caam_get_era());
dev_info(dev, "job rings = %d, qi = %d\n",
ctrlpriv->total_jobrs, ctrlpriv->qi_present);
diff --git a/drivers/crypto/caam/ctrl.h b/drivers/crypto/caam/ctrl.h
index 980d44eaaf40..cac5402a46eb 100644
--- a/drivers/crypto/caam/ctrl.h
+++ b/drivers/crypto/caam/ctrl.h
@@ -8,6 +8,6 @@
#define CTRL_H
/* Prototypes for backend-level services exposed to APIs */
-int caam_get_era(u64 caam_id);
+int caam_get_era(void);
#endif /* CTRL_H */
diff --git a/drivers/crypto/caam/desc_constr.h b/drivers/crypto/caam/desc_constr.h
index cd5f678847ce..7eec20bb3849 100644
--- a/drivers/crypto/caam/desc_constr.h
+++ b/drivers/crypto/caam/desc_constr.h
@@ -155,21 +155,29 @@ static inline void append_cmd_data(u32 *desc, void *data, int len,
append_data(desc, data, len);
}
-static inline u32 *append_jump(u32 *desc, u32 options)
-{
- u32 *cmd = desc_end(desc);
-
- PRINT_POS;
- append_cmd(desc, CMD_JUMP | options);
-
- return cmd;
+#define APPEND_CMD_RET(cmd, op) \
+static inline u32 *append_##cmd(u32 *desc, u32 options) \
+{ \
+ u32 *cmd = desc_end(desc); \
+ PRINT_POS; \
+ append_cmd(desc, CMD_##op | options); \
+ return cmd; \
}
+APPEND_CMD_RET(jump, JUMP)
+APPEND_CMD_RET(move, MOVE)
static inline void set_jump_tgt_here(u32 *desc, u32 *jump_cmd)
{
*jump_cmd = *jump_cmd | (desc_len(desc) - (jump_cmd - desc));
}
+static inline void set_move_tgt_here(u32 *desc, u32 *move_cmd)
+{
+ *move_cmd &= ~MOVE_OFFSET_MASK;
+ *move_cmd = *move_cmd | ((desc_len(desc) << (MOVE_OFFSET_SHIFT + 2)) &
+ MOVE_OFFSET_MASK);
+}
+
#define APPEND_CMD(cmd, op) \
static inline void append_##cmd(u32 *desc, u32 options) \
{ \
@@ -177,7 +185,6 @@ static inline void append_##cmd(u32 *desc, u32 options) \
append_cmd(desc, CMD_##op | options); \
}
APPEND_CMD(operation, OPERATION)
-APPEND_CMD(move, MOVE)
#define APPEND_CMD_LEN(cmd, op) \
static inline void append_##cmd(u32 *desc, unsigned int len, u32 options) \
@@ -328,7 +335,7 @@ append_cmd(desc, CMD_MATH | MATH_FUN_##op | MATH_DEST_##dest | \
do { \
APPEND_MATH(op, desc, dest, src_0, src_1, CAAM_CMD_SZ); \
append_cmd(desc, data); \
-} while (0);
+} while (0)
#define append_math_add_imm_u32(desc, dest, src0, src1, data) \
APPEND_MATH_IMM_u32(ADD, desc, dest, src0, src1, data)
diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h
index d50174f45b21..cbde8b95a6f8 100644
--- a/drivers/crypto/caam/regs.h
+++ b/drivers/crypto/caam/regs.h
@@ -74,10 +74,10 @@
#endif
#else
#ifdef __LITTLE_ENDIAN
-#define wr_reg32(reg, data) __raw_writel(reg, data)
+#define wr_reg32(reg, data) __raw_writel(data, reg)
#define rd_reg32(reg) __raw_readl(reg)
#ifdef CONFIG_64BIT
-#define wr_reg64(reg, data) __raw_writeq(reg, data)
+#define wr_reg64(reg, data) __raw_writeq(data, reg)
#define rd_reg64(reg) __raw_readq(reg)
#endif
#endif
diff --git a/drivers/crypto/ccp/ccp-crypto-main.c b/drivers/crypto/ccp/ccp-crypto-main.c
index 2636f044789d..20dc848481e7 100644
--- a/drivers/crypto/ccp/ccp-crypto-main.c
+++ b/drivers/crypto/ccp/ccp-crypto-main.c
@@ -11,6 +11,7 @@
*/
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/ccp.h>
@@ -24,28 +25,33 @@ MODULE_LICENSE("GPL");
MODULE_VERSION("1.0.0");
MODULE_DESCRIPTION("AMD Cryptographic Coprocessor crypto API support");
+static unsigned int aes_disable;
+module_param(aes_disable, uint, 0444);
+MODULE_PARM_DESC(aes_disable, "Disable use of AES - any non-zero value");
+
+static unsigned int sha_disable;
+module_param(sha_disable, uint, 0444);
+MODULE_PARM_DESC(sha_disable, "Disable use of SHA - any non-zero value");
+
/* List heads for the supported algorithms */
static LIST_HEAD(hash_algs);
static LIST_HEAD(cipher_algs);
-/* For any tfm, requests for that tfm on the same CPU must be returned
- * in the order received. With multiple queues available, the CCP can
- * process more than one cmd at a time. Therefore we must maintain
- * a cmd list to insure the proper ordering of requests on a given tfm/cpu
- * combination.
+/* For any tfm, requests for that tfm must be returned on the order
+ * received. With multiple queues available, the CCP can process more
+ * than one cmd at a time. Therefore we must maintain a cmd list to insure
+ * the proper ordering of requests on a given tfm.
*/
-struct ccp_crypto_cpu_queue {
+struct ccp_crypto_queue {
struct list_head cmds;
struct list_head *backlog;
unsigned int cmd_count;
};
-#define CCP_CRYPTO_MAX_QLEN 50
+#define CCP_CRYPTO_MAX_QLEN 100
-struct ccp_crypto_percpu_queue {
- struct ccp_crypto_cpu_queue __percpu *cpu_queue;
-};
-static struct ccp_crypto_percpu_queue req_queue;
+static struct ccp_crypto_queue req_queue;
+static spinlock_t req_queue_lock;
struct ccp_crypto_cmd {
struct list_head entry;
@@ -62,8 +68,6 @@ struct ccp_crypto_cmd {
/* Used for held command processing to determine state */
int ret;
-
- int cpu;
};
struct ccp_crypto_cpu {
@@ -82,25 +86,21 @@ static inline bool ccp_crypto_success(int err)
return true;
}
-/*
- * ccp_crypto_cmd_complete must be called while running on the appropriate
- * cpu and the caller must have done a get_cpu to disable preemption
- */
static struct ccp_crypto_cmd *ccp_crypto_cmd_complete(
struct ccp_crypto_cmd *crypto_cmd, struct ccp_crypto_cmd **backlog)
{
- struct ccp_crypto_cpu_queue *cpu_queue;
struct ccp_crypto_cmd *held = NULL, *tmp;
+ unsigned long flags;
*backlog = NULL;
- cpu_queue = this_cpu_ptr(req_queue.cpu_queue);
+ spin_lock_irqsave(&req_queue_lock, flags);
/* Held cmds will be after the current cmd in the queue so start
* searching for a cmd with a matching tfm for submission.
*/
tmp = crypto_cmd;
- list_for_each_entry_continue(tmp, &cpu_queue->cmds, entry) {
+ list_for_each_entry_continue(tmp, &req_queue.cmds, entry) {
if (crypto_cmd->tfm != tmp->tfm)
continue;
held = tmp;
@@ -111,47 +111,45 @@ static struct ccp_crypto_cmd *ccp_crypto_cmd_complete(
* Because cmds can be executed from any point in the cmd list
* special precautions have to be taken when handling the backlog.
*/
- if (cpu_queue->backlog != &cpu_queue->cmds) {
+ if (req_queue.backlog != &req_queue.cmds) {
/* Skip over this cmd if it is the next backlog cmd */
- if (cpu_queue->backlog == &crypto_cmd->entry)
- cpu_queue->backlog = crypto_cmd->entry.next;
+ if (req_queue.backlog == &crypto_cmd->entry)
+ req_queue.backlog = crypto_cmd->entry.next;
- *backlog = container_of(cpu_queue->backlog,
+ *backlog = container_of(req_queue.backlog,
struct ccp_crypto_cmd, entry);
- cpu_queue->backlog = cpu_queue->backlog->next;
+ req_queue.backlog = req_queue.backlog->next;
/* Skip over this cmd if it is now the next backlog cmd */
- if (cpu_queue->backlog == &crypto_cmd->entry)
- cpu_queue->backlog = crypto_cmd->entry.next;
+ if (req_queue.backlog == &crypto_cmd->entry)
+ req_queue.backlog = crypto_cmd->entry.next;
}
/* Remove the cmd entry from the list of cmds */
- cpu_queue->cmd_count--;
+ req_queue.cmd_count--;
list_del(&crypto_cmd->entry);
+ spin_unlock_irqrestore(&req_queue_lock, flags);
+
return held;
}
-static void ccp_crypto_complete_on_cpu(struct work_struct *work)
+static void ccp_crypto_complete(void *data, int err)
{
- struct ccp_crypto_cpu *cpu_work =
- container_of(work, struct ccp_crypto_cpu, work);
- struct ccp_crypto_cmd *crypto_cmd = cpu_work->crypto_cmd;
+ struct ccp_crypto_cmd *crypto_cmd = data;
struct ccp_crypto_cmd *held, *next, *backlog;
struct crypto_async_request *req = crypto_cmd->req;
struct ccp_ctx *ctx = crypto_tfm_ctx(req->tfm);
- int cpu, ret;
-
- cpu = get_cpu();
+ int ret;
- if (cpu_work->err == -EINPROGRESS) {
+ if (err == -EINPROGRESS) {
/* Only propogate the -EINPROGRESS if necessary */
if (crypto_cmd->ret == -EBUSY) {
crypto_cmd->ret = -EINPROGRESS;
req->complete(req, -EINPROGRESS);
}
- goto e_cpu;
+ return;
}
/* Operation has completed - update the queue before invoking
@@ -169,18 +167,25 @@ static void ccp_crypto_complete_on_cpu(struct work_struct *work)
req->complete(req, -EINPROGRESS);
/* Completion callbacks */
- ret = cpu_work->err;
+ ret = err;
if (ctx->complete)
ret = ctx->complete(req, ret);
req->complete(req, ret);
/* Submit the next cmd */
while (held) {
+ /* Since we have already queued the cmd, we must indicate that
+ * we can backlog so as not to "lose" this request.
+ */
+ held->cmd->flags |= CCP_CMD_MAY_BACKLOG;
ret = ccp_enqueue_cmd(held->cmd);
if (ccp_crypto_success(ret))
break;
/* Error occurred, report it and get the next entry */
+ ctx = crypto_tfm_ctx(held->req->tfm);
+ if (ctx->complete)
+ ret = ctx->complete(held->req, ret);
held->req->complete(held->req, ret);
next = ccp_crypto_cmd_complete(held, &backlog);
@@ -194,52 +199,29 @@ static void ccp_crypto_complete_on_cpu(struct work_struct *work)
}
kfree(crypto_cmd);
-
-e_cpu:
- put_cpu();
-
- complete(&cpu_work->completion);
-}
-
-static void ccp_crypto_complete(void *data, int err)
-{
- struct ccp_crypto_cmd *crypto_cmd = data;
- struct ccp_crypto_cpu cpu_work;
-
- INIT_WORK(&cpu_work.work, ccp_crypto_complete_on_cpu);
- init_completion(&cpu_work.completion);
- cpu_work.crypto_cmd = crypto_cmd;
- cpu_work.err = err;
-
- schedule_work_on(crypto_cmd->cpu, &cpu_work.work);
-
- /* Keep the completion call synchronous */
- wait_for_completion(&cpu_work.completion);
}
static int ccp_crypto_enqueue_cmd(struct ccp_crypto_cmd *crypto_cmd)
{
- struct ccp_crypto_cpu_queue *cpu_queue;
struct ccp_crypto_cmd *active = NULL, *tmp;
- int cpu, ret;
-
- cpu = get_cpu();
- crypto_cmd->cpu = cpu;
+ unsigned long flags;
+ bool free_cmd = true;
+ int ret;
- cpu_queue = this_cpu_ptr(req_queue.cpu_queue);
+ spin_lock_irqsave(&req_queue_lock, flags);
/* Check if the cmd can/should be queued */
- if (cpu_queue->cmd_count >= CCP_CRYPTO_MAX_QLEN) {
+ if (req_queue.cmd_count >= CCP_CRYPTO_MAX_QLEN) {
ret = -EBUSY;
if (!(crypto_cmd->cmd->flags & CCP_CMD_MAY_BACKLOG))
- goto e_cpu;
+ goto e_lock;
}
/* Look for an entry with the same tfm. If there is a cmd
- * with the same tfm in the list for this cpu then the current
- * cmd cannot be submitted to the CCP yet.
+ * with the same tfm in the list then the current cmd cannot
+ * be submitted to the CCP yet.
*/
- list_for_each_entry(tmp, &cpu_queue->cmds, entry) {
+ list_for_each_entry(tmp, &req_queue.cmds, entry) {
if (crypto_cmd->tfm != tmp->tfm)
continue;
active = tmp;
@@ -250,21 +232,29 @@ static int ccp_crypto_enqueue_cmd(struct ccp_crypto_cmd *crypto_cmd)
if (!active) {
ret = ccp_enqueue_cmd(crypto_cmd->cmd);
if (!ccp_crypto_success(ret))
- goto e_cpu;
+ goto e_lock; /* Error, don't queue it */
+ if ((ret == -EBUSY) &&
+ !(crypto_cmd->cmd->flags & CCP_CMD_MAY_BACKLOG))
+ goto e_lock; /* Not backlogging, don't queue it */
}
- if (cpu_queue->cmd_count >= CCP_CRYPTO_MAX_QLEN) {
+ if (req_queue.cmd_count >= CCP_CRYPTO_MAX_QLEN) {
ret = -EBUSY;
- if (cpu_queue->backlog == &cpu_queue->cmds)
- cpu_queue->backlog = &crypto_cmd->entry;
+ if (req_queue.backlog == &req_queue.cmds)
+ req_queue.backlog = &crypto_cmd->entry;
}
crypto_cmd->ret = ret;
- cpu_queue->cmd_count++;
- list_add_tail(&crypto_cmd->entry, &cpu_queue->cmds);
+ req_queue.cmd_count++;
+ list_add_tail(&crypto_cmd->entry, &req_queue.cmds);
+
+ free_cmd = false;
-e_cpu:
- put_cpu();
+e_lock:
+ spin_unlock_irqrestore(&req_queue_lock, flags);
+
+ if (free_cmd)
+ kfree(crypto_cmd);
return ret;
}
@@ -281,7 +271,6 @@ int ccp_crypto_enqueue_request(struct crypto_async_request *req,
{
struct ccp_crypto_cmd *crypto_cmd;
gfp_t gfp;
- int ret;
gfp = req->flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL : GFP_ATOMIC;
@@ -306,11 +295,7 @@ int ccp_crypto_enqueue_request(struct crypto_async_request *req,
else
cmd->flags &= ~CCP_CMD_MAY_BACKLOG;
- ret = ccp_crypto_enqueue_cmd(crypto_cmd);
- if (!ccp_crypto_success(ret))
- kfree(crypto_cmd);
-
- return ret;
+ return ccp_crypto_enqueue_cmd(crypto_cmd);
}
struct scatterlist *ccp_crypto_sg_table_add(struct sg_table *table,
@@ -337,21 +322,25 @@ static int ccp_register_algs(void)
{
int ret;
- ret = ccp_register_aes_algs(&cipher_algs);
- if (ret)
- return ret;
+ if (!aes_disable) {
+ ret = ccp_register_aes_algs(&cipher_algs);
+ if (ret)
+ return ret;
- ret = ccp_register_aes_cmac_algs(&hash_algs);
- if (ret)
- return ret;
+ ret = ccp_register_aes_cmac_algs(&hash_algs);
+ if (ret)
+ return ret;
- ret = ccp_register_aes_xts_algs(&cipher_algs);
- if (ret)
- return ret;
+ ret = ccp_register_aes_xts_algs(&cipher_algs);
+ if (ret)
+ return ret;
+ }
- ret = ccp_register_sha_algs(&hash_algs);
- if (ret)
- return ret;
+ if (!sha_disable) {
+ ret = ccp_register_sha_algs(&hash_algs);
+ if (ret)
+ return ret;
+ }
return 0;
}
@@ -374,50 +363,18 @@ static void ccp_unregister_algs(void)
}
}
-static int ccp_init_queues(void)
-{
- struct ccp_crypto_cpu_queue *cpu_queue;
- int cpu;
-
- req_queue.cpu_queue = alloc_percpu(struct ccp_crypto_cpu_queue);
- if (!req_queue.cpu_queue)
- return -ENOMEM;
-
- for_each_possible_cpu(cpu) {
- cpu_queue = per_cpu_ptr(req_queue.cpu_queue, cpu);
- INIT_LIST_HEAD(&cpu_queue->cmds);
- cpu_queue->backlog = &cpu_queue->cmds;
- cpu_queue->cmd_count = 0;
- }
-
- return 0;
-}
-
-static void ccp_fini_queue(void)
-{
- struct ccp_crypto_cpu_queue *cpu_queue;
- int cpu;
-
- for_each_possible_cpu(cpu) {
- cpu_queue = per_cpu_ptr(req_queue.cpu_queue, cpu);
- BUG_ON(!list_empty(&cpu_queue->cmds));
- }
- free_percpu(req_queue.cpu_queue);
-}
-
static int ccp_crypto_init(void)
{
int ret;
- ret = ccp_init_queues();
- if (ret)
- return ret;
+ spin_lock_init(&req_queue_lock);
+ INIT_LIST_HEAD(&req_queue.cmds);
+ req_queue.backlog = &req_queue.cmds;
+ req_queue.cmd_count = 0;
ret = ccp_register_algs();
- if (ret) {
+ if (ret)
ccp_unregister_algs();
- ccp_fini_queue();
- }
return ret;
}
@@ -425,7 +382,6 @@ static int ccp_crypto_init(void)
static void ccp_crypto_exit(void)
{
ccp_unregister_algs();
- ccp_fini_queue();
}
module_init(ccp_crypto_init);
diff --git a/drivers/crypto/ccp/ccp-crypto-sha.c b/drivers/crypto/ccp/ccp-crypto-sha.c
index 3867290b3531..873f23425245 100644
--- a/drivers/crypto/ccp/ccp-crypto-sha.c
+++ b/drivers/crypto/ccp/ccp-crypto-sha.c
@@ -24,75 +24,10 @@
#include "ccp-crypto.h"
-struct ccp_sha_result {
- struct completion completion;
- int err;
-};
-
-static void ccp_sync_hash_complete(struct crypto_async_request *req, int err)
-{
- struct ccp_sha_result *result = req->data;
-
- if (err == -EINPROGRESS)
- return;
-
- result->err = err;
- complete(&result->completion);
-}
-
-static int ccp_sync_hash(struct crypto_ahash *tfm, u8 *buf,
- struct scatterlist *sg, unsigned int len)
-{
- struct ccp_sha_result result;
- struct ahash_request *req;
- int ret;
-
- init_completion(&result.completion);
-
- req = ahash_request_alloc(tfm, GFP_KERNEL);
- if (!req)
- return -ENOMEM;
-
- ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
- ccp_sync_hash_complete, &result);
- ahash_request_set_crypt(req, sg, buf, len);
-
- ret = crypto_ahash_digest(req);
- if ((ret == -EINPROGRESS) || (ret == -EBUSY)) {
- ret = wait_for_completion_interruptible(&result.completion);
- if (!ret)
- ret = result.err;
- }
-
- ahash_request_free(req);
-
- return ret;
-}
-
-static int ccp_sha_finish_hmac(struct crypto_async_request *async_req)
-{
- struct ahash_request *req = ahash_request_cast(async_req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct ccp_ctx *ctx = crypto_ahash_ctx(tfm);
- struct ccp_sha_req_ctx *rctx = ahash_request_ctx(req);
- struct scatterlist sg[2];
- unsigned int block_size =
- crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
- unsigned int digest_size = crypto_ahash_digestsize(tfm);
-
- sg_init_table(sg, ARRAY_SIZE(sg));
- sg_set_buf(&sg[0], ctx->u.sha.opad, block_size);
- sg_set_buf(&sg[1], rctx->ctx, digest_size);
-
- return ccp_sync_hash(ctx->u.sha.hmac_tfm, req->result, sg,
- block_size + digest_size);
-}
-
static int ccp_sha_complete(struct crypto_async_request *async_req, int ret)
{
struct ahash_request *req = ahash_request_cast(async_req);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct ccp_ctx *ctx = crypto_ahash_ctx(tfm);
struct ccp_sha_req_ctx *rctx = ahash_request_ctx(req);
unsigned int digest_size = crypto_ahash_digestsize(tfm);
@@ -112,10 +47,6 @@ static int ccp_sha_complete(struct crypto_async_request *async_req, int ret)
if (req->result)
memcpy(req->result, rctx->ctx, digest_size);
- /* If we're doing an HMAC, we need to perform that on the final op */
- if (rctx->final && ctx->u.sha.key_len)
- ret = ccp_sha_finish_hmac(async_req);
-
e_free:
sg_free_table(&rctx->data_sg);
@@ -126,6 +57,7 @@ static int ccp_do_sha_update(struct ahash_request *req, unsigned int nbytes,
unsigned int final)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct ccp_ctx *ctx = crypto_ahash_ctx(tfm);
struct ccp_sha_req_ctx *rctx = ahash_request_ctx(req);
struct scatterlist *sg;
unsigned int block_size =
@@ -196,6 +128,11 @@ static int ccp_do_sha_update(struct ahash_request *req, unsigned int nbytes,
rctx->cmd.u.sha.ctx_len = sizeof(rctx->ctx);
rctx->cmd.u.sha.src = sg;
rctx->cmd.u.sha.src_len = rctx->hash_cnt;
+ rctx->cmd.u.sha.opad = ctx->u.sha.key_len ?
+ &ctx->u.sha.opad_sg : NULL;
+ rctx->cmd.u.sha.opad_len = ctx->u.sha.key_len ?
+ ctx->u.sha.opad_count : 0;
+ rctx->cmd.u.sha.first = rctx->first;
rctx->cmd.u.sha.final = rctx->final;
rctx->cmd.u.sha.msg_bits = rctx->msg_bits;
@@ -218,7 +155,6 @@ static int ccp_sha_init(struct ahash_request *req)
memset(rctx, 0, sizeof(*rctx));
- memcpy(rctx->ctx, alg->init, sizeof(rctx->ctx));
rctx->type = alg->type;
rctx->first = 1;
@@ -261,10 +197,13 @@ static int ccp_sha_setkey(struct crypto_ahash *tfm, const u8 *key,
unsigned int key_len)
{
struct ccp_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
- struct scatterlist sg;
- unsigned int block_size =
- crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
- unsigned int digest_size = crypto_ahash_digestsize(tfm);
+ struct crypto_shash *shash = ctx->u.sha.hmac_tfm;
+ struct {
+ struct shash_desc sdesc;
+ char ctx[crypto_shash_descsize(shash)];
+ } desc;
+ unsigned int block_size = crypto_shash_blocksize(shash);
+ unsigned int digest_size = crypto_shash_digestsize(shash);
int i, ret;
/* Set to zero until complete */
@@ -277,8 +216,12 @@ static int ccp_sha_setkey(struct crypto_ahash *tfm, const u8 *key,
if (key_len > block_size) {
/* Must hash the input key */
- sg_init_one(&sg, key, key_len);
- ret = ccp_sync_hash(tfm, ctx->u.sha.key, &sg, key_len);
+ desc.sdesc.tfm = shash;
+ desc.sdesc.flags = crypto_ahash_get_flags(tfm) &
+ CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ ret = crypto_shash_digest(&desc.sdesc, key, key_len,
+ ctx->u.sha.key);
if (ret) {
crypto_ahash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
return -EINVAL;
@@ -293,6 +236,9 @@ static int ccp_sha_setkey(struct crypto_ahash *tfm, const u8 *key,
ctx->u.sha.opad[i] = ctx->u.sha.key[i] ^ 0x5c;
}
+ sg_init_one(&ctx->u.sha.opad_sg, ctx->u.sha.opad, block_size);
+ ctx->u.sha.opad_count = block_size;
+
ctx->u.sha.key_len = key_len;
return 0;
@@ -319,10 +265,9 @@ static int ccp_hmac_sha_cra_init(struct crypto_tfm *tfm)
{
struct ccp_ctx *ctx = crypto_tfm_ctx(tfm);
struct ccp_crypto_ahash_alg *alg = ccp_crypto_ahash_alg(tfm);
- struct crypto_ahash *hmac_tfm;
+ struct crypto_shash *hmac_tfm;
- hmac_tfm = crypto_alloc_ahash(alg->child_alg,
- CRYPTO_ALG_TYPE_AHASH, 0);
+ hmac_tfm = crypto_alloc_shash(alg->child_alg, 0, 0);
if (IS_ERR(hmac_tfm)) {
pr_warn("could not load driver %s need for HMAC support\n",
alg->child_alg);
@@ -339,35 +284,14 @@ static void ccp_hmac_sha_cra_exit(struct crypto_tfm *tfm)
struct ccp_ctx *ctx = crypto_tfm_ctx(tfm);
if (ctx->u.sha.hmac_tfm)
- crypto_free_ahash(ctx->u.sha.hmac_tfm);
+ crypto_free_shash(ctx->u.sha.hmac_tfm);
ccp_sha_cra_exit(tfm);
}
-static const __be32 sha1_init[CCP_SHA_CTXSIZE / sizeof(__be32)] = {
- cpu_to_be32(SHA1_H0), cpu_to_be32(SHA1_H1),
- cpu_to_be32(SHA1_H2), cpu_to_be32(SHA1_H3),
- cpu_to_be32(SHA1_H4), 0, 0, 0,
-};
-
-static const __be32 sha224_init[CCP_SHA_CTXSIZE / sizeof(__be32)] = {
- cpu_to_be32(SHA224_H0), cpu_to_be32(SHA224_H1),
- cpu_to_be32(SHA224_H2), cpu_to_be32(SHA224_H3),
- cpu_to_be32(SHA224_H4), cpu_to_be32(SHA224_H5),
- cpu_to_be32(SHA224_H6), cpu_to_be32(SHA224_H7),
-};
-
-static const __be32 sha256_init[CCP_SHA_CTXSIZE / sizeof(__be32)] = {
- cpu_to_be32(SHA256_H0), cpu_to_be32(SHA256_H1),
- cpu_to_be32(SHA256_H2), cpu_to_be32(SHA256_H3),
- cpu_to_be32(SHA256_H4), cpu_to_be32(SHA256_H5),
- cpu_to_be32(SHA256_H6), cpu_to_be32(SHA256_H7),
-};
-
struct ccp_sha_def {
const char *name;
const char *drv_name;
- const __be32 *init;
enum ccp_sha_type type;
u32 digest_size;
u32 block_size;
@@ -377,7 +301,6 @@ static struct ccp_sha_def sha_algs[] = {
{
.name = "sha1",
.drv_name = "sha1-ccp",
- .init = sha1_init,
.type = CCP_SHA_TYPE_1,
.digest_size = SHA1_DIGEST_SIZE,
.block_size = SHA1_BLOCK_SIZE,
@@ -385,7 +308,6 @@ static struct ccp_sha_def sha_algs[] = {
{
.name = "sha224",
.drv_name = "sha224-ccp",
- .init = sha224_init,
.type = CCP_SHA_TYPE_224,
.digest_size = SHA224_DIGEST_SIZE,
.block_size = SHA224_BLOCK_SIZE,
@@ -393,7 +315,6 @@ static struct ccp_sha_def sha_algs[] = {
{
.name = "sha256",
.drv_name = "sha256-ccp",
- .init = sha256_init,
.type = CCP_SHA_TYPE_256,
.digest_size = SHA256_DIGEST_SIZE,
.block_size = SHA256_BLOCK_SIZE,
@@ -460,7 +381,6 @@ static int ccp_register_sha_alg(struct list_head *head,
INIT_LIST_HEAD(&ccp_alg->entry);
- ccp_alg->init = def->init;
ccp_alg->type = def->type;
alg = &ccp_alg->alg;
diff --git a/drivers/crypto/ccp/ccp-crypto.h b/drivers/crypto/ccp/ccp-crypto.h
index b222231b6169..9aa4ae184f7f 100644
--- a/drivers/crypto/ccp/ccp-crypto.h
+++ b/drivers/crypto/ccp/ccp-crypto.h
@@ -137,11 +137,14 @@ struct ccp_aes_cmac_req_ctx {
#define MAX_SHA_BLOCK_SIZE SHA256_BLOCK_SIZE
struct ccp_sha_ctx {
+ struct scatterlist opad_sg;
+ unsigned int opad_count;
+
unsigned int key_len;
u8 key[MAX_SHA_BLOCK_SIZE];
u8 ipad[MAX_SHA_BLOCK_SIZE];
u8 opad[MAX_SHA_BLOCK_SIZE];
- struct crypto_ahash *hmac_tfm;
+ struct crypto_shash *hmac_tfm;
};
struct ccp_sha_req_ctx {
@@ -167,9 +170,6 @@ struct ccp_sha_req_ctx {
unsigned int buf_count;
u8 buf[MAX_SHA_BLOCK_SIZE];
- /* HMAC support field */
- struct scatterlist pad_sg;
-
/* CCP driver command */
struct ccp_cmd cmd;
};
diff --git a/drivers/crypto/ccp/ccp-dev.c b/drivers/crypto/ccp/ccp-dev.c
index c3bc21264600..2c7816149b01 100644
--- a/drivers/crypto/ccp/ccp-dev.c
+++ b/drivers/crypto/ccp/ccp-dev.c
@@ -30,6 +30,11 @@ MODULE_LICENSE("GPL");
MODULE_VERSION("1.0.0");
MODULE_DESCRIPTION("AMD Cryptographic Coprocessor driver");
+struct ccp_tasklet_data {
+ struct completion completion;
+ struct ccp_cmd *cmd;
+};
+
static struct ccp_device *ccp_dev;
static inline struct ccp_device *ccp_get_device(void)
@@ -192,17 +197,23 @@ static struct ccp_cmd *ccp_dequeue_cmd(struct ccp_cmd_queue *cmd_q)
return cmd;
}
-static void ccp_do_cmd_complete(struct work_struct *work)
+static void ccp_do_cmd_complete(unsigned long data)
{
- struct ccp_cmd *cmd = container_of(work, struct ccp_cmd, work);
+ struct ccp_tasklet_data *tdata = (struct ccp_tasklet_data *)data;
+ struct ccp_cmd *cmd = tdata->cmd;
cmd->callback(cmd->data, cmd->ret);
+ complete(&tdata->completion);
}
static int ccp_cmd_queue_thread(void *data)
{
struct ccp_cmd_queue *cmd_q = (struct ccp_cmd_queue *)data;
struct ccp_cmd *cmd;
+ struct ccp_tasklet_data tdata;
+ struct tasklet_struct tasklet;
+
+ tasklet_init(&tasklet, ccp_do_cmd_complete, (unsigned long)&tdata);
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
@@ -220,8 +231,10 @@ static int ccp_cmd_queue_thread(void *data)
cmd->ret = ccp_run_cmd(cmd_q, cmd);
/* Schedule the completion callback */
- INIT_WORK(&cmd->work, ccp_do_cmd_complete);
- schedule_work(&cmd->work);
+ tdata.cmd = cmd;
+ init_completion(&tdata.completion);
+ tasklet_schedule(&tasklet);
+ wait_for_completion(&tdata.completion);
}
__set_current_state(TASK_RUNNING);
diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c
index 71ed3ade7e12..9ae006d69df4 100644
--- a/drivers/crypto/ccp/ccp-ops.c
+++ b/drivers/crypto/ccp/ccp-ops.c
@@ -23,6 +23,7 @@
#include <linux/ccp.h>
#include <linux/scatterlist.h>
#include <crypto/scatterwalk.h>
+#include <crypto/sha.h>
#include "ccp-dev.h"
@@ -132,6 +133,27 @@ struct ccp_op {
} u;
};
+/* SHA initial context values */
+static const __be32 ccp_sha1_init[CCP_SHA_CTXSIZE / sizeof(__be32)] = {
+ cpu_to_be32(SHA1_H0), cpu_to_be32(SHA1_H1),
+ cpu_to_be32(SHA1_H2), cpu_to_be32(SHA1_H3),
+ cpu_to_be32(SHA1_H4), 0, 0, 0,
+};
+
+static const __be32 ccp_sha224_init[CCP_SHA_CTXSIZE / sizeof(__be32)] = {
+ cpu_to_be32(SHA224_H0), cpu_to_be32(SHA224_H1),
+ cpu_to_be32(SHA224_H2), cpu_to_be32(SHA224_H3),
+ cpu_to_be32(SHA224_H4), cpu_to_be32(SHA224_H5),
+ cpu_to_be32(SHA224_H6), cpu_to_be32(SHA224_H7),
+};
+
+static const __be32 ccp_sha256_init[CCP_SHA_CTXSIZE / sizeof(__be32)] = {
+ cpu_to_be32(SHA256_H0), cpu_to_be32(SHA256_H1),
+ cpu_to_be32(SHA256_H2), cpu_to_be32(SHA256_H3),
+ cpu_to_be32(SHA256_H4), cpu_to_be32(SHA256_H5),
+ 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
@@ -1411,7 +1433,27 @@ static int ccp_run_sha_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
if (ret)
return ret;
- ccp_set_dm_area(&ctx, 0, sha->ctx, 0, sha->ctx_len);
+ if (sha->first) {
+ const __be32 *init;
+
+ switch (sha->type) {
+ case CCP_SHA_TYPE_1:
+ init = ccp_sha1_init;
+ break;
+ case CCP_SHA_TYPE_224:
+ init = ccp_sha224_init;
+ break;
+ case CCP_SHA_TYPE_256:
+ init = ccp_sha256_init;
+ break;
+ default:
+ ret = -EINVAL;
+ goto e_ctx;
+ }
+ memcpy(ctx.address, init, CCP_SHA_CTXSIZE);
+ } else
+ ccp_set_dm_area(&ctx, 0, sha->ctx, 0, sha->ctx_len);
+
ret = ccp_copy_to_ksb(cmd_q, &ctx, op.jobid, op.ksb_ctx,
CCP_PASSTHRU_BYTESWAP_256BIT);
if (ret) {
@@ -1451,6 +1493,66 @@ static int ccp_run_sha_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
ccp_get_dm_area(&ctx, 0, sha->ctx, 0, sha->ctx_len);
+ if (sha->final && sha->opad) {
+ /* HMAC operation, recursively perform final SHA */
+ struct ccp_cmd hmac_cmd;
+ struct scatterlist sg;
+ u64 block_size, digest_size;
+ u8 *hmac_buf;
+
+ switch (sha->type) {
+ case CCP_SHA_TYPE_1:
+ block_size = SHA1_BLOCK_SIZE;
+ digest_size = SHA1_DIGEST_SIZE;
+ break;
+ case CCP_SHA_TYPE_224:
+ block_size = SHA224_BLOCK_SIZE;
+ digest_size = SHA224_DIGEST_SIZE;
+ break;
+ case CCP_SHA_TYPE_256:
+ block_size = SHA256_BLOCK_SIZE;
+ digest_size = SHA256_DIGEST_SIZE;
+ break;
+ default:
+ ret = -EINVAL;
+ goto e_data;
+ }
+
+ if (sha->opad_len != block_size) {
+ ret = -EINVAL;
+ goto e_data;
+ }
+
+ hmac_buf = kmalloc(block_size + digest_size, GFP_KERNEL);
+ if (!hmac_buf) {
+ ret = -ENOMEM;
+ goto e_data;
+ }
+ sg_init_one(&sg, hmac_buf, block_size + digest_size);
+
+ scatterwalk_map_and_copy(hmac_buf, sha->opad, 0, block_size, 0);
+ memcpy(hmac_buf + block_size, ctx.address, digest_size);
+
+ memset(&hmac_cmd, 0, sizeof(hmac_cmd));
+ hmac_cmd.engine = CCP_ENGINE_SHA;
+ hmac_cmd.u.sha.type = sha->type;
+ hmac_cmd.u.sha.ctx = sha->ctx;
+ hmac_cmd.u.sha.ctx_len = sha->ctx_len;
+ hmac_cmd.u.sha.src = &sg;
+ hmac_cmd.u.sha.src_len = block_size + digest_size;
+ hmac_cmd.u.sha.opad = NULL;
+ hmac_cmd.u.sha.opad_len = 0;
+ hmac_cmd.u.sha.first = 1;
+ hmac_cmd.u.sha.final = 1;
+ hmac_cmd.u.sha.msg_bits = (block_size + digest_size) << 3;
+
+ ret = ccp_run_sha_cmd(cmd_q, &hmac_cmd);
+ if (ret)
+ cmd->engine_error = hmac_cmd.engine_error;
+
+ kfree(hmac_buf);
+ }
+
e_data:
ccp_free_data(&src, cmd_q);
@@ -1666,8 +1768,8 @@ static int ccp_run_passthru_cmd(struct ccp_cmd_queue *cmd_q,
op.dst.type = CCP_MEMTYPE_SYSTEM;
op.dst.u.dma.address = sg_dma_address(dst.sg_wa.sg);
- op.src.u.dma.offset = dst.sg_wa.sg_used;
- op.src.u.dma.length = op.src.u.dma.length;
+ op.dst.u.dma.offset = dst.sg_wa.sg_used;
+ op.dst.u.dma.length = op.src.u.dma.length;
ret = ccp_perform_passthru(&op);
if (ret) {
diff --git a/drivers/crypto/mxs-dcp.c b/drivers/crypto/mxs-dcp.c
index a6db7fa6f891..7bbe0ab21eca 100644
--- a/drivers/crypto/mxs-dcp.c
+++ b/drivers/crypto/mxs-dcp.c
@@ -29,6 +29,8 @@
#define DCP_MAX_CHANS 4
#define DCP_BUF_SZ PAGE_SIZE
+#define DCP_ALIGNMENT 64
+
/* DCP DMA descriptor. */
struct dcp_dma_desc {
uint32_t next_cmd_addr;
@@ -48,7 +50,6 @@ struct dcp_coherent_block {
uint8_t sha_in_buf[DCP_BUF_SZ];
uint8_t aes_key[2 * AES_KEYSIZE_128];
- uint8_t sha_digest[SHA256_DIGEST_SIZE];
struct dcp_dma_desc desc[DCP_MAX_CHANS];
};
@@ -83,13 +84,16 @@ struct dcp_async_ctx {
unsigned int hot:1;
/* Crypto-specific context */
- unsigned int enc:1;
- unsigned int ecb:1;
struct crypto_ablkcipher *fallback;
unsigned int key_len;
uint8_t key[AES_KEYSIZE_128];
};
+struct dcp_aes_req_ctx {
+ unsigned int enc:1;
+ unsigned int ecb:1;
+};
+
struct dcp_sha_req_ctx {
unsigned int init:1;
unsigned int fini:1;
@@ -190,10 +194,12 @@ static int mxs_dcp_start_dma(struct dcp_async_ctx *actx)
/*
* Encryption (AES128)
*/
-static int mxs_dcp_run_aes(struct dcp_async_ctx *actx, int init)
+static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
+ struct ablkcipher_request *req, int init)
{
struct dcp *sdcp = global_sdcp;
struct dcp_dma_desc *desc = &sdcp->coh->desc[actx->chan];
+ struct dcp_aes_req_ctx *rctx = ablkcipher_request_ctx(req);
int ret;
dma_addr_t key_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_key,
@@ -212,14 +218,14 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx, int init)
/* Payload contains the key. */
desc->control0 |= MXS_DCP_CONTROL0_PAYLOAD_KEY;
- if (actx->enc)
+ if (rctx->enc)
desc->control0 |= MXS_DCP_CONTROL0_CIPHER_ENCRYPT;
if (init)
desc->control0 |= MXS_DCP_CONTROL0_CIPHER_INIT;
desc->control1 = MXS_DCP_CONTROL1_CIPHER_SELECT_AES128;
- if (actx->ecb)
+ if (rctx->ecb)
desc->control1 |= MXS_DCP_CONTROL1_CIPHER_MODE_ECB;
else
desc->control1 |= MXS_DCP_CONTROL1_CIPHER_MODE_CBC;
@@ -247,6 +253,7 @@ static int mxs_dcp_aes_block_crypt(struct crypto_async_request *arq)
struct ablkcipher_request *req = ablkcipher_request_cast(arq);
struct dcp_async_ctx *actx = crypto_tfm_ctx(arq->tfm);
+ struct dcp_aes_req_ctx *rctx = ablkcipher_request_ctx(req);
struct scatterlist *dst = req->dst;
struct scatterlist *src = req->src;
@@ -271,7 +278,7 @@ static int mxs_dcp_aes_block_crypt(struct crypto_async_request *arq)
/* Copy the key from the temporary location. */
memcpy(key, actx->key, actx->key_len);
- if (!actx->ecb) {
+ if (!rctx->ecb) {
/* Copy the CBC IV just past the key. */
memcpy(key + AES_KEYSIZE_128, req->info, AES_KEYSIZE_128);
/* CBC needs the INIT set. */
@@ -300,7 +307,7 @@ static int mxs_dcp_aes_block_crypt(struct crypto_async_request *arq)
* submit the buffer.
*/
if (actx->fill == out_off || sg_is_last(src)) {
- ret = mxs_dcp_run_aes(actx, init);
+ ret = mxs_dcp_run_aes(actx, req, init);
if (ret)
return ret;
init = 0;
@@ -391,13 +398,14 @@ static int mxs_dcp_aes_enqueue(struct ablkcipher_request *req, int enc, int ecb)
struct dcp *sdcp = global_sdcp;
struct crypto_async_request *arq = &req->base;
struct dcp_async_ctx *actx = crypto_tfm_ctx(arq->tfm);
+ struct dcp_aes_req_ctx *rctx = ablkcipher_request_ctx(req);
int ret;
if (unlikely(actx->key_len != AES_KEYSIZE_128))
return mxs_dcp_block_fallback(req, enc);
- actx->enc = enc;
- actx->ecb = ecb;
+ rctx->enc = enc;
+ rctx->ecb = ecb;
actx->chan = DCP_CHAN_CRYPTO;
mutex_lock(&sdcp->mutex[actx->chan]);
@@ -484,7 +492,7 @@ static int mxs_dcp_aes_fallback_init(struct crypto_tfm *tfm)
return PTR_ERR(blk);
actx->fallback = blk;
- tfm->crt_ablkcipher.reqsize = sizeof(struct dcp_async_ctx);
+ tfm->crt_ablkcipher.reqsize = sizeof(struct dcp_aes_req_ctx);
return 0;
}
@@ -507,13 +515,11 @@ static int mxs_dcp_run_sha(struct ahash_request *req)
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct dcp_async_ctx *actx = crypto_ahash_ctx(tfm);
struct dcp_sha_req_ctx *rctx = ahash_request_ctx(req);
+ struct hash_alg_common *halg = crypto_hash_alg_common(tfm);
struct dcp_dma_desc *desc = &sdcp->coh->desc[actx->chan];
- dma_addr_t digest_phys = dma_map_single(sdcp->dev,
- sdcp->coh->sha_digest,
- SHA256_DIGEST_SIZE,
- DMA_FROM_DEVICE);
+ dma_addr_t digest_phys = 0;
dma_addr_t buf_phys = dma_map_single(sdcp->dev, sdcp->coh->sha_in_buf,
DCP_BUF_SZ, DMA_TO_DEVICE);
@@ -534,14 +540,18 @@ static int mxs_dcp_run_sha(struct ahash_request *req)
/* Set HASH_TERM bit for last transfer block. */
if (rctx->fini) {
+ digest_phys = dma_map_single(sdcp->dev, req->result,
+ halg->digestsize, DMA_FROM_DEVICE);
desc->control0 |= MXS_DCP_CONTROL0_HASH_TERM;
desc->payload = digest_phys;
}
ret = mxs_dcp_start_dma(actx);
- dma_unmap_single(sdcp->dev, digest_phys, SHA256_DIGEST_SIZE,
- DMA_FROM_DEVICE);
+ if (rctx->fini)
+ dma_unmap_single(sdcp->dev, digest_phys, halg->digestsize,
+ DMA_FROM_DEVICE);
+
dma_unmap_single(sdcp->dev, buf_phys, DCP_BUF_SZ, DMA_TO_DEVICE);
return ret;
@@ -558,7 +568,6 @@ static int dcp_sha_req_to_buf(struct crypto_async_request *arq)
struct hash_alg_common *halg = crypto_hash_alg_common(tfm);
const int nents = sg_nents(req->src);
- uint8_t *digest = sdcp->coh->sha_digest;
uint8_t *in_buf = sdcp->coh->sha_in_buf;
uint8_t *src_buf;
@@ -605,14 +614,20 @@ static int dcp_sha_req_to_buf(struct crypto_async_request *arq)
rctx->fini = 1;
/* Submit whatever is left. */
+ if (!req->result)
+ return -EINVAL;
+
ret = mxs_dcp_run_sha(req);
- if (ret || !req->result)
+ if (ret)
return ret;
+
actx->fill = 0;
/* For some reason, the result is flipped. */
- for (i = 0; i < halg->digestsize; i++)
- req->result[i] = digest[halg->digestsize - i - 1];
+ for (i = 0; i < halg->digestsize / 2; i++) {
+ swap(req->result[i],
+ req->result[halg->digestsize - i - 1]);
+ }
}
return 0;
@@ -901,9 +916,14 @@ static int mxs_dcp_probe(struct platform_device *pdev)
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dcp_vmi_irq = platform_get_irq(pdev, 0);
+ if (dcp_vmi_irq < 0) {
+ ret = dcp_vmi_irq;
+ goto err_mutex;
+ }
+
dcp_irq = platform_get_irq(pdev, 1);
- if (dcp_vmi_irq < 0 || dcp_irq < 0) {
- ret = -EINVAL;
+ if (dcp_irq < 0) {
+ ret = dcp_irq;
goto err_mutex;
}
@@ -935,15 +955,20 @@ static int mxs_dcp_probe(struct platform_device *pdev)
}
/* Allocate coherent helper block. */
- sdcp->coh = kzalloc(sizeof(struct dcp_coherent_block), GFP_KERNEL);
+ sdcp->coh = devm_kzalloc(dev, sizeof(*sdcp->coh) + DCP_ALIGNMENT,
+ GFP_KERNEL);
if (!sdcp->coh) {
- dev_err(dev, "Error allocating coherent block\n");
ret = -ENOMEM;
goto err_mutex;
}
+ /* Re-align the structure so it fits the DCP constraints. */
+ sdcp->coh = PTR_ALIGN(sdcp->coh, DCP_ALIGNMENT);
+
/* Restart the DCP block. */
- stmp_reset_block(sdcp->base);
+ ret = stmp_reset_block(sdcp->base);
+ if (ret)
+ goto err_mutex;
/* Initialize control register. */
writel(MXS_DCP_CTRL_GATHER_RESIDUAL_WRITES |
@@ -982,7 +1007,7 @@ static int mxs_dcp_probe(struct platform_device *pdev)
if (IS_ERR(sdcp->thread[DCP_CHAN_HASH_SHA])) {
dev_err(dev, "Error starting SHA thread!\n");
ret = PTR_ERR(sdcp->thread[DCP_CHAN_HASH_SHA]);
- goto err_free_coherent;
+ goto err_mutex;
}
sdcp->thread[DCP_CHAN_CRYPTO] = kthread_run(dcp_chan_thread_aes,
@@ -1040,8 +1065,6 @@ err_destroy_aes_thread:
err_destroy_sha_thread:
kthread_stop(sdcp->thread[DCP_CHAN_HASH_SHA]);
-err_free_coherent:
- kfree(sdcp->coh);
err_mutex:
mutex_unlock(&global_mutex);
return ret;
@@ -1051,8 +1074,6 @@ static int mxs_dcp_remove(struct platform_device *pdev)
{
struct dcp *sdcp = platform_get_drvdata(pdev);
- kfree(sdcp->coh);
-
if (sdcp->caps & MXS_DCP_CAPABILITY1_SHA256)
crypto_unregister_ahash(&dcp_sha256_alg);
diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c
index dde41f1df608..cb98fa54573d 100644
--- a/drivers/crypto/omap-aes.c
+++ b/drivers/crypto/omap-aes.c
@@ -1307,9 +1307,7 @@ static int omap_aes_resume(struct device *dev)
}
#endif
-static const struct dev_pm_ops omap_aes_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(omap_aes_suspend, omap_aes_resume)
-};
+static SIMPLE_DEV_PM_OPS(omap_aes_pm_ops, omap_aes_suspend, omap_aes_resume);
static struct platform_driver omap_aes_driver = {
.probe = omap_aes_probe,
diff --git a/drivers/crypto/omap-des.c b/drivers/crypto/omap-des.c
new file mode 100644
index 000000000000..ec5f13162b73
--- /dev/null
+++ b/drivers/crypto/omap-des.c
@@ -0,0 +1,1216 @@
+/*
+ * Support for OMAP DES and Triple DES HW acceleration.
+ *
+ * Copyright (c) 2013 Texas Instruments Incorporated
+ * Author: Joel Fernandes <joelf@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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#ifdef DEBUG
+#define prn(num) printk(#num "=%d\n", num)
+#define prx(num) printk(#num "=%x\n", num)
+#else
+#define prn(num) do { } while (0)
+#define prx(num) do { } while (0)
+#endif
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/omap-dma.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/crypto.h>
+#include <linux/interrupt.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/des.h>
+
+#define DST_MAXBURST 2
+
+#define DES_BLOCK_WORDS (DES_BLOCK_SIZE >> 2)
+
+#define _calc_walked(inout) (dd->inout##_walk.offset - dd->inout##_sg->offset)
+
+#define DES_REG_KEY(dd, x) ((dd)->pdata->key_ofs - \
+ ((x ^ 0x01) * 0x04))
+
+#define DES_REG_IV(dd, x) ((dd)->pdata->iv_ofs + ((x) * 0x04))
+
+#define DES_REG_CTRL(dd) ((dd)->pdata->ctrl_ofs)
+#define DES_REG_CTRL_CBC BIT(4)
+#define DES_REG_CTRL_TDES BIT(3)
+#define DES_REG_CTRL_DIRECTION BIT(2)
+#define DES_REG_CTRL_INPUT_READY BIT(1)
+#define DES_REG_CTRL_OUTPUT_READY BIT(0)
+
+#define DES_REG_DATA_N(dd, x) ((dd)->pdata->data_ofs + ((x) * 0x04))
+
+#define DES_REG_REV(dd) ((dd)->pdata->rev_ofs)
+
+#define DES_REG_MASK(dd) ((dd)->pdata->mask_ofs)
+
+#define DES_REG_LENGTH_N(x) (0x24 + ((x) * 0x04))
+
+#define DES_REG_IRQ_STATUS(dd) ((dd)->pdata->irq_status_ofs)
+#define DES_REG_IRQ_ENABLE(dd) ((dd)->pdata->irq_enable_ofs)
+#define DES_REG_IRQ_DATA_IN BIT(1)
+#define DES_REG_IRQ_DATA_OUT BIT(2)
+
+#define FLAGS_MODE_MASK 0x000f
+#define FLAGS_ENCRYPT BIT(0)
+#define FLAGS_CBC BIT(1)
+#define FLAGS_INIT BIT(4)
+#define FLAGS_BUSY BIT(6)
+
+struct omap_des_ctx {
+ struct omap_des_dev *dd;
+
+ int keylen;
+ u32 key[(3 * DES_KEY_SIZE) / sizeof(u32)];
+ unsigned long flags;
+};
+
+struct omap_des_reqctx {
+ unsigned long mode;
+};
+
+#define OMAP_DES_QUEUE_LENGTH 1
+#define OMAP_DES_CACHE_SIZE 0
+
+struct omap_des_algs_info {
+ struct crypto_alg *algs_list;
+ unsigned int size;
+ unsigned int registered;
+};
+
+struct omap_des_pdata {
+ struct omap_des_algs_info *algs_info;
+ unsigned int algs_info_size;
+
+ void (*trigger)(struct omap_des_dev *dd, int length);
+
+ u32 key_ofs;
+ u32 iv_ofs;
+ u32 ctrl_ofs;
+ u32 data_ofs;
+ u32 rev_ofs;
+ u32 mask_ofs;
+ u32 irq_enable_ofs;
+ u32 irq_status_ofs;
+
+ u32 dma_enable_in;
+ u32 dma_enable_out;
+ u32 dma_start;
+
+ u32 major_mask;
+ u32 major_shift;
+ u32 minor_mask;
+ u32 minor_shift;
+};
+
+struct omap_des_dev {
+ struct list_head list;
+ unsigned long phys_base;
+ void __iomem *io_base;
+ struct omap_des_ctx *ctx;
+ struct device *dev;
+ unsigned long flags;
+ int err;
+
+ /* spinlock used for queues */
+ spinlock_t lock;
+ struct crypto_queue queue;
+
+ struct tasklet_struct done_task;
+ struct tasklet_struct queue_task;
+
+ struct ablkcipher_request *req;
+ /*
+ * total is used by PIO mode for book keeping so introduce
+ * variable total_save as need it to calc page_order
+ */
+ size_t total;
+ size_t total_save;
+
+ struct scatterlist *in_sg;
+ struct scatterlist *out_sg;
+
+ /* Buffers for copying for unaligned cases */
+ struct scatterlist in_sgl;
+ struct scatterlist out_sgl;
+ struct scatterlist *orig_out;
+ int sgs_copied;
+
+ struct scatter_walk in_walk;
+ struct scatter_walk out_walk;
+ int dma_in;
+ struct dma_chan *dma_lch_in;
+ int dma_out;
+ struct dma_chan *dma_lch_out;
+ int in_sg_len;
+ int out_sg_len;
+ int pio_only;
+ const struct omap_des_pdata *pdata;
+};
+
+/* keep registered devices data here */
+static LIST_HEAD(dev_list);
+static DEFINE_SPINLOCK(list_lock);
+
+#ifdef DEBUG
+#define omap_des_read(dd, offset) \
+ ({ \
+ int _read_ret; \
+ _read_ret = __raw_readl(dd->io_base + offset); \
+ pr_err("omap_des_read(" #offset "=%#x)= %#x\n", \
+ offset, _read_ret); \
+ _read_ret; \
+ })
+#else
+static inline u32 omap_des_read(struct omap_des_dev *dd, u32 offset)
+{
+ return __raw_readl(dd->io_base + offset);
+}
+#endif
+
+#ifdef DEBUG
+#define omap_des_write(dd, offset, value) \
+ do { \
+ pr_err("omap_des_write(" #offset "=%#x) value=%#x\n", \
+ offset, value); \
+ __raw_writel(value, dd->io_base + offset); \
+ } while (0)
+#else
+static inline void omap_des_write(struct omap_des_dev *dd, u32 offset,
+ u32 value)
+{
+ __raw_writel(value, dd->io_base + offset);
+}
+#endif
+
+static inline void omap_des_write_mask(struct omap_des_dev *dd, u32 offset,
+ u32 value, u32 mask)
+{
+ u32 val;
+
+ val = omap_des_read(dd, offset);
+ val &= ~mask;
+ val |= value;
+ omap_des_write(dd, offset, val);
+}
+
+static void omap_des_write_n(struct omap_des_dev *dd, u32 offset,
+ u32 *value, int count)
+{
+ for (; count--; value++, offset += 4)
+ omap_des_write(dd, offset, *value);
+}
+
+static int omap_des_hw_init(struct omap_des_dev *dd)
+{
+ /*
+ * clocks are enabled when request starts and disabled when finished.
+ * It may be long delays between requests.
+ * Device might go to off mode to save power.
+ */
+ pm_runtime_get_sync(dd->dev);
+
+ if (!(dd->flags & FLAGS_INIT)) {
+ dd->flags |= FLAGS_INIT;
+ dd->err = 0;
+ }
+
+ return 0;
+}
+
+static int omap_des_write_ctrl(struct omap_des_dev *dd)
+{
+ unsigned int key32;
+ int i, err;
+ u32 val = 0, mask = 0;
+
+ err = omap_des_hw_init(dd);
+ if (err)
+ return err;
+
+ key32 = dd->ctx->keylen / sizeof(u32);
+
+ /* it seems a key should always be set even if it has not changed */
+ for (i = 0; i < key32; i++) {
+ omap_des_write(dd, DES_REG_KEY(dd, i),
+ __le32_to_cpu(dd->ctx->key[i]));
+ }
+
+ if ((dd->flags & FLAGS_CBC) && dd->req->info)
+ omap_des_write_n(dd, DES_REG_IV(dd, 0), dd->req->info, 2);
+
+ if (dd->flags & FLAGS_CBC)
+ val |= DES_REG_CTRL_CBC;
+ if (dd->flags & FLAGS_ENCRYPT)
+ val |= DES_REG_CTRL_DIRECTION;
+ if (key32 == 6)
+ val |= DES_REG_CTRL_TDES;
+
+ mask |= DES_REG_CTRL_CBC | DES_REG_CTRL_DIRECTION | DES_REG_CTRL_TDES;
+
+ omap_des_write_mask(dd, DES_REG_CTRL(dd), val, mask);
+
+ return 0;
+}
+
+static void omap_des_dma_trigger_omap4(struct omap_des_dev *dd, int length)
+{
+ u32 mask, val;
+
+ omap_des_write(dd, DES_REG_LENGTH_N(0), length);
+
+ val = dd->pdata->dma_start;
+
+ if (dd->dma_lch_out != NULL)
+ val |= dd->pdata->dma_enable_out;
+ if (dd->dma_lch_in != NULL)
+ val |= dd->pdata->dma_enable_in;
+
+ mask = dd->pdata->dma_enable_out | dd->pdata->dma_enable_in |
+ dd->pdata->dma_start;
+
+ omap_des_write_mask(dd, DES_REG_MASK(dd), val, mask);
+}
+
+static void omap_des_dma_stop(struct omap_des_dev *dd)
+{
+ u32 mask;
+
+ mask = dd->pdata->dma_enable_out | dd->pdata->dma_enable_in |
+ dd->pdata->dma_start;
+
+ omap_des_write_mask(dd, DES_REG_MASK(dd), 0, mask);
+}
+
+static struct omap_des_dev *omap_des_find_dev(struct omap_des_ctx *ctx)
+{
+ struct omap_des_dev *dd = NULL, *tmp;
+
+ spin_lock_bh(&list_lock);
+ if (!ctx->dd) {
+ list_for_each_entry(tmp, &dev_list, list) {
+ /* FIXME: take fist available des core */
+ dd = tmp;
+ break;
+ }
+ ctx->dd = dd;
+ } else {
+ /* already found before */
+ dd = ctx->dd;
+ }
+ spin_unlock_bh(&list_lock);
+
+ return dd;
+}
+
+static void omap_des_dma_out_callback(void *data)
+{
+ struct omap_des_dev *dd = data;
+
+ /* dma_lch_out - completed */
+ tasklet_schedule(&dd->done_task);
+}
+
+static int omap_des_dma_init(struct omap_des_dev *dd)
+{
+ int err = -ENOMEM;
+ dma_cap_mask_t mask;
+
+ dd->dma_lch_out = NULL;
+ dd->dma_lch_in = NULL;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ dd->dma_lch_in = dma_request_slave_channel_compat(mask,
+ omap_dma_filter_fn,
+ &dd->dma_in,
+ dd->dev, "rx");
+ if (!dd->dma_lch_in) {
+ dev_err(dd->dev, "Unable to request in DMA channel\n");
+ goto err_dma_in;
+ }
+
+ dd->dma_lch_out = dma_request_slave_channel_compat(mask,
+ omap_dma_filter_fn,
+ &dd->dma_out,
+ dd->dev, "tx");
+ if (!dd->dma_lch_out) {
+ dev_err(dd->dev, "Unable to request out DMA channel\n");
+ goto err_dma_out;
+ }
+
+ return 0;
+
+err_dma_out:
+ dma_release_channel(dd->dma_lch_in);
+err_dma_in:
+ if (err)
+ pr_err("error: %d\n", err);
+ return err;
+}
+
+static void omap_des_dma_cleanup(struct omap_des_dev *dd)
+{
+ dma_release_channel(dd->dma_lch_out);
+ dma_release_channel(dd->dma_lch_in);
+}
+
+static void sg_copy_buf(void *buf, struct scatterlist *sg,
+ unsigned int start, unsigned int nbytes, int out)
+{
+ struct scatter_walk walk;
+
+ if (!nbytes)
+ return;
+
+ scatterwalk_start(&walk, sg);
+ scatterwalk_advance(&walk, start);
+ scatterwalk_copychunks(buf, &walk, nbytes, out);
+ scatterwalk_done(&walk, out, 0);
+}
+
+static int omap_des_crypt_dma(struct crypto_tfm *tfm,
+ struct scatterlist *in_sg, struct scatterlist *out_sg,
+ int in_sg_len, int out_sg_len)
+{
+ struct omap_des_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct omap_des_dev *dd = ctx->dd;
+ struct dma_async_tx_descriptor *tx_in, *tx_out;
+ struct dma_slave_config cfg;
+ int ret;
+
+ if (dd->pio_only) {
+ scatterwalk_start(&dd->in_walk, dd->in_sg);
+ scatterwalk_start(&dd->out_walk, dd->out_sg);
+
+ /* Enable DATAIN interrupt and let it take
+ care of the rest */
+ omap_des_write(dd, DES_REG_IRQ_ENABLE(dd), 0x2);
+ return 0;
+ }
+
+ dma_sync_sg_for_device(dd->dev, dd->in_sg, in_sg_len, DMA_TO_DEVICE);
+
+ memset(&cfg, 0, sizeof(cfg));
+
+ cfg.src_addr = dd->phys_base + DES_REG_DATA_N(dd, 0);
+ cfg.dst_addr = dd->phys_base + DES_REG_DATA_N(dd, 0);
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.src_maxburst = DST_MAXBURST;
+ cfg.dst_maxburst = DST_MAXBURST;
+
+ /* IN */
+ ret = dmaengine_slave_config(dd->dma_lch_in, &cfg);
+ if (ret) {
+ dev_err(dd->dev, "can't configure IN dmaengine slave: %d\n",
+ ret);
+ return ret;
+ }
+
+ tx_in = dmaengine_prep_slave_sg(dd->dma_lch_in, in_sg, in_sg_len,
+ DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx_in) {
+ dev_err(dd->dev, "IN prep_slave_sg() failed\n");
+ return -EINVAL;
+ }
+
+ /* No callback necessary */
+ tx_in->callback_param = dd;
+
+ /* OUT */
+ ret = dmaengine_slave_config(dd->dma_lch_out, &cfg);
+ if (ret) {
+ dev_err(dd->dev, "can't configure OUT dmaengine slave: %d\n",
+ ret);
+ return ret;
+ }
+
+ tx_out = dmaengine_prep_slave_sg(dd->dma_lch_out, out_sg, out_sg_len,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx_out) {
+ dev_err(dd->dev, "OUT prep_slave_sg() failed\n");
+ return -EINVAL;
+ }
+
+ tx_out->callback = omap_des_dma_out_callback;
+ tx_out->callback_param = dd;
+
+ dmaengine_submit(tx_in);
+ dmaengine_submit(tx_out);
+
+ dma_async_issue_pending(dd->dma_lch_in);
+ dma_async_issue_pending(dd->dma_lch_out);
+
+ /* start DMA */
+ dd->pdata->trigger(dd, dd->total);
+
+ return 0;
+}
+
+static int omap_des_crypt_dma_start(struct omap_des_dev *dd)
+{
+ struct crypto_tfm *tfm = crypto_ablkcipher_tfm(
+ crypto_ablkcipher_reqtfm(dd->req));
+ int err;
+
+ pr_debug("total: %d\n", dd->total);
+
+ if (!dd->pio_only) {
+ err = dma_map_sg(dd->dev, dd->in_sg, dd->in_sg_len,
+ DMA_TO_DEVICE);
+ if (!err) {
+ dev_err(dd->dev, "dma_map_sg() error\n");
+ return -EINVAL;
+ }
+
+ err = dma_map_sg(dd->dev, dd->out_sg, dd->out_sg_len,
+ DMA_FROM_DEVICE);
+ if (!err) {
+ dev_err(dd->dev, "dma_map_sg() error\n");
+ return -EINVAL;
+ }
+ }
+
+ err = omap_des_crypt_dma(tfm, dd->in_sg, dd->out_sg, dd->in_sg_len,
+ dd->out_sg_len);
+ if (err && !dd->pio_only) {
+ dma_unmap_sg(dd->dev, dd->in_sg, dd->in_sg_len, DMA_TO_DEVICE);
+ dma_unmap_sg(dd->dev, dd->out_sg, dd->out_sg_len,
+ DMA_FROM_DEVICE);
+ }
+
+ return err;
+}
+
+static void omap_des_finish_req(struct omap_des_dev *dd, int err)
+{
+ struct ablkcipher_request *req = dd->req;
+
+ pr_debug("err: %d\n", err);
+
+ pm_runtime_put(dd->dev);
+ dd->flags &= ~FLAGS_BUSY;
+
+ req->base.complete(&req->base, 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);
+
+ dmaengine_terminate_all(dd->dma_lch_in);
+ dmaengine_terminate_all(dd->dma_lch_out);
+
+ dma_unmap_sg(dd->dev, dd->in_sg, dd->in_sg_len, DMA_TO_DEVICE);
+ dma_unmap_sg(dd->dev, dd->out_sg, dd->out_sg_len, DMA_FROM_DEVICE);
+
+ return err;
+}
+
+static int omap_des_copy_needed(struct scatterlist *sg)
+{
+ while (sg) {
+ if (!IS_ALIGNED(sg->offset, 4))
+ return -1;
+ if (!IS_ALIGNED(sg->length, DES_BLOCK_SIZE))
+ return -1;
+ sg = sg_next(sg);
+ }
+ return 0;
+}
+
+static int omap_des_copy_sgs(struct omap_des_dev *dd)
+{
+ void *buf_in, *buf_out;
+ int pages;
+
+ pages = dd->total >> PAGE_SHIFT;
+
+ if (dd->total & (PAGE_SIZE-1))
+ pages++;
+
+ BUG_ON(!pages);
+
+ buf_in = (void *)__get_free_pages(GFP_ATOMIC, pages);
+ buf_out = (void *)__get_free_pages(GFP_ATOMIC, pages);
+
+ if (!buf_in || !buf_out) {
+ pr_err("Couldn't allocated pages for unaligned cases.\n");
+ return -1;
+ }
+
+ dd->orig_out = dd->out_sg;
+
+ sg_copy_buf(buf_in, dd->in_sg, 0, dd->total, 0);
+
+ sg_init_table(&dd->in_sgl, 1);
+ sg_set_buf(&dd->in_sgl, buf_in, dd->total);
+ dd->in_sg = &dd->in_sgl;
+
+ sg_init_table(&dd->out_sgl, 1);
+ sg_set_buf(&dd->out_sgl, buf_out, dd->total);
+ dd->out_sg = &dd->out_sgl;
+
+ return 0;
+}
+
+static int omap_des_handle_queue(struct omap_des_dev *dd,
+ struct ablkcipher_request *req)
+{
+ struct crypto_async_request *async_req, *backlog;
+ struct omap_des_ctx *ctx;
+ struct omap_des_reqctx *rctx;
+ unsigned long flags;
+ int err, ret = 0;
+
+ spin_lock_irqsave(&dd->lock, flags);
+ if (req)
+ ret = ablkcipher_enqueue_request(&dd->queue, req);
+ if (dd->flags & 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)
+ dd->flags |= FLAGS_BUSY;
+ spin_unlock_irqrestore(&dd->lock, flags);
+
+ if (!async_req)
+ return ret;
+
+ if (backlog)
+ backlog->complete(backlog, -EINPROGRESS);
+
+ req = ablkcipher_request_cast(async_req);
+
+ /* assign new request to device */
+ dd->req = req;
+ dd->total = req->nbytes;
+ dd->total_save = req->nbytes;
+ dd->in_sg = req->src;
+ dd->out_sg = req->dst;
+
+ if (omap_des_copy_needed(dd->in_sg) ||
+ omap_des_copy_needed(dd->out_sg)) {
+ if (omap_des_copy_sgs(dd))
+ pr_err("Failed to copy SGs for unaligned cases\n");
+ dd->sgs_copied = 1;
+ } else {
+ dd->sgs_copied = 0;
+ }
+
+ dd->in_sg_len = scatterwalk_bytes_sglen(dd->in_sg, dd->total);
+ dd->out_sg_len = scatterwalk_bytes_sglen(dd->out_sg, dd->total);
+ BUG_ON(dd->in_sg_len < 0 || dd->out_sg_len < 0);
+
+ rctx = ablkcipher_request_ctx(req);
+ ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
+ rctx->mode &= FLAGS_MODE_MASK;
+ dd->flags = (dd->flags & ~FLAGS_MODE_MASK) | rctx->mode;
+
+ dd->ctx = ctx;
+ ctx->dd = dd;
+
+ err = omap_des_write_ctrl(dd);
+ if (!err)
+ err = omap_des_crypt_dma_start(dd);
+ if (err) {
+ /* des_task will not finish it, so do it here */
+ omap_des_finish_req(dd, err);
+ tasklet_schedule(&dd->queue_task);
+ }
+
+ return ret; /* return ret, which is enqueue return value */
+}
+
+static void omap_des_done_task(unsigned long data)
+{
+ struct omap_des_dev *dd = (struct omap_des_dev *)data;
+ void *buf_in, *buf_out;
+ int pages;
+
+ pr_debug("enter done_task\n");
+
+ if (!dd->pio_only) {
+ dma_sync_sg_for_device(dd->dev, dd->out_sg, dd->out_sg_len,
+ DMA_FROM_DEVICE);
+ dma_unmap_sg(dd->dev, dd->in_sg, dd->in_sg_len, DMA_TO_DEVICE);
+ dma_unmap_sg(dd->dev, dd->out_sg, dd->out_sg_len,
+ DMA_FROM_DEVICE);
+ omap_des_crypt_dma_stop(dd);
+ }
+
+ if (dd->sgs_copied) {
+ buf_in = sg_virt(&dd->in_sgl);
+ buf_out = sg_virt(&dd->out_sgl);
+
+ sg_copy_buf(buf_out, dd->orig_out, 0, dd->total_save, 1);
+
+ pages = get_order(dd->total_save);
+ free_pages((unsigned long)buf_in, pages);
+ free_pages((unsigned long)buf_out, pages);
+ }
+
+ omap_des_finish_req(dd, 0);
+ omap_des_handle_queue(dd, NULL);
+
+ pr_debug("exit\n");
+}
+
+static void omap_des_queue_task(unsigned long data)
+{
+ struct omap_des_dev *dd = (struct omap_des_dev *)data;
+
+ omap_des_handle_queue(dd, NULL);
+}
+
+static int omap_des_crypt(struct ablkcipher_request *req, unsigned long mode)
+{
+ struct omap_des_ctx *ctx = crypto_ablkcipher_ctx(
+ crypto_ablkcipher_reqtfm(req));
+ struct omap_des_reqctx *rctx = ablkcipher_request_ctx(req);
+ struct omap_des_dev *dd;
+
+ pr_debug("nbytes: %d, enc: %d, cbc: %d\n", req->nbytes,
+ !!(mode & FLAGS_ENCRYPT),
+ !!(mode & FLAGS_CBC));
+
+ if (!IS_ALIGNED(req->nbytes, DES_BLOCK_SIZE)) {
+ pr_err("request size is not exact amount of DES blocks\n");
+ return -EINVAL;
+ }
+
+ dd = omap_des_find_dev(ctx);
+ if (!dd)
+ return -ENODEV;
+
+ rctx->mode = mode;
+
+ return omap_des_handle_queue(dd, req);
+}
+
+/* ********************** ALG API ************************************ */
+
+static int omap_des_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct omap_des_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+
+ if (keylen != DES_KEY_SIZE && keylen != (3*DES_KEY_SIZE))
+ return -EINVAL;
+
+ pr_debug("enter, keylen: %d\n", keylen);
+
+ memcpy(ctx->key, key, keylen);
+ ctx->keylen = keylen;
+
+ return 0;
+}
+
+static int omap_des_ecb_encrypt(struct ablkcipher_request *req)
+{
+ return omap_des_crypt(req, FLAGS_ENCRYPT);
+}
+
+static int omap_des_ecb_decrypt(struct ablkcipher_request *req)
+{
+ return omap_des_crypt(req, 0);
+}
+
+static int omap_des_cbc_encrypt(struct ablkcipher_request *req)
+{
+ return omap_des_crypt(req, FLAGS_ENCRYPT | FLAGS_CBC);
+}
+
+static int omap_des_cbc_decrypt(struct ablkcipher_request *req)
+{
+ return omap_des_crypt(req, FLAGS_CBC);
+}
+
+static int omap_des_cra_init(struct crypto_tfm *tfm)
+{
+ pr_debug("enter\n");
+
+ tfm->crt_ablkcipher.reqsize = sizeof(struct omap_des_reqctx);
+
+ return 0;
+}
+
+static void omap_des_cra_exit(struct crypto_tfm *tfm)
+{
+ pr_debug("enter\n");
+}
+
+/* ********************** ALGS ************************************ */
+
+static struct crypto_alg algs_ecb_cbc[] = {
+{
+ .cra_name = "ecb(des)",
+ .cra_driver_name = "ecb-des-omap",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_KERN_DRIVER_ONLY |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct omap_des_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = omap_des_cra_init,
+ .cra_exit = omap_des_cra_exit,
+ .cra_u.ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .setkey = omap_des_setkey,
+ .encrypt = omap_des_ecb_encrypt,
+ .decrypt = omap_des_ecb_decrypt,
+ }
+},
+{
+ .cra_name = "cbc(des)",
+ .cra_driver_name = "cbc-des-omap",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_KERN_DRIVER_ONLY |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct omap_des_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = omap_des_cra_init,
+ .cra_exit = omap_des_cra_exit,
+ .cra_u.ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ .setkey = omap_des_setkey,
+ .encrypt = omap_des_cbc_encrypt,
+ .decrypt = omap_des_cbc_decrypt,
+ }
+},
+{
+ .cra_name = "ecb(des3_ede)",
+ .cra_driver_name = "ecb-des3-omap",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_KERN_DRIVER_ONLY |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct omap_des_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = omap_des_cra_init,
+ .cra_exit = omap_des_cra_exit,
+ .cra_u.ablkcipher = {
+ .min_keysize = 3*DES_KEY_SIZE,
+ .max_keysize = 3*DES_KEY_SIZE,
+ .setkey = omap_des_setkey,
+ .encrypt = omap_des_ecb_encrypt,
+ .decrypt = omap_des_ecb_decrypt,
+ }
+},
+{
+ .cra_name = "cbc(des3_ede)",
+ .cra_driver_name = "cbc-des3-omap",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_KERN_DRIVER_ONLY |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct omap_des_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = omap_des_cra_init,
+ .cra_exit = omap_des_cra_exit,
+ .cra_u.ablkcipher = {
+ .min_keysize = 3*DES_KEY_SIZE,
+ .max_keysize = 3*DES_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ .setkey = omap_des_setkey,
+ .encrypt = omap_des_cbc_encrypt,
+ .decrypt = omap_des_cbc_decrypt,
+ }
+}
+};
+
+static struct omap_des_algs_info omap_des_algs_info_ecb_cbc[] = {
+ {
+ .algs_list = algs_ecb_cbc,
+ .size = ARRAY_SIZE(algs_ecb_cbc),
+ },
+};
+
+#ifdef CONFIG_OF
+static const struct omap_des_pdata omap_des_pdata_omap4 = {
+ .algs_info = omap_des_algs_info_ecb_cbc,
+ .algs_info_size = ARRAY_SIZE(omap_des_algs_info_ecb_cbc),
+ .trigger = omap_des_dma_trigger_omap4,
+ .key_ofs = 0x14,
+ .iv_ofs = 0x18,
+ .ctrl_ofs = 0x20,
+ .data_ofs = 0x28,
+ .rev_ofs = 0x30,
+ .mask_ofs = 0x34,
+ .irq_status_ofs = 0x3c,
+ .irq_enable_ofs = 0x40,
+ .dma_enable_in = BIT(5),
+ .dma_enable_out = BIT(6),
+ .major_mask = 0x0700,
+ .major_shift = 8,
+ .minor_mask = 0x003f,
+ .minor_shift = 0,
+};
+
+static irqreturn_t omap_des_irq(int irq, void *dev_id)
+{
+ struct omap_des_dev *dd = dev_id;
+ u32 status, i;
+ u32 *src, *dst;
+
+ status = omap_des_read(dd, DES_REG_IRQ_STATUS(dd));
+ if (status & DES_REG_IRQ_DATA_IN) {
+ omap_des_write(dd, DES_REG_IRQ_ENABLE(dd), 0x0);
+
+ BUG_ON(!dd->in_sg);
+
+ BUG_ON(_calc_walked(in) > dd->in_sg->length);
+
+ src = sg_virt(dd->in_sg) + _calc_walked(in);
+
+ for (i = 0; i < DES_BLOCK_WORDS; i++) {
+ omap_des_write(dd, DES_REG_DATA_N(dd, i), *src);
+
+ scatterwalk_advance(&dd->in_walk, 4);
+ if (dd->in_sg->length == _calc_walked(in)) {
+ dd->in_sg = scatterwalk_sg_next(dd->in_sg);
+ if (dd->in_sg) {
+ scatterwalk_start(&dd->in_walk,
+ dd->in_sg);
+ src = sg_virt(dd->in_sg) +
+ _calc_walked(in);
+ }
+ } else {
+ src++;
+ }
+ }
+
+ /* Clear IRQ status */
+ status &= ~DES_REG_IRQ_DATA_IN;
+ omap_des_write(dd, DES_REG_IRQ_STATUS(dd), status);
+
+ /* Enable DATA_OUT interrupt */
+ omap_des_write(dd, DES_REG_IRQ_ENABLE(dd), 0x4);
+
+ } else if (status & DES_REG_IRQ_DATA_OUT) {
+ omap_des_write(dd, DES_REG_IRQ_ENABLE(dd), 0x0);
+
+ BUG_ON(!dd->out_sg);
+
+ BUG_ON(_calc_walked(out) > dd->out_sg->length);
+
+ dst = sg_virt(dd->out_sg) + _calc_walked(out);
+
+ for (i = 0; i < DES_BLOCK_WORDS; i++) {
+ *dst = omap_des_read(dd, DES_REG_DATA_N(dd, i));
+ scatterwalk_advance(&dd->out_walk, 4);
+ if (dd->out_sg->length == _calc_walked(out)) {
+ dd->out_sg = scatterwalk_sg_next(dd->out_sg);
+ if (dd->out_sg) {
+ scatterwalk_start(&dd->out_walk,
+ dd->out_sg);
+ dst = sg_virt(dd->out_sg) +
+ _calc_walked(out);
+ }
+ } else {
+ dst++;
+ }
+ }
+
+ dd->total -= DES_BLOCK_SIZE;
+
+ BUG_ON(dd->total < 0);
+
+ /* Clear IRQ status */
+ status &= ~DES_REG_IRQ_DATA_OUT;
+ omap_des_write(dd, DES_REG_IRQ_STATUS(dd), status);
+
+ if (!dd->total)
+ /* All bytes read! */
+ tasklet_schedule(&dd->done_task);
+ else
+ /* Enable DATA_IN interrupt for next block */
+ omap_des_write(dd, DES_REG_IRQ_ENABLE(dd), 0x2);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct of_device_id omap_des_of_match[] = {
+ {
+ .compatible = "ti,omap4-des",
+ .data = &omap_des_pdata_omap4,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_des_of_match);
+
+static int omap_des_get_of(struct omap_des_dev *dd,
+ struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(omap_des_of_match), &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "no compatible OF match\n");
+ return -EINVAL;
+ }
+
+ dd->dma_out = -1; /* Dummy value that's unused */
+ dd->dma_in = -1; /* Dummy value that's unused */
+ dd->pdata = match->data;
+
+ return 0;
+}
+#else
+static int omap_des_get_of(struct omap_des_dev *dd,
+ struct device *dev)
+{
+ return -EINVAL;
+}
+#endif
+
+static int omap_des_get_pdev(struct omap_des_dev *dd,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *r;
+ int err = 0;
+
+ /* Get the DMA out channel */
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r) {
+ dev_err(dev, "no DMA out resource info\n");
+ err = -ENODEV;
+ goto err;
+ }
+ dd->dma_out = r->start;
+
+ /* Get the DMA in channel */
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!r) {
+ dev_err(dev, "no DMA in resource info\n");
+ err = -ENODEV;
+ goto err;
+ }
+ dd->dma_in = r->start;
+
+ /* non-DT devices get pdata from pdev */
+ dd->pdata = pdev->dev.platform_data;
+
+err:
+ return err;
+}
+
+static int omap_des_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct omap_des_dev *dd;
+ struct crypto_alg *algp;
+ struct resource *res;
+ int err = -ENOMEM, i, j, irq = -1;
+ u32 reg;
+
+ dd = devm_kzalloc(dev, sizeof(struct omap_des_dev), GFP_KERNEL);
+ if (dd == NULL) {
+ dev_err(dev, "unable to alloc data struct.\n");
+ goto err_data;
+ }
+ dd->dev = dev;
+ platform_set_drvdata(pdev, dd);
+
+ spin_lock_init(&dd->lock);
+ crypto_init_queue(&dd->queue, OMAP_DES_QUEUE_LENGTH);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no MEM resource info\n");
+ goto err_res;
+ }
+
+ err = (dev->of_node) ? omap_des_get_of(dd, pdev) :
+ omap_des_get_pdev(dd, pdev);
+ if (err)
+ goto err_res;
+
+ dd->io_base = devm_request_and_ioremap(dev, res);
+ if (!dd->io_base) {
+ dev_err(dev, "can't ioremap\n");
+ err = -ENOMEM;
+ goto err_res;
+ }
+ dd->phys_base = res->start;
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ omap_des_dma_stop(dd);
+
+ reg = omap_des_read(dd, DES_REG_REV(dd));
+
+ pm_runtime_put_sync(dev);
+
+ dev_info(dev, "OMAP DES hw accel rev: %u.%u\n",
+ (reg & dd->pdata->major_mask) >> dd->pdata->major_shift,
+ (reg & dd->pdata->minor_mask) >> dd->pdata->minor_shift);
+
+ tasklet_init(&dd->done_task, omap_des_done_task, (unsigned long)dd);
+ tasklet_init(&dd->queue_task, omap_des_queue_task, (unsigned long)dd);
+
+ err = omap_des_dma_init(dd);
+ if (err && DES_REG_IRQ_STATUS(dd) && DES_REG_IRQ_ENABLE(dd)) {
+ dd->pio_only = 1;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "can't get IRQ resource\n");
+ goto err_irq;
+ }
+
+ err = devm_request_irq(dev, irq, omap_des_irq, 0,
+ dev_name(dev), dd);
+ if (err) {
+ dev_err(dev, "Unable to grab omap-des IRQ\n");
+ goto err_irq;
+ }
+ }
+
+
+ INIT_LIST_HEAD(&dd->list);
+ spin_lock(&list_lock);
+ list_add_tail(&dd->list, &dev_list);
+ spin_unlock(&list_lock);
+
+ for (i = 0; i < dd->pdata->algs_info_size; i++) {
+ for (j = 0; j < dd->pdata->algs_info[i].size; j++) {
+ algp = &dd->pdata->algs_info[i].algs_list[j];
+
+ pr_debug("reg alg: %s\n", algp->cra_name);
+ INIT_LIST_HEAD(&algp->cra_list);
+
+ err = crypto_register_alg(algp);
+ if (err)
+ goto err_algs;
+
+ dd->pdata->algs_info[i].registered++;
+ }
+ }
+
+ return 0;
+err_algs:
+ for (i = dd->pdata->algs_info_size - 1; i >= 0; i--)
+ for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--)
+ crypto_unregister_alg(
+ &dd->pdata->algs_info[i].algs_list[j]);
+ if (!dd->pio_only)
+ omap_des_dma_cleanup(dd);
+err_irq:
+ tasklet_kill(&dd->done_task);
+ tasklet_kill(&dd->queue_task);
+ pm_runtime_disable(dev);
+err_res:
+ dd = NULL;
+err_data:
+ dev_err(dev, "initialization failed.\n");
+ return err;
+}
+
+static int omap_des_remove(struct platform_device *pdev)
+{
+ struct omap_des_dev *dd = platform_get_drvdata(pdev);
+ int i, j;
+
+ if (!dd)
+ return -ENODEV;
+
+ spin_lock(&list_lock);
+ list_del(&dd->list);
+ spin_unlock(&list_lock);
+
+ for (i = dd->pdata->algs_info_size - 1; i >= 0; i--)
+ for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--)
+ crypto_unregister_alg(
+ &dd->pdata->algs_info[i].algs_list[j]);
+
+ tasklet_kill(&dd->done_task);
+ tasklet_kill(&dd->queue_task);
+ omap_des_dma_cleanup(dd);
+ pm_runtime_disable(dd->dev);
+ dd = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int omap_des_suspend(struct device *dev)
+{
+ pm_runtime_put_sync(dev);
+ return 0;
+}
+
+static int omap_des_resume(struct device *dev)
+{
+ pm_runtime_get_sync(dev);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(omap_des_pm_ops, omap_des_suspend, omap_des_resume);
+
+static struct platform_driver omap_des_driver = {
+ .probe = omap_des_probe,
+ .remove = omap_des_remove,
+ .driver = {
+ .name = "omap-des",
+ .owner = THIS_MODULE,
+ .pm = &omap_des_pm_ops,
+ .of_match_table = of_match_ptr(omap_des_of_match),
+ },
+};
+
+module_platform_driver(omap_des_driver);
+
+MODULE_DESCRIPTION("OMAP DES hw acceleration support.");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Joel Fernandes <joelf@ti.com>");
diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c
index a727a6a59653..710d86386965 100644
--- a/drivers/crypto/omap-sham.c
+++ b/drivers/crypto/omap-sham.c
@@ -636,11 +636,17 @@ static size_t omap_sham_append_buffer(struct omap_sham_reqctx *ctx,
static size_t omap_sham_append_sg(struct omap_sham_reqctx *ctx)
{
size_t count;
+ const u8 *vaddr;
while (ctx->sg) {
+ vaddr = kmap_atomic(sg_page(ctx->sg));
+
count = omap_sham_append_buffer(ctx,
- sg_virt(ctx->sg) + ctx->offset,
+ vaddr + ctx->offset,
ctx->sg->length - ctx->offset);
+
+ kunmap_atomic((void *)vaddr);
+
if (!count)
break;
ctx->offset += count;
@@ -2022,9 +2028,7 @@ static int omap_sham_resume(struct device *dev)
}
#endif
-static const struct dev_pm_ops omap_sham_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(omap_sham_suspend, omap_sham_resume)
-};
+static SIMPLE_DEV_PM_OPS(omap_sham_pm_ops, omap_sham_suspend, omap_sham_resume);
static struct platform_driver omap_sham_driver = {
.probe = omap_sham_probe,
diff --git a/drivers/crypto/picoxcell_crypto.c b/drivers/crypto/picoxcell_crypto.c
index a6175ba6d238..5da5b98b8f29 100644
--- a/drivers/crypto/picoxcell_crypto.c
+++ b/drivers/crypto/picoxcell_crypto.c
@@ -1720,22 +1720,16 @@ static int spacc_probe(struct platform_device *pdev)
engine->name = dev_name(&pdev->dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ engine->regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(engine->regs))
+ return PTR_ERR(engine->regs);
+
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!mem || !irq) {
+ if (!irq) {
dev_err(&pdev->dev, "no memory/irq resource for engine\n");
return -ENXIO;
}
- if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
- engine->name))
- return -ENOMEM;
-
- engine->regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- if (!engine->regs) {
- dev_err(&pdev->dev, "memory map failed\n");
- return -ENOMEM;
- }
-
if (devm_request_irq(&pdev->dev, irq->start, spacc_spacc_irq, 0,
engine->name, engine)) {
dev_err(engine->dev, "failed to request IRQ\n");
diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c
index cf149b19ff47..be45762f390a 100644
--- a/drivers/crypto/s5p-sss.c
+++ b/drivers/crypto/s5p-sss.c
@@ -568,17 +568,14 @@ static int s5p_aes_probe(struct platform_device *pdev)
if (s5p_dev)
return -EEXIST;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
- if (!devm_request_mem_region(dev, res->start,
- resource_size(res), pdev->name))
- return -EBUSY;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pdata->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pdata->ioaddr))
+ return PTR_ERR(pdata->ioaddr);
pdata->clk = devm_clk_get(dev, "secss");
if (IS_ERR(pdata->clk)) {
@@ -589,8 +586,6 @@ static int s5p_aes_probe(struct platform_device *pdev)
clk_enable(pdata->clk);
spin_lock_init(&pdata->lock);
- pdata->ioaddr = devm_ioremap(dev, res->start,
- resource_size(res));
pdata->irq_hash = platform_get_irq_byname(pdev, "hash");
if (pdata->irq_hash < 0) {
diff --git a/drivers/crypto/sahara.c b/drivers/crypto/sahara.c
index 785a9ded7bdf..07a5987ce67d 100644
--- a/drivers/crypto/sahara.c
+++ b/drivers/crypto/sahara.c
@@ -885,22 +885,9 @@ static int sahara_probe(struct platform_device *pdev)
/* Get the base address */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to get memory region resource\n");
- return -ENODEV;
- }
-
- if (devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), SAHARA_NAME) == NULL) {
- dev_err(&pdev->dev, "failed to request memory region\n");
- return -ENOENT;
- }
- dev->regs_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!dev->regs_base) {
- dev_err(&pdev->dev, "failed to ioremap address region\n");
- return -ENOENT;
- }
+ dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->regs_base))
+ return PTR_ERR(dev->regs_base);
/* Get the IRQ */
irq = platform_get_irq(pdev, 0);
@@ -909,10 +896,11 @@ static int sahara_probe(struct platform_device *pdev)
return irq;
}
- if (devm_request_irq(&pdev->dev, irq, sahara_irq_handler,
- 0, SAHARA_NAME, dev) < 0) {
+ err = devm_request_irq(&pdev->dev, irq, sahara_irq_handler,
+ 0, dev_name(&pdev->dev), dev);
+ if (err) {
dev_err(&pdev->dev, "failed to request irq\n");
- return -ENOENT;
+ return err;
}
/* clocks */
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 5967667e1a8f..624b8be0c365 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -2637,6 +2637,8 @@ static int talitos_probe(struct platform_device *ofdev)
if (!priv)
return -ENOMEM;
+ INIT_LIST_HEAD(&priv->alg_list);
+
dev_set_drvdata(dev, priv);
priv->ofdev = ofdev;
@@ -2657,8 +2659,6 @@ static int talitos_probe(struct platform_device *ofdev)
(unsigned long)dev);
}
- INIT_LIST_HEAD(&priv->alg_list);
-
priv->reg = of_iomap(np, 0);
if (!priv->reg) {
dev_err(dev, "failed to of_iomap\n");
diff --git a/drivers/crypto/tegra-aes.c b/drivers/crypto/tegra-aes.c
deleted file mode 100644
index 060eecc5dbc3..000000000000
--- a/drivers/crypto/tegra-aes.c
+++ /dev/null
@@ -1,1087 +0,0 @@
-/*
- * drivers/crypto/tegra-aes.c
- *
- * Driver for NVIDIA Tegra AES hardware engine residing inside the
- * Bit Stream Engine for Video (BSEV) hardware block.
- *
- * The programming sequence for this engine is with the help
- * of commands which travel via a command queue residing between the
- * CPU and the BSEV block. The BSEV engine has an internal RAM (VRAM)
- * where the final input plaintext, keys and the IV have to be copied
- * before starting the encrypt/decrypt operation.
- *
- * Copyright (c) 2010, NVIDIA Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/scatterlist.h>
-#include <linux/dma-mapping.h>
-#include <linux/io.h>
-#include <linux/mutex.h>
-#include <linux/interrupt.h>
-#include <linux/completion.h>
-#include <linux/workqueue.h>
-
-#include <crypto/scatterwalk.h>
-#include <crypto/aes.h>
-#include <crypto/internal/rng.h>
-
-#include "tegra-aes.h"
-
-#define FLAGS_MODE_MASK 0x00FF
-#define FLAGS_ENCRYPT BIT(0)
-#define FLAGS_CBC BIT(1)
-#define FLAGS_GIV BIT(2)
-#define FLAGS_RNG BIT(3)
-#define FLAGS_OFB BIT(4)
-#define FLAGS_NEW_KEY BIT(5)
-#define FLAGS_NEW_IV BIT(6)
-#define FLAGS_INIT BIT(7)
-#define FLAGS_FAST BIT(8)
-#define FLAGS_BUSY 9
-
-/*
- * Defines AES engine Max process bytes size in one go, which takes 1 msec.
- * AES engine spends about 176 cycles/16-bytes or 11 cycles/byte
- * The duration CPU can use the BSE to 1 msec, then the number of available
- * cycles of AVP/BSE is 216K. In this duration, AES can process 216/11 ~= 19KB
- * Based on this AES_HW_DMA_BUFFER_SIZE_BYTES is configured to 16KB.
- */
-#define AES_HW_DMA_BUFFER_SIZE_BYTES 0x4000
-
-/*
- * The key table length is 64 bytes
- * (This includes first upto 32 bytes key + 16 bytes original initial vector
- * and 16 bytes updated initial vector)
- */
-#define AES_HW_KEY_TABLE_LENGTH_BYTES 64
-
-/*
- * The memory being used is divides as follows:
- * 1. Key - 32 bytes
- * 2. Original IV - 16 bytes
- * 3. Updated IV - 16 bytes
- * 4. Key schedule - 256 bytes
- *
- * 1+2+3 constitute the hw key table.
- */
-#define AES_HW_IV_SIZE 16
-#define AES_HW_KEYSCHEDULE_LEN 256
-#define AES_IVKEY_SIZE (AES_HW_KEY_TABLE_LENGTH_BYTES + AES_HW_KEYSCHEDULE_LEN)
-
-/* Define commands required for AES operation */
-enum {
- CMD_BLKSTARTENGINE = 0x0E,
- CMD_DMASETUP = 0x10,
- CMD_DMACOMPLETE = 0x11,
- CMD_SETTABLE = 0x15,
- CMD_MEMDMAVD = 0x22,
-};
-
-/* Define sub-commands */
-enum {
- SUBCMD_VRAM_SEL = 0x1,
- SUBCMD_CRYPTO_TABLE_SEL = 0x3,
- SUBCMD_KEY_TABLE_SEL = 0x8,
-};
-
-/* memdma_vd command */
-#define MEMDMA_DIR_DTOVRAM 0 /* sdram -> vram */
-#define MEMDMA_DIR_VTODRAM 1 /* vram -> sdram */
-#define MEMDMA_DIR_SHIFT 25
-#define MEMDMA_NUM_WORDS_SHIFT 12
-
-/* command queue bit shifts */
-enum {
- CMDQ_KEYTABLEADDR_SHIFT = 0,
- CMDQ_KEYTABLEID_SHIFT = 17,
- CMDQ_VRAMSEL_SHIFT = 23,
- CMDQ_TABLESEL_SHIFT = 24,
- CMDQ_OPCODE_SHIFT = 26,
-};
-
-/*
- * The secure key slot contains a unique secure key generated
- * and loaded by the bootloader. This slot is marked as non-accessible
- * to the kernel.
- */
-#define SSK_SLOT_NUM 4
-
-#define AES_NR_KEYSLOTS 8
-#define TEGRA_AES_QUEUE_LENGTH 50
-#define DEFAULT_RNG_BLK_SZ 16
-
-/* The command queue depth */
-#define AES_HW_MAX_ICQ_LENGTH 5
-
-struct tegra_aes_slot {
- struct list_head node;
- int slot_num;
-};
-
-static struct tegra_aes_slot ssk = {
- .slot_num = SSK_SLOT_NUM,
-};
-
-struct tegra_aes_reqctx {
- unsigned long mode;
-};
-
-struct tegra_aes_dev {
- struct device *dev;
- void __iomem *io_base;
- dma_addr_t ivkey_phys_base;
- void __iomem *ivkey_base;
- struct clk *aes_clk;
- struct tegra_aes_ctx *ctx;
- int irq;
- unsigned long flags;
- struct completion op_complete;
- u32 *buf_in;
- dma_addr_t dma_buf_in;
- u32 *buf_out;
- dma_addr_t dma_buf_out;
- u8 *iv;
- u8 dt[DEFAULT_RNG_BLK_SZ];
- int ivlen;
- u64 ctr;
- spinlock_t lock;
- struct crypto_queue queue;
- struct tegra_aes_slot *slots;
- struct ablkcipher_request *req;
- size_t total;
- struct scatterlist *in_sg;
- size_t in_offset;
- struct scatterlist *out_sg;
- size_t out_offset;
-};
-
-static struct tegra_aes_dev *aes_dev;
-
-struct tegra_aes_ctx {
- struct tegra_aes_dev *dd;
- unsigned long flags;
- struct tegra_aes_slot *slot;
- u8 key[AES_MAX_KEY_SIZE];
- size_t keylen;
-};
-
-static struct tegra_aes_ctx rng_ctx = {
- .flags = FLAGS_NEW_KEY,
- .keylen = AES_KEYSIZE_128,
-};
-
-/* keep registered devices data here */
-static struct list_head dev_list;
-static DEFINE_SPINLOCK(list_lock);
-static DEFINE_MUTEX(aes_lock);
-
-static void aes_workqueue_handler(struct work_struct *work);
-static DECLARE_WORK(aes_work, aes_workqueue_handler);
-static struct workqueue_struct *aes_wq;
-
-static inline u32 aes_readl(struct tegra_aes_dev *dd, u32 offset)
-{
- return readl(dd->io_base + offset);
-}
-
-static inline void aes_writel(struct tegra_aes_dev *dd, u32 val, u32 offset)
-{
- writel(val, dd->io_base + offset);
-}
-
-static int aes_start_crypt(struct tegra_aes_dev *dd, u32 in_addr, u32 out_addr,
- int nblocks, int mode, bool upd_iv)
-{
- u32 cmdq[AES_HW_MAX_ICQ_LENGTH];
- int i, eng_busy, icq_empty, ret;
- u32 value;
-
- /* reset all the interrupt bits */
- aes_writel(dd, 0xFFFFFFFF, TEGRA_AES_INTR_STATUS);
-
- /* enable error, dma xfer complete interrupts */
- aes_writel(dd, 0x33, TEGRA_AES_INT_ENB);
-
- cmdq[0] = CMD_DMASETUP << CMDQ_OPCODE_SHIFT;
- cmdq[1] = in_addr;
- cmdq[2] = CMD_BLKSTARTENGINE << CMDQ_OPCODE_SHIFT | (nblocks-1);
- cmdq[3] = CMD_DMACOMPLETE << CMDQ_OPCODE_SHIFT;
-
- value = aes_readl(dd, TEGRA_AES_CMDQUE_CONTROL);
- /* access SDRAM through AHB */
- value &= ~TEGRA_AES_CMDQ_CTRL_SRC_STM_SEL_FIELD;
- value &= ~TEGRA_AES_CMDQ_CTRL_DST_STM_SEL_FIELD;
- value |= TEGRA_AES_CMDQ_CTRL_SRC_STM_SEL_FIELD |
- TEGRA_AES_CMDQ_CTRL_DST_STM_SEL_FIELD |
- TEGRA_AES_CMDQ_CTRL_ICMDQEN_FIELD;
- aes_writel(dd, value, TEGRA_AES_CMDQUE_CONTROL);
- dev_dbg(dd->dev, "cmd_q_ctrl=0x%x", value);
-
- value = (0x1 << TEGRA_AES_SECURE_INPUT_ALG_SEL_SHIFT) |
- ((dd->ctx->keylen * 8) <<
- TEGRA_AES_SECURE_INPUT_KEY_LEN_SHIFT) |
- ((u32)upd_iv << TEGRA_AES_SECURE_IV_SELECT_SHIFT);
-
- if (mode & FLAGS_CBC) {
- value |= ((((mode & FLAGS_ENCRYPT) ? 2 : 3)
- << TEGRA_AES_SECURE_XOR_POS_SHIFT) |
- (((mode & FLAGS_ENCRYPT) ? 2 : 3)
- << TEGRA_AES_SECURE_VCTRAM_SEL_SHIFT) |
- ((mode & FLAGS_ENCRYPT) ? 1 : 0)
- << TEGRA_AES_SECURE_CORE_SEL_SHIFT);
- } else if (mode & FLAGS_OFB) {
- value |= ((TEGRA_AES_SECURE_XOR_POS_FIELD) |
- (2 << TEGRA_AES_SECURE_INPUT_SEL_SHIFT) |
- (TEGRA_AES_SECURE_CORE_SEL_FIELD));
- } else if (mode & FLAGS_RNG) {
- value |= (((mode & FLAGS_ENCRYPT) ? 1 : 0)
- << TEGRA_AES_SECURE_CORE_SEL_SHIFT |
- TEGRA_AES_SECURE_RNG_ENB_FIELD);
- } else {
- value |= (((mode & FLAGS_ENCRYPT) ? 1 : 0)
- << TEGRA_AES_SECURE_CORE_SEL_SHIFT);
- }
-
- dev_dbg(dd->dev, "secure_in_sel=0x%x", value);
- aes_writel(dd, value, TEGRA_AES_SECURE_INPUT_SELECT);
-
- aes_writel(dd, out_addr, TEGRA_AES_SECURE_DEST_ADDR);
- reinit_completion(&dd->op_complete);
-
- for (i = 0; i < AES_HW_MAX_ICQ_LENGTH - 1; i++) {
- do {
- value = aes_readl(dd, TEGRA_AES_INTR_STATUS);
- eng_busy = value & TEGRA_AES_ENGINE_BUSY_FIELD;
- icq_empty = value & TEGRA_AES_ICQ_EMPTY_FIELD;
- } while (eng_busy && !icq_empty);
- aes_writel(dd, cmdq[i], TEGRA_AES_ICMDQUE_WR);
- }
-
- ret = wait_for_completion_timeout(&dd->op_complete,
- msecs_to_jiffies(150));
- if (ret == 0) {
- dev_err(dd->dev, "timed out (0x%x)\n",
- aes_readl(dd, TEGRA_AES_INTR_STATUS));
- return -ETIMEDOUT;
- }
-
- aes_writel(dd, cmdq[AES_HW_MAX_ICQ_LENGTH - 1], TEGRA_AES_ICMDQUE_WR);
- return 0;
-}
-
-static void aes_release_key_slot(struct tegra_aes_slot *slot)
-{
- if (slot->slot_num == SSK_SLOT_NUM)
- return;
-
- spin_lock(&list_lock);
- list_add_tail(&slot->node, &dev_list);
- slot = NULL;
- spin_unlock(&list_lock);
-}
-
-static struct tegra_aes_slot *aes_find_key_slot(void)
-{
- struct tegra_aes_slot *slot = NULL;
- struct list_head *new_head;
- int empty;
-
- spin_lock(&list_lock);
- empty = list_empty(&dev_list);
- if (!empty) {
- slot = list_entry(&dev_list, struct tegra_aes_slot, node);
- new_head = dev_list.next;
- list_del(&dev_list);
- dev_list.next = new_head->next;
- dev_list.prev = NULL;
- }
- spin_unlock(&list_lock);
-
- return slot;
-}
-
-static int aes_set_key(struct tegra_aes_dev *dd)
-{
- u32 value, cmdq[2];
- struct tegra_aes_ctx *ctx = dd->ctx;
- int eng_busy, icq_empty, dma_busy;
- bool use_ssk = false;
-
- /* use ssk? */
- if (!dd->ctx->slot) {
- dev_dbg(dd->dev, "using ssk");
- dd->ctx->slot = &ssk;
- use_ssk = true;
- }
-
- /* enable key schedule generation in hardware */
- value = aes_readl(dd, TEGRA_AES_SECURE_CONFIG_EXT);
- value &= ~TEGRA_AES_SECURE_KEY_SCH_DIS_FIELD;
- aes_writel(dd, value, TEGRA_AES_SECURE_CONFIG_EXT);
-
- /* select the key slot */
- value = aes_readl(dd, TEGRA_AES_SECURE_CONFIG);
- value &= ~TEGRA_AES_SECURE_KEY_INDEX_FIELD;
- value |= (ctx->slot->slot_num << TEGRA_AES_SECURE_KEY_INDEX_SHIFT);
- aes_writel(dd, value, TEGRA_AES_SECURE_CONFIG);
-
- if (use_ssk)
- return 0;
-
- /* copy the key table from sdram to vram */
- cmdq[0] = CMD_MEMDMAVD << CMDQ_OPCODE_SHIFT |
- MEMDMA_DIR_DTOVRAM << MEMDMA_DIR_SHIFT |
- AES_HW_KEY_TABLE_LENGTH_BYTES / sizeof(u32) <<
- MEMDMA_NUM_WORDS_SHIFT;
- cmdq[1] = (u32)dd->ivkey_phys_base;
-
- aes_writel(dd, cmdq[0], TEGRA_AES_ICMDQUE_WR);
- aes_writel(dd, cmdq[1], TEGRA_AES_ICMDQUE_WR);
-
- do {
- value = aes_readl(dd, TEGRA_AES_INTR_STATUS);
- eng_busy = value & TEGRA_AES_ENGINE_BUSY_FIELD;
- icq_empty = value & TEGRA_AES_ICQ_EMPTY_FIELD;
- dma_busy = value & TEGRA_AES_DMA_BUSY_FIELD;
- } while (eng_busy && !icq_empty && dma_busy);
-
- /* settable command to get key into internal registers */
- value = CMD_SETTABLE << CMDQ_OPCODE_SHIFT |
- SUBCMD_CRYPTO_TABLE_SEL << CMDQ_TABLESEL_SHIFT |
- SUBCMD_VRAM_SEL << CMDQ_VRAMSEL_SHIFT |
- (SUBCMD_KEY_TABLE_SEL | ctx->slot->slot_num) <<
- CMDQ_KEYTABLEID_SHIFT;
- aes_writel(dd, value, TEGRA_AES_ICMDQUE_WR);
-
- do {
- value = aes_readl(dd, TEGRA_AES_INTR_STATUS);
- eng_busy = value & TEGRA_AES_ENGINE_BUSY_FIELD;
- icq_empty = value & TEGRA_AES_ICQ_EMPTY_FIELD;
- } while (eng_busy && !icq_empty);
-
- return 0;
-}
-
-static int tegra_aes_handle_req(struct tegra_aes_dev *dd)
-{
- struct crypto_async_request *async_req, *backlog;
- struct crypto_ablkcipher *tfm;
- struct tegra_aes_ctx *ctx;
- struct tegra_aes_reqctx *rctx;
- struct ablkcipher_request *req;
- unsigned long flags;
- int dma_max = AES_HW_DMA_BUFFER_SIZE_BYTES;
- int ret = 0, nblocks, total;
- int count = 0;
- dma_addr_t addr_in, addr_out;
- struct scatterlist *in_sg, *out_sg;
-
- if (!dd)
- return -EINVAL;
-
- spin_lock_irqsave(&dd->lock, flags);
- backlog = crypto_get_backlog(&dd->queue);
- async_req = crypto_dequeue_request(&dd->queue);
- if (!async_req)
- clear_bit(FLAGS_BUSY, &dd->flags);
- spin_unlock_irqrestore(&dd->lock, flags);
-
- if (!async_req)
- return -ENODATA;
-
- if (backlog)
- backlog->complete(backlog, -EINPROGRESS);
-
- req = ablkcipher_request_cast(async_req);
-
- dev_dbg(dd->dev, "%s: get new req\n", __func__);
-
- if (!req->src || !req->dst)
- return -EINVAL;
-
- /* take mutex to access the aes hw */
- mutex_lock(&aes_lock);
-
- /* 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;
-
- in_sg = dd->in_sg;
- out_sg = dd->out_sg;
-
- total = dd->total;
-
- tfm = crypto_ablkcipher_reqtfm(req);
- rctx = ablkcipher_request_ctx(req);
- ctx = crypto_ablkcipher_ctx(tfm);
- rctx->mode &= FLAGS_MODE_MASK;
- dd->flags = (dd->flags & ~FLAGS_MODE_MASK) | rctx->mode;
-
- dd->iv = (u8 *)req->info;
- dd->ivlen = crypto_ablkcipher_ivsize(tfm);
-
- /* assign new context to device */
- ctx->dd = dd;
- dd->ctx = ctx;
-
- if (ctx->flags & FLAGS_NEW_KEY) {
- /* copy the key */
- memcpy(dd->ivkey_base, ctx->key, ctx->keylen);
- memset(dd->ivkey_base + ctx->keylen, 0, AES_HW_KEY_TABLE_LENGTH_BYTES - ctx->keylen);
- aes_set_key(dd);
- ctx->flags &= ~FLAGS_NEW_KEY;
- }
-
- if (((dd->flags & FLAGS_CBC) || (dd->flags & FLAGS_OFB)) && dd->iv) {
- /* set iv to the aes hw slot
- * Hw generates updated iv only after iv is set in slot.
- * So key and iv is passed asynchronously.
- */
- memcpy(dd->buf_in, dd->iv, dd->ivlen);
-
- ret = aes_start_crypt(dd, (u32)dd->dma_buf_in,
- dd->dma_buf_out, 1, FLAGS_CBC, false);
- if (ret < 0) {
- dev_err(dd->dev, "aes_start_crypt fail(%d)\n", ret);
- goto out;
- }
- }
-
- while (total) {
- dev_dbg(dd->dev, "remain: %d\n", total);
- ret = dma_map_sg(dd->dev, in_sg, 1, DMA_TO_DEVICE);
- if (!ret) {
- dev_err(dd->dev, "dma_map_sg() error\n");
- goto out;
- }
-
- ret = dma_map_sg(dd->dev, out_sg, 1, DMA_FROM_DEVICE);
- if (!ret) {
- dev_err(dd->dev, "dma_map_sg() error\n");
- dma_unmap_sg(dd->dev, dd->in_sg,
- 1, DMA_TO_DEVICE);
- goto out;
- }
-
- addr_in = sg_dma_address(in_sg);
- addr_out = sg_dma_address(out_sg);
- dd->flags |= FLAGS_FAST;
- count = min_t(int, sg_dma_len(in_sg), dma_max);
- WARN_ON(sg_dma_len(in_sg) != sg_dma_len(out_sg));
- nblocks = DIV_ROUND_UP(count, AES_BLOCK_SIZE);
-
- ret = aes_start_crypt(dd, addr_in, addr_out, nblocks,
- dd->flags, true);
-
- dma_unmap_sg(dd->dev, out_sg, 1, DMA_FROM_DEVICE);
- dma_unmap_sg(dd->dev, in_sg, 1, DMA_TO_DEVICE);
-
- if (ret < 0) {
- dev_err(dd->dev, "aes_start_crypt fail(%d)\n", ret);
- goto out;
- }
- dd->flags &= ~FLAGS_FAST;
-
- dev_dbg(dd->dev, "out: copied %d\n", count);
- total -= count;
- in_sg = sg_next(in_sg);
- out_sg = sg_next(out_sg);
- WARN_ON(((total != 0) && (!in_sg || !out_sg)));
- }
-
-out:
- mutex_unlock(&aes_lock);
-
- dd->total = total;
-
- if (dd->req->base.complete)
- dd->req->base.complete(&dd->req->base, ret);
-
- dev_dbg(dd->dev, "%s: exit\n", __func__);
- return ret;
-}
-
-static int tegra_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
- unsigned int keylen)
-{
- struct tegra_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm);
- struct tegra_aes_dev *dd = aes_dev;
- struct tegra_aes_slot *key_slot;
-
- if ((keylen != AES_KEYSIZE_128) && (keylen != AES_KEYSIZE_192) &&
- (keylen != AES_KEYSIZE_256)) {
- dev_err(dd->dev, "unsupported key size\n");
- crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
- return -EINVAL;
- }
-
- dev_dbg(dd->dev, "keylen: %d\n", keylen);
-
- ctx->dd = dd;
-
- if (key) {
- if (!ctx->slot) {
- key_slot = aes_find_key_slot();
- if (!key_slot) {
- dev_err(dd->dev, "no empty slot\n");
- return -ENOMEM;
- }
-
- ctx->slot = key_slot;
- }
-
- memcpy(ctx->key, key, keylen);
- ctx->keylen = keylen;
- }
-
- ctx->flags |= FLAGS_NEW_KEY;
- dev_dbg(dd->dev, "done\n");
- return 0;
-}
-
-static void aes_workqueue_handler(struct work_struct *work)
-{
- struct tegra_aes_dev *dd = aes_dev;
- int ret;
-
- ret = clk_prepare_enable(dd->aes_clk);
- if (ret)
- BUG_ON("clock enable failed");
-
- /* empty the crypto queue and then return */
- do {
- ret = tegra_aes_handle_req(dd);
- } while (!ret);
-
- clk_disable_unprepare(dd->aes_clk);
-}
-
-static irqreturn_t aes_irq(int irq, void *dev_id)
-{
- struct tegra_aes_dev *dd = (struct tegra_aes_dev *)dev_id;
- u32 value = aes_readl(dd, TEGRA_AES_INTR_STATUS);
- int busy = test_bit(FLAGS_BUSY, &dd->flags);
-
- if (!busy) {
- dev_dbg(dd->dev, "spurious interrupt\n");
- return IRQ_NONE;
- }
-
- dev_dbg(dd->dev, "irq_stat: 0x%x\n", value);
- if (value & TEGRA_AES_INT_ERROR_MASK)
- aes_writel(dd, TEGRA_AES_INT_ERROR_MASK, TEGRA_AES_INTR_STATUS);
-
- if (!(value & TEGRA_AES_ENGINE_BUSY_FIELD))
- complete(&dd->op_complete);
- else
- return IRQ_NONE;
-
- return IRQ_HANDLED;
-}
-
-static int tegra_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
-{
- struct tegra_aes_reqctx *rctx = ablkcipher_request_ctx(req);
- struct tegra_aes_dev *dd = aes_dev;
- unsigned long flags;
- int err = 0;
- int busy;
-
- dev_dbg(dd->dev, "nbytes: %d, enc: %d, cbc: %d, ofb: %d\n",
- req->nbytes, !!(mode & FLAGS_ENCRYPT),
- !!(mode & FLAGS_CBC), !!(mode & FLAGS_OFB));
-
- rctx->mode = mode;
-
- spin_lock_irqsave(&dd->lock, flags);
- err = ablkcipher_enqueue_request(&dd->queue, req);
- busy = test_and_set_bit(FLAGS_BUSY, &dd->flags);
- spin_unlock_irqrestore(&dd->lock, flags);
-
- if (!busy)
- queue_work(aes_wq, &aes_work);
-
- return err;
-}
-
-static int tegra_aes_ecb_encrypt(struct ablkcipher_request *req)
-{
- return tegra_aes_crypt(req, FLAGS_ENCRYPT);
-}
-
-static int tegra_aes_ecb_decrypt(struct ablkcipher_request *req)
-{
- return tegra_aes_crypt(req, 0);
-}
-
-static int tegra_aes_cbc_encrypt(struct ablkcipher_request *req)
-{
- return tegra_aes_crypt(req, FLAGS_ENCRYPT | FLAGS_CBC);
-}
-
-static int tegra_aes_cbc_decrypt(struct ablkcipher_request *req)
-{
- return tegra_aes_crypt(req, FLAGS_CBC);
-}
-
-static int tegra_aes_ofb_encrypt(struct ablkcipher_request *req)
-{
- return tegra_aes_crypt(req, FLAGS_ENCRYPT | FLAGS_OFB);
-}
-
-static int tegra_aes_ofb_decrypt(struct ablkcipher_request *req)
-{
- return tegra_aes_crypt(req, FLAGS_OFB);
-}
-
-static int tegra_aes_get_random(struct crypto_rng *tfm, u8 *rdata,
- unsigned int dlen)
-{
- struct tegra_aes_dev *dd = aes_dev;
- struct tegra_aes_ctx *ctx = &rng_ctx;
- int ret, i;
- u8 *dest = rdata, *dt = dd->dt;
-
- /* take mutex to access the aes hw */
- mutex_lock(&aes_lock);
-
- ret = clk_prepare_enable(dd->aes_clk);
- if (ret) {
- mutex_unlock(&aes_lock);
- return ret;
- }
-
- ctx->dd = dd;
- dd->ctx = ctx;
- dd->flags = FLAGS_ENCRYPT | FLAGS_RNG;
-
- memcpy(dd->buf_in, dt, DEFAULT_RNG_BLK_SZ);
-
- ret = aes_start_crypt(dd, (u32)dd->dma_buf_in,
- (u32)dd->dma_buf_out, 1, dd->flags, true);
- if (ret < 0) {
- dev_err(dd->dev, "aes_start_crypt fail(%d)\n", ret);
- dlen = ret;
- goto out;
- }
- memcpy(dest, dd->buf_out, dlen);
-
- /* update the DT */
- for (i = DEFAULT_RNG_BLK_SZ - 1; i >= 0; i--) {
- dt[i] += 1;
- if (dt[i] != 0)
- break;
- }
-
-out:
- clk_disable_unprepare(dd->aes_clk);
- mutex_unlock(&aes_lock);
-
- dev_dbg(dd->dev, "%s: done\n", __func__);
- return dlen;
-}
-
-static int tegra_aes_rng_reset(struct crypto_rng *tfm, u8 *seed,
- unsigned int slen)
-{
- struct tegra_aes_dev *dd = aes_dev;
- struct tegra_aes_ctx *ctx = &rng_ctx;
- struct tegra_aes_slot *key_slot;
- int ret = 0;
- u8 tmp[16]; /* 16 bytes = 128 bits of entropy */
- u8 *dt;
-
- if (!ctx || !dd) {
- pr_err("ctx=0x%x, dd=0x%x\n",
- (unsigned int)ctx, (unsigned int)dd);
- return -EINVAL;
- }
-
- if (slen < (DEFAULT_RNG_BLK_SZ + AES_KEYSIZE_128)) {
- dev_err(dd->dev, "seed size invalid");
- return -ENOMEM;
- }
-
- /* take mutex to access the aes hw */
- mutex_lock(&aes_lock);
-
- if (!ctx->slot) {
- key_slot = aes_find_key_slot();
- if (!key_slot) {
- dev_err(dd->dev, "no empty slot\n");
- mutex_unlock(&aes_lock);
- return -ENOMEM;
- }
- ctx->slot = key_slot;
- }
-
- ctx->dd = dd;
- dd->ctx = ctx;
- dd->ctr = 0;
-
- ctx->keylen = AES_KEYSIZE_128;
- ctx->flags |= FLAGS_NEW_KEY;
-
- /* copy the key to the key slot */
- memcpy(dd->ivkey_base, seed + DEFAULT_RNG_BLK_SZ, AES_KEYSIZE_128);
- memset(dd->ivkey_base + AES_KEYSIZE_128, 0, AES_HW_KEY_TABLE_LENGTH_BYTES - AES_KEYSIZE_128);
-
- dd->iv = seed;
- dd->ivlen = slen;
-
- dd->flags = FLAGS_ENCRYPT | FLAGS_RNG;
-
- ret = clk_prepare_enable(dd->aes_clk);
- if (ret) {
- mutex_unlock(&aes_lock);
- return ret;
- }
-
- aes_set_key(dd);
-
- /* set seed to the aes hw slot */
- memcpy(dd->buf_in, dd->iv, DEFAULT_RNG_BLK_SZ);
- ret = aes_start_crypt(dd, (u32)dd->dma_buf_in,
- dd->dma_buf_out, 1, FLAGS_CBC, false);
- if (ret < 0) {
- dev_err(dd->dev, "aes_start_crypt fail(%d)\n", ret);
- goto out;
- }
-
- if (dd->ivlen >= (2 * DEFAULT_RNG_BLK_SZ + AES_KEYSIZE_128)) {
- dt = dd->iv + DEFAULT_RNG_BLK_SZ + AES_KEYSIZE_128;
- } else {
- get_random_bytes(tmp, sizeof(tmp));
- dt = tmp;
- }
- memcpy(dd->dt, dt, DEFAULT_RNG_BLK_SZ);
-
-out:
- clk_disable_unprepare(dd->aes_clk);
- mutex_unlock(&aes_lock);
-
- dev_dbg(dd->dev, "%s: done\n", __func__);
- return ret;
-}
-
-static int tegra_aes_cra_init(struct crypto_tfm *tfm)
-{
- tfm->crt_ablkcipher.reqsize = sizeof(struct tegra_aes_reqctx);
-
- return 0;
-}
-
-static void tegra_aes_cra_exit(struct crypto_tfm *tfm)
-{
- struct tegra_aes_ctx *ctx =
- crypto_ablkcipher_ctx((struct crypto_ablkcipher *)tfm);
-
- if (ctx && ctx->slot)
- aes_release_key_slot(ctx->slot);
-}
-
-static struct crypto_alg algs[] = {
- {
- .cra_name = "ecb(aes)",
- .cra_driver_name = "ecb-aes-tegra",
- .cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
- .cra_blocksize = AES_BLOCK_SIZE,
- .cra_alignmask = 3,
- .cra_type = &crypto_ablkcipher_type,
- .cra_u.ablkcipher = {
- .min_keysize = AES_MIN_KEY_SIZE,
- .max_keysize = AES_MAX_KEY_SIZE,
- .setkey = tegra_aes_setkey,
- .encrypt = tegra_aes_ecb_encrypt,
- .decrypt = tegra_aes_ecb_decrypt,
- },
- }, {
- .cra_name = "cbc(aes)",
- .cra_driver_name = "cbc-aes-tegra",
- .cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
- .cra_blocksize = AES_BLOCK_SIZE,
- .cra_alignmask = 3,
- .cra_type = &crypto_ablkcipher_type,
- .cra_u.ablkcipher = {
- .min_keysize = AES_MIN_KEY_SIZE,
- .max_keysize = AES_MAX_KEY_SIZE,
- .ivsize = AES_MIN_KEY_SIZE,
- .setkey = tegra_aes_setkey,
- .encrypt = tegra_aes_cbc_encrypt,
- .decrypt = tegra_aes_cbc_decrypt,
- }
- }, {
- .cra_name = "ofb(aes)",
- .cra_driver_name = "ofb-aes-tegra",
- .cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
- .cra_blocksize = AES_BLOCK_SIZE,
- .cra_alignmask = 3,
- .cra_type = &crypto_ablkcipher_type,
- .cra_u.ablkcipher = {
- .min_keysize = AES_MIN_KEY_SIZE,
- .max_keysize = AES_MAX_KEY_SIZE,
- .ivsize = AES_MIN_KEY_SIZE,
- .setkey = tegra_aes_setkey,
- .encrypt = tegra_aes_ofb_encrypt,
- .decrypt = tegra_aes_ofb_decrypt,
- }
- }, {
- .cra_name = "ansi_cprng",
- .cra_driver_name = "rng-aes-tegra",
- .cra_flags = CRYPTO_ALG_TYPE_RNG,
- .cra_ctxsize = sizeof(struct tegra_aes_ctx),
- .cra_type = &crypto_rng_type,
- .cra_u.rng = {
- .rng_make_random = tegra_aes_get_random,
- .rng_reset = tegra_aes_rng_reset,
- .seedsize = AES_KEYSIZE_128 + (2 * DEFAULT_RNG_BLK_SZ),
- }
- }
-};
-
-static int tegra_aes_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct tegra_aes_dev *dd;
- struct resource *res;
- int err = -ENOMEM, i = 0, j;
-
- dd = devm_kzalloc(dev, sizeof(struct tegra_aes_dev), GFP_KERNEL);
- if (dd == NULL) {
- dev_err(dev, "unable to alloc data struct.\n");
- return err;
- }
-
- dd->dev = dev;
- platform_set_drvdata(pdev, dd);
-
- dd->slots = devm_kzalloc(dev, sizeof(struct tegra_aes_slot) *
- AES_NR_KEYSLOTS, GFP_KERNEL);
- if (dd->slots == NULL) {
- dev_err(dev, "unable to alloc slot struct.\n");
- goto out;
- }
-
- spin_lock_init(&dd->lock);
- crypto_init_queue(&dd->queue, TEGRA_AES_QUEUE_LENGTH);
-
- /* Get the module base address */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "invalid resource type: base\n");
- err = -ENODEV;
- goto out;
- }
-
- if (!devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res),
- dev_name(&pdev->dev))) {
- dev_err(&pdev->dev, "Couldn't request MEM resource\n");
- return -ENODEV;
- }
-
- dd->io_base = devm_ioremap(dev, res->start, resource_size(res));
- if (!dd->io_base) {
- dev_err(dev, "can't ioremap register space\n");
- err = -ENOMEM;
- goto out;
- }
-
- /* Initialize the vde clock */
- dd->aes_clk = devm_clk_get(dev, "vde");
- if (IS_ERR(dd->aes_clk)) {
- dev_err(dev, "iclock intialization failed.\n");
- err = -ENODEV;
- goto out;
- }
-
- err = clk_set_rate(dd->aes_clk, ULONG_MAX);
- if (err) {
- dev_err(dd->dev, "iclk set_rate fail(%d)\n", err);
- goto out;
- }
-
- /*
- * the foll contiguous memory is allocated as follows -
- * - hardware key table
- * - key schedule
- */
- dd->ivkey_base = dma_alloc_coherent(dev, AES_HW_KEY_TABLE_LENGTH_BYTES,
- &dd->ivkey_phys_base,
- GFP_KERNEL);
- if (!dd->ivkey_base) {
- dev_err(dev, "can not allocate iv/key buffer\n");
- err = -ENOMEM;
- goto out;
- }
-
- dd->buf_in = dma_alloc_coherent(dev, AES_HW_DMA_BUFFER_SIZE_BYTES,
- &dd->dma_buf_in, GFP_KERNEL);
- if (!dd->buf_in) {
- dev_err(dev, "can not allocate dma-in buffer\n");
- err = -ENOMEM;
- goto out;
- }
-
- dd->buf_out = dma_alloc_coherent(dev, AES_HW_DMA_BUFFER_SIZE_BYTES,
- &dd->dma_buf_out, GFP_KERNEL);
- if (!dd->buf_out) {
- dev_err(dev, "can not allocate dma-out buffer\n");
- err = -ENOMEM;
- goto out;
- }
-
- init_completion(&dd->op_complete);
- aes_wq = alloc_workqueue("tegra_aes_wq", WQ_HIGHPRI | WQ_UNBOUND, 1);
- if (!aes_wq) {
- dev_err(dev, "alloc_workqueue failed\n");
- err = -ENOMEM;
- goto out;
- }
-
- /* get the irq */
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "invalid resource type: base\n");
- err = -ENODEV;
- goto out;
- }
- dd->irq = res->start;
-
- err = devm_request_irq(dev, dd->irq, aes_irq, IRQF_TRIGGER_HIGH |
- IRQF_SHARED, "tegra-aes", dd);
- if (err) {
- dev_err(dev, "request_irq failed\n");
- goto out;
- }
-
- mutex_init(&aes_lock);
- INIT_LIST_HEAD(&dev_list);
-
- spin_lock_init(&list_lock);
- spin_lock(&list_lock);
- for (i = 0; i < AES_NR_KEYSLOTS; i++) {
- if (i == SSK_SLOT_NUM)
- continue;
- dd->slots[i].slot_num = i;
- INIT_LIST_HEAD(&dd->slots[i].node);
- list_add_tail(&dd->slots[i].node, &dev_list);
- }
- spin_unlock(&list_lock);
-
- aes_dev = dd;
- for (i = 0; i < ARRAY_SIZE(algs); i++) {
- algs[i].cra_priority = 300;
- algs[i].cra_ctxsize = sizeof(struct tegra_aes_ctx);
- algs[i].cra_module = THIS_MODULE;
- algs[i].cra_init = tegra_aes_cra_init;
- algs[i].cra_exit = tegra_aes_cra_exit;
-
- err = crypto_register_alg(&algs[i]);
- if (err)
- goto out;
- }
-
- dev_info(dev, "registered");
- return 0;
-
-out:
- for (j = 0; j < i; j++)
- crypto_unregister_alg(&algs[j]);
- if (dd->ivkey_base)
- dma_free_coherent(dev, AES_HW_KEY_TABLE_LENGTH_BYTES,
- dd->ivkey_base, dd->ivkey_phys_base);
- if (dd->buf_in)
- dma_free_coherent(dev, AES_HW_DMA_BUFFER_SIZE_BYTES,
- dd->buf_in, dd->dma_buf_in);
- if (dd->buf_out)
- dma_free_coherent(dev, AES_HW_DMA_BUFFER_SIZE_BYTES,
- dd->buf_out, dd->dma_buf_out);
- if (aes_wq)
- destroy_workqueue(aes_wq);
- spin_lock(&list_lock);
- list_del(&dev_list);
- spin_unlock(&list_lock);
-
- aes_dev = NULL;
-
- dev_err(dev, "%s: initialization failed.\n", __func__);
- return err;
-}
-
-static int tegra_aes_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct tegra_aes_dev *dd = platform_get_drvdata(pdev);
- int i;
-
- for (i = 0; i < ARRAY_SIZE(algs); i++)
- crypto_unregister_alg(&algs[i]);
-
- cancel_work_sync(&aes_work);
- destroy_workqueue(aes_wq);
- spin_lock(&list_lock);
- list_del(&dev_list);
- spin_unlock(&list_lock);
-
- dma_free_coherent(dev, AES_HW_KEY_TABLE_LENGTH_BYTES,
- dd->ivkey_base, dd->ivkey_phys_base);
- dma_free_coherent(dev, AES_HW_DMA_BUFFER_SIZE_BYTES,
- dd->buf_in, dd->dma_buf_in);
- dma_free_coherent(dev, AES_HW_DMA_BUFFER_SIZE_BYTES,
- dd->buf_out, dd->dma_buf_out);
- aes_dev = NULL;
-
- return 0;
-}
-
-static struct of_device_id tegra_aes_of_match[] = {
- { .compatible = "nvidia,tegra20-aes", },
- { .compatible = "nvidia,tegra30-aes", },
- { },
-};
-
-static struct platform_driver tegra_aes_driver = {
- .probe = tegra_aes_probe,
- .remove = tegra_aes_remove,
- .driver = {
- .name = "tegra-aes",
- .owner = THIS_MODULE,
- .of_match_table = tegra_aes_of_match,
- },
-};
-
-module_platform_driver(tegra_aes_driver);
-
-MODULE_DESCRIPTION("Tegra AES/OFB/CPRNG hw acceleration support.");
-MODULE_AUTHOR("NVIDIA Corporation");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/crypto/tegra-aes.h b/drivers/crypto/tegra-aes.h
deleted file mode 100644
index 6006333a8934..000000000000
--- a/drivers/crypto/tegra-aes.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2010, NVIDIA Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * 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.
- */
-
-#ifndef __CRYPTODEV_TEGRA_AES_H
-#define __CRYPTODEV_TEGRA_AES_H
-
-#define TEGRA_AES_ICMDQUE_WR 0x1000
-#define TEGRA_AES_CMDQUE_CONTROL 0x1008
-#define TEGRA_AES_INTR_STATUS 0x1018
-#define TEGRA_AES_INT_ENB 0x1040
-#define TEGRA_AES_CONFIG 0x1044
-#define TEGRA_AES_IRAM_ACCESS_CFG 0x10A0
-#define TEGRA_AES_SECURE_DEST_ADDR 0x1100
-#define TEGRA_AES_SECURE_INPUT_SELECT 0x1104
-#define TEGRA_AES_SECURE_CONFIG 0x1108
-#define TEGRA_AES_SECURE_CONFIG_EXT 0x110C
-#define TEGRA_AES_SECURE_SECURITY 0x1110
-#define TEGRA_AES_SECURE_HASH_RESULT0 0x1120
-#define TEGRA_AES_SECURE_HASH_RESULT1 0x1124
-#define TEGRA_AES_SECURE_HASH_RESULT2 0x1128
-#define TEGRA_AES_SECURE_HASH_RESULT3 0x112C
-#define TEGRA_AES_SECURE_SEC_SEL0 0x1140
-#define TEGRA_AES_SECURE_SEC_SEL1 0x1144
-#define TEGRA_AES_SECURE_SEC_SEL2 0x1148
-#define TEGRA_AES_SECURE_SEC_SEL3 0x114C
-#define TEGRA_AES_SECURE_SEC_SEL4 0x1150
-#define TEGRA_AES_SECURE_SEC_SEL5 0x1154
-#define TEGRA_AES_SECURE_SEC_SEL6 0x1158
-#define TEGRA_AES_SECURE_SEC_SEL7 0x115C
-
-/* interrupt status reg masks and shifts */
-#define TEGRA_AES_ENGINE_BUSY_FIELD BIT(0)
-#define TEGRA_AES_ICQ_EMPTY_FIELD BIT(3)
-#define TEGRA_AES_DMA_BUSY_FIELD BIT(23)
-
-/* secure select reg masks and shifts */
-#define TEGRA_AES_SECURE_SEL0_KEYREAD_ENB0_FIELD BIT(0)
-
-/* secure config ext masks and shifts */
-#define TEGRA_AES_SECURE_KEY_SCH_DIS_FIELD BIT(15)
-
-/* secure config masks and shifts */
-#define TEGRA_AES_SECURE_KEY_INDEX_SHIFT 20
-#define TEGRA_AES_SECURE_KEY_INDEX_FIELD (0x1F << TEGRA_AES_SECURE_KEY_INDEX_SHIFT)
-#define TEGRA_AES_SECURE_BLOCK_CNT_SHIFT 0
-#define TEGRA_AES_SECURE_BLOCK_CNT_FIELD (0xFFFFF << TEGRA_AES_SECURE_BLOCK_CNT_SHIFT)
-
-/* stream interface select masks and shifts */
-#define TEGRA_AES_CMDQ_CTRL_UCMDQEN_FIELD BIT(0)
-#define TEGRA_AES_CMDQ_CTRL_ICMDQEN_FIELD BIT(1)
-#define TEGRA_AES_CMDQ_CTRL_SRC_STM_SEL_FIELD BIT(4)
-#define TEGRA_AES_CMDQ_CTRL_DST_STM_SEL_FIELD BIT(5)
-
-/* config register masks and shifts */
-#define TEGRA_AES_CONFIG_ENDIAN_ENB_FIELD BIT(10)
-#define TEGRA_AES_CONFIG_MODE_SEL_SHIFT 0
-#define TEGRA_AES_CONFIG_MODE_SEL_FIELD (0x1F << TEGRA_AES_CONFIG_MODE_SEL_SHIFT)
-
-/* extended config */
-#define TEGRA_AES_SECURE_OFFSET_CNT_SHIFT 24
-#define TEGRA_AES_SECURE_OFFSET_CNT_FIELD (0xFF << TEGRA_AES_SECURE_OFFSET_CNT_SHIFT)
-#define TEGRA_AES_SECURE_KEYSCHED_GEN_FIELD BIT(15)
-
-/* init vector select */
-#define TEGRA_AES_SECURE_IV_SELECT_SHIFT 10
-#define TEGRA_AES_SECURE_IV_SELECT_FIELD BIT(10)
-
-/* secure engine input */
-#define TEGRA_AES_SECURE_INPUT_ALG_SEL_SHIFT 28
-#define TEGRA_AES_SECURE_INPUT_ALG_SEL_FIELD (0xF << TEGRA_AES_SECURE_INPUT_ALG_SEL_SHIFT)
-#define TEGRA_AES_SECURE_INPUT_KEY_LEN_SHIFT 16
-#define TEGRA_AES_SECURE_INPUT_KEY_LEN_FIELD (0xFFF << TEGRA_AES_SECURE_INPUT_KEY_LEN_SHIFT)
-#define TEGRA_AES_SECURE_RNG_ENB_FIELD BIT(11)
-#define TEGRA_AES_SECURE_CORE_SEL_SHIFT 9
-#define TEGRA_AES_SECURE_CORE_SEL_FIELD BIT(9)
-#define TEGRA_AES_SECURE_VCTRAM_SEL_SHIFT 7
-#define TEGRA_AES_SECURE_VCTRAM_SEL_FIELD (0x3 << TEGRA_AES_SECURE_VCTRAM_SEL_SHIFT)
-#define TEGRA_AES_SECURE_INPUT_SEL_SHIFT 5
-#define TEGRA_AES_SECURE_INPUT_SEL_FIELD (0x3 << TEGRA_AES_SECURE_INPUT_SEL_SHIFT)
-#define TEGRA_AES_SECURE_XOR_POS_SHIFT 3
-#define TEGRA_AES_SECURE_XOR_POS_FIELD (0x3 << TEGRA_AES_SECURE_XOR_POS_SHIFT)
-#define TEGRA_AES_SECURE_HASH_ENB_FIELD BIT(2)
-#define TEGRA_AES_SECURE_ON_THE_FLY_FIELD BIT(0)
-
-/* interrupt error mask */
-#define TEGRA_AES_INT_ERROR_MASK 0xFFF000
-
-#endif
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
index 362e7c49f2e1..64ceca2920b8 100644
--- a/drivers/dma/omap-dma.c
+++ b/drivers/dma/omap-dma.c
@@ -5,6 +5,7 @@
* 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/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
@@ -26,11 +27,21 @@ struct omap_dmadev {
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;
+ bool legacy;
+ spinlock_t irq_lock;
+ uint32_t irq_enable_mask;
+ struct omap_chan *lch_map[32];
};
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;
struct dma_slave_config cfg;
unsigned dma_sig;
@@ -54,19 +65,93 @@ struct omap_desc {
dma_addr_t dev_addr;
int16_t fi; /* for OMAP_DMA_SYNC_PACKET */
- uint8_t es; /* OMAP_DMA_DATA_TYPE_xxx */
- uint8_t sync_mode; /* OMAP_DMA_SYNC_xxx */
- uint8_t sync_type; /* OMAP_DMA_xxx_SYNC* */
- uint8_t periph_port; /* Peripheral port */
+ uint8_t es; /* CSDP_DATA_TYPE_xxx */
+ uint32_t ccr; /* CCR value */
+ uint16_t clnk_ctrl; /* CLNK_CTRL value */
+ uint16_t cicr; /* CICR value */
+ uint32_t csdp; /* CSDP value */
unsigned sglen;
struct omap_sg sg[0];
};
+enum {
+ CCR_FS = BIT(5),
+ CCR_READ_PRIORITY = BIT(6),
+ CCR_ENABLE = BIT(7),
+ CCR_AUTO_INIT = BIT(8), /* OMAP1 only */
+ CCR_REPEAT = BIT(9), /* OMAP1 only */
+ CCR_OMAP31_DISABLE = BIT(10), /* OMAP1 only */
+ CCR_SUSPEND_SENSITIVE = BIT(8), /* OMAP2+ only */
+ CCR_RD_ACTIVE = BIT(9), /* OMAP2+ only */
+ CCR_WR_ACTIVE = BIT(10), /* OMAP2+ only */
+ CCR_SRC_AMODE_CONSTANT = 0 << 12,
+ CCR_SRC_AMODE_POSTINC = 1 << 12,
+ CCR_SRC_AMODE_SGLIDX = 2 << 12,
+ CCR_SRC_AMODE_DBLIDX = 3 << 12,
+ CCR_DST_AMODE_CONSTANT = 0 << 14,
+ CCR_DST_AMODE_POSTINC = 1 << 14,
+ CCR_DST_AMODE_SGLIDX = 2 << 14,
+ CCR_DST_AMODE_DBLIDX = 3 << 14,
+ CCR_CONSTANT_FILL = BIT(16),
+ CCR_TRANSPARENT_COPY = BIT(17),
+ CCR_BS = BIT(18),
+ CCR_SUPERVISOR = BIT(22),
+ CCR_PREFETCH = BIT(23),
+ CCR_TRIGGER_SRC = BIT(24),
+ CCR_BUFFERING_DISABLE = BIT(25),
+ CCR_WRITE_PRIORITY = BIT(26),
+ CCR_SYNC_ELEMENT = 0,
+ CCR_SYNC_FRAME = CCR_FS,
+ CCR_SYNC_BLOCK = CCR_BS,
+ CCR_SYNC_PACKET = CCR_BS | CCR_FS,
+
+ CSDP_DATA_TYPE_8 = 0,
+ CSDP_DATA_TYPE_16 = 1,
+ CSDP_DATA_TYPE_32 = 2,
+ CSDP_SRC_PORT_EMIFF = 0 << 2, /* OMAP1 only */
+ CSDP_SRC_PORT_EMIFS = 1 << 2, /* OMAP1 only */
+ CSDP_SRC_PORT_OCP_T1 = 2 << 2, /* OMAP1 only */
+ CSDP_SRC_PORT_TIPB = 3 << 2, /* OMAP1 only */
+ CSDP_SRC_PORT_OCP_T2 = 4 << 2, /* OMAP1 only */
+ CSDP_SRC_PORT_MPUI = 5 << 2, /* OMAP1 only */
+ CSDP_SRC_PACKED = BIT(6),
+ CSDP_SRC_BURST_1 = 0 << 7,
+ CSDP_SRC_BURST_16 = 1 << 7,
+ CSDP_SRC_BURST_32 = 2 << 7,
+ CSDP_SRC_BURST_64 = 3 << 7,
+ CSDP_DST_PORT_EMIFF = 0 << 9, /* OMAP1 only */
+ CSDP_DST_PORT_EMIFS = 1 << 9, /* OMAP1 only */
+ CSDP_DST_PORT_OCP_T1 = 2 << 9, /* OMAP1 only */
+ CSDP_DST_PORT_TIPB = 3 << 9, /* OMAP1 only */
+ CSDP_DST_PORT_OCP_T2 = 4 << 9, /* OMAP1 only */
+ CSDP_DST_PORT_MPUI = 5 << 9, /* OMAP1 only */
+ CSDP_DST_PACKED = BIT(13),
+ CSDP_DST_BURST_1 = 0 << 14,
+ CSDP_DST_BURST_16 = 1 << 14,
+ CSDP_DST_BURST_32 = 2 << 14,
+ CSDP_DST_BURST_64 = 3 << 14,
+
+ CICR_TOUT_IE = BIT(0), /* OMAP1 only */
+ CICR_DROP_IE = BIT(1),
+ CICR_HALF_IE = BIT(2),
+ CICR_FRAME_IE = BIT(3),
+ CICR_LAST_IE = BIT(4),
+ CICR_BLOCK_IE = BIT(5),
+ CICR_PKT_IE = BIT(7), /* OMAP2+ only */
+ CICR_TRANS_ERR_IE = BIT(8), /* OMAP2+ only */
+ CICR_SUPERVISOR_ERR_IE = BIT(10), /* OMAP2+ only */
+ CICR_MISALIGNED_ERR_IE = BIT(11), /* OMAP2+ only */
+ CICR_DRAIN_IE = BIT(12), /* OMAP2+ only */
+ CICR_SUPER_BLOCK_IE = BIT(14), /* OMAP2+ only */
+
+ CLNK_CTRL_ENABLE_LNK = BIT(15),
+};
+
static const unsigned es_bytes[] = {
- [OMAP_DMA_DATA_TYPE_S8] = 1,
- [OMAP_DMA_DATA_TYPE_S16] = 2,
- [OMAP_DMA_DATA_TYPE_S32] = 4,
+ [CSDP_DATA_TYPE_8] = 1,
+ [CSDP_DATA_TYPE_16] = 2,
+ [CSDP_DATA_TYPE_32] = 4,
};
static struct of_dma_filter_info omap_dma_info = {
@@ -93,28 +178,214 @@ static void omap_dma_desc_free(struct virt_dma_desc *vd)
kfree(container_of(vd, struct omap_desc, vd));
}
+static void omap_dma_write(uint32_t val, unsigned type, void __iomem *addr)
+{
+ switch (type) {
+ case OMAP_DMA_REG_16BIT:
+ writew_relaxed(val, addr);
+ break;
+ case OMAP_DMA_REG_2X16BIT:
+ writew_relaxed(val, addr);
+ writew_relaxed(val >> 16, addr + 2);
+ break;
+ case OMAP_DMA_REG_32BIT:
+ writel_relaxed(val, addr);
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+static unsigned omap_dma_read(unsigned type, void __iomem *addr)
+{
+ unsigned val;
+
+ switch (type) {
+ case OMAP_DMA_REG_16BIT:
+ val = readw_relaxed(addr);
+ break;
+ case OMAP_DMA_REG_2X16BIT:
+ val = readw_relaxed(addr);
+ val |= readw_relaxed(addr + 2) << 16;
+ break;
+ case OMAP_DMA_REG_32BIT:
+ val = readl_relaxed(addr);
+ break;
+ default:
+ WARN_ON(1);
+ val = 0;
+ }
+
+ return val;
+}
+
+static void omap_dma_glbl_write(struct omap_dmadev *od, unsigned reg, unsigned val)
+{
+ const struct omap_dma_reg *r = od->reg_map + reg;
+
+ WARN_ON(r->stride);
+
+ omap_dma_write(val, r->type, od->base + r->offset);
+}
+
+static unsigned omap_dma_glbl_read(struct omap_dmadev *od, unsigned reg)
+{
+ const struct omap_dma_reg *r = od->reg_map + reg;
+
+ WARN_ON(r->stride);
+
+ return omap_dma_read(r->type, od->base + r->offset);
+}
+
+static void omap_dma_chan_write(struct omap_chan *c, unsigned reg, unsigned val)
+{
+ const struct omap_dma_reg *r = c->reg_map + reg;
+
+ omap_dma_write(val, r->type, c->channel_base + r->offset);
+}
+
+static unsigned omap_dma_chan_read(struct omap_chan *c, unsigned reg)
+{
+ const struct omap_dma_reg *r = c->reg_map + reg;
+
+ return omap_dma_read(r->type, c->channel_base + r->offset);
+}
+
+static void omap_dma_clear_csr(struct omap_chan *c)
+{
+ if (dma_omap1())
+ omap_dma_chan_read(c, CSR);
+ else
+ omap_dma_chan_write(c, CSR, ~0);
+}
+
+static unsigned omap_dma_get_csr(struct omap_chan *c)
+{
+ unsigned val = omap_dma_chan_read(c, CSR);
+
+ if (!dma_omap1())
+ omap_dma_chan_write(c, CSR, val);
+
+ return val;
+}
+
+static void omap_dma_assign(struct omap_dmadev *od, struct omap_chan *c,
+ unsigned lch)
+{
+ c->channel_base = od->base + od->plat->channel_stride * lch;
+
+ od->lch_map[lch] = c;
+}
+
+static void omap_dma_start(struct omap_chan *c, struct omap_desc *d)
+{
+ struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
+
+ if (__dma_omap15xx(od->plat->dma_attr))
+ omap_dma_chan_write(c, CPC, 0);
+ else
+ omap_dma_chan_write(c, CDAC, 0);
+
+ omap_dma_clear_csr(c);
+
+ /* Enable interrupts */
+ omap_dma_chan_write(c, CICR, d->cicr);
+
+ /* Enable channel */
+ omap_dma_chan_write(c, CCR, d->ccr | CCR_ENABLE);
+}
+
+static void omap_dma_stop(struct omap_chan *c)
+{
+ struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
+ uint32_t val;
+
+ /* disable irq */
+ omap_dma_chan_write(c, CICR, 0);
+
+ omap_dma_clear_csr(c);
+
+ val = omap_dma_chan_read(c, CCR);
+ if (od->plat->errata & DMA_ERRATA_i541 && val & CCR_TRIGGER_SRC) {
+ uint32_t sysconfig;
+ unsigned i;
+
+ sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG);
+ val = sysconfig & ~DMA_SYSCONFIG_MIDLEMODE_MASK;
+ val |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE);
+ omap_dma_glbl_write(od, OCP_SYSCONFIG, val);
+
+ val = omap_dma_chan_read(c, CCR);
+ val &= ~CCR_ENABLE;
+ omap_dma_chan_write(c, CCR, val);
+
+ /* Wait for sDMA FIFO to drain */
+ for (i = 0; ; i++) {
+ val = omap_dma_chan_read(c, CCR);
+ if (!(val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE)))
+ break;
+
+ if (i > 100)
+ break;
+
+ udelay(5);
+ }
+
+ if (val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE))
+ dev_err(c->vc.chan.device->dev,
+ "DMA drain did not complete on lch %d\n",
+ c->dma_ch);
+
+ omap_dma_glbl_write(od, OCP_SYSCONFIG, sysconfig);
+ } else {
+ val &= ~CCR_ENABLE;
+ omap_dma_chan_write(c, CCR, val);
+ }
+
+ mb();
+
+ if (!__dma_omap15xx(od->plat->dma_attr) && c->cyclic) {
+ val = omap_dma_chan_read(c, CLNK_CTRL);
+
+ if (dma_omap1())
+ val |= 1 << 14; /* set the STOP_LNK bit */
+ else
+ val &= ~CLNK_CTRL_ENABLE_LNK;
+
+ omap_dma_chan_write(c, CLNK_CTRL, val);
+ }
+}
+
static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
unsigned idx)
{
struct omap_sg *sg = d->sg + idx;
+ unsigned cxsa, cxei, cxfi;
- if (d->dir == DMA_DEV_TO_MEM)
- omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
- OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
- else
- omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
- OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+ if (d->dir == DMA_DEV_TO_MEM) {
+ cxsa = CDSA;
+ cxei = CDEI;
+ cxfi = CDFI;
+ } else {
+ cxsa = CSSA;
+ cxei = CSEI;
+ cxfi = CSFI;
+ }
- omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
- d->sync_mode, c->dma_sig, d->sync_type);
+ omap_dma_chan_write(c, cxsa, sg->addr);
+ omap_dma_chan_write(c, cxei, 0);
+ omap_dma_chan_write(c, cxfi, 0);
+ omap_dma_chan_write(c, CEN, sg->en);
+ omap_dma_chan_write(c, CFN, sg->fn);
- omap_start_dma(c->dma_ch);
+ omap_dma_start(c, d);
}
static void omap_dma_start_desc(struct omap_chan *c)
{
struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
struct omap_desc *d;
+ unsigned cxsa, cxei, cxfi;
if (!vd) {
c->desc = NULL;
@@ -126,12 +397,32 @@ static void omap_dma_start_desc(struct omap_chan *c)
c->desc = d = to_omap_dma_desc(&vd->tx);
c->sgidx = 0;
- if (d->dir == DMA_DEV_TO_MEM)
- omap_set_dma_src_params(c->dma_ch, d->periph_port,
- OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi);
- else
- omap_set_dma_dest_params(c->dma_ch, d->periph_port,
- OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi);
+ /*
+ * This provides the necessary barrier to ensure data held in
+ * DMA coherent memory is visible to the DMA engine prior to
+ * the transfer starting.
+ */
+ mb();
+
+ omap_dma_chan_write(c, CCR, d->ccr);
+ if (dma_omap1())
+ omap_dma_chan_write(c, CCR2, d->ccr >> 16);
+
+ if (d->dir == DMA_DEV_TO_MEM) {
+ cxsa = CSSA;
+ cxei = CSEI;
+ cxfi = CSFI;
+ } else {
+ cxsa = CDSA;
+ cxei = CDEI;
+ cxfi = CDFI;
+ }
+
+ omap_dma_chan_write(c, cxsa, d->dev_addr);
+ omap_dma_chan_write(c, cxei, 0);
+ omap_dma_chan_write(c, cxfi, d->fi);
+ omap_dma_chan_write(c, CSDP, d->csdp);
+ omap_dma_chan_write(c, CLNK_CTRL, d->clnk_ctrl);
omap_dma_start_sg(c, d, 0);
}
@@ -186,24 +477,118 @@ static void omap_dma_sched(unsigned long data)
}
}
+static irqreturn_t omap_dma_irq(int irq, void *devid)
+{
+ struct omap_dmadev *od = devid;
+ unsigned status, channel;
+
+ spin_lock(&od->irq_lock);
+
+ status = omap_dma_glbl_read(od, IRQSTATUS_L1);
+ status &= od->irq_enable_mask;
+ if (status == 0) {
+ spin_unlock(&od->irq_lock);
+ return IRQ_NONE;
+ }
+
+ while ((channel = ffs(status)) != 0) {
+ unsigned mask, csr;
+ struct omap_chan *c;
+
+ channel -= 1;
+ mask = BIT(channel);
+ status &= ~mask;
+
+ c = od->lch_map[channel];
+ if (c == NULL) {
+ /* This should never happen */
+ dev_err(od->ddev.dev, "invalid channel %u\n", channel);
+ continue;
+ }
+
+ csr = omap_dma_get_csr(c);
+ omap_dma_glbl_write(od, IRQSTATUS_L1, mask);
+
+ omap_dma_callback(channel, csr, c);
+ }
+
+ spin_unlock(&od->irq_lock);
+
+ return IRQ_HANDLED;
+}
+
static int omap_dma_alloc_chan_resources(struct dma_chan *chan)
{
+ struct omap_dmadev *od = to_omap_dma_dev(chan->device);
struct omap_chan *c = to_omap_dma_chan(chan);
+ int ret;
+
+ if (od->legacy) {
+ ret = omap_request_dma(c->dma_sig, "DMA engine",
+ omap_dma_callback, c, &c->dma_ch);
+ } else {
+ ret = omap_request_dma(c->dma_sig, "DMA engine", NULL, NULL,
+ &c->dma_ch);
+ }
+
+ dev_dbg(od->ddev.dev, "allocating channel %u for %u\n",
+ c->dma_ch, c->dma_sig);
- dev_dbg(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig);
+ if (ret >= 0) {
+ omap_dma_assign(od, c, c->dma_ch);
- return omap_request_dma(c->dma_sig, "DMA engine",
- omap_dma_callback, c, &c->dma_ch);
+ if (!od->legacy) {
+ unsigned val;
+
+ spin_lock_irq(&od->irq_lock);
+ val = BIT(c->dma_ch);
+ omap_dma_glbl_write(od, IRQSTATUS_L1, val);
+ od->irq_enable_mask |= val;
+ omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask);
+
+ val = omap_dma_glbl_read(od, IRQENABLE_L0);
+ val &= ~BIT(c->dma_ch);
+ omap_dma_glbl_write(od, IRQENABLE_L0, val);
+ spin_unlock_irq(&od->irq_lock);
+ }
+ }
+
+ if (dma_omap1()) {
+ if (__dma_omap16xx(od->plat->dma_attr)) {
+ c->ccr = CCR_OMAP31_DISABLE;
+ /* Duplicate what plat-omap/dma.c does */
+ c->ccr |= c->dma_ch + 1;
+ } else {
+ c->ccr = c->dma_sig & 0x1f;
+ }
+ } else {
+ c->ccr = c->dma_sig & 0x1f;
+ c->ccr |= (c->dma_sig & ~0x1f) << 14;
+ }
+ if (od->plat->errata & DMA_ERRATA_IFRAME_BUFFERING)
+ c->ccr |= CCR_BUFFERING_DISABLE;
+
+ return ret;
}
static void omap_dma_free_chan_resources(struct dma_chan *chan)
{
+ struct omap_dmadev *od = to_omap_dma_dev(chan->device);
struct omap_chan *c = to_omap_dma_chan(chan);
+ if (!od->legacy) {
+ spin_lock_irq(&od->irq_lock);
+ od->irq_enable_mask &= ~BIT(c->dma_ch);
+ omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask);
+ spin_unlock_irq(&od->irq_lock);
+ }
+
+ c->channel_base = NULL;
+ od->lch_map[c->dma_ch] = NULL;
vchan_free_chan_resources(&c->vc);
omap_free_dma(c->dma_ch);
- dev_dbg(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig);
+ dev_dbg(od->ddev.dev, "freeing channel for %u\n", c->dma_sig);
}
static size_t omap_dma_sg_size(struct omap_sg *sg)
@@ -239,6 +624,74 @@ static size_t omap_dma_desc_size_pos(struct omap_desc *d, dma_addr_t addr)
return size;
}
+/*
+ * OMAP 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is
+ * read before the DMA controller finished disabling the channel.
+ */
+static uint32_t omap_dma_chan_read_3_3(struct omap_chan *c, unsigned reg)
+{
+ struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
+ uint32_t val;
+
+ val = omap_dma_chan_read(c, reg);
+ if (val == 0 && od->plat->errata & DMA_ERRATA_3_3)
+ val = omap_dma_chan_read(c, reg);
+
+ return val;
+}
+
+static dma_addr_t omap_dma_get_src_pos(struct omap_chan *c)
+{
+ struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
+ dma_addr_t addr, cdac;
+
+ if (__dma_omap15xx(od->plat->dma_attr)) {
+ addr = omap_dma_chan_read(c, CPC);
+ } else {
+ addr = omap_dma_chan_read_3_3(c, CSAC);
+ cdac = omap_dma_chan_read_3_3(c, CDAC);
+
+ /*
+ * CDAC == 0 indicates that the DMA transfer on the channel has
+ * not been started (no data has been transferred so far).
+ * Return the programmed source start address in this case.
+ */
+ if (cdac == 0)
+ addr = omap_dma_chan_read(c, CSSA);
+ }
+
+ if (dma_omap1())
+ addr |= omap_dma_chan_read(c, CSSA) & 0xffff0000;
+
+ return addr;
+}
+
+static dma_addr_t omap_dma_get_dst_pos(struct omap_chan *c)
+{
+ struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
+ dma_addr_t addr;
+
+ if (__dma_omap15xx(od->plat->dma_attr)) {
+ addr = omap_dma_chan_read(c, CPC);
+ } else {
+ addr = omap_dma_chan_read_3_3(c, CDAC);
+
+ /*
+ * CDAC == 0 indicates that the DMA transfer on the channel
+ * has not been started (no data has been transferred so
+ * far). Return the programmed destination start address in
+ * this case.
+ */
+ if (addr == 0)
+ addr = omap_dma_chan_read(c, CDSA);
+ }
+
+ if (dma_omap1())
+ addr |= omap_dma_chan_read(c, CDSA) & 0xffff0000;
+
+ return addr;
+}
+
static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate)
{
@@ -260,9 +713,9 @@ static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
dma_addr_t pos;
if (d->dir == DMA_MEM_TO_DEV)
- pos = omap_get_dma_src_pos(c->dma_ch);
+ pos = omap_dma_get_src_pos(c);
else if (d->dir == DMA_DEV_TO_MEM)
- pos = omap_get_dma_dst_pos(c->dma_ch);
+ pos = omap_dma_get_dst_pos(c);
else
pos = 0;
@@ -304,24 +757,23 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen,
enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
{
+ struct omap_dmadev *od = to_omap_dma_dev(chan->device);
struct omap_chan *c = to_omap_dma_chan(chan);
enum dma_slave_buswidth dev_width;
struct scatterlist *sgent;
struct omap_desc *d;
dma_addr_t dev_addr;
- unsigned i, j = 0, es, en, frame_bytes, sync_type;
+ unsigned i, j = 0, es, en, frame_bytes;
u32 burst;
if (dir == DMA_DEV_TO_MEM) {
dev_addr = c->cfg.src_addr;
dev_width = c->cfg.src_addr_width;
burst = c->cfg.src_maxburst;
- sync_type = OMAP_DMA_SRC_SYNC;
} else if (dir == DMA_MEM_TO_DEV) {
dev_addr = c->cfg.dst_addr;
dev_width = c->cfg.dst_addr_width;
burst = c->cfg.dst_maxburst;
- sync_type = OMAP_DMA_DST_SYNC;
} else {
dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
return NULL;
@@ -330,13 +782,13 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
/* Bus width translates to the element size (ES) */
switch (dev_width) {
case DMA_SLAVE_BUSWIDTH_1_BYTE:
- es = OMAP_DMA_DATA_TYPE_S8;
+ es = CSDP_DATA_TYPE_8;
break;
case DMA_SLAVE_BUSWIDTH_2_BYTES:
- es = OMAP_DMA_DATA_TYPE_S16;
+ es = CSDP_DATA_TYPE_16;
break;
case DMA_SLAVE_BUSWIDTH_4_BYTES:
- es = OMAP_DMA_DATA_TYPE_S32;
+ es = CSDP_DATA_TYPE_32;
break;
default: /* not reached */
return NULL;
@@ -350,9 +802,31 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
d->dir = dir;
d->dev_addr = dev_addr;
d->es = es;
- d->sync_mode = OMAP_DMA_SYNC_FRAME;
- d->sync_type = sync_type;
- d->periph_port = OMAP_DMA_PORT_TIPB;
+
+ d->ccr = c->ccr | CCR_SYNC_FRAME;
+ if (dir == DMA_DEV_TO_MEM)
+ d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_CONSTANT;
+ else
+ d->ccr |= CCR_DST_AMODE_CONSTANT | CCR_SRC_AMODE_POSTINC;
+
+ d->cicr = CICR_DROP_IE | CICR_BLOCK_IE;
+ d->csdp = es;
+
+ if (dma_omap1()) {
+ d->cicr |= CICR_TOUT_IE;
+
+ if (dir == DMA_DEV_TO_MEM)
+ d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_TIPB;
+ else
+ d->csdp |= CSDP_DST_PORT_TIPB | CSDP_SRC_PORT_EMIFF;
+ } else {
+ if (dir == DMA_DEV_TO_MEM)
+ d->ccr |= CCR_TRIGGER_SRC;
+
+ d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
+ }
+ if (od->plat->errata & DMA_ERRATA_PARALLEL_CHANNELS)
+ d->clnk_ctrl = c->dma_ch;
/*
* Build our scatterlist entries: each contains the address,
@@ -382,23 +856,22 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
size_t period_len, enum dma_transfer_direction dir, unsigned long flags,
void *context)
{
+ struct omap_dmadev *od = to_omap_dma_dev(chan->device);
struct omap_chan *c = to_omap_dma_chan(chan);
enum dma_slave_buswidth dev_width;
struct omap_desc *d;
dma_addr_t dev_addr;
- unsigned es, sync_type;
+ unsigned es;
u32 burst;
if (dir == DMA_DEV_TO_MEM) {
dev_addr = c->cfg.src_addr;
dev_width = c->cfg.src_addr_width;
burst = c->cfg.src_maxburst;
- sync_type = OMAP_DMA_SRC_SYNC;
} else if (dir == DMA_MEM_TO_DEV) {
dev_addr = c->cfg.dst_addr;
dev_width = c->cfg.dst_addr_width;
burst = c->cfg.dst_maxburst;
- sync_type = OMAP_DMA_DST_SYNC;
} else {
dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
return NULL;
@@ -407,13 +880,13 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
/* Bus width translates to the element size (ES) */
switch (dev_width) {
case DMA_SLAVE_BUSWIDTH_1_BYTE:
- es = OMAP_DMA_DATA_TYPE_S8;
+ es = CSDP_DATA_TYPE_8;
break;
case DMA_SLAVE_BUSWIDTH_2_BYTES:
- es = OMAP_DMA_DATA_TYPE_S16;
+ es = CSDP_DATA_TYPE_16;
break;
case DMA_SLAVE_BUSWIDTH_4_BYTES:
- es = OMAP_DMA_DATA_TYPE_S32;
+ es = CSDP_DATA_TYPE_32;
break;
default: /* not reached */
return NULL;
@@ -428,32 +901,51 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
d->dev_addr = dev_addr;
d->fi = burst;
d->es = es;
- if (burst)
- d->sync_mode = OMAP_DMA_SYNC_PACKET;
- else
- d->sync_mode = OMAP_DMA_SYNC_ELEMENT;
- d->sync_type = sync_type;
- d->periph_port = OMAP_DMA_PORT_MPUI;
d->sg[0].addr = buf_addr;
d->sg[0].en = period_len / es_bytes[es];
d->sg[0].fn = buf_len / period_len;
d->sglen = 1;
- if (!c->cyclic) {
- c->cyclic = true;
- omap_dma_link_lch(c->dma_ch, c->dma_ch);
+ d->ccr = c->ccr;
+ if (dir == DMA_DEV_TO_MEM)
+ d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_CONSTANT;
+ else
+ d->ccr |= CCR_DST_AMODE_CONSTANT | CCR_SRC_AMODE_POSTINC;
- if (flags & DMA_PREP_INTERRUPT)
- omap_enable_dma_irq(c->dma_ch, OMAP_DMA_FRAME_IRQ);
+ d->cicr = CICR_DROP_IE;
+ if (flags & DMA_PREP_INTERRUPT)
+ d->cicr |= CICR_FRAME_IE;
- omap_disable_dma_irq(c->dma_ch, OMAP_DMA_BLOCK_IRQ);
- }
+ d->csdp = es;
+
+ if (dma_omap1()) {
+ d->cicr |= CICR_TOUT_IE;
+
+ if (dir == DMA_DEV_TO_MEM)
+ d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_MPUI;
+ else
+ d->csdp |= CSDP_DST_PORT_MPUI | CSDP_SRC_PORT_EMIFF;
+ } else {
+ if (burst)
+ d->ccr |= CCR_SYNC_PACKET;
+ else
+ d->ccr |= CCR_SYNC_ELEMENT;
+
+ if (dir == DMA_DEV_TO_MEM)
+ d->ccr |= CCR_TRIGGER_SRC;
+
+ d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
- if (dma_omap2plus()) {
- omap_set_dma_src_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16);
- omap_set_dma_dest_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16);
+ d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64;
}
+ if (__dma_omap15xx(od->plat->dma_attr))
+ d->ccr |= CCR_AUTO_INIT | CCR_REPEAT;
+ else
+ d->clnk_ctrl = c->dma_ch | CLNK_CTRL_ENABLE_LNK;
+
+ c->cyclic = true;
+
return vchan_tx_prep(&c->vc, &d->vd, flags);
}
@@ -483,20 +975,19 @@ static int omap_dma_terminate_all(struct omap_chan *c)
/*
* Stop DMA activity: we assume the callback will not be called
- * after omap_stop_dma() returns (even if it does, it will see
+ * after omap_dma_stop() returns (even if it does, it will see
* c->desc is NULL and exit.)
*/
if (c->desc) {
c->desc = NULL;
/* Avoid stopping the dma twice */
if (!c->paused)
- omap_stop_dma(c->dma_ch);
+ omap_dma_stop(c);
}
if (c->cyclic) {
c->cyclic = false;
c->paused = false;
- omap_dma_unlink_lch(c->dma_ch, c->dma_ch);
}
vchan_get_all_descriptors(&c->vc, &head);
@@ -513,7 +1004,7 @@ static int omap_dma_pause(struct omap_chan *c)
return -EINVAL;
if (!c->paused) {
- omap_stop_dma(c->dma_ch);
+ omap_dma_stop(c);
c->paused = true;
}
@@ -527,7 +1018,7 @@ static int omap_dma_resume(struct omap_chan *c)
return -EINVAL;
if (c->paused) {
- omap_start_dma(c->dma_ch);
+ omap_dma_start(c, c->desc);
c->paused = false;
}
@@ -573,6 +1064,7 @@ static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig)
if (!c)
return -ENOMEM;
+ c->reg_map = od->reg_map;
c->dma_sig = dma_sig;
c->vc.desc_free = omap_dma_desc_free;
vchan_init(&c->vc, &od->ddev);
@@ -594,18 +1086,29 @@ static void omap_dma_free(struct omap_dmadev *od)
tasklet_kill(&c->vc.task);
kfree(c);
}
- kfree(od);
}
static int omap_dma_probe(struct platform_device *pdev)
{
struct omap_dmadev *od;
- int rc, i;
+ struct resource *res;
+ int rc, i, irq;
- od = kzalloc(sizeof(*od), GFP_KERNEL);
+ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
if (!od)
return -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ od->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(od->base))
+ return PTR_ERR(od->base);
+
+ od->plat = omap_get_plat_info();
+ if (!od->plat)
+ return -EPROBE_DEFER;
+
+ od->reg_map = od->plat->reg_map;
+
dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask);
od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources;
@@ -619,6 +1122,7 @@ static int omap_dma_probe(struct platform_device *pdev)
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);
@@ -630,6 +1134,21 @@ static int omap_dma_probe(struct platform_device *pdev)
}
}
+ irq = platform_get_irq(pdev, 1);
+ if (irq <= 0) {
+ dev_info(&pdev->dev, "failed to get L1 IRQ: %d\n", irq);
+ od->legacy = true;
+ } else {
+ /* Disable all interrupts */
+ od->irq_enable_mask = 0;
+ omap_dma_glbl_write(od, IRQENABLE_L1, 0);
+
+ rc = devm_request_irq(&pdev->dev, irq, omap_dma_irq,
+ IRQF_SHARED, "omap-dma-engine", od);
+ if (rc)
+ return rc;
+ }
+
rc = dma_async_device_register(&od->ddev);
if (rc) {
pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
@@ -666,6 +1185,12 @@ static int omap_dma_remove(struct platform_device *pdev)
of_dma_controller_free(pdev->dev.of_node);
dma_async_device_unregister(&od->ddev);
+
+ if (!od->legacy) {
+ /* Disable all interrupts */
+ omap_dma_glbl_write(od, IRQENABLE_L0, 0);
+ }
+
omap_dma_free(od);
return 0;
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index b335c6ab5efe..01fae8289cf0 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -7,7 +7,7 @@
*
* Written Doug Thompson <norsk5@xmission.com> www.softwarebitmaker.com
*
- * (c) 2012-2013 - Mauro Carvalho Chehab <mchehab@redhat.com>
+ * (c) 2012-2013 - Mauro Carvalho Chehab
* The entire API were re-written, and ported to use struct device
*
*/
diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c
index d5a98a45c062..8399b4e16fe0 100644
--- a/drivers/edac/ghes_edac.c
+++ b/drivers/edac/ghes_edac.c
@@ -4,7 +4,7 @@
* This file may be distributed under the terms of the GNU General Public
* License version 2.
*
- * Copyright (c) 2013 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2013 by Mauro Carvalho Chehab
*
* Red Hat Inc. http://www.redhat.com
*/
diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c
index 5381e98d9c0c..6ef6ad1ba16e 100644
--- a/drivers/edac/i5400_edac.c
+++ b/drivers/edac/i5400_edac.c
@@ -6,7 +6,7 @@
*
* Copyright (c) 2008 by:
* Ben Woodard <woodard@redhat.com>
- * Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Mauro Carvalho Chehab
*
* Red Hat Inc. http://www.redhat.com
*
@@ -1469,7 +1469,7 @@ module_exit(i5400_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Woodard <woodard@redhat.com>");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("MC Driver for Intel I5400 memory controllers - "
I5400_REVISION);
diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c
index 57e96a3350f0..dcac982fdc7a 100644
--- a/drivers/edac/i7300_edac.c
+++ b/drivers/edac/i7300_edac.c
@@ -5,7 +5,7 @@
* GNU General Public License version 2 only.
*
* Copyright (c) 2010 by:
- * Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Mauro Carvalho Chehab
*
* Red Hat Inc. http://www.redhat.com
*
@@ -1209,7 +1209,7 @@ module_init(i7300_init);
module_exit(i7300_exit);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("MC Driver for Intel I7300 memory controllers - "
I7300_REVISION);
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 8bc83b99974b..9cd0b301f81b 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -9,7 +9,7 @@
* GNU General Public License version 2 only.
*
* Copyright (c) 2009-2010 by:
- * Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Mauro Carvalho Chehab
*
* Red Hat Inc. http://www.redhat.com
*
@@ -2457,7 +2457,7 @@ module_init(i7core_init);
module_exit(i7core_exit);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
I7CORE_REVISION);
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 347c7a1c2725..deea0dc9999b 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -7,7 +7,7 @@
* GNU General Public License version 2 only.
*
* Copyright (c) 2011 by:
- * Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Mauro Carvalho Chehab
*/
#include <linux/module.h>
@@ -1828,6 +1828,7 @@ static int sbridge_mce_check_error(struct notifier_block *nb, unsigned long val,
struct mce *mce = (struct mce *)data;
struct mem_ctl_info *mci;
struct sbridge_pvt *pvt;
+ char *type;
if (get_edac_report_status() == EDAC_REPORTING_DISABLED)
return NOTIFY_DONE;
@@ -1846,17 +1847,23 @@ static int sbridge_mce_check_error(struct notifier_block *nb, unsigned long val,
if ((mce->status & 0xefff) >> 7 != 1)
return NOTIFY_DONE;
- printk("sbridge: HANDLING MCE MEMORY ERROR\n");
+ if (mce->mcgstatus & MCG_STATUS_MCIP)
+ type = "Exception";
+ else
+ type = "Event";
+
+ sbridge_mc_printk(mci, KERN_DEBUG, "HANDLING MCE MEMORY ERROR\n");
- printk("CPU %d: Machine Check Exception: %Lx Bank %d: %016Lx\n",
- mce->extcpu, mce->mcgstatus, mce->bank, mce->status);
- printk("TSC %llx ", mce->tsc);
- printk("ADDR %llx ", mce->addr);
- printk("MISC %llx ", mce->misc);
+ sbridge_mc_printk(mci, KERN_DEBUG, "CPU %d: Machine Check %s: %Lx "
+ "Bank %d: %016Lx\n", mce->extcpu, type,
+ mce->mcgstatus, mce->bank, mce->status);
+ sbridge_mc_printk(mci, KERN_DEBUG, "TSC %llx ", mce->tsc);
+ sbridge_mc_printk(mci, KERN_DEBUG, "ADDR %llx ", mce->addr);
+ sbridge_mc_printk(mci, KERN_DEBUG, "MISC %llx ", mce->misc);
- printk("PROCESSOR %u:%x TIME %llu SOCKET %u APIC %x\n",
- mce->cpuvendor, mce->cpuid, mce->time,
- mce->socketid, mce->apicid);
+ sbridge_mc_printk(mci, KERN_DEBUG, "PROCESSOR %u:%x TIME %llu SOCKET "
+ "%u APIC %x\n", mce->cpuvendor, mce->cpuid,
+ mce->time, mce->socketid, mce->apicid);
/* Only handle if it is the right mc controller */
if (cpu_data(mce->cpu).phys_proc_id != pvt->sbridge_dev->mc)
@@ -2176,7 +2183,7 @@ module_param(edac_op_state, int, 0444);
MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("MC Driver for Intel Sandy Bridge and Ivy Bridge memory controllers - "
SBRIDGE_REVISION);
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 903f24d28ba0..a86c49a605c6 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -55,6 +55,9 @@ config GPIO_ACPI
def_bool y
depends on ACPI
+config GPIOLIB_IRQCHIP
+ bool
+
config DEBUG_GPIO
bool "Debug GPIO calls"
depends on DEBUG_KERNEL
@@ -128,6 +131,15 @@ config GPIO_GENERIC_PLATFORM
help
Say yes here to support basic platform_device memory-mapped GPIO controllers.
+config GPIO_DWAPB
+ tristate "Synopsys DesignWare APB GPIO driver"
+ select GPIO_GENERIC
+ select GENERIC_IRQ_CHIP
+ depends on OF_GPIO
+ help
+ Say Y or M here to build support for the Synopsys DesignWare APB
+ GPIO block.
+
config GPIO_IT8761E
tristate "IT8761E GPIO support"
depends on X86 # unconditional access to IO space.
@@ -145,6 +157,12 @@ config GPIO_EP93XX
depends on ARCH_EP93XX
select GPIO_GENERIC
+config GPIO_ZEVIO
+ bool "LSI ZEVIO SoC memory mapped GPIOs"
+ depends on ARM && OF_GPIO
+ help
+ Say yes here to support the GPIO controller in LSI ZEVIO SoCs.
+
config GPIO_MM_LANTIQ
bool "Lantiq Memory mapped GPIOs"
depends on LANTIQ && SOC_XWAY
@@ -192,7 +210,7 @@ config GPIO_MSM_V1
config GPIO_MSM_V2
tristate "Qualcomm MSM GPIO v2"
- depends on GPIOLIB && OF && ARCH_MSM
+ depends on GPIOLIB && OF && ARCH_QCOM
help
Say yes here to support the GPIO interface on ARM v7 based
Qualcomm MSM chips. Most of the pins on the MSM can be
@@ -228,7 +246,8 @@ config GPIO_OCTEON
config GPIO_PL061
bool "PrimeCell PL061 GPIO support"
depends on ARM_AMBA
- select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+ select GPIOLIB_IRQCHIP
help
Say yes here to support the PrimeCell PL061 GPIO device
@@ -275,8 +294,15 @@ config GPIO_STA2X11
Say yes here to support the STA2x11/ConneXt GPIO device.
The GPIO module has 128 GPIO pins with alternate functions.
+config GPIO_SYSCON
+ tristate "GPIO based on SYSCON"
+ depends on MFD_SYSCON && OF
+ help
+ Say yes here to support GPIO functionality though SYSCON driver.
+
config GPIO_TS5500
tristate "TS-5500 DIO blocks and compatibles"
+ depends on TS5500 || COMPILE_TEST
help
This driver supports Digital I/O exposed by pin blocks found on some
Technologic Systems platforms. It includes, but is not limited to, 3
@@ -462,7 +488,7 @@ config GPIO_MC9S08DZ60
Select this to enable the MC9S08DZ60 GPIO driver
config GPIO_PCA953X
- tristate "PCA953x, PCA955x, PCA957x, TCA64xx, and MAX7310 I/O ports"
+ tristate "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports"
depends on I2C
help
Say yes here to provide access to several register-oriented
@@ -472,11 +498,15 @@ config GPIO_PCA953X
4 bits: pca9536, pca9537
8 bits: max7310, max7315, pca6107, pca9534, pca9538, pca9554,
- pca9556, pca9557, pca9574, tca6408
+ pca9556, pca9557, pca9574, tca6408, xra1202
16 bits: max7312, max7313, pca9535, pca9539, pca9555, pca9575,
tca6416
+ 24 bits: tca6424
+
+ 40 bits: pca9505, pca9698
+
config GPIO_PCA953X_IRQ
bool "Interrupt controller support for PCA953x"
depends on GPIO_PCA953X=y
@@ -630,7 +660,7 @@ comment "PCI GPIO expanders:"
config GPIO_CS5535
tristate "AMD CS5535/CS5536 GPIO support"
- depends on PCI && X86 && MFD_CS5535
+ depends on MFD_CS5535
help
The AMD CS5535 and CS5536 southbridges support 28 GPIO pins that
can be used for quite a number of things. The CS5535/6 is found on
@@ -642,7 +672,7 @@ config GPIO_BT8XX
tristate "BT8XX GPIO abuser"
depends on PCI && VIDEO_BT848=n
help
- The BT8xx frame grabber chip has 24 GPIO pins than can be abused
+ The BT8xx frame grabber chip has 24 GPIO pins that can be abused
as a cheap PCI GPIO card.
This chip can be found on Miro, Hauppauge and STB TV-cards.
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 5d50179ece16..6309aff1d806 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o
+obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o
obj-$(CONFIG_GPIO_EM) += gpio-em.o
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o
@@ -76,11 +77,11 @@ obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
+obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o
obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
-obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o
obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
@@ -99,3 +100,4 @@ obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
+obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c
index 6fc6206b38bd..b2239d678d01 100644
--- a/drivers/gpio/gpio-adnp.c
+++ b/drivers/gpio/gpio-adnp.c
@@ -408,24 +408,23 @@ static void adnp_irq_bus_unlock(struct irq_data *data)
mutex_unlock(&adnp->irq_lock);
}
-static unsigned int adnp_irq_startup(struct irq_data *data)
+static int adnp_irq_reqres(struct irq_data *data)
{
struct adnp *adnp = irq_data_get_irq_chip_data(data);
- if (gpio_lock_as_irq(&adnp->gpio, data->hwirq))
+ if (gpio_lock_as_irq(&adnp->gpio, data->hwirq)) {
dev_err(adnp->gpio.dev,
"unable to lock HW IRQ %lu for IRQ\n",
data->hwirq);
- /* Satisfy the .enable semantics by unmasking the line */
- adnp_irq_unmask(data);
+ return -EINVAL;
+ }
return 0;
}
-static void adnp_irq_shutdown(struct irq_data *data)
+static void adnp_irq_relres(struct irq_data *data)
{
struct adnp *adnp = irq_data_get_irq_chip_data(data);
- adnp_irq_mask(data);
gpio_unlock_as_irq(&adnp->gpio, data->hwirq);
}
@@ -436,8 +435,8 @@ static struct irq_chip adnp_irq_chip = {
.irq_set_type = adnp_irq_set_type,
.irq_bus_lock = adnp_irq_bus_lock,
.irq_bus_sync_unlock = adnp_irq_bus_unlock,
- .irq_startup = adnp_irq_startup,
- .irq_shutdown = adnp_irq_shutdown,
+ .irq_request_resources = adnp_irq_reqres,
+ .irq_release_resources = adnp_irq_relres,
};
static int adnp_irq_map(struct irq_domain *domain, unsigned int irq,
diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c
index 3f190e68f973..d974020b78bb 100644
--- a/drivers/gpio/gpio-adp5588.c
+++ b/drivers/gpio/gpio-adp5588.c
@@ -67,9 +67,20 @@ static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
{
struct adp5588_gpio *dev =
container_of(chip, struct adp5588_gpio, gpio_chip);
+ unsigned bank = ADP5588_BANK(off);
+ unsigned bit = ADP5588_BIT(off);
+ int val;
- return !!(adp5588_gpio_read(dev->client,
- GPIO_DAT_STAT1 + ADP5588_BANK(off)) & ADP5588_BIT(off));
+ mutex_lock(&dev->lock);
+
+ if (dev->dir[bank] & bit)
+ val = dev->dat_out[bank];
+ else
+ val = adp5588_gpio_read(dev->client, GPIO_DAT_STAT1 + bank);
+
+ mutex_unlock(&dev->lock);
+
+ return !!(val & bit);
}
static void adp5588_gpio_set_value(struct gpio_chip *chip,
@@ -386,6 +397,7 @@ static int adp5588_gpio_probe(struct i2c_client *client,
gc->ngpio = ADP5588_MAXGPIO;
gc->label = client->name;
gc->owner = THIS_MODULE;
+ gc->names = pdata->names;
mutex_init(&dev->lock);
diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c
index f32357e2d78d..3f6b33ce9bd4 100644
--- a/drivers/gpio/gpio-bcm-kona.c
+++ b/drivers/gpio/gpio-bcm-kona.c
@@ -28,6 +28,10 @@
#define GPIO_BANK(gpio) ((gpio) >> 5)
#define GPIO_BIT(gpio) ((gpio) & (GPIO_PER_BANK - 1))
+/* There is a GPIO control register for each GPIO */
+#define GPIO_CONTROL(gpio) (0x00000100 + ((gpio) << 2))
+
+/* The remaining registers are per GPIO bank */
#define GPIO_OUT_STATUS(bank) (0x00000000 + ((bank) << 2))
#define GPIO_IN_STATUS(bank) (0x00000020 + ((bank) << 2))
#define GPIO_OUT_SET(bank) (0x00000040 + ((bank) << 2))
@@ -35,7 +39,6 @@
#define GPIO_INT_STATUS(bank) (0x00000080 + ((bank) << 2))
#define GPIO_INT_MASK(bank) (0x000000a0 + ((bank) << 2))
#define GPIO_INT_MSKCLR(bank) (0x000000c0 + ((bank) << 2))
-#define GPIO_CONTROL(bank) (0x00000100 + ((bank) << 2))
#define GPIO_PWD_STATUS(bank) (0x00000500 + ((bank) << 2))
#define GPIO_GPPWR_OFFSET 0x00000520
@@ -80,22 +83,43 @@ static inline struct bcm_kona_gpio *to_kona_gpio(struct gpio_chip *chip)
return container_of(chip, struct bcm_kona_gpio, gpio_chip);
}
-static void bcm_kona_gpio_set_lockcode_bank(void __iomem *reg_base,
- int bank_id, int lockcode)
+static inline void bcm_kona_gpio_write_lock_regs(void __iomem *reg_base,
+ int bank_id, u32 lockcode)
{
writel(BCM_GPIO_PASSWD, reg_base + GPIO_GPPWR_OFFSET);
writel(lockcode, reg_base + GPIO_PWD_STATUS(bank_id));
}
-static inline void bcm_kona_gpio_lock_bank(void __iomem *reg_base, int bank_id)
+static void bcm_kona_gpio_lock_gpio(struct bcm_kona_gpio *kona_gpio,
+ unsigned gpio)
{
- bcm_kona_gpio_set_lockcode_bank(reg_base, bank_id, LOCK_CODE);
+ u32 val;
+ unsigned long flags;
+ int bank_id = GPIO_BANK(gpio);
+
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ val = readl(kona_gpio->reg_base + GPIO_PWD_STATUS(bank_id));
+ val |= BIT(gpio);
+ bcm_kona_gpio_write_lock_regs(kona_gpio->reg_base, bank_id, val);
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
}
-static inline void bcm_kona_gpio_unlock_bank(void __iomem *reg_base,
- int bank_id)
+static void bcm_kona_gpio_unlock_gpio(struct bcm_kona_gpio *kona_gpio,
+ unsigned gpio)
{
- bcm_kona_gpio_set_lockcode_bank(reg_base, bank_id, UNLOCK_CODE);
+ u32 val;
+ unsigned long flags;
+ int bank_id = GPIO_BANK(gpio);
+
+ spin_lock_irqsave(&kona_gpio->lock, flags);
+
+ val = readl(kona_gpio->reg_base + GPIO_PWD_STATUS(bank_id));
+ val &= ~BIT(gpio);
+ bcm_kona_gpio_write_lock_regs(kona_gpio->reg_base, bank_id, val);
+
+ spin_unlock_irqrestore(&kona_gpio->lock, flags);
}
static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
@@ -110,7 +134,6 @@ static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
kona_gpio = to_kona_gpio(chip);
reg_base = kona_gpio->reg_base;
spin_lock_irqsave(&kona_gpio->lock, flags);
- bcm_kona_gpio_unlock_bank(reg_base, bank_id);
/* determine the GPIO pin direction */
val = readl(reg_base + GPIO_CONTROL(gpio));
@@ -127,7 +150,6 @@ static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
writel(val, reg_base + reg_offset);
out:
- bcm_kona_gpio_lock_bank(reg_base, bank_id);
spin_unlock_irqrestore(&kona_gpio->lock, flags);
}
@@ -143,7 +165,6 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio)
kona_gpio = to_kona_gpio(chip);
reg_base = kona_gpio->reg_base;
spin_lock_irqsave(&kona_gpio->lock, flags);
- bcm_kona_gpio_unlock_bank(reg_base, bank_id);
/* determine the GPIO pin direction */
val = readl(reg_base + GPIO_CONTROL(gpio));
@@ -154,32 +175,43 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio)
GPIO_IN_STATUS(bank_id) : GPIO_OUT_STATUS(bank_id);
val = readl(reg_base + reg_offset);
- bcm_kona_gpio_lock_bank(reg_base, bank_id);
spin_unlock_irqrestore(&kona_gpio->lock, flags);
/* return the specified bit status */
return !!(val & BIT(bit));
}
+static int bcm_kona_gpio_request(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcm_kona_gpio *kona_gpio = to_kona_gpio(chip);
+
+ bcm_kona_gpio_unlock_gpio(kona_gpio, gpio);
+ return 0;
+}
+
+static void bcm_kona_gpio_free(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcm_kona_gpio *kona_gpio = to_kona_gpio(chip);
+
+ bcm_kona_gpio_lock_gpio(kona_gpio, gpio);
+}
+
static int bcm_kona_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
{
struct bcm_kona_gpio *kona_gpio;
void __iomem *reg_base;
u32 val;
unsigned long flags;
- int bank_id = GPIO_BANK(gpio);
kona_gpio = to_kona_gpio(chip);
reg_base = kona_gpio->reg_base;
spin_lock_irqsave(&kona_gpio->lock, flags);
- bcm_kona_gpio_unlock_bank(reg_base, bank_id);
val = readl(reg_base + GPIO_CONTROL(gpio));
val &= ~GPIO_GPCTR0_IOTR_MASK;
val |= GPIO_GPCTR0_IOTR_CMD_INPUT;
writel(val, reg_base + GPIO_CONTROL(gpio));
- bcm_kona_gpio_lock_bank(reg_base, bank_id);
spin_unlock_irqrestore(&kona_gpio->lock, flags);
return 0;
@@ -198,7 +230,6 @@ static int bcm_kona_gpio_direction_output(struct gpio_chip *chip,
kona_gpio = to_kona_gpio(chip);
reg_base = kona_gpio->reg_base;
spin_lock_irqsave(&kona_gpio->lock, flags);
- bcm_kona_gpio_unlock_bank(reg_base, bank_id);
val = readl(reg_base + GPIO_CONTROL(gpio));
val &= ~GPIO_GPCTR0_IOTR_MASK;
@@ -210,7 +241,6 @@ static int bcm_kona_gpio_direction_output(struct gpio_chip *chip,
val |= BIT(bit);
writel(val, reg_base + reg_offset);
- bcm_kona_gpio_lock_bank(reg_base, bank_id);
spin_unlock_irqrestore(&kona_gpio->lock, flags);
return 0;
@@ -233,7 +263,6 @@ static int bcm_kona_gpio_set_debounce(struct gpio_chip *chip, unsigned gpio,
void __iomem *reg_base;
u32 val, res;
unsigned long flags;
- int bank_id = GPIO_BANK(gpio);
kona_gpio = to_kona_gpio(chip);
reg_base = kona_gpio->reg_base;
@@ -257,7 +286,6 @@ static int bcm_kona_gpio_set_debounce(struct gpio_chip *chip, unsigned gpio,
/* spin lock for read-modify-write of the GPIO register */
spin_lock_irqsave(&kona_gpio->lock, flags);
- bcm_kona_gpio_unlock_bank(reg_base, bank_id);
val = readl(reg_base + GPIO_CONTROL(gpio));
val &= ~GPIO_GPCTR0_DBR_MASK;
@@ -272,7 +300,6 @@ static int bcm_kona_gpio_set_debounce(struct gpio_chip *chip, unsigned gpio,
writel(val, reg_base + GPIO_CONTROL(gpio));
- bcm_kona_gpio_lock_bank(reg_base, bank_id);
spin_unlock_irqrestore(&kona_gpio->lock, flags);
return 0;
@@ -281,6 +308,8 @@ static int bcm_kona_gpio_set_debounce(struct gpio_chip *chip, unsigned gpio,
static struct gpio_chip template_chip = {
.label = "bcm-kona-gpio",
.owner = THIS_MODULE,
+ .request = bcm_kona_gpio_request,
+ .free = bcm_kona_gpio_free,
.direction_input = bcm_kona_gpio_direction_input,
.get = bcm_kona_gpio_get,
.direction_output = bcm_kona_gpio_direction_output,
@@ -294,7 +323,7 @@ static void bcm_kona_gpio_irq_ack(struct irq_data *d)
{
struct bcm_kona_gpio *kona_gpio;
void __iomem *reg_base;
- int gpio = d->hwirq;
+ unsigned gpio = d->hwirq;
int bank_id = GPIO_BANK(gpio);
int bit = GPIO_BIT(gpio);
u32 val;
@@ -303,13 +332,11 @@ static void bcm_kona_gpio_irq_ack(struct irq_data *d)
kona_gpio = irq_data_get_irq_chip_data(d);
reg_base = kona_gpio->reg_base;
spin_lock_irqsave(&kona_gpio->lock, flags);
- bcm_kona_gpio_unlock_bank(reg_base, bank_id);
val = readl(reg_base + GPIO_INT_STATUS(bank_id));
val |= BIT(bit);
writel(val, reg_base + GPIO_INT_STATUS(bank_id));
- bcm_kona_gpio_lock_bank(reg_base, bank_id);
spin_unlock_irqrestore(&kona_gpio->lock, flags);
}
@@ -317,7 +344,7 @@ static void bcm_kona_gpio_irq_mask(struct irq_data *d)
{
struct bcm_kona_gpio *kona_gpio;
void __iomem *reg_base;
- int gpio = d->hwirq;
+ unsigned gpio = d->hwirq;
int bank_id = GPIO_BANK(gpio);
int bit = GPIO_BIT(gpio);
u32 val;
@@ -326,13 +353,11 @@ static void bcm_kona_gpio_irq_mask(struct irq_data *d)
kona_gpio = irq_data_get_irq_chip_data(d);
reg_base = kona_gpio->reg_base;
spin_lock_irqsave(&kona_gpio->lock, flags);
- bcm_kona_gpio_unlock_bank(reg_base, bank_id);
val = readl(reg_base + GPIO_INT_MASK(bank_id));
val |= BIT(bit);
writel(val, reg_base + GPIO_INT_MASK(bank_id));
- bcm_kona_gpio_lock_bank(reg_base, bank_id);
spin_unlock_irqrestore(&kona_gpio->lock, flags);
}
@@ -340,7 +365,7 @@ static void bcm_kona_gpio_irq_unmask(struct irq_data *d)
{
struct bcm_kona_gpio *kona_gpio;
void __iomem *reg_base;
- int gpio = d->hwirq;
+ unsigned gpio = d->hwirq;
int bank_id = GPIO_BANK(gpio);
int bit = GPIO_BIT(gpio);
u32 val;
@@ -349,13 +374,11 @@ static void bcm_kona_gpio_irq_unmask(struct irq_data *d)
kona_gpio = irq_data_get_irq_chip_data(d);
reg_base = kona_gpio->reg_base;
spin_lock_irqsave(&kona_gpio->lock, flags);
- bcm_kona_gpio_unlock_bank(reg_base, bank_id);
val = readl(reg_base + GPIO_INT_MSKCLR(bank_id));
val |= BIT(bit);
writel(val, reg_base + GPIO_INT_MSKCLR(bank_id));
- bcm_kona_gpio_lock_bank(reg_base, bank_id);
spin_unlock_irqrestore(&kona_gpio->lock, flags);
}
@@ -363,11 +386,10 @@ static int bcm_kona_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
struct bcm_kona_gpio *kona_gpio;
void __iomem *reg_base;
- int gpio = d->hwirq;
+ unsigned gpio = d->hwirq;
u32 lvl_type;
u32 val;
unsigned long flags;
- int bank_id = GPIO_BANK(gpio);
kona_gpio = irq_data_get_irq_chip_data(d);
reg_base = kona_gpio->reg_base;
@@ -394,14 +416,12 @@ static int bcm_kona_gpio_irq_set_type(struct irq_data *d, unsigned int type)
}
spin_lock_irqsave(&kona_gpio->lock, flags);
- bcm_kona_gpio_unlock_bank(reg_base, bank_id);
val = readl(reg_base + GPIO_CONTROL(gpio));
val &= ~GPIO_GPCTR0_ITR_MASK;
val |= lvl_type << GPIO_GPCTR0_ITR_SHIFT;
writel(val, reg_base + GPIO_CONTROL(gpio));
- bcm_kona_gpio_lock_bank(reg_base, bank_id);
spin_unlock_irqrestore(&kona_gpio->lock, flags);
return 0;
@@ -424,7 +444,6 @@ static void bcm_kona_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
*/
reg_base = bank->kona_gpio->reg_base;
bank_id = bank->id;
- bcm_kona_gpio_unlock_bank(reg_base, bank_id);
while ((sta = readl(reg_base + GPIO_INT_STATUS(bank_id)) &
(~(readl(reg_base + GPIO_INT_MASK(bank_id)))))) {
@@ -444,28 +463,26 @@ static void bcm_kona_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
}
}
- bcm_kona_gpio_lock_bank(reg_base, bank_id);
-
chained_irq_exit(chip, desc);
}
-static unsigned int bcm_kona_gpio_irq_startup(struct irq_data *d)
+static int bcm_kona_gpio_irq_reqres(struct irq_data *d)
{
struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d);
- if (gpio_lock_as_irq(&kona_gpio->gpio_chip, d->hwirq))
+ if (gpio_lock_as_irq(&kona_gpio->gpio_chip, d->hwirq)) {
dev_err(kona_gpio->gpio_chip.dev,
"unable to lock HW IRQ %lu for IRQ\n",
d->hwirq);
- bcm_kona_gpio_irq_unmask(d);
+ return -EINVAL;
+ }
return 0;
}
-static void bcm_kona_gpio_irq_shutdown(struct irq_data *d)
+static void bcm_kona_gpio_irq_relres(struct irq_data *d)
{
struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d);
- bcm_kona_gpio_irq_mask(d);
gpio_unlock_as_irq(&kona_gpio->gpio_chip, d->hwirq);
}
@@ -475,8 +492,8 @@ static struct irq_chip bcm_gpio_irq_chip = {
.irq_mask = bcm_kona_gpio_irq_mask,
.irq_unmask = bcm_kona_gpio_irq_unmask,
.irq_set_type = bcm_kona_gpio_irq_set_type,
- .irq_startup = bcm_kona_gpio_irq_startup,
- .irq_shutdown = bcm_kona_gpio_irq_shutdown,
+ .irq_request_resources = bcm_kona_gpio_irq_reqres,
+ .irq_release_resources = bcm_kona_gpio_irq_relres,
};
static struct __initconst of_device_id bcm_kona_gpio_of_match[] = {
@@ -531,10 +548,12 @@ static void bcm_kona_gpio_reset(struct bcm_kona_gpio *kona_gpio)
reg_base = kona_gpio->reg_base;
/* disable interrupts and clear status */
for (i = 0; i < kona_gpio->num_bank; i++) {
- bcm_kona_gpio_unlock_bank(reg_base, i);
+ /* Unlock the entire bank first */
+ bcm_kona_gpio_write_lock_regs(kona_gpio, i, UNLOCK_CODE);
writel(0xffffffff, reg_base + GPIO_INT_MASK(i));
writel(0xffffffff, reg_base + GPIO_INT_STATUS(i));
- bcm_kona_gpio_lock_bank(reg_base, i);
+ /* Now re-lock the bank */
+ bcm_kona_gpio_write_lock_regs(kona_gpio, i, LOCK_CODE);
}
}
diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c
index 3c2ba2ad0ada..e1e861239e95 100644
--- a/drivers/gpio/gpio-clps711x.c
+++ b/drivers/gpio/gpio-clps711x.c
@@ -65,6 +65,7 @@ static int clps711x_gpio_probe(struct platform_device *pdev)
}
bgc->gc.base = id * 8;
+ bgc->gc.owner = THIS_MODULE;
platform_set_drvdata(pdev, bgc);
return gpiochip_add(&bgc->gc);
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index 7629b4f12b7f..339f9dac591b 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -37,6 +37,8 @@ struct davinci_gpio_regs {
u32 intstat;
};
+typedef struct irq_chip *(*gpio_get_irq_chip_cb_t)(unsigned int irq);
+
#define BINTEN 0x8 /* GPIO Interrupt Per-Bank Enable Register */
#define chip2controller(chip) \
@@ -172,6 +174,27 @@ of_err:
return NULL;
}
+#ifdef CONFIG_OF_GPIO
+static int davinci_gpio_of_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec,
+ u32 *flags)
+{
+ struct davinci_gpio_controller *chips = dev_get_drvdata(gc->dev);
+ struct davinci_gpio_platform_data *pdata = dev_get_platdata(gc->dev);
+
+ if (gpiospec->args[0] > pdata->ngpio)
+ return -EINVAL;
+
+ if (gc != &chips[gpiospec->args[0] / 32].chip)
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpiospec->args[1];
+
+ return gpiospec->args[0] % 32;
+}
+#endif
+
static int davinci_gpio_probe(struct platform_device *pdev)
{
int i, base;
@@ -236,6 +259,9 @@ static int davinci_gpio_probe(struct platform_device *pdev)
chips[i].chip.ngpio = 32;
#ifdef CONFIG_OF_GPIO
+ chips[i].chip.of_gpio_n_cells = 2;
+ chips[i].chip.of_xlate = davinci_gpio_of_xlate;
+ chips[i].chip.dev = dev;
chips[i].chip.of_node = dev->of_node;
#endif
spin_lock_init(&chips[i].lock);
@@ -413,6 +439,26 @@ static const struct irq_domain_ops davinci_gpio_irq_ops = {
.xlate = irq_domain_xlate_onetwocell,
};
+static struct irq_chip *davinci_gpio_get_irq_chip(unsigned int irq)
+{
+ static struct irq_chip_type gpio_unbanked;
+
+ gpio_unbanked = *container_of(irq_get_chip(irq),
+ struct irq_chip_type, chip);
+
+ return &gpio_unbanked.chip;
+};
+
+static struct irq_chip *keystone_gpio_get_irq_chip(unsigned int irq)
+{
+ static struct irq_chip gpio_unbanked;
+
+ gpio_unbanked = *irq_get_chip(irq);
+ return &gpio_unbanked;
+};
+
+static const struct of_device_id davinci_gpio_ids[];
+
/*
* NOTE: for suspend/resume, probably best to make a platform_device with
* suspend_late/resume_resume calls hooking into results of the set_wake()
@@ -423,7 +469,8 @@ static const struct irq_domain_ops davinci_gpio_irq_ops = {
static int davinci_gpio_irq_setup(struct platform_device *pdev)
{
- unsigned gpio, irq, bank;
+ unsigned gpio, bank;
+ int irq;
struct clk *clk;
u32 binten = 0;
unsigned ngpio, bank_irq;
@@ -433,6 +480,18 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
struct davinci_gpio_platform_data *pdata = dev->platform_data;
struct davinci_gpio_regs __iomem *g;
struct irq_domain *irq_domain = NULL;
+ const struct of_device_id *match;
+ struct irq_chip *irq_chip;
+ gpio_get_irq_chip_cb_t gpio_get_irq_chip;
+
+ /*
+ * Use davinci_gpio_get_irq_chip by default to handle non DT cases
+ */
+ gpio_get_irq_chip = davinci_gpio_get_irq_chip;
+ match = of_match_device(of_match_ptr(davinci_gpio_ids),
+ dev);
+ if (match)
+ gpio_get_irq_chip = (gpio_get_irq_chip_cb_t)match->data;
ngpio = pdata->ngpio;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
@@ -489,8 +548,6 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
* IRQ mux conflicts; gpio_irq_type_unbanked() is only for GPIOs.
*/
if (pdata->gpio_unbanked) {
- static struct irq_chip_type gpio_unbanked;
-
/* pass "bank 0" GPIO IRQs to AINTC */
chips[0].chip.to_irq = gpio_to_irq_unbanked;
chips[0].gpio_irq = bank_irq;
@@ -499,10 +556,9 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
/* AINTC handles mask/unmask; GPIO handles triggering */
irq = bank_irq;
- gpio_unbanked = *container_of(irq_get_chip(irq),
- struct irq_chip_type, chip);
- gpio_unbanked.chip.name = "GPIO-AINTC";
- gpio_unbanked.chip.irq_set_type = gpio_irq_type_unbanked;
+ irq_chip = gpio_get_irq_chip(irq);
+ irq_chip->name = "GPIO-AINTC";
+ irq_chip->irq_set_type = gpio_irq_type_unbanked;
/* default trigger: both edges */
g = gpio2regs(0);
@@ -511,7 +567,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
/* set the direct IRQs up to use that irqchip */
for (gpio = 0; gpio < pdata->gpio_unbanked; gpio++, irq++) {
- irq_set_chip(irq, &gpio_unbanked.chip);
+ irq_set_chip(irq, irq_chip);
irq_set_handler_data(irq, &chips[gpio / 32]);
irq_set_status_flags(irq, IRQ_TYPE_EDGE_BOTH);
}
@@ -554,7 +610,8 @@ done:
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id davinci_gpio_ids[] = {
- { .compatible = "ti,dm6441-gpio", },
+ { .compatible = "ti,keystone-gpio", keystone_gpio_get_irq_chip},
+ { .compatible = "ti,dm6441-gpio", davinci_gpio_get_irq_chip},
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, davinci_gpio_ids);
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c
new file mode 100644
index 000000000000..ed5711f77e2d
--- /dev/null
+++ b/drivers/gpio/gpio-dwapb.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2011 Jamie Iles
+ *
+ * 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.
+ *
+ * All enquiries to support@picochip.com
+ */
+#include <linux/basic_mmio_gpio.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/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>
+#include <linux/spinlock.h>
+
+#define GPIO_SWPORTA_DR 0x00
+#define GPIO_SWPORTA_DDR 0x04
+#define GPIO_SWPORTB_DR 0x0c
+#define GPIO_SWPORTB_DDR 0x10
+#define GPIO_SWPORTC_DR 0x18
+#define GPIO_SWPORTC_DDR 0x1c
+#define GPIO_SWPORTD_DR 0x24
+#define GPIO_SWPORTD_DDR 0x28
+#define GPIO_INTEN 0x30
+#define GPIO_INTMASK 0x34
+#define GPIO_INTTYPE_LEVEL 0x38
+#define GPIO_INT_POLARITY 0x3c
+#define GPIO_INTSTATUS 0x40
+#define GPIO_PORTA_EOI 0x4c
+#define GPIO_EXT_PORTA 0x50
+#define GPIO_EXT_PORTB 0x54
+#define GPIO_EXT_PORTC 0x58
+#define GPIO_EXT_PORTD 0x5c
+
+#define DWAPB_MAX_PORTS 4
+#define GPIO_EXT_PORT_SIZE (GPIO_EXT_PORTB - GPIO_EXT_PORTA)
+#define GPIO_SWPORT_DR_SIZE (GPIO_SWPORTB_DR - GPIO_SWPORTA_DR)
+#define GPIO_SWPORT_DDR_SIZE (GPIO_SWPORTB_DDR - GPIO_SWPORTA_DDR)
+
+struct dwapb_gpio;
+
+struct dwapb_gpio_port {
+ struct bgpio_chip bgc;
+ bool is_registered;
+ struct dwapb_gpio *gpio;
+};
+
+struct dwapb_gpio {
+ struct device *dev;
+ void __iomem *regs;
+ struct dwapb_gpio_port *ports;
+ unsigned int nr_ports;
+ struct irq_domain *domain;
+};
+
+static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ struct dwapb_gpio_port *port = container_of(bgc, struct
+ dwapb_gpio_port, bgc);
+ struct dwapb_gpio *gpio = port->gpio;
+
+ return irq_find_mapping(gpio->domain, offset);
+}
+
+static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
+{
+ u32 v = readl(gpio->regs + GPIO_INT_POLARITY);
+
+ if (gpio_get_value(gpio->ports[0].bgc.gc.base + offs))
+ v &= ~BIT(offs);
+ else
+ v |= BIT(offs);
+
+ writel(v, gpio->regs + GPIO_INT_POLARITY);
+}
+
+static void dwapb_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ struct dwapb_gpio *gpio = irq_get_handler_data(irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 irq_status = readl_relaxed(gpio->regs + GPIO_INTSTATUS);
+
+ while (irq_status) {
+ int hwirq = fls(irq_status) - 1;
+ int gpio_irq = irq_find_mapping(gpio->domain, hwirq);
+
+ generic_handle_irq(gpio_irq);
+ irq_status &= ~BIT(hwirq);
+
+ if ((irq_get_trigger_type(gpio_irq) & IRQ_TYPE_SENSE_MASK)
+ == IRQ_TYPE_EDGE_BOTH)
+ dwapb_toggle_trigger(gpio, hwirq);
+ }
+
+ if (chip->irq_eoi)
+ chip->irq_eoi(irq_desc_get_irq_data(desc));
+}
+
+static void dwapb_irq_enable(struct irq_data *d)
+{
+ struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = igc->private;
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+ val = readl(gpio->regs + GPIO_INTEN);
+ val |= BIT(d->hwirq);
+ writel(val, gpio->regs + GPIO_INTEN);
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static void dwapb_irq_disable(struct irq_data *d)
+{
+ struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = igc->private;
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+ val = readl(gpio->regs + GPIO_INTEN);
+ val &= ~BIT(d->hwirq);
+ writel(val, gpio->regs + GPIO_INTEN);
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static int dwapb_irq_reqres(struct irq_data *d)
+{
+ struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = igc->private;
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+
+ if (gpio_lock_as_irq(&bgc->gc, irqd_to_hwirq(d))) {
+ dev_err(gpio->dev, "unable to lock HW IRQ %lu for IRQ\n",
+ irqd_to_hwirq(d));
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void dwapb_irq_relres(struct irq_data *d)
+{
+ struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = igc->private;
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+
+ gpio_unlock_as_irq(&bgc->gc, irqd_to_hwirq(d));
+}
+
+static int dwapb_irq_set_type(struct irq_data *d, u32 type)
+{
+ struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
+ struct dwapb_gpio *gpio = igc->private;
+ struct bgpio_chip *bgc = &gpio->ports[0].bgc;
+ int bit = d->hwirq;
+ unsigned long level, polarity, flags;
+
+ if (type & ~(IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING |
+ IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
+ return -EINVAL;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+ level = readl(gpio->regs + GPIO_INTTYPE_LEVEL);
+ polarity = readl(gpio->regs + GPIO_INT_POLARITY);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ level |= BIT(bit);
+ dwapb_toggle_trigger(gpio, bit);
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ level |= BIT(bit);
+ polarity |= BIT(bit);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ level |= BIT(bit);
+ polarity &= ~BIT(bit);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ level &= ~BIT(bit);
+ polarity |= BIT(bit);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ level &= ~BIT(bit);
+ polarity &= ~BIT(bit);
+ break;
+ }
+
+ writel(level, gpio->regs + GPIO_INTTYPE_LEVEL);
+ writel(polarity, gpio->regs + GPIO_INT_POLARITY);
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+
+static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
+ struct dwapb_gpio_port *port)
+{
+ struct gpio_chip *gc = &port->bgc.gc;
+ struct device_node *node = gc->of_node;
+ struct irq_chip_generic *irq_gc;
+ unsigned int hwirq, ngpio = gc->ngpio;
+ struct irq_chip_type *ct;
+ int err, irq;
+
+ irq = irq_of_parse_and_map(node, 0);
+ if (!irq) {
+ dev_warn(gpio->dev, "no irq for bank %s\n",
+ port->bgc.gc.of_node->full_name);
+ return;
+ }
+
+ gpio->domain = irq_domain_add_linear(node, ngpio,
+ &irq_generic_chip_ops, gpio);
+ if (!gpio->domain)
+ return;
+
+ err = irq_alloc_domain_generic_chips(gpio->domain, ngpio, 1,
+ "gpio-dwapb", handle_level_irq,
+ IRQ_NOREQUEST, 0,
+ IRQ_GC_INIT_NESTED_LOCK);
+ if (err) {
+ dev_info(gpio->dev, "irq_alloc_domain_generic_chips failed\n");
+ irq_domain_remove(gpio->domain);
+ gpio->domain = NULL;
+ return;
+ }
+
+ irq_gc = irq_get_domain_generic_chip(gpio->domain, 0);
+ if (!irq_gc) {
+ irq_domain_remove(gpio->domain);
+ gpio->domain = NULL;
+ return;
+ }
+
+ irq_gc->reg_base = gpio->regs;
+ irq_gc->private = gpio;
+
+ ct = irq_gc->chip_types;
+ ct->chip.irq_ack = irq_gc_ack_set_bit;
+ ct->chip.irq_mask = irq_gc_mask_set_bit;
+ ct->chip.irq_unmask = irq_gc_mask_clr_bit;
+ ct->chip.irq_set_type = dwapb_irq_set_type;
+ ct->chip.irq_enable = dwapb_irq_enable;
+ ct->chip.irq_disable = dwapb_irq_disable;
+ ct->chip.irq_request_resources = dwapb_irq_reqres;
+ ct->chip.irq_release_resources = dwapb_irq_relres;
+ ct->regs.ack = GPIO_PORTA_EOI;
+ ct->regs.mask = GPIO_INTMASK;
+
+ irq_setup_generic_chip(irq_gc, IRQ_MSK(port->bgc.gc.ngpio),
+ IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0);
+
+ irq_set_chained_handler(irq, dwapb_irq_handler);
+ irq_set_handler_data(irq, gpio);
+
+ for (hwirq = 0 ; hwirq < ngpio ; hwirq++)
+ irq_create_mapping(gpio->domain, hwirq);
+
+ port->bgc.gc.to_irq = dwapb_gpio_to_irq;
+}
+
+static void dwapb_irq_teardown(struct dwapb_gpio *gpio)
+{
+ struct dwapb_gpio_port *port = &gpio->ports[0];
+ struct gpio_chip *gc = &port->bgc.gc;
+ unsigned int ngpio = gc->ngpio;
+ irq_hw_number_t hwirq;
+
+ if (!gpio->domain)
+ return;
+
+ for (hwirq = 0 ; hwirq < ngpio ; hwirq++)
+ irq_dispose_mapping(irq_find_mapping(gpio->domain, hwirq));
+
+ irq_domain_remove(gpio->domain);
+ gpio->domain = NULL;
+}
+
+static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
+ struct device_node *port_np,
+ unsigned int offs)
+{
+ struct dwapb_gpio_port *port;
+ u32 port_idx, ngpio;
+ void __iomem *dat, *set, *dirout;
+ int err;
+
+ if (of_property_read_u32(port_np, "reg", &port_idx) ||
+ port_idx >= DWAPB_MAX_PORTS) {
+ dev_err(gpio->dev, "missing/invalid port index for %s\n",
+ port_np->full_name);
+ return -EINVAL;
+ }
+
+ port = &gpio->ports[offs];
+ port->gpio = gpio;
+
+ if (of_property_read_u32(port_np, "snps,nr-gpios", &ngpio)) {
+ dev_info(gpio->dev, "failed to get number of gpios for %s\n",
+ port_np->full_name);
+ ngpio = 32;
+ }
+
+ dat = gpio->regs + GPIO_EXT_PORTA + (port_idx * GPIO_EXT_PORT_SIZE);
+ set = gpio->regs + GPIO_SWPORTA_DR + (port_idx * GPIO_SWPORT_DR_SIZE);
+ dirout = gpio->regs + GPIO_SWPORTA_DDR +
+ (port_idx * GPIO_SWPORT_DDR_SIZE);
+
+ err = bgpio_init(&port->bgc, gpio->dev, 4, dat, set, NULL, dirout,
+ NULL, false);
+ if (err) {
+ dev_err(gpio->dev, "failed to init gpio chip for %s\n",
+ port_np->full_name);
+ return err;
+ }
+
+ port->bgc.gc.ngpio = ngpio;
+ port->bgc.gc.of_node = port_np;
+
+ /*
+ * Only port A can provide interrupts in all configurations of the IP.
+ */
+ if (port_idx == 0 &&
+ of_property_read_bool(port_np, "interrupt-controller"))
+ dwapb_configure_irqs(gpio, port);
+
+ err = gpiochip_add(&port->bgc.gc);
+ if (err)
+ dev_err(gpio->dev, "failed to register gpiochip for %s\n",
+ port_np->full_name);
+ else
+ port->is_registered = true;
+
+ return err;
+}
+
+static void dwapb_gpio_unregister(struct dwapb_gpio *gpio)
+{
+ unsigned int m;
+
+ for (m = 0; m < gpio->nr_ports; ++m)
+ if (gpio->ports[m].is_registered)
+ WARN_ON(gpiochip_remove(&gpio->ports[m].bgc.gc));
+}
+
+static int dwapb_gpio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct dwapb_gpio *gpio;
+ struct device_node *np;
+ int err;
+ unsigned int offs = 0;
+
+ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+ gpio->dev = &pdev->dev;
+
+ gpio->nr_ports = of_get_child_count(pdev->dev.of_node);
+ if (!gpio->nr_ports) {
+ err = -EINVAL;
+ goto out_err;
+ }
+ gpio->ports = devm_kzalloc(&pdev->dev, gpio->nr_ports *
+ sizeof(*gpio->ports), GFP_KERNEL);
+ if (!gpio->ports) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gpio->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpio->regs)) {
+ err = PTR_ERR(gpio->regs);
+ goto out_err;
+ }
+
+ for_each_child_of_node(pdev->dev.of_node, np) {
+ err = dwapb_gpio_add_port(gpio, np, offs++);
+ if (err)
+ goto out_unregister;
+ }
+ platform_set_drvdata(pdev, gpio);
+
+ return 0;
+
+out_unregister:
+ dwapb_gpio_unregister(gpio);
+ dwapb_irq_teardown(gpio);
+
+out_err:
+ return err;
+}
+
+static int dwapb_gpio_remove(struct platform_device *pdev)
+{
+ struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
+
+ dwapb_gpio_unregister(gpio);
+ dwapb_irq_teardown(gpio);
+
+ return 0;
+}
+
+static const struct of_device_id dwapb_of_match[] = {
+ { .compatible = "snps,dw-apb-gpio" },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dwapb_of_match);
+
+static struct platform_driver dwapb_gpio_driver = {
+ .driver = {
+ .name = "gpio-dwapb",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(dwapb_of_match),
+ },
+ .probe = dwapb_gpio_probe,
+ .remove = dwapb_gpio_remove,
+};
+
+module_platform_driver(dwapb_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jamie Iles");
+MODULE_DESCRIPTION("Synopsys DesignWare APB GPIO driver");
diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c
index 1e98a9873967..8765bd6f48e1 100644
--- a/drivers/gpio/gpio-em.c
+++ b/drivers/gpio/gpio-em.c
@@ -99,23 +99,23 @@ static void em_gio_irq_enable(struct irq_data *d)
em_gio_write(p, GIO_IEN, BIT(irqd_to_hwirq(d)));
}
-static unsigned int em_gio_irq_startup(struct irq_data *d)
+static int em_gio_irq_reqres(struct irq_data *d)
{
struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
- if (gpio_lock_as_irq(&p->gpio_chip, irqd_to_hwirq(d)))
+ if (gpio_lock_as_irq(&p->gpio_chip, irqd_to_hwirq(d))) {
dev_err(p->gpio_chip.dev,
"unable to lock HW IRQ %lu for IRQ\n",
irqd_to_hwirq(d));
- em_gio_irq_enable(d);
+ return -EINVAL;
+ }
return 0;
}
-static void em_gio_irq_shutdown(struct irq_data *d)
+static void em_gio_irq_relres(struct irq_data *d)
{
struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
- em_gio_irq_disable(d);
gpio_unlock_as_irq(&p->gpio_chip, irqd_to_hwirq(d));
}
@@ -359,8 +359,8 @@ static int em_gio_probe(struct platform_device *pdev)
irq_chip->irq_mask = em_gio_irq_disable;
irq_chip->irq_unmask = em_gio_irq_enable;
irq_chip->irq_set_type = em_gio_irq_set_type;
- irq_chip->irq_startup = em_gio_irq_startup;
- irq_chip->irq_shutdown = em_gio_irq_shutdown;
+ irq_chip->irq_request_resources = em_gio_irq_reqres;
+ irq_chip->irq_release_resources = em_gio_irq_relres;
irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND;
p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
index d2196bf73847..b5dff9e742f8 100644
--- a/drivers/gpio/gpio-generic.c
+++ b/drivers/gpio/gpio-generic.c
@@ -139,7 +139,7 @@ static int bgpio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
- return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio);
+ return !!(bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio));
}
static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
@@ -488,7 +488,7 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
void __iomem *dirout;
void __iomem *dirin;
unsigned long sz;
- unsigned long flags = 0;
+ unsigned long flags = pdev->id_entry->driver_data;
int err;
struct bgpio_chip *bgc;
struct bgpio_pdata *pdata = dev_get_platdata(dev);
@@ -519,9 +519,6 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
if (err)
return err;
- if (!strcmp(platform_get_device_id(pdev)->name, "basic-mmio-gpio-be"))
- flags |= BGPIOF_BIG_ENDIAN;
-
bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL);
if (!bgc)
return -ENOMEM;
@@ -531,6 +528,8 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
return err;
if (pdata) {
+ if (pdata->label)
+ bgc->gc.label = pdata->label;
bgc->gc.base = pdata->base;
if (pdata->ngpio > 0)
bgc->gc.ngpio = pdata->ngpio;
@@ -549,9 +548,14 @@ static int bgpio_pdev_remove(struct platform_device *pdev)
}
static const struct platform_device_id bgpio_id_table[] = {
- { "basic-mmio-gpio", },
- { "basic-mmio-gpio-be", },
- {},
+ {
+ .name = "basic-mmio-gpio",
+ .driver_data = 0,
+ }, {
+ .name = "basic-mmio-gpio-be",
+ .driver_data = BGPIOF_BIG_ENDIAN,
+ },
+ { }
};
MODULE_DEVICE_TABLE(platform, bgpio_id_table);
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
index f5bf3c38bca6..e73c6755a5eb 100644
--- a/drivers/gpio/gpio-ich.c
+++ b/drivers/gpio/gpio-ich.c
@@ -1,5 +1,5 @@
/*
- * Intel ICH6-10, Series 5 and 6 GPIO driver
+ * Intel ICH6-10, Series 5 and 6, Atom C2000 (Avoton/Rangeley) GPIO driver
*
* Copyright (C) 2010 Extreme Engineering Solutions.
*
@@ -55,6 +55,16 @@ static const u8 ichx_reglen[3] = {
0x30, 0x10, 0x10,
};
+static const u8 avoton_regs[4][3] = {
+ {0x00, 0x80, 0x00},
+ {0x04, 0x84, 0x00},
+ {0x08, 0x88, 0x00},
+};
+
+static const u8 avoton_reglen[3] = {
+ 0x10, 0x10, 0x00,
+};
+
#define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start)
#define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start)
@@ -62,6 +72,13 @@ struct ichx_desc {
/* Max GPIO pins the chipset can have */
uint ngpio;
+ /* chipset registers */
+ const u8 (*regs)[3];
+ const u8 *reglen;
+
+ /* GPO_BLINK is available on this chipset */
+ bool have_blink;
+
/* Whether the chipset has GPIO in GPE0_STS in the PM IO region */
bool uses_gpe0;
@@ -71,6 +88,12 @@ struct ichx_desc {
/* Some chipsets have quirks, let these use their own request/get */
int (*request)(struct gpio_chip *chip, unsigned offset);
int (*get)(struct gpio_chip *chip, unsigned offset);
+
+ /*
+ * Some chipsets don't let reading output values on GPIO_LVL register
+ * this option allows driver caching written output values
+ */
+ bool use_outlvl_cache;
};
static struct {
@@ -82,6 +105,7 @@ static struct {
struct ichx_desc *desc; /* Pointer to chipset-specific description */
u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */
u8 use_gpio; /* Which GPIO groups are usable */
+ int outlvl_cache[3]; /* cached output values */
} ichx_priv;
static int modparam_gpiobase = -1; /* dynamic */
@@ -99,13 +123,23 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
spin_lock_irqsave(&ichx_priv.lock, flags);
- data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
+ if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache)
+ data = ichx_priv.outlvl_cache[reg_nr];
+ else
+ data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
+ ichx_priv.gpio_base);
+
if (val)
data |= 1 << bit;
else
data &= ~(1 << bit);
- ICHX_WRITE(data, ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
- tmp = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
+ ICHX_WRITE(data, ichx_priv.desc->regs[reg][reg_nr],
+ ichx_priv.gpio_base);
+ if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache)
+ ichx_priv.outlvl_cache[reg_nr] = data;
+
+ tmp = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
+ ichx_priv.gpio_base);
if (verify && data != tmp)
ret = -EPERM;
@@ -123,7 +157,11 @@ static int ichx_read_bit(int reg, unsigned nr)
spin_lock_irqsave(&ichx_priv.lock, flags);
- data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
+ data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
+ ichx_priv.gpio_base);
+
+ if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache)
+ data = ichx_priv.outlvl_cache[reg_nr] | data;
spin_unlock_irqrestore(&ichx_priv.lock, flags);
@@ -151,7 +189,7 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
int val)
{
/* Disable blink hardware which is available for GPIOs from 0 to 31. */
- if (nr < 32)
+ if (nr < 32 && ichx_priv.desc->have_blink)
ichx_write_bit(GPO_BLINK, nr, 0, 0);
/* Set GPIO output value. */
@@ -266,6 +304,7 @@ static struct ichx_desc ich6_desc = {
.uses_gpe0 = true,
.ngpio = 50,
+ .have_blink = true,
};
/* Intel 3100 */
@@ -290,24 +329,49 @@ static struct ichx_desc i3100_desc = {
/* ICH7 and ICH8-based */
static struct ichx_desc ich7_desc = {
.ngpio = 50,
+ .have_blink = true,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
};
/* ICH9-based */
static struct ichx_desc ich9_desc = {
.ngpio = 61,
+ .have_blink = true,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
};
/* ICH10-based - Consumer/corporate versions have different amount of GPIO */
static struct ichx_desc ich10_cons_desc = {
.ngpio = 61,
+ .have_blink = true,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
};
static struct ichx_desc ich10_corp_desc = {
.ngpio = 72,
+ .have_blink = true,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
};
/* Intel 5 series, 6 series, 3400 series, and C200 series */
static struct ichx_desc intel5_desc = {
.ngpio = 76,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
+};
+
+/* Avoton */
+static struct ichx_desc avoton_desc = {
+ /* Avoton has only 59 GPIOs, but we assume the first set of register
+ * (Core) has 32 instead of 31 to keep gpio-ich compliance
+ */
+ .ngpio = 60,
+ .regs = avoton_regs,
+ .reglen = avoton_reglen,
+ .use_outlvl_cache = true,
};
static int ichx_gpio_request_regions(struct resource *res_base,
@@ -318,11 +382,12 @@ static int ichx_gpio_request_regions(struct resource *res_base,
if (!res_base || !res_base->start || !res_base->end)
return -ENODEV;
- for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) {
+ for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) {
if (!(use_gpio & (1 << i)))
continue;
- if (!request_region(res_base->start + ichx_regs[0][i],
- ichx_reglen[i], name))
+ if (!request_region(
+ res_base->start + ichx_priv.desc->regs[0][i],
+ ichx_priv.desc->reglen[i], name))
goto request_err;
}
return 0;
@@ -332,8 +397,8 @@ request_err:
for (i--; i >= 0; i--) {
if (!(use_gpio & (1 << i)))
continue;
- release_region(res_base->start + ichx_regs[0][i],
- ichx_reglen[i]);
+ release_region(res_base->start + ichx_priv.desc->regs[0][i],
+ ichx_priv.desc->reglen[i]);
}
return -EBUSY;
}
@@ -342,11 +407,11 @@ static void ichx_gpio_release_regions(struct resource *res_base, u8 use_gpio)
{
int i;
- for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) {
+ for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) {
if (!(use_gpio & (1 << i)))
continue;
- release_region(res_base->start + ichx_regs[0][i],
- ichx_reglen[i]);
+ release_region(res_base->start + ichx_priv.desc->regs[0][i],
+ ichx_priv.desc->reglen[i]);
}
}
@@ -383,6 +448,9 @@ static int ichx_gpio_probe(struct platform_device *pdev)
case ICH_V10CONS_GPIO:
ichx_priv.desc = &ich10_cons_desc;
break;
+ case AVOTON_GPIO:
+ ichx_priv.desc = &avoton_desc;
+ break;
default:
return -ENODEV;
}
diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c
index e585163f1ad5..118a6bf455d9 100644
--- a/drivers/gpio/gpio-intel-mid.c
+++ b/drivers/gpio/gpio-intel-mid.c
@@ -1,7 +1,7 @@
/*
- * Moorestown platform Langwell chip GPIO driver
+ * Intel MID GPIO driver
*
- * Copyright (c) 2008, 2009, 2013, Intel Corporation.
+ * Copyright (c) 2008-2014 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
@@ -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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Supports:
@@ -235,23 +231,23 @@ static void intel_mid_irq_mask(struct irq_data *d)
{
}
-static unsigned int intel_mid_irq_startup(struct irq_data *d)
+static int intel_mid_irq_reqres(struct irq_data *d)
{
struct intel_mid_gpio *priv = irq_data_get_irq_chip_data(d);
- if (gpio_lock_as_irq(&priv->chip, irqd_to_hwirq(d)))
+ if (gpio_lock_as_irq(&priv->chip, irqd_to_hwirq(d))) {
dev_err(priv->chip.dev,
"unable to lock HW IRQ %lu for IRQ\n",
irqd_to_hwirq(d));
- intel_mid_irq_unmask(d);
+ return -EINVAL;
+ }
return 0;
}
-static void intel_mid_irq_shutdown(struct irq_data *d)
+static void intel_mid_irq_relres(struct irq_data *d)
{
struct intel_mid_gpio *priv = irq_data_get_irq_chip_data(d);
- intel_mid_irq_mask(d);
gpio_unlock_as_irq(&priv->chip, irqd_to_hwirq(d));
}
@@ -260,8 +256,8 @@ static struct irq_chip intel_mid_irqchip = {
.irq_mask = intel_mid_irq_mask,
.irq_unmask = intel_mid_irq_unmask,
.irq_set_type = intel_mid_irq_type,
- .irq_startup = intel_mid_irq_startup,
- .irq_shutdown = intel_mid_irq_shutdown,
+ .irq_request_resources = intel_mid_irq_reqres,
+ .irq_release_resources = intel_mid_irq_relres,
};
static const struct intel_mid_gpio_ddata gpio_lincroft = {
diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c
index c22a61be3a9c..0a5e9d3f308c 100644
--- a/drivers/gpio/gpio-iop.c
+++ b/drivers/gpio/gpio-iop.c
@@ -111,6 +111,8 @@ static int iop3xx_gpio_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
return gpiochip_add(&iop3xx_chip);
}
diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c
index 66b18535b5ae..9a82a9074a2c 100644
--- a/drivers/gpio/gpio-lynxpoint.c
+++ b/drivers/gpio/gpio-lynxpoint.c
@@ -188,7 +188,7 @@ static int lp_irq_type(struct irq_data *d, unsigned type)
static int lp_gpio_get(struct gpio_chip *chip, unsigned offset)
{
unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1);
- return inl(reg) & IN_LVL_BIT;
+ return !!(inl(reg) & IN_LVL_BIT);
}
static void lp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
@@ -301,23 +301,23 @@ static void lp_irq_disable(struct irq_data *d)
spin_unlock_irqrestore(&lg->lock, flags);
}
-static unsigned int lp_irq_startup(struct irq_data *d)
+static int lp_irq_reqres(struct irq_data *d)
{
struct lp_gpio *lg = irq_data_get_irq_chip_data(d);
- if (gpio_lock_as_irq(&lg->chip, irqd_to_hwirq(d)))
+ if (gpio_lock_as_irq(&lg->chip, irqd_to_hwirq(d))) {
dev_err(lg->chip.dev,
"unable to lock HW IRQ %lu for IRQ\n",
irqd_to_hwirq(d));
- lp_irq_enable(d);
+ return -EINVAL;
+ }
return 0;
}
-static void lp_irq_shutdown(struct irq_data *d)
+static void lp_irq_relres(struct irq_data *d)
{
struct lp_gpio *lg = irq_data_get_irq_chip_data(d);
- lp_irq_disable(d);
gpio_unlock_as_irq(&lg->chip, irqd_to_hwirq(d));
}
@@ -328,8 +328,8 @@ static struct irq_chip lp_irqchip = {
.irq_enable = lp_irq_enable,
.irq_disable = lp_irq_disable,
.irq_set_type = lp_irq_type,
- .irq_startup = lp_irq_startup,
- .irq_shutdown = lp_irq_shutdown,
+ .irq_request_resources = lp_irq_reqres,
+ .irq_release_resources = lp_irq_relres,
.flags = IRQCHIP_SKIP_SET_WAKE,
};
diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c
index 36cb290764b6..7c36f2b0983d 100644
--- a/drivers/gpio/gpio-max732x.c
+++ b/drivers/gpio/gpio-max732x.c
@@ -622,6 +622,13 @@ static int max732x_probe(struct i2c_client *client,
goto out_failed;
}
+ if (nr_port > 8 && !chip->client_dummy) {
+ dev_err(&client->dev,
+ "Failed to allocate second group I2C device\n");
+ ret = -ENODEV;
+ goto out_failed;
+ }
+
mutex_init(&chip->lock);
max732x_readb(chip, is_group_a(chip, 0), &chip->reg_out[0]);
@@ -647,6 +654,8 @@ static int max732x_probe(struct i2c_client *client,
return 0;
out_failed:
+ if (chip->client_dummy)
+ i2c_unregister_device(chip->client_dummy);
max732x_irq_teardown(chip);
return ret;
}
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c
index 1ac288ea810d..99a68310e7c0 100644
--- a/drivers/gpio/gpio-mcp23s08.c
+++ b/drivers/gpio/gpio-mcp23s08.c
@@ -173,7 +173,7 @@ static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
tx[0] = mcp->addr | 0x01;
tx[1] = reg;
- status = spi_write_then_read(mcp->data, tx, sizeof tx, rx, sizeof rx);
+ status = spi_write_then_read(mcp->data, tx, sizeof(tx), rx, sizeof(rx));
return (status < 0) ? status : rx[0];
}
@@ -184,7 +184,7 @@ static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
tx[0] = mcp->addr;
tx[1] = reg;
tx[2] = val;
- return spi_write_then_read(mcp->data, tx, sizeof tx, NULL, 0);
+ return spi_write_then_read(mcp->data, tx, sizeof(tx), NULL, 0);
}
static int
@@ -193,13 +193,13 @@ mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
u8 tx[2], *tmp;
int status;
- if ((n + reg) > sizeof mcp->cache)
+ if ((n + reg) > sizeof(mcp->cache))
return -EINVAL;
tx[0] = mcp->addr | 0x01;
tx[1] = reg;
tmp = (u8 *)vals;
- status = spi_write_then_read(mcp->data, tx, sizeof tx, tmp, n);
+ status = spi_write_then_read(mcp->data, tx, sizeof(tx), tmp, n);
if (status >= 0) {
while (n--)
vals[n] = tmp[n]; /* expand to 16bit */
@@ -214,7 +214,7 @@ static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg)
tx[0] = mcp->addr | 0x01;
tx[1] = reg << 1;
- status = spi_write_then_read(mcp->data, tx, sizeof tx, rx, sizeof rx);
+ status = spi_write_then_read(mcp->data, tx, sizeof(tx), rx, sizeof(rx));
return (status < 0) ? status : (rx[0] | (rx[1] << 8));
}
@@ -226,7 +226,7 @@ static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
tx[1] = reg << 1;
tx[2] = val;
tx[3] = val >> 8;
- return spi_write_then_read(mcp->data, tx, sizeof tx, NULL, 0);
+ return spi_write_then_read(mcp->data, tx, sizeof(tx), NULL, 0);
}
static int
@@ -235,12 +235,12 @@ mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
u8 tx[2];
int status;
- if ((n + reg) > sizeof mcp->cache)
+ if ((n + reg) > sizeof(mcp->cache))
return -EINVAL;
tx[0] = mcp->addr | 0x01;
tx[1] = reg << 1;
- status = spi_write_then_read(mcp->data, tx, sizeof tx,
+ status = spi_write_then_read(mcp->data, tx, sizeof(tx),
(u8 *)vals, n * 2);
if (status >= 0) {
while (n--)
@@ -440,24 +440,24 @@ static void mcp23s08_irq_bus_unlock(struct irq_data *data)
mutex_unlock(&mcp->irq_lock);
}
-static unsigned int mcp23s08_irq_startup(struct irq_data *data)
+static int mcp23s08_irq_reqres(struct irq_data *data)
{
struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
- if (gpio_lock_as_irq(&mcp->chip, data->hwirq))
+ if (gpio_lock_as_irq(&mcp->chip, data->hwirq)) {
dev_err(mcp->chip.dev,
"unable to lock HW IRQ %lu for IRQ usage\n",
data->hwirq);
+ return -EINVAL;
+ }
- mcp23s08_irq_unmask(data);
return 0;
}
-static void mcp23s08_irq_shutdown(struct irq_data *data)
+static void mcp23s08_irq_relres(struct irq_data *data)
{
struct mcp23s08 *mcp = irq_data_get_irq_chip_data(data);
- mcp23s08_irq_mask(data);
gpio_unlock_as_irq(&mcp->chip, data->hwirq);
}
@@ -468,8 +468,8 @@ static struct irq_chip mcp23s08_irq_chip = {
.irq_set_type = mcp23s08_irq_set_type,
.irq_bus_lock = mcp23s08_irq_bus_lock,
.irq_bus_sync_unlock = mcp23s08_irq_bus_unlock,
- .irq_startup = mcp23s08_irq_startup,
- .irq_shutdown = mcp23s08_irq_shutdown,
+ .irq_request_resources = mcp23s08_irq_reqres,
+ .irq_release_resources = mcp23s08_irq_relres,
};
static int mcp23s08_irq_setup(struct mcp23s08 *mcp)
@@ -567,7 +567,7 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
(mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo",
(mcp->cache[MCP_GPPU] & mask) ? "up" : " ");
/* NOTE: ignoring the irq-related registers */
- seq_printf(s, "\n");
+ seq_puts(s, "\n");
}
done:
mutex_unlock(&mcp->lock);
@@ -789,7 +789,7 @@ static int mcp230xx_probe(struct i2c_client *client,
pullups = pdata->chip[0].pullups;
}
- mcp = kzalloc(sizeof *mcp, GFP_KERNEL);
+ mcp = kzalloc(sizeof(*mcp), GFP_KERNEL);
if (!mcp)
return -ENOMEM;
@@ -925,7 +925,7 @@ static int mcp23s08_probe(struct spi_device *spi)
base = pdata->base;
}
- data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08),
+ data = kzalloc(sizeof(*data) + chips * sizeof(struct mcp23s08),
GFP_KERNEL);
if (!data)
return -ENOMEM;
diff --git a/drivers/gpio/gpio-moxart.c b/drivers/gpio/gpio-moxart.c
index 2af990022cc9..ccd45704e5fd 100644
--- a/drivers/gpio/gpio-moxart.c
+++ b/drivers/gpio/gpio-moxart.c
@@ -48,25 +48,6 @@ static void moxart_gpio_free(struct gpio_chip *chip, unsigned offset)
pinctrl_free_gpio(offset);
}
-static int moxart_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- struct moxart_gpio_chip *gc = to_moxart_gpio(chip);
- void __iomem *ioaddr = gc->base + GPIO_PIN_DIRECTION;
-
- writel(readl(ioaddr) & ~BIT(offset), ioaddr);
- return 0;
-}
-
-static int moxart_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct moxart_gpio_chip *gc = to_moxart_gpio(chip);
- void __iomem *ioaddr = gc->base + GPIO_PIN_DIRECTION;
-
- writel(readl(ioaddr) | BIT(offset), ioaddr);
- return 0;
-}
-
static void moxart_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct moxart_gpio_chip *gc = to_moxart_gpio(chip);
@@ -78,7 +59,6 @@ static void moxart_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
else
reg = reg & ~BIT(offset);
-
writel(reg, ioaddr);
}
@@ -93,6 +73,26 @@ static int moxart_gpio_get(struct gpio_chip *chip, unsigned offset)
return !!(readl(gc->base + GPIO_DATA_IN) & BIT(offset));
}
+static int moxart_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct moxart_gpio_chip *gc = to_moxart_gpio(chip);
+ void __iomem *ioaddr = gc->base + GPIO_PIN_DIRECTION;
+
+ writel(readl(ioaddr) & ~BIT(offset), ioaddr);
+ return 0;
+}
+
+static int moxart_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct moxart_gpio_chip *gc = to_moxart_gpio(chip);
+ void __iomem *ioaddr = gc->base + GPIO_PIN_DIRECTION;
+
+ moxart_gpio_set(chip, offset, value);
+ writel(readl(ioaddr) | BIT(offset), ioaddr);
+ return 0;
+}
+
static struct gpio_chip moxart_template_chip = {
.label = "moxart-gpio",
.request = moxart_gpio_request,
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index 3b1fd1ce460f..d42509422394 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -44,6 +44,7 @@
#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/irqchip/chained_irq.h>
/*
* GPIO unit register offsets.
@@ -438,12 +439,15 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type)
static void mvebu_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
struct mvebu_gpio_chip *mvchip = irq_get_handler_data(irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
u32 cause, type;
int i;
if (mvchip == NULL)
return;
+ chained_irq_enter(chip, desc);
+
cause = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) &
readl_relaxed(mvebu_gpioreg_level_mask(mvchip));
cause |= readl_relaxed(mvebu_gpioreg_edge_cause(mvchip)) &
@@ -466,8 +470,11 @@ static void mvebu_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
polarity ^= 1 << i;
writel_relaxed(polarity, mvebu_gpioreg_in_pol(mvchip));
}
+
generic_handle_irq(irq);
}
+
+ chained_irq_exit(chip, desc);
}
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
index 532bcb336eff..8ffdd7d2bade 100644
--- a/drivers/gpio/gpio-mxs.c
+++ b/drivers/gpio/gpio-mxs.c
@@ -214,7 +214,8 @@ static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
ct->regs.ack = PINCTRL_IRQSTAT(port) + MXS_CLR;
ct->regs.mask = PINCTRL_IRQEN(port);
- irq_setup_generic_chip(gc, IRQ_MSK(32), 0, IRQ_NOREQUEST, 0);
+ irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK,
+ IRQ_NOREQUEST, 0);
}
static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 424319061e09..19b886c21b1d 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -1214,24 +1214,10 @@ static int omap_gpio_probe(struct platform_device *pdev)
/* Static mapping, never released */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (unlikely(!res)) {
- dev_err(dev, "Invalid mem resource\n");
- irq_domain_remove(bank->domain);
- return -ENODEV;
- }
-
- if (!devm_request_mem_region(dev, res->start, resource_size(res),
- pdev->name)) {
- dev_err(dev, "Region already claimed\n");
+ bank->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(bank->base)) {
irq_domain_remove(bank->domain);
- return -EBUSY;
- }
-
- bank->base = devm_ioremap(dev, res->start, resource_size(res));
- if (!bank->base) {
- dev_err(dev, "Could not ioremap\n");
- irq_domain_remove(bank->domain);
- return -ENOMEM;
+ return PTR_ERR(bank->base);
}
platform_set_drvdata(pdev, bank);
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 019b23b955a2..d550d8e58705 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -1,5 +1,5 @@
/*
- * PCA953x 4/8/16 bit I/O ports
+ * PCA953x 4/8/16/24/40 bit I/O ports
*
* Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
* Copyright (C) 2007 Marvell International Ltd.
@@ -59,6 +59,7 @@ static const struct i2c_device_id pca953x_id[] = {
{ "pca9557", 8 | PCA953X_TYPE, },
{ "pca9574", 8 | PCA957X_TYPE | PCA_INT, },
{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
+ { "pca9698", 40 | PCA953X_TYPE, },
{ "max7310", 8 | PCA953X_TYPE, },
{ "max7312", 16 | PCA953X_TYPE | PCA_INT, },
@@ -68,6 +69,7 @@ static const struct i2c_device_id pca953x_id[] = {
{ "tca6408", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca6416", 16 | PCA953X_TYPE | PCA_INT, },
{ "tca6424", 24 | PCA953X_TYPE | PCA_INT, },
+ { "xra1202", 8 | PCA953X_TYPE },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca953x_id);
@@ -625,11 +627,12 @@ pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, u32 *invert)
const __be32 *val;
int size;
+ *gpio_base = -1;
+
node = client->dev.of_node;
if (node == NULL)
return;
- *gpio_base = -1;
val = of_get_property(node, "linux,gpio-base", &size);
WARN(val, "%s: device-tree property 'linux,gpio-base' is deprecated!", __func__);
if (val) {
@@ -812,6 +815,7 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "nxp,pca9557", },
{ .compatible = "nxp,pca9574", },
{ .compatible = "nxp,pca9575", },
+ { .compatible = "nxp,pca9698", },
{ .compatible = "maxim,max7310", },
{ .compatible = "maxim,max7312", },
@@ -822,6 +826,8 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "ti,tca6408", },
{ .compatible = "ti,tca6416", },
{ .compatible = "ti,tca6424", },
+
+ { .compatible = "exar,xra1202", },
{ }
};
diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c
index 9656c196772e..83a156397474 100644
--- a/drivers/gpio/gpio-pch.c
+++ b/drivers/gpio/gpio-pch.c
@@ -138,9 +138,6 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
unsigned long flags;
spin_lock_irqsave(&chip->spinlock, flags);
- pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
- pm |= (1 << nr);
- iowrite32(pm, &chip->reg->pm);
reg_val = ioread32(&chip->reg->po);
if (val)
@@ -148,6 +145,11 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
else
reg_val &= ~(1 << nr);
iowrite32(reg_val, &chip->reg->po);
+
+ pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
+ pm |= (1 << nr);
+ iowrite32(pm, &chip->reg->pm);
+
spin_unlock_irqrestore(&chip->spinlock, flags);
return 0;
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index b4d42112d02d..b0f475243cef 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -15,7 +15,6 @@
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
-#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/bitops.h>
#include <linux/workqueue.h>
@@ -53,7 +52,6 @@ struct pl061_gpio {
spinlock_t lock;
void __iomem *base;
- struct irq_domain *domain;
struct gpio_chip gc;
#ifdef CONFIG_PM
@@ -137,19 +135,14 @@ static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value)
writeb(!!value << offset, chip->base + (1 << (offset + 2)));
}
-static int pl061_to_irq(struct gpio_chip *gc, unsigned offset)
-{
- struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
-
- return irq_create_mapping(chip->domain, offset);
-}
-
static int pl061_irq_type(struct irq_data *d, unsigned trigger)
{
- struct pl061_gpio *chip = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
int offset = irqd_to_hwirq(d);
unsigned long flags;
u8 gpiois, gpioibe, gpioiev;
+ u8 bit = BIT(offset);
if (offset < 0 || offset >= PL061_GPIO_NR)
return -EINVAL;
@@ -157,30 +150,31 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
spin_lock_irqsave(&chip->lock, flags);
gpioiev = readb(chip->base + GPIOIEV);
-
gpiois = readb(chip->base + GPIOIS);
+ gpioibe = readb(chip->base + GPIOIBE);
+
if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
- gpiois |= 1 << offset;
+ gpiois |= bit;
if (trigger & IRQ_TYPE_LEVEL_HIGH)
- gpioiev |= 1 << offset;
+ gpioiev |= bit;
else
- gpioiev &= ~(1 << offset);
+ gpioiev &= ~bit;
} else
- gpiois &= ~(1 << offset);
- writeb(gpiois, chip->base + GPIOIS);
+ gpiois &= ~bit;
- gpioibe = readb(chip->base + GPIOIBE);
if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
- gpioibe |= 1 << offset;
+ /* Setting this makes GPIOEV be ignored */
+ gpioibe |= bit;
else {
- gpioibe &= ~(1 << offset);
+ gpioibe &= ~bit;
if (trigger & IRQ_TYPE_EDGE_RISING)
- gpioiev |= 1 << offset;
+ gpioiev |= bit;
else if (trigger & IRQ_TYPE_EDGE_FALLING)
- gpioiev &= ~(1 << offset);
+ gpioiev &= ~bit;
}
- writeb(gpioibe, chip->base + GPIOIBE);
+ writeb(gpiois, chip->base + GPIOIS);
+ writeb(gpioibe, chip->base + GPIOIBE);
writeb(gpioiev, chip->base + GPIOIEV);
spin_unlock_irqrestore(&chip->lock, flags);
@@ -192,7 +186,8 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc)
{
unsigned long pending;
int offset;
- struct pl061_gpio *chip = irq_desc_get_handler_data(desc);
+ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
struct irq_chip *irqchip = irq_desc_get_chip(desc);
chained_irq_enter(irqchip, desc);
@@ -201,7 +196,8 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc)
writeb(pending, chip->base + GPIOIC);
if (pending) {
for_each_set_bit(offset, &pending, PL061_GPIO_NR)
- generic_handle_irq(pl061_to_irq(&chip->gc, offset));
+ generic_handle_irq(irq_find_mapping(gc->irqdomain,
+ offset));
}
chained_irq_exit(irqchip, desc);
@@ -209,7 +205,8 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc)
static void pl061_irq_mask(struct irq_data *d)
{
- struct pl061_gpio *chip = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
u8 mask = 1 << (irqd_to_hwirq(d) % PL061_GPIO_NR);
u8 gpioie;
@@ -221,7 +218,8 @@ static void pl061_irq_mask(struct irq_data *d)
static void pl061_irq_unmask(struct irq_data *d)
{
- struct pl061_gpio *chip = irq_data_get_irq_chip_data(d);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
u8 mask = 1 << (irqd_to_hwirq(d) % PL061_GPIO_NR);
u8 gpioie;
@@ -232,30 +230,12 @@ static void pl061_irq_unmask(struct irq_data *d)
}
static struct irq_chip pl061_irqchip = {
- .name = "pl061 gpio",
+ .name = "pl061",
.irq_mask = pl061_irq_mask,
.irq_unmask = pl061_irq_unmask,
.irq_set_type = pl061_irq_type,
};
-static int pl061_irq_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hwirq)
-{
- struct pl061_gpio *chip = d->host_data;
-
- irq_set_chip_and_handler_name(irq, &pl061_irqchip, handle_simple_irq,
- "pl061");
- irq_set_chip_data(irq, chip);
- irq_set_irq_type(irq, IRQ_TYPE_NONE);
-
- return 0;
-}
-
-static const struct irq_domain_ops pl061_domain_ops = {
- .map = pl061_irq_map,
- .xlate = irq_domain_xlate_twocell,
-};
-
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
@@ -270,21 +250,18 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
if (pdata) {
chip->gc.base = pdata->gpio_base;
irq_base = pdata->irq_base;
- if (irq_base <= 0)
+ if (irq_base <= 0) {
+ dev_err(&adev->dev, "invalid IRQ base in pdata\n");
return -ENODEV;
+ }
} else {
chip->gc.base = -1;
irq_base = 0;
}
- if (!devm_request_mem_region(dev, adev->res.start,
- resource_size(&adev->res), "pl061"))
- return -EBUSY;
-
- chip->base = devm_ioremap(dev, adev->res.start,
- resource_size(&adev->res));
- if (!chip->base)
- return -ENOMEM;
+ chip->base = devm_ioremap_resource(dev, &adev->res);
+ if (IS_ERR(chip->base))
+ return PTR_ERR(chip->base);
spin_lock_init(&chip->lock);
@@ -294,7 +271,6 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
chip->gc.direction_output = pl061_direction_output;
chip->gc.get = pl061_get_value;
chip->gc.set = pl061_set_value;
- chip->gc.to_irq = pl061_to_irq;
chip->gc.ngpio = PL061_GPIO_NR;
chip->gc.label = dev_name(dev);
chip->gc.dev = dev;
@@ -309,16 +285,20 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
*/
writeb(0, chip->base + GPIOIE); /* disable irqs */
irq = adev->irq[0];
- if (irq < 0)
+ if (irq < 0) {
+ dev_err(&adev->dev, "invalid IRQ\n");
return -ENODEV;
+ }
- irq_set_chained_handler(irq, pl061_irq_handler);
- irq_set_handler_data(irq, chip);
-
- chip->domain = irq_domain_add_simple(adev->dev.of_node, PL061_GPIO_NR,
- irq_base, &pl061_domain_ops, chip);
- if (!chip->domain)
- return -ENODEV;
+ ret = gpiochip_irqchip_add(&chip->gc, &pl061_irqchip,
+ irq_base, handle_simple_irq,
+ IRQ_TYPE_NONE);
+ if (ret) {
+ dev_info(&adev->dev, "could not add irqchip\n");
+ return ret;
+ }
+ gpiochip_set_chained_irqchip(&chip->gc, &pl061_irqchip,
+ irq, pl061_irq_handler);
for (i = 0; i < PL061_GPIO_NR; i++) {
if (pdata) {
@@ -331,6 +311,8 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
}
amba_set_drvdata(adev, chip);
+ dev_info(&adev->dev, "PL061 GPIO chip @%pa registered\n",
+ &adev->res.start);
return 0;
}
diff --git a/drivers/gpio/gpio-rc5t583.c b/drivers/gpio/gpio-rc5t583.c
index 122b776fdc0b..9b423173ab50 100644
--- a/drivers/gpio/gpio-rc5t583.c
+++ b/drivers/gpio/gpio-rc5t583.c
@@ -97,7 +97,7 @@ static int rc5t583_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct rc5t583_gpio *rc5t583_gpio = to_rc5t583_gpio(gc);
- if ((offset >= 0) && (offset < 8))
+ if (offset < RC5T583_MAX_GPIO)
return rc5t583_gpio->rc5t583->irq_base +
RC5T583_IRQ_GPIO0 + offset;
return -EINVAL;
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index ca76ce751540..03c91482432c 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -356,12 +356,13 @@ static int gpio_rcar_probe(struct platform_device *pdev)
struct resource *io, *irq;
struct gpio_chip *gpio_chip;
struct irq_chip *irq_chip;
- const char *name = dev_name(&pdev->dev);
+ struct device *dev = &pdev->dev;
+ const char *name = dev_name(dev);
int ret;
- p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+ p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
if (!p) {
- dev_err(&pdev->dev, "failed to allocate driver data\n");
+ dev_err(dev, "failed to allocate driver data\n");
ret = -ENOMEM;
goto err0;
}
@@ -380,15 +381,14 @@ static int gpio_rcar_probe(struct platform_device *pdev)
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!io || !irq) {
- dev_err(&pdev->dev, "missing IRQ or IOMEM\n");
+ dev_err(dev, "missing IRQ or IOMEM\n");
ret = -EINVAL;
goto err0;
}
- p->base = devm_ioremap_nocache(&pdev->dev, io->start,
- resource_size(io));
+ p->base = devm_ioremap_nocache(dev, io->start, resource_size(io));
if (!p->base) {
- dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ dev_err(dev, "failed to remap I/O memory\n");
ret = -ENXIO;
goto err0;
}
@@ -402,7 +402,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
gpio_chip->set = gpio_rcar_set;
gpio_chip->to_irq = gpio_rcar_to_irq;
gpio_chip->label = name;
- gpio_chip->dev = &pdev->dev;
+ gpio_chip->dev = dev;
gpio_chip->owner = THIS_MODULE;
gpio_chip->base = p->config.gpio_base;
gpio_chip->ngpio = p->config.number_of_pins;
@@ -421,30 +421,30 @@ static int gpio_rcar_probe(struct platform_device *pdev)
&gpio_rcar_irq_domain_ops, p);
if (!p->irq_domain) {
ret = -ENXIO;
- dev_err(&pdev->dev, "cannot initialize irq domain\n");
+ dev_err(dev, "cannot initialize irq domain\n");
goto err0;
}
- if (devm_request_irq(&pdev->dev, irq->start,
- gpio_rcar_irq_handler, IRQF_SHARED, name, p)) {
- dev_err(&pdev->dev, "failed to request IRQ\n");
+ if (devm_request_irq(dev, irq->start, gpio_rcar_irq_handler,
+ IRQF_SHARED, name, p)) {
+ dev_err(dev, "failed to request IRQ\n");
ret = -ENOENT;
goto err1;
}
ret = gpiochip_add(gpio_chip);
if (ret) {
- dev_err(&pdev->dev, "failed to add GPIO controller\n");
+ dev_err(dev, "failed to add GPIO controller\n");
goto err1;
}
- dev_info(&pdev->dev, "driving %d GPIOs\n", p->config.number_of_pins);
+ dev_info(dev, "driving %d GPIOs\n", p->config.number_of_pins);
/* warn in case of mismatch if irq base is specified */
if (p->config.irq_base) {
ret = irq_find_mapping(p->irq_domain, 0);
if (p->config.irq_base != ret)
- dev_warn(&pdev->dev, "irq base mismatch (%u/%u)\n",
+ dev_warn(dev, "irq base mismatch (%u/%u)\n",
p->config.irq_base, ret);
}
@@ -452,7 +452,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
ret = gpiochip_add_pin_range(gpio_chip, p->config.pctl_name, 0,
gpio_chip->base, gpio_chip->ngpio);
if (ret < 0)
- dev_warn(&pdev->dev, "failed to add pin range\n");
+ dev_warn(dev, "failed to add pin range\n");
}
return 0;
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index a85e00bf9834..07105ee5c9ae 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -379,6 +379,7 @@ static int s5p64x0_gpio_setcfg_rbank(struct samsung_gpio_chip *chip,
case 6:
shift = ((off + 1) & 7) * 4;
reg -= 4;
+ break;
default:
shift = ((off + 1) & 7) * 4;
break;
diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c
new file mode 100644
index 000000000000..b50fe1297748
--- /dev/null
+++ b/drivers/gpio/gpio-syscon.c
@@ -0,0 +1,191 @@
+/*
+ * SYSCON GPIO driver
+ *
+ * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define GPIO_SYSCON_FEAT_IN BIT(0)
+#define GPIO_SYSCON_FEAT_OUT BIT(1)
+#define GPIO_SYSCON_FEAT_DIR BIT(2)
+
+/* SYSCON driver is designed to use 32-bit wide registers */
+#define SYSCON_REG_SIZE (4)
+#define SYSCON_REG_BITS (SYSCON_REG_SIZE * 8)
+
+/**
+ * struct syscon_gpio_data - Configuration for the device.
+ * compatible: SYSCON driver compatible string.
+ * flags: Set of GPIO_SYSCON_FEAT_ flags:
+ * GPIO_SYSCON_FEAT_IN: GPIOs supports input,
+ * GPIO_SYSCON_FEAT_OUT: GPIOs supports output,
+ * GPIO_SYSCON_FEAT_DIR: GPIOs supports switch direction.
+ * bit_count: Number of bits used as GPIOs.
+ * dat_bit_offset: Offset (in bits) to the first GPIO bit.
+ * dir_bit_offset: Optional offset (in bits) to the first bit to switch
+ * GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag).
+ */
+
+struct syscon_gpio_data {
+ const char *compatible;
+ unsigned int flags;
+ unsigned int bit_count;
+ unsigned int dat_bit_offset;
+ unsigned int dir_bit_offset;
+};
+
+struct syscon_gpio_priv {
+ struct gpio_chip chip;
+ struct regmap *syscon;
+ const struct syscon_gpio_data *data;
+};
+
+static inline struct syscon_gpio_priv *to_syscon_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct syscon_gpio_priv, chip);
+}
+
+static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+ unsigned int val, offs = priv->data->dat_bit_offset + offset;
+ int ret;
+
+ ret = regmap_read(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & BIT(offs % SYSCON_REG_BITS));
+}
+
+static void syscon_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+ unsigned int offs = priv->data->dat_bit_offset + offset;
+
+ regmap_update_bits(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+ BIT(offs % SYSCON_REG_BITS),
+ val ? BIT(offs % SYSCON_REG_BITS) : 0);
+}
+
+static int syscon_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+
+ if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
+ unsigned int offs = priv->data->dir_bit_offset + offset;
+
+ regmap_update_bits(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+ BIT(offs % SYSCON_REG_BITS), 0);
+ }
+
+ return 0;
+}
+
+static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+
+ if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
+ unsigned int offs = priv->data->dir_bit_offset + offset;
+
+ regmap_update_bits(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+ BIT(offs % SYSCON_REG_BITS),
+ BIT(offs % SYSCON_REG_BITS));
+ }
+
+ syscon_gpio_set(chip, offset, val);
+
+ return 0;
+}
+
+static const struct syscon_gpio_data clps711x_mctrl_gpio = {
+ /* ARM CLPS711X SYSFLG1 Bits 8-10 */
+ .compatible = "cirrus,clps711x-syscon1",
+ .flags = GPIO_SYSCON_FEAT_IN,
+ .bit_count = 3,
+ .dat_bit_offset = 0x40 * 8 + 8,
+};
+
+static const struct of_device_id syscon_gpio_ids[] = {
+ {
+ .compatible = "cirrus,clps711x-mctrl-gpio",
+ .data = &clps711x_mctrl_gpio,
+ },
+ { }
+};
+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);
+ struct syscon_gpio_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->data = of_id->data;
+
+ priv->syscon =
+ syscon_regmap_lookup_by_compatible(priv->data->compatible);
+ if (IS_ERR(priv->syscon))
+ return PTR_ERR(priv->syscon);
+
+ priv->chip.dev = dev;
+ priv->chip.owner = THIS_MODULE;
+ priv->chip.label = dev_name(dev);
+ priv->chip.base = -1;
+ priv->chip.ngpio = priv->data->bit_count;
+ priv->chip.get = syscon_gpio_get;
+ if (priv->data->flags & GPIO_SYSCON_FEAT_IN)
+ priv->chip.direction_input = syscon_gpio_dir_in;
+ if (priv->data->flags & GPIO_SYSCON_FEAT_OUT) {
+ priv->chip.set = syscon_gpio_set;
+ priv->chip.direction_output = syscon_gpio_dir_out;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return gpiochip_add(&priv->chip);
+}
+
+static int syscon_gpio_remove(struct platform_device *pdev)
+{
+ struct syscon_gpio_priv *priv = platform_get_drvdata(pdev);
+
+ return gpiochip_remove(&priv->chip);
+}
+
+static struct platform_driver syscon_gpio_driver = {
+ .driver = {
+ .name = "gpio-syscon",
+ .owner = THIS_MODULE,
+ .of_match_table = syscon_gpio_ids,
+ },
+ .probe = syscon_gpio_probe,
+ .remove = syscon_gpio_remove,
+};
+module_platform_driver(syscon_gpio_driver);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("SYSCON GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-tnetv107x.c b/drivers/gpio/gpio-tnetv107x.c
deleted file mode 100644
index 4aa481579a05..000000000000
--- a/drivers/gpio/gpio-tnetv107x.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Texas Instruments TNETV107X GPIO Controller
- *
- * Copyright (C) 2010 Texas Instruments
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/gpio.h>
-#include <linux/platform_data/gpio-davinci.h>
-
-#include <mach/common.h>
-#include <mach/tnetv107x.h>
-
-struct tnetv107x_gpio_regs {
- u32 idver;
- u32 data_in[3];
- u32 data_out[3];
- u32 direction[3];
- u32 enable[3];
-};
-
-#define gpio_reg_index(gpio) ((gpio) >> 5)
-#define gpio_reg_bit(gpio) BIT((gpio) & 0x1f)
-
-#define gpio_reg_rmw(reg, mask, val) \
- __raw_writel((__raw_readl(reg) & ~(mask)) | (val), (reg))
-
-#define gpio_reg_set_bit(reg, gpio) \
- gpio_reg_rmw((reg) + gpio_reg_index(gpio), 0, gpio_reg_bit(gpio))
-
-#define gpio_reg_clear_bit(reg, gpio) \
- gpio_reg_rmw((reg) + gpio_reg_index(gpio), gpio_reg_bit(gpio), 0)
-
-#define gpio_reg_get_bit(reg, gpio) \
- (__raw_readl((reg) + gpio_reg_index(gpio)) & gpio_reg_bit(gpio))
-
-#define chip2controller(chip) \
- container_of(chip, struct davinci_gpio_controller, chip)
-
-#define TNETV107X_GPIO_CTLRS DIV_ROUND_UP(TNETV107X_N_GPIO, 32)
-
-static struct davinci_gpio_controller chips[TNETV107X_GPIO_CTLRS];
-
-static int tnetv107x_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
- struct davinci_gpio_controller *ctlr = chip2controller(chip);
- struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
- unsigned gpio = chip->base + offset;
- unsigned long flags;
-
- spin_lock_irqsave(&ctlr->lock, flags);
-
- gpio_reg_set_bit(regs->enable, gpio);
-
- spin_unlock_irqrestore(&ctlr->lock, flags);
-
- return 0;
-}
-
-static void tnetv107x_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
- struct davinci_gpio_controller *ctlr = chip2controller(chip);
- struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
- unsigned gpio = chip->base + offset;
- unsigned long flags;
-
- spin_lock_irqsave(&ctlr->lock, flags);
-
- gpio_reg_clear_bit(regs->enable, gpio);
-
- spin_unlock_irqrestore(&ctlr->lock, flags);
-}
-
-static int tnetv107x_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
-{
- struct davinci_gpio_controller *ctlr = chip2controller(chip);
- struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
- unsigned gpio = chip->base + offset;
- unsigned long flags;
-
- spin_lock_irqsave(&ctlr->lock, flags);
-
- gpio_reg_set_bit(regs->direction, gpio);
-
- spin_unlock_irqrestore(&ctlr->lock, flags);
-
- return 0;
-}
-
-static int tnetv107x_gpio_dir_out(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct davinci_gpio_controller *ctlr = chip2controller(chip);
- struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
- unsigned gpio = chip->base + offset;
- unsigned long flags;
-
- spin_lock_irqsave(&ctlr->lock, flags);
-
- if (value)
- gpio_reg_set_bit(regs->data_out, gpio);
- else
- gpio_reg_clear_bit(regs->data_out, gpio);
-
- gpio_reg_clear_bit(regs->direction, gpio);
-
- spin_unlock_irqrestore(&ctlr->lock, flags);
-
- return 0;
-}
-
-static int tnetv107x_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- struct davinci_gpio_controller *ctlr = chip2controller(chip);
- struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
- unsigned gpio = chip->base + offset;
- int ret;
-
- ret = gpio_reg_get_bit(regs->data_in, gpio);
-
- return ret ? 1 : 0;
-}
-
-static void tnetv107x_gpio_set(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct davinci_gpio_controller *ctlr = chip2controller(chip);
- struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
- unsigned gpio = chip->base + offset;
- unsigned long flags;
-
- spin_lock_irqsave(&ctlr->lock, flags);
-
- if (value)
- gpio_reg_set_bit(regs->data_out, gpio);
- else
- gpio_reg_clear_bit(regs->data_out, gpio);
-
- spin_unlock_irqrestore(&ctlr->lock, flags);
-}
-
-static int __init tnetv107x_gpio_setup(void)
-{
- int i, base;
- unsigned ngpio;
- struct davinci_soc_info *soc_info = &davinci_soc_info;
- struct tnetv107x_gpio_regs *regs;
- struct davinci_gpio_controller *ctlr;
-
- if (soc_info->gpio_type != GPIO_TYPE_TNETV107X)
- return 0;
-
- ngpio = soc_info->gpio_num;
- if (ngpio == 0) {
- pr_err("GPIO setup: how many GPIOs?\n");
- return -EINVAL;
- }
-
- if (WARN_ON(TNETV107X_N_GPIO < ngpio))
- ngpio = TNETV107X_N_GPIO;
-
- regs = ioremap(soc_info->gpio_base, SZ_4K);
- if (WARN_ON(!regs))
- return -EINVAL;
-
- for (i = 0, base = 0; base < ngpio; i++, base += 32) {
- ctlr = &chips[i];
-
- ctlr->chip.label = "tnetv107x";
- ctlr->chip.can_sleep = false;
- ctlr->chip.base = base;
- ctlr->chip.ngpio = ngpio - base;
- if (ctlr->chip.ngpio > 32)
- ctlr->chip.ngpio = 32;
-
- ctlr->chip.request = tnetv107x_gpio_request;
- ctlr->chip.free = tnetv107x_gpio_free;
- ctlr->chip.direction_input = tnetv107x_gpio_dir_in;
- ctlr->chip.get = tnetv107x_gpio_get;
- ctlr->chip.direction_output = tnetv107x_gpio_dir_out;
- ctlr->chip.set = tnetv107x_gpio_set;
-
- spin_lock_init(&ctlr->lock);
-
- ctlr->regs = regs;
- ctlr->set_data = &regs->data_out[i];
- ctlr->clr_data = &regs->data_out[i];
- ctlr->in_data = &regs->data_in[i];
-
- gpiochip_add(&ctlr->chip);
- }
-
- soc_info->gpio_ctlrs = chips;
- soc_info->gpio_ctlrs_num = DIV_ROUND_UP(ngpio, 32);
- return 0;
-}
-pure_initcall(tnetv107x_gpio_setup);
diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c
index 8b88ca2eda9c..3ebb1a5ff22e 100644
--- a/drivers/gpio/gpio-twl4030.c
+++ b/drivers/gpio/gpio-twl4030.c
@@ -139,7 +139,6 @@ static u8 cached_leden;
static void twl4030_led_set_value(int led, int value)
{
u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM;
- int status;
if (led)
mask <<= 1;
@@ -148,8 +147,9 @@ static void twl4030_led_set_value(int led, int value)
cached_leden &= ~mask;
else
cached_leden |= mask;
- status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
- TWL4030_LED_LEDEN_REG);
+
+ WARN_ON_ONCE(twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
+ TWL4030_LED_LEDEN_REG));
}
static int twl4030_set_gpio_direction(int gpio, int is_input)
diff --git a/drivers/gpio/gpio-tz1090.c b/drivers/gpio/gpio-tz1090.c
index 23e061392411..5246a60eff6d 100644
--- a/drivers/gpio/gpio-tz1090.c
+++ b/drivers/gpio/gpio-tz1090.c
@@ -488,26 +488,26 @@ static int tz1090_gpio_bank_probe(struct tz1090_gpio_bank_info *info)
gc->chip_types[0].handler = handle_level_irq;
gc->chip_types[0].regs.ack = REG_GPIO_IRQ_STS;
gc->chip_types[0].regs.mask = REG_GPIO_IRQ_EN;
- gc->chip_types[0].chip.irq_startup = gpio_startup_irq,
- gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit,
- gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit,
- gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit,
- gc->chip_types[0].chip.irq_set_type = gpio_set_irq_type,
- gc->chip_types[0].chip.irq_set_wake = gpio_set_irq_wake,
- gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND,
+ gc->chip_types[0].chip.irq_startup = gpio_startup_irq;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_set_type = gpio_set_irq_type;
+ gc->chip_types[0].chip.irq_set_wake = gpio_set_irq_wake;
+ gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
/* edge chip type */
gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
gc->chip_types[1].handler = handle_edge_irq;
gc->chip_types[1].regs.ack = REG_GPIO_IRQ_STS;
gc->chip_types[1].regs.mask = REG_GPIO_IRQ_EN;
- gc->chip_types[1].chip.irq_startup = gpio_startup_irq,
- gc->chip_types[1].chip.irq_ack = irq_gc_ack_clr_bit,
- gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit,
- gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit,
- gc->chip_types[1].chip.irq_set_type = gpio_set_irq_type,
- gc->chip_types[1].chip.irq_set_wake = gpio_set_irq_wake,
- gc->chip_types[1].chip.flags = IRQCHIP_MASK_ON_SUSPEND,
+ gc->chip_types[1].chip.irq_startup = gpio_startup_irq;
+ gc->chip_types[1].chip.irq_ack = irq_gc_ack_clr_bit;
+ gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[1].chip.irq_set_type = gpio_set_irq_type;
+ gc->chip_types[1].chip.irq_set_wake = gpio_set_irq_wake;
+ gc->chip_types[1].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
/* Setup chained handler for this GPIO bank */
irq_set_handler_data(bank->irq, bank);
diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c
new file mode 100644
index 000000000000..9bf5034b6cdb
--- /dev/null
+++ b/drivers/gpio/gpio-zevio.c
@@ -0,0 +1,220 @@
+/*
+ * GPIO controller in LSI ZEVIO SoCs.
+ *
+ * Author: Fabian Vogt <fabian@ritter-vogt.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/spinlock.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+/*
+ * Memory layout:
+ * This chip has four gpio sections, each controls 8 GPIOs.
+ * Bit 0 in section 0 is GPIO 0, bit 2 in section 1 is GPIO 10.
+ * Disclaimer: Reverse engineered!
+ * For more information refer to:
+ * http://hackspire.unsads.com/wiki/index.php/Memory-mapped_I/O_ports#90000000_-_General_Purpose_I.2FO_.28GPIO.29
+ *
+ * 0x00-0x3F: Section 0
+ * +0x00: Masked interrupt status (read-only)
+ * +0x04: R: Interrupt status W: Reset interrupt status
+ * +0x08: R: Interrupt mask W: Mask interrupt
+ * +0x0C: W: Unmask interrupt (write-only)
+ * +0x10: Direction: I/O=1/0
+ * +0x14: Output
+ * +0x18: Input (read-only)
+ * +0x20: R: Level interrupt W: Set as level interrupt
+ * 0x40-0x7F: Section 1
+ * 0x80-0xBF: Section 2
+ * 0xC0-0xFF: Section 3
+ */
+
+#define ZEVIO_GPIO_SECTION_SIZE 0x40
+
+/* Offsets to various registers */
+#define ZEVIO_GPIO_INT_MASKED_STATUS 0x00
+#define ZEVIO_GPIO_INT_STATUS 0x04
+#define ZEVIO_GPIO_INT_UNMASK 0x08
+#define ZEVIO_GPIO_INT_MASK 0x0C
+#define ZEVIO_GPIO_DIRECTION 0x10
+#define ZEVIO_GPIO_OUTPUT 0x14
+#define ZEVIO_GPIO_INPUT 0x18
+#define ZEVIO_GPIO_INT_STICKY 0x20
+
+#define to_zevio_gpio(chip) container_of(to_of_mm_gpio_chip(chip), \
+ struct zevio_gpio, chip)
+
+/* Bit number of GPIO in its section */
+#define ZEVIO_GPIO_BIT(gpio) (gpio&7)
+
+struct zevio_gpio {
+ spinlock_t lock;
+ struct of_mm_gpio_chip chip;
+};
+
+static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin,
+ unsigned port_offset)
+{
+ unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE;
+ return readl(IOMEM(c->chip.regs + section_offset + port_offset));
+}
+
+static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin,
+ unsigned port_offset, u32 val)
+{
+ unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE;
+ writel(val, IOMEM(c->chip.regs + section_offset + port_offset));
+}
+
+/* Functions for struct gpio_chip */
+static int zevio_gpio_get(struct gpio_chip *chip, unsigned pin)
+{
+ struct zevio_gpio *controller = to_zevio_gpio(chip);
+
+ /* Only reading allowed, so no spinlock needed */
+ u32 val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_INPUT);
+
+ return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1;
+}
+
+static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
+{
+ struct zevio_gpio *controller = to_zevio_gpio(chip);
+ u32 val;
+
+ spin_lock(&controller->lock);
+ val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT);
+ if (value)
+ val |= BIT(ZEVIO_GPIO_BIT(pin));
+ else
+ val &= ~BIT(ZEVIO_GPIO_BIT(pin));
+
+ zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val);
+ spin_unlock(&controller->lock);
+}
+
+static int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
+{
+ struct zevio_gpio *controller = to_zevio_gpio(chip);
+ u32 val;
+
+ spin_lock(&controller->lock);
+
+ val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION);
+ val |= BIT(ZEVIO_GPIO_BIT(pin));
+ zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val);
+
+ spin_unlock(&controller->lock);
+
+ return 0;
+}
+
+static int zevio_gpio_direction_output(struct gpio_chip *chip,
+ unsigned pin, int value)
+{
+ struct zevio_gpio *controller = to_zevio_gpio(chip);
+ u32 val;
+
+ spin_lock(&controller->lock);
+ val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT);
+ if (value)
+ val |= BIT(ZEVIO_GPIO_BIT(pin));
+ else
+ val &= ~BIT(ZEVIO_GPIO_BIT(pin));
+
+ zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val);
+ val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION);
+ val &= ~BIT(ZEVIO_GPIO_BIT(pin));
+ zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val);
+
+ spin_unlock(&controller->lock);
+
+ return 0;
+}
+
+static int zevio_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+ /*
+ * TODO: Implement IRQs.
+ * Not implemented yet due to weird lockups
+ */
+
+ return -ENXIO;
+}
+
+static struct gpio_chip zevio_gpio_chip = {
+ .direction_input = zevio_gpio_direction_input,
+ .direction_output = zevio_gpio_direction_output,
+ .set = zevio_gpio_set,
+ .get = zevio_gpio_get,
+ .to_irq = zevio_gpio_to_irq,
+ .base = 0,
+ .owner = THIS_MODULE,
+ .ngpio = 32,
+ .of_gpio_n_cells = 2,
+};
+
+/* Initialization */
+static int zevio_gpio_probe(struct platform_device *pdev)
+{
+ struct zevio_gpio *controller;
+ int status, i;
+
+ controller = devm_kzalloc(&pdev->dev, sizeof(*controller), GFP_KERNEL);
+ if (!controller) {
+ dev_err(&pdev->dev, "not enough free memory\n");
+ return -ENOMEM;
+ }
+
+ /* Copy our reference */
+ controller->chip.gc = zevio_gpio_chip;
+ controller->chip.gc.dev = &pdev->dev;
+
+ status = of_mm_gpiochip_add(pdev->dev.of_node, &(controller->chip));
+ if (status) {
+ dev_err(&pdev->dev, "failed to add gpiochip: %d\n", status);
+ return status;
+ }
+
+ spin_lock_init(&controller->lock);
+
+ /* Disable interrupts, they only cause errors */
+ for (i = 0; i < controller->chip.gc.ngpio; i += 8)
+ zevio_gpio_port_set(controller, i, ZEVIO_GPIO_INT_MASK, 0xFF);
+
+ dev_dbg(controller->chip.gc.dev, "ZEVIO GPIO controller set up!\n");
+
+ return 0;
+}
+
+static struct of_device_id zevio_gpio_of_match[] = {
+ { .compatible = "lsi,zevio-gpio", },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, zevio_gpio_of_match);
+
+static struct platform_driver zevio_gpio_driver = {
+ .driver = {
+ .name = "gpio-zevio",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(zevio_gpio_of_match),
+ },
+ .probe = zevio_gpio_probe,
+};
+module_platform_driver(zevio_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Fabian Vogt <fabian@ritter-vogt.de>");
+MODULE_DESCRIPTION("LSI ZEVIO SoC GPIO driver");
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 716ee9843110..bf0f8b476696 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -16,16 +16,35 @@
#include <linux/export.h>
#include <linux/acpi.h>
#include <linux/interrupt.h>
+#include <linux/mutex.h>
#include "gpiolib.h"
-struct acpi_gpio_evt_pin {
+struct acpi_gpio_event {
struct list_head node;
- acpi_handle *evt_handle;
+ acpi_handle handle;
unsigned int pin;
unsigned int irq;
};
+struct acpi_gpio_connection {
+ struct list_head node;
+ struct gpio_desc *desc;
+};
+
+struct acpi_gpio_chip {
+ /*
+ * ACPICA requires that the first field of the context parameter
+ * passed to acpi_install_address_space_handler() is large enough
+ * to hold struct acpi_connection_info.
+ */
+ struct acpi_connection_info conn_info;
+ struct list_head conns;
+ struct mutex conn_lock;
+ struct gpio_chip *chip;
+ struct list_head events;
+};
+
static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
{
if (!gc->dev)
@@ -60,176 +79,195 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
if (pin < 0 || pin > chip->ngpio)
return ERR_PTR(-EINVAL);
- return gpio_to_desc(chip->base + pin);
+ return gpiochip_get_desc(chip, pin);
}
static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
{
- acpi_handle handle = data;
+ struct acpi_gpio_event *event = data;
- acpi_evaluate_object(handle, NULL, NULL, NULL);
+ acpi_evaluate_object(event->handle, NULL, NULL, NULL);
return IRQ_HANDLED;
}
static irqreturn_t acpi_gpio_irq_handler_evt(int irq, void *data)
{
- struct acpi_gpio_evt_pin *evt_pin = data;
+ struct acpi_gpio_event *event = data;
- acpi_execute_simple_method(evt_pin->evt_handle, NULL, evt_pin->pin);
+ acpi_execute_simple_method(event->handle, NULL, event->pin);
return IRQ_HANDLED;
}
-static void acpi_gpio_evt_dh(acpi_handle handle, void *data)
+static void acpi_gpio_chip_dh(acpi_handle handle, void *data)
{
/* The address of this function is used as a key. */
}
-/**
- * acpi_gpiochip_request_interrupts() - Register isr for gpio chip ACPI events
- * @chip: gpio chip
- *
- * ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are
- * handled by ACPI event methods which need to be called from the GPIO
- * chip's interrupt handler. acpi_gpiochip_request_interrupts finds out which
- * gpio pins have acpi event methods and assigns interrupt handlers that calls
- * the acpi event methods for those pins.
- */
-static void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
+static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
+ void *context)
{
- struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
- struct acpi_resource *res;
+ struct acpi_gpio_chip *acpi_gpio = context;
+ struct gpio_chip *chip = acpi_gpio->chip;
+ struct acpi_resource_gpio *agpio;
acpi_handle handle, evt_handle;
- struct list_head *evt_pins = NULL;
- acpi_status status;
- unsigned int pin;
- int irq, ret;
- char ev_name[5];
+ struct acpi_gpio_event *event;
+ irq_handler_t handler = NULL;
+ struct gpio_desc *desc;
+ unsigned long irqflags;
+ int ret, pin, irq;
- if (!chip->dev || !chip->to_irq)
- return;
+ if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
+ return AE_OK;
- handle = ACPI_HANDLE(chip->dev);
- if (!handle)
- return;
+ agpio = &ares->data.gpio;
+ if (agpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT)
+ return AE_OK;
- status = acpi_get_event_resources(handle, &buf);
- if (ACPI_FAILURE(status))
- return;
+ handle = ACPI_HANDLE(chip->dev);
+ pin = agpio->pin_table[0];
+
+ if (pin <= 255) {
+ char ev_name[5];
+ sprintf(ev_name, "_%c%02X",
+ agpio->triggering == ACPI_EDGE_SENSITIVE ? 'E' : 'L',
+ pin);
+ if (ACPI_SUCCESS(acpi_get_handle(handle, ev_name, &evt_handle)))
+ handler = acpi_gpio_irq_handler;
+ }
+ if (!handler) {
+ if (ACPI_SUCCESS(acpi_get_handle(handle, "_EVT", &evt_handle)))
+ handler = acpi_gpio_irq_handler_evt;
+ }
+ if (!handler)
+ return AE_BAD_PARAMETER;
- status = acpi_get_handle(handle, "_EVT", &evt_handle);
- if (ACPI_SUCCESS(status)) {
- evt_pins = kzalloc(sizeof(*evt_pins), GFP_KERNEL);
- if (evt_pins) {
- INIT_LIST_HEAD(evt_pins);
- status = acpi_attach_data(handle, acpi_gpio_evt_dh,
- evt_pins);
- if (ACPI_FAILURE(status)) {
- kfree(evt_pins);
- evt_pins = NULL;
- }
- }
+ desc = gpiochip_get_desc(chip, pin);
+ if (IS_ERR(desc)) {
+ dev_err(chip->dev, "Failed to get GPIO descriptor\n");
+ return AE_ERROR;
}
- /*
- * If a GPIO interrupt has an ACPI event handler method, or _EVT is
- * present, set up an interrupt handler that calls the ACPI event
- * handler.
- */
- for (res = buf.pointer;
- res && (res->type != ACPI_RESOURCE_TYPE_END_TAG);
- res = ACPI_NEXT_RESOURCE(res)) {
- irq_handler_t handler = NULL;
- void *data;
-
- if (res->type != ACPI_RESOURCE_TYPE_GPIO ||
- res->data.gpio.connection_type !=
- ACPI_RESOURCE_GPIO_TYPE_INT)
- continue;
+ ret = gpiochip_request_own_desc(desc, "ACPI:Event");
+ if (ret) {
+ dev_err(chip->dev, "Failed to request GPIO\n");
+ return AE_ERROR;
+ }
- pin = res->data.gpio.pin_table[0];
- if (pin > chip->ngpio)
- continue;
+ gpiod_direction_input(desc);
- irq = chip->to_irq(chip, pin);
- if (irq < 0)
- continue;
+ ret = gpiod_lock_as_irq(desc);
+ if (ret) {
+ dev_err(chip->dev, "Failed to lock GPIO as interrupt\n");
+ goto fail_free_desc;
+ }
- if (pin <= 255) {
- acpi_handle ev_handle;
+ irq = gpiod_to_irq(desc);
+ if (irq < 0) {
+ dev_err(chip->dev, "Failed to translate GPIO to IRQ\n");
+ goto fail_unlock_irq;
+ }
- sprintf(ev_name, "_%c%02X",
- res->data.gpio.triggering ? 'E' : 'L', pin);
- status = acpi_get_handle(handle, ev_name, &ev_handle);
- if (ACPI_SUCCESS(status)) {
- handler = acpi_gpio_irq_handler;
- data = ev_handle;
- }
+ irqflags = IRQF_ONESHOT;
+ if (agpio->triggering == ACPI_LEVEL_SENSITIVE) {
+ if (agpio->polarity == ACPI_ACTIVE_HIGH)
+ irqflags |= IRQF_TRIGGER_HIGH;
+ else
+ irqflags |= IRQF_TRIGGER_LOW;
+ } else {
+ switch (agpio->polarity) {
+ case ACPI_ACTIVE_HIGH:
+ irqflags |= IRQF_TRIGGER_RISING;
+ break;
+ case ACPI_ACTIVE_LOW:
+ irqflags |= IRQF_TRIGGER_FALLING;
+ break;
+ default:
+ irqflags |= IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING;
+ break;
}
- if (!handler && evt_pins) {
- struct acpi_gpio_evt_pin *evt_pin;
+ }
- evt_pin = kzalloc(sizeof(*evt_pin), GFP_KERNEL);
- if (!evt_pin)
- continue;
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
+ if (!event)
+ goto fail_unlock_irq;
- list_add_tail(&evt_pin->node, evt_pins);
- evt_pin->evt_handle = evt_handle;
- evt_pin->pin = pin;
- evt_pin->irq = irq;
- handler = acpi_gpio_irq_handler_evt;
- data = evt_pin;
- }
- if (!handler)
- continue;
+ event->handle = evt_handle;
+ event->irq = irq;
+ event->pin = pin;
- /* Assume BIOS sets the triggering, so no flags */
- ret = devm_request_threaded_irq(chip->dev, irq, NULL, handler,
- 0, "GPIO-signaled-ACPI-event",
- data);
- if (ret)
- dev_err(chip->dev,
- "Failed to request IRQ %d ACPI event handler\n",
- irq);
+ ret = request_threaded_irq(event->irq, NULL, handler, irqflags,
+ "ACPI:Event", event);
+ if (ret) {
+ dev_err(chip->dev, "Failed to setup interrupt handler for %d\n",
+ event->irq);
+ goto fail_free_event;
}
+
+ list_add_tail(&event->node, &acpi_gpio->events);
+ return AE_OK;
+
+fail_free_event:
+ kfree(event);
+fail_unlock_irq:
+ gpiod_unlock_as_irq(desc);
+fail_free_desc:
+ gpiochip_free_own_desc(desc);
+
+ return AE_ERROR;
}
/**
- * acpi_gpiochip_free_interrupts() - Free GPIO _EVT ACPI event interrupts.
- * @chip: gpio chip
- *
- * Free interrupts associated with the _EVT method for the given GPIO chip.
+ * acpi_gpiochip_request_interrupts() - Register isr for gpio chip ACPI events
+ * @acpi_gpio: ACPI GPIO chip
*
- * The remaining ACPI event interrupts associated with the chip are freed
- * automatically.
+ * ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are
+ * handled by ACPI event methods which need to be called from the GPIO
+ * chip's interrupt handler. acpi_gpiochip_request_interrupts finds out which
+ * gpio pins have acpi event methods and assigns interrupt handlers that calls
+ * the acpi event methods for those pins.
*/
-static void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
+static void acpi_gpiochip_request_interrupts(struct acpi_gpio_chip *acpi_gpio)
{
- acpi_handle handle;
- acpi_status status;
- struct list_head *evt_pins;
- struct acpi_gpio_evt_pin *evt_pin, *ep;
+ struct gpio_chip *chip = acpi_gpio->chip;
if (!chip->dev || !chip->to_irq)
return;
- handle = ACPI_HANDLE(chip->dev);
- if (!handle)
- return;
+ INIT_LIST_HEAD(&acpi_gpio->events);
+ acpi_walk_resources(ACPI_HANDLE(chip->dev), "_AEI",
+ acpi_gpiochip_request_interrupt, acpi_gpio);
+}
- status = acpi_get_data(handle, acpi_gpio_evt_dh, (void **)&evt_pins);
- if (ACPI_FAILURE(status))
+/**
+ * acpi_gpiochip_free_interrupts() - Free GPIO ACPI event interrupts.
+ * @acpi_gpio: ACPI GPIO chip
+ *
+ * Free interrupts associated with GPIO ACPI event method for the given
+ * GPIO chip.
+ */
+static void acpi_gpiochip_free_interrupts(struct acpi_gpio_chip *acpi_gpio)
+{
+ struct acpi_gpio_event *event, *ep;
+ struct gpio_chip *chip = acpi_gpio->chip;
+
+ if (!chip->dev || !chip->to_irq)
return;
- list_for_each_entry_safe_reverse(evt_pin, ep, evt_pins, node) {
- devm_free_irq(chip->dev, evt_pin->irq, evt_pin);
- list_del(&evt_pin->node);
- kfree(evt_pin);
- }
+ list_for_each_entry_safe_reverse(event, ep, &acpi_gpio->events, node) {
+ struct gpio_desc *desc;
- acpi_detach_data(handle, acpi_gpio_evt_dh);
- kfree(evt_pins);
+ free_irq(event->irq, event);
+ desc = gpiochip_get_desc(chip, event->pin);
+ if (WARN_ON(IS_ERR(desc)))
+ continue;
+ gpiod_unlock_as_irq(desc);
+ gpiochip_free_own_desc(desc);
+ list_del(&event->node);
+ kfree(event);
+ }
}
struct acpi_gpio_lookup {
@@ -310,12 +348,202 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index,
return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT);
}
+static acpi_status
+acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
+ u32 bits, u64 *value, void *handler_context,
+ void *region_context)
+{
+ struct acpi_gpio_chip *achip = region_context;
+ struct gpio_chip *chip = achip->chip;
+ struct acpi_resource_gpio *agpio;
+ struct acpi_resource *ares;
+ acpi_status status;
+ bool pull_up;
+ int i;
+
+ status = acpi_buffer_to_resource(achip->conn_info.connection,
+ achip->conn_info.length, &ares);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO)) {
+ ACPI_FREE(ares);
+ return AE_BAD_PARAMETER;
+ }
+
+ agpio = &ares->data.gpio;
+ pull_up = agpio->pin_config == ACPI_PIN_CONFIG_PULLUP;
+
+ if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT &&
+ function == ACPI_WRITE)) {
+ ACPI_FREE(ares);
+ return AE_BAD_PARAMETER;
+ }
+
+ for (i = 0; i < agpio->pin_table_length; i++) {
+ unsigned pin = agpio->pin_table[i];
+ struct acpi_gpio_connection *conn;
+ struct gpio_desc *desc;
+ bool found;
+
+ desc = gpiochip_get_desc(chip, pin);
+ if (IS_ERR(desc)) {
+ status = AE_ERROR;
+ goto out;
+ }
+
+ mutex_lock(&achip->conn_lock);
+
+ found = false;
+ list_for_each_entry(conn, &achip->conns, node) {
+ if (conn->desc == desc) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ int ret;
+
+ ret = gpiochip_request_own_desc(desc, "ACPI:OpRegion");
+ if (ret) {
+ status = AE_ERROR;
+ mutex_unlock(&achip->conn_lock);
+ goto out;
+ }
+
+ switch (agpio->io_restriction) {
+ case ACPI_IO_RESTRICT_INPUT:
+ gpiod_direction_input(desc);
+ break;
+ case ACPI_IO_RESTRICT_OUTPUT:
+ /*
+ * ACPI GPIO resources don't contain an
+ * initial value for the GPIO. Therefore we
+ * deduce that value from the pull field
+ * instead. If the pin is pulled up we
+ * assume default to be high, otherwise
+ * low.
+ */
+ gpiod_direction_output(desc, pull_up);
+ break;
+ default:
+ /*
+ * Assume that the BIOS has configured the
+ * direction and pull accordingly.
+ */
+ break;
+ }
+
+ conn = kzalloc(sizeof(*conn), GFP_KERNEL);
+ if (!conn) {
+ status = AE_NO_MEMORY;
+ gpiochip_free_own_desc(desc);
+ mutex_unlock(&achip->conn_lock);
+ goto out;
+ }
+
+ conn->desc = desc;
+ list_add_tail(&conn->node, &achip->conns);
+ }
+
+ mutex_unlock(&achip->conn_lock);
+
+ if (function == ACPI_WRITE)
+ gpiod_set_raw_value(desc, !!((1 << i) & *value));
+ else
+ *value |= gpiod_get_raw_value(desc) << i;
+ }
+
+out:
+ ACPI_FREE(ares);
+ return status;
+}
+
+static void acpi_gpiochip_request_regions(struct acpi_gpio_chip *achip)
+{
+ struct gpio_chip *chip = achip->chip;
+ acpi_handle handle = ACPI_HANDLE(chip->dev);
+ acpi_status status;
+
+ INIT_LIST_HEAD(&achip->conns);
+ mutex_init(&achip->conn_lock);
+ status = acpi_install_address_space_handler(handle, ACPI_ADR_SPACE_GPIO,
+ acpi_gpio_adr_space_handler,
+ NULL, achip);
+ if (ACPI_FAILURE(status))
+ dev_err(chip->dev, "Failed to install GPIO OpRegion handler\n");
+}
+
+static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip)
+{
+ struct gpio_chip *chip = achip->chip;
+ acpi_handle handle = ACPI_HANDLE(chip->dev);
+ struct acpi_gpio_connection *conn, *tmp;
+ acpi_status status;
+
+ status = acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GPIO,
+ acpi_gpio_adr_space_handler);
+ if (ACPI_FAILURE(status)) {
+ dev_err(chip->dev, "Failed to remove GPIO OpRegion handler\n");
+ return;
+ }
+
+ list_for_each_entry_safe_reverse(conn, tmp, &achip->conns, node) {
+ gpiochip_free_own_desc(conn->desc);
+ list_del(&conn->node);
+ kfree(conn);
+ }
+}
+
void acpi_gpiochip_add(struct gpio_chip *chip)
{
- acpi_gpiochip_request_interrupts(chip);
+ struct acpi_gpio_chip *acpi_gpio;
+ acpi_handle handle;
+ acpi_status status;
+
+ handle = ACPI_HANDLE(chip->dev);
+ if (!handle)
+ return;
+
+ acpi_gpio = kzalloc(sizeof(*acpi_gpio), GFP_KERNEL);
+ if (!acpi_gpio) {
+ dev_err(chip->dev,
+ "Failed to allocate memory for ACPI GPIO chip\n");
+ return;
+ }
+
+ acpi_gpio->chip = chip;
+
+ status = acpi_attach_data(handle, acpi_gpio_chip_dh, acpi_gpio);
+ if (ACPI_FAILURE(status)) {
+ dev_err(chip->dev, "Failed to attach ACPI GPIO chip\n");
+ kfree(acpi_gpio);
+ return;
+ }
+
+ acpi_gpiochip_request_interrupts(acpi_gpio);
+ acpi_gpiochip_request_regions(acpi_gpio);
}
void acpi_gpiochip_remove(struct gpio_chip *chip)
{
- acpi_gpiochip_free_interrupts(chip);
+ struct acpi_gpio_chip *acpi_gpio;
+ acpi_handle handle;
+ acpi_status status;
+
+ handle = ACPI_HANDLE(chip->dev);
+ if (!handle)
+ return;
+
+ status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(chip->dev, "Failed to retrieve ACPI GPIO chip\n");
+ return;
+ }
+
+ acpi_gpiochip_free_regions(acpi_gpio);
+ acpi_gpiochip_free_interrupts(acpi_gpio);
+
+ acpi_detach_data(handle, acpi_gpio_chip_dh);
+ kfree(acpi_gpio);
}
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index e0a98f581f58..2024d45e5503 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -12,6 +12,7 @@
*/
#include <linux/device.h>
+#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/io.h>
@@ -90,7 +91,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
of_node_put(gg_data.gpiospec.np);
pr_debug("%s exited with status %d\n", __func__,
- PTR_RET(gg_data.out_gpio));
+ PTR_ERR_OR_ZERO(gg_data.out_gpio));
return gg_data.out_gpio;
}
EXPORT_SYMBOL(of_get_named_gpiod_flags);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 50c4922fe53a..761013f8b82f 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -164,16 +164,17 @@ struct gpio_desc *gpio_to_desc(unsigned gpio)
EXPORT_SYMBOL_GPL(gpio_to_desc);
/**
- * Convert an offset on a certain chip to a corresponding descriptor
+ * Get the GPIO descriptor corresponding to the given hw number for this chip.
*/
-static struct gpio_desc *gpiochip_offset_to_desc(struct gpio_chip *chip,
- unsigned int offset)
+struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
+ u16 hwnum)
{
- if (offset >= chip->ngpio)
+ if (hwnum >= chip->ngpio)
return ERR_PTR(-EINVAL);
- return &chip->desc[offset];
+ return &chip->desc[hwnum];
}
+EXPORT_SYMBOL_GPL(gpiochip_get_desc);
/**
* Convert a GPIO descriptor to the integer namespace.
@@ -350,9 +351,9 @@ static ssize_t gpio_direction_store(struct device *dev,
if (!test_bit(FLAG_EXPORT, &desc->flags))
status = -EIO;
else if (sysfs_streq(buf, "high"))
- status = gpiod_direction_output(desc, 1);
+ status = gpiod_direction_output_raw(desc, 1);
else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low"))
- status = gpiod_direction_output(desc, 0);
+ status = gpiod_direction_output_raw(desc, 0);
else if (sysfs_streq(buf, "in"))
status = gpiod_direction_input(desc);
else
@@ -1253,6 +1254,9 @@ fail:
}
EXPORT_SYMBOL_GPL(gpiochip_add);
+/* Forward-declaration */
+static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
+
/**
* gpiochip_remove() - unregister a gpio_chip
* @chip: the chip to unregister
@@ -1265,11 +1269,13 @@ int gpiochip_remove(struct gpio_chip *chip)
int status = 0;
unsigned id;
+ acpi_gpiochip_remove(chip);
+
spin_lock_irqsave(&gpio_lock, flags);
+ gpiochip_irqchip_remove(chip);
gpiochip_remove_pin_ranges(chip);
of_gpiochip_remove(chip);
- acpi_gpiochip_remove(chip);
for (id = 0; id < chip->ngpio; id++) {
if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) {
@@ -1337,6 +1343,215 @@ static struct gpio_chip *find_chip_by_name(const char *name)
return gpiochip_find((void *)name, gpiochip_match_name);
}
+#ifdef CONFIG_GPIOLIB_IRQCHIP
+
+/*
+ * The following is irqchip helper code for gpiochips.
+ */
+
+/**
+ * gpiochip_add_chained_irqchip() - adds a chained irqchip to a gpiochip
+ * @gpiochip: the gpiochip to add the irqchip to
+ * @irqchip: the irqchip to add to the gpiochip
+ * @parent_irq: the irq number corresponding to the parent IRQ for this
+ * chained irqchip
+ * @parent_handler: the parent interrupt handler for the accumulated IRQ
+ * coming out of the gpiochip
+ */
+void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
+ struct irq_chip *irqchip,
+ int parent_irq,
+ irq_flow_handler_t parent_handler)
+{
+ irq_set_chained_handler(parent_irq, parent_handler);
+ /*
+ * The parent irqchip is already using the chip_data for this
+ * irqchip, so our callbacks simply use the handler_data.
+ */
+ irq_set_handler_data(parent_irq, gpiochip);
+}
+EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip);
+
+/**
+ * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip
+ * @d: the irqdomain used by this irqchip
+ * @irq: the global irq number used by this GPIO irqchip irq
+ * @hwirq: the local IRQ/GPIO line offset on this gpiochip
+ *
+ * This function will set up the mapping for a certain IRQ line on a
+ * gpiochip by assigning the gpiochip as chip data, and using the irqchip
+ * stored inside the gpiochip.
+ */
+static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct gpio_chip *chip = d->host_data;
+
+ irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler);
+ irq_set_chip_data(irq, chip);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ irq_set_irq_type(irq, chip->irq_default_type);
+
+ return 0;
+}
+
+static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)
+{
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops gpiochip_domain_ops = {
+ .map = gpiochip_irq_map,
+ .unmap = gpiochip_irq_unmap,
+ /* Virtually all GPIO irqchips are twocell:ed */
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int gpiochip_irq_reqres(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ if (gpio_lock_as_irq(chip, d->hwirq)) {
+ chip_err(chip,
+ "unable to lock HW IRQ %lu for IRQ\n",
+ d->hwirq);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void gpiochip_irq_relres(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ gpio_unlock_as_irq(chip, d->hwirq);
+}
+
+static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return irq_find_mapping(chip->irqdomain, offset);
+}
+
+/**
+ * gpiochip_irqchip_remove() - removes an irqchip added to a gpiochip
+ * @gpiochip: the gpiochip to remove the irqchip from
+ *
+ * This is called only from gpiochip_remove()
+ */
+static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
+{
+ unsigned int offset;
+
+ /* Remove all IRQ mappings and delete the domain */
+ if (gpiochip->irqdomain) {
+ for (offset = 0; offset < gpiochip->ngpio; offset++)
+ irq_dispose_mapping(gpiochip->irq_base + offset);
+ irq_domain_remove(gpiochip->irqdomain);
+ }
+
+ if (gpiochip->irqchip) {
+ gpiochip->irqchip->irq_request_resources = NULL;
+ gpiochip->irqchip->irq_release_resources = NULL;
+ gpiochip->irqchip = NULL;
+ }
+}
+
+/**
+ * gpiochip_irqchip_add() - adds an irqchip to a gpiochip
+ * @gpiochip: the gpiochip to add the irqchip to
+ * @irqchip: the irqchip to add to the gpiochip
+ * @first_irq: if not dynamically assigned, the base (first) IRQ to
+ * allocate gpiochip irqs from
+ * @handler: the irq handler to use (often a predefined irq core function)
+ * @type: the default type for IRQs on this irqchip
+ *
+ * This function closely associates a certain irqchip with a certain
+ * gpiochip, providing an irq domain to translate the local IRQs to
+ * global irqs in the gpiolib core, and making sure that the gpiochip
+ * is passed as chip data to all related functions. Driver callbacks
+ * need to use container_of() to get their local state containers back
+ * from the gpiochip passed as chip data. An irqdomain will be stored
+ * in the gpiochip that shall be used by the driver to handle IRQ number
+ * translation. The gpiochip will need to be initialized and registered
+ * before calling this function.
+ *
+ * This function will handle two cell:ed simple IRQs and assumes all
+ * the pins on the gpiochip can generate a unique IRQ. Everything else
+ * need to be open coded.
+ */
+int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
+ struct irq_chip *irqchip,
+ unsigned int first_irq,
+ irq_flow_handler_t handler,
+ unsigned int type)
+{
+ struct device_node *of_node;
+ unsigned int offset;
+ unsigned irq_base = 0;
+
+ if (!gpiochip || !irqchip)
+ return -EINVAL;
+
+ if (!gpiochip->dev) {
+ pr_err("missing gpiochip .dev parent pointer\n");
+ return -EINVAL;
+ }
+ of_node = gpiochip->dev->of_node;
+#ifdef CONFIG_OF_GPIO
+ /*
+ * If the gpiochip has an assigned OF node this takes precendence
+ * FIXME: get rid of this and use gpiochip->dev->of_node everywhere
+ */
+ if (gpiochip->of_node)
+ of_node = gpiochip->of_node;
+#endif
+ gpiochip->irqchip = irqchip;
+ gpiochip->irq_handler = handler;
+ gpiochip->irq_default_type = type;
+ gpiochip->to_irq = gpiochip_to_irq;
+ gpiochip->irqdomain = irq_domain_add_simple(of_node,
+ gpiochip->ngpio, first_irq,
+ &gpiochip_domain_ops, gpiochip);
+ if (!gpiochip->irqdomain) {
+ gpiochip->irqchip = NULL;
+ return -EINVAL;
+ }
+ irqchip->irq_request_resources = gpiochip_irq_reqres;
+ irqchip->irq_release_resources = gpiochip_irq_relres;
+
+ /*
+ * Prepare the mapping since the irqchip shall be orthogonal to
+ * any gpiochip calls. If the first_irq was zero, this is
+ * necessary to allocate descriptors for all IRQs.
+ */
+ for (offset = 0; offset < gpiochip->ngpio; offset++) {
+ irq_base = irq_create_mapping(gpiochip->irqdomain, offset);
+ if (offset == 0)
+ /*
+ * Store the base into the gpiochip to be used when
+ * unmapping the irqs.
+ */
+ gpiochip->irq_base = irq_base;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_irqchip_add);
+
+#else /* CONFIG_GPIOLIB_IRQCHIP */
+
+static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {}
+
+#endif /* CONFIG_GPIOLIB_IRQCHIP */
+
#ifdef CONFIG_PINCTRL
/**
@@ -1457,26 +1672,14 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
* on each other, and help provide better diagnostics in debugfs.
* They're called even less than the "set direction" calls.
*/
-static int gpiod_request(struct gpio_desc *desc, const char *label)
+static int __gpiod_request(struct gpio_desc *desc, const char *label)
{
- struct gpio_chip *chip;
- int status = -EPROBE_DEFER;
+ struct gpio_chip *chip = desc->chip;
+ int status;
unsigned long flags;
- if (!desc) {
- pr_warn("%s: invalid GPIO\n", __func__);
- return -EINVAL;
- }
-
spin_lock_irqsave(&gpio_lock, flags);
- chip = desc->chip;
- if (chip == NULL)
- goto done;
-
- if (!try_module_get(chip->owner))
- goto done;
-
/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
*/
@@ -1486,7 +1689,6 @@ static int gpiod_request(struct gpio_desc *desc, const char *label)
status = 0;
} else {
status = -EBUSY;
- module_put(chip->owner);
goto done;
}
@@ -1498,7 +1700,6 @@ static int gpiod_request(struct gpio_desc *desc, const char *label)
if (status < 0) {
desc_set_label(desc, NULL);
- module_put(chip->owner);
clear_bit(FLAG_REQUESTED, &desc->flags);
goto done;
}
@@ -1510,9 +1711,34 @@ static int gpiod_request(struct gpio_desc *desc, const char *label)
spin_lock_irqsave(&gpio_lock, flags);
}
done:
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return status;
+}
+
+static int gpiod_request(struct gpio_desc *desc, const char *label)
+{
+ int status = -EPROBE_DEFER;
+ struct gpio_chip *chip;
+
+ if (!desc) {
+ pr_warn("%s: invalid GPIO\n", __func__);
+ return -EINVAL;
+ }
+
+ chip = desc->chip;
+ if (!chip)
+ goto done;
+
+ if (try_module_get(chip->owner)) {
+ status = __gpiod_request(desc, label);
+ if (status < 0)
+ module_put(chip->owner);
+ }
+
+done:
if (status)
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
- spin_unlock_irqrestore(&gpio_lock, flags);
+
return status;
}
@@ -1522,18 +1748,14 @@ int gpio_request(unsigned gpio, const char *label)
}
EXPORT_SYMBOL_GPL(gpio_request);
-static void gpiod_free(struct gpio_desc *desc)
+static bool __gpiod_free(struct gpio_desc *desc)
{
+ bool ret = false;
unsigned long flags;
struct gpio_chip *chip;
might_sleep();
- if (!desc) {
- WARN_ON(extra_checks);
- return;
- }
-
gpiod_unexport(desc);
spin_lock_irqsave(&gpio_lock, flags);
@@ -1547,15 +1769,23 @@ static void gpiod_free(struct gpio_desc *desc)
spin_lock_irqsave(&gpio_lock, flags);
}
desc_set_label(desc, NULL);
- module_put(desc->chip->owner);
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags);
clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
- } else
- WARN_ON(extra_checks);
+ ret = true;
+ }
spin_unlock_irqrestore(&gpio_lock, flags);
+ return ret;
+}
+
+static void gpiod_free(struct gpio_desc *desc)
+{
+ if (desc && __gpiod_free(desc))
+ module_put(desc->chip->owner);
+ else
+ WARN_ON(extra_checks);
}
void gpio_free(unsigned gpio)
@@ -1590,7 +1820,7 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
if (flags & GPIOF_DIR_IN)
err = gpiod_direction_input(desc);
else
- err = gpiod_direction_output(desc,
+ err = gpiod_direction_output_raw(desc,
(flags & GPIOF_INIT_HIGH) ? 1 : 0);
if (err)
@@ -1677,6 +1907,37 @@ const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
}
EXPORT_SYMBOL_GPL(gpiochip_is_requested);
+/**
+ * gpiochip_request_own_desc - Allow GPIO chip to request its own descriptor
+ * @desc: GPIO descriptor to request
+ * @label: label for the GPIO
+ *
+ * Function allows GPIO chip drivers to request and use their own GPIO
+ * descriptors via gpiolib API. Difference to gpiod_request() is that this
+ * function will not increase reference count of the GPIO chip module. This
+ * allows the GPIO chip module to be unloaded as needed (we assume that the
+ * GPIO chip driver handles freeing the GPIOs it has requested).
+ */
+int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label)
+{
+ if (!desc || !desc->chip)
+ return -EINVAL;
+
+ return __gpiod_request(desc, label);
+}
+
+/**
+ * gpiochip_free_own_desc - Free GPIO requested by the chip driver
+ * @desc: GPIO descriptor to free
+ *
+ * Function frees the given GPIO requested previously with
+ * gpiochip_request_own_desc().
+ */
+void gpiochip_free_own_desc(struct gpio_desc *desc)
+{
+ if (desc)
+ __gpiod_free(desc);
+}
/* Drivers MUST set GPIO direction before making get/set calls. In
* some cases this is done in early boot, before IRQs are enabled.
@@ -1756,28 +2017,13 @@ fail:
}
EXPORT_SYMBOL_GPL(gpiod_direction_input);
-/**
- * gpiod_direction_output - set the GPIO direction to input
- * @desc: GPIO to set to output
- * @value: initial output value of the GPIO
- *
- * Set the direction of the passed GPIO to output, such as gpiod_set_value() can
- * be called safely on it. The initial value of the output must be specified.
- *
- * Return 0 in case of success, else an error code.
- */
-int gpiod_direction_output(struct gpio_desc *desc, int value)
+static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
{
unsigned long flags;
struct gpio_chip *chip;
int status = -EINVAL;
int offset;
- if (!desc || !desc->chip) {
- pr_warn("%s: invalid GPIO\n", __func__);
- return -EINVAL;
- }
-
/* GPIOs used for IRQs shall not be set as output */
if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) {
gpiod_err(desc,
@@ -1840,6 +2086,50 @@ fail:
gpiod_dbg(desc, "%s: gpio status %d\n", __func__, status);
return status;
}
+
+/**
+ * gpiod_direction_output_raw - set the GPIO direction to output
+ * @desc: GPIO to set to output
+ * @value: initial output value of the GPIO
+ *
+ * Set the direction of the passed GPIO to output, such as gpiod_set_value() can
+ * be called safely on it. The initial value of the output must be specified
+ * as raw value on the physical line without regard for the ACTIVE_LOW status.
+ *
+ * Return 0 in case of success, else an error code.
+ */
+int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
+{
+ if (!desc || !desc->chip) {
+ pr_warn("%s: invalid GPIO\n", __func__);
+ return -EINVAL;
+ }
+ return _gpiod_direction_output_raw(desc, value);
+}
+EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
+
+/**
+ * gpiod_direction_output - set the GPIO direction to output
+ * @desc: GPIO to set to output
+ * @value: initial output value of the GPIO
+ *
+ * Set the direction of the passed GPIO to output, such as gpiod_set_value() can
+ * be called safely on it. The initial value of the output must be specified
+ * as the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
+ * account.
+ *
+ * Return 0 in case of success, else an error code.
+ */
+int gpiod_direction_output(struct gpio_desc *desc, int value)
+{
+ if (!desc || !desc->chip) {
+ pr_warn("%s: invalid GPIO\n", __func__);
+ return -EINVAL;
+ }
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ value = !value;
+ return _gpiod_direction_output_raw(desc, value);
+}
EXPORT_SYMBOL_GPL(gpiod_direction_output);
/**
@@ -1928,15 +2218,15 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low);
* that the GPIO was actually requested.
*/
-static int _gpiod_get_raw_value(const struct gpio_desc *desc)
+static bool _gpiod_get_raw_value(const struct gpio_desc *desc)
{
struct gpio_chip *chip;
- int value;
+ bool value;
int offset;
chip = desc->chip;
offset = gpio_chip_hwgpio(desc);
- value = chip->get ? chip->get(chip, offset) : 0;
+ value = chip->get ? chip->get(chip, offset) : false;
trace_gpio_value(desc_to_gpio(desc), 1, value);
return value;
}
@@ -1992,7 +2282,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
* @desc: gpio descriptor whose state need to be set.
* @value: Non-zero for setting it HIGH otherise it will set to LOW.
*/
-static void _gpio_set_open_drain_value(struct gpio_desc *desc, int value)
+static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
{
int err = 0;
struct gpio_chip *chip = desc->chip;
@@ -2019,7 +2309,7 @@ static void _gpio_set_open_drain_value(struct gpio_desc *desc, int value)
* @desc: gpio descriptor whose state need to be set.
* @value: Non-zero for setting it HIGH otherise it will set to LOW.
*/
-static void _gpio_set_open_source_value(struct gpio_desc *desc, int value)
+static void _gpio_set_open_source_value(struct gpio_desc *desc, bool value)
{
int err = 0;
struct gpio_chip *chip = desc->chip;
@@ -2041,7 +2331,7 @@ static void _gpio_set_open_source_value(struct gpio_desc *desc, int value)
__func__, err);
}
-static void _gpiod_set_raw_value(struct gpio_desc *desc, int value)
+static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
{
struct gpio_chip *chip;
@@ -2137,10 +2427,7 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq);
* @gpio: the GPIO line to lock as used for IRQ
*
* This is used directly by GPIO drivers that want to lock down
- * a certain GPIO line to be used as IRQs, for example in the
- * .to_irq() callback of their gpio_chip, or in the .irq_enable()
- * of its irq_chip implementation if the GPIO is known from that
- * code.
+ * a certain GPIO line to be used for IRQs.
*/
int gpiod_lock_as_irq(struct gpio_desc *desc)
{
@@ -2161,7 +2448,7 @@ EXPORT_SYMBOL_GPL(gpiod_lock_as_irq);
int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
{
- return gpiod_lock_as_irq(gpiochip_offset_to_desc(chip, offset));
+ return gpiod_lock_as_irq(gpiochip_get_desc(chip, offset));
}
EXPORT_SYMBOL_GPL(gpio_lock_as_irq);
@@ -2183,7 +2470,7 @@ EXPORT_SYMBOL_GPL(gpiod_unlock_as_irq);
void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
{
- return gpiod_unlock_as_irq(gpiochip_offset_to_desc(chip, offset));
+ return gpiod_unlock_as_irq(gpiochip_get_desc(chip, offset));
}
EXPORT_SYMBOL_GPL(gpio_unlock_as_irq);
@@ -2404,7 +2691,7 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
return ERR_PTR(-EINVAL);
}
- desc = gpiochip_offset_to_desc(chip, p->chip_hwnum);
+ desc = gpiochip_get_desc(chip, p->chip_hwnum);
*flags = p->flags;
return desc;
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 82be586c1f90..cf092941a9fd 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -43,4 +43,7 @@ acpi_get_gpiod_by_index(struct device *dev, int index,
}
#endif
+int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label);
+void gpiochip_free_own_desc(struct gpio_desc *desc);
+
#endif /* GPIOLIB_H */
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index ea92b827e787..f7a81209beb3 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -564,7 +564,7 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
* Caller must hold mode config lock.
*
* Setup a new configuration, provided by the upper layers (either an ioctl call
- * from userspace or internally e.g. from the fbdev suppport code) in @set, and
+ * from userspace or internally e.g. from the fbdev support code) in @set, and
* enable it. This is the main helper functions for drivers that implement
* kernel mode setting with the crtc helper functions and the assorted
* ->prepare(), ->modeset() and ->commit() helper callbacks.
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index 0eaf5a27e120..a8f9dba2a816 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -237,7 +237,6 @@ struct drm_exynos_file_private {
* otherwise default one.
* @da_space_size: size of device address space.
* if 0 then default value is used for it.
- * @da_space_order: order to device address space.
*/
struct exynos_drm_private {
struct drm_fb_helper *fb_helper;
@@ -255,7 +254,6 @@ struct exynos_drm_private {
unsigned long da_start;
unsigned long da_space_size;
- unsigned long da_space_order;
};
/*
diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.c b/drivers/gpu/drm/exynos/exynos_drm_iommu.c
index fb8db0378274..b32b291f88ff 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_iommu.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.c
@@ -36,12 +36,10 @@ int drm_create_iommu_mapping(struct drm_device *drm_dev)
priv->da_start = EXYNOS_DEV_ADDR_START;
if (!priv->da_space_size)
priv->da_space_size = EXYNOS_DEV_ADDR_SIZE;
- if (!priv->da_space_order)
- priv->da_space_order = EXYNOS_DEV_ADDR_ORDER;
mapping = arm_iommu_create_mapping(&platform_bus_type, priv->da_start,
- priv->da_space_size,
- priv->da_space_order);
+ priv->da_space_size);
+
if (IS_ERR(mapping))
return PTR_ERR(mapping);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
index 598e60f57d4b..72376d41c512 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
@@ -14,7 +14,6 @@
#define EXYNOS_DEV_ADDR_START 0x20000000
#define EXYNOS_DEV_ADDR_SIZE 0x40000000
-#define EXYNOS_DEV_ADDR_ORDER 0x0
#ifdef CONFIG_DRM_EXYNOS_IOMMU
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index c69d1e07a3a6..b6984971ce0c 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -3,7 +3,7 @@ config DRM_MSM
tristate "MSM DRM"
depends on DRM
depends on MSM_IOMMU
- depends on (ARCH_MSM && ARCH_MSM8960) || (ARM && COMPILE_TEST)
+ depends on ARCH_MSM8960 || (ARM && COMPILE_TEST)
select DRM_KMS_HELPER
select SHMEM
select TMPFS
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
index 912759daf562..86f4ead0441d 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -37,7 +37,7 @@ struct omap_connector {
void copy_timings_omap_to_drm(struct drm_display_mode *mode,
struct omap_video_timings *timings)
{
- mode->clock = timings->pixel_clock;
+ mode->clock = timings->pixelclock / 1000;
mode->hdisplay = timings->x_res;
mode->hsync_start = mode->hdisplay + timings->hfp;
@@ -68,7 +68,7 @@ void copy_timings_omap_to_drm(struct drm_display_mode *mode,
void copy_timings_drm_to_omap(struct omap_video_timings *timings,
struct drm_display_mode *mode)
{
- timings->pixel_clock = mode->clock;
+ timings->pixelclock = mode->clock * 1000;
timings->x_res = mode->hdisplay;
timings->hfp = mode->hsync_start - mode->hdisplay;
@@ -220,7 +220,7 @@ static int omap_connector_mode_valid(struct drm_connector *connector,
if (!r) {
/* check if vrefresh is still valid */
new_mode = drm_mode_duplicate(dev, mode);
- new_mode->clock = timings.pixel_clock;
+ new_mode->clock = timings.pixelclock / 1000;
new_mode->vrefresh = 0;
if (mode->vrefresh == drm_mode_vrefresh(new_mode))
ret = MODE_OK;
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index f7220011a00b..7af9d0b5dea1 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -175,6 +175,15 @@ config HID_PRODIKEYS
multimedia keyboard, but will lack support for the musical keyboard
and some additional multimedia keys.
+config HID_CP2112
+ tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
+ depends on USB_HID && I2C && GPIOLIB
+ ---help---
+ Support for Silicon Labs CP2112 HID USB to SMBus Master Bridge.
+ This is a HID device driver which registers as an i2c adapter
+ and gpiochip to expose these functions of the CP2112. The
+ customizable USB descriptor fields are exposed as sysfs attributes.
+
config HID_CYPRESS
tristate "Cypress mouse and barcode readers" if EXPERT
depends on HID
@@ -608,25 +617,27 @@ config HID_SAMSUNG
Support for Samsung InfraRed remote control or keyboards.
config HID_SONY
- tristate "Sony PS2/3 accessories"
+ tristate "Sony PS2/3/4 accessories"
depends on USB_HID
depends on NEW_LEDS
depends on LEDS_CLASS
+ select POWER_SUPPLY
---help---
Support for
* Sony PS3 6-axis controllers
+ * Sony PS4 DualShock 4 controllers
* Buzz controllers
* Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
* Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
config SONY_FF
- bool "Sony PS2/3 accessories force feedback support"
+ bool "Sony PS2/3/4 accessories force feedback support"
depends on HID_SONY
select INPUT_FF_MEMLESS
---help---
- Say Y here if you have a Sony PS2/3 accessory and want to enable force
- feedback support for it.
+ Say Y here if you have a Sony PS2/3/4 accessory and want to enable
+ force feedback support for it.
config HID_SPEEDLINK
tristate "Speedlink VAD Cezanne mouse support"
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 30e44318f87f..fc712dde02a4 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
+obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o
obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 2448d757b449..9e8064205bc7 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1248,6 +1248,11 @@ void hid_output_report(struct hid_report *report, __u8 *data)
}
EXPORT_SYMBOL_GPL(hid_output_report);
+static int hid_report_len(struct hid_report *report)
+{
+ return ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7;
+}
+
/*
* Allocator for buffer that is going to be passed to hid_output_report()
*/
@@ -1258,7 +1263,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags)
* of implement() working on 8 byte chunks
*/
- int len = ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7;
+ int len = hid_report_len(report);
return kmalloc(len, flags);
}
@@ -1314,6 +1319,41 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
return report;
}
+/*
+ * Implement a generic .request() callback, using .raw_request()
+ * DO NOT USE in hid drivers directly, but through hid_hw_request instead.
+ */
+void __hid_request(struct hid_device *hid, struct hid_report *report,
+ int reqtype)
+{
+ char *buf;
+ int ret;
+ int len;
+
+ buf = hid_alloc_report_buf(report, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ len = hid_report_len(report);
+
+ if (reqtype == HID_REQ_SET_REPORT)
+ hid_output_report(report, buf);
+
+ ret = hid->ll_driver->raw_request(hid, report->id, buf, len,
+ report->type, reqtype);
+ if (ret < 0) {
+ dbg_hid("unable to complete request: %d\n", ret);
+ goto out;
+ }
+
+ if (reqtype == HID_REQ_GET_REPORT)
+ hid_input_report(hid, report->type, buf, ret, 0);
+
+out:
+ kfree(buf);
+}
+EXPORT_SYMBOL_GPL(__hid_request);
+
int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
int interrupt)
{
@@ -1693,6 +1733,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) },
@@ -1782,6 +1823,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
@@ -2434,6 +2476,14 @@ int hid_add_device(struct hid_device *hdev)
return -ENODEV;
/*
+ * Check for the mandatory transport channel.
+ */
+ if (!hdev->ll_driver->raw_request) {
+ hid_err(hdev, "transport driver missing .raw_request()\n");
+ return -EINVAL;
+ }
+
+ /*
* Read the device report descriptor once and use as template
* for the driver-specific modifications.
*/
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
new file mode 100644
index 000000000000..56be85a9a77c
--- /dev/null
+++ b/drivers/hid/hid-cp2112.c
@@ -0,0 +1,1073 @@
+/*
+ * hid-cp2112.c - Silicon Labs HID USB to SMBus master bridge
+ * Copyright (c) 2013,2014 Uplogix, Inc.
+ * David Barksdale <dbarksdale@uplogix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+/*
+ * The Silicon Labs CP2112 chip is a USB HID device which provides an
+ * SMBus controller for talking to slave devices and 8 GPIO pins. The
+ * host communicates with the CP2112 via raw HID reports.
+ *
+ * Data Sheet:
+ * http://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf
+ * Programming Interface Specification:
+ * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN495.pdf
+ */
+
+#include <linux/gpio.h>
+#include <linux/hid.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/nls.h>
+#include <linux/usb/ch9.h>
+#include "hid-ids.h"
+
+enum {
+ CP2112_GPIO_CONFIG = 0x02,
+ CP2112_GPIO_GET = 0x03,
+ CP2112_GPIO_SET = 0x04,
+ CP2112_GET_VERSION_INFO = 0x05,
+ CP2112_SMBUS_CONFIG = 0x06,
+ CP2112_DATA_READ_REQUEST = 0x10,
+ CP2112_DATA_WRITE_READ_REQUEST = 0x11,
+ CP2112_DATA_READ_FORCE_SEND = 0x12,
+ CP2112_DATA_READ_RESPONSE = 0x13,
+ CP2112_DATA_WRITE_REQUEST = 0x14,
+ CP2112_TRANSFER_STATUS_REQUEST = 0x15,
+ CP2112_TRANSFER_STATUS_RESPONSE = 0x16,
+ CP2112_CANCEL_TRANSFER = 0x17,
+ CP2112_LOCK_BYTE = 0x20,
+ CP2112_USB_CONFIG = 0x21,
+ CP2112_MANUFACTURER_STRING = 0x22,
+ CP2112_PRODUCT_STRING = 0x23,
+ CP2112_SERIAL_STRING = 0x24,
+};
+
+enum {
+ STATUS0_IDLE = 0x00,
+ STATUS0_BUSY = 0x01,
+ STATUS0_COMPLETE = 0x02,
+ STATUS0_ERROR = 0x03,
+};
+
+enum {
+ STATUS1_TIMEOUT_NACK = 0x00,
+ STATUS1_TIMEOUT_BUS = 0x01,
+ STATUS1_ARBITRATION_LOST = 0x02,
+ STATUS1_READ_INCOMPLETE = 0x03,
+ STATUS1_WRITE_INCOMPLETE = 0x04,
+ STATUS1_SUCCESS = 0x05,
+};
+
+struct cp2112_smbus_config_report {
+ u8 report; /* CP2112_SMBUS_CONFIG */
+ __be32 clock_speed; /* Hz */
+ u8 device_address; /* Stored in the upper 7 bits */
+ u8 auto_send_read; /* 1 = enabled, 0 = disabled */
+ __be16 write_timeout; /* ms, 0 = no timeout */
+ __be16 read_timeout; /* ms, 0 = no timeout */
+ u8 scl_low_timeout; /* 1 = enabled, 0 = disabled */
+ __be16 retry_time; /* # of retries, 0 = no limit */
+} __packed;
+
+struct cp2112_usb_config_report {
+ u8 report; /* CP2112_USB_CONFIG */
+ __le16 vid; /* Vendor ID */
+ __le16 pid; /* Product ID */
+ u8 max_power; /* Power requested in 2mA units */
+ u8 power_mode; /* 0x00 = bus powered
+ 0x01 = self powered & regulator off
+ 0x02 = self powered & regulator on */
+ u8 release_major;
+ u8 release_minor;
+ u8 mask; /* What fields to program */
+} __packed;
+
+struct cp2112_read_req_report {
+ u8 report; /* CP2112_DATA_READ_REQUEST */
+ u8 slave_address;
+ __be16 length;
+} __packed;
+
+struct cp2112_write_read_req_report {
+ u8 report; /* CP2112_DATA_WRITE_READ_REQUEST */
+ u8 slave_address;
+ __be16 length;
+ u8 target_address_length;
+ u8 target_address[16];
+} __packed;
+
+struct cp2112_write_req_report {
+ u8 report; /* CP2112_DATA_WRITE_REQUEST */
+ u8 slave_address;
+ u8 length;
+ u8 data[61];
+} __packed;
+
+struct cp2112_force_read_report {
+ u8 report; /* CP2112_DATA_READ_FORCE_SEND */
+ __be16 length;
+} __packed;
+
+struct cp2112_xfer_status_report {
+ u8 report; /* CP2112_TRANSFER_STATUS_RESPONSE */
+ u8 status0; /* STATUS0_* */
+ u8 status1; /* STATUS1_* */
+ __be16 retries;
+ __be16 length;
+} __packed;
+
+struct cp2112_string_report {
+ u8 dummy; /* force .string to be aligned */
+ u8 report; /* CP2112_*_STRING */
+ u8 length; /* length in bytes of everyting after .report */
+ u8 type; /* USB_DT_STRING */
+ wchar_t string[30]; /* UTF16_LITTLE_ENDIAN string */
+} __packed;
+
+/* Number of times to request transfer status before giving up waiting for a
+ transfer to complete. This may need to be changed if SMBUS clock, retries,
+ or read/write/scl_low timeout settings are changed. */
+static const int XFER_STATUS_RETRIES = 10;
+
+/* Time in ms to wait for a CP2112_DATA_READ_RESPONSE or
+ CP2112_TRANSFER_STATUS_RESPONSE. */
+static const int RESPONSE_TIMEOUT = 50;
+
+static const struct hid_device_id cp2112_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, cp2112_devices);
+
+struct cp2112_device {
+ struct i2c_adapter adap;
+ struct hid_device *hdev;
+ wait_queue_head_t wait;
+ u8 read_data[61];
+ u8 read_length;
+ int xfer_status;
+ atomic_t read_avail;
+ atomic_t xfer_avail;
+ struct gpio_chip gc;
+};
+
+static int gpio_push_pull = 0xFF;
+module_param(gpio_push_pull, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(gpio_push_pull, "GPIO push-pull configuration bitmask");
+
+static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct cp2112_device *dev = container_of(chip, struct cp2112_device,
+ gc);
+ struct hid_device *hdev = dev->hdev;
+ u8 buf[5];
+ int ret;
+
+ ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
+ sizeof(buf), HID_FEATURE_REPORT,
+ HID_REQ_GET_REPORT);
+ if (ret != sizeof(buf)) {
+ hid_err(hdev, "error requesting GPIO config: %d\n", ret);
+ return ret;
+ }
+
+ buf[1] &= ~(1 << offset);
+ buf[2] = gpio_push_pull;
+
+ ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret < 0) {
+ hid_err(hdev, "error setting GPIO config: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct cp2112_device *dev = container_of(chip, struct cp2112_device,
+ gc);
+ struct hid_device *hdev = dev->hdev;
+ u8 buf[3];
+ int ret;
+
+ buf[0] = CP2112_GPIO_SET;
+ buf[1] = value ? 0xff : 0;
+ buf[2] = 1 << offset;
+
+ ret = hid_hw_raw_request(hdev, CP2112_GPIO_SET, buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret < 0)
+ hid_err(hdev, "error setting GPIO values: %d\n", ret);
+}
+
+static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct cp2112_device *dev = container_of(chip, struct cp2112_device,
+ gc);
+ struct hid_device *hdev = dev->hdev;
+ u8 buf[2];
+ int ret;
+
+ ret = hid_hw_raw_request(hdev, CP2112_GPIO_GET, buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+ if (ret != sizeof(buf)) {
+ hid_err(hdev, "error requesting GPIO values: %d\n", ret);
+ return ret;
+ }
+
+ return (buf[1] >> offset) & 1;
+}
+
+static int cp2112_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct cp2112_device *dev = container_of(chip, struct cp2112_device,
+ gc);
+ struct hid_device *hdev = dev->hdev;
+ u8 buf[5];
+ int ret;
+
+ cp2112_gpio_set(chip, offset, value);
+
+ ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
+ sizeof(buf), HID_FEATURE_REPORT,
+ HID_REQ_GET_REPORT);
+ if (ret != sizeof(buf)) {
+ hid_err(hdev, "error requesting GPIO config: %d\n", ret);
+ return ret;
+ }
+
+ buf[1] |= 1 << offset;
+ buf[2] = gpio_push_pull;
+
+ ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret < 0) {
+ hid_err(hdev, "error setting GPIO config: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cp2112_hid_get(struct hid_device *hdev, unsigned char report_number,
+ u8 *data, size_t count, unsigned char report_type)
+{
+ u8 *buf;
+ int ret;
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = hid_hw_raw_request(hdev, report_number, buf, count,
+ report_type, HID_REQ_GET_REPORT);
+ memcpy(data, buf, count);
+ kfree(buf);
+ return ret;
+}
+
+static int cp2112_hid_output(struct hid_device *hdev, u8 *data, size_t count,
+ unsigned char report_type)
+{
+ u8 *buf;
+ int ret;
+
+ buf = kmemdup(data, count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (report_type == HID_OUTPUT_REPORT)
+ ret = hid_hw_output_report(hdev, buf, count);
+ else
+ ret = hid_hw_raw_request(hdev, buf[0], buf, count, report_type,
+ HID_REQ_SET_REPORT);
+
+ kfree(buf);
+ return ret;
+}
+
+static int cp2112_wait(struct cp2112_device *dev, atomic_t *avail)
+{
+ int ret = 0;
+
+ /* We have sent either a CP2112_TRANSFER_STATUS_REQUEST or a
+ * CP2112_DATA_READ_FORCE_SEND and we are waiting for the response to
+ * come in cp2112_raw_event or timeout. There will only be one of these
+ * in flight at any one time. The timeout is extremely large and is a
+ * last resort if the CP2112 has died. If we do timeout we don't expect
+ * to receive the response which would cause data races, it's not like
+ * we can do anything about it anyway.
+ */
+ ret = wait_event_interruptible_timeout(dev->wait,
+ atomic_read(avail), msecs_to_jiffies(RESPONSE_TIMEOUT));
+ if (-ERESTARTSYS == ret)
+ return ret;
+ if (!ret)
+ return -ETIMEDOUT;
+
+ atomic_set(avail, 0);
+ return 0;
+}
+
+static int cp2112_xfer_status(struct cp2112_device *dev)
+{
+ struct hid_device *hdev = dev->hdev;
+ u8 buf[2];
+ int ret;
+
+ buf[0] = CP2112_TRANSFER_STATUS_REQUEST;
+ buf[1] = 0x01;
+ atomic_set(&dev->xfer_avail, 0);
+
+ ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT);
+ if (ret < 0) {
+ hid_warn(hdev, "Error requesting status: %d\n", ret);
+ return ret;
+ }
+
+ ret = cp2112_wait(dev, &dev->xfer_avail);
+ if (ret)
+ return ret;
+
+ return dev->xfer_status;
+}
+
+static int cp2112_read(struct cp2112_device *dev, u8 *data, size_t size)
+{
+ struct hid_device *hdev = dev->hdev;
+ struct cp2112_force_read_report report;
+ int ret;
+
+ report.report = CP2112_DATA_READ_FORCE_SEND;
+ report.length = cpu_to_be16(size);
+
+ atomic_set(&dev->read_avail, 0);
+
+ ret = cp2112_hid_output(hdev, &report.report, sizeof(report),
+ HID_OUTPUT_REPORT);
+ if (ret < 0) {
+ hid_warn(hdev, "Error requesting data: %d\n", ret);
+ return ret;
+ }
+
+ ret = cp2112_wait(dev, &dev->read_avail);
+ if (ret)
+ return ret;
+
+ hid_dbg(hdev, "read %d of %zd bytes requested\n",
+ dev->read_length, size);
+
+ if (size > dev->read_length)
+ size = dev->read_length;
+
+ memcpy(data, dev->read_data, size);
+ return dev->read_length;
+}
+
+static int cp2112_read_req(void *buf, u8 slave_address, u16 length)
+{
+ struct cp2112_read_req_report *report = buf;
+
+ if (length < 1 || length > 512)
+ return -EINVAL;
+
+ report->report = CP2112_DATA_READ_REQUEST;
+ report->slave_address = slave_address << 1;
+ report->length = cpu_to_be16(length);
+ return sizeof(*report);
+}
+
+static int cp2112_write_read_req(void *buf, u8 slave_address, u16 length,
+ u8 command, u8 *data, u8 data_length)
+{
+ struct cp2112_write_read_req_report *report = buf;
+
+ if (length < 1 || length > 512
+ || data_length > sizeof(report->target_address) - 1)
+ return -EINVAL;
+
+ report->report = CP2112_DATA_WRITE_READ_REQUEST;
+ report->slave_address = slave_address << 1;
+ report->length = cpu_to_be16(length);
+ report->target_address_length = data_length + 1;
+ report->target_address[0] = command;
+ memcpy(&report->target_address[1], data, data_length);
+ return data_length + 6;
+}
+
+static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data,
+ u8 data_length)
+{
+ struct cp2112_write_req_report *report = buf;
+
+ if (data_length > sizeof(report->data) - 1)
+ return -EINVAL;
+
+ report->report = CP2112_DATA_WRITE_REQUEST;
+ report->slave_address = slave_address << 1;
+ report->length = data_length + 1;
+ report->data[0] = command;
+ memcpy(&report->data[1], data, data_length);
+ return data_length + 4;
+}
+
+static int cp2112_xfer(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data;
+ struct hid_device *hdev = dev->hdev;
+ u8 buf[64];
+ __be16 word;
+ ssize_t count;
+ size_t read_length = 0;
+ unsigned int retries;
+ int ret;
+
+ hid_dbg(hdev, "%s addr 0x%x flags 0x%x cmd 0x%x size %d\n",
+ read_write == I2C_SMBUS_WRITE ? "write" : "read",
+ addr, flags, command, size);
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ read_length = 1;
+
+ if (I2C_SMBUS_READ == read_write)
+ count = cp2112_read_req(buf, addr, read_length);
+ else
+ count = cp2112_write_req(buf, addr, data->byte, NULL,
+ 0);
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ read_length = 1;
+
+ if (I2C_SMBUS_READ == read_write)
+ count = cp2112_write_read_req(buf, addr, read_length,
+ command, NULL, 0);
+ else
+ count = cp2112_write_req(buf, addr, command,
+ &data->byte, 1);
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ read_length = 2;
+ word = cpu_to_be16(data->word);
+
+ if (I2C_SMBUS_READ == read_write)
+ count = cp2112_write_read_req(buf, addr, read_length,
+ command, NULL, 0);
+ else
+ count = cp2112_write_req(buf, addr, command,
+ (u8 *)&word, 2);
+ break;
+ case I2C_SMBUS_PROC_CALL:
+ size = I2C_SMBUS_WORD_DATA;
+ read_write = I2C_SMBUS_READ;
+ read_length = 2;
+ word = cpu_to_be16(data->word);
+
+ count = cp2112_write_read_req(buf, addr, read_length, command,
+ (u8 *)&word, 2);
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ size = I2C_SMBUS_BLOCK_DATA;
+ /* fallthrough */
+ case I2C_SMBUS_BLOCK_DATA:
+ if (I2C_SMBUS_READ == read_write) {
+ count = cp2112_write_read_req(buf, addr,
+ I2C_SMBUS_BLOCK_MAX,
+ command, NULL, 0);
+ } else {
+ count = cp2112_write_req(buf, addr, command,
+ data->block,
+ data->block[0] + 1);
+ }
+ break;
+ case I2C_SMBUS_BLOCK_PROC_CALL:
+ size = I2C_SMBUS_BLOCK_DATA;
+ read_write = I2C_SMBUS_READ;
+
+ count = cp2112_write_read_req(buf, addr, I2C_SMBUS_BLOCK_MAX,
+ command, data->block,
+ data->block[0] + 1);
+ break;
+ default:
+ hid_warn(hdev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
+ }
+
+ if (count < 0)
+ return count;
+
+ ret = hid_hw_power(hdev, PM_HINT_FULLON);
+ if (ret < 0) {
+ hid_err(hdev, "power management error: %d\n", ret);
+ return ret;
+ }
+
+ ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT);
+ if (ret < 0) {
+ hid_warn(hdev, "Error starting transaction: %d\n", ret);
+ goto power_normal;
+ }
+
+ for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) {
+ ret = cp2112_xfer_status(dev);
+ if (-EBUSY == ret)
+ continue;
+ if (ret < 0)
+ goto power_normal;
+ break;
+ }
+
+ if (XFER_STATUS_RETRIES <= retries) {
+ hid_warn(hdev, "Transfer timed out, cancelling.\n");
+ buf[0] = CP2112_CANCEL_TRANSFER;
+ buf[1] = 0x01;
+
+ ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT);
+ if (ret < 0)
+ hid_warn(hdev, "Error cancelling transaction: %d\n",
+ ret);
+
+ ret = -ETIMEDOUT;
+ goto power_normal;
+ }
+
+ if (I2C_SMBUS_WRITE == read_write) {
+ ret = 0;
+ goto power_normal;
+ }
+
+ if (I2C_SMBUS_BLOCK_DATA == size)
+ read_length = ret;
+
+ ret = cp2112_read(dev, buf, read_length);
+ if (ret < 0)
+ goto power_normal;
+ if (ret != read_length) {
+ hid_warn(hdev, "short read: %d < %zd\n", ret, read_length);
+ ret = -EIO;
+ goto power_normal;
+ }
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+ data->byte = buf[0];
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ data->word = be16_to_cpup((__be16 *)buf);
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ if (read_length > I2C_SMBUS_BLOCK_MAX) {
+ ret = -EPROTO;
+ goto power_normal;
+ }
+
+ memcpy(data->block, buf, read_length);
+ break;
+ }
+
+ ret = 0;
+power_normal:
+ hid_hw_power(hdev, PM_HINT_NORMAL);
+ hid_dbg(hdev, "transfer finished: %d\n", ret);
+ return ret;
+}
+
+static u32 cp2112_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK |
+ I2C_FUNC_SMBUS_PROC_CALL |
+ I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
+}
+
+static const struct i2c_algorithm smbus_algorithm = {
+ .smbus_xfer = cp2112_xfer,
+ .functionality = cp2112_functionality,
+};
+
+static int cp2112_get_usb_config(struct hid_device *hdev,
+ struct cp2112_usb_config_report *cfg)
+{
+ int ret;
+
+ ret = cp2112_hid_get(hdev, CP2112_USB_CONFIG, (u8 *)cfg, sizeof(*cfg),
+ HID_FEATURE_REPORT);
+ if (ret != sizeof(*cfg)) {
+ hid_err(hdev, "error reading usb config: %d\n", ret);
+ if (ret < 0)
+ return ret;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cp2112_set_usb_config(struct hid_device *hdev,
+ struct cp2112_usb_config_report *cfg)
+{
+ int ret;
+
+ BUG_ON(cfg->report != CP2112_USB_CONFIG);
+
+ ret = cp2112_hid_output(hdev, (u8 *)cfg, sizeof(*cfg),
+ HID_FEATURE_REPORT);
+ if (ret != sizeof(*cfg)) {
+ hid_err(hdev, "error writing usb config: %d\n", ret);
+ if (ret < 0)
+ return ret;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void chmod_sysfs_attrs(struct hid_device *hdev);
+
+#define CP2112_CONFIG_ATTR(name, store, format, ...) \
+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 cp2112_usb_config_report cfg; \
+ int ret = cp2112_get_usb_config(hdev, &cfg); \
+ if (ret) \
+ return ret; \
+ store; \
+ ret = cp2112_set_usb_config(hdev, &cfg); \
+ if (ret) \
+ return ret; \
+ chmod_sysfs_attrs(hdev); \
+ return count; \
+} \
+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 cp2112_usb_config_report cfg; \
+ int ret = cp2112_get_usb_config(hdev, &cfg); \
+ if (ret) \
+ return ret; \
+ return scnprintf(buf, PAGE_SIZE, format, ##__VA_ARGS__); \
+} \
+static DEVICE_ATTR_RW(name);
+
+CP2112_CONFIG_ATTR(vendor_id, ({
+ u16 vid;
+
+ if (sscanf(buf, "%hi", &vid) != 1)
+ return -EINVAL;
+
+ cfg.vid = cpu_to_le16(vid);
+ cfg.mask = 0x01;
+}), "0x%04x\n", le16_to_cpu(cfg.vid));
+
+CP2112_CONFIG_ATTR(product_id, ({
+ u16 pid;
+
+ if (sscanf(buf, "%hi", &pid) != 1)
+ return -EINVAL;
+
+ cfg.pid = cpu_to_le16(pid);
+ cfg.mask = 0x02;
+}), "0x%04x\n", le16_to_cpu(cfg.pid));
+
+CP2112_CONFIG_ATTR(max_power, ({
+ int mA;
+
+ if (sscanf(buf, "%i", &mA) != 1)
+ return -EINVAL;
+
+ cfg.max_power = (mA + 1) / 2;
+ cfg.mask = 0x04;
+}), "%u mA\n", cfg.max_power * 2);
+
+CP2112_CONFIG_ATTR(power_mode, ({
+ if (sscanf(buf, "%hhi", &cfg.power_mode) != 1)
+ return -EINVAL;
+
+ cfg.mask = 0x08;
+}), "%u\n", cfg.power_mode);
+
+CP2112_CONFIG_ATTR(release_version, ({
+ if (sscanf(buf, "%hhi.%hhi", &cfg.release_major, &cfg.release_minor)
+ != 2)
+ return -EINVAL;
+
+ cfg.mask = 0x10;
+}), "%u.%u\n", cfg.release_major, cfg.release_minor);
+
+#undef CP2112_CONFIG_ATTR
+
+struct cp2112_pstring_attribute {
+ struct device_attribute attr;
+ unsigned char report;
+};
+
+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 cp2112_pstring_attribute *attr =
+ container_of(kattr, struct cp2112_pstring_attribute, attr);
+ struct cp2112_string_report report;
+ int ret;
+
+ memset(&report, 0, sizeof(report));
+
+ ret = utf8s_to_utf16s(buf, count, UTF16_LITTLE_ENDIAN,
+ report.string, ARRAY_SIZE(report.string));
+ report.report = attr->report;
+ report.length = ret * sizeof(report.string[0]) + 2;
+ report.type = USB_DT_STRING;
+
+ ret = cp2112_hid_output(hdev, &report.report, report.length + 1,
+ HID_FEATURE_REPORT);
+ if (ret != report.length + 1) {
+ hid_err(hdev, "error writing %s string: %d\n", kattr->attr.name,
+ ret);
+ if (ret < 0)
+ return ret;
+ return -EIO;
+ }
+
+ chmod_sysfs_attrs(hdev);
+ return count;
+}
+
+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 cp2112_pstring_attribute *attr =
+ container_of(kattr, struct cp2112_pstring_attribute, attr);
+ struct cp2112_string_report report;
+ u8 length;
+ int ret;
+
+ ret = cp2112_hid_get(hdev, attr->report, &report.report,
+ sizeof(report) - 1, HID_FEATURE_REPORT);
+ if (ret < 3) {
+ hid_err(hdev, "error reading %s string: %d\n", kattr->attr.name,
+ ret);
+ if (ret < 0)
+ return ret;
+ return -EIO;
+ }
+
+ if (report.length < 2) {
+ hid_err(hdev, "invalid %s string length: %d\n",
+ kattr->attr.name, report.length);
+ return -EIO;
+ }
+
+ length = report.length > ret - 1 ? ret - 1 : report.length;
+ length = (length - 2) / sizeof(report.string[0]);
+ ret = utf16s_to_utf8s(report.string, length, UTF16_LITTLE_ENDIAN, buf,
+ PAGE_SIZE - 1);
+ buf[ret++] = '\n';
+ return ret;
+}
+
+#define CP2112_PSTR_ATTR(name, _report) \
+static struct cp2112_pstring_attribute dev_attr_##name = { \
+ .attr = __ATTR(name, (S_IWUSR | S_IRUGO), pstr_show, pstr_store), \
+ .report = _report, \
+};
+
+CP2112_PSTR_ATTR(manufacturer, CP2112_MANUFACTURER_STRING);
+CP2112_PSTR_ATTR(product, CP2112_PRODUCT_STRING);
+CP2112_PSTR_ATTR(serial, CP2112_SERIAL_STRING);
+
+#undef CP2112_PSTR_ATTR
+
+static const struct attribute_group cp2112_attr_group = {
+ .attrs = (struct attribute *[]){
+ &dev_attr_vendor_id.attr,
+ &dev_attr_product_id.attr,
+ &dev_attr_max_power.attr,
+ &dev_attr_power_mode.attr,
+ &dev_attr_release_version.attr,
+ &dev_attr_manufacturer.attr.attr,
+ &dev_attr_product.attr.attr,
+ &dev_attr_serial.attr.attr,
+ NULL
+ }
+};
+
+/* Chmoding our sysfs attributes is simply a way to expose which fields in the
+ * PROM have already been programmed. We do not depend on this preventing
+ * writing to these attributes since the CP2112 will simply ignore writes to
+ * already-programmed fields. This is why there is no sense in fixing this
+ * racy behaviour.
+ */
+static void chmod_sysfs_attrs(struct hid_device *hdev)
+{
+ struct attribute **attr;
+ u8 buf[2];
+ int ret;
+
+ ret = cp2112_hid_get(hdev, CP2112_LOCK_BYTE, buf, sizeof(buf),
+ HID_FEATURE_REPORT);
+ if (ret != sizeof(buf)) {
+ hid_err(hdev, "error reading lock byte: %d\n", ret);
+ return;
+ }
+
+ for (attr = cp2112_attr_group.attrs; *attr; ++attr) {
+ umode_t mode = (buf[1] & 1) ? S_IWUSR | S_IRUGO : S_IRUGO;
+ ret = sysfs_chmod_file(&hdev->dev.kobj, *attr, mode);
+ if (ret < 0)
+ hid_err(hdev, "error chmoding sysfs file %s\n",
+ (*attr)->name);
+ buf[1] >>= 1;
+ }
+}
+
+static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ struct cp2112_device *dev;
+ u8 buf[3];
+ struct cp2112_smbus_config_report config;
+ int ret;
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "parse failed\n");
+ return ret;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ return ret;
+ }
+
+ ret = hid_hw_open(hdev);
+ if (ret) {
+ hid_err(hdev, "hw open failed\n");
+ goto err_hid_stop;
+ }
+
+ ret = hid_hw_power(hdev, PM_HINT_FULLON);
+ if (ret < 0) {
+ hid_err(hdev, "power management error: %d\n", ret);
+ goto err_hid_close;
+ }
+
+ ret = cp2112_hid_get(hdev, CP2112_GET_VERSION_INFO, buf, sizeof(buf),
+ HID_FEATURE_REPORT);
+ if (ret != sizeof(buf)) {
+ hid_err(hdev, "error requesting version\n");
+ if (ret >= 0)
+ ret = -EIO;
+ goto err_power_normal;
+ }
+
+ hid_info(hdev, "Part Number: 0x%02X Device Version: 0x%02X\n",
+ buf[1], buf[2]);
+
+ ret = cp2112_hid_get(hdev, CP2112_SMBUS_CONFIG, (u8 *)&config,
+ sizeof(config), HID_FEATURE_REPORT);
+ if (ret != sizeof(config)) {
+ hid_err(hdev, "error requesting SMBus config\n");
+ if (ret >= 0)
+ ret = -EIO;
+ goto err_power_normal;
+ }
+
+ config.retry_time = cpu_to_be16(1);
+
+ ret = cp2112_hid_output(hdev, (u8 *)&config, sizeof(config),
+ HID_FEATURE_REPORT);
+ if (ret != sizeof(config)) {
+ hid_err(hdev, "error setting SMBus config\n");
+ if (ret >= 0)
+ ret = -EIO;
+ goto err_power_normal;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err_power_normal;
+ }
+
+ hid_set_drvdata(hdev, (void *)dev);
+ dev->hdev = hdev;
+ dev->adap.owner = THIS_MODULE;
+ dev->adap.class = I2C_CLASS_HWMON;
+ dev->adap.algo = &smbus_algorithm;
+ dev->adap.algo_data = dev;
+ dev->adap.dev.parent = &hdev->dev;
+ snprintf(dev->adap.name, sizeof(dev->adap.name),
+ "CP2112 SMBus Bridge on hiddev%d", hdev->minor);
+ init_waitqueue_head(&dev->wait);
+
+ hid_device_io_start(hdev);
+ ret = i2c_add_adapter(&dev->adap);
+ hid_device_io_stop(hdev);
+
+ if (ret) {
+ hid_err(hdev, "error registering i2c adapter\n");
+ goto err_free_dev;
+ }
+
+ hid_dbg(hdev, "adapter registered\n");
+
+ dev->gc.label = "cp2112_gpio";
+ dev->gc.direction_input = cp2112_gpio_direction_input;
+ dev->gc.direction_output = cp2112_gpio_direction_output;
+ dev->gc.set = cp2112_gpio_set;
+ dev->gc.get = cp2112_gpio_get;
+ dev->gc.base = -1;
+ dev->gc.ngpio = 8;
+ dev->gc.can_sleep = 1;
+ dev->gc.dev = &hdev->dev;
+
+ ret = gpiochip_add(&dev->gc);
+ if (ret < 0) {
+ hid_err(hdev, "error registering gpio chip\n");
+ goto err_free_i2c;
+ }
+
+ ret = sysfs_create_group(&hdev->dev.kobj, &cp2112_attr_group);
+ if (ret < 0) {
+ hid_err(hdev, "error creating sysfs attrs\n");
+ goto err_gpiochip_remove;
+ }
+
+ chmod_sysfs_attrs(hdev);
+ hid_hw_power(hdev, PM_HINT_NORMAL);
+
+ return ret;
+
+err_gpiochip_remove:
+ if (gpiochip_remove(&dev->gc) < 0)
+ hid_err(hdev, "error removing gpio chip\n");
+err_free_i2c:
+ i2c_del_adapter(&dev->adap);
+err_free_dev:
+ kfree(dev);
+err_power_normal:
+ hid_hw_power(hdev, PM_HINT_NORMAL);
+err_hid_close:
+ hid_hw_close(hdev);
+err_hid_stop:
+ hid_hw_stop(hdev);
+ return ret;
+}
+
+static void cp2112_remove(struct hid_device *hdev)
+{
+ struct cp2112_device *dev = hid_get_drvdata(hdev);
+
+ sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group);
+ if (gpiochip_remove(&dev->gc))
+ hid_err(hdev, "unable to remove gpio chip\n");
+ i2c_del_adapter(&dev->adap);
+ /* i2c_del_adapter has finished removing all i2c devices from our
+ * adapter. Well behaved devices should no longer call our cp2112_xfer
+ * and should have waited for any pending calls to finish. It has also
+ * waited for device_unregister(&adap->dev) to complete. Therefore we
+ * can safely free our struct cp2112_device.
+ */
+ hid_hw_close(hdev);
+ hid_hw_stop(hdev);
+ kfree(dev);
+}
+
+static int cp2112_raw_event(struct hid_device *hdev, struct hid_report *report,
+ u8 *data, int size)
+{
+ struct cp2112_device *dev = hid_get_drvdata(hdev);
+ struct cp2112_xfer_status_report *xfer = (void *)data;
+
+ switch (data[0]) {
+ case CP2112_TRANSFER_STATUS_RESPONSE:
+ hid_dbg(hdev, "xfer status: %02x %02x %04x %04x\n",
+ xfer->status0, xfer->status1,
+ be16_to_cpu(xfer->retries), be16_to_cpu(xfer->length));
+
+ switch (xfer->status0) {
+ case STATUS0_IDLE:
+ dev->xfer_status = -EAGAIN;
+ break;
+ case STATUS0_BUSY:
+ dev->xfer_status = -EBUSY;
+ break;
+ case STATUS0_COMPLETE:
+ dev->xfer_status = be16_to_cpu(xfer->length);
+ break;
+ case STATUS0_ERROR:
+ switch (xfer->status1) {
+ case STATUS1_TIMEOUT_NACK:
+ case STATUS1_TIMEOUT_BUS:
+ dev->xfer_status = -ETIMEDOUT;
+ break;
+ default:
+ dev->xfer_status = -EIO;
+ break;
+ }
+ break;
+ default:
+ dev->xfer_status = -EINVAL;
+ break;
+ }
+
+ atomic_set(&dev->xfer_avail, 1);
+ break;
+ case CP2112_DATA_READ_RESPONSE:
+ hid_dbg(hdev, "read response: %02x %02x\n", data[1], data[2]);
+
+ dev->read_length = data[2];
+ if (dev->read_length > sizeof(dev->read_data))
+ dev->read_length = sizeof(dev->read_data);
+
+ memcpy(dev->read_data, &data[3], dev->read_length);
+ atomic_set(&dev->read_avail, 1);
+ break;
+ default:
+ hid_err(hdev, "unknown report\n");
+
+ return 0;
+ }
+
+ wake_up_interruptible(&dev->wait);
+ return 1;
+}
+
+static struct hid_driver cp2112_driver = {
+ .name = "cp2112",
+ .id_table = cp2112_devices,
+ .probe = cp2112_probe,
+ .remove = cp2112_remove,
+ .raw_event = cp2112_raw_event,
+};
+
+module_hid_driver(cp2112_driver);
+MODULE_DESCRIPTION("Silicon Labs HID USB to SMBus master bridge");
+MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c
index c24908f14934..f52dbcb7133b 100644
--- a/drivers/hid/hid-hyperv.c
+++ b/drivers/hid/hid-hyperv.c
@@ -460,12 +460,22 @@ static void mousevsc_hid_stop(struct hid_device *hid)
{
}
+static int mousevsc_hid_raw_request(struct hid_device *hid,
+ unsigned char report_num,
+ __u8 *buf, size_t len,
+ unsigned char rtype,
+ int reqtype)
+{
+ return 0;
+}
+
static struct hid_ll_driver mousevsc_ll_driver = {
.parse = mousevsc_hid_parse,
.open = mousevsc_hid_open,
.close = mousevsc_hid_close,
.start = mousevsc_hid_start,
.stop = mousevsc_hid_stop,
+ .raw_request = mousevsc_hid_raw_request,
};
static struct hid_driver mousevsc_hid_driver;
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 830de69d2df0..bd221263c739 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -67,6 +67,9 @@
#define USB_VENDOR_ID_ALPS 0x0433
#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101
+#define USB_VENDOR_ID_ANTON 0x1130
+#define USB_DEVICE_ID_ANTON_TOUCH_PAD 0x3101
+
#define USB_VENDOR_ID_APPLE 0x05ac
#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d
@@ -242,6 +245,7 @@
#define USB_VENDOR_ID_CYGNAL 0x10c4
#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a
#define USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH 0x81b9
+#define USB_DEVICE_ID_CYGNAL_CP2112 0xea90
#define USB_DEVICE_ID_CYGNAL_RADIO_SI4713 0x8244
@@ -616,6 +620,7 @@
#define USB_VENDOR_ID_MICROSOFT 0x045e
#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b
+#define USB_DEVICE_ID_MS_OFFICE_KB 0x0048
#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d
#define USB_DEVICE_ID_MS_NE4K 0x00db
#define USB_DEVICE_ID_MS_NE4K_JP 0x00dc
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index a713e6211419..e7e8b19a9284 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -350,9 +350,9 @@ static int hidinput_get_battery_property(struct power_supply *psy,
ret = -ENOMEM;
break;
}
- ret = dev->hid_get_raw_report(dev, dev->battery_report_id,
- buf, 2,
- dev->battery_report_type);
+ ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2,
+ dev->battery_report_type,
+ HID_REQ_GET_REPORT);
if (ret != 2) {
ret = -ENODATA;
@@ -789,10 +789,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case 0x199: map_key_clear(KEY_CHAT); break;
case 0x19c: map_key_clear(KEY_LOGOFF); break;
case 0x19e: map_key_clear(KEY_COFFEE); break;
+ case 0x1a3: map_key_clear(KEY_NEXT); break;
+ case 0x1a4: map_key_clear(KEY_PREVIOUS); break;
case 0x1a6: map_key_clear(KEY_HELP); break;
case 0x1a7: map_key_clear(KEY_DOCUMENTS); break;
case 0x1ab: map_key_clear(KEY_SPELLCHECK); break;
case 0x1ae: map_key_clear(KEY_KEYBOARD); break;
+ case 0x1b4: map_key_clear(KEY_FILE); break;
case 0x1b6: map_key_clear(KEY_IMAGES); break;
case 0x1b7: map_key_clear(KEY_AUDIO); break;
case 0x1b8: map_key_clear(KEY_VIDEO); break;
@@ -1150,7 +1153,7 @@ static void hidinput_led_worker(struct work_struct *work)
led_work);
struct hid_field *field;
struct hid_report *report;
- int len;
+ int len, ret;
__u8 *buf;
field = hidinput_get_led_field(hid);
@@ -1184,7 +1187,10 @@ static void hidinput_led_worker(struct work_struct *work)
hid_output_report(report, buf);
/* synchronous output report */
- hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT);
+ ret = hid_hw_output_report(hid, buf, len);
+ if (ret == -ENOSYS)
+ hid_hw_raw_request(hid, report->id, buf, len, HID_OUTPUT_REPORT,
+ HID_REQ_SET_REPORT);
kfree(buf);
}
@@ -1263,10 +1269,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)
}
input_set_drvdata(input_dev, hid);
- if (hid->ll_driver->hidinput_input_event)
- input_dev->event = hid->ll_driver->hidinput_input_event;
- else if (hid->ll_driver->request || hid->hid_output_raw_report)
- input_dev->event = hidinput_input_event;
+ input_dev->event = hidinput_input_event;
input_dev->open = hidinput_open;
input_dev->close = hidinput_close;
input_dev->setkeycode = hidinput_setkeycode;
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index 9fe9d4ac3114..a976f48263f6 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -692,7 +692,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret >= 0) {
/* insert a little delay of 10 jiffies ~ 40ms */
@@ -704,7 +705,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
buf[1] = 0xB2;
get_random_bytes(&buf[2], 2);
- ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
}
}
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index f45279c3b11a..486dbde2ba2d 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -44,14 +44,6 @@ static const char kbd_descriptor[] = {
0x19, 0xE0, /* USAGE_MINIMUM (Left Control) */
0x29, 0xE7, /* USAGE_MAXIMUM (Right GUI) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
- 0x95, 0x05, /* REPORT COUNT (5) */
- 0x05, 0x08, /* USAGE PAGE (LED page) */
- 0x19, 0x01, /* USAGE MINIMUM (1) */
- 0x29, 0x05, /* USAGE MAXIMUM (5) */
- 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */
- 0x95, 0x01, /* REPORT COUNT (1) */
- 0x75, 0x03, /* REPORT SIZE (3) */
- 0x91, 0x01, /* OUTPUT (Constant) */
0x95, 0x06, /* REPORT_COUNT (6) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
@@ -60,6 +52,18 @@ static const char kbd_descriptor[] = {
0x19, 0x00, /* USAGE_MINIMUM (no event) */
0x2A, 0xFF, 0x00, /* USAGE_MAXIMUM (reserved) */
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
+ 0x85, 0x0e, /* REPORT_ID (14) */
+ 0x05, 0x08, /* USAGE PAGE (LED page) */
+ 0x95, 0x05, /* REPORT COUNT (5) */
+ 0x75, 0x01, /* REPORT SIZE (1) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
+ 0x19, 0x01, /* USAGE MINIMUM (1) */
+ 0x29, 0x05, /* USAGE MAXIMUM (5) */
+ 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */
+ 0x95, 0x01, /* REPORT COUNT (1) */
+ 0x75, 0x03, /* REPORT SIZE (3) */
+ 0x91, 0x01, /* OUTPUT (Constant) */
0xC0
};
@@ -189,9 +193,6 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {
static struct hid_ll_driver logi_dj_ll_driver;
-static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
- size_t count,
- unsigned char report_type);
static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
@@ -258,7 +259,6 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
}
dj_hiddev->ll_driver = &logi_dj_ll_driver;
- dj_hiddev->hid_output_raw_report = logi_dj_output_hidraw_report;
dj_hiddev->dev.parent = &djrcv_hdev->dev;
dj_hiddev->bus = BUS_USB;
@@ -540,14 +540,35 @@ static void logi_dj_ll_close(struct hid_device *hid)
dbg_hid("%s:%s\n", __func__, hid->phys);
}
-static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
- size_t count,
- unsigned char report_type)
+static int logi_dj_ll_raw_request(struct hid_device *hid,
+ unsigned char reportnum, __u8 *buf,
+ size_t count, unsigned char report_type,
+ int reqtype)
{
- /* Called by hid raw to send data */
- dbg_hid("%s\n", __func__);
+ struct dj_device *djdev = hid->driver_data;
+ struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev;
+ u8 *out_buf;
+ int ret;
- return 0;
+ if (buf[0] != REPORT_TYPE_LEDS)
+ return -EINVAL;
+
+ out_buf = kzalloc(DJREPORT_SHORT_LENGTH, GFP_ATOMIC);
+ if (!out_buf)
+ return -ENOMEM;
+
+ if (count < DJREPORT_SHORT_LENGTH - 2)
+ count = DJREPORT_SHORT_LENGTH - 2;
+
+ out_buf[0] = REPORT_ID_DJ_SHORT;
+ out_buf[1] = djdev->device_index;
+ memcpy(out_buf + 2, buf, count);
+
+ ret = hid_hw_raw_request(djrcv_dev->hdev, out_buf[0], out_buf,
+ DJREPORT_SHORT_LENGTH, report_type, reqtype);
+
+ kfree(out_buf);
+ return ret;
}
static void rdcat(char *rdesc, unsigned int *rsize, const char *data, unsigned int size)
@@ -613,58 +634,6 @@ static int logi_dj_ll_parse(struct hid_device *hid)
return retval;
}
-static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
- unsigned int code, int value)
-{
- /* Sent by the input layer to handle leds and Force Feedback */
- struct hid_device *dj_hiddev = input_get_drvdata(dev);
- struct dj_device *dj_dev = dj_hiddev->driver_data;
-
- struct dj_receiver_dev *djrcv_dev =
- dev_get_drvdata(dj_hiddev->dev.parent);
- struct hid_device *dj_rcv_hiddev = djrcv_dev->hdev;
- struct hid_report_enum *output_report_enum;
-
- struct hid_field *field;
- struct hid_report *report;
- unsigned char *data;
- int offset;
-
- dbg_hid("%s: %s, type:%d | code:%d | value:%d\n",
- __func__, dev->phys, type, code, value);
-
- if (type != EV_LED)
- return -1;
-
- offset = hidinput_find_field(dj_hiddev, type, code, &field);
-
- if (offset == -1) {
- dev_warn(&dev->dev, "event field not found\n");
- return -1;
- }
- hid_set_field(field, offset, value);
-
- data = hid_alloc_report_buf(field->report, GFP_ATOMIC);
- if (!data) {
- dev_warn(&dev->dev, "failed to allocate report buf memory\n");
- return -1;
- }
-
- hid_output_report(field->report, &data[0]);
-
- output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT];
- report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
- hid_set_field(report->field[0], 0, dj_dev->device_index);
- hid_set_field(report->field[0], 1, REPORT_TYPE_LEDS);
- hid_set_field(report->field[0], 2, data[1]);
-
- hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT);
-
- kfree(data);
-
- return 0;
-}
-
static int logi_dj_ll_start(struct hid_device *hid)
{
dbg_hid("%s\n", __func__);
@@ -683,7 +652,7 @@ static struct hid_ll_driver logi_dj_ll_driver = {
.stop = logi_dj_ll_stop,
.open = logi_dj_ll_open,
.close = logi_dj_ll_close,
- .hidinput_input_event = logi_dj_ll_input_event,
+ .raw_request = logi_dj_ll_raw_request,
};
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 3b43d1cfa936..ecc2cbf300cc 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -538,8 +538,8 @@ static int magicmouse_probe(struct hid_device *hdev,
* but there seems to be no other way of switching the mode.
* Thus the super-ugly hacky success check below.
*/
- ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature),
- HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, feature[0], feature, sizeof(feature),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret != -EIO && ret != sizeof(feature)) {
hid_err(hdev, "unable to request touch data (%d)\n", ret);
goto err_stop_hw;
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 404a3a8a82f1..6fd58175a291 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -62,9 +62,48 @@ static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,
{
struct input_dev *input = hi->input;
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
+ switch (usage->hid & HID_USAGE) {
+ /*
+ * Microsoft uses these 2 reserved usage ids for 2 keys on
+ * the MS office kb labelled "Office Home" and "Task Pane".
+ */
+ case 0x29d:
+ ms_map_key_clear(KEY_PROG1);
+ return 1;
+ case 0x29e:
+ ms_map_key_clear(KEY_PROG2);
+ return 1;
+ }
+ return 0;
+ }
+
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
+ return 0;
+
switch (usage->hid & HID_USAGE) {
case 0xfd06: ms_map_key_clear(KEY_CHAT); break;
case 0xfd07: ms_map_key_clear(KEY_PHONE); break;
+ case 0xff00:
+ /* Special keypad keys */
+ ms_map_key_clear(KEY_KPEQUAL);
+ set_bit(KEY_KPLEFTPAREN, input->keybit);
+ set_bit(KEY_KPRIGHTPAREN, input->keybit);
+ break;
+ case 0xff01:
+ /* Scroll wheel */
+ hid_map_usage_clear(hi, usage, bit, max, EV_REL, REL_WHEEL);
+ break;
+ case 0xff02:
+ /*
+ * This byte contains a copy of the modifier keys byte of a
+ * standard hid keyboard report, as send by interface 0
+ * (this usage is found on interface 1).
+ *
+ * This byte only gets send when another key in the same report
+ * changes state, and as such is useless, ignore it.
+ */
+ return -1;
case 0xff05:
set_bit(EV_REP, input->evbit);
ms_map_key_clear(KEY_F13);
@@ -83,6 +122,9 @@ static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,
static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
unsigned long **bit, int *max)
{
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
+ return 0;
+
set_bit(EV_REP, hi->input->evbit);
switch (usage->hid & HID_USAGE) {
case 0xfd08: ms_map_key_clear(KEY_FORWARD); break;
@@ -102,9 +144,6 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
{
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
- if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
- return 0;
-
if (quirks & MS_ERGONOMY) {
int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max);
if (ret)
@@ -134,14 +173,39 @@ static int ms_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+ struct input_dev *input;
if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
!usage->type)
return 0;
+ input = field->hidinput->input;
+
/* Handling MS keyboards special buttons */
+ if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff00)) {
+ /* Special keypad keys */
+ input_report_key(input, KEY_KPEQUAL, value & 0x01);
+ input_report_key(input, KEY_KPLEFTPAREN, value & 0x02);
+ input_report_key(input, KEY_KPRIGHTPAREN, value & 0x04);
+ return 1;
+ }
+
+ if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff01)) {
+ /* Scroll wheel */
+ int step = ((value & 0x60) >> 5) + 1;
+
+ switch (value & 0x1f) {
+ case 0x01:
+ input_report_rel(input, REL_WHEEL, step);
+ break;
+ case 0x1f:
+ input_report_rel(input, REL_WHEEL, -step);
+ break;
+ }
+ return 1;
+ }
+
if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
- struct input_dev *input = field->hidinput->input;
static unsigned int last_key = 0;
unsigned int key = 0;
switch (value) {
@@ -194,6 +258,8 @@ err_free:
static const struct hid_device_id ms_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),
.driver_data = MS_HIDINPUT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB),
+ .driver_data = MS_ERGONOMY },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K),
.driver_data = MS_ERGONOMY },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP),
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 221d503f1c24..35278e43c7a4 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -68,6 +68,9 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_HOVERING (1 << 11)
#define MT_QUIRK_CONTACT_CNT_ACCURATE (1 << 12)
+#define MT_INPUTMODE_TOUCHSCREEN 0x02
+#define MT_INPUTMODE_TOUCHPAD 0x03
+
struct mt_slot {
__s32 x, y, cx, cy, p, w, h;
__s32 contactid; /* the device ContactID assigned to this slot */
@@ -84,6 +87,7 @@ struct mt_class {
__s32 sn_pressure; /* Signal/noise ratio for pressure events */
__u8 maxcontacts;
bool is_indirect; /* true for touchpads */
+ bool export_all_inputs; /* do not ignore mouse, keyboards, etc... */
};
struct mt_fields {
@@ -100,11 +104,11 @@ struct mt_device {
int cc_value_index; /* contact count value index in the field */
unsigned last_slot_field; /* the last field of a slot */
unsigned mt_report_id; /* the report ID of the multitouch device */
- unsigned pen_report_id; /* the report ID of the pen device */
__s16 inputmode; /* InputMode HID feature, -1 if non-existent */
__s16 inputmode_index; /* InputMode HID feature index in the report */
__s16 maxcontact_report_id; /* Maximum Contact Number HID feature,
-1 if non-existent */
+ __u8 inputmode_value; /* InputMode HID feature value */
__u8 num_received; /* how many contacts we received */
__u8 num_expected; /* expected last contact index */
__u8 maxcontacts;
@@ -128,16 +132,17 @@ static void mt_post_parse(struct mt_device *td);
#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0005
#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0006
#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0007
-#define MT_CLS_DUAL_NSMU_CONTACTID 0x0008
+/* reserved 0x0008 */
#define MT_CLS_INRANGE_CONTACTNUMBER 0x0009
#define MT_CLS_NSMU 0x000a
-#define MT_CLS_DUAL_CONTACT_NUMBER 0x0010
-#define MT_CLS_DUAL_CONTACT_ID 0x0011
+/* reserved 0x0010 */
+/* reserved 0x0011 */
#define MT_CLS_WIN_8 0x0012
+#define MT_CLS_EXPORT_ALL_INPUTS 0x0013
/* vendor specific classes */
#define MT_CLS_3M 0x0101
-#define MT_CLS_CYPRESS 0x0102
+/* reserved 0x0102 */
#define MT_CLS_EGALAX 0x0103
#define MT_CLS_EGALAX_SERIAL 0x0104
#define MT_CLS_TOPSEED 0x0105
@@ -189,28 +194,18 @@ static struct mt_class mt_classes[] = {
.quirks = MT_QUIRK_VALID_IS_INRANGE |
MT_QUIRK_SLOT_IS_CONTACTNUMBER,
.maxcontacts = 2 },
- { .name = MT_CLS_DUAL_NSMU_CONTACTID,
- .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
- MT_QUIRK_SLOT_IS_CONTACTID,
- .maxcontacts = 2 },
{ .name = MT_CLS_INRANGE_CONTACTNUMBER,
.quirks = MT_QUIRK_VALID_IS_INRANGE |
MT_QUIRK_SLOT_IS_CONTACTNUMBER },
- { .name = MT_CLS_DUAL_CONTACT_NUMBER,
- .quirks = MT_QUIRK_ALWAYS_VALID |
- MT_QUIRK_CONTACT_CNT_ACCURATE |
- MT_QUIRK_SLOT_IS_CONTACTNUMBER,
- .maxcontacts = 2 },
- { .name = MT_CLS_DUAL_CONTACT_ID,
- .quirks = MT_QUIRK_ALWAYS_VALID |
- MT_QUIRK_CONTACT_CNT_ACCURATE |
- MT_QUIRK_SLOT_IS_CONTACTID,
- .maxcontacts = 2 },
{ .name = MT_CLS_WIN_8,
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_IGNORE_DUPLICATES |
MT_QUIRK_HOVERING |
MT_QUIRK_CONTACT_CNT_ACCURATE },
+ { .name = MT_CLS_EXPORT_ALL_INPUTS,
+ .quirks = MT_QUIRK_ALWAYS_VALID |
+ MT_QUIRK_CONTACT_CNT_ACCURATE,
+ .export_all_inputs = true },
/*
* vendor specific classes
@@ -223,10 +218,6 @@ static struct mt_class mt_classes[] = {
.sn_height = 128,
.maxcontacts = 60,
},
- { .name = MT_CLS_CYPRESS,
- .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
- MT_QUIRK_CYPRESS,
- .maxcontacts = 10 },
{ .name = MT_CLS_EGALAX,
.quirks = MT_QUIRK_SLOT_IS_CONTACTID |
MT_QUIRK_VALID_IS_INRANGE,
@@ -360,45 +351,6 @@ static void mt_store_field(struct hid_usage *usage, struct mt_device *td,
f->usages[f->length++] = usage->hid;
}
-static int mt_pen_input_mapping(struct hid_device *hdev, struct hid_input *hi,
- struct hid_field *field, struct hid_usage *usage,
- unsigned long **bit, int *max)
-{
- struct mt_device *td = hid_get_drvdata(hdev);
-
- td->pen_report_id = field->report->id;
-
- return 0;
-}
-
-static int mt_pen_input_mapped(struct hid_device *hdev, struct hid_input *hi,
- struct hid_field *field, struct hid_usage *usage,
- unsigned long **bit, int *max)
-{
- return 0;
-}
-
-static int mt_pen_event(struct hid_device *hid, struct hid_field *field,
- struct hid_usage *usage, __s32 value)
-{
- /* let hid-input handle it */
- return 0;
-}
-
-static void mt_pen_report(struct hid_device *hid, struct hid_report *report)
-{
- struct hid_field *field = report->field[0];
-
- input_sync(field->hidinput->input);
-}
-
-static void mt_pen_input_configured(struct hid_device *hdev,
- struct hid_input *hi)
-{
- /* force BTN_STYLUS to allow tablet matching in udev */
- __set_bit(BTN_STYLUS, hi->input->keybit);
-}
-
static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
@@ -415,8 +367,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
* Model touchscreens providing buttons as touchpads.
*/
if (field->application == HID_DG_TOUCHPAD ||
- (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
+ (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
td->mt_flags |= INPUT_MT_POINTER;
+ td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
+ }
if (usage->usage_index)
prev_usage = &field->usage[usage->usage_index - 1];
@@ -776,28 +730,52 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- /* Only map fields from TouchScreen or TouchPad collections.
- * We need to ignore fields that belong to other collections
- * such as Mouse that might have the same GenericDesktop usages. */
- if (field->application != HID_DG_TOUCHSCREEN &&
+ struct mt_device *td = hid_get_drvdata(hdev);
+
+ /*
+ * If mtclass.export_all_inputs is not set, only map fields from
+ * TouchScreen or TouchPad collections. We need to ignore fields
+ * that belong to other collections such as Mouse that might have
+ * the same GenericDesktop usages.
+ */
+ if (!td->mtclass.export_all_inputs &&
+ field->application != HID_DG_TOUCHSCREEN &&
field->application != HID_DG_PEN &&
field->application != HID_DG_TOUCHPAD)
return -1;
+ /*
+ * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN"
+ * for the stylus.
+ */
if (field->physical == HID_DG_STYLUS)
- return mt_pen_input_mapping(hdev, hi, field, usage, bit, max);
+ return 0;
- return mt_touch_input_mapping(hdev, hi, field, usage, bit, max);
+ if (field->application == HID_DG_TOUCHSCREEN ||
+ field->application == HID_DG_TOUCHPAD)
+ return mt_touch_input_mapping(hdev, hi, field, usage, bit, max);
+
+ /* let hid-core decide for the others */
+ return 0;
}
static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
+ /*
+ * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN"
+ * for the stylus.
+ */
if (field->physical == HID_DG_STYLUS)
- return mt_pen_input_mapped(hdev, hi, field, usage, bit, max);
+ return 0;
- return mt_touch_input_mapped(hdev, hi, field, usage, bit, max);
+ if (field->application == HID_DG_TOUCHSCREEN ||
+ field->application == HID_DG_TOUCHPAD)
+ return mt_touch_input_mapped(hdev, hi, field, usage, bit, max);
+
+ /* let hid-core decide for the others */
+ return 0;
}
static int mt_event(struct hid_device *hid, struct hid_field *field,
@@ -808,25 +786,22 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
if (field->report->id == td->mt_report_id)
return mt_touch_event(hid, field, usage, value);
- if (field->report->id == td->pen_report_id)
- return mt_pen_event(hid, field, usage, value);
-
- /* ignore other reports */
- return 1;
+ return 0;
}
static void mt_report(struct hid_device *hid, struct hid_report *report)
{
struct mt_device *td = hid_get_drvdata(hid);
+ struct hid_field *field = report->field[0];
if (!(hid->claimed & HID_CLAIMED_INPUT))
return;
if (report->id == td->mt_report_id)
- mt_touch_report(hid, report);
+ return mt_touch_report(hid, report);
- if (report->id == td->pen_report_id)
- mt_pen_report(hid, report);
+ if (field && field->hidinput && field->hidinput->input)
+ input_sync(field->hidinput->input);
}
static void mt_set_input_mode(struct hid_device *hdev)
@@ -841,7 +816,7 @@ static void mt_set_input_mode(struct hid_device *hdev)
re = &(hdev->report_enum[HID_FEATURE_REPORT]);
r = re->report_id_hash[td->inputmode];
if (r) {
- r->field[0]->value[td->inputmode_index] = 0x02;
+ r->field[0]->value[td->inputmode_index] = td->inputmode_value;
hid_hw_request(hdev, r, HID_REQ_SET_REPORT);
}
}
@@ -907,13 +882,49 @@ static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
struct mt_device *td = hid_get_drvdata(hdev);
char *name;
const char *suffix = NULL;
+ struct hid_field *field = hi->report->field[0];
if (hi->report->id == td->mt_report_id)
mt_touch_input_configured(hdev, hi);
+ /*
+ * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN"
+ * for the stylus. Check this first, and then rely on the application
+ * field.
+ */
if (hi->report->field[0]->physical == HID_DG_STYLUS) {
suffix = "Pen";
- mt_pen_input_configured(hdev, hi);
+ /* force BTN_STYLUS to allow tablet matching in udev */
+ __set_bit(BTN_STYLUS, hi->input->keybit);
+ } else {
+ switch (field->application) {
+ case HID_GD_KEYBOARD:
+ suffix = "Keyboard";
+ break;
+ case HID_GD_KEYPAD:
+ suffix = "Keypad";
+ break;
+ case HID_GD_MOUSE:
+ suffix = "Mouse";
+ break;
+ case HID_DG_STYLUS:
+ suffix = "Pen";
+ /* force BTN_STYLUS to allow tablet matching in udev */
+ __set_bit(BTN_STYLUS, hi->input->keybit);
+ break;
+ case HID_DG_TOUCHSCREEN:
+ /* we do not set suffix = "Touchscreen" */
+ break;
+ case HID_GD_SYSTEM_CONTROL:
+ suffix = "System Control";
+ break;
+ case HID_CP_CONSUMER_CONTROL:
+ suffix = "Consumer Control";
+ break;
+ default:
+ suffix = "UNKNOWN";
+ break;
+ }
}
if (suffix) {
@@ -973,9 +984,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
td->mtclass = *mtclass;
td->inputmode = -1;
td->maxcontact_report_id = -1;
+ td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
td->cc_index = -1;
td->mt_report_id = -1;
- td->pen_report_id = -1;
hid_set_drvdata(hdev, td);
td->fields = devm_kzalloc(&hdev->dev, sizeof(struct mt_fields),
@@ -1034,6 +1045,12 @@ static void mt_remove(struct hid_device *hdev)
hid_hw_stop(hdev);
}
+/*
+ * This list contains only:
+ * - VID/PID of products not working with the default multitouch handling
+ * - 2 generic rules.
+ * So there is no point in adding here any device with MT_CLS_DEFAULT.
+ */
static const struct hid_device_id mt_devices[] = {
/* 3M panels */
@@ -1047,33 +1064,25 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_3M,
USB_DEVICE_ID_3M3266) },
- /* ActionStar panels */
- { .driver_data = MT_CLS_NSMU,
- MT_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR,
- USB_DEVICE_ID_ACTIONSTAR_1011) },
+ /* Anton devices */
+ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
+ MT_USB_DEVICE(USB_VENDOR_ID_ANTON,
+ USB_DEVICE_ID_ANTON_TOUCH_PAD) },
/* Atmel panels */
{ .driver_data = MT_CLS_SERIAL,
MT_USB_DEVICE(USB_VENDOR_ID_ATMEL,
- USB_DEVICE_ID_ATMEL_MULTITOUCH) },
- { .driver_data = MT_CLS_SERIAL,
- MT_USB_DEVICE(USB_VENDOR_ID_ATMEL,
USB_DEVICE_ID_ATMEL_MXT_DIGITIZER) },
/* Baanto multitouch devices */
{ .driver_data = MT_CLS_NSMU,
MT_USB_DEVICE(USB_VENDOR_ID_BAANTO,
USB_DEVICE_ID_BAANTO_MT_190W2) },
+
/* Cando panels */
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
MT_USB_DEVICE(USB_VENDOR_ID_CANDO,
USB_DEVICE_ID_CANDO_MULTI_TOUCH) },
- { .driver_data = MT_CLS_DUAL_CONTACT_NUMBER,
- MT_USB_DEVICE(USB_VENDOR_ID_CANDO,
- USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) },
- { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
- MT_USB_DEVICE(USB_VENDOR_ID_CANDO,
- USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) },
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
MT_USB_DEVICE(USB_VENDOR_ID_CANDO,
USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) },
@@ -1088,16 +1097,6 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_CVTOUCH,
USB_DEVICE_ID_CVTOUCH_SCREEN) },
- /* Cypress panel */
- { .driver_data = MT_CLS_CYPRESS,
- HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS,
- USB_DEVICE_ID_CYPRESS_TRUETOUCH) },
-
- /* Data Modul easyMaxTouch */
- { .driver_data = MT_CLS_DEFAULT,
- MT_USB_DEVICE(USB_VENDOR_ID_DATA_MODUL,
- USB_VENDOR_ID_DATA_MODUL_EASYMAXTOUCH) },
-
/* eGalax devices (resistive) */
{ .driver_data = MT_CLS_EGALAX,
MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
@@ -1156,11 +1155,6 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
- /* Elo TouchSystems IntelliTouch Plus panel */
- { .driver_data = MT_CLS_DUAL_CONTACT_ID,
- MT_USB_DEVICE(USB_VENDOR_ID_ELO,
- USB_DEVICE_ID_ELO_TS2515) },
-
/* Flatfrog Panels */
{ .driver_data = MT_CLS_FLATFROG,
MT_USB_DEVICE(USB_VENDOR_ID_FLATFROG,
@@ -1209,37 +1203,11 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT,
USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) },
- /* Ideacom panel */
- { .driver_data = MT_CLS_SERIAL,
- MT_USB_DEVICE(USB_VENDOR_ID_IDEACOM,
- USB_DEVICE_ID_IDEACOM_IDC6650) },
- { .driver_data = MT_CLS_SERIAL,
- MT_USB_DEVICE(USB_VENDOR_ID_IDEACOM,
- USB_DEVICE_ID_IDEACOM_IDC6651) },
-
/* Ilitek dual touch panel */
{ .driver_data = MT_CLS_NSMU,
MT_USB_DEVICE(USB_VENDOR_ID_ILITEK,
USB_DEVICE_ID_ILITEK_MULTITOUCH) },
- /* IRTOUCH panels */
- { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
- MT_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS,
- USB_DEVICE_ID_IRTOUCH_INFRARED_USB) },
-
- /* LG Display panels */
- { .driver_data = MT_CLS_DEFAULT,
- MT_USB_DEVICE(USB_VENDOR_ID_LG,
- USB_DEVICE_ID_LG_MULTITOUCH) },
-
- /* Lumio panels */
- { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
- MT_USB_DEVICE(USB_VENDOR_ID_LUMIO,
- USB_DEVICE_ID_CRYSTALTOUCH) },
- { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
- MT_USB_DEVICE(USB_VENDOR_ID_LUMIO,
- USB_DEVICE_ID_CRYSTALTOUCH_DUAL) },
-
/* MosArt panels */
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
MT_USB_DEVICE(USB_VENDOR_ID_ASUS,
@@ -1251,11 +1219,6 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_TURBOX,
USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) },
- /* Nexio panels */
- { .driver_data = MT_CLS_DEFAULT,
- MT_USB_DEVICE(USB_VENDOR_ID_NEXIO,
- USB_DEVICE_ID_NEXIO_MULTITOUCH_420)},
-
/* Panasonic panels */
{ .driver_data = MT_CLS_PANASONIC,
MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC,
@@ -1269,11 +1232,6 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_NOVATEK,
USB_DEVICE_ID_NOVATEK_PCT) },
- /* PenMount panels */
- { .driver_data = MT_CLS_CONFIDENCE,
- MT_USB_DEVICE(USB_VENDOR_ID_PENMOUNT,
- USB_DEVICE_ID_PENMOUNT_PCI) },
-
/* PixArt optical touch screen */
{ .driver_data = MT_CLS_INRANGE_CONTACTNUMBER,
MT_USB_DEVICE(USB_VENDOR_ID_PIXART,
@@ -1287,44 +1245,18 @@ static const struct hid_device_id mt_devices[] = {
/* PixCir-based panels */
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
- MT_USB_DEVICE(USB_VENDOR_ID_HANVON,
- USB_DEVICE_ID_HANVON_MULTITOUCH) },
- { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
MT_USB_DEVICE(USB_VENDOR_ID_CANDO,
USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) },
/* Quanta-based panels */
{ .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID,
MT_USB_DEVICE(USB_VENDOR_ID_QUANTA,
- USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
- { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID,
- MT_USB_DEVICE(USB_VENDOR_ID_QUANTA,
USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001) },
- { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID,
- MT_USB_DEVICE(USB_VENDOR_ID_QUANTA,
- USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) },
-
- /* SiS panels */
- { .driver_data = MT_CLS_DEFAULT,
- HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH,
- USB_DEVICE_ID_SIS9200_TOUCH) },
- { .driver_data = MT_CLS_DEFAULT,
- HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH,
- USB_DEVICE_ID_SIS817_TOUCH) },
- { .driver_data = MT_CLS_DEFAULT,
- HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH,
- USB_DEVICE_ID_SIS1030_TOUCH) },
/* Stantum panels */
{ .driver_data = MT_CLS_CONFIDENCE,
- MT_USB_DEVICE(USB_VENDOR_ID_STANTUM,
- USB_DEVICE_ID_MTP)},
- { .driver_data = MT_CLS_CONFIDENCE,
MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM,
USB_DEVICE_ID_MTP_STM)},
- { .driver_data = MT_CLS_DEFAULT,
- MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX,
- USB_DEVICE_ID_MTP_SITRONIX)},
/* TopSeed panels */
{ .driver_data = MT_CLS_TOPSEED,
@@ -1383,11 +1315,6 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_CSR2) },
- /* Zytronic panels */
- { .driver_data = MT_CLS_SERIAL,
- MT_USB_DEVICE(USB_VENDOR_ID_ZYTRONIC,
- USB_DEVICE_ID_ZYTRONIC_ZXY100) },
-
/* Generic MT device */
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) },
diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
index 59d5eb1e742c..cf1a9f1c1217 100644
--- a/drivers/hid/hid-picolcd_cir.c
+++ b/drivers/hid/hid-picolcd_cir.c
@@ -114,7 +114,7 @@ int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
rdev->priv = data;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
rdev->open = picolcd_cir_open;
rdev->close = picolcd_cir_close;
rdev->input_name = data->hdev->name;
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index 9c22e14c57f0..5182031f7b52 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -56,9 +56,9 @@ struct sensor_hub_pending {
* @dyn_callback_lock: spin lock to protect callback list
* @hid_sensor_hub_client_devs: Stores all MFD cells for a hub instance.
* @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached).
+ * @ref_cnt: Number of MFD clients have opened this device
*/
struct sensor_hub_data {
- struct hid_sensor_hub_device *hsdev;
struct mutex mutex;
spinlock_t lock;
struct sensor_hub_pending pending;
@@ -67,6 +67,7 @@ struct sensor_hub_data {
struct mfd_cell *hid_sensor_hub_client_devs;
int hid_sensor_client_cnt;
unsigned long quirks;
+ int ref_cnt;
};
/**
@@ -79,6 +80,7 @@ struct sensor_hub_data {
struct hid_sensor_hub_callbacks_list {
struct list_head list;
u32 usage_id;
+ struct hid_sensor_hub_device *hsdev;
struct hid_sensor_hub_callbacks *usage_callback;
void *priv;
};
@@ -97,20 +99,18 @@ static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev,
return NULL;
}
-static int sensor_hub_get_physical_device_count(
- struct hid_report_enum *report_enum)
+static int sensor_hub_get_physical_device_count(struct hid_device *hdev)
{
- struct hid_report *report;
- struct hid_field *field;
- int cnt = 0;
+ int i;
+ int count = 0;
- list_for_each_entry(report, &report_enum->report_list, list) {
- field = report->field[0];
- if (report->maxfield && field && field->physical)
- cnt++;
+ for (i = 0; i < hdev->maxcollection; ++i) {
+ struct hid_collection *collection = &hdev->collection[i];
+ if (collection->type == HID_COLLECTION_PHYSICAL)
+ ++count;
}
- return cnt;
+ return count;
}
static void sensor_hub_fill_attr_info(
@@ -128,15 +128,23 @@ static void sensor_hub_fill_attr_info(
static struct hid_sensor_hub_callbacks *sensor_hub_get_callback(
struct hid_device *hdev,
- u32 usage_id, void **priv)
+ u32 usage_id,
+ int collection_index,
+ struct hid_sensor_hub_device **hsdev,
+ void **priv)
{
struct hid_sensor_hub_callbacks_list *callback;
struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
spin_lock(&pdata->dyn_callback_lock);
list_for_each_entry(callback, &pdata->dyn_callback_list, list)
- if (callback->usage_id == usage_id) {
+ if (callback->usage_id == usage_id &&
+ (collection_index >=
+ callback->hsdev->start_collection_index) &&
+ (collection_index <
+ callback->hsdev->end_collection_index)) {
*priv = callback->priv;
+ *hsdev = callback->hsdev;
spin_unlock(&pdata->dyn_callback_lock);
return callback->usage_callback;
}
@@ -154,7 +162,8 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
spin_lock(&pdata->dyn_callback_lock);
list_for_each_entry(callback, &pdata->dyn_callback_list, list)
- if (callback->usage_id == usage_id) {
+ if (callback->usage_id == usage_id &&
+ callback->hsdev == hsdev) {
spin_unlock(&pdata->dyn_callback_lock);
return -EINVAL;
}
@@ -163,6 +172,7 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
spin_unlock(&pdata->dyn_callback_lock);
return -ENOMEM;
}
+ callback->hsdev = hsdev;
callback->usage_callback = usage_callback;
callback->usage_id = usage_id;
callback->priv = NULL;
@@ -181,7 +191,8 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
spin_lock(&pdata->dyn_callback_lock);
list_for_each_entry(callback, &pdata->dyn_callback_list, list)
- if (callback->usage_id == usage_id) {
+ if (callback->usage_id == usage_id &&
+ callback->hsdev == hsdev) {
list_del(&callback->list);
kfree(callback);
break;
@@ -260,13 +271,12 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
spin_lock_irqsave(&data->lock, flags);
data->pending.status = true;
+ spin_unlock_irqrestore(&data->lock, flags);
report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT);
- if (!report) {
- spin_unlock_irqrestore(&data->lock, flags);
+ if (!report)
goto err_free;
- }
+
hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
- spin_unlock_irqrestore(&data->lock, flags);
wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5);
switch (data->pending.raw_size) {
case 1:
@@ -291,6 +301,28 @@ err_free:
}
EXPORT_SYMBOL_GPL(sensor_hub_input_attr_get_raw_value);
+int hid_sensor_get_usage_index(struct hid_sensor_hub_device *hsdev,
+ u32 report_id, int field_index, u32 usage_id)
+{
+ struct hid_report *report;
+ struct hid_field *field;
+ int i;
+
+ report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT);
+ if (!report || (field_index >= report->maxfield))
+ goto done_proc;
+
+ field = report->field[field_index];
+ for (i = 0; i < field->maxusage; ++i) {
+ if (field->usage[i].hid == usage_id)
+ return field->usage[i].usage_index;
+ }
+
+done_proc:
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(hid_sensor_get_usage_index);
+
int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
u8 type,
u32 usage_id,
@@ -298,8 +330,7 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
struct hid_sensor_hub_attribute_info *info)
{
int ret = -1;
- int i, j;
- int collection_index = -1;
+ int i;
struct hid_report *report;
struct hid_field *field;
struct hid_report_enum *report_enum;
@@ -313,44 +344,31 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
info->units = -1;
info->unit_expo = -1;
- for (i = 0; i < hdev->maxcollection; ++i) {
- struct hid_collection *collection = &hdev->collection[i];
- if (usage_id == collection->usage) {
- collection_index = i;
- break;
- }
- }
- if (collection_index == -1)
- goto err_ret;
-
report_enum = &hdev->report_enum[type];
list_for_each_entry(report, &report_enum->report_list, list) {
for (i = 0; i < report->maxfield; ++i) {
field = report->field[i];
- if (field->physical == usage_id &&
- field->logical == attr_usage_id) {
- sensor_hub_fill_attr_info(info, i, report->id,
- field);
- ret = 0;
- } else {
- for (j = 0; j < field->maxusage; ++j) {
- if (field->usage[j].hid ==
- attr_usage_id &&
- field->usage[j].collection_index ==
- collection_index) {
- sensor_hub_fill_attr_info(info,
- i, report->id, field);
- ret = 0;
- break;
- }
+ if (field->maxusage) {
+ if (field->physical == usage_id &&
+ (field->logical == attr_usage_id ||
+ field->usage[0].hid ==
+ attr_usage_id) &&
+ (field->usage[0].collection_index >=
+ hsdev->start_collection_index) &&
+ (field->usage[0].collection_index <
+ hsdev->end_collection_index)) {
+
+ sensor_hub_fill_attr_info(info, i,
+ report->id,
+ field);
+ ret = 0;
+ break;
}
}
- if (ret == 0)
- break;
}
+
}
-err_ret:
return ret;
}
EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info);
@@ -366,7 +384,7 @@ static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message)
list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
if (callback->usage_callback->suspend)
callback->usage_callback->suspend(
- pdata->hsdev, callback->priv);
+ callback->hsdev, callback->priv);
}
spin_unlock(&pdata->dyn_callback_lock);
@@ -383,7 +401,7 @@ static int sensor_hub_resume(struct hid_device *hdev)
list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
if (callback->usage_callback->resume)
callback->usage_callback->resume(
- pdata->hsdev, callback->priv);
+ callback->hsdev, callback->priv);
}
spin_unlock(&pdata->dyn_callback_lock);
@@ -410,6 +428,7 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
struct hid_sensor_hub_callbacks *callback = NULL;
struct hid_collection *collection = NULL;
void *priv = NULL;
+ struct hid_sensor_hub_device *hsdev = NULL;
hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n",
report->id, size, report->type);
@@ -444,23 +463,26 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
report->field[i]->usage->collection_index];
hid_dbg(hdev, "collection->usage %x\n",
collection->usage);
- callback = sensor_hub_get_callback(pdata->hsdev->hdev,
- report->field[i]->physical,
- &priv);
+
+ callback = sensor_hub_get_callback(hdev,
+ report->field[i]->physical,
+ report->field[i]->usage[0].collection_index,
+ &hsdev, &priv);
+
if (callback && callback->capture_sample) {
if (report->field[i]->logical)
- callback->capture_sample(pdata->hsdev,
+ callback->capture_sample(hsdev,
report->field[i]->logical, sz, ptr,
callback->pdev);
else
- callback->capture_sample(pdata->hsdev,
+ callback->capture_sample(hsdev,
report->field[i]->usage->hid, sz, ptr,
callback->pdev);
}
ptr += sz;
}
if (callback && collection && callback->send_event)
- callback->send_event(pdata->hsdev, collection->usage,
+ callback->send_event(hsdev, collection->usage,
callback->pdev);
spin_unlock_irqrestore(&pdata->lock, flags);
@@ -473,7 +495,7 @@ int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev)
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
mutex_lock(&data->mutex);
- if (!hsdev->ref_cnt) {
+ if (!data->ref_cnt) {
ret = hid_hw_open(hsdev->hdev);
if (ret) {
hid_err(hsdev->hdev, "failed to open hid device\n");
@@ -481,7 +503,7 @@ int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev)
return ret;
}
}
- hsdev->ref_cnt++;
+ data->ref_cnt++;
mutex_unlock(&data->mutex);
return ret;
@@ -493,8 +515,8 @@ void sensor_hub_device_close(struct hid_sensor_hub_device *hsdev)
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
mutex_lock(&data->mutex);
- hsdev->ref_cnt--;
- if (!hsdev->ref_cnt)
+ data->ref_cnt--;
+ if (!data->ref_cnt)
hid_hw_close(hsdev->hdev);
mutex_unlock(&data->mutex);
}
@@ -541,26 +563,19 @@ static int sensor_hub_probe(struct hid_device *hdev,
struct sensor_hub_data *sd;
int i;
char *name;
- struct hid_report *report;
- struct hid_report_enum *report_enum;
- struct hid_field *field;
int dev_cnt;
+ struct hid_sensor_hub_device *hsdev;
+ struct hid_sensor_hub_device *last_hsdev = NULL;
sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL);
if (!sd) {
hid_err(hdev, "cannot allocate Sensor data\n");
return -ENOMEM;
}
- sd->hsdev = devm_kzalloc(&hdev->dev, sizeof(*sd->hsdev), GFP_KERNEL);
- if (!sd->hsdev) {
- hid_err(hdev, "cannot allocate hid_sensor_hub_device\n");
- return -ENOMEM;
- }
+
hid_set_drvdata(hdev, sd);
sd->quirks = id->driver_data;
- sd->hsdev->hdev = hdev;
- sd->hsdev->vendor_id = hdev->vendor;
- sd->hsdev->product_id = hdev->product;
+
spin_lock_init(&sd->lock);
spin_lock_init(&sd->dyn_callback_lock);
mutex_init(&sd->mutex);
@@ -578,9 +593,8 @@ static int sensor_hub_probe(struct hid_device *hdev,
}
INIT_LIST_HEAD(&sd->dyn_callback_list);
sd->hid_sensor_client_cnt = 0;
- report_enum = &hdev->report_enum[HID_INPUT_REPORT];
- dev_cnt = sensor_hub_get_physical_device_count(report_enum);
+ dev_cnt = sensor_hub_get_physical_device_count(hdev);
if (dev_cnt > HID_MAX_PHY_DEVICES) {
hid_err(hdev, "Invalid Physical device count\n");
ret = -EINVAL;
@@ -594,42 +608,63 @@ static int sensor_hub_probe(struct hid_device *hdev,
ret = -ENOMEM;
goto err_stop_hw;
}
- list_for_each_entry(report, &report_enum->report_list, list) {
- hid_dbg(hdev, "Report id:%x\n", report->id);
- field = report->field[0];
- if (report->maxfield && field &&
- field->physical) {
+
+ for (i = 0; i < hdev->maxcollection; ++i) {
+ struct hid_collection *collection = &hdev->collection[i];
+
+ if (collection->type == HID_COLLECTION_PHYSICAL) {
+
+ hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL);
+ if (!hsdev) {
+ hid_err(hdev, "cannot allocate hid_sensor_hub_device\n");
+ ret = -ENOMEM;
+ goto err_no_mem;
+ }
+ hsdev->hdev = hdev;
+ hsdev->vendor_id = hdev->vendor;
+ hsdev->product_id = hdev->product;
+ hsdev->start_collection_index = i;
+ if (last_hsdev)
+ last_hsdev->end_collection_index = i;
+ last_hsdev = hsdev;
name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x",
- field->physical);
+ collection->usage);
if (name == NULL) {
hid_err(hdev, "Failed MFD device name\n");
ret = -ENOMEM;
- goto err_free_names;
+ goto err_no_mem;
}
sd->hid_sensor_hub_client_devs[
- sd->hid_sensor_client_cnt].id = PLATFORM_DEVID_AUTO;
+ sd->hid_sensor_client_cnt].id =
+ PLATFORM_DEVID_AUTO;
sd->hid_sensor_hub_client_devs[
sd->hid_sensor_client_cnt].name = name;
sd->hid_sensor_hub_client_devs[
sd->hid_sensor_client_cnt].platform_data =
- sd->hsdev;
+ hsdev;
sd->hid_sensor_hub_client_devs[
sd->hid_sensor_client_cnt].pdata_size =
- sizeof(*sd->hsdev);
- hid_dbg(hdev, "Adding %s:%p\n", name, sd);
+ sizeof(*hsdev);
+ hid_dbg(hdev, "Adding %s:%d\n", name,
+ hsdev->start_collection_index);
sd->hid_sensor_client_cnt++;
}
}
+ if (last_hsdev)
+ last_hsdev->end_collection_index = i;
+
ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs,
sd->hid_sensor_client_cnt, NULL, 0, NULL);
if (ret < 0)
- goto err_free_names;
+ goto err_no_mem;
return ret;
-err_free_names:
- for (i = 0; i < sd->hid_sensor_client_cnt ; ++i)
+err_no_mem:
+ for (i = 0; i < sd->hid_sensor_client_cnt; ++i) {
kfree(sd->hid_sensor_hub_client_devs[i].name);
+ kfree(sd->hid_sensor_hub_client_devs[i].platform_data);
+ }
kfree(sd->hid_sensor_hub_client_devs);
err_stop_hw:
hid_hw_stop(hdev);
@@ -651,8 +686,10 @@ static void sensor_hub_remove(struct hid_device *hdev)
complete(&data->pending.ready);
spin_unlock_irqrestore(&data->lock, flags);
mfd_remove_devices(&hdev->dev);
- for (i = 0; i < data->hid_sensor_client_cnt ; ++i)
+ for (i = 0; i < data->hid_sensor_client_cnt; ++i) {
kfree(data->hid_sensor_hub_client_devs[i].name);
+ kfree(data->hid_sensor_hub_client_devs[i].platform_data);
+ }
kfree(data->hid_sensor_hub_client_devs);
hid_set_drvdata(hdev, NULL);
mutex_destroy(&data->mutex);
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 2f19b15f47f2..69204afea7a4 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -17,7 +17,8 @@
* any later version.
*/
-/* NOTE: in order for the Sony PS3 BD Remote Control to be found by
+/*
+ * NOTE: in order for the Sony PS3 BD Remote Control to be found by
* a Bluetooth host, the key combination Start+Enter has to be kept pressed
* for about 7 seconds with the Bluetooth Host Controller in discovering mode.
*
@@ -28,8 +29,11 @@
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/usb.h>
#include <linux/leds.h>
+#include <linux/power_supply.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/input/mt.h>
#include "hid-ids.h"
@@ -41,8 +45,13 @@
#define DUALSHOCK4_CONTROLLER_USB BIT(5)
#define DUALSHOCK4_CONTROLLER_BT BIT(6)
-#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER_USB)
-#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER_USB | DUALSHOCK4_CONTROLLER_USB)
+#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
+#define DUALSHOCK4_CONTROLLER (DUALSHOCK4_CONTROLLER_USB |\
+ DUALSHOCK4_CONTROLLER_BT)
+#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\
+ DUALSHOCK4_CONTROLLER)
+#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
+#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
#define MAX_LEDS 4
@@ -74,7 +83,8 @@ static const u8 sixaxis_rdesc_fixup2[] = {
0xb1, 0x02, 0xc0, 0xc0,
};
-/* The default descriptor doesn't provide mapping for the accelerometers
+/*
+ * The default descriptor doesn't provide mapping for the accelerometers
* or orientation sensors. This fixed descriptor maps the accelerometers
* to usage values 0x40, 0x41 and 0x42 and maps the orientation sensors
* to usage values 0x43, 0x44 and 0x45.
@@ -333,6 +343,217 @@ static u8 dualshock4_usb_rdesc[] = {
0xC0 /* End Collection */
};
+/*
+ * The default behavior of the Dualshock 4 is to send reports using report
+ * type 1 when running over Bluetooth. However, as soon as it receives a
+ * report of type 17 to set the LEDs or rumble it starts returning it's state
+ * in report 17 instead of 1. Since report 17 is undefined in the default HID
+ * descriptor the button and axis definitions must be moved to report 17 or
+ * the HID layer won't process the received input once a report is sent.
+ */
+static u8 dualshock4_bt_rdesc[] = {
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x05, /* Usage (Gamepad), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0x01, /* Report ID (1), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x0A, /* Report Count (9), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x04, 0xFF, /* Usage Page (FF04h), */
+ 0x85, 0x02, /* Report ID (2), */
+ 0x09, 0x24, /* Usage (24h), */
+ 0x95, 0x24, /* Report Count (36), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xA3, /* Report ID (163), */
+ 0x09, 0x25, /* Usage (25h), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x05, /* Report ID (5), */
+ 0x09, 0x26, /* Usage (26h), */
+ 0x95, 0x28, /* Report Count (40), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x06, /* Report ID (6), */
+ 0x09, 0x27, /* Usage (27h), */
+ 0x95, 0x34, /* Report Count (52), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x07, /* Report ID (7), */
+ 0x09, 0x28, /* Usage (28h), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x08, /* Report ID (8), */
+ 0x09, 0x29, /* Usage (29h), */
+ 0x95, 0x2F, /* Report Count (47), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x06, 0x03, 0xFF, /* Usage Page (FF03h), */
+ 0x85, 0x03, /* Report ID (3), */
+ 0x09, 0x21, /* Usage (21h), */
+ 0x95, 0x26, /* Report Count (38), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x04, /* Report ID (4), */
+ 0x09, 0x22, /* Usage (22h), */
+ 0x95, 0x2E, /* Report Count (46), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xF0, /* Report ID (240), */
+ 0x09, 0x47, /* Usage (47h), */
+ 0x95, 0x3F, /* Report Count (63), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xF1, /* Report ID (241), */
+ 0x09, 0x48, /* Usage (48h), */
+ 0x95, 0x3F, /* Report Count (63), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xF2, /* Report ID (242), */
+ 0x09, 0x49, /* Usage (49h), */
+ 0x95, 0x0F, /* Report Count (15), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x11, /* Report ID (17), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x09, 0x20, /* Usage (20h), */
+ 0x95, 0x02, /* Report Count (2), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x09, 0x31, /* Usage (Y), */
+ 0x09, 0x32, /* Usage (Z), */
+ 0x09, 0x35, /* Usage (Rz), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x04, /* Report Count (4), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x39, /* Usage (Hat Switch), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x25, 0x07, /* Logical Maximum (7), */
+ 0x75, 0x04, /* Report Size (4), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x42, /* Input (Variable, Null State), */
+ 0x05, 0x09, /* Usage Page (Button), */
+ 0x19, 0x01, /* Usage Minimum (01h), */
+ 0x29, 0x0E, /* Usage Maximum (0Eh), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x0E, /* Report Count (14), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x06, /* Report Size (6), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x33, /* Usage (Rx), */
+ 0x09, 0x34, /* Usage (Ry), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x02, /* Report Count (2), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x09, 0x20, /* Usage (20h), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x19, 0x40, /* Usage Minimum (40h), */
+ 0x29, 0x42, /* Usage Maximum (42h), */
+ 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */
+ 0x26, 0x00, 0x7F, /* Logical Maximum (32767), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x19, 0x43, /* Usage Minimum (43h), */
+ 0x29, 0x45, /* Usage Maximum (45h), */
+ 0x16, 0xFF, 0xBF, /* Logical Minimum (-16385), */
+ 0x26, 0x00, 0x40, /* Logical Maximum (16384), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x09, 0x20, /* Usage (20h), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x31, /* Report Count (51), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x21, /* Usage (21h), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x4D, /* Report Count (77), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x12, /* Report ID (18), */
+ 0x09, 0x22, /* Usage (22h), */
+ 0x95, 0x8D, /* Report Count (141), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x23, /* Usage (23h), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x13, /* Report ID (19), */
+ 0x09, 0x24, /* Usage (24h), */
+ 0x95, 0xCD, /* Report Count (205), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x25, /* Usage (25h), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x14, /* Report ID (20), */
+ 0x09, 0x26, /* Usage (26h), */
+ 0x96, 0x0D, 0x01, /* Report Count (269), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x27, /* Usage (27h), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x15, /* Report ID (21), */
+ 0x09, 0x28, /* Usage (28h), */
+ 0x96, 0x4D, 0x01, /* Report Count (333), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x29, /* Usage (29h), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x16, /* Report ID (22), */
+ 0x09, 0x2A, /* Usage (2Ah), */
+ 0x96, 0x8D, 0x01, /* Report Count (397), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x2B, /* Usage (2Bh), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x17, /* Report ID (23), */
+ 0x09, 0x2C, /* Usage (2Ch), */
+ 0x96, 0xCD, 0x01, /* Report Count (461), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x2D, /* Usage (2Dh), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x18, /* Report ID (24), */
+ 0x09, 0x2E, /* Usage (2Eh), */
+ 0x96, 0x0D, 0x02, /* Report Count (525), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x2F, /* Usage (2Fh), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x85, 0x19, /* Report ID (25), */
+ 0x09, 0x30, /* Usage (30h), */
+ 0x96, 0x22, 0x02, /* Report Count (546), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x31, /* Usage (31h), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x06, 0x80, 0xFF, /* Usage Page (FF80h), */
+ 0x85, 0x82, /* Report ID (130), */
+ 0x09, 0x22, /* Usage (22h), */
+ 0x95, 0x3F, /* Report Count (63), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x83, /* Report ID (131), */
+ 0x09, 0x23, /* Usage (23h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x84, /* Report ID (132), */
+ 0x09, 0x24, /* Usage (24h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x90, /* Report ID (144), */
+ 0x09, 0x30, /* Usage (30h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x91, /* Report ID (145), */
+ 0x09, 0x31, /* Usage (31h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x92, /* Report ID (146), */
+ 0x09, 0x32, /* Usage (32h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0x93, /* Report ID (147), */
+ 0x09, 0x33, /* Usage (33h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xA0, /* Report ID (160), */
+ 0x09, 0x40, /* Usage (40h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0x85, 0xA4, /* Report ID (164), */
+ 0x09, 0x44, /* Usage (44h), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0 /* End Collection */
+};
+
static __u8 ps3remote_rdesc[] = {
0x05, 0x01, /* GUsagePage Generic Desktop */
0x09, 0x05, /* LUsage 0x05 [Game Pad] */
@@ -450,7 +671,8 @@ static const unsigned int ps3remote_keymap_remote_buttons[] = {
};
static const unsigned int buzz_keymap[] = {
- /* The controller has 4 remote buzzers, each with one LED and 5
+ /*
+ * The controller has 4 remote buzzers, each with one LED and 5
* buttons.
*
* We use the mapping chosen by the controller, which is:
@@ -488,19 +710,35 @@ static const unsigned int buzz_keymap[] = {
[20] = BTN_TRIGGER_HAPPY20,
};
+static enum power_supply_property sony_battery_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_SCOPE,
+ POWER_SUPPLY_PROP_STATUS,
+};
+
+static spinlock_t sony_dev_list_lock;
+static LIST_HEAD(sony_device_list);
+
struct sony_sc {
+ spinlock_t lock;
+ struct list_head list_node;
struct hid_device *hdev;
struct led_classdev *leds[MAX_LEDS];
- struct hid_report *output_report;
unsigned long quirks;
struct work_struct state_worker;
+ struct power_supply battery;
#ifdef CONFIG_SONY_FF
__u8 left;
__u8 right;
#endif
+ __u8 mac_address[6];
__u8 worker_initialized;
+ __u8 cable_state;
+ __u8 battery_charging;
+ __u8 battery_capacity;
__u8 led_state[MAX_LEDS];
__u8 led_count;
};
@@ -578,6 +816,10 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
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) {
+ hid_info(hdev, "Using modified Dualshock 4 Bluetooth report descriptor\n");
+ rdesc = dualshock4_bt_rdesc;
+ *rsize = sizeof(dualshock4_bt_rdesc);
}
/* The HID descriptor exposed over BT has a trailing zero byte */
@@ -601,20 +843,127 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
return rdesc;
}
+static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
+{
+ static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
+ unsigned long flags;
+ __u8 cable_state, battery_capacity, battery_charging;
+
+ /*
+ * The sixaxis is charging if the battery value is 0xee
+ * and it is fully charged if the value is 0xef.
+ * It does not report the actual level while charging so it
+ * is set to 100% while charging is in progress.
+ */
+ if (rd[30] >= 0xee) {
+ battery_capacity = 100;
+ battery_charging = !(rd[30] & 0x01);
+ } else {
+ __u8 index = rd[30] <= 5 ? rd[30] : 5;
+ battery_capacity = sixaxis_battery_capacity[index];
+ battery_charging = 0;
+ }
+ cable_state = !(rd[31] & 0x04);
+
+ spin_lock_irqsave(&sc->lock, flags);
+ sc->cable_state = cable_state;
+ sc->battery_capacity = battery_capacity;
+ sc->battery_charging = battery_charging;
+ spin_unlock_irqrestore(&sc->lock, flags);
+}
+
+static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
+{
+ struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
+ struct hid_input, list);
+ struct input_dev *input_dev = hidinput->input;
+ unsigned long flags;
+ int n, offset;
+ __u8 cable_state, battery_capacity, battery_charging;
+
+ /*
+ * Battery and touchpad data starts at byte 30 in the USB report and
+ * 32 in Bluetooth report.
+ */
+ offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 30 : 32;
+
+ /*
+ * The lower 4 bits of byte 30 contain the battery level
+ * and the 5th bit contains the USB cable state.
+ */
+ cable_state = (rd[offset] >> 4) & 0x01;
+ battery_capacity = rd[offset] & 0x0F;
+
+ /*
+ * When a USB power source is connected the battery level ranges from
+ * 0 to 10, and when running on battery power it ranges from 0 to 9.
+ * A battery level above 10 when plugged in means charge completed.
+ */
+ if (!cable_state || battery_capacity > 10)
+ battery_charging = 0;
+ else
+ battery_charging = 1;
+
+ if (!cable_state)
+ battery_capacity++;
+ if (battery_capacity > 10)
+ battery_capacity = 10;
+
+ battery_capacity *= 10;
+
+ spin_lock_irqsave(&sc->lock, flags);
+ sc->cable_state = cable_state;
+ sc->battery_capacity = battery_capacity;
+ sc->battery_charging = battery_charging;
+ spin_unlock_irqrestore(&sc->lock, flags);
+
+ offset += 5;
+
+ /*
+ * The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB
+ * and 37 on Bluetooth.
+ * The first 7 bits of the first byte is a counter and bit 8 is a touch
+ * indicator that is 0 when pressed and 1 when not pressed.
+ * The next 3 bytes are two 12 bit touch coordinates, X and Y.
+ * The data for the second touch is in the same format and immediatly
+ * follows the data for the first.
+ */
+ for (n = 0; n < 2; n++) {
+ __u16 x, y;
+
+ x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8);
+ y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4);
+
+ input_mt_slot(input_dev, n);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
+ !(rd[offset] >> 7));
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+
+ offset += 4;
+ }
+}
+
static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
__u8 *rd, int size)
{
struct sony_sc *sc = hid_get_drvdata(hdev);
- /* Sixaxis HID report has acclerometers/gyro with MSByte first, this
+ /*
+ * Sixaxis HID report has acclerometers/gyro with MSByte first, this
* has to be BYTE_SWAPPED before passing up to joystick interface
*/
- if ((sc->quirks & (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)) &&
- rd[0] == 0x01 && size == 49) {
+ if ((sc->quirks & SIXAXIS_CONTROLLER) && rd[0] == 0x01 && size == 49) {
swap(rd[41], rd[42]);
swap(rd[43], rd[44]);
swap(rd[45], rd[46]);
swap(rd[47], rd[48]);
+
+ sixaxis_parse_report(sc, rd, size);
+ } else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
+ size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
+ && rd[0] == 0x11 && size == 78)) {
+ dualshock4_parse_report(sc, rd, size);
}
return 0;
@@ -657,45 +1006,6 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
}
/*
- * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP
- * like it should according to usbhid/hid-core.c::usbhid_output_raw_report()
- * so we need to override that forcing HID Output Reports on the Control EP.
- *
- * There is also another issue about HID Output Reports via USB, the Sixaxis
- * does not want the report_id as part of the data packet, so we have to
- * discard buf[0] when sending the actual control message, even for numbered
- * reports, humpf!
- */
-static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
- size_t count, unsigned char report_type)
-{
- struct usb_interface *intf = to_usb_interface(hid->dev.parent);
- struct usb_device *dev = interface_to_usbdev(intf);
- struct usb_host_interface *interface = intf->cur_altsetting;
- int report_id = buf[0];
- int ret;
-
- if (report_type == HID_OUTPUT_REPORT) {
- /* Don't send the Report ID */
- buf++;
- count--;
- }
-
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- HID_REQ_SET_REPORT,
- USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- ((report_type + 1) << 8) | report_id,
- interface->desc.bInterfaceNumber, buf, count,
- USB_CTRL_SET_TIMEOUT);
-
- /* Count also the Report ID, in case of an Output report. */
- if (ret > 0 && report_type == HID_OUTPUT_REPORT)
- ret++;
-
- return ret;
-}
-
-/*
* Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
* to "operational". Without this, the ps3 controller will not report any
* events.
@@ -708,7 +1018,8 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
if (!buf)
return -ENOMEM;
- ret = hdev->hid_get_raw_report(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT,
+ HID_REQ_GET_REPORT);
if (ret < 0)
hid_err(hdev, "can't set operational mode\n");
@@ -721,7 +1032,20 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
static int sixaxis_set_operational_bt(struct hid_device *hdev)
{
unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
- return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
+ return hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+}
+
+/*
+ * Requesting feature report 0x02 in Bluetooth mode changes the state of the
+ * controller so that it sends full input reports of type 0x11.
+ */
+static int dualshock4_set_operational_bt(struct hid_device *hdev)
+{
+ __u8 buf[37] = { 0 };
+
+ return hid_hw_raw_request(hdev, 0x02, buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
}
static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
@@ -751,8 +1075,7 @@ static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
buzz_set_leds(hdev, leds);
- } else if ((drv_data->quirks & SIXAXIS_CONTROLLER_USB) ||
- (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB)) {
+ } else {
for (n = 0; n < count; n++)
drv_data->led_state[n] = leds[n];
schedule_work(&drv_data->state_worker);
@@ -792,7 +1115,6 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
struct sony_sc *drv_data;
int n;
- int on = 0;
drv_data = hid_get_drvdata(hdev);
if (!drv_data) {
@@ -801,13 +1123,11 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
}
for (n = 0; n < drv_data->led_count; n++) {
- if (led == drv_data->leds[n]) {
- on = !!(drv_data->led_state[n]);
- break;
- }
+ if (led == drv_data->leds[n])
+ return drv_data->led_state[n];
}
- return on ? LED_FULL : LED_OFF;
+ return LED_OFF;
}
static void sony_leds_remove(struct hid_device *hdev)
@@ -857,7 +1177,7 @@ static int sony_leds_init(struct hid_device *hdev)
/* Validate expected report characteristics. */
if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
return -ENODEV;
- } else if (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) {
+ } else if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
drv_data->led_count = 3;
max_brightness = 255;
use_colors = 1;
@@ -871,9 +1191,11 @@ static int sony_leds_init(struct hid_device *hdev)
name_fmt = "%s::sony%d";
}
- /* Clear LEDs as we have no way of reading their initial state. This is
+ /*
+ * Clear LEDs as we have no way of reading their initial state. This is
* only relevant if the driver is loaded after somebody actively set the
- * LEDs to on */
+ * LEDs to on
+ */
sony_set_leds(hdev, initial_values, drv_data->led_count);
name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
@@ -943,29 +1265,45 @@ static void sixaxis_state_worker(struct work_struct *work)
buf[10] |= sc->led_state[2] << 3;
buf[10] |= sc->led_state[3] << 4;
- sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf),
- HID_OUTPUT_REPORT);
+ hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT,
+ HID_REQ_SET_REPORT);
}
static void dualshock4_state_worker(struct work_struct *work)
{
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
struct hid_device *hdev = sc->hdev;
- struct hid_report *report = sc->output_report;
- __s32 *value = report->field[0]->value;
+ int offset;
- value[0] = 0x03;
+ __u8 buf[78] = { 0 };
+
+ if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
+ buf[0] = 0x05;
+ buf[1] = 0x03;
+ offset = 4;
+ } else {
+ buf[0] = 0x11;
+ buf[1] = 0xB0;
+ buf[3] = 0x0F;
+ offset = 6;
+ }
#ifdef CONFIG_SONY_FF
- value[3] = sc->right;
- value[4] = sc->left;
+ buf[offset++] = sc->right;
+ buf[offset++] = sc->left;
+#else
+ offset += 2;
#endif
- value[5] = sc->led_state[0];
- value[6] = sc->led_state[1];
- value[7] = sc->led_state[2];
+ buf[offset++] = sc->led_state[0];
+ buf[offset++] = sc->led_state[1];
+ buf[offset++] = sc->led_state[2];
- hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
+ if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
+ hid_hw_output_report(hdev, buf, 32);
+ else
+ hid_hw_raw_request(hdev, 0x11, buf, 78,
+ HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
}
#ifdef CONFIG_SONY_FF
@@ -1000,35 +1338,247 @@ static int sony_init_ff(struct hid_device *hdev)
{
return 0;
}
+
#endif
-static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size)
+static int sony_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
{
- struct list_head *head, *list;
- struct hid_report *report;
+ struct sony_sc *sc = container_of(psy, struct sony_sc, battery);
+ unsigned long flags;
+ int ret = 0;
+ u8 battery_charging, battery_capacity, cable_state;
+
+ spin_lock_irqsave(&sc->lock, flags);
+ battery_charging = sc->battery_charging;
+ battery_capacity = sc->battery_capacity;
+ cable_state = sc->cable_state;
+ spin_unlock_irqrestore(&sc->lock, flags);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_SCOPE:
+ val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = battery_capacity;
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ if (battery_charging)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else
+ if (battery_capacity == 100 && cable_state)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int sony_battery_probe(struct sony_sc *sc)
+{
+ static atomic_t power_id_seq = ATOMIC_INIT(0);
+ unsigned long power_id;
struct hid_device *hdev = sc->hdev;
+ int ret;
+
+ /*
+ * Set the default battery level to 100% to avoid low battery warnings
+ * if the battery is polled before the first device report is received.
+ */
+ sc->battery_capacity = 100;
+
+ power_id = (unsigned long)atomic_inc_return(&power_id_seq);
+
+ sc->battery.properties = sony_battery_props;
+ sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
+ sc->battery.get_property = sony_battery_get_property;
+ sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+ sc->battery.use_for_apm = 0;
+ sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%lu",
+ power_id);
+ if (!sc->battery.name)
+ return -ENOMEM;
- list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
+ ret = power_supply_register(&hdev->dev, &sc->battery);
+ if (ret) {
+ hid_err(hdev, "Unable to register battery device\n");
+ goto err_free;
+ }
- list_for_each(head, list) {
- report = list_entry(head, struct hid_report, list);
+ power_supply_powers(&sc->battery, &hdev->dev);
+ return 0;
- if (report->id == req_id) {
- if (report->size < req_size) {
- hid_err(hdev, "Output report 0x%02x (%i bits) is smaller than requested size (%i bits)\n",
- req_id, report->size, req_size);
- return -EINVAL;
- }
- sc->output_report = report;
- return 0;
+err_free:
+ kfree(sc->battery.name);
+ sc->battery.name = NULL;
+ return ret;
+}
+
+static void sony_battery_remove(struct sony_sc *sc)
+{
+ if (!sc->battery.name)
+ return;
+
+ power_supply_unregister(&sc->battery);
+ kfree(sc->battery.name);
+ sc->battery.name = NULL;
+}
+
+static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
+ int w, int h)
+{
+ struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
+ struct hid_input, list);
+ struct input_dev *input_dev = hidinput->input;
+ int ret;
+
+ ret = input_mt_init_slots(input_dev, touch_count, 0);
+ if (ret < 0) {
+ hid_err(sc->hdev, "Unable to initialize multi-touch slots\n");
+ return ret;
+ }
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0);
+
+ return 0;
+}
+
+/*
+ * If a controller is plugged in via USB while already connected via Bluetooth
+ * it will show up as two devices. A global list of connected controllers and
+ * their MAC addresses is maintained to ensure that a device is only connected
+ * once.
+ */
+static int sony_check_add_dev_list(struct sony_sc *sc)
+{
+ struct sony_sc *entry;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&sony_dev_list_lock, flags);
+
+ list_for_each_entry(entry, &sony_device_list, list_node) {
+ ret = memcmp(sc->mac_address, entry->mac_address,
+ sizeof(sc->mac_address));
+ if (!ret) {
+ ret = -EEXIST;
+ hid_info(sc->hdev, "controller with MAC address %pMR already connected\n",
+ sc->mac_address);
+ goto unlock;
}
}
- hid_err(hdev, "Unable to locate output report 0x%02x\n", req_id);
+ ret = 0;
+ list_add(&(sc->list_node), &sony_device_list);
- return -EINVAL;
+unlock:
+ spin_unlock_irqrestore(&sony_dev_list_lock, flags);
+ return ret;
+}
+
+static void sony_remove_dev_list(struct sony_sc *sc)
+{
+ unsigned long flags;
+
+ if (sc->list_node.next) {
+ spin_lock_irqsave(&sony_dev_list_lock, flags);
+ list_del(&(sc->list_node));
+ spin_unlock_irqrestore(&sony_dev_list_lock, flags);
+ }
}
+static int sony_get_bt_devaddr(struct sony_sc *sc)
+{
+ int ret;
+
+ /* HIDP stores the device MAC address as a string in the uniq field. */
+ ret = strlen(sc->hdev->uniq);
+ if (ret != 17)
+ return -EINVAL;
+
+ ret = sscanf(sc->hdev->uniq,
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ &sc->mac_address[5], &sc->mac_address[4], &sc->mac_address[3],
+ &sc->mac_address[2], &sc->mac_address[1], &sc->mac_address[0]);
+
+ if (ret != 6)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sony_check_add(struct sony_sc *sc)
+{
+ int n, ret;
+
+ if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
+ (sc->quirks & SIXAXIS_CONTROLLER_BT)) {
+ /*
+ * sony_get_bt_devaddr() attempts to parse the Bluetooth MAC
+ * address from the uniq string where HIDP stores it.
+ * As uniq cannot be guaranteed to be a MAC address in all cases
+ * a failure of this function should not prevent the connection.
+ */
+ if (sony_get_bt_devaddr(sc) < 0) {
+ hid_warn(sc->hdev, "UNIQ does not contain a MAC address; duplicate check skipped\n");
+ return 0;
+ }
+ } else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
+ __u8 buf[7];
+
+ /*
+ * The MAC address of a DS4 controller connected via USB can be
+ * retrieved with feature report 0x81. The address begins at
+ * offset 1.
+ */
+ ret = hid_hw_raw_request(sc->hdev, 0x81, buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+
+ if (ret != 7) {
+ hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n");
+ return ret < 0 ? ret : -EINVAL;
+ }
+
+ memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
+ } else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
+ __u8 buf[18];
+
+ /*
+ * The MAC address of a Sixaxis controller connected via USB can
+ * be retrieved with feature report 0xf2. The address begins at
+ * offset 4.
+ */
+ ret = hid_hw_raw_request(sc->hdev, 0xf2, buf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+
+ if (ret != 18) {
+ hid_err(sc->hdev, "failed to retrieve feature report 0xf2 with the Sixaxis MAC address\n");
+ return ret < 0 ? ret : -EINVAL;
+ }
+
+ /*
+ * The Sixaxis device MAC in the report is big-endian and must
+ * be byte-swapped.
+ */
+ for (n = 0; n < 6; n++)
+ sc->mac_address[5-n] = buf[4+n];
+ } else {
+ return 0;
+ }
+
+ return sony_check_add_dev_list(sc);
+}
+
+
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
@@ -1066,17 +1616,48 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
- hdev->hid_output_raw_report = sixaxis_usb_output_raw_report;
+ /*
+ * The Sony Sixaxis does not handle HID Output Reports on the
+ * Interrupt EP like it could, so we need to force HID Output
+ * Reports to use HID_REQ_SET_REPORT on the Control EP.
+ *
+ * There is also another issue about HID Output Reports via USB,
+ * the Sixaxis does not want the report_id as part of the data
+ * packet, so we have to discard buf[0] when sending the actual
+ * control message, even for numbered reports, humpf!
+ */
+ hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
+ hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
ret = sixaxis_set_operational_usb(hdev);
-
sc->worker_initialized = 1;
INIT_WORK(&sc->state_worker, sixaxis_state_worker);
- }
- else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
+ } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
+ /*
+ * The Sixaxis wants output reports sent on the ctrl endpoint
+ * when connected via Bluetooth.
+ */
+ hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
ret = sixaxis_set_operational_bt(hdev);
- else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
- /* Report 5 (31 bytes) is used to send data to the controller via USB */
- ret = sony_set_output_report(sc, 0x05, 248);
+ sc->worker_initialized = 1;
+ INIT_WORK(&sc->state_worker, sixaxis_state_worker);
+ } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
+ if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
+ /*
+ * The DualShock 4 wants output reports sent on the ctrl
+ * endpoint when connected via Bluetooth.
+ */
+ hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
+ ret = dualshock4_set_operational_bt(hdev);
+ if (ret < 0) {
+ hid_err(hdev, "failed to set the Dualshock 4 operational mode\n");
+ goto err_stop;
+ }
+ }
+ /*
+ * The Dualshock 4 touchpad supports 2 touches and has a
+ * resolution of 1920x940.
+ */
+ ret = sony_register_touchpad(sc, 2, 1920, 940);
if (ret < 0)
goto err_stop;
@@ -1089,22 +1670,46 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret < 0)
goto err_stop;
+ ret = sony_check_add(sc);
+ if (ret < 0)
+ goto err_stop;
+
if (sc->quirks & SONY_LED_SUPPORT) {
ret = sony_leds_init(hdev);
if (ret < 0)
goto err_stop;
}
+ if (sc->quirks & SONY_BATTERY_SUPPORT) {
+ ret = sony_battery_probe(sc);
+ if (ret < 0)
+ goto err_stop;
+
+ /* Open the device to receive reports with battery info */
+ ret = hid_hw_open(hdev);
+ if (ret < 0) {
+ hid_err(hdev, "hw open failed\n");
+ goto err_stop;
+ }
+ }
+
if (sc->quirks & SONY_FF_SUPPORT) {
ret = sony_init_ff(hdev);
if (ret < 0)
- goto err_stop;
+ goto err_close;
}
return 0;
+err_close:
+ hid_hw_close(hdev);
err_stop:
if (sc->quirks & SONY_LED_SUPPORT)
sony_leds_remove(hdev);
+ if (sc->quirks & SONY_BATTERY_SUPPORT)
+ sony_battery_remove(sc);
+ if (sc->worker_initialized)
+ cancel_work_sync(&sc->state_worker);
+ sony_remove_dev_list(sc);
hid_hw_stop(hdev);
return ret;
}
@@ -1118,6 +1723,15 @@ static void sony_remove(struct hid_device *hdev)
if (sc->worker_initialized)
cancel_work_sync(&sc->state_worker);
+ if (sc->quirks & SONY_BATTERY_SUPPORT) {
+ hid_hw_close(hdev);
+ sony_battery_remove(sc);
+ }
+
+ if (sc->worker_initialized)
+ cancel_work_sync(&sc->state_worker);
+
+ sony_remove_dev_list(sc);
hid_hw_stop(hdev);
}
diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c
index 99342cfa0ea2..a97c78845f7b 100644
--- a/drivers/hid/hid-thingm.c
+++ b/drivers/hid/hid-thingm.c
@@ -48,8 +48,8 @@ static int blink1_send_command(struct blink1_data *data,
buf[0], buf[1], buf[2], buf[3], buf[4],
buf[5], buf[6], buf[7], buf[8]);
- ret = data->hdev->hid_output_raw_report(data->hdev, buf,
- BLINK1_CMD_SIZE, HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(data->hdev, buf[0], buf, BLINK1_CMD_SIZE,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
return ret < 0 ? ret : 0;
}
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
index 60c75dcbbdb8..902013ec041b 100644
--- a/drivers/hid/hid-wacom.c
+++ b/drivers/hid/hid-wacom.c
@@ -128,8 +128,8 @@ static void wacom_set_image(struct hid_device *hdev, const char *image,
rep_data[0] = WAC_CMD_ICON_START_STOP;
rep_data[1] = 0;
- ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
- HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret < 0)
goto err;
@@ -143,15 +143,15 @@ static void wacom_set_image(struct hid_device *hdev, const char *image,
rep_data[j + 3] = p[(i << 6) + j];
rep_data[2] = i;
- ret = hdev->hid_output_raw_report(hdev, rep_data, 67,
- HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 67,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
}
rep_data[0] = WAC_CMD_ICON_START_STOP;
rep_data[1] = 0;
- ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
- HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
err:
return;
@@ -183,7 +183,8 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev,
buf[3] = value;
/* use fixed brightness for OLEDs */
buf[4] = 0x08;
- hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT);
+ hid_hw_raw_request(hdev, buf[0], buf, 9, HID_FEATURE_REPORT,
+ HID_REQ_SET_REPORT);
kfree(buf);
}
@@ -339,8 +340,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
rep_data[0] = 0x03 ; rep_data[1] = 0x00;
limit = 3;
do {
- ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
- HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
} while (ret < 0 && limit-- > 0);
if (ret >= 0) {
@@ -352,8 +353,9 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
rep_data[1] = 0x00;
limit = 3;
do {
- ret = hdev->hid_output_raw_report(hdev,
- rep_data, 2, HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, rep_data[0],
+ rep_data, 2, HID_FEATURE_REPORT,
+ HID_REQ_SET_REPORT);
} while (ret < 0 && limit-- > 0);
if (ret >= 0) {
@@ -378,8 +380,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
rep_data[0] = 0x03;
rep_data[1] = wdata->features;
- ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
- HID_FEATURE_REPORT);
+ ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret >= 0)
wdata->high_speed = speed;
break;
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index abb20db2b443..d00391418d1a 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -28,14 +28,14 @@ static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
__u8 *buf;
int ret;
- if (!hdev->hid_output_raw_report)
+ if (!hdev->ll_driver->output_report)
return -ENODEV;
buf = kmemdup(buffer, count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT);
+ ret = hid_hw_output_report(hdev, buf, count);
kfree(buf);
return ret;
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index ab24ce2eb28f..9c2d7c23f296 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -123,10 +123,6 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
dev = hidraw_table[minor]->hid;
- if (!dev->hid_output_raw_report) {
- ret = -ENODEV;
- goto out;
- }
if (count > HID_MAX_BUFFER_SIZE) {
hid_warn(dev, "pid %d passed too large report\n",
@@ -153,7 +149,21 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
goto out_free;
}
- ret = dev->hid_output_raw_report(dev, buf, count, report_type);
+ if ((report_type == HID_OUTPUT_REPORT) &&
+ !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) {
+ ret = hid_hw_output_report(dev, buf, count);
+ /*
+ * compatibility with old implementation of USB-HID and I2C-HID:
+ * if the device does not support receiving output reports,
+ * on an interrupt endpoint, fallback to SET_REPORT HID command.
+ */
+ if (ret != -ENOSYS)
+ goto out_free;
+ }
+
+ ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type,
+ HID_REQ_SET_REPORT);
+
out_free:
kfree(buf);
out:
@@ -189,7 +199,7 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t
dev = hidraw_table[minor]->hid;
- if (!dev->hid_get_raw_report) {
+ if (!dev->ll_driver->raw_request) {
ret = -ENODEV;
goto out;
}
@@ -216,14 +226,15 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t
/*
* Read the first byte from the user. This is the report number,
- * which is passed to dev->hid_get_raw_report().
+ * which is passed to hid_hw_raw_request().
*/
if (copy_from_user(&report_number, buffer, 1)) {
ret = -EFAULT;
goto out_free;
}
- ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type);
+ ret = hid_hw_raw_request(dev, report_number, buf, count, report_type,
+ HID_REQ_GET_REPORT);
if (ret < 0)
goto out_free;
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index 42eebd14de1f..b50860db92f1 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -25,6 +25,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/err.h>
@@ -256,18 +257,27 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,
return 0;
}
-static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
- u8 reportID, unsigned char *buf, size_t data_len)
+/**
+ * i2c_hid_set_or_send_report: forward an incoming report to the device
+ * @client: the i2c_client of the device
+ * @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
+ * @reportID: the report ID
+ * @buf: the actual data to transfer, without the report ID
+ * @len: size of buf
+ * @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report
+ */
+static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType,
+ u8 reportID, unsigned char *buf, size_t data_len, bool use_data)
{
struct i2c_hid *ihid = i2c_get_clientdata(client);
u8 *args = ihid->argsbuf;
- const struct i2c_hid_cmd * hidcmd = &hid_set_report_cmd;
+ const struct i2c_hid_cmd *hidcmd;
int ret;
u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
- /* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */
+ /* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */
u16 size = 2 /* size */ +
(reportID ? 1 : 0) /* reportID */ +
data_len /* buf */;
@@ -278,6 +288,9 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
i2c_hid_dbg(ihid, "%s\n", __func__);
+ if (!use_data && maxOutputLength == 0)
+ return -ENOSYS;
+
if (reportID >= 0x0F) {
args[index++] = reportID;
reportID = 0x0F;
@@ -287,9 +300,10 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
* use the data register for feature reports or if the device does not
* support the output register
*/
- if (reportType == 0x03 || maxOutputLength == 0) {
+ if (use_data) {
args[index++] = dataRegister & 0xFF;
args[index++] = dataRegister >> 8;
+ hidcmd = &hid_set_report_cmd;
} else {
args[index++] = outputRegister & 0xFF;
args[index++] = outputRegister >> 8;
@@ -454,10 +468,18 @@ static void i2c_hid_init_reports(struct hid_device *hid)
return;
}
+ /*
+ * The device must be powered on while we fetch initial reports
+ * from it.
+ */
+ pm_runtime_get_sync(&client->dev);
+
list_for_each_entry(report,
&hid->report_enum[HID_FEATURE_REPORT].report_list, list)
i2c_hid_init_report(report, inbuf, ihid->bufsize);
+ pm_runtime_put(&client->dev);
+
kfree(inbuf);
}
@@ -550,7 +572,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
}
static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
- size_t count, unsigned char report_type)
+ size_t count, unsigned char report_type, bool use_data)
{
struct i2c_client *client = hid->driver_data;
int report_id = buf[0];
@@ -564,9 +586,9 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
count--;
}
- ret = i2c_hid_set_report(client,
+ ret = i2c_hid_set_or_send_report(client,
report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
- report_id, buf, count);
+ report_id, buf, count, use_data);
if (report_id && ret >= 0)
ret++; /* add report_id to the number of transfered bytes */
@@ -574,34 +596,27 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
return ret;
}
-static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep,
- int reqtype)
+static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf,
+ size_t count)
{
- struct i2c_client *client = hid->driver_data;
- char *buf;
- int ret;
- int len = i2c_hid_get_report_length(rep) - 2;
-
- buf = hid_alloc_report_buf(rep, GFP_KERNEL);
- if (!buf)
- return;
+ return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT,
+ false);
+}
+static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype,
+ int reqtype)
+{
switch (reqtype) {
case HID_REQ_GET_REPORT:
- ret = i2c_hid_get_raw_report(hid, rep->id, buf, len, rep->type);
- if (ret < 0)
- dev_err(&client->dev, "%s: unable to get report: %d\n",
- __func__, ret);
- else
- hid_input_report(hid, rep->type, buf, ret, 0);
- break;
+ return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype);
case HID_REQ_SET_REPORT:
- hid_output_report(rep, buf);
- i2c_hid_output_raw_report(hid, buf, len, rep->type);
- break;
+ if (buf[0] != reportnum)
+ return -EINVAL;
+ return i2c_hid_output_raw_report(hid, buf, len, rtype, true);
+ default:
+ return -EIO;
}
-
- kfree(buf);
}
static int i2c_hid_parse(struct hid_device *hid)
@@ -703,8 +718,8 @@ static int i2c_hid_open(struct hid_device *hid)
mutex_lock(&i2c_hid_open_mut);
if (!hid->open++) {
- ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
- if (ret) {
+ ret = pm_runtime_get_sync(&client->dev);
+ if (ret < 0) {
hid->open--;
goto done;
}
@@ -712,7 +727,7 @@ static int i2c_hid_open(struct hid_device *hid)
}
done:
mutex_unlock(&i2c_hid_open_mut);
- return ret;
+ return ret < 0 ? ret : 0;
}
static void i2c_hid_close(struct hid_device *hid)
@@ -729,7 +744,7 @@ static void i2c_hid_close(struct hid_device *hid)
clear_bit(I2C_HID_STARTED, &ihid->flags);
/* Save some power */
- i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+ pm_runtime_put(&client->dev);
}
mutex_unlock(&i2c_hid_open_mut);
}
@@ -738,19 +753,18 @@ static int i2c_hid_power(struct hid_device *hid, int lvl)
{
struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client);
- int ret = 0;
i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);
switch (lvl) {
case PM_HINT_FULLON:
- ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
+ pm_runtime_get_sync(&client->dev);
break;
case PM_HINT_NORMAL:
- ret = i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+ pm_runtime_put(&client->dev);
break;
}
- return ret;
+ return 0;
}
static struct hid_ll_driver i2c_hid_ll_driver = {
@@ -760,7 +774,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = {
.open = i2c_hid_open,
.close = i2c_hid_close,
.power = i2c_hid_power,
- .request = i2c_hid_request,
+ .output_report = i2c_hid_output_report,
+ .raw_request = i2c_hid_raw_request,
};
static int i2c_hid_init_irq(struct i2c_client *client)
@@ -973,13 +988,17 @@ static int i2c_hid_probe(struct i2c_client *client,
if (ret < 0)
goto err;
+ pm_runtime_get_noresume(&client->dev);
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+
ret = i2c_hid_fetch_hid_descriptor(ihid);
if (ret < 0)
- goto err;
+ goto err_pm;
ret = i2c_hid_init_irq(client);
if (ret < 0)
- goto err;
+ goto err_pm;
hid = hid_allocate_device();
if (IS_ERR(hid)) {
@@ -991,8 +1010,6 @@ static int i2c_hid_probe(struct i2c_client *client,
hid->driver_data = client;
hid->ll_driver = &i2c_hid_ll_driver;
- hid->hid_get_raw_report = i2c_hid_get_raw_report;
- hid->hid_output_raw_report = i2c_hid_output_raw_report;
hid->dev.parent = &client->dev;
ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
hid->bus = BUS_I2C;
@@ -1010,6 +1027,7 @@ static int i2c_hid_probe(struct i2c_client *client,
goto err_mem_free;
}
+ pm_runtime_put(&client->dev);
return 0;
err_mem_free:
@@ -1018,6 +1036,10 @@ err_mem_free:
err_irq:
free_irq(client->irq, ihid);
+err_pm:
+ pm_runtime_put_noidle(&client->dev);
+ pm_runtime_disable(&client->dev);
+
err:
i2c_hid_free_buffers(ihid);
kfree(ihid);
@@ -1029,6 +1051,11 @@ static int i2c_hid_remove(struct i2c_client *client)
struct i2c_hid *ihid = i2c_get_clientdata(client);
struct hid_device *hid;
+ pm_runtime_get_sync(&client->dev);
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+
hid = ihid->hid;
hid_destroy_device(hid);
@@ -1074,7 +1101,31 @@ static int i2c_hid_resume(struct device *dev)
}
#endif
-static SIMPLE_DEV_PM_OPS(i2c_hid_pm, i2c_hid_suspend, i2c_hid_resume);
+#ifdef CONFIG_PM_RUNTIME
+static int i2c_hid_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+ disable_irq(client->irq);
+ return 0;
+}
+
+static int i2c_hid_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ enable_irq(client->irq);
+ i2c_hid_set_power(client, I2C_HID_PWR_ON);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops i2c_hid_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
+ SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume,
+ NULL)
+};
static const struct i2c_device_id i2c_hid_id_table[] = {
{ "hid", 0 },
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index cedc6da93c19..0d078c32db4f 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -244,12 +244,35 @@ static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
return count;
}
+static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
+ size_t count)
+{
+ return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT);
+}
+
+static int uhid_raw_request(struct hid_device *hid, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype,
+ int reqtype)
+{
+ switch (reqtype) {
+ case HID_REQ_GET_REPORT:
+ return uhid_hid_get_raw(hid, reportnum, buf, len, rtype);
+ case HID_REQ_SET_REPORT:
+ /* TODO: implement proper SET_REPORT functionality */
+ return -ENOSYS;
+ default:
+ return -EIO;
+ }
+}
+
static struct hid_ll_driver uhid_hid_driver = {
.start = uhid_hid_start,
.stop = uhid_hid_stop,
.open = uhid_hid_open,
.close = uhid_hid_close,
.parse = uhid_hid_parse,
+ .output_report = uhid_hid_output_report,
+ .raw_request = uhid_raw_request,
};
#ifdef CONFIG_COMPAT
@@ -377,8 +400,6 @@ static int uhid_dev_create(struct uhid_device *uhid,
hid->uniq[63] = 0;
hid->ll_driver = &uhid_hid_driver;
- hid->hid_get_raw_report = uhid_hid_get_raw;
- hid->hid_output_raw_report = uhid_hid_output_raw;
hid->bus = ev->u.create.bus;
hid->vendor = ev->u.create.vendor;
hid->product = ev->u.create.product;
@@ -407,6 +428,67 @@ err_free:
return ret;
}
+static int uhid_dev_create2(struct uhid_device *uhid,
+ const struct uhid_event *ev)
+{
+ struct hid_device *hid;
+ int ret;
+
+ if (uhid->running)
+ return -EALREADY;
+
+ uhid->rd_size = ev->u.create2.rd_size;
+ if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
+ return -EINVAL;
+
+ uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL);
+ if (!uhid->rd_data)
+ return -ENOMEM;
+
+ memcpy(uhid->rd_data, ev->u.create2.rd_data, uhid->rd_size);
+
+ hid = hid_allocate_device();
+ if (IS_ERR(hid)) {
+ ret = PTR_ERR(hid);
+ goto err_free;
+ }
+
+ strncpy(hid->name, ev->u.create2.name, 127);
+ hid->name[127] = 0;
+ strncpy(hid->phys, ev->u.create2.phys, 63);
+ hid->phys[63] = 0;
+ strncpy(hid->uniq, ev->u.create2.uniq, 63);
+ hid->uniq[63] = 0;
+
+ hid->ll_driver = &uhid_hid_driver;
+ hid->bus = ev->u.create2.bus;
+ hid->vendor = ev->u.create2.vendor;
+ hid->product = ev->u.create2.product;
+ hid->version = ev->u.create2.version;
+ hid->country = ev->u.create2.country;
+ hid->driver_data = uhid;
+ hid->dev.parent = uhid_misc.this_device;
+
+ uhid->hid = hid;
+ uhid->running = true;
+
+ ret = hid_add_device(hid);
+ if (ret) {
+ hid_err(hid, "Cannot register HID device\n");
+ goto err_hid;
+ }
+
+ return 0;
+
+err_hid:
+ hid_destroy_device(hid);
+ uhid->hid = NULL;
+ uhid->running = false;
+err_free:
+ kfree(uhid->rd_data);
+ return ret;
+}
+
static int uhid_dev_destroy(struct uhid_device *uhid)
{
if (!uhid->running)
@@ -435,6 +517,17 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)
return 0;
}
+static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)
+{
+ if (!uhid->running)
+ return -EINVAL;
+
+ hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data,
+ min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0);
+
+ return 0;
+}
+
static int uhid_dev_feature_answer(struct uhid_device *uhid,
struct uhid_event *ev)
{
@@ -571,12 +664,18 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
case UHID_CREATE:
ret = uhid_dev_create(uhid, &uhid->input_buf);
break;
+ case UHID_CREATE2:
+ ret = uhid_dev_create2(uhid, &uhid->input_buf);
+ break;
case UHID_DESTROY:
ret = uhid_dev_destroy(uhid);
break;
case UHID_INPUT:
ret = uhid_dev_input(uhid, &uhid->input_buf);
break;
+ case UHID_INPUT2:
+ ret = uhid_dev_input2(uhid, &uhid->input_buf);
+ break;
case UHID_FEATURE_ANSWER:
ret = uhid_dev_feature_answer(uhid, &uhid->input_buf);
break;
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 44df131d390a..7b88f4cb9902 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -884,52 +884,66 @@ static int usbhid_get_raw_report(struct hid_device *hid,
return ret;
}
-static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count,
- unsigned char report_type)
+static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum,
+ __u8 *buf, size_t count, unsigned char rtype)
{
struct usbhid_device *usbhid = hid->driver_data;
struct usb_device *dev = hid_to_usb_dev(hid);
struct usb_interface *intf = usbhid->intf;
struct usb_host_interface *interface = intf->cur_altsetting;
- int ret;
+ int ret, skipped_report_id = 0;
- if (usbhid->urbout && report_type != HID_FEATURE_REPORT) {
- int actual_length;
- int skipped_report_id = 0;
+ /* Byte 0 is the report number. Report data starts at byte 1.*/
+ if ((rtype == HID_OUTPUT_REPORT) &&
+ (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORT_ID))
+ buf[0] = 0;
+ else
+ buf[0] = reportnum;
+
+ if (buf[0] == 0x0) {
+ /* Don't send the Report ID */
+ buf++;
+ count--;
+ skipped_report_id = 1;
+ }
- if (buf[0] == 0x0) {
- /* Don't send the Report ID */
- buf++;
- count--;
- skipped_report_id = 1;
- }
- ret = usb_interrupt_msg(dev, usbhid->urbout->pipe,
- buf, count, &actual_length,
- USB_CTRL_SET_TIMEOUT);
- /* return the number of bytes transferred */
- if (ret == 0) {
- ret = actual_length;
- /* count also the report id */
- if (skipped_report_id)
- ret++;
- }
- } else {
- int skipped_report_id = 0;
- int report_id = buf[0];
- if (buf[0] == 0x0) {
- /* Don't send the Report ID */
- buf++;
- count--;
- skipped_report_id = 1;
- }
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
HID_REQ_SET_REPORT,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- ((report_type + 1) << 8) | report_id,
+ ((rtype + 1) << 8) | reportnum,
interface->desc.bInterfaceNumber, buf, count,
USB_CTRL_SET_TIMEOUT);
- /* count also the report id, if this was a numbered report. */
- if (ret > 0 && skipped_report_id)
+ /* count also the report id, if this was a numbered report. */
+ if (ret > 0 && skipped_report_id)
+ ret++;
+
+ return ret;
+}
+
+static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
+{
+ struct usbhid_device *usbhid = hid->driver_data;
+ struct usb_device *dev = hid_to_usb_dev(hid);
+ int actual_length, skipped_report_id = 0, ret;
+
+ if (!usbhid->urbout)
+ return -ENOSYS;
+
+ if (buf[0] == 0x0) {
+ /* Don't send the Report ID */
+ buf++;
+ count--;
+ skipped_report_id = 1;
+ }
+
+ ret = usb_interrupt_msg(dev, usbhid->urbout->pipe,
+ buf, count, &actual_length,
+ USB_CTRL_SET_TIMEOUT);
+ /* return the number of bytes transferred */
+ if (ret == 0) {
+ ret = actual_length;
+ /* count also the report id */
+ if (skipped_report_id)
ret++;
}
@@ -1200,6 +1214,20 @@ static void usbhid_request(struct hid_device *hid, struct hid_report *rep, int r
}
}
+static int usbhid_raw_request(struct hid_device *hid, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype,
+ int reqtype)
+{
+ switch (reqtype) {
+ case HID_REQ_GET_REPORT:
+ return usbhid_get_raw_report(hid, reportnum, buf, len, rtype);
+ case HID_REQ_SET_REPORT:
+ return usbhid_set_raw_report(hid, reportnum, buf, len, rtype);
+ default:
+ return -EIO;
+ }
+}
+
static int usbhid_idle(struct hid_device *hid, int report, int idle,
int reqtype)
{
@@ -1223,6 +1251,8 @@ static struct hid_ll_driver usb_hid_driver = {
.power = usbhid_power,
.request = usbhid_request,
.wait = usbhid_wait_io,
+ .raw_request = usbhid_raw_request,
+ .output_report = usbhid_output_report,
.idle = usbhid_idle,
};
@@ -1253,8 +1283,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
usb_set_intfdata(intf, hid);
hid->ll_driver = &usb_hid_driver;
- hid->hid_get_raw_report = usbhid_get_raw_report;
- hid->hid_output_raw_report = usbhid_output_raw_report;
hid->ff_init = hid_pidff_init;
#ifdef CONFIG_USB_HIDDEV
hid->hiddev_connect = hiddev_connect;
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index b53d87958710..b13172cfbeef 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -280,8 +280,8 @@ config SENSORS_K10TEMP
If you say yes here you get support for the temperature
sensor(s) inside your CPU. Supported are later revisions of
the AMD Family 10h and all revisions of the AMD Family 11h,
- 12h (Llano), 14h (Brazos), 15h (Bulldozer/Trinity) and
- 16h (Kabini) microarchitectures.
+ 12h (Llano), 14h (Brazos), 15h (Bulldozer/Trinity/Kaveri) and
+ 16h (Kabini/Mullins) microarchitectures.
This driver can also be built as a module. If so, the module
will be called k10temp.
diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c
index 29dd9f746dfa..3eb4281689b5 100644
--- a/drivers/hwmon/adm1021.c
+++ b/drivers/hwmon/adm1021.c
@@ -79,9 +79,11 @@ enum chips {
/* Each client has this additional data */
struct adm1021_data {
- struct device *hwmon_dev;
+ struct i2c_client *client;
enum chips type;
+ const struct attribute_group *groups[3];
+
struct mutex update_lock;
char valid; /* !=0 if following fields are valid */
char low_power; /* !=0 if device in low power mode */
@@ -101,7 +103,6 @@ static int adm1021_probe(struct i2c_client *client,
static int adm1021_detect(struct i2c_client *client,
struct i2c_board_info *info);
static void adm1021_init_client(struct i2c_client *client);
-static int adm1021_remove(struct i2c_client *client);
static struct adm1021_data *adm1021_update_device(struct device *dev);
/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
@@ -128,7 +129,6 @@ static struct i2c_driver adm1021_driver = {
.name = "adm1021",
},
.probe = adm1021_probe,
- .remove = adm1021_remove,
.id_table = adm1021_id,
.detect = adm1021_detect,
.address_list = normal_i2c,
@@ -182,8 +182,8 @@ static ssize_t set_temp_max(struct device *dev,
const char *buf, size_t count)
{
int index = to_sensor_dev_attr(devattr)->index;
- struct i2c_client *client = to_i2c_client(dev);
- struct adm1021_data *data = i2c_get_clientdata(client);
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
long temp;
int err;
@@ -207,8 +207,8 @@ static ssize_t set_temp_min(struct device *dev,
const char *buf, size_t count)
{
int index = to_sensor_dev_attr(devattr)->index;
- struct i2c_client *client = to_i2c_client(dev);
- struct adm1021_data *data = i2c_get_clientdata(client);
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
long temp;
int err;
@@ -238,8 +238,8 @@ static ssize_t set_low_power(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct adm1021_data *data = i2c_get_clientdata(client);
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
char low_power;
unsigned long val;
int err;
@@ -412,15 +412,15 @@ static int adm1021_detect(struct i2c_client *client,
static int adm1021_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct device *dev = &client->dev;
struct adm1021_data *data;
- int err;
+ struct device *hwmon_dev;
- data = devm_kzalloc(&client->dev, sizeof(struct adm1021_data),
- GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct adm1021_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- i2c_set_clientdata(client, data);
+ data->client = client;
data->type = id->driver_data;
mutex_init(&data->update_lock);
@@ -428,29 +428,14 @@ static int adm1021_probe(struct i2c_client *client,
if (data->type != lm84 && !read_only)
adm1021_init_client(client);
- /* Register sysfs hooks */
- err = sysfs_create_group(&client->dev.kobj, &adm1021_group);
- if (err)
- return err;
-
- if (data->type != lm84) {
- err = sysfs_create_group(&client->dev.kobj, &adm1021_min_group);
- if (err)
- goto error;
- }
+ data->groups[0] = &adm1021_group;
+ if (data->type != lm84)
+ data->groups[1] = &adm1021_min_group;
- data->hwmon_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- goto error;
- }
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
- return 0;
-
-error:
- sysfs_remove_group(&client->dev.kobj, &adm1021_min_group);
- sysfs_remove_group(&client->dev.kobj, &adm1021_group);
- return err;
+ return PTR_ERR_OR_ZERO(hwmon_dev);
}
static void adm1021_init_client(struct i2c_client *client)
@@ -462,21 +447,10 @@ static void adm1021_init_client(struct i2c_client *client)
i2c_smbus_write_byte_data(client, ADM1021_REG_CONV_RATE_W, 0x04);
}
-static int adm1021_remove(struct i2c_client *client)
-{
- struct adm1021_data *data = i2c_get_clientdata(client);
-
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &adm1021_min_group);
- sysfs_remove_group(&client->dev.kobj, &adm1021_group);
-
- return 0;
-}
-
static struct adm1021_data *adm1021_update_device(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct adm1021_data *data = i2c_get_clientdata(client);
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
mutex_lock(&data->update_lock);
@@ -484,7 +458,7 @@ static struct adm1021_data *adm1021_update_device(struct device *dev)
|| !data->valid) {
int i;
- dev_dbg(&client->dev, "Starting adm1021 update\n");
+ dev_dbg(dev, "Starting adm1021 update\n");
for (i = 0; i < 2; i++) {
data->temp[i] = 1000 *
diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c
index 8d9f2a0e8efe..71463689d163 100644
--- a/drivers/hwmon/asc7621.c
+++ b/drivers/hwmon/asc7621.c
@@ -1115,7 +1115,6 @@ asc7621_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENOMEM;
i2c_set_clientdata(client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/* Initialize the asc7621 chip */
diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c
index ddff02e3e66f..6edce42c61d5 100644
--- a/drivers/hwmon/atxp1.c
+++ b/drivers/hwmon/atxp1.c
@@ -353,8 +353,6 @@ static int atxp1_probe(struct i2c_client *new_client,
data->vrm = vid_which_vrm();
i2c_set_clientdata(new_client, data);
- data->valid = 0;
-
mutex_init(&data->update_lock);
/* Register sysfs hooks */
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index f31bc4c48644..6d02e3b06375 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -810,20 +810,20 @@ static int __init coretemp_init(void)
if (err)
goto exit;
- get_online_cpus();
+ cpu_notifier_register_begin();
for_each_online_cpu(i)
get_core_online(i);
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
- put_online_cpus();
+ cpu_notifier_register_done();
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
- register_hotcpu_notifier(&coretemp_cpu_notifier);
- put_online_cpus();
+ __register_hotcpu_notifier(&coretemp_cpu_notifier);
+ cpu_notifier_register_done();
return 0;
#ifndef CONFIG_HOTPLUG_CPU
@@ -838,8 +838,8 @@ static void __exit coretemp_exit(void)
{
struct pdev_entry *p, *n;
- get_online_cpus();
- unregister_hotcpu_notifier(&coretemp_cpu_notifier);
+ cpu_notifier_register_begin();
+ __unregister_hotcpu_notifier(&coretemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
@@ -847,7 +847,7 @@ static void __exit coretemp_exit(void)
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
- put_online_cpus();
+ cpu_notifier_register_done();
platform_driver_unregister(&coretemp_driver);
}
diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c
index 1a8aa1265262..32f5132c4652 100644
--- a/drivers/hwmon/f71805f.c
+++ b/drivers/hwmon/f71805f.c
@@ -1648,7 +1648,7 @@ static void __exit f71805f_exit(void)
platform_driver_unregister(&f71805f_driver);
}
-MODULE_AUTHOR("Jean Delvare <khali@linux-fr>");
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("F71805F/F71872F hardware monitoring driver");
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 70749fc15a4f..a327fd3402a7 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -11,6 +11,7 @@
* similar parts. The other devices are supported by different drivers.
*
* Supports: IT8603E Super I/O chip w/LPC interface
+ * IT8623E Super I/O chip w/LPC interface
* IT8705F Super I/O chip w/LPC interface
* IT8712F Super I/O chip w/LPC interface
* IT8716F Super I/O chip w/LPC interface
@@ -147,7 +148,8 @@ static inline void superio_exit(void)
#define IT8772E_DEVID 0x8772
#define IT8782F_DEVID 0x8782
#define IT8783E_DEVID 0x8783
-#define IT8306E_DEVID 0x8603
+#define IT8603E_DEVID 0x8603
+#define IT8623E_DEVID 0x8623
#define IT87_ACT_REG 0x30
#define IT87_BASE_REG 0x60
@@ -1431,7 +1433,7 @@ static ssize_t show_label(struct device *dev, struct device_attribute *attr,
static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0);
static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1);
static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2);
-/* special AVCC3 IT8306E in9 */
+/* special AVCC3 IT8603E in9 */
static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 0);
static ssize_t show_name(struct device *dev, struct device_attribute
@@ -1766,7 +1768,8 @@ static int __init it87_find(unsigned short *address,
case IT8783E_DEVID:
sio_data->type = it8783;
break;
- case IT8306E_DEVID:
+ case IT8603E_DEVID:
+ case IT8623E_DEVID:
sio_data->type = it8603;
break;
case 0xffff: /* No device at all */
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
index baf375b5ab0d..f7b46f68ef43 100644
--- a/drivers/hwmon/k10temp.c
+++ b/drivers/hwmon/k10temp.c
@@ -212,6 +212,7 @@ static const struct pci_device_id k10temp_id_table[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
{}
};
MODULE_DEVICE_TABLE(pci, k10temp_id_table);
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index b4ad598feb6c..848b9611151f 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -155,8 +155,9 @@ enum chips { lm63, lm64, lm96163 };
*/
struct lm63_data {
- struct device *hwmon_dev;
+ struct i2c_client *client;
struct mutex update_lock;
+ const struct attribute_group *groups[5];
char valid; /* zero until following fields are valid */
char lut_valid; /* zero until lut fields are valid */
unsigned long last_updated; /* in jiffies */
@@ -218,9 +219,9 @@ static inline int lut_temp_to_reg(struct lm63_data *data, long val)
* Update the lookup table register cache.
* client->update_lock must be held when calling this function.
*/
-static void lm63_update_lut(struct i2c_client *client)
+static void lm63_update_lut(struct lm63_data *data)
{
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct i2c_client *client = data->client;
int i;
if (time_after(jiffies, data->lut_last_updated + 5 * HZ) ||
@@ -241,8 +242,8 @@ static void lm63_update_lut(struct i2c_client *client)
static struct lm63_data *lm63_update_device(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long next_update;
mutex_lock(&data->update_lock);
@@ -310,7 +311,7 @@ static struct lm63_data *lm63_update_device(struct device *dev)
data->valid = 1;
}
- lm63_update_lut(client);
+ lm63_update_lut(data);
mutex_unlock(&data->update_lock);
@@ -321,18 +322,17 @@ static struct lm63_data *lm63_update_device(struct device *dev)
* Trip points in the lookup table should be in ascending order for both
* temperatures and PWM output values.
*/
-static int lm63_lut_looks_bad(struct i2c_client *client)
+static int lm63_lut_looks_bad(struct device *dev, struct lm63_data *data)
{
- struct lm63_data *data = i2c_get_clientdata(client);
int i;
mutex_lock(&data->update_lock);
- lm63_update_lut(client);
+ lm63_update_lut(data);
for (i = 1; i < data->lut_size; i++) {
if (data->pwm1[1 + i - 1] > data->pwm1[1 + i]
|| data->temp8[3 + i - 1] > data->temp8[3 + i]) {
- dev_warn(&client->dev,
+ dev_warn(dev,
"Lookup table doesn't look sane (check entries %d and %d)\n",
i, i + 1);
break;
@@ -358,8 +358,8 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
static ssize_t set_fan(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long val;
int err;
@@ -399,8 +399,8 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
int nr = attr->index;
unsigned long val;
int err;
@@ -435,8 +435,8 @@ static ssize_t set_pwm1_enable(struct device *dev,
struct device_attribute *dummy,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long val;
int err;
@@ -450,7 +450,7 @@ static ssize_t set_pwm1_enable(struct device *dev,
* Only let the user switch to automatic mode if the lookup table
* looks sane.
*/
- if (val == 2 && lm63_lut_looks_bad(client))
+ if (val == 2 && lm63_lut_looks_bad(dev, data))
return -EPERM;
mutex_lock(&data->update_lock);
@@ -461,7 +461,7 @@ static ssize_t set_pwm1_enable(struct device *dev,
else
data->config_fan &= ~0x20;
i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN,
- data->config_fan);
+ data->config_fan);
mutex_unlock(&data->update_lock);
return count;
}
@@ -505,8 +505,8 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
int nr = attr->index;
long val;
int err;
@@ -579,8 +579,8 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
};
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
long val;
int err;
int nr = attr->index;
@@ -635,8 +635,8 @@ static ssize_t set_temp2_crit_hyst(struct device *dev,
struct device_attribute *dummy,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
long val;
int err;
long hyst;
@@ -657,11 +657,11 @@ static ssize_t set_temp2_crit_hyst(struct device *dev,
* Set conversion rate.
* client->update_lock must be held when calling this function.
*/
-static void lm63_set_convrate(struct i2c_client *client, struct lm63_data *data,
- unsigned int interval)
+static void lm63_set_convrate(struct lm63_data *data, unsigned int interval)
{
- int i;
+ struct i2c_client *client = data->client;
unsigned int update_interval;
+ int i;
/* Shift calculations to avoid rounding errors */
interval <<= 6;
@@ -689,8 +689,7 @@ static ssize_t set_update_interval(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
unsigned long val;
int err;
@@ -699,7 +698,7 @@ static ssize_t set_update_interval(struct device *dev,
return err;
mutex_lock(&data->update_lock);
- lm63_set_convrate(client, data, clamp_val(val, 0, 100000));
+ lm63_set_convrate(data, clamp_val(val, 0, 100000));
mutex_unlock(&data->update_lock);
return count;
@@ -708,8 +707,7 @@ static ssize_t set_update_interval(struct device *dev,
static ssize_t show_type(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
return sprintf(buf, data->trutherm ? "1\n" : "2\n");
}
@@ -717,8 +715,8 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr,
static ssize_t set_type(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long val;
int ret;
u8 reg;
@@ -915,6 +913,15 @@ static struct attribute *lm63_attributes[] = {
NULL
};
+static struct attribute *lm63_attributes_temp2_type[] = {
+ &dev_attr_temp2_type.attr,
+ NULL
+};
+
+static const struct attribute_group lm63_group_temp2_type = {
+ .attrs = lm63_attributes_temp2_type,
+};
+
static struct attribute *lm63_attributes_extra_lut[] = {
&sensor_dev_attr_pwm1_auto_point9_pwm.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point9_temp.dev_attr.attr,
@@ -946,8 +953,7 @@ static umode_t lm63_attribute_mode(struct kobject *kobj,
struct attribute *attr, int index)
{
struct device *dev = container_of(kobj, struct device, kobj);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
if (attr == &sensor_dev_attr_temp2_crit.dev_attr.attr
&& (data->kind == lm64 ||
@@ -1026,9 +1032,10 @@ static int lm63_detect(struct i2c_client *client,
* Ideally we shouldn't have to initialize anything, since the BIOS
* should have taken care of everything
*/
-static void lm63_init_client(struct i2c_client *client)
+static void lm63_init_client(struct lm63_data *data)
{
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct i2c_client *client = data->client;
+ struct device *dev = &client->dev;
u8 convrate;
data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
@@ -1037,7 +1044,7 @@ static void lm63_init_client(struct i2c_client *client)
/* Start converting if needed */
if (data->config & 0x40) { /* standby */
- dev_dbg(&client->dev, "Switching to operational mode\n");
+ dev_dbg(dev, "Switching to operational mode\n");
data->config &= 0xA7;
i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
data->config);
@@ -1090,13 +1097,13 @@ static void lm63_init_client(struct i2c_client *client)
/* Show some debug info about the LM63 configuration */
if (data->kind == lm63)
- dev_dbg(&client->dev, "Alert/tach pin configured for %s\n",
+ dev_dbg(dev, "Alert/tach pin configured for %s\n",
(data->config & 0x04) ? "tachometer input" :
"alert output");
- dev_dbg(&client->dev, "PWM clock %s kHz, output frequency %u Hz\n",
+ dev_dbg(dev, "PWM clock %s kHz, output frequency %u Hz\n",
(data->config_fan & 0x08) ? "1.4" : "360",
((data->config_fan & 0x08) ? 700 : 180000) / data->pwm1_freq);
- dev_dbg(&client->dev, "PWM output active %s, %s mode\n",
+ dev_dbg(dev, "PWM output active %s, %s mode\n",
(data->config_fan & 0x10) ? "low" : "high",
(data->config_fan & 0x20) ? "manual" : "auto");
}
@@ -1104,15 +1111,16 @@ static void lm63_init_client(struct i2c_client *client)
static int lm63_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
struct lm63_data *data;
- int err;
+ int groups = 0;
- data = devm_kzalloc(&client->dev, sizeof(struct lm63_data), GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct lm63_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- i2c_set_clientdata(client, data);
- data->valid = 0;
+ data->client = client;
mutex_init(&data->update_lock);
/* Set the device type */
@@ -1121,59 +1129,21 @@ static int lm63_probe(struct i2c_client *client,
data->temp2_offset = 16000;
/* Initialize chip */
- lm63_init_client(client);
+ lm63_init_client(data);
/* Register sysfs hooks */
- err = sysfs_create_group(&client->dev.kobj, &lm63_group);
- if (err)
- return err;
- if (data->config & 0x04) { /* tachometer enabled */
- err = sysfs_create_group(&client->dev.kobj, &lm63_group_fan1);
- if (err)
- goto exit_remove_files;
- }
- if (data->kind == lm96163) {
- err = device_create_file(&client->dev, &dev_attr_temp2_type);
- if (err)
- goto exit_remove_files;
-
- err = sysfs_create_group(&client->dev.kobj,
- &lm63_group_extra_lut);
- if (err)
- goto exit_remove_files;
- }
-
- data->hwmon_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- goto exit_remove_files;
- }
+ data->groups[groups++] = &lm63_group;
+ if (data->config & 0x04) /* tachometer enabled */
+ data->groups[groups++] = &lm63_group_fan1;
- return 0;
-
-exit_remove_files:
- sysfs_remove_group(&client->dev.kobj, &lm63_group);
- sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1);
if (data->kind == lm96163) {
- device_remove_file(&client->dev, &dev_attr_temp2_type);
- sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut);
+ data->groups[groups++] = &lm63_group_temp2_type;
+ data->groups[groups++] = &lm63_group_extra_lut;
}
- return err;
-}
-
-static int lm63_remove(struct i2c_client *client)
-{
- struct lm63_data *data = i2c_get_clientdata(client);
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &lm63_group);
- sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1);
- if (data->kind == lm96163) {
- device_remove_file(&client->dev, &dev_attr_temp2_type);
- sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut);
- }
-
- return 0;
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
}
/*
@@ -1194,7 +1164,6 @@ static struct i2c_driver lm63_driver = {
.name = "lm63",
},
.probe = lm63_probe,
- .remove = lm63_remove,
.id_table = lm63_id,
.detect = lm63_detect,
.address_list = normal_i2c,
diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c
index f17beb5e6dd6..502771c06fd9 100644
--- a/drivers/hwmon/lm77.c
+++ b/drivers/hwmon/lm77.c
@@ -348,7 +348,6 @@ static int lm77_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENOMEM;
i2c_set_clientdata(client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/* Initialize the LM77 chip */
diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c
index eba89aac3ece..bd0a1ebbf867 100644
--- a/drivers/hwmon/lm80.c
+++ b/drivers/hwmon/lm80.c
@@ -112,7 +112,7 @@ static inline long TEMP_FROM_REG(u16 temp)
*/
struct lm80_data {
- struct device *hwmon_dev;
+ struct i2c_client *client;
struct mutex update_lock;
char error; /* !=0 if error occurred during last update */
char valid; /* !=0 if following fields are valid */
@@ -140,7 +140,6 @@ static int lm80_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info);
static void lm80_init_client(struct i2c_client *client);
-static int lm80_remove(struct i2c_client *client);
static struct lm80_data *lm80_update_device(struct device *dev);
static int lm80_read_value(struct i2c_client *client, u8 reg);
static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value);
@@ -162,7 +161,6 @@ static struct i2c_driver lm80_driver = {
.name = "lm80",
},
.probe = lm80_probe,
- .remove = lm80_remove,
.id_table = lm80_id,
.detect = lm80_detect,
.address_list = normal_i2c,
@@ -191,8 +189,8 @@ static ssize_t set_in_##suffix(struct device *dev, \
struct device_attribute *attr, const char *buf, size_t count) \
{ \
int nr = to_sensor_dev_attr(attr)->index; \
- struct i2c_client *client = to_i2c_client(dev); \
- struct lm80_data *data = i2c_get_clientdata(client); \
+ struct lm80_data *data = dev_get_drvdata(dev); \
+ struct i2c_client *client = data->client; \
long val; \
int err = kstrtol(buf, 10, &val); \
if (err < 0) \
@@ -235,8 +233,8 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int nr = to_sensor_dev_attr(attr)->index;
- struct i2c_client *client = to_i2c_client(dev);
- struct lm80_data *data = i2c_get_clientdata(client);
+ struct lm80_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long val;
int err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -259,8 +257,8 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int nr = to_sensor_dev_attr(attr)->index;
- struct i2c_client *client = to_i2c_client(dev);
- struct lm80_data *data = i2c_get_clientdata(client);
+ struct lm80_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long min, val;
u8 reg;
int err = kstrtoul(buf, 10, &val);
@@ -286,7 +284,7 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
data->fan_div[nr] = 3;
break;
default:
- dev_err(&client->dev,
+ dev_err(dev,
"fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
val);
mutex_unlock(&data->update_lock);
@@ -332,8 +330,8 @@ show_temp(os_hyst, temp_os_hyst);
static ssize_t set_temp_##suffix(struct device *dev, \
struct device_attribute *attr, const char *buf, size_t count) \
{ \
- struct i2c_client *client = to_i2c_client(dev); \
- struct lm80_data *data = i2c_get_clientdata(client); \
+ struct lm80_data *data = dev_get_drvdata(dev); \
+ struct i2c_client *client = data->client; \
long val; \
int err = kstrtol(buf, 10, &val); \
if (err < 0) \
@@ -440,7 +438,7 @@ static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 13);
* Real code
*/
-static struct attribute *lm80_attributes[] = {
+static struct attribute *lm80_attrs[] = {
&sensor_dev_attr_in0_min.dev_attr.attr,
&sensor_dev_attr_in1_min.dev_attr.attr,
&sensor_dev_attr_in2_min.dev_attr.attr,
@@ -487,10 +485,7 @@ static struct attribute *lm80_attributes[] = {
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
NULL
};
-
-static const struct attribute_group lm80_group = {
- .attrs = lm80_attributes,
-};
+ATTRIBUTE_GROUPS(lm80);
/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info)
@@ -541,14 +536,15 @@ static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info)
static int lm80_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
struct lm80_data *data;
- int err;
- data = devm_kzalloc(&client->dev, sizeof(struct lm80_data), GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct lm80_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- i2c_set_clientdata(client, data);
+ data->client = client;
mutex_init(&data->update_lock);
/* Initialize the LM80 chip */
@@ -558,32 +554,10 @@ static int lm80_probe(struct i2c_client *client,
data->fan_min[0] = lm80_read_value(client, LM80_REG_FAN_MIN(1));
data->fan_min[1] = lm80_read_value(client, LM80_REG_FAN_MIN(2));
- /* Register sysfs hooks */
- err = sysfs_create_group(&client->dev.kobj, &lm80_group);
- if (err)
- return err;
-
- data->hwmon_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- goto error_remove;
- }
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, lm80_groups);
- return 0;
-
-error_remove:
- sysfs_remove_group(&client->dev.kobj, &lm80_group);
- return err;
-}
-
-static int lm80_remove(struct i2c_client *client)
-{
- struct lm80_data *data = i2c_get_clientdata(client);
-
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &lm80_group);
-
- return 0;
+ return PTR_ERR_OR_ZERO(hwmon_dev);
}
static int lm80_read_value(struct i2c_client *client, u8 reg)
@@ -614,8 +588,8 @@ static void lm80_init_client(struct i2c_client *client)
static struct lm80_data *lm80_update_device(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm80_data *data = i2c_get_clientdata(client);
+ struct lm80_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
int i;
int rv;
int prev_rv;
@@ -627,7 +601,7 @@ static struct lm80_data *lm80_update_device(struct device *dev)
lm80_init_client(client);
if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
- dev_dbg(&client->dev, "Starting lm80 update\n");
+ dev_dbg(dev, "Starting lm80 update\n");
for (i = 0; i <= 6; i++) {
rv = lm80_read_value(client, LM80_REG_IN(i));
if (rv < 0)
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
index abd270243ba7..be02155788c3 100644
--- a/drivers/hwmon/lm83.c
+++ b/drivers/hwmon/lm83.c
@@ -349,7 +349,6 @@ static int lm83_probe(struct i2c_client *new_client,
return -ENOMEM;
i2c_set_clientdata(new_client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/*
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index 4c5f20231c1a..ba1d83d48056 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -903,7 +903,6 @@ static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENOMEM;
i2c_set_clientdata(client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/* Initialize the LM87 chip */
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 701e952ae523..c9ff08dbe10c 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -365,7 +365,9 @@ enum lm90_temp11_reg_index {
*/
struct lm90_data {
+ struct i2c_client *client;
struct device *hwmon_dev;
+ const struct attribute_group *groups[6];
struct mutex update_lock;
struct regulator *regulator;
char valid; /* zero until following fields are valid */
@@ -513,8 +515,8 @@ static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
static struct lm90_data *lm90_update_device(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm90_data *data = i2c_get_clientdata(client);
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long next_update;
mutex_lock(&data->update_lock);
@@ -793,8 +795,8 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
};
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm90_data *data = i2c_get_clientdata(client);
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
int nr = attr->index;
long val;
int err;
@@ -860,8 +862,8 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
};
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm90_data *data = i2c_get_clientdata(client);
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
int nr = attr->nr;
int index = attr->index;
long val;
@@ -922,8 +924,8 @@ static ssize_t show_temphyst(struct device *dev,
static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm90_data *data = i2c_get_clientdata(client);
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
long val;
int err;
int temp;
@@ -976,8 +978,8 @@ static ssize_t set_update_interval(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm90_data *data = i2c_get_clientdata(client);
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long val;
int err;
@@ -1057,6 +1059,15 @@ static const struct attribute_group lm90_group = {
.attrs = lm90_attributes,
};
+static struct attribute *lm90_temp2_offset_attributes[] = {
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm90_temp2_offset_group = {
+ .attrs = lm90_temp2_offset_attributes,
+};
+
/*
* Additional attributes for devices with emergency sensors
*/
@@ -1393,22 +1404,6 @@ static int lm90_detect(struct i2c_client *client,
return 0;
}
-static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data)
-{
- struct device *dev = &client->dev;
-
- if (data->flags & LM90_HAVE_TEMP3)
- sysfs_remove_group(&dev->kobj, &lm90_temp3_group);
- if (data->flags & LM90_HAVE_EMERGENCY_ALARM)
- sysfs_remove_group(&dev->kobj, &lm90_emergency_alarm_group);
- if (data->flags & LM90_HAVE_EMERGENCY)
- sysfs_remove_group(&dev->kobj, &lm90_emergency_group);
- if (data->flags & LM90_HAVE_OFFSET)
- device_remove_file(dev, &sensor_dev_attr_temp2_offset.dev_attr);
- device_remove_file(dev, &dev_attr_pec);
- sysfs_remove_group(&dev->kobj, &lm90_group);
-}
-
static void lm90_restore_conf(struct i2c_client *client, struct lm90_data *data)
{
/* Restore initial configuration */
@@ -1418,10 +1413,9 @@ static void lm90_restore_conf(struct i2c_client *client, struct lm90_data *data)
data->config_orig);
}
-static void lm90_init_client(struct i2c_client *client)
+static void lm90_init_client(struct i2c_client *client, struct lm90_data *data)
{
u8 config, convrate;
- struct lm90_data *data = i2c_get_clientdata(client);
if (lm90_read_reg(client, LM90_REG_R_CONVRATE, &convrate) < 0) {
dev_warn(&client->dev, "Failed to read convrate register!\n");
@@ -1519,6 +1513,7 @@ static int lm90_probe(struct i2c_client *client,
struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
struct lm90_data *data;
struct regulator *regulator;
+ int groups = 0;
int err;
regulator = devm_regulator_get(dev, "vcc");
@@ -1527,15 +1522,15 @@ static int lm90_probe(struct i2c_client *client,
err = regulator_enable(regulator);
if (err < 0) {
- dev_err(&client->dev,
- "Failed to enable regulator: %d\n", err);
+ dev_err(dev, "Failed to enable regulator: %d\n", err);
return err;
}
- data = devm_kzalloc(&client->dev, sizeof(struct lm90_data), GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct lm90_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
+ data->client = client;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -1562,44 +1557,34 @@ static int lm90_probe(struct i2c_client *client,
data->max_convrate = lm90_params[data->kind].max_convrate;
/* Initialize the LM90 chip */
- lm90_init_client(client);
+ lm90_init_client(client, data);
/* Register sysfs hooks */
- err = sysfs_create_group(&dev->kobj, &lm90_group);
- if (err)
- goto exit_restore;
+ data->groups[groups++] = &lm90_group;
+
+ if (data->flags & LM90_HAVE_OFFSET)
+ data->groups[groups++] = &lm90_temp2_offset_group;
+
+ if (data->flags & LM90_HAVE_EMERGENCY)
+ data->groups[groups++] = &lm90_emergency_group;
+
+ if (data->flags & LM90_HAVE_EMERGENCY_ALARM)
+ data->groups[groups++] = &lm90_emergency_alarm_group;
+
+ if (data->flags & LM90_HAVE_TEMP3)
+ data->groups[groups++] = &lm90_temp3_group;
+
if (client->flags & I2C_CLIENT_PEC) {
err = device_create_file(dev, &dev_attr_pec);
if (err)
- goto exit_remove_files;
- }
- if (data->flags & LM90_HAVE_OFFSET) {
- err = device_create_file(dev,
- &sensor_dev_attr_temp2_offset.dev_attr);
- if (err)
- goto exit_remove_files;
- }
- if (data->flags & LM90_HAVE_EMERGENCY) {
- err = sysfs_create_group(&dev->kobj, &lm90_emergency_group);
- if (err)
- goto exit_remove_files;
- }
- if (data->flags & LM90_HAVE_EMERGENCY_ALARM) {
- err = sysfs_create_group(&dev->kobj,
- &lm90_emergency_alarm_group);
- if (err)
- goto exit_remove_files;
- }
- if (data->flags & LM90_HAVE_TEMP3) {
- err = sysfs_create_group(&dev->kobj, &lm90_temp3_group);
- if (err)
- goto exit_remove_files;
+ goto exit_restore;
}
- data->hwmon_dev = hwmon_device_register(dev);
+ data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
- goto exit_remove_files;
+ goto exit_remove_pec;
}
if (client->irq) {
@@ -1618,8 +1603,8 @@ static int lm90_probe(struct i2c_client *client,
exit_unregister:
hwmon_device_unregister(data->hwmon_dev);
-exit_remove_files:
- lm90_remove_files(client, data);
+exit_remove_pec:
+ device_remove_file(dev, &dev_attr_pec);
exit_restore:
lm90_restore_conf(client, data);
regulator_disable(data->regulator);
@@ -1632,7 +1617,7 @@ static int lm90_remove(struct i2c_client *client)
struct lm90_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
- lm90_remove_files(client, data);
+ device_remove_file(&client->dev, &dev_attr_pec);
lm90_restore_conf(client, data);
regulator_disable(data->regulator);
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index 9d0e87a4f0cb..b9022db6511a 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -380,7 +380,6 @@ static int lm92_probe(struct i2c_client *new_client,
return -ENOMEM;
i2c_set_clientdata(new_client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/* Initialize the chipset */
diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c
index 6f1c6c0dbaf5..adf23165a6a7 100644
--- a/drivers/hwmon/lm93.c
+++ b/drivers/hwmon/lm93.c
@@ -2754,7 +2754,6 @@ static int lm93_probe(struct i2c_client *client,
i2c_set_clientdata(client, data);
/* housekeeping */
- data->valid = 0;
data->update = update;
mutex_init(&data->update_lock);
diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c
index 6638e997f83f..4c23afe113e2 100644
--- a/drivers/hwmon/max1619.c
+++ b/drivers/hwmon/max1619.c
@@ -273,7 +273,6 @@ static int max1619_probe(struct i2c_client *new_client,
return -ENOMEM;
i2c_set_clientdata(new_client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/* Initialize the MAX1619 chip */
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c
index 330fe117e219..988181e4cfcd 100644
--- a/drivers/hwmon/pc87360.c
+++ b/drivers/hwmon/pc87360.c
@@ -1225,7 +1225,7 @@ static int pc87360_probe(struct platform_device *pdev)
int i;
struct pc87360_data *data;
int err = 0;
- const char *name = "pc87360";
+ const char *name;
int use_thermistors = 0;
struct device *dev = &pdev->dev;
@@ -1233,13 +1233,14 @@ static int pc87360_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
- data->fannr = 2;
- data->innr = 0;
- data->tempnr = 0;
-
switch (devid) {
+ default:
+ name = "pc87360";
+ data->fannr = 2;
+ break;
case 0xe8:
name = "pc87363";
+ data->fannr = 2;
break;
case 0xe4:
name = "pc87364";
@@ -1260,7 +1261,6 @@ static int pc87360_probe(struct platform_device *pdev)
}
data->name = name;
- data->valid = 0;
mutex_init(&data->lock);
mutex_init(&data->update_lock);
platform_set_drvdata(pdev, data);
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
index 38944e94f65f..8df43c51de2c 100644
--- a/drivers/hwmon/via-cputemp.c
+++ b/drivers/hwmon/via-cputemp.c
@@ -319,7 +319,7 @@ static int __init via_cputemp_init(void)
if (err)
goto exit;
- get_online_cpus();
+ cpu_notifier_register_begin();
for_each_online_cpu(i) {
struct cpuinfo_x86 *c = &cpu_data(i);
@@ -339,14 +339,14 @@ static int __init via_cputemp_init(void)
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
- put_online_cpus();
+ cpu_notifier_register_done();
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
- register_hotcpu_notifier(&via_cputemp_cpu_notifier);
- put_online_cpus();
+ __register_hotcpu_notifier(&via_cputemp_cpu_notifier);
+ cpu_notifier_register_done();
return 0;
#ifndef CONFIG_HOTPLUG_CPU
@@ -361,8 +361,8 @@ static void __exit via_cputemp_exit(void)
{
struct pdev_entry *p, *n;
- get_online_cpus();
- unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
+ cpu_notifier_register_begin();
+ __unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
@@ -370,7 +370,7 @@ static void __exit via_cputemp_exit(void)
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
- put_online_cpus();
+ cpu_notifier_register_done();
platform_driver_unregister(&via_cputemp_driver);
}
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index df585808adb6..4068db4d9580 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -1376,7 +1376,6 @@ w83792d_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENOMEM;
i2c_set_clientdata(client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
err = w83792d_detect_subclients(client);
diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c
index 6384b268f590..ac3043122011 100644
--- a/drivers/hwmon/w83l785ts.c
+++ b/drivers/hwmon/w83l785ts.c
@@ -188,12 +188,8 @@ static int w83l785ts_probe(struct i2c_client *client,
return -ENOMEM;
i2c_set_clientdata(client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
- /* Default values in case the first read fails (unlikely). */
- data->temp[1] = data->temp[0] = 0;
-
/*
* Initialize the W83L785TS chip
* Nothing yet, assume it is already started.
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index 9ddbf550bafa..a43220c2e3d9 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -873,14 +873,19 @@ static int __init intel_idle_init(void)
if (intel_idle_cpuidle_devices == NULL)
return -ENOMEM;
+ cpu_notifier_register_begin();
+
for_each_online_cpu(i) {
retval = intel_idle_cpu_init(i);
if (retval) {
+ cpu_notifier_register_done();
cpuidle_unregister_driver(&intel_idle_driver);
return retval;
}
}
- register_cpu_notifier(&cpu_hotplug_notifier);
+ __register_cpu_notifier(&cpu_hotplug_notifier);
+
+ cpu_notifier_register_done();
return 0;
}
@@ -890,10 +895,13 @@ static void __exit intel_idle_exit(void)
intel_idle_cpuidle_devices_uninit();
cpuidle_unregister_driver(&intel_idle_driver);
+ cpu_notifier_register_begin();
if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE)
on_each_cpu(__setup_broadcast_timer, (void *)false, 1);
- unregister_cpu_notifier(&cpu_hotplug_notifier);
+ __unregister_cpu_notifier(&cpu_hotplug_notifier);
+
+ cpu_notifier_register_done();
return;
}
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 4bf4c16de976..d86196cfe4b4 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -193,6 +193,16 @@ config TI_AM335X_ADC
Say yes here to build support for Texas Instruments ADC
driver which is also a MFD client.
+config TWL4030_MADC
+ tristate "TWL4030 MADC (Monitoring A/D Converter)"
+ depends on TWL4030_CORE
+ help
+ This driver provides support for Triton TWL4030-MADC. The
+ driver supports both RT and SW conversion methods.
+
+ This driver can also be built as a module. If so, the module will be
+ called twl4030-madc.
+
config TWL6030_GPADC
tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"
depends on TWL4030_CORE
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index bb252540664a..ab346d88c688 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
+obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
diff --git a/drivers/mfd/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
index 4c583e471339..7de1c4c87942 100644
--- a/drivers/mfd/twl4030-madc.c
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -29,7 +29,6 @@
*
*/
-#include <linux/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -47,20 +46,84 @@
#include <linux/gfp.h>
#include <linux/err.h>
-/*
+#include <linux/iio/iio.h>
+
+/**
* struct twl4030_madc_data - a container for madc info
- * @dev - pointer to device structure for madc
- * @lock - mutex protecting this data structure
- * @requests - Array of request struct corresponding to SW1, SW2 and RT
- * @imr - Interrupt mask register of MADC
- * @isr - Interrupt status register of MADC
+ * @dev: Pointer to device structure for madc
+ * @lock: Mutex protecting this data structure
+ * @requests: Array of request struct corresponding to SW1, SW2 and RT
+ * @use_second_irq: IRQ selection (main or co-processor)
+ * @imr: Interrupt mask register of MADC
+ * @isr: Interrupt status register of MADC
*/
struct twl4030_madc_data {
struct device *dev;
struct mutex lock; /* mutex protecting this data structure */
struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
- int imr;
- int isr;
+ bool use_second_irq;
+ u8 imr;
+ u8 isr;
+};
+
+static int twl4030_madc_read(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct twl4030_madc_data *madc = iio_priv(iio_dev);
+ struct twl4030_madc_request req;
+ int ret;
+
+ req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
+
+ req.channels = BIT(chan->channel);
+ req.active = false;
+ req.func_cb = NULL;
+ req.type = TWL4030_MADC_WAIT;
+ req.raw = !(mask == IIO_CHAN_INFO_PROCESSED);
+ req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW);
+
+ ret = twl4030_madc_conversion(&req);
+ if (ret < 0)
+ return ret;
+
+ *val = req.rbuf[chan->channel];
+
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info twl4030_madc_iio_info = {
+ .read_raw = &twl4030_madc_read,
+ .driver_module = THIS_MODULE,
+};
+
+#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \
+ .type = _type, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ .datasheet_name = _name, \
+ .indexed = 1, \
+}
+
+static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
+ TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"),
+ TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"),
+ TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"),
+ TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"),
+ TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"),
+ TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"),
+ TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"),
+ TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"),
+ TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"),
+ TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"),
+ TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"),
+ TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"),
+ TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"),
+ TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"),
+ TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"),
+ TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"),
};
static struct twl4030_madc_data *twl4030_madc;
@@ -91,17 +154,16 @@ twl4030_divider_ratios[16] = {
};
-/*
- * Conversion table from -3 to 55 degree Celcius
- */
-static int therm_tbl[] = {
-30800, 29500, 28300, 27100,
-26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900,
-17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100,
-11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310,
-8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830,
-5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170,
-4040, 3910, 3790, 3670, 3550
+/* Conversion table from -3 to 55 degrees Celcius */
+static int twl4030_therm_tbl[] = {
+ 30800, 29500, 28300, 27100,
+ 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700,
+ 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100,
+ 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280,
+ 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710,
+ 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920,
+ 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670,
+ 3550
};
/*
@@ -133,37 +195,32 @@ const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
},
};
-/*
- * Function to read a particular channel value.
- * @madc - pointer to struct twl4030_madc_data
- * @reg - lsb of ADC Channel
- * If the i2c read fails it returns an error else returns 0.
+/**
+ * twl4030_madc_channel_raw_read() - Function to read a particular channel value
+ * @madc: pointer to struct twl4030_madc_data
+ * @reg: lsb of ADC Channel
+ *
+ * Return: 0 on success, an error code otherwise.
*/
static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
{
- u8 msb, lsb;
+ u16 val;
int ret;
/*
* For each ADC channel, we have MSB and LSB register pair. MSB address
* is always LSB address+1. reg parameter is the address of LSB register
*/
- ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1);
+ ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg);
if (ret) {
- dev_err(madc->dev, "unable to read MSB register 0x%X\n",
- reg + 1);
- return ret;
- }
- ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg);
- if (ret) {
- dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg);
+ dev_err(madc->dev, "unable to read register 0x%X\n", reg);
return ret;
}
- return (int)(((msb << 8) | lsb) >> 6);
+ return (int)(val >> 6);
}
/*
- * Return battery temperature
+ * Return battery temperature in degrees Celsius
* Or < 0 on failure.
*/
static int twl4030battery_temperature(int raw_volt)
@@ -172,18 +229,18 @@ static int twl4030battery_temperature(int raw_volt)
int temp, curr, volt, res, ret;
volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
- /* Getting and calculating the supply current in micro ampers */
+ /* Getting and calculating the supply current in micro amperes */
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
REG_BCICTL2);
if (ret < 0)
return ret;
+
curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
/* Getting and calculating the thermistor resistance in ohms */
res = volt * 1000 / curr;
/* calculating temperature */
for (temp = 58; temp >= 0; temp--) {
- int actual = therm_tbl[temp];
-
+ int actual = twl4030_therm_tbl[temp];
if ((actual - res) >= 0)
break;
}
@@ -205,11 +262,12 @@ static int twl4030battery_current(int raw_volt)
else /* slope of 0.88 mV/mA */
return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
}
+
/*
* Function to read channel values
* @madc - pointer to twl4030_madc_data struct
* @reg_base - Base address of the first channel
- * @Channels - 16 bit bitmap. If the bit is set, channel value is read
+ * @Channels - 16 bit bitmap. If the bit is set, channel's value is read
* @buf - The channel values are stored here. if read fails error
* @raw - Return raw values without conversion
* value is stored
@@ -220,17 +278,17 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
long channels, int *buf,
bool raw)
{
- int count = 0, count_req = 0, i;
+ int count = 0;
+ int i;
u8 reg;
for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
- reg = reg_base + 2 * i;
+ reg = reg_base + (2 * i);
buf[i] = twl4030_madc_channel_raw_read(madc, reg);
if (buf[i] < 0) {
- dev_err(madc->dev,
- "Unable to read register 0x%X\n", reg);
- count_req++;
- continue;
+ dev_err(madc->dev, "Unable to read register 0x%X\n",
+ reg);
+ return buf[i];
}
if (raw) {
count++;
@@ -241,7 +299,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
buf[i] = twl4030battery_current(buf[i]);
if (buf[i] < 0) {
dev_err(madc->dev, "err reading current\n");
- count_req++;
+ return buf[i];
} else {
count++;
buf[i] = buf[i] - 750;
@@ -251,7 +309,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
buf[i] = twl4030battery_temperature(buf[i]);
if (buf[i] < 0) {
dev_err(madc->dev, "err reading temperature\n");
- count_req++;
+ return buf[i];
} else {
buf[i] -= 3;
count++;
@@ -272,8 +330,6 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
twl4030_divider_ratios[i].numerator);
}
}
- if (count_req)
- dev_err(madc->dev, "%d channel conversion failed\n", count_req);
return count;
}
@@ -297,13 +353,13 @@ static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
madc->imr);
return ret;
}
+
val &= ~(1 << id);
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
if (ret) {
dev_err(madc->dev,
"unable to write imr register 0x%X\n", madc->imr);
return ret;
-
}
return 0;
@@ -366,7 +422,7 @@ static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
continue;
ret = twl4030_madc_disable_irq(madc, i);
if (ret < 0)
- dev_dbg(madc->dev, "Disable interrupt failed%d\n", i);
+ dev_dbg(madc->dev, "Disable interrupt failed %d\n", i);
madc->requests[i].result_pending = 1;
}
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
@@ -448,21 +504,17 @@ static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
{
const struct twl4030_madc_conversion_method *method;
int ret = 0;
+
+ if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2)
+ return -ENOTSUPP;
+
method = &twl4030_conversion_methods[conv_method];
- switch (conv_method) {
- case TWL4030_MADC_SW1:
- case TWL4030_MADC_SW2:
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
- TWL4030_MADC_SW_START, method->ctrl);
- if (ret) {
- dev_err(madc->dev,
- "unable to write ctrl register 0x%X\n",
- method->ctrl);
- return ret;
- }
- break;
- default:
- break;
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START,
+ method->ctrl);
+ if (ret) {
+ dev_err(madc->dev, "unable to write ctrl register 0x%X\n",
+ method->ctrl);
+ return ret;
}
return 0;
@@ -513,7 +565,6 @@ static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
int twl4030_madc_conversion(struct twl4030_madc_request *req)
{
const struct twl4030_madc_conversion_method *method;
- u8 ch_msb, ch_lsb;
int ret;
if (!req || !twl4030_madc)
@@ -529,38 +580,22 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
ret = -EBUSY;
goto out;
}
- ch_msb = (req->channels >> 8) & 0xff;
- ch_lsb = req->channels & 0xff;
method = &twl4030_conversion_methods[req->method];
/* Select channels to be converted */
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1);
- if (ret) {
- dev_err(twl4030_madc->dev,
- "unable to write sel register 0x%X\n", method->sel + 1);
- goto out;
- }
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
+ ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel);
if (ret) {
dev_err(twl4030_madc->dev,
- "unable to write sel register 0x%X\n", method->sel + 1);
+ "unable to write sel register 0x%X\n", method->sel);
goto out;
}
/* Select averaging for all channels if do_avg is set */
if (req->do_avg) {
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
- ch_msb, method->avg + 1);
+ ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels,
+ method->avg);
if (ret) {
dev_err(twl4030_madc->dev,
"unable to write avg register 0x%X\n",
- method->avg + 1);
- goto out;
- }
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
- ch_lsb, method->avg);
- if (ret) {
- dev_err(twl4030_madc->dev,
- "unable to write sel reg 0x%X\n",
- method->sel + 1);
+ method->avg);
goto out;
}
}
@@ -601,10 +636,6 @@ out:
}
EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
-/*
- * Return channel value
- * Or < 0 on failure.
- */
int twl4030_get_madc_conversion(int channel_no)
{
struct twl4030_madc_request req;
@@ -625,20 +656,25 @@ int twl4030_get_madc_conversion(int channel_no)
}
EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
-/*
+/**
+ * twl4030_madc_set_current_generator() - setup bias current
+ *
+ * @madc: pointer to twl4030_madc_data struct
+ * @chan: can be one of the two values:
+ * TWL4030_BCI_ITHEN
+ * Enables bias current for main battery type reading
+ * TWL4030_BCI_TYPEN
+ * Enables bias current for main battery temperature sensing
+ * @on: enable or disable chan.
+ *
* Function to enable or disable bias current for
* main battery type reading or temperature sensing
- * @madc - pointer to twl4030_madc_data struct
- * @chan - can be one of the two values
- * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading
- * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature
- * sensing
- * @on - enable or disable chan.
*/
static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
int chan, int on)
{
int ret;
+ int regmask;
u8 regval;
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
@@ -648,10 +684,13 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
TWL4030_BCI_BCICTL1);
return ret;
}
+
+ regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
if (on)
- regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
+ regval |= regmask;
else
- regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN;
+ regval &= ~regmask;
+
ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
regval, TWL4030_BCI_BCICTL1);
if (ret) {
@@ -666,7 +705,7 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
/*
* Function that sets MADC software power on bit to enable MADC
* @madc - pointer to twl4030_madc_data struct
- * @on - Enable or disable MADC software powen on bit.
+ * @on - Enable or disable MADC software power on bit.
* returns error if i2c read/write fails else 0
*/
static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
@@ -702,31 +741,52 @@ static int twl4030_madc_probe(struct platform_device *pdev)
{
struct twl4030_madc_data *madc;
struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
- int ret;
+ struct device_node *np = pdev->dev.of_node;
+ int irq, ret;
u8 regval;
+ struct iio_dev *iio_dev = NULL;
- if (!pdata) {
- dev_err(&pdev->dev, "platform_data not available\n");
+ if (!pdata && !np) {
+ dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n");
return -EINVAL;
}
- madc = kzalloc(sizeof(*madc), GFP_KERNEL);
- if (!madc)
+
+ iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc));
+ if (!iio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
return -ENOMEM;
+ }
+ madc = iio_priv(iio_dev);
madc->dev = &pdev->dev;
+ iio_dev->name = dev_name(&pdev->dev);
+ iio_dev->dev.parent = &pdev->dev;
+ iio_dev->dev.of_node = pdev->dev.of_node;
+ iio_dev->info = &twl4030_madc_iio_info;
+ iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->channels = twl4030_madc_iio_channels;
+ iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels);
+
/*
* Phoenix provides 2 interrupt lines. The first one is connected to
* the OMAP. The other one can be connected to the other processor such
* as modem. Hence two separate ISR and IMR registers.
*/
- madc->imr = (pdata->irq_line == 1) ?
- TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
- madc->isr = (pdata->irq_line == 1) ?
- TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
+ if (pdata)
+ madc->use_second_irq = (pdata->irq_line != 1);
+ else
+ madc->use_second_irq = of_property_read_bool(np,
+ "ti,system-uses-second-madc-irq");
+
+ madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 :
+ TWL4030_MADC_IMR1;
+ madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 :
+ TWL4030_MADC_ISR1;
+
ret = twl4030_madc_set_power(madc, 1);
if (ret < 0)
- goto err_power;
+ return ret;
ret = twl4030_madc_set_current_generator(madc, 0, 1);
if (ret < 0)
goto err_current_generator;
@@ -768,46 +828,63 @@ static int twl4030_madc_probe(struct platform_device *pdev)
}
}
- platform_set_drvdata(pdev, madc);
+ platform_set_drvdata(pdev, iio_dev);
mutex_init(&madc->lock);
- ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL,
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
twl4030_madc_threaded_irq_handler,
IRQF_TRIGGER_RISING, "twl4030_madc", madc);
if (ret) {
- dev_dbg(&pdev->dev, "could not request irq\n");
+ dev_err(&pdev->dev, "could not request irq\n");
goto err_i2c;
}
twl4030_madc = madc;
+
+ ret = iio_device_register(iio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register iio device\n");
+ goto err_i2c;
+ }
+
return 0;
+
err_i2c:
twl4030_madc_set_current_generator(madc, 0, 0);
err_current_generator:
twl4030_madc_set_power(madc, 0);
-err_power:
- kfree(madc);
-
return ret;
}
static int twl4030_madc_remove(struct platform_device *pdev)
{
- struct twl4030_madc_data *madc = platform_get_drvdata(pdev);
+ struct iio_dev *iio_dev = platform_get_drvdata(pdev);
+ struct twl4030_madc_data *madc = iio_priv(iio_dev);
+
+ iio_device_unregister(iio_dev);
- free_irq(platform_get_irq(pdev, 0), madc);
twl4030_madc_set_current_generator(madc, 0, 0);
twl4030_madc_set_power(madc, 0);
- kfree(madc);
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id twl_madc_of_match[] = {
+ { .compatible = "ti,twl4030-madc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, twl_madc_of_match);
+#endif
+
static struct platform_driver twl4030_madc_driver = {
.probe = twl4030_madc_probe,
.remove = twl4030_madc_remove,
.driver = {
.name = "twl4030_madc",
.owner = THIS_MODULE,
- },
+ .of_match_table = of_match_ptr(twl_madc_of_match),
+ },
};
module_platform_driver(twl4030_madc_driver);
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
index 7dcf83998e6f..dbefbdaf7cd1 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -38,29 +38,40 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
if (state) {
if (sensor_hub_device_open(st->hsdev))
return -EIO;
- state_val =
- HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM;
- report_val =
- HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM;
-
+ state_val = hid_sensor_get_usage_index(st->hsdev,
+ st->power_state.report_id,
+ st->power_state.index,
+ HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM);
+ report_val = hid_sensor_get_usage_index(st->hsdev,
+ st->report_state.report_id,
+ st->report_state.index,
+ HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
} else {
sensor_hub_device_close(st->hsdev);
- state_val =
- HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM;
- report_val =
- HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM;
+ state_val = hid_sensor_get_usage_index(st->hsdev,
+ st->power_state.report_id,
+ st->power_state.index,
+ HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM);
+ report_val = hid_sensor_get_usage_index(st->hsdev,
+ st->report_state.report_id,
+ st->report_state.index,
+ HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
}
-
st->data_ready = state;
- state_val += st->power_state.logical_minimum;
- report_val += st->report_state.logical_minimum;
- sensor_hub_set_feature(st->hsdev, st->power_state.report_id,
+
+ if (state_val >= 0) {
+ state_val += st->power_state.logical_minimum;
+ sensor_hub_set_feature(st->hsdev, st->power_state.report_id,
st->power_state.index,
(s32)state_val);
+ }
- sensor_hub_set_feature(st->hsdev, st->report_state.report_id,
+ if (report_val >= 0) {
+ report_val += st->report_state.logical_minimum;
+ sensor_hub_set_feature(st->hsdev, st->report_state.report_id,
st->report_state.index,
(s32)report_val);
+ }
return 0;
}
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index 0601b9daf840..c3239170d8b7 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -349,23 +349,6 @@ static void cm_init_av_for_response(struct cm_port *port, struct ib_wc *wc,
grh, &av->ah_attr);
}
-int ib_update_cm_av(struct ib_cm_id *id, const u8 *smac, const u8 *alt_smac)
-{
- struct cm_id_private *cm_id_priv;
-
- cm_id_priv = container_of(id, struct cm_id_private, id);
-
- if (smac != NULL)
- memcpy(cm_id_priv->av.smac, smac, sizeof(cm_id_priv->av.smac));
-
- if (alt_smac != NULL)
- memcpy(cm_id_priv->alt_av.smac, alt_smac,
- sizeof(cm_id_priv->alt_av.smac));
-
- return 0;
-}
-EXPORT_SYMBOL(ib_update_cm_av);
-
static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
{
struct cm_device *cm_dev;
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 199958d9ddc8..42c3058e6e9c 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -1284,15 +1284,6 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
struct rdma_id_private *listen_id, *conn_id;
struct rdma_cm_event event;
int offset, ret;
- u8 smac[ETH_ALEN];
- u8 alt_smac[ETH_ALEN];
- u8 *psmac = smac;
- u8 *palt_smac = alt_smac;
- int is_iboe = ((rdma_node_get_transport(cm_id->device->node_type) ==
- RDMA_TRANSPORT_IB) &&
- (rdma_port_get_link_layer(cm_id->device,
- ib_event->param.req_rcvd.port) ==
- IB_LINK_LAYER_ETHERNET));
listen_id = cm_id->context;
if (!cma_check_req_qp_type(&listen_id->id, ib_event))
@@ -1336,28 +1327,11 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
ret = conn_id->id.event_handler(&conn_id->id, &event);
if (ret)
goto err3;
-
- if (is_iboe) {
- if (ib_event->param.req_rcvd.primary_path != NULL)
- rdma_addr_find_smac_by_sgid(
- &ib_event->param.req_rcvd.primary_path->sgid,
- psmac, NULL);
- else
- psmac = NULL;
- if (ib_event->param.req_rcvd.alternate_path != NULL)
- rdma_addr_find_smac_by_sgid(
- &ib_event->param.req_rcvd.alternate_path->sgid,
- palt_smac, NULL);
- else
- palt_smac = NULL;
- }
/*
* Acquire mutex to prevent user executing rdma_destroy_id()
* while we're accessing the cm_id.
*/
mutex_lock(&lock);
- if (is_iboe)
- ib_update_cm_av(cm_id, psmac, palt_smac);
if (cma_comp(conn_id, RDMA_CM_CONNECT) &&
(conn_id->id.qp_type != IB_QPT_UD))
ib_send_cm_mra(cm_id, CMA_CM_MRA_SETTING, NULL, 0);
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 4c837e66516b..ab31f136d04b 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -1022,12 +1022,21 @@ int ib_send_mad(struct ib_mad_send_wr_private *mad_send_wr)
mad_send_wr->send_buf.mad,
sge[0].length,
DMA_TO_DEVICE);
+ if (unlikely(ib_dma_mapping_error(mad_agent->device, sge[0].addr)))
+ return -ENOMEM;
+
mad_send_wr->header_mapping = sge[0].addr;
sge[1].addr = ib_dma_map_single(mad_agent->device,
ib_get_payload(mad_send_wr),
sge[1].length,
DMA_TO_DEVICE);
+ if (unlikely(ib_dma_mapping_error(mad_agent->device, sge[1].addr))) {
+ ib_dma_unmap_single(mad_agent->device,
+ mad_send_wr->header_mapping,
+ sge[0].length, DMA_TO_DEVICE);
+ return -ENOMEM;
+ }
mad_send_wr->payload_mapping = sge[1].addr;
spin_lock_irqsave(&qp_info->send_queue.lock, flags);
@@ -2590,6 +2599,11 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
sizeof *mad_priv -
sizeof mad_priv->header,
DMA_FROM_DEVICE);
+ if (unlikely(ib_dma_mapping_error(qp_info->port_priv->device,
+ sg_list.addr))) {
+ ret = -ENOMEM;
+ break;
+ }
mad_priv->header.mapping = sg_list.addr;
recv_wr.wr_id = (unsigned long)&mad_priv->header.mad_list;
mad_priv->header.mad_list.mad_queue = recv_queue;
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index a84112322071..a3a2e9c1639b 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -42,29 +42,29 @@
#include "uverbs.h"
-#define IB_UMEM_MAX_PAGE_CHUNK \
- ((PAGE_SIZE - offsetof(struct ib_umem_chunk, page_list)) / \
- ((void *) &((struct ib_umem_chunk *) 0)->page_list[1] - \
- (void *) &((struct ib_umem_chunk *) 0)->page_list[0]))
static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int dirty)
{
- struct ib_umem_chunk *chunk, *tmp;
+ struct scatterlist *sg;
+ struct page *page;
int i;
- list_for_each_entry_safe(chunk, tmp, &umem->chunk_list, list) {
- ib_dma_unmap_sg(dev, chunk->page_list,
- chunk->nents, DMA_BIDIRECTIONAL);
- for (i = 0; i < chunk->nents; ++i) {
- struct page *page = sg_page(&chunk->page_list[i]);
+ if (umem->nmap > 0)
+ ib_dma_unmap_sg(dev, umem->sg_head.sgl,
+ umem->nmap,
+ DMA_BIDIRECTIONAL);
- if (umem->writable && dirty)
- set_page_dirty_lock(page);
- put_page(page);
- }
+ for_each_sg(umem->sg_head.sgl, sg, umem->npages, i) {
- kfree(chunk);
+ page = sg_page(sg);
+ if (umem->writable && dirty)
+ set_page_dirty_lock(page);
+ put_page(page);
}
+
+ sg_free_table(&umem->sg_head);
+ return;
+
}
/**
@@ -81,15 +81,15 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
struct ib_umem *umem;
struct page **page_list;
struct vm_area_struct **vma_list;
- struct ib_umem_chunk *chunk;
unsigned long locked;
unsigned long lock_limit;
unsigned long cur_base;
unsigned long npages;
int ret;
- int off;
int i;
DEFINE_DMA_ATTRS(attrs);
+ struct scatterlist *sg, *sg_list_start;
+ int need_release = 0;
if (dmasync)
dma_set_attr(DMA_ATTR_WRITE_BARRIER, &attrs);
@@ -97,7 +97,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
if (!can_do_mlock())
return ERR_PTR(-EPERM);
- umem = kmalloc(sizeof *umem, GFP_KERNEL);
+ umem = kzalloc(sizeof *umem, GFP_KERNEL);
if (!umem)
return ERR_PTR(-ENOMEM);
@@ -117,8 +117,6 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
/* We assume the memory is from hugetlb until proved otherwise */
umem->hugetlb = 1;
- INIT_LIST_HEAD(&umem->chunk_list);
-
page_list = (struct page **) __get_free_page(GFP_KERNEL);
if (!page_list) {
kfree(umem);
@@ -147,7 +145,18 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
cur_base = addr & PAGE_MASK;
- ret = 0;
+ if (npages == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = sg_alloc_table(&umem->sg_head, npages, GFP_KERNEL);
+ if (ret)
+ goto out;
+
+ need_release = 1;
+ sg_list_start = umem->sg_head.sgl;
+
while (npages) {
ret = get_user_pages(current, current->mm, cur_base,
min_t(unsigned long, npages,
@@ -157,54 +166,38 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
if (ret < 0)
goto out;
+ umem->npages += ret;
cur_base += ret * PAGE_SIZE;
npages -= ret;
- off = 0;
-
- while (ret) {
- chunk = kmalloc(sizeof *chunk + sizeof (struct scatterlist) *
- min_t(int, ret, IB_UMEM_MAX_PAGE_CHUNK),
- GFP_KERNEL);
- if (!chunk) {
- ret = -ENOMEM;
- goto out;
- }
-
- chunk->nents = min_t(int, ret, IB_UMEM_MAX_PAGE_CHUNK);
- sg_init_table(chunk->page_list, chunk->nents);
- for (i = 0; i < chunk->nents; ++i) {
- if (vma_list &&
- !is_vm_hugetlb_page(vma_list[i + off]))
- umem->hugetlb = 0;
- sg_set_page(&chunk->page_list[i], page_list[i + off], PAGE_SIZE, 0);
- }
-
- chunk->nmap = ib_dma_map_sg_attrs(context->device,
- &chunk->page_list[0],
- chunk->nents,
- DMA_BIDIRECTIONAL,
- &attrs);
- if (chunk->nmap <= 0) {
- for (i = 0; i < chunk->nents; ++i)
- put_page(sg_page(&chunk->page_list[i]));
- kfree(chunk);
-
- ret = -ENOMEM;
- goto out;
- }
-
- ret -= chunk->nents;
- off += chunk->nents;
- list_add_tail(&chunk->list, &umem->chunk_list);
+ for_each_sg(sg_list_start, sg, ret, i) {
+ if (vma_list && !is_vm_hugetlb_page(vma_list[i]))
+ umem->hugetlb = 0;
+
+ sg_set_page(sg, page_list[i], PAGE_SIZE, 0);
}
- ret = 0;
+ /* preparing for next loop */
+ sg_list_start = sg;
}
+ umem->nmap = ib_dma_map_sg_attrs(context->device,
+ umem->sg_head.sgl,
+ umem->npages,
+ DMA_BIDIRECTIONAL,
+ &attrs);
+
+ if (umem->nmap <= 0) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = 0;
+
out:
if (ret < 0) {
- __ib_umem_release(context->device, umem, 0);
+ if (need_release)
+ __ib_umem_release(context->device, umem, 0);
kfree(umem);
} else
current->mm->pinned_vm = locked;
@@ -278,17 +271,16 @@ EXPORT_SYMBOL(ib_umem_release);
int ib_umem_page_count(struct ib_umem *umem)
{
- struct ib_umem_chunk *chunk;
int shift;
int i;
int n;
+ struct scatterlist *sg;
shift = ilog2(umem->page_size);
n = 0;
- list_for_each_entry(chunk, &umem->chunk_list, list)
- for (i = 0; i < chunk->nmap; ++i)
- n += sg_dma_len(&chunk->page_list[i]) >> shift;
+ for_each_sg(umem->sg_head.sgl, sg, umem->nmap, i)
+ n += sg_dma_len(sg) >> shift;
return n;
}
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index 3ac795115438..92525f855d82 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -1169,6 +1169,45 @@ int ib_dereg_mr(struct ib_mr *mr)
}
EXPORT_SYMBOL(ib_dereg_mr);
+struct ib_mr *ib_create_mr(struct ib_pd *pd,
+ struct ib_mr_init_attr *mr_init_attr)
+{
+ struct ib_mr *mr;
+
+ if (!pd->device->create_mr)
+ return ERR_PTR(-ENOSYS);
+
+ mr = pd->device->create_mr(pd, mr_init_attr);
+
+ if (!IS_ERR(mr)) {
+ mr->device = pd->device;
+ mr->pd = pd;
+ mr->uobject = NULL;
+ atomic_inc(&pd->usecnt);
+ atomic_set(&mr->usecnt, 0);
+ }
+
+ return mr;
+}
+EXPORT_SYMBOL(ib_create_mr);
+
+int ib_destroy_mr(struct ib_mr *mr)
+{
+ struct ib_pd *pd;
+ int ret;
+
+ if (atomic_read(&mr->usecnt))
+ return -EBUSY;
+
+ pd = mr->pd;
+ ret = mr->device->destroy_mr(mr);
+ if (!ret)
+ atomic_dec(&pd->usecnt);
+
+ return ret;
+}
+EXPORT_SYMBOL(ib_destroy_mr);
+
struct ib_mr *ib_alloc_fast_reg_mr(struct ib_pd *pd, int max_page_list_len)
{
struct ib_mr *mr;
@@ -1398,3 +1437,11 @@ int ib_destroy_flow(struct ib_flow *flow_id)
return err;
}
EXPORT_SYMBOL(ib_destroy_flow);
+
+int ib_check_mr_status(struct ib_mr *mr, u32 check_mask,
+ struct ib_mr_status *mr_status)
+{
+ return mr->device->check_mr_status ?
+ mr->device->check_mr_status(mr, check_mask, mr_status) : -ENOSYS;
+}
+EXPORT_SYMBOL(ib_check_mr_status);
diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c
index 07eb3a8067d8..8af33cf1fc4e 100644
--- a/drivers/infiniband/hw/amso1100/c2_provider.c
+++ b/drivers/infiniband/hw/amso1100/c2_provider.c
@@ -431,9 +431,9 @@ static struct ib_mr *c2_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u64 *pages;
u64 kva = 0;
int shift, n, len;
- int i, j, k;
+ int i, k, entry;
int err = 0;
- struct ib_umem_chunk *chunk;
+ struct scatterlist *sg;
struct c2_pd *c2pd = to_c2pd(pd);
struct c2_mr *c2mr;
@@ -452,10 +452,7 @@ static struct ib_mr *c2_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
}
shift = ffs(c2mr->umem->page_size) - 1;
-
- n = 0;
- list_for_each_entry(chunk, &c2mr->umem->chunk_list, list)
- n += chunk->nents;
+ n = c2mr->umem->nmap;
pages = kmalloc(n * sizeof(u64), GFP_KERNEL);
if (!pages) {
@@ -464,14 +461,12 @@ static struct ib_mr *c2_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
}
i = 0;
- list_for_each_entry(chunk, &c2mr->umem->chunk_list, list) {
- for (j = 0; j < chunk->nmap; ++j) {
- len = sg_dma_len(&chunk->page_list[j]) >> shift;
- for (k = 0; k < len; ++k) {
- pages[i++] =
- sg_dma_address(&chunk->page_list[j]) +
- (c2mr->umem->page_size * k);
- }
+ for_each_sg(c2mr->umem->sg_head.sgl, sg, c2mr->umem->nmap, entry) {
+ len = sg_dma_len(sg) >> shift;
+ for (k = 0; k < len; ++k) {
+ pages[i++] =
+ sg_dma_address(sg) +
+ (c2mr->umem->page_size * k);
}
}
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index d2283837d451..811b24a539c0 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -618,14 +618,13 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
{
__be64 *pages;
int shift, n, len;
- int i, j, k;
+ int i, k, entry;
int err = 0;
- struct ib_umem_chunk *chunk;
struct iwch_dev *rhp;
struct iwch_pd *php;
struct iwch_mr *mhp;
struct iwch_reg_user_mr_resp uresp;
-
+ struct scatterlist *sg;
PDBG("%s ib_pd %p\n", __func__, pd);
php = to_iwch_pd(pd);
@@ -645,9 +644,7 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
shift = ffs(mhp->umem->page_size) - 1;
- n = 0;
- list_for_each_entry(chunk, &mhp->umem->chunk_list, list)
- n += chunk->nents;
+ n = mhp->umem->nmap;
err = iwch_alloc_pbl(mhp, n);
if (err)
@@ -661,12 +658,10 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
i = n = 0;
- list_for_each_entry(chunk, &mhp->umem->chunk_list, list)
- for (j = 0; j < chunk->nmap; ++j) {
- len = sg_dma_len(&chunk->page_list[j]) >> shift;
+ for_each_sg(mhp->umem->sg_head.sgl, sg, mhp->umem->nmap, entry) {
+ len = sg_dma_len(sg) >> shift;
for (k = 0; k < len; ++k) {
- pages[i++] = cpu_to_be64(sg_dma_address(
- &chunk->page_list[j]) +
+ pages[i++] = cpu_to_be64(sg_dma_address(sg) +
mhp->umem->page_size * k);
if (i == PAGE_SIZE / sizeof *pages) {
err = iwch_write_pbl(mhp, pages, i, n);
@@ -676,7 +671,7 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
i = 0;
}
}
- }
+ }
if (i)
err = iwch_write_pbl(mhp, pages, i, n);
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index d286bdebe2ab..02436d5d0dab 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -98,9 +98,9 @@ int c4iw_debug;
module_param(c4iw_debug, int, 0644);
MODULE_PARM_DESC(c4iw_debug, "Enable debug logging (default=0)");
-static int peer2peer;
+static int peer2peer = 1;
module_param(peer2peer, int, 0644);
-MODULE_PARM_DESC(peer2peer, "Support peer2peer ULPs (default=0)");
+MODULE_PARM_DESC(peer2peer, "Support peer2peer ULPs (default=1)");
static int p2p_type = FW_RI_INIT_P2PTYPE_READ_REQ;
module_param(p2p_type, int, 0644);
@@ -400,7 +400,8 @@ static struct dst_entry *find_route(struct c4iw_dev *dev, __be32 local_ip,
n = dst_neigh_lookup(&rt->dst, &peer_ip);
if (!n)
return NULL;
- if (!our_interface(dev, n->dev)) {
+ if (!our_interface(dev, n->dev) &&
+ !(n->dev->flags & IFF_LOOPBACK)) {
dst_release(&rt->dst);
return NULL;
}
@@ -759,8 +760,9 @@ static void send_mpa_req(struct c4iw_ep *ep, struct sk_buff *skb,
ep->mpa_skb = skb;
c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t);
start_ep_timer(ep);
- state_set(&ep->com, MPA_REQ_SENT);
+ __state_set(&ep->com, MPA_REQ_SENT);
ep->mpa_attr.initiator = 1;
+ ep->snd_seq += mpalen;
return;
}
@@ -840,6 +842,7 @@ static int send_mpa_reject(struct c4iw_ep *ep, const void *pdata, u8 plen)
t4_set_arp_err_handler(skb, NULL, arp_failure_discard);
BUG_ON(ep->mpa_skb);
ep->mpa_skb = skb;
+ ep->snd_seq += mpalen;
return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t);
}
@@ -923,7 +926,8 @@ static int send_mpa_reply(struct c4iw_ep *ep, const void *pdata, u8 plen)
skb_get(skb);
t4_set_arp_err_handler(skb, NULL, arp_failure_discard);
ep->mpa_skb = skb;
- state_set(&ep->com, MPA_REP_SENT);
+ __state_set(&ep->com, MPA_REP_SENT);
+ ep->snd_seq += mpalen;
return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t);
}
@@ -940,6 +944,7 @@ static int act_establish(struct c4iw_dev *dev, struct sk_buff *skb)
PDBG("%s ep %p tid %u snd_isn %u rcv_isn %u\n", __func__, ep, tid,
be32_to_cpu(req->snd_isn), be32_to_cpu(req->rcv_isn));
+ mutex_lock(&ep->com.mutex);
dst_confirm(ep->dst);
/* setup the hwtid for this connection */
@@ -963,17 +968,18 @@ static int act_establish(struct c4iw_dev *dev, struct sk_buff *skb)
send_mpa_req(ep, skb, 1);
else
send_mpa_req(ep, skb, mpa_rev);
-
+ mutex_unlock(&ep->com.mutex);
return 0;
}
-static void close_complete_upcall(struct c4iw_ep *ep)
+static void close_complete_upcall(struct c4iw_ep *ep, int status)
{
struct iw_cm_event event;
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
memset(&event, 0, sizeof(event));
event.event = IW_CM_EVENT_CLOSE;
+ event.status = status;
if (ep->com.cm_id) {
PDBG("close complete delivered ep %p cm_id %p tid %u\n",
ep, ep->com.cm_id, ep->hwtid);
@@ -987,7 +993,6 @@ static void close_complete_upcall(struct c4iw_ep *ep)
static int abort_connection(struct c4iw_ep *ep, struct sk_buff *skb, gfp_t gfp)
{
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
- close_complete_upcall(ep);
state_set(&ep->com, ABORTING);
set_bit(ABORT_CONN, &ep->com.history);
return send_abort(ep, skb, gfp);
@@ -1066,9 +1071,10 @@ static void connect_reply_upcall(struct c4iw_ep *ep, int status)
}
}
-static void connect_request_upcall(struct c4iw_ep *ep)
+static int connect_request_upcall(struct c4iw_ep *ep)
{
struct iw_cm_event event;
+ int ret;
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
memset(&event, 0, sizeof(event));
@@ -1093,15 +1099,14 @@ static void connect_request_upcall(struct c4iw_ep *ep)
event.private_data_len = ep->plen;
event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
}
- if (state_read(&ep->parent_ep->com) != DEAD) {
- c4iw_get_ep(&ep->com);
- ep->parent_ep->com.cm_id->event_handler(
- ep->parent_ep->com.cm_id,
- &event);
- }
+ c4iw_get_ep(&ep->com);
+ ret = ep->parent_ep->com.cm_id->event_handler(ep->parent_ep->com.cm_id,
+ &event);
+ if (ret)
+ c4iw_put_ep(&ep->com);
set_bit(CONNREQ_UPCALL, &ep->com.history);
c4iw_put_ep(&ep->parent_ep->com);
- ep->parent_ep = NULL;
+ return ret;
}
static void established_upcall(struct c4iw_ep *ep)
@@ -1165,7 +1170,7 @@ static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb)
* the connection.
*/
stop_ep_timer(ep);
- if (state_read(&ep->com) != MPA_REQ_SENT)
+ if (ep->com.state != MPA_REQ_SENT)
return;
/*
@@ -1240,7 +1245,7 @@ static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb)
* start reply message including private data. And
* the MPA header is valid.
*/
- state_set(&ep->com, FPDU_MODE);
+ __state_set(&ep->com, FPDU_MODE);
ep->mpa_attr.crc_enabled = (mpa->flags & MPA_CRC) | crc_enabled ? 1 : 0;
ep->mpa_attr.recv_marker_enabled = markers_enabled;
ep->mpa_attr.xmit_marker_enabled = mpa->flags & MPA_MARKERS ? 1 : 0;
@@ -1355,7 +1360,7 @@ static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb)
}
goto out;
err:
- state_set(&ep->com, ABORTING);
+ __state_set(&ep->com, ABORTING);
send_abort(ep, skb, GFP_KERNEL);
out:
connect_reply_upcall(ep, err);
@@ -1370,7 +1375,7 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
- if (state_read(&ep->com) != MPA_REQ_WAIT)
+ if (ep->com.state != MPA_REQ_WAIT)
return;
/*
@@ -1400,7 +1405,6 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
return;
PDBG("%s enter (%s line %u)\n", __func__, __FILE__, __LINE__);
- stop_ep_timer(ep);
mpa = (struct mpa_message *) ep->mpa_pkt;
/*
@@ -1492,10 +1496,18 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
ep->mpa_attr.xmit_marker_enabled, ep->mpa_attr.version,
ep->mpa_attr.p2p_type);
- state_set(&ep->com, MPA_REQ_RCVD);
+ __state_set(&ep->com, MPA_REQ_RCVD);
+ stop_ep_timer(ep);
/* drive upcall */
- connect_request_upcall(ep);
+ mutex_lock(&ep->parent_ep->com.mutex);
+ if (ep->parent_ep->com.state != DEAD) {
+ if (connect_request_upcall(ep))
+ abort_connection(ep, skb, GFP_KERNEL);
+ } else {
+ abort_connection(ep, skb, GFP_KERNEL);
+ }
+ mutex_unlock(&ep->parent_ep->com.mutex);
return;
}
@@ -1509,14 +1521,17 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb)
__u8 status = hdr->status;
ep = lookup_tid(t, tid);
+ if (!ep)
+ return 0;
PDBG("%s ep %p tid %u dlen %u\n", __func__, ep, ep->hwtid, dlen);
skb_pull(skb, sizeof(*hdr));
skb_trim(skb, dlen);
+ mutex_lock(&ep->com.mutex);
/* update RX credits */
update_rx_credits(ep, dlen);
- switch (state_read(&ep->com)) {
+ switch (ep->com.state) {
case MPA_REQ_SENT:
ep->rcv_seq += dlen;
process_mpa_reply(ep, skb);
@@ -1532,7 +1547,7 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb)
pr_err("%s Unexpected streaming data." \
" qpid %u ep %p state %d tid %u status %d\n",
__func__, ep->com.qp->wq.sq.qid, ep,
- state_read(&ep->com), ep->hwtid, status);
+ ep->com.state, ep->hwtid, status);
attrs.next_state = C4IW_QP_STATE_TERMINATE;
c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
C4IW_QP_ATTR_NEXT_STATE, &attrs, 0);
@@ -1541,6 +1556,7 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb)
default:
break;
}
+ mutex_unlock(&ep->com.mutex);
return 0;
}
@@ -1647,6 +1663,15 @@ static inline int act_open_has_tid(int status)
status != CPL_ERR_ARP_MISS;
}
+/* Returns whether a CPL status conveys negative advice.
+ */
+static int is_neg_adv(unsigned int status)
+{
+ return status == CPL_ERR_RTX_NEG_ADVICE ||
+ status == CPL_ERR_PERSIST_NEG_ADVICE ||
+ status == CPL_ERR_KEEPALV_NEG_ADVICE;
+}
+
#define ACT_OPEN_RETRY_COUNT 2
static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
@@ -1835,7 +1860,7 @@ static int act_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
PDBG("%s ep %p atid %u status %u errno %d\n", __func__, ep, atid,
status, status2errno(status));
- if (status == CPL_ERR_RTX_NEG_ADVICE) {
+ if (is_neg_adv(status)) {
printk(KERN_WARNING MOD "Connection problems for atid %u\n",
atid);
return 0;
@@ -2246,7 +2271,7 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
}
- close_complete_upcall(ep);
+ close_complete_upcall(ep, 0);
__state_set(&ep->com, DEAD);
release = 1;
disconnect = 0;
@@ -2265,15 +2290,6 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
return 0;
}
-/*
- * Returns whether an ABORT_REQ_RSS message is a negative advice.
- */
-static int is_neg_adv_abort(unsigned int status)
-{
- return status == CPL_ERR_RTX_NEG_ADVICE ||
- status == CPL_ERR_PERSIST_NEG_ADVICE;
-}
-
static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
{
struct cpl_abort_req_rss *req = cplhdr(skb);
@@ -2287,7 +2303,7 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
unsigned int tid = GET_TID(req);
ep = lookup_tid(t, tid);
- if (is_neg_adv_abort(req->status)) {
+ if (is_neg_adv(req->status)) {
PDBG("%s neg_adv_abort ep %p tid %u\n", __func__, ep,
ep->hwtid);
return 0;
@@ -2425,7 +2441,7 @@ static int close_con_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
C4IW_QP_ATTR_NEXT_STATE,
&attrs, 1);
}
- close_complete_upcall(ep);
+ close_complete_upcall(ep, 0);
__state_set(&ep->com, DEAD);
release = 1;
break;
@@ -2500,22 +2516,28 @@ static int fw4_ack(struct c4iw_dev *dev, struct sk_buff *skb)
int c4iw_reject_cr(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len)
{
- int err;
+ int err = 0;
+ int disconnect = 0;
struct c4iw_ep *ep = to_ep(cm_id);
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
- if (state_read(&ep->com) == DEAD) {
+ mutex_lock(&ep->com.mutex);
+ if (ep->com.state == DEAD) {
+ mutex_unlock(&ep->com.mutex);
c4iw_put_ep(&ep->com);
return -ECONNRESET;
}
set_bit(ULP_REJECT, &ep->com.history);
- BUG_ON(state_read(&ep->com) != MPA_REQ_RCVD);
+ BUG_ON(ep->com.state != MPA_REQ_RCVD);
if (mpa_rev == 0)
abort_connection(ep, NULL, GFP_KERNEL);
else {
err = send_mpa_reject(ep, pdata, pdata_len);
- err = c4iw_ep_disconnect(ep, 0, GFP_KERNEL);
+ disconnect = 1;
}
+ mutex_unlock(&ep->com.mutex);
+ if (disconnect)
+ err = c4iw_ep_disconnect(ep, 0, GFP_KERNEL);
c4iw_put_ep(&ep->com);
return 0;
}
@@ -2530,12 +2552,14 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
struct c4iw_qp *qp = get_qhp(h, conn_param->qpn);
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
- if (state_read(&ep->com) == DEAD) {
+
+ mutex_lock(&ep->com.mutex);
+ if (ep->com.state == DEAD) {
err = -ECONNRESET;
goto err;
}
- BUG_ON(state_read(&ep->com) != MPA_REQ_RCVD);
+ BUG_ON(ep->com.state != MPA_REQ_RCVD);
BUG_ON(!qp);
set_bit(ULP_ACCEPT, &ep->com.history);
@@ -2604,14 +2628,16 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
if (err)
goto err1;
- state_set(&ep->com, FPDU_MODE);
+ __state_set(&ep->com, FPDU_MODE);
established_upcall(ep);
+ mutex_unlock(&ep->com.mutex);
c4iw_put_ep(&ep->com);
return 0;
err1:
ep->com.cm_id = NULL;
cm_id->rem_ref(cm_id);
err:
+ mutex_unlock(&ep->com.mutex);
c4iw_put_ep(&ep->com);
return err;
}
@@ -2980,7 +3006,7 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp)
rdev = &ep->com.dev->rdev;
if (c4iw_fatal_error(rdev)) {
fatal = 1;
- close_complete_upcall(ep);
+ close_complete_upcall(ep, -EIO);
ep->com.state = DEAD;
}
switch (ep->com.state) {
@@ -3022,7 +3048,7 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp)
if (close) {
if (abrupt) {
set_bit(EP_DISC_ABORT, &ep->com.history);
- close_complete_upcall(ep);
+ close_complete_upcall(ep, -ECONNRESET);
ret = send_abort(ep, NULL, gfp);
} else {
set_bit(EP_DISC_CLOSE, &ep->com.history);
@@ -3203,6 +3229,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb,
struct sk_buff *req_skb;
struct fw_ofld_connection_wr *req;
struct cpl_pass_accept_req *cpl = cplhdr(skb);
+ int ret;
req_skb = alloc_skb(sizeof(struct fw_ofld_connection_wr), GFP_KERNEL);
req = (struct fw_ofld_connection_wr *)__skb_put(req_skb, sizeof(*req));
@@ -3239,7 +3266,13 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb,
req->cookie = (unsigned long)skb;
set_wr_txq(req_skb, CPL_PRIORITY_CONTROL, port_id);
- cxgb4_ofld_send(dev->rdev.lldi.ports[0], req_skb);
+ ret = cxgb4_ofld_send(dev->rdev.lldi.ports[0], req_skb);
+ if (ret < 0) {
+ pr_err("%s - cxgb4_ofld_send error %d - dropping\n", __func__,
+ ret);
+ kfree_skb(skb);
+ kfree_skb(req_skb);
+ }
}
/*
@@ -3346,13 +3379,13 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb)
pi = (struct port_info *)netdev_priv(pdev);
tx_chan = cxgb4_port_chan(pdev);
}
+ neigh_release(neigh);
if (!e) {
pr_err("%s - failed to allocate l2t entry!\n",
__func__);
goto free_dst;
}
- neigh_release(neigh);
step = dev->rdev.lldi.nrxq / dev->rdev.lldi.nchan;
rss_qid = dev->rdev.lldi.rxq_ids[pi->port_id * step];
window = (__force u16) htons((__force u16)tcph->window);
@@ -3427,6 +3460,7 @@ static void process_timeout(struct c4iw_ep *ep)
&attrs, 1);
}
__state_set(&ep->com, ABORTING);
+ close_complete_upcall(ep, -ETIMEDOUT);
break;
default:
WARN(1, "%s unexpected state ep %p tid %u state %u\n",
@@ -3570,7 +3604,7 @@ static int peer_abort_intr(struct c4iw_dev *dev, struct sk_buff *skb)
kfree_skb(skb);
return 0;
}
- if (is_neg_adv_abort(req->status)) {
+ if (is_neg_adv(req->status)) {
PDBG("%s neg_adv_abort ep %p tid %u\n", __func__, ep,
ep->hwtid);
kfree_skb(skb);
diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c
index 88de3aa9c5b0..ce468e542428 100644
--- a/drivers/infiniband/hw/cxgb4/cq.c
+++ b/drivers/infiniband/hw/cxgb4/cq.c
@@ -365,8 +365,14 @@ void c4iw_flush_hw_cq(struct c4iw_cq *chp)
if (CQE_OPCODE(hw_cqe) == FW_RI_READ_RESP) {
- /*
- * drop peer2peer RTR reads.
+ /* If we have reached here because of async
+ * event or other error, and have egress error
+ * then drop
+ */
+ if (CQE_TYPE(hw_cqe) == 1)
+ goto next_cqe;
+
+ /* drop peer2peer RTR reads.
*/
if (CQE_WRID_STAG(hw_cqe) == 1)
goto next_cqe;
@@ -511,8 +517,18 @@ static int poll_cq(struct t4_wq *wq, struct t4_cq *cq, struct t4_cqe *cqe,
*/
if (RQ_TYPE(hw_cqe) && (CQE_OPCODE(hw_cqe) == FW_RI_READ_RESP)) {
- /*
- * If this is an unsolicited read response, then the read
+ /* If we have reached here because of async
+ * event or other error, and have egress error
+ * then drop
+ */
+ if (CQE_TYPE(hw_cqe) == 1) {
+ if (CQE_STATUS(hw_cqe))
+ t4_set_wq_in_error(wq);
+ ret = -EAGAIN;
+ goto skip_cqe;
+ }
+
+ /* If this is an unsolicited read response, then the read
* was generated by the kernel driver as part of peer-2-peer
* connection setup. So ignore the completion.
*/
@@ -603,7 +619,7 @@ proc_cqe:
*/
if (SQ_TYPE(hw_cqe)) {
int idx = CQE_WRID_SQ_IDX(hw_cqe);
- BUG_ON(idx > wq->sq.size);
+ BUG_ON(idx >= wq->sq.size);
/*
* Account for any unsignaled completions completed by
@@ -617,7 +633,7 @@ proc_cqe:
wq->sq.in_use -= wq->sq.size + idx - wq->sq.cidx;
else
wq->sq.in_use -= idx - wq->sq.cidx;
- BUG_ON(wq->sq.in_use < 0 && wq->sq.in_use < wq->sq.size);
+ BUG_ON(wq->sq.in_use <= 0 && wq->sq.in_use >= wq->sq.size);
wq->sq.cidx = (uint16_t)idx;
PDBG("%s completing sq idx %u\n", __func__, wq->sq.cidx);
@@ -881,7 +897,7 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
/*
* Make actual HW queue 2x to avoid cdix_inc overflows.
*/
- hwentries = entries * 2;
+ hwentries = min(entries * 2, T4_MAX_IQ_SIZE);
/*
* Make HW queue at least 64 entries so GTS updates aren't too
@@ -930,6 +946,7 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
if (!mm2)
goto err4;
+ memset(&uresp, 0, sizeof(uresp));
uresp.qid_mask = rhp->rdev.cqmask;
uresp.cqid = chp->cq.cqid;
uresp.size = chp->cq.size;
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index 4a033853312e..9489a388376c 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -64,6 +64,10 @@ struct uld_ctx {
static LIST_HEAD(uld_ctx_list);
static DEFINE_MUTEX(dev_mutex);
+#define DB_FC_RESUME_SIZE 64
+#define DB_FC_RESUME_DELAY 1
+#define DB_FC_DRAIN_THRESH 0
+
static struct dentry *c4iw_debugfs_root;
struct c4iw_debugfs_data {
@@ -282,7 +286,7 @@ static const struct file_operations stag_debugfs_fops = {
.llseek = default_llseek,
};
-static char *db_state_str[] = {"NORMAL", "FLOW_CONTROL", "RECOVERY"};
+static char *db_state_str[] = {"NORMAL", "FLOW_CONTROL", "RECOVERY", "STOPPED"};
static int stats_show(struct seq_file *seq, void *v)
{
@@ -311,9 +315,10 @@ static int stats_show(struct seq_file *seq, void *v)
seq_printf(seq, " DB FULL: %10llu\n", dev->rdev.stats.db_full);
seq_printf(seq, " DB EMPTY: %10llu\n", dev->rdev.stats.db_empty);
seq_printf(seq, " DB DROP: %10llu\n", dev->rdev.stats.db_drop);
- seq_printf(seq, " DB State: %s Transitions %llu\n",
+ seq_printf(seq, " DB State: %s Transitions %llu FC Interruptions %llu\n",
db_state_str[dev->db_state],
- dev->rdev.stats.db_state_transitions);
+ dev->rdev.stats.db_state_transitions,
+ dev->rdev.stats.db_fc_interruptions);
seq_printf(seq, "TCAM_FULL: %10llu\n", dev->rdev.stats.tcam_full);
seq_printf(seq, "ACT_OFLD_CONN_FAILS: %10llu\n",
dev->rdev.stats.act_ofld_conn_fails);
@@ -643,6 +648,12 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev)
printk(KERN_ERR MOD "error %d initializing ocqp pool\n", err);
goto err4;
}
+ rdev->status_page = (struct t4_dev_status_page *)
+ __get_free_page(GFP_KERNEL);
+ if (!rdev->status_page) {
+ pr_err(MOD "error allocating status page\n");
+ goto err4;
+ }
return 0;
err4:
c4iw_rqtpool_destroy(rdev);
@@ -656,6 +667,7 @@ err1:
static void c4iw_rdev_close(struct c4iw_rdev *rdev)
{
+ free_page((unsigned long)rdev->status_page);
c4iw_pblpool_destroy(rdev);
c4iw_rqtpool_destroy(rdev);
c4iw_destroy_resource(&rdev->resource);
@@ -703,18 +715,6 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
pr_info("%s: On-Chip Queues not supported on this device.\n",
pci_name(infop->pdev));
- if (!is_t4(infop->adapter_type)) {
- if (!allow_db_fc_on_t5) {
- db_fc_threshold = 100000;
- pr_info("DB Flow Control Disabled.\n");
- }
-
- if (!allow_db_coalescing_on_t5) {
- db_coalescing_threshold = -1;
- pr_info("DB Coalescing Disabled.\n");
- }
- }
-
devp = (struct c4iw_dev *)ib_alloc_device(sizeof(*devp));
if (!devp) {
printk(KERN_ERR MOD "Cannot allocate ib device\n");
@@ -749,6 +749,7 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
spin_lock_init(&devp->lock);
mutex_init(&devp->rdev.stats.lock);
mutex_init(&devp->db_mutex);
+ INIT_LIST_HEAD(&devp->db_fc_list);
if (c4iw_debugfs_root) {
devp->debugfs_root = debugfs_create_dir(
@@ -897,11 +898,13 @@ static int c4iw_uld_rx_handler(void *handle, const __be64 *rsp,
}
opcode = *(u8 *)rsp;
- if (c4iw_handlers[opcode])
+ if (c4iw_handlers[opcode]) {
c4iw_handlers[opcode](dev, skb);
- else
+ } else {
pr_info("%s no handler opcode 0x%x...\n", __func__,
opcode);
+ kfree_skb(skb);
+ }
return 0;
nomem:
@@ -977,13 +980,16 @@ static int disable_qp_db(int id, void *p, void *data)
static void stop_queues(struct uld_ctx *ctx)
{
- spin_lock_irq(&ctx->dev->lock);
- if (ctx->dev->db_state == NORMAL) {
- ctx->dev->rdev.stats.db_state_transitions++;
- ctx->dev->db_state = FLOW_CONTROL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->dev->lock, flags);
+ ctx->dev->rdev.stats.db_state_transitions++;
+ ctx->dev->db_state = STOPPED;
+ if (ctx->dev->rdev.flags & T4_STATUS_PAGE_DISABLED)
idr_for_each(&ctx->dev->qpidr, disable_qp_db, NULL);
- }
- spin_unlock_irq(&ctx->dev->lock);
+ else
+ ctx->dev->rdev.status_page->db_off = 1;
+ spin_unlock_irqrestore(&ctx->dev->lock, flags);
}
static int enable_qp_db(int id, void *p, void *data)
@@ -994,15 +1000,70 @@ static int enable_qp_db(int id, void *p, void *data)
return 0;
}
+static void resume_rc_qp(struct c4iw_qp *qp)
+{
+ spin_lock(&qp->lock);
+ t4_ring_sq_db(&qp->wq, qp->wq.sq.wq_pidx_inc);
+ qp->wq.sq.wq_pidx_inc = 0;
+ t4_ring_rq_db(&qp->wq, qp->wq.rq.wq_pidx_inc);
+ qp->wq.rq.wq_pidx_inc = 0;
+ spin_unlock(&qp->lock);
+}
+
+static void resume_a_chunk(struct uld_ctx *ctx)
+{
+ int i;
+ struct c4iw_qp *qp;
+
+ for (i = 0; i < DB_FC_RESUME_SIZE; i++) {
+ qp = list_first_entry(&ctx->dev->db_fc_list, struct c4iw_qp,
+ db_fc_entry);
+ list_del_init(&qp->db_fc_entry);
+ resume_rc_qp(qp);
+ if (list_empty(&ctx->dev->db_fc_list))
+ break;
+ }
+}
+
static void resume_queues(struct uld_ctx *ctx)
{
spin_lock_irq(&ctx->dev->lock);
- if (ctx->dev->qpcnt <= db_fc_threshold &&
- ctx->dev->db_state == FLOW_CONTROL) {
- ctx->dev->db_state = NORMAL;
- ctx->dev->rdev.stats.db_state_transitions++;
- idr_for_each(&ctx->dev->qpidr, enable_qp_db, NULL);
+ if (ctx->dev->db_state != STOPPED)
+ goto out;
+ ctx->dev->db_state = FLOW_CONTROL;
+ while (1) {
+ if (list_empty(&ctx->dev->db_fc_list)) {
+ WARN_ON(ctx->dev->db_state != FLOW_CONTROL);
+ ctx->dev->db_state = NORMAL;
+ ctx->dev->rdev.stats.db_state_transitions++;
+ if (ctx->dev->rdev.flags & T4_STATUS_PAGE_DISABLED) {
+ idr_for_each(&ctx->dev->qpidr, enable_qp_db,
+ NULL);
+ } else {
+ ctx->dev->rdev.status_page->db_off = 0;
+ }
+ break;
+ } else {
+ if (cxgb4_dbfifo_count(ctx->dev->rdev.lldi.ports[0], 1)
+ < (ctx->dev->rdev.lldi.dbfifo_int_thresh <<
+ DB_FC_DRAIN_THRESH)) {
+ resume_a_chunk(ctx);
+ }
+ if (!list_empty(&ctx->dev->db_fc_list)) {
+ spin_unlock_irq(&ctx->dev->lock);
+ if (DB_FC_RESUME_DELAY) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(DB_FC_RESUME_DELAY);
+ }
+ spin_lock_irq(&ctx->dev->lock);
+ if (ctx->dev->db_state != FLOW_CONTROL)
+ break;
+ }
+ }
}
+out:
+ if (ctx->dev->db_state != NORMAL)
+ ctx->dev->rdev.stats.db_fc_interruptions++;
spin_unlock_irq(&ctx->dev->lock);
}
@@ -1028,12 +1089,12 @@ static int count_qps(int id, void *p, void *data)
return 0;
}
-static void deref_qps(struct qp_list qp_list)
+static void deref_qps(struct qp_list *qp_list)
{
int idx;
- for (idx = 0; idx < qp_list.idx; idx++)
- c4iw_qp_rem_ref(&qp_list.qps[idx]->ibqp);
+ for (idx = 0; idx < qp_list->idx; idx++)
+ c4iw_qp_rem_ref(&qp_list->qps[idx]->ibqp);
}
static void recover_lost_dbs(struct uld_ctx *ctx, struct qp_list *qp_list)
@@ -1044,17 +1105,22 @@ static void recover_lost_dbs(struct uld_ctx *ctx, struct qp_list *qp_list)
for (idx = 0; idx < qp_list->idx; idx++) {
struct c4iw_qp *qp = qp_list->qps[idx];
+ spin_lock_irq(&qp->rhp->lock);
+ spin_lock(&qp->lock);
ret = cxgb4_sync_txq_pidx(qp->rhp->rdev.lldi.ports[0],
qp->wq.sq.qid,
t4_sq_host_wq_pidx(&qp->wq),
t4_sq_wq_size(&qp->wq));
if (ret) {
- printk(KERN_ERR MOD "%s: Fatal error - "
+ pr_err(KERN_ERR MOD "%s: Fatal error - "
"DB overflow recovery failed - "
"error syncing SQ qid %u\n",
pci_name(ctx->lldi.pdev), qp->wq.sq.qid);
+ spin_unlock(&qp->lock);
+ spin_unlock_irq(&qp->rhp->lock);
return;
}
+ qp->wq.sq.wq_pidx_inc = 0;
ret = cxgb4_sync_txq_pidx(qp->rhp->rdev.lldi.ports[0],
qp->wq.rq.qid,
@@ -1062,12 +1128,17 @@ static void recover_lost_dbs(struct uld_ctx *ctx, struct qp_list *qp_list)
t4_rq_wq_size(&qp->wq));
if (ret) {
- printk(KERN_ERR MOD "%s: Fatal error - "
+ pr_err(KERN_ERR MOD "%s: Fatal error - "
"DB overflow recovery failed - "
"error syncing RQ qid %u\n",
pci_name(ctx->lldi.pdev), qp->wq.rq.qid);
+ spin_unlock(&qp->lock);
+ spin_unlock_irq(&qp->rhp->lock);
return;
}
+ qp->wq.rq.wq_pidx_inc = 0;
+ spin_unlock(&qp->lock);
+ spin_unlock_irq(&qp->rhp->lock);
/* Wait for the dbfifo to drain */
while (cxgb4_dbfifo_count(qp->rhp->rdev.lldi.ports[0], 1) > 0) {
@@ -1083,36 +1154,22 @@ static void recover_queues(struct uld_ctx *ctx)
struct qp_list qp_list;
int ret;
- /* lock out kernel db ringers */
- mutex_lock(&ctx->dev->db_mutex);
-
- /* put all queues in to recovery mode */
- spin_lock_irq(&ctx->dev->lock);
- ctx->dev->db_state = RECOVERY;
- ctx->dev->rdev.stats.db_state_transitions++;
- idr_for_each(&ctx->dev->qpidr, disable_qp_db, NULL);
- spin_unlock_irq(&ctx->dev->lock);
-
/* slow everybody down */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(usecs_to_jiffies(1000));
- /* Wait for the dbfifo to completely drain. */
- while (cxgb4_dbfifo_count(ctx->dev->rdev.lldi.ports[0], 1) > 0) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(usecs_to_jiffies(10));
- }
-
/* flush the SGE contexts */
ret = cxgb4_flush_eq_cache(ctx->dev->rdev.lldi.ports[0]);
if (ret) {
printk(KERN_ERR MOD "%s: Fatal error - DB overflow recovery failed\n",
pci_name(ctx->lldi.pdev));
- goto out;
+ return;
}
/* Count active queues so we can build a list of queues to recover */
spin_lock_irq(&ctx->dev->lock);
+ WARN_ON(ctx->dev->db_state != STOPPED);
+ ctx->dev->db_state = RECOVERY;
idr_for_each(&ctx->dev->qpidr, count_qps, &count);
qp_list.qps = kzalloc(count * sizeof *qp_list.qps, GFP_ATOMIC);
@@ -1120,7 +1177,7 @@ static void recover_queues(struct uld_ctx *ctx)
printk(KERN_ERR MOD "%s: Fatal error - DB overflow recovery failed\n",
pci_name(ctx->lldi.pdev));
spin_unlock_irq(&ctx->dev->lock);
- goto out;
+ return;
}
qp_list.idx = 0;
@@ -1133,29 +1190,13 @@ static void recover_queues(struct uld_ctx *ctx)
recover_lost_dbs(ctx, &qp_list);
/* we're almost done! deref the qps and clean up */
- deref_qps(qp_list);
+ deref_qps(&qp_list);
kfree(qp_list.qps);
- /* Wait for the dbfifo to completely drain again */
- while (cxgb4_dbfifo_count(ctx->dev->rdev.lldi.ports[0], 1) > 0) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(usecs_to_jiffies(10));
- }
-
- /* resume the queues */
spin_lock_irq(&ctx->dev->lock);
- if (ctx->dev->qpcnt > db_fc_threshold)
- ctx->dev->db_state = FLOW_CONTROL;
- else {
- ctx->dev->db_state = NORMAL;
- idr_for_each(&ctx->dev->qpidr, enable_qp_db, NULL);
- }
- ctx->dev->rdev.stats.db_state_transitions++;
+ WARN_ON(ctx->dev->db_state != RECOVERY);
+ ctx->dev->db_state = STOPPED;
spin_unlock_irq(&ctx->dev->lock);
-
-out:
- /* start up kernel db ringers again */
- mutex_unlock(&ctx->dev->db_mutex);
}
static int c4iw_uld_control(void *handle, enum cxgb4_control control, ...)
@@ -1165,9 +1206,7 @@ static int c4iw_uld_control(void *handle, enum cxgb4_control control, ...)
switch (control) {
case CXGB4_CONTROL_DB_FULL:
stop_queues(ctx);
- mutex_lock(&ctx->dev->rdev.stats.lock);
ctx->dev->rdev.stats.db_full++;
- mutex_unlock(&ctx->dev->rdev.stats.lock);
break;
case CXGB4_CONTROL_DB_EMPTY:
resume_queues(ctx);
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index 23eaeabab93b..e872203c5424 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -109,6 +109,7 @@ struct c4iw_dev_ucontext {
enum c4iw_rdev_flags {
T4_FATAL_ERROR = (1<<0),
+ T4_STATUS_PAGE_DISABLED = (1<<1),
};
struct c4iw_stat {
@@ -130,6 +131,7 @@ struct c4iw_stats {
u64 db_empty;
u64 db_drop;
u64 db_state_transitions;
+ u64 db_fc_interruptions;
u64 tcam_full;
u64 act_ofld_conn_fails;
u64 pas_ofld_conn_fails;
@@ -150,6 +152,7 @@ struct c4iw_rdev {
unsigned long oc_mw_pa;
void __iomem *oc_mw_kva;
struct c4iw_stats stats;
+ struct t4_dev_status_page *status_page;
};
static inline int c4iw_fatal_error(struct c4iw_rdev *rdev)
@@ -211,7 +214,8 @@ static inline int c4iw_wait_for_reply(struct c4iw_rdev *rdev,
enum db_state {
NORMAL = 0,
FLOW_CONTROL = 1,
- RECOVERY = 2
+ RECOVERY = 2,
+ STOPPED = 3
};
struct c4iw_dev {
@@ -225,10 +229,10 @@ struct c4iw_dev {
struct mutex db_mutex;
struct dentry *debugfs_root;
enum db_state db_state;
- int qpcnt;
struct idr hwtid_idr;
struct idr atid_idr;
struct idr stid_idr;
+ struct list_head db_fc_list;
};
static inline struct c4iw_dev *to_c4iw_dev(struct ib_device *ibdev)
@@ -369,6 +373,7 @@ struct c4iw_fr_page_list {
DEFINE_DMA_UNMAP_ADDR(mapping);
dma_addr_t dma_addr;
struct c4iw_dev *dev;
+ int pll_len;
};
static inline struct c4iw_fr_page_list *to_c4iw_fr_page_list(
@@ -432,6 +437,7 @@ struct c4iw_qp_attributes {
struct c4iw_qp {
struct ib_qp ibqp;
+ struct list_head db_fc_entry;
struct c4iw_dev *rhp;
struct c4iw_ep *ep;
struct c4iw_qp_attributes attr;
@@ -441,6 +447,7 @@ struct c4iw_qp {
atomic_t refcnt;
wait_queue_head_t wait;
struct timer_list timer;
+ int sq_sig_all;
};
static inline struct c4iw_qp *to_c4iw_qp(struct ib_qp *ibqp)
diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c
index 41b11951a30a..f9ca072a99ed 100644
--- a/drivers/infiniband/hw/cxgb4/mem.c
+++ b/drivers/infiniband/hw/cxgb4/mem.c
@@ -37,9 +37,9 @@
#include "iw_cxgb4.h"
-int use_dsgl = 1;
+int use_dsgl = 0;
module_param(use_dsgl, int, 0644);
-MODULE_PARM_DESC(use_dsgl, "Use DSGL for PBL/FastReg (default=1)");
+MODULE_PARM_DESC(use_dsgl, "Use DSGL for PBL/FastReg (default=0)");
#define T4_ULPTX_MIN_IO 32
#define C4IW_MAX_INLINE_SIZE 96
@@ -678,9 +678,9 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
{
__be64 *pages;
int shift, n, len;
- int i, j, k;
+ int i, k, entry;
int err = 0;
- struct ib_umem_chunk *chunk;
+ struct scatterlist *sg;
struct c4iw_dev *rhp;
struct c4iw_pd *php;
struct c4iw_mr *mhp;
@@ -710,10 +710,7 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
shift = ffs(mhp->umem->page_size) - 1;
- n = 0;
- list_for_each_entry(chunk, &mhp->umem->chunk_list, list)
- n += chunk->nents;
-
+ n = mhp->umem->nmap;
err = alloc_pbl(mhp, n);
if (err)
goto err;
@@ -726,24 +723,22 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
i = n = 0;
- list_for_each_entry(chunk, &mhp->umem->chunk_list, list)
- for (j = 0; j < chunk->nmap; ++j) {
- len = sg_dma_len(&chunk->page_list[j]) >> shift;
- for (k = 0; k < len; ++k) {
- pages[i++] = cpu_to_be64(sg_dma_address(
- &chunk->page_list[j]) +
- mhp->umem->page_size * k);
- if (i == PAGE_SIZE / sizeof *pages) {
- err = write_pbl(&mhp->rhp->rdev,
- pages,
- mhp->attr.pbl_addr + (n << 3), i);
- if (err)
- goto pbl_done;
- n += i;
- i = 0;
- }
+ for_each_sg(mhp->umem->sg_head.sgl, sg, mhp->umem->nmap, entry) {
+ len = sg_dma_len(sg) >> shift;
+ for (k = 0; k < len; ++k) {
+ pages[i++] = cpu_to_be64(sg_dma_address(sg) +
+ mhp->umem->page_size * k);
+ if (i == PAGE_SIZE / sizeof *pages) {
+ err = write_pbl(&mhp->rhp->rdev,
+ pages,
+ mhp->attr.pbl_addr + (n << 3), i);
+ if (err)
+ goto pbl_done;
+ n += i;
+ i = 0;
}
}
+ }
if (i)
err = write_pbl(&mhp->rhp->rdev, pages,
@@ -903,7 +898,11 @@ struct ib_fast_reg_page_list *c4iw_alloc_fastreg_pbl(struct ib_device *device,
dma_unmap_addr_set(c4pl, mapping, dma_addr);
c4pl->dma_addr = dma_addr;
c4pl->dev = dev;
- c4pl->ibpl.max_page_list_len = pll_len;
+ c4pl->pll_len = pll_len;
+
+ PDBG("%s c4pl %p pll_len %u page_list %p dma_addr %pad\n",
+ __func__, c4pl, c4pl->pll_len, c4pl->ibpl.page_list,
+ &c4pl->dma_addr);
return &c4pl->ibpl;
}
@@ -912,8 +911,12 @@ void c4iw_free_fastreg_pbl(struct ib_fast_reg_page_list *ibpl)
{
struct c4iw_fr_page_list *c4pl = to_c4iw_fr_page_list(ibpl);
+ PDBG("%s c4pl %p pll_len %u page_list %p dma_addr %pad\n",
+ __func__, c4pl, c4pl->pll_len, c4pl->ibpl.page_list,
+ &c4pl->dma_addr);
+
dma_free_coherent(&c4pl->dev->rdev.lldi.pdev->dev,
- c4pl->ibpl.max_page_list_len,
+ c4pl->pll_len,
c4pl->ibpl.page_list, dma_unmap_addr(c4pl, mapping));
kfree(c4pl);
}
diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c
index 7e94c9a656a1..79429256023a 100644
--- a/drivers/infiniband/hw/cxgb4/provider.c
+++ b/drivers/infiniband/hw/cxgb4/provider.c
@@ -106,15 +106,56 @@ static struct ib_ucontext *c4iw_alloc_ucontext(struct ib_device *ibdev,
{
struct c4iw_ucontext *context;
struct c4iw_dev *rhp = to_c4iw_dev(ibdev);
+ static int warned;
+ struct c4iw_alloc_ucontext_resp uresp;
+ int ret = 0;
+ struct c4iw_mm_entry *mm = NULL;
PDBG("%s ibdev %p\n", __func__, ibdev);
context = kzalloc(sizeof(*context), GFP_KERNEL);
- if (!context)
- return ERR_PTR(-ENOMEM);
+ if (!context) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
c4iw_init_dev_ucontext(&rhp->rdev, &context->uctx);
INIT_LIST_HEAD(&context->mmaps);
spin_lock_init(&context->mmap_lock);
+
+ if (udata->outlen < sizeof(uresp)) {
+ if (!warned++)
+ pr_err(MOD "Warning - downlevel libcxgb4 (non-fatal), device status page disabled.");
+ rhp->rdev.flags |= T4_STATUS_PAGE_DISABLED;
+ } else {
+ mm = kmalloc(sizeof(*mm), GFP_KERNEL);
+ if (!mm) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ uresp.status_page_size = PAGE_SIZE;
+
+ spin_lock(&context->mmap_lock);
+ uresp.status_page_key = context->key;
+ context->key += PAGE_SIZE;
+ spin_unlock(&context->mmap_lock);
+
+ ret = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
+ if (ret)
+ goto err_mm;
+
+ mm->key = uresp.status_page_key;
+ mm->addr = virt_to_phys(rhp->rdev.status_page);
+ mm->len = PAGE_SIZE;
+ insert_mmap(context, mm);
+ }
return &context->ibucontext;
+err_mm:
+ kfree(mm);
+err_free:
+ kfree(context);
+err:
+ return ERR_PTR(ret);
}
static int c4iw_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index 582936708e6e..cb76eb5eee1f 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -638,6 +638,46 @@ void c4iw_qp_rem_ref(struct ib_qp *qp)
wake_up(&(to_c4iw_qp(qp)->wait));
}
+static void add_to_fc_list(struct list_head *head, struct list_head *entry)
+{
+ if (list_empty(entry))
+ list_add_tail(entry, head);
+}
+
+static int ring_kernel_sq_db(struct c4iw_qp *qhp, u16 inc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&qhp->rhp->lock, flags);
+ spin_lock(&qhp->lock);
+ if (qhp->rhp->db_state == NORMAL) {
+ t4_ring_sq_db(&qhp->wq, inc);
+ } else {
+ add_to_fc_list(&qhp->rhp->db_fc_list, &qhp->db_fc_entry);
+ qhp->wq.sq.wq_pidx_inc += inc;
+ }
+ spin_unlock(&qhp->lock);
+ spin_unlock_irqrestore(&qhp->rhp->lock, flags);
+ return 0;
+}
+
+static int ring_kernel_rq_db(struct c4iw_qp *qhp, u16 inc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&qhp->rhp->lock, flags);
+ spin_lock(&qhp->lock);
+ if (qhp->rhp->db_state == NORMAL) {
+ t4_ring_rq_db(&qhp->wq, inc);
+ } else {
+ add_to_fc_list(&qhp->rhp->db_fc_list, &qhp->db_fc_entry);
+ qhp->wq.rq.wq_pidx_inc += inc;
+ }
+ spin_unlock(&qhp->lock);
+ spin_unlock_irqrestore(&qhp->rhp->lock, flags);
+ return 0;
+}
+
int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
struct ib_send_wr **bad_wr)
{
@@ -675,7 +715,7 @@ int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
fw_flags = 0;
if (wr->send_flags & IB_SEND_SOLICITED)
fw_flags |= FW_RI_SOLICITED_EVENT_FLAG;
- if (wr->send_flags & IB_SEND_SIGNALED)
+ if (wr->send_flags & IB_SEND_SIGNALED || qhp->sq_sig_all)
fw_flags |= FW_RI_COMPLETION_FLAG;
swsqe = &qhp->wq.sq.sw_sq[qhp->wq.sq.pidx];
switch (wr->opcode) {
@@ -736,7 +776,8 @@ int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
}
swsqe->idx = qhp->wq.sq.pidx;
swsqe->complete = 0;
- swsqe->signaled = (wr->send_flags & IB_SEND_SIGNALED);
+ swsqe->signaled = (wr->send_flags & IB_SEND_SIGNALED) ||
+ qhp->sq_sig_all;
swsqe->flushed = 0;
swsqe->wr_id = wr->wr_id;
@@ -750,9 +791,13 @@ int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
t4_sq_produce(&qhp->wq, len16);
idx += DIV_ROUND_UP(len16*16, T4_EQ_ENTRY_SIZE);
}
- if (t4_wq_db_enabled(&qhp->wq))
+ if (!qhp->rhp->rdev.status_page->db_off) {
t4_ring_sq_db(&qhp->wq, idx);
- spin_unlock_irqrestore(&qhp->lock, flag);
+ spin_unlock_irqrestore(&qhp->lock, flag);
+ } else {
+ spin_unlock_irqrestore(&qhp->lock, flag);
+ ring_kernel_sq_db(qhp, idx);
+ }
return err;
}
@@ -812,9 +857,13 @@ int c4iw_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr,
wr = wr->next;
num_wrs--;
}
- if (t4_wq_db_enabled(&qhp->wq))
+ if (!qhp->rhp->rdev.status_page->db_off) {
t4_ring_rq_db(&qhp->wq, idx);
- spin_unlock_irqrestore(&qhp->lock, flag);
+ spin_unlock_irqrestore(&qhp->lock, flag);
+ } else {
+ spin_unlock_irqrestore(&qhp->lock, flag);
+ ring_kernel_rq_db(qhp, idx);
+ }
return err;
}
@@ -1200,35 +1249,6 @@ out:
return ret;
}
-/*
- * Called by the library when the qp has user dbs disabled due to
- * a DB_FULL condition. This function will single-thread all user
- * DB rings to avoid overflowing the hw db-fifo.
- */
-static int ring_kernel_db(struct c4iw_qp *qhp, u32 qid, u16 inc)
-{
- int delay = db_delay_usecs;
-
- mutex_lock(&qhp->rhp->db_mutex);
- do {
-
- /*
- * The interrupt threshold is dbfifo_int_thresh << 6. So
- * make sure we don't cross that and generate an interrupt.
- */
- if (cxgb4_dbfifo_count(qhp->rhp->rdev.lldi.ports[0], 1) <
- (qhp->rhp->rdev.lldi.dbfifo_int_thresh << 5)) {
- writel(QID(qid) | PIDX(inc), qhp->wq.db);
- break;
- }
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(usecs_to_jiffies(delay));
- delay = min(delay << 1, 2000);
- } while (1);
- mutex_unlock(&qhp->rhp->db_mutex);
- return 0;
-}
-
int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
enum c4iw_qp_attr_mask mask,
struct c4iw_qp_attributes *attrs,
@@ -1278,11 +1298,11 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
}
if (mask & C4IW_QP_ATTR_SQ_DB) {
- ret = ring_kernel_db(qhp, qhp->wq.sq.qid, attrs->sq_db_inc);
+ ret = ring_kernel_sq_db(qhp, attrs->sq_db_inc);
goto out;
}
if (mask & C4IW_QP_ATTR_RQ_DB) {
- ret = ring_kernel_db(qhp, qhp->wq.rq.qid, attrs->rq_db_inc);
+ ret = ring_kernel_rq_db(qhp, attrs->rq_db_inc);
goto out;
}
@@ -1465,14 +1485,6 @@ out:
return ret;
}
-static int enable_qp_db(int id, void *p, void *data)
-{
- struct c4iw_qp *qp = p;
-
- t4_enable_wq_db(&qp->wq);
- return 0;
-}
-
int c4iw_destroy_qp(struct ib_qp *ib_qp)
{
struct c4iw_dev *rhp;
@@ -1490,22 +1502,15 @@ int c4iw_destroy_qp(struct ib_qp *ib_qp)
c4iw_modify_qp(rhp, qhp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 0);
wait_event(qhp->wait, !qhp->ep);
- spin_lock_irq(&rhp->lock);
- remove_handle_nolock(rhp, &rhp->qpidr, qhp->wq.sq.qid);
- rhp->qpcnt--;
- BUG_ON(rhp->qpcnt < 0);
- if (rhp->qpcnt <= db_fc_threshold && rhp->db_state == FLOW_CONTROL) {
- rhp->rdev.stats.db_state_transitions++;
- rhp->db_state = NORMAL;
- idr_for_each(&rhp->qpidr, enable_qp_db, NULL);
- }
- if (db_coalescing_threshold >= 0)
- if (rhp->qpcnt <= db_coalescing_threshold)
- cxgb4_enable_db_coalescing(rhp->rdev.lldi.ports[0]);
- spin_unlock_irq(&rhp->lock);
+ remove_handle(rhp, &rhp->qpidr, qhp->wq.sq.qid);
atomic_dec(&qhp->refcnt);
wait_event(qhp->wait, !atomic_read(&qhp->refcnt));
+ spin_lock_irq(&rhp->lock);
+ if (!list_empty(&qhp->db_fc_entry))
+ list_del_init(&qhp->db_fc_entry);
+ spin_unlock_irq(&rhp->lock);
+
ucontext = ib_qp->uobject ?
to_c4iw_ucontext(ib_qp->uobject->context) : NULL;
destroy_qp(&rhp->rdev, &qhp->wq,
@@ -1516,14 +1521,6 @@ int c4iw_destroy_qp(struct ib_qp *ib_qp)
return 0;
}
-static int disable_qp_db(int id, void *p, void *data)
-{
- struct c4iw_qp *qp = p;
-
- t4_disable_wq_db(&qp->wq);
- return 0;
-}
-
struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
struct ib_udata *udata)
{
@@ -1533,7 +1530,7 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
struct c4iw_cq *schp;
struct c4iw_cq *rchp;
struct c4iw_create_qp_resp uresp;
- int sqsize, rqsize;
+ unsigned int sqsize, rqsize;
struct c4iw_ucontext *ucontext;
int ret;
struct c4iw_mm_entry *mm1, *mm2, *mm3, *mm4, *mm5 = NULL;
@@ -1605,25 +1602,13 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
qhp->attr.enable_bind = 1;
qhp->attr.max_ord = 1;
qhp->attr.max_ird = 1;
+ qhp->sq_sig_all = attrs->sq_sig_type == IB_SIGNAL_ALL_WR;
spin_lock_init(&qhp->lock);
mutex_init(&qhp->mutex);
init_waitqueue_head(&qhp->wait);
atomic_set(&qhp->refcnt, 1);
- spin_lock_irq(&rhp->lock);
- if (rhp->db_state != NORMAL)
- t4_disable_wq_db(&qhp->wq);
- rhp->qpcnt++;
- if (rhp->qpcnt > db_fc_threshold && rhp->db_state == NORMAL) {
- rhp->rdev.stats.db_state_transitions++;
- rhp->db_state = FLOW_CONTROL;
- idr_for_each(&rhp->qpidr, disable_qp_db, NULL);
- }
- if (db_coalescing_threshold >= 0)
- if (rhp->qpcnt > db_coalescing_threshold)
- cxgb4_disable_db_coalescing(rhp->rdev.lldi.ports[0]);
- ret = insert_handle_nolock(rhp, &rhp->qpidr, qhp, qhp->wq.sq.qid);
- spin_unlock_irq(&rhp->lock);
+ ret = insert_handle(rhp, &rhp->qpidr, qhp, qhp->wq.sq.qid);
if (ret)
goto err2;
@@ -1709,6 +1694,7 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
}
qhp->ibqp.qp_num = qhp->wq.sq.qid;
init_timer(&(qhp->timer));
+ INIT_LIST_HEAD(&qhp->db_fc_entry);
PDBG("%s qhp %p sq_num_entries %d, rq_num_entries %d qpid 0x%0x\n",
__func__, qhp, qhp->attr.sq_num_entries, qhp->attr.rq_num_entries,
qhp->wq.sq.qid);
diff --git a/drivers/infiniband/hw/cxgb4/t4.h b/drivers/infiniband/hw/cxgb4/t4.h
index e73ace739183..eeca8b1e6376 100644
--- a/drivers/infiniband/hw/cxgb4/t4.h
+++ b/drivers/infiniband/hw/cxgb4/t4.h
@@ -300,6 +300,7 @@ struct t4_sq {
u16 cidx;
u16 pidx;
u16 wq_pidx;
+ u16 wq_pidx_inc;
u16 flags;
short flush_cidx;
};
@@ -324,6 +325,7 @@ struct t4_rq {
u16 cidx;
u16 pidx;
u16 wq_pidx;
+ u16 wq_pidx_inc;
};
struct t4_wq {
@@ -609,3 +611,7 @@ static inline void t4_set_cq_in_error(struct t4_cq *cq)
((struct t4_status_page *)&cq->queue[cq->size])->qp_err = 1;
}
#endif
+
+struct t4_dev_status_page {
+ u8 db_off;
+};
diff --git a/drivers/infiniband/hw/cxgb4/user.h b/drivers/infiniband/hw/cxgb4/user.h
index 32b754c35ab7..11ccd276e5d9 100644
--- a/drivers/infiniband/hw/cxgb4/user.h
+++ b/drivers/infiniband/hw/cxgb4/user.h
@@ -70,4 +70,9 @@ struct c4iw_create_qp_resp {
__u32 qid_mask;
__u32 flags;
};
+
+struct c4iw_alloc_ucontext_resp {
+ __u64 status_page_key;
+ __u32 status_page_size;
+};
#endif
diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h
index f08f6eaf3fa8..bd45e0f3923f 100644
--- a/drivers/infiniband/hw/ehca/ehca_classes.h
+++ b/drivers/infiniband/hw/ehca/ehca_classes.h
@@ -322,7 +322,7 @@ struct ehca_mr_pginfo {
} phy;
struct { /* type EHCA_MR_PGI_USER section */
struct ib_umem *region;
- struct ib_umem_chunk *next_chunk;
+ struct scatterlist *next_sg;
u64 next_nmap;
} usr;
struct { /* type EHCA_MR_PGI_FMR section */
diff --git a/drivers/infiniband/hw/ehca/ehca_cq.c b/drivers/infiniband/hw/ehca/ehca_cq.c
index 212150c25ea0..8cc837537768 100644
--- a/drivers/infiniband/hw/ehca/ehca_cq.c
+++ b/drivers/infiniband/hw/ehca/ehca_cq.c
@@ -283,6 +283,7 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
(my_cq->galpas.user.fw_handle & (PAGE_SIZE - 1));
if (ib_copy_to_udata(udata, &resp, sizeof(resp))) {
ehca_err(device, "Copy to udata failed.");
+ cq = ERR_PTR(-EFAULT);
goto create_cq_exit4;
}
}
diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.c b/drivers/infiniband/hw/ehca/ehca_mrmw.c
index bcfb0c183620..3488e8c9fcb4 100644
--- a/drivers/infiniband/hw/ehca/ehca_mrmw.c
+++ b/drivers/infiniband/hw/ehca/ehca_mrmw.c
@@ -400,10 +400,7 @@ reg_user_mr_fallback:
pginfo.num_hwpages = num_hwpages;
pginfo.u.usr.region = e_mr->umem;
pginfo.next_hwpage = e_mr->umem->offset / hwpage_size;
- pginfo.u.usr.next_chunk = list_prepare_entry(pginfo.u.usr.next_chunk,
- (&e_mr->umem->chunk_list),
- list);
-
+ pginfo.u.usr.next_sg = pginfo.u.usr.region->sg_head.sgl;
ret = ehca_reg_mr(shca, e_mr, (u64 *)virt, length, mr_access_flags,
e_pd, &pginfo, &e_mr->ib.ib_mr.lkey,
&e_mr->ib.ib_mr.rkey, EHCA_REG_MR);
@@ -1858,61 +1855,39 @@ static int ehca_set_pagebuf_user1(struct ehca_mr_pginfo *pginfo,
u64 *kpage)
{
int ret = 0;
- struct ib_umem_chunk *prev_chunk;
- struct ib_umem_chunk *chunk;
u64 pgaddr;
- u32 i = 0;
u32 j = 0;
int hwpages_per_kpage = PAGE_SIZE / pginfo->hwpage_size;
-
- /* loop over desired chunk entries */
- chunk = pginfo->u.usr.next_chunk;
- prev_chunk = pginfo->u.usr.next_chunk;
- list_for_each_entry_continue(
- chunk, (&(pginfo->u.usr.region->chunk_list)), list) {
- for (i = pginfo->u.usr.next_nmap; i < chunk->nmap; ) {
- pgaddr = page_to_pfn(sg_page(&chunk->page_list[i]))
- << PAGE_SHIFT ;
- *kpage = pgaddr + (pginfo->next_hwpage *
- pginfo->hwpage_size);
- if ( !(*kpage) ) {
- ehca_gen_err("pgaddr=%llx "
- "chunk->page_list[i]=%llx "
- "i=%x next_hwpage=%llx",
- pgaddr, (u64)sg_dma_address(
- &chunk->page_list[i]),
- i, pginfo->next_hwpage);
- return -EFAULT;
- }
- (pginfo->hwpage_cnt)++;
- (pginfo->next_hwpage)++;
- kpage++;
- if (pginfo->next_hwpage % hwpages_per_kpage == 0) {
- (pginfo->kpage_cnt)++;
- (pginfo->u.usr.next_nmap)++;
- pginfo->next_hwpage = 0;
- i++;
- }
- j++;
- if (j >= number) break;
+ struct scatterlist **sg = &pginfo->u.usr.next_sg;
+
+ while (*sg != NULL) {
+ pgaddr = page_to_pfn(sg_page(*sg))
+ << PAGE_SHIFT;
+ *kpage = pgaddr + (pginfo->next_hwpage *
+ pginfo->hwpage_size);
+ if (!(*kpage)) {
+ ehca_gen_err("pgaddr=%llx "
+ "sg_dma_address=%llx "
+ "entry=%llx next_hwpage=%llx",
+ pgaddr, (u64)sg_dma_address(*sg),
+ pginfo->u.usr.next_nmap,
+ pginfo->next_hwpage);
+ return -EFAULT;
}
- if ((pginfo->u.usr.next_nmap >= chunk->nmap) &&
- (j >= number)) {
- pginfo->u.usr.next_nmap = 0;
- prev_chunk = chunk;
- break;
- } else if (pginfo->u.usr.next_nmap >= chunk->nmap) {
- pginfo->u.usr.next_nmap = 0;
- prev_chunk = chunk;
- } else if (j >= number)
+ (pginfo->hwpage_cnt)++;
+ (pginfo->next_hwpage)++;
+ kpage++;
+ if (pginfo->next_hwpage % hwpages_per_kpage == 0) {
+ (pginfo->kpage_cnt)++;
+ (pginfo->u.usr.next_nmap)++;
+ pginfo->next_hwpage = 0;
+ *sg = sg_next(*sg);
+ }
+ j++;
+ if (j >= number)
break;
- else
- prev_chunk = chunk;
}
- pginfo->u.usr.next_chunk =
- list_prepare_entry(prev_chunk,
- (&(pginfo->u.usr.region->chunk_list)),
- list);
+
return ret;
}
@@ -1920,20 +1895,19 @@ static int ehca_set_pagebuf_user1(struct ehca_mr_pginfo *pginfo,
* check given pages for contiguous layout
* last page addr is returned in prev_pgaddr for further check
*/
-static int ehca_check_kpages_per_ate(struct scatterlist *page_list,
- int start_idx, int end_idx,
+static int ehca_check_kpages_per_ate(struct scatterlist **sg,
+ int num_pages,
u64 *prev_pgaddr)
{
- int t;
- for (t = start_idx; t <= end_idx; t++) {
- u64 pgaddr = page_to_pfn(sg_page(&page_list[t])) << PAGE_SHIFT;
+ for (; *sg && num_pages > 0; *sg = sg_next(*sg), num_pages--) {
+ u64 pgaddr = page_to_pfn(sg_page(*sg)) << PAGE_SHIFT;
if (ehca_debug_level >= 3)
ehca_gen_dbg("chunk_page=%llx value=%016llx", pgaddr,
*(u64 *)__va(pgaddr));
if (pgaddr - PAGE_SIZE != *prev_pgaddr) {
ehca_gen_err("uncontiguous page found pgaddr=%llx "
- "prev_pgaddr=%llx page_list_i=%x",
- pgaddr, *prev_pgaddr, t);
+ "prev_pgaddr=%llx entries_left_in_hwpage=%x",
+ pgaddr, *prev_pgaddr, num_pages);
return -EINVAL;
}
*prev_pgaddr = pgaddr;
@@ -1947,111 +1921,80 @@ static int ehca_set_pagebuf_user2(struct ehca_mr_pginfo *pginfo,
u64 *kpage)
{
int ret = 0;
- struct ib_umem_chunk *prev_chunk;
- struct ib_umem_chunk *chunk;
u64 pgaddr, prev_pgaddr;
- u32 i = 0;
u32 j = 0;
int kpages_per_hwpage = pginfo->hwpage_size / PAGE_SIZE;
int nr_kpages = kpages_per_hwpage;
+ struct scatterlist **sg = &pginfo->u.usr.next_sg;
+
+ while (*sg != NULL) {
- /* loop over desired chunk entries */
- chunk = pginfo->u.usr.next_chunk;
- prev_chunk = pginfo->u.usr.next_chunk;
- list_for_each_entry_continue(
- chunk, (&(pginfo->u.usr.region->chunk_list)), list) {
- for (i = pginfo->u.usr.next_nmap; i < chunk->nmap; ) {
- if (nr_kpages == kpages_per_hwpage) {
- pgaddr = ( page_to_pfn(sg_page(&chunk->page_list[i]))
- << PAGE_SHIFT );
- *kpage = pgaddr;
- if ( !(*kpage) ) {
- ehca_gen_err("pgaddr=%llx i=%x",
- pgaddr, i);
+ if (nr_kpages == kpages_per_hwpage) {
+ pgaddr = (page_to_pfn(sg_page(*sg))
+ << PAGE_SHIFT);
+ *kpage = pgaddr;
+ if (!(*kpage)) {
+ ehca_gen_err("pgaddr=%llx entry=%llx",
+ pgaddr, pginfo->u.usr.next_nmap);
+ ret = -EFAULT;
+ return ret;
+ }
+ /*
+ * The first page in a hwpage must be aligned;
+ * the first MR page is exempt from this rule.
+ */
+ if (pgaddr & (pginfo->hwpage_size - 1)) {
+ if (pginfo->hwpage_cnt) {
+ ehca_gen_err(
+ "invalid alignment "
+ "pgaddr=%llx entry=%llx "
+ "mr_pgsize=%llx",
+ pgaddr, pginfo->u.usr.next_nmap,
+ pginfo->hwpage_size);
ret = -EFAULT;
return ret;
}
- /*
- * The first page in a hwpage must be aligned;
- * the first MR page is exempt from this rule.
- */
- if (pgaddr & (pginfo->hwpage_size - 1)) {
- if (pginfo->hwpage_cnt) {
- ehca_gen_err(
- "invalid alignment "
- "pgaddr=%llx i=%x "
- "mr_pgsize=%llx",
- pgaddr, i,
- pginfo->hwpage_size);
- ret = -EFAULT;
- return ret;
- }
- /* first MR page */
- pginfo->kpage_cnt =
- (pgaddr &
- (pginfo->hwpage_size - 1)) >>
- PAGE_SHIFT;
- nr_kpages -= pginfo->kpage_cnt;
- *kpage = pgaddr &
- ~(pginfo->hwpage_size - 1);
- }
- if (ehca_debug_level >= 3) {
- u64 val = *(u64 *)__va(pgaddr);
- ehca_gen_dbg("kpage=%llx chunk_page=%llx "
- "value=%016llx",
- *kpage, pgaddr, val);
- }
- prev_pgaddr = pgaddr;
- i++;
- pginfo->kpage_cnt++;
- pginfo->u.usr.next_nmap++;
- nr_kpages--;
- if (!nr_kpages)
- goto next_kpage;
- continue;
+ /* first MR page */
+ pginfo->kpage_cnt =
+ (pgaddr &
+ (pginfo->hwpage_size - 1)) >>
+ PAGE_SHIFT;
+ nr_kpages -= pginfo->kpage_cnt;
+ *kpage = pgaddr &
+ ~(pginfo->hwpage_size - 1);
}
- if (i + nr_kpages > chunk->nmap) {
- ret = ehca_check_kpages_per_ate(
- chunk->page_list, i,
- chunk->nmap - 1, &prev_pgaddr);
- if (ret) return ret;
- pginfo->kpage_cnt += chunk->nmap - i;
- pginfo->u.usr.next_nmap += chunk->nmap - i;
- nr_kpages -= chunk->nmap - i;
- break;
+ if (ehca_debug_level >= 3) {
+ u64 val = *(u64 *)__va(pgaddr);
+ ehca_gen_dbg("kpage=%llx page=%llx "
+ "value=%016llx",
+ *kpage, pgaddr, val);
}
+ prev_pgaddr = pgaddr;
+ *sg = sg_next(*sg);
+ pginfo->kpage_cnt++;
+ pginfo->u.usr.next_nmap++;
+ nr_kpages--;
+ if (!nr_kpages)
+ goto next_kpage;
+ continue;
+ }
+
+ ret = ehca_check_kpages_per_ate(sg, nr_kpages,
+ &prev_pgaddr);
+ if (ret)
+ return ret;
+ pginfo->kpage_cnt += nr_kpages;
+ pginfo->u.usr.next_nmap += nr_kpages;
- ret = ehca_check_kpages_per_ate(chunk->page_list, i,
- i + nr_kpages - 1,
- &prev_pgaddr);
- if (ret) return ret;
- i += nr_kpages;
- pginfo->kpage_cnt += nr_kpages;
- pginfo->u.usr.next_nmap += nr_kpages;
next_kpage:
- nr_kpages = kpages_per_hwpage;
- (pginfo->hwpage_cnt)++;
- kpage++;
- j++;
- if (j >= number) break;
- }
- if ((pginfo->u.usr.next_nmap >= chunk->nmap) &&
- (j >= number)) {
- pginfo->u.usr.next_nmap = 0;
- prev_chunk = chunk;
- break;
- } else if (pginfo->u.usr.next_nmap >= chunk->nmap) {
- pginfo->u.usr.next_nmap = 0;
- prev_chunk = chunk;
- } else if (j >= number)
+ nr_kpages = kpages_per_hwpage;
+ (pginfo->hwpage_cnt)++;
+ kpage++;
+ j++;
+ if (j >= number)
break;
- else
- prev_chunk = chunk;
}
- pginfo->u.usr.next_chunk =
- list_prepare_entry(prev_chunk,
- (&(pginfo->u.usr.region->chunk_list)),
- list);
+
return ret;
}
@@ -2591,16 +2534,6 @@ static void ehca_dma_unmap_sg(struct ib_device *dev, struct scatterlist *sg,
/* This is only a stub; nothing to be done here */
}
-static u64 ehca_dma_address(struct ib_device *dev, struct scatterlist *sg)
-{
- return sg->dma_address;
-}
-
-static unsigned int ehca_dma_len(struct ib_device *dev, struct scatterlist *sg)
-{
- return sg->length;
-}
-
static void ehca_dma_sync_single_for_cpu(struct ib_device *dev, u64 addr,
size_t size,
enum dma_data_direction dir)
@@ -2653,8 +2586,6 @@ struct ib_dma_mapping_ops ehca_dma_mapping_ops = {
.unmap_page = ehca_dma_unmap_page,
.map_sg = ehca_dma_map_sg,
.unmap_sg = ehca_dma_unmap_sg,
- .dma_address = ehca_dma_address,
- .dma_len = ehca_dma_len,
.sync_single_for_cpu = ehca_dma_sync_single_for_cpu,
.sync_single_for_device = ehca_dma_sync_single_for_device,
.alloc_coherent = ehca_dma_alloc_coherent,
diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c
index 714293b78518..e2f9a51f4a38 100644
--- a/drivers/infiniband/hw/ipath/ipath_diag.c
+++ b/drivers/infiniband/hw/ipath/ipath_diag.c
@@ -326,7 +326,7 @@ static ssize_t ipath_diagpkt_write(struct file *fp,
size_t count, loff_t *off)
{
u32 __iomem *piobuf;
- u32 plen, clen, pbufn;
+ u32 plen, pbufn, maxlen_reserve;
struct ipath_diag_pkt odp;
struct ipath_diag_xpkt dp;
u32 *tmpbuf = NULL;
@@ -335,51 +335,29 @@ static ssize_t ipath_diagpkt_write(struct file *fp,
u64 val;
u32 l_state, lt_state; /* LinkState, LinkTrainingState */
- if (count < sizeof(odp)) {
- ret = -EINVAL;
- goto bail;
- }
if (count == sizeof(dp)) {
if (copy_from_user(&dp, data, sizeof(dp))) {
ret = -EFAULT;
goto bail;
}
- } else if (copy_from_user(&odp, data, sizeof(odp))) {
- ret = -EFAULT;
+ } else if (count == sizeof(odp)) {
+ if (copy_from_user(&odp, data, sizeof(odp))) {
+ ret = -EFAULT;
+ goto bail;
+ }
+ } else {
+ ret = -EINVAL;
goto bail;
}
- /*
- * Due to padding/alignment issues (lessened with new struct)
- * the old and new structs are the same length. We need to
- * disambiguate them, which we can do because odp.len has never
- * been less than the total of LRH+BTH+DETH so far, while
- * dp.unit (same offset) unit is unlikely to get that high.
- * Similarly, dp.data, the pointer to user at the same offset
- * as odp.unit, is almost certainly at least one (512byte)page
- * "above" NULL. The if-block below can be omitted if compatibility
- * between a new driver and older diagnostic code is unimportant.
- * compatibility the other direction (new diags, old driver) is
- * handled in the diagnostic code, with a warning.
- */
- if (dp.unit >= 20 && dp.data < 512) {
- /* very probable version mismatch. Fix it up */
- memcpy(&odp, &dp, sizeof(odp));
- /* We got a legacy dp, copy elements to dp */
- dp.unit = odp.unit;
- dp.data = odp.data;
- dp.len = odp.len;
- dp.pbc_wd = 0; /* Indicate we need to compute PBC wd */
- }
-
/* send count must be an exact number of dwords */
if (dp.len & 3) {
ret = -EINVAL;
goto bail;
}
- clen = dp.len >> 2;
+ plen = dp.len >> 2;
dd = ipath_lookup(dp.unit);
if (!dd || !(dd->ipath_flags & IPATH_PRESENT) ||
@@ -422,16 +400,22 @@ static ssize_t ipath_diagpkt_write(struct file *fp,
goto bail;
}
- /* need total length before first word written */
- /* +1 word is for the qword padding */
- plen = sizeof(u32) + dp.len;
-
- if ((plen + 4) > dd->ipath_ibmaxlen) {
+ /*
+ * need total length before first word written, plus 2 Dwords. One Dword
+ * is for padding so we get the full user data when not aligned on
+ * a word boundary. The other Dword is to make sure we have room for the
+ * ICRC which gets tacked on later.
+ */
+ maxlen_reserve = 2 * sizeof(u32);
+ if (dp.len > dd->ipath_ibmaxlen - maxlen_reserve) {
ipath_dbg("Pkt len 0x%x > ibmaxlen %x\n",
- plen - 4, dd->ipath_ibmaxlen);
+ dp.len, dd->ipath_ibmaxlen);
ret = -EINVAL;
- goto bail; /* before writing pbc */
+ goto bail;
}
+
+ plen = sizeof(u32) + dp.len;
+
tmpbuf = vmalloc(plen);
if (!tmpbuf) {
dev_info(&dd->pcidev->dev, "Unable to allocate tmp buffer, "
@@ -473,11 +457,11 @@ static ssize_t ipath_diagpkt_write(struct file *fp,
*/
if (dd->ipath_flags & IPATH_PIO_FLUSH_WC) {
ipath_flush_wc();
- __iowrite32_copy(piobuf + 2, tmpbuf, clen - 1);
+ __iowrite32_copy(piobuf + 2, tmpbuf, plen - 1);
ipath_flush_wc();
- __raw_writel(tmpbuf[clen - 1], piobuf + clen + 1);
+ __raw_writel(tmpbuf[plen - 1], piobuf + plen + 1);
} else
- __iowrite32_copy(piobuf + 2, tmpbuf, clen);
+ __iowrite32_copy(piobuf + 2, tmpbuf, plen);
ipath_flush_wc();
diff --git a/drivers/infiniband/hw/ipath/ipath_dma.c b/drivers/infiniband/hw/ipath/ipath_dma.c
index 644c2c74e054..123a8c053539 100644
--- a/drivers/infiniband/hw/ipath/ipath_dma.c
+++ b/drivers/infiniband/hw/ipath/ipath_dma.c
@@ -115,6 +115,10 @@ static int ipath_map_sg(struct ib_device *dev, struct scatterlist *sgl,
ret = 0;
break;
}
+ sg->dma_address = addr + sg->offset;
+#ifdef CONFIG_NEED_SG_DMA_LENGTH
+ sg->dma_length = sg->length;
+#endif
}
return ret;
}
@@ -126,21 +130,6 @@ static void ipath_unmap_sg(struct ib_device *dev,
BUG_ON(!valid_dma_direction(direction));
}
-static u64 ipath_sg_dma_address(struct ib_device *dev, struct scatterlist *sg)
-{
- u64 addr = (u64) page_address(sg_page(sg));
-
- if (addr)
- addr += sg->offset;
- return addr;
-}
-
-static unsigned int ipath_sg_dma_len(struct ib_device *dev,
- struct scatterlist *sg)
-{
- return sg->length;
-}
-
static void ipath_sync_single_for_cpu(struct ib_device *dev,
u64 addr,
size_t size,
@@ -176,17 +165,15 @@ static void ipath_dma_free_coherent(struct ib_device *dev, size_t size,
}
struct ib_dma_mapping_ops ipath_dma_mapping_ops = {
- ipath_mapping_error,
- ipath_dma_map_single,
- ipath_dma_unmap_single,
- ipath_dma_map_page,
- ipath_dma_unmap_page,
- ipath_map_sg,
- ipath_unmap_sg,
- ipath_sg_dma_address,
- ipath_sg_dma_len,
- ipath_sync_single_for_cpu,
- ipath_sync_single_for_device,
- ipath_dma_alloc_coherent,
- ipath_dma_free_coherent
+ .mapping_error = ipath_mapping_error,
+ .map_single = ipath_dma_map_single,
+ .unmap_single = ipath_dma_unmap_single,
+ .map_page = ipath_dma_map_page,
+ .unmap_page = ipath_dma_unmap_page,
+ .map_sg = ipath_map_sg,
+ .unmap_sg = ipath_unmap_sg,
+ .sync_single_for_cpu = ipath_sync_single_for_cpu,
+ .sync_single_for_device = ipath_sync_single_for_device,
+ .alloc_coherent = ipath_dma_alloc_coherent,
+ .free_coherent = ipath_dma_free_coherent
};
diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c
index e346d3890a0e..5e61e9bff697 100644
--- a/drivers/infiniband/hw/ipath/ipath_mr.c
+++ b/drivers/infiniband/hw/ipath/ipath_mr.c
@@ -188,8 +188,8 @@ struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
{
struct ipath_mr *mr;
struct ib_umem *umem;
- struct ib_umem_chunk *chunk;
- int n, m, i;
+ int n, m, entry;
+ struct scatterlist *sg;
struct ib_mr *ret;
if (length == 0) {
@@ -202,10 +202,7 @@ struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
if (IS_ERR(umem))
return (void *) umem;
- n = 0;
- list_for_each_entry(chunk, &umem->chunk_list, list)
- n += chunk->nents;
-
+ n = umem->nmap;
mr = alloc_mr(n, &to_idev(pd->device)->lk_table);
if (!mr) {
ret = ERR_PTR(-ENOMEM);
@@ -224,22 +221,20 @@ struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
m = 0;
n = 0;
- list_for_each_entry(chunk, &umem->chunk_list, list) {
- for (i = 0; i < chunk->nents; i++) {
- void *vaddr;
-
- vaddr = page_address(sg_page(&chunk->page_list[i]));
- if (!vaddr) {
- ret = ERR_PTR(-EINVAL);
- goto bail;
- }
- mr->mr.map[m]->segs[n].vaddr = vaddr;
- mr->mr.map[m]->segs[n].length = umem->page_size;
- n++;
- if (n == IPATH_SEGSZ) {
- m++;
- n = 0;
- }
+ for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
+ void *vaddr;
+
+ vaddr = page_address(sg_page(sg));
+ if (!vaddr) {
+ ret = ERR_PTR(-EINVAL);
+ goto bail;
+ }
+ mr->mr.map[m]->segs[n].vaddr = vaddr;
+ mr->mr.map[m]->segs[n].length = umem->page_size;
+ n++;
+ if (n == IPATH_SEGSZ) {
+ m++;
+ n = 0;
}
}
ret = &mr->ibmr;
diff --git a/drivers/infiniband/hw/mlx4/alias_GUID.c b/drivers/infiniband/hw/mlx4/alias_GUID.c
index 2f215b93db6b..0eb141c41416 100644
--- a/drivers/infiniband/hw/mlx4/alias_GUID.c
+++ b/drivers/infiniband/hw/mlx4/alias_GUID.c
@@ -154,7 +154,7 @@ void mlx4_ib_notify_slaves_on_guid_change(struct mlx4_ib_dev *dev,
continue;
slave_id = (block_num * NUM_ALIAS_GUID_IN_REC) + i ;
- if (slave_id >= dev->dev->num_slaves)
+ if (slave_id >= dev->dev->num_vfs + 1)
return;
tmp_cur_ag = *(__be64 *)&p_data[i * GUID_REC_SIZE];
form_cache_ag = get_cached_alias_guid(dev, port_num,
diff --git a/drivers/infiniband/hw/mlx4/cm.c b/drivers/infiniband/hw/mlx4/cm.c
index d1f5f1dd77b0..56a593e0ae5d 100644
--- a/drivers/infiniband/hw/mlx4/cm.c
+++ b/drivers/infiniband/hw/mlx4/cm.c
@@ -61,6 +61,11 @@ struct cm_generic_msg {
__be32 remote_comm_id;
};
+struct cm_sidr_generic_msg {
+ struct ib_mad_hdr hdr;
+ __be32 request_id;
+};
+
struct cm_req_msg {
unsigned char unused[0x60];
union ib_gid primary_path_sgid;
@@ -69,28 +74,62 @@ struct cm_req_msg {
static void set_local_comm_id(struct ib_mad *mad, u32 cm_id)
{
- struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
- msg->local_comm_id = cpu_to_be32(cm_id);
+ if (mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) {
+ struct cm_sidr_generic_msg *msg =
+ (struct cm_sidr_generic_msg *)mad;
+ msg->request_id = cpu_to_be32(cm_id);
+ } else if (mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) {
+ pr_err("trying to set local_comm_id in SIDR_REP\n");
+ return;
+ } else {
+ struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
+ msg->local_comm_id = cpu_to_be32(cm_id);
+ }
}
static u32 get_local_comm_id(struct ib_mad *mad)
{
- struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
-
- return be32_to_cpu(msg->local_comm_id);
+ if (mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) {
+ struct cm_sidr_generic_msg *msg =
+ (struct cm_sidr_generic_msg *)mad;
+ return be32_to_cpu(msg->request_id);
+ } else if (mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) {
+ pr_err("trying to set local_comm_id in SIDR_REP\n");
+ return -1;
+ } else {
+ struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
+ return be32_to_cpu(msg->local_comm_id);
+ }
}
static void set_remote_comm_id(struct ib_mad *mad, u32 cm_id)
{
- struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
- msg->remote_comm_id = cpu_to_be32(cm_id);
+ if (mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) {
+ struct cm_sidr_generic_msg *msg =
+ (struct cm_sidr_generic_msg *)mad;
+ msg->request_id = cpu_to_be32(cm_id);
+ } else if (mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) {
+ pr_err("trying to set remote_comm_id in SIDR_REQ\n");
+ return;
+ } else {
+ struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
+ msg->remote_comm_id = cpu_to_be32(cm_id);
+ }
}
static u32 get_remote_comm_id(struct ib_mad *mad)
{
- struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
-
- return be32_to_cpu(msg->remote_comm_id);
+ if (mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) {
+ struct cm_sidr_generic_msg *msg =
+ (struct cm_sidr_generic_msg *)mad;
+ return be32_to_cpu(msg->request_id);
+ } else if (mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) {
+ pr_err("trying to set remote_comm_id in SIDR_REQ\n");
+ return -1;
+ } else {
+ struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
+ return be32_to_cpu(msg->remote_comm_id);
+ }
}
static union ib_gid gid_from_req_msg(struct ib_device *ibdev, struct ib_mad *mad)
@@ -282,19 +321,21 @@ int mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id
u32 sl_cm_id;
int pv_cm_id = -1;
- sl_cm_id = get_local_comm_id(mad);
-
if (mad->mad_hdr.attr_id == CM_REQ_ATTR_ID ||
- mad->mad_hdr.attr_id == CM_REP_ATTR_ID) {
+ mad->mad_hdr.attr_id == CM_REP_ATTR_ID ||
+ mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) {
+ sl_cm_id = get_local_comm_id(mad);
id = id_map_alloc(ibdev, slave_id, sl_cm_id);
if (IS_ERR(id)) {
mlx4_ib_warn(ibdev, "%s: id{slave: %d, sl_cm_id: 0x%x} Failed to id_map_alloc\n",
__func__, slave_id, sl_cm_id);
return PTR_ERR(id);
}
- } else if (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID) {
+ } else if (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID ||
+ mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) {
return 0;
} else {
+ sl_cm_id = get_local_comm_id(mad);
id = id_map_get(ibdev, &pv_cm_id, slave_id, sl_cm_id);
}
@@ -315,14 +356,18 @@ int mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id
}
int mlx4_ib_demux_cm_handler(struct ib_device *ibdev, int port, int *slave,
- struct ib_mad *mad)
+ struct ib_mad *mad)
{
u32 pv_cm_id;
struct id_map_entry *id;
- if (mad->mad_hdr.attr_id == CM_REQ_ATTR_ID) {
+ if (mad->mad_hdr.attr_id == CM_REQ_ATTR_ID ||
+ mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) {
union ib_gid gid;
+ if (!slave)
+ return 0;
+
gid = gid_from_req_msg(ibdev, mad);
*slave = mlx4_ib_find_real_gid(ibdev, port, gid.global.interface_id);
if (*slave < 0) {
@@ -341,7 +386,8 @@ int mlx4_ib_demux_cm_handler(struct ib_device *ibdev, int port, int *slave,
return -ENOENT;
}
- *slave = id->slave_id;
+ if (slave)
+ *slave = id->slave_id;
set_remote_comm_id(mad, id->sl_cm_id);
if (mad->mad_hdr.attr_id == CM_DREQ_ATTR_ID)
diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c
index cc40f08ca8f1..5f640814cc81 100644
--- a/drivers/infiniband/hw/mlx4/cq.c
+++ b/drivers/infiniband/hw/mlx4/cq.c
@@ -564,7 +564,7 @@ static int mlx4_ib_ipoib_csum_ok(__be16 status, __be16 checksum)
}
static int use_tunnel_data(struct mlx4_ib_qp *qp, struct mlx4_ib_cq *cq, struct ib_wc *wc,
- unsigned tail, struct mlx4_cqe *cqe)
+ unsigned tail, struct mlx4_cqe *cqe, int is_eth)
{
struct mlx4_ib_proxy_sqp_hdr *hdr;
@@ -574,12 +574,20 @@ static int use_tunnel_data(struct mlx4_ib_qp *qp, struct mlx4_ib_cq *cq, struct
DMA_FROM_DEVICE);
hdr = (struct mlx4_ib_proxy_sqp_hdr *) (qp->sqp_proxy_rcv[tail].addr);
wc->pkey_index = be16_to_cpu(hdr->tun.pkey_index);
- wc->slid = be16_to_cpu(hdr->tun.slid_mac_47_32);
- wc->sl = (u8) (be16_to_cpu(hdr->tun.sl_vid) >> 12);
wc->src_qp = be32_to_cpu(hdr->tun.flags_src_qp) & 0xFFFFFF;
wc->wc_flags |= (hdr->tun.g_ml_path & 0x80) ? (IB_WC_GRH) : 0;
wc->dlid_path_bits = 0;
+ if (is_eth) {
+ wc->vlan_id = be16_to_cpu(hdr->tun.sl_vid);
+ memcpy(&(wc->smac[0]), (char *)&hdr->tun.mac_31_0, 4);
+ memcpy(&(wc->smac[4]), (char *)&hdr->tun.slid_mac_47_32, 2);
+ wc->wc_flags |= (IB_WC_WITH_VLAN | IB_WC_WITH_SMAC);
+ } else {
+ wc->slid = be16_to_cpu(hdr->tun.slid_mac_47_32);
+ wc->sl = (u8) (be16_to_cpu(hdr->tun.sl_vid) >> 12);
+ }
+
return 0;
}
@@ -594,6 +602,7 @@ static int mlx4_ib_poll_one(struct mlx4_ib_cq *cq,
struct mlx4_srq *msrq = NULL;
int is_send;
int is_error;
+ int is_eth;
u32 g_mlpath_rqpn;
u16 wqe_ctr;
unsigned tail = 0;
@@ -778,11 +787,15 @@ repoll:
break;
}
+ is_eth = (rdma_port_get_link_layer(wc->qp->device,
+ (*cur_qp)->port) ==
+ IB_LINK_LAYER_ETHERNET);
if (mlx4_is_mfunc(to_mdev(cq->ibcq.device)->dev)) {
if ((*cur_qp)->mlx4_ib_qp_type &
(MLX4_IB_QPT_PROXY_SMI_OWNER |
MLX4_IB_QPT_PROXY_SMI | MLX4_IB_QPT_PROXY_GSI))
- return use_tunnel_data(*cur_qp, cq, wc, tail, cqe);
+ return use_tunnel_data(*cur_qp, cq, wc, tail,
+ cqe, is_eth);
}
wc->slid = be16_to_cpu(cqe->rlid);
@@ -793,20 +806,21 @@ repoll:
wc->pkey_index = be32_to_cpu(cqe->immed_rss_invalid) & 0x7f;
wc->wc_flags |= mlx4_ib_ipoib_csum_ok(cqe->status,
cqe->checksum) ? IB_WC_IP_CSUM_OK : 0;
- if (rdma_port_get_link_layer(wc->qp->device,
- (*cur_qp)->port) == IB_LINK_LAYER_ETHERNET)
+ if (is_eth) {
wc->sl = be16_to_cpu(cqe->sl_vid) >> 13;
- else
- wc->sl = be16_to_cpu(cqe->sl_vid) >> 12;
- if (be32_to_cpu(cqe->vlan_my_qpn) & MLX4_CQE_VLAN_PRESENT_MASK) {
- wc->vlan_id = be16_to_cpu(cqe->sl_vid) &
- MLX4_CQE_VID_MASK;
+ if (be32_to_cpu(cqe->vlan_my_qpn) &
+ MLX4_CQE_VLAN_PRESENT_MASK) {
+ wc->vlan_id = be16_to_cpu(cqe->sl_vid) &
+ MLX4_CQE_VID_MASK;
+ } else {
+ wc->vlan_id = 0xffff;
+ }
+ memcpy(wc->smac, cqe->smac, ETH_ALEN);
+ wc->wc_flags |= (IB_WC_WITH_VLAN | IB_WC_WITH_SMAC);
} else {
+ wc->sl = be16_to_cpu(cqe->sl_vid) >> 12;
wc->vlan_id = 0xffff;
}
- wc->wc_flags |= IB_WC_WITH_VLAN;
- memcpy(wc->smac, cqe->smac, ETH_ALEN);
- wc->wc_flags |= IB_WC_WITH_SMAC;
}
return 0;
diff --git a/drivers/infiniband/hw/mlx4/doorbell.c b/drivers/infiniband/hw/mlx4/doorbell.c
index 8aee4233b388..c51740986367 100644
--- a/drivers/infiniband/hw/mlx4/doorbell.c
+++ b/drivers/infiniband/hw/mlx4/doorbell.c
@@ -45,7 +45,6 @@ int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, unsigned long virt,
struct mlx4_db *db)
{
struct mlx4_ib_user_db_page *page;
- struct ib_umem_chunk *chunk;
int err = 0;
mutex_lock(&context->db_page_mutex);
@@ -73,8 +72,7 @@ int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, unsigned long virt,
list_add(&page->list, &context->db_page_list);
found:
- chunk = list_entry(page->umem->chunk_list.next, struct ib_umem_chunk, list);
- db->dma = sg_dma_address(chunk->page_list) + (virt & ~PAGE_MASK);
+ db->dma = sg_dma_address(page->umem->sg_head.sgl) + (virt & ~PAGE_MASK);
db->u.user_page = page;
++page->refcnt;
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index f2a3f48107e7..fd36ec672632 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -467,6 +467,7 @@ int mlx4_ib_send_to_slave(struct mlx4_ib_dev *dev, int slave, u8 port,
int ret = 0;
u16 tun_pkey_ix;
u16 cached_pkey;
+ u8 is_eth = dev->dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH;
if (dest_qpt > IB_QPT_GSI)
return -EINVAL;
@@ -509,6 +510,10 @@ int mlx4_ib_send_to_slave(struct mlx4_ib_dev *dev, int slave, u8 port,
* The driver will set the force loopback bit in post_send */
memset(&attr, 0, sizeof attr);
attr.port_num = port;
+ if (is_eth) {
+ memcpy(&attr.grh.dgid.raw[0], &grh->dgid.raw[0], 16);
+ attr.ah_flags = IB_AH_GRH;
+ }
ah = ib_create_ah(tun_ctx->pd, &attr);
if (IS_ERR(ah))
return -ENOMEM;
@@ -540,11 +545,36 @@ int mlx4_ib_send_to_slave(struct mlx4_ib_dev *dev, int slave, u8 port,
/* adjust tunnel data */
tun_mad->hdr.pkey_index = cpu_to_be16(tun_pkey_ix);
- tun_mad->hdr.sl_vid = cpu_to_be16(((u16)(wc->sl)) << 12);
- tun_mad->hdr.slid_mac_47_32 = cpu_to_be16(wc->slid);
tun_mad->hdr.flags_src_qp = cpu_to_be32(wc->src_qp & 0xFFFFFF);
tun_mad->hdr.g_ml_path = (grh && (wc->wc_flags & IB_WC_GRH)) ? 0x80 : 0;
+ if (is_eth) {
+ u16 vlan = 0;
+ if (mlx4_get_slave_default_vlan(dev->dev, port, slave, &vlan,
+ NULL)) {
+ /* VST mode */
+ if (vlan != wc->vlan_id)
+ /* Packet vlan is not the VST-assigned vlan.
+ * Drop the packet.
+ */
+ goto out;
+ else
+ /* Remove the vlan tag before forwarding
+ * the packet to the VF.
+ */
+ vlan = 0xffff;
+ } else {
+ vlan = wc->vlan_id;
+ }
+
+ tun_mad->hdr.sl_vid = cpu_to_be16(vlan);
+ memcpy((char *)&tun_mad->hdr.mac_31_0, &(wc->smac[0]), 4);
+ memcpy((char *)&tun_mad->hdr.slid_mac_47_32, &(wc->smac[4]), 2);
+ } else {
+ tun_mad->hdr.sl_vid = cpu_to_be16(((u16)(wc->sl)) << 12);
+ tun_mad->hdr.slid_mac_47_32 = cpu_to_be16(wc->slid);
+ }
+
ib_dma_sync_single_for_device(&dev->ib_dev,
tun_qp->tx_ring[tun_tx_ix].buf.map,
sizeof (struct mlx4_rcv_tunnel_mad),
@@ -580,6 +610,41 @@ static int mlx4_ib_demux_mad(struct ib_device *ibdev, u8 port,
int err;
int slave;
u8 *slave_id;
+ int is_eth = 0;
+
+ if (rdma_port_get_link_layer(ibdev, port) == IB_LINK_LAYER_INFINIBAND)
+ is_eth = 0;
+ else
+ is_eth = 1;
+
+ if (is_eth) {
+ if (!(wc->wc_flags & IB_WC_GRH)) {
+ mlx4_ib_warn(ibdev, "RoCE grh not present.\n");
+ return -EINVAL;
+ }
+ if (mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_CM) {
+ 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)) {
+ mlx4_ib_warn(ibdev, "failed matching grh\n");
+ return -ENOENT;
+ }
+ if (slave >= dev->dev->caps.sqp_demux) {
+ mlx4_ib_warn(ibdev, "slave id: %d is bigger than allowed:%d\n",
+ slave, dev->dev->caps.sqp_demux);
+ return -ENOENT;
+ }
+
+ if (mlx4_ib_demux_cm_handler(ibdev, port, NULL, mad))
+ return 0;
+
+ err = mlx4_ib_send_to_slave(dev, slave, port, wc->qp->qp_type, wc, grh, mad);
+ if (err)
+ pr_debug("failed sending to slave %d via tunnel qp (%d)\n",
+ slave, err);
+ return 0;
+ }
/* Initially assume that this mad is for us */
slave = mlx4_master_func_num(dev->dev);
@@ -1076,8 +1141,9 @@ static int is_proxy_qp0(struct mlx4_ib_dev *dev, int qpn, int slave)
int mlx4_ib_send_to_wire(struct mlx4_ib_dev *dev, int slave, u8 port,
- enum ib_qp_type dest_qpt, u16 pkey_index, u32 remote_qpn,
- u32 qkey, struct ib_ah_attr *attr, struct ib_mad *mad)
+ enum ib_qp_type dest_qpt, u16 pkey_index,
+ u32 remote_qpn, u32 qkey, struct ib_ah_attr *attr,
+ u8 *s_mac, struct ib_mad *mad)
{
struct ib_sge list;
struct ib_send_wr wr, *bad_wr;
@@ -1166,6 +1232,9 @@ int mlx4_ib_send_to_wire(struct mlx4_ib_dev *dev, int slave, u8 port,
wr.num_sge = 1;
wr.opcode = IB_WR_SEND;
wr.send_flags = IB_SEND_SIGNALED;
+ if (s_mac)
+ memcpy(to_mah(ah)->av.eth.s_mac, s_mac, 6);
+
ret = ib_post_send(send_qp, &wr, &bad_wr);
out:
@@ -1174,6 +1243,22 @@ out:
return ret;
}
+static int get_slave_base_gid_ix(struct mlx4_ib_dev *dev, int slave, int port)
+{
+ if (rdma_port_get_link_layer(&dev->ib_dev, port) == IB_LINK_LAYER_INFINIBAND)
+ return slave;
+ return mlx4_get_base_gid_ix(dev->dev, slave, port);
+}
+
+static void fill_in_real_sgid_index(struct mlx4_ib_dev *dev, int slave, int port,
+ struct ib_ah_attr *ah_attr)
+{
+ if (rdma_port_get_link_layer(&dev->ib_dev, port) == IB_LINK_LAYER_INFINIBAND)
+ ah_attr->grh.sgid_index = slave;
+ else
+ ah_attr->grh.sgid_index += get_slave_base_gid_ix(dev, slave, port);
+}
+
static void mlx4_ib_multiplex_mad(struct mlx4_ib_demux_pv_ctx *ctx, struct ib_wc *wc)
{
struct mlx4_ib_dev *dev = to_mdev(ctx->ib_dev);
@@ -1184,6 +1269,7 @@ static void mlx4_ib_multiplex_mad(struct mlx4_ib_demux_pv_ctx *ctx, struct ib_wc
struct ib_ah_attr ah_attr;
u8 *slave_id;
int slave;
+ int port;
/* Get slave that sent this packet */
if (wc->src_qp < dev->dev->phys_caps.base_proxy_sqpn ||
@@ -1260,12 +1346,18 @@ static void mlx4_ib_multiplex_mad(struct mlx4_ib_demux_pv_ctx *ctx, struct ib_wc
memcpy(&ah.av, &tunnel->hdr.av, sizeof (struct mlx4_av));
ah.ibah.device = ctx->ib_dev;
mlx4_ib_query_ah(&ah.ibah, &ah_attr);
- if ((ah_attr.ah_flags & IB_AH_GRH) &&
- (ah_attr.grh.sgid_index != slave)) {
- mlx4_ib_warn(ctx->ib_dev, "slave:%d accessed invalid sgid_index:%d\n",
- slave, ah_attr.grh.sgid_index);
+ if (ah_attr.ah_flags & IB_AH_GRH)
+ fill_in_real_sgid_index(dev, slave, ctx->port, &ah_attr);
+
+ port = mlx4_slave_convert_port(dev->dev, slave, ah_attr.port_num);
+ if (port < 0)
return;
- }
+ ah_attr.port_num = port;
+ memcpy(ah_attr.dmac, tunnel->hdr.mac, 6);
+ ah_attr.vlan_id = be16_to_cpu(tunnel->hdr.vlan);
+ /* if slave have default vlan use it */
+ mlx4_get_slave_default_vlan(dev->dev, ctx->port, slave,
+ &ah_attr.vlan_id, &ah_attr.sl);
mlx4_ib_send_to_wire(dev, slave, ctx->port,
is_proxy_qp0(dev, wc->src_qp, slave) ?
@@ -1273,7 +1365,7 @@ static void mlx4_ib_multiplex_mad(struct mlx4_ib_demux_pv_ctx *ctx, struct ib_wc
be16_to_cpu(tunnel->hdr.pkey_index),
be32_to_cpu(tunnel->hdr.remote_qpn),
be32_to_cpu(tunnel->hdr.qkey),
- &ah_attr, &tunnel->mad);
+ &ah_attr, wc->smac, &tunnel->mad);
}
static int mlx4_ib_alloc_pv_bufs(struct mlx4_ib_demux_pv_ctx *ctx,
@@ -1850,7 +1942,15 @@ static int mlx4_ib_alloc_demux_ctx(struct mlx4_ib_dev *dev,
ctx->port = port;
ctx->ib_dev = &dev->ib_dev;
- for (i = 0; i < dev->dev->caps.sqp_demux; i++) {
+ for (i = 0;
+ i < min(dev->dev->caps.sqp_demux, (u16)(dev->dev->num_vfs + 1));
+ i++) {
+ struct mlx4_active_ports actv_ports =
+ mlx4_get_active_ports(dev->dev, i);
+
+ if (!test_bit(port - 1, actv_ports.ports))
+ continue;
+
ret = alloc_pv_object(dev, i, port, &ctx->tun[i]);
if (ret) {
ret = -ENOMEM;
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index f9c12e92fdd6..1b6dbe156a37 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -1546,7 +1546,7 @@ static int mlx4_ib_addr_event(int event, struct net_device *event_netdev,
iboe = &ibdev->iboe;
spin_lock(&iboe->lock);
- for (port = 1; port <= MLX4_MAX_PORTS; ++port)
+ for (port = 1; port <= ibdev->dev->caps.num_ports; ++port)
if ((netif_is_bond_master(real_dev) &&
(real_dev == iboe->masters[port - 1])) ||
(!netif_is_bond_master(real_dev) &&
@@ -1569,14 +1569,14 @@ static u8 mlx4_ib_get_dev_port(struct net_device *dev,
iboe = &ibdev->iboe;
- for (port = 1; port <= MLX4_MAX_PORTS; ++port)
+ for (port = 1; port <= ibdev->dev->caps.num_ports; ++port)
if ((netif_is_bond_master(real_dev) &&
(real_dev == iboe->masters[port - 1])) ||
(!netif_is_bond_master(real_dev) &&
(real_dev == iboe->netdevs[port - 1])))
break;
- if ((port == 0) || (port > MLX4_MAX_PORTS))
+ if ((port == 0) || (port > ibdev->dev->caps.num_ports))
return 0;
else
return port;
@@ -1626,7 +1626,7 @@ static void mlx4_ib_get_dev_addr(struct net_device *dev,
union ib_gid gid;
- if ((port == 0) || (port > MLX4_MAX_PORTS))
+ if ((port == 0) || (port > ibdev->dev->caps.num_ports))
return;
/* IPv4 gids */
@@ -1803,7 +1803,7 @@ static void init_pkeys(struct mlx4_ib_dev *ibdev)
static void mlx4_ib_alloc_eqs(struct mlx4_dev *dev, struct mlx4_ib_dev *ibdev)
{
- char name[32];
+ char name[80];
int eq_per_port = 0;
int added_eqs = 0;
int total_eqs = 0;
@@ -1833,8 +1833,8 @@ static void mlx4_ib_alloc_eqs(struct mlx4_dev *dev, struct mlx4_ib_dev *ibdev)
eq = 0;
mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB) {
for (j = 0; j < eq_per_port; j++) {
- sprintf(name, "mlx4-ib-%d-%d@%s",
- i, j, dev->pdev->bus->name);
+ snprintf(name, sizeof(name), "mlx4-ib-%d-%d@%s",
+ i, j, dev->pdev->bus->name);
/* Set IRQ for specific name (per ring) */
if (mlx4_assign_eq(dev, name, NULL,
&ibdev->eq_table[eq])) {
@@ -1888,14 +1888,6 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
pr_info_once("%s", mlx4_ib_version);
- mlx4_foreach_non_ib_transport_port(i, dev)
- num_ports++;
-
- if (mlx4_is_mfunc(dev) && num_ports) {
- dev_err(&dev->pdev->dev, "RoCE is not supported over SRIOV as yet\n");
- return NULL;
- }
-
num_ports = 0;
mlx4_foreach_ib_transport_port(i, dev)
num_ports++;
@@ -2056,8 +2048,9 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
err = mlx4_counter_alloc(ibdev->dev, &ibdev->counters[i]);
if (err)
ibdev->counters[i] = -1;
- } else
- ibdev->counters[i] = -1;
+ } else {
+ ibdev->counters[i] = -1;
+ }
}
mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB)
@@ -2331,17 +2324,24 @@ static void do_slave_init(struct mlx4_ib_dev *ibdev, int slave, int do_init)
struct mlx4_dev *dev = ibdev->dev;
int i;
unsigned long flags;
+ struct mlx4_active_ports actv_ports;
+ unsigned int ports;
+ unsigned int first_port;
if (!mlx4_is_master(dev))
return;
- dm = kcalloc(dev->caps.num_ports, sizeof *dm, GFP_ATOMIC);
+ actv_ports = mlx4_get_active_ports(dev, slave);
+ ports = bitmap_weight(actv_ports.ports, dev->caps.num_ports);
+ first_port = find_first_bit(actv_ports.ports, dev->caps.num_ports);
+
+ dm = kcalloc(ports, sizeof(*dm), GFP_ATOMIC);
if (!dm) {
pr_err("failed to allocate memory for tunneling qp update\n");
goto out;
}
- for (i = 0; i < dev->caps.num_ports; i++) {
+ for (i = 0; i < ports; i++) {
dm[i] = kmalloc(sizeof (struct mlx4_ib_demux_work), GFP_ATOMIC);
if (!dm[i]) {
pr_err("failed to allocate memory for tunneling qp update work struct\n");
@@ -2353,9 +2353,9 @@ static void do_slave_init(struct mlx4_ib_dev *ibdev, int slave, int do_init)
}
}
/* initialize or tear down tunnel QPs for the slave */
- for (i = 0; i < dev->caps.num_ports; i++) {
+ for (i = 0; i < ports; i++) {
INIT_WORK(&dm[i]->work, mlx4_ib_tunnels_update_work);
- dm[i]->port = i + 1;
+ dm[i]->port = first_port + i + 1;
dm[i]->slave = slave;
dm[i]->do_init = do_init;
dm[i]->dev = ibdev;
diff --git a/drivers/infiniband/hw/mlx4/mcg.c b/drivers/infiniband/hw/mlx4/mcg.c
index 25b2cdff00f8..ed327e6c8fdc 100644
--- a/drivers/infiniband/hw/mlx4/mcg.c
+++ b/drivers/infiniband/hw/mlx4/mcg.c
@@ -215,8 +215,9 @@ static int send_mad_to_wire(struct mlx4_ib_demux_ctx *ctx, struct ib_mad *mad)
}
mlx4_ib_query_ah(dev->sm_ah[ctx->port - 1], &ah_attr);
spin_unlock(&dev->sm_lock);
- return mlx4_ib_send_to_wire(dev, mlx4_master_func_num(dev->dev), ctx->port,
- IB_QPT_GSI, 0, 1, IB_QP1_QKEY, &ah_attr, mad);
+ return mlx4_ib_send_to_wire(dev, mlx4_master_func_num(dev->dev),
+ ctx->port, IB_QPT_GSI, 0, 1, IB_QP1_QKEY,
+ &ah_attr, NULL, mad);
}
static int send_mad_to_slave(int slave, struct mlx4_ib_demux_ctx *ctx,
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index a230683af940..f589522fddfd 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -241,6 +241,22 @@ struct mlx4_ib_proxy_sqp_hdr {
struct mlx4_rcv_tunnel_hdr tun;
} __packed;
+struct mlx4_roce_smac_vlan_info {
+ u64 smac;
+ int smac_index;
+ int smac_port;
+ u64 candidate_smac;
+ int candidate_smac_index;
+ int candidate_smac_port;
+ u16 vid;
+ int vlan_index;
+ int vlan_port;
+ u16 candidate_vid;
+ int candidate_vlan_index;
+ int candidate_vlan_port;
+ int update_vid;
+};
+
struct mlx4_ib_qp {
struct ib_qp ibqp;
struct mlx4_qp mqp;
@@ -273,8 +289,9 @@ struct mlx4_ib_qp {
struct list_head gid_list;
struct list_head steering_rules;
struct mlx4_ib_buf *sqp_proxy_rcv;
+ struct mlx4_roce_smac_vlan_info pri;
+ struct mlx4_roce_smac_vlan_info alt;
u64 reg_id;
-
};
struct mlx4_ib_srq {
@@ -720,9 +737,12 @@ void mlx4_ib_tunnels_update_work(struct work_struct *work);
int mlx4_ib_send_to_slave(struct mlx4_ib_dev *dev, int slave, u8 port,
enum ib_qp_type qpt, struct ib_wc *wc,
struct ib_grh *grh, struct ib_mad *mad);
+
int mlx4_ib_send_to_wire(struct mlx4_ib_dev *dev, int slave, u8 port,
enum ib_qp_type dest_qpt, u16 pkey_index, u32 remote_qpn,
- u32 qkey, struct ib_ah_attr *attr, struct ib_mad *mad);
+ u32 qkey, struct ib_ah_attr *attr, u8 *s_mac,
+ struct ib_mad *mad);
+
__be64 mlx4_ib_get_new_demux_tid(struct mlx4_ib_demux_ctx *ctx);
int mlx4_ib_demux_cm_handler(struct ib_device *ibdev, int port, int *slave,
diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c
index e471f089ff00..cb2a8727f3fb 100644
--- a/drivers/infiniband/hw/mlx4/mr.c
+++ b/drivers/infiniband/hw/mlx4/mr.c
@@ -90,11 +90,11 @@ int mlx4_ib_umem_write_mtt(struct mlx4_ib_dev *dev, struct mlx4_mtt *mtt,
struct ib_umem *umem)
{
u64 *pages;
- struct ib_umem_chunk *chunk;
- int i, j, k;
+ int i, k, entry;
int n;
int len;
int err = 0;
+ struct scatterlist *sg;
pages = (u64 *) __get_free_page(GFP_KERNEL);
if (!pages)
@@ -102,26 +102,25 @@ int mlx4_ib_umem_write_mtt(struct mlx4_ib_dev *dev, struct mlx4_mtt *mtt,
i = n = 0;
- list_for_each_entry(chunk, &umem->chunk_list, list)
- for (j = 0; j < chunk->nmap; ++j) {
- len = sg_dma_len(&chunk->page_list[j]) >> mtt->page_shift;
- for (k = 0; k < len; ++k) {
- pages[i++] = sg_dma_address(&chunk->page_list[j]) +
- umem->page_size * k;
- /*
- * Be friendly to mlx4_write_mtt() and
- * pass it chunks of appropriate size.
- */
- if (i == PAGE_SIZE / sizeof (u64)) {
- err = mlx4_write_mtt(dev->dev, mtt, n,
- i, pages);
- if (err)
- goto out;
- n += i;
- i = 0;
- }
+ for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
+ len = sg_dma_len(sg) >> mtt->page_shift;
+ for (k = 0; k < len; ++k) {
+ pages[i++] = sg_dma_address(sg) +
+ umem->page_size * k;
+ /*
+ * Be friendly to mlx4_write_mtt() and
+ * pass it chunks of appropriate size.
+ */
+ if (i == PAGE_SIZE / sizeof (u64)) {
+ err = mlx4_write_mtt(dev->dev, mtt, n,
+ i, pages);
+ if (err)
+ goto out;
+ n += i;
+ i = 0;
}
}
+ }
if (i)
err = mlx4_write_mtt(dev->dev, mtt, n, i, pages);
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index d8f4d1fe8494..41308af4163c 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -662,10 +662,14 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
if (!sqp)
return -ENOMEM;
qp = &sqp->qp;
+ qp->pri.vid = 0xFFFF;
+ qp->alt.vid = 0xFFFF;
} else {
qp = kzalloc(sizeof (struct mlx4_ib_qp), GFP_KERNEL);
if (!qp)
return -ENOMEM;
+ qp->pri.vid = 0xFFFF;
+ qp->alt.vid = 0xFFFF;
}
} else
qp = *caller_qp;
@@ -940,11 +944,32 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
{
struct mlx4_ib_cq *send_cq, *recv_cq;
- if (qp->state != IB_QPS_RESET)
+ if (qp->state != IB_QPS_RESET) {
if (mlx4_qp_modify(dev->dev, NULL, to_mlx4_state(qp->state),
MLX4_QP_STATE_RST, NULL, 0, 0, &qp->mqp))
pr_warn("modify QP %06x to RESET failed.\n",
qp->mqp.qpn);
+ if (qp->pri.smac) {
+ mlx4_unregister_mac(dev->dev, qp->pri.smac_port, qp->pri.smac);
+ qp->pri.smac = 0;
+ }
+ if (qp->alt.smac) {
+ mlx4_unregister_mac(dev->dev, qp->alt.smac_port, qp->alt.smac);
+ qp->alt.smac = 0;
+ }
+ if (qp->pri.vid < 0x1000) {
+ mlx4_unregister_vlan(dev->dev, qp->pri.vlan_port, qp->pri.vid);
+ qp->pri.vid = 0xFFFF;
+ qp->pri.candidate_vid = 0xFFFF;
+ qp->pri.update_vid = 0;
+ }
+ if (qp->alt.vid < 0x1000) {
+ mlx4_unregister_vlan(dev->dev, qp->alt.vlan_port, qp->alt.vid);
+ qp->alt.vid = 0xFFFF;
+ qp->alt.candidate_vid = 0xFFFF;
+ qp->alt.update_vid = 0;
+ }
+ }
get_cqs(qp, &send_cq, &recv_cq);
@@ -1057,6 +1082,8 @@ struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
qp = kzalloc(sizeof *qp, GFP_KERNEL);
if (!qp)
return ERR_PTR(-ENOMEM);
+ qp->pri.vid = 0xFFFF;
+ qp->alt.vid = 0xFFFF;
/* fall through */
case IB_QPT_UD:
{
@@ -1188,12 +1215,13 @@ static void mlx4_set_sched(struct mlx4_qp_path *path, u8 port)
static int _mlx4_set_path(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah,
u64 smac, u16 vlan_tag, struct mlx4_qp_path *path,
- u8 port)
+ struct mlx4_roce_smac_vlan_info *smac_info, u8 port)
{
int is_eth = rdma_port_get_link_layer(&dev->ib_dev, port) ==
IB_LINK_LAYER_ETHERNET;
int vidx;
int smac_index;
+ int err;
path->grh_mylmc = ah->src_path_bits & 0x7f;
@@ -1223,61 +1251,103 @@ static int _mlx4_set_path(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah,
}
if (is_eth) {
- path->sched_queue = MLX4_IB_DEFAULT_SCHED_QUEUE |
- ((port - 1) << 6) | ((ah->sl & 7) << 3);
-
if (!(ah->ah_flags & IB_AH_GRH))
return -1;
- memcpy(path->dmac, ah->dmac, ETH_ALEN);
- path->ackto = MLX4_IB_LINK_TYPE_ETH;
- /* find the index into MAC table for IBoE */
- if (!is_zero_ether_addr((const u8 *)&smac)) {
- if (mlx4_find_cached_mac(dev->dev, port, smac,
- &smac_index))
- return -ENOENT;
- } else {
- smac_index = 0;
- }
-
- path->grh_mylmc &= 0x80 | smac_index;
+ path->sched_queue = MLX4_IB_DEFAULT_SCHED_QUEUE |
+ ((port - 1) << 6) | ((ah->sl & 7) << 3);
path->feup |= MLX4_FEUP_FORCE_ETH_UP;
if (vlan_tag < 0x1000) {
- if (mlx4_find_cached_vlan(dev->dev, port, vlan_tag, &vidx))
- return -ENOENT;
-
- path->vlan_index = vidx;
- path->fl = 1 << 6;
+ if (smac_info->vid < 0x1000) {
+ /* both valid vlan ids */
+ if (smac_info->vid != vlan_tag) {
+ /* different VIDs. unreg old and reg new */
+ err = mlx4_register_vlan(dev->dev, port, vlan_tag, &vidx);
+ if (err)
+ return err;
+ smac_info->candidate_vid = vlan_tag;
+ smac_info->candidate_vlan_index = vidx;
+ smac_info->candidate_vlan_port = port;
+ smac_info->update_vid = 1;
+ path->vlan_index = vidx;
+ } else {
+ path->vlan_index = smac_info->vlan_index;
+ }
+ } else {
+ /* no current vlan tag in qp */
+ err = mlx4_register_vlan(dev->dev, port, vlan_tag, &vidx);
+ if (err)
+ return err;
+ smac_info->candidate_vid = vlan_tag;
+ smac_info->candidate_vlan_index = vidx;
+ smac_info->candidate_vlan_port = port;
+ smac_info->update_vid = 1;
+ path->vlan_index = vidx;
+ }
path->feup |= MLX4_FVL_FORCE_ETH_VLAN;
+ path->fl = 1 << 6;
+ } else {
+ /* have current vlan tag. unregister it at modify-qp success */
+ if (smac_info->vid < 0x1000) {
+ smac_info->candidate_vid = 0xFFFF;
+ smac_info->update_vid = 1;
+ }
}
- } else
+
+ /* get smac_index for RoCE use.
+ * If no smac was yet assigned, register one.
+ * If one was already assigned, but the new mac differs,
+ * unregister the old one and register the new one.
+ */
+ if (!smac_info->smac || smac_info->smac != smac) {
+ /* register candidate now, unreg if needed, after success */
+ smac_index = mlx4_register_mac(dev->dev, port, smac);
+ if (smac_index >= 0) {
+ smac_info->candidate_smac_index = smac_index;
+ smac_info->candidate_smac = smac;
+ smac_info->candidate_smac_port = port;
+ } else {
+ return -EINVAL;
+ }
+ } else {
+ smac_index = smac_info->smac_index;
+ }
+
+ memcpy(path->dmac, ah->dmac, 6);
+ path->ackto = MLX4_IB_LINK_TYPE_ETH;
+ /* put MAC table smac index for IBoE */
+ path->grh_mylmc = (u8) (smac_index) | 0x80;
+ } else {
path->sched_queue = MLX4_IB_DEFAULT_SCHED_QUEUE |
((port - 1) << 6) | ((ah->sl & 0xf) << 2);
+ }
return 0;
}
static int mlx4_set_path(struct mlx4_ib_dev *dev, const struct ib_qp_attr *qp,
enum ib_qp_attr_mask qp_attr_mask,
+ struct mlx4_ib_qp *mqp,
struct mlx4_qp_path *path, u8 port)
{
return _mlx4_set_path(dev, &qp->ah_attr,
mlx4_mac_to_u64((u8 *)qp->smac),
(qp_attr_mask & IB_QP_VID) ? qp->vlan_id : 0xffff,
- path, port);
+ path, &mqp->pri, port);
}
static int mlx4_set_alt_path(struct mlx4_ib_dev *dev,
const struct ib_qp_attr *qp,
enum ib_qp_attr_mask qp_attr_mask,
+ struct mlx4_ib_qp *mqp,
struct mlx4_qp_path *path, u8 port)
{
return _mlx4_set_path(dev, &qp->alt_ah_attr,
mlx4_mac_to_u64((u8 *)qp->alt_smac),
(qp_attr_mask & IB_QP_ALT_VID) ?
qp->alt_vlan_id : 0xffff,
- path, port);
+ path, &mqp->alt, port);
}
static void update_mcg_macs(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp)
@@ -1292,6 +1362,37 @@ static void update_mcg_macs(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp)
}
}
+static int handle_eth_ud_smac_index(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp, u8 *smac,
+ struct mlx4_qp_context *context)
+{
+ struct net_device *ndev;
+ u64 u64_mac;
+ int smac_index;
+
+
+ ndev = dev->iboe.netdevs[qp->port - 1];
+ if (ndev) {
+ smac = ndev->dev_addr;
+ u64_mac = mlx4_mac_to_u64(smac);
+ } else {
+ u64_mac = dev->dev->caps.def_mac[qp->port];
+ }
+
+ context->pri_path.sched_queue = MLX4_IB_DEFAULT_SCHED_QUEUE | ((qp->port - 1) << 6);
+ if (!qp->pri.smac) {
+ smac_index = mlx4_register_mac(dev->dev, qp->port, u64_mac);
+ if (smac_index >= 0) {
+ qp->pri.candidate_smac_index = smac_index;
+ qp->pri.candidate_smac = u64_mac;
+ qp->pri.candidate_smac_port = qp->port;
+ context->pri_path.grh_mylmc = 0x80 | (u8) smac_index;
+ } else {
+ return -ENOENT;
+ }
+ }
+ return 0;
+}
+
static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
const struct ib_qp_attr *attr, int attr_mask,
enum ib_qp_state cur_state, enum ib_qp_state new_state)
@@ -1403,7 +1504,7 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
}
if (attr_mask & IB_QP_AV) {
- if (mlx4_set_path(dev, attr, attr_mask, &context->pri_path,
+ if (mlx4_set_path(dev, attr, attr_mask, qp, &context->pri_path,
attr_mask & IB_QP_PORT ?
attr->port_num : qp->port))
goto out;
@@ -1426,7 +1527,8 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
dev->dev->caps.pkey_table_len[attr->alt_port_num])
goto out;
- if (mlx4_set_alt_path(dev, attr, attr_mask, &context->alt_path,
+ if (mlx4_set_alt_path(dev, attr, attr_mask, qp,
+ &context->alt_path,
attr->alt_port_num))
goto out;
@@ -1532,6 +1634,20 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
context->pri_path.fl = 0x80;
context->pri_path.sched_queue |= MLX4_IB_DEFAULT_SCHED_QUEUE;
}
+ if (rdma_port_get_link_layer(&dev->ib_dev, qp->port) ==
+ IB_LINK_LAYER_ETHERNET) {
+ if (qp->mlx4_ib_qp_type == MLX4_IB_QPT_TUN_GSI ||
+ qp->mlx4_ib_qp_type == MLX4_IB_QPT_GSI)
+ context->pri_path.feup = 1 << 7; /* don't fsm */
+ /* handle smac_index */
+ if (qp->mlx4_ib_qp_type == MLX4_IB_QPT_UD ||
+ qp->mlx4_ib_qp_type == MLX4_IB_QPT_PROXY_GSI ||
+ qp->mlx4_ib_qp_type == MLX4_IB_QPT_TUN_GSI) {
+ err = handle_eth_ud_smac_index(dev, qp, (u8 *)attr->smac, context);
+ if (err)
+ return -EINVAL;
+ }
+ }
}
if (qp->ibqp.qp_type == IB_QPT_RAW_PACKET)
@@ -1619,28 +1735,113 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
* If we moved a kernel QP to RESET, clean up all old CQ
* entries and reinitialize the QP.
*/
- if (new_state == IB_QPS_RESET && !ibqp->uobject) {
- mlx4_ib_cq_clean(recv_cq, qp->mqp.qpn,
- ibqp->srq ? to_msrq(ibqp->srq): NULL);
- if (send_cq != recv_cq)
- mlx4_ib_cq_clean(send_cq, qp->mqp.qpn, NULL);
+ if (new_state == IB_QPS_RESET) {
+ if (!ibqp->uobject) {
+ mlx4_ib_cq_clean(recv_cq, qp->mqp.qpn,
+ ibqp->srq ? to_msrq(ibqp->srq) : NULL);
+ if (send_cq != recv_cq)
+ mlx4_ib_cq_clean(send_cq, qp->mqp.qpn, NULL);
+
+ qp->rq.head = 0;
+ qp->rq.tail = 0;
+ qp->sq.head = 0;
+ qp->sq.tail = 0;
+ qp->sq_next_wqe = 0;
+ if (qp->rq.wqe_cnt)
+ *qp->db.db = 0;
- qp->rq.head = 0;
- qp->rq.tail = 0;
- qp->sq.head = 0;
- qp->sq.tail = 0;
- qp->sq_next_wqe = 0;
- if (qp->rq.wqe_cnt)
- *qp->db.db = 0;
+ if (qp->flags & MLX4_IB_QP_NETIF)
+ mlx4_ib_steer_qp_reg(dev, qp, 0);
+ }
+ if (qp->pri.smac) {
+ mlx4_unregister_mac(dev->dev, qp->pri.smac_port, qp->pri.smac);
+ qp->pri.smac = 0;
+ }
+ if (qp->alt.smac) {
+ mlx4_unregister_mac(dev->dev, qp->alt.smac_port, qp->alt.smac);
+ qp->alt.smac = 0;
+ }
+ if (qp->pri.vid < 0x1000) {
+ mlx4_unregister_vlan(dev->dev, qp->pri.vlan_port, qp->pri.vid);
+ qp->pri.vid = 0xFFFF;
+ qp->pri.candidate_vid = 0xFFFF;
+ qp->pri.update_vid = 0;
+ }
- if (qp->flags & MLX4_IB_QP_NETIF)
- mlx4_ib_steer_qp_reg(dev, qp, 0);
+ if (qp->alt.vid < 0x1000) {
+ mlx4_unregister_vlan(dev->dev, qp->alt.vlan_port, qp->alt.vid);
+ qp->alt.vid = 0xFFFF;
+ qp->alt.candidate_vid = 0xFFFF;
+ qp->alt.update_vid = 0;
+ }
}
-
out:
if (err && steer_qp)
mlx4_ib_steer_qp_reg(dev, qp, 0);
kfree(context);
+ if (qp->pri.candidate_smac) {
+ if (err) {
+ mlx4_unregister_mac(dev->dev, qp->pri.candidate_smac_port, qp->pri.candidate_smac);
+ } else {
+ if (qp->pri.smac)
+ mlx4_unregister_mac(dev->dev, qp->pri.smac_port, qp->pri.smac);
+ qp->pri.smac = qp->pri.candidate_smac;
+ qp->pri.smac_index = qp->pri.candidate_smac_index;
+ qp->pri.smac_port = qp->pri.candidate_smac_port;
+ }
+ qp->pri.candidate_smac = 0;
+ qp->pri.candidate_smac_index = 0;
+ qp->pri.candidate_smac_port = 0;
+ }
+ if (qp->alt.candidate_smac) {
+ if (err) {
+ mlx4_unregister_mac(dev->dev, qp->alt.candidate_smac_port, qp->alt.candidate_smac);
+ } else {
+ if (qp->alt.smac)
+ mlx4_unregister_mac(dev->dev, qp->alt.smac_port, qp->alt.smac);
+ qp->alt.smac = qp->alt.candidate_smac;
+ qp->alt.smac_index = qp->alt.candidate_smac_index;
+ qp->alt.smac_port = qp->alt.candidate_smac_port;
+ }
+ qp->alt.candidate_smac = 0;
+ qp->alt.candidate_smac_index = 0;
+ qp->alt.candidate_smac_port = 0;
+ }
+
+ if (qp->pri.update_vid) {
+ if (err) {
+ if (qp->pri.candidate_vid < 0x1000)
+ mlx4_unregister_vlan(dev->dev, qp->pri.candidate_vlan_port,
+ qp->pri.candidate_vid);
+ } else {
+ if (qp->pri.vid < 0x1000)
+ mlx4_unregister_vlan(dev->dev, qp->pri.vlan_port,
+ qp->pri.vid);
+ qp->pri.vid = qp->pri.candidate_vid;
+ qp->pri.vlan_port = qp->pri.candidate_vlan_port;
+ qp->pri.vlan_index = qp->pri.candidate_vlan_index;
+ }
+ qp->pri.candidate_vid = 0xFFFF;
+ qp->pri.update_vid = 0;
+ }
+
+ if (qp->alt.update_vid) {
+ if (err) {
+ if (qp->alt.candidate_vid < 0x1000)
+ mlx4_unregister_vlan(dev->dev, qp->alt.candidate_vlan_port,
+ qp->alt.candidate_vid);
+ } else {
+ if (qp->alt.vid < 0x1000)
+ mlx4_unregister_vlan(dev->dev, qp->alt.vlan_port,
+ qp->alt.vid);
+ qp->alt.vid = qp->alt.candidate_vid;
+ qp->alt.vlan_port = qp->alt.candidate_vlan_port;
+ qp->alt.vlan_index = qp->alt.candidate_vlan_index;
+ }
+ qp->alt.candidate_vid = 0xFFFF;
+ qp->alt.update_vid = 0;
+ }
+
return err;
}
@@ -1842,9 +2043,9 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
{
struct ib_device *ib_dev = sqp->qp.ibqp.device;
struct mlx4_wqe_mlx_seg *mlx = wqe;
+ struct mlx4_wqe_ctrl_seg *ctrl = wqe;
struct mlx4_wqe_inline_seg *inl = wqe + sizeof *mlx;
struct mlx4_ib_ah *ah = to_mah(wr->wr.ud.ah);
- struct net_device *ndev;
union ib_gid sgid;
u16 pkey;
int send_size;
@@ -1868,12 +2069,11 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
/* When multi-function is enabled, the ib_core gid
* indexes don't necessarily match the hw ones, so
* we must use our own cache */
- sgid.global.subnet_prefix =
- to_mdev(ib_dev)->sriov.demux[sqp->qp.port - 1].
- subnet_prefix;
- sgid.global.interface_id =
- to_mdev(ib_dev)->sriov.demux[sqp->qp.port - 1].
- guid_cache[ah->av.ib.gid_index];
+ err = mlx4_get_roce_gid_from_slave(to_mdev(ib_dev)->dev,
+ be32_to_cpu(ah->av.ib.port_pd) >> 24,
+ ah->av.ib.gid_index, &sgid.raw[0]);
+ if (err)
+ return err;
} else {
err = ib_get_cached_gid(ib_dev,
be32_to_cpu(ah->av.ib.port_pd) >> 24,
@@ -1882,7 +2082,7 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
return err;
}
- if (ah->av.eth.vlan != 0xffff) {
+ if (ah->av.eth.vlan != cpu_to_be16(0xffff)) {
vlan = be16_to_cpu(ah->av.eth.vlan) & 0x0fff;
is_vlan = 1;
}
@@ -1902,6 +2102,9 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
sqp->ud_header.grh.flow_label =
ah->av.ib.sl_tclass_flowlabel & cpu_to_be32(0xfffff);
sqp->ud_header.grh.hop_limit = ah->av.ib.hop_limit;
+ if (is_eth)
+ memcpy(sqp->ud_header.grh.source_gid.raw, sgid.raw, 16);
+ else {
if (mlx4_is_mfunc(to_mdev(ib_dev)->dev)) {
/* When multi-function is enabled, the ib_core gid
* indexes don't necessarily match the hw ones, so
@@ -1917,6 +2120,7 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
be32_to_cpu(ah->av.ib.port_pd) >> 24,
ah->av.ib.gid_index,
&sqp->ud_header.grh.source_gid);
+ }
memcpy(sqp->ud_header.grh.destination_gid.raw,
ah->av.ib.dgid, 16);
}
@@ -1949,16 +2153,23 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
if (is_eth) {
u8 *smac;
+ struct in6_addr in6;
+
u16 pcp = (be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 29) << 13;
mlx->sched_prio = cpu_to_be16(pcp);
memcpy(sqp->ud_header.eth.dmac_h, ah->av.eth.mac, 6);
/* FIXME: cache smac value? */
- ndev = to_mdev(sqp->qp.ibqp.device)->iboe.netdevs[sqp->qp.port - 1];
- if (!ndev)
- return -ENODEV;
- smac = ndev->dev_addr;
+ memcpy(&ctrl->srcrb_flags16[0], ah->av.eth.mac, 2);
+ memcpy(&ctrl->imm, ah->av.eth.mac + 2, 4);
+ memcpy(&in6, sgid.raw, sizeof(in6));
+
+ if (!mlx4_is_mfunc(to_mdev(ib_dev)->dev))
+ smac = to_mdev(sqp->qp.ibqp.device)->
+ iboe.netdevs[sqp->qp.port - 1]->dev_addr;
+ else /* use the src mac of the tunnel */
+ smac = ah->av.eth.s_mac;
memcpy(sqp->ud_header.eth.smac_h, smac, 6);
if (!memcmp(sqp->ud_header.eth.smac_h, sqp->ud_header.eth.dmac_h, 6))
mlx->flags |= cpu_to_be32(MLX4_WQE_CTRL_FORCE_LOOPBACK);
@@ -2190,6 +2401,8 @@ static void build_tunnel_header(struct ib_send_wr *wr, void *wqe, unsigned *mlx_
hdr.remote_qpn = cpu_to_be32(wr->wr.ud.remote_qpn);
hdr.pkey_index = cpu_to_be16(wr->wr.ud.pkey_index);
hdr.qkey = cpu_to_be32(wr->wr.ud.remote_qkey);
+ memcpy(hdr.mac, ah->av.eth.mac, 6);
+ hdr.vlan = ah->av.eth.vlan;
spc = MLX4_INLINE_ALIGN -
((unsigned long) (inl + 1) & (MLX4_INLINE_ALIGN - 1));
diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c
index db2ea31df832..5a38e43eca65 100644
--- a/drivers/infiniband/hw/mlx4/sysfs.c
+++ b/drivers/infiniband/hw/mlx4/sysfs.c
@@ -627,6 +627,7 @@ static int register_one_pkey_tree(struct mlx4_ib_dev *dev, int slave)
int port;
struct kobject *p, *t;
struct mlx4_port *mport;
+ struct mlx4_active_ports actv_ports;
get_name(dev, name, slave, sizeof name);
@@ -649,7 +650,11 @@ static int register_one_pkey_tree(struct mlx4_ib_dev *dev, int slave)
goto err_ports;
}
+ actv_ports = mlx4_get_active_ports(dev->dev, slave);
+
for (port = 1; port <= dev->dev->caps.num_ports; ++port) {
+ if (!test_bit(port - 1, actv_ports.ports))
+ continue;
err = add_port(dev, port, slave);
if (err)
goto err_add;
diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c
index b1705ce6eb88..62bb6b49dc1d 100644
--- a/drivers/infiniband/hw/mlx5/cq.c
+++ b/drivers/infiniband/hw/mlx5/cq.c
@@ -366,6 +366,38 @@ static void free_cq_buf(struct mlx5_ib_dev *dev, struct mlx5_ib_cq_buf *buf)
mlx5_buf_free(&dev->mdev, &buf->buf);
}
+static void get_sig_err_item(struct mlx5_sig_err_cqe *cqe,
+ struct ib_sig_err *item)
+{
+ u16 syndrome = be16_to_cpu(cqe->syndrome);
+
+#define GUARD_ERR (1 << 13)
+#define APPTAG_ERR (1 << 12)
+#define REFTAG_ERR (1 << 11)
+
+ if (syndrome & GUARD_ERR) {
+ item->err_type = IB_SIG_BAD_GUARD;
+ item->expected = be32_to_cpu(cqe->expected_trans_sig) >> 16;
+ item->actual = be32_to_cpu(cqe->actual_trans_sig) >> 16;
+ } else
+ if (syndrome & REFTAG_ERR) {
+ item->err_type = IB_SIG_BAD_REFTAG;
+ item->expected = be32_to_cpu(cqe->expected_reftag);
+ item->actual = be32_to_cpu(cqe->actual_reftag);
+ } else
+ if (syndrome & APPTAG_ERR) {
+ item->err_type = IB_SIG_BAD_APPTAG;
+ item->expected = be32_to_cpu(cqe->expected_trans_sig) & 0xffff;
+ item->actual = be32_to_cpu(cqe->actual_trans_sig) & 0xffff;
+ } else {
+ pr_err("Got signature completion error with bad syndrome %04x\n",
+ syndrome);
+ }
+
+ item->sig_err_offset = be64_to_cpu(cqe->err_offset);
+ item->key = be32_to_cpu(cqe->mkey);
+}
+
static int mlx5_poll_one(struct mlx5_ib_cq *cq,
struct mlx5_ib_qp **cur_qp,
struct ib_wc *wc)
@@ -375,6 +407,9 @@ static int mlx5_poll_one(struct mlx5_ib_cq *cq,
struct mlx5_cqe64 *cqe64;
struct mlx5_core_qp *mqp;
struct mlx5_ib_wq *wq;
+ struct mlx5_sig_err_cqe *sig_err_cqe;
+ struct mlx5_core_mr *mmr;
+ struct mlx5_ib_mr *mr;
uint8_t opcode;
uint32_t qpn;
u16 wqe_ctr;
@@ -475,6 +510,33 @@ repoll:
}
}
break;
+ case MLX5_CQE_SIG_ERR:
+ sig_err_cqe = (struct mlx5_sig_err_cqe *)cqe64;
+
+ read_lock(&dev->mdev.priv.mr_table.lock);
+ mmr = __mlx5_mr_lookup(&dev->mdev,
+ mlx5_base_mkey(be32_to_cpu(sig_err_cqe->mkey)));
+ if (unlikely(!mmr)) {
+ read_unlock(&dev->mdev.priv.mr_table.lock);
+ mlx5_ib_warn(dev, "CQE@CQ %06x for unknown MR %6x\n",
+ cq->mcq.cqn, be32_to_cpu(sig_err_cqe->mkey));
+ return -EINVAL;
+ }
+
+ mr = to_mibmr(mmr);
+ get_sig_err_item(sig_err_cqe, &mr->sig->err_item);
+ mr->sig->sig_err_exists = true;
+ mr->sig->sigerr_count++;
+
+ mlx5_ib_warn(dev, "CQN: 0x%x Got SIGERR on key: 0x%x err_type %x err_offset %llx expected %x actual %x\n",
+ cq->mcq.cqn, mr->sig->err_item.key,
+ mr->sig->err_item.err_type,
+ mr->sig->err_item.sig_err_offset,
+ mr->sig->err_item.expected,
+ mr->sig->err_item.actual);
+
+ read_unlock(&dev->mdev.priv.mr_table.lock);
+ goto repoll;
}
return 0;
diff --git a/drivers/infiniband/hw/mlx5/doorbell.c b/drivers/infiniband/hw/mlx5/doorbell.c
index 256a23344f28..ece028fc47d6 100644
--- a/drivers/infiniband/hw/mlx5/doorbell.c
+++ b/drivers/infiniband/hw/mlx5/doorbell.c
@@ -47,7 +47,6 @@ int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt,
struct mlx5_db *db)
{
struct mlx5_ib_user_db_page *page;
- struct ib_umem_chunk *chunk;
int err = 0;
mutex_lock(&context->db_page_mutex);
@@ -75,8 +74,7 @@ int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt,
list_add(&page->list, &context->db_page_list);
found:
- chunk = list_entry(page->umem->chunk_list.next, struct ib_umem_chunk, list);
- db->dma = sg_dma_address(chunk->page_list) + (virt & ~PAGE_MASK);
+ db->dma = sg_dma_address(page->umem->sg_head.sgl) + (virt & ~PAGE_MASK);
db->u.user_page = page;
++page->refcnt;
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index bf900579ac08..fa6dc870adae 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -273,6 +273,15 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
if (flags & MLX5_DEV_CAP_FLAG_XRC)
props->device_cap_flags |= IB_DEVICE_XRC;
props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS;
+ if (flags & MLX5_DEV_CAP_FLAG_SIG_HAND_OVER) {
+ props->device_cap_flags |= IB_DEVICE_SIGNATURE_HANDOVER;
+ /* At this stage no support for signature handover */
+ props->sig_prot_cap = IB_PROT_T10DIF_TYPE_1 |
+ IB_PROT_T10DIF_TYPE_2 |
+ IB_PROT_T10DIF_TYPE_3;
+ props->sig_guard_cap = IB_GUARD_T10DIF_CRC |
+ IB_GUARD_T10DIF_CSUM;
+ }
props->vendor_id = be32_to_cpup((__be32 *)(out_mad->data + 36)) &
0xffffff;
@@ -1423,12 +1432,15 @@ static int init_one(struct pci_dev *pdev,
dev->ib_dev.get_dma_mr = mlx5_ib_get_dma_mr;
dev->ib_dev.reg_user_mr = mlx5_ib_reg_user_mr;
dev->ib_dev.dereg_mr = mlx5_ib_dereg_mr;
+ dev->ib_dev.destroy_mr = mlx5_ib_destroy_mr;
dev->ib_dev.attach_mcast = mlx5_ib_mcg_attach;
dev->ib_dev.detach_mcast = mlx5_ib_mcg_detach;
dev->ib_dev.process_mad = mlx5_ib_process_mad;
+ dev->ib_dev.create_mr = mlx5_ib_create_mr;
dev->ib_dev.alloc_fast_reg_mr = mlx5_ib_alloc_fast_reg_mr;
dev->ib_dev.alloc_fast_reg_page_list = mlx5_ib_alloc_fast_reg_page_list;
dev->ib_dev.free_fast_reg_page_list = mlx5_ib_free_fast_reg_page_list;
+ dev->ib_dev.check_mr_status = mlx5_ib_check_mr_status;
if (mdev->caps.flags & MLX5_DEV_CAP_FLAG_XRC) {
dev->ib_dev.alloc_xrcd = mlx5_ib_alloc_xrcd;
diff --git a/drivers/infiniband/hw/mlx5/mem.c b/drivers/infiniband/hw/mlx5/mem.c
index 3a5322870b96..8499aec94db6 100644
--- a/drivers/infiniband/hw/mlx5/mem.c
+++ b/drivers/infiniband/hw/mlx5/mem.c
@@ -44,16 +44,17 @@
void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift,
int *ncont, int *order)
{
- struct ib_umem_chunk *chunk;
unsigned long tmp;
unsigned long m;
- int i, j, k;
+ int i, k;
u64 base = 0;
int p = 0;
int skip;
int mask;
u64 len;
u64 pfn;
+ struct scatterlist *sg;
+ int entry;
addr = addr >> PAGE_SHIFT;
tmp = (unsigned long)addr;
@@ -61,32 +62,31 @@ void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift,
skip = 1 << m;
mask = skip - 1;
i = 0;
- list_for_each_entry(chunk, &umem->chunk_list, list)
- for (j = 0; j < chunk->nmap; j++) {
- len = sg_dma_len(&chunk->page_list[j]) >> PAGE_SHIFT;
- pfn = sg_dma_address(&chunk->page_list[j]) >> PAGE_SHIFT;
- for (k = 0; k < len; k++) {
- if (!(i & mask)) {
- tmp = (unsigned long)pfn;
- m = min(m, find_first_bit(&tmp, sizeof(tmp)));
+ for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
+ len = sg_dma_len(sg) >> PAGE_SHIFT;
+ pfn = sg_dma_address(sg) >> PAGE_SHIFT;
+ for (k = 0; k < len; k++) {
+ if (!(i & mask)) {
+ tmp = (unsigned long)pfn;
+ m = min(m, find_first_bit(&tmp, sizeof(tmp)));
+ skip = 1 << m;
+ mask = skip - 1;
+ base = pfn;
+ p = 0;
+ } else {
+ if (base + p != pfn) {
+ tmp = (unsigned long)p;
+ m = find_first_bit(&tmp, sizeof(tmp));
skip = 1 << m;
mask = skip - 1;
base = pfn;
p = 0;
- } else {
- if (base + p != pfn) {
- tmp = (unsigned long)p;
- m = find_first_bit(&tmp, sizeof(tmp));
- skip = 1 << m;
- mask = skip - 1;
- base = pfn;
- p = 0;
- }
}
- p++;
- i++;
}
+ p++;
+ i++;
}
+ }
if (i) {
m = min_t(unsigned long, ilog2(roundup_pow_of_two(i)), m);
@@ -112,32 +112,32 @@ void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
{
int shift = page_shift - PAGE_SHIFT;
int mask = (1 << shift) - 1;
- struct ib_umem_chunk *chunk;
- int i, j, k;
+ int i, k;
u64 cur = 0;
u64 base;
int len;
+ struct scatterlist *sg;
+ int entry;
i = 0;
- list_for_each_entry(chunk, &umem->chunk_list, list)
- for (j = 0; j < chunk->nmap; j++) {
- len = sg_dma_len(&chunk->page_list[j]) >> PAGE_SHIFT;
- base = sg_dma_address(&chunk->page_list[j]);
- for (k = 0; k < len; k++) {
- if (!(i & mask)) {
- cur = base + (k << PAGE_SHIFT);
- if (umr)
- cur |= 3;
+ for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
+ len = sg_dma_len(sg) >> PAGE_SHIFT;
+ base = sg_dma_address(sg);
+ for (k = 0; k < len; k++) {
+ if (!(i & mask)) {
+ cur = base + (k << PAGE_SHIFT);
+ if (umr)
+ cur |= 3;
- pas[i >> shift] = cpu_to_be64(cur);
- mlx5_ib_dbg(dev, "pas[%d] 0x%llx\n",
- i >> shift, be64_to_cpu(pas[i >> shift]));
- } else
- mlx5_ib_dbg(dev, "=====> 0x%llx\n",
- base + (k << PAGE_SHIFT));
- i++;
- }
+ pas[i >> shift] = cpu_to_be64(cur);
+ mlx5_ib_dbg(dev, "pas[%d] 0x%llx\n",
+ i >> shift, be64_to_cpu(pas[i >> shift]));
+ } else
+ mlx5_ib_dbg(dev, "=====> 0x%llx\n",
+ base + (k << PAGE_SHIFT));
+ i++;
}
+ }
}
int mlx5_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset)
diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
index 389e31965773..50541586e0a6 100644
--- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
+++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
@@ -189,6 +189,9 @@ struct mlx5_ib_qp {
int create_type;
u32 pa_lkey;
+
+ /* Store signature errors */
+ bool signature_en;
};
struct mlx5_ib_cq_buf {
@@ -265,6 +268,7 @@ struct mlx5_ib_mr {
enum ib_wc_status status;
struct mlx5_ib_dev *dev;
struct mlx5_create_mkey_mbox_out out;
+ struct mlx5_core_sig_ctx *sig;
};
struct mlx5_ib_fast_reg_page_list {
@@ -396,6 +400,11 @@ static inline struct mlx5_ib_qp *to_mibqp(struct mlx5_core_qp *mqp)
return container_of(mqp, struct mlx5_ib_qp, mqp);
}
+static inline struct mlx5_ib_mr *to_mibmr(struct mlx5_core_mr *mmr)
+{
+ return container_of(mmr, struct mlx5_ib_mr, mmr);
+}
+
static inline struct mlx5_ib_pd *to_mpd(struct ib_pd *ibpd)
{
return container_of(ibpd, struct mlx5_ib_pd, ibpd);
@@ -495,6 +504,9 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u64 virt_addr, int access_flags,
struct ib_udata *udata);
int mlx5_ib_dereg_mr(struct ib_mr *ibmr);
+int mlx5_ib_destroy_mr(struct ib_mr *ibmr);
+struct ib_mr *mlx5_ib_create_mr(struct ib_pd *pd,
+ struct ib_mr_init_attr *mr_init_attr);
struct ib_mr *mlx5_ib_alloc_fast_reg_mr(struct ib_pd *pd,
int max_page_list_len);
struct ib_fast_reg_page_list *mlx5_ib_alloc_fast_reg_page_list(struct ib_device *ibdev,
@@ -530,6 +542,8 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev);
int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev);
int mlx5_mr_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift);
void mlx5_umr_cq_handler(struct ib_cq *cq, void *cq_context);
+int mlx5_ib_check_mr_status(struct ib_mr *ibmr, u32 check_mask,
+ struct ib_mr_status *mr_status);
static inline void init_query_mad(struct ib_smp *mad)
{
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
index 7c95ca1f0c25..81392b26d078 100644
--- a/drivers/infiniband/hw/mlx5/mr.c
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -992,6 +992,122 @@ int mlx5_ib_dereg_mr(struct ib_mr *ibmr)
return 0;
}
+struct ib_mr *mlx5_ib_create_mr(struct ib_pd *pd,
+ struct ib_mr_init_attr *mr_init_attr)
+{
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
+ struct mlx5_create_mkey_mbox_in *in;
+ struct mlx5_ib_mr *mr;
+ int access_mode, err;
+ int ndescs = roundup(mr_init_attr->max_reg_descriptors, 4);
+
+ mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+ if (!mr)
+ return ERR_PTR(-ENOMEM);
+
+ in = kzalloc(sizeof(*in), GFP_KERNEL);
+ if (!in) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ in->seg.status = 1 << 6; /* free */
+ in->seg.xlt_oct_size = cpu_to_be32(ndescs);
+ in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
+ in->seg.flags_pd = cpu_to_be32(to_mpd(pd)->pdn);
+ access_mode = MLX5_ACCESS_MODE_MTT;
+
+ if (mr_init_attr->flags & IB_MR_SIGNATURE_EN) {
+ u32 psv_index[2];
+
+ in->seg.flags_pd = cpu_to_be32(be32_to_cpu(in->seg.flags_pd) |
+ MLX5_MKEY_BSF_EN);
+ in->seg.bsfs_octo_size = cpu_to_be32(MLX5_MKEY_BSF_OCTO_SIZE);
+ mr->sig = kzalloc(sizeof(*mr->sig), GFP_KERNEL);
+ if (!mr->sig) {
+ err = -ENOMEM;
+ goto err_free_in;
+ }
+
+ /* create mem & wire PSVs */
+ err = mlx5_core_create_psv(&dev->mdev, to_mpd(pd)->pdn,
+ 2, psv_index);
+ if (err)
+ goto err_free_sig;
+
+ access_mode = MLX5_ACCESS_MODE_KLM;
+ mr->sig->psv_memory.psv_idx = psv_index[0];
+ mr->sig->psv_wire.psv_idx = psv_index[1];
+
+ mr->sig->sig_status_checked = true;
+ mr->sig->sig_err_exists = false;
+ /* Next UMR, Arm SIGERR */
+ ++mr->sig->sigerr_count;
+ }
+
+ in->seg.flags = MLX5_PERM_UMR_EN | access_mode;
+ err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in, sizeof(*in),
+ NULL, NULL, NULL);
+ if (err)
+ goto err_destroy_psv;
+
+ mr->ibmr.lkey = mr->mmr.key;
+ mr->ibmr.rkey = mr->mmr.key;
+ mr->umem = NULL;
+ kfree(in);
+
+ return &mr->ibmr;
+
+err_destroy_psv:
+ if (mr->sig) {
+ if (mlx5_core_destroy_psv(&dev->mdev,
+ mr->sig->psv_memory.psv_idx))
+ mlx5_ib_warn(dev, "failed to destroy mem psv %d\n",
+ mr->sig->psv_memory.psv_idx);
+ if (mlx5_core_destroy_psv(&dev->mdev,
+ mr->sig->psv_wire.psv_idx))
+ mlx5_ib_warn(dev, "failed to destroy wire psv %d\n",
+ mr->sig->psv_wire.psv_idx);
+ }
+err_free_sig:
+ kfree(mr->sig);
+err_free_in:
+ kfree(in);
+err_free:
+ kfree(mr);
+ return ERR_PTR(err);
+}
+
+int mlx5_ib_destroy_mr(struct ib_mr *ibmr)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibmr->device);
+ struct mlx5_ib_mr *mr = to_mmr(ibmr);
+ int err;
+
+ if (mr->sig) {
+ if (mlx5_core_destroy_psv(&dev->mdev,
+ mr->sig->psv_memory.psv_idx))
+ mlx5_ib_warn(dev, "failed to destroy mem psv %d\n",
+ mr->sig->psv_memory.psv_idx);
+ if (mlx5_core_destroy_psv(&dev->mdev,
+ mr->sig->psv_wire.psv_idx))
+ mlx5_ib_warn(dev, "failed to destroy wire psv %d\n",
+ mr->sig->psv_wire.psv_idx);
+ kfree(mr->sig);
+ }
+
+ err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr);
+ if (err) {
+ mlx5_ib_warn(dev, "failed to destroy mkey 0x%x (%d)\n",
+ mr->mmr.key, err);
+ return err;
+ }
+
+ kfree(mr);
+
+ return err;
+}
+
struct ib_mr *mlx5_ib_alloc_fast_reg_mr(struct ib_pd *pd,
int max_page_list_len)
{
@@ -1077,3 +1193,44 @@ void mlx5_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list)
kfree(mfrpl->ibfrpl.page_list);
kfree(mfrpl);
}
+
+int mlx5_ib_check_mr_status(struct ib_mr *ibmr, u32 check_mask,
+ struct ib_mr_status *mr_status)
+{
+ struct mlx5_ib_mr *mmr = to_mmr(ibmr);
+ int ret = 0;
+
+ if (check_mask & ~IB_MR_CHECK_SIG_STATUS) {
+ pr_err("Invalid status check mask\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mr_status->fail_status = 0;
+ if (check_mask & IB_MR_CHECK_SIG_STATUS) {
+ if (!mmr->sig) {
+ ret = -EINVAL;
+ pr_err("signature status check requested on a non-signature enabled MR\n");
+ goto done;
+ }
+
+ mmr->sig->sig_status_checked = true;
+ if (!mmr->sig->sig_err_exists)
+ goto done;
+
+ if (ibmr->lkey == mmr->sig->err_item.key)
+ memcpy(&mr_status->sig_err, &mmr->sig->err_item,
+ sizeof(mr_status->sig_err));
+ else {
+ mr_status->sig_err.err_type = IB_SIG_BAD_GUARD;
+ mr_status->sig_err.sig_err_offset = 0;
+ mr_status->sig_err.key = mmr->sig->err_item.key;
+ }
+
+ mmr->sig->sig_err_exists = false;
+ mr_status->fail_status |= IB_MR_CHECK_SIG_STATUS;
+ }
+
+done:
+ return ret;
+}
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index 7dfe8a1c84cf..ae788d27b93f 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -256,8 +256,11 @@ static int calc_send_wqe(struct ib_qp_init_attr *attr)
}
size += attr->cap.max_send_sge * sizeof(struct mlx5_wqe_data_seg);
-
- return ALIGN(max_t(int, inl_size, size), MLX5_SEND_WQE_BB);
+ if (attr->create_flags & IB_QP_CREATE_SIGNATURE_EN &&
+ ALIGN(max_t(int, inl_size, size), MLX5_SEND_WQE_BB) < MLX5_SIG_WQE_SIZE)
+ return MLX5_SIG_WQE_SIZE;
+ else
+ return ALIGN(max_t(int, inl_size, size), MLX5_SEND_WQE_BB);
}
static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr,
@@ -284,6 +287,9 @@ static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr,
sizeof(struct mlx5_wqe_inline_seg);
attr->cap.max_inline_data = qp->max_inline_data;
+ if (attr->create_flags & IB_QP_CREATE_SIGNATURE_EN)
+ qp->signature_en = true;
+
wq_size = roundup_pow_of_two(attr->cap.max_send_wr * wqe_size);
qp->sq.wqe_cnt = wq_size / MLX5_SEND_WQE_BB;
if (qp->sq.wqe_cnt > dev->mdev.caps.max_wqes) {
@@ -665,7 +671,7 @@ static int create_kernel_qp(struct mlx5_ib_dev *dev,
int err;
uuari = &dev->mdev.priv.uuari;
- if (init_attr->create_flags)
+ if (init_attr->create_flags & ~IB_QP_CREATE_SIGNATURE_EN)
return -EINVAL;
if (init_attr->qp_type == MLX5_IB_QPT_REG_UMR)
@@ -1771,6 +1777,27 @@ static __be64 frwr_mkey_mask(void)
return cpu_to_be64(result);
}
+static __be64 sig_mkey_mask(void)
+{
+ u64 result;
+
+ result = MLX5_MKEY_MASK_LEN |
+ MLX5_MKEY_MASK_PAGE_SIZE |
+ MLX5_MKEY_MASK_START_ADDR |
+ MLX5_MKEY_MASK_EN_SIGERR |
+ MLX5_MKEY_MASK_EN_RINVAL |
+ MLX5_MKEY_MASK_KEY |
+ MLX5_MKEY_MASK_LR |
+ MLX5_MKEY_MASK_LW |
+ MLX5_MKEY_MASK_RR |
+ MLX5_MKEY_MASK_RW |
+ MLX5_MKEY_MASK_SMALL_FENCE |
+ MLX5_MKEY_MASK_FREE |
+ MLX5_MKEY_MASK_BSF_EN;
+
+ return cpu_to_be64(result);
+}
+
static void set_frwr_umr_segment(struct mlx5_wqe_umr_ctrl_seg *umr,
struct ib_send_wr *wr, int li)
{
@@ -1826,7 +1853,7 @@ static u8 get_umr_flags(int acc)
(acc & IB_ACCESS_REMOTE_WRITE ? MLX5_PERM_REMOTE_WRITE : 0) |
(acc & IB_ACCESS_REMOTE_READ ? MLX5_PERM_REMOTE_READ : 0) |
(acc & IB_ACCESS_LOCAL_WRITE ? MLX5_PERM_LOCAL_WRITE : 0) |
- MLX5_PERM_LOCAL_READ | MLX5_PERM_UMR_EN | MLX5_ACCESS_MODE_MTT;
+ MLX5_PERM_LOCAL_READ | MLX5_PERM_UMR_EN;
}
static void set_mkey_segment(struct mlx5_mkey_seg *seg, struct ib_send_wr *wr,
@@ -1838,7 +1865,8 @@ static void set_mkey_segment(struct mlx5_mkey_seg *seg, struct ib_send_wr *wr,
return;
}
- seg->flags = get_umr_flags(wr->wr.fast_reg.access_flags);
+ seg->flags = get_umr_flags(wr->wr.fast_reg.access_flags) |
+ MLX5_ACCESS_MODE_MTT;
*writ = seg->flags & (MLX5_PERM_LOCAL_WRITE | IB_ACCESS_REMOTE_WRITE);
seg->qpn_mkey7_0 = cpu_to_be32((wr->wr.fast_reg.rkey & 0xff) | 0xffffff00);
seg->flags_pd = cpu_to_be32(MLX5_MKEY_REMOTE_INVAL);
@@ -1954,6 +1982,342 @@ static int set_data_inl_seg(struct mlx5_ib_qp *qp, struct ib_send_wr *wr,
return 0;
}
+static u16 prot_field_size(enum ib_signature_type type)
+{
+ switch (type) {
+ case IB_SIG_TYPE_T10_DIF:
+ return MLX5_DIF_SIZE;
+ default:
+ return 0;
+ }
+}
+
+static u8 bs_selector(int block_size)
+{
+ switch (block_size) {
+ case 512: return 0x1;
+ case 520: return 0x2;
+ case 4096: return 0x3;
+ case 4160: return 0x4;
+ case 1073741824: return 0x5;
+ default: return 0;
+ }
+}
+
+static int format_selector(struct ib_sig_attrs *attr,
+ struct ib_sig_domain *domain,
+ int *selector)
+{
+
+#define FORMAT_DIF_NONE 0
+#define FORMAT_DIF_CRC_INC 8
+#define FORMAT_DIF_CRC_NO_INC 12
+#define FORMAT_DIF_CSUM_INC 13
+#define FORMAT_DIF_CSUM_NO_INC 14
+
+ switch (domain->sig.dif.type) {
+ case IB_T10DIF_NONE:
+ /* No DIF */
+ *selector = FORMAT_DIF_NONE;
+ break;
+ case IB_T10DIF_TYPE1: /* Fall through */
+ case IB_T10DIF_TYPE2:
+ switch (domain->sig.dif.bg_type) {
+ case IB_T10DIF_CRC:
+ *selector = FORMAT_DIF_CRC_INC;
+ break;
+ case IB_T10DIF_CSUM:
+ *selector = FORMAT_DIF_CSUM_INC;
+ break;
+ default:
+ return 1;
+ }
+ break;
+ case IB_T10DIF_TYPE3:
+ switch (domain->sig.dif.bg_type) {
+ case IB_T10DIF_CRC:
+ *selector = domain->sig.dif.type3_inc_reftag ?
+ FORMAT_DIF_CRC_INC :
+ FORMAT_DIF_CRC_NO_INC;
+ break;
+ case IB_T10DIF_CSUM:
+ *selector = domain->sig.dif.type3_inc_reftag ?
+ FORMAT_DIF_CSUM_INC :
+ FORMAT_DIF_CSUM_NO_INC;
+ break;
+ default:
+ return 1;
+ }
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+static int mlx5_set_bsf(struct ib_mr *sig_mr,
+ struct ib_sig_attrs *sig_attrs,
+ struct mlx5_bsf *bsf, u32 data_size)
+{
+ struct mlx5_core_sig_ctx *msig = to_mmr(sig_mr)->sig;
+ struct mlx5_bsf_basic *basic = &bsf->basic;
+ struct ib_sig_domain *mem = &sig_attrs->mem;
+ struct ib_sig_domain *wire = &sig_attrs->wire;
+ int ret, selector;
+
+ switch (sig_attrs->mem.sig_type) {
+ case IB_SIG_TYPE_T10_DIF:
+ if (sig_attrs->wire.sig_type != IB_SIG_TYPE_T10_DIF)
+ return -EINVAL;
+
+ /* Input domain check byte mask */
+ basic->check_byte_mask = sig_attrs->check_mask;
+ if (mem->sig.dif.pi_interval == wire->sig.dif.pi_interval &&
+ mem->sig.dif.type == wire->sig.dif.type) {
+ /* Same block structure */
+ basic->bsf_size_sbs = 1 << 4;
+ if (mem->sig.dif.bg_type == wire->sig.dif.bg_type)
+ basic->wire.copy_byte_mask = 0xff;
+ else
+ basic->wire.copy_byte_mask = 0x3f;
+ } else
+ basic->wire.bs_selector = bs_selector(wire->sig.dif.pi_interval);
+
+ basic->mem.bs_selector = bs_selector(mem->sig.dif.pi_interval);
+ basic->raw_data_size = cpu_to_be32(data_size);
+
+ ret = format_selector(sig_attrs, mem, &selector);
+ if (ret)
+ return -EINVAL;
+ basic->m_bfs_psv = cpu_to_be32(selector << 24 |
+ msig->psv_memory.psv_idx);
+
+ ret = format_selector(sig_attrs, wire, &selector);
+ if (ret)
+ return -EINVAL;
+ basic->w_bfs_psv = cpu_to_be32(selector << 24 |
+ msig->psv_wire.psv_idx);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int set_sig_data_segment(struct ib_send_wr *wr, struct mlx5_ib_qp *qp,
+ void **seg, int *size)
+{
+ struct ib_sig_attrs *sig_attrs = wr->wr.sig_handover.sig_attrs;
+ struct ib_mr *sig_mr = wr->wr.sig_handover.sig_mr;
+ struct mlx5_bsf *bsf;
+ u32 data_len = wr->sg_list->length;
+ u32 data_key = wr->sg_list->lkey;
+ u64 data_va = wr->sg_list->addr;
+ int ret;
+ int wqe_size;
+
+ if (!wr->wr.sig_handover.prot) {
+ /**
+ * Source domain doesn't contain signature information
+ * So need construct:
+ * ------------------
+ * | data_klm |
+ * ------------------
+ * | BSF |
+ * ------------------
+ **/
+ struct mlx5_klm *data_klm = *seg;
+
+ data_klm->bcount = cpu_to_be32(data_len);
+ data_klm->key = cpu_to_be32(data_key);
+ data_klm->va = cpu_to_be64(data_va);
+ wqe_size = ALIGN(sizeof(*data_klm), 64);
+ } else {
+ /**
+ * Source domain contains signature information
+ * So need construct a strided block format:
+ * ---------------------------
+ * | stride_block_ctrl |
+ * ---------------------------
+ * | data_klm |
+ * ---------------------------
+ * | prot_klm |
+ * ---------------------------
+ * | BSF |
+ * ---------------------------
+ **/
+ struct mlx5_stride_block_ctrl_seg *sblock_ctrl;
+ struct mlx5_stride_block_entry *data_sentry;
+ struct mlx5_stride_block_entry *prot_sentry;
+ u32 prot_key = wr->wr.sig_handover.prot->lkey;
+ u64 prot_va = wr->wr.sig_handover.prot->addr;
+ u16 block_size = sig_attrs->mem.sig.dif.pi_interval;
+ int prot_size;
+
+ sblock_ctrl = *seg;
+ data_sentry = (void *)sblock_ctrl + sizeof(*sblock_ctrl);
+ prot_sentry = (void *)data_sentry + sizeof(*data_sentry);
+
+ prot_size = prot_field_size(sig_attrs->mem.sig_type);
+ if (!prot_size) {
+ pr_err("Bad block size given: %u\n", block_size);
+ return -EINVAL;
+ }
+ sblock_ctrl->bcount_per_cycle = cpu_to_be32(block_size +
+ prot_size);
+ sblock_ctrl->op = cpu_to_be32(MLX5_STRIDE_BLOCK_OP);
+ sblock_ctrl->repeat_count = cpu_to_be32(data_len / block_size);
+ sblock_ctrl->num_entries = cpu_to_be16(2);
+
+ data_sentry->bcount = cpu_to_be16(block_size);
+ data_sentry->key = cpu_to_be32(data_key);
+ data_sentry->va = cpu_to_be64(data_va);
+ prot_sentry->bcount = cpu_to_be16(prot_size);
+ prot_sentry->key = cpu_to_be32(prot_key);
+
+ if (prot_key == data_key && prot_va == data_va) {
+ /**
+ * The data and protection are interleaved
+ * in a single memory region
+ **/
+ prot_sentry->va = cpu_to_be64(data_va + block_size);
+ prot_sentry->stride = cpu_to_be16(block_size + prot_size);
+ data_sentry->stride = prot_sentry->stride;
+ } else {
+ /* The data and protection are two different buffers */
+ prot_sentry->va = cpu_to_be64(prot_va);
+ data_sentry->stride = cpu_to_be16(block_size);
+ prot_sentry->stride = cpu_to_be16(prot_size);
+ }
+ wqe_size = ALIGN(sizeof(*sblock_ctrl) + sizeof(*data_sentry) +
+ sizeof(*prot_sentry), 64);
+ }
+
+ *seg += wqe_size;
+ *size += wqe_size / 16;
+ if (unlikely((*seg == qp->sq.qend)))
+ *seg = mlx5_get_send_wqe(qp, 0);
+
+ bsf = *seg;
+ ret = mlx5_set_bsf(sig_mr, sig_attrs, bsf, data_len);
+ if (ret)
+ return -EINVAL;
+
+ *seg += sizeof(*bsf);
+ *size += sizeof(*bsf) / 16;
+ if (unlikely((*seg == qp->sq.qend)))
+ *seg = mlx5_get_send_wqe(qp, 0);
+
+ return 0;
+}
+
+static void set_sig_mkey_segment(struct mlx5_mkey_seg *seg,
+ struct ib_send_wr *wr, u32 nelements,
+ u32 length, u32 pdn)
+{
+ struct ib_mr *sig_mr = wr->wr.sig_handover.sig_mr;
+ u32 sig_key = sig_mr->rkey;
+ u8 sigerr = to_mmr(sig_mr)->sig->sigerr_count & 1;
+
+ memset(seg, 0, sizeof(*seg));
+
+ seg->flags = get_umr_flags(wr->wr.sig_handover.access_flags) |
+ MLX5_ACCESS_MODE_KLM;
+ seg->qpn_mkey7_0 = cpu_to_be32((sig_key & 0xff) | 0xffffff00);
+ seg->flags_pd = cpu_to_be32(MLX5_MKEY_REMOTE_INVAL | sigerr << 26 |
+ MLX5_MKEY_BSF_EN | pdn);
+ seg->len = cpu_to_be64(length);
+ seg->xlt_oct_size = cpu_to_be32(be16_to_cpu(get_klm_octo(nelements)));
+ seg->bsfs_octo_size = cpu_to_be32(MLX5_MKEY_BSF_OCTO_SIZE);
+}
+
+static void set_sig_umr_segment(struct mlx5_wqe_umr_ctrl_seg *umr,
+ struct ib_send_wr *wr, u32 nelements)
+{
+ memset(umr, 0, sizeof(*umr));
+
+ umr->flags = MLX5_FLAGS_INLINE | MLX5_FLAGS_CHECK_FREE;
+ umr->klm_octowords = get_klm_octo(nelements);
+ umr->bsf_octowords = cpu_to_be16(MLX5_MKEY_BSF_OCTO_SIZE);
+ umr->mkey_mask = sig_mkey_mask();
+}
+
+
+static int set_sig_umr_wr(struct ib_send_wr *wr, struct mlx5_ib_qp *qp,
+ void **seg, int *size)
+{
+ struct mlx5_ib_mr *sig_mr = to_mmr(wr->wr.sig_handover.sig_mr);
+ u32 pdn = get_pd(qp)->pdn;
+ u32 klm_oct_size;
+ int region_len, ret;
+
+ if (unlikely(wr->num_sge != 1) ||
+ unlikely(wr->wr.sig_handover.access_flags &
+ IB_ACCESS_REMOTE_ATOMIC) ||
+ unlikely(!sig_mr->sig) || unlikely(!qp->signature_en) ||
+ unlikely(!sig_mr->sig->sig_status_checked))
+ return -EINVAL;
+
+ /* length of the protected region, data + protection */
+ region_len = wr->sg_list->length;
+ if (wr->wr.sig_handover.prot)
+ region_len += wr->wr.sig_handover.prot->length;
+
+ /**
+ * KLM octoword size - if protection was provided
+ * then we use strided block format (3 octowords),
+ * else we use single KLM (1 octoword)
+ **/
+ klm_oct_size = wr->wr.sig_handover.prot ? 3 : 1;
+
+ set_sig_umr_segment(*seg, wr, klm_oct_size);
+ *seg += sizeof(struct mlx5_wqe_umr_ctrl_seg);
+ *size += sizeof(struct mlx5_wqe_umr_ctrl_seg) / 16;
+ if (unlikely((*seg == qp->sq.qend)))
+ *seg = mlx5_get_send_wqe(qp, 0);
+
+ set_sig_mkey_segment(*seg, wr, klm_oct_size, region_len, pdn);
+ *seg += sizeof(struct mlx5_mkey_seg);
+ *size += sizeof(struct mlx5_mkey_seg) / 16;
+ if (unlikely((*seg == qp->sq.qend)))
+ *seg = mlx5_get_send_wqe(qp, 0);
+
+ ret = set_sig_data_segment(wr, qp, seg, size);
+ if (ret)
+ return ret;
+
+ sig_mr->sig->sig_status_checked = false;
+ return 0;
+}
+
+static int set_psv_wr(struct ib_sig_domain *domain,
+ u32 psv_idx, void **seg, int *size)
+{
+ struct mlx5_seg_set_psv *psv_seg = *seg;
+
+ memset(psv_seg, 0, sizeof(*psv_seg));
+ psv_seg->psv_num = cpu_to_be32(psv_idx);
+ switch (domain->sig_type) {
+ case IB_SIG_TYPE_T10_DIF:
+ psv_seg->transient_sig = cpu_to_be32(domain->sig.dif.bg << 16 |
+ domain->sig.dif.app_tag);
+ psv_seg->ref_tag = cpu_to_be32(domain->sig.dif.ref_tag);
+
+ *seg += sizeof(*psv_seg);
+ *size += sizeof(*psv_seg) / 16;
+ break;
+
+ default:
+ pr_err("Bad signature type given.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
static int set_frwr_li_wr(void **seg, struct ib_send_wr *wr, int *size,
struct mlx5_core_dev *mdev, struct mlx5_ib_pd *pd, struct mlx5_ib_qp *qp)
{
@@ -2041,6 +2405,59 @@ static u8 get_fence(u8 fence, struct ib_send_wr *wr)
}
}
+static int begin_wqe(struct mlx5_ib_qp *qp, void **seg,
+ struct mlx5_wqe_ctrl_seg **ctrl,
+ struct ib_send_wr *wr, int *idx,
+ int *size, int nreq)
+{
+ int err = 0;
+
+ if (unlikely(mlx5_wq_overflow(&qp->sq, nreq, qp->ibqp.send_cq))) {
+ err = -ENOMEM;
+ return err;
+ }
+
+ *idx = qp->sq.cur_post & (qp->sq.wqe_cnt - 1);
+ *seg = mlx5_get_send_wqe(qp, *idx);
+ *ctrl = *seg;
+ *(uint32_t *)(*seg + 8) = 0;
+ (*ctrl)->imm = send_ieth(wr);
+ (*ctrl)->fm_ce_se = qp->sq_signal_bits |
+ (wr->send_flags & IB_SEND_SIGNALED ?
+ MLX5_WQE_CTRL_CQ_UPDATE : 0) |
+ (wr->send_flags & IB_SEND_SOLICITED ?
+ MLX5_WQE_CTRL_SOLICITED : 0);
+
+ *seg += sizeof(**ctrl);
+ *size = sizeof(**ctrl) / 16;
+
+ return err;
+}
+
+static void finish_wqe(struct mlx5_ib_qp *qp,
+ struct mlx5_wqe_ctrl_seg *ctrl,
+ u8 size, unsigned idx, u64 wr_id,
+ int nreq, u8 fence, u8 next_fence,
+ u32 mlx5_opcode)
+{
+ u8 opmod = 0;
+
+ ctrl->opmod_idx_opcode = cpu_to_be32(((u32)(qp->sq.cur_post) << 8) |
+ mlx5_opcode | ((u32)opmod << 24));
+ ctrl->qpn_ds = cpu_to_be32(size | (qp->mqp.qpn << 8));
+ ctrl->fm_ce_se |= fence;
+ qp->fm_cache = next_fence;
+ if (unlikely(qp->wq_sig))
+ ctrl->signature = wq_sig(ctrl);
+
+ qp->sq.wrid[idx] = wr_id;
+ qp->sq.w_list[idx].opcode = mlx5_opcode;
+ qp->sq.wqe_head[idx] = qp->sq.head + nreq;
+ qp->sq.cur_post += DIV_ROUND_UP(size * 16, MLX5_SEND_WQE_BB);
+ qp->sq.w_list[idx].next = qp->sq.cur_post;
+}
+
+
int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
struct ib_send_wr **bad_wr)
{
@@ -2048,13 +2465,13 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
struct mlx5_core_dev *mdev = &dev->mdev;
struct mlx5_ib_qp *qp = to_mqp(ibqp);
+ struct mlx5_ib_mr *mr;
struct mlx5_wqe_data_seg *dpseg;
struct mlx5_wqe_xrc_seg *xrc;
struct mlx5_bf *bf = qp->bf;
int uninitialized_var(size);
void *qend = qp->sq.qend;
unsigned long flags;
- u32 mlx5_opcode;
unsigned idx;
int err = 0;
int inl = 0;
@@ -2063,7 +2480,6 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
int nreq;
int i;
u8 next_fence = 0;
- u8 opmod = 0;
u8 fence;
spin_lock_irqsave(&qp->sq.lock, flags);
@@ -2076,36 +2492,23 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
goto out;
}
- if (unlikely(mlx5_wq_overflow(&qp->sq, nreq, qp->ibqp.send_cq))) {
+ fence = qp->fm_cache;
+ num_sge = wr->num_sge;
+ if (unlikely(num_sge > qp->sq.max_gs)) {
mlx5_ib_warn(dev, "\n");
err = -ENOMEM;
*bad_wr = wr;
goto out;
}
- fence = qp->fm_cache;
- num_sge = wr->num_sge;
- if (unlikely(num_sge > qp->sq.max_gs)) {
+ err = begin_wqe(qp, &seg, &ctrl, wr, &idx, &size, nreq);
+ if (err) {
mlx5_ib_warn(dev, "\n");
err = -ENOMEM;
*bad_wr = wr;
goto out;
}
- idx = qp->sq.cur_post & (qp->sq.wqe_cnt - 1);
- seg = mlx5_get_send_wqe(qp, idx);
- ctrl = seg;
- *(uint32_t *)(seg + 8) = 0;
- ctrl->imm = send_ieth(wr);
- ctrl->fm_ce_se = qp->sq_signal_bits |
- (wr->send_flags & IB_SEND_SIGNALED ?
- MLX5_WQE_CTRL_CQ_UPDATE : 0) |
- (wr->send_flags & IB_SEND_SOLICITED ?
- MLX5_WQE_CTRL_SOLICITED : 0);
-
- seg += sizeof(*ctrl);
- size = sizeof(*ctrl) / 16;
-
switch (ibqp->qp_type) {
case IB_QPT_XRC_INI:
xrc = seg;
@@ -2158,6 +2561,73 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
num_sge = 0;
break;
+ case IB_WR_REG_SIG_MR:
+ qp->sq.wr_data[idx] = IB_WR_REG_SIG_MR;
+ mr = to_mmr(wr->wr.sig_handover.sig_mr);
+
+ ctrl->imm = cpu_to_be32(mr->ibmr.rkey);
+ err = set_sig_umr_wr(wr, qp, &seg, &size);
+ if (err) {
+ mlx5_ib_warn(dev, "\n");
+ *bad_wr = wr;
+ goto out;
+ }
+
+ finish_wqe(qp, ctrl, size, idx, wr->wr_id,
+ nreq, get_fence(fence, wr),
+ next_fence, MLX5_OPCODE_UMR);
+ /*
+ * SET_PSV WQEs are not signaled and solicited
+ * on error
+ */
+ wr->send_flags &= ~IB_SEND_SIGNALED;
+ wr->send_flags |= IB_SEND_SOLICITED;
+ err = begin_wqe(qp, &seg, &ctrl, wr,
+ &idx, &size, nreq);
+ if (err) {
+ mlx5_ib_warn(dev, "\n");
+ err = -ENOMEM;
+ *bad_wr = wr;
+ goto out;
+ }
+
+ err = set_psv_wr(&wr->wr.sig_handover.sig_attrs->mem,
+ mr->sig->psv_memory.psv_idx, &seg,
+ &size);
+ if (err) {
+ mlx5_ib_warn(dev, "\n");
+ *bad_wr = wr;
+ goto out;
+ }
+
+ finish_wqe(qp, ctrl, size, idx, wr->wr_id,
+ nreq, get_fence(fence, wr),
+ next_fence, MLX5_OPCODE_SET_PSV);
+ err = begin_wqe(qp, &seg, &ctrl, wr,
+ &idx, &size, nreq);
+ if (err) {
+ mlx5_ib_warn(dev, "\n");
+ err = -ENOMEM;
+ *bad_wr = wr;
+ goto out;
+ }
+
+ next_fence = MLX5_FENCE_MODE_INITIATOR_SMALL;
+ err = set_psv_wr(&wr->wr.sig_handover.sig_attrs->wire,
+ mr->sig->psv_wire.psv_idx, &seg,
+ &size);
+ if (err) {
+ mlx5_ib_warn(dev, "\n");
+ *bad_wr = wr;
+ goto out;
+ }
+
+ finish_wqe(qp, ctrl, size, idx, wr->wr_id,
+ nreq, get_fence(fence, wr),
+ next_fence, MLX5_OPCODE_SET_PSV);
+ num_sge = 0;
+ goto skip_psv;
+
default:
break;
}
@@ -2238,22 +2708,10 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
}
}
- mlx5_opcode = mlx5_ib_opcode[wr->opcode];
- ctrl->opmod_idx_opcode = cpu_to_be32(((u32)(qp->sq.cur_post) << 8) |
- mlx5_opcode |
- ((u32)opmod << 24));
- ctrl->qpn_ds = cpu_to_be32(size | (qp->mqp.qpn << 8));
- ctrl->fm_ce_se |= get_fence(fence, wr);
- qp->fm_cache = next_fence;
- if (unlikely(qp->wq_sig))
- ctrl->signature = wq_sig(ctrl);
-
- qp->sq.wrid[idx] = wr->wr_id;
- qp->sq.w_list[idx].opcode = mlx5_opcode;
- qp->sq.wqe_head[idx] = qp->sq.head + nreq;
- qp->sq.cur_post += DIV_ROUND_UP(size * 16, MLX5_SEND_WQE_BB);
- qp->sq.w_list[idx].next = qp->sq.cur_post;
-
+ finish_wqe(qp, ctrl, size, idx, wr->wr_id, nreq,
+ get_fence(fence, wr), next_fence,
+ mlx5_ib_opcode[wr->opcode]);
+skip_psv:
if (0)
dump_wqe(qp, idx, size);
}
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c
index 5b71d43bd89c..415f8e1a54db 100644
--- a/drivers/infiniband/hw/mthca/mthca_provider.c
+++ b/drivers/infiniband/hw/mthca/mthca_provider.c
@@ -695,6 +695,7 @@ static struct ib_cq *mthca_create_cq(struct ib_device *ibdev, int entries,
if (context && ib_copy_to_udata(udata, &cq->cqn, sizeof (__u32))) {
mthca_free_cq(to_mdev(ibdev), cq);
+ err = -EFAULT;
goto err_free;
}
@@ -976,12 +977,12 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u64 virt, int acc, struct ib_udata *udata)
{
struct mthca_dev *dev = to_mdev(pd->device);
- struct ib_umem_chunk *chunk;
+ struct scatterlist *sg;
struct mthca_mr *mr;
struct mthca_reg_mr ucmd;
u64 *pages;
int shift, n, len;
- int i, j, k;
+ int i, k, entry;
int err = 0;
int write_mtt_size;
@@ -1009,10 +1010,7 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
}
shift = ffs(mr->umem->page_size) - 1;
-
- n = 0;
- list_for_each_entry(chunk, &mr->umem->chunk_list, list)
- n += chunk->nents;
+ n = mr->umem->nmap;
mr->mtt = mthca_alloc_mtt(dev, n);
if (IS_ERR(mr->mtt)) {
@@ -1030,25 +1028,24 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
write_mtt_size = min(mthca_write_mtt_size(dev), (int) (PAGE_SIZE / sizeof *pages));
- list_for_each_entry(chunk, &mr->umem->chunk_list, list)
- for (j = 0; j < chunk->nmap; ++j) {
- len = sg_dma_len(&chunk->page_list[j]) >> shift;
- for (k = 0; k < len; ++k) {
- pages[i++] = sg_dma_address(&chunk->page_list[j]) +
- mr->umem->page_size * k;
- /*
- * Be friendly to write_mtt and pass it chunks
- * of appropriate size.
- */
- if (i == write_mtt_size) {
- err = mthca_write_mtt(dev, mr->mtt, n, pages, i);
- if (err)
- goto mtt_done;
- n += i;
- i = 0;
- }
+ for_each_sg(mr->umem->sg_head.sgl, sg, mr->umem->nmap, entry) {
+ len = sg_dma_len(sg) >> shift;
+ for (k = 0; k < len; ++k) {
+ pages[i++] = sg_dma_address(sg) +
+ mr->umem->page_size * k;
+ /*
+ * Be friendly to write_mtt and pass it chunks
+ * of appropriate size.
+ */
+ if (i == write_mtt_size) {
+ err = mthca_write_mtt(dev, mr->mtt, n, pages, i);
+ if (err)
+ goto mtt_done;
+ n += i;
+ i = 0;
}
}
+ }
if (i)
err = mthca_write_mtt(dev, mr->mtt, n, pages, i);
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index 9c9f2f57e960..dfa9df484505 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -128,6 +128,7 @@ static void build_mpa_v1(struct nes_cm_node *, void *, u8);
static void build_rdma0_msg(struct nes_cm_node *, struct nes_qp **);
static void print_core(struct nes_cm_core *core);
+static void record_ird_ord(struct nes_cm_node *, u16, u16);
/* External CM API Interface */
/* instance of function pointers for client API */
@@ -317,7 +318,6 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
}
}
-
if (priv_data_len + mpa_hdr_len != len) {
nes_debug(NES_DBG_CM, "The received ietf buffer was not right"
" complete (%x + %x != %x)\n",
@@ -356,25 +356,57 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
/* send reset */
return -EINVAL;
}
+ if (ird_size == IETF_NO_IRD_ORD || ord_size == IETF_NO_IRD_ORD)
+ cm_node->mpav2_ird_ord = IETF_NO_IRD_ORD;
- if (cm_node->state != NES_CM_STATE_MPAREQ_SENT) {
+ if (cm_node->mpav2_ird_ord != IETF_NO_IRD_ORD) {
/* responder */
- if (cm_node->ord_size > ird_size)
- cm_node->ord_size = ird_size;
- } else {
- /* initiator */
- if (cm_node->ord_size > ird_size)
- cm_node->ord_size = ird_size;
-
- if (cm_node->ird_size < ord_size) {
- /* no resources available */
- /* send terminate message */
- return -EINVAL;
+ if (cm_node->state != NES_CM_STATE_MPAREQ_SENT) {
+ /* we are still negotiating */
+ if (ord_size > NES_MAX_IRD) {
+ cm_node->ird_size = NES_MAX_IRD;
+ } else {
+ cm_node->ird_size = ord_size;
+ if (ord_size == 0 &&
+ (rtr_ctrl_ord & IETF_RDMA0_READ)) {
+ cm_node->ird_size = 1;
+ nes_debug(NES_DBG_CM,
+ "%s: Remote peer doesn't support RDMA0_READ (ord=%u)\n",
+ __func__, ord_size);
+ }
+ }
+ if (ird_size > NES_MAX_ORD)
+ cm_node->ord_size = NES_MAX_ORD;
+ else
+ cm_node->ord_size = ird_size;
+ } else { /* initiator */
+ if (ord_size > NES_MAX_IRD) {
+ nes_debug(NES_DBG_CM,
+ "%s: Unable to support the requested (ord =%u)\n",
+ __func__, ord_size);
+ return -EINVAL;
+ }
+ cm_node->ird_size = ord_size;
+
+ if (ird_size > NES_MAX_ORD) {
+ cm_node->ord_size = NES_MAX_ORD;
+ } else {
+ if (ird_size == 0 &&
+ (rtr_ctrl_ord & IETF_RDMA0_READ)) {
+ nes_debug(NES_DBG_CM,
+ "%s: Remote peer doesn't support RDMA0_READ (ird=%u)\n",
+ __func__, ird_size);
+ return -EINVAL;
+ } else {
+ cm_node->ord_size = ird_size;
+ }
+ }
}
}
if (rtr_ctrl_ord & IETF_RDMA0_READ) {
cm_node->send_rdma0_op = SEND_RDMA_READ_ZERO;
+
} else if (rtr_ctrl_ord & IETF_RDMA0_WRITE) {
cm_node->send_rdma0_op = SEND_RDMA_WRITE_ZERO;
} else { /* Not supported RDMA0 operation */
@@ -514,6 +546,19 @@ static void print_core(struct nes_cm_core *core)
nes_debug(NES_DBG_CM, "-------------- end core ---------------\n");
}
+static void record_ird_ord(struct nes_cm_node *cm_node,
+ u16 conn_ird, u16 conn_ord)
+{
+ if (conn_ird > NES_MAX_IRD)
+ conn_ird = NES_MAX_IRD;
+
+ if (conn_ord > NES_MAX_ORD)
+ conn_ord = NES_MAX_ORD;
+
+ cm_node->ird_size = conn_ird;
+ cm_node->ord_size = conn_ord;
+}
+
/**
* cm_build_mpa_frame - build a MPA V1 frame or MPA V2 frame
*/
@@ -557,11 +602,13 @@ static void build_mpa_v2(struct nes_cm_node *cm_node,
mpa_frame->priv_data_len += htons(IETF_RTR_MSG_SIZE);
/* initialize RTR msg */
- ctrl_ird = (cm_node->ird_size > IETF_NO_IRD_ORD) ?
- IETF_NO_IRD_ORD : cm_node->ird_size;
- ctrl_ord = (cm_node->ord_size > IETF_NO_IRD_ORD) ?
- IETF_NO_IRD_ORD : cm_node->ord_size;
-
+ if (cm_node->mpav2_ird_ord == IETF_NO_IRD_ORD) {
+ ctrl_ird = IETF_NO_IRD_ORD;
+ ctrl_ord = IETF_NO_IRD_ORD;
+ } else {
+ ctrl_ird = cm_node->ird_size & IETF_NO_IRD_ORD;
+ ctrl_ord = cm_node->ord_size & IETF_NO_IRD_ORD;
+ }
ctrl_ird |= IETF_PEER_TO_PEER;
ctrl_ird |= IETF_FLPDU_ZERO_LEN;
@@ -610,7 +657,7 @@ static void build_rdma0_msg(struct nes_cm_node *cm_node, struct nes_qp **nesqp_a
struct nes_qp *nesqp = *nesqp_addr;
struct nes_hw_qp_wqe *wqe = &nesqp->hwqp.sq_vbase[0];
- u64temp = (unsigned long)nesqp;
+ u64temp = (unsigned long)nesqp->nesuqp_addr;
u64temp |= NES_SW_CONTEXT_ALIGN >> 1;
set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX, u64temp);
@@ -1409,8 +1456,9 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
cm_node->mpa_frame_rev = mpa_version;
cm_node->send_rdma0_op = SEND_RDMA_READ_ZERO;
- cm_node->ird_size = IETF_NO_IRD_ORD;
- cm_node->ord_size = IETF_NO_IRD_ORD;
+ cm_node->mpav2_ird_ord = 0;
+ cm_node->ird_size = 0;
+ cm_node->ord_size = 0;
nes_debug(NES_DBG_CM, "Make node addresses : loc = %pI4:%x, rem = %pI4:%x\n",
&cm_node->loc_addr, cm_node->loc_port,
@@ -3027,11 +3075,11 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
rem_ref_cm_node(cm_node->cm_core, cm_node);
return -ECONNRESET;
}
-
/* associate the node with the QP */
nesqp->cm_node = (void *)cm_node;
cm_node->nesqp = nesqp;
+
nes_debug(NES_DBG_CM, "QP%u, cm_node=%p, jiffies = %lu listener = %p\n",
nesqp->hwqp.qp_id, cm_node, jiffies, cm_node->listener);
atomic_inc(&cm_accepts);
@@ -3054,6 +3102,11 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
if (cm_node->mpa_frame_rev == IETF_MPA_V1)
mpa_frame_offset = 4;
+ if (cm_node->mpa_frame_rev == IETF_MPA_V1 ||
+ cm_node->mpav2_ird_ord == IETF_NO_IRD_ORD) {
+ record_ird_ord(cm_node, (u16)conn_param->ird, (u16)conn_param->ord);
+ }
+
memcpy(mpa_v2_frame->priv_data, conn_param->private_data,
conn_param->private_data_len);
@@ -3117,7 +3170,6 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
}
nesqp->skip_lsmm = 1;
-
/* Cache the cm_id in the qp */
nesqp->cm_id = cm_id;
cm_node->cm_id = cm_id;
@@ -3154,7 +3206,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32(
((u32)1 << NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT));
nesqp->nesqp_context->ird_ord_sizes |=
- cpu_to_le32((u32)conn_param->ord);
+ cpu_to_le32((u32)cm_node->ord_size);
memset(&nes_quad, 0, sizeof(nes_quad));
nes_quad.DstIpAdrIndex =
@@ -3194,6 +3246,9 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
cm_event.remote_addr = cm_id->remote_addr;
cm_event.private_data = NULL;
cm_event.private_data_len = 0;
+ cm_event.ird = cm_node->ird_size;
+ cm_event.ord = cm_node->ord_size;
+
ret = cm_id->event_handler(cm_id, &cm_event);
attr.qp_state = IB_QPS_RTS;
nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL);
@@ -3290,14 +3345,8 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
/* cache the cm_id in the qp */
nesqp->cm_id = cm_id;
-
cm_id->provider_data = nesqp;
-
nesqp->private_data_len = conn_param->private_data_len;
- nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((u32)conn_param->ord);
- /* space for rdma0 read msg */
- if (conn_param->ord == 0)
- nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32(1);
nes_debug(NES_DBG_CM, "requested ord = 0x%08X.\n", (u32)conn_param->ord);
nes_debug(NES_DBG_CM, "mpa private data len =%u\n",
@@ -3334,6 +3383,11 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
return -ENOMEM;
}
+ record_ird_ord(cm_node, (u16)conn_param->ird, (u16)conn_param->ord);
+ if (cm_node->send_rdma0_op == SEND_RDMA_READ_ZERO &&
+ cm_node->ord_size == 0)
+ cm_node->ord_size = 1;
+
cm_node->apbvt_set = apbvt_set;
nesqp->cm_node = cm_node;
cm_node->nesqp = nesqp;
@@ -3530,6 +3584,8 @@ static void cm_event_connected(struct nes_cm_event *event)
nesqp->nesqp_context->ird_ord_sizes |=
cpu_to_le32((u32)1 <<
NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT);
+ nesqp->nesqp_context->ird_ord_sizes |=
+ cpu_to_le32((u32)cm_node->ord_size);
/* Adjust tail for not having a LSMM */
/*nesqp->hwqp.sq_tail = 1;*/
@@ -3742,8 +3798,13 @@ static void cm_event_mpa_req(struct nes_cm_event *event)
cm_event_raddr->sin_addr.s_addr = htonl(event->cm_info.rem_addr);
cm_event.private_data = cm_node->mpa_frame_buf;
cm_event.private_data_len = (u8)cm_node->mpa_frame_size;
+ if (cm_node->mpa_frame_rev == IETF_MPA_V1) {
+ cm_event.ird = NES_MAX_IRD;
+ cm_event.ord = NES_MAX_ORD;
+ } else {
cm_event.ird = cm_node->ird_size;
cm_event.ord = cm_node->ord_size;
+ }
ret = cm_id->event_handler(cm_id, &cm_event);
if (ret)
diff --git a/drivers/infiniband/hw/nes/nes_cm.h b/drivers/infiniband/hw/nes/nes_cm.h
index 4646e6666087..522c99cd07c4 100644
--- a/drivers/infiniband/hw/nes/nes_cm.h
+++ b/drivers/infiniband/hw/nes/nes_cm.h
@@ -58,6 +58,8 @@
#define IETF_RDMA0_WRITE 0x8000
#define IETF_RDMA0_READ 0x4000
#define IETF_NO_IRD_ORD 0x3FFF
+#define NES_MAX_IRD 0x40
+#define NES_MAX_ORD 0x7F
enum ietf_mpa_flags {
IETF_MPA_FLAGS_MARKERS = 0x80, /* receive Markers */
@@ -333,6 +335,7 @@ struct nes_cm_node {
enum mpa_frame_version mpa_frame_rev;
u16 ird_size;
u16 ord_size;
+ u16 mpav2_ird_ord;
u16 mpa_frame_size;
struct iw_cm_id *cm_id;
diff --git a/drivers/infiniband/hw/nes/nes_user.h b/drivers/infiniband/hw/nes/nes_user.h
index 4926de744488..529c421bb15c 100644
--- a/drivers/infiniband/hw/nes/nes_user.h
+++ b/drivers/infiniband/hw/nes/nes_user.h
@@ -39,8 +39,8 @@
#include <linux/types.h>
-#define NES_ABI_USERSPACE_VER 1
-#define NES_ABI_KERNEL_VER 1
+#define NES_ABI_USERSPACE_VER 2
+#define NES_ABI_KERNEL_VER 2
/*
* Make sure that all structs defined in this file remain laid out so
@@ -78,6 +78,7 @@ struct nes_create_cq_req {
struct nes_create_qp_req {
__u64 user_wqe_buffers;
+ __u64 user_qp_buffer;
};
enum iwnes_memreg_type {
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index 8308e3634767..218dd3574285 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -1186,11 +1186,13 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd,
nes_free_resource(nesadapter, nesadapter->allocated_qps, qp_num);
kfree(nesqp->allocated_buffer);
nes_debug(NES_DBG_QP, "ib_copy_from_udata() Failed \n");
- return NULL;
+ return ERR_PTR(-EFAULT);
}
if (req.user_wqe_buffers) {
virt_wqs = 1;
}
+ if (req.user_qp_buffer)
+ nesqp->nesuqp_addr = req.user_qp_buffer;
if ((ibpd->uobject) && (ibpd->uobject->context)) {
nesqp->user_mode = 1;
nes_ucontext = to_nesucontext(ibpd->uobject->context);
@@ -2307,7 +2309,7 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
struct nes_device *nesdev = nesvnic->nesdev;
struct nes_adapter *nesadapter = nesdev->nesadapter;
struct ib_mr *ibmr = ERR_PTR(-EINVAL);
- struct ib_umem_chunk *chunk;
+ struct scatterlist *sg;
struct nes_ucontext *nes_ucontext;
struct nes_pbl *nespbl;
struct nes_mr *nesmr;
@@ -2315,7 +2317,7 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
struct nes_mem_reg_req req;
struct nes_vpbl vpbl;
struct nes_root_vpbl root_vpbl;
- int nmap_index, page_index;
+ int entry, page_index;
int page_count = 0;
int err, pbl_depth = 0;
int chunk_pages;
@@ -2330,6 +2332,7 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u16 pbl_count;
u8 single_page = 1;
u8 stag_key;
+ int first_page = 1;
region = ib_umem_get(pd->uobject->context, start, length, acc, 0);
if (IS_ERR(region)) {
@@ -2380,128 +2383,125 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
}
nesmr->region = region;
- list_for_each_entry(chunk, &region->chunk_list, list) {
- nes_debug(NES_DBG_MR, "Chunk: nents = %u, nmap = %u .\n",
- chunk->nents, chunk->nmap);
- for (nmap_index = 0; nmap_index < chunk->nmap; ++nmap_index) {
- if (sg_dma_address(&chunk->page_list[nmap_index]) & ~PAGE_MASK) {
- ib_umem_release(region);
- nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
- nes_debug(NES_DBG_MR, "Unaligned Memory Buffer: 0x%x\n",
- (unsigned int) sg_dma_address(&chunk->page_list[nmap_index]));
- ibmr = ERR_PTR(-EINVAL);
- kfree(nesmr);
- goto reg_user_mr_err;
- }
+ for_each_sg(region->sg_head.sgl, sg, region->nmap, entry) {
+ if (sg_dma_address(sg) & ~PAGE_MASK) {
+ ib_umem_release(region);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ nes_debug(NES_DBG_MR, "Unaligned Memory Buffer: 0x%x\n",
+ (unsigned int) sg_dma_address(sg));
+ ibmr = ERR_PTR(-EINVAL);
+ kfree(nesmr);
+ goto reg_user_mr_err;
+ }
- if (!sg_dma_len(&chunk->page_list[nmap_index])) {
- ib_umem_release(region);
- nes_free_resource(nesadapter, nesadapter->allocated_mrs,
- stag_index);
- nes_debug(NES_DBG_MR, "Invalid Buffer Size\n");
- ibmr = ERR_PTR(-EINVAL);
- kfree(nesmr);
- goto reg_user_mr_err;
- }
+ if (!sg_dma_len(sg)) {
+ ib_umem_release(region);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs,
+ stag_index);
+ nes_debug(NES_DBG_MR, "Invalid Buffer Size\n");
+ ibmr = ERR_PTR(-EINVAL);
+ kfree(nesmr);
+ goto reg_user_mr_err;
+ }
- region_length += sg_dma_len(&chunk->page_list[nmap_index]);
- chunk_pages = sg_dma_len(&chunk->page_list[nmap_index]) >> 12;
- region_length -= skip_pages << 12;
- for (page_index=skip_pages; page_index < chunk_pages; page_index++) {
- skip_pages = 0;
- if ((page_count!=0)&&(page_count<<12)-(region->offset&(4096-1))>=region->length)
- goto enough_pages;
- if ((page_count&0x01FF) == 0) {
- if (page_count >= 1024 * 512) {
+ region_length += sg_dma_len(sg);
+ chunk_pages = sg_dma_len(sg) >> 12;
+ region_length -= skip_pages << 12;
+ for (page_index = skip_pages; page_index < chunk_pages; page_index++) {
+ skip_pages = 0;
+ if ((page_count != 0) && (page_count<<12)-(region->offset&(4096-1)) >= region->length)
+ goto enough_pages;
+ if ((page_count&0x01FF) == 0) {
+ if (page_count >= 1024 * 512) {
+ ib_umem_release(region);
+ nes_free_resource(nesadapter,
+ nesadapter->allocated_mrs, stag_index);
+ kfree(nesmr);
+ ibmr = ERR_PTR(-E2BIG);
+ goto reg_user_mr_err;
+ }
+ if (root_pbl_index == 1) {
+ root_vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev,
+ 8192, &root_vpbl.pbl_pbase);
+ nes_debug(NES_DBG_MR, "Allocating root PBL, va = %p, pa = 0x%08X\n",
+ root_vpbl.pbl_vbase, (unsigned int)root_vpbl.pbl_pbase);
+ if (!root_vpbl.pbl_vbase) {
ib_umem_release(region);
- nes_free_resource(nesadapter,
- nesadapter->allocated_mrs, stag_index);
+ pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
+ vpbl.pbl_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs,
+ stag_index);
kfree(nesmr);
- ibmr = ERR_PTR(-E2BIG);
+ ibmr = ERR_PTR(-ENOMEM);
goto reg_user_mr_err;
}
- if (root_pbl_index == 1) {
- root_vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev,
- 8192, &root_vpbl.pbl_pbase);
- nes_debug(NES_DBG_MR, "Allocating root PBL, va = %p, pa = 0x%08X\n",
- root_vpbl.pbl_vbase, (unsigned int)root_vpbl.pbl_pbase);
- if (!root_vpbl.pbl_vbase) {
- ib_umem_release(region);
- pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
- vpbl.pbl_pbase);
- nes_free_resource(nesadapter, nesadapter->allocated_mrs,
- stag_index);
- kfree(nesmr);
- ibmr = ERR_PTR(-ENOMEM);
- goto reg_user_mr_err;
- }
- root_vpbl.leaf_vpbl = kzalloc(sizeof(*root_vpbl.leaf_vpbl)*1024,
- GFP_KERNEL);
- if (!root_vpbl.leaf_vpbl) {
- ib_umem_release(region);
- pci_free_consistent(nesdev->pcidev, 8192, root_vpbl.pbl_vbase,
- root_vpbl.pbl_pbase);
- pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
- vpbl.pbl_pbase);
- nes_free_resource(nesadapter, nesadapter->allocated_mrs,
- stag_index);
- kfree(nesmr);
- ibmr = ERR_PTR(-ENOMEM);
- goto reg_user_mr_err;
- }
- root_vpbl.pbl_vbase[0].pa_low =
- cpu_to_le32((u32)vpbl.pbl_pbase);
- root_vpbl.pbl_vbase[0].pa_high =
- cpu_to_le32((u32)((((u64)vpbl.pbl_pbase) >> 32)));
- root_vpbl.leaf_vpbl[0] = vpbl;
- }
- vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 4096,
- &vpbl.pbl_pbase);
- nes_debug(NES_DBG_MR, "Allocating leaf PBL, va = %p, pa = 0x%08X\n",
- vpbl.pbl_vbase, (unsigned int)vpbl.pbl_pbase);
- if (!vpbl.pbl_vbase) {
+ root_vpbl.leaf_vpbl = kzalloc(sizeof(*root_vpbl.leaf_vpbl)*1024,
+ GFP_KERNEL);
+ if (!root_vpbl.leaf_vpbl) {
ib_umem_release(region);
- nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
- ibmr = ERR_PTR(-ENOMEM);
+ pci_free_consistent(nesdev->pcidev, 8192, root_vpbl.pbl_vbase,
+ root_vpbl.pbl_pbase);
+ pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
+ vpbl.pbl_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs,
+ stag_index);
kfree(nesmr);
+ ibmr = ERR_PTR(-ENOMEM);
goto reg_user_mr_err;
}
- if (1 <= root_pbl_index) {
- root_vpbl.pbl_vbase[root_pbl_index].pa_low =
- cpu_to_le32((u32)vpbl.pbl_pbase);
- root_vpbl.pbl_vbase[root_pbl_index].pa_high =
- cpu_to_le32((u32)((((u64)vpbl.pbl_pbase)>>32)));
- root_vpbl.leaf_vpbl[root_pbl_index] = vpbl;
- }
- root_pbl_index++;
- cur_pbl_index = 0;
+ root_vpbl.pbl_vbase[0].pa_low =
+ cpu_to_le32((u32)vpbl.pbl_pbase);
+ root_vpbl.pbl_vbase[0].pa_high =
+ cpu_to_le32((u32)((((u64)vpbl.pbl_pbase) >> 32)));
+ root_vpbl.leaf_vpbl[0] = vpbl;
}
- if (single_page) {
- if (page_count != 0) {
- if ((last_dma_addr+4096) !=
- (sg_dma_address(&chunk->page_list[nmap_index])+
- (page_index*4096)))
- single_page = 0;
- last_dma_addr = sg_dma_address(&chunk->page_list[nmap_index])+
- (page_index*4096);
- } else {
- first_dma_addr = sg_dma_address(&chunk->page_list[nmap_index])+
- (page_index*4096);
- last_dma_addr = first_dma_addr;
- }
+ vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 4096,
+ &vpbl.pbl_pbase);
+ nes_debug(NES_DBG_MR, "Allocating leaf PBL, va = %p, pa = 0x%08X\n",
+ vpbl.pbl_vbase, (unsigned int)vpbl.pbl_pbase);
+ if (!vpbl.pbl_vbase) {
+ ib_umem_release(region);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ ibmr = ERR_PTR(-ENOMEM);
+ kfree(nesmr);
+ goto reg_user_mr_err;
+ }
+ if (1 <= root_pbl_index) {
+ root_vpbl.pbl_vbase[root_pbl_index].pa_low =
+ cpu_to_le32((u32)vpbl.pbl_pbase);
+ root_vpbl.pbl_vbase[root_pbl_index].pa_high =
+ cpu_to_le32((u32)((((u64)vpbl.pbl_pbase)>>32)));
+ root_vpbl.leaf_vpbl[root_pbl_index] = vpbl;
+ }
+ root_pbl_index++;
+ cur_pbl_index = 0;
+ }
+ if (single_page) {
+ if (page_count != 0) {
+ if ((last_dma_addr+4096) !=
+ (sg_dma_address(sg)+
+ (page_index*4096)))
+ single_page = 0;
+ last_dma_addr = sg_dma_address(sg)+
+ (page_index*4096);
+ } else {
+ first_dma_addr = sg_dma_address(sg)+
+ (page_index*4096);
+ last_dma_addr = first_dma_addr;
}
-
- vpbl.pbl_vbase[cur_pbl_index].pa_low =
- cpu_to_le32((u32)(sg_dma_address(&chunk->page_list[nmap_index])+
- (page_index*4096)));
- vpbl.pbl_vbase[cur_pbl_index].pa_high =
- cpu_to_le32((u32)((((u64)(sg_dma_address(&chunk->page_list[nmap_index])+
- (page_index*4096))) >> 32)));
- cur_pbl_index++;
- page_count++;
}
+
+ vpbl.pbl_vbase[cur_pbl_index].pa_low =
+ cpu_to_le32((u32)(sg_dma_address(sg)+
+ (page_index*4096)));
+ vpbl.pbl_vbase[cur_pbl_index].pa_high =
+ cpu_to_le32((u32)((((u64)(sg_dma_address(sg)+
+ (page_index*4096))) >> 32)));
+ cur_pbl_index++;
+ page_count++;
}
}
+
enough_pages:
nes_debug(NES_DBG_MR, "calculating stag, stag_index=0x%08x, driver_key=0x%08x,"
" stag_key=0x%08x\n",
@@ -2613,25 +2613,28 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
nespbl->pbl_size, (unsigned long) nespbl->pbl_pbase,
(void *) nespbl->pbl_vbase, nespbl->user_base);
- list_for_each_entry(chunk, &region->chunk_list, list) {
- for (nmap_index = 0; nmap_index < chunk->nmap; ++nmap_index) {
- chunk_pages = sg_dma_len(&chunk->page_list[nmap_index]) >> 12;
- chunk_pages += (sg_dma_len(&chunk->page_list[nmap_index]) & (4096-1)) ? 1 : 0;
- nespbl->page = sg_page(&chunk->page_list[0]);
- for (page_index=0; page_index<chunk_pages; page_index++) {
- ((__le32 *)pbl)[0] = cpu_to_le32((u32)
- (sg_dma_address(&chunk->page_list[nmap_index])+
- (page_index*4096)));
- ((__le32 *)pbl)[1] = cpu_to_le32(((u64)
- (sg_dma_address(&chunk->page_list[nmap_index])+
- (page_index*4096)))>>32);
- nes_debug(NES_DBG_MR, "pbl=%p, *pbl=0x%016llx, 0x%08x%08x\n", pbl,
- (unsigned long long)*pbl,
- le32_to_cpu(((__le32 *)pbl)[1]), le32_to_cpu(((__le32 *)pbl)[0]));
- pbl++;
- }
+ for_each_sg(region->sg_head.sgl, sg, region->nmap, entry) {
+ chunk_pages = sg_dma_len(sg) >> 12;
+ chunk_pages += (sg_dma_len(sg) & (4096-1)) ? 1 : 0;
+ if (first_page) {
+ nespbl->page = sg_page(sg);
+ first_page = 0;
+ }
+
+ for (page_index = 0; page_index < chunk_pages; page_index++) {
+ ((__le32 *)pbl)[0] = cpu_to_le32((u32)
+ (sg_dma_address(sg)+
+ (page_index*4096)));
+ ((__le32 *)pbl)[1] = cpu_to_le32(((u64)
+ (sg_dma_address(sg)+
+ (page_index*4096)))>>32);
+ nes_debug(NES_DBG_MR, "pbl=%p, *pbl=0x%016llx, 0x%08x%08x\n", pbl,
+ (unsigned long long)*pbl,
+ le32_to_cpu(((__le32 *)pbl)[1]), le32_to_cpu(((__le32 *)pbl)[0]));
+ pbl++;
}
}
+
if (req.reg_type == IWNES_MEMREG_TYPE_QP) {
list_add_tail(&nespbl->list, &nes_ucontext->qp_reg_mem_list);
} else {
@@ -3134,9 +3137,7 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
" original_last_aeq = 0x%04X. last_aeq = 0x%04X.\n",
nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
original_last_aeq, nesqp->last_aeq);
- if ((!ret) ||
- ((original_last_aeq != NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) &&
- (ret))) {
+ if (!ret || original_last_aeq != NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) {
if (dont_wait) {
if (nesqp->cm_id && nesqp->hw_tcp_state != 0) {
nes_debug(NES_DBG_MOD_QP, "QP%u Queuing fake disconnect for QP refcount (%d),"
diff --git a/drivers/infiniband/hw/nes/nes_verbs.h b/drivers/infiniband/hw/nes/nes_verbs.h
index 0eff7c44d76b..309b31c31ae1 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.h
+++ b/drivers/infiniband/hw/nes/nes_verbs.h
@@ -184,5 +184,6 @@ struct nes_qp {
u8 pau_busy;
u8 pau_pending;
u8 pau_state;
+ __u64 nesuqp_addr;
};
#endif /* NES_VERBS_H */
diff --git a/drivers/infiniband/hw/ocrdma/Makefile b/drivers/infiniband/hw/ocrdma/Makefile
index 06a5bed12e43..d1bfd4f4cdde 100644
--- a/drivers/infiniband/hw/ocrdma/Makefile
+++ b/drivers/infiniband/hw/ocrdma/Makefile
@@ -2,4 +2,4 @@ ccflags-y := -Idrivers/net/ethernet/emulex/benet
obj-$(CONFIG_INFINIBAND_OCRDMA) += ocrdma.o
-ocrdma-y := ocrdma_main.o ocrdma_verbs.o ocrdma_hw.o ocrdma_ah.o
+ocrdma-y := ocrdma_main.o ocrdma_verbs.o ocrdma_hw.o ocrdma_ah.o ocrdma_stats.o
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma.h b/drivers/infiniband/hw/ocrdma/ocrdma.h
index 7c001b97b23f..19011dbb930f 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma.h
@@ -35,17 +35,27 @@
#include <rdma/ib_verbs.h>
#include <rdma/ib_user_verbs.h>
+#include <rdma/ib_addr.h>
#include <be_roce.h>
#include "ocrdma_sli.h"
-#define OCRDMA_ROCE_DEV_VERSION "1.0.0"
+#define OCRDMA_ROCE_DRV_VERSION "10.2.145.0u"
+
+#define OCRDMA_ROCE_DRV_DESC "Emulex OneConnect RoCE Driver"
#define OCRDMA_NODE_DESC "Emulex OneConnect RoCE HCA"
+#define OC_NAME_SH OCRDMA_NODE_DESC "(Skyhawk)"
+#define OC_NAME_UNKNOWN OCRDMA_NODE_DESC "(Unknown)"
+
+#define OC_SKH_DEVICE_PF 0x720
+#define OC_SKH_DEVICE_VF 0x728
#define OCRDMA_MAX_AH 512
#define OCRDMA_UVERBS(CMD_NAME) (1ull << IB_USER_VERBS_CMD_##CMD_NAME)
+#define convert_to_64bit(lo, hi) ((u64)hi << 32 | (u64)lo)
+
struct ocrdma_dev_attr {
u8 fw_ver[32];
u32 vendor_id;
@@ -65,6 +75,7 @@ struct ocrdma_dev_attr {
int max_mr;
u64 max_mr_size;
u32 max_num_mr_pbl;
+ int max_mw;
int max_fmr;
int max_map_per_fmr;
int max_pages_per_frmr;
@@ -83,6 +94,12 @@ struct ocrdma_dev_attr {
u8 num_ird_pages;
};
+struct ocrdma_dma_mem {
+ void *va;
+ dma_addr_t pa;
+ u32 size;
+};
+
struct ocrdma_pbl {
void *va;
dma_addr_t pa;
@@ -148,6 +165,26 @@ struct ocrdma_mr {
struct ocrdma_hw_mr hwmr;
};
+struct ocrdma_stats {
+ u8 type;
+ struct ocrdma_dev *dev;
+};
+
+struct stats_mem {
+ struct ocrdma_mqe mqe;
+ void *va;
+ dma_addr_t pa;
+ u32 size;
+ char *debugfs_mem;
+};
+
+struct phy_info {
+ u16 auto_speeds_supported;
+ u16 fixed_speeds_supported;
+ u16 phy_type;
+ u16 interface_type;
+};
+
struct ocrdma_dev {
struct ib_device ibdev;
struct ocrdma_dev_attr attr;
@@ -191,12 +228,30 @@ struct ocrdma_dev {
struct mqe_ctx mqe_ctx;
struct be_dev_info nic_info;
+ struct phy_info phy;
+ char model_number[32];
+ u32 hba_port_num;
struct list_head entry;
struct rcu_head rcu;
int id;
- struct ocrdma_mr *stag_arr[OCRDMA_MAX_STAG];
+ u64 stag_arr[OCRDMA_MAX_STAG];
u16 pvid;
+ u32 asic_id;
+
+ ulong last_stats_time;
+ struct mutex stats_lock; /* provide synch for debugfs operations */
+ struct stats_mem stats_mem;
+ struct ocrdma_stats rsrc_stats;
+ struct ocrdma_stats rx_stats;
+ struct ocrdma_stats wqe_stats;
+ struct ocrdma_stats tx_stats;
+ struct ocrdma_stats db_err_stats;
+ struct ocrdma_stats tx_qp_err_stats;
+ struct ocrdma_stats rx_qp_err_stats;
+ struct ocrdma_stats tx_dbg_stats;
+ struct ocrdma_stats rx_dbg_stats;
+ struct dentry *dir;
};
struct ocrdma_cq {
@@ -209,8 +264,8 @@ struct ocrdma_cq {
*/
u32 max_hw_cqe;
bool phase_change;
- bool armed, solicited;
- bool arm_needed;
+ bool deferred_arm, deferred_sol;
+ bool first_arm;
spinlock_t cq_lock ____cacheline_aligned; /* provide synchronization
* to cq polling
@@ -223,6 +278,7 @@ struct ocrdma_cq {
struct ocrdma_ucontext *ucontext;
dma_addr_t pa;
u32 len;
+ u32 cqe_cnt;
/* head of all qp's sq and rq for which cqes need to be flushed
* by the software.
@@ -232,7 +288,6 @@ struct ocrdma_cq {
struct ocrdma_pd {
struct ib_pd ibpd;
- struct ocrdma_dev *dev;
struct ocrdma_ucontext *uctx;
u32 id;
int num_dpp_qp;
@@ -317,10 +372,8 @@ struct ocrdma_qp {
bool dpp_enabled;
u8 *ird_q_va;
bool signaled;
- u16 db_cache;
};
-
struct ocrdma_ucontext {
struct ib_ucontext ibucontext;
@@ -385,13 +438,6 @@ static inline struct ocrdma_srq *get_ocrdma_srq(struct ib_srq *ibsrq)
return container_of(ibsrq, struct ocrdma_srq, ibsrq);
}
-
-static inline int ocrdma_get_num_posted_shift(struct ocrdma_qp *qp)
-{
- return ((qp->dev->nic_info.dev_family == OCRDMA_GEN2_FAMILY &&
- qp->id < 128) ? 24 : 16);
-}
-
static inline int is_cqe_valid(struct ocrdma_cq *cq, struct ocrdma_cqe *cqe)
{
int cqe_valid;
@@ -436,4 +482,40 @@ static inline int ocrdma_resolve_dmac(struct ocrdma_dev *dev,
return 0;
}
+static inline char *hca_name(struct ocrdma_dev *dev)
+{
+ switch (dev->nic_info.pdev->device) {
+ case OC_SKH_DEVICE_PF:
+ case OC_SKH_DEVICE_VF:
+ return OC_NAME_SH;
+ default:
+ return OC_NAME_UNKNOWN;
+ }
+}
+
+static inline int ocrdma_get_eq_table_index(struct ocrdma_dev *dev,
+ int eqid)
+{
+ int indx;
+
+ for (indx = 0; indx < dev->eq_cnt; indx++) {
+ if (dev->eq_tbl[indx].q.id == eqid)
+ return indx;
+ }
+
+ return -EINVAL;
+}
+
+static inline u8 ocrdma_get_asic_type(struct ocrdma_dev *dev)
+{
+ if (dev->nic_info.dev_family == 0xF && !dev->asic_id) {
+ pci_read_config_dword(
+ dev->nic_info.pdev,
+ OCRDMA_SLI_ASIC_ID_OFFSET, &dev->asic_id);
+ }
+
+ return (dev->asic_id & OCRDMA_SLI_ASIC_GEN_NUM_MASK) >>
+ OCRDMA_SLI_ASIC_GEN_NUM_SHIFT;
+}
+
#endif
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_abi.h b/drivers/infiniband/hw/ocrdma/ocrdma_abi.h
index fbac8eb44036..1554cca5712a 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_abi.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_abi.h
@@ -28,7 +28,8 @@
#ifndef __OCRDMA_ABI_H__
#define __OCRDMA_ABI_H__
-#define OCRDMA_ABI_VERSION 1
+#define OCRDMA_ABI_VERSION 2
+#define OCRDMA_BE_ROCE_ABI_VERSION 1
/* user kernel communication data structures. */
struct ocrdma_alloc_ucontext_resp {
@@ -107,9 +108,7 @@ struct ocrdma_create_qp_uresp {
u32 db_sq_offset;
u32 db_rq_offset;
u32 db_shift;
- u64 rsvd1;
- u64 rsvd2;
- u64 rsvd3;
+ u64 rsvd[11];
} __packed;
struct ocrdma_create_srq_uresp {
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_ah.c b/drivers/infiniband/hw/ocrdma/ocrdma_ah.c
index 34071143006e..d4cc01f10c01 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_ah.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_ah.c
@@ -100,7 +100,7 @@ struct ib_ah *ocrdma_create_ah(struct ib_pd *ibpd, struct ib_ah_attr *attr)
if (!(attr->ah_flags & IB_AH_GRH))
return ERR_PTR(-EINVAL);
- ah = kzalloc(sizeof *ah, GFP_ATOMIC);
+ ah = kzalloc(sizeof(*ah), GFP_ATOMIC);
if (!ah)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
index 1664d648cbfc..3bbf2010a821 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
@@ -32,7 +32,6 @@
#include <rdma/ib_verbs.h>
#include <rdma/ib_user_verbs.h>
-#include <rdma/ib_addr.h>
#include "ocrdma.h"
#include "ocrdma_hw.h"
@@ -243,6 +242,23 @@ static int ocrdma_get_mbx_errno(u32 status)
return err_num;
}
+char *port_speed_string(struct ocrdma_dev *dev)
+{
+ char *str = "";
+ u16 speeds_supported;
+
+ speeds_supported = dev->phy.fixed_speeds_supported |
+ dev->phy.auto_speeds_supported;
+ if (speeds_supported & OCRDMA_PHY_SPEED_40GBPS)
+ str = "40Gbps ";
+ else if (speeds_supported & OCRDMA_PHY_SPEED_10GBPS)
+ str = "10Gbps ";
+ else if (speeds_supported & OCRDMA_PHY_SPEED_1GBPS)
+ str = "1Gbps ";
+
+ return str;
+}
+
static int ocrdma_get_mbx_cqe_errno(u16 cqe_status)
{
int err_num = -EINVAL;
@@ -332,6 +348,11 @@ static void *ocrdma_init_emb_mqe(u8 opcode, u32 cmd_len)
return mqe;
}
+static void *ocrdma_alloc_mqe(void)
+{
+ return kzalloc(sizeof(struct ocrdma_mqe), GFP_KERNEL);
+}
+
static void ocrdma_free_q(struct ocrdma_dev *dev, struct ocrdma_queue_info *q)
{
dma_free_coherent(&dev->nic_info.pdev->dev, q->size, q->va, q->dma);
@@ -364,8 +385,8 @@ static void ocrdma_build_q_pages(struct ocrdma_pa *q_pa, int cnt,
}
}
-static int ocrdma_mbx_delete_q(struct ocrdma_dev *dev, struct ocrdma_queue_info *q,
- int queue_type)
+static int ocrdma_mbx_delete_q(struct ocrdma_dev *dev,
+ struct ocrdma_queue_info *q, int queue_type)
{
u8 opcode = 0;
int status;
@@ -444,7 +465,7 @@ mbx_err:
return status;
}
-static int ocrdma_get_irq(struct ocrdma_dev *dev, struct ocrdma_eq *eq)
+int ocrdma_get_irq(struct ocrdma_dev *dev, struct ocrdma_eq *eq)
{
int irq;
@@ -574,6 +595,7 @@ static int ocrdma_create_mq(struct ocrdma_dev *dev)
if (status)
goto alloc_err;
+ dev->eq_tbl[0].cq_cnt++;
status = ocrdma_mbx_mq_cq_create(dev, &dev->mq.cq, &dev->eq_tbl[0].q);
if (status)
goto mbx_cq_free;
@@ -639,7 +661,7 @@ static void ocrdma_dispatch_ibevent(struct ocrdma_dev *dev,
{
struct ocrdma_qp *qp = NULL;
struct ocrdma_cq *cq = NULL;
- struct ib_event ib_evt;
+ struct ib_event ib_evt = { 0 };
int cq_event = 0;
int qp_event = 1;
int srq_event = 0;
@@ -664,6 +686,8 @@ static void ocrdma_dispatch_ibevent(struct ocrdma_dev *dev,
case OCRDMA_CQ_OVERRUN_ERROR:
ib_evt.element.cq = &cq->ibcq;
ib_evt.event = IB_EVENT_CQ_ERR;
+ cq_event = 1;
+ qp_event = 0;
break;
case OCRDMA_CQ_QPCAT_ERROR:
ib_evt.element.qp = &qp->ibqp;
@@ -725,6 +749,7 @@ static void ocrdma_dispatch_ibevent(struct ocrdma_dev *dev,
qp->srq->ibsrq.
srq_context);
} else if (dev_event) {
+ pr_err("%s: Fatal event received\n", dev->ibdev.name);
ib_dispatch_event(&ib_evt);
}
@@ -752,7 +777,6 @@ static void ocrdma_process_grp5_aync(struct ocrdma_dev *dev,
}
}
-
static void ocrdma_process_acqe(struct ocrdma_dev *dev, void *ae_cqe)
{
/* async CQE processing */
@@ -799,8 +823,6 @@ static int ocrdma_mq_cq_handler(struct ocrdma_dev *dev, u16 cq_id)
ocrdma_process_acqe(dev, cqe);
else if (cqe->valid_ae_cmpl_cons & OCRDMA_MCQE_CMPL_MASK)
ocrdma_process_mcqe(dev, cqe);
- else
- pr_err("%s() cqe->compl is not set.\n", __func__);
memset(cqe, 0, sizeof(struct ocrdma_mcqe));
ocrdma_mcq_inc_tail(dev);
}
@@ -858,16 +880,8 @@ static void ocrdma_qp_cq_handler(struct ocrdma_dev *dev, u16 cq_idx)
BUG();
cq = dev->cq_tbl[cq_idx];
- if (cq == NULL) {
- pr_err("%s%d invalid id=0x%x\n", __func__, dev->id, cq_idx);
+ if (cq == NULL)
return;
- }
- spin_lock_irqsave(&cq->cq_lock, flags);
- cq->armed = false;
- cq->solicited = false;
- spin_unlock_irqrestore(&cq->cq_lock, flags);
-
- ocrdma_ring_cq_db(dev, cq->id, false, false, 0);
if (cq->ibcq.comp_handler) {
spin_lock_irqsave(&cq->comp_handler_lock, flags);
@@ -892,27 +906,35 @@ static irqreturn_t ocrdma_irq_handler(int irq, void *handle)
struct ocrdma_dev *dev = eq->dev;
struct ocrdma_eqe eqe;
struct ocrdma_eqe *ptr;
- u16 eqe_popped = 0;
u16 cq_id;
- while (1) {
+ int budget = eq->cq_cnt;
+
+ do {
ptr = ocrdma_get_eqe(eq);
eqe = *ptr;
ocrdma_le32_to_cpu(&eqe, sizeof(eqe));
if ((eqe.id_valid & OCRDMA_EQE_VALID_MASK) == 0)
break;
- eqe_popped += 1;
+
ptr->id_valid = 0;
+ /* ring eq doorbell as soon as its consumed. */
+ ocrdma_ring_eq_db(dev, eq->q.id, false, true, 1);
/* check whether its CQE or not. */
if ((eqe.id_valid & OCRDMA_EQE_FOR_CQE_MASK) == 0) {
cq_id = eqe.id_valid >> OCRDMA_EQE_RESOURCE_ID_SHIFT;
ocrdma_cq_handler(dev, cq_id);
}
ocrdma_eq_inc_tail(eq);
- }
- ocrdma_ring_eq_db(dev, eq->q.id, true, true, eqe_popped);
- /* Ring EQ doorbell with num_popped to 0 to enable interrupts again. */
- if (dev->nic_info.intr_mode == BE_INTERRUPT_MODE_INTX)
- ocrdma_ring_eq_db(dev, eq->q.id, true, true, 0);
+
+ /* There can be a stale EQE after the last bound CQ is
+ * destroyed. EQE valid and budget == 0 implies this.
+ */
+ if (budget)
+ budget--;
+
+ } while (budget);
+
+ ocrdma_ring_eq_db(dev, eq->q.id, true, true, 0);
return IRQ_HANDLED;
}
@@ -949,7 +971,8 @@ static int ocrdma_mbx_cmd(struct ocrdma_dev *dev, struct ocrdma_mqe *mqe)
{
int status = 0;
u16 cqe_status, ext_status;
- struct ocrdma_mqe *rsp;
+ struct ocrdma_mqe *rsp_mqe;
+ struct ocrdma_mbx_rsp *rsp = NULL;
mutex_lock(&dev->mqe_ctx.lock);
ocrdma_post_mqe(dev, mqe);
@@ -958,23 +981,61 @@ static int ocrdma_mbx_cmd(struct ocrdma_dev *dev, struct ocrdma_mqe *mqe)
goto mbx_err;
cqe_status = dev->mqe_ctx.cqe_status;
ext_status = dev->mqe_ctx.ext_status;
- rsp = ocrdma_get_mqe_rsp(dev);
- ocrdma_copy_le32_to_cpu(mqe, rsp, (sizeof(*mqe)));
+ rsp_mqe = ocrdma_get_mqe_rsp(dev);
+ ocrdma_copy_le32_to_cpu(mqe, rsp_mqe, (sizeof(*mqe)));
+ if ((mqe->hdr.spcl_sge_cnt_emb & OCRDMA_MQE_HDR_EMB_MASK) >>
+ OCRDMA_MQE_HDR_EMB_SHIFT)
+ rsp = &mqe->u.rsp;
+
if (cqe_status || ext_status) {
- pr_err("%s() opcode=0x%x, cqe_status=0x%x, ext_status=0x%x\n",
- __func__,
- (rsp->u.rsp.subsys_op & OCRDMA_MBX_RSP_OPCODE_MASK) >>
- OCRDMA_MBX_RSP_OPCODE_SHIFT, cqe_status, ext_status);
+ pr_err("%s() cqe_status=0x%x, ext_status=0x%x,",
+ __func__, cqe_status, ext_status);
+ if (rsp) {
+ /* This is for embedded cmds. */
+ pr_err("opcode=0x%x, subsystem=0x%x\n",
+ (rsp->subsys_op & OCRDMA_MBX_RSP_OPCODE_MASK) >>
+ OCRDMA_MBX_RSP_OPCODE_SHIFT,
+ (rsp->subsys_op & OCRDMA_MBX_RSP_SUBSYS_MASK) >>
+ OCRDMA_MBX_RSP_SUBSYS_SHIFT);
+ }
status = ocrdma_get_mbx_cqe_errno(cqe_status);
goto mbx_err;
}
- if (mqe->u.rsp.status & OCRDMA_MBX_RSP_STATUS_MASK)
+ /* For non embedded, rsp errors are handled in ocrdma_nonemb_mbx_cmd */
+ if (rsp && (mqe->u.rsp.status & OCRDMA_MBX_RSP_STATUS_MASK))
status = ocrdma_get_mbx_errno(mqe->u.rsp.status);
mbx_err:
mutex_unlock(&dev->mqe_ctx.lock);
return status;
}
+static int ocrdma_nonemb_mbx_cmd(struct ocrdma_dev *dev, struct ocrdma_mqe *mqe,
+ void *payload_va)
+{
+ int status = 0;
+ struct ocrdma_mbx_rsp *rsp = payload_va;
+
+ if ((mqe->hdr.spcl_sge_cnt_emb & OCRDMA_MQE_HDR_EMB_MASK) >>
+ OCRDMA_MQE_HDR_EMB_SHIFT)
+ BUG();
+
+ status = ocrdma_mbx_cmd(dev, mqe);
+ if (!status)
+ /* For non embedded, only CQE failures are handled in
+ * ocrdma_mbx_cmd. We need to check for RSP errors.
+ */
+ if (rsp->status & OCRDMA_MBX_RSP_STATUS_MASK)
+ status = ocrdma_get_mbx_errno(rsp->status);
+
+ if (status)
+ pr_err("opcode=0x%x, subsystem=0x%x\n",
+ (rsp->subsys_op & OCRDMA_MBX_RSP_OPCODE_MASK) >>
+ OCRDMA_MBX_RSP_OPCODE_SHIFT,
+ (rsp->subsys_op & OCRDMA_MBX_RSP_SUBSYS_MASK) >>
+ OCRDMA_MBX_RSP_SUBSYS_SHIFT);
+ return status;
+}
+
static void ocrdma_get_attr(struct ocrdma_dev *dev,
struct ocrdma_dev_attr *attr,
struct ocrdma_mbx_query_config *rsp)
@@ -985,6 +1046,9 @@ static void ocrdma_get_attr(struct ocrdma_dev *dev,
attr->max_qp =
(rsp->qp_srq_cq_ird_ord & OCRDMA_MBX_QUERY_CFG_MAX_QP_MASK) >>
OCRDMA_MBX_QUERY_CFG_MAX_QP_SHIFT;
+ attr->max_srq =
+ (rsp->max_srq_rpir_qps & OCRDMA_MBX_QUERY_CFG_MAX_SRQ_MASK) >>
+ OCRDMA_MBX_QUERY_CFG_MAX_SRQ_OFFSET;
attr->max_send_sge = ((rsp->max_write_send_sge &
OCRDMA_MBX_QUERY_CFG_MAX_SEND_SGE_MASK) >>
OCRDMA_MBX_QUERY_CFG_MAX_SEND_SGE_SHIFT);
@@ -1000,9 +1064,6 @@ static void ocrdma_get_attr(struct ocrdma_dev *dev,
attr->max_ord_per_qp = (rsp->max_ird_ord_per_qp &
OCRDMA_MBX_QUERY_CFG_MAX_ORD_PER_QP_MASK) >>
OCRDMA_MBX_QUERY_CFG_MAX_ORD_PER_QP_SHIFT;
- attr->max_srq =
- (rsp->max_srq_rpir_qps & OCRDMA_MBX_QUERY_CFG_MAX_SRQ_MASK) >>
- OCRDMA_MBX_QUERY_CFG_MAX_SRQ_OFFSET;
attr->max_ird_per_qp = (rsp->max_ird_ord_per_qp &
OCRDMA_MBX_QUERY_CFG_MAX_IRD_PER_QP_MASK) >>
OCRDMA_MBX_QUERY_CFG_MAX_IRD_PER_QP_SHIFT;
@@ -1015,6 +1076,7 @@ static void ocrdma_get_attr(struct ocrdma_dev *dev,
attr->local_ca_ack_delay = (rsp->max_pd_ca_ack_delay &
OCRDMA_MBX_QUERY_CFG_CA_ACK_DELAY_MASK) >>
OCRDMA_MBX_QUERY_CFG_CA_ACK_DELAY_SHIFT;
+ attr->max_mw = rsp->max_mw;
attr->max_mr = rsp->max_mr;
attr->max_mr_size = ~0ull;
attr->max_fmr = 0;
@@ -1036,7 +1098,7 @@ static void ocrdma_get_attr(struct ocrdma_dev *dev,
attr->max_inline_data =
attr->wqe_size - (sizeof(struct ocrdma_hdr_wqe) +
sizeof(struct ocrdma_sge));
- if (dev->nic_info.dev_family == OCRDMA_GEN2_FAMILY) {
+ if (ocrdma_get_asic_type(dev) == OCRDMA_ASIC_GEN_SKH_R) {
attr->ird = 1;
attr->ird_page_size = OCRDMA_MIN_Q_PAGE_SIZE;
attr->num_ird_pages = MAX_OCRDMA_IRD_PAGES;
@@ -1110,6 +1172,96 @@ mbx_err:
return status;
}
+int ocrdma_mbx_rdma_stats(struct ocrdma_dev *dev, bool reset)
+{
+ struct ocrdma_rdma_stats_req *req = dev->stats_mem.va;
+ struct ocrdma_mqe *mqe = &dev->stats_mem.mqe;
+ struct ocrdma_rdma_stats_resp *old_stats = NULL;
+ int status;
+
+ old_stats = kzalloc(sizeof(*old_stats), GFP_KERNEL);
+ if (old_stats == NULL)
+ return -ENOMEM;
+
+ memset(mqe, 0, sizeof(*mqe));
+ mqe->hdr.pyld_len = dev->stats_mem.size;
+ mqe->hdr.spcl_sge_cnt_emb |=
+ (1 << OCRDMA_MQE_HDR_SGE_CNT_SHIFT) &
+ OCRDMA_MQE_HDR_SGE_CNT_MASK;
+ mqe->u.nonemb_req.sge[0].pa_lo = (u32) (dev->stats_mem.pa & 0xffffffff);
+ mqe->u.nonemb_req.sge[0].pa_hi = (u32) upper_32_bits(dev->stats_mem.pa);
+ mqe->u.nonemb_req.sge[0].len = dev->stats_mem.size;
+
+ /* Cache the old stats */
+ memcpy(old_stats, req, sizeof(struct ocrdma_rdma_stats_resp));
+ memset(req, 0, dev->stats_mem.size);
+
+ ocrdma_init_mch((struct ocrdma_mbx_hdr *)req,
+ OCRDMA_CMD_GET_RDMA_STATS,
+ OCRDMA_SUBSYS_ROCE,
+ dev->stats_mem.size);
+ if (reset)
+ req->reset_stats = reset;
+
+ status = ocrdma_nonemb_mbx_cmd(dev, mqe, dev->stats_mem.va);
+ if (status)
+ /* Copy from cache, if mbox fails */
+ memcpy(req, old_stats, sizeof(struct ocrdma_rdma_stats_resp));
+ else
+ ocrdma_le32_to_cpu(req, dev->stats_mem.size);
+
+ kfree(old_stats);
+ return status;
+}
+
+static int ocrdma_mbx_get_ctrl_attribs(struct ocrdma_dev *dev)
+{
+ int status = -ENOMEM;
+ struct ocrdma_dma_mem dma;
+ struct ocrdma_mqe *mqe;
+ struct ocrdma_get_ctrl_attribs_rsp *ctrl_attr_rsp;
+ struct mgmt_hba_attribs *hba_attribs;
+
+ mqe = ocrdma_alloc_mqe();
+ if (!mqe)
+ return status;
+ memset(mqe, 0, sizeof(*mqe));
+
+ dma.size = sizeof(struct ocrdma_get_ctrl_attribs_rsp);
+ dma.va = dma_alloc_coherent(&dev->nic_info.pdev->dev,
+ dma.size, &dma.pa, GFP_KERNEL);
+ if (!dma.va)
+ goto free_mqe;
+
+ mqe->hdr.pyld_len = dma.size;
+ mqe->hdr.spcl_sge_cnt_emb |=
+ (1 << OCRDMA_MQE_HDR_SGE_CNT_SHIFT) &
+ OCRDMA_MQE_HDR_SGE_CNT_MASK;
+ mqe->u.nonemb_req.sge[0].pa_lo = (u32) (dma.pa & 0xffffffff);
+ mqe->u.nonemb_req.sge[0].pa_hi = (u32) upper_32_bits(dma.pa);
+ mqe->u.nonemb_req.sge[0].len = dma.size;
+
+ memset(dma.va, 0, dma.size);
+ ocrdma_init_mch((struct ocrdma_mbx_hdr *)dma.va,
+ OCRDMA_CMD_GET_CTRL_ATTRIBUTES,
+ OCRDMA_SUBSYS_COMMON,
+ dma.size);
+
+ status = ocrdma_nonemb_mbx_cmd(dev, mqe, dma.va);
+ if (!status) {
+ ctrl_attr_rsp = (struct ocrdma_get_ctrl_attribs_rsp *)dma.va;
+ hba_attribs = &ctrl_attr_rsp->ctrl_attribs.hba_attribs;
+
+ dev->hba_port_num = hba_attribs->phy_port;
+ strncpy(dev->model_number,
+ hba_attribs->controller_model_number, 31);
+ }
+ dma_free_coherent(&dev->nic_info.pdev->dev, dma.size, dma.va, dma.pa);
+free_mqe:
+ kfree(mqe);
+ return status;
+}
+
static int ocrdma_mbx_query_dev(struct ocrdma_dev *dev)
{
int status = -ENOMEM;
@@ -1157,6 +1309,35 @@ mbx_err:
return status;
}
+static int ocrdma_mbx_get_phy_info(struct ocrdma_dev *dev)
+{
+ int status = -ENOMEM;
+ struct ocrdma_mqe *cmd;
+ struct ocrdma_get_phy_info_rsp *rsp;
+
+ cmd = ocrdma_init_emb_mqe(OCRDMA_CMD_PHY_DETAILS, sizeof(*cmd));
+ if (!cmd)
+ return status;
+
+ ocrdma_init_mch((struct ocrdma_mbx_hdr *)&cmd->u.cmd[0],
+ OCRDMA_CMD_PHY_DETAILS, OCRDMA_SUBSYS_COMMON,
+ sizeof(*cmd));
+
+ status = ocrdma_mbx_cmd(dev, (struct ocrdma_mqe *)cmd);
+ if (status)
+ goto mbx_err;
+
+ rsp = (struct ocrdma_get_phy_info_rsp *)cmd;
+ dev->phy.phy_type = le16_to_cpu(rsp->phy_type);
+ dev->phy.auto_speeds_supported =
+ le16_to_cpu(rsp->auto_speeds_supported);
+ dev->phy.fixed_speeds_supported =
+ le16_to_cpu(rsp->fixed_speeds_supported);
+mbx_err:
+ kfree(cmd);
+ return status;
+}
+
int ocrdma_mbx_alloc_pd(struct ocrdma_dev *dev, struct ocrdma_pd *pd)
{
int status = -ENOMEM;
@@ -1226,7 +1407,7 @@ static int ocrdma_build_q_conf(u32 *num_entries, int entry_size,
static int ocrdma_mbx_create_ah_tbl(struct ocrdma_dev *dev)
{
- int i ;
+ int i;
int status = 0;
int max_ah;
struct ocrdma_create_ah_tbl *cmd;
@@ -1357,12 +1538,10 @@ static void ocrdma_unbind_eq(struct ocrdma_dev *dev, u16 eq_id)
int i;
mutex_lock(&dev->dev_lock);
- for (i = 0; i < dev->eq_cnt; i++) {
- if (dev->eq_tbl[i].q.id != eq_id)
- continue;
- dev->eq_tbl[i].cq_cnt -= 1;
- break;
- }
+ i = ocrdma_get_eq_table_index(dev, eq_id);
+ if (i == -EINVAL)
+ BUG();
+ dev->eq_tbl[i].cq_cnt -= 1;
mutex_unlock(&dev->dev_lock);
}
@@ -1380,7 +1559,7 @@ int ocrdma_mbx_create_cq(struct ocrdma_dev *dev, struct ocrdma_cq *cq,
__func__, dev->id, dev->attr.max_cqe, entries);
return -EINVAL;
}
- if (dpp_cq && (dev->nic_info.dev_family != OCRDMA_GEN2_FAMILY))
+ if (dpp_cq && (ocrdma_get_asic_type(dev) != OCRDMA_ASIC_GEN_SKH_R))
return -EINVAL;
if (dpp_cq) {
@@ -1417,6 +1596,7 @@ int ocrdma_mbx_create_cq(struct ocrdma_dev *dev, struct ocrdma_cq *cq,
cq->eqn = ocrdma_bind_eq(dev);
cmd->cmd.req.rsvd_version = OCRDMA_CREATE_CQ_VER3;
cqe_count = cq->len / cqe_size;
+ cq->cqe_cnt = cqe_count;
if (cqe_count > 1024) {
/* Set cnt to 3 to indicate more than 1024 cq entries */
cmd->cmd.ev_cnt_flags |= (0x3 << OCRDMA_CREATE_CQ_CNT_SHIFT);
@@ -1439,7 +1619,7 @@ int ocrdma_mbx_create_cq(struct ocrdma_dev *dev, struct ocrdma_cq *cq,
}
/* shared eq between all the consumer cqs. */
cmd->cmd.eqn = cq->eqn;
- if (dev->nic_info.dev_family == OCRDMA_GEN2_FAMILY) {
+ if (ocrdma_get_asic_type(dev) == OCRDMA_ASIC_GEN_SKH_R) {
if (dpp_cq)
cmd->cmd.pgsz_pgcnt |= OCRDMA_CREATE_CQ_DPP <<
OCRDMA_CREATE_CQ_TYPE_SHIFT;
@@ -1484,12 +1664,9 @@ int ocrdma_mbx_destroy_cq(struct ocrdma_dev *dev, struct ocrdma_cq *cq)
(cq->id << OCRDMA_DESTROY_CQ_QID_SHIFT) &
OCRDMA_DESTROY_CQ_QID_MASK;
- ocrdma_unbind_eq(dev, cq->eqn);
status = ocrdma_mbx_cmd(dev, (struct ocrdma_mqe *)cmd);
- if (status)
- goto mbx_err;
+ ocrdma_unbind_eq(dev, cq->eqn);
dma_free_coherent(&dev->nic_info.pdev->dev, cq->len, cq->va, cq->pa);
-mbx_err:
kfree(cmd);
return status;
}
@@ -2029,8 +2206,7 @@ int ocrdma_mbx_create_qp(struct ocrdma_qp *qp, struct ib_qp_init_attr *attrs,
OCRDMA_CREATE_QP_REQ_RQ_CQID_MASK;
qp->rq_cq = cq;
- if (pd->dpp_enabled && attrs->cap.max_inline_data && pd->num_dpp_qp &&
- (attrs->cap.max_inline_data <= dev->attr.max_inline_data)) {
+ if (pd->dpp_enabled && pd->num_dpp_qp) {
ocrdma_set_create_qp_dpp_cmd(cmd, pd, qp, enable_dpp_cq,
dpp_cq_id);
}
@@ -2099,7 +2275,7 @@ static int ocrdma_set_av_params(struct ocrdma_qp *qp,
memcpy(&cmd->params.dgid[0], &ah_attr->grh.dgid.raw[0],
sizeof(cmd->params.dgid));
status = ocrdma_query_gid(&qp->dev->ibdev, 1,
- ah_attr->grh.sgid_index, &sgid);
+ ah_attr->grh.sgid_index, &sgid);
if (status)
return status;
@@ -2127,8 +2303,7 @@ static int ocrdma_set_av_params(struct ocrdma_qp *qp,
static int ocrdma_set_qp_params(struct ocrdma_qp *qp,
struct ocrdma_modify_qp *cmd,
- struct ib_qp_attr *attrs, int attr_mask,
- enum ib_qp_state old_qps)
+ struct ib_qp_attr *attrs, int attr_mask)
{
int status = 0;
@@ -2233,8 +2408,7 @@ pmtu_err:
}
int ocrdma_mbx_modify_qp(struct ocrdma_dev *dev, struct ocrdma_qp *qp,
- struct ib_qp_attr *attrs, int attr_mask,
- enum ib_qp_state old_qps)
+ struct ib_qp_attr *attrs, int attr_mask)
{
int status = -ENOMEM;
struct ocrdma_modify_qp *cmd;
@@ -2257,7 +2431,7 @@ int ocrdma_mbx_modify_qp(struct ocrdma_dev *dev, struct ocrdma_qp *qp,
OCRDMA_QP_PARAMS_STATE_MASK;
}
- status = ocrdma_set_qp_params(qp, cmd, attrs, attr_mask, old_qps);
+ status = ocrdma_set_qp_params(qp, cmd, attrs, attr_mask);
if (status)
goto mbx_err;
status = ocrdma_mbx_cmd(dev, (struct ocrdma_mqe *)cmd);
@@ -2488,7 +2662,7 @@ static int ocrdma_create_eqs(struct ocrdma_dev *dev)
for (i = 0; i < num_eq; i++) {
status = ocrdma_create_eq(dev, &dev->eq_tbl[i],
- OCRDMA_EQ_LEN);
+ OCRDMA_EQ_LEN);
if (status) {
status = -EINVAL;
break;
@@ -2533,6 +2707,13 @@ int ocrdma_init_hw(struct ocrdma_dev *dev)
status = ocrdma_mbx_create_ah_tbl(dev);
if (status)
goto conf_err;
+ status = ocrdma_mbx_get_phy_info(dev);
+ if (status)
+ goto conf_err;
+ status = ocrdma_mbx_get_ctrl_attribs(dev);
+ if (status)
+ goto conf_err;
+
return 0;
conf_err:
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_hw.h b/drivers/infiniband/hw/ocrdma/ocrdma_hw.h
index 82fe332ae6c6..e513f7293142 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_hw.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_hw.h
@@ -112,8 +112,7 @@ int ocrdma_mbx_create_qp(struct ocrdma_qp *, struct ib_qp_init_attr *attrs,
u8 enable_dpp_cq, u16 dpp_cq_id, u16 *dpp_offset,
u16 *dpp_credit_lmt);
int ocrdma_mbx_modify_qp(struct ocrdma_dev *, struct ocrdma_qp *,
- struct ib_qp_attr *attrs, int attr_mask,
- enum ib_qp_state old_qps);
+ struct ib_qp_attr *attrs, int attr_mask);
int ocrdma_mbx_query_qp(struct ocrdma_dev *, struct ocrdma_qp *,
struct ocrdma_qp_params *param);
int ocrdma_mbx_destroy_qp(struct ocrdma_dev *, struct ocrdma_qp *);
@@ -132,5 +131,8 @@ int ocrdma_qp_state_change(struct ocrdma_qp *, enum ib_qp_state new_state,
bool ocrdma_is_qp_in_sq_flushlist(struct ocrdma_cq *, struct ocrdma_qp *);
bool ocrdma_is_qp_in_rq_flushlist(struct ocrdma_cq *, struct ocrdma_qp *);
void ocrdma_flush_qp(struct ocrdma_qp *);
+int ocrdma_get_irq(struct ocrdma_dev *dev, struct ocrdma_eq *eq);
+int ocrdma_mbx_rdma_stats(struct ocrdma_dev *, bool reset);
+char *port_speed_string(struct ocrdma_dev *dev);
#endif /* __OCRDMA_HW_H__ */
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
index 1a8a945efa60..7c504e079744 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
@@ -39,10 +39,11 @@
#include "ocrdma_ah.h"
#include "be_roce.h"
#include "ocrdma_hw.h"
+#include "ocrdma_stats.h"
#include "ocrdma_abi.h"
-MODULE_VERSION(OCRDMA_ROCE_DEV_VERSION);
-MODULE_DESCRIPTION("Emulex RoCE HCA Driver");
+MODULE_VERSION(OCRDMA_ROCE_DRV_VERSION);
+MODULE_DESCRIPTION(OCRDMA_ROCE_DRV_DESC " " OCRDMA_ROCE_DRV_VERSION);
MODULE_AUTHOR("Emulex Corporation");
MODULE_LICENSE("GPL");
@@ -286,7 +287,7 @@ static int ocrdma_register_device(struct ocrdma_dev *dev)
dev->ibdev.process_mad = ocrdma_process_mad;
- if (dev->nic_info.dev_family == OCRDMA_GEN2_FAMILY) {
+ if (ocrdma_get_asic_type(dev) == OCRDMA_ASIC_GEN_SKH_R) {
dev->ibdev.uverbs_cmd_mask |=
OCRDMA_UVERBS(CREATE_SRQ) |
OCRDMA_UVERBS(MODIFY_SRQ) |
@@ -338,9 +339,42 @@ static void ocrdma_free_resources(struct ocrdma_dev *dev)
kfree(dev->sgid_tbl);
}
+/* OCRDMA sysfs interface */
+static ssize_t show_rev(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct ocrdma_dev *dev = dev_get_drvdata(device);
+
+ return scnprintf(buf, PAGE_SIZE, "0x%x\n", dev->nic_info.pdev->vendor);
+}
+
+static ssize_t show_fw_ver(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct ocrdma_dev *dev = dev_get_drvdata(device);
+
+ return scnprintf(buf, PAGE_SIZE, "%s", &dev->attr.fw_ver[0]);
+}
+
+static DEVICE_ATTR(hw_rev, S_IRUGO, show_rev, NULL);
+static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
+
+static struct device_attribute *ocrdma_attributes[] = {
+ &dev_attr_hw_rev,
+ &dev_attr_fw_ver
+};
+
+static void ocrdma_remove_sysfiles(struct ocrdma_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ocrdma_attributes); i++)
+ device_remove_file(&dev->ibdev.dev, ocrdma_attributes[i]);
+}
+
static struct ocrdma_dev *ocrdma_add(struct be_dev_info *dev_info)
{
- int status = 0;
+ int status = 0, i;
struct ocrdma_dev *dev;
dev = (struct ocrdma_dev *)ib_alloc_device(sizeof(struct ocrdma_dev));
@@ -369,11 +403,25 @@ static struct ocrdma_dev *ocrdma_add(struct be_dev_info *dev_info)
if (status)
goto alloc_err;
+ for (i = 0; i < ARRAY_SIZE(ocrdma_attributes); i++)
+ if (device_create_file(&dev->ibdev.dev, ocrdma_attributes[i]))
+ goto sysfs_err;
spin_lock(&ocrdma_devlist_lock);
list_add_tail_rcu(&dev->entry, &ocrdma_dev_list);
spin_unlock(&ocrdma_devlist_lock);
+ /* Init stats */
+ ocrdma_add_port_stats(dev);
+
+ pr_info("%s %s: %s \"%s\" port %d\n",
+ dev_name(&dev->nic_info.pdev->dev), hca_name(dev),
+ port_speed_string(dev), dev->model_number,
+ dev->hba_port_num);
+ pr_info("%s ocrdma%d driver loaded successfully\n",
+ dev_name(&dev->nic_info.pdev->dev), dev->id);
return dev;
+sysfs_err:
+ ocrdma_remove_sysfiles(dev);
alloc_err:
ocrdma_free_resources(dev);
ocrdma_cleanup_hw(dev);
@@ -400,6 +448,9 @@ static void ocrdma_remove(struct ocrdma_dev *dev)
/* first unregister with stack to stop all the active traffic
* of the registered clients.
*/
+ ocrdma_rem_port_stats(dev);
+ ocrdma_remove_sysfiles(dev);
+
ib_unregister_device(&dev->ibdev);
spin_lock(&ocrdma_devlist_lock);
@@ -437,7 +488,7 @@ static int ocrdma_close(struct ocrdma_dev *dev)
cur_qp = dev->qp_tbl;
for (i = 0; i < OCRDMA_MAX_QP; i++) {
qp = cur_qp[i];
- if (qp) {
+ if (qp && qp->ibqp.qp_type != IB_QPT_GSI) {
/* change the QP state to ERROR */
_ocrdma_modify_qp(&qp->ibqp, &attrs, attr_mask);
@@ -478,6 +529,7 @@ static struct ocrdma_driver ocrdma_drv = {
.add = ocrdma_add,
.remove = ocrdma_remove,
.state_change_handler = ocrdma_event_handler,
+ .be_abi_version = OCRDMA_BE_ROCE_ABI_VERSION,
};
static void ocrdma_unregister_inet6addr_notifier(void)
@@ -487,10 +539,17 @@ static void ocrdma_unregister_inet6addr_notifier(void)
#endif
}
+static void ocrdma_unregister_inetaddr_notifier(void)
+{
+ unregister_inetaddr_notifier(&ocrdma_inetaddr_notifier);
+}
+
static int __init ocrdma_init_module(void)
{
int status;
+ ocrdma_init_debugfs();
+
status = register_inetaddr_notifier(&ocrdma_inetaddr_notifier);
if (status)
return status;
@@ -498,13 +557,19 @@ static int __init ocrdma_init_module(void)
#if IS_ENABLED(CONFIG_IPV6)
status = register_inet6addr_notifier(&ocrdma_inet6addr_notifier);
if (status)
- return status;
+ goto err_notifier6;
#endif
status = be_roce_register_driver(&ocrdma_drv);
if (status)
- ocrdma_unregister_inet6addr_notifier();
+ goto err_be_reg;
+ return 0;
+
+err_be_reg:
+ ocrdma_unregister_inet6addr_notifier();
+err_notifier6:
+ ocrdma_unregister_inetaddr_notifier();
return status;
}
@@ -512,6 +577,8 @@ static void __exit ocrdma_exit_module(void)
{
be_roce_unregister_driver(&ocrdma_drv);
ocrdma_unregister_inet6addr_notifier();
+ ocrdma_unregister_inetaddr_notifier();
+ ocrdma_rem_debugfs();
}
module_init(ocrdma_init_module);
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_sli.h b/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
index 60d5ac23ea80..96c9ee602ba4 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
@@ -30,8 +30,16 @@
#define Bit(_b) (1 << (_b))
-#define OCRDMA_GEN1_FAMILY 0xB
-#define OCRDMA_GEN2_FAMILY 0x0F
+enum {
+ OCRDMA_ASIC_GEN_SKH_R = 0x04,
+ OCRDMA_ASIC_GEN_LANCER = 0x0B
+};
+
+enum {
+ OCRDMA_ASIC_REV_A0 = 0x00,
+ OCRDMA_ASIC_REV_B0 = 0x10,
+ OCRDMA_ASIC_REV_C0 = 0x20
+};
#define OCRDMA_SUBSYS_ROCE 10
enum {
@@ -64,6 +72,7 @@ enum {
OCRDMA_CMD_ATTACH_MCAST,
OCRDMA_CMD_DETACH_MCAST,
+ OCRDMA_CMD_GET_RDMA_STATS,
OCRDMA_CMD_MAX
};
@@ -74,12 +83,14 @@ enum {
OCRDMA_CMD_CREATE_CQ = 12,
OCRDMA_CMD_CREATE_EQ = 13,
OCRDMA_CMD_CREATE_MQ = 21,
+ OCRDMA_CMD_GET_CTRL_ATTRIBUTES = 32,
OCRDMA_CMD_GET_FW_VER = 35,
OCRDMA_CMD_DELETE_MQ = 53,
OCRDMA_CMD_DELETE_CQ = 54,
OCRDMA_CMD_DELETE_EQ = 55,
OCRDMA_CMD_GET_FW_CONFIG = 58,
- OCRDMA_CMD_CREATE_MQ_EXT = 90
+ OCRDMA_CMD_CREATE_MQ_EXT = 90,
+ OCRDMA_CMD_PHY_DETAILS = 102
};
enum {
@@ -103,7 +114,10 @@ enum {
OCRDMA_DB_GEN2_SRQ_OFFSET = OCRDMA_DB_GEN2_RQ_OFFSET,
OCRDMA_DB_CQ_OFFSET = 0x120,
OCRDMA_DB_EQ_OFFSET = OCRDMA_DB_CQ_OFFSET,
- OCRDMA_DB_MQ_OFFSET = 0x140
+ OCRDMA_DB_MQ_OFFSET = 0x140,
+
+ OCRDMA_DB_SQ_SHIFT = 16,
+ OCRDMA_DB_RQ_SHIFT = 24
};
#define OCRDMA_DB_CQ_RING_ID_MASK 0x3FF /* bits 0 - 9 */
@@ -138,6 +152,10 @@ enum {
#define OCRDMA_MIN_Q_PAGE_SIZE (4096)
#define OCRDMA_MAX_Q_PAGES (8)
+#define OCRDMA_SLI_ASIC_ID_OFFSET 0x9C
+#define OCRDMA_SLI_ASIC_REV_MASK 0x000000FF
+#define OCRDMA_SLI_ASIC_GEN_NUM_MASK 0x0000FF00
+#define OCRDMA_SLI_ASIC_GEN_NUM_SHIFT 0x08
/*
# 0: 4K Bytes
# 1: 8K Bytes
@@ -562,6 +580,30 @@ enum {
OCRDMA_FN_MODE_RDMA = 0x4
};
+struct ocrdma_get_phy_info_rsp {
+ struct ocrdma_mqe_hdr hdr;
+ struct ocrdma_mbx_rsp rsp;
+
+ u16 phy_type;
+ u16 interface_type;
+ u32 misc_params;
+ u16 ext_phy_details;
+ u16 rsvd;
+ u16 auto_speeds_supported;
+ u16 fixed_speeds_supported;
+ u32 future_use[2];
+};
+
+enum {
+ OCRDMA_PHY_SPEED_ZERO = 0x0,
+ OCRDMA_PHY_SPEED_10MBPS = 0x1,
+ OCRDMA_PHY_SPEED_100MBPS = 0x2,
+ OCRDMA_PHY_SPEED_1GBPS = 0x4,
+ OCRDMA_PHY_SPEED_10GBPS = 0x8,
+ OCRDMA_PHY_SPEED_40GBPS = 0x20
+};
+
+
struct ocrdma_get_link_speed_rsp {
struct ocrdma_mqe_hdr hdr;
struct ocrdma_mbx_rsp rsp;
@@ -590,7 +632,7 @@ enum {
enum {
OCRDMA_CREATE_CQ_VER2 = 2,
- OCRDMA_CREATE_CQ_VER3 = 3,
+ OCRDMA_CREATE_CQ_VER3 = 3,
OCRDMA_CREATE_CQ_PAGE_CNT_MASK = 0xFFFF,
OCRDMA_CREATE_CQ_PAGE_SIZE_SHIFT = 16,
@@ -1050,6 +1092,7 @@ enum {
OCRDMA_MODIFY_QP_RSP_MAX_ORD_MASK = 0xFFFF <<
OCRDMA_MODIFY_QP_RSP_MAX_ORD_SHIFT
};
+
struct ocrdma_modify_qp_rsp {
struct ocrdma_mqe_hdr hdr;
struct ocrdma_mbx_rsp rsp;
@@ -1062,8 +1105,8 @@ struct ocrdma_query_qp {
struct ocrdma_mqe_hdr hdr;
struct ocrdma_mbx_hdr req;
-#define OCRDMA_QUERY_UP_QP_ID_SHIFT 0
-#define OCRDMA_QUERY_UP_QP_ID_MASK 0xFFFFFF
+#define OCRDMA_QUERY_UP_QP_ID_SHIFT 0
+#define OCRDMA_QUERY_UP_QP_ID_MASK 0xFFFFFF
u32 qp_id;
};
@@ -1703,4 +1746,208 @@ struct ocrdma_av {
u32 valid;
} __packed;
+struct ocrdma_rsrc_stats {
+ u32 dpp_pds;
+ u32 non_dpp_pds;
+ u32 rc_dpp_qps;
+ u32 uc_dpp_qps;
+ u32 ud_dpp_qps;
+ u32 rc_non_dpp_qps;
+ u32 rsvd;
+ u32 uc_non_dpp_qps;
+ u32 ud_non_dpp_qps;
+ u32 rsvd1;
+ u32 srqs;
+ u32 rbqs;
+ u32 r64K_nsmr;
+ u32 r64K_to_2M_nsmr;
+ u32 r2M_to_44M_nsmr;
+ u32 r44M_to_1G_nsmr;
+ u32 r1G_to_4G_nsmr;
+ u32 nsmr_count_4G_to_32G;
+ u32 r32G_to_64G_nsmr;
+ u32 r64G_to_128G_nsmr;
+ u32 r128G_to_higher_nsmr;
+ u32 embedded_nsmr;
+ u32 frmr;
+ u32 prefetch_qps;
+ u32 ondemand_qps;
+ u32 phy_mr;
+ u32 mw;
+ u32 rsvd2[7];
+};
+
+struct ocrdma_db_err_stats {
+ u32 sq_doorbell_errors;
+ u32 cq_doorbell_errors;
+ u32 rq_srq_doorbell_errors;
+ u32 cq_overflow_errors;
+ u32 rsvd[4];
+};
+
+struct ocrdma_wqe_stats {
+ u32 large_send_rc_wqes_lo;
+ u32 large_send_rc_wqes_hi;
+ u32 large_write_rc_wqes_lo;
+ u32 large_write_rc_wqes_hi;
+ u32 rsvd[4];
+ u32 read_wqes_lo;
+ u32 read_wqes_hi;
+ u32 frmr_wqes_lo;
+ u32 frmr_wqes_hi;
+ u32 mw_bind_wqes_lo;
+ u32 mw_bind_wqes_hi;
+ u32 invalidate_wqes_lo;
+ u32 invalidate_wqes_hi;
+ u32 rsvd1[2];
+ u32 dpp_wqe_drops;
+ u32 rsvd2[5];
+};
+
+struct ocrdma_tx_stats {
+ u32 send_pkts_lo;
+ u32 send_pkts_hi;
+ u32 write_pkts_lo;
+ u32 write_pkts_hi;
+ u32 read_pkts_lo;
+ u32 read_pkts_hi;
+ u32 read_rsp_pkts_lo;
+ u32 read_rsp_pkts_hi;
+ u32 ack_pkts_lo;
+ u32 ack_pkts_hi;
+ u32 send_bytes_lo;
+ u32 send_bytes_hi;
+ u32 write_bytes_lo;
+ u32 write_bytes_hi;
+ u32 read_req_bytes_lo;
+ u32 read_req_bytes_hi;
+ u32 read_rsp_bytes_lo;
+ u32 read_rsp_bytes_hi;
+ u32 ack_timeouts;
+ u32 rsvd[5];
+};
+
+
+struct ocrdma_tx_qp_err_stats {
+ u32 local_length_errors;
+ u32 local_protection_errors;
+ u32 local_qp_operation_errors;
+ u32 retry_count_exceeded_errors;
+ u32 rnr_retry_count_exceeded_errors;
+ u32 rsvd[3];
+};
+
+struct ocrdma_rx_stats {
+ u32 roce_frame_bytes_lo;
+ u32 roce_frame_bytes_hi;
+ u32 roce_frame_icrc_drops;
+ u32 roce_frame_payload_len_drops;
+ u32 ud_drops;
+ u32 qp1_drops;
+ u32 psn_error_request_packets;
+ u32 psn_error_resp_packets;
+ u32 rnr_nak_timeouts;
+ u32 rnr_nak_receives;
+ u32 roce_frame_rxmt_drops;
+ u32 nak_count_psn_sequence_errors;
+ u32 rc_drop_count_lookup_errors;
+ u32 rq_rnr_naks;
+ u32 srq_rnr_naks;
+ u32 roce_frames_lo;
+ u32 roce_frames_hi;
+ u32 rsvd;
+};
+
+struct ocrdma_rx_qp_err_stats {
+ u32 nak_invalid_requst_errors;
+ u32 nak_remote_operation_errors;
+ u32 nak_count_remote_access_errors;
+ u32 local_length_errors;
+ u32 local_protection_errors;
+ u32 local_qp_operation_errors;
+ u32 rsvd[2];
+};
+
+struct ocrdma_tx_dbg_stats {
+ u32 data[100];
+};
+
+struct ocrdma_rx_dbg_stats {
+ u32 data[200];
+};
+
+struct ocrdma_rdma_stats_req {
+ struct ocrdma_mbx_hdr hdr;
+ u8 reset_stats;
+ u8 rsvd[3];
+} __packed;
+
+struct ocrdma_rdma_stats_resp {
+ struct ocrdma_mbx_hdr hdr;
+ struct ocrdma_rsrc_stats act_rsrc_stats;
+ struct ocrdma_rsrc_stats th_rsrc_stats;
+ struct ocrdma_db_err_stats db_err_stats;
+ struct ocrdma_wqe_stats wqe_stats;
+ struct ocrdma_tx_stats tx_stats;
+ struct ocrdma_tx_qp_err_stats tx_qp_err_stats;
+ struct ocrdma_rx_stats rx_stats;
+ struct ocrdma_rx_qp_err_stats rx_qp_err_stats;
+ struct ocrdma_tx_dbg_stats tx_dbg_stats;
+ struct ocrdma_rx_dbg_stats rx_dbg_stats;
+} __packed;
+
+
+struct mgmt_hba_attribs {
+ u8 flashrom_version_string[32];
+ u8 manufacturer_name[32];
+ u32 supported_modes;
+ u32 rsvd0[3];
+ u8 ncsi_ver_string[12];
+ u32 default_extended_timeout;
+ u8 controller_model_number[32];
+ u8 controller_description[64];
+ u8 controller_serial_number[32];
+ u8 ip_version_string[32];
+ u8 firmware_version_string[32];
+ u8 bios_version_string[32];
+ u8 redboot_version_string[32];
+ u8 driver_version_string[32];
+ u8 fw_on_flash_version_string[32];
+ u32 functionalities_supported;
+ u16 max_cdblength;
+ u8 asic_revision;
+ u8 generational_guid[16];
+ u8 hba_port_count;
+ u16 default_link_down_timeout;
+ u8 iscsi_ver_min_max;
+ u8 multifunction_device;
+ u8 cache_valid;
+ u8 hba_status;
+ u8 max_domains_supported;
+ u8 phy_port;
+ u32 firmware_post_status;
+ u32 hba_mtu[8];
+ u32 rsvd1[4];
+};
+
+struct mgmt_controller_attrib {
+ struct mgmt_hba_attribs hba_attribs;
+ u16 pci_vendor_id;
+ u16 pci_device_id;
+ u16 pci_sub_vendor_id;
+ u16 pci_sub_system_id;
+ u8 pci_bus_number;
+ u8 pci_device_number;
+ u8 pci_function_number;
+ u8 interface_type;
+ u64 unique_identifier;
+ u32 rsvd0[5];
+};
+
+struct ocrdma_get_ctrl_attribs_rsp {
+ struct ocrdma_mbx_hdr hdr;
+ struct mgmt_controller_attrib ctrl_attribs;
+};
+
+
#endif /* __OCRDMA_SLI_H__ */
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c
new file mode 100644
index 000000000000..6c54106f5e64
--- /dev/null
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c
@@ -0,0 +1,623 @@
+/*******************************************************************
+ * This file is part of the Emulex RoCE Device Driver for *
+ * RoCE (RDMA over Converged Ethernet) adapters. *
+ * Copyright (C) 2008-2014 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of version 2 of the GNU General *
+ * Public License as published by the Free Software Foundation. *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ *
+ * Contact Information:
+ * linux-drivers@emulex.com
+ *
+ * Emulex
+ * 3333 Susan Street
+ * Costa Mesa, CA 92626
+ *******************************************************************/
+
+#include <rdma/ib_addr.h>
+#include "ocrdma_stats.h"
+
+static struct dentry *ocrdma_dbgfs_dir;
+
+static int ocrdma_add_stat(char *start, char *pcur,
+ char *name, u64 count)
+{
+ char buff[128] = {0};
+ int cpy_len = 0;
+
+ snprintf(buff, 128, "%s: %llu\n", name, count);
+ cpy_len = strlen(buff);
+
+ if (pcur + cpy_len > start + OCRDMA_MAX_DBGFS_MEM) {
+ pr_err("%s: No space in stats buff\n", __func__);
+ return 0;
+ }
+
+ memcpy(pcur, buff, cpy_len);
+ return cpy_len;
+}
+
+static bool ocrdma_alloc_stats_mem(struct ocrdma_dev *dev)
+{
+ struct stats_mem *mem = &dev->stats_mem;
+
+ /* Alloc mbox command mem*/
+ mem->size = max_t(u32, sizeof(struct ocrdma_rdma_stats_req),
+ sizeof(struct ocrdma_rdma_stats_resp));
+
+ mem->va = dma_alloc_coherent(&dev->nic_info.pdev->dev, mem->size,
+ &mem->pa, GFP_KERNEL);
+ if (!mem->va) {
+ pr_err("%s: stats mbox allocation failed\n", __func__);
+ return false;
+ }
+
+ memset(mem->va, 0, mem->size);
+
+ /* Alloc debugfs mem */
+ mem->debugfs_mem = kzalloc(OCRDMA_MAX_DBGFS_MEM, GFP_KERNEL);
+ if (!mem->debugfs_mem) {
+ pr_err("%s: stats debugfs mem allocation failed\n", __func__);
+ return false;
+ }
+
+ return true;
+}
+
+static void ocrdma_release_stats_mem(struct ocrdma_dev *dev)
+{
+ struct stats_mem *mem = &dev->stats_mem;
+
+ if (mem->va)
+ dma_free_coherent(&dev->nic_info.pdev->dev, mem->size,
+ mem->va, mem->pa);
+ kfree(mem->debugfs_mem);
+}
+
+static char *ocrdma_resource_stats(struct ocrdma_dev *dev)
+{
+ char *stats = dev->stats_mem.debugfs_mem, *pcur;
+ struct ocrdma_rdma_stats_resp *rdma_stats =
+ (struct ocrdma_rdma_stats_resp *)dev->stats_mem.va;
+ struct ocrdma_rsrc_stats *rsrc_stats = &rdma_stats->act_rsrc_stats;
+
+ memset(stats, 0, (OCRDMA_MAX_DBGFS_MEM));
+
+ pcur = stats;
+ pcur += ocrdma_add_stat(stats, pcur, "active_dpp_pds",
+ (u64)rsrc_stats->dpp_pds);
+ pcur += ocrdma_add_stat(stats, pcur, "active_non_dpp_pds",
+ (u64)rsrc_stats->non_dpp_pds);
+ pcur += ocrdma_add_stat(stats, pcur, "active_rc_dpp_qps",
+ (u64)rsrc_stats->rc_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "active_uc_dpp_qps",
+ (u64)rsrc_stats->uc_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "active_ud_dpp_qps",
+ (u64)rsrc_stats->ud_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "active_rc_non_dpp_qps",
+ (u64)rsrc_stats->rc_non_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "active_uc_non_dpp_qps",
+ (u64)rsrc_stats->uc_non_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "active_ud_non_dpp_qps",
+ (u64)rsrc_stats->ud_non_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "active_srqs",
+ (u64)rsrc_stats->srqs);
+ pcur += ocrdma_add_stat(stats, pcur, "active_rbqs",
+ (u64)rsrc_stats->rbqs);
+ pcur += ocrdma_add_stat(stats, pcur, "active_64K_nsmr",
+ (u64)rsrc_stats->r64K_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "active_64K_to_2M_nsmr",
+ (u64)rsrc_stats->r64K_to_2M_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "active_2M_to_44M_nsmr",
+ (u64)rsrc_stats->r2M_to_44M_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "active_44M_to_1G_nsmr",
+ (u64)rsrc_stats->r44M_to_1G_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "active_1G_to_4G_nsmr",
+ (u64)rsrc_stats->r1G_to_4G_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "active_nsmr_count_4G_to_32G",
+ (u64)rsrc_stats->nsmr_count_4G_to_32G);
+ pcur += ocrdma_add_stat(stats, pcur, "active_32G_to_64G_nsmr",
+ (u64)rsrc_stats->r32G_to_64G_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "active_64G_to_128G_nsmr",
+ (u64)rsrc_stats->r64G_to_128G_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "active_128G_to_higher_nsmr",
+ (u64)rsrc_stats->r128G_to_higher_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "active_embedded_nsmr",
+ (u64)rsrc_stats->embedded_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "active_frmr",
+ (u64)rsrc_stats->frmr);
+ pcur += ocrdma_add_stat(stats, pcur, "active_prefetch_qps",
+ (u64)rsrc_stats->prefetch_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "active_ondemand_qps",
+ (u64)rsrc_stats->ondemand_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "active_phy_mr",
+ (u64)rsrc_stats->phy_mr);
+ pcur += ocrdma_add_stat(stats, pcur, "active_mw",
+ (u64)rsrc_stats->mw);
+
+ /* Print the threshold stats */
+ rsrc_stats = &rdma_stats->th_rsrc_stats;
+
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_dpp_pds",
+ (u64)rsrc_stats->dpp_pds);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_non_dpp_pds",
+ (u64)rsrc_stats->non_dpp_pds);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_rc_dpp_qps",
+ (u64)rsrc_stats->rc_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_uc_dpp_qps",
+ (u64)rsrc_stats->uc_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_ud_dpp_qps",
+ (u64)rsrc_stats->ud_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_rc_non_dpp_qps",
+ (u64)rsrc_stats->rc_non_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_uc_non_dpp_qps",
+ (u64)rsrc_stats->uc_non_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_ud_non_dpp_qps",
+ (u64)rsrc_stats->ud_non_dpp_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_srqs",
+ (u64)rsrc_stats->srqs);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_rbqs",
+ (u64)rsrc_stats->rbqs);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_64K_nsmr",
+ (u64)rsrc_stats->r64K_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_64K_to_2M_nsmr",
+ (u64)rsrc_stats->r64K_to_2M_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_2M_to_44M_nsmr",
+ (u64)rsrc_stats->r2M_to_44M_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_44M_to_1G_nsmr",
+ (u64)rsrc_stats->r44M_to_1G_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_1G_to_4G_nsmr",
+ (u64)rsrc_stats->r1G_to_4G_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_nsmr_count_4G_to_32G",
+ (u64)rsrc_stats->nsmr_count_4G_to_32G);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_32G_to_64G_nsmr",
+ (u64)rsrc_stats->r32G_to_64G_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_64G_to_128G_nsmr",
+ (u64)rsrc_stats->r64G_to_128G_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_128G_to_higher_nsmr",
+ (u64)rsrc_stats->r128G_to_higher_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_embedded_nsmr",
+ (u64)rsrc_stats->embedded_nsmr);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_frmr",
+ (u64)rsrc_stats->frmr);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_prefetch_qps",
+ (u64)rsrc_stats->prefetch_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_ondemand_qps",
+ (u64)rsrc_stats->ondemand_qps);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_phy_mr",
+ (u64)rsrc_stats->phy_mr);
+ pcur += ocrdma_add_stat(stats, pcur, "threshold_mw",
+ (u64)rsrc_stats->mw);
+ return stats;
+}
+
+static char *ocrdma_rx_stats(struct ocrdma_dev *dev)
+{
+ char *stats = dev->stats_mem.debugfs_mem, *pcur;
+ struct ocrdma_rdma_stats_resp *rdma_stats =
+ (struct ocrdma_rdma_stats_resp *)dev->stats_mem.va;
+ struct ocrdma_rx_stats *rx_stats = &rdma_stats->rx_stats;
+
+ memset(stats, 0, (OCRDMA_MAX_DBGFS_MEM));
+
+ pcur = stats;
+ pcur += ocrdma_add_stat
+ (stats, pcur, "roce_frame_bytes",
+ convert_to_64bit(rx_stats->roce_frame_bytes_lo,
+ rx_stats->roce_frame_bytes_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "roce_frame_icrc_drops",
+ (u64)rx_stats->roce_frame_icrc_drops);
+ pcur += ocrdma_add_stat(stats, pcur, "roce_frame_payload_len_drops",
+ (u64)rx_stats->roce_frame_payload_len_drops);
+ pcur += ocrdma_add_stat(stats, pcur, "ud_drops",
+ (u64)rx_stats->ud_drops);
+ pcur += ocrdma_add_stat(stats, pcur, "qp1_drops",
+ (u64)rx_stats->qp1_drops);
+ pcur += ocrdma_add_stat(stats, pcur, "psn_error_request_packets",
+ (u64)rx_stats->psn_error_request_packets);
+ pcur += ocrdma_add_stat(stats, pcur, "psn_error_resp_packets",
+ (u64)rx_stats->psn_error_resp_packets);
+ pcur += ocrdma_add_stat(stats, pcur, "rnr_nak_timeouts",
+ (u64)rx_stats->rnr_nak_timeouts);
+ pcur += ocrdma_add_stat(stats, pcur, "rnr_nak_receives",
+ (u64)rx_stats->rnr_nak_receives);
+ pcur += ocrdma_add_stat(stats, pcur, "roce_frame_rxmt_drops",
+ (u64)rx_stats->roce_frame_rxmt_drops);
+ pcur += ocrdma_add_stat(stats, pcur, "nak_count_psn_sequence_errors",
+ (u64)rx_stats->nak_count_psn_sequence_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "rc_drop_count_lookup_errors",
+ (u64)rx_stats->rc_drop_count_lookup_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "rq_rnr_naks",
+ (u64)rx_stats->rq_rnr_naks);
+ pcur += ocrdma_add_stat(stats, pcur, "srq_rnr_naks",
+ (u64)rx_stats->srq_rnr_naks);
+ pcur += ocrdma_add_stat(stats, pcur, "roce_frames",
+ convert_to_64bit(rx_stats->roce_frames_lo,
+ rx_stats->roce_frames_hi));
+
+ return stats;
+}
+
+static char *ocrdma_tx_stats(struct ocrdma_dev *dev)
+{
+ char *stats = dev->stats_mem.debugfs_mem, *pcur;
+ struct ocrdma_rdma_stats_resp *rdma_stats =
+ (struct ocrdma_rdma_stats_resp *)dev->stats_mem.va;
+ struct ocrdma_tx_stats *tx_stats = &rdma_stats->tx_stats;
+
+ memset(stats, 0, (OCRDMA_MAX_DBGFS_MEM));
+
+ pcur = stats;
+ pcur += ocrdma_add_stat(stats, pcur, "send_pkts",
+ convert_to_64bit(tx_stats->send_pkts_lo,
+ tx_stats->send_pkts_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "write_pkts",
+ convert_to_64bit(tx_stats->write_pkts_lo,
+ tx_stats->write_pkts_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "read_pkts",
+ convert_to_64bit(tx_stats->read_pkts_lo,
+ tx_stats->read_pkts_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "read_rsp_pkts",
+ convert_to_64bit(tx_stats->read_rsp_pkts_lo,
+ tx_stats->read_rsp_pkts_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "ack_pkts",
+ convert_to_64bit(tx_stats->ack_pkts_lo,
+ tx_stats->ack_pkts_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "send_bytes",
+ convert_to_64bit(tx_stats->send_bytes_lo,
+ tx_stats->send_bytes_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "write_bytes",
+ convert_to_64bit(tx_stats->write_bytes_lo,
+ tx_stats->write_bytes_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "read_req_bytes",
+ convert_to_64bit(tx_stats->read_req_bytes_lo,
+ tx_stats->read_req_bytes_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "read_rsp_bytes",
+ convert_to_64bit(tx_stats->read_rsp_bytes_lo,
+ tx_stats->read_rsp_bytes_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "ack_timeouts",
+ (u64)tx_stats->ack_timeouts);
+
+ return stats;
+}
+
+static char *ocrdma_wqe_stats(struct ocrdma_dev *dev)
+{
+ char *stats = dev->stats_mem.debugfs_mem, *pcur;
+ struct ocrdma_rdma_stats_resp *rdma_stats =
+ (struct ocrdma_rdma_stats_resp *)dev->stats_mem.va;
+ struct ocrdma_wqe_stats *wqe_stats = &rdma_stats->wqe_stats;
+
+ memset(stats, 0, (OCRDMA_MAX_DBGFS_MEM));
+
+ pcur = stats;
+ pcur += ocrdma_add_stat(stats, pcur, "large_send_rc_wqes",
+ convert_to_64bit(wqe_stats->large_send_rc_wqes_lo,
+ wqe_stats->large_send_rc_wqes_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "large_write_rc_wqes",
+ convert_to_64bit(wqe_stats->large_write_rc_wqes_lo,
+ wqe_stats->large_write_rc_wqes_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "read_wqes",
+ convert_to_64bit(wqe_stats->read_wqes_lo,
+ wqe_stats->read_wqes_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "frmr_wqes",
+ convert_to_64bit(wqe_stats->frmr_wqes_lo,
+ wqe_stats->frmr_wqes_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "mw_bind_wqes",
+ convert_to_64bit(wqe_stats->mw_bind_wqes_lo,
+ wqe_stats->mw_bind_wqes_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "invalidate_wqes",
+ convert_to_64bit(wqe_stats->invalidate_wqes_lo,
+ wqe_stats->invalidate_wqes_hi));
+ pcur += ocrdma_add_stat(stats, pcur, "dpp_wqe_drops",
+ (u64)wqe_stats->dpp_wqe_drops);
+ return stats;
+}
+
+static char *ocrdma_db_errstats(struct ocrdma_dev *dev)
+{
+ char *stats = dev->stats_mem.debugfs_mem, *pcur;
+ struct ocrdma_rdma_stats_resp *rdma_stats =
+ (struct ocrdma_rdma_stats_resp *)dev->stats_mem.va;
+ struct ocrdma_db_err_stats *db_err_stats = &rdma_stats->db_err_stats;
+
+ memset(stats, 0, (OCRDMA_MAX_DBGFS_MEM));
+
+ pcur = stats;
+ pcur += ocrdma_add_stat(stats, pcur, "sq_doorbell_errors",
+ (u64)db_err_stats->sq_doorbell_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "cq_doorbell_errors",
+ (u64)db_err_stats->cq_doorbell_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "rq_srq_doorbell_errors",
+ (u64)db_err_stats->rq_srq_doorbell_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "cq_overflow_errors",
+ (u64)db_err_stats->cq_overflow_errors);
+ return stats;
+}
+
+static char *ocrdma_rxqp_errstats(struct ocrdma_dev *dev)
+{
+ char *stats = dev->stats_mem.debugfs_mem, *pcur;
+ struct ocrdma_rdma_stats_resp *rdma_stats =
+ (struct ocrdma_rdma_stats_resp *)dev->stats_mem.va;
+ struct ocrdma_rx_qp_err_stats *rx_qp_err_stats =
+ &rdma_stats->rx_qp_err_stats;
+
+ memset(stats, 0, (OCRDMA_MAX_DBGFS_MEM));
+
+ pcur = stats;
+ pcur += ocrdma_add_stat(stats, pcur, "nak_invalid_requst_errors",
+ (u64)rx_qp_err_stats->nak_invalid_requst_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "nak_remote_operation_errors",
+ (u64)rx_qp_err_stats->nak_remote_operation_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "nak_count_remote_access_errors",
+ (u64)rx_qp_err_stats->nak_count_remote_access_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "local_length_errors",
+ (u64)rx_qp_err_stats->local_length_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "local_protection_errors",
+ (u64)rx_qp_err_stats->local_protection_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "local_qp_operation_errors",
+ (u64)rx_qp_err_stats->local_qp_operation_errors);
+ return stats;
+}
+
+static char *ocrdma_txqp_errstats(struct ocrdma_dev *dev)
+{
+ char *stats = dev->stats_mem.debugfs_mem, *pcur;
+ struct ocrdma_rdma_stats_resp *rdma_stats =
+ (struct ocrdma_rdma_stats_resp *)dev->stats_mem.va;
+ struct ocrdma_tx_qp_err_stats *tx_qp_err_stats =
+ &rdma_stats->tx_qp_err_stats;
+
+ memset(stats, 0, (OCRDMA_MAX_DBGFS_MEM));
+
+ pcur = stats;
+ pcur += ocrdma_add_stat(stats, pcur, "local_length_errors",
+ (u64)tx_qp_err_stats->local_length_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "local_protection_errors",
+ (u64)tx_qp_err_stats->local_protection_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "local_qp_operation_errors",
+ (u64)tx_qp_err_stats->local_qp_operation_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "retry_count_exceeded_errors",
+ (u64)tx_qp_err_stats->retry_count_exceeded_errors);
+ pcur += ocrdma_add_stat(stats, pcur, "rnr_retry_count_exceeded_errors",
+ (u64)tx_qp_err_stats->rnr_retry_count_exceeded_errors);
+ return stats;
+}
+
+static char *ocrdma_tx_dbg_stats(struct ocrdma_dev *dev)
+{
+ int i;
+ char *pstats = dev->stats_mem.debugfs_mem;
+ struct ocrdma_rdma_stats_resp *rdma_stats =
+ (struct ocrdma_rdma_stats_resp *)dev->stats_mem.va;
+ struct ocrdma_tx_dbg_stats *tx_dbg_stats =
+ &rdma_stats->tx_dbg_stats;
+
+ memset(pstats, 0, (OCRDMA_MAX_DBGFS_MEM));
+
+ for (i = 0; i < 100; i++)
+ pstats += snprintf(pstats, 80, "DW[%d] = 0x%x\n", i,
+ tx_dbg_stats->data[i]);
+
+ return dev->stats_mem.debugfs_mem;
+}
+
+static char *ocrdma_rx_dbg_stats(struct ocrdma_dev *dev)
+{
+ int i;
+ char *pstats = dev->stats_mem.debugfs_mem;
+ struct ocrdma_rdma_stats_resp *rdma_stats =
+ (struct ocrdma_rdma_stats_resp *)dev->stats_mem.va;
+ struct ocrdma_rx_dbg_stats *rx_dbg_stats =
+ &rdma_stats->rx_dbg_stats;
+
+ memset(pstats, 0, (OCRDMA_MAX_DBGFS_MEM));
+
+ for (i = 0; i < 200; i++)
+ pstats += snprintf(pstats, 80, "DW[%d] = 0x%x\n", i,
+ rx_dbg_stats->data[i]);
+
+ return dev->stats_mem.debugfs_mem;
+}
+
+static void ocrdma_update_stats(struct ocrdma_dev *dev)
+{
+ ulong now = jiffies, secs;
+ int status = 0;
+
+ secs = jiffies_to_msecs(now - dev->last_stats_time) / 1000U;
+ if (secs) {
+ /* update */
+ status = ocrdma_mbx_rdma_stats(dev, false);
+ if (status)
+ pr_err("%s: stats mbox failed with status = %d\n",
+ __func__, status);
+ dev->last_stats_time = jiffies;
+ }
+}
+
+static ssize_t ocrdma_dbgfs_ops_read(struct file *filp, char __user *buffer,
+ size_t usr_buf_len, loff_t *ppos)
+{
+ struct ocrdma_stats *pstats = filp->private_data;
+ struct ocrdma_dev *dev = pstats->dev;
+ ssize_t status = 0;
+ char *data = NULL;
+
+ /* No partial reads */
+ if (*ppos != 0)
+ return 0;
+
+ mutex_lock(&dev->stats_lock);
+
+ ocrdma_update_stats(dev);
+
+ switch (pstats->type) {
+ case OCRDMA_RSRC_STATS:
+ data = ocrdma_resource_stats(dev);
+ break;
+ case OCRDMA_RXSTATS:
+ data = ocrdma_rx_stats(dev);
+ break;
+ case OCRDMA_WQESTATS:
+ data = ocrdma_wqe_stats(dev);
+ break;
+ case OCRDMA_TXSTATS:
+ data = ocrdma_tx_stats(dev);
+ break;
+ case OCRDMA_DB_ERRSTATS:
+ data = ocrdma_db_errstats(dev);
+ break;
+ case OCRDMA_RXQP_ERRSTATS:
+ data = ocrdma_rxqp_errstats(dev);
+ break;
+ case OCRDMA_TXQP_ERRSTATS:
+ data = ocrdma_txqp_errstats(dev);
+ break;
+ case OCRDMA_TX_DBG_STATS:
+ data = ocrdma_tx_dbg_stats(dev);
+ break;
+ case OCRDMA_RX_DBG_STATS:
+ data = ocrdma_rx_dbg_stats(dev);
+ break;
+
+ default:
+ status = -EFAULT;
+ goto exit;
+ }
+
+ if (usr_buf_len < strlen(data)) {
+ status = -ENOSPC;
+ goto exit;
+ }
+
+ status = simple_read_from_buffer(buffer, usr_buf_len, ppos, data,
+ strlen(data));
+exit:
+ mutex_unlock(&dev->stats_lock);
+ return status;
+}
+
+static int ocrdma_debugfs_open(struct inode *inode, struct file *file)
+{
+ if (inode->i_private)
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static const struct file_operations ocrdma_dbg_ops = {
+ .owner = THIS_MODULE,
+ .open = ocrdma_debugfs_open,
+ .read = ocrdma_dbgfs_ops_read,
+};
+
+void ocrdma_add_port_stats(struct ocrdma_dev *dev)
+{
+ if (!ocrdma_dbgfs_dir)
+ return;
+
+ /* Create post stats base dir */
+ dev->dir = debugfs_create_dir(dev->ibdev.name, ocrdma_dbgfs_dir);
+ if (!dev->dir)
+ goto err;
+
+ dev->rsrc_stats.type = OCRDMA_RSRC_STATS;
+ dev->rsrc_stats.dev = dev;
+ if (!debugfs_create_file("resource_stats", S_IRUSR, dev->dir,
+ &dev->rsrc_stats, &ocrdma_dbg_ops))
+ goto err;
+
+ dev->rx_stats.type = OCRDMA_RXSTATS;
+ dev->rx_stats.dev = dev;
+ if (!debugfs_create_file("rx_stats", S_IRUSR, dev->dir,
+ &dev->rx_stats, &ocrdma_dbg_ops))
+ goto err;
+
+ dev->wqe_stats.type = OCRDMA_WQESTATS;
+ dev->wqe_stats.dev = dev;
+ if (!debugfs_create_file("wqe_stats", S_IRUSR, dev->dir,
+ &dev->wqe_stats, &ocrdma_dbg_ops))
+ goto err;
+
+ dev->tx_stats.type = OCRDMA_TXSTATS;
+ dev->tx_stats.dev = dev;
+ if (!debugfs_create_file("tx_stats", S_IRUSR, dev->dir,
+ &dev->tx_stats, &ocrdma_dbg_ops))
+ goto err;
+
+ dev->db_err_stats.type = OCRDMA_DB_ERRSTATS;
+ dev->db_err_stats.dev = dev;
+ if (!debugfs_create_file("db_err_stats", S_IRUSR, dev->dir,
+ &dev->db_err_stats, &ocrdma_dbg_ops))
+ goto err;
+
+
+ dev->tx_qp_err_stats.type = OCRDMA_TXQP_ERRSTATS;
+ dev->tx_qp_err_stats.dev = dev;
+ if (!debugfs_create_file("tx_qp_err_stats", S_IRUSR, dev->dir,
+ &dev->tx_qp_err_stats, &ocrdma_dbg_ops))
+ goto err;
+
+ dev->rx_qp_err_stats.type = OCRDMA_RXQP_ERRSTATS;
+ dev->rx_qp_err_stats.dev = dev;
+ if (!debugfs_create_file("rx_qp_err_stats", S_IRUSR, dev->dir,
+ &dev->rx_qp_err_stats, &ocrdma_dbg_ops))
+ goto err;
+
+
+ dev->tx_dbg_stats.type = OCRDMA_TX_DBG_STATS;
+ dev->tx_dbg_stats.dev = dev;
+ if (!debugfs_create_file("tx_dbg_stats", S_IRUSR, dev->dir,
+ &dev->tx_dbg_stats, &ocrdma_dbg_ops))
+ goto err;
+
+ dev->rx_dbg_stats.type = OCRDMA_RX_DBG_STATS;
+ dev->rx_dbg_stats.dev = dev;
+ if (!debugfs_create_file("rx_dbg_stats", S_IRUSR, dev->dir,
+ &dev->rx_dbg_stats, &ocrdma_dbg_ops))
+ goto err;
+
+ /* Now create dma_mem for stats mbx command */
+ if (!ocrdma_alloc_stats_mem(dev))
+ goto err;
+
+ mutex_init(&dev->stats_lock);
+
+ return;
+err:
+ ocrdma_release_stats_mem(dev);
+ debugfs_remove_recursive(dev->dir);
+ dev->dir = NULL;
+}
+
+void ocrdma_rem_port_stats(struct ocrdma_dev *dev)
+{
+ if (!dev->dir)
+ return;
+ mutex_destroy(&dev->stats_lock);
+ ocrdma_release_stats_mem(dev);
+ debugfs_remove(dev->dir);
+}
+
+void ocrdma_init_debugfs(void)
+{
+ /* Create base dir in debugfs root dir */
+ ocrdma_dbgfs_dir = debugfs_create_dir("ocrdma", NULL);
+}
+
+void ocrdma_rem_debugfs(void)
+{
+ debugfs_remove_recursive(ocrdma_dbgfs_dir);
+}
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_stats.h b/drivers/infiniband/hw/ocrdma/ocrdma_stats.h
new file mode 100644
index 000000000000..5f5e20c46d7c
--- /dev/null
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_stats.h
@@ -0,0 +1,54 @@
+/*******************************************************************
+ * This file is part of the Emulex RoCE Device Driver for *
+ * RoCE (RDMA over Converged Ethernet) adapters. *
+ * Copyright (C) 2008-2014 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of version 2 of the GNU General *
+ * Public License as published by the Free Software Foundation. *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ *
+ * Contact Information:
+ * linux-drivers@emulex.com
+ *
+ * Emulex
+ * 3333 Susan Street
+ * Costa Mesa, CA 92626
+ *******************************************************************/
+
+#ifndef __OCRDMA_STATS_H__
+#define __OCRDMA_STATS_H__
+
+#include <linux/debugfs.h>
+#include "ocrdma.h"
+#include "ocrdma_hw.h"
+
+#define OCRDMA_MAX_DBGFS_MEM 4096
+
+enum OCRDMA_STATS_TYPE {
+ OCRDMA_RSRC_STATS,
+ OCRDMA_RXSTATS,
+ OCRDMA_WQESTATS,
+ OCRDMA_TXSTATS,
+ OCRDMA_DB_ERRSTATS,
+ OCRDMA_RXQP_ERRSTATS,
+ OCRDMA_TXQP_ERRSTATS,
+ OCRDMA_TX_DBG_STATS,
+ OCRDMA_RX_DBG_STATS
+};
+
+void ocrdma_rem_debugfs(void);
+void ocrdma_init_debugfs(void);
+void ocrdma_rem_port_stats(struct ocrdma_dev *dev);
+void ocrdma_add_port_stats(struct ocrdma_dev *dev);
+
+#endif /* __OCRDMA_STATS_H__ */
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
index e0cc201be41a..edf6211d84b8 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
@@ -53,7 +53,7 @@ int ocrdma_query_gid(struct ib_device *ibdev, u8 port,
dev = get_ocrdma_dev(ibdev);
memset(sgid, 0, sizeof(*sgid));
- if (index >= OCRDMA_MAX_SGID)
+ if (index > OCRDMA_MAX_SGID)
return -EINVAL;
memcpy(sgid, &dev->sgid_tbl[index], sizeof(*sgid));
@@ -89,7 +89,7 @@ int ocrdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr)
attr->max_cq = dev->attr.max_cq;
attr->max_cqe = dev->attr.max_cqe;
attr->max_mr = dev->attr.max_mr;
- attr->max_mw = 0;
+ attr->max_mw = dev->attr.max_mw;
attr->max_pd = dev->attr.max_pd;
attr->atomic_cap = 0;
attr->max_fmr = 0;
@@ -144,7 +144,6 @@ static inline void get_link_speed_and_width(struct ocrdma_dev *dev,
}
}
-
int ocrdma_query_port(struct ib_device *ibdev,
u8 port, struct ib_port_attr *props)
{
@@ -267,7 +266,7 @@ static struct ocrdma_pd *_ocrdma_alloc_pd(struct ocrdma_dev *dev,
if (udata && uctx) {
pd->dpp_enabled =
- dev->nic_info.dev_family == OCRDMA_GEN2_FAMILY;
+ ocrdma_get_asic_type(dev) == OCRDMA_ASIC_GEN_SKH_R;
pd->num_dpp_qp =
pd->dpp_enabled ? OCRDMA_PD_MAX_DPP_ENABLED_QP : 0;
}
@@ -726,10 +725,10 @@ static void build_user_pbes(struct ocrdma_dev *dev, struct ocrdma_mr *mr,
u32 num_pbes)
{
struct ocrdma_pbe *pbe;
- struct ib_umem_chunk *chunk;
+ struct scatterlist *sg;
struct ocrdma_pbl *pbl_tbl = mr->hwmr.pbl_table;
struct ib_umem *umem = mr->umem;
- int i, shift, pg_cnt, pages, pbe_cnt, total_num_pbes = 0;
+ int shift, pg_cnt, pages, pbe_cnt, entry, total_num_pbes = 0;
if (!mr->hwmr.num_pbes)
return;
@@ -739,39 +738,37 @@ static void build_user_pbes(struct ocrdma_dev *dev, struct ocrdma_mr *mr,
shift = ilog2(umem->page_size);
- list_for_each_entry(chunk, &umem->chunk_list, list) {
- /* get all the dma regions from the chunk. */
- for (i = 0; i < chunk->nmap; i++) {
- pages = sg_dma_len(&chunk->page_list[i]) >> shift;
- for (pg_cnt = 0; pg_cnt < pages; pg_cnt++) {
- /* store the page address in pbe */
- pbe->pa_lo =
- cpu_to_le32(sg_dma_address
- (&chunk->page_list[i]) +
- (umem->page_size * pg_cnt));
- pbe->pa_hi =
- cpu_to_le32(upper_32_bits
- ((sg_dma_address
- (&chunk->page_list[i]) +
- umem->page_size * pg_cnt)));
- pbe_cnt += 1;
- total_num_pbes += 1;
- pbe++;
-
- /* if done building pbes, issue the mbx cmd. */
- if (total_num_pbes == num_pbes)
- return;
-
- /* if the given pbl is full storing the pbes,
- * move to next pbl.
- */
- if (pbe_cnt ==
- (mr->hwmr.pbl_size / sizeof(u64))) {
- pbl_tbl++;
- pbe = (struct ocrdma_pbe *)pbl_tbl->va;
- pbe_cnt = 0;
- }
+ for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
+ pages = sg_dma_len(sg) >> shift;
+ for (pg_cnt = 0; pg_cnt < pages; pg_cnt++) {
+ /* store the page address in pbe */
+ pbe->pa_lo =
+ cpu_to_le32(sg_dma_address
+ (sg) +
+ (umem->page_size * pg_cnt));
+ pbe->pa_hi =
+ cpu_to_le32(upper_32_bits
+ ((sg_dma_address
+ (sg) +
+ umem->page_size * pg_cnt)));
+ pbe_cnt += 1;
+ total_num_pbes += 1;
+ pbe++;
+
+ /* if done building pbes, issue the mbx cmd. */
+ if (total_num_pbes == num_pbes)
+ return;
+
+ /* if the given pbl is full storing the pbes,
+ * move to next pbl.
+ */
+ if (pbe_cnt ==
+ (mr->hwmr.pbl_size / sizeof(u64))) {
+ pbl_tbl++;
+ pbe = (struct ocrdma_pbe *)pbl_tbl->va;
+ pbe_cnt = 0;
}
+
}
}
}
@@ -840,8 +837,7 @@ int ocrdma_dereg_mr(struct ib_mr *ib_mr)
status = ocrdma_mbx_dealloc_lkey(dev, mr->hwmr.fr_mr, mr->hwmr.lkey);
- if (mr->hwmr.fr_mr == 0)
- ocrdma_free_mr_pbl_tbl(dev, &mr->hwmr);
+ ocrdma_free_mr_pbl_tbl(dev, &mr->hwmr);
/* it could be user registered memory. */
if (mr->umem)
@@ -910,6 +906,7 @@ struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev, int entries, int vector,
spin_lock_init(&cq->comp_handler_lock);
INIT_LIST_HEAD(&cq->sq_head);
INIT_LIST_HEAD(&cq->rq_head);
+ cq->first_arm = true;
if (ib_ctx) {
uctx = get_ocrdma_ucontext(ib_ctx);
@@ -927,9 +924,7 @@ struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev, int entries, int vector,
goto ctx_err;
}
cq->phase = OCRDMA_CQE_VALID;
- cq->arm_needed = true;
dev->cq_tbl[cq->id] = cq;
-
return &cq->ibcq;
ctx_err:
@@ -952,15 +947,52 @@ int ocrdma_resize_cq(struct ib_cq *ibcq, int new_cnt,
return status;
}
+static void ocrdma_flush_cq(struct ocrdma_cq *cq)
+{
+ int cqe_cnt;
+ int valid_count = 0;
+ unsigned long flags;
+
+ struct ocrdma_dev *dev = get_ocrdma_dev(cq->ibcq.device);
+ struct ocrdma_cqe *cqe = NULL;
+
+ cqe = cq->va;
+ cqe_cnt = cq->cqe_cnt;
+
+ /* Last irq might have scheduled a polling thread
+ * sync-up with it before hard flushing.
+ */
+ spin_lock_irqsave(&cq->cq_lock, flags);
+ while (cqe_cnt) {
+ if (is_cqe_valid(cq, cqe))
+ valid_count++;
+ cqe++;
+ cqe_cnt--;
+ }
+ ocrdma_ring_cq_db(dev, cq->id, false, false, valid_count);
+ spin_unlock_irqrestore(&cq->cq_lock, flags);
+}
+
int ocrdma_destroy_cq(struct ib_cq *ibcq)
{
int status;
struct ocrdma_cq *cq = get_ocrdma_cq(ibcq);
+ struct ocrdma_eq *eq = NULL;
struct ocrdma_dev *dev = get_ocrdma_dev(ibcq->device);
int pdid = 0;
+ u32 irq, indx;
- status = ocrdma_mbx_destroy_cq(dev, cq);
+ dev->cq_tbl[cq->id] = NULL;
+ indx = ocrdma_get_eq_table_index(dev, cq->eqn);
+ if (indx == -EINVAL)
+ BUG();
+ eq = &dev->eq_tbl[indx];
+ irq = ocrdma_get_irq(dev, eq);
+ synchronize_irq(irq);
+ ocrdma_flush_cq(cq);
+
+ status = ocrdma_mbx_destroy_cq(dev, cq);
if (cq->ucontext) {
pdid = cq->ucontext->cntxt_pd->id;
ocrdma_del_mmap(cq->ucontext, (u64) cq->pa,
@@ -969,7 +1001,6 @@ int ocrdma_destroy_cq(struct ib_cq *ibcq)
ocrdma_get_db_addr(dev, pdid),
dev->nic_info.db_page_size);
}
- dev->cq_tbl[cq->id] = NULL;
kfree(cq);
return status;
@@ -1092,15 +1123,9 @@ static int ocrdma_copy_qp_uresp(struct ocrdma_qp *qp,
}
uresp.db_page_addr = usr_db;
uresp.db_page_size = dev->nic_info.db_page_size;
- if (dev->nic_info.dev_family == OCRDMA_GEN2_FAMILY) {
- uresp.db_sq_offset = OCRDMA_DB_GEN2_SQ_OFFSET;
- uresp.db_rq_offset = OCRDMA_DB_GEN2_RQ_OFFSET;
- uresp.db_shift = 24;
- } else {
- uresp.db_sq_offset = OCRDMA_DB_SQ_OFFSET;
- uresp.db_rq_offset = OCRDMA_DB_RQ_OFFSET;
- uresp.db_shift = 16;
- }
+ uresp.db_sq_offset = OCRDMA_DB_GEN2_SQ_OFFSET;
+ uresp.db_rq_offset = OCRDMA_DB_GEN2_RQ_OFFSET;
+ uresp.db_shift = OCRDMA_DB_RQ_SHIFT;
if (qp->dpp_enabled) {
uresp.dpp_credit = dpp_credit_lmt;
@@ -1132,7 +1157,7 @@ err:
static void ocrdma_set_qp_db(struct ocrdma_dev *dev, struct ocrdma_qp *qp,
struct ocrdma_pd *pd)
{
- if (dev->nic_info.dev_family == OCRDMA_GEN2_FAMILY) {
+ if (ocrdma_get_asic_type(dev) == OCRDMA_ASIC_GEN_SKH_R) {
qp->sq_db = dev->nic_info.db +
(pd->id * dev->nic_info.db_page_size) +
OCRDMA_DB_GEN2_SQ_OFFSET;
@@ -1182,7 +1207,6 @@ static void ocrdma_set_qp_init_params(struct ocrdma_qp *qp,
qp->signaled = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR) ? true : false;
}
-
static void ocrdma_store_gsi_qp_cq(struct ocrdma_dev *dev,
struct ib_qp_init_attr *attrs)
{
@@ -1268,17 +1292,6 @@ gen_err:
return ERR_PTR(status);
}
-
-static void ocrdma_flush_rq_db(struct ocrdma_qp *qp)
-{
- if (qp->db_cache) {
- u32 val = qp->rq.dbid | (qp->db_cache <<
- ocrdma_get_num_posted_shift(qp));
- iowrite32(val, qp->rq_db);
- qp->db_cache = 0;
- }
-}
-
int _ocrdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
int attr_mask)
{
@@ -1296,9 +1309,7 @@ int _ocrdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
*/
if (status < 0)
return status;
- status = ocrdma_mbx_modify_qp(dev, qp, attr, attr_mask, old_qps);
- if (!status && attr_mask & IB_QP_STATE && attr->qp_state == IB_QPS_RTR)
- ocrdma_flush_rq_db(qp);
+ status = ocrdma_mbx_modify_qp(dev, qp, attr, attr_mask);
return status;
}
@@ -1510,7 +1521,7 @@ static void ocrdma_discard_cqes(struct ocrdma_qp *qp, struct ocrdma_cq *cq)
int discard_cnt = 0;
u32 cur_getp, stop_getp;
struct ocrdma_cqe *cqe;
- u32 qpn = 0;
+ u32 qpn = 0, wqe_idx = 0;
spin_lock_irqsave(&cq->cq_lock, cq_flags);
@@ -1539,24 +1550,29 @@ static void ocrdma_discard_cqes(struct ocrdma_qp *qp, struct ocrdma_cq *cq)
if (qpn == 0 || qpn != qp->id)
goto skip_cqe;
- /* mark cqe discarded so that it is not picked up later
- * in the poll_cq().
- */
- discard_cnt += 1;
- cqe->cmn.qpn = 0;
if (is_cqe_for_sq(cqe)) {
ocrdma_hwq_inc_tail(&qp->sq);
} else {
if (qp->srq) {
+ wqe_idx = (le32_to_cpu(cqe->rq.buftag_qpn) >>
+ OCRDMA_CQE_BUFTAG_SHIFT) &
+ qp->srq->rq.max_wqe_idx;
+ if (wqe_idx < 1)
+ BUG();
spin_lock_irqsave(&qp->srq->q_lock, flags);
ocrdma_hwq_inc_tail(&qp->srq->rq);
- ocrdma_srq_toggle_bit(qp->srq, cur_getp);
+ ocrdma_srq_toggle_bit(qp->srq, wqe_idx - 1);
spin_unlock_irqrestore(&qp->srq->q_lock, flags);
} else {
ocrdma_hwq_inc_tail(&qp->rq);
}
}
+ /* mark cqe discarded so that it is not picked up later
+ * in the poll_cq().
+ */
+ discard_cnt += 1;
+ cqe->cmn.qpn = 0;
skip_cqe:
cur_getp = (cur_getp + 1) % cq->max_hw_cqe;
} while (cur_getp != stop_getp);
@@ -1659,7 +1675,7 @@ static int ocrdma_copy_srq_uresp(struct ocrdma_dev *dev, struct ocrdma_srq *srq,
(srq->pd->id * dev->nic_info.db_page_size);
uresp.db_page_size = dev->nic_info.db_page_size;
uresp.num_rqe_allocated = srq->rq.max_cnt;
- if (dev->nic_info.dev_family == OCRDMA_GEN2_FAMILY) {
+ if (ocrdma_get_asic_type(dev) == OCRDMA_ASIC_GEN_SKH_R) {
uresp.db_rq_offset = OCRDMA_DB_GEN2_RQ_OFFSET;
uresp.db_shift = 24;
} else {
@@ -2009,15 +2025,15 @@ static int ocrdma_build_fr(struct ocrdma_qp *qp, struct ocrdma_hdr_wqe *hdr,
fast_reg->num_sges = wr->wr.fast_reg.page_list_len;
fast_reg->size_sge =
get_encoded_page_size(1 << wr->wr.fast_reg.page_shift);
- mr = (struct ocrdma_mr *) (unsigned long) qp->dev->stag_arr[(hdr->lkey >> 8) &
- (OCRDMA_MAX_STAG - 1)];
+ mr = (struct ocrdma_mr *) (unsigned long)
+ qp->dev->stag_arr[(hdr->lkey >> 8) & (OCRDMA_MAX_STAG - 1)];
build_frmr_pbes(wr, mr->hwmr.pbl_table, &mr->hwmr);
return 0;
}
static void ocrdma_ring_sq_db(struct ocrdma_qp *qp)
{
- u32 val = qp->sq.dbid | (1 << 16);
+ u32 val = qp->sq.dbid | (1 << OCRDMA_DB_SQ_SHIFT);
iowrite32(val, qp->sq_db);
}
@@ -2122,12 +2138,9 @@ int ocrdma_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
static void ocrdma_ring_rq_db(struct ocrdma_qp *qp)
{
- u32 val = qp->rq.dbid | (1 << ocrdma_get_num_posted_shift(qp));
+ u32 val = qp->rq.dbid | (1 << OCRDMA_DB_RQ_SHIFT);
- if (qp->state != OCRDMA_QPS_INIT)
- iowrite32(val, qp->rq_db);
- else
- qp->db_cache++;
+ iowrite32(val, qp->rq_db);
}
static void ocrdma_build_rqe(struct ocrdma_hdr_wqe *rqe, struct ib_recv_wr *wr,
@@ -2213,7 +2226,7 @@ static int ocrdma_srq_get_idx(struct ocrdma_srq *srq)
if (row == srq->bit_fields_len)
BUG();
- return indx;
+ return indx + 1; /* Use from index 1 */
}
static void ocrdma_ring_srq_db(struct ocrdma_srq *srq)
@@ -2550,10 +2563,13 @@ static void ocrdma_update_free_srq_cqe(struct ib_wc *ibwc,
srq = get_ocrdma_srq(qp->ibqp.srq);
wqe_idx = (le32_to_cpu(cqe->rq.buftag_qpn) >>
- OCRDMA_CQE_BUFTAG_SHIFT) & srq->rq.max_wqe_idx;
+ OCRDMA_CQE_BUFTAG_SHIFT) & srq->rq.max_wqe_idx;
+ if (wqe_idx < 1)
+ BUG();
+
ibwc->wr_id = srq->rqe_wr_id_tbl[wqe_idx];
spin_lock_irqsave(&srq->q_lock, flags);
- ocrdma_srq_toggle_bit(srq, wqe_idx);
+ ocrdma_srq_toggle_bit(srq, wqe_idx - 1);
spin_unlock_irqrestore(&srq->q_lock, flags);
ocrdma_hwq_inc_tail(&srq->rq);
}
@@ -2705,10 +2721,18 @@ expand_cqe:
}
stop_cqe:
cq->getp = cur_getp;
- if (polled_hw_cqes || expand || stop) {
- ocrdma_ring_cq_db(dev, cq->id, cq->armed, cq->solicited,
+ if (cq->deferred_arm) {
+ ocrdma_ring_cq_db(dev, cq->id, true, cq->deferred_sol,
+ polled_hw_cqes);
+ cq->deferred_arm = false;
+ cq->deferred_sol = false;
+ } else {
+ /* We need to pop the CQE. No need to arm */
+ ocrdma_ring_cq_db(dev, cq->id, false, cq->deferred_sol,
polled_hw_cqes);
+ cq->deferred_sol = false;
}
+
return i;
}
@@ -2780,30 +2804,28 @@ int ocrdma_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags cq_flags)
struct ocrdma_cq *cq = get_ocrdma_cq(ibcq);
struct ocrdma_dev *dev = get_ocrdma_dev(ibcq->device);
u16 cq_id;
- u16 cur_getp;
- struct ocrdma_cqe *cqe;
unsigned long flags;
+ bool arm_needed = false, sol_needed = false;
cq_id = cq->id;
spin_lock_irqsave(&cq->cq_lock, flags);
if (cq_flags & IB_CQ_NEXT_COMP || cq_flags & IB_CQ_SOLICITED)
- cq->armed = true;
+ arm_needed = true;
if (cq_flags & IB_CQ_SOLICITED)
- cq->solicited = true;
-
- cur_getp = cq->getp;
- cqe = cq->va + cur_getp;
+ sol_needed = true;
- /* check whether any valid cqe exist or not, if not then safe to
- * arm. If cqe is not yet consumed, then let it get consumed and then
- * we arm it to avoid false interrupts.
- */
- if (!is_cqe_valid(cq, cqe) || cq->arm_needed) {
- cq->arm_needed = false;
- ocrdma_ring_cq_db(dev, cq_id, cq->armed, cq->solicited, 0);
+ if (cq->first_arm) {
+ ocrdma_ring_cq_db(dev, cq_id, arm_needed, sol_needed, 0);
+ cq->first_arm = false;
+ goto skip_defer;
}
+ cq->deferred_arm = true;
+
+skip_defer:
+ cq->deferred_sol = sol_needed;
spin_unlock_irqrestore(&cq->cq_lock, flags);
+
return 0;
}
@@ -2838,7 +2860,8 @@ struct ib_mr *ocrdma_alloc_frmr(struct ib_pd *ibpd, int max_page_list_len)
goto mbx_err;
mr->ibmr.rkey = mr->hwmr.lkey;
mr->ibmr.lkey = mr->hwmr.lkey;
- dev->stag_arr[(mr->hwmr.lkey >> 8) & (OCRDMA_MAX_STAG - 1)] = mr;
+ dev->stag_arr[(mr->hwmr.lkey >> 8) & (OCRDMA_MAX_STAG - 1)] =
+ (unsigned long) mr;
return &mr->ibmr;
mbx_err:
ocrdma_free_mr_pbl_tbl(dev, &mr->hwmr);
diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h
index 1946101419a3..c00ae093b6f8 100644
--- a/drivers/infiniband/hw/qib/qib.h
+++ b/drivers/infiniband/hw/qib/qib.h
@@ -868,8 +868,10 @@ struct qib_devdata {
/* last buffer for user use */
u32 lastctxt_piobuf;
- /* saturating counter of (non-port-specific) device interrupts */
- u32 int_counter;
+ /* reset value */
+ u64 z_int_counter;
+ /* percpu intcounter */
+ u64 __percpu *int_counter;
/* pio bufs allocated per ctxt */
u32 pbufsctxt;
@@ -1184,7 +1186,7 @@ int qib_setup_eagerbufs(struct qib_ctxtdata *);
void qib_set_ctxtcnt(struct qib_devdata *);
int qib_create_ctxts(struct qib_devdata *dd);
struct qib_ctxtdata *qib_create_ctxtdata(struct qib_pportdata *, u32, int);
-void qib_init_pportdata(struct qib_pportdata *, struct qib_devdata *, u8, u8);
+int qib_init_pportdata(struct qib_pportdata *, struct qib_devdata *, u8, u8);
void qib_free_ctxtdata(struct qib_devdata *, struct qib_ctxtdata *);
u32 qib_kreceive(struct qib_ctxtdata *, u32 *, u32 *);
@@ -1449,6 +1451,10 @@ void qib_nomsi(struct qib_devdata *);
void qib_nomsix(struct qib_devdata *);
void qib_pcie_getcmd(struct qib_devdata *, u16 *, u8 *, u8 *);
void qib_pcie_reenable(struct qib_devdata *, u16, u8, u8);
+/* interrupts for device */
+u64 qib_int_counter(struct qib_devdata *);
+/* interrupt for all devices */
+u64 qib_sps_ints(void);
/*
* dma_addr wrappers - all 0's invalid for hw
diff --git a/drivers/infiniband/hw/qib/qib_diag.c b/drivers/infiniband/hw/qib/qib_diag.c
index 1686fd4bda87..5dfda4c5cc9c 100644
--- a/drivers/infiniband/hw/qib/qib_diag.c
+++ b/drivers/infiniband/hw/qib/qib_diag.c
@@ -546,7 +546,7 @@ static ssize_t qib_diagpkt_write(struct file *fp,
size_t count, loff_t *off)
{
u32 __iomem *piobuf;
- u32 plen, clen, pbufn;
+ u32 plen, pbufn, maxlen_reserve;
struct qib_diag_xpkt dp;
u32 *tmpbuf = NULL;
struct qib_devdata *dd;
@@ -590,15 +590,20 @@ static ssize_t qib_diagpkt_write(struct file *fp,
}
ppd = &dd->pport[dp.port - 1];
- /* need total length before first word written */
- /* +1 word is for the qword padding */
- plen = sizeof(u32) + dp.len;
- clen = dp.len >> 2;
-
- if ((plen + 4) > ppd->ibmaxlen) {
+ /*
+ * need total length before first word written, plus 2 Dwords. One Dword
+ * is for padding so we get the full user data when not aligned on
+ * a word boundary. The other Dword is to make sure we have room for the
+ * ICRC which gets tacked on later.
+ */
+ maxlen_reserve = 2 * sizeof(u32);
+ if (dp.len > ppd->ibmaxlen - maxlen_reserve) {
ret = -EINVAL;
- goto bail; /* before writing pbc */
+ goto bail;
}
+
+ plen = sizeof(u32) + dp.len;
+
tmpbuf = vmalloc(plen);
if (!tmpbuf) {
qib_devinfo(dd->pcidev,
@@ -638,11 +643,11 @@ static ssize_t qib_diagpkt_write(struct file *fp,
*/
if (dd->flags & QIB_PIO_FLUSH_WC) {
qib_flush_wc();
- qib_pio_copy(piobuf + 2, tmpbuf, clen - 1);
+ qib_pio_copy(piobuf + 2, tmpbuf, plen - 1);
qib_flush_wc();
- __raw_writel(tmpbuf[clen - 1], piobuf + clen + 1);
+ __raw_writel(tmpbuf[plen - 1], piobuf + plen + 1);
} else
- qib_pio_copy(piobuf + 2, tmpbuf, clen);
+ qib_pio_copy(piobuf + 2, tmpbuf, plen);
if (dd->flags & QIB_USE_SPCL_TRIG) {
u32 spcl_off = (pbufn >= dd->piobcnt2k) ? 2047 : 1023;
@@ -689,28 +694,23 @@ int qib_register_observer(struct qib_devdata *dd,
const struct diag_observer *op)
{
struct diag_observer_list_elt *olp;
- int ret = -EINVAL;
+ unsigned long flags;
if (!dd || !op)
- goto bail;
- ret = -ENOMEM;
+ return -EINVAL;
olp = vmalloc(sizeof *olp);
if (!olp) {
pr_err("vmalloc for observer failed\n");
- goto bail;
+ return -ENOMEM;
}
- if (olp) {
- unsigned long flags;
- spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
- olp->op = op;
- olp->next = dd->diag_observer_list;
- dd->diag_observer_list = olp;
- spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
- ret = 0;
- }
-bail:
- return ret;
+ spin_lock_irqsave(&dd->qib_diag_trans_lock, flags);
+ olp->op = op;
+ olp->next = dd->diag_observer_list;
+ dd->diag_observer_list = olp;
+ spin_unlock_irqrestore(&dd->qib_diag_trans_lock, flags);
+
+ return 0;
}
/* Remove all registered observers when device is closed */
diff --git a/drivers/infiniband/hw/qib/qib_dma.c b/drivers/infiniband/hw/qib/qib_dma.c
index 2920bb39a65b..59fe092b4b0f 100644
--- a/drivers/infiniband/hw/qib/qib_dma.c
+++ b/drivers/infiniband/hw/qib/qib_dma.c
@@ -108,6 +108,10 @@ static int qib_map_sg(struct ib_device *dev, struct scatterlist *sgl,
ret = 0;
break;
}
+ sg->dma_address = addr + sg->offset;
+#ifdef CONFIG_NEED_SG_DMA_LENGTH
+ sg->dma_length = sg->length;
+#endif
}
return ret;
}
@@ -119,21 +123,6 @@ static void qib_unmap_sg(struct ib_device *dev,
BUG_ON(!valid_dma_direction(direction));
}
-static u64 qib_sg_dma_address(struct ib_device *dev, struct scatterlist *sg)
-{
- u64 addr = (u64) page_address(sg_page(sg));
-
- if (addr)
- addr += sg->offset;
- return addr;
-}
-
-static unsigned int qib_sg_dma_len(struct ib_device *dev,
- struct scatterlist *sg)
-{
- return sg->length;
-}
-
static void qib_sync_single_for_cpu(struct ib_device *dev, u64 addr,
size_t size, enum dma_data_direction dir)
{
@@ -173,8 +162,6 @@ struct ib_dma_mapping_ops qib_dma_mapping_ops = {
.unmap_page = qib_dma_unmap_page,
.map_sg = qib_map_sg,
.unmap_sg = qib_unmap_sg,
- .dma_address = qib_sg_dma_address,
- .dma_len = qib_sg_dma_len,
.sync_single_for_cpu = qib_sync_single_for_cpu,
.sync_single_for_device = qib_sync_single_for_device,
.alloc_coherent = qib_dma_alloc_coherent,
diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c
index 275f247f9fca..b15e34eeef68 100644
--- a/drivers/infiniband/hw/qib/qib_file_ops.c
+++ b/drivers/infiniband/hw/qib/qib_file_ops.c
@@ -1459,7 +1459,7 @@ static int get_a_ctxt(struct file *fp, const struct qib_user_info *uinfo,
cused++;
else
cfree++;
- if (pusable && cfree && cused < inuse) {
+ if (cfree && cused < inuse) {
udd = dd;
inuse = cused;
}
@@ -1578,7 +1578,7 @@ static int do_qib_user_sdma_queue_create(struct file *fp)
struct qib_ctxtdata *rcd = fd->rcd;
struct qib_devdata *dd = rcd->dd;
- if (dd->flags & QIB_HAS_SEND_DMA)
+ if (dd->flags & QIB_HAS_SEND_DMA) {
fd->pq = qib_user_sdma_queue_create(&dd->pcidev->dev,
dd->unit,
@@ -1586,6 +1586,7 @@ static int do_qib_user_sdma_queue_create(struct file *fp)
fd->subctxt);
if (!fd->pq)
return -ENOMEM;
+ }
return 0;
}
diff --git a/drivers/infiniband/hw/qib/qib_fs.c b/drivers/infiniband/hw/qib/qib_fs.c
index c61e2a92b3c1..cab610ccd50e 100644
--- a/drivers/infiniband/hw/qib/qib_fs.c
+++ b/drivers/infiniband/hw/qib/qib_fs.c
@@ -105,6 +105,7 @@ static int create_file(const char *name, umode_t mode,
static ssize_t driver_stats_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
+ qib_stats.sps_ints = qib_sps_ints();
return simple_read_from_buffer(buf, count, ppos, &qib_stats,
sizeof qib_stats);
}
diff --git a/drivers/infiniband/hw/qib/qib_iba6120.c b/drivers/infiniband/hw/qib/qib_iba6120.c
index 84e593d6007b..d68266ac7619 100644
--- a/drivers/infiniband/hw/qib/qib_iba6120.c
+++ b/drivers/infiniband/hw/qib/qib_iba6120.c
@@ -1634,9 +1634,7 @@ static irqreturn_t qib_6120intr(int irq, void *data)
goto bail;
}
- qib_stats.sps_ints++;
- if (dd->int_counter != (u32) -1)
- dd->int_counter++;
+ this_cpu_inc(*dd->int_counter);
if (unlikely(istat & (~QLOGIC_IB_I_BITSEXTANT |
QLOGIC_IB_I_GPIO | QLOGIC_IB_I_ERROR)))
@@ -1808,7 +1806,8 @@ static int qib_6120_setup_reset(struct qib_devdata *dd)
* isn't set.
*/
dd->flags &= ~(QIB_INITTED | QIB_PRESENT);
- dd->int_counter = 0; /* so we check interrupts work again */
+ /* so we check interrupts work again */
+ dd->z_int_counter = qib_int_counter(dd);
val = dd->control | QLOGIC_IB_C_RESET;
writeq(val, &dd->kregbase[kr_control]);
mb(); /* prevent compiler re-ordering around actual reset */
@@ -3266,7 +3265,9 @@ static int init_6120_variables(struct qib_devdata *dd)
dd->eep_st_masks[2].errs_to_log = ERR_MASK(ResetNegated);
- qib_init_pportdata(ppd, dd, 0, 1);
+ ret = qib_init_pportdata(ppd, dd, 0, 1);
+ if (ret)
+ goto bail;
ppd->link_width_supported = IB_WIDTH_1X | IB_WIDTH_4X;
ppd->link_speed_supported = QIB_IB_SDR;
ppd->link_width_enabled = IB_WIDTH_4X;
diff --git a/drivers/infiniband/hw/qib/qib_iba7220.c b/drivers/infiniband/hw/qib/qib_iba7220.c
index 454c2e7668fe..7dec89fdc124 100644
--- a/drivers/infiniband/hw/qib/qib_iba7220.c
+++ b/drivers/infiniband/hw/qib/qib_iba7220.c
@@ -1962,10 +1962,7 @@ static irqreturn_t qib_7220intr(int irq, void *data)
goto bail;
}
- qib_stats.sps_ints++;
- if (dd->int_counter != (u32) -1)
- dd->int_counter++;
-
+ this_cpu_inc(*dd->int_counter);
if (unlikely(istat & (~QLOGIC_IB_I_BITSEXTANT |
QLOGIC_IB_I_GPIO | QLOGIC_IB_I_ERROR)))
unlikely_7220_intr(dd, istat);
@@ -2120,7 +2117,8 @@ static int qib_setup_7220_reset(struct qib_devdata *dd)
* isn't set.
*/
dd->flags &= ~(QIB_INITTED | QIB_PRESENT);
- dd->int_counter = 0; /* so we check interrupts work again */
+ /* so we check interrupts work again */
+ dd->z_int_counter = qib_int_counter(dd);
val = dd->control | QLOGIC_IB_C_RESET;
writeq(val, &dd->kregbase[kr_control]);
mb(); /* prevent compiler reordering around actual reset */
@@ -4061,7 +4059,9 @@ static int qib_init_7220_variables(struct qib_devdata *dd)
init_waitqueue_head(&cpspec->autoneg_wait);
INIT_DELAYED_WORK(&cpspec->autoneg_work, autoneg_7220_work);
- qib_init_pportdata(ppd, dd, 0, 1);
+ ret = qib_init_pportdata(ppd, dd, 0, 1);
+ if (ret)
+ goto bail;
ppd->link_width_supported = IB_WIDTH_1X | IB_WIDTH_4X;
ppd->link_speed_supported = QIB_IB_SDR | QIB_IB_DDR;
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index d1bd21319d7d..a7eb32517a04 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -3115,9 +3115,7 @@ static irqreturn_t qib_7322intr(int irq, void *data)
goto bail;
}
- qib_stats.sps_ints++;
- if (dd->int_counter != (u32) -1)
- dd->int_counter++;
+ this_cpu_inc(*dd->int_counter);
/* handle "errors" of various kinds first, device ahead of port */
if (unlikely(istat & (~QIB_I_BITSEXTANT | QIB_I_GPIO |
@@ -3186,9 +3184,7 @@ static irqreturn_t qib_7322pintr(int irq, void *data)
*/
return IRQ_HANDLED;
- qib_stats.sps_ints++;
- if (dd->int_counter != (u32) -1)
- dd->int_counter++;
+ this_cpu_inc(*dd->int_counter);
/* Clear the interrupt bit we expect to be set. */
qib_write_kreg(dd, kr_intclear, ((1ULL << QIB_I_RCVAVAIL_LSB) |
@@ -3215,9 +3211,7 @@ static irqreturn_t qib_7322bufavail(int irq, void *data)
*/
return IRQ_HANDLED;
- qib_stats.sps_ints++;
- if (dd->int_counter != (u32) -1)
- dd->int_counter++;
+ this_cpu_inc(*dd->int_counter);
/* Clear the interrupt bit we expect to be set. */
qib_write_kreg(dd, kr_intclear, QIB_I_SPIOBUFAVAIL);
@@ -3248,9 +3242,7 @@ static irqreturn_t sdma_intr(int irq, void *data)
*/
return IRQ_HANDLED;
- qib_stats.sps_ints++;
- if (dd->int_counter != (u32) -1)
- dd->int_counter++;
+ this_cpu_inc(*dd->int_counter);
/* Clear the interrupt bit we expect to be set. */
qib_write_kreg(dd, kr_intclear, ppd->hw_pidx ?
@@ -3277,9 +3269,7 @@ static irqreturn_t sdma_idle_intr(int irq, void *data)
*/
return IRQ_HANDLED;
- qib_stats.sps_ints++;
- if (dd->int_counter != (u32) -1)
- dd->int_counter++;
+ this_cpu_inc(*dd->int_counter);
/* Clear the interrupt bit we expect to be set. */
qib_write_kreg(dd, kr_intclear, ppd->hw_pidx ?
@@ -3306,9 +3296,7 @@ static irqreturn_t sdma_progress_intr(int irq, void *data)
*/
return IRQ_HANDLED;
- qib_stats.sps_ints++;
- if (dd->int_counter != (u32) -1)
- dd->int_counter++;
+ this_cpu_inc(*dd->int_counter);
/* Clear the interrupt bit we expect to be set. */
qib_write_kreg(dd, kr_intclear, ppd->hw_pidx ?
@@ -3336,9 +3324,7 @@ static irqreturn_t sdma_cleanup_intr(int irq, void *data)
*/
return IRQ_HANDLED;
- qib_stats.sps_ints++;
- if (dd->int_counter != (u32) -1)
- dd->int_counter++;
+ this_cpu_inc(*dd->int_counter);
/* Clear the interrupt bit we expect to be set. */
qib_write_kreg(dd, kr_intclear, ppd->hw_pidx ?
@@ -3723,7 +3709,8 @@ static int qib_do_7322_reset(struct qib_devdata *dd)
dd->pport->cpspec->ibsymdelta = 0;
dd->pport->cpspec->iblnkerrdelta = 0;
dd->pport->cpspec->ibmalfdelta = 0;
- dd->int_counter = 0; /* so we check interrupts work again */
+ /* so we check interrupts work again */
+ dd->z_int_counter = qib_int_counter(dd);
/*
* Keep chip from being accessed until we are ready. Use
@@ -6557,7 +6544,11 @@ static int qib_init_7322_variables(struct qib_devdata *dd)
}
dd->num_pports++;
- qib_init_pportdata(ppd, dd, pidx, dd->num_pports);
+ ret = qib_init_pportdata(ppd, dd, pidx, dd->num_pports);
+ if (ret) {
+ dd->num_pports--;
+ goto bail;
+ }
ppd->link_width_supported = IB_WIDTH_1X | IB_WIDTH_4X;
ppd->link_width_enabled = IB_WIDTH_4X;
diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c
index 24e802f4ea2f..5b7aeb224a30 100644
--- a/drivers/infiniband/hw/qib/qib_init.c
+++ b/drivers/infiniband/hw/qib/qib_init.c
@@ -130,7 +130,6 @@ void qib_set_ctxtcnt(struct qib_devdata *dd)
int qib_create_ctxts(struct qib_devdata *dd)
{
unsigned i;
- int ret;
int local_node_id = pcibus_to_node(dd->pcidev->bus);
if (local_node_id < 0)
@@ -145,8 +144,7 @@ int qib_create_ctxts(struct qib_devdata *dd)
if (!dd->rcd) {
qib_dev_err(dd,
"Unable to allocate ctxtdata array, failing\n");
- ret = -ENOMEM;
- goto done;
+ return -ENOMEM;
}
/* create (one or more) kctxt */
@@ -163,15 +161,14 @@ int qib_create_ctxts(struct qib_devdata *dd)
if (!rcd) {
qib_dev_err(dd,
"Unable to allocate ctxtdata for Kernel ctxt, failing\n");
- ret = -ENOMEM;
- goto done;
+ kfree(dd->rcd);
+ dd->rcd = NULL;
+ return -ENOMEM;
}
rcd->pkeys[0] = QIB_DEFAULT_P_KEY;
rcd->seq_cnt = 1;
}
- ret = 0;
-done:
- return ret;
+ return 0;
}
/*
@@ -233,7 +230,7 @@ struct qib_ctxtdata *qib_create_ctxtdata(struct qib_pportdata *ppd, u32 ctxt,
/*
* Common code for initializing the physical port structure.
*/
-void qib_init_pportdata(struct qib_pportdata *ppd, struct qib_devdata *dd,
+int qib_init_pportdata(struct qib_pportdata *ppd, struct qib_devdata *dd,
u8 hw_pidx, u8 port)
{
int size;
@@ -243,6 +240,7 @@ void qib_init_pportdata(struct qib_pportdata *ppd, struct qib_devdata *dd,
spin_lock_init(&ppd->sdma_lock);
spin_lock_init(&ppd->lflags_lock);
+ spin_lock_init(&ppd->cc_shadow_lock);
init_waitqueue_head(&ppd->state_wait);
init_timer(&ppd->symerr_clear_timer);
@@ -250,8 +248,10 @@ void qib_init_pportdata(struct qib_pportdata *ppd, struct qib_devdata *dd,
ppd->symerr_clear_timer.data = (unsigned long)ppd;
ppd->qib_wq = NULL;
-
- spin_lock_init(&ppd->cc_shadow_lock);
+ ppd->ibport_data.pmastats =
+ alloc_percpu(struct qib_pma_counters);
+ if (!ppd->ibport_data.pmastats)
+ return -ENOMEM;
if (qib_cc_table_size < IB_CCT_MIN_ENTRIES)
goto bail;
@@ -299,7 +299,7 @@ void qib_init_pportdata(struct qib_pportdata *ppd, struct qib_devdata *dd,
goto bail_3;
}
- return;
+ return 0;
bail_3:
kfree(ppd->ccti_entries_shadow);
@@ -313,7 +313,7 @@ bail_1:
bail:
/* User is intentionally disabling the congestion control agent */
if (!qib_cc_table_size)
- return;
+ return 0;
if (qib_cc_table_size < IB_CCT_MIN_ENTRIES) {
qib_cc_table_size = 0;
@@ -324,7 +324,7 @@ bail:
qib_dev_err(dd, "Congestion Control Agent disabled for port %d\n",
port);
- return;
+ return 0;
}
static int init_pioavailregs(struct qib_devdata *dd)
@@ -525,6 +525,7 @@ static void enable_chip(struct qib_devdata *dd)
static void verify_interrupt(unsigned long opaque)
{
struct qib_devdata *dd = (struct qib_devdata *) opaque;
+ u64 int_counter;
if (!dd)
return; /* being torn down */
@@ -533,7 +534,8 @@ static void verify_interrupt(unsigned long opaque)
* If we don't have a lid or any interrupts, let the user know and
* don't bother checking again.
*/
- if (dd->int_counter == 0) {
+ int_counter = qib_int_counter(dd) - dd->z_int_counter;
+ if (int_counter == 0) {
if (!dd->f_intr_fallback(dd))
dev_err(&dd->pcidev->dev,
"No interrupts detected, not usable.\n");
@@ -633,6 +635,12 @@ wq_error:
return -ENOMEM;
}
+static void qib_free_pportdata(struct qib_pportdata *ppd)
+{
+ free_percpu(ppd->ibport_data.pmastats);
+ ppd->ibport_data.pmastats = NULL;
+}
+
/**
* qib_init - do the actual initialization sequence on the chip
* @dd: the qlogic_ib device
@@ -920,6 +928,7 @@ static void qib_shutdown_device(struct qib_devdata *dd)
destroy_workqueue(ppd->qib_wq);
ppd->qib_wq = NULL;
}
+ qib_free_pportdata(ppd);
}
qib_update_eeprom_log(dd);
@@ -1079,9 +1088,34 @@ void qib_free_devdata(struct qib_devdata *dd)
#ifdef CONFIG_DEBUG_FS
qib_dbg_ibdev_exit(&dd->verbs_dev);
#endif
+ free_percpu(dd->int_counter);
ib_dealloc_device(&dd->verbs_dev.ibdev);
}
+u64 qib_int_counter(struct qib_devdata *dd)
+{
+ int cpu;
+ u64 int_counter = 0;
+
+ for_each_possible_cpu(cpu)
+ int_counter += *per_cpu_ptr(dd->int_counter, cpu);
+ return int_counter;
+}
+
+u64 qib_sps_ints(void)
+{
+ unsigned long flags;
+ struct qib_devdata *dd;
+ u64 sps_ints = 0;
+
+ spin_lock_irqsave(&qib_devs_lock, flags);
+ list_for_each_entry(dd, &qib_dev_list, list) {
+ sps_ints += qib_int_counter(dd);
+ }
+ spin_unlock_irqrestore(&qib_devs_lock, flags);
+ return sps_ints;
+}
+
/*
* Allocate our primary per-unit data structure. Must be done via verbs
* allocator, because the verbs cleanup process both does cleanup and
@@ -1097,14 +1131,10 @@ struct qib_devdata *qib_alloc_devdata(struct pci_dev *pdev, size_t extra)
int ret;
dd = (struct qib_devdata *) ib_alloc_device(sizeof(*dd) + extra);
- if (!dd) {
- dd = ERR_PTR(-ENOMEM);
- goto bail;
- }
+ if (!dd)
+ return ERR_PTR(-ENOMEM);
-#ifdef CONFIG_DEBUG_FS
- qib_dbg_ibdev_init(&dd->verbs_dev);
-#endif
+ INIT_LIST_HEAD(&dd->list);
idr_preload(GFP_KERNEL);
spin_lock_irqsave(&qib_devs_lock, flags);
@@ -1121,11 +1151,13 @@ struct qib_devdata *qib_alloc_devdata(struct pci_dev *pdev, size_t extra)
if (ret < 0) {
qib_early_err(&pdev->dev,
"Could not allocate unit ID: error %d\n", -ret);
-#ifdef CONFIG_DEBUG_FS
- qib_dbg_ibdev_exit(&dd->verbs_dev);
-#endif
- ib_dealloc_device(&dd->verbs_dev.ibdev);
- dd = ERR_PTR(ret);
+ goto bail;
+ }
+ dd->int_counter = alloc_percpu(u64);
+ if (!dd->int_counter) {
+ ret = -ENOMEM;
+ qib_early_err(&pdev->dev,
+ "Could not allocate per-cpu int_counter\n");
goto bail;
}
@@ -1139,9 +1171,15 @@ struct qib_devdata *qib_alloc_devdata(struct pci_dev *pdev, size_t extra)
qib_early_err(&pdev->dev,
"Could not alloc cpulist info, cpu affinity might be wrong\n");
}
-
-bail:
+#ifdef CONFIG_DEBUG_FS
+ qib_dbg_ibdev_init(&dd->verbs_dev);
+#endif
return dd;
+bail:
+ if (!list_empty(&dd->list))
+ list_del_init(&dd->list);
+ ib_dealloc_device(&dd->verbs_dev.ibdev);
+ return ERR_PTR(ret);;
}
/*
diff --git a/drivers/infiniband/hw/qib/qib_mad.c b/drivers/infiniband/hw/qib/qib_mad.c
index ccb119143d20..edad991d60ed 100644
--- a/drivers/infiniband/hw/qib/qib_mad.c
+++ b/drivers/infiniband/hw/qib/qib_mad.c
@@ -1634,6 +1634,23 @@ static int pma_get_portcounters_cong(struct ib_pma_mad *pmp,
return reply((struct ib_smp *)pmp);
}
+static void qib_snapshot_pmacounters(
+ struct qib_ibport *ibp,
+ struct qib_pma_counters *pmacounters)
+{
+ struct qib_pma_counters *p;
+ int cpu;
+
+ memset(pmacounters, 0, sizeof(*pmacounters));
+ for_each_possible_cpu(cpu) {
+ p = per_cpu_ptr(ibp->pmastats, cpu);
+ pmacounters->n_unicast_xmit += p->n_unicast_xmit;
+ pmacounters->n_unicast_rcv += p->n_unicast_rcv;
+ pmacounters->n_multicast_xmit += p->n_multicast_xmit;
+ pmacounters->n_multicast_rcv += p->n_multicast_rcv;
+ }
+}
+
static int pma_get_portcounters_ext(struct ib_pma_mad *pmp,
struct ib_device *ibdev, u8 port)
{
@@ -1642,6 +1659,7 @@ static int pma_get_portcounters_ext(struct ib_pma_mad *pmp,
struct qib_ibport *ibp = to_iport(ibdev, port);
struct qib_pportdata *ppd = ppd_from_ibp(ibp);
u64 swords, rwords, spkts, rpkts, xwait;
+ struct qib_pma_counters pma;
u8 port_select = p->port_select;
memset(pmp->data, 0, sizeof(pmp->data));
@@ -1664,10 +1682,17 @@ static int pma_get_portcounters_ext(struct ib_pma_mad *pmp,
p->port_rcv_data = cpu_to_be64(rwords);
p->port_xmit_packets = cpu_to_be64(spkts);
p->port_rcv_packets = cpu_to_be64(rpkts);
- p->port_unicast_xmit_packets = cpu_to_be64(ibp->n_unicast_xmit);
- p->port_unicast_rcv_packets = cpu_to_be64(ibp->n_unicast_rcv);
- p->port_multicast_xmit_packets = cpu_to_be64(ibp->n_multicast_xmit);
- p->port_multicast_rcv_packets = cpu_to_be64(ibp->n_multicast_rcv);
+
+ qib_snapshot_pmacounters(ibp, &pma);
+
+ p->port_unicast_xmit_packets = cpu_to_be64(pma.n_unicast_xmit
+ - ibp->z_unicast_xmit);
+ p->port_unicast_rcv_packets = cpu_to_be64(pma.n_unicast_rcv
+ - ibp->z_unicast_rcv);
+ p->port_multicast_xmit_packets = cpu_to_be64(pma.n_multicast_xmit
+ - ibp->z_multicast_xmit);
+ p->port_multicast_rcv_packets = cpu_to_be64(pma.n_multicast_rcv
+ - ibp->z_multicast_rcv);
bail:
return reply((struct ib_smp *) pmp);
@@ -1795,6 +1820,7 @@ static int pma_set_portcounters_ext(struct ib_pma_mad *pmp,
struct qib_ibport *ibp = to_iport(ibdev, port);
struct qib_pportdata *ppd = ppd_from_ibp(ibp);
u64 swords, rwords, spkts, rpkts, xwait;
+ struct qib_pma_counters pma;
qib_snapshot_counters(ppd, &swords, &rwords, &spkts, &rpkts, &xwait);
@@ -1810,17 +1836,19 @@ static int pma_set_portcounters_ext(struct ib_pma_mad *pmp,
if (p->counter_select & IB_PMA_SELX_PORT_RCV_PACKETS)
ibp->z_port_rcv_packets = rpkts;
+ qib_snapshot_pmacounters(ibp, &pma);
+
if (p->counter_select & IB_PMA_SELX_PORT_UNI_XMIT_PACKETS)
- ibp->n_unicast_xmit = 0;
+ ibp->z_unicast_xmit = pma.n_unicast_xmit;
if (p->counter_select & IB_PMA_SELX_PORT_UNI_RCV_PACKETS)
- ibp->n_unicast_rcv = 0;
+ ibp->z_unicast_rcv = pma.n_unicast_rcv;
if (p->counter_select & IB_PMA_SELX_PORT_MULTI_XMIT_PACKETS)
- ibp->n_multicast_xmit = 0;
+ ibp->z_multicast_xmit = pma.n_multicast_xmit;
if (p->counter_select & IB_PMA_SELX_PORT_MULTI_RCV_PACKETS)
- ibp->n_multicast_rcv = 0;
+ ibp->z_multicast_rcv = pma.n_multicast_rcv;
return pma_get_portcounters_ext(pmp, ibdev, port);
}
diff --git a/drivers/infiniband/hw/qib/qib_mr.c b/drivers/infiniband/hw/qib/qib_mr.c
index e6687ded8210..9bbb55347cc1 100644
--- a/drivers/infiniband/hw/qib/qib_mr.c
+++ b/drivers/infiniband/hw/qib/qib_mr.c
@@ -232,8 +232,8 @@ struct ib_mr *qib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
{
struct qib_mr *mr;
struct ib_umem *umem;
- struct ib_umem_chunk *chunk;
- int n, m, i;
+ struct scatterlist *sg;
+ int n, m, entry;
struct ib_mr *ret;
if (length == 0) {
@@ -246,9 +246,7 @@ struct ib_mr *qib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
if (IS_ERR(umem))
return (void *) umem;
- n = 0;
- list_for_each_entry(chunk, &umem->chunk_list, list)
- n += chunk->nents;
+ n = umem->nmap;
mr = alloc_mr(n, pd);
if (IS_ERR(mr)) {
@@ -268,11 +266,10 @@ struct ib_mr *qib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
mr->mr.page_shift = ilog2(umem->page_size);
m = 0;
n = 0;
- list_for_each_entry(chunk, &umem->chunk_list, list) {
- for (i = 0; i < chunk->nents; i++) {
+ for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
void *vaddr;
- vaddr = page_address(sg_page(&chunk->page_list[i]));
+ vaddr = page_address(sg_page(sg));
if (!vaddr) {
ret = ERR_PTR(-EINVAL);
goto bail;
@@ -284,7 +281,6 @@ struct ib_mr *qib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
m++;
n = 0;
}
- }
}
ret = &mr->ibmr;
diff --git a/drivers/infiniband/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c
index 3ab341320ead..2f2501890c4e 100644
--- a/drivers/infiniband/hw/qib/qib_rc.c
+++ b/drivers/infiniband/hw/qib/qib_rc.c
@@ -752,7 +752,7 @@ void qib_send_rc_ack(struct qib_qp *qp)
qib_flush_wc();
qib_sendbuf_done(dd, pbufn);
- ibp->n_unicast_xmit++;
+ this_cpu_inc(ibp->pmastats->n_unicast_xmit);
goto done;
queue_ack:
diff --git a/drivers/infiniband/hw/qib/qib_ruc.c b/drivers/infiniband/hw/qib/qib_ruc.c
index 357b6cfcd46c..4c07a8b34ffe 100644
--- a/drivers/infiniband/hw/qib/qib_ruc.c
+++ b/drivers/infiniband/hw/qib/qib_ruc.c
@@ -703,6 +703,7 @@ void qib_make_ruc_header(struct qib_qp *qp, struct qib_other_headers *ohdr,
ohdr->bth[0] = cpu_to_be32(bth0);
ohdr->bth[1] = cpu_to_be32(qp->remote_qpn);
ohdr->bth[2] = cpu_to_be32(bth2);
+ this_cpu_inc(ibp->pmastats->n_unicast_xmit);
}
/**
diff --git a/drivers/infiniband/hw/qib/qib_ud.c b/drivers/infiniband/hw/qib/qib_ud.c
index 3ad651c3356c..aaf7039f8ed2 100644
--- a/drivers/infiniband/hw/qib/qib_ud.c
+++ b/drivers/infiniband/hw/qib/qib_ud.c
@@ -280,11 +280,11 @@ int qib_make_ud_req(struct qib_qp *qp)
ah_attr = &to_iah(wqe->wr.wr.ud.ah)->attr;
if (ah_attr->dlid >= QIB_MULTICAST_LID_BASE) {
if (ah_attr->dlid != QIB_PERMISSIVE_LID)
- ibp->n_multicast_xmit++;
+ this_cpu_inc(ibp->pmastats->n_multicast_xmit);
else
- ibp->n_unicast_xmit++;
+ this_cpu_inc(ibp->pmastats->n_unicast_xmit);
} else {
- ibp->n_unicast_xmit++;
+ this_cpu_inc(ibp->pmastats->n_unicast_xmit);
lid = ah_attr->dlid & ~((1 << ppd->lmc) - 1);
if (unlikely(lid == ppd->lid)) {
/*
diff --git a/drivers/infiniband/hw/qib/qib_user_sdma.c b/drivers/infiniband/hw/qib/qib_user_sdma.c
index 165aee2ca8a0..d2806cae234c 100644
--- a/drivers/infiniband/hw/qib/qib_user_sdma.c
+++ b/drivers/infiniband/hw/qib/qib_user_sdma.c
@@ -52,6 +52,17 @@
/* attempt to drain the queue for 5secs */
#define QIB_USER_SDMA_DRAIN_TIMEOUT 500
+/*
+ * track how many times a process open this driver.
+ */
+static struct rb_root qib_user_sdma_rb_root = RB_ROOT;
+
+struct qib_user_sdma_rb_node {
+ struct rb_node node;
+ int refcount;
+ pid_t pid;
+};
+
struct qib_user_sdma_pkt {
struct list_head list; /* list element */
@@ -120,15 +131,60 @@ struct qib_user_sdma_queue {
/* dma page table */
struct rb_root dma_pages_root;
+ struct qib_user_sdma_rb_node *sdma_rb_node;
+
/* protect everything above... */
struct mutex lock;
};
+static struct qib_user_sdma_rb_node *
+qib_user_sdma_rb_search(struct rb_root *root, pid_t pid)
+{
+ struct qib_user_sdma_rb_node *sdma_rb_node;
+ struct rb_node *node = root->rb_node;
+
+ while (node) {
+ sdma_rb_node = container_of(node,
+ struct qib_user_sdma_rb_node, node);
+ if (pid < sdma_rb_node->pid)
+ node = node->rb_left;
+ else if (pid > sdma_rb_node->pid)
+ node = node->rb_right;
+ else
+ return sdma_rb_node;
+ }
+ return NULL;
+}
+
+static int
+qib_user_sdma_rb_insert(struct rb_root *root, struct qib_user_sdma_rb_node *new)
+{
+ struct rb_node **node = &(root->rb_node);
+ struct rb_node *parent = NULL;
+ struct qib_user_sdma_rb_node *got;
+
+ while (*node) {
+ got = container_of(*node, struct qib_user_sdma_rb_node, node);
+ parent = *node;
+ if (new->pid < got->pid)
+ node = &((*node)->rb_left);
+ else if (new->pid > got->pid)
+ node = &((*node)->rb_right);
+ else
+ return 0;
+ }
+
+ rb_link_node(&new->node, parent, node);
+ rb_insert_color(&new->node, root);
+ return 1;
+}
+
struct qib_user_sdma_queue *
qib_user_sdma_queue_create(struct device *dev, int unit, int ctxt, int sctxt)
{
struct qib_user_sdma_queue *pq =
kmalloc(sizeof(struct qib_user_sdma_queue), GFP_KERNEL);
+ struct qib_user_sdma_rb_node *sdma_rb_node;
if (!pq)
goto done;
@@ -138,6 +194,7 @@ qib_user_sdma_queue_create(struct device *dev, int unit, int ctxt, int sctxt)
pq->num_pending = 0;
pq->num_sending = 0;
pq->added = 0;
+ pq->sdma_rb_node = NULL;
INIT_LIST_HEAD(&pq->sent);
spin_lock_init(&pq->sent_lock);
@@ -163,8 +220,30 @@ qib_user_sdma_queue_create(struct device *dev, int unit, int ctxt, int sctxt)
pq->dma_pages_root = RB_ROOT;
+ sdma_rb_node = qib_user_sdma_rb_search(&qib_user_sdma_rb_root,
+ current->pid);
+ if (sdma_rb_node) {
+ sdma_rb_node->refcount++;
+ } else {
+ int ret;
+ sdma_rb_node = kmalloc(sizeof(
+ struct qib_user_sdma_rb_node), GFP_KERNEL);
+ if (!sdma_rb_node)
+ goto err_rb;
+
+ sdma_rb_node->refcount = 1;
+ sdma_rb_node->pid = current->pid;
+
+ ret = qib_user_sdma_rb_insert(&qib_user_sdma_rb_root,
+ sdma_rb_node);
+ BUG_ON(ret == 0);
+ }
+ pq->sdma_rb_node = sdma_rb_node;
+
goto done;
+err_rb:
+ dma_pool_destroy(pq->header_cache);
err_slab:
kmem_cache_destroy(pq->pkt_slab);
err_kfree:
@@ -1020,8 +1099,13 @@ void qib_user_sdma_queue_destroy(struct qib_user_sdma_queue *pq)
if (!pq)
return;
- kmem_cache_destroy(pq->pkt_slab);
+ pq->sdma_rb_node->refcount--;
+ if (pq->sdma_rb_node->refcount == 0) {
+ rb_erase(&pq->sdma_rb_node->node, &qib_user_sdma_rb_root);
+ kfree(pq->sdma_rb_node);
+ }
dma_pool_destroy(pq->header_cache);
+ kmem_cache_destroy(pq->pkt_slab);
kfree(pq);
}
@@ -1241,26 +1325,52 @@ static int qib_user_sdma_push_pkts(struct qib_pportdata *ppd,
struct qib_user_sdma_queue *pq,
struct list_head *pktlist, int count)
{
- int ret = 0;
unsigned long flags;
if (unlikely(!(ppd->lflags & QIBL_LINKACTIVE)))
return -ECOMM;
- spin_lock_irqsave(&ppd->sdma_lock, flags);
-
- if (unlikely(!__qib_sdma_running(ppd))) {
- ret = -ECOMM;
- goto unlock;
+ /* non-blocking mode */
+ if (pq->sdma_rb_node->refcount > 1) {
+ spin_lock_irqsave(&ppd->sdma_lock, flags);
+ if (unlikely(!__qib_sdma_running(ppd))) {
+ spin_unlock_irqrestore(&ppd->sdma_lock, flags);
+ return -ECOMM;
+ }
+ pq->num_pending += count;
+ list_splice_tail_init(pktlist, &ppd->sdma_userpending);
+ qib_user_sdma_send_desc(ppd, &ppd->sdma_userpending);
+ spin_unlock_irqrestore(&ppd->sdma_lock, flags);
+ return 0;
}
+ /* In this case, descriptors from this process are not
+ * linked to ppd pending queue, interrupt handler
+ * won't update this process, it is OK to directly
+ * modify without sdma lock.
+ */
+
+
pq->num_pending += count;
- list_splice_tail_init(pktlist, &ppd->sdma_userpending);
- qib_user_sdma_send_desc(ppd, &ppd->sdma_userpending);
+ /*
+ * Blocking mode for single rail process, we must
+ * release/regain sdma_lock to give other process
+ * chance to make progress. This is important for
+ * performance.
+ */
+ do {
+ spin_lock_irqsave(&ppd->sdma_lock, flags);
+ if (unlikely(!__qib_sdma_running(ppd))) {
+ spin_unlock_irqrestore(&ppd->sdma_lock, flags);
+ return -ECOMM;
+ }
+ qib_user_sdma_send_desc(ppd, pktlist);
+ if (!list_empty(pktlist))
+ qib_sdma_make_progress(ppd);
+ spin_unlock_irqrestore(&ppd->sdma_lock, flags);
+ } while (!list_empty(pktlist));
-unlock:
- spin_unlock_irqrestore(&ppd->sdma_lock, flags);
- return ret;
+ return 0;
}
int qib_user_sdma_writev(struct qib_ctxtdata *rcd,
@@ -1290,7 +1400,7 @@ int qib_user_sdma_writev(struct qib_ctxtdata *rcd,
qib_user_sdma_queue_clean(ppd, pq);
while (dim) {
- int mxp = 8;
+ int mxp = 1;
int ndesc = 0;
ret = qib_user_sdma_queue_pkts(dd, ppd, pq,
diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c
index 092b0bb1bb78..9bcfbd842980 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.c
+++ b/drivers/infiniband/hw/qib/qib_verbs.c
@@ -662,7 +662,7 @@ void qib_ib_rcv(struct qib_ctxtdata *rcd, void *rhdr, void *data, u32 tlen)
mcast = qib_mcast_find(ibp, &hdr->u.l.grh.dgid);
if (mcast == NULL)
goto drop;
- ibp->n_multicast_rcv++;
+ this_cpu_inc(ibp->pmastats->n_multicast_rcv);
list_for_each_entry_rcu(p, &mcast->qp_list, list)
qib_qp_rcv(rcd, hdr, 1, data, tlen, p->qp);
/*
@@ -678,8 +678,8 @@ void qib_ib_rcv(struct qib_ctxtdata *rcd, void *rhdr, void *data, u32 tlen)
&rcd->lookaside_qp->refcount))
wake_up(
&rcd->lookaside_qp->wait);
- rcd->lookaside_qp = NULL;
- }
+ rcd->lookaside_qp = NULL;
+ }
}
if (!rcd->lookaside_qp) {
qp = qib_lookup_qpn(ibp, qp_num);
@@ -689,7 +689,7 @@ void qib_ib_rcv(struct qib_ctxtdata *rcd, void *rhdr, void *data, u32 tlen)
rcd->lookaside_qpn = qp_num;
} else
qp = rcd->lookaside_qp;
- ibp->n_unicast_rcv++;
+ this_cpu_inc(ibp->pmastats->n_unicast_rcv);
qib_qp_rcv(rcd, hdr, lnh == QIB_LRH_GRH, data, tlen, qp);
}
return;
diff --git a/drivers/infiniband/hw/qib/qib_verbs.h b/drivers/infiniband/hw/qib/qib_verbs.h
index a01c7d2cf541..bfc8948fdd35 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.h
+++ b/drivers/infiniband/hw/qib/qib_verbs.h
@@ -664,6 +664,13 @@ struct qib_opcode_stats_perctx {
struct qib_opcode_stats stats[128];
};
+struct qib_pma_counters {
+ u64 n_unicast_xmit; /* total unicast packets sent */
+ u64 n_unicast_rcv; /* total unicast packets received */
+ u64 n_multicast_xmit; /* total multicast packets sent */
+ u64 n_multicast_rcv; /* total multicast packets received */
+};
+
struct qib_ibport {
struct qib_qp __rcu *qp0;
struct qib_qp __rcu *qp1;
@@ -680,10 +687,11 @@ struct qib_ibport {
__be64 mkey;
__be64 guids[QIB_GUIDS_PER_PORT - 1]; /* writable GUIDs */
u64 tid; /* TID for traps */
- u64 n_unicast_xmit; /* total unicast packets sent */
- u64 n_unicast_rcv; /* total unicast packets received */
- u64 n_multicast_xmit; /* total multicast packets sent */
- u64 n_multicast_rcv; /* total multicast packets received */
+ struct qib_pma_counters __percpu *pmastats;
+ u64 z_unicast_xmit; /* starting count for PMA */
+ u64 z_unicast_rcv; /* starting count for PMA */
+ u64 z_multicast_xmit; /* starting count for PMA */
+ u64 z_multicast_rcv; /* starting count for PMA */
u64 z_symbol_error_counter; /* starting count for PMA */
u64 z_link_error_recovery_counter; /* starting count for PMA */
u64 z_link_downed_counter; /* starting count for PMA */
diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c
index 16755cdab2c0..801a1d6937e4 100644
--- a/drivers/infiniband/hw/usnic/usnic_uiom.c
+++ b/drivers/infiniband/hw/usnic/usnic_uiom.c
@@ -286,7 +286,7 @@ iter_chunk:
err = iommu_map(pd->domain, va_start, pa_start,
size, flags);
if (err) {
- usnic_err("Failed to map va 0x%lx pa 0x%pa size 0x%zx with err %d\n",
+ usnic_err("Failed to map va 0x%lx pa %pa size 0x%zx with err %d\n",
va_start, &pa_start, size, err);
goto err_out;
}
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index dd03cfe596d6..25f195ef44b0 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -5,7 +5,7 @@
* Copyright (C) 2004 Alex Aizman
* Copyright (C) 2005 Mike Christie
* Copyright (c) 2005, 2006 Voltaire, Inc. All rights reserved.
- * Copyright (c) 2013 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2013-2014 Mellanox Technologies. All rights reserved.
* maintained by openib-general@openib.org
*
* This software is available to you under a choice of one of two
@@ -82,6 +82,8 @@ static unsigned int iscsi_max_lun = 512;
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
int iser_debug_level = 0;
+bool iser_pi_enable = false;
+int iser_pi_guard = 0;
MODULE_DESCRIPTION("iSER (iSCSI Extensions for RDMA) Datamover");
MODULE_LICENSE("Dual BSD/GPL");
@@ -91,6 +93,12 @@ MODULE_VERSION(DRV_VER);
module_param_named(debug_level, iser_debug_level, int, 0644);
MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0 (default:disabled)");
+module_param_named(pi_enable, iser_pi_enable, bool, 0644);
+MODULE_PARM_DESC(pi_enable, "Enable T10-PI offload support (default:disabled)");
+
+module_param_named(pi_guard, iser_pi_guard, int, 0644);
+MODULE_PARM_DESC(pi_guard, "T10-PI guard_type, 0:CRC|1:IP_CSUM (default:CRC)");
+
struct iser_global ig;
void
@@ -138,8 +146,8 @@ static int iscsi_iser_pdu_alloc(struct iscsi_task *task, uint8_t opcode)
int iser_initialize_task_headers(struct iscsi_task *task,
struct iser_tx_desc *tx_desc)
{
- struct iscsi_iser_conn *iser_conn = task->conn->dd_data;
- struct iser_device *device = iser_conn->ib_conn->device;
+ struct iser_conn *ib_conn = task->conn->dd_data;
+ struct iser_device *device = ib_conn->device;
struct iscsi_iser_task *iser_task = task->dd_data;
u64 dma_addr;
@@ -153,7 +161,7 @@ int iser_initialize_task_headers(struct iscsi_task *task,
tx_desc->tx_sg[0].length = ISER_HEADERS_LEN;
tx_desc->tx_sg[0].lkey = device->mr->lkey;
- iser_task->iser_conn = iser_conn;
+ iser_task->ib_conn = ib_conn;
return 0;
}
/**
@@ -176,6 +184,8 @@ iscsi_iser_task_init(struct iscsi_task *task)
iser_task->command_sent = 0;
iser_task_rdma_init(iser_task);
+ iser_task->sc = task->sc;
+
return 0;
}
@@ -278,10 +288,9 @@ iscsi_iser_task_xmit(struct iscsi_task *task)
static void iscsi_iser_cleanup_task(struct iscsi_task *task)
{
struct iscsi_iser_task *iser_task = task->dd_data;
- struct iser_tx_desc *tx_desc = &iser_task->desc;
-
- struct iscsi_iser_conn *iser_conn = task->conn->dd_data;
- struct iser_device *device = iser_conn->ib_conn->device;
+ struct iser_tx_desc *tx_desc = &iser_task->desc;
+ struct iser_conn *ib_conn = task->conn->dd_data;
+ struct iser_device *device = ib_conn->device;
ib_dma_unmap_single(device->ib_device,
tx_desc->dma_addr, ISER_HEADERS_LEN, DMA_TO_DEVICE);
@@ -296,14 +305,25 @@ static void iscsi_iser_cleanup_task(struct iscsi_task *task)
}
}
+static u8 iscsi_iser_check_protection(struct iscsi_task *task, sector_t *sector)
+{
+ struct iscsi_iser_task *iser_task = task->dd_data;
+
+ if (iser_task->dir[ISER_DIR_IN])
+ return iser_check_task_pi_status(iser_task, ISER_DIR_IN,
+ sector);
+ else
+ return iser_check_task_pi_status(iser_task, ISER_DIR_OUT,
+ sector);
+}
+
static struct iscsi_cls_conn *
iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
{
struct iscsi_conn *conn;
struct iscsi_cls_conn *cls_conn;
- struct iscsi_iser_conn *iser_conn;
- cls_conn = iscsi_conn_setup(cls_session, sizeof(*iser_conn), conn_idx);
+ cls_conn = iscsi_conn_setup(cls_session, 0, conn_idx);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
@@ -314,10 +334,6 @@ iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
*/
conn->max_recv_dlength = ISER_RECV_DATA_SEG_LEN;
- iser_conn = conn->dd_data;
- conn->dd_data = iser_conn;
- iser_conn->iscsi_conn = conn;
-
return cls_conn;
}
@@ -325,8 +341,7 @@ static void
iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn)
{
struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_iser_conn *iser_conn = conn->dd_data;
- struct iser_conn *ib_conn = iser_conn->ib_conn;
+ struct iser_conn *ib_conn = conn->dd_data;
iscsi_conn_teardown(cls_conn);
/*
@@ -335,7 +350,7 @@ iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn)
* we free it here.
*/
if (ib_conn) {
- ib_conn->iser_conn = NULL;
+ ib_conn->iscsi_conn = NULL;
iser_conn_put(ib_conn, 1); /* deref iscsi/ib conn unbinding */
}
}
@@ -346,7 +361,6 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
int is_leading)
{
struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_iser_conn *iser_conn;
struct iscsi_session *session;
struct iser_conn *ib_conn;
struct iscsi_endpoint *ep;
@@ -373,11 +387,11 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
/* binds the iSER connection retrieved from the previously
* connected ep_handle to the iSCSI layer connection. exchanges
* connection pointers */
- iser_info("binding iscsi/iser conn %p %p to ib_conn %p\n",
- conn, conn->dd_data, ib_conn);
- iser_conn = conn->dd_data;
- ib_conn->iser_conn = iser_conn;
- iser_conn->ib_conn = ib_conn;
+ iser_info("binding iscsi conn %p to ib_conn %p\n", conn, ib_conn);
+
+ conn->dd_data = ib_conn;
+ ib_conn->iscsi_conn = conn;
+
iser_conn_get(ib_conn); /* ref iscsi/ib conn binding */
return 0;
}
@@ -386,8 +400,7 @@ static void
iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
{
struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_iser_conn *iser_conn = conn->dd_data;
- struct iser_conn *ib_conn = iser_conn->ib_conn;
+ struct iser_conn *ib_conn = conn->dd_data;
/*
* Userspace may have goofed up and not bound the connection or
@@ -401,7 +414,7 @@ iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
*/
iser_conn_put(ib_conn, 1); /* deref iscsi/ib conn unbinding */
}
- iser_conn->ib_conn = NULL;
+ conn->dd_data = NULL;
}
static void iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session)
@@ -413,6 +426,17 @@ static void iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session)
iscsi_host_free(shost);
}
+static inline unsigned int
+iser_dif_prot_caps(int prot_caps)
+{
+ return ((prot_caps & IB_PROT_T10DIF_TYPE_1) ? SHOST_DIF_TYPE1_PROTECTION |
+ SHOST_DIX_TYPE1_PROTECTION : 0) |
+ ((prot_caps & IB_PROT_T10DIF_TYPE_2) ? SHOST_DIF_TYPE2_PROTECTION |
+ SHOST_DIX_TYPE2_PROTECTION : 0) |
+ ((prot_caps & IB_PROT_T10DIF_TYPE_3) ? SHOST_DIF_TYPE3_PROTECTION |
+ SHOST_DIX_TYPE3_PROTECTION : 0);
+}
+
static struct iscsi_cls_session *
iscsi_iser_session_create(struct iscsi_endpoint *ep,
uint16_t cmds_max, uint16_t qdepth,
@@ -437,8 +461,18 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep,
* older userspace tools (before 2.0-870) did not pass us
* the leading conn's ep so this will be NULL;
*/
- if (ep)
+ if (ep) {
ib_conn = ep->dd_data;
+ if (ib_conn->pi_support) {
+ u32 sig_caps = ib_conn->device->dev_attr.sig_prot_cap;
+
+ scsi_host_set_prot(shost, iser_dif_prot_caps(sig_caps));
+ if (iser_pi_guard)
+ scsi_host_set_guard(shost, SHOST_DIX_GUARD_IP);
+ else
+ scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
+ }
+ }
if (iscsi_host_add(shost,
ep ? ib_conn->device->ib_device->dma_device : NULL))
@@ -618,7 +652,7 @@ iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
struct iser_conn *ib_conn;
ib_conn = ep->dd_data;
- if (ib_conn->iser_conn)
+ if (ib_conn->iscsi_conn)
/*
* Must suspend xmit path if the ep is bound to the
* iscsi_conn, so we know we are not accessing the ib_conn
@@ -626,7 +660,7 @@ iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
*
* This may not be bound if the ep poll failed.
*/
- iscsi_suspend_tx(ib_conn->iser_conn->iscsi_conn);
+ iscsi_suspend_tx(ib_conn->iscsi_conn);
iser_info("ib conn %p state %d\n", ib_conn, ib_conn->state);
@@ -732,6 +766,7 @@ static struct iscsi_transport iscsi_iser_transport = {
.xmit_task = iscsi_iser_task_xmit,
.cleanup_task = iscsi_iser_cleanup_task,
.alloc_pdu = iscsi_iser_pdu_alloc,
+ .check_protection = iscsi_iser_check_protection,
/* recovery */
.session_recovery_timedout = iscsi_session_recovery_timedout,
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index 67914027c614..324129f80d40 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -8,7 +8,7 @@
*
* Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved.
* Copyright (c) 2005, 2006 Cisco Systems. All rights reserved.
- * Copyright (c) 2013 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2013-2014 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
@@ -46,6 +46,8 @@
#include <linux/printk.h>
#include <scsi/libiscsi.h>
#include <scsi/scsi_transport_iscsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
@@ -67,7 +69,7 @@
#define DRV_NAME "iser"
#define PFX DRV_NAME ": "
-#define DRV_VER "1.1"
+#define DRV_VER "1.3"
#define iser_dbg(fmt, arg...) \
do { \
@@ -134,10 +136,21 @@
ISER_MAX_TX_MISC_PDUS + \
ISER_MAX_RX_MISC_PDUS)
+/* Max registration work requests per command */
+#define ISER_MAX_REG_WR_PER_CMD 5
+
+/* For Signature we don't support DATAOUTs so no need to make room for them */
+#define ISER_QP_SIG_MAX_REQ_DTOS (ISER_DEF_XMIT_CMDS_MAX * \
+ (1 + ISER_MAX_REG_WR_PER_CMD) + \
+ ISER_MAX_TX_MISC_PDUS + \
+ ISER_MAX_RX_MISC_PDUS)
+
#define ISER_VER 0x10
#define ISER_WSV 0x08
#define ISER_RSV 0x04
+#define ISER_FASTREG_LI_WRID 0xffffffffffffffffULL
+
struct iser_hdr {
u8 flags;
u8 rsvd[3];
@@ -201,7 +214,6 @@ struct iser_data_buf {
/* fwd declarations */
struct iser_device;
struct iser_cq_desc;
-struct iscsi_iser_conn;
struct iscsi_iser_task;
struct iscsi_endpoint;
@@ -258,6 +270,7 @@ struct iscsi_iser_task;
struct iser_device {
struct ib_device *ib_device;
struct ib_pd *pd;
+ struct ib_device_attr dev_attr;
struct ib_cq *rx_cq[ISER_MAX_CQ];
struct ib_cq *tx_cq[ISER_MAX_CQ];
struct ib_mr *mr;
@@ -277,17 +290,35 @@ struct iser_device {
enum iser_data_dir cmd_dir);
};
+#define ISER_CHECK_GUARD 0xc0
+#define ISER_CHECK_REFTAG 0x0f
+#define ISER_CHECK_APPTAG 0x30
+
+enum iser_reg_indicator {
+ ISER_DATA_KEY_VALID = 1 << 0,
+ ISER_PROT_KEY_VALID = 1 << 1,
+ ISER_SIG_KEY_VALID = 1 << 2,
+ ISER_FASTREG_PROTECTED = 1 << 3,
+};
+
+struct iser_pi_context {
+ struct ib_mr *prot_mr;
+ struct ib_fast_reg_page_list *prot_frpl;
+ struct ib_mr *sig_mr;
+};
+
struct fast_reg_descriptor {
struct list_head list;
/* For fast registration - FRWR */
struct ib_mr *data_mr;
struct ib_fast_reg_page_list *data_frpl;
- /* Valid for fast registration flag */
- bool valid;
+ struct iser_pi_context *pi_ctx;
+ /* registration indicators container */
+ u8 reg_indicators;
};
struct iser_conn {
- struct iscsi_iser_conn *iser_conn; /* iser conn for upcalls */
+ struct iscsi_conn *iscsi_conn;
struct iscsi_endpoint *ep;
enum iser_ib_conn_state state; /* rdma connection state */
atomic_t refcount;
@@ -310,6 +341,9 @@ struct iser_conn {
unsigned int rx_desc_head;
struct iser_rx_desc *rx_descs;
struct ib_recv_wr rx_wr[ISER_MIN_POSTED_RX];
+ bool pi_support;
+
+ /* Connection memory registration pool */
union {
struct {
struct ib_fmr_pool *pool; /* pool of IB FMRs */
@@ -319,24 +353,22 @@ struct iser_conn {
struct {
struct list_head pool;
int pool_size;
- } frwr;
- } fastreg;
-};
-
-struct iscsi_iser_conn {
- struct iscsi_conn *iscsi_conn;/* ptr to iscsi conn */
- struct iser_conn *ib_conn; /* iSER IB conn */
+ } fastreg;
+ };
};
struct iscsi_iser_task {
struct iser_tx_desc desc;
- struct iscsi_iser_conn *iser_conn;
+ struct iser_conn *ib_conn;
enum iser_task_status status;
+ struct scsi_cmnd *sc;
int command_sent; /* set if command sent */
int dir[ISER_DIRS_NUM]; /* set if dir use*/
struct iser_regd_buf rdma_regd[ISER_DIRS_NUM];/* regd rdma buf */
struct iser_data_buf data[ISER_DIRS_NUM]; /* orig. data des*/
struct iser_data_buf data_copy[ISER_DIRS_NUM];/* contig. copy */
+ struct iser_data_buf prot[ISER_DIRS_NUM]; /* prot desc */
+ struct iser_data_buf prot_copy[ISER_DIRS_NUM];/* prot copy */
};
struct iser_page_vec {
@@ -362,6 +394,8 @@ struct iser_global {
extern struct iser_global ig;
extern int iser_debug_level;
+extern bool iser_pi_enable;
+extern int iser_pi_guard;
/* allocate connection resources needed for rdma functionality */
int iser_conn_set_full_featured_mode(struct iscsi_conn *conn);
@@ -401,13 +435,15 @@ void iser_task_rdma_finalize(struct iscsi_iser_task *task);
void iser_free_rx_descriptors(struct iser_conn *ib_conn);
-void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *task,
- enum iser_data_dir cmd_dir);
+void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
+ struct iser_data_buf *mem,
+ struct iser_data_buf *mem_copy,
+ enum iser_data_dir cmd_dir);
int iser_reg_rdma_mem_fmr(struct iscsi_iser_task *task,
enum iser_data_dir cmd_dir);
-int iser_reg_rdma_mem_frwr(struct iscsi_iser_task *task,
- enum iser_data_dir cmd_dir);
+int iser_reg_rdma_mem_fastreg(struct iscsi_iser_task *task,
+ enum iser_data_dir cmd_dir);
int iser_connect(struct iser_conn *ib_conn,
struct sockaddr_in *src_addr,
@@ -420,8 +456,8 @@ int iser_reg_page_vec(struct iser_conn *ib_conn,
void iser_unreg_mem_fmr(struct iscsi_iser_task *iser_task,
enum iser_data_dir cmd_dir);
-void iser_unreg_mem_frwr(struct iscsi_iser_task *iser_task,
- enum iser_data_dir cmd_dir);
+void iser_unreg_mem_fastreg(struct iscsi_iser_task *iser_task,
+ enum iser_data_dir cmd_dir);
int iser_post_recvl(struct iser_conn *ib_conn);
int iser_post_recvm(struct iser_conn *ib_conn, int count);
@@ -432,12 +468,15 @@ int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
enum iser_data_dir iser_dir,
enum dma_data_direction dma_dir);
-void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task);
+void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task,
+ struct iser_data_buf *data);
int iser_initialize_task_headers(struct iscsi_task *task,
struct iser_tx_desc *tx_desc);
int iser_alloc_rx_descriptors(struct iser_conn *ib_conn, struct iscsi_session *session);
int iser_create_fmr_pool(struct iser_conn *ib_conn, unsigned cmds_max);
void iser_free_fmr_pool(struct iser_conn *ib_conn);
-int iser_create_frwr_pool(struct iser_conn *ib_conn, unsigned cmds_max);
-void iser_free_frwr_pool(struct iser_conn *ib_conn);
+int iser_create_fastreg_pool(struct iser_conn *ib_conn, unsigned cmds_max);
+void iser_free_fastreg_pool(struct iser_conn *ib_conn);
+u8 iser_check_task_pi_status(struct iscsi_iser_task *iser_task,
+ enum iser_data_dir cmd_dir, sector_t *sector);
#endif
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index 334f34b1cd46..2e2d903db838 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved.
- * Copyright (c) 2013 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2013-2014 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
@@ -49,7 +49,7 @@ static int iser_prepare_read_cmd(struct iscsi_task *task,
{
struct iscsi_iser_task *iser_task = task->dd_data;
- struct iser_device *device = iser_task->iser_conn->ib_conn->device;
+ struct iser_device *device = iser_task->ib_conn->device;
struct iser_regd_buf *regd_buf;
int err;
struct iser_hdr *hdr = &iser_task->desc.iser_header;
@@ -62,11 +62,22 @@ static int iser_prepare_read_cmd(struct iscsi_task *task,
if (err)
return err;
+ if (scsi_prot_sg_count(iser_task->sc)) {
+ struct iser_data_buf *pbuf_in = &iser_task->prot[ISER_DIR_IN];
+
+ err = iser_dma_map_task_data(iser_task,
+ pbuf_in,
+ ISER_DIR_IN,
+ DMA_FROM_DEVICE);
+ if (err)
+ return err;
+ }
+
if (edtl > iser_task->data[ISER_DIR_IN].data_len) {
iser_err("Total data length: %ld, less than EDTL: "
"%d, in READ cmd BHS itt: %d, conn: 0x%p\n",
iser_task->data[ISER_DIR_IN].data_len, edtl,
- task->itt, iser_task->iser_conn);
+ task->itt, iser_task->ib_conn);
return -EINVAL;
}
@@ -99,7 +110,7 @@ iser_prepare_write_cmd(struct iscsi_task *task,
unsigned int edtl)
{
struct iscsi_iser_task *iser_task = task->dd_data;
- struct iser_device *device = iser_task->iser_conn->ib_conn->device;
+ struct iser_device *device = iser_task->ib_conn->device;
struct iser_regd_buf *regd_buf;
int err;
struct iser_hdr *hdr = &iser_task->desc.iser_header;
@@ -113,6 +124,17 @@ iser_prepare_write_cmd(struct iscsi_task *task,
if (err)
return err;
+ if (scsi_prot_sg_count(iser_task->sc)) {
+ struct iser_data_buf *pbuf_out = &iser_task->prot[ISER_DIR_OUT];
+
+ err = iser_dma_map_task_data(iser_task,
+ pbuf_out,
+ ISER_DIR_OUT,
+ DMA_TO_DEVICE);
+ if (err)
+ return err;
+ }
+
if (edtl > iser_task->data[ISER_DIR_OUT].data_len) {
iser_err("Total data length: %ld, less than EDTL: %d, "
"in WRITE cmd BHS itt: %d, conn: 0x%p\n",
@@ -327,7 +349,7 @@ free_login_buf:
static int iser_post_rx_bufs(struct iscsi_conn *conn, struct iscsi_hdr *req)
{
- struct iscsi_iser_conn *iser_conn = conn->dd_data;
+ struct iser_conn *ib_conn = conn->dd_data;
struct iscsi_session *session = conn->session;
iser_dbg("req op %x flags %x\n", req->opcode, req->flags);
@@ -340,19 +362,18 @@ static int iser_post_rx_bufs(struct iscsi_conn *conn, struct iscsi_hdr *req)
* response) and no posted send buffers left - they must have been
* consumed during previous login phases.
*/
- WARN_ON(iser_conn->ib_conn->post_recv_buf_count != 1);
- WARN_ON(atomic_read(&iser_conn->ib_conn->post_send_buf_count) != 0);
+ WARN_ON(ib_conn->post_recv_buf_count != 1);
+ WARN_ON(atomic_read(&ib_conn->post_send_buf_count) != 0);
if (session->discovery_sess) {
iser_info("Discovery session, re-using login RX buffer\n");
return 0;
} else
iser_info("Normal session, posting batch of RX %d buffers\n",
- iser_conn->ib_conn->min_posted_rx);
+ ib_conn->min_posted_rx);
/* Initial post receive buffers */
- if (iser_post_recvm(iser_conn->ib_conn,
- iser_conn->ib_conn->min_posted_rx))
+ if (iser_post_recvm(ib_conn, ib_conn->min_posted_rx))
return -ENOMEM;
return 0;
@@ -364,11 +385,11 @@ static int iser_post_rx_bufs(struct iscsi_conn *conn, struct iscsi_hdr *req)
int iser_send_command(struct iscsi_conn *conn,
struct iscsi_task *task)
{
- struct iscsi_iser_conn *iser_conn = conn->dd_data;
+ struct iser_conn *ib_conn = conn->dd_data;
struct iscsi_iser_task *iser_task = task->dd_data;
unsigned long edtl;
int err;
- struct iser_data_buf *data_buf;
+ struct iser_data_buf *data_buf, *prot_buf;
struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)task->hdr;
struct scsi_cmnd *sc = task->sc;
struct iser_tx_desc *tx_desc = &iser_task->desc;
@@ -377,20 +398,28 @@ int iser_send_command(struct iscsi_conn *conn,
/* build the tx desc regd header and add it to the tx desc dto */
tx_desc->type = ISCSI_TX_SCSI_COMMAND;
- iser_create_send_desc(iser_conn->ib_conn, tx_desc);
+ iser_create_send_desc(ib_conn, tx_desc);
- if (hdr->flags & ISCSI_FLAG_CMD_READ)
+ if (hdr->flags & ISCSI_FLAG_CMD_READ) {
data_buf = &iser_task->data[ISER_DIR_IN];
- else
+ prot_buf = &iser_task->prot[ISER_DIR_IN];
+ } else {
data_buf = &iser_task->data[ISER_DIR_OUT];
+ prot_buf = &iser_task->prot[ISER_DIR_OUT];
+ }
if (scsi_sg_count(sc)) { /* using a scatter list */
data_buf->buf = scsi_sglist(sc);
data_buf->size = scsi_sg_count(sc);
}
-
data_buf->data_len = scsi_bufflen(sc);
+ if (scsi_prot_sg_count(sc)) {
+ prot_buf->buf = scsi_prot_sglist(sc);
+ prot_buf->size = scsi_prot_sg_count(sc);
+ prot_buf->data_len = sc->prot_sdb->length;
+ }
+
if (hdr->flags & ISCSI_FLAG_CMD_READ) {
err = iser_prepare_read_cmd(task, edtl);
if (err)
@@ -408,7 +437,7 @@ int iser_send_command(struct iscsi_conn *conn,
iser_task->status = ISER_TASK_STATUS_STARTED;
- err = iser_post_send(iser_conn->ib_conn, tx_desc);
+ err = iser_post_send(ib_conn, tx_desc);
if (!err)
return 0;
@@ -424,7 +453,7 @@ int iser_send_data_out(struct iscsi_conn *conn,
struct iscsi_task *task,
struct iscsi_data *hdr)
{
- struct iscsi_iser_conn *iser_conn = conn->dd_data;
+ struct iser_conn *ib_conn = conn->dd_data;
struct iscsi_iser_task *iser_task = task->dd_data;
struct iser_tx_desc *tx_desc = NULL;
struct iser_regd_buf *regd_buf;
@@ -473,7 +502,7 @@ int iser_send_data_out(struct iscsi_conn *conn,
itt, buf_offset, data_seg_len);
- err = iser_post_send(iser_conn->ib_conn, tx_desc);
+ err = iser_post_send(ib_conn, tx_desc);
if (!err)
return 0;
@@ -486,19 +515,18 @@ send_data_out_error:
int iser_send_control(struct iscsi_conn *conn,
struct iscsi_task *task)
{
- struct iscsi_iser_conn *iser_conn = conn->dd_data;
+ struct iser_conn *ib_conn = conn->dd_data;
struct iscsi_iser_task *iser_task = task->dd_data;
struct iser_tx_desc *mdesc = &iser_task->desc;
unsigned long data_seg_len;
int err = 0;
struct iser_device *device;
- struct iser_conn *ib_conn = iser_conn->ib_conn;
/* build the tx desc regd header and add it to the tx desc dto */
mdesc->type = ISCSI_TX_CONTROL;
- iser_create_send_desc(iser_conn->ib_conn, mdesc);
+ iser_create_send_desc(ib_conn, mdesc);
- device = iser_conn->ib_conn->device;
+ device = ib_conn->device;
data_seg_len = ntoh24(task->hdr->dlength);
@@ -513,14 +541,13 @@ int iser_send_control(struct iscsi_conn *conn,
ib_conn->login_req_dma, task->data_count,
DMA_TO_DEVICE);
- memcpy(iser_conn->ib_conn->login_req_buf, task->data,
- task->data_count);
+ memcpy(ib_conn->login_req_buf, task->data, task->data_count);
ib_dma_sync_single_for_device(device->ib_device,
ib_conn->login_req_dma, task->data_count,
DMA_TO_DEVICE);
- tx_dsg->addr = iser_conn->ib_conn->login_req_dma;
+ tx_dsg->addr = ib_conn->login_req_dma;
tx_dsg->length = task->data_count;
tx_dsg->lkey = device->mr->lkey;
mdesc->num_sge = 2;
@@ -529,7 +556,7 @@ int iser_send_control(struct iscsi_conn *conn,
if (task == conn->login_task) {
iser_dbg("op %x dsl %lx, posting login rx buffer\n",
task->hdr->opcode, data_seg_len);
- err = iser_post_recvl(iser_conn->ib_conn);
+ err = iser_post_recvl(ib_conn);
if (err)
goto send_control_error;
err = iser_post_rx_bufs(conn, task->hdr);
@@ -537,7 +564,7 @@ int iser_send_control(struct iscsi_conn *conn,
goto send_control_error;
}
- err = iser_post_send(iser_conn->ib_conn, mdesc);
+ err = iser_post_send(ib_conn, mdesc);
if (!err)
return 0;
@@ -553,7 +580,6 @@ void iser_rcv_completion(struct iser_rx_desc *rx_desc,
unsigned long rx_xfer_len,
struct iser_conn *ib_conn)
{
- struct iscsi_iser_conn *conn = ib_conn->iser_conn;
struct iscsi_hdr *hdr;
u64 rx_dma;
int rx_buflen, outstanding, count, err;
@@ -575,17 +601,17 @@ void iser_rcv_completion(struct iser_rx_desc *rx_desc,
iser_dbg("op 0x%x itt 0x%x dlen %d\n", hdr->opcode,
hdr->itt, (int)(rx_xfer_len - ISER_HEADERS_LEN));
- iscsi_iser_recv(conn->iscsi_conn, hdr,
- rx_desc->data, rx_xfer_len - ISER_HEADERS_LEN);
+ iscsi_iser_recv(ib_conn->iscsi_conn, hdr, rx_desc->data,
+ rx_xfer_len - ISER_HEADERS_LEN);
ib_dma_sync_single_for_device(ib_conn->device->ib_device, rx_dma,
- rx_buflen, DMA_FROM_DEVICE);
+ rx_buflen, DMA_FROM_DEVICE);
/* decrementing conn->post_recv_buf_count only --after-- freeing the *
* task eliminates the need to worry on tasks which are completed in *
* parallel to the execution of iser_conn_term. So the code that waits *
* for the posted rx bufs refcount to become zero handles everything */
- conn->ib_conn->post_recv_buf_count--;
+ ib_conn->post_recv_buf_count--;
if (rx_dma == ib_conn->login_resp_dma)
return;
@@ -635,6 +661,9 @@ void iser_task_rdma_init(struct iscsi_iser_task *iser_task)
iser_task->data[ISER_DIR_IN].data_len = 0;
iser_task->data[ISER_DIR_OUT].data_len = 0;
+ iser_task->prot[ISER_DIR_IN].data_len = 0;
+ iser_task->prot[ISER_DIR_OUT].data_len = 0;
+
memset(&iser_task->rdma_regd[ISER_DIR_IN], 0,
sizeof(struct iser_regd_buf));
memset(&iser_task->rdma_regd[ISER_DIR_OUT], 0,
@@ -643,28 +672,63 @@ void iser_task_rdma_init(struct iscsi_iser_task *iser_task)
void iser_task_rdma_finalize(struct iscsi_iser_task *iser_task)
{
- struct iser_device *device = iser_task->iser_conn->ib_conn->device;
- int is_rdma_aligned = 1;
+ struct iser_device *device = iser_task->ib_conn->device;
+ int is_rdma_data_aligned = 1;
+ int is_rdma_prot_aligned = 1;
+ int prot_count = scsi_prot_sg_count(iser_task->sc);
/* if we were reading, copy back to unaligned sglist,
* anyway dma_unmap and free the copy
*/
if (iser_task->data_copy[ISER_DIR_IN].copy_buf != NULL) {
- is_rdma_aligned = 0;
- iser_finalize_rdma_unaligned_sg(iser_task, ISER_DIR_IN);
+ is_rdma_data_aligned = 0;
+ iser_finalize_rdma_unaligned_sg(iser_task,
+ &iser_task->data[ISER_DIR_IN],
+ &iser_task->data_copy[ISER_DIR_IN],
+ ISER_DIR_IN);
}
+
if (iser_task->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
- is_rdma_aligned = 0;
- iser_finalize_rdma_unaligned_sg(iser_task, ISER_DIR_OUT);
+ is_rdma_data_aligned = 0;
+ iser_finalize_rdma_unaligned_sg(iser_task,
+ &iser_task->data[ISER_DIR_OUT],
+ &iser_task->data_copy[ISER_DIR_OUT],
+ ISER_DIR_OUT);
+ }
+
+ if (iser_task->prot_copy[ISER_DIR_IN].copy_buf != NULL) {
+ is_rdma_prot_aligned = 0;
+ iser_finalize_rdma_unaligned_sg(iser_task,
+ &iser_task->prot[ISER_DIR_IN],
+ &iser_task->prot_copy[ISER_DIR_IN],
+ ISER_DIR_IN);
+ }
+
+ if (iser_task->prot_copy[ISER_DIR_OUT].copy_buf != NULL) {
+ is_rdma_prot_aligned = 0;
+ iser_finalize_rdma_unaligned_sg(iser_task,
+ &iser_task->prot[ISER_DIR_OUT],
+ &iser_task->prot_copy[ISER_DIR_OUT],
+ ISER_DIR_OUT);
}
- if (iser_task->dir[ISER_DIR_IN])
+ if (iser_task->dir[ISER_DIR_IN]) {
device->iser_unreg_rdma_mem(iser_task, ISER_DIR_IN);
+ if (is_rdma_data_aligned)
+ iser_dma_unmap_task_data(iser_task,
+ &iser_task->data[ISER_DIR_IN]);
+ if (prot_count && is_rdma_prot_aligned)
+ iser_dma_unmap_task_data(iser_task,
+ &iser_task->prot[ISER_DIR_IN]);
+ }
- if (iser_task->dir[ISER_DIR_OUT])
+ if (iser_task->dir[ISER_DIR_OUT]) {
device->iser_unreg_rdma_mem(iser_task, ISER_DIR_OUT);
-
- /* if the data was unaligned, it was already unmapped and then copied */
- if (is_rdma_aligned)
- iser_dma_unmap_task_data(iser_task);
+ if (is_rdma_data_aligned)
+ iser_dma_unmap_task_data(iser_task,
+ &iser_task->data[ISER_DIR_OUT]);
+ if (prot_count && is_rdma_prot_aligned)
+ iser_dma_unmap_task_data(iser_task,
+ &iser_task->prot[ISER_DIR_OUT]);
+ }
}
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
index 1ce0c97d2ccb..47acd3ad3a17 100644
--- a/drivers/infiniband/ulp/iser/iser_memory.c
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved.
- * Copyright (c) 2013 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2013-2014 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
@@ -45,13 +45,19 @@
* iser_start_rdma_unaligned_sg
*/
static int iser_start_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
+ struct iser_data_buf *data,
+ struct iser_data_buf *data_copy,
enum iser_data_dir cmd_dir)
{
- int dma_nents;
- struct ib_device *dev;
+ struct ib_device *dev = iser_task->ib_conn->device->ib_device;
+ struct scatterlist *sgl = (struct scatterlist *)data->buf;
+ struct scatterlist *sg;
char *mem = NULL;
- struct iser_data_buf *data = &iser_task->data[cmd_dir];
- unsigned long cmd_data_len = data->data_len;
+ unsigned long cmd_data_len = 0;
+ int dma_nents, i;
+
+ for_each_sg(sgl, sg, data->size, i)
+ cmd_data_len += ib_sg_dma_len(dev, sg);
if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
mem = (void *)__get_free_pages(GFP_ATOMIC,
@@ -61,17 +67,16 @@ static int iser_start_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
if (mem == NULL) {
iser_err("Failed to allocate mem size %d %d for copying sglist\n",
- data->size,(int)cmd_data_len);
+ data->size, (int)cmd_data_len);
return -ENOMEM;
}
if (cmd_dir == ISER_DIR_OUT) {
/* copy the unaligned sg the buffer which is used for RDMA */
- struct scatterlist *sgl = (struct scatterlist *)data->buf;
- struct scatterlist *sg;
int i;
char *p, *from;
+ sgl = (struct scatterlist *)data->buf;
p = mem;
for_each_sg(sgl, sg, data->size, i) {
from = kmap_atomic(sg_page(sg));
@@ -83,39 +88,37 @@ static int iser_start_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
}
}
- sg_init_one(&iser_task->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
- iser_task->data_copy[cmd_dir].buf =
- &iser_task->data_copy[cmd_dir].sg_single;
- iser_task->data_copy[cmd_dir].size = 1;
+ sg_init_one(&data_copy->sg_single, mem, cmd_data_len);
+ data_copy->buf = &data_copy->sg_single;
+ data_copy->size = 1;
+ data_copy->copy_buf = mem;
- iser_task->data_copy[cmd_dir].copy_buf = mem;
-
- dev = iser_task->iser_conn->ib_conn->device->ib_device;
- dma_nents = ib_dma_map_sg(dev,
- &iser_task->data_copy[cmd_dir].sg_single,
- 1,
+ dma_nents = ib_dma_map_sg(dev, &data_copy->sg_single, 1,
(cmd_dir == ISER_DIR_OUT) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE);
BUG_ON(dma_nents == 0);
- iser_task->data_copy[cmd_dir].dma_nents = dma_nents;
+ data_copy->dma_nents = dma_nents;
+ data_copy->data_len = cmd_data_len;
+
return 0;
}
/**
* iser_finalize_rdma_unaligned_sg
*/
+
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
- enum iser_data_dir cmd_dir)
+ struct iser_data_buf *data,
+ struct iser_data_buf *data_copy,
+ enum iser_data_dir cmd_dir)
{
struct ib_device *dev;
- struct iser_data_buf *mem_copy;
unsigned long cmd_data_len;
- dev = iser_task->iser_conn->ib_conn->device->ib_device;
- mem_copy = &iser_task->data_copy[cmd_dir];
+ dev = iser_task->ib_conn->device->ib_device;
- ib_dma_unmap_sg(dev, &mem_copy->sg_single, 1,
+ ib_dma_unmap_sg(dev, &data_copy->sg_single, 1,
(cmd_dir == ISER_DIR_OUT) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE);
@@ -127,10 +130,10 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
int i;
/* copy back read RDMA to unaligned sg */
- mem = mem_copy->copy_buf;
+ mem = data_copy->copy_buf;
- sgl = (struct scatterlist *)iser_task->data[ISER_DIR_IN].buf;
- sg_size = iser_task->data[ISER_DIR_IN].size;
+ sgl = (struct scatterlist *)data->buf;
+ sg_size = data->size;
p = mem;
for_each_sg(sgl, sg, sg_size, i) {
@@ -143,15 +146,15 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
}
}
- cmd_data_len = iser_task->data[cmd_dir].data_len;
+ cmd_data_len = data->data_len;
if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
- free_pages((unsigned long)mem_copy->copy_buf,
+ free_pages((unsigned long)data_copy->copy_buf,
ilog2(roundup_pow_of_two(cmd_data_len)) - PAGE_SHIFT);
else
- kfree(mem_copy->copy_buf);
+ kfree(data_copy->copy_buf);
- mem_copy->copy_buf = NULL;
+ data_copy->copy_buf = NULL;
}
#define IS_4K_ALIGNED(addr) ((((unsigned long)addr) & ~MASK_4K) == 0)
@@ -319,7 +322,7 @@ int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
struct ib_device *dev;
iser_task->dir[iser_dir] = 1;
- dev = iser_task->iser_conn->ib_conn->device->ib_device;
+ dev = iser_task->ib_conn->device->ib_device;
data->dma_nents = ib_dma_map_sg(dev, data->buf, data->size, dma_dir);
if (data->dma_nents == 0) {
@@ -329,31 +332,23 @@ int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
return 0;
}
-void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task)
+void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task,
+ struct iser_data_buf *data)
{
struct ib_device *dev;
- struct iser_data_buf *data;
- dev = iser_task->iser_conn->ib_conn->device->ib_device;
-
- if (iser_task->dir[ISER_DIR_IN]) {
- data = &iser_task->data[ISER_DIR_IN];
- ib_dma_unmap_sg(dev, data->buf, data->size, DMA_FROM_DEVICE);
- }
-
- if (iser_task->dir[ISER_DIR_OUT]) {
- data = &iser_task->data[ISER_DIR_OUT];
- ib_dma_unmap_sg(dev, data->buf, data->size, DMA_TO_DEVICE);
- }
+ dev = iser_task->ib_conn->device->ib_device;
+ ib_dma_unmap_sg(dev, data->buf, data->size, DMA_FROM_DEVICE);
}
static int fall_to_bounce_buf(struct iscsi_iser_task *iser_task,
struct ib_device *ibdev,
+ struct iser_data_buf *mem,
+ struct iser_data_buf *mem_copy,
enum iser_data_dir cmd_dir,
int aligned_len)
{
- struct iscsi_conn *iscsi_conn = iser_task->iser_conn->iscsi_conn;
- struct iser_data_buf *mem = &iser_task->data[cmd_dir];
+ struct iscsi_conn *iscsi_conn = iser_task->ib_conn->iscsi_conn;
iscsi_conn->fmr_unalign_cnt++;
iser_warn("rdma alignment violation (%d/%d aligned) or FMR not supported\n",
@@ -363,12 +358,12 @@ static int fall_to_bounce_buf(struct iscsi_iser_task *iser_task,
iser_data_buf_dump(mem, ibdev);
/* unmap the command data before accessing it */
- iser_dma_unmap_task_data(iser_task);
+ iser_dma_unmap_task_data(iser_task, mem);
/* allocate copy buf, if we are writing, copy the */
/* unaligned scatterlist, dma map the copy */
- if (iser_start_rdma_unaligned_sg(iser_task, cmd_dir) != 0)
- return -ENOMEM;
+ if (iser_start_rdma_unaligned_sg(iser_task, mem, mem_copy, cmd_dir) != 0)
+ return -ENOMEM;
return 0;
}
@@ -382,7 +377,7 @@ static int fall_to_bounce_buf(struct iscsi_iser_task *iser_task,
int iser_reg_rdma_mem_fmr(struct iscsi_iser_task *iser_task,
enum iser_data_dir cmd_dir)
{
- struct iser_conn *ib_conn = iser_task->iser_conn->ib_conn;
+ struct iser_conn *ib_conn = iser_task->ib_conn;
struct iser_device *device = ib_conn->device;
struct ib_device *ibdev = device->ib_device;
struct iser_data_buf *mem = &iser_task->data[cmd_dir];
@@ -396,7 +391,8 @@ int iser_reg_rdma_mem_fmr(struct iscsi_iser_task *iser_task,
aligned_len = iser_data_buf_aligned_len(mem, ibdev);
if (aligned_len != mem->dma_nents) {
- err = fall_to_bounce_buf(iser_task, ibdev,
+ err = fall_to_bounce_buf(iser_task, ibdev, mem,
+ &iser_task->data_copy[cmd_dir],
cmd_dir, aligned_len);
if (err) {
iser_err("failed to allocate bounce buffer\n");
@@ -422,8 +418,8 @@ int iser_reg_rdma_mem_fmr(struct iscsi_iser_task *iser_task,
(unsigned long)regd_buf->reg.va,
(unsigned long)regd_buf->reg.len);
} else { /* use FMR for multiple dma entries */
- iser_page_vec_build(mem, ib_conn->fastreg.fmr.page_vec, ibdev);
- err = iser_reg_page_vec(ib_conn, ib_conn->fastreg.fmr.page_vec,
+ iser_page_vec_build(mem, ib_conn->fmr.page_vec, ibdev);
+ err = iser_reg_page_vec(ib_conn, ib_conn->fmr.page_vec,
&regd_buf->reg);
if (err && err != -EAGAIN) {
iser_data_buf_dump(mem, ibdev);
@@ -431,12 +427,12 @@ int iser_reg_rdma_mem_fmr(struct iscsi_iser_task *iser_task,
mem->dma_nents,
ntoh24(iser_task->desc.iscsi_header.dlength));
iser_err("page_vec: data_size = 0x%x, length = %d, offset = 0x%x\n",
- ib_conn->fastreg.fmr.page_vec->data_size,
- ib_conn->fastreg.fmr.page_vec->length,
- ib_conn->fastreg.fmr.page_vec->offset);
- for (i = 0; i < ib_conn->fastreg.fmr.page_vec->length; i++)
+ ib_conn->fmr.page_vec->data_size,
+ ib_conn->fmr.page_vec->length,
+ ib_conn->fmr.page_vec->offset);
+ for (i = 0; i < ib_conn->fmr.page_vec->length; i++)
iser_err("page_vec[%d] = 0x%llx\n", i,
- (unsigned long long) ib_conn->fastreg.fmr.page_vec->pages[i]);
+ (unsigned long long) ib_conn->fmr.page_vec->pages[i]);
}
if (err)
return err;
@@ -444,94 +440,280 @@ int iser_reg_rdma_mem_fmr(struct iscsi_iser_task *iser_task,
return 0;
}
-static int iser_fast_reg_mr(struct fast_reg_descriptor *desc,
- struct iser_conn *ib_conn,
+static inline enum ib_t10_dif_type
+scsi2ib_prot_type(unsigned char prot_type)
+{
+ switch (prot_type) {
+ case SCSI_PROT_DIF_TYPE0:
+ return IB_T10DIF_NONE;
+ case SCSI_PROT_DIF_TYPE1:
+ return IB_T10DIF_TYPE1;
+ case SCSI_PROT_DIF_TYPE2:
+ return IB_T10DIF_TYPE2;
+ case SCSI_PROT_DIF_TYPE3:
+ return IB_T10DIF_TYPE3;
+ default:
+ return IB_T10DIF_NONE;
+ }
+}
+
+
+static int
+iser_set_sig_attrs(struct scsi_cmnd *sc, struct ib_sig_attrs *sig_attrs)
+{
+ unsigned char scsi_ptype = scsi_get_prot_type(sc);
+
+ sig_attrs->mem.sig_type = IB_SIG_TYPE_T10_DIF;
+ sig_attrs->wire.sig_type = IB_SIG_TYPE_T10_DIF;
+ sig_attrs->mem.sig.dif.pi_interval = sc->device->sector_size;
+ sig_attrs->wire.sig.dif.pi_interval = sc->device->sector_size;
+
+ switch (scsi_get_prot_op(sc)) {
+ case SCSI_PROT_WRITE_INSERT:
+ case SCSI_PROT_READ_STRIP:
+ sig_attrs->mem.sig.dif.type = IB_T10DIF_NONE;
+ sig_attrs->wire.sig.dif.type = scsi2ib_prot_type(scsi_ptype);
+ sig_attrs->wire.sig.dif.bg_type = IB_T10DIF_CRC;
+ sig_attrs->wire.sig.dif.ref_tag = scsi_get_lba(sc) &
+ 0xffffffff;
+ break;
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ sig_attrs->mem.sig.dif.type = scsi2ib_prot_type(scsi_ptype);
+ sig_attrs->mem.sig.dif.bg_type = IB_T10DIF_CRC;
+ sig_attrs->mem.sig.dif.ref_tag = scsi_get_lba(sc) &
+ 0xffffffff;
+ sig_attrs->wire.sig.dif.type = IB_T10DIF_NONE;
+ break;
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ sig_attrs->mem.sig.dif.type = scsi2ib_prot_type(scsi_ptype);
+ sig_attrs->mem.sig.dif.bg_type = IB_T10DIF_CRC;
+ sig_attrs->mem.sig.dif.ref_tag = scsi_get_lba(sc) &
+ 0xffffffff;
+ sig_attrs->wire.sig.dif.type = scsi2ib_prot_type(scsi_ptype);
+ sig_attrs->wire.sig.dif.bg_type = IB_T10DIF_CRC;
+ sig_attrs->wire.sig.dif.ref_tag = scsi_get_lba(sc) &
+ 0xffffffff;
+ break;
+ default:
+ iser_err("Unsupported PI operation %d\n",
+ scsi_get_prot_op(sc));
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static int
+iser_set_prot_checks(struct scsi_cmnd *sc, u8 *mask)
+{
+ switch (scsi_get_prot_type(sc)) {
+ case SCSI_PROT_DIF_TYPE0:
+ *mask = 0x0;
+ break;
+ case SCSI_PROT_DIF_TYPE1:
+ case SCSI_PROT_DIF_TYPE2:
+ *mask = ISER_CHECK_GUARD | ISER_CHECK_REFTAG;
+ break;
+ case SCSI_PROT_DIF_TYPE3:
+ *mask = ISER_CHECK_GUARD;
+ break;
+ default:
+ iser_err("Unsupported protection type %d\n",
+ scsi_get_prot_type(sc));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+iser_reg_sig_mr(struct iscsi_iser_task *iser_task,
+ struct fast_reg_descriptor *desc, struct ib_sge *data_sge,
+ struct ib_sge *prot_sge, struct ib_sge *sig_sge)
+{
+ struct iser_conn *ib_conn = iser_task->ib_conn;
+ struct iser_pi_context *pi_ctx = desc->pi_ctx;
+ struct ib_send_wr sig_wr, inv_wr;
+ struct ib_send_wr *bad_wr, *wr = NULL;
+ struct ib_sig_attrs sig_attrs;
+ int ret;
+ u32 key;
+
+ memset(&sig_attrs, 0, sizeof(sig_attrs));
+ ret = iser_set_sig_attrs(iser_task->sc, &sig_attrs);
+ if (ret)
+ goto err;
+
+ ret = iser_set_prot_checks(iser_task->sc, &sig_attrs.check_mask);
+ if (ret)
+ goto err;
+
+ if (!(desc->reg_indicators & ISER_SIG_KEY_VALID)) {
+ memset(&inv_wr, 0, sizeof(inv_wr));
+ inv_wr.opcode = IB_WR_LOCAL_INV;
+ inv_wr.wr_id = ISER_FASTREG_LI_WRID;
+ inv_wr.ex.invalidate_rkey = pi_ctx->sig_mr->rkey;
+ wr = &inv_wr;
+ /* Bump the key */
+ key = (u8)(pi_ctx->sig_mr->rkey & 0x000000FF);
+ ib_update_fast_reg_key(pi_ctx->sig_mr, ++key);
+ }
+
+ memset(&sig_wr, 0, sizeof(sig_wr));
+ sig_wr.opcode = IB_WR_REG_SIG_MR;
+ sig_wr.wr_id = ISER_FASTREG_LI_WRID;
+ sig_wr.sg_list = data_sge;
+ sig_wr.num_sge = 1;
+ sig_wr.wr.sig_handover.sig_attrs = &sig_attrs;
+ sig_wr.wr.sig_handover.sig_mr = pi_ctx->sig_mr;
+ if (scsi_prot_sg_count(iser_task->sc))
+ sig_wr.wr.sig_handover.prot = prot_sge;
+ sig_wr.wr.sig_handover.access_flags = IB_ACCESS_LOCAL_WRITE |
+ IB_ACCESS_REMOTE_READ |
+ IB_ACCESS_REMOTE_WRITE;
+
+ if (!wr)
+ wr = &sig_wr;
+ else
+ wr->next = &sig_wr;
+
+ ret = ib_post_send(ib_conn->qp, wr, &bad_wr);
+ if (ret) {
+ iser_err("reg_sig_mr failed, ret:%d\n", ret);
+ goto err;
+ }
+ desc->reg_indicators &= ~ISER_SIG_KEY_VALID;
+
+ sig_sge->lkey = pi_ctx->sig_mr->lkey;
+ sig_sge->addr = 0;
+ sig_sge->length = data_sge->length + prot_sge->length;
+ if (scsi_get_prot_op(iser_task->sc) == SCSI_PROT_WRITE_INSERT ||
+ scsi_get_prot_op(iser_task->sc) == SCSI_PROT_READ_STRIP) {
+ sig_sge->length += (data_sge->length /
+ iser_task->sc->device->sector_size) * 8;
+ }
+
+ iser_dbg("sig_sge: addr: 0x%llx length: %u lkey: 0x%x\n",
+ sig_sge->addr, sig_sge->length,
+ sig_sge->lkey);
+err:
+ return ret;
+}
+
+static int iser_fast_reg_mr(struct iscsi_iser_task *iser_task,
struct iser_regd_buf *regd_buf,
- u32 offset, unsigned int data_size,
- unsigned int page_list_len)
+ struct iser_data_buf *mem,
+ enum iser_reg_indicator ind,
+ struct ib_sge *sge)
{
+ struct fast_reg_descriptor *desc = regd_buf->reg.mem_h;
+ struct iser_conn *ib_conn = iser_task->ib_conn;
+ struct iser_device *device = ib_conn->device;
+ struct ib_device *ibdev = device->ib_device;
+ struct ib_mr *mr;
+ struct ib_fast_reg_page_list *frpl;
struct ib_send_wr fastreg_wr, inv_wr;
struct ib_send_wr *bad_wr, *wr = NULL;
u8 key;
- int ret;
+ int ret, offset, size, plen;
+
+ /* if there a single dma entry, dma mr suffices */
+ if (mem->dma_nents == 1) {
+ struct scatterlist *sg = (struct scatterlist *)mem->buf;
- if (!desc->valid) {
+ sge->lkey = device->mr->lkey;
+ sge->addr = ib_sg_dma_address(ibdev, &sg[0]);
+ sge->length = ib_sg_dma_len(ibdev, &sg[0]);
+
+ iser_dbg("Single DMA entry: lkey=0x%x, addr=0x%llx, length=0x%x\n",
+ sge->lkey, sge->addr, sge->length);
+ return 0;
+ }
+
+ if (ind == ISER_DATA_KEY_VALID) {
+ mr = desc->data_mr;
+ frpl = desc->data_frpl;
+ } else {
+ mr = desc->pi_ctx->prot_mr;
+ frpl = desc->pi_ctx->prot_frpl;
+ }
+
+ plen = iser_sg_to_page_vec(mem, device->ib_device, frpl->page_list,
+ &offset, &size);
+ if (plen * SIZE_4K < size) {
+ iser_err("fast reg page_list too short to hold this SG\n");
+ return -EINVAL;
+ }
+
+ if (!(desc->reg_indicators & ind)) {
memset(&inv_wr, 0, sizeof(inv_wr));
+ inv_wr.wr_id = ISER_FASTREG_LI_WRID;
inv_wr.opcode = IB_WR_LOCAL_INV;
- inv_wr.send_flags = IB_SEND_SIGNALED;
- inv_wr.ex.invalidate_rkey = desc->data_mr->rkey;
+ inv_wr.ex.invalidate_rkey = mr->rkey;
wr = &inv_wr;
/* Bump the key */
- key = (u8)(desc->data_mr->rkey & 0x000000FF);
- ib_update_fast_reg_key(desc->data_mr, ++key);
+ key = (u8)(mr->rkey & 0x000000FF);
+ ib_update_fast_reg_key(mr, ++key);
}
/* Prepare FASTREG WR */
memset(&fastreg_wr, 0, sizeof(fastreg_wr));
+ fastreg_wr.wr_id = ISER_FASTREG_LI_WRID;
fastreg_wr.opcode = IB_WR_FAST_REG_MR;
- fastreg_wr.send_flags = IB_SEND_SIGNALED;
- fastreg_wr.wr.fast_reg.iova_start = desc->data_frpl->page_list[0] + offset;
- fastreg_wr.wr.fast_reg.page_list = desc->data_frpl;
- fastreg_wr.wr.fast_reg.page_list_len = page_list_len;
+ fastreg_wr.wr.fast_reg.iova_start = frpl->page_list[0] + offset;
+ fastreg_wr.wr.fast_reg.page_list = frpl;
+ fastreg_wr.wr.fast_reg.page_list_len = plen;
fastreg_wr.wr.fast_reg.page_shift = SHIFT_4K;
- fastreg_wr.wr.fast_reg.length = data_size;
- fastreg_wr.wr.fast_reg.rkey = desc->data_mr->rkey;
+ fastreg_wr.wr.fast_reg.length = size;
+ fastreg_wr.wr.fast_reg.rkey = mr->rkey;
fastreg_wr.wr.fast_reg.access_flags = (IB_ACCESS_LOCAL_WRITE |
IB_ACCESS_REMOTE_WRITE |
IB_ACCESS_REMOTE_READ);
- if (!wr) {
+ if (!wr)
wr = &fastreg_wr;
- atomic_inc(&ib_conn->post_send_buf_count);
- } else {
+ else
wr->next = &fastreg_wr;
- atomic_add(2, &ib_conn->post_send_buf_count);
- }
ret = ib_post_send(ib_conn->qp, wr, &bad_wr);
if (ret) {
- if (bad_wr->next)
- atomic_sub(2, &ib_conn->post_send_buf_count);
- else
- atomic_dec(&ib_conn->post_send_buf_count);
iser_err("fast registration failed, ret:%d\n", ret);
return ret;
}
- desc->valid = false;
+ desc->reg_indicators &= ~ind;
- regd_buf->reg.mem_h = desc;
- regd_buf->reg.lkey = desc->data_mr->lkey;
- regd_buf->reg.rkey = desc->data_mr->rkey;
- regd_buf->reg.va = desc->data_frpl->page_list[0] + offset;
- regd_buf->reg.len = data_size;
- regd_buf->reg.is_mr = 1;
+ sge->lkey = mr->lkey;
+ sge->addr = frpl->page_list[0] + offset;
+ sge->length = size;
return ret;
}
/**
- * iser_reg_rdma_mem_frwr - Registers memory intended for RDMA,
+ * iser_reg_rdma_mem_fastreg - Registers memory intended for RDMA,
* using Fast Registration WR (if possible) obtaining rkey and va
*
* returns 0 on success, errno code on failure
*/
-int iser_reg_rdma_mem_frwr(struct iscsi_iser_task *iser_task,
- enum iser_data_dir cmd_dir)
+int iser_reg_rdma_mem_fastreg(struct iscsi_iser_task *iser_task,
+ enum iser_data_dir cmd_dir)
{
- struct iser_conn *ib_conn = iser_task->iser_conn->ib_conn;
+ struct iser_conn *ib_conn = iser_task->ib_conn;
struct iser_device *device = ib_conn->device;
struct ib_device *ibdev = device->ib_device;
struct iser_data_buf *mem = &iser_task->data[cmd_dir];
struct iser_regd_buf *regd_buf = &iser_task->rdma_regd[cmd_dir];
- struct fast_reg_descriptor *desc;
- unsigned int data_size, page_list_len;
+ struct fast_reg_descriptor *desc = NULL;
+ struct ib_sge data_sge;
int err, aligned_len;
unsigned long flags;
- u32 offset;
aligned_len = iser_data_buf_aligned_len(mem, ibdev);
if (aligned_len != mem->dma_nents) {
- err = fall_to_bounce_buf(iser_task, ibdev,
+ err = fall_to_bounce_buf(iser_task, ibdev, mem,
+ &iser_task->data_copy[cmd_dir],
cmd_dir, aligned_len);
if (err) {
iser_err("failed to allocate bounce buffer\n");
@@ -540,41 +722,79 @@ int iser_reg_rdma_mem_frwr(struct iscsi_iser_task *iser_task,
mem = &iser_task->data_copy[cmd_dir];
}
- /* if there a single dma entry, dma mr suffices */
- if (mem->dma_nents == 1) {
- struct scatterlist *sg = (struct scatterlist *)mem->buf;
-
- regd_buf->reg.lkey = device->mr->lkey;
- regd_buf->reg.rkey = device->mr->rkey;
- regd_buf->reg.len = ib_sg_dma_len(ibdev, &sg[0]);
- regd_buf->reg.va = ib_sg_dma_address(ibdev, &sg[0]);
- regd_buf->reg.is_mr = 0;
- } else {
+ if (mem->dma_nents != 1 ||
+ scsi_get_prot_op(iser_task->sc) != SCSI_PROT_NORMAL) {
spin_lock_irqsave(&ib_conn->lock, flags);
- desc = list_first_entry(&ib_conn->fastreg.frwr.pool,
+ desc = list_first_entry(&ib_conn->fastreg.pool,
struct fast_reg_descriptor, list);
list_del(&desc->list);
spin_unlock_irqrestore(&ib_conn->lock, flags);
- page_list_len = iser_sg_to_page_vec(mem, device->ib_device,
- desc->data_frpl->page_list,
- &offset, &data_size);
-
- if (page_list_len * SIZE_4K < data_size) {
- iser_err("fast reg page_list too short to hold this SG\n");
- err = -EINVAL;
- goto err_reg;
+ regd_buf->reg.mem_h = desc;
+ }
+
+ err = iser_fast_reg_mr(iser_task, regd_buf, mem,
+ ISER_DATA_KEY_VALID, &data_sge);
+ if (err)
+ goto err_reg;
+
+ if (scsi_get_prot_op(iser_task->sc) != SCSI_PROT_NORMAL) {
+ struct ib_sge prot_sge, sig_sge;
+
+ memset(&prot_sge, 0, sizeof(prot_sge));
+ if (scsi_prot_sg_count(iser_task->sc)) {
+ mem = &iser_task->prot[cmd_dir];
+ aligned_len = iser_data_buf_aligned_len(mem, ibdev);
+ if (aligned_len != mem->dma_nents) {
+ err = fall_to_bounce_buf(iser_task, ibdev, mem,
+ &iser_task->prot_copy[cmd_dir],
+ cmd_dir, aligned_len);
+ if (err) {
+ iser_err("failed to allocate bounce buffer\n");
+ return err;
+ }
+ mem = &iser_task->prot_copy[cmd_dir];
+ }
+
+ err = iser_fast_reg_mr(iser_task, regd_buf, mem,
+ ISER_PROT_KEY_VALID, &prot_sge);
+ if (err)
+ goto err_reg;
}
- err = iser_fast_reg_mr(desc, ib_conn, regd_buf,
- offset, data_size, page_list_len);
- if (err)
- goto err_reg;
+ err = iser_reg_sig_mr(iser_task, desc, &data_sge,
+ &prot_sge, &sig_sge);
+ if (err) {
+ iser_err("Failed to register signature mr\n");
+ return err;
+ }
+ desc->reg_indicators |= ISER_FASTREG_PROTECTED;
+
+ regd_buf->reg.lkey = sig_sge.lkey;
+ regd_buf->reg.rkey = desc->pi_ctx->sig_mr->rkey;
+ regd_buf->reg.va = sig_sge.addr;
+ regd_buf->reg.len = sig_sge.length;
+ regd_buf->reg.is_mr = 1;
+ } else {
+ if (desc) {
+ regd_buf->reg.rkey = desc->data_mr->rkey;
+ regd_buf->reg.is_mr = 1;
+ } else {
+ regd_buf->reg.rkey = device->mr->rkey;
+ regd_buf->reg.is_mr = 0;
+ }
+
+ regd_buf->reg.lkey = data_sge.lkey;
+ regd_buf->reg.va = data_sge.addr;
+ regd_buf->reg.len = data_sge.length;
}
return 0;
err_reg:
- spin_lock_irqsave(&ib_conn->lock, flags);
- list_add_tail(&desc->list, &ib_conn->fastreg.frwr.pool);
- spin_unlock_irqrestore(&ib_conn->lock, flags);
+ if (desc) {
+ spin_lock_irqsave(&ib_conn->lock, flags);
+ list_add_tail(&desc->list, &ib_conn->fastreg.pool);
+ spin_unlock_irqrestore(&ib_conn->lock, flags);
+ }
+
return err;
}
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index ca37edef2791..32849f2becde 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved.
* Copyright (c) 2005, 2006 Cisco Systems. All rights reserved.
- * Copyright (c) 2013 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2013-2014 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
@@ -71,17 +71,14 @@ static void iser_event_handler(struct ib_event_handler *handler,
*/
static int iser_create_device_ib_res(struct iser_device *device)
{
- int i, j;
struct iser_cq_desc *cq_desc;
- struct ib_device_attr *dev_attr;
+ struct ib_device_attr *dev_attr = &device->dev_attr;
+ int ret, i, j;
- dev_attr = kmalloc(sizeof(*dev_attr), GFP_KERNEL);
- if (!dev_attr)
- return -ENOMEM;
-
- if (ib_query_device(device->ib_device, dev_attr)) {
+ ret = ib_query_device(device->ib_device, dev_attr);
+ if (ret) {
pr_warn("Query device failed for %s\n", device->ib_device->name);
- goto dev_attr_err;
+ return ret;
}
/* Assign function handles - based on FMR support */
@@ -94,14 +91,14 @@ static int iser_create_device_ib_res(struct iser_device *device)
device->iser_unreg_rdma_mem = iser_unreg_mem_fmr;
} else
if (dev_attr->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS) {
- iser_info("FRWR supported, using FRWR for registration\n");
- device->iser_alloc_rdma_reg_res = iser_create_frwr_pool;
- device->iser_free_rdma_reg_res = iser_free_frwr_pool;
- device->iser_reg_rdma_mem = iser_reg_rdma_mem_frwr;
- device->iser_unreg_rdma_mem = iser_unreg_mem_frwr;
+ iser_info("FastReg supported, using FastReg for registration\n");
+ device->iser_alloc_rdma_reg_res = iser_create_fastreg_pool;
+ device->iser_free_rdma_reg_res = iser_free_fastreg_pool;
+ device->iser_reg_rdma_mem = iser_reg_rdma_mem_fastreg;
+ device->iser_unreg_rdma_mem = iser_unreg_mem_fastreg;
} else {
- iser_err("IB device does not support FMRs nor FRWRs, can't register memory\n");
- goto dev_attr_err;
+ iser_err("IB device does not support FMRs nor FastRegs, can't register memory\n");
+ return -1;
}
device->cqs_used = min(ISER_MAX_CQ, device->ib_device->num_comp_vectors);
@@ -158,7 +155,6 @@ static int iser_create_device_ib_res(struct iser_device *device)
if (ib_register_event_handler(&device->event_handler))
goto handler_err;
- kfree(dev_attr);
return 0;
handler_err:
@@ -178,8 +174,6 @@ pd_err:
kfree(device->cq_desc);
cq_desc_err:
iser_err("failed to allocate an IB resource\n");
-dev_attr_err:
- kfree(dev_attr);
return -1;
}
@@ -221,13 +215,13 @@ int iser_create_fmr_pool(struct iser_conn *ib_conn, unsigned cmds_max)
struct ib_fmr_pool_param params;
int ret = -ENOMEM;
- ib_conn->fastreg.fmr.page_vec = kmalloc(sizeof(struct iser_page_vec) +
- (sizeof(u64)*(ISCSI_ISER_SG_TABLESIZE + 1)),
- GFP_KERNEL);
- if (!ib_conn->fastreg.fmr.page_vec)
+ ib_conn->fmr.page_vec = kmalloc(sizeof(*ib_conn->fmr.page_vec) +
+ (sizeof(u64)*(ISCSI_ISER_SG_TABLESIZE + 1)),
+ GFP_KERNEL);
+ if (!ib_conn->fmr.page_vec)
return ret;
- ib_conn->fastreg.fmr.page_vec->pages = (u64 *)(ib_conn->fastreg.fmr.page_vec + 1);
+ ib_conn->fmr.page_vec->pages = (u64 *)(ib_conn->fmr.page_vec + 1);
params.page_shift = SHIFT_4K;
/* when the first/last SG element are not start/end *
@@ -243,16 +237,16 @@ int iser_create_fmr_pool(struct iser_conn *ib_conn, unsigned cmds_max)
IB_ACCESS_REMOTE_WRITE |
IB_ACCESS_REMOTE_READ);
- ib_conn->fastreg.fmr.pool = ib_create_fmr_pool(device->pd, &params);
- if (!IS_ERR(ib_conn->fastreg.fmr.pool))
+ ib_conn->fmr.pool = ib_create_fmr_pool(device->pd, &params);
+ if (!IS_ERR(ib_conn->fmr.pool))
return 0;
/* no FMR => no need for page_vec */
- kfree(ib_conn->fastreg.fmr.page_vec);
- ib_conn->fastreg.fmr.page_vec = NULL;
+ kfree(ib_conn->fmr.page_vec);
+ ib_conn->fmr.page_vec = NULL;
- ret = PTR_ERR(ib_conn->fastreg.fmr.pool);
- ib_conn->fastreg.fmr.pool = NULL;
+ ret = PTR_ERR(ib_conn->fmr.pool);
+ ib_conn->fmr.pool = NULL;
if (ret != -ENOSYS) {
iser_err("FMR allocation failed, err %d\n", ret);
return ret;
@@ -268,93 +262,173 @@ int iser_create_fmr_pool(struct iser_conn *ib_conn, unsigned cmds_max)
void iser_free_fmr_pool(struct iser_conn *ib_conn)
{
iser_info("freeing conn %p fmr pool %p\n",
- ib_conn, ib_conn->fastreg.fmr.pool);
+ ib_conn, ib_conn->fmr.pool);
+
+ if (ib_conn->fmr.pool != NULL)
+ ib_destroy_fmr_pool(ib_conn->fmr.pool);
+
+ ib_conn->fmr.pool = NULL;
+
+ kfree(ib_conn->fmr.page_vec);
+ ib_conn->fmr.page_vec = NULL;
+}
+
+static int
+iser_create_fastreg_desc(struct ib_device *ib_device, struct ib_pd *pd,
+ bool pi_enable, struct fast_reg_descriptor *desc)
+{
+ int ret;
+
+ desc->data_frpl = ib_alloc_fast_reg_page_list(ib_device,
+ ISCSI_ISER_SG_TABLESIZE + 1);
+ if (IS_ERR(desc->data_frpl)) {
+ ret = PTR_ERR(desc->data_frpl);
+ iser_err("Failed to allocate ib_fast_reg_page_list err=%d\n",
+ ret);
+ return PTR_ERR(desc->data_frpl);
+ }
+
+ desc->data_mr = ib_alloc_fast_reg_mr(pd, ISCSI_ISER_SG_TABLESIZE + 1);
+ if (IS_ERR(desc->data_mr)) {
+ ret = PTR_ERR(desc->data_mr);
+ iser_err("Failed to allocate ib_fast_reg_mr err=%d\n", ret);
+ goto fast_reg_mr_failure;
+ }
+ desc->reg_indicators |= ISER_DATA_KEY_VALID;
+
+ if (pi_enable) {
+ struct ib_mr_init_attr mr_init_attr = {0};
+ struct iser_pi_context *pi_ctx = NULL;
- if (ib_conn->fastreg.fmr.pool != NULL)
- ib_destroy_fmr_pool(ib_conn->fastreg.fmr.pool);
+ desc->pi_ctx = kzalloc(sizeof(*desc->pi_ctx), GFP_KERNEL);
+ if (!desc->pi_ctx) {
+ iser_err("Failed to allocate pi context\n");
+ ret = -ENOMEM;
+ goto pi_ctx_alloc_failure;
+ }
+ pi_ctx = desc->pi_ctx;
+
+ pi_ctx->prot_frpl = ib_alloc_fast_reg_page_list(ib_device,
+ ISCSI_ISER_SG_TABLESIZE);
+ if (IS_ERR(pi_ctx->prot_frpl)) {
+ ret = PTR_ERR(pi_ctx->prot_frpl);
+ iser_err("Failed to allocate prot frpl ret=%d\n",
+ ret);
+ goto prot_frpl_failure;
+ }
+
+ pi_ctx->prot_mr = ib_alloc_fast_reg_mr(pd,
+ ISCSI_ISER_SG_TABLESIZE + 1);
+ if (IS_ERR(pi_ctx->prot_mr)) {
+ ret = PTR_ERR(pi_ctx->prot_mr);
+ iser_err("Failed to allocate prot frmr ret=%d\n",
+ ret);
+ goto prot_mr_failure;
+ }
+ desc->reg_indicators |= ISER_PROT_KEY_VALID;
+
+ mr_init_attr.max_reg_descriptors = 2;
+ mr_init_attr.flags |= IB_MR_SIGNATURE_EN;
+ pi_ctx->sig_mr = ib_create_mr(pd, &mr_init_attr);
+ if (IS_ERR(pi_ctx->sig_mr)) {
+ ret = PTR_ERR(pi_ctx->sig_mr);
+ iser_err("Failed to allocate signature enabled mr err=%d\n",
+ ret);
+ goto sig_mr_failure;
+ }
+ desc->reg_indicators |= ISER_SIG_KEY_VALID;
+ }
+ desc->reg_indicators &= ~ISER_FASTREG_PROTECTED;
- ib_conn->fastreg.fmr.pool = NULL;
+ iser_dbg("Create fr_desc %p page_list %p\n",
+ desc, desc->data_frpl->page_list);
- kfree(ib_conn->fastreg.fmr.page_vec);
- ib_conn->fastreg.fmr.page_vec = NULL;
+ return 0;
+sig_mr_failure:
+ ib_dereg_mr(desc->pi_ctx->prot_mr);
+prot_mr_failure:
+ ib_free_fast_reg_page_list(desc->pi_ctx->prot_frpl);
+prot_frpl_failure:
+ kfree(desc->pi_ctx);
+pi_ctx_alloc_failure:
+ ib_dereg_mr(desc->data_mr);
+fast_reg_mr_failure:
+ ib_free_fast_reg_page_list(desc->data_frpl);
+
+ return ret;
}
/**
- * iser_create_frwr_pool - Creates pool of fast_reg descriptors
+ * iser_create_fastreg_pool - Creates pool of fast_reg descriptors
* for fast registration work requests.
* returns 0 on success, or errno code on failure
*/
-int iser_create_frwr_pool(struct iser_conn *ib_conn, unsigned cmds_max)
+int iser_create_fastreg_pool(struct iser_conn *ib_conn, unsigned cmds_max)
{
struct iser_device *device = ib_conn->device;
struct fast_reg_descriptor *desc;
int i, ret;
- INIT_LIST_HEAD(&ib_conn->fastreg.frwr.pool);
- ib_conn->fastreg.frwr.pool_size = 0;
+ INIT_LIST_HEAD(&ib_conn->fastreg.pool);
+ ib_conn->fastreg.pool_size = 0;
for (i = 0; i < cmds_max; i++) {
- desc = kmalloc(sizeof(*desc), GFP_KERNEL);
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (!desc) {
iser_err("Failed to allocate a new fast_reg descriptor\n");
ret = -ENOMEM;
goto err;
}
- desc->data_frpl = ib_alloc_fast_reg_page_list(device->ib_device,
- ISCSI_ISER_SG_TABLESIZE + 1);
- if (IS_ERR(desc->data_frpl)) {
- ret = PTR_ERR(desc->data_frpl);
- iser_err("Failed to allocate ib_fast_reg_page_list err=%d\n", ret);
- goto fast_reg_page_failure;
+ ret = iser_create_fastreg_desc(device->ib_device, device->pd,
+ ib_conn->pi_support, desc);
+ if (ret) {
+ iser_err("Failed to create fastreg descriptor err=%d\n",
+ ret);
+ kfree(desc);
+ goto err;
}
- desc->data_mr = ib_alloc_fast_reg_mr(device->pd,
- ISCSI_ISER_SG_TABLESIZE + 1);
- if (IS_ERR(desc->data_mr)) {
- ret = PTR_ERR(desc->data_mr);
- iser_err("Failed to allocate ib_fast_reg_mr err=%d\n", ret);
- goto fast_reg_mr_failure;
- }
- desc->valid = true;
- list_add_tail(&desc->list, &ib_conn->fastreg.frwr.pool);
- ib_conn->fastreg.frwr.pool_size++;
+ list_add_tail(&desc->list, &ib_conn->fastreg.pool);
+ ib_conn->fastreg.pool_size++;
}
return 0;
-fast_reg_mr_failure:
- ib_free_fast_reg_page_list(desc->data_frpl);
-fast_reg_page_failure:
- kfree(desc);
err:
- iser_free_frwr_pool(ib_conn);
+ iser_free_fastreg_pool(ib_conn);
return ret;
}
/**
- * iser_free_frwr_pool - releases the pool of fast_reg descriptors
+ * iser_free_fastreg_pool - releases the pool of fast_reg descriptors
*/
-void iser_free_frwr_pool(struct iser_conn *ib_conn)
+void iser_free_fastreg_pool(struct iser_conn *ib_conn)
{
struct fast_reg_descriptor *desc, *tmp;
int i = 0;
- if (list_empty(&ib_conn->fastreg.frwr.pool))
+ if (list_empty(&ib_conn->fastreg.pool))
return;
- iser_info("freeing conn %p frwr pool\n", ib_conn);
+ iser_info("freeing conn %p fr pool\n", ib_conn);
- list_for_each_entry_safe(desc, tmp, &ib_conn->fastreg.frwr.pool, list) {
+ list_for_each_entry_safe(desc, tmp, &ib_conn->fastreg.pool, list) {
list_del(&desc->list);
ib_free_fast_reg_page_list(desc->data_frpl);
ib_dereg_mr(desc->data_mr);
+ if (desc->pi_ctx) {
+ ib_free_fast_reg_page_list(desc->pi_ctx->prot_frpl);
+ ib_dereg_mr(desc->pi_ctx->prot_mr);
+ ib_destroy_mr(desc->pi_ctx->sig_mr);
+ kfree(desc->pi_ctx);
+ }
kfree(desc);
++i;
}
- if (i < ib_conn->fastreg.frwr.pool_size)
+ if (i < ib_conn->fastreg.pool_size)
iser_warn("pool still has %d regions registered\n",
- ib_conn->fastreg.frwr.pool_size - i);
+ ib_conn->fastreg.pool_size - i);
}
/**
@@ -389,12 +463,17 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn)
init_attr.qp_context = (void *)ib_conn;
init_attr.send_cq = device->tx_cq[min_index];
init_attr.recv_cq = device->rx_cq[min_index];
- init_attr.cap.max_send_wr = ISER_QP_MAX_REQ_DTOS;
init_attr.cap.max_recv_wr = ISER_QP_MAX_RECV_DTOS;
init_attr.cap.max_send_sge = 2;
init_attr.cap.max_recv_sge = 1;
init_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
init_attr.qp_type = IB_QPT_RC;
+ if (ib_conn->pi_support) {
+ init_attr.cap.max_send_wr = ISER_QP_SIG_MAX_REQ_DTOS;
+ init_attr.create_flags |= IB_QP_CREATE_SIGNATURE_EN;
+ } else {
+ init_attr.cap.max_send_wr = ISER_QP_MAX_REQ_DTOS;
+ }
ret = rdma_create_qp(ib_conn->cma_id, device->pd, &init_attr);
if (ret)
@@ -591,6 +670,19 @@ static int iser_addr_handler(struct rdma_cm_id *cma_id)
ib_conn = (struct iser_conn *)cma_id->context;
ib_conn->device = device;
+ /* connection T10-PI support */
+ if (iser_pi_enable) {
+ if (!(device->dev_attr.device_cap_flags &
+ IB_DEVICE_SIGNATURE_HANDOVER)) {
+ iser_warn("T10-PI requested but not supported on %s, "
+ "continue without T10-PI\n",
+ ib_conn->device->ib_device->name);
+ ib_conn->pi_support = false;
+ } else {
+ ib_conn->pi_support = true;
+ }
+ }
+
ret = rdma_resolve_route(cma_id, 1000);
if (ret) {
iser_err("resolve route failed: %d\n", ret);
@@ -636,6 +728,11 @@ failure:
static void iser_connected_handler(struct rdma_cm_id *cma_id)
{
struct iser_conn *ib_conn;
+ struct ib_qp_attr attr;
+ struct ib_qp_init_attr init_attr;
+
+ (void)ib_query_qp(cma_id->qp, &attr, ~0, &init_attr);
+ iser_info("remote qpn:%x my qpn:%x\n", attr.dest_qp_num, cma_id->qp->qp_num);
ib_conn = (struct iser_conn *)cma_id->context;
ib_conn->state = ISER_CONN_UP;
@@ -653,9 +750,8 @@ static int iser_disconnected_handler(struct rdma_cm_id *cma_id)
* terminated asynchronously from the iSCSI layer's perspective. */
if (iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP,
ISER_CONN_TERMINATING)){
- if (ib_conn->iser_conn)
- iscsi_conn_failure(ib_conn->iser_conn->iscsi_conn,
- ISCSI_ERR_CONN_FAILED);
+ if (ib_conn->iscsi_conn)
+ iscsi_conn_failure(ib_conn->iscsi_conn, ISCSI_ERR_CONN_FAILED);
else
iser_err("iscsi_iser connection isn't bound\n");
}
@@ -801,7 +897,7 @@ int iser_reg_page_vec(struct iser_conn *ib_conn,
page_list = page_vec->pages;
io_addr = page_list[0];
- mem = ib_fmr_pool_map_phys(ib_conn->fastreg.fmr.pool,
+ mem = ib_fmr_pool_map_phys(ib_conn->fmr.pool,
page_list,
page_vec->length,
io_addr);
@@ -855,11 +951,11 @@ void iser_unreg_mem_fmr(struct iscsi_iser_task *iser_task,
reg->mem_h = NULL;
}
-void iser_unreg_mem_frwr(struct iscsi_iser_task *iser_task,
- enum iser_data_dir cmd_dir)
+void iser_unreg_mem_fastreg(struct iscsi_iser_task *iser_task,
+ enum iser_data_dir cmd_dir)
{
struct iser_mem_reg *reg = &iser_task->rdma_regd[cmd_dir].reg;
- struct iser_conn *ib_conn = iser_task->iser_conn->ib_conn;
+ struct iser_conn *ib_conn = iser_task->ib_conn;
struct fast_reg_descriptor *desc = reg->mem_h;
if (!reg->is_mr)
@@ -868,7 +964,7 @@ void iser_unreg_mem_frwr(struct iscsi_iser_task *iser_task,
reg->mem_h = NULL;
reg->is_mr = 0;
spin_lock_bh(&ib_conn->lock);
- list_add_tail(&desc->list, &ib_conn->fastreg.frwr.pool);
+ list_add_tail(&desc->list, &ib_conn->fastreg.pool);
spin_unlock_bh(&ib_conn->lock);
}
@@ -969,7 +1065,7 @@ static void iser_handle_comp_error(struct iser_tx_desc *desc,
* perspective. */
if (iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP,
ISER_CONN_TERMINATING))
- iscsi_conn_failure(ib_conn->iser_conn->iscsi_conn,
+ iscsi_conn_failure(ib_conn->iscsi_conn,
ISCSI_ERR_CONN_FAILED);
/* no more non completed posts to the QP, complete the
@@ -993,18 +1089,16 @@ static int iser_drain_tx_cq(struct iser_device *device, int cq_index)
if (wc.status == IB_WC_SUCCESS) {
if (wc.opcode == IB_WC_SEND)
iser_snd_completion(tx_desc, ib_conn);
- else if (wc.opcode == IB_WC_LOCAL_INV ||
- wc.opcode == IB_WC_FAST_REG_MR) {
- atomic_dec(&ib_conn->post_send_buf_count);
- continue;
- } else
+ else
iser_err("expected opcode %d got %d\n",
IB_WC_SEND, wc.opcode);
} else {
iser_err("tx id %llx status %d vend_err %x\n",
- wc.wr_id, wc.status, wc.vendor_err);
- atomic_dec(&ib_conn->post_send_buf_count);
- iser_handle_comp_error(tx_desc, ib_conn);
+ wc.wr_id, wc.status, wc.vendor_err);
+ if (wc.wr_id != ISER_FASTREG_LI_WRID) {
+ atomic_dec(&ib_conn->post_send_buf_count);
+ iser_handle_comp_error(tx_desc, ib_conn);
+ }
}
completed_tx++;
}
@@ -1022,8 +1116,12 @@ static void iser_cq_tasklet_fn(unsigned long data)
struct iser_rx_desc *desc;
unsigned long xfer_len;
struct iser_conn *ib_conn;
- int completed_tx, completed_rx;
- completed_tx = completed_rx = 0;
+ int completed_tx, completed_rx = 0;
+
+ /* First do tx drain, so in a case where we have rx flushes and a successful
+ * tx completion we will still go through completion error handling.
+ */
+ completed_tx = iser_drain_tx_cq(device, cq_index);
while (ib_poll_cq(cq, 1, &wc) == 1) {
desc = (struct iser_rx_desc *) (unsigned long) wc.wr_id;
@@ -1051,7 +1149,6 @@ static void iser_cq_tasklet_fn(unsigned long data)
* " would not cause interrupts to be missed" */
ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
- completed_tx += iser_drain_tx_cq(device, cq_index);
iser_dbg("got %d rx %d tx completions\n", completed_rx, completed_tx);
}
@@ -1063,3 +1160,51 @@ static void iser_cq_callback(struct ib_cq *cq, void *cq_context)
tasklet_schedule(&device->cq_tasklet[cq_index]);
}
+
+u8 iser_check_task_pi_status(struct iscsi_iser_task *iser_task,
+ enum iser_data_dir cmd_dir, sector_t *sector)
+{
+ struct iser_mem_reg *reg = &iser_task->rdma_regd[cmd_dir].reg;
+ struct fast_reg_descriptor *desc = reg->mem_h;
+ unsigned long sector_size = iser_task->sc->device->sector_size;
+ struct ib_mr_status mr_status;
+ int ret;
+
+ if (desc && desc->reg_indicators & ISER_FASTREG_PROTECTED) {
+ desc->reg_indicators &= ~ISER_FASTREG_PROTECTED;
+ ret = ib_check_mr_status(desc->pi_ctx->sig_mr,
+ IB_MR_CHECK_SIG_STATUS, &mr_status);
+ if (ret) {
+ pr_err("ib_check_mr_status failed, ret %d\n", ret);
+ goto err;
+ }
+
+ 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 = scsi_get_lba(iser_task->sc) + sector_off;
+
+ pr_err("PI error found type %d at sector %llx "
+ "expected %x vs actual %x\n",
+ mr_status.sig_err.err_type,
+ (unsigned long long)*sector,
+ mr_status.sig_err.expected,
+ mr_status.sig_err.actual);
+
+ switch (mr_status.sig_err.err_type) {
+ case IB_SIG_BAD_GUARD:
+ return 0x1;
+ case IB_SIG_BAD_REFTAG:
+ return 0x3;
+ case IB_SIG_BAD_APPTAG:
+ return 0x2;
+ }
+ }
+ }
+
+ return 0;
+err:
+ /* Not alot we can do here, return ambiguous guard error */
+ return 0x1;
+}
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 529b6bcdca7a..66a908bf3fb9 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -411,6 +411,8 @@ static void srp_path_rec_completion(int status,
static int srp_lookup_path(struct srp_target_port *target)
{
+ int ret;
+
target->path.numb_path = 1;
init_completion(&target->done);
@@ -431,7 +433,9 @@ static int srp_lookup_path(struct srp_target_port *target)
if (target->path_query_id < 0)
return target->path_query_id;
- wait_for_completion(&target->done);
+ ret = wait_for_completion_interruptible(&target->done);
+ if (ret < 0)
+ return ret;
if (target->status < 0)
shost_printk(KERN_WARNING, target->scsi_host,
@@ -710,7 +714,9 @@ static int srp_connect_target(struct srp_target_port *target)
ret = srp_send_req(target);
if (ret)
return ret;
- wait_for_completion(&target->done);
+ ret = wait_for_completion_interruptible(&target->done);
+ if (ret < 0)
+ return ret;
/*
* The CM event handling code will set status to
@@ -777,6 +783,7 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd,
* srp_claim_req - Take ownership of the scmnd associated with a request.
* @target: SRP target port.
* @req: SRP request.
+ * @sdev: If not NULL, only take ownership for this SCSI device.
* @scmnd: If NULL, take ownership of @req->scmnd. If not NULL, only take
* ownership of @req->scmnd if it equals @scmnd.
*
@@ -785,16 +792,17 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd,
*/
static struct scsi_cmnd *srp_claim_req(struct srp_target_port *target,
struct srp_request *req,
+ struct scsi_device *sdev,
struct scsi_cmnd *scmnd)
{
unsigned long flags;
spin_lock_irqsave(&target->lock, flags);
- if (!scmnd) {
+ if (req->scmnd &&
+ (!sdev || req->scmnd->device == sdev) &&
+ (!scmnd || req->scmnd == scmnd)) {
scmnd = req->scmnd;
req->scmnd = NULL;
- } else if (req->scmnd == scmnd) {
- req->scmnd = NULL;
} else {
scmnd = NULL;
}
@@ -821,9 +829,10 @@ static void srp_free_req(struct srp_target_port *target,
}
static void srp_finish_req(struct srp_target_port *target,
- struct srp_request *req, int result)
+ struct srp_request *req, struct scsi_device *sdev,
+ int result)
{
- struct scsi_cmnd *scmnd = srp_claim_req(target, req, NULL);
+ struct scsi_cmnd *scmnd = srp_claim_req(target, req, sdev, NULL);
if (scmnd) {
srp_free_req(target, req, scmnd, 0);
@@ -835,11 +844,20 @@ static void srp_finish_req(struct srp_target_port *target,
static void srp_terminate_io(struct srp_rport *rport)
{
struct srp_target_port *target = rport->lld_data;
+ struct Scsi_Host *shost = target->scsi_host;
+ struct scsi_device *sdev;
int i;
+ /*
+ * Invoking srp_terminate_io() while srp_queuecommand() is running
+ * is not safe. Hence the warning statement below.
+ */
+ shost_for_each_device(sdev, shost)
+ WARN_ON_ONCE(sdev->request_queue->request_fn_active);
+
for (i = 0; i < target->req_ring_size; ++i) {
struct srp_request *req = &target->req_ring[i];
- srp_finish_req(target, req, DID_TRANSPORT_FAILFAST << 16);
+ srp_finish_req(target, req, NULL, DID_TRANSPORT_FAILFAST << 16);
}
}
@@ -876,7 +894,7 @@ static int srp_rport_reconnect(struct srp_rport *rport)
for (i = 0; i < target->req_ring_size; ++i) {
struct srp_request *req = &target->req_ring[i];
- srp_finish_req(target, req, DID_RESET << 16);
+ srp_finish_req(target, req, NULL, DID_RESET << 16);
}
INIT_LIST_HEAD(&target->free_tx);
@@ -1284,7 +1302,7 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
complete(&target->tsk_mgmt_done);
} else {
req = &target->req_ring[rsp->tag];
- scmnd = srp_claim_req(target, req, NULL);
+ scmnd = srp_claim_req(target, req, NULL, NULL);
if (!scmnd) {
shost_printk(KERN_ERR, target->scsi_host,
"Null scmnd for RSP w/tag %016llx\n",
@@ -1804,8 +1822,10 @@ static void srp_cm_rej_handler(struct ib_cm_id *cm_id,
shost_printk(KERN_WARNING, shost,
PFX "SRP_LOGIN_REJ: requested max_it_iu_len too large\n");
else
- shost_printk(KERN_WARNING, shost,
- PFX "SRP LOGIN REJECTED, reason 0x%08x\n", reason);
+ shost_printk(KERN_WARNING, shost, PFX
+ "SRP LOGIN from %pI6 to %pI6 REJECTED, reason 0x%08x\n",
+ target->path.sgid.raw,
+ target->orig_dgid, reason);
} else
shost_printk(KERN_WARNING, shost,
" REJ reason: IB_CM_REJ_CONSUMER_DEFINED,"
@@ -1863,6 +1883,7 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
case IB_CM_TIMEWAIT_EXIT:
shost_printk(KERN_ERR, target->scsi_host,
PFX "connection closed\n");
+ comp = 1;
target->status = 0;
break;
@@ -1999,7 +2020,7 @@ static int srp_abort(struct scsi_cmnd *scmnd)
shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n");
- if (!req || !srp_claim_req(target, req, scmnd))
+ if (!req || !srp_claim_req(target, req, NULL, scmnd))
return SUCCESS;
if (srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
SRP_TSK_ABORT_TASK) == 0)
@@ -2030,8 +2051,7 @@ static int srp_reset_device(struct scsi_cmnd *scmnd)
for (i = 0; i < target->req_ring_size; ++i) {
struct srp_request *req = &target->req_ring[i];
- if (req->scmnd && req->scmnd->device == scmnd->device)
- srp_finish_req(target, req, DID_RESET << 16);
+ srp_finish_req(target, req, scmnd->device, DID_RESET << 16);
}
return SUCCESS;
@@ -2612,6 +2632,8 @@ static ssize_t srp_create_target(struct device *dev,
target->tl_retry_count = 7;
target->queue_size = SRP_DEFAULT_QUEUE_SIZE;
+ mutex_lock(&host->add_target_mutex);
+
ret = srp_parse_options(buf, target);
if (ret)
goto err;
@@ -2649,16 +2671,9 @@ static ssize_t srp_create_target(struct device *dev,
if (ret)
goto err_free_mem;
- ib_query_gid(ibdev, host->port, 0, &target->path.sgid);
-
- shost_printk(KERN_DEBUG, target->scsi_host, PFX
- "new target: id_ext %016llx ioc_guid %016llx pkey %04x "
- "service_id %016llx dgid %pI6\n",
- (unsigned long long) be64_to_cpu(target->id_ext),
- (unsigned long long) be64_to_cpu(target->ioc_guid),
- be16_to_cpu(target->path.pkey),
- (unsigned long long) be64_to_cpu(target->service_id),
- target->path.dgid.raw);
+ ret = ib_query_gid(ibdev, host->port, 0, &target->path.sgid);
+ if (ret)
+ goto err_free_mem;
ret = srp_create_target_ib(target);
if (ret)
@@ -2679,7 +2694,19 @@ static ssize_t srp_create_target(struct device *dev,
if (ret)
goto err_disconnect;
- return count;
+ shost_printk(KERN_DEBUG, target->scsi_host, PFX
+ "new target: id_ext %016llx ioc_guid %016llx pkey %04x service_id %016llx sgid %pI6 dgid %pI6\n",
+ be64_to_cpu(target->id_ext),
+ be64_to_cpu(target->ioc_guid),
+ be16_to_cpu(target->path.pkey),
+ be64_to_cpu(target->service_id),
+ target->path.sgid.raw, target->path.dgid.raw);
+
+ ret = count;
+
+out:
+ mutex_unlock(&host->add_target_mutex);
+ return ret;
err_disconnect:
srp_disconnect_target(target);
@@ -2695,8 +2722,7 @@ err_free_mem:
err:
scsi_host_put(target_host);
-
- return ret;
+ goto out;
}
static DEVICE_ATTR(add_target, S_IWUSR, NULL, srp_create_target);
@@ -2732,6 +2758,7 @@ static struct srp_host *srp_add_port(struct srp_device *device, u8 port)
INIT_LIST_HEAD(&host->target_list);
spin_lock_init(&host->target_lock);
init_completion(&host->released);
+ mutex_init(&host->add_target_mutex);
host->srp_dev = device;
host->port = port;
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index 575681063f38..aad27b7b4a46 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -105,6 +105,7 @@ struct srp_host {
spinlock_t target_lock;
struct completion released;
struct list_head list;
+ struct mutex add_target_mutex;
};
struct srp_request {
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a673c9f3a0b9..76842d7dc2e3 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -151,6 +151,18 @@ config KEYBOARD_BFIN
To compile this driver as a module, choose M here: the
module will be called bf54x-keys.
+config KEYBOARD_CLPS711X
+ tristate "CLPS711X Keypad support"
+ depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST)
+ select INPUT_MATRIXKMAP
+ select INPUT_POLLDEV
+ help
+ Say Y here to enable the matrix keypad on the Cirrus Logic
+ CLPS711X CPUs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called clps711x-keypad.
+
config KEYBOARD_LKKBD
tristate "DECstation/VAXstation LK201/LK401 keyboard"
select SERIO
@@ -595,16 +607,6 @@ config KEYBOARD_TC3589X
To compile this driver as a module, choose M here: the
module will be called tc3589x-keypad.
-config KEYBOARD_TNETV107X
- tristate "TI TNETV107X keypad support"
- depends on ARCH_DAVINCI_TNETV107X
- select INPUT_MATRIXKMAP
- help
- Say Y here if you want to use the TNETV107X keypad.
-
- To compile this driver as a module, choose M here: the
- module will be called tnetv107x-keypad.
-
config KEYBOARD_TWL4030
tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
depends on TWL4030_CORE
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index a699b6172303..11cff7b84b47 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
+obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o
obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
@@ -53,7 +54,6 @@ obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
-obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c
new file mode 100644
index 000000000000..3955aecee44b
--- /dev/null
+++ b/drivers/input/keyboard/clps711x-keypad.c
@@ -0,0 +1,207 @@
+/*
+ * Cirrus Logic CLPS711X Keypad driver
+ *
+ * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/sched.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/clps711x.h>
+
+#define CLPS711X_KEYPAD_COL_COUNT 8
+
+struct clps711x_gpio_data {
+ struct gpio_desc *desc;
+ DECLARE_BITMAP(last_state, CLPS711X_KEYPAD_COL_COUNT);
+};
+
+struct clps711x_keypad_data {
+ struct regmap *syscon;
+ int row_count;
+ unsigned int row_shift;
+ struct clps711x_gpio_data *gpio_data;
+};
+
+static void clps711x_keypad_poll(struct input_polled_dev *dev)
+{
+ const unsigned short *keycodes = dev->input->keycode;
+ struct clps711x_keypad_data *priv = dev->private;
+ bool sync = false;
+ int col, row;
+
+ for (col = 0; col < CLPS711X_KEYPAD_COL_COUNT; col++) {
+ /* Assert column */
+ regmap_update_bits(priv->syscon, SYSCON_OFFSET,
+ SYSCON1_KBDSCAN_MASK,
+ SYSCON1_KBDSCAN(8 + col));
+
+ /* Scan rows */
+ for (row = 0; row < priv->row_count; row++) {
+ struct clps711x_gpio_data *data = &priv->gpio_data[row];
+ bool state, state1;
+
+ /* Read twice for protection against fluctuations */
+ do {
+ state = gpiod_get_value_cansleep(data->desc);
+ cond_resched();
+ state1 = gpiod_get_value_cansleep(data->desc);
+ } while (state != state1);
+
+ if (test_bit(col, data->last_state) != state) {
+ int code = MATRIX_SCAN_CODE(row, col,
+ priv->row_shift);
+
+ if (state) {
+ set_bit(col, data->last_state);
+ input_event(dev->input, EV_MSC,
+ MSC_SCAN, code);
+ } else {
+ clear_bit(col, data->last_state);
+ }
+
+ if (keycodes[code])
+ input_report_key(dev->input,
+ keycodes[code], state);
+ sync = true;
+ }
+ }
+
+ /* Set all columns to low */
+ regmap_update_bits(priv->syscon, SYSCON_OFFSET,
+ SYSCON1_KBDSCAN_MASK, SYSCON1_KBDSCAN(1));
+ }
+
+ if (sync)
+ input_sync(dev->input);
+}
+
+static int clps711x_keypad_probe(struct platform_device *pdev)
+{
+ struct clps711x_keypad_data *priv;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct input_polled_dev *poll_dev;
+ u32 poll_interval;
+ int i, err;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->syscon =
+ syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1");
+ if (IS_ERR(priv->syscon))
+ return PTR_ERR(priv->syscon);
+
+ priv->row_count = of_gpio_named_count(np, "row-gpios");
+ if (priv->row_count < 1)
+ return -EINVAL;
+
+ priv->gpio_data = devm_kzalloc(dev,
+ sizeof(*priv->gpio_data) * priv->row_count,
+ GFP_KERNEL);
+ if (!priv->gpio_data)
+ return -ENOMEM;
+
+ priv->row_shift = get_count_order(CLPS711X_KEYPAD_COL_COUNT);
+
+ for (i = 0; i < priv->row_count; i++) {
+ struct clps711x_gpio_data *data = &priv->gpio_data[i];
+
+ data->desc = devm_gpiod_get_index(dev, "row", i);
+ if (!data->desc)
+ return -EINVAL;
+
+ if (IS_ERR(data->desc))
+ return PTR_ERR(data->desc);
+
+ gpiod_direction_input(data->desc);
+ }
+
+ err = of_property_read_u32(np, "poll-interval", &poll_interval);
+ if (err)
+ return err;
+
+ poll_dev = input_allocate_polled_device();
+ if (!poll_dev)
+ return -ENOMEM;
+
+ poll_dev->private = priv;
+ poll_dev->poll = clps711x_keypad_poll;
+ poll_dev->poll_interval = poll_interval;
+ poll_dev->input->name = pdev->name;
+ poll_dev->input->dev.parent = dev;
+ poll_dev->input->id.bustype = BUS_HOST;
+ poll_dev->input->id.vendor = 0x0001;
+ poll_dev->input->id.product = 0x0001;
+ poll_dev->input->id.version = 0x0100;
+
+ err = matrix_keypad_build_keymap(NULL, NULL, priv->row_count,
+ CLPS711X_KEYPAD_COL_COUNT,
+ NULL, poll_dev->input);
+ if (err)
+ goto out_err;
+
+ input_set_capability(poll_dev->input, EV_MSC, MSC_SCAN);
+ if (of_property_read_bool(np, "autorepeat"))
+ __set_bit(EV_REP, poll_dev->input->evbit);
+
+ platform_set_drvdata(pdev, poll_dev);
+
+ /* Set all columns to low */
+ regmap_update_bits(priv->syscon, SYSCON_OFFSET, SYSCON1_KBDSCAN_MASK,
+ SYSCON1_KBDSCAN(1));
+
+ err = input_register_polled_device(poll_dev);
+ if (err)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ input_free_polled_device(poll_dev);
+ return err;
+}
+
+static int clps711x_keypad_remove(struct platform_device *pdev)
+{
+ struct input_polled_dev *poll_dev = platform_get_drvdata(pdev);
+
+ input_unregister_polled_device(poll_dev);
+ input_free_polled_device(poll_dev);
+
+ return 0;
+}
+
+static struct of_device_id clps711x_keypad_of_match[] = {
+ { .compatible = "cirrus,clps711x-keypad", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match);
+
+static struct platform_driver clps711x_keypad_driver = {
+ .driver = {
+ .name = "clps711x-keypad",
+ .owner = THIS_MODULE,
+ .of_match_table = clps711x_keypad_of_match,
+ },
+ .probe = clps711x_keypad_probe,
+ .remove = clps711x_keypad_remove,
+};
+module_platform_driver(clps711x_keypad_driver);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CLPS711X Keypad driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c
index cbf4f8038cba..97ec33572e56 100644
--- a/drivers/input/keyboard/imx_keypad.c
+++ b/drivers/input/keyboard/imx_keypad.c
@@ -439,7 +439,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq defined in platform data\n");
- return -EINVAL;
+ return irq;
}
input_dev = devm_input_allocate_device(&pdev->dev);
@@ -449,7 +449,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
}
keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad),
- GFP_KERNEL);
+ GFP_KERNEL);
if (!keypad) {
dev_err(&pdev->dev, "not enough memory for driver data\n");
return -ENOMEM;
diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
index 2c9f19ac35ea..80c6b0ef3fc8 100644
--- a/drivers/input/keyboard/pmic8xxx-keypad.c
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -19,10 +19,9 @@
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/mutex.h>
-
-#include <linux/mfd/pm8xxx/core.h>
-#include <linux/mfd/pm8xxx/gpio.h>
-#include <linux/input/pmic8xxx-keypad.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/input/matrix_keypad.h>
#define PM8XXX_MAX_ROWS 18
#define PM8XXX_MAX_COLS 8
@@ -85,8 +84,10 @@
/**
* struct pmic8xxx_kp - internal keypad data structure
- * @pdata - keypad platform data pointer
+ * @num_cols - number of columns of keypad
+ * @num_rows - number of row of keypad
* @input - input device pointer for keypad
+ * @regmap - regmap handle
* @key_sense_irq - key press/release irq number
* @key_stuck_irq - key stuck notification irq number
* @keycodes - array to hold the key codes
@@ -96,8 +97,10 @@
* @ctrl_reg - control register value
*/
struct pmic8xxx_kp {
- const struct pm8xxx_keypad_platform_data *pdata;
+ unsigned int num_rows;
+ unsigned int num_cols;
struct input_dev *input;
+ struct regmap *regmap;
int key_sense_irq;
int key_stuck_irq;
@@ -110,40 +113,13 @@ struct pmic8xxx_kp {
u8 ctrl_reg;
};
-static int pmic8xxx_kp_write_u8(struct pmic8xxx_kp *kp,
- u8 data, u16 reg)
-{
- int rc;
-
- rc = pm8xxx_writeb(kp->dev->parent, reg, data);
- return rc;
-}
-
-static int pmic8xxx_kp_read(struct pmic8xxx_kp *kp,
- u8 *data, u16 reg, unsigned num_bytes)
-{
- int rc;
-
- rc = pm8xxx_read_buf(kp->dev->parent, reg, data, num_bytes);
- return rc;
-}
-
-static int pmic8xxx_kp_read_u8(struct pmic8xxx_kp *kp,
- u8 *data, u16 reg)
-{
- int rc;
-
- rc = pmic8xxx_kp_read(kp, data, reg, 1);
- return rc;
-}
-
static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col)
{
/* all keys pressed on that particular row? */
if (col == 0x00)
- return 1 << kp->pdata->num_cols;
+ return 1 << kp->num_cols;
else
- return col & ((1 << kp->pdata->num_cols) - 1);
+ return col & ((1 << kp->num_cols) - 1);
}
/*
@@ -161,9 +137,9 @@ static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col)
static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp)
{
int rc;
- u8 scan_val;
+ unsigned int scan_val;
- rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN);
+ rc = regmap_read(kp->regmap, KEYP_SCAN, &scan_val);
if (rc < 0) {
dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
return rc;
@@ -171,7 +147,7 @@ static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp)
scan_val |= 0x1;
- rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
+ rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val);
if (rc < 0) {
dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
return rc;
@@ -187,31 +163,29 @@ static int pmic8xxx_kp_read_data(struct pmic8xxx_kp *kp, u16 *state,
u16 data_reg, int read_rows)
{
int rc, row;
- u8 new_data[PM8XXX_MAX_ROWS];
-
- rc = pmic8xxx_kp_read(kp, new_data, data_reg, read_rows);
- if (rc)
- return rc;
+ unsigned int val;
- for (row = 0; row < kp->pdata->num_rows; row++) {
- dev_dbg(kp->dev, "new_data[%d] = %d\n", row,
- new_data[row]);
- state[row] = pmic8xxx_col_state(kp, new_data[row]);
+ for (row = 0; row < read_rows; row++) {
+ rc = regmap_read(kp->regmap, data_reg, &val);
+ if (rc)
+ return rc;
+ dev_dbg(kp->dev, "%d = %d\n", row, val);
+ state[row] = pmic8xxx_col_state(kp, val);
}
- return rc;
+ return 0;
}
static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
u16 *old_state)
{
int rc, read_rows;
- u8 scan_val;
+ unsigned int scan_val;
- if (kp->pdata->num_rows < PM8XXX_MIN_ROWS)
+ if (kp->num_rows < PM8XXX_MIN_ROWS)
read_rows = PM8XXX_MIN_ROWS;
else
- read_rows = kp->pdata->num_rows;
+ read_rows = kp->num_rows;
pmic8xxx_chk_sync_read(kp);
@@ -236,14 +210,14 @@ static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
/* 4 * 32KHz clocks */
udelay((4 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
- rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN);
+ rc = regmap_read(kp->regmap, KEYP_SCAN, &scan_val);
if (rc < 0) {
dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
return rc;
}
scan_val &= 0xFE;
- rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
+ rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val);
if (rc < 0)
dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
@@ -255,13 +229,13 @@ static void __pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
{
int row, col, code;
- for (row = 0; row < kp->pdata->num_rows; row++) {
+ for (row = 0; row < kp->num_rows; row++) {
int bits_changed = new_state[row] ^ old_state[row];
if (!bits_changed)
continue;
- for (col = 0; col < kp->pdata->num_cols; col++) {
+ for (col = 0; col < kp->num_cols; col++) {
if (!(bits_changed & (1 << col)))
continue;
@@ -287,9 +261,9 @@ static bool pmic8xxx_detect_ghost_keys(struct pmic8xxx_kp *kp, u16 *new_state)
u16 check, row_state;
check = 0;
- for (row = 0; row < kp->pdata->num_rows; row++) {
+ for (row = 0; row < kp->num_rows; row++) {
row_state = (~new_state[row]) &
- ((1 << kp->pdata->num_cols) - 1);
+ ((1 << kp->num_cols) - 1);
if (hweight16(row_state) > 1) {
if (found_first == -1)
@@ -379,10 +353,10 @@ static irqreturn_t pmic8xxx_kp_stuck_irq(int irq, void *data)
static irqreturn_t pmic8xxx_kp_irq(int irq, void *data)
{
struct pmic8xxx_kp *kp = data;
- u8 ctrl_val, events;
+ unsigned int ctrl_val, events;
int rc;
- rc = pmic8xxx_kp_read(kp, &ctrl_val, KEYP_CTRL, 1);
+ rc = regmap_read(kp->regmap, KEYP_CTRL, &ctrl_val);
if (rc < 0) {
dev_err(kp->dev, "failed to read keyp_ctrl register\n");
return IRQ_HANDLED;
@@ -397,8 +371,13 @@ static irqreturn_t pmic8xxx_kp_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp)
+static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp,
+ struct platform_device *pdev)
{
+ const struct device_node *of_node = pdev->dev.of_node;
+ unsigned int scan_delay_ms;
+ unsigned int row_hold_ns;
+ unsigned int debounce_ms;
int bits, rc, cycles;
u8 scan_val = 0, ctrl_val = 0;
static const u8 row_bits[] = {
@@ -406,40 +385,69 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp)
};
/* Find column bits */
- if (kp->pdata->num_cols < KEYP_CTRL_SCAN_COLS_MIN)
+ if (kp->num_cols < KEYP_CTRL_SCAN_COLS_MIN)
bits = 0;
else
- bits = kp->pdata->num_cols - KEYP_CTRL_SCAN_COLS_MIN;
+ bits = kp->num_cols - KEYP_CTRL_SCAN_COLS_MIN;
ctrl_val = (bits & KEYP_CTRL_SCAN_COLS_BITS) <<
KEYP_CTRL_SCAN_COLS_SHIFT;
/* Find row bits */
- if (kp->pdata->num_rows < KEYP_CTRL_SCAN_ROWS_MIN)
+ if (kp->num_rows < KEYP_CTRL_SCAN_ROWS_MIN)
bits = 0;
else
- bits = row_bits[kp->pdata->num_rows - KEYP_CTRL_SCAN_ROWS_MIN];
+ bits = row_bits[kp->num_rows - KEYP_CTRL_SCAN_ROWS_MIN];
ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT);
- rc = pmic8xxx_kp_write_u8(kp, ctrl_val, KEYP_CTRL);
+ rc = regmap_write(kp->regmap, KEYP_CTRL, ctrl_val);
if (rc < 0) {
dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc);
return rc;
}
- bits = (kp->pdata->debounce_ms / 5) - 1;
+ if (of_property_read_u32(of_node, "scan-delay", &scan_delay_ms))
+ scan_delay_ms = MIN_SCAN_DELAY;
+
+ if (scan_delay_ms > MAX_SCAN_DELAY || scan_delay_ms < MIN_SCAN_DELAY ||
+ !is_power_of_2(scan_delay_ms)) {
+ dev_err(&pdev->dev, "invalid keypad scan time supplied\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(of_node, "row-hold", &row_hold_ns))
+ row_hold_ns = MIN_ROW_HOLD_DELAY;
+
+ if (row_hold_ns > MAX_ROW_HOLD_DELAY ||
+ row_hold_ns < MIN_ROW_HOLD_DELAY ||
+ ((row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) {
+ dev_err(&pdev->dev, "invalid keypad row hold time supplied\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(of_node, "debounce", &debounce_ms))
+ debounce_ms = MIN_DEBOUNCE_TIME;
+
+ if (((debounce_ms % 5) != 0) ||
+ debounce_ms > MAX_DEBOUNCE_TIME ||
+ debounce_ms < MIN_DEBOUNCE_TIME) {
+ dev_err(&pdev->dev, "invalid debounce time supplied\n");
+ return -EINVAL;
+ }
+
+ bits = (debounce_ms / 5) - 1;
scan_val |= (bits << KEYP_SCAN_DBOUNCE_SHIFT);
- bits = fls(kp->pdata->scan_delay_ms) - 1;
+ bits = fls(scan_delay_ms) - 1;
scan_val |= (bits << KEYP_SCAN_PAUSE_SHIFT);
/* Row hold time is a multiple of 32KHz cycles. */
- cycles = (kp->pdata->row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC;
+ cycles = (row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC;
scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT);
- rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
+ rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val);
if (rc)
dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
@@ -447,34 +455,13 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp)
}
-static int pmic8xxx_kp_config_gpio(int gpio_start, int num_gpios,
- struct pmic8xxx_kp *kp, struct pm_gpio *gpio_config)
-{
- int rc, i;
-
- if (gpio_start < 0 || num_gpios < 0)
- return -EINVAL;
-
- for (i = 0; i < num_gpios; i++) {
- rc = pm8xxx_gpio_config(gpio_start + i, gpio_config);
- if (rc) {
- dev_err(kp->dev, "%s: FAIL pm8xxx_gpio_config():"
- "for PM GPIO [%d] rc=%d.\n",
- __func__, gpio_start + i, rc);
- return rc;
- }
- }
-
- return 0;
-}
-
static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp)
{
int rc;
kp->ctrl_reg |= KEYP_CTRL_KEYP_EN;
- rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
+ rc = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg);
if (rc < 0)
dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc);
@@ -487,7 +474,7 @@ static int pmic8xxx_kp_disable(struct pmic8xxx_kp *kp)
kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN;
- rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
+ rc = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg);
if (rc < 0)
return rc;
@@ -520,106 +507,62 @@ static void pmic8xxx_kp_close(struct input_dev *dev)
*/
static int pmic8xxx_kp_probe(struct platform_device *pdev)
{
- const struct pm8xxx_keypad_platform_data *pdata =
- dev_get_platdata(&pdev->dev);
- const struct matrix_keymap_data *keymap_data;
+ unsigned int rows, cols;
+ bool repeat;
+ bool wakeup;
struct pmic8xxx_kp *kp;
int rc;
- u8 ctrl_val;
-
- struct pm_gpio kypd_drv = {
- .direction = PM_GPIO_DIR_OUT,
- .output_buffer = PM_GPIO_OUT_BUF_OPEN_DRAIN,
- .output_value = 0,
- .pull = PM_GPIO_PULL_NO,
- .vin_sel = PM_GPIO_VIN_S3,
- .out_strength = PM_GPIO_STRENGTH_LOW,
- .function = PM_GPIO_FUNC_1,
- .inv_int_pol = 1,
- };
-
- struct pm_gpio kypd_sns = {
- .direction = PM_GPIO_DIR_IN,
- .pull = PM_GPIO_PULL_UP_31P5,
- .vin_sel = PM_GPIO_VIN_S3,
- .out_strength = PM_GPIO_STRENGTH_NO,
- .function = PM_GPIO_FUNC_NORMAL,
- .inv_int_pol = 1,
- };
+ unsigned int ctrl_val;
+ rc = matrix_keypad_parse_of_params(&pdev->dev, &rows, &cols);
+ if (rc)
+ return rc;
- if (!pdata || !pdata->num_cols || !pdata->num_rows ||
- pdata->num_cols > PM8XXX_MAX_COLS ||
- pdata->num_rows > PM8XXX_MAX_ROWS ||
- pdata->num_cols < PM8XXX_MIN_COLS) {
+ if (cols > PM8XXX_MAX_COLS || rows > PM8XXX_MAX_ROWS ||
+ cols < PM8XXX_MIN_COLS) {
dev_err(&pdev->dev, "invalid platform data\n");
return -EINVAL;
}
- if (!pdata->scan_delay_ms ||
- pdata->scan_delay_ms > MAX_SCAN_DELAY ||
- pdata->scan_delay_ms < MIN_SCAN_DELAY ||
- !is_power_of_2(pdata->scan_delay_ms)) {
- dev_err(&pdev->dev, "invalid keypad scan time supplied\n");
- return -EINVAL;
- }
-
- if (!pdata->row_hold_ns ||
- pdata->row_hold_ns > MAX_ROW_HOLD_DELAY ||
- pdata->row_hold_ns < MIN_ROW_HOLD_DELAY ||
- ((pdata->row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) {
- dev_err(&pdev->dev, "invalid keypad row hold time supplied\n");
- return -EINVAL;
- }
-
- if (!pdata->debounce_ms ||
- ((pdata->debounce_ms % 5) != 0) ||
- pdata->debounce_ms > MAX_DEBOUNCE_TIME ||
- pdata->debounce_ms < MIN_DEBOUNCE_TIME) {
- dev_err(&pdev->dev, "invalid debounce time supplied\n");
- return -EINVAL;
- }
-
- keymap_data = pdata->keymap_data;
- if (!keymap_data) {
- dev_err(&pdev->dev, "no keymap data supplied\n");
- return -EINVAL;
- }
+ repeat = !of_property_read_bool(pdev->dev.of_node,
+ "linux,input-no-autorepeat");
+ wakeup = of_property_read_bool(pdev->dev.of_node,
+ "linux,keypad-wakeup");
- kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+ kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
if (!kp)
return -ENOMEM;
+ kp->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!kp->regmap)
+ return -ENODEV;
+
platform_set_drvdata(pdev, kp);
- kp->pdata = pdata;
+ kp->num_rows = rows;
+ kp->num_cols = cols;
kp->dev = &pdev->dev;
- kp->input = input_allocate_device();
+ kp->input = devm_input_allocate_device(&pdev->dev);
if (!kp->input) {
dev_err(&pdev->dev, "unable to allocate input device\n");
- rc = -ENOMEM;
- goto err_alloc_device;
+ return -ENOMEM;
}
kp->key_sense_irq = platform_get_irq(pdev, 0);
if (kp->key_sense_irq < 0) {
dev_err(&pdev->dev, "unable to get keypad sense irq\n");
- rc = -ENXIO;
- goto err_get_irq;
+ return kp->key_sense_irq;
}
kp->key_stuck_irq = platform_get_irq(pdev, 1);
if (kp->key_stuck_irq < 0) {
dev_err(&pdev->dev, "unable to get keypad stuck irq\n");
- rc = -ENXIO;
- goto err_get_irq;
+ return kp->key_stuck_irq;
}
- kp->input->name = pdata->input_name ? : "PMIC8XXX keypad";
- kp->input->phys = pdata->input_phys_device ? : "pmic8xxx_keypad/input0";
-
- kp->input->dev.parent = &pdev->dev;
+ kp->input->name = "PMIC8XXX keypad";
+ kp->input->phys = "pmic8xxx_keypad/input0";
kp->input->id.bustype = BUS_I2C;
kp->input->id.version = 0x0001;
@@ -629,15 +572,15 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
kp->input->open = pmic8xxx_kp_open;
kp->input->close = pmic8xxx_kp_close;
- rc = matrix_keypad_build_keymap(keymap_data, NULL,
+ rc = matrix_keypad_build_keymap(NULL, NULL,
PM8XXX_MAX_ROWS, PM8XXX_MAX_COLS,
kp->keycodes, kp->input);
if (rc) {
dev_err(&pdev->dev, "failed to build keymap\n");
- goto err_get_irq;
+ return rc;
}
- if (pdata->rep)
+ if (repeat)
__set_bit(EV_REP, kp->input->evbit);
input_set_capability(kp->input, EV_MSC, MSC_SCAN);
@@ -647,44 +590,32 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
memset(kp->keystate, 0xff, sizeof(kp->keystate));
memset(kp->stuckstate, 0xff, sizeof(kp->stuckstate));
- rc = pmic8xxx_kpd_init(kp);
+ rc = pmic8xxx_kpd_init(kp, pdev);
if (rc < 0) {
dev_err(&pdev->dev, "unable to initialize keypad controller\n");
- goto err_get_irq;
- }
-
- rc = pmic8xxx_kp_config_gpio(pdata->cols_gpio_start,
- pdata->num_cols, kp, &kypd_sns);
- if (rc < 0) {
- dev_err(&pdev->dev, "unable to configure keypad sense lines\n");
- goto err_gpio_config;
- }
-
- rc = pmic8xxx_kp_config_gpio(pdata->rows_gpio_start,
- pdata->num_rows, kp, &kypd_drv);
- if (rc < 0) {
- dev_err(&pdev->dev, "unable to configure keypad drive lines\n");
- goto err_gpio_config;
+ return rc;
}
- rc = request_any_context_irq(kp->key_sense_irq, pmic8xxx_kp_irq,
- IRQF_TRIGGER_RISING, "pmic-keypad", kp);
+ rc = devm_request_any_context_irq(&pdev->dev, kp->key_sense_irq,
+ pmic8xxx_kp_irq, IRQF_TRIGGER_RISING, "pmic-keypad",
+ kp);
if (rc < 0) {
dev_err(&pdev->dev, "failed to request keypad sense irq\n");
- goto err_get_irq;
+ return rc;
}
- rc = request_any_context_irq(kp->key_stuck_irq, pmic8xxx_kp_stuck_irq,
- IRQF_TRIGGER_RISING, "pmic-keypad-stuck", kp);
+ rc = devm_request_any_context_irq(&pdev->dev, kp->key_stuck_irq,
+ pmic8xxx_kp_stuck_irq, IRQF_TRIGGER_RISING,
+ "pmic-keypad-stuck", kp);
if (rc < 0) {
dev_err(&pdev->dev, "failed to request keypad stuck irq\n");
- goto err_req_stuck_irq;
+ return rc;
}
- rc = pmic8xxx_kp_read_u8(kp, &ctrl_val, KEYP_CTRL);
+ rc = regmap_read(kp->regmap, KEYP_CTRL, &ctrl_val);
if (rc < 0) {
dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n");
- goto err_pmic_reg_read;
+ return rc;
}
kp->ctrl_reg = ctrl_val;
@@ -692,34 +623,10 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
rc = input_register_device(kp->input);
if (rc < 0) {
dev_err(&pdev->dev, "unable to register keypad input device\n");
- goto err_pmic_reg_read;
+ return rc;
}
- device_init_wakeup(&pdev->dev, pdata->wakeup);
-
- return 0;
-
-err_pmic_reg_read:
- free_irq(kp->key_stuck_irq, kp);
-err_req_stuck_irq:
- free_irq(kp->key_sense_irq, kp);
-err_gpio_config:
-err_get_irq:
- input_free_device(kp->input);
-err_alloc_device:
- kfree(kp);
- return rc;
-}
-
-static int pmic8xxx_kp_remove(struct platform_device *pdev)
-{
- struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
-
- device_init_wakeup(&pdev->dev, 0);
- free_irq(kp->key_stuck_irq, kp);
- free_irq(kp->key_sense_irq, kp);
- input_unregister_device(kp->input);
- kfree(kp);
+ device_init_wakeup(&pdev->dev, wakeup);
return 0;
}
@@ -769,13 +676,20 @@ static int pmic8xxx_kp_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops,
pmic8xxx_kp_suspend, pmic8xxx_kp_resume);
+static const struct of_device_id pm8xxx_match_table[] = {
+ { .compatible = "qcom,pm8058-keypad" },
+ { .compatible = "qcom,pm8921-keypad" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_match_table);
+
static struct platform_driver pmic8xxx_kp_driver = {
.probe = pmic8xxx_kp_probe,
- .remove = pmic8xxx_kp_remove,
.driver = {
- .name = PM8XXX_KEYPAD_DEV_NAME,
+ .name = "pm8xxx-keypad",
.owner = THIS_MODULE,
.pm = &pm8xxx_kp_pm_ops,
+ .of_match_table = pm8xxx_match_table,
},
};
module_platform_driver(pmic8xxx_kp_driver);
diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c
deleted file mode 100644
index 086511c2121b..000000000000
--- a/drivers/input/keyboard/tnetv107x-keypad.c
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Texas Instruments TNETV107X Keypad Driver
- *
- * Copyright (C) 2010 Texas Instruments
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/errno.h>
-#include <linux/input.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/input/matrix_keypad.h>
-#include <linux/module.h>
-
-#define BITS(x) (BIT(x) - 1)
-
-#define KEYPAD_ROWS 9
-#define KEYPAD_COLS 9
-
-#define DEBOUNCE_MIN 0x400ul
-#define DEBOUNCE_MAX 0x3ffffffful
-
-struct keypad_regs {
- u32 rev;
- u32 mode;
- u32 mask;
- u32 pol;
- u32 dclock;
- u32 rclock;
- u32 stable_cnt;
- u32 in_en;
- u32 out;
- u32 out_en;
- u32 in;
- u32 lock;
- u32 pres[3];
-};
-
-#define keypad_read(kp, reg) __raw_readl(&(kp)->regs->reg)
-#define keypad_write(kp, reg, val) __raw_writel(val, &(kp)->regs->reg)
-
-struct keypad_data {
- struct input_dev *input_dev;
- struct resource *res;
- struct keypad_regs __iomem *regs;
- struct clk *clk;
- struct device *dev;
- spinlock_t lock;
- int irq_press;
- int irq_release;
- int rows, cols, row_shift;
- int debounce_ms, active_low;
- u32 prev_keys[3];
- unsigned short keycodes[];
-};
-
-static irqreturn_t keypad_irq(int irq, void *data)
-{
- struct keypad_data *kp = data;
- int i, bit, val, row, col, code;
- unsigned long flags;
- u32 curr_keys[3];
- u32 change;
-
- spin_lock_irqsave(&kp->lock, flags);
-
- memset(curr_keys, 0, sizeof(curr_keys));
- if (irq == kp->irq_press)
- for (i = 0; i < 3; i++)
- curr_keys[i] = keypad_read(kp, pres[i]);
-
- for (i = 0; i < 3; i++) {
- change = curr_keys[i] ^ kp->prev_keys[i];
-
- while (change) {
- bit = fls(change) - 1;
- change ^= BIT(bit);
- val = curr_keys[i] & BIT(bit);
- bit += i * 32;
- row = bit / KEYPAD_COLS;
- col = bit % KEYPAD_COLS;
-
- code = MATRIX_SCAN_CODE(row, col, kp->row_shift);
- input_event(kp->input_dev, EV_MSC, MSC_SCAN, code);
- input_report_key(kp->input_dev, kp->keycodes[code],
- val);
- }
- }
- input_sync(kp->input_dev);
- memcpy(kp->prev_keys, curr_keys, sizeof(curr_keys));
-
- if (irq == kp->irq_press)
- keypad_write(kp, lock, 0); /* Allow hardware updates */
-
- spin_unlock_irqrestore(&kp->lock, flags);
-
- return IRQ_HANDLED;
-}
-
-static int keypad_start(struct input_dev *dev)
-{
- struct keypad_data *kp = input_get_drvdata(dev);
- unsigned long mask, debounce, clk_rate_khz;
- unsigned long flags;
-
- clk_enable(kp->clk);
- clk_rate_khz = clk_get_rate(kp->clk) / 1000;
-
- spin_lock_irqsave(&kp->lock, flags);
-
- /* Initialize device registers */
- keypad_write(kp, mode, 0);
-
- mask = BITS(kp->rows) << KEYPAD_COLS;
- mask |= BITS(kp->cols);
- keypad_write(kp, mask, ~mask);
-
- keypad_write(kp, pol, kp->active_low ? 0 : 0x3ffff);
- keypad_write(kp, stable_cnt, 3);
-
- debounce = kp->debounce_ms * clk_rate_khz;
- debounce = clamp(debounce, DEBOUNCE_MIN, DEBOUNCE_MAX);
- keypad_write(kp, dclock, debounce);
- keypad_write(kp, rclock, 4 * debounce);
-
- keypad_write(kp, in_en, 1);
-
- spin_unlock_irqrestore(&kp->lock, flags);
-
- return 0;
-}
-
-static void keypad_stop(struct input_dev *dev)
-{
- struct keypad_data *kp = input_get_drvdata(dev);
-
- synchronize_irq(kp->irq_press);
- synchronize_irq(kp->irq_release);
- clk_disable(kp->clk);
-}
-
-static int keypad_probe(struct platform_device *pdev)
-{
- const struct matrix_keypad_platform_data *pdata;
- const struct matrix_keymap_data *keymap_data;
- struct device *dev = &pdev->dev;
- struct keypad_data *kp;
- int error = 0, sz, row_shift;
- u32 rev = 0;
-
- pdata = dev_get_platdata(&pdev->dev);
- if (!pdata) {
- dev_err(dev, "cannot find device data\n");
- return -EINVAL;
- }
-
- keymap_data = pdata->keymap_data;
- if (!keymap_data) {
- dev_err(dev, "cannot find keymap data\n");
- return -EINVAL;
- }
-
- row_shift = get_count_order(pdata->num_col_gpios);
- sz = offsetof(struct keypad_data, keycodes);
- sz += (pdata->num_row_gpios << row_shift) * sizeof(kp->keycodes[0]);
- kp = kzalloc(sz, GFP_KERNEL);
- if (!kp) {
- dev_err(dev, "cannot allocate device info\n");
- return -ENOMEM;
- }
-
- kp->dev = dev;
- kp->rows = pdata->num_row_gpios;
- kp->cols = pdata->num_col_gpios;
- kp->row_shift = row_shift;
- platform_set_drvdata(pdev, kp);
- spin_lock_init(&kp->lock);
-
- kp->irq_press = platform_get_irq_byname(pdev, "press");
- kp->irq_release = platform_get_irq_byname(pdev, "release");
- if (kp->irq_press < 0 || kp->irq_release < 0) {
- dev_err(dev, "cannot determine device interrupts\n");
- error = -ENODEV;
- goto error_res;
- }
-
- kp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!kp->res) {
- dev_err(dev, "cannot determine register area\n");
- error = -ENODEV;
- goto error_res;
- }
-
- if (!request_mem_region(kp->res->start, resource_size(kp->res),
- pdev->name)) {
- dev_err(dev, "cannot claim register memory\n");
- kp->res = NULL;
- error = -EINVAL;
- goto error_res;
- }
-
- kp->regs = ioremap(kp->res->start, resource_size(kp->res));
- if (!kp->regs) {
- dev_err(dev, "cannot map register memory\n");
- error = -ENOMEM;
- goto error_map;
- }
-
- kp->clk = clk_get(dev, NULL);
- if (IS_ERR(kp->clk)) {
- dev_err(dev, "cannot claim device clock\n");
- error = PTR_ERR(kp->clk);
- goto error_clk;
- }
-
- error = request_threaded_irq(kp->irq_press, NULL, keypad_irq,
- IRQF_ONESHOT, dev_name(dev), kp);
- if (error < 0) {
- dev_err(kp->dev, "Could not allocate keypad press key irq\n");
- goto error_irq_press;
- }
-
- error = request_threaded_irq(kp->irq_release, NULL, keypad_irq,
- IRQF_ONESHOT, dev_name(dev), kp);
- if (error < 0) {
- dev_err(kp->dev, "Could not allocate keypad release key irq\n");
- goto error_irq_release;
- }
-
- kp->input_dev = input_allocate_device();
- if (!kp->input_dev) {
- dev_err(dev, "cannot allocate input device\n");
- error = -ENOMEM;
- goto error_input;
- }
-
- kp->input_dev->name = pdev->name;
- kp->input_dev->dev.parent = &pdev->dev;
- kp->input_dev->open = keypad_start;
- kp->input_dev->close = keypad_stop;
-
- clk_enable(kp->clk);
- rev = keypad_read(kp, rev);
- kp->input_dev->id.bustype = BUS_HOST;
- kp->input_dev->id.product = ((rev >> 8) & 0x07);
- kp->input_dev->id.version = ((rev >> 16) & 0xfff);
- clk_disable(kp->clk);
-
- error = matrix_keypad_build_keymap(keymap_data, NULL,
- kp->rows, kp->cols,
- kp->keycodes, kp->input_dev);
- if (error) {
- dev_err(dev, "Failed to build keymap\n");
- goto error_reg;
- }
-
- if (!pdata->no_autorepeat)
- kp->input_dev->evbit[0] |= BIT_MASK(EV_REP);
- input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN);
-
- input_set_drvdata(kp->input_dev, kp);
-
- error = input_register_device(kp->input_dev);
- if (error < 0) {
- dev_err(dev, "Could not register input device\n");
- goto error_reg;
- }
-
- return 0;
-
-
-error_reg:
- input_free_device(kp->input_dev);
-error_input:
- free_irq(kp->irq_release, kp);
-error_irq_release:
- free_irq(kp->irq_press, kp);
-error_irq_press:
- clk_put(kp->clk);
-error_clk:
- iounmap(kp->regs);
-error_map:
- release_mem_region(kp->res->start, resource_size(kp->res));
-error_res:
- kfree(kp);
- return error;
-}
-
-static int keypad_remove(struct platform_device *pdev)
-{
- struct keypad_data *kp = platform_get_drvdata(pdev);
-
- free_irq(kp->irq_press, kp);
- free_irq(kp->irq_release, kp);
- input_unregister_device(kp->input_dev);
- clk_put(kp->clk);
- iounmap(kp->regs);
- release_mem_region(kp->res->start, resource_size(kp->res));
- kfree(kp);
-
- return 0;
-}
-
-static struct platform_driver keypad_driver = {
- .probe = keypad_probe,
- .remove = keypad_remove,
- .driver.name = "tnetv107x-keypad",
- .driver.owner = THIS_MODULE,
-};
-module_platform_driver(keypad_driver);
-
-MODULE_AUTHOR("Cyril Chemparathy");
-MODULE_DESCRIPTION("TNETV107X Keypad Driver");
-MODULE_ALIAS("platform:tnetv107x-keypad");
-MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 7904ab05527a..5928ea71dd69 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -156,7 +156,7 @@ config INPUT_MAX8925_ONKEY
config INPUT_MAX8997_HAPTIC
tristate "MAXIM MAX8997 haptic controller support"
- depends on PWM && HAVE_PWM && MFD_MAX8997
+ depends on PWM && MFD_MAX8997
select INPUT_FF_MEMLESS
help
This option enables device driver support for the haptic controller
@@ -269,7 +269,7 @@ config INPUT_COBALT_BTNS
config INPUT_WISTRON_BTNS
tristate "x86 Wistron laptop button interface"
- depends on X86 && !X86_64
+ depends on X86_32
select INPUT_POLLDEV
select INPUT_SPARSEKMAP
select NEW_LEDS
@@ -470,7 +470,7 @@ config INPUT_PCF8574
config INPUT_PWM_BEEPER
tristate "PWM beeper support"
- depends on PWM && HAVE_PWM
+ depends on PWM
help
Say Y here to get support for PWM based beeper devices.
@@ -666,4 +666,14 @@ config INPUT_IDEAPAD_SLIDEBAR
To compile this driver as a module, choose M here: the
module will be called ideapad_slidebar.
+config INPUT_SOC_BUTTON_ARRAY
+ tristate "Windows-compatible SoC Button Array"
+ depends on KEYBOARD_GPIO
+ help
+ Say Y here if you have a SoC-based tablet that originally
+ runs Windows 8.
+
+ To compile this driver as a module, choose M here: the
+ module will be called soc_button_array.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index cda71fc52fb3..4955ad322a01 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o
+obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index e204f26b0011..5a736397d9c8 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -51,6 +51,8 @@ struct ims_pcu_backlight {
#define IMS_PCU_BL_VERSION_LEN (9 + 1)
#define IMS_PCU_BL_RESET_REASON_LEN (2 + 1)
+#define IMS_PCU_PCU_B_DEVICE_ID 5
+
#define IMS_PCU_BUF_SIZE 128
struct ims_pcu {
@@ -68,6 +70,9 @@ struct ims_pcu {
char bl_version[IMS_PCU_BL_VERSION_LEN];
char reset_reason[IMS_PCU_BL_RESET_REASON_LEN];
int update_firmware_status;
+ u8 device_id;
+
+ u8 ofn_reg_addr;
struct usb_interface *ctrl_intf;
@@ -371,6 +376,8 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
#define IMS_PCU_CMD_GET_DEVICE_ID 0xae
#define IMS_PCU_CMD_SPECIAL_INFO 0xb0
#define IMS_PCU_CMD_BOOTLOADER 0xb1 /* Pass data to bootloader */
+#define IMS_PCU_CMD_OFN_SET_CONFIG 0xb3
+#define IMS_PCU_CMD_OFN_GET_CONFIG 0xb4
/* PCU responses */
#define IMS_PCU_RSP_STATUS 0xc0
@@ -389,6 +396,9 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
#define IMS_PCU_RSP_GET_DEVICE_ID 0xce
#define IMS_PCU_RSP_SPECIAL_INFO 0xd0
#define IMS_PCU_RSP_BOOTLOADER 0xd1 /* Bootloader response */
+#define IMS_PCU_RSP_OFN_SET_CONFIG 0xd2
+#define IMS_PCU_RSP_OFN_GET_CONFIG 0xd3
+
#define IMS_PCU_RSP_EVNT_BUTTONS 0xe0 /* Unsolicited, button state */
#define IMS_PCU_GAMEPAD_MASK 0x0001ff80UL /* Bits 7 through 16 */
@@ -1256,6 +1266,225 @@ static struct attribute_group ims_pcu_attr_group = {
.attrs = ims_pcu_attrs,
};
+/* Support for a separate OFN attribute group */
+
+#define OFN_REG_RESULT_OFFSET 2
+
+static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data)
+{
+ int error;
+ s16 result;
+
+ error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG,
+ &addr, sizeof(addr));
+ if (error)
+ return error;
+
+ result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
+ if (result < 0)
+ return -EIO;
+
+ /* We only need LSB */
+ *data = pcu->cmd_buf[OFN_REG_RESULT_OFFSET];
+ return 0;
+}
+
+static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data)
+{
+ u8 buffer[] = { addr, data };
+ int error;
+ s16 result;
+
+ error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG,
+ &buffer, sizeof(buffer));
+ if (error)
+ return error;
+
+ result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
+ if (result < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static ssize_t ims_pcu_ofn_reg_data_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ int error;
+ u8 data;
+
+ mutex_lock(&pcu->cmd_mutex);
+ error = ims_pcu_read_ofn_config(pcu, pcu->ofn_reg_addr, &data);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ if (error)
+ return error;
+
+ return scnprintf(buf, PAGE_SIZE, "%x\n", data);
+}
+
+static ssize_t ims_pcu_ofn_reg_data_store(struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ int error;
+ u8 value;
+
+ error = kstrtou8(buf, 0, &value);
+ if (error)
+ return error;
+
+ mutex_lock(&pcu->cmd_mutex);
+ error = ims_pcu_write_ofn_config(pcu, pcu->ofn_reg_addr, value);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error ?: count;
+}
+
+static DEVICE_ATTR(reg_data, S_IRUGO | S_IWUSR,
+ ims_pcu_ofn_reg_data_show, ims_pcu_ofn_reg_data_store);
+
+static ssize_t ims_pcu_ofn_reg_addr_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ int error;
+
+ mutex_lock(&pcu->cmd_mutex);
+ error = scnprintf(buf, PAGE_SIZE, "%x\n", pcu->ofn_reg_addr);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error;
+}
+
+static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ int error;
+ u8 value;
+
+ error = kstrtou8(buf, 0, &value);
+ if (error)
+ return error;
+
+ mutex_lock(&pcu->cmd_mutex);
+ pcu->ofn_reg_addr = value;
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error ?: count;
+}
+
+static DEVICE_ATTR(reg_addr, S_IRUGO | S_IWUSR,
+ ims_pcu_ofn_reg_addr_show, ims_pcu_ofn_reg_addr_store);
+
+struct ims_pcu_ofn_bit_attribute {
+ struct device_attribute dattr;
+ u8 addr;
+ u8 nr;
+};
+
+static ssize_t ims_pcu_ofn_bit_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ struct ims_pcu_ofn_bit_attribute *attr =
+ container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr);
+ int error;
+ u8 data;
+
+ mutex_lock(&pcu->cmd_mutex);
+ error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ if (error)
+ return error;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", !!(data & (1 << attr->nr)));
+}
+
+static ssize_t ims_pcu_ofn_bit_store(struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ struct ims_pcu_ofn_bit_attribute *attr =
+ container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr);
+ int error;
+ int value;
+ u8 data;
+
+ error = kstrtoint(buf, 0, &value);
+ if (error)
+ return error;
+
+ if (value > 1)
+ return -EINVAL;
+
+ mutex_lock(&pcu->cmd_mutex);
+
+ error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
+ if (!error) {
+ if (value)
+ data |= 1U << attr->nr;
+ else
+ data &= ~(1U << attr->nr);
+
+ error = ims_pcu_write_ofn_config(pcu, attr->addr, data);
+ }
+
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error ?: count;
+}
+
+#define IMS_PCU_OFN_BIT_ATTR(_field, _addr, _nr) \
+struct ims_pcu_ofn_bit_attribute ims_pcu_ofn_attr_##_field = { \
+ .dattr = __ATTR(_field, S_IWUSR | S_IRUGO, \
+ ims_pcu_ofn_bit_show, ims_pcu_ofn_bit_store), \
+ .addr = _addr, \
+ .nr = _nr, \
+}
+
+static IMS_PCU_OFN_BIT_ATTR(engine_enable, 0x60, 7);
+static IMS_PCU_OFN_BIT_ATTR(speed_enable, 0x60, 6);
+static IMS_PCU_OFN_BIT_ATTR(assert_enable, 0x60, 5);
+static IMS_PCU_OFN_BIT_ATTR(xyquant_enable, 0x60, 4);
+static IMS_PCU_OFN_BIT_ATTR(xyscale_enable, 0x60, 1);
+
+static IMS_PCU_OFN_BIT_ATTR(scale_x2, 0x63, 6);
+static IMS_PCU_OFN_BIT_ATTR(scale_y2, 0x63, 7);
+
+static struct attribute *ims_pcu_ofn_attrs[] = {
+ &dev_attr_reg_data.attr,
+ &dev_attr_reg_addr.attr,
+ &ims_pcu_ofn_attr_engine_enable.dattr.attr,
+ &ims_pcu_ofn_attr_speed_enable.dattr.attr,
+ &ims_pcu_ofn_attr_assert_enable.dattr.attr,
+ &ims_pcu_ofn_attr_xyquant_enable.dattr.attr,
+ &ims_pcu_ofn_attr_xyscale_enable.dattr.attr,
+ &ims_pcu_ofn_attr_scale_x2.dattr.attr,
+ &ims_pcu_ofn_attr_scale_y2.dattr.attr,
+ NULL
+};
+
+static struct attribute_group ims_pcu_ofn_attr_group = {
+ .name = "ofn",
+ .attrs = ims_pcu_ofn_attrs,
+};
+
static void ims_pcu_irq(struct urb *urb)
{
struct ims_pcu *pcu = urb->context;
@@ -1624,7 +1853,6 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
static atomic_t device_no = ATOMIC_INIT(0);
const struct ims_pcu_device_info *info;
- u8 device_id;
int error;
error = ims_pcu_get_device_info(pcu);
@@ -1633,7 +1861,7 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
return error;
}
- error = ims_pcu_identify_type(pcu, &device_id);
+ error = ims_pcu_identify_type(pcu, &pcu->device_id);
if (error) {
dev_err(pcu->dev,
"Failed to identify device, error: %d\n", error);
@@ -1645,9 +1873,9 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
return 0;
}
- if (device_id >= ARRAY_SIZE(ims_pcu_device_info) ||
- !ims_pcu_device_info[device_id].keymap) {
- dev_err(pcu->dev, "Device ID %d is not valid\n", device_id);
+ if (pcu->device_id >= ARRAY_SIZE(ims_pcu_device_info) ||
+ !ims_pcu_device_info[pcu->device_id].keymap) {
+ dev_err(pcu->dev, "Device ID %d is not valid\n", pcu->device_id);
/* Same as above, punt to userspace */
return 0;
}
@@ -1655,11 +1883,21 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
/* Device appears to be operable, complete initialization */
pcu->device_no = atomic_inc_return(&device_no) - 1;
+ /*
+ * PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor
+ */
+ if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) {
+ error = sysfs_create_group(&pcu->dev->kobj,
+ &ims_pcu_ofn_attr_group);
+ if (error)
+ return error;
+ }
+
error = ims_pcu_setup_backlight(pcu);
if (error)
return error;
- info = &ims_pcu_device_info[device_id];
+ info = &ims_pcu_device_info[pcu->device_id];
error = ims_pcu_setup_buttons(pcu, info->keymap, info->keymap_len);
if (error)
goto err_destroy_backlight;
@@ -1674,10 +1912,10 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
return 0;
-err_destroy_backlight:
- ims_pcu_destroy_backlight(pcu);
err_destroy_buttons:
ims_pcu_destroy_buttons(pcu);
+err_destroy_backlight:
+ ims_pcu_destroy_backlight(pcu);
return error;
}
@@ -1691,6 +1929,10 @@ static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu)
ims_pcu_destroy_gamepad(pcu);
ims_pcu_destroy_buttons(pcu);
ims_pcu_destroy_backlight(pcu);
+
+ if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID)
+ sysfs_remove_group(&pcu->dev->kobj,
+ &ims_pcu_ofn_attr_group);
}
}
diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c
index 17ccba88d636..ed8e5e8449d3 100644
--- a/drivers/input/misc/ixp4xx-beeper.c
+++ b/drivers/input/misc/ixp4xx-beeper.c
@@ -67,7 +67,7 @@ static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned
}
if (value > 20 && value < 32767)
- count = (IXP4XX_TIMER_FREQ / (value * 4)) - 1;
+ count = (ixp4xx_timer_freq / (value * 4)) - 1;
ixp4xx_spkr_control(pin, count);
diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c
index b88b7cbf93e2..6a915ba31bba 100644
--- a/drivers/input/misc/pm8xxx-vibrator.c
+++ b/drivers/input/misc/pm8xxx-vibrator.c
@@ -142,7 +142,6 @@ static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data,
}
static int pm8xxx_vib_probe(struct platform_device *pdev)
-
{
struct pm8xxx_vib *vib;
struct input_dev *input_dev;
@@ -214,12 +213,20 @@ static int pm8xxx_vib_suspend(struct device *dev)
static SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL);
+static const struct of_device_id pm8xxx_vib_id_table[] = {
+ { .compatible = "qcom,pm8058-vib" },
+ { .compatible = "qcom,pm8921-vib" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_vib_id_table);
+
static struct platform_driver pm8xxx_vib_driver = {
.probe = pm8xxx_vib_probe,
.driver = {
.name = "pm8xxx-vib",
.owner = THIS_MODULE,
.pm = &pm8xxx_vib_pm_ops,
+ .of_match_table = pm8xxx_vib_id_table,
},
};
module_platform_driver(pm8xxx_vib_driver);
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
index 0e1a05f95858..1cb8fda7a166 100644
--- a/drivers/input/misc/pmic8xxx-pwrkey.c
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -19,8 +19,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/log2.h>
-
-#include <linux/input/pmic8xxx-pwrkey.h>
+#include <linux/of.h>
#define PON_CNTL_1 0x1C
#define PON_CNTL_PULL_UP BIT(7)
@@ -89,15 +88,15 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
unsigned int pon_cntl;
struct regmap *regmap;
struct pmic8xxx_pwrkey *pwrkey;
- const struct pm8xxx_pwrkey_platform_data *pdata =
- dev_get_platdata(&pdev->dev);
+ u32 kpd_delay;
+ bool pull_up;
- if (!pdata) {
- dev_err(&pdev->dev, "power key platform data not supplied\n");
- return -EINVAL;
- }
+ if (of_property_read_u32(pdev->dev.of_node, "debounce", &kpd_delay))
+ kpd_delay = 0;
- if (pdata->kpd_trigger_delay_us > 62500) {
+ pull_up = of_property_read_bool(pdev->dev.of_node, "pull-up");
+
+ if (kpd_delay > 62500) {
dev_err(&pdev->dev, "invalid power key trigger delay\n");
return -EINVAL;
}
@@ -125,7 +124,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
pwr->name = "pmic8xxx_pwrkey";
pwr->phys = "pmic8xxx_pwrkey/input0";
- delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC;
+ delay = (kpd_delay << 10) / USEC_PER_SEC;
delay = 1 + ilog2(delay);
err = regmap_read(regmap, PON_CNTL_1, &pon_cntl);
@@ -136,7 +135,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK;
pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK);
- if (pdata->pull_up)
+ if (pull_up)
pon_cntl |= PON_CNTL_PULL_UP;
else
pon_cntl &= ~PON_CNTL_PULL_UP;
@@ -172,7 +171,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, pwrkey);
- device_init_wakeup(&pdev->dev, pdata->wakeup);
+ device_init_wakeup(&pdev->dev, 1);
return 0;
}
@@ -184,13 +183,21 @@ static int pmic8xxx_pwrkey_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id pm8xxx_pwr_key_id_table[] = {
+ { .compatible = "qcom,pm8058-pwrkey" },
+ { .compatible = "qcom,pm8921-pwrkey" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table);
+
static struct platform_driver pmic8xxx_pwrkey_driver = {
.probe = pmic8xxx_pwrkey_probe,
.remove = pmic8xxx_pwrkey_remove,
.driver = {
- .name = PM8XXX_PWRKEY_DEV_NAME,
+ .name = "pm8xxx-pwrkey",
.owner = THIS_MODULE,
.pm = &pm8xxx_pwr_key_pm_ops,
+ .of_match_table = pm8xxx_pwr_key_id_table,
},
};
module_platform_driver(pmic8xxx_pwrkey_driver);
diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c
index e8897c36d21b..e4104f9b2e6d 100644
--- a/drivers/input/misc/sirfsoc-onkey.c
+++ b/drivers/input/misc/sirfsoc-onkey.c
@@ -1,7 +1,8 @@
/*
* Power key driver for SiRF PrimaII
*
- * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company.
+ * Copyright (c) 2013 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
+ * company.
*
* Licensed under GPLv2 or later.
*/
@@ -13,16 +14,41 @@
#include <linux/input.h>
#include <linux/rtc/sirfsoc_rtciobrg.h>
#include <linux/of.h>
+#include <linux/workqueue.h>
struct sirfsoc_pwrc_drvdata {
u32 pwrc_base;
struct input_dev *input;
+ struct delayed_work work;
};
#define PWRC_ON_KEY_BIT (1 << 0)
#define PWRC_INT_STATUS 0xc
#define PWRC_INT_MASK 0x10
+#define PWRC_PIN_STATUS 0x14
+#define PWRC_KEY_DETECT_UP_TIME 20 /* ms*/
+
+static int sirfsoc_pwrc_is_on_key_down(struct sirfsoc_pwrc_drvdata *pwrcdrv)
+{
+ u32 state = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base +
+ PWRC_PIN_STATUS);
+ return !(state & PWRC_ON_KEY_BIT); /* ON_KEY is active low */
+}
+
+static void sirfsoc_pwrc_report_event(struct work_struct *work)
+{
+ struct sirfsoc_pwrc_drvdata *pwrcdrv =
+ container_of(work, struct sirfsoc_pwrc_drvdata, work.work);
+
+ if (sirfsoc_pwrc_is_on_key_down(pwrcdrv)) {
+ schedule_delayed_work(&pwrcdrv->work,
+ msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME));
+ } else {
+ input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 0);
+ input_sync(pwrcdrv->input);
+ }
+}
static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id)
{
@@ -34,21 +60,44 @@ static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id)
sirfsoc_rtc_iobrg_writel(int_status & ~PWRC_ON_KEY_BIT,
pwrcdrv->pwrc_base + PWRC_INT_STATUS);
- /*
- * For a typical Linux system, we report KEY_SUSPEND to trigger apm-power.c
- * to queue a SUSPEND APM event
- */
- input_event(pwrcdrv->input, EV_PWR, KEY_SUSPEND, 1);
+ input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 1);
input_sync(pwrcdrv->input);
-
- /*
- * Todo: report KEY_POWER event for Android platforms, Android PowerManager
- * will handle the suspend and powerdown/hibernation
- */
+ schedule_delayed_work(&pwrcdrv->work,
+ msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME));
return IRQ_HANDLED;
}
+static void sirfsoc_pwrc_toggle_interrupts(struct sirfsoc_pwrc_drvdata *pwrcdrv,
+ bool enable)
+{
+ u32 int_mask;
+
+ int_mask = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + PWRC_INT_MASK);
+ if (enable)
+ int_mask |= PWRC_ON_KEY_BIT;
+ else
+ int_mask &= ~PWRC_ON_KEY_BIT;
+ sirfsoc_rtc_iobrg_writel(int_mask, pwrcdrv->pwrc_base + PWRC_INT_MASK);
+}
+
+static int sirfsoc_pwrc_open(struct input_dev *input)
+{
+ struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input);
+
+ sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true);
+
+ return 0;
+}
+
+static void sirfsoc_pwrc_close(struct input_dev *input)
+{
+ struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input);
+
+ sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false);
+ cancel_delayed_work_sync(&pwrcdrv->work);
+}
+
static const struct of_device_id sirfsoc_pwrc_of_match[] = {
{ .compatible = "sirf,prima2-pwrc" },
{},
@@ -70,7 +119,7 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev)
}
/*
- * we can't use of_iomap because pwrc is not mapped in memory,
+ * We can't use of_iomap because pwrc is not mapped in memory,
* the so-called base address is only offset in rtciobrg
*/
error = of_property_read_u32(np, "reg", &pwrcdrv->pwrc_base);
@@ -86,11 +135,22 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev)
pwrcdrv->input->name = "sirfsoc pwrckey";
pwrcdrv->input->phys = "pwrc/input0";
- pwrcdrv->input->evbit[0] = BIT_MASK(EV_PWR);
+ pwrcdrv->input->evbit[0] = BIT_MASK(EV_KEY);
+ input_set_capability(pwrcdrv->input, EV_KEY, KEY_POWER);
+
+ INIT_DELAYED_WORK(&pwrcdrv->work, sirfsoc_pwrc_report_event);
+
+ pwrcdrv->input->open = sirfsoc_pwrc_open;
+ pwrcdrv->input->close = sirfsoc_pwrc_close;
+
+ input_set_drvdata(pwrcdrv->input, pwrcdrv);
+
+ /* Make sure the device is quiesced */
+ sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false);
irq = platform_get_irq(pdev, 0);
error = devm_request_irq(&pdev->dev, irq,
- sirfsoc_pwrc_isr, IRQF_SHARED,
+ sirfsoc_pwrc_isr, 0,
"sirfsoc_pwrc_int", pwrcdrv);
if (error) {
dev_err(&pdev->dev, "unable to claim irq %d, error: %d\n",
@@ -98,11 +158,6 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev)
return error;
}
- sirfsoc_rtc_iobrg_writel(
- sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + PWRC_INT_MASK) |
- PWRC_ON_KEY_BIT,
- pwrcdrv->pwrc_base + PWRC_INT_MASK);
-
error = input_register_device(pwrcdrv->input);
if (error) {
dev_err(&pdev->dev,
@@ -111,7 +166,7 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev)
return error;
}
- platform_set_drvdata(pdev, pwrcdrv);
+ dev_set_drvdata(&pdev->dev, pwrcdrv);
device_init_wakeup(&pdev->dev, 1);
return 0;
@@ -125,25 +180,25 @@ static int sirfsoc_pwrc_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
-static int pwrc_resume(struct device *dev)
+static int sirfsoc_pwrc_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct sirfsoc_pwrc_drvdata *pwrcdrv = platform_get_drvdata(pdev);
+ struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_get_drvdata(dev);
+ struct input_dev *input = pwrcdrv->input;
/*
* Do not mask pwrc interrupt as we want pwrc work as a wakeup source
* if users touch X_ONKEY_B, see arch/arm/mach-prima2/pm.c
*/
- sirfsoc_rtc_iobrg_writel(
- sirfsoc_rtc_iobrg_readl(
- pwrcdrv->pwrc_base + PWRC_INT_MASK) | PWRC_ON_KEY_BIT,
- pwrcdrv->pwrc_base + PWRC_INT_MASK);
+ mutex_lock(&input->mutex);
+ if (input->users)
+ sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true);
+ mutex_unlock(&input->mutex);
return 0;
}
#endif
-static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, pwrc_resume);
+static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, sirfsoc_pwrc_resume);
static struct platform_driver sirfsoc_pwrc_driver = {
.probe = sirfsoc_pwrc_probe,
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
new file mode 100644
index 000000000000..08ead2aaede5
--- /dev/null
+++ b/drivers/input/misc/soc_button_array.c
@@ -0,0 +1,218 @@
+/*
+ * Supports for the button array on SoC tablets originally running
+ * Windows 8.
+ *
+ * (C) Copyright 2014 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; version 2
+ * of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/pnp.h>
+
+/*
+ * Definition of buttons on the tablet. The ACPI index of each button
+ * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
+ * Platforms"
+ */
+#define MAX_NBUTTONS 5
+
+struct soc_button_info {
+ const char *name;
+ int acpi_index;
+ unsigned int event_type;
+ unsigned int event_code;
+ bool autorepeat;
+ bool wakeup;
+};
+
+/*
+ * Some of the buttons like volume up/down are auto repeat, while others
+ * are not. To support both, we register two platform devices, and put
+ * buttons into them based on whether the key should be auto repeat.
+ */
+#define BUTTON_TYPES 2
+
+struct soc_button_data {
+ struct platform_device *children[BUTTON_TYPES];
+};
+
+/*
+ * Get the Nth GPIO number from the ACPI object.
+ */
+static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
+{
+ struct gpio_desc *desc;
+ int gpio;
+
+ desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ gpio = desc_to_gpio(desc);
+
+ gpiod_put(desc);
+
+ return gpio;
+}
+
+static struct platform_device *
+soc_button_device_create(struct pnp_dev *pdev,
+ const struct soc_button_info *button_info,
+ bool autorepeat)
+{
+ const struct soc_button_info *info;
+ struct platform_device *pd;
+ struct gpio_keys_button *gpio_keys;
+ struct gpio_keys_platform_data *gpio_keys_pdata;
+ int n_buttons = 0;
+ int gpio;
+ int error;
+
+ gpio_keys_pdata = devm_kzalloc(&pdev->dev,
+ sizeof(*gpio_keys_pdata) +
+ sizeof(*gpio_keys) * MAX_NBUTTONS,
+ GFP_KERNEL);
+ gpio_keys = (void *)(gpio_keys_pdata + 1);
+
+ for (info = button_info; info->name; info++) {
+ if (info->autorepeat != autorepeat)
+ continue;
+
+ gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index);
+ if (gpio < 0)
+ continue;
+
+ gpio_keys[n_buttons].type = info->event_type;
+ gpio_keys[n_buttons].code = info->event_code;
+ gpio_keys[n_buttons].gpio = gpio;
+ gpio_keys[n_buttons].active_low = 1;
+ gpio_keys[n_buttons].desc = info->name;
+ gpio_keys[n_buttons].wakeup = info->wakeup;
+ n_buttons++;
+ }
+
+ if (n_buttons == 0) {
+ error = -ENODEV;
+ goto err_free_mem;
+ }
+
+ gpio_keys_pdata->buttons = gpio_keys;
+ gpio_keys_pdata->nbuttons = n_buttons;
+ gpio_keys_pdata->rep = autorepeat;
+
+ pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO);
+ if (!pd) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ error = platform_device_add_data(pd, gpio_keys_pdata,
+ sizeof(*gpio_keys_pdata));
+ if (error)
+ goto err_free_pdev;
+
+ error = platform_device_add(pd);
+ if (error)
+ goto err_free_pdev;
+
+ return pd;
+
+err_free_pdev:
+ platform_device_put(pd);
+err_free_mem:
+ devm_kfree(&pdev->dev, gpio_keys_pdata);
+ return ERR_PTR(error);
+}
+
+static void soc_button_remove(struct pnp_dev *pdev)
+{
+ struct soc_button_data *priv = pnp_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < BUTTON_TYPES; i++)
+ if (priv->children[i])
+ platform_device_unregister(priv->children[i]);
+}
+
+static int soc_button_pnp_probe(struct pnp_dev *pdev,
+ const struct pnp_device_id *id)
+{
+ const struct soc_button_info *button_info = (void *)id->driver_data;
+ struct soc_button_data *priv;
+ struct platform_device *pd;
+ int i;
+ int error;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ pnp_set_drvdata(pdev, priv);
+
+ for (i = 0; i < BUTTON_TYPES; i++) {
+ pd = soc_button_device_create(pdev, button_info, i == 0);
+ if (IS_ERR(pd)) {
+ error = PTR_ERR(pd);
+ if (error != -ENODEV) {
+ soc_button_remove(pdev);
+ return error;
+ }
+ }
+
+ priv->children[i] = pd;
+ }
+
+ if (!priv->children[0] && !priv->children[1])
+ return -ENODEV;
+
+ return 0;
+}
+
+static struct soc_button_info soc_button_PNP0C40[] = {
+ { "power", 0, EV_KEY, KEY_POWER, false, true },
+ { "home", 1, EV_KEY, KEY_HOME, false, true },
+ { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
+ { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
+ { "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false },
+ { }
+};
+
+static const struct pnp_device_id soc_button_pnp_match[] = {
+ { .id = "PNP0C40", .driver_data = (long)soc_button_PNP0C40 },
+ { .id = "" }
+};
+MODULE_DEVICE_TABLE(pnp, soc_button_pnp_match);
+
+static struct pnp_driver soc_button_pnp_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = soc_button_pnp_match,
+ .probe = soc_button_pnp_probe,
+ .remove = soc_button_remove,
+};
+
+static int __init soc_button_init(void)
+{
+ return pnp_register_driver(&soc_button_pnp_driver);
+}
+
+static void __exit soc_button_exit(void)
+{
+ pnp_unregister_driver(&soc_button_pnp_driver);
+}
+
+module_init(soc_button_init);
+module_exit(soc_button_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 772835938a52..856936247500 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -20,6 +20,8 @@
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
+ * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
+ * - add UI_GET_SYSNAME ioctl
* 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
* - updated ff support for the changes in kernel interface
* - added MODULE_VERSION
@@ -670,6 +672,31 @@ static int uinput_ff_upload_from_user(const char __user *buffer,
__ret; \
})
+static int uinput_str_to_user(void __user *dest, const char *str,
+ unsigned int maxlen)
+{
+ char __user *p = dest;
+ int len, ret;
+
+ if (!str)
+ return -ENOENT;
+
+ if (maxlen == 0)
+ return -EINVAL;
+
+ len = strlen(str) + 1;
+ if (len > maxlen)
+ len = maxlen;
+
+ ret = copy_to_user(p, str, len);
+ if (ret)
+ return -EFAULT;
+
+ /* force terminating '\0' */
+ ret = put_user(0, p + len - 1);
+ return ret ? -EFAULT : len;
+}
+
static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
unsigned long arg, void __user *p)
{
@@ -679,6 +706,8 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
struct uinput_ff_erase ff_erase;
struct uinput_request *req;
char *phys;
+ const char *name;
+ unsigned int size;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
@@ -693,51 +722,51 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
switch (cmd) {
case UI_DEV_CREATE:
retval = uinput_create_device(udev);
- break;
+ goto out;
case UI_DEV_DESTROY:
uinput_destroy_device(udev);
- break;
+ goto out;
case UI_SET_EVBIT:
retval = uinput_set_bit(arg, evbit, EV_MAX);
- break;
+ goto out;
case UI_SET_KEYBIT:
retval = uinput_set_bit(arg, keybit, KEY_MAX);
- break;
+ goto out;
case UI_SET_RELBIT:
retval = uinput_set_bit(arg, relbit, REL_MAX);
- break;
+ goto out;
case UI_SET_ABSBIT:
retval = uinput_set_bit(arg, absbit, ABS_MAX);
- break;
+ goto out;
case UI_SET_MSCBIT:
retval = uinput_set_bit(arg, mscbit, MSC_MAX);
- break;
+ goto out;
case UI_SET_LEDBIT:
retval = uinput_set_bit(arg, ledbit, LED_MAX);
- break;
+ goto out;
case UI_SET_SNDBIT:
retval = uinput_set_bit(arg, sndbit, SND_MAX);
- break;
+ goto out;
case UI_SET_FFBIT:
retval = uinput_set_bit(arg, ffbit, FF_MAX);
- break;
+ goto out;
case UI_SET_SWBIT:
retval = uinput_set_bit(arg, swbit, SW_MAX);
- break;
+ goto out;
case UI_SET_PROPBIT:
retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
- break;
+ goto out;
case UI_SET_PHYS:
if (udev->state == UIST_CREATED) {
@@ -753,18 +782,18 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
kfree(udev->dev->phys);
udev->dev->phys = phys;
- break;
+ goto out;
case UI_BEGIN_FF_UPLOAD:
retval = uinput_ff_upload_from_user(p, &ff_up);
if (retval)
- break;
+ goto out;
req = uinput_request_find(udev, ff_up.request_id);
if (!req || req->code != UI_FF_UPLOAD ||
!req->u.upload.effect) {
retval = -EINVAL;
- break;
+ goto out;
}
ff_up.retval = 0;
@@ -775,65 +804,77 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
memset(&ff_up.old, 0, sizeof(struct ff_effect));
retval = uinput_ff_upload_to_user(p, &ff_up);
- break;
+ goto out;
case UI_BEGIN_FF_ERASE:
if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
retval = -EFAULT;
- break;
+ goto out;
}
req = uinput_request_find(udev, ff_erase.request_id);
if (!req || req->code != UI_FF_ERASE) {
retval = -EINVAL;
- break;
+ goto out;
}
ff_erase.retval = 0;
ff_erase.effect_id = req->u.effect_id;
if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
retval = -EFAULT;
- break;
+ goto out;
}
- break;
+ goto out;
case UI_END_FF_UPLOAD:
retval = uinput_ff_upload_from_user(p, &ff_up);
if (retval)
- break;
+ goto out;
req = uinput_request_find(udev, ff_up.request_id);
if (!req || req->code != UI_FF_UPLOAD ||
!req->u.upload.effect) {
retval = -EINVAL;
- break;
+ goto out;
}
req->retval = ff_up.retval;
uinput_request_done(udev, req);
- break;
+ goto out;
case UI_END_FF_ERASE:
if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
retval = -EFAULT;
- break;
+ goto out;
}
req = uinput_request_find(udev, ff_erase.request_id);
if (!req || req->code != UI_FF_ERASE) {
retval = -EINVAL;
- break;
+ goto out;
}
req->retval = ff_erase.retval;
uinput_request_done(udev, req);
- break;
+ goto out;
+ }
- default:
- retval = -EINVAL;
+ size = _IOC_SIZE(cmd);
+
+ /* Now check variable-length commands */
+ switch (cmd & ~IOCSIZE_MASK) {
+ case UI_GET_SYSNAME(0):
+ if (udev->state != UIST_CREATED) {
+ retval = -ENOENT;
+ goto out;
+ }
+ name = dev_name(&udev->dev->dev);
+ retval = uinput_str_to_user(p, name, size);
+ goto out;
}
+ retval = -EINVAL;
out:
mutex_unlock(&udev->mutex);
return retval;
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
index b6505454bcc4..7b7add5061a5 100644
--- a/drivers/input/misc/wistron_btns.c
+++ b/drivers/input/misc/wistron_btns.c
@@ -277,6 +277,16 @@ static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = {
{ KE_END, 0 }
};
+static struct key_entry keymap_fs_amilo_pro_v8210[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} }, /* Fn+F1 */
+ { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */
+ { KE_BLUETOOTH, 0x30 }, /* Fn+F10 */
+ { KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */
+ { KE_KEY, 0x36, {KEY_WWW} }, /* www button */
+ { KE_WIFI, 0x78 }, /* satelite dish button */
+ { KE_END, FE_WIFI_LED }
+};
+
static struct key_entry keymap_fujitsu_n3510[] __initdata = {
{ KE_KEY, 0x11, {KEY_PROG1} },
{ KE_KEY, 0x12, {KEY_PROG2} },
@@ -654,6 +664,15 @@ static const struct dmi_system_id dmi_ids[] __initconst = {
.driver_data = keymap_fs_amilo_pro_v3505
},
{
+ /* Fujitsu-Siemens Amilo Pro Edition V8210 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Series V8210"),
+ },
+ .driver_data = keymap_fs_amilo_pro_v8210
+ },
+ {
/* Fujitsu-Siemens Amilo M7400 */
.callback = dmi_matched,
.matches = {
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
index 800ca7dfafc2..ef234c9b2f2f 100644
--- a/drivers/input/mouse/appletouch.c
+++ b/drivers/input/mouse/appletouch.c
@@ -48,6 +48,7 @@ struct atp_info {
int yfact; /* Y multiplication factor */
int datalen; /* size of USB transfers */
void (*callback)(struct urb *); /* callback function */
+ int fuzz; /* fuzz touchpad generates */
};
static void atp_complete_geyser_1_2(struct urb *urb);
@@ -61,6 +62,7 @@ static const struct atp_info fountain_info = {
.yfact = 43,
.datalen = 81,
.callback = atp_complete_geyser_1_2,
+ .fuzz = 16,
};
static const struct atp_info geyser1_info = {
@@ -71,6 +73,7 @@ static const struct atp_info geyser1_info = {
.yfact = 43,
.datalen = 81,
.callback = atp_complete_geyser_1_2,
+ .fuzz = 16,
};
static const struct atp_info geyser2_info = {
@@ -81,6 +84,7 @@ static const struct atp_info geyser2_info = {
.yfact = 43,
.datalen = 64,
.callback = atp_complete_geyser_1_2,
+ .fuzz = 0,
};
static const struct atp_info geyser3_info = {
@@ -90,6 +94,7 @@ static const struct atp_info geyser3_info = {
.yfact = 64,
.datalen = 64,
.callback = atp_complete_geyser_3_4,
+ .fuzz = 0,
};
static const struct atp_info geyser4_info = {
@@ -99,6 +104,7 @@ static const struct atp_info geyser4_info = {
.yfact = 64,
.datalen = 64,
.callback = atp_complete_geyser_3_4,
+ .fuzz = 0,
};
#define ATP_DEVICE(prod, info) \
@@ -155,8 +161,11 @@ MODULE_DEVICE_TABLE(usb, atp_table);
#define ATP_XSENSORS 26
#define ATP_YSENSORS 16
-/* amount of fuzz this touchpad generates */
-#define ATP_FUZZ 16
+/*
+ * The largest possible bank of sensors with additional buffer of 4 extra values
+ * on either side, for an array of smoothed sensor values.
+ */
+#define ATP_SMOOTHSIZE 34
/* maximum pressure this driver will report */
#define ATP_PRESSURE 300
@@ -165,7 +174,13 @@ MODULE_DEVICE_TABLE(usb, atp_table);
* Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
* ignored.
*/
-#define ATP_THRESHOLD 5
+#define ATP_THRESHOLD 5
+
+/*
+ * How far we'll bitshift our sensor values before averaging them. Mitigates
+ * rounding errors.
+ */
+#define ATP_SCALE 12
/* Geyser initialization constants */
#define ATP_GEYSER_MODE_READ_REQUEST_ID 1
@@ -203,11 +218,14 @@ struct atp {
bool valid; /* are the samples valid? */
bool size_detect_done;
bool overflow_warned;
+ int fingers_old; /* last reported finger count */
int x_old; /* last reported x/y, */
int y_old; /* used for smoothing */
signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS];
signed char xy_old[ATP_XSENSORS + ATP_YSENSORS];
int xy_acc[ATP_XSENSORS + ATP_YSENSORS];
+ int smooth[ATP_SMOOTHSIZE];
+ int smooth_tmp[ATP_SMOOTHSIZE];
int idlecount; /* number of empty packets */
struct work_struct work;
};
@@ -326,10 +344,17 @@ static void atp_reinit(struct work_struct *work)
retval);
}
-static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
- int *z, int *fingers)
+static int atp_calculate_abs(struct atp *dev, int offset, int nb_sensors,
+ int fact, int *z, int *fingers)
{
- int i;
+ int i, pass;
+
+ /*
+ * Use offset to point xy_sensors at the first value in dev->xy_acc
+ * for whichever dimension we're looking at this particular go-round.
+ */
+ int *xy_sensors = dev->xy_acc + offset;
+
/* values to calculate mean */
int pcum = 0, psum = 0;
int is_increasing = 0;
@@ -341,9 +366,6 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
if (is_increasing)
is_increasing = 0;
- continue;
- }
-
/*
* Makes the finger detection more versatile. For example,
* two fingers with no gap will be detected. Also, my
@@ -358,27 +380,63 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
*
* - Jason Parekh <jasonparekh@gmail.com>
*/
- if (i < 1 ||
+
+ } else if (i < 1 ||
(!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
(*fingers)++;
is_increasing = 1;
} else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) {
is_increasing = 0;
}
+ }
+
+ if (*fingers < 1) /* No need to continue if no fingers are found. */
+ return 0;
+ /*
+ * Use a smoothed version of sensor data for movement calculations, to
+ * combat noise without needing to rely so heavily on a threshold.
+ * This improves tracking.
+ *
+ * The smoothed array is bigger than the original so that the smoothing
+ * doesn't result in edge values being truncated.
+ */
+
+ memset(dev->smooth, 0, 4 * sizeof(dev->smooth[0]));
+ /* Pull base values, scaled up to help avoid truncation errors. */
+ for (i = 0; i < nb_sensors; i++)
+ dev->smooth[i + 4] = xy_sensors[i] << ATP_SCALE;
+ memset(&dev->smooth[nb_sensors + 4], 0, 4 * sizeof(dev->smooth[0]));
+
+ for (pass = 0; pass < 4; pass++) {
+ /* Handle edge. */
+ dev->smooth_tmp[0] = (dev->smooth[0] + dev->smooth[1]) / 2;
+
+ /* Average values with neighbors. */
+ for (i = 1; i < nb_sensors + 7; i++)
+ dev->smooth_tmp[i] = (dev->smooth[i - 1] +
+ dev->smooth[i] * 2 +
+ dev->smooth[i + 1]) / 4;
+
+ /* Handle other edge. */
+ dev->smooth_tmp[i] = (dev->smooth[i - 1] + dev->smooth[i]) / 2;
+
+ memcpy(dev->smooth, dev->smooth_tmp, sizeof(dev->smooth));
+ }
+
+ for (i = 0; i < nb_sensors + 8; i++) {
/*
- * Subtracts threshold so a high sensor that just passes the
- * threshold won't skew the calculated absolute coordinate.
- * Fixes an issue where slowly moving the mouse would
- * occasionally jump a number of pixels (slowly moving the
- * finger makes this issue most apparent.)
+ * Skip values if they're small enough to be truncated to 0
+ * by scale. Mostly noise.
*/
- pcum += (xy_sensors[i] - threshold) * i;
- psum += (xy_sensors[i] - threshold);
+ if ((dev->smooth[i] >> ATP_SCALE) > 0) {
+ pcum += dev->smooth[i] * i;
+ psum += dev->smooth[i];
+ }
}
if (psum > 0) {
- *z = psum;
+ *z = psum >> ATP_SCALE; /* Scale down pressure output. */
return pcum * fact / psum;
}
@@ -455,7 +513,7 @@ static void atp_detect_size(struct atp *dev)
input_set_abs_params(dev->input, ABS_X, 0,
(dev->info->xsensors_17 - 1) *
dev->info->xfact - 1,
- ATP_FUZZ, 0);
+ dev->info->fuzz, 0);
break;
}
}
@@ -471,7 +529,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
{
int x, y, x_z, y_z, x_f, y_f;
int retval, i, j;
- int key;
+ int key, fingers;
struct atp *dev = urb->context;
int status = atp_status_check(urb);
@@ -548,16 +606,18 @@ static void atp_complete_geyser_1_2(struct urb *urb)
dbg_dump("accumulator", dev->xy_acc);
- x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
+ x = atp_calculate_abs(dev, 0, ATP_XSENSORS,
dev->info->xfact, &x_z, &x_f);
- y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
+ y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS,
dev->info->yfact, &y_z, &y_f);
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
- if (x && y) {
+ fingers = max(x_f, y_f);
+
+ if (x && y && fingers == dev->fingers_old) {
if (dev->x_old != -1) {
- x = (dev->x_old * 3 + x) >> 2;
- y = (dev->y_old * 3 + y) >> 2;
+ x = (dev->x_old * 7 + x) >> 3;
+ y = (dev->y_old * 7 + y) >> 3;
dev->x_old = x;
dev->y_old = y;
@@ -571,7 +631,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
input_report_abs(dev->input, ABS_Y, y);
input_report_abs(dev->input, ABS_PRESSURE,
min(ATP_PRESSURE, x_z + y_z));
- atp_report_fingers(dev->input, max(x_f, y_f));
+ atp_report_fingers(dev->input, fingers);
}
dev->x_old = x;
dev->y_old = y;
@@ -579,6 +639,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
} else if (!x && !y) {
dev->x_old = dev->y_old = -1;
+ dev->fingers_old = 0;
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
@@ -587,6 +648,10 @@ static void atp_complete_geyser_1_2(struct urb *urb)
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
+ if (fingers != dev->fingers_old)
+ dev->x_old = dev->y_old = -1;
+ dev->fingers_old = fingers;
+
input_report_key(dev->input, BTN_LEFT, key);
input_sync(dev->input);
@@ -604,7 +669,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)
{
int x, y, x_z, y_z, x_f, y_f;
int retval, i, j;
- int key;
+ int key, fingers;
struct atp *dev = urb->context;
int status = atp_status_check(urb);
@@ -660,16 +725,19 @@ static void atp_complete_geyser_3_4(struct urb *urb)
dbg_dump("accumulator", dev->xy_acc);
- x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
+ x = atp_calculate_abs(dev, 0, ATP_XSENSORS,
dev->info->xfact, &x_z, &x_f);
- y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
+ y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS,
dev->info->yfact, &y_z, &y_f);
+
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
- if (x && y) {
+ fingers = max(x_f, y_f);
+
+ if (x && y && fingers == dev->fingers_old) {
if (dev->x_old != -1) {
- x = (dev->x_old * 3 + x) >> 2;
- y = (dev->y_old * 3 + y) >> 2;
+ x = (dev->x_old * 7 + x) >> 3;
+ y = (dev->y_old * 7 + y) >> 3;
dev->x_old = x;
dev->y_old = y;
@@ -683,7 +751,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)
input_report_abs(dev->input, ABS_Y, y);
input_report_abs(dev->input, ABS_PRESSURE,
min(ATP_PRESSURE, x_z + y_z));
- atp_report_fingers(dev->input, max(x_f, y_f));
+ atp_report_fingers(dev->input, fingers);
}
dev->x_old = x;
dev->y_old = y;
@@ -691,6 +759,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)
} else if (!x && !y) {
dev->x_old = dev->y_old = -1;
+ dev->fingers_old = 0;
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
@@ -699,6 +768,10 @@ static void atp_complete_geyser_3_4(struct urb *urb)
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
+ if (fingers != dev->fingers_old)
+ dev->x_old = dev->y_old = -1;
+ dev->fingers_old = fingers;
+
input_report_key(dev->input, BTN_LEFT, key);
input_sync(dev->input);
@@ -843,10 +916,10 @@ static int atp_probe(struct usb_interface *iface,
input_set_abs_params(input_dev, ABS_X, 0,
(dev->info->xsensors - 1) * dev->info->xfact - 1,
- ATP_FUZZ, 0);
+ dev->info->fuzz, 0);
input_set_abs_params(input_dev, ABS_Y, 0,
(dev->info->ysensors - 1) * dev->info->yfact - 1,
- ATP_FUZZ, 0);
+ dev->info->fuzz, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
set_bit(EV_KEY, input_dev->evbit);
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index aec54e283580..bc2d47431bdc 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -263,7 +263,7 @@ config SERIO_APBPS2
config SERIO_OLPC_APSP
tristate "OLPC AP-SP input support"
- depends on OF
+ depends on OLPC || COMPILE_TEST
help
Say Y here if you want support for the keyboard and touchpad included
in the OLPC XO-1.75 and XO-4 laptops.
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index d7a7e54f6465..852858e5d8d0 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -984,7 +984,7 @@ static void hp_sdc_exit(void)
free_irq(hp_sdc.irq, &hp_sdc);
write_unlock_irq(&hp_sdc.lock);
- del_timer(&hp_sdc.kicker);
+ del_timer_sync(&hp_sdc.kicker);
tasklet_kill(&hp_sdc.task);
diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c
index a70aa555bbff..e7409c45bdd0 100644
--- a/drivers/input/sparse-keymap.c
+++ b/drivers/input/sparse-keymap.c
@@ -236,7 +236,7 @@ EXPORT_SYMBOL(sparse_keymap_setup);
* in an input device that was set up by sparse_keymap_setup().
* NOTE: It is safe to cal this function while input device is
* still registered (however the drivers should care not to try to
- * use freed keymap and thus have to shut off interrups/polling
+ * use freed keymap and thus have to shut off interrupts/polling
* before freeing the keymap).
*/
void sparse_keymap_free(struct input_dev *dev)
diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c
index caecffe8caff..858045694e9d 100644
--- a/drivers/input/tablet/gtco.c
+++ b/drivers/input/tablet/gtco.c
@@ -848,7 +848,7 @@ static int gtco_probe(struct usb_interface *usbinterface,
gtco->inputdevice = input_dev;
/* Save interface information */
- gtco->usbdev = usb_get_dev(interface_to_usbdev(usbinterface));
+ gtco->usbdev = interface_to_usbdev(usbinterface);
gtco->intf = usbinterface;
/* Allocate some data for incoming reports */
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 07e9e82029d1..68edc9db2c64 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -514,15 +514,6 @@ config TOUCHSCREEN_MIGOR
To compile this driver as a module, choose M here: the
module will be called migor_ts.
-config TOUCHSCREEN_TNETV107X
- tristate "TI TNETV107X touchscreen support"
- depends on ARCH_DAVINCI_TNETV107X
- help
- Say Y here if you want to use the TNETV107X touchscreen.
-
- To compile this driver as a module, choose M here: the
- module will be called tnetv107x-ts.
-
config TOUCHSCREEN_TOUCHRIGHT
tristate "Touchright serial touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 62801f213346..4bc954b7c7c3 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -56,7 +56,6 @@ obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
-obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index 412a85ec9ba5..f8815bebc9ef 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
+ * Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support)
+ * Lothar Waßmann <LW@KARO-electronics.de> (DT support)
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -33,6 +35,7 @@
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <linux/input/mt.h>
#include <linux/input/edt-ft5x06.h>
@@ -45,6 +48,14 @@
#define WORK_REGISTER_NUM_X 0x33
#define WORK_REGISTER_NUM_Y 0x34
+#define M09_REGISTER_THRESHOLD 0x80
+#define M09_REGISTER_GAIN 0x92
+#define M09_REGISTER_OFFSET 0x93
+#define M09_REGISTER_NUM_X 0x94
+#define M09_REGISTER_NUM_Y 0x95
+
+#define NO_REGISTER 0xff
+
#define WORK_REGISTER_OPMODE 0x3c
#define FACTORY_REGISTER_OPMODE 0x01
@@ -59,12 +70,30 @@
#define EDT_RAW_DATA_RETRIES 100
#define EDT_RAW_DATA_DELAY 1 /* msec */
+enum edt_ver {
+ M06,
+ M09,
+};
+
+struct edt_reg_addr {
+ int reg_threshold;
+ int reg_report_rate;
+ int reg_gain;
+ int reg_offset;
+ int reg_num_x;
+ int reg_num_y;
+};
+
struct edt_ft5x06_ts_data {
struct i2c_client *client;
struct input_dev *input;
u16 num_x;
u16 num_y;
+ int reset_pin;
+ int irq_pin;
+ int wake_pin;
+
#if defined(CONFIG_DEBUG_FS)
struct dentry *debug_dir;
u8 *raw_buffer;
@@ -79,6 +108,9 @@ struct edt_ft5x06_ts_data {
int report_rate;
char name[EDT_NAME_LEN];
+
+ struct edt_reg_addr reg_addr;
+ enum edt_ver version;
};
static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
@@ -136,33 +168,58 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
{
struct edt_ft5x06_ts_data *tsdata = dev_id;
struct device *dev = &tsdata->client->dev;
- u8 cmd = 0xf9;
- u8 rdbuf[26];
+ u8 cmd;
+ u8 rdbuf[29];
int i, type, x, y, id;
+ int offset, tplen, datalen;
int error;
+ switch (tsdata->version) {
+ case M06:
+ cmd = 0xf9; /* tell the controller to send touch data */
+ offset = 5; /* where the actual touch data starts */
+ tplen = 4; /* data comes in so called frames */
+ datalen = 26; /* how much bytes to listen for */
+ break;
+
+ case M09:
+ cmd = 0x02;
+ offset = 1;
+ tplen = 6;
+ datalen = 29;
+ break;
+
+ default:
+ goto out;
+ }
+
memset(rdbuf, 0, sizeof(rdbuf));
error = edt_ft5x06_ts_readwrite(tsdata->client,
sizeof(cmd), &cmd,
- sizeof(rdbuf), rdbuf);
+ datalen, rdbuf);
if (error) {
dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
error);
goto out;
}
- if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) {
- dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n",
- rdbuf[0], rdbuf[1], rdbuf[2]);
- goto out;
- }
+ /* M09 does not send header or CRC */
+ if (tsdata->version == M06) {
+ if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa ||
+ rdbuf[2] != datalen) {
+ dev_err_ratelimited(dev,
+ "Unexpected header: %02x%02x%02x!\n",
+ rdbuf[0], rdbuf[1], rdbuf[2]);
+ goto out;
+ }
- if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, 26))
- goto out;
+ if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen))
+ goto out;
+ }
for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
- u8 *buf = &rdbuf[i * 4 + 5];
+ u8 *buf = &rdbuf[i * tplen + offset];
bool down;
type = buf[0] >> 6;
@@ -170,10 +227,14 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
if (type == TOUCH_EVENT_RESERVED)
continue;
+ /* M06 sometimes sends bogus coordinates in TOUCH_DOWN */
+ if (tsdata->version == M06 && type == TOUCH_EVENT_DOWN)
+ continue;
+
x = ((buf[0] << 8) | buf[1]) & 0x0fff;
y = ((buf[2] << 8) | buf[3]) & 0x0fff;
id = (buf[2] >> 4) & 0x0f;
- down = (type != TOUCH_EVENT_UP);
+ down = type != TOUCH_EVENT_UP;
input_mt_slot(tsdata->input, id);
input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down);
@@ -197,12 +258,25 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
{
u8 wrbuf[4];
- wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
- wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
- wrbuf[2] = value;
- wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
-
- return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL);
+ switch (tsdata->version) {
+ case M06:
+ wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+ wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+ wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+ wrbuf[2] = value;
+ wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
+ return edt_ft5x06_ts_readwrite(tsdata->client, 4,
+ wrbuf, 0, NULL);
+ case M09:
+ wrbuf[0] = addr;
+ wrbuf[1] = value;
+
+ return edt_ft5x06_ts_readwrite(tsdata->client, 3,
+ wrbuf, 0, NULL);
+
+ default:
+ return -EINVAL;
+ }
}
static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
@@ -211,19 +285,36 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
u8 wrbuf[2], rdbuf[2];
int error;
- wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
- wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
- wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40;
+ switch (tsdata->version) {
+ case M06:
+ wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+ wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+ wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40;
- error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf);
- if (error)
- return error;
+ error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2,
+ rdbuf);
+ if (error)
+ return error;
- if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) {
- dev_err(&tsdata->client->dev,
- "crc error: 0x%02x expected, got 0x%02x\n",
- wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]);
- return -EIO;
+ if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) {
+ dev_err(&tsdata->client->dev,
+ "crc error: 0x%02x expected, got 0x%02x\n",
+ wrbuf[0] ^ wrbuf[1] ^ rdbuf[0],
+ rdbuf[1]);
+ return -EIO;
+ }
+ break;
+
+ case M09:
+ wrbuf[0] = addr;
+ error = edt_ft5x06_ts_readwrite(tsdata->client, 1,
+ wrbuf, 1, rdbuf);
+ if (error)
+ return error;
+ break;
+
+ default:
+ return -EINVAL;
}
return rdbuf[0];
@@ -234,19 +325,21 @@ struct edt_ft5x06_attribute {
size_t field_offset;
u8 limit_low;
u8 limit_high;
- u8 addr;
+ u8 addr_m06;
+ u8 addr_m09;
};
-#define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high) \
+#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \
+ _limit_low, _limit_high) \
struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \
.dattr = __ATTR(_field, _mode, \
edt_ft5x06_setting_show, \
edt_ft5x06_setting_store), \
- .field_offset = \
- offsetof(struct edt_ft5x06_ts_data, _field), \
+ .field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \
+ .addr_m06 = _addr_m06, \
+ .addr_m09 = _addr_m09, \
.limit_low = _limit_low, \
.limit_high = _limit_high, \
- .addr = _addr, \
}
static ssize_t edt_ft5x06_setting_show(struct device *dev,
@@ -257,10 +350,11 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
struct edt_ft5x06_attribute *attr =
container_of(dattr, struct edt_ft5x06_attribute, dattr);
- u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
+ u8 *field = (u8 *)tsdata + attr->field_offset;
int val;
size_t count = 0;
int error = 0;
+ u8 addr;
mutex_lock(&tsdata->mutex);
@@ -269,15 +363,33 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
goto out;
}
- val = edt_ft5x06_register_read(tsdata, attr->addr);
- if (val < 0) {
- error = val;
- dev_err(&tsdata->client->dev,
- "Failed to fetch attribute %s, error %d\n",
- dattr->attr.name, error);
+ switch (tsdata->version) {
+ case M06:
+ addr = attr->addr_m06;
+ break;
+
+ case M09:
+ addr = attr->addr_m09;
+ break;
+
+ default:
+ error = -ENODEV;
goto out;
}
+ if (addr != NO_REGISTER) {
+ val = edt_ft5x06_register_read(tsdata, addr);
+ if (val < 0) {
+ error = val;
+ dev_err(&tsdata->client->dev,
+ "Failed to fetch attribute %s, error %d\n",
+ dattr->attr.name, error);
+ goto out;
+ }
+ } else {
+ val = *field;
+ }
+
if (val != *field) {
dev_warn(&tsdata->client->dev,
"%s: read (%d) and stored value (%d) differ\n",
@@ -299,9 +411,10 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
struct edt_ft5x06_attribute *attr =
container_of(dattr, struct edt_ft5x06_attribute, dattr);
- u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
+ u8 *field = (u8 *)tsdata + attr->field_offset;
unsigned int val;
int error;
+ u8 addr;
mutex_lock(&tsdata->mutex);
@@ -319,14 +432,29 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
goto out;
}
- error = edt_ft5x06_register_write(tsdata, attr->addr, val);
- if (error) {
- dev_err(&tsdata->client->dev,
- "Failed to update attribute %s, error: %d\n",
- dattr->attr.name, error);
+ switch (tsdata->version) {
+ case M06:
+ addr = attr->addr_m06;
+ break;
+
+ case M09:
+ addr = attr->addr_m09;
+ break;
+
+ default:
+ error = -ENODEV;
goto out;
}
+ if (addr != NO_REGISTER) {
+ error = edt_ft5x06_register_write(tsdata, addr, val);
+ if (error) {
+ dev_err(&tsdata->client->dev,
+ "Failed to update attribute %s, error: %d\n",
+ dattr->attr.name, error);
+ goto out;
+ }
+ }
*field = val;
out:
@@ -334,12 +462,14 @@ out:
return error ?: count;
}
-static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31);
-static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31);
-static EDT_ATTR(threshold, S_IWUSR | S_IRUGO,
- WORK_REGISTER_THRESHOLD, 20, 80);
-static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO,
- WORK_REGISTER_REPORT_RATE, 3, 14);
+static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN,
+ M09_REGISTER_GAIN, 0, 31);
+static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET,
+ M09_REGISTER_OFFSET, 0, 31);
+static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD,
+ M09_REGISTER_THRESHOLD, 20, 80);
+static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
+ NO_REGISTER, 3, 14);
static struct attribute *edt_ft5x06_attrs[] = {
&edt_ft5x06_attr_gain.dattr.attr,
@@ -374,6 +504,9 @@ static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
}
/* mode register is 0x3c when in the work mode */
+ if (tsdata->version == M09)
+ goto m09_out;
+
error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03);
if (error) {
dev_err(&client->dev,
@@ -406,12 +539,18 @@ err_out:
enable_irq(client->irq);
return error;
+
+m09_out:
+ dev_err(&client->dev, "No factory mode support for M09\n");
+ return -EINVAL;
+
}
static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
{
struct i2c_client *client = tsdata->client;
int retries = EDT_SWITCH_MODE_RETRIES;
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
int ret;
int error;
@@ -444,13 +583,14 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
tsdata->raw_buffer = NULL;
/* restore parameters */
- edt_ft5x06_register_write(tsdata, WORK_REGISTER_THRESHOLD,
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold,
tsdata->threshold);
- edt_ft5x06_register_write(tsdata, WORK_REGISTER_GAIN,
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
tsdata->gain);
- edt_ft5x06_register_write(tsdata, WORK_REGISTER_OFFSET,
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
tsdata->offset);
- edt_ft5x06_register_write(tsdata, WORK_REGISTER_REPORT_RATE,
+ if (reg_addr->reg_report_rate)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
tsdata->report_rate);
enable_irq(client->irq);
@@ -479,7 +619,7 @@ static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode)
if (mode != tsdata->factory_mode) {
retval = mode ? edt_ft5x06_factory_mode(tsdata) :
- edt_ft5x06_work_mode(tsdata);
+ edt_ft5x06_work_mode(tsdata);
}
mutex_unlock(&tsdata->mutex);
@@ -568,7 +708,6 @@ out:
return error ?: read;
};
-
static const struct file_operations debugfs_raw_data_fops = {
.open = simple_open,
.read = edt_ft5x06_debugfs_raw_data_read,
@@ -614,58 +753,100 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
#endif /* CONFIG_DEBUGFS */
-
-
static int edt_ft5x06_ts_reset(struct i2c_client *client,
- int reset_pin)
+ struct edt_ft5x06_ts_data *tsdata)
{
int error;
- if (gpio_is_valid(reset_pin)) {
+ if (gpio_is_valid(tsdata->wake_pin)) {
+ error = devm_gpio_request_one(&client->dev,
+ tsdata->wake_pin, GPIOF_OUT_INIT_LOW,
+ "edt-ft5x06 wake");
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to request GPIO %d as wake pin, error %d\n",
+ tsdata->wake_pin, error);
+ return error;
+ }
+
+ msleep(5);
+ gpio_set_value(tsdata->wake_pin, 1);
+ }
+ if (gpio_is_valid(tsdata->reset_pin)) {
/* this pulls reset down, enabling the low active reset */
- error = devm_gpio_request_one(&client->dev, reset_pin,
- GPIOF_OUT_INIT_LOW,
- "edt-ft5x06 reset");
+ error = devm_gpio_request_one(&client->dev,
+ tsdata->reset_pin, GPIOF_OUT_INIT_LOW,
+ "edt-ft5x06 reset");
if (error) {
dev_err(&client->dev,
"Failed to request GPIO %d as reset pin, error %d\n",
- reset_pin, error);
+ tsdata->reset_pin, error);
return error;
}
- mdelay(50);
- gpio_set_value(reset_pin, 1);
- mdelay(100);
+ msleep(5);
+ gpio_set_value(tsdata->reset_pin, 1);
+ msleep(300);
}
return 0;
}
static int edt_ft5x06_ts_identify(struct i2c_client *client,
- char *model_name,
- char *fw_version)
+ struct edt_ft5x06_ts_data *tsdata,
+ char *fw_version)
{
u8 rdbuf[EDT_NAME_LEN];
char *p;
int error;
+ char *model_name = tsdata->name;
+ /* see what we find if we assume it is a M06 *
+ * if we get less than EDT_NAME_LEN, we don't want
+ * to have garbage in there
+ */
+ memset(rdbuf, 0, sizeof(rdbuf));
error = edt_ft5x06_ts_readwrite(client, 1, "\xbb",
EDT_NAME_LEN - 1, rdbuf);
if (error)
return error;
- /* remove last '$' end marker */
- rdbuf[EDT_NAME_LEN - 1] = '\0';
- if (rdbuf[EDT_NAME_LEN - 2] == '$')
- rdbuf[EDT_NAME_LEN - 2] = '\0';
+ /* if we find something consistent, stay with that assumption
+ * at least M09 won't send 3 bytes here
+ */
+ if (!(strnicmp(rdbuf + 1, "EP0", 3))) {
+ tsdata->version = M06;
+
+ /* remove last '$' end marker */
+ rdbuf[EDT_NAME_LEN - 1] = '\0';
+ if (rdbuf[EDT_NAME_LEN - 2] == '$')
+ rdbuf[EDT_NAME_LEN - 2] = '\0';
+
+ /* look for Model/Version separator */
+ p = strchr(rdbuf, '*');
+ if (p)
+ *p++ = '\0';
+ strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
+ strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
+ } else {
+ /* since there are only two versions around (M06, M09) */
+ tsdata->version = M09;
+
+ error = edt_ft5x06_ts_readwrite(client, 1, "\xA6",
+ 2, rdbuf);
+ if (error)
+ return error;
+
+ strlcpy(fw_version, rdbuf, 2);
- /* look for Model/Version separator */
- p = strchr(rdbuf, '*');
- if (p)
- *p++ = '\0';
+ error = edt_ft5x06_ts_readwrite(client, 1, "\xA8",
+ 1, rdbuf);
+ if (error)
+ return error;
- strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
- strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
+ snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09",
+ rdbuf[0] >> 4, rdbuf[0] & 0x0F);
+ }
return 0;
}
@@ -675,33 +856,104 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
pdata->name <= edt_ft5x06_attr_##name.limit_high) \
edt_ft5x06_register_write(tsdata, reg, pdata->name)
+#define EDT_GET_PROP(name, reg) { \
+ u32 val; \
+ if (of_property_read_u32(np, #name, &val) == 0) \
+ edt_ft5x06_register_write(tsdata, reg, val); \
+}
+
+static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np,
+ struct edt_ft5x06_ts_data *tsdata)
+{
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
+ EDT_GET_PROP(threshold, reg_addr->reg_threshold);
+ EDT_GET_PROP(gain, reg_addr->reg_gain);
+ EDT_GET_PROP(offset, reg_addr->reg_offset);
+}
+
static void
edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata,
const struct edt_ft5x06_platform_data *pdata)
{
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
if (!pdata->use_parameters)
return;
/* pick up defaults from the platform data */
- EDT_ATTR_CHECKSET(threshold, WORK_REGISTER_THRESHOLD);
- EDT_ATTR_CHECKSET(gain, WORK_REGISTER_GAIN);
- EDT_ATTR_CHECKSET(offset, WORK_REGISTER_OFFSET);
- EDT_ATTR_CHECKSET(report_rate, WORK_REGISTER_REPORT_RATE);
+ EDT_ATTR_CHECKSET(threshold, reg_addr->reg_threshold);
+ EDT_ATTR_CHECKSET(gain, reg_addr->reg_gain);
+ EDT_ATTR_CHECKSET(offset, reg_addr->reg_offset);
+ if (reg_addr->reg_report_rate != NO_REGISTER)
+ EDT_ATTR_CHECKSET(report_rate, reg_addr->reg_report_rate);
}
static void
edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
{
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
tsdata->threshold = edt_ft5x06_register_read(tsdata,
- WORK_REGISTER_THRESHOLD);
- tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN);
- tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET);
- tsdata->report_rate = edt_ft5x06_register_read(tsdata,
- WORK_REGISTER_REPORT_RATE);
- tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X);
- tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y);
+ reg_addr->reg_threshold);
+ tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain);
+ tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
+ if (reg_addr->reg_report_rate != NO_REGISTER)
+ tsdata->report_rate = edt_ft5x06_register_read(tsdata,
+ reg_addr->reg_report_rate);
+ tsdata->num_x = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_x);
+ tsdata->num_y = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_y);
+}
+
+static void
+edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
+{
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
+ switch (tsdata->version) {
+ case M06:
+ reg_addr->reg_threshold = WORK_REGISTER_THRESHOLD;
+ reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE;
+ reg_addr->reg_gain = WORK_REGISTER_GAIN;
+ reg_addr->reg_offset = WORK_REGISTER_OFFSET;
+ reg_addr->reg_num_x = WORK_REGISTER_NUM_X;
+ reg_addr->reg_num_y = WORK_REGISTER_NUM_Y;
+ break;
+
+ case M09:
+ reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
+ reg_addr->reg_gain = M09_REGISTER_GAIN;
+ reg_addr->reg_offset = M09_REGISTER_OFFSET;
+ reg_addr->reg_num_x = M09_REGISTER_NUM_X;
+ reg_addr->reg_num_y = M09_REGISTER_NUM_Y;
+ break;
+ }
}
+#ifdef CONFIG_OF
+static int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
+ struct edt_ft5x06_ts_data *tsdata)
+{
+ struct device_node *np = dev->of_node;
+
+ /*
+ * irq_pin is not needed for DT setup.
+ * irq is associated via 'interrupts' property in DT
+ */
+ tsdata->irq_pin = -EINVAL;
+ tsdata->reset_pin = of_get_named_gpio(np, "reset-gpios", 0);
+ tsdata->wake_pin = of_get_named_gpio(np, "wake-gpios", 0);
+
+ return 0;
+}
+#else
+static inline int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
+ struct edt_ft5x06_ts_data *tsdata)
+{
+ return -ENODEV;
+}
+#endif
+
static int edt_ft5x06_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -714,32 +966,40 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
+ tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
+ if (!tsdata) {
+ dev_err(&client->dev, "failed to allocate driver data.\n");
+ return -ENOMEM;
+ }
+
if (!pdata) {
- dev_err(&client->dev, "no platform data?\n");
- return -EINVAL;
+ error = edt_ft5x06_i2c_ts_probe_dt(&client->dev, tsdata);
+ if (error) {
+ dev_err(&client->dev,
+ "DT probe failed and no platform data present\n");
+ return error;
+ }
+ } else {
+ tsdata->reset_pin = pdata->reset_pin;
+ tsdata->irq_pin = pdata->irq_pin;
+ tsdata->wake_pin = -EINVAL;
}
- error = edt_ft5x06_ts_reset(client, pdata->reset_pin);
+ error = edt_ft5x06_ts_reset(client, tsdata);
if (error)
return error;
- if (gpio_is_valid(pdata->irq_pin)) {
- error = devm_gpio_request_one(&client->dev, pdata->irq_pin,
- GPIOF_IN, "edt-ft5x06 irq");
+ if (gpio_is_valid(tsdata->irq_pin)) {
+ error = devm_gpio_request_one(&client->dev, tsdata->irq_pin,
+ GPIOF_IN, "edt-ft5x06 irq");
if (error) {
dev_err(&client->dev,
"Failed to request GPIO %d, error %d\n",
- pdata->irq_pin, error);
+ tsdata->irq_pin, error);
return error;
}
}
- tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
- if (!tsdata) {
- dev_err(&client->dev, "failed to allocate driver data.\n");
- return -ENOMEM;
- }
-
input = devm_input_allocate_device(&client->dev);
if (!input) {
dev_err(&client->dev, "failed to allocate input device.\n");
@@ -751,13 +1011,19 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
tsdata->input = input;
tsdata->factory_mode = false;
- error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version);
+ error = edt_ft5x06_ts_identify(client, tsdata, fw_version);
if (error) {
dev_err(&client->dev, "touchscreen probe failed\n");
return error;
}
- edt_ft5x06_ts_get_defaults(tsdata, pdata);
+ edt_ft5x06_ts_set_regs(tsdata);
+
+ if (!pdata)
+ edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata);
+ else
+ edt_ft5x06_ts_get_defaults(tsdata, pdata);
+
edt_ft5x06_ts_get_parameters(tsdata);
dev_dbg(&client->dev,
@@ -787,10 +1053,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
input_set_drvdata(input, tsdata);
i2c_set_clientdata(client, tsdata);
- error = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, edt_ft5x06_ts_isr,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- client->name, tsdata);
+ error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ edt_ft5x06_ts_isr,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, tsdata);
if (error) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
return error;
@@ -801,19 +1067,21 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
return error;
error = input_register_device(input);
- if (error) {
- sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
- return error;
- }
+ if (error)
+ goto err_remove_attrs;
edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
device_init_wakeup(&client->dev, 1);
dev_dbg(&client->dev,
- "EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n",
- pdata->irq_pin, pdata->reset_pin);
+ "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
+ client->irq, tsdata->wake_pin, tsdata->reset_pin);
return 0;
+
+err_remove_attrs:
+ sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+ return error;
}
static int edt_ft5x06_ts_remove(struct i2c_client *client)
@@ -852,15 +1120,26 @@ static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
static const struct i2c_device_id edt_ft5x06_ts_id[] = {
- { "edt-ft5x06", 0 },
- { }
+ { "edt-ft5x06", 0, },
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
+#ifdef CONFIG_OF
+static const struct of_device_id edt_ft5x06_of_match[] = {
+ { .compatible = "edt,edt-ft5206", },
+ { .compatible = "edt,edt-ft5306", },
+ { .compatible = "edt,edt-ft5406", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
+#endif
+
static struct i2c_driver edt_ft5x06_ts_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "edt_ft5x06",
+ .of_match_table = of_match_ptr(edt_ft5x06_of_match),
.pm = &edt_ft5x06_ts_pm_ops,
},
.id_table = edt_ft5x06_ts_id,
diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c
deleted file mode 100644
index c47827a26e3c..000000000000
--- a/drivers/input/touchscreen/tnetv107x-ts.c
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Texas Instruments TNETV107X Touchscreen Driver
- *
- * Copyright (C) 2010 Texas Instruments
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/errno.h>
-#include <linux/input.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/ctype.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-
-#include <mach/tnetv107x.h>
-
-#define TSC_PENUP_POLL (HZ / 5)
-#define IDLE_TIMEOUT 100 /* msec */
-
-/*
- * The first and last samples of a touch interval are usually garbage and need
- * to be filtered out with these devices. The following definitions control
- * the number of samples skipped.
- */
-#define TSC_HEAD_SKIP 1
-#define TSC_TAIL_SKIP 1
-#define TSC_SKIP (TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1)
-#define TSC_SAMPLES (TSC_SKIP + 1)
-
-/* Register Offsets */
-struct tsc_regs {
- u32 rev;
- u32 tscm;
- u32 bwcm;
- u32 swc;
- u32 adcchnl;
- u32 adcdata;
- u32 chval[4];
-};
-
-/* TSC Mode Configuration Register (tscm) bits */
-#define WMODE BIT(0)
-#define TSKIND BIT(1)
-#define ZMEASURE_EN BIT(2)
-#define IDLE BIT(3)
-#define TSC_EN BIT(4)
-#define STOP BIT(5)
-#define ONE_SHOT BIT(6)
-#define SINGLE BIT(7)
-#define AVG BIT(8)
-#define AVGNUM(x) (((x) & 0x03) << 9)
-#define PVSTC(x) (((x) & 0x07) << 11)
-#define PON BIT(14)
-#define PONBG BIT(15)
-#define AFERST BIT(16)
-
-/* ADC DATA Capture Register bits */
-#define DATA_VALID BIT(16)
-
-/* Register Access Macros */
-#define tsc_read(ts, reg) __raw_readl(&(ts)->regs->reg)
-#define tsc_write(ts, reg, val) __raw_writel(val, &(ts)->regs->reg);
-#define tsc_set_bits(ts, reg, val) \
- tsc_write(ts, reg, tsc_read(ts, reg) | (val))
-#define tsc_clr_bits(ts, reg, val) \
- tsc_write(ts, reg, tsc_read(ts, reg) & ~(val))
-
-struct sample {
- int x, y, p;
-};
-
-struct tsc_data {
- struct input_dev *input_dev;
- struct resource *res;
- struct tsc_regs __iomem *regs;
- struct timer_list timer;
- spinlock_t lock;
- struct clk *clk;
- struct device *dev;
- int sample_count;
- struct sample samples[TSC_SAMPLES];
- int tsc_irq;
-};
-
-static int tsc_read_sample(struct tsc_data *ts, struct sample* sample)
-{
- int x, y, z1, z2, t, p = 0;
- u32 val;
-
- val = tsc_read(ts, chval[0]);
- if (val & DATA_VALID)
- x = val & 0xffff;
- else
- return -EINVAL;
-
- y = tsc_read(ts, chval[1]) & 0xffff;
- z1 = tsc_read(ts, chval[2]) & 0xffff;
- z2 = tsc_read(ts, chval[3]) & 0xffff;
-
- if (z1) {
- t = ((600 * x) * (z2 - z1));
- p = t / (u32) (z1 << 12);
- if (p < 0)
- p = 0;
- }
-
- sample->x = x;
- sample->y = y;
- sample->p = p;
-
- return 0;
-}
-
-static void tsc_poll(unsigned long data)
-{
- struct tsc_data *ts = (struct tsc_data *)data;
- unsigned long flags;
- int i, val, x, y, p;
-
- spin_lock_irqsave(&ts->lock, flags);
-
- if (ts->sample_count >= TSC_SKIP) {
- input_report_abs(ts->input_dev, ABS_PRESSURE, 0);
- input_report_key(ts->input_dev, BTN_TOUCH, 0);
- input_sync(ts->input_dev);
- } else if (ts->sample_count > 0) {
- /*
- * A touch event lasted less than our skip count. Salvage and
- * report anyway.
- */
- for (i = 0, val = 0; i < ts->sample_count; i++)
- val += ts->samples[i].x;
- x = val / ts->sample_count;
-
- for (i = 0, val = 0; i < ts->sample_count; i++)
- val += ts->samples[i].y;
- y = val / ts->sample_count;
-
- for (i = 0, val = 0; i < ts->sample_count; i++)
- val += ts->samples[i].p;
- p = val / ts->sample_count;
-
- input_report_abs(ts->input_dev, ABS_X, x);
- input_report_abs(ts->input_dev, ABS_Y, y);
- input_report_abs(ts->input_dev, ABS_PRESSURE, p);
- input_report_key(ts->input_dev, BTN_TOUCH, 1);
- input_sync(ts->input_dev);
- }
-
- ts->sample_count = 0;
-
- spin_unlock_irqrestore(&ts->lock, flags);
-}
-
-static irqreturn_t tsc_irq(int irq, void *dev_id)
-{
- struct tsc_data *ts = (struct tsc_data *)dev_id;
- struct sample *sample;
- int index;
-
- spin_lock(&ts->lock);
-
- index = ts->sample_count % TSC_SAMPLES;
- sample = &ts->samples[index];
- if (tsc_read_sample(ts, sample) < 0)
- goto out;
-
- if (++ts->sample_count >= TSC_SKIP) {
- index = (ts->sample_count - TSC_TAIL_SKIP - 1) % TSC_SAMPLES;
- sample = &ts->samples[index];
-
- input_report_abs(ts->input_dev, ABS_X, sample->x);
- input_report_abs(ts->input_dev, ABS_Y, sample->y);
- input_report_abs(ts->input_dev, ABS_PRESSURE, sample->p);
- if (ts->sample_count == TSC_SKIP)
- input_report_key(ts->input_dev, BTN_TOUCH, 1);
- input_sync(ts->input_dev);
- }
- mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL);
-out:
- spin_unlock(&ts->lock);
- return IRQ_HANDLED;
-}
-
-static int tsc_start(struct input_dev *dev)
-{
- struct tsc_data *ts = input_get_drvdata(dev);
- unsigned long timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT);
- u32 val;
-
- clk_enable(ts->clk);
-
- /* Go to idle mode, before any initialization */
- while (time_after(timeout, jiffies)) {
- if (tsc_read(ts, tscm) & IDLE)
- break;
- }
-
- if (time_before(timeout, jiffies)) {
- dev_warn(ts->dev, "timeout waiting for idle\n");
- clk_disable(ts->clk);
- return -EIO;
- }
-
- /* Configure TSC Control register*/
- val = (PONBG | PON | PVSTC(4) | ONE_SHOT | ZMEASURE_EN);
- tsc_write(ts, tscm, val);
-
- /* Bring TSC out of reset: Clear AFE reset bit */
- val &= ~(AFERST);
- tsc_write(ts, tscm, val);
-
- /* Configure all pins for hardware control*/
- tsc_write(ts, bwcm, 0);
-
- /* Finally enable the TSC */
- tsc_set_bits(ts, tscm, TSC_EN);
-
- return 0;
-}
-
-static void tsc_stop(struct input_dev *dev)
-{
- struct tsc_data *ts = input_get_drvdata(dev);
-
- tsc_clr_bits(ts, tscm, TSC_EN);
- synchronize_irq(ts->tsc_irq);
- del_timer_sync(&ts->timer);
- clk_disable(ts->clk);
-}
-
-static int tsc_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct tsc_data *ts;
- int error = 0;
- u32 rev = 0;
-
- ts = kzalloc(sizeof(struct tsc_data), GFP_KERNEL);
- if (!ts) {
- dev_err(dev, "cannot allocate device info\n");
- return -ENOMEM;
- }
-
- ts->dev = dev;
- spin_lock_init(&ts->lock);
- setup_timer(&ts->timer, tsc_poll, (unsigned long)ts);
- platform_set_drvdata(pdev, ts);
-
- ts->tsc_irq = platform_get_irq(pdev, 0);
- if (ts->tsc_irq < 0) {
- dev_err(dev, "cannot determine device interrupt\n");
- error = -ENODEV;
- goto error_res;
- }
-
- ts->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!ts->res) {
- dev_err(dev, "cannot determine register area\n");
- error = -ENODEV;
- goto error_res;
- }
-
- if (!request_mem_region(ts->res->start, resource_size(ts->res),
- pdev->name)) {
- dev_err(dev, "cannot claim register memory\n");
- ts->res = NULL;
- error = -EINVAL;
- goto error_res;
- }
-
- ts->regs = ioremap(ts->res->start, resource_size(ts->res));
- if (!ts->regs) {
- dev_err(dev, "cannot map register memory\n");
- error = -ENOMEM;
- goto error_map;
- }
-
- ts->clk = clk_get(dev, NULL);
- if (IS_ERR(ts->clk)) {
- dev_err(dev, "cannot claim device clock\n");
- error = PTR_ERR(ts->clk);
- goto error_clk;
- }
-
- error = request_threaded_irq(ts->tsc_irq, NULL, tsc_irq, IRQF_ONESHOT,
- dev_name(dev), ts);
- if (error < 0) {
- dev_err(ts->dev, "Could not allocate ts irq\n");
- goto error_irq;
- }
-
- ts->input_dev = input_allocate_device();
- if (!ts->input_dev) {
- dev_err(dev, "cannot allocate input device\n");
- error = -ENOMEM;
- goto error_input;
- }
- input_set_drvdata(ts->input_dev, ts);
-
- ts->input_dev->name = pdev->name;
- ts->input_dev->id.bustype = BUS_HOST;
- ts->input_dev->dev.parent = &pdev->dev;
- ts->input_dev->open = tsc_start;
- ts->input_dev->close = tsc_stop;
-
- clk_enable(ts->clk);
- rev = tsc_read(ts, rev);
- ts->input_dev->id.product = ((rev >> 8) & 0x07);
- ts->input_dev->id.version = ((rev >> 16) & 0xfff);
- clk_disable(ts->clk);
-
- __set_bit(EV_KEY, ts->input_dev->evbit);
- __set_bit(EV_ABS, ts->input_dev->evbit);
- __set_bit(BTN_TOUCH, ts->input_dev->keybit);
-
- input_set_abs_params(ts->input_dev, ABS_X, 0, 0xffff, 5, 0);
- input_set_abs_params(ts->input_dev, ABS_Y, 0, 0xffff, 5, 0);
- input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 4095, 128, 0);
-
- error = input_register_device(ts->input_dev);
- if (error < 0) {
- dev_err(dev, "failed input device registration\n");
- goto error_reg;
- }
-
- return 0;
-
-error_reg:
- input_free_device(ts->input_dev);
-error_input:
- free_irq(ts->tsc_irq, ts);
-error_irq:
- clk_put(ts->clk);
-error_clk:
- iounmap(ts->regs);
-error_map:
- release_mem_region(ts->res->start, resource_size(ts->res));
-error_res:
- kfree(ts);
-
- return error;
-}
-
-static int tsc_remove(struct platform_device *pdev)
-{
- struct tsc_data *ts = platform_get_drvdata(pdev);
-
- input_unregister_device(ts->input_dev);
- free_irq(ts->tsc_irq, ts);
- clk_put(ts->clk);
- iounmap(ts->regs);
- release_mem_region(ts->res->start, resource_size(ts->res));
- kfree(ts);
-
- return 0;
-}
-
-static struct platform_driver tsc_driver = {
- .probe = tsc_probe,
- .remove = tsc_remove,
- .driver.name = "tnetv107x-ts",
- .driver.owner = THIS_MODULE,
-};
-module_platform_driver(tsc_driver);
-
-MODULE_AUTHOR("Cyril Chemparathy");
-MODULE_DESCRIPTION("TNETV107X Touchscreen Driver");
-MODULE_ALIAS("platform:tnetv107x-ts");
-MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
index 2175f3419002..01d30cedde46 100644
--- a/drivers/input/touchscreen/zforce_ts.c
+++ b/drivers/input/touchscreen/zforce_ts.c
@@ -29,10 +29,13 @@
#include <linux/sysfs.h>
#include <linux/input/mt.h>
#include <linux/platform_data/zforce_ts.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#define WAIT_TIMEOUT msecs_to_jiffies(1000)
#define FRAME_START 0xee
+#define FRAME_MAXSIZE 257
/* Offsets of the different parts of the payload the controller sends */
#define PAYLOAD_HEADER 0
@@ -64,7 +67,7 @@
#define RESPONSE_STATUS 0X1e
/*
- * Notifications are send by the touch controller without
+ * Notifications are sent by the touch controller without
* being requested by the driver and include for example
* touch indications
*/
@@ -103,8 +106,8 @@ struct zforce_point {
* @suspended device suspended
* @access_mutex serialize i2c-access, to keep multipart reads together
* @command_done completion to wait for the command result
- * @command_mutex serialize commands send to the ic
- * @command_waiting the id of the command that that is currently waiting
+ * @command_mutex serialize commands sent to the ic
+ * @command_waiting the id of the command that is currently waiting
* for a result
* @command_result returned result of the command
*/
@@ -235,7 +238,8 @@ static int zforce_scan_frequency(struct zforce_ts *ts, u16 idle, u16 finger,
(finger & 0xff), ((finger >> 8) & 0xff),
(stylus & 0xff), ((stylus >> 8) & 0xff) };
- dev_dbg(&client->dev, "set scan frequency to (idle: %d, finger: %d, stylus: %d)\n",
+ dev_dbg(&client->dev,
+ "set scan frequency to (idle: %d, finger: %d, stylus: %d)\n",
idle, finger, stylus);
return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
@@ -255,7 +259,7 @@ static int zforce_setconfig(struct zforce_ts *ts, char b1)
static int zforce_start(struct zforce_ts *ts)
{
struct i2c_client *client = ts->client;
- const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+ const struct zforce_ts_platdata *pdata = ts->pdata;
int ret;
dev_dbg(&client->dev, "starting device\n");
@@ -326,13 +330,14 @@ static int zforce_stop(struct zforce_ts *ts)
static int zforce_touch_event(struct zforce_ts *ts, u8 *payload)
{
struct i2c_client *client = ts->client;
- const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+ const struct zforce_ts_platdata *pdata = ts->pdata;
struct zforce_point point;
int count, i, num = 0;
count = payload[0];
if (count > ZFORCE_REPORT_POINTS) {
- dev_warn(&client->dev, "to many coordinates %d, expected max %d\n",
+ dev_warn(&client->dev,
+ "too many coordinates %d, expected max %d\n",
count, ZFORCE_REPORT_POINTS);
count = ZFORCE_REPORT_POINTS;
}
@@ -421,7 +426,7 @@ static int zforce_read_packet(struct zforce_ts *ts, u8 *buf)
goto unlock;
}
- if (buf[PAYLOAD_LENGTH] <= 0 || buf[PAYLOAD_LENGTH] > 255) {
+ if (buf[PAYLOAD_LENGTH] == 0) {
dev_err(&client->dev, "invalid payload length: %d\n",
buf[PAYLOAD_LENGTH]);
ret = -EIO;
@@ -471,9 +476,9 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
{
struct zforce_ts *ts = dev_id;
struct i2c_client *client = ts->client;
- const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+ const struct zforce_ts_platdata *pdata = ts->pdata;
int ret;
- u8 payload_buffer[512];
+ u8 payload_buffer[FRAME_MAXSIZE];
u8 *payload;
/*
@@ -494,8 +499,8 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
while (!gpio_get_value(pdata->gpio_int)) {
ret = zforce_read_packet(ts, payload_buffer);
if (ret < 0) {
- dev_err(&client->dev, "could not read packet, ret: %d\n",
- ret);
+ dev_err(&client->dev,
+ "could not read packet, ret: %d\n", ret);
break;
}
@@ -539,7 +544,8 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
payload[RESPONSE_DATA + 4];
ts->version_rev = (payload[RESPONSE_DATA + 7] << 8) |
payload[RESPONSE_DATA + 6];
- dev_dbg(&ts->client->dev, "Firmware Version %04x:%04x %04x:%04x\n",
+ dev_dbg(&ts->client->dev,
+ "Firmware Version %04x:%04x %04x:%04x\n",
ts->version_major, ts->version_minor,
ts->version_build, ts->version_rev);
@@ -552,7 +558,8 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
break;
default:
- dev_err(&ts->client->dev, "unrecognized response id: 0x%x\n",
+ dev_err(&ts->client->dev,
+ "unrecognized response id: 0x%x\n",
payload[RESPONSE_ID]);
break;
}
@@ -618,7 +625,8 @@ static int zforce_suspend(struct device *dev)
enable_irq_wake(client->irq);
} else if (input->users) {
- dev_dbg(&client->dev, "suspend without being a wakeup source\n");
+ dev_dbg(&client->dev,
+ "suspend without being a wakeup source\n");
ret = zforce_stop(ts);
if (ret)
@@ -684,6 +692,45 @@ static void zforce_reset(void *data)
gpio_set_value(ts->pdata->gpio_rst, 0);
}
+static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev)
+{
+ struct zforce_ts_platdata *pdata;
+ struct device_node *np = dev->of_node;
+
+ if (!np)
+ return ERR_PTR(-ENOENT);
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "failed to allocate platform data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pdata->gpio_int = of_get_gpio(np, 0);
+ if (!gpio_is_valid(pdata->gpio_int)) {
+ dev_err(dev, "failed to get interrupt gpio\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata->gpio_rst = of_get_gpio(np, 1);
+ if (!gpio_is_valid(pdata->gpio_rst)) {
+ dev_err(dev, "failed to get reset gpio\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(np, "x-size", &pdata->x_max)) {
+ dev_err(dev, "failed to get x-size property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(np, "y-size", &pdata->y_max)) {
+ dev_err(dev, "failed to get y-size property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return pdata;
+}
+
static int zforce_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -692,8 +739,11 @@ static int zforce_probe(struct i2c_client *client,
struct input_dev *input_dev;
int ret;
- if (!pdata)
- return -EINVAL;
+ if (!pdata) {
+ pdata = zforce_parse_dt(&client->dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
ts = devm_kzalloc(&client->dev, sizeof(struct zforce_ts), GFP_KERNEL);
if (!ts)
@@ -798,7 +848,7 @@ static int zforce_probe(struct i2c_client *client,
return ret;
}
- /* this gets the firmware version among other informations */
+ /* this gets the firmware version among other information */
ret = zforce_command_wait(ts, COMMAND_STATUS);
if (ret < 0) {
dev_err(&client->dev, "couldn't get status, %d\n", ret);
@@ -829,11 +879,20 @@ static struct i2c_device_id zforce_idtable[] = {
};
MODULE_DEVICE_TABLE(i2c, zforce_idtable);
+#ifdef CONFIG_OF
+static struct of_device_id zforce_dt_idtable[] = {
+ { .compatible = "neonode,zforce" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, zforce_dt_idtable);
+#endif
+
static struct i2c_driver zforce_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "zforce-ts",
.pm = &zforce_pm_ops,
+ .of_match_table = of_match_ptr(zforce_dt_idtable),
},
.probe = zforce_probe,
.id_table = zforce_idtable,
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 79bbc21c1d01..df56e4c74a7e 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -207,7 +207,7 @@ config SHMOBILE_IOMMU
bool "IOMMU for Renesas IPMMU/IPMMUI"
default n
depends on ARM
- depends on SH_MOBILE || COMPILE_TEST
+ depends on ARCH_SHMOBILE || COMPILE_TEST
select IOMMU_API
select ARM_DMA_USE_IOMMU
select SHMOBILE_IPMMU
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index faf0da4bb3a2..c949520bd196 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -963,7 +963,7 @@ static void build_inv_iommu_pasid(struct iommu_cmd *cmd, u16 domid, int pasid,
address &= ~(0xfffULL);
- cmd->data[0] = pasid & PASID_MASK;
+ cmd->data[0] = pasid;
cmd->data[1] = domid;
cmd->data[2] = lower_32_bits(address);
cmd->data[3] = upper_32_bits(address);
@@ -982,10 +982,10 @@ static void build_inv_iotlb_pasid(struct iommu_cmd *cmd, u16 devid, int pasid,
address &= ~(0xfffULL);
cmd->data[0] = devid;
- cmd->data[0] |= (pasid & 0xff) << 16;
+ cmd->data[0] |= ((pasid >> 8) & 0xff) << 16;
cmd->data[0] |= (qdep & 0xff) << 24;
cmd->data[1] = devid;
- cmd->data[1] |= ((pasid >> 8) & 0xfff) << 16;
+ cmd->data[1] |= (pasid & 0xff) << 16;
cmd->data[2] = lower_32_bits(address);
cmd->data[2] |= CMD_INV_IOMMU_PAGES_GN_MASK;
cmd->data[3] = upper_32_bits(address);
@@ -1001,7 +1001,7 @@ static void build_complete_ppr(struct iommu_cmd *cmd, u16 devid, int pasid,
cmd->data[0] = devid;
if (gn) {
- cmd->data[1] = pasid & PASID_MASK;
+ cmd->data[1] = pasid;
cmd->data[2] = CMD_INV_IOMMU_PAGES_GN_MASK;
}
cmd->data[3] = tag & 0x1ff;
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 28b4bea7c109..b76c58dbe30c 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -150,7 +150,7 @@ int amd_iommus_present;
bool amd_iommu_np_cache __read_mostly;
bool amd_iommu_iotlb_sup __read_mostly = true;
-u32 amd_iommu_max_pasids __read_mostly = ~0;
+u32 amd_iommu_max_pasid __read_mostly = ~0;
bool amd_iommu_v2_present __read_mostly;
bool amd_iommu_pc_present __read_mostly;
@@ -1231,14 +1231,16 @@ static int iommu_init_pci(struct amd_iommu *iommu)
if (iommu_feature(iommu, FEATURE_GT)) {
int glxval;
- u32 pasids;
- u64 shift;
+ u32 max_pasid;
+ u64 pasmax;
- shift = iommu->features & FEATURE_PASID_MASK;
- shift >>= FEATURE_PASID_SHIFT;
- pasids = (1 << shift);
+ pasmax = iommu->features & FEATURE_PASID_MASK;
+ pasmax >>= FEATURE_PASID_SHIFT;
+ max_pasid = (1 << (pasmax + 1)) - 1;
- amd_iommu_max_pasids = min(amd_iommu_max_pasids, pasids);
+ amd_iommu_max_pasid = min(amd_iommu_max_pasid, max_pasid);
+
+ BUG_ON(amd_iommu_max_pasid & ~PASID_MASK);
glxval = iommu->features & FEATURE_GLXVAL_MASK;
glxval >>= FEATURE_GLXVAL_SHIFT;
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index cff039df056e..f1a5abf11acf 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -99,7 +99,12 @@
#define FEATURE_GLXVAL_SHIFT 14
#define FEATURE_GLXVAL_MASK (0x03ULL << FEATURE_GLXVAL_SHIFT)
-#define PASID_MASK 0x000fffff
+/* Note:
+ * The current driver only support 16-bit PASID.
+ * Currently, hardware only implement upto 16-bit PASID
+ * even though the spec says it could have upto 20 bits.
+ */
+#define PASID_MASK 0x0000ffff
/* MMIO status bits */
#define MMIO_STATUS_EVT_INT_MASK (1 << 1)
@@ -697,8 +702,8 @@ extern unsigned long *amd_iommu_pd_alloc_bitmap;
*/
extern u32 amd_iommu_unmap_flush;
-/* Smallest number of PASIDs supported by any IOMMU in the system */
-extern u32 amd_iommu_max_pasids;
+/* Smallest max PASID supported by any IOMMU in the system */
+extern u32 amd_iommu_max_pasid;
extern bool amd_iommu_v2_present;
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 1d9ab39af29f..8b89e33a89fe 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -48,7 +48,7 @@
#include <asm/pgalloc.h>
/* Maximum number of stream IDs assigned to a single device */
-#define MAX_MASTER_STREAMIDS 8
+#define MAX_MASTER_STREAMIDS MAX_PHANDLE_ARGS
/* Maximum number of context banks per SMMU */
#define ARM_SMMU_MAX_CBS 128
@@ -60,6 +60,16 @@
#define ARM_SMMU_GR0(smmu) ((smmu)->base)
#define ARM_SMMU_GR1(smmu) ((smmu)->base + (smmu)->pagesize)
+/*
+ * SMMU global address space with conditional offset to access secure
+ * aliases of non-secure registers (e.g. nsCR0: 0x400, nsGFSR: 0x448,
+ * nsGFSYNR0: 0x450)
+ */
+#define ARM_SMMU_GR0_NS(smmu) \
+ ((smmu)->base + \
+ ((smmu->options & ARM_SMMU_OPT_SECURE_CFG_ACCESS) \
+ ? 0x400 : 0))
+
/* Page table bits */
#define ARM_SMMU_PTE_XN (((pteval_t)3) << 53)
#define ARM_SMMU_PTE_CONT (((pteval_t)1) << 52)
@@ -351,6 +361,9 @@ struct arm_smmu_device {
#define ARM_SMMU_FEAT_TRANS_S2 (1 << 3)
#define ARM_SMMU_FEAT_TRANS_NESTED (1 << 4)
u32 features;
+
+#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
+ u32 options;
int version;
u32 num_context_banks;
@@ -401,6 +414,29 @@ struct arm_smmu_domain {
static DEFINE_SPINLOCK(arm_smmu_devices_lock);
static LIST_HEAD(arm_smmu_devices);
+struct arm_smmu_option_prop {
+ u32 opt;
+ const char *prop;
+};
+
+static struct arm_smmu_option_prop arm_smmu_options [] = {
+ { ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" },
+ { 0, NULL},
+};
+
+static void parse_driver_options(struct arm_smmu_device *smmu)
+{
+ int i = 0;
+ do {
+ if (of_property_read_bool(smmu->dev->of_node,
+ arm_smmu_options[i].prop)) {
+ smmu->options |= arm_smmu_options[i].opt;
+ dev_notice(smmu->dev, "option %s\n",
+ arm_smmu_options[i].prop);
+ }
+ } while (arm_smmu_options[++i].opt);
+}
+
static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
struct device_node *dev_node)
{
@@ -614,16 +650,16 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
{
u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
struct arm_smmu_device *smmu = dev;
- void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+ void __iomem *gr0_base = ARM_SMMU_GR0_NS(smmu);
gfsr = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
- if (!gfsr)
- return IRQ_NONE;
-
gfsynr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR0);
gfsynr1 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR1);
gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2);
+ if (!gfsr)
+ return IRQ_NONE;
+
dev_err_ratelimited(smmu->dev,
"Unexpected global fault, this could be serious\n");
dev_err_ratelimited(smmu->dev,
@@ -642,7 +678,7 @@ static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
/* Ensure new page tables are visible to the hardware walker */
if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) {
- dsb();
+ dsb(ishst);
} else {
/*
* If the SMMU can't walk tables in the CPU caches, treat them
@@ -990,9 +1026,8 @@ static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain)
/*
* Recursively free the page tables for this domain. We don't
- * care about speculative TLB filling, because the TLB will be
- * nuked next time this context bank is re-allocated and no devices
- * currently map to these tables.
+ * care about speculative TLB filling because the tables should
+ * not be active in any context bank at this point (SCTLR.M is 0).
*/
pgd = pgd_base;
for (i = 0; i < PTRS_PER_PGD; ++i) {
@@ -1218,7 +1253,7 @@ static bool arm_smmu_pte_is_contiguous_range(unsigned long addr,
static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
unsigned long addr, unsigned long end,
- unsigned long pfn, int flags, int stage)
+ unsigned long pfn, int prot, int stage)
{
pte_t *pte, *start;
pteval_t pteval = ARM_SMMU_PTE_PAGE | ARM_SMMU_PTE_AF | ARM_SMMU_PTE_XN;
@@ -1240,28 +1275,28 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
if (stage == 1) {
pteval |= ARM_SMMU_PTE_AP_UNPRIV | ARM_SMMU_PTE_nG;
- if (!(flags & IOMMU_WRITE) && (flags & IOMMU_READ))
+ if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
pteval |= ARM_SMMU_PTE_AP_RDONLY;
- if (flags & IOMMU_CACHE)
+ if (prot & IOMMU_CACHE)
pteval |= (MAIR_ATTR_IDX_CACHE <<
ARM_SMMU_PTE_ATTRINDX_SHIFT);
} else {
pteval |= ARM_SMMU_PTE_HAP_FAULT;
- if (flags & IOMMU_READ)
+ if (prot & IOMMU_READ)
pteval |= ARM_SMMU_PTE_HAP_READ;
- if (flags & IOMMU_WRITE)
+ if (prot & IOMMU_WRITE)
pteval |= ARM_SMMU_PTE_HAP_WRITE;
- if (flags & IOMMU_CACHE)
+ if (prot & IOMMU_CACHE)
pteval |= ARM_SMMU_PTE_MEMATTR_OIWB;
else
pteval |= ARM_SMMU_PTE_MEMATTR_NC;
}
/* If no access, create a faulting entry to avoid TLB fills */
- if (flags & IOMMU_EXEC)
+ if (prot & IOMMU_EXEC)
pteval &= ~ARM_SMMU_PTE_XN;
- else if (!(flags & (IOMMU_READ | IOMMU_WRITE)))
+ else if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
pteval &= ~ARM_SMMU_PTE_PAGE;
pteval |= ARM_SMMU_PTE_SH_IS;
@@ -1323,7 +1358,7 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
unsigned long addr, unsigned long end,
- phys_addr_t phys, int flags, int stage)
+ phys_addr_t phys, int prot, int stage)
{
int ret;
pmd_t *pmd;
@@ -1347,7 +1382,7 @@ static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
do {
next = pmd_addr_end(addr, end);
ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, end, pfn,
- flags, stage);
+ prot, stage);
phys += next - addr;
} while (pmd++, addr = next, addr < end);
@@ -1356,7 +1391,7 @@ static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
unsigned long addr, unsigned long end,
- phys_addr_t phys, int flags, int stage)
+ phys_addr_t phys, int prot, int stage)
{
int ret = 0;
pud_t *pud;
@@ -1380,7 +1415,7 @@ static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
do {
next = pud_addr_end(addr, end);
ret = arm_smmu_alloc_init_pmd(smmu, pud, addr, next, phys,
- flags, stage);
+ prot, stage);
phys += next - addr;
} while (pud++, addr = next, addr < end);
@@ -1389,7 +1424,7 @@ static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
unsigned long iova, phys_addr_t paddr,
- size_t size, int flags)
+ size_t size, int prot)
{
int ret, stage;
unsigned long end;
@@ -1397,7 +1432,7 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
pgd_t *pgd = root_cfg->pgd;
struct arm_smmu_device *smmu = root_cfg->smmu;
- unsigned long irqflags;
+ unsigned long flags;
if (root_cfg->cbar == CBAR_TYPE_S2_TRANS) {
stage = 2;
@@ -1420,14 +1455,14 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
if (paddr & ~output_mask)
return -ERANGE;
- spin_lock_irqsave(&smmu_domain->lock, irqflags);
+ spin_lock_irqsave(&smmu_domain->lock, flags);
pgd += pgd_index(iova);
end = iova + size;
do {
unsigned long next = pgd_addr_end(iova, end);
ret = arm_smmu_alloc_init_pud(smmu, pgd, iova, next, paddr,
- flags, stage);
+ prot, stage);
if (ret)
goto out_unlock;
@@ -1436,13 +1471,13 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
} while (pgd++, iova != end);
out_unlock:
- spin_unlock_irqrestore(&smmu_domain->lock, irqflags);
+ spin_unlock_irqrestore(&smmu_domain->lock, flags);
return ret;
}
static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
- phys_addr_t paddr, size_t size, int flags)
+ phys_addr_t paddr, size_t size, int prot)
{
struct arm_smmu_domain *smmu_domain = domain->priv;
@@ -1453,7 +1488,7 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
if ((phys_addr_t)iova & ~smmu_domain->output_mask)
return -ERANGE;
- return arm_smmu_handle_mapping(smmu_domain, iova, paddr, size, flags);
+ return arm_smmu_handle_mapping(smmu_domain, iova, paddr, size, prot);
}
static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
@@ -1597,9 +1632,9 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
int i = 0;
u32 reg;
- /* Clear Global FSR */
- reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
- writel(reg, gr0_base + ARM_SMMU_GR0_sGFSR);
+ /* clear global FSR */
+ reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
+ writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
/* Mark all SMRn as invalid and all S2CRn as bypass */
for (i = 0; i < smmu->num_mapping_groups; ++i) {
@@ -1619,7 +1654,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH);
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH);
- reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0);
+ reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
/* Enable fault reporting */
reg |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE);
@@ -1638,7 +1673,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
/* Push the button */
arm_smmu_tlb_sync(smmu);
- writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_sCR0);
+ writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
}
static int arm_smmu_id_size_to_bits(int size)
@@ -1885,6 +1920,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
if (err)
goto out_put_parent;
+ parse_driver_options(smmu);
+
if (smmu->version > 1 &&
smmu->num_context_banks != smmu->num_context_irqs) {
dev_err(dev,
@@ -1969,7 +2006,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
free_irq(smmu->irqs[i], smmu);
/* Turn the thing off */
- writel_relaxed(sCR0_CLIENTPD, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_sCR0);
+ writel(sCR0_CLIENTPD,ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
return 0;
}
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 158156543410..f445c10df8df 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -43,14 +43,24 @@
#include "irq_remapping.h"
-/* No locks are needed as DMA remapping hardware unit
- * list is constructed at boot time and hotplug of
- * these units are not supported by the architecture.
+/*
+ * Assumptions:
+ * 1) The hotplug framework guarentees that DMAR unit will be hot-added
+ * before IO devices managed by that unit.
+ * 2) The hotplug framework guarantees that DMAR unit will be hot-removed
+ * after IO devices managed by that unit.
+ * 3) Hotplug events are rare.
+ *
+ * Locking rules for DMA and interrupt remapping related global data structures:
+ * 1) Use dmar_global_lock in process context
+ * 2) Use RCU in interrupt context
*/
+DECLARE_RWSEM(dmar_global_lock);
LIST_HEAD(dmar_drhd_units);
struct acpi_table_header * __initdata dmar_tbl;
static acpi_size dmar_tbl_size;
+static int dmar_dev_scope_status = 1;
static int alloc_iommu(struct dmar_drhd_unit *drhd);
static void free_iommu(struct intel_iommu *iommu);
@@ -62,73 +72,20 @@ static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
* the very end.
*/
if (drhd->include_all)
- list_add_tail(&drhd->list, &dmar_drhd_units);
+ list_add_tail_rcu(&drhd->list, &dmar_drhd_units);
else
- list_add(&drhd->list, &dmar_drhd_units);
+ list_add_rcu(&drhd->list, &dmar_drhd_units);
}
-static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
- struct pci_dev **dev, u16 segment)
-{
- struct pci_bus *bus;
- struct pci_dev *pdev = NULL;
- struct acpi_dmar_pci_path *path;
- int count;
-
- bus = pci_find_bus(segment, scope->bus);
- path = (struct acpi_dmar_pci_path *)(scope + 1);
- count = (scope->length - sizeof(struct acpi_dmar_device_scope))
- / sizeof(struct acpi_dmar_pci_path);
-
- while (count) {
- if (pdev)
- pci_dev_put(pdev);
- /*
- * Some BIOSes list non-exist devices in DMAR table, just
- * ignore it
- */
- if (!bus) {
- pr_warn("Device scope bus [%d] not found\n", scope->bus);
- break;
- }
- pdev = pci_get_slot(bus, PCI_DEVFN(path->device, path->function));
- if (!pdev) {
- /* warning will be printed below */
- break;
- }
- path ++;
- count --;
- bus = pdev->subordinate;
- }
- if (!pdev) {
- pr_warn("Device scope device [%04x:%02x:%02x.%02x] not found\n",
- segment, scope->bus, path->device, path->function);
- return 0;
- }
- if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT && \
- pdev->subordinate) || (scope->entry_type == \
- ACPI_DMAR_SCOPE_TYPE_BRIDGE && !pdev->subordinate)) {
- pci_dev_put(pdev);
- pr_warn("Device scope type does not match for %s\n",
- pci_name(pdev));
- return -EINVAL;
- }
- *dev = pdev;
- return 0;
-}
-
-int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
- struct pci_dev ***devices, u16 segment)
+void *dmar_alloc_dev_scope(void *start, void *end, int *cnt)
{
struct acpi_dmar_device_scope *scope;
- void * tmp = start;
- int index;
- int ret;
*cnt = 0;
while (start < end) {
scope = start;
- if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
+ if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ACPI ||
+ scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE)
(*cnt)++;
else if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC &&
@@ -138,43 +95,236 @@ int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
start += scope->length;
}
if (*cnt == 0)
- return 0;
+ return NULL;
- *devices = kcalloc(*cnt, sizeof(struct pci_dev *), GFP_KERNEL);
- if (!*devices)
- return -ENOMEM;
+ return kcalloc(*cnt, sizeof(struct dmar_dev_scope), GFP_KERNEL);
+}
- start = tmp;
- index = 0;
- while (start < end) {
+void dmar_free_dev_scope(struct dmar_dev_scope **devices, int *cnt)
+{
+ int i;
+ struct device *tmp_dev;
+
+ if (*devices && *cnt) {
+ for_each_active_dev_scope(*devices, *cnt, i, tmp_dev)
+ put_device(tmp_dev);
+ kfree(*devices);
+ }
+
+ *devices = NULL;
+ *cnt = 0;
+}
+
+/* Optimize out kzalloc()/kfree() for normal cases */
+static char dmar_pci_notify_info_buf[64];
+
+static struct dmar_pci_notify_info *
+dmar_alloc_pci_notify_info(struct pci_dev *dev, unsigned long event)
+{
+ int level = 0;
+ size_t size;
+ struct pci_dev *tmp;
+ struct dmar_pci_notify_info *info;
+
+ BUG_ON(dev->is_virtfn);
+
+ /* Only generate path[] for device addition event */
+ if (event == BUS_NOTIFY_ADD_DEVICE)
+ for (tmp = dev; tmp; tmp = tmp->bus->self)
+ level++;
+
+ size = sizeof(*info) + level * sizeof(struct acpi_dmar_pci_path);
+ if (size <= sizeof(dmar_pci_notify_info_buf)) {
+ info = (struct dmar_pci_notify_info *)dmar_pci_notify_info_buf;
+ } else {
+ info = kzalloc(size, GFP_KERNEL);
+ if (!info) {
+ pr_warn("Out of memory when allocating notify_info "
+ "for %s.\n", pci_name(dev));
+ if (dmar_dev_scope_status == 0)
+ dmar_dev_scope_status = -ENOMEM;
+ return NULL;
+ }
+ }
+
+ info->event = event;
+ info->dev = dev;
+ info->seg = pci_domain_nr(dev->bus);
+ info->level = level;
+ if (event == BUS_NOTIFY_ADD_DEVICE) {
+ for (tmp = dev, level--; tmp; tmp = tmp->bus->self) {
+ info->path[level].device = PCI_SLOT(tmp->devfn);
+ info->path[level].function = PCI_FUNC(tmp->devfn);
+ if (pci_is_root_bus(tmp->bus))
+ info->bus = tmp->bus->number;
+ }
+ }
+
+ return info;
+}
+
+static inline void dmar_free_pci_notify_info(struct dmar_pci_notify_info *info)
+{
+ if ((void *)info != dmar_pci_notify_info_buf)
+ kfree(info);
+}
+
+static bool dmar_match_pci_path(struct dmar_pci_notify_info *info, int bus,
+ struct acpi_dmar_pci_path *path, int count)
+{
+ int i;
+
+ if (info->bus != bus)
+ return false;
+ if (info->level != count)
+ return false;
+
+ for (i = 0; i < count; i++) {
+ if (path[i].device != info->path[i].device ||
+ path[i].function != info->path[i].function)
+ return false;
+ }
+
+ return true;
+}
+
+/* Return: > 0 if match found, 0 if no match found, < 0 if error happens */
+int dmar_insert_dev_scope(struct dmar_pci_notify_info *info,
+ void *start, void*end, u16 segment,
+ struct dmar_dev_scope *devices,
+ int devices_cnt)
+{
+ int i, level;
+ struct device *tmp, *dev = &info->dev->dev;
+ struct acpi_dmar_device_scope *scope;
+ struct acpi_dmar_pci_path *path;
+
+ if (segment != info->seg)
+ return 0;
+
+ for (; start < end; start += scope->length) {
scope = start;
- if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
- scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) {
- ret = dmar_parse_one_dev_scope(scope,
- &(*devices)[index], segment);
- if (ret) {
- dmar_free_dev_scope(devices, cnt);
- return ret;
- }
- index ++;
+ if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_ENDPOINT &&
+ scope->entry_type != ACPI_DMAR_SCOPE_TYPE_BRIDGE)
+ continue;
+
+ path = (struct acpi_dmar_pci_path *)(scope + 1);
+ level = (scope->length - sizeof(*scope)) / sizeof(*path);
+ if (!dmar_match_pci_path(info, scope->bus, path, level))
+ continue;
+
+ if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT) ^
+ (info->dev->hdr_type == PCI_HEADER_TYPE_NORMAL)) {
+ pr_warn("Device scope type does not match for %s\n",
+ pci_name(info->dev));
+ return -EINVAL;
}
- start += scope->length;
+
+ for_each_dev_scope(devices, devices_cnt, i, tmp)
+ if (tmp == NULL) {
+ devices[i].bus = info->dev->bus->number;
+ devices[i].devfn = info->dev->devfn;
+ rcu_assign_pointer(devices[i].dev,
+ get_device(dev));
+ return 1;
+ }
+ BUG_ON(i >= devices_cnt);
}
return 0;
}
-void dmar_free_dev_scope(struct pci_dev ***devices, int *cnt)
+int dmar_remove_dev_scope(struct dmar_pci_notify_info *info, u16 segment,
+ struct dmar_dev_scope *devices, int count)
{
- if (*devices && *cnt) {
- while (--*cnt >= 0)
- pci_dev_put((*devices)[*cnt]);
- kfree(*devices);
- *devices = NULL;
- *cnt = 0;
+ int index;
+ struct device *tmp;
+
+ if (info->seg != segment)
+ return 0;
+
+ for_each_active_dev_scope(devices, count, index, tmp)
+ if (tmp == &info->dev->dev) {
+ rcu_assign_pointer(devices[index].dev, NULL);
+ synchronize_rcu();
+ put_device(tmp);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int dmar_pci_bus_add_dev(struct dmar_pci_notify_info *info)
+{
+ int ret = 0;
+ struct dmar_drhd_unit *dmaru;
+ struct acpi_dmar_hardware_unit *drhd;
+
+ for_each_drhd_unit(dmaru) {
+ if (dmaru->include_all)
+ continue;
+
+ drhd = container_of(dmaru->hdr,
+ struct acpi_dmar_hardware_unit, header);
+ ret = dmar_insert_dev_scope(info, (void *)(drhd + 1),
+ ((void *)drhd) + drhd->header.length,
+ dmaru->segment,
+ dmaru->devices, dmaru->devices_cnt);
+ if (ret != 0)
+ break;
}
+ if (ret >= 0)
+ ret = dmar_iommu_notify_scope_dev(info);
+ if (ret < 0 && dmar_dev_scope_status == 0)
+ dmar_dev_scope_status = ret;
+
+ return ret;
}
+static void dmar_pci_bus_del_dev(struct dmar_pci_notify_info *info)
+{
+ struct dmar_drhd_unit *dmaru;
+
+ for_each_drhd_unit(dmaru)
+ if (dmar_remove_dev_scope(info, dmaru->segment,
+ dmaru->devices, dmaru->devices_cnt))
+ break;
+ dmar_iommu_notify_scope_dev(info);
+}
+
+static int dmar_pci_bus_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pci_dev *pdev = to_pci_dev(data);
+ struct dmar_pci_notify_info *info;
+
+ /* Only care about add/remove events for physical functions */
+ if (pdev->is_virtfn)
+ return NOTIFY_DONE;
+ if (action != BUS_NOTIFY_ADD_DEVICE && action != BUS_NOTIFY_DEL_DEVICE)
+ return NOTIFY_DONE;
+
+ info = dmar_alloc_pci_notify_info(pdev, action);
+ if (!info)
+ return NOTIFY_DONE;
+
+ down_write(&dmar_global_lock);
+ if (action == BUS_NOTIFY_ADD_DEVICE)
+ dmar_pci_bus_add_dev(info);
+ else if (action == BUS_NOTIFY_DEL_DEVICE)
+ dmar_pci_bus_del_dev(info);
+ up_write(&dmar_global_lock);
+
+ dmar_free_pci_notify_info(info);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block dmar_pci_bus_nb = {
+ .notifier_call = dmar_pci_bus_notifier,
+ .priority = INT_MIN,
+};
+
/**
* dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition
* structure which uniquely represent one DMA remapping hardware unit
@@ -196,9 +346,18 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
dmaru->reg_base_addr = drhd->address;
dmaru->segment = drhd->segment;
dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
+ dmaru->devices = dmar_alloc_dev_scope((void *)(drhd + 1),
+ ((void *)drhd) + drhd->header.length,
+ &dmaru->devices_cnt);
+ if (dmaru->devices_cnt && dmaru->devices == NULL) {
+ kfree(dmaru);
+ return -ENOMEM;
+ }
ret = alloc_iommu(dmaru);
if (ret) {
+ dmar_free_dev_scope(&dmaru->devices,
+ &dmaru->devices_cnt);
kfree(dmaru);
return ret;
}
@@ -215,19 +374,24 @@ static void dmar_free_drhd(struct dmar_drhd_unit *dmaru)
kfree(dmaru);
}
-static int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru)
+static int __init dmar_parse_one_andd(struct acpi_dmar_header *header)
{
- struct acpi_dmar_hardware_unit *drhd;
-
- drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr;
-
- if (dmaru->include_all)
- return 0;
+ struct acpi_dmar_andd *andd = (void *)header;
+
+ /* Check for NUL termination within the designated length */
+ if (strnlen(andd->object_name, header->length - 8) == header->length - 8) {
+ WARN_TAINT(1, TAINT_FIRMWARE_WORKAROUND,
+ "Your BIOS is broken; ANDD object name is not NUL-terminated\n"
+ "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
+ dmi_get_system_info(DMI_BIOS_VENDOR),
+ dmi_get_system_info(DMI_BIOS_VERSION),
+ dmi_get_system_info(DMI_PRODUCT_VERSION));
+ return -EINVAL;
+ }
+ pr_info("ANDD device: %x name: %s\n", andd->device_number,
+ andd->object_name);
- return dmar_parse_dev_scope((void *)(drhd + 1),
- ((void *)drhd) + drhd->header.length,
- &dmaru->devices_cnt, &dmaru->devices,
- drhd->segment);
+ return 0;
}
#ifdef CONFIG_ACPI_NUMA
@@ -293,6 +457,10 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
(unsigned long long)rhsa->base_address,
rhsa->proximity_domain);
break;
+ case ACPI_DMAR_TYPE_ANDD:
+ /* We don't print this here because we need to sanity-check
+ it first. So print it in dmar_parse_one_andd() instead. */
+ break;
}
}
@@ -378,6 +546,9 @@ parse_dmar_table(void)
ret = dmar_parse_one_rhsa(entry_header);
#endif
break;
+ case ACPI_DMAR_TYPE_ANDD:
+ ret = dmar_parse_one_andd(entry_header);
+ break;
default:
pr_warn("Unknown DMAR structure type %d\n",
entry_header->type);
@@ -394,14 +565,15 @@ parse_dmar_table(void)
return ret;
}
-static int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
- struct pci_dev *dev)
+static int dmar_pci_device_match(struct dmar_dev_scope devices[],
+ int cnt, struct pci_dev *dev)
{
int index;
+ struct device *tmp;
while (dev) {
- for (index = 0; index < cnt; index++)
- if (dev == devices[index])
+ for_each_active_dev_scope(devices, cnt, index, tmp)
+ if (dev_is_pci(tmp) && dev == to_pci_dev(tmp))
return 1;
/* Check our parent */
@@ -414,11 +586,12 @@ static int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
struct dmar_drhd_unit *
dmar_find_matched_drhd_unit(struct pci_dev *dev)
{
- struct dmar_drhd_unit *dmaru = NULL;
+ struct dmar_drhd_unit *dmaru;
struct acpi_dmar_hardware_unit *drhd;
dev = pci_physfn(dev);
+ rcu_read_lock();
for_each_drhd_unit(dmaru) {
drhd = container_of(dmaru->hdr,
struct acpi_dmar_hardware_unit,
@@ -426,44 +599,128 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev)
if (dmaru->include_all &&
drhd->segment == pci_domain_nr(dev->bus))
- return dmaru;
+ goto out;
if (dmar_pci_device_match(dmaru->devices,
dmaru->devices_cnt, dev))
- return dmaru;
+ goto out;
}
+ dmaru = NULL;
+out:
+ rcu_read_unlock();
- return NULL;
+ return dmaru;
}
-int __init dmar_dev_scope_init(void)
+static void __init dmar_acpi_insert_dev_scope(u8 device_number,
+ struct acpi_device *adev)
{
- static int dmar_dev_scope_initialized;
- struct dmar_drhd_unit *drhd;
- int ret = -ENODEV;
-
- if (dmar_dev_scope_initialized)
- return dmar_dev_scope_initialized;
+ struct dmar_drhd_unit *dmaru;
+ struct acpi_dmar_hardware_unit *drhd;
+ struct acpi_dmar_device_scope *scope;
+ struct device *tmp;
+ int i;
+ struct acpi_dmar_pci_path *path;
- if (list_empty(&dmar_drhd_units))
- goto fail;
+ for_each_drhd_unit(dmaru) {
+ drhd = container_of(dmaru->hdr,
+ struct acpi_dmar_hardware_unit,
+ header);
- list_for_each_entry(drhd, &dmar_drhd_units, list) {
- ret = dmar_parse_dev(drhd);
- if (ret)
- goto fail;
+ for (scope = (void *)(drhd + 1);
+ (unsigned long)scope < ((unsigned long)drhd) + drhd->header.length;
+ scope = ((void *)scope) + scope->length) {
+ if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_ACPI)
+ continue;
+ if (scope->enumeration_id != device_number)
+ continue;
+
+ path = (void *)(scope + 1);
+ pr_info("ACPI device \"%s\" under DMAR at %llx as %02x:%02x.%d\n",
+ dev_name(&adev->dev), dmaru->reg_base_addr,
+ scope->bus, path->device, path->function);
+ for_each_dev_scope(dmaru->devices, dmaru->devices_cnt, i, tmp)
+ if (tmp == NULL) {
+ dmaru->devices[i].bus = scope->bus;
+ dmaru->devices[i].devfn = PCI_DEVFN(path->device,
+ path->function);
+ rcu_assign_pointer(dmaru->devices[i].dev,
+ get_device(&adev->dev));
+ return;
+ }
+ BUG_ON(i >= dmaru->devices_cnt);
+ }
}
+ pr_warn("No IOMMU scope found for ANDD enumeration ID %d (%s)\n",
+ device_number, dev_name(&adev->dev));
+}
- ret = dmar_parse_rmrr_atsr_dev();
- if (ret)
- goto fail;
+static int __init dmar_acpi_dev_scope_init(void)
+{
+ struct acpi_dmar_andd *andd;
+
+ if (dmar_tbl == NULL)
+ return -ENODEV;
- dmar_dev_scope_initialized = 1;
+ for (andd = (void *)dmar_tbl + sizeof(struct acpi_table_dmar);
+ ((unsigned long)andd) < ((unsigned long)dmar_tbl) + dmar_tbl->length;
+ andd = ((void *)andd) + andd->header.length) {
+ if (andd->header.type == ACPI_DMAR_TYPE_ANDD) {
+ acpi_handle h;
+ struct acpi_device *adev;
+
+ if (!ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT,
+ andd->object_name,
+ &h))) {
+ pr_err("Failed to find handle for ACPI object %s\n",
+ andd->object_name);
+ continue;
+ }
+ acpi_bus_get_device(h, &adev);
+ if (!adev) {
+ pr_err("Failed to get device for ACPI object %s\n",
+ andd->object_name);
+ continue;
+ }
+ dmar_acpi_insert_dev_scope(andd->device_number, adev);
+ }
+ }
return 0;
+}
-fail:
- dmar_dev_scope_initialized = ret;
- return ret;
+int __init dmar_dev_scope_init(void)
+{
+ struct pci_dev *dev = NULL;
+ struct dmar_pci_notify_info *info;
+
+ if (dmar_dev_scope_status != 1)
+ return dmar_dev_scope_status;
+
+ if (list_empty(&dmar_drhd_units)) {
+ dmar_dev_scope_status = -ENODEV;
+ } else {
+ dmar_dev_scope_status = 0;
+
+ dmar_acpi_dev_scope_init();
+
+ for_each_pci_dev(dev) {
+ if (dev->is_virtfn)
+ continue;
+
+ info = dmar_alloc_pci_notify_info(dev,
+ BUS_NOTIFY_ADD_DEVICE);
+ if (!info) {
+ return dmar_dev_scope_status;
+ } else {
+ dmar_pci_bus_add_dev(info);
+ dmar_free_pci_notify_info(info);
+ }
+ }
+
+ bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb);
+ }
+
+ return dmar_dev_scope_status;
}
@@ -557,6 +814,7 @@ int __init detect_intel_iommu(void)
{
int ret;
+ down_write(&dmar_global_lock);
ret = dmar_table_detect();
if (ret)
ret = check_zero_address();
@@ -574,6 +832,7 @@ int __init detect_intel_iommu(void)
}
early_acpi_os_unmap_memory((void __iomem *)dmar_tbl, dmar_tbl_size);
dmar_tbl = NULL;
+ up_write(&dmar_global_lock);
return ret ? 1 : -ENODEV;
}
@@ -696,6 +955,7 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
}
iommu->agaw = agaw;
iommu->msagaw = msagaw;
+ iommu->segment = drhd->segment;
iommu->node = -1;
@@ -1386,10 +1646,15 @@ static int __init dmar_free_unused_resources(void)
if (irq_remapping_enabled || intel_iommu_enabled)
return 0;
+ if (dmar_dev_scope_status != 1 && !list_empty(&dmar_drhd_units))
+ bus_unregister_notifier(&pci_bus_type, &dmar_pci_bus_nb);
+
+ down_write(&dmar_global_lock);
list_for_each_entry_safe(dmaru, dmaru_n, &dmar_drhd_units, list) {
list_del(&dmaru->list);
dmar_free_drhd(dmaru);
}
+ up_write(&dmar_global_lock);
return 0;
}
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index a22c86c867fa..69fa7da5e48b 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, Intel Corporation.
+ * Copyright © 2006-2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -10,15 +10,11 @@
* 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.
- *
- * Copyright (C) 2006-2008 Intel Corporation
- * Author: Ashok Raj <ashok.raj@intel.com>
- * Author: Shaohua Li <shaohua.li@intel.com>
- * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
- * Author: Fenghua Yu <fenghua.yu@intel.com>
+ * Authors: David Woodhouse <dwmw2@infradead.org>,
+ * Ashok Raj <ashok.raj@intel.com>,
+ * Shaohua Li <shaohua.li@intel.com>,
+ * Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>,
+ * Fenghua Yu <fenghua.yu@intel.com>
*/
#include <linux/init.h>
@@ -33,6 +29,7 @@
#include <linux/dmar.h>
#include <linux/dma-mapping.h>
#include <linux/mempool.h>
+#include <linux/memory.h>
#include <linux/timer.h>
#include <linux/iova.h>
#include <linux/iommu.h>
@@ -372,14 +369,36 @@ struct dmar_domain {
struct device_domain_info {
struct list_head link; /* link to domain siblings */
struct list_head global; /* link to global list */
- int segment; /* PCI domain */
u8 bus; /* PCI bus number */
u8 devfn; /* PCI devfn number */
- struct pci_dev *dev; /* it's NULL for PCIe-to-PCI bridge */
+ struct device *dev; /* it's NULL for PCIe-to-PCI bridge */
struct intel_iommu *iommu; /* IOMMU used by this device */
struct dmar_domain *domain; /* pointer to domain */
};
+struct dmar_rmrr_unit {
+ struct list_head list; /* list of rmrr units */
+ struct acpi_dmar_header *hdr; /* ACPI header */
+ u64 base_address; /* reserved base address*/
+ u64 end_address; /* reserved end address */
+ struct dmar_dev_scope *devices; /* target devices */
+ int devices_cnt; /* target device count */
+};
+
+struct dmar_atsr_unit {
+ struct list_head list; /* list of ATSR units */
+ struct acpi_dmar_header *hdr; /* ACPI header */
+ struct dmar_dev_scope *devices; /* target devices */
+ int devices_cnt; /* target device count */
+ u8 include_all:1; /* include all ports */
+};
+
+static LIST_HEAD(dmar_atsr_units);
+static LIST_HEAD(dmar_rmrr_units);
+
+#define for_each_rmrr_units(rmrr) \
+ list_for_each_entry(rmrr, &dmar_rmrr_units, list)
+
static void flush_unmaps_timeout(unsigned long data);
static DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0);
@@ -389,6 +408,7 @@ struct deferred_flush_tables {
int next;
struct iova *iova[HIGH_WATER_MARK];
struct dmar_domain *domain[HIGH_WATER_MARK];
+ struct page *freelist[HIGH_WATER_MARK];
};
static struct deferred_flush_tables *deferred_flush;
@@ -402,7 +422,12 @@ static LIST_HEAD(unmaps_to_do);
static int timer_on;
static long list_size;
+static void domain_exit(struct dmar_domain *domain);
static void domain_remove_dev_info(struct dmar_domain *domain);
+static void domain_remove_one_dev_info(struct dmar_domain *domain,
+ struct device *dev);
+static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
+ struct device *dev);
#ifdef CONFIG_INTEL_IOMMU_DEFAULT_ON
int dmar_disabled = 0;
@@ -566,18 +591,31 @@ static struct intel_iommu *domain_get_iommu(struct dmar_domain *domain)
static void domain_update_iommu_coherency(struct dmar_domain *domain)
{
- int i;
-
- i = find_first_bit(domain->iommu_bmp, g_num_of_iommus);
+ struct dmar_drhd_unit *drhd;
+ struct intel_iommu *iommu;
+ int i, found = 0;
- domain->iommu_coherency = i < g_num_of_iommus ? 1 : 0;
+ domain->iommu_coherency = 1;
for_each_set_bit(i, domain->iommu_bmp, g_num_of_iommus) {
+ found = 1;
if (!ecap_coherent(g_iommus[i]->ecap)) {
domain->iommu_coherency = 0;
break;
}
}
+ if (found)
+ return;
+
+ /* No hardware attached; use lowest common denominator */
+ rcu_read_lock();
+ for_each_active_iommu(iommu, drhd) {
+ if (!ecap_coherent(iommu->ecap)) {
+ domain->iommu_coherency = 0;
+ break;
+ }
+ }
+ rcu_read_unlock();
}
static void domain_update_iommu_snooping(struct dmar_domain *domain)
@@ -606,12 +644,15 @@ static void domain_update_iommu_superpage(struct dmar_domain *domain)
}
/* set iommu_superpage to the smallest common denominator */
+ rcu_read_lock();
for_each_active_iommu(iommu, drhd) {
mask &= cap_super_page_val(iommu->cap);
if (!mask) {
break;
}
}
+ rcu_read_unlock();
+
domain->iommu_superpage = fls(mask);
}
@@ -623,32 +664,56 @@ static void domain_update_iommu_cap(struct dmar_domain *domain)
domain_update_iommu_superpage(domain);
}
-static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn)
+static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
{
struct dmar_drhd_unit *drhd = NULL;
+ struct intel_iommu *iommu;
+ struct device *tmp;
+ struct pci_dev *ptmp, *pdev = NULL;
+ u16 segment;
int i;
- for_each_active_drhd_unit(drhd) {
- if (segment != drhd->segment)
+ if (dev_is_pci(dev)) {
+ pdev = to_pci_dev(dev);
+ segment = pci_domain_nr(pdev->bus);
+ } else if (ACPI_COMPANION(dev))
+ dev = &ACPI_COMPANION(dev)->dev;
+
+ rcu_read_lock();
+ for_each_active_iommu(iommu, drhd) {
+ if (pdev && segment != drhd->segment)
continue;
- for (i = 0; i < drhd->devices_cnt; i++) {
- if (drhd->devices[i] &&
- drhd->devices[i]->bus->number == bus &&
- drhd->devices[i]->devfn == devfn)
- return drhd->iommu;
- if (drhd->devices[i] &&
- drhd->devices[i]->subordinate &&
- drhd->devices[i]->subordinate->number <= bus &&
- drhd->devices[i]->subordinate->busn_res.end >= bus)
- return drhd->iommu;
+ for_each_active_dev_scope(drhd->devices,
+ drhd->devices_cnt, i, tmp) {
+ if (tmp == dev) {
+ *bus = drhd->devices[i].bus;
+ *devfn = drhd->devices[i].devfn;
+ goto out;
+ }
+
+ if (!pdev || !dev_is_pci(tmp))
+ continue;
+
+ ptmp = to_pci_dev(tmp);
+ if (ptmp->subordinate &&
+ ptmp->subordinate->number <= pdev->bus->number &&
+ ptmp->subordinate->busn_res.end >= pdev->bus->number)
+ goto got_pdev;
}
- if (drhd->include_all)
- return drhd->iommu;
+ if (pdev && drhd->include_all) {
+ got_pdev:
+ *bus = pdev->bus->number;
+ *devfn = pdev->devfn;
+ goto out;
+ }
}
+ iommu = NULL;
+ out:
+ rcu_read_unlock();
- return NULL;
+ return iommu;
}
static void domain_flush_cache(struct dmar_domain *domain,
@@ -748,7 +813,7 @@ out:
}
static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
- unsigned long pfn, int target_level)
+ unsigned long pfn, int *target_level)
{
int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
struct dma_pte *parent, *pte = NULL;
@@ -763,14 +828,14 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
parent = domain->pgd;
- while (level > 0) {
+ while (1) {
void *tmp_page;
offset = pfn_level_offset(pfn, level);
pte = &parent[offset];
- if (!target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte)))
+ if (!*target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte)))
break;
- if (level == target_level)
+ if (level == *target_level)
break;
if (!dma_pte_present(pte)) {
@@ -791,10 +856,16 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
domain_flush_cache(domain, pte, sizeof(*pte));
}
}
+ if (level == 1)
+ break;
+
parent = phys_to_virt(dma_pte_addr(pte));
level--;
}
+ if (!*target_level)
+ *target_level = level;
+
return pte;
}
@@ -832,7 +903,7 @@ static struct dma_pte *dma_pfn_level_pte(struct dmar_domain *domain,
}
/* clear last level pte, a tlb flush should be followed */
-static int dma_pte_clear_range(struct dmar_domain *domain,
+static void dma_pte_clear_range(struct dmar_domain *domain,
unsigned long start_pfn,
unsigned long last_pfn)
{
@@ -862,8 +933,6 @@ static int dma_pte_clear_range(struct dmar_domain *domain,
(void *)pte - (void *)first_pte);
} while (start_pfn && start_pfn <= last_pfn);
-
- return min_t(int, (large_page - 1) * 9, MAX_AGAW_PFN_WIDTH);
}
static void dma_pte_free_level(struct dmar_domain *domain, int level,
@@ -921,6 +990,123 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain,
}
}
+/* When a page at a given level is being unlinked from its parent, we don't
+ need to *modify* it at all. All we need to do is make a list of all the
+ pages which can be freed just as soon as we've flushed the IOTLB and we
+ know the hardware page-walk will no longer touch them.
+ The 'pte' argument is the *parent* PTE, pointing to the page that is to
+ be freed. */
+static struct page *dma_pte_list_pagetables(struct dmar_domain *domain,
+ int level, struct dma_pte *pte,
+ struct page *freelist)
+{
+ struct page *pg;
+
+ pg = pfn_to_page(dma_pte_addr(pte) >> PAGE_SHIFT);
+ pg->freelist = freelist;
+ freelist = pg;
+
+ if (level == 1)
+ return freelist;
+
+ for (pte = page_address(pg); !first_pte_in_page(pte); pte++) {
+ if (dma_pte_present(pte) && !dma_pte_superpage(pte))
+ freelist = dma_pte_list_pagetables(domain, level - 1,
+ pte, freelist);
+ }
+
+ return freelist;
+}
+
+static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level,
+ struct dma_pte *pte, unsigned long pfn,
+ unsigned long start_pfn,
+ unsigned long last_pfn,
+ struct page *freelist)
+{
+ struct dma_pte *first_pte = NULL, *last_pte = NULL;
+
+ pfn = max(start_pfn, pfn);
+ pte = &pte[pfn_level_offset(pfn, level)];
+
+ do {
+ unsigned long level_pfn;
+
+ if (!dma_pte_present(pte))
+ goto next;
+
+ level_pfn = pfn & level_mask(level);
+
+ /* If range covers entire pagetable, free it */
+ if (start_pfn <= level_pfn &&
+ last_pfn >= level_pfn + level_size(level) - 1) {
+ /* These suborbinate page tables are going away entirely. Don't
+ bother to clear them; we're just going to *free* them. */
+ if (level > 1 && !dma_pte_superpage(pte))
+ freelist = dma_pte_list_pagetables(domain, level - 1, pte, freelist);
+
+ dma_clear_pte(pte);
+ if (!first_pte)
+ first_pte = pte;
+ last_pte = pte;
+ } else if (level > 1) {
+ /* Recurse down into a level that isn't *entirely* obsolete */
+ freelist = dma_pte_clear_level(domain, level - 1,
+ phys_to_virt(dma_pte_addr(pte)),
+ level_pfn, start_pfn, last_pfn,
+ freelist);
+ }
+next:
+ pfn += level_size(level);
+ } while (!first_pte_in_page(++pte) && pfn <= last_pfn);
+
+ if (first_pte)
+ domain_flush_cache(domain, first_pte,
+ (void *)++last_pte - (void *)first_pte);
+
+ return freelist;
+}
+
+/* We can't just free the pages because the IOMMU may still be walking
+ the page tables, and may have cached the intermediate levels. The
+ pages can only be freed after the IOTLB flush has been done. */
+struct page *domain_unmap(struct dmar_domain *domain,
+ unsigned long start_pfn,
+ unsigned long last_pfn)
+{
+ int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
+ struct page *freelist = NULL;
+
+ BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width);
+ BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width);
+ BUG_ON(start_pfn > last_pfn);
+
+ /* we don't need lock here; nobody else touches the iova range */
+ freelist = dma_pte_clear_level(domain, agaw_to_level(domain->agaw),
+ domain->pgd, 0, start_pfn, last_pfn, NULL);
+
+ /* free pgd */
+ if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) {
+ struct page *pgd_page = virt_to_page(domain->pgd);
+ pgd_page->freelist = freelist;
+ freelist = pgd_page;
+
+ domain->pgd = NULL;
+ }
+
+ return freelist;
+}
+
+void dma_free_pagelist(struct page *freelist)
+{
+ struct page *pg;
+
+ while ((pg = freelist)) {
+ freelist = pg->freelist;
+ free_pgtable_page(page_address(pg));
+ }
+}
+
/* iommu handling */
static int iommu_alloc_root_entry(struct intel_iommu *iommu)
{
@@ -1030,7 +1216,7 @@ static void __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did,
break;
case DMA_TLB_PSI_FLUSH:
val = DMA_TLB_PSI_FLUSH|DMA_TLB_IVT|DMA_TLB_DID(did);
- /* Note: always flush non-leaf currently */
+ /* IH bit is passed in as part of address */
val_iva = size_order | addr;
break;
default:
@@ -1069,13 +1255,14 @@ static void __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did,
(unsigned long long)DMA_TLB_IAIG(val));
}
-static struct device_domain_info *iommu_support_dev_iotlb(
- struct dmar_domain *domain, int segment, u8 bus, u8 devfn)
+static struct device_domain_info *
+iommu_support_dev_iotlb (struct dmar_domain *domain, struct intel_iommu *iommu,
+ u8 bus, u8 devfn)
{
int found = 0;
unsigned long flags;
struct device_domain_info *info;
- struct intel_iommu *iommu = device_to_iommu(segment, bus, devfn);
+ struct pci_dev *pdev;
if (!ecap_dev_iotlb_support(iommu->ecap))
return NULL;
@@ -1091,34 +1278,35 @@ static struct device_domain_info *iommu_support_dev_iotlb(
}
spin_unlock_irqrestore(&device_domain_lock, flags);
- if (!found || !info->dev)
+ if (!found || !info->dev || !dev_is_pci(info->dev))
return NULL;
- if (!pci_find_ext_capability(info->dev, PCI_EXT_CAP_ID_ATS))
- return NULL;
+ pdev = to_pci_dev(info->dev);
- if (!dmar_find_matched_atsr_unit(info->dev))
+ if (!pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ATS))
return NULL;
- info->iommu = iommu;
+ if (!dmar_find_matched_atsr_unit(pdev))
+ return NULL;
return info;
}
static void iommu_enable_dev_iotlb(struct device_domain_info *info)
{
- if (!info)
+ if (!info || !dev_is_pci(info->dev))
return;
- pci_enable_ats(info->dev, VTD_PAGE_SHIFT);
+ pci_enable_ats(to_pci_dev(info->dev), VTD_PAGE_SHIFT);
}
static void iommu_disable_dev_iotlb(struct device_domain_info *info)
{
- if (!info->dev || !pci_ats_enabled(info->dev))
+ if (!info->dev || !dev_is_pci(info->dev) ||
+ !pci_ats_enabled(to_pci_dev(info->dev)))
return;
- pci_disable_ats(info->dev);
+ pci_disable_ats(to_pci_dev(info->dev));
}
static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
@@ -1130,24 +1318,31 @@ static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
spin_lock_irqsave(&device_domain_lock, flags);
list_for_each_entry(info, &domain->devices, link) {
- if (!info->dev || !pci_ats_enabled(info->dev))
+ struct pci_dev *pdev;
+ if (!info->dev || !dev_is_pci(info->dev))
+ continue;
+
+ pdev = to_pci_dev(info->dev);
+ if (!pci_ats_enabled(pdev))
continue;
sid = info->bus << 8 | info->devfn;
- qdep = pci_ats_queue_depth(info->dev);
+ qdep = pci_ats_queue_depth(pdev);
qi_flush_dev_iotlb(info->iommu, sid, qdep, addr, mask);
}
spin_unlock_irqrestore(&device_domain_lock, flags);
}
static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, u16 did,
- unsigned long pfn, unsigned int pages, int map)
+ unsigned long pfn, unsigned int pages, int ih, int map)
{
unsigned int mask = ilog2(__roundup_pow_of_two(pages));
uint64_t addr = (uint64_t)pfn << VTD_PAGE_SHIFT;
BUG_ON(pages == 0);
+ if (ih)
+ ih = 1 << 6;
/*
* Fallback to domain selective flush if no PSI support or the size is
* too big.
@@ -1158,7 +1353,7 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, u16 did,
iommu->flush.flush_iotlb(iommu, did, 0, 0,
DMA_TLB_DSI_FLUSH);
else
- iommu->flush.flush_iotlb(iommu, did, addr, mask,
+ iommu->flush.flush_iotlb(iommu, did, addr | ih, mask,
DMA_TLB_PSI_FLUSH);
/*
@@ -1261,10 +1456,6 @@ static int iommu_init_domains(struct intel_iommu *iommu)
return 0;
}
-
-static void domain_exit(struct dmar_domain *domain);
-static void vm_domain_exit(struct dmar_domain *domain);
-
static void free_dmar_iommu(struct intel_iommu *iommu)
{
struct dmar_domain *domain;
@@ -1273,18 +1464,21 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
if ((iommu->domains) && (iommu->domain_ids)) {
for_each_set_bit(i, iommu->domain_ids, cap_ndoms(iommu->cap)) {
+ /*
+ * Domain id 0 is reserved for invalid translation
+ * if hardware supports caching mode.
+ */
+ if (cap_caching_mode(iommu->cap) && i == 0)
+ continue;
+
domain = iommu->domains[i];
clear_bit(i, iommu->domain_ids);
spin_lock_irqsave(&domain->iommu_lock, flags);
count = --domain->iommu_count;
spin_unlock_irqrestore(&domain->iommu_lock, flags);
- if (count == 0) {
- if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE)
- vm_domain_exit(domain);
- else
- domain_exit(domain);
- }
+ if (count == 0)
+ domain_exit(domain);
}
}
@@ -1298,21 +1492,14 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
g_iommus[iommu->seq_id] = NULL;
- /* if all iommus are freed, free g_iommus */
- for (i = 0; i < g_num_of_iommus; i++) {
- if (g_iommus[i])
- break;
- }
-
- if (i == g_num_of_iommus)
- kfree(g_iommus);
-
/* free context mapping */
free_context_table(iommu);
}
-static struct dmar_domain *alloc_domain(void)
+static struct dmar_domain *alloc_domain(bool vm)
{
+ /* domain id for virtual machine, it won't be set in context */
+ static atomic_t vm_domid = ATOMIC_INIT(0);
struct dmar_domain *domain;
domain = alloc_domain_mem();
@@ -1320,8 +1507,15 @@ static struct dmar_domain *alloc_domain(void)
return NULL;
domain->nid = -1;
+ domain->iommu_count = 0;
memset(domain->iommu_bmp, 0, sizeof(domain->iommu_bmp));
domain->flags = 0;
+ spin_lock_init(&domain->iommu_lock);
+ INIT_LIST_HEAD(&domain->devices);
+ if (vm) {
+ domain->id = atomic_inc_return(&vm_domid);
+ domain->flags = DOMAIN_FLAG_VIRTUAL_MACHINE;
+ }
return domain;
}
@@ -1345,6 +1539,7 @@ static int iommu_attach_domain(struct dmar_domain *domain,
}
domain->id = num;
+ domain->iommu_count++;
set_bit(num, iommu->domain_ids);
set_bit(iommu->seq_id, domain->iommu_bmp);
iommu->domains[num] = domain;
@@ -1358,22 +1553,16 @@ static void iommu_detach_domain(struct dmar_domain *domain,
{
unsigned long flags;
int num, ndomains;
- int found = 0;
spin_lock_irqsave(&iommu->lock, flags);
ndomains = cap_ndoms(iommu->cap);
for_each_set_bit(num, iommu->domain_ids, ndomains) {
if (iommu->domains[num] == domain) {
- found = 1;
+ clear_bit(num, iommu->domain_ids);
+ iommu->domains[num] = NULL;
break;
}
}
-
- if (found) {
- clear_bit(num, iommu->domain_ids);
- clear_bit(iommu->seq_id, domain->iommu_bmp);
- iommu->domains[num] = NULL;
- }
spin_unlock_irqrestore(&iommu->lock, flags);
}
@@ -1445,8 +1634,6 @@ static int domain_init(struct dmar_domain *domain, int guest_width)
unsigned long sagaw;
init_iova_domain(&domain->iovad, DMA_32BIT_PFN);
- spin_lock_init(&domain->iommu_lock);
-
domain_reserve_special_ranges(domain);
/* calculate AGAW */
@@ -1465,7 +1652,6 @@ static int domain_init(struct dmar_domain *domain, int guest_width)
return -ENODEV;
}
domain->agaw = agaw;
- INIT_LIST_HEAD(&domain->devices);
if (ecap_coherent(iommu->ecap))
domain->iommu_coherency = 1;
@@ -1477,8 +1663,11 @@ static int domain_init(struct dmar_domain *domain, int guest_width)
else
domain->iommu_snooping = 0;
- domain->iommu_superpage = fls(cap_super_page_val(iommu->cap));
- domain->iommu_count = 1;
+ if (intel_iommu_superpage)
+ domain->iommu_superpage = fls(cap_super_page_val(iommu->cap));
+ else
+ domain->iommu_superpage = 0;
+
domain->nid = iommu->node;
/* always allocate the top pgd */
@@ -1493,6 +1682,7 @@ static void domain_exit(struct dmar_domain *domain)
{
struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu;
+ struct page *freelist = NULL;
/* Domain 0 is reserved, so dont process it */
if (!domain)
@@ -1502,29 +1692,33 @@ static void domain_exit(struct dmar_domain *domain)
if (!intel_iommu_strict)
flush_unmaps_timeout(0);
+ /* remove associated devices */
domain_remove_dev_info(domain);
+
/* destroy iovas */
put_iova_domain(&domain->iovad);
- /* clear ptes */
- dma_pte_clear_range(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
-
- /* free page tables */
- dma_pte_free_pagetable(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
+ freelist = domain_unmap(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
+ /* clear attached or cached domains */
+ rcu_read_lock();
for_each_active_iommu(iommu, drhd)
- if (test_bit(iommu->seq_id, domain->iommu_bmp))
+ if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE ||
+ test_bit(iommu->seq_id, domain->iommu_bmp))
iommu_detach_domain(domain, iommu);
+ rcu_read_unlock();
+
+ dma_free_pagelist(freelist);
free_domain_mem(domain);
}
-static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
- u8 bus, u8 devfn, int translation)
+static int domain_context_mapping_one(struct dmar_domain *domain,
+ struct intel_iommu *iommu,
+ u8 bus, u8 devfn, int translation)
{
struct context_entry *context;
unsigned long flags;
- struct intel_iommu *iommu;
struct dma_pte *pgd;
unsigned long num;
unsigned long ndomains;
@@ -1539,10 +1733,6 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
BUG_ON(translation != CONTEXT_TT_PASS_THROUGH &&
translation != CONTEXT_TT_MULTI_LEVEL);
- iommu = device_to_iommu(segment, bus, devfn);
- if (!iommu)
- return -ENODEV;
-
context = device_to_context_entry(iommu, bus, devfn);
if (!context)
return -ENOMEM;
@@ -1600,7 +1790,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
context_set_domain_id(context, id);
if (translation != CONTEXT_TT_PASS_THROUGH) {
- info = iommu_support_dev_iotlb(domain, segment, bus, devfn);
+ info = iommu_support_dev_iotlb(domain, iommu, bus, devfn);
translation = info ? CONTEXT_TT_DEV_IOTLB :
CONTEXT_TT_MULTI_LEVEL;
}
@@ -1650,27 +1840,32 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
}
static int
-domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev,
- int translation)
+domain_context_mapping(struct dmar_domain *domain, struct device *dev,
+ int translation)
{
int ret;
- struct pci_dev *tmp, *parent;
+ struct pci_dev *pdev, *tmp, *parent;
+ struct intel_iommu *iommu;
+ u8 bus, devfn;
+
+ iommu = device_to_iommu(dev, &bus, &devfn);
+ if (!iommu)
+ return -ENODEV;
- ret = domain_context_mapping_one(domain, pci_domain_nr(pdev->bus),
- pdev->bus->number, pdev->devfn,
+ ret = domain_context_mapping_one(domain, iommu, bus, devfn,
translation);
- if (ret)
+ if (ret || !dev_is_pci(dev))
return ret;
/* dependent device mapping */
+ pdev = to_pci_dev(dev);
tmp = pci_find_upstream_pcie_bridge(pdev);
if (!tmp)
return 0;
/* Secondary interface's bus number and devfn 0 */
parent = pdev->bus->self;
while (parent != tmp) {
- ret = domain_context_mapping_one(domain,
- pci_domain_nr(parent->bus),
+ ret = domain_context_mapping_one(domain, iommu,
parent->bus->number,
parent->devfn, translation);
if (ret)
@@ -1678,33 +1873,33 @@ domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev,
parent = parent->bus->self;
}
if (pci_is_pcie(tmp)) /* this is a PCIe-to-PCI bridge */
- return domain_context_mapping_one(domain,
- pci_domain_nr(tmp->subordinate),
+ return domain_context_mapping_one(domain, iommu,
tmp->subordinate->number, 0,
translation);
else /* this is a legacy PCI bridge */
- return domain_context_mapping_one(domain,
- pci_domain_nr(tmp->bus),
+ return domain_context_mapping_one(domain, iommu,
tmp->bus->number,
tmp->devfn,
translation);
}
-static int domain_context_mapped(struct pci_dev *pdev)
+static int domain_context_mapped(struct device *dev)
{
int ret;
- struct pci_dev *tmp, *parent;
+ struct pci_dev *pdev, *tmp, *parent;
struct intel_iommu *iommu;
+ u8 bus, devfn;
- iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number,
- pdev->devfn);
+ iommu = device_to_iommu(dev, &bus, &devfn);
if (!iommu)
return -ENODEV;
- ret = device_context_mapped(iommu, pdev->bus->number, pdev->devfn);
- if (!ret)
+ ret = device_context_mapped(iommu, bus, devfn);
+ if (!ret || !dev_is_pci(dev))
return ret;
+
/* dependent device mapping */
+ pdev = to_pci_dev(dev);
tmp = pci_find_upstream_pcie_bridge(pdev);
if (!tmp)
return ret;
@@ -1800,7 +1995,7 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
if (!pte) {
largepage_lvl = hardware_largepage_caps(domain, iov_pfn, phys_pfn, sg_res);
- first_pte = pte = pfn_to_dma_pte(domain, iov_pfn, largepage_lvl);
+ first_pte = pte = pfn_to_dma_pte(domain, iov_pfn, &largepage_lvl);
if (!pte)
return -ENOMEM;
/* It is large page*/
@@ -1899,14 +2094,13 @@ static inline void unlink_domain_info(struct device_domain_info *info)
list_del(&info->link);
list_del(&info->global);
if (info->dev)
- info->dev->dev.archdata.iommu = NULL;
+ info->dev->archdata.iommu = NULL;
}
static void domain_remove_dev_info(struct dmar_domain *domain)
{
struct device_domain_info *info;
- unsigned long flags;
- struct intel_iommu *iommu;
+ unsigned long flags, flags2;
spin_lock_irqsave(&device_domain_lock, flags);
while (!list_empty(&domain->devices)) {
@@ -1916,10 +2110,23 @@ static void domain_remove_dev_info(struct dmar_domain *domain)
spin_unlock_irqrestore(&device_domain_lock, flags);
iommu_disable_dev_iotlb(info);
- iommu = device_to_iommu(info->segment, info->bus, info->devfn);
- iommu_detach_dev(iommu, info->bus, info->devfn);
- free_devinfo_mem(info);
+ iommu_detach_dev(info->iommu, info->bus, info->devfn);
+ if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) {
+ iommu_detach_dependent_devices(info->iommu, info->dev);
+ /* clear this iommu in iommu_bmp, update iommu count
+ * and capabilities
+ */
+ spin_lock_irqsave(&domain->iommu_lock, flags2);
+ if (test_and_clear_bit(info->iommu->seq_id,
+ domain->iommu_bmp)) {
+ domain->iommu_count--;
+ domain_update_iommu_cap(domain);
+ }
+ spin_unlock_irqrestore(&domain->iommu_lock, flags2);
+ }
+
+ free_devinfo_mem(info);
spin_lock_irqsave(&device_domain_lock, flags);
}
spin_unlock_irqrestore(&device_domain_lock, flags);
@@ -1927,155 +2134,151 @@ static void domain_remove_dev_info(struct dmar_domain *domain)
/*
* find_domain
- * Note: we use struct pci_dev->dev.archdata.iommu stores the info
+ * Note: we use struct device->archdata.iommu stores the info
*/
-static struct dmar_domain *
-find_domain(struct pci_dev *pdev)
+static struct dmar_domain *find_domain(struct device *dev)
{
struct device_domain_info *info;
/* No lock here, assumes no domain exit in normal case */
- info = pdev->dev.archdata.iommu;
+ info = dev->archdata.iommu;
if (info)
return info->domain;
return NULL;
}
+static inline struct device_domain_info *
+dmar_search_domain_by_dev_info(int segment, int bus, int devfn)
+{
+ struct device_domain_info *info;
+
+ list_for_each_entry(info, &device_domain_list, global)
+ if (info->iommu->segment == segment && info->bus == bus &&
+ info->devfn == devfn)
+ return info;
+
+ return NULL;
+}
+
+static struct dmar_domain *dmar_insert_dev_info(struct intel_iommu *iommu,
+ int bus, int devfn,
+ struct device *dev,
+ struct dmar_domain *domain)
+{
+ struct dmar_domain *found = NULL;
+ struct device_domain_info *info;
+ unsigned long flags;
+
+ info = alloc_devinfo_mem();
+ if (!info)
+ return NULL;
+
+ info->bus = bus;
+ info->devfn = devfn;
+ info->dev = dev;
+ info->domain = domain;
+ info->iommu = iommu;
+ if (!dev)
+ domain->flags |= DOMAIN_FLAG_P2P_MULTIPLE_DEVICES;
+
+ spin_lock_irqsave(&device_domain_lock, flags);
+ if (dev)
+ found = find_domain(dev);
+ else {
+ struct device_domain_info *info2;
+ info2 = dmar_search_domain_by_dev_info(iommu->segment, bus, devfn);
+ if (info2)
+ found = info2->domain;
+ }
+ if (found) {
+ spin_unlock_irqrestore(&device_domain_lock, flags);
+ free_devinfo_mem(info);
+ /* Caller must free the original domain */
+ return found;
+ }
+
+ list_add(&info->link, &domain->devices);
+ list_add(&info->global, &device_domain_list);
+ if (dev)
+ dev->archdata.iommu = info;
+ spin_unlock_irqrestore(&device_domain_lock, flags);
+
+ return domain;
+}
+
/* domain is initialized */
-static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
+static struct dmar_domain *get_domain_for_dev(struct device *dev, int gaw)
{
- struct dmar_domain *domain, *found = NULL;
- struct intel_iommu *iommu;
- struct dmar_drhd_unit *drhd;
- struct device_domain_info *info, *tmp;
- struct pci_dev *dev_tmp;
+ struct dmar_domain *domain, *free = NULL;
+ struct intel_iommu *iommu = NULL;
+ struct device_domain_info *info;
+ struct pci_dev *dev_tmp = NULL;
unsigned long flags;
- int bus = 0, devfn = 0;
- int segment;
- int ret;
+ u8 bus, devfn, bridge_bus, bridge_devfn;
- domain = find_domain(pdev);
+ domain = find_domain(dev);
if (domain)
return domain;
- segment = pci_domain_nr(pdev->bus);
+ if (dev_is_pci(dev)) {
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u16 segment;
- dev_tmp = pci_find_upstream_pcie_bridge(pdev);
- if (dev_tmp) {
- if (pci_is_pcie(dev_tmp)) {
- bus = dev_tmp->subordinate->number;
- devfn = 0;
- } else {
- bus = dev_tmp->bus->number;
- devfn = dev_tmp->devfn;
- }
- spin_lock_irqsave(&device_domain_lock, flags);
- list_for_each_entry(info, &device_domain_list, global) {
- if (info->segment == segment &&
- info->bus == bus && info->devfn == devfn) {
- found = info->domain;
- break;
+ segment = pci_domain_nr(pdev->bus);
+ dev_tmp = pci_find_upstream_pcie_bridge(pdev);
+ if (dev_tmp) {
+ if (pci_is_pcie(dev_tmp)) {
+ bridge_bus = dev_tmp->subordinate->number;
+ bridge_devfn = 0;
+ } else {
+ bridge_bus = dev_tmp->bus->number;
+ bridge_devfn = dev_tmp->devfn;
}
- }
- spin_unlock_irqrestore(&device_domain_lock, flags);
- /* pcie-pci bridge already has a domain, uses it */
- if (found) {
- domain = found;
- goto found_domain;
+ spin_lock_irqsave(&device_domain_lock, flags);
+ info = dmar_search_domain_by_dev_info(segment, bus, devfn);
+ if (info) {
+ iommu = info->iommu;
+ domain = info->domain;
+ }
+ spin_unlock_irqrestore(&device_domain_lock, flags);
+ /* pcie-pci bridge already has a domain, uses it */
+ if (info)
+ goto found_domain;
}
}
- domain = alloc_domain();
- if (!domain)
+ iommu = device_to_iommu(dev, &bus, &devfn);
+ if (!iommu)
goto error;
- /* Allocate new domain for the device */
- drhd = dmar_find_matched_drhd_unit(pdev);
- if (!drhd) {
- printk(KERN_ERR "IOMMU: can't find DMAR for device %s\n",
- pci_name(pdev));
- free_domain_mem(domain);
- return NULL;
- }
- iommu = drhd->iommu;
-
- ret = iommu_attach_domain(domain, iommu);
- if (ret) {
+ /* Allocate and initialize new domain for the device */
+ domain = alloc_domain(false);
+ if (!domain)
+ goto error;
+ if (iommu_attach_domain(domain, iommu)) {
free_domain_mem(domain);
+ domain = NULL;
goto error;
}
-
- if (domain_init(domain, gaw)) {
- domain_exit(domain);
+ free = domain;
+ if (domain_init(domain, gaw))
goto error;
- }
/* register pcie-to-pci device */
if (dev_tmp) {
- info = alloc_devinfo_mem();
- if (!info) {
- domain_exit(domain);
+ domain = dmar_insert_dev_info(iommu, bridge_bus, bridge_devfn,
+ NULL, domain);
+ if (!domain)
goto error;
- }
- info->segment = segment;
- info->bus = bus;
- info->devfn = devfn;
- info->dev = NULL;
- info->domain = domain;
- /* This domain is shared by devices under p2p bridge */
- domain->flags |= DOMAIN_FLAG_P2P_MULTIPLE_DEVICES;
-
- /* pcie-to-pci bridge already has a domain, uses it */
- found = NULL;
- spin_lock_irqsave(&device_domain_lock, flags);
- list_for_each_entry(tmp, &device_domain_list, global) {
- if (tmp->segment == segment &&
- tmp->bus == bus && tmp->devfn == devfn) {
- found = tmp->domain;
- break;
- }
- }
- if (found) {
- spin_unlock_irqrestore(&device_domain_lock, flags);
- free_devinfo_mem(info);
- domain_exit(domain);
- domain = found;
- } else {
- list_add(&info->link, &domain->devices);
- list_add(&info->global, &device_domain_list);
- spin_unlock_irqrestore(&device_domain_lock, flags);
- }
}
found_domain:
- info = alloc_devinfo_mem();
- if (!info)
- goto error;
- info->segment = segment;
- info->bus = pdev->bus->number;
- info->devfn = pdev->devfn;
- info->dev = pdev;
- info->domain = domain;
- spin_lock_irqsave(&device_domain_lock, flags);
- /* somebody is fast */
- found = find_domain(pdev);
- if (found != NULL) {
- spin_unlock_irqrestore(&device_domain_lock, flags);
- if (found != domain) {
- domain_exit(domain);
- domain = found;
- }
- free_devinfo_mem(info);
- return domain;
- }
- list_add(&info->link, &domain->devices);
- list_add(&info->global, &device_domain_list);
- pdev->dev.archdata.iommu = info;
- spin_unlock_irqrestore(&device_domain_lock, flags);
- return domain;
+ domain = dmar_insert_dev_info(iommu, bus, devfn, dev, domain);
error:
- /* recheck it here, maybe others set it */
- return find_domain(pdev);
+ if (free != domain)
+ domain_exit(free);
+
+ return domain;
}
static int iommu_identity_mapping;
@@ -2109,14 +2312,14 @@ static int iommu_domain_identity_map(struct dmar_domain *domain,
DMA_PTE_READ|DMA_PTE_WRITE);
}
-static int iommu_prepare_identity_map(struct pci_dev *pdev,
+static int iommu_prepare_identity_map(struct device *dev,
unsigned long long start,
unsigned long long end)
{
struct dmar_domain *domain;
int ret;
- domain = get_domain_for_dev(pdev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
+ domain = get_domain_for_dev(dev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
if (!domain)
return -ENOMEM;
@@ -2126,13 +2329,13 @@ static int iommu_prepare_identity_map(struct pci_dev *pdev,
up to start with in si_domain */
if (domain == si_domain && hw_pass_through) {
printk("Ignoring identity map for HW passthrough device %s [0x%Lx - 0x%Lx]\n",
- pci_name(pdev), start, end);
+ dev_name(dev), start, end);
return 0;
}
printk(KERN_INFO
"IOMMU: Setting identity map for device %s [0x%Lx - 0x%Lx]\n",
- pci_name(pdev), start, end);
+ dev_name(dev), start, end);
if (end < start) {
WARN(1, "Your BIOS is broken; RMRR ends before it starts!\n"
@@ -2160,7 +2363,7 @@ static int iommu_prepare_identity_map(struct pci_dev *pdev,
goto error;
/* context entry init */
- ret = domain_context_mapping(domain, pdev, CONTEXT_TT_MULTI_LEVEL);
+ ret = domain_context_mapping(domain, dev, CONTEXT_TT_MULTI_LEVEL);
if (ret)
goto error;
@@ -2172,12 +2375,12 @@ static int iommu_prepare_identity_map(struct pci_dev *pdev,
}
static inline int iommu_prepare_rmrr_dev(struct dmar_rmrr_unit *rmrr,
- struct pci_dev *pdev)
+ struct device *dev)
{
- if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
+ if (dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
return 0;
- return iommu_prepare_identity_map(pdev, rmrr->base_address,
- rmrr->end_address);
+ return iommu_prepare_identity_map(dev, rmrr->base_address,
+ rmrr->end_address);
}
#ifdef CONFIG_INTEL_IOMMU_FLOPPY_WA
@@ -2191,7 +2394,7 @@ static inline void iommu_prepare_isa(void)
return;
printk(KERN_INFO "IOMMU: Prepare 0-16MiB unity mapping for LPC\n");
- ret = iommu_prepare_identity_map(pdev, 0, 16*1024*1024 - 1);
+ ret = iommu_prepare_identity_map(&pdev->dev, 0, 16*1024*1024 - 1);
if (ret)
printk(KERN_ERR "IOMMU: Failed to create 0-16MiB identity map; "
@@ -2213,10 +2416,12 @@ static int __init si_domain_init(int hw)
struct intel_iommu *iommu;
int nid, ret = 0;
- si_domain = alloc_domain();
+ si_domain = alloc_domain(false);
if (!si_domain)
return -EFAULT;
+ si_domain->flags = DOMAIN_FLAG_STATIC_IDENTITY;
+
for_each_active_iommu(iommu, drhd) {
ret = iommu_attach_domain(si_domain, iommu);
if (ret) {
@@ -2230,7 +2435,6 @@ static int __init si_domain_init(int hw)
return -EFAULT;
}
- si_domain->flags = DOMAIN_FLAG_STATIC_IDENTITY;
pr_debug("IOMMU: identity mapping domain is domain %d\n",
si_domain->id);
@@ -2252,16 +2456,14 @@ static int __init si_domain_init(int hw)
return 0;
}
-static void domain_remove_one_dev_info(struct dmar_domain *domain,
- struct pci_dev *pdev);
-static int identity_mapping(struct pci_dev *pdev)
+static int identity_mapping(struct device *dev)
{
struct device_domain_info *info;
if (likely(!iommu_identity_mapping))
return 0;
- info = pdev->dev.archdata.iommu;
+ info = dev->archdata.iommu;
if (info && info != DUMMY_DEVICE_DOMAIN_INFO)
return (info->domain == si_domain);
@@ -2269,111 +2471,112 @@ static int identity_mapping(struct pci_dev *pdev)
}
static int domain_add_dev_info(struct dmar_domain *domain,
- struct pci_dev *pdev,
- int translation)
+ struct device *dev, int translation)
{
- struct device_domain_info *info;
- unsigned long flags;
+ struct dmar_domain *ndomain;
+ struct intel_iommu *iommu;
+ u8 bus, devfn;
int ret;
- info = alloc_devinfo_mem();
- if (!info)
- return -ENOMEM;
-
- info->segment = pci_domain_nr(pdev->bus);
- info->bus = pdev->bus->number;
- info->devfn = pdev->devfn;
- info->dev = pdev;
- info->domain = domain;
+ iommu = device_to_iommu(dev, &bus, &devfn);
+ if (!iommu)
+ return -ENODEV;
- spin_lock_irqsave(&device_domain_lock, flags);
- list_add(&info->link, &domain->devices);
- list_add(&info->global, &device_domain_list);
- pdev->dev.archdata.iommu = info;
- spin_unlock_irqrestore(&device_domain_lock, flags);
+ ndomain = dmar_insert_dev_info(iommu, bus, devfn, dev, domain);
+ if (ndomain != domain)
+ return -EBUSY;
- ret = domain_context_mapping(domain, pdev, translation);
+ ret = domain_context_mapping(domain, dev, translation);
if (ret) {
- spin_lock_irqsave(&device_domain_lock, flags);
- unlink_domain_info(info);
- spin_unlock_irqrestore(&device_domain_lock, flags);
- free_devinfo_mem(info);
+ domain_remove_one_dev_info(domain, dev);
return ret;
}
return 0;
}
-static bool device_has_rmrr(struct pci_dev *dev)
+static bool device_has_rmrr(struct device *dev)
{
struct dmar_rmrr_unit *rmrr;
+ struct device *tmp;
int i;
+ rcu_read_lock();
for_each_rmrr_units(rmrr) {
- for (i = 0; i < rmrr->devices_cnt; i++) {
- /*
- * Return TRUE if this RMRR contains the device that
- * is passed in.
- */
- if (rmrr->devices[i] == dev)
+ /*
+ * Return TRUE if this RMRR contains the device that
+ * is passed in.
+ */
+ for_each_active_dev_scope(rmrr->devices,
+ rmrr->devices_cnt, i, tmp)
+ if (tmp == dev) {
+ rcu_read_unlock();
return true;
- }
+ }
}
+ rcu_read_unlock();
return false;
}
-static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
+static int iommu_should_identity_map(struct device *dev, int startup)
{
- /*
- * We want to prevent any device associated with an RMRR from
- * getting placed into the SI Domain. This is done because
- * problems exist when devices are moved in and out of domains
- * and their respective RMRR info is lost. We exempt USB devices
- * from this process due to their usage of RMRRs that are known
- * to not be needed after BIOS hand-off to OS.
- */
- if (device_has_rmrr(pdev) &&
- (pdev->class >> 8) != PCI_CLASS_SERIAL_USB)
- return 0;
+ if (dev_is_pci(dev)) {
+ struct pci_dev *pdev = to_pci_dev(dev);
- if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
- return 1;
+ /*
+ * We want to prevent any device associated with an RMRR from
+ * getting placed into the SI Domain. This is done because
+ * problems exist when devices are moved in and out of domains
+ * and their respective RMRR info is lost. We exempt USB devices
+ * from this process due to their usage of RMRRs that are known
+ * to not be needed after BIOS hand-off to OS.
+ */
+ if (device_has_rmrr(dev) &&
+ (pdev->class >> 8) != PCI_CLASS_SERIAL_USB)
+ return 0;
- if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
- return 1;
+ if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
+ return 1;
- if (!(iommu_identity_mapping & IDENTMAP_ALL))
- return 0;
+ if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
+ return 1;
- /*
- * We want to start off with all devices in the 1:1 domain, and
- * take them out later if we find they can't access all of memory.
- *
- * However, we can't do this for PCI devices behind bridges,
- * because all PCI devices behind the same bridge will end up
- * with the same source-id on their transactions.
- *
- * Practically speaking, we can't change things around for these
- * devices at run-time, because we can't be sure there'll be no
- * DMA transactions in flight for any of their siblings.
- *
- * So PCI devices (unless they're on the root bus) as well as
- * their parent PCI-PCI or PCIe-PCI bridges must be left _out_ of
- * the 1:1 domain, just in _case_ one of their siblings turns out
- * not to be able to map all of memory.
- */
- if (!pci_is_pcie(pdev)) {
- if (!pci_is_root_bus(pdev->bus))
+ if (!(iommu_identity_mapping & IDENTMAP_ALL))
return 0;
- if (pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI)
+
+ /*
+ * We want to start off with all devices in the 1:1 domain, and
+ * take them out later if we find they can't access all of memory.
+ *
+ * However, we can't do this for PCI devices behind bridges,
+ * because all PCI devices behind the same bridge will end up
+ * with the same source-id on their transactions.
+ *
+ * Practically speaking, we can't change things around for these
+ * devices at run-time, because we can't be sure there'll be no
+ * DMA transactions in flight for any of their siblings.
+ *
+ * So PCI devices (unless they're on the root bus) as well as
+ * their parent PCI-PCI or PCIe-PCI bridges must be left _out_ of
+ * the 1:1 domain, just in _case_ one of their siblings turns out
+ * not to be able to map all of memory.
+ */
+ if (!pci_is_pcie(pdev)) {
+ if (!pci_is_root_bus(pdev->bus))
+ return 0;
+ if (pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI)
+ return 0;
+ } else if (pci_pcie_type(pdev) == PCI_EXP_TYPE_PCI_BRIDGE)
return 0;
- } else if (pci_pcie_type(pdev) == PCI_EXP_TYPE_PCI_BRIDGE)
- return 0;
+ } else {
+ if (device_has_rmrr(dev))
+ return 0;
+ }
- /*
+ /*
* At boot time, we don't yet know if devices will be 64-bit capable.
- * Assume that they will -- if they turn out not to be, then we can
+ * Assume that they will — if they turn out not to be, then we can
* take them out of the 1:1 domain later.
*/
if (!startup) {
@@ -2381,42 +2584,77 @@ static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
* If the device's dma_mask is less than the system's memory
* size then this is not a candidate for identity mapping.
*/
- u64 dma_mask = pdev->dma_mask;
+ u64 dma_mask = *dev->dma_mask;
- if (pdev->dev.coherent_dma_mask &&
- pdev->dev.coherent_dma_mask < dma_mask)
- dma_mask = pdev->dev.coherent_dma_mask;
+ if (dev->coherent_dma_mask &&
+ dev->coherent_dma_mask < dma_mask)
+ dma_mask = dev->coherent_dma_mask;
- return dma_mask >= dma_get_required_mask(&pdev->dev);
+ return dma_mask >= dma_get_required_mask(dev);
}
return 1;
}
+static int __init dev_prepare_static_identity_mapping(struct device *dev, int hw)
+{
+ int ret;
+
+ if (!iommu_should_identity_map(dev, 1))
+ return 0;
+
+ ret = domain_add_dev_info(si_domain, dev,
+ hw ? CONTEXT_TT_PASS_THROUGH :
+ CONTEXT_TT_MULTI_LEVEL);
+ if (!ret)
+ pr_info("IOMMU: %s identity mapping for device %s\n",
+ hw ? "hardware" : "software", dev_name(dev));
+ else if (ret == -ENODEV)
+ /* device not associated with an iommu */
+ ret = 0;
+
+ return ret;
+}
+
+
static int __init iommu_prepare_static_identity_mapping(int hw)
{
struct pci_dev *pdev = NULL;
- int ret;
+ struct dmar_drhd_unit *drhd;
+ struct intel_iommu *iommu;
+ struct device *dev;
+ int i;
+ int ret = 0;
ret = si_domain_init(hw);
if (ret)
return -EFAULT;
for_each_pci_dev(pdev) {
- if (iommu_should_identity_map(pdev, 1)) {
- ret = domain_add_dev_info(si_domain, pdev,
- hw ? CONTEXT_TT_PASS_THROUGH :
- CONTEXT_TT_MULTI_LEVEL);
- if (ret) {
- /* device not associated with an iommu */
- if (ret == -ENODEV)
- continue;
- return ret;
+ ret = dev_prepare_static_identity_mapping(&pdev->dev, hw);
+ if (ret)
+ return ret;
+ }
+
+ for_each_active_iommu(iommu, drhd)
+ for_each_active_dev_scope(drhd->devices, drhd->devices_cnt, i, dev) {
+ struct acpi_device_physical_node *pn;
+ struct acpi_device *adev;
+
+ if (dev->bus != &acpi_bus_type)
+ continue;
+
+ adev= to_acpi_device(dev);
+ mutex_lock(&adev->physical_node_lock);
+ list_for_each_entry(pn, &adev->physical_node_list, node) {
+ ret = dev_prepare_static_identity_mapping(pn->dev, hw);
+ if (ret)
+ break;
}
- pr_info("IOMMU: %s identity mapping for device %s\n",
- hw ? "hardware" : "software", pci_name(pdev));
+ mutex_unlock(&adev->physical_node_lock);
+ if (ret)
+ return ret;
}
- }
return 0;
}
@@ -2425,7 +2663,7 @@ static int __init init_dmars(void)
{
struct dmar_drhd_unit *drhd;
struct dmar_rmrr_unit *rmrr;
- struct pci_dev *pdev;
+ struct device *dev;
struct intel_iommu *iommu;
int i, ret;
@@ -2461,7 +2699,7 @@ static int __init init_dmars(void)
sizeof(struct deferred_flush_tables), GFP_KERNEL);
if (!deferred_flush) {
ret = -ENOMEM;
- goto error;
+ goto free_g_iommus;
}
for_each_active_iommu(iommu, drhd) {
@@ -2469,7 +2707,7 @@ static int __init init_dmars(void)
ret = iommu_init_domains(iommu);
if (ret)
- goto error;
+ goto free_iommu;
/*
* TBD:
@@ -2479,7 +2717,7 @@ static int __init init_dmars(void)
ret = iommu_alloc_root_entry(iommu);
if (ret) {
printk(KERN_ERR "IOMMU: allocate root entry failed\n");
- goto error;
+ goto free_iommu;
}
if (!ecap_pass_through(iommu->ecap))
hw_pass_through = 0;
@@ -2548,7 +2786,7 @@ static int __init init_dmars(void)
ret = iommu_prepare_static_identity_mapping(hw_pass_through);
if (ret) {
printk(KERN_CRIT "Failed to setup IOMMU pass-through\n");
- goto error;
+ goto free_iommu;
}
}
/*
@@ -2567,15 +2805,10 @@ static int __init init_dmars(void)
*/
printk(KERN_INFO "IOMMU: Setting RMRR:\n");
for_each_rmrr_units(rmrr) {
- for (i = 0; i < rmrr->devices_cnt; i++) {
- pdev = rmrr->devices[i];
- /*
- * some BIOS lists non-exist devices in DMAR
- * table.
- */
- if (!pdev)
- continue;
- ret = iommu_prepare_rmrr_dev(rmrr, pdev);
+ /* some BIOS lists non-exist devices in DMAR table. */
+ for_each_active_dev_scope(rmrr->devices, rmrr->devices_cnt,
+ i, dev) {
+ ret = iommu_prepare_rmrr_dev(rmrr, dev);
if (ret)
printk(KERN_ERR
"IOMMU: mapping reserved region failed\n");
@@ -2606,7 +2839,7 @@ static int __init init_dmars(void)
ret = dmar_set_interrupt(iommu);
if (ret)
- goto error;
+ goto free_iommu;
iommu_set_root_entry(iommu);
@@ -2615,17 +2848,20 @@ static int __init init_dmars(void)
ret = iommu_enable_translation(iommu);
if (ret)
- goto error;
+ goto free_iommu;
iommu_disable_protect_mem_regions(iommu);
}
return 0;
-error:
+
+free_iommu:
for_each_active_iommu(iommu, drhd)
free_dmar_iommu(iommu);
kfree(deferred_flush);
+free_g_iommus:
kfree(g_iommus);
+error:
return ret;
}
@@ -2634,7 +2870,6 @@ static struct iova *intel_alloc_iova(struct device *dev,
struct dmar_domain *domain,
unsigned long nrpages, uint64_t dma_mask)
{
- struct pci_dev *pdev = to_pci_dev(dev);
struct iova *iova = NULL;
/* Restrict dma_mask to the width that the iommu can handle */
@@ -2654,34 +2889,31 @@ static struct iova *intel_alloc_iova(struct device *dev,
iova = alloc_iova(&domain->iovad, nrpages, IOVA_PFN(dma_mask), 1);
if (unlikely(!iova)) {
printk(KERN_ERR "Allocating %ld-page iova for %s failed",
- nrpages, pci_name(pdev));
+ nrpages, dev_name(dev));
return NULL;
}
return iova;
}
-static struct dmar_domain *__get_valid_domain_for_dev(struct pci_dev *pdev)
+static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev)
{
struct dmar_domain *domain;
int ret;
- domain = get_domain_for_dev(pdev,
- DEFAULT_DOMAIN_ADDRESS_WIDTH);
+ domain = get_domain_for_dev(dev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
if (!domain) {
- printk(KERN_ERR
- "Allocating domain for %s failed", pci_name(pdev));
+ printk(KERN_ERR "Allocating domain for %s failed",
+ dev_name(dev));
return NULL;
}
/* make sure context mapping is ok */
- if (unlikely(!domain_context_mapped(pdev))) {
- ret = domain_context_mapping(domain, pdev,
- CONTEXT_TT_MULTI_LEVEL);
+ if (unlikely(!domain_context_mapped(dev))) {
+ ret = domain_context_mapping(domain, dev, CONTEXT_TT_MULTI_LEVEL);
if (ret) {
- printk(KERN_ERR
- "Domain context map for %s failed",
- pci_name(pdev));
+ printk(KERN_ERR "Domain context map for %s failed",
+ dev_name(dev));
return NULL;
}
}
@@ -2689,51 +2921,46 @@ static struct dmar_domain *__get_valid_domain_for_dev(struct pci_dev *pdev)
return domain;
}
-static inline struct dmar_domain *get_valid_domain_for_dev(struct pci_dev *dev)
+static inline struct dmar_domain *get_valid_domain_for_dev(struct device *dev)
{
struct device_domain_info *info;
/* No lock here, assumes no domain exit in normal case */
- info = dev->dev.archdata.iommu;
+ info = dev->archdata.iommu;
if (likely(info))
return info->domain;
return __get_valid_domain_for_dev(dev);
}
-static int iommu_dummy(struct pci_dev *pdev)
+static int iommu_dummy(struct device *dev)
{
- return pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO;
+ return dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO;
}
-/* Check if the pdev needs to go through non-identity map and unmap process.*/
+/* Check if the dev needs to go through non-identity map and unmap process.*/
static int iommu_no_mapping(struct device *dev)
{
- struct pci_dev *pdev;
int found;
- if (unlikely(!dev_is_pci(dev)))
- return 1;
-
- pdev = to_pci_dev(dev);
- if (iommu_dummy(pdev))
+ if (iommu_dummy(dev))
return 1;
if (!iommu_identity_mapping)
return 0;
- found = identity_mapping(pdev);
+ found = identity_mapping(dev);
if (found) {
- if (iommu_should_identity_map(pdev, 0))
+ if (iommu_should_identity_map(dev, 0))
return 1;
else {
/*
* 32 bit DMA is removed from si_domain and fall back
* to non-identity mapping.
*/
- domain_remove_one_dev_info(si_domain, pdev);
+ domain_remove_one_dev_info(si_domain, dev);
printk(KERN_INFO "32bit %s uses non-identity mapping\n",
- pci_name(pdev));
+ dev_name(dev));
return 0;
}
} else {
@@ -2741,15 +2968,15 @@ static int iommu_no_mapping(struct device *dev)
* In case of a detached 64 bit DMA device from vm, the device
* is put into si_domain for identity mapping.
*/
- if (iommu_should_identity_map(pdev, 0)) {
+ if (iommu_should_identity_map(dev, 0)) {
int ret;
- ret = domain_add_dev_info(si_domain, pdev,
+ ret = domain_add_dev_info(si_domain, dev,
hw_pass_through ?
CONTEXT_TT_PASS_THROUGH :
CONTEXT_TT_MULTI_LEVEL);
if (!ret) {
printk(KERN_INFO "64bit %s uses identity mapping\n",
- pci_name(pdev));
+ dev_name(dev));
return 1;
}
}
@@ -2758,10 +2985,9 @@ static int iommu_no_mapping(struct device *dev)
return 0;
}
-static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
+static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr,
size_t size, int dir, u64 dma_mask)
{
- struct pci_dev *pdev = to_pci_dev(hwdev);
struct dmar_domain *domain;
phys_addr_t start_paddr;
struct iova *iova;
@@ -2772,17 +2998,17 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
BUG_ON(dir == DMA_NONE);
- if (iommu_no_mapping(hwdev))
+ if (iommu_no_mapping(dev))
return paddr;
- domain = get_valid_domain_for_dev(pdev);
+ domain = get_valid_domain_for_dev(dev);
if (!domain)
return 0;
iommu = domain_get_iommu(domain);
size = aligned_nrpages(paddr, size);
- iova = intel_alloc_iova(hwdev, domain, dma_to_mm_pfn(size), dma_mask);
+ iova = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size), dma_mask);
if (!iova)
goto error;
@@ -2808,7 +3034,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
/* it's a non-present to present mapping. Only flush if caching mode */
if (cap_caching_mode(iommu->cap))
- iommu_flush_iotlb_psi(iommu, domain->id, mm_to_dma_pfn(iova->pfn_lo), size, 1);
+ iommu_flush_iotlb_psi(iommu, domain->id, mm_to_dma_pfn(iova->pfn_lo), size, 0, 1);
else
iommu_flush_write_buffer(iommu);
@@ -2820,7 +3046,7 @@ error:
if (iova)
__free_iova(&domain->iovad, iova);
printk(KERN_ERR"Device %s request: %zx@%llx dir %d --- failed\n",
- pci_name(pdev), size, (unsigned long long)paddr, dir);
+ dev_name(dev), size, (unsigned long long)paddr, dir);
return 0;
}
@@ -2830,7 +3056,7 @@ static dma_addr_t intel_map_page(struct device *dev, struct page *page,
struct dma_attrs *attrs)
{
return __intel_map_single(dev, page_to_phys(page) + offset, size,
- dir, to_pci_dev(dev)->dma_mask);
+ dir, *dev->dma_mask);
}
static void flush_unmaps(void)
@@ -2860,13 +3086,16 @@ static void flush_unmaps(void)
/* On real hardware multiple invalidations are expensive */
if (cap_caching_mode(iommu->cap))
iommu_flush_iotlb_psi(iommu, domain->id,
- iova->pfn_lo, iova->pfn_hi - iova->pfn_lo + 1, 0);
+ iova->pfn_lo, iova->pfn_hi - iova->pfn_lo + 1,
+ !deferred_flush[i].freelist[j], 0);
else {
mask = ilog2(mm_to_dma_pfn(iova->pfn_hi - iova->pfn_lo + 1));
iommu_flush_dev_iotlb(deferred_flush[i].domain[j],
(uint64_t)iova->pfn_lo << PAGE_SHIFT, mask);
}
__free_iova(&deferred_flush[i].domain[j]->iovad, iova);
+ if (deferred_flush[i].freelist[j])
+ dma_free_pagelist(deferred_flush[i].freelist[j]);
}
deferred_flush[i].next = 0;
}
@@ -2883,7 +3112,7 @@ static void flush_unmaps_timeout(unsigned long data)
spin_unlock_irqrestore(&async_umap_flush_lock, flags);
}
-static void add_unmap(struct dmar_domain *dom, struct iova *iova)
+static void add_unmap(struct dmar_domain *dom, struct iova *iova, struct page *freelist)
{
unsigned long flags;
int next, iommu_id;
@@ -2899,6 +3128,7 @@ static void add_unmap(struct dmar_domain *dom, struct iova *iova)
next = deferred_flush[iommu_id].next;
deferred_flush[iommu_id].domain[next] = dom;
deferred_flush[iommu_id].iova[next] = iova;
+ deferred_flush[iommu_id].freelist[next] = freelist;
deferred_flush[iommu_id].next++;
if (!timer_on) {
@@ -2913,16 +3143,16 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
size_t size, enum dma_data_direction dir,
struct dma_attrs *attrs)
{
- struct pci_dev *pdev = to_pci_dev(dev);
struct dmar_domain *domain;
unsigned long start_pfn, last_pfn;
struct iova *iova;
struct intel_iommu *iommu;
+ struct page *freelist;
if (iommu_no_mapping(dev))
return;
- domain = find_domain(pdev);
+ domain = find_domain(dev);
BUG_ON(!domain);
iommu = domain_get_iommu(domain);
@@ -2936,21 +3166,18 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
last_pfn = mm_to_dma_pfn(iova->pfn_hi + 1) - 1;
pr_debug("Device %s unmapping: pfn %lx-%lx\n",
- pci_name(pdev), start_pfn, last_pfn);
+ dev_name(dev), start_pfn, last_pfn);
- /* clear the whole page */
- dma_pte_clear_range(domain, start_pfn, last_pfn);
-
- /* free page tables */
- dma_pte_free_pagetable(domain, start_pfn, last_pfn);
+ freelist = domain_unmap(domain, start_pfn, last_pfn);
if (intel_iommu_strict) {
iommu_flush_iotlb_psi(iommu, domain->id, start_pfn,
- last_pfn - start_pfn + 1, 0);
+ last_pfn - start_pfn + 1, !freelist, 0);
/* free iova */
__free_iova(&domain->iovad, iova);
+ dma_free_pagelist(freelist);
} else {
- add_unmap(domain, iova);
+ add_unmap(domain, iova, freelist);
/*
* queue up the release of the unmap to save the 1/6th of the
* cpu used up by the iotlb flush operation...
@@ -2958,7 +3185,7 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
}
}
-static void *intel_alloc_coherent(struct device *hwdev, size_t size,
+static void *intel_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flags,
struct dma_attrs *attrs)
{
@@ -2968,10 +3195,10 @@ static void *intel_alloc_coherent(struct device *hwdev, size_t size,
size = PAGE_ALIGN(size);
order = get_order(size);
- if (!iommu_no_mapping(hwdev))
+ if (!iommu_no_mapping(dev))
flags &= ~(GFP_DMA | GFP_DMA32);
- else if (hwdev->coherent_dma_mask < dma_get_required_mask(hwdev)) {
- if (hwdev->coherent_dma_mask < DMA_BIT_MASK(32))
+ else if (dev->coherent_dma_mask < dma_get_required_mask(dev)) {
+ if (dev->coherent_dma_mask < DMA_BIT_MASK(32))
flags |= GFP_DMA;
else
flags |= GFP_DMA32;
@@ -2982,16 +3209,16 @@ static void *intel_alloc_coherent(struct device *hwdev, size_t size,
return NULL;
memset(vaddr, 0, size);
- *dma_handle = __intel_map_single(hwdev, virt_to_bus(vaddr), size,
+ *dma_handle = __intel_map_single(dev, virt_to_bus(vaddr), size,
DMA_BIDIRECTIONAL,
- hwdev->coherent_dma_mask);
+ dev->coherent_dma_mask);
if (*dma_handle)
return vaddr;
free_pages((unsigned long)vaddr, order);
return NULL;
}
-static void intel_free_coherent(struct device *hwdev, size_t size, void *vaddr,
+static void intel_free_coherent(struct device *dev, size_t size, void *vaddr,
dma_addr_t dma_handle, struct dma_attrs *attrs)
{
int order;
@@ -2999,24 +3226,24 @@ static void intel_free_coherent(struct device *hwdev, size_t size, void *vaddr,
size = PAGE_ALIGN(size);
order = get_order(size);
- intel_unmap_page(hwdev, dma_handle, size, DMA_BIDIRECTIONAL, NULL);
+ intel_unmap_page(dev, dma_handle, size, DMA_BIDIRECTIONAL, NULL);
free_pages((unsigned long)vaddr, order);
}
-static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist,
+static void intel_unmap_sg(struct device *dev, struct scatterlist *sglist,
int nelems, enum dma_data_direction dir,
struct dma_attrs *attrs)
{
- struct pci_dev *pdev = to_pci_dev(hwdev);
struct dmar_domain *domain;
unsigned long start_pfn, last_pfn;
struct iova *iova;
struct intel_iommu *iommu;
+ struct page *freelist;
- if (iommu_no_mapping(hwdev))
+ if (iommu_no_mapping(dev))
return;
- domain = find_domain(pdev);
+ domain = find_domain(dev);
BUG_ON(!domain);
iommu = domain_get_iommu(domain);
@@ -3029,19 +3256,16 @@ static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist,
start_pfn = mm_to_dma_pfn(iova->pfn_lo);
last_pfn = mm_to_dma_pfn(iova->pfn_hi + 1) - 1;
- /* clear the whole page */
- dma_pte_clear_range(domain, start_pfn, last_pfn);
-
- /* free page tables */
- dma_pte_free_pagetable(domain, start_pfn, last_pfn);
+ freelist = domain_unmap(domain, start_pfn, last_pfn);
if (intel_iommu_strict) {
iommu_flush_iotlb_psi(iommu, domain->id, start_pfn,
- last_pfn - start_pfn + 1, 0);
+ last_pfn - start_pfn + 1, !freelist, 0);
/* free iova */
__free_iova(&domain->iovad, iova);
+ dma_free_pagelist(freelist);
} else {
- add_unmap(domain, iova);
+ add_unmap(domain, iova, freelist);
/*
* queue up the release of the unmap to save the 1/6th of the
* cpu used up by the iotlb flush operation...
@@ -3063,11 +3287,10 @@ static int intel_nontranslate_map_sg(struct device *hddev,
return nelems;
}
-static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int nelems,
+static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nelems,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
int i;
- struct pci_dev *pdev = to_pci_dev(hwdev);
struct dmar_domain *domain;
size_t size = 0;
int prot = 0;
@@ -3078,10 +3301,10 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
struct intel_iommu *iommu;
BUG_ON(dir == DMA_NONE);
- if (iommu_no_mapping(hwdev))
- return intel_nontranslate_map_sg(hwdev, sglist, nelems, dir);
+ if (iommu_no_mapping(dev))
+ return intel_nontranslate_map_sg(dev, sglist, nelems, dir);
- domain = get_valid_domain_for_dev(pdev);
+ domain = get_valid_domain_for_dev(dev);
if (!domain)
return 0;
@@ -3090,8 +3313,8 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
for_each_sg(sglist, sg, nelems, i)
size += aligned_nrpages(sg->offset, sg->length);
- iova = intel_alloc_iova(hwdev, domain, dma_to_mm_pfn(size),
- pdev->dma_mask);
+ iova = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size),
+ *dev->dma_mask);
if (!iova) {
sglist->dma_length = 0;
return 0;
@@ -3124,7 +3347,7 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
/* it's a non-present to present mapping. Only flush if caching mode */
if (cap_caching_mode(iommu->cap))
- iommu_flush_iotlb_psi(iommu, domain->id, start_vpfn, size, 1);
+ iommu_flush_iotlb_psi(iommu, domain->id, start_vpfn, size, 0, 1);
else
iommu_flush_write_buffer(iommu);
@@ -3259,29 +3482,28 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB, quir
static void __init init_no_remapping_devices(void)
{
struct dmar_drhd_unit *drhd;
+ struct device *dev;
+ int i;
for_each_drhd_unit(drhd) {
if (!drhd->include_all) {
- int i;
- for (i = 0; i < drhd->devices_cnt; i++)
- if (drhd->devices[i] != NULL)
- break;
- /* ignore DMAR unit if no pci devices exist */
+ for_each_active_dev_scope(drhd->devices,
+ drhd->devices_cnt, i, dev)
+ break;
+ /* ignore DMAR unit if no devices exist */
if (i == drhd->devices_cnt)
drhd->ignored = 1;
}
}
for_each_active_drhd_unit(drhd) {
- int i;
if (drhd->include_all)
continue;
- for (i = 0; i < drhd->devices_cnt; i++)
- if (drhd->devices[i] &&
- !IS_GFX_DEVICE(drhd->devices[i]))
+ for_each_active_dev_scope(drhd->devices,
+ drhd->devices_cnt, i, dev)
+ if (!dev_is_pci(dev) || !IS_GFX_DEVICE(to_pci_dev(dev)))
break;
-
if (i < drhd->devices_cnt)
continue;
@@ -3291,11 +3513,9 @@ static void __init init_no_remapping_devices(void)
intel_iommu_gfx_mapped = 1;
} else {
drhd->ignored = 1;
- for (i = 0; i < drhd->devices_cnt; i++) {
- if (!drhd->devices[i])
- continue;
- drhd->devices[i]->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO;
- }
+ for_each_active_dev_scope(drhd->devices,
+ drhd->devices_cnt, i, dev)
+ dev->archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO;
}
}
}
@@ -3438,13 +3658,6 @@ static void __init init_iommu_pm_ops(void)
static inline void init_iommu_pm_ops(void) {}
#endif /* CONFIG_PM */
-LIST_HEAD(dmar_rmrr_units);
-
-static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr)
-{
- list_add(&rmrr->list, &dmar_rmrr_units);
-}
-
int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
{
@@ -3459,25 +3672,19 @@ int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
rmrr = (struct acpi_dmar_reserved_memory *)header;
rmrru->base_address = rmrr->base_address;
rmrru->end_address = rmrr->end_address;
+ rmrru->devices = dmar_alloc_dev_scope((void *)(rmrr + 1),
+ ((void *)rmrr) + rmrr->header.length,
+ &rmrru->devices_cnt);
+ if (rmrru->devices_cnt && rmrru->devices == NULL) {
+ kfree(rmrru);
+ return -ENOMEM;
+ }
- dmar_register_rmrr_unit(rmrru);
- return 0;
-}
-
-static int __init
-rmrr_parse_dev(struct dmar_rmrr_unit *rmrru)
-{
- struct acpi_dmar_reserved_memory *rmrr;
+ list_add(&rmrru->list, &dmar_rmrr_units);
- rmrr = (struct acpi_dmar_reserved_memory *) rmrru->hdr;
- return dmar_parse_dev_scope((void *)(rmrr + 1),
- ((void *)rmrr) + rmrr->header.length,
- &rmrru->devices_cnt, &rmrru->devices,
- rmrr->segment);
+ return 0;
}
-static LIST_HEAD(dmar_atsr_units);
-
int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
{
struct acpi_dmar_atsr *atsr;
@@ -3490,26 +3697,21 @@ int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
atsru->hdr = hdr;
atsru->include_all = atsr->flags & 0x1;
+ if (!atsru->include_all) {
+ atsru->devices = dmar_alloc_dev_scope((void *)(atsr + 1),
+ (void *)atsr + atsr->header.length,
+ &atsru->devices_cnt);
+ if (atsru->devices_cnt && atsru->devices == NULL) {
+ kfree(atsru);
+ return -ENOMEM;
+ }
+ }
- list_add(&atsru->list, &dmar_atsr_units);
+ list_add_rcu(&atsru->list, &dmar_atsr_units);
return 0;
}
-static int __init atsr_parse_dev(struct dmar_atsr_unit *atsru)
-{
- struct acpi_dmar_atsr *atsr;
-
- if (atsru->include_all)
- return 0;
-
- atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
- return dmar_parse_dev_scope((void *)(atsr + 1),
- (void *)atsr + atsr->header.length,
- &atsru->devices_cnt, &atsru->devices,
- atsr->segment);
-}
-
static void intel_iommu_free_atsr(struct dmar_atsr_unit *atsru)
{
dmar_free_dev_scope(&atsru->devices, &atsru->devices_cnt);
@@ -3535,62 +3737,97 @@ static void intel_iommu_free_dmars(void)
int dmar_find_matched_atsr_unit(struct pci_dev *dev)
{
- int i;
+ int i, ret = 1;
struct pci_bus *bus;
+ struct pci_dev *bridge = NULL;
+ struct device *tmp;
struct acpi_dmar_atsr *atsr;
struct dmar_atsr_unit *atsru;
dev = pci_physfn(dev);
-
- list_for_each_entry(atsru, &dmar_atsr_units, list) {
- atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
- if (atsr->segment == pci_domain_nr(dev->bus))
- goto found;
- }
-
- return 0;
-
-found:
for (bus = dev->bus; bus; bus = bus->parent) {
- struct pci_dev *bridge = bus->self;
-
+ bridge = bus->self;
if (!bridge || !pci_is_pcie(bridge) ||
pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE)
return 0;
-
- if (pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT) {
- for (i = 0; i < atsru->devices_cnt; i++)
- if (atsru->devices[i] == bridge)
- return 1;
+ if (pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT)
break;
- }
}
+ if (!bridge)
+ return 0;
- if (atsru->include_all)
- return 1;
+ rcu_read_lock();
+ list_for_each_entry_rcu(atsru, &dmar_atsr_units, list) {
+ atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
+ if (atsr->segment != pci_domain_nr(dev->bus))
+ continue;
- return 0;
+ for_each_dev_scope(atsru->devices, atsru->devices_cnt, i, tmp)
+ if (tmp == &bridge->dev)
+ goto out;
+
+ if (atsru->include_all)
+ goto out;
+ }
+ ret = 0;
+out:
+ rcu_read_unlock();
+
+ return ret;
}
-int __init dmar_parse_rmrr_atsr_dev(void)
+int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
{
- struct dmar_rmrr_unit *rmrr;
- struct dmar_atsr_unit *atsr;
int ret = 0;
+ struct dmar_rmrr_unit *rmrru;
+ struct dmar_atsr_unit *atsru;
+ struct acpi_dmar_atsr *atsr;
+ struct acpi_dmar_reserved_memory *rmrr;
- list_for_each_entry(rmrr, &dmar_rmrr_units, list) {
- ret = rmrr_parse_dev(rmrr);
- if (ret)
- return ret;
+ if (!intel_iommu_enabled && system_state != SYSTEM_BOOTING)
+ return 0;
+
+ list_for_each_entry(rmrru, &dmar_rmrr_units, list) {
+ rmrr = container_of(rmrru->hdr,
+ struct acpi_dmar_reserved_memory, header);
+ if (info->event == BUS_NOTIFY_ADD_DEVICE) {
+ ret = dmar_insert_dev_scope(info, (void *)(rmrr + 1),
+ ((void *)rmrr) + rmrr->header.length,
+ rmrr->segment, rmrru->devices,
+ rmrru->devices_cnt);
+ if (ret > 0)
+ break;
+ else if(ret < 0)
+ return ret;
+ } else if (info->event == BUS_NOTIFY_DEL_DEVICE) {
+ if (dmar_remove_dev_scope(info, rmrr->segment,
+ rmrru->devices, rmrru->devices_cnt))
+ break;
+ }
}
- list_for_each_entry(atsr, &dmar_atsr_units, list) {
- ret = atsr_parse_dev(atsr);
- if (ret)
- return ret;
+ list_for_each_entry(atsru, &dmar_atsr_units, list) {
+ if (atsru->include_all)
+ continue;
+
+ atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
+ if (info->event == BUS_NOTIFY_ADD_DEVICE) {
+ ret = dmar_insert_dev_scope(info, (void *)(atsr + 1),
+ (void *)atsr + atsr->header.length,
+ atsr->segment, atsru->devices,
+ atsru->devices_cnt);
+ if (ret > 0)
+ break;
+ else if(ret < 0)
+ return ret;
+ } else if (info->event == BUS_NOTIFY_DEL_DEVICE) {
+ if (dmar_remove_dev_scope(info, atsr->segment,
+ atsru->devices, atsru->devices_cnt))
+ break;
+ }
}
- return ret;
+ return 0;
}
/*
@@ -3603,24 +3840,26 @@ static int device_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct device *dev = data;
- struct pci_dev *pdev = to_pci_dev(dev);
struct dmar_domain *domain;
- if (iommu_no_mapping(dev))
+ if (iommu_dummy(dev))
return 0;
- domain = find_domain(pdev);
- if (!domain)
+ if (action != BUS_NOTIFY_UNBOUND_DRIVER &&
+ action != BUS_NOTIFY_DEL_DEVICE)
return 0;
- if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through) {
- domain_remove_one_dev_info(domain, pdev);
+ domain = find_domain(dev);
+ if (!domain)
+ return 0;
- if (!(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) &&
- !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) &&
- list_empty(&domain->devices))
- domain_exit(domain);
- }
+ down_read(&dmar_global_lock);
+ domain_remove_one_dev_info(domain, dev);
+ if (!(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) &&
+ !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) &&
+ list_empty(&domain->devices))
+ domain_exit(domain);
+ up_read(&dmar_global_lock);
return 0;
}
@@ -3629,6 +3868,75 @@ static struct notifier_block device_nb = {
.notifier_call = device_notifier,
};
+static int intel_iommu_memory_notifier(struct notifier_block *nb,
+ unsigned long val, void *v)
+{
+ struct memory_notify *mhp = v;
+ unsigned long long start, end;
+ unsigned long start_vpfn, last_vpfn;
+
+ switch (val) {
+ case MEM_GOING_ONLINE:
+ start = mhp->start_pfn << PAGE_SHIFT;
+ end = ((mhp->start_pfn + mhp->nr_pages) << PAGE_SHIFT) - 1;
+ if (iommu_domain_identity_map(si_domain, start, end)) {
+ pr_warn("dmar: failed to build identity map for [%llx-%llx]\n",
+ start, end);
+ return NOTIFY_BAD;
+ }
+ break;
+
+ case MEM_OFFLINE:
+ case MEM_CANCEL_ONLINE:
+ start_vpfn = mm_to_dma_pfn(mhp->start_pfn);
+ last_vpfn = mm_to_dma_pfn(mhp->start_pfn + mhp->nr_pages - 1);
+ while (start_vpfn <= last_vpfn) {
+ struct iova *iova;
+ struct dmar_drhd_unit *drhd;
+ struct intel_iommu *iommu;
+ struct page *freelist;
+
+ iova = find_iova(&si_domain->iovad, start_vpfn);
+ if (iova == NULL) {
+ pr_debug("dmar: failed get IOVA for PFN %lx\n",
+ start_vpfn);
+ break;
+ }
+
+ iova = split_and_remove_iova(&si_domain->iovad, iova,
+ start_vpfn, last_vpfn);
+ if (iova == NULL) {
+ pr_warn("dmar: failed to split IOVA PFN [%lx-%lx]\n",
+ start_vpfn, last_vpfn);
+ return NOTIFY_BAD;
+ }
+
+ freelist = domain_unmap(si_domain, iova->pfn_lo,
+ iova->pfn_hi);
+
+ rcu_read_lock();
+ for_each_active_iommu(iommu, drhd)
+ iommu_flush_iotlb_psi(iommu, si_domain->id,
+ iova->pfn_lo,
+ iova->pfn_hi - iova->pfn_lo + 1,
+ !freelist, 0);
+ rcu_read_unlock();
+ dma_free_pagelist(freelist);
+
+ start_vpfn = iova->pfn_hi + 1;
+ free_iova_mem(iova);
+ }
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block intel_iommu_memory_nb = {
+ .notifier_call = intel_iommu_memory_notifier,
+ .priority = 0
+};
+
int __init intel_iommu_init(void)
{
int ret = -ENODEV;
@@ -3638,6 +3946,13 @@ int __init intel_iommu_init(void)
/* VT-d is required for a TXT/tboot launch, so enforce that */
force_on = tboot_force_iommu();
+ if (iommu_init_mempool()) {
+ if (force_on)
+ panic("tboot: Failed to initialize iommu memory\n");
+ return -ENOMEM;
+ }
+
+ down_write(&dmar_global_lock);
if (dmar_table_init()) {
if (force_on)
panic("tboot: Failed to initialize DMAR table\n");
@@ -3660,12 +3975,6 @@ int __init intel_iommu_init(void)
if (no_iommu || dmar_disabled)
goto out_free_dmar;
- if (iommu_init_mempool()) {
- if (force_on)
- panic("tboot: Failed to initialize iommu memory\n");
- goto out_free_dmar;
- }
-
if (list_empty(&dmar_rmrr_units))
printk(KERN_INFO "DMAR: No RMRR found\n");
@@ -3675,7 +3984,7 @@ int __init intel_iommu_init(void)
if (dmar_init_reserved_ranges()) {
if (force_on)
panic("tboot: Failed to reserve iommu ranges\n");
- goto out_free_mempool;
+ goto out_free_reserved_range;
}
init_no_remapping_devices();
@@ -3687,6 +3996,7 @@ int __init intel_iommu_init(void)
printk(KERN_ERR "IOMMU: dmar init failed\n");
goto out_free_reserved_range;
}
+ up_write(&dmar_global_lock);
printk(KERN_INFO
"PCI-DMA: Intel(R) Virtualization Technology for Directed I/O\n");
@@ -3699,8 +4009,9 @@ int __init intel_iommu_init(void)
init_iommu_pm_ops();
bus_set_iommu(&pci_bus_type, &intel_iommu_ops);
-
bus_register_notifier(&pci_bus_type, &device_nb);
+ if (si_domain && !hw_pass_through)
+ register_memory_notifier(&intel_iommu_memory_nb);
intel_iommu_enabled = 1;
@@ -3708,21 +4019,23 @@ int __init intel_iommu_init(void)
out_free_reserved_range:
put_iova_domain(&reserved_iova_list);
-out_free_mempool:
- iommu_exit_mempool();
out_free_dmar:
intel_iommu_free_dmars();
+ up_write(&dmar_global_lock);
+ iommu_exit_mempool();
return ret;
}
static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
- struct pci_dev *pdev)
+ struct device *dev)
{
- struct pci_dev *tmp, *parent;
+ struct pci_dev *tmp, *parent, *pdev;
- if (!iommu || !pdev)
+ if (!iommu || !dev || !dev_is_pci(dev))
return;
+ pdev = to_pci_dev(dev);
+
/* dependent device detach */
tmp = pci_find_upstream_pcie_bridge(pdev);
/* Secondary interface's bus number and devfn 0 */
@@ -3743,29 +4056,28 @@ static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
}
static void domain_remove_one_dev_info(struct dmar_domain *domain,
- struct pci_dev *pdev)
+ struct device *dev)
{
struct device_domain_info *info, *tmp;
struct intel_iommu *iommu;
unsigned long flags;
int found = 0;
+ u8 bus, devfn;
- iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number,
- pdev->devfn);
+ iommu = device_to_iommu(dev, &bus, &devfn);
if (!iommu)
return;
spin_lock_irqsave(&device_domain_lock, flags);
list_for_each_entry_safe(info, tmp, &domain->devices, link) {
- if (info->segment == pci_domain_nr(pdev->bus) &&
- info->bus == pdev->bus->number &&
- info->devfn == pdev->devfn) {
+ if (info->iommu == iommu && info->bus == bus &&
+ info->devfn == devfn) {
unlink_domain_info(info);
spin_unlock_irqrestore(&device_domain_lock, flags);
iommu_disable_dev_iotlb(info);
iommu_detach_dev(iommu, info->bus, info->devfn);
- iommu_detach_dependent_devices(iommu, pdev);
+ iommu_detach_dependent_devices(iommu, dev);
free_devinfo_mem(info);
spin_lock_irqsave(&device_domain_lock, flags);
@@ -3780,8 +4092,7 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain,
* owned by this domain, clear this iommu in iommu_bmp
* update iommu count and coherency
*/
- if (iommu == device_to_iommu(info->segment, info->bus,
- info->devfn))
+ if (info->iommu == iommu)
found = 1;
}
@@ -3805,67 +4116,11 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain,
}
}
-static void vm_domain_remove_all_dev_info(struct dmar_domain *domain)
-{
- struct device_domain_info *info;
- struct intel_iommu *iommu;
- unsigned long flags1, flags2;
-
- spin_lock_irqsave(&device_domain_lock, flags1);
- while (!list_empty(&domain->devices)) {
- info = list_entry(domain->devices.next,
- struct device_domain_info, link);
- unlink_domain_info(info);
- spin_unlock_irqrestore(&device_domain_lock, flags1);
-
- iommu_disable_dev_iotlb(info);
- iommu = device_to_iommu(info->segment, info->bus, info->devfn);
- iommu_detach_dev(iommu, info->bus, info->devfn);
- iommu_detach_dependent_devices(iommu, info->dev);
-
- /* clear this iommu in iommu_bmp, update iommu count
- * and capabilities
- */
- spin_lock_irqsave(&domain->iommu_lock, flags2);
- if (test_and_clear_bit(iommu->seq_id,
- domain->iommu_bmp)) {
- domain->iommu_count--;
- domain_update_iommu_cap(domain);
- }
- spin_unlock_irqrestore(&domain->iommu_lock, flags2);
-
- free_devinfo_mem(info);
- spin_lock_irqsave(&device_domain_lock, flags1);
- }
- spin_unlock_irqrestore(&device_domain_lock, flags1);
-}
-
-/* domain id for virtual machine, it won't be set in context */
-static atomic_t vm_domid = ATOMIC_INIT(0);
-
-static struct dmar_domain *iommu_alloc_vm_domain(void)
-{
- struct dmar_domain *domain;
-
- domain = alloc_domain_mem();
- if (!domain)
- return NULL;
-
- domain->id = atomic_inc_return(&vm_domid);
- domain->nid = -1;
- memset(domain->iommu_bmp, 0, sizeof(domain->iommu_bmp));
- domain->flags = DOMAIN_FLAG_VIRTUAL_MACHINE;
-
- return domain;
-}
-
static int md_domain_init(struct dmar_domain *domain, int guest_width)
{
int adjust_width;
init_iova_domain(&domain->iovad, DMA_32BIT_PFN);
- spin_lock_init(&domain->iommu_lock);
-
domain_reserve_special_ranges(domain);
/* calculate AGAW */
@@ -3873,9 +4128,6 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width)
adjust_width = guestwidth_to_adjustwidth(guest_width);
domain->agaw = width_to_agaw(adjust_width);
- INIT_LIST_HEAD(&domain->devices);
-
- domain->iommu_count = 0;
domain->iommu_coherency = 0;
domain->iommu_snooping = 0;
domain->iommu_superpage = 0;
@@ -3890,53 +4142,11 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width)
return 0;
}
-static void iommu_free_vm_domain(struct dmar_domain *domain)
-{
- unsigned long flags;
- struct dmar_drhd_unit *drhd;
- struct intel_iommu *iommu;
- unsigned long i;
- unsigned long ndomains;
-
- for_each_active_iommu(iommu, drhd) {
- ndomains = cap_ndoms(iommu->cap);
- for_each_set_bit(i, iommu->domain_ids, ndomains) {
- if (iommu->domains[i] == domain) {
- spin_lock_irqsave(&iommu->lock, flags);
- clear_bit(i, iommu->domain_ids);
- iommu->domains[i] = NULL;
- spin_unlock_irqrestore(&iommu->lock, flags);
- break;
- }
- }
- }
-}
-
-static void vm_domain_exit(struct dmar_domain *domain)
-{
- /* Domain 0 is reserved, so dont process it */
- if (!domain)
- return;
-
- vm_domain_remove_all_dev_info(domain);
- /* destroy iovas */
- put_iova_domain(&domain->iovad);
-
- /* clear ptes */
- dma_pte_clear_range(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
-
- /* free page tables */
- dma_pte_free_pagetable(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
-
- iommu_free_vm_domain(domain);
- free_domain_mem(domain);
-}
-
static int intel_iommu_domain_init(struct iommu_domain *domain)
{
struct dmar_domain *dmar_domain;
- dmar_domain = iommu_alloc_vm_domain();
+ dmar_domain = alloc_domain(true);
if (!dmar_domain) {
printk(KERN_ERR
"intel_iommu_domain_init: dmar_domain == NULL\n");
@@ -3945,7 +4155,7 @@ static int intel_iommu_domain_init(struct iommu_domain *domain)
if (md_domain_init(dmar_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) {
printk(KERN_ERR
"intel_iommu_domain_init() failed\n");
- vm_domain_exit(dmar_domain);
+ domain_exit(dmar_domain);
return -ENOMEM;
}
domain_update_iommu_cap(dmar_domain);
@@ -3963,33 +4173,32 @@ static void intel_iommu_domain_destroy(struct iommu_domain *domain)
struct dmar_domain *dmar_domain = domain->priv;
domain->priv = NULL;
- vm_domain_exit(dmar_domain);
+ domain_exit(dmar_domain);
}
static int intel_iommu_attach_device(struct iommu_domain *domain,
struct device *dev)
{
struct dmar_domain *dmar_domain = domain->priv;
- struct pci_dev *pdev = to_pci_dev(dev);
struct intel_iommu *iommu;
int addr_width;
+ u8 bus, devfn;
- /* normally pdev is not mapped */
- if (unlikely(domain_context_mapped(pdev))) {
+ /* normally dev is not mapped */
+ if (unlikely(domain_context_mapped(dev))) {
struct dmar_domain *old_domain;
- old_domain = find_domain(pdev);
+ old_domain = find_domain(dev);
if (old_domain) {
if (dmar_domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE ||
dmar_domain->flags & DOMAIN_FLAG_STATIC_IDENTITY)
- domain_remove_one_dev_info(old_domain, pdev);
+ domain_remove_one_dev_info(old_domain, dev);
else
domain_remove_dev_info(old_domain);
}
}
- iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number,
- pdev->devfn);
+ iommu = device_to_iommu(dev, &bus, &devfn);
if (!iommu)
return -ENODEV;
@@ -4021,16 +4230,15 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
dmar_domain->agaw--;
}
- return domain_add_dev_info(dmar_domain, pdev, CONTEXT_TT_MULTI_LEVEL);
+ return domain_add_dev_info(dmar_domain, dev, CONTEXT_TT_MULTI_LEVEL);
}
static void intel_iommu_detach_device(struct iommu_domain *domain,
struct device *dev)
{
struct dmar_domain *dmar_domain = domain->priv;
- struct pci_dev *pdev = to_pci_dev(dev);
- domain_remove_one_dev_info(dmar_domain, pdev);
+ domain_remove_one_dev_info(dmar_domain, dev);
}
static int intel_iommu_map(struct iommu_domain *domain,
@@ -4072,18 +4280,51 @@ static int intel_iommu_map(struct iommu_domain *domain,
}
static size_t intel_iommu_unmap(struct iommu_domain *domain,
- unsigned long iova, size_t size)
+ unsigned long iova, size_t size)
{
struct dmar_domain *dmar_domain = domain->priv;
- int order;
+ struct page *freelist = NULL;
+ struct intel_iommu *iommu;
+ unsigned long start_pfn, last_pfn;
+ unsigned int npages;
+ int iommu_id, num, ndomains, level = 0;
+
+ /* Cope with horrid API which requires us to unmap more than the
+ size argument if it happens to be a large-page mapping. */
+ if (!pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, &level))
+ BUG();
+
+ if (size < VTD_PAGE_SIZE << level_to_offset_bits(level))
+ size = VTD_PAGE_SIZE << level_to_offset_bits(level);
+
+ start_pfn = iova >> VTD_PAGE_SHIFT;
+ last_pfn = (iova + size - 1) >> VTD_PAGE_SHIFT;
+
+ freelist = domain_unmap(dmar_domain, start_pfn, last_pfn);
+
+ npages = last_pfn - start_pfn + 1;
+
+ for_each_set_bit(iommu_id, dmar_domain->iommu_bmp, g_num_of_iommus) {
+ iommu = g_iommus[iommu_id];
+
+ /*
+ * find bit position of dmar_domain
+ */
+ ndomains = cap_ndoms(iommu->cap);
+ for_each_set_bit(num, iommu->domain_ids, ndomains) {
+ if (iommu->domains[num] == dmar_domain)
+ iommu_flush_iotlb_psi(iommu, num, start_pfn,
+ npages, !freelist, 0);
+ }
+
+ }
- order = dma_pte_clear_range(dmar_domain, iova >> VTD_PAGE_SHIFT,
- (iova + size - 1) >> VTD_PAGE_SHIFT);
+ dma_free_pagelist(freelist);
if (dmar_domain->max_addr == iova + size)
dmar_domain->max_addr = iova;
- return PAGE_SIZE << order;
+ return size;
}
static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
@@ -4091,9 +4332,10 @@ static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
{
struct dmar_domain *dmar_domain = domain->priv;
struct dma_pte *pte;
+ int level = 0;
u64 phys = 0;
- pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, 0);
+ pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, &level);
if (pte)
phys = dma_pte_addr(pte);
@@ -4121,9 +4363,9 @@ static int intel_iommu_add_device(struct device *dev)
struct pci_dev *bridge, *dma_pdev = NULL;
struct iommu_group *group;
int ret;
+ u8 bus, devfn;
- if (!device_to_iommu(pci_domain_nr(pdev->bus),
- pdev->bus->number, pdev->devfn))
+ if (!device_to_iommu(dev, &bus, &devfn))
return -ENODEV;
bridge = pci_find_upstream_pcie_bridge(pdev);
diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index ef5f65dbafe9..9b174893f0f5 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -38,6 +38,17 @@ static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
static struct hpet_scope ir_hpet[MAX_HPET_TBS];
static int ir_ioapic_num, ir_hpet_num;
+/*
+ * Lock ordering:
+ * ->dmar_global_lock
+ * ->irq_2_ir_lock
+ * ->qi->q_lock
+ * ->iommu->register_lock
+ * Note:
+ * intel_irq_remap_ops.{supported,prepare,enable,disable,reenable} are called
+ * in single-threaded environment with interrupt disabled, so no need to tabke
+ * the dmar_global_lock.
+ */
static DEFINE_RAW_SPINLOCK(irq_2_ir_lock);
static int __init parse_ioapics_under_ir(void);
@@ -307,12 +318,14 @@ static int set_ioapic_sid(struct irte *irte, int apic)
if (!irte)
return -1;
+ down_read(&dmar_global_lock);
for (i = 0; i < MAX_IO_APICS; i++) {
if (ir_ioapic[i].id == apic) {
sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn;
break;
}
}
+ up_read(&dmar_global_lock);
if (sid == 0) {
pr_warning("Failed to set source-id of IOAPIC (%d)\n", apic);
@@ -332,12 +345,14 @@ static int set_hpet_sid(struct irte *irte, u8 id)
if (!irte)
return -1;
+ down_read(&dmar_global_lock);
for (i = 0; i < MAX_HPET_TBS; i++) {
if (ir_hpet[i].id == id) {
sid = (ir_hpet[i].bus << 8) | ir_hpet[i].devfn;
break;
}
}
+ up_read(&dmar_global_lock);
if (sid == 0) {
pr_warning("Failed to set source-id of HPET block (%d)\n", id);
@@ -794,10 +809,16 @@ static int __init parse_ioapics_under_ir(void)
static int __init ir_dev_scope_init(void)
{
+ int ret;
+
if (!irq_remapping_enabled)
return 0;
- return dmar_dev_scope_init();
+ down_write(&dmar_global_lock);
+ ret = dmar_dev_scope_init();
+ up_write(&dmar_global_lock);
+
+ return ret;
}
rootfs_initcall(ir_dev_scope_init);
@@ -878,23 +899,27 @@ static int intel_setup_ioapic_entry(int irq,
struct io_apic_irq_attr *attr)
{
int ioapic_id = mpc_ioapic_id(attr->ioapic);
- struct intel_iommu *iommu = map_ioapic_to_ir(ioapic_id);
+ struct intel_iommu *iommu;
struct IR_IO_APIC_route_entry *entry;
struct irte irte;
int index;
+ down_read(&dmar_global_lock);
+ iommu = map_ioapic_to_ir(ioapic_id);
if (!iommu) {
pr_warn("No mapping iommu for ioapic %d\n", ioapic_id);
- return -ENODEV;
- }
-
- entry = (struct IR_IO_APIC_route_entry *)route_entry;
-
- index = alloc_irte(iommu, irq, 1);
- if (index < 0) {
- pr_warn("Failed to allocate IRTE for ioapic %d\n", ioapic_id);
- return -ENOMEM;
+ index = -ENODEV;
+ } else {
+ index = alloc_irte(iommu, irq, 1);
+ if (index < 0) {
+ pr_warn("Failed to allocate IRTE for ioapic %d\n",
+ ioapic_id);
+ index = -ENOMEM;
+ }
}
+ up_read(&dmar_global_lock);
+ if (index < 0)
+ return index;
prepare_irte(&irte, vector, destination);
@@ -913,6 +938,7 @@ static int intel_setup_ioapic_entry(int irq,
irte.avail, irte.vector, irte.dest_id,
irte.sid, irte.sq, irte.svt);
+ entry = (struct IR_IO_APIC_route_entry *)route_entry;
memset(entry, 0, sizeof(*entry));
entry->index2 = (index >> 15) & 0x1;
@@ -1043,20 +1069,23 @@ static int intel_msi_alloc_irq(struct pci_dev *dev, int irq, int nvec)
struct intel_iommu *iommu;
int index;
+ down_read(&dmar_global_lock);
iommu = map_dev_to_ir(dev);
if (!iommu) {
printk(KERN_ERR
"Unable to map PCI %s to iommu\n", pci_name(dev));
- return -ENOENT;
+ index = -ENOENT;
+ } else {
+ index = alloc_irte(iommu, irq, nvec);
+ if (index < 0) {
+ printk(KERN_ERR
+ "Unable to allocate %d IRTE for PCI %s\n",
+ nvec, pci_name(dev));
+ index = -ENOSPC;
+ }
}
+ up_read(&dmar_global_lock);
- index = alloc_irte(iommu, irq, nvec);
- if (index < 0) {
- printk(KERN_ERR
- "Unable to allocate %d IRTE for PCI %s\n", nvec,
- pci_name(dev));
- return -ENOSPC;
- }
return index;
}
@@ -1064,33 +1093,40 @@ static int intel_msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
int index, int sub_handle)
{
struct intel_iommu *iommu;
+ int ret = -ENOENT;
+ down_read(&dmar_global_lock);
iommu = map_dev_to_ir(pdev);
- if (!iommu)
- return -ENOENT;
- /*
- * setup the mapping between the irq and the IRTE
- * base index, the sub_handle pointing to the
- * appropriate interrupt remap table entry.
- */
- set_irte_irq(irq, iommu, index, sub_handle);
+ if (iommu) {
+ /*
+ * setup the mapping between the irq and the IRTE
+ * base index, the sub_handle pointing to the
+ * appropriate interrupt remap table entry.
+ */
+ set_irte_irq(irq, iommu, index, sub_handle);
+ ret = 0;
+ }
+ up_read(&dmar_global_lock);
- return 0;
+ return ret;
}
static int intel_setup_hpet_msi(unsigned int irq, unsigned int id)
{
- struct intel_iommu *iommu = map_hpet_to_ir(id);
+ int ret = -1;
+ struct intel_iommu *iommu;
int index;
- if (!iommu)
- return -1;
-
- index = alloc_irte(iommu, irq, 1);
- if (index < 0)
- return -1;
+ down_read(&dmar_global_lock);
+ iommu = map_hpet_to_ir(id);
+ if (iommu) {
+ index = alloc_irte(iommu, irq, 1);
+ if (index >= 0)
+ ret = 0;
+ }
+ up_read(&dmar_global_lock);
- return 0;
+ return ret;
}
struct irq_remap_ops intel_irq_remap_ops = {
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index 67da6cff74e8..f6b17e6af2fb 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -342,19 +342,30 @@ __is_range_overlap(struct rb_node *node,
return 0;
}
+static inline struct iova *
+alloc_and_init_iova(unsigned long pfn_lo, unsigned long pfn_hi)
+{
+ struct iova *iova;
+
+ iova = alloc_iova_mem();
+ if (iova) {
+ iova->pfn_lo = pfn_lo;
+ iova->pfn_hi = pfn_hi;
+ }
+
+ return iova;
+}
+
static struct iova *
__insert_new_range(struct iova_domain *iovad,
unsigned long pfn_lo, unsigned long pfn_hi)
{
struct iova *iova;
- iova = alloc_iova_mem();
- if (!iova)
- return iova;
+ iova = alloc_and_init_iova(pfn_lo, pfn_hi);
+ if (iova)
+ iova_insert_rbtree(&iovad->rbroot, iova);
- iova->pfn_hi = pfn_hi;
- iova->pfn_lo = pfn_lo;
- iova_insert_rbtree(&iovad->rbroot, iova);
return iova;
}
@@ -433,3 +444,44 @@ copy_reserved_iova(struct iova_domain *from, struct iova_domain *to)
}
spin_unlock_irqrestore(&from->iova_rbtree_lock, flags);
}
+
+struct iova *
+split_and_remove_iova(struct iova_domain *iovad, struct iova *iova,
+ unsigned long pfn_lo, unsigned long pfn_hi)
+{
+ unsigned long flags;
+ struct iova *prev = NULL, *next = NULL;
+
+ spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
+ if (iova->pfn_lo < pfn_lo) {
+ prev = alloc_and_init_iova(iova->pfn_lo, pfn_lo - 1);
+ if (prev == NULL)
+ goto error;
+ }
+ if (iova->pfn_hi > pfn_hi) {
+ next = alloc_and_init_iova(pfn_hi + 1, iova->pfn_hi);
+ if (next == NULL)
+ goto error;
+ }
+
+ __cached_rbnode_delete_update(iovad, iova);
+ rb_erase(&iova->node, &iovad->rbroot);
+
+ if (prev) {
+ iova_insert_rbtree(&iovad->rbroot, prev);
+ iova->pfn_lo = pfn_lo;
+ }
+ if (next) {
+ iova_insert_rbtree(&iovad->rbroot, next);
+ iova->pfn_hi = pfn_hi;
+ }
+ spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
+
+ return iova;
+
+error:
+ spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
+ if (prev)
+ free_iova_mem(prev);
+ return NULL;
+}
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index bcd78a720630..7fcbfc498fa9 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -23,6 +23,9 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_iommu.h>
+#include <linux/of_irq.h>
#include <asm/cacheflush.h>
@@ -146,13 +149,10 @@ static int iommu_enable(struct omap_iommu *obj)
struct platform_device *pdev = to_platform_device(obj->dev);
struct iommu_platform_data *pdata = pdev->dev.platform_data;
- if (!pdata)
- return -EINVAL;
-
if (!arch_iommu)
return -ENODEV;
- if (pdata->deassert_reset) {
+ if (pdata && pdata->deassert_reset) {
err = pdata->deassert_reset(pdev, pdata->reset_name);
if (err) {
dev_err(obj->dev, "deassert_reset failed: %d\n", err);
@@ -172,14 +172,11 @@ static void iommu_disable(struct omap_iommu *obj)
struct platform_device *pdev = to_platform_device(obj->dev);
struct iommu_platform_data *pdata = pdev->dev.platform_data;
- if (!pdata)
- return;
-
arch_iommu->disable(obj);
pm_runtime_put_sync(obj->dev);
- if (pdata->assert_reset)
+ if (pdata && pdata->assert_reset)
pdata->assert_reset(pdev, pdata->reset_name);
}
@@ -523,7 +520,8 @@ static void flush_iopte_range(u32 *first, u32 *last)
static void iopte_free(u32 *iopte)
{
/* Note: freed iopte's must be clean ready for re-use */
- kmem_cache_free(iopte_cachep, iopte);
+ if (iopte)
+ kmem_cache_free(iopte_cachep, iopte);
}
static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da)
@@ -863,7 +861,7 @@ static int device_match_by_alias(struct device *dev, void *data)
**/
static struct omap_iommu *omap_iommu_attach(const char *name, u32 *iopgd)
{
- int err = -ENOMEM;
+ int err;
struct device *dev;
struct omap_iommu *obj;
@@ -871,7 +869,7 @@ static struct omap_iommu *omap_iommu_attach(const char *name, u32 *iopgd)
(void *)name,
device_match_by_alias);
if (!dev)
- return NULL;
+ return ERR_PTR(-ENODEV);
obj = to_iommu(dev);
@@ -890,8 +888,10 @@ static struct omap_iommu *omap_iommu_attach(const char *name, u32 *iopgd)
goto err_enable;
flush_iotlb_all(obj);
- if (!try_module_get(obj->owner))
+ if (!try_module_get(obj->owner)) {
+ err = -ENODEV;
goto err_module;
+ }
spin_unlock(&obj->iommu_lock);
@@ -940,17 +940,41 @@ static int omap_iommu_probe(struct platform_device *pdev)
struct omap_iommu *obj;
struct resource *res;
struct iommu_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *of = pdev->dev.of_node;
- obj = kzalloc(sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL);
+ obj = devm_kzalloc(&pdev->dev, sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL);
if (!obj)
return -ENOMEM;
- obj->nr_tlb_entries = pdata->nr_tlb_entries;
- obj->name = pdata->name;
+ if (of) {
+ obj->name = dev_name(&pdev->dev);
+ obj->nr_tlb_entries = 32;
+ err = of_property_read_u32(of, "ti,#tlb-entries",
+ &obj->nr_tlb_entries);
+ if (err && err != -EINVAL)
+ return err;
+ if (obj->nr_tlb_entries != 32 && obj->nr_tlb_entries != 8)
+ return -EINVAL;
+ /*
+ * da_start and da_end are needed for omap-iovmm, so hardcode
+ * these values as used by OMAP3 ISP - the only user for
+ * omap-iovmm
+ */
+ obj->da_start = 0;
+ obj->da_end = 0xfffff000;
+ if (of_find_property(of, "ti,iommu-bus-err-back", NULL))
+ obj->has_bus_err_back = MMU_GP_REG_BUS_ERR_BACK_EN;
+ } else {
+ obj->nr_tlb_entries = pdata->nr_tlb_entries;
+ obj->name = pdata->name;
+ obj->da_start = pdata->da_start;
+ obj->da_end = pdata->da_end;
+ }
+ if (obj->da_end <= obj->da_start)
+ return -EINVAL;
+
obj->dev = &pdev->dev;
obj->ctx = (void *)obj + sizeof(*obj);
- obj->da_start = pdata->da_start;
- obj->da_end = pdata->da_end;
spin_lock_init(&obj->iommu_lock);
mutex_init(&obj->mmap_lock);
@@ -958,33 +982,18 @@ static int omap_iommu_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&obj->mmap);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -ENODEV;
- goto err_mem;
- }
-
- res = request_mem_region(res->start, resource_size(res),
- dev_name(&pdev->dev));
- if (!res) {
- err = -EIO;
- goto err_mem;
- }
-
- obj->regbase = ioremap(res->start, resource_size(res));
- if (!obj->regbase) {
- err = -ENOMEM;
- goto err_ioremap;
- }
+ obj->regbase = devm_ioremap_resource(obj->dev, res);
+ if (IS_ERR(obj->regbase))
+ return PTR_ERR(obj->regbase);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- err = -ENODEV;
- goto err_irq;
- }
- err = request_irq(irq, iommu_fault_handler, IRQF_SHARED,
- dev_name(&pdev->dev), obj);
+ if (irq < 0)
+ return -ENODEV;
+
+ err = devm_request_irq(obj->dev, irq, iommu_fault_handler, IRQF_SHARED,
+ dev_name(obj->dev), obj);
if (err < 0)
- goto err_irq;
+ return err;
platform_set_drvdata(pdev, obj);
pm_runtime_irq_safe(obj->dev);
@@ -992,42 +1001,34 @@ static int omap_iommu_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "%s registered\n", obj->name);
return 0;
-
-err_irq:
- iounmap(obj->regbase);
-err_ioremap:
- release_mem_region(res->start, resource_size(res));
-err_mem:
- kfree(obj);
- return err;
}
static int omap_iommu_remove(struct platform_device *pdev)
{
- int irq;
- struct resource *res;
struct omap_iommu *obj = platform_get_drvdata(pdev);
iopgtable_clear_entry_all(obj);
- irq = platform_get_irq(pdev, 0);
- free_irq(irq, obj);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
- iounmap(obj->regbase);
-
pm_runtime_disable(obj->dev);
dev_info(&pdev->dev, "%s removed\n", obj->name);
- kfree(obj);
return 0;
}
+static struct of_device_id omap_iommu_of_match[] = {
+ { .compatible = "ti,omap2-iommu" },
+ { .compatible = "ti,omap4-iommu" },
+ { .compatible = "ti,dra7-iommu" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_iommu_of_match);
+
static struct platform_driver omap_iommu_driver = {
.probe = omap_iommu_probe,
.remove = omap_iommu_remove,
.driver = {
.name = "omap-iommu",
+ .of_match_table = of_match_ptr(omap_iommu_of_match),
},
};
@@ -1253,6 +1254,49 @@ static int omap_iommu_domain_has_cap(struct iommu_domain *domain,
return 0;
}
+static int omap_iommu_add_device(struct device *dev)
+{
+ struct omap_iommu_arch_data *arch_data;
+ struct device_node *np;
+
+ /*
+ * Allocate the archdata iommu structure for DT-based devices.
+ *
+ * TODO: Simplify this when removing non-DT support completely from the
+ * IOMMU users.
+ */
+ if (!dev->of_node)
+ return 0;
+
+ np = of_parse_phandle(dev->of_node, "iommus", 0);
+ if (!np)
+ return 0;
+
+ arch_data = kzalloc(sizeof(*arch_data), GFP_KERNEL);
+ if (!arch_data) {
+ of_node_put(np);
+ return -ENOMEM;
+ }
+
+ arch_data->name = kstrdup(dev_name(dev), GFP_KERNEL);
+ dev->archdata.iommu = arch_data;
+
+ of_node_put(np);
+
+ return 0;
+}
+
+static void omap_iommu_remove_device(struct device *dev)
+{
+ struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
+
+ if (!dev->of_node || !arch_data)
+ return;
+
+ kfree(arch_data->name);
+ kfree(arch_data);
+}
+
static struct iommu_ops omap_iommu_ops = {
.domain_init = omap_iommu_domain_init,
.domain_destroy = omap_iommu_domain_destroy,
@@ -1262,6 +1306,8 @@ static struct iommu_ops omap_iommu_ops = {
.unmap = omap_iommu_unmap,
.iova_to_phys = omap_iommu_iova_to_phys,
.domain_has_cap = omap_iommu_domain_has_cap,
+ .add_device = omap_iommu_add_device,
+ .remove_device = omap_iommu_remove_device,
.pgsize_bitmap = OMAP_IOMMU_PGSIZES,
};
diff --git a/drivers/iommu/omap-iommu.h b/drivers/iommu/omap-iommu.h
index 120084206602..ea920c3e94ff 100644
--- a/drivers/iommu/omap-iommu.h
+++ b/drivers/iommu/omap-iommu.h
@@ -52,6 +52,8 @@ struct omap_iommu {
void *ctx; /* iommu context: registres saved area */
u32 da_start;
u32 da_end;
+
+ int has_bus_err_back;
};
struct cr_regs {
@@ -130,6 +132,7 @@ static inline struct omap_iommu *dev_to_omap_iommu(struct device *dev)
#define MMU_READ_CAM 0x68
#define MMU_READ_RAM 0x6c
#define MMU_EMU_FAULT_AD 0x70
+#define MMU_GP_REG 0x88
#define MMU_REG_SIZE 256
@@ -163,6 +166,8 @@ static inline struct omap_iommu *dev_to_omap_iommu(struct device *dev)
#define MMU_RAM_MIXED_MASK (1 << MMU_RAM_MIXED_SHIFT)
#define MMU_RAM_MIXED MMU_RAM_MIXED_MASK
+#define MMU_GP_REG_BUS_ERR_BACK_EN 0x1
+
/*
* utilities for super page(16MB, 1MB, 64KB and 4KB)
*/
diff --git a/drivers/iommu/omap-iommu2.c b/drivers/iommu/omap-iommu2.c
index d745094a69dd..5e1ea3b0bf16 100644
--- a/drivers/iommu/omap-iommu2.c
+++ b/drivers/iommu/omap-iommu2.c
@@ -98,6 +98,9 @@ static int omap2_iommu_enable(struct omap_iommu *obj)
iommu_write_reg(obj, pa, MMU_TTB);
+ if (obj->has_bus_err_back)
+ iommu_write_reg(obj, MMU_GP_REG_BUS_ERR_BACK_EN, MMU_GP_REG);
+
__iommu_set_twl(obj, true);
return 0;
diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c
index 7a3b928fad1c..464acda0bbc4 100644
--- a/drivers/iommu/shmobile-iommu.c
+++ b/drivers/iommu/shmobile-iommu.c
@@ -343,7 +343,7 @@ static int shmobile_iommu_add_device(struct device *dev)
mapping = archdata->iommu_mapping;
if (!mapping) {
mapping = arm_iommu_create_mapping(&platform_bus_type, 0,
- L1_LEN << 20, 0);
+ L1_LEN << 20);
if (IS_ERR(mapping))
return PTR_ERR(mapping);
archdata->iommu_mapping = mapping;
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 61ffdca96e25..d770f7406631 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -39,6 +39,14 @@ config IMGPDC_IRQ
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
+config CLPS711X_IRQCHIP
+ bool
+ depends on ARCH_CLPS711X
+ select IRQ_DOMAIN
+ select MULTI_IRQ_HANDLER
+ select SPARSE_IRQ
+ default y
+
config ORION_IRQCHIP
bool
select IRQ_DOMAIN
@@ -69,3 +77,11 @@ config VERSATILE_FPGA_IRQ_NR
config XTENSA_MX
bool
select IRQ_DOMAIN
+
+config IRQ_CROSSBAR
+ bool
+ help
+ Support for a CROSSBAR ip that preceeds the main interrupt controller.
+ The primary irqchip invokes the crossbar's callback which inturn allocates
+ a free irq and configures the IP. Thus the peripheral interrupts are
+ routed to one of the free irqchip interrupt lines.
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 1c0c151d108c..f180f8d5fb7b 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o
obj-$(CONFIG_METAG) += irq-metag-ext.o
obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
+obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o
obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
@@ -27,3 +28,4 @@ obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.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/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index 40e6440348ff..f8636a650cf6 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -17,7 +17,6 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <asm/mach/irq.h>
#include "irqchip.h"
@@ -81,7 +80,7 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq);
if (unlikely(!cascade_irq))
- do_bad_IRQ(irq, desc);
+ handle_bad_irq(irq, desc);
else
generic_handle_irq(cascade_irq);
diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c
new file mode 100644
index 000000000000..33340dc97d1d
--- /dev/null
+++ b/drivers/irqchip/irq-clps711x.c
@@ -0,0 +1,243 @@
+/*
+ * CLPS711X IRQ driver
+ *
+ * Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+
+#include <asm/exception.h>
+#include <asm/mach/irq.h>
+
+#include "irqchip.h"
+
+#define CLPS711X_INTSR1 (0x0240)
+#define CLPS711X_INTMR1 (0x0280)
+#define CLPS711X_BLEOI (0x0600)
+#define CLPS711X_MCEOI (0x0640)
+#define CLPS711X_TEOI (0x0680)
+#define CLPS711X_TC1EOI (0x06c0)
+#define CLPS711X_TC2EOI (0x0700)
+#define CLPS711X_RTCEOI (0x0740)
+#define CLPS711X_UMSEOI (0x0780)
+#define CLPS711X_COEOI (0x07c0)
+#define CLPS711X_INTSR2 (0x1240)
+#define CLPS711X_INTMR2 (0x1280)
+#define CLPS711X_SRXEOF (0x1600)
+#define CLPS711X_KBDEOI (0x1700)
+#define CLPS711X_INTSR3 (0x2240)
+#define CLPS711X_INTMR3 (0x2280)
+
+static const struct {
+#define CLPS711X_FLAG_EN (1 << 0)
+#define CLPS711X_FLAG_FIQ (1 << 1)
+ unsigned int flags;
+ phys_addr_t eoi;
+} clps711x_irqs[] = {
+ [1] = { CLPS711X_FLAG_FIQ, CLPS711X_BLEOI, },
+ [3] = { CLPS711X_FLAG_FIQ, CLPS711X_MCEOI, },
+ [4] = { CLPS711X_FLAG_EN, CLPS711X_COEOI, },
+ [5] = { CLPS711X_FLAG_EN, },
+ [6] = { CLPS711X_FLAG_EN, },
+ [7] = { CLPS711X_FLAG_EN, },
+ [8] = { CLPS711X_FLAG_EN, CLPS711X_TC1EOI, },
+ [9] = { CLPS711X_FLAG_EN, CLPS711X_TC2EOI, },
+ [10] = { CLPS711X_FLAG_EN, CLPS711X_RTCEOI, },
+ [11] = { CLPS711X_FLAG_EN, CLPS711X_TEOI, },
+ [12] = { CLPS711X_FLAG_EN, },
+ [13] = { CLPS711X_FLAG_EN, },
+ [14] = { CLPS711X_FLAG_EN, CLPS711X_UMSEOI, },
+ [15] = { CLPS711X_FLAG_EN, CLPS711X_SRXEOF, },
+ [16] = { CLPS711X_FLAG_EN, CLPS711X_KBDEOI, },
+ [17] = { CLPS711X_FLAG_EN, },
+ [18] = { CLPS711X_FLAG_EN, },
+ [28] = { CLPS711X_FLAG_EN, },
+ [29] = { CLPS711X_FLAG_EN, },
+ [32] = { CLPS711X_FLAG_FIQ, },
+};
+
+static struct {
+ void __iomem *base;
+ void __iomem *intmr[3];
+ void __iomem *intsr[3];
+ struct irq_domain *domain;
+ struct irq_domain_ops ops;
+} *clps711x_intc;
+
+static asmlinkage void __exception_irq_entry clps711x_irqh(struct pt_regs *regs)
+{
+ u32 irqnr, irqstat;
+
+ do {
+ irqstat = readw_relaxed(clps711x_intc->intmr[0]) &
+ readw_relaxed(clps711x_intc->intsr[0]);
+ if (irqstat) {
+ irqnr = irq_find_mapping(clps711x_intc->domain,
+ fls(irqstat) - 1);
+ handle_IRQ(irqnr, regs);
+ }
+
+ irqstat = readw_relaxed(clps711x_intc->intmr[1]) &
+ readw_relaxed(clps711x_intc->intsr[1]);
+ if (irqstat) {
+ irqnr = irq_find_mapping(clps711x_intc->domain,
+ fls(irqstat) - 1 + 16);
+ handle_IRQ(irqnr, regs);
+ }
+ } while (irqstat);
+}
+
+static void clps711x_intc_eoi(struct irq_data *d)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hwirq].eoi);
+}
+
+static void clps711x_intc_mask(struct irq_data *d)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
+ u32 tmp;
+
+ tmp = readl_relaxed(intmr);
+ tmp &= ~(1 << (hwirq % 16));
+ writel_relaxed(tmp, intmr);
+}
+
+static void clps711x_intc_unmask(struct irq_data *d)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
+ u32 tmp;
+
+ tmp = readl_relaxed(intmr);
+ tmp |= 1 << (hwirq % 16);
+ writel_relaxed(tmp, intmr);
+}
+
+static struct irq_chip clps711x_intc_chip = {
+ .name = "clps711x-intc",
+ .irq_eoi = clps711x_intc_eoi,
+ .irq_mask = clps711x_intc_mask,
+ .irq_unmask = clps711x_intc_unmask,
+};
+
+static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_flow_handler_t handler = handle_level_irq;
+ unsigned int flags = IRQF_VALID | IRQF_PROBE;
+
+ if (!clps711x_irqs[hw].flags)
+ return 0;
+
+ if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) {
+ handler = handle_bad_irq;
+ flags |= IRQF_NOAUTOEN;
+ } else if (clps711x_irqs[hw].eoi) {
+ handler = handle_fasteoi_irq;
+ }
+
+ /* Clear down pending interrupt */
+ if (clps711x_irqs[hw].eoi)
+ writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hw].eoi);
+
+ irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler);
+ set_irq_flags(virq, flags);
+
+ return 0;
+}
+
+static int __init _clps711x_intc_init(struct device_node *np,
+ phys_addr_t base, resource_size_t size)
+{
+ int err;
+
+ clps711x_intc = kzalloc(sizeof(*clps711x_intc), GFP_KERNEL);
+ if (!clps711x_intc)
+ return -ENOMEM;
+
+ clps711x_intc->base = ioremap(base, size);
+ if (!clps711x_intc->base) {
+ err = -ENOMEM;
+ goto out_kfree;
+ }
+
+ clps711x_intc->intsr[0] = clps711x_intc->base + CLPS711X_INTSR1;
+ clps711x_intc->intmr[0] = clps711x_intc->base + CLPS711X_INTMR1;
+ clps711x_intc->intsr[1] = clps711x_intc->base + CLPS711X_INTSR2;
+ clps711x_intc->intmr[1] = clps711x_intc->base + CLPS711X_INTMR2;
+ clps711x_intc->intsr[2] = clps711x_intc->base + CLPS711X_INTSR3;
+ clps711x_intc->intmr[2] = clps711x_intc->base + CLPS711X_INTMR3;
+
+ /* Mask all interrupts */
+ writel_relaxed(0, clps711x_intc->intmr[0]);
+ writel_relaxed(0, clps711x_intc->intmr[1]);
+ writel_relaxed(0, clps711x_intc->intmr[2]);
+
+ err = irq_alloc_descs(-1, 0, ARRAY_SIZE(clps711x_irqs), numa_node_id());
+ if (IS_ERR_VALUE(err))
+ goto out_iounmap;
+
+ clps711x_intc->ops.map = clps711x_intc_irq_map;
+ clps711x_intc->ops.xlate = irq_domain_xlate_onecell;
+ clps711x_intc->domain =
+ irq_domain_add_legacy(np, ARRAY_SIZE(clps711x_irqs),
+ 0, 0, &clps711x_intc->ops, NULL);
+ if (!clps711x_intc->domain) {
+ err = -ENOMEM;
+ goto out_irqfree;
+ }
+
+ irq_set_default_host(clps711x_intc->domain);
+ set_handle_irq(clps711x_irqh);
+
+#ifdef CONFIG_FIQ
+ init_FIQ(0);
+#endif
+
+ return 0;
+
+out_irqfree:
+ irq_free_descs(0, ARRAY_SIZE(clps711x_irqs));
+
+out_iounmap:
+ iounmap(clps711x_intc->base);
+
+out_kfree:
+ kfree(clps711x_intc);
+
+ return err;
+}
+
+void __init clps711x_intc_init(phys_addr_t base, resource_size_t size)
+{
+ BUG_ON(_clps711x_intc_init(NULL, base, size));
+}
+
+#ifdef CONFIG_IRQCHIP
+static int __init clps711x_intc_init_dt(struct device_node *np,
+ struct device_node *parent)
+{
+ struct resource res;
+ int err;
+
+ err = of_address_to_resource(np, 0, &res);
+ if (err)
+ return err;
+
+ return _clps711x_intc_init(np, res.start, resource_size(&res));
+}
+IRQCHIP_DECLARE(clps711x, "cirrus,clps711x-intc", clps711x_intc_init_dt);
+#endif
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
new file mode 100644
index 000000000000..fc817d28d1fe
--- /dev/null
+++ b/drivers/irqchip/irq-crossbar.c
@@ -0,0 +1,208 @@
+/*
+ * drivers/irqchip/irq-crossbar.c
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Sricharan R <r.sricharan@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/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/irqchip/arm-gic.h>
+
+#define IRQ_FREE -1
+#define GIC_IRQ_START 32
+
+/*
+ * @int_max: maximum number of supported interrupts
+ * @irq_map: array of interrupts to crossbar number mapping
+ * @crossbar_base: crossbar base address
+ * @register_offsets: offsets for each irq number
+ */
+struct crossbar_device {
+ uint int_max;
+ uint *irq_map;
+ void __iomem *crossbar_base;
+ int *register_offsets;
+ void (*write) (int, int);
+};
+
+static struct crossbar_device *cb;
+
+static inline void crossbar_writel(int irq_no, int cb_no)
+{
+ writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
+}
+
+static inline void crossbar_writew(int irq_no, int cb_no)
+{
+ writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
+}
+
+static inline void crossbar_writeb(int irq_no, int cb_no)
+{
+ writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
+}
+
+static inline int allocate_free_irq(int cb_no)
+{
+ int i;
+
+ for (i = 0; i < cb->int_max; i++) {
+ if (cb->irq_map[i] == IRQ_FREE) {
+ cb->irq_map[i] = cb_no;
+ return i;
+ }
+ }
+
+ return -ENODEV;
+}
+
+static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]);
+ return 0;
+}
+
+static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
+
+ if (hw > GIC_IRQ_START)
+ cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE;
+}
+
+static int crossbar_domain_xlate(struct irq_domain *d,
+ struct device_node *controller,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ unsigned long ret;
+
+ ret = allocate_free_irq(intspec[1]);
+
+ if (IS_ERR_VALUE(ret))
+ return ret;
+
+ *out_hwirq = ret + GIC_IRQ_START;
+ return 0;
+}
+
+const struct irq_domain_ops routable_irq_domain_ops = {
+ .map = crossbar_domain_map,
+ .unmap = crossbar_domain_unmap,
+ .xlate = crossbar_domain_xlate
+};
+
+static int __init crossbar_of_init(struct device_node *node)
+{
+ int i, size, max, reserved = 0, entry;
+ const __be32 *irqsr;
+
+ cb = kzalloc(sizeof(struct cb_device *), GFP_KERNEL);
+
+ if (!cb)
+ return -ENOMEM;
+
+ cb->crossbar_base = of_iomap(node, 0);
+ if (!cb->crossbar_base)
+ goto err1;
+
+ of_property_read_u32(node, "ti,max-irqs", &max);
+ cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL);
+ if (!cb->irq_map)
+ goto err2;
+
+ cb->int_max = max;
+
+ for (i = 0; i < max; i++)
+ cb->irq_map[i] = IRQ_FREE;
+
+ /* Get and mark reserved irqs */
+ irqsr = of_get_property(node, "ti,irqs-reserved", &size);
+ if (irqsr) {
+ size /= sizeof(__be32);
+
+ for (i = 0; i < size; i++) {
+ of_property_read_u32_index(node,
+ "ti,irqs-reserved",
+ i, &entry);
+ if (entry > max) {
+ pr_err("Invalid reserved entry\n");
+ goto err3;
+ }
+ cb->irq_map[entry] = 0;
+ }
+ }
+
+ cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL);
+ if (!cb->register_offsets)
+ goto err3;
+
+ of_property_read_u32(node, "ti,reg-size", &size);
+
+ switch (size) {
+ case 1:
+ cb->write = crossbar_writeb;
+ break;
+ case 2:
+ cb->write = crossbar_writew;
+ break;
+ case 4:
+ cb->write = crossbar_writel;
+ break;
+ default:
+ pr_err("Invalid reg-size property\n");
+ goto err4;
+ break;
+ }
+
+ /*
+ * Register offsets are not linear because of the
+ * reserved irqs. so find and store the offsets once.
+ */
+ for (i = 0; i < max; i++) {
+ if (!cb->irq_map[i])
+ continue;
+
+ cb->register_offsets[i] = reserved;
+ reserved += size;
+ }
+
+ register_routable_domain_ops(&routable_irq_domain_ops);
+ return 0;
+
+err4:
+ kfree(cb->register_offsets);
+err3:
+ kfree(cb->irq_map);
+err2:
+ iounmap(cb->crossbar_base);
+err1:
+ kfree(cb);
+ return -ENOMEM;
+}
+
+static const struct of_device_id crossbar_match[] __initconst = {
+ { .compatible = "ti,irq-crossbar" },
+ {}
+};
+
+int __init irqcrossbar_init(void)
+{
+ struct device_node *np;
+ np = of_find_matching_node(NULL, crossbar_match);
+ if (!np)
+ return -ENODEV;
+
+ crossbar_of_init(np);
+ return 0;
+}
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 531769b2433a..4300b6606f5e 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -661,9 +661,9 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
/*
* Ensure that stores to Normal memory are visible to the
- * other CPUs before issuing the IPI.
+ * other CPUs before they observe us issuing the IPI.
*/
- dsb();
+ dmb(ishst);
/* this always happens on GIC0 */
writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
@@ -824,16 +824,25 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_set_chip_and_handler(irq, &gic_chip,
handle_fasteoi_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+
+ gic_routable_irq_domain_ops->map(d, irq, hw);
}
irq_set_chip_data(irq, d->host_data);
return 0;
}
+static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ gic_routable_irq_domain_ops->unmap(d, irq);
+}
+
static int gic_irq_domain_xlate(struct irq_domain *d,
struct device_node *controller,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type)
{
+ unsigned long ret = 0;
+
if (d->of_node != controller)
return -EINVAL;
if (intsize < 3)
@@ -843,11 +852,20 @@ static int gic_irq_domain_xlate(struct irq_domain *d,
*out_hwirq = intspec[1] + 16;
/* For SPIs, we need to add 16 more to get the GIC irq ID number */
- if (!intspec[0])
- *out_hwirq += 16;
+ if (!intspec[0]) {
+ ret = gic_routable_irq_domain_ops->xlate(d, controller,
+ intspec,
+ intsize,
+ out_hwirq,
+ out_type);
+
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ }
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
- return 0;
+
+ return ret;
}
#ifdef CONFIG_SMP
@@ -871,9 +889,41 @@ static struct notifier_block gic_cpu_notifier = {
static const struct irq_domain_ops gic_irq_domain_ops = {
.map = gic_irq_domain_map,
+ .unmap = gic_irq_domain_unmap,
.xlate = gic_irq_domain_xlate,
};
+/* Default functions for routable irq domain */
+static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ return 0;
+}
+
+static void gic_routable_irq_domain_unmap(struct irq_domain *d,
+ unsigned int irq)
+{
+}
+
+static int gic_routable_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *controller,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ *out_hwirq += 16;
+ return 0;
+}
+
+const struct irq_domain_ops gic_default_routable_irq_domain_ops = {
+ .map = gic_routable_irq_domain_map,
+ .unmap = gic_routable_irq_domain_unmap,
+ .xlate = gic_routable_irq_domain_xlate,
+};
+
+const struct irq_domain_ops *gic_routable_irq_domain_ops =
+ &gic_default_routable_irq_domain_ops;
+
void __init gic_init_bases(unsigned int gic_nr, int irq_start,
void __iomem *dist_base, void __iomem *cpu_base,
u32 percpu_offset, struct device_node *node)
@@ -881,6 +931,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
irq_hw_number_t hwirq_base;
struct gic_chip_data *gic;
int gic_irqs, irq_base, i;
+ int nr_routable_irqs;
BUG_ON(gic_nr >= MAX_GIC_NR);
@@ -946,14 +997,25 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
gic->gic_irqs = gic_irqs;
gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
- irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());
- if (IS_ERR_VALUE(irq_base)) {
- WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
- irq_start);
- irq_base = irq_start;
+
+ if (of_property_read_u32(node, "arm,routable-irqs",
+ &nr_routable_irqs)) {
+ irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
+ numa_node_id());
+ if (IS_ERR_VALUE(irq_base)) {
+ WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
+ irq_start);
+ irq_base = irq_start;
+ }
+
+ gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
+ hwirq_base, &gic_irq_domain_ops, gic);
+ } else {
+ gic->domain = irq_domain_add_linear(node, nr_routable_irqs,
+ &gic_irq_domain_ops,
+ gic);
}
- gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
- hwirq_base, &gic_irq_domain_ops, gic);
+
if (WARN_ON(!gic->domain))
return;
diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c
index 3c8827fe83f3..1c3e2c9b46ba 100644
--- a/drivers/irqchip/irq-mmp.c
+++ b/drivers/irqchip/irq-mmp.c
@@ -22,7 +22,7 @@
#include <linux/of_irq.h>
#include <asm/exception.h>
-#include <asm/mach/irq.h>
+#include <asm/hardirq.h>
#include "irqchip.h"
diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c
index 473f09a74d4d..37dab0b472cd 100644
--- a/drivers/irqchip/irq-vic.c
+++ b/drivers/irqchip/irq-vic.c
@@ -57,6 +57,7 @@
/**
* struct vic_device - VIC PM device
+ * @parent_irq: The parent IRQ number of the VIC if cascaded, or 0.
* @irq: The IRQ number for the base of the VIC.
* @base: The register base for the VIC.
* @valid_sources: A bitmask of valid interrupts
@@ -224,6 +225,17 @@ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs)
return handled;
}
+static void vic_handle_irq_cascaded(unsigned int irq, struct irq_desc *desc)
+{
+ u32 stat, hwirq;
+ struct vic_device *vic = irq_desc_get_handler_data(desc);
+
+ while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) {
+ hwirq = ffs(stat) - 1;
+ generic_handle_irq(irq_find_mapping(vic->domain, hwirq));
+ }
+}
+
/*
* Keep iterating over all registered VIC's until there are no pending
* interrupts.
@@ -246,6 +258,7 @@ static struct irq_domain_ops vic_irqdomain_ops = {
/**
* vic_register() - Register a VIC.
* @base: The base address of the VIC.
+ * @parent_irq: The parent IRQ if cascaded, else 0.
* @irq: The base IRQ for the VIC.
* @valid_sources: bitmask of valid interrupts
* @resume_sources: bitmask of interrupts allowed for resume sources.
@@ -257,7 +270,8 @@ static struct irq_domain_ops vic_irqdomain_ops = {
*
* This also configures the IRQ domain for the VIC.
*/
-static void __init vic_register(void __iomem *base, unsigned int irq,
+static void __init vic_register(void __iomem *base, unsigned int parent_irq,
+ unsigned int irq,
u32 valid_sources, u32 resume_sources,
struct device_node *node)
{
@@ -273,15 +287,25 @@ static void __init vic_register(void __iomem *base, unsigned int irq,
v->base = base;
v->valid_sources = valid_sources;
v->resume_sources = resume_sources;
- v->irq = irq;
set_handle_irq(vic_handle_irq);
vic_id++;
+
+ if (parent_irq) {
+ irq_set_handler_data(parent_irq, v);
+ irq_set_chained_handler(parent_irq, vic_handle_irq_cascaded);
+ }
+
v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,
&vic_irqdomain_ops, v);
/* create an IRQ mapping for each valid IRQ */
for (i = 0; i < fls(valid_sources); i++)
if (valid_sources & (1 << i))
irq_create_mapping(v->domain, i);
+ /* If no base IRQ was passed, figure out our allocated base */
+ if (irq)
+ v->irq = irq;
+ else
+ v->irq = irq_find_mapping(v->domain, 0);
}
static void vic_ack_irq(struct irq_data *d)
@@ -409,10 +433,10 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start,
writel(32, base + VIC_PL190_DEF_VECT_ADDR);
}
- vic_register(base, irq_start, vic_sources, 0, node);
+ vic_register(base, 0, irq_start, vic_sources, 0, node);
}
-void __init __vic_init(void __iomem *base, int irq_start,
+void __init __vic_init(void __iomem *base, int parent_irq, int irq_start,
u32 vic_sources, u32 resume_sources,
struct device_node *node)
{
@@ -449,7 +473,7 @@ void __init __vic_init(void __iomem *base, int irq_start,
vic_init2(base);
- vic_register(base, irq_start, vic_sources, resume_sources, node);
+ vic_register(base, parent_irq, irq_start, vic_sources, resume_sources, node);
}
/**
@@ -462,8 +486,30 @@ void __init __vic_init(void __iomem *base, int irq_start,
void __init vic_init(void __iomem *base, unsigned int irq_start,
u32 vic_sources, u32 resume_sources)
{
- __vic_init(base, irq_start, vic_sources, resume_sources, NULL);
+ __vic_init(base, 0, irq_start, vic_sources, resume_sources, NULL);
+}
+
+/**
+ * vic_init_cascaded() - initialise a cascaded vectored interrupt controller
+ * @base: iomem base address
+ * @parent_irq: the parent IRQ we're cascaded off
+ * @irq_start: starting interrupt number, must be muliple of 32
+ * @vic_sources: bitmask of interrupt sources to allow
+ * @resume_sources: bitmask of interrupt sources to allow for resume
+ *
+ * This returns the base for the new interrupts or negative on error.
+ */
+int __init vic_init_cascaded(void __iomem *base, unsigned int parent_irq,
+ u32 vic_sources, u32 resume_sources)
+{
+ struct vic_device *v;
+
+ v = &vic_devices[vic_id];
+ __vic_init(base, parent_irq, 0, vic_sources, resume_sources, NULL);
+ /* Return out acquired base */
+ return v->irq;
}
+EXPORT_SYMBOL_GPL(vic_init_cascaded);
#ifdef CONFIG_OF
int __init vic_of_init(struct device_node *node, struct device_node *parent)
@@ -485,7 +531,7 @@ int __init vic_of_init(struct device_node *node, struct device_node *parent)
/*
* Passing 0 as first IRQ makes the simple domain allocate descriptors
*/
- __vic_init(regs, 0, interrupt_mask, wakeup_mask, node);
+ __vic_init(regs, 0, 0, interrupt_mask, wakeup_mask, node);
return 0;
}
diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c
index b4147c0b14b7..c3a1b061838d 100644
--- a/drivers/isdn/act2000/module.c
+++ b/drivers/isdn/act2000/module.c
@@ -796,7 +796,7 @@ static void __exit act2000_exit(void)
act2000_card *last;
while (card) {
unregister_card(card);
- del_timer(&card->ptimer);
+ del_timer_sync(&card->ptimer);
card = card->next;
}
card = cards;
diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c
index fb4f1bac0133..1c5dc345e7c5 100644
--- a/drivers/isdn/divert/divert_procfs.c
+++ b/drivers/isdn/divert/divert_procfs.c
@@ -86,12 +86,13 @@ isdn_divert_read(struct file *file, char __user *buf, size_t count, loff_t *off)
struct divert_info *inf;
int len;
- if (!*((struct divert_info **) file->private_data)) {
+ if (!(inf = *((struct divert_info **) file->private_data))) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
- interruptible_sleep_on(&(rd_queue));
+ wait_event_interruptible(rd_queue, (inf =
+ *((struct divert_info **) file->private_data)));
}
- if (!(inf = *((struct divert_info **) file->private_data)))
+ if (!inf)
return (0);
inf->usage_cnt--; /* new usage count */
diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c
index 2be1c8a3bb5f..d8ef64da26f1 100644
--- a/drivers/isdn/hisax/elsa.c
+++ b/drivers/isdn/hisax/elsa.c
@@ -509,7 +509,8 @@ static void
set_arcofi(struct IsdnCardState *cs, int bc) {
cs->dc.isac.arcofi_bc = bc;
arcofi_fsm(cs, ARCOFI_START, &ARCOFI_COP_5);
- interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+ wait_event_interruptible(cs->dc.isac.arcofi_wait,
+ cs->dc.isac.arcofi_state == ARCOFI_NOP);
}
static int
@@ -528,7 +529,8 @@ check_arcofi(struct IsdnCardState *cs)
}
cs->dc.isac.arcofi_bc = 0;
arcofi_fsm(cs, ARCOFI_START, &ARCOFI_VERSION);
- interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+ wait_event_interruptible(cs->dc.isac.arcofi_wait,
+ cs->dc.isac.arcofi_state == ARCOFI_NOP);
if (!test_and_clear_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags)) {
debugl1(cs, "Arcofi response received %d bytes", cs->dc.isac.mon_rxp);
p = cs->dc.isac.mon_rx;
@@ -595,7 +597,8 @@ check_arcofi(struct IsdnCardState *cs)
Elsa_Types[cs->subtyp],
cs->hw.elsa.base + 8);
arcofi_fsm(cs, ARCOFI_START, &ARCOFI_XOP_0);
- interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+ wait_event_interruptible(cs->dc.isac.arcofi_wait,
+ cs->dc.isac.arcofi_state == ARCOFI_NOP);
return (1);
}
return (0);
diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c
index 3f84dd8f1757..a2a358c1dc8e 100644
--- a/drivers/isdn/hisax/elsa_ser.c
+++ b/drivers/isdn/hisax/elsa_ser.c
@@ -573,7 +573,8 @@ modem_l2l1(struct PStack *st, int pr, void *arg)
test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
bcs->cs->dc.isac.arcofi_bc = st->l1.bc;
arcofi_fsm(bcs->cs, ARCOFI_START, &ARCOFI_XOP_0);
- interruptible_sleep_on(&bcs->cs->dc.isac.arcofi_wait);
+ wait_event_interruptible(bcs->cs->dc.isac.arcofi_wait,
+ bcs->cs->dc.isac.arcofi_state == ARCOFI_NOP);
bcs->cs->hw.elsa.MFlag = 1;
} else {
printk(KERN_WARNING "ElsaSer: unknown pr %x\n", pr);
diff --git a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c
index b61e8d5e84ad..7b5fd8fb1761 100644
--- a/drivers/isdn/hysdn/hysdn_proclog.c
+++ b/drivers/isdn/hysdn/hysdn_proclog.c
@@ -175,14 +175,15 @@ hysdn_log_read(struct file *file, char __user *buf, size_t count, loff_t *off)
int len;
hysdn_card *card = PDE_DATA(file_inode(file));
- if (!*((struct log_data **) file->private_data)) {
+ if (!(inf = *((struct log_data **) file->private_data))) {
struct procdata *pd = card->proclog;
if (file->f_flags & O_NONBLOCK)
return (-EAGAIN);
- interruptible_sleep_on(&(pd->rd_queue));
+ wait_event_interruptible(pd->rd_queue, (inf =
+ *((struct log_data **) file->private_data)));
}
- if (!(inf = *((struct log_data **) file->private_data)))
+ if (!inf)
return (0);
inf->usage_cnt--; /* new usage count */
diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
index 9bb12ba3191f..9b856e1890d1 100644
--- a/drivers/isdn/i4l/isdn_common.c
+++ b/drivers/isdn/i4l/isdn_common.c
@@ -777,7 +777,8 @@ isdn_readbchan(int di, int channel, u_char *buf, u_char *fp, int len, wait_queue
return 0;
if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) {
if (sleep)
- interruptible_sleep_on(sleep);
+ wait_event_interruptible(*sleep,
+ !skb_queue_empty(&dev->drv[di]->rpqueue[channel]));
else
return 0;
}
@@ -1072,7 +1073,8 @@ isdn_read(struct file *file, char __user *buf, size_t count, loff_t *off)
retval = -EAGAIN;
goto out;
}
- interruptible_sleep_on(&(dev->info_waitq));
+ wait_event_interruptible(dev->info_waitq,
+ file->private_data);
}
p = isdn_statstr();
file->private_data = NULL;
@@ -1128,7 +1130,8 @@ isdn_read(struct file *file, char __user *buf, size_t count, loff_t *off)
retval = -EAGAIN;
goto out;
}
- interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));
+ wait_event_interruptible(dev->drv[drvidx]->st_waitq,
+ dev->drv[drvidx]->stavail);
}
if (dev->drv[drvidx]->interface->readstat) {
if (count > dev->drv[drvidx]->stavail)
@@ -1188,8 +1191,8 @@ isdn_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
goto out;
}
chidx = isdn_minor2chan(minor);
- while ((retval = isdn_writebuf_stub(drvidx, chidx, buf, count)) == 0)
- interruptible_sleep_on(&dev->drv[drvidx]->snd_waitq[chidx]);
+ wait_event_interruptible(dev->drv[drvidx]->snd_waitq[chidx],
+ (retval = isdn_writebuf_stub(drvidx, chidx, buf, count)));
goto out;
}
if (minor <= ISDN_MINOR_CTRLMAX) {
@@ -2378,7 +2381,7 @@ static void __exit isdn_exit(void)
}
isdn_tty_exit();
unregister_chrdev(ISDN_MAJOR, "isdn");
- del_timer(&dev->timer);
+ del_timer_sync(&dev->timer);
/* call vfree with interrupts enabled, else it will hang */
vfree(dev);
printk(KERN_NOTICE "ISDN-subsystem unloaded\n");
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index 38ceac5053a0..a5da511e3c9a 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -378,10 +378,15 @@ isdn_ppp_release(int min, struct file *file)
is->slcomp = NULL;
#endif
#ifdef CONFIG_IPPP_FILTER
- kfree(is->pass_filter);
- is->pass_filter = NULL;
- kfree(is->active_filter);
- is->active_filter = NULL;
+ if (is->pass_filter) {
+ sk_unattached_filter_destroy(is->pass_filter);
+ is->pass_filter = NULL;
+ }
+
+ if (is->active_filter) {
+ sk_unattached_filter_destroy(is->active_filter);
+ is->active_filter = NULL;
+ }
#endif
/* TODO: if this was the previous master: link the stuff to the new master */
@@ -629,25 +634,41 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
#ifdef CONFIG_IPPP_FILTER
case PPPIOCSPASS:
{
+ struct sock_fprog fprog;
struct sock_filter *code;
- int len = get_filter(argp, &code);
+ int err, len = get_filter(argp, &code);
+
if (len < 0)
return len;
- kfree(is->pass_filter);
- is->pass_filter = code;
- is->pass_len = len;
- break;
+
+ fprog.len = len;
+ fprog.filter = code;
+
+ if (is->pass_filter)
+ sk_unattached_filter_destroy(is->pass_filter);
+ err = sk_unattached_filter_create(&is->pass_filter, &fprog);
+ kfree(code);
+
+ return err;
}
case PPPIOCSACTIVE:
{
+ struct sock_fprog fprog;
struct sock_filter *code;
- int len = get_filter(argp, &code);
+ int err, len = get_filter(argp, &code);
+
if (len < 0)
return len;
- kfree(is->active_filter);
- is->active_filter = code;
- is->active_len = len;
- break;
+
+ fprog.len = len;
+ fprog.filter = code;
+
+ if (is->active_filter)
+ sk_unattached_filter_destroy(is->active_filter);
+ err = sk_unattached_filter_create(&is->active_filter, &fprog);
+ kfree(code);
+
+ return err;
}
#endif /* CONFIG_IPPP_FILTER */
default:
@@ -1147,14 +1168,14 @@ isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *
}
if (is->pass_filter
- && sk_run_filter(skb, is->pass_filter) == 0) {
+ && SK_RUN_FILTER(is->pass_filter, skb) == 0) {
if (is->debug & 0x2)
printk(KERN_DEBUG "IPPP: inbound frame filtered.\n");
kfree_skb(skb);
return;
}
if (!(is->active_filter
- && sk_run_filter(skb, is->active_filter) == 0)) {
+ && SK_RUN_FILTER(is->active_filter, skb) == 0)) {
if (is->debug & 0x2)
printk(KERN_DEBUG "IPPP: link-active filter: resetting huptimer.\n");
lp->huptimer = 0;
@@ -1293,14 +1314,14 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
}
if (ipt->pass_filter
- && sk_run_filter(skb, ipt->pass_filter) == 0) {
+ && SK_RUN_FILTER(ipt->pass_filter, skb) == 0) {
if (ipt->debug & 0x4)
printk(KERN_DEBUG "IPPP: outbound frame filtered.\n");
kfree_skb(skb);
goto unlock;
}
if (!(ipt->active_filter
- && sk_run_filter(skb, ipt->active_filter) == 0)) {
+ && SK_RUN_FILTER(ipt->active_filter, skb) == 0)) {
if (ipt->debug & 0x4)
printk(KERN_DEBUG "IPPP: link-active filter: resetting huptimer.\n");
lp->huptimer = 0;
@@ -1490,9 +1511,9 @@ int isdn_ppp_autodial_filter(struct sk_buff *skb, isdn_net_local *lp)
}
drop |= is->pass_filter
- && sk_run_filter(skb, is->pass_filter) == 0;
+ && SK_RUN_FILTER(is->pass_filter, skb) == 0;
drop |= is->active_filter
- && sk_run_filter(skb, is->active_filter) == 0;
+ && SK_RUN_FILTER(is->active_filter, skb) == 0;
skb_push(skb, IPPP_MAX_HEADER - 4);
return drop;
diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c
index 1eaf62273903..f02cc506fbfa 100644
--- a/drivers/isdn/pcbit/drv.c
+++ b/drivers/isdn/pcbit/drv.c
@@ -796,6 +796,7 @@ static void set_running_timeout(unsigned long ptr)
#endif
dev = (struct pcbit_dev *) ptr;
+ dev->l2_state = L2_DOWN;
wake_up_interruptible(&dev->set_running_wq);
}
@@ -818,7 +819,8 @@ static int set_protocol_running(struct pcbit_dev *dev)
add_timer(&dev->set_running_timer);
- interruptible_sleep_on(&dev->set_running_wq);
+ wait_event(dev->set_running_wq, dev->l2_state == L2_RUNNING ||
+ dev->l2_state == L2_DOWN);
del_timer(&dev->set_running_timer);
@@ -842,8 +844,6 @@ static int set_protocol_running(struct pcbit_dev *dev)
printk(KERN_DEBUG "pcbit: initialization failed\n");
printk(KERN_DEBUG "pcbit: firmware not loaded\n");
- dev->l2_state = L2_DOWN;
-
#ifdef DEBUG
printk(KERN_DEBUG "Bank3 = %02x\n",
readb(dev->sh_mem + BANK3));
diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c
index 92acc81f844d..d6f19b168e8a 100644
--- a/drivers/isdn/sc/init.c
+++ b/drivers/isdn/sc/init.c
@@ -390,8 +390,8 @@ static void __exit sc_exit(void)
/*
* kill the timers
*/
- del_timer(&(sc_adapter[i]->reset_timer));
- del_timer(&(sc_adapter[i]->stat_timer));
+ del_timer_sync(&(sc_adapter[i]->reset_timer));
+ del_timer_sync(&(sc_adapter[i]->stat_timer));
/*
* Tell I4L we're toast
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 72156c123033..44c358ecf5a1 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -421,7 +421,7 @@ config LEDS_MC13783
config LEDS_NS2
tristate "LED support for Network Space v2 GPIO LEDs"
depends on LEDS_CLASS
- depends on ARCH_KIRKWOOD
+ depends on ARCH_KIRKWOOD || MACH_KIRKWOOD
default y
help
This option enable support for the dual-GPIO LED found on the
@@ -431,7 +431,7 @@ config LEDS_NS2
config LEDS_NETXBIG
tristate "LED support for Big Network series LEDs"
depends on LEDS_CLASS
- depends on ARCH_KIRKWOOD
+ depends on ARCH_KIRKWOOD || MACH_KIRKWOOD
default y
help
This option enable support for LEDs found on the LaCie 2Big
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 95ad936e6048..5bdedf6df153 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -285,6 +285,17 @@ config DM_CACHE_CLEANER
A simple cache policy that writes back all data to the
origin. Used when decommissioning a dm-cache.
+config DM_ERA
+ tristate "Era target (EXPERIMENTAL)"
+ depends on BLK_DEV_DM
+ default n
+ select DM_PERSISTENT_DATA
+ select DM_BIO_PRISON
+ ---help---
+ dm-era tracks which parts of a block device are written to
+ over time. Useful for maintaining cache coherency when using
+ vendor snapshots.
+
config DM_MIRROR
tristate "Mirror target"
depends on BLK_DEV_DM
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index f26d83292579..a2da532b1c2b 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -14,6 +14,7 @@ dm-thin-pool-y += dm-thin.o dm-thin-metadata.o
dm-cache-y += dm-cache-target.o dm-cache-metadata.o dm-cache-policy.o
dm-cache-mq-y += dm-cache-policy-mq.o
dm-cache-cleaner-y += dm-cache-policy-cleaner.o
+dm-era-y += dm-era-target.o
md-mod-y += md.o bitmap.o
raid456-y += raid5.o
@@ -53,6 +54,7 @@ obj-$(CONFIG_DM_VERITY) += dm-verity.o
obj-$(CONFIG_DM_CACHE) += dm-cache.o
obj-$(CONFIG_DM_CACHE_MQ) += dm-cache-mq.o
obj-$(CONFIG_DM_CACHE_CLEANER) += dm-cache-cleaner.o
+obj-$(CONFIG_DM_ERA) += dm-era.o
ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs += dm-uevent.o
diff --git a/drivers/md/dm-cache-block-types.h b/drivers/md/dm-cache-block-types.h
index bed4ad4e1b7c..aac0e2df06be 100644
--- a/drivers/md/dm-cache-block-types.h
+++ b/drivers/md/dm-cache-block-types.h
@@ -19,7 +19,6 @@
typedef dm_block_t __bitwise__ dm_oblock_t;
typedef uint32_t __bitwise__ dm_cblock_t;
-typedef dm_block_t __bitwise__ dm_dblock_t;
static inline dm_oblock_t to_oblock(dm_block_t b)
{
@@ -41,14 +40,4 @@ static inline uint32_t from_cblock(dm_cblock_t b)
return (__force uint32_t) b;
}
-static inline dm_dblock_t to_dblock(dm_block_t b)
-{
- return (__force dm_dblock_t) b;
-}
-
-static inline dm_block_t from_dblock(dm_dblock_t b)
-{
- return (__force dm_block_t) b;
-}
-
#endif /* DM_CACHE_BLOCK_TYPES_H */
diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c
index 9ef0752e8a08..4ead4ba60656 100644
--- a/drivers/md/dm-cache-metadata.c
+++ b/drivers/md/dm-cache-metadata.c
@@ -109,7 +109,7 @@ struct dm_cache_metadata {
dm_block_t discard_root;
sector_t discard_block_size;
- dm_dblock_t discard_nr_blocks;
+ dm_oblock_t discard_nr_blocks;
sector_t data_block_size;
dm_cblock_t cache_blocks;
@@ -120,6 +120,12 @@ struct dm_cache_metadata {
unsigned policy_version[CACHE_POLICY_VERSION_SIZE];
size_t policy_hint_size;
struct dm_cache_statistics stats;
+
+ /*
+ * Reading the space map root can fail, so we read it into this
+ * buffer before the superblock is locked and updated.
+ */
+ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
};
/*-------------------------------------------------------------------
@@ -260,11 +266,31 @@ static void __setup_mapping_info(struct dm_cache_metadata *cmd)
}
}
+static int __save_sm_root(struct dm_cache_metadata *cmd)
+{
+ int r;
+ size_t metadata_len;
+
+ r = dm_sm_root_size(cmd->metadata_sm, &metadata_len);
+ if (r < 0)
+ return r;
+
+ return dm_sm_copy_root(cmd->metadata_sm, &cmd->metadata_space_map_root,
+ metadata_len);
+}
+
+static void __copy_sm_root(struct dm_cache_metadata *cmd,
+ struct cache_disk_superblock *disk_super)
+{
+ memcpy(&disk_super->metadata_space_map_root,
+ &cmd->metadata_space_map_root,
+ sizeof(cmd->metadata_space_map_root));
+}
+
static int __write_initial_superblock(struct dm_cache_metadata *cmd)
{
int r;
struct dm_block *sblock;
- size_t metadata_len;
struct cache_disk_superblock *disk_super;
sector_t bdev_size = i_size_read(cmd->bdev->bd_inode) >> SECTOR_SHIFT;
@@ -272,12 +298,16 @@ static int __write_initial_superblock(struct dm_cache_metadata *cmd)
if (bdev_size > DM_CACHE_METADATA_MAX_SECTORS)
bdev_size = DM_CACHE_METADATA_MAX_SECTORS;
- r = dm_sm_root_size(cmd->metadata_sm, &metadata_len);
+ r = dm_tm_pre_commit(cmd->tm);
if (r < 0)
return r;
- r = dm_tm_pre_commit(cmd->tm);
- if (r < 0)
+ /*
+ * dm_sm_copy_root() can fail. So we need to do it before we start
+ * updating the superblock.
+ */
+ r = __save_sm_root(cmd);
+ if (r)
return r;
r = superblock_lock_zero(cmd, &sblock);
@@ -293,16 +323,13 @@ static int __write_initial_superblock(struct dm_cache_metadata *cmd)
memset(disk_super->policy_version, 0, sizeof(disk_super->policy_version));
disk_super->policy_hint_size = 0;
- r = dm_sm_copy_root(cmd->metadata_sm, &disk_super->metadata_space_map_root,
- metadata_len);
- if (r < 0)
- goto bad_locked;
+ __copy_sm_root(cmd, disk_super);
disk_super->mapping_root = cpu_to_le64(cmd->root);
disk_super->hint_root = cpu_to_le64(cmd->hint_root);
disk_super->discard_root = cpu_to_le64(cmd->discard_root);
disk_super->discard_block_size = cpu_to_le64(cmd->discard_block_size);
- disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks));
+ disk_super->discard_nr_blocks = cpu_to_le64(from_oblock(cmd->discard_nr_blocks));
disk_super->metadata_block_size = cpu_to_le32(DM_CACHE_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
disk_super->data_block_size = cpu_to_le32(cmd->data_block_size);
disk_super->cache_blocks = cpu_to_le32(0);
@@ -313,10 +340,6 @@ static int __write_initial_superblock(struct dm_cache_metadata *cmd)
disk_super->write_misses = cpu_to_le32(0);
return dm_tm_commit(cmd->tm, sblock);
-
-bad_locked:
- dm_bm_unlock(sblock);
- return r;
}
static int __format_metadata(struct dm_cache_metadata *cmd)
@@ -496,7 +519,7 @@ static void read_superblock_fields(struct dm_cache_metadata *cmd,
cmd->hint_root = le64_to_cpu(disk_super->hint_root);
cmd->discard_root = le64_to_cpu(disk_super->discard_root);
cmd->discard_block_size = le64_to_cpu(disk_super->discard_block_size);
- cmd->discard_nr_blocks = to_dblock(le64_to_cpu(disk_super->discard_nr_blocks));
+ cmd->discard_nr_blocks = to_oblock(le64_to_cpu(disk_super->discard_nr_blocks));
cmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
cmd->cache_blocks = to_cblock(le32_to_cpu(disk_super->cache_blocks));
strncpy(cmd->policy_name, disk_super->policy_name, sizeof(cmd->policy_name));
@@ -530,8 +553,9 @@ static int __begin_transaction_flags(struct dm_cache_metadata *cmd,
disk_super = dm_block_data(sblock);
update_flags(disk_super, mutator);
read_superblock_fields(cmd, disk_super);
+ dm_bm_unlock(sblock);
- return dm_bm_flush_and_unlock(cmd->bm, sblock);
+ return dm_bm_flush(cmd->bm);
}
static int __begin_transaction(struct dm_cache_metadata *cmd)
@@ -559,7 +583,6 @@ static int __commit_transaction(struct dm_cache_metadata *cmd,
flags_mutator mutator)
{
int r;
- size_t metadata_len;
struct cache_disk_superblock *disk_super;
struct dm_block *sblock;
@@ -577,8 +600,8 @@ static int __commit_transaction(struct dm_cache_metadata *cmd,
if (r < 0)
return r;
- r = dm_sm_root_size(cmd->metadata_sm, &metadata_len);
- if (r < 0)
+ r = __save_sm_root(cmd);
+ if (r)
return r;
r = superblock_lock(cmd, &sblock);
@@ -594,7 +617,7 @@ static int __commit_transaction(struct dm_cache_metadata *cmd,
disk_super->hint_root = cpu_to_le64(cmd->hint_root);
disk_super->discard_root = cpu_to_le64(cmd->discard_root);
disk_super->discard_block_size = cpu_to_le64(cmd->discard_block_size);
- disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks));
+ disk_super->discard_nr_blocks = cpu_to_le64(from_oblock(cmd->discard_nr_blocks));
disk_super->cache_blocks = cpu_to_le32(from_cblock(cmd->cache_blocks));
strncpy(disk_super->policy_name, cmd->policy_name, sizeof(disk_super->policy_name));
disk_super->policy_version[0] = cpu_to_le32(cmd->policy_version[0]);
@@ -605,13 +628,7 @@ static int __commit_transaction(struct dm_cache_metadata *cmd,
disk_super->read_misses = cpu_to_le32(cmd->stats.read_misses);
disk_super->write_hits = cpu_to_le32(cmd->stats.write_hits);
disk_super->write_misses = cpu_to_le32(cmd->stats.write_misses);
-
- r = dm_sm_copy_root(cmd->metadata_sm, &disk_super->metadata_space_map_root,
- metadata_len);
- if (r < 0) {
- dm_bm_unlock(sblock);
- return r;
- }
+ __copy_sm_root(cmd, disk_super);
return dm_tm_commit(cmd->tm, sblock);
}
@@ -771,15 +788,15 @@ out:
int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
sector_t discard_block_size,
- dm_dblock_t new_nr_entries)
+ dm_oblock_t new_nr_entries)
{
int r;
down_write(&cmd->root_lock);
r = dm_bitset_resize(&cmd->discard_info,
cmd->discard_root,
- from_dblock(cmd->discard_nr_blocks),
- from_dblock(new_nr_entries),
+ from_oblock(cmd->discard_nr_blocks),
+ from_oblock(new_nr_entries),
false, &cmd->discard_root);
if (!r) {
cmd->discard_block_size = discard_block_size;
@@ -792,28 +809,28 @@ int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
return r;
}
-static int __set_discard(struct dm_cache_metadata *cmd, dm_dblock_t b)
+static int __set_discard(struct dm_cache_metadata *cmd, dm_oblock_t b)
{
return dm_bitset_set_bit(&cmd->discard_info, cmd->discard_root,
- from_dblock(b), &cmd->discard_root);
+ from_oblock(b), &cmd->discard_root);
}
-static int __clear_discard(struct dm_cache_metadata *cmd, dm_dblock_t b)
+static int __clear_discard(struct dm_cache_metadata *cmd, dm_oblock_t b)
{
return dm_bitset_clear_bit(&cmd->discard_info, cmd->discard_root,
- from_dblock(b), &cmd->discard_root);
+ from_oblock(b), &cmd->discard_root);
}
-static int __is_discarded(struct dm_cache_metadata *cmd, dm_dblock_t b,
+static int __is_discarded(struct dm_cache_metadata *cmd, dm_oblock_t b,
bool *is_discarded)
{
return dm_bitset_test_bit(&cmd->discard_info, cmd->discard_root,
- from_dblock(b), &cmd->discard_root,
+ from_oblock(b), &cmd->discard_root,
is_discarded);
}
static int __discard(struct dm_cache_metadata *cmd,
- dm_dblock_t dblock, bool discard)
+ dm_oblock_t dblock, bool discard)
{
int r;
@@ -826,7 +843,7 @@ static int __discard(struct dm_cache_metadata *cmd,
}
int dm_cache_set_discard(struct dm_cache_metadata *cmd,
- dm_dblock_t dblock, bool discard)
+ dm_oblock_t dblock, bool discard)
{
int r;
@@ -844,8 +861,8 @@ static int __load_discards(struct dm_cache_metadata *cmd,
dm_block_t b;
bool discard;
- for (b = 0; b < from_dblock(cmd->discard_nr_blocks); b++) {
- dm_dblock_t dblock = to_dblock(b);
+ for (b = 0; b < from_oblock(cmd->discard_nr_blocks); b++) {
+ dm_oblock_t dblock = to_oblock(b);
if (cmd->clean_when_opened) {
r = __is_discarded(cmd, dblock, &discard);
@@ -1228,22 +1245,12 @@ static int begin_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *po
return 0;
}
-int dm_cache_begin_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *policy)
+static int save_hint(void *context, dm_cblock_t cblock, dm_oblock_t oblock, uint32_t hint)
{
+ struct dm_cache_metadata *cmd = context;
+ __le32 value = cpu_to_le32(hint);
int r;
- down_write(&cmd->root_lock);
- r = begin_hints(cmd, policy);
- up_write(&cmd->root_lock);
-
- return r;
-}
-
-static int save_hint(struct dm_cache_metadata *cmd, dm_cblock_t cblock,
- uint32_t hint)
-{
- int r;
- __le32 value = cpu_to_le32(hint);
__dm_bless_for_disk(&value);
r = dm_array_set_value(&cmd->hint_info, cmd->hint_root,
@@ -1253,16 +1260,25 @@ static int save_hint(struct dm_cache_metadata *cmd, dm_cblock_t cblock,
return r;
}
-int dm_cache_save_hint(struct dm_cache_metadata *cmd, dm_cblock_t cblock,
- uint32_t hint)
+static int write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *policy)
{
int r;
- if (!hints_array_initialized(cmd))
- return 0;
+ r = begin_hints(cmd, policy);
+ if (r) {
+ DMERR("begin_hints failed");
+ return r;
+ }
+
+ return policy_walk_mappings(policy, save_hint, cmd);
+}
+
+int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *policy)
+{
+ int r;
down_write(&cmd->root_lock);
- r = save_hint(cmd, cblock, hint);
+ r = write_hints(cmd, policy);
up_write(&cmd->root_lock);
return r;
diff --git a/drivers/md/dm-cache-metadata.h b/drivers/md/dm-cache-metadata.h
index cd906f14f98d..cd70a78623a3 100644
--- a/drivers/md/dm-cache-metadata.h
+++ b/drivers/md/dm-cache-metadata.h
@@ -72,14 +72,14 @@ dm_cblock_t dm_cache_size(struct dm_cache_metadata *cmd);
int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
sector_t discard_block_size,
- dm_dblock_t new_nr_entries);
+ dm_oblock_t new_nr_entries);
typedef int (*load_discard_fn)(void *context, sector_t discard_block_size,
- dm_dblock_t dblock, bool discarded);
+ dm_oblock_t dblock, bool discarded);
int dm_cache_load_discards(struct dm_cache_metadata *cmd,
load_discard_fn fn, void *context);
-int dm_cache_set_discard(struct dm_cache_metadata *cmd, dm_dblock_t dblock, bool discard);
+int dm_cache_set_discard(struct dm_cache_metadata *cmd, dm_oblock_t dblock, bool discard);
int dm_cache_remove_mapping(struct dm_cache_metadata *cmd, dm_cblock_t cblock);
int dm_cache_insert_mapping(struct dm_cache_metadata *cmd, dm_cblock_t cblock, dm_oblock_t oblock);
@@ -128,14 +128,7 @@ void dm_cache_dump(struct dm_cache_metadata *cmd);
* rather than querying the policy for each cblock, we let it walk its data
* structures and fill in the hints in whatever order it wishes.
*/
-
-int dm_cache_begin_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *p);
-
-/*
- * requests hints for every cblock and stores in the metadata device.
- */
-int dm_cache_save_hint(struct dm_cache_metadata *cmd,
- dm_cblock_t cblock, uint32_t hint);
+int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *p);
/*
* Query method. Are all the blocks in the cache clean?
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 074b9c8e4cf0..1bf4a71919ec 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -237,9 +237,8 @@ struct cache {
/*
* origin_blocks entries, discarded if set.
*/
- dm_dblock_t discard_nr_blocks;
+ dm_oblock_t discard_nr_blocks;
unsigned long *discard_bitset;
- uint32_t discard_block_size; /* a power of 2 times sectors per block */
/*
* Rather than reconstructing the table line for the status we just
@@ -526,48 +525,33 @@ static dm_block_t block_div(dm_block_t b, uint32_t n)
return b;
}
-static dm_dblock_t oblock_to_dblock(struct cache *cache, dm_oblock_t oblock)
-{
- uint32_t discard_blocks = cache->discard_block_size;
- dm_block_t b = from_oblock(oblock);
-
- if (!block_size_is_power_of_two(cache))
- discard_blocks = discard_blocks / cache->sectors_per_block;
- else
- discard_blocks >>= cache->sectors_per_block_shift;
-
- b = block_div(b, discard_blocks);
-
- return to_dblock(b);
-}
-
-static void set_discard(struct cache *cache, dm_dblock_t b)
+static void set_discard(struct cache *cache, dm_oblock_t b)
{
unsigned long flags;
atomic_inc(&cache->stats.discard_count);
spin_lock_irqsave(&cache->lock, flags);
- set_bit(from_dblock(b), cache->discard_bitset);
+ set_bit(from_oblock(b), cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
}
-static void clear_discard(struct cache *cache, dm_dblock_t b)
+static void clear_discard(struct cache *cache, dm_oblock_t b)
{
unsigned long flags;
spin_lock_irqsave(&cache->lock, flags);
- clear_bit(from_dblock(b), cache->discard_bitset);
+ clear_bit(from_oblock(b), cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
}
-static bool is_discarded(struct cache *cache, dm_dblock_t b)
+static bool is_discarded(struct cache *cache, dm_oblock_t b)
{
int r;
unsigned long flags;
spin_lock_irqsave(&cache->lock, flags);
- r = test_bit(from_dblock(b), cache->discard_bitset);
+ r = test_bit(from_oblock(b), cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
return r;
@@ -579,8 +563,7 @@ static bool is_discarded_oblock(struct cache *cache, dm_oblock_t b)
unsigned long flags;
spin_lock_irqsave(&cache->lock, flags);
- r = test_bit(from_dblock(oblock_to_dblock(cache, b)),
- cache->discard_bitset);
+ r = test_bit(from_oblock(b), cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
return r;
@@ -705,7 +688,7 @@ static void remap_to_origin_clear_discard(struct cache *cache, struct bio *bio,
check_if_tick_bio_needed(cache, bio);
remap_to_origin(cache, bio);
if (bio_data_dir(bio) == WRITE)
- clear_discard(cache, oblock_to_dblock(cache, oblock));
+ clear_discard(cache, oblock);
}
static void remap_to_cache_dirty(struct cache *cache, struct bio *bio,
@@ -715,7 +698,7 @@ static void remap_to_cache_dirty(struct cache *cache, struct bio *bio,
remap_to_cache(cache, bio, cblock);
if (bio_data_dir(bio) == WRITE) {
set_dirty(cache, oblock, cblock);
- clear_discard(cache, oblock_to_dblock(cache, oblock));
+ clear_discard(cache, oblock);
}
}
@@ -1288,14 +1271,14 @@ static void process_flush_bio(struct cache *cache, struct bio *bio)
static void process_discard_bio(struct cache *cache, struct bio *bio)
{
dm_block_t start_block = dm_sector_div_up(bio->bi_iter.bi_sector,
- cache->discard_block_size);
+ cache->sectors_per_block);
dm_block_t end_block = bio_end_sector(bio);
dm_block_t b;
- end_block = block_div(end_block, cache->discard_block_size);
+ end_block = block_div(end_block, cache->sectors_per_block);
for (b = start_block; b < end_block; b++)
- set_discard(cache, to_dblock(b));
+ set_discard(cache, to_oblock(b));
bio_endio(bio, 0);
}
@@ -2171,35 +2154,6 @@ static int create_cache_policy(struct cache *cache, struct cache_args *ca,
return 0;
}
-/*
- * We want the discard block size to be a power of two, at least the size
- * of the cache block size, and have no more than 2^14 discard blocks
- * across the origin.
- */
-#define MAX_DISCARD_BLOCKS (1 << 14)
-
-static bool too_many_discard_blocks(sector_t discard_block_size,
- sector_t origin_size)
-{
- (void) sector_div(origin_size, discard_block_size);
-
- return origin_size > MAX_DISCARD_BLOCKS;
-}
-
-static sector_t calculate_discard_block_size(sector_t cache_block_size,
- sector_t origin_size)
-{
- sector_t discard_block_size;
-
- discard_block_size = roundup_pow_of_two(cache_block_size);
-
- if (origin_size)
- while (too_many_discard_blocks(discard_block_size, origin_size))
- discard_block_size *= 2;
-
- return discard_block_size;
-}
-
#define DEFAULT_MIGRATION_THRESHOLD 2048
static int cache_create(struct cache_args *ca, struct cache **result)
@@ -2321,16 +2275,13 @@ static int cache_create(struct cache_args *ca, struct cache **result)
}
clear_bitset(cache->dirty_bitset, from_cblock(cache->cache_size));
- cache->discard_block_size =
- calculate_discard_block_size(cache->sectors_per_block,
- cache->origin_sectors);
- cache->discard_nr_blocks = oblock_to_dblock(cache, cache->origin_blocks);
- cache->discard_bitset = alloc_bitset(from_dblock(cache->discard_nr_blocks));
+ cache->discard_nr_blocks = cache->origin_blocks;
+ cache->discard_bitset = alloc_bitset(from_oblock(cache->discard_nr_blocks));
if (!cache->discard_bitset) {
*error = "could not allocate discard bitset";
goto bad;
}
- clear_bitset(cache->discard_bitset, from_dblock(cache->discard_nr_blocks));
+ clear_bitset(cache->discard_bitset, from_oblock(cache->discard_nr_blocks));
cache->copier = dm_kcopyd_client_create(&dm_kcopyd_throttle);
if (IS_ERR(cache->copier)) {
@@ -2614,16 +2565,16 @@ static int write_discard_bitset(struct cache *cache)
{
unsigned i, r;
- r = dm_cache_discard_bitset_resize(cache->cmd, cache->discard_block_size,
- cache->discard_nr_blocks);
+ r = dm_cache_discard_bitset_resize(cache->cmd, cache->sectors_per_block,
+ cache->origin_blocks);
if (r) {
DMERR("could not resize on-disk discard bitset");
return r;
}
- for (i = 0; i < from_dblock(cache->discard_nr_blocks); i++) {
- r = dm_cache_set_discard(cache->cmd, to_dblock(i),
- is_discarded(cache, to_dblock(i)));
+ for (i = 0; i < from_oblock(cache->discard_nr_blocks); i++) {
+ r = dm_cache_set_discard(cache->cmd, to_oblock(i),
+ is_discarded(cache, to_oblock(i)));
if (r)
return r;
}
@@ -2631,30 +2582,6 @@ static int write_discard_bitset(struct cache *cache)
return 0;
}
-static int save_hint(void *context, dm_cblock_t cblock, dm_oblock_t oblock,
- uint32_t hint)
-{
- struct cache *cache = context;
- return dm_cache_save_hint(cache->cmd, cblock, hint);
-}
-
-static int write_hints(struct cache *cache)
-{
- int r;
-
- r = dm_cache_begin_hints(cache->cmd, cache->policy);
- if (r) {
- DMERR("dm_cache_begin_hints failed");
- return r;
- }
-
- r = policy_walk_mappings(cache->policy, save_hint, cache);
- if (r)
- DMERR("policy_walk_mappings failed");
-
- return r;
-}
-
/*
* returns true on success
*/
@@ -2672,7 +2599,7 @@ static bool sync_metadata(struct cache *cache)
save_stats(cache);
- r3 = write_hints(cache);
+ r3 = dm_cache_write_hints(cache->cmd, cache->policy);
if (r3)
DMERR("could not write hints");
@@ -2720,16 +2647,14 @@ static int load_mapping(void *context, dm_oblock_t oblock, dm_cblock_t cblock,
}
static int load_discard(void *context, sector_t discard_block_size,
- dm_dblock_t dblock, bool discard)
+ dm_oblock_t oblock, bool discard)
{
struct cache *cache = context;
- /* FIXME: handle mis-matched block size */
-
if (discard)
- set_discard(cache, dblock);
+ set_discard(cache, oblock);
else
- clear_discard(cache, dblock);
+ clear_discard(cache, oblock);
return 0;
}
@@ -3120,8 +3045,8 @@ static void set_discard_limits(struct cache *cache, struct queue_limits *limits)
/*
* FIXME: these limits may be incompatible with the cache device
*/
- limits->max_discard_sectors = cache->discard_block_size * 1024;
- limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT;
+ limits->max_discard_sectors = cache->sectors_per_block;
+ limits->discard_granularity = cache->sectors_per_block << SECTOR_SHIFT;
}
static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
@@ -3145,7 +3070,7 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
static struct target_type cache_target = {
.name = "cache",
- .version = {1, 3, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = cache_ctr,
.dtr = cache_dtr,
diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c
new file mode 100644
index 000000000000..414dad4cb49b
--- /dev/null
+++ b/drivers/md/dm-era-target.c
@@ -0,0 +1,1746 @@
+#include "dm.h"
+#include "persistent-data/dm-transaction-manager.h"
+#include "persistent-data/dm-bitset.h"
+#include "persistent-data/dm-space-map.h"
+
+#include <linux/dm-io.h>
+#include <linux/dm-kcopyd.h>
+#include <linux/init.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#define DM_MSG_PREFIX "era"
+
+#define SUPERBLOCK_LOCATION 0
+#define SUPERBLOCK_MAGIC 2126579579
+#define SUPERBLOCK_CSUM_XOR 146538381
+#define MIN_ERA_VERSION 1
+#define MAX_ERA_VERSION 1
+#define INVALID_WRITESET_ROOT SUPERBLOCK_LOCATION
+#define MIN_BLOCK_SIZE 8
+
+/*----------------------------------------------------------------
+ * Writeset
+ *--------------------------------------------------------------*/
+struct writeset_metadata {
+ uint32_t nr_bits;
+ dm_block_t root;
+};
+
+struct writeset {
+ struct writeset_metadata md;
+
+ /*
+ * An in core copy of the bits to save constantly doing look ups on
+ * disk.
+ */
+ unsigned long *bits;
+};
+
+/*
+ * This does not free off the on disk bitset as this will normally be done
+ * after digesting into the era array.
+ */
+static void writeset_free(struct writeset *ws)
+{
+ vfree(ws->bits);
+}
+
+static int setup_on_disk_bitset(struct dm_disk_bitset *info,
+ unsigned nr_bits, dm_block_t *root)
+{
+ int r;
+
+ r = dm_bitset_empty(info, root);
+ if (r)
+ return r;
+
+ return dm_bitset_resize(info, *root, 0, nr_bits, false, root);
+}
+
+static size_t bitset_size(unsigned nr_bits)
+{
+ return sizeof(unsigned long) * dm_div_up(nr_bits, BITS_PER_LONG);
+}
+
+/*
+ * Allocates memory for the in core bitset.
+ */
+static int writeset_alloc(struct writeset *ws, dm_block_t nr_blocks)
+{
+ ws->md.nr_bits = nr_blocks;
+ ws->md.root = INVALID_WRITESET_ROOT;
+ ws->bits = vzalloc(bitset_size(nr_blocks));
+ if (!ws->bits) {
+ DMERR("%s: couldn't allocate in memory bitset", __func__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/*
+ * Wipes the in-core bitset, and creates a new on disk bitset.
+ */
+static int writeset_init(struct dm_disk_bitset *info, struct writeset *ws)
+{
+ int r;
+
+ memset(ws->bits, 0, bitset_size(ws->md.nr_bits));
+
+ r = setup_on_disk_bitset(info, ws->md.nr_bits, &ws->md.root);
+ if (r) {
+ DMERR("%s: setup_on_disk_bitset failed", __func__);
+ return r;
+ }
+
+ return 0;
+}
+
+static bool writeset_marked(struct writeset *ws, dm_block_t block)
+{
+ return test_bit(block, ws->bits);
+}
+
+static int writeset_marked_on_disk(struct dm_disk_bitset *info,
+ struct writeset_metadata *m, dm_block_t block,
+ bool *result)
+{
+ dm_block_t old = m->root;
+
+ /*
+ * The bitset was flushed when it was archived, so we know there'll
+ * be no change to the root.
+ */
+ int r = dm_bitset_test_bit(info, m->root, block, &m->root, result);
+ if (r) {
+ DMERR("%s: dm_bitset_test_bit failed", __func__);
+ return r;
+ }
+
+ BUG_ON(m->root != old);
+
+ return r;
+}
+
+/*
+ * Returns < 0 on error, 0 if the bit wasn't previously set, 1 if it was.
+ */
+static int writeset_test_and_set(struct dm_disk_bitset *info,
+ struct writeset *ws, uint32_t block)
+{
+ int r;
+
+ if (!test_and_set_bit(block, ws->bits)) {
+ r = dm_bitset_set_bit(info, ws->md.root, block, &ws->md.root);
+ if (r) {
+ /* FIXME: fail mode */
+ return r;
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+/*----------------------------------------------------------------
+ * On disk metadata layout
+ *--------------------------------------------------------------*/
+#define SPACE_MAP_ROOT_SIZE 128
+#define UUID_LEN 16
+
+struct writeset_disk {
+ __le32 nr_bits;
+ __le64 root;
+} __packed;
+
+struct superblock_disk {
+ __le32 csum;
+ __le32 flags;
+ __le64 blocknr;
+
+ __u8 uuid[UUID_LEN];
+ __le64 magic;
+ __le32 version;
+
+ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
+
+ __le32 data_block_size;
+ __le32 metadata_block_size;
+ __le32 nr_blocks;
+
+ __le32 current_era;
+ struct writeset_disk current_writeset;
+
+ /*
+ * Only these two fields are valid within the metadata snapshot.
+ */
+ __le64 writeset_tree_root;
+ __le64 era_array_root;
+
+ __le64 metadata_snap;
+} __packed;
+
+/*----------------------------------------------------------------
+ * Superblock validation
+ *--------------------------------------------------------------*/
+static void sb_prepare_for_write(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t sb_block_size)
+{
+ struct superblock_disk *disk = dm_block_data(b);
+
+ disk->blocknr = cpu_to_le64(dm_block_location(b));
+ disk->csum = cpu_to_le32(dm_bm_checksum(&disk->flags,
+ sb_block_size - sizeof(__le32),
+ SUPERBLOCK_CSUM_XOR));
+}
+
+static int check_metadata_version(struct superblock_disk *disk)
+{
+ uint32_t metadata_version = le32_to_cpu(disk->version);
+ if (metadata_version < MIN_ERA_VERSION || metadata_version > MAX_ERA_VERSION) {
+ DMERR("Era metadata version %u found, but only versions between %u and %u supported.",
+ metadata_version, MIN_ERA_VERSION, MAX_ERA_VERSION);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sb_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t sb_block_size)
+{
+ struct superblock_disk *disk = dm_block_data(b);
+ __le32 csum_le;
+
+ if (dm_block_location(b) != le64_to_cpu(disk->blocknr)) {
+ DMERR("sb_check failed: blocknr %llu: wanted %llu",
+ le64_to_cpu(disk->blocknr),
+ (unsigned long long)dm_block_location(b));
+ return -ENOTBLK;
+ }
+
+ if (le64_to_cpu(disk->magic) != SUPERBLOCK_MAGIC) {
+ DMERR("sb_check failed: magic %llu: wanted %llu",
+ le64_to_cpu(disk->magic),
+ (unsigned long long) SUPERBLOCK_MAGIC);
+ return -EILSEQ;
+ }
+
+ csum_le = cpu_to_le32(dm_bm_checksum(&disk->flags,
+ sb_block_size - sizeof(__le32),
+ SUPERBLOCK_CSUM_XOR));
+ if (csum_le != disk->csum) {
+ DMERR("sb_check failed: csum %u: wanted %u",
+ le32_to_cpu(csum_le), le32_to_cpu(disk->csum));
+ return -EILSEQ;
+ }
+
+ return check_metadata_version(disk);
+}
+
+static struct dm_block_validator sb_validator = {
+ .name = "superblock",
+ .prepare_for_write = sb_prepare_for_write,
+ .check = sb_check
+};
+
+/*----------------------------------------------------------------
+ * Low level metadata handling
+ *--------------------------------------------------------------*/
+#define DM_ERA_METADATA_BLOCK_SIZE 4096
+#define DM_ERA_METADATA_CACHE_SIZE 64
+#define ERA_MAX_CONCURRENT_LOCKS 5
+
+struct era_metadata {
+ struct block_device *bdev;
+ struct dm_block_manager *bm;
+ struct dm_space_map *sm;
+ struct dm_transaction_manager *tm;
+
+ dm_block_t block_size;
+ uint32_t nr_blocks;
+
+ uint32_t current_era;
+
+ /*
+ * We preallocate 2 writesets. When an era rolls over we
+ * switch between them. This means the allocation is done at
+ * preresume time, rather than on the io path.
+ */
+ struct writeset writesets[2];
+ struct writeset *current_writeset;
+
+ dm_block_t writeset_tree_root;
+ dm_block_t era_array_root;
+
+ struct dm_disk_bitset bitset_info;
+ struct dm_btree_info writeset_tree_info;
+ struct dm_array_info era_array_info;
+
+ dm_block_t metadata_snap;
+
+ /*
+ * A flag that is set whenever a writeset has been archived.
+ */
+ bool archived_writesets;
+
+ /*
+ * Reading the space map root can fail, so we read it into this
+ * buffer before the superblock is locked and updated.
+ */
+ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
+};
+
+static int superblock_read_lock(struct era_metadata *md,
+ struct dm_block **sblock)
+{
+ return dm_bm_read_lock(md->bm, SUPERBLOCK_LOCATION,
+ &sb_validator, sblock);
+}
+
+static int superblock_lock_zero(struct era_metadata *md,
+ struct dm_block **sblock)
+{
+ return dm_bm_write_lock_zero(md->bm, SUPERBLOCK_LOCATION,
+ &sb_validator, sblock);
+}
+
+static int superblock_lock(struct era_metadata *md,
+ struct dm_block **sblock)
+{
+ return dm_bm_write_lock(md->bm, SUPERBLOCK_LOCATION,
+ &sb_validator, sblock);
+}
+
+/* FIXME: duplication with cache and thin */
+static int superblock_all_zeroes(struct dm_block_manager *bm, bool *result)
+{
+ int r;
+ unsigned i;
+ struct dm_block *b;
+ __le64 *data_le, zero = cpu_to_le64(0);
+ unsigned sb_block_size = dm_bm_block_size(bm) / sizeof(__le64);
+
+ /*
+ * We can't use a validator here - it may be all zeroes.
+ */
+ r = dm_bm_read_lock(bm, SUPERBLOCK_LOCATION, NULL, &b);
+ if (r)
+ return r;
+
+ data_le = dm_block_data(b);
+ *result = true;
+ for (i = 0; i < sb_block_size; i++) {
+ if (data_le[i] != zero) {
+ *result = false;
+ break;
+ }
+ }
+
+ return dm_bm_unlock(b);
+}
+
+/*----------------------------------------------------------------*/
+
+static void ws_pack(const struct writeset_metadata *core, struct writeset_disk *disk)
+{
+ disk->nr_bits = cpu_to_le32(core->nr_bits);
+ disk->root = cpu_to_le64(core->root);
+}
+
+static void ws_unpack(const struct writeset_disk *disk, struct writeset_metadata *core)
+{
+ core->nr_bits = le32_to_cpu(disk->nr_bits);
+ core->root = le64_to_cpu(disk->root);
+}
+
+static void ws_inc(void *context, const void *value)
+{
+ struct era_metadata *md = context;
+ struct writeset_disk ws_d;
+ dm_block_t b;
+
+ memcpy(&ws_d, value, sizeof(ws_d));
+ b = le64_to_cpu(ws_d.root);
+
+ dm_tm_inc(md->tm, b);
+}
+
+static void ws_dec(void *context, const void *value)
+{
+ struct era_metadata *md = context;
+ struct writeset_disk ws_d;
+ dm_block_t b;
+
+ memcpy(&ws_d, value, sizeof(ws_d));
+ b = le64_to_cpu(ws_d.root);
+
+ dm_bitset_del(&md->bitset_info, b);
+}
+
+static int ws_eq(void *context, const void *value1, const void *value2)
+{
+ return !memcmp(value1, value2, sizeof(struct writeset_metadata));
+}
+
+/*----------------------------------------------------------------*/
+
+static void setup_writeset_tree_info(struct era_metadata *md)
+{
+ struct dm_btree_value_type *vt = &md->writeset_tree_info.value_type;
+ md->writeset_tree_info.tm = md->tm;
+ md->writeset_tree_info.levels = 1;
+ vt->context = md;
+ vt->size = sizeof(struct writeset_disk);
+ vt->inc = ws_inc;
+ vt->dec = ws_dec;
+ vt->equal = ws_eq;
+}
+
+static void setup_era_array_info(struct era_metadata *md)
+
+{
+ struct dm_btree_value_type vt;
+ vt.context = NULL;
+ vt.size = sizeof(__le32);
+ vt.inc = NULL;
+ vt.dec = NULL;
+ vt.equal = NULL;
+
+ dm_array_info_init(&md->era_array_info, md->tm, &vt);
+}
+
+static void setup_infos(struct era_metadata *md)
+{
+ dm_disk_bitset_init(md->tm, &md->bitset_info);
+ setup_writeset_tree_info(md);
+ setup_era_array_info(md);
+}
+
+/*----------------------------------------------------------------*/
+
+static int create_fresh_metadata(struct era_metadata *md)
+{
+ int r;
+
+ r = dm_tm_create_with_sm(md->bm, SUPERBLOCK_LOCATION,
+ &md->tm, &md->sm);
+ if (r < 0) {
+ DMERR("dm_tm_create_with_sm failed");
+ return r;
+ }
+
+ setup_infos(md);
+
+ r = dm_btree_empty(&md->writeset_tree_info, &md->writeset_tree_root);
+ if (r) {
+ DMERR("couldn't create new writeset tree");
+ goto bad;
+ }
+
+ r = dm_array_empty(&md->era_array_info, &md->era_array_root);
+ if (r) {
+ DMERR("couldn't create era array");
+ goto bad;
+ }
+
+ return 0;
+
+bad:
+ dm_sm_destroy(md->sm);
+ dm_tm_destroy(md->tm);
+
+ return r;
+}
+
+static int save_sm_root(struct era_metadata *md)
+{
+ int r;
+ size_t metadata_len;
+
+ r = dm_sm_root_size(md->sm, &metadata_len);
+ if (r < 0)
+ return r;
+
+ return dm_sm_copy_root(md->sm, &md->metadata_space_map_root,
+ metadata_len);
+}
+
+static void copy_sm_root(struct era_metadata *md, struct superblock_disk *disk)
+{
+ memcpy(&disk->metadata_space_map_root,
+ &md->metadata_space_map_root,
+ sizeof(md->metadata_space_map_root));
+}
+
+/*
+ * Writes a superblock, including the static fields that don't get updated
+ * with every commit (possible optimisation here). 'md' should be fully
+ * constructed when this is called.
+ */
+static void prepare_superblock(struct era_metadata *md, struct superblock_disk *disk)
+{
+ disk->magic = cpu_to_le64(SUPERBLOCK_MAGIC);
+ disk->flags = cpu_to_le32(0ul);
+
+ /* FIXME: can't keep blanking the uuid (uuid is currently unused though) */
+ memset(disk->uuid, 0, sizeof(disk->uuid));
+ disk->version = cpu_to_le32(MAX_ERA_VERSION);
+
+ copy_sm_root(md, disk);
+
+ disk->data_block_size = cpu_to_le32(md->block_size);
+ disk->metadata_block_size = cpu_to_le32(DM_ERA_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
+ disk->nr_blocks = cpu_to_le32(md->nr_blocks);
+ disk->current_era = cpu_to_le32(md->current_era);
+
+ ws_pack(&md->current_writeset->md, &disk->current_writeset);
+ disk->writeset_tree_root = cpu_to_le64(md->writeset_tree_root);
+ disk->era_array_root = cpu_to_le64(md->era_array_root);
+ disk->metadata_snap = cpu_to_le64(md->metadata_snap);
+}
+
+static int write_superblock(struct era_metadata *md)
+{
+ int r;
+ struct dm_block *sblock;
+ struct superblock_disk *disk;
+
+ r = save_sm_root(md);
+ if (r) {
+ DMERR("%s: save_sm_root failed", __func__);
+ return r;
+ }
+
+ r = superblock_lock_zero(md, &sblock);
+ if (r)
+ return r;
+
+ disk = dm_block_data(sblock);
+ prepare_superblock(md, disk);
+
+ return dm_tm_commit(md->tm, sblock);
+}
+
+/*
+ * Assumes block_size and the infos are set.
+ */
+static int format_metadata(struct era_metadata *md)
+{
+ int r;
+
+ r = create_fresh_metadata(md);
+ if (r)
+ return r;
+
+ r = write_superblock(md);
+ if (r) {
+ dm_sm_destroy(md->sm);
+ dm_tm_destroy(md->tm);
+ return r;
+ }
+
+ return 0;
+}
+
+static int open_metadata(struct era_metadata *md)
+{
+ int r;
+ struct dm_block *sblock;
+ struct superblock_disk *disk;
+
+ r = superblock_read_lock(md, &sblock);
+ if (r) {
+ DMERR("couldn't read_lock superblock");
+ return r;
+ }
+
+ disk = dm_block_data(sblock);
+ r = dm_tm_open_with_sm(md->bm, SUPERBLOCK_LOCATION,
+ disk->metadata_space_map_root,
+ sizeof(disk->metadata_space_map_root),
+ &md->tm, &md->sm);
+ if (r) {
+ DMERR("dm_tm_open_with_sm failed");
+ goto bad;
+ }
+
+ setup_infos(md);
+
+ md->block_size = le32_to_cpu(disk->data_block_size);
+ md->nr_blocks = le32_to_cpu(disk->nr_blocks);
+ md->current_era = le32_to_cpu(disk->current_era);
+
+ md->writeset_tree_root = le64_to_cpu(disk->writeset_tree_root);
+ md->era_array_root = le64_to_cpu(disk->era_array_root);
+ md->metadata_snap = le64_to_cpu(disk->metadata_snap);
+ md->archived_writesets = true;
+
+ return dm_bm_unlock(sblock);
+
+bad:
+ dm_bm_unlock(sblock);
+ return r;
+}
+
+static int open_or_format_metadata(struct era_metadata *md,
+ bool may_format)
+{
+ int r;
+ bool unformatted = false;
+
+ r = superblock_all_zeroes(md->bm, &unformatted);
+ if (r)
+ return r;
+
+ if (unformatted)
+ return may_format ? format_metadata(md) : -EPERM;
+
+ return open_metadata(md);
+}
+
+static int create_persistent_data_objects(struct era_metadata *md,
+ bool may_format)
+{
+ int r;
+
+ md->bm = dm_block_manager_create(md->bdev, DM_ERA_METADATA_BLOCK_SIZE,
+ DM_ERA_METADATA_CACHE_SIZE,
+ ERA_MAX_CONCURRENT_LOCKS);
+ if (IS_ERR(md->bm)) {
+ DMERR("could not create block manager");
+ return PTR_ERR(md->bm);
+ }
+
+ r = open_or_format_metadata(md, may_format);
+ if (r)
+ dm_block_manager_destroy(md->bm);
+
+ return r;
+}
+
+static void destroy_persistent_data_objects(struct era_metadata *md)
+{
+ dm_sm_destroy(md->sm);
+ dm_tm_destroy(md->tm);
+ dm_block_manager_destroy(md->bm);
+}
+
+/*
+ * This waits until all era_map threads have picked up the new filter.
+ */
+static void swap_writeset(struct era_metadata *md, struct writeset *new_writeset)
+{
+ rcu_assign_pointer(md->current_writeset, new_writeset);
+ synchronize_rcu();
+}
+
+/*----------------------------------------------------------------
+ * Writesets get 'digested' into the main era array.
+ *
+ * We're using a coroutine here so the worker thread can do the digestion,
+ * thus avoiding synchronisation of the metadata. Digesting a whole
+ * writeset in one go would cause too much latency.
+ *--------------------------------------------------------------*/
+struct digest {
+ uint32_t era;
+ unsigned nr_bits, current_bit;
+ struct writeset_metadata writeset;
+ __le32 value;
+ struct dm_disk_bitset info;
+
+ int (*step)(struct era_metadata *, struct digest *);
+};
+
+static int metadata_digest_lookup_writeset(struct era_metadata *md,
+ struct digest *d);
+
+static int metadata_digest_remove_writeset(struct era_metadata *md,
+ struct digest *d)
+{
+ int r;
+ uint64_t key = d->era;
+
+ r = dm_btree_remove(&md->writeset_tree_info, md->writeset_tree_root,
+ &key, &md->writeset_tree_root);
+ if (r) {
+ DMERR("%s: dm_btree_remove failed", __func__);
+ return r;
+ }
+
+ d->step = metadata_digest_lookup_writeset;
+ return 0;
+}
+
+#define INSERTS_PER_STEP 100
+
+static int metadata_digest_transcribe_writeset(struct era_metadata *md,
+ struct digest *d)
+{
+ int r;
+ bool marked;
+ unsigned b, e = min(d->current_bit + INSERTS_PER_STEP, d->nr_bits);
+
+ for (b = d->current_bit; b < e; b++) {
+ r = writeset_marked_on_disk(&d->info, &d->writeset, b, &marked);
+ if (r) {
+ DMERR("%s: writeset_marked_on_disk failed", __func__);
+ return r;
+ }
+
+ if (!marked)
+ continue;
+
+ __dm_bless_for_disk(&d->value);
+ r = dm_array_set_value(&md->era_array_info, md->era_array_root,
+ b, &d->value, &md->era_array_root);
+ if (r) {
+ DMERR("%s: dm_array_set_value failed", __func__);
+ return r;
+ }
+ }
+
+ if (b == d->nr_bits)
+ d->step = metadata_digest_remove_writeset;
+ else
+ d->current_bit = b;
+
+ return 0;
+}
+
+static int metadata_digest_lookup_writeset(struct era_metadata *md,
+ struct digest *d)
+{
+ int r;
+ uint64_t key;
+ struct writeset_disk disk;
+
+ r = dm_btree_find_lowest_key(&md->writeset_tree_info,
+ md->writeset_tree_root, &key);
+ if (r < 0)
+ return r;
+
+ d->era = key;
+
+ r = dm_btree_lookup(&md->writeset_tree_info,
+ md->writeset_tree_root, &key, &disk);
+ if (r) {
+ if (r == -ENODATA) {
+ d->step = NULL;
+ return 0;
+ }
+
+ DMERR("%s: dm_btree_lookup failed", __func__);
+ return r;
+ }
+
+ ws_unpack(&disk, &d->writeset);
+ d->value = cpu_to_le32(key);
+
+ d->nr_bits = min(d->writeset.nr_bits, md->nr_blocks);
+ d->current_bit = 0;
+ d->step = metadata_digest_transcribe_writeset;
+
+ return 0;
+}
+
+static int metadata_digest_start(struct era_metadata *md, struct digest *d)
+{
+ if (d->step)
+ return 0;
+
+ memset(d, 0, sizeof(*d));
+
+ /*
+ * We initialise another bitset info to avoid any caching side
+ * effects with the previous one.
+ */
+ dm_disk_bitset_init(md->tm, &d->info);
+ d->step = metadata_digest_lookup_writeset;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+ * High level metadata interface. Target methods should use these, and not
+ * the lower level ones.
+ *--------------------------------------------------------------*/
+static struct era_metadata *metadata_open(struct block_device *bdev,
+ sector_t block_size,
+ bool may_format)
+{
+ int r;
+ struct era_metadata *md = kzalloc(sizeof(*md), GFP_KERNEL);
+
+ if (!md)
+ return NULL;
+
+ md->bdev = bdev;
+ md->block_size = block_size;
+
+ md->writesets[0].md.root = INVALID_WRITESET_ROOT;
+ md->writesets[1].md.root = INVALID_WRITESET_ROOT;
+ md->current_writeset = &md->writesets[0];
+
+ r = create_persistent_data_objects(md, may_format);
+ if (r) {
+ kfree(md);
+ return ERR_PTR(r);
+ }
+
+ return md;
+}
+
+static void metadata_close(struct era_metadata *md)
+{
+ destroy_persistent_data_objects(md);
+ kfree(md);
+}
+
+static bool valid_nr_blocks(dm_block_t n)
+{
+ /*
+ * dm_bitset restricts us to 2^32. test_bit & co. restrict us
+ * further to 2^31 - 1
+ */
+ return n < (1ull << 31);
+}
+
+static int metadata_resize(struct era_metadata *md, void *arg)
+{
+ int r;
+ dm_block_t *new_size = arg;
+ __le32 value;
+
+ if (!valid_nr_blocks(*new_size)) {
+ DMERR("Invalid number of origin blocks %llu",
+ (unsigned long long) *new_size);
+ return -EINVAL;
+ }
+
+ writeset_free(&md->writesets[0]);
+ writeset_free(&md->writesets[1]);
+
+ r = writeset_alloc(&md->writesets[0], *new_size);
+ if (r) {
+ DMERR("%s: writeset_alloc failed for writeset 0", __func__);
+ return r;
+ }
+
+ r = writeset_alloc(&md->writesets[1], *new_size);
+ if (r) {
+ DMERR("%s: writeset_alloc failed for writeset 1", __func__);
+ return r;
+ }
+
+ value = cpu_to_le32(0u);
+ __dm_bless_for_disk(&value);
+ r = dm_array_resize(&md->era_array_info, md->era_array_root,
+ md->nr_blocks, *new_size,
+ &value, &md->era_array_root);
+ if (r) {
+ DMERR("%s: dm_array_resize failed", __func__);
+ return r;
+ }
+
+ md->nr_blocks = *new_size;
+ return 0;
+}
+
+static int metadata_era_archive(struct era_metadata *md)
+{
+ int r;
+ uint64_t keys[1];
+ struct writeset_disk value;
+
+ r = dm_bitset_flush(&md->bitset_info, md->current_writeset->md.root,
+ &md->current_writeset->md.root);
+ if (r) {
+ DMERR("%s: dm_bitset_flush failed", __func__);
+ return r;
+ }
+
+ ws_pack(&md->current_writeset->md, &value);
+ md->current_writeset->md.root = INVALID_WRITESET_ROOT;
+
+ keys[0] = md->current_era;
+ __dm_bless_for_disk(&value);
+ r = dm_btree_insert(&md->writeset_tree_info, md->writeset_tree_root,
+ keys, &value, &md->writeset_tree_root);
+ if (r) {
+ DMERR("%s: couldn't insert writeset into btree", __func__);
+ /* FIXME: fail mode */
+ return r;
+ }
+
+ md->archived_writesets = true;
+
+ return 0;
+}
+
+static struct writeset *next_writeset(struct era_metadata *md)
+{
+ return (md->current_writeset == &md->writesets[0]) ?
+ &md->writesets[1] : &md->writesets[0];
+}
+
+static int metadata_new_era(struct era_metadata *md)
+{
+ int r;
+ struct writeset *new_writeset = next_writeset(md);
+
+ r = writeset_init(&md->bitset_info, new_writeset);
+ if (r) {
+ DMERR("%s: writeset_init failed", __func__);
+ return r;
+ }
+
+ swap_writeset(md, new_writeset);
+ md->current_era++;
+
+ return 0;
+}
+
+static int metadata_era_rollover(struct era_metadata *md)
+{
+ int r;
+
+ if (md->current_writeset->md.root != INVALID_WRITESET_ROOT) {
+ r = metadata_era_archive(md);
+ if (r) {
+ DMERR("%s: metadata_archive_era failed", __func__);
+ /* FIXME: fail mode? */
+ return r;
+ }
+ }
+
+ r = metadata_new_era(md);
+ if (r) {
+ DMERR("%s: new era failed", __func__);
+ /* FIXME: fail mode */
+ return r;
+ }
+
+ return 0;
+}
+
+static bool metadata_current_marked(struct era_metadata *md, dm_block_t block)
+{
+ bool r;
+ struct writeset *ws;
+
+ rcu_read_lock();
+ ws = rcu_dereference(md->current_writeset);
+ r = writeset_marked(ws, block);
+ rcu_read_unlock();
+
+ return r;
+}
+
+static int metadata_commit(struct era_metadata *md)
+{
+ int r;
+ struct dm_block *sblock;
+
+ if (md->current_writeset->md.root != SUPERBLOCK_LOCATION) {
+ r = dm_bitset_flush(&md->bitset_info, md->current_writeset->md.root,
+ &md->current_writeset->md.root);
+ if (r) {
+ DMERR("%s: bitset flush failed", __func__);
+ return r;
+ }
+ }
+
+ r = save_sm_root(md);
+ if (r) {
+ DMERR("%s: save_sm_root failed", __func__);
+ return r;
+ }
+
+ r = dm_tm_pre_commit(md->tm);
+ if (r) {
+ DMERR("%s: pre commit failed", __func__);
+ return r;
+ }
+
+ r = superblock_lock(md, &sblock);
+ if (r) {
+ DMERR("%s: superblock lock failed", __func__);
+ return r;
+ }
+
+ prepare_superblock(md, dm_block_data(sblock));
+
+ return dm_tm_commit(md->tm, sblock);
+}
+
+static int metadata_checkpoint(struct era_metadata *md)
+{
+ /*
+ * For now we just rollover, but later I want to put a check in to
+ * avoid this if the filter is still pretty fresh.
+ */
+ return metadata_era_rollover(md);
+}
+
+/*
+ * Metadata snapshots allow userland to access era data.
+ */
+static int metadata_take_snap(struct era_metadata *md)
+{
+ int r, inc;
+ struct dm_block *clone;
+
+ if (md->metadata_snap != SUPERBLOCK_LOCATION) {
+ DMERR("%s: metadata snapshot already exists", __func__);
+ return -EINVAL;
+ }
+
+ r = metadata_era_rollover(md);
+ if (r) {
+ DMERR("%s: era rollover failed", __func__);
+ return r;
+ }
+
+ r = metadata_commit(md);
+ if (r) {
+ DMERR("%s: pre commit failed", __func__);
+ return r;
+ }
+
+ r = dm_sm_inc_block(md->sm, SUPERBLOCK_LOCATION);
+ if (r) {
+ DMERR("%s: couldn't increment superblock", __func__);
+ return r;
+ }
+
+ r = dm_tm_shadow_block(md->tm, SUPERBLOCK_LOCATION,
+ &sb_validator, &clone, &inc);
+ if (r) {
+ DMERR("%s: couldn't shadow superblock", __func__);
+ dm_sm_dec_block(md->sm, SUPERBLOCK_LOCATION);
+ return r;
+ }
+ BUG_ON(!inc);
+
+ r = dm_sm_inc_block(md->sm, md->writeset_tree_root);
+ if (r) {
+ DMERR("%s: couldn't inc writeset tree root", __func__);
+ dm_tm_unlock(md->tm, clone);
+ return r;
+ }
+
+ r = dm_sm_inc_block(md->sm, md->era_array_root);
+ if (r) {
+ DMERR("%s: couldn't inc era tree root", __func__);
+ dm_sm_dec_block(md->sm, md->writeset_tree_root);
+ dm_tm_unlock(md->tm, clone);
+ return r;
+ }
+
+ md->metadata_snap = dm_block_location(clone);
+
+ r = dm_tm_unlock(md->tm, clone);
+ if (r) {
+ DMERR("%s: couldn't unlock clone", __func__);
+ md->metadata_snap = SUPERBLOCK_LOCATION;
+ return r;
+ }
+
+ return 0;
+}
+
+static int metadata_drop_snap(struct era_metadata *md)
+{
+ int r;
+ dm_block_t location;
+ struct dm_block *clone;
+ struct superblock_disk *disk;
+
+ if (md->metadata_snap == SUPERBLOCK_LOCATION) {
+ DMERR("%s: no snap to drop", __func__);
+ return -EINVAL;
+ }
+
+ r = dm_tm_read_lock(md->tm, md->metadata_snap, &sb_validator, &clone);
+ if (r) {
+ DMERR("%s: couldn't read lock superblock clone", __func__);
+ return r;
+ }
+
+ /*
+ * Whatever happens now we'll commit with no record of the metadata
+ * snap.
+ */
+ md->metadata_snap = SUPERBLOCK_LOCATION;
+
+ disk = dm_block_data(clone);
+ r = dm_btree_del(&md->writeset_tree_info,
+ le64_to_cpu(disk->writeset_tree_root));
+ if (r) {
+ DMERR("%s: error deleting writeset tree clone", __func__);
+ dm_tm_unlock(md->tm, clone);
+ return r;
+ }
+
+ r = dm_array_del(&md->era_array_info, le64_to_cpu(disk->era_array_root));
+ if (r) {
+ DMERR("%s: error deleting era array clone", __func__);
+ dm_tm_unlock(md->tm, clone);
+ return r;
+ }
+
+ location = dm_block_location(clone);
+ dm_tm_unlock(md->tm, clone);
+
+ return dm_sm_dec_block(md->sm, location);
+}
+
+struct metadata_stats {
+ dm_block_t used;
+ dm_block_t total;
+ dm_block_t snap;
+ uint32_t era;
+};
+
+static int metadata_get_stats(struct era_metadata *md, void *ptr)
+{
+ int r;
+ struct metadata_stats *s = ptr;
+ dm_block_t nr_free, nr_total;
+
+ r = dm_sm_get_nr_free(md->sm, &nr_free);
+ if (r) {
+ DMERR("dm_sm_get_nr_free returned %d", r);
+ return r;
+ }
+
+ r = dm_sm_get_nr_blocks(md->sm, &nr_total);
+ if (r) {
+ DMERR("dm_pool_get_metadata_dev_size returned %d", r);
+ return r;
+ }
+
+ s->used = nr_total - nr_free;
+ s->total = nr_total;
+ s->snap = md->metadata_snap;
+ s->era = md->current_era;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+
+struct era {
+ struct dm_target *ti;
+ struct dm_target_callbacks callbacks;
+
+ struct dm_dev *metadata_dev;
+ struct dm_dev *origin_dev;
+
+ dm_block_t nr_blocks;
+ uint32_t sectors_per_block;
+ int sectors_per_block_shift;
+ struct era_metadata *md;
+
+ struct workqueue_struct *wq;
+ struct work_struct worker;
+
+ spinlock_t deferred_lock;
+ struct bio_list deferred_bios;
+
+ spinlock_t rpc_lock;
+ struct list_head rpc_calls;
+
+ struct digest digest;
+ atomic_t suspended;
+};
+
+struct rpc {
+ struct list_head list;
+
+ int (*fn0)(struct era_metadata *);
+ int (*fn1)(struct era_metadata *, void *);
+ void *arg;
+ int result;
+
+ struct completion complete;
+};
+
+/*----------------------------------------------------------------
+ * Remapping.
+ *---------------------------------------------------------------*/
+static bool block_size_is_power_of_two(struct era *era)
+{
+ return era->sectors_per_block_shift >= 0;
+}
+
+static dm_block_t get_block(struct era *era, struct bio *bio)
+{
+ sector_t block_nr = bio->bi_iter.bi_sector;
+
+ if (!block_size_is_power_of_two(era))
+ (void) sector_div(block_nr, era->sectors_per_block);
+ else
+ block_nr >>= era->sectors_per_block_shift;
+
+ return block_nr;
+}
+
+static void remap_to_origin(struct era *era, struct bio *bio)
+{
+ bio->bi_bdev = era->origin_dev->bdev;
+}
+
+/*----------------------------------------------------------------
+ * Worker thread
+ *--------------------------------------------------------------*/
+static void wake_worker(struct era *era)
+{
+ if (!atomic_read(&era->suspended))
+ queue_work(era->wq, &era->worker);
+}
+
+static void process_old_eras(struct era *era)
+{
+ int r;
+
+ if (!era->digest.step)
+ return;
+
+ r = era->digest.step(era->md, &era->digest);
+ if (r < 0) {
+ DMERR("%s: digest step failed, stopping digestion", __func__);
+ era->digest.step = NULL;
+
+ } else if (era->digest.step)
+ wake_worker(era);
+}
+
+static void process_deferred_bios(struct era *era)
+{
+ int r;
+ struct bio_list deferred_bios, marked_bios;
+ struct bio *bio;
+ bool commit_needed = false;
+ bool failed = false;
+
+ bio_list_init(&deferred_bios);
+ bio_list_init(&marked_bios);
+
+ spin_lock(&era->deferred_lock);
+ bio_list_merge(&deferred_bios, &era->deferred_bios);
+ bio_list_init(&era->deferred_bios);
+ spin_unlock(&era->deferred_lock);
+
+ while ((bio = bio_list_pop(&deferred_bios))) {
+ r = writeset_test_and_set(&era->md->bitset_info,
+ era->md->current_writeset,
+ get_block(era, bio));
+ if (r < 0) {
+ /*
+ * This is bad news, we need to rollback.
+ * FIXME: finish.
+ */
+ failed = true;
+
+ } else if (r == 0)
+ commit_needed = true;
+
+ bio_list_add(&marked_bios, bio);
+ }
+
+ if (commit_needed) {
+ r = metadata_commit(era->md);
+ if (r)
+ failed = true;
+ }
+
+ if (failed)
+ while ((bio = bio_list_pop(&marked_bios)))
+ bio_io_error(bio);
+ else
+ while ((bio = bio_list_pop(&marked_bios)))
+ generic_make_request(bio);
+}
+
+static void process_rpc_calls(struct era *era)
+{
+ int r;
+ bool need_commit = false;
+ struct list_head calls;
+ struct rpc *rpc, *tmp;
+
+ INIT_LIST_HEAD(&calls);
+ spin_lock(&era->rpc_lock);
+ list_splice_init(&era->rpc_calls, &calls);
+ spin_unlock(&era->rpc_lock);
+
+ list_for_each_entry_safe(rpc, tmp, &calls, list) {
+ rpc->result = rpc->fn0 ? rpc->fn0(era->md) : rpc->fn1(era->md, rpc->arg);
+ need_commit = true;
+ }
+
+ if (need_commit) {
+ r = metadata_commit(era->md);
+ if (r)
+ list_for_each_entry_safe(rpc, tmp, &calls, list)
+ rpc->result = r;
+ }
+
+ list_for_each_entry_safe(rpc, tmp, &calls, list)
+ complete(&rpc->complete);
+}
+
+static void kick_off_digest(struct era *era)
+{
+ if (era->md->archived_writesets) {
+ era->md->archived_writesets = false;
+ metadata_digest_start(era->md, &era->digest);
+ }
+}
+
+static void do_work(struct work_struct *ws)
+{
+ struct era *era = container_of(ws, struct era, worker);
+
+ kick_off_digest(era);
+ process_old_eras(era);
+ process_deferred_bios(era);
+ process_rpc_calls(era);
+}
+
+static void defer_bio(struct era *era, struct bio *bio)
+{
+ spin_lock(&era->deferred_lock);
+ bio_list_add(&era->deferred_bios, bio);
+ spin_unlock(&era->deferred_lock);
+
+ wake_worker(era);
+}
+
+/*
+ * Make an rpc call to the worker to change the metadata.
+ */
+static int perform_rpc(struct era *era, struct rpc *rpc)
+{
+ rpc->result = 0;
+ init_completion(&rpc->complete);
+
+ spin_lock(&era->rpc_lock);
+ list_add(&rpc->list, &era->rpc_calls);
+ spin_unlock(&era->rpc_lock);
+
+ wake_worker(era);
+ wait_for_completion(&rpc->complete);
+
+ return rpc->result;
+}
+
+static int in_worker0(struct era *era, int (*fn)(struct era_metadata *))
+{
+ struct rpc rpc;
+ rpc.fn0 = fn;
+ rpc.fn1 = NULL;
+
+ return perform_rpc(era, &rpc);
+}
+
+static int in_worker1(struct era *era,
+ int (*fn)(struct era_metadata *, void *), void *arg)
+{
+ struct rpc rpc;
+ rpc.fn0 = NULL;
+ rpc.fn1 = fn;
+ rpc.arg = arg;
+
+ return perform_rpc(era, &rpc);
+}
+
+static void start_worker(struct era *era)
+{
+ atomic_set(&era->suspended, 0);
+}
+
+static void stop_worker(struct era *era)
+{
+ atomic_set(&era->suspended, 1);
+ flush_workqueue(era->wq);
+}
+
+/*----------------------------------------------------------------
+ * Target methods
+ *--------------------------------------------------------------*/
+static int dev_is_congested(struct dm_dev *dev, int bdi_bits)
+{
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+ return bdi_congested(&q->backing_dev_info, bdi_bits);
+}
+
+static int era_is_congested(struct dm_target_callbacks *cb, int bdi_bits)
+{
+ struct era *era = container_of(cb, struct era, callbacks);
+ return dev_is_congested(era->origin_dev, bdi_bits);
+}
+
+static void era_destroy(struct era *era)
+{
+ metadata_close(era->md);
+
+ if (era->wq)
+ destroy_workqueue(era->wq);
+
+ if (era->origin_dev)
+ dm_put_device(era->ti, era->origin_dev);
+
+ if (era->metadata_dev)
+ dm_put_device(era->ti, era->metadata_dev);
+
+ kfree(era);
+}
+
+static dm_block_t calc_nr_blocks(struct era *era)
+{
+ return dm_sector_div_up(era->ti->len, era->sectors_per_block);
+}
+
+static bool valid_block_size(dm_block_t block_size)
+{
+ bool greater_than_zero = block_size > 0;
+ bool multiple_of_min_block_size = (block_size & (MIN_BLOCK_SIZE - 1)) == 0;
+
+ return greater_than_zero && multiple_of_min_block_size;
+}
+
+/*
+ * <metadata dev> <data dev> <data block size (sectors)>
+ */
+static int era_ctr(struct dm_target *ti, unsigned argc, char **argv)
+{
+ int r;
+ char dummy;
+ struct era *era;
+ struct era_metadata *md;
+
+ if (argc != 3) {
+ ti->error = "Invalid argument count";
+ return -EINVAL;
+ }
+
+ era = kzalloc(sizeof(*era), GFP_KERNEL);
+ if (!era) {
+ ti->error = "Error allocating era structure";
+ return -ENOMEM;
+ }
+
+ era->ti = ti;
+
+ r = dm_get_device(ti, argv[0], FMODE_READ | FMODE_WRITE, &era->metadata_dev);
+ if (r) {
+ ti->error = "Error opening metadata device";
+ era_destroy(era);
+ return -EINVAL;
+ }
+
+ r = dm_get_device(ti, argv[1], FMODE_READ | FMODE_WRITE, &era->origin_dev);
+ if (r) {
+ ti->error = "Error opening data device";
+ era_destroy(era);
+ return -EINVAL;
+ }
+
+ r = sscanf(argv[2], "%u%c", &era->sectors_per_block, &dummy);
+ if (r != 1) {
+ ti->error = "Error parsing block size";
+ era_destroy(era);
+ return -EINVAL;
+ }
+
+ r = dm_set_target_max_io_len(ti, era->sectors_per_block);
+ if (r) {
+ ti->error = "could not set max io len";
+ era_destroy(era);
+ return -EINVAL;
+ }
+
+ if (!valid_block_size(era->sectors_per_block)) {
+ ti->error = "Invalid block size";
+ era_destroy(era);
+ return -EINVAL;
+ }
+ if (era->sectors_per_block & (era->sectors_per_block - 1))
+ era->sectors_per_block_shift = -1;
+ else
+ era->sectors_per_block_shift = __ffs(era->sectors_per_block);
+
+ md = metadata_open(era->metadata_dev->bdev, era->sectors_per_block, true);
+ if (IS_ERR(md)) {
+ ti->error = "Error reading metadata";
+ era_destroy(era);
+ return PTR_ERR(md);
+ }
+ era->md = md;
+
+ era->nr_blocks = calc_nr_blocks(era);
+
+ r = metadata_resize(era->md, &era->nr_blocks);
+ if (r) {
+ ti->error = "couldn't resize metadata";
+ era_destroy(era);
+ return -ENOMEM;
+ }
+
+ era->wq = alloc_ordered_workqueue("dm-" DM_MSG_PREFIX, WQ_MEM_RECLAIM);
+ if (!era->wq) {
+ ti->error = "could not create workqueue for metadata object";
+ era_destroy(era);
+ return -ENOMEM;
+ }
+ INIT_WORK(&era->worker, do_work);
+
+ spin_lock_init(&era->deferred_lock);
+ bio_list_init(&era->deferred_bios);
+
+ spin_lock_init(&era->rpc_lock);
+ INIT_LIST_HEAD(&era->rpc_calls);
+
+ ti->private = era;
+ ti->num_flush_bios = 1;
+ ti->flush_supported = true;
+
+ ti->num_discard_bios = 1;
+ ti->discards_supported = true;
+ era->callbacks.congested_fn = era_is_congested;
+ dm_table_add_target_callbacks(ti->table, &era->callbacks);
+
+ return 0;
+}
+
+static void era_dtr(struct dm_target *ti)
+{
+ era_destroy(ti->private);
+}
+
+static int era_map(struct dm_target *ti, struct bio *bio)
+{
+ struct era *era = ti->private;
+ dm_block_t block = get_block(era, bio);
+
+ /*
+ * All bios get remapped to the origin device. We do this now, but
+ * it may not get issued until later. Depending on whether the
+ * block is marked in this era.
+ */
+ remap_to_origin(era, bio);
+
+ /*
+ * REQ_FLUSH bios carry no data, so we're not interested in them.
+ */
+ if (!(bio->bi_rw & REQ_FLUSH) &&
+ (bio_data_dir(bio) == WRITE) &&
+ !metadata_current_marked(era->md, block)) {
+ defer_bio(era, bio);
+ return DM_MAPIO_SUBMITTED;
+ }
+
+ return DM_MAPIO_REMAPPED;
+}
+
+static void era_postsuspend(struct dm_target *ti)
+{
+ int r;
+ struct era *era = ti->private;
+
+ r = in_worker0(era, metadata_era_archive);
+ if (r) {
+ DMERR("%s: couldn't archive current era", __func__);
+ /* FIXME: fail mode */
+ }
+
+ stop_worker(era);
+}
+
+static int era_preresume(struct dm_target *ti)
+{
+ int r;
+ struct era *era = ti->private;
+ dm_block_t new_size = calc_nr_blocks(era);
+
+ if (era->nr_blocks != new_size) {
+ r = in_worker1(era, metadata_resize, &new_size);
+ if (r)
+ return r;
+
+ era->nr_blocks = new_size;
+ }
+
+ start_worker(era);
+
+ r = in_worker0(era, metadata_new_era);
+ if (r) {
+ DMERR("%s: metadata_era_rollover failed", __func__);
+ return r;
+ }
+
+ return 0;
+}
+
+/*
+ * Status format:
+ *
+ * <metadata block size> <#used metadata blocks>/<#total metadata blocks>
+ * <current era> <held metadata root | '-'>
+ */
+static void era_status(struct dm_target *ti, status_type_t type,
+ unsigned status_flags, char *result, unsigned maxlen)
+{
+ int r;
+ struct era *era = ti->private;
+ ssize_t sz = 0;
+ struct metadata_stats stats;
+ char buf[BDEVNAME_SIZE];
+
+ switch (type) {
+ case STATUSTYPE_INFO:
+ r = in_worker1(era, metadata_get_stats, &stats);
+ if (r)
+ goto err;
+
+ DMEMIT("%u %llu/%llu %u",
+ (unsigned) (DM_ERA_METADATA_BLOCK_SIZE >> SECTOR_SHIFT),
+ (unsigned long long) stats.used,
+ (unsigned long long) stats.total,
+ (unsigned) stats.era);
+
+ if (stats.snap != SUPERBLOCK_LOCATION)
+ DMEMIT(" %llu", stats.snap);
+ else
+ DMEMIT(" -");
+ break;
+
+ case STATUSTYPE_TABLE:
+ format_dev_t(buf, era->metadata_dev->bdev->bd_dev);
+ DMEMIT("%s ", buf);
+ format_dev_t(buf, era->origin_dev->bdev->bd_dev);
+ DMEMIT("%s %u", buf, era->sectors_per_block);
+ break;
+ }
+
+ return;
+
+err:
+ DMEMIT("Error");
+}
+
+static int era_message(struct dm_target *ti, unsigned argc, char **argv)
+{
+ struct era *era = ti->private;
+
+ if (argc != 1) {
+ DMERR("incorrect number of message arguments");
+ return -EINVAL;
+ }
+
+ if (!strcasecmp(argv[0], "checkpoint"))
+ return in_worker0(era, metadata_checkpoint);
+
+ if (!strcasecmp(argv[0], "take_metadata_snap"))
+ return in_worker0(era, metadata_take_snap);
+
+ if (!strcasecmp(argv[0], "drop_metadata_snap"))
+ return in_worker0(era, metadata_drop_snap);
+
+ DMERR("unsupported message '%s'", argv[0]);
+ return -EINVAL;
+}
+
+static sector_t get_dev_size(struct dm_dev *dev)
+{
+ return i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT;
+}
+
+static int era_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data)
+{
+ struct era *era = ti->private;
+ return fn(ti, era->origin_dev, 0, get_dev_size(era->origin_dev), data);
+}
+
+static int era_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
+ struct bio_vec *biovec, int max_size)
+{
+ struct era *era = ti->private;
+ struct request_queue *q = bdev_get_queue(era->origin_dev->bdev);
+
+ if (!q->merge_bvec_fn)
+ return max_size;
+
+ bvm->bi_bdev = era->origin_dev->bdev;
+
+ return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
+}
+
+static void era_io_hints(struct dm_target *ti, struct queue_limits *limits)
+{
+ struct era *era = ti->private;
+ uint64_t io_opt_sectors = limits->io_opt >> SECTOR_SHIFT;
+
+ /*
+ * If the system-determined stacked limits are compatible with the
+ * era device's blocksize (io_opt is a factor) do not override them.
+ */
+ if (io_opt_sectors < era->sectors_per_block ||
+ do_div(io_opt_sectors, era->sectors_per_block)) {
+ blk_limits_io_min(limits, 0);
+ blk_limits_io_opt(limits, era->sectors_per_block << SECTOR_SHIFT);
+ }
+}
+
+/*----------------------------------------------------------------*/
+
+static struct target_type era_target = {
+ .name = "era",
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = era_ctr,
+ .dtr = era_dtr,
+ .map = era_map,
+ .postsuspend = era_postsuspend,
+ .preresume = era_preresume,
+ .status = era_status,
+ .message = era_message,
+ .iterate_devices = era_iterate_devices,
+ .merge = era_merge,
+ .io_hints = era_io_hints
+};
+
+static int __init dm_era_init(void)
+{
+ int r;
+
+ r = dm_register_target(&era_target);
+ if (r) {
+ DMERR("era target registration failed: %d", r);
+ return r;
+ }
+
+ return 0;
+}
+
+static void __exit dm_era_exit(void)
+{
+ dm_unregister_target(&era_target);
+}
+
+module_init(dm_era_init);
+module_exit(dm_era_exit);
+
+MODULE_DESCRIPTION(DM_NAME " era target");
+MODULE_AUTHOR("Joe Thornber <ejt@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 422a9fdeb53e..aa009e865871 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -93,10 +93,6 @@ struct multipath {
unsigned pg_init_count; /* Number of times pg_init called */
unsigned pg_init_delay_msecs; /* Number of msecs before pg_init retry */
- unsigned queue_size;
- struct work_struct process_queued_ios;
- struct list_head queued_ios;
-
struct work_struct trigger_event;
/*
@@ -121,9 +117,9 @@ typedef int (*action_fn) (struct pgpath *pgpath);
static struct kmem_cache *_mpio_cache;
static struct workqueue_struct *kmultipathd, *kmpath_handlerd;
-static void process_queued_ios(struct work_struct *work);
static void trigger_event(struct work_struct *work);
static void activate_path(struct work_struct *work);
+static int __pgpath_busy(struct pgpath *pgpath);
/*-----------------------------------------------
@@ -195,11 +191,9 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
m = kzalloc(sizeof(*m), GFP_KERNEL);
if (m) {
INIT_LIST_HEAD(&m->priority_groups);
- INIT_LIST_HEAD(&m->queued_ios);
spin_lock_init(&m->lock);
m->queue_io = 1;
m->pg_init_delay_msecs = DM_PG_INIT_DELAY_DEFAULT;
- INIT_WORK(&m->process_queued_ios, process_queued_ios);
INIT_WORK(&m->trigger_event, trigger_event);
init_waitqueue_head(&m->pg_init_wait);
mutex_init(&m->work_mutex);
@@ -256,13 +250,21 @@ static void clear_mapinfo(struct multipath *m, union map_info *info)
* Path selection
*-----------------------------------------------*/
-static void __pg_init_all_paths(struct multipath *m)
+static int __pg_init_all_paths(struct multipath *m)
{
struct pgpath *pgpath;
unsigned long pg_init_delay = 0;
+ if (m->pg_init_in_progress || m->pg_init_disabled)
+ return 0;
+
m->pg_init_count++;
m->pg_init_required = 0;
+
+ /* Check here to reset pg_init_required */
+ if (!m->current_pg)
+ return 0;
+
if (m->pg_init_delay_retry)
pg_init_delay = msecs_to_jiffies(m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT ?
m->pg_init_delay_msecs : DM_PG_INIT_DELAY_MSECS);
@@ -274,6 +276,7 @@ static void __pg_init_all_paths(struct multipath *m)
pg_init_delay))
m->pg_init_in_progress++;
}
+ return m->pg_init_in_progress;
}
static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
@@ -365,19 +368,26 @@ failed:
*/
static int __must_push_back(struct multipath *m)
{
- return (m->queue_if_no_path != m->saved_queue_if_no_path &&
- dm_noflush_suspending(m->ti));
+ return (m->queue_if_no_path ||
+ (m->queue_if_no_path != m->saved_queue_if_no_path &&
+ dm_noflush_suspending(m->ti)));
}
-static int map_io(struct multipath *m, struct request *clone,
- union map_info *map_context, unsigned was_queued)
+#define pg_ready(m) (!(m)->queue_io && !(m)->pg_init_required)
+
+/*
+ * Map cloned requests
+ */
+static int multipath_map(struct dm_target *ti, struct request *clone,
+ union map_info *map_context)
{
- int r = DM_MAPIO_REMAPPED;
+ struct multipath *m = (struct multipath *) ti->private;
+ int r = DM_MAPIO_REQUEUE;
size_t nr_bytes = blk_rq_bytes(clone);
unsigned long flags;
struct pgpath *pgpath;
struct block_device *bdev;
- struct dm_mpath_io *mpio = map_context->ptr;
+ struct dm_mpath_io *mpio;
spin_lock_irqsave(&m->lock, flags);
@@ -388,38 +398,33 @@ static int map_io(struct multipath *m, struct request *clone,
pgpath = m->current_pgpath;
- if (was_queued)
- m->queue_size--;
-
- if (m->pg_init_required) {
- if (!m->pg_init_in_progress)
- queue_work(kmultipathd, &m->process_queued_ios);
- r = DM_MAPIO_REQUEUE;
- } else if ((pgpath && m->queue_io) ||
- (!pgpath && m->queue_if_no_path)) {
- /* Queue for the daemon to resubmit */
- list_add_tail(&clone->queuelist, &m->queued_ios);
- m->queue_size++;
- if (!m->queue_io)
- queue_work(kmultipathd, &m->process_queued_ios);
- pgpath = NULL;
- r = DM_MAPIO_SUBMITTED;
- } else if (pgpath) {
- bdev = pgpath->path.dev->bdev;
- clone->q = bdev_get_queue(bdev);
- clone->rq_disk = bdev->bd_disk;
- } else if (__must_push_back(m))
- r = DM_MAPIO_REQUEUE;
- else
- r = -EIO; /* Failed */
+ if (!pgpath) {
+ if (!__must_push_back(m))
+ r = -EIO; /* Failed */
+ goto out_unlock;
+ }
+ if (!pg_ready(m)) {
+ __pg_init_all_paths(m);
+ goto out_unlock;
+ }
+ if (set_mapinfo(m, map_context) < 0)
+ /* ENOMEM, requeue */
+ goto out_unlock;
+ bdev = pgpath->path.dev->bdev;
+ clone->q = bdev_get_queue(bdev);
+ clone->rq_disk = bdev->bd_disk;
+ clone->cmd_flags |= REQ_FAILFAST_TRANSPORT;
+ mpio = map_context->ptr;
mpio->pgpath = pgpath;
mpio->nr_bytes = nr_bytes;
-
- if (r == DM_MAPIO_REMAPPED && pgpath->pg->ps.type->start_io)
- pgpath->pg->ps.type->start_io(&pgpath->pg->ps, &pgpath->path,
+ if (pgpath->pg->ps.type->start_io)
+ pgpath->pg->ps.type->start_io(&pgpath->pg->ps,
+ &pgpath->path,
nr_bytes);
+ r = DM_MAPIO_REMAPPED;
+out_unlock:
spin_unlock_irqrestore(&m->lock, flags);
return r;
@@ -440,76 +445,14 @@ static int queue_if_no_path(struct multipath *m, unsigned queue_if_no_path,
else
m->saved_queue_if_no_path = queue_if_no_path;
m->queue_if_no_path = queue_if_no_path;
- if (!m->queue_if_no_path && m->queue_size)
- queue_work(kmultipathd, &m->process_queued_ios);
+ if (!m->queue_if_no_path)
+ dm_table_run_md_queue_async(m->ti->table);
spin_unlock_irqrestore(&m->lock, flags);
return 0;
}
-/*-----------------------------------------------------------------
- * The multipath daemon is responsible for resubmitting queued ios.
- *---------------------------------------------------------------*/
-
-static void dispatch_queued_ios(struct multipath *m)
-{
- int r;
- unsigned long flags;
- union map_info *info;
- struct request *clone, *n;
- LIST_HEAD(cl);
-
- spin_lock_irqsave(&m->lock, flags);
- list_splice_init(&m->queued_ios, &cl);
- spin_unlock_irqrestore(&m->lock, flags);
-
- list_for_each_entry_safe(clone, n, &cl, queuelist) {
- list_del_init(&clone->queuelist);
-
- info = dm_get_rq_mapinfo(clone);
-
- r = map_io(m, clone, info, 1);
- if (r < 0) {
- clear_mapinfo(m, info);
- dm_kill_unmapped_request(clone, r);
- } else if (r == DM_MAPIO_REMAPPED)
- dm_dispatch_request(clone);
- else if (r == DM_MAPIO_REQUEUE) {
- clear_mapinfo(m, info);
- dm_requeue_unmapped_request(clone);
- }
- }
-}
-
-static void process_queued_ios(struct work_struct *work)
-{
- struct multipath *m =
- container_of(work, struct multipath, process_queued_ios);
- struct pgpath *pgpath = NULL;
- unsigned must_queue = 1;
- unsigned long flags;
-
- spin_lock_irqsave(&m->lock, flags);
-
- if (!m->current_pgpath)
- __choose_pgpath(m, 0);
-
- pgpath = m->current_pgpath;
-
- if ((pgpath && !m->queue_io) ||
- (!pgpath && !m->queue_if_no_path))
- must_queue = 0;
-
- if (m->pg_init_required && !m->pg_init_in_progress && pgpath &&
- !m->pg_init_disabled)
- __pg_init_all_paths(m);
-
- spin_unlock_irqrestore(&m->lock, flags);
- if (!must_queue)
- dispatch_queued_ios(m);
-}
-
/*
* An event is triggered whenever a path is taken out of use.
* Includes path failure and PG bypass.
@@ -972,27 +915,6 @@ static void multipath_dtr(struct dm_target *ti)
}
/*
- * Map cloned requests
- */
-static int multipath_map(struct dm_target *ti, struct request *clone,
- union map_info *map_context)
-{
- int r;
- struct multipath *m = (struct multipath *) ti->private;
-
- if (set_mapinfo(m, map_context) < 0)
- /* ENOMEM, requeue */
- return DM_MAPIO_REQUEUE;
-
- clone->cmd_flags |= REQ_FAILFAST_TRANSPORT;
- r = map_io(m, clone, map_context, 0);
- if (r < 0 || r == DM_MAPIO_REQUEUE)
- clear_mapinfo(m, map_context);
-
- return r;
-}
-
-/*
* Take a path out of use.
*/
static int fail_path(struct pgpath *pgpath)
@@ -1054,9 +976,9 @@ static int reinstate_path(struct pgpath *pgpath)
pgpath->is_active = 1;
- if (!m->nr_valid_paths++ && m->queue_size) {
+ if (!m->nr_valid_paths++) {
m->current_pgpath = NULL;
- queue_work(kmultipathd, &m->process_queued_ios);
+ dm_table_run_md_queue_async(m->ti->table);
} else if (m->hw_handler_name && (m->current_pg == pgpath->pg)) {
if (queue_work(kmpath_handlerd, &pgpath->activate_path.work))
m->pg_init_in_progress++;
@@ -1252,11 +1174,12 @@ static void pg_init_done(void *data, int errors)
/* Activations of other paths are still on going */
goto out;
- if (!m->pg_init_required)
- m->queue_io = 0;
-
- m->pg_init_delay_retry = delay_retry;
- queue_work(kmultipathd, &m->process_queued_ios);
+ if (m->pg_init_required) {
+ m->pg_init_delay_retry = delay_retry;
+ if (__pg_init_all_paths(m))
+ goto out;
+ }
+ m->queue_io = 0;
/*
* Wake up any thread waiting to suspend.
@@ -1272,8 +1195,11 @@ static void activate_path(struct work_struct *work)
struct pgpath *pgpath =
container_of(work, struct pgpath, activate_path.work);
- scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
- pg_init_done, pgpath);
+ if (pgpath->is_active)
+ scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
+ pg_init_done, pgpath);
+ else
+ pg_init_done(pgpath, SCSI_DH_DEV_OFFLINED);
}
static int noretry_error(int error)
@@ -1433,7 +1359,7 @@ static void multipath_status(struct dm_target *ti, status_type_t type,
/* Features */
if (type == STATUSTYPE_INFO)
- DMEMIT("2 %u %u ", m->queue_size, m->pg_init_count);
+ DMEMIT("2 %u %u ", m->queue_io, m->pg_init_count);
else {
DMEMIT("%u ", m->queue_if_no_path +
(m->pg_init_retries > 0) * 2 +
@@ -1552,7 +1478,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
}
if (argc != 2) {
- DMWARN("Unrecognised multipath message received.");
+ DMWARN("Invalid multipath message arguments. Expected 2 arguments, got %d.", argc);
goto out;
}
@@ -1570,7 +1496,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
else if (!strcasecmp(argv[0], "fail_path"))
action = fail_path;
else {
- DMWARN("Unrecognised multipath message received.");
+ DMWARN("Unrecognised multipath message received: %s", argv[0]);
goto out;
}
@@ -1632,8 +1558,17 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
r = err;
}
- if (r == -ENOTCONN && !fatal_signal_pending(current))
- queue_work(kmultipathd, &m->process_queued_ios);
+ if (r == -ENOTCONN && !fatal_signal_pending(current)) {
+ spin_lock_irqsave(&m->lock, flags);
+ if (!m->current_pg) {
+ /* Path status changed, redo selection */
+ __choose_pgpath(m, 0);
+ }
+ if (m->pg_init_required)
+ __pg_init_all_paths(m);
+ spin_unlock_irqrestore(&m->lock, flags);
+ dm_table_run_md_queue_async(m->ti->table);
+ }
return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
}
@@ -1684,7 +1619,7 @@ static int multipath_busy(struct dm_target *ti)
spin_lock_irqsave(&m->lock, flags);
/* pg_init in progress, requeue until done */
- if (m->pg_init_in_progress) {
+ if (!pg_ready(m)) {
busy = 1;
goto out;
}
@@ -1737,7 +1672,7 @@ out:
*---------------------------------------------------------------*/
static struct target_type multipath_target = {
.name = "multipath",
- .version = {1, 6, 0},
+ .version = {1, 7, 0},
.module = THIS_MODULE,
.ctr = multipath_ctr,
.dtr = multipath_dtr,
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 6a7f2b83a126..50601ec7017a 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -945,7 +945,7 @@ bool dm_table_request_based(struct dm_table *t)
return dm_table_get_type(t) == DM_TYPE_REQUEST_BASED;
}
-int dm_table_alloc_md_mempools(struct dm_table *t)
+static int dm_table_alloc_md_mempools(struct dm_table *t)
{
unsigned type = dm_table_get_type(t);
unsigned per_bio_data_size = 0;
@@ -1618,6 +1618,25 @@ struct mapped_device *dm_table_get_md(struct dm_table *t)
}
EXPORT_SYMBOL(dm_table_get_md);
+void dm_table_run_md_queue_async(struct dm_table *t)
+{
+ struct mapped_device *md;
+ struct request_queue *queue;
+ unsigned long flags;
+
+ if (!dm_table_request_based(t))
+ return;
+
+ md = dm_table_get_md(t);
+ queue = dm_get_md_queue(md);
+ if (queue) {
+ spin_lock_irqsave(queue->queue_lock, flags);
+ blk_run_queue_async(queue);
+ spin_unlock_irqrestore(queue->queue_lock, flags);
+ }
+}
+EXPORT_SYMBOL(dm_table_run_md_queue_async);
+
static int device_discard_capable(struct dm_target *ti, struct dm_dev *dev,
sector_t start, sector_t len, void *data)
{
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index fb9efc829182..b086a945edcb 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -192,6 +192,13 @@ struct dm_pool_metadata {
* operation possible in this state is the closing of the device.
*/
bool fail_io:1;
+
+ /*
+ * Reading the space map roots can fail, so we read it into these
+ * buffers before the superblock is locked and updated.
+ */
+ __u8 data_space_map_root[SPACE_MAP_ROOT_SIZE];
+ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
};
struct dm_thin_device {
@@ -431,26 +438,53 @@ static void __setup_btree_details(struct dm_pool_metadata *pmd)
pmd->details_info.value_type.equal = NULL;
}
+static int save_sm_roots(struct dm_pool_metadata *pmd)
+{
+ int r;
+ size_t len;
+
+ r = dm_sm_root_size(pmd->metadata_sm, &len);
+ if (r < 0)
+ return r;
+
+ r = dm_sm_copy_root(pmd->metadata_sm, &pmd->metadata_space_map_root, len);
+ if (r < 0)
+ return r;
+
+ r = dm_sm_root_size(pmd->data_sm, &len);
+ if (r < 0)
+ return r;
+
+ return dm_sm_copy_root(pmd->data_sm, &pmd->data_space_map_root, len);
+}
+
+static void copy_sm_roots(struct dm_pool_metadata *pmd,
+ struct thin_disk_superblock *disk)
+{
+ memcpy(&disk->metadata_space_map_root,
+ &pmd->metadata_space_map_root,
+ sizeof(pmd->metadata_space_map_root));
+
+ memcpy(&disk->data_space_map_root,
+ &pmd->data_space_map_root,
+ sizeof(pmd->data_space_map_root));
+}
+
static int __write_initial_superblock(struct dm_pool_metadata *pmd)
{
int r;
struct dm_block *sblock;
- size_t metadata_len, data_len;
struct thin_disk_superblock *disk_super;
sector_t bdev_size = i_size_read(pmd->bdev->bd_inode) >> SECTOR_SHIFT;
if (bdev_size > THIN_METADATA_MAX_SECTORS)
bdev_size = THIN_METADATA_MAX_SECTORS;
- r = dm_sm_root_size(pmd->metadata_sm, &metadata_len);
- if (r < 0)
- return r;
-
- r = dm_sm_root_size(pmd->data_sm, &data_len);
+ r = dm_sm_commit(pmd->data_sm);
if (r < 0)
return r;
- r = dm_sm_commit(pmd->data_sm);
+ r = save_sm_roots(pmd);
if (r < 0)
return r;
@@ -471,15 +505,7 @@ static int __write_initial_superblock(struct dm_pool_metadata *pmd)
disk_super->trans_id = 0;
disk_super->held_root = 0;
- r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root,
- metadata_len);
- if (r < 0)
- goto bad_locked;
-
- r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root,
- data_len);
- if (r < 0)
- goto bad_locked;
+ copy_sm_roots(pmd, disk_super);
disk_super->data_mapping_root = cpu_to_le64(pmd->root);
disk_super->device_details_root = cpu_to_le64(pmd->details_root);
@@ -488,10 +514,6 @@ static int __write_initial_superblock(struct dm_pool_metadata *pmd)
disk_super->data_block_size = cpu_to_le32(pmd->data_block_size);
return dm_tm_commit(pmd->tm, sblock);
-
-bad_locked:
- dm_bm_unlock(sblock);
- return r;
}
static int __format_metadata(struct dm_pool_metadata *pmd)
@@ -769,6 +791,10 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
if (r < 0)
return r;
+ r = save_sm_roots(pmd);
+ if (r < 0)
+ return r;
+
r = superblock_lock(pmd, &sblock);
if (r)
return r;
@@ -780,21 +806,9 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
disk_super->trans_id = cpu_to_le64(pmd->trans_id);
disk_super->flags = cpu_to_le32(pmd->flags);
- r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root,
- metadata_len);
- if (r < 0)
- goto out_locked;
-
- r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root,
- data_len);
- if (r < 0)
- goto out_locked;
+ copy_sm_roots(pmd, disk_super);
return dm_tm_commit(pmd->tm, sblock);
-
-out_locked:
- dm_bm_unlock(sblock);
- return r;
}
struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index be70d38745f7..53728be84dee 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -12,9 +12,11 @@
#include <linux/dm-io.h>
#include <linux/dm-kcopyd.h>
#include <linux/list.h>
+#include <linux/rculist.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/rbtree.h>
#define DM_MSG_PREFIX "thin"
@@ -178,12 +180,10 @@ struct pool {
unsigned ref_count;
spinlock_t lock;
- struct bio_list deferred_bios;
struct bio_list deferred_flush_bios;
struct list_head prepared_mappings;
struct list_head prepared_discards;
-
- struct bio_list retry_on_resume_list;
+ struct list_head active_thins;
struct dm_deferred_set *shared_read_ds;
struct dm_deferred_set *all_io_ds;
@@ -220,6 +220,7 @@ struct pool_c {
* Target context for a thin.
*/
struct thin_c {
+ struct list_head list;
struct dm_dev *pool_dev;
struct dm_dev *origin_dev;
dm_thin_id dev_id;
@@ -227,6 +228,10 @@ struct thin_c {
struct pool *pool;
struct dm_thin_device *td;
bool requeue_mode:1;
+ spinlock_t lock;
+ struct bio_list deferred_bio_list;
+ struct bio_list retry_on_resume_list;
+ struct rb_root sort_bio_list; /* sorted list of deferred bios */
};
/*----------------------------------------------------------------*/
@@ -287,9 +292,9 @@ static void cell_defer_no_holder_no_free(struct thin_c *tc,
struct pool *pool = tc->pool;
unsigned long flags;
- spin_lock_irqsave(&pool->lock, flags);
- dm_cell_release_no_holder(pool->prison, cell, &pool->deferred_bios);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ dm_cell_release_no_holder(pool->prison, cell, &tc->deferred_bio_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
wake_worker(pool);
}
@@ -368,6 +373,7 @@ struct dm_thin_endio_hook {
struct dm_deferred_entry *shared_read_entry;
struct dm_deferred_entry *all_io_entry;
struct dm_thin_new_mapping *overwrite_mapping;
+ struct rb_node rb_node;
};
static void requeue_bio_list(struct thin_c *tc, struct bio_list *master)
@@ -378,30 +384,22 @@ static void requeue_bio_list(struct thin_c *tc, struct bio_list *master)
bio_list_init(&bios);
- spin_lock_irqsave(&tc->pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
bio_list_merge(&bios, master);
bio_list_init(master);
- spin_unlock_irqrestore(&tc->pool->lock, flags);
+ spin_unlock_irqrestore(&tc->lock, flags);
- while ((bio = bio_list_pop(&bios))) {
- struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
-
- if (h->tc == tc)
- bio_endio(bio, DM_ENDIO_REQUEUE);
- else
- bio_list_add(master, bio);
- }
+ while ((bio = bio_list_pop(&bios)))
+ bio_endio(bio, DM_ENDIO_REQUEUE);
}
static void requeue_io(struct thin_c *tc)
{
- struct pool *pool = tc->pool;
-
- requeue_bio_list(tc, &pool->deferred_bios);
- requeue_bio_list(tc, &pool->retry_on_resume_list);
+ requeue_bio_list(tc, &tc->deferred_bio_list);
+ requeue_bio_list(tc, &tc->retry_on_resume_list);
}
-static void error_retry_list(struct pool *pool)
+static void error_thin_retry_list(struct thin_c *tc)
{
struct bio *bio;
unsigned long flags;
@@ -409,15 +407,25 @@ static void error_retry_list(struct pool *pool)
bio_list_init(&bios);
- spin_lock_irqsave(&pool->lock, flags);
- bio_list_merge(&bios, &pool->retry_on_resume_list);
- bio_list_init(&pool->retry_on_resume_list);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ bio_list_merge(&bios, &tc->retry_on_resume_list);
+ bio_list_init(&tc->retry_on_resume_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
while ((bio = bio_list_pop(&bios)))
bio_io_error(bio);
}
+static void error_retry_list(struct pool *pool)
+{
+ struct thin_c *tc;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(tc, &pool->active_thins, list)
+ error_thin_retry_list(tc);
+ rcu_read_unlock();
+}
+
/*
* This section of code contains the logic for processing a thin device's IO.
* Much of the code depends on pool object resources (lists, workqueues, etc)
@@ -608,9 +616,9 @@ static void cell_defer(struct thin_c *tc, struct dm_bio_prison_cell *cell)
struct pool *pool = tc->pool;
unsigned long flags;
- spin_lock_irqsave(&pool->lock, flags);
- cell_release(pool, cell, &pool->deferred_bios);
- spin_unlock_irqrestore(&tc->pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ cell_release(pool, cell, &tc->deferred_bio_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
wake_worker(pool);
}
@@ -623,9 +631,9 @@ static void cell_defer_no_holder(struct thin_c *tc, struct dm_bio_prison_cell *c
struct pool *pool = tc->pool;
unsigned long flags;
- spin_lock_irqsave(&pool->lock, flags);
- cell_release_no_holder(pool, cell, &pool->deferred_bios);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ cell_release_no_holder(pool, cell, &tc->deferred_bio_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
wake_worker(pool);
}
@@ -1001,12 +1009,11 @@ static void retry_on_resume(struct bio *bio)
{
struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
struct thin_c *tc = h->tc;
- struct pool *pool = tc->pool;
unsigned long flags;
- spin_lock_irqsave(&pool->lock, flags);
- bio_list_add(&pool->retry_on_resume_list, bio);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ bio_list_add(&tc->retry_on_resume_list, bio);
+ spin_unlock_irqrestore(&tc->lock, flags);
}
static bool should_error_unserviceable_bio(struct pool *pool)
@@ -1363,38 +1370,111 @@ static int need_commit_due_to_time(struct pool *pool)
jiffies > pool->last_commit_jiffies + COMMIT_PERIOD;
}
-static void process_deferred_bios(struct pool *pool)
+#define thin_pbd(node) rb_entry((node), struct dm_thin_endio_hook, rb_node)
+#define thin_bio(pbd) dm_bio_from_per_bio_data((pbd), sizeof(struct dm_thin_endio_hook))
+
+static void __thin_bio_rb_add(struct thin_c *tc, struct bio *bio)
+{
+ struct rb_node **rbp, *parent;
+ struct dm_thin_endio_hook *pbd;
+ sector_t bi_sector = bio->bi_iter.bi_sector;
+
+ rbp = &tc->sort_bio_list.rb_node;
+ parent = NULL;
+ while (*rbp) {
+ parent = *rbp;
+ pbd = thin_pbd(parent);
+
+ if (bi_sector < thin_bio(pbd)->bi_iter.bi_sector)
+ rbp = &(*rbp)->rb_left;
+ else
+ rbp = &(*rbp)->rb_right;
+ }
+
+ pbd = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
+ rb_link_node(&pbd->rb_node, parent, rbp);
+ rb_insert_color(&pbd->rb_node, &tc->sort_bio_list);
+}
+
+static void __extract_sorted_bios(struct thin_c *tc)
+{
+ struct rb_node *node;
+ struct dm_thin_endio_hook *pbd;
+ struct bio *bio;
+
+ for (node = rb_first(&tc->sort_bio_list); node; node = rb_next(node)) {
+ pbd = thin_pbd(node);
+ bio = thin_bio(pbd);
+
+ bio_list_add(&tc->deferred_bio_list, bio);
+ rb_erase(&pbd->rb_node, &tc->sort_bio_list);
+ }
+
+ WARN_ON(!RB_EMPTY_ROOT(&tc->sort_bio_list));
+}
+
+static void __sort_thin_deferred_bios(struct thin_c *tc)
+{
+ struct bio *bio;
+ struct bio_list bios;
+
+ bio_list_init(&bios);
+ bio_list_merge(&bios, &tc->deferred_bio_list);
+ bio_list_init(&tc->deferred_bio_list);
+
+ /* Sort deferred_bio_list using rb-tree */
+ while ((bio = bio_list_pop(&bios)))
+ __thin_bio_rb_add(tc, bio);
+
+ /*
+ * Transfer the sorted bios in sort_bio_list back to
+ * deferred_bio_list to allow lockless submission of
+ * all bios.
+ */
+ __extract_sorted_bios(tc);
+}
+
+static void process_thin_deferred_bios(struct thin_c *tc)
{
+ struct pool *pool = tc->pool;
unsigned long flags;
struct bio *bio;
struct bio_list bios;
+ struct blk_plug plug;
+
+ if (tc->requeue_mode) {
+ requeue_bio_list(tc, &tc->deferred_bio_list);
+ return;
+ }
bio_list_init(&bios);
- spin_lock_irqsave(&pool->lock, flags);
- bio_list_merge(&bios, &pool->deferred_bios);
- bio_list_init(&pool->deferred_bios);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
- while ((bio = bio_list_pop(&bios))) {
- struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
- struct thin_c *tc = h->tc;
+ if (bio_list_empty(&tc->deferred_bio_list)) {
+ spin_unlock_irqrestore(&tc->lock, flags);
+ return;
+ }
- if (tc->requeue_mode) {
- bio_endio(bio, DM_ENDIO_REQUEUE);
- continue;
- }
+ __sort_thin_deferred_bios(tc);
+
+ bio_list_merge(&bios, &tc->deferred_bio_list);
+ bio_list_init(&tc->deferred_bio_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
+
+ blk_start_plug(&plug);
+ while ((bio = bio_list_pop(&bios))) {
/*
* If we've got no free new_mapping structs, and processing
* this bio might require one, we pause until there are some
* prepared mappings to process.
*/
if (ensure_next_mapping(pool)) {
- spin_lock_irqsave(&pool->lock, flags);
- bio_list_merge(&pool->deferred_bios, &bios);
- spin_unlock_irqrestore(&pool->lock, flags);
-
+ spin_lock_irqsave(&tc->lock, flags);
+ bio_list_add(&tc->deferred_bio_list, bio);
+ bio_list_merge(&tc->deferred_bio_list, &bios);
+ spin_unlock_irqrestore(&tc->lock, flags);
break;
}
@@ -1403,6 +1483,20 @@ static void process_deferred_bios(struct pool *pool)
else
pool->process_bio(tc, bio);
}
+ blk_finish_plug(&plug);
+}
+
+static void process_deferred_bios(struct pool *pool)
+{
+ unsigned long flags;
+ struct bio *bio;
+ struct bio_list bios;
+ struct thin_c *tc;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(tc, &pool->active_thins, list)
+ process_thin_deferred_bios(tc);
+ rcu_read_unlock();
/*
* If there are any deferred flush bios, we must commit
@@ -1634,9 +1728,9 @@ static void thin_defer_bio(struct thin_c *tc, struct bio *bio)
unsigned long flags;
struct pool *pool = tc->pool;
- spin_lock_irqsave(&pool->lock, flags);
- bio_list_add(&pool->deferred_bios, bio);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ bio_list_add(&tc->deferred_bio_list, bio);
+ spin_unlock_irqrestore(&tc->lock, flags);
wake_worker(pool);
}
@@ -1757,26 +1851,29 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
static int pool_is_congested(struct dm_target_callbacks *cb, int bdi_bits)
{
- int r;
- unsigned long flags;
struct pool_c *pt = container_of(cb, struct pool_c, callbacks);
+ struct request_queue *q;
- spin_lock_irqsave(&pt->pool->lock, flags);
- r = !bio_list_empty(&pt->pool->retry_on_resume_list);
- spin_unlock_irqrestore(&pt->pool->lock, flags);
+ if (get_pool_mode(pt->pool) == PM_OUT_OF_DATA_SPACE)
+ return 1;
- if (!r) {
- struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
- r = bdi_congested(&q->backing_dev_info, bdi_bits);
- }
-
- return r;
+ q = bdev_get_queue(pt->data_dev->bdev);
+ return bdi_congested(&q->backing_dev_info, bdi_bits);
}
-static void __requeue_bios(struct pool *pool)
+static void requeue_bios(struct pool *pool)
{
- bio_list_merge(&pool->deferred_bios, &pool->retry_on_resume_list);
- bio_list_init(&pool->retry_on_resume_list);
+ unsigned long flags;
+ struct thin_c *tc;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(tc, &pool->active_thins, list) {
+ spin_lock_irqsave(&tc->lock, flags);
+ bio_list_merge(&tc->deferred_bio_list, &tc->retry_on_resume_list);
+ bio_list_init(&tc->retry_on_resume_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
+ }
+ rcu_read_unlock();
}
/*----------------------------------------------------------------
@@ -1957,12 +2054,11 @@ static struct pool *pool_create(struct mapped_device *pool_md,
INIT_WORK(&pool->worker, do_worker);
INIT_DELAYED_WORK(&pool->waker, do_waker);
spin_lock_init(&pool->lock);
- bio_list_init(&pool->deferred_bios);
bio_list_init(&pool->deferred_flush_bios);
INIT_LIST_HEAD(&pool->prepared_mappings);
INIT_LIST_HEAD(&pool->prepared_discards);
+ INIT_LIST_HEAD(&pool->active_thins);
pool->low_water_triggered = false;
- bio_list_init(&pool->retry_on_resume_list);
pool->shared_read_ds = dm_deferred_set_create();
if (!pool->shared_read_ds) {
@@ -2507,8 +2603,8 @@ static void pool_resume(struct dm_target *ti)
spin_lock_irqsave(&pool->lock, flags);
pool->low_water_triggered = false;
- __requeue_bios(pool);
spin_unlock_irqrestore(&pool->lock, flags);
+ requeue_bios(pool);
do_waker(&pool->waker.work);
}
@@ -2947,7 +3043,7 @@ static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 11, 0},
+ .version = {1, 12, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
@@ -2968,6 +3064,12 @@ static struct target_type pool_target = {
static void thin_dtr(struct dm_target *ti)
{
struct thin_c *tc = ti->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc->pool->lock, flags);
+ list_del_rcu(&tc->list);
+ spin_unlock_irqrestore(&tc->pool->lock, flags);
+ synchronize_rcu();
mutex_lock(&dm_thin_pool_table.mutex);
@@ -3014,6 +3116,10 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
r = -ENOMEM;
goto out_unlock;
}
+ spin_lock_init(&tc->lock);
+ bio_list_init(&tc->deferred_bio_list);
+ bio_list_init(&tc->retry_on_resume_list);
+ tc->sort_bio_list = RB_ROOT;
if (argc == 3) {
r = dm_get_device(ti, argv[2], FMODE_READ, &origin_dev);
@@ -3085,6 +3191,17 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
mutex_unlock(&dm_thin_pool_table.mutex);
+ spin_lock(&tc->pool->lock);
+ list_add_tail_rcu(&tc->list, &tc->pool->active_thins);
+ spin_unlock(&tc->pool->lock);
+ /*
+ * This synchronize_rcu() call is needed here otherwise we risk a
+ * wake_worker() call finding no bios to process (because the newly
+ * added tc isn't yet visible). So this reduces latency since we
+ * aren't then dependent on the periodic commit to wake_worker().
+ */
+ synchronize_rcu();
+
return 0;
bad_target_max_io_len:
@@ -3250,7 +3367,7 @@ static int thin_iterate_devices(struct dm_target *ti,
static struct target_type thin_target = {
.name = "thin",
- .version = {1, 11, 0},
+ .version = {1, 12, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 8c53b09b9a2c..455e64916498 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -94,13 +94,6 @@ struct dm_rq_clone_bio_info {
struct bio clone;
};
-union map_info *dm_get_mapinfo(struct bio *bio)
-{
- if (bio && bio->bi_private)
- return &((struct dm_target_io *)bio->bi_private)->info;
- return NULL;
-}
-
union map_info *dm_get_rq_mapinfo(struct request *rq)
{
if (rq && rq->end_io_data)
@@ -475,6 +468,11 @@ sector_t dm_get_size(struct mapped_device *md)
return get_capacity(md->disk);
}
+struct request_queue *dm_get_md_queue(struct mapped_device *md)
+{
+ return md->queue;
+}
+
struct dm_stats *dm_get_stats(struct mapped_device *md)
{
return &md->stats;
@@ -760,7 +758,7 @@ static void dec_pending(struct dm_io *io, int error)
static void clone_endio(struct bio *bio, int error)
{
int r = 0;
- struct dm_target_io *tio = bio->bi_private;
+ struct dm_target_io *tio = container_of(bio, struct dm_target_io, clone);
struct dm_io *io = tio->io;
struct mapped_device *md = tio->io->md;
dm_endio_fn endio = tio->ti->type->end_io;
@@ -794,7 +792,8 @@ static void clone_endio(struct bio *bio, int error)
*/
static void end_clone_bio(struct bio *clone, int error)
{
- struct dm_rq_clone_bio_info *info = clone->bi_private;
+ struct dm_rq_clone_bio_info *info =
+ container_of(clone, struct dm_rq_clone_bio_info, clone);
struct dm_rq_target_io *tio = info->tio;
struct bio *bio = info->orig;
unsigned int nr_bytes = info->orig->bi_iter.bi_size;
@@ -1120,7 +1119,6 @@ static void __map_bio(struct dm_target_io *tio)
struct dm_target *ti = tio->ti;
clone->bi_end_io = clone_endio;
- clone->bi_private = tio;
/*
* Map the clone. If r == 0 we don't need to do
@@ -1195,7 +1193,6 @@ static struct dm_target_io *alloc_tio(struct clone_info *ci,
tio->io = ci->io;
tio->ti = ti;
- memset(&tio->info, 0, sizeof(tio->info));
tio->target_bio_nr = target_bio_nr;
return tio;
@@ -1530,7 +1527,6 @@ static int dm_rq_bio_constructor(struct bio *bio, struct bio *bio_orig,
info->orig = bio_orig;
info->tio = tio;
bio->bi_end_io = end_clone_bio;
- bio->bi_private = info;
return 0;
}
@@ -2172,7 +2168,7 @@ static struct dm_table *__unbind(struct mapped_device *md)
return NULL;
dm_table_event_callback(map, NULL, NULL);
- rcu_assign_pointer(md->map, NULL);
+ RCU_INIT_POINTER(md->map, NULL);
dm_sync_table(md);
return map;
@@ -2873,8 +2869,6 @@ static const struct block_device_operations dm_blk_dops = {
.owner = THIS_MODULE
};
-EXPORT_SYMBOL(dm_get_mapinfo);
-
/*
* module hooks
*/
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index c4569f02f50f..ed76126aac54 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -73,7 +73,6 @@ unsigned dm_table_get_type(struct dm_table *t);
struct target_type *dm_table_get_immutable_target_type(struct dm_table *t);
bool dm_table_request_based(struct dm_table *t);
bool dm_table_supports_discards(struct dm_table *t);
-int dm_table_alloc_md_mempools(struct dm_table *t);
void dm_table_free_md_mempools(struct dm_table *t);
struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t);
@@ -189,6 +188,7 @@ int dm_lock_for_deletion(struct mapped_device *md, bool mark_deferred, bool only
int dm_cancel_deferred_remove(struct mapped_device *md);
int dm_request_based(struct mapped_device *md);
sector_t dm_get_size(struct mapped_device *md);
+struct request_queue *dm_get_md_queue(struct mapped_device *md);
struct dm_stats *dm_get_stats(struct mapped_device *md);
int dm_kobject_uevent(struct mapped_device *md, enum kobject_action action,
diff --git a/drivers/md/persistent-data/dm-bitset.c b/drivers/md/persistent-data/dm-bitset.c
index cd9a86d4cdf0..36f7cc2c7109 100644
--- a/drivers/md/persistent-data/dm-bitset.c
+++ b/drivers/md/persistent-data/dm-bitset.c
@@ -65,7 +65,7 @@ int dm_bitset_flush(struct dm_disk_bitset *info, dm_block_t root,
int r;
__le64 value;
- if (!info->current_index_set)
+ if (!info->current_index_set || !info->dirty)
return 0;
value = cpu_to_le64(info->current_bits);
@@ -77,6 +77,8 @@ int dm_bitset_flush(struct dm_disk_bitset *info, dm_block_t root,
return r;
info->current_index_set = false;
+ info->dirty = false;
+
return 0;
}
EXPORT_SYMBOL_GPL(dm_bitset_flush);
@@ -94,6 +96,8 @@ static int read_bits(struct dm_disk_bitset *info, dm_block_t root,
info->current_bits = le64_to_cpu(value);
info->current_index_set = true;
info->current_index = array_index;
+ info->dirty = false;
+
return 0;
}
@@ -126,6 +130,8 @@ int dm_bitset_set_bit(struct dm_disk_bitset *info, dm_block_t root,
return r;
set_bit(b, (unsigned long *) &info->current_bits);
+ info->dirty = true;
+
return 0;
}
EXPORT_SYMBOL_GPL(dm_bitset_set_bit);
@@ -141,6 +147,8 @@ int dm_bitset_clear_bit(struct dm_disk_bitset *info, dm_block_t root,
return r;
clear_bit(b, (unsigned long *) &info->current_bits);
+ info->dirty = true;
+
return 0;
}
EXPORT_SYMBOL_GPL(dm_bitset_clear_bit);
diff --git a/drivers/md/persistent-data/dm-bitset.h b/drivers/md/persistent-data/dm-bitset.h
index e1b9bea14aa1..c2287d672ef5 100644
--- a/drivers/md/persistent-data/dm-bitset.h
+++ b/drivers/md/persistent-data/dm-bitset.h
@@ -71,6 +71,7 @@ struct dm_disk_bitset {
uint64_t current_bits;
bool current_index_set:1;
+ bool dirty:1;
};
/*
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
index 455f79279a16..087411c95ffc 100644
--- a/drivers/md/persistent-data/dm-block-manager.c
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -595,25 +595,14 @@ int dm_bm_unlock(struct dm_block *b)
}
EXPORT_SYMBOL_GPL(dm_bm_unlock);
-int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
- struct dm_block *superblock)
+int dm_bm_flush(struct dm_block_manager *bm)
{
- int r;
-
if (bm->read_only)
return -EPERM;
- r = dm_bufio_write_dirty_buffers(bm->bufio);
- if (unlikely(r)) {
- dm_bm_unlock(superblock);
- return r;
- }
-
- dm_bm_unlock(superblock);
-
return dm_bufio_write_dirty_buffers(bm->bufio);
}
-EXPORT_SYMBOL_GPL(dm_bm_flush_and_unlock);
+EXPORT_SYMBOL_GPL(dm_bm_flush);
void dm_bm_prefetch(struct dm_block_manager *bm, dm_block_t b)
{
diff --git a/drivers/md/persistent-data/dm-block-manager.h b/drivers/md/persistent-data/dm-block-manager.h
index 13cd58e1fe69..1b95dfc17786 100644
--- a/drivers/md/persistent-data/dm-block-manager.h
+++ b/drivers/md/persistent-data/dm-block-manager.h
@@ -105,8 +105,7 @@ int dm_bm_unlock(struct dm_block *b);
*
* This method always blocks.
*/
-int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
- struct dm_block *superblock);
+int dm_bm_flush(struct dm_block_manager *bm);
/*
* Request data is prefetched into the cache.
diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c
index 81da1a26042e..3bc30a0ae3d6 100644
--- a/drivers/md/persistent-data/dm-transaction-manager.c
+++ b/drivers/md/persistent-data/dm-transaction-manager.c
@@ -154,7 +154,7 @@ int dm_tm_pre_commit(struct dm_transaction_manager *tm)
if (r < 0)
return r;
- return 0;
+ return dm_bm_flush(tm->bm);
}
EXPORT_SYMBOL_GPL(dm_tm_pre_commit);
@@ -164,8 +164,9 @@ int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *root)
return -EWOULDBLOCK;
wipe_shadow_table(tm);
+ dm_bm_unlock(root);
- return dm_bm_flush_and_unlock(tm->bm, root);
+ return dm_bm_flush(tm->bm);
}
EXPORT_SYMBOL_GPL(dm_tm_commit);
diff --git a/drivers/md/persistent-data/dm-transaction-manager.h b/drivers/md/persistent-data/dm-transaction-manager.h
index b5b139076ca5..2772ed2a781a 100644
--- a/drivers/md/persistent-data/dm-transaction-manager.h
+++ b/drivers/md/persistent-data/dm-transaction-manager.h
@@ -38,18 +38,17 @@ struct dm_transaction_manager *dm_tm_create_non_blocking_clone(struct dm_transac
/*
* We use a 2-phase commit here.
*
- * i) In the first phase the block manager is told to start flushing, and
- * the changes to the space map are written to disk. You should interrogate
- * your particular space map to get detail of its root node etc. to be
- * included in your superblock.
+ * i) Make all changes for the transaction *except* for the superblock.
+ * Then call dm_tm_pre_commit() to flush them to disk.
*
- * ii) @root will be committed last. You shouldn't use more than the
- * first 512 bytes of @root if you wish the transaction to survive a power
- * failure. You *must* have a write lock held on @root for both stage (i)
- * and (ii). The commit will drop the write lock.
+ * ii) Lock your superblock. Update. Then call dm_tm_commit() which will
+ * unlock the superblock and flush it. No other blocks should be updated
+ * during this period. Care should be taken to never unlock a partially
+ * updated superblock; perform any operations that could fail *before* you
+ * take the superblock lock.
*/
int dm_tm_pre_commit(struct dm_transaction_manager *tm);
-int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *root);
+int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *superblock);
/*
* These methods are the only way to get hold of a writeable block.
diff --git a/drivers/media/common/siano/smsdvb-debugfs.c b/drivers/media/common/siano/smsdvb-debugfs.c
index 0bb4430535f9..2408d7e9451e 100644
--- a/drivers/media/common/siano/smsdvb-debugfs.c
+++ b/drivers/media/common/siano/smsdvb-debugfs.c
@@ -1,6 +1,6 @@
/***********************************************************************
*
- * Copyright(c) 2013 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright(c) 2013 Mauro Carvalho Chehab
*
* 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
diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
index b8c5cad78537..6d7c0c858bd0 100644
--- a/drivers/media/common/siano/smsir.c
+++ b/drivers/media/common/siano/smsir.c
@@ -88,7 +88,7 @@ int sms_ir_init(struct smscore_device_t *coredev)
dev->priv = coredev;
dev->driver_type = RC_DRIVER_IR_RAW;
- dev->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(dev, RC_BIT_ALL);
dev->map_name = sms_get_board(board_id)->rc_codes;
dev->driver_name = MODULE_NAME;
diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
index f19a2ccd1e4b..1bdc0e7e8b79 100644
--- a/drivers/media/dvb-core/dvb-usb-ids.h
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
@@ -257,6 +257,7 @@
#define USB_PID_TERRATEC_T5 0x10a1
#define USB_PID_NOXON_DAB_STICK 0x00b3
#define USB_PID_NOXON_DAB_STICK_REV2 0x00e0
+#define USB_PID_NOXON_DAB_STICK_REV3 0x00b4
#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e
#define USB_PID_PINNACLE_PCTV2000E 0x022c
#define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
index 1f925e856974..6ce435ac866f 100644
--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -1279,7 +1279,7 @@ static int dtv_property_process_get(struct dvb_frontend *fe,
switch(tvp->cmd) {
case DTV_ENUM_DELSYS:
ncaps = 0;
- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ while (ncaps < MAX_DELSYS && fe->ops.delsys[ncaps]) {
tvp->u.buffer.data[ncaps] = fe->ops.delsys[ncaps];
ncaps++;
}
@@ -1596,7 +1596,7 @@ static int dvbv5_set_delivery_system(struct dvb_frontend *fe,
* supported
*/
ncaps = 0;
- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ while (ncaps < MAX_DELSYS && fe->ops.delsys[ncaps]) {
if (fe->ops.delsys[ncaps] == desired_system) {
c->delivery_system = desired_system;
dev_dbg(fe->dvb->device,
@@ -1628,7 +1628,7 @@ static int dvbv5_set_delivery_system(struct dvb_frontend *fe,
* of the desired system
*/
ncaps = 0;
- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ while (ncaps < MAX_DELSYS && fe->ops.delsys[ncaps]) {
if (dvbv3_type(fe->ops.delsys[ncaps]) == type)
delsys = fe->ops.delsys[ncaps];
ncaps++;
@@ -1703,7 +1703,7 @@ static int dvbv3_set_delivery_system(struct dvb_frontend *fe)
* DVBv3 standard
*/
ncaps = 0;
- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ while (ncaps < MAX_DELSYS && fe->ops.delsys[ncaps]) {
if (dvbv3_type(fe->ops.delsys[ncaps]) != DVBV3_UNKNOWN) {
delsys = fe->ops.delsys[ncaps];
break;
@@ -1882,6 +1882,8 @@ static int dtv_property_process_set(struct dvb_frontend *fe,
c->lna = tvp->u.data;
if (fe->ops.set_lna)
r = fe->ops.set_lna(fe);
+ if (r < 0)
+ c->lna = LNA_AUTO;
break;
default:
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index dd12a1ebda82..025fc5496bfc 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -441,7 +441,7 @@ config DVB_RTL2830
config DVB_RTL2832
tristate "Realtek RTL2832 DVB-T"
- depends on DVB_CORE && I2C
+ depends on DVB_CORE && I2C && I2C_MUX
default m if !MEDIA_SUBDRV_AUTOSELECT
help
Say Y when you want to support this frontend.
@@ -650,6 +650,8 @@ config DVB_TUNER_DIB0090
comment "SEC control devices for DVB-S"
depends on DVB_CORE
+source "drivers/media/dvb-frontends/drx39xyj/Kconfig"
+
config DVB_LNBP21
tristate "LNBP21/LNBH24 SEC controllers"
depends on DVB_CORE && I2C
@@ -733,14 +735,6 @@ config DVB_IX2505V
help
A DVB-S tuner module. Say Y when you want to support this frontend.
-config DVB_IT913X_FE
- tristate "it913x frontend and it9137 tuner"
- depends on DVB_CORE && I2C
- default m if !MEDIA_SUBDRV_AUTOSELECT
- help
- A DVB-T tuner module.
- Say Y when you want to support this frontend.
-
config DVB_M88RS2000
tristate "M88RS2000 DVB-S demodulator and tuner"
depends on DVB_CORE && I2C
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index 0c75a6aafb9d..282aba2fe8db 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -92,13 +92,13 @@ obj-$(CONFIG_DVB_HD29L2) += hd29l2.o
obj-$(CONFIG_DVB_DS3000) += ds3000.o
obj-$(CONFIG_DVB_TS2020) += ts2020.o
obj-$(CONFIG_DVB_MB86A16) += mb86a16.o
+obj-$(CONFIG_DVB_DRX39XYJ) += drx39xyj/
obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o
obj-$(CONFIG_DVB_IX2505V) += ix2505v.o
obj-$(CONFIG_DVB_STV0367) += stv0367.o
obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
obj-$(CONFIG_DVB_DRXK) += drxk.o
obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
-obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
obj-$(CONFIG_DVB_A8293) += a8293.o
obj-$(CONFIG_DVB_TDA10071) += tda10071.o
obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c
index 65728c25ea05..be4bec2a9640 100644
--- a/drivers/media/dvb-frontends/af9033.c
+++ b/drivers/media/dvb-frontends/af9033.c
@@ -989,10 +989,62 @@ err:
return ret;
}
+static int af9033_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&state->i2c->dev, "%s: onoff=%d\n", __func__, onoff);
+
+ ret = af9033_wr_reg_mask(state, 0x80f993, onoff, 0x01);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid, int onoff)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+ u8 wbuf[2] = {(pid >> 0) & 0xff, (pid >> 8) & 0xff};
+
+ dev_dbg(&state->i2c->dev, "%s: index=%d pid=%04x onoff=%d\n",
+ __func__, index, pid, onoff);
+
+ if (pid > 0x1fff)
+ return 0;
+
+ ret = af9033_wr_regs(state, 0x80f996, wbuf, 2);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg(state, 0x80f994, onoff);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg(state, 0x80f995, index);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
static struct dvb_frontend_ops af9033_ops;
struct dvb_frontend *af9033_attach(const struct af9033_config *config,
- struct i2c_adapter *i2c)
+ struct i2c_adapter *i2c,
+ struct af9033_ops *ops)
{
int ret;
struct af9033_state *state;
@@ -1067,6 +1119,11 @@ struct dvb_frontend *af9033_attach(const struct af9033_config *config,
memcpy(&state->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops));
state->fe.demodulator_priv = state;
+ if (ops) {
+ ops->pid_filter = af9033_pid_filter;
+ ops->pid_filter_ctrl = af9033_pid_filter_ctrl;
+ }
+
return &state->fe;
err:
diff --git a/drivers/media/dvb-frontends/af9033.h b/drivers/media/dvb-frontends/af9033.h
index c286e8f1ec02..539f4db678b8 100644
--- a/drivers/media/dvb-frontends/af9033.h
+++ b/drivers/media/dvb-frontends/af9033.h
@@ -78,16 +78,42 @@ struct af9033_config {
};
+struct af9033_ops {
+ int (*pid_filter_ctrl)(struct dvb_frontend *fe, int onoff);
+ int (*pid_filter)(struct dvb_frontend *fe, int index, u16 pid,
+ int onoff);
+};
+
+
#if IS_ENABLED(CONFIG_DVB_AF9033)
-extern struct dvb_frontend *af9033_attach(const struct af9033_config *config,
- struct i2c_adapter *i2c);
+extern
+struct dvb_frontend *af9033_attach(const struct af9033_config *config,
+ struct i2c_adapter *i2c,
+ struct af9033_ops *ops);
+
#else
-static inline struct dvb_frontend *af9033_attach(
- const struct af9033_config *config, struct i2c_adapter *i2c)
+static inline
+struct dvb_frontend *af9033_attach(const struct af9033_config *config,
+ struct i2c_adapter *i2c,
+ struct af9033_ops *ops)
{
pr_warn("%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
+
+static inline int af9033_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid,
+ int onoff)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
#endif
#endif /* AF9033_H */
diff --git a/drivers/media/dvb-frontends/drx39xyj/Kconfig b/drivers/media/dvb-frontends/drx39xyj/Kconfig
new file mode 100644
index 000000000000..15628eb5cf0c
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/Kconfig
@@ -0,0 +1,7 @@
+config DVB_DRX39XYJ
+ tristate "Micronas DRX-J demodulator"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
diff --git a/drivers/media/dvb-frontends/drx39xyj/Makefile b/drivers/media/dvb-frontends/drx39xyj/Makefile
new file mode 100644
index 000000000000..672e07774955
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/Makefile
@@ -0,0 +1,6 @@
+drx39xyj-objs := drxj.o
+
+obj-$(CONFIG_DVB_DRX39XYJ) += drx39xyj.o
+
+ccflags-y += -I$(srctree)/drivers/media/dvb-core/
+ccflags-y += -I$(srctree)/drivers/media/tuners/
diff --git a/drivers/media/dvb-frontends/drx39xyj/bsp_i2c.h b/drivers/media/dvb-frontends/drx39xyj/bsp_i2c.h
new file mode 100644
index 000000000000..5b5421f70388
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/bsp_i2c.h
@@ -0,0 +1,139 @@
+/*
+ I2C API, implementation depends on board specifics
+
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ 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 of Trident Microsystems nor Hauppauge Computer Works
+ 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 HOLDER 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.
+
+ This module encapsulates I2C access.In some applications several devices
+ share one I2C bus. If these devices have the same I2C address some kind
+ off "switch" must be implemented to ensure error free communication with
+ one device. In case such a "switch" is used, the device ID can be used
+ to implement control over this "switch".
+*/
+
+#ifndef __BSPI2C_H__
+#define __BSPI2C_H__
+
+#include "bsp_types.h"
+
+/*
+ * This structure contains the I2C address, the device ID and a user_data pointer.
+ * The user_data pointer can be used for application specific purposes.
+ */
+struct i2c_device_addr {
+ u16 i2c_addr; /* The I2C address of the device. */
+ u16 i2c_dev_id; /* The device identifier. */
+ void *user_data; /* User data pointer */
+};
+
+
+/**
+* \def IS_I2C_10BIT( addr )
+* \brief Determine if I2C address 'addr' is a 10 bits address or not.
+* \param addr The I2C address.
+* \return int.
+* \retval 0 if address is not a 10 bits I2C address.
+* \retval 1 if address is a 10 bits I2C address.
+*/
+#define IS_I2C_10BIT(addr) \
+ (((addr) & 0xF8) == 0xF0)
+
+/*------------------------------------------------------------------------------
+Exported FUNCTIONS
+------------------------------------------------------------------------------*/
+
+/**
+* \fn drxbsp_i2c_init()
+* \brief Initialize I2C communication module.
+* \return drx_status_t Return status.
+* \retval 0 Initialization successful.
+* \retval -EIO Initialization failed.
+*/
+ drx_status_t drxbsp_i2c_init(void);
+
+/**
+* \fn drxbsp_i2c_term()
+* \brief Terminate I2C communication module.
+* \return drx_status_t Return status.
+* \retval 0 Termination successful.
+* \retval -EIO Termination failed.
+*/
+ drx_status_t drxbsp_i2c_term(void);
+
+/**
+* \fn drx_status_t drxbsp_i2c_write_read( struct i2c_device_addr *w_dev_addr,
+* u16 w_count,
+* u8 *wData,
+* struct i2c_device_addr *r_dev_addr,
+* u16 r_count,
+* u8 *r_data)
+* \brief Read and/or write count bytes from I2C bus, store them in data[].
+* \param w_dev_addr The device i2c address and the device ID to write to
+* \param w_count The number of bytes to write
+* \param wData The array to write the data to
+* \param r_dev_addr The device i2c address and the device ID to read from
+* \param r_count The number of bytes to read
+* \param r_data The array to read the data from
+* \return drx_status_t Return status.
+* \retval 0 Succes.
+* \retval -EIO Failure.
+* \retval -EINVAL Parameter 'wcount' is not zero but parameter
+* 'wdata' contains NULL.
+* Idem for 'rcount' and 'rdata'.
+* Both w_dev_addr and r_dev_addr are NULL.
+*
+* This function must implement an atomic write and/or read action on the I2C bus
+* No other process may use the I2C bus when this function is executing.
+* The critical section of this function runs from and including the I2C
+* write, up to and including the I2C read action.
+*
+* The device ID can be useful if several devices share an I2C address.
+* It can be used to control a "switch" on the I2C bus to the correct device.
+*/
+ drx_status_t drxbsp_i2c_write_read(struct i2c_device_addr *w_dev_addr,
+ u16 w_count,
+ u8 *w_data,
+ struct i2c_device_addr *r_dev_addr,
+ u16 r_count, u8 *r_data);
+
+/**
+* \fn drxbsp_i2c_error_text()
+* \brief Returns a human readable error.
+* Counter part of numerical drx_i2c_error_g.
+*
+* \return char* Pointer to human readable error text.
+*/
+ char *drxbsp_i2c_error_text(void);
+
+/**
+* \var drx_i2c_error_g;
+* \brief I2C specific error codes, platform dependent.
+*/
+ extern int drx_i2c_error_g;
+
+#endif /* __BSPI2C_H__ */
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h b/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h
new file mode 100644
index 000000000000..cfd0b96b6939
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h
@@ -0,0 +1,45 @@
+/*
+ * Driver for Micronas DRX39xx family (drx3933j)
+ *
+ * Written by Devin Heitmueller <devin.heitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#ifndef DRX39XXJ_H
+#define DRX39XXJ_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+#include "drx_driver.h"
+
+struct drx39xxj_state {
+ struct i2c_adapter *i2c;
+ struct drx_demod_instance *demod;
+ struct dvb_frontend frontend;
+ unsigned int i2c_gate_open:1;
+ const struct firmware *fw;
+};
+
+#if IS_ENABLED(CONFIG_DVB_DRX39XYJ)
+struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c) {
+ return NULL;
+};
+#endif
+
+#endif /* DVB_DUMMY_FE_H */
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h b/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h
new file mode 100644
index 000000000000..354ec07eae87
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h
@@ -0,0 +1,256 @@
+/*
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ 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 of Trident Microsystems nor Hauppauge Computer Works
+ 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 HOLDER 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.
+*/
+
+/*******************************************************************************
+* FILENAME: $Id: drx_dap_fasi.h,v 1.5 2009/07/07 14:21:40 justin Exp $
+*
+* DESCRIPTION:
+* Part of DRX driver.
+* Data access protocol: Fast Access Sequential Interface (fasi)
+* Fast access, because of short addressing format (16 instead of 32 bits addr)
+* Sequential, because of I2C.
+*
+* USAGE:
+* Include.
+*
+* NOTES:
+*
+*
+*******************************************************************************/
+
+/*-------- compilation control switches --------------------------------------*/
+
+#ifndef __DRX_DAP_FASI_H__
+#define __DRX_DAP_FASI_H__
+
+/*-------- Required includes -------------------------------------------------*/
+
+#include "drx_driver.h"
+
+/*-------- Defines, configuring the API --------------------------------------*/
+
+/********************************************
+* Allowed address formats
+********************************************/
+
+/*
+* Comments about short/long addressing format:
+*
+* The DAP FASI offers long address format (4 bytes) and short address format
+* (2 bytes). The DAP can operate in 3 modes:
+* (1) only short
+* (2) only long
+* (3) both long and short but short preferred and long only when necesarry
+*
+* These modes must be selected compile time via compile switches.
+* Compile switch settings for the diffrent modes:
+* (1) DRXDAPFASI_LONG_ADDR_ALLOWED=0, DRXDAPFASI_SHORT_ADDR_ALLOWED=1
+* (2) DRXDAPFASI_LONG_ADDR_ALLOWED=1, DRXDAPFASI_SHORT_ADDR_ALLOWED=0
+* (3) DRXDAPFASI_LONG_ADDR_ALLOWED=1, DRXDAPFASI_SHORT_ADDR_ALLOWED=1
+*
+* The default setting will be (3) both long and short.
+* The default setting will need no compile switches.
+* The default setting must be overridden if compile switches are already
+* defined.
+*
+*/
+
+/* set default */
+#if !defined(DRXDAPFASI_LONG_ADDR_ALLOWED)
+#define DRXDAPFASI_LONG_ADDR_ALLOWED 1
+#endif
+
+/* set default */
+#if !defined(DRXDAPFASI_SHORT_ADDR_ALLOWED)
+#define DRXDAPFASI_SHORT_ADDR_ALLOWED 1
+#endif
+
+/* check */
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 0) && \
+ (DRXDAPFASI_SHORT_ADDR_ALLOWED == 0))
+#error At least one of short- or long-addressing format must be allowed.
+*; /* illegal statement to force compiler error */
+#endif
+
+/********************************************
+* Single/master multi master setting
+********************************************/
+/*
+* Comments about SINGLE MASTER/MULTI MASTER modes:
+*
+* Consider the two sides:1) the master and 2)the slave.
+*
+* Master:
+* Single/multimaster operation set via DRXDAP_SINGLE_MASTER compile switch
+* + single master mode means no use of repeated starts
+* + multi master mode means use of repeated starts
+* Default is single master.
+* Default can be overriden by setting the compile switch DRXDAP_SINGLE_MASTER.
+*
+* Slave:
+* Single/multi master selected via the flags in the FASI protocol.
+* + single master means remember memory address between i2c packets
+* + multimaster means flush memory address between i2c packets
+* Default is single master, DAP FASI changes multi-master setting silently
+* into single master setting. This cannot be overrriden.
+*
+*/
+/* set default */
+#ifndef DRXDAP_SINGLE_MASTER
+#define DRXDAP_SINGLE_MASTER 0
+#endif
+
+/********************************************
+* Chunk/mode checking
+********************************************/
+/*
+* Comments about DRXDAP_MAX_WCHUNKSIZE in single or multi master mode and
+* in combination with short and long addressing format. All text below
+* assumes long addressing format. The table also includes information
+* for short ADDRessing format.
+*
+* In single master mode, data can be written by sending the register address
+* first, then two or four bytes of data in the next packet.
+* Because the device address plus a register address equals five bytes,
+* the mimimum chunk size must be five.
+* If ten-bit I2C device addresses are used, the minimum chunk size must be six,
+* because the I2C device address will then occupy two bytes when writing.
+*
+* Data in single master mode is transferred as follows:
+* <S> <devW> a0 a1 a2 a3 <P>
+* <S> <devW> d0 d1 [d2 d3] <P>
+* ..
+* or
+* ..
+* <S> <devW> a0 a1 a2 a3 <P>
+* <S> <devR> --- <P>
+*
+* In multi-master mode, the data must immediately follow the address (an I2C
+* stop resets the internal address), and hence the minimum chunk size is
+* 1 <I2C address> + 4 (register address) + 2 (data to send) = 7 bytes (8 if
+* 10-bit I2C device addresses are used).
+*
+* The 7-bit or 10-bit i2c address parameters is a runtime parameter.
+* The other parameters can be limited via compile time switches.
+*
+*-------------------------------------------------------------------------------
+*
+* Minimum chunk size table (in bytes):
+*
+* +----------------+----------------+
+* | 7b i2c addr | 10b i2c addr |
+* +----------------+----------------+
+* | single | multi | single | multi |
+* ------+--------+-------+--------+-------+
+* short | 3 | 5 | 4 | 6 |
+* long | 5 | 7 | 6 | 8 |
+* ------+--------+-------+--------+-------+
+*
+*/
+
+/* set default */
+#if !defined(DRXDAP_MAX_WCHUNKSIZE)
+#define DRXDAP_MAX_WCHUNKSIZE 254
+#endif
+
+/* check */
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 0) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1))
+#if DRXDAP_SINGLE_MASTER
+#define DRXDAP_MAX_WCHUNKSIZE_MIN 3
+#else
+#define DRXDAP_MAX_WCHUNKSIZE_MIN 5
+#endif
+#else
+#if DRXDAP_SINGLE_MASTER
+#define DRXDAP_MAX_WCHUNKSIZE_MIN 5
+#else
+#define DRXDAP_MAX_WCHUNKSIZE_MIN 7
+#endif
+#endif
+
+#if DRXDAP_MAX_WCHUNKSIZE < DRXDAP_MAX_WCHUNKSIZE_MIN
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 0) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1))
+#if DRXDAP_SINGLE_MASTER
+#error DRXDAP_MAX_WCHUNKSIZE must be at least 3 in single master mode
+*; /* illegal statement to force compiler error */
+#else
+#error DRXDAP_MAX_WCHUNKSIZE must be at least 5 in multi master mode
+*; /* illegal statement to force compiler error */
+#endif
+#else
+#if DRXDAP_SINGLE_MASTER
+#error DRXDAP_MAX_WCHUNKSIZE must be at least 5 in single master mode
+*; /* illegal statement to force compiler error */
+#else
+#error DRXDAP_MAX_WCHUNKSIZE must be at least 7 in multi master mode
+*; /* illegal statement to force compiler error */
+#endif
+#endif
+#endif
+
+/* set default */
+#if !defined(DRXDAP_MAX_RCHUNKSIZE)
+#define DRXDAP_MAX_RCHUNKSIZE 254
+#endif
+
+/* check */
+#if DRXDAP_MAX_RCHUNKSIZE < 2
+#error DRXDAP_MAX_RCHUNKSIZE must be at least 2
+*; /* illegal statement to force compiler error */
+#endif
+
+/* check */
+#if DRXDAP_MAX_RCHUNKSIZE & 1
+#error DRXDAP_MAX_RCHUNKSIZE must be even
+*; /* illegal statement to force compiler error */
+#endif
+
+/*-------- Public API functions ----------------------------------------------*/
+
+extern struct drx_access_func drx_dap_fasi_funct_g;
+
+#define DRXDAP_FASI_RMW 0x10000000
+#define DRXDAP_FASI_BROADCAST 0x20000000
+#define DRXDAP_FASI_CLEARCRC 0x80000000
+#define DRXDAP_FASI_SINGLE_MASTER 0xC0000000
+#define DRXDAP_FASI_MULTI_MASTER 0x40000000
+#define DRXDAP_FASI_SMM_SWITCH 0x40000000 /* single/multi master switch */
+#define DRXDAP_FASI_MODEFLAGS 0xC0000000
+#define DRXDAP_FASI_FLAGS 0xF0000000
+
+#define DRXDAP_FASI_ADDR2BLOCK(addr) (((addr)>>22)&0x3F)
+#define DRXDAP_FASI_ADDR2BANK(addr) (((addr)>>16)&0x3F)
+#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr)&0x7FFF)
+
+#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0)
+#define DRXDAP_FASI_LONG_FORMAT(addr) (((addr) & 0xFC30FF80) != 0)
+#define DRXDAP_FASI_OFFSET_TOO_LARGE(addr) (((addr) & 0x00008000) != 0)
+
+#endif /* __DRX_DAP_FASI_H__ */
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
new file mode 100644
index 000000000000..9076bf21cc8a
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
@@ -0,0 +1,2343 @@
+/*
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ 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 of Trident Microsystems nor Hauppauge Computer Works
+ 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 HOLDER 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 __DRXDRIVER_H__
+#define __DRXDRIVER_H__
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+
+/*
+ * This structure contains the I2C address, the device ID and a user_data pointer.
+ * The user_data pointer can be used for application specific purposes.
+ */
+struct i2c_device_addr {
+ u16 i2c_addr; /* The I2C address of the device. */
+ u16 i2c_dev_id; /* The device identifier. */
+ void *user_data; /* User data pointer */
+};
+
+/**
+* \def IS_I2C_10BIT( addr )
+* \brief Determine if I2C address 'addr' is a 10 bits address or not.
+* \param addr The I2C address.
+* \return int.
+* \retval 0 if address is not a 10 bits I2C address.
+* \retval 1 if address is a 10 bits I2C address.
+*/
+#define IS_I2C_10BIT(addr) \
+ (((addr) & 0xF8) == 0xF0)
+
+/*------------------------------------------------------------------------------
+Exported FUNCTIONS
+------------------------------------------------------------------------------*/
+
+/**
+* \fn drxbsp_i2c_init()
+* \brief Initialize I2C communication module.
+* \return int Return status.
+* \retval 0 Initialization successful.
+* \retval -EIO Initialization failed.
+*/
+int drxbsp_i2c_init(void);
+
+/**
+* \fn drxbsp_i2c_term()
+* \brief Terminate I2C communication module.
+* \return int Return status.
+* \retval 0 Termination successful.
+* \retval -EIO Termination failed.
+*/
+int drxbsp_i2c_term(void);
+
+/**
+* \fn int drxbsp_i2c_write_read( struct i2c_device_addr *w_dev_addr,
+* u16 w_count,
+* u8 * wData,
+* struct i2c_device_addr *r_dev_addr,
+* u16 r_count,
+* u8 * r_data)
+* \brief Read and/or write count bytes from I2C bus, store them in data[].
+* \param w_dev_addr The device i2c address and the device ID to write to
+* \param w_count The number of bytes to write
+* \param wData The array to write the data to
+* \param r_dev_addr The device i2c address and the device ID to read from
+* \param r_count The number of bytes to read
+* \param r_data The array to read the data from
+* \return int Return status.
+* \retval 0 Succes.
+* \retval -EIO Failure.
+* \retval -EINVAL Parameter 'wcount' is not zero but parameter
+* 'wdata' contains NULL.
+* Idem for 'rcount' and 'rdata'.
+* Both w_dev_addr and r_dev_addr are NULL.
+*
+* This function must implement an atomic write and/or read action on the I2C bus
+* No other process may use the I2C bus when this function is executing.
+* The critical section of this function runs from and including the I2C
+* write, up to and including the I2C read action.
+*
+* The device ID can be useful if several devices share an I2C address.
+* It can be used to control a "switch" on the I2C bus to the correct device.
+*/
+int drxbsp_i2c_write_read(struct i2c_device_addr *w_dev_addr,
+ u16 w_count,
+ u8 *wData,
+ struct i2c_device_addr *r_dev_addr,
+ u16 r_count, u8 *r_data);
+
+/**
+* \fn drxbsp_i2c_error_text()
+* \brief Returns a human readable error.
+* Counter part of numerical drx_i2c_error_g.
+*
+* \return char* Pointer to human readable error text.
+*/
+char *drxbsp_i2c_error_text(void);
+
+/**
+* \var drx_i2c_error_g;
+* \brief I2C specific error codes, platform dependent.
+*/
+extern int drx_i2c_error_g;
+
+#define TUNER_MODE_SUB0 0x0001 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB1 0x0002 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB2 0x0004 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB3 0x0008 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB4 0x0010 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB5 0x0020 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB6 0x0040 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB7 0x0080 /* for sub-mode (e.g. RF-AGC setting) */
+
+#define TUNER_MODE_DIGITAL 0x0100 /* for digital channel (e.g. DVB-T) */
+#define TUNER_MODE_ANALOG 0x0200 /* for analog channel (e.g. PAL) */
+#define TUNER_MODE_SWITCH 0x0400 /* during channel switch & scanning */
+#define TUNER_MODE_LOCK 0x0800 /* after tuner has locked */
+#define TUNER_MODE_6MHZ 0x1000 /* for 6MHz bandwidth channels */
+#define TUNER_MODE_7MHZ 0x2000 /* for 7MHz bandwidth channels */
+#define TUNER_MODE_8MHZ 0x4000 /* for 8MHz bandwidth channels */
+
+#define TUNER_MODE_SUB_MAX 8
+#define TUNER_MODE_SUBALL (TUNER_MODE_SUB0 | TUNER_MODE_SUB1 | \
+ TUNER_MODE_SUB2 | TUNER_MODE_SUB3 | \
+ TUNER_MODE_SUB4 | TUNER_MODE_SUB5 | \
+ TUNER_MODE_SUB6 | TUNER_MODE_SUB7)
+
+
+enum tuner_lock_status {
+ TUNER_LOCKED,
+ TUNER_NOT_LOCKED
+};
+
+struct tuner_common {
+ char *name; /* Tuner brand & type name */
+ s32 min_freq_rf; /* Lowest RF input frequency, in kHz */
+ s32 max_freq_rf; /* Highest RF input frequency, in kHz */
+
+ u8 sub_mode; /* Index to sub-mode in use */
+ char ***sub_mode_descriptions; /* Pointer to description of sub-modes */
+ u8 sub_modes; /* Number of available sub-modes */
+
+ /* The following fields will be either 0, NULL or false and do not need
+ initialisation */
+ void *self_check; /* gives proof of initialization */
+ bool programmed; /* only valid if self_check is OK */
+ s32 r_ffrequency; /* only valid if programmed */
+ s32 i_ffrequency; /* only valid if programmed */
+
+ void *my_user_data; /* pointer to associated demod instance */
+ u16 my_capabilities; /* value for storing application flags */
+};
+
+struct tuner_instance;
+
+typedef int(*tuner_open_func_t) (struct tuner_instance *tuner);
+typedef int(*tuner_close_func_t) (struct tuner_instance *tuner);
+
+typedef int(*tuner_set_frequency_func_t) (struct tuner_instance *tuner,
+ u32 mode,
+ s32
+ frequency);
+
+typedef int(*tuner_get_frequency_func_t) (struct tuner_instance *tuner,
+ u32 mode,
+ s32 *
+ r_ffrequency,
+ s32 *
+ i_ffrequency);
+
+typedef int(*tuner_lock_status_func_t) (struct tuner_instance *tuner,
+ enum tuner_lock_status *
+ lock_stat);
+
+typedef int(*tune_ri2c_write_read_func_t) (struct tuner_instance *tuner,
+ struct i2c_device_addr *
+ w_dev_addr, u16 w_count,
+ u8 *wData,
+ struct i2c_device_addr *
+ r_dev_addr, u16 r_count,
+ u8 *r_data);
+
+struct tuner_ops {
+ tuner_open_func_t open_func;
+ tuner_close_func_t close_func;
+ tuner_set_frequency_func_t set_frequency_func;
+ tuner_get_frequency_func_t get_frequency_func;
+ tuner_lock_status_func_t lock_status_func;
+ tune_ri2c_write_read_func_t i2c_write_read_func;
+
+};
+
+struct tuner_instance {
+ struct i2c_device_addr my_i2c_dev_addr;
+ struct tuner_common *my_common_attr;
+ void *my_ext_attr;
+ struct tuner_ops *my_funct;
+};
+
+int drxbsp_tuner_set_frequency(struct tuner_instance *tuner,
+ u32 mode,
+ s32 frequency);
+
+int drxbsp_tuner_get_frequency(struct tuner_instance *tuner,
+ u32 mode,
+ s32 *r_ffrequency,
+ s32 *i_ffrequency);
+
+int drxbsp_tuner_default_i2c_write_read(struct tuner_instance *tuner,
+ struct i2c_device_addr *w_dev_addr,
+ u16 w_count,
+ u8 *wData,
+ struct i2c_device_addr *r_dev_addr,
+ u16 r_count, u8 *r_data);
+
+/**************
+*
+* This section configures the DRX Data Access Protocols (DAPs).
+*
+**************/
+
+/**
+* \def DRXDAP_SINGLE_MASTER
+* \brief Enable I2C single or I2C multimaster mode on host.
+*
+* Set to 1 to enable single master mode
+* Set to 0 to enable multi master mode
+*
+* The actual DAP implementation may be restricted to only one of the modes.
+* A compiler warning or error will be generated if the DAP implementation
+* overides or cannot handle the mode defined below.
+*
+*/
+#ifndef DRXDAP_SINGLE_MASTER
+#define DRXDAP_SINGLE_MASTER 1
+#endif
+
+/**
+* \def DRXDAP_MAX_WCHUNKSIZE
+* \brief Defines maximum chunksize of an i2c write action by host.
+*
+* This indicates the maximum size of data the I2C device driver is able to
+* write at a time. This includes I2C device address and register addressing.
+*
+* This maximum size may be restricted by the actual DAP implementation.
+* A compiler warning or error will be generated if the DAP implementation
+* overides or cannot handle the chunksize defined below.
+*
+* Beware that the DAP uses DRXDAP_MAX_WCHUNKSIZE to create a temporary data
+* buffer. Do not undefine or choose too large, unless your system is able to
+* handle a stack buffer of that size.
+*
+*/
+#ifndef DRXDAP_MAX_WCHUNKSIZE
+#define DRXDAP_MAX_WCHUNKSIZE 60
+#endif
+
+/**
+* \def DRXDAP_MAX_RCHUNKSIZE
+* \brief Defines maximum chunksize of an i2c read action by host.
+*
+* This indicates the maximum size of data the I2C device driver is able to read
+* at a time. Minimum value is 2. Also, the read chunk size must be even.
+*
+* This maximum size may be restricted by the actual DAP implementation.
+* A compiler warning or error will be generated if the DAP implementation
+* overides or cannot handle the chunksize defined below.
+*
+*/
+#ifndef DRXDAP_MAX_RCHUNKSIZE
+#define DRXDAP_MAX_RCHUNKSIZE 60
+#endif
+
+/**************
+*
+* This section describes drxdriver defines.
+*
+**************/
+
+/**
+* \def DRX_UNKNOWN
+* \brief Generic UNKNOWN value for DRX enumerated types.
+*
+* Used to indicate that the parameter value is unknown or not yet initalized.
+*/
+#ifndef DRX_UNKNOWN
+#define DRX_UNKNOWN (254)
+#endif
+
+/**
+* \def DRX_AUTO
+* \brief Generic AUTO value for DRX enumerated types.
+*
+* Used to instruct the driver to automatically determine the value of the
+* parameter.
+*/
+#ifndef DRX_AUTO
+#define DRX_AUTO (255)
+#endif
+
+/**************
+*
+* This section describes flag definitions for the device capbilities.
+*
+**************/
+
+/**
+* \brief LNA capability flag
+*
+* Device has a Low Noise Amplifier
+*
+*/
+#define DRX_CAPABILITY_HAS_LNA (1UL << 0)
+/**
+* \brief OOB-RX capability flag
+*
+* Device has OOB-RX
+*
+*/
+#define DRX_CAPABILITY_HAS_OOBRX (1UL << 1)
+/**
+* \brief ATV capability flag
+*
+* Device has ATV
+*
+*/
+#define DRX_CAPABILITY_HAS_ATV (1UL << 2)
+/**
+* \brief DVB-T capability flag
+*
+* Device has DVB-T
+*
+*/
+#define DRX_CAPABILITY_HAS_DVBT (1UL << 3)
+/**
+* \brief ITU-B capability flag
+*
+* Device has ITU-B
+*
+*/
+#define DRX_CAPABILITY_HAS_ITUB (1UL << 4)
+/**
+* \brief Audio capability flag
+*
+* Device has Audio
+*
+*/
+#define DRX_CAPABILITY_HAS_AUD (1UL << 5)
+/**
+* \brief SAW switch capability flag
+*
+* Device has SAW switch
+*
+*/
+#define DRX_CAPABILITY_HAS_SAWSW (1UL << 6)
+/**
+* \brief GPIO1 capability flag
+*
+* Device has GPIO1
+*
+*/
+#define DRX_CAPABILITY_HAS_GPIO1 (1UL << 7)
+/**
+* \brief GPIO2 capability flag
+*
+* Device has GPIO2
+*
+*/
+#define DRX_CAPABILITY_HAS_GPIO2 (1UL << 8)
+/**
+* \brief IRQN capability flag
+*
+* Device has IRQN
+*
+*/
+#define DRX_CAPABILITY_HAS_IRQN (1UL << 9)
+/**
+* \brief 8VSB capability flag
+*
+* Device has 8VSB
+*
+*/
+#define DRX_CAPABILITY_HAS_8VSB (1UL << 10)
+/**
+* \brief SMA-TX capability flag
+*
+* Device has SMATX
+*
+*/
+#define DRX_CAPABILITY_HAS_SMATX (1UL << 11)
+/**
+* \brief SMA-RX capability flag
+*
+* Device has SMARX
+*
+*/
+#define DRX_CAPABILITY_HAS_SMARX (1UL << 12)
+/**
+* \brief ITU-A/C capability flag
+*
+* Device has ITU-A/C
+*
+*/
+#define DRX_CAPABILITY_HAS_ITUAC (1UL << 13)
+
+/*-------------------------------------------------------------------------
+MACROS
+-------------------------------------------------------------------------*/
+/* Macros to stringify the version number */
+#define DRX_VERSIONSTRING(MAJOR, MINOR, PATCH) \
+ DRX_VERSIONSTRING_HELP(MAJOR)"." \
+ DRX_VERSIONSTRING_HELP(MINOR)"." \
+ DRX_VERSIONSTRING_HELP(PATCH)
+#define DRX_VERSIONSTRING_HELP(NUM) #NUM
+
+/**
+* \brief Macro to create byte array elements from 16 bit integers.
+* This macro is used to create byte arrays for block writes.
+* Block writes speed up I2C traffic between host and demod.
+* The macro takes care of the required byte order in a 16 bits word.
+* x->lowbyte(x), highbyte(x)
+*/
+#define DRX_16TO8(x) ((u8) (((u16)x) & 0xFF)), \
+ ((u8)((((u16)x)>>8)&0xFF))
+
+/**
+* \brief Macro to sign extend signed 9 bit value to signed 16 bit value
+*/
+#define DRX_S9TOS16(x) ((((u16)x)&0x100) ? ((s16)((u16)(x)|0xFF00)) : (x))
+
+/**
+* \brief Macro to sign extend signed 9 bit value to signed 16 bit value
+*/
+#define DRX_S24TODRXFREQ(x) ((((u32) x) & 0x00800000UL) ? \
+ ((s32) \
+ (((u32) x) | 0xFF000000)) : \
+ ((s32) x))
+
+/**
+* \brief Macro to convert 16 bit register value to a s32
+*/
+#define DRX_U16TODRXFREQ(x) ((x & 0x8000) ? \
+ ((s32) \
+ (((u32) x) | 0xFFFF0000)) : \
+ ((s32) x))
+
+/*-------------------------------------------------------------------------
+ENUM
+-------------------------------------------------------------------------*/
+
+/**
+* \enum enum drx_standard
+* \brief Modulation standards.
+*/
+enum drx_standard {
+ DRX_STANDARD_DVBT = 0, /**< Terrestrial DVB-T. */
+ DRX_STANDARD_8VSB, /**< Terrestrial 8VSB. */
+ DRX_STANDARD_NTSC, /**< Terrestrial\Cable analog NTSC. */
+ DRX_STANDARD_PAL_SECAM_BG,
+ /**< Terrestrial analog PAL/SECAM B/G */
+ DRX_STANDARD_PAL_SECAM_DK,
+ /**< Terrestrial analog PAL/SECAM D/K */
+ DRX_STANDARD_PAL_SECAM_I,
+ /**< Terrestrial analog PAL/SECAM I */
+ DRX_STANDARD_PAL_SECAM_L,
+ /**< Terrestrial analog PAL/SECAM L
+ with negative modulation */
+ DRX_STANDARD_PAL_SECAM_LP,
+ /**< Terrestrial analog PAL/SECAM L
+ with positive modulation */
+ DRX_STANDARD_ITU_A, /**< Cable ITU ANNEX A. */
+ DRX_STANDARD_ITU_B, /**< Cable ITU ANNEX B. */
+ DRX_STANDARD_ITU_C, /**< Cable ITU ANNEX C. */
+ DRX_STANDARD_ITU_D, /**< Cable ITU ANNEX D. */
+ DRX_STANDARD_FM, /**< Terrestrial\Cable FM radio */
+ DRX_STANDARD_DTMB, /**< Terrestrial DTMB standard (China)*/
+ DRX_STANDARD_UNKNOWN = DRX_UNKNOWN,
+ /**< Standard unknown. */
+ DRX_STANDARD_AUTO = DRX_AUTO
+ /**< Autodetect standard. */
+};
+
+/**
+* \enum enum drx_standard
+* \brief Modulation sub-standards.
+*/
+enum drx_substandard {
+ DRX_SUBSTANDARD_MAIN = 0, /**< Main subvariant of standard */
+ DRX_SUBSTANDARD_ATV_BG_SCANDINAVIA,
+ DRX_SUBSTANDARD_ATV_DK_POLAND,
+ DRX_SUBSTANDARD_ATV_DK_CHINA,
+ DRX_SUBSTANDARD_UNKNOWN = DRX_UNKNOWN,
+ /**< Sub-standard unknown. */
+ DRX_SUBSTANDARD_AUTO = DRX_AUTO
+ /**< Auto (default) sub-standard */
+};
+
+/**
+* \enum enum drx_bandwidth
+* \brief Channel bandwidth or channel spacing.
+*/
+enum drx_bandwidth {
+ DRX_BANDWIDTH_8MHZ = 0, /**< Bandwidth 8 MHz. */
+ DRX_BANDWIDTH_7MHZ, /**< Bandwidth 7 MHz. */
+ DRX_BANDWIDTH_6MHZ, /**< Bandwidth 6 MHz. */
+ DRX_BANDWIDTH_UNKNOWN = DRX_UNKNOWN,
+ /**< Bandwidth unknown. */
+ DRX_BANDWIDTH_AUTO = DRX_AUTO
+ /**< Auto Set Bandwidth */
+};
+
+/**
+* \enum enum drx_mirror
+* \brief Indicate if channel spectrum is mirrored or not.
+*/
+enum drx_mirror {
+ DRX_MIRROR_NO = 0, /**< Spectrum is not mirrored. */
+ DRX_MIRROR_YES, /**< Spectrum is mirrored. */
+ DRX_MIRROR_UNKNOWN = DRX_UNKNOWN,
+ /**< Unknown if spectrum is mirrored. */
+ DRX_MIRROR_AUTO = DRX_AUTO
+ /**< Autodetect if spectrum is mirrored. */
+};
+
+/**
+* \enum enum drx_modulation
+* \brief Constellation type of the channel.
+*/
+enum drx_modulation {
+ DRX_CONSTELLATION_BPSK = 0, /**< Modulation is BPSK. */
+ DRX_CONSTELLATION_QPSK, /**< Constellation is QPSK. */
+ DRX_CONSTELLATION_PSK8, /**< Constellation is PSK8. */
+ DRX_CONSTELLATION_QAM16, /**< Constellation is QAM16. */
+ DRX_CONSTELLATION_QAM32, /**< Constellation is QAM32. */
+ DRX_CONSTELLATION_QAM64, /**< Constellation is QAM64. */
+ DRX_CONSTELLATION_QAM128, /**< Constellation is QAM128. */
+ DRX_CONSTELLATION_QAM256, /**< Constellation is QAM256. */
+ DRX_CONSTELLATION_QAM512, /**< Constellation is QAM512. */
+ DRX_CONSTELLATION_QAM1024, /**< Constellation is QAM1024. */
+ DRX_CONSTELLATION_QPSK_NR, /**< Constellation is QPSK_NR */
+ DRX_CONSTELLATION_UNKNOWN = DRX_UNKNOWN,
+ /**< Constellation unknown. */
+ DRX_CONSTELLATION_AUTO = DRX_AUTO
+ /**< Autodetect constellation. */
+};
+
+/**
+* \enum enum drx_hierarchy
+* \brief Hierarchy of the channel.
+*/
+enum drx_hierarchy {
+ DRX_HIERARCHY_NONE = 0, /**< None hierarchical channel. */
+ DRX_HIERARCHY_ALPHA1, /**< Hierarchical channel, alpha=1. */
+ DRX_HIERARCHY_ALPHA2, /**< Hierarchical channel, alpha=2. */
+ DRX_HIERARCHY_ALPHA4, /**< Hierarchical channel, alpha=4. */
+ DRX_HIERARCHY_UNKNOWN = DRX_UNKNOWN,
+ /**< Hierarchy unknown. */
+ DRX_HIERARCHY_AUTO = DRX_AUTO
+ /**< Autodetect hierarchy. */
+};
+
+/**
+* \enum enum drx_priority
+* \brief Channel priority in case of hierarchical transmission.
+*/
+enum drx_priority {
+ DRX_PRIORITY_LOW = 0, /**< Low priority channel. */
+ DRX_PRIORITY_HIGH, /**< High priority channel. */
+ DRX_PRIORITY_UNKNOWN = DRX_UNKNOWN
+ /**< Priority unknown. */
+};
+
+/**
+* \enum enum drx_coderate
+* \brief Channel priority in case of hierarchical transmission.
+*/
+enum drx_coderate {
+ DRX_CODERATE_1DIV2 = 0, /**< Code rate 1/2nd. */
+ DRX_CODERATE_2DIV3, /**< Code rate 2/3nd. */
+ DRX_CODERATE_3DIV4, /**< Code rate 3/4nd. */
+ DRX_CODERATE_5DIV6, /**< Code rate 5/6nd. */
+ DRX_CODERATE_7DIV8, /**< Code rate 7/8nd. */
+ DRX_CODERATE_UNKNOWN = DRX_UNKNOWN,
+ /**< Code rate unknown. */
+ DRX_CODERATE_AUTO = DRX_AUTO
+ /**< Autodetect code rate. */
+};
+
+/**
+* \enum enum drx_guard
+* \brief Guard interval of a channel.
+*/
+enum drx_guard {
+ DRX_GUARD_1DIV32 = 0, /**< Guard interval 1/32nd. */
+ DRX_GUARD_1DIV16, /**< Guard interval 1/16th. */
+ DRX_GUARD_1DIV8, /**< Guard interval 1/8th. */
+ DRX_GUARD_1DIV4, /**< Guard interval 1/4th. */
+ DRX_GUARD_UNKNOWN = DRX_UNKNOWN,
+ /**< Guard interval unknown. */
+ DRX_GUARD_AUTO = DRX_AUTO
+ /**< Autodetect guard interval. */
+};
+
+/**
+* \enum enum drx_fft_mode
+* \brief FFT mode.
+*/
+enum drx_fft_mode {
+ DRX_FFTMODE_2K = 0, /**< 2K FFT mode. */
+ DRX_FFTMODE_4K, /**< 4K FFT mode. */
+ DRX_FFTMODE_8K, /**< 8K FFT mode. */
+ DRX_FFTMODE_UNKNOWN = DRX_UNKNOWN,
+ /**< FFT mode unknown. */
+ DRX_FFTMODE_AUTO = DRX_AUTO
+ /**< Autodetect FFT mode. */
+};
+
+/**
+* \enum enum drx_classification
+* \brief Channel classification.
+*/
+enum drx_classification {
+ DRX_CLASSIFICATION_GAUSS = 0, /**< Gaussion noise. */
+ DRX_CLASSIFICATION_HVY_GAUSS, /**< Heavy Gaussion noise. */
+ DRX_CLASSIFICATION_COCHANNEL, /**< Co-channel. */
+ DRX_CLASSIFICATION_STATIC, /**< Static echo. */
+ DRX_CLASSIFICATION_MOVING, /**< Moving echo. */
+ DRX_CLASSIFICATION_ZERODB, /**< Zero dB echo. */
+ DRX_CLASSIFICATION_UNKNOWN = DRX_UNKNOWN,
+ /**< Unknown classification */
+ DRX_CLASSIFICATION_AUTO = DRX_AUTO
+ /**< Autodetect classification. */
+};
+
+/**
+* /enum enum drx_interleave_mode
+* /brief Interleave modes
+*/
+enum drx_interleave_mode {
+ DRX_INTERLEAVEMODE_I128_J1 = 0,
+ DRX_INTERLEAVEMODE_I128_J1_V2,
+ DRX_INTERLEAVEMODE_I128_J2,
+ DRX_INTERLEAVEMODE_I64_J2,
+ DRX_INTERLEAVEMODE_I128_J3,
+ DRX_INTERLEAVEMODE_I32_J4,
+ DRX_INTERLEAVEMODE_I128_J4,
+ DRX_INTERLEAVEMODE_I16_J8,
+ DRX_INTERLEAVEMODE_I128_J5,
+ DRX_INTERLEAVEMODE_I8_J16,
+ DRX_INTERLEAVEMODE_I128_J6,
+ DRX_INTERLEAVEMODE_RESERVED_11,
+ DRX_INTERLEAVEMODE_I128_J7,
+ DRX_INTERLEAVEMODE_RESERVED_13,
+ DRX_INTERLEAVEMODE_I128_J8,
+ DRX_INTERLEAVEMODE_RESERVED_15,
+ DRX_INTERLEAVEMODE_I12_J17,
+ DRX_INTERLEAVEMODE_I5_J4,
+ DRX_INTERLEAVEMODE_B52_M240,
+ DRX_INTERLEAVEMODE_B52_M720,
+ DRX_INTERLEAVEMODE_B52_M48,
+ DRX_INTERLEAVEMODE_B52_M0,
+ DRX_INTERLEAVEMODE_UNKNOWN = DRX_UNKNOWN,
+ /**< Unknown interleave mode */
+ DRX_INTERLEAVEMODE_AUTO = DRX_AUTO
+ /**< Autodetect interleave mode */
+};
+
+/**
+* \enum enum drx_carrier_mode
+* \brief Channel Carrier Mode.
+*/
+enum drx_carrier_mode {
+ DRX_CARRIER_MULTI = 0, /**< Multi carrier mode */
+ DRX_CARRIER_SINGLE, /**< Single carrier mode */
+ DRX_CARRIER_UNKNOWN = DRX_UNKNOWN,
+ /**< Carrier mode unknown. */
+ DRX_CARRIER_AUTO = DRX_AUTO /**< Autodetect carrier mode */
+};
+
+/**
+* \enum enum drx_frame_mode
+* \brief Channel Frame Mode.
+*/
+enum drx_frame_mode {
+ DRX_FRAMEMODE_420 = 0, /**< 420 with variable PN */
+ DRX_FRAMEMODE_595, /**< 595 */
+ DRX_FRAMEMODE_945, /**< 945 with variable PN */
+ DRX_FRAMEMODE_420_FIXED_PN,
+ /**< 420 with fixed PN */
+ DRX_FRAMEMODE_945_FIXED_PN,
+ /**< 945 with fixed PN */
+ DRX_FRAMEMODE_UNKNOWN = DRX_UNKNOWN,
+ /**< Frame mode unknown. */
+ DRX_FRAMEMODE_AUTO = DRX_AUTO
+ /**< Autodetect frame mode */
+};
+
+/**
+* \enum enum drx_tps_frame
+* \brief Frame number in current super-frame.
+*/
+enum drx_tps_frame {
+ DRX_TPS_FRAME1 = 0, /**< TPS frame 1. */
+ DRX_TPS_FRAME2, /**< TPS frame 2. */
+ DRX_TPS_FRAME3, /**< TPS frame 3. */
+ DRX_TPS_FRAME4, /**< TPS frame 4. */
+ DRX_TPS_FRAME_UNKNOWN = DRX_UNKNOWN
+ /**< TPS frame unknown. */
+};
+
+/**
+* \enum enum drx_ldpc
+* \brief TPS LDPC .
+*/
+enum drx_ldpc {
+ DRX_LDPC_0_4 = 0, /**< LDPC 0.4 */
+ DRX_LDPC_0_6, /**< LDPC 0.6 */
+ DRX_LDPC_0_8, /**< LDPC 0.8 */
+ DRX_LDPC_UNKNOWN = DRX_UNKNOWN,
+ /**< LDPC unknown. */
+ DRX_LDPC_AUTO = DRX_AUTO /**< Autodetect LDPC */
+};
+
+/**
+* \enum enum drx_pilot_mode
+* \brief Pilot modes in DTMB.
+*/
+enum drx_pilot_mode {
+ DRX_PILOT_ON = 0, /**< Pilot On */
+ DRX_PILOT_OFF, /**< Pilot Off */
+ DRX_PILOT_UNKNOWN = DRX_UNKNOWN,
+ /**< Pilot unknown. */
+ DRX_PILOT_AUTO = DRX_AUTO /**< Autodetect Pilot */
+};
+
+/**
+ * enum drxu_code_action - indicate if firmware has to be uploaded or verified.
+ * @UCODE_UPLOAD: Upload the microcode image to device
+ * @UCODE_VERIFY: Compare microcode image with code on device
+ */
+enum drxu_code_action {
+ UCODE_UPLOAD,
+ UCODE_VERIFY
+};
+
+/**
+* \enum enum drx_lock_status * \brief Used to reflect current lock status of demodulator.
+*
+* The generic lock states have device dependent semantics.
+
+ DRX_NEVER_LOCK = 0,
+ **< Device will never lock on this signal *
+ DRX_NOT_LOCKED,
+ **< Device has no lock at all *
+ DRX_LOCK_STATE_1,
+ **< Generic lock state *
+ DRX_LOCK_STATE_2,
+ **< Generic lock state *
+ DRX_LOCK_STATE_3,
+ **< Generic lock state *
+ DRX_LOCK_STATE_4,
+ **< Generic lock state *
+ DRX_LOCK_STATE_5,
+ **< Generic lock state *
+ DRX_LOCK_STATE_6,
+ **< Generic lock state *
+ DRX_LOCK_STATE_7,
+ **< Generic lock state *
+ DRX_LOCK_STATE_8,
+ **< Generic lock state *
+ DRX_LOCK_STATE_9,
+ **< Generic lock state *
+ DRX_LOCKED **< Device is in lock *
+*/
+
+enum drx_lock_status {
+ DRX_NEVER_LOCK = 0,
+ DRX_NOT_LOCKED,
+ DRX_LOCK_STATE_1,
+ DRX_LOCK_STATE_2,
+ DRX_LOCK_STATE_3,
+ DRX_LOCK_STATE_4,
+ DRX_LOCK_STATE_5,
+ DRX_LOCK_STATE_6,
+ DRX_LOCK_STATE_7,
+ DRX_LOCK_STATE_8,
+ DRX_LOCK_STATE_9,
+ DRX_LOCKED
+};
+
+/**
+* \enum enum drx_uio* \brief Used to address a User IO (UIO).
+*/
+enum drx_uio {
+ DRX_UIO1,
+ DRX_UIO2,
+ DRX_UIO3,
+ DRX_UIO4,
+ DRX_UIO5,
+ DRX_UIO6,
+ DRX_UIO7,
+ DRX_UIO8,
+ DRX_UIO9,
+ DRX_UIO10,
+ DRX_UIO11,
+ DRX_UIO12,
+ DRX_UIO13,
+ DRX_UIO14,
+ DRX_UIO15,
+ DRX_UIO16,
+ DRX_UIO17,
+ DRX_UIO18,
+ DRX_UIO19,
+ DRX_UIO20,
+ DRX_UIO21,
+ DRX_UIO22,
+ DRX_UIO23,
+ DRX_UIO24,
+ DRX_UIO25,
+ DRX_UIO26,
+ DRX_UIO27,
+ DRX_UIO28,
+ DRX_UIO29,
+ DRX_UIO30,
+ DRX_UIO31,
+ DRX_UIO32,
+ DRX_UIO_MAX = DRX_UIO32
+};
+
+/**
+* \enum enum drxuio_mode * \brief Used to configure the modus oprandi of a UIO.
+*
+* DRX_UIO_MODE_FIRMWARE is an old uio mode.
+* It is replaced by the modes DRX_UIO_MODE_FIRMWARE0 .. DRX_UIO_MODE_FIRMWARE9.
+* To be backward compatible DRX_UIO_MODE_FIRMWARE is equivalent to
+* DRX_UIO_MODE_FIRMWARE0.
+*/
+enum drxuio_mode {
+ DRX_UIO_MODE_DISABLE = 0x01,
+ /**< not used, pin is configured as input */
+ DRX_UIO_MODE_READWRITE = 0x02,
+ /**< used for read/write by application */
+ DRX_UIO_MODE_FIRMWARE = 0x04,
+ /**< controlled by firmware, function 0 */
+ DRX_UIO_MODE_FIRMWARE0 = DRX_UIO_MODE_FIRMWARE,
+ /**< same as above */
+ DRX_UIO_MODE_FIRMWARE1 = 0x08,
+ /**< controlled by firmware, function 1 */
+ DRX_UIO_MODE_FIRMWARE2 = 0x10,
+ /**< controlled by firmware, function 2 */
+ DRX_UIO_MODE_FIRMWARE3 = 0x20,
+ /**< controlled by firmware, function 3 */
+ DRX_UIO_MODE_FIRMWARE4 = 0x40,
+ /**< controlled by firmware, function 4 */
+ DRX_UIO_MODE_FIRMWARE5 = 0x80
+ /**< controlled by firmware, function 5 */
+};
+
+/**
+* \enum enum drxoob_downstream_standard * \brief Used to select OOB standard.
+*
+* Based on ANSI 55-1 and 55-2
+*/
+enum drxoob_downstream_standard {
+ DRX_OOB_MODE_A = 0,
+ /**< ANSI 55-1 */
+ DRX_OOB_MODE_B_GRADE_A,
+ /**< ANSI 55-2 A */
+ DRX_OOB_MODE_B_GRADE_B
+ /**< ANSI 55-2 B */
+};
+
+/*-------------------------------------------------------------------------
+STRUCTS
+-------------------------------------------------------------------------*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== CTRL CFG related data structures ========================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+#ifndef DRX_CFG_BASE
+#define DRX_CFG_BASE 0
+#endif
+
+#define DRX_CFG_MPEG_OUTPUT (DRX_CFG_BASE + 0) /* MPEG TS output */
+#define DRX_CFG_PKTERR (DRX_CFG_BASE + 1) /* Packet Error */
+#define DRX_CFG_SYMCLK_OFFS (DRX_CFG_BASE + 2) /* Symbol Clk Offset */
+#define DRX_CFG_SMA (DRX_CFG_BASE + 3) /* Smart Antenna */
+#define DRX_CFG_PINSAFE (DRX_CFG_BASE + 4) /* Pin safe mode */
+#define DRX_CFG_SUBSTANDARD (DRX_CFG_BASE + 5) /* substandard */
+#define DRX_CFG_AUD_VOLUME (DRX_CFG_BASE + 6) /* volume */
+#define DRX_CFG_AUD_RDS (DRX_CFG_BASE + 7) /* rds */
+#define DRX_CFG_AUD_AUTOSOUND (DRX_CFG_BASE + 8) /* ASS & ASC */
+#define DRX_CFG_AUD_ASS_THRES (DRX_CFG_BASE + 9) /* ASS Thresholds */
+#define DRX_CFG_AUD_DEVIATION (DRX_CFG_BASE + 10) /* Deviation */
+#define DRX_CFG_AUD_PRESCALE (DRX_CFG_BASE + 11) /* Prescale */
+#define DRX_CFG_AUD_MIXER (DRX_CFG_BASE + 12) /* Mixer */
+#define DRX_CFG_AUD_AVSYNC (DRX_CFG_BASE + 13) /* AVSync */
+#define DRX_CFG_AUD_CARRIER (DRX_CFG_BASE + 14) /* Audio carriers */
+#define DRX_CFG_I2S_OUTPUT (DRX_CFG_BASE + 15) /* I2S output */
+#define DRX_CFG_ATV_STANDARD (DRX_CFG_BASE + 16) /* ATV standard */
+#define DRX_CFG_SQI_SPEED (DRX_CFG_BASE + 17) /* SQI speed */
+#define DRX_CTRL_CFG_MAX (DRX_CFG_BASE + 18) /* never to be used */
+
+#define DRX_CFG_PINS_SAFE_MODE DRX_CFG_PINSAFE
+/*============================================================================*/
+/*============================================================================*/
+/*== CTRL related data structures ============================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/**
+ * struct drxu_code_info Parameters for microcode upload and verfiy.
+ *
+ * @mc_file: microcode file name
+ *
+ * Used by DRX_CTRL_LOAD_UCODE and DRX_CTRL_VERIFY_UCODE
+ */
+struct drxu_code_info {
+ char *mc_file;
+};
+
+/**
+* \struct drx_mc_version_rec_t
+* \brief Microcode version record
+* Version numbers are stored in BCD format, as usual:
+* o major number = bits 31-20 (first three nibbles of MSW)
+* o minor number = bits 19-16 (fourth nibble of MSW)
+* o patch number = bits 15-0 (remaining nibbles in LSW)
+*
+* The device type indicates for which the device is meant. It is based on the
+* JTAG ID, using everything except the bond ID and the metal fix.
+*
+* Special values:
+* - mc_dev_type == 0 => any device allowed
+* - mc_base_version == 0.0.0 => full microcode (mc_version is the version)
+* - mc_base_version != 0.0.0 => patch microcode, the base microcode version
+* (mc_version is the version)
+*/
+#define AUX_VER_RECORD 0x8000
+
+struct drx_mc_version_rec {
+ u16 aux_type; /* type of aux data - 0x8000 for version record */
+ u32 mc_dev_type; /* device type, based on JTAG ID */
+ u32 mc_version; /* version of microcode */
+ u32 mc_base_version; /* in case of patch: the original microcode version */
+};
+
+/*========================================*/
+
+/**
+* \struct drx_filter_info_t
+* \brief Parameters for loading filter coefficients
+*
+* Used by DRX_CTRL_LOAD_FILTER
+*/
+struct drx_filter_info {
+ u8 *data_re;
+ /**< pointer to coefficients for RE */
+ u8 *data_im;
+ /**< pointer to coefficients for IM */
+ u16 size_re;
+ /**< size of coefficients for RE */
+ u16 size_im;
+ /**< size of coefficients for IM */
+};
+
+/*========================================*/
+
+/**
+* \struct struct drx_channel * \brief The set of parameters describing a single channel.
+*
+* Used by DRX_CTRL_SET_CHANNEL and DRX_CTRL_GET_CHANNEL.
+* Only certain fields need to be used for a specfic standard.
+*
+*/
+struct drx_channel {
+ s32 frequency;
+ /**< frequency in kHz */
+ enum drx_bandwidth bandwidth;
+ /**< bandwidth */
+ enum drx_mirror mirror; /**< mirrored or not on RF */
+ enum drx_modulation constellation;
+ /**< constellation */
+ enum drx_hierarchy hierarchy;
+ /**< hierarchy */
+ enum drx_priority priority; /**< priority */
+ enum drx_coderate coderate; /**< coderate */
+ enum drx_guard guard; /**< guard interval */
+ enum drx_fft_mode fftmode; /**< fftmode */
+ enum drx_classification classification;
+ /**< classification */
+ u32 symbolrate;
+ /**< symbolrate in symbols/sec */
+ enum drx_interleave_mode interleavemode;
+ /**< interleaveMode QAM */
+ enum drx_ldpc ldpc; /**< ldpc */
+ enum drx_carrier_mode carrier; /**< carrier */
+ enum drx_frame_mode framemode;
+ /**< frame mode */
+ enum drx_pilot_mode pilot; /**< pilot mode */
+};
+
+/*========================================*/
+
+enum drx_cfg_sqi_speed {
+ DRX_SQI_SPEED_FAST = 0,
+ DRX_SQI_SPEED_MEDIUM,
+ DRX_SQI_SPEED_SLOW,
+ DRX_SQI_SPEED_UNKNOWN = DRX_UNKNOWN
+};
+
+/*========================================*/
+
+/**
+* \struct struct drx_complex * A complex number.
+*
+* Used by DRX_CTRL_CONSTEL.
+*/
+struct drx_complex {
+ s16 im;
+ /**< Imaginary part. */
+ s16 re;
+ /**< Real part. */
+};
+
+/*========================================*/
+
+/**
+* \struct struct drx_frequency_plan * Array element of a frequency plan.
+*
+* Used by DRX_CTRL_SCAN_INIT.
+*/
+struct drx_frequency_plan {
+ s32 first;
+ /**< First centre frequency in this band */
+ s32 last;
+ /**< Last centre frequency in this band */
+ s32 step;
+ /**< Stepping frequency in this band */
+ enum drx_bandwidth bandwidth;
+ /**< Bandwidth within this frequency band */
+ u16 ch_number;
+ /**< First channel number in this band, or first
+ index in ch_names */
+ char **ch_names;
+ /**< Optional list of channel names in this
+ band */
+};
+
+/*========================================*/
+
+/**
+* \struct struct drx_scan_param * Parameters for channel scan.
+*
+* Used by DRX_CTRL_SCAN_INIT.
+*/
+struct drx_scan_param {
+ struct drx_frequency_plan *frequency_plan;
+ /**< Frequency plan (array)*/
+ u16 frequency_plan_size; /**< Number of bands */
+ u32 num_tries; /**< Max channels tried */
+ s32 skip; /**< Minimum frequency step to take
+ after a channel is found */
+ void *ext_params; /**< Standard specific params */
+};
+
+/*========================================*/
+
+/**
+* \brief Scan commands.
+* Used by scanning algorithms.
+*/
+enum drx_scan_command {
+ DRX_SCAN_COMMAND_INIT = 0,/**< Initialize scanning */
+ DRX_SCAN_COMMAND_NEXT, /**< Next scan */
+ DRX_SCAN_COMMAND_STOP /**< Stop scanning */
+};
+
+/*========================================*/
+
+/**
+* \brief Inner scan function prototype.
+*/
+typedef int(*drx_scan_func_t) (void *scan_context,
+ enum drx_scan_command scan_command,
+ struct drx_channel *scan_channel,
+ bool *get_next_channel);
+
+/*========================================*/
+
+/**
+* \struct struct drxtps_info * TPS information, DVB-T specific.
+*
+* Used by DRX_CTRL_TPS_INFO.
+*/
+ struct drxtps_info {
+ enum drx_fft_mode fftmode; /**< Fft mode */
+ enum drx_guard guard; /**< Guard interval */
+ enum drx_modulation constellation;
+ /**< Constellation */
+ enum drx_hierarchy hierarchy;
+ /**< Hierarchy */
+ enum drx_coderate high_coderate;
+ /**< High code rate */
+ enum drx_coderate low_coderate;
+ /**< Low cod rate */
+ enum drx_tps_frame frame; /**< Tps frame */
+ u8 length; /**< Length */
+ u16 cell_id; /**< Cell id */
+ };
+
+/*========================================*/
+
+/**
+* \brief Power mode of device.
+*
+* Used by DRX_CTRL_SET_POWER_MODE.
+*/
+ enum drx_power_mode {
+ DRX_POWER_UP = 0,
+ /**< Generic , Power Up Mode */
+ DRX_POWER_MODE_1,
+ /**< Device specific , Power Up Mode */
+ DRX_POWER_MODE_2,
+ /**< Device specific , Power Up Mode */
+ DRX_POWER_MODE_3,
+ /**< Device specific , Power Up Mode */
+ DRX_POWER_MODE_4,
+ /**< Device specific , Power Up Mode */
+ DRX_POWER_MODE_5,
+ /**< Device specific , Power Up Mode */
+ DRX_POWER_MODE_6,
+ /**< Device specific , Power Up Mode */
+ DRX_POWER_MODE_7,
+ /**< Device specific , Power Up Mode */
+ DRX_POWER_MODE_8,
+ /**< Device specific , Power Up Mode */
+
+ DRX_POWER_MODE_9,
+ /**< Device specific , Power Down Mode */
+ DRX_POWER_MODE_10,
+ /**< Device specific , Power Down Mode */
+ DRX_POWER_MODE_11,
+ /**< Device specific , Power Down Mode */
+ DRX_POWER_MODE_12,
+ /**< Device specific , Power Down Mode */
+ DRX_POWER_MODE_13,
+ /**< Device specific , Power Down Mode */
+ DRX_POWER_MODE_14,
+ /**< Device specific , Power Down Mode */
+ DRX_POWER_MODE_15,
+ /**< Device specific , Power Down Mode */
+ DRX_POWER_MODE_16,
+ /**< Device specific , Power Down Mode */
+ DRX_POWER_DOWN = 255
+ /**< Generic , Power Down Mode */
+ };
+
+/*========================================*/
+
+/**
+* \enum enum drx_module * \brief Software module identification.
+*
+* Used by DRX_CTRL_VERSION.
+*/
+ enum drx_module {
+ DRX_MODULE_DEVICE,
+ DRX_MODULE_MICROCODE,
+ DRX_MODULE_DRIVERCORE,
+ DRX_MODULE_DEVICEDRIVER,
+ DRX_MODULE_DAP,
+ DRX_MODULE_BSP_I2C,
+ DRX_MODULE_BSP_TUNER,
+ DRX_MODULE_BSP_HOST,
+ DRX_MODULE_UNKNOWN
+ };
+
+/**
+* \enum struct drx_version * \brief Version information of one software module.
+*
+* Used by DRX_CTRL_VERSION.
+*/
+ struct drx_version {
+ enum drx_module module_type;
+ /**< Type identifier of the module */
+ char *module_name;
+ /**< Name or description of module */
+ u16 v_major; /**< Major version number */
+ u16 v_minor; /**< Minor version number */
+ u16 v_patch; /**< Patch version number */
+ char *v_string; /**< Version as text string */
+ };
+
+/**
+* \enum struct drx_version_list * \brief List element of NULL terminated, linked list for version information.
+*
+* Used by DRX_CTRL_VERSION.
+*/
+struct drx_version_list {
+ struct drx_version *version;/**< Version information */
+ struct drx_version_list *next;
+ /**< Next list element */
+};
+
+/*========================================*/
+
+/**
+* \brief Parameters needed to confiugure a UIO.
+*
+* Used by DRX_CTRL_UIO_CFG.
+*/
+ struct drxuio_cfg {
+ enum drx_uio uio;
+ /**< UIO identifier */
+ enum drxuio_mode mode;
+ /**< UIO operational mode */
+ };
+
+/*========================================*/
+
+/**
+* \brief Parameters needed to read from or write to a UIO.
+*
+* Used by DRX_CTRL_UIO_READ and DRX_CTRL_UIO_WRITE.
+*/
+ struct drxuio_data {
+ enum drx_uio uio;
+ /**< UIO identifier */
+ bool value;
+ /**< UIO value (true=1, false=0) */
+ };
+
+/*========================================*/
+
+/**
+* \brief Parameters needed to configure OOB.
+*
+* Used by DRX_CTRL_SET_OOB.
+*/
+ struct drxoob {
+ s32 frequency; /**< Frequency in kHz */
+ enum drxoob_downstream_standard standard;
+ /**< OOB standard */
+ bool spectrum_inverted; /**< If true, then spectrum
+ is inverted */
+ };
+
+/*========================================*/
+
+/**
+* \brief Metrics from OOB.
+*
+* Used by DRX_CTRL_GET_OOB.
+*/
+ struct drxoob_status {
+ s32 frequency; /**< Frequency in Khz */
+ enum drx_lock_status lock; /**< Lock status */
+ u32 mer; /**< MER */
+ s32 symbol_rate_offset; /**< Symbolrate offset in ppm */
+ };
+
+/*========================================*/
+
+/**
+* \brief Device dependent configuration data.
+*
+* Used by DRX_CTRL_SET_CFG and DRX_CTRL_GET_CFG.
+* A sort of nested drx_ctrl() functionality for device specific controls.
+*/
+ struct drx_cfg {
+ u32 cfg_type;
+ /**< Function identifier */
+ void *cfg_data;
+ /**< Function data */
+ };
+
+/*========================================*/
+
+/**
+* /struct DRXMpegStartWidth_t
+* MStart width [nr MCLK cycles] for serial MPEG output.
+*/
+
+ enum drxmpeg_str_width {
+ DRX_MPEG_STR_WIDTH_1,
+ DRX_MPEG_STR_WIDTH_8
+ };
+
+/* CTRL CFG MPEG ouput */
+/**
+* \struct struct drx_cfg_mpeg_output * \brief Configuartion parameters for MPEG output control.
+*
+* Used by DRX_CFG_MPEG_OUTPUT, in combination with DRX_CTRL_SET_CFG and
+* DRX_CTRL_GET_CFG.
+*/
+
+ struct drx_cfg_mpeg_output {
+ bool enable_mpeg_output;/**< If true, enable MPEG output */
+ bool insert_rs_byte; /**< If true, insert RS byte */
+ bool enable_parallel; /**< If true, parallel out otherwise
+ serial */
+ bool invert_data; /**< If true, invert DATA signals */
+ bool invert_err; /**< If true, invert ERR signal */
+ bool invert_str; /**< If true, invert STR signals */
+ bool invert_val; /**< If true, invert VAL signals */
+ bool invert_clk; /**< If true, invert CLK signals */
+ bool static_clk; /**< If true, static MPEG clockrate
+ will be used, otherwise clockrate
+ will adapt to the bitrate of the
+ TS */
+ u32 bitrate; /**< Maximum bitrate in b/s in case
+ static clockrate is selected */
+ enum drxmpeg_str_width width_str;
+ /**< MPEG start width */
+ };
+
+
+/*========================================*/
+
+/**
+* \struct struct drxi2c_data * \brief Data for I2C via 2nd or 3rd or etc I2C port.
+*
+* Used by DRX_CTRL_I2C_READWRITE.
+* If port_nr is equal to primairy port_nr BSPI2C will be used.
+*
+*/
+ struct drxi2c_data {
+ u16 port_nr; /**< I2C port number */
+ struct i2c_device_addr *w_dev_addr;
+ /**< Write device address */
+ u16 w_count; /**< Size of write data in bytes */
+ u8 *wData; /**< Pointer to write data */
+ struct i2c_device_addr *r_dev_addr;
+ /**< Read device address */
+ u16 r_count; /**< Size of data to read in bytes */
+ u8 *r_data; /**< Pointer to read buffer */
+ };
+
+/*========================================*/
+
+/**
+* \enum enum drx_aud_standard * \brief Audio standard identifier.
+*
+* Used by DRX_CTRL_SET_AUD.
+*/
+ enum drx_aud_standard {
+ DRX_AUD_STANDARD_BTSC, /**< set BTSC standard (USA) */
+ DRX_AUD_STANDARD_A2, /**< set A2-Korea FM Stereo */
+ DRX_AUD_STANDARD_EIAJ, /**< set to Japanese FM Stereo */
+ DRX_AUD_STANDARD_FM_STEREO,/**< set to FM-Stereo Radio */
+ DRX_AUD_STANDARD_M_MONO, /**< for 4.5 MHz mono detected */
+ DRX_AUD_STANDARD_D_K_MONO, /**< for 6.5 MHz mono detected */
+ DRX_AUD_STANDARD_BG_FM, /**< set BG_FM standard */
+ DRX_AUD_STANDARD_D_K1, /**< set D_K1 standard */
+ DRX_AUD_STANDARD_D_K2, /**< set D_K2 standard */
+ DRX_AUD_STANDARD_D_K3, /**< set D_K3 standard */
+ DRX_AUD_STANDARD_BG_NICAM_FM,
+ /**< set BG_NICAM_FM standard */
+ DRX_AUD_STANDARD_L_NICAM_AM,
+ /**< set L_NICAM_AM standard */
+ DRX_AUD_STANDARD_I_NICAM_FM,
+ /**< set I_NICAM_FM standard */
+ DRX_AUD_STANDARD_D_K_NICAM_FM,
+ /**< set D_K_NICAM_FM standard */
+ DRX_AUD_STANDARD_NOT_READY,/**< used to detect audio standard */
+ DRX_AUD_STANDARD_AUTO = DRX_AUTO,
+ /**< Automatic Standard Detection */
+ DRX_AUD_STANDARD_UNKNOWN = DRX_UNKNOWN
+ /**< used as auto and for readback */
+ };
+
+/* CTRL_AUD_GET_STATUS - struct drx_aud_status */
+/**
+* \enum enum drx_aud_nicam_status * \brief Status of NICAM carrier.
+*/
+ enum drx_aud_nicam_status {
+ DRX_AUD_NICAM_DETECTED = 0,
+ /**< NICAM carrier detected */
+ DRX_AUD_NICAM_NOT_DETECTED,
+ /**< NICAM carrier not detected */
+ DRX_AUD_NICAM_BAD /**< NICAM carrier bad quality */
+ };
+
+/**
+* \struct struct drx_aud_status * \brief Audio status characteristics.
+*/
+ struct drx_aud_status {
+ bool stereo; /**< stereo detection */
+ bool carrier_a; /**< carrier A detected */
+ bool carrier_b; /**< carrier B detected */
+ bool sap; /**< sap / bilingual detection */
+ bool rds; /**< RDS data array present */
+ enum drx_aud_nicam_status nicam_status;
+ /**< status of NICAM carrier */
+ s8 fm_ident; /**< FM Identification value */
+ };
+
+/* CTRL_AUD_READ_RDS - DRXRDSdata_t */
+
+/**
+* \struct DRXRDSdata_t
+* \brief Raw RDS data array.
+*/
+ struct drx_cfg_aud_rds {
+ bool valid; /**< RDS data validation */
+ u16 data[18]; /**< data from one RDS data array */
+ };
+
+/* DRX_CFG_AUD_VOLUME - struct drx_cfg_aud_volume - set/get */
+/**
+* \enum DRXAudAVCDecayTime_t
+* \brief Automatic volume control configuration.
+*/
+ enum drx_aud_avc_mode {
+ DRX_AUD_AVC_OFF, /**< Automatic volume control off */
+ DRX_AUD_AVC_DECAYTIME_8S, /**< level volume in 8 seconds */
+ DRX_AUD_AVC_DECAYTIME_4S, /**< level volume in 4 seconds */
+ DRX_AUD_AVC_DECAYTIME_2S, /**< level volume in 2 seconds */
+ DRX_AUD_AVC_DECAYTIME_20MS/**< level volume in 20 millisec */
+ };
+
+/**
+* /enum DRXAudMaxAVCGain_t
+* /brief Automatic volume control max gain in audio baseband.
+*/
+ enum drx_aud_avc_max_gain {
+ DRX_AUD_AVC_MAX_GAIN_0DB, /**< maximum AVC gain 0 dB */
+ DRX_AUD_AVC_MAX_GAIN_6DB, /**< maximum AVC gain 6 dB */
+ DRX_AUD_AVC_MAX_GAIN_12DB /**< maximum AVC gain 12 dB */
+ };
+
+/**
+* /enum DRXAudMaxAVCAtten_t
+* /brief Automatic volume control max attenuation in audio baseband.
+*/
+ enum drx_aud_avc_max_atten {
+ DRX_AUD_AVC_MAX_ATTEN_12DB,
+ /**< maximum AVC attenuation 12 dB */
+ DRX_AUD_AVC_MAX_ATTEN_18DB,
+ /**< maximum AVC attenuation 18 dB */
+ DRX_AUD_AVC_MAX_ATTEN_24DB/**< maximum AVC attenuation 24 dB */
+ };
+/**
+* \struct struct drx_cfg_aud_volume * \brief Audio volume configuration.
+*/
+ struct drx_cfg_aud_volume {
+ bool mute; /**< mute overrides volume setting */
+ s16 volume; /**< volume, range -114 to 12 dB */
+ enum drx_aud_avc_mode avc_mode; /**< AVC auto volume control mode */
+ u16 avc_ref_level; /**< AVC reference level */
+ enum drx_aud_avc_max_gain avc_max_gain;
+ /**< AVC max gain selection */
+ enum drx_aud_avc_max_atten avc_max_atten;
+ /**< AVC max attenuation selection */
+ s16 strength_left; /**< quasi-peak, left speaker */
+ s16 strength_right; /**< quasi-peak, right speaker */
+ };
+
+/* DRX_CFG_I2S_OUTPUT - struct drx_cfg_i2s_output - set/get */
+/**
+* \enum enum drxi2s_mode * \brief I2S output mode.
+*/
+ enum drxi2s_mode {
+ DRX_I2S_MODE_MASTER, /**< I2S is in master mode */
+ DRX_I2S_MODE_SLAVE /**< I2S is in slave mode */
+ };
+
+/**
+* \enum enum drxi2s_word_length * \brief Width of I2S data.
+*/
+ enum drxi2s_word_length {
+ DRX_I2S_WORDLENGTH_32 = 0,/**< I2S data is 32 bit wide */
+ DRX_I2S_WORDLENGTH_16 = 1 /**< I2S data is 16 bit wide */
+ };
+
+/**
+* \enum enum drxi2s_format * \brief Data wordstrobe alignment for I2S.
+*/
+ enum drxi2s_format {
+ DRX_I2S_FORMAT_WS_WITH_DATA,
+ /**< I2S data and wordstrobe are aligned */
+ DRX_I2S_FORMAT_WS_ADVANCED
+ /**< I2S data one cycle after wordstrobe */
+ };
+
+/**
+* \enum enum drxi2s_polarity * \brief Polarity of I2S data.
+*/
+ enum drxi2s_polarity {
+ DRX_I2S_POLARITY_RIGHT,/**< wordstrobe - right high, left low */
+ DRX_I2S_POLARITY_LEFT /**< wordstrobe - right low, left high */
+ };
+
+/**
+* \struct struct drx_cfg_i2s_output * \brief I2S output configuration.
+*/
+ struct drx_cfg_i2s_output {
+ bool output_enable; /**< I2S output enable */
+ u32 frequency; /**< range from 8000-48000 Hz */
+ enum drxi2s_mode mode; /**< I2S mode, master or slave */
+ enum drxi2s_word_length word_length;
+ /**< I2S wordlength, 16 or 32 bits */
+ enum drxi2s_polarity polarity;/**< I2S wordstrobe polarity */
+ enum drxi2s_format format; /**< I2S wordstrobe delay to data */
+ };
+
+/* ------------------------------expert interface-----------------------------*/
+/**
+* /enum enum drx_aud_fm_deemphasis * setting for FM-Deemphasis in audio demodulator.
+*
+*/
+ enum drx_aud_fm_deemphasis {
+ DRX_AUD_FM_DEEMPH_50US,
+ DRX_AUD_FM_DEEMPH_75US,
+ DRX_AUD_FM_DEEMPH_OFF
+ };
+
+/**
+* /enum DRXAudDeviation_t
+* setting for deviation mode in audio demodulator.
+*
+*/
+ enum drx_cfg_aud_deviation {
+ DRX_AUD_DEVIATION_NORMAL,
+ DRX_AUD_DEVIATION_HIGH
+ };
+
+/**
+* /enum enum drx_no_carrier_option * setting for carrier, mute/noise.
+*
+*/
+ enum drx_no_carrier_option {
+ DRX_NO_CARRIER_MUTE,
+ DRX_NO_CARRIER_NOISE
+ };
+
+/**
+* \enum DRXAudAutoSound_t
+* \brief Automatic Sound
+*/
+ enum drx_cfg_aud_auto_sound {
+ DRX_AUD_AUTO_SOUND_OFF = 0,
+ DRX_AUD_AUTO_SOUND_SELECT_ON_CHANGE_ON,
+ DRX_AUD_AUTO_SOUND_SELECT_ON_CHANGE_OFF
+ };
+
+/**
+* \enum DRXAudASSThres_t
+* \brief Automatic Sound Select Thresholds
+*/
+ struct drx_cfg_aud_ass_thres {
+ u16 a2; /* A2 Threshold for ASS configuration */
+ u16 btsc; /* BTSC Threshold for ASS configuration */
+ u16 nicam; /* Nicam Threshold for ASS configuration */
+ };
+
+/**
+* \struct struct drx_aud_carrier * \brief Carrier detection related parameters
+*/
+ struct drx_aud_carrier {
+ u16 thres; /* carrier detetcion threshold for primary carrier (A) */
+ enum drx_no_carrier_option opt; /* Mute or noise at no carrier detection (A) */
+ s32 shift; /* DC level of incoming signal (A) */
+ s32 dco; /* frequency adjustment (A) */
+ };
+
+/**
+* \struct struct drx_cfg_aud_carriers * \brief combining carrier A & B to one struct
+*/
+ struct drx_cfg_aud_carriers {
+ struct drx_aud_carrier a;
+ struct drx_aud_carrier b;
+ };
+
+/**
+* /enum enum drx_aud_i2s_src * Selection of audio source
+*/
+ enum drx_aud_i2s_src {
+ DRX_AUD_SRC_MONO,
+ DRX_AUD_SRC_STEREO_OR_AB,
+ DRX_AUD_SRC_STEREO_OR_A,
+ DRX_AUD_SRC_STEREO_OR_B};
+
+/**
+* \enum enum drx_aud_i2s_matrix * \brief Used for selecting I2S output.
+*/
+ enum drx_aud_i2s_matrix {
+ DRX_AUD_I2S_MATRIX_A_MONO,
+ /**< A sound only, stereo or mono */
+ DRX_AUD_I2S_MATRIX_B_MONO,
+ /**< B sound only, stereo or mono */
+ DRX_AUD_I2S_MATRIX_STEREO,
+ /**< A+B sound, transparant */
+ DRX_AUD_I2S_MATRIX_MONO /**< A+B mixed to mono sum, (L+R)/2 */};
+
+/**
+* /enum enum drx_aud_fm_matrix * setting for FM-Matrix in audio demodulator.
+*
+*/
+ enum drx_aud_fm_matrix {
+ DRX_AUD_FM_MATRIX_NO_MATRIX,
+ DRX_AUD_FM_MATRIX_GERMAN,
+ DRX_AUD_FM_MATRIX_KOREAN,
+ DRX_AUD_FM_MATRIX_SOUND_A,
+ DRX_AUD_FM_MATRIX_SOUND_B};
+
+/**
+* \struct DRXAudMatrices_t
+* \brief Mixer settings
+*/
+struct drx_cfg_aud_mixer {
+ enum drx_aud_i2s_src source_i2s;
+ enum drx_aud_i2s_matrix matrix_i2s;
+ enum drx_aud_fm_matrix matrix_fm;
+};
+
+/**
+* \enum DRXI2SVidSync_t
+* \brief Audio/video synchronization, interacts with I2S mode.
+* AUTO_1 and AUTO_2 are for automatic video standard detection with preference
+* for NTSC or Monochrome, because the frequencies are too close (59.94 & 60 Hz)
+*/
+ enum drx_cfg_aud_av_sync {
+ DRX_AUD_AVSYNC_OFF,/**< audio/video synchronization is off */
+ DRX_AUD_AVSYNC_NTSC,
+ /**< it is an NTSC system */
+ DRX_AUD_AVSYNC_MONOCHROME,
+ /**< it is a MONOCHROME system */
+ DRX_AUD_AVSYNC_PAL_SECAM
+ /**< it is a PAL/SECAM system */};
+
+/**
+* \struct struct drx_cfg_aud_prescale * \brief Prescalers
+*/
+struct drx_cfg_aud_prescale {
+ u16 fm_deviation;
+ s16 nicam_gain;
+};
+
+/**
+* \struct struct drx_aud_beep * \brief Beep
+*/
+struct drx_aud_beep {
+ s16 volume; /* dB */
+ u16 frequency; /* Hz */
+ bool mute;
+};
+
+/**
+* \enum enum drx_aud_btsc_detect * \brief BTSC detetcion mode
+*/
+ enum drx_aud_btsc_detect {
+ DRX_BTSC_STEREO,
+ DRX_BTSC_MONO_AND_SAP};
+
+/**
+* \struct struct drx_aud_data * \brief Audio data structure
+*/
+struct drx_aud_data {
+ /* audio storage */
+ bool audio_is_active;
+ enum drx_aud_standard audio_standard;
+ struct drx_cfg_i2s_output i2sdata;
+ struct drx_cfg_aud_volume volume;
+ enum drx_cfg_aud_auto_sound auto_sound;
+ struct drx_cfg_aud_ass_thres ass_thresholds;
+ struct drx_cfg_aud_carriers carriers;
+ struct drx_cfg_aud_mixer mixer;
+ enum drx_cfg_aud_deviation deviation;
+ enum drx_cfg_aud_av_sync av_sync;
+ struct drx_cfg_aud_prescale prescale;
+ enum drx_aud_fm_deemphasis deemph;
+ enum drx_aud_btsc_detect btsc_detect;
+ /* rds */
+ u16 rds_data_counter;
+ bool rds_data_present;
+};
+
+/**
+* \enum enum drx_qam_lock_range * \brief QAM lock range mode
+*/
+ enum drx_qam_lock_range {
+ DRX_QAM_LOCKRANGE_NORMAL,
+ DRX_QAM_LOCKRANGE_EXTENDED};
+
+/*============================================================================*/
+/*============================================================================*/
+/*== Data access structures ==================================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/* Address on device */
+ typedef u32 dr_xaddr_t, *pdr_xaddr_t;
+
+/* Protocol specific flags */
+ typedef u32 dr_xflags_t, *pdr_xflags_t;
+
+/* Write block of data to device */
+ typedef int(*drx_write_block_func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u16 datasize, /* size of data in bytes */
+ u8 *data, /* data to send */
+ u32 flags);
+
+/* Read block of data from device */
+ typedef int(*drx_read_block_func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u16 datasize, /* size of data in bytes */
+ u8 *data, /* receive buffer */
+ u32 flags);
+
+/* Write 8-bits value to device */
+ typedef int(*drx_write_reg8func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u8 data, /* data to send */
+ u32 flags);
+
+/* Read 8-bits value to device */
+ typedef int(*drx_read_reg8func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u8 *data, /* receive buffer */
+ u32 flags);
+
+/* Read modify write 8-bits value to device */
+ typedef int(*drx_read_modify_write_reg8func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 waddr, /* write address of register */
+ u32 raddr, /* read address of register */
+ u8 wdata, /* data to write */
+ u8 *rdata); /* data to read */
+
+/* Write 16-bits value to device */
+ typedef int(*drx_write_reg16func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u16 data, /* data to send */
+ u32 flags);
+
+/* Read 16-bits value to device */
+ typedef int(*drx_read_reg16func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u16 *data, /* receive buffer */
+ u32 flags);
+
+/* Read modify write 16-bits value to device */
+ typedef int(*drx_read_modify_write_reg16func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 waddr, /* write address of register */
+ u32 raddr, /* read address of register */
+ u16 wdata, /* data to write */
+ u16 *rdata); /* data to read */
+
+/* Write 32-bits value to device */
+ typedef int(*drx_write_reg32func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u32 data, /* data to send */
+ u32 flags);
+
+/* Read 32-bits value to device */
+ typedef int(*drx_read_reg32func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u32 *data, /* receive buffer */
+ u32 flags);
+
+/* Read modify write 32-bits value to device */
+ typedef int(*drx_read_modify_write_reg32func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 waddr, /* write address of register */
+ u32 raddr, /* read address of register */
+ u32 wdata, /* data to write */
+ u32 *rdata); /* data to read */
+
+/**
+* \struct struct drx_access_func * \brief Interface to an access protocol.
+*/
+struct drx_access_func {
+ drx_write_block_func_t write_block_func;
+ drx_read_block_func_t read_block_func;
+ drx_write_reg8func_t write_reg8func;
+ drx_read_reg8func_t read_reg8func;
+ drx_read_modify_write_reg8func_t read_modify_write_reg8func;
+ drx_write_reg16func_t write_reg16func;
+ drx_read_reg16func_t read_reg16func;
+ drx_read_modify_write_reg16func_t read_modify_write_reg16func;
+ drx_write_reg32func_t write_reg32func;
+ drx_read_reg32func_t read_reg32func;
+ drx_read_modify_write_reg32func_t read_modify_write_reg32func;
+};
+
+/* Register address and data for register dump function */
+struct drx_reg_dump {
+ u32 address;
+ u32 data;
+};
+
+/*============================================================================*/
+/*============================================================================*/
+/*== Demod instance data structures ==========================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/**
+* \struct struct drx_common_attr * \brief Set of common attributes, shared by all DRX devices.
+*/
+ struct drx_common_attr {
+ /* Microcode (firmware) attributes */
+ char *microcode_file; /**< microcode filename */
+ bool verify_microcode;
+ /**< Use microcode verify or not. */
+ struct drx_mc_version_rec mcversion;
+ /**< Version record of microcode from file */
+
+ /* Clocks and tuner attributes */
+ s32 intermediate_freq;
+ /**< IF,if tuner instance not used. (kHz)*/
+ s32 sys_clock_freq;
+ /**< Systemclock frequency. (kHz) */
+ s32 osc_clock_freq;
+ /**< Oscillator clock frequency. (kHz) */
+ s16 osc_clock_deviation;
+ /**< Oscillator clock deviation. (ppm) */
+ bool mirror_freq_spect;
+ /**< Mirror IF frequency spectrum or not.*/
+
+ /* Initial MPEG output attributes */
+ struct drx_cfg_mpeg_output mpeg_cfg;
+ /**< MPEG configuration */
+
+ bool is_opened; /**< if true instance is already opened. */
+
+ /* Channel scan */
+ struct drx_scan_param *scan_param;
+ /**< scan parameters */
+ u16 scan_freq_plan_index;
+ /**< next index in freq plan */
+ s32 scan_next_frequency;
+ /**< next freq to scan */
+ bool scan_ready; /**< scan ready flag */
+ u32 scan_max_channels;/**< number of channels in freqplan */
+ u32 scan_channels_scanned;
+ /**< number of channels scanned */
+ /* Channel scan - inner loop: demod related */
+ drx_scan_func_t scan_function;
+ /**< function to check channel */
+ /* Channel scan - inner loop: SYSObj related */
+ void *scan_context; /**< Context Pointer of SYSObj */
+ /* Channel scan - parameters for default DTV scan function in core driver */
+ u16 scan_demod_lock_timeout;
+ /**< millisecs to wait for lock */
+ enum drx_lock_status scan_desired_lock;
+ /**< lock requirement for channel found */
+ /* scan_active can be used by SetChannel to decide how to program the tuner,
+ fast or slow (but stable). Usually fast during scan. */
+ bool scan_active; /**< true when scan routines are active */
+
+ /* Power management */
+ enum drx_power_mode current_power_mode;
+ /**< current power management mode */
+
+ /* Tuner */
+ u8 tuner_port_nr; /**< nr of I2C port to wich tuner is */
+ s32 tuner_min_freq_rf;
+ /**< minimum RF input frequency, in kHz */
+ s32 tuner_max_freq_rf;
+ /**< maximum RF input frequency, in kHz */
+ bool tuner_rf_agc_pol; /**< if true invert RF AGC polarity */
+ bool tuner_if_agc_pol; /**< if true invert IF AGC polarity */
+ bool tuner_slow_mode; /**< if true invert IF AGC polarity */
+
+ struct drx_channel current_channel;
+ /**< current channel parameters */
+ enum drx_standard current_standard;
+ /**< current standard selection */
+ enum drx_standard prev_standard;
+ /**< previous standard selection */
+ enum drx_standard di_cache_standard;
+ /**< standard in DI cache if available */
+ bool use_bootloader; /**< use bootloader in open */
+ u32 capabilities; /**< capabilities flags */
+ u32 product_id; /**< product ID inc. metal fix number */};
+
+/*
+* Generic functions for DRX devices.
+*/
+
+struct drx_demod_instance;
+
+/**
+* \struct struct drx_demod_instance * \brief Top structure of demodulator instance.
+*/
+struct drx_demod_instance {
+ /**< data access protocol functions */
+ struct i2c_device_addr *my_i2c_dev_addr;
+ /**< i2c address and device identifier */
+ struct drx_common_attr *my_common_attr;
+ /**< common DRX attributes */
+ void *my_ext_attr; /**< device specific attributes */
+ /* generic demodulator data */
+
+ struct i2c_adapter *i2c;
+ const struct firmware *firmware;
+};
+
+/*-------------------------------------------------------------------------
+MACROS
+Conversion from enum values to human readable form.
+-------------------------------------------------------------------------*/
+
+/* standard */
+
+#define DRX_STR_STANDARD(x) ( \
+ (x == DRX_STANDARD_DVBT) ? "DVB-T" : \
+ (x == DRX_STANDARD_8VSB) ? "8VSB" : \
+ (x == DRX_STANDARD_NTSC) ? "NTSC" : \
+ (x == DRX_STANDARD_PAL_SECAM_BG) ? "PAL/SECAM B/G" : \
+ (x == DRX_STANDARD_PAL_SECAM_DK) ? "PAL/SECAM D/K" : \
+ (x == DRX_STANDARD_PAL_SECAM_I) ? "PAL/SECAM I" : \
+ (x == DRX_STANDARD_PAL_SECAM_L) ? "PAL/SECAM L" : \
+ (x == DRX_STANDARD_PAL_SECAM_LP) ? "PAL/SECAM LP" : \
+ (x == DRX_STANDARD_ITU_A) ? "ITU-A" : \
+ (x == DRX_STANDARD_ITU_B) ? "ITU-B" : \
+ (x == DRX_STANDARD_ITU_C) ? "ITU-C" : \
+ (x == DRX_STANDARD_ITU_D) ? "ITU-D" : \
+ (x == DRX_STANDARD_FM) ? "FM" : \
+ (x == DRX_STANDARD_DTMB) ? "DTMB" : \
+ (x == DRX_STANDARD_AUTO) ? "Auto" : \
+ (x == DRX_STANDARD_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+/* channel */
+
+#define DRX_STR_BANDWIDTH(x) ( \
+ (x == DRX_BANDWIDTH_8MHZ) ? "8 MHz" : \
+ (x == DRX_BANDWIDTH_7MHZ) ? "7 MHz" : \
+ (x == DRX_BANDWIDTH_6MHZ) ? "6 MHz" : \
+ (x == DRX_BANDWIDTH_AUTO) ? "Auto" : \
+ (x == DRX_BANDWIDTH_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_FFTMODE(x) ( \
+ (x == DRX_FFTMODE_2K) ? "2k" : \
+ (x == DRX_FFTMODE_4K) ? "4k" : \
+ (x == DRX_FFTMODE_8K) ? "8k" : \
+ (x == DRX_FFTMODE_AUTO) ? "Auto" : \
+ (x == DRX_FFTMODE_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_GUARD(x) ( \
+ (x == DRX_GUARD_1DIV32) ? "1/32nd" : \
+ (x == DRX_GUARD_1DIV16) ? "1/16th" : \
+ (x == DRX_GUARD_1DIV8) ? "1/8th" : \
+ (x == DRX_GUARD_1DIV4) ? "1/4th" : \
+ (x == DRX_GUARD_AUTO) ? "Auto" : \
+ (x == DRX_GUARD_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_CONSTELLATION(x) ( \
+ (x == DRX_CONSTELLATION_BPSK) ? "BPSK" : \
+ (x == DRX_CONSTELLATION_QPSK) ? "QPSK" : \
+ (x == DRX_CONSTELLATION_PSK8) ? "PSK8" : \
+ (x == DRX_CONSTELLATION_QAM16) ? "QAM16" : \
+ (x == DRX_CONSTELLATION_QAM32) ? "QAM32" : \
+ (x == DRX_CONSTELLATION_QAM64) ? "QAM64" : \
+ (x == DRX_CONSTELLATION_QAM128) ? "QAM128" : \
+ (x == DRX_CONSTELLATION_QAM256) ? "QAM256" : \
+ (x == DRX_CONSTELLATION_QAM512) ? "QAM512" : \
+ (x == DRX_CONSTELLATION_QAM1024) ? "QAM1024" : \
+ (x == DRX_CONSTELLATION_QPSK_NR) ? "QPSK_NR" : \
+ (x == DRX_CONSTELLATION_AUTO) ? "Auto" : \
+ (x == DRX_CONSTELLATION_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_CODERATE(x) ( \
+ (x == DRX_CODERATE_1DIV2) ? "1/2nd" : \
+ (x == DRX_CODERATE_2DIV3) ? "2/3rd" : \
+ (x == DRX_CODERATE_3DIV4) ? "3/4th" : \
+ (x == DRX_CODERATE_5DIV6) ? "5/6th" : \
+ (x == DRX_CODERATE_7DIV8) ? "7/8th" : \
+ (x == DRX_CODERATE_AUTO) ? "Auto" : \
+ (x == DRX_CODERATE_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_HIERARCHY(x) ( \
+ (x == DRX_HIERARCHY_NONE) ? "None" : \
+ (x == DRX_HIERARCHY_ALPHA1) ? "Alpha=1" : \
+ (x == DRX_HIERARCHY_ALPHA2) ? "Alpha=2" : \
+ (x == DRX_HIERARCHY_ALPHA4) ? "Alpha=4" : \
+ (x == DRX_HIERARCHY_AUTO) ? "Auto" : \
+ (x == DRX_HIERARCHY_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_PRIORITY(x) ( \
+ (x == DRX_PRIORITY_LOW) ? "Low" : \
+ (x == DRX_PRIORITY_HIGH) ? "High" : \
+ (x == DRX_PRIORITY_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_MIRROR(x) ( \
+ (x == DRX_MIRROR_NO) ? "Normal" : \
+ (x == DRX_MIRROR_YES) ? "Mirrored" : \
+ (x == DRX_MIRROR_AUTO) ? "Auto" : \
+ (x == DRX_MIRROR_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_CLASSIFICATION(x) ( \
+ (x == DRX_CLASSIFICATION_GAUSS) ? "Gaussion" : \
+ (x == DRX_CLASSIFICATION_HVY_GAUSS) ? "Heavy Gaussion" : \
+ (x == DRX_CLASSIFICATION_COCHANNEL) ? "Co-channel" : \
+ (x == DRX_CLASSIFICATION_STATIC) ? "Static echo" : \
+ (x == DRX_CLASSIFICATION_MOVING) ? "Moving echo" : \
+ (x == DRX_CLASSIFICATION_ZERODB) ? "Zero dB echo" : \
+ (x == DRX_CLASSIFICATION_UNKNOWN) ? "Unknown" : \
+ (x == DRX_CLASSIFICATION_AUTO) ? "Auto" : \
+ "(Invalid)")
+
+#define DRX_STR_INTERLEAVEMODE(x) ( \
+ (x == DRX_INTERLEAVEMODE_I128_J1) ? "I128_J1" : \
+ (x == DRX_INTERLEAVEMODE_I128_J1_V2) ? "I128_J1_V2" : \
+ (x == DRX_INTERLEAVEMODE_I128_J2) ? "I128_J2" : \
+ (x == DRX_INTERLEAVEMODE_I64_J2) ? "I64_J2" : \
+ (x == DRX_INTERLEAVEMODE_I128_J3) ? "I128_J3" : \
+ (x == DRX_INTERLEAVEMODE_I32_J4) ? "I32_J4" : \
+ (x == DRX_INTERLEAVEMODE_I128_J4) ? "I128_J4" : \
+ (x == DRX_INTERLEAVEMODE_I16_J8) ? "I16_J8" : \
+ (x == DRX_INTERLEAVEMODE_I128_J5) ? "I128_J5" : \
+ (x == DRX_INTERLEAVEMODE_I8_J16) ? "I8_J16" : \
+ (x == DRX_INTERLEAVEMODE_I128_J6) ? "I128_J6" : \
+ (x == DRX_INTERLEAVEMODE_RESERVED_11) ? "Reserved 11" : \
+ (x == DRX_INTERLEAVEMODE_I128_J7) ? "I128_J7" : \
+ (x == DRX_INTERLEAVEMODE_RESERVED_13) ? "Reserved 13" : \
+ (x == DRX_INTERLEAVEMODE_I128_J8) ? "I128_J8" : \
+ (x == DRX_INTERLEAVEMODE_RESERVED_15) ? "Reserved 15" : \
+ (x == DRX_INTERLEAVEMODE_I12_J17) ? "I12_J17" : \
+ (x == DRX_INTERLEAVEMODE_I5_J4) ? "I5_J4" : \
+ (x == DRX_INTERLEAVEMODE_B52_M240) ? "B52_M240" : \
+ (x == DRX_INTERLEAVEMODE_B52_M720) ? "B52_M720" : \
+ (x == DRX_INTERLEAVEMODE_B52_M48) ? "B52_M48" : \
+ (x == DRX_INTERLEAVEMODE_B52_M0) ? "B52_M0" : \
+ (x == DRX_INTERLEAVEMODE_UNKNOWN) ? "Unknown" : \
+ (x == DRX_INTERLEAVEMODE_AUTO) ? "Auto" : \
+ "(Invalid)")
+
+#define DRX_STR_LDPC(x) ( \
+ (x == DRX_LDPC_0_4) ? "0.4" : \
+ (x == DRX_LDPC_0_6) ? "0.6" : \
+ (x == DRX_LDPC_0_8) ? "0.8" : \
+ (x == DRX_LDPC_AUTO) ? "Auto" : \
+ (x == DRX_LDPC_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+#define DRX_STR_CARRIER(x) ( \
+ (x == DRX_CARRIER_MULTI) ? "Multi" : \
+ (x == DRX_CARRIER_SINGLE) ? "Single" : \
+ (x == DRX_CARRIER_AUTO) ? "Auto" : \
+ (x == DRX_CARRIER_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+#define DRX_STR_FRAMEMODE(x) ( \
+ (x == DRX_FRAMEMODE_420) ? "420" : \
+ (x == DRX_FRAMEMODE_595) ? "595" : \
+ (x == DRX_FRAMEMODE_945) ? "945" : \
+ (x == DRX_FRAMEMODE_420_FIXED_PN) ? "420 with fixed PN" : \
+ (x == DRX_FRAMEMODE_945_FIXED_PN) ? "945 with fixed PN" : \
+ (x == DRX_FRAMEMODE_AUTO) ? "Auto" : \
+ (x == DRX_FRAMEMODE_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+#define DRX_STR_PILOT(x) ( \
+ (x == DRX_PILOT_ON) ? "On" : \
+ (x == DRX_PILOT_OFF) ? "Off" : \
+ (x == DRX_PILOT_AUTO) ? "Auto" : \
+ (x == DRX_PILOT_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+/* TPS */
+
+#define DRX_STR_TPS_FRAME(x) ( \
+ (x == DRX_TPS_FRAME1) ? "Frame1" : \
+ (x == DRX_TPS_FRAME2) ? "Frame2" : \
+ (x == DRX_TPS_FRAME3) ? "Frame3" : \
+ (x == DRX_TPS_FRAME4) ? "Frame4" : \
+ (x == DRX_TPS_FRAME_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+/* lock status */
+
+#define DRX_STR_LOCKSTATUS(x) ( \
+ (x == DRX_NEVER_LOCK) ? "Never" : \
+ (x == DRX_NOT_LOCKED) ? "No" : \
+ (x == DRX_LOCKED) ? "Locked" : \
+ (x == DRX_LOCK_STATE_1) ? "Lock state 1" : \
+ (x == DRX_LOCK_STATE_2) ? "Lock state 2" : \
+ (x == DRX_LOCK_STATE_3) ? "Lock state 3" : \
+ (x == DRX_LOCK_STATE_4) ? "Lock state 4" : \
+ (x == DRX_LOCK_STATE_5) ? "Lock state 5" : \
+ (x == DRX_LOCK_STATE_6) ? "Lock state 6" : \
+ (x == DRX_LOCK_STATE_7) ? "Lock state 7" : \
+ (x == DRX_LOCK_STATE_8) ? "Lock state 8" : \
+ (x == DRX_LOCK_STATE_9) ? "Lock state 9" : \
+ "(Invalid)")
+
+/* version information , modules */
+#define DRX_STR_MODULE(x) ( \
+ (x == DRX_MODULE_DEVICE) ? "Device" : \
+ (x == DRX_MODULE_MICROCODE) ? "Microcode" : \
+ (x == DRX_MODULE_DRIVERCORE) ? "CoreDriver" : \
+ (x == DRX_MODULE_DEVICEDRIVER) ? "DeviceDriver" : \
+ (x == DRX_MODULE_BSP_I2C) ? "BSP I2C" : \
+ (x == DRX_MODULE_BSP_TUNER) ? "BSP Tuner" : \
+ (x == DRX_MODULE_BSP_HOST) ? "BSP Host" : \
+ (x == DRX_MODULE_DAP) ? "Data Access Protocol" : \
+ (x == DRX_MODULE_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+#define DRX_STR_POWER_MODE(x) ( \
+ (x == DRX_POWER_UP) ? "DRX_POWER_UP " : \
+ (x == DRX_POWER_MODE_1) ? "DRX_POWER_MODE_1" : \
+ (x == DRX_POWER_MODE_2) ? "DRX_POWER_MODE_2" : \
+ (x == DRX_POWER_MODE_3) ? "DRX_POWER_MODE_3" : \
+ (x == DRX_POWER_MODE_4) ? "DRX_POWER_MODE_4" : \
+ (x == DRX_POWER_MODE_5) ? "DRX_POWER_MODE_5" : \
+ (x == DRX_POWER_MODE_6) ? "DRX_POWER_MODE_6" : \
+ (x == DRX_POWER_MODE_7) ? "DRX_POWER_MODE_7" : \
+ (x == DRX_POWER_MODE_8) ? "DRX_POWER_MODE_8" : \
+ (x == DRX_POWER_MODE_9) ? "DRX_POWER_MODE_9" : \
+ (x == DRX_POWER_MODE_10) ? "DRX_POWER_MODE_10" : \
+ (x == DRX_POWER_MODE_11) ? "DRX_POWER_MODE_11" : \
+ (x == DRX_POWER_MODE_12) ? "DRX_POWER_MODE_12" : \
+ (x == DRX_POWER_MODE_13) ? "DRX_POWER_MODE_13" : \
+ (x == DRX_POWER_MODE_14) ? "DRX_POWER_MODE_14" : \
+ (x == DRX_POWER_MODE_15) ? "DRX_POWER_MODE_15" : \
+ (x == DRX_POWER_MODE_16) ? "DRX_POWER_MODE_16" : \
+ (x == DRX_POWER_DOWN) ? "DRX_POWER_DOWN " : \
+ "(Invalid)")
+
+#define DRX_STR_OOB_STANDARD(x) ( \
+ (x == DRX_OOB_MODE_A) ? "ANSI 55-1 " : \
+ (x == DRX_OOB_MODE_B_GRADE_A) ? "ANSI 55-2 A" : \
+ (x == DRX_OOB_MODE_B_GRADE_B) ? "ANSI 55-2 B" : \
+ "(Invalid)")
+
+#define DRX_STR_AUD_STANDARD(x) ( \
+ (x == DRX_AUD_STANDARD_BTSC) ? "BTSC" : \
+ (x == DRX_AUD_STANDARD_A2) ? "A2" : \
+ (x == DRX_AUD_STANDARD_EIAJ) ? "EIAJ" : \
+ (x == DRX_AUD_STANDARD_FM_STEREO) ? "FM Stereo" : \
+ (x == DRX_AUD_STANDARD_AUTO) ? "Auto" : \
+ (x == DRX_AUD_STANDARD_M_MONO) ? "M-Standard Mono" : \
+ (x == DRX_AUD_STANDARD_D_K_MONO) ? "D/K Mono FM" : \
+ (x == DRX_AUD_STANDARD_BG_FM) ? "B/G-Dual Carrier FM (A2)" : \
+ (x == DRX_AUD_STANDARD_D_K1) ? "D/K1-Dual Carrier FM" : \
+ (x == DRX_AUD_STANDARD_D_K2) ? "D/K2-Dual Carrier FM" : \
+ (x == DRX_AUD_STANDARD_D_K3) ? "D/K3-Dual Carrier FM" : \
+ (x == DRX_AUD_STANDARD_BG_NICAM_FM) ? "B/G-NICAM-FM" : \
+ (x == DRX_AUD_STANDARD_L_NICAM_AM) ? "L-NICAM-AM" : \
+ (x == DRX_AUD_STANDARD_I_NICAM_FM) ? "I-NICAM-FM" : \
+ (x == DRX_AUD_STANDARD_D_K_NICAM_FM) ? "D/K-NICAM-FM" : \
+ (x == DRX_AUD_STANDARD_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_AUD_STEREO(x) ( \
+ (x == true) ? "Stereo" : \
+ (x == false) ? "Mono" : \
+ "(Invalid)")
+
+#define DRX_STR_AUD_SAP(x) ( \
+ (x == true) ? "Present" : \
+ (x == false) ? "Not present" : \
+ "(Invalid)")
+
+#define DRX_STR_AUD_CARRIER(x) ( \
+ (x == true) ? "Present" : \
+ (x == false) ? "Not present" : \
+ "(Invalid)")
+
+#define DRX_STR_AUD_RDS(x) ( \
+ (x == true) ? "Available" : \
+ (x == false) ? "Not Available" : \
+ "(Invalid)")
+
+#define DRX_STR_AUD_NICAM_STATUS(x) ( \
+ (x == DRX_AUD_NICAM_DETECTED) ? "Detected" : \
+ (x == DRX_AUD_NICAM_NOT_DETECTED) ? "Not detected" : \
+ (x == DRX_AUD_NICAM_BAD) ? "Bad" : \
+ "(Invalid)")
+
+#define DRX_STR_RDS_VALID(x) ( \
+ (x == true) ? "Valid" : \
+ (x == false) ? "Not Valid" : \
+ "(Invalid)")
+
+/*-------------------------------------------------------------------------
+Access macros
+-------------------------------------------------------------------------*/
+
+/**
+* \brief Create a compilable reference to the microcode attribute
+* \param d pointer to demod instance
+*
+* Used as main reference to an attribute field.
+* Used by both macro implementation and function implementation.
+* These macros are defined to avoid duplication of code in macro and function
+* definitions that handle access of demod common or extended attributes.
+*
+*/
+
+#define DRX_ATTR_MCRECORD(d) ((d)->my_common_attr->mcversion)
+#define DRX_ATTR_MIRRORFREQSPECT(d) ((d)->my_common_attr->mirror_freq_spect)
+#define DRX_ATTR_CURRENTPOWERMODE(d)((d)->my_common_attr->current_power_mode)
+#define DRX_ATTR_ISOPENED(d) ((d)->my_common_attr->is_opened)
+#define DRX_ATTR_USEBOOTLOADER(d) ((d)->my_common_attr->use_bootloader)
+#define DRX_ATTR_CURRENTSTANDARD(d) ((d)->my_common_attr->current_standard)
+#define DRX_ATTR_PREVSTANDARD(d) ((d)->my_common_attr->prev_standard)
+#define DRX_ATTR_CACHESTANDARD(d) ((d)->my_common_attr->di_cache_standard)
+#define DRX_ATTR_CURRENTCHANNEL(d) ((d)->my_common_attr->current_channel)
+#define DRX_ATTR_MICROCODE(d) ((d)->my_common_attr->microcode)
+#define DRX_ATTR_VERIFYMICROCODE(d) ((d)->my_common_attr->verify_microcode)
+#define DRX_ATTR_CAPABILITIES(d) ((d)->my_common_attr->capabilities)
+#define DRX_ATTR_PRODUCTID(d) ((d)->my_common_attr->product_id)
+#define DRX_ATTR_INTERMEDIATEFREQ(d) ((d)->my_common_attr->intermediate_freq)
+#define DRX_ATTR_SYSCLOCKFREQ(d) ((d)->my_common_attr->sys_clock_freq)
+#define DRX_ATTR_TUNERRFAGCPOL(d) ((d)->my_common_attr->tuner_rf_agc_pol)
+#define DRX_ATTR_TUNERIFAGCPOL(d) ((d)->my_common_attr->tuner_if_agc_pol)
+#define DRX_ATTR_TUNERSLOWMODE(d) ((d)->my_common_attr->tuner_slow_mode)
+#define DRX_ATTR_TUNERSPORTNR(d) ((d)->my_common_attr->tuner_port_nr)
+#define DRX_ATTR_I2CADDR(d) ((d)->my_i2c_dev_addr->i2c_addr)
+#define DRX_ATTR_I2CDEVID(d) ((d)->my_i2c_dev_addr->i2c_dev_id)
+#define DRX_ISMCVERTYPE(x) ((x) == AUX_VER_RECORD)
+
+/**************************/
+
+/* Macros with device-specific handling are converted to CFG functions */
+
+#define DRX_ACCESSMACRO_SET(demod, value, cfg_name, data_type) \
+ do { \
+ struct drx_cfg config; \
+ data_type cfg_data; \
+ config.cfg_type = cfg_name; \
+ config.cfg_data = &cfg_data; \
+ cfg_data = value; \
+ drx_ctrl(demod, DRX_CTRL_SET_CFG, &config); \
+ } while (0)
+
+#define DRX_ACCESSMACRO_GET(demod, value, cfg_name, data_type, error_value) \
+ do { \
+ int cfg_status; \
+ struct drx_cfg config; \
+ data_type cfg_data; \
+ config.cfg_type = cfg_name; \
+ config.cfg_data = &cfg_data; \
+ cfg_status = drx_ctrl(demod, DRX_CTRL_GET_CFG, &config); \
+ if (cfg_status == 0) { \
+ value = cfg_data; \
+ } else { \
+ value = (data_type)error_value; \
+ } \
+ } while (0)
+
+/* Configuration functions for usage by Access (XS) Macros */
+
+#ifndef DRX_XS_CFG_BASE
+#define DRX_XS_CFG_BASE (500)
+#endif
+
+#define DRX_XS_CFG_PRESET (DRX_XS_CFG_BASE + 0)
+#define DRX_XS_CFG_AUD_BTSC_DETECT (DRX_XS_CFG_BASE + 1)
+#define DRX_XS_CFG_QAM_LOCKRANGE (DRX_XS_CFG_BASE + 2)
+
+/* Access Macros with device-specific handling */
+
+#define DRX_SET_PRESET(d, x) \
+ DRX_ACCESSMACRO_SET((d), (x), DRX_XS_CFG_PRESET, char*)
+#define DRX_GET_PRESET(d, x) \
+ DRX_ACCESSMACRO_GET((d), (x), DRX_XS_CFG_PRESET, char*, "ERROR")
+
+#define DRX_SET_AUD_BTSC_DETECT(d, x) DRX_ACCESSMACRO_SET((d), (x), \
+ DRX_XS_CFG_AUD_BTSC_DETECT, enum drx_aud_btsc_detect)
+#define DRX_GET_AUD_BTSC_DETECT(d, x) DRX_ACCESSMACRO_GET((d), (x), \
+ DRX_XS_CFG_AUD_BTSC_DETECT, enum drx_aud_btsc_detect, DRX_UNKNOWN)
+
+#define DRX_SET_QAM_LOCKRANGE(d, x) DRX_ACCESSMACRO_SET((d), (x), \
+ DRX_XS_CFG_QAM_LOCKRANGE, enum drx_qam_lock_range)
+#define DRX_GET_QAM_LOCKRANGE(d, x) DRX_ACCESSMACRO_GET((d), (x), \
+ DRX_XS_CFG_QAM_LOCKRANGE, enum drx_qam_lock_range, DRX_UNKNOWN)
+
+/**
+* \brief Macro to check if std is an ATV standard
+* \retval true std is an ATV standard
+* \retval false std is an ATV standard
+*/
+#define DRX_ISATVSTD(std) (((std) == DRX_STANDARD_PAL_SECAM_BG) || \
+ ((std) == DRX_STANDARD_PAL_SECAM_DK) || \
+ ((std) == DRX_STANDARD_PAL_SECAM_I) || \
+ ((std) == DRX_STANDARD_PAL_SECAM_L) || \
+ ((std) == DRX_STANDARD_PAL_SECAM_LP) || \
+ ((std) == DRX_STANDARD_NTSC) || \
+ ((std) == DRX_STANDARD_FM))
+
+/**
+* \brief Macro to check if std is an QAM standard
+* \retval true std is an QAM standards
+* \retval false std is an QAM standards
+*/
+#define DRX_ISQAMSTD(std) (((std) == DRX_STANDARD_ITU_A) || \
+ ((std) == DRX_STANDARD_ITU_B) || \
+ ((std) == DRX_STANDARD_ITU_C) || \
+ ((std) == DRX_STANDARD_ITU_D))
+
+/**
+* \brief Macro to check if std is VSB standard
+* \retval true std is VSB standard
+* \retval false std is not VSB standard
+*/
+#define DRX_ISVSBSTD(std) ((std) == DRX_STANDARD_8VSB)
+
+/**
+* \brief Macro to check if std is DVBT standard
+* \retval true std is DVBT standard
+* \retval false std is not DVBT standard
+*/
+#define DRX_ISDVBTSTD(std) ((std) == DRX_STANDARD_DVBT)
+
+/*-------------------------------------------------------------------------
+THE END
+-------------------------------------------------------------------------*/
+#endif /* __DRXDRIVER_H__ */
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver_version.h b/drivers/media/dvb-frontends/drx39xyj/drx_driver_version.h
new file mode 100644
index 000000000000..ff05a4ffb190
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drx_driver_version.h
@@ -0,0 +1,72 @@
+/*
+ *******************************************************************************
+ * WARNING - THIS FILE HAS BEEN GENERATED - DO NOT CHANGE
+ *
+ * Filename: drx_driver_version.h
+ * Generated on: Mon Jan 18 12:09:23 2010
+ * Generated by: IDF:x 1.3.0
+ * Generated from: ../../../device/drxj/version
+ * Output start: [entry point]
+ *
+ * filename last modified re-use
+ *
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ 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 of Trident Microsystems nor Hauppauge Computer Works
+ 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 HOLDER 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.
+*/
+
+/* -----------------------------------------------------
+ * version.idf Mon Jan 18 11:56:10 2010 -
+ *
+ */
+
+#ifndef __DRX_DRIVER_VERSION__H__
+#define __DRX_DRIVER_VERSION__H__ INCLUDED
+
+#ifdef _REGISTERTABLE_
+#include <registertable.h>
+ extern register_table_t drx_driver_version[];
+ extern register_table_info_t drx_driver_version_info[];
+#endif /* _REGISTERTABLE_ */
+
+/*
+ *==============================================================================
+ * VERSION
+ * version@/var/cvs/projects/drxj.cvsroot/hostcode/drxdriver/device/drxj
+ *==============================================================================
+ */
+
+#define VERSION__A 0x0
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 0
+#define VERSION_PATCH 56
+
+#endif /* __DRX_DRIVER_VERSION__H__ */
+/*
+ * End of file (drx_driver_version.h)
+ *******************************************************************************
+ */
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c
new file mode 100644
index 000000000000..9482954fd453
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c
@@ -0,0 +1,12400 @@
+/*
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ 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 of Trident Microsystems nor Hauppauge Computer Works
+ 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 HOLDER 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.
+
+ DRXJ specific implementation of DRX driver
+ authors: Dragan Savic, Milos Nikolic, Mihajlo Katona, Tao Ding, Paul Janssen
+
+ The Linux DVB Driver for Micronas DRX39xx family (drx3933j) was
+ written by Devin Heitmueller <devin.heitmueller@kernellabs.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*-----------------------------------------------------------------------------
+INCLUDE FILES
+----------------------------------------------------------------------------*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "drx39xxj.h"
+
+#include "drxj.h"
+#include "drxj_map.h"
+
+/*============================================================================*/
+/*=== DEFINES ================================================================*/
+/*============================================================================*/
+
+#define DRX39XX_MAIN_FIRMWARE "dvb-fe-drxj-mc-1.0.8.fw"
+
+/**
+* \brief Maximum u32 value.
+*/
+#ifndef MAX_U32
+#define MAX_U32 ((u32) (0xFFFFFFFFL))
+#endif
+
+/* Customer configurable hardware settings, etc */
+#ifndef MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH
+#define MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH 0x02
+#endif
+
+#ifndef MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH
+#define MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH 0x02
+#endif
+
+#ifndef MPEG_OUTPUT_CLK_DRIVE_STRENGTH
+#define MPEG_OUTPUT_CLK_DRIVE_STRENGTH 0x06
+#endif
+
+#ifndef OOB_CRX_DRIVE_STRENGTH
+#define OOB_CRX_DRIVE_STRENGTH 0x02
+#endif
+
+#ifndef OOB_DRX_DRIVE_STRENGTH
+#define OOB_DRX_DRIVE_STRENGTH 0x02
+#endif
+/**** START DJCOMBO patches to DRXJ registermap constants *********************/
+/**** registermap 200706071303 from drxj **************************************/
+#define ATV_TOP_CR_AMP_TH_FM 0x0
+#define ATV_TOP_CR_AMP_TH_L 0xA
+#define ATV_TOP_CR_AMP_TH_LP 0xA
+#define ATV_TOP_CR_AMP_TH_BG 0x8
+#define ATV_TOP_CR_AMP_TH_DK 0x8
+#define ATV_TOP_CR_AMP_TH_I 0x8
+#define ATV_TOP_CR_CONT_CR_D_MN 0x18
+#define ATV_TOP_CR_CONT_CR_D_FM 0x0
+#define ATV_TOP_CR_CONT_CR_D_L 0x20
+#define ATV_TOP_CR_CONT_CR_D_LP 0x20
+#define ATV_TOP_CR_CONT_CR_D_BG 0x18
+#define ATV_TOP_CR_CONT_CR_D_DK 0x18
+#define ATV_TOP_CR_CONT_CR_D_I 0x18
+#define ATV_TOP_CR_CONT_CR_I_MN 0x80
+#define ATV_TOP_CR_CONT_CR_I_FM 0x0
+#define ATV_TOP_CR_CONT_CR_I_L 0x80
+#define ATV_TOP_CR_CONT_CR_I_LP 0x80
+#define ATV_TOP_CR_CONT_CR_I_BG 0x80
+#define ATV_TOP_CR_CONT_CR_I_DK 0x80
+#define ATV_TOP_CR_CONT_CR_I_I 0x80
+#define ATV_TOP_CR_CONT_CR_P_MN 0x4
+#define ATV_TOP_CR_CONT_CR_P_FM 0x0
+#define ATV_TOP_CR_CONT_CR_P_L 0x4
+#define ATV_TOP_CR_CONT_CR_P_LP 0x4
+#define ATV_TOP_CR_CONT_CR_P_BG 0x4
+#define ATV_TOP_CR_CONT_CR_P_DK 0x4
+#define ATV_TOP_CR_CONT_CR_P_I 0x4
+#define ATV_TOP_CR_OVM_TH_MN 0xA0
+#define ATV_TOP_CR_OVM_TH_FM 0x0
+#define ATV_TOP_CR_OVM_TH_L 0xA0
+#define ATV_TOP_CR_OVM_TH_LP 0xA0
+#define ATV_TOP_CR_OVM_TH_BG 0xA0
+#define ATV_TOP_CR_OVM_TH_DK 0xA0
+#define ATV_TOP_CR_OVM_TH_I 0xA0
+#define ATV_TOP_EQU0_EQU_C0_FM 0x0
+#define ATV_TOP_EQU0_EQU_C0_L 0x3
+#define ATV_TOP_EQU0_EQU_C0_LP 0x3
+#define ATV_TOP_EQU0_EQU_C0_BG 0x7
+#define ATV_TOP_EQU0_EQU_C0_DK 0x0
+#define ATV_TOP_EQU0_EQU_C0_I 0x3
+#define ATV_TOP_EQU1_EQU_C1_FM 0x0
+#define ATV_TOP_EQU1_EQU_C1_L 0x1F6
+#define ATV_TOP_EQU1_EQU_C1_LP 0x1F6
+#define ATV_TOP_EQU1_EQU_C1_BG 0x197
+#define ATV_TOP_EQU1_EQU_C1_DK 0x198
+#define ATV_TOP_EQU1_EQU_C1_I 0x1F6
+#define ATV_TOP_EQU2_EQU_C2_FM 0x0
+#define ATV_TOP_EQU2_EQU_C2_L 0x28
+#define ATV_TOP_EQU2_EQU_C2_LP 0x28
+#define ATV_TOP_EQU2_EQU_C2_BG 0xC5
+#define ATV_TOP_EQU2_EQU_C2_DK 0xB0
+#define ATV_TOP_EQU2_EQU_C2_I 0x28
+#define ATV_TOP_EQU3_EQU_C3_FM 0x0
+#define ATV_TOP_EQU3_EQU_C3_L 0x192
+#define ATV_TOP_EQU3_EQU_C3_LP 0x192
+#define ATV_TOP_EQU3_EQU_C3_BG 0x12E
+#define ATV_TOP_EQU3_EQU_C3_DK 0x18E
+#define ATV_TOP_EQU3_EQU_C3_I 0x192
+#define ATV_TOP_STD_MODE_MN 0x0
+#define ATV_TOP_STD_MODE_FM 0x1
+#define ATV_TOP_STD_MODE_L 0x0
+#define ATV_TOP_STD_MODE_LP 0x0
+#define ATV_TOP_STD_MODE_BG 0x0
+#define ATV_TOP_STD_MODE_DK 0x0
+#define ATV_TOP_STD_MODE_I 0x0
+#define ATV_TOP_STD_VID_POL_MN 0x0
+#define ATV_TOP_STD_VID_POL_FM 0x0
+#define ATV_TOP_STD_VID_POL_L 0x2
+#define ATV_TOP_STD_VID_POL_LP 0x2
+#define ATV_TOP_STD_VID_POL_BG 0x0
+#define ATV_TOP_STD_VID_POL_DK 0x0
+#define ATV_TOP_STD_VID_POL_I 0x0
+#define ATV_TOP_VID_AMP_MN 0x380
+#define ATV_TOP_VID_AMP_FM 0x0
+#define ATV_TOP_VID_AMP_L 0xF50
+#define ATV_TOP_VID_AMP_LP 0xF50
+#define ATV_TOP_VID_AMP_BG 0x380
+#define ATV_TOP_VID_AMP_DK 0x394
+#define ATV_TOP_VID_AMP_I 0x3D8
+#define IQM_CF_OUT_ENA_OFDM__M 0x4
+#define IQM_FS_ADJ_SEL_B_QAM 0x1
+#define IQM_FS_ADJ_SEL_B_OFF 0x0
+#define IQM_FS_ADJ_SEL_B_VSB 0x2
+#define IQM_RC_ADJ_SEL_B_OFF 0x0
+#define IQM_RC_ADJ_SEL_B_QAM 0x1
+#define IQM_RC_ADJ_SEL_B_VSB 0x2
+/**** END DJCOMBO patches to DRXJ registermap *********************************/
+
+#include "drx_driver_version.h"
+
+/* #define DRX_DEBUG */
+#ifdef DRX_DEBUG
+#include <stdio.h>
+#endif
+
+/*-----------------------------------------------------------------------------
+ENUMS
+----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+DEFINES
+----------------------------------------------------------------------------*/
+#ifndef DRXJ_WAKE_UP_KEY
+#define DRXJ_WAKE_UP_KEY (demod->my_i2c_dev_addr->i2c_addr)
+#endif
+
+/**
+* \def DRXJ_DEF_I2C_ADDR
+* \brief Default I2C addres of a demodulator instance.
+*/
+#define DRXJ_DEF_I2C_ADDR (0x52)
+
+/**
+* \def DRXJ_DEF_DEMOD_DEV_ID
+* \brief Default device identifier of a demodultor instance.
+*/
+#define DRXJ_DEF_DEMOD_DEV_ID (1)
+
+/**
+* \def DRXJ_SCAN_TIMEOUT
+* \brief Timeout value for waiting on demod lock during channel scan (millisec).
+*/
+#define DRXJ_SCAN_TIMEOUT 1000
+
+/**
+* \def HI_I2C_DELAY
+* \brief HI timing delay for I2C timing (in nano seconds)
+*
+* Used to compute HI_CFG_DIV
+*/
+#define HI_I2C_DELAY 42
+
+/**
+* \def HI_I2C_BRIDGE_DELAY
+* \brief HI timing delay for I2C timing (in nano seconds)
+*
+* Used to compute HI_CFG_BDL
+*/
+#define HI_I2C_BRIDGE_DELAY 750
+
+/**
+* \brief Time Window for MER and SER Measurement in Units of Segment duration.
+*/
+#define VSB_TOP_MEASUREMENT_PERIOD 64
+#define SYMBOLS_PER_SEGMENT 832
+
+/**
+* \brief bit rate and segment rate constants used for SER and BER.
+*/
+/* values taken from the QAM microcode */
+#define DRXJ_QAM_SL_SIG_POWER_QAM_UNKNOWN 0
+#define DRXJ_QAM_SL_SIG_POWER_QPSK 32768
+#define DRXJ_QAM_SL_SIG_POWER_QAM8 24576
+#define DRXJ_QAM_SL_SIG_POWER_QAM16 40960
+#define DRXJ_QAM_SL_SIG_POWER_QAM32 20480
+#define DRXJ_QAM_SL_SIG_POWER_QAM64 43008
+#define DRXJ_QAM_SL_SIG_POWER_QAM128 20992
+#define DRXJ_QAM_SL_SIG_POWER_QAM256 43520
+/**
+* \brief Min supported symbolrates.
+*/
+#ifndef DRXJ_QAM_SYMBOLRATE_MIN
+#define DRXJ_QAM_SYMBOLRATE_MIN (520000)
+#endif
+
+/**
+* \brief Max supported symbolrates.
+*/
+#ifndef DRXJ_QAM_SYMBOLRATE_MAX
+#define DRXJ_QAM_SYMBOLRATE_MAX (7233000)
+#endif
+
+/**
+* \def DRXJ_QAM_MAX_WAITTIME
+* \brief Maximal wait time for QAM auto constellation in ms
+*/
+#ifndef DRXJ_QAM_MAX_WAITTIME
+#define DRXJ_QAM_MAX_WAITTIME 900
+#endif
+
+#ifndef DRXJ_QAM_FEC_LOCK_WAITTIME
+#define DRXJ_QAM_FEC_LOCK_WAITTIME 150
+#endif
+
+#ifndef DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME
+#define DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME 200
+#endif
+
+/**
+* \def SCU status and results
+* \brief SCU
+*/
+#define DRX_SCU_READY 0
+#define DRXJ_MAX_WAITTIME 100 /* ms */
+#define FEC_RS_MEASUREMENT_PERIOD 12894 /* 1 sec */
+#define FEC_RS_MEASUREMENT_PRESCALE 1 /* n sec */
+
+/**
+* \def DRX_AUD_MAX_DEVIATION
+* \brief Needed for calculation of prescale feature in AUD
+*/
+#ifndef DRXJ_AUD_MAX_FM_DEVIATION
+#define DRXJ_AUD_MAX_FM_DEVIATION 100 /* kHz */
+#endif
+
+/**
+* \brief Needed for calculation of NICAM prescale feature in AUD
+*/
+#ifndef DRXJ_AUD_MAX_NICAM_PRESCALE
+#define DRXJ_AUD_MAX_NICAM_PRESCALE (9) /* dB */
+#endif
+
+/**
+* \brief Needed for calculation of NICAM prescale feature in AUD
+*/
+#ifndef DRXJ_AUD_MAX_WAITTIME
+#define DRXJ_AUD_MAX_WAITTIME 250 /* ms */
+#endif
+
+/* ATV config changed flags */
+#define DRXJ_ATV_CHANGED_COEF (0x00000001UL)
+#define DRXJ_ATV_CHANGED_PEAK_FLT (0x00000008UL)
+#define DRXJ_ATV_CHANGED_NOISE_FLT (0x00000010UL)
+#define DRXJ_ATV_CHANGED_OUTPUT (0x00000020UL)
+#define DRXJ_ATV_CHANGED_SIF_ATT (0x00000040UL)
+
+/* UIO define */
+#define DRX_UIO_MODE_FIRMWARE_SMA DRX_UIO_MODE_FIRMWARE0
+#define DRX_UIO_MODE_FIRMWARE_SAW DRX_UIO_MODE_FIRMWARE1
+
+/*
+ * MICROCODE RELATED DEFINES
+ */
+
+/* Magic word for checking correct Endianess of microcode data */
+#define DRX_UCODE_MAGIC_WORD ((((u16)'H')<<8)+((u16)'L'))
+
+/* CRC flag in ucode header, flags field. */
+#define DRX_UCODE_CRC_FLAG (0x0001)
+
+/*
+ * Maximum size of buffer used to verify the microcode.
+ * Must be an even number
+ */
+#define DRX_UCODE_MAX_BUF_SIZE (DRXDAP_MAX_RCHUNKSIZE)
+
+#if DRX_UCODE_MAX_BUF_SIZE & 1
+#error DRX_UCODE_MAX_BUF_SIZE must be an even number
+#endif
+
+/*
+ * Power mode macros
+ */
+
+#define DRX_ISPOWERDOWNMODE(mode) ((mode == DRX_POWER_MODE_9) || \
+ (mode == DRX_POWER_MODE_10) || \
+ (mode == DRX_POWER_MODE_11) || \
+ (mode == DRX_POWER_MODE_12) || \
+ (mode == DRX_POWER_MODE_13) || \
+ (mode == DRX_POWER_MODE_14) || \
+ (mode == DRX_POWER_MODE_15) || \
+ (mode == DRX_POWER_MODE_16) || \
+ (mode == DRX_POWER_DOWN))
+
+/* Pin safe mode macro */
+#define DRXJ_PIN_SAFE_MODE 0x0000
+/*============================================================================*/
+/*=== GLOBAL VARIABLEs =======================================================*/
+/*============================================================================*/
+/**
+*/
+
+/**
+* \brief Temporary register definitions.
+* (register definitions that are not yet available in register master)
+*/
+
+/******************************************************************************/
+/* Audio block 0x103 is write only. To avoid shadowing in driver accessing */
+/* RAM adresses directly. This must be READ ONLY to avoid problems. */
+/* Writing to the interface adresses is more than only writing the RAM */
+/* locations */
+/******************************************************************************/
+/**
+* \brief RAM location of MODUS registers
+*/
+#define AUD_DEM_RAM_MODUS_HI__A 0x10204A3
+#define AUD_DEM_RAM_MODUS_HI__M 0xF000
+
+#define AUD_DEM_RAM_MODUS_LO__A 0x10204A4
+#define AUD_DEM_RAM_MODUS_LO__M 0x0FFF
+
+/**
+* \brief RAM location of I2S config registers
+*/
+#define AUD_DEM_RAM_I2S_CONFIG1__A 0x10204B1
+#define AUD_DEM_RAM_I2S_CONFIG2__A 0x10204B2
+
+/**
+* \brief RAM location of DCO config registers
+*/
+#define AUD_DEM_RAM_DCO_B_HI__A 0x1020461
+#define AUD_DEM_RAM_DCO_B_LO__A 0x1020462
+#define AUD_DEM_RAM_DCO_A_HI__A 0x1020463
+#define AUD_DEM_RAM_DCO_A_LO__A 0x1020464
+
+/**
+* \brief RAM location of Threshold registers
+*/
+#define AUD_DEM_RAM_NICAM_THRSHLD__A 0x102045A
+#define AUD_DEM_RAM_A2_THRSHLD__A 0x10204BB
+#define AUD_DEM_RAM_BTSC_THRSHLD__A 0x10204A6
+
+/**
+* \brief RAM location of Carrier Threshold registers
+*/
+#define AUD_DEM_RAM_CM_A_THRSHLD__A 0x10204AF
+#define AUD_DEM_RAM_CM_B_THRSHLD__A 0x10204B0
+
+/**
+* \brief FM Matrix register fix
+*/
+#ifdef AUD_DEM_WR_FM_MATRIX__A
+#undef AUD_DEM_WR_FM_MATRIX__A
+#endif
+#define AUD_DEM_WR_FM_MATRIX__A 0x105006F
+
+/*============================================================================*/
+/**
+* \brief Defines required for audio
+*/
+#define AUD_VOLUME_ZERO_DB 115
+#define AUD_VOLUME_DB_MIN -60
+#define AUD_VOLUME_DB_MAX 12
+#define AUD_CARRIER_STRENGTH_QP_0DB 0x4000
+#define AUD_CARRIER_STRENGTH_QP_0DB_LOG10T100 421
+#define AUD_MAX_AVC_REF_LEVEL 15
+#define AUD_I2S_FREQUENCY_MAX 48000UL
+#define AUD_I2S_FREQUENCY_MIN 12000UL
+#define AUD_RDS_ARRAY_SIZE 18
+
+/**
+* \brief Needed for calculation of prescale feature in AUD
+*/
+#ifndef DRX_AUD_MAX_FM_DEVIATION
+#define DRX_AUD_MAX_FM_DEVIATION (100) /* kHz */
+#endif
+
+/**
+* \brief Needed for calculation of NICAM prescale feature in AUD
+*/
+#ifndef DRX_AUD_MAX_NICAM_PRESCALE
+#define DRX_AUD_MAX_NICAM_PRESCALE (9) /* dB */
+#endif
+
+/*============================================================================*/
+/* Values for I2S Master/Slave pin configurations */
+#define SIO_PDR_I2S_CL_CFG_MODE__MASTER 0x0004
+#define SIO_PDR_I2S_CL_CFG_DRIVE__MASTER 0x0008
+#define SIO_PDR_I2S_CL_CFG_MODE__SLAVE 0x0004
+#define SIO_PDR_I2S_CL_CFG_DRIVE__SLAVE 0x0000
+
+#define SIO_PDR_I2S_DA_CFG_MODE__MASTER 0x0003
+#define SIO_PDR_I2S_DA_CFG_DRIVE__MASTER 0x0008
+#define SIO_PDR_I2S_DA_CFG_MODE__SLAVE 0x0003
+#define SIO_PDR_I2S_DA_CFG_DRIVE__SLAVE 0x0008
+
+#define SIO_PDR_I2S_WS_CFG_MODE__MASTER 0x0004
+#define SIO_PDR_I2S_WS_CFG_DRIVE__MASTER 0x0008
+#define SIO_PDR_I2S_WS_CFG_MODE__SLAVE 0x0004
+#define SIO_PDR_I2S_WS_CFG_DRIVE__SLAVE 0x0000
+
+/*============================================================================*/
+/*=== REGISTER ACCESS MACROS =================================================*/
+/*============================================================================*/
+
+/**
+* This macro is used to create byte arrays for block writes.
+* Block writes speed up I2C traffic between host and demod.
+* The macro takes care of the required byte order in a 16 bits word.
+* x -> lowbyte(x), highbyte(x)
+*/
+#define DRXJ_16TO8(x) ((u8) (((u16)x) & 0xFF)), \
+ ((u8)((((u16)x)>>8)&0xFF))
+/**
+* This macro is used to convert byte array to 16 bit register value for block read.
+* Block read speed up I2C traffic between host and demod.
+* The macro takes care of the required byte order in a 16 bits word.
+*/
+#define DRXJ_8TO16(x) ((u16) (x[0] | (x[1] << 8)))
+
+/*============================================================================*/
+/*=== MISC DEFINES ===========================================================*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*=== HI COMMAND RELATED DEFINES =============================================*/
+/*============================================================================*/
+
+/**
+* \brief General maximum number of retries for ucode command interfaces
+*/
+#define DRXJ_MAX_RETRIES (100)
+
+/*============================================================================*/
+/*=== STANDARD RELATED MACROS ================================================*/
+/*============================================================================*/
+
+#define DRXJ_ISATVSTD(std) ((std == DRX_STANDARD_PAL_SECAM_BG) || \
+ (std == DRX_STANDARD_PAL_SECAM_DK) || \
+ (std == DRX_STANDARD_PAL_SECAM_I) || \
+ (std == DRX_STANDARD_PAL_SECAM_L) || \
+ (std == DRX_STANDARD_PAL_SECAM_LP) || \
+ (std == DRX_STANDARD_NTSC) || \
+ (std == DRX_STANDARD_FM))
+
+#define DRXJ_ISQAMSTD(std) ((std == DRX_STANDARD_ITU_A) || \
+ (std == DRX_STANDARD_ITU_B) || \
+ (std == DRX_STANDARD_ITU_C) || \
+ (std == DRX_STANDARD_ITU_D))
+
+/*-----------------------------------------------------------------------------
+GLOBAL VARIABLES
+----------------------------------------------------------------------------*/
+/*
+ * DRXJ DAP structures
+ */
+
+static int drxdap_fasi_read_block(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 datasize,
+ u8 *data, u32 flags);
+
+
+static int drxj_dap_read_modify_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 waddr,
+ u32 raddr,
+ u16 wdata, u16 *rdata);
+
+static int drxj_dap_read_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 *data, u32 flags);
+
+static int drxdap_fasi_read_reg32(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u32 *data, u32 flags);
+
+static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 datasize,
+ u8 *data, u32 flags);
+
+static int drxj_dap_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 data, u32 flags);
+
+static int drxdap_fasi_write_reg32(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u32 data, u32 flags);
+
+static struct drxj_data drxj_data_g = {
+ false, /* has_lna : true if LNA (aka PGA) present */
+ false, /* has_oob : true if OOB supported */
+ false, /* has_ntsc: true if NTSC supported */
+ false, /* has_btsc: true if BTSC supported */
+ false, /* has_smatx: true if SMA_TX pin is available */
+ false, /* has_smarx: true if SMA_RX pin is available */
+ false, /* has_gpio : true if GPIO pin is available */
+ false, /* has_irqn : true if IRQN pin is available */
+ 0, /* mfx A1/A2/A... */
+
+ /* tuner settings */
+ false, /* tuner mirrors RF signal */
+ /* standard/channel settings */
+ DRX_STANDARD_UNKNOWN, /* current standard */
+ DRX_CONSTELLATION_AUTO, /* constellation */
+ 0, /* frequency in KHz */
+ DRX_BANDWIDTH_UNKNOWN, /* curr_bandwidth */
+ DRX_MIRROR_NO, /* mirror */
+
+ /* signal quality information: */
+ /* default values taken from the QAM Programming guide */
+ /* fec_bits_desired should not be less than 4000000 */
+ 4000000, /* fec_bits_desired */
+ 5, /* fec_vd_plen */
+ 4, /* qam_vd_prescale */
+ 0xFFFF, /* qamVDPeriod */
+ 204 * 8, /* fec_rs_plen annex A */
+ 1, /* fec_rs_prescale */
+ FEC_RS_MEASUREMENT_PERIOD, /* fec_rs_period */
+ true, /* reset_pkt_err_acc */
+ 0, /* pkt_err_acc_start */
+
+ /* HI configuration */
+ 0, /* hi_cfg_timing_div */
+ 0, /* hi_cfg_bridge_delay */
+ 0, /* hi_cfg_wake_up_key */
+ 0, /* hi_cfg_ctrl */
+ 0, /* HICfgTimeout */
+ /* UIO configuartion */
+ DRX_UIO_MODE_DISABLE, /* uio_sma_rx_mode */
+ DRX_UIO_MODE_DISABLE, /* uio_sma_tx_mode */
+ DRX_UIO_MODE_DISABLE, /* uioASELMode */
+ DRX_UIO_MODE_DISABLE, /* uio_irqn_mode */
+ /* FS setting */
+ 0UL, /* iqm_fs_rate_ofs */
+ false, /* pos_image */
+ /* RC setting */
+ 0UL, /* iqm_rc_rate_ofs */
+ /* AUD information */
+/* false, * flagSetAUDdone */
+/* false, * detectedRDS */
+/* true, * flagASDRequest */
+/* false, * flagHDevClear */
+/* false, * flagHDevSet */
+/* (u16) 0xFFF, * rdsLastCount */
+
+ /* ATV configuartion */
+ 0UL, /* flags cfg changes */
+ /* shadow of ATV_TOP_EQU0__A */
+ {-5,
+ ATV_TOP_EQU0_EQU_C0_FM,
+ ATV_TOP_EQU0_EQU_C0_L,
+ ATV_TOP_EQU0_EQU_C0_LP,
+ ATV_TOP_EQU0_EQU_C0_BG,
+ ATV_TOP_EQU0_EQU_C0_DK,
+ ATV_TOP_EQU0_EQU_C0_I},
+ /* shadow of ATV_TOP_EQU1__A */
+ {-50,
+ ATV_TOP_EQU1_EQU_C1_FM,
+ ATV_TOP_EQU1_EQU_C1_L,
+ ATV_TOP_EQU1_EQU_C1_LP,
+ ATV_TOP_EQU1_EQU_C1_BG,
+ ATV_TOP_EQU1_EQU_C1_DK,
+ ATV_TOP_EQU1_EQU_C1_I},
+ /* shadow of ATV_TOP_EQU2__A */
+ {210,
+ ATV_TOP_EQU2_EQU_C2_FM,
+ ATV_TOP_EQU2_EQU_C2_L,
+ ATV_TOP_EQU2_EQU_C2_LP,
+ ATV_TOP_EQU2_EQU_C2_BG,
+ ATV_TOP_EQU2_EQU_C2_DK,
+ ATV_TOP_EQU2_EQU_C2_I},
+ /* shadow of ATV_TOP_EQU3__A */
+ {-160,
+ ATV_TOP_EQU3_EQU_C3_FM,
+ ATV_TOP_EQU3_EQU_C3_L,
+ ATV_TOP_EQU3_EQU_C3_LP,
+ ATV_TOP_EQU3_EQU_C3_BG,
+ ATV_TOP_EQU3_EQU_C3_DK,
+ ATV_TOP_EQU3_EQU_C3_I},
+ false, /* flag: true=bypass */
+ ATV_TOP_VID_PEAK__PRE, /* shadow of ATV_TOP_VID_PEAK__A */
+ ATV_TOP_NOISE_TH__PRE, /* shadow of ATV_TOP_NOISE_TH__A */
+ true, /* flag CVBS ouput enable */
+ false, /* flag SIF ouput enable */
+ DRXJ_SIF_ATTENUATION_0DB, /* current SIF att setting */
+ { /* qam_rf_agc_cfg */
+ DRX_STANDARD_ITU_B, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level */
+ 0xFFFF, /* max_output_level */
+ 0x0000, /* speed */
+ 0x0000, /* top */
+ 0x0000 /* c.o.c. */
+ },
+ { /* qam_if_agc_cfg */
+ DRX_STANDARD_ITU_B, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level */
+ 0xFFFF, /* max_output_level */
+ 0x0000, /* speed */
+ 0x0000, /* top (don't care) */
+ 0x0000 /* c.o.c. (don't care) */
+ },
+ { /* vsb_rf_agc_cfg */
+ DRX_STANDARD_8VSB, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level */
+ 0xFFFF, /* max_output_level */
+ 0x0000, /* speed */
+ 0x0000, /* top (don't care) */
+ 0x0000 /* c.o.c. (don't care) */
+ },
+ { /* vsb_if_agc_cfg */
+ DRX_STANDARD_8VSB, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level */
+ 0xFFFF, /* max_output_level */
+ 0x0000, /* speed */
+ 0x0000, /* top (don't care) */
+ 0x0000 /* c.o.c. (don't care) */
+ },
+ 0, /* qam_pga_cfg */
+ 0, /* vsb_pga_cfg */
+ { /* qam_pre_saw_cfg */
+ DRX_STANDARD_ITU_B, /* standard */
+ 0, /* reference */
+ false /* use_pre_saw */
+ },
+ { /* vsb_pre_saw_cfg */
+ DRX_STANDARD_8VSB, /* standard */
+ 0, /* reference */
+ false /* use_pre_saw */
+ },
+
+ /* Version information */
+#ifndef _CH_
+ {
+ "01234567890", /* human readable version microcode */
+ "01234567890" /* human readable version device specific code */
+ },
+ {
+ { /* struct drx_version for microcode */
+ DRX_MODULE_UNKNOWN,
+ (char *)(NULL),
+ 0,
+ 0,
+ 0,
+ (char *)(NULL)
+ },
+ { /* struct drx_version for device specific code */
+ DRX_MODULE_UNKNOWN,
+ (char *)(NULL),
+ 0,
+ 0,
+ 0,
+ (char *)(NULL)
+ }
+ },
+ {
+ { /* struct drx_version_list for microcode */
+ (struct drx_version *) (NULL),
+ (struct drx_version_list *) (NULL)
+ },
+ { /* struct drx_version_list for device specific code */
+ (struct drx_version *) (NULL),
+ (struct drx_version_list *) (NULL)
+ }
+ },
+#endif
+ false, /* smart_ant_inverted */
+ /* Tracking filter setting for OOB */
+ {
+ 12000,
+ 9300,
+ 6600,
+ 5280,
+ 3700,
+ 3000,
+ 2000,
+ 0},
+ false, /* oob_power_on */
+ 0, /* mpeg_ts_static_bitrate */
+ false, /* disable_te_ihandling */
+ false, /* bit_reverse_mpeg_outout */
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_AUTO, /* mpeg_output_clock_rate */
+ DRXJ_MPEG_START_WIDTH_1CLKCYC, /* mpeg_start_width */
+
+ /* Pre SAW & Agc configuration for ATV */
+ {
+ DRX_STANDARD_NTSC, /* standard */
+ 7, /* reference */
+ true /* use_pre_saw */
+ },
+ { /* ATV RF-AGC */
+ DRX_STANDARD_NTSC, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level (d.c.) */
+ 0, /* max_output_level (d.c.) */
+ 3, /* speed */
+ 9500, /* top */
+ 4000 /* cut-off current */
+ },
+ { /* ATV IF-AGC */
+ DRX_STANDARD_NTSC, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level (d.c.) */
+ 0, /* max_output_level (d.c.) */
+ 3, /* speed */
+ 2400, /* top */
+ 0 /* c.o.c. (d.c.) */
+ },
+ 140, /* ATV PGA config */
+ 0, /* curr_symbol_rate */
+
+ false, /* pdr_safe_mode */
+ SIO_PDR_GPIO_CFG__PRE, /* pdr_safe_restore_val_gpio */
+ SIO_PDR_VSYNC_CFG__PRE, /* pdr_safe_restore_val_v_sync */
+ SIO_PDR_SMA_RX_CFG__PRE, /* pdr_safe_restore_val_sma_rx */
+ SIO_PDR_SMA_TX_CFG__PRE, /* pdr_safe_restore_val_sma_tx */
+
+ 4, /* oob_pre_saw */
+ DRXJ_OOB_LO_POW_MINUS10DB, /* oob_lo_pow */
+ {
+ false /* aud_data, only first member */
+ },
+};
+
+/**
+* \var drxj_default_addr_g
+* \brief Default I2C address and device identifier.
+*/
+static struct i2c_device_addr drxj_default_addr_g = {
+ DRXJ_DEF_I2C_ADDR, /* i2c address */
+ DRXJ_DEF_DEMOD_DEV_ID /* device id */
+};
+
+/**
+* \var drxj_default_comm_attr_g
+* \brief Default common attributes of a drxj demodulator instance.
+*/
+static struct drx_common_attr drxj_default_comm_attr_g = {
+ NULL, /* ucode file */
+ true, /* ucode verify switch */
+ {0}, /* version record */
+
+ 44000, /* IF in kHz in case no tuner instance is used */
+ (151875 - 0), /* system clock frequency in kHz */
+ 0, /* oscillator frequency kHz */
+ 0, /* oscillator deviation in ppm, signed */
+ false, /* If true mirror frequency spectrum */
+ {
+ /* MPEG output configuration */
+ true, /* If true, enable MPEG ouput */
+ false, /* If true, insert RS byte */
+ false, /* If true, parallel out otherwise serial */
+ false, /* If true, invert DATA signals */
+ false, /* If true, invert ERR signal */
+ false, /* If true, invert STR signals */
+ false, /* If true, invert VAL signals */
+ false, /* If true, invert CLK signals */
+ true, /* If true, static MPEG clockrate will
+ be used, otherwise clockrate will
+ adapt to the bitrate of the TS */
+ 19392658UL, /* Maximum bitrate in b/s in case
+ static clockrate is selected */
+ DRX_MPEG_STR_WIDTH_1 /* MPEG Start width in clock cycles */
+ },
+ /* Initilisations below can be ommited, they require no user input and
+ are initialy 0, NULL or false. The compiler will initialize them to these
+ values when ommited. */
+ false, /* is_opened */
+
+ /* SCAN */
+ NULL, /* no scan params yet */
+ 0, /* current scan index */
+ 0, /* next scan frequency */
+ false, /* scan ready flag */
+ 0, /* max channels to scan */
+ 0, /* nr of channels scanned */
+ NULL, /* default scan function */
+ NULL, /* default context pointer */
+ 0, /* millisec to wait for demod lock */
+ DRXJ_DEMOD_LOCK, /* desired lock */
+ false,
+
+ /* Power management */
+ DRX_POWER_UP,
+
+ /* Tuner */
+ 1, /* nr of I2C port to wich tuner is */
+ 0L, /* minimum RF input frequency, in kHz */
+ 0L, /* maximum RF input frequency, in kHz */
+ false, /* Rf Agc Polarity */
+ false, /* If Agc Polarity */
+ false, /* tuner slow mode */
+
+ { /* current channel (all 0) */
+ 0UL /* channel.frequency */
+ },
+ DRX_STANDARD_UNKNOWN, /* current standard */
+ DRX_STANDARD_UNKNOWN, /* previous standard */
+ DRX_STANDARD_UNKNOWN, /* di_cache_standard */
+ false, /* use_bootloader */
+ 0UL, /* capabilities */
+ 0 /* mfx */
+};
+
+/**
+* \var drxj_default_demod_g
+* \brief Default drxj demodulator instance.
+*/
+static struct drx_demod_instance drxj_default_demod_g = {
+ &drxj_default_addr_g, /* i2c address & device id */
+ &drxj_default_comm_attr_g, /* demod common attributes */
+ &drxj_data_g /* demod device specific attributes */
+};
+
+/**
+* \brief Default audio data structure for DRK demodulator instance.
+*
+* This structure is DRXK specific.
+*
+*/
+static struct drx_aud_data drxj_default_aud_data_g = {
+ false, /* audio_is_active */
+ DRX_AUD_STANDARD_AUTO, /* audio_standard */
+
+ /* i2sdata */
+ {
+ false, /* output_enable */
+ 48000, /* frequency */
+ DRX_I2S_MODE_MASTER, /* mode */
+ DRX_I2S_WORDLENGTH_32, /* word_length */
+ DRX_I2S_POLARITY_RIGHT, /* polarity */
+ DRX_I2S_FORMAT_WS_WITH_DATA /* format */
+ },
+ /* volume */
+ {
+ true, /* mute; */
+ 0, /* volume */
+ DRX_AUD_AVC_OFF, /* avc_mode */
+ 0, /* avc_ref_level */
+ DRX_AUD_AVC_MAX_GAIN_12DB, /* avc_max_gain */
+ DRX_AUD_AVC_MAX_ATTEN_24DB, /* avc_max_atten */
+ 0, /* strength_left */
+ 0 /* strength_right */
+ },
+ DRX_AUD_AUTO_SOUND_SELECT_ON_CHANGE_ON, /* auto_sound */
+ /* ass_thresholds */
+ {
+ 440, /* A2 */
+ 12, /* BTSC */
+ 700, /* NICAM */
+ },
+ /* carrier */
+ {
+ /* a */
+ {
+ 42, /* thres */
+ DRX_NO_CARRIER_NOISE, /* opt */
+ 0, /* shift */
+ 0 /* dco */
+ },
+ /* b */
+ {
+ 42, /* thres */
+ DRX_NO_CARRIER_MUTE, /* opt */
+ 0, /* shift */
+ 0 /* dco */
+ },
+
+ },
+ /* mixer */
+ {
+ DRX_AUD_SRC_STEREO_OR_A, /* source_i2s */
+ DRX_AUD_I2S_MATRIX_STEREO, /* matrix_i2s */
+ DRX_AUD_FM_MATRIX_SOUND_A /* matrix_fm */
+ },
+ DRX_AUD_DEVIATION_NORMAL, /* deviation */
+ DRX_AUD_AVSYNC_OFF, /* av_sync */
+
+ /* prescale */
+ {
+ DRX_AUD_MAX_FM_DEVIATION, /* fm_deviation */
+ DRX_AUD_MAX_NICAM_PRESCALE /* nicam_gain */
+ },
+ DRX_AUD_FM_DEEMPH_75US, /* deemph */
+ DRX_BTSC_STEREO, /* btsc_detect */
+ 0, /* rds_data_counter */
+ false /* rds_data_present */
+};
+
+/*-----------------------------------------------------------------------------
+STRUCTURES
+----------------------------------------------------------------------------*/
+struct drxjeq_stat {
+ u16 eq_mse;
+ u8 eq_mode;
+ u8 eq_ctrl;
+ u8 eq_stat;
+};
+
+/* HI command */
+struct drxj_hi_cmd {
+ u16 cmd;
+ u16 param1;
+ u16 param2;
+ u16 param3;
+ u16 param4;
+ u16 param5;
+ u16 param6;
+};
+
+/*============================================================================*/
+/*=== MICROCODE RELATED STRUCTURES ===========================================*/
+/*============================================================================*/
+
+/**
+ * struct drxu_code_block_hdr - Structure of the microcode block headers
+ *
+ * @addr: Destination address of the data in this block
+ * @size: Size of the block data following this header counted in
+ * 16 bits words
+ * @CRC: CRC value of the data block, only valid if CRC flag is
+ * set.
+ */
+struct drxu_code_block_hdr {
+ u32 addr;
+ u16 size;
+ u16 flags;
+ u16 CRC;
+};
+
+/*-----------------------------------------------------------------------------
+FUNCTIONS
+----------------------------------------------------------------------------*/
+/* Some prototypes */
+static int
+hi_command(struct i2c_device_addr *dev_addr,
+ const struct drxj_hi_cmd *cmd, u16 *result);
+
+static int
+ctrl_lock_status(struct drx_demod_instance *demod, enum drx_lock_status *lock_stat);
+
+static int
+ctrl_power_mode(struct drx_demod_instance *demod, enum drx_power_mode *mode);
+
+static int power_down_aud(struct drx_demod_instance *demod);
+
+static int
+ctrl_set_cfg_pre_saw(struct drx_demod_instance *demod, struct drxj_cfg_pre_saw *pre_saw);
+
+static int
+ctrl_set_cfg_afe_gain(struct drx_demod_instance *demod, struct drxj_cfg_afe_gain *afe_gain);
+
+/*============================================================================*/
+/*============================================================================*/
+/*== HELPER FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+
+/*============================================================================*/
+
+/*
+* \fn u32 frac28(u32 N, u32 D)
+* \brief Compute: (1<<28)*N/D
+* \param N 32 bits
+* \param D 32 bits
+* \return (1<<28)*N/D
+* This function is used to avoid floating-point calculations as they may
+* not be present on the target platform.
+
+* frac28 performs an unsigned 28/28 bits division to 32-bit fixed point
+* fraction used for setting the Frequency Shifter registers.
+* N and D can hold numbers up to width: 28-bits.
+* The 4 bits integer part and the 28 bits fractional part are calculated.
+
+* Usage condition: ((1<<28)*n)/d < ((1<<32)-1) => (n/d) < 15.999
+
+* N: 0...(1<<28)-1 = 268435454
+* D: 0...(1<<28)-1
+* Q: 0...(1<<32)-1
+*/
+static u32 frac28(u32 N, u32 D)
+{
+ int i = 0;
+ u32 Q1 = 0;
+ u32 R0 = 0;
+
+ R0 = (N % D) << 4; /* 32-28 == 4 shifts possible at max */
+ Q1 = N / D; /* integer part, only the 4 least significant bits
+ will be visible in the result */
+
+ /* division using radix 16, 7 nibbles in the result */
+ for (i = 0; i < 7; i++) {
+ Q1 = (Q1 << 4) | R0 / D;
+ R0 = (R0 % D) << 4;
+ }
+ /* rounding */
+ if ((R0 >> 3) >= D)
+ Q1++;
+
+ return Q1;
+}
+
+/**
+* \fn u32 log1_times100( u32 x)
+* \brief Compute: 100*log10(x)
+* \param x 32 bits
+* \return 100*log10(x)
+*
+* 100*log10(x)
+* = 100*(log2(x)/log2(10)))
+* = (100*(2^15)*log2(x))/((2^15)*log2(10))
+* = ((200*(2^15)*log2(x))/((2^15)*log2(10)))/2
+* = ((200*(2^15)*(log2(x/y)+log2(y)))/((2^15)*log2(10)))/2
+* = ((200*(2^15)*log2(x/y))+(200*(2^15)*log2(y)))/((2^15)*log2(10)))/2
+*
+* where y = 2^k and 1<= (x/y) < 2
+*/
+
+static u32 log1_times100(u32 x)
+{
+ static const u8 scale = 15;
+ static const u8 index_width = 5;
+ /*
+ log2lut[n] = (1<<scale) * 200 * log2( 1.0 + ( (1.0/(1<<INDEXWIDTH)) * n ))
+ 0 <= n < ((1<<INDEXWIDTH)+1)
+ */
+
+ static const u32 log2lut[] = {
+ 0, /* 0.000000 */
+ 290941, /* 290941.300628 */
+ 573196, /* 573196.476418 */
+ 847269, /* 847269.179851 */
+ 1113620, /* 1113620.489452 */
+ 1372674, /* 1372673.576986 */
+ 1624818, /* 1624817.752104 */
+ 1870412, /* 1870411.981536 */
+ 2109788, /* 2109787.962654 */
+ 2343253, /* 2343252.817465 */
+ 2571091, /* 2571091.461923 */
+ 2793569, /* 2793568.696416 */
+ 3010931, /* 3010931.055901 */
+ 3223408, /* 3223408.452106 */
+ 3431216, /* 3431215.635215 */
+ 3634553, /* 3634553.498355 */
+ 3833610, /* 3833610.244726 */
+ 4028562, /* 4028562.434393 */
+ 4219576, /* 4219575.925308 */
+ 4406807, /* 4406806.721144 */
+ 4590402, /* 4590401.736809 */
+ 4770499, /* 4770499.491025 */
+ 4947231, /* 4947230.734179 */
+ 5120719, /* 5120719.018555 */
+ 5291081, /* 5291081.217197 */
+ 5458428, /* 5458427.996830 */
+ 5622864, /* 5622864.249668 */
+ 5784489, /* 5784489.488298 */
+ 5943398, /* 5943398.207380 */
+ 6099680, /* 6099680.215452 */
+ 6253421, /* 6253420.939751 */
+ 6404702, /* 6404701.706649 */
+ 6553600, /* 6553600.000000 */
+ };
+
+ u8 i = 0;
+ u32 y = 0;
+ u32 d = 0;
+ u32 k = 0;
+ u32 r = 0;
+
+ if (x == 0)
+ return 0;
+
+ /* Scale x (normalize) */
+ /* computing y in log(x/y) = log(x) - log(y) */
+ if ((x & (((u32) (-1)) << (scale + 1))) == 0) {
+ for (k = scale; k > 0; k--) {
+ if (x & (((u32) 1) << scale))
+ break;
+ x <<= 1;
+ }
+ } else {
+ for (k = scale; k < 31; k++) {
+ if ((x & (((u32) (-1)) << (scale + 1))) == 0)
+ break;
+ x >>= 1;
+ }
+ }
+ /*
+ Now x has binary point between bit[scale] and bit[scale-1]
+ and 1.0 <= x < 2.0 */
+
+ /* correction for divison: log(x) = log(x/y)+log(y) */
+ y = k * ((((u32) 1) << scale) * 200);
+
+ /* remove integer part */
+ x &= ((((u32) 1) << scale) - 1);
+ /* get index */
+ i = (u8) (x >> (scale - index_width));
+ /* compute delta (x-a) */
+ d = x & ((((u32) 1) << (scale - index_width)) - 1);
+ /* compute log, multiplication ( d* (.. )) must be within range ! */
+ y += log2lut[i] +
+ ((d * (log2lut[i + 1] - log2lut[i])) >> (scale - index_width));
+ /* Conver to log10() */
+ y /= 108853; /* (log2(10) << scale) */
+ r = (y >> 1);
+ /* rounding */
+ if (y & ((u32)1))
+ r++;
+
+ return r;
+
+}
+
+/**
+* \fn u32 frac_times1e6( u16 N, u32 D)
+* \brief Compute: (N/D) * 1000000.
+* \param N nominator 16-bits.
+* \param D denominator 32-bits.
+* \return u32
+* \retval ((N/D) * 1000000), 32 bits
+*
+* No check on D=0!
+*/
+static u32 frac_times1e6(u32 N, u32 D)
+{
+ u32 remainder = 0;
+ u32 frac = 0;
+
+ /*
+ frac = (N * 1000000) / D
+ To let it fit in a 32 bits computation:
+ frac = (N * (1000000 >> 4)) / (D >> 4)
+ This would result in a problem in case D < 16 (div by 0).
+ So we do it more elaborate as shown below.
+ */
+ frac = (((u32) N) * (1000000 >> 4)) / D;
+ frac <<= 4;
+ remainder = (((u32) N) * (1000000 >> 4)) % D;
+ remainder <<= 4;
+ frac += remainder / D;
+ remainder = remainder % D;
+ if ((remainder * 2) > D)
+ frac++;
+
+ return frac;
+}
+
+/*============================================================================*/
+
+
+/**
+* \brief Values for NICAM prescaler gain. Computed from dB to integer
+* and rounded. For calc used formula: 16*10^(prescaleGain[dB]/20).
+*
+*/
+static const u16 nicam_presc_table_val[43] = {
+ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16,
+ 18, 20, 23, 25, 28, 32, 36, 40, 45,
+ 51, 57, 64, 71, 80, 90, 101, 113, 127
+};
+
+/*============================================================================*/
+/*== END HELPER FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== DRXJ DAP FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*
+ This layer takes care of some device specific register access protocols:
+ -conversion to short address format
+ -access to audio block
+ This layer is placed between the drx_dap_fasi and the rest of the drxj
+ specific implementation. This layer can use address map knowledge whereas
+ dap_fasi may not use memory map knowledge.
+
+ * For audio currently only 16 bits read and write register access is
+ supported. More is not needed. RMW and 32 or 8 bit access on audio
+ registers will have undefined behaviour. Flags (RMW, CRC reset, broadcast
+ single/multi master) will be ignored.
+
+ TODO: check ignoring single/multimaster is ok for AUD access ?
+*/
+
+#define DRXJ_ISAUDWRITE(addr) (((((addr)>>16)&1) == 1) ? true : false)
+#define DRXJ_DAP_AUDTRIF_TIMEOUT 80 /* millisec */
+/*============================================================================*/
+
+/**
+* \fn bool is_handled_by_aud_tr_if( u32 addr )
+* \brief Check if this address is handled by the audio token ring interface.
+* \param addr
+* \return bool
+* \retval true Yes, handled by audio token ring interface
+* \retval false No, not handled by audio token ring interface
+*
+*/
+static
+bool is_handled_by_aud_tr_if(u32 addr)
+{
+ bool retval = false;
+
+ if ((DRXDAP_FASI_ADDR2BLOCK(addr) == 4) &&
+ (DRXDAP_FASI_ADDR2BANK(addr) > 1) &&
+ (DRXDAP_FASI_ADDR2BANK(addr) < 6)) {
+ retval = true;
+ }
+
+ return retval;
+}
+
+/*============================================================================*/
+
+int drxbsp_i2c_write_read(struct i2c_device_addr *w_dev_addr,
+ u16 w_count,
+ u8 *wData,
+ struct i2c_device_addr *r_dev_addr,
+ u16 r_count, u8 *r_data)
+{
+ struct drx39xxj_state *state;
+ struct i2c_msg msg[2];
+ unsigned int num_msgs;
+
+ if (w_dev_addr == NULL) {
+ /* Read only */
+ state = r_dev_addr->user_data;
+ msg[0].addr = r_dev_addr->i2c_addr >> 1;
+ msg[0].flags = I2C_M_RD;
+ msg[0].buf = r_data;
+ msg[0].len = r_count;
+ num_msgs = 1;
+ } else if (r_dev_addr == NULL) {
+ /* Write only */
+ state = w_dev_addr->user_data;
+ msg[0].addr = w_dev_addr->i2c_addr >> 1;
+ msg[0].flags = 0;
+ msg[0].buf = wData;
+ msg[0].len = w_count;
+ num_msgs = 1;
+ } else {
+ /* Both write and read */
+ state = w_dev_addr->user_data;
+ msg[0].addr = w_dev_addr->i2c_addr >> 1;
+ msg[0].flags = 0;
+ msg[0].buf = wData;
+ msg[0].len = w_count;
+ msg[1].addr = r_dev_addr->i2c_addr >> 1;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = r_data;
+ msg[1].len = r_count;
+ num_msgs = 2;
+ }
+
+ if (state->i2c == NULL) {
+ pr_err("i2c was zero, aborting\n");
+ return 0;
+ }
+ if (i2c_transfer(state->i2c, msg, num_msgs) != num_msgs) {
+ pr_warn("drx3933: I2C write/read failed\n");
+ return -EREMOTEIO;
+ }
+
+#ifdef DJH_DEBUG
+ if (w_dev_addr == NULL || r_dev_addr == NULL)
+ return 0;
+
+ state = w_dev_addr->user_data;
+
+ if (state->i2c == NULL)
+ return 0;
+
+ msg[0].addr = w_dev_addr->i2c_addr;
+ msg[0].flags = 0;
+ msg[0].buf = wData;
+ msg[0].len = w_count;
+ msg[1].addr = r_dev_addr->i2c_addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = r_data;
+ msg[1].len = r_count;
+ num_msgs = 2;
+
+ pr_debug("drx3933 i2c operation addr=%x i2c=%p, wc=%x rc=%x\n",
+ w_dev_addr->i2c_addr, state->i2c, w_count, r_count);
+
+ if (i2c_transfer(state->i2c, msg, 2) != 2) {
+ pr_warn("drx3933: I2C write/read failed\n");
+ return -EREMOTEIO;
+ }
+#endif
+ return 0;
+}
+
+/*============================================================================*/
+
+/******************************
+*
+* int drxdap_fasi_read_block (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u16 datasize, -- number of bytes to read
+* u8 *data, -- data to receive
+* u32 flags) -- special device flags
+*
+* Read block data from chip address. Because the chip is word oriented,
+* the number of bytes to read must be even.
+*
+* Make sure that the buffer to receive the data is large enough.
+*
+* Although this function expects an even number of bytes, it is still byte
+* oriented, and the data read back is NOT translated to the endianness of
+* the target platform.
+*
+* Output:
+* - 0 if reading was successful
+* in that case: data read is in *data.
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_read_block(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 datasize,
+ u8 *data, u32 flags)
+{
+ u8 buf[4];
+ u16 bufx;
+ int rc;
+ u16 overhead_size = 0;
+
+ /* Check parameters ******************************************************* */
+ if (dev_addr == NULL)
+ return -EINVAL;
+
+ overhead_size = (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1) +
+ (DRXDAP_FASI_LONG_FORMAT(addr) ? 4 : 2);
+
+ if ((DRXDAP_FASI_OFFSET_TOO_LARGE(addr)) ||
+ ((!(DRXDAPFASI_LONG_ADDR_ALLOWED)) &&
+ DRXDAP_FASI_LONG_FORMAT(addr)) ||
+ (overhead_size > (DRXDAP_MAX_WCHUNKSIZE)) ||
+ ((datasize != 0) && (data == NULL)) || ((datasize & 1) == 1)) {
+ return -EINVAL;
+ }
+
+ /* ReadModifyWrite & mode flag bits are not allowed */
+ flags &= (~DRXDAP_FASI_RMW & ~DRXDAP_FASI_MODEFLAGS);
+#if DRXDAP_SINGLE_MASTER
+ flags |= DRXDAP_FASI_SINGLE_MASTER;
+#endif
+
+ /* Read block from I2C **************************************************** */
+ do {
+ u16 todo = (datasize < DRXDAP_MAX_RCHUNKSIZE ?
+ datasize : DRXDAP_MAX_RCHUNKSIZE);
+
+ bufx = 0;
+
+ addr &= ~DRXDAP_FASI_FLAGS;
+ addr |= flags;
+
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1))
+ /* short format address preferred but long format otherwise */
+ if (DRXDAP_FASI_LONG_FORMAT(addr)) {
+#endif
+#if (DRXDAPFASI_LONG_ADDR_ALLOWED == 1)
+ buf[bufx++] = (u8) (((addr << 1) & 0xFF) | 0x01);
+ buf[bufx++] = (u8) ((addr >> 16) & 0xFF);
+ buf[bufx++] = (u8) ((addr >> 24) & 0xFF);
+ buf[bufx++] = (u8) ((addr >> 7) & 0xFF);
+#endif
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1))
+ } else {
+#endif
+#if (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1)
+ buf[bufx++] = (u8) ((addr << 1) & 0xFF);
+ buf[bufx++] =
+ (u8) (((addr >> 16) & 0x0F) |
+ ((addr >> 18) & 0xF0));
+#endif
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1))
+ }
+#endif
+
+#if DRXDAP_SINGLE_MASTER
+ /*
+ * In single master mode, split the read and write actions.
+ * No special action is needed for write chunks here.
+ */
+ rc = drxbsp_i2c_write_read(dev_addr, bufx, buf,
+ NULL, 0, NULL);
+ if (rc == 0)
+ rc = drxbsp_i2c_write_read(NULL, 0, NULL, dev_addr, todo, data);
+#else
+ /* In multi master mode, do everything in one RW action */
+ rc = drxbsp_i2c_write_read(dev_addr, bufx, buf, dev_addr, todo,
+ data);
+#endif
+ data += todo;
+ addr += (todo >> 1);
+ datasize -= todo;
+ } while (datasize && rc == 0);
+
+ return rc;
+}
+
+
+/******************************
+*
+* int drxdap_fasi_read_reg16 (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u16 *data, -- data to receive
+* u32 flags) -- special device flags
+*
+* Read one 16-bit register or memory location. The data received back is
+* converted back to the target platform's endianness.
+*
+* Output:
+* - 0 if reading was successful
+* in that case: read data is at *data
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_read_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 *data, u32 flags)
+{
+ u8 buf[sizeof(*data)];
+ int rc;
+
+ if (!data)
+ return -EINVAL;
+
+ rc = drxdap_fasi_read_block(dev_addr, addr, sizeof(*data), buf, flags);
+ *data = buf[0] + (((u16) buf[1]) << 8);
+ return rc;
+}
+
+/******************************
+*
+* int drxdap_fasi_read_reg32 (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u32 *data, -- data to receive
+* u32 flags) -- special device flags
+*
+* Read one 32-bit register or memory location. The data received back is
+* converted back to the target platform's endianness.
+*
+* Output:
+* - 0 if reading was successful
+* in that case: read data is at *data
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_read_reg32(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u32 *data, u32 flags)
+{
+ u8 buf[sizeof(*data)];
+ int rc;
+
+ if (!data)
+ return -EINVAL;
+
+ rc = drxdap_fasi_read_block(dev_addr, addr, sizeof(*data), buf, flags);
+ *data = (((u32) buf[0]) << 0) +
+ (((u32) buf[1]) << 8) +
+ (((u32) buf[2]) << 16) + (((u32) buf[3]) << 24);
+ return rc;
+}
+
+/******************************
+*
+* int drxdap_fasi_write_block (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u16 datasize, -- number of bytes to read
+* u8 *data, -- data to receive
+* u32 flags) -- special device flags
+*
+* Write block data to chip address. Because the chip is word oriented,
+* the number of bytes to write must be even.
+*
+* Although this function expects an even number of bytes, it is still byte
+* oriented, and the data being written is NOT translated from the endianness of
+* the target platform.
+*
+* Output:
+* - 0 if writing was successful
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 datasize,
+ u8 *data, u32 flags)
+{
+ u8 buf[DRXDAP_MAX_WCHUNKSIZE];
+ int st = -EIO;
+ int first_err = 0;
+ u16 overhead_size = 0;
+ u16 block_size = 0;
+
+ /* Check parameters ******************************************************* */
+ if (dev_addr == NULL)
+ return -EINVAL;
+
+ overhead_size = (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1) +
+ (DRXDAP_FASI_LONG_FORMAT(addr) ? 4 : 2);
+
+ if ((DRXDAP_FASI_OFFSET_TOO_LARGE(addr)) ||
+ ((!(DRXDAPFASI_LONG_ADDR_ALLOWED)) &&
+ DRXDAP_FASI_LONG_FORMAT(addr)) ||
+ (overhead_size > (DRXDAP_MAX_WCHUNKSIZE)) ||
+ ((datasize != 0) && (data == NULL)) || ((datasize & 1) == 1))
+ return -EINVAL;
+
+ flags &= DRXDAP_FASI_FLAGS;
+ flags &= ~DRXDAP_FASI_MODEFLAGS;
+#if DRXDAP_SINGLE_MASTER
+ flags |= DRXDAP_FASI_SINGLE_MASTER;
+#endif
+
+ /* Write block to I2C ***************************************************** */
+ block_size = ((DRXDAP_MAX_WCHUNKSIZE) - overhead_size) & ~1;
+ do {
+ u16 todo = 0;
+ u16 bufx = 0;
+
+ /* Buffer device address */
+ addr &= ~DRXDAP_FASI_FLAGS;
+ addr |= flags;
+#if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1))
+ /* short format address preferred but long format otherwise */
+ if (DRXDAP_FASI_LONG_FORMAT(addr)) {
+#endif
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1)
+ buf[bufx++] = (u8) (((addr << 1) & 0xFF) | 0x01);
+ buf[bufx++] = (u8) ((addr >> 16) & 0xFF);
+ buf[bufx++] = (u8) ((addr >> 24) & 0xFF);
+ buf[bufx++] = (u8) ((addr >> 7) & 0xFF);
+#endif
+#if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1))
+ } else {
+#endif
+#if ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1)
+ buf[bufx++] = (u8) ((addr << 1) & 0xFF);
+ buf[bufx++] =
+ (u8) (((addr >> 16) & 0x0F) |
+ ((addr >> 18) & 0xF0));
+#endif
+#if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1))
+ }
+#endif
+
+ /*
+ In single master mode block_size can be 0. In such a case this I2C
+ sequense will be visible: (1) write address {i2c addr,
+ 4 bytes chip address} (2) write data {i2c addr, 4 bytes data }
+ (3) write address (4) write data etc...
+ Addres must be rewriten because HI is reset after data transport and
+ expects an address.
+ */
+ todo = (block_size < datasize ? block_size : datasize);
+ if (todo == 0) {
+ u16 overhead_size_i2c_addr = 0;
+ u16 data_block_size = 0;
+
+ overhead_size_i2c_addr =
+ (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1);
+ data_block_size =
+ (DRXDAP_MAX_WCHUNKSIZE - overhead_size_i2c_addr) & ~1;
+
+ /* write device address */
+ st = drxbsp_i2c_write_read(dev_addr,
+ (u16) (bufx),
+ buf,
+ (struct i2c_device_addr *)(NULL),
+ 0, (u8 *)(NULL));
+
+ if ((st != 0) && (first_err == 0)) {
+ /* at the end, return the first error encountered */
+ first_err = st;
+ }
+ bufx = 0;
+ todo =
+ (data_block_size <
+ datasize ? data_block_size : datasize);
+ }
+ memcpy(&buf[bufx], data, todo);
+ /* write (address if can do and) data */
+ st = drxbsp_i2c_write_read(dev_addr,
+ (u16) (bufx + todo),
+ buf,
+ (struct i2c_device_addr *)(NULL),
+ 0, (u8 *)(NULL));
+
+ if ((st != 0) && (first_err == 0)) {
+ /* at the end, return the first error encountered */
+ first_err = st;
+ }
+ datasize -= todo;
+ data += todo;
+ addr += (todo >> 1);
+ } while (datasize);
+
+ return first_err;
+}
+
+/******************************
+*
+* int drxdap_fasi_write_reg16 (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u16 data, -- data to send
+* u32 flags) -- special device flags
+*
+* Write one 16-bit register or memory location. The data being written is
+* converted from the target platform's endianness to little endian.
+*
+* Output:
+* - 0 if writing was successful
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 data, u32 flags)
+{
+ u8 buf[sizeof(data)];
+
+ buf[0] = (u8) ((data >> 0) & 0xFF);
+ buf[1] = (u8) ((data >> 8) & 0xFF);
+
+ return drxdap_fasi_write_block(dev_addr, addr, sizeof(data), buf, flags);
+}
+
+/******************************
+*
+* int drxdap_fasi_read_modify_write_reg16 (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 waddr, -- address of chip register/memory
+* u32 raddr, -- chip address to read back from
+* u16 wdata, -- data to send
+* u16 *rdata) -- data to receive back
+*
+* Write 16-bit data, then read back the original contents of that location.
+* Requires long addressing format to be allowed.
+*
+* Before sending data, the data is converted to little endian. The
+* data received back is converted back to the target platform's endianness.
+*
+* WARNING: This function is only guaranteed to work if there is one
+* master on the I2C bus.
+*
+* Output:
+* - 0 if reading was successful
+* in that case: read back data is at *rdata
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_read_modify_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 waddr,
+ u32 raddr,
+ u16 wdata, u16 *rdata)
+{
+ int rc = -EIO;
+
+#if (DRXDAPFASI_LONG_ADDR_ALLOWED == 1)
+ if (rdata == NULL)
+ return -EINVAL;
+
+ rc = drxdap_fasi_write_reg16(dev_addr, waddr, wdata, DRXDAP_FASI_RMW);
+ if (rc == 0)
+ rc = drxdap_fasi_read_reg16(dev_addr, raddr, rdata, 0);
+#endif
+
+ return rc;
+}
+
+/******************************
+*
+* int drxdap_fasi_write_reg32 (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u32 data, -- data to send
+* u32 flags) -- special device flags
+*
+* Write one 32-bit register or memory location. The data being written is
+* converted from the target platform's endianness to little endian.
+*
+* Output:
+* - 0 if writing was successful
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_write_reg32(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u32 data, u32 flags)
+{
+ u8 buf[sizeof(data)];
+
+ buf[0] = (u8) ((data >> 0) & 0xFF);
+ buf[1] = (u8) ((data >> 8) & 0xFF);
+ buf[2] = (u8) ((data >> 16) & 0xFF);
+ buf[3] = (u8) ((data >> 24) & 0xFF);
+
+ return drxdap_fasi_write_block(dev_addr, addr, sizeof(data), buf, flags);
+}
+
+/*============================================================================*/
+
+/**
+* \fn int drxj_dap_rm_write_reg16short
+* \brief Read modify write 16 bits audio register using short format only.
+* \param dev_addr
+* \param waddr Address to write to
+* \param raddr Address to read from (usually SIO_HI_RA_RAM_S0_RMWBUF__A)
+* \param wdata Data to write
+* \param rdata Buffer for data to read
+* \return int
+* \retval 0 Succes
+* \retval -EIO Timeout, I2C error, illegal bank
+*
+* 16 bits register read modify write access using short addressing format only.
+* Requires knowledge of the registermap, thus device dependent.
+* Using DAP FASI directly to avoid endless recursion of RMWs to audio registers.
+*
+*/
+
+/* TODO correct define should be #if ( DRXDAPFASI_SHORT_ADDR_ALLOWED==1 )
+ See comments drxj_dap_read_modify_write_reg16 */
+#if (DRXDAPFASI_LONG_ADDR_ALLOWED == 0)
+static int drxj_dap_rm_write_reg16short(struct i2c_device_addr *dev_addr,
+ u32 waddr,
+ u32 raddr,
+ u16 wdata, u16 *rdata)
+{
+ int rc;
+
+ if (rdata == NULL)
+ return -EINVAL;
+
+ /* Set RMW flag */
+ rc = drxdap_fasi_write_reg16(dev_addr,
+ SIO_HI_RA_RAM_S0_FLG_ACC__A,
+ SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__M,
+ 0x0000);
+ if (rc == 0) {
+ /* Write new data: triggers RMW */
+ rc = drxdap_fasi_write_reg16(dev_addr, waddr, wdata,
+ 0x0000);
+ }
+ if (rc == 0) {
+ /* Read old data */
+ rc = drxdap_fasi_read_reg16(dev_addr, raddr, rdata,
+ 0x0000);
+ }
+ if (rc == 0) {
+ /* Reset RMW flag */
+ rc = drxdap_fasi_write_reg16(dev_addr,
+ SIO_HI_RA_RAM_S0_FLG_ACC__A,
+ 0, 0x0000);
+ }
+
+ return rc;
+}
+#endif
+
+/*============================================================================*/
+
+static int drxj_dap_read_modify_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 waddr,
+ u32 raddr,
+ u16 wdata, u16 *rdata)
+{
+ /* TODO: correct short/long addressing format decision,
+ now long format has higher prio then short because short also
+ needs virt bnks (not impl yet) for certain audio registers */
+#if (DRXDAPFASI_LONG_ADDR_ALLOWED == 1)
+ return drxdap_fasi_read_modify_write_reg16(dev_addr,
+ waddr,
+ raddr, wdata, rdata);
+#else
+ return drxj_dap_rm_write_reg16short(dev_addr, waddr, raddr, wdata, rdata);
+#endif
+}
+
+
+/*============================================================================*/
+
+/**
+* \fn int drxj_dap_read_aud_reg16
+* \brief Read 16 bits audio register
+* \param dev_addr
+* \param addr
+* \param data
+* \return int
+* \retval 0 Succes
+* \retval -EIO Timeout, I2C error, illegal bank
+*
+* 16 bits register read access via audio token ring interface.
+*
+*/
+static int drxj_dap_read_aud_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr, u16 *data)
+{
+ u32 start_timer = 0;
+ u32 current_timer = 0;
+ u32 delta_timer = 0;
+ u16 tr_status = 0;
+ int stat = -EIO;
+
+ /* No read possible for bank 3, return with error */
+ if (DRXDAP_FASI_ADDR2BANK(addr) == 3) {
+ stat = -EINVAL;
+ } else {
+ const u32 write_bit = ((dr_xaddr_t) 1) << 16;
+
+ /* Force reset write bit */
+ addr &= (~write_bit);
+
+ /* Set up read */
+ start_timer = jiffies_to_msecs(jiffies);
+ do {
+ /* RMW to aud TR IF until request is granted or timeout */
+ stat = drxj_dap_read_modify_write_reg16(dev_addr,
+ addr,
+ SIO_HI_RA_RAM_S0_RMWBUF__A,
+ 0x0000, &tr_status);
+
+ if (stat != 0)
+ break;
+
+ current_timer = jiffies_to_msecs(jiffies);
+ delta_timer = current_timer - start_timer;
+ if (delta_timer > DRXJ_DAP_AUDTRIF_TIMEOUT) {
+ stat = -EIO;
+ break;
+ }
+
+ } while (((tr_status & AUD_TOP_TR_CTR_FIFO_LOCK__M) ==
+ AUD_TOP_TR_CTR_FIFO_LOCK_LOCKED) ||
+ ((tr_status & AUD_TOP_TR_CTR_FIFO_FULL__M) ==
+ AUD_TOP_TR_CTR_FIFO_FULL_FULL));
+ } /* if ( DRXDAP_FASI_ADDR2BANK(addr)!=3 ) */
+
+ /* Wait for read ready status or timeout */
+ if (stat == 0) {
+ start_timer = jiffies_to_msecs(jiffies);
+
+ while ((tr_status & AUD_TOP_TR_CTR_FIFO_RD_RDY__M) !=
+ AUD_TOP_TR_CTR_FIFO_RD_RDY_READY) {
+ stat = drxj_dap_read_reg16(dev_addr,
+ AUD_TOP_TR_CTR__A,
+ &tr_status, 0x0000);
+ if (stat != 0)
+ break;
+
+ current_timer = jiffies_to_msecs(jiffies);
+ delta_timer = current_timer - start_timer;
+ if (delta_timer > DRXJ_DAP_AUDTRIF_TIMEOUT) {
+ stat = -EIO;
+ break;
+ }
+ } /* while ( ... ) */
+ }
+
+ /* Read value */
+ if (stat == 0)
+ stat = drxj_dap_read_modify_write_reg16(dev_addr,
+ AUD_TOP_TR_RD_REG__A,
+ SIO_HI_RA_RAM_S0_RMWBUF__A,
+ 0x0000, data);
+ return stat;
+}
+
+/*============================================================================*/
+
+static int drxj_dap_read_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 *data, u32 flags)
+{
+ int stat = -EIO;
+
+ /* Check param */
+ if ((dev_addr == NULL) || (data == NULL))
+ return -EINVAL;
+
+ if (is_handled_by_aud_tr_if(addr))
+ stat = drxj_dap_read_aud_reg16(dev_addr, addr, data);
+ else
+ stat = drxdap_fasi_read_reg16(dev_addr, addr, data, flags);
+
+ return stat;
+}
+/*============================================================================*/
+
+/**
+* \fn int drxj_dap_write_aud_reg16
+* \brief Write 16 bits audio register
+* \param dev_addr
+* \param addr
+* \param data
+* \return int
+* \retval 0 Succes
+* \retval -EIO Timeout, I2C error, illegal bank
+*
+* 16 bits register write access via audio token ring interface.
+*
+*/
+static int drxj_dap_write_aud_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr, u16 data)
+{
+ int stat = -EIO;
+
+ /* No write possible for bank 2, return with error */
+ if (DRXDAP_FASI_ADDR2BANK(addr) == 2) {
+ stat = -EINVAL;
+ } else {
+ u32 start_timer = 0;
+ u32 current_timer = 0;
+ u32 delta_timer = 0;
+ u16 tr_status = 0;
+ const u32 write_bit = ((dr_xaddr_t) 1) << 16;
+
+ /* Force write bit */
+ addr |= write_bit;
+ start_timer = jiffies_to_msecs(jiffies);
+ do {
+ /* RMW to aud TR IF until request is granted or timeout */
+ stat = drxj_dap_read_modify_write_reg16(dev_addr,
+ addr,
+ SIO_HI_RA_RAM_S0_RMWBUF__A,
+ data, &tr_status);
+ if (stat != 0)
+ break;
+
+ current_timer = jiffies_to_msecs(jiffies);
+ delta_timer = current_timer - start_timer;
+ if (delta_timer > DRXJ_DAP_AUDTRIF_TIMEOUT) {
+ stat = -EIO;
+ break;
+ }
+
+ } while (((tr_status & AUD_TOP_TR_CTR_FIFO_LOCK__M) ==
+ AUD_TOP_TR_CTR_FIFO_LOCK_LOCKED) ||
+ ((tr_status & AUD_TOP_TR_CTR_FIFO_FULL__M) ==
+ AUD_TOP_TR_CTR_FIFO_FULL_FULL));
+
+ } /* if ( DRXDAP_FASI_ADDR2BANK(addr)!=2 ) */
+
+ return stat;
+}
+
+/*============================================================================*/
+
+static int drxj_dap_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 data, u32 flags)
+{
+ int stat = -EIO;
+
+ /* Check param */
+ if (dev_addr == NULL)
+ return -EINVAL;
+
+ if (is_handled_by_aud_tr_if(addr))
+ stat = drxj_dap_write_aud_reg16(dev_addr, addr, data);
+ else
+ stat = drxdap_fasi_write_reg16(dev_addr,
+ addr, data, flags);
+
+ return stat;
+}
+
+/*============================================================================*/
+
+/* Free data ram in SIO HI */
+#define SIO_HI_RA_RAM_USR_BEGIN__A 0x420040
+#define SIO_HI_RA_RAM_USR_END__A 0x420060
+
+#define DRXJ_HI_ATOMIC_BUF_START (SIO_HI_RA_RAM_USR_BEGIN__A)
+#define DRXJ_HI_ATOMIC_BUF_END (SIO_HI_RA_RAM_USR_BEGIN__A + 7)
+#define DRXJ_HI_ATOMIC_READ SIO_HI_RA_RAM_PAR_3_ACP_RW_READ
+#define DRXJ_HI_ATOMIC_WRITE SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE
+
+/**
+* \fn int drxj_dap_atomic_read_write_block()
+* \brief Basic access routine for atomic read or write access
+* \param dev_addr pointer to i2c dev address
+* \param addr destination/source address
+* \param datasize size of data buffer in bytes
+* \param data pointer to data buffer
+* \return int
+* \retval 0 Succes
+* \retval -EIO Timeout, I2C error, illegal bank
+*
+*/
+static
+int drxj_dap_atomic_read_write_block(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 datasize,
+ u8 *data, bool read_flag)
+{
+ struct drxj_hi_cmd hi_cmd;
+ int rc;
+ u16 word;
+ u16 dummy = 0;
+ u16 i = 0;
+
+ /* Parameter check */
+ if (!data || !dev_addr || ((datasize % 2)) || ((datasize / 2) > 8))
+ return -EINVAL;
+
+ /* Set up HI parameters to read or write n bytes */
+ hi_cmd.cmd = SIO_HI_RA_RAM_CMD_ATOMIC_COPY;
+ hi_cmd.param1 =
+ (u16) ((DRXDAP_FASI_ADDR2BLOCK(DRXJ_HI_ATOMIC_BUF_START) << 6) +
+ DRXDAP_FASI_ADDR2BANK(DRXJ_HI_ATOMIC_BUF_START));
+ hi_cmd.param2 =
+ (u16) DRXDAP_FASI_ADDR2OFFSET(DRXJ_HI_ATOMIC_BUF_START);
+ hi_cmd.param3 = (u16) ((datasize / 2) - 1);
+ if (!read_flag)
+ hi_cmd.param3 |= DRXJ_HI_ATOMIC_WRITE;
+ else
+ hi_cmd.param3 |= DRXJ_HI_ATOMIC_READ;
+ hi_cmd.param4 = (u16) ((DRXDAP_FASI_ADDR2BLOCK(addr) << 6) +
+ DRXDAP_FASI_ADDR2BANK(addr));
+ hi_cmd.param5 = (u16) DRXDAP_FASI_ADDR2OFFSET(addr);
+
+ if (!read_flag) {
+ /* write data to buffer */
+ for (i = 0; i < (datasize / 2); i++) {
+
+ word = ((u16) data[2 * i]);
+ word += (((u16) data[(2 * i) + 1]) << 8);
+ drxj_dap_write_reg16(dev_addr,
+ (DRXJ_HI_ATOMIC_BUF_START + i),
+ word, 0);
+ }
+ }
+
+ rc = hi_command(dev_addr, &hi_cmd, &dummy);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (read_flag) {
+ /* read data from buffer */
+ for (i = 0; i < (datasize / 2); i++) {
+ drxj_dap_read_reg16(dev_addr,
+ (DRXJ_HI_ATOMIC_BUF_START + i),
+ &word, 0);
+ data[2 * i] = (u8) (word & 0xFF);
+ data[(2 * i) + 1] = (u8) (word >> 8);
+ }
+ }
+
+ return 0;
+
+rw_error:
+ return -EIO;
+
+}
+
+/*============================================================================*/
+
+/**
+* \fn int drxj_dap_atomic_read_reg32()
+* \brief Atomic read of 32 bits words
+*/
+static
+int drxj_dap_atomic_read_reg32(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u32 *data, u32 flags)
+{
+ u8 buf[sizeof(*data)];
+ int rc = -EIO;
+ u32 word = 0;
+
+ if (!data)
+ return -EINVAL;
+
+ rc = drxj_dap_atomic_read_write_block(dev_addr, addr,
+ sizeof(*data), buf, true);
+
+ if (rc < 0)
+ return 0;
+
+ word = (u32) buf[3];
+ word <<= 8;
+ word |= (u32) buf[2];
+ word <<= 8;
+ word |= (u32) buf[1];
+ word <<= 8;
+ word |= (u32) buf[0];
+
+ *data = word;
+
+ return rc;
+}
+
+/*============================================================================*/
+
+/*============================================================================*/
+/*== END DRXJ DAP FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== HOST INTERFACE FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/**
+* \fn int hi_cfg_command()
+* \brief Configure HI with settings stored in the demod structure.
+* \param demod Demodulator.
+* \return int.
+*
+* This routine was created because to much orthogonal settings have
+* been put into one HI API function (configure). Especially the I2C bridge
+* enable/disable should not need re-configuration of the HI.
+*
+*/
+static int hi_cfg_command(const struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct drxj_hi_cmd hi_cmd;
+ u16 result = 0;
+ int rc;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ hi_cmd.cmd = SIO_HI_RA_RAM_CMD_CONFIG;
+ hi_cmd.param1 = SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY;
+ hi_cmd.param2 = ext_attr->hi_cfg_timing_div;
+ hi_cmd.param3 = ext_attr->hi_cfg_bridge_delay;
+ hi_cmd.param4 = ext_attr->hi_cfg_wake_up_key;
+ hi_cmd.param5 = ext_attr->hi_cfg_ctrl;
+ hi_cmd.param6 = ext_attr->hi_cfg_transmit;
+
+ rc = hi_command(demod->my_i2c_dev_addr, &hi_cmd, &result);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Reset power down flag (set one call only) */
+ ext_attr->hi_cfg_ctrl &= (~(SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ));
+
+ return 0;
+
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int hi_command()
+* \brief Configure HI with settings stored in the demod structure.
+* \param dev_addr I2C address.
+* \param cmd HI command.
+* \param result HI command result.
+* \return int.
+*
+* Sends command to HI
+*
+*/
+static int
+hi_command(struct i2c_device_addr *dev_addr, const struct drxj_hi_cmd *cmd, u16 *result)
+{
+ u16 wait_cmd = 0;
+ u16 nr_retries = 0;
+ bool powerdown_cmd = false;
+ int rc;
+
+ /* Write parameters */
+ switch (cmd->cmd) {
+
+ case SIO_HI_RA_RAM_CMD_CONFIG:
+ case SIO_HI_RA_RAM_CMD_ATOMIC_COPY:
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_6__A, cmd->param6, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_5__A, cmd->param5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_4__A, cmd->param4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_3__A, cmd->param3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* fallthrough */
+ case SIO_HI_RA_RAM_CMD_BRDCTRL:
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_2__A, cmd->param2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_1__A, cmd->param1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* fallthrough */
+ case SIO_HI_RA_RAM_CMD_NULL:
+ /* No parameters */
+ break;
+
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ /* Write command */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_CMD__A, cmd->cmd, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if ((cmd->cmd) == SIO_HI_RA_RAM_CMD_RESET)
+ msleep(1);
+
+ /* Detect power down to ommit reading result */
+ powerdown_cmd = (bool) ((cmd->cmd == SIO_HI_RA_RAM_CMD_CONFIG) &&
+ (((cmd->
+ param5) & SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M)
+ == SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ));
+ if (!powerdown_cmd) {
+ /* Wait until command rdy */
+ do {
+ nr_retries++;
+ if (nr_retries > DRXJ_MAX_RETRIES) {
+ pr_err("timeout\n");
+ goto rw_error;
+ }
+
+ rc = drxj_dap_read_reg16(dev_addr, SIO_HI_RA_RAM_CMD__A, &wait_cmd, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } while (wait_cmd != 0);
+
+ /* Read result */
+ rc = drxj_dap_read_reg16(dev_addr, SIO_HI_RA_RAM_RES__A, result, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ }
+ /* if ( powerdown_cmd == true ) */
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int init_hi( const struct drx_demod_instance *demod )
+* \brief Initialise and configurate HI.
+* \param demod pointer to demod data.
+* \return int Return status.
+* \retval 0 Success.
+* \retval -EIO Failure.
+*
+* Needs to know Psys (System Clock period) and Posc (Osc Clock period)
+* Need to store configuration in driver because of the way I2C
+* bridging is controlled.
+*
+*/
+static int init_hi(const struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL);
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ int rc;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+
+ /* PATCH for bug 5003, HI ucode v3.1.0 */
+ rc = drxj_dap_write_reg16(dev_addr, 0x4301D7, 0x801, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Timing div, 250ns/Psys */
+ /* Timing div, = ( delay (nano seconds) * sysclk (kHz) )/ 1000 */
+ ext_attr->hi_cfg_timing_div =
+ (u16) ((common_attr->sys_clock_freq / 1000) * HI_I2C_DELAY) / 1000;
+ /* Clipping */
+ if ((ext_attr->hi_cfg_timing_div) > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M)
+ ext_attr->hi_cfg_timing_div = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M;
+ /* Bridge delay, uses oscilator clock */
+ /* Delay = ( delay (nano seconds) * oscclk (kHz) )/ 1000 */
+ /* SDA brdige delay */
+ ext_attr->hi_cfg_bridge_delay =
+ (u16) ((common_attr->osc_clock_freq / 1000) * HI_I2C_BRIDGE_DELAY) /
+ 1000;
+ /* Clipping */
+ if ((ext_attr->hi_cfg_bridge_delay) > SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M)
+ ext_attr->hi_cfg_bridge_delay = SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M;
+ /* SCL bridge delay, same as SDA for now */
+ ext_attr->hi_cfg_bridge_delay += ((ext_attr->hi_cfg_bridge_delay) <<
+ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B);
+ /* Wakeup key, setting the read flag (as suggest in the documentation) does
+ not always result into a working solution (barebones worked VI2C failed).
+ Not setting the bit works in all cases . */
+ ext_attr->hi_cfg_wake_up_key = DRXJ_WAKE_UP_KEY;
+ /* port/bridge/power down ctrl */
+ ext_attr->hi_cfg_ctrl = (SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE);
+ /* transit mode time out delay and watch dog divider */
+ ext_attr->hi_cfg_transmit = SIO_HI_RA_RAM_PAR_6__PRE;
+
+ rc = hi_cfg_command(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+/*== END HOST INTERFACE FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== AUXILIARY FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/**
+* \fn int get_device_capabilities()
+* \brief Get and store device capabilities.
+* \param demod Pointer to demodulator instance.
+* \return int.
+* \return 0 Success
+* \retval -EIO Failure
+*
+* Depending on pulldowns on MDx pins the following internals are set:
+* * common_attr->osc_clock_freq
+* * ext_attr->has_lna
+* * ext_attr->has_ntsc
+* * ext_attr->has_btsc
+* * ext_attr->has_oob
+*
+*/
+static int get_device_capabilities(struct drx_demod_instance *demod)
+{
+ struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL);
+ struct drxj_data *ext_attr = (struct drxj_data *) NULL;
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ u16 sio_pdr_ohw_cfg = 0;
+ u32 sio_top_jtagid_lo = 0;
+ u16 bid = 0;
+ int rc;
+
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_read_reg16(dev_addr, SIO_PDR_OHW_CFG__A, &sio_pdr_ohw_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ switch ((sio_pdr_ohw_cfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) {
+ case 0:
+ /* ignore (bypass ?) */
+ break;
+ case 1:
+ /* 27 MHz */
+ common_attr->osc_clock_freq = 27000;
+ break;
+ case 2:
+ /* 20.25 MHz */
+ common_attr->osc_clock_freq = 20250;
+ break;
+ case 3:
+ /* 4 MHz */
+ common_attr->osc_clock_freq = 4000;
+ break;
+ default:
+ return -EIO;
+ }
+
+ /*
+ Determine device capabilities
+ Based on pinning v47
+ */
+ rc = drxdap_fasi_read_reg32(dev_addr, SIO_TOP_JTAGID_LO__A, &sio_top_jtagid_lo, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->mfx = (u8) ((sio_top_jtagid_lo >> 29) & 0xF);
+
+ switch ((sio_top_jtagid_lo >> 12) & 0xFF) {
+ case 0x31:
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_read_reg16(dev_addr, SIO_PDR_UIO_IN_HI__A, &bid, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ bid = (bid >> 10) & 0xf;
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ ext_attr->has_lna = true;
+ ext_attr->has_ntsc = false;
+ ext_attr->has_btsc = false;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = false;
+ ext_attr->has_gpio = false;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x33:
+ ext_attr->has_lna = false;
+ ext_attr->has_ntsc = false;
+ ext_attr->has_btsc = false;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = false;
+ ext_attr->has_gpio = false;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x45:
+ ext_attr->has_lna = true;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = false;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x46:
+ ext_attr->has_lna = false;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = false;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x41:
+ ext_attr->has_lna = true;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = true;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x43:
+ ext_attr->has_lna = false;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = true;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x32:
+ ext_attr->has_lna = true;
+ ext_attr->has_ntsc = false;
+ ext_attr->has_btsc = false;
+ ext_attr->has_oob = true;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = true;
+ break;
+ case 0x34:
+ ext_attr->has_lna = false;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = true;
+ ext_attr->has_oob = true;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = true;
+ break;
+ case 0x42:
+ ext_attr->has_lna = true;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = true;
+ ext_attr->has_oob = true;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = true;
+ break;
+ case 0x44:
+ ext_attr->has_lna = false;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = true;
+ ext_attr->has_oob = true;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = true;
+ break;
+ default:
+ /* Unknown device variant */
+ return -EIO;
+ break;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int power_up_device()
+* \brief Power up device.
+* \param demod Pointer to demodulator instance.
+* \return int.
+* \return 0 Success
+* \retval -EIO Failure, I2C or max retries reached
+*
+*/
+
+#ifndef DRXJ_MAX_RETRIES_POWERUP
+#define DRXJ_MAX_RETRIES_POWERUP 10
+#endif
+
+static int power_up_device(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ u8 data = 0;
+ u16 retry_count = 0;
+ struct i2c_device_addr wake_up_addr;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ wake_up_addr.i2c_addr = DRXJ_WAKE_UP_KEY;
+ wake_up_addr.i2c_dev_id = dev_addr->i2c_dev_id;
+ wake_up_addr.user_data = dev_addr->user_data;
+ /*
+ * I2C access may fail in this case: no ack
+ * dummy write must be used to wake uop device, dummy read must be used to
+ * reset HI state machine (avoiding actual writes)
+ */
+ do {
+ data = 0;
+ drxbsp_i2c_write_read(&wake_up_addr, 1, &data,
+ (struct i2c_device_addr *)(NULL), 0,
+ (u8 *)(NULL));
+ msleep(10);
+ retry_count++;
+ } while ((drxbsp_i2c_write_read
+ ((struct i2c_device_addr *) (NULL), 0, (u8 *)(NULL), dev_addr, 1,
+ &data)
+ != 0) && (retry_count < DRXJ_MAX_RETRIES_POWERUP));
+
+ /* Need some recovery time .... */
+ msleep(10);
+
+ if (retry_count == DRXJ_MAX_RETRIES_POWERUP)
+ return -EIO;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/* MPEG Output Configuration Functions - begin */
+/*----------------------------------------------------------------------------*/
+/**
+* \fn int ctrl_set_cfg_mpeg_output()
+* \brief Set MPEG output configuration of the device.
+* \param devmod Pointer to demodulator instance.
+* \param cfg_data Pointer to mpeg output configuaration.
+* \return int.
+*
+* Configure MPEG output parameters.
+*
+*/
+static int
+ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_output *cfg_data)
+{
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL);
+ int rc;
+ u16 fec_oc_reg_mode = 0;
+ u16 fec_oc_reg_ipr_mode = 0;
+ u16 fec_oc_reg_ipr_invert = 0;
+ u32 max_bit_rate = 0;
+ u32 rcn_rate = 0;
+ u32 nr_bits = 0;
+ u16 sio_pdr_md_cfg = 0;
+ /* data mask for the output data byte */
+ u16 invert_data_mask =
+ FEC_OC_IPR_INVERT_MD7__M | FEC_OC_IPR_INVERT_MD6__M |
+ FEC_OC_IPR_INVERT_MD5__M | FEC_OC_IPR_INVERT_MD4__M |
+ FEC_OC_IPR_INVERT_MD3__M | FEC_OC_IPR_INVERT_MD2__M |
+ FEC_OC_IPR_INVERT_MD1__M | FEC_OC_IPR_INVERT_MD0__M;
+
+ /* check arguments */
+ if ((demod == NULL) || (cfg_data == NULL))
+ return -EINVAL;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+
+ if (cfg_data->enable_mpeg_output == true) {
+ /* quick and dirty patch to set MPEG incase current std is not
+ producing MPEG */
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ break;
+ default:
+ return 0;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_OCR_INVERT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_USAGE__A, 7, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* 2048 bytes fifo ram */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_CTL_UPD_RATE__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_INT_UPD_RATE__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_A__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_B__A, 7, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_RCN_GAIN__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Low Water Mark for synchronization */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_LWM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* High Water Mark for synchronization */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_HWM__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ switch (ext_attr->constellation) {
+ case DRX_CONSTELLATION_QAM256:
+ nr_bits = 8;
+ break;
+ case DRX_CONSTELLATION_QAM128:
+ nr_bits = 7;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ nr_bits = 6;
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ nr_bits = 5;
+ break;
+ case DRX_CONSTELLATION_QAM16:
+ nr_bits = 4;
+ break;
+ default:
+ return -EIO;
+ } /* ext_attr->constellation */
+ /* max_bit_rate = symbol_rate * nr_bits * coef */
+ /* coef = 188/204 */
+ max_bit_rate =
+ (ext_attr->curr_symbol_rate / 8) * nr_bits * 188;
+ /* pass through b/c Annex A/c need following settings */
+ case DRX_STANDARD_ITU_B:
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_USAGE__A, FEC_OC_FCT_USAGE__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_CTL_UPD_RATE__A, FEC_OC_TMD_CTL_UPD_RATE__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_INT_UPD_RATE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_A__A, FEC_OC_AVR_PARM_A__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_B__A, FEC_OC_AVR_PARM_B__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (cfg_data->static_clk == true) {
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_RCN_GAIN__A, 0xD, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_RCN_GAIN__A, FEC_OC_RCN_GAIN__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_LWM__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_HWM__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ break;
+ } /* swtich (standard) */
+
+ /* Check insertion of the Reed-Solomon parity bytes */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_MODE__A, &fec_oc_reg_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_IPR_MODE__A, &fec_oc_reg_ipr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (cfg_data->insert_rs_byte == true) {
+ /* enable parity symbol forward */
+ fec_oc_reg_mode |= FEC_OC_MODE_PARITY__M;
+ /* MVAL disable during parity bytes */
+ fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M;
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ rcn_rate = 0x004854D3;
+ break;
+ case DRX_STANDARD_ITU_B:
+ fec_oc_reg_mode |= FEC_OC_MODE_TRANSPARENT__M;
+ switch (ext_attr->constellation) {
+ case DRX_CONSTELLATION_QAM256:
+ rcn_rate = 0x008945E7;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ rcn_rate = 0x005F64D4;
+ break;
+ default:
+ return -EIO;
+ }
+ break;
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ /* insert_rs_byte = true -> coef = 188/188 -> 1, RS bits are in MPEG output */
+ rcn_rate =
+ (frac28
+ (max_bit_rate,
+ (u32) (common_attr->sys_clock_freq / 8))) /
+ 188;
+ break;
+ default:
+ return -EIO;
+ } /* ext_attr->standard */
+ } else { /* insert_rs_byte == false */
+
+ /* disable parity symbol forward */
+ fec_oc_reg_mode &= (~FEC_OC_MODE_PARITY__M);
+ /* MVAL enable during parity bytes */
+ fec_oc_reg_ipr_mode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M);
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ rcn_rate = 0x0041605C;
+ break;
+ case DRX_STANDARD_ITU_B:
+ fec_oc_reg_mode &= (~FEC_OC_MODE_TRANSPARENT__M);
+ switch (ext_attr->constellation) {
+ case DRX_CONSTELLATION_QAM256:
+ rcn_rate = 0x0082D6A0;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ rcn_rate = 0x005AEC1A;
+ break;
+ default:
+ return -EIO;
+ }
+ break;
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ /* insert_rs_byte = false -> coef = 188/204, RS bits not in MPEG output */
+ rcn_rate =
+ (frac28
+ (max_bit_rate,
+ (u32) (common_attr->sys_clock_freq / 8))) /
+ 204;
+ break;
+ default:
+ return -EIO;
+ } /* ext_attr->standard */
+ }
+
+ if (cfg_data->enable_parallel == true) { /* MPEG data output is paralel -> clear ipr_mode[0] */
+ fec_oc_reg_ipr_mode &= (~(FEC_OC_IPR_MODE_SERIAL__M));
+ } else { /* MPEG data output is serial -> set ipr_mode[0] */
+ fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_SERIAL__M;
+ }
+
+ /* Control slective inversion of output bits */
+ if (cfg_data->invert_data == true)
+ fec_oc_reg_ipr_invert |= invert_data_mask;
+ else
+ fec_oc_reg_ipr_invert &= (~(invert_data_mask));
+
+ if (cfg_data->invert_err == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MERR__M;
+ else
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MERR__M));
+
+ if (cfg_data->invert_str == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MSTRT__M;
+ else
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MSTRT__M));
+
+ if (cfg_data->invert_val == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MVAL__M;
+ else
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MVAL__M));
+
+ if (cfg_data->invert_clk == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MCLK__M;
+ else
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MCLK__M));
+
+
+ if (cfg_data->static_clk == true) { /* Static mode */
+ u32 dto_rate = 0;
+ u32 bit_rate = 0;
+ u16 fec_oc_dto_burst_len = 0;
+ u16 fec_oc_dto_period = 0;
+
+ fec_oc_dto_burst_len = FEC_OC_DTO_BURST_LEN__PRE;
+
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ fec_oc_dto_period = 4;
+ if (cfg_data->insert_rs_byte == true)
+ fec_oc_dto_burst_len = 208;
+ break;
+ case DRX_STANDARD_ITU_A:
+ {
+ u32 symbol_rate_th = 6400000;
+ if (cfg_data->insert_rs_byte == true) {
+ fec_oc_dto_burst_len = 204;
+ symbol_rate_th = 5900000;
+ }
+ if (ext_attr->curr_symbol_rate >=
+ symbol_rate_th) {
+ fec_oc_dto_period = 0;
+ } else {
+ fec_oc_dto_period = 1;
+ }
+ }
+ break;
+ case DRX_STANDARD_ITU_B:
+ fec_oc_dto_period = 1;
+ if (cfg_data->insert_rs_byte == true)
+ fec_oc_dto_burst_len = 128;
+ break;
+ case DRX_STANDARD_ITU_C:
+ fec_oc_dto_period = 1;
+ if (cfg_data->insert_rs_byte == true)
+ fec_oc_dto_burst_len = 204;
+ break;
+ default:
+ return -EIO;
+ }
+ bit_rate =
+ common_attr->sys_clock_freq * 1000 / (fec_oc_dto_period +
+ 2);
+ dto_rate =
+ frac28(bit_rate, common_attr->sys_clock_freq * 1000);
+ dto_rate >>= 3;
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_RATE_HI__A, (u16)((dto_rate >> 16) & FEC_OC_DTO_RATE_HI__M), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_RATE_LO__A, (u16)(dto_rate & FEC_OC_DTO_RATE_LO_RATE_LO__M), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_MODE__A, FEC_OC_DTO_MODE_DYNAMIC__M | FEC_OC_DTO_MODE_OFFSET_ENABLE__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_MODE__A, FEC_OC_FCT_MODE_RAT_ENA__M | FEC_OC_FCT_MODE_VIRT_ENA__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_BURST_LEN__A, fec_oc_dto_burst_len, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (ext_attr->mpeg_output_clock_rate != DRXJ_MPEGOUTPUT_CLOCK_RATE_AUTO)
+ fec_oc_dto_period = ext_attr->mpeg_output_clock_rate - 1;
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_PERIOD__A, fec_oc_dto_period, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else { /* Dynamic mode */
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_MODE__A, FEC_OC_DTO_MODE_DYNAMIC__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_MODE__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ rc = drxdap_fasi_write_reg32(dev_addr, FEC_OC_RCN_CTL_RATE_LO__A, rcn_rate, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Write appropriate registers with requested configuration */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_MODE__A, fec_oc_reg_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_IPR_MODE__A, fec_oc_reg_ipr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_IPR_INVERT__A, fec_oc_reg_ipr_invert, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* enabling for both parallel and serial now */
+ /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0xFABA, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Set MPEG TS pads to outputmode */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MSTRT_CFG__A, 0x0013, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MERR_CFG__A, 0x0013, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MCLK_CFG__A, MPEG_OUTPUT_CLK_DRIVE_STRENGTH << SIO_PDR_MCLK_CFG_DRIVE__B | 0x03 << SIO_PDR_MCLK_CFG_MODE__B, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MVAL_CFG__A, 0x0013, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ sio_pdr_md_cfg =
+ MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH <<
+ SIO_PDR_MD0_CFG_DRIVE__B | 0x03 << SIO_PDR_MD0_CFG_MODE__B;
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD0_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (cfg_data->enable_parallel == true) { /* MPEG data output is paralel -> set MD1 to MD7 to output mode */
+ sio_pdr_md_cfg =
+ MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH <<
+ SIO_PDR_MD0_CFG_DRIVE__B | 0x03 <<
+ SIO_PDR_MD0_CFG_MODE__B;
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD0_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD1_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD2_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD3_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD4_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD5_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD6_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD7_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else { /* MPEG data output is serial -> set MD1 to MD7 to tri-state */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD1_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD2_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD3_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD4_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD5_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD6_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD7_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ /* Enable Monitor Bus output over MPEG pads and ctl input */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MON_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Write nomagic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0xFABA, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Set MPEG TS pads to inputmode */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MSTRT_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MERR_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MCLK_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MVAL_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD0_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD1_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD2_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD3_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD4_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD5_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD6_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD7_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Enable Monitor Bus output over MPEG pads and ctl input */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MON_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Write nomagic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* save values for restore after re-acquire */
+ common_attr->mpeg_cfg.enable_mpeg_output = cfg_data->enable_mpeg_output;
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*----------------------------------------------------------------------------*/
+
+
+/*----------------------------------------------------------------------------*/
+/* MPEG Output Configuration Functions - end */
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/* miscellaneous configuartions - begin */
+/*----------------------------------------------------------------------------*/
+
+/**
+* \fn int set_mpegtei_handling()
+* \brief Activate MPEG TEI handling settings.
+* \param devmod Pointer to demodulator instance.
+* \return int.
+*
+* This routine should be called during a set channel of QAM/VSB
+*
+*/
+static int set_mpegtei_handling(struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ int rc;
+ u16 fec_oc_dpr_mode = 0;
+ u16 fec_oc_snc_mode = 0;
+ u16 fec_oc_ems_mode = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_DPR_MODE__A, &fec_oc_dpr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_EMS_MODE__A, &fec_oc_ems_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* reset to default, allow TEI bit to be changed */
+ fec_oc_dpr_mode &= (~FEC_OC_DPR_MODE_ERR_DISABLE__M);
+ fec_oc_snc_mode &= (~(FEC_OC_SNC_MODE_ERROR_CTL__M |
+ FEC_OC_SNC_MODE_CORR_DISABLE__M));
+ fec_oc_ems_mode &= (~FEC_OC_EMS_MODE_MODE__M);
+
+ if (ext_attr->disable_te_ihandling) {
+ /* do not change TEI bit */
+ fec_oc_dpr_mode |= FEC_OC_DPR_MODE_ERR_DISABLE__M;
+ fec_oc_snc_mode |= FEC_OC_SNC_MODE_CORR_DISABLE__M |
+ ((0x2) << (FEC_OC_SNC_MODE_ERROR_CTL__B));
+ fec_oc_ems_mode |= ((0x01) << (FEC_OC_EMS_MODE_MODE__B));
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DPR_MODE__A, fec_oc_dpr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_MODE__A, fec_oc_snc_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_EMS_MODE__A, fec_oc_ems_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+* \fn int bit_reverse_mpeg_output()
+* \brief Set MPEG output bit-endian settings.
+* \param devmod Pointer to demodulator instance.
+* \return int.
+*
+* This routine should be called during a set channel of QAM/VSB
+*
+*/
+static int bit_reverse_mpeg_output(struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ int rc;
+ u16 fec_oc_ipr_mode = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_IPR_MODE__A, &fec_oc_ipr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* reset to default (normal bit order) */
+ fec_oc_ipr_mode &= (~FEC_OC_IPR_MODE_REVERSE_ORDER__M);
+
+ if (ext_attr->bit_reverse_mpeg_outout)
+ fec_oc_ipr_mode |= FEC_OC_IPR_MODE_REVERSE_ORDER__M;
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_IPR_MODE__A, fec_oc_ipr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+* \fn int set_mpeg_start_width()
+* \brief Set MPEG start width.
+* \param devmod Pointer to demodulator instance.
+* \return int.
+*
+* This routine should be called during a set channel of QAM/VSB
+*
+*/
+static int set_mpeg_start_width(struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ struct drx_common_attr *common_attr = (struct drx_common_attr *) NULL;
+ int rc;
+ u16 fec_oc_comm_mb = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ common_attr = demod->my_common_attr;
+
+ if ((common_attr->mpeg_cfg.static_clk == true)
+ && (common_attr->mpeg_cfg.enable_parallel == false)) {
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_COMM_MB__A, &fec_oc_comm_mb, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fec_oc_comm_mb &= ~FEC_OC_COMM_MB_CTL_ON;
+ if (ext_attr->mpeg_start_width == DRXJ_MPEG_START_WIDTH_8CLKCYC)
+ fec_oc_comm_mb |= FEC_OC_COMM_MB_CTL_ON;
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_COMM_MB__A, fec_oc_comm_mb, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*----------------------------------------------------------------------------*/
+/* miscellaneous configuartions - end */
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/* UIO Configuration Functions - begin */
+/*----------------------------------------------------------------------------*/
+/**
+* \fn int ctrl_set_uio_cfg()
+* \brief Configure modus oprandi UIO.
+* \param demod Pointer to demodulator instance.
+* \param uio_cfg Pointer to a configuration setting for a certain UIO.
+* \return int.
+*/
+static int ctrl_set_uio_cfg(struct drx_demod_instance *demod, struct drxuio_cfg *uio_cfg)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ int rc;
+
+ if ((uio_cfg == NULL) || (demod == NULL))
+ return -EINVAL;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ switch (uio_cfg->uio) {
+ /*====================================================================*/
+ case DRX_UIO1:
+ /* DRX_UIO1: SMA_TX UIO-1 */
+ if (!ext_attr->has_smatx)
+ return -EIO;
+ switch (uio_cfg->mode) {
+ case DRX_UIO_MODE_FIRMWARE_SMA: /* falltrough */
+ case DRX_UIO_MODE_FIRMWARE_SAW: /* falltrough */
+ case DRX_UIO_MODE_READWRITE:
+ ext_attr->uio_sma_tx_mode = uio_cfg->mode;
+ break;
+ case DRX_UIO_MODE_DISABLE:
+ ext_attr->uio_sma_tx_mode = uio_cfg->mode;
+ /* pad configuration register is set 0 - input mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_CFG__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EINVAL;
+ } /* switch ( uio_cfg->mode ) */
+ break;
+ /*====================================================================*/
+ case DRX_UIO2:
+ /* DRX_UIO2: SMA_RX UIO-2 */
+ if (!ext_attr->has_smarx)
+ return -EIO;
+ switch (uio_cfg->mode) {
+ case DRX_UIO_MODE_FIRMWARE0: /* falltrough */
+ case DRX_UIO_MODE_READWRITE:
+ ext_attr->uio_sma_rx_mode = uio_cfg->mode;
+ break;
+ case DRX_UIO_MODE_DISABLE:
+ ext_attr->uio_sma_rx_mode = uio_cfg->mode;
+ /* pad configuration register is set 0 - input mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_RX_CFG__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EINVAL;
+ break;
+ } /* switch ( uio_cfg->mode ) */
+ break;
+ /*====================================================================*/
+ case DRX_UIO3:
+ /* DRX_UIO3: GPIO UIO-3 */
+ if (!ext_attr->has_gpio)
+ return -EIO;
+ switch (uio_cfg->mode) {
+ case DRX_UIO_MODE_FIRMWARE0: /* falltrough */
+ case DRX_UIO_MODE_READWRITE:
+ ext_attr->uio_gpio_mode = uio_cfg->mode;
+ break;
+ case DRX_UIO_MODE_DISABLE:
+ ext_attr->uio_gpio_mode = uio_cfg->mode;
+ /* pad configuration register is set 0 - input mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_GPIO_CFG__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EINVAL;
+ break;
+ } /* switch ( uio_cfg->mode ) */
+ break;
+ /*====================================================================*/
+ case DRX_UIO4:
+ /* DRX_UIO4: IRQN UIO-4 */
+ if (!ext_attr->has_irqn)
+ return -EIO;
+ switch (uio_cfg->mode) {
+ case DRX_UIO_MODE_READWRITE:
+ ext_attr->uio_irqn_mode = uio_cfg->mode;
+ break;
+ case DRX_UIO_MODE_DISABLE:
+ /* pad configuration register is set 0 - input mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_IRQN_CFG__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->uio_irqn_mode = uio_cfg->mode;
+ break;
+ case DRX_UIO_MODE_FIRMWARE0: /* falltrough */
+ default:
+ return -EINVAL;
+ break;
+ } /* switch ( uio_cfg->mode ) */
+ break;
+ /*====================================================================*/
+ default:
+ return -EINVAL;
+ } /* switch ( uio_cfg->uio ) */
+
+ /* Write magic word to disable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int ctrl_uio_write()
+* \brief Write to a UIO.
+* \param demod Pointer to demodulator instance.
+* \param uio_data Pointer to data container for a certain UIO.
+* \return int.
+*/
+static int
+ctrl_uio_write(struct drx_demod_instance *demod, struct drxuio_data *uio_data)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ int rc;
+ u16 pin_cfg_value = 0;
+ u16 value = 0;
+
+ if ((uio_data == NULL) || (demod == NULL))
+ return -EINVAL;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ switch (uio_data->uio) {
+ /*====================================================================*/
+ case DRX_UIO1:
+ /* DRX_UIO1: SMA_TX UIO-1 */
+ if (!ext_attr->has_smatx)
+ return -EIO;
+ if ((ext_attr->uio_sma_tx_mode != DRX_UIO_MODE_READWRITE)
+ && (ext_attr->uio_sma_tx_mode != DRX_UIO_MODE_FIRMWARE_SAW)) {
+ return -EIO;
+ }
+ pin_cfg_value = 0;
+ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+ pin_cfg_value |= 0x0113;
+ /* io_pad_cfg_mode output mode is drive always */
+ /* io_pad_cfg_drive is set to power 2 (23 mA) */
+
+ /* write to io pad configuration register - output mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_CFG__A, pin_cfg_value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* use corresponding bit in io data output registar */
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, &value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (!uio_data->value)
+ value &= 0x7FFF; /* write zero to 15th bit - 1st UIO */
+ else
+ value |= 0x8000; /* write one to 15th bit - 1st UIO */
+
+ /* write back to io data output register */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ /*======================================================================*/
+ case DRX_UIO2:
+ /* DRX_UIO2: SMA_RX UIO-2 */
+ if (!ext_attr->has_smarx)
+ return -EIO;
+ if (ext_attr->uio_sma_rx_mode != DRX_UIO_MODE_READWRITE)
+ return -EIO;
+
+ pin_cfg_value = 0;
+ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+ pin_cfg_value |= 0x0113;
+ /* io_pad_cfg_mode output mode is drive always */
+ /* io_pad_cfg_drive is set to power 2 (23 mA) */
+
+ /* write to io pad configuration register - output mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_RX_CFG__A, pin_cfg_value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* use corresponding bit in io data output registar */
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, &value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (!uio_data->value)
+ value &= 0xBFFF; /* write zero to 14th bit - 2nd UIO */
+ else
+ value |= 0x4000; /* write one to 14th bit - 2nd UIO */
+
+ /* write back to io data output register */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ /*====================================================================*/
+ case DRX_UIO3:
+ /* DRX_UIO3: ASEL UIO-3 */
+ if (!ext_attr->has_gpio)
+ return -EIO;
+ if (ext_attr->uio_gpio_mode != DRX_UIO_MODE_READWRITE)
+ return -EIO;
+
+ pin_cfg_value = 0;
+ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+ pin_cfg_value |= 0x0113;
+ /* io_pad_cfg_mode output mode is drive always */
+ /* io_pad_cfg_drive is set to power 2 (23 mA) */
+
+ /* write to io pad configuration register - output mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_GPIO_CFG__A, pin_cfg_value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* use corresponding bit in io data output registar */
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_HI__A, &value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (!uio_data->value)
+ value &= 0xFFFB; /* write zero to 2nd bit - 3rd UIO */
+ else
+ value |= 0x0004; /* write one to 2nd bit - 3rd UIO */
+
+ /* write back to io data output register */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_HI__A, value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ /*=====================================================================*/
+ case DRX_UIO4:
+ /* DRX_UIO4: IRQN UIO-4 */
+ if (!ext_attr->has_irqn)
+ return -EIO;
+
+ if (ext_attr->uio_irqn_mode != DRX_UIO_MODE_READWRITE)
+ return -EIO;
+
+ pin_cfg_value = 0;
+ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+ pin_cfg_value |= 0x0113;
+ /* io_pad_cfg_mode output mode is drive always */
+ /* io_pad_cfg_drive is set to power 2 (23 mA) */
+
+ /* write to io pad configuration register - output mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_IRQN_CFG__A, pin_cfg_value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* use corresponding bit in io data output registar */
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, &value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (uio_data->value == false)
+ value &= 0xEFFF; /* write zero to 12th bit - 4th UIO */
+ else
+ value |= 0x1000; /* write one to 12th bit - 4th UIO */
+
+ /* write back to io data output register */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ /*=====================================================================*/
+ default:
+ return -EINVAL;
+ } /* switch ( uio_data->uio ) */
+
+ /* Write magic word to disable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*---------------------------------------------------------------------------*/
+/* UIO Configuration Functions - end */
+/*---------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/* I2C Bridge Functions - begin */
+/*----------------------------------------------------------------------------*/
+/**
+* \fn int ctrl_i2c_bridge()
+* \brief Open or close the I2C switch to tuner.
+* \param demod Pointer to demodulator instance.
+* \param bridge_closed Pointer to bool indication if bridge is closed not.
+* \return int.
+
+*/
+static int
+ctrl_i2c_bridge(struct drx_demod_instance *demod, bool *bridge_closed)
+{
+ struct drxj_hi_cmd hi_cmd;
+ u16 result = 0;
+
+ /* check arguments */
+ if (bridge_closed == NULL)
+ return -EINVAL;
+
+ hi_cmd.cmd = SIO_HI_RA_RAM_CMD_BRDCTRL;
+ hi_cmd.param1 = SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY;
+ if (*bridge_closed)
+ hi_cmd.param2 = SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED;
+ else
+ hi_cmd.param2 = SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN;
+
+ return hi_command(demod->my_i2c_dev_addr, &hi_cmd, &result);
+}
+
+/*----------------------------------------------------------------------------*/
+/* I2C Bridge Functions - end */
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/* Smart antenna Functions - begin */
+/*----------------------------------------------------------------------------*/
+/**
+* \fn int smart_ant_init()
+* \brief Initialize Smart Antenna.
+* \param pointer to struct drx_demod_instance.
+* \return int.
+*
+*/
+static int smart_ant_init(struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = NULL;
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxuio_cfg uio_cfg = { DRX_UIO1, DRX_UIO_MODE_FIRMWARE_SMA };
+ int rc;
+ u16 data = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* init smart antenna */
+ rc = drxj_dap_read_reg16(dev_addr, SIO_SA_TX_COMMAND__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (ext_attr->smart_ant_inverted) {
+ rc = drxj_dap_write_reg16(dev_addr, SIO_SA_TX_COMMAND__A, (data | SIO_SA_TX_COMMAND_TX_INVERT__M) | SIO_SA_TX_COMMAND_TX_ENABLE__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = drxj_dap_write_reg16(dev_addr, SIO_SA_TX_COMMAND__A, (data & (~SIO_SA_TX_COMMAND_TX_INVERT__M)) | SIO_SA_TX_COMMAND_TX_ENABLE__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* config SMA_TX pin to smart antenna mode */
+ rc = ctrl_set_uio_cfg(demod, &uio_cfg);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_CFG__A, 0x13, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_GPIO_FNC__A, 0x03, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Write magic word to disable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+static int scu_command(struct i2c_device_addr *dev_addr, struct drxjscu_cmd *cmd)
+{
+ int rc;
+ u16 cur_cmd = 0;
+ unsigned long timeout;
+
+ /* Check param */
+ if (cmd == NULL)
+ return -EINVAL;
+
+ /* Wait until SCU command interface is ready to receive command */
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_COMMAND__A, &cur_cmd, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (cur_cmd != DRX_SCU_READY)
+ return -EIO;
+
+ switch (cmd->parameter_len) {
+ case 5:
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_4__A, *(cmd->parameter + 4), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* fallthrough */
+ case 4:
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_3__A, *(cmd->parameter + 3), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* fallthrough */
+ case 3:
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_2__A, *(cmd->parameter + 2), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* fallthrough */
+ case 2:
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_1__A, *(cmd->parameter + 1), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* fallthrough */
+ case 1:
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_0__A, *(cmd->parameter + 0), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* fallthrough */
+ case 0:
+ /* do nothing */
+ break;
+ default:
+ /* this number of parameters is not supported */
+ return -EIO;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_COMMAND__A, cmd->command, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Wait until SCU has processed command */
+ timeout = jiffies + msecs_to_jiffies(DRXJ_MAX_WAITTIME);
+ while (time_is_after_jiffies(timeout)) {
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_COMMAND__A, &cur_cmd, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (cur_cmd == DRX_SCU_READY)
+ break;
+ usleep_range(1000, 2000);
+ }
+
+ if (cur_cmd != DRX_SCU_READY)
+ return -EIO;
+
+ /* read results */
+ if ((cmd->result_len > 0) && (cmd->result != NULL)) {
+ s16 err;
+
+ switch (cmd->result_len) {
+ case 4:
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_3__A, cmd->result + 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* fallthrough */
+ case 3:
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_2__A, cmd->result + 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* fallthrough */
+ case 2:
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_1__A, cmd->result + 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* fallthrough */
+ case 1:
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_0__A, cmd->result + 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* fallthrough */
+ case 0:
+ /* do nothing */
+ break;
+ default:
+ /* this number of parameters is not supported */
+ return -EIO;
+ }
+
+ /* Check if an error was reported by SCU */
+ err = cmd->result[0];
+
+ /* check a few fixed error codes */
+ if ((err == (s16) SCU_RAM_PARAM_0_RESULT_UNKSTD)
+ || (err == (s16) SCU_RAM_PARAM_0_RESULT_UNKCMD)
+ || (err == (s16) SCU_RAM_PARAM_0_RESULT_INVPAR)
+ || (err == (s16) SCU_RAM_PARAM_0_RESULT_SIZE)
+ ) {
+ return -EINVAL;
+ }
+ /* here it is assumed that negative means error, and positive no error */
+ else if (err < 0)
+ return -EIO;
+ else
+ return 0;
+ }
+
+ return 0;
+
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int DRXJ_DAP_SCUAtomicReadWriteBlock()
+* \brief Basic access routine for SCU atomic read or write access
+* \param dev_addr pointer to i2c dev address
+* \param addr destination/source address
+* \param datasize size of data buffer in bytes
+* \param data pointer to data buffer
+* \return int
+* \retval 0 Succes
+* \retval -EIO Timeout, I2C error, illegal bank
+*
+*/
+#define ADDR_AT_SCU_SPACE(x) ((x - 0x82E000) * 2)
+static
+int drxj_dap_scu_atomic_read_write_block(struct i2c_device_addr *dev_addr, u32 addr, u16 datasize, /* max 30 bytes because the limit of SCU parameter */
+ u8 *data, bool read_flag)
+{
+ struct drxjscu_cmd scu_cmd;
+ int rc;
+ u16 set_param_parameters[15];
+ u16 cmd_result[15];
+
+ /* Parameter check */
+ if (!data || !dev_addr || (datasize % 2) || ((datasize / 2) > 16))
+ return -EINVAL;
+
+ set_param_parameters[1] = (u16) ADDR_AT_SCU_SPACE(addr);
+ if (read_flag) { /* read */
+ set_param_parameters[0] = ((~(0x0080)) & datasize);
+ scu_cmd.parameter_len = 2;
+ scu_cmd.result_len = datasize / 2 + 2;
+ } else {
+ int i = 0;
+
+ set_param_parameters[0] = 0x0080 | datasize;
+ for (i = 0; i < (datasize / 2); i++) {
+ set_param_parameters[i + 2] =
+ (data[2 * i] | (data[(2 * i) + 1] << 8));
+ }
+ scu_cmd.parameter_len = datasize / 2 + 2;
+ scu_cmd.result_len = 1;
+ }
+
+ scu_cmd.command =
+ SCU_RAM_COMMAND_STANDARD_TOP |
+ SCU_RAM_COMMAND_CMD_AUX_SCU_ATOMIC_ACCESS;
+ scu_cmd.result = cmd_result;
+ scu_cmd.parameter = set_param_parameters;
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (read_flag) {
+ int i = 0;
+ /* read data from buffer */
+ for (i = 0; i < (datasize / 2); i++) {
+ data[2 * i] = (u8) (scu_cmd.result[i + 2] & 0xFF);
+ data[(2 * i) + 1] = (u8) (scu_cmd.result[i + 2] >> 8);
+ }
+ }
+
+ return 0;
+
+rw_error:
+ return -EIO;
+
+}
+
+/*============================================================================*/
+
+/**
+* \fn int DRXJ_DAP_AtomicReadReg16()
+* \brief Atomic read of 16 bits words
+*/
+static
+int drxj_dap_scu_atomic_read_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 *data, u32 flags)
+{
+ u8 buf[2];
+ int rc = -EIO;
+ u16 word = 0;
+
+ if (!data)
+ return -EINVAL;
+
+ rc = drxj_dap_scu_atomic_read_write_block(dev_addr, addr, 2, buf, true);
+ if (rc < 0)
+ return rc;
+
+ word = (u16) (buf[0] + (buf[1] << 8));
+
+ *data = word;
+
+ return rc;
+}
+
+/*============================================================================*/
+/**
+* \fn int drxj_dap_scu_atomic_write_reg16()
+* \brief Atomic read of 16 bits words
+*/
+static
+int drxj_dap_scu_atomic_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 data, u32 flags)
+{
+ u8 buf[2];
+ int rc = -EIO;
+
+ buf[0] = (u8) (data & 0xff);
+ buf[1] = (u8) ((data >> 8) & 0xff);
+
+ rc = drxj_dap_scu_atomic_read_write_block(dev_addr, addr, 2, buf, false);
+
+ return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+/**
+* \brief Measure result of ADC synchronisation
+* \param demod demod instance
+* \param count (returned) count
+* \return int.
+* \retval 0 Success
+* \retval -EIO Failure: I2C error
+*
+*/
+static int adc_sync_measurement(struct drx_demod_instance *demod, u16 *count)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ int rc;
+ u16 data = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+
+ /* Start measurement */
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_START_LOCK__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Wait at least 3*128*(1/sysclk) <<< 1 millisec */
+ msleep(1);
+
+ *count = 0;
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_PHASE0__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (data == 127)
+ *count = *count + 1;
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_PHASE1__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (data == 127)
+ *count = *count + 1;
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_PHASE2__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (data == 127)
+ *count = *count + 1;
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \brief Synchronize analog and digital clock domains
+* \param demod demod instance
+* \return int.
+* \retval 0 Success
+* \retval -EIO Failure: I2C error or failure to synchronize
+*
+* An IQM reset will also reset the results of this synchronization.
+* After an IQM reset this routine needs to be called again.
+*
+*/
+
+static int adc_synchronization(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ int rc;
+ u16 count = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+
+ rc = adc_sync_measurement(demod, &count);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (count == 1) {
+ /* Try sampling on a diffrent edge */
+ u16 clk_neg = 0;
+
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_CLKNEG__A, &clk_neg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ clk_neg ^= IQM_AF_CLKNEG_CLKNEGDATA__M;
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLKNEG__A, clk_neg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = adc_sync_measurement(demod, &count);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* TODO: implement fallback scenarios */
+ if (count < 2)
+ return -EIO;
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+/*== END AUXILIARY FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== 8VSB & QAM COMMON DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+/**
+* \fn int init_agc ()
+* \brief Initialize AGC for all standards.
+* \param demod instance of demodulator.
+* \param channel pointer to channel data.
+* \return int.
+*/
+static int init_agc(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drx_common_attr *common_attr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ struct drxj_cfg_agc *p_agc_rf_settings = NULL;
+ struct drxj_cfg_agc *p_agc_if_settings = NULL;
+ int rc;
+ u16 ingain_tgt_max = 0;
+ u16 clp_dir_to = 0;
+ u16 sns_sum_max = 0;
+ u16 clp_sum_max = 0;
+ u16 sns_dir_to = 0;
+ u16 ki_innergain_min = 0;
+ u16 agc_ki = 0;
+ u16 ki_max = 0;
+ u16 if_iaccu_hi_tgt_min = 0;
+ u16 data = 0;
+ u16 agc_ki_dgain = 0;
+ u16 ki_min = 0;
+ u16 clp_ctrl_mode = 0;
+ u16 agc_rf = 0;
+ u16 agc_if = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ clp_sum_max = 1023;
+ clp_dir_to = (u16) (-9);
+ sns_sum_max = 1023;
+ sns_dir_to = (u16) (-9);
+ ki_innergain_min = (u16) (-32768);
+ ki_max = 0x032C;
+ agc_ki_dgain = 0xC;
+ if_iaccu_hi_tgt_min = 2047;
+ ki_min = 0x0117;
+ ingain_tgt_max = 16383;
+ clp_ctrl_mode = 0;
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CYCCNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_WD__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_STP__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_CYCCNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_WD__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_STP__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN__A, 1024, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_VSB_AGC_POW_TGT__A, 22600, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT__A, 13200, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ p_agc_if_settings = &(ext_attr->vsb_if_agc_cfg);
+ p_agc_rf_settings = &(ext_attr->vsb_rf_agc_cfg);
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ case DRX_STANDARD_ITU_B:
+ ingain_tgt_max = 5119;
+ clp_sum_max = 1023;
+ clp_dir_to = (u16) (-5);
+ sns_sum_max = 127;
+ sns_dir_to = (u16) (-3);
+ ki_innergain_min = 0;
+ ki_max = 0x0657;
+ if_iaccu_hi_tgt_min = 2047;
+ agc_ki_dgain = 0x7;
+ ki_min = 0x0117;
+ clp_ctrl_mode = 0;
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CYCCNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_WD__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_STP__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_CYCCNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_WD__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_STP__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ p_agc_if_settings = &(ext_attr->qam_if_agc_cfg);
+ p_agc_rf_settings = &(ext_attr->qam_rf_agc_cfg);
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT__A, p_agc_if_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_AGC_KI__A, &agc_ki, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ agc_ki &= 0xf000;
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI__A, agc_ki, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ /* for new AGC interface */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT_MIN__A, p_agc_if_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN__A, p_agc_if_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* Gain fed from inner to outer AGC */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingain_tgt_max, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A, if_iaccu_hi_tgt_min, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_IF_IACCU_HI__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* set to p_agc_settings->top before */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_IF_IACCU_LO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_RF_IACCU_HI__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_RF_IACCU_LO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_RF_MAX__A, 32767, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM_MAX__A, clp_sum_max, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM_MAX__A, sns_sum_max, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_INNERGAIN_MIN__A, ki_innergain_min, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_CYCLEN__A, 500, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_CYCLEN__A, 500, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MIN__A, ki_min, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAX__A, ki_max, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_RED__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM_MIN__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CYCLEN__A, 500, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_TO__A, clp_dir_to, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM_MIN__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_TO__A, sns_dir_to, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CTRL_MODE__A, clp_ctrl_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ agc_rf = 0x800 + p_agc_rf_settings->cut_off_current;
+ if (common_attr->tuner_rf_agc_pol == true)
+ agc_rf = 0x87ff - agc_rf;
+
+ agc_if = 0x800;
+ if (common_attr->tuner_if_agc_pol == true)
+ agc_rf = 0x87ff - agc_rf;
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AGC_RF__A, agc_rf, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AGC_IF__A, agc_if, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Set/restore Ki DGAIN factor */
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_DGAIN__M;
+ data |= (agc_ki_dgain << SCU_RAM_AGC_KI_DGAIN__B);
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int set_frequency ()
+* \brief Set frequency shift.
+* \param demod instance of demodulator.
+* \param channel pointer to channel data.
+* \param tuner_freq_offset residual frequency from tuner.
+* \return int.
+*/
+static int
+set_frequency(struct drx_demod_instance *demod,
+ struct drx_channel *channel, s32 tuner_freq_offset)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ int rc;
+ s32 sampling_frequency = 0;
+ s32 frequency_shift = 0;
+ s32 if_freq_actual = 0;
+ s32 rf_freq_residual = -1 * tuner_freq_offset;
+ s32 adc_freq = 0;
+ s32 intermediate_freq = 0;
+ u32 iqm_fs_rate_ofs = 0;
+ bool adc_flip = true;
+ bool select_pos_image = false;
+ bool rf_mirror;
+ bool tuner_mirror;
+ bool image_to_select = true;
+ s32 fm_frequency_shift = 0;
+
+ rf_mirror = (ext_attr->mirror == DRX_MIRROR_YES) ? true : false;
+ tuner_mirror = demod->my_common_attr->mirror_freq_spect ? false : true;
+ /*
+ Program frequency shifter
+ No need to account for mirroring on RF
+ */
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_ITU_A: /* fallthrough */
+ case DRX_STANDARD_ITU_C: /* fallthrough */
+ case DRX_STANDARD_PAL_SECAM_LP: /* fallthrough */
+ case DRX_STANDARD_8VSB:
+ select_pos_image = true;
+ break;
+ case DRX_STANDARD_FM:
+ /* After IQM FS sound carrier must appear at 4 Mhz in spect.
+ Sound carrier is already 3Mhz above centre frequency due
+ to tuner setting so now add an extra shift of 1MHz... */
+ fm_frequency_shift = 1000;
+ case DRX_STANDARD_ITU_B: /* fallthrough */
+ case DRX_STANDARD_NTSC: /* fallthrough */
+ case DRX_STANDARD_PAL_SECAM_BG: /* fallthrough */
+ case DRX_STANDARD_PAL_SECAM_DK: /* fallthrough */
+ case DRX_STANDARD_PAL_SECAM_I: /* fallthrough */
+ case DRX_STANDARD_PAL_SECAM_L:
+ select_pos_image = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ intermediate_freq = demod->my_common_attr->intermediate_freq;
+ sampling_frequency = demod->my_common_attr->sys_clock_freq / 3;
+ if (tuner_mirror)
+ if_freq_actual = intermediate_freq + rf_freq_residual + fm_frequency_shift;
+ else
+ if_freq_actual = intermediate_freq - rf_freq_residual - fm_frequency_shift;
+ if (if_freq_actual > sampling_frequency / 2) {
+ /* adc mirrors */
+ adc_freq = sampling_frequency - if_freq_actual;
+ adc_flip = true;
+ } else {
+ /* adc doesn't mirror */
+ adc_freq = if_freq_actual;
+ adc_flip = false;
+ }
+
+ frequency_shift = adc_freq;
+ image_to_select =
+ (bool) (rf_mirror ^ tuner_mirror ^ adc_flip ^ select_pos_image);
+ iqm_fs_rate_ofs = frac28(frequency_shift, sampling_frequency);
+
+ if (image_to_select)
+ iqm_fs_rate_ofs = ~iqm_fs_rate_ofs + 1;
+
+ /* Program frequency shifter with tuner offset compensation */
+ /* frequency_shift += tuner_freq_offset; TODO */
+ rc = drxdap_fasi_write_reg32(dev_addr, IQM_FS_RATE_OFS_LO__A, iqm_fs_rate_ofs, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->iqm_fs_rate_ofs = iqm_fs_rate_ofs;
+ ext_attr->pos_image = (bool) (rf_mirror ^ tuner_mirror ^ select_pos_image);
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int get_acc_pkt_err()
+* \brief Retrieve signal strength for VSB and QAM.
+* \param demod Pointer to demod instance
+* \param packet_err Pointer to packet error
+* \return int.
+* \retval 0 sig_strength contains valid data.
+* \retval -EINVAL sig_strength is NULL.
+* \retval -EIO Erroneous data, sig_strength contains invalid data.
+*/
+#ifdef DRXJ_SIGNAL_ACCUM_ERR
+static int get_acc_pkt_err(struct drx_demod_instance *demod, u16 *packet_err)
+{
+ int rc;
+ static u16 pkt_err;
+ static u16 last_pkt_err;
+ u16 data = 0;
+ struct drxj_data *ext_attr = NULL;
+ struct i2c_device_addr *dev_addr = NULL;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (ext_attr->reset_pkt_err_acc) {
+ last_pkt_err = data;
+ pkt_err = 0;
+ ext_attr->reset_pkt_err_acc = false;
+ }
+
+ if (data < last_pkt_err) {
+ pkt_err += 0xffff - last_pkt_err;
+ pkt_err += data;
+ } else {
+ pkt_err += (data - last_pkt_err);
+ }
+ *packet_err = pkt_err;
+ last_pkt_err = data;
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+#endif
+
+
+/*============================================================================*/
+
+/**
+* \fn int set_agc_rf ()
+* \brief Configure RF AGC
+* \param demod instance of demodulator.
+* \param agc_settings AGC configuration structure
+* \return int.
+*/
+static int
+set_agc_rf(struct drx_demod_instance *demod, struct drxj_cfg_agc *agc_settings, bool atomic)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ struct drxj_cfg_agc *p_agc_settings = NULL;
+ struct drx_common_attr *common_attr = NULL;
+ int rc;
+ drx_write_reg16func_t scu_wr16 = NULL;
+ drx_read_reg16func_t scu_rr16 = NULL;
+
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ if (atomic) {
+ scu_rr16 = drxj_dap_scu_atomic_read_reg16;
+ scu_wr16 = drxj_dap_scu_atomic_write_reg16;
+ } else {
+ scu_rr16 = drxj_dap_read_reg16;
+ scu_wr16 = drxj_dap_write_reg16;
+ }
+
+ /* Configure AGC only if standard is currently active */
+ if ((ext_attr->standard == agc_settings->standard) ||
+ (DRXJ_ISQAMSTD(ext_attr->standard) &&
+ DRXJ_ISQAMSTD(agc_settings->standard)) ||
+ (DRXJ_ISATVSTD(ext_attr->standard) &&
+ DRXJ_ISATVSTD(agc_settings->standard))) {
+ u16 data = 0;
+
+ switch (agc_settings->ctrl_mode) {
+ case DRX_AGC_CTRL_AUTO:
+
+ /* Enable RF AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data |= IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE;
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Enable SCU RF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_RF__M;
+ if (ext_attr->standard == DRX_STANDARD_8VSB)
+ data |= (2 << SCU_RAM_AGC_KI_RF__B);
+ else if (DRXJ_ISQAMSTD(ext_attr->standard))
+ data |= (5 << SCU_RAM_AGC_KI_RF__B);
+ else
+ data |= (4 << SCU_RAM_AGC_KI_RF__B);
+
+ if (common_attr->tuner_rf_agc_pol)
+ data |= SCU_RAM_AGC_KI_INV_RF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_KI_INV_RF_POL__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Set speed ( using complementary reduction value ) */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI_RED__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI_RED__A, (~(agc_settings->speed << SCU_RAM_AGC_KI_RED_RAGC_RED__B) & SCU_RAM_AGC_KI_RED_RAGC_RED__M) | data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (agc_settings->standard == DRX_STANDARD_8VSB)
+ p_agc_settings = &(ext_attr->vsb_if_agc_cfg);
+ else if (DRXJ_ISQAMSTD(agc_settings->standard))
+ p_agc_settings = &(ext_attr->qam_if_agc_cfg);
+ else if (DRXJ_ISATVSTD(agc_settings->standard))
+ p_agc_settings = &(ext_attr->atv_if_agc_cfg);
+ else
+ return -EINVAL;
+
+ /* Set TOP, only if IF-AGC is in AUTO mode */
+ if (p_agc_settings->ctrl_mode == DRX_AGC_CTRL_AUTO) {
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, agc_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, agc_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* Cut-Off current */
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_RF_IACCU_HI_CO__A, agc_settings->cut_off_current, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_AGC_CTRL_USER:
+
+ /* Enable RF AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data |= IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE;
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Disable SCU RF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_RF__M;
+ if (common_attr->tuner_rf_agc_pol)
+ data |= SCU_RAM_AGC_KI_INV_RF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_KI_INV_RF_POL__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Write value to output pin */
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_RF_IACCU_HI__A, agc_settings->output_level, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_AGC_CTRL_OFF:
+
+ /* Disable RF AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= (~IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE);
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Disable SCU RF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_RF__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EINVAL;
+ } /* switch ( agcsettings->ctrl_mode ) */
+ }
+
+ /* Store rf agc settings */
+ switch (agc_settings->standard) {
+ case DRX_STANDARD_8VSB:
+ ext_attr->vsb_rf_agc_cfg = *agc_settings;
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ ext_attr->qam_rf_agc_cfg = *agc_settings;
+ break;
+#endif
+ default:
+ return -EIO;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int set_agc_if ()
+* \brief Configure If AGC
+* \param demod instance of demodulator.
+* \param agc_settings AGC configuration structure
+* \return int.
+*/
+static int
+set_agc_if(struct drx_demod_instance *demod, struct drxj_cfg_agc *agc_settings, bool atomic)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ struct drxj_cfg_agc *p_agc_settings = NULL;
+ struct drx_common_attr *common_attr = NULL;
+ drx_write_reg16func_t scu_wr16 = NULL;
+ drx_read_reg16func_t scu_rr16 = NULL;
+ int rc;
+
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ if (atomic) {
+ scu_rr16 = drxj_dap_scu_atomic_read_reg16;
+ scu_wr16 = drxj_dap_scu_atomic_write_reg16;
+ } else {
+ scu_rr16 = drxj_dap_read_reg16;
+ scu_wr16 = drxj_dap_write_reg16;
+ }
+
+ /* Configure AGC only if standard is currently active */
+ if ((ext_attr->standard == agc_settings->standard) ||
+ (DRXJ_ISQAMSTD(ext_attr->standard) &&
+ DRXJ_ISQAMSTD(agc_settings->standard)) ||
+ (DRXJ_ISATVSTD(ext_attr->standard) &&
+ DRXJ_ISATVSTD(agc_settings->standard))) {
+ u16 data = 0;
+
+ switch (agc_settings->ctrl_mode) {
+ case DRX_AGC_CTRL_AUTO:
+ /* Enable IF AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data |= IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE;
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Enable SCU IF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_IF_AGC_DISABLE__M;
+ data &= ~SCU_RAM_AGC_KI_IF__M;
+ if (ext_attr->standard == DRX_STANDARD_8VSB)
+ data |= (3 << SCU_RAM_AGC_KI_IF__B);
+ else if (DRXJ_ISQAMSTD(ext_attr->standard))
+ data |= (6 << SCU_RAM_AGC_KI_IF__B);
+ else
+ data |= (5 << SCU_RAM_AGC_KI_IF__B);
+
+ if (common_attr->tuner_if_agc_pol)
+ data |= SCU_RAM_AGC_KI_INV_IF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_KI_INV_IF_POL__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Set speed (using complementary reduction value) */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI_RED__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M;
+ rc = (*scu_wr16) (dev_addr, SCU_RAM_AGC_KI_RED__A, (~(agc_settings->speed << SCU_RAM_AGC_KI_RED_IAGC_RED__B) & SCU_RAM_AGC_KI_RED_IAGC_RED__M) | data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (agc_settings->standard == DRX_STANDARD_8VSB)
+ p_agc_settings = &(ext_attr->vsb_rf_agc_cfg);
+ else if (DRXJ_ISQAMSTD(agc_settings->standard))
+ p_agc_settings = &(ext_attr->qam_rf_agc_cfg);
+ else if (DRXJ_ISATVSTD(agc_settings->standard))
+ p_agc_settings = &(ext_attr->atv_rf_agc_cfg);
+ else
+ return -EINVAL;
+
+ /* Restore TOP */
+ if (p_agc_settings->ctrl_mode == DRX_AGC_CTRL_AUTO) {
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, p_agc_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, p_agc_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ break;
+
+ case DRX_AGC_CTRL_USER:
+
+ /* Enable IF AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data |= IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE;
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Disable SCU IF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_IF_AGC_DISABLE__M;
+ data |= SCU_RAM_AGC_KI_IF_AGC_DISABLE__M;
+ if (common_attr->tuner_if_agc_pol)
+ data |= SCU_RAM_AGC_KI_INV_IF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_KI_INV_IF_POL__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Write value to output pin */
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, agc_settings->output_level, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+
+ case DRX_AGC_CTRL_OFF:
+
+ /* Disable If AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= (~IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE);
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Disable SCU IF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_IF_AGC_DISABLE__M;
+ data |= SCU_RAM_AGC_KI_IF_AGC_DISABLE__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EINVAL;
+ } /* switch ( agcsettings->ctrl_mode ) */
+
+ /* always set the top to support configurations without if-loop */
+ rc = (*scu_wr16) (dev_addr, SCU_RAM_AGC_INGAIN_TGT_MIN__A, agc_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* Store if agc settings */
+ switch (agc_settings->standard) {
+ case DRX_STANDARD_8VSB:
+ ext_attr->vsb_if_agc_cfg = *agc_settings;
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ ext_attr->qam_if_agc_cfg = *agc_settings;
+ break;
+#endif
+ default:
+ return -EIO;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int set_iqm_af ()
+* \brief Configure IQM AF registers
+* \param demod instance of demodulator.
+* \param active
+* \return int.
+*/
+static int set_iqm_af(struct drx_demod_instance *demod, bool active)
+{
+ u16 data = 0;
+ struct i2c_device_addr *dev_addr = NULL;
+ int rc;
+
+ dev_addr = demod->my_i2c_dev_addr;
+
+ /* Configure IQM */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (!active)
+ data &= ((~IQM_AF_STDBY_STDBY_ADC_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_AMP_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_PD_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE));
+ else
+ data |= (IQM_AF_STDBY_STDBY_ADC_A2_ACTIVE | IQM_AF_STDBY_STDBY_AMP_A2_ACTIVE | IQM_AF_STDBY_STDBY_PD_A2_ACTIVE | IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE | IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE);
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+/*== END 8VSB & QAM COMMON DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== 8VSB DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/**
+* \fn int power_down_vsb ()
+* \brief Powr down QAM related blocks.
+* \param demod instance of demodulator.
+* \param channel pointer to channel data.
+* \return int.
+*/
+static int power_down_vsb(struct drx_demod_instance *demod, bool primary)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxjscu_cmd cmd_scu = { /* command */ 0,
+ /* parameter_len */ 0,
+ /* result_len */ 0,
+ /* *parameter */ NULL,
+ /* *result */ NULL
+ };
+ struct drx_cfg_mpeg_output cfg_mpeg_output;
+ int rc;
+ u16 cmd_result = 0;
+
+ /*
+ STOP demodulator
+ reset of FEC and VSB HW
+ */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB |
+ SCU_RAM_COMMAND_CMD_DEMOD_STOP;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* stop all comm_exec */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_COMM_EXEC__A, VSB_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (primary) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_iqm_af(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ cfg_mpeg_output.enable_mpeg_output = false;
+ rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int set_vsb_leak_n_gain ()
+* \brief Set ATSC demod.
+* \param demod instance of demodulator.
+* \return int.
+*/
+static int set_vsb_leak_n_gain(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ int rc;
+
+ const u8 vsb_ffe_leak_gain_ram0[] = {
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO1 */
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO2 */
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO3 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO4 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO5 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO6 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO7 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO8 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO9 */
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO10 */
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO11 */
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO12 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO1 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO2 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO3 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO4 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO5 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO6 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO7 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO8 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO9 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO10 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO11 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO12 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO1 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO2 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO3 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO4 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO5 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO6 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO7 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO8 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO9 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO10 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO11 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO12 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO1 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO2 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO3 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO4 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO5 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO6 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO7 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO8 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO9 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO10 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO11 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO12 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO1 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO2 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO3 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO4 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO5 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO6 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO7 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO8 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO9 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO10 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO11 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO12 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO1 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO2 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO3 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO4 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO5 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO6 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO7 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO8 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO9 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO10 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO11 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO12 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO1 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO2 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO3 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO4 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO5 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO6 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO7 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO8 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO9 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO10 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO11 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO12 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO1 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO2 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO3 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO4 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO5 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO6 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO7 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO8 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO9 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO10 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO11 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO12 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO1 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO2 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO3 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO4 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO5 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO6 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO7 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO8 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO9 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO10 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO11 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO12 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN1 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN2 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN3 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN4 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN5 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN6 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN7 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN8 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN9 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN10 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN11 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN12 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN1 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN2 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN3 */
+ DRXJ_16TO8(0x1010), /* FIRRCA1GAIN4 */
+ DRXJ_16TO8(0x1010), /* FIRRCA1GAIN5 */
+ DRXJ_16TO8(0x1010), /* FIRRCA1GAIN6 */
+ DRXJ_16TO8(0x1010), /* FIRRCA1GAIN7 */
+ DRXJ_16TO8(0x1010) /* FIRRCA1GAIN8 */
+ };
+
+ const u8 vsb_ffe_leak_gain_ram1[] = {
+ DRXJ_16TO8(0x1010), /* FIRRCA1GAIN9 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN10 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN11 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN12 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN1 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN2 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN3 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN4 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN5 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN6 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN7 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN8 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN9 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN10 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN11 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN12 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN1 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN2 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN3 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN4 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN5 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN6 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN7 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN8 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN9 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN10 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN11 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN12 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN1 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN2 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN3 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN4 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN5 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN6 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN7 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN8 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN9 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN10 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN11 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN12 */
+ DRXJ_16TO8(0x001f), /* DFETRAINLKRATIO */
+ DRXJ_16TO8(0x01ff), /* DFERCA1TRAINLKRATIO */
+ DRXJ_16TO8(0x01ff), /* DFERCA1DATALKRATIO */
+ DRXJ_16TO8(0x004f), /* DFERCA2TRAINLKRATIO */
+ DRXJ_16TO8(0x004f), /* DFERCA2DATALKRATIO */
+ DRXJ_16TO8(0x01ff), /* DFEDDM1TRAINLKRATIO */
+ DRXJ_16TO8(0x01ff), /* DFEDDM1DATALKRATIO */
+ DRXJ_16TO8(0x0352), /* DFEDDM2TRAINLKRATIO */
+ DRXJ_16TO8(0x0352), /* DFEDDM2DATALKRATIO */
+ DRXJ_16TO8(0x0000), /* DFETRAINGAIN */
+ DRXJ_16TO8(0x2020), /* DFERCA1GAIN */
+ DRXJ_16TO8(0x1010), /* DFERCA2GAIN */
+ DRXJ_16TO8(0x1818), /* DFEDDM1GAIN */
+ DRXJ_16TO8(0x1212) /* DFEDDM2GAIN */
+ };
+
+ dev_addr = demod->my_i2c_dev_addr;
+ rc = drxdap_fasi_write_block(dev_addr, VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__A, sizeof(vsb_ffe_leak_gain_ram0), ((u8 *)vsb_ffe_leak_gain_ram0), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__A, sizeof(vsb_ffe_leak_gain_ram1), ((u8 *)vsb_ffe_leak_gain_ram1), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int set_vsb()
+* \brief Set 8VSB demod.
+* \param demod instance of demodulator.
+* \return int.
+*
+*/
+static int set_vsb(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ int rc;
+ struct drx_common_attr *common_attr = NULL;
+ struct drxjscu_cmd cmd_scu;
+ struct drxj_data *ext_attr = NULL;
+ u16 cmd_result = 0;
+ u16 cmd_param = 0;
+ const u8 vsb_taps_re[] = {
+ DRXJ_16TO8(-2), /* re0 */
+ DRXJ_16TO8(4), /* re1 */
+ DRXJ_16TO8(1), /* re2 */
+ DRXJ_16TO8(-4), /* re3 */
+ DRXJ_16TO8(1), /* re4 */
+ DRXJ_16TO8(4), /* re5 */
+ DRXJ_16TO8(-3), /* re6 */
+ DRXJ_16TO8(-3), /* re7 */
+ DRXJ_16TO8(6), /* re8 */
+ DRXJ_16TO8(1), /* re9 */
+ DRXJ_16TO8(-9), /* re10 */
+ DRXJ_16TO8(3), /* re11 */
+ DRXJ_16TO8(12), /* re12 */
+ DRXJ_16TO8(-9), /* re13 */
+ DRXJ_16TO8(-15), /* re14 */
+ DRXJ_16TO8(17), /* re15 */
+ DRXJ_16TO8(19), /* re16 */
+ DRXJ_16TO8(-29), /* re17 */
+ DRXJ_16TO8(-22), /* re18 */
+ DRXJ_16TO8(45), /* re19 */
+ DRXJ_16TO8(25), /* re20 */
+ DRXJ_16TO8(-70), /* re21 */
+ DRXJ_16TO8(-28), /* re22 */
+ DRXJ_16TO8(111), /* re23 */
+ DRXJ_16TO8(30), /* re24 */
+ DRXJ_16TO8(-201), /* re25 */
+ DRXJ_16TO8(-31), /* re26 */
+ DRXJ_16TO8(629) /* re27 */
+ };
+
+ dev_addr = demod->my_i2c_dev_addr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* stop all comm_exec */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_COMM_EXEC__A, VSB_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* reset demodulator */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB
+ | SCU_RAM_COMMAND_CMD_DEMOD_RESET;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_DCF_BYPASS__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_ADJ_SEL__A, IQM_FS_ADJ_SEL_B_VSB, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_ADJ_SEL__A, IQM_RC_ADJ_SEL_B_VSB, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->iqm_rc_rate_ofs = 0x00AD0D79;
+ rc = drxdap_fasi_write_reg32(dev_addr, IQM_RC_RATE_OFS_LO__A, ext_attr->iqm_rc_rate_ofs, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CFAGC_GAINSHIFT__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN1TRK__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_CROUT_ENA__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_STRETCH__A, 28, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_ACTIVE__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SYMMETRIC__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_MIDTAP__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_OUT_ENA__A, IQM_CF_OUT_ENA_VSB__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE__A, 1393, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE_SH__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_POW_MEAS_LEN__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(vsb_taps_re), ((u8 *)vsb_taps_re), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(vsb_taps_re), ((u8 *)vsb_taps_re), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_BNTHRESH__A, 330, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* set higher threshold */
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CLPLASTNUM__A, 90, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* burst detection on */
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_RCA1__A, 0x0042, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* drop thresholds by 1 dB */
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_RCA2__A, 0x0053, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* drop thresholds by 2 dB */
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_EQCTRL__A, 0x1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* cma on */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_GPIO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* GPIO */
+
+ /* Initialize the FEC Subsystem */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_TOP_ANNEX__A, FEC_TOP_ANNEX_D, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ {
+ u16 fec_oc_snc_mode = 0;
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* output data even when not locked */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_MODE__A, fec_oc_snc_mode | FEC_OC_SNC_MODE_UNLOCK_ENABLE__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* set clip */
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_LEN__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_TH__A, 470, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_SNS_LEN__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_PT__A, 0xD4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no transparent, no A&C framing; parity is set in mpegoutput */
+ {
+ u16 fec_oc_reg_mode = 0;
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_MODE__A, &fec_oc_reg_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_MODE__A, fec_oc_reg_mode & (~(FEC_OC_MODE_TRANSPARENT__M | FEC_OC_MODE_CLEAR__M | FEC_OC_MODE_RETAIN_FRAMING__M)), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_DI_TIMEOUT_LO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* timeout counter for restarting */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_DI_TIMEOUT_HI__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MODE__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* bypass disabled */
+ /* initialize RS packet error measurement parameters */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PERIOD__A, FEC_RS_MEASUREMENT_PERIOD, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PRESCALE__A, FEC_RS_MEASUREMENT_PRESCALE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* init measurement period of MER/SER */
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_MEASUREMENT_PERIOD__A, VSB_TOP_MEASUREMENT_PERIOD, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_reg32(dev_addr, SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_MEAS_COUNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CKGN1TRK__A, 128, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* B-Input to ADC, PGA+filter in standby */
+ if (!ext_attr->has_lna) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AMUX__A, 0x02, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* turn on IQMAF. It has to be in front of setAgc**() */
+ rc = set_iqm_af(demod, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = adc_synchronization(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = init_agc(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_agc_if(demod, &(ext_attr->vsb_if_agc_cfg), false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_agc_rf(demod, &(ext_attr->vsb_rf_agc_cfg), false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ {
+ /* TODO fix this, store a struct drxj_cfg_afe_gain structure in struct drxj_data instead
+ of only the gain */
+ struct drxj_cfg_afe_gain vsb_pga_cfg = { DRX_STANDARD_8VSB, 0 };
+
+ vsb_pga_cfg.gain = ext_attr->vsb_pga_cfg;
+ rc = ctrl_set_cfg_afe_gain(demod, &vsb_pga_cfg);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = ctrl_set_cfg_pre_saw(demod, &(ext_attr->vsb_pre_saw_cfg));
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Mpeg output has to be in front of FEC active */
+ rc = set_mpegtei_handling(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = bit_reverse_mpeg_output(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_mpeg_start_width(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ {
+ /* TODO: move to set_standard after hardware reset value problem is solved */
+ /* Configure initial MPEG output */
+ struct drx_cfg_mpeg_output cfg_mpeg_output;
+
+ memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output));
+ cfg_mpeg_output.enable_mpeg_output = true;
+
+ rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* TBD: what parameters should be set */
+ cmd_param = 0x00; /* Default mode AGC on, etc */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM;
+ cmd_scu.parameter_len = 1;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = &cmd_param;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_BEAGC_GAINSHIFT__A, 0x0004, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_PT__A, 0x00D2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SYSSMTRNCTRL__A, VSB_TOP_SYSSMTRNCTRL__PRE | VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_BEDETCTRL__A, 0x142, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_LBAGCREFLVL__A, 640, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN1ACQ__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN1TRK__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN2TRK__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* start demodulator */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB
+ | SCU_RAM_COMMAND_CMD_DEMOD_START;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_COMM_EXEC__A, VSB_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn static short get_vsb_post_rs_pck_err(struct i2c_device_addr *dev_addr, u16 *PckErrs)
+* \brief Get the values of packet error in 8VSB mode
+* \return Error code
+*/
+static int get_vsb_post_rs_pck_err(struct i2c_device_addr *dev_addr,
+ u32 *pck_errs, u32 *pck_count)
+{
+ int rc;
+ u16 data = 0;
+ u16 period = 0;
+ u16 prescale = 0;
+ u16 packet_errors_mant = 0;
+ u16 packet_errors_exp = 0;
+
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_FAILURES__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ packet_errors_mant = data & FEC_RS_NR_FAILURES_FIXED_MANT__M;
+ packet_errors_exp = (data & FEC_RS_NR_FAILURES_EXP__M)
+ >> FEC_RS_NR_FAILURES_EXP__B;
+ period = FEC_RS_MEASUREMENT_PERIOD;
+ prescale = FEC_RS_MEASUREMENT_PRESCALE;
+ /* packet error rate = (error packet number) per second */
+ /* 77.3 us is time for per packet */
+ if (period * prescale == 0) {
+ pr_err("error: period and/or prescale is zero!\n");
+ return -EIO;
+ }
+ *pck_errs = packet_errors_mant * (1 << packet_errors_exp);
+ *pck_count = period * prescale * 77;
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn static short GetVSBBer(struct i2c_device_addr *dev_addr, u32 *ber)
+* \brief Get the values of ber in VSB mode
+* \return Error code
+*/
+static int get_vs_bpost_viterbi_ber(struct i2c_device_addr *dev_addr,
+ u32 *ber, u32 *cnt)
+{
+ int rc;
+ u16 data = 0;
+ u16 period = 0;
+ u16 prescale = 0;
+ u16 bit_errors_mant = 0;
+ u16 bit_errors_exp = 0;
+
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_BIT_ERRORS__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ period = FEC_RS_MEASUREMENT_PERIOD;
+ prescale = FEC_RS_MEASUREMENT_PRESCALE;
+
+ bit_errors_mant = data & FEC_RS_NR_BIT_ERRORS_FIXED_MANT__M;
+ bit_errors_exp = (data & FEC_RS_NR_BIT_ERRORS_EXP__M)
+ >> FEC_RS_NR_BIT_ERRORS_EXP__B;
+
+ *cnt = period * prescale * 207 * ((bit_errors_exp > 2) ? 1 : 8);
+
+ if (((bit_errors_mant << bit_errors_exp) >> 3) > 68700)
+ *ber = (*cnt) * 26570;
+ else {
+ if (period * prescale == 0) {
+ pr_err("error: period and/or prescale is zero!\n");
+ return -EIO;
+ }
+ *ber = bit_errors_mant << ((bit_errors_exp > 2) ?
+ (bit_errors_exp - 3) : bit_errors_exp);
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn static short get_vs_bpre_viterbi_ber(struct i2c_device_addr *dev_addr, u32 *ber)
+* \brief Get the values of ber in VSB mode
+* \return Error code
+*/
+static int get_vs_bpre_viterbi_ber(struct i2c_device_addr *dev_addr,
+ u32 *ber, u32 *cnt)
+{
+ u16 data = 0;
+ int rc;
+
+ rc = drxj_dap_read_reg16(dev_addr, VSB_TOP_NR_SYM_ERRS__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ return -EIO;
+ }
+ *ber = data;
+ *cnt = VSB_TOP_MEASUREMENT_PERIOD * SYMBOLS_PER_SEGMENT;
+
+ return 0;
+}
+
+/**
+* \fn static int get_vsbmer(struct i2c_device_addr *dev_addr, u16 *mer)
+* \brief Get the values of MER
+* \return Error code
+*/
+static int get_vsbmer(struct i2c_device_addr *dev_addr, u16 *mer)
+{
+ int rc;
+ u16 data_hi = 0;
+
+ rc = drxj_dap_read_reg16(dev_addr, VSB_TOP_ERR_ENERGY_H__A, &data_hi, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ *mer =
+ (u16) (log1_times100(21504) - log1_times100((data_hi << 6) / 52));
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+
+/*============================================================================*/
+/*== END 8VSB DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== QAM DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/**
+* \fn int power_down_qam ()
+* \brief Powr down QAM related blocks.
+* \param demod instance of demodulator.
+* \param channel pointer to channel data.
+* \return int.
+*/
+static int power_down_qam(struct drx_demod_instance *demod, bool primary)
+{
+ struct drxjscu_cmd cmd_scu = { /* command */ 0,
+ /* parameter_len */ 0,
+ /* result_len */ 0,
+ /* *parameter */ NULL,
+ /* *result */ NULL
+ };
+ int rc;
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drx_cfg_mpeg_output cfg_mpeg_output;
+ struct drx_common_attr *common_attr = demod->my_common_attr;
+ u16 cmd_result = 0;
+
+ /*
+ STOP demodulator
+ resets IQM, QAM and FEC HW blocks
+ */
+ /* stop all comm_exec */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_STOP;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (primary) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_iqm_af(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output));
+ cfg_mpeg_output.enable_mpeg_output = false;
+
+ rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+* \fn int set_qam_measurement ()
+* \brief Setup of the QAM Measuremnt intervals for signal quality
+* \param demod instance of demod.
+* \param constellation current constellation.
+* \return int.
+*
+* NOTE:
+* Take into account that for certain settings the errorcounters can overflow.
+* The implementation does not check this.
+*
+* TODO: overriding the ext_attr->fec_bits_desired by constellation dependent
+* constants to get a measurement period of approx. 1 sec. Remove fec_bits_desired
+* field ?
+*
+*/
+#ifndef DRXJ_VSB_ONLY
+static int
+set_qam_measurement(struct drx_demod_instance *demod,
+ enum drx_modulation constellation, u32 symbol_rate)
+{
+ struct i2c_device_addr *dev_addr = NULL; /* device address for I2C writes */
+ struct drxj_data *ext_attr = NULL; /* Global data container for DRXJ specif data */
+ int rc;
+ u32 fec_bits_desired = 0; /* BER accounting period */
+ u16 fec_rs_plen = 0; /* defines RS BER measurement period */
+ u16 fec_rs_prescale = 0; /* ReedSolomon Measurement Prescale */
+ u32 fec_rs_period = 0; /* Value for corresponding I2C register */
+ u32 fec_rs_bit_cnt = 0; /* Actual precise amount of bits */
+ u32 fec_oc_snc_fail_period = 0; /* Value for corresponding I2C register */
+ u32 qam_vd_period = 0; /* Value for corresponding I2C register */
+ u32 qam_vd_bit_cnt = 0; /* Actual precise amount of bits */
+ u16 fec_vd_plen = 0; /* no of trellis symbols: VD SER measur period */
+ u16 qam_vd_prescale = 0; /* Viterbi Measurement Prescale */
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ fec_bits_desired = ext_attr->fec_bits_desired;
+ fec_rs_prescale = ext_attr->fec_rs_prescale;
+
+ switch (constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ fec_bits_desired = 4 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ fec_bits_desired = 5 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ fec_bits_desired = 6 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM128:
+ fec_bits_desired = 7 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ fec_bits_desired = 8 * symbol_rate;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Parameters for Reed-Solomon Decoder */
+ /* fecrs_period = (int)ceil(FEC_BITS_DESIRED/(fecrs_prescale*plen)) */
+ /* rs_bit_cnt = fecrs_period*fecrs_prescale*plen */
+ /* result is within 32 bit arithmetic -> */
+ /* no need for mult or frac functions */
+
+ /* TODO: use constant instead of calculation and remove the fec_rs_plen in ext_attr */
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ fec_rs_plen = 204 * 8;
+ break;
+ case DRX_STANDARD_ITU_B:
+ fec_rs_plen = 128 * 7;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ext_attr->fec_rs_plen = fec_rs_plen; /* for getSigQual */
+ fec_rs_bit_cnt = fec_rs_prescale * fec_rs_plen; /* temp storage */
+ if (fec_rs_bit_cnt == 0) {
+ pr_err("error: fec_rs_bit_cnt is zero!\n");
+ return -EIO;
+ }
+ fec_rs_period = fec_bits_desired / fec_rs_bit_cnt + 1; /* ceil */
+ if (ext_attr->standard != DRX_STANDARD_ITU_B)
+ fec_oc_snc_fail_period = fec_rs_period;
+
+ /* limit to max 16 bit value (I2C register width) if needed */
+ if (fec_rs_period > 0xFFFF)
+ fec_rs_period = 0xFFFF;
+
+ /* write corresponding registers */
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ break;
+ case DRX_STANDARD_ITU_B:
+ switch (constellation) {
+ case DRX_CONSTELLATION_QAM64:
+ fec_rs_period = 31581;
+ fec_oc_snc_fail_period = 17932;
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ fec_rs_period = 45446;
+ fec_oc_snc_fail_period = 25805;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_FAIL_PERIOD__A, (u16)fec_oc_snc_fail_period, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PERIOD__A, (u16)fec_rs_period, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PRESCALE__A, fec_rs_prescale, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->fec_rs_period = (u16) fec_rs_period;
+ ext_attr->fec_rs_prescale = fec_rs_prescale;
+ rc = drxdap_fasi_write_reg32(dev_addr, SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_MEAS_COUNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ /* Parameters for Viterbi Decoder */
+ /* qamvd_period = (int)ceil(FEC_BITS_DESIRED/ */
+ /* (qamvd_prescale*plen*(qam_constellation+1))) */
+ /* vd_bit_cnt = qamvd_period*qamvd_prescale*plen */
+ /* result is within 32 bit arithmetic -> */
+ /* no need for mult or frac functions */
+
+ /* a(8 bit) * b(8 bit) = 16 bit result => mult32 not needed */
+ fec_vd_plen = ext_attr->fec_vd_plen;
+ qam_vd_prescale = ext_attr->qam_vd_prescale;
+ qam_vd_bit_cnt = qam_vd_prescale * fec_vd_plen; /* temp storage */
+
+ switch (constellation) {
+ case DRX_CONSTELLATION_QAM64:
+ /* a(16 bit) * b(4 bit) = 20 bit result => mult32 not needed */
+ qam_vd_period =
+ qam_vd_bit_cnt * (QAM_TOP_CONSTELLATION_QAM64 + 1)
+ * (QAM_TOP_CONSTELLATION_QAM64 + 1);
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ /* a(16 bit) * b(5 bit) = 21 bit result => mult32 not needed */
+ qam_vd_period =
+ qam_vd_bit_cnt * (QAM_TOP_CONSTELLATION_QAM256 + 1)
+ * (QAM_TOP_CONSTELLATION_QAM256 + 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (qam_vd_period == 0) {
+ pr_err("error: qam_vd_period is zero!\n");
+ return -EIO;
+ }
+ qam_vd_period = fec_bits_desired / qam_vd_period;
+ /* limit to max 16 bit value (I2C register width) if needed */
+ if (qam_vd_period > 0xFFFF)
+ qam_vd_period = 0xFFFF;
+
+ /* a(16 bit) * b(16 bit) = 32 bit result => mult32 not needed */
+ qam_vd_bit_cnt *= qam_vd_period;
+
+ rc = drxj_dap_write_reg16(dev_addr, QAM_VD_MEASUREMENT_PERIOD__A, (u16)qam_vd_period, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_VD_MEASUREMENT_PRESCALE__A, qam_vd_prescale, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->qam_vd_period = (u16) qam_vd_period;
+ ext_attr->qam_vd_prescale = qam_vd_prescale;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+* \fn int set_qam16 ()
+* \brief QAM16 specific setup
+* \param demod instance of demod.
+* \return int.
+*/
+static int set_qam16(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ const u8 qam_dq_qual_fun[] = {
+ DRXJ_16TO8(2), /* fun0 */
+ DRXJ_16TO8(2), /* fun1 */
+ DRXJ_16TO8(2), /* fun2 */
+ DRXJ_16TO8(2), /* fun3 */
+ DRXJ_16TO8(3), /* fun4 */
+ DRXJ_16TO8(3), /* fun5 */
+ };
+ const u8 qam_eq_cma_rad[] = {
+ DRXJ_16TO8(13517), /* RAD0 */
+ DRXJ_16TO8(13517), /* RAD1 */
+ DRXJ_16TO8(13517), /* RAD2 */
+ DRXJ_16TO8(13517), /* RAD3 */
+ DRXJ_16TO8(13517), /* RAD4 */
+ DRXJ_16TO8(13517), /* RAD5 */
+ };
+
+ rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 140, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 120, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 230, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 95, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 105, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 56, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 220, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 25, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 6, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-24), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16)(-65), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-127), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 240, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 40960, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+* \fn int set_qam32 ()
+* \brief QAM32 specific setup
+* \param demod instance of demod.
+* \return int.
+*/
+static int set_qam32(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ const u8 qam_dq_qual_fun[] = {
+ DRXJ_16TO8(3), /* fun0 */
+ DRXJ_16TO8(3), /* fun1 */
+ DRXJ_16TO8(3), /* fun2 */
+ DRXJ_16TO8(3), /* fun3 */
+ DRXJ_16TO8(4), /* fun4 */
+ DRXJ_16TO8(4), /* fun5 */
+ };
+ const u8 qam_eq_cma_rad[] = {
+ DRXJ_16TO8(6707), /* RAD0 */
+ DRXJ_16TO8(6707), /* RAD1 */
+ DRXJ_16TO8(6707), /* RAD2 */
+ DRXJ_16TO8(6707), /* RAD3 */
+ DRXJ_16TO8(6707), /* RAD4 */
+ DRXJ_16TO8(6707), /* RAD5 */
+ };
+
+ rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 90, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 170, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 56, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 140, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16)(-16), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-26), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16)(-56), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-86), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 176, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 20480, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+* \fn int set_qam64 ()
+* \brief QAM64 specific setup
+* \param demod instance of demod.
+* \return int.
+*/
+static int set_qam64(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ const u8 qam_dq_qual_fun[] = { /* this is hw reset value. no necessary to re-write */
+ DRXJ_16TO8(4), /* fun0 */
+ DRXJ_16TO8(4), /* fun1 */
+ DRXJ_16TO8(4), /* fun2 */
+ DRXJ_16TO8(4), /* fun3 */
+ DRXJ_16TO8(6), /* fun4 */
+ DRXJ_16TO8(6), /* fun5 */
+ };
+ const u8 qam_eq_cma_rad[] = {
+ DRXJ_16TO8(13336), /* RAD0 */
+ DRXJ_16TO8(12618), /* RAD1 */
+ DRXJ_16TO8(11988), /* RAD2 */
+ DRXJ_16TO8(13809), /* RAD3 */
+ DRXJ_16TO8(13809), /* RAD4 */
+ DRXJ_16TO8(15609), /* RAD5 */
+ };
+
+ rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 105, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 60, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 195, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 84, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 141, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 7, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-15), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16)(-45), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-80), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 30, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 48, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 160, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 43008, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+* \fn int set_qam128 ()
+* \brief QAM128 specific setup
+* \param demod: instance of demod.
+* \return int.
+*/
+static int set_qam128(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ const u8 qam_dq_qual_fun[] = {
+ DRXJ_16TO8(6), /* fun0 */
+ DRXJ_16TO8(6), /* fun1 */
+ DRXJ_16TO8(6), /* fun2 */
+ DRXJ_16TO8(6), /* fun3 */
+ DRXJ_16TO8(9), /* fun4 */
+ DRXJ_16TO8(9), /* fun5 */
+ };
+ const u8 qam_eq_cma_rad[] = {
+ DRXJ_16TO8(6164), /* RAD0 */
+ DRXJ_16TO8(6598), /* RAD1 */
+ DRXJ_16TO8(6394), /* RAD2 */
+ DRXJ_16TO8(6409), /* RAD3 */
+ DRXJ_16TO8(6656), /* RAD4 */
+ DRXJ_16TO8(7238), /* RAD5 */
+ };
+
+ rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 60, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 140, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 65, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-1), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-23), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 144, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 20992, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+* \fn int set_qam256 ()
+* \brief QAM256 specific setup
+* \param demod: instance of demod.
+* \return int.
+*/
+static int set_qam256(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ const u8 qam_dq_qual_fun[] = {
+ DRXJ_16TO8(8), /* fun0 */
+ DRXJ_16TO8(8), /* fun1 */
+ DRXJ_16TO8(8), /* fun2 */
+ DRXJ_16TO8(8), /* fun3 */
+ DRXJ_16TO8(12), /* fun4 */
+ DRXJ_16TO8(12), /* fun5 */
+ };
+ const u8 qam_eq_cma_rad[] = {
+ DRXJ_16TO8(12345), /* RAD0 */
+ DRXJ_16TO8(12345), /* RAD1 */
+ DRXJ_16TO8(13626), /* RAD2 */
+ DRXJ_16TO8(12931), /* RAD3 */
+ DRXJ_16TO8(14719), /* RAD4 */
+ DRXJ_16TO8(15356), /* RAD5 */
+ };
+
+ rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 60, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 150, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 110, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 74, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 18, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 13, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, 7, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 25, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 48, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 43520, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+#define QAM_SET_OP_ALL 0x1
+#define QAM_SET_OP_CONSTELLATION 0x2
+#define QAM_SET_OP_SPECTRUM 0X4
+
+/**
+* \fn int set_qam ()
+* \brief Set QAM demod.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return int.
+*/
+static int
+set_qam(struct drx_demod_instance *demod,
+ struct drx_channel *channel, s32 tuner_freq_offset, u32 op)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ struct drx_common_attr *common_attr = NULL;
+ int rc;
+ u32 adc_frequency = 0;
+ u32 iqm_rc_rate = 0;
+ u16 cmd_result = 0;
+ u16 lc_symbol_freq = 0;
+ u16 iqm_rc_stretch = 0;
+ u16 set_env_parameters = 0;
+ u16 set_param_parameters[2] = { 0 };
+ struct drxjscu_cmd cmd_scu = { /* command */ 0,
+ /* parameter_len */ 0,
+ /* result_len */ 0,
+ /* parameter */ NULL,
+ /* result */ NULL
+ };
+ const u8 qam_a_taps[] = {
+ DRXJ_16TO8(-1), /* re0 */
+ DRXJ_16TO8(1), /* re1 */
+ DRXJ_16TO8(1), /* re2 */
+ DRXJ_16TO8(-1), /* re3 */
+ DRXJ_16TO8(-1), /* re4 */
+ DRXJ_16TO8(2), /* re5 */
+ DRXJ_16TO8(1), /* re6 */
+ DRXJ_16TO8(-2), /* re7 */
+ DRXJ_16TO8(0), /* re8 */
+ DRXJ_16TO8(3), /* re9 */
+ DRXJ_16TO8(-1), /* re10 */
+ DRXJ_16TO8(-3), /* re11 */
+ DRXJ_16TO8(4), /* re12 */
+ DRXJ_16TO8(1), /* re13 */
+ DRXJ_16TO8(-8), /* re14 */
+ DRXJ_16TO8(4), /* re15 */
+ DRXJ_16TO8(13), /* re16 */
+ DRXJ_16TO8(-13), /* re17 */
+ DRXJ_16TO8(-19), /* re18 */
+ DRXJ_16TO8(28), /* re19 */
+ DRXJ_16TO8(25), /* re20 */
+ DRXJ_16TO8(-53), /* re21 */
+ DRXJ_16TO8(-31), /* re22 */
+ DRXJ_16TO8(96), /* re23 */
+ DRXJ_16TO8(37), /* re24 */
+ DRXJ_16TO8(-190), /* re25 */
+ DRXJ_16TO8(-40), /* re26 */
+ DRXJ_16TO8(619) /* re27 */
+ };
+ const u8 qam_b64_taps[] = {
+ DRXJ_16TO8(0), /* re0 */
+ DRXJ_16TO8(-2), /* re1 */
+ DRXJ_16TO8(1), /* re2 */
+ DRXJ_16TO8(2), /* re3 */
+ DRXJ_16TO8(-2), /* re4 */
+ DRXJ_16TO8(0), /* re5 */
+ DRXJ_16TO8(4), /* re6 */
+ DRXJ_16TO8(-2), /* re7 */
+ DRXJ_16TO8(-4), /* re8 */
+ DRXJ_16TO8(4), /* re9 */
+ DRXJ_16TO8(3), /* re10 */
+ DRXJ_16TO8(-6), /* re11 */
+ DRXJ_16TO8(0), /* re12 */
+ DRXJ_16TO8(6), /* re13 */
+ DRXJ_16TO8(-5), /* re14 */
+ DRXJ_16TO8(-3), /* re15 */
+ DRXJ_16TO8(11), /* re16 */
+ DRXJ_16TO8(-4), /* re17 */
+ DRXJ_16TO8(-19), /* re18 */
+ DRXJ_16TO8(19), /* re19 */
+ DRXJ_16TO8(28), /* re20 */
+ DRXJ_16TO8(-45), /* re21 */
+ DRXJ_16TO8(-36), /* re22 */
+ DRXJ_16TO8(90), /* re23 */
+ DRXJ_16TO8(42), /* re24 */
+ DRXJ_16TO8(-185), /* re25 */
+ DRXJ_16TO8(-46), /* re26 */
+ DRXJ_16TO8(614) /* re27 */
+ };
+ const u8 qam_b256_taps[] = {
+ DRXJ_16TO8(-2), /* re0 */
+ DRXJ_16TO8(4), /* re1 */
+ DRXJ_16TO8(1), /* re2 */
+ DRXJ_16TO8(-4), /* re3 */
+ DRXJ_16TO8(0), /* re4 */
+ DRXJ_16TO8(4), /* re5 */
+ DRXJ_16TO8(-2), /* re6 */
+ DRXJ_16TO8(-4), /* re7 */
+ DRXJ_16TO8(5), /* re8 */
+ DRXJ_16TO8(2), /* re9 */
+ DRXJ_16TO8(-8), /* re10 */
+ DRXJ_16TO8(2), /* re11 */
+ DRXJ_16TO8(11), /* re12 */
+ DRXJ_16TO8(-8), /* re13 */
+ DRXJ_16TO8(-15), /* re14 */
+ DRXJ_16TO8(16), /* re15 */
+ DRXJ_16TO8(19), /* re16 */
+ DRXJ_16TO8(-27), /* re17 */
+ DRXJ_16TO8(-22), /* re18 */
+ DRXJ_16TO8(44), /* re19 */
+ DRXJ_16TO8(26), /* re20 */
+ DRXJ_16TO8(-69), /* re21 */
+ DRXJ_16TO8(-28), /* re22 */
+ DRXJ_16TO8(110), /* re23 */
+ DRXJ_16TO8(31), /* re24 */
+ DRXJ_16TO8(-201), /* re25 */
+ DRXJ_16TO8(-32), /* re26 */
+ DRXJ_16TO8(628) /* re27 */
+ };
+ const u8 qam_c_taps[] = {
+ DRXJ_16TO8(-3), /* re0 */
+ DRXJ_16TO8(3), /* re1 */
+ DRXJ_16TO8(2), /* re2 */
+ DRXJ_16TO8(-4), /* re3 */
+ DRXJ_16TO8(0), /* re4 */
+ DRXJ_16TO8(4), /* re5 */
+ DRXJ_16TO8(-1), /* re6 */
+ DRXJ_16TO8(-4), /* re7 */
+ DRXJ_16TO8(3), /* re8 */
+ DRXJ_16TO8(3), /* re9 */
+ DRXJ_16TO8(-5), /* re10 */
+ DRXJ_16TO8(0), /* re11 */
+ DRXJ_16TO8(9), /* re12 */
+ DRXJ_16TO8(-4), /* re13 */
+ DRXJ_16TO8(-12), /* re14 */
+ DRXJ_16TO8(10), /* re15 */
+ DRXJ_16TO8(16), /* re16 */
+ DRXJ_16TO8(-21), /* re17 */
+ DRXJ_16TO8(-20), /* re18 */
+ DRXJ_16TO8(37), /* re19 */
+ DRXJ_16TO8(25), /* re20 */
+ DRXJ_16TO8(-62), /* re21 */
+ DRXJ_16TO8(-28), /* re22 */
+ DRXJ_16TO8(105), /* re23 */
+ DRXJ_16TO8(31), /* re24 */
+ DRXJ_16TO8(-197), /* re25 */
+ DRXJ_16TO8(-33), /* re26 */
+ DRXJ_16TO8(626) /* re27 */
+ };
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) {
+ if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM256:
+ iqm_rc_rate = 0x00AE3562;
+ lc_symbol_freq =
+ QAM_LC_SYMBOL_FREQ_FREQ_QAM_B_256;
+ channel->symbolrate = 5360537;
+ iqm_rc_stretch = IQM_RC_STRETCH_QAM_B_256;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ iqm_rc_rate = 0x00C05A0E;
+ lc_symbol_freq = 409;
+ channel->symbolrate = 5056941;
+ iqm_rc_stretch = IQM_RC_STRETCH_QAM_B_64;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ adc_frequency = (common_attr->sys_clock_freq * 1000) / 3;
+ if (channel->symbolrate == 0) {
+ pr_err("error: channel symbolrate is zero!\n");
+ return -EIO;
+ }
+ iqm_rc_rate =
+ (adc_frequency / channel->symbolrate) * (1 << 21) +
+ (frac28
+ ((adc_frequency % channel->symbolrate),
+ channel->symbolrate) >> 7) - (1 << 23);
+ lc_symbol_freq =
+ (u16) (frac28
+ (channel->symbolrate +
+ (adc_frequency >> 13),
+ adc_frequency) >> 16);
+ if (lc_symbol_freq > 511)
+ lc_symbol_freq = 511;
+
+ iqm_rc_stretch = 21;
+ }
+
+ if (ext_attr->standard == DRX_STANDARD_ITU_A) {
+ set_env_parameters = QAM_TOP_ANNEX_A; /* annex */
+ set_param_parameters[0] = channel->constellation; /* constellation */
+ set_param_parameters[1] = DRX_INTERLEAVEMODE_I12_J17; /* interleave mode */
+ } else if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ set_env_parameters = QAM_TOP_ANNEX_B; /* annex */
+ set_param_parameters[0] = channel->constellation; /* constellation */
+ set_param_parameters[1] = channel->interleavemode; /* interleave mode */
+ } else if (ext_attr->standard == DRX_STANDARD_ITU_C) {
+ set_env_parameters = QAM_TOP_ANNEX_C; /* annex */
+ set_param_parameters[0] = channel->constellation; /* constellation */
+ set_param_parameters[1] = DRX_INTERLEAVEMODE_I12_J17; /* interleave mode */
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ if (op & QAM_SET_OP_ALL) {
+ /*
+ STEP 1: reset demodulator
+ resets IQM, QAM and FEC HW blocks
+ resets SCU variables
+ */
+ /* stop all comm_exec */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_RESET;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) {
+ /*
+ STEP 2: configure demodulator
+ -set env
+ -set params (resets IQM,QAM,FEC HW; initializes some SCU variables )
+ */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV;
+ cmd_scu.parameter_len = 1;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = &set_env_parameters;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM;
+ cmd_scu.parameter_len = 2;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = set_param_parameters;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* set symbol rate */
+ rc = drxdap_fasi_write_reg32(dev_addr, IQM_RC_RATE_OFS_LO__A, iqm_rc_rate, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->iqm_rc_rate_ofs = iqm_rc_rate;
+ rc = set_qam_measurement(demod, channel->constellation, channel->symbolrate);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ /* STEP 3: enable the system in a mode where the ADC provides valid signal
+ setup constellation independent registers */
+ /* from qam_cmd.py script (qam_driver_b) */
+ /* TODO: remove re-writes of HW reset values */
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_SPECTRUM)) {
+ rc = set_frequency(demod, channel, tuner_freq_offset);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) {
+
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_SYMBOL_FREQ__A, lc_symbol_freq, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_STRETCH__A, iqm_rc_stretch, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ if (op & QAM_SET_OP_ALL) {
+ if (!ext_attr->has_lna) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AMUX__A, 0x02, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SYMMETRIC__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_MIDTAP__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_OUT_ENA__A, IQM_CF_OUT_ENA_QAM__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_WR_RSV_0__A, 0x5f, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* scu temporary shut down agc */
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_SYNC_SEL__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_LEN__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_TH__A, 448, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_SNS_LEN__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PDREF__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, 0x10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PGA_GAIN__A, 11, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_POW_MEAS_LEN__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE_SH__A, IQM_CF_SCALE_SH__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_TIMEOUT__A, QAM_SY_TIMEOUT__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_LWM__A, QAM_SY_SYNC_LWM__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_AWM__A, QAM_SY_SYNC_AWM__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_HWM__A, QAM_SY_SYNC_HWM__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ } else {
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ case DRX_CONSTELLATION_QAM64:
+ case DRX_CONSTELLATION_QAM256:
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_LWM__A, 0x03, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_AWM__A, 0x04, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_HWM__A, QAM_SY_SYNC_HWM__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ case DRX_CONSTELLATION_QAM128:
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_LWM__A, 0x03, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_AWM__A, 0x05, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_HWM__A, 0x06, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EIO;
+ } /* switch */
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_MODE__A, QAM_LC_MODE__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_RATE_LIMIT__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_LPF_FACTORP__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_LPF_FACTORI__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_MODE__A, 7, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB0__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB1__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB2__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB3__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB4__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB5__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB6__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB8__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB9__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB10__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB12__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB15__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB16__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB20__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB25__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_ADJ_SEL__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_ADJ_SEL__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_ADJ_SEL__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_POW_MEAS_LEN__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_GPIO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* No more resets of the IQM, current standard correctly set =>
+ now AGCs can be configured. */
+ /* turn on IQMAF. It has to be in front of setAgc**() */
+ rc = set_iqm_af(demod, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = adc_synchronization(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = init_agc(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_agc_if(demod, &(ext_attr->qam_if_agc_cfg), false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_agc_rf(demod, &(ext_attr->qam_rf_agc_cfg), false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ {
+ /* TODO fix this, store a struct drxj_cfg_afe_gain structure in struct drxj_data instead
+ of only the gain */
+ struct drxj_cfg_afe_gain qam_pga_cfg = { DRX_STANDARD_ITU_B, 0 };
+
+ qam_pga_cfg.gain = ext_attr->qam_pga_cfg;
+ rc = ctrl_set_cfg_afe_gain(demod, &qam_pga_cfg);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = ctrl_set_cfg_pre_saw(demod, &(ext_attr->qam_pre_saw_cfg));
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) {
+ if (ext_attr->standard == DRX_STANDARD_ITU_A) {
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_a_taps), ((u8 *)qam_a_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_a_taps), ((u8 *)qam_a_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM64:
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_b64_taps), ((u8 *)qam_b64_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_b64_taps), ((u8 *)qam_b64_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_b256_taps), ((u8 *)qam_b256_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_b256_taps), ((u8 *)qam_b256_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EIO;
+ }
+ } else if (ext_attr->standard == DRX_STANDARD_ITU_C) {
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_c_taps), ((u8 *)qam_c_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_c_taps), ((u8 *)qam_c_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* SETP 4: constellation specific setup */
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ rc = set_qam16(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ rc = set_qam32(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ rc = set_qam64(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_QAM128:
+ rc = set_qam128(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ rc = set_qam256(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EIO;
+ } /* switch */
+ }
+
+ if ((op & QAM_SET_OP_ALL)) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE_SH__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Mpeg output has to be in front of FEC active */
+ rc = set_mpegtei_handling(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = bit_reverse_mpeg_output(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_mpeg_start_width(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ {
+ /* TODO: move to set_standard after hardware reset value problem is solved */
+ /* Configure initial MPEG output */
+ struct drx_cfg_mpeg_output cfg_mpeg_output;
+
+ memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output));
+ cfg_mpeg_output.enable_mpeg_output = true;
+
+ rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ }
+
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) {
+
+ /* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_START;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_COMM_EXEC__A, QAM_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+static int ctrl_get_qam_sig_quality(struct drx_demod_instance *demod);
+
+static int qam_flip_spec(struct drx_demod_instance *demod, struct drx_channel *channel)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ int rc;
+ u32 iqm_fs_rate_ofs = 0;
+ u32 iqm_fs_rate_lo = 0;
+ u16 qam_ctl_ena = 0;
+ u16 data = 0;
+ u16 equ_mode = 0;
+ u16 fsm_state = 0;
+ int i = 0;
+ int ofsofs = 0;
+
+ /* Silence the controlling of lc, equ, and the acquisition state machine */
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_QAM_CTL_ENA__A, &qam_ctl_ena, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_CTL_ENA__A, qam_ctl_ena & ~(SCU_RAM_QAM_CTL_ENA_ACQ__M | SCU_RAM_QAM_CTL_ENA_EQU__M | SCU_RAM_QAM_CTL_ENA_LC__M), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* freeze the frequency control loop */
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_CF__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_CF1__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_atomic_read_reg32(dev_addr, IQM_FS_RATE_OFS_LO__A, &iqm_fs_rate_ofs, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_atomic_read_reg32(dev_addr, IQM_FS_RATE_LO__A, &iqm_fs_rate_lo, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ofsofs = iqm_fs_rate_lo - iqm_fs_rate_ofs;
+ iqm_fs_rate_ofs = ~iqm_fs_rate_ofs + 1;
+ iqm_fs_rate_ofs -= 2 * ofsofs;
+
+ /* freeze dq/fq updating */
+ rc = drxj_dap_read_reg16(dev_addr, QAM_DQ_MODE__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data = (data & 0xfff9);
+ rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* lc_cp / _ci / _ca */
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_CI__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_EP__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_LA_FACTOR__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* flip the spec */
+ rc = drxdap_fasi_write_reg32(dev_addr, IQM_FS_RATE_OFS_LO__A, iqm_fs_rate_ofs, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->iqm_fs_rate_ofs = iqm_fs_rate_ofs;
+ ext_attr->pos_image = (ext_attr->pos_image) ? false : true;
+
+ /* freeze dq/fq updating */
+ rc = drxj_dap_read_reg16(dev_addr, QAM_DQ_MODE__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ equ_mode = data;
+ data = (data & 0xfff9);
+ rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ for (i = 0; i < 28; i++) {
+ rc = drxj_dap_read_reg16(dev_addr, QAM_DQ_TAP_IM_EL0__A + (2 * i), &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_TAP_IM_EL0__A + (2 * i), -data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ for (i = 0; i < 24; i++) {
+ rc = drxj_dap_read_reg16(dev_addr, QAM_FQ_TAP_IM_EL0__A + (2 * i), &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_TAP_IM_EL0__A + (2 * i), -data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ data = equ_mode;
+ rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_STATE_TGT__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ i = 0;
+ while ((fsm_state != 4) && (i++ < 100)) {
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_QAM_FSM_STATE__A, &fsm_state, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_CTL_ENA__A, (qam_ctl_ena | 0x0016), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+
+}
+
+#define NO_LOCK 0x0
+#define DEMOD_LOCKED 0x1
+#define SYNC_FLIPPED 0x2
+#define SPEC_MIRRORED 0x4
+/**
+* \fn int qam64auto ()
+* \brief auto do sync pattern switching and mirroring.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \param tuner_freq_offset: tuner frequency offset.
+* \param lock_status: pointer to lock status.
+* \return int.
+*/
+static int
+qam64auto(struct drx_demod_instance *demod,
+ struct drx_channel *channel,
+ s32 tuner_freq_offset, enum drx_lock_status *lock_status)
+{
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drx39xxj_state *state = dev_addr->user_data;
+ struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache;
+ int rc;
+ u32 lck_state = NO_LOCK;
+ u32 start_time = 0;
+ u32 d_locked_time = 0;
+ u32 timeout_ofs = 0;
+ u16 data = 0;
+
+ /* external attributes for storing aquired channel constellation */
+ *lock_status = DRX_NOT_LOCKED;
+ start_time = jiffies_to_msecs(jiffies);
+ lck_state = NO_LOCK;
+ do {
+ rc = ctrl_lock_status(demod, lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ switch (lck_state) {
+ case NO_LOCK:
+ if (*lock_status == DRXJ_DEMOD_LOCK) {
+ rc = ctrl_get_qam_sig_quality(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (p->cnr.stat[0].svalue > 20800) {
+ lck_state = DEMOD_LOCKED;
+ /* some delay to see if fec_lock possible TODO find the right value */
+ timeout_ofs += DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME; /* see something, waiting longer */
+ d_locked_time = jiffies_to_msecs(jiffies);
+ }
+ }
+ break;
+ case DEMOD_LOCKED:
+ if ((*lock_status == DRXJ_DEMOD_LOCK) && /* still demod_lock in 150ms */
+ ((jiffies_to_msecs(jiffies) - d_locked_time) >
+ DRXJ_QAM_FEC_LOCK_WAITTIME)) {
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, data | 0x1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ lck_state = SYNC_FLIPPED;
+ msleep(10);
+ }
+ break;
+ case SYNC_FLIPPED:
+ if (*lock_status == DRXJ_DEMOD_LOCK) {
+ if (channel->mirror == DRX_MIRROR_AUTO) {
+ /* flip sync pattern back */
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, data & 0xFFFE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* flip spectrum */
+ ext_attr->mirror = DRX_MIRROR_YES;
+ rc = qam_flip_spec(demod, channel);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ lck_state = SPEC_MIRRORED;
+ /* reset timer TODO: still need 500ms? */
+ start_time = d_locked_time =
+ jiffies_to_msecs(jiffies);
+ timeout_ofs = 0;
+ } else { /* no need to wait lock */
+
+ start_time =
+ jiffies_to_msecs(jiffies) -
+ DRXJ_QAM_MAX_WAITTIME - timeout_ofs;
+ }
+ }
+ break;
+ case SPEC_MIRRORED:
+ if ((*lock_status == DRXJ_DEMOD_LOCK) && /* still demod_lock in 150ms */
+ ((jiffies_to_msecs(jiffies) - d_locked_time) >
+ DRXJ_QAM_FEC_LOCK_WAITTIME)) {
+ rc = ctrl_get_qam_sig_quality(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (p->cnr.stat[0].svalue > 20800) {
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, data | 0x1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no need to wait lock */
+ start_time =
+ jiffies_to_msecs(jiffies) -
+ DRXJ_QAM_MAX_WAITTIME - timeout_ofs;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ msleep(10);
+ } while
+ ((*lock_status != DRX_LOCKED) &&
+ (*lock_status != DRX_NEVER_LOCK) &&
+ ((jiffies_to_msecs(jiffies) - start_time) <
+ (DRXJ_QAM_MAX_WAITTIME + timeout_ofs))
+ );
+ /* Returning control to apllication ... */
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int qam256auto ()
+* \brief auto do sync pattern switching and mirroring.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \param tuner_freq_offset: tuner frequency offset.
+* \param lock_status: pointer to lock status.
+* \return int.
+*/
+static int
+qam256auto(struct drx_demod_instance *demod,
+ struct drx_channel *channel,
+ s32 tuner_freq_offset, enum drx_lock_status *lock_status)
+{
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drx39xxj_state *state = dev_addr->user_data;
+ struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache;
+ int rc;
+ u32 lck_state = NO_LOCK;
+ u32 start_time = 0;
+ u32 d_locked_time = 0;
+ u32 timeout_ofs = DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME;
+
+ /* external attributes for storing aquired channel constellation */
+ *lock_status = DRX_NOT_LOCKED;
+ start_time = jiffies_to_msecs(jiffies);
+ lck_state = NO_LOCK;
+ do {
+ rc = ctrl_lock_status(demod, lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ switch (lck_state) {
+ case NO_LOCK:
+ if (*lock_status == DRXJ_DEMOD_LOCK) {
+ rc = ctrl_get_qam_sig_quality(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (p->cnr.stat[0].svalue > 26800) {
+ lck_state = DEMOD_LOCKED;
+ timeout_ofs += DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME; /* see something, wait longer */
+ d_locked_time = jiffies_to_msecs(jiffies);
+ }
+ }
+ break;
+ case DEMOD_LOCKED:
+ if (*lock_status == DRXJ_DEMOD_LOCK) {
+ if ((channel->mirror == DRX_MIRROR_AUTO) &&
+ ((jiffies_to_msecs(jiffies) - d_locked_time) >
+ DRXJ_QAM_FEC_LOCK_WAITTIME)) {
+ ext_attr->mirror = DRX_MIRROR_YES;
+ rc = qam_flip_spec(demod, channel);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ lck_state = SPEC_MIRRORED;
+ /* reset timer TODO: still need 300ms? */
+ start_time = jiffies_to_msecs(jiffies);
+ timeout_ofs = -DRXJ_QAM_MAX_WAITTIME / 2;
+ }
+ }
+ break;
+ case SPEC_MIRRORED:
+ break;
+ default:
+ break;
+ }
+ msleep(10);
+ } while
+ ((*lock_status < DRX_LOCKED) &&
+ (*lock_status != DRX_NEVER_LOCK) &&
+ ((jiffies_to_msecs(jiffies) - start_time) <
+ (DRXJ_QAM_MAX_WAITTIME + timeout_ofs)));
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int set_qam_channel ()
+* \brief Set QAM channel according to the requested constellation.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return int.
+*/
+static int
+set_qam_channel(struct drx_demod_instance *demod,
+ struct drx_channel *channel, s32 tuner_freq_offset)
+{
+ struct drxj_data *ext_attr = NULL;
+ int rc;
+ enum drx_lock_status lock_status = DRX_NOT_LOCKED;
+ bool auto_flag = false;
+
+ /* external attributes for storing aquired channel constellation */
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* set QAM channel constellation */
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ case DRX_CONSTELLATION_QAM32:
+ case DRX_CONSTELLATION_QAM128:
+ return -EINVAL;
+ case DRX_CONSTELLATION_QAM64:
+ case DRX_CONSTELLATION_QAM256:
+ if (ext_attr->standard != DRX_STANDARD_ITU_B)
+ return -EINVAL;
+
+ ext_attr->constellation = channel->constellation;
+ if (channel->mirror == DRX_MIRROR_AUTO)
+ ext_attr->mirror = DRX_MIRROR_NO;
+ else
+ ext_attr->mirror = channel->mirror;
+
+ rc = set_qam(demod, channel, tuner_freq_offset, QAM_SET_OP_ALL);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (channel->constellation == DRX_CONSTELLATION_QAM64)
+ rc = qam64auto(demod, channel, tuner_freq_offset,
+ &lock_status);
+ else
+ rc = qam256auto(demod, channel, tuner_freq_offset,
+ &lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_AUTO: /* for channel scan */
+ if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ u16 qam_ctl_ena = 0;
+
+ auto_flag = true;
+
+ /* try to lock default QAM constellation: QAM256 */
+ channel->constellation = DRX_CONSTELLATION_QAM256;
+ ext_attr->constellation = DRX_CONSTELLATION_QAM256;
+ if (channel->mirror == DRX_MIRROR_AUTO)
+ ext_attr->mirror = DRX_MIRROR_NO;
+ else
+ ext_attr->mirror = channel->mirror;
+ rc = set_qam(demod, channel, tuner_freq_offset,
+ QAM_SET_OP_ALL);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = qam256auto(demod, channel, tuner_freq_offset,
+ &lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (lock_status >= DRX_LOCKED) {
+ channel->constellation = DRX_CONSTELLATION_AUTO;
+ break;
+ }
+
+ /* QAM254 not locked. Try QAM64 constellation */
+ channel->constellation = DRX_CONSTELLATION_QAM64;
+ ext_attr->constellation = DRX_CONSTELLATION_QAM64;
+ if (channel->mirror == DRX_MIRROR_AUTO)
+ ext_attr->mirror = DRX_MIRROR_NO;
+ else
+ ext_attr->mirror = channel->mirror;
+
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ &qam_ctl_ena, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ qam_ctl_ena & ~SCU_RAM_QAM_CTL_ENA_ACQ__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_FSM_STATE_TGT__A,
+ 0x2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* force to rate hunting */
+
+ rc = set_qam(demod, channel, tuner_freq_offset,
+ QAM_SET_OP_CONSTELLATION);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ qam_ctl_ena, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = qam64auto(demod, channel, tuner_freq_offset,
+ &lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ channel->constellation = DRX_CONSTELLATION_AUTO;
+ } else if (ext_attr->standard == DRX_STANDARD_ITU_C) {
+ u16 qam_ctl_ena = 0;
+
+ channel->constellation = DRX_CONSTELLATION_QAM64;
+ ext_attr->constellation = DRX_CONSTELLATION_QAM64;
+ auto_flag = true;
+
+ if (channel->mirror == DRX_MIRROR_AUTO)
+ ext_attr->mirror = DRX_MIRROR_NO;
+ else
+ ext_attr->mirror = channel->mirror;
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ &qam_ctl_ena, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ qam_ctl_ena & ~SCU_RAM_QAM_CTL_ENA_ACQ__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_FSM_STATE_TGT__A,
+ 0x2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* force to rate hunting */
+
+ rc = set_qam(demod, channel, tuner_freq_offset,
+ QAM_SET_OP_CONSTELLATION);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ qam_ctl_ena, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = qam64auto(demod, channel, tuner_freq_offset,
+ &lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ channel->constellation = DRX_CONSTELLATION_AUTO;
+ } else {
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+rw_error:
+ /* restore starting value */
+ if (auto_flag)
+ channel->constellation = DRX_CONSTELLATION_AUTO;
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+* \fn static short get_qamrs_err_count(struct i2c_device_addr *dev_addr)
+* \brief Get RS error count in QAM mode (used for post RS BER calculation)
+* \return Error code
+*
+* precondition: measurement period & measurement prescale must be set
+*
+*/
+static int
+get_qamrs_err_count(struct i2c_device_addr *dev_addr,
+ struct drxjrs_errors *rs_errors)
+{
+ int rc;
+ u16 nr_bit_errors = 0,
+ nr_symbol_errors = 0,
+ nr_packet_errors = 0, nr_failures = 0, nr_snc_par_fail_count = 0;
+
+ /* check arguments */
+ if (dev_addr == NULL)
+ return -EINVAL;
+
+ /* all reported errors are received in the */
+ /* most recently finished measurment period */
+ /* no of pre RS bit errors */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_BIT_ERRORS__A, &nr_bit_errors, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no of symbol errors */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_SYMBOL_ERRORS__A, &nr_symbol_errors, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no of packet errors */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_PACKET_ERRORS__A, &nr_packet_errors, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no of failures to decode */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_FAILURES__A, &nr_failures, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no of post RS bit erros */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_FAIL_COUNT__A, &nr_snc_par_fail_count, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* TODO: NOTE */
+ /* These register values are fetched in non-atomic fashion */
+ /* It is possible that the read values contain unrelated information */
+
+ rs_errors->nr_bit_errors = nr_bit_errors & FEC_RS_NR_BIT_ERRORS__M;
+ rs_errors->nr_symbol_errors = nr_symbol_errors & FEC_RS_NR_SYMBOL_ERRORS__M;
+ rs_errors->nr_packet_errors = nr_packet_errors & FEC_RS_NR_PACKET_ERRORS__M;
+ rs_errors->nr_failures = nr_failures & FEC_RS_NR_FAILURES__M;
+ rs_errors->nr_snc_par_fail_count =
+ nr_snc_par_fail_count & FEC_OC_SNC_FAIL_COUNT__M;
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+ * \fn int get_sig_strength()
+ * \brief Retrieve signal strength for VSB and QAM.
+ * \param demod Pointer to demod instance
+ * \param u16-t Pointer to signal strength data; range 0, .. , 100.
+ * \return int.
+ * \retval 0 sig_strength contains valid data.
+ * \retval -EINVAL sig_strength is NULL.
+ * \retval -EIO Erroneous data, sig_strength contains invalid data.
+ */
+#define DRXJ_AGC_TOP 0x2800
+#define DRXJ_AGC_SNS 0x1600
+#define DRXJ_RFAGC_MAX 0x3fff
+#define DRXJ_RFAGC_MIN 0x800
+
+static int get_sig_strength(struct drx_demod_instance *demod, u16 *sig_strength)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ u16 rf_gain = 0;
+ u16 if_gain = 0;
+ u16 if_agc_sns = 0;
+ u16 if_agc_top = 0;
+ u16 rf_agc_max = 0;
+ u16 rf_agc_min = 0;
+
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_AGC_IF__A, &if_gain, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if_gain &= IQM_AF_AGC_IF__M;
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_AGC_RF__A, &rf_gain, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rf_gain &= IQM_AF_AGC_RF__M;
+
+ if_agc_sns = DRXJ_AGC_SNS;
+ if_agc_top = DRXJ_AGC_TOP;
+ rf_agc_max = DRXJ_RFAGC_MAX;
+ rf_agc_min = DRXJ_RFAGC_MIN;
+
+ if (if_gain > if_agc_top) {
+ if (rf_gain > rf_agc_max)
+ *sig_strength = 100;
+ else if (rf_gain > rf_agc_min) {
+ if (rf_agc_max == rf_agc_min) {
+ pr_err("error: rf_agc_max == rf_agc_min\n");
+ return -EIO;
+ }
+ *sig_strength =
+ 75 + 25 * (rf_gain - rf_agc_min) / (rf_agc_max -
+ rf_agc_min);
+ } else
+ *sig_strength = 75;
+ } else if (if_gain > if_agc_sns) {
+ if (if_agc_top == if_agc_sns) {
+ pr_err("error: if_agc_top == if_agc_sns\n");
+ return -EIO;
+ }
+ *sig_strength =
+ 20 + 55 * (if_gain - if_agc_sns) / (if_agc_top - if_agc_sns);
+ } else {
+ if (!if_agc_sns) {
+ pr_err("error: if_agc_sns is zero!\n");
+ return -EIO;
+ }
+ *sig_strength = (20 * if_gain / if_agc_sns);
+ }
+
+ if (*sig_strength <= 7)
+ *sig_strength = 0;
+
+ return 0;
+ rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int ctrl_get_qam_sig_quality()
+* \brief Retreive QAM signal quality from device.
+* \param devmod Pointer to demodulator instance.
+* \param sig_quality Pointer to signal quality data.
+* \return int.
+* \retval 0 sig_quality contains valid data.
+* \retval -EINVAL sig_quality is NULL.
+* \retval -EIO Erroneous data, sig_quality contains invalid data.
+
+* Pre-condition: Device must be started and in lock.
+*/
+static int
+ctrl_get_qam_sig_quality(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ struct drx39xxj_state *state = dev_addr->user_data;
+ struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache;
+ struct drxjrs_errors measuredrs_errors = { 0, 0, 0, 0, 0 };
+ enum drx_modulation constellation = ext_attr->constellation;
+ int rc;
+
+ u32 pre_bit_err_rs = 0; /* pre RedSolomon Bit Error Rate */
+ u32 post_bit_err_rs = 0; /* post RedSolomon Bit Error Rate */
+ u32 pkt_errs = 0; /* no of packet errors in RS */
+ u16 qam_sl_err_power = 0; /* accumulated error between raw and sliced symbols */
+ u16 qsym_err_vd = 0; /* quadrature symbol errors in QAM_VD */
+ u16 fec_oc_period = 0; /* SNC sync failure measurement period */
+ u16 fec_rs_prescale = 0; /* ReedSolomon Measurement Prescale */
+ u16 fec_rs_period = 0; /* Value for corresponding I2C register */
+ /* calculation constants */
+ u32 rs_bit_cnt = 0; /* RedSolomon Bit Count */
+ u32 qam_sl_sig_power = 0; /* used for MER, depends of QAM constellation */
+ /* intermediate results */
+ u32 e = 0; /* exponent value used for QAM BER/SER */
+ u32 m = 0; /* mantisa value used for QAM BER/SER */
+ u32 ber_cnt = 0; /* BER count */
+ /* signal quality info */
+ u32 qam_sl_mer = 0; /* QAM MER */
+ u32 qam_pre_rs_ber = 0; /* Pre RedSolomon BER */
+ u32 qam_post_rs_ber = 0; /* Post RedSolomon BER */
+ u32 qam_vd_ser = 0; /* ViterbiDecoder SER */
+ u16 qam_vd_prescale = 0; /* Viterbi Measurement Prescale */
+ u16 qam_vd_period = 0; /* Viterbi Measurement period */
+ u32 vd_bit_cnt = 0; /* ViterbiDecoder Bit Count */
+
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ /* read the physical registers */
+ /* Get the RS error data */
+ rc = get_qamrs_err_count(dev_addr, &measuredrs_errors);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* get the register value needed for MER */
+ rc = drxj_dap_read_reg16(dev_addr, QAM_SL_ERR_POWER__A, &qam_sl_err_power, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* get the register value needed for post RS BER */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_FAIL_PERIOD__A, &fec_oc_period, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* get constants needed for signal quality calculation */
+ fec_rs_period = ext_attr->fec_rs_period;
+ fec_rs_prescale = ext_attr->fec_rs_prescale;
+ rs_bit_cnt = fec_rs_period * fec_rs_prescale * ext_attr->fec_rs_plen;
+ qam_vd_period = ext_attr->qam_vd_period;
+ qam_vd_prescale = ext_attr->qam_vd_prescale;
+ vd_bit_cnt = qam_vd_period * qam_vd_prescale * ext_attr->fec_vd_plen;
+
+ /* DRXJ_QAM_SL_SIG_POWER_QAMxxx * 4 */
+ switch (constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM16 << 2;
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM32 << 2;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM64 << 2;
+ break;
+ case DRX_CONSTELLATION_QAM128:
+ qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM128 << 2;
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM256 << 2;
+ break;
+ default:
+ return -EIO;
+ }
+
+ /* ------------------------------ */
+ /* MER Calculation */
+ /* ------------------------------ */
+ /* MER is good if it is above 27.5 for QAM256 or 21.5 for QAM64 */
+
+ /* 10.0*log10(qam_sl_sig_power * 4.0 / qam_sl_err_power); */
+ if (qam_sl_err_power == 0)
+ qam_sl_mer = 0;
+ else
+ qam_sl_mer = log1_times100(qam_sl_sig_power) - log1_times100((u32)qam_sl_err_power);
+
+ /* ----------------------------------------- */
+ /* Pre Viterbi Symbol Error Rate Calculation */
+ /* ----------------------------------------- */
+ /* pre viterbi SER is good if it is bellow 0.025 */
+
+ /* get the register value */
+ /* no of quadrature symbol errors */
+ rc = drxj_dap_read_reg16(dev_addr, QAM_VD_NR_QSYM_ERRORS__A, &qsym_err_vd, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Extract the Exponent and the Mantisa */
+ /* of number of quadrature symbol errors */
+ e = (qsym_err_vd & QAM_VD_NR_QSYM_ERRORS_EXP__M) >>
+ QAM_VD_NR_QSYM_ERRORS_EXP__B;
+ m = (qsym_err_vd & QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__M) >>
+ QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__B;
+
+ if ((m << e) >> 3 > 549752)
+ qam_vd_ser = 500000 * vd_bit_cnt * ((e > 2) ? 1 : 8) / 8;
+ else
+ qam_vd_ser = m << ((e > 2) ? (e - 3) : e);
+
+ /* --------------------------------------- */
+ /* pre and post RedSolomon BER Calculation */
+ /* --------------------------------------- */
+ /* pre RS BER is good if it is below 3.5e-4 */
+
+ /* get the register values */
+ pre_bit_err_rs = (u32) measuredrs_errors.nr_bit_errors;
+ pkt_errs = post_bit_err_rs = (u32) measuredrs_errors.nr_snc_par_fail_count;
+
+ /* Extract the Exponent and the Mantisa of the */
+ /* pre Reed-Solomon bit error count */
+ e = (pre_bit_err_rs & FEC_RS_NR_BIT_ERRORS_EXP__M) >>
+ FEC_RS_NR_BIT_ERRORS_EXP__B;
+ m = (pre_bit_err_rs & FEC_RS_NR_BIT_ERRORS_FIXED_MANT__M) >>
+ FEC_RS_NR_BIT_ERRORS_FIXED_MANT__B;
+
+ ber_cnt = m << e;
+
+ /*qam_pre_rs_ber = frac_times1e6( ber_cnt, rs_bit_cnt ); */
+ if (m > (rs_bit_cnt >> (e + 1)) || (rs_bit_cnt >> e) == 0)
+ qam_pre_rs_ber = 500000 * rs_bit_cnt >> e;
+ else
+ qam_pre_rs_ber = ber_cnt;
+
+ /* post RS BER = 1000000* (11.17 * FEC_OC_SNC_FAIL_COUNT__A) / */
+ /* (1504.0 * FEC_OC_SNC_FAIL_PERIOD__A) */
+ /*
+ => c = (1000000*100*11.17)/1504 =
+ post RS BER = (( c* FEC_OC_SNC_FAIL_COUNT__A) /
+ (100 * FEC_OC_SNC_FAIL_PERIOD__A)
+ *100 and /100 is for more precision.
+ => (20 bits * 12 bits) /(16 bits * 7 bits) => safe in 32 bits computation
+
+ Precision errors still possible.
+ */
+ e = post_bit_err_rs * 742686;
+ m = fec_oc_period * 100;
+ if (fec_oc_period == 0)
+ qam_post_rs_ber = 0xFFFFFFFF;
+ else
+ qam_post_rs_ber = e / m;
+
+ /* fill signal quality data structure */
+ p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+
+ p->cnr.stat[0].svalue = ((u16) qam_sl_mer) * 100;
+ if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ p->pre_bit_error.stat[0].uvalue += qam_vd_ser;
+ p->pre_bit_count.stat[0].uvalue += vd_bit_cnt * ((e > 2) ? 1 : 8) / 8;
+ } else {
+ p->pre_bit_error.stat[0].uvalue += qam_pre_rs_ber;
+ p->pre_bit_count.stat[0].uvalue += rs_bit_cnt >> e;
+ }
+
+ p->post_bit_error.stat[0].uvalue += qam_post_rs_ber;
+ p->post_bit_count.stat[0].uvalue += rs_bit_cnt >> e;
+
+ p->block_error.stat[0].uvalue += pkt_errs;
+
+#ifdef DRXJ_SIGNAL_ACCUM_ERR
+ rc = get_acc_pkt_err(demod, &sig_quality->packet_error);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+#endif
+
+ return 0;
+rw_error:
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ return -EIO;
+}
+
+#endif /* #ifndef DRXJ_VSB_ONLY */
+
+/*============================================================================*/
+/*== END QAM DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== ATV DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*
+ Implementation notes.
+
+ NTSC/FM AGCs
+
+ Four AGCs are used for NTSC:
+ (1) RF (used to attenuate the input signal in case of to much power)
+ (2) IF (used to attenuate the input signal in case of to much power)
+ (3) Video AGC (used to amplify the output signal in case input to low)
+ (4) SIF AGC (used to amplify the output signal in case input to low)
+
+ Video AGC is coupled to RF and IF. SIF AGC is not coupled. It is assumed
+ that the coupling between Video AGC and the RF and IF AGCs also works in
+ favor of the SIF AGC.
+
+ Three AGCs are used for FM:
+ (1) RF (used to attenuate the input signal in case of to much power)
+ (2) IF (used to attenuate the input signal in case of to much power)
+ (3) SIF AGC (used to amplify the output signal in case input to low)
+
+ The SIF AGC is now coupled to the RF/IF AGCs.
+ The SIF AGC is needed for both SIF ouput and the internal SIF signal to
+ the AUD block.
+
+ RF and IF AGCs DACs are part of AFE, Video and SIF AGC DACs are part of
+ the ATV block. The AGC control algorithms are all implemented in
+ microcode.
+
+ ATV SETTINGS
+
+ (Shadow settings will not be used for now, they will be implemented
+ later on because of the schedule)
+
+ Several HW/SCU "settings" can be used for ATV. The standard selection
+ will reset most of these settings. To avoid that the end user apllication
+ has to perform these settings each time the ATV or FM standards is
+ selected the driver will shadow these settings. This enables the end user
+ to perform the settings only once after a drx_open(). The driver must
+ write the shadow settings to HW/SCU incase:
+ ( setstandard FM/ATV) ||
+ ( settings have changed && FM/ATV standard is active)
+ The shadow settings will be stored in the device specific data container.
+ A set of flags will be defined to flag changes in shadow settings.
+ A routine will be implemented to write all changed shadow settings to
+ HW/SCU.
+
+ The "settings" will consist of: AGC settings, filter settings etc.
+
+ Disadvantage of use of shadow settings:
+ Direct changes in HW/SCU registers will not be reflected in the
+ shadow settings and these changes will be overwritten during a next
+ update. This can happen during evaluation. This will not be a problem
+ for normal customer usage.
+*/
+/* -------------------------------------------------------------------------- */
+
+/**
+* \fn int power_down_atv ()
+* \brief Power down ATV.
+* \param demod instance of demodulator
+* \param standard either NTSC or FM (sub strandard for ATV )
+* \return int.
+*
+* Stops and thus resets ATV and IQM block
+* SIF and CVBS ADC are powered down
+* Calls audio power down
+*/
+static int
+power_down_atv(struct drx_demod_instance *demod, enum drx_standard standard, bool primary)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxjscu_cmd cmd_scu = { /* command */ 0,
+ /* parameter_len */ 0,
+ /* result_len */ 0,
+ /* *parameter */ NULL,
+ /* *result */ NULL
+ };
+ int rc;
+ u16 cmd_result = 0;
+
+ /* ATV NTSC */
+
+ /* Stop ATV SCU (will reset ATV and IQM hardware */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_ATV |
+ SCU_RAM_COMMAND_CMD_DEMOD_STOP;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Disable ATV outputs (ATV reset enables CVBS, undo this) */
+ rc = drxj_dap_write_reg16(dev_addr, ATV_TOP_STDBY__A, (ATV_TOP_STDBY_SIF_STDBY_STANDBY & (~ATV_TOP_STDBY_CVBS_STDBY_A2_ACTIVE)), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, ATV_COMM_EXEC__A, ATV_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (primary) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_iqm_af(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = power_down_aud(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Power up AUD.
+* \param demod instance of demodulator
+* \return int.
+*
+*/
+static int power_down_aud(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ int rc;
+
+ dev_addr = (struct i2c_device_addr *)demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ rc = drxj_dap_write_reg16(dev_addr, AUD_COMM_EXEC__A, AUD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ ext_attr->aud_data.audio_is_active = false;
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int set_orx_nsu_aox()
+* \brief Configure OrxNsuAox for OOB
+* \param demod instance of demodulator.
+* \param active
+* \return int.
+*/
+static int set_orx_nsu_aox(struct drx_demod_instance *demod, bool active)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ u16 data = 0;
+
+ /* Configure NSU_AOX */
+ rc = drxj_dap_read_reg16(dev_addr, ORX_NSU_AOX_STDBY_W__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (!active)
+ data &= ((~ORX_NSU_AOX_STDBY_W_STDBYADC_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYPD_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_ON));
+ else
+ data |= (ORX_NSU_AOX_STDBY_W_STDBYADC_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYPD_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_ON);
+ rc = drxj_dap_write_reg16(dev_addr, ORX_NSU_AOX_STDBY_W__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/**
+* \fn int ctrl_set_oob()
+* \brief Set OOB channel to be used.
+* \param demod instance of demodulator
+* \param oob_param OOB parameters for channel setting.
+* \frequency should be in KHz
+* \return int.
+*
+* Accepts only. Returns error otherwise.
+* Demapper value is written after scu_command START
+* because START command causes COMM_EXEC transition
+* from 0 to 1 which causes all registers to be
+* overwritten with initial value
+*
+*/
+
+/* Nyquist filter impulse response */
+#define IMPULSE_COSINE_ALPHA_0_3 {-3, -4, -1, 6, 10, 7, -5, -20, -25, -10, 29, 79, 123, 140} /*sqrt raised-cosine filter with alpha=0.3 */
+#define IMPULSE_COSINE_ALPHA_0_5 { 2, 0, -2, -2, 2, 5, 2, -10, -20, -14, 20, 74, 125, 145} /*sqrt raised-cosine filter with alpha=0.5 */
+#define IMPULSE_COSINE_ALPHA_RO_0_5 { 0, 0, 1, 2, 3, 0, -7, -15, -16, 0, 34, 77, 114, 128} /*full raised-cosine filter with alpha=0.5 (receiver only) */
+
+/* Coefficients for the nyquist fitler (total: 27 taps) */
+#define NYQFILTERLEN 27
+
+static int ctrl_set_oob(struct drx_demod_instance *demod, struct drxoob *oob_param)
+{
+ int rc;
+ s32 freq = 0; /* KHz */
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ u16 i = 0;
+ bool mirror_freq_spect_oob = false;
+ u16 trk_filter_value = 0;
+ struct drxjscu_cmd scu_cmd;
+ u16 set_param_parameters[3];
+ u16 cmd_result[2] = { 0, 0 };
+ s16 nyquist_coeffs[4][(NYQFILTERLEN + 1) / 2] = {
+ IMPULSE_COSINE_ALPHA_0_3, /* Target Mode 0 */
+ IMPULSE_COSINE_ALPHA_0_3, /* Target Mode 1 */
+ IMPULSE_COSINE_ALPHA_0_5, /* Target Mode 2 */
+ IMPULSE_COSINE_ALPHA_RO_0_5 /* Target Mode 3 */
+ };
+ u8 mode_val[4] = { 2, 2, 0, 1 };
+ u8 pfi_coeffs[4][6] = {
+ {DRXJ_16TO8(-92), DRXJ_16TO8(-108), DRXJ_16TO8(100)}, /* TARGET_MODE = 0: PFI_A = -23/32; PFI_B = -54/32; PFI_C = 25/32; fg = 0.5 MHz (Att=26dB) */
+ {DRXJ_16TO8(-64), DRXJ_16TO8(-80), DRXJ_16TO8(80)}, /* TARGET_MODE = 1: PFI_A = -16/32; PFI_B = -40/32; PFI_C = 20/32; fg = 1.0 MHz (Att=28dB) */
+ {DRXJ_16TO8(-80), DRXJ_16TO8(-98), DRXJ_16TO8(92)}, /* TARGET_MODE = 2, 3: PFI_A = -20/32; PFI_B = -49/32; PFI_C = 23/32; fg = 0.8 MHz (Att=25dB) */
+ {DRXJ_16TO8(-80), DRXJ_16TO8(-98), DRXJ_16TO8(92)} /* TARGET_MODE = 2, 3: PFI_A = -20/32; PFI_B = -49/32; PFI_C = 23/32; fg = 0.8 MHz (Att=25dB) */
+ };
+ u16 mode_index;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ mirror_freq_spect_oob = ext_attr->mirror_freq_spect_oob;
+
+ /* Check parameters */
+ if (oob_param == NULL) {
+ /* power off oob module */
+ scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB
+ | SCU_RAM_COMMAND_CMD_DEMOD_STOP;
+ scu_cmd.parameter_len = 0;
+ scu_cmd.result_len = 1;
+ scu_cmd.result = cmd_result;
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_orx_nsu_aox(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_COMM_EXEC__A, ORX_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ ext_attr->oob_power_on = false;
+ return 0;
+ }
+
+ freq = oob_param->frequency;
+ if ((freq < 70000) || (freq > 130000))
+ return -EIO;
+ freq = (freq - 50000) / 50;
+
+ {
+ u16 index = 0;
+ u16 remainder = 0;
+ u16 *trk_filtercfg = ext_attr->oob_trk_filter_cfg;
+
+ index = (u16) ((freq - 400) / 200);
+ remainder = (u16) ((freq - 400) % 200);
+ trk_filter_value =
+ trk_filtercfg[index] - (trk_filtercfg[index] -
+ trk_filtercfg[index +
+ 1]) / 10 * remainder /
+ 20;
+ }
+
+ /*********/
+ /* Stop */
+ /*********/
+ rc = drxj_dap_write_reg16(dev_addr, ORX_COMM_EXEC__A, ORX_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB
+ | SCU_RAM_COMMAND_CMD_DEMOD_STOP;
+ scu_cmd.parameter_len = 0;
+ scu_cmd.result_len = 1;
+ scu_cmd.result = cmd_result;
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /*********/
+ /* Reset */
+ /*********/
+ scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB
+ | SCU_RAM_COMMAND_CMD_DEMOD_RESET;
+ scu_cmd.parameter_len = 0;
+ scu_cmd.result_len = 1;
+ scu_cmd.result = cmd_result;
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /***********/
+ /* SET_ENV */
+ /***********/
+ /* set frequency, spectrum inversion and data rate */
+ scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV;
+ scu_cmd.parameter_len = 3;
+ /* 1-data rate;2-frequency */
+ switch (oob_param->standard) {
+ case DRX_OOB_MODE_A:
+ if (
+ /* signal is transmitted inverted */
+ ((oob_param->spectrum_inverted == true) &&
+ /* and tuner is not mirroring the signal */
+ (!mirror_freq_spect_oob)) |
+ /* or */
+ /* signal is transmitted noninverted */
+ ((oob_param->spectrum_inverted == false) &&
+ /* and tuner is mirroring the signal */
+ (mirror_freq_spect_oob))
+ )
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_INVSPEC;
+ else
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_REGSPEC;
+ break;
+ case DRX_OOB_MODE_B_GRADE_A:
+ if (
+ /* signal is transmitted inverted */
+ ((oob_param->spectrum_inverted == true) &&
+ /* and tuner is not mirroring the signal */
+ (!mirror_freq_spect_oob)) |
+ /* or */
+ /* signal is transmitted noninverted */
+ ((oob_param->spectrum_inverted == false) &&
+ /* and tuner is mirroring the signal */
+ (mirror_freq_spect_oob))
+ )
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_INVSPEC;
+ else
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_REGSPEC;
+ break;
+ case DRX_OOB_MODE_B_GRADE_B:
+ default:
+ if (
+ /* signal is transmitted inverted */
+ ((oob_param->spectrum_inverted == true) &&
+ /* and tuner is not mirroring the signal */
+ (!mirror_freq_spect_oob)) |
+ /* or */
+ /* signal is transmitted noninverted */
+ ((oob_param->spectrum_inverted == false) &&
+ /* and tuner is mirroring the signal */
+ (mirror_freq_spect_oob))
+ )
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_INVSPEC;
+ else
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_REGSPEC;
+ break;
+ }
+ set_param_parameters[1] = (u16) (freq & 0xFFFF);
+ set_param_parameters[2] = trk_filter_value;
+ scu_cmd.parameter = set_param_parameters;
+ scu_cmd.result_len = 1;
+ scu_cmd.result = cmd_result;
+ mode_index = mode_val[(set_param_parameters[0] & 0xC0) >> 6];
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0xFABA, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_OOB_CRX_CFG__A, OOB_CRX_DRIVE_STRENGTH << SIO_PDR_OOB_CRX_CFG_DRIVE__B | 0x03 << SIO_PDR_OOB_CRX_CFG_MODE__B, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_OOB_DRX_CFG__A, OOB_DRX_DRIVE_STRENGTH << SIO_PDR_OOB_DRX_CFG_DRIVE__B | 0x03 << SIO_PDR_OOB_DRX_CFG_MODE__B, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* Write magic word to disable pdr reg write */
+
+ rc = drxj_dap_write_reg16(dev_addr, ORX_TOP_COMM_KEY__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_AAG_LEN_W__A, 16000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_AAG_THR_W__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* ddc */
+ rc = drxj_dap_write_reg16(dev_addr, ORX_DDC_OFO_SET_W__A, ORX_DDC_OFO_SET_W__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* nsu */
+ rc = drxj_dap_write_reg16(dev_addr, ORX_NSU_AOX_LOPOW_W__A, ext_attr->oob_lo_pow, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* initialization for target mode */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TARGET_MODE__A, SCU_RAM_ORX_TARGET_MODE_2048KBPS_SQRT, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FREQ_GAIN_CORR__A, SCU_RAM_ORX_FREQ_GAIN_CORR_2048KBPS, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Reset bits for timing and freq. recovery */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_CPH__A, 0x0001, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_CTI__A, 0x0002, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_KRN__A, 0x0004, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_KRP__A, 0x0008, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* AGN_LOCK = {2048>>3, -2048, 8, -8, 0, 1}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_LOCK_TH__A, 2048 >> 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_ONLOCK_TTH__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_UNLOCK_TTH__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_LOCK_MASK__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* DGN_LOCK = {10, -2048, 8, -8, 0, 1<<1}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_LOCK_TH__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_ONLOCK_TTH__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_UNLOCK_TTH__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_LOCK_MASK__A, 1 << 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* FRQ_LOCK = {15,-2048, 8, -8, 0, 1<<2}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_LOCK_TH__A, 17, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_ONLOCK_TTH__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_UNLOCK_TTH__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_LOCK_MASK__A, 1 << 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* PHA_LOCK = {5000, -2048, 8, -8, 0, 1<<3}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_LOCK_TH__A, 3000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_ONLOCK_TTH__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_UNLOCK_TTH__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_LOCK_MASK__A, 1 << 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* TIM_LOCK = {300, -2048, 8, -8, 0, 1<<4}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_LOCK_TH__A, 400, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_ONLOCK_TTH__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_UNLOCK_TTH__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_LOCK_MASK__A, 1 << 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* EQU_LOCK = {20, -2048, 8, -8, 0, 1<<5}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_LOCK_TH__A, 20, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_ONLOCK_TTH__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_UNLOCK_TTH__A, (u16)(-4), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_LOCK_MASK__A, 1 << 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* PRE-Filter coefficients (PFI) */
+ rc = drxdap_fasi_write_block(dev_addr, ORX_FWP_PFI_A_W__A, sizeof(pfi_coeffs[mode_index]), ((u8 *)pfi_coeffs[mode_index]), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_TOP_MDE_W__A, mode_index, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* NYQUIST-Filter coefficients (NYQ) */
+ for (i = 0; i < (NYQFILTERLEN + 1) / 2; i++) {
+ rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_NYQ_ADR_W__A, i, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_NYQ_COF_RW__A, nyquist_coeffs[mode_index][i], 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_NYQ_ADR_W__A, 31, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_COMM_EXEC__A, ORX_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /*********/
+ /* Start */
+ /*********/
+ scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB
+ | SCU_RAM_COMMAND_CMD_DEMOD_START;
+ scu_cmd.parameter_len = 0;
+ scu_cmd.result_len = 1;
+ scu_cmd.result = cmd_result;
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = set_orx_nsu_aox(demod, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_NSU_AOX_STHR_W__A, ext_attr->oob_pre_saw, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ ext_attr->oob_power_on = true;
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+/*== END OOB DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+
+/*=============================================================================
+ ===== MC command related functions ==========================================
+ ===========================================================================*/
+
+/*=============================================================================
+ ===== ctrl_set_channel() ==========================================================
+ ===========================================================================*/
+/**
+* \fn int ctrl_set_channel()
+* \brief Select a new transmission channel.
+* \param demod instance of demod.
+* \param channel Pointer to channel data.
+* \return int.
+*
+* In case the tuner module is not used and in case of NTSC/FM the pogrammer
+* must tune the tuner to the centre frequency of the NTSC/FM channel.
+*
+*/
+static int
+ctrl_set_channel(struct drx_demod_instance *demod, struct drx_channel *channel)
+{
+ int rc;
+ s32 tuner_freq_offset = 0;
+ struct drxj_data *ext_attr = NULL;
+ struct i2c_device_addr *dev_addr = NULL;
+ enum drx_standard standard = DRX_STANDARD_UNKNOWN;
+#ifndef DRXJ_VSB_ONLY
+ u32 min_symbol_rate = 0;
+ u32 max_symbol_rate = 0;
+ int bandwidth_temp = 0;
+ int bandwidth = 0;
+#endif
+ /*== check arguments ======================================================*/
+ if ((demod == NULL) || (channel == NULL))
+ return -EINVAL;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ standard = ext_attr->standard;
+
+ /* check valid standards */
+ switch (standard) {
+ case DRX_STANDARD_8VSB:
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+#endif /* DRXJ_VSB_ONLY */
+ break;
+ case DRX_STANDARD_UNKNOWN:
+ default:
+ return -EINVAL;
+ }
+
+ /* check bandwidth QAM annex B, NTSC and 8VSB */
+ if ((standard == DRX_STANDARD_ITU_B) ||
+ (standard == DRX_STANDARD_8VSB) ||
+ (standard == DRX_STANDARD_NTSC)) {
+ switch (channel->bandwidth) {
+ case DRX_BANDWIDTH_6MHZ:
+ case DRX_BANDWIDTH_UNKNOWN: /* fall through */
+ channel->bandwidth = DRX_BANDWIDTH_6MHZ;
+ break;
+ case DRX_BANDWIDTH_8MHZ: /* fall through */
+ case DRX_BANDWIDTH_7MHZ: /* fall through */
+ default:
+ return -EINVAL;
+ }
+ }
+
+ /* For QAM annex A and annex C:
+ -check symbolrate and constellation
+ -derive bandwidth from symbolrate (input bandwidth is ignored)
+ */
+#ifndef DRXJ_VSB_ONLY
+ if ((standard == DRX_STANDARD_ITU_A) ||
+ (standard == DRX_STANDARD_ITU_C)) {
+ struct drxuio_cfg uio_cfg = { DRX_UIO1, DRX_UIO_MODE_FIRMWARE_SAW };
+ int bw_rolloff_factor = 0;
+
+ bw_rolloff_factor = (standard == DRX_STANDARD_ITU_A) ? 115 : 113;
+ min_symbol_rate = DRXJ_QAM_SYMBOLRATE_MIN;
+ max_symbol_rate = DRXJ_QAM_SYMBOLRATE_MAX;
+ /* config SMA_TX pin to SAW switch mode */
+ rc = ctrl_set_uio_cfg(demod, &uio_cfg);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (channel->symbolrate < min_symbol_rate ||
+ channel->symbolrate > max_symbol_rate) {
+ return -EINVAL;
+ }
+
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM16: /* fall through */
+ case DRX_CONSTELLATION_QAM32: /* fall through */
+ case DRX_CONSTELLATION_QAM64: /* fall through */
+ case DRX_CONSTELLATION_QAM128: /* fall through */
+ case DRX_CONSTELLATION_QAM256:
+ bandwidth_temp = channel->symbolrate * bw_rolloff_factor;
+ bandwidth = bandwidth_temp / 100;
+
+ if ((bandwidth_temp % 100) >= 50)
+ bandwidth++;
+
+ if (bandwidth <= 6100000) {
+ channel->bandwidth = DRX_BANDWIDTH_6MHZ;
+ } else if ((bandwidth > 6100000)
+ && (bandwidth <= 7100000)) {
+ channel->bandwidth = DRX_BANDWIDTH_7MHZ;
+ } else if (bandwidth > 7100000) {
+ channel->bandwidth = DRX_BANDWIDTH_8MHZ;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ /* For QAM annex B:
+ -check constellation
+ */
+ if (standard == DRX_STANDARD_ITU_B) {
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_AUTO:
+ case DRX_CONSTELLATION_QAM256:
+ case DRX_CONSTELLATION_QAM64:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (channel->interleavemode) {
+ case DRX_INTERLEAVEMODE_I128_J1:
+ case DRX_INTERLEAVEMODE_I128_J1_V2:
+ case DRX_INTERLEAVEMODE_I128_J2:
+ case DRX_INTERLEAVEMODE_I64_J2:
+ case DRX_INTERLEAVEMODE_I128_J3:
+ case DRX_INTERLEAVEMODE_I32_J4:
+ case DRX_INTERLEAVEMODE_I128_J4:
+ case DRX_INTERLEAVEMODE_I16_J8:
+ case DRX_INTERLEAVEMODE_I128_J5:
+ case DRX_INTERLEAVEMODE_I8_J16:
+ case DRX_INTERLEAVEMODE_I128_J6:
+ case DRX_INTERLEAVEMODE_I128_J7:
+ case DRX_INTERLEAVEMODE_I128_J8:
+ case DRX_INTERLEAVEMODE_I12_J17:
+ case DRX_INTERLEAVEMODE_I5_J4:
+ case DRX_INTERLEAVEMODE_B52_M240:
+ case DRX_INTERLEAVEMODE_B52_M720:
+ case DRX_INTERLEAVEMODE_UNKNOWN:
+ case DRX_INTERLEAVEMODE_AUTO:
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ if ((ext_attr->uio_sma_tx_mode) == DRX_UIO_MODE_FIRMWARE_SAW) {
+ /* SAW SW, user UIO is used for switchable SAW */
+ struct drxuio_data uio1 = { DRX_UIO1, false };
+
+ switch (channel->bandwidth) {
+ case DRX_BANDWIDTH_8MHZ:
+ uio1.value = true;
+ break;
+ case DRX_BANDWIDTH_7MHZ:
+ uio1.value = false;
+ break;
+ case DRX_BANDWIDTH_6MHZ:
+ uio1.value = false;
+ break;
+ case DRX_BANDWIDTH_UNKNOWN:
+ default:
+ return -EINVAL;
+ }
+
+ rc = ctrl_uio_write(demod, &uio1);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+#endif /* DRXJ_VSB_ONLY */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ tuner_freq_offset = 0;
+
+ /*== Setup demod for specific standard ====================================*/
+ switch (standard) {
+ case DRX_STANDARD_8VSB:
+ if (channel->mirror == DRX_MIRROR_AUTO)
+ ext_attr->mirror = DRX_MIRROR_NO;
+ else
+ ext_attr->mirror = channel->mirror;
+ rc = set_vsb(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_frequency(demod, channel, tuner_freq_offset);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A: /* fallthrough */
+ case DRX_STANDARD_ITU_B: /* fallthrough */
+ case DRX_STANDARD_ITU_C:
+ rc = set_qam_channel(demod, channel, tuner_freq_offset);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+#endif
+ case DRX_STANDARD_UNKNOWN:
+ default:
+ return -EIO;
+ }
+
+ /* flag the packet error counter reset */
+ ext_attr->reset_pkt_err_acc = true;
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*=============================================================================
+ ===== SigQuality() ==========================================================
+ ===========================================================================*/
+
+/**
+* \fn int ctrl_sig_quality()
+* \brief Retreive signal quality form device.
+* \param devmod Pointer to demodulator instance.
+* \param sig_quality Pointer to signal quality data.
+* \return int.
+* \retval 0 sig_quality contains valid data.
+* \retval -EINVAL sig_quality is NULL.
+* \retval -EIO Erroneous data, sig_quality contains invalid data.
+
+*/
+static int
+ctrl_sig_quality(struct drx_demod_instance *demod,
+ enum drx_lock_status lock_status)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ struct drx39xxj_state *state = dev_addr->user_data;
+ struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache;
+ enum drx_standard standard = ext_attr->standard;
+ int rc;
+ u32 ber, cnt, err, pkt;
+ u16 mer, strength;
+
+ rc = get_sig_strength(demod, &strength);
+ if (rc < 0) {
+ pr_err("error getting signal strength %d\n", rc);
+ p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ p->strength.stat[0].uvalue = 65535UL * strength/ 100;
+ }
+
+ switch (standard) {
+ case DRX_STANDARD_8VSB:
+#ifdef DRXJ_SIGNAL_ACCUM_ERR
+ rc = get_acc_pkt_err(demod, &pkt);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+#endif
+ if (lock_status != DRXJ_DEMOD_LOCK && lock_status != DRX_LOCKED) {
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ rc = get_vsb_post_rs_pck_err(dev_addr, &err, &pkt);
+ if (rc != 0) {
+ pr_err("error %d getting UCB\n", rc);
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_error.stat[0].uvalue += err;
+ p->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_count.stat[0].uvalue += pkt;
+ }
+
+ /* PostViterbi is compute in steps of 10^(-6) */
+ rc = get_vs_bpre_viterbi_ber(dev_addr, &ber, &cnt);
+ if (rc != 0) {
+ pr_err("error %d getting pre-ber\n", rc);
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->pre_bit_error.stat[0].uvalue += ber;
+ p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->pre_bit_count.stat[0].uvalue += cnt;
+ }
+
+ rc = get_vs_bpost_viterbi_ber(dev_addr, &ber, &cnt);
+ if (rc != 0) {
+ pr_err("error %d getting post-ber\n", rc);
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_error.stat[0].uvalue += ber;
+ p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_count.stat[0].uvalue += cnt;
+ }
+ rc = get_vsbmer(dev_addr, &mer);
+ if (rc != 0) {
+ pr_err("error %d getting MER\n", rc);
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ p->cnr.stat[0].svalue = mer * 100;
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ }
+ }
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ rc = ctrl_get_qam_sig_quality(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+#endif
+ default:
+ return -EIO;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+* \fn int ctrl_lock_status()
+* \brief Retreive lock status .
+* \param dev_addr Pointer to demodulator device address.
+* \param lock_stat Pointer to lock status structure.
+* \return int.
+*
+*/
+static int
+ctrl_lock_status(struct drx_demod_instance *demod, enum drx_lock_status *lock_stat)
+{
+ enum drx_standard standard = DRX_STANDARD_UNKNOWN;
+ struct drxj_data *ext_attr = NULL;
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxjscu_cmd cmd_scu = { /* command */ 0,
+ /* parameter_len */ 0,
+ /* result_len */ 0,
+ /* *parameter */ NULL,
+ /* *result */ NULL
+ };
+ int rc;
+ u16 cmd_result[2] = { 0, 0 };
+ u16 demod_lock = SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_DEMOD_LOCKED;
+
+ /* check arguments */
+ if ((demod == NULL) || (lock_stat == NULL))
+ return -EINVAL;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ standard = ext_attr->standard;
+
+ *lock_stat = DRX_NOT_LOCKED;
+
+ /* define the SCU command code */
+ switch (standard) {
+ case DRX_STANDARD_8VSB:
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB |
+ SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK;
+ demod_lock |= 0x6;
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK;
+ break;
+#endif
+ case DRX_STANDARD_UNKNOWN: /* fallthrough */
+ default:
+ return -EIO;
+ }
+
+ /* define the SCU command paramters and execute the command */
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 2;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* set the lock status */
+ if (cmd_scu.result[1] < demod_lock) {
+ /* 0x0000 NOT LOCKED */
+ *lock_stat = DRX_NOT_LOCKED;
+ } else if (cmd_scu.result[1] < SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_LOCKED) {
+ *lock_stat = DRXJ_DEMOD_LOCK;
+ } else if (cmd_scu.result[1] <
+ SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_NEVER_LOCK) {
+ /* 0x8000 DEMOD + FEC LOCKED (system lock) */
+ *lock_stat = DRX_LOCKED;
+ } else {
+ /* 0xC000 NEVER LOCKED */
+ /* (system will never be able to lock to the signal) */
+ *lock_stat = DRX_NEVER_LOCK;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+* \fn int ctrl_set_standard()
+* \brief Set modulation standard to be used.
+* \param standard Modulation standard.
+* \return int.
+*
+* Setup stuff for the desired demodulation standard.
+* Disable and power down the previous selected demodulation standard
+*
+*/
+static int
+ctrl_set_standard(struct drx_demod_instance *demod, enum drx_standard *standard)
+{
+ struct drxj_data *ext_attr = NULL;
+ int rc;
+ enum drx_standard prev_standard;
+
+ /* check arguments */
+ if ((standard == NULL) || (demod == NULL))
+ return -EINVAL;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ prev_standard = ext_attr->standard;
+
+ /*
+ Stop and power down previous standard
+ */
+ switch (prev_standard) {
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A: /* fallthrough */
+ case DRX_STANDARD_ITU_B: /* fallthrough */
+ case DRX_STANDARD_ITU_C:
+ rc = power_down_qam(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+#endif
+ case DRX_STANDARD_8VSB:
+ rc = power_down_vsb(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_STANDARD_UNKNOWN:
+ /* Do nothing */
+ break;
+ case DRX_STANDARD_AUTO: /* fallthrough */
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ Initialize channel independent registers
+ Power up new standard
+ */
+ ext_attr->standard = *standard;
+
+ switch (*standard) {
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A: /* fallthrough */
+ case DRX_STANDARD_ITU_B: /* fallthrough */
+ case DRX_STANDARD_ITU_C:
+ do {
+ u16 dummy;
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SCU_RAM_VERSION_HI__A, &dummy, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } while (0);
+ break;
+#endif
+ case DRX_STANDARD_8VSB:
+ rc = set_vsb_leak_n_gain(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ ext_attr->standard = DRX_STANDARD_UNKNOWN;
+ return -EINVAL;
+ break;
+ }
+
+ return 0;
+rw_error:
+ /* Don't know what the standard is now ... try again */
+ ext_attr->standard = DRX_STANDARD_UNKNOWN;
+ return -EIO;
+}
+
+/*============================================================================*/
+
+static void drxj_reset_mode(struct drxj_data *ext_attr)
+{
+ /* Initialize default AFE configuartion for QAM */
+ if (ext_attr->has_lna) {
+ /* IF AGC off, PGA active */
+#ifndef DRXJ_VSB_ONLY
+ ext_attr->qam_if_agc_cfg.standard = DRX_STANDARD_ITU_B;
+ ext_attr->qam_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_OFF;
+ ext_attr->qam_pga_cfg = 140 + (11 * 13);
+#endif
+ ext_attr->vsb_if_agc_cfg.standard = DRX_STANDARD_8VSB;
+ ext_attr->vsb_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_OFF;
+ ext_attr->vsb_pga_cfg = 140 + (11 * 13);
+ } else {
+ /* IF AGC on, PGA not active */
+#ifndef DRXJ_VSB_ONLY
+ ext_attr->qam_if_agc_cfg.standard = DRX_STANDARD_ITU_B;
+ ext_attr->qam_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO;
+ ext_attr->qam_if_agc_cfg.min_output_level = 0;
+ ext_attr->qam_if_agc_cfg.max_output_level = 0x7FFF;
+ ext_attr->qam_if_agc_cfg.speed = 3;
+ ext_attr->qam_if_agc_cfg.top = 1297;
+ ext_attr->qam_pga_cfg = 140;
+#endif
+ ext_attr->vsb_if_agc_cfg.standard = DRX_STANDARD_8VSB;
+ ext_attr->vsb_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO;
+ ext_attr->vsb_if_agc_cfg.min_output_level = 0;
+ ext_attr->vsb_if_agc_cfg.max_output_level = 0x7FFF;
+ ext_attr->vsb_if_agc_cfg.speed = 3;
+ ext_attr->vsb_if_agc_cfg.top = 1024;
+ ext_attr->vsb_pga_cfg = 140;
+ }
+ /* TODO: remove min_output_level and max_output_level for both QAM and VSB after */
+ /* mc has not used them */
+#ifndef DRXJ_VSB_ONLY
+ ext_attr->qam_rf_agc_cfg.standard = DRX_STANDARD_ITU_B;
+ ext_attr->qam_rf_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO;
+ ext_attr->qam_rf_agc_cfg.min_output_level = 0;
+ ext_attr->qam_rf_agc_cfg.max_output_level = 0x7FFF;
+ ext_attr->qam_rf_agc_cfg.speed = 3;
+ ext_attr->qam_rf_agc_cfg.top = 9500;
+ ext_attr->qam_rf_agc_cfg.cut_off_current = 4000;
+ ext_attr->qam_pre_saw_cfg.standard = DRX_STANDARD_ITU_B;
+ ext_attr->qam_pre_saw_cfg.reference = 0x07;
+ ext_attr->qam_pre_saw_cfg.use_pre_saw = true;
+#endif
+ /* Initialize default AFE configuartion for VSB */
+ ext_attr->vsb_rf_agc_cfg.standard = DRX_STANDARD_8VSB;
+ ext_attr->vsb_rf_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO;
+ ext_attr->vsb_rf_agc_cfg.min_output_level = 0;
+ ext_attr->vsb_rf_agc_cfg.max_output_level = 0x7FFF;
+ ext_attr->vsb_rf_agc_cfg.speed = 3;
+ ext_attr->vsb_rf_agc_cfg.top = 9500;
+ ext_attr->vsb_rf_agc_cfg.cut_off_current = 4000;
+ ext_attr->vsb_pre_saw_cfg.standard = DRX_STANDARD_8VSB;
+ ext_attr->vsb_pre_saw_cfg.reference = 0x07;
+ ext_attr->vsb_pre_saw_cfg.use_pre_saw = true;
+}
+
+/**
+* \fn int ctrl_power_mode()
+* \brief Set the power mode of the device to the specified power mode
+* \param demod Pointer to demodulator instance.
+* \param mode Pointer to new power mode.
+* \return int.
+* \retval 0 Success
+* \retval -EIO I2C error or other failure
+* \retval -EINVAL Invalid mode argument.
+*
+*
+*/
+static int
+ctrl_power_mode(struct drx_demod_instance *demod, enum drx_power_mode *mode)
+{
+ struct drx_common_attr *common_attr = (struct drx_common_attr *) NULL;
+ struct drxj_data *ext_attr = (struct drxj_data *) NULL;
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)NULL;
+ int rc;
+ u16 sio_cc_pwd_mode = 0;
+
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+
+ /* Check arguments */
+ if (mode == NULL)
+ return -EINVAL;
+
+ /* If already in requested power mode, do nothing */
+ if (common_attr->current_power_mode == *mode)
+ return 0;
+
+ switch (*mode) {
+ case DRX_POWER_UP:
+ case DRXJ_POWER_DOWN_MAIN_PATH:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_NONE;
+ break;
+ case DRXJ_POWER_DOWN_CORE:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_CLOCK;
+ break;
+ case DRXJ_POWER_DOWN_PLL:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_PLL;
+ break;
+ case DRX_POWER_DOWN:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OSC;
+ break;
+ default:
+ /* Unknow sleep mode */
+ return -EINVAL;
+ break;
+ }
+
+ /* Check if device needs to be powered up */
+ if ((common_attr->current_power_mode != DRX_POWER_UP)) {
+ rc = power_up_device(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ if ((*mode == DRX_POWER_UP)) {
+ /* Restore analog & pin configuartion */
+
+ /* Initialize default AFE configuartion for VSB */
+ drxj_reset_mode(ext_attr);
+ } else {
+ /* Power down to requested mode */
+ /* Backup some register settings */
+ /* Set pins with possible pull-ups connected to them in input mode */
+ /* Analog power down */
+ /* ADC power down */
+ /* Power down device */
+ /* stop all comm_exec */
+ /*
+ Stop and power down previous standard
+ */
+
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ rc = power_down_qam(demod, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_STANDARD_8VSB:
+ rc = power_down_vsb(demod, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_STANDARD_PAL_SECAM_BG: /* fallthrough */
+ case DRX_STANDARD_PAL_SECAM_DK: /* fallthrough */
+ case DRX_STANDARD_PAL_SECAM_I: /* fallthrough */
+ case DRX_STANDARD_PAL_SECAM_L: /* fallthrough */
+ case DRX_STANDARD_PAL_SECAM_LP: /* fallthrough */
+ case DRX_STANDARD_NTSC: /* fallthrough */
+ case DRX_STANDARD_FM:
+ rc = power_down_atv(demod, ext_attr->standard, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_STANDARD_UNKNOWN:
+ /* Do nothing */
+ break;
+ case DRX_STANDARD_AUTO: /* fallthrough */
+ default:
+ return -EIO;
+ }
+ ext_attr->standard = DRX_STANDARD_UNKNOWN;
+ }
+
+ if (*mode != DRXJ_POWER_DOWN_MAIN_PATH) {
+ rc = drxj_dap_write_reg16(dev_addr, SIO_CC_PWD_MODE__A, sio_cc_pwd_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if ((*mode != DRX_POWER_UP)) {
+ /* Initialize HI, wakeup key especially before put IC to sleep */
+ rc = init_hi(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ ext_attr->hi_cfg_ctrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+ rc = hi_cfg_command(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ }
+
+ common_attr->current_power_mode = *mode;
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+/*== CTRL Set/Get Config related functions ===================================*/
+/*============================================================================*/
+
+/**
+* \fn int ctrl_set_cfg_pre_saw()
+* \brief Set Pre-saw reference.
+* \param demod demod instance
+* \param u16 *
+* \return int.
+*
+* Check arguments
+* Dispatch handling to standard specific function.
+*
+*/
+static int
+ctrl_set_cfg_pre_saw(struct drx_demod_instance *demod, struct drxj_cfg_pre_saw *pre_saw)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ int rc;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* check arguments */
+ if ((pre_saw == NULL) || (pre_saw->reference > IQM_AF_PDREF__M)
+ ) {
+ return -EINVAL;
+ }
+
+ /* Only if standard is currently active */
+ if ((ext_attr->standard == pre_saw->standard) ||
+ (DRXJ_ISQAMSTD(ext_attr->standard) &&
+ DRXJ_ISQAMSTD(pre_saw->standard)) ||
+ (DRXJ_ISATVSTD(ext_attr->standard) &&
+ DRXJ_ISATVSTD(pre_saw->standard))) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PDREF__A, pre_saw->reference, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* Store pre-saw settings */
+ switch (pre_saw->standard) {
+ case DRX_STANDARD_8VSB:
+ ext_attr->vsb_pre_saw_cfg = *pre_saw;
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A: /* fallthrough */
+ case DRX_STANDARD_ITU_B: /* fallthrough */
+ case DRX_STANDARD_ITU_C:
+ ext_attr->qam_pre_saw_cfg = *pre_saw;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+/**
+* \fn int ctrl_set_cfg_afe_gain()
+* \brief Set AFE Gain.
+* \param demod demod instance
+* \param u16 *
+* \return int.
+*
+* Check arguments
+* Dispatch handling to standard specific function.
+*
+*/
+static int
+ctrl_set_cfg_afe_gain(struct drx_demod_instance *demod, struct drxj_cfg_afe_gain *afe_gain)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ int rc;
+ u8 gain = 0;
+
+ /* check arguments */
+ if (afe_gain == NULL)
+ return -EINVAL;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ switch (afe_gain->standard) {
+ case DRX_STANDARD_8VSB: /* fallthrough */
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A: /* fallthrough */
+ case DRX_STANDARD_ITU_B: /* fallthrough */
+ case DRX_STANDARD_ITU_C:
+#endif
+ /* Do nothing */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* TODO PGA gain is also written by microcode (at least by QAM and VSB)
+ So I (PJ) think interface requires choice between auto, user mode */
+
+ if (afe_gain->gain >= 329)
+ gain = 15;
+ else if (afe_gain->gain <= 147)
+ gain = 0;
+ else
+ gain = (afe_gain->gain - 140 + 6) / 13;
+
+ /* Only if standard is currently active */
+ if (ext_attr->standard == afe_gain->standard) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PGA_GAIN__A, gain, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* Store AFE Gain settings */
+ switch (afe_gain->standard) {
+ case DRX_STANDARD_8VSB:
+ ext_attr->vsb_pga_cfg = gain * 13 + 140;
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A: /* fallthrough */
+ case DRX_STANDARD_ITU_B: /* fallthrough */
+ case DRX_STANDARD_ITU_C:
+ ext_attr->qam_pga_cfg = gain * 13 + 140;
+ break;
+#endif
+ default:
+ return -EIO;
+ }
+
+ return 0;
+rw_error:
+ return -EIO;
+}
+
+/*============================================================================*/
+
+
+/*=============================================================================
+===== EXPORTED FUNCTIONS ====================================================*/
+
+static int drx_ctrl_u_code(struct drx_demod_instance *demod,
+ struct drxu_code_info *mc_info,
+ enum drxu_code_action action);
+
+/**
+* \fn drxj_open()
+* \brief Open the demod instance, configure device, configure drxdriver
+* \return Status_t Return status.
+*
+* drxj_open() can be called with a NULL ucode image => no ucode upload.
+* This means that drxj_open() must NOT contain SCU commands or, in general,
+* rely on SCU or AUD ucode to be present.
+*
+*/
+
+static int drxj_open(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ struct drx_common_attr *common_attr = NULL;
+ u32 driver_version = 0;
+ struct drxu_code_info ucode_info;
+ struct drx_cfg_mpeg_output cfg_mpeg_output;
+ int rc;
+ enum drx_power_mode power_mode = DRX_POWER_UP;
+
+ if ((demod == NULL) ||
+ (demod->my_common_attr == NULL) ||
+ (demod->my_ext_attr == NULL) ||
+ (demod->my_i2c_dev_addr == NULL) ||
+ (demod->my_common_attr->is_opened)) {
+ return -EINVAL;
+ }
+
+ /* Check arguments */
+ if (demod->my_ext_attr == NULL)
+ return -EINVAL;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+
+ rc = ctrl_power_mode(demod, &power_mode);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (power_mode != DRX_POWER_UP) {
+ rc = -EINVAL;
+ pr_err("failed to powerup device\n");
+ goto rw_error;
+ }
+
+ /* has to be in front of setIqmAf and setOrxNsuAox */
+ rc = get_device_capabilities(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /*
+ * Soft reset of sys- and osc-clockdomain
+ *
+ * HACK: On windows, it writes a 0x07 here, instead of just 0x03.
+ * As we didn't load the firmware here yet, we should do the same.
+ * Btw, this is coherent with DRX-K, where we send reset codes
+ * for modulation (OFTM, in DRX-k), SYS and OSC clock domains.
+ */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_CC_SOFT_RST__A, (0x04 | SIO_CC_SOFT_RST_SYS__M | SIO_CC_SOFT_RST_OSC__M), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ msleep(1);
+
+ /* TODO first make sure that everything keeps working before enabling this */
+ /* PowerDownAnalogBlocks() */
+ rc = drxj_dap_write_reg16(dev_addr, ATV_TOP_STDBY__A, (~ATV_TOP_STDBY_CVBS_STDBY_A2_ACTIVE) | ATV_TOP_STDBY_SIF_STDBY_STANDBY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = set_iqm_af(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_orx_nsu_aox(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = init_hi(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* disable mpegoutput pins */
+ memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output));
+ cfg_mpeg_output.enable_mpeg_output = false;
+
+ rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Stop AUD Inform SetAudio it will need to do all setting */
+ rc = power_down_aud(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Stop SCU */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Upload microcode */
+ if (common_attr->microcode_file != NULL) {
+ /* Dirty trick to use common ucode upload & verify,
+ pretend device is already open */
+ common_attr->is_opened = true;
+ ucode_info.mc_file = common_attr->microcode_file;
+
+ if (DRX_ISPOWERDOWNMODE(demod->my_common_attr->current_power_mode)) {
+ pr_err("Should powerup before loading the firmware.");
+ return -EINVAL;
+ }
+
+ rc = drx_ctrl_u_code(demod, &ucode_info, UCODE_UPLOAD);
+ if (rc != 0) {
+ pr_err("error %d while uploading the firmware\n", rc);
+ goto rw_error;
+ }
+ if (common_attr->verify_microcode == true) {
+ rc = drx_ctrl_u_code(demod, &ucode_info, UCODE_VERIFY);
+ if (rc != 0) {
+ pr_err("error %d while verifying the firmware\n",
+ rc);
+ goto rw_error;
+ }
+ }
+ common_attr->is_opened = false;
+ }
+
+ /* Run SCU for a little while to initialize microcode version numbers */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Initialize scan timeout */
+ common_attr->scan_demod_lock_timeout = DRXJ_SCAN_TIMEOUT;
+ common_attr->scan_desired_lock = DRX_LOCKED;
+
+ drxj_reset_mode(ext_attr);
+ ext_attr->standard = DRX_STANDARD_UNKNOWN;
+
+ rc = smart_ant_init(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Stamp driver version number in SCU data RAM in BCD code
+ Done to enable field application engineers to retreive drxdriver version
+ via I2C from SCU RAM
+ */
+ driver_version = (VERSION_MAJOR / 100) % 10;
+ driver_version <<= 4;
+ driver_version += (VERSION_MAJOR / 10) % 10;
+ driver_version <<= 4;
+ driver_version += (VERSION_MAJOR % 10);
+ driver_version <<= 4;
+ driver_version += (VERSION_MINOR % 10);
+ driver_version <<= 4;
+ driver_version += (VERSION_PATCH / 1000) % 10;
+ driver_version <<= 4;
+ driver_version += (VERSION_PATCH / 100) % 10;
+ driver_version <<= 4;
+ driver_version += (VERSION_PATCH / 10) % 10;
+ driver_version <<= 4;
+ driver_version += (VERSION_PATCH % 10);
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_DRIVER_VER_HI__A, (u16)(driver_version >> 16), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_DRIVER_VER_LO__A, (u16)(driver_version & 0xFFFF), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = ctrl_set_oob(demod, NULL);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* refresh the audio data structure with default */
+ ext_attr->aud_data = drxj_default_aud_data_g;
+
+ demod->my_common_attr->is_opened = true;
+ return 0;
+rw_error:
+ common_attr->is_opened = false;
+ return -EIO;
+}
+
+/*============================================================================*/
+/**
+* \fn drxj_close()
+* \brief Close the demod instance, power down the device
+* \return Status_t Return status.
+*
+*/
+static int drxj_close(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ enum drx_power_mode power_mode = DRX_POWER_UP;
+
+ if ((demod->my_common_attr == NULL) ||
+ (demod->my_ext_attr == NULL) ||
+ (demod->my_i2c_dev_addr == NULL) ||
+ (!demod->my_common_attr->is_opened)) {
+ return -EINVAL;
+ }
+
+ /* power up */
+ rc = ctrl_power_mode(demod, &power_mode);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ power_mode = DRX_POWER_DOWN;
+ rc = ctrl_power_mode(demod, &power_mode);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ DRX_ATTR_ISOPENED(demod) = false;
+
+ return 0;
+rw_error:
+ DRX_ATTR_ISOPENED(demod) = false;
+
+ return -EIO;
+}
+
+/*
+ * Microcode related functions
+ */
+
+/**
+ * drx_u_code_compute_crc - Compute CRC of block of microcode data.
+ * @block_data: Pointer to microcode data.
+ * @nr_words: Size of microcode block (number of 16 bits words).
+ *
+ * returns The computed CRC residue.
+ */
+static u16 drx_u_code_compute_crc(u8 *block_data, u16 nr_words)
+{
+ u16 i = 0;
+ u16 j = 0;
+ u32 crc_word = 0;
+ u32 carry = 0;
+
+ while (i < nr_words) {
+ crc_word |= (u32)be16_to_cpu(*(u32 *)(block_data));
+ for (j = 0; j < 16; j++) {
+ crc_word <<= 1;
+ if (carry != 0)
+ crc_word ^= 0x80050000UL;
+ carry = crc_word & 0x80000000UL;
+ }
+ i++;
+ block_data += (sizeof(u16));
+ }
+ return (u16)(crc_word >> 16);
+}
+
+/**
+ * drx_check_firmware - checks if the loaded firmware is valid
+ *
+ * @demod: demod structure
+ * @mc_data: pointer to the start of the firmware
+ * @size: firmware size
+ */
+static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data,
+ unsigned size)
+{
+ struct drxu_code_block_hdr block_hdr;
+ int i;
+ unsigned count = 2 * sizeof(u16);
+ u32 mc_dev_type, mc_version, mc_base_version;
+ u16 mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data + sizeof(u16)));
+
+ /*
+ * Scan microcode blocks first for version info
+ * and firmware check
+ */
+
+ /* Clear version block */
+ DRX_ATTR_MCRECORD(demod).aux_type = 0;
+ DRX_ATTR_MCRECORD(demod).mc_dev_type = 0;
+ DRX_ATTR_MCRECORD(demod).mc_version = 0;
+ DRX_ATTR_MCRECORD(demod).mc_base_version = 0;
+
+ for (i = 0; i < mc_nr_of_blks; i++) {
+ if (count + 3 * sizeof(u16) + sizeof(u32) > size)
+ goto eof;
+
+ /* Process block header */
+ block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data + count));
+ count += sizeof(u32);
+ block_hdr.size = be16_to_cpu(*(u32 *)(mc_data + count));
+ count += sizeof(u16);
+ block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data + count));
+ count += sizeof(u16);
+ block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data + count));
+ count += sizeof(u16);
+
+ pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
+ count, block_hdr.addr, block_hdr.size, block_hdr.flags,
+ block_hdr.CRC);
+
+ if (block_hdr.flags & 0x8) {
+ u8 *auxblk = ((void *)mc_data) + block_hdr.addr;
+ u16 auxtype;
+
+ if (block_hdr.addr + sizeof(u16) > size)
+ goto eof;
+
+ auxtype = be16_to_cpu(*(u32 *)(auxblk));
+
+ /* Aux block. Check type */
+ if (DRX_ISMCVERTYPE(auxtype)) {
+ if (block_hdr.addr + 2 * sizeof(u16) + 2 * sizeof (u32) > size)
+ goto eof;
+
+ auxblk += sizeof(u16);
+ mc_dev_type = be32_to_cpu(*(u32 *)(auxblk));
+ auxblk += sizeof(u32);
+ mc_version = be32_to_cpu(*(u32 *)(auxblk));
+ auxblk += sizeof(u32);
+ mc_base_version = be32_to_cpu(*(u32 *)(auxblk));
+
+ DRX_ATTR_MCRECORD(demod).aux_type = auxtype;
+ DRX_ATTR_MCRECORD(demod).mc_dev_type = mc_dev_type;
+ DRX_ATTR_MCRECORD(demod).mc_version = mc_version;
+ DRX_ATTR_MCRECORD(demod).mc_base_version = mc_base_version;
+
+ pr_info("Firmware dev %x, ver %x, base ver %x\n",
+ mc_dev_type, mc_version, mc_base_version);
+
+ }
+ } else if (count + block_hdr.size * sizeof(u16) > size)
+ goto eof;
+
+ count += block_hdr.size * sizeof(u16);
+ }
+ return 0;
+eof:
+ pr_err("Firmware is truncated at pos %u/%u\n", count, size);
+ return -EINVAL;
+}
+
+/**
+ * drx_ctrl_u_code - Handle microcode upload or verify.
+ * @dev_addr: Address of device.
+ * @mc_info: Pointer to information about microcode data.
+ * @action: Either UCODE_UPLOAD or UCODE_VERIFY
+ *
+ * This function returns:
+ * 0:
+ * - In case of UCODE_UPLOAD: code is successfully uploaded.
+ * - In case of UCODE_VERIFY: image on device is equal to
+ * image provided to this control function.
+ * -EIO:
+ * - In case of UCODE_UPLOAD: I2C error.
+ * - In case of UCODE_VERIFY: I2C error or image on device
+ * is not equal to image provided to this control function.
+ * -EINVAL:
+ * - Invalid arguments.
+ * - Provided image is corrupt
+ */
+static int drx_ctrl_u_code(struct drx_demod_instance *demod,
+ struct drxu_code_info *mc_info,
+ enum drxu_code_action action)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ u16 i = 0;
+ u16 mc_nr_of_blks = 0;
+ u16 mc_magic_word = 0;
+ const u8 *mc_data_init = NULL;
+ u8 *mc_data = NULL;
+ unsigned size;
+ char *mc_file;
+
+ /* Check arguments */
+ if (!mc_info || !mc_info->mc_file)
+ return -EINVAL;
+
+ mc_file = mc_info->mc_file;
+
+ if (!demod->firmware) {
+ const struct firmware *fw = NULL;
+
+ rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent);
+ if (rc < 0) {
+ pr_err("Couldn't read firmware %s\n", mc_file);
+ return rc;
+ }
+ demod->firmware = fw;
+
+ if (demod->firmware->size < 2 * sizeof(u16)) {
+ rc = -EINVAL;
+ pr_err("Firmware is too short!\n");
+ goto release;
+ }
+
+ pr_info("Firmware %s, size %zu\n",
+ mc_file, demod->firmware->size);
+ }
+
+ mc_data_init = demod->firmware->data;
+ size = demod->firmware->size;
+
+ mc_data = (void *)mc_data_init;
+ /* Check data */
+ mc_magic_word = be16_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u16);
+ mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u16);
+
+ if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) {
+ rc = -EINVAL;
+ pr_err("Firmware magic word doesn't match\n");
+ goto release;
+ }
+
+ if (action == UCODE_UPLOAD) {
+ rc = drx_check_firmware(demod, (u8 *)mc_data_init, size);
+ if (rc)
+ goto release;
+ pr_info("Uploading firmware %s\n", mc_file);
+ } else {
+ pr_info("Verifying if firmware upload was ok.\n");
+ }
+
+ /* Process microcode blocks */
+ for (i = 0; i < mc_nr_of_blks; i++) {
+ struct drxu_code_block_hdr block_hdr;
+ u16 mc_block_nr_bytes = 0;
+
+ /* Process block header */
+ block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u32);
+ block_hdr.size = be16_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u16);
+ block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u16);
+ block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u16);
+
+ pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
+ (unsigned)(mc_data - mc_data_init), block_hdr.addr,
+ block_hdr.size, block_hdr.flags, block_hdr.CRC);
+
+ /* Check block header on:
+ - data larger than 64Kb
+ - if CRC enabled check CRC
+ */
+ if ((block_hdr.size > 0x7FFF) ||
+ (((block_hdr.flags & DRX_UCODE_CRC_FLAG) != 0) &&
+ (block_hdr.CRC != drx_u_code_compute_crc(mc_data, block_hdr.size)))
+ ) {
+ /* Wrong data ! */
+ rc = -EINVAL;
+ pr_err("firmware CRC is wrong\n");
+ goto release;
+ }
+
+ if (!block_hdr.size)
+ continue;
+
+ mc_block_nr_bytes = block_hdr.size * ((u16) sizeof(u16));
+
+ /* Perform the desired action */
+ switch (action) {
+ case UCODE_UPLOAD: /* Upload microcode */
+ if (drxdap_fasi_write_block(dev_addr,
+ block_hdr.addr,
+ mc_block_nr_bytes,
+ mc_data, 0x0000)) {
+ rc = -EIO;
+ pr_err("error writing firmware at pos %u\n",
+ (unsigned)(mc_data - mc_data_init));
+ goto release;
+ }
+ break;
+ case UCODE_VERIFY: { /* Verify uploaded microcode */
+ int result = 0;
+ u8 mc_data_buffer[DRX_UCODE_MAX_BUF_SIZE];
+ u32 bytes_to_comp = 0;
+ u32 bytes_left = mc_block_nr_bytes;
+ u32 curr_addr = block_hdr.addr;
+ u8 *curr_ptr = mc_data;
+
+ while (bytes_left != 0) {
+ if (bytes_left > DRX_UCODE_MAX_BUF_SIZE)
+ bytes_to_comp = DRX_UCODE_MAX_BUF_SIZE;
+ else
+ bytes_to_comp = bytes_left;
+
+ if (drxdap_fasi_read_block(dev_addr,
+ curr_addr,
+ (u16)bytes_to_comp,
+ (u8 *)mc_data_buffer,
+ 0x0000)) {
+ pr_err("error reading firmware at pos %u\n",
+ (unsigned)(mc_data - mc_data_init));
+ return -EIO;
+ }
+
+ result = memcmp(curr_ptr, mc_data_buffer,
+ bytes_to_comp);
+
+ if (result) {
+ pr_err("error verifying firmware at pos %u\n",
+ (unsigned)(mc_data - mc_data_init));
+ return -EIO;
+ }
+
+ curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2));
+ curr_ptr =&(curr_ptr[bytes_to_comp]);
+ bytes_left -=((u32) bytes_to_comp);
+ }
+ break;
+ }
+ default:
+ return -EINVAL;
+ break;
+
+ }
+ mc_data += mc_block_nr_bytes;
+ }
+
+ return 0;
+
+release:
+ release_firmware(demod->firmware);
+ demod->firmware = NULL;
+
+ return rc;
+}
+
+/*
+ * The Linux DVB Driver for Micronas DRX39xx family (drx3933j)
+ *
+ * Written by Devin Heitmueller <devin.heitmueller@kernellabs.com>
+ */
+
+static int drx39xxj_set_powerstate(struct dvb_frontend *fe, int enable)
+{
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+ int result;
+ enum drx_power_mode power_mode;
+
+ if (enable)
+ power_mode = DRX_POWER_UP;
+ else
+ power_mode = DRX_POWER_DOWN;
+
+ result = ctrl_power_mode(demod, &power_mode);
+ if (result != 0) {
+ pr_err("Power state change failed\n");
+ return 0;
+ }
+
+ return 0;
+}
+
+static int drx39xxj_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+ int result;
+ enum drx_lock_status lock_status;
+
+ *status = 0;
+
+ result = ctrl_lock_status(demod, &lock_status);
+ if (result != 0) {
+ pr_err("drx39xxj: could not get lock status!\n");
+ *status = 0;
+ }
+
+ switch (lock_status) {
+ case DRX_NEVER_LOCK:
+ *status = 0;
+ pr_err("drx says NEVER_LOCK\n");
+ break;
+ case DRX_NOT_LOCKED:
+ *status = 0;
+ break;
+ case DRX_LOCK_STATE_1:
+ case DRX_LOCK_STATE_2:
+ case DRX_LOCK_STATE_3:
+ case DRX_LOCK_STATE_4:
+ case DRX_LOCK_STATE_5:
+ case DRX_LOCK_STATE_6:
+ case DRX_LOCK_STATE_7:
+ case DRX_LOCK_STATE_8:
+ case DRX_LOCK_STATE_9:
+ *status = FE_HAS_SIGNAL
+ | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC;
+ break;
+ case DRX_LOCKED:
+ *status = FE_HAS_SIGNAL
+ | FE_HAS_CARRIER
+ | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ break;
+ default:
+ pr_err("Lock state unknown %d\n", lock_status);
+ }
+ ctrl_sig_quality(demod, lock_status);
+
+ return 0;
+}
+
+static int drx39xxj_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ if (p->pre_bit_error.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
+ *ber = 0;
+ return 0;
+ }
+
+ if (!p->pre_bit_count.stat[0].uvalue) {
+ if (!p->pre_bit_error.stat[0].uvalue)
+ *ber = 0;
+ else
+ *ber = 1000000;
+ } else {
+ *ber = frac_times1e6(p->pre_bit_error.stat[0].uvalue,
+ p->pre_bit_count.stat[0].uvalue);
+ }
+ return 0;
+}
+
+static int drx39xxj_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ if (p->strength.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
+ *strength = 0;
+ return 0;
+ }
+
+ *strength = p->strength.stat[0].uvalue;
+ return 0;
+}
+
+static int drx39xxj_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u64 tmp64;
+
+ if (p->cnr.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
+ *snr = 0;
+ return 0;
+ }
+
+ tmp64 = p->cnr.stat[0].svalue;
+ do_div(tmp64, 10);
+ *snr = tmp64;
+ return 0;
+}
+
+static int drx39xxj_read_ucblocks(struct dvb_frontend *fe, u32 *ucb)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ if (p->block_error.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
+ *ucb = 0;
+ return 0;
+ }
+
+ *ucb = p->block_error.stat[0].uvalue;
+ return 0;
+}
+
+static int drx39xxj_set_frontend(struct dvb_frontend *fe)
+{
+#ifdef DJH_DEBUG
+ int i;
+#endif
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+ enum drx_standard standard = DRX_STANDARD_8VSB;
+ struct drx_channel channel;
+ int result;
+ struct drxuio_data uio_data;
+ static const struct drx_channel def_channel = {
+ /* frequency */ 0,
+ /* bandwidth */ DRX_BANDWIDTH_6MHZ,
+ /* mirror */ DRX_MIRROR_NO,
+ /* constellation */ DRX_CONSTELLATION_AUTO,
+ /* hierarchy */ DRX_HIERARCHY_UNKNOWN,
+ /* priority */ DRX_PRIORITY_UNKNOWN,
+ /* coderate */ DRX_CODERATE_UNKNOWN,
+ /* guard */ DRX_GUARD_UNKNOWN,
+ /* fftmode */ DRX_FFTMODE_UNKNOWN,
+ /* classification */ DRX_CLASSIFICATION_AUTO,
+ /* symbolrate */ 5057000,
+ /* interleavemode */ DRX_INTERLEAVEMODE_UNKNOWN,
+ /* ldpc */ DRX_LDPC_UNKNOWN,
+ /* carrier */ DRX_CARRIER_UNKNOWN,
+ /* frame mode */ DRX_FRAMEMODE_UNKNOWN
+ };
+ u32 constellation = DRX_CONSTELLATION_AUTO;
+
+ /* Bring the demod out of sleep */
+ drx39xxj_set_powerstate(fe, 1);
+
+ if (fe->ops.tuner_ops.set_params) {
+ u32 int_freq;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ /* Set tuner to desired frequency and standard */
+ fe->ops.tuner_ops.set_params(fe);
+
+ /* Use the tuner's IF */
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ fe->ops.tuner_ops.get_if_frequency(fe, &int_freq);
+ demod->my_common_attr->intermediate_freq = int_freq / 1000;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ switch (p->delivery_system) {
+ case SYS_ATSC:
+ standard = DRX_STANDARD_8VSB;
+ break;
+ case SYS_DVBC_ANNEX_B:
+ standard = DRX_STANDARD_ITU_B;
+
+ switch (p->modulation) {
+ case QAM_64:
+ constellation = DRX_CONSTELLATION_QAM64;
+ break;
+ case QAM_256:
+ constellation = DRX_CONSTELLATION_QAM256;
+ break;
+ default:
+ constellation = DRX_CONSTELLATION_AUTO;
+ break;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* Set the standard (will be powered up if necessary */
+ result = ctrl_set_standard(demod, &standard);
+ if (result != 0) {
+ pr_err("Failed to set standard! result=%02x\n",
+ result);
+ return -EINVAL;
+ }
+
+ /* set channel parameters */
+ channel = def_channel;
+ channel.frequency = p->frequency / 1000;
+ channel.bandwidth = DRX_BANDWIDTH_6MHZ;
+ channel.constellation = constellation;
+
+ /* program channel */
+ result = ctrl_set_channel(demod, &channel);
+ if (result != 0) {
+ pr_err("Failed to set channel!\n");
+ return -EINVAL;
+ }
+ /* Just for giggles, let's shut off the LNA again.... */
+ uio_data.uio = DRX_UIO1;
+ uio_data.value = false;
+ result = ctrl_uio_write(demod, &uio_data);
+ if (result != 0) {
+ pr_err("Failed to disable LNA!\n");
+ return 0;
+ }
+
+ /* After set_frontend, except for strength, stats aren't available */
+ p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+
+ return 0;
+}
+
+static int drx39xxj_sleep(struct dvb_frontend *fe)
+{
+ /* power-down the demodulator */
+ return drx39xxj_set_powerstate(fe, 0);
+}
+
+static int drx39xxj_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+ bool i2c_gate_state;
+ int result;
+
+#ifdef DJH_DEBUG
+ pr_debug("i2c gate call: enable=%d state=%d\n", enable,
+ state->i2c_gate_open);
+#endif
+
+ if (enable)
+ i2c_gate_state = true;
+ else
+ i2c_gate_state = false;
+
+ if (state->i2c_gate_open == enable) {
+ /* We're already in the desired state */
+ return 0;
+ }
+
+ result = ctrl_i2c_bridge(demod, &i2c_gate_state);
+ if (result != 0) {
+ pr_err("drx39xxj: could not open i2c gate [%d]\n",
+ result);
+ dump_stack();
+ } else {
+ state->i2c_gate_open = enable;
+ }
+ return 0;
+}
+
+static int drx39xxj_init(struct dvb_frontend *fe)
+{
+ /* Bring the demod out of sleep */
+ drx39xxj_set_powerstate(fe, 1);
+
+ return 0;
+}
+
+static int drx39xxj_set_lna(struct dvb_frontend *fe)
+{
+ int result;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ struct drxuio_cfg uio_cfg;
+ struct drxuio_data uio_data;
+
+ if (c->lna) {
+ if (!ext_attr->has_lna) {
+ pr_err("LNA is not supported on this device!\n");
+ return -EINVAL;
+
+ }
+ }
+
+ /* Turn off the LNA */
+ uio_cfg.uio = DRX_UIO1;
+ uio_cfg.mode = DRX_UIO_MODE_READWRITE;
+ /* Configure user-I/O #3: enable read/write */
+ result = ctrl_set_uio_cfg(demod, &uio_cfg);
+ if (result) {
+ pr_err("Failed to setup LNA GPIO!\n");
+ return result;
+ }
+
+ uio_data.uio = DRX_UIO1;
+ uio_data.value = c->lna;
+ result = ctrl_uio_write(demod, &uio_data);
+ if (result != 0) {
+ pr_err("Failed to %sable LNA!\n",
+ c->lna ? "en" : "dis");
+ return result;
+ }
+
+ return 0;
+}
+
+static int drx39xxj_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static void drx39xxj_release(struct dvb_frontend *fe)
+{
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+
+ drxj_close(demod);
+
+ kfree(demod->my_ext_attr);
+ kfree(demod->my_common_attr);
+ kfree(demod->my_i2c_dev_addr);
+ if (demod->firmware)
+ release_firmware(demod->firmware);
+ kfree(demod);
+ kfree(state);
+}
+
+static struct dvb_frontend_ops drx39xxj_ops;
+
+struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c)
+{
+ struct drx39xxj_state *state = NULL;
+ struct i2c_device_addr *demod_addr = NULL;
+ struct drx_common_attr *demod_comm_attr = NULL;
+ struct drxj_data *demod_ext_attr = NULL;
+ struct drx_demod_instance *demod = NULL;
+ struct dtv_frontend_properties *p;
+ struct drxuio_cfg uio_cfg;
+ struct drxuio_data uio_data;
+ int result;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct drx39xxj_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ demod = kmalloc(sizeof(struct drx_demod_instance), GFP_KERNEL);
+ if (demod == NULL)
+ goto error;
+
+ demod_addr = kmalloc(sizeof(struct i2c_device_addr), GFP_KERNEL);
+ if (demod_addr == NULL)
+ goto error;
+ memcpy(demod_addr, &drxj_default_addr_g,
+ sizeof(struct i2c_device_addr));
+
+ demod_comm_attr = kmalloc(sizeof(struct drx_common_attr), GFP_KERNEL);
+ if (demod_comm_attr == NULL)
+ goto error;
+ memcpy(demod_comm_attr, &drxj_default_comm_attr_g,
+ sizeof(struct drx_common_attr));
+
+ demod_ext_attr = kmalloc(sizeof(struct drxj_data), GFP_KERNEL);
+ if (demod_ext_attr == NULL)
+ goto error;
+ memcpy(demod_ext_attr, &drxj_data_g, sizeof(struct drxj_data));
+
+ /* setup the state */
+ state->i2c = i2c;
+ state->demod = demod;
+
+ /* setup the demod data */
+ memcpy(demod, &drxj_default_demod_g, sizeof(struct drx_demod_instance));
+
+ demod->my_i2c_dev_addr = demod_addr;
+ demod->my_common_attr = demod_comm_attr;
+ demod->my_i2c_dev_addr->user_data = state;
+ demod->my_common_attr->microcode_file = DRX39XX_MAIN_FIRMWARE;
+ demod->my_common_attr->verify_microcode = true;
+ demod->my_common_attr->intermediate_freq = 5000;
+ demod->my_common_attr->current_power_mode = DRX_POWER_DOWN;
+ demod->my_ext_attr = demod_ext_attr;
+ ((struct drxj_data *)demod_ext_attr)->uio_sma_tx_mode = DRX_UIO_MODE_READWRITE;
+ demod->i2c = i2c;
+
+ result = drxj_open(demod);
+ if (result != 0) {
+ pr_err("DRX open failed! Aborting\n");
+ goto error;
+ }
+
+ /* Turn off the LNA */
+ uio_cfg.uio = DRX_UIO1;
+ uio_cfg.mode = DRX_UIO_MODE_READWRITE;
+ /* Configure user-I/O #3: enable read/write */
+ result = ctrl_set_uio_cfg(demod, &uio_cfg);
+ if (result) {
+ pr_err("Failed to setup LNA GPIO!\n");
+ goto error;
+ }
+
+ uio_data.uio = DRX_UIO1;
+ uio_data.value = false;
+ result = ctrl_uio_write(demod, &uio_data);
+ if (result != 0) {
+ pr_err("Failed to disable LNA!\n");
+ goto error;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &drx39xxj_ops,
+ sizeof(struct dvb_frontend_ops));
+
+ state->frontend.demodulator_priv = state;
+
+ /* Initialize stats - needed for DVBv5 stats to work */
+ p = &state->frontend.dtv_property_cache;
+ p->strength.len = 1;
+ p->pre_bit_count.len = 1;
+ p->pre_bit_error.len = 1;
+ p->post_bit_count.len = 1;
+ p->post_bit_error.len = 1;
+ p->block_count.len = 1;
+ p->block_error.len = 1;
+ p->cnr.len = 1;
+
+ p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ return &state->frontend;
+
+error:
+ kfree(demod_ext_attr);
+ kfree(demod_comm_attr);
+ kfree(demod_addr);
+ kfree(demod);
+ kfree(state);
+
+ return NULL;
+}
+EXPORT_SYMBOL(drx39xxj_attach);
+
+static struct dvb_frontend_ops drx39xxj_ops = {
+ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
+ .info = {
+ .name = "Micronas DRX39xxj family Frontend",
+ .frequency_stepsize = 62500,
+ .frequency_min = 51000000,
+ .frequency_max = 858000000,
+ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+
+ .init = drx39xxj_init,
+ .i2c_gate_ctrl = drx39xxj_i2c_gate_ctrl,
+ .sleep = drx39xxj_sleep,
+ .set_frontend = drx39xxj_set_frontend,
+ .get_tune_settings = drx39xxj_get_tune_settings,
+ .read_status = drx39xxj_read_status,
+ .read_ber = drx39xxj_read_ber,
+ .read_signal_strength = drx39xxj_read_signal_strength,
+ .read_snr = drx39xxj_read_snr,
+ .read_ucblocks = drx39xxj_read_ucblocks,
+ .release = drx39xxj_release,
+ .set_lna = drx39xxj_set_lna,
+};
+
+MODULE_DESCRIPTION("Micronas DRX39xxj Frontend");
+MODULE_AUTHOR("Devin Heitmueller");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(DRX39XX_MAIN_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.h b/drivers/media/dvb-frontends/drx39xyj/drxj.h
new file mode 100644
index 000000000000..55ad535197d2
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.h
@@ -0,0 +1,650 @@
+
+/*
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ 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 of Trident Microsystems nor Hauppauge Computer Works
+ 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 HOLDER 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.
+
+ DRXJ specific header file
+
+ Authors: Dragan Savic, Milos Nikolic, Mihajlo Katona, Tao Ding, Paul Janssen
+*/
+
+#ifndef __DRXJ_H__
+#define __DRXJ_H__
+/*-------------------------------------------------------------------------
+INCLUDES
+-------------------------------------------------------------------------*/
+
+#include "drx_driver.h"
+#include "drx_dap_fasi.h"
+
+/* Check DRX-J specific dap condition */
+/* Multi master mode and short addr format only will not work.
+ RMW, CRC reset, broadcast and switching back to single master mode
+ cannot be done with short addr only in multi master mode. */
+#if ((DRXDAP_SINGLE_MASTER == 0) && (DRXDAPFASI_LONG_ADDR_ALLOWED == 0))
+#error "Multi master mode and short addressing only is an illegal combination"
+ *; /* Generate a fatal compiler error to make sure it stops here,
+ this is necesarry because not all compilers stop after a #error. */
+#endif
+
+/*-------------------------------------------------------------------------
+TYPEDEFS
+-------------------------------------------------------------------------*/
+/*============================================================================*/
+/*============================================================================*/
+/*== code support ============================================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== SCU cmd if =============================================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+ struct drxjscu_cmd {
+ u16 command;
+ /**< Command number */
+ u16 parameter_len;
+ /**< Data length in byte */
+ u16 result_len;
+ /**< result length in byte */
+ u16 *parameter;
+ /**< General purpous param */
+ u16 *result;
+ /**< General purpous param */};
+
+/*============================================================================*/
+/*============================================================================*/
+/*== CTRL CFG related data structures ========================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/* extra intermediate lock state for VSB,QAM,NTSC */
+#define DRXJ_DEMOD_LOCK (DRX_LOCK_STATE_1)
+
+/* OOB lock states */
+#define DRXJ_OOB_AGC_LOCK (DRX_LOCK_STATE_1) /* analog gain control lock */
+#define DRXJ_OOB_SYNC_LOCK (DRX_LOCK_STATE_2) /* digital gain control lock */
+
+/* Intermediate powermodes for DRXJ */
+#define DRXJ_POWER_DOWN_MAIN_PATH DRX_POWER_MODE_8
+#define DRXJ_POWER_DOWN_CORE DRX_POWER_MODE_9
+#define DRXJ_POWER_DOWN_PLL DRX_POWER_MODE_10
+
+/* supstition for GPIO FNC mux */
+#define APP_O (0x0000)
+
+/*#define DRX_CTRL_BASE (0x0000)*/
+
+#define DRXJ_CTRL_CFG_BASE (0x1000)
+ enum drxj_cfg_type {
+ DRXJ_CFG_AGC_RF = DRXJ_CTRL_CFG_BASE,
+ DRXJ_CFG_AGC_IF,
+ DRXJ_CFG_AGC_INTERNAL,
+ DRXJ_CFG_PRE_SAW,
+ DRXJ_CFG_AFE_GAIN,
+ DRXJ_CFG_SYMBOL_CLK_OFFSET,
+ DRXJ_CFG_ACCUM_CR_RS_CW_ERR,
+ DRXJ_CFG_FEC_MERS_SEQ_COUNT,
+ DRXJ_CFG_OOB_MISC,
+ DRXJ_CFG_SMART_ANT,
+ DRXJ_CFG_OOB_PRE_SAW,
+ DRXJ_CFG_VSB_MISC,
+ DRXJ_CFG_RESET_PACKET_ERR,
+
+ /* ATV (FM) */
+ DRXJ_CFG_ATV_OUTPUT, /* also for FM (SIF control) but not likely */
+ DRXJ_CFG_ATV_MISC,
+ DRXJ_CFG_ATV_EQU_COEF,
+ DRXJ_CFG_ATV_AGC_STATUS, /* also for FM ( IF,RF, audioAGC ) */
+
+ DRXJ_CFG_MPEG_OUTPUT_MISC,
+ DRXJ_CFG_HW_CFG,
+ DRXJ_CFG_OOB_LO_POW,
+
+ DRXJ_CFG_MAX /* dummy, never to be used */};
+
+/**
+* /struct enum drxj_cfg_smart_ant_io * smart antenna i/o.
+*/
+enum drxj_cfg_smart_ant_io {
+ DRXJ_SMT_ANT_OUTPUT = 0,
+ DRXJ_SMT_ANT_INPUT
+};
+
+/**
+* /struct struct drxj_cfg_smart_ant * Set smart antenna.
+*/
+ struct drxj_cfg_smart_ant {
+ enum drxj_cfg_smart_ant_io io;
+ u16 ctrl_data;
+ };
+
+/**
+* /struct DRXJAGCSTATUS_t
+* AGC status information from the DRXJ-IQM-AF.
+*/
+struct drxj_agc_status {
+ u16 IFAGC;
+ u16 RFAGC;
+ u16 digital_agc;
+};
+
+/* DRXJ_CFG_AGC_RF, DRXJ_CFG_AGC_IF */
+
+/**
+* /struct enum drxj_agc_ctrl_mode * Available AGCs modes in the DRXJ.
+*/
+ enum drxj_agc_ctrl_mode {
+ DRX_AGC_CTRL_AUTO = 0,
+ DRX_AGC_CTRL_USER,
+ DRX_AGC_CTRL_OFF};
+
+/**
+* /struct struct drxj_cfg_agc * Generic interface for all AGCs present on the DRXJ.
+*/
+ struct drxj_cfg_agc {
+ enum drx_standard standard; /* standard for which these settings apply */
+ enum drxj_agc_ctrl_mode ctrl_mode; /* off, user, auto */
+ u16 output_level; /* range dependent on AGC */
+ u16 min_output_level; /* range dependent on AGC */
+ u16 max_output_level; /* range dependent on AGC */
+ u16 speed; /* range dependent on AGC */
+ u16 top; /* rf-agc take over point */
+ u16 cut_off_current; /* rf-agc is accelerated if output current
+ is below cut-off current */};
+
+/* DRXJ_CFG_PRE_SAW */
+
+/**
+* /struct struct drxj_cfg_pre_saw * Interface to configure pre SAW sense.
+*/
+ struct drxj_cfg_pre_saw {
+ enum drx_standard standard; /* standard to which these settings apply */
+ u16 reference; /* pre SAW reference value, range 0 .. 31 */
+ bool use_pre_saw; /* true algorithms must use pre SAW sense */};
+
+/* DRXJ_CFG_AFE_GAIN */
+
+/**
+* /struct struct drxj_cfg_afe_gain * Interface to configure gain of AFE (LNA + PGA).
+*/
+ struct drxj_cfg_afe_gain {
+ enum drx_standard standard; /* standard to which these settings apply */
+ u16 gain; /* gain in 0.1 dB steps, DRXJ range 140 .. 335 */};
+
+/**
+* /struct drxjrs_errors
+* Available failure information in DRXJ_FEC_RS.
+*
+* Container for errors that are received in the most recently finished measurment period
+*
+*/
+ struct drxjrs_errors {
+ u16 nr_bit_errors;
+ /**< no of pre RS bit errors */
+ u16 nr_symbol_errors;
+ /**< no of pre RS symbol errors */
+ u16 nr_packet_errors;
+ /**< no of pre RS packet errors */
+ u16 nr_failures;
+ /**< no of post RS failures to decode */
+ u16 nr_snc_par_fail_count;
+ /**< no of post RS bit erros */
+ };
+
+/**
+* /struct struct drxj_cfg_vsb_misc * symbol error rate
+*/
+ struct drxj_cfg_vsb_misc {
+ u32 symb_error;
+ /**< symbol error rate sps */};
+
+/**
+* /enum enum drxj_mpeg_output_clock_rate * Mpeg output clock rate.
+*
+*/
+ enum drxj_mpeg_start_width {
+ DRXJ_MPEG_START_WIDTH_1CLKCYC,
+ DRXJ_MPEG_START_WIDTH_8CLKCYC};
+
+/**
+* /enum enum drxj_mpeg_output_clock_rate * Mpeg output clock rate.
+*
+*/
+ enum drxj_mpeg_output_clock_rate {
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_AUTO,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_75973K,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_50625K,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_37968K,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_30375K,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_25313K,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_21696K};
+
+/**
+* /struct DRXJCfgMisc_t
+* Change TEI bit of MPEG output
+* reverse MPEG output bit order
+* set MPEG output clock rate
+*/
+ struct drxj_cfg_mpeg_output_misc {
+ bool disable_tei_handling; /**< if true pass (not change) TEI bit */
+ bool bit_reverse_mpeg_outout; /**< if true, parallel: msb on MD0; serial: lsb out first */
+ enum drxj_mpeg_output_clock_rate mpeg_output_clock_rate;
+ /**< set MPEG output clock rate that overwirtes the derived one from symbol rate */
+ enum drxj_mpeg_start_width mpeg_start_width; /**< set MPEG output start width */};
+
+/**
+* /enum enum drxj_xtal_freq * Supported external crystal reference frequency.
+*/
+ enum drxj_xtal_freq {
+ DRXJ_XTAL_FREQ_RSVD,
+ DRXJ_XTAL_FREQ_27MHZ,
+ DRXJ_XTAL_FREQ_20P25MHZ,
+ DRXJ_XTAL_FREQ_4MHZ};
+
+/**
+* /enum enum drxj_xtal_freq * Supported external crystal reference frequency.
+*/
+ enum drxji2c_speed {
+ DRXJ_I2C_SPEED_400KBPS,
+ DRXJ_I2C_SPEED_100KBPS};
+
+/**
+* /struct struct drxj_cfg_hw_cfg * Get hw configuration, such as crystal reference frequency, I2C speed, etc...
+*/
+ struct drxj_cfg_hw_cfg {
+ enum drxj_xtal_freq xtal_freq;
+ /**< crystal reference frequency */
+ enum drxji2c_speed i2c_speed;
+ /**< 100 or 400 kbps */};
+
+/*
+ * DRXJ_CFG_ATV_MISC
+ */
+ struct drxj_cfg_atv_misc {
+ s16 peak_filter; /* -8 .. 15 */
+ u16 noise_filter; /* 0 .. 15 */};
+
+/*
+ * struct drxj_cfg_oob_misc */
+#define DRXJ_OOB_STATE_RESET 0x0
+#define DRXJ_OOB_STATE_AGN_HUNT 0x1
+#define DRXJ_OOB_STATE_DGN_HUNT 0x2
+#define DRXJ_OOB_STATE_AGC_HUNT 0x3
+#define DRXJ_OOB_STATE_FRQ_HUNT 0x4
+#define DRXJ_OOB_STATE_PHA_HUNT 0x8
+#define DRXJ_OOB_STATE_TIM_HUNT 0x10
+#define DRXJ_OOB_STATE_EQU_HUNT 0x20
+#define DRXJ_OOB_STATE_EQT_HUNT 0x30
+#define DRXJ_OOB_STATE_SYNC 0x40
+
+struct drxj_cfg_oob_misc {
+ struct drxj_agc_status agc;
+ bool eq_lock;
+ bool sym_timing_lock;
+ bool phase_lock;
+ bool freq_lock;
+ bool dig_gain_lock;
+ bool ana_gain_lock;
+ u8 state;
+};
+
+/*
+ * Index of in array of coef
+ */
+ enum drxj_cfg_oob_lo_power {
+ DRXJ_OOB_LO_POW_MINUS0DB = 0,
+ DRXJ_OOB_LO_POW_MINUS5DB,
+ DRXJ_OOB_LO_POW_MINUS10DB,
+ DRXJ_OOB_LO_POW_MINUS15DB,
+ DRXJ_OOB_LO_POW_MAX};
+
+/*
+ * DRXJ_CFG_ATV_EQU_COEF
+ */
+ struct drxj_cfg_atv_equ_coef {
+ s16 coef0; /* -256 .. 255 */
+ s16 coef1; /* -256 .. 255 */
+ s16 coef2; /* -256 .. 255 */
+ s16 coef3; /* -256 .. 255 */};
+
+/*
+ * Index of in array of coef
+ */
+ enum drxj_coef_array_index {
+ DRXJ_COEF_IDX_MN = 0,
+ DRXJ_COEF_IDX_FM,
+ DRXJ_COEF_IDX_L,
+ DRXJ_COEF_IDX_LP,
+ DRXJ_COEF_IDX_BG,
+ DRXJ_COEF_IDX_DK,
+ DRXJ_COEF_IDX_I,
+ DRXJ_COEF_IDX_MAX};
+
+/*
+ * DRXJ_CFG_ATV_OUTPUT
+ */
+
+/**
+* /enum DRXJAttenuation_t
+* Attenuation setting for SIF AGC.
+*
+*/
+ enum drxjsif_attenuation {
+ DRXJ_SIF_ATTENUATION_0DB,
+ DRXJ_SIF_ATTENUATION_3DB,
+ DRXJ_SIF_ATTENUATION_6DB,
+ DRXJ_SIF_ATTENUATION_9DB};
+
+/**
+* /struct struct drxj_cfg_atv_output * SIF attenuation setting.
+*
+*/
+struct drxj_cfg_atv_output {
+ bool enable_cvbs_output; /* true= enabled */
+ bool enable_sif_output; /* true= enabled */
+ enum drxjsif_attenuation sif_attenuation;
+};
+
+/*
+ DRXJ_CFG_ATV_AGC_STATUS (get only)
+*/
+/* TODO : AFE interface not yet finished, subject to change */
+ struct drxj_cfg_atv_agc_status {
+ u16 rf_agc_gain; /* 0 .. 877 uA */
+ u16 if_agc_gain; /* 0 .. 877 uA */
+ s16 video_agc_gain; /* -75 .. 1972 in 0.1 dB steps */
+ s16 audio_agc_gain; /* -4 .. 1020 in 0.1 dB steps */
+ u16 rf_agc_loop_gain; /* 0 .. 7 */
+ u16 if_agc_loop_gain; /* 0 .. 7 */
+ u16 video_agc_loop_gain; /* 0 .. 7 */};
+
+/*============================================================================*/
+/*============================================================================*/
+/*== CTRL related data structures ============================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/* NONE */
+
+/*============================================================================*/
+/*============================================================================*/
+
+/*========================================*/
+/**
+* /struct struct drxj_data * DRXJ specific attributes.
+*
+* Global data container for DRXJ specific data.
+*
+*/
+ struct drxj_data {
+ /* device capabilties (determined during drx_open()) */
+ bool has_lna; /**< true if LNA (aka PGA) present */
+ bool has_oob; /**< true if OOB supported */
+ bool has_ntsc; /**< true if NTSC supported */
+ bool has_btsc; /**< true if BTSC supported */
+ bool has_smatx; /**< true if mat_tx is available */
+ bool has_smarx; /**< true if mat_rx is available */
+ bool has_gpio; /**< true if GPIO is available */
+ bool has_irqn; /**< true if IRQN is available */
+ /* A1/A2/A... */
+ u8 mfx; /**< metal fix */
+
+ /* tuner settings */
+ bool mirror_freq_spect_oob;/**< tuner inversion (true = tuner mirrors the signal */
+
+ /* standard/channel settings */
+ enum drx_standard standard; /**< current standard information */
+ enum drx_modulation constellation;
+ /**< current constellation */
+ s32 frequency; /**< center signal frequency in KHz */
+ enum drx_bandwidth curr_bandwidth;
+ /**< current channel bandwidth */
+ enum drx_mirror mirror; /**< current channel mirror */
+
+ /* signal quality information */
+ u32 fec_bits_desired; /**< BER accounting period */
+ u16 fec_vd_plen; /**< no of trellis symbols: VD SER measurement period */
+ u16 qam_vd_prescale; /**< Viterbi Measurement Prescale */
+ u16 qam_vd_period; /**< Viterbi Measurement period */
+ u16 fec_rs_plen; /**< defines RS BER measurement period */
+ u16 fec_rs_prescale; /**< ReedSolomon Measurement Prescale */
+ u16 fec_rs_period; /**< ReedSolomon Measurement period */
+ bool reset_pkt_err_acc; /**< Set a flag to reset accumulated packet error */
+ u16 pkt_err_acc_start; /**< Set a flag to reset accumulated packet error */
+
+ /* HI configuration */
+ u16 hi_cfg_timing_div; /**< HI Configure() parameter 2 */
+ u16 hi_cfg_bridge_delay; /**< HI Configure() parameter 3 */
+ u16 hi_cfg_wake_up_key; /**< HI Configure() parameter 4 */
+ u16 hi_cfg_ctrl; /**< HI Configure() parameter 5 */
+ u16 hi_cfg_transmit; /**< HI Configure() parameter 6 */
+
+ /* UIO configuartion */
+ enum drxuio_mode uio_sma_rx_mode;/**< current mode of SmaRx pin */
+ enum drxuio_mode uio_sma_tx_mode;/**< current mode of SmaTx pin */
+ enum drxuio_mode uio_gpio_mode; /**< current mode of ASEL pin */
+ enum drxuio_mode uio_irqn_mode; /**< current mode of IRQN pin */
+
+ /* IQM fs frequecy shift and inversion */
+ u32 iqm_fs_rate_ofs; /**< frequency shifter setting after setchannel */
+ bool pos_image; /**< Ture: positive image */
+ /* IQM RC frequecy shift */
+ u32 iqm_rc_rate_ofs; /**< frequency shifter setting after setchannel */
+
+ /* ATV configuartion */
+ u32 atv_cfg_changed_flags; /**< flag: flags cfg changes */
+ s16 atv_top_equ0[DRXJ_COEF_IDX_MAX]; /**< shadow of ATV_TOP_EQU0__A */
+ s16 atv_top_equ1[DRXJ_COEF_IDX_MAX]; /**< shadow of ATV_TOP_EQU1__A */
+ s16 atv_top_equ2[DRXJ_COEF_IDX_MAX]; /**< shadow of ATV_TOP_EQU2__A */
+ s16 atv_top_equ3[DRXJ_COEF_IDX_MAX]; /**< shadow of ATV_TOP_EQU3__A */
+ bool phase_correction_bypass;/**< flag: true=bypass */
+ s16 atv_top_vid_peak; /**< shadow of ATV_TOP_VID_PEAK__A */
+ u16 atv_top_noise_th; /**< shadow of ATV_TOP_NOISE_TH__A */
+ bool enable_cvbs_output; /**< flag CVBS ouput enable */
+ bool enable_sif_output; /**< flag SIF ouput enable */
+ enum drxjsif_attenuation sif_attenuation;
+ /**< current SIF att setting */
+ /* Agc configuration for QAM and VSB */
+ struct drxj_cfg_agc qam_rf_agc_cfg; /**< qam RF AGC config */
+ struct drxj_cfg_agc qam_if_agc_cfg; /**< qam IF AGC config */
+ struct drxj_cfg_agc vsb_rf_agc_cfg; /**< vsb RF AGC config */
+ struct drxj_cfg_agc vsb_if_agc_cfg; /**< vsb IF AGC config */
+
+ /* PGA gain configuration for QAM and VSB */
+ u16 qam_pga_cfg; /**< qam PGA config */
+ u16 vsb_pga_cfg; /**< vsb PGA config */
+
+ /* Pre SAW configuration for QAM and VSB */
+ struct drxj_cfg_pre_saw qam_pre_saw_cfg;
+ /**< qam pre SAW config */
+ struct drxj_cfg_pre_saw vsb_pre_saw_cfg;
+ /**< qam pre SAW config */
+
+ /* Version information */
+ char v_text[2][12]; /**< allocated text versions */
+ struct drx_version v_version[2]; /**< allocated versions structs */
+ struct drx_version_list v_list_elements[2];
+ /**< allocated version list */
+
+ /* smart antenna configuration */
+ bool smart_ant_inverted;
+
+ /* Tracking filter setting for OOB */
+ u16 oob_trk_filter_cfg[8];
+ bool oob_power_on;
+
+ /* MPEG static bitrate setting */
+ u32 mpeg_ts_static_bitrate; /**< bitrate static MPEG output */
+ bool disable_te_ihandling; /**< MPEG TS TEI handling */
+ bool bit_reverse_mpeg_outout;/**< MPEG output bit order */
+ enum drxj_mpeg_output_clock_rate mpeg_output_clock_rate;
+ /**< MPEG output clock rate */
+ enum drxj_mpeg_start_width mpeg_start_width;
+ /**< MPEG Start width */
+
+ /* Pre SAW & Agc configuration for ATV */
+ struct drxj_cfg_pre_saw atv_pre_saw_cfg;
+ /**< atv pre SAW config */
+ struct drxj_cfg_agc atv_rf_agc_cfg; /**< atv RF AGC config */
+ struct drxj_cfg_agc atv_if_agc_cfg; /**< atv IF AGC config */
+ u16 atv_pga_cfg; /**< atv pga config */
+
+ u32 curr_symbol_rate;
+
+ /* pin-safe mode */
+ bool pdr_safe_mode; /**< PDR safe mode activated */
+ u16 pdr_safe_restore_val_gpio;
+ u16 pdr_safe_restore_val_v_sync;
+ u16 pdr_safe_restore_val_sma_rx;
+ u16 pdr_safe_restore_val_sma_tx;
+
+ /* OOB pre-saw value */
+ u16 oob_pre_saw;
+ enum drxj_cfg_oob_lo_power oob_lo_pow;
+
+ struct drx_aud_data aud_data;
+ /**< audio storage */};
+
+/*-------------------------------------------------------------------------
+Access MACROS
+-------------------------------------------------------------------------*/
+/**
+* \brief Compilable references to attributes
+* \param d pointer to demod instance
+*
+* Used as main reference to an attribute field.
+* Can be used by both macro implementation and function implementation.
+* These macros are defined to avoid duplication of code in macro and function
+* definitions that handle access of demod common or extended attributes.
+*
+*/
+
+#define DRXJ_ATTR_BTSC_DETECT(d) \
+ (((struct drxj_data *)(d)->my_ext_attr)->aud_data.btsc_detect)
+
+/*-------------------------------------------------------------------------
+DEFINES
+-------------------------------------------------------------------------*/
+
+/**
+* \def DRXJ_NTSC_CARRIER_FREQ_OFFSET
+* \brief Offset from picture carrier to centre frequency in kHz, in RF domain
+*
+* For NTSC standard.
+* NTSC channels are listed by their picture carrier frequency (Fpc).
+* The function DRX_CTRL_SET_CHANNEL requires the centre frequency as input.
+* In case the tuner module is not used the DRX-J requires that the tuner is
+* tuned to the centre frequency of the channel:
+*
+* Fcentre = Fpc + DRXJ_NTSC_CARRIER_FREQ_OFFSET
+*
+*/
+#define DRXJ_NTSC_CARRIER_FREQ_OFFSET ((s32)(1750))
+
+/**
+* \def DRXJ_PAL_SECAM_BG_CARRIER_FREQ_OFFSET
+* \brief Offset from picture carrier to centre frequency in kHz, in RF domain
+*
+* For PAL/SECAM - BG standard. This define is needed in case the tuner module
+* is NOT used. PAL/SECAM channels are listed by their picture carrier frequency (Fpc).
+* The DRX-J requires that the tuner is tuned to:
+* Fpc + DRXJ_PAL_SECAM_BG_CARRIER_FREQ_OFFSET
+*
+* In case the tuner module is used the drxdriver takes care of this.
+* In case the tuner module is NOT used the application programmer must take
+* care of this.
+*
+*/
+#define DRXJ_PAL_SECAM_BG_CARRIER_FREQ_OFFSET ((s32)(2375))
+
+/**
+* \def DRXJ_PAL_SECAM_DKIL_CARRIER_FREQ_OFFSET
+* \brief Offset from picture carrier to centre frequency in kHz, in RF domain
+*
+* For PAL/SECAM - DK, I, L standards. This define is needed in case the tuner module
+* is NOT used. PAL/SECAM channels are listed by their picture carrier frequency (Fpc).
+* The DRX-J requires that the tuner is tuned to:
+* Fpc + DRXJ_PAL_SECAM_DKIL_CARRIER_FREQ_OFFSET
+*
+* In case the tuner module is used the drxdriver takes care of this.
+* In case the tuner module is NOT used the application programmer must take
+* care of this.
+*
+*/
+#define DRXJ_PAL_SECAM_DKIL_CARRIER_FREQ_OFFSET ((s32)(2775))
+
+/**
+* \def DRXJ_PAL_SECAM_LP_CARRIER_FREQ_OFFSET
+* \brief Offset from picture carrier to centre frequency in kHz, in RF domain
+*
+* For PAL/SECAM - LP standard. This define is needed in case the tuner module
+* is NOT used. PAL/SECAM channels are listed by their picture carrier frequency (Fpc).
+* The DRX-J requires that the tuner is tuned to:
+* Fpc + DRXJ_PAL_SECAM_LP_CARRIER_FREQ_OFFSET
+*
+* In case the tuner module is used the drxdriver takes care of this.
+* In case the tuner module is NOT used the application programmer must take
+* care of this.
+*/
+#define DRXJ_PAL_SECAM_LP_CARRIER_FREQ_OFFSET ((s32)(-3255))
+
+/**
+* \def DRXJ_FM_CARRIER_FREQ_OFFSET
+* \brief Offset from sound carrier to centre frequency in kHz, in RF domain
+*
+* For FM standard.
+* FM channels are listed by their sound carrier frequency (Fsc).
+* The function DRX_CTRL_SET_CHANNEL requires the Ffm frequency (see below) as
+* input.
+* In case the tuner module is not used the DRX-J requires that the tuner is
+* tuned to the Ffm frequency of the channel.
+*
+* Ffm = Fsc + DRXJ_FM_CARRIER_FREQ_OFFSET
+*
+*/
+#define DRXJ_FM_CARRIER_FREQ_OFFSET ((s32)(-3000))
+
+/* Revision types -------------------------------------------------------*/
+
+#define DRXJ_TYPE_ID (0x3946000DUL)
+
+/* Macros ---------------------------------------------------------------*/
+
+/* Convert OOB lock status to string */
+#define DRXJ_STR_OOB_LOCKSTATUS(x) ( \
+ (x == DRX_NEVER_LOCK) ? "Never" : \
+ (x == DRX_NOT_LOCKED) ? "No" : \
+ (x == DRX_LOCKED) ? "Locked" : \
+ (x == DRX_LOCK_STATE_1) ? "AGC lock" : \
+ (x == DRX_LOCK_STATE_2) ? "sync lock" : \
+ "(Invalid)")
+
+#endif /* __DRXJ_H__ */
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj_map.h b/drivers/media/dvb-frontends/drx39xyj/drxj_map.h
new file mode 100644
index 000000000000..0bbd4ae1f524
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj_map.h
@@ -0,0 +1,15055 @@
+/*
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ 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 of Trident Microsystems nor Hauppauge Computer Works
+ 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 HOLDER 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.
+*/
+
+/*
+ ***********************************************************************************************************************
+ * WARNING - THIS FILE HAS BEEN GENERATED - DO NOT CHANGE
+ *
+ * Filename: drxj_map.h
+ * Generated on: Mon Jan 18 12:09:24 2010
+ * Generated by: IDF:x 1.3.0
+ * Generated from: reg_map
+ * Output start: [entry point]
+ *
+ * filename last modified re-use
+ * -----------------------------------------------------
+ * reg_map.1.tmp Mon Jan 18 12:09:24 2010 -
+ *
+ */
+
+#ifndef __DRXJ_MAP__H__
+#define __DRXJ_MAP__H__ INCLUDED
+
+#ifdef _REGISTERTABLE_
+#include <registertable.h>
+ extern register_table_t drxj_map[];
+ extern register_table_info_t drxj_map_info[];
+#endif
+
+#define ATV_COMM_EXEC__A 0xC00000
+#define ATV_COMM_EXEC__W 2
+#define ATV_COMM_EXEC__M 0x3
+#define ATV_COMM_EXEC__PRE 0x0
+#define ATV_COMM_EXEC_STOP 0x0
+#define ATV_COMM_EXEC_ACTIVE 0x1
+#define ATV_COMM_EXEC_HOLD 0x2
+
+#define ATV_COMM_STATE__A 0xC00001
+#define ATV_COMM_STATE__W 16
+#define ATV_COMM_STATE__M 0xFFFF
+#define ATV_COMM_STATE__PRE 0x0
+#define ATV_COMM_MB__A 0xC00002
+#define ATV_COMM_MB__W 16
+#define ATV_COMM_MB__M 0xFFFF
+#define ATV_COMM_MB__PRE 0x0
+#define ATV_COMM_INT_REQ__A 0xC00003
+#define ATV_COMM_INT_REQ__W 16
+#define ATV_COMM_INT_REQ__M 0xFFFF
+#define ATV_COMM_INT_REQ__PRE 0x0
+#define ATV_COMM_INT_REQ_COMM_INT_REQ__B 0
+#define ATV_COMM_INT_REQ_COMM_INT_REQ__W 1
+#define ATV_COMM_INT_REQ_COMM_INT_REQ__M 0x1
+#define ATV_COMM_INT_REQ_COMM_INT_REQ__PRE 0x0
+
+#define ATV_COMM_INT_STA__A 0xC00005
+#define ATV_COMM_INT_STA__W 16
+#define ATV_COMM_INT_STA__M 0xFFFF
+#define ATV_COMM_INT_STA__PRE 0x0
+#define ATV_COMM_INT_MSK__A 0xC00006
+#define ATV_COMM_INT_MSK__W 16
+#define ATV_COMM_INT_MSK__M 0xFFFF
+#define ATV_COMM_INT_MSK__PRE 0x0
+#define ATV_COMM_INT_STM__A 0xC00007
+#define ATV_COMM_INT_STM__W 16
+#define ATV_COMM_INT_STM__M 0xFFFF
+#define ATV_COMM_INT_STM__PRE 0x0
+
+#define ATV_COMM_KEY__A 0xC0000F
+#define ATV_COMM_KEY__W 16
+#define ATV_COMM_KEY__M 0xFFFF
+#define ATV_COMM_KEY__PRE 0x0
+#define ATV_COMM_KEY_KEY 0xFABA
+#define ATV_COMM_KEY_MIN 0x0
+#define ATV_COMM_KEY_MAX 0xFFFF
+
+#define ATV_TOP_COMM_EXEC__A 0xC10000
+#define ATV_TOP_COMM_EXEC__W 2
+#define ATV_TOP_COMM_EXEC__M 0x3
+#define ATV_TOP_COMM_EXEC__PRE 0x0
+#define ATV_TOP_COMM_EXEC_STOP 0x0
+#define ATV_TOP_COMM_EXEC_ACTIVE 0x1
+#define ATV_TOP_COMM_EXEC_HOLD 0x2
+
+#define ATV_TOP_COMM_STATE__A 0xC10001
+#define ATV_TOP_COMM_STATE__W 16
+#define ATV_TOP_COMM_STATE__M 0xFFFF
+#define ATV_TOP_COMM_STATE__PRE 0x0
+#define ATV_TOP_COMM_STATE_STATE__B 0
+#define ATV_TOP_COMM_STATE_STATE__W 16
+#define ATV_TOP_COMM_STATE_STATE__M 0xFFFF
+#define ATV_TOP_COMM_STATE_STATE__PRE 0x0
+
+#define ATV_TOP_COMM_MB__A 0xC10002
+#define ATV_TOP_COMM_MB__W 16
+#define ATV_TOP_COMM_MB__M 0xFFFF
+#define ATV_TOP_COMM_MB__PRE 0x0
+#define ATV_TOP_COMM_MB_CTL__B 0
+#define ATV_TOP_COMM_MB_CTL__W 1
+#define ATV_TOP_COMM_MB_CTL__M 0x1
+#define ATV_TOP_COMM_MB_CTL__PRE 0x0
+#define ATV_TOP_COMM_MB_OBS__B 1
+#define ATV_TOP_COMM_MB_OBS__W 1
+#define ATV_TOP_COMM_MB_OBS__M 0x2
+#define ATV_TOP_COMM_MB_OBS__PRE 0x0
+
+#define ATV_TOP_COMM_MB_MUX_CTRL__B 2
+#define ATV_TOP_COMM_MB_MUX_CTRL__W 4
+#define ATV_TOP_COMM_MB_MUX_CTRL__M 0x3C
+#define ATV_TOP_COMM_MB_MUX_CTRL__PRE 0x0
+#define ATV_TOP_COMM_MB_MUX_CTRL_PEAK_S 0x0
+#define ATV_TOP_COMM_MB_MUX_CTRL_VID_GAIN 0x4
+#define ATV_TOP_COMM_MB_MUX_CTRL_CORR_O 0x8
+#define ATV_TOP_COMM_MB_MUX_CTRL_CR_ROT_O 0xC
+#define ATV_TOP_COMM_MB_MUX_CTRL_CR_IIR_IQ 0x10
+#define ATV_TOP_COMM_MB_MUX_CTRL_VIDEO_O 0x14
+#define ATV_TOP_COMM_MB_MUX_CTRL_SIF_O 0x18
+#define ATV_TOP_COMM_MB_MUX_CTRL_SIF2025_O 0x1C
+#define ATV_TOP_COMM_MB_MUX_CTRL_POST_S 0x20
+
+#define ATV_TOP_COMM_MB_MUX_OBS__B 6
+#define ATV_TOP_COMM_MB_MUX_OBS__W 4
+#define ATV_TOP_COMM_MB_MUX_OBS__M 0x3C0
+#define ATV_TOP_COMM_MB_MUX_OBS__PRE 0x0
+#define ATV_TOP_COMM_MB_MUX_OBS_PEAK_S 0x0
+#define ATV_TOP_COMM_MB_MUX_OBS_VID_GAIN 0x40
+#define ATV_TOP_COMM_MB_MUX_OBS_CORR_O 0x80
+#define ATV_TOP_COMM_MB_MUX_OBS_CR_ROT_O 0xC0
+#define ATV_TOP_COMM_MB_MUX_OBS_CR_IIR_IQ 0x100
+#define ATV_TOP_COMM_MB_MUX_OBS_VIDEO_O 0x140
+#define ATV_TOP_COMM_MB_MUX_OBS_SIF_O 0x180
+#define ATV_TOP_COMM_MB_MUX_OBS_SIF2025_O 0x1C0
+#define ATV_TOP_COMM_MB_MUX_OBS_POST_S 0x200
+
+#define ATV_TOP_COMM_INT_REQ__A 0xC10003
+#define ATV_TOP_COMM_INT_REQ__W 16
+#define ATV_TOP_COMM_INT_REQ__M 0xFFFF
+#define ATV_TOP_COMM_INT_REQ__PRE 0x0
+#define ATV_TOP_COMM_INT_STA__A 0xC10005
+#define ATV_TOP_COMM_INT_STA__W 16
+#define ATV_TOP_COMM_INT_STA__M 0xFFFF
+#define ATV_TOP_COMM_INT_STA__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STA_FAGC_STA__B 0
+#define ATV_TOP_COMM_INT_STA_FAGC_STA__W 1
+#define ATV_TOP_COMM_INT_STA_FAGC_STA__M 0x1
+#define ATV_TOP_COMM_INT_STA_FAGC_STA__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STA_OVM_STA__B 1
+#define ATV_TOP_COMM_INT_STA_OVM_STA__W 1
+#define ATV_TOP_COMM_INT_STA_OVM_STA__M 0x2
+#define ATV_TOP_COMM_INT_STA_OVM_STA__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STA_AMPTH_STA__B 2
+#define ATV_TOP_COMM_INT_STA_AMPTH_STA__W 1
+#define ATV_TOP_COMM_INT_STA_AMPTH_STA__M 0x4
+#define ATV_TOP_COMM_INT_STA_AMPTH_STA__PRE 0x0
+
+#define ATV_TOP_COMM_INT_MSK__A 0xC10006
+#define ATV_TOP_COMM_INT_MSK__W 16
+#define ATV_TOP_COMM_INT_MSK__M 0xFFFF
+#define ATV_TOP_COMM_INT_MSK__PRE 0x0
+
+#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__B 0
+#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__W 1
+#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__M 0x1
+#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__PRE 0x0
+
+#define ATV_TOP_COMM_INT_MSK_OVM_MSK__B 1
+#define ATV_TOP_COMM_INT_MSK_OVM_MSK__W 1
+#define ATV_TOP_COMM_INT_MSK_OVM_MSK__M 0x2
+#define ATV_TOP_COMM_INT_MSK_OVM_MSK__PRE 0x0
+
+#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__B 2
+#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__W 1
+#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__M 0x4
+#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STM__A 0xC10007
+#define ATV_TOP_COMM_INT_STM__W 16
+#define ATV_TOP_COMM_INT_STM__M 0xFFFF
+#define ATV_TOP_COMM_INT_STM__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STM_FAGC_STM__B 0
+#define ATV_TOP_COMM_INT_STM_FAGC_STM__W 1
+#define ATV_TOP_COMM_INT_STM_FAGC_STM__M 0x1
+#define ATV_TOP_COMM_INT_STM_FAGC_STM__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STM_OVM_STM__B 1
+#define ATV_TOP_COMM_INT_STM_OVM_STM__W 1
+#define ATV_TOP_COMM_INT_STM_OVM_STM__M 0x2
+#define ATV_TOP_COMM_INT_STM_OVM_STM__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STM_AMPTH_STM__B 2
+#define ATV_TOP_COMM_INT_STM_AMPTH_STM__W 1
+#define ATV_TOP_COMM_INT_STM_AMPTH_STM__M 0x4
+#define ATV_TOP_COMM_INT_STM_AMPTH_STM__PRE 0x0
+
+#define ATV_TOP_COMM_KEY__A 0xC1000F
+#define ATV_TOP_COMM_KEY__W 16
+#define ATV_TOP_COMM_KEY__M 0xFFFF
+#define ATV_TOP_COMM_KEY__PRE 0x0
+
+#define ATV_TOP_COMM_KEY_KEY__B 0
+#define ATV_TOP_COMM_KEY_KEY__W 16
+#define ATV_TOP_COMM_KEY_KEY__M 0xFFFF
+#define ATV_TOP_COMM_KEY_KEY__PRE 0x0
+#define ATV_TOP_COMM_KEY_KEY_KEY 0xFABA
+#define ATV_TOP_COMM_KEY_KEY_MIN 0x0
+#define ATV_TOP_COMM_KEY_KEY_MAX 0xFFFF
+
+#define ATV_TOP_CR_AMP_TH__A 0xC10010
+#define ATV_TOP_CR_AMP_TH__W 8
+#define ATV_TOP_CR_AMP_TH__M 0xFF
+#define ATV_TOP_CR_AMP_TH__PRE 0x8
+#define ATV_TOP_CR_AMP_TH_MN 0x8
+
+#define ATV_TOP_CR_CONT__A 0xC10011
+#define ATV_TOP_CR_CONT__W 9
+#define ATV_TOP_CR_CONT__M 0x1FF
+#define ATV_TOP_CR_CONT__PRE 0x9C
+
+#define ATV_TOP_CR_CONT_CR_P__B 0
+#define ATV_TOP_CR_CONT_CR_P__W 3
+#define ATV_TOP_CR_CONT_CR_P__M 0x7
+#define ATV_TOP_CR_CONT_CR_P__PRE 0x4
+#define ATV_TOP_CR_CONT_CR_P_MN 0x4
+#define ATV_TOP_CR_CONT_CR_P_FM 0x0
+
+#define ATV_TOP_CR_CONT_CR_D__B 3
+#define ATV_TOP_CR_CONT_CR_D__W 3
+#define ATV_TOP_CR_CONT_CR_D__M 0x38
+#define ATV_TOP_CR_CONT_CR_D__PRE 0x18
+#define ATV_TOP_CR_CONT_CR_D_MN 0x18
+#define ATV_TOP_CR_CONT_CR_D_FM 0x0
+
+#define ATV_TOP_CR_CONT_CR_I__B 6
+#define ATV_TOP_CR_CONT_CR_I__W 3
+#define ATV_TOP_CR_CONT_CR_I__M 0x1C0
+#define ATV_TOP_CR_CONT_CR_I__PRE 0x80
+#define ATV_TOP_CR_CONT_CR_I_MN 0x80
+#define ATV_TOP_CR_CONT_CR_I_FM 0x0
+
+#define ATV_TOP_CR_OVM_TH__A 0xC10012
+#define ATV_TOP_CR_OVM_TH__W 8
+#define ATV_TOP_CR_OVM_TH__M 0xFF
+#define ATV_TOP_CR_OVM_TH__PRE 0xA0
+#define ATV_TOP_CR_OVM_TH_MN 0xA0
+#define ATV_TOP_CR_OVM_TH_FM 0x0
+
+#define ATV_TOP_NOISE_TH__A 0xC10013
+#define ATV_TOP_NOISE_TH__W 4
+#define ATV_TOP_NOISE_TH__M 0xF
+#define ATV_TOP_NOISE_TH__PRE 0x8
+#define ATV_TOP_NOISE_TH_MN 0x8
+
+#define ATV_TOP_EQU0__A 0xC10014
+#define ATV_TOP_EQU0__W 9
+#define ATV_TOP_EQU0__M 0x1FF
+#define ATV_TOP_EQU0__PRE 0x1FB
+
+#define ATV_TOP_EQU0_EQU_C0__B 0
+#define ATV_TOP_EQU0_EQU_C0__W 9
+#define ATV_TOP_EQU0_EQU_C0__M 0x1FF
+#define ATV_TOP_EQU0_EQU_C0__PRE 0x1FB
+#define ATV_TOP_EQU0_EQU_C0_MN 0xFB
+
+#define ATV_TOP_EQU1__A 0xC10015
+#define ATV_TOP_EQU1__W 9
+#define ATV_TOP_EQU1__M 0x1FF
+#define ATV_TOP_EQU1__PRE 0x1CE
+
+#define ATV_TOP_EQU1_EQU_C1__B 0
+#define ATV_TOP_EQU1_EQU_C1__W 9
+#define ATV_TOP_EQU1_EQU_C1__M 0x1FF
+#define ATV_TOP_EQU1_EQU_C1__PRE 0x1CE
+#define ATV_TOP_EQU1_EQU_C1_MN 0xCE
+
+#define ATV_TOP_EQU2__A 0xC10016
+#define ATV_TOP_EQU2__W 9
+#define ATV_TOP_EQU2__M 0x1FF
+#define ATV_TOP_EQU2__PRE 0xD2
+
+#define ATV_TOP_EQU2_EQU_C2__B 0
+#define ATV_TOP_EQU2_EQU_C2__W 9
+#define ATV_TOP_EQU2_EQU_C2__M 0x1FF
+#define ATV_TOP_EQU2_EQU_C2__PRE 0xD2
+#define ATV_TOP_EQU2_EQU_C2_MN 0xD2
+
+#define ATV_TOP_EQU3__A 0xC10017
+#define ATV_TOP_EQU3__W 9
+#define ATV_TOP_EQU3__M 0x1FF
+#define ATV_TOP_EQU3__PRE 0x160
+
+#define ATV_TOP_EQU3_EQU_C3__B 0
+#define ATV_TOP_EQU3_EQU_C3__W 9
+#define ATV_TOP_EQU3_EQU_C3__M 0x1FF
+#define ATV_TOP_EQU3_EQU_C3__PRE 0x160
+#define ATV_TOP_EQU3_EQU_C3_MN 0x60
+
+#define ATV_TOP_ROT_MODE__A 0xC10018
+#define ATV_TOP_ROT_MODE__W 1
+#define ATV_TOP_ROT_MODE__M 0x1
+#define ATV_TOP_ROT_MODE__PRE 0x0
+#define ATV_TOP_ROT_MODE_AMPTH_DEPEND 0x0
+#define ATV_TOP_ROT_MODE_ALWAYS 0x1
+
+#define ATV_TOP_MOD_CONTROL__A 0xC10019
+#define ATV_TOP_MOD_CONTROL__W 12
+#define ATV_TOP_MOD_CONTROL__M 0xFFF
+#define ATV_TOP_MOD_CONTROL__PRE 0x5B1
+
+#define ATV_TOP_MOD_CONTROL_MOD_IR__B 0
+#define ATV_TOP_MOD_CONTROL_MOD_IR__W 3
+#define ATV_TOP_MOD_CONTROL_MOD_IR__M 0x7
+#define ATV_TOP_MOD_CONTROL_MOD_IR__PRE 0x1
+#define ATV_TOP_MOD_CONTROL_MOD_IR_MN 0x1
+#define ATV_TOP_MOD_CONTROL_MOD_IR_FM 0x0
+
+#define ATV_TOP_MOD_CONTROL_MOD_IF__B 3
+#define ATV_TOP_MOD_CONTROL_MOD_IF__W 4
+#define ATV_TOP_MOD_CONTROL_MOD_IF__M 0x78
+#define ATV_TOP_MOD_CONTROL_MOD_IF__PRE 0x30
+#define ATV_TOP_MOD_CONTROL_MOD_IF_MN 0x30
+#define ATV_TOP_MOD_CONTROL_MOD_IF_FM 0x0
+
+#define ATV_TOP_MOD_CONTROL_MOD_MODE__B 7
+#define ATV_TOP_MOD_CONTROL_MOD_MODE__W 1
+#define ATV_TOP_MOD_CONTROL_MOD_MODE__M 0x80
+#define ATV_TOP_MOD_CONTROL_MOD_MODE__PRE 0x80
+#define ATV_TOP_MOD_CONTROL_MOD_MODE_RISE 0x0
+#define ATV_TOP_MOD_CONTROL_MOD_MODE_RISE_FALL 0x80
+
+#define ATV_TOP_MOD_CONTROL_MOD_TH__B 8
+#define ATV_TOP_MOD_CONTROL_MOD_TH__W 4
+#define ATV_TOP_MOD_CONTROL_MOD_TH__M 0xF00
+#define ATV_TOP_MOD_CONTROL_MOD_TH__PRE 0x500
+#define ATV_TOP_MOD_CONTROL_MOD_TH_MN 0x500
+#define ATV_TOP_MOD_CONTROL_MOD_TH_FM 0x0
+
+#define ATV_TOP_STD__A 0xC1001A
+#define ATV_TOP_STD__W 2
+#define ATV_TOP_STD__M 0x3
+#define ATV_TOP_STD__PRE 0x0
+
+#define ATV_TOP_STD_MODE__B 0
+#define ATV_TOP_STD_MODE__W 1
+#define ATV_TOP_STD_MODE__M 0x1
+#define ATV_TOP_STD_MODE__PRE 0x0
+#define ATV_TOP_STD_MODE_MN 0x0
+#define ATV_TOP_STD_MODE_FM 0x1
+
+#define ATV_TOP_STD_VID_POL__B 1
+#define ATV_TOP_STD_VID_POL__W 1
+#define ATV_TOP_STD_VID_POL__M 0x2
+#define ATV_TOP_STD_VID_POL__PRE 0x0
+#define ATV_TOP_STD_VID_POL_NEG 0x0
+#define ATV_TOP_STD_VID_POL_POS 0x2
+
+#define ATV_TOP_VID_AMP__A 0xC1001B
+#define ATV_TOP_VID_AMP__W 12
+#define ATV_TOP_VID_AMP__M 0xFFF
+#define ATV_TOP_VID_AMP__PRE 0x380
+#define ATV_TOP_VID_AMP_MN 0x380
+#define ATV_TOP_VID_AMP_FM 0x0
+
+#define ATV_TOP_VID_PEAK__A 0xC1001C
+#define ATV_TOP_VID_PEAK__W 5
+#define ATV_TOP_VID_PEAK__M 0x1F
+#define ATV_TOP_VID_PEAK__PRE 0x1
+
+#define ATV_TOP_FAGC_TH__A 0xC1001D
+#define ATV_TOP_FAGC_TH__W 11
+#define ATV_TOP_FAGC_TH__M 0x7FF
+#define ATV_TOP_FAGC_TH__PRE 0x2B2
+#define ATV_TOP_FAGC_TH_MN 0x2B2
+
+#define ATV_TOP_SYNC_SLICE__A 0xC1001E
+#define ATV_TOP_SYNC_SLICE__W 11
+#define ATV_TOP_SYNC_SLICE__M 0x7FF
+#define ATV_TOP_SYNC_SLICE__PRE 0x243
+#define ATV_TOP_SYNC_SLICE_MN 0x243
+
+#define ATV_TOP_SIF_GAIN__A 0xC1001F
+#define ATV_TOP_SIF_GAIN__W 11
+#define ATV_TOP_SIF_GAIN__M 0x7FF
+#define ATV_TOP_SIF_GAIN__PRE 0x0
+
+#define ATV_TOP_SIF_TP__A 0xC10020
+#define ATV_TOP_SIF_TP__W 6
+#define ATV_TOP_SIF_TP__M 0x3F
+#define ATV_TOP_SIF_TP__PRE 0x0
+
+#define ATV_TOP_MOD_ACCU__A 0xC10021
+#define ATV_TOP_MOD_ACCU__W 10
+#define ATV_TOP_MOD_ACCU__M 0x3FF
+#define ATV_TOP_MOD_ACCU__PRE 0x0
+
+#define ATV_TOP_CR_FREQ__A 0xC10022
+#define ATV_TOP_CR_FREQ__W 8
+#define ATV_TOP_CR_FREQ__M 0xFF
+#define ATV_TOP_CR_FREQ__PRE 0x0
+
+#define ATV_TOP_CR_PHAD__A 0xC10023
+#define ATV_TOP_CR_PHAD__W 12
+#define ATV_TOP_CR_PHAD__M 0xFFF
+#define ATV_TOP_CR_PHAD__PRE 0x0
+
+#define ATV_TOP_AF_SIF_ATT__A 0xC10024
+#define ATV_TOP_AF_SIF_ATT__W 2
+#define ATV_TOP_AF_SIF_ATT__M 0x3
+#define ATV_TOP_AF_SIF_ATT__PRE 0x0
+#define ATV_TOP_AF_SIF_ATT_0DB 0x0
+#define ATV_TOP_AF_SIF_ATT_M3DB 0x1
+#define ATV_TOP_AF_SIF_ATT_M6DB 0x2
+#define ATV_TOP_AF_SIF_ATT_M9DB 0x3
+
+#define ATV_TOP_STDBY__A 0xC10025
+#define ATV_TOP_STDBY__W 2
+#define ATV_TOP_STDBY__M 0x3
+#define ATV_TOP_STDBY__PRE 0x1
+
+#define ATV_TOP_STDBY_SIF_STDBY__B 0
+#define ATV_TOP_STDBY_SIF_STDBY__W 1
+#define ATV_TOP_STDBY_SIF_STDBY__M 0x1
+#define ATV_TOP_STDBY_SIF_STDBY__PRE 0x1
+#define ATV_TOP_STDBY_SIF_STDBY_ACTIVE 0x0
+#define ATV_TOP_STDBY_SIF_STDBY_STANDBY 0x1
+
+#define ATV_TOP_STDBY_CVBS_STDBY__B 1
+#define ATV_TOP_STDBY_CVBS_STDBY__W 1
+#define ATV_TOP_STDBY_CVBS_STDBY__M 0x2
+#define ATV_TOP_STDBY_CVBS_STDBY__PRE 0x0
+#define ATV_TOP_STDBY_CVBS_STDBY_A1_ACTIVE 0x0
+#define ATV_TOP_STDBY_CVBS_STDBY_A1_STANDBY 0x2
+#define ATV_TOP_STDBY_CVBS_STDBY_A2_ACTIVE 0x2
+#define ATV_TOP_STDBY_CVBS_STDBY_A2_STANDBY 0x0
+
+#define ATV_TOP_OVERRIDE_SFR__A 0xC10026
+#define ATV_TOP_OVERRIDE_SFR__W 1
+#define ATV_TOP_OVERRIDE_SFR__M 0x1
+#define ATV_TOP_OVERRIDE_SFR__PRE 0x0
+#define ATV_TOP_OVERRIDE_SFR_ACTIVE 0x0
+#define ATV_TOP_OVERRIDE_SFR_OVERRIDE 0x1
+
+#define ATV_TOP_SFR_VID_GAIN__A 0xC10027
+#define ATV_TOP_SFR_VID_GAIN__W 16
+#define ATV_TOP_SFR_VID_GAIN__M 0xFFFF
+#define ATV_TOP_SFR_VID_GAIN__PRE 0x0
+
+#define ATV_TOP_SFR_AGC_RES__A 0xC10028
+#define ATV_TOP_SFR_AGC_RES__W 5
+#define ATV_TOP_SFR_AGC_RES__M 0x1F
+#define ATV_TOP_SFR_AGC_RES__PRE 0x0
+
+#define ATV_TOP_OVM_COMP__A 0xC10029
+#define ATV_TOP_OVM_COMP__W 12
+#define ATV_TOP_OVM_COMP__M 0xFFF
+#define ATV_TOP_OVM_COMP__PRE 0x0
+#define ATV_TOP_OUT_CONF__A 0xC1002A
+#define ATV_TOP_OUT_CONF__W 5
+#define ATV_TOP_OUT_CONF__M 0x1F
+#define ATV_TOP_OUT_CONF__PRE 0x0
+
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__B 0
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__W 1
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__M 0x1
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__PRE 0x0
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN_UNSIGNED 0x0
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN_SIGNED 0x1
+
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__B 1
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__W 1
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__M 0x2
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__PRE 0x0
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN_UNSIGNED 0x0
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN_SIGNED 0x2
+
+#define ATV_TOP_OUT_CONF_SIF20_SIGN__B 2
+#define ATV_TOP_OUT_CONF_SIF20_SIGN__W 1
+#define ATV_TOP_OUT_CONF_SIF20_SIGN__M 0x4
+#define ATV_TOP_OUT_CONF_SIF20_SIGN__PRE 0x0
+#define ATV_TOP_OUT_CONF_SIF20_SIGN_UNSIGNED 0x0
+#define ATV_TOP_OUT_CONF_SIF20_SIGN_SIGNED 0x4
+
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__B 3
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__W 1
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__M 0x8
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__PRE 0x0
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR_NORMAL 0x0
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR_BITREVERSED 0x8
+
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR__B 4
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR__W 1
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR__M 0x10
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR__PRE 0x0
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR_NORMAL 0x0
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR_BITREVERSED 0x10
+
+#define ATV_AFT_COMM_EXEC__A 0xFF0000
+#define ATV_AFT_COMM_EXEC__W 2
+#define ATV_AFT_COMM_EXEC__M 0x3
+#define ATV_AFT_COMM_EXEC__PRE 0x0
+#define ATV_AFT_COMM_EXEC_STOP 0x0
+#define ATV_AFT_COMM_EXEC_ACTIVE 0x1
+#define ATV_AFT_COMM_EXEC_HOLD 0x2
+
+#define ATV_AFT_TST__A 0xFF0010
+#define ATV_AFT_TST__W 4
+#define ATV_AFT_TST__M 0xF
+#define ATV_AFT_TST__PRE 0x0
+
+#define AUD_COMM_EXEC__A 0x1000000
+#define AUD_COMM_EXEC__W 2
+#define AUD_COMM_EXEC__M 0x3
+#define AUD_COMM_EXEC__PRE 0x0
+#define AUD_COMM_EXEC_STOP 0x0
+#define AUD_COMM_EXEC_ACTIVE 0x1
+
+#define AUD_COMM_MB__A 0x1000002
+#define AUD_COMM_MB__W 16
+#define AUD_COMM_MB__M 0xFFFF
+#define AUD_COMM_MB__PRE 0x0
+
+#define AUD_TOP_COMM_EXEC__A 0x1010000
+#define AUD_TOP_COMM_EXEC__W 2
+#define AUD_TOP_COMM_EXEC__M 0x3
+#define AUD_TOP_COMM_EXEC__PRE 0x0
+#define AUD_TOP_COMM_EXEC_STOP 0x0
+#define AUD_TOP_COMM_EXEC_ACTIVE 0x1
+
+#define AUD_TOP_COMM_MB__A 0x1010002
+#define AUD_TOP_COMM_MB__W 16
+#define AUD_TOP_COMM_MB__M 0xFFFF
+#define AUD_TOP_COMM_MB__PRE 0x0
+
+#define AUD_TOP_COMM_MB_CTL__B 0
+#define AUD_TOP_COMM_MB_CTL__W 1
+#define AUD_TOP_COMM_MB_CTL__M 0x1
+#define AUD_TOP_COMM_MB_CTL__PRE 0x0
+#define AUD_TOP_COMM_MB_CTL_CTR_OFF 0x0
+#define AUD_TOP_COMM_MB_CTL_CTR_ON 0x1
+
+#define AUD_TOP_COMM_MB_OBS__B 1
+#define AUD_TOP_COMM_MB_OBS__W 1
+#define AUD_TOP_COMM_MB_OBS__M 0x2
+#define AUD_TOP_COMM_MB_OBS__PRE 0x0
+#define AUD_TOP_COMM_MB_OBS_OBS_OFF 0x0
+#define AUD_TOP_COMM_MB_OBS_OBS_ON 0x2
+
+#define AUD_TOP_COMM_MB_MUX_CTRL__B 2
+#define AUD_TOP_COMM_MB_MUX_CTRL__W 4
+#define AUD_TOP_COMM_MB_MUX_CTRL__M 0x3C
+#define AUD_TOP_COMM_MB_MUX_CTRL__PRE 0x0
+#define AUD_TOP_COMM_MB_MUX_CTRL_DEMOD_TBO 0x0
+#define AUD_TOP_COMM_MB_MUX_CTRL_XDFP_IRQS 0x4
+#define AUD_TOP_COMM_MB_MUX_CTRL_OBSERVEPC 0x8
+#define AUD_TOP_COMM_MB_MUX_CTRL_SAOUT 0xC
+#define AUD_TOP_COMM_MB_MUX_CTRL_XDFP_SCHEQ 0x10
+
+#define AUD_TOP_COMM_MB_MUX_OBS__B 6
+#define AUD_TOP_COMM_MB_MUX_OBS__W 4
+#define AUD_TOP_COMM_MB_MUX_OBS__M 0x3C0
+#define AUD_TOP_COMM_MB_MUX_OBS__PRE 0x0
+#define AUD_TOP_COMM_MB_MUX_OBS_DEMOD_TBO 0x0
+#define AUD_TOP_COMM_MB_MUX_OBS_XDFP_IRQS 0x40
+#define AUD_TOP_COMM_MB_MUX_OBS_OBSERVEPC 0x80
+#define AUD_TOP_COMM_MB_MUX_OBS_SAOUT 0xC0
+#define AUD_TOP_COMM_MB_MUX_OBS_XDFP_SCHEQ 0x100
+
+#define AUD_TOP_TR_MDE__A 0x1010010
+#define AUD_TOP_TR_MDE__W 5
+#define AUD_TOP_TR_MDE__M 0x1F
+#define AUD_TOP_TR_MDE__PRE 0x18
+
+#define AUD_TOP_TR_MDE_FIFO_SIZE__B 0
+#define AUD_TOP_TR_MDE_FIFO_SIZE__W 4
+#define AUD_TOP_TR_MDE_FIFO_SIZE__M 0xF
+#define AUD_TOP_TR_MDE_FIFO_SIZE__PRE 0x8
+
+#define AUD_TOP_TR_MDE_RD_LOCK__B 4
+#define AUD_TOP_TR_MDE_RD_LOCK__W 1
+#define AUD_TOP_TR_MDE_RD_LOCK__M 0x10
+#define AUD_TOP_TR_MDE_RD_LOCK__PRE 0x10
+#define AUD_TOP_TR_MDE_RD_LOCK_NORMAL 0x0
+#define AUD_TOP_TR_MDE_RD_LOCK_LOCK 0x10
+
+#define AUD_TOP_TR_CTR__A 0x1010011
+#define AUD_TOP_TR_CTR__W 4
+#define AUD_TOP_TR_CTR__M 0xF
+#define AUD_TOP_TR_CTR__PRE 0x0
+
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY__B 0
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY__W 1
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY__M 0x1
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY__PRE 0x0
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY_NOT_READY 0x0
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY_READY 0x1
+
+#define AUD_TOP_TR_CTR_FIFO_EMPTY__B 1
+#define AUD_TOP_TR_CTR_FIFO_EMPTY__W 1
+#define AUD_TOP_TR_CTR_FIFO_EMPTY__M 0x2
+#define AUD_TOP_TR_CTR_FIFO_EMPTY__PRE 0x0
+#define AUD_TOP_TR_CTR_FIFO_EMPTY_NOT_EMPTY 0x0
+#define AUD_TOP_TR_CTR_FIFO_EMPTY_EMPTY 0x2
+
+#define AUD_TOP_TR_CTR_FIFO_LOCK__B 2
+#define AUD_TOP_TR_CTR_FIFO_LOCK__W 1
+#define AUD_TOP_TR_CTR_FIFO_LOCK__M 0x4
+#define AUD_TOP_TR_CTR_FIFO_LOCK__PRE 0x0
+#define AUD_TOP_TR_CTR_FIFO_LOCK_UNLOCKED 0x0
+#define AUD_TOP_TR_CTR_FIFO_LOCK_LOCKED 0x4
+
+#define AUD_TOP_TR_CTR_FIFO_FULL__B 3
+#define AUD_TOP_TR_CTR_FIFO_FULL__W 1
+#define AUD_TOP_TR_CTR_FIFO_FULL__M 0x8
+#define AUD_TOP_TR_CTR_FIFO_FULL__PRE 0x0
+#define AUD_TOP_TR_CTR_FIFO_FULL_EMPTY 0x0
+#define AUD_TOP_TR_CTR_FIFO_FULL_FULL 0x8
+
+#define AUD_TOP_TR_RD_REG__A 0x1010012
+#define AUD_TOP_TR_RD_REG__W 16
+#define AUD_TOP_TR_RD_REG__M 0xFFFF
+#define AUD_TOP_TR_RD_REG__PRE 0x0
+
+#define AUD_TOP_TR_RD_REG_RESULT__B 0
+#define AUD_TOP_TR_RD_REG_RESULT__W 16
+#define AUD_TOP_TR_RD_REG_RESULT__M 0xFFFF
+#define AUD_TOP_TR_RD_REG_RESULT__PRE 0x0
+
+#define AUD_TOP_TR_TIMER__A 0x1010013
+#define AUD_TOP_TR_TIMER__W 16
+#define AUD_TOP_TR_TIMER__M 0xFFFF
+#define AUD_TOP_TR_TIMER__PRE 0x0
+
+#define AUD_TOP_TR_TIMER_CYCLES__B 0
+#define AUD_TOP_TR_TIMER_CYCLES__W 16
+#define AUD_TOP_TR_TIMER_CYCLES__M 0xFFFF
+#define AUD_TOP_TR_TIMER_CYCLES__PRE 0x0
+
+#define AUD_TOP_DEMOD_TBO_SEL__A 0x1010014
+#define AUD_TOP_DEMOD_TBO_SEL__W 5
+#define AUD_TOP_DEMOD_TBO_SEL__M 0x1F
+#define AUD_TOP_DEMOD_TBO_SEL__PRE 0x0
+
+#define AUD_DEM_WR_MODUS__A 0x1030030
+#define AUD_DEM_WR_MODUS__W 16
+#define AUD_DEM_WR_MODUS__M 0xFFFF
+#define AUD_DEM_WR_MODUS__PRE 0x0
+
+#define AUD_DEM_WR_MODUS_MOD_ASS__B 0
+#define AUD_DEM_WR_MODUS_MOD_ASS__W 1
+#define AUD_DEM_WR_MODUS_MOD_ASS__M 0x1
+#define AUD_DEM_WR_MODUS_MOD_ASS__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_ASS_OFF 0x0
+#define AUD_DEM_WR_MODUS_MOD_ASS_ON 0x1
+
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR__B 1
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR__W 1
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR__M 0x2
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR_DISABLE 0x0
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR_ENABLE 0x2
+
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__B 2
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__W 1
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__M 0x4
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG_ENABLED 0x0
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG_DISABLED 0x4
+
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A__B 8
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A__W 1
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A__M 0x100
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A_NORMAL 0x0
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A_HIGH_DEVIATION 0x100
+
+#define AUD_DEM_WR_MODUS_MOD_CM_A__B 9
+#define AUD_DEM_WR_MODUS_MOD_CM_A__W 1
+#define AUD_DEM_WR_MODUS_MOD_CM_A__M 0x200
+#define AUD_DEM_WR_MODUS_MOD_CM_A__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_CM_A_MUTE 0x0
+#define AUD_DEM_WR_MODUS_MOD_CM_A_NOISE 0x200
+
+#define AUD_DEM_WR_MODUS_MOD_CM_B__B 10
+#define AUD_DEM_WR_MODUS_MOD_CM_B__W 1
+#define AUD_DEM_WR_MODUS_MOD_CM_B__M 0x400
+#define AUD_DEM_WR_MODUS_MOD_CM_B__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_CM_B_MUTE 0x0
+#define AUD_DEM_WR_MODUS_MOD_CM_B_NOISE 0x400
+
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO__B 11
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO__W 1
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO__M 0x800
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO_US_75U 0x0
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO_EU_50U 0x800
+
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__B 12
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__W 1
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__M 0x1000
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ_SECAM 0x0
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ_D_K 0x1000
+
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__B 13
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__W 2
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__M 0x6000
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_M_KOREA 0x0
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_M_BTSC 0x2000
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_M_EIAJ 0x4000
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_CHROMA 0x6000
+
+#define AUD_DEM_WR_MODUS_MOD_BTSC__B 15
+#define AUD_DEM_WR_MODUS_MOD_BTSC__W 1
+#define AUD_DEM_WR_MODUS_MOD_BTSC__M 0x8000
+#define AUD_DEM_WR_MODUS_MOD_BTSC__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_BTSC_BTSC_STEREO 0x0
+#define AUD_DEM_WR_MODUS_MOD_BTSC_BTSC_SAP 0x8000
+
+#define AUD_DEM_WR_STANDARD_SEL__A 0x1030020
+#define AUD_DEM_WR_STANDARD_SEL__W 16
+#define AUD_DEM_WR_STANDARD_SEL__M 0xFFFF
+#define AUD_DEM_WR_STANDARD_SEL__PRE 0x0
+
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__B 0
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__W 12
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__M 0xFFF
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__PRE 0x0
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_AUTO 0x1
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_M_KOREA 0x2
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BG_FM 0x3
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K1 0x4
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K2 0x5
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K3 0x7
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BG_NICAM_FM 0x8
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_L_NICAM_AM 0x9
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_I_NICAM_FM 0xA
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K_NICAM_FM 0xB
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BTSC_STEREO 0x20
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BTSC_SAP 0x21
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_EIA_J 0x30
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_FM_RADIO 0x40
+
+#define AUD_DEM_RD_STANDARD_RES__A 0x102007E
+#define AUD_DEM_RD_STANDARD_RES__W 16
+#define AUD_DEM_RD_STANDARD_RES__M 0xFFFF
+#define AUD_DEM_RD_STANDARD_RES__PRE 0x0
+
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__B 0
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__W 16
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__M 0xFFFF
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__PRE 0x0
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_NO_SOUND_STANDARD 0x0
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_NTSC_M_DUAL_CARRIER_FM 0x2
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_B_G_DUAL_CARRIER_FM 0x3
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K1_DUAL_CARRIER_FM 0x4
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K2_DUAL_CARRIER_FM 0x5
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K3_DUAL_CARRIER_FM 0x7
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_B_G_NICAM_FM 0x8
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_L_NICAM_AM 0x9
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_I_NICAM_FM 0xA
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K_NICAM_FM 0xB
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_BTSC_STEREO 0x20
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_BTSC_MONO_SAP 0x21
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_NTSC_EIA_J 0x30
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_FM_RADIO 0x40
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_DETECTION_STILL_ACTIVE 0x7FF
+
+#define AUD_DEM_RD_STATUS__A 0x1020200
+#define AUD_DEM_RD_STATUS__W 16
+#define AUD_DEM_RD_STATUS__M 0xFFFF
+#define AUD_DEM_RD_STATUS__PRE 0x0
+
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__B 0
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__W 1
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__M 0x1
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS_NO_RDS_DATA 0x0
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS_NEW_RDS_DATA 0x1
+
+#define AUD_DEM_RD_STATUS_STAT_CARR_A__B 1
+#define AUD_DEM_RD_STATUS_STAT_CARR_A__W 1
+#define AUD_DEM_RD_STATUS_STAT_CARR_A__M 0x2
+#define AUD_DEM_RD_STATUS_STAT_CARR_A__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_CARR_A_DETECTED 0x0
+#define AUD_DEM_RD_STATUS_STAT_CARR_A_NOT_DETECTED 0x2
+
+#define AUD_DEM_RD_STATUS_STAT_CARR_B__B 2
+#define AUD_DEM_RD_STATUS_STAT_CARR_B__W 1
+#define AUD_DEM_RD_STATUS_STAT_CARR_B__M 0x4
+#define AUD_DEM_RD_STATUS_STAT_CARR_B__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_CARR_B_DETECTED 0x0
+#define AUD_DEM_RD_STATUS_STAT_CARR_B_NOT_DETECTED 0x4
+
+#define AUD_DEM_RD_STATUS_STAT_NICAM__B 5
+#define AUD_DEM_RD_STATUS_STAT_NICAM__W 1
+#define AUD_DEM_RD_STATUS_STAT_NICAM__M 0x20
+#define AUD_DEM_RD_STATUS_STAT_NICAM__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_NICAM_NO_NICAM 0x0
+#define AUD_DEM_RD_STATUS_STAT_NICAM_NICAM_DETECTED 0x20
+
+#define AUD_DEM_RD_STATUS_STAT_STEREO__B 6
+#define AUD_DEM_RD_STATUS_STAT_STEREO__W 1
+#define AUD_DEM_RD_STATUS_STAT_STEREO__M 0x40
+#define AUD_DEM_RD_STATUS_STAT_STEREO__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_STEREO_NO_STEREO 0x0
+#define AUD_DEM_RD_STATUS_STAT_STEREO_STEREO 0x40
+
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__B 7
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__W 1
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__M 0x80
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO_DEPENDENT_FM_MONO_PROGRAM 0x0
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO_INDEPENDENT_FM_MONO_PROGRAM 0x80
+
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__B 8
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__W 1
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__M 0x100
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP_NO_SAP 0x0
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP_SAP 0x100
+
+#define AUD_DEM_RD_STATUS_BAD_NICAM__B 9
+#define AUD_DEM_RD_STATUS_BAD_NICAM__W 1
+#define AUD_DEM_RD_STATUS_BAD_NICAM__M 0x200
+#define AUD_DEM_RD_STATUS_BAD_NICAM__PRE 0x0
+#define AUD_DEM_RD_STATUS_BAD_NICAM_OK 0x0
+#define AUD_DEM_RD_STATUS_BAD_NICAM_BAD 0x200
+
+#define AUD_DEM_RD_RDS_ARRAY_CNT__A 0x102020F
+#define AUD_DEM_RD_RDS_ARRAY_CNT__W 12
+#define AUD_DEM_RD_RDS_ARRAY_CNT__M 0xFFF
+#define AUD_DEM_RD_RDS_ARRAY_CNT__PRE 0x0
+
+#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__B 0
+#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__W 12
+#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__M 0xFFF
+#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__PRE 0x0
+#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT_RDS_DATA_NOT_VALID 0xFFF
+
+#define AUD_DEM_RD_RDS_DATA__A 0x1020210
+#define AUD_DEM_RD_RDS_DATA__W 12
+#define AUD_DEM_RD_RDS_DATA__M 0xFFF
+#define AUD_DEM_RD_RDS_DATA__PRE 0x0
+
+#define AUD_DSP_WR_FM_PRESC__A 0x105000E
+#define AUD_DSP_WR_FM_PRESC__W 16
+#define AUD_DSP_WR_FM_PRESC__M 0xFFFF
+#define AUD_DSP_WR_FM_PRESC__PRE 0x0
+
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__B 8
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__W 8
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__M 0xFF00
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__PRE 0x0
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_28_KHZ_FM_DEVIATION 0x7F00
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_50_KHZ_FM_DEVIATION 0x4800
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_75_KHZ_FM_DEVIATION 0x3000
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_100_KHZ_FM_DEVIATION 0x2400
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_150_KHZ_FM_DEVIATION 0x1800
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_180_KHZ_FM_DEVIATION 0x1300
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_380_KHZ_FM_DEVIATION 0x900
+
+#define AUD_DSP_WR_NICAM_PRESC__A 0x1050010
+#define AUD_DSP_WR_NICAM_PRESC__W 16
+#define AUD_DSP_WR_NICAM_PRESC__M 0xFFFF
+#define AUD_DSP_WR_NICAM_PRESC__PRE 0x0
+#define AUD_DSP_WR_VOLUME__A 0x1050000
+#define AUD_DSP_WR_VOLUME__W 16
+#define AUD_DSP_WR_VOLUME__M 0xFFFF
+#define AUD_DSP_WR_VOLUME__PRE 0x0
+
+#define AUD_DSP_WR_VOLUME_VOL_MAIN__B 8
+#define AUD_DSP_WR_VOLUME_VOL_MAIN__W 8
+#define AUD_DSP_WR_VOLUME_VOL_MAIN__M 0xFF00
+#define AUD_DSP_WR_VOLUME_VOL_MAIN__PRE 0x0
+
+#define AUD_DSP_WR_SRC_I2S_MATR__A 0x1050038
+#define AUD_DSP_WR_SRC_I2S_MATR__W 16
+#define AUD_DSP_WR_SRC_I2S_MATR__M 0xFFFF
+#define AUD_DSP_WR_SRC_I2S_MATR__PRE 0x0
+
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__B 8
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__W 8
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__M 0xFF00
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__PRE 0x0
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_MONO 0x0
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_STEREO_AB 0x100
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_STEREO_A 0x300
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_STEREO_B 0x400
+
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__B 0
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__W 8
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__M 0xFF
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__PRE 0x0
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_SOUND_A 0x0
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_SOUND_B 0x10
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_STEREO 0x20
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_MONO 0x30
+
+#define AUD_DSP_WR_AVC__A 0x1050029
+#define AUD_DSP_WR_AVC__W 16
+#define AUD_DSP_WR_AVC__M 0xFFFF
+#define AUD_DSP_WR_AVC__PRE 0x0
+
+#define AUD_DSP_WR_AVC_AVC_ON__B 14
+#define AUD_DSP_WR_AVC_AVC_ON__W 2
+#define AUD_DSP_WR_AVC_AVC_ON__M 0xC000
+#define AUD_DSP_WR_AVC_AVC_ON__PRE 0x0
+#define AUD_DSP_WR_AVC_AVC_ON_OFF 0x0
+#define AUD_DSP_WR_AVC_AVC_ON_ON 0xC000
+
+#define AUD_DSP_WR_AVC_AVC_DECAY__B 8
+#define AUD_DSP_WR_AVC_AVC_DECAY__W 4
+#define AUD_DSP_WR_AVC_AVC_DECAY__M 0xF00
+#define AUD_DSP_WR_AVC_AVC_DECAY__PRE 0x0
+#define AUD_DSP_WR_AVC_AVC_DECAY_8_SEC 0x800
+#define AUD_DSP_WR_AVC_AVC_DECAY_4_SEC 0x400
+#define AUD_DSP_WR_AVC_AVC_DECAY_2_SEC 0x200
+#define AUD_DSP_WR_AVC_AVC_DECAY_20_MSEC 0x100
+
+#define AUD_DSP_WR_AVC_AVC_REF_LEV__B 4
+#define AUD_DSP_WR_AVC_AVC_REF_LEV__W 4
+#define AUD_DSP_WR_AVC_AVC_REF_LEV__M 0xF0
+#define AUD_DSP_WR_AVC_AVC_REF_LEV__PRE 0x0
+
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT__B 2
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT__W 2
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT__M 0xC
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT__PRE 0x0
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT_24DB 0x0
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT_18DB 0x4
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT_12DB 0x8
+
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__B 0
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__W 2
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__M 0x3
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__PRE 0x0
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN_6DB 0x0
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN_12DB 0x1
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN_0DB 0x3
+
+#define AUD_DSP_WR_QPEAK__A 0x105000C
+#define AUD_DSP_WR_QPEAK__W 16
+#define AUD_DSP_WR_QPEAK__M 0xFFFF
+#define AUD_DSP_WR_QPEAK__PRE 0x0
+
+#define AUD_DSP_WR_QPEAK_SRC_QP__B 8
+#define AUD_DSP_WR_QPEAK_SRC_QP__W 8
+#define AUD_DSP_WR_QPEAK_SRC_QP__M 0xFF00
+#define AUD_DSP_WR_QPEAK_SRC_QP__PRE 0x0
+#define AUD_DSP_WR_QPEAK_SRC_QP_MONO 0x0
+#define AUD_DSP_WR_QPEAK_SRC_QP_STEREO_AB 0x100
+#define AUD_DSP_WR_QPEAK_SRC_QP_STEREO_A 0x300
+#define AUD_DSP_WR_QPEAK_SRC_QP_STEREO_B 0x400
+
+#define AUD_DSP_WR_QPEAK_MAT_QP__B 0
+#define AUD_DSP_WR_QPEAK_MAT_QP__W 8
+#define AUD_DSP_WR_QPEAK_MAT_QP__M 0xFF
+#define AUD_DSP_WR_QPEAK_MAT_QP__PRE 0x0
+#define AUD_DSP_WR_QPEAK_MAT_QP_SOUND_A 0x0
+#define AUD_DSP_WR_QPEAK_MAT_QP_SOUND_B 0x10
+#define AUD_DSP_WR_QPEAK_MAT_QP_STEREO 0x20
+#define AUD_DSP_WR_QPEAK_MAT_QP_MONO 0x30
+
+#define AUD_DSP_RD_QPEAK_L__A 0x1040019
+#define AUD_DSP_RD_QPEAK_L__W 16
+#define AUD_DSP_RD_QPEAK_L__M 0xFFFF
+#define AUD_DSP_RD_QPEAK_L__PRE 0x0
+
+#define AUD_DSP_RD_QPEAK_R__A 0x104001A
+#define AUD_DSP_RD_QPEAK_R__W 16
+#define AUD_DSP_RD_QPEAK_R__M 0xFFFF
+#define AUD_DSP_RD_QPEAK_R__PRE 0x0
+
+#define AUD_DSP_WR_BEEPER__A 0x1050014
+#define AUD_DSP_WR_BEEPER__W 16
+#define AUD_DSP_WR_BEEPER__M 0xFFFF
+#define AUD_DSP_WR_BEEPER__PRE 0x0
+
+#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__B 8
+#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__W 7
+#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__M 0x7F00
+#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__PRE 0x0
+
+#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__B 0
+#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__W 7
+#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__M 0x7F
+#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__PRE 0x0
+
+#define AUD_DEM_WR_I2S_CONFIG2__A 0x1030050
+#define AUD_DEM_WR_I2S_CONFIG2__W 16
+#define AUD_DEM_WR_I2S_CONFIG2__M 0xFFFF
+#define AUD_DEM_WR_I2S_CONFIG2__PRE 0x0
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__B 6
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__M 0x40
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL_NORMAL 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL_INVERTED 0x40
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__B 4
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__M 0x10
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE_DISABLE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE_ENABLE 0x10
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__B 3
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__M 0x8
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST_MASTER 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST_SLAVE 0x8
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__B 2
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__M 0x4
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL_LEFT_LOW 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL_LEFT_HIGH 0x4
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__B 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__M 0x2
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE_NO_DELAY 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE_DELAY 0x2
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__B 0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__M 0x1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN_BIT_32 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN_BIT_16 0x1
+
+#define AUD_DSP_WR_I2S_OUT_FS__A 0x105002A
+#define AUD_DSP_WR_I2S_OUT_FS__W 16
+#define AUD_DSP_WR_I2S_OUT_FS__M 0xFFFF
+#define AUD_DSP_WR_I2S_OUT_FS__PRE 0x0
+
+#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__B 0
+#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__W 16
+#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__M 0xFFFF
+#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__PRE 0x0
+
+#define AUD_DSP_WR_AV_SYNC__A 0x105002B
+#define AUD_DSP_WR_AV_SYNC__W 16
+#define AUD_DSP_WR_AV_SYNC__M 0xFFFF
+#define AUD_DSP_WR_AV_SYNC__PRE 0x0
+
+#define AUD_DSP_WR_AV_SYNC_AV_ON__B 15
+#define AUD_DSP_WR_AV_SYNC_AV_ON__W 1
+#define AUD_DSP_WR_AV_SYNC_AV_ON__M 0x8000
+#define AUD_DSP_WR_AV_SYNC_AV_ON__PRE 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_ON_DISABLE 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_ON_ENABLE 0x8000
+
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__B 14
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__W 1
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__M 0x4000
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__PRE 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ_MONOCHROME 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ_NTSC 0x4000
+
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__B 0
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__W 2
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__M 0x3
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__PRE 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_AUTO 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_PAL_SECAM 0x1
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_NTSC 0x2
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_MONOCHROME 0x3
+
+#define AUD_DSP_RD_STATUS2__A 0x104007B
+#define AUD_DSP_RD_STATUS2__W 16
+#define AUD_DSP_RD_STATUS2__M 0xFFFF
+#define AUD_DSP_RD_STATUS2__PRE 0x0
+
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE__B 15
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE__W 1
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE__M 0x8000
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE__PRE 0x0
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE_NO_SYNC 0x0
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE_SYNC_ACTIVE 0x8000
+
+#define AUD_DSP_RD_XDFP_FW__A 0x104001D
+#define AUD_DSP_RD_XDFP_FW__W 16
+#define AUD_DSP_RD_XDFP_FW__M 0xFFFF
+#define AUD_DSP_RD_XDFP_FW__PRE 0x344
+
+#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__B 0
+#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__W 16
+#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__M 0xFFFF
+#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__PRE 0x344
+
+#define AUD_DSP_RD_XFP_FW__A 0x10404B8
+#define AUD_DSP_RD_XFP_FW__W 16
+#define AUD_DSP_RD_XFP_FW__M 0xFFFF
+#define AUD_DSP_RD_XFP_FW__PRE 0x42
+
+#define AUD_DSP_RD_XFP_FW_FP_FW_REV__B 0
+#define AUD_DSP_RD_XFP_FW_FP_FW_REV__W 16
+#define AUD_DSP_RD_XFP_FW_FP_FW_REV__M 0xFFFF
+#define AUD_DSP_RD_XFP_FW_FP_FW_REV__PRE 0x42
+
+#define AUD_DEM_WR_DCO_B_HI__A 0x103009B
+#define AUD_DEM_WR_DCO_B_HI__W 16
+#define AUD_DEM_WR_DCO_B_HI__M 0xFFFF
+#define AUD_DEM_WR_DCO_B_HI__PRE 0x0
+
+#define AUD_DEM_WR_DCO_B_LO__A 0x1030093
+#define AUD_DEM_WR_DCO_B_LO__W 16
+#define AUD_DEM_WR_DCO_B_LO__M 0xFFFF
+#define AUD_DEM_WR_DCO_B_LO__PRE 0x0
+
+#define AUD_DEM_WR_DCO_A_HI__A 0x10300AB
+#define AUD_DEM_WR_DCO_A_HI__W 16
+#define AUD_DEM_WR_DCO_A_HI__M 0xFFFF
+#define AUD_DEM_WR_DCO_A_HI__PRE 0x0
+
+#define AUD_DEM_WR_DCO_A_LO__A 0x10300A3
+#define AUD_DEM_WR_DCO_A_LO__W 16
+#define AUD_DEM_WR_DCO_A_LO__M 0xFFFF
+#define AUD_DEM_WR_DCO_A_LO__PRE 0x0
+#define AUD_DEM_WR_NICAM_THRSHLD__A 0x1030021
+#define AUD_DEM_WR_NICAM_THRSHLD__W 16
+#define AUD_DEM_WR_NICAM_THRSHLD__M 0xFFFF
+#define AUD_DEM_WR_NICAM_THRSHLD__PRE 0x2BC
+
+#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__B 0
+#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__W 12
+#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__M 0xFFF
+#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__PRE 0x2BC
+
+#define AUD_DEM_WR_A2_THRSHLD__A 0x1030022
+#define AUD_DEM_WR_A2_THRSHLD__W 16
+#define AUD_DEM_WR_A2_THRSHLD__M 0xFFFF
+#define AUD_DEM_WR_A2_THRSHLD__PRE 0x190
+
+#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__B 0
+#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__W 12
+#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__M 0xFFF
+#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__PRE 0x190
+
+#define AUD_DEM_WR_BTSC_THRSHLD__A 0x1030023
+#define AUD_DEM_WR_BTSC_THRSHLD__W 16
+#define AUD_DEM_WR_BTSC_THRSHLD__M 0xFFFF
+#define AUD_DEM_WR_BTSC_THRSHLD__PRE 0xC
+
+#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__B 0
+#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__W 12
+#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__M 0xFFF
+#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__PRE 0xC
+
+#define AUD_DEM_WR_CM_A_THRSHLD__A 0x1030024
+#define AUD_DEM_WR_CM_A_THRSHLD__W 16
+#define AUD_DEM_WR_CM_A_THRSHLD__M 0xFFFF
+#define AUD_DEM_WR_CM_A_THRSHLD__PRE 0x2A
+
+#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__B 0
+#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__W 12
+#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__M 0xFFF
+#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__PRE 0x2A
+
+#define AUD_DEM_WR_CM_B_THRSHLD__A 0x1030025
+#define AUD_DEM_WR_CM_B_THRSHLD__W 16
+#define AUD_DEM_WR_CM_B_THRSHLD__M 0xFFFF
+#define AUD_DEM_WR_CM_B_THRSHLD__PRE 0x2A
+
+#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__B 0
+#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__W 12
+#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__M 0xFFF
+#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__PRE 0x2A
+
+#define AUD_DEM_RD_NIC_C_AD_BITS__A 0x1020023
+#define AUD_DEM_RD_NIC_C_AD_BITS__W 16
+#define AUD_DEM_RD_NIC_C_AD_BITS__M 0xFFFF
+#define AUD_DEM_RD_NIC_C_AD_BITS__PRE 0x0
+
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__B 0
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__W 1
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__M 0x1
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__PRE 0x0
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC_NOT_SYNCED 0x0
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC_SYNCED 0x1
+
+#define AUD_DEM_RD_NIC_C_AD_BITS_C__B 1
+#define AUD_DEM_RD_NIC_C_AD_BITS_C__W 4
+#define AUD_DEM_RD_NIC_C_AD_BITS_C__M 0x1E
+#define AUD_DEM_RD_NIC_C_AD_BITS_C__PRE 0x0
+
+#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__B 5
+#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__W 3
+#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__M 0xE0
+#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__PRE 0x0
+
+#define AUD_DEM_RD_NIC_ADD_BITS_HI__A 0x1020038
+#define AUD_DEM_RD_NIC_ADD_BITS_HI__W 16
+#define AUD_DEM_RD_NIC_ADD_BITS_HI__M 0xFFFF
+#define AUD_DEM_RD_NIC_ADD_BITS_HI__PRE 0x0
+
+#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__B 0
+#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__W 8
+#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__M 0xFF
+#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__PRE 0x0
+
+#define AUD_DEM_RD_NIC_CIB__A 0x1020038
+#define AUD_DEM_RD_NIC_CIB__W 16
+#define AUD_DEM_RD_NIC_CIB__M 0xFFFF
+#define AUD_DEM_RD_NIC_CIB__PRE 0x0
+
+#define AUD_DEM_RD_NIC_CIB_CIB2__B 0
+#define AUD_DEM_RD_NIC_CIB_CIB2__W 1
+#define AUD_DEM_RD_NIC_CIB_CIB2__M 0x1
+#define AUD_DEM_RD_NIC_CIB_CIB2__PRE 0x0
+
+#define AUD_DEM_RD_NIC_CIB_CIB1__B 1
+#define AUD_DEM_RD_NIC_CIB_CIB1__W 1
+#define AUD_DEM_RD_NIC_CIB_CIB1__M 0x2
+#define AUD_DEM_RD_NIC_CIB_CIB1__PRE 0x0
+
+#define AUD_DEM_RD_NIC_ERROR_RATE__A 0x1020057
+#define AUD_DEM_RD_NIC_ERROR_RATE__W 16
+#define AUD_DEM_RD_NIC_ERROR_RATE__M 0xFFFF
+#define AUD_DEM_RD_NIC_ERROR_RATE__PRE 0x0
+
+#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__B 0
+#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__W 12
+#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__M 0xFFF
+#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__PRE 0x0
+
+#define AUD_DEM_WR_FM_DEEMPH__A 0x103000F
+#define AUD_DEM_WR_FM_DEEMPH__W 16
+#define AUD_DEM_WR_FM_DEEMPH__M 0xFFFF
+#define AUD_DEM_WR_FM_DEEMPH__PRE 0x0
+#define AUD_DEM_WR_FM_DEEMPH_50US 0x0
+#define AUD_DEM_WR_FM_DEEMPH_75US 0x1
+#define AUD_DEM_WR_FM_DEEMPH_OFF 0x3F
+
+#define AUD_DEM_WR_FM_MATRIX__A 0x103006F
+#define AUD_DEM_WR_FM_MATRIX__W 16
+#define AUD_DEM_WR_FM_MATRIX__M 0xFFFF
+#define AUD_DEM_WR_FM_MATRIX__PRE 0x0
+#define AUD_DEM_WR_FM_MATRIX_NO_MATRIX 0x0
+#define AUD_DEM_WR_FM_MATRIX_GERMAN_MATRIX 0x1
+#define AUD_DEM_WR_FM_MATRIX_KOREAN_MATRIX 0x2
+#define AUD_DEM_WR_FM_MATRIX_SOUND_A 0x3
+#define AUD_DEM_WR_FM_MATRIX_SOUND_B 0x4
+
+#define AUD_DSP_RD_FM_IDENT_VALUE__A 0x1040018
+#define AUD_DSP_RD_FM_IDENT_VALUE__W 16
+#define AUD_DSP_RD_FM_IDENT_VALUE__M 0xFFFF
+#define AUD_DSP_RD_FM_IDENT_VALUE__PRE 0x0
+
+#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__B 8
+#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__W 8
+#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__M 0xFF00
+#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__PRE 0x0
+
+#define AUD_DSP_RD_FM_DC_LEVEL_A__A 0x104001B
+#define AUD_DSP_RD_FM_DC_LEVEL_A__W 16
+#define AUD_DSP_RD_FM_DC_LEVEL_A__M 0xFFFF
+#define AUD_DSP_RD_FM_DC_LEVEL_A__PRE 0x0
+
+#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__B 0
+#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__W 16
+#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__M 0xFFFF
+#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__PRE 0x0
+
+#define AUD_DSP_RD_FM_DC_LEVEL_B__A 0x104001C
+#define AUD_DSP_RD_FM_DC_LEVEL_B__W 16
+#define AUD_DSP_RD_FM_DC_LEVEL_B__M 0xFFFF
+#define AUD_DSP_RD_FM_DC_LEVEL_B__PRE 0x0
+
+#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__B 0
+#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__W 16
+#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__M 0xFFFF
+#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__PRE 0x0
+
+#define AUD_DEM_WR_FM_DC_NOTCH_SW__A 0x1030017
+#define AUD_DEM_WR_FM_DC_NOTCH_SW__W 16
+#define AUD_DEM_WR_FM_DC_NOTCH_SW__M 0xFFFF
+#define AUD_DEM_WR_FM_DC_NOTCH_SW__PRE 0x0
+
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__B 0
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__W 16
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__M 0xFFFF
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__PRE 0x0
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW_ON 0x0
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW_OFF 0x3F
+
+#define AUD_DSP_WR_SYNC_OUT__A 0x1050026
+#define AUD_DSP_WR_SYNC_OUT__W 16
+#define AUD_DSP_WR_SYNC_OUT__M 0xFFFF
+#define AUD_DSP_WR_SYNC_OUT__PRE 0x0
+#define AUD_DSP_WR_SYNC_OUT_OFF 0x0
+#define AUD_DSP_WR_SYNC_OUT_SYNCHRONOUS 0x1
+
+#define AUD_XFP_DRAM_1K__A 0x1060000
+#define AUD_XFP_DRAM_1K__W 16
+#define AUD_XFP_DRAM_1K__M 0xFFFF
+#define AUD_XFP_DRAM_1K__PRE 0x0
+#define AUD_XFP_DRAM_1K_D__B 0
+#define AUD_XFP_DRAM_1K_D__W 16
+#define AUD_XFP_DRAM_1K_D__M 0xFFFF
+#define AUD_XFP_DRAM_1K_D__PRE 0x0
+
+#define AUD_XFP_PRAM_4K__A 0x1070000
+#define AUD_XFP_PRAM_4K__W 16
+#define AUD_XFP_PRAM_4K__M 0xFFFF
+#define AUD_XFP_PRAM_4K__PRE 0x0
+#define AUD_XFP_PRAM_4K_D__B 0
+#define AUD_XFP_PRAM_4K_D__W 16
+#define AUD_XFP_PRAM_4K_D__M 0xFFFF
+#define AUD_XFP_PRAM_4K_D__PRE 0x0
+
+#define AUD_XDFP_DRAM_1K__A 0x1080000
+#define AUD_XDFP_DRAM_1K__W 16
+#define AUD_XDFP_DRAM_1K__M 0xFFFF
+#define AUD_XDFP_DRAM_1K__PRE 0x0
+#define AUD_XDFP_DRAM_1K_D__B 0
+#define AUD_XDFP_DRAM_1K_D__W 16
+#define AUD_XDFP_DRAM_1K_D__M 0xFFFF
+#define AUD_XDFP_DRAM_1K_D__PRE 0x0
+
+#define AUD_XDFP_PRAM_4K__A 0x1090000
+#define AUD_XDFP_PRAM_4K__W 16
+#define AUD_XDFP_PRAM_4K__M 0xFFFF
+#define AUD_XDFP_PRAM_4K__PRE 0x0
+#define AUD_XDFP_PRAM_4K_D__B 0
+#define AUD_XDFP_PRAM_4K_D__W 16
+#define AUD_XDFP_PRAM_4K_D__M 0xFFFF
+#define AUD_XDFP_PRAM_4K_D__PRE 0x0
+
+#define FEC_COMM_EXEC__A 0x2400000
+#define FEC_COMM_EXEC__W 2
+#define FEC_COMM_EXEC__M 0x3
+#define FEC_COMM_EXEC__PRE 0x0
+#define FEC_COMM_EXEC_STOP 0x0
+#define FEC_COMM_EXEC_ACTIVE 0x1
+#define FEC_COMM_EXEC_HOLD 0x2
+
+#define FEC_COMM_MB__A 0x2400002
+#define FEC_COMM_MB__W 16
+#define FEC_COMM_MB__M 0xFFFF
+#define FEC_COMM_MB__PRE 0x0
+#define FEC_COMM_INT_REQ__A 0x2400003
+#define FEC_COMM_INT_REQ__W 16
+#define FEC_COMM_INT_REQ__M 0xFFFF
+#define FEC_COMM_INT_REQ__PRE 0x0
+#define FEC_COMM_INT_REQ_OC_REQ__B 0
+#define FEC_COMM_INT_REQ_OC_REQ__W 1
+#define FEC_COMM_INT_REQ_OC_REQ__M 0x1
+#define FEC_COMM_INT_REQ_OC_REQ__PRE 0x0
+#define FEC_COMM_INT_REQ_RS_REQ__B 1
+#define FEC_COMM_INT_REQ_RS_REQ__W 1
+#define FEC_COMM_INT_REQ_RS_REQ__M 0x2
+#define FEC_COMM_INT_REQ_RS_REQ__PRE 0x0
+#define FEC_COMM_INT_REQ_DI_REQ__B 2
+#define FEC_COMM_INT_REQ_DI_REQ__W 1
+#define FEC_COMM_INT_REQ_DI_REQ__M 0x4
+#define FEC_COMM_INT_REQ_DI_REQ__PRE 0x0
+
+#define FEC_COMM_INT_STA__A 0x2400005
+#define FEC_COMM_INT_STA__W 16
+#define FEC_COMM_INT_STA__M 0xFFFF
+#define FEC_COMM_INT_STA__PRE 0x0
+#define FEC_COMM_INT_MSK__A 0x2400006
+#define FEC_COMM_INT_MSK__W 16
+#define FEC_COMM_INT_MSK__M 0xFFFF
+#define FEC_COMM_INT_MSK__PRE 0x0
+#define FEC_COMM_INT_STM__A 0x2400007
+#define FEC_COMM_INT_STM__W 16
+#define FEC_COMM_INT_STM__M 0xFFFF
+#define FEC_COMM_INT_STM__PRE 0x0
+
+#define FEC_TOP_COMM_EXEC__A 0x2410000
+#define FEC_TOP_COMM_EXEC__W 2
+#define FEC_TOP_COMM_EXEC__M 0x3
+#define FEC_TOP_COMM_EXEC__PRE 0x0
+#define FEC_TOP_COMM_EXEC_STOP 0x0
+#define FEC_TOP_COMM_EXEC_ACTIVE 0x1
+#define FEC_TOP_COMM_EXEC_HOLD 0x2
+
+#define FEC_TOP_ANNEX__A 0x2410010
+#define FEC_TOP_ANNEX__W 2
+#define FEC_TOP_ANNEX__M 0x3
+#define FEC_TOP_ANNEX__PRE 0x0
+#define FEC_TOP_ANNEX_A 0x0
+#define FEC_TOP_ANNEX_B 0x1
+#define FEC_TOP_ANNEX_C 0x2
+#define FEC_TOP_ANNEX_D 0x3
+
+#define FEC_DI_COMM_EXEC__A 0x2420000
+#define FEC_DI_COMM_EXEC__W 2
+#define FEC_DI_COMM_EXEC__M 0x3
+#define FEC_DI_COMM_EXEC__PRE 0x0
+#define FEC_DI_COMM_EXEC_STOP 0x0
+#define FEC_DI_COMM_EXEC_ACTIVE 0x1
+#define FEC_DI_COMM_EXEC_HOLD 0x2
+
+#define FEC_DI_COMM_MB__A 0x2420002
+#define FEC_DI_COMM_MB__W 2
+#define FEC_DI_COMM_MB__M 0x3
+#define FEC_DI_COMM_MB__PRE 0x0
+#define FEC_DI_COMM_MB_CTL__B 0
+#define FEC_DI_COMM_MB_CTL__W 1
+#define FEC_DI_COMM_MB_CTL__M 0x1
+#define FEC_DI_COMM_MB_CTL__PRE 0x0
+#define FEC_DI_COMM_MB_CTL_OFF 0x0
+#define FEC_DI_COMM_MB_CTL_ON 0x1
+#define FEC_DI_COMM_MB_OBS__B 1
+#define FEC_DI_COMM_MB_OBS__W 1
+#define FEC_DI_COMM_MB_OBS__M 0x2
+#define FEC_DI_COMM_MB_OBS__PRE 0x0
+#define FEC_DI_COMM_MB_OBS_OFF 0x0
+#define FEC_DI_COMM_MB_OBS_ON 0x2
+
+#define FEC_DI_COMM_INT_REQ__A 0x2420003
+#define FEC_DI_COMM_INT_REQ__W 1
+#define FEC_DI_COMM_INT_REQ__M 0x1
+#define FEC_DI_COMM_INT_REQ__PRE 0x0
+#define FEC_DI_COMM_INT_STA__A 0x2420005
+#define FEC_DI_COMM_INT_STA__W 2
+#define FEC_DI_COMM_INT_STA__M 0x3
+#define FEC_DI_COMM_INT_STA__PRE 0x0
+
+#define FEC_DI_COMM_INT_STA_STAT_INT__B 0
+#define FEC_DI_COMM_INT_STA_STAT_INT__W 1
+#define FEC_DI_COMM_INT_STA_STAT_INT__M 0x1
+#define FEC_DI_COMM_INT_STA_STAT_INT__PRE 0x0
+
+#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__B 1
+#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__W 1
+#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__M 0x2
+#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__PRE 0x0
+
+#define FEC_DI_COMM_INT_MSK__A 0x2420006
+#define FEC_DI_COMM_INT_MSK__W 2
+#define FEC_DI_COMM_INT_MSK__M 0x3
+#define FEC_DI_COMM_INT_MSK__PRE 0x0
+#define FEC_DI_COMM_INT_MSK_STAT_INT__B 0
+#define FEC_DI_COMM_INT_MSK_STAT_INT__W 1
+#define FEC_DI_COMM_INT_MSK_STAT_INT__M 0x1
+#define FEC_DI_COMM_INT_MSK_STAT_INT__PRE 0x0
+#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__B 1
+#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__W 1
+#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__M 0x2
+#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__PRE 0x0
+
+#define FEC_DI_COMM_INT_STM__A 0x2420007
+#define FEC_DI_COMM_INT_STM__W 2
+#define FEC_DI_COMM_INT_STM__M 0x3
+#define FEC_DI_COMM_INT_STM__PRE 0x0
+#define FEC_DI_COMM_INT_STM_STAT_INT__B 0
+#define FEC_DI_COMM_INT_STM_STAT_INT__W 1
+#define FEC_DI_COMM_INT_STM_STAT_INT__M 0x1
+#define FEC_DI_COMM_INT_STM_STAT_INT__PRE 0x0
+#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__B 1
+#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__W 1
+#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__M 0x2
+#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__PRE 0x0
+
+#define FEC_DI_STATUS__A 0x2420010
+#define FEC_DI_STATUS__W 1
+#define FEC_DI_STATUS__M 0x1
+#define FEC_DI_STATUS__PRE 0x0
+#define FEC_DI_MODE__A 0x2420011
+#define FEC_DI_MODE__W 3
+#define FEC_DI_MODE__M 0x7
+#define FEC_DI_MODE__PRE 0x0
+
+#define FEC_DI_MODE_NO_SYNC__B 0
+#define FEC_DI_MODE_NO_SYNC__W 1
+#define FEC_DI_MODE_NO_SYNC__M 0x1
+#define FEC_DI_MODE_NO_SYNC__PRE 0x0
+
+#define FEC_DI_MODE_IGNORE_LOST_SYNC__B 1
+#define FEC_DI_MODE_IGNORE_LOST_SYNC__W 1
+#define FEC_DI_MODE_IGNORE_LOST_SYNC__M 0x2
+#define FEC_DI_MODE_IGNORE_LOST_SYNC__PRE 0x0
+
+#define FEC_DI_MODE_IGNORE_TIMEOUT__B 2
+#define FEC_DI_MODE_IGNORE_TIMEOUT__W 1
+#define FEC_DI_MODE_IGNORE_TIMEOUT__M 0x4
+#define FEC_DI_MODE_IGNORE_TIMEOUT__PRE 0x0
+
+#define FEC_DI_CONTROL_WORD__A 0x2420012
+#define FEC_DI_CONTROL_WORD__W 4
+#define FEC_DI_CONTROL_WORD__M 0xF
+#define FEC_DI_CONTROL_WORD__PRE 0x0
+
+#define FEC_DI_RESTART__A 0x2420013
+#define FEC_DI_RESTART__W 1
+#define FEC_DI_RESTART__M 0x1
+#define FEC_DI_RESTART__PRE 0x0
+
+#define FEC_DI_TIMEOUT_LO__A 0x2420014
+#define FEC_DI_TIMEOUT_LO__W 16
+#define FEC_DI_TIMEOUT_LO__M 0xFFFF
+#define FEC_DI_TIMEOUT_LO__PRE 0x0
+
+#define FEC_DI_TIMEOUT_HI__A 0x2420015
+#define FEC_DI_TIMEOUT_HI__W 8
+#define FEC_DI_TIMEOUT_HI__M 0xFF
+#define FEC_DI_TIMEOUT_HI__PRE 0xA
+
+#define FEC_RS_COMM_EXEC__A 0x2430000
+#define FEC_RS_COMM_EXEC__W 2
+#define FEC_RS_COMM_EXEC__M 0x3
+#define FEC_RS_COMM_EXEC__PRE 0x0
+#define FEC_RS_COMM_EXEC_STOP 0x0
+#define FEC_RS_COMM_EXEC_ACTIVE 0x1
+#define FEC_RS_COMM_EXEC_HOLD 0x2
+
+#define FEC_RS_COMM_MB__A 0x2430002
+#define FEC_RS_COMM_MB__W 2
+#define FEC_RS_COMM_MB__M 0x3
+#define FEC_RS_COMM_MB__PRE 0x0
+#define FEC_RS_COMM_MB_CTL__B 0
+#define FEC_RS_COMM_MB_CTL__W 1
+#define FEC_RS_COMM_MB_CTL__M 0x1
+#define FEC_RS_COMM_MB_CTL__PRE 0x0
+#define FEC_RS_COMM_MB_CTL_OFF 0x0
+#define FEC_RS_COMM_MB_CTL_ON 0x1
+#define FEC_RS_COMM_MB_OBS__B 1
+#define FEC_RS_COMM_MB_OBS__W 1
+#define FEC_RS_COMM_MB_OBS__M 0x2
+#define FEC_RS_COMM_MB_OBS__PRE 0x0
+#define FEC_RS_COMM_MB_OBS_OFF 0x0
+#define FEC_RS_COMM_MB_OBS_ON 0x2
+
+#define FEC_RS_COMM_INT_REQ__A 0x2430003
+#define FEC_RS_COMM_INT_REQ__W 1
+#define FEC_RS_COMM_INT_REQ__M 0x1
+#define FEC_RS_COMM_INT_REQ__PRE 0x0
+#define FEC_RS_COMM_INT_STA__A 0x2430005
+#define FEC_RS_COMM_INT_STA__W 2
+#define FEC_RS_COMM_INT_STA__M 0x3
+#define FEC_RS_COMM_INT_STA__PRE 0x0
+
+#define FEC_RS_COMM_INT_STA_FAILURE_INT__B 0
+#define FEC_RS_COMM_INT_STA_FAILURE_INT__W 1
+#define FEC_RS_COMM_INT_STA_FAILURE_INT__M 0x1
+#define FEC_RS_COMM_INT_STA_FAILURE_INT__PRE 0x0
+
+#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__B 1
+#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__W 1
+#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__M 0x2
+#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__PRE 0x0
+
+#define FEC_RS_COMM_INT_MSK__A 0x2430006
+#define FEC_RS_COMM_INT_MSK__W 2
+#define FEC_RS_COMM_INT_MSK__M 0x3
+#define FEC_RS_COMM_INT_MSK__PRE 0x0
+#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__B 0
+#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__W 1
+#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__M 0x1
+#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__PRE 0x0
+#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__B 1
+#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__W 1
+#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__M 0x2
+#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__PRE 0x0
+
+#define FEC_RS_COMM_INT_STM__A 0x2430007
+#define FEC_RS_COMM_INT_STM__W 2
+#define FEC_RS_COMM_INT_STM__M 0x3
+#define FEC_RS_COMM_INT_STM__PRE 0x0
+#define FEC_RS_COMM_INT_STM_FAILURE_MSK__B 0
+#define FEC_RS_COMM_INT_STM_FAILURE_MSK__W 1
+#define FEC_RS_COMM_INT_STM_FAILURE_MSK__M 0x1
+#define FEC_RS_COMM_INT_STM_FAILURE_MSK__PRE 0x0
+#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__B 1
+#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__W 1
+#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__M 0x2
+#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__PRE 0x0
+
+#define FEC_RS_STATUS__A 0x2430010
+#define FEC_RS_STATUS__W 1
+#define FEC_RS_STATUS__M 0x1
+#define FEC_RS_STATUS__PRE 0x0
+#define FEC_RS_MODE__A 0x2430011
+#define FEC_RS_MODE__W 1
+#define FEC_RS_MODE__M 0x1
+#define FEC_RS_MODE__PRE 0x0
+
+#define FEC_RS_MODE_BYPASS__B 0
+#define FEC_RS_MODE_BYPASS__W 1
+#define FEC_RS_MODE_BYPASS__M 0x1
+#define FEC_RS_MODE_BYPASS__PRE 0x0
+
+#define FEC_RS_MEASUREMENT_PERIOD__A 0x2430012
+#define FEC_RS_MEASUREMENT_PERIOD__W 16
+#define FEC_RS_MEASUREMENT_PERIOD__M 0xFFFF
+#define FEC_RS_MEASUREMENT_PERIOD__PRE 0x1171
+
+#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__B 0
+#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__W 16
+#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__M 0xFFFF
+#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__PRE 0x1171
+
+#define FEC_RS_MEASUREMENT_PRESCALE__A 0x2430013
+#define FEC_RS_MEASUREMENT_PRESCALE__W 16
+#define FEC_RS_MEASUREMENT_PRESCALE__M 0xFFFF
+#define FEC_RS_MEASUREMENT_PRESCALE__PRE 0x1
+
+#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__B 0
+#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__W 16
+#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__M 0xFFFF
+#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__PRE 0x1
+
+#define FEC_RS_NR_BIT_ERRORS__A 0x2430014
+#define FEC_RS_NR_BIT_ERRORS__W 16
+#define FEC_RS_NR_BIT_ERRORS__M 0xFFFF
+#define FEC_RS_NR_BIT_ERRORS__PRE 0xFFFF
+
+#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__B 0
+#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__W 12
+#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__M 0xFFF
+#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__PRE 0xFFF
+
+#define FEC_RS_NR_BIT_ERRORS_EXP__B 12
+#define FEC_RS_NR_BIT_ERRORS_EXP__W 4
+#define FEC_RS_NR_BIT_ERRORS_EXP__M 0xF000
+#define FEC_RS_NR_BIT_ERRORS_EXP__PRE 0xF000
+
+#define FEC_RS_NR_SYMBOL_ERRORS__A 0x2430015
+#define FEC_RS_NR_SYMBOL_ERRORS__W 16
+#define FEC_RS_NR_SYMBOL_ERRORS__M 0xFFFF
+#define FEC_RS_NR_SYMBOL_ERRORS__PRE 0xFFFF
+
+#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__B 0
+#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__W 12
+#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__M 0xFFF
+#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__PRE 0xFFF
+
+#define FEC_RS_NR_SYMBOL_ERRORS_EXP__B 12
+#define FEC_RS_NR_SYMBOL_ERRORS_EXP__W 4
+#define FEC_RS_NR_SYMBOL_ERRORS_EXP__M 0xF000
+#define FEC_RS_NR_SYMBOL_ERRORS_EXP__PRE 0xF000
+
+#define FEC_RS_NR_PACKET_ERRORS__A 0x2430016
+#define FEC_RS_NR_PACKET_ERRORS__W 16
+#define FEC_RS_NR_PACKET_ERRORS__M 0xFFFF
+#define FEC_RS_NR_PACKET_ERRORS__PRE 0xFFFF
+
+#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__B 0
+#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__W 12
+#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__M 0xFFF
+#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__PRE 0xFFF
+
+#define FEC_RS_NR_PACKET_ERRORS_EXP__B 12
+#define FEC_RS_NR_PACKET_ERRORS_EXP__W 4
+#define FEC_RS_NR_PACKET_ERRORS_EXP__M 0xF000
+#define FEC_RS_NR_PACKET_ERRORS_EXP__PRE 0xF000
+
+#define FEC_RS_NR_FAILURES__A 0x2430017
+#define FEC_RS_NR_FAILURES__W 16
+#define FEC_RS_NR_FAILURES__M 0xFFFF
+#define FEC_RS_NR_FAILURES__PRE 0x0
+
+#define FEC_RS_NR_FAILURES_FIXED_MANT__B 0
+#define FEC_RS_NR_FAILURES_FIXED_MANT__W 12
+#define FEC_RS_NR_FAILURES_FIXED_MANT__M 0xFFF
+#define FEC_RS_NR_FAILURES_FIXED_MANT__PRE 0x0
+
+#define FEC_RS_NR_FAILURES_EXP__B 12
+#define FEC_RS_NR_FAILURES_EXP__W 4
+#define FEC_RS_NR_FAILURES_EXP__M 0xF000
+#define FEC_RS_NR_FAILURES_EXP__PRE 0x0
+
+#define FEC_OC_COMM_EXEC__A 0x2440000
+#define FEC_OC_COMM_EXEC__W 2
+#define FEC_OC_COMM_EXEC__M 0x3
+#define FEC_OC_COMM_EXEC__PRE 0x0
+#define FEC_OC_COMM_EXEC_STOP 0x0
+#define FEC_OC_COMM_EXEC_ACTIVE 0x1
+#define FEC_OC_COMM_EXEC_HOLD 0x2
+
+#define FEC_OC_COMM_MB__A 0x2440002
+#define FEC_OC_COMM_MB__W 2
+#define FEC_OC_COMM_MB__M 0x3
+#define FEC_OC_COMM_MB__PRE 0x0
+#define FEC_OC_COMM_MB_CTL__B 0
+#define FEC_OC_COMM_MB_CTL__W 1
+#define FEC_OC_COMM_MB_CTL__M 0x1
+#define FEC_OC_COMM_MB_CTL__PRE 0x0
+#define FEC_OC_COMM_MB_CTL_OFF 0x0
+#define FEC_OC_COMM_MB_CTL_ON 0x1
+#define FEC_OC_COMM_MB_OBS__B 1
+#define FEC_OC_COMM_MB_OBS__W 1
+#define FEC_OC_COMM_MB_OBS__M 0x2
+#define FEC_OC_COMM_MB_OBS__PRE 0x0
+#define FEC_OC_COMM_MB_OBS_OFF 0x0
+#define FEC_OC_COMM_MB_OBS_ON 0x2
+
+#define FEC_OC_COMM_INT_REQ__A 0x2440003
+#define FEC_OC_COMM_INT_REQ__W 1
+#define FEC_OC_COMM_INT_REQ__M 0x1
+#define FEC_OC_COMM_INT_REQ__PRE 0x0
+#define FEC_OC_COMM_INT_STA__A 0x2440005
+#define FEC_OC_COMM_INT_STA__W 8
+#define FEC_OC_COMM_INT_STA__M 0xFF
+#define FEC_OC_COMM_INT_STA__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__B 0
+#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__W 1
+#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__M 0x1
+#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__B 1
+#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__W 1
+#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__M 0x2
+#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__B 2
+#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__W 1
+#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__M 0x4
+#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__B 3
+#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__W 1
+#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__M 0x8
+#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__B 4
+#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__W 1
+#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__M 0x10
+#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__B 5
+#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__W 1
+#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__M 0x20
+#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__B 6
+#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__W 1
+#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__M 0x40
+#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__B 7
+#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__W 1
+#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__M 0x80
+#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_MSK__A 0x2440006
+#define FEC_OC_COMM_INT_MSK__W 8
+#define FEC_OC_COMM_INT_MSK__M 0xFF
+#define FEC_OC_COMM_INT_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__B 0
+#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__M 0x1
+#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__B 1
+#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__M 0x2
+#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__B 2
+#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__M 0x4
+#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__B 3
+#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__M 0x8
+#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__B 4
+#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__M 0x10
+#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__B 5
+#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__M 0x20
+#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__B 6
+#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__M 0x40
+#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__B 7
+#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__M 0x80
+#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__PRE 0x0
+
+#define FEC_OC_COMM_INT_STM__A 0x2440007
+#define FEC_OC_COMM_INT_STM__W 8
+#define FEC_OC_COMM_INT_STM__M 0xFF
+#define FEC_OC_COMM_INT_STM__PRE 0x0
+#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__B 0
+#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__W 1
+#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__M 0x1
+#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__B 1
+#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__W 1
+#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__M 0x2
+#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__B 2
+#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__W 1
+#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__M 0x4
+#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__B 3
+#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__W 1
+#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__M 0x8
+#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__B 4
+#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__W 1
+#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__M 0x10
+#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__B 5
+#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__W 1
+#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__M 0x20
+#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__B 6
+#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__W 1
+#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__M 0x40
+#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__B 7
+#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__W 1
+#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__M 0x80
+#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__PRE 0x0
+
+#define FEC_OC_STATUS__A 0x2440010
+#define FEC_OC_STATUS__W 5
+#define FEC_OC_STATUS__M 0x1F
+#define FEC_OC_STATUS__PRE 0x0
+
+#define FEC_OC_STATUS_DPR_STATUS__B 0
+#define FEC_OC_STATUS_DPR_STATUS__W 1
+#define FEC_OC_STATUS_DPR_STATUS__M 0x1
+#define FEC_OC_STATUS_DPR_STATUS__PRE 0x0
+
+#define FEC_OC_STATUS_SNC_STATUS__B 1
+#define FEC_OC_STATUS_SNC_STATUS__W 2
+#define FEC_OC_STATUS_SNC_STATUS__M 0x6
+#define FEC_OC_STATUS_SNC_STATUS__PRE 0x0
+
+#define FEC_OC_STATUS_FIFO_FULL__B 3
+#define FEC_OC_STATUS_FIFO_FULL__W 1
+#define FEC_OC_STATUS_FIFO_FULL__M 0x8
+#define FEC_OC_STATUS_FIFO_FULL__PRE 0x0
+
+#define FEC_OC_STATUS_FIFO_EMPTY__B 4
+#define FEC_OC_STATUS_FIFO_EMPTY__W 1
+#define FEC_OC_STATUS_FIFO_EMPTY__M 0x10
+#define FEC_OC_STATUS_FIFO_EMPTY__PRE 0x0
+
+#define FEC_OC_MODE__A 0x2440011
+#define FEC_OC_MODE__W 4
+#define FEC_OC_MODE__M 0xF
+#define FEC_OC_MODE__PRE 0x0
+
+#define FEC_OC_MODE_PARITY__B 0
+#define FEC_OC_MODE_PARITY__W 1
+#define FEC_OC_MODE_PARITY__M 0x1
+#define FEC_OC_MODE_PARITY__PRE 0x0
+
+#define FEC_OC_MODE_TRANSPARENT__B 1
+#define FEC_OC_MODE_TRANSPARENT__W 1
+#define FEC_OC_MODE_TRANSPARENT__M 0x2
+#define FEC_OC_MODE_TRANSPARENT__PRE 0x0
+
+#define FEC_OC_MODE_CLEAR__B 2
+#define FEC_OC_MODE_CLEAR__W 1
+#define FEC_OC_MODE_CLEAR__M 0x4
+#define FEC_OC_MODE_CLEAR__PRE 0x0
+
+#define FEC_OC_MODE_RETAIN_FRAMING__B 3
+#define FEC_OC_MODE_RETAIN_FRAMING__W 1
+#define FEC_OC_MODE_RETAIN_FRAMING__M 0x8
+#define FEC_OC_MODE_RETAIN_FRAMING__PRE 0x0
+
+#define FEC_OC_DPR_MODE__A 0x2440012
+#define FEC_OC_DPR_MODE__W 2
+#define FEC_OC_DPR_MODE__M 0x3
+#define FEC_OC_DPR_MODE__PRE 0x0
+
+#define FEC_OC_DPR_MODE_ERR_DISABLE__B 0
+#define FEC_OC_DPR_MODE_ERR_DISABLE__W 1
+#define FEC_OC_DPR_MODE_ERR_DISABLE__M 0x1
+#define FEC_OC_DPR_MODE_ERR_DISABLE__PRE 0x0
+
+#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__B 1
+#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__W 1
+#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__M 0x2
+#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__PRE 0x0
+
+#define FEC_OC_DPR_UNLOCK__A 0x2440013
+#define FEC_OC_DPR_UNLOCK__W 1
+#define FEC_OC_DPR_UNLOCK__M 0x1
+#define FEC_OC_DPR_UNLOCK__PRE 0x0
+#define FEC_OC_DTO_MODE__A 0x2440014
+#define FEC_OC_DTO_MODE__W 3
+#define FEC_OC_DTO_MODE__M 0x7
+#define FEC_OC_DTO_MODE__PRE 0x0
+
+#define FEC_OC_DTO_MODE_DYNAMIC__B 0
+#define FEC_OC_DTO_MODE_DYNAMIC__W 1
+#define FEC_OC_DTO_MODE_DYNAMIC__M 0x1
+#define FEC_OC_DTO_MODE_DYNAMIC__PRE 0x0
+
+#define FEC_OC_DTO_MODE_DUTY_CYCLE__B 1
+#define FEC_OC_DTO_MODE_DUTY_CYCLE__W 1
+#define FEC_OC_DTO_MODE_DUTY_CYCLE__M 0x2
+#define FEC_OC_DTO_MODE_DUTY_CYCLE__PRE 0x0
+
+#define FEC_OC_DTO_MODE_OFFSET_ENABLE__B 2
+#define FEC_OC_DTO_MODE_OFFSET_ENABLE__W 1
+#define FEC_OC_DTO_MODE_OFFSET_ENABLE__M 0x4
+#define FEC_OC_DTO_MODE_OFFSET_ENABLE__PRE 0x0
+
+#define FEC_OC_DTO_PERIOD__A 0x2440015
+#define FEC_OC_DTO_PERIOD__W 8
+#define FEC_OC_DTO_PERIOD__M 0xFF
+#define FEC_OC_DTO_PERIOD__PRE 0x0
+#define FEC_OC_DTO_RATE_LO__A 0x2440016
+#define FEC_OC_DTO_RATE_LO__W 16
+#define FEC_OC_DTO_RATE_LO__M 0xFFFF
+#define FEC_OC_DTO_RATE_LO__PRE 0x0
+
+#define FEC_OC_DTO_RATE_LO_RATE_LO__B 0
+#define FEC_OC_DTO_RATE_LO_RATE_LO__W 16
+#define FEC_OC_DTO_RATE_LO_RATE_LO__M 0xFFFF
+#define FEC_OC_DTO_RATE_LO_RATE_LO__PRE 0x0
+
+#define FEC_OC_DTO_RATE_HI__A 0x2440017
+#define FEC_OC_DTO_RATE_HI__W 10
+#define FEC_OC_DTO_RATE_HI__M 0x3FF
+#define FEC_OC_DTO_RATE_HI__PRE 0xC0
+
+#define FEC_OC_DTO_RATE_HI_RATE_HI__B 0
+#define FEC_OC_DTO_RATE_HI_RATE_HI__W 10
+#define FEC_OC_DTO_RATE_HI_RATE_HI__M 0x3FF
+#define FEC_OC_DTO_RATE_HI_RATE_HI__PRE 0xC0
+
+#define FEC_OC_DTO_BURST_LEN__A 0x2440018
+#define FEC_OC_DTO_BURST_LEN__W 8
+#define FEC_OC_DTO_BURST_LEN__M 0xFF
+#define FEC_OC_DTO_BURST_LEN__PRE 0xBC
+
+#define FEC_OC_DTO_BURST_LEN_BURST_LEN__B 0
+#define FEC_OC_DTO_BURST_LEN_BURST_LEN__W 8
+#define FEC_OC_DTO_BURST_LEN_BURST_LEN__M 0xFF
+#define FEC_OC_DTO_BURST_LEN_BURST_LEN__PRE 0xBC
+
+#define FEC_OC_FCT_MODE__A 0x244001A
+#define FEC_OC_FCT_MODE__W 2
+#define FEC_OC_FCT_MODE__M 0x3
+#define FEC_OC_FCT_MODE__PRE 0x0
+
+#define FEC_OC_FCT_MODE_RAT_ENA__B 0
+#define FEC_OC_FCT_MODE_RAT_ENA__W 1
+#define FEC_OC_FCT_MODE_RAT_ENA__M 0x1
+#define FEC_OC_FCT_MODE_RAT_ENA__PRE 0x0
+
+#define FEC_OC_FCT_MODE_VIRT_ENA__B 1
+#define FEC_OC_FCT_MODE_VIRT_ENA__W 1
+#define FEC_OC_FCT_MODE_VIRT_ENA__M 0x2
+#define FEC_OC_FCT_MODE_VIRT_ENA__PRE 0x0
+
+#define FEC_OC_FCT_USAGE__A 0x244001B
+#define FEC_OC_FCT_USAGE__W 3
+#define FEC_OC_FCT_USAGE__M 0x7
+#define FEC_OC_FCT_USAGE__PRE 0x2
+
+#define FEC_OC_FCT_USAGE_USAGE__B 0
+#define FEC_OC_FCT_USAGE_USAGE__W 3
+#define FEC_OC_FCT_USAGE_USAGE__M 0x7
+#define FEC_OC_FCT_USAGE_USAGE__PRE 0x2
+
+#define FEC_OC_FCT_OCCUPATION__A 0x244001C
+#define FEC_OC_FCT_OCCUPATION__W 12
+#define FEC_OC_FCT_OCCUPATION__M 0xFFF
+#define FEC_OC_FCT_OCCUPATION__PRE 0x0
+
+#define FEC_OC_FCT_OCCUPATION_OCCUPATION__B 0
+#define FEC_OC_FCT_OCCUPATION_OCCUPATION__W 12
+#define FEC_OC_FCT_OCCUPATION_OCCUPATION__M 0xFFF
+#define FEC_OC_FCT_OCCUPATION_OCCUPATION__PRE 0x0
+
+#define FEC_OC_TMD_MODE__A 0x244001E
+#define FEC_OC_TMD_MODE__W 3
+#define FEC_OC_TMD_MODE__M 0x7
+#define FEC_OC_TMD_MODE__PRE 0x4
+
+#define FEC_OC_TMD_MODE_MODE__B 0
+#define FEC_OC_TMD_MODE_MODE__W 3
+#define FEC_OC_TMD_MODE_MODE__M 0x7
+#define FEC_OC_TMD_MODE_MODE__PRE 0x4
+
+#define FEC_OC_TMD_COUNT__A 0x244001F
+#define FEC_OC_TMD_COUNT__W 10
+#define FEC_OC_TMD_COUNT__M 0x3FF
+#define FEC_OC_TMD_COUNT__PRE 0x1F4
+
+#define FEC_OC_TMD_COUNT_COUNT__B 0
+#define FEC_OC_TMD_COUNT_COUNT__W 10
+#define FEC_OC_TMD_COUNT_COUNT__M 0x3FF
+#define FEC_OC_TMD_COUNT_COUNT__PRE 0x1F4
+
+#define FEC_OC_TMD_HI_MARGIN__A 0x2440020
+#define FEC_OC_TMD_HI_MARGIN__W 11
+#define FEC_OC_TMD_HI_MARGIN__M 0x7FF
+#define FEC_OC_TMD_HI_MARGIN__PRE 0x200
+
+#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__B 0
+#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__W 11
+#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__M 0x7FF
+#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__PRE 0x200
+
+#define FEC_OC_TMD_LO_MARGIN__A 0x2440021
+#define FEC_OC_TMD_LO_MARGIN__W 11
+#define FEC_OC_TMD_LO_MARGIN__M 0x7FF
+#define FEC_OC_TMD_LO_MARGIN__PRE 0x100
+
+#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__B 0
+#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__W 11
+#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__M 0x7FF
+#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__PRE 0x100
+
+#define FEC_OC_TMD_CTL_UPD_RATE__A 0x2440022
+#define FEC_OC_TMD_CTL_UPD_RATE__W 4
+#define FEC_OC_TMD_CTL_UPD_RATE__M 0xF
+#define FEC_OC_TMD_CTL_UPD_RATE__PRE 0x1
+
+#define FEC_OC_TMD_CTL_UPD_RATE_RATE__B 0
+#define FEC_OC_TMD_CTL_UPD_RATE_RATE__W 4
+#define FEC_OC_TMD_CTL_UPD_RATE_RATE__M 0xF
+#define FEC_OC_TMD_CTL_UPD_RATE_RATE__PRE 0x1
+
+#define FEC_OC_TMD_INT_UPD_RATE__A 0x2440023
+#define FEC_OC_TMD_INT_UPD_RATE__W 4
+#define FEC_OC_TMD_INT_UPD_RATE__M 0xF
+#define FEC_OC_TMD_INT_UPD_RATE__PRE 0x4
+
+#define FEC_OC_TMD_INT_UPD_RATE_RATE__B 0
+#define FEC_OC_TMD_INT_UPD_RATE_RATE__W 4
+#define FEC_OC_TMD_INT_UPD_RATE_RATE__M 0xF
+#define FEC_OC_TMD_INT_UPD_RATE_RATE__PRE 0x4
+
+#define FEC_OC_AVR_PARM_A__A 0x2440026
+#define FEC_OC_AVR_PARM_A__W 4
+#define FEC_OC_AVR_PARM_A__M 0xF
+#define FEC_OC_AVR_PARM_A__PRE 0x6
+
+#define FEC_OC_AVR_PARM_A_PARM__B 0
+#define FEC_OC_AVR_PARM_A_PARM__W 4
+#define FEC_OC_AVR_PARM_A_PARM__M 0xF
+#define FEC_OC_AVR_PARM_A_PARM__PRE 0x6
+
+#define FEC_OC_AVR_PARM_B__A 0x2440027
+#define FEC_OC_AVR_PARM_B__W 4
+#define FEC_OC_AVR_PARM_B__M 0xF
+#define FEC_OC_AVR_PARM_B__PRE 0x4
+
+#define FEC_OC_AVR_PARM_B_PARM__B 0
+#define FEC_OC_AVR_PARM_B_PARM__W 4
+#define FEC_OC_AVR_PARM_B_PARM__M 0xF
+#define FEC_OC_AVR_PARM_B_PARM__PRE 0x4
+
+#define FEC_OC_AVR_AVG_LO__A 0x2440028
+#define FEC_OC_AVR_AVG_LO__W 16
+#define FEC_OC_AVR_AVG_LO__M 0xFFFF
+#define FEC_OC_AVR_AVG_LO__PRE 0x0
+
+#define FEC_OC_AVR_AVG_LO_AVG_LO__B 0
+#define FEC_OC_AVR_AVG_LO_AVG_LO__W 16
+#define FEC_OC_AVR_AVG_LO_AVG_LO__M 0xFFFF
+#define FEC_OC_AVR_AVG_LO_AVG_LO__PRE 0x0
+
+#define FEC_OC_AVR_AVG_HI__A 0x2440029
+#define FEC_OC_AVR_AVG_HI__W 6
+#define FEC_OC_AVR_AVG_HI__M 0x3F
+#define FEC_OC_AVR_AVG_HI__PRE 0x0
+
+#define FEC_OC_AVR_AVG_HI_AVG_HI__B 0
+#define FEC_OC_AVR_AVG_HI_AVG_HI__W 6
+#define FEC_OC_AVR_AVG_HI_AVG_HI__M 0x3F
+#define FEC_OC_AVR_AVG_HI_AVG_HI__PRE 0x0
+
+#define FEC_OC_RCN_MODE__A 0x244002C
+#define FEC_OC_RCN_MODE__W 5
+#define FEC_OC_RCN_MODE__M 0x1F
+#define FEC_OC_RCN_MODE__PRE 0x1F
+
+#define FEC_OC_RCN_MODE_MODE__B 0
+#define FEC_OC_RCN_MODE_MODE__W 5
+#define FEC_OC_RCN_MODE_MODE__M 0x1F
+#define FEC_OC_RCN_MODE_MODE__PRE 0x1F
+
+#define FEC_OC_RCN_OCC_SETTLE__A 0x244002D
+#define FEC_OC_RCN_OCC_SETTLE__W 11
+#define FEC_OC_RCN_OCC_SETTLE__M 0x7FF
+#define FEC_OC_RCN_OCC_SETTLE__PRE 0x180
+
+#define FEC_OC_RCN_OCC_SETTLE_LEVEL__B 0
+#define FEC_OC_RCN_OCC_SETTLE_LEVEL__W 11
+#define FEC_OC_RCN_OCC_SETTLE_LEVEL__M 0x7FF
+#define FEC_OC_RCN_OCC_SETTLE_LEVEL__PRE 0x180
+
+#define FEC_OC_RCN_GAIN__A 0x244002E
+#define FEC_OC_RCN_GAIN__W 4
+#define FEC_OC_RCN_GAIN__M 0xF
+#define FEC_OC_RCN_GAIN__PRE 0xC
+
+#define FEC_OC_RCN_GAIN_GAIN__B 0
+#define FEC_OC_RCN_GAIN_GAIN__W 4
+#define FEC_OC_RCN_GAIN_GAIN__M 0xF
+#define FEC_OC_RCN_GAIN_GAIN__PRE 0xC
+
+#define FEC_OC_RCN_CTL_RATE_LO__A 0x2440030
+#define FEC_OC_RCN_CTL_RATE_LO__W 16
+#define FEC_OC_RCN_CTL_RATE_LO__M 0xFFFF
+#define FEC_OC_RCN_CTL_RATE_LO__PRE 0x0
+
+#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__B 0
+#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__W 16
+#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__M 0xFFFF
+#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__PRE 0x0
+
+#define FEC_OC_RCN_CTL_RATE_HI__A 0x2440031
+#define FEC_OC_RCN_CTL_RATE_HI__W 8
+#define FEC_OC_RCN_CTL_RATE_HI__M 0xFF
+#define FEC_OC_RCN_CTL_RATE_HI__PRE 0xC0
+
+#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__B 0
+#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__W 8
+#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__M 0xFF
+#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__PRE 0xC0
+
+#define FEC_OC_RCN_CTL_STEP_LO__A 0x2440032
+#define FEC_OC_RCN_CTL_STEP_LO__W 16
+#define FEC_OC_RCN_CTL_STEP_LO__M 0xFFFF
+#define FEC_OC_RCN_CTL_STEP_LO__PRE 0x0
+
+#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__B 0
+#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__W 16
+#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__M 0xFFFF
+#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__PRE 0x0
+
+#define FEC_OC_RCN_CTL_STEP_HI__A 0x2440033
+#define FEC_OC_RCN_CTL_STEP_HI__W 8
+#define FEC_OC_RCN_CTL_STEP_HI__M 0xFF
+#define FEC_OC_RCN_CTL_STEP_HI__PRE 0x8
+
+#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__B 0
+#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__W 8
+#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__M 0xFF
+#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__PRE 0x8
+
+#define FEC_OC_RCN_DTO_OFS_LO__A 0x2440034
+#define FEC_OC_RCN_DTO_OFS_LO__W 16
+#define FEC_OC_RCN_DTO_OFS_LO__M 0xFFFF
+#define FEC_OC_RCN_DTO_OFS_LO__PRE 0x0
+
+#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__B 0
+#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__W 16
+#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__M 0xFFFF
+#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__PRE 0x0
+
+#define FEC_OC_RCN_DTO_OFS_HI__A 0x2440035
+#define FEC_OC_RCN_DTO_OFS_HI__W 8
+#define FEC_OC_RCN_DTO_OFS_HI__M 0xFF
+#define FEC_OC_RCN_DTO_OFS_HI__PRE 0x0
+
+#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__B 0
+#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__W 8
+#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__M 0xFF
+#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__PRE 0x0
+
+#define FEC_OC_RCN_DTO_RATE_LO__A 0x2440036
+#define FEC_OC_RCN_DTO_RATE_LO__W 16
+#define FEC_OC_RCN_DTO_RATE_LO__M 0xFFFF
+#define FEC_OC_RCN_DTO_RATE_LO__PRE 0x0
+
+#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__B 0
+#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__W 16
+#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__M 0xFFFF
+#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__PRE 0x0
+
+#define FEC_OC_RCN_DTO_RATE_HI__A 0x2440037
+#define FEC_OC_RCN_DTO_RATE_HI__W 8
+#define FEC_OC_RCN_DTO_RATE_HI__M 0xFF
+#define FEC_OC_RCN_DTO_RATE_HI__PRE 0x0
+
+#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__B 0
+#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__W 8
+#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__M 0xFF
+#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__PRE 0x0
+
+#define FEC_OC_RCN_RATE_CLIP_LO__A 0x2440038
+#define FEC_OC_RCN_RATE_CLIP_LO__W 16
+#define FEC_OC_RCN_RATE_CLIP_LO__M 0xFFFF
+#define FEC_OC_RCN_RATE_CLIP_LO__PRE 0x0
+
+#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__B 0
+#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__W 16
+#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__M 0xFFFF
+#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__PRE 0x0
+
+#define FEC_OC_RCN_RATE_CLIP_HI__A 0x2440039
+#define FEC_OC_RCN_RATE_CLIP_HI__W 8
+#define FEC_OC_RCN_RATE_CLIP_HI__M 0xFF
+#define FEC_OC_RCN_RATE_CLIP_HI__PRE 0xF0
+
+#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__B 0
+#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__W 8
+#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__M 0xFF
+#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__PRE 0xF0
+
+#define FEC_OC_RCN_DYN_RATE_LO__A 0x244003A
+#define FEC_OC_RCN_DYN_RATE_LO__W 16
+#define FEC_OC_RCN_DYN_RATE_LO__M 0xFFFF
+#define FEC_OC_RCN_DYN_RATE_LO__PRE 0x0
+
+#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__B 0
+#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__W 16
+#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__M 0xFFFF
+#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__PRE 0x0
+
+#define FEC_OC_RCN_DYN_RATE_HI__A 0x244003B
+#define FEC_OC_RCN_DYN_RATE_HI__W 8
+#define FEC_OC_RCN_DYN_RATE_HI__M 0xFF
+#define FEC_OC_RCN_DYN_RATE_HI__PRE 0x0
+
+#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__B 0
+#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__W 8
+#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__M 0xFF
+#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__PRE 0x0
+
+#define FEC_OC_SNC_MODE__A 0x2440040
+#define FEC_OC_SNC_MODE__W 4
+#define FEC_OC_SNC_MODE__M 0xF
+#define FEC_OC_SNC_MODE__PRE 0x0
+
+#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__B 0
+#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__W 1
+#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__M 0x1
+#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__PRE 0x0
+
+#define FEC_OC_SNC_MODE_ERROR_CTL__B 1
+#define FEC_OC_SNC_MODE_ERROR_CTL__W 2
+#define FEC_OC_SNC_MODE_ERROR_CTL__M 0x6
+#define FEC_OC_SNC_MODE_ERROR_CTL__PRE 0x0
+
+#define FEC_OC_SNC_MODE_CORR_DISABLE__B 3
+#define FEC_OC_SNC_MODE_CORR_DISABLE__W 1
+#define FEC_OC_SNC_MODE_CORR_DISABLE__M 0x8
+#define FEC_OC_SNC_MODE_CORR_DISABLE__PRE 0x0
+
+#define FEC_OC_SNC_LWM__A 0x2440041
+#define FEC_OC_SNC_LWM__W 4
+#define FEC_OC_SNC_LWM__M 0xF
+#define FEC_OC_SNC_LWM__PRE 0x3
+
+#define FEC_OC_SNC_LWM_MARK__B 0
+#define FEC_OC_SNC_LWM_MARK__W 4
+#define FEC_OC_SNC_LWM_MARK__M 0xF
+#define FEC_OC_SNC_LWM_MARK__PRE 0x3
+
+#define FEC_OC_SNC_HWM__A 0x2440042
+#define FEC_OC_SNC_HWM__W 4
+#define FEC_OC_SNC_HWM__M 0xF
+#define FEC_OC_SNC_HWM__PRE 0x5
+
+#define FEC_OC_SNC_HWM_MARK__B 0
+#define FEC_OC_SNC_HWM_MARK__W 4
+#define FEC_OC_SNC_HWM_MARK__M 0xF
+#define FEC_OC_SNC_HWM_MARK__PRE 0x5
+
+#define FEC_OC_SNC_UNLOCK__A 0x2440043
+#define FEC_OC_SNC_UNLOCK__W 1
+#define FEC_OC_SNC_UNLOCK__M 0x1
+#define FEC_OC_SNC_UNLOCK__PRE 0x0
+
+#define FEC_OC_SNC_UNLOCK_RESTART__B 0
+#define FEC_OC_SNC_UNLOCK_RESTART__W 1
+#define FEC_OC_SNC_UNLOCK_RESTART__M 0x1
+#define FEC_OC_SNC_UNLOCK_RESTART__PRE 0x0
+
+#define FEC_OC_SNC_LOCK_COUNT__A 0x2440044
+#define FEC_OC_SNC_LOCK_COUNT__W 12
+#define FEC_OC_SNC_LOCK_COUNT__M 0xFFF
+#define FEC_OC_SNC_LOCK_COUNT__PRE 0x0
+
+#define FEC_OC_SNC_LOCK_COUNT_COUNT__B 0
+#define FEC_OC_SNC_LOCK_COUNT_COUNT__W 12
+#define FEC_OC_SNC_LOCK_COUNT_COUNT__M 0xFFF
+#define FEC_OC_SNC_LOCK_COUNT_COUNT__PRE 0x0
+
+#define FEC_OC_SNC_FAIL_COUNT__A 0x2440045
+#define FEC_OC_SNC_FAIL_COUNT__W 12
+#define FEC_OC_SNC_FAIL_COUNT__M 0xFFF
+#define FEC_OC_SNC_FAIL_COUNT__PRE 0x0
+
+#define FEC_OC_SNC_FAIL_COUNT_COUNT__B 0
+#define FEC_OC_SNC_FAIL_COUNT_COUNT__W 12
+#define FEC_OC_SNC_FAIL_COUNT_COUNT__M 0xFFF
+#define FEC_OC_SNC_FAIL_COUNT_COUNT__PRE 0x0
+
+#define FEC_OC_SNC_FAIL_PERIOD__A 0x2440046
+#define FEC_OC_SNC_FAIL_PERIOD__W 16
+#define FEC_OC_SNC_FAIL_PERIOD__M 0xFFFF
+#define FEC_OC_SNC_FAIL_PERIOD__PRE 0x1171
+
+#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__B 0
+#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__W 16
+#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__M 0xFFFF
+#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__PRE 0x1171
+
+#define FEC_OC_EMS_MODE__A 0x2440047
+#define FEC_OC_EMS_MODE__W 2
+#define FEC_OC_EMS_MODE__M 0x3
+#define FEC_OC_EMS_MODE__PRE 0x0
+
+#define FEC_OC_EMS_MODE_MODE__B 0
+#define FEC_OC_EMS_MODE_MODE__W 2
+#define FEC_OC_EMS_MODE_MODE__M 0x3
+#define FEC_OC_EMS_MODE_MODE__PRE 0x0
+
+#define FEC_OC_IPR_MODE__A 0x2440048
+#define FEC_OC_IPR_MODE__W 12
+#define FEC_OC_IPR_MODE__M 0xFFF
+#define FEC_OC_IPR_MODE__PRE 0x0
+
+#define FEC_OC_IPR_MODE_SERIAL__B 0
+#define FEC_OC_IPR_MODE_SERIAL__W 1
+#define FEC_OC_IPR_MODE_SERIAL__M 0x1
+#define FEC_OC_IPR_MODE_SERIAL__PRE 0x0
+
+#define FEC_OC_IPR_MODE_REVERSE_ORDER__B 1
+#define FEC_OC_IPR_MODE_REVERSE_ORDER__W 1
+#define FEC_OC_IPR_MODE_REVERSE_ORDER__M 0x2
+#define FEC_OC_IPR_MODE_REVERSE_ORDER__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__B 2
+#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__W 1
+#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M 0x4
+#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__B 3
+#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__W 1
+#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__M 0x8
+#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__B 4
+#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__W 1
+#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__M 0x10
+#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MERR_DIS_PAR__B 5
+#define FEC_OC_IPR_MODE_MERR_DIS_PAR__W 1
+#define FEC_OC_IPR_MODE_MERR_DIS_PAR__M 0x20
+#define FEC_OC_IPR_MODE_MERR_DIS_PAR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MD_DIS_PAR__B 6
+#define FEC_OC_IPR_MODE_MD_DIS_PAR__W 1
+#define FEC_OC_IPR_MODE_MD_DIS_PAR__M 0x40
+#define FEC_OC_IPR_MODE_MD_DIS_PAR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__B 7
+#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__W 1
+#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__M 0x80
+#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__B 8
+#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__W 1
+#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__M 0x100
+#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MERR_DIS_ERR__B 9
+#define FEC_OC_IPR_MODE_MERR_DIS_ERR__W 1
+#define FEC_OC_IPR_MODE_MERR_DIS_ERR__M 0x200
+#define FEC_OC_IPR_MODE_MERR_DIS_ERR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MD_DIS_ERR__B 10
+#define FEC_OC_IPR_MODE_MD_DIS_ERR__W 1
+#define FEC_OC_IPR_MODE_MD_DIS_ERR__M 0x400
+#define FEC_OC_IPR_MODE_MD_DIS_ERR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__B 11
+#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__W 1
+#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__M 0x800
+#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__PRE 0x0
+
+#define FEC_OC_IPR_INVERT__A 0x2440049
+#define FEC_OC_IPR_INVERT__W 12
+#define FEC_OC_IPR_INVERT__M 0xFFF
+#define FEC_OC_IPR_INVERT__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD0__B 0
+#define FEC_OC_IPR_INVERT_MD0__W 1
+#define FEC_OC_IPR_INVERT_MD0__M 0x1
+#define FEC_OC_IPR_INVERT_MD0__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD1__B 1
+#define FEC_OC_IPR_INVERT_MD1__W 1
+#define FEC_OC_IPR_INVERT_MD1__M 0x2
+#define FEC_OC_IPR_INVERT_MD1__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD2__B 2
+#define FEC_OC_IPR_INVERT_MD2__W 1
+#define FEC_OC_IPR_INVERT_MD2__M 0x4
+#define FEC_OC_IPR_INVERT_MD2__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD3__B 3
+#define FEC_OC_IPR_INVERT_MD3__W 1
+#define FEC_OC_IPR_INVERT_MD3__M 0x8
+#define FEC_OC_IPR_INVERT_MD3__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD4__B 4
+#define FEC_OC_IPR_INVERT_MD4__W 1
+#define FEC_OC_IPR_INVERT_MD4__M 0x10
+#define FEC_OC_IPR_INVERT_MD4__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD5__B 5
+#define FEC_OC_IPR_INVERT_MD5__W 1
+#define FEC_OC_IPR_INVERT_MD5__M 0x20
+#define FEC_OC_IPR_INVERT_MD5__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD6__B 6
+#define FEC_OC_IPR_INVERT_MD6__W 1
+#define FEC_OC_IPR_INVERT_MD6__M 0x40
+#define FEC_OC_IPR_INVERT_MD6__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD7__B 7
+#define FEC_OC_IPR_INVERT_MD7__W 1
+#define FEC_OC_IPR_INVERT_MD7__M 0x80
+#define FEC_OC_IPR_INVERT_MD7__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MERR__B 8
+#define FEC_OC_IPR_INVERT_MERR__W 1
+#define FEC_OC_IPR_INVERT_MERR__M 0x100
+#define FEC_OC_IPR_INVERT_MERR__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MSTRT__B 9
+#define FEC_OC_IPR_INVERT_MSTRT__W 1
+#define FEC_OC_IPR_INVERT_MSTRT__M 0x200
+#define FEC_OC_IPR_INVERT_MSTRT__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MVAL__B 10
+#define FEC_OC_IPR_INVERT_MVAL__W 1
+#define FEC_OC_IPR_INVERT_MVAL__M 0x400
+#define FEC_OC_IPR_INVERT_MVAL__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MCLK__B 11
+#define FEC_OC_IPR_INVERT_MCLK__W 1
+#define FEC_OC_IPR_INVERT_MCLK__M 0x800
+#define FEC_OC_IPR_INVERT_MCLK__PRE 0x0
+
+#define FEC_OC_OCR_MODE__A 0x2440050
+#define FEC_OC_OCR_MODE__W 4
+#define FEC_OC_OCR_MODE__M 0xF
+#define FEC_OC_OCR_MODE__PRE 0x0
+
+#define FEC_OC_OCR_MODE_MB_SELECT__B 0
+#define FEC_OC_OCR_MODE_MB_SELECT__W 1
+#define FEC_OC_OCR_MODE_MB_SELECT__M 0x1
+#define FEC_OC_OCR_MODE_MB_SELECT__PRE 0x0
+
+#define FEC_OC_OCR_MODE_GRAB_ENABLE__B 1
+#define FEC_OC_OCR_MODE_GRAB_ENABLE__W 1
+#define FEC_OC_OCR_MODE_GRAB_ENABLE__M 0x2
+#define FEC_OC_OCR_MODE_GRAB_ENABLE__PRE 0x0
+
+#define FEC_OC_OCR_MODE_GRAB_SELECT__B 2
+#define FEC_OC_OCR_MODE_GRAB_SELECT__W 1
+#define FEC_OC_OCR_MODE_GRAB_SELECT__M 0x4
+#define FEC_OC_OCR_MODE_GRAB_SELECT__PRE 0x0
+
+#define FEC_OC_OCR_MODE_GRAB_COUNTED__B 3
+#define FEC_OC_OCR_MODE_GRAB_COUNTED__W 1
+#define FEC_OC_OCR_MODE_GRAB_COUNTED__M 0x8
+#define FEC_OC_OCR_MODE_GRAB_COUNTED__PRE 0x0
+
+#define FEC_OC_OCR_RATE__A 0x2440051
+#define FEC_OC_OCR_RATE__W 4
+#define FEC_OC_OCR_RATE__M 0xF
+#define FEC_OC_OCR_RATE__PRE 0x0
+
+#define FEC_OC_OCR_RATE_RATE__B 0
+#define FEC_OC_OCR_RATE_RATE__W 4
+#define FEC_OC_OCR_RATE_RATE__M 0xF
+#define FEC_OC_OCR_RATE_RATE__PRE 0x0
+
+#define FEC_OC_OCR_INVERT__A 0x2440052
+#define FEC_OC_OCR_INVERT__W 12
+#define FEC_OC_OCR_INVERT__M 0xFFF
+#define FEC_OC_OCR_INVERT__PRE 0x800
+
+#define FEC_OC_OCR_INVERT_INVERT__B 0
+#define FEC_OC_OCR_INVERT_INVERT__W 12
+#define FEC_OC_OCR_INVERT_INVERT__M 0xFFF
+#define FEC_OC_OCR_INVERT_INVERT__PRE 0x800
+
+#define FEC_OC_OCR_GRAB_COUNT__A 0x2440053
+#define FEC_OC_OCR_GRAB_COUNT__W 16
+#define FEC_OC_OCR_GRAB_COUNT__M 0xFFFF
+#define FEC_OC_OCR_GRAB_COUNT__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_COUNT_COUNT__B 0
+#define FEC_OC_OCR_GRAB_COUNT_COUNT__W 16
+#define FEC_OC_OCR_GRAB_COUNT_COUNT__M 0xFFFF
+#define FEC_OC_OCR_GRAB_COUNT_COUNT__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_SYNC__A 0x2440054
+#define FEC_OC_OCR_GRAB_SYNC__W 8
+#define FEC_OC_OCR_GRAB_SYNC__M 0xFF
+#define FEC_OC_OCR_GRAB_SYNC__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__B 0
+#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__W 3
+#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__M 0x7
+#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__B 3
+#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__W 4
+#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__M 0x78
+#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__B 7
+#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__W 1
+#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__M 0x80
+#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD0__A 0x2440055
+#define FEC_OC_OCR_GRAB_RD0__W 10
+#define FEC_OC_OCR_GRAB_RD0__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD0__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD0_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD0_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD0_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD0_DATA__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD1__A 0x2440056
+#define FEC_OC_OCR_GRAB_RD1__W 10
+#define FEC_OC_OCR_GRAB_RD1__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD1__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD1_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD1_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD1_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD1_DATA__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD2__A 0x2440057
+#define FEC_OC_OCR_GRAB_RD2__W 10
+#define FEC_OC_OCR_GRAB_RD2__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD2__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD2_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD2_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD2_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD2_DATA__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD3__A 0x2440058
+#define FEC_OC_OCR_GRAB_RD3__W 10
+#define FEC_OC_OCR_GRAB_RD3__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD3__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD3_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD3_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD3_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD3_DATA__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD4__A 0x2440059
+#define FEC_OC_OCR_GRAB_RD4__W 10
+#define FEC_OC_OCR_GRAB_RD4__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD4__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD4_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD4_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD4_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD4_DATA__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD5__A 0x244005A
+#define FEC_OC_OCR_GRAB_RD5__W 10
+#define FEC_OC_OCR_GRAB_RD5__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD5__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD5_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD5_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD5_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD5_DATA__PRE 0x0
+
+#define FEC_DI_RAM__A 0x2450000
+
+#define FEC_RS_RAM__A 0x2460000
+
+#define FEC_OC_RAM__A 0x2470000
+
+#define IQM_COMM_EXEC__A 0x1800000
+#define IQM_COMM_EXEC__W 2
+#define IQM_COMM_EXEC__M 0x3
+#define IQM_COMM_EXEC__PRE 0x0
+#define IQM_COMM_EXEC_STOP 0x0
+#define IQM_COMM_EXEC_ACTIVE 0x1
+#define IQM_COMM_EXEC_HOLD 0x2
+
+#define IQM_COMM_MB__A 0x1800002
+#define IQM_COMM_MB__W 16
+#define IQM_COMM_MB__M 0xFFFF
+#define IQM_COMM_MB__PRE 0x0
+#define IQM_COMM_INT_REQ__A 0x1800003
+#define IQM_COMM_INT_REQ__W 2
+#define IQM_COMM_INT_REQ__M 0x3
+#define IQM_COMM_INT_REQ__PRE 0x0
+
+#define IQM_COMM_INT_REQ_AF_REQ__B 0
+#define IQM_COMM_INT_REQ_AF_REQ__W 1
+#define IQM_COMM_INT_REQ_AF_REQ__M 0x1
+#define IQM_COMM_INT_REQ_AF_REQ__PRE 0x0
+
+#define IQM_COMM_INT_REQ_CF_REQ__B 1
+#define IQM_COMM_INT_REQ_CF_REQ__W 1
+#define IQM_COMM_INT_REQ_CF_REQ__M 0x2
+#define IQM_COMM_INT_REQ_CF_REQ__PRE 0x0
+
+#define IQM_COMM_INT_STA__A 0x1800005
+#define IQM_COMM_INT_STA__W 16
+#define IQM_COMM_INT_STA__M 0xFFFF
+#define IQM_COMM_INT_STA__PRE 0x0
+#define IQM_COMM_INT_MSK__A 0x1800006
+#define IQM_COMM_INT_MSK__W 16
+#define IQM_COMM_INT_MSK__M 0xFFFF
+#define IQM_COMM_INT_MSK__PRE 0x0
+#define IQM_COMM_INT_STM__A 0x1800007
+#define IQM_COMM_INT_STM__W 16
+#define IQM_COMM_INT_STM__M 0xFFFF
+#define IQM_COMM_INT_STM__PRE 0x0
+
+#define IQM_FS_COMM_EXEC__A 0x1820000
+#define IQM_FS_COMM_EXEC__W 2
+#define IQM_FS_COMM_EXEC__M 0x3
+#define IQM_FS_COMM_EXEC__PRE 0x0
+#define IQM_FS_COMM_EXEC_STOP 0x0
+#define IQM_FS_COMM_EXEC_ACTIVE 0x1
+#define IQM_FS_COMM_EXEC_HOLD 0x2
+
+#define IQM_FS_COMM_MB__A 0x1820002
+#define IQM_FS_COMM_MB__W 2
+#define IQM_FS_COMM_MB__M 0x3
+#define IQM_FS_COMM_MB__PRE 0x0
+#define IQM_FS_COMM_MB_CTL__B 0
+#define IQM_FS_COMM_MB_CTL__W 1
+#define IQM_FS_COMM_MB_CTL__M 0x1
+#define IQM_FS_COMM_MB_CTL__PRE 0x0
+#define IQM_FS_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_FS_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_FS_COMM_MB_OBS__B 1
+#define IQM_FS_COMM_MB_OBS__W 1
+#define IQM_FS_COMM_MB_OBS__M 0x2
+#define IQM_FS_COMM_MB_OBS__PRE 0x0
+#define IQM_FS_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_FS_COMM_MB_OBS_OBS_ON 0x2
+
+#define IQM_FS_RATE_OFS_LO__A 0x1820010
+#define IQM_FS_RATE_OFS_LO__W 16
+#define IQM_FS_RATE_OFS_LO__M 0xFFFF
+#define IQM_FS_RATE_OFS_LO__PRE 0x0
+#define IQM_FS_RATE_OFS_HI__A 0x1820011
+#define IQM_FS_RATE_OFS_HI__W 12
+#define IQM_FS_RATE_OFS_HI__M 0xFFF
+#define IQM_FS_RATE_OFS_HI__PRE 0x0
+#define IQM_FS_RATE_LO__A 0x1820012
+#define IQM_FS_RATE_LO__W 16
+#define IQM_FS_RATE_LO__M 0xFFFF
+#define IQM_FS_RATE_LO__PRE 0x0
+#define IQM_FS_RATE_HI__A 0x1820013
+#define IQM_FS_RATE_HI__W 12
+#define IQM_FS_RATE_HI__M 0xFFF
+#define IQM_FS_RATE_HI__PRE 0x0
+
+#define IQM_FS_ADJ_SEL__A 0x1820014
+#define IQM_FS_ADJ_SEL__W 2
+#define IQM_FS_ADJ_SEL__M 0x3
+#define IQM_FS_ADJ_SEL__PRE 0x0
+#define IQM_FS_ADJ_SEL_OFF 0x0
+#define IQM_FS_ADJ_SEL_QAM 0x1
+#define IQM_FS_ADJ_SEL_VSB 0x2
+
+#define IQM_FD_COMM_EXEC__A 0x1830000
+#define IQM_FD_COMM_EXEC__W 2
+#define IQM_FD_COMM_EXEC__M 0x3
+#define IQM_FD_COMM_EXEC__PRE 0x0
+#define IQM_FD_COMM_EXEC_STOP 0x0
+#define IQM_FD_COMM_EXEC_ACTIVE 0x1
+#define IQM_FD_COMM_EXEC_HOLD 0x2
+
+#define IQM_FD_COMM_MB__A 0x1830002
+#define IQM_FD_COMM_MB__W 2
+#define IQM_FD_COMM_MB__M 0x3
+#define IQM_FD_COMM_MB__PRE 0x0
+#define IQM_FD_COMM_MB_CTL__B 0
+#define IQM_FD_COMM_MB_CTL__W 1
+#define IQM_FD_COMM_MB_CTL__M 0x1
+#define IQM_FD_COMM_MB_CTL__PRE 0x0
+#define IQM_FD_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_FD_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_FD_COMM_MB_OBS__B 1
+#define IQM_FD_COMM_MB_OBS__W 1
+#define IQM_FD_COMM_MB_OBS__M 0x2
+#define IQM_FD_COMM_MB_OBS__PRE 0x0
+#define IQM_FD_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_FD_COMM_MB_OBS_OBS_ON 0x2
+
+#define IQM_RC_COMM_EXEC__A 0x1840000
+#define IQM_RC_COMM_EXEC__W 2
+#define IQM_RC_COMM_EXEC__M 0x3
+#define IQM_RC_COMM_EXEC__PRE 0x0
+#define IQM_RC_COMM_EXEC_STOP 0x0
+#define IQM_RC_COMM_EXEC_ACTIVE 0x1
+#define IQM_RC_COMM_EXEC_HOLD 0x2
+
+#define IQM_RC_COMM_MB__A 0x1840002
+#define IQM_RC_COMM_MB__W 2
+#define IQM_RC_COMM_MB__M 0x3
+#define IQM_RC_COMM_MB__PRE 0x0
+#define IQM_RC_COMM_MB_CTL__B 0
+#define IQM_RC_COMM_MB_CTL__W 1
+#define IQM_RC_COMM_MB_CTL__M 0x1
+#define IQM_RC_COMM_MB_CTL__PRE 0x0
+#define IQM_RC_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_RC_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_RC_COMM_MB_OBS__B 1
+#define IQM_RC_COMM_MB_OBS__W 1
+#define IQM_RC_COMM_MB_OBS__M 0x2
+#define IQM_RC_COMM_MB_OBS__PRE 0x0
+#define IQM_RC_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_RC_COMM_MB_OBS_OBS_ON 0x2
+
+#define IQM_RC_RATE_OFS_LO__A 0x1840010
+#define IQM_RC_RATE_OFS_LO__W 16
+#define IQM_RC_RATE_OFS_LO__M 0xFFFF
+#define IQM_RC_RATE_OFS_LO__PRE 0x0
+#define IQM_RC_RATE_OFS_HI__A 0x1840011
+#define IQM_RC_RATE_OFS_HI__W 8
+#define IQM_RC_RATE_OFS_HI__M 0xFF
+#define IQM_RC_RATE_OFS_HI__PRE 0x0
+#define IQM_RC_RATE_LO__A 0x1840012
+#define IQM_RC_RATE_LO__W 16
+#define IQM_RC_RATE_LO__M 0xFFFF
+#define IQM_RC_RATE_LO__PRE 0x0
+#define IQM_RC_RATE_HI__A 0x1840013
+#define IQM_RC_RATE_HI__W 8
+#define IQM_RC_RATE_HI__M 0xFF
+#define IQM_RC_RATE_HI__PRE 0x0
+
+#define IQM_RC_ADJ_SEL__A 0x1840014
+#define IQM_RC_ADJ_SEL__W 2
+#define IQM_RC_ADJ_SEL__M 0x3
+#define IQM_RC_ADJ_SEL__PRE 0x0
+#define IQM_RC_ADJ_SEL_OFF 0x0
+#define IQM_RC_ADJ_SEL_QAM 0x1
+#define IQM_RC_ADJ_SEL_VSB 0x2
+
+#define IQM_RC_CROUT_ENA__A 0x1840015
+#define IQM_RC_CROUT_ENA__W 1
+#define IQM_RC_CROUT_ENA__M 0x1
+#define IQM_RC_CROUT_ENA__PRE 0x0
+
+#define IQM_RC_CROUT_ENA_ENA__B 0
+#define IQM_RC_CROUT_ENA_ENA__W 1
+#define IQM_RC_CROUT_ENA_ENA__M 0x1
+#define IQM_RC_CROUT_ENA_ENA__PRE 0x0
+
+#define IQM_RC_STRETCH__A 0x1840016
+#define IQM_RC_STRETCH__W 5
+#define IQM_RC_STRETCH__M 0x1F
+#define IQM_RC_STRETCH__PRE 0x0
+#define IQM_RC_STRETCH_QAM_B_64 0x1E
+#define IQM_RC_STRETCH_QAM_B_256 0x1C
+#define IQM_RC_STRETCH_ATV 0xF
+
+#define IQM_RT_COMM_EXEC__A 0x1850000
+#define IQM_RT_COMM_EXEC__W 2
+#define IQM_RT_COMM_EXEC__M 0x3
+#define IQM_RT_COMM_EXEC__PRE 0x0
+#define IQM_RT_COMM_EXEC_STOP 0x0
+#define IQM_RT_COMM_EXEC_ACTIVE 0x1
+#define IQM_RT_COMM_EXEC_HOLD 0x2
+
+#define IQM_RT_COMM_MB__A 0x1850002
+#define IQM_RT_COMM_MB__W 2
+#define IQM_RT_COMM_MB__M 0x3
+#define IQM_RT_COMM_MB__PRE 0x0
+#define IQM_RT_COMM_MB_CTL__B 0
+#define IQM_RT_COMM_MB_CTL__W 1
+#define IQM_RT_COMM_MB_CTL__M 0x1
+#define IQM_RT_COMM_MB_CTL__PRE 0x0
+#define IQM_RT_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_RT_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_RT_COMM_MB_OBS__B 1
+#define IQM_RT_COMM_MB_OBS__W 1
+#define IQM_RT_COMM_MB_OBS__M 0x2
+#define IQM_RT_COMM_MB_OBS__PRE 0x0
+#define IQM_RT_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_RT_COMM_MB_OBS_OBS_ON 0x2
+
+#define IQM_RT_ACTIVE__A 0x1850010
+#define IQM_RT_ACTIVE__W 2
+#define IQM_RT_ACTIVE__M 0x3
+#define IQM_RT_ACTIVE__PRE 0x0
+
+#define IQM_RT_ACTIVE_ACTIVE_RT__B 0
+#define IQM_RT_ACTIVE_ACTIVE_RT__W 1
+#define IQM_RT_ACTIVE_ACTIVE_RT__M 0x1
+#define IQM_RT_ACTIVE_ACTIVE_RT__PRE 0x0
+#define IQM_RT_ACTIVE_ACTIVE_RT_ATV_FCR_OFF 0x0
+#define IQM_RT_ACTIVE_ACTIVE_RT_ATV_FCR_ON 0x1
+
+#define IQM_RT_ACTIVE_ACTIVE_CR__B 1
+#define IQM_RT_ACTIVE_ACTIVE_CR__W 1
+#define IQM_RT_ACTIVE_ACTIVE_CR__M 0x2
+#define IQM_RT_ACTIVE_ACTIVE_CR__PRE 0x0
+#define IQM_RT_ACTIVE_ACTIVE_CR_ATV_CR_OFF 0x0
+#define IQM_RT_ACTIVE_ACTIVE_CR_ATV_CR_ON 0x2
+
+#define IQM_RT_LO_INCR__A 0x1850011
+#define IQM_RT_LO_INCR__W 12
+#define IQM_RT_LO_INCR__M 0xFFF
+#define IQM_RT_LO_INCR__PRE 0x588
+#define IQM_RT_LO_INCR_FM 0x0
+#define IQM_RT_LO_INCR_MN 0x588
+
+#define IQM_RT_ROT_BP__A 0x1850012
+#define IQM_RT_ROT_BP__W 2
+#define IQM_RT_ROT_BP__M 0x3
+#define IQM_RT_ROT_BP__PRE 0x0
+
+#define IQM_RT_ROT_BP_ROT_OFF__B 0
+#define IQM_RT_ROT_BP_ROT_OFF__W 1
+#define IQM_RT_ROT_BP_ROT_OFF__M 0x1
+#define IQM_RT_ROT_BP_ROT_OFF__PRE 0x0
+#define IQM_RT_ROT_BP_ROT_OFF_ACTIVE 0x0
+#define IQM_RT_ROT_BP_ROT_OFF_OFF 0x1
+
+#define IQM_RT_ROT_BP_ROT_BPF__B 1
+#define IQM_RT_ROT_BP_ROT_BPF__W 1
+#define IQM_RT_ROT_BP_ROT_BPF__M 0x2
+#define IQM_RT_ROT_BP_ROT_BPF__PRE 0x0
+
+#define IQM_RT_LP_BP__A 0x1850013
+#define IQM_RT_LP_BP__W 1
+#define IQM_RT_LP_BP__M 0x1
+#define IQM_RT_LP_BP__PRE 0x0
+
+#define IQM_RT_DELAY__A 0x1850014
+#define IQM_RT_DELAY__W 7
+#define IQM_RT_DELAY__M 0x7F
+#define IQM_RT_DELAY__PRE 0x45
+
+#define IQM_CF_COMM_EXEC__A 0x1860000
+#define IQM_CF_COMM_EXEC__W 2
+#define IQM_CF_COMM_EXEC__M 0x3
+#define IQM_CF_COMM_EXEC__PRE 0x0
+#define IQM_CF_COMM_EXEC_STOP 0x0
+#define IQM_CF_COMM_EXEC_ACTIVE 0x1
+#define IQM_CF_COMM_EXEC_HOLD 0x2
+
+#define IQM_CF_COMM_MB__A 0x1860002
+#define IQM_CF_COMM_MB__W 2
+#define IQM_CF_COMM_MB__M 0x3
+#define IQM_CF_COMM_MB__PRE 0x0
+#define IQM_CF_COMM_MB_CTL__B 0
+#define IQM_CF_COMM_MB_CTL__W 1
+#define IQM_CF_COMM_MB_CTL__M 0x1
+#define IQM_CF_COMM_MB_CTL__PRE 0x0
+#define IQM_CF_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_CF_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_CF_COMM_MB_OBS__B 1
+#define IQM_CF_COMM_MB_OBS__W 1
+#define IQM_CF_COMM_MB_OBS__M 0x2
+#define IQM_CF_COMM_MB_OBS__PRE 0x0
+#define IQM_CF_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_CF_COMM_MB_OBS_OBS_ON 0x2
+
+#define IQM_CF_COMM_INT_REQ__A 0x1860003
+#define IQM_CF_COMM_INT_REQ__W 1
+#define IQM_CF_COMM_INT_REQ__M 0x1
+#define IQM_CF_COMM_INT_REQ__PRE 0x0
+#define IQM_CF_COMM_INT_STA__A 0x1860005
+#define IQM_CF_COMM_INT_STA__W 1
+#define IQM_CF_COMM_INT_STA__M 0x1
+#define IQM_CF_COMM_INT_STA__PRE 0x0
+#define IQM_CF_COMM_INT_STA_PM__B 0
+#define IQM_CF_COMM_INT_STA_PM__W 1
+#define IQM_CF_COMM_INT_STA_PM__M 0x1
+#define IQM_CF_COMM_INT_STA_PM__PRE 0x0
+
+#define IQM_CF_COMM_INT_MSK__A 0x1860006
+#define IQM_CF_COMM_INT_MSK__W 1
+#define IQM_CF_COMM_INT_MSK__M 0x1
+#define IQM_CF_COMM_INT_MSK__PRE 0x0
+#define IQM_CF_COMM_INT_MSK_PM__B 0
+#define IQM_CF_COMM_INT_MSK_PM__W 1
+#define IQM_CF_COMM_INT_MSK_PM__M 0x1
+#define IQM_CF_COMM_INT_MSK_PM__PRE 0x0
+
+#define IQM_CF_COMM_INT_STM__A 0x1860007
+#define IQM_CF_COMM_INT_STM__W 1
+#define IQM_CF_COMM_INT_STM__M 0x1
+#define IQM_CF_COMM_INT_STM__PRE 0x0
+#define IQM_CF_COMM_INT_STM_PM__B 0
+#define IQM_CF_COMM_INT_STM_PM__W 1
+#define IQM_CF_COMM_INT_STM_PM__M 0x1
+#define IQM_CF_COMM_INT_STM_PM__PRE 0x0
+
+#define IQM_CF_SYMMETRIC__A 0x1860010
+#define IQM_CF_SYMMETRIC__W 2
+#define IQM_CF_SYMMETRIC__M 0x3
+#define IQM_CF_SYMMETRIC__PRE 0x0
+
+#define IQM_CF_SYMMETRIC_RE__B 0
+#define IQM_CF_SYMMETRIC_RE__W 1
+#define IQM_CF_SYMMETRIC_RE__M 0x1
+#define IQM_CF_SYMMETRIC_RE__PRE 0x0
+
+#define IQM_CF_SYMMETRIC_IM__B 1
+#define IQM_CF_SYMMETRIC_IM__W 1
+#define IQM_CF_SYMMETRIC_IM__M 0x2
+#define IQM_CF_SYMMETRIC_IM__PRE 0x0
+
+#define IQM_CF_MIDTAP__A 0x1860011
+#define IQM_CF_MIDTAP__W 2
+#define IQM_CF_MIDTAP__M 0x3
+#define IQM_CF_MIDTAP__PRE 0x3
+
+#define IQM_CF_MIDTAP_RE__B 0
+#define IQM_CF_MIDTAP_RE__W 1
+#define IQM_CF_MIDTAP_RE__M 0x1
+#define IQM_CF_MIDTAP_RE__PRE 0x1
+
+#define IQM_CF_MIDTAP_IM__B 1
+#define IQM_CF_MIDTAP_IM__W 1
+#define IQM_CF_MIDTAP_IM__M 0x2
+#define IQM_CF_MIDTAP_IM__PRE 0x2
+
+#define IQM_CF_OUT_ENA__A 0x1860012
+#define IQM_CF_OUT_ENA__W 3
+#define IQM_CF_OUT_ENA__M 0x7
+#define IQM_CF_OUT_ENA__PRE 0x0
+
+#define IQM_CF_OUT_ENA_ATV__B 0
+#define IQM_CF_OUT_ENA_ATV__W 1
+#define IQM_CF_OUT_ENA_ATV__M 0x1
+#define IQM_CF_OUT_ENA_ATV__PRE 0x0
+
+#define IQM_CF_OUT_ENA_QAM__B 1
+#define IQM_CF_OUT_ENA_QAM__W 1
+#define IQM_CF_OUT_ENA_QAM__M 0x2
+#define IQM_CF_OUT_ENA_QAM__PRE 0x0
+
+#define IQM_CF_OUT_ENA_VSB__B 2
+#define IQM_CF_OUT_ENA_VSB__W 1
+#define IQM_CF_OUT_ENA_VSB__M 0x4
+#define IQM_CF_OUT_ENA_VSB__PRE 0x0
+
+#define IQM_CF_ADJ_SEL__A 0x1860013
+#define IQM_CF_ADJ_SEL__W 2
+#define IQM_CF_ADJ_SEL__M 0x3
+#define IQM_CF_ADJ_SEL__PRE 0x0
+#define IQM_CF_SCALE__A 0x1860014
+#define IQM_CF_SCALE__W 14
+#define IQM_CF_SCALE__M 0x3FFF
+#define IQM_CF_SCALE__PRE 0x400
+
+#define IQM_CF_SCALE_SH__A 0x1860015
+#define IQM_CF_SCALE_SH__W 2
+#define IQM_CF_SCALE_SH__M 0x3
+#define IQM_CF_SCALE_SH__PRE 0x0
+
+#define IQM_CF_AMP__A 0x1860016
+#define IQM_CF_AMP__W 14
+#define IQM_CF_AMP__M 0x3FFF
+#define IQM_CF_AMP__PRE 0x0
+
+#define IQM_CF_POW_MEAS_LEN__A 0x1860017
+#define IQM_CF_POW_MEAS_LEN__W 3
+#define IQM_CF_POW_MEAS_LEN__M 0x7
+#define IQM_CF_POW_MEAS_LEN__PRE 0x2
+#define IQM_CF_POW_MEAS_LEN_QAM_B_64 0x1
+#define IQM_CF_POW_MEAS_LEN_QAM_B_256 0x1
+
+#define IQM_CF_POW__A 0x1860018
+#define IQM_CF_POW__W 16
+#define IQM_CF_POW__M 0xFFFF
+#define IQM_CF_POW__PRE 0x2
+#define IQM_CF_TAP_RE0__A 0x1860020
+#define IQM_CF_TAP_RE0__W 7
+#define IQM_CF_TAP_RE0__M 0x7F
+#define IQM_CF_TAP_RE0__PRE 0x2
+#define IQM_CF_TAP_RE1__A 0x1860021
+#define IQM_CF_TAP_RE1__W 7
+#define IQM_CF_TAP_RE1__M 0x7F
+#define IQM_CF_TAP_RE1__PRE 0x2
+#define IQM_CF_TAP_RE2__A 0x1860022
+#define IQM_CF_TAP_RE2__W 7
+#define IQM_CF_TAP_RE2__M 0x7F
+#define IQM_CF_TAP_RE2__PRE 0x2
+#define IQM_CF_TAP_RE3__A 0x1860023
+#define IQM_CF_TAP_RE3__W 7
+#define IQM_CF_TAP_RE3__M 0x7F
+#define IQM_CF_TAP_RE3__PRE 0x2
+#define IQM_CF_TAP_RE4__A 0x1860024
+#define IQM_CF_TAP_RE4__W 7
+#define IQM_CF_TAP_RE4__M 0x7F
+#define IQM_CF_TAP_RE4__PRE 0x2
+#define IQM_CF_TAP_RE5__A 0x1860025
+#define IQM_CF_TAP_RE5__W 7
+#define IQM_CF_TAP_RE5__M 0x7F
+#define IQM_CF_TAP_RE5__PRE 0x2
+#define IQM_CF_TAP_RE6__A 0x1860026
+#define IQM_CF_TAP_RE6__W 7
+#define IQM_CF_TAP_RE6__M 0x7F
+#define IQM_CF_TAP_RE6__PRE 0x2
+#define IQM_CF_TAP_RE7__A 0x1860027
+#define IQM_CF_TAP_RE7__W 9
+#define IQM_CF_TAP_RE7__M 0x1FF
+#define IQM_CF_TAP_RE7__PRE 0x2
+#define IQM_CF_TAP_RE8__A 0x1860028
+#define IQM_CF_TAP_RE8__W 9
+#define IQM_CF_TAP_RE8__M 0x1FF
+#define IQM_CF_TAP_RE8__PRE 0x2
+#define IQM_CF_TAP_RE9__A 0x1860029
+#define IQM_CF_TAP_RE9__W 9
+#define IQM_CF_TAP_RE9__M 0x1FF
+#define IQM_CF_TAP_RE9__PRE 0x2
+#define IQM_CF_TAP_RE10__A 0x186002A
+#define IQM_CF_TAP_RE10__W 9
+#define IQM_CF_TAP_RE10__M 0x1FF
+#define IQM_CF_TAP_RE10__PRE 0x2
+#define IQM_CF_TAP_RE11__A 0x186002B
+#define IQM_CF_TAP_RE11__W 9
+#define IQM_CF_TAP_RE11__M 0x1FF
+#define IQM_CF_TAP_RE11__PRE 0x2
+#define IQM_CF_TAP_RE12__A 0x186002C
+#define IQM_CF_TAP_RE12__W 9
+#define IQM_CF_TAP_RE12__M 0x1FF
+#define IQM_CF_TAP_RE12__PRE 0x2
+#define IQM_CF_TAP_RE13__A 0x186002D
+#define IQM_CF_TAP_RE13__W 9
+#define IQM_CF_TAP_RE13__M 0x1FF
+#define IQM_CF_TAP_RE13__PRE 0x2
+#define IQM_CF_TAP_RE14__A 0x186002E
+#define IQM_CF_TAP_RE14__W 9
+#define IQM_CF_TAP_RE14__M 0x1FF
+#define IQM_CF_TAP_RE14__PRE 0x2
+#define IQM_CF_TAP_RE15__A 0x186002F
+#define IQM_CF_TAP_RE15__W 9
+#define IQM_CF_TAP_RE15__M 0x1FF
+#define IQM_CF_TAP_RE15__PRE 0x2
+#define IQM_CF_TAP_RE16__A 0x1860030
+#define IQM_CF_TAP_RE16__W 9
+#define IQM_CF_TAP_RE16__M 0x1FF
+#define IQM_CF_TAP_RE16__PRE 0x2
+#define IQM_CF_TAP_RE17__A 0x1860031
+#define IQM_CF_TAP_RE17__W 9
+#define IQM_CF_TAP_RE17__M 0x1FF
+#define IQM_CF_TAP_RE17__PRE 0x2
+#define IQM_CF_TAP_RE18__A 0x1860032
+#define IQM_CF_TAP_RE18__W 9
+#define IQM_CF_TAP_RE18__M 0x1FF
+#define IQM_CF_TAP_RE18__PRE 0x2
+#define IQM_CF_TAP_RE19__A 0x1860033
+#define IQM_CF_TAP_RE19__W 9
+#define IQM_CF_TAP_RE19__M 0x1FF
+#define IQM_CF_TAP_RE19__PRE 0x2
+#define IQM_CF_TAP_RE20__A 0x1860034
+#define IQM_CF_TAP_RE20__W 9
+#define IQM_CF_TAP_RE20__M 0x1FF
+#define IQM_CF_TAP_RE20__PRE 0x2
+#define IQM_CF_TAP_RE21__A 0x1860035
+#define IQM_CF_TAP_RE21__W 11
+#define IQM_CF_TAP_RE21__M 0x7FF
+#define IQM_CF_TAP_RE21__PRE 0x2
+#define IQM_CF_TAP_RE22__A 0x1860036
+#define IQM_CF_TAP_RE22__W 11
+#define IQM_CF_TAP_RE22__M 0x7FF
+#define IQM_CF_TAP_RE22__PRE 0x2
+#define IQM_CF_TAP_RE23__A 0x1860037
+#define IQM_CF_TAP_RE23__W 11
+#define IQM_CF_TAP_RE23__M 0x7FF
+#define IQM_CF_TAP_RE23__PRE 0x2
+#define IQM_CF_TAP_RE24__A 0x1860038
+#define IQM_CF_TAP_RE24__W 11
+#define IQM_CF_TAP_RE24__M 0x7FF
+#define IQM_CF_TAP_RE24__PRE 0x2
+#define IQM_CF_TAP_RE25__A 0x1860039
+#define IQM_CF_TAP_RE25__W 11
+#define IQM_CF_TAP_RE25__M 0x7FF
+#define IQM_CF_TAP_RE25__PRE 0x2
+#define IQM_CF_TAP_RE26__A 0x186003A
+#define IQM_CF_TAP_RE26__W 11
+#define IQM_CF_TAP_RE26__M 0x7FF
+#define IQM_CF_TAP_RE26__PRE 0x2
+#define IQM_CF_TAP_RE27__A 0x186003B
+#define IQM_CF_TAP_RE27__W 11
+#define IQM_CF_TAP_RE27__M 0x7FF
+#define IQM_CF_TAP_RE27__PRE 0x2
+#define IQM_CF_TAP_IM0__A 0x1860040
+#define IQM_CF_TAP_IM0__W 7
+#define IQM_CF_TAP_IM0__M 0x7F
+#define IQM_CF_TAP_IM0__PRE 0x2
+#define IQM_CF_TAP_IM1__A 0x1860041
+#define IQM_CF_TAP_IM1__W 7
+#define IQM_CF_TAP_IM1__M 0x7F
+#define IQM_CF_TAP_IM1__PRE 0x2
+#define IQM_CF_TAP_IM2__A 0x1860042
+#define IQM_CF_TAP_IM2__W 7
+#define IQM_CF_TAP_IM2__M 0x7F
+#define IQM_CF_TAP_IM2__PRE 0x2
+#define IQM_CF_TAP_IM3__A 0x1860043
+#define IQM_CF_TAP_IM3__W 7
+#define IQM_CF_TAP_IM3__M 0x7F
+#define IQM_CF_TAP_IM3__PRE 0x2
+#define IQM_CF_TAP_IM4__A 0x1860044
+#define IQM_CF_TAP_IM4__W 7
+#define IQM_CF_TAP_IM4__M 0x7F
+#define IQM_CF_TAP_IM4__PRE 0x2
+#define IQM_CF_TAP_IM5__A 0x1860045
+#define IQM_CF_TAP_IM5__W 7
+#define IQM_CF_TAP_IM5__M 0x7F
+#define IQM_CF_TAP_IM5__PRE 0x2
+#define IQM_CF_TAP_IM6__A 0x1860046
+#define IQM_CF_TAP_IM6__W 7
+#define IQM_CF_TAP_IM6__M 0x7F
+#define IQM_CF_TAP_IM6__PRE 0x2
+#define IQM_CF_TAP_IM7__A 0x1860047
+#define IQM_CF_TAP_IM7__W 9
+#define IQM_CF_TAP_IM7__M 0x1FF
+#define IQM_CF_TAP_IM7__PRE 0x2
+#define IQM_CF_TAP_IM8__A 0x1860048
+#define IQM_CF_TAP_IM8__W 9
+#define IQM_CF_TAP_IM8__M 0x1FF
+#define IQM_CF_TAP_IM8__PRE 0x2
+#define IQM_CF_TAP_IM9__A 0x1860049
+#define IQM_CF_TAP_IM9__W 9
+#define IQM_CF_TAP_IM9__M 0x1FF
+#define IQM_CF_TAP_IM9__PRE 0x2
+#define IQM_CF_TAP_IM10__A 0x186004A
+#define IQM_CF_TAP_IM10__W 9
+#define IQM_CF_TAP_IM10__M 0x1FF
+#define IQM_CF_TAP_IM10__PRE 0x2
+#define IQM_CF_TAP_IM11__A 0x186004B
+#define IQM_CF_TAP_IM11__W 9
+#define IQM_CF_TAP_IM11__M 0x1FF
+#define IQM_CF_TAP_IM11__PRE 0x2
+#define IQM_CF_TAP_IM12__A 0x186004C
+#define IQM_CF_TAP_IM12__W 9
+#define IQM_CF_TAP_IM12__M 0x1FF
+#define IQM_CF_TAP_IM12__PRE 0x2
+#define IQM_CF_TAP_IM13__A 0x186004D
+#define IQM_CF_TAP_IM13__W 9
+#define IQM_CF_TAP_IM13__M 0x1FF
+#define IQM_CF_TAP_IM13__PRE 0x2
+#define IQM_CF_TAP_IM14__A 0x186004E
+#define IQM_CF_TAP_IM14__W 9
+#define IQM_CF_TAP_IM14__M 0x1FF
+#define IQM_CF_TAP_IM14__PRE 0x2
+#define IQM_CF_TAP_IM15__A 0x186004F
+#define IQM_CF_TAP_IM15__W 9
+#define IQM_CF_TAP_IM15__M 0x1FF
+#define IQM_CF_TAP_IM15__PRE 0x2
+#define IQM_CF_TAP_IM16__A 0x1860050
+#define IQM_CF_TAP_IM16__W 9
+#define IQM_CF_TAP_IM16__M 0x1FF
+#define IQM_CF_TAP_IM16__PRE 0x2
+#define IQM_CF_TAP_IM17__A 0x1860051
+#define IQM_CF_TAP_IM17__W 9
+#define IQM_CF_TAP_IM17__M 0x1FF
+#define IQM_CF_TAP_IM17__PRE 0x2
+#define IQM_CF_TAP_IM18__A 0x1860052
+#define IQM_CF_TAP_IM18__W 9
+#define IQM_CF_TAP_IM18__M 0x1FF
+#define IQM_CF_TAP_IM18__PRE 0x2
+#define IQM_CF_TAP_IM19__A 0x1860053
+#define IQM_CF_TAP_IM19__W 9
+#define IQM_CF_TAP_IM19__M 0x1FF
+#define IQM_CF_TAP_IM19__PRE 0x2
+#define IQM_CF_TAP_IM20__A 0x1860054
+#define IQM_CF_TAP_IM20__W 9
+#define IQM_CF_TAP_IM20__M 0x1FF
+#define IQM_CF_TAP_IM20__PRE 0x2
+#define IQM_CF_TAP_IM21__A 0x1860055
+#define IQM_CF_TAP_IM21__W 11
+#define IQM_CF_TAP_IM21__M 0x7FF
+#define IQM_CF_TAP_IM21__PRE 0x2
+#define IQM_CF_TAP_IM22__A 0x1860056
+#define IQM_CF_TAP_IM22__W 11
+#define IQM_CF_TAP_IM22__M 0x7FF
+#define IQM_CF_TAP_IM22__PRE 0x2
+#define IQM_CF_TAP_IM23__A 0x1860057
+#define IQM_CF_TAP_IM23__W 11
+#define IQM_CF_TAP_IM23__M 0x7FF
+#define IQM_CF_TAP_IM23__PRE 0x2
+#define IQM_CF_TAP_IM24__A 0x1860058
+#define IQM_CF_TAP_IM24__W 11
+#define IQM_CF_TAP_IM24__M 0x7FF
+#define IQM_CF_TAP_IM24__PRE 0x2
+#define IQM_CF_TAP_IM25__A 0x1860059
+#define IQM_CF_TAP_IM25__W 11
+#define IQM_CF_TAP_IM25__M 0x7FF
+#define IQM_CF_TAP_IM25__PRE 0x2
+#define IQM_CF_TAP_IM26__A 0x186005A
+#define IQM_CF_TAP_IM26__W 11
+#define IQM_CF_TAP_IM26__M 0x7FF
+#define IQM_CF_TAP_IM26__PRE 0x2
+#define IQM_CF_TAP_IM27__A 0x186005B
+#define IQM_CF_TAP_IM27__W 11
+#define IQM_CF_TAP_IM27__M 0x7FF
+#define IQM_CF_TAP_IM27__PRE 0x2
+
+#define IQM_AF_COMM_EXEC__A 0x1870000
+#define IQM_AF_COMM_EXEC__W 2
+#define IQM_AF_COMM_EXEC__M 0x3
+#define IQM_AF_COMM_EXEC__PRE 0x0
+#define IQM_AF_COMM_EXEC_STOP 0x0
+#define IQM_AF_COMM_EXEC_ACTIVE 0x1
+#define IQM_AF_COMM_EXEC_HOLD 0x2
+
+#define IQM_AF_COMM_MB__A 0x1870002
+#define IQM_AF_COMM_MB__W 8
+#define IQM_AF_COMM_MB__M 0xFF
+#define IQM_AF_COMM_MB__PRE 0x0
+#define IQM_AF_COMM_MB_CTL__B 0
+#define IQM_AF_COMM_MB_CTL__W 1
+#define IQM_AF_COMM_MB_CTL__M 0x1
+#define IQM_AF_COMM_MB_CTL__PRE 0x0
+#define IQM_AF_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_AF_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_AF_COMM_MB_OBS__B 1
+#define IQM_AF_COMM_MB_OBS__W 1
+#define IQM_AF_COMM_MB_OBS__M 0x2
+#define IQM_AF_COMM_MB_OBS__PRE 0x0
+#define IQM_AF_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_AF_COMM_MB_OBS_OBS_ON 0x2
+#define IQM_AF_COMM_MB_MUX_CTRL__B 2
+#define IQM_AF_COMM_MB_MUX_CTRL__W 3
+#define IQM_AF_COMM_MB_MUX_CTRL__M 0x1C
+#define IQM_AF_COMM_MB_MUX_CTRL__PRE 0x0
+#define IQM_AF_COMM_MB_MUX_CTRL_AF_DATA_INPUT 0x0
+#define IQM_AF_COMM_MB_MUX_CTRL_SENSE_INPUT 0x4
+#define IQM_AF_COMM_MB_MUX_CTRL_AF_DATA_OUTPUT 0x8
+#define IQM_AF_COMM_MB_MUX_CTRL_IF_AGC_OUTPUT 0xC
+#define IQM_AF_COMM_MB_MUX_CTRL_RF_AGC_OUTPUT 0x10
+#define IQM_AF_COMM_MB_MUX_OBS__B 5
+#define IQM_AF_COMM_MB_MUX_OBS__W 3
+#define IQM_AF_COMM_MB_MUX_OBS__M 0xE0
+#define IQM_AF_COMM_MB_MUX_OBS__PRE 0x0
+#define IQM_AF_COMM_MB_MUX_OBS_AF_DATA_INPUT 0x0
+#define IQM_AF_COMM_MB_MUX_OBS_SENSE_INPUT 0x20
+#define IQM_AF_COMM_MB_MUX_OBS_AF_DATA_OUTPUT 0x40
+#define IQM_AF_COMM_MB_MUX_OBS_IF_AGC_OUTPUT 0x60
+#define IQM_AF_COMM_MB_MUX_OBS_RF_AGC_OUTPUT 0x80
+
+#define IQM_AF_COMM_INT_REQ__A 0x1870003
+#define IQM_AF_COMM_INT_REQ__W 1
+#define IQM_AF_COMM_INT_REQ__M 0x1
+#define IQM_AF_COMM_INT_REQ__PRE 0x0
+#define IQM_AF_COMM_INT_STA__A 0x1870005
+#define IQM_AF_COMM_INT_STA__W 2
+#define IQM_AF_COMM_INT_STA__M 0x3
+#define IQM_AF_COMM_INT_STA__PRE 0x0
+#define IQM_AF_COMM_INT_STA_CLP_INT_STA__B 0
+#define IQM_AF_COMM_INT_STA_CLP_INT_STA__W 1
+#define IQM_AF_COMM_INT_STA_CLP_INT_STA__M 0x1
+#define IQM_AF_COMM_INT_STA_CLP_INT_STA__PRE 0x0
+#define IQM_AF_COMM_INT_STA_SNS_INT_STA__B 1
+#define IQM_AF_COMM_INT_STA_SNS_INT_STA__W 1
+#define IQM_AF_COMM_INT_STA_SNS_INT_STA__M 0x2
+#define IQM_AF_COMM_INT_STA_SNS_INT_STA__PRE 0x0
+
+#define IQM_AF_COMM_INT_MSK__A 0x1870006
+#define IQM_AF_COMM_INT_MSK__W 2
+#define IQM_AF_COMM_INT_MSK__M 0x3
+#define IQM_AF_COMM_INT_MSK__PRE 0x0
+#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__B 0
+#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__W 1
+#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__M 0x1
+#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__PRE 0x0
+#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__B 1
+#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__W 1
+#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__M 0x2
+#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__PRE 0x0
+
+#define IQM_AF_COMM_INT_STM__A 0x1870007
+#define IQM_AF_COMM_INT_STM__W 2
+#define IQM_AF_COMM_INT_STM__M 0x3
+#define IQM_AF_COMM_INT_STM__PRE 0x0
+#define IQM_AF_COMM_INT_STM_CLP_INT_STA__B 0
+#define IQM_AF_COMM_INT_STM_CLP_INT_STA__W 1
+#define IQM_AF_COMM_INT_STM_CLP_INT_STA__M 0x1
+#define IQM_AF_COMM_INT_STM_CLP_INT_STA__PRE 0x0
+#define IQM_AF_COMM_INT_STM_SNS_INT_STA__B 1
+#define IQM_AF_COMM_INT_STM_SNS_INT_STA__W 1
+#define IQM_AF_COMM_INT_STM_SNS_INT_STA__M 0x2
+#define IQM_AF_COMM_INT_STM_SNS_INT_STA__PRE 0x0
+
+#define IQM_AF_FDB_SEL__A 0x1870010
+#define IQM_AF_FDB_SEL__W 1
+#define IQM_AF_FDB_SEL__M 0x1
+#define IQM_AF_FDB_SEL__PRE 0x0
+
+#define IQM_AF_INVEXT__A 0x1870011
+#define IQM_AF_INVEXT__W 1
+#define IQM_AF_INVEXT__M 0x1
+#define IQM_AF_INVEXT__PRE 0x0
+#define IQM_AF_CLKNEG__A 0x1870012
+#define IQM_AF_CLKNEG__W 2
+#define IQM_AF_CLKNEG__M 0x3
+#define IQM_AF_CLKNEG__PRE 0x0
+
+#define IQM_AF_CLKNEG_CLKNEGPEAK__B 0
+#define IQM_AF_CLKNEG_CLKNEGPEAK__W 1
+#define IQM_AF_CLKNEG_CLKNEGPEAK__M 0x1
+#define IQM_AF_CLKNEG_CLKNEGPEAK__PRE 0x0
+#define IQM_AF_CLKNEG_CLKNEGPEAK_CLK_ADC_PEAK_POS 0x0
+#define IQM_AF_CLKNEG_CLKNEGPEAK_CLK_ADC_PEAK_NEG 0x1
+
+#define IQM_AF_CLKNEG_CLKNEGDATA__B 1
+#define IQM_AF_CLKNEG_CLKNEGDATA__W 1
+#define IQM_AF_CLKNEG_CLKNEGDATA__M 0x2
+#define IQM_AF_CLKNEG_CLKNEGDATA__PRE 0x0
+#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS 0x0
+#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG 0x2
+
+#define IQM_AF_MON_IN_MUX__A 0x1870013
+#define IQM_AF_MON_IN_MUX__W 2
+#define IQM_AF_MON_IN_MUX__M 0x3
+#define IQM_AF_MON_IN_MUX__PRE 0x0
+
+#define IQM_AF_MON_IN5__A 0x1870014
+#define IQM_AF_MON_IN5__W 10
+#define IQM_AF_MON_IN5__M 0x3FF
+#define IQM_AF_MON_IN5__PRE 0x0
+
+#define IQM_AF_MON_IN4__A 0x1870015
+#define IQM_AF_MON_IN4__W 10
+#define IQM_AF_MON_IN4__M 0x3FF
+#define IQM_AF_MON_IN4__PRE 0x0
+
+#define IQM_AF_MON_IN3__A 0x1870016
+#define IQM_AF_MON_IN3__W 10
+#define IQM_AF_MON_IN3__M 0x3FF
+#define IQM_AF_MON_IN3__PRE 0x0
+
+#define IQM_AF_MON_IN2__A 0x1870017
+#define IQM_AF_MON_IN2__W 10
+#define IQM_AF_MON_IN2__M 0x3FF
+#define IQM_AF_MON_IN2__PRE 0x0
+
+#define IQM_AF_MON_IN1__A 0x1870018
+#define IQM_AF_MON_IN1__W 10
+#define IQM_AF_MON_IN1__M 0x3FF
+#define IQM_AF_MON_IN1__PRE 0x0
+
+#define IQM_AF_MON_IN0__A 0x1870019
+#define IQM_AF_MON_IN0__W 10
+#define IQM_AF_MON_IN0__M 0x3FF
+#define IQM_AF_MON_IN0__PRE 0x0
+
+#define IQM_AF_MON_IN_VAL__A 0x187001A
+#define IQM_AF_MON_IN_VAL__W 1
+#define IQM_AF_MON_IN_VAL__M 0x1
+#define IQM_AF_MON_IN_VAL__PRE 0x0
+
+#define IQM_AF_START_LOCK__A 0x187001B
+#define IQM_AF_START_LOCK__W 1
+#define IQM_AF_START_LOCK__M 0x1
+#define IQM_AF_START_LOCK__PRE 0x0
+
+#define IQM_AF_PHASE0__A 0x187001C
+#define IQM_AF_PHASE0__W 7
+#define IQM_AF_PHASE0__M 0x7F
+#define IQM_AF_PHASE0__PRE 0x0
+
+#define IQM_AF_PHASE1__A 0x187001D
+#define IQM_AF_PHASE1__W 7
+#define IQM_AF_PHASE1__M 0x7F
+#define IQM_AF_PHASE1__PRE 0x0
+
+#define IQM_AF_PHASE2__A 0x187001E
+#define IQM_AF_PHASE2__W 7
+#define IQM_AF_PHASE2__M 0x7F
+#define IQM_AF_PHASE2__PRE 0x0
+
+#define IQM_AF_SCU_PHASE__A 0x187001F
+#define IQM_AF_SCU_PHASE__W 2
+#define IQM_AF_SCU_PHASE__M 0x3
+#define IQM_AF_SCU_PHASE__PRE 0x0
+
+#define IQM_AF_SYNC_SEL__A 0x1870020
+#define IQM_AF_SYNC_SEL__W 2
+#define IQM_AF_SYNC_SEL__M 0x3
+#define IQM_AF_SYNC_SEL__PRE 0x0
+#define IQM_AF_ADC_CONF__A 0x1870021
+#define IQM_AF_ADC_CONF__W 4
+#define IQM_AF_ADC_CONF__M 0xF
+#define IQM_AF_ADC_CONF__PRE 0x0
+
+#define IQM_AF_ADC_CONF_ADC_SIGN__B 0
+#define IQM_AF_ADC_CONF_ADC_SIGN__W 1
+#define IQM_AF_ADC_CONF_ADC_SIGN__M 0x1
+#define IQM_AF_ADC_CONF_ADC_SIGN__PRE 0x0
+#define IQM_AF_ADC_CONF_ADC_SIGN_ADC_SIGNED 0x0
+#define IQM_AF_ADC_CONF_ADC_SIGN_ADC_UNSIGNED 0x1
+
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC__B 1
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC__W 1
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC__M 0x2
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC__PRE 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC_ADC_NORMAL 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC_ADC_BITREVERSED 0x2
+
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__B 2
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__W 1
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__M 0x4
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__PRE 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI_IFAGC_DAC_NORMAL 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI_IFAGC_DAC_BITREVERSED 0x4
+
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__B 3
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__W 1
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__M 0x8
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__PRE 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR_RFAGC_DAC_NORMAL 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR_RFAGC_DAC_BITREVERSED 0x8
+
+#define IQM_AF_CLP_CLIP__A 0x1870022
+#define IQM_AF_CLP_CLIP__W 16
+#define IQM_AF_CLP_CLIP__M 0xFFFF
+#define IQM_AF_CLP_CLIP__PRE 0x0
+
+#define IQM_AF_CLP_LEN__A 0x1870023
+#define IQM_AF_CLP_LEN__W 16
+#define IQM_AF_CLP_LEN__M 0xFFFF
+#define IQM_AF_CLP_LEN__PRE 0x0
+#define IQM_AF_CLP_LEN_QAM_B_64 0x400
+#define IQM_AF_CLP_LEN_QAM_B_256 0x400
+#define IQM_AF_CLP_LEN_ATV 0x0
+
+#define IQM_AF_CLP_TH__A 0x1870024
+#define IQM_AF_CLP_TH__W 9
+#define IQM_AF_CLP_TH__M 0x1FF
+#define IQM_AF_CLP_TH__PRE 0x0
+#define IQM_AF_CLP_TH_QAM_B_64 0x80
+#define IQM_AF_CLP_TH_QAM_B_256 0x80
+#define IQM_AF_CLP_TH_ATV 0x1C0
+
+#define IQM_AF_DCF_BYPASS__A 0x1870025
+#define IQM_AF_DCF_BYPASS__W 1
+#define IQM_AF_DCF_BYPASS__M 0x1
+#define IQM_AF_DCF_BYPASS__PRE 0x0
+#define IQM_AF_DCF_BYPASS_ACTIVE 0x0
+#define IQM_AF_DCF_BYPASS_BYPASS 0x1
+
+#define IQM_AF_SNS_LEN__A 0x1870026
+#define IQM_AF_SNS_LEN__W 16
+#define IQM_AF_SNS_LEN__M 0xFFFF
+#define IQM_AF_SNS_LEN__PRE 0x0
+#define IQM_AF_SNS_LEN_QAM_B_64 0x400
+#define IQM_AF_SNS_LEN_QAM_B_256 0x400
+#define IQM_AF_SNS_LEN_ATV 0x0
+
+#define IQM_AF_SNS_SENSE__A 0x1870027
+#define IQM_AF_SNS_SENSE__W 16
+#define IQM_AF_SNS_SENSE__M 0xFFFF
+#define IQM_AF_SNS_SENSE__PRE 0x0
+
+#define IQM_AF_AGC_IF__A 0x1870028
+#define IQM_AF_AGC_IF__W 15
+#define IQM_AF_AGC_IF__M 0x7FFF
+#define IQM_AF_AGC_IF__PRE 0x0
+
+#define IQM_AF_AGC_RF__A 0x1870029
+#define IQM_AF_AGC_RF__W 15
+#define IQM_AF_AGC_RF__M 0x7FFF
+#define IQM_AF_AGC_RF__PRE 0x0
+
+#define IQM_AF_PGA_GAIN__A 0x187002A
+#define IQM_AF_PGA_GAIN__W 4
+#define IQM_AF_PGA_GAIN__M 0xF
+#define IQM_AF_PGA_GAIN__PRE 0x0
+
+#define IQM_AF_PDREF__A 0x187002B
+#define IQM_AF_PDREF__W 5
+#define IQM_AF_PDREF__M 0x1F
+#define IQM_AF_PDREF__PRE 0x0
+#define IQM_AF_PDREF_QAM_B_64 0xF
+#define IQM_AF_PDREF_QAM_B_256 0xF
+#define IQM_AF_PDREF_ATV 0xF
+
+#define IQM_AF_STDBY__A 0x187002C
+#define IQM_AF_STDBY__W 6
+#define IQM_AF_STDBY__M 0x3F
+#define IQM_AF_STDBY__PRE 0x0
+
+#define IQM_AF_STDBY_STDBY_BIAS__B 0
+#define IQM_AF_STDBY_STDBY_BIAS__W 1
+#define IQM_AF_STDBY_STDBY_BIAS__M 0x1
+#define IQM_AF_STDBY_STDBY_BIAS__PRE 0x0
+#define IQM_AF_STDBY_STDBY_BIAS_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_BIAS_STANDBY 0x1
+
+#define IQM_AF_STDBY_STDBY_ADC__B 1
+#define IQM_AF_STDBY_STDBY_ADC__W 1
+#define IQM_AF_STDBY_STDBY_ADC__M 0x2
+#define IQM_AF_STDBY_STDBY_ADC__PRE 0x0
+#define IQM_AF_STDBY_STDBY_ADC_A1_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_ADC_A1_STANDBY 0x2
+#define IQM_AF_STDBY_STDBY_ADC_A2_ACTIVE 0x2
+#define IQM_AF_STDBY_STDBY_ADC_A2_STANDBY 0x0
+
+#define IQM_AF_STDBY_STDBY_AMP__B 2
+#define IQM_AF_STDBY_STDBY_AMP__W 1
+#define IQM_AF_STDBY_STDBY_AMP__M 0x4
+#define IQM_AF_STDBY_STDBY_AMP__PRE 0x0
+#define IQM_AF_STDBY_STDBY_AMP_A1_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_AMP_A1_STANDBY 0x4
+#define IQM_AF_STDBY_STDBY_AMP_A2_ACTIVE 0x4
+#define IQM_AF_STDBY_STDBY_AMP_A2_STANDBY 0x0
+
+#define IQM_AF_STDBY_STDBY_PD__B 3
+#define IQM_AF_STDBY_STDBY_PD__W 1
+#define IQM_AF_STDBY_STDBY_PD__M 0x8
+#define IQM_AF_STDBY_STDBY_PD__PRE 0x0
+#define IQM_AF_STDBY_STDBY_PD_A1_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_PD_A1_STANDBY 0x8
+#define IQM_AF_STDBY_STDBY_PD_A2_ACTIVE 0x8
+#define IQM_AF_STDBY_STDBY_PD_A2_STANDBY 0x0
+
+#define IQM_AF_STDBY_STDBY_TAGC_IF__B 4
+#define IQM_AF_STDBY_STDBY_TAGC_IF__W 1
+#define IQM_AF_STDBY_STDBY_TAGC_IF__M 0x10
+#define IQM_AF_STDBY_STDBY_TAGC_IF__PRE 0x0
+#define IQM_AF_STDBY_STDBY_TAGC_IF_A1_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_TAGC_IF_A1_STANDBY 0x10
+#define IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE 0x10
+#define IQM_AF_STDBY_STDBY_TAGC_IF_A2_STANDBY 0x0
+
+#define IQM_AF_STDBY_STDBY_TAGC_RF__B 5
+#define IQM_AF_STDBY_STDBY_TAGC_RF__W 1
+#define IQM_AF_STDBY_STDBY_TAGC_RF__M 0x20
+#define IQM_AF_STDBY_STDBY_TAGC_RF__PRE 0x0
+#define IQM_AF_STDBY_STDBY_TAGC_RF_A1_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_TAGC_RF_A1_STANDBY 0x20
+#define IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE 0x20
+#define IQM_AF_STDBY_STDBY_TAGC_RF_A2_STANDBY 0x0
+
+#define IQM_AF_AMUX__A 0x187002D
+#define IQM_AF_AMUX__W 2
+#define IQM_AF_AMUX__M 0x3
+#define IQM_AF_AMUX__PRE 0x0
+
+#define IQM_AF_TST_AFEMAIN__A 0x187002E
+#define IQM_AF_TST_AFEMAIN__W 8
+#define IQM_AF_TST_AFEMAIN__M 0xFF
+#define IQM_AF_TST_AFEMAIN__PRE 0x0
+
+#define IQM_RT_RAM__A 0x1880000
+
+#define IQM_RT_RAM_DLY__B 0
+#define IQM_RT_RAM_DLY__W 13
+#define IQM_RT_RAM_DLY__M 0x1FFF
+#define IQM_RT_RAM_DLY__PRE 0x0
+
+#define ORX_COMM_EXEC__A 0x2000000
+#define ORX_COMM_EXEC__W 2
+#define ORX_COMM_EXEC__M 0x3
+#define ORX_COMM_EXEC__PRE 0x0
+#define ORX_COMM_EXEC_STOP 0x0
+#define ORX_COMM_EXEC_ACTIVE 0x1
+#define ORX_COMM_EXEC_HOLD 0x2
+
+#define ORX_COMM_STATE__A 0x2000001
+#define ORX_COMM_STATE__W 16
+#define ORX_COMM_STATE__M 0xFFFF
+#define ORX_COMM_STATE__PRE 0x0
+#define ORX_COMM_MB__A 0x2000002
+#define ORX_COMM_MB__W 16
+#define ORX_COMM_MB__M 0xFFFF
+#define ORX_COMM_MB__PRE 0x0
+#define ORX_COMM_INT_REQ__A 0x2000003
+#define ORX_COMM_INT_REQ__W 16
+#define ORX_COMM_INT_REQ__M 0xFFFF
+#define ORX_COMM_INT_REQ__PRE 0x0
+#define ORX_COMM_INT_REQ_EQU_REQ__B 0
+#define ORX_COMM_INT_REQ_EQU_REQ__W 1
+#define ORX_COMM_INT_REQ_EQU_REQ__M 0x1
+#define ORX_COMM_INT_REQ_EQU_REQ__PRE 0x0
+#define ORX_COMM_INT_REQ_DDC_REQ__B 1
+#define ORX_COMM_INT_REQ_DDC_REQ__W 1
+#define ORX_COMM_INT_REQ_DDC_REQ__M 0x2
+#define ORX_COMM_INT_REQ_DDC_REQ__PRE 0x0
+#define ORX_COMM_INT_REQ_FWP_REQ__B 2
+#define ORX_COMM_INT_REQ_FWP_REQ__W 1
+#define ORX_COMM_INT_REQ_FWP_REQ__M 0x4
+#define ORX_COMM_INT_REQ_FWP_REQ__PRE 0x0
+#define ORX_COMM_INT_REQ_CON_REQ__B 3
+#define ORX_COMM_INT_REQ_CON_REQ__W 1
+#define ORX_COMM_INT_REQ_CON_REQ__M 0x8
+#define ORX_COMM_INT_REQ_CON_REQ__PRE 0x0
+#define ORX_COMM_INT_REQ_NSU_REQ__B 4
+#define ORX_COMM_INT_REQ_NSU_REQ__W 1
+#define ORX_COMM_INT_REQ_NSU_REQ__M 0x10
+#define ORX_COMM_INT_REQ_NSU_REQ__PRE 0x0
+
+#define ORX_COMM_INT_STA__A 0x2000005
+#define ORX_COMM_INT_STA__W 16
+#define ORX_COMM_INT_STA__M 0xFFFF
+#define ORX_COMM_INT_STA__PRE 0x0
+#define ORX_COMM_INT_MSK__A 0x2000006
+#define ORX_COMM_INT_MSK__W 16
+#define ORX_COMM_INT_MSK__M 0xFFFF
+#define ORX_COMM_INT_MSK__PRE 0x0
+#define ORX_COMM_INT_STM__A 0x2000007
+#define ORX_COMM_INT_STM__W 16
+#define ORX_COMM_INT_STM__M 0xFFFF
+#define ORX_COMM_INT_STM__PRE 0x0
+
+#define ORX_TOP_COMM_EXEC__A 0x2010000
+#define ORX_TOP_COMM_EXEC__W 2
+#define ORX_TOP_COMM_EXEC__M 0x3
+#define ORX_TOP_COMM_EXEC__PRE 0x0
+#define ORX_TOP_COMM_EXEC_STOP 0x0
+#define ORX_TOP_COMM_EXEC_ACTIVE 0x1
+#define ORX_TOP_COMM_EXEC_HOLD 0x2
+
+#define ORX_TOP_COMM_KEY__A 0x201000F
+#define ORX_TOP_COMM_KEY__W 16
+#define ORX_TOP_COMM_KEY__M 0xFFFF
+#define ORX_TOP_COMM_KEY__PRE 0x0
+#define ORX_TOP_COMM_KEY_KEY 0xFABA
+
+#define ORX_TOP_MDE_W__A 0x2010010
+#define ORX_TOP_MDE_W__W 2
+#define ORX_TOP_MDE_W__M 0x3
+#define ORX_TOP_MDE_W__PRE 0x2
+#define ORX_TOP_MDE_W_RATE_1544KBPS 0x0
+#define ORX_TOP_MDE_W_RATE_3088KBPS 0x1
+#define ORX_TOP_MDE_W_RATE_2048KBPS_SQRT 0x2
+#define ORX_TOP_MDE_W_RATE_2048KBPS_RO 0x3
+
+#define ORX_TOP_AIF_CTRL_W__A 0x2010011
+#define ORX_TOP_AIF_CTRL_W__W 3
+#define ORX_TOP_AIF_CTRL_W__M 0x7
+#define ORX_TOP_AIF_CTRL_W__PRE 0x0
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__B 0
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__W 1
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__M 0x1
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__PRE 0x0
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE_ADC_SAMPL_ON_POS_CLK_EDGE 0x0
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE_ADC_SAMPL_ON_NEG_CLK_EDGE 0x1
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__B 1
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__W 1
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__M 0x2
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__PRE 0x0
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE_REGULAR_BIT_ORDER_ADC 0x0
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE_REVERSAL_BIT_ORDER_ADC 0x2
+#define ORX_TOP_AIF_CTRL_W_INV_MSB__B 2
+#define ORX_TOP_AIF_CTRL_W_INV_MSB__W 1
+#define ORX_TOP_AIF_CTRL_W_INV_MSB__M 0x4
+#define ORX_TOP_AIF_CTRL_W_INV_MSB__PRE 0x0
+#define ORX_TOP_AIF_CTRL_W_INV_MSB_NO_MSB_INVERSION_ADC 0x0
+#define ORX_TOP_AIF_CTRL_W_INV_MSB_MSB_INVERSION_ADC 0x4
+
+#define ORX_FWP_COMM_EXEC__A 0x2020000
+#define ORX_FWP_COMM_EXEC__W 2
+#define ORX_FWP_COMM_EXEC__M 0x3
+#define ORX_FWP_COMM_EXEC__PRE 0x0
+#define ORX_FWP_COMM_EXEC_STOP 0x0
+#define ORX_FWP_COMM_EXEC_ACTIVE 0x1
+#define ORX_FWP_COMM_EXEC_HOLD 0x2
+
+#define ORX_FWP_COMM_MB__A 0x2020002
+#define ORX_FWP_COMM_MB__W 8
+#define ORX_FWP_COMM_MB__M 0xFF
+#define ORX_FWP_COMM_MB__PRE 0x0
+#define ORX_FWP_COMM_MB_CTL__B 0
+#define ORX_FWP_COMM_MB_CTL__W 1
+#define ORX_FWP_COMM_MB_CTL__M 0x1
+#define ORX_FWP_COMM_MB_CTL__PRE 0x0
+#define ORX_FWP_COMM_MB_CTL_OFF 0x0
+#define ORX_FWP_COMM_MB_CTL_ON 0x1
+#define ORX_FWP_COMM_MB_OBS__B 1
+#define ORX_FWP_COMM_MB_OBS__W 1
+#define ORX_FWP_COMM_MB_OBS__M 0x2
+#define ORX_FWP_COMM_MB_OBS__PRE 0x0
+#define ORX_FWP_COMM_MB_OBS_OFF 0x0
+#define ORX_FWP_COMM_MB_OBS_ON 0x2
+
+#define ORX_FWP_COMM_MB_CTL_MUX__B 2
+#define ORX_FWP_COMM_MB_CTL_MUX__W 3
+#define ORX_FWP_COMM_MB_CTL_MUX__M 0x1C
+#define ORX_FWP_COMM_MB_CTL_MUX__PRE 0x0
+
+#define ORX_FWP_COMM_MB_OBS_MUX__B 5
+#define ORX_FWP_COMM_MB_OBS_MUX__W 3
+#define ORX_FWP_COMM_MB_OBS_MUX__M 0xE0
+#define ORX_FWP_COMM_MB_OBS_MUX__PRE 0x0
+
+#define ORX_FWP_AAG_LEN_W__A 0x2020010
+#define ORX_FWP_AAG_LEN_W__W 16
+#define ORX_FWP_AAG_LEN_W__M 0xFFFF
+#define ORX_FWP_AAG_LEN_W__PRE 0x800
+
+#define ORX_FWP_AAG_THR_W__A 0x2020011
+#define ORX_FWP_AAG_THR_W__W 8
+#define ORX_FWP_AAG_THR_W__M 0xFF
+#define ORX_FWP_AAG_THR_W__PRE 0x50
+
+#define ORX_FWP_AAG_THR_CNT_R__A 0x2020012
+#define ORX_FWP_AAG_THR_CNT_R__W 16
+#define ORX_FWP_AAG_THR_CNT_R__M 0xFFFF
+#define ORX_FWP_AAG_THR_CNT_R__PRE 0x0
+
+#define ORX_FWP_AAG_SNS_CNT_R__A 0x2020013
+#define ORX_FWP_AAG_SNS_CNT_R__W 16
+#define ORX_FWP_AAG_SNS_CNT_R__M 0xFFFF
+#define ORX_FWP_AAG_SNS_CNT_R__PRE 0x0
+
+#define ORX_FWP_PFI_A_W__A 0x2020014
+#define ORX_FWP_PFI_A_W__W 8
+#define ORX_FWP_PFI_A_W__M 0xFF
+#define ORX_FWP_PFI_A_W__PRE 0xB0
+#define ORX_FWP_PFI_A_W_RATE_2048KBPS 0xB0
+#define ORX_FWP_PFI_A_W_RATE_1544KBPS 0xA4
+#define ORX_FWP_PFI_A_W_RATE_3088KBPS 0xC0
+
+#define ORX_FWP_PFI_B_W__A 0x2020015
+#define ORX_FWP_PFI_B_W__W 8
+#define ORX_FWP_PFI_B_W__M 0xFF
+#define ORX_FWP_PFI_B_W__PRE 0x9E
+#define ORX_FWP_PFI_B_W_RATE_2048KBPS 0x9E
+#define ORX_FWP_PFI_B_W_RATE_1544KBPS 0x94
+#define ORX_FWP_PFI_B_W_RATE_3088KBPS 0xB0
+
+#define ORX_FWP_PFI_C_W__A 0x2020016
+#define ORX_FWP_PFI_C_W__W 8
+#define ORX_FWP_PFI_C_W__M 0xFF
+#define ORX_FWP_PFI_C_W__PRE 0x5C
+#define ORX_FWP_PFI_C_W_RATE_2048KBPS 0x5C
+#define ORX_FWP_PFI_C_W_RATE_1544KBPS 0x64
+#define ORX_FWP_PFI_C_W_RATE_3088KBPS 0x50
+
+#define ORX_FWP_KR1_AMP_R__A 0x2020017
+#define ORX_FWP_KR1_AMP_R__W 9
+#define ORX_FWP_KR1_AMP_R__M 0x1FF
+#define ORX_FWP_KR1_AMP_R__PRE 0x0
+
+#define ORX_FWP_KR1_LDT_W__A 0x2020018
+#define ORX_FWP_KR1_LDT_W__W 3
+#define ORX_FWP_KR1_LDT_W__M 0x7
+#define ORX_FWP_KR1_LDT_W__PRE 0x2
+#define ORX_FWP_SRC_DGN_W__A 0x2020019
+#define ORX_FWP_SRC_DGN_W__W 16
+#define ORX_FWP_SRC_DGN_W__M 0xFFFF
+#define ORX_FWP_SRC_DGN_W__PRE 0x1FF
+
+#define ORX_FWP_SRC_DGN_W_MANT__B 0
+#define ORX_FWP_SRC_DGN_W_MANT__W 9
+#define ORX_FWP_SRC_DGN_W_MANT__M 0x1FF
+#define ORX_FWP_SRC_DGN_W_MANT__PRE 0x1FF
+
+#define ORX_FWP_SRC_DGN_W_EXP__B 12
+#define ORX_FWP_SRC_DGN_W_EXP__W 4
+#define ORX_FWP_SRC_DGN_W_EXP__M 0xF000
+#define ORX_FWP_SRC_DGN_W_EXP__PRE 0x0
+
+#define ORX_FWP_NYQ_ADR_W__A 0x202001A
+#define ORX_FWP_NYQ_ADR_W__W 5
+#define ORX_FWP_NYQ_ADR_W__M 0x1F
+#define ORX_FWP_NYQ_ADR_W__PRE 0x1F
+
+#define ORX_FWP_NYQ_COF_RW__A 0x202001B
+#define ORX_FWP_NYQ_COF_RW__W 10
+#define ORX_FWP_NYQ_COF_RW__M 0x3FF
+#define ORX_FWP_NYQ_COF_RW__PRE 0x0
+
+#define ORX_FWP_IQM_FRQ_W__A 0x202001C
+#define ORX_FWP_IQM_FRQ_W__W 16
+#define ORX_FWP_IQM_FRQ_W__M 0xFFFF
+#define ORX_FWP_IQM_FRQ_W__PRE 0x4301
+
+#define ORX_EQU_COMM_EXEC__A 0x2030000
+#define ORX_EQU_COMM_EXEC__W 2
+#define ORX_EQU_COMM_EXEC__M 0x3
+#define ORX_EQU_COMM_EXEC__PRE 0x0
+#define ORX_EQU_COMM_EXEC_STOP 0x0
+#define ORX_EQU_COMM_EXEC_ACTIVE 0x1
+#define ORX_EQU_COMM_EXEC_HOLD 0x2
+
+#define ORX_EQU_COMM_MB__A 0x2030002
+#define ORX_EQU_COMM_MB__W 8
+#define ORX_EQU_COMM_MB__M 0xFF
+#define ORX_EQU_COMM_MB__PRE 0x0
+#define ORX_EQU_COMM_MB_CTL__B 0
+#define ORX_EQU_COMM_MB_CTL__W 1
+#define ORX_EQU_COMM_MB_CTL__M 0x1
+#define ORX_EQU_COMM_MB_CTL__PRE 0x0
+#define ORX_EQU_COMM_MB_CTL_OFF 0x0
+#define ORX_EQU_COMM_MB_CTL_ON 0x1
+#define ORX_EQU_COMM_MB_OBS__B 1
+#define ORX_EQU_COMM_MB_OBS__W 1
+#define ORX_EQU_COMM_MB_OBS__M 0x2
+#define ORX_EQU_COMM_MB_OBS__PRE 0x0
+#define ORX_EQU_COMM_MB_OBS_OFF 0x0
+#define ORX_EQU_COMM_MB_OBS_ON 0x2
+
+#define ORX_EQU_COMM_MB_CTL_MUX__B 2
+#define ORX_EQU_COMM_MB_CTL_MUX__W 3
+#define ORX_EQU_COMM_MB_CTL_MUX__M 0x1C
+#define ORX_EQU_COMM_MB_CTL_MUX__PRE 0x0
+
+#define ORX_EQU_COMM_MB_OBS_MUX__B 5
+#define ORX_EQU_COMM_MB_OBS_MUX__W 3
+#define ORX_EQU_COMM_MB_OBS_MUX__M 0xE0
+#define ORX_EQU_COMM_MB_OBS_MUX__PRE 0x0
+
+#define ORX_EQU_COMM_INT_REQ__A 0x2030003
+#define ORX_EQU_COMM_INT_REQ__W 1
+#define ORX_EQU_COMM_INT_REQ__M 0x1
+#define ORX_EQU_COMM_INT_REQ__PRE 0x0
+#define ORX_EQU_COMM_INT_STA__A 0x2030005
+#define ORX_EQU_COMM_INT_STA__W 2
+#define ORX_EQU_COMM_INT_STA__M 0x3
+#define ORX_EQU_COMM_INT_STA__PRE 0x0
+
+#define ORX_EQU_COMM_INT_STA_FFF_READ__B 0
+#define ORX_EQU_COMM_INT_STA_FFF_READ__W 1
+#define ORX_EQU_COMM_INT_STA_FFF_READ__M 0x1
+#define ORX_EQU_COMM_INT_STA_FFF_READ__PRE 0x0
+
+#define ORX_EQU_COMM_INT_STA_FBF_READ__B 1
+#define ORX_EQU_COMM_INT_STA_FBF_READ__W 1
+#define ORX_EQU_COMM_INT_STA_FBF_READ__M 0x2
+#define ORX_EQU_COMM_INT_STA_FBF_READ__PRE 0x0
+
+#define ORX_EQU_COMM_INT_MSK__A 0x2030006
+#define ORX_EQU_COMM_INT_MSK__W 2
+#define ORX_EQU_COMM_INT_MSK__M 0x3
+#define ORX_EQU_COMM_INT_MSK__PRE 0x0
+#define ORX_EQU_COMM_INT_MSK_FFF_READ__B 0
+#define ORX_EQU_COMM_INT_MSK_FFF_READ__W 1
+#define ORX_EQU_COMM_INT_MSK_FFF_READ__M 0x1
+#define ORX_EQU_COMM_INT_MSK_FFF_READ__PRE 0x0
+#define ORX_EQU_COMM_INT_MSK_FBF_READ__B 1
+#define ORX_EQU_COMM_INT_MSK_FBF_READ__W 1
+#define ORX_EQU_COMM_INT_MSK_FBF_READ__M 0x2
+#define ORX_EQU_COMM_INT_MSK_FBF_READ__PRE 0x0
+
+#define ORX_EQU_COMM_INT_STM__A 0x2030007
+#define ORX_EQU_COMM_INT_STM__W 2
+#define ORX_EQU_COMM_INT_STM__M 0x3
+#define ORX_EQU_COMM_INT_STM__PRE 0x0
+#define ORX_EQU_COMM_INT_STM_FFF_READ__B 0
+#define ORX_EQU_COMM_INT_STM_FFF_READ__W 1
+#define ORX_EQU_COMM_INT_STM_FFF_READ__M 0x1
+#define ORX_EQU_COMM_INT_STM_FFF_READ__PRE 0x0
+#define ORX_EQU_COMM_INT_STM_FBF_READ__B 1
+#define ORX_EQU_COMM_INT_STM_FBF_READ__W 1
+#define ORX_EQU_COMM_INT_STM_FBF_READ__M 0x2
+#define ORX_EQU_COMM_INT_STM_FBF_READ__PRE 0x0
+
+#define ORX_EQU_FFF_SCL_W__A 0x2030010
+#define ORX_EQU_FFF_SCL_W__W 1
+#define ORX_EQU_FFF_SCL_W__M 0x1
+#define ORX_EQU_FFF_SCL_W__PRE 0x0
+#define ORX_EQU_FFF_SCL_W_SCALE_GAIN_1 0x0
+#define ORX_EQU_FFF_SCL_W_SCALE_GAIN_2 0x1
+
+#define ORX_EQU_FFF_UPD_W__A 0x2030011
+#define ORX_EQU_FFF_UPD_W__W 1
+#define ORX_EQU_FFF_UPD_W__M 0x1
+#define ORX_EQU_FFF_UPD_W__PRE 0x0
+#define ORX_EQU_FFF_UPD_W_NO_UPDATE 0x0
+#define ORX_EQU_FFF_UPD_W_LMS_UPDATE 0x1
+
+#define ORX_EQU_FFF_STP_W__A 0x2030012
+#define ORX_EQU_FFF_STP_W__W 3
+#define ORX_EQU_FFF_STP_W__M 0x7
+#define ORX_EQU_FFF_STP_W__PRE 0x2
+
+#define ORX_EQU_FFF_LEA_W__A 0x2030013
+#define ORX_EQU_FFF_LEA_W__W 4
+#define ORX_EQU_FFF_LEA_W__M 0xF
+#define ORX_EQU_FFF_LEA_W__PRE 0x4
+
+#define ORX_EQU_FFF_RWT_W__A 0x2030014
+#define ORX_EQU_FFF_RWT_W__W 2
+#define ORX_EQU_FFF_RWT_W__M 0x3
+#define ORX_EQU_FFF_RWT_W__PRE 0x0
+
+#define ORX_EQU_FFF_C0RE_RW__A 0x2030015
+#define ORX_EQU_FFF_C0RE_RW__W 12
+#define ORX_EQU_FFF_C0RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C0RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C0IM_RW__A 0x2030016
+#define ORX_EQU_FFF_C0IM_RW__W 12
+#define ORX_EQU_FFF_C0IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C0IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C1RE_RW__A 0x2030017
+#define ORX_EQU_FFF_C1RE_RW__W 12
+#define ORX_EQU_FFF_C1RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C1RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C1IM_RW__A 0x2030018
+#define ORX_EQU_FFF_C1IM_RW__W 12
+#define ORX_EQU_FFF_C1IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C1IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C2RE_RW__A 0x2030019
+#define ORX_EQU_FFF_C2RE_RW__W 12
+#define ORX_EQU_FFF_C2RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C2RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C2IM_RW__A 0x203001A
+#define ORX_EQU_FFF_C2IM_RW__W 12
+#define ORX_EQU_FFF_C2IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C2IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C3RE_RW__A 0x203001B
+#define ORX_EQU_FFF_C3RE_RW__W 12
+#define ORX_EQU_FFF_C3RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C3RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C3IM_RW__A 0x203001C
+#define ORX_EQU_FFF_C3IM_RW__W 12
+#define ORX_EQU_FFF_C3IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C3IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C4RE_RW__A 0x203001D
+#define ORX_EQU_FFF_C4RE_RW__W 12
+#define ORX_EQU_FFF_C4RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C4RE_RW__PRE 0x400
+
+#define ORX_EQU_FFF_C4IM_RW__A 0x203001E
+#define ORX_EQU_FFF_C4IM_RW__W 12
+#define ORX_EQU_FFF_C4IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C4IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C5RE_RW__A 0x203001F
+#define ORX_EQU_FFF_C5RE_RW__W 12
+#define ORX_EQU_FFF_C5RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C5RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C5IM_RW__A 0x2030020
+#define ORX_EQU_FFF_C5IM_RW__W 12
+#define ORX_EQU_FFF_C5IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C5IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C6RE_RW__A 0x2030021
+#define ORX_EQU_FFF_C6RE_RW__W 12
+#define ORX_EQU_FFF_C6RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C6RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C6IM_RW__A 0x2030022
+#define ORX_EQU_FFF_C6IM_RW__W 12
+#define ORX_EQU_FFF_C6IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C6IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C7RE_RW__A 0x2030023
+#define ORX_EQU_FFF_C7RE_RW__W 12
+#define ORX_EQU_FFF_C7RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C7RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C7IM_RW__A 0x2030024
+#define ORX_EQU_FFF_C7IM_RW__W 12
+#define ORX_EQU_FFF_C7IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C7IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C8RE_RW__A 0x2030025
+#define ORX_EQU_FFF_C8RE_RW__W 12
+#define ORX_EQU_FFF_C8RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C8RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C8IM_RW__A 0x2030026
+#define ORX_EQU_FFF_C8IM_RW__W 12
+#define ORX_EQU_FFF_C8IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C8IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C9RE_RW__A 0x2030027
+#define ORX_EQU_FFF_C9RE_RW__W 12
+#define ORX_EQU_FFF_C9RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C9RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C9IM_RW__A 0x2030028
+#define ORX_EQU_FFF_C9IM_RW__W 12
+#define ORX_EQU_FFF_C9IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C9IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C10RE_RW__A 0x2030029
+#define ORX_EQU_FFF_C10RE_RW__W 12
+#define ORX_EQU_FFF_C10RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C10RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C10IM_RW__A 0x203002A
+#define ORX_EQU_FFF_C10IM_RW__W 12
+#define ORX_EQU_FFF_C10IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C10IM_RW__PRE 0x0
+
+#define ORX_EQU_MXB_SEL_W__A 0x203002B
+#define ORX_EQU_MXB_SEL_W__W 1
+#define ORX_EQU_MXB_SEL_W__M 0x1
+#define ORX_EQU_MXB_SEL_W__PRE 0x0
+#define ORX_EQU_MXB_SEL_W_UNDECIDED_SYMBOLS 0x0
+#define ORX_EQU_MXB_SEL_W_DECIDED_SYMBOLS 0x1
+
+#define ORX_EQU_FBF_UPD_W__A 0x203002C
+#define ORX_EQU_FBF_UPD_W__W 1
+#define ORX_EQU_FBF_UPD_W__M 0x1
+#define ORX_EQU_FBF_UPD_W__PRE 0x0
+#define ORX_EQU_FBF_UPD_W_NO_UPDATE 0x0
+#define ORX_EQU_FBF_UPD_W_LMS_UPDATE 0x1
+
+#define ORX_EQU_FBF_STP_W__A 0x203002D
+#define ORX_EQU_FBF_STP_W__W 3
+#define ORX_EQU_FBF_STP_W__M 0x7
+#define ORX_EQU_FBF_STP_W__PRE 0x2
+
+#define ORX_EQU_FBF_LEA_W__A 0x203002E
+#define ORX_EQU_FBF_LEA_W__W 4
+#define ORX_EQU_FBF_LEA_W__M 0xF
+#define ORX_EQU_FBF_LEA_W__PRE 0x4
+
+#define ORX_EQU_FBF_RWT_W__A 0x203002F
+#define ORX_EQU_FBF_RWT_W__W 2
+#define ORX_EQU_FBF_RWT_W__M 0x3
+#define ORX_EQU_FBF_RWT_W__PRE 0x0
+
+#define ORX_EQU_FBF_C0RE_RW__A 0x2030030
+#define ORX_EQU_FBF_C0RE_RW__W 12
+#define ORX_EQU_FBF_C0RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C0RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C0IM_RW__A 0x2030031
+#define ORX_EQU_FBF_C0IM_RW__W 12
+#define ORX_EQU_FBF_C0IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C0IM_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C1RE_RW__A 0x2030032
+#define ORX_EQU_FBF_C1RE_RW__W 12
+#define ORX_EQU_FBF_C1RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C1RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C1IM_RW__A 0x2030033
+#define ORX_EQU_FBF_C1IM_RW__W 12
+#define ORX_EQU_FBF_C1IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C1IM_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C2RE_RW__A 0x2030034
+#define ORX_EQU_FBF_C2RE_RW__W 12
+#define ORX_EQU_FBF_C2RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C2RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C2IM_RW__A 0x2030035
+#define ORX_EQU_FBF_C2IM_RW__W 12
+#define ORX_EQU_FBF_C2IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C2IM_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C3RE_RW__A 0x2030036
+#define ORX_EQU_FBF_C3RE_RW__W 12
+#define ORX_EQU_FBF_C3RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C3RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C3IM_RW__A 0x2030037
+#define ORX_EQU_FBF_C3IM_RW__W 12
+#define ORX_EQU_FBF_C3IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C3IM_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C4RE_RW__A 0x2030038
+#define ORX_EQU_FBF_C4RE_RW__W 12
+#define ORX_EQU_FBF_C4RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C4RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C4IM_RW__A 0x2030039
+#define ORX_EQU_FBF_C4IM_RW__W 12
+#define ORX_EQU_FBF_C4IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C4IM_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C5RE_RW__A 0x203003A
+#define ORX_EQU_FBF_C5RE_RW__W 12
+#define ORX_EQU_FBF_C5RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C5RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C5IM_RW__A 0x203003B
+#define ORX_EQU_FBF_C5IM_RW__W 12
+#define ORX_EQU_FBF_C5IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C5IM_RW__PRE 0x0
+
+#define ORX_EQU_ERR_SEL_W__A 0x203003C
+#define ORX_EQU_ERR_SEL_W__W 1
+#define ORX_EQU_ERR_SEL_W__M 0x1
+#define ORX_EQU_ERR_SEL_W__PRE 0x0
+#define ORX_EQU_ERR_SEL_W_CMA_ERROR 0x0
+#define ORX_EQU_ERR_SEL_W_DDA_ERROR 0x1
+
+#define ORX_EQU_ERR_TIS_W__A 0x203003D
+#define ORX_EQU_ERR_TIS_W__W 1
+#define ORX_EQU_ERR_TIS_W__M 0x1
+#define ORX_EQU_ERR_TIS_W__PRE 0x0
+#define ORX_EQU_ERR_TIS_W_CMA_SIGNALS 0x0
+#define ORX_EQU_ERR_TIS_W_DDA_SIGNALS 0x1
+
+#define ORX_EQU_ERR_EDI_R__A 0x203003E
+#define ORX_EQU_ERR_EDI_R__W 5
+#define ORX_EQU_ERR_EDI_R__M 0x1F
+#define ORX_EQU_ERR_EDI_R__PRE 0xF
+
+#define ORX_EQU_ERR_EDQ_R__A 0x203003F
+#define ORX_EQU_ERR_EDQ_R__W 5
+#define ORX_EQU_ERR_EDQ_R__M 0x1F
+#define ORX_EQU_ERR_EDQ_R__PRE 0xF
+
+#define ORX_EQU_ERR_ECI_R__A 0x2030040
+#define ORX_EQU_ERR_ECI_R__W 5
+#define ORX_EQU_ERR_ECI_R__M 0x1F
+#define ORX_EQU_ERR_ECI_R__PRE 0xF
+
+#define ORX_EQU_ERR_ECQ_R__A 0x2030041
+#define ORX_EQU_ERR_ECQ_R__W 5
+#define ORX_EQU_ERR_ECQ_R__M 0x1F
+#define ORX_EQU_ERR_ECQ_R__PRE 0xF
+
+#define ORX_EQU_MER_MER_R__A 0x2030042
+#define ORX_EQU_MER_MER_R__W 6
+#define ORX_EQU_MER_MER_R__M 0x3F
+#define ORX_EQU_MER_MER_R__PRE 0x3F
+
+#define ORX_EQU_MER_LDT_W__A 0x2030043
+#define ORX_EQU_MER_LDT_W__W 3
+#define ORX_EQU_MER_LDT_W__M 0x7
+#define ORX_EQU_MER_LDT_W__PRE 0x4
+
+#define ORX_EQU_SYN_LEN_W__A 0x2030044
+#define ORX_EQU_SYN_LEN_W__W 16
+#define ORX_EQU_SYN_LEN_W__M 0xFFFF
+#define ORX_EQU_SYN_LEN_W__PRE 0x0
+
+#define ORX_DDC_COMM_EXEC__A 0x2040000
+#define ORX_DDC_COMM_EXEC__W 2
+#define ORX_DDC_COMM_EXEC__M 0x3
+#define ORX_DDC_COMM_EXEC__PRE 0x0
+#define ORX_DDC_COMM_EXEC_STOP 0x0
+#define ORX_DDC_COMM_EXEC_ACTIVE 0x1
+#define ORX_DDC_COMM_EXEC_HOLD 0x2
+
+#define ORX_DDC_COMM_MB__A 0x2040002
+#define ORX_DDC_COMM_MB__W 6
+#define ORX_DDC_COMM_MB__M 0x3F
+#define ORX_DDC_COMM_MB__PRE 0x0
+#define ORX_DDC_COMM_MB_CTL__B 0
+#define ORX_DDC_COMM_MB_CTL__W 1
+#define ORX_DDC_COMM_MB_CTL__M 0x1
+#define ORX_DDC_COMM_MB_CTL__PRE 0x0
+#define ORX_DDC_COMM_MB_CTL_OFF 0x0
+#define ORX_DDC_COMM_MB_CTL_ON 0x1
+#define ORX_DDC_COMM_MB_OBS__B 1
+#define ORX_DDC_COMM_MB_OBS__W 1
+#define ORX_DDC_COMM_MB_OBS__M 0x2
+#define ORX_DDC_COMM_MB_OBS__PRE 0x0
+#define ORX_DDC_COMM_MB_OBS_OFF 0x0
+#define ORX_DDC_COMM_MB_OBS_ON 0x2
+
+#define ORX_DDC_COMM_MB_CTL_MUX__B 2
+#define ORX_DDC_COMM_MB_CTL_MUX__W 2
+#define ORX_DDC_COMM_MB_CTL_MUX__M 0xC
+#define ORX_DDC_COMM_MB_CTL_MUX__PRE 0x0
+
+#define ORX_DDC_COMM_MB_OBS_MUX__B 4
+#define ORX_DDC_COMM_MB_OBS_MUX__W 2
+#define ORX_DDC_COMM_MB_OBS_MUX__M 0x30
+#define ORX_DDC_COMM_MB_OBS_MUX__PRE 0x0
+
+#define ORX_DDC_COMM_INT_REQ__A 0x2040003
+#define ORX_DDC_COMM_INT_REQ__W 1
+#define ORX_DDC_COMM_INT_REQ__M 0x1
+#define ORX_DDC_COMM_INT_REQ__PRE 0x0
+#define ORX_DDC_COMM_INT_STA__A 0x2040005
+#define ORX_DDC_COMM_INT_STA__W 1
+#define ORX_DDC_COMM_INT_STA__M 0x1
+#define ORX_DDC_COMM_INT_STA__PRE 0x0
+#define ORX_DDC_COMM_INT_MSK__A 0x2040006
+#define ORX_DDC_COMM_INT_MSK__W 1
+#define ORX_DDC_COMM_INT_MSK__M 0x1
+#define ORX_DDC_COMM_INT_MSK__PRE 0x0
+#define ORX_DDC_COMM_INT_STM__A 0x2040007
+#define ORX_DDC_COMM_INT_STM__W 1
+#define ORX_DDC_COMM_INT_STM__M 0x1
+#define ORX_DDC_COMM_INT_STM__PRE 0x0
+#define ORX_DDC_DEC_MAP_W__A 0x2040010
+#define ORX_DDC_DEC_MAP_W__W 9
+#define ORX_DDC_DEC_MAP_W__M 0x1FF
+#define ORX_DDC_DEC_MAP_W__PRE 0x178
+
+#define ORX_DDC_DEC_MAP_W_QUADR0__B 0
+#define ORX_DDC_DEC_MAP_W_QUADR0__W 2
+#define ORX_DDC_DEC_MAP_W_QUADR0__M 0x3
+#define ORX_DDC_DEC_MAP_W_QUADR0__PRE 0x0
+#define ORX_DDC_DEC_MAP_W_QUADR0_ROTATE_DEFAULT 0x0
+#define ORX_DDC_DEC_MAP_W_QUADR0_ROTATE_ALTERNATE 0x0
+
+#define ORX_DDC_DEC_MAP_W_QUADR1__B 2
+#define ORX_DDC_DEC_MAP_W_QUADR1__W 2
+#define ORX_DDC_DEC_MAP_W_QUADR1__M 0xC
+#define ORX_DDC_DEC_MAP_W_QUADR1__PRE 0x8
+#define ORX_DDC_DEC_MAP_W_QUADR1_ROTATE_DEFAULT 0x8
+#define ORX_DDC_DEC_MAP_W_QUADR1_ROTATE_ALTERNATE 0x4
+
+#define ORX_DDC_DEC_MAP_W_QUADR2__B 4
+#define ORX_DDC_DEC_MAP_W_QUADR2__W 2
+#define ORX_DDC_DEC_MAP_W_QUADR2__M 0x30
+#define ORX_DDC_DEC_MAP_W_QUADR2__PRE 0x30
+#define ORX_DDC_DEC_MAP_W_QUADR2_ROTATE_DEFAULT 0x30
+#define ORX_DDC_DEC_MAP_W_QUADR2_ROTATE_ALTERNATE 0x30
+
+#define ORX_DDC_DEC_MAP_W_QUADR3__B 6
+#define ORX_DDC_DEC_MAP_W_QUADR3__W 2
+#define ORX_DDC_DEC_MAP_W_QUADR3__M 0xC0
+#define ORX_DDC_DEC_MAP_W_QUADR3__PRE 0x40
+#define ORX_DDC_DEC_MAP_W_QUADR3_ROTATE_DEFAULT 0x40
+#define ORX_DDC_DEC_MAP_W_QUADR3_ROTATE_ALTERNATE 0x80
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__B 8
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__W 1
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__M 0x100
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__PRE 0x100
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD_COHERENT_DECODING 0x0
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD_DIFF_DECODING 0x100
+
+#define ORX_DDC_OFO_SET_W__A 0x2040011
+#define ORX_DDC_OFO_SET_W__W 16
+#define ORX_DDC_OFO_SET_W__M 0xFFFF
+#define ORX_DDC_OFO_SET_W__PRE 0x1402
+
+#define ORX_DDC_OFO_SET_W_PHASE__B 0
+#define ORX_DDC_OFO_SET_W_PHASE__W 7
+#define ORX_DDC_OFO_SET_W_PHASE__M 0x7F
+#define ORX_DDC_OFO_SET_W_PHASE__PRE 0x2
+
+#define ORX_DDC_OFO_SET_W_CRXHITIME__B 7
+#define ORX_DDC_OFO_SET_W_CRXHITIME__W 7
+#define ORX_DDC_OFO_SET_W_CRXHITIME__M 0x3F80
+#define ORX_DDC_OFO_SET_W_CRXHITIME__PRE 0x1400
+
+#define ORX_DDC_OFO_SET_W_CRXINV__B 14
+#define ORX_DDC_OFO_SET_W_CRXINV__W 1
+#define ORX_DDC_OFO_SET_W_CRXINV__M 0x4000
+#define ORX_DDC_OFO_SET_W_CRXINV__PRE 0x0
+
+#define ORX_DDC_OFO_SET_W_DISABLE__B 15
+#define ORX_DDC_OFO_SET_W_DISABLE__W 1
+#define ORX_DDC_OFO_SET_W_DISABLE__M 0x8000
+#define ORX_DDC_OFO_SET_W_DISABLE__PRE 0x0
+
+#define ORX_CON_COMM_EXEC__A 0x2050000
+#define ORX_CON_COMM_EXEC__W 2
+#define ORX_CON_COMM_EXEC__M 0x3
+#define ORX_CON_COMM_EXEC__PRE 0x0
+#define ORX_CON_COMM_EXEC_STOP 0x0
+#define ORX_CON_COMM_EXEC_ACTIVE 0x1
+#define ORX_CON_COMM_EXEC_HOLD 0x2
+
+#define ORX_CON_LDT_W__A 0x2050010
+#define ORX_CON_LDT_W__W 3
+#define ORX_CON_LDT_W__M 0x7
+#define ORX_CON_LDT_W__PRE 0x3
+
+#define ORX_CON_LDT_W_CON_LDT_W__B 0
+#define ORX_CON_LDT_W_CON_LDT_W__W 3
+#define ORX_CON_LDT_W_CON_LDT_W__M 0x7
+#define ORX_CON_LDT_W_CON_LDT_W__PRE 0x3
+
+#define ORX_CON_RST_W__A 0x2050011
+#define ORX_CON_RST_W__W 4
+#define ORX_CON_RST_W__M 0xF
+#define ORX_CON_RST_W__PRE 0x0
+
+#define ORX_CON_RST_W_CPH__B 0
+#define ORX_CON_RST_W_CPH__W 1
+#define ORX_CON_RST_W_CPH__M 0x1
+#define ORX_CON_RST_W_CPH__PRE 0x0
+
+#define ORX_CON_RST_W_CTI__B 1
+#define ORX_CON_RST_W_CTI__W 1
+#define ORX_CON_RST_W_CTI__M 0x2
+#define ORX_CON_RST_W_CTI__PRE 0x0
+
+#define ORX_CON_RST_W_KRN__B 2
+#define ORX_CON_RST_W_KRN__W 1
+#define ORX_CON_RST_W_KRN__M 0x4
+#define ORX_CON_RST_W_KRN__PRE 0x0
+
+#define ORX_CON_RST_W_KRP__B 3
+#define ORX_CON_RST_W_KRP__W 1
+#define ORX_CON_RST_W_KRP__M 0x8
+#define ORX_CON_RST_W_KRP__PRE 0x0
+
+#define ORX_CON_CPH_PHI_R__A 0x2050012
+#define ORX_CON_CPH_PHI_R__W 16
+#define ORX_CON_CPH_PHI_R__M 0xFFFF
+#define ORX_CON_CPH_PHI_R__PRE 0x0
+
+#define ORX_CON_CPH_FRQ_R__A 0x2050013
+#define ORX_CON_CPH_FRQ_R__W 16
+#define ORX_CON_CPH_FRQ_R__M 0xFFFF
+#define ORX_CON_CPH_FRQ_R__PRE 0x0
+
+#define ORX_CON_CPH_AMP_R__A 0x2050014
+#define ORX_CON_CPH_AMP_R__W 16
+#define ORX_CON_CPH_AMP_R__M 0xFFFF
+#define ORX_CON_CPH_AMP_R__PRE 0x0
+
+#define ORX_CON_CPH_KDF_W__A 0x2050015
+#define ORX_CON_CPH_KDF_W__W 4
+#define ORX_CON_CPH_KDF_W__M 0xF
+#define ORX_CON_CPH_KDF_W__PRE 0x0
+
+#define ORX_CON_CPH_KPF_W__A 0x2050016
+#define ORX_CON_CPH_KPF_W__W 4
+#define ORX_CON_CPH_KPF_W__M 0xF
+#define ORX_CON_CPH_KPF_W__PRE 0x0
+
+#define ORX_CON_CPH_KIF_W__A 0x2050017
+#define ORX_CON_CPH_KIF_W__W 4
+#define ORX_CON_CPH_KIF_W__M 0xF
+#define ORX_CON_CPH_KIF_W__PRE 0x0
+#define ORX_CON_CPH_APT_W__A 0x2050018
+#define ORX_CON_CPH_APT_W__W 16
+#define ORX_CON_CPH_APT_W__M 0xFFFF
+#define ORX_CON_CPH_APT_W__PRE 0x804
+
+#define ORX_CON_CPH_APT_W_PTH__B 0
+#define ORX_CON_CPH_APT_W_PTH__W 8
+#define ORX_CON_CPH_APT_W_PTH__M 0xFF
+#define ORX_CON_CPH_APT_W_PTH__PRE 0x4
+
+#define ORX_CON_CPH_APT_W_ATH__B 8
+#define ORX_CON_CPH_APT_W_ATH__W 8
+#define ORX_CON_CPH_APT_W_ATH__M 0xFF00
+#define ORX_CON_CPH_APT_W_ATH__PRE 0x800
+
+#define ORX_CON_CPH_WLC_W__A 0x2050019
+#define ORX_CON_CPH_WLC_W__W 8
+#define ORX_CON_CPH_WLC_W__M 0xFF
+#define ORX_CON_CPH_WLC_W__PRE 0x81
+
+#define ORX_CON_CPH_WLC_W_LATC__B 0
+#define ORX_CON_CPH_WLC_W_LATC__W 4
+#define ORX_CON_CPH_WLC_W_LATC__M 0xF
+#define ORX_CON_CPH_WLC_W_LATC__PRE 0x1
+
+#define ORX_CON_CPH_WLC_W_WLIM__B 4
+#define ORX_CON_CPH_WLC_W_WLIM__W 4
+#define ORX_CON_CPH_WLC_W_WLIM__M 0xF0
+#define ORX_CON_CPH_WLC_W_WLIM__PRE 0x80
+
+#define ORX_CON_CPH_DLY_W__A 0x205001A
+#define ORX_CON_CPH_DLY_W__W 3
+#define ORX_CON_CPH_DLY_W__M 0x7
+#define ORX_CON_CPH_DLY_W__PRE 0x4
+
+#define ORX_CON_CPH_TCL_W__A 0x205001B
+#define ORX_CON_CPH_TCL_W__W 3
+#define ORX_CON_CPH_TCL_W__M 0x7
+#define ORX_CON_CPH_TCL_W__PRE 0x3
+
+#define ORX_CON_KRP_AMP_R__A 0x205001C
+#define ORX_CON_KRP_AMP_R__W 9
+#define ORX_CON_KRP_AMP_R__M 0x1FF
+#define ORX_CON_KRP_AMP_R__PRE 0x0
+
+#define ORX_CON_KRN_AMP_R__A 0x205001D
+#define ORX_CON_KRN_AMP_R__W 9
+#define ORX_CON_KRN_AMP_R__M 0x1FF
+#define ORX_CON_KRN_AMP_R__PRE 0x0
+
+#define ORX_CON_CTI_DTI_R__A 0x205001E
+#define ORX_CON_CTI_DTI_R__W 16
+#define ORX_CON_CTI_DTI_R__M 0xFFFF
+#define ORX_CON_CTI_DTI_R__PRE 0x0
+
+#define ORX_CON_CTI_KDT_W__A 0x205001F
+#define ORX_CON_CTI_KDT_W__W 4
+#define ORX_CON_CTI_KDT_W__M 0xF
+#define ORX_CON_CTI_KDT_W__PRE 0x4
+
+#define ORX_CON_CTI_KPT_W__A 0x2050020
+#define ORX_CON_CTI_KPT_W__W 4
+#define ORX_CON_CTI_KPT_W__M 0xF
+#define ORX_CON_CTI_KPT_W__PRE 0x3
+
+#define ORX_CON_CTI_KIT_W__A 0x2050021
+#define ORX_CON_CTI_KIT_W__W 4
+#define ORX_CON_CTI_KIT_W__M 0xF
+#define ORX_CON_CTI_KIT_W__PRE 0xB
+
+#define ORX_CON_CTI_TAT_W__A 0x2050022
+#define ORX_CON_CTI_TAT_W__W 4
+#define ORX_CON_CTI_TAT_W__M 0xF
+#define ORX_CON_CTI_TAT_W__PRE 0x3
+
+#define ORX_NSU_COMM_EXEC__A 0x2060000
+#define ORX_NSU_COMM_EXEC__W 2
+#define ORX_NSU_COMM_EXEC__M 0x3
+#define ORX_NSU_COMM_EXEC__PRE 0x0
+#define ORX_NSU_COMM_EXEC_STOP 0x0
+#define ORX_NSU_COMM_EXEC_ACTIVE 0x1
+#define ORX_NSU_COMM_EXEC_HOLD 0x2
+
+#define ORX_NSU_AOX_STDBY_W__A 0x2060010
+#define ORX_NSU_AOX_STDBY_W__W 8
+#define ORX_NSU_AOX_STDBY_W__M 0xFF
+#define ORX_NSU_AOX_STDBY_W__PRE 0x0
+
+#define ORX_NSU_AOX_STDBY_W_STDBYADC__B 0
+#define ORX_NSU_AOX_STDBY_W_STDBYADC__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYADC__M 0x1
+#define ORX_NSU_AOX_STDBY_W_STDBYADC__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYADC_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYADC_A1_OFF 0x1
+#define ORX_NSU_AOX_STDBY_W_STDBYADC_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYADC_A2_ON 0x1
+
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP__B 1
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP__M 0x2
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A1_OFF 0x2
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_ON 0x2
+
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__B 2
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__M 0x4
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A1_OFF 0x4
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_ON 0x4
+
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL__B 3
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL__M 0x8
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A1_OFF 0x8
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_ON 0x8
+
+#define ORX_NSU_AOX_STDBY_W_STDBYPD__B 4
+#define ORX_NSU_AOX_STDBY_W_STDBYPD__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYPD__M 0x10
+#define ORX_NSU_AOX_STDBY_W_STDBYPD__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPD_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPD_A1_OFF 0x10
+#define ORX_NSU_AOX_STDBY_W_STDBYPD_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPD_A2_ON 0x10
+
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__B 5
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__M 0x20
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A1_OFF 0x20
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_ON 0x20
+
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__B 6
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__M 0x40
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A1_OFF 0x40
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_ON 0x40
+
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT__B 7
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT__M 0x80
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A1_OFF 0x80
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_ON 0x80
+
+#define ORX_NSU_AOX_LOFRQ_W__A 0x2060011
+#define ORX_NSU_AOX_LOFRQ_W__W 16
+#define ORX_NSU_AOX_LOFRQ_W__M 0xFFFF
+#define ORX_NSU_AOX_LOFRQ_W__PRE 0x0
+#define ORX_NSU_AOX_LOMDE_W__A 0x2060012
+#define ORX_NSU_AOX_LOMDE_W__W 16
+#define ORX_NSU_AOX_LOMDE_W__M 0xFFFF
+#define ORX_NSU_AOX_LOMDE_W__PRE 0x0
+
+#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__B 0
+#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__W 8
+#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__M 0xFF
+#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__PRE 0x0
+
+#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__B 13
+#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__W 1
+#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__M 0x2000
+#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__PRE 0x0
+
+#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__B 14
+#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__W 2
+#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__M 0xC000
+#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__PRE 0x0
+
+#define ORX_NSU_AOX_LOPOW_W__A 0x2060013
+#define ORX_NSU_AOX_LOPOW_W__W 2
+#define ORX_NSU_AOX_LOPOW_W__M 0x3
+#define ORX_NSU_AOX_LOPOW_W__PRE 0x0
+#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS0DB 0x0
+#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS5DB 0x1
+#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS10DB 0x2
+#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS15DB 0x3
+
+#define ORX_NSU_AOX_STHR_W__A 0x2060014
+#define ORX_NSU_AOX_STHR_W__W 5
+#define ORX_NSU_AOX_STHR_W__M 0x1F
+#define ORX_NSU_AOX_STHR_W__PRE 0x0
+
+#define ORX_NSU_TUN_RFGAIN_W__A 0x2060015
+#define ORX_NSU_TUN_RFGAIN_W__W 15
+#define ORX_NSU_TUN_RFGAIN_W__M 0x7FFF
+#define ORX_NSU_TUN_RFGAIN_W__PRE 0x0
+
+#define ORX_NSU_TUN_IFGAIN_W__A 0x2060016
+#define ORX_NSU_TUN_IFGAIN_W__W 15
+#define ORX_NSU_TUN_IFGAIN_W__M 0x7FFF
+#define ORX_NSU_TUN_IFGAIN_W__PRE 0x0
+
+#define ORX_NSU_TUN_BPF_W__A 0x2060017
+#define ORX_NSU_TUN_BPF_W__W 15
+#define ORX_NSU_TUN_BPF_W__M 0x7FFF
+#define ORX_NSU_TUN_BPF_W__PRE 0x1F9
+#define ORX_NSU_NSS_BITSWAP_W__A 0x2060018
+#define ORX_NSU_NSS_BITSWAP_W__W 3
+#define ORX_NSU_NSS_BITSWAP_W__M 0x7
+#define ORX_NSU_NSS_BITSWAP_W__PRE 0x0
+
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__B 0
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__W 1
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__M 0x1
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__PRE 0x0
+
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__B 1
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__W 1
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__M 0x2
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__PRE 0x0
+
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__B 2
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__W 1
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__M 0x4
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__PRE 0x0
+
+#define ORX_TST_COMM_EXEC__A 0x23F0000
+#define ORX_TST_COMM_EXEC__W 2
+#define ORX_TST_COMM_EXEC__M 0x3
+#define ORX_TST_COMM_EXEC__PRE 0x0
+#define ORX_TST_COMM_EXEC_STOP 0x0
+#define ORX_TST_COMM_EXEC_ACTIVE 0x1
+#define ORX_TST_COMM_EXEC_HOLD 0x2
+
+#define ORX_TST_AOX_TST_W__A 0x23F0010
+#define ORX_TST_AOX_TST_W__W 8
+#define ORX_TST_AOX_TST_W__M 0xFF
+#define ORX_TST_AOX_TST_W__PRE 0x0
+
+#define QAM_COMM_EXEC__A 0x1400000
+#define QAM_COMM_EXEC__W 2
+#define QAM_COMM_EXEC__M 0x3
+#define QAM_COMM_EXEC__PRE 0x0
+#define QAM_COMM_EXEC_STOP 0x0
+#define QAM_COMM_EXEC_ACTIVE 0x1
+#define QAM_COMM_EXEC_HOLD 0x2
+
+#define QAM_COMM_MB__A 0x1400002
+#define QAM_COMM_MB__W 16
+#define QAM_COMM_MB__M 0xFFFF
+#define QAM_COMM_MB__PRE 0x0
+#define QAM_COMM_INT_REQ__A 0x1400003
+#define QAM_COMM_INT_REQ__W 16
+#define QAM_COMM_INT_REQ__M 0xFFFF
+#define QAM_COMM_INT_REQ__PRE 0x0
+
+#define QAM_COMM_INT_REQ_SL_REQ__B 0
+#define QAM_COMM_INT_REQ_SL_REQ__W 1
+#define QAM_COMM_INT_REQ_SL_REQ__M 0x1
+#define QAM_COMM_INT_REQ_SL_REQ__PRE 0x0
+
+#define QAM_COMM_INT_REQ_LC_REQ__B 1
+#define QAM_COMM_INT_REQ_LC_REQ__W 1
+#define QAM_COMM_INT_REQ_LC_REQ__M 0x2
+#define QAM_COMM_INT_REQ_LC_REQ__PRE 0x0
+
+#define QAM_COMM_INT_REQ_VD_REQ__B 2
+#define QAM_COMM_INT_REQ_VD_REQ__W 1
+#define QAM_COMM_INT_REQ_VD_REQ__M 0x4
+#define QAM_COMM_INT_REQ_VD_REQ__PRE 0x0
+
+#define QAM_COMM_INT_REQ_SY_REQ__B 3
+#define QAM_COMM_INT_REQ_SY_REQ__W 1
+#define QAM_COMM_INT_REQ_SY_REQ__M 0x8
+#define QAM_COMM_INT_REQ_SY_REQ__PRE 0x0
+
+#define QAM_COMM_INT_STA__A 0x1400005
+#define QAM_COMM_INT_STA__W 16
+#define QAM_COMM_INT_STA__M 0xFFFF
+#define QAM_COMM_INT_STA__PRE 0x0
+#define QAM_COMM_INT_MSK__A 0x1400006
+#define QAM_COMM_INT_MSK__W 16
+#define QAM_COMM_INT_MSK__M 0xFFFF
+#define QAM_COMM_INT_MSK__PRE 0x0
+#define QAM_COMM_INT_STM__A 0x1400007
+#define QAM_COMM_INT_STM__W 16
+#define QAM_COMM_INT_STM__M 0xFFFF
+#define QAM_COMM_INT_STM__PRE 0x0
+
+#define QAM_TOP_COMM_EXEC__A 0x1410000
+#define QAM_TOP_COMM_EXEC__W 2
+#define QAM_TOP_COMM_EXEC__M 0x3
+#define QAM_TOP_COMM_EXEC__PRE 0x0
+#define QAM_TOP_COMM_EXEC_STOP 0x0
+#define QAM_TOP_COMM_EXEC_ACTIVE 0x1
+#define QAM_TOP_COMM_EXEC_HOLD 0x2
+
+#define QAM_TOP_ANNEX__A 0x1410010
+#define QAM_TOP_ANNEX__W 2
+#define QAM_TOP_ANNEX__M 0x3
+#define QAM_TOP_ANNEX__PRE 0x1
+#define QAM_TOP_ANNEX_A 0x0
+#define QAM_TOP_ANNEX_B 0x1
+#define QAM_TOP_ANNEX_C 0x2
+#define QAM_TOP_ANNEX_D 0x3
+
+#define QAM_TOP_CONSTELLATION__A 0x1410011
+#define QAM_TOP_CONSTELLATION__W 3
+#define QAM_TOP_CONSTELLATION__M 0x7
+#define QAM_TOP_CONSTELLATION__PRE 0x5
+#define QAM_TOP_CONSTELLATION_NONE 0x0
+#define QAM_TOP_CONSTELLATION_QPSK 0x1
+#define QAM_TOP_CONSTELLATION_QAM8 0x2
+#define QAM_TOP_CONSTELLATION_QAM16 0x3
+#define QAM_TOP_CONSTELLATION_QAM32 0x4
+#define QAM_TOP_CONSTELLATION_QAM64 0x5
+#define QAM_TOP_CONSTELLATION_QAM128 0x6
+#define QAM_TOP_CONSTELLATION_QAM256 0x7
+
+#define QAM_FQ_COMM_EXEC__A 0x1420000
+#define QAM_FQ_COMM_EXEC__W 2
+#define QAM_FQ_COMM_EXEC__M 0x3
+#define QAM_FQ_COMM_EXEC__PRE 0x0
+#define QAM_FQ_COMM_EXEC_STOP 0x0
+#define QAM_FQ_COMM_EXEC_ACTIVE 0x1
+#define QAM_FQ_COMM_EXEC_HOLD 0x2
+
+#define QAM_FQ_MODE__A 0x1420010
+#define QAM_FQ_MODE__W 3
+#define QAM_FQ_MODE__M 0x7
+#define QAM_FQ_MODE__PRE 0x0
+
+#define QAM_FQ_MODE_TAPRESET__B 0
+#define QAM_FQ_MODE_TAPRESET__W 1
+#define QAM_FQ_MODE_TAPRESET__M 0x1
+#define QAM_FQ_MODE_TAPRESET__PRE 0x0
+#define QAM_FQ_MODE_TAPRESET_RST 0x1
+
+#define QAM_FQ_MODE_TAPLMS__B 1
+#define QAM_FQ_MODE_TAPLMS__W 1
+#define QAM_FQ_MODE_TAPLMS__M 0x2
+#define QAM_FQ_MODE_TAPLMS__PRE 0x0
+#define QAM_FQ_MODE_TAPLMS_UPD 0x2
+
+#define QAM_FQ_MODE_TAPDRAIN__B 2
+#define QAM_FQ_MODE_TAPDRAIN__W 1
+#define QAM_FQ_MODE_TAPDRAIN__M 0x4
+#define QAM_FQ_MODE_TAPDRAIN__PRE 0x0
+#define QAM_FQ_MODE_TAPDRAIN_DRAIN 0x4
+
+#define QAM_FQ_MU_FACTOR__A 0x1420011
+#define QAM_FQ_MU_FACTOR__W 3
+#define QAM_FQ_MU_FACTOR__M 0x7
+#define QAM_FQ_MU_FACTOR__PRE 0x0
+
+#define QAM_FQ_LA_FACTOR__A 0x1420012
+#define QAM_FQ_LA_FACTOR__W 4
+#define QAM_FQ_LA_FACTOR__M 0xF
+#define QAM_FQ_LA_FACTOR__PRE 0xC
+#define QAM_FQ_CENTTAP_IDX__A 0x1420016
+#define QAM_FQ_CENTTAP_IDX__W 5
+#define QAM_FQ_CENTTAP_IDX__M 0x1F
+#define QAM_FQ_CENTTAP_IDX__PRE 0x13
+
+#define QAM_FQ_CENTTAP_IDX_IDX__B 0
+#define QAM_FQ_CENTTAP_IDX_IDX__W 5
+#define QAM_FQ_CENTTAP_IDX_IDX__M 0x1F
+#define QAM_FQ_CENTTAP_IDX_IDX__PRE 0x13
+
+#define QAM_FQ_CENTTAP_VALUE__A 0x1420017
+#define QAM_FQ_CENTTAP_VALUE__W 12
+#define QAM_FQ_CENTTAP_VALUE__M 0xFFF
+#define QAM_FQ_CENTTAP_VALUE__PRE 0x600
+
+#define QAM_FQ_CENTTAP_VALUE_TAP__B 0
+#define QAM_FQ_CENTTAP_VALUE_TAP__W 12
+#define QAM_FQ_CENTTAP_VALUE_TAP__M 0xFFF
+#define QAM_FQ_CENTTAP_VALUE_TAP__PRE 0x600
+
+#define QAM_FQ_TAP_RE_EL0__A 0x1420020
+#define QAM_FQ_TAP_RE_EL0__W 12
+#define QAM_FQ_TAP_RE_EL0__M 0xFFF
+#define QAM_FQ_TAP_RE_EL0__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL0_TAP__B 0
+#define QAM_FQ_TAP_RE_EL0_TAP__W 12
+#define QAM_FQ_TAP_RE_EL0_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL0_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL0__A 0x1420021
+#define QAM_FQ_TAP_IM_EL0__W 12
+#define QAM_FQ_TAP_IM_EL0__M 0xFFF
+#define QAM_FQ_TAP_IM_EL0__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL0_TAP__B 0
+#define QAM_FQ_TAP_IM_EL0_TAP__W 12
+#define QAM_FQ_TAP_IM_EL0_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL0_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL1__A 0x1420022
+#define QAM_FQ_TAP_RE_EL1__W 12
+#define QAM_FQ_TAP_RE_EL1__M 0xFFF
+#define QAM_FQ_TAP_RE_EL1__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL1_TAP__B 0
+#define QAM_FQ_TAP_RE_EL1_TAP__W 12
+#define QAM_FQ_TAP_RE_EL1_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL1_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL1__A 0x1420023
+#define QAM_FQ_TAP_IM_EL1__W 12
+#define QAM_FQ_TAP_IM_EL1__M 0xFFF
+#define QAM_FQ_TAP_IM_EL1__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL1_TAP__B 0
+#define QAM_FQ_TAP_IM_EL1_TAP__W 12
+#define QAM_FQ_TAP_IM_EL1_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL1_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL2__A 0x1420024
+#define QAM_FQ_TAP_RE_EL2__W 12
+#define QAM_FQ_TAP_RE_EL2__M 0xFFF
+#define QAM_FQ_TAP_RE_EL2__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL2_TAP__B 0
+#define QAM_FQ_TAP_RE_EL2_TAP__W 12
+#define QAM_FQ_TAP_RE_EL2_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL2_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL2__A 0x1420025
+#define QAM_FQ_TAP_IM_EL2__W 12
+#define QAM_FQ_TAP_IM_EL2__M 0xFFF
+#define QAM_FQ_TAP_IM_EL2__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL2_TAP__B 0
+#define QAM_FQ_TAP_IM_EL2_TAP__W 12
+#define QAM_FQ_TAP_IM_EL2_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL2_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL3__A 0x1420026
+#define QAM_FQ_TAP_RE_EL3__W 12
+#define QAM_FQ_TAP_RE_EL3__M 0xFFF
+#define QAM_FQ_TAP_RE_EL3__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL3_TAP__B 0
+#define QAM_FQ_TAP_RE_EL3_TAP__W 12
+#define QAM_FQ_TAP_RE_EL3_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL3_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL3__A 0x1420027
+#define QAM_FQ_TAP_IM_EL3__W 12
+#define QAM_FQ_TAP_IM_EL3__M 0xFFF
+#define QAM_FQ_TAP_IM_EL3__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL3_TAP__B 0
+#define QAM_FQ_TAP_IM_EL3_TAP__W 12
+#define QAM_FQ_TAP_IM_EL3_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL3_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL4__A 0x1420028
+#define QAM_FQ_TAP_RE_EL4__W 12
+#define QAM_FQ_TAP_RE_EL4__M 0xFFF
+#define QAM_FQ_TAP_RE_EL4__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL4_TAP__B 0
+#define QAM_FQ_TAP_RE_EL4_TAP__W 12
+#define QAM_FQ_TAP_RE_EL4_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL4_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL4__A 0x1420029
+#define QAM_FQ_TAP_IM_EL4__W 12
+#define QAM_FQ_TAP_IM_EL4__M 0xFFF
+#define QAM_FQ_TAP_IM_EL4__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL4_TAP__B 0
+#define QAM_FQ_TAP_IM_EL4_TAP__W 12
+#define QAM_FQ_TAP_IM_EL4_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL4_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL5__A 0x142002A
+#define QAM_FQ_TAP_RE_EL5__W 12
+#define QAM_FQ_TAP_RE_EL5__M 0xFFF
+#define QAM_FQ_TAP_RE_EL5__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL5_TAP__B 0
+#define QAM_FQ_TAP_RE_EL5_TAP__W 12
+#define QAM_FQ_TAP_RE_EL5_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL5_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL5__A 0x142002B
+#define QAM_FQ_TAP_IM_EL5__W 12
+#define QAM_FQ_TAP_IM_EL5__M 0xFFF
+#define QAM_FQ_TAP_IM_EL5__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL5_TAP__B 0
+#define QAM_FQ_TAP_IM_EL5_TAP__W 12
+#define QAM_FQ_TAP_IM_EL5_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL5_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL6__A 0x142002C
+#define QAM_FQ_TAP_RE_EL6__W 12
+#define QAM_FQ_TAP_RE_EL6__M 0xFFF
+#define QAM_FQ_TAP_RE_EL6__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL6_TAP__B 0
+#define QAM_FQ_TAP_RE_EL6_TAP__W 12
+#define QAM_FQ_TAP_RE_EL6_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL6_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL6__A 0x142002D
+#define QAM_FQ_TAP_IM_EL6__W 12
+#define QAM_FQ_TAP_IM_EL6__M 0xFFF
+#define QAM_FQ_TAP_IM_EL6__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL6_TAP__B 0
+#define QAM_FQ_TAP_IM_EL6_TAP__W 12
+#define QAM_FQ_TAP_IM_EL6_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL6_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL7__A 0x142002E
+#define QAM_FQ_TAP_RE_EL7__W 12
+#define QAM_FQ_TAP_RE_EL7__M 0xFFF
+#define QAM_FQ_TAP_RE_EL7__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL7_TAP__B 0
+#define QAM_FQ_TAP_RE_EL7_TAP__W 12
+#define QAM_FQ_TAP_RE_EL7_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL7_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL7__A 0x142002F
+#define QAM_FQ_TAP_IM_EL7__W 12
+#define QAM_FQ_TAP_IM_EL7__M 0xFFF
+#define QAM_FQ_TAP_IM_EL7__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL7_TAP__B 0
+#define QAM_FQ_TAP_IM_EL7_TAP__W 12
+#define QAM_FQ_TAP_IM_EL7_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL7_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL8__A 0x1420030
+#define QAM_FQ_TAP_RE_EL8__W 12
+#define QAM_FQ_TAP_RE_EL8__M 0xFFF
+#define QAM_FQ_TAP_RE_EL8__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL8_TAP__B 0
+#define QAM_FQ_TAP_RE_EL8_TAP__W 12
+#define QAM_FQ_TAP_RE_EL8_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL8_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL8__A 0x1420031
+#define QAM_FQ_TAP_IM_EL8__W 12
+#define QAM_FQ_TAP_IM_EL8__M 0xFFF
+#define QAM_FQ_TAP_IM_EL8__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL8_TAP__B 0
+#define QAM_FQ_TAP_IM_EL8_TAP__W 12
+#define QAM_FQ_TAP_IM_EL8_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL8_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL9__A 0x1420032
+#define QAM_FQ_TAP_RE_EL9__W 12
+#define QAM_FQ_TAP_RE_EL9__M 0xFFF
+#define QAM_FQ_TAP_RE_EL9__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL9_TAP__B 0
+#define QAM_FQ_TAP_RE_EL9_TAP__W 12
+#define QAM_FQ_TAP_RE_EL9_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL9_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL9__A 0x1420033
+#define QAM_FQ_TAP_IM_EL9__W 12
+#define QAM_FQ_TAP_IM_EL9__M 0xFFF
+#define QAM_FQ_TAP_IM_EL9__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL9_TAP__B 0
+#define QAM_FQ_TAP_IM_EL9_TAP__W 12
+#define QAM_FQ_TAP_IM_EL9_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL9_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL10__A 0x1420034
+#define QAM_FQ_TAP_RE_EL10__W 12
+#define QAM_FQ_TAP_RE_EL10__M 0xFFF
+#define QAM_FQ_TAP_RE_EL10__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL10_TAP__B 0
+#define QAM_FQ_TAP_RE_EL10_TAP__W 12
+#define QAM_FQ_TAP_RE_EL10_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL10_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL10__A 0x1420035
+#define QAM_FQ_TAP_IM_EL10__W 12
+#define QAM_FQ_TAP_IM_EL10__M 0xFFF
+#define QAM_FQ_TAP_IM_EL10__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL10_TAP__B 0
+#define QAM_FQ_TAP_IM_EL10_TAP__W 12
+#define QAM_FQ_TAP_IM_EL10_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL10_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL11__A 0x1420036
+#define QAM_FQ_TAP_RE_EL11__W 12
+#define QAM_FQ_TAP_RE_EL11__M 0xFFF
+#define QAM_FQ_TAP_RE_EL11__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL11_TAP__B 0
+#define QAM_FQ_TAP_RE_EL11_TAP__W 12
+#define QAM_FQ_TAP_RE_EL11_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL11_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL11__A 0x1420037
+#define QAM_FQ_TAP_IM_EL11__W 12
+#define QAM_FQ_TAP_IM_EL11__M 0xFFF
+#define QAM_FQ_TAP_IM_EL11__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL11_TAP__B 0
+#define QAM_FQ_TAP_IM_EL11_TAP__W 12
+#define QAM_FQ_TAP_IM_EL11_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL11_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL12__A 0x1420038
+#define QAM_FQ_TAP_RE_EL12__W 12
+#define QAM_FQ_TAP_RE_EL12__M 0xFFF
+#define QAM_FQ_TAP_RE_EL12__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL12_TAP__B 0
+#define QAM_FQ_TAP_RE_EL12_TAP__W 12
+#define QAM_FQ_TAP_RE_EL12_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL12_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL12__A 0x1420039
+#define QAM_FQ_TAP_IM_EL12__W 12
+#define QAM_FQ_TAP_IM_EL12__M 0xFFF
+#define QAM_FQ_TAP_IM_EL12__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL12_TAP__B 0
+#define QAM_FQ_TAP_IM_EL12_TAP__W 12
+#define QAM_FQ_TAP_IM_EL12_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL12_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL13__A 0x142003A
+#define QAM_FQ_TAP_RE_EL13__W 12
+#define QAM_FQ_TAP_RE_EL13__M 0xFFF
+#define QAM_FQ_TAP_RE_EL13__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL13_TAP__B 0
+#define QAM_FQ_TAP_RE_EL13_TAP__W 12
+#define QAM_FQ_TAP_RE_EL13_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL13_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL13__A 0x142003B
+#define QAM_FQ_TAP_IM_EL13__W 12
+#define QAM_FQ_TAP_IM_EL13__M 0xFFF
+#define QAM_FQ_TAP_IM_EL13__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL13_TAP__B 0
+#define QAM_FQ_TAP_IM_EL13_TAP__W 12
+#define QAM_FQ_TAP_IM_EL13_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL13_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL14__A 0x142003C
+#define QAM_FQ_TAP_RE_EL14__W 12
+#define QAM_FQ_TAP_RE_EL14__M 0xFFF
+#define QAM_FQ_TAP_RE_EL14__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL14_TAP__B 0
+#define QAM_FQ_TAP_RE_EL14_TAP__W 12
+#define QAM_FQ_TAP_RE_EL14_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL14_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL14__A 0x142003D
+#define QAM_FQ_TAP_IM_EL14__W 12
+#define QAM_FQ_TAP_IM_EL14__M 0xFFF
+#define QAM_FQ_TAP_IM_EL14__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL14_TAP__B 0
+#define QAM_FQ_TAP_IM_EL14_TAP__W 12
+#define QAM_FQ_TAP_IM_EL14_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL14_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL15__A 0x142003E
+#define QAM_FQ_TAP_RE_EL15__W 12
+#define QAM_FQ_TAP_RE_EL15__M 0xFFF
+#define QAM_FQ_TAP_RE_EL15__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL15_TAP__B 0
+#define QAM_FQ_TAP_RE_EL15_TAP__W 12
+#define QAM_FQ_TAP_RE_EL15_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL15_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL15__A 0x142003F
+#define QAM_FQ_TAP_IM_EL15__W 12
+#define QAM_FQ_TAP_IM_EL15__M 0xFFF
+#define QAM_FQ_TAP_IM_EL15__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL15_TAP__B 0
+#define QAM_FQ_TAP_IM_EL15_TAP__W 12
+#define QAM_FQ_TAP_IM_EL15_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL15_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL16__A 0x1420040
+#define QAM_FQ_TAP_RE_EL16__W 12
+#define QAM_FQ_TAP_RE_EL16__M 0xFFF
+#define QAM_FQ_TAP_RE_EL16__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL16_TAP__B 0
+#define QAM_FQ_TAP_RE_EL16_TAP__W 12
+#define QAM_FQ_TAP_RE_EL16_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL16_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL16__A 0x1420041
+#define QAM_FQ_TAP_IM_EL16__W 12
+#define QAM_FQ_TAP_IM_EL16__M 0xFFF
+#define QAM_FQ_TAP_IM_EL16__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL16_TAP__B 0
+#define QAM_FQ_TAP_IM_EL16_TAP__W 12
+#define QAM_FQ_TAP_IM_EL16_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL16_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL17__A 0x1420042
+#define QAM_FQ_TAP_RE_EL17__W 12
+#define QAM_FQ_TAP_RE_EL17__M 0xFFF
+#define QAM_FQ_TAP_RE_EL17__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL17_TAP__B 0
+#define QAM_FQ_TAP_RE_EL17_TAP__W 12
+#define QAM_FQ_TAP_RE_EL17_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL17_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL17__A 0x1420043
+#define QAM_FQ_TAP_IM_EL17__W 12
+#define QAM_FQ_TAP_IM_EL17__M 0xFFF
+#define QAM_FQ_TAP_IM_EL17__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL17_TAP__B 0
+#define QAM_FQ_TAP_IM_EL17_TAP__W 12
+#define QAM_FQ_TAP_IM_EL17_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL17_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL18__A 0x1420044
+#define QAM_FQ_TAP_RE_EL18__W 12
+#define QAM_FQ_TAP_RE_EL18__M 0xFFF
+#define QAM_FQ_TAP_RE_EL18__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL18_TAP__B 0
+#define QAM_FQ_TAP_RE_EL18_TAP__W 12
+#define QAM_FQ_TAP_RE_EL18_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL18_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL18__A 0x1420045
+#define QAM_FQ_TAP_IM_EL18__W 12
+#define QAM_FQ_TAP_IM_EL18__M 0xFFF
+#define QAM_FQ_TAP_IM_EL18__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL18_TAP__B 0
+#define QAM_FQ_TAP_IM_EL18_TAP__W 12
+#define QAM_FQ_TAP_IM_EL18_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL18_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL19__A 0x1420046
+#define QAM_FQ_TAP_RE_EL19__W 12
+#define QAM_FQ_TAP_RE_EL19__M 0xFFF
+#define QAM_FQ_TAP_RE_EL19__PRE 0x600
+
+#define QAM_FQ_TAP_RE_EL19_TAP__B 0
+#define QAM_FQ_TAP_RE_EL19_TAP__W 12
+#define QAM_FQ_TAP_RE_EL19_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL19_TAP__PRE 0x600
+
+#define QAM_FQ_TAP_IM_EL19__A 0x1420047
+#define QAM_FQ_TAP_IM_EL19__W 12
+#define QAM_FQ_TAP_IM_EL19__M 0xFFF
+#define QAM_FQ_TAP_IM_EL19__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL19_TAP__B 0
+#define QAM_FQ_TAP_IM_EL19_TAP__W 12
+#define QAM_FQ_TAP_IM_EL19_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL19_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL20__A 0x1420048
+#define QAM_FQ_TAP_RE_EL20__W 12
+#define QAM_FQ_TAP_RE_EL20__M 0xFFF
+#define QAM_FQ_TAP_RE_EL20__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL20_TAP__B 0
+#define QAM_FQ_TAP_RE_EL20_TAP__W 12
+#define QAM_FQ_TAP_RE_EL20_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL20_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL20__A 0x1420049
+#define QAM_FQ_TAP_IM_EL20__W 12
+#define QAM_FQ_TAP_IM_EL20__M 0xFFF
+#define QAM_FQ_TAP_IM_EL20__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL20_TAP__B 0
+#define QAM_FQ_TAP_IM_EL20_TAP__W 12
+#define QAM_FQ_TAP_IM_EL20_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL20_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL21__A 0x142004A
+#define QAM_FQ_TAP_RE_EL21__W 12
+#define QAM_FQ_TAP_RE_EL21__M 0xFFF
+#define QAM_FQ_TAP_RE_EL21__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL21_TAP__B 0
+#define QAM_FQ_TAP_RE_EL21_TAP__W 12
+#define QAM_FQ_TAP_RE_EL21_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL21_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL21__A 0x142004B
+#define QAM_FQ_TAP_IM_EL21__W 12
+#define QAM_FQ_TAP_IM_EL21__M 0xFFF
+#define QAM_FQ_TAP_IM_EL21__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL21_TAP__B 0
+#define QAM_FQ_TAP_IM_EL21_TAP__W 12
+#define QAM_FQ_TAP_IM_EL21_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL21_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL22__A 0x142004C
+#define QAM_FQ_TAP_RE_EL22__W 12
+#define QAM_FQ_TAP_RE_EL22__M 0xFFF
+#define QAM_FQ_TAP_RE_EL22__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL22_TAP__B 0
+#define QAM_FQ_TAP_RE_EL22_TAP__W 12
+#define QAM_FQ_TAP_RE_EL22_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL22_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL22__A 0x142004D
+#define QAM_FQ_TAP_IM_EL22__W 12
+#define QAM_FQ_TAP_IM_EL22__M 0xFFF
+#define QAM_FQ_TAP_IM_EL22__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL22_TAP__B 0
+#define QAM_FQ_TAP_IM_EL22_TAP__W 12
+#define QAM_FQ_TAP_IM_EL22_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL22_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL23__A 0x142004E
+#define QAM_FQ_TAP_RE_EL23__W 12
+#define QAM_FQ_TAP_RE_EL23__M 0xFFF
+#define QAM_FQ_TAP_RE_EL23__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL23_TAP__B 0
+#define QAM_FQ_TAP_RE_EL23_TAP__W 12
+#define QAM_FQ_TAP_RE_EL23_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL23_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL23__A 0x142004F
+#define QAM_FQ_TAP_IM_EL23__W 12
+#define QAM_FQ_TAP_IM_EL23__M 0xFFF
+#define QAM_FQ_TAP_IM_EL23__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL23_TAP__B 0
+#define QAM_FQ_TAP_IM_EL23_TAP__W 12
+#define QAM_FQ_TAP_IM_EL23_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL23_TAP__PRE 0x2
+
+#define QAM_SL_COMM_EXEC__A 0x1430000
+#define QAM_SL_COMM_EXEC__W 2
+#define QAM_SL_COMM_EXEC__M 0x3
+#define QAM_SL_COMM_EXEC__PRE 0x0
+#define QAM_SL_COMM_EXEC_STOP 0x0
+#define QAM_SL_COMM_EXEC_ACTIVE 0x1
+#define QAM_SL_COMM_EXEC_HOLD 0x2
+
+#define QAM_SL_COMM_MB__A 0x1430002
+#define QAM_SL_COMM_MB__W 4
+#define QAM_SL_COMM_MB__M 0xF
+#define QAM_SL_COMM_MB__PRE 0x0
+#define QAM_SL_COMM_MB_CTL__B 0
+#define QAM_SL_COMM_MB_CTL__W 1
+#define QAM_SL_COMM_MB_CTL__M 0x1
+#define QAM_SL_COMM_MB_CTL__PRE 0x0
+#define QAM_SL_COMM_MB_CTL_OFF 0x0
+#define QAM_SL_COMM_MB_CTL_ON 0x1
+#define QAM_SL_COMM_MB_OBS__B 1
+#define QAM_SL_COMM_MB_OBS__W 1
+#define QAM_SL_COMM_MB_OBS__M 0x2
+#define QAM_SL_COMM_MB_OBS__PRE 0x0
+#define QAM_SL_COMM_MB_OBS_OFF 0x0
+#define QAM_SL_COMM_MB_OBS_ON 0x2
+#define QAM_SL_COMM_MB_MUX_OBS__B 2
+#define QAM_SL_COMM_MB_MUX_OBS__W 2
+#define QAM_SL_COMM_MB_MUX_OBS__M 0xC
+#define QAM_SL_COMM_MB_MUX_OBS__PRE 0x0
+#define QAM_SL_COMM_MB_MUX_OBS_CONST_CORR 0x0
+#define QAM_SL_COMM_MB_MUX_OBS_CONST2LC_O 0x4
+#define QAM_SL_COMM_MB_MUX_OBS_CONST2DQ_O 0x8
+#define QAM_SL_COMM_MB_MUX_OBS_VDEC_O 0xC
+
+#define QAM_SL_COMM_INT_REQ__A 0x1430003
+#define QAM_SL_COMM_INT_REQ__W 1
+#define QAM_SL_COMM_INT_REQ__M 0x1
+#define QAM_SL_COMM_INT_REQ__PRE 0x0
+#define QAM_SL_COMM_INT_STA__A 0x1430005
+#define QAM_SL_COMM_INT_STA__W 2
+#define QAM_SL_COMM_INT_STA__M 0x3
+#define QAM_SL_COMM_INT_STA__PRE 0x0
+
+#define QAM_SL_COMM_INT_STA_MED_ERR_INT__B 0
+#define QAM_SL_COMM_INT_STA_MED_ERR_INT__W 1
+#define QAM_SL_COMM_INT_STA_MED_ERR_INT__M 0x1
+#define QAM_SL_COMM_INT_STA_MED_ERR_INT__PRE 0x0
+
+#define QAM_SL_COMM_INT_STA_MER_INT__B 1
+#define QAM_SL_COMM_INT_STA_MER_INT__W 1
+#define QAM_SL_COMM_INT_STA_MER_INT__M 0x2
+#define QAM_SL_COMM_INT_STA_MER_INT__PRE 0x0
+
+#define QAM_SL_COMM_INT_MSK__A 0x1430006
+#define QAM_SL_COMM_INT_MSK__W 2
+#define QAM_SL_COMM_INT_MSK__M 0x3
+#define QAM_SL_COMM_INT_MSK__PRE 0x0
+#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__B 0
+#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__W 1
+#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__M 0x1
+#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__PRE 0x0
+#define QAM_SL_COMM_INT_MSK_MER_MSK__B 1
+#define QAM_SL_COMM_INT_MSK_MER_MSK__W 1
+#define QAM_SL_COMM_INT_MSK_MER_MSK__M 0x2
+#define QAM_SL_COMM_INT_MSK_MER_MSK__PRE 0x0
+
+#define QAM_SL_COMM_INT_STM__A 0x1430007
+#define QAM_SL_COMM_INT_STM__W 2
+#define QAM_SL_COMM_INT_STM__M 0x3
+#define QAM_SL_COMM_INT_STM__PRE 0x0
+#define QAM_SL_COMM_INT_STM_MED_ERR_STM__B 0
+#define QAM_SL_COMM_INT_STM_MED_ERR_STM__W 1
+#define QAM_SL_COMM_INT_STM_MED_ERR_STM__M 0x1
+#define QAM_SL_COMM_INT_STM_MED_ERR_STM__PRE 0x0
+#define QAM_SL_COMM_INT_STM_MER_STM__B 1
+#define QAM_SL_COMM_INT_STM_MER_STM__W 1
+#define QAM_SL_COMM_INT_STM_MER_STM__M 0x2
+#define QAM_SL_COMM_INT_STM_MER_STM__PRE 0x0
+
+#define QAM_SL_MODE__A 0x1430010
+#define QAM_SL_MODE__W 11
+#define QAM_SL_MODE__M 0x7FF
+#define QAM_SL_MODE__PRE 0x0
+
+#define QAM_SL_MODE_SLICER4LC__B 0
+#define QAM_SL_MODE_SLICER4LC__W 2
+#define QAM_SL_MODE_SLICER4LC__M 0x3
+#define QAM_SL_MODE_SLICER4LC__PRE 0x0
+#define QAM_SL_MODE_SLICER4LC_RECT 0x0
+#define QAM_SL_MODE_SLICER4LC_ONET 0x1
+#define QAM_SL_MODE_SLICER4LC_RAD 0x2
+
+#define QAM_SL_MODE_SLICER4DQ__B 2
+#define QAM_SL_MODE_SLICER4DQ__W 2
+#define QAM_SL_MODE_SLICER4DQ__M 0xC
+#define QAM_SL_MODE_SLICER4DQ__PRE 0x0
+#define QAM_SL_MODE_SLICER4DQ_RECT 0x0
+#define QAM_SL_MODE_SLICER4DQ_ONET 0x4
+#define QAM_SL_MODE_SLICER4DQ_RAD 0x8
+
+#define QAM_SL_MODE_SLICER4VD__B 4
+#define QAM_SL_MODE_SLICER4VD__W 2
+#define QAM_SL_MODE_SLICER4VD__M 0x30
+#define QAM_SL_MODE_SLICER4VD__PRE 0x0
+#define QAM_SL_MODE_SLICER4VD_RECT 0x0
+#define QAM_SL_MODE_SLICER4VD_ONET 0x10
+#define QAM_SL_MODE_SLICER4VD_RAD 0x20
+
+#define QAM_SL_MODE_ROT_DIS__B 6
+#define QAM_SL_MODE_ROT_DIS__W 1
+#define QAM_SL_MODE_ROT_DIS__M 0x40
+#define QAM_SL_MODE_ROT_DIS__PRE 0x0
+
+#define QAM_SL_MODE_DQROT_DIS__B 7
+#define QAM_SL_MODE_DQROT_DIS__W 1
+#define QAM_SL_MODE_DQROT_DIS__M 0x80
+#define QAM_SL_MODE_DQROT_DIS__PRE 0x0
+
+#define QAM_SL_MODE_DFE_DIS__B 8
+#define QAM_SL_MODE_DFE_DIS__W 1
+#define QAM_SL_MODE_DFE_DIS__M 0x100
+#define QAM_SL_MODE_DFE_DIS__PRE 0x0
+
+#define QAM_SL_MODE_RADIUS_MIX__B 9
+#define QAM_SL_MODE_RADIUS_MIX__W 1
+#define QAM_SL_MODE_RADIUS_MIX__M 0x200
+#define QAM_SL_MODE_RADIUS_MIX__PRE 0x0
+
+#define QAM_SL_MODE_TILT_COMP__B 10
+#define QAM_SL_MODE_TILT_COMP__W 1
+#define QAM_SL_MODE_TILT_COMP__M 0x400
+#define QAM_SL_MODE_TILT_COMP__PRE 0x0
+
+#define QAM_SL_K_FACTOR__A 0x1430011
+#define QAM_SL_K_FACTOR__W 4
+#define QAM_SL_K_FACTOR__M 0xF
+#define QAM_SL_K_FACTOR__PRE 0x0
+#define QAM_SL_MEDIAN__A 0x1430012
+#define QAM_SL_MEDIAN__W 14
+#define QAM_SL_MEDIAN__M 0x3FFF
+#define QAM_SL_MEDIAN__PRE 0x0
+
+#define QAM_SL_MEDIAN_LENGTH__B 0
+#define QAM_SL_MEDIAN_LENGTH__W 2
+#define QAM_SL_MEDIAN_LENGTH__M 0x3
+#define QAM_SL_MEDIAN_LENGTH__PRE 0x0
+
+#define QAM_SL_MEDIAN_CORRECT__B 2
+#define QAM_SL_MEDIAN_CORRECT__W 4
+#define QAM_SL_MEDIAN_CORRECT__M 0x3C
+#define QAM_SL_MEDIAN_CORRECT__PRE 0x0
+
+#define QAM_SL_MEDIAN_TOLERANCE__B 6
+#define QAM_SL_MEDIAN_TOLERANCE__W 7
+#define QAM_SL_MEDIAN_TOLERANCE__M 0x1FC0
+#define QAM_SL_MEDIAN_TOLERANCE__PRE 0x0
+
+#define QAM_SL_MEDIAN_FAST__B 13
+#define QAM_SL_MEDIAN_FAST__W 1
+#define QAM_SL_MEDIAN_FAST__M 0x2000
+#define QAM_SL_MEDIAN_FAST__PRE 0x0
+
+#define QAM_SL_ALPHA__A 0x1430013
+#define QAM_SL_ALPHA__W 3
+#define QAM_SL_ALPHA__M 0x7
+#define QAM_SL_ALPHA__PRE 0x0
+
+#define QAM_SL_PHASELIMIT__A 0x1430014
+#define QAM_SL_PHASELIMIT__W 9
+#define QAM_SL_PHASELIMIT__M 0x1FF
+#define QAM_SL_PHASELIMIT__PRE 0x0
+#define QAM_SL_MTA_LENGTH__A 0x1430015
+#define QAM_SL_MTA_LENGTH__W 2
+#define QAM_SL_MTA_LENGTH__M 0x3
+#define QAM_SL_MTA_LENGTH__PRE 0x1
+
+#define QAM_SL_MTA_LENGTH_LENGTH__B 0
+#define QAM_SL_MTA_LENGTH_LENGTH__W 2
+#define QAM_SL_MTA_LENGTH_LENGTH__M 0x3
+#define QAM_SL_MTA_LENGTH_LENGTH__PRE 0x1
+
+#define QAM_SL_MEDIAN_ERROR__A 0x1430016
+#define QAM_SL_MEDIAN_ERROR__W 10
+#define QAM_SL_MEDIAN_ERROR__M 0x3FF
+#define QAM_SL_MEDIAN_ERROR__PRE 0x0
+
+#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__B 0
+#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__W 10
+#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__M 0x3FF
+#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__PRE 0x0
+
+#define QAM_SL_ERR_POWER__A 0x1430017
+#define QAM_SL_ERR_POWER__W 16
+#define QAM_SL_ERR_POWER__M 0xFFFF
+#define QAM_SL_ERR_POWER__PRE 0x0
+
+#define QAM_DQ_COMM_EXEC__A 0x1440000
+#define QAM_DQ_COMM_EXEC__W 2
+#define QAM_DQ_COMM_EXEC__M 0x3
+#define QAM_DQ_COMM_EXEC__PRE 0x0
+#define QAM_DQ_COMM_EXEC_STOP 0x0
+#define QAM_DQ_COMM_EXEC_ACTIVE 0x1
+#define QAM_DQ_COMM_EXEC_HOLD 0x2
+
+#define QAM_DQ_MODE__A 0x1440010
+#define QAM_DQ_MODE__W 5
+#define QAM_DQ_MODE__M 0x1F
+#define QAM_DQ_MODE__PRE 0x0
+
+#define QAM_DQ_MODE_TAPRESET__B 0
+#define QAM_DQ_MODE_TAPRESET__W 1
+#define QAM_DQ_MODE_TAPRESET__M 0x1
+#define QAM_DQ_MODE_TAPRESET__PRE 0x0
+#define QAM_DQ_MODE_TAPRESET_RST 0x1
+
+#define QAM_DQ_MODE_TAPLMS__B 1
+#define QAM_DQ_MODE_TAPLMS__W 1
+#define QAM_DQ_MODE_TAPLMS__M 0x2
+#define QAM_DQ_MODE_TAPLMS__PRE 0x0
+#define QAM_DQ_MODE_TAPLMS_UPD 0x2
+
+#define QAM_DQ_MODE_TAPDRAIN__B 2
+#define QAM_DQ_MODE_TAPDRAIN__W 1
+#define QAM_DQ_MODE_TAPDRAIN__M 0x4
+#define QAM_DQ_MODE_TAPDRAIN__PRE 0x0
+#define QAM_DQ_MODE_TAPDRAIN_DRAIN 0x4
+
+#define QAM_DQ_MODE_FB__B 3
+#define QAM_DQ_MODE_FB__W 2
+#define QAM_DQ_MODE_FB__M 0x18
+#define QAM_DQ_MODE_FB__PRE 0x0
+#define QAM_DQ_MODE_FB_CMA 0x0
+#define QAM_DQ_MODE_FB_RADIUS 0x8
+#define QAM_DQ_MODE_FB_DFB 0x10
+#define QAM_DQ_MODE_FB_TRELLIS 0x18
+
+#define QAM_DQ_MU_FACTOR__A 0x1440011
+#define QAM_DQ_MU_FACTOR__W 3
+#define QAM_DQ_MU_FACTOR__M 0x7
+#define QAM_DQ_MU_FACTOR__PRE 0x0
+
+#define QAM_DQ_LA_FACTOR__A 0x1440012
+#define QAM_DQ_LA_FACTOR__W 4
+#define QAM_DQ_LA_FACTOR__M 0xF
+#define QAM_DQ_LA_FACTOR__PRE 0xC
+
+#define QAM_DQ_CMA_RATIO__A 0x1440013
+#define QAM_DQ_CMA_RATIO__W 14
+#define QAM_DQ_CMA_RATIO__M 0x3FFF
+#define QAM_DQ_CMA_RATIO__PRE 0x3CF9
+#define QAM_DQ_CMA_RATIO_QPSK 0x2000
+#define QAM_DQ_CMA_RATIO_QAM16 0x34CD
+#define QAM_DQ_CMA_RATIO_QAM64 0x3A00
+#define QAM_DQ_CMA_RATIO_QAM256 0x3B4D
+#define QAM_DQ_CMA_RATIO_QAM1024 0x3BA0
+
+#define QAM_DQ_QUAL_RADSEL__A 0x1440014
+#define QAM_DQ_QUAL_RADSEL__W 3
+#define QAM_DQ_QUAL_RADSEL__M 0x7
+#define QAM_DQ_QUAL_RADSEL__PRE 0x0
+
+#define QAM_DQ_QUAL_RADSEL_BIT__B 0
+#define QAM_DQ_QUAL_RADSEL_BIT__W 3
+#define QAM_DQ_QUAL_RADSEL_BIT__M 0x7
+#define QAM_DQ_QUAL_RADSEL_BIT__PRE 0x0
+#define QAM_DQ_QUAL_RADSEL_BIT_PURE_RADIUS 0x0
+#define QAM_DQ_QUAL_RADSEL_BIT_PURE_CMA 0x6
+
+#define QAM_DQ_QUAL_ENA__A 0x1440015
+#define QAM_DQ_QUAL_ENA__W 1
+#define QAM_DQ_QUAL_ENA__M 0x1
+#define QAM_DQ_QUAL_ENA__PRE 0x0
+
+#define QAM_DQ_QUAL_ENA_ENA__B 0
+#define QAM_DQ_QUAL_ENA_ENA__W 1
+#define QAM_DQ_QUAL_ENA_ENA__M 0x1
+#define QAM_DQ_QUAL_ENA_ENA__PRE 0x0
+#define QAM_DQ_QUAL_ENA_ENA_QUAL_WEIGHTING 0x1
+
+#define QAM_DQ_QUAL_FUN0__A 0x1440018
+#define QAM_DQ_QUAL_FUN0__W 6
+#define QAM_DQ_QUAL_FUN0__M 0x3F
+#define QAM_DQ_QUAL_FUN0__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN0_BIT__B 0
+#define QAM_DQ_QUAL_FUN0_BIT__W 6
+#define QAM_DQ_QUAL_FUN0_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN0_BIT__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN1__A 0x1440019
+#define QAM_DQ_QUAL_FUN1__W 6
+#define QAM_DQ_QUAL_FUN1__M 0x3F
+#define QAM_DQ_QUAL_FUN1__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN1_BIT__B 0
+#define QAM_DQ_QUAL_FUN1_BIT__W 6
+#define QAM_DQ_QUAL_FUN1_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN1_BIT__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN2__A 0x144001A
+#define QAM_DQ_QUAL_FUN2__W 6
+#define QAM_DQ_QUAL_FUN2__M 0x3F
+#define QAM_DQ_QUAL_FUN2__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN2_BIT__B 0
+#define QAM_DQ_QUAL_FUN2_BIT__W 6
+#define QAM_DQ_QUAL_FUN2_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN2_BIT__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN3__A 0x144001B
+#define QAM_DQ_QUAL_FUN3__W 6
+#define QAM_DQ_QUAL_FUN3__M 0x3F
+#define QAM_DQ_QUAL_FUN3__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN3_BIT__B 0
+#define QAM_DQ_QUAL_FUN3_BIT__W 6
+#define QAM_DQ_QUAL_FUN3_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN3_BIT__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN4__A 0x144001C
+#define QAM_DQ_QUAL_FUN4__W 6
+#define QAM_DQ_QUAL_FUN4__M 0x3F
+#define QAM_DQ_QUAL_FUN4__PRE 0x6
+
+#define QAM_DQ_QUAL_FUN4_BIT__B 0
+#define QAM_DQ_QUAL_FUN4_BIT__W 6
+#define QAM_DQ_QUAL_FUN4_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN4_BIT__PRE 0x6
+
+#define QAM_DQ_QUAL_FUN5__A 0x144001D
+#define QAM_DQ_QUAL_FUN5__W 6
+#define QAM_DQ_QUAL_FUN5__M 0x3F
+#define QAM_DQ_QUAL_FUN5__PRE 0x6
+
+#define QAM_DQ_QUAL_FUN5_BIT__B 0
+#define QAM_DQ_QUAL_FUN5_BIT__W 6
+#define QAM_DQ_QUAL_FUN5_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN5_BIT__PRE 0x6
+
+#define QAM_DQ_RAW_LIM__A 0x144001E
+#define QAM_DQ_RAW_LIM__W 5
+#define QAM_DQ_RAW_LIM__M 0x1F
+#define QAM_DQ_RAW_LIM__PRE 0x1F
+
+#define QAM_DQ_RAW_LIM_BIT__B 0
+#define QAM_DQ_RAW_LIM_BIT__W 5
+#define QAM_DQ_RAW_LIM_BIT__M 0x1F
+#define QAM_DQ_RAW_LIM_BIT__PRE 0x1F
+
+#define QAM_DQ_TAP_RE_EL0__A 0x1440020
+#define QAM_DQ_TAP_RE_EL0__W 12
+#define QAM_DQ_TAP_RE_EL0__M 0xFFF
+#define QAM_DQ_TAP_RE_EL0__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL0_TAP__B 0
+#define QAM_DQ_TAP_RE_EL0_TAP__W 12
+#define QAM_DQ_TAP_RE_EL0_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL0_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL0__A 0x1440021
+#define QAM_DQ_TAP_IM_EL0__W 12
+#define QAM_DQ_TAP_IM_EL0__M 0xFFF
+#define QAM_DQ_TAP_IM_EL0__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL0_TAP__B 0
+#define QAM_DQ_TAP_IM_EL0_TAP__W 12
+#define QAM_DQ_TAP_IM_EL0_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL0_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL1__A 0x1440022
+#define QAM_DQ_TAP_RE_EL1__W 12
+#define QAM_DQ_TAP_RE_EL1__M 0xFFF
+#define QAM_DQ_TAP_RE_EL1__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL1_TAP__B 0
+#define QAM_DQ_TAP_RE_EL1_TAP__W 12
+#define QAM_DQ_TAP_RE_EL1_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL1_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL1__A 0x1440023
+#define QAM_DQ_TAP_IM_EL1__W 12
+#define QAM_DQ_TAP_IM_EL1__M 0xFFF
+#define QAM_DQ_TAP_IM_EL1__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL1_TAP__B 0
+#define QAM_DQ_TAP_IM_EL1_TAP__W 12
+#define QAM_DQ_TAP_IM_EL1_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL1_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL2__A 0x1440024
+#define QAM_DQ_TAP_RE_EL2__W 12
+#define QAM_DQ_TAP_RE_EL2__M 0xFFF
+#define QAM_DQ_TAP_RE_EL2__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL2_TAP__B 0
+#define QAM_DQ_TAP_RE_EL2_TAP__W 12
+#define QAM_DQ_TAP_RE_EL2_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL2_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL2__A 0x1440025
+#define QAM_DQ_TAP_IM_EL2__W 12
+#define QAM_DQ_TAP_IM_EL2__M 0xFFF
+#define QAM_DQ_TAP_IM_EL2__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL2_TAP__B 0
+#define QAM_DQ_TAP_IM_EL2_TAP__W 12
+#define QAM_DQ_TAP_IM_EL2_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL2_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL3__A 0x1440026
+#define QAM_DQ_TAP_RE_EL3__W 12
+#define QAM_DQ_TAP_RE_EL3__M 0xFFF
+#define QAM_DQ_TAP_RE_EL3__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL3_TAP__B 0
+#define QAM_DQ_TAP_RE_EL3_TAP__W 12
+#define QAM_DQ_TAP_RE_EL3_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL3_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL3__A 0x1440027
+#define QAM_DQ_TAP_IM_EL3__W 12
+#define QAM_DQ_TAP_IM_EL3__M 0xFFF
+#define QAM_DQ_TAP_IM_EL3__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL3_TAP__B 0
+#define QAM_DQ_TAP_IM_EL3_TAP__W 12
+#define QAM_DQ_TAP_IM_EL3_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL3_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL4__A 0x1440028
+#define QAM_DQ_TAP_RE_EL4__W 12
+#define QAM_DQ_TAP_RE_EL4__M 0xFFF
+#define QAM_DQ_TAP_RE_EL4__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL4_TAP__B 0
+#define QAM_DQ_TAP_RE_EL4_TAP__W 12
+#define QAM_DQ_TAP_RE_EL4_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL4_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL4__A 0x1440029
+#define QAM_DQ_TAP_IM_EL4__W 12
+#define QAM_DQ_TAP_IM_EL4__M 0xFFF
+#define QAM_DQ_TAP_IM_EL4__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL4_TAP__B 0
+#define QAM_DQ_TAP_IM_EL4_TAP__W 12
+#define QAM_DQ_TAP_IM_EL4_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL4_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL5__A 0x144002A
+#define QAM_DQ_TAP_RE_EL5__W 12
+#define QAM_DQ_TAP_RE_EL5__M 0xFFF
+#define QAM_DQ_TAP_RE_EL5__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL5_TAP__B 0
+#define QAM_DQ_TAP_RE_EL5_TAP__W 12
+#define QAM_DQ_TAP_RE_EL5_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL5_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL5__A 0x144002B
+#define QAM_DQ_TAP_IM_EL5__W 12
+#define QAM_DQ_TAP_IM_EL5__M 0xFFF
+#define QAM_DQ_TAP_IM_EL5__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL5_TAP__B 0
+#define QAM_DQ_TAP_IM_EL5_TAP__W 12
+#define QAM_DQ_TAP_IM_EL5_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL5_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL6__A 0x144002C
+#define QAM_DQ_TAP_RE_EL6__W 12
+#define QAM_DQ_TAP_RE_EL6__M 0xFFF
+#define QAM_DQ_TAP_RE_EL6__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL6_TAP__B 0
+#define QAM_DQ_TAP_RE_EL6_TAP__W 12
+#define QAM_DQ_TAP_RE_EL6_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL6_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL6__A 0x144002D
+#define QAM_DQ_TAP_IM_EL6__W 12
+#define QAM_DQ_TAP_IM_EL6__M 0xFFF
+#define QAM_DQ_TAP_IM_EL6__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL6_TAP__B 0
+#define QAM_DQ_TAP_IM_EL6_TAP__W 12
+#define QAM_DQ_TAP_IM_EL6_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL6_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL7__A 0x144002E
+#define QAM_DQ_TAP_RE_EL7__W 12
+#define QAM_DQ_TAP_RE_EL7__M 0xFFF
+#define QAM_DQ_TAP_RE_EL7__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL7_TAP__B 0
+#define QAM_DQ_TAP_RE_EL7_TAP__W 12
+#define QAM_DQ_TAP_RE_EL7_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL7_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL7__A 0x144002F
+#define QAM_DQ_TAP_IM_EL7__W 12
+#define QAM_DQ_TAP_IM_EL7__M 0xFFF
+#define QAM_DQ_TAP_IM_EL7__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL7_TAP__B 0
+#define QAM_DQ_TAP_IM_EL7_TAP__W 12
+#define QAM_DQ_TAP_IM_EL7_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL7_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL8__A 0x1440030
+#define QAM_DQ_TAP_RE_EL8__W 12
+#define QAM_DQ_TAP_RE_EL8__M 0xFFF
+#define QAM_DQ_TAP_RE_EL8__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL8_TAP__B 0
+#define QAM_DQ_TAP_RE_EL8_TAP__W 12
+#define QAM_DQ_TAP_RE_EL8_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL8_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL8__A 0x1440031
+#define QAM_DQ_TAP_IM_EL8__W 12
+#define QAM_DQ_TAP_IM_EL8__M 0xFFF
+#define QAM_DQ_TAP_IM_EL8__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL8_TAP__B 0
+#define QAM_DQ_TAP_IM_EL8_TAP__W 12
+#define QAM_DQ_TAP_IM_EL8_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL8_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL9__A 0x1440032
+#define QAM_DQ_TAP_RE_EL9__W 12
+#define QAM_DQ_TAP_RE_EL9__M 0xFFF
+#define QAM_DQ_TAP_RE_EL9__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL9_TAP__B 0
+#define QAM_DQ_TAP_RE_EL9_TAP__W 12
+#define QAM_DQ_TAP_RE_EL9_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL9_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL9__A 0x1440033
+#define QAM_DQ_TAP_IM_EL9__W 12
+#define QAM_DQ_TAP_IM_EL9__M 0xFFF
+#define QAM_DQ_TAP_IM_EL9__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL9_TAP__B 0
+#define QAM_DQ_TAP_IM_EL9_TAP__W 12
+#define QAM_DQ_TAP_IM_EL9_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL9_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL10__A 0x1440034
+#define QAM_DQ_TAP_RE_EL10__W 12
+#define QAM_DQ_TAP_RE_EL10__M 0xFFF
+#define QAM_DQ_TAP_RE_EL10__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL10_TAP__B 0
+#define QAM_DQ_TAP_RE_EL10_TAP__W 12
+#define QAM_DQ_TAP_RE_EL10_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL10_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL10__A 0x1440035
+#define QAM_DQ_TAP_IM_EL10__W 12
+#define QAM_DQ_TAP_IM_EL10__M 0xFFF
+#define QAM_DQ_TAP_IM_EL10__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL10_TAP__B 0
+#define QAM_DQ_TAP_IM_EL10_TAP__W 12
+#define QAM_DQ_TAP_IM_EL10_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL10_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL11__A 0x1440036
+#define QAM_DQ_TAP_RE_EL11__W 12
+#define QAM_DQ_TAP_RE_EL11__M 0xFFF
+#define QAM_DQ_TAP_RE_EL11__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL11_TAP__B 0
+#define QAM_DQ_TAP_RE_EL11_TAP__W 12
+#define QAM_DQ_TAP_RE_EL11_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL11_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL11__A 0x1440037
+#define QAM_DQ_TAP_IM_EL11__W 12
+#define QAM_DQ_TAP_IM_EL11__M 0xFFF
+#define QAM_DQ_TAP_IM_EL11__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL11_TAP__B 0
+#define QAM_DQ_TAP_IM_EL11_TAP__W 12
+#define QAM_DQ_TAP_IM_EL11_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL11_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL12__A 0x1440038
+#define QAM_DQ_TAP_RE_EL12__W 12
+#define QAM_DQ_TAP_RE_EL12__M 0xFFF
+#define QAM_DQ_TAP_RE_EL12__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL12_TAP__B 0
+#define QAM_DQ_TAP_RE_EL12_TAP__W 12
+#define QAM_DQ_TAP_RE_EL12_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL12_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL12__A 0x1440039
+#define QAM_DQ_TAP_IM_EL12__W 12
+#define QAM_DQ_TAP_IM_EL12__M 0xFFF
+#define QAM_DQ_TAP_IM_EL12__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL12_TAP__B 0
+#define QAM_DQ_TAP_IM_EL12_TAP__W 12
+#define QAM_DQ_TAP_IM_EL12_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL12_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL13__A 0x144003A
+#define QAM_DQ_TAP_RE_EL13__W 12
+#define QAM_DQ_TAP_RE_EL13__M 0xFFF
+#define QAM_DQ_TAP_RE_EL13__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL13_TAP__B 0
+#define QAM_DQ_TAP_RE_EL13_TAP__W 12
+#define QAM_DQ_TAP_RE_EL13_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL13_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL13__A 0x144003B
+#define QAM_DQ_TAP_IM_EL13__W 12
+#define QAM_DQ_TAP_IM_EL13__M 0xFFF
+#define QAM_DQ_TAP_IM_EL13__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL13_TAP__B 0
+#define QAM_DQ_TAP_IM_EL13_TAP__W 12
+#define QAM_DQ_TAP_IM_EL13_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL13_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL14__A 0x144003C
+#define QAM_DQ_TAP_RE_EL14__W 12
+#define QAM_DQ_TAP_RE_EL14__M 0xFFF
+#define QAM_DQ_TAP_RE_EL14__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL14_TAP__B 0
+#define QAM_DQ_TAP_RE_EL14_TAP__W 12
+#define QAM_DQ_TAP_RE_EL14_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL14_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL14__A 0x144003D
+#define QAM_DQ_TAP_IM_EL14__W 12
+#define QAM_DQ_TAP_IM_EL14__M 0xFFF
+#define QAM_DQ_TAP_IM_EL14__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL14_TAP__B 0
+#define QAM_DQ_TAP_IM_EL14_TAP__W 12
+#define QAM_DQ_TAP_IM_EL14_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL14_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL15__A 0x144003E
+#define QAM_DQ_TAP_RE_EL15__W 12
+#define QAM_DQ_TAP_RE_EL15__M 0xFFF
+#define QAM_DQ_TAP_RE_EL15__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL15_TAP__B 0
+#define QAM_DQ_TAP_RE_EL15_TAP__W 12
+#define QAM_DQ_TAP_RE_EL15_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL15_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL15__A 0x144003F
+#define QAM_DQ_TAP_IM_EL15__W 12
+#define QAM_DQ_TAP_IM_EL15__M 0xFFF
+#define QAM_DQ_TAP_IM_EL15__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL15_TAP__B 0
+#define QAM_DQ_TAP_IM_EL15_TAP__W 12
+#define QAM_DQ_TAP_IM_EL15_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL15_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL16__A 0x1440040
+#define QAM_DQ_TAP_RE_EL16__W 12
+#define QAM_DQ_TAP_RE_EL16__M 0xFFF
+#define QAM_DQ_TAP_RE_EL16__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL16_TAP__B 0
+#define QAM_DQ_TAP_RE_EL16_TAP__W 12
+#define QAM_DQ_TAP_RE_EL16_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL16_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL16__A 0x1440041
+#define QAM_DQ_TAP_IM_EL16__W 12
+#define QAM_DQ_TAP_IM_EL16__M 0xFFF
+#define QAM_DQ_TAP_IM_EL16__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL16_TAP__B 0
+#define QAM_DQ_TAP_IM_EL16_TAP__W 12
+#define QAM_DQ_TAP_IM_EL16_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL16_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL17__A 0x1440042
+#define QAM_DQ_TAP_RE_EL17__W 12
+#define QAM_DQ_TAP_RE_EL17__M 0xFFF
+#define QAM_DQ_TAP_RE_EL17__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL17_TAP__B 0
+#define QAM_DQ_TAP_RE_EL17_TAP__W 12
+#define QAM_DQ_TAP_RE_EL17_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL17_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL17__A 0x1440043
+#define QAM_DQ_TAP_IM_EL17__W 12
+#define QAM_DQ_TAP_IM_EL17__M 0xFFF
+#define QAM_DQ_TAP_IM_EL17__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL17_TAP__B 0
+#define QAM_DQ_TAP_IM_EL17_TAP__W 12
+#define QAM_DQ_TAP_IM_EL17_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL17_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL18__A 0x1440044
+#define QAM_DQ_TAP_RE_EL18__W 12
+#define QAM_DQ_TAP_RE_EL18__M 0xFFF
+#define QAM_DQ_TAP_RE_EL18__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL18_TAP__B 0
+#define QAM_DQ_TAP_RE_EL18_TAP__W 12
+#define QAM_DQ_TAP_RE_EL18_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL18_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL18__A 0x1440045
+#define QAM_DQ_TAP_IM_EL18__W 12
+#define QAM_DQ_TAP_IM_EL18__M 0xFFF
+#define QAM_DQ_TAP_IM_EL18__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL18_TAP__B 0
+#define QAM_DQ_TAP_IM_EL18_TAP__W 12
+#define QAM_DQ_TAP_IM_EL18_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL18_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL19__A 0x1440046
+#define QAM_DQ_TAP_RE_EL19__W 12
+#define QAM_DQ_TAP_RE_EL19__M 0xFFF
+#define QAM_DQ_TAP_RE_EL19__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL19_TAP__B 0
+#define QAM_DQ_TAP_RE_EL19_TAP__W 12
+#define QAM_DQ_TAP_RE_EL19_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL19_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL19__A 0x1440047
+#define QAM_DQ_TAP_IM_EL19__W 12
+#define QAM_DQ_TAP_IM_EL19__M 0xFFF
+#define QAM_DQ_TAP_IM_EL19__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL19_TAP__B 0
+#define QAM_DQ_TAP_IM_EL19_TAP__W 12
+#define QAM_DQ_TAP_IM_EL19_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL19_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL20__A 0x1440048
+#define QAM_DQ_TAP_RE_EL20__W 12
+#define QAM_DQ_TAP_RE_EL20__M 0xFFF
+#define QAM_DQ_TAP_RE_EL20__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL20_TAP__B 0
+#define QAM_DQ_TAP_RE_EL20_TAP__W 12
+#define QAM_DQ_TAP_RE_EL20_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL20_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL20__A 0x1440049
+#define QAM_DQ_TAP_IM_EL20__W 12
+#define QAM_DQ_TAP_IM_EL20__M 0xFFF
+#define QAM_DQ_TAP_IM_EL20__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL20_TAP__B 0
+#define QAM_DQ_TAP_IM_EL20_TAP__W 12
+#define QAM_DQ_TAP_IM_EL20_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL20_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL21__A 0x144004A
+#define QAM_DQ_TAP_RE_EL21__W 12
+#define QAM_DQ_TAP_RE_EL21__M 0xFFF
+#define QAM_DQ_TAP_RE_EL21__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL21_TAP__B 0
+#define QAM_DQ_TAP_RE_EL21_TAP__W 12
+#define QAM_DQ_TAP_RE_EL21_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL21_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL21__A 0x144004B
+#define QAM_DQ_TAP_IM_EL21__W 12
+#define QAM_DQ_TAP_IM_EL21__M 0xFFF
+#define QAM_DQ_TAP_IM_EL21__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL21_TAP__B 0
+#define QAM_DQ_TAP_IM_EL21_TAP__W 12
+#define QAM_DQ_TAP_IM_EL21_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL21_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL22__A 0x144004C
+#define QAM_DQ_TAP_RE_EL22__W 12
+#define QAM_DQ_TAP_RE_EL22__M 0xFFF
+#define QAM_DQ_TAP_RE_EL22__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL22_TAP__B 0
+#define QAM_DQ_TAP_RE_EL22_TAP__W 12
+#define QAM_DQ_TAP_RE_EL22_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL22_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL22__A 0x144004D
+#define QAM_DQ_TAP_IM_EL22__W 12
+#define QAM_DQ_TAP_IM_EL22__M 0xFFF
+#define QAM_DQ_TAP_IM_EL22__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL22_TAP__B 0
+#define QAM_DQ_TAP_IM_EL22_TAP__W 12
+#define QAM_DQ_TAP_IM_EL22_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL22_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL23__A 0x144004E
+#define QAM_DQ_TAP_RE_EL23__W 12
+#define QAM_DQ_TAP_RE_EL23__M 0xFFF
+#define QAM_DQ_TAP_RE_EL23__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL23_TAP__B 0
+#define QAM_DQ_TAP_RE_EL23_TAP__W 12
+#define QAM_DQ_TAP_RE_EL23_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL23_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL23__A 0x144004F
+#define QAM_DQ_TAP_IM_EL23__W 12
+#define QAM_DQ_TAP_IM_EL23__M 0xFFF
+#define QAM_DQ_TAP_IM_EL23__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL23_TAP__B 0
+#define QAM_DQ_TAP_IM_EL23_TAP__W 12
+#define QAM_DQ_TAP_IM_EL23_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL23_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL24__A 0x1440050
+#define QAM_DQ_TAP_RE_EL24__W 12
+#define QAM_DQ_TAP_RE_EL24__M 0xFFF
+#define QAM_DQ_TAP_RE_EL24__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL24_TAP__B 0
+#define QAM_DQ_TAP_RE_EL24_TAP__W 12
+#define QAM_DQ_TAP_RE_EL24_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL24_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL24__A 0x1440051
+#define QAM_DQ_TAP_IM_EL24__W 12
+#define QAM_DQ_TAP_IM_EL24__M 0xFFF
+#define QAM_DQ_TAP_IM_EL24__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL24_TAP__B 0
+#define QAM_DQ_TAP_IM_EL24_TAP__W 12
+#define QAM_DQ_TAP_IM_EL24_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL24_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL25__A 0x1440052
+#define QAM_DQ_TAP_RE_EL25__W 12
+#define QAM_DQ_TAP_RE_EL25__M 0xFFF
+#define QAM_DQ_TAP_RE_EL25__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL25_TAP__B 0
+#define QAM_DQ_TAP_RE_EL25_TAP__W 12
+#define QAM_DQ_TAP_RE_EL25_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL25_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL25__A 0x1440053
+#define QAM_DQ_TAP_IM_EL25__W 12
+#define QAM_DQ_TAP_IM_EL25__M 0xFFF
+#define QAM_DQ_TAP_IM_EL25__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL25_TAP__B 0
+#define QAM_DQ_TAP_IM_EL25_TAP__W 12
+#define QAM_DQ_TAP_IM_EL25_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL25_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL26__A 0x1440054
+#define QAM_DQ_TAP_RE_EL26__W 12
+#define QAM_DQ_TAP_RE_EL26__M 0xFFF
+#define QAM_DQ_TAP_RE_EL26__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL26_TAP__B 0
+#define QAM_DQ_TAP_RE_EL26_TAP__W 12
+#define QAM_DQ_TAP_RE_EL26_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL26_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL26__A 0x1440055
+#define QAM_DQ_TAP_IM_EL26__W 12
+#define QAM_DQ_TAP_IM_EL26__M 0xFFF
+#define QAM_DQ_TAP_IM_EL26__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL26_TAP__B 0
+#define QAM_DQ_TAP_IM_EL26_TAP__W 12
+#define QAM_DQ_TAP_IM_EL26_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL26_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL27__A 0x1440056
+#define QAM_DQ_TAP_RE_EL27__W 12
+#define QAM_DQ_TAP_RE_EL27__M 0xFFF
+#define QAM_DQ_TAP_RE_EL27__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL27_TAP__B 0
+#define QAM_DQ_TAP_RE_EL27_TAP__W 12
+#define QAM_DQ_TAP_RE_EL27_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL27_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL27__A 0x1440057
+#define QAM_DQ_TAP_IM_EL27__W 12
+#define QAM_DQ_TAP_IM_EL27__M 0xFFF
+#define QAM_DQ_TAP_IM_EL27__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL27_TAP__B 0
+#define QAM_DQ_TAP_IM_EL27_TAP__W 12
+#define QAM_DQ_TAP_IM_EL27_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL27_TAP__PRE 0x2
+
+#define QAM_LC_COMM_EXEC__A 0x1450000
+#define QAM_LC_COMM_EXEC__W 2
+#define QAM_LC_COMM_EXEC__M 0x3
+#define QAM_LC_COMM_EXEC__PRE 0x0
+#define QAM_LC_COMM_EXEC_STOP 0x0
+#define QAM_LC_COMM_EXEC_ACTIVE 0x1
+#define QAM_LC_COMM_EXEC_HOLD 0x2
+
+#define QAM_LC_COMM_MB__A 0x1450002
+#define QAM_LC_COMM_MB__W 2
+#define QAM_LC_COMM_MB__M 0x3
+#define QAM_LC_COMM_MB__PRE 0x0
+#define QAM_LC_COMM_MB_CTL__B 0
+#define QAM_LC_COMM_MB_CTL__W 1
+#define QAM_LC_COMM_MB_CTL__M 0x1
+#define QAM_LC_COMM_MB_CTL__PRE 0x0
+#define QAM_LC_COMM_MB_CTL_OFF 0x0
+#define QAM_LC_COMM_MB_CTL_ON 0x1
+#define QAM_LC_COMM_MB_OBS__B 1
+#define QAM_LC_COMM_MB_OBS__W 1
+#define QAM_LC_COMM_MB_OBS__M 0x2
+#define QAM_LC_COMM_MB_OBS__PRE 0x0
+#define QAM_LC_COMM_MB_OBS_OFF 0x0
+#define QAM_LC_COMM_MB_OBS_ON 0x2
+
+#define QAM_LC_COMM_INT_REQ__A 0x1450003
+#define QAM_LC_COMM_INT_REQ__W 1
+#define QAM_LC_COMM_INT_REQ__M 0x1
+#define QAM_LC_COMM_INT_REQ__PRE 0x0
+#define QAM_LC_COMM_INT_STA__A 0x1450005
+#define QAM_LC_COMM_INT_STA__W 3
+#define QAM_LC_COMM_INT_STA__M 0x7
+#define QAM_LC_COMM_INT_STA__PRE 0x0
+
+#define QAM_LC_COMM_INT_STA_READY__B 0
+#define QAM_LC_COMM_INT_STA_READY__W 1
+#define QAM_LC_COMM_INT_STA_READY__M 0x1
+#define QAM_LC_COMM_INT_STA_READY__PRE 0x0
+
+#define QAM_LC_COMM_INT_STA_OVERFLOW__B 1
+#define QAM_LC_COMM_INT_STA_OVERFLOW__W 1
+#define QAM_LC_COMM_INT_STA_OVERFLOW__M 0x2
+#define QAM_LC_COMM_INT_STA_OVERFLOW__PRE 0x0
+
+#define QAM_LC_COMM_INT_STA_FREQ_WRAP__B 2
+#define QAM_LC_COMM_INT_STA_FREQ_WRAP__W 1
+#define QAM_LC_COMM_INT_STA_FREQ_WRAP__M 0x4
+#define QAM_LC_COMM_INT_STA_FREQ_WRAP__PRE 0x0
+
+#define QAM_LC_COMM_INT_MSK__A 0x1450006
+#define QAM_LC_COMM_INT_MSK__W 3
+#define QAM_LC_COMM_INT_MSK__M 0x7
+#define QAM_LC_COMM_INT_MSK__PRE 0x0
+#define QAM_LC_COMM_INT_MSK_READY__B 0
+#define QAM_LC_COMM_INT_MSK_READY__W 1
+#define QAM_LC_COMM_INT_MSK_READY__M 0x1
+#define QAM_LC_COMM_INT_MSK_READY__PRE 0x0
+#define QAM_LC_COMM_INT_MSK_OVERFLOW__B 1
+#define QAM_LC_COMM_INT_MSK_OVERFLOW__W 1
+#define QAM_LC_COMM_INT_MSK_OVERFLOW__M 0x2
+#define QAM_LC_COMM_INT_MSK_OVERFLOW__PRE 0x0
+#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__B 2
+#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__W 1
+#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__M 0x4
+#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__PRE 0x0
+
+#define QAM_LC_COMM_INT_STM__A 0x1450007
+#define QAM_LC_COMM_INT_STM__W 3
+#define QAM_LC_COMM_INT_STM__M 0x7
+#define QAM_LC_COMM_INT_STM__PRE 0x0
+#define QAM_LC_COMM_INT_STM_READY__B 0
+#define QAM_LC_COMM_INT_STM_READY__W 1
+#define QAM_LC_COMM_INT_STM_READY__M 0x1
+#define QAM_LC_COMM_INT_STM_READY__PRE 0x0
+#define QAM_LC_COMM_INT_STM_OVERFLOW__B 1
+#define QAM_LC_COMM_INT_STM_OVERFLOW__W 1
+#define QAM_LC_COMM_INT_STM_OVERFLOW__M 0x2
+#define QAM_LC_COMM_INT_STM_OVERFLOW__PRE 0x0
+#define QAM_LC_COMM_INT_STM_FREQ_WRAP__B 2
+#define QAM_LC_COMM_INT_STM_FREQ_WRAP__W 1
+#define QAM_LC_COMM_INT_STM_FREQ_WRAP__M 0x4
+#define QAM_LC_COMM_INT_STM_FREQ_WRAP__PRE 0x0
+
+#define QAM_LC_MODE__A 0x1450010
+#define QAM_LC_MODE__W 3
+#define QAM_LC_MODE__M 0x7
+#define QAM_LC_MODE__PRE 0x7
+
+#define QAM_LC_MODE_ENABLE_A__B 0
+#define QAM_LC_MODE_ENABLE_A__W 1
+#define QAM_LC_MODE_ENABLE_A__M 0x1
+#define QAM_LC_MODE_ENABLE_A__PRE 0x1
+
+#define QAM_LC_MODE_ENABLE_F__B 1
+#define QAM_LC_MODE_ENABLE_F__W 1
+#define QAM_LC_MODE_ENABLE_F__M 0x2
+#define QAM_LC_MODE_ENABLE_F__PRE 0x2
+
+#define QAM_LC_MODE_ENABLE_R__B 2
+#define QAM_LC_MODE_ENABLE_R__W 1
+#define QAM_LC_MODE_ENABLE_R__M 0x4
+#define QAM_LC_MODE_ENABLE_R__PRE 0x4
+
+#define QAM_LC_CA__A 0x1450011
+#define QAM_LC_CA__W 6
+#define QAM_LC_CA__M 0x3F
+#define QAM_LC_CA__PRE 0x28
+
+#define QAM_LC_CA_COEF__B 0
+#define QAM_LC_CA_COEF__W 6
+#define QAM_LC_CA_COEF__M 0x3F
+#define QAM_LC_CA_COEF__PRE 0x28
+
+#define QAM_LC_CF__A 0x1450012
+#define QAM_LC_CF__W 8
+#define QAM_LC_CF__M 0xFF
+#define QAM_LC_CF__PRE 0x8C
+
+#define QAM_LC_CF_COEF__B 0
+#define QAM_LC_CF_COEF__W 8
+#define QAM_LC_CF_COEF__M 0xFF
+#define QAM_LC_CF_COEF__PRE 0x8C
+
+#define QAM_LC_CF1__A 0x1450013
+#define QAM_LC_CF1__W 8
+#define QAM_LC_CF1__M 0xFF
+#define QAM_LC_CF1__PRE 0x1E
+
+#define QAM_LC_CF1_COEF__B 0
+#define QAM_LC_CF1_COEF__W 8
+#define QAM_LC_CF1_COEF__M 0xFF
+#define QAM_LC_CF1_COEF__PRE 0x1E
+
+#define QAM_LC_CP__A 0x1450014
+#define QAM_LC_CP__W 8
+#define QAM_LC_CP__M 0xFF
+#define QAM_LC_CP__PRE 0x78
+
+#define QAM_LC_CP_COEF__B 0
+#define QAM_LC_CP_COEF__W 8
+#define QAM_LC_CP_COEF__M 0xFF
+#define QAM_LC_CP_COEF__PRE 0x78
+
+#define QAM_LC_CI__A 0x1450015
+#define QAM_LC_CI__W 8
+#define QAM_LC_CI__M 0xFF
+#define QAM_LC_CI__PRE 0x46
+
+#define QAM_LC_CI_COEF__B 0
+#define QAM_LC_CI_COEF__W 8
+#define QAM_LC_CI_COEF__M 0xFF
+#define QAM_LC_CI_COEF__PRE 0x46
+
+#define QAM_LC_EP__A 0x1450016
+#define QAM_LC_EP__W 6
+#define QAM_LC_EP__M 0x3F
+#define QAM_LC_EP__PRE 0x0
+
+#define QAM_LC_EP_COEF__B 0
+#define QAM_LC_EP_COEF__W 6
+#define QAM_LC_EP_COEF__M 0x3F
+#define QAM_LC_EP_COEF__PRE 0x0
+
+#define QAM_LC_EI__A 0x1450017
+#define QAM_LC_EI__W 6
+#define QAM_LC_EI__M 0x3F
+#define QAM_LC_EI__PRE 0x0
+
+#define QAM_LC_EI_COEF__B 0
+#define QAM_LC_EI_COEF__W 6
+#define QAM_LC_EI_COEF__M 0x3F
+#define QAM_LC_EI_COEF__PRE 0x0
+
+#define QAM_LC_QUAL_TAB0__A 0x1450018
+#define QAM_LC_QUAL_TAB0__W 5
+#define QAM_LC_QUAL_TAB0__M 0x1F
+#define QAM_LC_QUAL_TAB0__PRE 0x1
+
+#define QAM_LC_QUAL_TAB0_VALUE__B 0
+#define QAM_LC_QUAL_TAB0_VALUE__W 5
+#define QAM_LC_QUAL_TAB0_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB0_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB1__A 0x1450019
+#define QAM_LC_QUAL_TAB1__W 5
+#define QAM_LC_QUAL_TAB1__M 0x1F
+#define QAM_LC_QUAL_TAB1__PRE 0x1
+
+#define QAM_LC_QUAL_TAB1_VALUE__B 0
+#define QAM_LC_QUAL_TAB1_VALUE__W 5
+#define QAM_LC_QUAL_TAB1_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB1_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB2__A 0x145001A
+#define QAM_LC_QUAL_TAB2__W 5
+#define QAM_LC_QUAL_TAB2__M 0x1F
+#define QAM_LC_QUAL_TAB2__PRE 0x1
+
+#define QAM_LC_QUAL_TAB2_VALUE__B 0
+#define QAM_LC_QUAL_TAB2_VALUE__W 5
+#define QAM_LC_QUAL_TAB2_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB2_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB3__A 0x145001B
+#define QAM_LC_QUAL_TAB3__W 5
+#define QAM_LC_QUAL_TAB3__M 0x1F
+#define QAM_LC_QUAL_TAB3__PRE 0x1
+
+#define QAM_LC_QUAL_TAB3_VALUE__B 0
+#define QAM_LC_QUAL_TAB3_VALUE__W 5
+#define QAM_LC_QUAL_TAB3_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB3_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB4__A 0x145001C
+#define QAM_LC_QUAL_TAB4__W 5
+#define QAM_LC_QUAL_TAB4__M 0x1F
+#define QAM_LC_QUAL_TAB4__PRE 0x1
+
+#define QAM_LC_QUAL_TAB4_VALUE__B 0
+#define QAM_LC_QUAL_TAB4_VALUE__W 5
+#define QAM_LC_QUAL_TAB4_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB4_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB5__A 0x145001D
+#define QAM_LC_QUAL_TAB5__W 5
+#define QAM_LC_QUAL_TAB5__M 0x1F
+#define QAM_LC_QUAL_TAB5__PRE 0x1
+
+#define QAM_LC_QUAL_TAB5_VALUE__B 0
+#define QAM_LC_QUAL_TAB5_VALUE__W 5
+#define QAM_LC_QUAL_TAB5_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB5_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB6__A 0x145001E
+#define QAM_LC_QUAL_TAB6__W 5
+#define QAM_LC_QUAL_TAB6__M 0x1F
+#define QAM_LC_QUAL_TAB6__PRE 0x1
+
+#define QAM_LC_QUAL_TAB6_VALUE__B 0
+#define QAM_LC_QUAL_TAB6_VALUE__W 5
+#define QAM_LC_QUAL_TAB6_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB6_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB8__A 0x145001F
+#define QAM_LC_QUAL_TAB8__W 5
+#define QAM_LC_QUAL_TAB8__M 0x1F
+#define QAM_LC_QUAL_TAB8__PRE 0x1
+
+#define QAM_LC_QUAL_TAB8_VALUE__B 0
+#define QAM_LC_QUAL_TAB8_VALUE__W 5
+#define QAM_LC_QUAL_TAB8_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB8_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB9__A 0x1450020
+#define QAM_LC_QUAL_TAB9__W 5
+#define QAM_LC_QUAL_TAB9__M 0x1F
+#define QAM_LC_QUAL_TAB9__PRE 0x1
+
+#define QAM_LC_QUAL_TAB9_VALUE__B 0
+#define QAM_LC_QUAL_TAB9_VALUE__W 5
+#define QAM_LC_QUAL_TAB9_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB9_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB10__A 0x1450021
+#define QAM_LC_QUAL_TAB10__W 5
+#define QAM_LC_QUAL_TAB10__M 0x1F
+#define QAM_LC_QUAL_TAB10__PRE 0x1
+
+#define QAM_LC_QUAL_TAB10_VALUE__B 0
+#define QAM_LC_QUAL_TAB10_VALUE__W 5
+#define QAM_LC_QUAL_TAB10_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB10_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB12__A 0x1450022
+#define QAM_LC_QUAL_TAB12__W 5
+#define QAM_LC_QUAL_TAB12__M 0x1F
+#define QAM_LC_QUAL_TAB12__PRE 0x1
+
+#define QAM_LC_QUAL_TAB12_VALUE__B 0
+#define QAM_LC_QUAL_TAB12_VALUE__W 5
+#define QAM_LC_QUAL_TAB12_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB12_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB15__A 0x1450023
+#define QAM_LC_QUAL_TAB15__W 5
+#define QAM_LC_QUAL_TAB15__M 0x1F
+#define QAM_LC_QUAL_TAB15__PRE 0x1
+
+#define QAM_LC_QUAL_TAB15_VALUE__B 0
+#define QAM_LC_QUAL_TAB15_VALUE__W 5
+#define QAM_LC_QUAL_TAB15_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB15_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB16__A 0x1450024
+#define QAM_LC_QUAL_TAB16__W 5
+#define QAM_LC_QUAL_TAB16__M 0x1F
+#define QAM_LC_QUAL_TAB16__PRE 0x1
+
+#define QAM_LC_QUAL_TAB16_VALUE__B 0
+#define QAM_LC_QUAL_TAB16_VALUE__W 5
+#define QAM_LC_QUAL_TAB16_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB16_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB20__A 0x1450025
+#define QAM_LC_QUAL_TAB20__W 5
+#define QAM_LC_QUAL_TAB20__M 0x1F
+#define QAM_LC_QUAL_TAB20__PRE 0x1
+
+#define QAM_LC_QUAL_TAB20_VALUE__B 0
+#define QAM_LC_QUAL_TAB20_VALUE__W 5
+#define QAM_LC_QUAL_TAB20_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB20_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB25__A 0x1450026
+#define QAM_LC_QUAL_TAB25__W 5
+#define QAM_LC_QUAL_TAB25__M 0x1F
+#define QAM_LC_QUAL_TAB25__PRE 0x1
+
+#define QAM_LC_QUAL_TAB25_VALUE__B 0
+#define QAM_LC_QUAL_TAB25_VALUE__W 5
+#define QAM_LC_QUAL_TAB25_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB25_VALUE__PRE 0x1
+
+#define QAM_LC_EQ_TIMING__A 0x1450027
+#define QAM_LC_EQ_TIMING__W 10
+#define QAM_LC_EQ_TIMING__M 0x3FF
+#define QAM_LC_EQ_TIMING__PRE 0x0
+
+#define QAM_LC_EQ_TIMING_OFFS__B 0
+#define QAM_LC_EQ_TIMING_OFFS__W 10
+#define QAM_LC_EQ_TIMING_OFFS__M 0x3FF
+#define QAM_LC_EQ_TIMING_OFFS__PRE 0x0
+
+#define QAM_LC_LPF_FACTORP__A 0x1450028
+#define QAM_LC_LPF_FACTORP__W 3
+#define QAM_LC_LPF_FACTORP__M 0x7
+#define QAM_LC_LPF_FACTORP__PRE 0x3
+
+#define QAM_LC_LPF_FACTORP_FACTOR__B 0
+#define QAM_LC_LPF_FACTORP_FACTOR__W 3
+#define QAM_LC_LPF_FACTORP_FACTOR__M 0x7
+#define QAM_LC_LPF_FACTORP_FACTOR__PRE 0x3
+
+#define QAM_LC_LPF_FACTORI__A 0x1450029
+#define QAM_LC_LPF_FACTORI__W 3
+#define QAM_LC_LPF_FACTORI__M 0x7
+#define QAM_LC_LPF_FACTORI__PRE 0x3
+
+#define QAM_LC_LPF_FACTORI_FACTOR__B 0
+#define QAM_LC_LPF_FACTORI_FACTOR__W 3
+#define QAM_LC_LPF_FACTORI_FACTOR__M 0x7
+#define QAM_LC_LPF_FACTORI_FACTOR__PRE 0x3
+
+#define QAM_LC_RATE_LIMIT__A 0x145002A
+#define QAM_LC_RATE_LIMIT__W 2
+#define QAM_LC_RATE_LIMIT__M 0x3
+#define QAM_LC_RATE_LIMIT__PRE 0x3
+
+#define QAM_LC_RATE_LIMIT_LIMIT__B 0
+#define QAM_LC_RATE_LIMIT_LIMIT__W 2
+#define QAM_LC_RATE_LIMIT_LIMIT__M 0x3
+#define QAM_LC_RATE_LIMIT_LIMIT__PRE 0x3
+
+#define QAM_LC_SYMBOL_FREQ__A 0x145002B
+#define QAM_LC_SYMBOL_FREQ__W 10
+#define QAM_LC_SYMBOL_FREQ__M 0x3FF
+#define QAM_LC_SYMBOL_FREQ__PRE 0x199
+
+#define QAM_LC_SYMBOL_FREQ_FREQ__B 0
+#define QAM_LC_SYMBOL_FREQ_FREQ__W 10
+#define QAM_LC_SYMBOL_FREQ_FREQ__M 0x3FF
+#define QAM_LC_SYMBOL_FREQ_FREQ__PRE 0x199
+#define QAM_LC_SYMBOL_FREQ_FREQ_QAM_B_64 0x197
+#define QAM_LC_SYMBOL_FREQ_FREQ_QAM_B_256 0x1B2
+
+#define QAM_LC_MTA_LENGTH__A 0x145002C
+#define QAM_LC_MTA_LENGTH__W 2
+#define QAM_LC_MTA_LENGTH__M 0x3
+#define QAM_LC_MTA_LENGTH__PRE 0x2
+
+#define QAM_LC_MTA_LENGTH_LENGTH__B 0
+#define QAM_LC_MTA_LENGTH_LENGTH__W 2
+#define QAM_LC_MTA_LENGTH_LENGTH__M 0x3
+#define QAM_LC_MTA_LENGTH_LENGTH__PRE 0x2
+
+#define QAM_LC_AMP_ACCU__A 0x145002D
+#define QAM_LC_AMP_ACCU__W 14
+#define QAM_LC_AMP_ACCU__M 0x3FFF
+#define QAM_LC_AMP_ACCU__PRE 0x600
+
+#define QAM_LC_AMP_ACCU_ACCU__B 0
+#define QAM_LC_AMP_ACCU_ACCU__W 14
+#define QAM_LC_AMP_ACCU_ACCU__M 0x3FFF
+#define QAM_LC_AMP_ACCU_ACCU__PRE 0x600
+
+#define QAM_LC_FREQ_ACCU__A 0x145002E
+#define QAM_LC_FREQ_ACCU__W 10
+#define QAM_LC_FREQ_ACCU__M 0x3FF
+#define QAM_LC_FREQ_ACCU__PRE 0x0
+
+#define QAM_LC_FREQ_ACCU_ACCU__B 0
+#define QAM_LC_FREQ_ACCU_ACCU__W 10
+#define QAM_LC_FREQ_ACCU_ACCU__M 0x3FF
+#define QAM_LC_FREQ_ACCU_ACCU__PRE 0x0
+
+#define QAM_LC_RATE_ACCU__A 0x145002F
+#define QAM_LC_RATE_ACCU__W 10
+#define QAM_LC_RATE_ACCU__M 0x3FF
+#define QAM_LC_RATE_ACCU__PRE 0x0
+
+#define QAM_LC_RATE_ACCU_ACCU__B 0
+#define QAM_LC_RATE_ACCU_ACCU__W 10
+#define QAM_LC_RATE_ACCU_ACCU__M 0x3FF
+#define QAM_LC_RATE_ACCU_ACCU__PRE 0x0
+
+#define QAM_LC_AMPLITUDE__A 0x1450030
+#define QAM_LC_AMPLITUDE__W 10
+#define QAM_LC_AMPLITUDE__M 0x3FF
+#define QAM_LC_AMPLITUDE__PRE 0x0
+
+#define QAM_LC_AMPLITUDE_SIZE__B 0
+#define QAM_LC_AMPLITUDE_SIZE__W 10
+#define QAM_LC_AMPLITUDE_SIZE__M 0x3FF
+#define QAM_LC_AMPLITUDE_SIZE__PRE 0x0
+
+#define QAM_LC_RAD_ERROR__A 0x1450031
+#define QAM_LC_RAD_ERROR__W 10
+#define QAM_LC_RAD_ERROR__M 0x3FF
+#define QAM_LC_RAD_ERROR__PRE 0x0
+
+#define QAM_LC_RAD_ERROR_SIZE__B 0
+#define QAM_LC_RAD_ERROR_SIZE__W 10
+#define QAM_LC_RAD_ERROR_SIZE__M 0x3FF
+#define QAM_LC_RAD_ERROR_SIZE__PRE 0x0
+
+#define QAM_LC_FREQ_OFFS__A 0x1450032
+#define QAM_LC_FREQ_OFFS__W 10
+#define QAM_LC_FREQ_OFFS__M 0x3FF
+#define QAM_LC_FREQ_OFFS__PRE 0x0
+
+#define QAM_LC_FREQ_OFFS_OFFS__B 0
+#define QAM_LC_FREQ_OFFS_OFFS__W 10
+#define QAM_LC_FREQ_OFFS_OFFS__M 0x3FF
+#define QAM_LC_FREQ_OFFS_OFFS__PRE 0x0
+
+#define QAM_LC_PHASE_ERROR__A 0x1450033
+#define QAM_LC_PHASE_ERROR__W 10
+#define QAM_LC_PHASE_ERROR__M 0x3FF
+#define QAM_LC_PHASE_ERROR__PRE 0x0
+
+#define QAM_LC_PHASE_ERROR_SIZE__B 0
+#define QAM_LC_PHASE_ERROR_SIZE__W 10
+#define QAM_LC_PHASE_ERROR_SIZE__M 0x3FF
+#define QAM_LC_PHASE_ERROR_SIZE__PRE 0x0
+
+#define QAM_VD_COMM_EXEC__A 0x1460000
+#define QAM_VD_COMM_EXEC__W 2
+#define QAM_VD_COMM_EXEC__M 0x3
+#define QAM_VD_COMM_EXEC__PRE 0x0
+#define QAM_VD_COMM_EXEC_STOP 0x0
+#define QAM_VD_COMM_EXEC_ACTIVE 0x1
+#define QAM_VD_COMM_EXEC_HOLD 0x2
+
+#define QAM_VD_COMM_MB__A 0x1460002
+#define QAM_VD_COMM_MB__W 2
+#define QAM_VD_COMM_MB__M 0x3
+#define QAM_VD_COMM_MB__PRE 0x0
+#define QAM_VD_COMM_MB_CTL__B 0
+#define QAM_VD_COMM_MB_CTL__W 1
+#define QAM_VD_COMM_MB_CTL__M 0x1
+#define QAM_VD_COMM_MB_CTL__PRE 0x0
+#define QAM_VD_COMM_MB_CTL_OFF 0x0
+#define QAM_VD_COMM_MB_CTL_ON 0x1
+#define QAM_VD_COMM_MB_OBS__B 1
+#define QAM_VD_COMM_MB_OBS__W 1
+#define QAM_VD_COMM_MB_OBS__M 0x2
+#define QAM_VD_COMM_MB_OBS__PRE 0x0
+#define QAM_VD_COMM_MB_OBS_OFF 0x0
+#define QAM_VD_COMM_MB_OBS_ON 0x2
+
+#define QAM_VD_COMM_INT_REQ__A 0x1460003
+#define QAM_VD_COMM_INT_REQ__W 1
+#define QAM_VD_COMM_INT_REQ__M 0x1
+#define QAM_VD_COMM_INT_REQ__PRE 0x0
+#define QAM_VD_COMM_INT_STA__A 0x1460005
+#define QAM_VD_COMM_INT_STA__W 2
+#define QAM_VD_COMM_INT_STA__M 0x3
+#define QAM_VD_COMM_INT_STA__PRE 0x0
+
+#define QAM_VD_COMM_INT_STA_LOCK_INT__B 0
+#define QAM_VD_COMM_INT_STA_LOCK_INT__W 1
+#define QAM_VD_COMM_INT_STA_LOCK_INT__M 0x1
+#define QAM_VD_COMM_INT_STA_LOCK_INT__PRE 0x0
+
+#define QAM_VD_COMM_INT_STA_PERIOD_INT__B 1
+#define QAM_VD_COMM_INT_STA_PERIOD_INT__W 1
+#define QAM_VD_COMM_INT_STA_PERIOD_INT__M 0x2
+#define QAM_VD_COMM_INT_STA_PERIOD_INT__PRE 0x0
+
+#define QAM_VD_COMM_INT_MSK__A 0x1460006
+#define QAM_VD_COMM_INT_MSK__W 2
+#define QAM_VD_COMM_INT_MSK__M 0x3
+#define QAM_VD_COMM_INT_MSK__PRE 0x0
+#define QAM_VD_COMM_INT_MSK_LOCK_INT__B 0
+#define QAM_VD_COMM_INT_MSK_LOCK_INT__W 1
+#define QAM_VD_COMM_INT_MSK_LOCK_INT__M 0x1
+#define QAM_VD_COMM_INT_MSK_LOCK_INT__PRE 0x0
+#define QAM_VD_COMM_INT_MSK_PERIOD_INT__B 1
+#define QAM_VD_COMM_INT_MSK_PERIOD_INT__W 1
+#define QAM_VD_COMM_INT_MSK_PERIOD_INT__M 0x2
+#define QAM_VD_COMM_INT_MSK_PERIOD_INT__PRE 0x0
+
+#define QAM_VD_COMM_INT_STM__A 0x1460007
+#define QAM_VD_COMM_INT_STM__W 2
+#define QAM_VD_COMM_INT_STM__M 0x3
+#define QAM_VD_COMM_INT_STM__PRE 0x0
+#define QAM_VD_COMM_INT_STM_LOCK_INT__B 0
+#define QAM_VD_COMM_INT_STM_LOCK_INT__W 1
+#define QAM_VD_COMM_INT_STM_LOCK_INT__M 0x1
+#define QAM_VD_COMM_INT_STM_LOCK_INT__PRE 0x0
+#define QAM_VD_COMM_INT_STM_PERIOD_INT__B 1
+#define QAM_VD_COMM_INT_STM_PERIOD_INT__W 1
+#define QAM_VD_COMM_INT_STM_PERIOD_INT__M 0x2
+#define QAM_VD_COMM_INT_STM_PERIOD_INT__PRE 0x0
+
+#define QAM_VD_STATUS__A 0x1460010
+#define QAM_VD_STATUS__W 1
+#define QAM_VD_STATUS__M 0x1
+#define QAM_VD_STATUS__PRE 0x0
+
+#define QAM_VD_STATUS_LOCK__B 0
+#define QAM_VD_STATUS_LOCK__W 1
+#define QAM_VD_STATUS_LOCK__M 0x1
+#define QAM_VD_STATUS_LOCK__PRE 0x0
+
+#define QAM_VD_UNLOCK_CONTROL__A 0x1460011
+#define QAM_VD_UNLOCK_CONTROL__W 1
+#define QAM_VD_UNLOCK_CONTROL__M 0x1
+#define QAM_VD_UNLOCK_CONTROL__PRE 0x0
+
+#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__B 0
+#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__W 1
+#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__M 0x1
+#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__PRE 0x0
+
+#define QAM_VD_MIN_VOTING_ROUNDS__A 0x1460012
+#define QAM_VD_MIN_VOTING_ROUNDS__W 6
+#define QAM_VD_MIN_VOTING_ROUNDS__M 0x3F
+#define QAM_VD_MIN_VOTING_ROUNDS__PRE 0x10
+
+#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__B 0
+#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__W 6
+#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__M 0x3F
+#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__PRE 0x10
+
+#define QAM_VD_MAX_VOTING_ROUNDS__A 0x1460013
+#define QAM_VD_MAX_VOTING_ROUNDS__W 6
+#define QAM_VD_MAX_VOTING_ROUNDS__M 0x3F
+#define QAM_VD_MAX_VOTING_ROUNDS__PRE 0x10
+
+#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__B 0
+#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__W 6
+#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__M 0x3F
+#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__PRE 0x10
+
+#define QAM_VD_TRACEBACK_DEPTH__A 0x1460014
+#define QAM_VD_TRACEBACK_DEPTH__W 5
+#define QAM_VD_TRACEBACK_DEPTH__M 0x1F
+#define QAM_VD_TRACEBACK_DEPTH__PRE 0x10
+
+#define QAM_VD_TRACEBACK_DEPTH_LENGTH__B 0
+#define QAM_VD_TRACEBACK_DEPTH_LENGTH__W 5
+#define QAM_VD_TRACEBACK_DEPTH_LENGTH__M 0x1F
+#define QAM_VD_TRACEBACK_DEPTH_LENGTH__PRE 0x10
+
+#define QAM_VD_UNLOCK__A 0x1460015
+#define QAM_VD_UNLOCK__W 1
+#define QAM_VD_UNLOCK__M 0x1
+#define QAM_VD_UNLOCK__PRE 0x0
+#define QAM_VD_MEASUREMENT_PERIOD__A 0x1460016
+#define QAM_VD_MEASUREMENT_PERIOD__W 16
+#define QAM_VD_MEASUREMENT_PERIOD__M 0xFFFF
+#define QAM_VD_MEASUREMENT_PERIOD__PRE 0x8236
+
+#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__B 0
+#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__W 16
+#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__M 0xFFFF
+#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__PRE 0x8236
+
+#define QAM_VD_MEASUREMENT_PRESCALE__A 0x1460017
+#define QAM_VD_MEASUREMENT_PRESCALE__W 16
+#define QAM_VD_MEASUREMENT_PRESCALE__M 0xFFFF
+#define QAM_VD_MEASUREMENT_PRESCALE__PRE 0x4
+
+#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__B 0
+#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__W 16
+#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__M 0xFFFF
+#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__PRE 0x4
+
+#define QAM_VD_DELTA_PATH_METRIC__A 0x1460018
+#define QAM_VD_DELTA_PATH_METRIC__W 16
+#define QAM_VD_DELTA_PATH_METRIC__M 0xFFFF
+#define QAM_VD_DELTA_PATH_METRIC__PRE 0xFFFF
+
+#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__B 0
+#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__W 12
+#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__M 0xFFF
+#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__PRE 0xFFF
+
+#define QAM_VD_DELTA_PATH_METRIC_EXP__B 12
+#define QAM_VD_DELTA_PATH_METRIC_EXP__W 4
+#define QAM_VD_DELTA_PATH_METRIC_EXP__M 0xF000
+#define QAM_VD_DELTA_PATH_METRIC_EXP__PRE 0xF000
+
+#define QAM_VD_NR_QSYM_ERRORS__A 0x1460019
+#define QAM_VD_NR_QSYM_ERRORS__W 16
+#define QAM_VD_NR_QSYM_ERRORS__M 0xFFFF
+#define QAM_VD_NR_QSYM_ERRORS__PRE 0xFFFF
+
+#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__B 0
+#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__W 12
+#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__M 0xFFF
+#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__PRE 0xFFF
+
+#define QAM_VD_NR_QSYM_ERRORS_EXP__B 12
+#define QAM_VD_NR_QSYM_ERRORS_EXP__W 4
+#define QAM_VD_NR_QSYM_ERRORS_EXP__M 0xF000
+#define QAM_VD_NR_QSYM_ERRORS_EXP__PRE 0xF000
+
+#define QAM_VD_NR_SYMBOL_ERRORS__A 0x146001A
+#define QAM_VD_NR_SYMBOL_ERRORS__W 16
+#define QAM_VD_NR_SYMBOL_ERRORS__M 0xFFFF
+#define QAM_VD_NR_SYMBOL_ERRORS__PRE 0xFFFF
+
+#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__B 0
+#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__W 12
+#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__M 0xFFF
+#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__PRE 0xFFF
+
+#define QAM_VD_NR_SYMBOL_ERRORS_EXP__B 12
+#define QAM_VD_NR_SYMBOL_ERRORS_EXP__W 4
+#define QAM_VD_NR_SYMBOL_ERRORS_EXP__M 0xF000
+#define QAM_VD_NR_SYMBOL_ERRORS_EXP__PRE 0xF000
+
+#define QAM_VD_RELOCK_COUNT__A 0x146001B
+#define QAM_VD_RELOCK_COUNT__W 16
+#define QAM_VD_RELOCK_COUNT__M 0xFFFF
+#define QAM_VD_RELOCK_COUNT__PRE 0x0
+
+#define QAM_VD_RELOCK_COUNT_COUNT__B 0
+#define QAM_VD_RELOCK_COUNT_COUNT__W 8
+#define QAM_VD_RELOCK_COUNT_COUNT__M 0xFF
+#define QAM_VD_RELOCK_COUNT_COUNT__PRE 0x0
+
+#define QAM_SY_COMM_EXEC__A 0x1470000
+#define QAM_SY_COMM_EXEC__W 2
+#define QAM_SY_COMM_EXEC__M 0x3
+#define QAM_SY_COMM_EXEC__PRE 0x0
+#define QAM_SY_COMM_EXEC_STOP 0x0
+#define QAM_SY_COMM_EXEC_ACTIVE 0x1
+#define QAM_SY_COMM_EXEC_HOLD 0x2
+
+#define QAM_SY_COMM_MB__A 0x1470002
+#define QAM_SY_COMM_MB__W 2
+#define QAM_SY_COMM_MB__M 0x3
+#define QAM_SY_COMM_MB__PRE 0x0
+#define QAM_SY_COMM_MB_CTL__B 0
+#define QAM_SY_COMM_MB_CTL__W 1
+#define QAM_SY_COMM_MB_CTL__M 0x1
+#define QAM_SY_COMM_MB_CTL__PRE 0x0
+#define QAM_SY_COMM_MB_CTL_OFF 0x0
+#define QAM_SY_COMM_MB_CTL_ON 0x1
+#define QAM_SY_COMM_MB_OBS__B 1
+#define QAM_SY_COMM_MB_OBS__W 1
+#define QAM_SY_COMM_MB_OBS__M 0x2
+#define QAM_SY_COMM_MB_OBS__PRE 0x0
+#define QAM_SY_COMM_MB_OBS_OFF 0x0
+#define QAM_SY_COMM_MB_OBS_ON 0x2
+
+#define QAM_SY_COMM_INT_REQ__A 0x1470003
+#define QAM_SY_COMM_INT_REQ__W 1
+#define QAM_SY_COMM_INT_REQ__M 0x1
+#define QAM_SY_COMM_INT_REQ__PRE 0x0
+#define QAM_SY_COMM_INT_STA__A 0x1470005
+#define QAM_SY_COMM_INT_STA__W 4
+#define QAM_SY_COMM_INT_STA__M 0xF
+#define QAM_SY_COMM_INT_STA__PRE 0x0
+
+#define QAM_SY_COMM_INT_STA_LOCK_INT__B 0
+#define QAM_SY_COMM_INT_STA_LOCK_INT__W 1
+#define QAM_SY_COMM_INT_STA_LOCK_INT__M 0x1
+#define QAM_SY_COMM_INT_STA_LOCK_INT__PRE 0x0
+
+#define QAM_SY_COMM_INT_STA_UNLOCK_INT__B 1
+#define QAM_SY_COMM_INT_STA_UNLOCK_INT__W 1
+#define QAM_SY_COMM_INT_STA_UNLOCK_INT__M 0x2
+#define QAM_SY_COMM_INT_STA_UNLOCK_INT__PRE 0x0
+
+#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__B 2
+#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__W 1
+#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__M 0x4
+#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__PRE 0x0
+
+#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__B 3
+#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__W 1
+#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__M 0x8
+#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__PRE 0x0
+
+#define QAM_SY_COMM_INT_MSK__A 0x1470006
+#define QAM_SY_COMM_INT_MSK__W 4
+#define QAM_SY_COMM_INT_MSK__M 0xF
+#define QAM_SY_COMM_INT_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_MSK_LOCK_MSK__B 0
+#define QAM_SY_COMM_INT_MSK_LOCK_MSK__W 1
+#define QAM_SY_COMM_INT_MSK_LOCK_MSK__M 0x1
+#define QAM_SY_COMM_INT_MSK_LOCK_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__B 1
+#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__W 1
+#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__M 0x2
+#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__B 2
+#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__W 1
+#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__M 0x4
+#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__B 3
+#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__W 1
+#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__M 0x8
+#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__PRE 0x0
+
+#define QAM_SY_COMM_INT_STM__A 0x1470007
+#define QAM_SY_COMM_INT_STM__W 4
+#define QAM_SY_COMM_INT_STM__M 0xF
+#define QAM_SY_COMM_INT_STM__PRE 0x0
+#define QAM_SY_COMM_INT_STM_LOCK_MSK__B 0
+#define QAM_SY_COMM_INT_STM_LOCK_MSK__W 1
+#define QAM_SY_COMM_INT_STM_LOCK_MSK__M 0x1
+#define QAM_SY_COMM_INT_STM_LOCK_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__B 1
+#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__W 1
+#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__M 0x2
+#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__B 2
+#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__W 1
+#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__M 0x4
+#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__B 3
+#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__W 1
+#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__M 0x8
+#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__PRE 0x0
+
+#define QAM_SY_STATUS__A 0x1470010
+#define QAM_SY_STATUS__W 2
+#define QAM_SY_STATUS__M 0x3
+#define QAM_SY_STATUS__PRE 0x0
+
+#define QAM_SY_STATUS_SYNC_STATE__B 0
+#define QAM_SY_STATUS_SYNC_STATE__W 2
+#define QAM_SY_STATUS_SYNC_STATE__M 0x3
+#define QAM_SY_STATUS_SYNC_STATE__PRE 0x0
+
+#define QAM_SY_TIMEOUT__A 0x1470011
+#define QAM_SY_TIMEOUT__W 16
+#define QAM_SY_TIMEOUT__M 0xFFFF
+#define QAM_SY_TIMEOUT__PRE 0x3A98
+
+#define QAM_SY_SYNC_LWM__A 0x1470012
+#define QAM_SY_SYNC_LWM__W 4
+#define QAM_SY_SYNC_LWM__M 0xF
+#define QAM_SY_SYNC_LWM__PRE 0x2
+
+#define QAM_SY_SYNC_AWM__A 0x1470013
+#define QAM_SY_SYNC_AWM__W 4
+#define QAM_SY_SYNC_AWM__M 0xF
+#define QAM_SY_SYNC_AWM__PRE 0x3
+
+#define QAM_SY_SYNC_HWM__A 0x1470014
+#define QAM_SY_SYNC_HWM__W 4
+#define QAM_SY_SYNC_HWM__M 0xF
+#define QAM_SY_SYNC_HWM__PRE 0x5
+
+#define QAM_SY_UNLOCK__A 0x1470015
+#define QAM_SY_UNLOCK__W 1
+#define QAM_SY_UNLOCK__M 0x1
+#define QAM_SY_UNLOCK__PRE 0x0
+#define QAM_SY_CONTROL_WORD__A 0x1470016
+#define QAM_SY_CONTROL_WORD__W 4
+#define QAM_SY_CONTROL_WORD__M 0xF
+#define QAM_SY_CONTROL_WORD__PRE 0x0
+
+#define QAM_SY_CONTROL_WORD_CTRL_WORD__B 0
+#define QAM_SY_CONTROL_WORD_CTRL_WORD__W 4
+#define QAM_SY_CONTROL_WORD_CTRL_WORD__M 0xF
+#define QAM_SY_CONTROL_WORD_CTRL_WORD__PRE 0x0
+
+#define QAM_VD_ISS_RAM__A 0x1480000
+
+#define QAM_VD_QSS_RAM__A 0x1490000
+
+#define QAM_VD_SYM_RAM__A 0x14A0000
+
+#define SCU_COMM_EXEC__A 0x800000
+#define SCU_COMM_EXEC__W 2
+#define SCU_COMM_EXEC__M 0x3
+#define SCU_COMM_EXEC__PRE 0x0
+#define SCU_COMM_EXEC_STOP 0x0
+#define SCU_COMM_EXEC_ACTIVE 0x1
+#define SCU_COMM_EXEC_HOLD 0x2
+
+#define SCU_COMM_STATE__A 0x800001
+#define SCU_COMM_STATE__W 16
+#define SCU_COMM_STATE__M 0xFFFF
+#define SCU_COMM_STATE__PRE 0x0
+
+#define SCU_COMM_STATE_COMM_STATE__B 0
+#define SCU_COMM_STATE_COMM_STATE__W 16
+#define SCU_COMM_STATE_COMM_STATE__M 0xFFFF
+#define SCU_COMM_STATE_COMM_STATE__PRE 0x0
+
+#define SCU_TOP_COMM_EXEC__A 0x810000
+#define SCU_TOP_COMM_EXEC__W 2
+#define SCU_TOP_COMM_EXEC__M 0x3
+#define SCU_TOP_COMM_EXEC__PRE 0x0
+#define SCU_TOP_COMM_EXEC_STOP 0x0
+#define SCU_TOP_COMM_EXEC_ACTIVE 0x1
+#define SCU_TOP_COMM_EXEC_HOLD 0x2
+
+#define SCU_TOP_COMM_STATE__A 0x810001
+#define SCU_TOP_COMM_STATE__W 16
+#define SCU_TOP_COMM_STATE__M 0xFFFF
+#define SCU_TOP_COMM_STATE__PRE 0x0
+#define SCU_TOP_MWAIT_CTR__A 0x810010
+#define SCU_TOP_MWAIT_CTR__W 2
+#define SCU_TOP_MWAIT_CTR__M 0x3
+#define SCU_TOP_MWAIT_CTR__PRE 0x0
+
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__B 0
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__W 1
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__M 0x1
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__PRE 0x0
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL_TR_MW_OFF 0x0
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL_TR_MW_ON 0x1
+
+#define SCU_TOP_MWAIT_CTR_READY_DIS__B 1
+#define SCU_TOP_MWAIT_CTR_READY_DIS__W 1
+#define SCU_TOP_MWAIT_CTR_READY_DIS__M 0x2
+#define SCU_TOP_MWAIT_CTR_READY_DIS__PRE 0x0
+#define SCU_TOP_MWAIT_CTR_READY_DIS_NMI_ON 0x0
+#define SCU_TOP_MWAIT_CTR_READY_DIS_NMI_OFF 0x2
+
+#define SCU_LOW_RAM__A 0x820000
+
+#define SCU_LOW_RAM_LOW__B 0
+#define SCU_LOW_RAM_LOW__W 16
+#define SCU_LOW_RAM_LOW__M 0xFFFF
+#define SCU_LOW_RAM_LOW__PRE 0x0
+
+#define SCU_HIGH_RAM__A 0x830000
+
+#define SCU_HIGH_RAM_HIGH__B 0
+#define SCU_HIGH_RAM_HIGH__W 16
+#define SCU_HIGH_RAM_HIGH__M 0xFFFF
+#define SCU_HIGH_RAM_HIGH__PRE 0x0
+
+#define SCU_RAM_AGC_RF_MAX__A 0x831E96
+#define SCU_RAM_AGC_RF_MAX__W 15
+#define SCU_RAM_AGC_RF_MAX__M 0x7FFF
+#define SCU_RAM_AGC_RF_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A 0x831E97
+#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__W 16
+#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__M 0xFFFF
+#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__PRE 0x0
+
+#define SCU_RAM_AGC_KI_CYCCNT__A 0x831E98
+#define SCU_RAM_AGC_KI_CYCCNT__W 16
+#define SCU_RAM_AGC_KI_CYCCNT__M 0xFFFF
+#define SCU_RAM_AGC_KI_CYCCNT__PRE 0x0
+
+#define SCU_RAM_AGC_KI_CYCLEN__A 0x831E99
+#define SCU_RAM_AGC_KI_CYCLEN__W 16
+#define SCU_RAM_AGC_KI_CYCLEN__M 0xFFFF
+#define SCU_RAM_AGC_KI_CYCLEN__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_CYCLEN__A 0x831E9A
+#define SCU_RAM_AGC_SNS_CYCLEN__W 16
+#define SCU_RAM_AGC_SNS_CYCLEN__M 0xFFFF
+#define SCU_RAM_AGC_SNS_CYCLEN__PRE 0x0
+
+#define SCU_RAM_AGC_RF_SNS_DEV_MAX__A 0x831E9B
+#define SCU_RAM_AGC_RF_SNS_DEV_MAX__W 16
+#define SCU_RAM_AGC_RF_SNS_DEV_MAX__M 0xFFFF
+#define SCU_RAM_AGC_RF_SNS_DEV_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_RF_SNS_DEV_MIN__A 0x831E9C
+#define SCU_RAM_AGC_RF_SNS_DEV_MIN__W 16
+#define SCU_RAM_AGC_RF_SNS_DEV_MIN__M 0xFFFF
+#define SCU_RAM_AGC_RF_SNS_DEV_MIN__PRE 0x0
+#define SCU_RAM_AGC_KI__A 0x831E9D
+#define SCU_RAM_AGC_KI__W 15
+#define SCU_RAM_AGC_KI__M 0x7FFF
+#define SCU_RAM_AGC_KI__PRE 0x0
+
+#define SCU_RAM_AGC_KI_DGAIN__B 0
+#define SCU_RAM_AGC_KI_DGAIN__W 4
+#define SCU_RAM_AGC_KI_DGAIN__M 0xF
+#define SCU_RAM_AGC_KI_DGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_RF__B 4
+#define SCU_RAM_AGC_KI_RF__W 4
+#define SCU_RAM_AGC_KI_RF__M 0xF0
+#define SCU_RAM_AGC_KI_RF__PRE 0x0
+
+#define SCU_RAM_AGC_KI_IF__B 8
+#define SCU_RAM_AGC_KI_IF__W 4
+#define SCU_RAM_AGC_KI_IF__M 0xF00
+#define SCU_RAM_AGC_KI_IF__PRE 0x0
+
+#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__B 12
+#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__W 1
+#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__M 0x1000
+#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__PRE 0x0
+
+#define SCU_RAM_AGC_KI_INV_IF_POL__B 13
+#define SCU_RAM_AGC_KI_INV_IF_POL__W 1
+#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000
+#define SCU_RAM_AGC_KI_INV_IF_POL__PRE 0x0
+
+#define SCU_RAM_AGC_KI_INV_RF_POL__B 14
+#define SCU_RAM_AGC_KI_INV_RF_POL__W 1
+#define SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000
+#define SCU_RAM_AGC_KI_INV_RF_POL__PRE 0x0
+
+#define SCU_RAM_AGC_KI_RED__A 0x831E9E
+#define SCU_RAM_AGC_KI_RED__W 6
+#define SCU_RAM_AGC_KI_RED__M 0x3F
+#define SCU_RAM_AGC_KI_RED__PRE 0x0
+
+#define SCU_RAM_AGC_KI_RED_INNER_RED__B 0
+#define SCU_RAM_AGC_KI_RED_INNER_RED__W 2
+#define SCU_RAM_AGC_KI_RED_INNER_RED__M 0x3
+#define SCU_RAM_AGC_KI_RED_INNER_RED__PRE 0x0
+
+#define SCU_RAM_AGC_KI_RED_RAGC_RED__B 2
+#define SCU_RAM_AGC_KI_RED_RAGC_RED__W 2
+#define SCU_RAM_AGC_KI_RED_RAGC_RED__M 0xC
+#define SCU_RAM_AGC_KI_RED_RAGC_RED__PRE 0x0
+
+#define SCU_RAM_AGC_KI_RED_IAGC_RED__B 4
+#define SCU_RAM_AGC_KI_RED_IAGC_RED__W 2
+#define SCU_RAM_AGC_KI_RED_IAGC_RED__M 0x30
+#define SCU_RAM_AGC_KI_RED_IAGC_RED__PRE 0x0
+
+#define SCU_RAM_AGC_KI_INNERGAIN_MIN__A 0x831E9F
+#define SCU_RAM_AGC_KI_INNERGAIN_MIN__W 16
+#define SCU_RAM_AGC_KI_INNERGAIN_MIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_INNERGAIN_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MINGAIN__A 0x831EA0
+#define SCU_RAM_AGC_KI_MINGAIN__W 16
+#define SCU_RAM_AGC_KI_MINGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MINGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAXGAIN__A 0x831EA1
+#define SCU_RAM_AGC_KI_MAXGAIN__W 16
+#define SCU_RAM_AGC_KI_MAXGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MAXGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__A 0x831EA2
+#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__W 16
+#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__M 0xFFFF
+#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__PRE 0x0
+#define SCU_RAM_AGC_KI_MIN__A 0x831EA3
+#define SCU_RAM_AGC_KI_MIN__W 12
+#define SCU_RAM_AGC_KI_MIN__M 0xFFF
+#define SCU_RAM_AGC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MIN_DGAIN__B 0
+#define SCU_RAM_AGC_KI_MIN_DGAIN__W 4
+#define SCU_RAM_AGC_KI_MIN_DGAIN__M 0xF
+#define SCU_RAM_AGC_KI_MIN_DGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MIN_RF__B 4
+#define SCU_RAM_AGC_KI_MIN_RF__W 4
+#define SCU_RAM_AGC_KI_MIN_RF__M 0xF0
+#define SCU_RAM_AGC_KI_MIN_RF__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MIN_IF__B 8
+#define SCU_RAM_AGC_KI_MIN_IF__W 4
+#define SCU_RAM_AGC_KI_MIN_IF__M 0xF00
+#define SCU_RAM_AGC_KI_MIN_IF__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX__A 0x831EA4
+#define SCU_RAM_AGC_KI_MAX__W 12
+#define SCU_RAM_AGC_KI_MAX__M 0xFFF
+#define SCU_RAM_AGC_KI_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX_DGAIN__B 0
+#define SCU_RAM_AGC_KI_MAX_DGAIN__W 4
+#define SCU_RAM_AGC_KI_MAX_DGAIN__M 0xF
+#define SCU_RAM_AGC_KI_MAX_DGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX_RF__B 4
+#define SCU_RAM_AGC_KI_MAX_RF__W 4
+#define SCU_RAM_AGC_KI_MAX_RF__M 0xF0
+#define SCU_RAM_AGC_KI_MAX_RF__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX_IF__B 8
+#define SCU_RAM_AGC_KI_MAX_IF__W 4
+#define SCU_RAM_AGC_KI_MAX_IF__M 0xF00
+#define SCU_RAM_AGC_KI_MAX_IF__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_SUM__A 0x831EA5
+#define SCU_RAM_AGC_CLP_SUM__W 16
+#define SCU_RAM_AGC_CLP_SUM__M 0xFFFF
+#define SCU_RAM_AGC_CLP_SUM__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_SUM_MIN__A 0x831EA6
+#define SCU_RAM_AGC_CLP_SUM_MIN__W 16
+#define SCU_RAM_AGC_CLP_SUM_MIN__M 0xFFFF
+#define SCU_RAM_AGC_CLP_SUM_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_SUM_MAX__A 0x831EA7
+#define SCU_RAM_AGC_CLP_SUM_MAX__W 16
+#define SCU_RAM_AGC_CLP_SUM_MAX__M 0xFFFF
+#define SCU_RAM_AGC_CLP_SUM_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_CYCLEN__A 0x831EA8
+#define SCU_RAM_AGC_CLP_CYCLEN__W 16
+#define SCU_RAM_AGC_CLP_CYCLEN__M 0xFFFF
+#define SCU_RAM_AGC_CLP_CYCLEN__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_CYCCNT__A 0x831EA9
+#define SCU_RAM_AGC_CLP_CYCCNT__W 16
+#define SCU_RAM_AGC_CLP_CYCCNT__M 0xFFFF
+#define SCU_RAM_AGC_CLP_CYCCNT__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_DIR_TO__A 0x831EAA
+#define SCU_RAM_AGC_CLP_DIR_TO__W 8
+#define SCU_RAM_AGC_CLP_DIR_TO__M 0xFF
+#define SCU_RAM_AGC_CLP_DIR_TO__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_DIR_WD__A 0x831EAB
+#define SCU_RAM_AGC_CLP_DIR_WD__W 8
+#define SCU_RAM_AGC_CLP_DIR_WD__M 0xFF
+#define SCU_RAM_AGC_CLP_DIR_WD__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_DIR_STP__A 0x831EAC
+#define SCU_RAM_AGC_CLP_DIR_STP__W 16
+#define SCU_RAM_AGC_CLP_DIR_STP__M 0xFFFF
+#define SCU_RAM_AGC_CLP_DIR_STP__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_SUM__A 0x831EAD
+#define SCU_RAM_AGC_SNS_SUM__W 16
+#define SCU_RAM_AGC_SNS_SUM__M 0xFFFF
+#define SCU_RAM_AGC_SNS_SUM__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_SUM_MIN__A 0x831EAE
+#define SCU_RAM_AGC_SNS_SUM_MIN__W 16
+#define SCU_RAM_AGC_SNS_SUM_MIN__M 0xFFFF
+#define SCU_RAM_AGC_SNS_SUM_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_SUM_MAX__A 0x831EAF
+#define SCU_RAM_AGC_SNS_SUM_MAX__W 16
+#define SCU_RAM_AGC_SNS_SUM_MAX__M 0xFFFF
+#define SCU_RAM_AGC_SNS_SUM_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_CYCCNT__A 0x831EB0
+#define SCU_RAM_AGC_SNS_CYCCNT__W 16
+#define SCU_RAM_AGC_SNS_CYCCNT__M 0xFFFF
+#define SCU_RAM_AGC_SNS_CYCCNT__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_DIR_TO__A 0x831EB1
+#define SCU_RAM_AGC_SNS_DIR_TO__W 8
+#define SCU_RAM_AGC_SNS_DIR_TO__M 0xFF
+#define SCU_RAM_AGC_SNS_DIR_TO__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_DIR_WD__A 0x831EB2
+#define SCU_RAM_AGC_SNS_DIR_WD__W 8
+#define SCU_RAM_AGC_SNS_DIR_WD__M 0xFF
+#define SCU_RAM_AGC_SNS_DIR_WD__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_DIR_STP__A 0x831EB3
+#define SCU_RAM_AGC_SNS_DIR_STP__W 16
+#define SCU_RAM_AGC_SNS_DIR_STP__M 0xFFFF
+#define SCU_RAM_AGC_SNS_DIR_STP__PRE 0x0
+
+#define SCU_RAM_AGC_INGAIN__A 0x831EB4
+#define SCU_RAM_AGC_INGAIN__W 16
+#define SCU_RAM_AGC_INGAIN__M 0xFFFF
+#define SCU_RAM_AGC_INGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_INGAIN_TGT__A 0x831EB5
+#define SCU_RAM_AGC_INGAIN_TGT__W 15
+#define SCU_RAM_AGC_INGAIN_TGT__M 0x7FFF
+#define SCU_RAM_AGC_INGAIN_TGT__PRE 0x0
+
+#define SCU_RAM_AGC_INGAIN_TGT_MIN__A 0x831EB6
+#define SCU_RAM_AGC_INGAIN_TGT_MIN__W 15
+#define SCU_RAM_AGC_INGAIN_TGT_MIN__M 0x7FFF
+#define SCU_RAM_AGC_INGAIN_TGT_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_INGAIN_TGT_MAX__A 0x831EB7
+#define SCU_RAM_AGC_INGAIN_TGT_MAX__W 15
+#define SCU_RAM_AGC_INGAIN_TGT_MAX__M 0x7FFF
+#define SCU_RAM_AGC_INGAIN_TGT_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_IF_IACCU_HI__A 0x831EB8
+#define SCU_RAM_AGC_IF_IACCU_HI__W 16
+#define SCU_RAM_AGC_IF_IACCU_HI__M 0xFFFF
+#define SCU_RAM_AGC_IF_IACCU_HI__PRE 0x0
+
+#define SCU_RAM_AGC_IF_IACCU_LO__A 0x831EB9
+#define SCU_RAM_AGC_IF_IACCU_LO__W 8
+#define SCU_RAM_AGC_IF_IACCU_LO__M 0xFF
+#define SCU_RAM_AGC_IF_IACCU_LO__PRE 0x0
+
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT__A 0x831EBA
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT__W 15
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT__M 0x7FFF
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT__PRE 0x0
+
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A 0x831EBB
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__W 15
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__M 0x7FFF
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A 0x831EBC
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__W 15
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__M 0x7FFF
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_RF_IACCU_HI__A 0x831EBD
+#define SCU_RAM_AGC_RF_IACCU_HI__W 16
+#define SCU_RAM_AGC_RF_IACCU_HI__M 0xFFFF
+#define SCU_RAM_AGC_RF_IACCU_HI__PRE 0x0
+
+#define SCU_RAM_AGC_RF_IACCU_LO__A 0x831EBE
+#define SCU_RAM_AGC_RF_IACCU_LO__W 8
+#define SCU_RAM_AGC_RF_IACCU_LO__M 0xFF
+#define SCU_RAM_AGC_RF_IACCU_LO__PRE 0x0
+
+#define SCU_RAM_AGC_RF_IACCU_HI_CO__A 0x831EBF
+#define SCU_RAM_AGC_RF_IACCU_HI_CO__W 16
+#define SCU_RAM_AGC_RF_IACCU_HI_CO__M 0xFFFF
+#define SCU_RAM_AGC_RF_IACCU_HI_CO__PRE 0x0
+
+#define SCU_RAM_SP__A 0x831EC0
+#define SCU_RAM_SP__W 16
+#define SCU_RAM_SP__M 0xFFFF
+#define SCU_RAM_SP__PRE 0x0
+
+#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A 0x831EC1
+#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__W 16
+#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__M 0xFFFF
+#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MIN_IFGAIN__A 0x831EC2
+#define SCU_RAM_AGC_KI_MIN_IFGAIN__W 16
+#define SCU_RAM_AGC_KI_MIN_IFGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MIN_IFGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX_IFGAIN__A 0x831EC3
+#define SCU_RAM_AGC_KI_MAX_IFGAIN__W 16
+#define SCU_RAM_AGC_KI_MAX_IFGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MAX_IFGAIN__PRE 0x0
+
+#define SCU_RAM_FEC_MEAS_COUNT__A 0x831EC4
+#define SCU_RAM_FEC_MEAS_COUNT__W 16
+#define SCU_RAM_FEC_MEAS_COUNT__M 0xFFFF
+#define SCU_RAM_FEC_MEAS_COUNT__PRE 0x0
+
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__A 0x831EC5
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__W 16
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__M 0xFFFF
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__PRE 0x0
+
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__A 0x831EC6
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__W 16
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__M 0xFFFF
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__PRE 0x0
+#define SCU_RAM_GPIO__A 0x831EC7
+#define SCU_RAM_GPIO__W 1
+#define SCU_RAM_GPIO__M 0x1
+#define SCU_RAM_GPIO__PRE 0x0
+
+#define SCU_RAM_GPIO_HW_LOCK_IND__B 0
+#define SCU_RAM_GPIO_HW_LOCK_IND__W 1
+#define SCU_RAM_GPIO_HW_LOCK_IND__M 0x1
+#define SCU_RAM_GPIO_HW_LOCK_IND__PRE 0x0
+#define SCU_RAM_GPIO_HW_LOCK_IND_DISABLE 0x0
+#define SCU_RAM_GPIO_HW_LOCK_IND_ENABLE 0x1
+
+#define SCU_RAM_AGC_CLP_CTRL_MODE__A 0x831EC8
+#define SCU_RAM_AGC_CLP_CTRL_MODE__W 8
+#define SCU_RAM_AGC_CLP_CTRL_MODE__M 0xFF
+#define SCU_RAM_AGC_CLP_CTRL_MODE__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__B 0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__W 1
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__M 0x1
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__PRE 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW_false 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW_true 0x1
+
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__B 1
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__W 1
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__M 0x2
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__PRE 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP_FCC_ENABLE 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP_FCC_DISABLE 0x2
+
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__B 2
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__W 1
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__M 0x4
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__PRE 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC_DEC_DISABLE 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC_DEC_ENABLE 0x4
+
+#define SCU_RAM_AGC_KI_MIN_RFGAIN__A 0x831EC9
+#define SCU_RAM_AGC_KI_MIN_RFGAIN__W 16
+#define SCU_RAM_AGC_KI_MIN_RFGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MIN_RFGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX_RFGAIN__A 0x831ECA
+#define SCU_RAM_AGC_KI_MAX_RFGAIN__W 16
+#define SCU_RAM_AGC_KI_MAX_RFGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MAX_RFGAIN__PRE 0x0
+
+#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__A 0x831ECB
+#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__W 16
+#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__M 0xFFFF
+#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__PRE 0x0
+
+#define SCU_RAM_INHIBIT_1__A 0x831ECC
+#define SCU_RAM_INHIBIT_1__W 16
+#define SCU_RAM_INHIBIT_1__M 0xFFFF
+#define SCU_RAM_INHIBIT_1__PRE 0x0
+
+#define SCU_RAM_HTOL_BUF_0__A 0x831ECD
+#define SCU_RAM_HTOL_BUF_0__W 16
+#define SCU_RAM_HTOL_BUF_0__M 0xFFFF
+#define SCU_RAM_HTOL_BUF_0__PRE 0x0
+
+#define SCU_RAM_HTOL_BUF_1__A 0x831ECE
+#define SCU_RAM_HTOL_BUF_1__W 16
+#define SCU_RAM_HTOL_BUF_1__M 0xFFFF
+#define SCU_RAM_HTOL_BUF_1__PRE 0x0
+
+#define SCU_RAM_INHIBIT_2__A 0x831ECF
+#define SCU_RAM_INHIBIT_2__W 16
+#define SCU_RAM_INHIBIT_2__M 0xFFFF
+#define SCU_RAM_INHIBIT_2__PRE 0x0
+
+#define SCU_RAM_TR_SHORT_BUF_0__A 0x831ED0
+#define SCU_RAM_TR_SHORT_BUF_0__W 16
+#define SCU_RAM_TR_SHORT_BUF_0__M 0xFFFF
+#define SCU_RAM_TR_SHORT_BUF_0__PRE 0x0
+
+#define SCU_RAM_TR_SHORT_BUF_1__A 0x831ED1
+#define SCU_RAM_TR_SHORT_BUF_1__W 16
+#define SCU_RAM_TR_SHORT_BUF_1__M 0xFFFF
+#define SCU_RAM_TR_SHORT_BUF_1__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_0__A 0x831ED2
+#define SCU_RAM_TR_LONG_BUF_0__W 16
+#define SCU_RAM_TR_LONG_BUF_0__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_0__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_1__A 0x831ED3
+#define SCU_RAM_TR_LONG_BUF_1__W 16
+#define SCU_RAM_TR_LONG_BUF_1__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_1__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_2__A 0x831ED4
+#define SCU_RAM_TR_LONG_BUF_2__W 16
+#define SCU_RAM_TR_LONG_BUF_2__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_2__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_3__A 0x831ED5
+#define SCU_RAM_TR_LONG_BUF_3__W 16
+#define SCU_RAM_TR_LONG_BUF_3__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_3__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_4__A 0x831ED6
+#define SCU_RAM_TR_LONG_BUF_4__W 16
+#define SCU_RAM_TR_LONG_BUF_4__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_4__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_5__A 0x831ED7
+#define SCU_RAM_TR_LONG_BUF_5__W 16
+#define SCU_RAM_TR_LONG_BUF_5__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_5__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_6__A 0x831ED8
+#define SCU_RAM_TR_LONG_BUF_6__W 16
+#define SCU_RAM_TR_LONG_BUF_6__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_6__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_7__A 0x831ED9
+#define SCU_RAM_TR_LONG_BUF_7__W 16
+#define SCU_RAM_TR_LONG_BUF_7__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_7__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_8__A 0x831EDA
+#define SCU_RAM_TR_LONG_BUF_8__W 16
+#define SCU_RAM_TR_LONG_BUF_8__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_8__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_9__A 0x831EDB
+#define SCU_RAM_TR_LONG_BUF_9__W 16
+#define SCU_RAM_TR_LONG_BUF_9__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_9__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_10__A 0x831EDC
+#define SCU_RAM_TR_LONG_BUF_10__W 16
+#define SCU_RAM_TR_LONG_BUF_10__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_10__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_11__A 0x831EDD
+#define SCU_RAM_TR_LONG_BUF_11__W 16
+#define SCU_RAM_TR_LONG_BUF_11__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_11__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_12__A 0x831EDE
+#define SCU_RAM_TR_LONG_BUF_12__W 16
+#define SCU_RAM_TR_LONG_BUF_12__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_12__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_13__A 0x831EDF
+#define SCU_RAM_TR_LONG_BUF_13__W 16
+#define SCU_RAM_TR_LONG_BUF_13__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_13__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_14__A 0x831EE0
+#define SCU_RAM_TR_LONG_BUF_14__W 16
+#define SCU_RAM_TR_LONG_BUF_14__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_14__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_15__A 0x831EE1
+#define SCU_RAM_TR_LONG_BUF_15__W 16
+#define SCU_RAM_TR_LONG_BUF_15__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_15__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_16__A 0x831EE2
+#define SCU_RAM_TR_LONG_BUF_16__W 16
+#define SCU_RAM_TR_LONG_BUF_16__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_16__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_17__A 0x831EE3
+#define SCU_RAM_TR_LONG_BUF_17__W 16
+#define SCU_RAM_TR_LONG_BUF_17__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_17__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_18__A 0x831EE4
+#define SCU_RAM_TR_LONG_BUF_18__W 16
+#define SCU_RAM_TR_LONG_BUF_18__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_18__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_19__A 0x831EE5
+#define SCU_RAM_TR_LONG_BUF_19__W 16
+#define SCU_RAM_TR_LONG_BUF_19__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_19__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_20__A 0x831EE6
+#define SCU_RAM_TR_LONG_BUF_20__W 16
+#define SCU_RAM_TR_LONG_BUF_20__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_20__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_21__A 0x831EE7
+#define SCU_RAM_TR_LONG_BUF_21__W 16
+#define SCU_RAM_TR_LONG_BUF_21__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_21__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_22__A 0x831EE8
+#define SCU_RAM_TR_LONG_BUF_22__W 16
+#define SCU_RAM_TR_LONG_BUF_22__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_22__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_23__A 0x831EE9
+#define SCU_RAM_TR_LONG_BUF_23__W 16
+#define SCU_RAM_TR_LONG_BUF_23__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_23__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_24__A 0x831EEA
+#define SCU_RAM_TR_LONG_BUF_24__W 16
+#define SCU_RAM_TR_LONG_BUF_24__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_24__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_25__A 0x831EEB
+#define SCU_RAM_TR_LONG_BUF_25__W 16
+#define SCU_RAM_TR_LONG_BUF_25__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_25__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_26__A 0x831EEC
+#define SCU_RAM_TR_LONG_BUF_26__W 16
+#define SCU_RAM_TR_LONG_BUF_26__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_26__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_27__A 0x831EED
+#define SCU_RAM_TR_LONG_BUF_27__W 16
+#define SCU_RAM_TR_LONG_BUF_27__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_27__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_28__A 0x831EEE
+#define SCU_RAM_TR_LONG_BUF_28__W 16
+#define SCU_RAM_TR_LONG_BUF_28__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_28__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_29__A 0x831EEF
+#define SCU_RAM_TR_LONG_BUF_29__W 16
+#define SCU_RAM_TR_LONG_BUF_29__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_29__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_30__A 0x831EF0
+#define SCU_RAM_TR_LONG_BUF_30__W 16
+#define SCU_RAM_TR_LONG_BUF_30__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_30__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_31__A 0x831EF1
+#define SCU_RAM_TR_LONG_BUF_31__W 16
+#define SCU_RAM_TR_LONG_BUF_31__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_31__PRE 0x0
+#define SCU_RAM_ATV_AMS_MAX__A 0x831EF2
+#define SCU_RAM_ATV_AMS_MAX__W 11
+#define SCU_RAM_ATV_AMS_MAX__M 0x7FF
+#define SCU_RAM_ATV_AMS_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__B 0
+#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__W 11
+#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__M 0x7FF
+#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_AMS_MIN__A 0x831EF3
+#define SCU_RAM_ATV_AMS_MIN__W 11
+#define SCU_RAM_ATV_AMS_MIN__M 0x7FF
+#define SCU_RAM_ATV_AMS_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__B 0
+#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__W 11
+#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__M 0x7FF
+#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_FIELD_CNT__A 0x831EF4
+#define SCU_RAM_ATV_FIELD_CNT__W 9
+#define SCU_RAM_ATV_FIELD_CNT__M 0x1FF
+#define SCU_RAM_ATV_FIELD_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__B 0
+#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__W 9
+#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__M 0x1FF
+#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_AAGC_FAST__A 0x831EF5
+#define SCU_RAM_ATV_AAGC_FAST__W 1
+#define SCU_RAM_ATV_AAGC_FAST__M 0x1
+#define SCU_RAM_ATV_AAGC_FAST__PRE 0x0
+
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__B 0
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__W 1
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__M 0x1
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__PRE 0x0
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST_OFF 0x0
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST_ON 0x1
+
+#define SCU_RAM_ATV_AAGC_LP2__A 0x831EF6
+#define SCU_RAM_ATV_AAGC_LP2__W 16
+#define SCU_RAM_ATV_AAGC_LP2__M 0xFFFF
+#define SCU_RAM_ATV_AAGC_LP2__PRE 0x0
+
+#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__B 0
+#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__W 16
+#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__M 0xFFFF
+#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__PRE 0x0
+
+#define SCU_RAM_ATV_BP_LVL__A 0x831EF7
+#define SCU_RAM_ATV_BP_LVL__W 11
+#define SCU_RAM_ATV_BP_LVL__M 0x7FF
+#define SCU_RAM_ATV_BP_LVL__PRE 0x0
+
+#define SCU_RAM_ATV_BP_LVL_BP_LVL__B 0
+#define SCU_RAM_ATV_BP_LVL_BP_LVL__W 11
+#define SCU_RAM_ATV_BP_LVL_BP_LVL__M 0x7FF
+#define SCU_RAM_ATV_BP_LVL_BP_LVL__PRE 0x0
+
+#define SCU_RAM_ATV_BP_RELY__A 0x831EF8
+#define SCU_RAM_ATV_BP_RELY__W 8
+#define SCU_RAM_ATV_BP_RELY__M 0xFF
+#define SCU_RAM_ATV_BP_RELY__PRE 0x0
+
+#define SCU_RAM_ATV_BP_RELY_BP_RELY__B 0
+#define SCU_RAM_ATV_BP_RELY_BP_RELY__W 8
+#define SCU_RAM_ATV_BP_RELY_BP_RELY__M 0xFF
+#define SCU_RAM_ATV_BP_RELY_BP_RELY__PRE 0x0
+
+#define SCU_RAM_ATV_BP_MTA__A 0x831EF9
+#define SCU_RAM_ATV_BP_MTA__W 14
+#define SCU_RAM_ATV_BP_MTA__M 0x3FFF
+#define SCU_RAM_ATV_BP_MTA__PRE 0x0
+
+#define SCU_RAM_ATV_BP_MTA_BP_MTA__B 0
+#define SCU_RAM_ATV_BP_MTA_BP_MTA__W 14
+#define SCU_RAM_ATV_BP_MTA_BP_MTA__M 0x3FFF
+#define SCU_RAM_ATV_BP_MTA_BP_MTA__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF__A 0x831EFA
+#define SCU_RAM_ATV_BP_REF__W 11
+#define SCU_RAM_ATV_BP_REF__M 0x7FF
+#define SCU_RAM_ATV_BP_REF__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF_BP_REF__B 0
+#define SCU_RAM_ATV_BP_REF_BP_REF__W 11
+#define SCU_RAM_ATV_BP_REF_BP_REF__M 0x7FF
+#define SCU_RAM_ATV_BP_REF_BP_REF__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF_MIN__A 0x831EFB
+#define SCU_RAM_ATV_BP_REF_MIN__W 11
+#define SCU_RAM_ATV_BP_REF_MIN__M 0x7FF
+#define SCU_RAM_ATV_BP_REF_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__B 0
+#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__W 11
+#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__M 0x7FF
+#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF_MAX__A 0x831EFC
+#define SCU_RAM_ATV_BP_REF_MAX__W 11
+#define SCU_RAM_ATV_BP_REF_MAX__M 0x7FF
+#define SCU_RAM_ATV_BP_REF_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__B 0
+#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__W 11
+#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__M 0x7FF
+#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_BP_CNT__A 0x831EFD
+#define SCU_RAM_ATV_BP_CNT__W 8
+#define SCU_RAM_ATV_BP_CNT__M 0xFF
+#define SCU_RAM_ATV_BP_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_BP_CNT_BP_CNT__B 0
+#define SCU_RAM_ATV_BP_CNT_BP_CNT__W 8
+#define SCU_RAM_ATV_BP_CNT_BP_CNT__M 0xFF
+#define SCU_RAM_ATV_BP_CNT_BP_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_BP_XD_CNT__A 0x831EFE
+#define SCU_RAM_ATV_BP_XD_CNT__W 12
+#define SCU_RAM_ATV_BP_XD_CNT__M 0xFFF
+#define SCU_RAM_ATV_BP_XD_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__B 0
+#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__W 12
+#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__M 0xFFF
+#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_PAGC_KI_MIN__A 0x831EFF
+#define SCU_RAM_ATV_PAGC_KI_MIN__W 12
+#define SCU_RAM_ATV_PAGC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_PAGC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__B 0
+#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__W 12
+#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_BPC_KI_MIN__A 0x831F00
+#define SCU_RAM_ATV_BPC_KI_MIN__W 12
+#define SCU_RAM_ATV_BPC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_BPC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__B 0
+#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__W 12
+#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__A 0x831F01
+#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__W 16
+#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__M 0xFFFF
+#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__PRE 0x0
+
+#define SCU_RAM_ORX_RF_RX_DATA_RATE__A 0x831F02
+#define SCU_RAM_ORX_RF_RX_DATA_RATE__W 8
+#define SCU_RAM_ORX_RF_RX_DATA_RATE__M 0xFF
+#define SCU_RAM_ORX_RF_RX_DATA_RATE__PRE 0x0
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_REGSPEC 0x0
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_INVSPEC 0x1
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_REGSPEC_ALT 0x40
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_INVSPEC_ALT 0x41
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_REGSPEC 0x80
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_INVSPEC 0x81
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_REGSPEC 0xC0
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_INVSPEC 0xC1
+
+#define SCU_RAM_ORX_SCU_STATE__A 0x831F03
+#define SCU_RAM_ORX_SCU_STATE__W 8
+#define SCU_RAM_ORX_SCU_STATE__M 0xFF
+#define SCU_RAM_ORX_SCU_STATE__PRE 0x0
+#define SCU_RAM_ORX_SCU_STATE_RESET 0x0
+#define SCU_RAM_ORX_SCU_STATE_AGN_HUNT 0x1
+#define SCU_RAM_ORX_SCU_STATE_DGN_HUNT 0x2
+#define SCU_RAM_ORX_SCU_STATE_AGC_HUNT 0x3
+#define SCU_RAM_ORX_SCU_STATE_FRQ_HUNT 0x4
+#define SCU_RAM_ORX_SCU_STATE_PHA_HUNT 0x8
+#define SCU_RAM_ORX_SCU_STATE_TIM_HUNT 0x10
+#define SCU_RAM_ORX_SCU_STATE_EQU_HUNT 0x20
+#define SCU_RAM_ORX_SCU_STATE_EQT_HUNT 0x30
+#define SCU_RAM_ORX_SCU_STATE_SYNC 0x40
+
+#define SCU_RAM_ORX_SCU_LOCK__A 0x831F04
+#define SCU_RAM_ORX_SCU_LOCK__W 16
+#define SCU_RAM_ORX_SCU_LOCK__M 0xFFFF
+#define SCU_RAM_ORX_SCU_LOCK__PRE 0x0
+
+#define SCU_RAM_ORX_TARGET_MODE__A 0x831F05
+#define SCU_RAM_ORX_TARGET_MODE__W 2
+#define SCU_RAM_ORX_TARGET_MODE__M 0x3
+#define SCU_RAM_ORX_TARGET_MODE__PRE 0x0
+#define SCU_RAM_ORX_TARGET_MODE_1544KBPS 0x0
+#define SCU_RAM_ORX_TARGET_MODE_3088KBPS 0x1
+#define SCU_RAM_ORX_TARGET_MODE_2048KBPS_SQRT 0x2
+#define SCU_RAM_ORX_TARGET_MODE_2048KBPS_RO 0x3
+
+#define SCU_RAM_ORX_MER_MIN_DB__A 0x831F06
+#define SCU_RAM_ORX_MER_MIN_DB__W 8
+#define SCU_RAM_ORX_MER_MIN_DB__M 0xFF
+#define SCU_RAM_ORX_MER_MIN_DB__PRE 0x0
+
+#define SCU_RAM_ORX_RF_GAIN__A 0x831F07
+#define SCU_RAM_ORX_RF_GAIN__W 16
+#define SCU_RAM_ORX_RF_GAIN__M 0xFFFF
+#define SCU_RAM_ORX_RF_GAIN__PRE 0x0
+
+#define SCU_RAM_ORX_RF_GAIN_MIN__A 0x831F08
+#define SCU_RAM_ORX_RF_GAIN_MIN__W 16
+#define SCU_RAM_ORX_RF_GAIN_MIN__M 0xFFFF
+#define SCU_RAM_ORX_RF_GAIN_MIN__PRE 0x0
+
+#define SCU_RAM_ORX_RF_GAIN_MAX__A 0x831F09
+#define SCU_RAM_ORX_RF_GAIN_MAX__W 16
+#define SCU_RAM_ORX_RF_GAIN_MAX__M 0xFFFF
+#define SCU_RAM_ORX_RF_GAIN_MAX__PRE 0x0
+
+#define SCU_RAM_ORX_IF_GAIN__A 0x831F0A
+#define SCU_RAM_ORX_IF_GAIN__W 16
+#define SCU_RAM_ORX_IF_GAIN__M 0xFFFF
+#define SCU_RAM_ORX_IF_GAIN__PRE 0x0
+
+#define SCU_RAM_ORX_IF_GAIN_MIN__A 0x831F0B
+#define SCU_RAM_ORX_IF_GAIN_MIN__W 16
+#define SCU_RAM_ORX_IF_GAIN_MIN__M 0xFFFF
+#define SCU_RAM_ORX_IF_GAIN_MIN__PRE 0x0
+
+#define SCU_RAM_ORX_IF_GAIN_MAX__A 0x831F0C
+#define SCU_RAM_ORX_IF_GAIN_MAX__W 16
+#define SCU_RAM_ORX_IF_GAIN_MAX__M 0xFFFF
+#define SCU_RAM_ORX_IF_GAIN_MAX__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_HEADR__A 0x831F0D
+#define SCU_RAM_ORX_AGN_HEADR__W 16
+#define SCU_RAM_ORX_AGN_HEADR__M 0xFFFF
+#define SCU_RAM_ORX_AGN_HEADR__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_HEADR_STP__A 0x831F0E
+#define SCU_RAM_ORX_AGN_HEADR_STP__W 8
+#define SCU_RAM_ORX_AGN_HEADR_STP__M 0xFF
+#define SCU_RAM_ORX_AGN_HEADR_STP__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_KI__A 0x831F0F
+#define SCU_RAM_ORX_AGN_KI__W 8
+#define SCU_RAM_ORX_AGN_KI__M 0xFF
+#define SCU_RAM_ORX_AGN_KI__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_LOCK_TH__A 0x831F10
+#define SCU_RAM_ORX_AGN_LOCK_TH__W 16
+#define SCU_RAM_ORX_AGN_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_AGN_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_LOCK_WD__A 0x831F11
+#define SCU_RAM_ORX_AGN_LOCK_WD__W 16
+#define SCU_RAM_ORX_AGN_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_AGN_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_ONLOCK_TTH__A 0x831F12
+#define SCU_RAM_ORX_AGN_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_AGN_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_AGN_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_UNLOCK_TTH__A 0x831F13
+#define SCU_RAM_ORX_AGN_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_AGN_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_AGN_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_LOCK_TOTH__A 0x831F14
+#define SCU_RAM_ORX_AGN_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_AGN_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_AGN_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_LOCK_MASK__A 0x831F15
+#define SCU_RAM_ORX_AGN_LOCK_MASK__W 8
+#define SCU_RAM_ORX_AGN_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_AGN_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_DGN__A 0x831F16
+#define SCU_RAM_ORX_DGN__W 16
+#define SCU_RAM_ORX_DGN__M 0xFFFF
+#define SCU_RAM_ORX_DGN__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_MIN__A 0x831F17
+#define SCU_RAM_ORX_DGN_MIN__W 16
+#define SCU_RAM_ORX_DGN_MIN__M 0xFFFF
+#define SCU_RAM_ORX_DGN_MIN__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_MAX__A 0x831F18
+#define SCU_RAM_ORX_DGN_MAX__W 16
+#define SCU_RAM_ORX_DGN_MAX__M 0xFFFF
+#define SCU_RAM_ORX_DGN_MAX__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_AMP__A 0x831F19
+#define SCU_RAM_ORX_DGN_AMP__W 16
+#define SCU_RAM_ORX_DGN_AMP__M 0xFFFF
+#define SCU_RAM_ORX_DGN_AMP__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_AMPTARGET__A 0x831F1A
+#define SCU_RAM_ORX_DGN_AMPTARGET__W 16
+#define SCU_RAM_ORX_DGN_AMPTARGET__M 0xFFFF
+#define SCU_RAM_ORX_DGN_AMPTARGET__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_KI__A 0x831F1B
+#define SCU_RAM_ORX_DGN_KI__W 8
+#define SCU_RAM_ORX_DGN_KI__M 0xFF
+#define SCU_RAM_ORX_DGN_KI__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_LOCK_TH__A 0x831F1C
+#define SCU_RAM_ORX_DGN_LOCK_TH__W 16
+#define SCU_RAM_ORX_DGN_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_DGN_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_LOCK_WD__A 0x831F1D
+#define SCU_RAM_ORX_DGN_LOCK_WD__W 16
+#define SCU_RAM_ORX_DGN_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_DGN_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_ONLOCK_TTH__A 0x831F1E
+#define SCU_RAM_ORX_DGN_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_DGN_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_DGN_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_UNLOCK_TTH__A 0x831F1F
+#define SCU_RAM_ORX_DGN_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_DGN_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_DGN_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_LOCK_TOTH__A 0x831F20
+#define SCU_RAM_ORX_DGN_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_DGN_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_DGN_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_LOCK_MASK__A 0x831F21
+#define SCU_RAM_ORX_DGN_LOCK_MASK__W 8
+#define SCU_RAM_ORX_DGN_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_DGN_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_FREQ_GAIN_CORR__A 0x831F22
+#define SCU_RAM_ORX_FREQ_GAIN_CORR__W 8
+#define SCU_RAM_ORX_FREQ_GAIN_CORR__M 0xFF
+#define SCU_RAM_ORX_FREQ_GAIN_CORR__PRE 0x0
+#define SCU_RAM_ORX_FREQ_GAIN_CORR_1544KBPS 0x60
+#define SCU_RAM_ORX_FREQ_GAIN_CORR_2048KBPS 0x80
+#define SCU_RAM_ORX_FREQ_GAIN_CORR_3088KBPS 0xC0
+
+#define SCU_RAM_ORX_FRQ_OFFSET__A 0x831F23
+#define SCU_RAM_ORX_FRQ_OFFSET__W 16
+#define SCU_RAM_ORX_FRQ_OFFSET__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_OFFSET__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_OFFSET_MAX__A 0x831F24
+#define SCU_RAM_ORX_FRQ_OFFSET_MAX__W 15
+#define SCU_RAM_ORX_FRQ_OFFSET_MAX__M 0x7FFF
+#define SCU_RAM_ORX_FRQ_OFFSET_MAX__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_KI__A 0x831F25
+#define SCU_RAM_ORX_FRQ_KI__W 8
+#define SCU_RAM_ORX_FRQ_KI__M 0xFF
+#define SCU_RAM_ORX_FRQ_KI__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_DIFF__A 0x831F26
+#define SCU_RAM_ORX_FRQ_DIFF__W 16
+#define SCU_RAM_ORX_FRQ_DIFF__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_DIFF__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_LOCK_TH__A 0x831F27
+#define SCU_RAM_ORX_FRQ_LOCK_TH__W 16
+#define SCU_RAM_ORX_FRQ_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_LOCK_WD__A 0x831F28
+#define SCU_RAM_ORX_FRQ_LOCK_WD__W 16
+#define SCU_RAM_ORX_FRQ_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__A 0x831F29
+#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__A 0x831F2A
+#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_LOCK_TOTH__A 0x831F2B
+#define SCU_RAM_ORX_FRQ_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_FRQ_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_LOCK_MASK__A 0x831F2C
+#define SCU_RAM_ORX_FRQ_LOCK_MASK__W 8
+#define SCU_RAM_ORX_FRQ_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_FRQ_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_DIFF__A 0x831F2D
+#define SCU_RAM_ORX_PHA_DIFF__W 16
+#define SCU_RAM_ORX_PHA_DIFF__M 0xFFFF
+#define SCU_RAM_ORX_PHA_DIFF__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_LOCK_TH__A 0x831F2E
+#define SCU_RAM_ORX_PHA_LOCK_TH__W 16
+#define SCU_RAM_ORX_PHA_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_PHA_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_LOCK_WD__A 0x831F2F
+#define SCU_RAM_ORX_PHA_LOCK_WD__W 16
+#define SCU_RAM_ORX_PHA_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_PHA_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_ONLOCK_TTH__A 0x831F30
+#define SCU_RAM_ORX_PHA_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_PHA_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_PHA_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_UNLOCK_TTH__A 0x831F31
+#define SCU_RAM_ORX_PHA_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_PHA_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_PHA_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_LOCK_TOTH__A 0x831F32
+#define SCU_RAM_ORX_PHA_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_PHA_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_PHA_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_LOCK_MASK__A 0x831F33
+#define SCU_RAM_ORX_PHA_LOCK_MASK__W 8
+#define SCU_RAM_ORX_PHA_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_PHA_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_OFFSET__A 0x831F34
+#define SCU_RAM_ORX_TIM_OFFSET__W 16
+#define SCU_RAM_ORX_TIM_OFFSET__M 0xFFFF
+#define SCU_RAM_ORX_TIM_OFFSET__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_DIFF__A 0x831F35
+#define SCU_RAM_ORX_TIM_DIFF__W 16
+#define SCU_RAM_ORX_TIM_DIFF__M 0xFFFF
+#define SCU_RAM_ORX_TIM_DIFF__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_LOCK_TH__A 0x831F36
+#define SCU_RAM_ORX_TIM_LOCK_TH__W 16
+#define SCU_RAM_ORX_TIM_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_TIM_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_LOCK_WD__A 0x831F37
+#define SCU_RAM_ORX_TIM_LOCK_WD__W 16
+#define SCU_RAM_ORX_TIM_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_TIM_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_ONLOCK_TTH__A 0x831F38
+#define SCU_RAM_ORX_TIM_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_TIM_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_TIM_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_UNLOCK_TTH__A 0x831F39
+#define SCU_RAM_ORX_TIM_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_TIM_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_TIM_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_LOCK_TOTH__A 0x831F3A
+#define SCU_RAM_ORX_TIM_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_TIM_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_TIM_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_LOCK_MASK__A 0x831F3B
+#define SCU_RAM_ORX_TIM_LOCK_MASK__W 8
+#define SCU_RAM_ORX_TIM_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_TIM_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_DIFF__A 0x831F3C
+#define SCU_RAM_ORX_EQU_DIFF__W 16
+#define SCU_RAM_ORX_EQU_DIFF__M 0xFFFF
+#define SCU_RAM_ORX_EQU_DIFF__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_LOCK_TH__A 0x831F3D
+#define SCU_RAM_ORX_EQU_LOCK_TH__W 16
+#define SCU_RAM_ORX_EQU_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_EQU_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_LOCK_WD__A 0x831F3E
+#define SCU_RAM_ORX_EQU_LOCK_WD__W 16
+#define SCU_RAM_ORX_EQU_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_EQU_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_ONLOCK_TTH__A 0x831F3F
+#define SCU_RAM_ORX_EQU_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_EQU_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_EQU_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_UNLOCK_TTH__A 0x831F40
+#define SCU_RAM_ORX_EQU_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_EQU_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_EQU_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_LOCK_TOTH__A 0x831F41
+#define SCU_RAM_ORX_EQU_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_EQU_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_EQU_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_LOCK_MASK__A 0x831F42
+#define SCU_RAM_ORX_EQU_LOCK_MASK__W 8
+#define SCU_RAM_ORX_EQU_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_EQU_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_FLT_FRQ__A 0x831F43
+#define SCU_RAM_ORX_FLT_FRQ__W 16
+#define SCU_RAM_ORX_FLT_FRQ__M 0xFFFF
+#define SCU_RAM_ORX_FLT_FRQ__PRE 0x0
+#define SCU_RAM_ORX_RST_CPH__A 0x831F44
+#define SCU_RAM_ORX_RST_CPH__W 4
+#define SCU_RAM_ORX_RST_CPH__M 0xF
+#define SCU_RAM_ORX_RST_CPH__PRE 0x0
+
+#define SCU_RAM_ORX_RST_CPH_RST_CPH__B 0
+#define SCU_RAM_ORX_RST_CPH_RST_CPH__W 4
+#define SCU_RAM_ORX_RST_CPH_RST_CPH__M 0xF
+#define SCU_RAM_ORX_RST_CPH_RST_CPH__PRE 0x0
+
+#define SCU_RAM_ORX_RST_CTI__A 0x831F45
+#define SCU_RAM_ORX_RST_CTI__W 4
+#define SCU_RAM_ORX_RST_CTI__M 0xF
+#define SCU_RAM_ORX_RST_CTI__PRE 0x0
+
+#define SCU_RAM_ORX_RST_CTI_RST_CTI__B 0
+#define SCU_RAM_ORX_RST_CTI_RST_CTI__W 4
+#define SCU_RAM_ORX_RST_CTI_RST_CTI__M 0xF
+#define SCU_RAM_ORX_RST_CTI_RST_CTI__PRE 0x0
+
+#define SCU_RAM_ORX_RST_KRN__A 0x831F46
+#define SCU_RAM_ORX_RST_KRN__W 4
+#define SCU_RAM_ORX_RST_KRN__M 0xF
+#define SCU_RAM_ORX_RST_KRN__PRE 0x0
+
+#define SCU_RAM_ORX_RST_KRN_RST_KRN__B 0
+#define SCU_RAM_ORX_RST_KRN_RST_KRN__W 4
+#define SCU_RAM_ORX_RST_KRN_RST_KRN__M 0xF
+#define SCU_RAM_ORX_RST_KRN_RST_KRN__PRE 0x0
+
+#define SCU_RAM_ORX_RST_KRP__A 0x831F47
+#define SCU_RAM_ORX_RST_KRP__W 4
+#define SCU_RAM_ORX_RST_KRP__M 0xF
+#define SCU_RAM_ORX_RST_KRP__PRE 0x0
+
+#define SCU_RAM_ORX_RST_KRP_RST_KRP__B 0
+#define SCU_RAM_ORX_RST_KRP_RST_KRP__W 4
+#define SCU_RAM_ORX_RST_KRP_RST_KRP__M 0xF
+#define SCU_RAM_ORX_RST_KRP_RST_KRP__PRE 0x0
+
+#define SCU_RAM_ATV_STANDARD__A 0x831F48
+#define SCU_RAM_ATV_STANDARD__W 12
+#define SCU_RAM_ATV_STANDARD__M 0xFFF
+#define SCU_RAM_ATV_STANDARD__PRE 0x0
+
+#define SCU_RAM_ATV_STANDARD_STANDARD__B 0
+#define SCU_RAM_ATV_STANDARD_STANDARD__W 12
+#define SCU_RAM_ATV_STANDARD_STANDARD__M 0xFFF
+#define SCU_RAM_ATV_STANDARD_STANDARD__PRE 0x0
+#define SCU_RAM_ATV_STANDARD_STANDARD_MN 0x2
+#define SCU_RAM_ATV_STANDARD_STANDARD_B 0x103
+#define SCU_RAM_ATV_STANDARD_STANDARD_G 0x3
+#define SCU_RAM_ATV_STANDARD_STANDARD_DK 0x4
+#define SCU_RAM_ATV_STANDARD_STANDARD_L 0x9
+#define SCU_RAM_ATV_STANDARD_STANDARD_LP 0x109
+#define SCU_RAM_ATV_STANDARD_STANDARD_I 0xA
+#define SCU_RAM_ATV_STANDARD_STANDARD_FM 0x40
+
+#define SCU_RAM_ATV_DETECT__A 0x831F49
+#define SCU_RAM_ATV_DETECT__W 1
+#define SCU_RAM_ATV_DETECT__M 0x1
+#define SCU_RAM_ATV_DETECT__PRE 0x0
+
+#define SCU_RAM_ATV_DETECT_DETECT__B 0
+#define SCU_RAM_ATV_DETECT_DETECT__W 1
+#define SCU_RAM_ATV_DETECT_DETECT__M 0x1
+#define SCU_RAM_ATV_DETECT_DETECT__PRE 0x0
+#define SCU_RAM_ATV_DETECT_DETECT_false 0x0
+#define SCU_RAM_ATV_DETECT_DETECT_true 0x1
+
+#define SCU_RAM_ATV_DETECT_TH__A 0x831F4A
+#define SCU_RAM_ATV_DETECT_TH__W 8
+#define SCU_RAM_ATV_DETECT_TH__M 0xFF
+#define SCU_RAM_ATV_DETECT_TH__PRE 0x0
+
+#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__B 0
+#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__W 8
+#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__M 0xFF
+#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__PRE 0x0
+
+#define SCU_RAM_ATV_LOCK__A 0x831F4B
+#define SCU_RAM_ATV_LOCK__W 2
+#define SCU_RAM_ATV_LOCK__M 0x3
+#define SCU_RAM_ATV_LOCK__PRE 0x0
+
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__B 0
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__W 1
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__M 0x1
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__PRE 0x0
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT_NO_LOCK 0x0
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT_LOCK 0x1
+
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG__B 1
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG__W 1
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG__M 0x2
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG__PRE 0x0
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG_NO_SYNC 0x0
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG_SYNC 0x2
+
+#define SCU_RAM_ATV_CR_LOCK__A 0x831F4C
+#define SCU_RAM_ATV_CR_LOCK__W 11
+#define SCU_RAM_ATV_CR_LOCK__M 0x7FF
+#define SCU_RAM_ATV_CR_LOCK__PRE 0x0
+
+#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__B 0
+#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__W 11
+#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__M 0x7FF
+#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__PRE 0x0
+
+#define SCU_RAM_ATV_AGC_MODE__A 0x831F4D
+#define SCU_RAM_ATV_AGC_MODE__W 8
+#define SCU_RAM_ATV_AGC_MODE__M 0xFF
+#define SCU_RAM_ATV_AGC_MODE__PRE 0x0
+
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__B 2
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__W 1
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__M 0x4
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__PRE 0x0
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL_AGC_FAST 0x0
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL_AGC_SLOW 0x4
+
+#define SCU_RAM_ATV_AGC_MODE_BP_EN__B 3
+#define SCU_RAM_ATV_AGC_MODE_BP_EN__W 1
+#define SCU_RAM_ATV_AGC_MODE_BP_EN__M 0x8
+#define SCU_RAM_ATV_AGC_MODE_BP_EN__PRE 0x0
+#define SCU_RAM_ATV_AGC_MODE_BP_EN_BPC_DISABLE 0x0
+#define SCU_RAM_ATV_AGC_MODE_BP_EN_BPC_ENABLE 0x8
+
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD__B 4
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD__W 2
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD__M 0x30
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD__PRE 0x0
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD_SIF_AGC_OFF 0x0
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD_SIF_AGC_FM 0x10
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD_SIF_AGC_AM 0x20
+
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__B 6
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__W 1
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__M 0x40
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__PRE 0x0
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN_FAGC_DISABLE 0x0
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN_FAGC_ENABLE 0x40
+
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__B 7
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__W 1
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__M 0x80
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__PRE 0x0
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP_MWA_ENABLE 0x0
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP_MWA_DISABLE 0x80
+
+#define SCU_RAM_ATV_RSV_01__A 0x831F4E
+#define SCU_RAM_ATV_RSV_01__W 16
+#define SCU_RAM_ATV_RSV_01__M 0xFFFF
+#define SCU_RAM_ATV_RSV_01__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_02__A 0x831F4F
+#define SCU_RAM_ATV_RSV_02__W 16
+#define SCU_RAM_ATV_RSV_02__M 0xFFFF
+#define SCU_RAM_ATV_RSV_02__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_03__A 0x831F50
+#define SCU_RAM_ATV_RSV_03__W 16
+#define SCU_RAM_ATV_RSV_03__M 0xFFFF
+#define SCU_RAM_ATV_RSV_03__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_04__A 0x831F51
+#define SCU_RAM_ATV_RSV_04__W 16
+#define SCU_RAM_ATV_RSV_04__M 0xFFFF
+#define SCU_RAM_ATV_RSV_04__PRE 0x0
+#define SCU_RAM_ATV_FAGC_TH_RED__A 0x831F52
+#define SCU_RAM_ATV_FAGC_TH_RED__W 8
+#define SCU_RAM_ATV_FAGC_TH_RED__M 0xFF
+#define SCU_RAM_ATV_FAGC_TH_RED__PRE 0x0
+
+#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__B 0
+#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__W 8
+#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__M 0xFF
+#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__PRE 0x0
+
+#define SCU_RAM_ATV_AMS_MAX_REF__A 0x831F53
+#define SCU_RAM_ATV_AMS_MAX_REF__W 11
+#define SCU_RAM_ATV_AMS_MAX_REF__M 0x7FF
+#define SCU_RAM_ATV_AMS_MAX_REF__PRE 0x0
+
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__B 0
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__W 11
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__M 0x7FF
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__PRE 0x0
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_BG_MN 0x2BC
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_DK 0x2D0
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_I 0x314
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_LLP 0x28A
+
+#define SCU_RAM_ATV_ACT_AMX__A 0x831F54
+#define SCU_RAM_ATV_ACT_AMX__W 11
+#define SCU_RAM_ATV_ACT_AMX__M 0x7FF
+#define SCU_RAM_ATV_ACT_AMX__PRE 0x0
+
+#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__B 0
+#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__W 11
+#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__M 0x7FF
+#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__PRE 0x0
+
+#define SCU_RAM_ATV_ACT_AMI__A 0x831F55
+#define SCU_RAM_ATV_ACT_AMI__W 11
+#define SCU_RAM_ATV_ACT_AMI__M 0x7FF
+#define SCU_RAM_ATV_ACT_AMI__PRE 0x0
+
+#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__B 0
+#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__W 11
+#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__M 0x7FF
+#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_05__A 0x831F56
+#define SCU_RAM_ATV_RSV_05__W 16
+#define SCU_RAM_ATV_RSV_05__M 0xFFFF
+#define SCU_RAM_ATV_RSV_05__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_06__A 0x831F57
+#define SCU_RAM_ATV_RSV_06__W 16
+#define SCU_RAM_ATV_RSV_06__M 0xFFFF
+#define SCU_RAM_ATV_RSV_06__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_07__A 0x831F58
+#define SCU_RAM_ATV_RSV_07__W 16
+#define SCU_RAM_ATV_RSV_07__M 0xFFFF
+#define SCU_RAM_ATV_RSV_07__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_08__A 0x831F59
+#define SCU_RAM_ATV_RSV_08__W 16
+#define SCU_RAM_ATV_RSV_08__M 0xFFFF
+#define SCU_RAM_ATV_RSV_08__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_09__A 0x831F5A
+#define SCU_RAM_ATV_RSV_09__W 16
+#define SCU_RAM_ATV_RSV_09__M 0xFFFF
+#define SCU_RAM_ATV_RSV_09__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_10__A 0x831F5B
+#define SCU_RAM_ATV_RSV_10__W 16
+#define SCU_RAM_ATV_RSV_10__M 0xFFFF
+#define SCU_RAM_ATV_RSV_10__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_11__A 0x831F5C
+#define SCU_RAM_ATV_RSV_11__W 16
+#define SCU_RAM_ATV_RSV_11__M 0xFFFF
+#define SCU_RAM_ATV_RSV_11__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_12__A 0x831F5D
+#define SCU_RAM_ATV_RSV_12__W 16
+#define SCU_RAM_ATV_RSV_12__M 0xFFFF
+#define SCU_RAM_ATV_RSV_12__PRE 0x0
+#define SCU_RAM_ATV_VID_GAIN_HI__A 0x831F5E
+#define SCU_RAM_ATV_VID_GAIN_HI__W 16
+#define SCU_RAM_ATV_VID_GAIN_HI__M 0xFFFF
+#define SCU_RAM_ATV_VID_GAIN_HI__PRE 0x0
+
+#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__B 0
+#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__W 16
+#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__M 0xFFFF
+#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__PRE 0x0
+
+#define SCU_RAM_ATV_VID_GAIN_LO__A 0x831F5F
+#define SCU_RAM_ATV_VID_GAIN_LO__W 8
+#define SCU_RAM_ATV_VID_GAIN_LO__M 0xFF
+#define SCU_RAM_ATV_VID_GAIN_LO__PRE 0x0
+
+#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__B 0
+#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__W 8
+#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__M 0xFF
+#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_13__A 0x831F60
+#define SCU_RAM_ATV_RSV_13__W 16
+#define SCU_RAM_ATV_RSV_13__M 0xFFFF
+#define SCU_RAM_ATV_RSV_13__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_14__A 0x831F61
+#define SCU_RAM_ATV_RSV_14__W 16
+#define SCU_RAM_ATV_RSV_14__M 0xFFFF
+#define SCU_RAM_ATV_RSV_14__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_15__A 0x831F62
+#define SCU_RAM_ATV_RSV_15__W 16
+#define SCU_RAM_ATV_RSV_15__M 0xFFFF
+#define SCU_RAM_ATV_RSV_15__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_16__A 0x831F63
+#define SCU_RAM_ATV_RSV_16__W 16
+#define SCU_RAM_ATV_RSV_16__M 0xFFFF
+#define SCU_RAM_ATV_RSV_16__PRE 0x0
+#define SCU_RAM_ATV_AAGC_CNT__A 0x831F64
+#define SCU_RAM_ATV_AAGC_CNT__W 8
+#define SCU_RAM_ATV_AAGC_CNT__M 0xFF
+#define SCU_RAM_ATV_AAGC_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__B 0
+#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__W 8
+#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__M 0xFF
+#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_SIF_GAIN__A 0x831F65
+#define SCU_RAM_ATV_SIF_GAIN__W 11
+#define SCU_RAM_ATV_SIF_GAIN__M 0x7FF
+#define SCU_RAM_ATV_SIF_GAIN__PRE 0x0
+
+#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__B 0
+#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__W 11
+#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__M 0x7FF
+#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_17__A 0x831F66
+#define SCU_RAM_ATV_RSV_17__W 16
+#define SCU_RAM_ATV_RSV_17__M 0xFFFF
+#define SCU_RAM_ATV_RSV_17__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_18__A 0x831F67
+#define SCU_RAM_ATV_RSV_18__W 16
+#define SCU_RAM_ATV_RSV_18__M 0xFFFF
+#define SCU_RAM_ATV_RSV_18__PRE 0x0
+
+#define SCU_RAM_ATV_RATE_OFS__A 0x831F68
+#define SCU_RAM_ATV_RATE_OFS__W 12
+#define SCU_RAM_ATV_RATE_OFS__M 0xFFF
+#define SCU_RAM_ATV_RATE_OFS__PRE 0x0
+
+#define SCU_RAM_ATV_LO_INCR__A 0x831F69
+#define SCU_RAM_ATV_LO_INCR__W 12
+#define SCU_RAM_ATV_LO_INCR__M 0xFFF
+#define SCU_RAM_ATV_LO_INCR__PRE 0x0
+
+#define SCU_RAM_ATV_IIR_CRIT__A 0x831F6A
+#define SCU_RAM_ATV_IIR_CRIT__W 12
+#define SCU_RAM_ATV_IIR_CRIT__M 0xFFF
+#define SCU_RAM_ATV_IIR_CRIT__PRE 0x0
+
+#define SCU_RAM_ATV_DEF_RATE_OFS__A 0x831F6B
+#define SCU_RAM_ATV_DEF_RATE_OFS__W 12
+#define SCU_RAM_ATV_DEF_RATE_OFS__M 0xFFF
+#define SCU_RAM_ATV_DEF_RATE_OFS__PRE 0x0
+
+#define SCU_RAM_ATV_DEF_LO_INCR__A 0x831F6C
+#define SCU_RAM_ATV_DEF_LO_INCR__W 12
+#define SCU_RAM_ATV_DEF_LO_INCR__M 0xFFF
+#define SCU_RAM_ATV_DEF_LO_INCR__PRE 0x0
+
+#define SCU_RAM_ATV_ENABLE_IIR_WA__A 0x831F6D
+#define SCU_RAM_ATV_ENABLE_IIR_WA__W 1
+#define SCU_RAM_ATV_ENABLE_IIR_WA__M 0x1
+#define SCU_RAM_ATV_ENABLE_IIR_WA__PRE 0x0
+
+#define SCU_RAM_ATV_MOD_CONTROL__A 0x831F6E
+#define SCU_RAM_ATV_MOD_CONTROL__W 12
+#define SCU_RAM_ATV_MOD_CONTROL__M 0xFFF
+#define SCU_RAM_ATV_MOD_CONTROL__PRE 0x0
+
+#define SCU_RAM_ATV_PAGC_KI_MAX__A 0x831F6F
+#define SCU_RAM_ATV_PAGC_KI_MAX__W 12
+#define SCU_RAM_ATV_PAGC_KI_MAX__M 0xFFF
+#define SCU_RAM_ATV_PAGC_KI_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_BPC_KI_MAX__A 0x831F70
+#define SCU_RAM_ATV_BPC_KI_MAX__W 12
+#define SCU_RAM_ATV_BPC_KI_MAX__M 0xFFF
+#define SCU_RAM_ATV_BPC_KI_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_NAGC_KI_MAX__A 0x831F71
+#define SCU_RAM_ATV_NAGC_KI_MAX__W 12
+#define SCU_RAM_ATV_NAGC_KI_MAX__M 0xFFF
+#define SCU_RAM_ATV_NAGC_KI_MAX__PRE 0x0
+#define SCU_RAM_ATV_NAGC_KI_MIN__A 0x831F72
+#define SCU_RAM_ATV_NAGC_KI_MIN__W 12
+#define SCU_RAM_ATV_NAGC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_NAGC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__B 0
+#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__W 12
+#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_KI_CHANGE_TH__A 0x831F73
+#define SCU_RAM_ATV_KI_CHANGE_TH__W 8
+#define SCU_RAM_ATV_KI_CHANGE_TH__M 0xFF
+#define SCU_RAM_ATV_KI_CHANGE_TH__PRE 0x0
+
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__B 0
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__W 8
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__M 0xFF
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__PRE 0x0
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH_NEG_MOD 0x14
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH_POS_MOD 0x28
+
+#define SCU_RAM_QAM_PARAM_ANNEX__A 0x831F74
+#define SCU_RAM_QAM_PARAM_ANNEX__W 2
+#define SCU_RAM_QAM_PARAM_ANNEX__M 0x3
+#define SCU_RAM_QAM_PARAM_ANNEX__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT__B 0
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT__W 2
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT__M 0x3
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT__PRE 0x0
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_A 0x0
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_B 0x1
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_C 0x2
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_D 0x3
+
+#define SCU_RAM_QAM_PARAM_CONSTELLATION__A 0x831F75
+#define SCU_RAM_QAM_PARAM_CONSTELLATION__W 3
+#define SCU_RAM_QAM_PARAM_CONSTELLATION__M 0x7
+#define SCU_RAM_QAM_PARAM_CONSTELLATION__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__B 0
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__W 3
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__M 0x7
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__PRE 0x0
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_UNKNOWN 0x0
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_16 0x3
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_32 0x4
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_64 0x5
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_128 0x6
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_256 0x7
+
+#define SCU_RAM_QAM_PARAM_INTERLEAVE__A 0x831F76
+#define SCU_RAM_QAM_PARAM_INTERLEAVE__W 8
+#define SCU_RAM_QAM_PARAM_INTERLEAVE__M 0xFF
+#define SCU_RAM_QAM_PARAM_INTERLEAVE__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__B 0
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__W 8
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__M 0xFF
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__PRE 0x0
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J1 0x0
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J1_V2 0x1
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J2 0x2
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I64_J2 0x3
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J3 0x4
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I32_J4 0x5
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J4 0x6
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I16_J8 0x7
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J5 0x8
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I8_J16 0x9
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J6 0xA
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J7 0xC
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J8 0xE
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I12_J17 0x10
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I5_J4 0x11
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_UNKNOWN 0xFE
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_AUTO 0xFF
+
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__A 0x831F77
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__W 16
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__B 0
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__W 16
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__A 0x831F78
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__W 16
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__B 0
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__W 16
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CENTERTAP__A 0x831F79
+#define SCU_RAM_QAM_EQ_CENTERTAP__W 16
+#define SCU_RAM_QAM_EQ_CENTERTAP__M 0xFFFF
+#define SCU_RAM_QAM_EQ_CENTERTAP__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__B 0
+#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__W 8
+#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__M 0xFF
+#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_0__A 0x831F7A
+#define SCU_RAM_QAM_WR_RSV_0__W 16
+#define SCU_RAM_QAM_WR_RSV_0__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_0__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_0_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_0_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_0_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_0_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__A 0x831F7B
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__W 16
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__B 0
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__W 16
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__A 0x831F7C
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__W 16
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__B 0
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__W 16
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_5__A 0x831F7D
+#define SCU_RAM_QAM_WR_RSV_5__W 16
+#define SCU_RAM_QAM_WR_RSV_5__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_5__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_5_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_5_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_5_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_5_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_6__A 0x831F7E
+#define SCU_RAM_QAM_WR_RSV_6__W 16
+#define SCU_RAM_QAM_WR_RSV_6__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_6__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_6_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_6_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_6_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_6_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_7__A 0x831F7F
+#define SCU_RAM_QAM_WR_RSV_7__W 16
+#define SCU_RAM_QAM_WR_RSV_7__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_7__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_7_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_7_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_7_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_7_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_8__A 0x831F80
+#define SCU_RAM_QAM_WR_RSV_8__W 16
+#define SCU_RAM_QAM_WR_RSV_8__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_8__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_8_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_8_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_8_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_8_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_9__A 0x831F81
+#define SCU_RAM_QAM_WR_RSV_9__W 16
+#define SCU_RAM_QAM_WR_RSV_9__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_9__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_9_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_9_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_9_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_9_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_10__A 0x831F82
+#define SCU_RAM_QAM_WR_RSV_10__W 16
+#define SCU_RAM_QAM_WR_RSV_10__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_10__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_10_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_10_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_10_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_10_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FMHUM_TO__A 0x831F83
+#define SCU_RAM_QAM_FSM_FMHUM_TO__W 16
+#define SCU_RAM_QAM_FSM_FMHUM_TO__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FMHUM_TO__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__B 0
+#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__W 16
+#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT_NO_FMHUM_TO 0x0
+
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A 0x831F84
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__W 16
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__B 0
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__W 16
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A 0x831F85
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__W 16
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__B 0
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__W 16
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A 0x831F86
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__B 0
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A 0x831F87
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__B 0
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A 0x831F88
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__B 0
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A 0x831F89
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__B 0
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A 0x831F8A
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__B 0
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_STATE_TGT__A 0x831F8B
+#define SCU_RAM_QAM_FSM_STATE_TGT__W 4
+#define SCU_RAM_QAM_FSM_STATE_TGT__M 0xF
+#define SCU_RAM_QAM_FSM_STATE_TGT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__B 0
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__W 4
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__M 0xF
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_AMP 0x0
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_RATE 0x1
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_FREQ 0x2
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_UPRIGHT 0x3
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_PHASE 0x4
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_TRACKING_PHNOISE 0x5
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_TRACKING 0x6
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_TRACKING_BURST 0x7
+
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__A 0x831F8C
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__W 9
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__M 0x1FF
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__B 0
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__W 1
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__M 0x1
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_ATH__A 0x831F8D
+#define SCU_RAM_QAM_FSM_ATH__W 16
+#define SCU_RAM_QAM_FSM_ATH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_ATH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_ATH_BIT__B 0
+#define SCU_RAM_QAM_FSM_ATH_BIT__W 16
+#define SCU_RAM_QAM_FSM_ATH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_ATH_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RTH__A 0x831F8E
+#define SCU_RAM_QAM_FSM_RTH__W 16
+#define SCU_RAM_QAM_FSM_RTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_RTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_RTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_16 0x8C
+#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_32 0x50
+#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_64 0x4E
+#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_128 0x32
+#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_256 0x2D
+
+#define SCU_RAM_QAM_FSM_FTH__A 0x831F8F
+#define SCU_RAM_QAM_FSM_FTH__W 16
+#define SCU_RAM_QAM_FSM_FTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_FTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_FTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_16 0x32
+#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_32 0x1E
+#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_64 0x1E
+#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_128 0x14
+#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_256 0x14
+
+#define SCU_RAM_QAM_FSM_PTH__A 0x831F90
+#define SCU_RAM_QAM_FSM_PTH__W 16
+#define SCU_RAM_QAM_FSM_PTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_PTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_PTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_PTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_PTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_PTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_16 0xC8
+#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_32 0x96
+#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_64 0x8C
+#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_128 0x64
+#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_256 0x64
+
+#define SCU_RAM_QAM_FSM_MTH__A 0x831F91
+#define SCU_RAM_QAM_FSM_MTH__W 16
+#define SCU_RAM_QAM_FSM_MTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_MTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_MTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_MTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_MTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_MTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_16 0x5A
+#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_32 0x50
+#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_64 0x46
+#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_128 0x3C
+#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_256 0x50
+
+#define SCU_RAM_QAM_FSM_CTH__A 0x831F92
+#define SCU_RAM_QAM_FSM_CTH__W 16
+#define SCU_RAM_QAM_FSM_CTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_CTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_CTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_CTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_CTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_CTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_16 0xA0
+#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_32 0x8C
+#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_64 0x8C
+#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_128 0x8C
+#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_256 0x8C
+
+#define SCU_RAM_QAM_FSM_QTH__A 0x831F93
+#define SCU_RAM_QAM_FSM_QTH__W 16
+#define SCU_RAM_QAM_FSM_QTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_QTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_QTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_QTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_QTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_QTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_16 0xE6
+#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_32 0xAA
+#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_64 0xC3
+#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_128 0x8C
+#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_256 0x96
+
+#define SCU_RAM_QAM_FSM_RATE_LIM__A 0x831F94
+#define SCU_RAM_QAM_FSM_RATE_LIM__W 16
+#define SCU_RAM_QAM_FSM_RATE_LIM__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RATE_LIM__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__B 0
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__W 16
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_16 0x46
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_32 0x46
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_64 0x46
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_128 0x46
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_256 0x46
+
+#define SCU_RAM_QAM_FSM_FREQ_LIM__A 0x831F95
+#define SCU_RAM_QAM_FSM_FREQ_LIM__W 16
+#define SCU_RAM_QAM_FSM_FREQ_LIM__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FREQ_LIM__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__B 0
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__W 16
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_16 0x1E
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_32 0x14
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_64 0x28
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_128 0x8
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_256 0x28
+
+#define SCU_RAM_QAM_FSM_COUNT_LIM__A 0x831F96
+#define SCU_RAM_QAM_FSM_COUNT_LIM__W 16
+#define SCU_RAM_QAM_FSM_COUNT_LIM__M 0xFFFF
+#define SCU_RAM_QAM_FSM_COUNT_LIM__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__B 0
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__W 16
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_16 0x4
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_32 0x6
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_64 0x6
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_128 0x7
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_256 0x6
+
+#define SCU_RAM_QAM_LC_CA_COARSE__A 0x831F97
+#define SCU_RAM_QAM_LC_CA_COARSE__W 16
+#define SCU_RAM_QAM_LC_CA_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CA_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CA_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_CA_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_CA_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CA_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CA_MEDIUM__A 0x831F98
+#define SCU_RAM_QAM_LC_CA_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_CA_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_CA_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CA_FINE__A 0x831F99
+#define SCU_RAM_QAM_LC_CA_FINE__W 16
+#define SCU_RAM_QAM_LC_CA_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CA_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CA_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_CA_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_CA_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CA_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_COARSE__A 0x831F9A
+#define SCU_RAM_QAM_LC_CP_COARSE__W 16
+#define SCU_RAM_QAM_LC_CP_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CP_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_CP_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_CP_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CP_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_MEDIUM__A 0x831F9B
+#define SCU_RAM_QAM_LC_CP_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_CP_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_CP_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_FINE__A 0x831F9C
+#define SCU_RAM_QAM_LC_CP_FINE__W 16
+#define SCU_RAM_QAM_LC_CP_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CP_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_CP_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_CP_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CP_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_COARSE__A 0x831F9D
+#define SCU_RAM_QAM_LC_CI_COARSE__W 16
+#define SCU_RAM_QAM_LC_CI_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CI_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_CI_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_CI_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CI_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_MEDIUM__A 0x831F9E
+#define SCU_RAM_QAM_LC_CI_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_CI_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_CI_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_FINE__A 0x831F9F
+#define SCU_RAM_QAM_LC_CI_FINE__W 16
+#define SCU_RAM_QAM_LC_CI_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CI_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_CI_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_CI_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CI_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_COARSE__A 0x831FA0
+#define SCU_RAM_QAM_LC_EP_COARSE__W 16
+#define SCU_RAM_QAM_LC_EP_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_EP_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_EP_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_EP_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EP_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_MEDIUM__A 0x831FA1
+#define SCU_RAM_QAM_LC_EP_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_EP_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_EP_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_FINE__A 0x831FA2
+#define SCU_RAM_QAM_LC_EP_FINE__W 16
+#define SCU_RAM_QAM_LC_EP_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_EP_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_EP_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_EP_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EP_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_COARSE__A 0x831FA3
+#define SCU_RAM_QAM_LC_EI_COARSE__W 16
+#define SCU_RAM_QAM_LC_EI_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_EI_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_EI_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_EI_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EI_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_MEDIUM__A 0x831FA4
+#define SCU_RAM_QAM_LC_EI_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_EI_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_EI_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_FINE__A 0x831FA5
+#define SCU_RAM_QAM_LC_EI_FINE__W 16
+#define SCU_RAM_QAM_LC_EI_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_EI_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_EI_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_EI_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EI_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_COARSE__A 0x831FA6
+#define SCU_RAM_QAM_LC_CF_COARSE__W 16
+#define SCU_RAM_QAM_LC_CF_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_CF_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_CF_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_MEDIUM__A 0x831FA7
+#define SCU_RAM_QAM_LC_CF_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_CF_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_FINE__A 0x831FA8
+#define SCU_RAM_QAM_LC_CF_FINE__W 16
+#define SCU_RAM_QAM_LC_CF_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_CF_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_CF_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_COARSE__A 0x831FA9
+#define SCU_RAM_QAM_LC_CF1_COARSE__W 16
+#define SCU_RAM_QAM_LC_CF1_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF1_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_MEDIUM__A 0x831FAA
+#define SCU_RAM_QAM_LC_CF1_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_CF1_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF1_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_FINE__A 0x831FAB
+#define SCU_RAM_QAM_LC_CF1_FINE__W 16
+#define SCU_RAM_QAM_LC_CF1_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF1_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_CF1_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_CF1_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF1_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_SL_SIG_POWER__A 0x831FAC
+#define SCU_RAM_QAM_SL_SIG_POWER__W 16
+#define SCU_RAM_QAM_SL_SIG_POWER__M 0xFFFF
+#define SCU_RAM_QAM_SL_SIG_POWER__PRE 0x0
+
+#define SCU_RAM_QAM_SL_SIG_POWER_BIT__B 0
+#define SCU_RAM_QAM_SL_SIG_POWER_BIT__W 16
+#define SCU_RAM_QAM_SL_SIG_POWER_BIT__M 0xFFFF
+#define SCU_RAM_QAM_SL_SIG_POWER_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD0__A 0x831FAD
+#define SCU_RAM_QAM_EQ_CMA_RAD0__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD0__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD0__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_64 0x3418
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_128 0x1814
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_256 0x2CEE
+
+#define SCU_RAM_QAM_EQ_CMA_RAD1__A 0x831FAE
+#define SCU_RAM_QAM_EQ_CMA_RAD1__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD1__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD1__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_64 0x314A
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_128 0x19C6
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_256 0x2F34
+
+#define SCU_RAM_QAM_EQ_CMA_RAD2__A 0x831FAF
+#define SCU_RAM_QAM_EQ_CMA_RAD2__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD2__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD2__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_64 0x2ED4
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_128 0x18FA
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_256 0x30FF
+
+#define SCU_RAM_QAM_EQ_CMA_RAD3__A 0x831FB0
+#define SCU_RAM_QAM_EQ_CMA_RAD3__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD3__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD3__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_64 0x35F1
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_128 0x1909
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_256 0x3283
+
+#define SCU_RAM_QAM_EQ_CMA_RAD4__A 0x831FB1
+#define SCU_RAM_QAM_EQ_CMA_RAD4__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD4__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD4__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_64 0x35F1
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_128 0x1A00
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_256 0x353D
+
+#define SCU_RAM_QAM_EQ_CMA_RAD5__A 0x831FB2
+#define SCU_RAM_QAM_EQ_CMA_RAD5__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD5__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD5__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_64 0x3CF9
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_128 0x1C46
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_256 0x3C19
+
+#define SCU_RAM_QAM_CTL_ENA__A 0x831FB3
+#define SCU_RAM_QAM_CTL_ENA__W 16
+#define SCU_RAM_QAM_CTL_ENA__M 0xFFFF
+#define SCU_RAM_QAM_CTL_ENA__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_AMP__B 0
+#define SCU_RAM_QAM_CTL_ENA_AMP__W 1
+#define SCU_RAM_QAM_CTL_ENA_AMP__M 0x1
+#define SCU_RAM_QAM_CTL_ENA_AMP__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_ACQ__B 1
+#define SCU_RAM_QAM_CTL_ENA_ACQ__W 1
+#define SCU_RAM_QAM_CTL_ENA_ACQ__M 0x2
+#define SCU_RAM_QAM_CTL_ENA_ACQ__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_EQU__B 2
+#define SCU_RAM_QAM_CTL_ENA_EQU__W 1
+#define SCU_RAM_QAM_CTL_ENA_EQU__M 0x4
+#define SCU_RAM_QAM_CTL_ENA_EQU__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_SLC__B 3
+#define SCU_RAM_QAM_CTL_ENA_SLC__W 1
+#define SCU_RAM_QAM_CTL_ENA_SLC__M 0x8
+#define SCU_RAM_QAM_CTL_ENA_SLC__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_LC__B 4
+#define SCU_RAM_QAM_CTL_ENA_LC__W 1
+#define SCU_RAM_QAM_CTL_ENA_LC__M 0x10
+#define SCU_RAM_QAM_CTL_ENA_LC__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_AGC__B 5
+#define SCU_RAM_QAM_CTL_ENA_AGC__W 1
+#define SCU_RAM_QAM_CTL_ENA_AGC__M 0x20
+#define SCU_RAM_QAM_CTL_ENA_AGC__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_FEC__B 6
+#define SCU_RAM_QAM_CTL_ENA_FEC__W 1
+#define SCU_RAM_QAM_CTL_ENA_FEC__M 0x40
+#define SCU_RAM_QAM_CTL_ENA_FEC__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_AXIS__B 7
+#define SCU_RAM_QAM_CTL_ENA_AXIS__W 1
+#define SCU_RAM_QAM_CTL_ENA_AXIS__M 0x80
+#define SCU_RAM_QAM_CTL_ENA_AXIS__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_FMHUM__B 8
+#define SCU_RAM_QAM_CTL_ENA_FMHUM__W 1
+#define SCU_RAM_QAM_CTL_ENA_FMHUM__M 0x100
+#define SCU_RAM_QAM_CTL_ENA_FMHUM__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_EQTIME__B 9
+#define SCU_RAM_QAM_CTL_ENA_EQTIME__W 1
+#define SCU_RAM_QAM_CTL_ENA_EQTIME__M 0x200
+#define SCU_RAM_QAM_CTL_ENA_EQTIME__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_EXTLCK__B 10
+#define SCU_RAM_QAM_CTL_ENA_EXTLCK__W 1
+#define SCU_RAM_QAM_CTL_ENA_EXTLCK__M 0x400
+#define SCU_RAM_QAM_CTL_ENA_EXTLCK__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_1__A 0x831FB4
+#define SCU_RAM_QAM_WR_RSV_1__W 16
+#define SCU_RAM_QAM_WR_RSV_1__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_1__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_1_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_1_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_1_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_1_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_2__A 0x831FB5
+#define SCU_RAM_QAM_WR_RSV_2__W 16
+#define SCU_RAM_QAM_WR_RSV_2__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_2__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_2_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_2_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_2_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_2_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_3__A 0x831FB6
+#define SCU_RAM_QAM_WR_RSV_3__W 16
+#define SCU_RAM_QAM_WR_RSV_3__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_3__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_3_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_3_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_3_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_3_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__A 0x831FB7
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__W 3
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__M 0x7
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__B 0
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__W 3
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__M 0x7
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__PRE 0x0
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_UNKNOWN 0x0
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_16 0x3
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_32 0x4
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_64 0x5
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_128 0x6
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_256 0x7
+
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__A 0x831FB8
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__W 8
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__M 0xFF
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__B 0
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__W 8
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__M 0xFF
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__PRE 0x0
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J1 0x0
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J1_V2 0x1
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J2 0x2
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I64_J2 0x3
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J3 0x4
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I32_J4 0x5
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J4 0x6
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I16_J8 0x7
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J5 0x8
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I8_J16 0x9
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J6 0xA
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J7 0xC
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J8 0xE
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I12_J17 0x10
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I5_J4 0x11
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_UNKNOWN 0xFE
+
+#define SCU_RAM_QAM_RD_RSV_4__A 0x831FB9
+#define SCU_RAM_QAM_RD_RSV_4__W 16
+#define SCU_RAM_QAM_RD_RSV_4__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_4__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_4_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_4_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_4_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_4_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LOCKED__A 0x831FBA
+#define SCU_RAM_QAM_LOCKED__W 16
+#define SCU_RAM_QAM_LOCKED__M 0xFFFF
+#define SCU_RAM_QAM_LOCKED__PRE 0x0
+
+#define SCU_RAM_QAM_LOCKED_INTLEVEL__B 0
+#define SCU_RAM_QAM_LOCKED_INTLEVEL__W 8
+#define SCU_RAM_QAM_LOCKED_INTLEVEL__M 0xFF
+#define SCU_RAM_QAM_LOCKED_INTLEVEL__PRE 0x0
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_NOT_LOCKED 0x0
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_AMP_OK 0x1
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_RATE_OK 0x2
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_FREQ_OK 0x3
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_UPRIGHT_OK 0x4
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_PHNOISE_OK 0x5
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_TRACK_OK 0x6
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_IMPNOISE_OK 0x7
+
+#define SCU_RAM_QAM_LOCKED_LOCKED__B 8
+#define SCU_RAM_QAM_LOCKED_LOCKED__W 8
+#define SCU_RAM_QAM_LOCKED_LOCKED__M 0xFF00
+#define SCU_RAM_QAM_LOCKED_LOCKED__PRE 0x0
+#define SCU_RAM_QAM_LOCKED_LOCKED_NOT_LOCKED 0x0
+#define SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED 0x4000
+#define SCU_RAM_QAM_LOCKED_LOCKED_LOCKED 0x8000
+#define SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK 0xC000
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI__A 0x831FBB
+#define SCU_RAM_QAM_EVENTS_OCC_HI__W 16
+#define SCU_RAM_QAM_EVENTS_OCC_HI__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_OCC_HI__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__B 0
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__M 0x1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__B 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__M 0x2
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__B 2
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__M 0x4
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__B 3
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__M 0x8
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__B 4
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__M 0x10
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__B 5
+#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__M 0x20
+#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__B 6
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__M 0x40
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__B 7
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__M 0x80
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__B 8
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__M 0x100
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__B 9
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__M 0x200
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__B 10
+#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__M 0x400
+#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__B 11
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__M 0x800
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__B 12
+#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__W 4
+#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__M 0xF000
+#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO__A 0x831FBC
+#define SCU_RAM_QAM_EVENTS_OCC_LO__W 16
+#define SCU_RAM_QAM_EVENTS_OCC_LO__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_OCC_LO__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__B 0
+#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__M 0x1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__B 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__M 0x2
+#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__B 2
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__M 0x4
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__B 3
+#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__M 0x8
+#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__B 4
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__M 0x10
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__B 5
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__M 0x20
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__B 6
+#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__M 0x40
+#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__B 7
+#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__M 0x80
+#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__B 8
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__M 0x100
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__B 9
+#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__M 0x200
+#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__B 10
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__M 0x400
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__B 11
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__M 0x800
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__B 12
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__M 0x1000
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__B 13
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__M 0x2000
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__B 14
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__M 0x4000
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__B 15
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__M 0x8000
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_SCHED_HI__A 0x831FBD
+#define SCU_RAM_QAM_EVENTS_SCHED_HI__W 16
+#define SCU_RAM_QAM_EVENTS_SCHED_HI__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_SCHED_HI__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__B 0
+#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__W 16
+#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_SCHED_LO__A 0x831FBE
+#define SCU_RAM_QAM_EVENTS_SCHED_LO__W 16
+#define SCU_RAM_QAM_EVENTS_SCHED_LO__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_SCHED_LO__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__B 0
+#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__W 16
+#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_TASKLETS_SCHED__A 0x831FBF
+#define SCU_RAM_QAM_TASKLETS_SCHED__W 16
+#define SCU_RAM_QAM_TASKLETS_SCHED__M 0xFFFF
+#define SCU_RAM_QAM_TASKLETS_SCHED__PRE 0x0
+
+#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__B 0
+#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__W 16
+#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__M 0xFFFF
+#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_TASKLETS_RUN__A 0x831FC0
+#define SCU_RAM_QAM_TASKLETS_RUN__W 16
+#define SCU_RAM_QAM_TASKLETS_RUN__M 0xFFFF
+#define SCU_RAM_QAM_TASKLETS_RUN__PRE 0x0
+
+#define SCU_RAM_QAM_TASKLETS_RUN_BIT__B 0
+#define SCU_RAM_QAM_TASKLETS_RUN_BIT__W 16
+#define SCU_RAM_QAM_TASKLETS_RUN_BIT__M 0xFFFF
+#define SCU_RAM_QAM_TASKLETS_RUN_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__A 0x831FC1
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__W 16
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__M 0xFFFF
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__B 0
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__W 16
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__M 0xFFFF
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__A 0x831FC2
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__W 16
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__M 0xFFFF
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__B 0
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__W 16
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__M 0xFFFF
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_5__A 0x831FC3
+#define SCU_RAM_QAM_RD_RSV_5__W 16
+#define SCU_RAM_QAM_RD_RSV_5__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_5__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_5_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_5_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_5_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_5_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_6__A 0x831FC4
+#define SCU_RAM_QAM_RD_RSV_6__W 16
+#define SCU_RAM_QAM_RD_RSV_6__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_6__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_6_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_6_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_6_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_6_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_7__A 0x831FC5
+#define SCU_RAM_QAM_RD_RSV_7__W 16
+#define SCU_RAM_QAM_RD_RSV_7__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_7__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_7_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_7_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_7_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_7_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_8__A 0x831FC6
+#define SCU_RAM_QAM_RD_RSV_8__W 16
+#define SCU_RAM_QAM_RD_RSV_8__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_8__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_8_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_8_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_8_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_8_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_9__A 0x831FC7
+#define SCU_RAM_QAM_RD_RSV_9__W 16
+#define SCU_RAM_QAM_RD_RSV_9__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_9__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_9_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_9_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_9_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_9_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_10__A 0x831FC8
+#define SCU_RAM_QAM_RD_RSV_10__W 16
+#define SCU_RAM_QAM_RD_RSV_10__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_10__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_10_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_10_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_10_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_10_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_AGC_TPOW_OFFS__A 0x831FC9
+#define SCU_RAM_QAM_AGC_TPOW_OFFS__W 16
+#define SCU_RAM_QAM_AGC_TPOW_OFFS__M 0xFFFF
+#define SCU_RAM_QAM_AGC_TPOW_OFFS__PRE 0x0
+
+#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__B 0
+#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__W 16
+#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__M 0xFFFF
+#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_STATE__A 0x831FCA
+#define SCU_RAM_QAM_FSM_STATE__W 4
+#define SCU_RAM_QAM_FSM_STATE__M 0xF
+#define SCU_RAM_QAM_FSM_STATE__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_STATE_BIT__B 0
+#define SCU_RAM_QAM_FSM_STATE_BIT__W 4
+#define SCU_RAM_QAM_FSM_STATE_BIT__M 0xF
+#define SCU_RAM_QAM_FSM_STATE_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_AMP 0x0
+#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_RATE 0x1
+#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_FREQ 0x2
+#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_UPRIGHT 0x3
+#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_PHASE 0x4
+#define SCU_RAM_QAM_FSM_STATE_BIT_TRACKING_PHNOISE 0x5
+#define SCU_RAM_QAM_FSM_STATE_BIT_TRACKING 0x6
+#define SCU_RAM_QAM_FSM_STATE_BIT_TRACKING_BURST 0x7
+
+#define SCU_RAM_QAM_FSM_STATE_NEW__A 0x831FCB
+#define SCU_RAM_QAM_FSM_STATE_NEW__W 4
+#define SCU_RAM_QAM_FSM_STATE_NEW__M 0xF
+#define SCU_RAM_QAM_FSM_STATE_NEW__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__B 0
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__W 4
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__M 0xF
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_AMP 0x0
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_RATE 0x1
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_FREQ 0x2
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_UPRIGHT 0x3
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_PHASE 0x4
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_TRACKING_PHNOISE 0x5
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_TRACKING 0x6
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_TRACKING_BURST 0x7
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS__A 0x831FCC
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS__W 9
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS__M 0x1FF
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__B 0
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__M 0x1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__B 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__M 0x2
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__B 2
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__M 0x4
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__B 3
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__M 0x8
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__B 4
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__M 0x10
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__B 5
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__M 0x20
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__B 6
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__M 0x40
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__B 7
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__M 0x80
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__B 8
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__M 0x100
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RATE_VARIATION__A 0x831FCD
+#define SCU_RAM_QAM_FSM_RATE_VARIATION__W 16
+#define SCU_RAM_QAM_FSM_RATE_VARIATION__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RATE_VARIATION__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__B 0
+#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__W 16
+#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION__A 0x831FCE
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION__W 16
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__B 0
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__W 16
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_ERR_STATE__A 0x831FCF
+#define SCU_RAM_QAM_ERR_STATE__W 4
+#define SCU_RAM_QAM_ERR_STATE__M 0xF
+#define SCU_RAM_QAM_ERR_STATE__PRE 0x0
+
+#define SCU_RAM_QAM_ERR_STATE_BIT__B 0
+#define SCU_RAM_QAM_ERR_STATE_BIT__W 4
+#define SCU_RAM_QAM_ERR_STATE_BIT__M 0xF
+#define SCU_RAM_QAM_ERR_STATE_BIT__PRE 0x0
+#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_AMP 0x0
+#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_RATE 0x1
+#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_FREQ 0x2
+#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_UPRIGHT 0x3
+#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_PHASE 0x4
+#define SCU_RAM_QAM_ERR_STATE_BIT_TRACKING_PHNOISE 0x5
+#define SCU_RAM_QAM_ERR_STATE_BIT_TRACKING 0x6
+#define SCU_RAM_QAM_ERR_STATE_BIT_TRACKING_BURST 0x7
+
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS__A 0x831FD0
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS__W 9
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS__M 0x1FF
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS__PRE 0x0
+
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__B 0
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__W 1
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__M 0x1
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_LOCK__A 0x831FD1
+#define SCU_RAM_QAM_EQ_LOCK__W 1
+#define SCU_RAM_QAM_EQ_LOCK__M 0x1
+#define SCU_RAM_QAM_EQ_LOCK__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_LOCK_BIT__B 0
+#define SCU_RAM_QAM_EQ_LOCK_BIT__W 1
+#define SCU_RAM_QAM_EQ_LOCK_BIT__M 0x1
+#define SCU_RAM_QAM_EQ_LOCK_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_STATE__A 0x831FD2
+#define SCU_RAM_QAM_EQ_STATE__W 16
+#define SCU_RAM_QAM_EQ_STATE__M 0xFFFF
+#define SCU_RAM_QAM_EQ_STATE__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_STATE_BIT__B 0
+#define SCU_RAM_QAM_EQ_STATE_BIT__W 16
+#define SCU_RAM_QAM_EQ_STATE_BIT__M 0xFFFF
+#define SCU_RAM_QAM_EQ_STATE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_0__A 0x831FD3
+#define SCU_RAM_QAM_RD_RSV_0__W 16
+#define SCU_RAM_QAM_RD_RSV_0__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_0__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_0_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_0_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_0_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_0_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_1__A 0x831FD4
+#define SCU_RAM_QAM_RD_RSV_1__W 16
+#define SCU_RAM_QAM_RD_RSV_1__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_1__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_1_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_1_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_1_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_1_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_2__A 0x831FD5
+#define SCU_RAM_QAM_RD_RSV_2__W 16
+#define SCU_RAM_QAM_RD_RSV_2__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_2__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_2_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_2_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_2_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_2_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_3__A 0x831FD6
+#define SCU_RAM_QAM_RD_RSV_3__W 16
+#define SCU_RAM_QAM_RD_RSV_3__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_3__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_3_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_3_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_3_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_3_BIT__PRE 0x0
+
+#define SCU_RAM_VSB_CTL_MODE__A 0x831FD7
+#define SCU_RAM_VSB_CTL_MODE__W 2
+#define SCU_RAM_VSB_CTL_MODE__M 0x3
+#define SCU_RAM_VSB_CTL_MODE__PRE 0x0
+
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__B 0
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__W 1
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__M 0x1
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__PRE 0x0
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC_OFF 0x0
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC_ON 0x1
+
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__B 1
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__W 1
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__M 0x2
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__PRE 0x0
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON_OFF 0x0
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON_ON 0x2
+
+#define SCU_RAM_VSB_NOTCH_THRESHOLD__A 0x831FD8
+#define SCU_RAM_VSB_NOTCH_THRESHOLD__W 16
+#define SCU_RAM_VSB_NOTCH_THRESHOLD__M 0xFFFF
+#define SCU_RAM_VSB_NOTCH_THRESHOLD__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_0__A 0x831FD9
+#define SCU_RAM_VSB_RSV_0__W 16
+#define SCU_RAM_VSB_RSV_0__M 0xFFFF
+#define SCU_RAM_VSB_RSV_0__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_1__A 0x831FDA
+#define SCU_RAM_VSB_RSV_1__W 16
+#define SCU_RAM_VSB_RSV_1__M 0xFFFF
+#define SCU_RAM_VSB_RSV_1__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_2__A 0x831FDB
+#define SCU_RAM_VSB_RSV_2__W 16
+#define SCU_RAM_VSB_RSV_2__M 0xFFFF
+#define SCU_RAM_VSB_RSV_2__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_3__A 0x831FDC
+#define SCU_RAM_VSB_RSV_3__W 16
+#define SCU_RAM_VSB_RSV_3__M 0xFFFF
+#define SCU_RAM_VSB_RSV_3__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_4__A 0x831FDD
+#define SCU_RAM_VSB_RSV_4__W 16
+#define SCU_RAM_VSB_RSV_4__M 0xFFFF
+#define SCU_RAM_VSB_RSV_4__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_5__A 0x831FDE
+#define SCU_RAM_VSB_RSV_5__W 16
+#define SCU_RAM_VSB_RSV_5__M 0xFFFF
+#define SCU_RAM_VSB_RSV_5__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_6__A 0x831FDF
+#define SCU_RAM_VSB_RSV_6__W 16
+#define SCU_RAM_VSB_RSV_6__M 0xFFFF
+#define SCU_RAM_VSB_RSV_6__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_7__A 0x831FE0
+#define SCU_RAM_VSB_RSV_7__W 16
+#define SCU_RAM_VSB_RSV_7__M 0xFFFF
+#define SCU_RAM_VSB_RSV_7__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_8__A 0x831FE1
+#define SCU_RAM_VSB_RSV_8__W 16
+#define SCU_RAM_VSB_RSV_8__M 0xFFFF
+#define SCU_RAM_VSB_RSV_8__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_9__A 0x831FE2
+#define SCU_RAM_VSB_RSV_9__W 16
+#define SCU_RAM_VSB_RSV_9__M 0xFFFF
+#define SCU_RAM_VSB_RSV_9__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_10__A 0x831FE3
+#define SCU_RAM_VSB_RSV_10__W 16
+#define SCU_RAM_VSB_RSV_10__M 0xFFFF
+#define SCU_RAM_VSB_RSV_10__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_11__A 0x831FE4
+#define SCU_RAM_VSB_RSV_11__W 16
+#define SCU_RAM_VSB_RSV_11__M 0xFFFF
+#define SCU_RAM_VSB_RSV_11__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_12__A 0x831FE5
+#define SCU_RAM_VSB_RSV_12__W 16
+#define SCU_RAM_VSB_RSV_12__M 0xFFFF
+#define SCU_RAM_VSB_RSV_12__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_13__A 0x831FE6
+#define SCU_RAM_VSB_RSV_13__W 16
+#define SCU_RAM_VSB_RSV_13__M 0xFFFF
+#define SCU_RAM_VSB_RSV_13__PRE 0x0
+
+#define SCU_RAM_VSB_AGC_POW_TGT__A 0x831FE7
+#define SCU_RAM_VSB_AGC_POW_TGT__W 15
+#define SCU_RAM_VSB_AGC_POW_TGT__M 0x7FFF
+#define SCU_RAM_VSB_AGC_POW_TGT__PRE 0x0
+
+#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__A 0x831FE8
+#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__W 8
+#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__M 0xFF
+#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__PRE 0x0
+
+#define SCU_RAM_VSB_FIELD_NUMBER__A 0x831FE9
+#define SCU_RAM_VSB_FIELD_NUMBER__W 9
+#define SCU_RAM_VSB_FIELD_NUMBER__M 0x1FF
+#define SCU_RAM_VSB_FIELD_NUMBER__PRE 0x0
+
+#define SCU_RAM_VSB_SEGMENT_NUMBER__A 0x831FEA
+#define SCU_RAM_VSB_SEGMENT_NUMBER__W 10
+#define SCU_RAM_VSB_SEGMENT_NUMBER__M 0x3FF
+#define SCU_RAM_VSB_SEGMENT_NUMBER__PRE 0x0
+
+#define SCU_RAM_DRIVER_VER_HI__A 0x831FEB
+#define SCU_RAM_DRIVER_VER_HI__W 16
+#define SCU_RAM_DRIVER_VER_HI__M 0xFFFF
+#define SCU_RAM_DRIVER_VER_HI__PRE 0x0
+
+#define SCU_RAM_DRIVER_VER_LO__A 0x831FEC
+#define SCU_RAM_DRIVER_VER_LO__W 16
+#define SCU_RAM_DRIVER_VER_LO__M 0xFFFF
+#define SCU_RAM_DRIVER_VER_LO__PRE 0x0
+
+#define SCU_RAM_PARAM_15__A 0x831FED
+#define SCU_RAM_PARAM_15__W 16
+#define SCU_RAM_PARAM_15__M 0xFFFF
+#define SCU_RAM_PARAM_15__PRE 0x0
+
+#define SCU_RAM_PARAM_14__A 0x831FEE
+#define SCU_RAM_PARAM_14__W 16
+#define SCU_RAM_PARAM_14__M 0xFFFF
+#define SCU_RAM_PARAM_14__PRE 0x0
+
+#define SCU_RAM_PARAM_13__A 0x831FEF
+#define SCU_RAM_PARAM_13__W 16
+#define SCU_RAM_PARAM_13__M 0xFFFF
+#define SCU_RAM_PARAM_13__PRE 0x0
+
+#define SCU_RAM_PARAM_12__A 0x831FF0
+#define SCU_RAM_PARAM_12__W 16
+#define SCU_RAM_PARAM_12__M 0xFFFF
+#define SCU_RAM_PARAM_12__PRE 0x0
+
+#define SCU_RAM_PARAM_11__A 0x831FF1
+#define SCU_RAM_PARAM_11__W 16
+#define SCU_RAM_PARAM_11__M 0xFFFF
+#define SCU_RAM_PARAM_11__PRE 0x0
+
+#define SCU_RAM_PARAM_10__A 0x831FF2
+#define SCU_RAM_PARAM_10__W 16
+#define SCU_RAM_PARAM_10__M 0xFFFF
+#define SCU_RAM_PARAM_10__PRE 0x0
+
+#define SCU_RAM_PARAM_9__A 0x831FF3
+#define SCU_RAM_PARAM_9__W 16
+#define SCU_RAM_PARAM_9__M 0xFFFF
+#define SCU_RAM_PARAM_9__PRE 0x0
+
+#define SCU_RAM_PARAM_8__A 0x831FF4
+#define SCU_RAM_PARAM_8__W 16
+#define SCU_RAM_PARAM_8__M 0xFFFF
+#define SCU_RAM_PARAM_8__PRE 0x0
+
+#define SCU_RAM_PARAM_7__A 0x831FF5
+#define SCU_RAM_PARAM_7__W 16
+#define SCU_RAM_PARAM_7__M 0xFFFF
+#define SCU_RAM_PARAM_7__PRE 0x0
+
+#define SCU_RAM_PARAM_6__A 0x831FF6
+#define SCU_RAM_PARAM_6__W 16
+#define SCU_RAM_PARAM_6__M 0xFFFF
+#define SCU_RAM_PARAM_6__PRE 0x0
+
+#define SCU_RAM_PARAM_5__A 0x831FF7
+#define SCU_RAM_PARAM_5__W 16
+#define SCU_RAM_PARAM_5__M 0xFFFF
+#define SCU_RAM_PARAM_5__PRE 0x0
+
+#define SCU_RAM_PARAM_4__A 0x831FF8
+#define SCU_RAM_PARAM_4__W 16
+#define SCU_RAM_PARAM_4__M 0xFFFF
+#define SCU_RAM_PARAM_4__PRE 0x0
+
+#define SCU_RAM_PARAM_3__A 0x831FF9
+#define SCU_RAM_PARAM_3__W 16
+#define SCU_RAM_PARAM_3__M 0xFFFF
+#define SCU_RAM_PARAM_3__PRE 0x0
+
+#define SCU_RAM_PARAM_2__A 0x831FFA
+#define SCU_RAM_PARAM_2__W 16
+#define SCU_RAM_PARAM_2__M 0xFFFF
+#define SCU_RAM_PARAM_2__PRE 0x0
+
+#define SCU_RAM_PARAM_1__A 0x831FFB
+#define SCU_RAM_PARAM_1__W 16
+#define SCU_RAM_PARAM_1__M 0xFFFF
+#define SCU_RAM_PARAM_1__PRE 0x0
+#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_NOT_LOCKED 0x0
+#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_DEMOD_LOCKED 0x4000
+#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_LOCKED 0x8000
+#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_NEVER_LOCK 0xC000
+
+#define SCU_RAM_PARAM_0__A 0x831FFC
+#define SCU_RAM_PARAM_0__W 16
+#define SCU_RAM_PARAM_0__M 0xFFFF
+#define SCU_RAM_PARAM_0__PRE 0x0
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_MN_STANDARD 0x2
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_B_STANDARD 0x103
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_G_STANDARD 0x3
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_DK_STANDARD 0x4
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_L_STANDARD 0x9
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_LP_STANDARD 0x109
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_I_STANDARD 0xA
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_FM_STANDARD 0x40
+#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_A 0x0
+#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_B 0x1
+#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_C 0x2
+#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_D 0x3
+#define SCU_RAM_PARAM_0_RESULT_OK 0x0
+#define SCU_RAM_PARAM_0_RESULT_UNKCMD 0xFFFF
+#define SCU_RAM_PARAM_0_RESULT_UNKSTD 0xFFFE
+#define SCU_RAM_PARAM_0_RESULT_INVPAR 0xFFFD
+#define SCU_RAM_PARAM_0_RESULT_SIZE 0xFFFC
+
+#define SCU_RAM_COMMAND__A 0x831FFD
+#define SCU_RAM_COMMAND__W 16
+#define SCU_RAM_COMMAND__M 0xFFFF
+#define SCU_RAM_COMMAND__PRE 0x0
+#define SCU_RAM_COMMAND_CMD_DEMOD_RESET 0x1
+#define SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV 0x2
+#define SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM 0x3
+#define SCU_RAM_COMMAND_CMD_DEMOD_START 0x4
+#define SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK 0x5
+#define SCU_RAM_COMMAND_CMD_DEMOD_GET_PARAM 0x6
+#define SCU_RAM_COMMAND_CMD_DEMOD_HOLD 0x7
+#define SCU_RAM_COMMAND_CMD_DEMOD_RESUME 0x8
+#define SCU_RAM_COMMAND_CMD_DEMOD_STOP 0x9
+#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_ACTIVATE 0x80
+#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_INACTIVATE 0x81
+#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_SIGNAL 0x82
+#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_MONITOR 0x83
+#define SCU_RAM_COMMAND_CMD_STD_QAM_TSK_ENABLE 0x84
+#define SCU_RAM_COMMAND_CMD_STD_QAM_FSM_SET_STATE 0x85
+#define SCU_RAM_COMMAND_CMD_DEBUG_GET_IRQ_REGS 0x80
+#define SCU_RAM_COMMAND_CMD_DEBUG_HTOL 0x81
+#define SCU_RAM_COMMAND_CMD_DEBUG_GET_STACK_POINTER 0x82
+#define SCU_RAM_COMMAND_CMD_DEBUG_START_STACK_CHECK 0x83
+#define SCU_RAM_COMMAND_CMD_DEBUG_STOP_STACK_CHECK 0x84
+#define SCU_RAM_COMMAND_CMD_ADMIN_NOP 0xFF
+#define SCU_RAM_COMMAND_CMD_ADMIN_GET_VERSION 0xFE
+#define SCU_RAM_COMMAND_CMD_ADMIN_GET_JTAG_VERSION 0xFD
+#define SCU_RAM_COMMAND_CMD_AUX_SCU_ATOMIC_ACCESS 0xC0
+
+#define SCU_RAM_COMMAND_STANDARD__B 8
+#define SCU_RAM_COMMAND_STANDARD__W 8
+#define SCU_RAM_COMMAND_STANDARD__M 0xFF00
+#define SCU_RAM_COMMAND_STANDARD__PRE 0x0
+#define SCU_RAM_COMMAND_STANDARD_ATV 0x100
+#define SCU_RAM_COMMAND_STANDARD_QAM 0x200
+#define SCU_RAM_COMMAND_STANDARD_VSB 0x300
+#define SCU_RAM_COMMAND_STANDARD_OFDM 0x400
+#define SCU_RAM_COMMAND_STANDARD_OOB 0x8000
+#define SCU_RAM_COMMAND_STANDARD_TOP 0xFF00
+
+#define SCU_RAM_VERSION_HI__A 0x831FFE
+#define SCU_RAM_VERSION_HI__W 16
+#define SCU_RAM_VERSION_HI__M 0xFFFF
+#define SCU_RAM_VERSION_HI__PRE 0x0
+
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__B 12
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__W 4
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__M 0xF000
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__PRE 0x0
+
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__B 8
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__W 4
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__M 0xF00
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__PRE 0x0
+
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__B 4
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__W 4
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__M 0xF0
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__PRE 0x0
+
+#define SCU_RAM_VERSION_HI_VER_MINOR_N1__B 0
+#define SCU_RAM_VERSION_HI_VER_MINOR_N1__W 4
+#define SCU_RAM_VERSION_HI_VER_MINOR_N1__M 0xF
+#define SCU_RAM_VERSION_HI_VER_MINOR_N1__PRE 0x0
+
+#define SCU_RAM_VERSION_LO__A 0x831FFF
+#define SCU_RAM_VERSION_LO__W 16
+#define SCU_RAM_VERSION_LO__M 0xFFFF
+#define SCU_RAM_VERSION_LO__PRE 0x0
+
+#define SCU_RAM_VERSION_LO_VER_PATCH_N4__B 12
+#define SCU_RAM_VERSION_LO_VER_PATCH_N4__W 4
+#define SCU_RAM_VERSION_LO_VER_PATCH_N4__M 0xF000
+#define SCU_RAM_VERSION_LO_VER_PATCH_N4__PRE 0x0
+
+#define SCU_RAM_VERSION_LO_VER_PATCH_N3__B 8
+#define SCU_RAM_VERSION_LO_VER_PATCH_N3__W 4
+#define SCU_RAM_VERSION_LO_VER_PATCH_N3__M 0xF00
+#define SCU_RAM_VERSION_LO_VER_PATCH_N3__PRE 0x0
+
+#define SCU_RAM_VERSION_LO_VER_PATCH_N2__B 4
+#define SCU_RAM_VERSION_LO_VER_PATCH_N2__W 4
+#define SCU_RAM_VERSION_LO_VER_PATCH_N2__M 0xF0
+#define SCU_RAM_VERSION_LO_VER_PATCH_N2__PRE 0x0
+
+#define SCU_RAM_VERSION_LO_VER_PATCH_N1__B 0
+#define SCU_RAM_VERSION_LO_VER_PATCH_N1__W 4
+#define SCU_RAM_VERSION_LO_VER_PATCH_N1__M 0xF
+#define SCU_RAM_VERSION_LO_VER_PATCH_N1__PRE 0x0
+
+#define SIO_COMM_EXEC__A 0x400000
+#define SIO_COMM_EXEC__W 2
+#define SIO_COMM_EXEC__M 0x3
+#define SIO_COMM_EXEC__PRE 0x0
+#define SIO_COMM_EXEC_STOP 0x0
+#define SIO_COMM_EXEC_ACTIVE 0x1
+#define SIO_COMM_EXEC_HOLD 0x2
+
+#define SIO_COMM_STATE__A 0x400001
+#define SIO_COMM_STATE__W 16
+#define SIO_COMM_STATE__M 0xFFFF
+#define SIO_COMM_STATE__PRE 0x0
+#define SIO_COMM_MB__A 0x400002
+#define SIO_COMM_MB__W 16
+#define SIO_COMM_MB__M 0xFFFF
+#define SIO_COMM_MB__PRE 0x0
+#define SIO_COMM_INT_REQ__A 0x400003
+#define SIO_COMM_INT_REQ__W 16
+#define SIO_COMM_INT_REQ__M 0xFFFF
+#define SIO_COMM_INT_REQ__PRE 0x0
+
+#define SIO_COMM_INT_REQ_HI_REQ__B 0
+#define SIO_COMM_INT_REQ_HI_REQ__W 1
+#define SIO_COMM_INT_REQ_HI_REQ__M 0x1
+#define SIO_COMM_INT_REQ_HI_REQ__PRE 0x0
+
+#define SIO_COMM_INT_REQ_SA_REQ__B 1
+#define SIO_COMM_INT_REQ_SA_REQ__W 1
+#define SIO_COMM_INT_REQ_SA_REQ__M 0x2
+#define SIO_COMM_INT_REQ_SA_REQ__PRE 0x0
+
+#define SIO_COMM_INT_STA__A 0x400005
+#define SIO_COMM_INT_STA__W 16
+#define SIO_COMM_INT_STA__M 0xFFFF
+#define SIO_COMM_INT_STA__PRE 0x0
+#define SIO_COMM_INT_MSK__A 0x400006
+#define SIO_COMM_INT_MSK__W 16
+#define SIO_COMM_INT_MSK__M 0xFFFF
+#define SIO_COMM_INT_MSK__PRE 0x0
+#define SIO_COMM_INT_STM__A 0x400007
+#define SIO_COMM_INT_STM__W 16
+#define SIO_COMM_INT_STM__M 0xFFFF
+#define SIO_COMM_INT_STM__PRE 0x0
+
+#define SIO_TOP_COMM_EXEC__A 0x410000
+#define SIO_TOP_COMM_EXEC__W 2
+#define SIO_TOP_COMM_EXEC__M 0x3
+#define SIO_TOP_COMM_EXEC__PRE 0x0
+#define SIO_TOP_COMM_EXEC_STOP 0x0
+#define SIO_TOP_COMM_EXEC_ACTIVE 0x1
+#define SIO_TOP_COMM_EXEC_HOLD 0x2
+
+#define SIO_TOP_COMM_KEY__A 0x41000F
+#define SIO_TOP_COMM_KEY__W 16
+#define SIO_TOP_COMM_KEY__M 0xFFFF
+#define SIO_TOP_COMM_KEY__PRE 0x0
+#define SIO_TOP_COMM_KEY_KEY 0xFABA
+
+#define SIO_TOP_JTAGID_LO__A 0x410012
+#define SIO_TOP_JTAGID_LO__W 16
+#define SIO_TOP_JTAGID_LO__M 0xFFFF
+#define SIO_TOP_JTAGID_LO__PRE 0x0
+
+#define SIO_TOP_JTAGID_HI__A 0x410013
+#define SIO_TOP_JTAGID_HI__W 16
+#define SIO_TOP_JTAGID_HI__M 0xFFFF
+#define SIO_TOP_JTAGID_HI__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_FLG_SMM__A 0x420010
+#define SIO_HI_RA_RAM_S0_FLG_SMM__W 1
+#define SIO_HI_RA_RAM_S0_FLG_SMM__M 0x1
+#define SIO_HI_RA_RAM_S0_FLG_SMM__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_DEV_ID__A 0x420011
+#define SIO_HI_RA_RAM_S0_DEV_ID__W 7
+#define SIO_HI_RA_RAM_S0_DEV_ID__M 0x7F
+#define SIO_HI_RA_RAM_S0_DEV_ID__PRE 0x52
+
+#define SIO_HI_RA_RAM_S0_FLG_CRC__A 0x420012
+#define SIO_HI_RA_RAM_S0_FLG_CRC__W 1
+#define SIO_HI_RA_RAM_S0_FLG_CRC__M 0x1
+#define SIO_HI_RA_RAM_S0_FLG_CRC__PRE 0x0
+#define SIO_HI_RA_RAM_S0_FLG_ACC__A 0x420013
+#define SIO_HI_RA_RAM_S0_FLG_ACC__W 4
+#define SIO_HI_RA_RAM_S0_FLG_ACC__M 0xF
+#define SIO_HI_RA_RAM_S0_FLG_ACC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__B 0
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__W 2
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__M 0x3
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__B 2
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__W 1
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__M 0x4
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__B 3
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__W 1
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__M 0x8
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_STATE__A 0x420014
+#define SIO_HI_RA_RAM_S0_STATE__W 1
+#define SIO_HI_RA_RAM_S0_STATE__M 0x1
+#define SIO_HI_RA_RAM_S0_STATE__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__B 0
+#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__W 1
+#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__M 0x1
+#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_BLK_BNK__A 0x420015
+#define SIO_HI_RA_RAM_S0_BLK_BNK__W 12
+#define SIO_HI_RA_RAM_S0_BLK_BNK__M 0xFFF
+#define SIO_HI_RA_RAM_S0_BLK_BNK__PRE 0x82
+
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__B 0
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__W 6
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__M 0x3F
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__PRE 0x2
+
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__B 6
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__W 6
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__PRE 0x80
+
+#define SIO_HI_RA_RAM_S0_ADDR__A 0x420016
+#define SIO_HI_RA_RAM_S0_ADDR__W 16
+#define SIO_HI_RA_RAM_S0_ADDR__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_ADDR__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__B 0
+#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__W 16
+#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_CRC__A 0x420017
+#define SIO_HI_RA_RAM_S0_CRC__W 16
+#define SIO_HI_RA_RAM_S0_CRC__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_CRC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_BUFFER__A 0x420018
+#define SIO_HI_RA_RAM_S0_BUFFER__W 16
+#define SIO_HI_RA_RAM_S0_BUFFER__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_BUFFER__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_RMWBUF__A 0x420019
+#define SIO_HI_RA_RAM_S0_RMWBUF__W 16
+#define SIO_HI_RA_RAM_S0_RMWBUF__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_RMWBUF__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_FLG_VB__A 0x42001A
+#define SIO_HI_RA_RAM_S0_FLG_VB__W 1
+#define SIO_HI_RA_RAM_S0_FLG_VB__M 0x1
+#define SIO_HI_RA_RAM_S0_FLG_VB__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_TEMP0__A 0x42001B
+#define SIO_HI_RA_RAM_S0_TEMP0__W 16
+#define SIO_HI_RA_RAM_S0_TEMP0__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_TEMP0__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_TEMP1__A 0x42001C
+#define SIO_HI_RA_RAM_S0_TEMP1__W 16
+#define SIO_HI_RA_RAM_S0_TEMP1__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_TEMP1__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_OFFSET__A 0x42001D
+#define SIO_HI_RA_RAM_S0_OFFSET__W 16
+#define SIO_HI_RA_RAM_S0_OFFSET__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_OFFSET__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_FLG_SMM__A 0x420020
+#define SIO_HI_RA_RAM_S1_FLG_SMM__W 1
+#define SIO_HI_RA_RAM_S1_FLG_SMM__M 0x1
+#define SIO_HI_RA_RAM_S1_FLG_SMM__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_DEV_ID__A 0x420021
+#define SIO_HI_RA_RAM_S1_DEV_ID__W 7
+#define SIO_HI_RA_RAM_S1_DEV_ID__M 0x7F
+#define SIO_HI_RA_RAM_S1_DEV_ID__PRE 0x52
+
+#define SIO_HI_RA_RAM_S1_FLG_CRC__A 0x420022
+#define SIO_HI_RA_RAM_S1_FLG_CRC__W 1
+#define SIO_HI_RA_RAM_S1_FLG_CRC__M 0x1
+#define SIO_HI_RA_RAM_S1_FLG_CRC__PRE 0x0
+#define SIO_HI_RA_RAM_S1_FLG_ACC__A 0x420023
+#define SIO_HI_RA_RAM_S1_FLG_ACC__W 4
+#define SIO_HI_RA_RAM_S1_FLG_ACC__M 0xF
+#define SIO_HI_RA_RAM_S1_FLG_ACC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__B 0
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__W 2
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__M 0x3
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__B 2
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__W 1
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__M 0x4
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__B 3
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__W 1
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__M 0x8
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_STATE__A 0x420024
+#define SIO_HI_RA_RAM_S1_STATE__W 1
+#define SIO_HI_RA_RAM_S1_STATE__M 0x1
+#define SIO_HI_RA_RAM_S1_STATE__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__B 0
+#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__W 1
+#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__M 0x1
+#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_BLK_BNK__A 0x420025
+#define SIO_HI_RA_RAM_S1_BLK_BNK__W 12
+#define SIO_HI_RA_RAM_S1_BLK_BNK__M 0xFFF
+#define SIO_HI_RA_RAM_S1_BLK_BNK__PRE 0x82
+
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__B 0
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__W 6
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__M 0x3F
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__PRE 0x2
+
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__B 6
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__W 6
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__PRE 0x80
+
+#define SIO_HI_RA_RAM_S1_ADDR__A 0x420026
+#define SIO_HI_RA_RAM_S1_ADDR__W 16
+#define SIO_HI_RA_RAM_S1_ADDR__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_ADDR__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__B 0
+#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__W 16
+#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_CRC__A 0x420027
+#define SIO_HI_RA_RAM_S1_CRC__W 16
+#define SIO_HI_RA_RAM_S1_CRC__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_CRC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_BUFFER__A 0x420028
+#define SIO_HI_RA_RAM_S1_BUFFER__W 16
+#define SIO_HI_RA_RAM_S1_BUFFER__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_BUFFER__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_RMWBUF__A 0x420029
+#define SIO_HI_RA_RAM_S1_RMWBUF__W 16
+#define SIO_HI_RA_RAM_S1_RMWBUF__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_RMWBUF__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_FLG_VB__A 0x42002A
+#define SIO_HI_RA_RAM_S1_FLG_VB__W 1
+#define SIO_HI_RA_RAM_S1_FLG_VB__M 0x1
+#define SIO_HI_RA_RAM_S1_FLG_VB__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_TEMP0__A 0x42002B
+#define SIO_HI_RA_RAM_S1_TEMP0__W 16
+#define SIO_HI_RA_RAM_S1_TEMP0__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_TEMP0__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_TEMP1__A 0x42002C
+#define SIO_HI_RA_RAM_S1_TEMP1__W 16
+#define SIO_HI_RA_RAM_S1_TEMP1__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_TEMP1__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_OFFSET__A 0x42002D
+#define SIO_HI_RA_RAM_S1_OFFSET__W 16
+#define SIO_HI_RA_RAM_S1_OFFSET__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_OFFSET__PRE 0x0
+#define SIO_HI_RA_RAM_SEMA__A 0x420030
+#define SIO_HI_RA_RAM_SEMA__W 1
+#define SIO_HI_RA_RAM_SEMA__M 0x1
+#define SIO_HI_RA_RAM_SEMA__PRE 0x0
+#define SIO_HI_RA_RAM_SEMA_FREE 0x0
+#define SIO_HI_RA_RAM_SEMA_BUSY 0x1
+
+#define SIO_HI_RA_RAM_RES__A 0x420031
+#define SIO_HI_RA_RAM_RES__W 3
+#define SIO_HI_RA_RAM_RES__M 0x7
+#define SIO_HI_RA_RAM_RES__PRE 0x0
+#define SIO_HI_RA_RAM_RES_OK 0x0
+#define SIO_HI_RA_RAM_RES_ERROR 0x1
+#define SIO_HI_RA_RAM_RES_I2C_START_FOUND 0x1
+#define SIO_HI_RA_RAM_RES_I2C_STOP_FOUND 0x2
+#define SIO_HI_RA_RAM_RES_I2C_ARB_LOST 0x3
+#define SIO_HI_RA_RAM_RES_I2C_ERROR 0x4
+
+#define SIO_HI_RA_RAM_CMD__A 0x420032
+#define SIO_HI_RA_RAM_CMD__W 4
+#define SIO_HI_RA_RAM_CMD__M 0xF
+#define SIO_HI_RA_RAM_CMD__PRE 0x0
+#define SIO_HI_RA_RAM_CMD_NULL 0x0
+#define SIO_HI_RA_RAM_CMD_UIO 0x1
+#define SIO_HI_RA_RAM_CMD_RESET 0x2
+#define SIO_HI_RA_RAM_CMD_CONFIG 0x3
+#define SIO_HI_RA_RAM_CMD_INTERNAL_TRANSFER 0x4
+#define SIO_HI_RA_RAM_CMD_I2C_TRANSMIT 0x5
+#define SIO_HI_RA_RAM_CMD_EXEC 0x6
+#define SIO_HI_RA_RAM_CMD_BRDCTRL 0x7
+#define SIO_HI_RA_RAM_CMD_ATOMIC_COPY 0x8
+
+#define SIO_HI_RA_RAM_PAR_1__A 0x420033
+#define SIO_HI_RA_RAM_PAR_1__W 16
+#define SIO_HI_RA_RAM_PAR_1__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_1__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_1_PAR1__B 0
+#define SIO_HI_RA_RAM_PAR_1_PAR1__W 16
+#define SIO_HI_RA_RAM_PAR_1_PAR1__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_1_PAR1__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY 0x3945
+
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__B 0
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__W 6
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__M 0x3F
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__B 6
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__W 6
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__B 0
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__W 1
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__M 0x1
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__B 1
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__W 1
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__M 0x2
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE_DISABLE 0x0
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE_ENABLE 0x2
+
+#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__B 0
+#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__W 10
+#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__M 0x3FF
+#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__B 0
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__W 6
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__M 0x3F
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__B 6
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__W 6
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_2__A 0x420034
+#define SIO_HI_RA_RAM_PAR_2__W 16
+#define SIO_HI_RA_RAM_PAR_2__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_2__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_2_PAR2__B 0
+#define SIO_HI_RA_RAM_PAR_2_PAR2__W 16
+#define SIO_HI_RA_RAM_PAR_2_PAR2__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_2_PAR2__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__B 0
+#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__W 7
+#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__M 0x7F
+#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__PRE 0x25
+
+#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__B 0
+#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__W 16
+#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__B 0
+#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__W 16
+#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__B 2
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__W 1
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__M 0x4
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN 0x0
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED 0x4
+
+#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__B 0
+#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__W 16
+#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_3__A 0x420035
+#define SIO_HI_RA_RAM_PAR_3__W 16
+#define SIO_HI_RA_RAM_PAR_3__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_3__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_3_PAR3__B 0
+#define SIO_HI_RA_RAM_PAR_3_PAR3__W 16
+#define SIO_HI_RA_RAM_PAR_3_PAR3__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_3_PAR3__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__B 0
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__W 7
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M 0x7F
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__PRE 0x3F
+
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B 7
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__W 7
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__M 0x3F80
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__PRE 0x1F80
+
+#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__B 0
+#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__W 16
+#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__B 0
+#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__W 3
+#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__M 0x7
+#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW__B 3
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW__W 1
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW__M 0x8
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW_READ 0x0
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE 0x8
+
+#define SIO_HI_RA_RAM_PAR_4__A 0x420036
+#define SIO_HI_RA_RAM_PAR_4__W 16
+#define SIO_HI_RA_RAM_PAR_4__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_4__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_4_PAR4__B 0
+#define SIO_HI_RA_RAM_PAR_4_PAR4__W 16
+#define SIO_HI_RA_RAM_PAR_4_PAR4__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_4_PAR4__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__B 0
+#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__W 8
+#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__M 0xFF
+#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__PRE 0xC1
+
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__B 0
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__W 6
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__M 0x3F
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__B 6
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__W 6
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__B 0
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__W 6
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__M 0x3F
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__B 6
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__W 6
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_5__A 0x420037
+#define SIO_HI_RA_RAM_PAR_5__W 16
+#define SIO_HI_RA_RAM_PAR_5__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_5__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_5_PAR5__B 0
+#define SIO_HI_RA_RAM_PAR_5_PAR5__W 16
+#define SIO_HI_RA_RAM_PAR_5_PAR5__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_5_PAR5__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__B 0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__W 1
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__M 0x1
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0_NO_SLAVE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE 0x1
+
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__B 1
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__W 1
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__M 0x2
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1_NO_SLAVE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1_SLAVE 0x2
+
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__B 3
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__W 1
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M 0x8
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_AWAKE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ 0x8
+
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__B 5
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__W 1
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__M 0x20
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST_DISABLE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST_ENABLE 0x20
+
+#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__B 0
+#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__W 16
+#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__B 0
+#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__W 16
+#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_6__A 0x420038
+#define SIO_HI_RA_RAM_PAR_6__W 16
+#define SIO_HI_RA_RAM_PAR_6__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_6__PRE 0x95FF
+#define SIO_HI_RA_RAM_PAR_6_PAR6__B 0
+#define SIO_HI_RA_RAM_PAR_6_PAR6__W 16
+#define SIO_HI_RA_RAM_PAR_6_PAR6__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_6_PAR6__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__B 0
+#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__W 8
+#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__M 0xFF
+#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__PRE 0xFF
+
+#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__B 8
+#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__W 8
+#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__M 0xFF00
+#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__PRE 0x9500
+
+#define SIO_HI_RA_RAM_AB_TEMP__A 0x42006E
+#define SIO_HI_RA_RAM_AB_TEMP__W 16
+#define SIO_HI_RA_RAM_AB_TEMP__M 0xFFFF
+#define SIO_HI_RA_RAM_AB_TEMP__PRE 0x0
+
+#define SIO_HI_RA_RAM_I2C_CTL__A 0x42006F
+#define SIO_HI_RA_RAM_I2C_CTL__W 16
+#define SIO_HI_RA_RAM_I2C_CTL__M 0xFFFF
+#define SIO_HI_RA_RAM_I2C_CTL__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY0__A 0x420070
+#define SIO_HI_RA_RAM_VB_ENTRY0__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY0__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY0__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__B 0
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__W 4
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__M 0xF
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__B 4
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__W 4
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__M 0xF0
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__B 8
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__W 4
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__M 0xF00
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__B 12
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__W 4
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__M 0xF000
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET0__A 0x420071
+#define SIO_HI_RA_RAM_VB_OFFSET0__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET0__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET0__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY1__A 0x420072
+#define SIO_HI_RA_RAM_VB_ENTRY1__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY1__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY1__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET1__A 0x420073
+#define SIO_HI_RA_RAM_VB_OFFSET1__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET1__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET1__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY2__A 0x420074
+#define SIO_HI_RA_RAM_VB_ENTRY2__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY2__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY2__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET2__A 0x420075
+#define SIO_HI_RA_RAM_VB_OFFSET2__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET2__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET2__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY3__A 0x420076
+#define SIO_HI_RA_RAM_VB_ENTRY3__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY3__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY3__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET3__A 0x420077
+#define SIO_HI_RA_RAM_VB_OFFSET3__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET3__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET3__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY4__A 0x420078
+#define SIO_HI_RA_RAM_VB_ENTRY4__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY4__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY4__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET4__A 0x420079
+#define SIO_HI_RA_RAM_VB_OFFSET4__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET4__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET4__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY5__A 0x42007A
+#define SIO_HI_RA_RAM_VB_ENTRY5__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY5__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY5__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET5__A 0x42007B
+#define SIO_HI_RA_RAM_VB_OFFSET5__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET5__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET5__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY6__A 0x42007C
+#define SIO_HI_RA_RAM_VB_ENTRY6__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY6__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY6__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET6__A 0x42007D
+#define SIO_HI_RA_RAM_VB_OFFSET6__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET6__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET6__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY7__A 0x42007E
+#define SIO_HI_RA_RAM_VB_ENTRY7__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY7__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY7__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET7__A 0x42007F
+#define SIO_HI_RA_RAM_VB_OFFSET7__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET7__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET7__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_IF_RAM_TRP_BPT_0__A 0x430000
+#define SIO_HI_IF_RAM_TRP_BPT_0__W 12
+#define SIO_HI_IF_RAM_TRP_BPT_0__M 0xFFF
+#define SIO_HI_IF_RAM_TRP_BPT_0__PRE 0x0
+#define SIO_HI_IF_RAM_TRP_BPT_1__A 0x430001
+#define SIO_HI_IF_RAM_TRP_BPT_1__W 12
+#define SIO_HI_IF_RAM_TRP_BPT_1__M 0xFFF
+#define SIO_HI_IF_RAM_TRP_BPT_1__PRE 0x0
+#define SIO_HI_IF_RAM_TRP_STK_0__A 0x430002
+#define SIO_HI_IF_RAM_TRP_STK_0__W 12
+#define SIO_HI_IF_RAM_TRP_STK_0__M 0xFFF
+#define SIO_HI_IF_RAM_TRP_STK_0__PRE 0x0
+#define SIO_HI_IF_RAM_TRP_STK_1__A 0x430003
+#define SIO_HI_IF_RAM_TRP_STK_1__W 12
+#define SIO_HI_IF_RAM_TRP_STK_1__M 0xFFF
+#define SIO_HI_IF_RAM_TRP_STK_1__PRE 0x0
+#define SIO_HI_IF_RAM_FUN_BASE__A 0x430300
+#define SIO_HI_IF_RAM_FUN_BASE__W 12
+#define SIO_HI_IF_RAM_FUN_BASE__M 0xFFF
+#define SIO_HI_IF_RAM_FUN_BASE__PRE 0x0
+
+#define SIO_HI_IF_COMM_EXEC__A 0x440000
+#define SIO_HI_IF_COMM_EXEC__W 2
+#define SIO_HI_IF_COMM_EXEC__M 0x3
+#define SIO_HI_IF_COMM_EXEC__PRE 0x0
+#define SIO_HI_IF_COMM_EXEC_STOP 0x0
+#define SIO_HI_IF_COMM_EXEC_ACTIVE 0x1
+#define SIO_HI_IF_COMM_EXEC_HOLD 0x2
+#define SIO_HI_IF_COMM_EXEC_STEP 0x3
+
+#define SIO_HI_IF_COMM_STATE__A 0x440001
+#define SIO_HI_IF_COMM_STATE__W 10
+#define SIO_HI_IF_COMM_STATE__M 0x3FF
+#define SIO_HI_IF_COMM_STATE__PRE 0x0
+#define SIO_HI_IF_COMM_INT_REQ__A 0x440003
+#define SIO_HI_IF_COMM_INT_REQ__W 1
+#define SIO_HI_IF_COMM_INT_REQ__M 0x1
+#define SIO_HI_IF_COMM_INT_REQ__PRE 0x0
+#define SIO_HI_IF_COMM_INT_STA__A 0x440005
+#define SIO_HI_IF_COMM_INT_STA__W 1
+#define SIO_HI_IF_COMM_INT_STA__M 0x1
+#define SIO_HI_IF_COMM_INT_STA__PRE 0x0
+#define SIO_HI_IF_COMM_INT_STA_STAT__B 0
+#define SIO_HI_IF_COMM_INT_STA_STAT__W 1
+#define SIO_HI_IF_COMM_INT_STA_STAT__M 0x1
+#define SIO_HI_IF_COMM_INT_STA_STAT__PRE 0x0
+
+#define SIO_HI_IF_COMM_INT_MSK__A 0x440006
+#define SIO_HI_IF_COMM_INT_MSK__W 1
+#define SIO_HI_IF_COMM_INT_MSK__M 0x1
+#define SIO_HI_IF_COMM_INT_MSK__PRE 0x0
+#define SIO_HI_IF_COMM_INT_MSK_STAT__B 0
+#define SIO_HI_IF_COMM_INT_MSK_STAT__W 1
+#define SIO_HI_IF_COMM_INT_MSK_STAT__M 0x1
+#define SIO_HI_IF_COMM_INT_MSK_STAT__PRE 0x0
+
+#define SIO_HI_IF_COMM_INT_STM__A 0x440007
+#define SIO_HI_IF_COMM_INT_STM__W 1
+#define SIO_HI_IF_COMM_INT_STM__M 0x1
+#define SIO_HI_IF_COMM_INT_STM__PRE 0x0
+#define SIO_HI_IF_COMM_INT_STM_STAT__B 0
+#define SIO_HI_IF_COMM_INT_STM_STAT__W 1
+#define SIO_HI_IF_COMM_INT_STM_STAT__M 0x1
+#define SIO_HI_IF_COMM_INT_STM_STAT__PRE 0x0
+
+#define SIO_HI_IF_STK_0__A 0x440010
+#define SIO_HI_IF_STK_0__W 10
+#define SIO_HI_IF_STK_0__M 0x3FF
+#define SIO_HI_IF_STK_0__PRE 0x2
+
+#define SIO_HI_IF_STK_0_ADDR__B 0
+#define SIO_HI_IF_STK_0_ADDR__W 10
+#define SIO_HI_IF_STK_0_ADDR__M 0x3FF
+#define SIO_HI_IF_STK_0_ADDR__PRE 0x2
+
+#define SIO_HI_IF_STK_1__A 0x440011
+#define SIO_HI_IF_STK_1__W 10
+#define SIO_HI_IF_STK_1__M 0x3FF
+#define SIO_HI_IF_STK_1__PRE 0x2
+#define SIO_HI_IF_STK_1_ADDR__B 0
+#define SIO_HI_IF_STK_1_ADDR__W 10
+#define SIO_HI_IF_STK_1_ADDR__M 0x3FF
+#define SIO_HI_IF_STK_1_ADDR__PRE 0x2
+
+#define SIO_HI_IF_STK_2__A 0x440012
+#define SIO_HI_IF_STK_2__W 10
+#define SIO_HI_IF_STK_2__M 0x3FF
+#define SIO_HI_IF_STK_2__PRE 0x2
+#define SIO_HI_IF_STK_2_ADDR__B 0
+#define SIO_HI_IF_STK_2_ADDR__W 10
+#define SIO_HI_IF_STK_2_ADDR__M 0x3FF
+#define SIO_HI_IF_STK_2_ADDR__PRE 0x2
+
+#define SIO_HI_IF_STK_3__A 0x440013
+#define SIO_HI_IF_STK_3__W 10
+#define SIO_HI_IF_STK_3__M 0x3FF
+#define SIO_HI_IF_STK_3__PRE 0x2
+
+#define SIO_HI_IF_STK_3_ADDR__B 0
+#define SIO_HI_IF_STK_3_ADDR__W 10
+#define SIO_HI_IF_STK_3_ADDR__M 0x3FF
+#define SIO_HI_IF_STK_3_ADDR__PRE 0x2
+
+#define SIO_HI_IF_BPT_IDX__A 0x44001F
+#define SIO_HI_IF_BPT_IDX__W 1
+#define SIO_HI_IF_BPT_IDX__M 0x1
+#define SIO_HI_IF_BPT_IDX__PRE 0x0
+
+#define SIO_HI_IF_BPT_IDX_ADDR__B 0
+#define SIO_HI_IF_BPT_IDX_ADDR__W 1
+#define SIO_HI_IF_BPT_IDX_ADDR__M 0x1
+#define SIO_HI_IF_BPT_IDX_ADDR__PRE 0x0
+
+#define SIO_HI_IF_BPT__A 0x440020
+#define SIO_HI_IF_BPT__W 10
+#define SIO_HI_IF_BPT__M 0x3FF
+#define SIO_HI_IF_BPT__PRE 0x2
+
+#define SIO_HI_IF_BPT_ADDR__B 0
+#define SIO_HI_IF_BPT_ADDR__W 10
+#define SIO_HI_IF_BPT_ADDR__M 0x3FF
+#define SIO_HI_IF_BPT_ADDR__PRE 0x2
+
+#define SIO_CC_COMM_EXEC__A 0x450000
+#define SIO_CC_COMM_EXEC__W 2
+#define SIO_CC_COMM_EXEC__M 0x3
+#define SIO_CC_COMM_EXEC__PRE 0x0
+#define SIO_CC_COMM_EXEC_STOP 0x0
+#define SIO_CC_COMM_EXEC_ACTIVE 0x1
+#define SIO_CC_COMM_EXEC_HOLD 0x2
+
+#define SIO_CC_PLL_MODE__A 0x450010
+#define SIO_CC_PLL_MODE__W 6
+#define SIO_CC_PLL_MODE__M 0x3F
+#define SIO_CC_PLL_MODE__PRE 0x0
+
+#define SIO_CC_PLL_MODE_FREF_SEL__B 0
+#define SIO_CC_PLL_MODE_FREF_SEL__W 2
+#define SIO_CC_PLL_MODE_FREF_SEL__M 0x3
+#define SIO_CC_PLL_MODE_FREF_SEL__PRE 0x0
+#define SIO_CC_PLL_MODE_FREF_SEL_OHW 0x0
+#define SIO_CC_PLL_MODE_FREF_SEL_27_00 0x1
+#define SIO_CC_PLL_MODE_FREF_SEL_20_25 0x2
+#define SIO_CC_PLL_MODE_FREF_SEL_4_00 0x3
+
+#define SIO_CC_PLL_MODE_LOCKSEL__B 2
+#define SIO_CC_PLL_MODE_LOCKSEL__W 2
+#define SIO_CC_PLL_MODE_LOCKSEL__M 0xC
+#define SIO_CC_PLL_MODE_LOCKSEL__PRE 0x0
+
+#define SIO_CC_PLL_MODE_BYPASS__B 4
+#define SIO_CC_PLL_MODE_BYPASS__W 2
+#define SIO_CC_PLL_MODE_BYPASS__M 0x30
+#define SIO_CC_PLL_MODE_BYPASS__PRE 0x0
+#define SIO_CC_PLL_MODE_BYPASS_OHW 0x0
+#define SIO_CC_PLL_MODE_BYPASS_OFF 0x10
+#define SIO_CC_PLL_MODE_BYPASS_ON 0x20
+
+#define SIO_CC_PLL_TEST__A 0x450011
+#define SIO_CC_PLL_TEST__W 8
+#define SIO_CC_PLL_TEST__M 0xFF
+#define SIO_CC_PLL_TEST__PRE 0x0
+
+#define SIO_CC_PLL_LOCK__A 0x450012
+#define SIO_CC_PLL_LOCK__W 1
+#define SIO_CC_PLL_LOCK__M 0x1
+#define SIO_CC_PLL_LOCK__PRE 0x0
+#define SIO_CC_CLK_MODE__A 0x450014
+#define SIO_CC_CLK_MODE__W 5
+#define SIO_CC_CLK_MODE__M 0x1F
+#define SIO_CC_CLK_MODE__PRE 0x0
+
+#define SIO_CC_CLK_MODE_DELAY__B 0
+#define SIO_CC_CLK_MODE_DELAY__W 4
+#define SIO_CC_CLK_MODE_DELAY__M 0xF
+#define SIO_CC_CLK_MODE_DELAY__PRE 0x0
+
+#define SIO_CC_CLK_MODE_INVERT__B 4
+#define SIO_CC_CLK_MODE_INVERT__W 1
+#define SIO_CC_CLK_MODE_INVERT__M 0x10
+#define SIO_CC_CLK_MODE_INVERT__PRE 0x0
+
+#define SIO_CC_PWD_MODE__A 0x450015
+#define SIO_CC_PWD_MODE__W 3
+#define SIO_CC_PWD_MODE__M 0x7
+#define SIO_CC_PWD_MODE__PRE 0x0
+
+#define SIO_CC_PWD_MODE_LEVEL__B 0
+#define SIO_CC_PWD_MODE_LEVEL__W 2
+#define SIO_CC_PWD_MODE_LEVEL__M 0x3
+#define SIO_CC_PWD_MODE_LEVEL__PRE 0x0
+#define SIO_CC_PWD_MODE_LEVEL_NONE 0x0
+#define SIO_CC_PWD_MODE_LEVEL_CLOCK 0x1
+#define SIO_CC_PWD_MODE_LEVEL_PLL 0x2
+#define SIO_CC_PWD_MODE_LEVEL_OSC 0x3
+
+#define SIO_CC_PWD_MODE_USE_LOCK__B 2
+#define SIO_CC_PWD_MODE_USE_LOCK__W 1
+#define SIO_CC_PWD_MODE_USE_LOCK__M 0x4
+#define SIO_CC_PWD_MODE_USE_LOCK__PRE 0x0
+
+#define SIO_CC_SOFT_RST__A 0x450016
+#define SIO_CC_SOFT_RST__W 2
+#define SIO_CC_SOFT_RST__M 0x3
+#define SIO_CC_SOFT_RST__PRE 0x0
+
+#define SIO_CC_SOFT_RST_SYS__B 0
+#define SIO_CC_SOFT_RST_SYS__W 1
+#define SIO_CC_SOFT_RST_SYS__M 0x1
+#define SIO_CC_SOFT_RST_SYS__PRE 0x0
+
+#define SIO_CC_SOFT_RST_OSC__B 1
+#define SIO_CC_SOFT_RST_OSC__W 1
+#define SIO_CC_SOFT_RST_OSC__M 0x2
+#define SIO_CC_SOFT_RST_OSC__PRE 0x0
+
+#define SIO_CC_UPDATE__A 0x450017
+#define SIO_CC_UPDATE__W 16
+#define SIO_CC_UPDATE__M 0xFFFF
+#define SIO_CC_UPDATE__PRE 0x0
+#define SIO_CC_UPDATE_KEY 0xFABA
+
+#define SIO_SA_COMM_EXEC__A 0x460000
+#define SIO_SA_COMM_EXEC__W 2
+#define SIO_SA_COMM_EXEC__M 0x3
+#define SIO_SA_COMM_EXEC__PRE 0x0
+#define SIO_SA_COMM_EXEC_STOP 0x0
+#define SIO_SA_COMM_EXEC_ACTIVE 0x1
+#define SIO_SA_COMM_EXEC_HOLD 0x2
+
+#define SIO_SA_COMM_INT_REQ__A 0x460003
+#define SIO_SA_COMM_INT_REQ__W 1
+#define SIO_SA_COMM_INT_REQ__M 0x1
+#define SIO_SA_COMM_INT_REQ__PRE 0x0
+#define SIO_SA_COMM_INT_STA__A 0x460005
+#define SIO_SA_COMM_INT_STA__W 4
+#define SIO_SA_COMM_INT_STA__M 0xF
+#define SIO_SA_COMM_INT_STA__PRE 0x0
+
+#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__B 0
+#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__W 1
+#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__M 0x1
+#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__PRE 0x0
+
+#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__B 1
+#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__W 1
+#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__M 0x2
+#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__PRE 0x0
+
+#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__B 2
+#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__W 1
+#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__M 0x4
+#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__PRE 0x0
+
+#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__B 3
+#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__W 1
+#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__M 0x8
+#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__PRE 0x0
+
+#define SIO_SA_COMM_INT_MSK__A 0x460006
+#define SIO_SA_COMM_INT_MSK__W 4
+#define SIO_SA_COMM_INT_MSK__M 0xF
+#define SIO_SA_COMM_INT_MSK__PRE 0x0
+
+#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__B 0
+#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__W 1
+#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__M 0x1
+#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__B 1
+#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__W 1
+#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__M 0x2
+#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__B 2
+#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__W 1
+#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__M 0x4
+#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__B 3
+#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__W 1
+#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__M 0x8
+#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_STM__A 0x460007
+#define SIO_SA_COMM_INT_STM__W 4
+#define SIO_SA_COMM_INT_STM__M 0xF
+#define SIO_SA_COMM_INT_STM__PRE 0x0
+
+#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__B 0
+#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__W 1
+#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__M 0x1
+#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__B 1
+#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__W 1
+#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__M 0x2
+#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__B 2
+#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__W 1
+#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__M 0x4
+#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__B 3
+#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__W 1
+#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__M 0x8
+#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__PRE 0x0
+
+#define SIO_SA_PRESCALER__A 0x460010
+#define SIO_SA_PRESCALER__W 13
+#define SIO_SA_PRESCALER__M 0x1FFF
+#define SIO_SA_PRESCALER__PRE 0x18B7
+#define SIO_SA_TX_DATA0__A 0x460011
+#define SIO_SA_TX_DATA0__W 16
+#define SIO_SA_TX_DATA0__M 0xFFFF
+#define SIO_SA_TX_DATA0__PRE 0x0
+#define SIO_SA_TX_DATA1__A 0x460012
+#define SIO_SA_TX_DATA1__W 16
+#define SIO_SA_TX_DATA1__M 0xFFFF
+#define SIO_SA_TX_DATA1__PRE 0x0
+#define SIO_SA_TX_DATA2__A 0x460013
+#define SIO_SA_TX_DATA2__W 16
+#define SIO_SA_TX_DATA2__M 0xFFFF
+#define SIO_SA_TX_DATA2__PRE 0x0
+#define SIO_SA_TX_DATA3__A 0x460014
+#define SIO_SA_TX_DATA3__W 16
+#define SIO_SA_TX_DATA3__M 0xFFFF
+#define SIO_SA_TX_DATA3__PRE 0x0
+#define SIO_SA_TX_LENGTH__A 0x460015
+#define SIO_SA_TX_LENGTH__W 6
+#define SIO_SA_TX_LENGTH__M 0x3F
+#define SIO_SA_TX_LENGTH__PRE 0x0
+#define SIO_SA_TX_COMMAND__A 0x460016
+#define SIO_SA_TX_COMMAND__W 2
+#define SIO_SA_TX_COMMAND__M 0x3
+#define SIO_SA_TX_COMMAND__PRE 0x3
+
+#define SIO_SA_TX_COMMAND_TX_INVERT__B 0
+#define SIO_SA_TX_COMMAND_TX_INVERT__W 1
+#define SIO_SA_TX_COMMAND_TX_INVERT__M 0x1
+#define SIO_SA_TX_COMMAND_TX_INVERT__PRE 0x1
+
+#define SIO_SA_TX_COMMAND_TX_ENABLE__B 1
+#define SIO_SA_TX_COMMAND_TX_ENABLE__W 1
+#define SIO_SA_TX_COMMAND_TX_ENABLE__M 0x2
+#define SIO_SA_TX_COMMAND_TX_ENABLE__PRE 0x2
+
+#define SIO_SA_TX_STATUS__A 0x460017
+#define SIO_SA_TX_STATUS__W 2
+#define SIO_SA_TX_STATUS__M 0x3
+#define SIO_SA_TX_STATUS__PRE 0x0
+
+#define SIO_SA_TX_STATUS_BUSY__B 0
+#define SIO_SA_TX_STATUS_BUSY__W 1
+#define SIO_SA_TX_STATUS_BUSY__M 0x1
+#define SIO_SA_TX_STATUS_BUSY__PRE 0x0
+
+#define SIO_SA_TX_STATUS_BUFF_FULL__B 1
+#define SIO_SA_TX_STATUS_BUFF_FULL__W 1
+#define SIO_SA_TX_STATUS_BUFF_FULL__M 0x2
+#define SIO_SA_TX_STATUS_BUFF_FULL__PRE 0x0
+
+#define SIO_SA_RX_DATA0__A 0x460018
+#define SIO_SA_RX_DATA0__W 16
+#define SIO_SA_RX_DATA0__M 0xFFFF
+#define SIO_SA_RX_DATA0__PRE 0x0
+#define SIO_SA_RX_DATA1__A 0x460019
+#define SIO_SA_RX_DATA1__W 16
+#define SIO_SA_RX_DATA1__M 0xFFFF
+#define SIO_SA_RX_DATA1__PRE 0x0
+#define SIO_SA_RX_LENGTH__A 0x46001A
+#define SIO_SA_RX_LENGTH__W 6
+#define SIO_SA_RX_LENGTH__M 0x3F
+#define SIO_SA_RX_LENGTH__PRE 0x0
+#define SIO_SA_RX_COMMAND__A 0x46001B
+#define SIO_SA_RX_COMMAND__W 1
+#define SIO_SA_RX_COMMAND__M 0x1
+#define SIO_SA_RX_COMMAND__PRE 0x1
+
+#define SIO_SA_RX_COMMAND_RX_INVERT__B 0
+#define SIO_SA_RX_COMMAND_RX_INVERT__W 1
+#define SIO_SA_RX_COMMAND_RX_INVERT__M 0x1
+#define SIO_SA_RX_COMMAND_RX_INVERT__PRE 0x1
+
+#define SIO_SA_RX_STATUS__A 0x46001C
+#define SIO_SA_RX_STATUS__W 2
+#define SIO_SA_RX_STATUS__M 0x3
+#define SIO_SA_RX_STATUS__PRE 0x0
+
+#define SIO_SA_RX_STATUS_BUSY__B 0
+#define SIO_SA_RX_STATUS_BUSY__W 1
+#define SIO_SA_RX_STATUS_BUSY__M 0x1
+#define SIO_SA_RX_STATUS_BUSY__PRE 0x0
+
+#define SIO_SA_RX_STATUS_BUFF_FULL__B 1
+#define SIO_SA_RX_STATUS_BUFF_FULL__W 1
+#define SIO_SA_RX_STATUS_BUFF_FULL__M 0x2
+#define SIO_SA_RX_STATUS_BUFF_FULL__PRE 0x0
+
+#define SIO_PDR_COMM_EXEC__A 0x7F0000
+#define SIO_PDR_COMM_EXEC__W 2
+#define SIO_PDR_COMM_EXEC__M 0x3
+#define SIO_PDR_COMM_EXEC__PRE 0x0
+#define SIO_PDR_COMM_EXEC_STOP 0x0
+#define SIO_PDR_COMM_EXEC_ACTIVE 0x1
+#define SIO_PDR_COMM_EXEC_HOLD 0x2
+
+#define SIO_PDR_MON_CFG__A 0x7F0010
+#define SIO_PDR_MON_CFG__W 2
+#define SIO_PDR_MON_CFG__M 0x3
+#define SIO_PDR_MON_CFG__PRE 0x0
+
+#define SIO_PDR_MON_CFG_OSEL__B 0
+#define SIO_PDR_MON_CFG_OSEL__W 1
+#define SIO_PDR_MON_CFG_OSEL__M 0x1
+#define SIO_PDR_MON_CFG_OSEL__PRE 0x0
+
+#define SIO_PDR_MON_CFG_IACT__B 1
+#define SIO_PDR_MON_CFG_IACT__W 1
+#define SIO_PDR_MON_CFG_IACT__M 0x2
+#define SIO_PDR_MON_CFG_IACT__PRE 0x0
+
+#define SIO_PDR_FDB_CFG__A 0x7F0011
+#define SIO_PDR_FDB_CFG__W 2
+#define SIO_PDR_FDB_CFG__M 0x3
+#define SIO_PDR_FDB_CFG__PRE 0x0
+#define SIO_PDR_FDB_CFG_SEL__B 0
+#define SIO_PDR_FDB_CFG_SEL__W 2
+#define SIO_PDR_FDB_CFG_SEL__M 0x3
+#define SIO_PDR_FDB_CFG_SEL__PRE 0x0
+
+#define SIO_PDR_SMA_RX_SEL__A 0x7F0012
+#define SIO_PDR_SMA_RX_SEL__W 4
+#define SIO_PDR_SMA_RX_SEL__M 0xF
+#define SIO_PDR_SMA_RX_SEL__PRE 0x0
+#define SIO_PDR_SMA_RX_SEL_SEL__B 0
+#define SIO_PDR_SMA_RX_SEL_SEL__W 4
+#define SIO_PDR_SMA_RX_SEL_SEL__M 0xF
+#define SIO_PDR_SMA_RX_SEL_SEL__PRE 0x0
+
+#define SIO_PDR_SMA_TX_SILENT__A 0x7F0013
+#define SIO_PDR_SMA_TX_SILENT__W 1
+#define SIO_PDR_SMA_TX_SILENT__M 0x1
+#define SIO_PDR_SMA_TX_SILENT__PRE 0x0
+#define SIO_PDR_UIO_IN_LO__A 0x7F0014
+#define SIO_PDR_UIO_IN_LO__W 16
+#define SIO_PDR_UIO_IN_LO__M 0xFFFF
+#define SIO_PDR_UIO_IN_LO__PRE 0x0
+#define SIO_PDR_UIO_IN_LO_DATA__B 0
+#define SIO_PDR_UIO_IN_LO_DATA__W 16
+#define SIO_PDR_UIO_IN_LO_DATA__M 0xFFFF
+#define SIO_PDR_UIO_IN_LO_DATA__PRE 0x0
+
+#define SIO_PDR_UIO_IN_HI__A 0x7F0015
+#define SIO_PDR_UIO_IN_HI__W 14
+#define SIO_PDR_UIO_IN_HI__M 0x3FFF
+#define SIO_PDR_UIO_IN_HI__PRE 0x0
+#define SIO_PDR_UIO_IN_HI_DATA__B 0
+#define SIO_PDR_UIO_IN_HI_DATA__W 14
+#define SIO_PDR_UIO_IN_HI_DATA__M 0x3FFF
+#define SIO_PDR_UIO_IN_HI_DATA__PRE 0x0
+
+#define SIO_PDR_UIO_OUT_LO__A 0x7F0016
+#define SIO_PDR_UIO_OUT_LO__W 16
+#define SIO_PDR_UIO_OUT_LO__M 0xFFFF
+#define SIO_PDR_UIO_OUT_LO__PRE 0x0
+#define SIO_PDR_UIO_OUT_LO_DATA__B 0
+#define SIO_PDR_UIO_OUT_LO_DATA__W 16
+#define SIO_PDR_UIO_OUT_LO_DATA__M 0xFFFF
+#define SIO_PDR_UIO_OUT_LO_DATA__PRE 0x0
+
+#define SIO_PDR_UIO_OUT_HI__A 0x7F0017
+#define SIO_PDR_UIO_OUT_HI__W 14
+#define SIO_PDR_UIO_OUT_HI__M 0x3FFF
+#define SIO_PDR_UIO_OUT_HI__PRE 0x0
+#define SIO_PDR_UIO_OUT_HI_DATA__B 0
+#define SIO_PDR_UIO_OUT_HI_DATA__W 14
+#define SIO_PDR_UIO_OUT_HI_DATA__M 0x3FFF
+#define SIO_PDR_UIO_OUT_HI_DATA__PRE 0x0
+
+#define SIO_PDR_PWM1_MODE__A 0x7F0018
+#define SIO_PDR_PWM1_MODE__W 2
+#define SIO_PDR_PWM1_MODE__M 0x3
+#define SIO_PDR_PWM1_MODE__PRE 0x0
+#define SIO_PDR_PWM1_PRESCALE__A 0x7F0019
+#define SIO_PDR_PWM1_PRESCALE__W 6
+#define SIO_PDR_PWM1_PRESCALE__M 0x3F
+#define SIO_PDR_PWM1_PRESCALE__PRE 0x0
+#define SIO_PDR_PWM1_VALUE__A 0x7F001A
+#define SIO_PDR_PWM1_VALUE__W 11
+#define SIO_PDR_PWM1_VALUE__M 0x7FF
+#define SIO_PDR_PWM1_VALUE__PRE 0x0
+#define SIO_PDR_PWM2_MODE__A 0x7F001C
+#define SIO_PDR_PWM2_MODE__W 2
+#define SIO_PDR_PWM2_MODE__M 0x3
+#define SIO_PDR_PWM2_MODE__PRE 0x0
+#define SIO_PDR_PWM2_PRESCALE__A 0x7F001D
+#define SIO_PDR_PWM2_PRESCALE__W 6
+#define SIO_PDR_PWM2_PRESCALE__M 0x3F
+#define SIO_PDR_PWM2_PRESCALE__PRE 0x0
+#define SIO_PDR_PWM2_VALUE__A 0x7F001E
+#define SIO_PDR_PWM2_VALUE__W 11
+#define SIO_PDR_PWM2_VALUE__M 0x7FF
+#define SIO_PDR_PWM2_VALUE__PRE 0x0
+#define SIO_PDR_OHW_CFG__A 0x7F001F
+#define SIO_PDR_OHW_CFG__W 7
+#define SIO_PDR_OHW_CFG__M 0x7F
+#define SIO_PDR_OHW_CFG__PRE 0x0
+
+#define SIO_PDR_OHW_CFG_FREF_SEL__B 0
+#define SIO_PDR_OHW_CFG_FREF_SEL__W 2
+#define SIO_PDR_OHW_CFG_FREF_SEL__M 0x3
+#define SIO_PDR_OHW_CFG_FREF_SEL__PRE 0x0
+
+#define SIO_PDR_OHW_CFG_BYPASS__B 2
+#define SIO_PDR_OHW_CFG_BYPASS__W 1
+#define SIO_PDR_OHW_CFG_BYPASS__M 0x4
+#define SIO_PDR_OHW_CFG_BYPASS__PRE 0x0
+
+#define SIO_PDR_OHW_CFG_ASEL__B 3
+#define SIO_PDR_OHW_CFG_ASEL__W 3
+#define SIO_PDR_OHW_CFG_ASEL__M 0x38
+#define SIO_PDR_OHW_CFG_ASEL__PRE 0x0
+
+#define SIO_PDR_OHW_CFG_SPEED__B 6
+#define SIO_PDR_OHW_CFG_SPEED__W 1
+#define SIO_PDR_OHW_CFG_SPEED__M 0x40
+#define SIO_PDR_OHW_CFG_SPEED__PRE 0x0
+
+#define SIO_PDR_I2S_WS_CFG__A 0x7F0020
+#define SIO_PDR_I2S_WS_CFG__W 9
+#define SIO_PDR_I2S_WS_CFG__M 0x1FF
+#define SIO_PDR_I2S_WS_CFG__PRE 0x10
+#define SIO_PDR_I2S_WS_CFG_MODE__B 0
+#define SIO_PDR_I2S_WS_CFG_MODE__W 3
+#define SIO_PDR_I2S_WS_CFG_MODE__M 0x7
+#define SIO_PDR_I2S_WS_CFG_MODE__PRE 0x0
+#define SIO_PDR_I2S_WS_CFG_DRIVE__B 3
+#define SIO_PDR_I2S_WS_CFG_DRIVE__W 3
+#define SIO_PDR_I2S_WS_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2S_WS_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2S_WS_CFG_KEEP__B 6
+#define SIO_PDR_I2S_WS_CFG_KEEP__W 2
+#define SIO_PDR_I2S_WS_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2S_WS_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2S_WS_CFG_UIO__B 8
+#define SIO_PDR_I2S_WS_CFG_UIO__W 1
+#define SIO_PDR_I2S_WS_CFG_UIO__M 0x100
+#define SIO_PDR_I2S_WS_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_GPIO_CFG__A 0x7F0021
+#define SIO_PDR_GPIO_CFG__W 9
+#define SIO_PDR_GPIO_CFG__M 0x1FF
+#define SIO_PDR_GPIO_CFG__PRE 0x10
+#define SIO_PDR_GPIO_CFG_MODE__B 0
+#define SIO_PDR_GPIO_CFG_MODE__W 3
+#define SIO_PDR_GPIO_CFG_MODE__M 0x7
+#define SIO_PDR_GPIO_CFG_MODE__PRE 0x0
+#define SIO_PDR_GPIO_CFG_DRIVE__B 3
+#define SIO_PDR_GPIO_CFG_DRIVE__W 3
+#define SIO_PDR_GPIO_CFG_DRIVE__M 0x38
+#define SIO_PDR_GPIO_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_GPIO_CFG_KEEP__B 6
+#define SIO_PDR_GPIO_CFG_KEEP__W 2
+#define SIO_PDR_GPIO_CFG_KEEP__M 0xC0
+#define SIO_PDR_GPIO_CFG_KEEP__PRE 0x0
+#define SIO_PDR_GPIO_CFG_UIO__B 8
+#define SIO_PDR_GPIO_CFG_UIO__W 1
+#define SIO_PDR_GPIO_CFG_UIO__M 0x100
+#define SIO_PDR_GPIO_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_IRQN_CFG__A 0x7F0022
+#define SIO_PDR_IRQN_CFG__W 9
+#define SIO_PDR_IRQN_CFG__M 0x1FF
+#define SIO_PDR_IRQN_CFG__PRE 0x10
+#define SIO_PDR_IRQN_CFG_MODE__B 0
+#define SIO_PDR_IRQN_CFG_MODE__W 3
+#define SIO_PDR_IRQN_CFG_MODE__M 0x7
+#define SIO_PDR_IRQN_CFG_MODE__PRE 0x0
+#define SIO_PDR_IRQN_CFG_DRIVE__B 3
+#define SIO_PDR_IRQN_CFG_DRIVE__W 3
+#define SIO_PDR_IRQN_CFG_DRIVE__M 0x38
+#define SIO_PDR_IRQN_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_IRQN_CFG_KEEP__B 6
+#define SIO_PDR_IRQN_CFG_KEEP__W 2
+#define SIO_PDR_IRQN_CFG_KEEP__M 0xC0
+#define SIO_PDR_IRQN_CFG_KEEP__PRE 0x0
+#define SIO_PDR_IRQN_CFG_UIO__B 8
+#define SIO_PDR_IRQN_CFG_UIO__W 1
+#define SIO_PDR_IRQN_CFG_UIO__M 0x100
+#define SIO_PDR_IRQN_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_OOB_CRX_CFG__A 0x7F0023
+#define SIO_PDR_OOB_CRX_CFG__W 9
+#define SIO_PDR_OOB_CRX_CFG__M 0x1FF
+#define SIO_PDR_OOB_CRX_CFG__PRE 0x10
+#define SIO_PDR_OOB_CRX_CFG_MODE__B 0
+#define SIO_PDR_OOB_CRX_CFG_MODE__W 3
+#define SIO_PDR_OOB_CRX_CFG_MODE__M 0x7
+#define SIO_PDR_OOB_CRX_CFG_MODE__PRE 0x0
+#define SIO_PDR_OOB_CRX_CFG_DRIVE__B 3
+#define SIO_PDR_OOB_CRX_CFG_DRIVE__W 3
+#define SIO_PDR_OOB_CRX_CFG_DRIVE__M 0x38
+#define SIO_PDR_OOB_CRX_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_OOB_CRX_CFG_KEEP__B 6
+#define SIO_PDR_OOB_CRX_CFG_KEEP__W 2
+#define SIO_PDR_OOB_CRX_CFG_KEEP__M 0xC0
+#define SIO_PDR_OOB_CRX_CFG_KEEP__PRE 0x0
+#define SIO_PDR_OOB_CRX_CFG_UIO__B 8
+#define SIO_PDR_OOB_CRX_CFG_UIO__W 1
+#define SIO_PDR_OOB_CRX_CFG_UIO__M 0x100
+#define SIO_PDR_OOB_CRX_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_OOB_DRX_CFG__A 0x7F0024
+#define SIO_PDR_OOB_DRX_CFG__W 9
+#define SIO_PDR_OOB_DRX_CFG__M 0x1FF
+#define SIO_PDR_OOB_DRX_CFG__PRE 0x10
+#define SIO_PDR_OOB_DRX_CFG_MODE__B 0
+#define SIO_PDR_OOB_DRX_CFG_MODE__W 3
+#define SIO_PDR_OOB_DRX_CFG_MODE__M 0x7
+#define SIO_PDR_OOB_DRX_CFG_MODE__PRE 0x0
+#define SIO_PDR_OOB_DRX_CFG_DRIVE__B 3
+#define SIO_PDR_OOB_DRX_CFG_DRIVE__W 3
+#define SIO_PDR_OOB_DRX_CFG_DRIVE__M 0x38
+#define SIO_PDR_OOB_DRX_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_OOB_DRX_CFG_KEEP__B 6
+#define SIO_PDR_OOB_DRX_CFG_KEEP__W 2
+#define SIO_PDR_OOB_DRX_CFG_KEEP__M 0xC0
+#define SIO_PDR_OOB_DRX_CFG_KEEP__PRE 0x0
+#define SIO_PDR_OOB_DRX_CFG_UIO__B 8
+#define SIO_PDR_OOB_DRX_CFG_UIO__W 1
+#define SIO_PDR_OOB_DRX_CFG_UIO__M 0x100
+#define SIO_PDR_OOB_DRX_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MSTRT_CFG__A 0x7F0025
+#define SIO_PDR_MSTRT_CFG__W 9
+#define SIO_PDR_MSTRT_CFG__M 0x1FF
+#define SIO_PDR_MSTRT_CFG__PRE 0x50
+#define SIO_PDR_MSTRT_CFG_MODE__B 0
+#define SIO_PDR_MSTRT_CFG_MODE__W 3
+#define SIO_PDR_MSTRT_CFG_MODE__M 0x7
+#define SIO_PDR_MSTRT_CFG_MODE__PRE 0x0
+#define SIO_PDR_MSTRT_CFG_DRIVE__B 3
+#define SIO_PDR_MSTRT_CFG_DRIVE__W 3
+#define SIO_PDR_MSTRT_CFG_DRIVE__M 0x38
+#define SIO_PDR_MSTRT_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MSTRT_CFG_KEEP__B 6
+#define SIO_PDR_MSTRT_CFG_KEEP__W 2
+#define SIO_PDR_MSTRT_CFG_KEEP__M 0xC0
+#define SIO_PDR_MSTRT_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MSTRT_CFG_UIO__B 8
+#define SIO_PDR_MSTRT_CFG_UIO__W 1
+#define SIO_PDR_MSTRT_CFG_UIO__M 0x100
+#define SIO_PDR_MSTRT_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MERR_CFG__A 0x7F0026
+#define SIO_PDR_MERR_CFG__W 9
+#define SIO_PDR_MERR_CFG__M 0x1FF
+#define SIO_PDR_MERR_CFG__PRE 0x50
+#define SIO_PDR_MERR_CFG_MODE__B 0
+#define SIO_PDR_MERR_CFG_MODE__W 3
+#define SIO_PDR_MERR_CFG_MODE__M 0x7
+#define SIO_PDR_MERR_CFG_MODE__PRE 0x0
+#define SIO_PDR_MERR_CFG_DRIVE__B 3
+#define SIO_PDR_MERR_CFG_DRIVE__W 3
+#define SIO_PDR_MERR_CFG_DRIVE__M 0x38
+#define SIO_PDR_MERR_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MERR_CFG_KEEP__B 6
+#define SIO_PDR_MERR_CFG_KEEP__W 2
+#define SIO_PDR_MERR_CFG_KEEP__M 0xC0
+#define SIO_PDR_MERR_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MERR_CFG_UIO__B 8
+#define SIO_PDR_MERR_CFG_UIO__W 1
+#define SIO_PDR_MERR_CFG_UIO__M 0x100
+#define SIO_PDR_MERR_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MCLK_CFG__A 0x7F0028
+#define SIO_PDR_MCLK_CFG__W 9
+#define SIO_PDR_MCLK_CFG__M 0x1FF
+#define SIO_PDR_MCLK_CFG__PRE 0x50
+#define SIO_PDR_MCLK_CFG_MODE__B 0
+#define SIO_PDR_MCLK_CFG_MODE__W 3
+#define SIO_PDR_MCLK_CFG_MODE__M 0x7
+#define SIO_PDR_MCLK_CFG_MODE__PRE 0x0
+#define SIO_PDR_MCLK_CFG_DRIVE__B 3
+#define SIO_PDR_MCLK_CFG_DRIVE__W 3
+#define SIO_PDR_MCLK_CFG_DRIVE__M 0x38
+#define SIO_PDR_MCLK_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MCLK_CFG_KEEP__B 6
+#define SIO_PDR_MCLK_CFG_KEEP__W 2
+#define SIO_PDR_MCLK_CFG_KEEP__M 0xC0
+#define SIO_PDR_MCLK_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MCLK_CFG_UIO__B 8
+#define SIO_PDR_MCLK_CFG_UIO__W 1
+#define SIO_PDR_MCLK_CFG_UIO__M 0x100
+#define SIO_PDR_MCLK_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MVAL_CFG__A 0x7F0029
+#define SIO_PDR_MVAL_CFG__W 9
+#define SIO_PDR_MVAL_CFG__M 0x1FF
+#define SIO_PDR_MVAL_CFG__PRE 0x50
+#define SIO_PDR_MVAL_CFG_MODE__B 0
+#define SIO_PDR_MVAL_CFG_MODE__W 3
+#define SIO_PDR_MVAL_CFG_MODE__M 0x7
+#define SIO_PDR_MVAL_CFG_MODE__PRE 0x0
+#define SIO_PDR_MVAL_CFG_DRIVE__B 3
+#define SIO_PDR_MVAL_CFG_DRIVE__W 3
+#define SIO_PDR_MVAL_CFG_DRIVE__M 0x38
+#define SIO_PDR_MVAL_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MVAL_CFG_KEEP__B 6
+#define SIO_PDR_MVAL_CFG_KEEP__W 2
+#define SIO_PDR_MVAL_CFG_KEEP__M 0xC0
+#define SIO_PDR_MVAL_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MVAL_CFG_UIO__B 8
+#define SIO_PDR_MVAL_CFG_UIO__W 1
+#define SIO_PDR_MVAL_CFG_UIO__M 0x100
+#define SIO_PDR_MVAL_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD0_CFG__A 0x7F002A
+#define SIO_PDR_MD0_CFG__W 9
+#define SIO_PDR_MD0_CFG__M 0x1FF
+#define SIO_PDR_MD0_CFG__PRE 0x50
+#define SIO_PDR_MD0_CFG_MODE__B 0
+#define SIO_PDR_MD0_CFG_MODE__W 3
+#define SIO_PDR_MD0_CFG_MODE__M 0x7
+#define SIO_PDR_MD0_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD0_CFG_DRIVE__B 3
+#define SIO_PDR_MD0_CFG_DRIVE__W 3
+#define SIO_PDR_MD0_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD0_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD0_CFG_KEEP__B 6
+#define SIO_PDR_MD0_CFG_KEEP__W 2
+#define SIO_PDR_MD0_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD0_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD0_CFG_UIO__B 8
+#define SIO_PDR_MD0_CFG_UIO__W 1
+#define SIO_PDR_MD0_CFG_UIO__M 0x100
+#define SIO_PDR_MD0_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD1_CFG__A 0x7F002B
+#define SIO_PDR_MD1_CFG__W 9
+#define SIO_PDR_MD1_CFG__M 0x1FF
+#define SIO_PDR_MD1_CFG__PRE 0x50
+#define SIO_PDR_MD1_CFG_MODE__B 0
+#define SIO_PDR_MD1_CFG_MODE__W 3
+#define SIO_PDR_MD1_CFG_MODE__M 0x7
+#define SIO_PDR_MD1_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD1_CFG_DRIVE__B 3
+#define SIO_PDR_MD1_CFG_DRIVE__W 3
+#define SIO_PDR_MD1_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD1_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD1_CFG_KEEP__B 6
+#define SIO_PDR_MD1_CFG_KEEP__W 2
+#define SIO_PDR_MD1_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD1_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD1_CFG_UIO__B 8
+#define SIO_PDR_MD1_CFG_UIO__W 1
+#define SIO_PDR_MD1_CFG_UIO__M 0x100
+#define SIO_PDR_MD1_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD2_CFG__A 0x7F002C
+#define SIO_PDR_MD2_CFG__W 9
+#define SIO_PDR_MD2_CFG__M 0x1FF
+#define SIO_PDR_MD2_CFG__PRE 0x50
+#define SIO_PDR_MD2_CFG_MODE__B 0
+#define SIO_PDR_MD2_CFG_MODE__W 3
+#define SIO_PDR_MD2_CFG_MODE__M 0x7
+#define SIO_PDR_MD2_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD2_CFG_DRIVE__B 3
+#define SIO_PDR_MD2_CFG_DRIVE__W 3
+#define SIO_PDR_MD2_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD2_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD2_CFG_KEEP__B 6
+#define SIO_PDR_MD2_CFG_KEEP__W 2
+#define SIO_PDR_MD2_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD2_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD2_CFG_UIO__B 8
+#define SIO_PDR_MD2_CFG_UIO__W 1
+#define SIO_PDR_MD2_CFG_UIO__M 0x100
+#define SIO_PDR_MD2_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD3_CFG__A 0x7F002D
+#define SIO_PDR_MD3_CFG__W 9
+#define SIO_PDR_MD3_CFG__M 0x1FF
+#define SIO_PDR_MD3_CFG__PRE 0x50
+#define SIO_PDR_MD3_CFG_MODE__B 0
+#define SIO_PDR_MD3_CFG_MODE__W 3
+#define SIO_PDR_MD3_CFG_MODE__M 0x7
+#define SIO_PDR_MD3_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD3_CFG_DRIVE__B 3
+#define SIO_PDR_MD3_CFG_DRIVE__W 3
+#define SIO_PDR_MD3_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD3_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD3_CFG_KEEP__B 6
+#define SIO_PDR_MD3_CFG_KEEP__W 2
+#define SIO_PDR_MD3_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD3_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD3_CFG_UIO__B 8
+#define SIO_PDR_MD3_CFG_UIO__W 1
+#define SIO_PDR_MD3_CFG_UIO__M 0x100
+#define SIO_PDR_MD3_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD4_CFG__A 0x7F002F
+#define SIO_PDR_MD4_CFG__W 9
+#define SIO_PDR_MD4_CFG__M 0x1FF
+#define SIO_PDR_MD4_CFG__PRE 0x50
+#define SIO_PDR_MD4_CFG_MODE__B 0
+#define SIO_PDR_MD4_CFG_MODE__W 3
+#define SIO_PDR_MD4_CFG_MODE__M 0x7
+#define SIO_PDR_MD4_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD4_CFG_DRIVE__B 3
+#define SIO_PDR_MD4_CFG_DRIVE__W 3
+#define SIO_PDR_MD4_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD4_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD4_CFG_KEEP__B 6
+#define SIO_PDR_MD4_CFG_KEEP__W 2
+#define SIO_PDR_MD4_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD4_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD4_CFG_UIO__B 8
+#define SIO_PDR_MD4_CFG_UIO__W 1
+#define SIO_PDR_MD4_CFG_UIO__M 0x100
+#define SIO_PDR_MD4_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD5_CFG__A 0x7F0030
+#define SIO_PDR_MD5_CFG__W 9
+#define SIO_PDR_MD5_CFG__M 0x1FF
+#define SIO_PDR_MD5_CFG__PRE 0x50
+#define SIO_PDR_MD5_CFG_MODE__B 0
+#define SIO_PDR_MD5_CFG_MODE__W 3
+#define SIO_PDR_MD5_CFG_MODE__M 0x7
+#define SIO_PDR_MD5_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD5_CFG_DRIVE__B 3
+#define SIO_PDR_MD5_CFG_DRIVE__W 3
+#define SIO_PDR_MD5_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD5_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD5_CFG_KEEP__B 6
+#define SIO_PDR_MD5_CFG_KEEP__W 2
+#define SIO_PDR_MD5_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD5_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD5_CFG_UIO__B 8
+#define SIO_PDR_MD5_CFG_UIO__W 1
+#define SIO_PDR_MD5_CFG_UIO__M 0x100
+#define SIO_PDR_MD5_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD6_CFG__A 0x7F0031
+#define SIO_PDR_MD6_CFG__W 9
+#define SIO_PDR_MD6_CFG__M 0x1FF
+#define SIO_PDR_MD6_CFG__PRE 0x50
+#define SIO_PDR_MD6_CFG_MODE__B 0
+#define SIO_PDR_MD6_CFG_MODE__W 3
+#define SIO_PDR_MD6_CFG_MODE__M 0x7
+#define SIO_PDR_MD6_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD6_CFG_DRIVE__B 3
+#define SIO_PDR_MD6_CFG_DRIVE__W 3
+#define SIO_PDR_MD6_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD6_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD6_CFG_KEEP__B 6
+#define SIO_PDR_MD6_CFG_KEEP__W 2
+#define SIO_PDR_MD6_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD6_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD6_CFG_UIO__B 8
+#define SIO_PDR_MD6_CFG_UIO__W 1
+#define SIO_PDR_MD6_CFG_UIO__M 0x100
+#define SIO_PDR_MD6_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD7_CFG__A 0x7F0032
+#define SIO_PDR_MD7_CFG__W 9
+#define SIO_PDR_MD7_CFG__M 0x1FF
+#define SIO_PDR_MD7_CFG__PRE 0x50
+#define SIO_PDR_MD7_CFG_MODE__B 0
+#define SIO_PDR_MD7_CFG_MODE__W 3
+#define SIO_PDR_MD7_CFG_MODE__M 0x7
+#define SIO_PDR_MD7_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD7_CFG_DRIVE__B 3
+#define SIO_PDR_MD7_CFG_DRIVE__W 3
+#define SIO_PDR_MD7_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD7_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD7_CFG_KEEP__B 6
+#define SIO_PDR_MD7_CFG_KEEP__W 2
+#define SIO_PDR_MD7_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD7_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD7_CFG_UIO__B 8
+#define SIO_PDR_MD7_CFG_UIO__W 1
+#define SIO_PDR_MD7_CFG_UIO__M 0x100
+#define SIO_PDR_MD7_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2C_SCL1_CFG__A 0x7F0033
+#define SIO_PDR_I2C_SCL1_CFG__W 9
+#define SIO_PDR_I2C_SCL1_CFG__M 0x1FF
+#define SIO_PDR_I2C_SCL1_CFG__PRE 0x11
+#define SIO_PDR_I2C_SCL1_CFG_MODE__B 0
+#define SIO_PDR_I2C_SCL1_CFG_MODE__W 3
+#define SIO_PDR_I2C_SCL1_CFG_MODE__M 0x7
+#define SIO_PDR_I2C_SCL1_CFG_MODE__PRE 0x1
+#define SIO_PDR_I2C_SCL1_CFG_DRIVE__B 3
+#define SIO_PDR_I2C_SCL1_CFG_DRIVE__W 3
+#define SIO_PDR_I2C_SCL1_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2C_SCL1_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2C_SCL1_CFG_KEEP__B 6
+#define SIO_PDR_I2C_SCL1_CFG_KEEP__W 2
+#define SIO_PDR_I2C_SCL1_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2C_SCL1_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2C_SCL1_CFG_UIO__B 8
+#define SIO_PDR_I2C_SCL1_CFG_UIO__W 1
+#define SIO_PDR_I2C_SCL1_CFG_UIO__M 0x100
+#define SIO_PDR_I2C_SCL1_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2C_SDA1_CFG__A 0x7F0034
+#define SIO_PDR_I2C_SDA1_CFG__W 9
+#define SIO_PDR_I2C_SDA1_CFG__M 0x1FF
+#define SIO_PDR_I2C_SDA1_CFG__PRE 0x11
+#define SIO_PDR_I2C_SDA1_CFG_MODE__B 0
+#define SIO_PDR_I2C_SDA1_CFG_MODE__W 3
+#define SIO_PDR_I2C_SDA1_CFG_MODE__M 0x7
+#define SIO_PDR_I2C_SDA1_CFG_MODE__PRE 0x1
+#define SIO_PDR_I2C_SDA1_CFG_DRIVE__B 3
+#define SIO_PDR_I2C_SDA1_CFG_DRIVE__W 3
+#define SIO_PDR_I2C_SDA1_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2C_SDA1_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2C_SDA1_CFG_KEEP__B 6
+#define SIO_PDR_I2C_SDA1_CFG_KEEP__W 2
+#define SIO_PDR_I2C_SDA1_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2C_SDA1_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2C_SDA1_CFG_UIO__B 8
+#define SIO_PDR_I2C_SDA1_CFG_UIO__W 1
+#define SIO_PDR_I2C_SDA1_CFG_UIO__M 0x100
+#define SIO_PDR_I2C_SDA1_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_VSYNC_CFG__A 0x7F0036
+#define SIO_PDR_VSYNC_CFG__W 9
+#define SIO_PDR_VSYNC_CFG__M 0x1FF
+#define SIO_PDR_VSYNC_CFG__PRE 0x10
+#define SIO_PDR_VSYNC_CFG_MODE__B 0
+#define SIO_PDR_VSYNC_CFG_MODE__W 3
+#define SIO_PDR_VSYNC_CFG_MODE__M 0x7
+#define SIO_PDR_VSYNC_CFG_MODE__PRE 0x0
+#define SIO_PDR_VSYNC_CFG_DRIVE__B 3
+#define SIO_PDR_VSYNC_CFG_DRIVE__W 3
+#define SIO_PDR_VSYNC_CFG_DRIVE__M 0x38
+#define SIO_PDR_VSYNC_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_VSYNC_CFG_KEEP__B 6
+#define SIO_PDR_VSYNC_CFG_KEEP__W 2
+#define SIO_PDR_VSYNC_CFG_KEEP__M 0xC0
+#define SIO_PDR_VSYNC_CFG_KEEP__PRE 0x0
+#define SIO_PDR_VSYNC_CFG_UIO__B 8
+#define SIO_PDR_VSYNC_CFG_UIO__W 1
+#define SIO_PDR_VSYNC_CFG_UIO__M 0x100
+#define SIO_PDR_VSYNC_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_SMA_RX_CFG__A 0x7F0037
+#define SIO_PDR_SMA_RX_CFG__W 9
+#define SIO_PDR_SMA_RX_CFG__M 0x1FF
+#define SIO_PDR_SMA_RX_CFG__PRE 0x10
+#define SIO_PDR_SMA_RX_CFG_MODE__B 0
+#define SIO_PDR_SMA_RX_CFG_MODE__W 3
+#define SIO_PDR_SMA_RX_CFG_MODE__M 0x7
+#define SIO_PDR_SMA_RX_CFG_MODE__PRE 0x0
+#define SIO_PDR_SMA_RX_CFG_DRIVE__B 3
+#define SIO_PDR_SMA_RX_CFG_DRIVE__W 3
+#define SIO_PDR_SMA_RX_CFG_DRIVE__M 0x38
+#define SIO_PDR_SMA_RX_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_SMA_RX_CFG_KEEP__B 6
+#define SIO_PDR_SMA_RX_CFG_KEEP__W 2
+#define SIO_PDR_SMA_RX_CFG_KEEP__M 0xC0
+#define SIO_PDR_SMA_RX_CFG_KEEP__PRE 0x0
+#define SIO_PDR_SMA_RX_CFG_UIO__B 8
+#define SIO_PDR_SMA_RX_CFG_UIO__W 1
+#define SIO_PDR_SMA_RX_CFG_UIO__M 0x100
+#define SIO_PDR_SMA_RX_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_SMA_TX_CFG__A 0x7F0038
+#define SIO_PDR_SMA_TX_CFG__W 9
+#define SIO_PDR_SMA_TX_CFG__M 0x1FF
+#define SIO_PDR_SMA_TX_CFG__PRE 0x90
+#define SIO_PDR_SMA_TX_CFG_MODE__B 0
+#define SIO_PDR_SMA_TX_CFG_MODE__W 3
+#define SIO_PDR_SMA_TX_CFG_MODE__M 0x7
+#define SIO_PDR_SMA_TX_CFG_MODE__PRE 0x0
+#define SIO_PDR_SMA_TX_CFG_DRIVE__B 3
+#define SIO_PDR_SMA_TX_CFG_DRIVE__W 3
+#define SIO_PDR_SMA_TX_CFG_DRIVE__M 0x38
+#define SIO_PDR_SMA_TX_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_SMA_TX_CFG_KEEP__B 6
+#define SIO_PDR_SMA_TX_CFG_KEEP__W 2
+#define SIO_PDR_SMA_TX_CFG_KEEP__M 0xC0
+#define SIO_PDR_SMA_TX_CFG_KEEP__PRE 0x80
+#define SIO_PDR_SMA_TX_CFG_UIO__B 8
+#define SIO_PDR_SMA_TX_CFG_UIO__W 1
+#define SIO_PDR_SMA_TX_CFG_UIO__M 0x100
+#define SIO_PDR_SMA_TX_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2C_SDA2_CFG__A 0x7F003F
+#define SIO_PDR_I2C_SDA2_CFG__W 9
+#define SIO_PDR_I2C_SDA2_CFG__M 0x1FF
+#define SIO_PDR_I2C_SDA2_CFG__PRE 0x11
+#define SIO_PDR_I2C_SDA2_CFG_MODE__B 0
+#define SIO_PDR_I2C_SDA2_CFG_MODE__W 3
+#define SIO_PDR_I2C_SDA2_CFG_MODE__M 0x7
+#define SIO_PDR_I2C_SDA2_CFG_MODE__PRE 0x1
+#define SIO_PDR_I2C_SDA2_CFG_DRIVE__B 3
+#define SIO_PDR_I2C_SDA2_CFG_DRIVE__W 3
+#define SIO_PDR_I2C_SDA2_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2C_SDA2_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2C_SDA2_CFG_KEEP__B 6
+#define SIO_PDR_I2C_SDA2_CFG_KEEP__W 2
+#define SIO_PDR_I2C_SDA2_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2C_SDA2_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2C_SDA2_CFG_UIO__B 8
+#define SIO_PDR_I2C_SDA2_CFG_UIO__W 1
+#define SIO_PDR_I2C_SDA2_CFG_UIO__M 0x100
+#define SIO_PDR_I2C_SDA2_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2C_SCL2_CFG__A 0x7F0040
+#define SIO_PDR_I2C_SCL2_CFG__W 9
+#define SIO_PDR_I2C_SCL2_CFG__M 0x1FF
+#define SIO_PDR_I2C_SCL2_CFG__PRE 0x11
+#define SIO_PDR_I2C_SCL2_CFG_MODE__B 0
+#define SIO_PDR_I2C_SCL2_CFG_MODE__W 3
+#define SIO_PDR_I2C_SCL2_CFG_MODE__M 0x7
+#define SIO_PDR_I2C_SCL2_CFG_MODE__PRE 0x1
+#define SIO_PDR_I2C_SCL2_CFG_DRIVE__B 3
+#define SIO_PDR_I2C_SCL2_CFG_DRIVE__W 3
+#define SIO_PDR_I2C_SCL2_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2C_SCL2_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2C_SCL2_CFG_KEEP__B 6
+#define SIO_PDR_I2C_SCL2_CFG_KEEP__W 2
+#define SIO_PDR_I2C_SCL2_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2C_SCL2_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2C_SCL2_CFG_UIO__B 8
+#define SIO_PDR_I2C_SCL2_CFG_UIO__W 1
+#define SIO_PDR_I2C_SCL2_CFG_UIO__M 0x100
+#define SIO_PDR_I2C_SCL2_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2S_CL_CFG__A 0x7F0041
+#define SIO_PDR_I2S_CL_CFG__W 9
+#define SIO_PDR_I2S_CL_CFG__M 0x1FF
+#define SIO_PDR_I2S_CL_CFG__PRE 0x10
+#define SIO_PDR_I2S_CL_CFG_MODE__B 0
+#define SIO_PDR_I2S_CL_CFG_MODE__W 3
+#define SIO_PDR_I2S_CL_CFG_MODE__M 0x7
+#define SIO_PDR_I2S_CL_CFG_MODE__PRE 0x0
+#define SIO_PDR_I2S_CL_CFG_DRIVE__B 3
+#define SIO_PDR_I2S_CL_CFG_DRIVE__W 3
+#define SIO_PDR_I2S_CL_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2S_CL_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2S_CL_CFG_KEEP__B 6
+#define SIO_PDR_I2S_CL_CFG_KEEP__W 2
+#define SIO_PDR_I2S_CL_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2S_CL_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2S_CL_CFG_UIO__B 8
+#define SIO_PDR_I2S_CL_CFG_UIO__W 1
+#define SIO_PDR_I2S_CL_CFG_UIO__M 0x100
+#define SIO_PDR_I2S_CL_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2S_DA_CFG__A 0x7F0042
+#define SIO_PDR_I2S_DA_CFG__W 9
+#define SIO_PDR_I2S_DA_CFG__M 0x1FF
+#define SIO_PDR_I2S_DA_CFG__PRE 0x10
+#define SIO_PDR_I2S_DA_CFG_MODE__B 0
+#define SIO_PDR_I2S_DA_CFG_MODE__W 3
+#define SIO_PDR_I2S_DA_CFG_MODE__M 0x7
+#define SIO_PDR_I2S_DA_CFG_MODE__PRE 0x0
+#define SIO_PDR_I2S_DA_CFG_DRIVE__B 3
+#define SIO_PDR_I2S_DA_CFG_DRIVE__W 3
+#define SIO_PDR_I2S_DA_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2S_DA_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2S_DA_CFG_KEEP__B 6
+#define SIO_PDR_I2S_DA_CFG_KEEP__W 2
+#define SIO_PDR_I2S_DA_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2S_DA_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2S_DA_CFG_UIO__B 8
+#define SIO_PDR_I2S_DA_CFG_UIO__W 1
+#define SIO_PDR_I2S_DA_CFG_UIO__M 0x100
+#define SIO_PDR_I2S_DA_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_GPIO_GPIO_FNC__A 0x7F0050
+#define SIO_PDR_GPIO_GPIO_FNC__W 2
+#define SIO_PDR_GPIO_GPIO_FNC__M 0x3
+#define SIO_PDR_GPIO_GPIO_FNC__PRE 0x0
+#define SIO_PDR_GPIO_GPIO_FNC_SEL__B 0
+#define SIO_PDR_GPIO_GPIO_FNC_SEL__W 2
+#define SIO_PDR_GPIO_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_GPIO_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_IRQN_GPIO_FNC__A 0x7F0051
+#define SIO_PDR_IRQN_GPIO_FNC__W 2
+#define SIO_PDR_IRQN_GPIO_FNC__M 0x3
+#define SIO_PDR_IRQN_GPIO_FNC__PRE 0x0
+#define SIO_PDR_IRQN_GPIO_FNC_SEL__B 0
+#define SIO_PDR_IRQN_GPIO_FNC_SEL__W 2
+#define SIO_PDR_IRQN_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_IRQN_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MSTRT_GPIO_FNC__A 0x7F0052
+#define SIO_PDR_MSTRT_GPIO_FNC__W 2
+#define SIO_PDR_MSTRT_GPIO_FNC__M 0x3
+#define SIO_PDR_MSTRT_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MSTRT_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MSTRT_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MSTRT_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MSTRT_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MERR_GPIO_FNC__A 0x7F0053
+#define SIO_PDR_MERR_GPIO_FNC__W 2
+#define SIO_PDR_MERR_GPIO_FNC__M 0x3
+#define SIO_PDR_MERR_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MERR_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MERR_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MERR_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MERR_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MCLK_GPIO_FNC__A 0x7F0054
+#define SIO_PDR_MCLK_GPIO_FNC__W 2
+#define SIO_PDR_MCLK_GPIO_FNC__M 0x3
+#define SIO_PDR_MCLK_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MCLK_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MCLK_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MCLK_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MCLK_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MVAL_GPIO_FNC__A 0x7F0055
+#define SIO_PDR_MVAL_GPIO_FNC__W 2
+#define SIO_PDR_MVAL_GPIO_FNC__M 0x3
+#define SIO_PDR_MVAL_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MVAL_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MVAL_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MVAL_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MVAL_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD0_GPIO_FNC__A 0x7F0056
+#define SIO_PDR_MD0_GPIO_FNC__W 2
+#define SIO_PDR_MD0_GPIO_FNC__M 0x3
+#define SIO_PDR_MD0_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD0_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD0_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD0_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD0_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD1_GPIO_FNC__A 0x7F0057
+#define SIO_PDR_MD1_GPIO_FNC__W 2
+#define SIO_PDR_MD1_GPIO_FNC__M 0x3
+#define SIO_PDR_MD1_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD1_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD1_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD1_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD1_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD2_GPIO_FNC__A 0x7F0058
+#define SIO_PDR_MD2_GPIO_FNC__W 2
+#define SIO_PDR_MD2_GPIO_FNC__M 0x3
+#define SIO_PDR_MD2_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD2_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD2_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD2_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD2_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD3_GPIO_FNC__A 0x7F0059
+#define SIO_PDR_MD3_GPIO_FNC__W 2
+#define SIO_PDR_MD3_GPIO_FNC__M 0x3
+#define SIO_PDR_MD3_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD3_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD3_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD3_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD3_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD4_GPIO_FNC__A 0x7F005A
+#define SIO_PDR_MD4_GPIO_FNC__W 2
+#define SIO_PDR_MD4_GPIO_FNC__M 0x3
+#define SIO_PDR_MD4_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD4_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD4_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD4_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD4_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD5_GPIO_FNC__A 0x7F005B
+#define SIO_PDR_MD5_GPIO_FNC__W 2
+#define SIO_PDR_MD5_GPIO_FNC__M 0x3
+#define SIO_PDR_MD5_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD5_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD5_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD5_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD5_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD6_GPIO_FNC__A 0x7F005C
+#define SIO_PDR_MD6_GPIO_FNC__W 2
+#define SIO_PDR_MD6_GPIO_FNC__M 0x3
+#define SIO_PDR_MD6_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD6_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD6_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD6_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD6_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD7_GPIO_FNC__A 0x7F005D
+#define SIO_PDR_MD7_GPIO_FNC__W 2
+#define SIO_PDR_MD7_GPIO_FNC__M 0x3
+#define SIO_PDR_MD7_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD7_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD7_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD7_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD7_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_SMA_RX_GPIO_FNC__A 0x7F005E
+#define SIO_PDR_SMA_RX_GPIO_FNC__W 2
+#define SIO_PDR_SMA_RX_GPIO_FNC__M 0x3
+#define SIO_PDR_SMA_RX_GPIO_FNC__PRE 0x0
+#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__B 0
+#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__W 2
+#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_SMA_TX_GPIO_FNC__A 0x7F005F
+#define SIO_PDR_SMA_TX_GPIO_FNC__W 2
+#define SIO_PDR_SMA_TX_GPIO_FNC__M 0x3
+#define SIO_PDR_SMA_TX_GPIO_FNC__PRE 0x0
+#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__B 0
+#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__W 2
+#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__PRE 0x0
+
+#define VSB_COMM_EXEC__A 0x1C00000
+#define VSB_COMM_EXEC__W 2
+#define VSB_COMM_EXEC__M 0x3
+#define VSB_COMM_EXEC__PRE 0x0
+#define VSB_COMM_EXEC_STOP 0x0
+#define VSB_COMM_EXEC_ACTIVE 0x1
+#define VSB_COMM_EXEC_HOLD 0x2
+
+#define VSB_COMM_MB__A 0x1C00002
+#define VSB_COMM_MB__W 16
+#define VSB_COMM_MB__M 0xFFFF
+#define VSB_COMM_MB__PRE 0x0
+#define VSB_COMM_INT_REQ__A 0x1C00003
+#define VSB_COMM_INT_REQ__W 1
+#define VSB_COMM_INT_REQ__M 0x1
+#define VSB_COMM_INT_REQ__PRE 0x0
+
+#define VSB_COMM_INT_REQ_TOP_INT_REQ__B 0
+#define VSB_COMM_INT_REQ_TOP_INT_REQ__W 1
+#define VSB_COMM_INT_REQ_TOP_INT_REQ__M 0x1
+#define VSB_COMM_INT_REQ_TOP_INT_REQ__PRE 0x0
+
+#define VSB_COMM_INT_STA__A 0x1C00005
+#define VSB_COMM_INT_STA__W 16
+#define VSB_COMM_INT_STA__M 0xFFFF
+#define VSB_COMM_INT_STA__PRE 0x0
+
+#define VSB_COMM_INT_MSK__A 0x1C00006
+#define VSB_COMM_INT_MSK__W 16
+#define VSB_COMM_INT_MSK__M 0xFFFF
+#define VSB_COMM_INT_MSK__PRE 0x0
+
+#define VSB_COMM_INT_STM__A 0x1C00007
+#define VSB_COMM_INT_STM__W 16
+#define VSB_COMM_INT_STM__M 0xFFFF
+#define VSB_COMM_INT_STM__PRE 0x0
+
+#define VSB_TOP_COMM_EXEC__A 0x1C10000
+#define VSB_TOP_COMM_EXEC__W 2
+#define VSB_TOP_COMM_EXEC__M 0x3
+#define VSB_TOP_COMM_EXEC__PRE 0x0
+#define VSB_TOP_COMM_EXEC_STOP 0x0
+#define VSB_TOP_COMM_EXEC_ACTIVE 0x1
+#define VSB_TOP_COMM_EXEC_HOLD 0x2
+
+#define VSB_TOP_COMM_MB__A 0x1C10002
+#define VSB_TOP_COMM_MB__W 10
+#define VSB_TOP_COMM_MB__M 0x3FF
+#define VSB_TOP_COMM_MB__PRE 0x0
+
+#define VSB_TOP_COMM_MB_CTL__B 0
+#define VSB_TOP_COMM_MB_CTL__W 1
+#define VSB_TOP_COMM_MB_CTL__M 0x1
+#define VSB_TOP_COMM_MB_CTL__PRE 0x0
+#define VSB_TOP_COMM_MB_CTL_CTL_OFF 0x0
+#define VSB_TOP_COMM_MB_CTL_CTL_ON 0x1
+
+#define VSB_TOP_COMM_MB_OBS__B 1
+#define VSB_TOP_COMM_MB_OBS__W 1
+#define VSB_TOP_COMM_MB_OBS__M 0x2
+#define VSB_TOP_COMM_MB_OBS__PRE 0x0
+#define VSB_TOP_COMM_MB_OBS_OBS_OFF 0x0
+#define VSB_TOP_COMM_MB_OBS_OBS_ON 0x2
+
+#define VSB_TOP_COMM_MB_MUX_CTL__B 2
+#define VSB_TOP_COMM_MB_MUX_CTL__W 4
+#define VSB_TOP_COMM_MB_MUX_CTL__M 0x3C
+#define VSB_TOP_COMM_MB_MUX_CTL__PRE 0x0
+
+#define VSB_TOP_COMM_MB_MUX_OBS__B 6
+#define VSB_TOP_COMM_MB_MUX_OBS__W 4
+#define VSB_TOP_COMM_MB_MUX_OBS__M 0x3C0
+#define VSB_TOP_COMM_MB_MUX_OBS__PRE 0x0
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_FEC 0x0
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_IQM 0x40
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_IQM_AMPLITUDE 0x80
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_TCMEQ_1 0xC0
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_TCMEQ_2 0x100
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_FFE_1 0x140
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_FFE_2 0x180
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_DFE_1 0x1C0
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_DFE_2 0x200
+
+#define VSB_TOP_COMM_INT_REQ__A 0x1C10003
+#define VSB_TOP_COMM_INT_REQ__W 1
+#define VSB_TOP_COMM_INT_REQ__M 0x1
+#define VSB_TOP_COMM_INT_REQ__PRE 0x0
+#define VSB_TOP_COMM_INT_STA__A 0x1C10005
+#define VSB_TOP_COMM_INT_STA__W 6
+#define VSB_TOP_COMM_INT_STA__M 0x3F
+#define VSB_TOP_COMM_INT_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__B 0
+#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__W 1
+#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__M 0x1
+#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_LOCK_STA__B 1
+#define VSB_TOP_COMM_INT_STA_LOCK_STA__W 1
+#define VSB_TOP_COMM_INT_STA_LOCK_STA__M 0x2
+#define VSB_TOP_COMM_INT_STA_LOCK_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__B 2
+#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__W 1
+#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__M 0x4
+#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__B 3
+#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__W 1
+#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__M 0x8
+#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__B 4
+#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__W 1
+#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__M 0x10
+#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_MERSER_STA__B 5
+#define VSB_TOP_COMM_INT_STA_MERSER_STA__W 1
+#define VSB_TOP_COMM_INT_STA_MERSER_STA__M 0x20
+#define VSB_TOP_COMM_INT_STA_MERSER_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK__A 0x1C10006
+#define VSB_TOP_COMM_INT_MSK__W 6
+#define VSB_TOP_COMM_INT_MSK__M 0x3F
+#define VSB_TOP_COMM_INT_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__B 0
+#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__M 0x1
+#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__B 1
+#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__M 0x2
+#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__B 2
+#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__M 0x4
+#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__B 3
+#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__M 0x8
+#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__B 4
+#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__M 0x10
+#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__B 5
+#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__M 0x20
+#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM__A 0x1C10007
+#define VSB_TOP_COMM_INT_STM__W 6
+#define VSB_TOP_COMM_INT_STM__M 0x3F
+#define VSB_TOP_COMM_INT_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__B 0
+#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__W 1
+#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__M 0x1
+#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_LOCK_STM__B 1
+#define VSB_TOP_COMM_INT_STM_LOCK_STM__W 1
+#define VSB_TOP_COMM_INT_STM_LOCK_STM__M 0x2
+#define VSB_TOP_COMM_INT_STM_LOCK_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__B 2
+#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__W 1
+#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__M 0x4
+#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__B 3
+#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__W 1
+#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__M 0x8
+#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__B 4
+#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__W 1
+#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__M 0x10
+#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_MERSER_STM__B 5
+#define VSB_TOP_COMM_INT_STM_MERSER_STM__W 1
+#define VSB_TOP_COMM_INT_STM_MERSER_STM__M 0x20
+#define VSB_TOP_COMM_INT_STM_MERSER_STM__PRE 0x0
+
+#define VSB_TOP_CKGN1ACQ__A 0x1C10010
+#define VSB_TOP_CKGN1ACQ__W 8
+#define VSB_TOP_CKGN1ACQ__M 0xFF
+#define VSB_TOP_CKGN1ACQ__PRE 0x4
+
+#define VSB_TOP_CKGN1TRK__A 0x1C10011
+#define VSB_TOP_CKGN1TRK__W 8
+#define VSB_TOP_CKGN1TRK__M 0xFF
+#define VSB_TOP_CKGN1TRK__PRE 0x0
+
+#define VSB_TOP_CKGN2ACQ__A 0x1C10012
+#define VSB_TOP_CKGN2ACQ__W 8
+#define VSB_TOP_CKGN2ACQ__M 0xFF
+#define VSB_TOP_CKGN2ACQ__PRE 0x2
+
+#define VSB_TOP_CKGN2TRK__A 0x1C10013
+#define VSB_TOP_CKGN2TRK__W 8
+#define VSB_TOP_CKGN2TRK__M 0xFF
+#define VSB_TOP_CKGN2TRK__PRE 0x1
+
+#define VSB_TOP_CKGN3__A 0x1C10014
+#define VSB_TOP_CKGN3__W 8
+#define VSB_TOP_CKGN3__M 0xFF
+#define VSB_TOP_CKGN3__PRE 0x5
+
+#define VSB_TOP_CYGN1ACQ__A 0x1C10015
+#define VSB_TOP_CYGN1ACQ__W 8
+#define VSB_TOP_CYGN1ACQ__M 0xFF
+#define VSB_TOP_CYGN1ACQ__PRE 0x3
+
+#define VSB_TOP_CYGN1TRK__A 0x1C10016
+#define VSB_TOP_CYGN1TRK__W 8
+#define VSB_TOP_CYGN1TRK__M 0xFF
+#define VSB_TOP_CYGN1TRK__PRE 0x0
+
+#define VSB_TOP_CYGN2ACQ__A 0x1C10017
+#define VSB_TOP_CYGN2ACQ__W 8
+#define VSB_TOP_CYGN2ACQ__M 0xFF
+#define VSB_TOP_CYGN2ACQ__PRE 0x3
+
+#define VSB_TOP_CYGN2TRK__A 0x1C10018
+#define VSB_TOP_CYGN2TRK__W 8
+#define VSB_TOP_CYGN2TRK__M 0xFF
+#define VSB_TOP_CYGN2TRK__PRE 0x2
+
+#define VSB_TOP_CYGN3__A 0x1C10019
+#define VSB_TOP_CYGN3__W 8
+#define VSB_TOP_CYGN3__M 0xFF
+#define VSB_TOP_CYGN3__PRE 0x6
+#define VSB_TOP_SYNCCTRLWORD__A 0x1C1001A
+#define VSB_TOP_SYNCCTRLWORD__W 5
+#define VSB_TOP_SYNCCTRLWORD__M 0x1F
+#define VSB_TOP_SYNCCTRLWORD__PRE 0x0
+
+#define VSB_TOP_SYNCCTRLWORD_PRST__B 0
+#define VSB_TOP_SYNCCTRLWORD_PRST__W 1
+#define VSB_TOP_SYNCCTRLWORD_PRST__M 0x1
+#define VSB_TOP_SYNCCTRLWORD_PRST__PRE 0x0
+
+#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__B 1
+#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__W 1
+#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__M 0x2
+#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__PRE 0x0
+
+#define VSB_TOP_SYNCCTRLWORD_INVCNST__B 2
+#define VSB_TOP_SYNCCTRLWORD_INVCNST__W 1
+#define VSB_TOP_SYNCCTRLWORD_INVCNST__M 0x4
+#define VSB_TOP_SYNCCTRLWORD_INVCNST__PRE 0x0
+
+#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__B 3
+#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__W 1
+#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__M 0x8
+#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__PRE 0x0
+
+#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__B 4
+#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__W 1
+#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__M 0x10
+#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__PRE 0x0
+
+#define VSB_TOP_MAINSMUP__A 0x1C1001B
+#define VSB_TOP_MAINSMUP__W 8
+#define VSB_TOP_MAINSMUP__M 0xFF
+#define VSB_TOP_MAINSMUP__PRE 0xFF
+
+#define VSB_TOP_EQSMUP__A 0x1C1001C
+#define VSB_TOP_EQSMUP__W 8
+#define VSB_TOP_EQSMUP__M 0xFF
+#define VSB_TOP_EQSMUP__PRE 0xFF
+#define VSB_TOP_SYSMUXCTRL__A 0x1C1001D
+#define VSB_TOP_SYSMUXCTRL__W 13
+#define VSB_TOP_SYSMUXCTRL__M 0x1FFF
+#define VSB_TOP_SYSMUXCTRL__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__B 0
+#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__M 0x1
+#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__B 1
+#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__M 0x2
+#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__B 2
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__M 0x4
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__B 3
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__M 0x8
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__B 4
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__M 0x10
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__B 5
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__M 0x20
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__B 6
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__M 0x40
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__B 7
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__M 0x80
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__B 8
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__W 4
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__M 0xF00
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__B 12
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__M 0x1000
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__PRE 0x0
+
+#define VSB_TOP_SNRTH_RCA1__A 0x1C1001E
+#define VSB_TOP_SNRTH_RCA1__W 8
+#define VSB_TOP_SNRTH_RCA1__M 0xFF
+#define VSB_TOP_SNRTH_RCA1__PRE 0x53
+
+#define VSB_TOP_SNRTH_RCA1_DN__B 0
+#define VSB_TOP_SNRTH_RCA1_DN__W 4
+#define VSB_TOP_SNRTH_RCA1_DN__M 0xF
+#define VSB_TOP_SNRTH_RCA1_DN__PRE 0x3
+
+#define VSB_TOP_SNRTH_RCA1_UP__B 4
+#define VSB_TOP_SNRTH_RCA1_UP__W 4
+#define VSB_TOP_SNRTH_RCA1_UP__M 0xF0
+#define VSB_TOP_SNRTH_RCA1_UP__PRE 0x50
+
+#define VSB_TOP_SNRTH_RCA2__A 0x1C1001F
+#define VSB_TOP_SNRTH_RCA2__W 8
+#define VSB_TOP_SNRTH_RCA2__M 0xFF
+#define VSB_TOP_SNRTH_RCA2__PRE 0x75
+
+#define VSB_TOP_SNRTH_RCA2_DN__B 0
+#define VSB_TOP_SNRTH_RCA2_DN__W 4
+#define VSB_TOP_SNRTH_RCA2_DN__M 0xF
+#define VSB_TOP_SNRTH_RCA2_DN__PRE 0x5
+
+#define VSB_TOP_SNRTH_RCA2_UP__B 4
+#define VSB_TOP_SNRTH_RCA2_UP__W 4
+#define VSB_TOP_SNRTH_RCA2_UP__M 0xF0
+#define VSB_TOP_SNRTH_RCA2_UP__PRE 0x70
+
+#define VSB_TOP_SNRTH_DDM1__A 0x1C10020
+#define VSB_TOP_SNRTH_DDM1__W 8
+#define VSB_TOP_SNRTH_DDM1__M 0xFF
+#define VSB_TOP_SNRTH_DDM1__PRE 0xCA
+
+#define VSB_TOP_SNRTH_DDM1_DN__B 0
+#define VSB_TOP_SNRTH_DDM1_DN__W 4
+#define VSB_TOP_SNRTH_DDM1_DN__M 0xF
+#define VSB_TOP_SNRTH_DDM1_DN__PRE 0xA
+
+#define VSB_TOP_SNRTH_DDM1_UP__B 4
+#define VSB_TOP_SNRTH_DDM1_UP__W 4
+#define VSB_TOP_SNRTH_DDM1_UP__M 0xF0
+#define VSB_TOP_SNRTH_DDM1_UP__PRE 0xC0
+
+#define VSB_TOP_SNRTH_DDM2__A 0x1C10021
+#define VSB_TOP_SNRTH_DDM2__W 8
+#define VSB_TOP_SNRTH_DDM2__M 0xFF
+#define VSB_TOP_SNRTH_DDM2__PRE 0xCA
+
+#define VSB_TOP_SNRTH_DDM2_DN__B 0
+#define VSB_TOP_SNRTH_DDM2_DN__W 4
+#define VSB_TOP_SNRTH_DDM2_DN__M 0xF
+#define VSB_TOP_SNRTH_DDM2_DN__PRE 0xA
+
+#define VSB_TOP_SNRTH_DDM2_UP__B 4
+#define VSB_TOP_SNRTH_DDM2_UP__W 4
+#define VSB_TOP_SNRTH_DDM2_UP__M 0xF0
+#define VSB_TOP_SNRTH_DDM2_UP__PRE 0xC0
+
+#define VSB_TOP_SNRTH_PT__A 0x1C10022
+#define VSB_TOP_SNRTH_PT__W 8
+#define VSB_TOP_SNRTH_PT__M 0xFF
+#define VSB_TOP_SNRTH_PT__PRE 0xD8
+
+#define VSB_TOP_SNRTH_PT_DN__B 0
+#define VSB_TOP_SNRTH_PT_DN__W 4
+#define VSB_TOP_SNRTH_PT_DN__M 0xF
+#define VSB_TOP_SNRTH_PT_DN__PRE 0x8
+
+#define VSB_TOP_SNRTH_PT_UP__B 4
+#define VSB_TOP_SNRTH_PT_UP__W 4
+#define VSB_TOP_SNRTH_PT_UP__M 0xF0
+#define VSB_TOP_SNRTH_PT_UP__PRE 0xD0
+
+#define VSB_TOP_CYSMSTATES__A 0x1C10023
+#define VSB_TOP_CYSMSTATES__W 8
+#define VSB_TOP_CYSMSTATES__M 0xFF
+#define VSB_TOP_CYSMSTATES__PRE 0x0
+
+#define VSB_TOP_CYSMSTATES_SYSST__B 0
+#define VSB_TOP_CYSMSTATES_SYSST__W 4
+#define VSB_TOP_CYSMSTATES_SYSST__M 0xF
+#define VSB_TOP_CYSMSTATES_SYSST__PRE 0x0
+
+#define VSB_TOP_CYSMSTATES_EQST__B 4
+#define VSB_TOP_CYSMSTATES_EQST__W 4
+#define VSB_TOP_CYSMSTATES_EQST__M 0xF0
+#define VSB_TOP_CYSMSTATES_EQST__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL__A 0x1C10024
+#define VSB_TOP_SMALL_NOTCH_CONTROL__W 8
+#define VSB_TOP_SMALL_NOTCH_CONTROL__M 0xFF
+#define VSB_TOP_SMALL_NOTCH_CONTROL__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__B 0
+#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__W 1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__M 0x1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__B 1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__W 1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__M 0x2
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__B 2
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__W 1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__M 0x4
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__B 3
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__W 4
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__M 0x78
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__B 7
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__W 1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__M 0x80
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__PRE 0x0
+
+#define VSB_TOP_TAPREADCYC__A 0x1C10025
+#define VSB_TOP_TAPREADCYC__W 9
+#define VSB_TOP_TAPREADCYC__M 0x1FF
+#define VSB_TOP_TAPREADCYC__PRE 0x1
+
+#define VSB_TOP_VALIDPKLVL__A 0x1C10026
+#define VSB_TOP_VALIDPKLVL__W 13
+#define VSB_TOP_VALIDPKLVL__M 0x1FFF
+#define VSB_TOP_VALIDPKLVL__PRE 0x100
+
+#define VSB_TOP_CENTROID_FINE_DELAY__A 0x1C10027
+#define VSB_TOP_CENTROID_FINE_DELAY__W 10
+#define VSB_TOP_CENTROID_FINE_DELAY__M 0x3FF
+#define VSB_TOP_CENTROID_FINE_DELAY__PRE 0xFF
+
+#define VSB_TOP_CENTROID_SMACH_DELAY__A 0x1C10028
+#define VSB_TOP_CENTROID_SMACH_DELAY__W 10
+#define VSB_TOP_CENTROID_SMACH_DELAY__M 0x3FF
+#define VSB_TOP_CENTROID_SMACH_DELAY__PRE 0x1FF
+
+#define VSB_TOP_SNR__A 0x1C10029
+#define VSB_TOP_SNR__W 14
+#define VSB_TOP_SNR__M 0x3FFF
+#define VSB_TOP_SNR__PRE 0x0
+#define VSB_TOP_LOCKSTATUS__A 0x1C1002A
+#define VSB_TOP_LOCKSTATUS__W 7
+#define VSB_TOP_LOCKSTATUS__M 0x7F
+#define VSB_TOP_LOCKSTATUS__PRE 0x0
+
+#define VSB_TOP_LOCKSTATUS_VSBMODE__B 0
+#define VSB_TOP_LOCKSTATUS_VSBMODE__W 4
+#define VSB_TOP_LOCKSTATUS_VSBMODE__M 0xF
+#define VSB_TOP_LOCKSTATUS_VSBMODE__PRE 0x0
+
+#define VSB_TOP_LOCKSTATUS_FRMLOCK__B 4
+#define VSB_TOP_LOCKSTATUS_FRMLOCK__W 1
+#define VSB_TOP_LOCKSTATUS_FRMLOCK__M 0x10
+#define VSB_TOP_LOCKSTATUS_FRMLOCK__PRE 0x0
+
+#define VSB_TOP_LOCKSTATUS_CYLOCK__B 5
+#define VSB_TOP_LOCKSTATUS_CYLOCK__W 1
+#define VSB_TOP_LOCKSTATUS_CYLOCK__M 0x20
+#define VSB_TOP_LOCKSTATUS_CYLOCK__PRE 0x0
+
+#define VSB_TOP_LOCKSTATUS_DDMON__B 6
+#define VSB_TOP_LOCKSTATUS_DDMON__W 1
+#define VSB_TOP_LOCKSTATUS_DDMON__M 0x40
+#define VSB_TOP_LOCKSTATUS_DDMON__PRE 0x0
+
+#define VSB_TOP_CTST__A 0x1C1002B
+#define VSB_TOP_CTST__W 4
+#define VSB_TOP_CTST__M 0xF
+#define VSB_TOP_CTST__PRE 0x0
+#define VSB_TOP_EQSMRSTCTRL__A 0x1C1002C
+#define VSB_TOP_EQSMRSTCTRL__W 7
+#define VSB_TOP_EQSMRSTCTRL__M 0x7F
+#define VSB_TOP_EQSMRSTCTRL__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_RCAON__B 0
+#define VSB_TOP_EQSMRSTCTRL_RCAON__W 1
+#define VSB_TOP_EQSMRSTCTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMRSTCTRL_RCAON__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_DFEON__B 1
+#define VSB_TOP_EQSMRSTCTRL_DFEON__W 1
+#define VSB_TOP_EQSMRSTCTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMRSTCTRL_DFEON__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMRSTCTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMRSTCTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMRSTCTRL_DDMEN1__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMRSTCTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMRSTCTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMRSTCTRL_DDMEN2__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL__A 0x1C1002D
+#define VSB_TOP_EQSMTRNCTRL__W 7
+#define VSB_TOP_EQSMTRNCTRL__M 0x7F
+#define VSB_TOP_EQSMTRNCTRL__PRE 0x40
+
+#define VSB_TOP_EQSMTRNCTRL_RCAON__B 0
+#define VSB_TOP_EQSMTRNCTRL_RCAON__W 1
+#define VSB_TOP_EQSMTRNCTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMTRNCTRL_RCAON__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_DFEON__B 1
+#define VSB_TOP_EQSMTRNCTRL_DFEON__W 1
+#define VSB_TOP_EQSMTRNCTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMTRNCTRL_DFEON__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMTRNCTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMTRNCTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMTRNCTRL_DDMEN1__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMTRNCTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMTRNCTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMTRNCTRL_DDMEN2__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__PRE 0x40
+
+#define VSB_TOP_EQSMRCA1CTRL__A 0x1C1002E
+#define VSB_TOP_EQSMRCA1CTRL__W 7
+#define VSB_TOP_EQSMRCA1CTRL__M 0x7F
+#define VSB_TOP_EQSMRCA1CTRL__PRE 0x1
+
+#define VSB_TOP_EQSMRCA1CTRL_RCAON__B 0
+#define VSB_TOP_EQSMRCA1CTRL_RCAON__W 1
+#define VSB_TOP_EQSMRCA1CTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMRCA1CTRL_RCAON__PRE 0x1
+
+#define VSB_TOP_EQSMRCA1CTRL_DFEON__B 1
+#define VSB_TOP_EQSMRCA1CTRL_DFEON__W 1
+#define VSB_TOP_EQSMRCA1CTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMRCA1CTRL_DFEON__PRE 0x0
+
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__PRE 0x0
+
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__PRE 0x0
+
+#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__PRE 0x0
+
+#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__PRE 0x0
+
+#define VSB_TOP_EQSMRCA2CTRL__A 0x1C1002F
+#define VSB_TOP_EQSMRCA2CTRL__W 7
+#define VSB_TOP_EQSMRCA2CTRL__M 0x7F
+#define VSB_TOP_EQSMRCA2CTRL__PRE 0x3
+
+#define VSB_TOP_EQSMRCA2CTRL_RCAON__B 0
+#define VSB_TOP_EQSMRCA2CTRL_RCAON__W 1
+#define VSB_TOP_EQSMRCA2CTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMRCA2CTRL_RCAON__PRE 0x1
+
+#define VSB_TOP_EQSMRCA2CTRL_DFEON__B 1
+#define VSB_TOP_EQSMRCA2CTRL_DFEON__W 1
+#define VSB_TOP_EQSMRCA2CTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMRCA2CTRL_DFEON__PRE 0x2
+
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__PRE 0x0
+
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__PRE 0x0
+
+#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__PRE 0x0
+
+#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__PRE 0x0
+
+#define VSB_TOP_EQSMDDM1CTRL__A 0x1C10030
+#define VSB_TOP_EQSMDDM1CTRL__W 7
+#define VSB_TOP_EQSMDDM1CTRL__M 0x7F
+#define VSB_TOP_EQSMDDM1CTRL__PRE 0x6
+
+#define VSB_TOP_EQSMDDM1CTRL_RCAON__B 0
+#define VSB_TOP_EQSMDDM1CTRL_RCAON__W 1
+#define VSB_TOP_EQSMDDM1CTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMDDM1CTRL_RCAON__PRE 0x0
+
+#define VSB_TOP_EQSMDDM1CTRL_DFEON__B 1
+#define VSB_TOP_EQSMDDM1CTRL_DFEON__W 1
+#define VSB_TOP_EQSMDDM1CTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMDDM1CTRL_DFEON__PRE 0x2
+
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__PRE 0x4
+
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__PRE 0x0
+
+#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__PRE 0x0
+
+#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__PRE 0x0
+
+#define VSB_TOP_EQSMDDM2CTRL__A 0x1C10031
+#define VSB_TOP_EQSMDDM2CTRL__W 7
+#define VSB_TOP_EQSMDDM2CTRL__M 0x7F
+#define VSB_TOP_EQSMDDM2CTRL__PRE 0x1E
+
+#define VSB_TOP_EQSMDDM2CTRL_RCAON__B 0
+#define VSB_TOP_EQSMDDM2CTRL_RCAON__W 1
+#define VSB_TOP_EQSMDDM2CTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMDDM2CTRL_RCAON__PRE 0x0
+
+#define VSB_TOP_EQSMDDM2CTRL_DFEON__B 1
+#define VSB_TOP_EQSMDDM2CTRL_DFEON__W 1
+#define VSB_TOP_EQSMDDM2CTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMDDM2CTRL_DFEON__PRE 0x2
+
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__PRE 0x4
+
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__PRE 0x8
+
+#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__PRE 0x10
+
+#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__PRE 0x0
+
+#define VSB_TOP_SYSSMRSTCTRL__A 0x1C10032
+#define VSB_TOP_SYSSMRSTCTRL__W 11
+#define VSB_TOP_SYSSMRSTCTRL__M 0x7FF
+#define VSB_TOP_SYSSMRSTCTRL__PRE 0x7F9
+
+#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__PRE 0x1
+
+#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__PRE 0x0
+
+#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__PRE 0x0
+
+#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__PRE 0x8
+
+#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__PRE 0x10
+
+#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__PRE 0x20
+
+#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__PRE 0x40
+
+#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__PRE 0x80
+
+#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__PRE 0x100
+
+#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__PRE 0x200
+
+#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__PRE 0x400
+
+#define VSB_TOP_SYSSMCYCTRL__A 0x1C10033
+#define VSB_TOP_SYSSMCYCTRL__W 11
+#define VSB_TOP_SYSSMCYCTRL__M 0x7FF
+#define VSB_TOP_SYSSMCYCTRL__PRE 0x4E9
+
+#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__PRE 0x1
+
+#define VSB_TOP_SYSSMCYCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMCYCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMCYCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMCYCTRL_CTCALEN__PRE 0x0
+
+#define VSB_TOP_SYSSMCYCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMCYCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMCYCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMCYCTRL_STARTTRN__PRE 0x0
+
+#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__PRE 0x8
+
+#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__PRE 0x0
+
+#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__PRE 0x20
+
+#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__PRE 0x40
+
+#define VSB_TOP_SYSSMCYCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMCYCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMCYCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMCYCTRL_CKFRZ__PRE 0x80
+
+#define VSB_TOP_SYSSMCYCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMCYCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMCYCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMCYCTRL_CKBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__PRE 0x400
+
+#define VSB_TOP_SYSSMTRNCTRL__A 0x1C10034
+#define VSB_TOP_SYSSMTRNCTRL__W 11
+#define VSB_TOP_SYSSMTRNCTRL__M 0x7FF
+#define VSB_TOP_SYSSMTRNCTRL__PRE 0x204
+
+#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__PRE 0x4
+
+#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__PRE 0x200
+
+#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL__A 0x1C10035
+#define VSB_TOP_SYSSMEQCTRL__W 11
+#define VSB_TOP_SYSSMEQCTRL__M 0x7FF
+#define VSB_TOP_SYSSMEQCTRL__PRE 0x304
+
+#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMEQCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMEQCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMEQCTRL_CTCALEN__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMEQCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMEQCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMEQCTRL_STARTTRN__PRE 0x4
+
+#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMEQCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMEQCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMEQCTRL_CKFRZ__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMEQCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMEQCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMEQCTRL_CKBWSW__PRE 0x100
+
+#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__PRE 0x200
+
+#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__PRE 0x0
+
+#define VSB_TOP_SYSSMAGCCTRL__A 0x1C10036
+#define VSB_TOP_SYSSMAGCCTRL__W 11
+#define VSB_TOP_SYSSMAGCCTRL__M 0x7FF
+#define VSB_TOP_SYSSMAGCCTRL__PRE 0xF9
+
+#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__PRE 0x1
+
+#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__PRE 0x0
+
+#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__PRE 0x0
+
+#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__PRE 0x8
+
+#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__PRE 0x10
+
+#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__PRE 0x20
+
+#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__PRE 0x40
+
+#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__PRE 0x80
+
+#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL__A 0x1C10037
+#define VSB_TOP_SYSSMCTCTRL__W 11
+#define VSB_TOP_SYSSMCTCTRL__M 0x7FF
+#define VSB_TOP_SYSSMCTCTRL__PRE 0x4A
+
+#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMCTCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMCTCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMCTCTRL_CTCALEN__PRE 0x2
+
+#define VSB_TOP_SYSSMCTCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMCTCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMCTCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMCTCTRL_STARTTRN__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__PRE 0x8
+
+#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__PRE 0x40
+
+#define VSB_TOP_SYSSMCTCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMCTCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMCTCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMCTCTRL_CKFRZ__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMCTCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMCTCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMCTCTRL_CKBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__PRE 0x0
+
+#define VSB_TOP_EQCTRL__A 0x1C10038
+#define VSB_TOP_EQCTRL__W 10
+#define VSB_TOP_EQCTRL__M 0x3FF
+#define VSB_TOP_EQCTRL__PRE 0x6
+
+#define VSB_TOP_EQCTRL_STASSIGNEN__B 0
+#define VSB_TOP_EQCTRL_STASSIGNEN__W 1
+#define VSB_TOP_EQCTRL_STASSIGNEN__M 0x1
+#define VSB_TOP_EQCTRL_STASSIGNEN__PRE 0x0
+
+#define VSB_TOP_EQCTRL_ORCANCMAEN__B 1
+#define VSB_TOP_EQCTRL_ORCANCMAEN__W 1
+#define VSB_TOP_EQCTRL_ORCANCMAEN__M 0x2
+#define VSB_TOP_EQCTRL_ORCANCMAEN__PRE 0x2
+
+#define VSB_TOP_EQCTRL_ODAGCGO__B 2
+#define VSB_TOP_EQCTRL_ODAGCGO__W 1
+#define VSB_TOP_EQCTRL_ODAGCGO__M 0x4
+#define VSB_TOP_EQCTRL_ODAGCGO__PRE 0x4
+
+#define VSB_TOP_EQCTRL_OPTGAIN__B 3
+#define VSB_TOP_EQCTRL_OPTGAIN__W 3
+#define VSB_TOP_EQCTRL_OPTGAIN__M 0x38
+#define VSB_TOP_EQCTRL_OPTGAIN__PRE 0x0
+
+#define VSB_TOP_EQCTRL_TAPRAMWRTEN__B 6
+#define VSB_TOP_EQCTRL_TAPRAMWRTEN__W 1
+#define VSB_TOP_EQCTRL_TAPRAMWRTEN__M 0x40
+#define VSB_TOP_EQCTRL_TAPRAMWRTEN__PRE 0x0
+
+#define VSB_TOP_EQCTRL_CMAGAIN__B 7
+#define VSB_TOP_EQCTRL_CMAGAIN__W 3
+#define VSB_TOP_EQCTRL_CMAGAIN__M 0x380
+#define VSB_TOP_EQCTRL_CMAGAIN__PRE 0x0
+
+#define VSB_TOP_PREEQAGCCTRL__A 0x1C10039
+#define VSB_TOP_PREEQAGCCTRL__W 5
+#define VSB_TOP_PREEQAGCCTRL__M 0x1F
+#define VSB_TOP_PREEQAGCCTRL__PRE 0x10
+
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__B 0
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__W 4
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__M 0xF
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__PRE 0x0
+
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__B 4
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__W 1
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__M 0x10
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__PRE 0x10
+
+#define VSB_TOP_PREEQAGCPWRREFLVLHI__A 0x1C1003A
+#define VSB_TOP_PREEQAGCPWRREFLVLHI__W 8
+#define VSB_TOP_PREEQAGCPWRREFLVLHI__M 0xFF
+#define VSB_TOP_PREEQAGCPWRREFLVLHI__PRE 0x0
+
+#define VSB_TOP_PREEQAGCPWRREFLVLLO__A 0x1C1003B
+#define VSB_TOP_PREEQAGCPWRREFLVLLO__W 16
+#define VSB_TOP_PREEQAGCPWRREFLVLLO__M 0xFFFF
+#define VSB_TOP_PREEQAGCPWRREFLVLLO__PRE 0x1D66
+
+#define VSB_TOP_CORINGSEL__A 0x1C1003C
+#define VSB_TOP_CORINGSEL__W 8
+#define VSB_TOP_CORINGSEL__M 0xFF
+#define VSB_TOP_CORINGSEL__PRE 0x3
+#define VSB_TOP_BEDETCTRL__A 0x1C1003D
+#define VSB_TOP_BEDETCTRL__W 9
+#define VSB_TOP_BEDETCTRL__M 0x1FF
+#define VSB_TOP_BEDETCTRL__PRE 0x145
+
+#define VSB_TOP_BEDETCTRL_MIXRATIO__B 0
+#define VSB_TOP_BEDETCTRL_MIXRATIO__W 3
+#define VSB_TOP_BEDETCTRL_MIXRATIO__M 0x7
+#define VSB_TOP_BEDETCTRL_MIXRATIO__PRE 0x5
+
+#define VSB_TOP_BEDETCTRL_CYOFFSEL__B 3
+#define VSB_TOP_BEDETCTRL_CYOFFSEL__W 1
+#define VSB_TOP_BEDETCTRL_CYOFFSEL__M 0x8
+#define VSB_TOP_BEDETCTRL_CYOFFSEL__PRE 0x0
+
+#define VSB_TOP_BEDETCTRL_DATAOFFSEL__B 4
+#define VSB_TOP_BEDETCTRL_DATAOFFSEL__W 1
+#define VSB_TOP_BEDETCTRL_DATAOFFSEL__M 0x10
+#define VSB_TOP_BEDETCTRL_DATAOFFSEL__PRE 0x0
+
+#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__B 5
+#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__W 1
+#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__M 0x20
+#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__PRE 0x0
+
+#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__B 6
+#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__W 1
+#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__M 0x40
+#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__PRE 0x40
+
+#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__B 7
+#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__W 1
+#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__M 0x80
+#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__PRE 0x0
+
+#define VSB_TOP_BEDETCTRL_BYPASS_DMP__B 8
+#define VSB_TOP_BEDETCTRL_BYPASS_DMP__W 1
+#define VSB_TOP_BEDETCTRL_BYPASS_DMP__M 0x100
+#define VSB_TOP_BEDETCTRL_BYPASS_DMP__PRE 0x100
+
+#define VSB_TOP_LBAGCREFLVL__A 0x1C1003E
+#define VSB_TOP_LBAGCREFLVL__W 12
+#define VSB_TOP_LBAGCREFLVL__M 0xFFF
+#define VSB_TOP_LBAGCREFLVL__PRE 0x200
+
+#define VSB_TOP_UBAGCREFLVL__A 0x1C1003F
+#define VSB_TOP_UBAGCREFLVL__W 12
+#define VSB_TOP_UBAGCREFLVL__M 0xFFF
+#define VSB_TOP_UBAGCREFLVL__PRE 0x400
+
+#define VSB_TOP_NOTCH1_BIN_NUM__A 0x1C10040
+#define VSB_TOP_NOTCH1_BIN_NUM__W 11
+#define VSB_TOP_NOTCH1_BIN_NUM__M 0x7FF
+#define VSB_TOP_NOTCH1_BIN_NUM__PRE 0xB2
+
+#define VSB_TOP_NOTCH2_BIN_NUM__A 0x1C10041
+#define VSB_TOP_NOTCH2_BIN_NUM__W 11
+#define VSB_TOP_NOTCH2_BIN_NUM__M 0x7FF
+#define VSB_TOP_NOTCH2_BIN_NUM__PRE 0x40B
+
+#define VSB_TOP_NOTCH_START_BIN_NUM__A 0x1C10042
+#define VSB_TOP_NOTCH_START_BIN_NUM__W 11
+#define VSB_TOP_NOTCH_START_BIN_NUM__M 0x7FF
+#define VSB_TOP_NOTCH_START_BIN_NUM__PRE 0x7C0
+
+#define VSB_TOP_NOTCH_STOP_BIN_NUM__A 0x1C10043
+#define VSB_TOP_NOTCH_STOP_BIN_NUM__W 11
+#define VSB_TOP_NOTCH_STOP_BIN_NUM__M 0x7FF
+#define VSB_TOP_NOTCH_STOP_BIN_NUM__PRE 0x43F
+
+#define VSB_TOP_NOTCH_TEST_DURATION__A 0x1C10044
+#define VSB_TOP_NOTCH_TEST_DURATION__W 11
+#define VSB_TOP_NOTCH_TEST_DURATION__M 0x7FF
+#define VSB_TOP_NOTCH_TEST_DURATION__PRE 0x7FF
+
+#define VSB_TOP_RESULT_LARGE_PEAK_BIN__A 0x1C10045
+#define VSB_TOP_RESULT_LARGE_PEAK_BIN__W 11
+#define VSB_TOP_RESULT_LARGE_PEAK_BIN__M 0x7FF
+#define VSB_TOP_RESULT_LARGE_PEAK_BIN__PRE 0x0
+
+#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__A 0x1C10046
+#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__W 16
+#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__M 0xFFFF
+#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__PRE 0x0
+
+#define VSB_TOP_RESULT_SMALL_PEAK_BIN__A 0x1C10047
+#define VSB_TOP_RESULT_SMALL_PEAK_BIN__W 11
+#define VSB_TOP_RESULT_SMALL_PEAK_BIN__M 0x7FF
+#define VSB_TOP_RESULT_SMALL_PEAK_BIN__PRE 0x0
+
+#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__A 0x1C10048
+#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__W 16
+#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__M 0xFFFF
+#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__PRE 0x0
+
+#define VSB_TOP_NOTCH_SWEEP_RUNNING__A 0x1C10049
+#define VSB_TOP_NOTCH_SWEEP_RUNNING__W 1
+#define VSB_TOP_NOTCH_SWEEP_RUNNING__M 0x1
+#define VSB_TOP_NOTCH_SWEEP_RUNNING__PRE 0x0
+
+#define VSB_TOP_PREEQDAGCRATIO__A 0x1C1004A
+#define VSB_TOP_PREEQDAGCRATIO__W 13
+#define VSB_TOP_PREEQDAGCRATIO__M 0x1FFF
+#define VSB_TOP_PREEQDAGCRATIO__PRE 0x0
+#define VSB_TOP_AGC_TRUNCCTRL__A 0x1C1004B
+#define VSB_TOP_AGC_TRUNCCTRL__W 4
+#define VSB_TOP_AGC_TRUNCCTRL__M 0xF
+#define VSB_TOP_AGC_TRUNCCTRL__PRE 0xF
+
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__B 0
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__W 2
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__M 0x3
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__PRE 0x3
+
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__B 2
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__W 1
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__M 0x4
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__PRE 0x4
+
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__B 3
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__W 1
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__M 0x8
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__PRE 0x8
+
+#define VSB_TOP_BEAGC_DEADZONEINIT__A 0x1C1004C
+#define VSB_TOP_BEAGC_DEADZONEINIT__W 8
+#define VSB_TOP_BEAGC_DEADZONEINIT__M 0xFF
+#define VSB_TOP_BEAGC_DEADZONEINIT__PRE 0x50
+
+#define VSB_TOP_BEAGC_REFLEVEL__A 0x1C1004D
+#define VSB_TOP_BEAGC_REFLEVEL__W 9
+#define VSB_TOP_BEAGC_REFLEVEL__M 0x1FF
+#define VSB_TOP_BEAGC_REFLEVEL__PRE 0xAE
+
+#define VSB_TOP_BEAGC_GAINSHIFT__A 0x1C1004E
+#define VSB_TOP_BEAGC_GAINSHIFT__W 3
+#define VSB_TOP_BEAGC_GAINSHIFT__M 0x7
+#define VSB_TOP_BEAGC_GAINSHIFT__PRE 0x3
+
+#define VSB_TOP_BEAGC_REGINIT__A 0x1C1004F
+#define VSB_TOP_BEAGC_REGINIT__W 15
+#define VSB_TOP_BEAGC_REGINIT__M 0x7FFF
+#define VSB_TOP_BEAGC_REGINIT__PRE 0x40
+
+#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__B 14
+#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__W 1
+#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__M 0x4000
+#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__PRE 0x0
+
+#define VSB_TOP_BEAGC_SCALE__A 0x1C10050
+#define VSB_TOP_BEAGC_SCALE__W 14
+#define VSB_TOP_BEAGC_SCALE__M 0x3FFF
+#define VSB_TOP_BEAGC_SCALE__PRE 0x0
+
+#define VSB_TOP_CFAGC_DEADZONEINIT__A 0x1C10051
+#define VSB_TOP_CFAGC_DEADZONEINIT__W 8
+#define VSB_TOP_CFAGC_DEADZONEINIT__M 0xFF
+#define VSB_TOP_CFAGC_DEADZONEINIT__PRE 0x50
+
+#define VSB_TOP_CFAGC_REFLEVEL__A 0x1C10052
+#define VSB_TOP_CFAGC_REFLEVEL__W 9
+#define VSB_TOP_CFAGC_REFLEVEL__M 0x1FF
+#define VSB_TOP_CFAGC_REFLEVEL__PRE 0xAE
+
+#define VSB_TOP_CFAGC_GAINSHIFT__A 0x1C10053
+#define VSB_TOP_CFAGC_GAINSHIFT__W 3
+#define VSB_TOP_CFAGC_GAINSHIFT__M 0x7
+#define VSB_TOP_CFAGC_GAINSHIFT__PRE 0x3
+
+#define VSB_TOP_CFAGC_REGINIT__A 0x1C10054
+#define VSB_TOP_CFAGC_REGINIT__W 15
+#define VSB_TOP_CFAGC_REGINIT__M 0x7FFF
+#define VSB_TOP_CFAGC_REGINIT__PRE 0x80
+
+#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__B 14
+#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__W 1
+#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__M 0x4000
+#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__PRE 0x0
+
+#define VSB_TOP_CFAGC_SCALE__A 0x1C10055
+#define VSB_TOP_CFAGC_SCALE__W 14
+#define VSB_TOP_CFAGC_SCALE__M 0x3FFF
+#define VSB_TOP_CFAGC_SCALE__PRE 0x0
+
+#define VSB_TOP_CKTRKONCTL__A 0x1C10056
+#define VSB_TOP_CKTRKONCTL__W 2
+#define VSB_TOP_CKTRKONCTL__M 0x3
+#define VSB_TOP_CKTRKONCTL__PRE 0x0
+
+#define VSB_TOP_CYTRKONCTL__A 0x1C10057
+#define VSB_TOP_CYTRKONCTL__W 2
+#define VSB_TOP_CYTRKONCTL__M 0x3
+#define VSB_TOP_CYTRKONCTL__PRE 0x0
+
+#define VSB_TOP_PTONCTL__A 0x1C10058
+#define VSB_TOP_PTONCTL__W 2
+#define VSB_TOP_PTONCTL__M 0x3
+#define VSB_TOP_PTONCTL__PRE 0x0
+
+#define VSB_TOP_NOTCH_SCALE_1__A 0x1C10059
+#define VSB_TOP_NOTCH_SCALE_1__W 8
+#define VSB_TOP_NOTCH_SCALE_1__M 0xFF
+#define VSB_TOP_NOTCH_SCALE_1__PRE 0xA
+
+#define VSB_TOP_NOTCH_SCALE_2__A 0x1C1005A
+#define VSB_TOP_NOTCH_SCALE_2__W 8
+#define VSB_TOP_NOTCH_SCALE_2__M 0xFF
+#define VSB_TOP_NOTCH_SCALE_2__PRE 0xA
+
+#define VSB_TOP_FIRSTLARGFFETAP__A 0x1C1005B
+#define VSB_TOP_FIRSTLARGFFETAP__W 12
+#define VSB_TOP_FIRSTLARGFFETAP__M 0xFFF
+#define VSB_TOP_FIRSTLARGFFETAP__PRE 0x0
+
+#define VSB_TOP_FIRSTLARGFFETAPADDR__A 0x1C1005C
+#define VSB_TOP_FIRSTLARGFFETAPADDR__W 11
+#define VSB_TOP_FIRSTLARGFFETAPADDR__M 0x7FF
+#define VSB_TOP_FIRSTLARGFFETAPADDR__PRE 0x0
+
+#define VSB_TOP_SECONDLARGFFETAP__A 0x1C1005D
+#define VSB_TOP_SECONDLARGFFETAP__W 12
+#define VSB_TOP_SECONDLARGFFETAP__M 0xFFF
+#define VSB_TOP_SECONDLARGFFETAP__PRE 0x0
+
+#define VSB_TOP_SECONDLARGFFETAPADDR__A 0x1C1005E
+#define VSB_TOP_SECONDLARGFFETAPADDR__W 11
+#define VSB_TOP_SECONDLARGFFETAPADDR__M 0x7FF
+#define VSB_TOP_SECONDLARGFFETAPADDR__PRE 0x0
+
+#define VSB_TOP_FIRSTLARGDFETAP__A 0x1C1005F
+#define VSB_TOP_FIRSTLARGDFETAP__W 12
+#define VSB_TOP_FIRSTLARGDFETAP__M 0xFFF
+#define VSB_TOP_FIRSTLARGDFETAP__PRE 0x0
+
+#define VSB_TOP_FIRSTLARGDFETAPADDR__A 0x1C10060
+#define VSB_TOP_FIRSTLARGDFETAPADDR__W 11
+#define VSB_TOP_FIRSTLARGDFETAPADDR__M 0x7FF
+#define VSB_TOP_FIRSTLARGDFETAPADDR__PRE 0x0
+
+#define VSB_TOP_SECONDLARGDFETAP__A 0x1C10061
+#define VSB_TOP_SECONDLARGDFETAP__W 12
+#define VSB_TOP_SECONDLARGDFETAP__M 0xFFF
+#define VSB_TOP_SECONDLARGDFETAP__PRE 0x0
+
+#define VSB_TOP_SECONDLARGDFETAPADDR__A 0x1C10062
+#define VSB_TOP_SECONDLARGDFETAPADDR__W 11
+#define VSB_TOP_SECONDLARGDFETAPADDR__M 0x7FF
+#define VSB_TOP_SECONDLARGDFETAPADDR__PRE 0x0
+
+#define VSB_TOP_PARAOWDBUS__A 0x1C10063
+#define VSB_TOP_PARAOWDBUS__W 12
+#define VSB_TOP_PARAOWDBUS__M 0xFFF
+#define VSB_TOP_PARAOWDBUS__PRE 0x0
+#define VSB_TOP_PARAOWCTRL__A 0x1C10064
+#define VSB_TOP_PARAOWCTRL__W 7
+#define VSB_TOP_PARAOWCTRL__M 0x7F
+#define VSB_TOP_PARAOWCTRL__PRE 0x0
+
+#define VSB_TOP_PARAOWCTRL_PARAOWABUS__B 0
+#define VSB_TOP_PARAOWCTRL_PARAOWABUS__W 6
+#define VSB_TOP_PARAOWCTRL_PARAOWABUS__M 0x3F
+#define VSB_TOP_PARAOWCTRL_PARAOWABUS__PRE 0x0
+
+#define VSB_TOP_PARAOWCTRL_PARAOWEN__B 6
+#define VSB_TOP_PARAOWCTRL_PARAOWEN__W 1
+#define VSB_TOP_PARAOWCTRL_PARAOWEN__M 0x40
+#define VSB_TOP_PARAOWCTRL_PARAOWEN__PRE 0x0
+
+#define VSB_TOP_CURRENTSEGLOCAT__A 0x1C10065
+#define VSB_TOP_CURRENTSEGLOCAT__W 10
+#define VSB_TOP_CURRENTSEGLOCAT__M 0x3FF
+#define VSB_TOP_CURRENTSEGLOCAT__PRE 0x0
+
+#define VSB_TOP_MEASUREMENT_PERIOD__A 0x1C10066
+#define VSB_TOP_MEASUREMENT_PERIOD__W 16
+#define VSB_TOP_MEASUREMENT_PERIOD__M 0xFFFF
+#define VSB_TOP_MEASUREMENT_PERIOD__PRE 0x0
+
+#define VSB_TOP_NR_SYM_ERRS__A 0x1C10067
+#define VSB_TOP_NR_SYM_ERRS__W 16
+#define VSB_TOP_NR_SYM_ERRS__M 0xFFFF
+#define VSB_TOP_NR_SYM_ERRS__PRE 0xFFFF
+
+#define VSB_TOP_ERR_ENERGY_L__A 0x1C10068
+#define VSB_TOP_ERR_ENERGY_L__W 16
+#define VSB_TOP_ERR_ENERGY_L__M 0xFFFF
+#define VSB_TOP_ERR_ENERGY_L__PRE 0xFFFF
+
+#define VSB_TOP_ERR_ENERGY_H__A 0x1C10069
+#define VSB_TOP_ERR_ENERGY_H__W 16
+#define VSB_TOP_ERR_ENERGY_H__M 0xFFFF
+#define VSB_TOP_ERR_ENERGY_H__PRE 0xFFFF
+
+#define VSB_TOP_SLICER_SEL_8LEV__A 0x1C1006A
+#define VSB_TOP_SLICER_SEL_8LEV__W 1
+#define VSB_TOP_SLICER_SEL_8LEV__M 0x1
+#define VSB_TOP_SLICER_SEL_8LEV__PRE 0x1
+
+#define VSB_TOP_BNFIELD__A 0x1C1006B
+#define VSB_TOP_BNFIELD__W 3
+#define VSB_TOP_BNFIELD__M 0x7
+#define VSB_TOP_BNFIELD__PRE 0x3
+
+#define VSB_TOP_CLPLASTNUM__A 0x1C1006C
+#define VSB_TOP_CLPLASTNUM__W 8
+#define VSB_TOP_CLPLASTNUM__M 0xFF
+#define VSB_TOP_CLPLASTNUM__PRE 0x0
+
+#define VSB_TOP_BNSQERR__A 0x1C1006D
+#define VSB_TOP_BNSQERR__W 16
+#define VSB_TOP_BNSQERR__M 0xFFFF
+#define VSB_TOP_BNSQERR__PRE 0x1AD
+
+#define VSB_TOP_BNTHRESH__A 0x1C1006E
+#define VSB_TOP_BNTHRESH__W 9
+#define VSB_TOP_BNTHRESH__M 0x1FF
+#define VSB_TOP_BNTHRESH__PRE 0x120
+
+#define VSB_TOP_BNCLPNUM__A 0x1C1006F
+#define VSB_TOP_BNCLPNUM__W 16
+#define VSB_TOP_BNCLPNUM__M 0xFFFF
+#define VSB_TOP_BNCLPNUM__PRE 0x0
+#define VSB_TOP_PHASELOCKCTRL__A 0x1C10070
+#define VSB_TOP_PHASELOCKCTRL__W 7
+#define VSB_TOP_PHASELOCKCTRL__M 0x7F
+#define VSB_TOP_PHASELOCKCTRL__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__B 0
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__W 1
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__M 0x1
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__B 1
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__W 1
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__M 0x2
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__B 2
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__W 1
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__M 0x4
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__B 3
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__W 1
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__M 0x8
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__B 4
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__W 1
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__M 0x10
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__B 5
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__W 1
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__M 0x20
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__B 6
+#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__W 1
+#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__M 0x40
+#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__PRE 0x0
+
+#define VSB_TOP_DLOCKACCUM__A 0x1C10071
+#define VSB_TOP_DLOCKACCUM__W 16
+#define VSB_TOP_DLOCKACCUM__M 0xFFFF
+#define VSB_TOP_DLOCKACCUM__PRE 0x0
+
+#define VSB_TOP_PLOCKACCUM__A 0x1C10072
+#define VSB_TOP_PLOCKACCUM__W 16
+#define VSB_TOP_PLOCKACCUM__M 0xFFFF
+#define VSB_TOP_PLOCKACCUM__PRE 0x0
+
+#define VSB_TOP_CLOCKACCUM__A 0x1C10073
+#define VSB_TOP_CLOCKACCUM__W 16
+#define VSB_TOP_CLOCKACCUM__M 0xFFFF
+#define VSB_TOP_CLOCKACCUM__PRE 0x0
+
+#define VSB_TOP_DCRMVACUMI__A 0x1C10074
+#define VSB_TOP_DCRMVACUMI__W 10
+#define VSB_TOP_DCRMVACUMI__M 0x3FF
+#define VSB_TOP_DCRMVACUMI__PRE 0x0
+
+#define VSB_TOP_DCRMVACUMQ__A 0x1C10075
+#define VSB_TOP_DCRMVACUMQ__W 10
+#define VSB_TOP_DCRMVACUMQ__M 0x3FF
+#define VSB_TOP_DCRMVACUMQ__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__A 0x1C20000
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__A 0x1C20001
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__A 0x1C20002
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__A 0x1C20003
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__A 0x1C20004
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__A 0x1C20005
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__A 0x1C20006
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__A 0x1C20007
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__A 0x1C20008
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__A 0x1C20009
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__A 0x1C2000A
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__A 0x1C2000B
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__A 0x1C2000C
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__A 0x1C2000D
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__A 0x1C2000E
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__A 0x1C2000F
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__A 0x1C20010
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__A 0x1C20011
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__A 0x1C20012
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__A 0x1C20013
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__A 0x1C20014
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__A 0x1C20015
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__A 0x1C20016
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__A 0x1C20017
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__A 0x1C20018
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__A 0x1C20019
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__A 0x1C2001A
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__A 0x1C2001B
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__A 0x1C2001C
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__A 0x1C2001D
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__A 0x1C2001E
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__A 0x1C2001F
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__A 0x1C20020
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__A 0x1C20021
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__A 0x1C20022
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__A 0x1C20023
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__A 0x1C20024
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__A 0x1C20025
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__A 0x1C20026
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__A 0x1C20027
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__A 0x1C20028
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__A 0x1C20029
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__A 0x1C2002A
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__A 0x1C2002B
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__A 0x1C2002C
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__A 0x1C2002D
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__A 0x1C2002E
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__A 0x1C2002F
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__A 0x1C20030
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__A 0x1C20031
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__A 0x1C20032
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__A 0x1C20033
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__A 0x1C20034
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__A 0x1C20035
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__A 0x1C20036
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__A 0x1C20037
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__A 0x1C20038
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__A 0x1C20039
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__A 0x1C2003A
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__A 0x1C2003B
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__A 0x1C2003C
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__A 0x1C2003D
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__A 0x1C2003E
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__A 0x1C2003F
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__A 0x1C20040
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__A 0x1C20041
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__A 0x1C20042
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__A 0x1C20043
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__A 0x1C20044
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__A 0x1C20045
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__A 0x1C20046
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__A 0x1C20047
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__A 0x1C20048
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__A 0x1C20049
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__A 0x1C2004A
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__A 0x1C2004B
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__A 0x1C2004C
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__A 0x1C2004D
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__A 0x1C2004E
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__A 0x1C2004F
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__A 0x1C20050
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__A 0x1C20051
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__A 0x1C20052
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__A 0x1C20053
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__A 0x1C20054
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__A 0x1C20055
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__A 0x1C20056
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__A 0x1C20057
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__A 0x1C20058
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__A 0x1C20059
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__A 0x1C2005A
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__A 0x1C2005B
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__A 0x1C2005C
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__A 0x1C2005D
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__A 0x1C2005E
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__A 0x1C2005F
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__A 0x1C20060
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__A 0x1C20061
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__A 0x1C20062
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__A 0x1C20063
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__A 0x1C20064
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__A 0x1C20065
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__A 0x1C20066
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__A 0x1C20067
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__A 0x1C20068
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__A 0x1C20069
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__A 0x1C2006A
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__A 0x1C2006B
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__A 0x1C2006C
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__A 0x1C2006D
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__A 0x1C2006E
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__A 0x1C2006F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__A 0x1C20070
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__A 0x1C20071
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__A 0x1C20072
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__A 0x1C20073
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__A 0x1C20074
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__A 0x1C20075
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__A 0x1C20076
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__A 0x1C20077
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__PRE 0x0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__A 0x1C20078
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__A 0x1C20079
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__A 0x1C2007A
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__A 0x1C2007B
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__A 0x1C2007C
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__A 0x1C2007D
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__A 0x1C2007E
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__A 0x1C2007F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__A 0x1C30000
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__A 0x1C30001
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__A 0x1C30002
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__A 0x1C30003
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__A 0x1C30004
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__A 0x1C30005
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__A 0x1C30006
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__A 0x1C30007
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__A 0x1C30008
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__A 0x1C30009
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__A 0x1C3000A
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__A 0x1C3000B
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__A 0x1C3000C
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__A 0x1C3000D
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__A 0x1C3000E
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__A 0x1C3000F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__A 0x1C30010
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__A 0x1C30011
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__A 0x1C30012
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__A 0x1C30013
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__A 0x1C30014
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__A 0x1C30015
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__A 0x1C30016
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__A 0x1C30017
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__A 0x1C30018
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__A 0x1C30019
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__A 0x1C3001A
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__A 0x1C3001B
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__A 0x1C3001C
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__A 0x1C3001D
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__A 0x1C3001E
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__A 0x1C3001F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__A 0x1C30020
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__A 0x1C30021
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__A 0x1C30022
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__A 0x1C30023
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__A 0x1C30024
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__A 0x1C30025
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__A 0x1C30026
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__A 0x1C30027
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__A 0x1C30028
+#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__A 0x1C30029
+#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__A 0x1C3002A
+#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__A 0x1C3002B
+#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__A 0x1C3002C
+#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__A 0x1C3002D
+#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__A 0x1C3002E
+#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__A 0x1C3002F
+#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__A 0x1C30030
+#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__A 0x1C30031
+#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__M 0x7F
+#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__PRE 0x0
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__A 0x1C30032
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__W 15
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__B 0
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__M 0x7F
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__B 8
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__M 0x7F00
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__A 0x1C30033
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__W 15
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__B 0
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__M 0x7F
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__B 8
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__M 0x7F00
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__A 0x1C30034
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__W 15
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__B 0
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__M 0x7F
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__B 8
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__M 0x7F00
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__A 0x1C30035
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__W 15
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__B 0
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__M 0x7F
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__B 8
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__M 0x7F00
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__PRE 0x0
+
+#define VSB_TCMEQ_RAM__A 0x1C40000
+
+#define VSB_TCMEQ_RAM_TCMEQ_RAM__B 0
+#define VSB_TCMEQ_RAM_TCMEQ_RAM__W 16
+#define VSB_TCMEQ_RAM_TCMEQ_RAM__M 0xFFFF
+#define VSB_TCMEQ_RAM_TCMEQ_RAM__PRE 0x0
+
+#define VSB_FCPRE_RAM__A 0x1C50000
+
+#define VSB_FCPRE_RAM_FCPRE_RAM__B 0
+#define VSB_FCPRE_RAM_FCPRE_RAM__W 16
+#define VSB_FCPRE_RAM_FCPRE_RAM__M 0xFFFF
+#define VSB_FCPRE_RAM_FCPRE_RAM__PRE 0x0
+
+#define VSB_EQTAP_RAM__A 0x1C60000
+
+#define VSB_EQTAP_RAM_EQTAP_RAM__B 0
+#define VSB_EQTAP_RAM_EQTAP_RAM__W 12
+#define VSB_EQTAP_RAM_EQTAP_RAM__M 0xFFF
+#define VSB_EQTAP_RAM_EQTAP_RAM__PRE 0x0
+
+#endif
diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c
index 959ae36403b8..5b87ece69414 100644
--- a/drivers/media/dvb-frontends/drxd_hard.c
+++ b/drivers/media/dvb-frontends/drxd_hard.c
@@ -2688,11 +2688,11 @@ static int DRXD_init(struct drxd_state *state, const u8 *fw, u32 fw_size)
status = EnableAndResetMB(state);
if (status < 0)
break;
- if (state->type_A)
+ if (state->type_A) {
status = ResetCEFR(state);
if (status < 0)
break;
-
+ }
if (fw) {
status = DownloadMicrocode(state, fw, fw_size);
if (status < 0)
diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c
index 1e344b033277..335daeff91b9 100644
--- a/drivers/media/dvb-frontends/ds3000.c
+++ b/drivers/media/dvb-frontends/ds3000.c
@@ -616,7 +616,7 @@ static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr)
snr_reading = dvbs2_noise_reading / tmp;
if (snr_reading > 80)
snr_reading = 80;
- *snr = -(dvbs2_snr_tab[snr_reading] / 1000);
+ *snr = -(dvbs2_snr_tab[snr_reading - 1] / 1000);
}
dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
snr_reading, *snr);
diff --git a/drivers/media/dvb-frontends/it913x-fe-priv.h b/drivers/media/dvb-frontends/it913x-fe-priv.h
deleted file mode 100644
index eb6fd8aebdb3..000000000000
--- a/drivers/media/dvb-frontends/it913x-fe-priv.h
+++ /dev/null
@@ -1,1051 +0,0 @@
-
-struct it913xset { u32 pro;
- u32 address;
- u8 reg[15];
- u8 count;
-};
-
-struct adctable { u32 adcFrequency;
- u32 bandwidth;
- u32 coeff_1_2048;
- u32 coeff_1_4096;
- u32 coeff_1_8191;
- u32 coeff_1_8192;
- u32 coeff_1_8193;
- u32 coeff_2_2k;
- u32 coeff_2_4k;
- u32 coeff_2_8k;
- u16 bfsfcw_fftinx_ratio;
- u16 fftinx_bfsfcw_ratio;
-};
-
-/* clock and coeff tables only table 3 is used with IT9137*/
-/* TODO other tables relate AF9035 may be removed */
-static struct adctable tab1[] = {
- { 20156250, 6000000,
- 0x02b8ba6e, 0x015c5d37, 0x00ae340d, 0x00ae2e9b, 0x00ae292a,
- 0x015c5d37, 0x00ae2e9b, 0x0057174e, 0x02f1, 0x015c },
- { 20156250, 7000000,
- 0x032cd980, 0x01966cc0, 0x00cb3cba, 0x00cb3660, 0x00cb3007,
- 0x01966cc0, 0x00cb3660, 0x00659b30, 0x0285, 0x0196 },
- { 20156250, 8000000,
- 0x03a0f893, 0x01d07c49, 0x00e84567, 0x00e83e25, 0x00e836e3,
- 0x01d07c49, 0x00e83e25, 0x00741f12, 0x0234, 0x01d0 },
- { 20156250, 5000000,
- 0x02449b5c, 0x01224dae, 0x00912b60, 0x009126d7, 0x0091224e,
- 0x01224dae, 0x009126d7, 0x0048936b, 0x0387, 0x0122 }
-};
-
-static struct adctable tab2[] = {
- { 20187500, 6000000,
- 0x02b7a654, 0x015bd32a, 0x00adef04, 0x00ade995, 0x00ade426,
- 0x015bd32a, 0x00ade995, 0x0056f4ca, 0x02f2, 0x015c },
- { 20187500, 7000000,
- 0x032b9761, 0x0195cbb1, 0x00caec30, 0x00cae5d8, 0x00cadf81,
- 0x0195cbb1, 0x00cae5d8, 0x006572ec, 0x0286, 0x0196 },
- { 20187500, 8000000,
- 0x039f886f, 0x01cfc438, 0x00e7e95b, 0x00e7e21c, 0x00e7dadd,
- 0x01cfc438, 0x00e7e21c, 0x0073f10e, 0x0235, 0x01d0 },
- { 20187500, 5000000,
- 0x0243b546, 0x0121daa3, 0x0090f1d9, 0x0090ed51, 0x0090e8ca,
- 0x0121daa3, 0x0090ed51, 0x004876a9, 0x0388, 0x0122 }
-
-};
-
-static struct adctable tab3[] = {
- { 20250000, 6000000,
- 0x02b580ad, 0x015ac057, 0x00ad6597, 0x00ad602b, 0x00ad5ac1,
- 0x015ac057, 0x00ad602b, 0x0056b016, 0x02f4, 0x015b },
- { 20250000, 7000000,
- 0x03291620, 0x01948b10, 0x00ca4bda, 0x00ca4588, 0x00ca3f36,
- 0x01948b10, 0x00ca4588, 0x006522c4, 0x0288, 0x0195 },
- { 20250000, 8000000,
- 0x039cab92, 0x01ce55c9, 0x00e7321e, 0x00e72ae4, 0x00e723ab,
- 0x01ce55c9, 0x00e72ae4, 0x00739572, 0x0237, 0x01ce },
- { 20250000, 5000000,
- 0x0241eb3b, 0x0120f59e, 0x00907f53, 0x00907acf, 0x0090764b,
- 0x0120f59e, 0x00907acf, 0x00483d67, 0x038b, 0x0121 }
-
-};
-
-static struct adctable tab4[] = {
- { 20583333, 6000000,
- 0x02aa4598, 0x015522cc, 0x00aa96bb, 0x00aa9166, 0x00aa8c12,
- 0x015522cc, 0x00aa9166, 0x005548b3, 0x0300, 0x0155 },
- { 20583333, 7000000,
- 0x031bfbdc, 0x018dfdee, 0x00c7052f, 0x00c6fef7, 0x00c6f8bf,
- 0x018dfdee, 0x00c6fef7, 0x00637f7b, 0x0293, 0x018e },
- { 20583333, 8000000,
- 0x038db21f, 0x01c6d910, 0x00e373a3, 0x00e36c88, 0x00e3656d,
- 0x01c6d910, 0x00e36c88, 0x0071b644, 0x0240, 0x01c7 },
- { 20583333, 5000000,
- 0x02388f54, 0x011c47aa, 0x008e2846, 0x008e23d5, 0x008e1f64,
- 0x011c47aa, 0x008e23d5, 0x004711ea, 0x039a, 0x011c }
-
-};
-
-static struct adctable tab5[] = {
- { 20416667, 6000000,
- 0x02afd765, 0x0157ebb3, 0x00abfb39, 0x00abf5d9, 0x00abf07a,
- 0x0157ebb3, 0x00abf5d9, 0x0055faed, 0x02fa, 0x0158 },
- { 20416667, 7000000,
- 0x03227b4b, 0x01913da6, 0x00c8a518, 0x00c89ed3, 0x00c8988e,
- 0x01913da6, 0x00c89ed3, 0x00644f69, 0x028d, 0x0191 },
- { 20416667, 8000000,
- 0x03951f32, 0x01ca8f99, 0x00e54ef7, 0x00e547cc, 0x00e540a2,
- 0x01ca8f99, 0x00e547cc, 0x0072a3e6, 0x023c, 0x01cb },
- { 20416667, 5000000,
- 0x023d337f, 0x011e99c0, 0x008f515a, 0x008f4ce0, 0x008f4865,
- 0x011e99c0, 0x008f4ce0, 0x0047a670, 0x0393, 0x011f }
-
-};
-
-static struct adctable tab6[] = {
- { 20480000, 6000000,
- 0x02adb6db, 0x0156db6e, 0x00ab7312, 0x00ab6db7, 0x00ab685c,
- 0x0156db6e, 0x00ab6db7, 0x0055b6db, 0x02fd, 0x0157 },
- { 20480000, 7000000,
- 0x03200000, 0x01900000, 0x00c80640, 0x00c80000, 0x00c7f9c0,
- 0x01900000, 0x00c80000, 0x00640000, 0x028f, 0x0190 },
- { 20480000, 8000000,
- 0x03924925, 0x01c92492, 0x00e4996e, 0x00e49249, 0x00e48b25,
- 0x01c92492, 0x00e49249, 0x00724925, 0x023d, 0x01c9 },
- { 20480000, 5000000,
- 0x023b6db7, 0x011db6db, 0x008edfe5, 0x008edb6e, 0x008ed6f7,
- 0x011db6db, 0x008edb6e, 0x00476db7, 0x0396, 0x011e }
-};
-
-static struct adctable tab7[] = {
- { 20500000, 6000000,
- 0x02ad0b99, 0x015685cc, 0x00ab4840, 0x00ab42e6, 0x00ab3d8c,
- 0x015685cc, 0x00ab42e6, 0x0055a173, 0x02fd, 0x0157 },
- { 20500000, 7000000,
- 0x031f3832, 0x018f9c19, 0x00c7d44b, 0x00c7ce0c, 0x00c7c7ce,
- 0x018f9c19, 0x00c7ce0c, 0x0063e706, 0x0290, 0x0190 },
- { 20500000, 8000000,
- 0x039164cb, 0x01c8b266, 0x00e46056, 0x00e45933, 0x00e45210,
- 0x01c8b266, 0x00e45933, 0x00722c99, 0x023e, 0x01c9 },
- { 20500000, 5000000,
- 0x023adeff, 0x011d6f80, 0x008ebc36, 0x008eb7c0, 0x008eb34a,
- 0x011d6f80, 0x008eb7c0, 0x00475be0, 0x0396, 0x011d }
-
-};
-
-static struct adctable tab8[] = {
- { 20625000, 6000000,
- 0x02a8e4bd, 0x0154725e, 0x00aa3e81, 0x00aa392f, 0x00aa33de,
- 0x0154725e, 0x00aa392f, 0x00551c98, 0x0302, 0x0154 },
- { 20625000, 7000000,
- 0x031a6032, 0x018d3019, 0x00c69e41, 0x00c6980c, 0x00c691d8,
- 0x018d3019, 0x00c6980c, 0x00634c06, 0x0294, 0x018d },
- { 20625000, 8000000,
- 0x038bdba6, 0x01c5edd3, 0x00e2fe02, 0x00e2f6ea, 0x00e2efd2,
- 0x01c5edd3, 0x00e2f6ea, 0x00717b75, 0x0242, 0x01c6 },
- { 20625000, 5000000,
- 0x02376948, 0x011bb4a4, 0x008ddec1, 0x008dda52, 0x008dd5e3,
- 0x011bb4a4, 0x008dda52, 0x0046ed29, 0x039c, 0x011c }
-
-};
-
-struct table {
- u32 xtal;
- struct adctable *table;
-};
-
-static struct table fe_clockTable[] = {
- {12000000, tab3}, /* 12.00MHz */
- {20480000, tab6}, /* 20.48MHz */
- {36000000, tab3}, /* 36.00MHz */
- {30000000, tab1}, /* 30.00MHz */
- {26000000, tab4}, /* 26.00MHz */
- {28000000, tab5}, /* 28.00MHz */
- {32000000, tab7}, /* 32.00MHz */
- {34000000, tab2}, /* 34.00MHz */
- {24000000, tab1}, /* 24.00MHz */
- {22000000, tab8}, /* 22.00MHz */
-};
-
-/* fe get */
-fe_code_rate_t fe_code[] = {
- FEC_1_2,
- FEC_2_3,
- FEC_3_4,
- FEC_5_6,
- FEC_7_8,
- FEC_NONE,
-};
-
-fe_guard_interval_t fe_gi[] = {
- GUARD_INTERVAL_1_32,
- GUARD_INTERVAL_1_16,
- GUARD_INTERVAL_1_8,
- GUARD_INTERVAL_1_4,
-};
-
-fe_hierarchy_t fe_hi[] = {
- HIERARCHY_NONE,
- HIERARCHY_1,
- HIERARCHY_2,
- HIERARCHY_4,
-};
-
-fe_transmit_mode_t fe_mode[] = {
- TRANSMISSION_MODE_2K,
- TRANSMISSION_MODE_8K,
- TRANSMISSION_MODE_4K,
-};
-
-fe_modulation_t fe_con[] = {
- QPSK,
- QAM_16,
- QAM_64,
-};
-
-enum {
- PRIORITY_HIGH = 0, /* High-priority stream */
- PRIORITY_LOW, /* Low-priority stream */
-};
-
-/* Standard demodulator functions */
-static struct it913xset set_solo_fe[] = {
- {PRO_LINK, GPIOH5_EN, {0x01}, 0x01},
- {PRO_LINK, GPIOH5_ON, {0x01}, 0x01},
- {PRO_LINK, GPIOH5_O, {0x00}, 0x01},
- {PRO_LINK, GPIOH5_O, {0x01}, 0x01},
- {PRO_LINK, DVBT_INTEN, {0x04}, 0x01},
- {PRO_LINK, DVBT_ENABLE, {0x05}, 0x01},
- {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01},
- {PRO_LINK, HOSTB_MPEG_SER_MODE, {0x00}, 0x01},
- {PRO_LINK, HOSTB_MPEG_PAR_MODE, {0x00}, 0x01},
- {PRO_DMOD, DCA_UPPER_CHIP, {0x00}, 0x01},
- {PRO_LINK, HOSTB_DCA_UPPER, {0x00}, 0x01},
- {PRO_DMOD, DCA_LOWER_CHIP, {0x00}, 0x01},
- {PRO_LINK, HOSTB_DCA_LOWER, {0x00}, 0x01},
- {PRO_DMOD, DCA_PLATCH, {0x00}, 0x01},
- {PRO_DMOD, DCA_FPGA_LATCH, {0x00}, 0x01},
- {PRO_DMOD, DCA_STAND_ALONE, {0x01}, 0x01},
- {PRO_DMOD, DCA_ENABLE, {0x00}, 0x01},
- {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01},
- {PRO_DMOD, BFS_FCW, {0x00, 0x00, 0x00}, 0x03},
- {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
-};
-
-
-static struct it913xset init_1[] = {
- {PRO_LINK, LOCK3_OUT, {0x01}, 0x01},
- {PRO_LINK, PADMISCDRSR, {0x01}, 0x01},
- {PRO_LINK, PADMISCDR2, {0x00}, 0x01},
- {PRO_DMOD, 0xec57, {0x00, 0x00}, 0x02},
- {PRO_LINK, PADMISCDR4, {0x00}, 0x01}, /* Power up */
- {PRO_LINK, PADMISCDR8, {0x00}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */
-};
-
-
-/* Version 1 types */
-static struct it913xset it9135_v1[] = {
- {PRO_DMOD, 0x0051, {0x01}, 0x01},
- {PRO_DMOD, 0x0070, {0x0a}, 0x01},
- {PRO_DMOD, 0x007e, {0x04}, 0x01},
- {PRO_DMOD, 0x0081, {0x0a}, 0x01},
- {PRO_DMOD, 0x008a, {0x01}, 0x01},
- {PRO_DMOD, 0x008e, {0x01}, 0x01},
- {PRO_DMOD, 0x0092, {0x06}, 0x01},
- {PRO_DMOD, 0x0099, {0x01}, 0x01},
- {PRO_DMOD, 0x009f, {0xe1}, 0x01},
- {PRO_DMOD, 0x00a0, {0xcf}, 0x01},
- {PRO_DMOD, 0x00a3, {0x01}, 0x01},
- {PRO_DMOD, 0x00a5, {0x01}, 0x01},
- {PRO_DMOD, 0x00a6, {0x01}, 0x01},
- {PRO_DMOD, 0x00a9, {0x00}, 0x01},
- {PRO_DMOD, 0x00aa, {0x01}, 0x01},
- {PRO_DMOD, 0x00b0, {0x01}, 0x01},
- {PRO_DMOD, 0x00c2, {0x05}, 0x01},
- {PRO_DMOD, 0x00c6, {0x19}, 0x01},
- {PRO_DMOD, 0xf000, {0x0f}, 0x01},
- {PRO_DMOD, 0xf016, {0x10}, 0x01},
- {PRO_DMOD, 0xf017, {0x04}, 0x01},
- {PRO_DMOD, 0xf018, {0x05}, 0x01},
- {PRO_DMOD, 0xf019, {0x04}, 0x01},
- {PRO_DMOD, 0xf01a, {0x05}, 0x01},
- {PRO_DMOD, 0xf021, {0x03}, 0x01},
- {PRO_DMOD, 0xf022, {0x0a}, 0x01},
- {PRO_DMOD, 0xf023, {0x0a}, 0x01},
- {PRO_DMOD, 0xf02b, {0x00}, 0x01},
- {PRO_DMOD, 0xf02c, {0x01}, 0x01},
- {PRO_DMOD, 0xf064, {0x03}, 0x01},
- {PRO_DMOD, 0xf065, {0xf9}, 0x01},
- {PRO_DMOD, 0xf066, {0x03}, 0x01},
- {PRO_DMOD, 0xf067, {0x01}, 0x01},
- {PRO_DMOD, 0xf06f, {0xe0}, 0x01},
- {PRO_DMOD, 0xf070, {0x03}, 0x01},
- {PRO_DMOD, 0xf072, {0x0f}, 0x01},
- {PRO_DMOD, 0xf073, {0x03}, 0x01},
- {PRO_DMOD, 0xf078, {0x00}, 0x01},
- {PRO_DMOD, 0xf087, {0x00}, 0x01},
- {PRO_DMOD, 0xf09b, {0x3f}, 0x01},
- {PRO_DMOD, 0xf09c, {0x00}, 0x01},
- {PRO_DMOD, 0xf09d, {0x20}, 0x01},
- {PRO_DMOD, 0xf09e, {0x00}, 0x01},
- {PRO_DMOD, 0xf09f, {0x0c}, 0x01},
- {PRO_DMOD, 0xf0a0, {0x00}, 0x01},
- {PRO_DMOD, 0xf130, {0x04}, 0x01},
- {PRO_DMOD, 0xf132, {0x04}, 0x01},
- {PRO_DMOD, 0xf144, {0x1a}, 0x01},
- {PRO_DMOD, 0xf146, {0x00}, 0x01},
- {PRO_DMOD, 0xf14a, {0x01}, 0x01},
- {PRO_DMOD, 0xf14c, {0x00}, 0x01},
- {PRO_DMOD, 0xf14d, {0x00}, 0x01},
- {PRO_DMOD, 0xf14f, {0x04}, 0x01},
- {PRO_DMOD, 0xf158, {0x7f}, 0x01},
- {PRO_DMOD, 0xf15a, {0x00}, 0x01},
- {PRO_DMOD, 0xf15b, {0x08}, 0x01},
- {PRO_DMOD, 0xf15d, {0x03}, 0x01},
- {PRO_DMOD, 0xf15e, {0x05}, 0x01},
- {PRO_DMOD, 0xf163, {0x05}, 0x01},
- {PRO_DMOD, 0xf166, {0x01}, 0x01},
- {PRO_DMOD, 0xf167, {0x40}, 0x01},
- {PRO_DMOD, 0xf168, {0x0f}, 0x01},
- {PRO_DMOD, 0xf17a, {0x00}, 0x01},
- {PRO_DMOD, 0xf17b, {0x00}, 0x01},
- {PRO_DMOD, 0xf183, {0x01}, 0x01},
- {PRO_DMOD, 0xf19d, {0x40}, 0x01},
- {PRO_DMOD, 0xf1bc, {0x36}, 0x01},
- {PRO_DMOD, 0xf1bd, {0x00}, 0x01},
- {PRO_DMOD, 0xf1cb, {0xa0}, 0x01},
- {PRO_DMOD, 0xf1cc, {0x01}, 0x01},
- {PRO_DMOD, 0xf204, {0x10}, 0x01},
- {PRO_DMOD, 0xf214, {0x00}, 0x01},
- {PRO_DMOD, 0xf40e, {0x0a}, 0x01},
- {PRO_DMOD, 0xf40f, {0x40}, 0x01},
- {PRO_DMOD, 0xf410, {0x08}, 0x01},
- {PRO_DMOD, 0xf55f, {0x0a}, 0x01},
- {PRO_DMOD, 0xf561, {0x15}, 0x01},
- {PRO_DMOD, 0xf562, {0x20}, 0x01},
- {PRO_DMOD, 0xf5df, {0xfb}, 0x01},
- {PRO_DMOD, 0xf5e0, {0x00}, 0x01},
- {PRO_DMOD, 0xf5e3, {0x09}, 0x01},
- {PRO_DMOD, 0xf5e4, {0x01}, 0x01},
- {PRO_DMOD, 0xf5e5, {0x01}, 0x01},
- {PRO_DMOD, 0xf5f8, {0x01}, 0x01},
- {PRO_DMOD, 0xf5fd, {0x01}, 0x01},
- {PRO_DMOD, 0xf600, {0x05}, 0x01},
- {PRO_DMOD, 0xf601, {0x08}, 0x01},
- {PRO_DMOD, 0xf602, {0x0b}, 0x01},
- {PRO_DMOD, 0xf603, {0x0e}, 0x01},
- {PRO_DMOD, 0xf604, {0x11}, 0x01},
- {PRO_DMOD, 0xf605, {0x14}, 0x01},
- {PRO_DMOD, 0xf606, {0x17}, 0x01},
- {PRO_DMOD, 0xf607, {0x1f}, 0x01},
- {PRO_DMOD, 0xf60e, {0x00}, 0x01},
- {PRO_DMOD, 0xf60f, {0x04}, 0x01},
- {PRO_DMOD, 0xf610, {0x32}, 0x01},
- {PRO_DMOD, 0xf611, {0x10}, 0x01},
- {PRO_DMOD, 0xf707, {0xfc}, 0x01},
- {PRO_DMOD, 0xf708, {0x00}, 0x01},
- {PRO_DMOD, 0xf709, {0x37}, 0x01},
- {PRO_DMOD, 0xf70a, {0x00}, 0x01},
- {PRO_DMOD, 0xf78b, {0x01}, 0x01},
- {PRO_DMOD, 0xf80f, {0x40}, 0x01},
- {PRO_DMOD, 0xf810, {0x54}, 0x01},
- {PRO_DMOD, 0xf811, {0x5a}, 0x01},
- {PRO_DMOD, 0xf905, {0x01}, 0x01},
- {PRO_DMOD, 0xfb06, {0x03}, 0x01},
- {PRO_DMOD, 0xfd8b, {0x00}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */
-};
-
-static struct it913xset it9135_38[] = {
- {PRO_DMOD, 0x0043, {0x00}, 0x01},
- {PRO_DMOD, 0x0046, {0x38}, 0x01},
- {PRO_DMOD, 0x0051, {0x01}, 0x01},
- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0x0068, {0x0a}, 0x01},
- {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03},
- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xc8, 0x01}, 0x05},
- {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02},
- {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x02, 0x0a, 0x03, 0xc8, 0xb8,
- 0xd0, 0xc3, 0x01}, 0x0a},
- {PRO_DMOD, 0x008e, {0x01}, 0x01},
- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05},
- {PRO_DMOD, 0x0099, {0x01}, 0x01},
- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02},
- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02},
- {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04},
- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02},
- {PRO_DMOD, 0x00b0, {0x01}, 0x01},
- {PRO_DMOD, 0x00b3, {0x02, 0x32}, 0x02},
- {PRO_DMOD, 0x00b6, {0x14}, 0x01},
- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03},
- {PRO_DMOD, 0x00c4, {0x00}, 0x01},
- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02},
- {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03},
- {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03},
- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03},
- {PRO_DMOD, 0x00fc, { 0x02, 0x02, 0x02, 0x09, 0x50, 0x7b, 0x77,
- 0x00, 0x02, 0xc8, 0x05, 0x7b}, 0x0c},
- {PRO_DMOD, 0x0109, {0x02}, 0x01},
- {PRO_DMOD, 0x0115, {0x0a, 0x03, 0x02, 0x80}, 0x04},
- {PRO_DMOD, 0x011a, {0xc8, 0x7b, 0x8a, 0xa0}, 0x04},
- {PRO_DMOD, 0x0122, {0x02, 0x18, 0xc3}, 0x03},
- {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02},
- {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04},
- {PRO_DMOD, 0x0137, {0x01, 0x00, 0x07, 0x00, 0x06}, 0x05},
- {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xc8, 0x59}, 0x05},
- {PRO_DMOD, 0xf000, {0x0f}, 0x01},
- {PRO_DMOD, 0xf016, {0x10, 0x04, 0x05, 0x04, 0x05}, 0x05},
- {PRO_DMOD, 0xf01f, {0x8c, 0x00, 0x03, 0x0a, 0x0a}, 0x05},
- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00, 0x01}, 0x04},
- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04},
- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02},
- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02},
- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02},
- {PRO_DMOD, 0xf085, {0x00, 0x02, 0x00}, 0x03},
- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06},
- {PRO_DMOD, 0xf130, {0x04}, 0x01},
- {PRO_DMOD, 0xf132, {0x04}, 0x01},
- {PRO_DMOD, 0xf144, {0x1a}, 0x01},
- {PRO_DMOD, 0xf146, {0x00}, 0x01},
- {PRO_DMOD, 0xf14a, {0x01}, 0x01},
- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf14f, {0x04}, 0x01},
- {PRO_DMOD, 0xf158, {0x7f}, 0x01},
- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02},
- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02},
- {PRO_DMOD, 0xf163, {0x05}, 0x01},
- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03},
- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf183, {0x01}, 0x01},
- {PRO_DMOD, 0xf19d, {0x40}, 0x01},
- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02},
- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02},
- {PRO_DMOD, 0xf204, {0x10}, 0x01},
- {PRO_DMOD, 0xf214, {0x00}, 0x01},
- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04},
- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05},
- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04},
- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03},
- {PRO_DMOD, 0xf55f, {0x0a}, 0x01},
- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02},
- {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02},
- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03},
- {PRO_DMOD, 0xf5f8, {0x01}, 0x01},
- {PRO_DMOD, 0xf5fd, {0x01}, 0x01},
- {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17,
- 0x1f}, 0x08},
- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04},
- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04},
- {PRO_DMOD, 0xf78b, {0x01}, 0x01},
- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03},
- {PRO_DMOD, 0xf905, {0x01}, 0x01},
- {PRO_DMOD, 0xfb06, {0x03}, 0x01},
- {PRO_DMOD, 0xfd8b, {0x00}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */
-};
-
-static struct it913xset it9135_51[] = {
- {PRO_DMOD, 0x0043, {0x00}, 0x01},
- {PRO_DMOD, 0x0046, {0x51}, 0x01},
- {PRO_DMOD, 0x0051, {0x01}, 0x01},
- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0x0068, {0x0a}, 0x01},
- {PRO_DMOD, 0x0070, {0x0a, 0x06, 0x02}, 0x03},
- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xc8, 0x01}, 0x05},
- {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02},
- {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x02, 0x0a, 0x03, 0xc0, 0x96,
- 0xcf, 0xc3, 0x01}, 0x0a},
- {PRO_DMOD, 0x008e, {0x01}, 0x01},
- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05},
- {PRO_DMOD, 0x0099, {0x01}, 0x01},
- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02},
- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02},
- {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04},
- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02},
- {PRO_DMOD, 0x00b0, {0x01}, 0x01},
- {PRO_DMOD, 0x00b3, {0x02, 0x3c}, 0x02},
- {PRO_DMOD, 0x00b6, {0x14}, 0x01},
- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03},
- {PRO_DMOD, 0x00c4, {0x00}, 0x01},
- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02},
- {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03},
- {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03},
- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03},
- {PRO_DMOD, 0x00fc, { 0x03, 0x02, 0x02, 0x09, 0x50, 0x7a, 0x77,
- 0x01, 0x02, 0xb0, 0x02, 0x7a}, 0x0c},
- {PRO_DMOD, 0x0109, {0x02}, 0x01},
- {PRO_DMOD, 0x0115, {0x0a, 0x03, 0x02, 0x80}, 0x04},
- {PRO_DMOD, 0x011a, {0xc0, 0x7a, 0xac, 0x8c}, 0x04},
- {PRO_DMOD, 0x0122, {0x02, 0x70, 0xa4}, 0x03},
- {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02},
- {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04},
- {PRO_DMOD, 0x0137, {0x01, 0x00, 0x07, 0x00, 0x06}, 0x05},
- {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xc0, 0x59}, 0x05},
- {PRO_DMOD, 0xf000, {0x0f}, 0x01},
- {PRO_DMOD, 0xf016, {0x10, 0x04, 0x05, 0x04, 0x05}, 0x05},
- {PRO_DMOD, 0xf01f, {0x8c, 0x00, 0x03, 0x0a, 0x0a}, 0x05},
- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00, 0x01}, 0x04},
- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04},
- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02},
- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02},
- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02},
- {PRO_DMOD, 0xf085, {0xc0, 0x01, 0x00}, 0x03},
- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06},
- {PRO_DMOD, 0xf130, {0x04}, 0x01},
- {PRO_DMOD, 0xf132, {0x04}, 0x01},
- {PRO_DMOD, 0xf144, {0x1a}, 0x01},
- {PRO_DMOD, 0xf146, {0x00}, 0x01},
- {PRO_DMOD, 0xf14a, {0x01}, 0x01},
- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf14f, {0x04}, 0x01},
- {PRO_DMOD, 0xf158, {0x7f}, 0x01},
- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02},
- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02},
- {PRO_DMOD, 0xf163, {0x05}, 0x01},
- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03},
- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf183, {0x01}, 0x01},
- {PRO_DMOD, 0xf19d, {0x40}, 0x01},
- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02},
- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02},
- {PRO_DMOD, 0xf204, {0x10}, 0x01},
- {PRO_DMOD, 0xf214, {0x00}, 0x01},
- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04},
- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05},
- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04},
- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03},
- {PRO_DMOD, 0xf55f, {0x0a}, 0x01},
- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02},
- {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02},
- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03},
- {PRO_DMOD, 0xf5f8, {0x01}, 0x01},
- {PRO_DMOD, 0xf5fd, {0x01}, 0x01},
- {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17,
- 0x1f}, 0x08},
- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04},
- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04},
- {PRO_DMOD, 0xf78b, {0x01}, 0x01},
- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03},
- {PRO_DMOD, 0xf905, {0x01}, 0x01},
- {PRO_DMOD, 0xfb06, {0x03}, 0x01},
- {PRO_DMOD, 0xfd8b, {0x00}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */
-};
-
-static struct it913xset it9135_52[] = {
- {PRO_DMOD, 0x0043, {0x00}, 0x01},
- {PRO_DMOD, 0x0046, {0x52}, 0x01},
- {PRO_DMOD, 0x0051, {0x01}, 0x01},
- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0x0068, {0x10}, 0x01},
- {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03},
- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xa0, 0x01}, 0x05},
- {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02},
- {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x03, 0x0a, 0x03, 0xb3, 0x97,
- 0xc0, 0x9e, 0x01}, 0x0a},
- {PRO_DMOD, 0x008e, {0x01}, 0x01},
- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05},
- {PRO_DMOD, 0x0099, {0x01}, 0x01},
- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02},
- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02},
- {PRO_DMOD, 0x00a3, {0x01, 0x5c, 0x01, 0x01}, 0x04},
- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02},
- {PRO_DMOD, 0x00b0, {0x01}, 0x01},
- {PRO_DMOD, 0x00b3, {0x02, 0x3c}, 0x02},
- {PRO_DMOD, 0x00b6, {0x14}, 0x01},
- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03},
- {PRO_DMOD, 0x00c4, {0x00}, 0x01},
- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02},
- {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03},
- {PRO_DMOD, 0x00f3, {0x05, 0x91, 0x8c}, 0x03},
- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03},
- {PRO_DMOD, 0x00fc, { 0x03, 0x02, 0x02, 0x09, 0x50, 0x74, 0x77,
- 0x02, 0x02, 0xae, 0x02, 0x6e}, 0x0c},
- {PRO_DMOD, 0x0109, {0x02}, 0x01},
- {PRO_DMOD, 0x0115, {0x0a, 0x03, 0x02, 0x80}, 0x04},
- {PRO_DMOD, 0x011a, {0xcd, 0x62, 0xa4, 0x8c}, 0x04},
- {PRO_DMOD, 0x0122, {0x03, 0x18, 0x9e}, 0x03},
- {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02},
- {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04},
- {PRO_DMOD, 0x0137, {0x00, 0x00, 0x07, 0x00, 0x06}, 0x05},
- {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xb6, 0x59}, 0x05},
- {PRO_DMOD, 0xf000, {0x0f}, 0x01},
- {PRO_DMOD, 0xf016, {0x10, 0x04, 0x05, 0x04, 0x05}, 0x05},
- {PRO_DMOD, 0xf01f, {0x8c, 0x00, 0x03, 0x0a, 0x0a}, 0x05},
- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00, 0x01}, 0x04},
- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04},
- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02},
- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02},
- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02},
- {PRO_DMOD, 0xf085, {0xc0, 0x01, 0x00}, 0x03},
- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06},
- {PRO_DMOD, 0xf130, {0x04}, 0x01},
- {PRO_DMOD, 0xf132, {0x04}, 0x01},
- {PRO_DMOD, 0xf144, {0x1a}, 0x01},
- {PRO_DMOD, 0xf146, {0x00}, 0x01},
- {PRO_DMOD, 0xf14a, {0x01}, 0x01},
- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf14f, {0x04}, 0x01},
- {PRO_DMOD, 0xf158, {0x7f}, 0x01},
- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02},
- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02},
- {PRO_DMOD, 0xf163, {0x05}, 0x01},
- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03},
- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf183, {0x01}, 0x01},
- {PRO_DMOD, 0xf19d, {0x40}, 0x01},
- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02},
- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02},
- {PRO_DMOD, 0xf204, {0x10}, 0x01},
- {PRO_DMOD, 0xf214, {0x00}, 0x01},
- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04},
- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05},
- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04},
- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03},
- {PRO_DMOD, 0xf55f, {0x0a}, 0x01},
- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02},
- {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02},
- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03},
- {PRO_DMOD, 0xf5f8, {0x01}, 0x01},
- {PRO_DMOD, 0xf5fd, {0x01}, 0x01},
- {PRO_DMOD, 0xf600, {0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17,
- 0x1f}, 0x08},
- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04},
- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04},
- {PRO_DMOD, 0xf78b, {0x01}, 0x01},
- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03},
- {PRO_DMOD, 0xf905, {0x01}, 0x01},
- {PRO_DMOD, 0xfb06, {0x03}, 0x01},
- {PRO_DMOD, 0xfd8b, {0x00}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */
-};
-
-/* Version 2 types */
-static struct it913xset it9135_v2[] = {
- {PRO_DMOD, 0x0051, {0x01}, 0x01},
- {PRO_DMOD, 0x0070, {0x0a}, 0x01},
- {PRO_DMOD, 0x007e, {0x04}, 0x01},
- {PRO_DMOD, 0x0081, {0x0a}, 0x01},
- {PRO_DMOD, 0x008a, {0x01}, 0x01},
- {PRO_DMOD, 0x008e, {0x01}, 0x01},
- {PRO_DMOD, 0x0092, {0x06}, 0x01},
- {PRO_DMOD, 0x0099, {0x01}, 0x01},
- {PRO_DMOD, 0x009f, {0xe1}, 0x01},
- {PRO_DMOD, 0x00a0, {0xcf}, 0x01},
- {PRO_DMOD, 0x00a3, {0x01}, 0x01},
- {PRO_DMOD, 0x00a5, {0x01}, 0x01},
- {PRO_DMOD, 0x00a6, {0x01}, 0x01},
- {PRO_DMOD, 0x00a9, {0x00}, 0x01},
- {PRO_DMOD, 0x00aa, {0x01}, 0x01},
- {PRO_DMOD, 0x00b0, {0x01}, 0x01},
- {PRO_DMOD, 0x00c2, {0x05}, 0x01},
- {PRO_DMOD, 0x00c6, {0x19}, 0x01},
- {PRO_DMOD, 0xf000, {0x0f}, 0x01},
- {PRO_DMOD, 0xf02b, {0x00}, 0x01},
- {PRO_DMOD, 0xf064, {0x03}, 0x01},
- {PRO_DMOD, 0xf065, {0xf9}, 0x01},
- {PRO_DMOD, 0xf066, {0x03}, 0x01},
- {PRO_DMOD, 0xf067, {0x01}, 0x01},
- {PRO_DMOD, 0xf06f, {0xe0}, 0x01},
- {PRO_DMOD, 0xf070, {0x03}, 0x01},
- {PRO_DMOD, 0xf072, {0x0f}, 0x01},
- {PRO_DMOD, 0xf073, {0x03}, 0x01},
- {PRO_DMOD, 0xf078, {0x00}, 0x01},
- {PRO_DMOD, 0xf087, {0x00}, 0x01},
- {PRO_DMOD, 0xf09b, {0x3f}, 0x01},
- {PRO_DMOD, 0xf09c, {0x00}, 0x01},
- {PRO_DMOD, 0xf09d, {0x20}, 0x01},
- {PRO_DMOD, 0xf09e, {0x00}, 0x01},
- {PRO_DMOD, 0xf09f, {0x0c}, 0x01},
- {PRO_DMOD, 0xf0a0, {0x00}, 0x01},
- {PRO_DMOD, 0xf130, {0x04}, 0x01},
- {PRO_DMOD, 0xf132, {0x04}, 0x01},
- {PRO_DMOD, 0xf144, {0x1a}, 0x01},
- {PRO_DMOD, 0xf146, {0x00}, 0x01},
- {PRO_DMOD, 0xf14a, {0x01}, 0x01},
- {PRO_DMOD, 0xf14c, {0x00}, 0x01},
- {PRO_DMOD, 0xf14d, {0x00}, 0x01},
- {PRO_DMOD, 0xf14f, {0x04}, 0x01},
- {PRO_DMOD, 0xf158, {0x7f}, 0x01},
- {PRO_DMOD, 0xf15a, {0x00}, 0x01},
- {PRO_DMOD, 0xf15b, {0x08}, 0x01},
- {PRO_DMOD, 0xf15d, {0x03}, 0x01},
- {PRO_DMOD, 0xf15e, {0x05}, 0x01},
- {PRO_DMOD, 0xf163, {0x05}, 0x01},
- {PRO_DMOD, 0xf166, {0x01}, 0x01},
- {PRO_DMOD, 0xf167, {0x40}, 0x01},
- {PRO_DMOD, 0xf168, {0x0f}, 0x01},
- {PRO_DMOD, 0xf17a, {0x00}, 0x01},
- {PRO_DMOD, 0xf17b, {0x00}, 0x01},
- {PRO_DMOD, 0xf183, {0x01}, 0x01},
- {PRO_DMOD, 0xf19d, {0x40}, 0x01},
- {PRO_DMOD, 0xf1bc, {0x36}, 0x01},
- {PRO_DMOD, 0xf1bd, {0x00}, 0x01},
- {PRO_DMOD, 0xf1cb, {0xa0}, 0x01},
- {PRO_DMOD, 0xf1cc, {0x01}, 0x01},
- {PRO_DMOD, 0xf204, {0x10}, 0x01},
- {PRO_DMOD, 0xf214, {0x00}, 0x01},
- {PRO_DMOD, 0xf40e, {0x0a}, 0x01},
- {PRO_DMOD, 0xf40f, {0x40}, 0x01},
- {PRO_DMOD, 0xf410, {0x08}, 0x01},
- {PRO_DMOD, 0xf55f, {0x0a}, 0x01},
- {PRO_DMOD, 0xf561, {0x15}, 0x01},
- {PRO_DMOD, 0xf562, {0x20}, 0x01},
- {PRO_DMOD, 0xf5e3, {0x09}, 0x01},
- {PRO_DMOD, 0xf5e4, {0x01}, 0x01},
- {PRO_DMOD, 0xf5e5, {0x01}, 0x01},
- {PRO_DMOD, 0xf600, {0x05}, 0x01},
- {PRO_DMOD, 0xf601, {0x08}, 0x01},
- {PRO_DMOD, 0xf602, {0x0b}, 0x01},
- {PRO_DMOD, 0xf603, {0x0e}, 0x01},
- {PRO_DMOD, 0xf604, {0x11}, 0x01},
- {PRO_DMOD, 0xf605, {0x14}, 0x01},
- {PRO_DMOD, 0xf606, {0x17}, 0x01},
- {PRO_DMOD, 0xf607, {0x1f}, 0x01},
- {PRO_DMOD, 0xf60e, {0x00}, 0x01},
- {PRO_DMOD, 0xf60f, {0x04}, 0x01},
- {PRO_DMOD, 0xf610, {0x32}, 0x01},
- {PRO_DMOD, 0xf611, {0x10}, 0x01},
- {PRO_DMOD, 0xf707, {0xfc}, 0x01},
- {PRO_DMOD, 0xf708, {0x00}, 0x01},
- {PRO_DMOD, 0xf709, {0x37}, 0x01},
- {PRO_DMOD, 0xf70a, {0x00}, 0x01},
- {PRO_DMOD, 0xf78b, {0x01}, 0x01},
- {PRO_DMOD, 0xf80f, {0x40}, 0x01},
- {PRO_DMOD, 0xf810, {0x54}, 0x01},
- {PRO_DMOD, 0xf811, {0x5a}, 0x01},
- {PRO_DMOD, 0xf905, {0x01}, 0x01},
- {PRO_DMOD, 0xfb06, {0x03}, 0x01},
- {PRO_DMOD, 0xfd8b, {0x00}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */
-};
-
-static struct it913xset it9135_60[] = {
- {PRO_DMOD, 0x0043, {0x00}, 0x01},
- {PRO_DMOD, 0x0046, {0x60}, 0x01},
- {PRO_DMOD, 0x0051, {0x01}, 0x01},
- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0x0068, {0x0a}, 0x01},
- {PRO_DMOD, 0x006a, {0x03}, 0x01},
- {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03},
- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0x8c, 0x01}, 0x05},
- {PRO_DMOD, 0x007e, {0x04}, 0x01},
- {PRO_DMOD, 0x0081, {0x0a, 0x12}, 0x02},
- {PRO_DMOD, 0x0084, {0x0a, 0x33, 0xbe, 0xa0, 0xc6, 0xb6, 0x01}, 0x07},
- {PRO_DMOD, 0x008e, {0x01}, 0x01},
- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05},
- {PRO_DMOD, 0x0099, {0x01}, 0x01},
- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02},
- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02},
- {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04},
- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02},
- {PRO_DMOD, 0x00b0, {0x01}, 0x01},
- {PRO_DMOD, 0x00b3, {0x02, 0x3a}, 0x02},
- {PRO_DMOD, 0x00b6, {0x14}, 0x01},
- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05, 0x01, 0x00}, 0x05},
- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02},
- {PRO_DMOD, 0x00cb, {0x32, 0x2c, 0x4f, 0x30}, 0x04},
- {PRO_DMOD, 0x00f3, {0x05, 0xa0, 0x8c}, 0x03},
- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03},
- {PRO_DMOD, 0x00fc, { 0x03, 0x03, 0x02, 0x0a, 0x50, 0x7b, 0x8c,
- 0x00, 0x02, 0xbe, 0x00}, 0x0b},
- {PRO_DMOD, 0x0109, {0x02}, 0x01},
- {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02},
- {PRO_DMOD, 0x011a, {0xbe}, 0x01},
- {PRO_DMOD, 0x0124, {0xae}, 0x01},
- {PRO_DMOD, 0x0127, {0x00}, 0x01},
- {PRO_DMOD, 0x012a, {0x56, 0x50, 0x47, 0x42}, 0x04},
- {PRO_DMOD, 0x0137, {0x00}, 0x01},
- {PRO_DMOD, 0x013b, {0x08}, 0x01},
- {PRO_DMOD, 0x013f, {0x5b}, 0x01},
- {PRO_DMOD, 0x0141, { 0x59, 0xf9, 0x19, 0x19, 0x8c, 0x8c, 0x8c,
- 0x6e, 0x8c, 0x50, 0x8c, 0x8c, 0xac, 0xc6,
- 0x33}, 0x0f},
- {PRO_DMOD, 0x0151, {0x28}, 0x01},
- {PRO_DMOD, 0x0153, {0xbc}, 0x01},
- {PRO_DMOD, 0x0178, {0x09}, 0x01},
- {PRO_DMOD, 0x0181, {0x94, 0x6e}, 0x02},
- {PRO_DMOD, 0x0185, {0x24}, 0x01},
- {PRO_DMOD, 0x0187, {0x00, 0x00, 0xbe, 0x02, 0x80}, 0x05},
- {PRO_DMOD, 0xed02, {0xff}, 0x01},
- {PRO_DMOD, 0xee42, {0xff}, 0x01},
- {PRO_DMOD, 0xee82, {0xff}, 0x01},
- {PRO_DMOD, 0xf000, {0x0f}, 0x01},
- {PRO_DMOD, 0xf01f, {0x8c, 0x00}, 0x02},
- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00}, 0x03},
- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04},
- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02},
- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02},
- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02},
- {PRO_DMOD, 0xf087, {0x00}, 0x01},
- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06},
- {PRO_DMOD, 0xf130, {0x04}, 0x01},
- {PRO_DMOD, 0xf132, {0x04}, 0x01},
- {PRO_DMOD, 0xf144, {0x1a}, 0x01},
- {PRO_DMOD, 0xf146, {0x00}, 0x01},
- {PRO_DMOD, 0xf14a, {0x01}, 0x01},
- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf14f, {0x04}, 0x01},
- {PRO_DMOD, 0xf158, {0x7f}, 0x01},
- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02},
- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02},
- {PRO_DMOD, 0xf163, {0x05}, 0x01},
- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03},
- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf183, {0x01}, 0x01},
- {PRO_DMOD, 0xf19d, {0x40}, 0x01},
- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02},
- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02},
- {PRO_DMOD, 0xf204, {0x10}, 0x01},
- {PRO_DMOD, 0xf214, {0x00}, 0x01},
- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04},
- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05},
- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04},
- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03},
- {PRO_DMOD, 0xf55f, {0x0a}, 0x01},
- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02},
- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03},
- {PRO_DMOD, 0xf600, {0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17
- , 0x1f}, 0x08},
- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04},
- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04},
- {PRO_DMOD, 0xf78b, {0x01}, 0x01},
- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03},
- {PRO_DMOD, 0xf905, {0x01}, 0x01},
- {PRO_DMOD, 0xfb06, {0x03}, 0x01},
- {PRO_DMOD, 0xfd8b, {0x00}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */
-};
-
-static struct it913xset it9135_61[] = {
- {PRO_DMOD, 0x0043, {0x00}, 0x01},
- {PRO_DMOD, 0x0046, {0x61}, 0x01},
- {PRO_DMOD, 0x0051, {0x01}, 0x01},
- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0x0068, {0x06}, 0x01},
- {PRO_DMOD, 0x006a, {0x03}, 0x01},
- {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03},
- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0x90, 0x01}, 0x05},
- {PRO_DMOD, 0x007e, {0x04}, 0x01},
- {PRO_DMOD, 0x0081, {0x0a, 0x12}, 0x02},
- {PRO_DMOD, 0x0084, {0x0a, 0x33, 0xbc, 0x9c, 0xcc, 0xa8, 0x01}, 0x07},
- {PRO_DMOD, 0x008e, {0x01}, 0x01},
- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05},
- {PRO_DMOD, 0x0099, {0x01}, 0x01},
- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02},
- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02},
- {PRO_DMOD, 0x00a3, {0x01, 0x5c, 0x01, 0x01}, 0x04},
- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02},
- {PRO_DMOD, 0x00b0, {0x01}, 0x01},
- {PRO_DMOD, 0x00b3, {0x02, 0x3a}, 0x02},
- {PRO_DMOD, 0x00b6, {0x14}, 0x01},
- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05, 0x01, 0x00}, 0x05},
- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02},
- {PRO_DMOD, 0x00cb, {0x32, 0x2c, 0x4f, 0x30}, 0x04},
- {PRO_DMOD, 0x00f3, {0x05, 0xa0, 0x8c}, 0x03},
- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03},
- {PRO_DMOD, 0x00fc, { 0x03, 0x03, 0x02, 0x08, 0x50, 0x7b, 0x8c,
- 0x01, 0x02, 0xc8, 0x00}, 0x0b},
- {PRO_DMOD, 0x0109, {0x02}, 0x01},
- {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02},
- {PRO_DMOD, 0x011a, {0xc6}, 0x01},
- {PRO_DMOD, 0x0124, {0xa8}, 0x01},
- {PRO_DMOD, 0x0127, {0x00}, 0x01},
- {PRO_DMOD, 0x012a, {0x59, 0x50, 0x47, 0x42}, 0x04},
- {PRO_DMOD, 0x0137, {0x00}, 0x01},
- {PRO_DMOD, 0x013b, {0x05}, 0x01},
- {PRO_DMOD, 0x013f, {0x5b}, 0x01},
- {PRO_DMOD, 0x0141, { 0x59, 0xf9, 0x59, 0x59, 0x8c, 0x8c, 0x8c,
- 0x7b, 0x8c, 0x50, 0x8c, 0x8c, 0xa8, 0xc6,
- 0x33}, 0x0f},
- {PRO_DMOD, 0x0151, {0x28}, 0x01},
- {PRO_DMOD, 0x0153, {0xcc}, 0x01},
- {PRO_DMOD, 0x0178, {0x09}, 0x01},
- {PRO_DMOD, 0x0181, {0x9c, 0x76}, 0x02},
- {PRO_DMOD, 0x0185, {0x28}, 0x01},
- {PRO_DMOD, 0x0187, {0x01, 0x00, 0xaa, 0x02, 0x80}, 0x05},
- {PRO_DMOD, 0xed02, {0xff}, 0x01},
- {PRO_DMOD, 0xee42, {0xff}, 0x01},
- {PRO_DMOD, 0xee82, {0xff}, 0x01},
- {PRO_DMOD, 0xf000, {0x0f}, 0x01},
- {PRO_DMOD, 0xf01f, {0x8c, 0x00}, 0x02},
- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00}, 0x03},
- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04},
- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02},
- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02},
- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02},
- {PRO_DMOD, 0xf087, {0x00}, 0x01},
- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06},
- {PRO_DMOD, 0xf130, {0x04}, 0x01},
- {PRO_DMOD, 0xf132, {0x04}, 0x01},
- {PRO_DMOD, 0xf144, {0x1a}, 0x01},
- {PRO_DMOD, 0xf146, {0x00}, 0x01},
- {PRO_DMOD, 0xf14a, {0x01}, 0x01},
- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf14f, {0x04}, 0x01},
- {PRO_DMOD, 0xf158, {0x7f}, 0x01},
- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02},
- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02},
- {PRO_DMOD, 0xf163, {0x05}, 0x01},
- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03},
- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf183, {0x01}, 0x01},
- {PRO_DMOD, 0xf19d, {0x40}, 0x01},
- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02},
- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02},
- {PRO_DMOD, 0xf204, {0x10}, 0x01},
- {PRO_DMOD, 0xf214, {0x00}, 0x01},
- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04},
- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05},
- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04},
- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03},
- {PRO_DMOD, 0xf55f, {0x0a}, 0x01},
- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02},
- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03},
- {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17,
- 0x1f}, 0x08},
- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04},
- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04},
- {PRO_DMOD, 0xf78b, {0x01}, 0x01},
- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03},
- {PRO_DMOD, 0xf905, {0x01}, 0x01},
- {PRO_DMOD, 0xfb06, {0x03}, 0x01},
- {PRO_DMOD, 0xfd8b, {0x00}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */
-};
-
-static struct it913xset it9135_62[] = {
- {PRO_DMOD, 0x0043, {0x00}, 0x01},
- {PRO_DMOD, 0x0046, {0x62}, 0x01},
- {PRO_DMOD, 0x0051, {0x01}, 0x01},
- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0x0068, {0x0a}, 0x01},
- {PRO_DMOD, 0x006a, {0x03}, 0x01},
- {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03},
- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0x8c, 0x01}, 0x05},
- {PRO_DMOD, 0x007e, {0x04}, 0x01},
- {PRO_DMOD, 0x0081, {0x0a, 0x12}, 0x02},
- {PRO_DMOD, 0x0084, { 0x0a, 0x33, 0xb8, 0x9c, 0xb2, 0xa6, 0x01},
- 0x07},
- {PRO_DMOD, 0x008e, {0x01}, 0x01},
- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05},
- {PRO_DMOD, 0x0099, {0x01}, 0x01},
- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02},
- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02},
- {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04},
- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02},
- {PRO_DMOD, 0x00b0, {0x01}, 0x01},
- {PRO_DMOD, 0x00b3, {0x02, 0x3a}, 0x02},
- {PRO_DMOD, 0x00b6, {0x14}, 0x01},
- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05, 0x01, 0x00}, 0x05},
- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02},
- {PRO_DMOD, 0x00cb, {0x32, 0x2c, 0x4f, 0x30}, 0x04},
- {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03},
- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03},
- {PRO_DMOD, 0x00fc, { 0x02, 0x03, 0x02, 0x09, 0x50, 0x6e, 0x8c,
- 0x02, 0x02, 0xc2, 0x00}, 0x0b},
- {PRO_DMOD, 0x0109, {0x02}, 0x01},
- {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02},
- {PRO_DMOD, 0x011a, {0xb8}, 0x01},
- {PRO_DMOD, 0x0124, {0xa8}, 0x01},
- {PRO_DMOD, 0x0127, {0x00}, 0x01},
- {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04},
- {PRO_DMOD, 0x0137, {0x00}, 0x01},
- {PRO_DMOD, 0x013b, {0x05}, 0x01},
- {PRO_DMOD, 0x013f, {0x5b}, 0x01},
- {PRO_DMOD, 0x0141, { 0x59, 0xf9, 0x59, 0x19, 0x8c, 0x8c, 0x8c,
- 0x7b, 0x8c, 0x50, 0x70, 0x8c, 0x96, 0xd0,
- 0x33}, 0x0f},
- {PRO_DMOD, 0x0151, {0x28}, 0x01},
- {PRO_DMOD, 0x0153, {0xb2}, 0x01},
- {PRO_DMOD, 0x0178, {0x09}, 0x01},
- {PRO_DMOD, 0x0181, {0x9c, 0x6e}, 0x02},
- {PRO_DMOD, 0x0185, {0x24}, 0x01},
- {PRO_DMOD, 0x0187, {0x00, 0x00, 0xb8, 0x02, 0x80}, 0x05},
- {PRO_DMOD, 0xed02, {0xff}, 0x01},
- {PRO_DMOD, 0xee42, {0xff}, 0x01},
- {PRO_DMOD, 0xee82, {0xff}, 0x01},
- {PRO_DMOD, 0xf000, {0x0f}, 0x01},
- {PRO_DMOD, 0xf01f, {0x8c, 0x00}, 0x02},
- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00}, 0x03},
- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04},
- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02},
- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02},
- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02},
- {PRO_DMOD, 0xf087, {0x00}, 0x01},
- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06},
- {PRO_DMOD, 0xf130, {0x04}, 0x01},
- {PRO_DMOD, 0xf132, {0x04}, 0x01},
- {PRO_DMOD, 0xf144, {0x1a}, 0x01},
- {PRO_DMOD, 0xf146, {0x00}, 0x01},
- {PRO_DMOD, 0xf14a, {0x01}, 0x01},
- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf14f, {0x04}, 0x01},
- {PRO_DMOD, 0xf158, {0x7f}, 0x01},
- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02},
- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02},
- {PRO_DMOD, 0xf163, {0x05}, 0x01},
- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03},
- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02},
- {PRO_DMOD, 0xf183, {0x01}, 0x01},
- {PRO_DMOD, 0xf19d, {0x40}, 0x01},
- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02},
- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02},
- {PRO_DMOD, 0xf204, {0x10}, 0x01},
- {PRO_DMOD, 0xf214, {0x00}, 0x01},
- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04},
- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05},
- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04},
- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03},
- {PRO_DMOD, 0xf55f, {0x0a}, 0x01},
- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02},
- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03},
- {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17,
- 0x1f}, 0x08},
- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04},
- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04},
- {PRO_DMOD, 0xf78b, {0x01}, 0x01},
- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03},
- {PRO_DMOD, 0xf905, {0x01}, 0x01},
- {PRO_DMOD, 0xfb06, {0x03}, 0x01},
- {PRO_DMOD, 0xfd8b, {0x00}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */
-};
-
-/* Tuner setting scripts (still keeping it9137) */
-static struct it913xset it9137_tuner_off[] = {
- {PRO_DMOD, 0xfba8, {0x01}, 0x01}, /* Tuner Clock Off */
- {PRO_DMOD, 0xec40, {0x00}, 0x01}, /* Power Down Tuner */
- {PRO_DMOD, 0xec02, {0x3f, 0x1f, 0x3f, 0x3f}, 0x04},
- {PRO_DMOD, 0xec06, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00}, 0x0c},
- {PRO_DMOD, 0xec12, {0x00, 0x00, 0x00, 0x00}, 0x04},
- {PRO_DMOD, 0xec17, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00}, 0x09},
- {PRO_DMOD, 0xec22, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00}, 0x0a},
- {PRO_DMOD, 0xec20, {0x00}, 0x01},
- {PRO_DMOD, 0xec3f, {0x01}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
-};
-
-static struct it913xset set_it9135_template[] = {
- {PRO_DMOD, 0xee06, {0x00}, 0x01},
- {PRO_DMOD, 0xec56, {0x00}, 0x01},
- {PRO_DMOD, 0xec4c, {0x00}, 0x01},
- {PRO_DMOD, 0xec4d, {0x00}, 0x01},
- {PRO_DMOD, 0xec4e, {0x00}, 0x01},
- {PRO_DMOD, 0x011e, {0x00}, 0x01}, /* Older Devices */
- {PRO_DMOD, 0x011f, {0x00}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
-};
-
-static struct it913xset set_it9137_template[] = {
- {PRO_DMOD, 0xee06, {0x00}, 0x01},
- {PRO_DMOD, 0xec56, {0x00}, 0x01},
- {PRO_DMOD, 0xec4c, {0x00}, 0x01},
- {PRO_DMOD, 0xec4d, {0x00}, 0x01},
- {PRO_DMOD, 0xec4e, {0x00}, 0x01},
- {PRO_DMOD, 0xec4f, {0x00}, 0x01},
- {PRO_DMOD, 0xec50, {0x00}, 0x01},
- {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */
-};
diff --git a/drivers/media/dvb-frontends/it913x-fe.c b/drivers/media/dvb-frontends/it913x-fe.c
deleted file mode 100644
index 6e1c6eb340b7..000000000000
--- a/drivers/media/dvb-frontends/it913x-fe.c
+++ /dev/null
@@ -1,1045 +0,0 @@
-/*
- * Driver for it913x-fe Frontend
- *
- * with support for on chip it9137 integral tuner
- *
- * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
- * IT9137 Copyright (C) ITE Tech Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-
-#include "dvb_frontend.h"
-#include "it913x-fe.h"
-#include "it913x-fe-priv.h"
-
-static int it913x_debug;
-
-module_param_named(debug, it913x_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
-
-#define dprintk(level, args...) do { \
- if (level & it913x_debug) \
- printk(KERN_DEBUG "it913x-fe: " args); \
-} while (0)
-
-#define deb_info(args...) dprintk(0x01, args)
-#define debug_data_snipet(level, name, p) \
- dprintk(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \
- *p, *(p+1), *(p+2), *(p+3), *(p+4), \
- *(p+5), *(p+6), *(p+7));
-#define info(format, arg...) \
- printk(KERN_INFO "it913x-fe: " format "\n" , ## arg)
-
-struct it913x_fe_state {
- struct dvb_frontend frontend;
- struct i2c_adapter *i2c_adap;
- struct ite_config *config;
- u8 i2c_addr;
- u32 frequency;
- fe_modulation_t constellation;
- fe_transmit_mode_t transmission_mode;
- u8 priority;
- u32 crystalFrequency;
- u32 adcFrequency;
- u8 tuner_type;
- struct adctable *table;
- fe_status_t it913x_status;
- u16 tun_xtal;
- u8 tun_fdiv;
- u8 tun_clk_mode;
- u32 tun_fn_min;
- u32 ucblocks;
-};
-
-static int it913x_read_reg(struct it913x_fe_state *state,
- u32 reg, u8 *data, u8 count)
-{
- int ret;
- u8 pro = PRO_DMOD; /* All reads from demodulator */
- u8 b[4];
- struct i2c_msg msg[2] = {
- { .addr = state->i2c_addr + (pro << 1), .flags = 0,
- .buf = b, .len = sizeof(b) },
- { .addr = state->i2c_addr + (pro << 1), .flags = I2C_M_RD,
- .buf = data, .len = count }
- };
- b[0] = (u8) reg >> 24;
- b[1] = (u8)(reg >> 16) & 0xff;
- b[2] = (u8)(reg >> 8) & 0xff;
- b[3] = (u8) reg & 0xff;
-
- ret = i2c_transfer(state->i2c_adap, msg, 2);
-
- return ret;
-}
-
-static int it913x_read_reg_u8(struct it913x_fe_state *state, u32 reg)
-{
- int ret;
- u8 b[1];
- ret = it913x_read_reg(state, reg, &b[0], sizeof(b));
- return (ret < 0) ? -ENODEV : b[0];
-}
-
-static int it913x_write(struct it913x_fe_state *state,
- u8 pro, u32 reg, u8 buf[], u8 count)
-{
- u8 b[256];
- struct i2c_msg msg[1] = {
- { .addr = state->i2c_addr + (pro << 1), .flags = 0,
- .buf = b, .len = count + 4 }
- };
- int ret;
-
- b[0] = (u8) reg >> 24;
- b[1] = (u8)(reg >> 16) & 0xff;
- b[2] = (u8)(reg >> 8) & 0xff;
- b[3] = (u8) reg & 0xff;
- memcpy(&b[4], buf, count);
-
- ret = i2c_transfer(state->i2c_adap, msg, 1);
-
- if (ret < 0)
- return -EIO;
-
- return 0;
-}
-
-static int it913x_write_reg(struct it913x_fe_state *state,
- u8 pro, u32 reg, u32 data)
-{
- int ret;
- u8 b[4];
- u8 s;
-
- b[0] = data >> 24;
- b[1] = (data >> 16) & 0xff;
- b[2] = (data >> 8) & 0xff;
- b[3] = data & 0xff;
- /* expand write as needed */
- if (data < 0x100)
- s = 3;
- else if (data < 0x1000)
- s = 2;
- else if (data < 0x100000)
- s = 1;
- else
- s = 0;
-
- ret = it913x_write(state, pro, reg, &b[s], sizeof(b) - s);
-
- return ret;
-}
-
-static int it913x_fe_script_loader(struct it913x_fe_state *state,
- struct it913xset *loadscript)
-{
- int ret, i;
- if (loadscript == NULL)
- return -EINVAL;
-
- for (i = 0; i < 1000; ++i) {
- if (loadscript[i].pro == 0xff)
- break;
- ret = it913x_write(state, loadscript[i].pro,
- loadscript[i].address,
- loadscript[i].reg, loadscript[i].count);
- if (ret < 0)
- return -ENODEV;
- }
- return 0;
-}
-
-static int it913x_init_tuner(struct it913x_fe_state *state)
-{
- int ret, i, reg;
- u8 val, nv_val;
- u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2};
- u8 b[2];
-
- reg = it913x_read_reg_u8(state, 0xec86);
- switch (reg) {
- case 0:
- state->tun_clk_mode = reg;
- state->tun_xtal = 2000;
- state->tun_fdiv = 3;
- val = 16;
- break;
- case -ENODEV:
- return -ENODEV;
- case 1:
- default:
- state->tun_clk_mode = reg;
- state->tun_xtal = 640;
- state->tun_fdiv = 1;
- val = 6;
- break;
- }
-
- reg = it913x_read_reg_u8(state, 0xed03);
-
- if (reg < 0)
- return -ENODEV;
- else if (reg < ARRAY_SIZE(nv))
- nv_val = nv[reg];
- else
- nv_val = 2;
-
- for (i = 0; i < 50; i++) {
- ret = it913x_read_reg(state, 0xed23, &b[0], sizeof(b));
- reg = (b[1] << 8) + b[0];
- if (reg > 0)
- break;
- if (ret < 0)
- return -ENODEV;
- udelay(2000);
- }
- state->tun_fn_min = state->tun_xtal * reg;
- state->tun_fn_min /= (state->tun_fdiv * nv_val);
- deb_info("Tuner fn_min %d", state->tun_fn_min);
-
- if (state->config->chip_ver > 1)
- msleep(50);
- else {
- for (i = 0; i < 50; i++) {
- reg = it913x_read_reg_u8(state, 0xec82);
- if (reg > 0)
- break;
- if (reg < 0)
- return -ENODEV;
- udelay(2000);
- }
- }
-
- return it913x_write_reg(state, PRO_DMOD, 0xed81, val);
-}
-
-static int it9137_set_tuner(struct it913x_fe_state *state,
- u32 bandwidth, u32 frequency_m)
-{
- struct it913xset *set_tuner = set_it9137_template;
- int ret, reg;
- u32 frequency = frequency_m / 1000;
- u32 freq, temp_f, tmp;
- u16 iqik_m_cal;
- u16 n_div;
- u8 n;
- u8 l_band;
- u8 lna_band;
- u8 bw;
-
- if (state->config->firmware_ver == 1)
- set_tuner = set_it9135_template;
- else
- set_tuner = set_it9137_template;
-
- deb_info("Tuner Frequency %d Bandwidth %d", frequency, bandwidth);
-
- if (frequency >= 51000 && frequency <= 440000) {
- l_band = 0;
- lna_band = 0;
- } else if (frequency > 440000 && frequency <= 484000) {
- l_band = 1;
- lna_band = 1;
- } else if (frequency > 484000 && frequency <= 533000) {
- l_band = 1;
- lna_band = 2;
- } else if (frequency > 533000 && frequency <= 587000) {
- l_band = 1;
- lna_band = 3;
- } else if (frequency > 587000 && frequency <= 645000) {
- l_band = 1;
- lna_band = 4;
- } else if (frequency > 645000 && frequency <= 710000) {
- l_band = 1;
- lna_band = 5;
- } else if (frequency > 710000 && frequency <= 782000) {
- l_band = 1;
- lna_band = 6;
- } else if (frequency > 782000 && frequency <= 860000) {
- l_band = 1;
- lna_band = 7;
- } else if (frequency > 1450000 && frequency <= 1492000) {
- l_band = 1;
- lna_band = 0;
- } else if (frequency > 1660000 && frequency <= 1685000) {
- l_band = 1;
- lna_band = 1;
- } else
- return -EINVAL;
- set_tuner[0].reg[0] = lna_band;
-
- switch (bandwidth) {
- case 5000000:
- bw = 0;
- break;
- case 6000000:
- bw = 2;
- break;
- case 7000000:
- bw = 4;
- break;
- default:
- case 8000000:
- bw = 6;
- break;
- }
-
- set_tuner[1].reg[0] = bw;
- set_tuner[2].reg[0] = 0xa0 | (l_band << 3);
-
- if (frequency > 53000 && frequency <= 74000) {
- n_div = 48;
- n = 0;
- } else if (frequency > 74000 && frequency <= 111000) {
- n_div = 32;
- n = 1;
- } else if (frequency > 111000 && frequency <= 148000) {
- n_div = 24;
- n = 2;
- } else if (frequency > 148000 && frequency <= 222000) {
- n_div = 16;
- n = 3;
- } else if (frequency > 222000 && frequency <= 296000) {
- n_div = 12;
- n = 4;
- } else if (frequency > 296000 && frequency <= 445000) {
- n_div = 8;
- n = 5;
- } else if (frequency > 445000 && frequency <= state->tun_fn_min) {
- n_div = 6;
- n = 6;
- } else if (frequency > state->tun_fn_min && frequency <= 950000) {
- n_div = 4;
- n = 7;
- } else if (frequency > 1450000 && frequency <= 1680000) {
- n_div = 2;
- n = 0;
- } else
- return -EINVAL;
-
- reg = it913x_read_reg_u8(state, 0xed81);
- iqik_m_cal = (u16)reg * n_div;
-
- if (reg < 0x20) {
- if (state->tun_clk_mode == 0)
- iqik_m_cal = (iqik_m_cal * 9) >> 5;
- else
- iqik_m_cal >>= 1;
- } else {
- iqik_m_cal = 0x40 - iqik_m_cal;
- if (state->tun_clk_mode == 0)
- iqik_m_cal = ~((iqik_m_cal * 9) >> 5);
- else
- iqik_m_cal = ~(iqik_m_cal >> 1);
- }
-
- temp_f = frequency * (u32)n_div * (u32)state->tun_fdiv;
- freq = temp_f / state->tun_xtal;
- tmp = freq * state->tun_xtal;
-
- if ((temp_f - tmp) >= (state->tun_xtal >> 1))
- freq++;
-
- freq += (u32) n << 13;
- /* Frequency OMEGA_IQIK_M_CAL_MID*/
- temp_f = freq + (u32)iqik_m_cal;
-
- set_tuner[3].reg[0] = temp_f & 0xff;
- set_tuner[4].reg[0] = (temp_f >> 8) & 0xff;
-
- deb_info("High Frequency = %04x", temp_f);
-
- /* Lower frequency */
- set_tuner[5].reg[0] = freq & 0xff;
- set_tuner[6].reg[0] = (freq >> 8) & 0xff;
-
- deb_info("low Frequency = %04x", freq);
-
- ret = it913x_fe_script_loader(state, set_tuner);
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int it913x_fe_select_bw(struct it913x_fe_state *state,
- u32 bandwidth, u32 adcFrequency)
-{
- int ret, i;
- u8 buffer[256];
- u32 coeff[8];
- u16 bfsfcw_fftinx_ratio;
- u16 fftinx_bfsfcw_ratio;
- u8 count;
- u8 bw;
- u8 adcmultiplier;
-
- deb_info("Bandwidth %d Adc %d", bandwidth, adcFrequency);
-
- switch (bandwidth) {
- case 5000000:
- bw = 3;
- break;
- case 6000000:
- bw = 0;
- break;
- case 7000000:
- bw = 1;
- break;
- default:
- case 8000000:
- bw = 2;
- break;
- }
- ret = it913x_write_reg(state, PRO_DMOD, REG_BW, bw);
-
- if (state->table == NULL)
- return -EINVAL;
-
- /* In write order */
- coeff[0] = state->table[bw].coeff_1_2048;
- coeff[1] = state->table[bw].coeff_2_2k;
- coeff[2] = state->table[bw].coeff_1_8191;
- coeff[3] = state->table[bw].coeff_1_8192;
- coeff[4] = state->table[bw].coeff_1_8193;
- coeff[5] = state->table[bw].coeff_2_8k;
- coeff[6] = state->table[bw].coeff_1_4096;
- coeff[7] = state->table[bw].coeff_2_4k;
- bfsfcw_fftinx_ratio = state->table[bw].bfsfcw_fftinx_ratio;
- fftinx_bfsfcw_ratio = state->table[bw].fftinx_bfsfcw_ratio;
-
- /* ADC multiplier */
- ret = it913x_read_reg_u8(state, ADC_X_2);
- if (ret < 0)
- return -EINVAL;
-
- adcmultiplier = ret;
-
- count = 0;
-
- /* Build Buffer for COEFF Registers */
- for (i = 0; i < 8; i++) {
- if (adcmultiplier == 1)
- coeff[i] /= 2;
- buffer[count++] = (coeff[i] >> 24) & 0x3;
- buffer[count++] = (coeff[i] >> 16) & 0xff;
- buffer[count++] = (coeff[i] >> 8) & 0xff;
- buffer[count++] = coeff[i] & 0xff;
- }
-
- /* bfsfcw_fftinx_ratio register 0x21-0x22 */
- buffer[count++] = bfsfcw_fftinx_ratio & 0xff;
- buffer[count++] = (bfsfcw_fftinx_ratio >> 8) & 0xff;
- /* fftinx_bfsfcw_ratio register 0x23-0x24 */
- buffer[count++] = fftinx_bfsfcw_ratio & 0xff;
- buffer[count++] = (fftinx_bfsfcw_ratio >> 8) & 0xff;
- /* start at COEFF_1_2048 and write through to fftinx_bfsfcw_ratio*/
- ret = it913x_write(state, PRO_DMOD, COEFF_1_2048, buffer, count);
-
- for (i = 0; i < 42; i += 8)
- debug_data_snipet(0x1, "Buffer", &buffer[i]);
-
- return ret;
-}
-
-
-
-static int it913x_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- int ret, i;
- fe_status_t old_status = state->it913x_status;
- *status = 0;
-
- if (state->it913x_status == 0) {
- ret = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS);
- if (ret == 0x1) {
- *status |= FE_HAS_SIGNAL;
- for (i = 0; i < 40; i++) {
- ret = it913x_read_reg_u8(state, MP2IF_SYNC_LK);
- if (ret == 0x1)
- break;
- msleep(25);
- }
- if (ret == 0x1)
- *status |= FE_HAS_CARRIER
- | FE_HAS_VITERBI
- | FE_HAS_SYNC;
- state->it913x_status = *status;
- }
- }
-
- if (state->it913x_status & FE_HAS_SYNC) {
- ret = it913x_read_reg_u8(state, TPSD_LOCK);
- if (ret == 0x1)
- *status |= FE_HAS_LOCK
- | state->it913x_status;
- else
- state->it913x_status = 0;
- if (old_status != state->it913x_status)
- ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, ret);
- }
-
- return 0;
-}
-
-/* FEC values based on fe_code_rate_t non supported values 0*/
-int it913x_qpsk_pval[] = {0, -93, -91, -90, 0, -89, -88};
-int it913x_16qam_pval[] = {0, -87, -85, -84, 0, -83, -82};
-int it913x_64qam_pval[] = {0, -82, -80, -78, 0, -77, -76};
-
-static int it913x_get_signal_strength(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- struct it913x_fe_state *state = fe->demodulator_priv;
- u8 code_rate;
- int ret, temp;
- u8 lna_gain_os;
-
- ret = it913x_read_reg_u8(state, VAR_P_INBAND);
- if (ret < 0)
- return ret;
-
- /* VHF/UHF gain offset */
- if (state->frequency < 300000000)
- lna_gain_os = 7;
- else
- lna_gain_os = 14;
-
- temp = (ret - 100) - lna_gain_os;
-
- if (state->priority == PRIORITY_HIGH)
- code_rate = p->code_rate_HP;
- else
- code_rate = p->code_rate_LP;
-
- if (code_rate >= ARRAY_SIZE(it913x_qpsk_pval))
- return -EINVAL;
-
- deb_info("Reg VAR_P_INBAND:%d Calc Offset Value:%d", ret, temp);
-
- /* Apply FEC offset values*/
- switch (p->modulation) {
- case QPSK:
- temp -= it913x_qpsk_pval[code_rate];
- break;
- case QAM_16:
- temp -= it913x_16qam_pval[code_rate];
- break;
- case QAM_64:
- temp -= it913x_64qam_pval[code_rate];
- break;
- default:
- return -EINVAL;
- }
-
- if (temp < -15)
- ret = 0;
- else if ((-15 <= temp) && (temp < 0))
- ret = (2 * (temp + 15)) / 3;
- else if ((0 <= temp) && (temp < 20))
- ret = 4 * temp + 10;
- else if ((20 <= temp) && (temp < 35))
- ret = (2 * (temp - 20)) / 3 + 90;
- else if (temp >= 35)
- ret = 100;
-
- deb_info("Signal Strength :%d", ret);
-
- return ret;
-}
-
-static int it913x_fe_read_signal_strength(struct dvb_frontend *fe,
- u16 *strength)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- int ret = 0;
- if (state->config->read_slevel) {
- if (state->it913x_status & FE_HAS_SIGNAL)
- ret = it913x_read_reg_u8(state, SIGNAL_LEVEL);
- } else
- ret = it913x_get_signal_strength(fe);
-
- if (ret >= 0)
- *strength = (u16)((u32)ret * 0xffff / 0x64);
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- int ret;
- u8 reg[3];
- u32 snr_val, snr_min, snr_max;
- u32 temp;
-
- ret = it913x_read_reg(state, 0x2c, reg, sizeof(reg));
-
- snr_val = (u32)(reg[2] << 16) | (reg[1] << 8) | reg[0];
-
- ret |= it913x_read_reg(state, 0xf78b, reg, 1);
- if (reg[0])
- snr_val /= reg[0];
-
- if (state->transmission_mode == TRANSMISSION_MODE_2K)
- snr_val *= 4;
- else if (state->transmission_mode == TRANSMISSION_MODE_4K)
- snr_val *= 2;
-
- if (state->constellation == QPSK) {
- snr_min = 0xb4711;
- snr_max = 0x191451;
- } else if (state->constellation == QAM_16) {
- snr_min = 0x4f0d5;
- snr_max = 0xc7925;
- } else if (state->constellation == QAM_64) {
- snr_min = 0x256d0;
- snr_max = 0x626be;
- } else
- return -EINVAL;
-
- if (snr_val < snr_min)
- *snr = 0;
- else if (snr_val < snr_max) {
- temp = (snr_val - snr_min) >> 5;
- temp *= 0xffff;
- temp /= (snr_max - snr_min) >> 5;
- *snr = (u16)temp;
- } else
- *snr = 0xffff;
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int it913x_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- u8 reg[5];
- /* Read Aborted Packets and Pre-Viterbi error rate 5 bytes */
- it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg));
- state->ucblocks += (u32)(reg[1] << 8) | reg[0];
- *ber = (u32)(reg[4] << 16) | (reg[3] << 8) | reg[2];
- return 0;
-}
-
-static int it913x_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- int ret;
- u8 reg[2];
- /* Aborted Packets */
- ret = it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg));
- state->ucblocks += (u32)(reg[1] << 8) | reg[0];
- *ucblocks = state->ucblocks;
- return ret;
-}
-
-static int it913x_fe_get_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- struct it913x_fe_state *state = fe->demodulator_priv;
- u8 reg[8];
-
- it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg));
-
- if (reg[3] < 3)
- p->modulation = fe_con[reg[3]];
-
- if (reg[0] < 3)
- p->transmission_mode = fe_mode[reg[0]];
-
- if (reg[1] < 4)
- p->guard_interval = fe_gi[reg[1]];
-
- if (reg[2] < 4)
- p->hierarchy = fe_hi[reg[2]];
-
- state->priority = reg[5];
-
- p->code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE;
- p->code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE;
-
- /* Update internal state to reflect the autodetected props */
- state->constellation = p->modulation;
- state->transmission_mode = p->transmission_mode;
-
- return 0;
-}
-
-static int it913x_fe_set_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- struct it913x_fe_state *state = fe->demodulator_priv;
- int i;
- u8 empty_ch, last_ch;
-
- state->it913x_status = 0;
-
- /* Set bw*/
- it913x_fe_select_bw(state, p->bandwidth_hz,
- state->adcFrequency);
-
- /* Training Mode Off */
- it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0);
-
- /* Clear Empty Channel */
- it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0);
-
- /* Clear bits */
- it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0);
- /* LED on */
- it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
- /* Select Band*/
- if ((p->frequency >= 51000000) && (p->frequency <= 230000000))
- i = 0;
- else if ((p->frequency >= 350000000) && (p->frequency <= 900000000))
- i = 1;
- else if ((p->frequency >= 1450000000) && (p->frequency <= 1680000000))
- i = 2;
- else
- return -EOPNOTSUPP;
-
- it913x_write_reg(state, PRO_DMOD, FREE_BAND, i);
-
- deb_info("Frontend Set Tuner Type %02x", state->tuner_type);
- switch (state->tuner_type) {
- case IT9135_38:
- case IT9135_51:
- case IT9135_52:
- case IT9135_60:
- case IT9135_61:
- case IT9135_62:
- it9137_set_tuner(state,
- p->bandwidth_hz, p->frequency);
- break;
- default:
- if (fe->ops.tuner_ops.set_params) {
- fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
- break;
- }
- /* LED off */
- it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
- /* Trigger ofsm */
- it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
- last_ch = 2;
- for (i = 0; i < 40; ++i) {
- empty_ch = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS);
- if (last_ch == 1 && empty_ch == 1)
- break;
- if (last_ch == 2 && empty_ch == 2)
- return 0;
- last_ch = empty_ch;
- msleep(25);
- }
- for (i = 0; i < 40; ++i) {
- if (it913x_read_reg_u8(state, D_TPSD_LOCK) == 1)
- break;
- msleep(25);
- }
-
- state->frequency = p->frequency;
- return 0;
-}
-
-static int it913x_fe_suspend(struct it913x_fe_state *state)
-{
- int ret, i;
- u8 b;
-
- ret = it913x_write_reg(state, PRO_DMOD, SUSPEND_FLAG, 0x1);
-
- ret |= it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
-
- for (i = 0; i < 128; i++) {
- ret = it913x_read_reg(state, SUSPEND_FLAG, &b, 1);
- if (ret < 0)
- return -ENODEV;
- if (b == 0)
- break;
-
- }
-
- ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x8);
- /* Turn LED off */
- ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
-
- ret |= it913x_fe_script_loader(state, it9137_tuner_off);
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-/* Power sequence */
-/* Power Up Tuner on -> Frontend suspend off -> Tuner clk on */
-/* Power Down Frontend suspend on -> Tuner clk off -> Tuner off */
-
-static int it913x_fe_sleep(struct dvb_frontend *fe)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- return it913x_fe_suspend(state);
-}
-
-static u32 compute_div(u32 a, u32 b, u32 x)
-{
- u32 res = 0;
- u32 c = 0;
- u32 i = 0;
-
- if (a > b) {
- c = a / b;
- a = a - c * b;
- }
-
- for (i = 0; i < x; i++) {
- if (a >= b) {
- res += 1;
- a -= b;
- }
- a <<= 1;
- res <<= 1;
- }
-
- res = (c << x) + res;
-
- return res;
-}
-
-static int it913x_fe_start(struct it913x_fe_state *state)
-{
- struct it913xset *set_lna;
- struct it913xset *set_mode;
- int ret;
- u8 adf = (state->config->adf & 0xf);
- u32 adc, xtal;
- u8 b[4];
-
- if (state->config->chip_ver == 1)
- ret = it913x_init_tuner(state);
-
- info("ADF table value :%02x", adf);
-
- if (adf < 10) {
- state->crystalFrequency = fe_clockTable[adf].xtal ;
- state->table = fe_clockTable[adf].table;
- state->adcFrequency = state->table->adcFrequency;
-
- adc = compute_div(state->adcFrequency, 1000000ul, 19ul);
- xtal = compute_div(state->crystalFrequency, 1000000ul, 19ul);
-
- } else
- return -EINVAL;
-
- /* Set LED indicator on GPIOH3 */
- ret = it913x_write_reg(state, PRO_LINK, GPIOH3_EN, 0x1);
- ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_ON, 0x1);
- ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
-
- ret |= it913x_write_reg(state, PRO_LINK, 0xf641, state->tuner_type);
- ret |= it913x_write_reg(state, PRO_DMOD, 0xf5ca, 0x01);
- ret |= it913x_write_reg(state, PRO_DMOD, 0xf715, 0x01);
-
- b[0] = xtal & 0xff;
- b[1] = (xtal >> 8) & 0xff;
- b[2] = (xtal >> 16) & 0xff;
- b[3] = (xtal >> 24);
- ret |= it913x_write(state, PRO_DMOD, XTAL_CLK, b , 4);
-
- b[0] = adc & 0xff;
- b[1] = (adc >> 8) & 0xff;
- b[2] = (adc >> 16) & 0xff;
- ret |= it913x_write(state, PRO_DMOD, ADC_FREQ, b, 3);
-
- if (state->config->adc_x2)
- ret |= it913x_write_reg(state, PRO_DMOD, ADC_X_2, 0x01);
- b[0] = 0;
- b[1] = 0;
- b[2] = 0;
- ret |= it913x_write(state, PRO_DMOD, 0x0029, b, 3);
-
- info("Crystal Frequency :%d Adc Frequency :%d ADC X2: %02x",
- state->crystalFrequency, state->adcFrequency,
- state->config->adc_x2);
- deb_info("Xtal value :%04x Adc value :%04x", xtal, adc);
-
- if (ret < 0)
- return -ENODEV;
-
- /* v1 or v2 tuner script */
- if (state->config->chip_ver > 1)
- ret = it913x_fe_script_loader(state, it9135_v2);
- else
- ret = it913x_fe_script_loader(state, it9135_v1);
- if (ret < 0)
- return ret;
-
- /* LNA Scripts */
- switch (state->tuner_type) {
- case IT9135_51:
- set_lna = it9135_51;
- break;
- case IT9135_52:
- set_lna = it9135_52;
- break;
- case IT9135_60:
- set_lna = it9135_60;
- break;
- case IT9135_61:
- set_lna = it9135_61;
- break;
- case IT9135_62:
- set_lna = it9135_62;
- break;
- case IT9135_38:
- default:
- set_lna = it9135_38;
- }
- info("Tuner LNA type :%02x", state->tuner_type);
-
- ret = it913x_fe_script_loader(state, set_lna);
- if (ret < 0)
- return ret;
-
- if (state->config->chip_ver == 2) {
- ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x1);
- ret |= it913x_write_reg(state, PRO_LINK, PADODPU, 0x0);
- ret |= it913x_write_reg(state, PRO_LINK, AGC_O_D, 0x0);
- ret |= it913x_init_tuner(state);
- }
- if (ret < 0)
- return -ENODEV;
-
- /* Always solo frontend */
- set_mode = set_solo_fe;
- ret |= it913x_fe_script_loader(state, set_mode);
-
- ret |= it913x_fe_suspend(state);
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int it913x_fe_init(struct dvb_frontend *fe)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- int ret = 0;
- /* Power Up Tuner - common all versions */
- ret = it913x_write_reg(state, PRO_DMOD, 0xec40, 0x1);
-
- ret |= it913x_fe_script_loader(state, init_1);
-
- ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x0);
-
- ret |= it913x_write_reg(state, PRO_DMOD, 0xfba8, 0x0);
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static void it913x_fe_release(struct dvb_frontend *fe)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- kfree(state);
-}
-
-static struct dvb_frontend_ops it913x_fe_ofdm_ops;
-
-struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap,
- u8 i2c_addr, struct ite_config *config)
-{
- struct it913x_fe_state *state = NULL;
- int ret;
-
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct it913x_fe_state), GFP_KERNEL);
- if (state == NULL)
- return NULL;
- if (config == NULL)
- goto error;
-
- state->i2c_adap = i2c_adap;
- state->i2c_addr = i2c_addr;
- state->config = config;
-
- switch (state->config->tuner_id_0) {
- case IT9135_51:
- case IT9135_52:
- case IT9135_60:
- case IT9135_61:
- case IT9135_62:
- state->tuner_type = state->config->tuner_id_0;
- break;
- default:
- case IT9135_38:
- state->tuner_type = IT9135_38;
- }
-
- ret = it913x_fe_start(state);
- if (ret < 0)
- goto error;
-
-
- /* create dvb_frontend */
- memcpy(&state->frontend.ops, &it913x_fe_ofdm_ops,
- sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
-
- return &state->frontend;
-error:
- kfree(state);
- return NULL;
-}
-EXPORT_SYMBOL(it913x_fe_attach);
-
-static struct dvb_frontend_ops it913x_fe_ofdm_ops = {
- .delsys = { SYS_DVBT },
- .info = {
- .name = "it913x-fe DVB-T",
- .frequency_min = 51000000,
- .frequency_max = 1680000000,
- .frequency_stepsize = 62500,
- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
- FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
- FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
- FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO,
- },
-
- .release = it913x_fe_release,
-
- .init = it913x_fe_init,
- .sleep = it913x_fe_sleep,
-
- .set_frontend = it913x_fe_set_frontend,
- .get_frontend = it913x_fe_get_frontend,
-
- .read_status = it913x_fe_read_status,
- .read_signal_strength = it913x_fe_read_signal_strength,
- .read_snr = it913x_fe_read_snr,
- .read_ber = it913x_fe_read_ber,
- .read_ucblocks = it913x_fe_read_ucblocks,
-};
-
-MODULE_DESCRIPTION("it913x Frontend and it9137 tuner");
-MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
-MODULE_VERSION("1.15");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/it913x-fe.h b/drivers/media/dvb-frontends/it913x-fe.h
deleted file mode 100644
index df0ad4207343..000000000000
--- a/drivers/media/dvb-frontends/it913x-fe.h
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Driver for it913x Frontend
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
- */
-
-#ifndef IT913X_FE_H
-#define IT913X_FE_H
-
-#include <linux/kconfig.h>
-#include <linux/dvb/frontend.h>
-#include "dvb_frontend.h"
-
-struct ite_config {
- u8 chip_ver;
- u16 chip_type;
- u32 firmware;
- u8 firmware_ver;
- u8 adc_x2;
- u8 tuner_id_0;
- u8 tuner_id_1;
- u8 dual_mode;
- u8 adf;
- /* option to read SIGNAL_LEVEL */
- u8 read_slevel;
-};
-
-#if IS_ENABLED(CONFIG_DVB_IT913X_FE)
-extern struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap,
- u8 i2c_addr, struct ite_config *config);
-#else
-static inline struct dvb_frontend *it913x_fe_attach(
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr, struct ite_config *config)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif /* CONFIG_IT913X_FE */
-#define I2C_BASE_ADDR 0x10
-#define DEV_0 0x0
-#define DEV_1 0x10
-#define PRO_LINK 0x0
-#define PRO_DMOD 0x1
-#define DEV_0_DMOD (PRO_DMOD << 0x7)
-#define DEV_1_DMOD (DEV_0_DMOD | DEV_1)
-#define CHIP2_I2C_ADDR 0x3a
-
-#define AFE_MEM0 0xfb24
-
-#define MP2_SW_RST 0xf99d
-#define MP2IF2_SW_RST 0xf9a4
-
-#define PADODPU 0xd827
-#define THIRDODPU 0xd828
-#define AGC_O_D 0xd829
-
-#define EP0_TX_EN 0xdd11
-#define EP0_TX_NAK 0xdd13
-#define EP4_TX_LEN_LSB 0xdd88
-#define EP4_TX_LEN_MSB 0xdd89
-#define EP4_MAX_PKT 0xdd0c
-#define EP5_TX_LEN_LSB 0xdd8a
-#define EP5_TX_LEN_MSB 0xdd8b
-#define EP5_MAX_PKT 0xdd0d
-
-#define IO_MUX_POWER_CLK 0xd800
-#define CLK_O_EN 0xd81a
-#define I2C_CLK 0xf103
-#define I2C_CLK_100 0x7
-#define I2C_CLK_400 0x1a
-
-#define D_TPSD_LOCK 0xf5a9
-#define MP2IF2_EN 0xf9a3
-#define MP2IF_SERIAL 0xf985
-#define TSIS_ENABLE 0xf9cd
-#define MP2IF2_HALF_PSB 0xf9a5
-#define MP2IF_STOP_EN 0xf9b5
-#define MPEG_FULL_SPEED 0xf990
-#define TOP_HOSTB_SER_MODE 0xd91c
-
-#define PID_RST 0xf992
-#define PID_EN 0xf993
-#define PID_INX_EN 0xf994
-#define PID_INX 0xf995
-#define PID_LSB 0xf996
-#define PID_MSB 0xf997
-
-#define MP2IF_MPEG_PAR_MODE 0xf986
-#define DCA_UPPER_CHIP 0xf731
-#define DCA_LOWER_CHIP 0xf732
-#define DCA_PLATCH 0xf730
-#define DCA_FPGA_LATCH 0xf778
-#define DCA_STAND_ALONE 0xf73c
-#define DCA_ENABLE 0xf776
-
-#define DVBT_INTEN 0xf41f
-#define DVBT_ENABLE 0xf41a
-#define HOSTB_DCA_LOWER 0xd91f
-#define HOSTB_MPEG_PAR_MODE 0xd91b
-#define HOSTB_MPEG_SER_MODE 0xd91c
-#define HOSTB_MPEG_SER_DO7 0xd91d
-#define HOSTB_DCA_UPPER 0xd91e
-#define PADMISCDR2 0xd830
-#define PADMISCDR4 0xd831
-#define PADMISCDR8 0xd832
-#define PADMISCDRSR 0xd833
-#define LOCK3_OUT 0xd8fd
-
-#define GPIOH1_O 0xd8af
-#define GPIOH1_EN 0xd8b0
-#define GPIOH1_ON 0xd8b1
-#define GPIOH3_O 0xd8b3
-#define GPIOH3_EN 0xd8b4
-#define GPIOH3_ON 0xd8b5
-#define GPIOH5_O 0xd8bb
-#define GPIOH5_EN 0xd8bc
-#define GPIOH5_ON 0xd8bd
-
-#define AFE_MEM0 0xfb24
-
-#define REG_TPSD_TX_MODE 0xf900
-#define REG_TPSD_GI 0xf901
-#define REG_TPSD_HIER 0xf902
-#define REG_TPSD_CONST 0xf903
-#define REG_BW 0xf904
-#define REG_PRIV 0xf905
-#define REG_TPSD_HP_CODE 0xf906
-#define REG_TPSD_LP_CODE 0xf907
-
-#define MP2IF_SYNC_LK 0xf999
-#define ADC_FREQ 0xf1cd
-
-#define TRIGGER_OFSM 0x0000
-/* COEFF Registers start at 0x0001 to 0x0020 */
-#define COEFF_1_2048 0x0001
-#define XTAL_CLK 0x0025
-#define BFS_FCW 0x0029
-
-/* Error Regs */
-#define RSD_ABORT_PKT_LSB 0x0032
-#define RSD_ABORT_PKT_MSB 0x0033
-#define RSD_BIT_ERR_0_7 0x0034
-#define RSD_BIT_ERR_8_15 0x0035
-#define RSD_BIT_ERR_23_16 0x0036
-#define RSD_BIT_COUNT_LSB 0x0037
-#define RSD_BIT_COUNT_MSB 0x0038
-
-#define TPSD_LOCK 0x003c
-#define TRAINING_MODE 0x0040
-#define ADC_X_2 0x0045
-#define TUNER_ID 0x0046
-#define EMPTY_CHANNEL_STATUS 0x0047
-#define SIGNAL_LEVEL 0x0048
-#define SIGNAL_QUALITY 0x0049
-#define EST_SIGNAL_LEVEL 0x004a
-#define FREE_BAND 0x004b
-#define SUSPEND_FLAG 0x004c
-#define VAR_P_INBAND 0x00f7
-
-/* Build in tuner types */
-#define IT9137 0x38
-#define IT9135_38 0x38
-#define IT9135_51 0x51
-#define IT9135_52 0x52
-#define IT9135_60 0x60
-#define IT9135_61 0x61
-#define IT9135_62 0x62
-
-enum {
- CMD_DEMOD_READ = 0,
- CMD_DEMOD_WRITE,
- CMD_TUNER_READ,
- CMD_TUNER_WRITE,
- CMD_REG_EEPROM_READ,
- CMD_REG_EEPROM_WRITE,
- CMD_DATA_READ,
- CMD_VAR_READ = 8,
- CMD_VAR_WRITE,
- CMD_PLATFORM_GET,
- CMD_PLATFORM_SET,
- CMD_IP_CACHE,
- CMD_IP_ADD,
- CMD_IP_REMOVE,
- CMD_PID_ADD,
- CMD_PID_REMOVE,
- CMD_SIPSI_GET,
- CMD_SIPSI_MPE_RESET,
- CMD_H_PID_ADD = 0x15,
- CMD_H_PID_REMOVE,
- CMD_ABORT,
- CMD_IR_GET,
- CMD_IR_SET,
- CMD_FW_DOWNLOAD = 0x21,
- CMD_QUERYINFO,
- CMD_BOOT,
- CMD_FW_DOWNLOAD_BEGIN,
- CMD_FW_DOWNLOAD_END,
- CMD_RUN_CODE,
- CMD_SCATTER_READ = 0x28,
- CMD_SCATTER_WRITE,
- CMD_GENERIC_READ,
- CMD_GENERIC_WRITE
-};
-
-enum {
- READ_LONG,
- WRITE_LONG,
- READ_SHORT,
- WRITE_SHORT,
- READ_DATA,
- WRITE_DATA,
- WRITE_CMD,
-};
-
-enum {
- IT9135_AUTO = 0,
- IT9137_FW,
- IT9135_V1_FW,
- IT9135_V2_FW,
-};
-
-#endif /* IT913X_FE_H */
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index b8a7897e7bd8..2ef8ce13fb60 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -271,6 +271,13 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency);
if (ret)
goto err;
+ } else {
+ /*
+ * Use nominal target frequency as tuner driver does not provide
+ * actual frequency used. Carrier offset calculation is not
+ * valid.
+ */
+ tuner_frequency = c->frequency;
}
/* reset */
@@ -428,18 +435,10 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
goto err;
switch (target_mclk) {
- case 72000:
- u8tmp1 = 0x00; /* 0b00 */
- u8tmp2 = 0x03; /* 0b11 */
- break;
case 96000:
u8tmp1 = 0x02; /* 0b10 */
u8tmp2 = 0x01; /* 0b01 */
break;
- case 115200:
- u8tmp1 = 0x01; /* 0b01 */
- u8tmp2 = 0x01; /* 0b01 */
- break;
case 144000:
u8tmp1 = 0x00; /* 0b00 */
u8tmp2 = 0x01; /* 0b01 */
@@ -448,10 +447,6 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
u8tmp1 = 0x03; /* 0b11 */
u8tmp2 = 0x00; /* 0b00 */
break;
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid target_mclk\n", __func__);
- ret = -EINVAL;
- goto err;
}
ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0);
@@ -711,9 +706,6 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe)
case 1:
c->inversion = INVERSION_ON;
break;
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n",
- __func__);
}
switch ((buf[1] >> 5) & 0x07) {
@@ -793,9 +785,6 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe)
case 1:
c->pilot = PILOT_ON;
break;
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n",
- __func__);
}
switch ((buf[0] >> 6) & 0x07) {
@@ -823,9 +812,6 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe)
case 1:
c->inversion = INVERSION_ON;
break;
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n",
- __func__);
}
switch ((buf[2] >> 0) & 0x03) {
@@ -958,7 +944,7 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe,
switch (fe_sec_tone_mode) {
case SEC_TONE_ON:
tone = 0;
- reg_a1_mask = 0x87;
+ reg_a1_mask = 0x47;
break;
case SEC_TONE_OFF:
tone = 1;
diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c
index b2351466b0da..32cffca14d0b 100644
--- a/drivers/media/dvb-frontends/m88rs2000.c
+++ b/drivers/media/dvb-frontends/m88rs2000.c
@@ -715,6 +715,22 @@ static int m88rs2000_get_frontend(struct dvb_frontend *fe)
return 0;
}
+static int m88rs2000_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->symbol_rate > 3000000)
+ tune->min_delay_ms = 2000;
+ else
+ tune->min_delay_ms = 3000;
+
+ tune->step_size = c->symbol_rate / 16000;
+ tune->max_drift = c->symbol_rate / 2000;
+
+ return 0;
+}
+
static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct m88rs2000_state *state = fe->demodulator_priv;
@@ -746,7 +762,7 @@ static struct dvb_frontend_ops m88rs2000_ops = {
.symbol_rate_tolerance = 500, /* ppm */
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
- FE_CAN_QPSK |
+ FE_CAN_QPSK | FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_AUTO
},
@@ -766,6 +782,7 @@ static struct dvb_frontend_ops m88rs2000_ops = {
.set_frontend = m88rs2000_set_frontend,
.get_frontend = m88rs2000_get_frontend,
+ .get_tune_settings = m88rs2000_get_tune_settings,
};
struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config,
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index 2c7217fb1415..2f458bb188c7 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -1,7 +1,7 @@
/*
* Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver
*
- * Copyright (C) 2010-2013 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010-2013 Mauro Carvalho Chehab
* Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com>
*
* This program is free software; you can redistribute it and/or
@@ -2156,5 +2156,5 @@ static struct dvb_frontend_ops mb86a20s_ops = {
};
MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/mb86a20s.h b/drivers/media/dvb-frontends/mb86a20s.h
index 6627a3976087..cbeb941fba7c 100644
--- a/drivers/media/dvb-frontends/mb86a20s.h
+++ b/drivers/media/dvb-frontends/mb86a20s.h
@@ -1,7 +1,7 @@
/*
* Fujitsu mb86a20s driver
*
- * Copyright (C) 2010 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 Mauro Carvalho Chehab
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c
index ff73da9365e3..fdbed35c87fa 100644
--- a/drivers/media/dvb-frontends/rtl2832.c
+++ b/drivers/media/dvb-frontends/rtl2832.c
@@ -24,11 +24,6 @@
/* Max transfer size done by I2C transfer functions */
#define MAX_XFER_SIZE 64
-
-int rtl2832_debug;
-module_param_named(debug, rtl2832_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
#define REG_MASK(b) (BIT(b + 1) - 1)
static const struct rtl2832_reg_entry registers[] = {
@@ -185,12 +180,13 @@ static int rtl2832_wr(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
buf[0] = reg;
memcpy(&buf[1], val, len);
- ret = i2c_transfer(priv->i2c, msg, 1);
+ ret = i2c_transfer(priv->i2c_adapter, msg, 1);
if (ret == 1) {
ret = 0;
} else {
- dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
- "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c wr failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
@@ -214,12 +210,13 @@ static int rtl2832_rd(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
}
};
- ret = i2c_transfer(priv->i2c, msg, 2);
+ ret = i2c_transfer(priv->i2c_adapter, msg, 2);
if (ret == 2) {
ret = 0;
} else {
- dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
- "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c rd failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
@@ -417,7 +414,7 @@ static int rtl2832_set_if(struct dvb_frontend *fe, u32 if_freq)
ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq);
- return (ret);
+ return ret;
}
static int rtl2832_init(struct dvb_frontend *fe)
@@ -514,15 +511,10 @@ static int rtl2832_init(struct dvb_frontend *fe)
goto err;
}
- if (!fe->ops.tuner_ops.get_if_frequency) {
- ret = rtl2832_set_if(fe, priv->cfg.if_dvbt);
- if (ret)
- goto err;
- }
-
/*
* r820t NIM code does a software reset here at the demod -
- * may not be needed, as there's already a software reset at set_params()
+ * may not be needed, as there's already a software reset at
+ * set_params()
*/
#if 1
/* soft reset */
@@ -599,9 +591,9 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe)
};
- dev_dbg(&priv->i2c->dev, "%s: frequency=%d bandwidth_hz=%d " \
- "inversion=%d\n", __func__, c->frequency,
- c->bandwidth_hz, c->inversion);
+ dev_dbg(&priv->i2c->dev,
+ "%s: frequency=%d bandwidth_hz=%d inversion=%d\n",
+ __func__, c->frequency, c->bandwidth_hz, c->inversion);
/* program tuner */
if (fe->ops.tuner_ops.set_params)
@@ -899,9 +891,149 @@ static void rtl2832_release(struct dvb_frontend *fe)
struct rtl2832_priv *priv = fe->demodulator_priv;
dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+ cancel_delayed_work_sync(&priv->i2c_gate_work);
+ i2c_del_mux_adapter(priv->i2c_adapter_tuner);
+ i2c_del_mux_adapter(priv->i2c_adapter);
kfree(priv);
}
+/*
+ * Delay mechanism to avoid unneeded I2C gate open / close. Gate close is
+ * delayed here a little bit in order to see if there is sequence of I2C
+ * messages sent to same I2C bus.
+ * We must use unlocked version of __i2c_transfer() in order to avoid deadlock
+ * as lock is already taken by calling muxed i2c_transfer().
+ */
+static void rtl2832_i2c_gate_work(struct work_struct *work)
+{
+ struct rtl2832_priv *priv = container_of(work,
+ struct rtl2832_priv, i2c_gate_work.work);
+ struct i2c_adapter *adap = priv->i2c;
+ int ret;
+ u8 buf[2];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ /* select reg bank 1 */
+ buf[0] = 0x00;
+ buf[1] = 0x01;
+ ret = __i2c_transfer(adap, msg, 1);
+ if (ret != 1)
+ goto err;
+
+ priv->page = 1;
+
+ /* close I2C repeater gate */
+ buf[0] = 0x01;
+ buf[1] = 0x10;
+ ret = __i2c_transfer(adap, msg, 1);
+ if (ret != 1)
+ goto err;
+
+ priv->i2c_gate_state = 0;
+
+ return;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return;
+}
+
+static int rtl2832_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id)
+{
+ struct rtl2832_priv *priv = mux_priv;
+ int ret;
+ u8 buf[2], val;
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+ struct i2c_msg msg_rd[2] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = "\x01",
+ }, {
+ .addr = priv->cfg.i2c_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = &val,
+ }
+ };
+
+ /* terminate possible gate closing */
+ cancel_delayed_work_sync(&priv->i2c_gate_work);
+
+ if (priv->i2c_gate_state == chan_id)
+ return 0;
+
+ /* select reg bank 1 */
+ buf[0] = 0x00;
+ buf[1] = 0x01;
+ ret = __i2c_transfer(adap, msg, 1);
+ if (ret != 1)
+ goto err;
+
+ priv->page = 1;
+
+ /* we must read that register, otherwise there will be errors */
+ ret = __i2c_transfer(adap, msg_rd, 2);
+ if (ret != 2)
+ goto err;
+
+ /* open or close I2C repeater gate */
+ buf[0] = 0x01;
+ if (chan_id == 1)
+ buf[1] = 0x18; /* open */
+ else
+ buf[1] = 0x10; /* close */
+
+ ret = __i2c_transfer(adap, msg, 1);
+ if (ret != 1)
+ goto err;
+
+ priv->i2c_gate_state = chan_id;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return -EREMOTEIO;
+}
+
+static int rtl2832_deselect(struct i2c_adapter *adap, void *mux_priv,
+ u32 chan_id)
+{
+ struct rtl2832_priv *priv = mux_priv;
+ schedule_delayed_work(&priv->i2c_gate_work, usecs_to_jiffies(100));
+ return 0;
+}
+
+struct i2c_adapter *rtl2832_get_i2c_adapter(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ return priv->i2c_adapter_tuner;
+}
+EXPORT_SYMBOL(rtl2832_get_i2c_adapter);
+
+struct i2c_adapter *rtl2832_get_private_i2c_adapter(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ return priv->i2c_adapter;
+}
+EXPORT_SYMBOL(rtl2832_get_private_i2c_adapter);
+
struct dvb_frontend *rtl2832_attach(const struct rtl2832_config *cfg,
struct i2c_adapter *i2c)
{
@@ -920,12 +1052,25 @@ struct dvb_frontend *rtl2832_attach(const struct rtl2832_config *cfg,
priv->i2c = i2c;
priv->tuner = cfg->tuner;
memcpy(&priv->cfg, cfg, sizeof(struct rtl2832_config));
+ INIT_DELAYED_WORK(&priv->i2c_gate_work, rtl2832_i2c_gate_work);
+
+ /* create muxed i2c adapter for demod itself */
+ priv->i2c_adapter = i2c_add_mux_adapter(i2c, &i2c->dev, priv, 0, 0, 0,
+ rtl2832_select, NULL);
+ if (priv->i2c_adapter == NULL)
+ goto err;
/* check if the demod is there */
ret = rtl2832_rd_reg(priv, 0x00, 0x0, &tmp);
if (ret)
goto err;
+ /* create muxed i2c adapter for demod tuner bus */
+ priv->i2c_adapter_tuner = i2c_add_mux_adapter(i2c, &i2c->dev, priv,
+ 0, 1, 0, rtl2832_select, rtl2832_deselect);
+ if (priv->i2c_adapter_tuner == NULL)
+ goto err;
+
/* create dvb_frontend */
memcpy(&priv->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops));
priv->fe.demodulator_priv = priv;
@@ -936,6 +1081,8 @@ struct dvb_frontend *rtl2832_attach(const struct rtl2832_config *cfg,
return &priv->fe;
err:
dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+ if (priv && priv->i2c_adapter)
+ i2c_del_mux_adapter(priv->i2c_adapter);
kfree(priv);
return NULL;
}
diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h
index 2cfbb6a97061..cb3b6b0775b8 100644
--- a/drivers/media/dvb-frontends/rtl2832.h
+++ b/drivers/media/dvb-frontends/rtl2832.h
@@ -38,13 +38,6 @@ struct rtl2832_config {
u32 xtal;
/*
- * IFs for all used modes.
- * Hz
- * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000
- */
- u32 if_dvbt;
-
- /*
* tuner
* XXX: This must be keep sync with dvb_usb_rtl28xxu demod driver.
*/
@@ -58,11 +51,21 @@ struct rtl2832_config {
};
#if IS_ENABLED(CONFIG_DVB_RTL2832)
-extern struct dvb_frontend *rtl2832_attach(
+struct dvb_frontend *rtl2832_attach(
const struct rtl2832_config *cfg,
struct i2c_adapter *i2c
);
+
+extern struct i2c_adapter *rtl2832_get_i2c_adapter(
+ struct dvb_frontend *fe
+);
+
+extern struct i2c_adapter *rtl2832_get_private_i2c_adapter(
+ struct dvb_frontend *fe
+);
+
#else
+
static inline struct dvb_frontend *rtl2832_attach(
const struct rtl2832_config *config,
struct i2c_adapter *i2c
@@ -71,6 +74,21 @@ static inline struct dvb_frontend *rtl2832_attach(
pr_warn("%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
+
+static inline struct i2c_adapter *rtl2832_get_i2c_adapter(
+ struct dvb_frontend *fe
+)
+{
+ return NULL;
+}
+
+static inline struct i2c_adapter *rtl2832_get_private_i2c_adapter(
+ struct dvb_frontend *fe
+)
+{
+ return NULL;
+}
+
#endif
diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h
index b5f2b80092ee..ae469f032fe6 100644
--- a/drivers/media/dvb-frontends/rtl2832_priv.h
+++ b/drivers/media/dvb-frontends/rtl2832_priv.h
@@ -23,9 +23,12 @@
#include "dvb_frontend.h"
#include "rtl2832.h"
+#include <linux/i2c-mux.h>
struct rtl2832_priv {
struct i2c_adapter *i2c;
+ struct i2c_adapter *i2c_adapter;
+ struct i2c_adapter *i2c_adapter_tuner;
struct dvb_frontend fe;
struct rtl2832_config cfg;
@@ -34,6 +37,7 @@ struct rtl2832_priv {
u8 tuner;
u8 page; /* active register page */
+ struct delayed_work i2c_gate_work;
};
struct rtl2832_reg_entry {
@@ -267,7 +271,7 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_tua9001[] = {
{DVBT_OPT_ADC_IQ, 0x1},
{DVBT_AD_AVI, 0x0},
{DVBT_AD_AVQ, 0x0},
- {DVBT_SPEC_INV, 0x0},
+ {DVBT_SPEC_INV, 0x0},
};
static const struct rtl2832_reg_value rtl2832_tuner_init_fc0012[] = {
@@ -301,7 +305,7 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_fc0012[] = {
{DVBT_GI_PGA_STATE, 0x0},
{DVBT_EN_AGC_PGA, 0x1},
{DVBT_IF_AGC_MAN, 0x0},
- {DVBT_SPEC_INV, 0x0},
+ {DVBT_SPEC_INV, 0x0},
};
static const struct rtl2832_reg_value rtl2832_tuner_init_e4000[] = {
@@ -339,32 +343,32 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_e4000[] = {
{DVBT_REG_MONSEL, 0x1},
{DVBT_REG_MON, 0x1},
{DVBT_REG_4MSEL, 0x0},
- {DVBT_SPEC_INV, 0x0},
+ {DVBT_SPEC_INV, 0x0},
};
static const struct rtl2832_reg_value rtl2832_tuner_init_r820t[] = {
- {DVBT_DAGC_TRG_VAL, 0x39},
- {DVBT_AGC_TARG_VAL_0, 0x0},
- {DVBT_AGC_TARG_VAL_8_1, 0x40},
- {DVBT_AAGC_LOOP_GAIN, 0x16},
- {DVBT_LOOP_GAIN2_3_0, 0x8},
- {DVBT_LOOP_GAIN2_4, 0x1},
- {DVBT_LOOP_GAIN3, 0x18},
- {DVBT_VTOP1, 0x35},
- {DVBT_VTOP2, 0x21},
- {DVBT_VTOP3, 0x21},
- {DVBT_KRF1, 0x0},
- {DVBT_KRF2, 0x40},
- {DVBT_KRF3, 0x10},
- {DVBT_KRF4, 0x10},
- {DVBT_IF_AGC_MIN, 0x80},
- {DVBT_IF_AGC_MAX, 0x7f},
- {DVBT_RF_AGC_MIN, 0x80},
- {DVBT_RF_AGC_MAX, 0x7f},
- {DVBT_POLAR_RF_AGC, 0x0},
- {DVBT_POLAR_IF_AGC, 0x0},
- {DVBT_AD7_SETTING, 0xe9f4},
- {DVBT_SPEC_INV, 0x1},
+ {DVBT_DAGC_TRG_VAL, 0x39},
+ {DVBT_AGC_TARG_VAL_0, 0x0},
+ {DVBT_AGC_TARG_VAL_8_1, 0x40},
+ {DVBT_AAGC_LOOP_GAIN, 0x16},
+ {DVBT_LOOP_GAIN2_3_0, 0x8},
+ {DVBT_LOOP_GAIN2_4, 0x1},
+ {DVBT_LOOP_GAIN3, 0x18},
+ {DVBT_VTOP1, 0x35},
+ {DVBT_VTOP2, 0x21},
+ {DVBT_VTOP3, 0x21},
+ {DVBT_KRF1, 0x0},
+ {DVBT_KRF2, 0x40},
+ {DVBT_KRF3, 0x10},
+ {DVBT_KRF4, 0x10},
+ {DVBT_IF_AGC_MIN, 0x80},
+ {DVBT_IF_AGC_MAX, 0x7f},
+ {DVBT_RF_AGC_MIN, 0x80},
+ {DVBT_RF_AGC_MAX, 0x7f},
+ {DVBT_POLAR_RF_AGC, 0x0},
+ {DVBT_POLAR_IF_AGC, 0x0},
+ {DVBT_AD7_SETTING, 0xe9f4},
+ {DVBT_SPEC_INV, 0x1},
};
#endif /* RTL2832_PRIV_H */
diff --git a/drivers/media/dvb-frontends/s921.c b/drivers/media/dvb-frontends/s921.c
index a271ac3eaec0..69862e1fd9e9 100644
--- a/drivers/media/dvb-frontends/s921.c
+++ b/drivers/media/dvb-frontends/s921.c
@@ -2,7 +2,7 @@
* Sharp VA3A5JZ921 One Seg Broadcast Module driver
* This device is labeled as just S. 921 at the top of the frontend can
*
- * Copyright (C) 2009-2010 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2009-2010 Mauro Carvalho Chehab
* Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com>
*
* Developed for Leadership SBTVD 1seg device sold in Brazil
@@ -539,6 +539,6 @@ static struct dvb_frontend_ops s921_ops = {
};
MODULE_DESCRIPTION("DVB Frontend module for Sharp S921 hardware");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_AUTHOR("Douglas Landgraf <dougsland@redhat.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/s921.h b/drivers/media/dvb-frontends/s921.h
index 8d5e2a6e187c..9b20c9e0eb88 100644
--- a/drivers/media/dvb-frontends/s921.h
+++ b/drivers/media/dvb-frontends/s921.h
@@ -1,7 +1,7 @@
/*
* Sharp s921 driver
*
- * Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2009 Mauro Carvalho Chehab
* Copyright (C) 2009 Douglas Landgraf <dougsland@redhat.com>
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/media/dvb-frontends/stb6100.c b/drivers/media/dvb-frontends/stb6100.c
index cea175d19890..4ef8a5c7003e 100644
--- a/drivers/media/dvb-frontends/stb6100.c
+++ b/drivers/media/dvb-frontends/stb6100.c
@@ -193,7 +193,7 @@ static int stb6100_write_reg_range(struct stb6100_state *state, u8 buf[], int st
.len = len + 1
};
- if (1 + len > sizeof(buf)) {
+ if (1 + len > sizeof(cmdbuf)) {
printk(KERN_WARNING
"%s: i2c wr: len=%d is too big!\n",
KBUILD_MODNAME, len);
diff --git a/drivers/media/dvb-frontends/stv0900_sw.c b/drivers/media/dvb-frontends/stv0900_sw.c
index 0a40edfad739..4ce1d260b3eb 100644
--- a/drivers/media/dvb-frontends/stv0900_sw.c
+++ b/drivers/media/dvb-frontends/stv0900_sw.c
@@ -1081,7 +1081,7 @@ static int stv0900_wait_for_lock(struct stv0900_internal *intp,
lock = stv0900_get_demod_lock(intp, demod, dmd_timeout);
if (lock)
- lock = lock && stv0900_get_fec_lock(intp, demod, fec_timeout);
+ lock = stv0900_get_fec_lock(intp, demod, fec_timeout);
if (lock) {
lock = 0;
diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c
index 8ad3a57cf640..522fe00f5eee 100644
--- a/drivers/media/dvb-frontends/tda10071.c
+++ b/drivers/media/dvb-frontends/tda10071.c
@@ -42,8 +42,8 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
if (1 + len > sizeof(buf)) {
dev_warn(&priv->i2c->dev,
- "%s: i2c wr reg=%04x: len=%d is too big!\n",
- KBUILD_MODNAME, reg, len);
+ "%s: i2c wr reg=%04x: len=%d is too big!\n",
+ KBUILD_MODNAME, reg, len);
return -EINVAL;
}
@@ -54,8 +54,9 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
if (ret == 1) {
ret = 0;
} else {
- dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
- "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c wr failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
@@ -83,8 +84,8 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
if (len > sizeof(buf)) {
dev_warn(&priv->i2c->dev,
- "%s: i2c wr reg=%04x: len=%d is too big!\n",
- KBUILD_MODNAME, reg, len);
+ "%s: i2c wr reg=%04x: len=%d is too big!\n",
+ KBUILD_MODNAME, reg, len);
return -EINVAL;
}
@@ -93,8 +94,9 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
memcpy(val, buf, len);
ret = 0;
} else {
- dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
- "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c rd failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
@@ -491,10 +493,9 @@ static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status)
if (ret)
goto error;
- if (tmp & 0x01) /* tuner PLL */
- *status |= FE_HAS_SIGNAL;
+ /* 0x39[0] tuner PLL */
if (tmp & 0x02) /* demod PLL */
- *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
if (tmp & 0x04) /* viterbi or LDPC*/
*status |= FE_HAS_VITERBI;
if (tmp & 0x08) /* RS or BCH */
@@ -668,11 +669,11 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
int ret, i;
u8 mode, rolloff, pilot, inversion, div;
- dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d modulation=%d " \
- "frequency=%d symbol_rate=%d inversion=%d pilot=%d " \
- "rolloff=%d\n", __func__, c->delivery_system, c->modulation,
- c->frequency, c->symbol_rate, c->inversion, c->pilot,
- c->rolloff);
+ dev_dbg(&priv->i2c->dev,
+ "%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n",
+ __func__, c->delivery_system, c->modulation,
+ c->frequency, c->symbol_rate, c->inversion, c->pilot,
+ c->rolloff);
priv->delivery_system = SYS_UNDEFINED;
@@ -952,10 +953,8 @@ static int tda10071_init(struct dvb_frontend *fe)
/* request the firmware, this will block and timeout */
ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
if (ret) {
- dev_err(&priv->i2c->dev, "%s: did not find the " \
- "firmware file. (%s) Please see " \
- "linux/Documentation/dvb/ for more " \
- "details on firmware-problems. (%d)\n",
+ dev_err(&priv->i2c->dev,
+ "%s: did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)\n",
KBUILD_MODNAME, fw_file, ret);
goto error;
}
@@ -985,11 +984,12 @@ static int tda10071_init(struct dvb_frontend *fe)
if (ret)
goto error_release_firmware;
- dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state, " \
- "will try to load a firmware\n", KBUILD_MODNAME,
- tda10071_ops.info.name);
- dev_info(&priv->i2c->dev, "%s: downloading firmware from " \
- "file '%s'\n", KBUILD_MODNAME, fw_file);
+ dev_info(&priv->i2c->dev,
+ "%s: found a '%s' in cold state, will try to load a firmware\n",
+ KBUILD_MODNAME, tda10071_ops.info.name);
+ dev_info(&priv->i2c->dev,
+ "%s: downloading firmware from file '%s'\n",
+ KBUILD_MODNAME, fw_file);
/* do not download last byte */
fw_size = fw->size - 1;
@@ -1003,11 +1003,10 @@ static int tda10071_init(struct dvb_frontend *fe)
ret = tda10071_wr_regs(priv, 0xfa,
(u8 *) &fw->data[fw_size - remaining], len);
if (ret) {
- dev_err(&priv->i2c->dev, "%s: firmware " \
- "download failed=%d\n",
+ dev_err(&priv->i2c->dev,
+ "%s: firmware download failed=%d\n",
KBUILD_MODNAME, ret);
- if (ret)
- goto error_release_firmware;
+ goto error_release_firmware;
}
}
release_firmware(fw);
@@ -1069,12 +1068,17 @@ static int tda10071_init(struct dvb_frontend *fe)
if (ret)
goto error;
+ if (priv->cfg.tuner_i2c_addr)
+ tmp = priv->cfg.tuner_i2c_addr;
+ else
+ tmp = 0x14;
+
cmd.args[0] = CMD_TUNER_INIT;
cmd.args[1] = 0x00;
cmd.args[2] = 0x00;
cmd.args[3] = 0x00;
cmd.args[4] = 0x00;
- cmd.args[5] = (priv->cfg.tuner_i2c_addr) ? priv->cfg.tuner_i2c_addr : 0x14;
+ cmd.args[5] = tmp;
cmd.args[6] = 0x00;
cmd.args[7] = 0x03;
cmd.args[8] = 0x02;
@@ -1214,14 +1218,14 @@ struct dvb_frontend *tda10071_attach(const struct tda10071_config *config,
/* make sure demod i2c address is specified */
if (!config->demod_i2c_addr) {
- dev_dbg(&i2c->dev, "%s: invalid demod i2c address!\n", __func__);
+ dev_dbg(&i2c->dev, "%s: invalid demod i2c address\n", __func__);
ret = -EINVAL;
goto error;
}
/* make sure tuner i2c address is specified */
if (!config->tuner_i2c_addr) {
- dev_dbg(&i2c->dev, "%s: invalid tuner i2c address!\n", __func__);
+ dev_dbg(&i2c->dev, "%s: invalid tuner i2c address\n", __func__);
ret = -EINVAL;
goto error;
}
diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h
index f9542f68fe78..331b5a819383 100644
--- a/drivers/media/dvb-frontends/tda10071.h
+++ b/drivers/media/dvb-frontends/tda10071.h
@@ -79,7 +79,7 @@ extern struct dvb_frontend *tda10071_attach(
static inline struct dvb_frontend *tda10071_attach(
const struct tda10071_config *config, struct i2c_adapter *i2c)
{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
#endif
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 4aa9c5311cc5..441053be7f55 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -196,7 +196,7 @@ config VIDEO_ADV7183
config VIDEO_ADV7604
tristate "Analog Devices ADV7604 decoder"
- depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
---help---
Support for the Analog Devices ADV7604 video decoder.
@@ -208,7 +208,7 @@ config VIDEO_ADV7604
config VIDEO_ADV7842
tristate "Analog Devices ADV7842 decoder"
- depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
---help---
Support for the Analog Devices ADV7842 video decoder.
@@ -431,7 +431,7 @@ config VIDEO_ADV7393
config VIDEO_ADV7511
tristate "Analog Devices ADV7511 encoder"
- depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
---help---
Support for the Analog Devices ADV7511 video encoder.
@@ -579,6 +579,14 @@ config VIDEO_S5K6AA
This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
camera sensor with an embedded SoC image signal processor.
+config VIDEO_S5K6A3
+ tristate "Samsung S5K6A3 sensor support"
+ depends on MEDIA_CAMERA_SUPPORT
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a V4L2 sensor-level driver for Samsung S5K6A3 raw
+ camera sensor.
+
config VIDEO_S5K4ECGX
tristate "Samsung S5K4ECGX sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -629,6 +637,15 @@ config VIDEO_LM3560
This is a driver for the lm3560 dual flash controllers. It controls
flash, torch LEDs.
+config VIDEO_LM3646
+ tristate "LM3646 dual flash driver support"
+ depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+ depends on MEDIA_CAMERA_SUPPORT
+ select REGMAP_I2C
+ ---help---
+ This is a driver for the lm3646 dual flash controllers. It controls
+ flash, torch LEDs.
+
comment "Video improvement chips"
config VIDEO_UPD64031A
@@ -659,6 +676,7 @@ comment "Audio/Video compression chips"
config VIDEO_SAA6752HS
tristate "Philips SAA6752HS MPEG-2 Audio/Video Encoder"
depends on VIDEO_V4L2 && I2C
+ select CRC32
---help---
Support for the Philips SAA6752HS MPEG-2 video and MPEG-audio/AC-3
audio encoder with multiplexer.
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 48888ae876fb..01ae9328e582 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -66,12 +66,14 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
+obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o
obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o
obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o
obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/
obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o
obj-$(CONFIG_VIDEO_LM3560) += lm3560.o
+obj-$(CONFIG_VIDEO_LM3646) += lm3646.o
obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o
obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c
index 83225d6a0dd9..1b7ecfd88673 100644
--- a/drivers/media/i2c/ad9389b.c
+++ b/drivers/media/i2c/ad9389b.c
@@ -573,7 +573,7 @@ static const struct v4l2_subdev_core_ops ad9389b_core_ops = {
/* ------------------------------ PAD OPS ------------------------------ */
-static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
{
struct ad9389b_state *state = get_ad9389b_state(sd);
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index d7d99f1c69e4..5e638b159452 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -123,11 +123,11 @@
struct adv7180_state {
struct v4l2_ctrl_handler ctrl_hdl;
struct v4l2_subdev sd;
- struct work_struct work;
struct mutex mutex; /* mutual excl. when accessing chip */
int irq;
v4l2_std_id curr_norm;
bool autodetect;
+ bool powered;
u8 input;
};
#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
@@ -312,6 +312,37 @@ out:
return ret;
}
+static int adv7180_set_power(struct adv7180_state *state,
+ struct i2c_client *client, bool on)
+{
+ u8 val;
+
+ if (on)
+ val = ADV7180_PWR_MAN_ON;
+ else
+ val = ADV7180_PWR_MAN_OFF;
+
+ return i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, val);
+}
+
+static int adv7180_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct adv7180_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ ret = mutex_lock_interruptible(&state->mutex);
+ if (ret)
+ return ret;
+
+ ret = adv7180_set_power(state, client, on);
+ if (ret == 0)
+ state->powered = on;
+
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
@@ -442,6 +473,7 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
.s_std = adv7180_s_std,
+ .s_power = adv7180_s_power,
};
static const struct v4l2_subdev_ops adv7180_ops = {
@@ -449,10 +481,9 @@ static const struct v4l2_subdev_ops adv7180_ops = {
.video = &adv7180_video_ops,
};
-static void adv7180_work(struct work_struct *work)
+static irqreturn_t adv7180_irq(int irq, void *devid)
{
- struct adv7180_state *state = container_of(work, struct adv7180_state,
- work);
+ struct adv7180_state *state = devid;
struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
u8 isr3;
@@ -468,17 +499,6 @@ static void adv7180_work(struct work_struct *work)
__adv7180_status(client, NULL, &state->curr_norm);
mutex_unlock(&state->mutex);
- enable_irq(state->irq);
-}
-
-static irqreturn_t adv7180_irq(int irq, void *devid)
-{
- struct adv7180_state *state = devid;
-
- schedule_work(&state->work);
-
- disable_irq_nosync(state->irq);
-
return IRQ_HANDLED;
}
@@ -533,48 +553,52 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)
/* register for interrupts */
if (state->irq > 0) {
- ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME,
- state);
+ ret = request_threaded_irq(state->irq, NULL, adv7180_irq,
+ IRQF_ONESHOT, KBUILD_MODNAME, state);
if (ret)
return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
ADV7180_ADI_CTRL_IRQ_SPACE);
if (ret < 0)
- return ret;
+ goto err;
/* config the Interrupt pin to be active low */
ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
ADV7180_ICONF1_ACTIVE_LOW |
ADV7180_ICONF1_PSYNC_ONLY);
if (ret < 0)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
if (ret < 0)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
if (ret < 0)
- return ret;
+ goto err;
/* enable AD change interrupts interrupts */
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
ADV7180_IRQ3_AD_CHANGE);
if (ret < 0)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
if (ret < 0)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
0);
if (ret < 0)
- return ret;
+ goto err;
}
return 0;
+
+err:
+ free_irq(state->irq, state);
+ return ret;
}
static int adv7180_probe(struct i2c_client *client,
@@ -598,9 +622,9 @@ static int adv7180_probe(struct i2c_client *client,
}
state->irq = client->irq;
- INIT_WORK(&state->work, adv7180_work);
mutex_init(&state->mutex);
state->autodetect = true;
+ state->powered = true;
state->input = 0;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
@@ -611,15 +635,21 @@ static int adv7180_probe(struct i2c_client *client,
ret = init_device(client, state);
if (ret)
goto err_free_ctrl;
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret)
+ goto err_free_irq;
+
return 0;
+err_free_irq:
+ if (state->irq > 0)
+ free_irq(client->irq, state);
err_free_ctrl:
adv7180_exit_controls(state);
err_unreg_subdev:
mutex_destroy(&state->mutex);
- v4l2_device_unregister_subdev(sd);
err:
- printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret);
return ret;
}
@@ -628,20 +658,14 @@ static int adv7180_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct adv7180_state *state = to_state(sd);
- if (state->irq > 0) {
+ v4l2_async_unregister_subdev(sd);
+
+ if (state->irq > 0)
free_irq(client->irq, state);
- if (cancel_work_sync(&state->work)) {
- /*
- * Work was pending, therefore we need to enable
- * IRQ here to balance the disable_irq() done in the
- * interrupt handler.
- */
- enable_irq(state->irq);
- }
- }
- mutex_destroy(&state->mutex);
v4l2_device_unregister_subdev(sd);
+ adv7180_exit_controls(state);
+ mutex_destroy(&state->mutex);
return 0;
}
@@ -654,13 +678,10 @@ static const struct i2c_device_id adv7180_id[] = {
static int adv7180_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
- int ret;
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7180_state *state = to_state(sd);
- ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG,
- ADV7180_PWR_MAN_OFF);
- if (ret < 0)
- return ret;
- return 0;
+ return adv7180_set_power(state, client, false);
}
static int adv7180_resume(struct device *dev)
@@ -670,10 +691,11 @@ static int adv7180_resume(struct device *dev)
struct adv7180_state *state = to_state(sd);
int ret;
- ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG,
- ADV7180_PWR_MAN_ON);
- if (ret < 0)
- return ret;
+ if (state->powered) {
+ ret = adv7180_set_power(state, client, true);
+ if (ret)
+ return ret;
+ }
ret = init_device(client, state);
if (ret < 0)
return ret;
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index ee618942cb8e..942ca4b99297 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -597,7 +597,7 @@ static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
return 0;
}
-static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
{
struct adv7511_state *state = get_adv7511_state(sd);
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 71c8570bd9ea..98cc5407f1b1 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -1658,7 +1658,7 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
return 0;
}
-static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
{
struct adv7604_state *state = to_state(sd);
u8 *data = NULL;
@@ -1728,7 +1728,7 @@ static int get_edid_spa_location(const u8 *edid)
return -1;
}
-static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
{
struct adv7604_state *state = to_state(sd);
int spa_loc;
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index 9bbd6656fb8f..636ac08925f6 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -546,6 +546,14 @@ static void main_reset(struct v4l2_subdev *sd)
/* ----------------------------------------------------------------------- */
+static inline bool is_analog_input(struct v4l2_subdev *sd)
+{
+ struct adv7842_state *state = to_state(sd);
+
+ return ((state->mode == ADV7842_MODE_RGB) ||
+ (state->mode == ADV7842_MODE_COMP));
+}
+
static inline bool is_digital_input(struct v4l2_subdev *sd)
{
struct adv7842_state *state = to_state(sd);
@@ -1027,12 +1035,72 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,
cp_write(sd, 0xac, (height & 0x0f) << 4);
}
+static void adv7842_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c)
+{
+ struct adv7842_state *state = to_state(sd);
+ u8 offset_buf[4];
+
+ if (auto_offset) {
+ offset_a = 0x3ff;
+ offset_b = 0x3ff;
+ offset_c = 0x3ff;
+ }
+
+ v4l2_dbg(2, debug, sd, "%s: %s offset: a = 0x%x, b = 0x%x, c = 0x%x\n",
+ __func__, auto_offset ? "Auto" : "Manual",
+ offset_a, offset_b, offset_c);
+
+ offset_buf[0]= (cp_read(sd, 0x77) & 0xc0) | ((offset_a & 0x3f0) >> 4);
+ offset_buf[1] = ((offset_a & 0x00f) << 4) | ((offset_b & 0x3c0) >> 6);
+ offset_buf[2] = ((offset_b & 0x03f) << 2) | ((offset_c & 0x300) >> 8);
+ offset_buf[3] = offset_c & 0x0ff;
+
+ /* Registers must be written in this order with no i2c access in between */
+ if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x77, 4, offset_buf))
+ v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__);
+}
+
+static void adv7842_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c)
+{
+ struct adv7842_state *state = to_state(sd);
+ u8 gain_buf[4];
+ u8 gain_man = 1;
+ u8 agc_mode_man = 1;
+
+ if (auto_gain) {
+ gain_man = 0;
+ agc_mode_man = 0;
+ gain_a = 0x100;
+ gain_b = 0x100;
+ gain_c = 0x100;
+ }
+
+ v4l2_dbg(2, debug, sd, "%s: %s gain: a = 0x%x, b = 0x%x, c = 0x%x\n",
+ __func__, auto_gain ? "Auto" : "Manual",
+ gain_a, gain_b, gain_c);
+
+ gain_buf[0] = ((gain_man << 7) | (agc_mode_man << 6) | ((gain_a & 0x3f0) >> 4));
+ gain_buf[1] = (((gain_a & 0x00f) << 4) | ((gain_b & 0x3c0) >> 6));
+ gain_buf[2] = (((gain_b & 0x03f) << 2) | ((gain_c & 0x300) >> 8));
+ gain_buf[3] = ((gain_c & 0x0ff));
+
+ /* Registers must be written in this order with no i2c access in between */
+ if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x73, 4, gain_buf))
+ v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__);
+}
+
static void set_rgb_quantization_range(struct v4l2_subdev *sd)
{
struct adv7842_state *state = to_state(sd);
+ bool rgb_output = io_read(sd, 0x02) & 0x02;
+ bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80;
+
+ v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n",
+ __func__, state->rgb_quantization_range,
+ rgb_output, hdmi_signal);
- v4l2_dbg(2, debug, sd, "%s: rgb_quantization_range = %d\n",
- __func__, state->rgb_quantization_range);
+ adv7842_set_gain(sd, true, 0x0, 0x0, 0x0);
+ adv7842_set_offset(sd, true, 0x0, 0x0, 0x0);
switch (state->rgb_quantization_range) {
case V4L2_DV_RGB_RANGE_AUTO:
@@ -1050,7 +1118,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
break;
}
- if (hdmi_read(sd, 0x05) & 0x80) {
+ if (hdmi_signal) {
/* Receiving HDMI signal
* Set automode */
io_write_and_or(sd, 0x02, 0x0f, 0xf0);
@@ -1066,24 +1134,45 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
} else {
/* RGB full range (0-255) */
io_write_and_or(sd, 0x02, 0x0f, 0x10);
+
+ if (is_digital_input(sd) && rgb_output) {
+ adv7842_set_offset(sd, false, 0x40, 0x40, 0x40);
+ } else {
+ adv7842_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
+ adv7842_set_offset(sd, false, 0x70, 0x70, 0x70);
+ }
}
break;
case V4L2_DV_RGB_RANGE_LIMITED:
if (state->mode == ADV7842_MODE_COMP) {
/* YCrCb limited range (16-235) */
io_write_and_or(sd, 0x02, 0x0f, 0x20);
- } else {
- /* RGB limited range (16-235) */
- io_write_and_or(sd, 0x02, 0x0f, 0x00);
+ break;
}
+
+ /* RGB limited range (16-235) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x00);
+
break;
case V4L2_DV_RGB_RANGE_FULL:
if (state->mode == ADV7842_MODE_COMP) {
/* YCrCb full range (0-255) */
io_write_and_or(sd, 0x02, 0x0f, 0x60);
+ break;
+ }
+
+ /* RGB full range (0-255) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x10);
+
+ if (is_analog_input(sd) || hdmi_signal)
+ break;
+
+ /* Adjust gain/offset for DVI-D signals only */
+ if (rgb_output) {
+ adv7842_set_offset(sd, false, 0x40, 0x40, 0x40);
} else {
- /* RGB full range (0-255) */
- io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ adv7842_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
+ adv7842_set_offset(sd, false, 0x70, 0x70, 0x70);
}
break;
}
@@ -1360,12 +1449,11 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08);
bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a);
- freq = (hdmi_read(sd, 0x06) * 1000000) +
- ((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000;
-
+ freq = ((hdmi_read(sd, 0x51) << 1) + (hdmi_read(sd, 0x52) >> 7)) * 1000000;
+ freq += ((hdmi_read(sd, 0x52) & 0x7f) * 7813);
if (is_hdmi(sd)) {
/* adjust for deep color mode */
- freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0) >> 5) + 8);
+ freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0) >> 6) * 2 + 8);
}
bt->pixelclock = freq;
bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 +
@@ -1717,8 +1805,8 @@ static void select_input(struct v4l2_subdev *sd,
* (rev. 2.5, June 2010)" p. 17. */
afe_write(sd, 0x12, 0xfb); /* ADC noise shaping filter controls */
afe_write(sd, 0x0c, 0x0d); /* CP core gain controls */
- cp_write(sd, 0x3e, 0x80); /* CP core pre-gain control,
- enable color control */
+ cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */
+
/* CP coast control */
cp_write(sd, 0xc3, 0x33); /* Component mode */
@@ -1926,7 +2014,7 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
return 0;
}
-static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
{
struct adv7842_state *state = to_state(sd);
u8 *data = NULL;
@@ -1966,7 +2054,7 @@ static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi
return 0;
}
-static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e)
+static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
{
struct adv7842_state *state = to_state(sd);
int err = 0;
@@ -2103,7 +2191,8 @@ static void print_avi_infoframe(struct v4l2_subdev *sd)
{
int i;
uint8_t buf[14];
- uint8_t avi_inf_len;
+ u8 avi_len;
+ u8 avi_ver;
struct avi_info_frame avi;
if (!(hdmi_read(sd, 0x05) & 0x80)) {
@@ -2116,18 +2205,20 @@ static void print_avi_infoframe(struct v4l2_subdev *sd)
}
if (io_read(sd, 0x88) & 0x10) {
- /* Note: the ADV7842 calculated incorrect checksums for InfoFrames
- with a length of 14 or 15. See the ADV7842 Register Settings
- Recommendations document for more details. */
- v4l2_info(sd, "AVI infoframe checksum error\n");
- return;
+ v4l2_info(sd, "AVI infoframe checksum error has occurred earlier\n");
+ io_write(sd, 0x8a, 0x10); /* clear AVI_INF_CKS_ERR_RAW */
+ if (io_read(sd, 0x88) & 0x10) {
+ v4l2_info(sd, "AVI infoframe checksum error still present\n");
+ io_write(sd, 0x8a, 0x10); /* clear AVI_INF_CKS_ERR_RAW */
+ }
}
- avi_inf_len = infoframe_read(sd, 0xe2);
+ avi_len = infoframe_read(sd, 0xe2);
+ avi_ver = infoframe_read(sd, 0xe1);
v4l2_info(sd, "AVI infoframe version %d (%d byte)\n",
- infoframe_read(sd, 0xe1), avi_inf_len);
+ avi_ver, avi_len);
- if (infoframe_read(sd, 0xe1) != 0x02)
+ if (avi_ver != 0x02)
return;
for (i = 0; i < 14; i++)
@@ -2602,9 +2693,15 @@ static int adv7842_core_init(struct v4l2_subdev *sd)
/* disable I2C access to internal EDID ram from HDMI DDC ports */
rep_write_and_or(sd, 0x77, 0xf3, 0x00);
- hdmi_write(sd, 0x69, 0xa3); /* HPA manual */
- /* HPA disable on port A and B */
- io_write_and_or(sd, 0x20, 0xcf, 0x00);
+ if (pdata->hpa_auto) {
+ /* HPA auto, HPA 0.5s after Edid set and Cable detect */
+ hdmi_write(sd, 0x69, 0x5c);
+ } else {
+ /* HPA manual */
+ hdmi_write(sd, 0x69, 0xa3);
+ /* HPA disable on port A and B */
+ io_write_and_or(sd, 0x20, 0xcf, 0x00);
+ }
/* LLC */
io_write(sd, 0x19, 0x80 | pdata->llc_dll_phase);
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 99ee456700f4..c8fe1358ec9e 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -431,8 +431,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
* Initialize the other fields of rc_dev
*/
rc->map_name = ir->ir_codes;
- rc->allowed_protos = rc_type;
- rc->enabled_protocols = rc_type;
+ rc_set_allowed_protocols(rc, rc_type);
+ rc_set_enabled_protocols(rc, rc_type);
if (!rc->driver_name)
rc->driver_name = MODULE_NAME;
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
index d98ca3aebe23..c23de593c17d 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -15,12 +15,6 @@
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
#include <linux/delay.h>
@@ -42,7 +36,7 @@
#define REG_FLAG 0xd0
#define REG_CONFIG1 0xe0
-/* Fault Mask */
+/* fault mask */
#define FAULT_TIMEOUT (1<<0)
#define FAULT_OVERTEMP (1<<1)
#define FAULT_SHORT_CIRCUIT (1<<2)
@@ -53,7 +47,8 @@ enum led_enable {
MODE_FLASH = 0x3,
};
-/* struct lm3560_flash
+/**
+ * struct lm3560_flash
*
* @pdata: platform data
* @regmap: reg. map for i2c
@@ -98,7 +93,7 @@ static int lm3560_mode_ctrl(struct lm3560_flash *flash)
return rval;
}
-/* led1/2 enable/disable */
+/* led1/2 enable/disable */
static int lm3560_enable_ctrl(struct lm3560_flash *flash,
enum lm3560_led_id led_no, bool on)
{
@@ -168,7 +163,7 @@ static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash,
return rval;
}
-/* V4L2 controls */
+/* v4l2 controls */
static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
{
struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no);
@@ -297,6 +292,7 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no];
v4l2_ctrl_handler_init(hdl, 8);
+
/* flash mode */
v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,
V4L2_FLASH_LED_MODE_TORCH, ~0x7,
@@ -309,6 +305,7 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
/* flash strobe */
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+
/* flash strobe stop */
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
@@ -395,7 +392,7 @@ static int lm3560_init_device(struct lm3560_flash *flash)
rval = lm3560_mode_ctrl(flash);
if (rval < 0)
return rval;
- /* Reset faults */
+ /* reset faults */
rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
return rval;
}
@@ -419,8 +416,7 @@ static int lm3560_probe(struct i2c_client *client,
/* if there is no platform data, use chip default value */
if (pdata == NULL) {
- pdata =
- kzalloc(sizeof(struct lm3560_platform_data), GFP_KERNEL);
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (pdata == NULL)
return -ENODEV;
pdata->peak = LM3560_PEAK_3600mA;
diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c
new file mode 100644
index 000000000000..626fb4679c02
--- /dev/null
+++ b/drivers/media/i2c/lm3646.c
@@ -0,0 +1,414 @@
+/*
+ * drivers/media/i2c/lm3646.c
+ * General device driver for TI lm3646, Dual FLASH LED Driver
+ *
+ * Copyright (C) 2014 Texas Instruments
+ *
+ * Contact: Daniel Jeong <gshark.jeong@gmail.com>
+ * Ldd-Mlp <ldd-mlp@list.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/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/videodev2.h>
+#include <media/lm3646.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+/* registers definitions */
+#define REG_ENABLE 0x01
+#define REG_TORCH_BR 0x05
+#define REG_FLASH_BR 0x05
+#define REG_FLASH_TOUT 0x04
+#define REG_FLAG 0x08
+#define REG_STROBE_SRC 0x06
+#define REG_LED1_FLASH_BR 0x06
+#define REG_LED1_TORCH_BR 0x07
+
+#define MASK_ENABLE 0x03
+#define MASK_TORCH_BR 0x70
+#define MASK_FLASH_BR 0x0F
+#define MASK_FLASH_TOUT 0x07
+#define MASK_FLAG 0xFF
+#define MASK_STROBE_SRC 0x80
+
+/* Fault Mask */
+#define FAULT_TIMEOUT (1<<0)
+#define FAULT_SHORT_CIRCUIT (1<<1)
+#define FAULT_UVLO (1<<2)
+#define FAULT_IVFM (1<<3)
+#define FAULT_OCP (1<<4)
+#define FAULT_OVERTEMP (1<<5)
+#define FAULT_NTC_TRIP (1<<6)
+#define FAULT_OVP (1<<7)
+
+enum led_mode {
+ MODE_SHDN = 0x0,
+ MODE_TORCH = 0x2,
+ MODE_FLASH = 0x3,
+};
+
+/*
+ * struct lm3646_flash
+ *
+ * @pdata: platform data
+ * @regmap: reg. map for i2c
+ * @lock: muxtex for serial access.
+ * @led_mode: V4L2 LED mode
+ * @ctrls_led: V4L2 contols
+ * @subdev_led: V4L2 subdev
+ * @mode_reg : mode register value
+ */
+struct lm3646_flash {
+ struct device *dev;
+ struct lm3646_platform_data *pdata;
+ struct regmap *regmap;
+
+ struct v4l2_ctrl_handler ctrls_led;
+ struct v4l2_subdev subdev_led;
+
+ u8 mode_reg;
+};
+
+#define to_lm3646_flash(_ctrl) \
+ container_of(_ctrl->handler, struct lm3646_flash, ctrls_led)
+
+/* enable mode control */
+static int lm3646_mode_ctrl(struct lm3646_flash *flash,
+ enum v4l2_flash_led_mode led_mode)
+{
+ switch (led_mode) {
+ case V4L2_FLASH_LED_MODE_NONE:
+ return regmap_write(flash->regmap,
+ REG_ENABLE, flash->mode_reg | MODE_SHDN);
+ case V4L2_FLASH_LED_MODE_TORCH:
+ return regmap_write(flash->regmap,
+ REG_ENABLE, flash->mode_reg | MODE_TORCH);
+ case V4L2_FLASH_LED_MODE_FLASH:
+ return regmap_write(flash->regmap,
+ REG_ENABLE, flash->mode_reg | MODE_FLASH);
+ }
+ return -EINVAL;
+}
+
+/* V4L2 controls */
+static int lm3646_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct lm3646_flash *flash = to_lm3646_flash(ctrl);
+ unsigned int reg_val;
+ int rval;
+
+ if (ctrl->id != V4L2_CID_FLASH_FAULT)
+ return -EINVAL;
+
+ rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
+ if (rval < 0)
+ return rval;
+
+ ctrl->val = 0;
+ if (reg_val & FAULT_TIMEOUT)
+ ctrl->val |= V4L2_FLASH_FAULT_TIMEOUT;
+ if (reg_val & FAULT_SHORT_CIRCUIT)
+ ctrl->val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
+ if (reg_val & FAULT_UVLO)
+ ctrl->val |= V4L2_FLASH_FAULT_UNDER_VOLTAGE;
+ if (reg_val & FAULT_IVFM)
+ ctrl->val |= V4L2_FLASH_FAULT_INPUT_VOLTAGE;
+ if (reg_val & FAULT_OCP)
+ ctrl->val |= V4L2_FLASH_FAULT_OVER_CURRENT;
+ if (reg_val & FAULT_OVERTEMP)
+ ctrl->val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
+ if (reg_val & FAULT_NTC_TRIP)
+ ctrl->val |= V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE;
+ if (reg_val & FAULT_OVP)
+ ctrl->val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
+
+ return 0;
+}
+
+static int lm3646_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct lm3646_flash *flash = to_lm3646_flash(ctrl);
+ unsigned int reg_val;
+ int rval = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_FLASH_LED_MODE:
+
+ if (ctrl->val != V4L2_FLASH_LED_MODE_FLASH)
+ return lm3646_mode_ctrl(flash, ctrl->val);
+ /* switch to SHDN mode before flash strobe on */
+ return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE);
+
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ return regmap_update_bits(flash->regmap,
+ REG_STROBE_SRC, MASK_STROBE_SRC,
+ (ctrl->val) << 7);
+
+ case V4L2_CID_FLASH_STROBE:
+
+ /* read and check current mode of chip to start flash */
+ rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
+ if (rval < 0 || ((reg_val & MASK_ENABLE) != MODE_SHDN))
+ return rval;
+ /* flash on */
+ return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_FLASH);
+
+ case V4L2_CID_FLASH_STROBE_STOP:
+
+ /*
+ * flash mode will be turned automatically
+ * from FLASH mode to SHDN mode after flash duration timeout
+ * read and check current mode of chip to stop flash
+ */
+ rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
+ if (rval < 0)
+ return rval;
+ if ((reg_val & MASK_ENABLE) == MODE_FLASH)
+ return lm3646_mode_ctrl(flash,
+ V4L2_FLASH_LED_MODE_NONE);
+ return rval;
+
+ case V4L2_CID_FLASH_TIMEOUT:
+ return regmap_update_bits(flash->regmap,
+ REG_FLASH_TOUT, MASK_FLASH_TOUT,
+ LM3646_FLASH_TOUT_ms_TO_REG
+ (ctrl->val));
+
+ case V4L2_CID_FLASH_INTENSITY:
+ return regmap_update_bits(flash->regmap,
+ REG_FLASH_BR, MASK_FLASH_BR,
+ LM3646_TOTAL_FLASH_BRT_uA_TO_REG
+ (ctrl->val));
+
+ case V4L2_CID_FLASH_TORCH_INTENSITY:
+ return regmap_update_bits(flash->regmap,
+ REG_TORCH_BR, MASK_TORCH_BR,
+ LM3646_TOTAL_TORCH_BRT_uA_TO_REG
+ (ctrl->val) << 4);
+ }
+
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops lm3646_led_ctrl_ops = {
+ .g_volatile_ctrl = lm3646_get_ctrl,
+ .s_ctrl = lm3646_set_ctrl,
+};
+
+static int lm3646_init_controls(struct lm3646_flash *flash)
+{
+ struct v4l2_ctrl *fault;
+ struct v4l2_ctrl_handler *hdl = &flash->ctrls_led;
+ const struct v4l2_ctrl_ops *ops = &lm3646_led_ctrl_ops;
+
+ v4l2_ctrl_handler_init(hdl, 8);
+ /* flash mode */
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,
+ V4L2_FLASH_LED_MODE_TORCH, ~0x7,
+ V4L2_FLASH_LED_MODE_NONE);
+
+ /* flash source */
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_STROBE_SOURCE,
+ 0x1, ~0x3, V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
+
+ /* flash strobe */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+ /* flash strobe stop */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
+
+ /* flash strobe timeout */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT,
+ LM3646_FLASH_TOUT_MIN,
+ LM3646_FLASH_TOUT_MAX,
+ LM3646_FLASH_TOUT_STEP, flash->pdata->flash_timeout);
+
+ /* max flash current */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY,
+ LM3646_TOTAL_FLASH_BRT_MIN,
+ LM3646_TOTAL_FLASH_BRT_MAX,
+ LM3646_TOTAL_FLASH_BRT_STEP,
+ LM3646_TOTAL_FLASH_BRT_MAX);
+
+ /* max torch current */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY,
+ LM3646_TOTAL_TORCH_BRT_MIN,
+ LM3646_TOTAL_TORCH_BRT_MAX,
+ LM3646_TOTAL_TORCH_BRT_STEP,
+ LM3646_TOTAL_TORCH_BRT_MAX);
+
+ /* fault */
+ fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0,
+ V4L2_FLASH_FAULT_OVER_VOLTAGE
+ | V4L2_FLASH_FAULT_OVER_TEMPERATURE
+ | V4L2_FLASH_FAULT_SHORT_CIRCUIT
+ | V4L2_FLASH_FAULT_TIMEOUT, 0, 0);
+ if (fault != NULL)
+ fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ if (hdl->error)
+ return hdl->error;
+
+ flash->subdev_led.ctrl_handler = hdl;
+ return 0;
+}
+
+/* initialize device */
+static const struct v4l2_subdev_ops lm3646_ops = {
+ .core = NULL,
+};
+
+static const struct regmap_config lm3646_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xFF,
+};
+
+static int lm3646_subdev_init(struct lm3646_flash *flash)
+{
+ struct i2c_client *client = to_i2c_client(flash->dev);
+ int rval;
+
+ v4l2_i2c_subdev_init(&flash->subdev_led, client, &lm3646_ops);
+ flash->subdev_led.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ strcpy(flash->subdev_led.name, LM3646_NAME);
+ rval = lm3646_init_controls(flash);
+ if (rval)
+ goto err_out;
+ rval = media_entity_init(&flash->subdev_led.entity, 0, NULL, 0);
+ if (rval < 0)
+ goto err_out;
+ flash->subdev_led.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+ return rval;
+
+err_out:
+ v4l2_ctrl_handler_free(&flash->ctrls_led);
+ return rval;
+}
+
+static int lm3646_init_device(struct lm3646_flash *flash)
+{
+ unsigned int reg_val;
+ int rval;
+
+ /* read the value of mode register to reduce redundant i2c accesses */
+ rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
+ if (rval < 0)
+ return rval;
+ flash->mode_reg = reg_val & 0xfc;
+
+ /* output disable */
+ rval = lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE);
+ if (rval < 0)
+ return rval;
+
+ /*
+ * LED1 flash current setting
+ * LED2 flash current = Total(Max) flash current - LED1 flash current
+ */
+ rval = regmap_update_bits(flash->regmap,
+ REG_LED1_FLASH_BR, 0x7F,
+ LM3646_LED1_FLASH_BRT_uA_TO_REG
+ (flash->pdata->led1_flash_brt));
+
+ if (rval < 0)
+ return rval;
+
+ /*
+ * LED1 torch current setting
+ * LED2 torch current = Total(Max) torch current - LED1 torch current
+ */
+ rval = regmap_update_bits(flash->regmap,
+ REG_LED1_TORCH_BR, 0x7F,
+ LM3646_LED1_TORCH_BRT_uA_TO_REG
+ (flash->pdata->led1_torch_brt));
+ if (rval < 0)
+ return rval;
+
+ /* Reset flag register */
+ return regmap_read(flash->regmap, REG_FLAG, &reg_val);
+}
+
+static int lm3646_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct lm3646_flash *flash;
+ struct lm3646_platform_data *pdata = dev_get_platdata(&client->dev);
+ int rval;
+
+ flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
+ if (flash == NULL)
+ return -ENOMEM;
+
+ flash->regmap = devm_regmap_init_i2c(client, &lm3646_regmap);
+ if (IS_ERR(flash->regmap))
+ return PTR_ERR(flash->regmap);
+
+ /* check device tree if there is no platform data */
+ if (pdata == NULL) {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct lm3646_platform_data),
+ GFP_KERNEL);
+ if (pdata == NULL)
+ return -ENOMEM;
+ /* use default data in case of no platform data */
+ pdata->flash_timeout = LM3646_FLASH_TOUT_MAX;
+ pdata->led1_torch_brt = LM3646_LED1_TORCH_BRT_MAX;
+ pdata->led1_flash_brt = LM3646_LED1_FLASH_BRT_MAX;
+ }
+ flash->pdata = pdata;
+ flash->dev = &client->dev;
+
+ rval = lm3646_subdev_init(flash);
+ if (rval < 0)
+ return rval;
+
+ rval = lm3646_init_device(flash);
+ if (rval < 0)
+ return rval;
+
+ i2c_set_clientdata(client, flash);
+
+ return 0;
+}
+
+static int lm3646_remove(struct i2c_client *client)
+{
+ struct lm3646_flash *flash = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(&flash->subdev_led);
+ v4l2_ctrl_handler_free(&flash->ctrls_led);
+ media_entity_cleanup(&flash->subdev_led.entity);
+
+ return 0;
+}
+
+static const struct i2c_device_id lm3646_id_table[] = {
+ {LM3646_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lm3646_id_table);
+
+static struct i2c_driver lm3646_i2c_driver = {
+ .driver = {
+ .name = LM3646_NAME,
+ },
+ .probe = lm3646_probe,
+ .remove = lm3646_remove,
+ .id_table = lm3646_id_table,
+};
+
+module_i2c_driver(lm3646_i2c_driver);
+
+MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
+MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
+MODULE_DESCRIPTION("Texas Instruments LM3646 Dual Flash LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index 192c4aad05d6..33daace81297 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -78,6 +78,9 @@
#define MT9P031_PLL_CONFIG_1 0x11
#define MT9P031_PLL_CONFIG_2 0x12
#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a
+#define MT9P031_PIXEL_CLOCK_INVERT (1 << 15)
+#define MT9P031_PIXEL_CLOCK_SHIFT(n) ((n) << 8)
+#define MT9P031_PIXEL_CLOCK_DIVIDE(n) ((n) << 0)
#define MT9P031_FRAME_RESTART 0x0b
#define MT9P031_SHUTTER_DELAY 0x0c
#define MT9P031_RST 0x0d
@@ -130,6 +133,8 @@ struct mt9p031 {
enum mt9p031_model model;
struct aptina_pll pll;
+ unsigned int clk_div;
+ bool use_pll;
int reset;
struct v4l2_ctrl_handler ctrls;
@@ -198,6 +203,11 @@ static int mt9p031_reset(struct mt9p031 *mt9p031)
if (ret < 0)
return ret;
+ ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL,
+ MT9P031_PIXEL_CLOCK_DIVIDE(mt9p031->clk_div));
+ if (ret < 0)
+ return ret;
+
return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
0);
}
@@ -222,15 +232,34 @@ static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
struct mt9p031_platform_data *pdata = mt9p031->pdata;
+ int ret;
mt9p031->clk = devm_clk_get(&client->dev, NULL);
if (IS_ERR(mt9p031->clk))
return PTR_ERR(mt9p031->clk);
- clk_set_rate(mt9p031->clk, pdata->ext_freq);
+ ret = clk_set_rate(mt9p031->clk, pdata->ext_freq);
+ if (ret < 0)
+ return ret;
+
+ /* If the external clock frequency is out of bounds for the PLL use the
+ * pixel clock divider only and disable the PLL.
+ */
+ if (pdata->ext_freq > limits.ext_clock_max) {
+ unsigned int div;
+
+ div = DIV_ROUND_UP(pdata->ext_freq, pdata->target_freq);
+ div = roundup_pow_of_two(div) / 2;
+
+ mt9p031->clk_div = max_t(unsigned int, div, 64);
+ mt9p031->use_pll = false;
+
+ return 0;
+ }
mt9p031->pll.ext_clock = pdata->ext_freq;
mt9p031->pll.pix_clock = pdata->target_freq;
+ mt9p031->use_pll = true;
return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
}
@@ -240,6 +269,9 @@ static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
int ret;
+ if (!mt9p031->use_pll)
+ return 0;
+
ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
MT9P031_PLL_CONTROL_PWRON);
if (ret < 0)
@@ -265,6 +297,9 @@ static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
{
struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ if (!mt9p031->use_pll)
+ return 0;
+
return mt9p031_write(client, MT9P031_PLL_CONTROL,
MT9P031_PLL_CONTROL_PWROFF);
}
@@ -285,9 +320,15 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031)
if (ret < 0)
return ret;
- /* Emable clock */
- if (mt9p031->clk)
- clk_prepare_enable(mt9p031->clk);
+ /* Enable clock */
+ if (mt9p031->clk) {
+ ret = clk_prepare_enable(mt9p031->clk);
+ if (ret) {
+ regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators),
+ mt9p031->regulators);
+ return ret;
+ }
+ }
/* Now RESET_BAR must be high */
if (gpio_is_valid(mt9p031->reset)) {
diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c
index d41c70eaf838..422e068f5f1b 100644
--- a/drivers/media/i2c/mt9t001.c
+++ b/drivers/media/i2c/mt9t001.c
@@ -12,9 +12,11 @@
* published by the Free Software Foundation.
*/
+#include <linux/clk.h>
#include <linux/i2c.h>
-#include <linux/module.h>
#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/v4l2-mediabus.h>
@@ -55,6 +57,7 @@
#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0)
#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1)
#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6)
+#define MT9T001_OUTPUT_CONTROL_DEF 0x0002
#define MT9T001_SHUTTER_WIDTH_HIGH 0x08
#define MT9T001_SHUTTER_WIDTH_LOW 0x09
#define MT9T001_SHUTTER_WIDTH_MIN 1
@@ -116,6 +119,12 @@ struct mt9t001 {
struct v4l2_subdev subdev;
struct media_pad pad;
+ struct clk *clk;
+ struct regulator_bulk_data regulators[2];
+
+ struct mutex power_lock; /* lock to protect power_count */
+ int power_count;
+
struct v4l2_mbus_framefmt format;
struct v4l2_rect crop;
@@ -159,6 +168,77 @@ static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear,
return 0;
}
+static int mt9t001_reset(struct mt9t001 *mt9t001)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev);
+ int ret;
+
+ /* Reset the chip and stop data read out */
+ ret = mt9t001_write(client, MT9T001_RESET, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_RESET, 0);
+ if (ret < 0)
+ return ret;
+
+ mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF;
+
+ return mt9t001_set_output_control(mt9t001,
+ MT9T001_OUTPUT_CONTROL_CHIP_ENABLE,
+ 0);
+}
+
+static int mt9t001_power_on(struct mt9t001 *mt9t001)
+{
+ int ret;
+
+ /* Bring up the supplies */
+ ret = regulator_bulk_enable(ARRAY_SIZE(mt9t001->regulators),
+ mt9t001->regulators);
+ if (ret < 0)
+ return ret;
+
+ /* Enable clock */
+ ret = clk_prepare_enable(mt9t001->clk);
+ if (ret < 0)
+ regulator_bulk_disable(ARRAY_SIZE(mt9t001->regulators),
+ mt9t001->regulators);
+
+ return ret;
+}
+
+static void mt9t001_power_off(struct mt9t001 *mt9t001)
+{
+ regulator_bulk_disable(ARRAY_SIZE(mt9t001->regulators),
+ mt9t001->regulators);
+
+ clk_disable_unprepare(mt9t001->clk);
+}
+
+static int __mt9t001_set_power(struct mt9t001 *mt9t001, bool on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev);
+ int ret;
+
+ if (!on) {
+ mt9t001_power_off(mt9t001);
+ return 0;
+ }
+
+ ret = mt9t001_power_on(mt9t001);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_reset(mt9t001);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to reset the camera\n");
+ return ret;
+ }
+
+ return v4l2_ctrl_handler_setup(&mt9t001->ctrls);
+}
+
/* -----------------------------------------------------------------------------
* V4L2 subdev video operations
*/
@@ -195,6 +275,7 @@ static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable)
{
const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE;
struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9t001_platform_data *pdata = client->dev.platform_data;
struct mt9t001 *mt9t001 = to_mt9t001(subdev);
struct v4l2_mbus_framefmt *format = &mt9t001->format;
struct v4l2_rect *crop = &mt9t001->crop;
@@ -205,6 +286,14 @@ static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable)
if (!enable)
return mt9t001_set_output_control(mt9t001, mode, 0);
+ /* Configure the pixel clock polarity */
+ if (pdata->clk_pol) {
+ ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK,
+ MT9T001_PIXEL_CLOCK_INVERT);
+ if (ret < 0)
+ return ret;
+ }
+
/* Configure the window size and row/column bin */
hratio = DIV_ROUND_CLOSEST(crop->width, format->width);
vratio = DIV_ROUND_CLOSEST(crop->height, format->height);
@@ -630,9 +719,67 @@ static const struct v4l2_ctrl_config mt9t001_gains[] = {
};
/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int mt9t001_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ int ret = 0;
+
+ mutex_lock(&mt9t001->power_lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (mt9t001->power_count == !on) {
+ ret = __mt9t001_set_power(mt9t001, !!on);
+ if (ret < 0)
+ goto out;
+ }
+
+ /* Update the power count. */
+ mt9t001->power_count += on ? 1 : -1;
+ WARN_ON(mt9t001->power_count < 0);
+
+out:
+ mutex_unlock(&mt9t001->power_lock);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
* V4L2 subdev internal operations
*/
+static int mt9t001_registered(struct v4l2_subdev *subdev)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ s32 data;
+ int ret;
+
+ ret = mt9t001_power_on(mt9t001);
+ if (ret < 0) {
+ dev_err(&client->dev, "MT9T001 power up failed\n");
+ return ret;
+ }
+
+ /* Read out the chip version register */
+ data = mt9t001_read(client, MT9T001_CHIP_VERSION);
+ mt9t001_power_off(mt9t001);
+
+ if (data != MT9T001_CHIP_ID) {
+ dev_err(&client->dev,
+ "MT9T001 not detected, wrong version 0x%04x\n", data);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n",
+ client->addr);
+
+ return 0;
+}
+
static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
{
struct v4l2_mbus_framefmt *format;
@@ -651,9 +798,18 @@ static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
format->field = V4L2_FIELD_NONE;
format->colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
+ return mt9t001_set_power(subdev, 1);
+}
+
+static int mt9t001_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ return mt9t001_set_power(subdev, 0);
}
+static struct v4l2_subdev_core_ops mt9t001_subdev_core_ops = {
+ .s_power = mt9t001_set_power,
+};
+
static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = {
.s_stream = mt9t001_s_stream,
};
@@ -668,58 +824,17 @@ static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = {
};
static struct v4l2_subdev_ops mt9t001_subdev_ops = {
+ .core = &mt9t001_subdev_core_ops,
.video = &mt9t001_subdev_video_ops,
.pad = &mt9t001_subdev_pad_ops,
};
static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = {
+ .registered = mt9t001_registered,
.open = mt9t001_open,
+ .close = mt9t001_close,
};
-static int mt9t001_video_probe(struct i2c_client *client)
-{
- struct mt9t001_platform_data *pdata = client->dev.platform_data;
- s32 data;
- int ret;
-
- dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n",
- client->addr);
-
- /* Reset the chip and stop data read out */
- ret = mt9t001_write(client, MT9T001_RESET, 1);
- if (ret < 0)
- return ret;
-
- ret = mt9t001_write(client, MT9T001_RESET, 0);
- if (ret < 0)
- return ret;
-
- ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0);
- if (ret < 0)
- return ret;
-
- /* Configure the pixel clock polarity */
- if (pdata->clk_pol) {
- ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK,
- MT9T001_PIXEL_CLOCK_INVERT);
- if (ret < 0)
- return ret;
- }
-
- /* Read and check the sensor version */
- data = mt9t001_read(client, MT9T001_CHIP_VERSION);
- if (data != MT9T001_CHIP_ID) {
- dev_err(&client->dev, "MT9T001 not detected, wrong version "
- "0x%04x\n", data);
- return -ENODEV;
- }
-
- dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n",
- client->addr);
-
- return ret;
-}
-
static int mt9t001_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
@@ -740,14 +855,28 @@ static int mt9t001_probe(struct i2c_client *client,
return -EIO;
}
- ret = mt9t001_video_probe(client);
- if (ret < 0)
- return ret;
-
mt9t001 = devm_kzalloc(&client->dev, sizeof(*mt9t001), GFP_KERNEL);
if (!mt9t001)
return -ENOMEM;
+ mutex_init(&mt9t001->power_lock);
+ mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF;
+
+ mt9t001->regulators[0].supply = "vdd";
+ mt9t001->regulators[1].supply = "vaa";
+
+ ret = devm_regulator_bulk_get(&client->dev, 2, mt9t001->regulators);
+ if (ret < 0) {
+ dev_err(&client->dev, "Unable to get regulators\n");
+ return ret;
+ }
+
+ mt9t001->clk = devm_clk_get(&client->dev, NULL);
+ if (IS_ERR(mt9t001->clk)) {
+ dev_err(&client->dev, "Unable to get clock\n");
+ return PTR_ERR(mt9t001->clk);
+ }
+
v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) +
ARRAY_SIZE(mt9t001_gains) + 4);
diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c
index f74698cf14c9..47e475319a24 100644
--- a/drivers/media/i2c/mt9v011.c
+++ b/drivers/media/i2c/mt9v011.c
@@ -1,7 +1,7 @@
/*
* mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor
*
- * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com)
+ * Copyright (c) 2009 Mauro Carvalho Chehab
* This code is placed under the terms of the GNU General Public License v2
*/
@@ -16,7 +16,7 @@
#include <media/mt9v011.h>
MODULE_DESCRIPTION("Micron mt9v011 sensor driver");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_LICENSE("GPL");
static int debug;
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
index 36c504b78f2c..40172b8d8ea2 100644
--- a/drivers/media/i2c/mt9v032.c
+++ b/drivers/media/i2c/mt9v032.c
@@ -317,8 +317,14 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032)
struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
int ret;
- clk_set_rate(mt9v032->clk, mt9v032->sysclk);
- clk_prepare_enable(mt9v032->clk);
+ ret = clk_set_rate(mt9v032->clk, mt9v032->sysclk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(mt9v032->clk);
+ if (ret)
+ return ret;
+
udelay(1);
/* Reset the chip and stop data read out */
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index e7f555cc827a..a4459301b5f8 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -15,7 +15,7 @@
* GNU General Public License for more details.
*/
-#include <linux/sizes.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/gpio.h>
@@ -23,7 +23,9 @@
#include <linux/init.h>
#include <linux/media.h>
#include <linux/module.h>
+#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/videodev2.h>
@@ -33,6 +35,7 @@
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
#include <media/s5c73m3.h>
+#include <media/v4l2-of.h>
#include "s5c73m3.h"
@@ -46,6 +49,8 @@ static int update_fw;
module_param(update_fw, int, 0644);
#define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K
+#define S5C73M3_MIPI_DATA_LANES 4
+#define S5C73M3_CLK_NAME "cis_extclk"
static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = {
"vdd-int", /* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */
@@ -1355,9 +1360,20 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)
for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) {
ret = regulator_enable(state->supplies[i].consumer);
if (ret)
- goto err;
+ goto err_reg_dis;
}
+ ret = clk_set_rate(state->clock, state->mclk_frequency);
+ if (ret < 0)
+ goto err_reg_dis;
+
+ ret = clk_prepare_enable(state->clock);
+ if (ret < 0)
+ goto err_reg_dis;
+
+ v4l2_dbg(1, s5c73m3_dbg, &state->oif_sd, "clock frequency: %ld\n",
+ clk_get_rate(state->clock));
+
s5c73m3_gpio_deassert(state, STBY);
usleep_range(100, 200);
@@ -1365,7 +1381,8 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)
usleep_range(50, 100);
return 0;
-err:
+
+err_reg_dis:
for (--i; i >= 0; i--)
regulator_disable(state->supplies[i].consumer);
return ret;
@@ -1380,6 +1397,9 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
if (s5c73m3_gpio_assert(state, STBY))
usleep_range(100, 200);
+
+ clk_disable_unprepare(state->clock);
+
state->streaming = 0;
state->isp_ready = 0;
@@ -1388,6 +1408,7 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
if (ret)
goto err;
}
+
return 0;
err:
for (++i; i < S5C73M3_MAX_SUPPLIES; i++) {
@@ -1396,6 +1417,8 @@ err:
v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n",
state->supplies[i].supply, r);
}
+
+ clk_prepare_enable(state->clock);
return ret;
}
@@ -1451,17 +1474,6 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd)
S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD,
MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
- mutex_lock(&state->lock);
- ret = __s5c73m3_power_on(state);
- if (ret == 0)
- s5c73m3_get_fw_version(state);
-
- __s5c73m3_power_off(state);
- mutex_unlock(&state->lock);
-
- v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n",
- __func__, ret ? "failed" : "succeeded", ret);
-
return ret;
}
@@ -1519,41 +1531,112 @@ static const struct v4l2_subdev_ops oif_subdev_ops = {
.video = &s5c73m3_oif_video_ops,
};
-static int s5c73m3_configure_gpios(struct s5c73m3 *state,
- const struct s5c73m3_platform_data *pdata)
+static int s5c73m3_configure_gpios(struct s5c73m3 *state)
+{
+ static const char * const gpio_names[] = {
+ "S5C73M3_STBY", "S5C73M3_RST"
+ };
+ struct i2c_client *c = state->i2c_client;
+ struct s5c73m3_gpio *g = state->gpio;
+ int ret, i;
+
+ for (i = 0; i < GPIO_NUM; ++i) {
+ unsigned int flags = GPIOF_DIR_OUT;
+ if (g[i].level)
+ flags |= GPIOF_INIT_HIGH;
+ ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags,
+ gpio_names[i]);
+ if (ret) {
+ v4l2_err(c, "failed to request gpio %s\n",
+ gpio_names[i]);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int s5c73m3_parse_gpios(struct s5c73m3 *state)
+{
+ static const char * const prop_names[] = {
+ "standby-gpios", "xshutdown-gpios",
+ };
+ struct device *dev = &state->i2c_client->dev;
+ struct device_node *node = dev->of_node;
+ int ret, i;
+
+ for (i = 0; i < GPIO_NUM; ++i) {
+ enum of_gpio_flags of_flags;
+
+ ret = of_get_named_gpio_flags(node, prop_names[i],
+ 0, &of_flags);
+ if (ret < 0) {
+ dev_err(dev, "failed to parse %s DT property\n",
+ prop_names[i]);
+ return -EINVAL;
+ }
+ state->gpio[i].gpio = ret;
+ state->gpio[i].level = !(of_flags & OF_GPIO_ACTIVE_LOW);
+ }
+ return 0;
+}
+
+static int s5c73m3_get_platform_data(struct s5c73m3 *state)
{
struct device *dev = &state->i2c_client->dev;
- const struct s5c73m3_gpio *gpio;
- unsigned long flags;
+ const struct s5c73m3_platform_data *pdata = dev->platform_data;
+ struct device_node *node = dev->of_node;
+ struct device_node *node_ep;
+ struct v4l2_of_endpoint ep;
int ret;
- state->gpio[STBY].gpio = -EINVAL;
- state->gpio[RST].gpio = -EINVAL;
+ if (!node) {
+ if (!pdata) {
+ dev_err(dev, "Platform data not specified\n");
+ return -EINVAL;
+ }
- gpio = &pdata->gpio_stby;
- if (gpio_is_valid(gpio->gpio)) {
- flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
- | GPIOF_EXPORT;
- ret = devm_gpio_request_one(dev, gpio->gpio, flags,
- "S5C73M3_STBY");
- if (ret < 0)
- return ret;
+ state->mclk_frequency = pdata->mclk_frequency;
+ state->gpio[STBY] = pdata->gpio_stby;
+ state->gpio[RST] = pdata->gpio_reset;
+ return 0;
+ }
+
+ state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME);
+ if (IS_ERR(state->clock))
+ return PTR_ERR(state->clock);
- state->gpio[STBY] = *gpio;
+ if (of_property_read_u32(node, "clock-frequency",
+ &state->mclk_frequency)) {
+ state->mclk_frequency = S5C73M3_DEFAULT_MCLK_FREQ;
+ dev_info(dev, "using default %u Hz clock frequency\n",
+ state->mclk_frequency);
}
- gpio = &pdata->gpio_reset;
- if (gpio_is_valid(gpio->gpio)) {
- flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
- | GPIOF_EXPORT;
- ret = devm_gpio_request_one(dev, gpio->gpio, flags,
- "S5C73M3_RST");
- if (ret < 0)
- return ret;
+ ret = s5c73m3_parse_gpios(state);
+ if (ret < 0)
+ return -EINVAL;
- state->gpio[RST] = *gpio;
+ node_ep = v4l2_of_get_next_endpoint(node, NULL);
+ if (!node_ep) {
+ dev_warn(dev, "no endpoint defined for node: %s\n",
+ node->full_name);
+ return 0;
}
+ v4l2_of_parse_endpoint(node_ep, &ep);
+ of_node_put(node_ep);
+
+ if (ep.bus_type != V4L2_MBUS_CSI2) {
+ dev_err(dev, "unsupported bus type\n");
+ return -EINVAL;
+ }
+ /*
+ * Number of MIPI CSI-2 data lanes is currently not configurable,
+ * always a default value of 4 lanes is used.
+ */
+ if (ep.bus.mipi_csi2.num_data_lanes != S5C73M3_MIPI_DATA_LANES)
+ dev_info(dev, "falling back to 4 MIPI CSI-2 data lanes\n");
+
return 0;
}
@@ -1561,21 +1644,20 @@ static int s5c73m3_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
- const struct s5c73m3_platform_data *pdata = client->dev.platform_data;
struct v4l2_subdev *sd;
struct v4l2_subdev *oif_sd;
struct s5c73m3 *state;
int ret, i;
- if (pdata == NULL) {
- dev_err(&client->dev, "Platform data not specified\n");
- return -EINVAL;
- }
-
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
+ state->i2c_client = client;
+ ret = s5c73m3_get_platform_data(state);
+ if (ret < 0)
+ return ret;
+
mutex_init(&state->lock);
sd = &state->sensor_sd;
oif_sd = &state->oif_sd;
@@ -1613,11 +1695,7 @@ static int s5c73m3_probe(struct i2c_client *client,
if (ret < 0)
return ret;
- state->mclk_frequency = pdata->mclk_frequency;
- state->bus_type = pdata->bus_type;
- state->i2c_client = client;
-
- ret = s5c73m3_configure_gpios(state, pdata);
+ ret = s5c73m3_configure_gpios(state);
if (ret)
goto out_err;
@@ -1651,9 +1729,29 @@ static int s5c73m3_probe(struct i2c_client *client,
if (ret < 0)
goto out_err;
+ oif_sd->dev = dev;
+
+ ret = __s5c73m3_power_on(state);
+ if (ret < 0)
+ goto out_err1;
+
+ ret = s5c73m3_get_fw_version(state);
+ __s5c73m3_power_off(state);
+
+ if (ret < 0) {
+ dev_err(dev, "Device detection failed: %d\n", ret);
+ goto out_err1;
+ }
+
+ ret = v4l2_async_register_subdev(oif_sd);
+ if (ret < 0)
+ goto out_err1;
+
v4l2_info(sd, "%s: completed successfully\n", __func__);
return 0;
+out_err1:
+ s5c73m3_unregister_spi_driver(state);
out_err:
media_entity_cleanup(&sd->entity);
return ret;
@@ -1665,7 +1763,7 @@ static int s5c73m3_remove(struct i2c_client *client)
struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd);
struct v4l2_subdev *sensor_sd = &state->sensor_sd;
- v4l2_device_unregister_subdev(oif_sd);
+ v4l2_async_unregister_subdev(oif_sd);
v4l2_ctrl_handler_free(oif_sd->ctrl_handler);
media_entity_cleanup(&oif_sd->entity);
@@ -1684,8 +1782,17 @@ static const struct i2c_device_id s5c73m3_id[] = {
};
MODULE_DEVICE_TABLE(i2c, s5c73m3_id);
+#ifdef CONFIG_OF
+static const struct of_device_id s5c73m3_of_match[] = {
+ { .compatible = "samsung,s5c73m3" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, s5c73m3_of_match);
+#endif
+
static struct i2c_driver s5c73m3_i2c_driver = {
.driver = {
+ .of_match_table = of_match_ptr(s5c73m3_of_match),
.name = DRIVER_NAME,
},
.probe = s5c73m3_probe,
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
index 8079e26eb5e2..f60b265b4da1 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
@@ -27,6 +27,11 @@
#define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI"
+static const struct of_device_id s5c73m3_spi_ids[] = {
+ { .compatible = "samsung,s5c73m3" },
+ { }
+};
+
enum spi_direction {
SPI_DIR_RX,
SPI_DIR_TX
@@ -146,6 +151,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state)
spidrv->driver.name = S5C73M3_SPI_DRV_NAME;
spidrv->driver.bus = &spi_bus_type;
spidrv->driver.owner = THIS_MODULE;
+ spidrv->driver.of_match_table = s5c73m3_spi_ids;
return spi_register_driver(spidrv);
}
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h
index 9dfa516f6944..9656b6723dc6 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3.h
+++ b/drivers/media/i2c/s5c73m3/s5c73m3.h
@@ -17,6 +17,7 @@
#ifndef S5C73M3_H_
#define S5C73M3_H_
+#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/regulator/consumer.h>
#include <media/v4l2-common.h>
@@ -321,6 +322,7 @@ enum s5c73m3_oif_pads {
#define S5C73M3_MAX_SUPPLIES 6
+#define S5C73M3_DEFAULT_MCLK_FREQ 24000000U
struct s5c73m3_ctrls {
struct v4l2_ctrl_handler handler;
@@ -391,6 +393,8 @@ struct s5c73m3 {
struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES];
struct s5c73m3_gpio gpio[GPIO_NUM];
+ struct clk *clock;
+
/* External master clock frequency */
u32 mclk_frequency;
/* Video bus type - MIPI-CSI2/parallel */
diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c
new file mode 100644
index 000000000000..7bc2271ca009
--- /dev/null
+++ b/drivers/media/i2c/s5k6a3.c
@@ -0,0 +1,389 @@
+/*
+ * Samsung S5K6A3 image sensor driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-subdev.h>
+
+#define S5K6A3_SENSOR_MAX_WIDTH 1412
+#define S5K6A3_SENSOR_MAX_HEIGHT 1412
+#define S5K6A3_SENSOR_MIN_WIDTH 32
+#define S5K6A3_SENSOR_MIN_HEIGHT 32
+
+#define S5K6A3_DEFAULT_WIDTH 1296
+#define S5K6A3_DEFAULT_HEIGHT 732
+
+#define S5K6A3_DRV_NAME "S5K6A3"
+#define S5K6A3_CLK_NAME "extclk"
+#define S5K6A3_DEFAULT_CLK_FREQ 24000000U
+
+enum {
+ S5K6A3_SUPP_VDDA,
+ S5K6A3_SUPP_VDDIO,
+ S5K6A3_SUPP_AFVDD,
+ S5K6A3_NUM_SUPPLIES,
+};
+
+/**
+ * struct s5k6a3 - fimc-is sensor data structure
+ * @dev: pointer to this I2C client device structure
+ * @subdev: the image sensor's v4l2 subdev
+ * @pad: subdev media source pad
+ * @supplies: image sensor's voltage regulator supplies
+ * @gpio_reset: GPIO connected to the sensor's reset pin
+ * @lock: mutex protecting the structure's members below
+ * @format: media bus format at the sensor's source pad
+ */
+struct s5k6a3 {
+ struct device *dev;
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES];
+ int gpio_reset;
+ struct mutex lock;
+ struct v4l2_mbus_framefmt format;
+ struct clk *clock;
+ u32 clock_frequency;
+ int power_count;
+};
+
+static const char * const s5k6a3_supply_names[] = {
+ [S5K6A3_SUPP_VDDA] = "svdda",
+ [S5K6A3_SUPP_VDDIO] = "svddio",
+ [S5K6A3_SUPP_AFVDD] = "afvdd",
+};
+
+static inline struct s5k6a3 *sd_to_s5k6a3(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s5k6a3, subdev);
+}
+
+static const struct v4l2_mbus_framefmt s5k6a3_formats[] = {
+ {
+ .code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+ }
+};
+
+static const struct v4l2_mbus_framefmt *find_sensor_format(
+ struct v4l2_mbus_framefmt *mf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6a3_formats); i++)
+ if (mf->code == s5k6a3_formats[i].code)
+ return &s5k6a3_formats[i];
+
+ return &s5k6a3_formats[0];
+}
+
+static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(s5k6a3_formats))
+ return -EINVAL;
+
+ code->code = s5k6a3_formats[code->index].code;
+ return 0;
+}
+
+static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf)
+{
+ const struct v4l2_mbus_framefmt *fmt;
+
+ fmt = find_sensor_format(mf);
+ mf->code = fmt->code;
+ v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH,
+ S5K6A3_SENSOR_MAX_WIDTH, 0,
+ &mf->height, S5K6A3_SENSOR_MIN_HEIGHT,
+ S5K6A3_SENSOR_MAX_HEIGHT, 0, 0);
+}
+
+static struct v4l2_mbus_framefmt *__s5k6a3_get_format(
+ struct s5k6a3 *sensor, struct v4l2_subdev_fh *fh,
+ u32 pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
+
+ return &sensor->format;
+}
+
+static int s5k6a3_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ s5k6a3_try_format(&fmt->format);
+
+ mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+ if (mf) {
+ mutex_lock(&sensor->lock);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ *mf = fmt->format;
+ mutex_unlock(&sensor->lock);
+ }
+ return 0;
+}
+
+static int s5k6a3_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+
+ mutex_lock(&sensor->lock);
+ fmt->format = *mf;
+ mutex_unlock(&sensor->lock);
+ return 0;
+}
+
+static struct v4l2_subdev_pad_ops s5k6a3_pad_ops = {
+ .enum_mbus_code = s5k6a3_enum_mbus_code,
+ .get_fmt = s5k6a3_get_fmt,
+ .set_fmt = s5k6a3_set_fmt,
+};
+
+static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+
+ *format = s5k6a3_formats[0];
+ format->width = S5K6A3_DEFAULT_WIDTH;
+ format->height = S5K6A3_DEFAULT_HEIGHT;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = {
+ .open = s5k6a3_open,
+};
+
+static int __s5k6a3_power_on(struct s5k6a3 *sensor)
+{
+ int i = S5K6A3_SUPP_VDDA;
+ int ret;
+
+ ret = clk_set_rate(sensor->clock, sensor->clock_frequency);
+ if (ret < 0)
+ return ret;
+
+ ret = pm_runtime_get(sensor->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_enable(sensor->supplies[i].consumer);
+ if (ret < 0)
+ goto error_rpm_put;
+
+ ret = clk_prepare_enable(sensor->clock);
+ if (ret < 0)
+ goto error_reg_dis;
+
+ for (i++; i < S5K6A3_NUM_SUPPLIES; i++) {
+ ret = regulator_enable(sensor->supplies[i].consumer);
+ if (ret < 0)
+ goto error_reg_dis;
+ }
+
+ gpio_set_value(sensor->gpio_reset, 1);
+ usleep_range(600, 800);
+ gpio_set_value(sensor->gpio_reset, 0);
+ usleep_range(600, 800);
+ gpio_set_value(sensor->gpio_reset, 1);
+
+ /* Delay needed for the sensor initialization */
+ msleep(20);
+ return 0;
+
+error_reg_dis:
+ for (--i; i >= 0; --i)
+ regulator_disable(sensor->supplies[i].consumer);
+error_rpm_put:
+ pm_runtime_put(sensor->dev);
+ return ret;
+}
+
+static int __s5k6a3_power_off(struct s5k6a3 *sensor)
+{
+ int i;
+
+ gpio_set_value(sensor->gpio_reset, 0);
+
+ for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--)
+ regulator_disable(sensor->supplies[i].consumer);
+
+ clk_disable_unprepare(sensor->clock);
+ pm_runtime_put(sensor->dev);
+ return 0;
+}
+
+static int s5k6a3_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ int ret = 0;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->power_count == !on) {
+ if (on)
+ ret = __s5k6a3_power_on(sensor);
+ else
+ ret = __s5k6a3_power_off(sensor);
+
+ if (ret == 0)
+ sensor->power_count += on ? 1 : -1;
+ }
+
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static struct v4l2_subdev_core_ops s5k6a3_core_ops = {
+ .s_power = s5k6a3_s_power,
+};
+
+static struct v4l2_subdev_ops s5k6a3_subdev_ops = {
+ .core = &s5k6a3_core_ops,
+ .pad = &s5k6a3_pad_ops,
+};
+
+static int s5k6a3_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct s5k6a3 *sensor;
+ struct v4l2_subdev *sd;
+ int gpio, i, ret;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ mutex_init(&sensor->lock);
+ sensor->gpio_reset = -EINVAL;
+ sensor->clock = ERR_PTR(-EINVAL);
+ sensor->dev = dev;
+
+ sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME);
+ if (IS_ERR(sensor->clock))
+ return PTR_ERR(sensor->clock);
+
+ gpio = of_get_gpio_flags(dev->of_node, 0, NULL);
+ if (!gpio_is_valid(gpio))
+ return gpio;
+
+ ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW,
+ S5K6A3_DRV_NAME);
+ if (ret < 0)
+ return ret;
+
+ sensor->gpio_reset = gpio;
+
+ if (of_property_read_u32(dev->of_node, "clock-frequency",
+ &sensor->clock_frequency)) {
+ sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ;
+ dev_info(dev, "using default %u Hz clock frequency\n",
+ sensor->clock_frequency);
+ }
+
+ for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++)
+ sensor->supplies[i].supply = s5k6a3_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES,
+ sensor->supplies);
+ if (ret < 0)
+ return ret;
+
+ sd = &sensor->subdev;
+ v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops);
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->internal_ops = &s5k6a3_sd_internal_ops;
+
+ sensor->format.code = s5k6a3_formats[0].code;
+ sensor->format.width = S5K6A3_DEFAULT_WIDTH;
+ sensor->format.height = S5K6A3_DEFAULT_HEIGHT;
+
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_no_callbacks(dev);
+ pm_runtime_enable(dev);
+
+ ret = v4l2_async_register_subdev(sd);
+
+ if (ret < 0) {
+ pm_runtime_disable(&client->dev);
+ media_entity_cleanup(&sd->entity);
+ }
+
+ return ret;
+}
+
+static int s5k6a3_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ pm_runtime_disable(&client->dev);
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ return 0;
+}
+
+static const struct i2c_device_id s5k6a3_ids[] = {
+ { }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id s5k6a3_of_match[] = {
+ { .compatible = "samsung,s5k6a3" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s5k6a3_of_match);
+#endif
+
+static struct i2c_driver s5k6a3_driver = {
+ .driver = {
+ .of_match_table = of_match_ptr(s5k6a3_of_match),
+ .name = S5K6A3_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = s5k6a3_probe,
+ .remove = s5k6a3_remove,
+ .id_table = s5k6a3_ids,
+};
+
+module_i2c_driver(s5k6a3_driver);
+
+MODULE_DESCRIPTION("S5K6A3 image sensor subdev driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c
index ae9432637fcb..118f8ee88465 100644
--- a/drivers/media/i2c/sr030pc30.c
+++ b/drivers/media/i2c/sr030pc30.c
@@ -8,7 +8,7 @@
* and HeungJun Kim <riverful.kim@samsung.com>.
*
* Based on mt9v011 Micron Digital Image Sensor driver
- * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com)
+ * Copyright (c) 2009 Mauro Carvalho Chehab
*
* 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
diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c
index 04139eec8c4e..f72561e79739 100644
--- a/drivers/media/i2c/ths8200.c
+++ b/drivers/media/i2c/ths8200.c
@@ -217,8 +217,8 @@ static void ths8200_core_init(struct v4l2_subdev *sd)
/* Disable embedded syncs on the output by setting
* the amplitude to zero for all channels.
*/
- ths8200_write(sd, THS8200_DTG1_Y_SYNC_MSB, 0x2a);
- ths8200_write(sd, THS8200_DTG1_CBCR_SYNC_MSB, 0x2a);
+ ths8200_write(sd, THS8200_DTG1_Y_SYNC_MSB, 0x00);
+ ths8200_write(sd, THS8200_DTG1_CBCR_SYNC_MSB, 0x00);
}
static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
@@ -318,15 +318,15 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
(htotal(bt) >> 8) & 0x1f);
ths8200_write(sd, THS8200_DTG2_HLENGTH_HDLY_LSB, htotal(bt));
- /* v sync width transmitted */
- ths8200_write(sd, THS8200_DTG2_VLENGTH1_LSB, (bt->vsync) & 0xff);
+ /* v sync width transmitted (must add 1 to get correct output) */
+ ths8200_write(sd, THS8200_DTG2_VLENGTH1_LSB, (bt->vsync + 1) & 0xff);
ths8200_write_and_or(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB, 0x3f,
- ((bt->vsync) >> 2) & 0xc0);
+ ((bt->vsync + 1) >> 2) & 0xc0);
- /* The pixel value v sync is asserted on */
+ /* The pixel value v sync is asserted on (must add 1 to get correct output) */
ths8200_write_and_or(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB, 0xf8,
- (vtotal(bt)>>8) & 0x7);
- ths8200_write(sd, THS8200_DTG2_VDLY1_LSB, vtotal(bt));
+ ((vtotal(bt) + 1) >> 8) & 0x7);
+ ths8200_write(sd, THS8200_DTG2_VDLY1_LSB, vtotal(bt) + 1);
/* For progressive video vlength2 must be set to all 0 and vdly2 must
* be set to all 1.
@@ -336,11 +336,11 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
ths8200_write(sd, THS8200_DTG2_VDLY2_LSB, 0xff);
/* Internal delay factors to synchronize the sync pulses and the data */
- /* Experimental values delays (hor 4, ver 1) */
- ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_MSB, (htotal(bt)>>8) & 0x1f);
- ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_LSB, (htotal(bt) - 4) & 0xff);
+ /* Experimental values delays (hor 0, ver 0) */
+ ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_MSB, 0);
+ ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_LSB, 0);
ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_MSB, 0);
- ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_LSB, 1);
+ ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_LSB, 0);
/* Polarity of received and transmitted sync signals */
if (bt->polarities & V4L2_DV_HSYNC_POS_POL) {
@@ -356,7 +356,7 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
/* Timing of video input bus is derived from HS, VS, and FID dedicated
* inputs
*/
- ths8200_write(sd, THS8200_DTG2_CNTL, 0x47 | polarity);
+ ths8200_write(sd, THS8200_DTG2_CNTL, 0x44 | polarity);
/* leave reset */
ths8200_s_stream(sd, true);
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 542d2528b3f9..4fd3688e1164 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -16,9 +16,9 @@
#include "tvp5150_reg.h"
-#define TVP5150_H_MAX 720
-#define TVP5150_V_MAX_525_60 480
-#define TVP5150_V_MAX_OTHERS 576
+#define TVP5150_H_MAX 720U
+#define TVP5150_V_MAX_525_60 480U
+#define TVP5150_V_MAX_OTHERS 576U
#define TVP5150_MAX_CROP_LEFT 511
#define TVP5150_MAX_CROP_TOP 127
#define TVP5150_CROP_SHIFT 2
@@ -29,7 +29,7 @@ MODULE_LICENSE("GPL");
static int debug;
-module_param(debug, int, 0);
+module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-2)");
struct tvp5150 {
diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c
index d12bd33f39cb..8a0e84c7d495 100644
--- a/drivers/media/parport/bw-qcam.c
+++ b/drivers/media/parport/bw-qcam.c
@@ -667,13 +667,16 @@ static void buffer_queue(struct vb2_buffer *vb)
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
}
-static int buffer_finish(struct vb2_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
{
struct qcam *qcam = vb2_get_drv_priv(vb->vb2_queue);
void *vbuf = vb2_plane_vaddr(vb, 0);
int size = vb->vb2_queue->plane_sizes[0];
int len;
+ if (!vb2_is_streaming(vb->vb2_queue))
+ return;
+
mutex_lock(&qcam->lock);
parport_claim_or_block(qcam->pdev);
@@ -691,7 +694,6 @@ static int buffer_finish(struct vb2_buffer *vb)
if (len != size)
vb->state = VB2_BUF_STATE_ERROR;
vb2_set_plane_payload(vb, 0, len);
- return 0;
}
static struct vb2_ops qcam_video_qops = {
@@ -965,7 +967,7 @@ static struct qcam *qcam_init(struct parport *port)
q->drv_priv = qcam;
q->ops = &qcam_video_qops;
q->mem_ops = &vb2_vmalloc_memops;
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
err = vb2_queue_init(q);
if (err < 0) {
v4l2_err(v4l2_dev, "couldn't init vb2_queue for %s.\n", port->name);
diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c
index 6662b495b22c..d06963b3dcf3 100644
--- a/drivers/media/pci/bt8xx/bttv-cards.c
+++ b/drivers/media/pci/bt8xx/bttv-cards.c
@@ -2855,7 +2855,22 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_ABSENT,
.tuner_addr = ADDR_UNSET,
},
-
+ [BTTV_BOARD_KWORLD_VSTREAM_XPERT] = {
+ /* Pojar George <geoubuntu@gmail.com> */
+ .name = "Kworld V-Stream Xpert TV PVR878",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x001c0007,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 },
+ .gpiomute = 3,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TENA_9533_DI,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ .has_radio = 1,
+ },
};
static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c
index f36821367d8d..5930bce16658 100644
--- a/drivers/media/pci/bt8xx/bttv-input.c
+++ b/drivers/media/pci/bt8xx/bttv-input.c
@@ -483,6 +483,7 @@ int bttv_input_init(struct bttv *btv)
case BTTV_BOARD_ASKEY_CPH03X:
case BTTV_BOARD_CONCEPTRONIC_CTVFMI2:
case BTTV_BOARD_CONTVFMI:
+ case BTTV_BOARD_KWORLD_VSTREAM_XPERT:
ir_codes = RC_MAP_PIXELVIEW;
ir->mask_keycode = 0x001F00;
ir->mask_keyup = 0x006000;
diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h
index df578efe03c9..bb5da349a46e 100644
--- a/drivers/media/pci/bt8xx/bttv.h
+++ b/drivers/media/pci/bt8xx/bttv.h
@@ -188,6 +188,7 @@
#define BTTV_BOARD_ADLINK_MPG24 0xa2
#define BTTV_BOARD_BT848_CAP_14 0xa3
#define BTTV_BOARD_CYBERVISION_CV06 0xa4
+#define BTTV_BOARD_KWORLD_VSTREAM_XPERT 0xa5
/* more card-specific defines */
#define PT2254_L_CHANNEL 0x10
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index 05492053b473..4be01b3bd4f5 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -473,6 +473,7 @@ static struct ds3000_config tevii_ds3000_config = {
static struct ts2020_config tevii_ts2020_config = {
.tuner_address = 0x60,
.clk_out_div = 1,
+ .frequency_div = 1146000,
};
static struct cx24116_config dvbworld_cx24116_config = {
diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
index 8a49e7c9eddd..097d0a0b5f57 100644
--- a/drivers/media/pci/cx23885/cx23885-input.c
+++ b/drivers/media/pci/cx23885/cx23885-input.c
@@ -346,7 +346,7 @@ int cx23885_input_init(struct cx23885_dev *dev)
}
rc->dev.parent = &dev->pci->dev;
rc->driver_type = driver_type;
- rc->allowed_protos = allowed_protos;
+ rc_set_allowed_protocols(rc, allowed_protos);
rc->priv = kernel_ir;
rc->open = cx23885_input_ir_open;
rc->close = cx23885_input_ir_close;
diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
index f29e18c72f44..f991696a6c59 100644
--- a/drivers/media/pci/cx88/cx88-input.c
+++ b/drivers/media/pci/cx88/cx88-input.c
@@ -469,7 +469,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
dev->timeout = 10 * 1000 * 1000; /* 10 ms */
} else {
dev->driver_type = RC_DRIVER_SCANCODE;
- dev->allowed_protos = rc_type;
+ rc_set_allowed_protocols(dev, rc_type);
}
ir->core = core;
diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c
index 9375f30d9a81..fb52bda8d45f 100644
--- a/drivers/media/pci/ddbridge/ddbridge-core.c
+++ b/drivers/media/pci/ddbridge/ddbridge-core.c
@@ -876,10 +876,8 @@ static int dvb_input_attach(struct ddb_input *input)
return -ENODEV;
if (tuner_attach_tda18271(input) < 0)
return -ENODEV;
- if (input->fe) {
- if (dvb_register_frontend(adap, input->fe) < 0)
- return -ENODEV;
- }
+ if (dvb_register_frontend(adap, input->fe) < 0)
+ return -ENODEV;
if (input->fe2) {
if (dvb_register_frontend(adap, input->fe2) < 0)
return -ENODEV;
diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c
index c9b2350e92c8..6e4bdb90aa92 100644
--- a/drivers/media/pci/saa7134/saa7134-cards.c
+++ b/drivers/media/pci/saa7134/saa7134-cards.c
@@ -8045,8 +8045,8 @@ int saa7134_board_init2(struct saa7134_dev *dev)
break;
} /* switch() */
- /* initialize tuner */
- if (TUNER_ABSENT != dev->tuner_type) {
+ /* initialize tuner (don't do this when resuming) */
+ if (!dev->insuspend && TUNER_ABSENT != dev->tuner_type) {
int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
/* Note: radio tuner address is always filled in,
diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
index e5cfb6cfa18d..bb11443ed63e 100644
--- a/drivers/media/pci/sta2x11/sta2x11_vip.c
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
@@ -327,7 +327,7 @@ static void buffer_queue(struct vb2_buffer *vb)
}
spin_unlock(&vip->lock);
}
-static int buffer_finish(struct vb2_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
{
struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue);
struct vip_buffer *vip_buf = to_vip_buffer(vb);
@@ -337,9 +337,8 @@ static int buffer_finish(struct vb2_buffer *vb)
list_del_init(&vip_buf->list);
spin_unlock(&vip->lock);
- vip_active_buf_next(vip);
-
- return 0;
+ if (vb2_is_streaming(vb->vb2_queue))
+ vip_active_buf_next(vip);
}
static int start_streaming(struct vb2_queue *vq, unsigned int count)
diff --git a/drivers/media/pci/ttpci/av7110_hw.c b/drivers/media/pci/ttpci/av7110_hw.c
index 6299d5dadb82..300bd3c94738 100644
--- a/drivers/media/pci/ttpci/av7110_hw.c
+++ b/drivers/media/pci/ttpci/av7110_hw.c
@@ -501,7 +501,7 @@ int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...)
// dprintk(4, "%p\n", av7110);
- if (2 + num > sizeof(buf)) {
+ if (2 + num > ARRAY_SIZE(buf)) {
printk(KERN_WARNING
"%s: %s len=%d is too big!\n",
KBUILD_MODNAME, __func__, num);
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index b2a4403940c5..c137abfa0c54 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -36,7 +36,7 @@ source "drivers/media/platform/blackfin/Kconfig"
config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on MEDIA_CAMERA_SUPPORT
- depends on VIDEO_DEV && I2C
+ depends on VIDEO_DEV && I2C && HAS_DMA
depends on ARCH_SHMOBILE || COMPILE_TEST
select VIDEOBUF_DMA_CONTIG
help
diff --git a/drivers/media/platform/arv.c b/drivers/media/platform/arv.c
index e346d32d08ce..e9410e41ae0c 100644
--- a/drivers/media/platform/arv.c
+++ b/drivers/media/platform/arv.c
@@ -109,7 +109,7 @@ extern struct cpuinfo_m32r boot_cpu_data;
struct ar {
struct v4l2_device v4l2_dev;
struct video_device vdev;
- unsigned int start_capture; /* duaring capture in INT. mode. */
+ int start_capture; /* duaring capture in INT. mode. */
#if USE_INT
unsigned char *line_buff; /* DMA line buffer */
#endif
@@ -307,11 +307,11 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos)
/*
* Okay, kick AR LSI to invoke an interrupt
*/
- ar->start_capture = 0;
+ ar->start_capture = -1;
ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1);
local_irq_restore(flags);
/* .... AR interrupts .... */
- interruptible_sleep_on(&ar->wait);
+ wait_event_interruptible(ar->wait, ar->start_capture == 0);
if (signal_pending(current)) {
printk(KERN_ERR "arv: interrupted while get frame data.\n");
ret = -EINTR;
diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c
index 281916591437..200bec91182e 100644
--- a/drivers/media/platform/blackfin/bfin_capture.c
+++ b/drivers/media/platform/blackfin/bfin_capture.c
@@ -997,7 +997,7 @@ static int bcap_probe(struct platform_device *pdev)
q->buf_struct_size = sizeof(struct bcap_buffer);
q->ops = &bcap_video_qops;
q->mem_ops = &vb2_dma_contig_memops;
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(q);
if (ret)
diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
index 61f3dbcc259f..3e5199ee5d25 100644
--- a/drivers/media/platform/coda.c
+++ b/drivers/media/platform/coda.c
@@ -2428,7 +2428,7 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &coda_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
- src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -2440,7 +2440,7 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &coda_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
- dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
return vb2_queue_init(dst_vq);
}
@@ -2829,6 +2829,9 @@ static void coda_finish_encode(struct coda_ctx *ctx)
}
dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_buf->v4l2_buf.flags |=
+ src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index b02aba488826..b4f12d00be05 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -341,14 +341,8 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vpbe_fh *fh = vb2_get_drv_priv(vq);
struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
int ret;
- /* If buffer queue is empty, return error */
- if (list_empty(&layer->dma_queue)) {
- v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
- return -ENOBUFS;
- }
/* Get the next frame from the buffer queue */
layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
struct vpbe_disp_buffer, list);
@@ -1415,7 +1409,8 @@ static int vpbe_display_reqbufs(struct file *file, void *priv,
q->ops = &video_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct vpbe_disp_buffer);
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 1;
ret = vb2_queue_init(q);
if (ret) {
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 735ec47601a9..756da78bac23 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -272,13 +272,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
unsigned long flags;
int ret;
- /* If buffer queue is empty, return error */
spin_lock_irqsave(&common->irqlock, flags);
- if (list_empty(&common->dma_queue)) {
- spin_unlock_irqrestore(&common->irqlock, flags);
- vpif_dbg(1, debug, "buffer queue is empty\n");
- return -ENOBUFS;
- }
/* Get the next frame from the buffer queue */
common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
@@ -1023,7 +1017,8 @@ static int vpif_reqbufs(struct file *file, void *priv,
q->ops = &video_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct vpif_cap_buffer);
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 1;
ret = vb2_queue_init(q);
if (ret) {
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index 9d115cdc6bdb..0ac841e35aa4 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -234,13 +234,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
unsigned long flags;
int ret;
- /* If buffer queue is empty, return error */
spin_lock_irqsave(&common->irqlock, flags);
- if (list_empty(&common->dma_queue)) {
- spin_unlock_irqrestore(&common->irqlock, flags);
- vpif_err("buffer queue is empty\n");
- return -ENOBUFS;
- }
/* Get the next frame from the buffer queue */
common->next_frm = common->cur_frm =
@@ -983,7 +977,8 @@ static int vpif_reqbufs(struct file *file, void *priv,
q->ops = &video_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct vpif_disp_buffer);
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 1;
ret = vb2_queue_init(q);
if (ret) {
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c
index 810c3e13970c..d0ea94f58d6f 100644
--- a/drivers/media/platform/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c
@@ -88,8 +88,12 @@ 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) {
- src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp;
- src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode;
+ dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp;
+ dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode;
+ dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.flags |=
+ src_vb->v4l2_buf.flags
+ & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
v4l2_m2m_buf_done(src_vb, vb_state);
v4l2_m2m_buf_done(dst_vb, vb_state);
@@ -590,7 +594,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->ops = &gsc_m2m_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -603,7 +607,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->ops = &gsc_m2m_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
return vb2_queue_init(dst_vq);
}
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index 01ed1ecdff7e..e1b2ceba00c1 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/drivers/media/platform/exynos4-is/Kconfig
@@ -64,4 +64,13 @@ config VIDEO_EXYNOS4_FIMC_IS
To compile this driver as a module, choose M here: the
module will be called exynos4-fimc-is.
+config VIDEO_EXYNOS4_ISP_DMA_CAPTURE
+ bool "EXYNOS4x12 FIMC-IS ISP Direct DMA capture support"
+ depends on VIDEO_EXYNOS4_FIMC_IS
+ select VIDEO_EXYNOS4_IS_COMMON
+ default y
+ help
+ This option enables an additional video device node exposing a V4L2
+ video capture interface for the FIMC-IS ISP raw (Bayer) capture DMA.
+
endif # VIDEO_SAMSUNG_EXYNOS4_IS
diff --git a/drivers/media/platform/exynos4-is/Makefile b/drivers/media/platform/exynos4-is/Makefile
index c2ff29ba6856..eed1b185d813 100644
--- a/drivers/media/platform/exynos4-is/Makefile
+++ b/drivers/media/platform/exynos4-is/Makefile
@@ -6,6 +6,10 @@ exynos4-is-common-objs := common.o
exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o
exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o
+ifeq ($(CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE),y)
+exynos-fimc-is-objs += fimc-isp-video.o
+endif
+
obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o
obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS) += exynos-fimc-is.o
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
index 8a712ca91d11..92ae812abce2 100644
--- a/drivers/media/platform/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/exynos4-is/fimc-capture.c
@@ -1782,7 +1782,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc,
q->ops = &fimc_capture_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct fimc_vid_buffer);
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &fimc->lock;
ret = vb2_queue_init(q);
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c
index 9bf3ddd9e028..bf1465d1bf6d 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-param.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-param.c
@@ -56,7 +56,7 @@ static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is)
__hw_param_copy(dst, src);
}
-static int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)
+int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)
{
struct is_param_region *par = &is->is_p_region->parameter;
struct chain_config *cfg = &is->config[is->config_index];
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.h b/drivers/media/platform/exynos4-is/fimc-is-param.h
index f9358c27ae2d..8e31f7642776 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-param.h
+++ b/drivers/media/platform/exynos4-is/fimc-is-param.h
@@ -911,6 +911,10 @@ struct is_region {
u32 shared[MAX_SHARED_COUNT];
} __packed;
+/* Offset to the ISP DMA2 output buffer address array. */
+#define DMA2_OUTPUT_ADDR_ARRAY_OFFS \
+ (offsetof(struct is_region, shared) + 32 * sizeof(u32))
+
struct is_debug_frame_descriptor {
u32 sensor_frame_time;
u32 sensor_exposure_time;
@@ -988,6 +992,7 @@ struct sensor_open_extended {
struct fimc_is;
int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is);
+int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset);
void fimc_is_set_initial_params(struct fimc_is *is);
unsigned int __get_pending_param_count(struct fimc_is *is);
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c
index 2628733c4e10..cfe4406a83ff 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c
@@ -105,6 +105,20 @@ int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num_args)
return 0;
}
+void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask)
+{
+ if (hweight32(mask) == 1) {
+ dev_err(&is->pdev->dev, "%s(): not enough buffers (mask %#x)\n",
+ __func__, mask);
+ return;
+ }
+
+ if (mcuctl_read(is, MCUCTL_REG_ISSR(23)) != 0)
+ dev_dbg(&is->pdev->dev, "non-zero DMA buffer mask\n");
+
+ mcuctl_write(mask, is, MCUCTL_REG_ISSR(23));
+}
+
void fimc_is_hw_set_sensor_num(struct fimc_is *is)
{
pr_debug("setting sensor index to: %d\n", is->sensor_index);
@@ -112,7 +126,7 @@ void fimc_is_hw_set_sensor_num(struct fimc_is *is)
mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0));
mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2));
- mcuctl_write(FIMC_IS_SENSOR_NUM, is, MCUCTL_REG_ISSR(3));
+ mcuctl_write(FIMC_IS_SENSORS_NUM, is, MCUCTL_REG_ISSR(3));
}
void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index)
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/exynos4-is/fimc-is-regs.h
index 1d9d4ffc6ad5..141e5ddadbeb 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.h
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.h
@@ -147,6 +147,7 @@ int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num);
void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is);
int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is);
void fimc_is_hw_set_sensor_num(struct fimc_is *is);
+void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask);
void fimc_is_hw_stream_on(struct fimc_is *is);
void fimc_is_hw_stream_off(struct fimc_is *is);
int fimc_is_hw_set_param(struct fimc_is *is);
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/exynos4-is/fimc-is-sensor.c
index 6647421e5d3a..10e82e21b5d1 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-sensor.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.c
@@ -2,276 +2,21 @@
* Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
- *
* Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of_gpio.h>
-#include <linux/pm_runtime.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <media/v4l2-subdev.h>
-#include "fimc-is.h"
#include "fimc-is-sensor.h"
-#define DRIVER_NAME "FIMC-IS-SENSOR"
-
-static const char * const sensor_supply_names[] = {
- "svdda",
- "svddio",
-};
-
-static const struct v4l2_mbus_framefmt fimc_is_sensor_formats[] = {
- {
- .code = V4L2_MBUS_FMT_SGRBG10_1X10,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .field = V4L2_FIELD_NONE,
- }
-};
-
-static const struct v4l2_mbus_framefmt *find_sensor_format(
- struct v4l2_mbus_framefmt *mf)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fimc_is_sensor_formats); i++)
- if (mf->code == fimc_is_sensor_formats[i].code)
- return &fimc_is_sensor_formats[i];
-
- return &fimc_is_sensor_formats[0];
-}
-
-static int fimc_is_sensor_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->index >= ARRAY_SIZE(fimc_is_sensor_formats))
- return -EINVAL;
-
- code->code = fimc_is_sensor_formats[code->index].code;
- return 0;
-}
-
-static void fimc_is_sensor_try_format(struct fimc_is_sensor *sensor,
- struct v4l2_mbus_framefmt *mf)
-{
- const struct sensor_drv_data *dd = sensor->drvdata;
- const struct v4l2_mbus_framefmt *fmt;
-
- fmt = find_sensor_format(mf);
- mf->code = fmt->code;
- v4l_bound_align_image(&mf->width, 16 + 8, dd->width, 0,
- &mf->height, 12 + 8, dd->height, 0, 0);
-}
-
-static struct v4l2_mbus_framefmt *__fimc_is_sensor_get_format(
- struct fimc_is_sensor *sensor, struct v4l2_subdev_fh *fh,
- u32 pad, enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
-
- return &sensor->format;
-}
-
-static int fimc_is_sensor_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
- struct v4l2_mbus_framefmt *mf;
-
- fimc_is_sensor_try_format(sensor, &fmt->format);
-
- mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which);
- if (mf) {
- mutex_lock(&sensor->lock);
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- *mf = fmt->format;
- mutex_unlock(&sensor->lock);
- }
- return 0;
-}
-
-static int fimc_is_sensor_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
- struct v4l2_mbus_framefmt *mf;
-
- mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which);
-
- mutex_lock(&sensor->lock);
- fmt->format = *mf;
- mutex_unlock(&sensor->lock);
- return 0;
-}
-
-static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = {
- .enum_mbus_code = fimc_is_sensor_enum_mbus_code,
- .get_fmt = fimc_is_sensor_get_fmt,
- .set_fmt = fimc_is_sensor_set_fmt,
-};
-
-static int fimc_is_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
-
- *format = fimc_is_sensor_formats[0];
- format->width = FIMC_IS_SENSOR_DEF_PIX_WIDTH;
- format->height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT;
-
- return 0;
-}
-
-static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = {
- .open = fimc_is_sensor_open,
-};
-
-static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on)
-{
- struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
- int gpio = sensor->gpio_reset;
- int ret;
-
- if (on) {
- ret = pm_runtime_get(sensor->dev);
- if (ret < 0)
- return ret;
-
- ret = regulator_bulk_enable(SENSOR_NUM_SUPPLIES,
- sensor->supplies);
- if (ret < 0) {
- pm_runtime_put(sensor->dev);
- return ret;
- }
- if (gpio_is_valid(gpio)) {
- gpio_set_value(gpio, 1);
- usleep_range(600, 800);
- gpio_set_value(gpio, 0);
- usleep_range(10000, 11000);
- gpio_set_value(gpio, 1);
- }
-
- /* A delay needed for the sensor initialization. */
- msleep(20);
- } else {
- if (gpio_is_valid(gpio))
- gpio_set_value(gpio, 0);
-
- ret = regulator_bulk_disable(SENSOR_NUM_SUPPLIES,
- sensor->supplies);
- if (!ret)
- pm_runtime_put(sensor->dev);
- }
-
- pr_info("%s:%d: on: %d, ret: %d\n", __func__, __LINE__, on, ret);
-
- return ret;
-}
-
-static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = {
- .s_power = fimc_is_sensor_s_power,
-};
-
-static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = {
- .core = &fimc_is_sensor_core_ops,
- .pad = &fimc_is_sensor_pad_ops,
-};
-
-static const struct of_device_id fimc_is_sensor_of_match[];
-
-static int fimc_is_sensor_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct device *dev = &client->dev;
- struct fimc_is_sensor *sensor;
- const struct of_device_id *of_id;
- struct v4l2_subdev *sd;
- int gpio, i, ret;
-
- sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
- if (!sensor)
- return -ENOMEM;
-
- mutex_init(&sensor->lock);
- sensor->gpio_reset = -EINVAL;
-
- gpio = of_get_gpio_flags(dev->of_node, 0, NULL);
- if (gpio_is_valid(gpio)) {
- ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW,
- DRIVER_NAME);
- if (ret < 0)
- return ret;
- }
- sensor->gpio_reset = gpio;
-
- for (i = 0; i < SENSOR_NUM_SUPPLIES; i++)
- sensor->supplies[i].supply = sensor_supply_names[i];
-
- ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES,
- sensor->supplies);
- if (ret < 0)
- return ret;
-
- of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node);
- if (!of_id)
- return -ENODEV;
-
- sensor->drvdata = of_id->data;
- sensor->dev = dev;
-
- sd = &sensor->subdev;
- v4l2_i2c_subdev_init(sd, client, &fimc_is_sensor_subdev_ops);
- snprintf(sd->name, sizeof(sd->name), sensor->drvdata->subdev_name);
- sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- sensor->format.code = fimc_is_sensor_formats[0].code;
- sensor->format.width = FIMC_IS_SENSOR_DEF_PIX_WIDTH;
- sensor->format.height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT;
-
- sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
- if (ret < 0)
- return ret;
-
- pm_runtime_no_callbacks(dev);
- pm_runtime_enable(dev);
-
- return ret;
-}
-
-static int fimc_is_sensor_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- media_entity_cleanup(&sd->entity);
- return 0;
-}
-
-static const struct i2c_device_id fimc_is_sensor_ids[] = {
- { }
-};
-
static const struct sensor_drv_data s5k6a3_drvdata = {
.id = FIMC_IS_SENSOR_ID_S5K6A3,
- .subdev_name = "S5K6A3",
- .width = S5K6A3_SENSOR_WIDTH,
- .height = S5K6A3_SENSOR_HEIGHT,
+ .open_timeout = S5K6A3_OPEN_TIMEOUT,
};
-static const struct of_device_id fimc_is_sensor_of_match[] = {
+static const struct of_device_id fimc_is_sensor_of_ids[] = {
{
.compatible = "samsung,s5k6a3",
.data = &s5k6a3_drvdata,
@@ -279,27 +24,11 @@ static const struct of_device_id fimc_is_sensor_of_match[] = {
{ }
};
-static struct i2c_driver fimc_is_sensor_driver = {
- .driver = {
- .of_match_table = fimc_is_sensor_of_match,
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- },
- .probe = fimc_is_sensor_probe,
- .remove = fimc_is_sensor_remove,
- .id_table = fimc_is_sensor_ids,
-};
-
-int fimc_is_register_sensor_driver(void)
+const struct sensor_drv_data *fimc_is_sensor_get_drvdata(
+ struct device_node *node)
{
- return i2c_add_driver(&fimc_is_sensor_driver);
-}
+ const struct of_device_id *of_id;
-void fimc_is_unregister_sensor_driver(void)
-{
- i2c_del_driver(&fimc_is_sensor_driver);
+ of_id = of_match_node(fimc_is_sensor_of_ids, node);
+ return of_id ? of_id->data : NULL;
}
-
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("Exynos4x12 FIMC-IS image sensor subdev driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/exynos4-is/fimc-is-sensor.h
index 6036d49a6c68..173ccffa4bcd 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-sensor.h
+++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.h
@@ -13,24 +13,13 @@
#ifndef FIMC_IS_SENSOR_H_
#define FIMC_IS_SENSOR_H_
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-subdev.h>
-
-#define FIMC_IS_SENSOR_OPEN_TIMEOUT 2000 /* ms */
-
-#define FIMC_IS_SENSOR_DEF_PIX_WIDTH 1296
-#define FIMC_IS_SENSOR_DEF_PIX_HEIGHT 732
+#include <linux/of.h>
+#include <linux/types.h>
+#define S5K6A3_OPEN_TIMEOUT 2000 /* ms */
#define S5K6A3_SENSOR_WIDTH 1392
#define S5K6A3_SENSOR_HEIGHT 1392
-#define SENSOR_NUM_SUPPLIES 2
-
enum fimc_is_sensor_id {
FIMC_IS_SENSOR_ID_S5K3H2 = 1,
FIMC_IS_SENSOR_ID_S5K6A3,
@@ -45,45 +34,23 @@ enum fimc_is_sensor_id {
struct sensor_drv_data {
enum fimc_is_sensor_id id;
- const char * const subdev_name;
- unsigned int width;
- unsigned int height;
+ /* sensor open timeout in ms */
+ unsigned short open_timeout;
};
/**
* struct fimc_is_sensor - fimc-is sensor data structure
- * @dev: pointer to this I2C client device structure
- * @subdev: the image sensor's v4l2 subdev
- * @pad: subdev media source pad
- * @supplies: image sensor's voltage regulator supplies
- * @gpio_reset: GPIO connected to the sensor's reset pin
* @drvdata: a pointer to the sensor's parameters data structure
* @i2c_bus: ISP I2C bus index (0...1)
* @test_pattern: true to enable video test pattern
- * @lock: mutex protecting the structure's members below
- * @format: media bus format at the sensor's source pad
*/
struct fimc_is_sensor {
- struct device *dev;
- struct v4l2_subdev subdev;
- struct media_pad pad;
- struct regulator_bulk_data supplies[SENSOR_NUM_SUPPLIES];
- int gpio_reset;
const struct sensor_drv_data *drvdata;
unsigned int i2c_bus;
- bool test_pattern;
-
- struct mutex lock;
- struct v4l2_mbus_framefmt format;
+ u8 test_pattern;
};
-static inline
-struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct fimc_is_sensor, subdev);
-}
-
-int fimc_is_register_sensor_driver(void);
-void fimc_is_unregister_sensor_driver(void);
+const struct sensor_drv_data *fimc_is_sensor_get_drvdata(
+ struct device_node *node);
#endif /* FIMC_IS_SENSOR_H_ */
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 9bdfa4599bc3..128b73b6cce2 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -161,78 +161,69 @@ static void fimc_is_disable_clocks(struct fimc_is *is)
}
}
-static int fimc_is_parse_sensor_config(struct fimc_is_sensor *sensor,
- struct device_node *np)
+static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index,
+ struct device_node *node)
{
+ struct fimc_is_sensor *sensor = &is->sensor[index];
u32 tmp = 0;
int ret;
- np = of_graph_get_next_endpoint(np, NULL);
- if (!np)
+ sensor->drvdata = fimc_is_sensor_get_drvdata(node);
+ if (!sensor->drvdata) {
+ dev_err(&is->pdev->dev, "no driver data found for: %s\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ node = of_graph_get_next_endpoint(node, NULL);
+ if (!node)
return -ENXIO;
- np = of_graph_get_remote_port(np);
- if (!np)
+
+ node = of_graph_get_remote_port(node);
+ if (!node)
return -ENXIO;
/* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */
- ret = of_property_read_u32(np, "reg", &tmp);
- sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
+ ret = of_property_read_u32(node, "reg", &tmp);
+ if (ret < 0) {
+ dev_err(&is->pdev->dev, "reg property not found at: %s\n",
+ node->full_name);
+ return ret;
+ }
- return ret;
+ sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
+ return 0;
}
static int fimc_is_register_subdevs(struct fimc_is *is)
{
- struct device_node *adapter, *child;
- int ret;
+ struct device_node *i2c_bus, *child;
+ int ret, index = 0;
ret = fimc_isp_subdev_create(&is->isp);
if (ret < 0)
return ret;
- for_each_compatible_node(adapter, NULL, FIMC_IS_I2C_COMPATIBLE) {
- if (!of_find_device_by_node(adapter)) {
- of_node_put(adapter);
- return -EPROBE_DEFER;
- }
-
- for_each_available_child_of_node(adapter, child) {
- struct i2c_client *client;
- struct v4l2_subdev *sd;
-
- client = of_find_i2c_device_by_node(child);
- if (!client)
- goto e_retry;
-
- sd = i2c_get_clientdata(client);
- if (!sd)
- goto e_retry;
+ /* Initialize memory allocator context for the ISP DMA. */
+ is->isp.alloc_ctx = is->alloc_ctx;
- /* FIXME: Add support for multiple sensors. */
- if (WARN_ON(is->sensor))
- continue;
+ for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) {
+ for_each_available_child_of_node(i2c_bus, child) {
+ ret = fimc_is_parse_sensor_config(is, index, child);
- is->sensor = sd_to_fimc_is_sensor(sd);
-
- if (fimc_is_parse_sensor_config(is->sensor, child)) {
- dev_warn(&is->pdev->dev, "DT parse error: %s\n",
- child->full_name);
+ if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) {
+ of_node_put(child);
+ return ret;
}
- pr_debug("%s(): registered subdev: %p\n",
- __func__, sd->name);
+ index++;
}
}
return 0;
-
-e_retry:
- of_node_put(child);
- return -EPROBE_DEFER;
}
static int fimc_is_unregister_subdevs(struct fimc_is *is)
{
fimc_isp_subdev_destroy(&is->isp);
- is->sensor = NULL;
return 0;
}
@@ -647,7 +638,7 @@ static int fimc_is_hw_open_sensor(struct fimc_is *is,
fimc_is_hw_set_intgr0_gd0(is);
return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1,
- FIMC_IS_SENSOR_OPEN_TIMEOUT);
+ sensor->drvdata->open_timeout);
}
@@ -661,8 +652,8 @@ int fimc_is_hw_initialize(struct fimc_is *is)
u32 prev_id;
int i, ret;
- /* Sensor initialization. */
- ret = fimc_is_hw_open_sensor(is, is->sensor);
+ /* Sensor initialization. Only one sensor is currently supported. */
+ ret = fimc_is_hw_open_sensor(is, &is->sensor[0]);
if (ret < 0)
return ret;
@@ -977,27 +968,20 @@ static int fimc_is_module_init(void)
{
int ret;
- ret = fimc_is_register_sensor_driver();
- if (ret < 0)
- return ret;
-
ret = fimc_is_register_i2c_driver();
if (ret < 0)
- goto err_sens;
+ return ret;
ret = platform_driver_register(&fimc_is_driver);
- if (!ret)
- return ret;
- fimc_is_unregister_i2c_driver();
-err_sens:
- fimc_is_unregister_sensor_driver();
+ if (ret < 0)
+ fimc_is_unregister_i2c_driver();
+
return ret;
}
static void fimc_is_module_exit(void)
{
- fimc_is_unregister_sensor_driver();
fimc_is_unregister_i2c_driver();
platform_driver_unregister(&fimc_is_driver);
}
diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h
index 61bb0127e19d..e0be691af2d3 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.h
+++ b/drivers/media/platform/exynos4-is/fimc-is.h
@@ -39,7 +39,7 @@
#define FIMC_IS_FW_LOAD_TIMEOUT 1000 /* ms */
#define FIMC_IS_POWER_ON_TIMEOUT 1000 /* us */
-#define FIMC_IS_SENSOR_NUM 2
+#define FIMC_IS_SENSORS_NUM 2
/* Memory definitions */
#define FIMC_IS_CPU_MEM_SIZE (0xa00000)
@@ -253,7 +253,7 @@ struct fimc_is {
struct firmware *f_w;
struct fimc_isp isp;
- struct fimc_is_sensor *sensor;
+ struct fimc_is_sensor sensor[FIMC_IS_SENSORS_NUM];
struct fimc_is_setfile setfile;
struct vb2_alloc_ctx *alloc_ctx;
@@ -292,6 +292,11 @@ static inline struct fimc_is *fimc_isp_to_is(struct fimc_isp *isp)
return container_of(isp, struct fimc_is, isp);
}
+static inline struct chain_config *__get_curr_is_config(struct fimc_is *is)
+{
+ return &is->config[is->config_index];
+}
+
static inline void fimc_is_mem_barrier(void)
{
mb();
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c
new file mode 100644
index 000000000000..e92b4e115adb
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c
@@ -0,0 +1,660 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * FIMC-IS ISP video input and video output DMA interface driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * The hardware handling code derived from a driver written by
+ * Younghwan Joo <yhwan.joo@samsung.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/printk.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/s5p_fimc.h>
+
+#include "common.h"
+#include "media-dev.h"
+#include "fimc-is.h"
+#include "fimc-isp-video.h"
+#include "fimc-is-param.h"
+
+static int isp_video_capture_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ 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;
+ 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;
+ }
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ *num_buffers = clamp_t(u32, *num_buffers, FIMC_ISP_REQ_BUFS_MIN,
+ FIMC_ISP_REQ_BUFS_MAX);
+ *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;
+ allocators[i] = isp->alloc_ctx;
+ }
+
+ return 0;
+}
+
+static inline struct param_dma_output *__get_isp_dma2(struct fimc_is *is)
+{
+ return &__get_curr_is_config(is)->isp.dma2_output;
+}
+
+static int isp_video_capture_start_streaming(struct vb2_queue *q,
+ unsigned int count)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(q);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct param_dma_output *dma = __get_isp_dma2(is);
+ struct fimc_is_video *video = &isp->video_capture;
+ int ret;
+
+ if (!test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state) ||
+ test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state))
+ return 0;
+
+
+ dma->cmd = DMA_OUTPUT_COMMAND_ENABLE;
+ dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE;
+ dma->buffer_address = is->is_dma_p_region +
+ DMA2_OUTPUT_ADDR_ARRAY_OFFS;
+ dma->buffer_number = video->reqbufs_count;
+ dma->dma_out_mask = video->buf_mask;
+
+ isp_dbg(2, &video->ve.vdev,
+ "buf_count: %d, planes: %d, dma addr table: %#x\n",
+ video->buf_count, video->format->memplanes,
+ dma->buffer_address);
+
+ fimc_is_mem_barrier();
+
+ fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT);
+ __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT);
+
+ ret = fimc_is_itf_s_param(is, false);
+ if (ret < 0)
+ return ret;
+
+ ret = fimc_pipeline_call(&video->ve, set_stream, 1);
+ if (ret < 0)
+ return ret;
+
+ set_bit(ST_ISP_VID_CAP_STREAMING, &isp->state);
+ return ret;
+}
+
+static int isp_video_capture_stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(q);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct param_dma_output *dma = __get_isp_dma2(is);
+ int ret;
+
+ ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0);
+ if (ret < 0)
+ return ret;
+
+ dma->cmd = DMA_OUTPUT_COMMAND_DISABLE;
+ dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE;
+ dma->buffer_number = 0;
+ dma->buffer_address = 0;
+ dma->dma_out_mask = 0;
+
+ fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT);
+ __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT);
+
+ ret = fimc_is_itf_s_param(is, false);
+ if (ret < 0)
+ dev_warn(&is->pdev->dev, "%s: DMA stop failed\n", __func__);
+
+ fimc_is_hw_set_isp_buf_mask(is, 0);
+
+ clear_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state);
+ clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state);
+
+ isp->video_capture.buf_count = 0;
+ return 0;
+}
+
+static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_is_video *video = &isp->video_capture;
+ int i;
+
+ if (video->format == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < video->format->memplanes; i++) {
+ unsigned long size = video->pixfmt.plane_fmt[i].sizeimage;
+
+ if (vb2_plane_size(vb, i) < size) {
+ v4l2_err(&video->ve.vdev,
+ "User buffer too small (%ld < %ld)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ /* Check if we get one of the already known buffers. */
+ if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) {
+ dma_addr_t dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ int i;
+
+ for (i = 0; i < video->buf_count; i++)
+ if (video->buffers[i]->dma_addr[0] == dma_addr)
+ return 0;
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static void isp_video_capture_buffer_queue(struct vb2_buffer *vb)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_is_video *video = &isp->video_capture;
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct isp_video_buf *ivb = to_isp_video_buf(vb);
+ unsigned long flags;
+ unsigned int i;
+
+ if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) {
+ spin_lock_irqsave(&is->slock, flags);
+ video->buf_mask |= BIT(ivb->index);
+ spin_unlock_irqrestore(&is->slock, flags);
+ } else {
+ unsigned int num_planes = video->format->memplanes;
+
+ ivb->index = video->buf_count;
+ video->buffers[ivb->index] = ivb;
+
+ for (i = 0; i < num_planes; i++) {
+ int buf_index = ivb->index * num_planes + i;
+
+ ivb->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+ is->is_p_region->shared[32 + buf_index] =
+ ivb->dma_addr[i];
+
+ isp_dbg(2, &video->ve.vdev,
+ "dma_buf %d (%d/%d/%d) addr: %#x\n",
+ buf_index, ivb->index, i, vb->v4l2_buf.index,
+ ivb->dma_addr[i]);
+ }
+
+ if (++video->buf_count < video->reqbufs_count)
+ return;
+
+ video->buf_mask = (1UL << video->buf_count) - 1;
+ set_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state);
+ }
+
+ if (!test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state))
+ isp_video_capture_start_streaming(vb->vb2_queue, 0);
+}
+
+/*
+ * FIMC-IS ISP input and output DMA interface interrupt handler.
+ * Locking: called with is->slock spinlock held.
+ */
+void fimc_isp_video_irq_handler(struct fimc_is *is)
+{
+ struct fimc_is_video *video = &is->isp.video_capture;
+ struct vb2_buffer *vb;
+ int buf_index;
+
+ /* TODO: Ensure the DMA is really stopped in stop_streaming callback */
+ if (!test_bit(ST_ISP_VID_CAP_STREAMING, &is->isp.state))
+ return;
+
+ buf_index = (is->i2h_cmd.args[1] - 1) % video->buf_count;
+ vb = &video->buffers[buf_index]->vb;
+
+ v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+
+ video->buf_mask &= ~BIT(buf_index);
+ fimc_is_hw_set_isp_buf_mask(is, video->buf_mask);
+}
+
+static const struct vb2_ops isp_video_capture_qops = {
+ .queue_setup = isp_video_capture_queue_setup,
+ .buf_prepare = isp_video_capture_buffer_prepare,
+ .buf_queue = isp_video_capture_buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = isp_video_capture_start_streaming,
+ .stop_streaming = isp_video_capture_stop_streaming,
+};
+
+static int isp_video_open(struct file *file)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct exynos_video_entity *ve = &isp->video_capture.ve;
+ struct media_entity *me = &ve->vdev.entity;
+ int ret;
+
+ if (mutex_lock_interruptible(&isp->video_lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_fh_open(file);
+ if (ret < 0)
+ goto unlock;
+
+ ret = pm_runtime_get_sync(&isp->pdev->dev);
+ if (ret < 0)
+ goto rel_fh;
+
+ if (v4l2_fh_is_singular_file(file)) {
+ mutex_lock(&me->parent->graph_mutex);
+
+ ret = fimc_pipeline_call(ve, open, me, true);
+
+ /* Mark the video pipeline as in use. */
+ if (ret == 0)
+ me->use_count++;
+
+ mutex_unlock(&me->parent->graph_mutex);
+ }
+ if (!ret)
+ goto unlock;
+rel_fh:
+ v4l2_fh_release(file);
+unlock:
+ mutex_unlock(&isp->video_lock);
+ return ret;
+}
+
+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;
+ int ret = 0;
+
+ mutex_lock(&isp->video_lock);
+
+ if (v4l2_fh_is_singular_file(file) && ivc->streaming) {
+ media_entity_pipeline_stop(entity);
+ ivc->streaming = 0;
+ }
+
+ vb2_fop_release(file);
+
+ if (v4l2_fh_is_singular_file(file)) {
+ fimc_pipeline_call(&ivc->ve, close);
+
+ mutex_lock(&mdev->graph_mutex);
+ entity->use_count--;
+ mutex_unlock(&mdev->graph_mutex);
+ }
+
+ pm_runtime_put(&isp->pdev->dev);
+ mutex_unlock(&isp->video_lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations isp_video_fops = {
+ .owner = THIS_MODULE,
+ .open = isp_video_open,
+ .release = isp_video_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+/*
+ * Video node ioctl operations
+ */
+static int isp_video_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+
+ __fimc_vidioc_querycap(&isp->pdev->dev, cap, V4L2_CAP_STREAMING);
+ return 0;
+}
+
+static int isp_video_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ const struct fimc_fmt *fmt;
+
+ if (f->index >= FIMC_ISP_NUM_FORMATS)
+ return -EINVAL;
+
+ fmt = fimc_isp_find_format(NULL, NULL, f->index);
+ if (WARN_ON(fmt == NULL))
+ return -EINVAL;
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int isp_video_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+
+ f->fmt.pix_mp = isp->video_capture.pixfmt;
+ return 0;
+}
+
+static void __isp_video_try_fmt(struct fimc_isp *isp,
+ struct v4l2_pix_format_mplane *pixm,
+ const struct fimc_fmt **fmt)
+{
+ *fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2);
+
+ pixm->colorspace = V4L2_COLORSPACE_SRGB;
+ pixm->field = V4L2_FIELD_NONE;
+ pixm->num_planes = (*fmt)->memplanes;
+ pixm->pixelformat = (*fmt)->fourcc;
+ /*
+ * TODO: double check with the docmentation these width/height
+ * constraints are correct.
+ */
+ v4l_bound_align_image(&pixm->width, FIMC_ISP_SOURCE_WIDTH_MIN,
+ FIMC_ISP_SOURCE_WIDTH_MAX, 3,
+ &pixm->height, FIMC_ISP_SOURCE_HEIGHT_MIN,
+ FIMC_ISP_SOURCE_HEIGHT_MAX, 0, 0);
+}
+
+static int isp_video_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+
+ __isp_video_try_fmt(isp, &f->fmt.pix_mp, NULL);
+ return 0;
+}
+
+static int isp_video_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ const struct fimc_fmt *ifmt = NULL;
+ struct param_dma_output *dma = __get_isp_dma2(is);
+
+ __isp_video_try_fmt(isp, pixm, &ifmt);
+
+ if (WARN_ON(ifmt == NULL))
+ return -EINVAL;
+
+ dma->format = DMA_OUTPUT_FORMAT_BAYER;
+ dma->order = DMA_OUTPUT_ORDER_GB_BG;
+ dma->plane = ifmt->memplanes;
+ dma->bitwidth = ifmt->depth[0];
+ dma->width = pixm->width;
+ dma->height = pixm->height;
+
+ fimc_is_mem_barrier();
+
+ isp->video_capture.format = ifmt;
+ isp->video_capture.pixfmt = *pixm;
+
+ return 0;
+}
+
+/*
+ * Check for source/sink format differences at each link.
+ * Return 0 if the formats match or -EPIPE otherwise.
+ */
+static int isp_video_pipeline_validate(struct fimc_isp *isp)
+{
+ struct v4l2_subdev *sd = &isp->subdev;
+ struct v4l2_subdev_format sink_fmt, src_fmt;
+ struct media_pad *pad;
+ int ret;
+
+ while (1) {
+ /* Retrieve format at the sink pad */
+ pad = &sd->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+ sink_fmt.pad = pad->index;
+ sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ /* 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)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ src_fmt.pad = pad->index;
+ src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ if (src_fmt.format.width != sink_fmt.format.width ||
+ src_fmt.format.height != sink_fmt.format.height ||
+ src_fmt.format.code != sink_fmt.format.code)
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+static int isp_video_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct exynos_video_entity *ve = &isp->video_capture.ve;
+ struct media_entity *me = &ve->vdev.entity;
+ int ret;
+
+ ret = media_entity_pipeline_start(me, &ve->pipe->mp);
+ if (ret < 0)
+ return ret;
+
+ ret = isp_video_pipeline_validate(isp);
+ if (ret < 0)
+ goto p_stop;
+
+ ret = vb2_ioctl_streamon(file, priv, type);
+ if (ret < 0)
+ goto p_stop;
+
+ isp->video_capture.streaming = 1;
+ return 0;
+p_stop:
+ media_entity_pipeline_stop(me);
+ return ret;
+}
+
+static int isp_video_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct fimc_is_video *video = &isp->video_capture;
+ int ret;
+
+ ret = vb2_ioctl_streamoff(file, priv, type);
+ if (ret < 0)
+ return ret;
+
+ media_entity_pipeline_stop(&video->ve.vdev.entity);
+ video->streaming = 0;
+ return 0;
+}
+
+static int isp_video_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ int ret;
+
+ ret = vb2_ioctl_reqbufs(file, priv, rb);
+ if (ret < 0)
+ return ret;
+
+ if (rb->count && rb->count < FIMC_ISP_REQ_BUFS_MIN) {
+ rb->count = 0;
+ vb2_ioctl_reqbufs(file, priv, rb);
+ ret = -ENOMEM;
+ }
+
+ isp->video_capture.reqbufs_count = rb->count;
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
+ .vidioc_querycap = isp_video_querycap,
+ .vidioc_enum_fmt_vid_cap_mplane = isp_video_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = isp_video_try_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = isp_video_s_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = isp_video_g_fmt_mplane,
+ .vidioc_reqbufs = isp_video_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = isp_video_streamon,
+ .vidioc_streamoff = isp_video_streamoff,
+};
+
+int fimc_isp_video_device_register(struct fimc_isp *isp,
+ struct v4l2_device *v4l2_dev,
+ enum v4l2_buf_type type)
+{
+ struct vb2_queue *q = &isp->video_capture.vb_queue;
+ struct fimc_is_video *iv;
+ struct video_device *vdev;
+ int ret;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ iv = &isp->video_capture;
+ else
+ return -ENOSYS;
+
+ mutex_init(&isp->video_lock);
+ INIT_LIST_HEAD(&iv->pending_buf_q);
+ INIT_LIST_HEAD(&iv->active_buf_q);
+ iv->format = fimc_isp_find_format(NULL, NULL, 0);
+ iv->pixfmt.width = IS_DEFAULT_WIDTH;
+ iv->pixfmt.height = IS_DEFAULT_HEIGHT;
+ iv->pixfmt.pixelformat = iv->format->fourcc;
+ iv->pixfmt.colorspace = V4L2_COLORSPACE_SRGB;
+ iv->reqbufs_count = 0;
+
+ memset(q, 0, sizeof(*q));
+ q->type = type;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = &isp_video_capture_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct isp_video_buf);
+ q->drv_priv = isp;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &isp->video_lock;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0)
+ return ret;
+
+ vdev = &iv->ve.vdev;
+ memset(vdev, 0, sizeof(*vdev));
+ snprintf(vdev->name, sizeof(vdev->name), "fimc-is-isp.%s",
+ type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ?
+ "capture" : "output");
+ vdev->queue = q;
+ vdev->fops = &isp_video_fops;
+ vdev->ioctl_ops = &isp_video_ioctl_ops;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->minor = -1;
+ vdev->release = video_device_release_empty;
+ vdev->lock = &isp->video_lock;
+
+ iv->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vdev->entity, 1, &iv->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ video_set_drvdata(vdev, isp);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ media_entity_cleanup(&vdev->entity);
+ return ret;
+ }
+
+ v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
+ vdev->name, video_device_node_name(vdev));
+
+ return 0;
+}
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+ enum v4l2_buf_type type)
+{
+ struct exynos_video_entity *ve;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ ve = &isp->video_capture.ve;
+ else
+ return;
+
+ mutex_lock(&isp->video_lock);
+
+ if (video_is_registered(&ve->vdev)) {
+ video_unregister_device(&ve->vdev);
+ media_entity_cleanup(&ve->vdev.entity);
+ ve->pipe = NULL;
+ }
+
+ mutex_unlock(&isp->video_lock);
+}
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.h b/drivers/media/platform/exynos4-is/fimc-isp-video.h
new file mode 100644
index 000000000000..98c662654bb6
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-isp-video.h
@@ -0,0 +1,44 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FIMC_ISP_VIDEO__
+#define FIMC_ISP_VIDEO__
+
+#include <media/videobuf2-core.h>
+#include "fimc-isp.h"
+
+#ifdef CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE
+int fimc_isp_video_device_register(struct fimc_isp *isp,
+ struct v4l2_device *v4l2_dev,
+ enum v4l2_buf_type type);
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+ enum v4l2_buf_type type);
+
+void fimc_isp_video_irq_handler(struct fimc_is *is);
+#else
+static inline void fimc_isp_video_irq_handler(struct fimc_is *is)
+{
+}
+
+static inline int fimc_isp_video_device_register(struct fimc_isp *isp,
+ struct v4l2_device *v4l2_dev,
+ enum v4l2_buf_type type)
+{
+ return 0;
+}
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+ enum v4l2_buf_type type)
+{
+}
+#endif /* !CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE */
+
+#endif /* FIMC_ISP_VIDEO__ */
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c
index f3c6136aa5b4..be62d6b9ac48 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp.c
@@ -25,6 +25,7 @@
#include <media/v4l2-device.h>
#include "media-dev.h"
+#include "fimc-isp-video.h"
#include "fimc-is-command.h"
#include "fimc-is-param.h"
#include "fimc-is-regs.h"
@@ -93,8 +94,8 @@ void fimc_isp_irq_handler(struct fimc_is *is)
is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21));
fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP);
+ fimc_isp_video_irq_handler(is);
- /* TODO: Complete ISP DMA interrupt handler */
wake_up(&is->irq_queue);
}
@@ -388,7 +389,33 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd,
return 0;
}
+static int fimc_isp_subdev_registered(struct v4l2_subdev *sd)
+{
+ struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+ int ret;
+
+ /* Use pipeline object allocated by the media device. */
+ isp->video_capture.ve.pipe = v4l2_get_subdev_hostdata(sd);
+
+ ret = fimc_isp_video_device_register(isp, sd->v4l2_dev,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret < 0)
+ isp->video_capture.ve.pipe = NULL;
+
+ return ret;
+}
+
+static void fimc_isp_subdev_unregistered(struct v4l2_subdev *sd)
+{
+ struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+
+ fimc_isp_video_device_unregister(isp,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+}
+
static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = {
+ .registered = fimc_isp_subdev_registered,
+ .unregistered = fimc_isp_subdev_unregistered,
.open = fimc_isp_subdev_open,
};
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h
index 03bf95ab017b..4dc55a18d978 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.h
+++ b/drivers/media/platform/exynos4-is/fimc-isp.h
@@ -35,17 +35,18 @@ extern int fimc_isp_debug;
#define FIMC_ISP_SINK_WIDTH_MIN (16 + 8)
#define FIMC_ISP_SINK_HEIGHT_MIN (12 + 8)
#define FIMC_ISP_SOURCE_WIDTH_MIN 8
-#define FIMC_ISP_SOURC_HEIGHT_MIN 8
+#define FIMC_ISP_SOURCE_HEIGHT_MIN 8
#define FIMC_ISP_CAC_MARGIN_WIDTH 16
#define FIMC_ISP_CAC_MARGIN_HEIGHT 12
#define FIMC_ISP_SINK_WIDTH_MAX (4000 - 16)
#define FIMC_ISP_SINK_HEIGHT_MAX (4000 + 12)
#define FIMC_ISP_SOURCE_WIDTH_MAX 4000
-#define FIMC_ISP_SOURC_HEIGHT_MAX 4000
+#define FIMC_ISP_SOURCE_HEIGHT_MAX 4000
#define FIMC_ISP_NUM_FORMATS 3
#define FIMC_ISP_REQ_BUFS_MIN 2
+#define FIMC_ISP_REQ_BUFS_MAX 32
#define FIMC_ISP_SD_PAD_SINK 0
#define FIMC_ISP_SD_PAD_SRC_FIFO 1
@@ -100,6 +101,16 @@ struct fimc_isp_ctrls {
struct v4l2_ctrl *colorfx;
};
+struct isp_video_buf {
+ struct vb2_buffer vb;
+ dma_addr_t dma_addr[FIMC_ISP_MAX_PLANES];
+ unsigned int index;
+};
+
+#define to_isp_video_buf(_b) container_of(_b, struct isp_video_buf, vb)
+
+#define FIMC_ISP_MAX_BUFS 4
+
/**
* struct fimc_is_video - fimc-is video device structure
* @vdev: video_device structure
@@ -114,18 +125,26 @@ struct fimc_isp_ctrls {
* @format: current pixel format
*/
struct fimc_is_video {
- struct video_device vdev;
+ struct exynos_video_entity ve;
enum v4l2_buf_type type;
struct media_pad pad;
struct list_head pending_buf_q;
struct list_head active_buf_q;
struct vb2_queue vb_queue;
- unsigned int frame_count;
unsigned int reqbufs_count;
+ unsigned int buf_count;
+ unsigned int buf_mask;
+ unsigned int frame_count;
int streaming;
+ struct isp_video_buf *buffers[FIMC_ISP_MAX_BUFS];
const struct fimc_fmt *format;
+ struct v4l2_pix_format_mplane pixfmt;
};
+/* struct fimc_isp:state bit definitions */
+#define ST_ISP_VID_CAP_BUF_PREP 0
+#define ST_ISP_VID_CAP_STREAMING 1
+
/**
* struct fimc_isp - FIMC-IS ISP data structure
* @pdev: pointer to FIMC-IS platform device
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c
index 779ec3cd259d..3ad660b55b6b 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite.c
@@ -1313,7 +1313,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct flite_buffer);
q->drv_priv = fimc;
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &fimc->lock;
ret = vb2_queue_init(q);
diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c
index 9da95bd14820..36971d915b53 100644
--- a/drivers/media/platform/exynos4-is/fimc-m2m.c
+++ b/drivers/media/platform/exynos4-is/fimc-m2m.c
@@ -134,6 +134,9 @@ static void fimc_device_run(void *priv)
goto dma_unlock;
dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp;
+ dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.flags |=
+ src_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
/* Reconfigure hardware if the context has changed. */
if (fimc->m2m.ctx != ctx) {
@@ -557,7 +560,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->ops = &fimc_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->fimc_dev->lock;
ret = vb2_queue_init(src_vq);
@@ -570,7 +573,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->ops = &fimc_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->lock = &ctx->fimc_dev->lock;
return vb2_queue_init(dst_vq);
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index 04d6ecdd314c..e62211a80f0e 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -11,6 +11,8 @@
*/
#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/i2c.h>
@@ -25,6 +27,7 @@
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include <linux/slab.h>
+#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-of.h>
#include <media/media-device.h>
@@ -219,6 +222,7 @@ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep,
if (ret < 0)
return ret;
}
+
ret = fimc_md_set_camclk(sd, true);
if (ret < 0)
goto err_wbclk;
@@ -379,77 +383,18 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct i2c_adapter *adapter;
- if (!client)
+ if (!client || client->dev.of_node)
return;
v4l2_device_unregister_subdev(sd);
- if (!client->dev.of_node) {
- adapter = client->adapter;
- i2c_unregister_device(client);
- if (adapter)
- i2c_put_adapter(adapter);
- }
+ adapter = client->adapter;
+ i2c_unregister_device(client);
+ if (adapter)
+ i2c_put_adapter(adapter);
}
#ifdef CONFIG_OF
-/* Register I2C client subdev associated with @node. */
-static int fimc_md_of_add_sensor(struct fimc_md *fmd,
- struct device_node *node, int index)
-{
- struct fimc_sensor_info *si;
- struct i2c_client *client;
- struct v4l2_subdev *sd;
- int ret;
-
- if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
- return -EINVAL;
- si = &fmd->sensor[index];
-
- client = of_find_i2c_device_by_node(node);
- if (!client)
- return -EPROBE_DEFER;
-
- device_lock(&client->dev);
-
- if (!client->dev.driver ||
- !try_module_get(client->dev.driver->owner)) {
- ret = -EPROBE_DEFER;
- v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n",
- node->full_name);
- goto dev_put;
- }
-
- /* Enable sensor's master clock */
- ret = __fimc_md_set_camclk(fmd, &si->pdata, true);
- if (ret < 0)
- goto mod_put;
- sd = i2c_get_clientdata(client);
-
- ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
- __fimc_md_set_camclk(fmd, &si->pdata, false);
- if (ret < 0)
- goto mod_put;
-
- v4l2_set_subdev_hostdata(sd, &si->pdata);
- if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
- sd->grp_id = GRP_ID_FIMC_IS_SENSOR;
- else
- sd->grp_id = GRP_ID_SENSOR;
-
- si->subdev = sd;
- v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
- sd->name, fmd->num_sensors);
- fmd->num_sensors++;
-
-mod_put:
- module_put(client->dev.driver->owner);
-dev_put:
- device_unlock(&client->dev);
- put_device(&client->dev);
- return ret;
-}
-
/* Parse port node and register as a sub-device any sensor specified there. */
static int fimc_md_parse_port_node(struct fimc_md *fmd,
struct device_node *port,
@@ -458,7 +403,6 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
struct device_node *rem, *ep, *np;
struct fimc_source_info *pd;
struct v4l2_of_endpoint endpoint;
- int ret;
u32 val;
pd = &fmd->sensor[index].pdata;
@@ -486,6 +430,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
if (!of_property_read_u32(rem, "clock-frequency", &val))
pd->clk_frequency = val;
+ else
+ pd->clk_frequency = DEFAULT_SENSOR_CLK_FREQ;
if (pd->clk_frequency == 0) {
v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n",
@@ -525,10 +471,17 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
else
pd->fimc_bus_type = pd->sensor_bus_type;
- ret = fimc_md_of_add_sensor(fmd, rem, index);
- of_node_put(rem);
+ if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
+ return -EINVAL;
- return ret;
+ fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF;
+ fmd->sensor[index].asd.match.of.node = rem;
+ fmd->async_subdevs[index] = &fmd->sensor[index].asd;
+
+ fmd->num_sensors++;
+
+ of_node_put(rem);
+ return 0;
}
/* Register all SoC external sub-devices */
@@ -732,8 +685,16 @@ static int register_csis_entity(struct fimc_md *fmd,
static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is)
{
struct v4l2_subdev *sd = &is->isp.subdev;
+ struct exynos_media_pipeline *ep;
int ret;
+ /* Allocate pipeline object for the ISP capture video node. */
+ ep = fimc_md_pipeline_create(fmd);
+ if (!ep)
+ return -ENOMEM;
+
+ v4l2_set_subdev_hostdata(sd, ep);
+
ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
if (ret) {
v4l2_err(&fmd->v4l2_dev,
@@ -884,11 +845,13 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
v4l2_device_unregister_subdev(fmd->csis[i].sd);
fmd->csis[i].sd = NULL;
}
- for (i = 0; i < fmd->num_sensors; i++) {
- if (fmd->sensor[i].subdev == NULL)
- continue;
- fimc_md_unregister_sensor(fmd->sensor[i].subdev);
- fmd->sensor[i].subdev = NULL;
+ if (fmd->pdev->dev.of_node == NULL) {
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+ fimc_md_unregister_sensor(fmd->sensor[i].subdev);
+ fmd->sensor[i].subdev = NULL;
+ }
}
if (fmd->fimc_is)
@@ -1005,16 +968,17 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
/* Create FIMC-IS links */
static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)
{
+ struct fimc_isp *isp = &fmd->fimc_is->isp;
struct media_entity *source, *sink;
int i, ret;
- source = &fmd->fimc_is->isp.subdev.entity;
+ source = &isp->subdev.entity;
for (i = 0; i < FIMC_MAX_DEVS; i++) {
if (fmd->fimc[i] == NULL)
continue;
- /* Link from IS-ISP subdev to FIMC */
+ /* 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,
sink, FIMC_SD_PAD_SINK_FIFO, 0);
@@ -1022,7 +986,15 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)
return ret;
}
- return ret;
+ /* Link from FIMC-IS-ISP subdev to fimc-is-isp.capture video node */
+ sink = &isp->video_capture.ve.vdev.entity;
+
+ /* Skip this link if the fimc-is-isp video node driver isn't built-in */
+ if (sink->num_pads == 0)
+ return 0;
+
+ return media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_DMA,
+ sink, 0, 0);
}
/**
@@ -1223,6 +1195,14 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd,
struct fimc_camclk_info *camclk;
int ret = 0;
+ /*
+ * When device tree is used the sensor drivers are supposed to
+ * control the clock themselves. This whole function will be
+ * removed once S5PV210 platform is converted to the device tree.
+ */
+ if (fmd->pdev->dev.of_node)
+ return 0;
+
if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf)
return -EINVAL;
@@ -1277,6 +1257,14 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd);
struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
+ /*
+ * If there is a clock provider registered the sensors will
+ * handle their clock themselves, no need to control it on
+ * the host interface side.
+ */
+ if (fmd->clk_provider.num_clocks > 0)
+ return 0;
+
return __fimc_md_set_camclk(fmd, si, on);
}
@@ -1438,6 +1426,153 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd)
return 0;
}
+#ifdef CONFIG_OF
+static int cam_clk_prepare(struct clk_hw *hw)
+{
+ struct cam_clk *camclk = to_cam_clk(hw);
+ int ret;
+
+ if (camclk->fmd->pmf == NULL)
+ return -ENODEV;
+
+ ret = pm_runtime_get_sync(camclk->fmd->pmf);
+ return ret < 0 ? ret : 0;
+}
+
+static void cam_clk_unprepare(struct clk_hw *hw)
+{
+ struct cam_clk *camclk = to_cam_clk(hw);
+
+ if (camclk->fmd->pmf == NULL)
+ return;
+
+ pm_runtime_put_sync(camclk->fmd->pmf);
+}
+
+static const struct clk_ops cam_clk_ops = {
+ .prepare = cam_clk_prepare,
+ .unprepare = cam_clk_unprepare,
+};
+
+static void fimc_md_unregister_clk_provider(struct fimc_md *fmd)
+{
+ struct cam_clk_provider *cp = &fmd->clk_provider;
+ unsigned int i;
+
+ if (cp->of_node)
+ of_clk_del_provider(cp->of_node);
+
+ for (i = 0; i < cp->num_clocks; i++)
+ clk_unregister(cp->clks[i]);
+}
+
+static int fimc_md_register_clk_provider(struct fimc_md *fmd)
+{
+ struct cam_clk_provider *cp = &fmd->clk_provider;
+ struct device *dev = &fmd->pdev->dev;
+ int i, ret;
+
+ for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
+ struct cam_clk *camclk = &cp->camclk[i];
+ struct clk_init_data init;
+ const char *p_name;
+
+ ret = of_property_read_string_index(dev->of_node,
+ "clock-output-names", i, &init.name);
+ if (ret < 0)
+ break;
+
+ p_name = __clk_get_name(fmd->camclk[i].clock);
+
+ /* It's safe since clk_register() will duplicate the string. */
+ init.parent_names = &p_name;
+ init.num_parents = 1;
+ init.ops = &cam_clk_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ camclk->hw.init = &init;
+ camclk->fmd = fmd;
+
+ cp->clks[i] = clk_register(NULL, &camclk->hw);
+ if (IS_ERR(cp->clks[i])) {
+ dev_err(dev, "failed to register clock: %s (%ld)\n",
+ init.name, PTR_ERR(cp->clks[i]));
+ ret = PTR_ERR(cp->clks[i]);
+ goto err;
+ }
+ cp->num_clocks++;
+ }
+
+ if (cp->num_clocks == 0) {
+ dev_warn(dev, "clk provider not registered\n");
+ return 0;
+ }
+
+ cp->clk_data.clks = cp->clks;
+ cp->clk_data.clk_num = cp->num_clocks;
+ cp->of_node = dev->of_node;
+ ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
+ &cp->clk_data);
+ if (ret == 0)
+ return 0;
+err:
+ fimc_md_unregister_clk_provider(fmd);
+ return ret;
+}
+#else
+#define fimc_md_register_clk_provider(fmd) (0)
+#define fimc_md_unregister_clk_provider(fmd) (0)
+#endif
+
+static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct fimc_md *fmd = notifier_to_fimc_md(notifier);
+ struct fimc_sensor_info *si = NULL;
+ int i;
+
+ /* Find platform data for this sensor subdev */
+ for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
+ if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
+ si = &fmd->sensor[i];
+
+ if (si == NULL)
+ return -EINVAL;
+
+ v4l2_set_subdev_hostdata(subdev, &si->pdata);
+
+ if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
+ subdev->grp_id = GRP_ID_FIMC_IS_SENSOR;
+ else
+ subdev->grp_id = GRP_ID_SENSOR;
+
+ si->subdev = subdev;
+
+ v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
+ subdev->name, fmd->num_sensors);
+
+ fmd->num_sensors++;
+
+ return 0;
+}
+
+static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct fimc_md *fmd = notifier_to_fimc_md(notifier);
+ int ret;
+
+ mutex_lock(&fmd->media_dev.graph_mutex);
+
+ ret = fimc_md_create_links(fmd);
+ if (ret < 0)
+ goto unlock;
+
+ ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
+unlock:
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ return ret;
+}
+
static int fimc_md_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1470,63 +1605,91 @@ static int fimc_md_probe(struct platform_device *pdev)
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_md;
+ goto err_v4l2_dev;
}
+
ret = fimc_md_get_clocks(fmd);
if (ret)
- goto err_clk;
+ goto err_md;
fmd->user_subdev_api = (dev->of_node != NULL);
- /* Protect the media graph while we're registering entities */
- mutex_lock(&fmd->media_dev.graph_mutex);
-
ret = fimc_md_get_pinctrl(fmd);
if (ret < 0) {
if (ret != EPROBE_DEFER)
dev_err(dev, "Failed to get pinctrl: %d\n", ret);
- goto err_unlock;
+ goto err_clk;
}
+ platform_set_drvdata(pdev, fmd);
+
+ /* Protect the media graph while we're registering entities */
+ mutex_lock(&fmd->media_dev.graph_mutex);
+
if (dev->of_node)
ret = fimc_md_register_of_platform_entities(fmd, dev->of_node);
else
ret = bus_for_each_dev(&platform_bus_type, NULL, fmd,
fimc_md_pdev_match);
- if (ret)
- goto err_unlock;
+ if (ret) {
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ goto err_clk;
+ }
if (dev->platform_data || dev->of_node) {
ret = fimc_md_register_sensor_entities(fmd);
- if (ret)
- goto err_unlock;
+ if (ret) {
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ goto err_m_ent;
+ }
}
- ret = fimc_md_create_links(fmd);
- if (ret)
- goto err_unlock;
- ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
- if (ret)
- goto err_unlock;
+ mutex_unlock(&fmd->media_dev.graph_mutex);
ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
if (ret)
- goto err_unlock;
+ goto err_m_ent;
+ /*
+ * FIMC platform devices need to be registered before the sclk_cam
+ * clocks provider, as one of these devices needs to be activated
+ * to enable the clock.
+ */
+ ret = fimc_md_register_clk_provider(fmd);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "clock provider registration failed\n");
+ goto err_attr;
+ }
+
+ if (fmd->num_sensors > 0) {
+ fmd->subdev_notifier.subdevs = fmd->async_subdevs;
+ fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
+ fmd->subdev_notifier.bound = subdev_notifier_bound;
+ fmd->subdev_notifier.complete = subdev_notifier_complete;
+ fmd->num_sensors = 0;
+
+ ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
+ &fmd->subdev_notifier);
+ if (ret)
+ goto err_clk_p;
+ }
- platform_set_drvdata(pdev, fmd);
- mutex_unlock(&fmd->media_dev.graph_mutex);
return 0;
-err_unlock:
- mutex_unlock(&fmd->media_dev.graph_mutex);
+err_clk_p:
+ fimc_md_unregister_clk_provider(fmd);
+err_attr:
+ device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
err_clk:
fimc_md_put_clocks(fmd);
+err_m_ent:
fimc_md_unregister_entities(fmd);
- media_device_unregister(&fmd->media_dev);
err_md:
+ media_device_unregister(&fmd->media_dev);
+err_v4l2_dev:
v4l2_device_unregister(&fmd->v4l2_dev);
return ret;
}
@@ -1538,12 +1701,16 @@ static int fimc_md_remove(struct platform_device *pdev)
if (!fmd)
return 0;
+ fimc_md_unregister_clk_provider(fmd);
+ v4l2_async_notifier_unregister(&fmd->subdev_notifier);
+
v4l2_device_unregister(&fmd->v4l2_dev);
device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
fimc_md_unregister_entities(fmd);
fimc_md_pipelines_free(fmd);
media_device_unregister(&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 62599fd7756f..ee1e2519f728 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -10,6 +10,7 @@
#define FIMC_MDEVICE_H_
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/of.h>
@@ -31,8 +32,9 @@
#define PINCTRL_STATE_IDLE "idle"
-#define FIMC_MAX_SENSORS 8
+#define FIMC_MAX_SENSORS 4
#define FIMC_MAX_CAMCLKS 2
+#define DEFAULT_SENSOR_CLK_FREQ 24000000U
/* LCD/ISP Writeback clocks (PIXELASYNCMx) */
enum {
@@ -78,6 +80,7 @@ struct fimc_camclk_info {
/**
* struct fimc_sensor_info - image data source subdev information
* @pdata: sensor's atrributes passed as media device's platform data
+ * @asd: asynchronous subdev registration data structure
* @subdev: image sensor v4l2 subdev
* @host: fimc device the sensor is currently linked to
*
@@ -85,10 +88,17 @@ struct fimc_camclk_info {
*/
struct fimc_sensor_info {
struct fimc_source_info pdata;
+ struct v4l2_async_subdev asd;
struct v4l2_subdev *subdev;
struct fimc_dev *host;
};
+struct cam_clk {
+ struct clk_hw hw;
+ struct fimc_md *fmd;
+};
+#define to_cam_clk(_hw) container_of(_hw, struct cam_clk, hw)
+
/**
* struct fimc_md - fimc media device information
* @csis: MIPI CSIS subdevs data
@@ -105,6 +115,7 @@ struct fimc_sensor_info {
* @pinctrl: camera port pinctrl handle
* @state_default: pinctrl default state handle
* @state_idle: pinctrl idle state handle
+ * @cam_clk_provider: CAMCLK clock provider structure
* @user_subdev_api: true if subdevs are not configured by the host driver
* @slock: spinlock protecting @sensor array
*/
@@ -122,13 +133,25 @@ struct fimc_md {
struct media_device media_dev;
struct v4l2_device v4l2_dev;
struct platform_device *pdev;
+
struct fimc_pinctrl {
struct pinctrl *pinctrl;
struct pinctrl_state *state_default;
struct pinctrl_state *state_idle;
} pinctl;
- bool user_subdev_api;
+ struct cam_clk_provider {
+ struct clk *clks[FIMC_MAX_CAMCLKS];
+ struct clk_onecell_data clk_data;
+ struct device_node *of_node;
+ struct cam_clk camclk[FIMC_MAX_CAMCLKS];
+ int num_clocks;
+ } clk_provider;
+
+ struct v4l2_async_notifier subdev_notifier;
+ struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
+
+ bool user_subdev_api;
spinlock_t slock;
struct list_head pipelines;
};
@@ -145,6 +168,11 @@ static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
container_of(me->parent, struct fimc_md, media_dev);
}
+static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct fimc_md, subdev_notifier);
+}
+
static inline void fimc_md_graph_lock(struct exynos_video_entity *ve)
{
mutex_lock(&ve->vdev.entity.parent->graph_mutex);
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index 6bb86b581a34..c21d14fd61db 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -207,8 +207,11 @@ 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);
- src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp;
- src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode;
+ dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp;
+ dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.flags |=
+ src_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode;
v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
@@ -868,7 +871,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &deinterlace_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
- src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
q_data[V4L2_M2M_SRC].fmt = &formats[0];
q_data[V4L2_M2M_SRC].width = 640;
q_data[V4L2_M2M_SRC].height = 480;
@@ -885,7 +888,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &deinterlace_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
- dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
q_data[V4L2_M2M_DST].fmt = &formats[0];
q_data[V4L2_M2M_DST].width = 640;
q_data[V4L2_M2M_DST].height = 480;
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 32fab30a9105..8b34c485be79 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -1238,7 +1238,7 @@ static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)
return 0;
}
-static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
+static void mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
{
struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0);
@@ -1246,7 +1246,6 @@ static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
if (sg_table)
dma_unmap_sg(cam->dev, sg_table->sgl,
sg_table->nents, DMA_FROM_DEVICE);
- return 0;
}
static void mcam_vb_sg_buf_cleanup(struct vb2_buffer *vb)
diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c
index 08e24379b794..4f3096b17066 100644
--- a/drivers/media/platform/mem2mem_testdev.c
+++ b/drivers/media/platform/mem2mem_testdev.c
@@ -60,9 +60,7 @@ MODULE_PARM_DESC(debug, "activates debug info");
#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)
/* Default transaction time in msec */
-#define MEM2MEM_DEF_TRANSTIME 1000
-/* Default number of buffers per transaction */
-#define MEM2MEM_DEF_TRANSLEN 1
+#define MEM2MEM_DEF_TRANSTIME 40
#define MEM2MEM_COLOR_STEP (0xff >> 4)
#define MEM2MEM_NUM_TILES 8
@@ -114,6 +112,7 @@ struct m2mtest_q_data {
unsigned int width;
unsigned int height;
unsigned int sizeimage;
+ unsigned int sequence;
struct m2mtest_fmt *fmt;
};
@@ -236,9 +235,21 @@ static int device_process(struct m2mtest_ctx *ctx,
bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
w = 0;
+ out_vb->v4l2_buf.sequence = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++;
+ in_vb->v4l2_buf.sequence = q_data->sequence++;
memcpy(&out_vb->v4l2_buf.timestamp,
&in_vb->v4l2_buf.timestamp,
sizeof(struct timeval));
+ if (in_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TIMECODE)
+ memcpy(&out_vb->v4l2_buf.timecode, &in_vb->v4l2_buf.timecode,
+ sizeof(struct v4l2_timecode));
+ out_vb->v4l2_buf.field = in_vb->v4l2_buf.field;
+ out_vb->v4l2_buf.flags = in_vb->v4l2_buf.flags &
+ (V4L2_BUF_FLAG_TIMECODE |
+ V4L2_BUF_FLAG_KEYFRAME |
+ V4L2_BUF_FLAG_PFRAME |
+ V4L2_BUF_FLAG_BFRAME |
+ V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
switch (ctx->mode) {
case MEM2MEM_HFLIP | MEM2MEM_VFLIP:
@@ -505,19 +516,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)
{
- enum v4l2_field field;
-
- field = f->fmt.pix.field;
-
- if (field == V4L2_FIELD_ANY)
- field = V4L2_FIELD_NONE;
- else if (V4L2_FIELD_NONE != field)
- return -EINVAL;
-
/* V4L2 specification suggests the driver corrects the format struct
* if any of the dimensions is unsupported */
- f->fmt.pix.field = field;
-
if (f->fmt.pix.height < MIN_H)
f->fmt.pix.height = MIN_H;
else if (f->fmt.pix.height > MAX_H)
@@ -531,6 +531,8 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)
f->fmt.pix.width &= ~DIM_ALIGN_MASK;
f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.priv = 0;
return 0;
}
@@ -542,7 +544,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct m2mtest_ctx *ctx = file2ctx(file);
fmt = find_format(f);
- if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
+ if (!fmt) {
+ f->fmt.pix.pixelformat = formats[0].fourcc;
+ fmt = find_format(f);
+ }
+ if (!(fmt->types & MEM2MEM_CAPTURE)) {
v4l2_err(&ctx->dev->v4l2_dev,
"Fourcc format (0x%08x) invalid.\n",
f->fmt.pix.pixelformat);
@@ -560,7 +566,11 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
struct m2mtest_ctx *ctx = file2ctx(file);
fmt = find_format(f);
- if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
+ if (!fmt) {
+ f->fmt.pix.pixelformat = formats[0].fourcc;
+ fmt = find_format(f);
+ }
+ if (!(fmt->types & MEM2MEM_OUTPUT)) {
v4l2_err(&ctx->dev->v4l2_dev,
"Fourcc format (0x%08x) invalid.\n",
f->fmt.pix.pixelformat);
@@ -740,6 +750,15 @@ static int m2mtest_buf_prepare(struct vb2_buffer *vb)
dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
q_data = get_q_data(ctx, vb->vb2_queue->type);
+ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ if (vb->v4l2_buf.field == V4L2_FIELD_ANY)
+ vb->v4l2_buf.field = V4L2_FIELD_NONE;
+ if (vb->v4l2_buf.field != V4L2_FIELD_NONE) {
+ dprintk(ctx->dev, "%s field isn't supported\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n",
@@ -755,13 +774,45 @@ static int m2mtest_buf_prepare(struct vb2_buffer *vb)
static void m2mtest_buf_queue(struct vb2_buffer *vb)
{
struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
}
+static int m2mtest_start_streaming(struct vb2_queue *q, unsigned count)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
+ struct m2mtest_q_data *q_data = get_q_data(ctx, q->type);
+
+ q_data->sequence = 0;
+ return 0;
+}
+
+static int m2mtest_stop_streaming(struct vb2_queue *q)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
+ struct vb2_buffer *vb;
+ unsigned long flags;
+
+ for (;;) {
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ else
+ vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ if (vb == NULL)
+ return 0;
+ spin_lock_irqsave(&ctx->dev->irqlock, flags);
+ v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
+ spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
+ }
+ return 0;
+}
+
static struct vb2_ops m2mtest_qops = {
.queue_setup = m2mtest_queue_setup,
.buf_prepare = m2mtest_buf_prepare,
.buf_queue = m2mtest_buf_queue,
+ .start_streaming = m2mtest_start_streaming,
+ .stop_streaming = m2mtest_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
@@ -772,12 +823,12 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
int ret;
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
src_vq->drv_priv = ctx;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &m2mtest_qops;
src_vq->mem_ops = &vb2_vmalloc_memops;
- src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->dev->dev_mutex;
ret = vb2_queue_init(src_vq);
@@ -785,12 +836,12 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
return ret;
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
dst_vq->drv_priv = ctx;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &m2mtest_qops;
dst_vq->mem_ops = &vb2_vmalloc_memops;
- dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->lock = &ctx->dev->dev_mutex;
return vb2_queue_init(dst_vq);
@@ -801,10 +852,10 @@ static const struct v4l2_ctrl_config m2mtest_ctrl_trans_time_msec = {
.id = V4L2_CID_TRANS_TIME_MSEC,
.name = "Transaction Time (msec)",
.type = V4L2_CTRL_TYPE_INTEGER,
- .def = 1001,
+ .def = MEM2MEM_DEF_TRANSTIME,
.min = 1,
.max = 10001,
- .step = 100,
+ .step = 1,
};
static const struct v4l2_ctrl_config m2mtest_ctrl_trans_num_bufs = {
diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c
index c690435853bd..0b7480e82142 100644
--- a/drivers/media/platform/mx2_emmaprp.c
+++ b/drivers/media/platform/mx2_emmaprp.c
@@ -377,8 +377,13 @@ 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);
- src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp;
- src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode;
+ dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp;
+ dst_vb->v4l2_buf.flags &=
+ ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.flags |=
+ src_vb->v4l2_buf.flags
+ & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode;
spin_lock_irqsave(&pcdev->irqlock, flags);
v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
@@ -766,7 +771,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &emmaprp_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
- src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -778,7 +783,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &emmaprp_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
- dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
return vb2_queue_init(dst_vq);
}
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index dfd0a21a0658..9a726eacb29b 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -601,6 +601,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus)
switch (cur_display->type) {
case OMAP_DISPLAY_TYPE_DSI:
case OMAP_DISPLAY_TYPE_DPI:
+ case OMAP_DISPLAY_TYPE_DVI:
if (mgr_id == OMAP_DSS_CHANNEL_LCD)
irq = DISPC_IRQ_VSYNC;
else if (mgr_id == OMAP_DSS_CHANNEL_LCD2)
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c
index cf1c437a8687..62e7e5783ce8 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.c
+++ b/drivers/media/platform/omap/omap_vout_vrfb.c
@@ -270,7 +270,8 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0);
omap_start_dma(tx->dma_ch);
- interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT);
+ wait_event_interruptible_timeout(tx->wait, tx->tx_status == 1,
+ VRFB_TX_TIMEOUT);
if (tx->tx_status == 0) {
omap_stop_dma(tx->dma_ch);
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 5807185262fe..06a0df434249 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -391,7 +391,7 @@ static void isp_disable_interrupts(struct isp_device *isp)
* @isp: OMAP3 ISP device
* @idle: Consider idle state.
*
- * Set the power settings for the ISP and SBL bus and cConfigure the HS/VS
+ * Set the power settings for the ISP and SBL bus and configure the HS/VS
* interrupt source.
*
* We need to configure the HS/VS interrupt source before interrupts get
@@ -588,9 +588,6 @@ static void isp_isr_sbl(struct isp_device *isp)
* @_isp: Pointer to the OMAP3 ISP device
*
* Handles the corresponding callback if plugged in.
- *
- * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
- * IRQ wasn't handled.
*/
static irqreturn_t isp_isr(int irq, void *_isp)
{
@@ -1420,7 +1417,7 @@ int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
}
/*
- * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping
+ * omap3isp_module_sync_is_stopping - Helper to verify if module was stopping
* @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
* @stopping: flag which tells module wants to stop
*
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index 081f5ec5a663..6d5e69711907 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -265,7 +265,7 @@ void omap3isp_unregister_entities(struct platform_device *pdev);
/*
* isp_reg_readl - Read value of an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @isp_mmio_range: Range to which the register offset refers to.
* @reg_offset: Register offset to read from.
*
@@ -280,7 +280,7 @@ u32 isp_reg_readl(struct isp_device *isp, enum isp_mem_resources isp_mmio_range,
/*
* isp_reg_writel - Write value to an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @reg_value: 32 bit value to write to the register.
* @isp_mmio_range: Range to which the register offset refers to.
* @reg_offset: Register offset to write into.
@@ -293,8 +293,8 @@ void isp_reg_writel(struct isp_device *isp, u32 reg_value,
}
/*
- * isp_reg_and - Clear individual bits in an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * isp_reg_clr - Clear individual bits in an OMAP3 ISP register
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @mmio_range: Range to which the register offset refers to.
* @reg: Register offset to work on.
* @clr_bits: 32 bit value which would be cleared in the register.
@@ -310,7 +310,7 @@ void isp_reg_clr(struct isp_device *isp, enum isp_mem_resources mmio_range,
/*
* isp_reg_set - Set individual bits in an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @mmio_range: Range to which the register offset refers to.
* @reg: Register offset to work on.
* @set_bits: 32 bit value which would be set in the register.
@@ -326,7 +326,7 @@ void isp_reg_set(struct isp_device *isp, enum isp_mem_resources mmio_range,
/*
* isp_reg_clr_set - Clear and set invidial bits in an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @mmio_range: Range to which the register offset refers to.
* @reg: Register offset to work on.
* @clr_bits: 32 bit value which would be cleared in the register.
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index 5db2c88b9ad8..4d920c800ff5 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -293,7 +293,7 @@ static int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable)
isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC,
ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE);
ccdc->lsc.state = LSC_STATE_STOPPED;
- dev_warn(to_device(ccdc), "LSC prefecth timeout\n");
+ dev_warn(to_device(ccdc), "LSC prefetch timeout\n");
return -ETIMEDOUT;
}
ccdc->lsc.state = LSC_STATE_RUNNING;
@@ -674,7 +674,7 @@ static void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn)
/*
* ccdc_config - Set CCDC configuration from userspace
* @ccdc: Pointer to ISP CCDC device.
- * @userspace_add: Structure containing CCDC configuration sent from userspace.
+ * @ccdc_struct: Structure containing CCDC configuration sent from userspace.
*
* Returns 0 if successful, -EINVAL if the pointer to the configuration
* structure is null, or the copy_from_user function fails to copy user space
@@ -793,7 +793,7 @@ static void ccdc_apply_controls(struct isp_ccdc_device *ccdc)
/*
* omap3isp_ccdc_restore_context - Restore values of the CCDC module registers
- * @dev: Pointer to ISP device
+ * @isp: Pointer to ISP device
*/
void omap3isp_ccdc_restore_context(struct isp_device *isp)
{
@@ -2525,7 +2525,7 @@ error_video:
/*
* omap3isp_ccdc_init - CCDC module initialization.
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
*
* TODO: Get the initialisation values from platform data.
*
@@ -2564,7 +2564,7 @@ int omap3isp_ccdc_init(struct isp_device *isp)
/*
* omap3isp_ccdc_cleanup - CCDC module cleanup.
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
*/
void omap3isp_ccdc_cleanup(struct isp_device *isp)
{
diff --git a/drivers/media/platform/omap3isp/ispccdc.h b/drivers/media/platform/omap3isp/ispccdc.h
index a5da9e19edbf..9d24e4107864 100644
--- a/drivers/media/platform/omap3isp/ispccdc.h
+++ b/drivers/media/platform/omap3isp/ispccdc.h
@@ -63,12 +63,6 @@ struct ispccdc_lsc_config_req {
/*
* ispccdc_lsc - CCDC LSC parameters
- * @update_config: Set when user changes config
- * @request_enable: Whether LSC is requested to be enabled
- * @config: LSC config set by user
- * @update_table: Set when user provides a new LSC table to table_new
- * @table_new: LSC table set by user, ISP address
- * @table_inuse: LSC table currently in use, ISP address
*/
struct ispccdc_lsc {
enum ispccdc_lsc_state state;
diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index e84fe0543e47..b30b67d22a58 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
@@ -211,7 +211,7 @@ static void ccp2_mem_enable(struct isp_ccp2_device *ccp2, u8 enable)
/*
* ccp2_phyif_config - Initialize CCP2 phy interface config
* @ccp2: Pointer to ISP CCP2 device
- * @config: CCP2 platform data
+ * @pdata: CCP2 platform data
*
* Configure the CCP2 physical interface module from platform data.
*
@@ -518,7 +518,7 @@ static void ccp2_mem_configure(struct isp_ccp2_device *ccp2,
ISPCCP2_LCM_IRQSTATUS_EOF_IRQ,
OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQSTATUS);
- /* Enable LCM interupts */
+ /* Enable LCM interrupts */
isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQENABLE,
ISPCCP2_LCM_IRQSTATUS_EOF_IRQ |
ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ);
@@ -1096,7 +1096,7 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)
* implementation we use a fixed 32 bytes alignment regardless of the
* input format and width. If strict 128 bits alignment support is
* required ispvideo will need to be made aware of this special dual
- * alignement requirements.
+ * alignment requirements.
*/
ccp2->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
ccp2->video_in.bpl_alignment = 32;
diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c
index e070c24048ef..06a5f8164eaa 100644
--- a/drivers/media/platform/omap3isp/isphist.c
+++ b/drivers/media/platform/omap3isp/isphist.c
@@ -299,7 +299,7 @@ static u32 hist_get_buf_size(struct omap3isp_hist_config *conf)
/*
* hist_validate_params - Helper function to check user given params.
- * @user_cfg: Pointer to user configuration structure.
+ * @new_conf: Pointer to user configuration structure.
*
* Returns 0 on success configuration.
*/
@@ -351,7 +351,7 @@ static int hist_validate_params(struct ispstat *hist, void *new_conf)
buf_size = hist_get_buf_size(user_cfg);
if (buf_size > user_cfg->buf_size)
- /* User's buf_size request wasn't enoght */
+ /* User's buf_size request wasn't enough */
user_cfg->buf_size = buf_size;
else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE)
user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE;
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
index 1c776c1186f1..395b2b068c75 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/drivers/media/platform/omap3isp/isppreview.c
@@ -122,7 +122,7 @@ static struct omap3isp_prev_csc flr_prev_csc = {
#define PREV_MAX_OUT_WIDTH_REV_15 4096
/*
- * Coeficient Tables for the submodules in Preview.
+ * Coefficient Tables for the submodules in Preview.
* Array is initialised with the values from.the tables text file.
*/
@@ -971,7 +971,8 @@ static void preview_setup_hw(struct isp_prev_device *prev, u32 update,
/*
* preview_config_ycpos - Configure byte layout of YUV image.
- * @mode: Indicates the required byte layout.
+ * @prev: pointer to previewer private structure
+ * @pixelcode: pixel code
*/
static void
preview_config_ycpos(struct isp_prev_device *prev,
@@ -1079,6 +1080,7 @@ static void preview_config_input_format(struct isp_prev_device *prev,
*/
static void preview_config_input_size(struct isp_prev_device *prev, u32 active)
{
+ const struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK];
struct isp_device *isp = to_isp_device(prev);
unsigned int sph = prev->crop.left;
unsigned int eph = prev->crop.left + prev->crop.width - 1;
@@ -1086,6 +1088,14 @@ static void preview_config_input_size(struct isp_prev_device *prev, u32 active)
unsigned int elv = prev->crop.top + prev->crop.height - 1;
u32 features;
+ if (format->code != V4L2_MBUS_FMT_Y8_1X8 &&
+ format->code != V4L2_MBUS_FMT_Y10_1X10) {
+ sph -= 2;
+ eph += 2;
+ slv -= 2;
+ elv += 2;
+ }
+
features = (prev->params.params[0].features & active)
| (prev->params.params[1].features & ~active);
@@ -1363,8 +1373,8 @@ static void preview_init_params(struct isp_prev_device *prev)
}
/*
- * preview_max_out_width - Handle previewer hardware ouput limitations
- * @isp_revision : ISP revision
+ * preview_max_out_width - Handle previewer hardware output limitations
+ * @prev: pointer to previewer private structure
* returns maximum width output for current isp revision
*/
static unsigned int preview_max_out_width(struct isp_prev_device *prev)
@@ -1610,7 +1620,7 @@ static const struct v4l2_ctrl_ops preview_ctrl_ops = {
/*
* preview_ioctl - Handle preview module private ioctl's
- * @prev: pointer to preview context structure
+ * @sd: pointer to v4l2 subdev structure
* @cmd: configuration command
* @arg: configuration argument
* return -EINVAL or zero on success
@@ -2341,7 +2351,7 @@ error_video_in:
/*
* omap3isp_preview_init - Previewer initialization.
- * @dev : Pointer to ISP device
+ * @isp : Pointer to ISP device
* return -ENOMEM or zero on success
*/
int omap3isp_preview_init(struct isp_device *isp)
diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c
index 5f0f8fab1d17..a5e65858e799 100644
--- a/drivers/media/platform/omap3isp/ispqueue.c
+++ b/drivers/media/platform/omap3isp/ispqueue.c
@@ -597,7 +597,7 @@ static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking)
* isp_video_queue_free - Free video buffers memory
*
* Buffers can only be freed if the queue isn't streaming and if no buffer is
- * mapped to userspace. Return -EBUSY if those conditions aren't statisfied.
+ * mapped to userspace. Return -EBUSY if those conditions aren't satisfied.
*
* This function must be called with the queue lock held.
*/
diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c
index 0d36b8bc9f98..86369df81d74 100644
--- a/drivers/media/platform/omap3isp/ispresizer.c
+++ b/drivers/media/platform/omap3isp/ispresizer.c
@@ -206,7 +206,7 @@ static void resizer_set_bilinear(struct isp_res_device *res,
/*
* resizer_set_ycpos - Luminance and chrominance order
* @res: Device context.
- * @order: order type.
+ * @pixelcode: pixel code.
*/
static void resizer_set_ycpos(struct isp_res_device *res,
enum v4l2_mbus_pixelcode pixelcode)
@@ -918,8 +918,8 @@ static void resizer_calc_ratios(struct isp_res_device *res,
/*
* resizer_set_crop_params - Setup hardware with cropping parameters
* @res : resizer private structure
- * @crop_rect : current crop rectangle
- * @ratio : resizer ratios
+ * @input : format on sink pad
+ * @output : format on source pad
* return none
*/
static void resizer_set_crop_params(struct isp_res_device *res,
diff --git a/drivers/media/platform/omap3isp/ispresizer.h b/drivers/media/platform/omap3isp/ispresizer.h
index 70c1c0e1bbdf..9b01e9047c15 100644
--- a/drivers/media/platform/omap3isp/ispresizer.h
+++ b/drivers/media/platform/omap3isp/ispresizer.h
@@ -30,12 +30,12 @@
#include <linux/types.h>
/*
- * Constants for filter coefficents count
+ * Constants for filter coefficients count
*/
#define COEFF_CNT 32
/*
- * struct isprsz_coef - Structure for resizer filter coeffcients.
+ * struct isprsz_coef - Structure for resizer filter coefficients.
* @h_filter_coef_4tap: Horizontal filter coefficients for 8-phase/4-tap
* mode (.5x-4x)
* @v_filter_coef_4tap: Vertical filter coefficients for 8-phase/4-tap
diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c
index a75407c3a726..5707f85c4cc4 100644
--- a/drivers/media/platform/omap3isp/ispstat.c
+++ b/drivers/media/platform/omap3isp/ispstat.c
@@ -144,7 +144,7 @@ static int isp_stat_buf_check_magic(struct ispstat *stat,
for (w = buf->virt_addr + buf_size, end = w + MAGIC_SIZE;
w < end; w++) {
if (unlikely(*w != MAGIC_NUM)) {
- dev_dbg(stat->isp->dev, "%s: endding magic check does "
+ dev_dbg(stat->isp->dev, "%s: ending magic check does "
"not match.\n", stat->subdev.name);
return -EINVAL;
}
@@ -841,7 +841,7 @@ int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable)
if (enable) {
/*
* Only set enable PCR bit if the module was previously
- * enabled through ioct.
+ * enabled through ioctl.
*/
isp_stat_try_enable(stat);
} else {
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index 856fdf554035..85b4036ba5e4 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -333,7 +333,7 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)
/*
* ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @sglist: Pointer to source Scatter gather list to allocate.
* @sglen: Number of elements of the scatter-gatter list.
*
@@ -363,7 +363,7 @@ ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen)
/*
* ispmmu_vunmap - Unmap a device address from the ISP MMU
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @da: Device address generated from a ispmmu_vmap call.
*/
static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da)
@@ -886,7 +886,11 @@ static int isp_video_check_external_subdevs(struct isp_video *video,
struct v4l2_ext_controls ctrls;
struct v4l2_ext_control ctrl;
unsigned int i;
- int ret = 0;
+ int ret;
+
+ /* Memory-to-memory pipelines have no external subdev. */
+ if (pipe->input != NULL)
+ return 0;
for (i = 0; i < ARRAY_SIZE(ents); i++) {
/* Is the entity part of the pipeline? */
@@ -905,7 +909,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video,
if (!source) {
dev_warn(isp->dev, "can't find source, failing now\n");
- return ret;
+ return -EINVAL;
}
if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV)
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index 40b298ab87f1..4e4d1631e042 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -1160,7 +1160,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx)
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct camif_buffer);
q->drv_priv = vp;
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(q);
if (ret)
@@ -1592,26 +1592,27 @@ int s3c_camif_create_subdev(struct camif_dev *camif)
ARRAY_SIZE(s3c_camif_test_pattern_menu) - 1, 0, 0,
s3c_camif_test_pattern_menu);
- camif->ctrl_colorfx = v4l2_ctrl_new_std_menu(handler,
+ if (camif->variant->has_img_effect) {
+ camif->ctrl_colorfx = v4l2_ctrl_new_std_menu(handler,
&s3c_camif_subdev_ctrl_ops,
V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR,
~0x981f, V4L2_COLORFX_NONE);
- camif->ctrl_colorfx_cbcr = v4l2_ctrl_new_std(handler,
+ camif->ctrl_colorfx_cbcr = v4l2_ctrl_new_std(handler,
&s3c_camif_subdev_ctrl_ops,
V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0);
+ }
+
if (handler->error) {
v4l2_ctrl_handler_free(handler);
media_entity_cleanup(&sd->entity);
return handler->error;
}
- v4l2_ctrl_auto_cluster(2, &camif->ctrl_colorfx,
+ if (camif->variant->has_img_effect)
+ v4l2_ctrl_auto_cluster(2, &camif->ctrl_colorfx,
V4L2_COLORFX_SET_CBCR, false);
- if (!camif->variant->has_img_effect) {
- camif->ctrl_colorfx->flags |= V4L2_CTRL_FLAG_DISABLED;
- camif->ctrl_colorfx_cbcr->flags |= V4L2_CTRL_FLAG_DISABLED;
- }
+
sd->ctrl_handler = handler;
v4l2_set_subdevdata(sd, camif);
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index 0fcf7d75e841..357af1ebaeda 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -157,7 +157,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->ops = &g2d_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->dev->mutex;
ret = vb2_queue_init(src_vq);
@@ -170,7 +170,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->ops = &g2d_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->lock = &ctx->dev->mutex;
return vb2_queue_init(dst_vq);
@@ -560,6 +560,9 @@ static irqreturn_t g2d_isr(int irq, void *prv)
dst->v4l2_buf.timecode = src->v4l2_buf.timecode;
dst->v4l2_buf.timestamp = src->v4l2_buf.timestamp;
+ dst->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst->v4l2_buf.flags |=
+ src->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index 7d68d0b9966a..8a18972012f7 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -1701,7 +1701,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &s5p_jpeg_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
- src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->jpeg->lock;
ret = vb2_queue_init(src_vq);
@@ -1714,7 +1714,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &s5p_jpeg_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
- dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->lock = &ctx->jpeg->lock;
return vb2_queue_init(dst_vq);
@@ -1766,6 +1766,9 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;
dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_buf->v4l2_buf.flags |=
+ src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
v4l2_m2m_buf_done(src_buf, state);
if (curr_ctx->mode == S5P_JPEG_ENCODE)
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h
index 33f2c7374cfd..57fb05bb8c77 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-regs.h
@@ -210,19 +210,19 @@
/* JPEG CNTL Register bit */
#define EXYNOS4_ENC_DEC_MODE_MASK (0xfffffffc << 0)
-#define EXYNOS4_DEC_MODE (1 << 0)
-#define EXYNOS4_ENC_MODE (1 << 1)
+#define EXYNOS4_DEC_MODE (1 << 0)
+#define EXYNOS4_ENC_MODE (1 << 1)
#define EXYNOS4_AUTO_RST_MARKER (1 << 2)
#define EXYNOS4_RST_INTERVAL_SHIFT 3
#define EXYNOS4_RST_INTERVAL(x) (((x) & 0xffff) \
<< EXYNOS4_RST_INTERVAL_SHIFT)
#define EXYNOS4_HUF_TBL_EN (1 << 19)
#define EXYNOS4_HOR_SCALING_SHIFT 20
-#define EXYNOS4_HOR_SCALING_MASK (3 << EXYNOS4_HOR_SCALING_SHIFT)
+#define EXYNOS4_HOR_SCALING_MASK (3 << EXYNOS4_HOR_SCALING_SHIFT)
#define EXYNOS4_HOR_SCALING(x) (((x) & 0x3) \
<< EXYNOS4_HOR_SCALING_SHIFT)
#define EXYNOS4_VER_SCALING_SHIFT 22
-#define EXYNOS4_VER_SCALING_MASK (3 << EXYNOS4_VER_SCALING_SHIFT)
+#define EXYNOS4_VER_SCALING_MASK (3 << EXYNOS4_VER_SCALING_SHIFT)
#define EXYNOS4_VER_SCALING(x) (((x) & 0x3) \
<< EXYNOS4_VER_SCALING_SHIFT)
#define EXYNOS4_PADDING (1 << 27)
@@ -238,8 +238,8 @@
#define EXYNOS4_FRAME_ERR_EN (1 << 4)
#define EXYNOS4_INT_EN_ALL (0x1f << 0)
-#define EXYNOS4_MOD_REG_PROC_ENC (0 << 3)
-#define EXYNOS4_MOD_REG_PROC_DEC (1 << 3)
+#define EXYNOS4_MOD_REG_PROC_ENC (0 << 3)
+#define EXYNOS4_MOD_REG_PROC_DEC (1 << 3)
#define EXYNOS4_MOD_REG_SUBSAMPLE_444 (0 << 0)
#define EXYNOS4_MOD_REG_SUBSAMPLE_422 (1 << 0)
@@ -270,7 +270,7 @@
#define EXYNOS4_DEC_YUV_420_IMG (4 << 0)
#define EXYNOS4_GRAY_IMG_IP_SHIFT 3
-#define EXYNOS4_GRAY_IMG_IP_MASK (7 << EXYNOS4_GRAY_IMG_IP_SHIFT)
+#define EXYNOS4_GRAY_IMG_IP_MASK (7 << EXYNOS4_GRAY_IMG_IP_SHIFT)
#define EXYNOS4_GRAY_IMG_IP (4 << EXYNOS4_GRAY_IMG_IP_SHIFT)
#define EXYNOS4_RGB_IP_SHIFT 6
@@ -278,18 +278,18 @@
#define EXYNOS4_RGB_IP_RGB_16BIT_IMG (4 << EXYNOS4_RGB_IP_SHIFT)
#define EXYNOS4_RGB_IP_RGB_32BIT_IMG (5 << EXYNOS4_RGB_IP_SHIFT)
-#define EXYNOS4_YUV_444_IP_SHIFT 9
+#define EXYNOS4_YUV_444_IP_SHIFT 9
#define EXYNOS4_YUV_444_IP_MASK (7 << EXYNOS4_YUV_444_IP_SHIFT)
#define EXYNOS4_YUV_444_IP_YUV_444_2P_IMG (4 << EXYNOS4_YUV_444_IP_SHIFT)
#define EXYNOS4_YUV_444_IP_YUV_444_3P_IMG (5 << EXYNOS4_YUV_444_IP_SHIFT)
-#define EXYNOS4_YUV_422_IP_SHIFT 12
+#define EXYNOS4_YUV_422_IP_SHIFT 12
#define EXYNOS4_YUV_422_IP_MASK (7 << EXYNOS4_YUV_422_IP_SHIFT)
#define EXYNOS4_YUV_422_IP_YUV_422_1P_IMG (4 << EXYNOS4_YUV_422_IP_SHIFT)
#define EXYNOS4_YUV_422_IP_YUV_422_2P_IMG (5 << EXYNOS4_YUV_422_IP_SHIFT)
#define EXYNOS4_YUV_422_IP_YUV_422_3P_IMG (6 << EXYNOS4_YUV_422_IP_SHIFT)
-#define EXYNOS4_YUV_420_IP_SHIFT 15
+#define EXYNOS4_YUV_420_IP_SHIFT 15
#define EXYNOS4_YUV_420_IP_MASK (7 << EXYNOS4_YUV_420_IP_SHIFT)
#define EXYNOS4_YUV_420_IP_YUV_420_2P_IMG (4 << EXYNOS4_YUV_420_IP_SHIFT)
#define EXYNOS4_YUV_420_IP_YUV_420_3P_IMG (5 << EXYNOS4_YUV_420_IP_SHIFT)
@@ -303,8 +303,8 @@
#define EXYNOS4_JPEG_DECODED_IMG_FMT_MASK 0x03
-#define EXYNOS4_SWAP_CHROMA_CRCB (1 << 26)
-#define EXYNOS4_SWAP_CHROMA_CBCR (0 << 26)
+#define EXYNOS4_SWAP_CHROMA_CRCB (1 << 26)
+#define EXYNOS4_SWAP_CHROMA_CBCR (0 << 26)
/* JPEG HUFF count Register bit */
#define EXYNOS4_HUFF_COUNT_MASK 0xffff
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
index 2398cdf61341..8d0b686d9adb 100644
--- a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
+++ b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
@@ -229,6 +229,7 @@
#define S5P_FIMV_E_PADDING_CTRL_V6 0xf7a4
#define S5P_FIMV_E_MV_HOR_RANGE_V6 0xf7ac
#define S5P_FIMV_E_MV_VER_RANGE_V6 0xf7b0
+#define S5P_FIMV_E_MV_RANGE_V6_MASK 0x3fff
#define S5P_FIMV_E_VBV_BUFFER_SIZE_V6 0xf84c
#define S5P_FIMV_E_VBV_INIT_DELAY_V6 0xf850
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index e2aac592d29f..89356ae90238 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -232,6 +232,11 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
src_buf->b->v4l2_buf.timecode;
dst_buf->b->v4l2_buf.timestamp =
src_buf->b->v4l2_buf.timestamp;
+ dst_buf->b->v4l2_buf.flags &=
+ ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_buf->b->v4l2_buf.flags |=
+ src_buf->b->v4l2_buf.flags
+ & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
switch (frame_type) {
case S5P_FIMV_DECODE_FRAME_I_FRAME:
dst_buf->b->v4l2_buf.flags |=
@@ -794,7 +799,7 @@ static int s5p_mfc_open(struct file *file)
goto err_queue_init;
}
q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(q);
if (ret) {
mfc_err("Failed to initialize videobuf2 queue(capture)\n");
@@ -816,7 +821,7 @@ static int s5p_mfc_open(struct file *file)
goto err_queue_init;
}
q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(q);
if (ret) {
mfc_err("Failed to initialize videobuf2 queue(output)\n");
@@ -1147,9 +1152,9 @@ static int s5p_mfc_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err_dec_alloc;
}
- vfd->fops = &s5p_mfc_fops,
+ vfd->fops = &s5p_mfc_fops;
vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
- vfd->release = video_device_release,
+ vfd->release = video_device_release;
vfd->lock = &dev->mfc_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->vfl_dir = VFL_DIR_M2M;
@@ -1172,9 +1177,9 @@ static int s5p_mfc_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err_enc_alloc;
}
- vfd->fops = &s5p_mfc_fops,
+ vfd->fops = &s5p_mfc_fops;
vfd->ioctl_ops = get_enc_v4l2_ioctl_ops();
- vfd->release = video_device_release,
+ vfd->release = video_device_release;
vfd->lock = &dev->mfc_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->vfl_dir = VFL_DIR_M2M;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
index f723f1f2f578..5c28cc3e699b 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -426,6 +426,8 @@ struct s5p_mfc_vp8_enc_params {
struct s5p_mfc_enc_params {
u16 width;
u16 height;
+ u32 mv_h_range;
+ u32 mv_v_range;
u16 gop_size;
enum v4l2_mpeg_video_multi_slice_mode slice_mode;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
index 2475a3c9a0a6..ee05f2dd439b 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
@@ -44,8 +44,6 @@ int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)
return -ENOMEM;
}
- dev->bank1 = dev->bank1;
-
if (HAS_PORTNUM(dev) && IS_TWOPORT(dev)) {
bank2_virt = dma_alloc_coherent(dev->mem_dev_r, 1 << MFC_BASE_ALIGN_ORDER,
&bank2_dma_addr, GFP_KERNEL);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index 91b6e020ddf3..df83cd157bab 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -208,6 +208,24 @@ static struct mfc_control controls[] = {
.default_value = 0,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Horizontal MV Search Range",
+ .minimum = 16,
+ .maximum = 128,
+ .step = 16,
+ .default_value = 32,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Vertical MV Search Range",
+ .minimum = 16,
+ .maximum = 128,
+ .step = 16,
+ .default_value = 32,
+ },
+ {
.id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 0,
@@ -1417,6 +1435,12 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
p->vbv_size = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE:
+ p->mv_h_range = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE:
+ p->mv_v_range = ctrl->val;
+ break;
case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
p->codec.h264.cpb_size = ctrl->val;
break;
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 f6ff2dbf3a1d..f64621ae9b5a 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -727,14 +727,10 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
/* setting for MV range [16, 256] */
- reg = 0;
- reg &= ~(0x3FFF);
- reg = 256;
+ reg = (p->mv_h_range & S5P_FIMV_E_MV_RANGE_V6_MASK);
WRITEL(reg, S5P_FIMV_E_MV_HOR_RANGE_V6);
- reg = 0;
- reg &= ~(0x3FFF);
- reg = 256;
+ reg = (p->mv_v_range & S5P_FIMV_E_MV_RANGE_V6_MASK);
WRITEL(reg, S5P_FIMV_E_MV_VER_RANGE_V6);
WRITEL(0x0, S5P_FIMV_E_FRAME_INSERTION_V6);
diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c
index c5059ba0d733..a1ce55fd30f3 100644
--- a/drivers/media/platform/s5p-tv/mixer_video.c
+++ b/drivers/media/platform/s5p-tv/mixer_video.c
@@ -946,11 +946,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
mxr_dbg(mdev, "%s\n", __func__);
- if (count == 0) {
- mxr_dbg(mdev, "no output buffers queued\n");
- return -ENOBUFS;
- }
-
/* block any changes in output configuration */
mxr_output_get(mdev);
@@ -1124,6 +1119,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
.drv_priv = layer,
.buf_struct_size = sizeof(struct mxr_buffer),
.ops = &mxr_video_qops,
+ .min_buffers_needed = 1,
.mem_ops = &vb2_dma_contig_memops,
};
diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index 4835173d7f80..f0b6c900034d 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -472,7 +472,7 @@ static int isi_camera_init_videobuf(struct vb2_queue *q,
q->buf_struct_size = sizeof(struct frame_buffer);
q->ops = &isi_video_qops;
q->mem_ops = &vb2_dma_contig_memops;
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
return vb2_queue_init(q);
}
diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index d73abca9c6ee..3e844803bdca 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -794,7 +794,7 @@ static int mx2_camera_init_videobuf(struct vb2_queue *q,
q->ops = &mx2_videobuf_ops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct mx2_buffer);
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
return vb2_queue_init(q);
}
diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index f975b7008692..9ed81ac6881c 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -453,7 +453,7 @@ static int mx3_camera_init_videobuf(struct vb2_queue *q,
q->ops = &mx3_videobuf_ops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct mx3_camera_buffer);
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
return vb2_queue_init(q);
}
diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
index 3b1c05a72d00..704eee766487 100644
--- a/drivers/media/platform/soc_camera/rcar_vin.c
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -68,6 +68,8 @@
#define VNMC_YCAL (1 << 19)
#define VNMC_INF_YUV8_BT656 (0 << 16)
#define VNMC_INF_YUV8_BT601 (1 << 16)
+#define VNMC_INF_YUV10_BT656 (2 << 16)
+#define VNMC_INF_YUV10_BT601 (3 << 16)
#define VNMC_INF_YUV16 (5 << 16)
#define VNMC_VUP (1 << 10)
#define VNMC_IM_ODD (0 << 3)
@@ -275,6 +277,12 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
/* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */
vnmc |= priv->pdata->flags & RCAR_VIN_BT656 ?
VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601;
+ break;
+ case V4L2_MBUS_FMT_YUYV10_2X10:
+ /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */
+ vnmc |= priv->pdata->flags & RCAR_VIN_BT656 ?
+ VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601;
+ break;
default:
break;
}
@@ -1003,6 +1011,7 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,
switch (code) {
case V4L2_MBUS_FMT_YUYV8_1X16:
case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_YUYV10_2X10:
if (cam->extra_fmt)
break;
@@ -1360,7 +1369,7 @@ static int rcar_vin_init_videobuf2(struct vb2_queue *vq,
vq->ops = &rcar_vin_vb2_ops;
vq->mem_ops = &vb2_dma_contig_memops;
vq->buf_struct_size = sizeof(struct rcar_vin_buffer);
- vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
return vb2_queue_init(vq);
}
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 150bd4df413c..3e75a469cd49 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -1665,7 +1665,7 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
q->ops = &sh_mobile_ceu_videobuf_ops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer);
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
return vb2_queue_init(q);
}
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
index 1296c5386231..7a77a5b7a075 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -1278,6 +1278,8 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
d_buf = &d_vb->v4l2_buf;
d_buf->timestamp = s_buf->timestamp;
+ d_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ d_buf->flags |= s_buf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
if (s_buf->flags & V4L2_BUF_FLAG_TIMECODE) {
d_buf->flags |= V4L2_BUF_FLAG_TIMECODE;
d_buf->timecode = s_buf->timecode;
@@ -1770,7 +1772,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &vpe_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
- src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -1783,7 +1785,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &vpe_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
- dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
return vb2_queue_init(dst_vq);
}
diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c
index 2d4e73b45c5e..3890f4f42a78 100644
--- a/drivers/media/platform/vivi.c
+++ b/drivers/media/platform/vivi.c
@@ -70,10 +70,6 @@ static unsigned debug;
module_param(debug, uint, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
-static unsigned int vid_limit = 16;
-module_param(vid_limit, uint, 0644);
-MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
-
/* Global font descriptor */
static const u8 *font8x16;
@@ -191,7 +187,6 @@ struct vivi_buffer {
/* common v4l buffer stuff -- must be first */
struct vb2_buffer vb;
struct list_head list;
- const struct vivi_fmt *fmt;
};
struct vivi_dmaqueue {
@@ -254,7 +249,7 @@ struct vivi_dev {
struct v4l2_fract timeperframe;
unsigned int width, height;
struct vb2_queue vb_vidq;
- unsigned int field_count;
+ unsigned int seq_count;
u8 bars[9][3];
u8 line[MAX_WIDTH * 8] __attribute__((__aligned__(4)));
@@ -675,8 +670,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
dev->mv_count += 2;
buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
- dev->field_count++;
- buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
+ buf->vb.v4l2_buf.sequence = dev->seq_count++;
v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
}
@@ -818,19 +812,15 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
struct vivi_dev *dev = vb2_get_drv_priv(vq);
unsigned long size;
- if (fmt)
+ size = dev->width * dev->height * dev->pixelsize;
+ if (fmt) {
+ if (fmt->fmt.pix.sizeimage < size)
+ return -EINVAL;
size = fmt->fmt.pix.sizeimage;
- else
- size = dev->width * dev->height * dev->pixelsize;
-
- if (size == 0)
- return -EINVAL;
-
- if (0 == *nbuffers)
- *nbuffers = 32;
-
- while (size * *nbuffers > vid_limit * 1024 * 1024)
- (*nbuffers)--;
+ /* check against insane over 8K resolution buffers */
+ if (size > 7680 * 4320 * dev->pixelsize)
+ return -EINVAL;
+ }
*nplanes = 1;
@@ -876,8 +866,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
vb2_set_plane_payload(&buf->vb, 0, size);
- buf->fmt = dev->fmt;
-
precalculate_bars(dev);
precalculate_line(dev);
@@ -901,8 +889,20 @@ static void buffer_queue(struct vb2_buffer *vb)
static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ int err;
+
dprintk(dev, 1, "%s\n", __func__);
- return vivi_start_generating(dev);
+ dev->seq_count = 0;
+ err = vivi_start_generating(dev);
+ if (err) {
+ struct vivi_buffer *buf, *tmp;
+
+ list_for_each_entry_safe(buf, tmp, &dev->vidq.active, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+ }
+ }
+ return err;
}
/* abort streaming and wait for last buffer */
@@ -1121,7 +1121,11 @@ static int vidioc_enum_frameintervals(struct file *file, void *priv,
if (!fmt)
return -EINVAL;
- /* regarding width & height - we support any */
+ /* check for valid width/height */
+ if (fival->width < 48 || fival->width > MAX_WIDTH || (fival->width & 3))
+ return -EINVAL;
+ if (fival->height < 32 || fival->height > MAX_HEIGHT)
+ return -EINVAL;
fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
@@ -1439,7 +1443,7 @@ static int __init vivi_create_instance(int inst)
q->buf_struct_size = sizeof(struct vivi_buffer);
q->ops = &vivi_video_qops;
q->mem_ops = &vb2_vmalloc_memops;
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(q);
if (ret)
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index 94d1b02680c5..0313210c6e9e 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -1,7 +1,7 @@
/*
* vsp1.h -- R-Car VSP1 Driver
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index 0df0a994e575..2f74f0e0ddf5 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -1,7 +1,7 @@
/*
* vsp1_drv.c -- R-Car VSP1 Driver
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
index 0226e47df6d9..3fc9e4266caf 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -1,7 +1,7 @@
/*
* vsp1_entity.c -- R-Car VSP1 Base Entity
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
index e152798d7f38..f6fd6988aeb0 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/vsp1/vsp1_entity.h
@@ -1,7 +1,7 @@
/*
* vsp1_entity.h -- R-Car VSP1 Base Entity
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c
index 74a32e69ef10..135a78957014 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.c
+++ b/drivers/media/platform/vsp1/vsp1_lif.c
@@ -1,7 +1,7 @@
/*
* vsp1_lif.c -- R-Car VSP1 LCD Controller Interface
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/vsp1/vsp1_lif.h
index 89b93af56fdc..7b35879028de 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.h
+++ b/drivers/media/platform/vsp1/vsp1_lif.h
@@ -1,7 +1,7 @@
/*
* vsp1_lif.h -- R-Car VSP1 LCD Controller Interface
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
index bce2be5466b9..2b04d0f95c62 100644
--- a/drivers/media/platform/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -1,7 +1,7 @@
/*
* vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c
index 782f770daee5..ec3dab6a9b9b 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.c
@@ -1,7 +1,7 @@
/*
* vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index 6cbdb547470b..5c5ee81bbeae 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -1,7 +1,7 @@
/*
* vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c
index 0e50b37f060d..622342ac7770 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/vsp1/vsp1_uds.c
@@ -1,7 +1,7 @@
/*
* vsp1_uds.c -- R-Car VSP1 Up and Down Scaler
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h
index 972a285abdb9..479d12df1180 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.h
+++ b/drivers/media/platform/vsp1/vsp1_uds.h
@@ -1,7 +1,7 @@
/*
* vsp1_uds.h -- R-Car VSP1 Up and Down Scaler
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index b4687a834f85..b48f135ffc01 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -1,7 +1,7 @@
/*
* vsp1_video.c -- R-Car VSP1 Video Node
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -1051,7 +1051,7 @@ int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf)
video->queue.buf_struct_size = sizeof(struct vsp1_video_buffer);
video->queue.ops = &vsp1_video_queue_qops;
video->queue.mem_ops = &vb2_dma_contig_memops;
- video->queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(&video->queue);
if (ret < 0) {
dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n");
diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h
index d8612a378345..53e4b3745940 100644
--- a/drivers/media/platform/vsp1/vsp1_video.h
+++ b/drivers/media/platform/vsp1/vsp1_video.h
@@ -1,7 +1,7 @@
/*
* vsp1_video.h -- R-Car VSP1 Video Node
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 7baed81ff005..11a61c601da0 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -1,7 +1,7 @@
/*
* vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index 545c04cf7226..d719e59e2179 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -270,6 +270,16 @@ reset_rds:
outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
}
+static bool cadet_has_rds_data(struct cadet *dev)
+{
+ bool result;
+
+ mutex_lock(&dev->lock);
+ result = dev->rdsin != dev->rdsout;
+ mutex_unlock(&dev->lock);
+ return result;
+}
+
static void cadet_handler(unsigned long data)
{
@@ -279,13 +289,12 @@ static void cadet_handler(unsigned long data)
if (mutex_trylock(&dev->lock)) {
outb(0x3, dev->io); /* Select RDS Decoder Control */
if ((inb(dev->io + 1) & 0x20) != 0)
- printk(KERN_CRIT "cadet: RDS fifo overflow\n");
+ pr_err("cadet: RDS fifo overflow\n");
outb(0x80, dev->io); /* Select RDS fifo */
+
while ((inb(dev->io) & 0x80) != 0) {
dev->rdsbuf[dev->rdsin] = inb(dev->io + 1);
- if (dev->rdsin + 1 == dev->rdsout)
- printk(KERN_WARNING "cadet: RDS buffer overflow\n");
- else
+ if (dev->rdsin + 1 != dev->rdsout)
dev->rdsin++;
}
mutex_unlock(&dev->lock);
@@ -294,7 +303,7 @@ static void cadet_handler(unsigned long data)
/*
* Service pending read
*/
- if (dev->rdsin != dev->rdsout)
+ if (cadet_has_rds_data(dev))
wake_up_interruptible(&dev->read_queue);
/*
@@ -327,22 +336,21 @@ static ssize_t cadet_read(struct file *file, char __user *data, size_t count, lo
mutex_lock(&dev->lock);
if (dev->rdsstat == 0)
cadet_start_rds(dev);
- if (dev->rdsin == dev->rdsout) {
- if (file->f_flags & O_NONBLOCK) {
- i = -EWOULDBLOCK;
- goto unlock;
- }
- mutex_unlock(&dev->lock);
- interruptible_sleep_on(&dev->read_queue);
- mutex_lock(&dev->lock);
- }
+ mutex_unlock(&dev->lock);
+
+ if (!cadet_has_rds_data(dev) && (file->f_flags & O_NONBLOCK))
+ return -EWOULDBLOCK;
+ i = wait_event_interruptible(dev->read_queue, cadet_has_rds_data(dev));
+ if (i)
+ return i;
+
+ mutex_lock(&dev->lock);
while (i < count && dev->rdsin != dev->rdsout)
readbuf[i++] = dev->rdsbuf[dev->rdsout++];
+ mutex_unlock(&dev->lock);
if (i && copy_to_user(data, readbuf, i))
- i = -EFAULT;
-unlock:
- mutex_unlock(&dev->lock);
+ return -EFAULT;
return i;
}
@@ -352,7 +360,7 @@ static int vidioc_querycap(struct file *file, void *priv,
{
strlcpy(v->driver, "ADS Cadet", sizeof(v->driver));
strlcpy(v->card, "ADS Cadet", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ strlcpy(v->bus_info, "ISA:radio-cadet", sizeof(v->bus_info));
v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
@@ -491,7 +499,7 @@ static unsigned int cadet_poll(struct file *file, struct poll_table_struct *wait
cadet_start_rds(dev);
mutex_unlock(&dev->lock);
}
- if (dev->rdsin != dev->rdsout)
+ if (cadet_has_rds_data(dev))
res |= POLLIN | POLLRDNORM;
return res;
}
diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c
index fa3964022b96..3d127825eceb 100644
--- a/drivers/media/radio/radio-keene.c
+++ b/drivers/media/radio/radio-keene.c
@@ -416,22 +416,5 @@ static struct usb_driver usb_keene_driver = {
.reset_resume = usb_keene_resume,
};
-static int __init keene_init(void)
-{
- int retval = usb_register(&usb_keene_driver);
-
- if (retval)
- pr_err(KBUILD_MODNAME
- ": usb_register failed. Error number %d\n", retval);
-
- return retval;
-}
-
-static void __exit keene_exit(void)
-{
- usb_deregister(&usb_keene_driver);
-}
-
-module_init(keene_init);
-module_exit(keene_exit);
+module_usb_driver(usb_keene_driver);
diff --git a/drivers/media/radio/si4713/Kconfig b/drivers/media/radio/si4713/Kconfig
index a7c3ba85d12b..9c8b887cff75 100644
--- a/drivers/media/radio/si4713/Kconfig
+++ b/drivers/media/radio/si4713/Kconfig
@@ -1,7 +1,7 @@
config USB_SI4713
tristate "Silicon Labs Si4713 FM Radio Transmitter support with USB"
- depends on USB && RADIO_SI4713
- select SI4713
+ depends on USB && I2C && RADIO_SI4713
+ select I2C_SI4713
---help---
This is a driver for USB devices with the Silicon Labs SI4713
chip. Currently these devices are known to work.
@@ -16,7 +16,7 @@ config USB_SI4713
config PLATFORM_SI4713
tristate "Silicon Labs Si4713 FM Radio Transmitter support with I2C"
depends on I2C && RADIO_SI4713
- select SI4713
+ select I2C_SI4713
---help---
This is a driver for I2C devices with the Silicon Labs SI4713
chip.
diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c
index 779855b74bcd..86502b2786d0 100644
--- a/drivers/media/radio/si4713/radio-usb-si4713.c
+++ b/drivers/media/radio/si4713/radio-usb-si4713.c
@@ -223,7 +223,7 @@ struct si4713_start_seq_table {
* (0x03): Get serial number of the board (Response : CB000-00-00)
* (0x06, 0x03, 0x03, 0x08, 0x01, 0x0f) : Get Component revision
*/
-static struct si4713_start_seq_table start_seq[] = {
+static const struct si4713_start_seq_table start_seq[] = {
{ 1, { 0x03 } },
{ 2, { 0x32, 0x7f } },
@@ -261,7 +261,7 @@ static int si4713_start_seq(struct si4713_usb_device *radio)
for (i = 0; i < ARRAY_SIZE(start_seq); i++) {
int len = start_seq[i].len;
- u8 *payload = start_seq[i].payload;
+ const u8 *payload = start_seq[i].payload;
memcpy(radio->buffer + 1, payload, len);
memset(radio->buffer + len + 1, 0, BUFFER_LENGTH - 1 - len);
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 904f11367c29..8fbd377e6311 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -106,6 +106,15 @@ config IR_SANYO_DECODER
uses the Sanyo protocol (Sanyo, Aiwa, Chinon remotes),
and you need software decoding support.
+config IR_SHARP_DECODER
+ tristate "Enable IR raw decoder for the Sharp protocol"
+ depends on RC_CORE
+ default y
+
+ ---help---
+ Enable this option if you have an infrared remote control which
+ uses the Sharp protocol, and you need software decoding support.
+
config IR_MCE_KBD_DECODER
tristate "Enable IR raw decoder for the MCE keyboard/mouse protocol"
depends on RC_CORE
@@ -300,6 +309,8 @@ config IR_RX51
The driver uses omap DM timers for generating the carrier
wave and pulses.
+source "drivers/media/rc/img-ir/Kconfig"
+
config RC_LOOPBACK
tristate "Remote Control Loopback Driver"
depends on RC_CORE
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index f4eb32c0a455..f8b54ff46601 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o
obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o
obj-$(CONFIG_IR_RC5_SZ_DECODER) += ir-rc5-sz-decoder.o
obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o
+obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o
obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o
obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
@@ -31,3 +32,4 @@ obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
obj-$(CONFIG_IR_IGUANA) += iguanair.o
obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
obj-$(CONFIG_RC_ST) += st_rc.o
+obj-$(CONFIG_IR_IMG) += img-ir/
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
index 4d6a63fe6c5e..2df7c5516013 100644
--- a/drivers/media/rc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -784,7 +784,7 @@ static void ati_remote_rc_init(struct ati_remote *ati_remote)
rdev->priv = ati_remote;
rdev->driver_type = RC_DRIVER_SCANCODE;
- rdev->allowed_protos = RC_BIT_OTHER;
+ rc_set_allowed_protocols(rdev, RC_BIT_OTHER);
rdev->driver_name = "ati_remote";
rdev->open = ati_remote_rc_open;
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index c1444f84717d..fc9d23f2ed3f 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1059,7 +1059,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
learning_mode_force = false;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
rdev->priv = dev;
rdev->open = ene_open;
rdev->close = ene_close;
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index d6fa441655d2..46b66e59438f 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -541,7 +541,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
/* Set up the rc device */
rdev->priv = fintek;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
rdev->open = fintek_open;
rdev->close = fintek_close;
rdev->input_name = FINTEK_DESCRIPTION;
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 80c611c2e8c2..29b5f89813b4 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -145,9 +145,9 @@ static int gpio_ir_recv_probe(struct platform_device *pdev)
rcdev->dev.parent = &pdev->dev;
rcdev->driver_name = GPIO_IR_DRIVER_NAME;
if (pdata->allowed_protos)
- rcdev->allowed_protos = pdata->allowed_protos;
+ rc_set_allowed_protocols(rcdev, pdata->allowed_protos);
else
- rcdev->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rcdev, RC_BIT_ALL);
rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY;
gpio_dev->rcdev = rcdev;
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index fdae05c4f377..627ddfd61980 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -286,10 +286,10 @@ static int iguanair_receiver(struct iguanair *ir, bool enable)
}
/*
- * The iguana ir creates the carrier by busy spinning after each pulse or
- * space. This is counted in CPU cycles, with the CPU running at 24MHz. It is
+ * The iguanair creates the carrier by busy spinning after each half period.
+ * This is counted in CPU cycles, with the CPU running at 24MHz. It is
* broken down into 7-cycles and 4-cyles delays, with a preference for
- * 4-cycle delays.
+ * 4-cycle delays, minus the overhead of the loop itself (cycle_overhead).
*/
static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
{
@@ -316,7 +316,14 @@ static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
sevens = (4 - cycles) & 3;
fours = (cycles - sevens * 7) / 4;
- /* magic happens here */
+ /*
+ * The firmware interprets these values as a relative offset
+ * for a branch. Immediately following the branches, there
+ * 4 instructions of 7 cycles (2 bytes each) and 110
+ * instructions of 4 cycles (1 byte each). A relative branch
+ * of 0 will execute all of them, branch further for less
+ * cycle burning.
+ */
ir->packet->busy7 = (4 - sevens) * 2;
ir->packet->busy4 = 110 - fours;
}
@@ -357,20 +364,14 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
rc = -EINVAL;
goto out;
}
- while (periods > 127) {
- ir->packet->payload[size++] = 127 | space;
- periods -= 127;
+ while (periods) {
+ unsigned p = min(periods, 127u);
+ ir->packet->payload[size++] = p | space;
+ periods -= p;
}
-
- ir->packet->payload[size++] = periods | space;
space ^= 0x80;
}
- if (count == 0) {
- rc = -EINVAL;
- goto out;
- }
-
ir->packet->header.start = 0;
ir->packet->header.direction = DIR_OUT;
ir->packet->header.cmd = CMD_SEND;
@@ -494,7 +495,7 @@ static int iguanair_probe(struct usb_interface *intf,
usb_to_input_id(ir->udev, &rc->input_id);
rc->dev.parent = &intf->dev;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rc, RC_BIT_ALL);
rc->priv = ir;
rc->open = iguanair_open;
rc->close = iguanair_close;
diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig
new file mode 100644
index 000000000000..03ba9fc170fb
--- /dev/null
+++ b/drivers/media/rc/img-ir/Kconfig
@@ -0,0 +1,61 @@
+config IR_IMG
+ tristate "ImgTec IR Decoder"
+ depends on RC_CORE
+ select IR_IMG_HW if !IR_IMG_RAW
+ help
+ Say Y or M here if you want to use the ImgTec infrared decoder
+ functionality found in SoCs such as TZ1090.
+
+config IR_IMG_RAW
+ bool "Raw decoder"
+ depends on IR_IMG
+ help
+ Say Y here to enable the raw mode driver which passes raw IR signal
+ changes to the IR raw decoders for software decoding. This is much
+ less reliable (due to lack of timestamps) and consumes more
+ processing power than using hardware decode, but can be useful for
+ testing, debug, and to make more protocols available.
+
+config IR_IMG_HW
+ bool "Hardware decoder"
+ depends on IR_IMG
+ help
+ Say Y here to enable the hardware decode driver which decodes the IR
+ signals in hardware. This is more reliable, consumes less processing
+ power since only a single interrupt is received for each scancode,
+ and allows an IR scancode to be used as a wake event.
+
+config IR_IMG_NEC
+ bool "NEC protocol support"
+ depends on IR_IMG_HW
+ help
+ Say Y here to enable support for the NEC, extended NEC, and 32-bit
+ NEC protocols in the ImgTec infrared decoder block.
+
+config IR_IMG_JVC
+ bool "JVC protocol support"
+ depends on IR_IMG_HW
+ help
+ Say Y here to enable support for the JVC protocol in the ImgTec
+ infrared decoder block.
+
+config IR_IMG_SONY
+ bool "Sony protocol support"
+ depends on IR_IMG_HW
+ help
+ Say Y here to enable support for the Sony protocol in the ImgTec
+ infrared decoder block.
+
+config IR_IMG_SHARP
+ bool "Sharp protocol support"
+ depends on IR_IMG_HW
+ help
+ Say Y here to enable support for the Sharp protocol in the ImgTec
+ infrared decoder block.
+
+config IR_IMG_SANYO
+ bool "Sanyo protocol support"
+ depends on IR_IMG_HW
+ help
+ Say Y here to enable support for the Sanyo protocol (used by Sanyo,
+ Aiwa, Chinon remotes) in the ImgTec infrared decoder block.
diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile
new file mode 100644
index 000000000000..92a459d99509
--- /dev/null
+++ b/drivers/media/rc/img-ir/Makefile
@@ -0,0 +1,11 @@
+img-ir-y := img-ir-core.o
+img-ir-$(CONFIG_IR_IMG_RAW) += img-ir-raw.o
+img-ir-$(CONFIG_IR_IMG_HW) += img-ir-hw.o
+img-ir-$(CONFIG_IR_IMG_NEC) += img-ir-nec.o
+img-ir-$(CONFIG_IR_IMG_JVC) += img-ir-jvc.o
+img-ir-$(CONFIG_IR_IMG_SONY) += img-ir-sony.o
+img-ir-$(CONFIG_IR_IMG_SHARP) += img-ir-sharp.o
+img-ir-$(CONFIG_IR_IMG_SANYO) += img-ir-sanyo.o
+img-ir-objs := $(img-ir-y)
+
+obj-$(CONFIG_IR_IMG) += img-ir.o
diff --git a/drivers/media/rc/img-ir/img-ir-core.c b/drivers/media/rc/img-ir/img-ir-core.c
new file mode 100644
index 000000000000..6b7834834fb8
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-core.c
@@ -0,0 +1,176 @@
+/*
+ * ImgTec IR Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * This contains core img-ir code for setting up the driver. The two interfaces
+ * (raw and hardware decode) are handled separately.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include "img-ir.h"
+
+static irqreturn_t img_ir_isr(int irq, void *dev_id)
+{
+ struct img_ir_priv *priv = dev_id;
+ u32 irq_status;
+
+ spin_lock(&priv->lock);
+ /* we have to clear irqs before reading */
+ irq_status = img_ir_read(priv, IMG_IR_IRQ_STATUS);
+ img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_status);
+
+ /* don't handle valid data irqs if we're only interested in matches */
+ irq_status &= img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+
+ /* hand off edge interrupts to raw decode handler */
+ if (irq_status & IMG_IR_IRQ_EDGE && img_ir_raw_enabled(&priv->raw))
+ img_ir_isr_raw(priv, irq_status);
+
+ /* hand off hardware match interrupts to hardware decode handler */
+ if (irq_status & (IMG_IR_IRQ_DATA_MATCH |
+ IMG_IR_IRQ_DATA_VALID |
+ IMG_IR_IRQ_DATA2_VALID) &&
+ img_ir_hw_enabled(&priv->hw))
+ img_ir_isr_hw(priv, irq_status);
+
+ spin_unlock(&priv->lock);
+ return IRQ_HANDLED;
+}
+
+static void img_ir_setup(struct img_ir_priv *priv)
+{
+ /* start off with interrupts disabled */
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, 0);
+
+ img_ir_setup_raw(priv);
+ img_ir_setup_hw(priv);
+
+ if (!IS_ERR(priv->clk))
+ clk_prepare_enable(priv->clk);
+}
+
+static void img_ir_ident(struct img_ir_priv *priv)
+{
+ u32 core_rev = img_ir_read(priv, IMG_IR_CORE_REV);
+
+ dev_info(priv->dev,
+ "IMG IR Decoder (%d.%d.%d.%d) probed successfully\n",
+ (core_rev & IMG_IR_DESIGNER) >> IMG_IR_DESIGNER_SHIFT,
+ (core_rev & IMG_IR_MAJOR_REV) >> IMG_IR_MAJOR_REV_SHIFT,
+ (core_rev & IMG_IR_MINOR_REV) >> IMG_IR_MINOR_REV_SHIFT,
+ (core_rev & IMG_IR_MAINT_REV) >> IMG_IR_MAINT_REV_SHIFT);
+ dev_info(priv->dev, "Modes:%s%s\n",
+ img_ir_hw_enabled(&priv->hw) ? " hardware" : "",
+ img_ir_raw_enabled(&priv->raw) ? " raw" : "");
+}
+
+static int img_ir_probe(struct platform_device *pdev)
+{
+ struct img_ir_priv *priv;
+ struct resource *res_regs;
+ int irq, error, error2;
+
+ /* Get resources from platform device */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "cannot find IRQ resource\n");
+ return irq;
+ }
+
+ /* Private driver data */
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "cannot allocate device data\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, priv);
+ priv->dev = &pdev->dev;
+ spin_lock_init(&priv->lock);
+
+ /* Ioremap the registers */
+ res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->reg_base = devm_ioremap_resource(&pdev->dev, res_regs);
+ if (IS_ERR(priv->reg_base))
+ return PTR_ERR(priv->reg_base);
+
+ /* Get core clock */
+ priv->clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(priv->clk))
+ dev_warn(&pdev->dev, "cannot get core clock resource\n");
+ /*
+ * The driver doesn't need to know about the system ("sys") or power
+ * modulation ("mod") clocks yet
+ */
+
+ /* Set up raw & hw decoder */
+ error = img_ir_probe_raw(priv);
+ error2 = img_ir_probe_hw(priv);
+ if (error && error2)
+ return (error == -ENODEV) ? error2 : error;
+
+ /* Get the IRQ */
+ priv->irq = irq;
+ error = request_irq(priv->irq, img_ir_isr, 0, "img-ir", priv);
+ if (error) {
+ dev_err(&pdev->dev, "cannot register IRQ %u\n",
+ priv->irq);
+ error = -EIO;
+ goto err_irq;
+ }
+
+ img_ir_ident(priv);
+ img_ir_setup(priv);
+
+ return 0;
+
+err_irq:
+ img_ir_remove_hw(priv);
+ img_ir_remove_raw(priv);
+ return error;
+}
+
+static int img_ir_remove(struct platform_device *pdev)
+{
+ struct img_ir_priv *priv = platform_get_drvdata(pdev);
+
+ free_irq(priv->irq, img_ir_isr);
+ img_ir_remove_hw(priv);
+ img_ir_remove_raw(priv);
+
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(img_ir_pmops, img_ir_suspend, img_ir_resume);
+
+static const struct of_device_id img_ir_match[] = {
+ { .compatible = "img,ir-rev1" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, img_ir_match);
+
+static struct platform_driver img_ir_driver = {
+ .driver = {
+ .name = "img-ir",
+ .owner = THIS_MODULE,
+ .of_match_table = img_ir_match,
+ .pm = &img_ir_pmops,
+ },
+ .probe = img_ir_probe,
+ .remove = img_ir_remove,
+};
+
+module_platform_driver(img_ir_driver);
+
+MODULE_AUTHOR("Imagination Technologies Ltd.");
+MODULE_DESCRIPTION("ImgTec IR");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
new file mode 100644
index 000000000000..579a52b3edce
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-hw.c
@@ -0,0 +1,1053 @@
+/*
+ * ImgTec IR Hardware Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * This ties into the input subsystem using the RC-core. Protocol support is
+ * provided in separate modules which provide the parameters and scancode
+ * translation functions to set up the hardware decoder and interpret the
+ * resulting input.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <media/rc-core.h>
+#include "img-ir.h"
+
+/* Decoders lock (only modified to preprocess them) */
+static DEFINE_SPINLOCK(img_ir_decoders_lock);
+
+extern struct img_ir_decoder img_ir_nec;
+extern struct img_ir_decoder img_ir_jvc;
+extern struct img_ir_decoder img_ir_sony;
+extern struct img_ir_decoder img_ir_sharp;
+extern struct img_ir_decoder img_ir_sanyo;
+
+static bool img_ir_decoders_preprocessed;
+static struct img_ir_decoder *img_ir_decoders[] = {
+#ifdef CONFIG_IR_IMG_NEC
+ &img_ir_nec,
+#endif
+#ifdef CONFIG_IR_IMG_JVC
+ &img_ir_jvc,
+#endif
+#ifdef CONFIG_IR_IMG_SONY
+ &img_ir_sony,
+#endif
+#ifdef CONFIG_IR_IMG_SHARP
+ &img_ir_sharp,
+#endif
+#ifdef CONFIG_IR_IMG_SANYO
+ &img_ir_sanyo,
+#endif
+ NULL
+};
+
+#define IMG_IR_F_FILTER BIT(RC_FILTER_NORMAL) /* enable filtering */
+#define IMG_IR_F_WAKE BIT(RC_FILTER_WAKEUP) /* enable waking */
+
+/* code type quirks */
+
+#define IMG_IR_QUIRK_CODE_BROKEN 0x1 /* Decode is broken */
+#define IMG_IR_QUIRK_CODE_LEN_INCR 0x2 /* Bit length needs increment */
+
+/* functions for preprocessing timings, ensuring max is set */
+
+static void img_ir_timing_preprocess(struct img_ir_timing_range *range,
+ unsigned int unit)
+{
+ if (range->max < range->min)
+ range->max = range->min;
+ if (unit) {
+ /* multiply by unit and convert to microseconds */
+ range->min = (range->min*unit)/1000;
+ range->max = (range->max*unit + 999)/1000; /* round up */
+ }
+}
+
+static void img_ir_symbol_timing_preprocess(struct img_ir_symbol_timing *timing,
+ unsigned int unit)
+{
+ img_ir_timing_preprocess(&timing->pulse, unit);
+ img_ir_timing_preprocess(&timing->space, unit);
+}
+
+static void img_ir_timings_preprocess(struct img_ir_timings *timings,
+ unsigned int unit)
+{
+ img_ir_symbol_timing_preprocess(&timings->ldr, unit);
+ img_ir_symbol_timing_preprocess(&timings->s00, unit);
+ img_ir_symbol_timing_preprocess(&timings->s01, unit);
+ img_ir_symbol_timing_preprocess(&timings->s10, unit);
+ img_ir_symbol_timing_preprocess(&timings->s11, unit);
+ /* default s10 and s11 to s00 and s01 if no leader */
+ if (unit)
+ /* multiply by unit and convert to microseconds (round up) */
+ timings->ft.ft_min = (timings->ft.ft_min*unit + 999)/1000;
+}
+
+/* functions for filling empty fields with defaults */
+
+static void img_ir_timing_defaults(struct img_ir_timing_range *range,
+ struct img_ir_timing_range *defaults)
+{
+ if (!range->min)
+ range->min = defaults->min;
+ if (!range->max)
+ range->max = defaults->max;
+}
+
+static void img_ir_symbol_timing_defaults(struct img_ir_symbol_timing *timing,
+ struct img_ir_symbol_timing *defaults)
+{
+ img_ir_timing_defaults(&timing->pulse, &defaults->pulse);
+ img_ir_timing_defaults(&timing->space, &defaults->space);
+}
+
+static void img_ir_timings_defaults(struct img_ir_timings *timings,
+ struct img_ir_timings *defaults)
+{
+ img_ir_symbol_timing_defaults(&timings->ldr, &defaults->ldr);
+ img_ir_symbol_timing_defaults(&timings->s00, &defaults->s00);
+ img_ir_symbol_timing_defaults(&timings->s01, &defaults->s01);
+ img_ir_symbol_timing_defaults(&timings->s10, &defaults->s10);
+ img_ir_symbol_timing_defaults(&timings->s11, &defaults->s11);
+ if (!timings->ft.ft_min)
+ timings->ft.ft_min = defaults->ft.ft_min;
+}
+
+/* functions for converting timings to register values */
+
+/**
+ * img_ir_control() - Convert control struct to control register value.
+ * @control: Control data
+ *
+ * Returns: The control register value equivalent of @control.
+ */
+static u32 img_ir_control(const struct img_ir_control *control)
+{
+ u32 ctrl = control->code_type << IMG_IR_CODETYPE_SHIFT;
+ if (control->decoden)
+ ctrl |= IMG_IR_DECODEN;
+ if (control->hdrtog)
+ ctrl |= IMG_IR_HDRTOG;
+ if (control->ldrdec)
+ ctrl |= IMG_IR_LDRDEC;
+ if (control->decodinpol)
+ ctrl |= IMG_IR_DECODINPOL;
+ if (control->bitorien)
+ ctrl |= IMG_IR_BITORIEN;
+ if (control->d1validsel)
+ ctrl |= IMG_IR_D1VALIDSEL;
+ if (control->bitinv)
+ ctrl |= IMG_IR_BITINV;
+ if (control->decodend2)
+ ctrl |= IMG_IR_DECODEND2;
+ if (control->bitoriend2)
+ ctrl |= IMG_IR_BITORIEND2;
+ if (control->bitinvd2)
+ ctrl |= IMG_IR_BITINVD2;
+ return ctrl;
+}
+
+/**
+ * img_ir_timing_range_convert() - Convert microsecond range.
+ * @out: Output timing range in clock cycles with a shift.
+ * @in: Input timing range in microseconds.
+ * @tolerance: Tolerance as a fraction of 128 (roughly percent).
+ * @clock_hz: IR clock rate in Hz.
+ * @shift: Shift of output units.
+ *
+ * Converts min and max from microseconds to IR clock cycles, applies a
+ * tolerance, and shifts for the register, rounding in the right direction.
+ * Note that in and out can safely be the same object.
+ */
+static void img_ir_timing_range_convert(struct img_ir_timing_range *out,
+ const struct img_ir_timing_range *in,
+ unsigned int tolerance,
+ unsigned long clock_hz,
+ unsigned int shift)
+{
+ unsigned int min = in->min;
+ unsigned int max = in->max;
+ /* add a tolerance */
+ min = min - (min*tolerance >> 7);
+ max = max + (max*tolerance >> 7);
+ /* convert from microseconds into clock cycles */
+ min = min*clock_hz / 1000000;
+ max = (max*clock_hz + 999999) / 1000000; /* round up */
+ /* apply shift and copy to output */
+ out->min = min >> shift;
+ out->max = (max + ((1 << shift) - 1)) >> shift; /* round up */
+}
+
+/**
+ * img_ir_symbol_timing() - Convert symbol timing struct to register value.
+ * @timing: Symbol timing data
+ * @tolerance: Timing tolerance where 0-128 represents 0-100%
+ * @clock_hz: Frequency of source clock in Hz
+ * @pd_shift: Shift to apply to symbol period
+ * @w_shift: Shift to apply to symbol width
+ *
+ * Returns: Symbol timing register value based on arguments.
+ */
+static u32 img_ir_symbol_timing(const struct img_ir_symbol_timing *timing,
+ unsigned int tolerance,
+ unsigned long clock_hz,
+ unsigned int pd_shift,
+ unsigned int w_shift)
+{
+ struct img_ir_timing_range hw_pulse, hw_period;
+ /* we calculate period in hw_period, then convert in place */
+ hw_period.min = timing->pulse.min + timing->space.min;
+ hw_period.max = timing->pulse.max + timing->space.max;
+ img_ir_timing_range_convert(&hw_period, &hw_period,
+ tolerance, clock_hz, pd_shift);
+ img_ir_timing_range_convert(&hw_pulse, &timing->pulse,
+ tolerance, clock_hz, w_shift);
+ /* construct register value */
+ return (hw_period.max << IMG_IR_PD_MAX_SHIFT) |
+ (hw_period.min << IMG_IR_PD_MIN_SHIFT) |
+ (hw_pulse.max << IMG_IR_W_MAX_SHIFT) |
+ (hw_pulse.min << IMG_IR_W_MIN_SHIFT);
+}
+
+/**
+ * img_ir_free_timing() - Convert free time timing struct to register value.
+ * @timing: Free symbol timing data
+ * @clock_hz: Source clock frequency in Hz
+ *
+ * Returns: Free symbol timing register value.
+ */
+static u32 img_ir_free_timing(const struct img_ir_free_timing *timing,
+ unsigned long clock_hz)
+{
+ unsigned int minlen, maxlen, ft_min;
+ /* minlen is only 5 bits, and round minlen to multiple of 2 */
+ if (timing->minlen < 30)
+ minlen = timing->minlen & -2;
+ else
+ minlen = 30;
+ /* maxlen has maximum value of 48, and round maxlen to multiple of 2 */
+ if (timing->maxlen < 48)
+ maxlen = (timing->maxlen + 1) & -2;
+ else
+ maxlen = 48;
+ /* convert and shift ft_min, rounding upwards */
+ ft_min = (timing->ft_min*clock_hz + 999999) / 1000000;
+ ft_min = (ft_min + 7) >> 3;
+ /* construct register value */
+ return (maxlen << IMG_IR_MAXLEN_SHIFT) |
+ (minlen << IMG_IR_MINLEN_SHIFT) |
+ (ft_min << IMG_IR_FT_MIN_SHIFT);
+}
+
+/**
+ * img_ir_free_timing_dynamic() - Update free time register value.
+ * @st_ft: Static free time register value from img_ir_free_timing.
+ * @filter: Current filter which may additionally restrict min/max len.
+ *
+ * Returns: Updated free time register value based on the current filter.
+ */
+static u32 img_ir_free_timing_dynamic(u32 st_ft, struct img_ir_filter *filter)
+{
+ unsigned int minlen, maxlen, newminlen, newmaxlen;
+
+ /* round minlen, maxlen to multiple of 2 */
+ newminlen = filter->minlen & -2;
+ newmaxlen = (filter->maxlen + 1) & -2;
+ /* extract min/max len from register */
+ minlen = (st_ft & IMG_IR_MINLEN) >> IMG_IR_MINLEN_SHIFT;
+ maxlen = (st_ft & IMG_IR_MAXLEN) >> IMG_IR_MAXLEN_SHIFT;
+ /* if the new values are more restrictive, update the register value */
+ if (newminlen > minlen) {
+ st_ft &= ~IMG_IR_MINLEN;
+ st_ft |= newminlen << IMG_IR_MINLEN_SHIFT;
+ }
+ if (newmaxlen < maxlen) {
+ st_ft &= ~IMG_IR_MAXLEN;
+ st_ft |= newmaxlen << IMG_IR_MAXLEN_SHIFT;
+ }
+ return st_ft;
+}
+
+/**
+ * img_ir_timings_convert() - Convert timings to register values
+ * @regs: Output timing register values
+ * @timings: Input timing data
+ * @tolerance: Timing tolerance where 0-128 represents 0-100%
+ * @clock_hz: Source clock frequency in Hz
+ */
+static void img_ir_timings_convert(struct img_ir_timing_regvals *regs,
+ const struct img_ir_timings *timings,
+ unsigned int tolerance,
+ unsigned int clock_hz)
+{
+ /* leader symbol timings are divided by 16 */
+ regs->ldr = img_ir_symbol_timing(&timings->ldr, tolerance, clock_hz,
+ 4, 4);
+ /* other symbol timings, pd fields only are divided by 2 */
+ regs->s00 = img_ir_symbol_timing(&timings->s00, tolerance, clock_hz,
+ 1, 0);
+ regs->s01 = img_ir_symbol_timing(&timings->s01, tolerance, clock_hz,
+ 1, 0);
+ regs->s10 = img_ir_symbol_timing(&timings->s10, tolerance, clock_hz,
+ 1, 0);
+ regs->s11 = img_ir_symbol_timing(&timings->s11, tolerance, clock_hz,
+ 1, 0);
+ regs->ft = img_ir_free_timing(&timings->ft, clock_hz);
+}
+
+/**
+ * img_ir_decoder_preprocess() - Preprocess timings in decoder.
+ * @decoder: Decoder to be preprocessed.
+ *
+ * Ensures that the symbol timing ranges are valid with respect to ordering, and
+ * does some fixed conversion on them.
+ */
+static void img_ir_decoder_preprocess(struct img_ir_decoder *decoder)
+{
+ /* default tolerance */
+ if (!decoder->tolerance)
+ decoder->tolerance = 10; /* percent */
+ /* and convert tolerance to fraction out of 128 */
+ decoder->tolerance = decoder->tolerance * 128 / 100;
+
+ /* fill in implicit fields */
+ img_ir_timings_preprocess(&decoder->timings, decoder->unit);
+
+ /* do the same for repeat timings if applicable */
+ if (decoder->repeat) {
+ img_ir_timings_preprocess(&decoder->rtimings, decoder->unit);
+ img_ir_timings_defaults(&decoder->rtimings, &decoder->timings);
+ }
+}
+
+/**
+ * img_ir_decoder_convert() - Generate internal timings in decoder.
+ * @decoder: Decoder to be converted to internal timings.
+ * @timings: Timing register values.
+ * @clock_hz: IR clock rate in Hz.
+ *
+ * Fills out the repeat timings and timing register values for a specific clock
+ * rate.
+ */
+static void img_ir_decoder_convert(const struct img_ir_decoder *decoder,
+ struct img_ir_reg_timings *reg_timings,
+ unsigned int clock_hz)
+{
+ /* calculate control value */
+ reg_timings->ctrl = img_ir_control(&decoder->control);
+
+ /* fill in implicit fields and calculate register values */
+ img_ir_timings_convert(&reg_timings->timings, &decoder->timings,
+ decoder->tolerance, clock_hz);
+
+ /* do the same for repeat timings if applicable */
+ if (decoder->repeat)
+ img_ir_timings_convert(&reg_timings->rtimings,
+ &decoder->rtimings, decoder->tolerance,
+ clock_hz);
+}
+
+/**
+ * img_ir_write_timings() - Write timings to the hardware now
+ * @priv: IR private data
+ * @regs: Timing register values to write
+ * @type: RC filter type (RC_FILTER_*)
+ *
+ * Write timing register values @regs to the hardware, taking into account the
+ * current filter which may impose restrictions on the length of the expected
+ * data.
+ */
+static void img_ir_write_timings(struct img_ir_priv *priv,
+ struct img_ir_timing_regvals *regs,
+ enum rc_filter_type type)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+
+ /* filter may be more restrictive to minlen, maxlen */
+ u32 ft = regs->ft;
+ if (hw->flags & BIT(type))
+ ft = img_ir_free_timing_dynamic(regs->ft, &hw->filters[type]);
+ /* write to registers */
+ img_ir_write(priv, IMG_IR_LEAD_SYMB_TIMING, regs->ldr);
+ img_ir_write(priv, IMG_IR_S00_SYMB_TIMING, regs->s00);
+ img_ir_write(priv, IMG_IR_S01_SYMB_TIMING, regs->s01);
+ img_ir_write(priv, IMG_IR_S10_SYMB_TIMING, regs->s10);
+ img_ir_write(priv, IMG_IR_S11_SYMB_TIMING, regs->s11);
+ img_ir_write(priv, IMG_IR_FREE_SYMB_TIMING, ft);
+ dev_dbg(priv->dev, "timings: ldr=%#x, s=[%#x, %#x, %#x, %#x], ft=%#x\n",
+ regs->ldr, regs->s00, regs->s01, regs->s10, regs->s11, ft);
+}
+
+static void img_ir_write_filter(struct img_ir_priv *priv,
+ struct img_ir_filter *filter)
+{
+ if (filter) {
+ dev_dbg(priv->dev, "IR filter=%016llx & %016llx\n",
+ (unsigned long long)filter->data,
+ (unsigned long long)filter->mask);
+ img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_LW, (u32)filter->data);
+ img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_UP, (u32)(filter->data
+ >> 32));
+ img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, (u32)filter->mask);
+ img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, (u32)(filter->mask
+ >> 32));
+ } else {
+ dev_dbg(priv->dev, "IR clearing filter\n");
+ img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, 0);
+ img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, 0);
+ }
+}
+
+/* caller must have lock */
+static void _img_ir_set_filter(struct img_ir_priv *priv,
+ struct img_ir_filter *filter)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ u32 irq_en, irq_on;
+
+ irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+ if (filter) {
+ /* Only use the match interrupt */
+ hw->filters[RC_FILTER_NORMAL] = *filter;
+ hw->flags |= IMG_IR_F_FILTER;
+ irq_on = IMG_IR_IRQ_DATA_MATCH;
+ irq_en &= ~(IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID);
+ } else {
+ /* Only use the valid interrupt */
+ hw->flags &= ~IMG_IR_F_FILTER;
+ irq_en &= ~IMG_IR_IRQ_DATA_MATCH;
+ irq_on = IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID;
+ }
+ irq_en |= irq_on;
+
+ img_ir_write_filter(priv, filter);
+ /* clear any interrupts we're enabling so we don't handle old ones */
+ img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_on);
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
+}
+
+/* caller must have lock */
+static void _img_ir_set_wake_filter(struct img_ir_priv *priv,
+ struct img_ir_filter *filter)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ if (filter) {
+ /* Enable wake, and copy filter for later */
+ hw->filters[RC_FILTER_WAKEUP] = *filter;
+ hw->flags |= IMG_IR_F_WAKE;
+ } else {
+ /* Disable wake */
+ hw->flags &= ~IMG_IR_F_WAKE;
+ }
+}
+
+/* Callback for setting scancode filter */
+static int img_ir_set_filter(struct rc_dev *dev, enum rc_filter_type type,
+ struct rc_scancode_filter *sc_filter)
+{
+ struct img_ir_priv *priv = dev->priv;
+ struct img_ir_priv_hw *hw = &priv->hw;
+ struct img_ir_filter filter, *filter_ptr = &filter;
+ int ret = 0;
+
+ dev_dbg(priv->dev, "IR scancode %sfilter=%08x & %08x\n",
+ type == RC_FILTER_WAKEUP ? "wake " : "",
+ sc_filter->data,
+ sc_filter->mask);
+
+ spin_lock_irq(&priv->lock);
+
+ /* filtering can always be disabled */
+ if (!sc_filter->mask) {
+ filter_ptr = NULL;
+ goto set_unlock;
+ }
+
+ /* current decoder must support scancode filtering */
+ if (!hw->decoder || !hw->decoder->filter) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ /* convert scancode filter to raw filter */
+ filter.minlen = 0;
+ filter.maxlen = ~0;
+ ret = hw->decoder->filter(sc_filter, &filter, hw->enabled_protocols);
+ if (ret)
+ goto unlock;
+ dev_dbg(priv->dev, "IR raw %sfilter=%016llx & %016llx\n",
+ type == RC_FILTER_WAKEUP ? "wake " : "",
+ (unsigned long long)filter.data,
+ (unsigned long long)filter.mask);
+
+set_unlock:
+ /* apply raw filters */
+ switch (type) {
+ case RC_FILTER_NORMAL:
+ _img_ir_set_filter(priv, filter_ptr);
+ break;
+ case RC_FILTER_WAKEUP:
+ _img_ir_set_wake_filter(priv, filter_ptr);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+unlock:
+ spin_unlock_irq(&priv->lock);
+ return ret;
+}
+
+/**
+ * img_ir_set_decoder() - Set the current decoder.
+ * @priv: IR private data.
+ * @decoder: Decoder to use with immediate effect.
+ * @proto: Protocol bitmap (or 0 to use decoder->type).
+ */
+static void img_ir_set_decoder(struct img_ir_priv *priv,
+ const struct img_ir_decoder *decoder,
+ u64 proto)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ struct rc_dev *rdev = hw->rdev;
+ u32 ir_status, irq_en;
+ spin_lock_irq(&priv->lock);
+
+ /* switch off and disable interrupts */
+ img_ir_write(priv, IMG_IR_CONTROL, 0);
+ irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en & IMG_IR_IRQ_EDGE);
+ img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_ALL & ~IMG_IR_IRQ_EDGE);
+
+ /* ack any data already detected */
+ ir_status = img_ir_read(priv, IMG_IR_STATUS);
+ if (ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)) {
+ ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2);
+ img_ir_write(priv, IMG_IR_STATUS, ir_status);
+ img_ir_read(priv, IMG_IR_DATA_LW);
+ img_ir_read(priv, IMG_IR_DATA_UP);
+ }
+
+ /* stop the end timer and switch back to normal mode */
+ del_timer_sync(&hw->end_timer);
+ hw->mode = IMG_IR_M_NORMAL;
+
+ /* clear the wakeup scancode filter */
+ rdev->scancode_filters[RC_FILTER_WAKEUP].data = 0;
+ rdev->scancode_filters[RC_FILTER_WAKEUP].mask = 0;
+
+ /* clear raw filters */
+ _img_ir_set_filter(priv, NULL);
+ _img_ir_set_wake_filter(priv, NULL);
+
+ /* clear the enabled protocols */
+ hw->enabled_protocols = 0;
+
+ /* switch decoder */
+ hw->decoder = decoder;
+ if (!decoder)
+ goto unlock;
+
+ /* set the enabled protocols */
+ if (!proto)
+ proto = decoder->type;
+ hw->enabled_protocols = proto;
+
+ /* write the new timings */
+ img_ir_decoder_convert(decoder, &hw->reg_timings, hw->clk_hz);
+ img_ir_write_timings(priv, &hw->reg_timings.timings, RC_FILTER_NORMAL);
+
+ /* set up and enable */
+ img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
+
+
+unlock:
+ spin_unlock_irq(&priv->lock);
+}
+
+/**
+ * img_ir_decoder_compatable() - Find whether a decoder will work with a device.
+ * @priv: IR private data.
+ * @dec: Decoder to check.
+ *
+ * Returns: true if @dec is compatible with the device @priv refers to.
+ */
+static bool img_ir_decoder_compatible(struct img_ir_priv *priv,
+ const struct img_ir_decoder *dec)
+{
+ unsigned int ct;
+
+ /* don't accept decoders using code types which aren't supported */
+ ct = dec->control.code_type;
+ if (priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_BROKEN)
+ return false;
+
+ return true;
+}
+
+/**
+ * img_ir_allowed_protos() - Get allowed protocols from global decoder list.
+ * @priv: IR private data.
+ *
+ * Returns: Mask of protocols supported by the device @priv refers to.
+ */
+static u64 img_ir_allowed_protos(struct img_ir_priv *priv)
+{
+ u64 protos = 0;
+ struct img_ir_decoder **decp;
+
+ for (decp = img_ir_decoders; *decp; ++decp) {
+ const struct img_ir_decoder *dec = *decp;
+ if (img_ir_decoder_compatible(priv, dec))
+ protos |= dec->type;
+ }
+ return protos;
+}
+
+/* Callback for changing protocol using sysfs */
+static int img_ir_change_protocol(struct rc_dev *dev, u64 *ir_type)
+{
+ struct img_ir_priv *priv = dev->priv;
+ struct img_ir_priv_hw *hw = &priv->hw;
+ struct rc_dev *rdev = hw->rdev;
+ struct img_ir_decoder **decp;
+ u64 wakeup_protocols;
+
+ if (!*ir_type) {
+ /* disable all protocols */
+ img_ir_set_decoder(priv, NULL, 0);
+ goto success;
+ }
+ for (decp = img_ir_decoders; *decp; ++decp) {
+ const struct img_ir_decoder *dec = *decp;
+ if (!img_ir_decoder_compatible(priv, dec))
+ continue;
+ if (*ir_type & dec->type) {
+ *ir_type &= dec->type;
+ img_ir_set_decoder(priv, dec, *ir_type);
+ goto success;
+ }
+ }
+ return -EINVAL;
+
+success:
+ /*
+ * Only allow matching wakeup protocols for now, and only if filtering
+ * is supported.
+ */
+ wakeup_protocols = *ir_type;
+ if (!hw->decoder || !hw->decoder->filter)
+ wakeup_protocols = 0;
+ rc_set_allowed_wakeup_protocols(rdev, wakeup_protocols);
+ rc_set_enabled_wakeup_protocols(rdev, wakeup_protocols);
+ return 0;
+}
+
+/* Changes ir-core protocol device attribute */
+static void img_ir_set_protocol(struct img_ir_priv *priv, u64 proto)
+{
+ struct rc_dev *rdev = priv->hw.rdev;
+
+ spin_lock_irq(&rdev->rc_map.lock);
+ rdev->rc_map.rc_type = __ffs64(proto);
+ spin_unlock_irq(&rdev->rc_map.lock);
+
+ mutex_lock(&rdev->lock);
+ rc_set_enabled_protocols(rdev, proto);
+ rc_set_allowed_wakeup_protocols(rdev, proto);
+ rc_set_enabled_wakeup_protocols(rdev, proto);
+ mutex_unlock(&rdev->lock);
+}
+
+/* Set up IR decoders */
+static void img_ir_init_decoders(void)
+{
+ struct img_ir_decoder **decp;
+
+ spin_lock(&img_ir_decoders_lock);
+ if (!img_ir_decoders_preprocessed) {
+ for (decp = img_ir_decoders; *decp; ++decp)
+ img_ir_decoder_preprocess(*decp);
+ img_ir_decoders_preprocessed = true;
+ }
+ spin_unlock(&img_ir_decoders_lock);
+}
+
+#ifdef CONFIG_PM_SLEEP
+/**
+ * img_ir_enable_wake() - Switch to wake mode.
+ * @priv: IR private data.
+ *
+ * Returns: non-zero if the IR can wake the system.
+ */
+static int img_ir_enable_wake(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ int ret = 0;
+
+ spin_lock_irq(&priv->lock);
+ if (hw->flags & IMG_IR_F_WAKE) {
+ /* interrupt only on a match */
+ hw->suspend_irqen = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, IMG_IR_IRQ_DATA_MATCH);
+ img_ir_write_filter(priv, &hw->filters[RC_FILTER_WAKEUP]);
+ img_ir_write_timings(priv, &hw->reg_timings.timings,
+ RC_FILTER_WAKEUP);
+ hw->mode = IMG_IR_M_WAKE;
+ ret = 1;
+ }
+ spin_unlock_irq(&priv->lock);
+ return ret;
+}
+
+/**
+ * img_ir_disable_wake() - Switch out of wake mode.
+ * @priv: IR private data
+ *
+ * Returns: 1 if the hardware should be allowed to wake from a sleep state.
+ * 0 otherwise.
+ */
+static int img_ir_disable_wake(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ int ret = 0;
+
+ spin_lock_irq(&priv->lock);
+ if (hw->flags & IMG_IR_F_WAKE) {
+ /* restore normal filtering */
+ if (hw->flags & IMG_IR_F_FILTER) {
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+ (hw->suspend_irqen & IMG_IR_IRQ_EDGE) |
+ IMG_IR_IRQ_DATA_MATCH);
+ img_ir_write_filter(priv,
+ &hw->filters[RC_FILTER_NORMAL]);
+ } else {
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+ (hw->suspend_irqen & IMG_IR_IRQ_EDGE) |
+ IMG_IR_IRQ_DATA_VALID |
+ IMG_IR_IRQ_DATA2_VALID);
+ img_ir_write_filter(priv, NULL);
+ }
+ img_ir_write_timings(priv, &hw->reg_timings.timings,
+ RC_FILTER_NORMAL);
+ hw->mode = IMG_IR_M_NORMAL;
+ ret = 1;
+ }
+ spin_unlock_irq(&priv->lock);
+ return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+/* lock must be held */
+static void img_ir_begin_repeat(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ if (hw->mode == IMG_IR_M_NORMAL) {
+ /* switch to repeat timings */
+ img_ir_write(priv, IMG_IR_CONTROL, 0);
+ hw->mode = IMG_IR_M_REPEATING;
+ img_ir_write_timings(priv, &hw->reg_timings.rtimings,
+ RC_FILTER_NORMAL);
+ img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
+ }
+}
+
+/* lock must be held */
+static void img_ir_end_repeat(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ if (hw->mode == IMG_IR_M_REPEATING) {
+ /* switch to normal timings */
+ img_ir_write(priv, IMG_IR_CONTROL, 0);
+ hw->mode = IMG_IR_M_NORMAL;
+ img_ir_write_timings(priv, &hw->reg_timings.timings,
+ RC_FILTER_NORMAL);
+ img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
+ }
+}
+
+/* lock must be held */
+static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ const struct img_ir_decoder *dec = hw->decoder;
+ int ret = IMG_IR_SCANCODE;
+ int scancode;
+ if (dec->scancode)
+ ret = dec->scancode(len, raw, &scancode, hw->enabled_protocols);
+ else if (len >= 32)
+ scancode = (u32)raw;
+ else if (len < 32)
+ scancode = (u32)raw & ((1 << len)-1);
+ dev_dbg(priv->dev, "data (%u bits) = %#llx\n",
+ len, (unsigned long long)raw);
+ if (ret == IMG_IR_SCANCODE) {
+ dev_dbg(priv->dev, "decoded scan code %#x\n", scancode);
+ rc_keydown(hw->rdev, scancode, 0);
+ img_ir_end_repeat(priv);
+ } else if (ret == IMG_IR_REPEATCODE) {
+ if (hw->mode == IMG_IR_M_REPEATING) {
+ dev_dbg(priv->dev, "decoded repeat code\n");
+ rc_repeat(hw->rdev);
+ } else {
+ dev_dbg(priv->dev, "decoded unexpected repeat code, ignoring\n");
+ }
+ } else {
+ dev_dbg(priv->dev, "decode failed (%d)\n", ret);
+ return;
+ }
+
+
+ if (dec->repeat) {
+ unsigned long interval;
+
+ img_ir_begin_repeat(priv);
+
+ /* update timer, but allowing for 1/8th tolerance */
+ interval = dec->repeat + (dec->repeat >> 3);
+ mod_timer(&hw->end_timer,
+ jiffies + msecs_to_jiffies(interval));
+ }
+}
+
+/* timer function to end waiting for repeat. */
+static void img_ir_end_timer(unsigned long arg)
+{
+ struct img_ir_priv *priv = (struct img_ir_priv *)arg;
+
+ spin_lock_irq(&priv->lock);
+ img_ir_end_repeat(priv);
+ spin_unlock_irq(&priv->lock);
+}
+
+#ifdef CONFIG_COMMON_CLK
+static void img_ir_change_frequency(struct img_ir_priv *priv,
+ struct clk_notifier_data *change)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+
+ dev_dbg(priv->dev, "clk changed %lu HZ -> %lu HZ\n",
+ change->old_rate, change->new_rate);
+
+ spin_lock_irq(&priv->lock);
+ if (hw->clk_hz == change->new_rate)
+ goto unlock;
+ hw->clk_hz = change->new_rate;
+ /* refresh current timings */
+ if (hw->decoder) {
+ img_ir_decoder_convert(hw->decoder, &hw->reg_timings,
+ hw->clk_hz);
+ switch (hw->mode) {
+ case IMG_IR_M_NORMAL:
+ img_ir_write_timings(priv, &hw->reg_timings.timings,
+ RC_FILTER_NORMAL);
+ break;
+ case IMG_IR_M_REPEATING:
+ img_ir_write_timings(priv, &hw->reg_timings.rtimings,
+ RC_FILTER_NORMAL);
+ break;
+#ifdef CONFIG_PM_SLEEP
+ case IMG_IR_M_WAKE:
+ img_ir_write_timings(priv, &hw->reg_timings.timings,
+ RC_FILTER_WAKEUP);
+ break;
+#endif
+ }
+ }
+unlock:
+ spin_unlock_irq(&priv->lock);
+}
+
+static int img_ir_clk_notify(struct notifier_block *self, unsigned long action,
+ void *data)
+{
+ struct img_ir_priv *priv = container_of(self, struct img_ir_priv,
+ hw.clk_nb);
+ switch (action) {
+ case POST_RATE_CHANGE:
+ img_ir_change_frequency(priv, data);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+#endif /* CONFIG_COMMON_CLK */
+
+/* called with priv->lock held */
+void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ u32 ir_status, len, lw, up;
+ unsigned int ct;
+
+ /* use the current decoder */
+ if (!hw->decoder)
+ return;
+
+ ir_status = img_ir_read(priv, IMG_IR_STATUS);
+ if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)))
+ return;
+ ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2);
+ img_ir_write(priv, IMG_IR_STATUS, ir_status);
+
+ len = (ir_status & IMG_IR_RXDLEN) >> IMG_IR_RXDLEN_SHIFT;
+ /* some versions report wrong length for certain code types */
+ ct = hw->decoder->control.code_type;
+ if (hw->ct_quirks[ct] & IMG_IR_QUIRK_CODE_LEN_INCR)
+ ++len;
+
+ lw = img_ir_read(priv, IMG_IR_DATA_LW);
+ up = img_ir_read(priv, IMG_IR_DATA_UP);
+ img_ir_handle_data(priv, len, (u64)up << 32 | lw);
+}
+
+void img_ir_setup_hw(struct img_ir_priv *priv)
+{
+ struct img_ir_decoder **decp;
+
+ if (!priv->hw.rdev)
+ return;
+
+ /* Use the first available decoder (or disable stuff if NULL) */
+ for (decp = img_ir_decoders; *decp; ++decp) {
+ const struct img_ir_decoder *dec = *decp;
+ if (img_ir_decoder_compatible(priv, dec)) {
+ img_ir_set_protocol(priv, dec->type);
+ img_ir_set_decoder(priv, dec, 0);
+ return;
+ }
+ }
+ img_ir_set_decoder(priv, NULL, 0);
+}
+
+/**
+ * img_ir_probe_hw_caps() - Probe capabilities of the hardware.
+ * @priv: IR private data.
+ */
+static void img_ir_probe_hw_caps(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ /*
+ * When a version of the block becomes available without these quirks,
+ * they'll have to depend on the core revision.
+ */
+ hw->ct_quirks[IMG_IR_CODETYPE_PULSELEN]
+ |= IMG_IR_QUIRK_CODE_LEN_INCR;
+ hw->ct_quirks[IMG_IR_CODETYPE_BIPHASE]
+ |= IMG_IR_QUIRK_CODE_BROKEN;
+ hw->ct_quirks[IMG_IR_CODETYPE_2BITPULSEPOS]
+ |= IMG_IR_QUIRK_CODE_BROKEN;
+}
+
+int img_ir_probe_hw(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ struct rc_dev *rdev;
+ int error;
+
+ /* Ensure hardware decoders have been preprocessed */
+ img_ir_init_decoders();
+
+ /* Probe hardware capabilities */
+ img_ir_probe_hw_caps(priv);
+
+ /* Set up the end timer */
+ setup_timer(&hw->end_timer, img_ir_end_timer, (unsigned long)priv);
+
+ /* Register a clock notifier */
+ if (!IS_ERR(priv->clk)) {
+ hw->clk_hz = clk_get_rate(priv->clk);
+#ifdef CONFIG_COMMON_CLK
+ hw->clk_nb.notifier_call = img_ir_clk_notify;
+ error = clk_notifier_register(priv->clk, &hw->clk_nb);
+ if (error)
+ dev_warn(priv->dev,
+ "failed to register clock notifier\n");
+#endif
+ } else {
+ hw->clk_hz = 32768;
+ }
+
+ /* Allocate hardware decoder */
+ hw->rdev = rdev = rc_allocate_device();
+ if (!rdev) {
+ dev_err(priv->dev, "cannot allocate input device\n");
+ error = -ENOMEM;
+ goto err_alloc_rc;
+ }
+ rdev->priv = priv;
+ rdev->map_name = RC_MAP_EMPTY;
+ rc_set_allowed_protocols(rdev, img_ir_allowed_protos(priv));
+ rdev->input_name = "IMG Infrared Decoder";
+ rdev->s_filter = img_ir_set_filter;
+
+ /* Register hardware decoder */
+ error = rc_register_device(rdev);
+ if (error) {
+ dev_err(priv->dev, "failed to register IR input device\n");
+ goto err_register_rc;
+ }
+
+ /*
+ * Set this after rc_register_device as no protocols have been
+ * registered yet.
+ */
+ rdev->change_protocol = img_ir_change_protocol;
+
+ device_init_wakeup(priv->dev, 1);
+
+ return 0;
+
+err_register_rc:
+ img_ir_set_decoder(priv, NULL, 0);
+ hw->rdev = NULL;
+ rc_free_device(rdev);
+err_alloc_rc:
+#ifdef CONFIG_COMMON_CLK
+ if (!IS_ERR(priv->clk))
+ clk_notifier_unregister(priv->clk, &hw->clk_nb);
+#endif
+ return error;
+}
+
+void img_ir_remove_hw(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_hw *hw = &priv->hw;
+ struct rc_dev *rdev = hw->rdev;
+ if (!rdev)
+ return;
+ img_ir_set_decoder(priv, NULL, 0);
+ hw->rdev = NULL;
+ rc_unregister_device(rdev);
+#ifdef CONFIG_COMMON_CLK
+ if (!IS_ERR(priv->clk))
+ clk_notifier_unregister(priv->clk, &hw->clk_nb);
+#endif
+}
+
+#ifdef CONFIG_PM_SLEEP
+int img_ir_suspend(struct device *dev)
+{
+ struct img_ir_priv *priv = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev) && img_ir_enable_wake(priv))
+ enable_irq_wake(priv->irq);
+ return 0;
+}
+
+int img_ir_resume(struct device *dev)
+{
+ struct img_ir_priv *priv = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev) && img_ir_disable_wake(priv))
+ disable_irq_wake(priv->irq);
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h
new file mode 100644
index 000000000000..6c9a94a81190
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-hw.h
@@ -0,0 +1,269 @@
+/*
+ * ImgTec IR Hardware Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ */
+
+#ifndef _IMG_IR_HW_H_
+#define _IMG_IR_HW_H_
+
+#include <linux/kernel.h>
+#include <media/rc-core.h>
+
+/* constants */
+
+#define IMG_IR_CODETYPE_PULSELEN 0x0 /* Sony */
+#define IMG_IR_CODETYPE_PULSEDIST 0x1 /* NEC, Toshiba, Micom, Sharp */
+#define IMG_IR_CODETYPE_BIPHASE 0x2 /* RC-5/6 */
+#define IMG_IR_CODETYPE_2BITPULSEPOS 0x3 /* RC-MM */
+
+
+/* Timing information */
+
+/**
+ * struct img_ir_control - Decoder control settings
+ * @decoden: Primary decoder enable
+ * @code_type: Decode type (see IMG_IR_CODETYPE_*)
+ * @hdrtog: Detect header toggle symbol after leader symbol
+ * @ldrdec: Don't discard leader if maximum width reached
+ * @decodinpol: Decoder input polarity (1=active high)
+ * @bitorien: Bit orientation (1=MSB first)
+ * @d1validsel: Decoder 2 takes over if it detects valid data
+ * @bitinv: Bit inversion switch (1=don't invert)
+ * @decodend2: Secondary decoder enable (no leader symbol)
+ * @bitoriend2: Bit orientation (1=MSB first)
+ * @bitinvd2: Secondary decoder bit inversion switch (1=don't invert)
+ */
+struct img_ir_control {
+ unsigned decoden:1;
+ unsigned code_type:2;
+ unsigned hdrtog:1;
+ unsigned ldrdec:1;
+ unsigned decodinpol:1;
+ unsigned bitorien:1;
+ unsigned d1validsel:1;
+ unsigned bitinv:1;
+ unsigned decodend2:1;
+ unsigned bitoriend2:1;
+ unsigned bitinvd2:1;
+};
+
+/**
+ * struct img_ir_timing_range - range of timing values
+ * @min: Minimum timing value
+ * @max: Maximum timing value (if < @min, this will be set to @min during
+ * preprocessing step, so it is normally not explicitly initialised
+ * and is taken care of by the tolerance)
+ */
+struct img_ir_timing_range {
+ u16 min;
+ u16 max;
+};
+
+/**
+ * struct img_ir_symbol_timing - timing data for a symbol
+ * @pulse: Timing range for the length of the pulse in this symbol
+ * @space: Timing range for the length of the space in this symbol
+ */
+struct img_ir_symbol_timing {
+ struct img_ir_timing_range pulse;
+ struct img_ir_timing_range space;
+};
+
+/**
+ * struct img_ir_free_timing - timing data for free time symbol
+ * @minlen: Minimum number of bits of data
+ * @maxlen: Maximum number of bits of data
+ * @ft_min: Minimum free time after message
+ */
+struct img_ir_free_timing {
+ /* measured in bits */
+ u8 minlen;
+ u8 maxlen;
+ u16 ft_min;
+};
+
+/**
+ * struct img_ir_timings - Timing values.
+ * @ldr: Leader symbol timing data
+ * @s00: Zero symbol timing data for primary decoder
+ * @s01: One symbol timing data for primary decoder
+ * @s10: Zero symbol timing data for secondary (no leader symbol) decoder
+ * @s11: One symbol timing data for secondary (no leader symbol) decoder
+ * @ft: Free time symbol timing data
+ */
+struct img_ir_timings {
+ struct img_ir_symbol_timing ldr, s00, s01, s10, s11;
+ struct img_ir_free_timing ft;
+};
+
+/**
+ * struct img_ir_filter - Filter IR events.
+ * @data: Data to match.
+ * @mask: Mask of bits to compare.
+ * @minlen: Additional minimum number of bits.
+ * @maxlen: Additional maximum number of bits.
+ */
+struct img_ir_filter {
+ u64 data;
+ u64 mask;
+ u8 minlen;
+ u8 maxlen;
+};
+
+/**
+ * struct img_ir_timing_regvals - Calculated timing register values.
+ * @ldr: Leader symbol timing register value
+ * @s00: Zero symbol timing register value for primary decoder
+ * @s01: One symbol timing register value for primary decoder
+ * @s10: Zero symbol timing register value for secondary decoder
+ * @s11: One symbol timing register value for secondary decoder
+ * @ft: Free time symbol timing register value
+ */
+struct img_ir_timing_regvals {
+ u32 ldr, s00, s01, s10, s11, ft;
+};
+
+#define IMG_IR_SCANCODE 0 /* new scancode */
+#define IMG_IR_REPEATCODE 1 /* repeat the previous code */
+
+/**
+ * struct img_ir_decoder - Decoder settings for an IR protocol.
+ * @type: Protocol types bitmap.
+ * @tolerance: Timing tolerance as a percentage (default 10%).
+ * @unit: Unit of timings in nanoseconds (default 1 us).
+ * @timings: Primary timings
+ * @rtimings: Additional override timings while waiting for repeats.
+ * @repeat: Maximum repeat interval (always in milliseconds).
+ * @control: Control flags.
+ *
+ * @scancode: Pointer to function to convert the IR data into a scancode (it
+ * must be safe to execute in interrupt context).
+ * Returns IMG_IR_SCANCODE to emit new scancode.
+ * Returns IMG_IR_REPEATCODE to repeat previous code.
+ * Returns -errno (e.g. -EINVAL) on error.
+ * @filter: Pointer to function to convert scancode filter to raw hardware
+ * filter. The minlen and maxlen fields will have been initialised
+ * to the maximum range.
+ */
+struct img_ir_decoder {
+ /* core description */
+ u64 type;
+ unsigned int tolerance;
+ unsigned int unit;
+ struct img_ir_timings timings;
+ struct img_ir_timings rtimings;
+ unsigned int repeat;
+ struct img_ir_control control;
+
+ /* scancode logic */
+ int (*scancode)(int len, u64 raw, int *scancode, u64 protocols);
+ int (*filter)(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols);
+};
+
+/**
+ * struct img_ir_reg_timings - Reg values for decoder timings at clock rate.
+ * @ctrl: Processed control register value.
+ * @timings: Processed primary timings.
+ * @rtimings: Processed repeat timings.
+ */
+struct img_ir_reg_timings {
+ u32 ctrl;
+ struct img_ir_timing_regvals timings;
+ struct img_ir_timing_regvals rtimings;
+};
+
+int img_ir_register_decoder(struct img_ir_decoder *dec);
+void img_ir_unregister_decoder(struct img_ir_decoder *dec);
+
+struct img_ir_priv;
+
+#ifdef CONFIG_IR_IMG_HW
+
+enum img_ir_mode {
+ IMG_IR_M_NORMAL,
+ IMG_IR_M_REPEATING,
+#ifdef CONFIG_PM_SLEEP
+ IMG_IR_M_WAKE,
+#endif
+};
+
+/**
+ * struct img_ir_priv_hw - Private driver data for hardware decoder.
+ * @ct_quirks: Quirk bits for each code type.
+ * @rdev: Remote control device
+ * @clk_nb: Notifier block for clock notify events.
+ * @end_timer: Timer until repeat timeout.
+ * @decoder: Current decoder settings.
+ * @enabled_protocols: Currently enabled protocols.
+ * @clk_hz: Current core clock rate in Hz.
+ * @reg_timings: Timing reg values for decoder at clock rate.
+ * @flags: IMG_IR_F_*.
+ * @filters: HW filters (derived from scancode filters).
+ * @mode: Current decode mode.
+ * @suspend_irqen: Saved IRQ enable mask over suspend.
+ */
+struct img_ir_priv_hw {
+ unsigned int ct_quirks[4];
+ struct rc_dev *rdev;
+ struct notifier_block clk_nb;
+ struct timer_list end_timer;
+ const struct img_ir_decoder *decoder;
+ u64 enabled_protocols;
+ unsigned long clk_hz;
+ struct img_ir_reg_timings reg_timings;
+ unsigned int flags;
+ struct img_ir_filter filters[RC_FILTER_MAX];
+
+ enum img_ir_mode mode;
+ u32 suspend_irqen;
+};
+
+static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw)
+{
+ return hw->rdev;
+};
+
+void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status);
+void img_ir_setup_hw(struct img_ir_priv *priv);
+int img_ir_probe_hw(struct img_ir_priv *priv);
+void img_ir_remove_hw(struct img_ir_priv *priv);
+
+#ifdef CONFIG_PM_SLEEP
+int img_ir_suspend(struct device *dev);
+int img_ir_resume(struct device *dev);
+#else
+#define img_ir_suspend NULL
+#define img_ir_resume NULL
+#endif
+
+#else
+
+struct img_ir_priv_hw {
+};
+
+static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw)
+{
+ return false;
+};
+static inline void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status)
+{
+}
+static inline void img_ir_setup_hw(struct img_ir_priv *priv)
+{
+}
+static inline int img_ir_probe_hw(struct img_ir_priv *priv)
+{
+ return -ENODEV;
+}
+static inline void img_ir_remove_hw(struct img_ir_priv *priv)
+{
+}
+
+#define img_ir_suspend NULL
+#define img_ir_resume NULL
+
+#endif /* CONFIG_IR_IMG_HW */
+
+#endif /* _IMG_IR_HW_H_ */
diff --git a/drivers/media/rc/img-ir/img-ir-jvc.c b/drivers/media/rc/img-ir/img-ir-jvc.c
new file mode 100644
index 000000000000..10209d200efb
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-jvc.c
@@ -0,0 +1,81 @@
+/*
+ * ImgTec IR Decoder setup for JVC protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert JVC data to a scancode */
+static int img_ir_jvc_scancode(int len, u64 raw, int *scancode, u64 protocols)
+{
+ unsigned int cust, data;
+
+ if (len != 16)
+ return -EINVAL;
+
+ cust = (raw >> 0) & 0xff;
+ data = (raw >> 8) & 0xff;
+
+ *scancode = cust << 8 | data;
+ return IMG_IR_SCANCODE;
+}
+
+/* Convert JVC scancode to JVC data filter */
+static int img_ir_jvc_filter(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols)
+{
+ unsigned int cust, data;
+ unsigned int cust_m, data_m;
+
+ cust = (in->data >> 8) & 0xff;
+ cust_m = (in->mask >> 8) & 0xff;
+ data = (in->data >> 0) & 0xff;
+ data_m = (in->mask >> 0) & 0xff;
+
+ out->data = cust | data << 8;
+ out->mask = cust_m | data_m << 8;
+
+ return 0;
+}
+
+/*
+ * JVC decoder
+ * See also http://www.sbprojects.com/knowledge/ir/jvc.php
+ * http://support.jvc.com/consumer/support/documents/RemoteCodes.pdf
+ */
+struct img_ir_decoder img_ir_jvc = {
+ .type = RC_BIT_JVC,
+ .control = {
+ .decoden = 1,
+ .code_type = IMG_IR_CODETYPE_PULSEDIST,
+ },
+ /* main timings */
+ .unit = 527500, /* 527.5 us */
+ .timings = {
+ /* leader symbol */
+ .ldr = {
+ .pulse = { 16 /* 8.44 ms */ },
+ .space = { 8 /* 4.22 ms */ },
+ },
+ /* 0 symbol */
+ .s00 = {
+ .pulse = { 1 /* 527.5 us +-60 us */ },
+ .space = { 1 /* 527.5 us */ },
+ },
+ /* 1 symbol */
+ .s01 = {
+ .pulse = { 1 /* 527.5 us +-60 us */ },
+ .space = { 3 /* 1.5825 ms +-40 us */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 16,
+ .maxlen = 16,
+ .ft_min = 10, /* 5.275 ms */
+ },
+ },
+ /* scancode logic */
+ .scancode = img_ir_jvc_scancode,
+ .filter = img_ir_jvc_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c
new file mode 100644
index 000000000000..e7a731bc3a9b
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-nec.c
@@ -0,0 +1,148 @@
+/*
+ * ImgTec IR Decoder setup for NEC protocol.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert NEC data to a scancode */
+static int img_ir_nec_scancode(int len, u64 raw, int *scancode, u64 protocols)
+{
+ unsigned int addr, addr_inv, data, data_inv;
+ /* a repeat code has no data */
+ if (!len)
+ return IMG_IR_REPEATCODE;
+ if (len != 32)
+ return -EINVAL;
+ /* raw encoding: ddDDaaAA */
+ addr = (raw >> 0) & 0xff;
+ addr_inv = (raw >> 8) & 0xff;
+ data = (raw >> 16) & 0xff;
+ data_inv = (raw >> 24) & 0xff;
+ if ((data_inv ^ data) != 0xff) {
+ /* 32-bit NEC (used by Apple and TiVo remotes) */
+ /* scan encoding: aaAAddDD */
+ *scancode = addr_inv << 24 |
+ addr << 16 |
+ data_inv << 8 |
+ data;
+ } else if ((addr_inv ^ addr) != 0xff) {
+ /* Extended NEC */
+ /* scan encoding: AAaaDD */
+ *scancode = addr << 16 |
+ addr_inv << 8 |
+ data;
+ } else {
+ /* Normal NEC */
+ /* scan encoding: AADD */
+ *scancode = addr << 8 |
+ data;
+ }
+ return IMG_IR_SCANCODE;
+}
+
+/* Convert NEC scancode to NEC data filter */
+static int img_ir_nec_filter(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols)
+{
+ unsigned int addr, addr_inv, data, data_inv;
+ unsigned int addr_m, addr_inv_m, data_m, data_inv_m;
+
+ data = in->data & 0xff;
+ data_m = in->mask & 0xff;
+
+ if ((in->data | in->mask) & 0xff000000) {
+ /* 32-bit NEC (used by Apple and TiVo remotes) */
+ /* scan encoding: aaAAddDD */
+ addr_inv = (in->data >> 24) & 0xff;
+ addr_inv_m = (in->mask >> 24) & 0xff;
+ addr = (in->data >> 16) & 0xff;
+ addr_m = (in->mask >> 16) & 0xff;
+ data_inv = (in->data >> 8) & 0xff;
+ data_inv_m = (in->mask >> 8) & 0xff;
+ } else if ((in->data | in->mask) & 0x00ff0000) {
+ /* Extended NEC */
+ /* scan encoding AAaaDD */
+ addr = (in->data >> 16) & 0xff;
+ addr_m = (in->mask >> 16) & 0xff;
+ addr_inv = (in->data >> 8) & 0xff;
+ addr_inv_m = (in->mask >> 8) & 0xff;
+ data_inv = data ^ 0xff;
+ data_inv_m = data_m;
+ } else {
+ /* Normal NEC */
+ /* scan encoding: AADD */
+ addr = (in->data >> 8) & 0xff;
+ addr_m = (in->mask >> 8) & 0xff;
+ addr_inv = addr ^ 0xff;
+ addr_inv_m = addr_m;
+ data_inv = data ^ 0xff;
+ data_inv_m = data_m;
+ }
+
+ /* raw encoding: ddDDaaAA */
+ out->data = data_inv << 24 |
+ data << 16 |
+ addr_inv << 8 |
+ addr;
+ out->mask = data_inv_m << 24 |
+ data_m << 16 |
+ addr_inv_m << 8 |
+ addr_m;
+ return 0;
+}
+
+/*
+ * NEC decoder
+ * See also http://www.sbprojects.com/knowledge/ir/nec.php
+ * http://wiki.altium.com/display/ADOH/NEC+Infrared+Transmission+Protocol
+ */
+struct img_ir_decoder img_ir_nec = {
+ .type = RC_BIT_NEC,
+ .control = {
+ .decoden = 1,
+ .code_type = IMG_IR_CODETYPE_PULSEDIST,
+ },
+ /* main timings */
+ .unit = 562500, /* 562.5 us */
+ .timings = {
+ /* leader symbol */
+ .ldr = {
+ .pulse = { 16 /* 9ms */ },
+ .space = { 8 /* 4.5ms */ },
+ },
+ /* 0 symbol */
+ .s00 = {
+ .pulse = { 1 /* 562.5 us */ },
+ .space = { 1 /* 562.5 us */ },
+ },
+ /* 1 symbol */
+ .s01 = {
+ .pulse = { 1 /* 562.5 us */ },
+ .space = { 3 /* 1687.5 us */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 32,
+ .maxlen = 32,
+ .ft_min = 10, /* 5.625 ms */
+ },
+ },
+ /* repeat codes */
+ .repeat = 108, /* 108 ms */
+ .rtimings = {
+ /* leader symbol */
+ .ldr = {
+ .space = { 4 /* 2.25 ms */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 0, /* repeat code has no data */
+ .maxlen = 0,
+ },
+ },
+ /* scancode logic */
+ .scancode = img_ir_nec_scancode,
+ .filter = img_ir_nec_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c
new file mode 100644
index 000000000000..cfb01d9e571a
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-raw.c
@@ -0,0 +1,151 @@
+/*
+ * ImgTec IR Raw Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * This ties into the input subsystem using the RC-core in raw mode. Raw IR
+ * signal edges are reported and decoded by generic software decoders.
+ */
+
+#include <linux/spinlock.h>
+#include <media/rc-core.h>
+#include "img-ir.h"
+
+#define ECHO_TIMEOUT_MS 150 /* ms between echos */
+
+/* must be called with priv->lock held */
+static void img_ir_refresh_raw(struct img_ir_priv *priv, u32 irq_status)
+{
+ struct img_ir_priv_raw *raw = &priv->raw;
+ struct rc_dev *rc_dev = priv->raw.rdev;
+ int multiple;
+ u32 ir_status;
+
+ /* find whether both rise and fall was detected */
+ multiple = ((irq_status & IMG_IR_IRQ_EDGE) == IMG_IR_IRQ_EDGE);
+ /*
+ * If so, we need to see if the level has actually changed.
+ * If it's just noise that we didn't have time to process,
+ * there's no point reporting it.
+ */
+ ir_status = img_ir_read(priv, IMG_IR_STATUS) & IMG_IR_IRRXD;
+ if (multiple && ir_status == raw->last_status)
+ return;
+ raw->last_status = ir_status;
+
+ /* report the edge to the IR raw decoders */
+ if (ir_status) /* low */
+ ir_raw_event_store_edge(rc_dev, IR_SPACE);
+ else /* high */
+ ir_raw_event_store_edge(rc_dev, IR_PULSE);
+ ir_raw_event_handle(rc_dev);
+}
+
+/* called with priv->lock held */
+void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status)
+{
+ struct img_ir_priv_raw *raw = &priv->raw;
+
+ /* check not removing */
+ if (!raw->rdev)
+ return;
+
+ img_ir_refresh_raw(priv, irq_status);
+
+ /* start / push back the echo timer */
+ mod_timer(&raw->timer, jiffies + msecs_to_jiffies(ECHO_TIMEOUT_MS));
+}
+
+/*
+ * Echo timer callback function.
+ * The raw decoders expect to get a final sample even if there are no edges, in
+ * order to be assured of the final space. If there are no edges for a certain
+ * time we use this timer to emit a final sample to satisfy them.
+ */
+static void img_ir_echo_timer(unsigned long arg)
+{
+ struct img_ir_priv *priv = (struct img_ir_priv *)arg;
+
+ spin_lock_irq(&priv->lock);
+
+ /* check not removing */
+ if (priv->raw.rdev)
+ /*
+ * It's safe to pass irq_status=0 since it's only used to check
+ * for double edges.
+ */
+ img_ir_refresh_raw(priv, 0);
+
+ spin_unlock_irq(&priv->lock);
+}
+
+void img_ir_setup_raw(struct img_ir_priv *priv)
+{
+ u32 irq_en;
+
+ if (!priv->raw.rdev)
+ return;
+
+ /* clear and enable edge interrupts */
+ spin_lock_irq(&priv->lock);
+ irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+ irq_en |= IMG_IR_IRQ_EDGE;
+ img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
+ spin_unlock_irq(&priv->lock);
+}
+
+int img_ir_probe_raw(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_raw *raw = &priv->raw;
+ struct rc_dev *rdev;
+ int error;
+
+ /* Set up the echo timer */
+ setup_timer(&raw->timer, img_ir_echo_timer, (unsigned long)priv);
+
+ /* Allocate raw decoder */
+ raw->rdev = rdev = rc_allocate_device();
+ if (!rdev) {
+ dev_err(priv->dev, "cannot allocate raw input device\n");
+ return -ENOMEM;
+ }
+ rdev->priv = priv;
+ rdev->map_name = RC_MAP_EMPTY;
+ rdev->input_name = "IMG Infrared Decoder Raw";
+ rdev->driver_type = RC_DRIVER_IR_RAW;
+
+ /* Register raw decoder */
+ error = rc_register_device(rdev);
+ if (error) {
+ dev_err(priv->dev, "failed to register raw IR input device\n");
+ rc_free_device(rdev);
+ raw->rdev = NULL;
+ return error;
+ }
+
+ return 0;
+}
+
+void img_ir_remove_raw(struct img_ir_priv *priv)
+{
+ struct img_ir_priv_raw *raw = &priv->raw;
+ struct rc_dev *rdev = raw->rdev;
+ u32 irq_en;
+
+ if (!rdev)
+ return;
+
+ /* switch off and disable raw (edge) interrupts */
+ spin_lock_irq(&priv->lock);
+ raw->rdev = NULL;
+ irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+ irq_en &= ~IMG_IR_IRQ_EDGE;
+ img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
+ img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
+ spin_unlock_irq(&priv->lock);
+
+ rc_unregister_device(rdev);
+
+ del_timer_sync(&raw->timer);
+}
diff --git a/drivers/media/rc/img-ir/img-ir-raw.h b/drivers/media/rc/img-ir/img-ir-raw.h
new file mode 100644
index 000000000000..9802ffd51b9a
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-raw.h
@@ -0,0 +1,60 @@
+/*
+ * ImgTec IR Raw Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ */
+
+#ifndef _IMG_IR_RAW_H_
+#define _IMG_IR_RAW_H_
+
+struct img_ir_priv;
+
+#ifdef CONFIG_IR_IMG_RAW
+
+/**
+ * struct img_ir_priv_raw - Private driver data for raw decoder.
+ * @rdev: Raw remote control device
+ * @timer: Timer to echo samples to keep soft decoders happy.
+ * @last_status: Last raw status bits.
+ */
+struct img_ir_priv_raw {
+ struct rc_dev *rdev;
+ struct timer_list timer;
+ u32 last_status;
+};
+
+static inline bool img_ir_raw_enabled(struct img_ir_priv_raw *raw)
+{
+ return raw->rdev;
+};
+
+void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status);
+void img_ir_setup_raw(struct img_ir_priv *priv);
+int img_ir_probe_raw(struct img_ir_priv *priv);
+void img_ir_remove_raw(struct img_ir_priv *priv);
+
+#else
+
+struct img_ir_priv_raw {
+};
+static inline bool img_ir_raw_enabled(struct img_ir_priv_raw *raw)
+{
+ return false;
+};
+static inline void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status)
+{
+}
+static inline void img_ir_setup_raw(struct img_ir_priv *priv)
+{
+}
+static inline int img_ir_probe_raw(struct img_ir_priv *priv)
+{
+ return -ENODEV;
+}
+static inline void img_ir_remove_raw(struct img_ir_priv *priv)
+{
+}
+
+#endif /* CONFIG_IR_IMG_RAW */
+
+#endif /* _IMG_IR_RAW_H_ */
diff --git a/drivers/media/rc/img-ir/img-ir-sanyo.c b/drivers/media/rc/img-ir/img-ir-sanyo.c
new file mode 100644
index 000000000000..c2c763e08a41
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-sanyo.c
@@ -0,0 +1,122 @@
+/*
+ * ImgTec IR Decoder setup for Sanyo protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ *
+ * From ir-sanyo-decoder.c:
+ *
+ * This protocol uses the NEC protocol timings. However, data is formatted as:
+ * 13 bits Custom Code
+ * 13 bits NOT(Custom Code)
+ * 8 bits Key data
+ * 8 bits NOT(Key data)
+ *
+ * According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon
+ * Information for this protocol is available at the Sanyo LC7461 datasheet.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert Sanyo data to a scancode */
+static int img_ir_sanyo_scancode(int len, u64 raw, int *scancode, u64 protocols)
+{
+ unsigned int addr, addr_inv, data, data_inv;
+ /* a repeat code has no data */
+ if (!len)
+ return IMG_IR_REPEATCODE;
+ if (len != 42)
+ return -EINVAL;
+ addr = (raw >> 0) & 0x1fff;
+ addr_inv = (raw >> 13) & 0x1fff;
+ data = (raw >> 26) & 0xff;
+ data_inv = (raw >> 34) & 0xff;
+ /* Validate data */
+ if ((data_inv ^ data) != 0xff)
+ return -EINVAL;
+ /* Validate address */
+ if ((addr_inv ^ addr) != 0x1fff)
+ return -EINVAL;
+
+ /* Normal Sanyo */
+ *scancode = addr << 8 | data;
+ return IMG_IR_SCANCODE;
+}
+
+/* Convert Sanyo scancode to Sanyo data filter */
+static int img_ir_sanyo_filter(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols)
+{
+ unsigned int addr, addr_inv, data, data_inv;
+ unsigned int addr_m, data_m;
+
+ data = in->data & 0xff;
+ data_m = in->mask & 0xff;
+ data_inv = data ^ 0xff;
+
+ if (in->data & 0xff700000)
+ return -EINVAL;
+
+ addr = (in->data >> 8) & 0x1fff;
+ addr_m = (in->mask >> 8) & 0x1fff;
+ addr_inv = addr ^ 0x1fff;
+
+ out->data = (u64)data_inv << 34 |
+ (u64)data << 26 |
+ addr_inv << 13 |
+ addr;
+ out->mask = (u64)data_m << 34 |
+ (u64)data_m << 26 |
+ addr_m << 13 |
+ addr_m;
+ return 0;
+}
+
+/* Sanyo decoder */
+struct img_ir_decoder img_ir_sanyo = {
+ .type = RC_BIT_SANYO,
+ .control = {
+ .decoden = 1,
+ .code_type = IMG_IR_CODETYPE_PULSEDIST,
+ },
+ /* main timings */
+ .unit = 562500, /* 562.5 us */
+ .timings = {
+ /* leader symbol */
+ .ldr = {
+ .pulse = { 16 /* 9ms */ },
+ .space = { 8 /* 4.5ms */ },
+ },
+ /* 0 symbol */
+ .s00 = {
+ .pulse = { 1 /* 562.5 us */ },
+ .space = { 1 /* 562.5 us */ },
+ },
+ /* 1 symbol */
+ .s01 = {
+ .pulse = { 1 /* 562.5 us */ },
+ .space = { 3 /* 1687.5 us */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 42,
+ .maxlen = 42,
+ .ft_min = 10, /* 5.625 ms */
+ },
+ },
+ /* repeat codes */
+ .repeat = 108, /* 108 ms */
+ .rtimings = {
+ /* leader symbol */
+ .ldr = {
+ .space = { 4 /* 2.25 ms */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 0, /* repeat code has no data */
+ .maxlen = 0,
+ },
+ },
+ /* scancode logic */
+ .scancode = img_ir_sanyo_scancode,
+ .filter = img_ir_sanyo_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-sharp.c b/drivers/media/rc/img-ir/img-ir-sharp.c
new file mode 100644
index 000000000000..3397cc5a6794
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-sharp.c
@@ -0,0 +1,99 @@
+/*
+ * ImgTec IR Decoder setup for Sharp protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert Sharp data to a scancode */
+static int img_ir_sharp_scancode(int len, u64 raw, int *scancode, u64 protocols)
+{
+ unsigned int addr, cmd, exp, chk;
+
+ if (len != 15)
+ return -EINVAL;
+
+ addr = (raw >> 0) & 0x1f;
+ cmd = (raw >> 5) & 0xff;
+ exp = (raw >> 13) & 0x1;
+ chk = (raw >> 14) & 0x1;
+
+ /* validate data */
+ if (!exp)
+ return -EINVAL;
+ if (chk)
+ /* probably the second half of the message */
+ return -EINVAL;
+
+ *scancode = addr << 8 | cmd;
+ return IMG_IR_SCANCODE;
+}
+
+/* Convert Sharp scancode to Sharp data filter */
+static int img_ir_sharp_filter(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols)
+{
+ unsigned int addr, cmd, exp = 0, chk = 0;
+ unsigned int addr_m, cmd_m, exp_m = 0, chk_m = 0;
+
+ addr = (in->data >> 8) & 0x1f;
+ addr_m = (in->mask >> 8) & 0x1f;
+ cmd = (in->data >> 0) & 0xff;
+ cmd_m = (in->mask >> 0) & 0xff;
+ if (cmd_m) {
+ /* if filtering commands, we can only match the first part */
+ exp = 1;
+ exp_m = 1;
+ chk = 0;
+ chk_m = 1;
+ }
+
+ out->data = addr |
+ cmd << 5 |
+ exp << 13 |
+ chk << 14;
+ out->mask = addr_m |
+ cmd_m << 5 |
+ exp_m << 13 |
+ chk_m << 14;
+
+ return 0;
+}
+
+/*
+ * Sharp decoder
+ * See also http://www.sbprojects.com/knowledge/ir/sharp.php
+ */
+struct img_ir_decoder img_ir_sharp = {
+ .type = RC_BIT_SHARP,
+ .control = {
+ .decoden = 0,
+ .decodend2 = 1,
+ .code_type = IMG_IR_CODETYPE_PULSEDIST,
+ .d1validsel = 1,
+ },
+ /* main timings */
+ .tolerance = 20, /* 20% */
+ .timings = {
+ /* 0 symbol */
+ .s10 = {
+ .pulse = { 320 /* 320 us */ },
+ .space = { 680 /* 1 ms period */ },
+ },
+ /* 1 symbol */
+ .s11 = {
+ .pulse = { 320 /* 320 us */ },
+ .space = { 1680 /* 2 ms period */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 15,
+ .maxlen = 15,
+ .ft_min = 5000, /* 5 ms */
+ },
+ },
+ /* scancode logic */
+ .scancode = img_ir_sharp_scancode,
+ .filter = img_ir_sharp_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-sony.c b/drivers/media/rc/img-ir/img-ir-sony.c
new file mode 100644
index 000000000000..993409a51a71
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-sony.c
@@ -0,0 +1,145 @@
+/*
+ * ImgTec IR Decoder setup for Sony (SIRC) protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert Sony data to a scancode */
+static int img_ir_sony_scancode(int len, u64 raw, int *scancode, u64 protocols)
+{
+ unsigned int dev, subdev, func;
+
+ switch (len) {
+ case 12:
+ if (!(protocols & RC_BIT_SONY12))
+ return -EINVAL;
+ func = raw & 0x7f; /* first 7 bits */
+ raw >>= 7;
+ dev = raw & 0x1f; /* next 5 bits */
+ subdev = 0;
+ break;
+ case 15:
+ if (!(protocols & RC_BIT_SONY15))
+ return -EINVAL;
+ func = raw & 0x7f; /* first 7 bits */
+ raw >>= 7;
+ dev = raw & 0xff; /* next 8 bits */
+ subdev = 0;
+ break;
+ case 20:
+ if (!(protocols & RC_BIT_SONY20))
+ return -EINVAL;
+ func = raw & 0x7f; /* first 7 bits */
+ raw >>= 7;
+ dev = raw & 0x1f; /* next 5 bits */
+ raw >>= 5;
+ subdev = raw & 0xff; /* next 8 bits */
+ break;
+ default:
+ return -EINVAL;
+ }
+ *scancode = dev << 16 | subdev << 8 | func;
+ return IMG_IR_SCANCODE;
+}
+
+/* Convert NEC scancode to NEC data filter */
+static int img_ir_sony_filter(const struct rc_scancode_filter *in,
+ struct img_ir_filter *out, u64 protocols)
+{
+ unsigned int dev, subdev, func;
+ unsigned int dev_m, subdev_m, func_m;
+ unsigned int len = 0;
+
+ dev = (in->data >> 16) & 0xff;
+ dev_m = (in->mask >> 16) & 0xff;
+ subdev = (in->data >> 8) & 0xff;
+ subdev_m = (in->mask >> 8) & 0xff;
+ func = (in->data >> 0) & 0x7f;
+ func_m = (in->mask >> 0) & 0x7f;
+
+ if (subdev & subdev_m) {
+ /* can't encode subdev and higher device bits */
+ if (dev & dev_m & 0xe0)
+ return -EINVAL;
+ /* subdevice (extended) bits only in 20 bit encoding */
+ if (!(protocols & RC_BIT_SONY20))
+ return -EINVAL;
+ len = 20;
+ dev_m &= 0x1f;
+ } else if (dev & dev_m & 0xe0) {
+ /* upper device bits only in 15 bit encoding */
+ if (!(protocols & RC_BIT_SONY15))
+ return -EINVAL;
+ len = 15;
+ subdev_m = 0;
+ } else {
+ /*
+ * The hardware mask cannot distinguish high device bits and low
+ * extended bits, so logically AND those bits of the masks
+ * together.
+ */
+ subdev_m &= (dev_m >> 5) | 0xf8;
+ dev_m &= 0x1f;
+ }
+
+ /* ensure there aren't any bits straying between fields */
+ dev &= dev_m;
+ subdev &= subdev_m;
+
+ /* write the hardware filter */
+ out->data = func |
+ dev << 7 |
+ subdev << 15;
+ out->mask = func_m |
+ dev_m << 7 |
+ subdev_m << 15;
+
+ if (len) {
+ out->minlen = len;
+ out->maxlen = len;
+ }
+ return 0;
+}
+
+/*
+ * Sony SIRC decoder
+ * See also http://www.sbprojects.com/knowledge/ir/sirc.php
+ * http://picprojects.org.uk/projects/sirc/sonysirc.pdf
+ */
+struct img_ir_decoder img_ir_sony = {
+ .type = RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20,
+ .control = {
+ .decoden = 1,
+ .code_type = IMG_IR_CODETYPE_PULSELEN,
+ },
+ /* main timings */
+ .unit = 600000, /* 600 us */
+ .timings = {
+ /* leader symbol */
+ .ldr = {
+ .pulse = { 4 /* 2.4 ms */ },
+ .space = { 1 /* 600 us */ },
+ },
+ /* 0 symbol */
+ .s00 = {
+ .pulse = { 1 /* 600 us */ },
+ .space = { 1 /* 600 us */ },
+ },
+ /* 1 symbol */
+ .s01 = {
+ .pulse = { 2 /* 1.2 ms */ },
+ .space = { 1 /* 600 us */ },
+ },
+ /* free time */
+ .ft = {
+ .minlen = 12,
+ .maxlen = 20,
+ .ft_min = 10, /* 6 ms */
+ },
+ },
+ /* scancode logic */
+ .scancode = img_ir_sony_scancode,
+ .filter = img_ir_sony_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir.h b/drivers/media/rc/img-ir/img-ir.h
new file mode 100644
index 000000000000..afb189394af9
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir.h
@@ -0,0 +1,166 @@
+/*
+ * ImgTec IR Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ */
+
+#ifndef _IMG_IR_H_
+#define _IMG_IR_H_
+
+#include <linux/io.h>
+#include <linux/spinlock.h>
+
+#include "img-ir-raw.h"
+#include "img-ir-hw.h"
+
+/* registers */
+
+/* relative to the start of the IR block of registers */
+#define IMG_IR_CONTROL 0x00
+#define IMG_IR_STATUS 0x04
+#define IMG_IR_DATA_LW 0x08
+#define IMG_IR_DATA_UP 0x0c
+#define IMG_IR_LEAD_SYMB_TIMING 0x10
+#define IMG_IR_S00_SYMB_TIMING 0x14
+#define IMG_IR_S01_SYMB_TIMING 0x18
+#define IMG_IR_S10_SYMB_TIMING 0x1c
+#define IMG_IR_S11_SYMB_TIMING 0x20
+#define IMG_IR_FREE_SYMB_TIMING 0x24
+#define IMG_IR_POW_MOD_PARAMS 0x28
+#define IMG_IR_POW_MOD_ENABLE 0x2c
+#define IMG_IR_IRQ_MSG_DATA_LW 0x30
+#define IMG_IR_IRQ_MSG_DATA_UP 0x34
+#define IMG_IR_IRQ_MSG_MASK_LW 0x38
+#define IMG_IR_IRQ_MSG_MASK_UP 0x3c
+#define IMG_IR_IRQ_ENABLE 0x40
+#define IMG_IR_IRQ_STATUS 0x44
+#define IMG_IR_IRQ_CLEAR 0x48
+#define IMG_IR_IRCORE_ID 0xf0
+#define IMG_IR_CORE_REV 0xf4
+#define IMG_IR_CORE_DES1 0xf8
+#define IMG_IR_CORE_DES2 0xfc
+
+
+/* field masks */
+
+/* IMG_IR_CONTROL */
+#define IMG_IR_DECODEN 0x40000000
+#define IMG_IR_CODETYPE 0x30000000
+#define IMG_IR_CODETYPE_SHIFT 28
+#define IMG_IR_HDRTOG 0x08000000
+#define IMG_IR_LDRDEC 0x04000000
+#define IMG_IR_DECODINPOL 0x02000000 /* active high */
+#define IMG_IR_BITORIEN 0x01000000 /* MSB first */
+#define IMG_IR_D1VALIDSEL 0x00008000
+#define IMG_IR_BITINV 0x00000040 /* don't invert */
+#define IMG_IR_DECODEND2 0x00000010
+#define IMG_IR_BITORIEND2 0x00000002 /* MSB first */
+#define IMG_IR_BITINVD2 0x00000001 /* don't invert */
+
+/* IMG_IR_STATUS */
+#define IMG_IR_RXDVALD2 0x00001000
+#define IMG_IR_IRRXD 0x00000400
+#define IMG_IR_TOGSTATE 0x00000200
+#define IMG_IR_RXDVAL 0x00000040
+#define IMG_IR_RXDLEN 0x0000003f
+#define IMG_IR_RXDLEN_SHIFT 0
+
+/* IMG_IR_LEAD_SYMB_TIMING, IMG_IR_Sxx_SYMB_TIMING */
+#define IMG_IR_PD_MAX 0xff000000
+#define IMG_IR_PD_MAX_SHIFT 24
+#define IMG_IR_PD_MIN 0x00ff0000
+#define IMG_IR_PD_MIN_SHIFT 16
+#define IMG_IR_W_MAX 0x0000ff00
+#define IMG_IR_W_MAX_SHIFT 8
+#define IMG_IR_W_MIN 0x000000ff
+#define IMG_IR_W_MIN_SHIFT 0
+
+/* IMG_IR_FREE_SYMB_TIMING */
+#define IMG_IR_MAXLEN 0x0007e000
+#define IMG_IR_MAXLEN_SHIFT 13
+#define IMG_IR_MINLEN 0x00001f00
+#define IMG_IR_MINLEN_SHIFT 8
+#define IMG_IR_FT_MIN 0x000000ff
+#define IMG_IR_FT_MIN_SHIFT 0
+
+/* IMG_IR_POW_MOD_PARAMS */
+#define IMG_IR_PERIOD_LEN 0x3f000000
+#define IMG_IR_PERIOD_LEN_SHIFT 24
+#define IMG_IR_PERIOD_DUTY 0x003f0000
+#define IMG_IR_PERIOD_DUTY_SHIFT 16
+#define IMG_IR_STABLE_STOP 0x00003f00
+#define IMG_IR_STABLE_STOP_SHIFT 8
+#define IMG_IR_STABLE_START 0x0000003f
+#define IMG_IR_STABLE_START_SHIFT 0
+
+/* IMG_IR_POW_MOD_ENABLE */
+#define IMG_IR_POWER_OUT_EN 0x00000002
+#define IMG_IR_POWER_MOD_EN 0x00000001
+
+/* IMG_IR_IRQ_ENABLE, IMG_IR_IRQ_STATUS, IMG_IR_IRQ_CLEAR */
+#define IMG_IR_IRQ_DEC2_ERR 0x00000080
+#define IMG_IR_IRQ_DEC_ERR 0x00000040
+#define IMG_IR_IRQ_ACT_LEVEL 0x00000020
+#define IMG_IR_IRQ_FALL_EDGE 0x00000010
+#define IMG_IR_IRQ_RISE_EDGE 0x00000008
+#define IMG_IR_IRQ_DATA_MATCH 0x00000004
+#define IMG_IR_IRQ_DATA2_VALID 0x00000002
+#define IMG_IR_IRQ_DATA_VALID 0x00000001
+#define IMG_IR_IRQ_ALL 0x000000ff
+#define IMG_IR_IRQ_EDGE (IMG_IR_IRQ_FALL_EDGE | IMG_IR_IRQ_RISE_EDGE)
+
+/* IMG_IR_CORE_ID */
+#define IMG_IR_CORE_ID 0x00ff0000
+#define IMG_IR_CORE_ID_SHIFT 16
+#define IMG_IR_CORE_CONFIG 0x0000ffff
+#define IMG_IR_CORE_CONFIG_SHIFT 0
+
+/* IMG_IR_CORE_REV */
+#define IMG_IR_DESIGNER 0xff000000
+#define IMG_IR_DESIGNER_SHIFT 24
+#define IMG_IR_MAJOR_REV 0x00ff0000
+#define IMG_IR_MAJOR_REV_SHIFT 16
+#define IMG_IR_MINOR_REV 0x0000ff00
+#define IMG_IR_MINOR_REV_SHIFT 8
+#define IMG_IR_MAINT_REV 0x000000ff
+#define IMG_IR_MAINT_REV_SHIFT 0
+
+struct device;
+struct clk;
+
+/**
+ * struct img_ir_priv - Private driver data.
+ * @dev: Platform device.
+ * @irq: IRQ number.
+ * @clk: Input clock.
+ * @reg_base: Iomem base address of IR register block.
+ * @lock: Protects IR registers and variables in this struct.
+ * @raw: Driver data for raw decoder.
+ * @hw: Driver data for hardware decoder.
+ */
+struct img_ir_priv {
+ struct device *dev;
+ int irq;
+ struct clk *clk;
+ void __iomem *reg_base;
+ spinlock_t lock;
+
+ struct img_ir_priv_raw raw;
+ struct img_ir_priv_hw hw;
+};
+
+/* Hardware access */
+
+static inline void img_ir_write(struct img_ir_priv *priv,
+ unsigned int reg_offs, unsigned int data)
+{
+ iowrite32(data, priv->reg_base + reg_offs);
+}
+
+static inline unsigned int img_ir_read(struct img_ir_priv *priv,
+ unsigned int reg_offs)
+{
+ return ioread32(priv->reg_base + reg_offs);
+}
+
+#endif /* _IMG_IR_H_ */
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 822b9f47ca72..6f24e77b1488 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -1017,7 +1017,7 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 *rc_type)
unsigned char ir_proto_packet[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 };
- if (*rc_type && !(*rc_type & rc->allowed_protos))
+ if (*rc_type && !rc_protocols_allowed(rc, *rc_type))
dev_warn(dev, "Looks like you're trying to use an IR protocol "
"this device does not support\n");
@@ -1867,7 +1867,8 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
rdev->priv = ictx;
rdev->driver_type = RC_DRIVER_SCANCODE;
- rdev->allowed_protos = RC_BIT_OTHER | RC_BIT_RC6_MCE; /* iMON PAD or MCE */
+ /* iMON PAD or MCE */
+ rc_set_allowed_protocols(rdev, RC_BIT_OTHER | RC_BIT_RC6_MCE);
rdev->change_protocol = imon_ir_change_protocol;
rdev->driver_name = MOD_NAME;
@@ -1880,7 +1881,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
if (ictx->product == 0xffdc) {
imon_get_ffdc_type(ictx);
- rdev->allowed_protos = ictx->rc_type;
+ rc_set_allowed_protocols(rdev, ictx->rc_type);
}
imon_set_display_type(ictx);
diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c
index 3948138ca870..4ea62a1dcfda 100644
--- a/drivers/media/rc/ir-jvc-decoder.c
+++ b/drivers/media/rc/ir-jvc-decoder.c
@@ -47,7 +47,7 @@ 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))
+ if (!rc_protocols_enabled(dev, RC_BIT_JVC))
return 0;
if (!is_timing_event(ev)) {
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index ed2c8a1ed8ca..d731da6c414d 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -35,7 +35,7 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev)
struct lirc_codec *lirc = &dev->raw->lirc;
int sample;
- if (!(dev->enabled_protocols & RC_BIT_LIRC))
+ if (!rc_protocols_enabled(dev, RC_BIT_LIRC))
return 0;
if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf)
diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c
index 9f3c9b59f30c..0c55f794c8cf 100644
--- a/drivers/media/rc/ir-mce_kbd-decoder.c
+++ b/drivers/media/rc/ir-mce_kbd-decoder.c
@@ -216,7 +216,7 @@ 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))
+ if (!rc_protocols_enabled(dev, RC_BIT_MCE_KBD))
return 0;
if (!is_timing_event(ev)) {
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
index 9a9009411439..9de1791d2494 100644
--- a/drivers/media/rc/ir-nec-decoder.c
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -1,6 +1,6 @@
/* ir-nec-decoder.c - handle NEC IR Pulse/Space protocol
*
- * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -52,7 +52,7 @@ 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))
+ if (!rc_protocols_enabled(dev, RC_BIT_NEC))
return 0;
if (!is_timing_event(ev)) {
@@ -172,7 +172,10 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
if (send_32bits) {
/* NEC transport, but modified protocol, used by at
* least Apple and TiVo remotes */
- scancode = data->bits;
+ scancode = not_address << 24 |
+ address << 16 |
+ not_command << 8 |
+ command;
IR_dprintk(1, "NEC (modified) scancode 0x%08x\n", scancode);
} else if ((address ^ not_address) != 0xff) {
/* Extended NEC */
@@ -222,6 +225,6 @@ module_init(ir_nec_decode_init);
module_exit(ir_nec_decode_exit);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("NEC IR protocol decoder");
diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c
index 5c42750c7b71..763c9d131d0f 100644
--- a/drivers/media/rc/ir-raw.c
+++ b/drivers/media/rc/ir-raw.c
@@ -1,6 +1,6 @@
/* ir-raw.c - handle IR pulse/space events
*
- * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -256,7 +256,7 @@ int ir_raw_event_register(struct rc_dev *dev)
return -ENOMEM;
dev->raw->dev = dev;
- dev->enabled_protocols = ~0;
+ rc_set_enabled_protocols(dev, ~0);
rc = kfifo_alloc(&dev->raw->kfifo,
sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE,
GFP_KERNEL);
@@ -352,6 +352,7 @@ void ir_raw_init(void)
load_jvc_decode();
load_sony_decode();
load_sanyo_decode();
+ load_sharp_decode();
load_mce_kbd_decode();
load_lirc_codec();
diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
index 4e53a319c5d8..4295d9b250c8 100644
--- a/drivers/media/rc/ir-rc5-decoder.c
+++ b/drivers/media/rc/ir-rc5-decoder.c
@@ -1,6 +1,6 @@
/* ir-rc5-decoder.c - handle RC5(x) IR Pulse/Space protocol
*
- * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -52,7 +52,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
u8 toggle;
u32 scancode;
- if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X)))
+ if (!rc_protocols_enabled(dev, RC_BIT_RC5 | RC_BIT_RC5X))
return 0;
if (!is_timing_event(ev)) {
@@ -128,7 +128,7 @@ again:
if (data->wanted_bits == RC5X_NBITS) {
/* RC5X */
u8 xdata, command, system;
- if (!(dev->enabled_protocols & RC_BIT_RC5X)) {
+ if (!rc_protocols_enabled(dev, RC_BIT_RC5X)) {
data->state = STATE_INACTIVE;
return 0;
}
@@ -145,7 +145,7 @@ again:
} else {
/* RC5 */
u8 command, system;
- if (!(dev->enabled_protocols & RC_BIT_RC5)) {
+ if (!rc_protocols_enabled(dev, RC_BIT_RC5)) {
data->state = STATE_INACTIVE;
return 0;
}
@@ -193,6 +193,6 @@ module_init(ir_rc5_decode_init);
module_exit(ir_rc5_decode_exit);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("RC5(x) IR protocol decoder");
diff --git a/drivers/media/rc/ir-rc5-sz-decoder.c b/drivers/media/rc/ir-rc5-sz-decoder.c
index 865fe84fd854..dc18b7434db8 100644
--- a/drivers/media/rc/ir-rc5-sz-decoder.c
+++ b/drivers/media/rc/ir-rc5-sz-decoder.c
@@ -1,6 +1,6 @@
/* ir-rc5-sz-decoder.c - handle RC5 Streamzap IR Pulse/Space protocol
*
- * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
* Copyright (C) 2010 by Jarod Wilson <jarod@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -48,7 +48,7 @@ static int ir_rc5_sz_decode(struct rc_dev *dev, struct ir_raw_event ev)
u8 toggle, command, system;
u32 scancode;
- if (!(dev->enabled_protocols & RC_BIT_RC5_SZ))
+ if (!rc_protocols_enabled(dev, RC_BIT_RC5_SZ))
return 0;
if (!is_timing_event(ev)) {
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
index 7cba7d33a3fa..cfbd64e3999c 100644
--- a/drivers/media/rc/ir-rc6-decoder.c
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -89,9 +89,9 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
u8 toggle;
- 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)))
+ if (!rc_protocols_enabled(dev, 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)) {
diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c
index 0a06205b5677..eb715f04dc27 100644
--- a/drivers/media/rc/ir-sanyo-decoder.c
+++ b/drivers/media/rc/ir-sanyo-decoder.c
@@ -1,6 +1,6 @@
/* ir-sanyo-decoder.c - handle SANYO IR Pulse/Space protocol
*
- * Copyright (C) 2011 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2011 by Mauro Carvalho Chehab
*
* 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
@@ -58,7 +58,7 @@ 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))
+ if (!rc_protocols_enabled(dev, RC_BIT_SANYO))
return 0;
if (!is_timing_event(ev)) {
@@ -200,6 +200,6 @@ module_init(ir_sanyo_decode_init);
module_exit(ir_sanyo_decode_exit);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("SANYO IR protocol decoder");
diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c
new file mode 100644
index 000000000000..66d20394ceaa
--- /dev/null
+++ b/drivers/media/rc/ir-sharp-decoder.c
@@ -0,0 +1,200 @@
+/* ir-sharp-decoder.c - handle Sharp IR Pulse/Space protocol
+ *
+ * Copyright (C) 2013-2014 Imagination Technologies Ltd.
+ *
+ * Based on NEC decoder:
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitrev.h>
+#include <linux/module.h>
+#include "rc-core-priv.h"
+
+#define SHARP_NBITS 15
+#define SHARP_UNIT 40000 /* ns */
+#define SHARP_BIT_PULSE (8 * SHARP_UNIT) /* 320us */
+#define SHARP_BIT_0_PERIOD (25 * SHARP_UNIT) /* 1ms (680us space) */
+#define SHARP_BIT_1_PERIOD (50 * SHARP_UNIT) /* 2ms (1680ms space) */
+#define SHARP_ECHO_SPACE (1000 * SHARP_UNIT) /* 40 ms */
+#define SHARP_TRAILER_SPACE (125 * SHARP_UNIT) /* 5 ms (even longer) */
+
+enum sharp_state {
+ STATE_INACTIVE,
+ STATE_BIT_PULSE,
+ STATE_BIT_SPACE,
+ STATE_TRAILER_PULSE,
+ STATE_ECHO_SPACE,
+ STATE_TRAILER_SPACE,
+};
+
+/**
+ * ir_sharp_decode() - Decode one Sharp pulse or space
+ * @dev: the struct rc_dev descriptor of the device
+ * @duration: the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+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 (!rc_protocols_enabled(dev, RC_BIT_SHARP))
+ return 0;
+
+ if (!is_timing_event(ev)) {
+ if (ev.reset)
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ IR_dprintk(2, "Sharp decode started at state %d (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+ switch (data->state) {
+
+ case STATE_INACTIVE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, SHARP_BIT_PULSE,
+ SHARP_BIT_PULSE / 2))
+ break;
+
+ data->count = 0;
+ data->pulse_len = ev.duration;
+ data->state = STATE_BIT_SPACE;
+ return 0;
+
+ case STATE_BIT_PULSE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, SHARP_BIT_PULSE,
+ SHARP_BIT_PULSE / 2))
+ break;
+
+ data->pulse_len = ev.duration;
+ data->state = STATE_BIT_SPACE;
+ return 0;
+
+ case STATE_BIT_SPACE:
+ if (ev.pulse)
+ break;
+
+ data->bits <<= 1;
+ if (eq_margin(data->pulse_len + ev.duration, SHARP_BIT_1_PERIOD,
+ SHARP_BIT_PULSE * 2))
+ data->bits |= 1;
+ else if (!eq_margin(data->pulse_len + ev.duration,
+ SHARP_BIT_0_PERIOD, SHARP_BIT_PULSE * 2))
+ break;
+ data->count++;
+
+ if (data->count == SHARP_NBITS ||
+ data->count == SHARP_NBITS * 2)
+ data->state = STATE_TRAILER_PULSE;
+ else
+ data->state = STATE_BIT_PULSE;
+
+ return 0;
+
+ case STATE_TRAILER_PULSE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, SHARP_BIT_PULSE,
+ SHARP_BIT_PULSE / 2))
+ break;
+
+ if (data->count == SHARP_NBITS) {
+ /* exp,chk bits should be 1,0 */
+ if ((data->bits & 0x3) != 0x2)
+ break;
+ data->state = STATE_ECHO_SPACE;
+ } else {
+ data->state = STATE_TRAILER_SPACE;
+ }
+ return 0;
+
+ case STATE_ECHO_SPACE:
+ if (ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, SHARP_ECHO_SPACE,
+ SHARP_ECHO_SPACE / 4))
+ break;
+
+ data->state = STATE_BIT_PULSE;
+
+ return 0;
+
+ case STATE_TRAILER_SPACE:
+ if (ev.pulse)
+ break;
+
+ if (!geq_margin(ev.duration, SHARP_TRAILER_SPACE,
+ SHARP_BIT_PULSE / 2))
+ break;
+
+ /* Validate - command, ext, chk should be inverted in 2nd */
+ msg = (data->bits >> 15) & 0x7fff;
+ echo = data->bits & 0x7fff;
+ if ((msg ^ echo) != 0x3ff) {
+ IR_dprintk(1,
+ "Sharp checksum error: received 0x%04x, 0x%04x\n",
+ msg, echo);
+ break;
+ }
+
+ address = bitrev8((msg >> 7) & 0xf8);
+ command = bitrev8((msg >> 2) & 0xff);
+
+ scancode = address << 8 | command;
+ IR_dprintk(1, "Sharp scancode 0x%04x\n", scancode);
+
+ rc_keydown(dev, scancode, 0);
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ IR_dprintk(1, "Sharp decode failed at count %d state %d (%uus %s)\n",
+ data->count, data->state, TO_US(ev.duration),
+ TO_STR(ev.pulse));
+ data->state = STATE_INACTIVE;
+ return -EINVAL;
+}
+
+static struct ir_raw_handler sharp_handler = {
+ .protocols = RC_BIT_SHARP,
+ .decode = ir_sharp_decode,
+};
+
+static int __init ir_sharp_decode_init(void)
+{
+ ir_raw_handler_register(&sharp_handler);
+
+ pr_info("IR Sharp protocol handler initialized\n");
+ return 0;
+}
+
+static void __exit ir_sharp_decode_exit(void)
+{
+ ir_raw_handler_unregister(&sharp_handler);
+}
+
+module_init(ir_sharp_decode_init);
+module_exit(ir_sharp_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("James Hogan <james.hogan@imgtec.com>");
+MODULE_DESCRIPTION("Sharp IR protocol decoder");
diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
index 29ab9c2db060..599c19a73360 100644
--- a/drivers/media/rc/ir-sony-decoder.c
+++ b/drivers/media/rc/ir-sony-decoder.c
@@ -45,8 +45,8 @@ 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)))
+ if (!rc_protocols_enabled(dev, RC_BIT_SONY12 | RC_BIT_SONY15 |
+ RC_BIT_SONY20))
return 0;
if (!is_timing_event(ev)) {
@@ -124,7 +124,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
switch (data->count) {
case 12:
- if (!(dev->enabled_protocols & RC_BIT_SONY12)) {
+ if (!rc_protocols_enabled(dev, RC_BIT_SONY12)) {
data->state = STATE_INACTIVE;
return 0;
}
@@ -133,7 +133,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
function = bitrev8((data->bits >> 4) & 0xFE);
break;
case 15:
- if (!(dev->enabled_protocols & RC_BIT_SONY15)) {
+ if (!rc_protocols_enabled(dev, RC_BIT_SONY15)) {
data->state = STATE_INACTIVE;
return 0;
}
@@ -142,7 +142,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
function = bitrev8((data->bits >> 7) & 0xFE);
break;
case 20:
- if (!(dev->enabled_protocols & RC_BIT_SONY20)) {
+ if (!rc_protocols_enabled(dev, RC_BIT_SONY20)) {
data->state = STATE_INACTIVE;
return 0;
}
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 63b42252166a..ab24cc6d3655 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1563,7 +1563,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
/* set up ir-core props */
rdev->priv = itdev;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
rdev->open = ite_open;
rdev->close = ite_close;
rdev->s_idle = ite_s_idle;
diff --git a/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c b/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c
index b0e42df7ff82..01d901fbfc8b 100644
--- a/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c
+++ b/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -87,4 +87,4 @@ module_init(init_rc_map_adstech_dvb_t_pci)
module_exit(exit_rc_map_adstech_dvb_t_pci)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-apac-viewcomp.c b/drivers/media/rc/keymaps/rc-apac-viewcomp.c
index 8c92ff95f94d..bf9efa007e1c 100644
--- a/drivers/media/rc/keymaps/rc-apac-viewcomp.c
+++ b/drivers/media/rc/keymaps/rc-apac-viewcomp.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -78,4 +78,4 @@ module_init(init_rc_map_apac_viewcomp)
module_exit(exit_rc_map_apac_viewcomp)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-asus-pc39.c b/drivers/media/rc/keymaps/rc-asus-pc39.c
index 2caf2117759b..9e674ba5dd4f 100644
--- a/drivers/media/rc/keymaps/rc-asus-pc39.c
+++ b/drivers/media/rc/keymaps/rc-asus-pc39.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -89,4 +89,4 @@ module_init(init_rc_map_asus_pc39)
module_exit(exit_rc_map_asus_pc39)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-asus-ps3-100.c b/drivers/media/rc/keymaps/rc-asus-ps3-100.c
index ba76609c5936..e45de35f528f 100644
--- a/drivers/media/rc/keymaps/rc-asus-ps3-100.c
+++ b/drivers/media/rc/keymaps/rc-asus-ps3-100.c
@@ -1,6 +1,6 @@
/* asus-ps3-100.h - Keytable for asus_ps3_100 Remote Controller
*
- * Copyright (c) 2012 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2012 by Mauro Carvalho Chehab
*
* Based on a previous patch from Remi Schwartz <remi.schwartz@gmail.com>
*
@@ -88,4 +88,4 @@ module_init(init_rc_map_asus_ps3_100)
module_exit(exit_rc_map_asus_ps3_100)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c b/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c
index 2031224a2027..91392d4cfd6d 100644
--- a/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c
+++ b/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -67,4 +67,4 @@ module_init(init_rc_map_ati_tv_wonder_hd_600)
module_exit(exit_rc_map_ati_tv_wonder_hd_600)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-a16d.c b/drivers/media/rc/keymaps/rc-avermedia-a16d.c
index 894939ac17f2..ff30a71d623e 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-a16d.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-a16d.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -73,4 +73,4 @@ module_init(init_rc_map_avermedia_a16d)
module_exit(exit_rc_map_avermedia_a16d)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-cardbus.c b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
index d2aaf5b9e39f..d7471a6de9b4 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -95,4 +95,4 @@ module_init(init_rc_map_avermedia_cardbus)
module_exit(exit_rc_map_avermedia_cardbus)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-dvbt.c b/drivers/media/rc/keymaps/rc-avermedia-dvbt.c
index dc2baf062398..e2417d6331fe 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-dvbt.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-dvbt.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -76,4 +76,4 @@ module_init(init_rc_map_avermedia_dvbt)
module_exit(exit_rc_map_avermedia_dvbt)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-m135a.c b/drivers/media/rc/keymaps/rc-avermedia-m135a.c
index 04269d31fa19..843598a5f1b5 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-m135a.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-m135a.c
@@ -1,6 +1,6 @@
/* avermedia-m135a.c - Keytable for Avermedia M135A Remote Controllers
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
* Copyright (c) 2010 by Herton Ronaldo Krzesinski <herton@mandriva.com.br>
*
* This program is free software; you can redistribute it and/or modify
@@ -145,4 +145,4 @@ module_init(init_rc_map_avermedia_m135a)
module_exit(exit_rc_map_avermedia_m135a)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c b/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c
index e83b1a1939bf..b24e7481ac21 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c
@@ -93,4 +93,4 @@ module_init(init_rc_map_avermedia_m733a_rm_k6)
module_exit(exit_rc_map_avermedia_m733a_rm_k6)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia.c b/drivers/media/rc/keymaps/rc-avermedia.c
index c6063dfcd507..3f68fbecc188 100644
--- a/drivers/media/rc/keymaps/rc-avermedia.c
+++ b/drivers/media/rc/keymaps/rc-avermedia.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -84,4 +84,4 @@ module_init(init_rc_map_avermedia)
module_exit(exit_rc_map_avermedia)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avertv-303.c b/drivers/media/rc/keymaps/rc-avertv-303.c
index 14f78451e64e..c35bc5b835c4 100644
--- a/drivers/media/rc/keymaps/rc-avertv-303.c
+++ b/drivers/media/rc/keymaps/rc-avertv-303.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -83,4 +83,4 @@ module_init(init_rc_map_avertv_303)
module_exit(exit_rc_map_avertv_303)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-behold-columbus.c b/drivers/media/rc/keymaps/rc-behold-columbus.c
index 086b4b1f19e1..1fc344e9daa7 100644
--- a/drivers/media/rc/keymaps/rc-behold-columbus.c
+++ b/drivers/media/rc/keymaps/rc-behold-columbus.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -106,4 +106,4 @@ module_init(init_rc_map_behold_columbus)
module_exit(exit_rc_map_behold_columbus)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-behold.c b/drivers/media/rc/keymaps/rc-behold.c
index 0877e3480941..d6519f8ac95a 100644
--- a/drivers/media/rc/keymaps/rc-behold.c
+++ b/drivers/media/rc/keymaps/rc-behold.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -139,4 +139,4 @@ module_init(init_rc_map_behold)
module_exit(exit_rc_map_behold)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-budget-ci-old.c b/drivers/media/rc/keymaps/rc-budget-ci-old.c
index 8311e092c098..b196a5f436a3 100644
--- a/drivers/media/rc/keymaps/rc-budget-ci-old.c
+++ b/drivers/media/rc/keymaps/rc-budget-ci-old.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -91,4 +91,4 @@ module_init(init_rc_map_budget_ci_old)
module_exit(exit_rc_map_budget_ci_old)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-cinergy-1400.c b/drivers/media/rc/keymaps/rc-cinergy-1400.c
index 0c87fbaf99ab..a099c080bf8c 100644
--- a/drivers/media/rc/keymaps/rc-cinergy-1400.c
+++ b/drivers/media/rc/keymaps/rc-cinergy-1400.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -82,4 +82,4 @@ module_init(init_rc_map_cinergy_1400)
module_exit(exit_rc_map_cinergy_1400)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-cinergy.c b/drivers/media/rc/keymaps/rc-cinergy.c
index 309e9e3fb6f3..b0f4328bdd6f 100644
--- a/drivers/media/rc/keymaps/rc-cinergy.c
+++ b/drivers/media/rc/keymaps/rc-cinergy.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -76,4 +76,4 @@ module_init(init_rc_map_cinergy)
module_exit(exit_rc_map_cinergy)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dib0700-nec.c b/drivers/media/rc/keymaps/rc-dib0700-nec.c
index 492a05ade7e1..a0fa543c9f9e 100644
--- a/drivers/media/rc/keymaps/rc-dib0700-nec.c
+++ b/drivers/media/rc/keymaps/rc-dib0700-nec.c
@@ -1,6 +1,6 @@
/* rc-dvb0700-big.c - Keytable for devices in dvb0700
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* TODO: This table is a real mess, as it merges RC codes from several
* devices into a big table. It also has both RC-5 and NEC codes inside.
@@ -122,4 +122,4 @@ module_init(init_rc_map)
module_exit(exit_rc_map)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dib0700-rc5.c b/drivers/media/rc/keymaps/rc-dib0700-rc5.c
index 454ea596a7ee..907941145eb7 100644
--- a/drivers/media/rc/keymaps/rc-dib0700-rc5.c
+++ b/drivers/media/rc/keymaps/rc-dib0700-rc5.c
@@ -1,6 +1,6 @@
/* rc-dvb0700-big.c - Keytable for devices in dvb0700
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* TODO: This table is a real mess, as it merges RC codes from several
* devices into a big table. It also has both RC-5 and NEC codes inside.
@@ -233,4 +233,4 @@ module_init(init_rc_map)
module_exit(exit_rc_map)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dm1105-nec.c b/drivers/media/rc/keymaps/rc-dm1105-nec.c
index 67fc9fb0c007..46e7ae414cc8 100644
--- a/drivers/media/rc/keymaps/rc-dm1105-nec.c
+++ b/drivers/media/rc/keymaps/rc-dm1105-nec.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -74,4 +74,4 @@ module_init(init_rc_map_dm1105_nec)
module_exit(exit_rc_map_dm1105_nec)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c b/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c
index 91ea91de9179..d2826b46fea2 100644
--- a/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c
+++ b/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -76,4 +76,4 @@ module_init(init_rc_map_dntv_live_dvb_t)
module_exit(exit_rc_map_dntv_live_dvb_t)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c b/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c
index fd680d4d3eb6..0d74769467b5 100644
--- a/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c
+++ b/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -95,4 +95,4 @@ module_init(init_rc_map_dntv_live_dvbt_pro)
module_exit(exit_rc_map_dntv_live_dvbt_pro)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-em-terratec.c b/drivers/media/rc/keymaps/rc-em-terratec.c
index d1fcd64c0f90..7f1e06be175b 100644
--- a/drivers/media/rc/keymaps/rc-em-terratec.c
+++ b/drivers/media/rc/keymaps/rc-em-terratec.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -67,4 +67,4 @@ module_init(init_rc_map_em_terratec)
module_exit(exit_rc_map_em_terratec)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c b/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c
index 2fe45e41fe49..4fc3904daf06 100644
--- a/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c
+++ b/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -79,4 +79,4 @@ module_init(init_rc_map_encore_enltv_fm53)
module_exit(exit_rc_map_encore_enltv_fm53)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv.c b/drivers/media/rc/keymaps/rc-encore-enltv.c
index 223de75a6d1c..f1914e23d203 100644
--- a/drivers/media/rc/keymaps/rc-encore-enltv.c
+++ b/drivers/media/rc/keymaps/rc-encore-enltv.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -110,4 +110,4 @@ module_init(init_rc_map_encore_enltv)
module_exit(exit_rc_map_encore_enltv)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv2.c b/drivers/media/rc/keymaps/rc-encore-enltv2.c
index 669cbff22b7e..9c6c55240d18 100644
--- a/drivers/media/rc/keymaps/rc-encore-enltv2.c
+++ b/drivers/media/rc/keymaps/rc-encore-enltv2.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -88,4 +88,4 @@ module_init(init_rc_map_encore_enltv2)
module_exit(exit_rc_map_encore_enltv2)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-evga-indtube.c b/drivers/media/rc/keymaps/rc-evga-indtube.c
index 2c647fc25916..2370d2a3deb6 100644
--- a/drivers/media/rc/keymaps/rc-evga-indtube.c
+++ b/drivers/media/rc/keymaps/rc-evga-indtube.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -59,4 +59,4 @@ module_init(init_rc_map_evga_indtube)
module_exit(exit_rc_map_evga_indtube)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-eztv.c b/drivers/media/rc/keymaps/rc-eztv.c
index 76921445c1d9..b5c96ed84376 100644
--- a/drivers/media/rc/keymaps/rc-eztv.c
+++ b/drivers/media/rc/keymaps/rc-eztv.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -94,4 +94,4 @@ module_init(init_rc_map_eztv)
module_exit(exit_rc_map_eztv)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-flydvb.c b/drivers/media/rc/keymaps/rc-flydvb.c
index 3a6bba311b08..25cb89fac03c 100644
--- a/drivers/media/rc/keymaps/rc-flydvb.c
+++ b/drivers/media/rc/keymaps/rc-flydvb.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -75,4 +75,4 @@ module_init(init_rc_map_flydvb)
module_exit(exit_rc_map_flydvb)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-flyvideo.c b/drivers/media/rc/keymaps/rc-flyvideo.c
index bf9da584643b..e71377dd0534 100644
--- a/drivers/media/rc/keymaps/rc-flyvideo.c
+++ b/drivers/media/rc/keymaps/rc-flyvideo.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -68,4 +68,4 @@ module_init(init_rc_map_flyvideo)
module_exit(exit_rc_map_flyvideo)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c b/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c
index 2f0970fe7832..cf0608dc83d5 100644
--- a/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c
+++ b/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -96,4 +96,4 @@ module_init(init_rc_map_fusionhdtv_mce)
module_exit(exit_rc_map_fusionhdtv_mce)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-gadmei-rm008z.c b/drivers/media/rc/keymaps/rc-gadmei-rm008z.c
index 0e98ec467c34..03575bdb2eca 100644
--- a/drivers/media/rc/keymaps/rc-gadmei-rm008z.c
+++ b/drivers/media/rc/keymaps/rc-gadmei-rm008z.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -79,4 +79,4 @@ module_init(init_rc_map_gadmei_rm008z)
module_exit(exit_rc_map_gadmei_rm008z)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c b/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c
index a2e2faa1d1b3..b2ab13b0dcb1 100644
--- a/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c
+++ b/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -82,4 +82,4 @@ module_init(init_rc_map_genius_tvgo_a11mce)
module_exit(exit_rc_map_genius_tvgo_a11mce)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-gotview7135.c b/drivers/media/rc/keymaps/rc-gotview7135.c
index 864614e19314..229a36ac7f0a 100644
--- a/drivers/media/rc/keymaps/rc-gotview7135.c
+++ b/drivers/media/rc/keymaps/rc-gotview7135.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -77,4 +77,4 @@ module_init(init_rc_map_gotview7135)
module_exit(exit_rc_map_gotview7135)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-hauppauge.c b/drivers/media/rc/keymaps/rc-hauppauge.c
index 929bbbc16393..36d57f7c532b 100644
--- a/drivers/media/rc/keymaps/rc-hauppauge.c
+++ b/drivers/media/rc/keymaps/rc-hauppauge.c
@@ -8,7 +8,7 @@
* - Hauppauge Black;
* - DSR-0112 remote bundled with Haupauge MiniStick.
*
- * Copyright (c) 2010-2011 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010-2011 by Mauro Carvalho Chehab
*
* 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
@@ -290,4 +290,4 @@ module_init(init_rc_map_rc5_hauppauge_new)
module_exit(exit_rc_map_rc5_hauppauge_new)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-iodata-bctv7e.c b/drivers/media/rc/keymaps/rc-iodata-bctv7e.c
index 34540dfc3df5..9ee154cb0c6b 100644
--- a/drivers/media/rc/keymaps/rc-iodata-bctv7e.c
+++ b/drivers/media/rc/keymaps/rc-iodata-bctv7e.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -86,4 +86,4 @@ module_init(init_rc_map_iodata_bctv7e)
module_exit(exit_rc_map_iodata_bctv7e)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-kaiomy.c b/drivers/media/rc/keymaps/rc-kaiomy.c
index 4264a787c150..60803a732c08 100644
--- a/drivers/media/rc/keymaps/rc-kaiomy.c
+++ b/drivers/media/rc/keymaps/rc-kaiomy.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -85,4 +85,4 @@ module_init(init_rc_map_kaiomy)
module_exit(exit_rc_map_kaiomy)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-kworld-315u.c b/drivers/media/rc/keymaps/rc-kworld-315u.c
index e48cd267dda6..ba087eed1ed9 100644
--- a/drivers/media/rc/keymaps/rc-kworld-315u.c
+++ b/drivers/media/rc/keymaps/rc-kworld-315u.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -81,4 +81,4 @@ module_init(init_rc_map_kworld_315u)
module_exit(exit_rc_map_kworld_315u)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-kworld-pc150u.c b/drivers/media/rc/keymaps/rc-kworld-pc150u.c
index 233bb5ee087f..b92e571f4def 100644
--- a/drivers/media/rc/keymaps/rc-kworld-pc150u.c
+++ b/drivers/media/rc/keymaps/rc-kworld-pc150u.c
@@ -4,7 +4,7 @@
*
* Copyright (c) 2010 by Kyle Strickland
* (based on kworld-plus-tv-analog.c by
- * Mauro Carvalho Chehab <mchehab@redhat.com>)
+ * Mauro Carvalho Chehab)
*
* 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
diff --git a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
index 32998d6b787d..edc868564f99 100644
--- a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
+++ b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -97,4 +97,4 @@ module_init(init_rc_map_kworld_plus_tv_analog)
module_exit(exit_rc_map_kworld_plus_tv_analog)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-manli.c b/drivers/media/rc/keymaps/rc-manli.c
index e7038bb71bf6..92424ef2aaa6 100644
--- a/drivers/media/rc/keymaps/rc-manli.c
+++ b/drivers/media/rc/keymaps/rc-manli.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -132,4 +132,4 @@ module_init(init_rc_map_manli)
module_exit(exit_rc_map_manli)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c b/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c
index c393d8a50bca..fd7a55c56167 100644
--- a/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c
+++ b/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -121,4 +121,4 @@ module_init(init_rc_map_msi_tvanywhere_plus)
module_exit(exit_rc_map_msi_tvanywhere_plus)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-msi-tvanywhere.c b/drivers/media/rc/keymaps/rc-msi-tvanywhere.c
index a7003d3a3c8a..4233a8d4d63e 100644
--- a/drivers/media/rc/keymaps/rc-msi-tvanywhere.c
+++ b/drivers/media/rc/keymaps/rc-msi-tvanywhere.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -67,4 +67,4 @@ module_init(init_rc_map_msi_tvanywhere)
module_exit(exit_rc_map_msi_tvanywhere)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-nebula.c b/drivers/media/rc/keymaps/rc-nebula.c
index 3f0ddd7afd30..8ec881adb7cf 100644
--- a/drivers/media/rc/keymaps/rc-nebula.c
+++ b/drivers/media/rc/keymaps/rc-nebula.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -94,4 +94,4 @@ module_init(init_rc_map_nebula)
module_exit(exit_rc_map_nebula)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
index 8d4dae2e2ece..292bbad35d21 100644
--- a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
+++ b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -14,7 +14,7 @@
#include <linux/module.h>
/* Terratec Cinergy Hybrid T USB XS FM
- Mauro Carvalho Chehab <mchehab@redhat.com>
+ Mauro Carvalho Chehab
*/
static struct rc_map_table nec_terratec_cinergy_xs[] = {
@@ -155,4 +155,4 @@ module_init(init_rc_map_nec_terratec_cinergy_xs)
module_exit(exit_rc_map_nec_terratec_cinergy_xs)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-norwood.c b/drivers/media/rc/keymaps/rc-norwood.c
index 9e65f07157ab..ca1b82a2c54f 100644
--- a/drivers/media/rc/keymaps/rc-norwood.c
+++ b/drivers/media/rc/keymaps/rc-norwood.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -83,4 +83,4 @@ module_init(init_rc_map_norwood)
module_exit(exit_rc_map_norwood)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-npgtech.c b/drivers/media/rc/keymaps/rc-npgtech.c
index 65d0cfc3c33b..1fb946024512 100644
--- a/drivers/media/rc/keymaps/rc-npgtech.c
+++ b/drivers/media/rc/keymaps/rc-npgtech.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -78,4 +78,4 @@ module_init(init_rc_map_npgtech)
module_exit(exit_rc_map_npgtech)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pctv-sedna.c b/drivers/media/rc/keymaps/rc-pctv-sedna.c
index bf2cbdfe2e32..5ef01ab3fd50 100644
--- a/drivers/media/rc/keymaps/rc-pctv-sedna.c
+++ b/drivers/media/rc/keymaps/rc-pctv-sedna.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -78,4 +78,4 @@ module_init(init_rc_map_pctv_sedna)
module_exit(exit_rc_map_pctv_sedna)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-color.c b/drivers/media/rc/keymaps/rc-pinnacle-color.c
index b46cd8fe6438..a218b471a4ca 100644
--- a/drivers/media/rc/keymaps/rc-pinnacle-color.c
+++ b/drivers/media/rc/keymaps/rc-pinnacle-color.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -92,4 +92,4 @@ module_init(init_rc_map_pinnacle_color)
module_exit(exit_rc_map_pinnacle_color)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-grey.c b/drivers/media/rc/keymaps/rc-pinnacle-grey.c
index d525df9ad868..4a3f467a47a2 100644
--- a/drivers/media/rc/keymaps/rc-pinnacle-grey.c
+++ b/drivers/media/rc/keymaps/rc-pinnacle-grey.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -87,4 +87,4 @@ module_init(init_rc_map_pinnacle_grey)
module_exit(exit_rc_map_pinnacle_grey)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c b/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
index a4603d035374..e89cc10b68bf 100644
--- a/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
+++ b/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -68,4 +68,4 @@ module_init(init_rc_map_pinnacle_pctv_hd)
module_exit(exit_rc_map_pinnacle_pctv_hd)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview-002t.c b/drivers/media/rc/keymaps/rc-pixelview-002t.c
index 33eb64333c6f..d967c3816fdc 100644
--- a/drivers/media/rc/keymaps/rc-pixelview-002t.c
+++ b/drivers/media/rc/keymaps/rc-pixelview-002t.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -75,4 +75,4 @@ module_init(init_rc_map_pixelview)
module_exit(exit_rc_map_pixelview)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview-mk12.c b/drivers/media/rc/keymaps/rc-pixelview-mk12.c
index 21f4dd25c2ec..224d0efaa6e5 100644
--- a/drivers/media/rc/keymaps/rc-pixelview-mk12.c
+++ b/drivers/media/rc/keymaps/rc-pixelview-mk12.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -81,4 +81,4 @@ module_init(init_rc_map_pixelview)
module_exit(exit_rc_map_pixelview)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview-new.c b/drivers/media/rc/keymaps/rc-pixelview-new.c
index f944ad2cac2b..781d788d6b6d 100644
--- a/drivers/media/rc/keymaps/rc-pixelview-new.c
+++ b/drivers/media/rc/keymaps/rc-pixelview-new.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -81,4 +81,4 @@ module_init(init_rc_map_pixelview_new)
module_exit(exit_rc_map_pixelview_new)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview.c b/drivers/media/rc/keymaps/rc-pixelview.c
index a6020eea7b95..39e6feaa35a3 100644
--- a/drivers/media/rc/keymaps/rc-pixelview.c
+++ b/drivers/media/rc/keymaps/rc-pixelview.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -80,4 +80,4 @@ module_init(init_rc_map_pixelview)
module_exit(exit_rc_map_pixelview)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-powercolor-real-angel.c b/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
index e74c571a5e44..e96fa3ab9f4b 100644
--- a/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
+++ b/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -79,4 +79,4 @@ module_init(init_rc_map_powercolor_real_angel)
module_exit(exit_rc_map_powercolor_real_angel)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-proteus-2309.c b/drivers/media/rc/keymaps/rc-proteus-2309.c
index adee8035ce96..eef626ee02df 100644
--- a/drivers/media/rc/keymaps/rc-proteus-2309.c
+++ b/drivers/media/rc/keymaps/rc-proteus-2309.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -67,4 +67,4 @@ module_init(init_rc_map_proteus_2309)
module_exit(exit_rc_map_proteus_2309)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-purpletv.c b/drivers/media/rc/keymaps/rc-purpletv.c
index 722597a20e4a..cec6fe466829 100644
--- a/drivers/media/rc/keymaps/rc-purpletv.c
+++ b/drivers/media/rc/keymaps/rc-purpletv.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -79,4 +79,4 @@ module_init(init_rc_map_purpletv)
module_exit(exit_rc_map_purpletv)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pv951.c b/drivers/media/rc/keymaps/rc-pv951.c
index 0105d63c07a9..5ac89ce8c053 100644
--- a/drivers/media/rc/keymaps/rc-pv951.c
+++ b/drivers/media/rc/keymaps/rc-pv951.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -76,4 +76,4 @@ module_init(init_rc_map_pv951)
module_exit(exit_rc_map_pv951)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c b/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c
index 073694d50f49..9f778bd091db 100644
--- a/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c
+++ b/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -76,4 +76,4 @@ module_init(init_rc_map_real_audio_220_32_keys)
module_exit(exit_rc_map_real_audio_220_32_keys)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-tbs-nec.c b/drivers/media/rc/keymaps/rc-tbs-nec.c
index 5039be782bc5..24ce2a252502 100644
--- a/drivers/media/rc/keymaps/rc-tbs-nec.c
+++ b/drivers/media/rc/keymaps/rc-tbs-nec.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -73,4 +73,4 @@ module_init(init_rc_map_tbs_nec)
module_exit(exit_rc_map_tbs_nec)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c b/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c
index 53629fb0151f..97eb83ab5a35 100644
--- a/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c
+++ b/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -90,4 +90,4 @@ module_init(init_rc_map_terratec_cinergy_xs)
module_exit(exit_rc_map_terratec_cinergy_xs)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-tevii-nec.c b/drivers/media/rc/keymaps/rc-tevii-nec.c
index f2c3b75d8580..38e0c0875596 100644
--- a/drivers/media/rc/keymaps/rc-tevii-nec.c
+++ b/drivers/media/rc/keymaps/rc-tevii-nec.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -86,4 +86,4 @@ module_init(init_rc_map_tevii_nec)
module_exit(exit_rc_map_tevii_nec)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-tivo.c b/drivers/media/rc/keymaps/rc-tivo.c
index 454e06295692..5cc1b456e329 100644
--- a/drivers/media/rc/keymaps/rc-tivo.c
+++ b/drivers/media/rc/keymaps/rc-tivo.c
@@ -15,62 +15,62 @@
* Initial mapping is for the TiVo remote included in the Nero LiquidTV bundle,
* which also ships with a TiVo-branded IR transceiver, supported by the mceusb
* driver. Note that the remote uses an NEC-ish protocol, but instead of having
- * a command/not_command pair, it has a vendor ID of 0xa10c, but some keys, the
+ * a command/not_command pair, it has a vendor ID of 0x3085, but some keys, the
* NEC extended checksums do pass, so the table presently has the intended
* values and the checksum-passed versions for those keys.
*/
static struct rc_map_table tivo[] = {
- { 0xa10c900f, KEY_MEDIA }, /* TiVo Button */
- { 0xa10c0807, KEY_POWER2 }, /* TV Power */
- { 0xa10c8807, KEY_TV }, /* Live TV/Swap */
- { 0xa10c2c03, KEY_VIDEO_NEXT }, /* TV Input */
- { 0xa10cc807, KEY_INFO },
- { 0xa10cfa05, KEY_CYCLEWINDOWS }, /* Window */
+ { 0x3085f009, KEY_MEDIA }, /* TiVo Button */
+ { 0x3085e010, KEY_POWER2 }, /* TV Power */
+ { 0x3085e011, KEY_TV }, /* Live TV/Swap */
+ { 0x3085c034, KEY_VIDEO_NEXT }, /* TV Input */
+ { 0x3085e013, KEY_INFO },
+ { 0x3085a05f, KEY_CYCLEWINDOWS }, /* Window */
{ 0x0085305f, KEY_CYCLEWINDOWS },
- { 0xa10c6c03, KEY_EPG }, /* Guide */
+ { 0x3085c036, KEY_EPG }, /* Guide */
- { 0xa10c2807, KEY_UP },
- { 0xa10c6807, KEY_DOWN },
- { 0xa10ce807, KEY_LEFT },
- { 0xa10ca807, KEY_RIGHT },
+ { 0x3085e014, KEY_UP },
+ { 0x3085e016, KEY_DOWN },
+ { 0x3085e017, KEY_LEFT },
+ { 0x3085e015, KEY_RIGHT },
- { 0xa10c1807, KEY_SCROLLDOWN }, /* Red Thumbs Down */
- { 0xa10c9807, KEY_SELECT },
- { 0xa10c5807, KEY_SCROLLUP }, /* Green Thumbs Up */
+ { 0x3085e018, KEY_SCROLLDOWN }, /* Red Thumbs Down */
+ { 0x3085e019, KEY_SELECT },
+ { 0x3085e01a, KEY_SCROLLUP }, /* Green Thumbs Up */
- { 0xa10c3807, KEY_VOLUMEUP },
- { 0xa10cb807, KEY_VOLUMEDOWN },
- { 0xa10cd807, KEY_MUTE },
- { 0xa10c040b, KEY_RECORD },
- { 0xa10c7807, KEY_CHANNELUP },
- { 0xa10cf807, KEY_CHANNELDOWN },
+ { 0x3085e01c, KEY_VOLUMEUP },
+ { 0x3085e01d, KEY_VOLUMEDOWN },
+ { 0x3085e01b, KEY_MUTE },
+ { 0x3085d020, KEY_RECORD },
+ { 0x3085e01e, KEY_CHANNELUP },
+ { 0x3085e01f, KEY_CHANNELDOWN },
{ 0x0085301f, KEY_CHANNELDOWN },
- { 0xa10c840b, KEY_PLAY },
- { 0xa10cc40b, KEY_PAUSE },
- { 0xa10ca40b, KEY_SLOW },
- { 0xa10c440b, KEY_REWIND },
- { 0xa10c240b, KEY_FASTFORWARD },
- { 0xa10c640b, KEY_PREVIOUS },
- { 0xa10ce40b, KEY_NEXT }, /* ->| */
+ { 0x3085d021, KEY_PLAY },
+ { 0x3085d023, KEY_PAUSE },
+ { 0x3085d025, KEY_SLOW },
+ { 0x3085d022, KEY_REWIND },
+ { 0x3085d024, KEY_FASTFORWARD },
+ { 0x3085d026, KEY_PREVIOUS },
+ { 0x3085d027, KEY_NEXT }, /* ->| */
- { 0xa10c220d, KEY_ZOOM }, /* Aspect */
- { 0xa10c120d, KEY_STOP },
- { 0xa10c520d, KEY_DVD }, /* DVD Menu */
+ { 0x3085b044, KEY_ZOOM }, /* Aspect */
+ { 0x3085b048, KEY_STOP },
+ { 0x3085b04a, KEY_DVD }, /* DVD Menu */
- { 0xa10c140b, KEY_NUMERIC_1 },
- { 0xa10c940b, KEY_NUMERIC_2 },
- { 0xa10c540b, KEY_NUMERIC_3 },
- { 0xa10cd40b, KEY_NUMERIC_4 },
- { 0xa10c340b, KEY_NUMERIC_5 },
- { 0xa10cb40b, KEY_NUMERIC_6 },
- { 0xa10c740b, KEY_NUMERIC_7 },
- { 0xa10cf40b, KEY_NUMERIC_8 },
+ { 0x3085d028, KEY_NUMERIC_1 },
+ { 0x3085d029, KEY_NUMERIC_2 },
+ { 0x3085d02a, KEY_NUMERIC_3 },
+ { 0x3085d02b, KEY_NUMERIC_4 },
+ { 0x3085d02c, KEY_NUMERIC_5 },
+ { 0x3085d02d, KEY_NUMERIC_6 },
+ { 0x3085d02e, KEY_NUMERIC_7 },
+ { 0x3085d02f, KEY_NUMERIC_8 },
{ 0x0085302f, KEY_NUMERIC_8 },
- { 0xa10c0c03, KEY_NUMERIC_9 },
- { 0xa10c8c03, KEY_NUMERIC_0 },
- { 0xa10ccc03, KEY_ENTER },
- { 0xa10c4c03, KEY_CLEAR },
+ { 0x3085c030, KEY_NUMERIC_9 },
+ { 0x3085c031, KEY_NUMERIC_0 },
+ { 0x3085c033, KEY_ENTER },
+ { 0x3085c032, KEY_CLEAR },
};
static struct rc_map_list tivo_map = {
diff --git a/drivers/media/rc/keymaps/rc-tt-1500.c b/drivers/media/rc/keymaps/rc-tt-1500.c
index 80217ffc91db..c766d3b2b6b0 100644
--- a/drivers/media/rc/keymaps/rc-tt-1500.c
+++ b/drivers/media/rc/keymaps/rc-tt-1500.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -80,4 +80,4 @@ module_init(init_rc_map_tt_1500)
module_exit(exit_rc_map_tt_1500)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-videomate-s350.c b/drivers/media/rc/keymaps/rc-videomate-s350.c
index 8bfc3e8d909d..8a354775a2d8 100644
--- a/drivers/media/rc/keymaps/rc-videomate-s350.c
+++ b/drivers/media/rc/keymaps/rc-videomate-s350.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -83,4 +83,4 @@ module_init(init_rc_map_videomate_s350)
module_exit(exit_rc_map_videomate_s350)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c b/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c
index 390ce9431b35..eb0cda7766c4 100644
--- a/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c
+++ b/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -85,4 +85,4 @@ module_init(init_rc_map_videomate_tv_pvr)
module_exit(exit_rc_map_videomate_tv_pvr)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c b/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c
index 2852bf705064..c1dd598e828e 100644
--- a/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c
+++ b/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -80,4 +80,4 @@ module_init(init_rc_map_winfast_usbii_deluxe)
module_exit(exit_rc_map_winfast_usbii_deluxe)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-winfast.c b/drivers/media/rc/keymaps/rc-winfast.c
index 2df1cba23600..8a779da1e973 100644
--- a/drivers/media/rc/keymaps/rc-winfast.c
+++ b/drivers/media/rc/keymaps/rc-winfast.c
@@ -2,7 +2,7 @@
*
* keymap imported from ir-keymaps.c
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -100,4 +100,4 @@ module_init(init_rc_map_winfast)
module_exit(exit_rc_map_winfast)
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index a25bb1581e46..5d8f3d40d820 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -84,7 +84,7 @@
#define MCE_PORT_IR 0x4 /* (0x4 << 5) | MCE_CMD = 0x9f */
#define MCE_PORT_SYS 0x7 /* (0x7 << 5) | MCE_CMD = 0xff */
#define MCE_PORT_SER 0x6 /* 0xc0 thru 0xdf flush & 0x1f bytes */
-#define MCE_PORT_MASK 0xe0 /* Mask out command bits */
+#define MCE_PORT_MASK 0xe0 /* Mask out command bits */
/* Command port headers */
#define MCE_CMD_PORT_IR 0x9f /* IR-related cmd/rsp */
@@ -153,19 +153,6 @@
#define MCE_COMMAND_IRDATA 0x80
#define MCE_PACKET_LENGTH_MASK 0x1f /* Packet length mask */
-/* module parameters */
-#ifdef CONFIG_USB_DEBUG
-static bool debug = 1;
-#else
-static bool debug;
-#endif
-
-#define mce_dbg(dev, fmt, ...) \
- do { \
- if (debug) \
- dev_info(dev, fmt, ## __VA_ARGS__); \
- } while (0)
-
/* general constants */
#define SEND_FLAG_IN_PROGRESS 1
#define SEND_FLAG_COMPLETE 2
@@ -541,16 +528,13 @@ static int mceusb_cmd_datasize(u8 cmd, u8 subcmd)
static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
int offset, int len, bool out)
{
- char codes[USB_BUFLEN * 3 + 1];
- char inout[9];
+#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
+ char *inout;
u8 cmd, subcmd, data1, data2, data3, data4;
struct device *dev = ir->dev;
- int i, start, skip = 0;
+ int start, skip = 0;
u32 carrier, period;
- if (!debug)
- return;
-
/* skip meaningless 0xb1 0x60 header bytes on orig receiver */
if (ir->flags.microsoft_gen1 && !out && !offset)
skip = 2;
@@ -558,16 +542,10 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
if (len <= skip)
return;
- for (i = 0; i < len && i < USB_BUFLEN; i++)
- snprintf(codes + i * 3, 4, "%02x ", buf[i + offset] & 0xff);
-
- dev_info(dev, "%sx data: %s(length=%d)\n",
- (out ? "t" : "r"), codes, len);
+ dev_dbg(dev, "%cx data: %*ph (length=%d)",
+ (out ? 't' : 'r'), min(len, USB_BUFLEN), buf, len);
- if (out)
- strcpy(inout, "Request\0");
- else
- strcpy(inout, "Got\0");
+ inout = out ? "Request" : "Got";
start = offset + skip;
cmd = buf[start] & 0xff;
@@ -583,50 +561,50 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
break;
if ((subcmd == MCE_CMD_PORT_SYS) &&
(data1 == MCE_CMD_RESUME))
- dev_info(dev, "Device resume requested\n");
+ dev_dbg(dev, "Device resume requested");
else
- dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
+ dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
cmd, subcmd);
break;
case MCE_CMD_PORT_SYS:
switch (subcmd) {
case MCE_RSP_EQEMVER:
if (!out)
- dev_info(dev, "Emulator interface version %x\n",
+ dev_dbg(dev, "Emulator interface version %x",
data1);
break;
case MCE_CMD_G_REVISION:
if (len == 2)
- dev_info(dev, "Get hw/sw rev?\n");
+ dev_dbg(dev, "Get hw/sw rev?");
else
- dev_info(dev, "hw/sw rev 0x%02x 0x%02x "
- "0x%02x 0x%02x\n", data1, data2,
+ dev_dbg(dev, "hw/sw rev 0x%02x 0x%02x 0x%02x 0x%02x",
+ data1, data2,
buf[start + 4], buf[start + 5]);
break;
case MCE_CMD_RESUME:
- dev_info(dev, "Device resume requested\n");
+ dev_dbg(dev, "Device resume requested");
break;
case MCE_RSP_CMD_ILLEGAL:
- dev_info(dev, "Illegal PORT_SYS command\n");
+ dev_dbg(dev, "Illegal PORT_SYS command");
break;
case MCE_RSP_EQWAKEVERSION:
if (!out)
- dev_info(dev, "Wake version, proto: 0x%02x, "
+ dev_dbg(dev, "Wake version, proto: 0x%02x, "
"payload: 0x%02x, address: 0x%02x, "
- "version: 0x%02x\n",
+ "version: 0x%02x",
data1, data2, data3, data4);
break;
case MCE_RSP_GETPORTSTATUS:
if (!out)
/* We use data1 + 1 here, to match hw labels */
- dev_info(dev, "TX port %d: blaster is%s connected\n",
+ dev_dbg(dev, "TX port %d: blaster is%s connected",
data1 + 1, data4 ? " not" : "");
break;
case MCE_CMD_FLASHLED:
- dev_info(dev, "Attempting to flash LED\n");
+ dev_dbg(dev, "Attempting to flash LED");
break;
default:
- dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
+ dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
cmd, subcmd);
break;
}
@@ -634,13 +612,13 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
case MCE_CMD_PORT_IR:
switch (subcmd) {
case MCE_CMD_SIG_END:
- dev_info(dev, "End of signal\n");
+ dev_dbg(dev, "End of signal");
break;
case MCE_CMD_PING:
- dev_info(dev, "Ping\n");
+ dev_dbg(dev, "Ping");
break;
case MCE_CMD_UNKNOWN:
- dev_info(dev, "Resp to 9f 05 of 0x%02x 0x%02x\n",
+ dev_dbg(dev, "Resp to 9f 05 of 0x%02x 0x%02x",
data1, data2);
break;
case MCE_RSP_EQIRCFS:
@@ -649,51 +627,51 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
if (!period)
break;
carrier = (1000 * 1000) / period;
- dev_info(dev, "%s carrier of %u Hz (period %uus)\n",
+ dev_dbg(dev, "%s carrier of %u Hz (period %uus)",
inout, carrier, period);
break;
case MCE_CMD_GETIRCFS:
- dev_info(dev, "Get carrier mode and freq\n");
+ dev_dbg(dev, "Get carrier mode and freq");
break;
case MCE_RSP_EQIRTXPORTS:
- dev_info(dev, "%s transmit blaster mask of 0x%02x\n",
+ dev_dbg(dev, "%s transmit blaster mask of 0x%02x",
inout, data1);
break;
case MCE_RSP_EQIRTIMEOUT:
/* value is in units of 50us, so x*50/1000 ms */
period = ((data1 << 8) | data2) * MCE_TIME_UNIT / 1000;
- dev_info(dev, "%s receive timeout of %d ms\n",
+ dev_dbg(dev, "%s receive timeout of %d ms",
inout, period);
break;
case MCE_CMD_GETIRTIMEOUT:
- dev_info(dev, "Get receive timeout\n");
+ dev_dbg(dev, "Get receive timeout");
break;
case MCE_CMD_GETIRTXPORTS:
- dev_info(dev, "Get transmit blaster mask\n");
+ dev_dbg(dev, "Get transmit blaster mask");
break;
case MCE_RSP_EQIRRXPORTEN:
- dev_info(dev, "%s %s-range receive sensor in use\n",
+ dev_dbg(dev, "%s %s-range receive sensor in use",
inout, data1 == 0x02 ? "short" : "long");
break;
case MCE_CMD_GETIRRXPORTEN:
/* aka MCE_RSP_EQIRRXCFCNT */
if (out)
- dev_info(dev, "Get receive sensor\n");
+ dev_dbg(dev, "Get receive sensor");
else if (ir->learning_enabled)
- dev_info(dev, "RX pulse count: %d\n",
+ dev_dbg(dev, "RX pulse count: %d",
((data1 << 8) | data2));
break;
case MCE_RSP_EQIRNUMPORTS:
if (out)
break;
- dev_info(dev, "Num TX ports: %x, num RX ports: %x\n",
+ dev_dbg(dev, "Num TX ports: %x, num RX ports: %x",
data1, data2);
break;
case MCE_RSP_CMD_ILLEGAL:
- dev_info(dev, "Illegal PORT_IR command\n");
+ dev_dbg(dev, "Illegal PORT_IR command");
break;
default:
- dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
+ dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
cmd, subcmd);
break;
}
@@ -703,10 +681,11 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
}
if (cmd == MCE_IRDATA_TRAILER)
- dev_info(dev, "End of raw IR data\n");
+ dev_dbg(dev, "End of raw IR data");
else if ((cmd != MCE_CMD_PORT_IR) &&
((cmd & MCE_PORT_MASK) == MCE_COMMAND_IRDATA))
- dev_info(dev, "Raw IR data, %d pulse/space samples\n", ir->rem);
+ dev_dbg(dev, "Raw IR data, %d pulse/space samples", ir->rem);
+#endif
}
static void mce_async_callback(struct urb *urb)
@@ -718,10 +697,25 @@ static void mce_async_callback(struct urb *urb)
return;
ir = urb->context;
- if (ir) {
+
+ switch (urb->status) {
+ /* success */
+ case 0:
len = urb->actual_length;
mceusb_dev_printdata(ir, urb->transfer_buffer, 0, len, true);
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -EILSEQ:
+ case -ESHUTDOWN:
+ break;
+
+ case -EPIPE:
+ default:
+ dev_err(ir->dev, "Error: request urb status = %d", urb->status);
+ break;
}
/* the transfer buffer and urb were allocated in mce_request_packet */
@@ -770,17 +764,17 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
return;
}
- mce_dbg(dev, "receive request called (size=%#x)\n", size);
+ dev_dbg(dev, "receive request called (size=%#x)", size);
async_urb->transfer_buffer_length = size;
async_urb->dev = ir->usbdev;
res = usb_submit_urb(async_urb, GFP_ATOMIC);
if (res) {
- mce_dbg(dev, "receive request FAILED! (res=%d)\n", res);
+ dev_err(dev, "receive request FAILED! (res=%d)", res);
return;
}
- mce_dbg(dev, "receive request complete (res=%d)\n", res);
+ dev_dbg(dev, "receive request complete (res=%d)", res);
}
static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size)
@@ -895,8 +889,7 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier)
ir->carrier = carrier;
cmdbuf[2] = MCE_CMD_SIG_END;
cmdbuf[3] = MCE_IRDATA_TRAILER;
- mce_dbg(ir->dev, "%s: disabling carrier "
- "modulation\n", __func__);
+ dev_dbg(ir->dev, "disabling carrier modulation");
mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
return carrier;
}
@@ -907,8 +900,8 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier)
ir->carrier = carrier;
cmdbuf[2] = prescaler;
cmdbuf[3] = divisor;
- mce_dbg(ir->dev, "%s: requesting %u HZ "
- "carrier\n", __func__, carrier);
+ dev_dbg(ir->dev, "requesting %u HZ carrier",
+ carrier);
/* Transmit new carrier to mce device */
mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
@@ -998,7 +991,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK)
* US_TO_NS(MCE_TIME_UNIT);
- mce_dbg(ir->dev, "Storing %s with duration %d\n",
+ dev_dbg(ir->dev, "Storing %s with duration %d",
rawir.pulse ? "pulse" : "space",
rawir.duration);
@@ -1032,7 +1025,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
ir->parser_state = CMD_HEADER;
}
if (event) {
- mce_dbg(ir->dev, "processed IR data, calling ir_raw_event_handle\n");
+ dev_dbg(ir->dev, "processed IR data");
ir_raw_event_handle(ir->rc);
}
}
@@ -1055,7 +1048,7 @@ static void mceusb_dev_recv(struct urb *urb)
if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
ir->send_flags = SEND_FLAG_COMPLETE;
- mce_dbg(ir->dev, "setup answer received %d bytes\n",
+ dev_dbg(ir->dev, "setup answer received %d bytes\n",
buf_len);
}
@@ -1067,13 +1060,14 @@ static void mceusb_dev_recv(struct urb *urb)
case -ECONNRESET:
case -ENOENT:
+ case -EILSEQ:
case -ESHUTDOWN:
usb_unlink_urb(urb);
return;
case -EPIPE:
default:
- mce_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
+ dev_err(ir->dev, "Error: urb status = %d", urb->status);
break;
}
@@ -1095,7 +1089,7 @@ static void mceusb_gen1_init(struct mceusb_dev *ir)
data = kzalloc(USB_CTRL_MSG_SZ, GFP_KERNEL);
if (!data) {
- dev_err(dev, "%s: memory allocation failed!\n", __func__);
+ dev_err(dev, "%s: memory allocation failed!", __func__);
return;
}
@@ -1106,28 +1100,28 @@ static void mceusb_gen1_init(struct mceusb_dev *ir)
ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0,
data, USB_CTRL_MSG_SZ, HZ * 3);
- mce_dbg(dev, "%s - ret = %d\n", __func__, ret);
- mce_dbg(dev, "%s - data[0] = %d, data[1] = %d\n",
- __func__, data[0], data[1]);
+ dev_dbg(dev, "set address - ret = %d", ret);
+ dev_dbg(dev, "set address - data[0] = %d, data[1] = %d",
+ data[0], data[1]);
/* set feature: bit rate 38400 bps */
ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
0xc04e, 0x0000, NULL, 0, HZ * 3);
- mce_dbg(dev, "%s - ret = %d\n", __func__, ret);
+ dev_dbg(dev, "set feature - ret = %d", ret);
/* bRequest 4: set char length to 8 bits */
ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
4, USB_TYPE_VENDOR,
0x0808, 0x0000, NULL, 0, HZ * 3);
- mce_dbg(dev, "%s - retB = %d\n", __func__, ret);
+ dev_dbg(dev, "set char length - retB = %d", ret);
/* bRequest 2: set handshaking to use DTR/DSR */
ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
2, USB_TYPE_VENDOR,
0x0000, 0x0100, NULL, 0, HZ * 3);
- mce_dbg(dev, "%s - retC = %d\n", __func__, ret);
+ dev_dbg(dev, "set handshake - retC = %d", ret);
/* device resume */
mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME));
@@ -1198,7 +1192,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
rc = rc_allocate_device();
if (!rc) {
- dev_err(dev, "remote dev allocation failed\n");
+ dev_err(dev, "remote dev allocation failed");
goto out;
}
@@ -1217,7 +1211,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
rc->dev.parent = dev;
rc->priv = ir;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rc, RC_BIT_ALL);
rc->timeout = MS_TO_NS(100);
if (!ir->flags.no_tx) {
rc->s_tx_mask = mceusb_set_tx_mask;
@@ -1230,7 +1224,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
ret = rc_register_device(rc);
if (ret < 0) {
- dev_err(dev, "remote dev registration failed\n");
+ dev_err(dev, "remote dev registration failed");
goto out;
}
@@ -1258,7 +1252,7 @@ static int mceusb_dev_probe(struct usb_interface *intf,
bool tx_mask_normal;
int ir_intfnum;
- mce_dbg(&intf->dev, "%s called\n", __func__);
+ dev_dbg(&intf->dev, "%s called", __func__);
idesc = intf->cur_altsetting;
@@ -1286,8 +1280,7 @@ static int mceusb_dev_probe(struct usb_interface *intf,
ep_in = ep;
ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
ep_in->bInterval = 1;
- mce_dbg(&intf->dev, "acceptable inbound endpoint "
- "found\n");
+ dev_dbg(&intf->dev, "acceptable inbound endpoint found");
}
if ((ep_out == NULL)
@@ -1301,12 +1294,11 @@ static int mceusb_dev_probe(struct usb_interface *intf,
ep_out = ep;
ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
ep_out->bInterval = 1;
- mce_dbg(&intf->dev, "acceptable outbound endpoint "
- "found\n");
+ dev_dbg(&intf->dev, "acceptable outbound endpoint found");
}
}
if (ep_in == NULL) {
- mce_dbg(&intf->dev, "inbound and/or endpoint not found\n");
+ dev_dbg(&intf->dev, "inbound and/or endpoint not found");
return -ENODEV;
}
@@ -1357,7 +1349,7 @@ static int mceusb_dev_probe(struct usb_interface *intf,
ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* flush buffers on the device */
- mce_dbg(&intf->dev, "Flushing receive buffers\n");
+ dev_dbg(&intf->dev, "Flushing receive buffers\n");
mce_flush_rx_buffer(ir, maxp);
/* figure out which firmware/emulator version this hardware has */
@@ -1382,10 +1374,9 @@ static int mceusb_dev_probe(struct usb_interface *intf,
device_set_wakeup_capable(ir->dev, true);
device_set_wakeup_enable(ir->dev, true);
- dev_info(&intf->dev, "Registered %s with mce emulator interface "
- "version %x\n", name, ir->emver);
- dev_info(&intf->dev, "%x tx ports (0x%x cabled) and "
- "%x rx sensors (0x%x active)\n",
+ dev_info(&intf->dev, "Registered %s with mce emulator interface version %x",
+ name, ir->emver);
+ dev_info(&intf->dev, "%x tx ports (0x%x cabled) and %x rx sensors (0x%x active)",
ir->num_txports, ir->txports_cabled,
ir->num_rxports, ir->rxports_active);
@@ -1399,7 +1390,7 @@ urb_in_alloc_fail:
buf_in_alloc_fail:
kfree(ir);
mem_alloc_fail:
- dev_err(&intf->dev, "%s: device setup failed!\n", __func__);
+ dev_err(&intf->dev, "%s: device setup failed!", __func__);
return -ENOMEM;
}
@@ -1427,7 +1418,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf)
static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message)
{
struct mceusb_dev *ir = usb_get_intfdata(intf);
- dev_info(ir->dev, "suspend\n");
+ dev_info(ir->dev, "suspend");
usb_kill_urb(ir->urb_in);
return 0;
}
@@ -1435,7 +1426,7 @@ static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message)
static int mceusb_dev_resume(struct usb_interface *intf)
{
struct mceusb_dev *ir = usb_get_intfdata(intf);
- dev_info(ir->dev, "resume\n");
+ dev_info(ir->dev, "resume");
if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
return -EIO;
return 0;
@@ -1457,6 +1448,3 @@ MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(usb, mceusb_dev_table);
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 21ee0dc1b7ec..d244e1a83f43 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -330,9 +330,6 @@ static void nvt_cir_wake_ldev_init(struct nvt_dev *nvt)
/* Enable CIR Wake via PSOUT# (Pin60) */
nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE);
- /* enable cir interrupt of mouse/keyboard IRQ event */
- nvt_set_reg_bit(nvt, CIR_INTR_MOUSE_IRQ_BIT, CR_ACPI_IRQ_EVENTS);
-
/* enable pme interrupt of cir wakeup event */
nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2);
@@ -456,7 +453,6 @@ static void nvt_enable_wake(struct nvt_dev *nvt)
nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI);
nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE);
- nvt_set_reg_bit(nvt, CIR_INTR_MOUSE_IRQ_BIT, CR_ACPI_IRQ_EVENTS);
nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2);
nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
@@ -989,6 +985,12 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
goto exit_free_dev_rdev;
ret = -ENODEV;
+ /* activate pnp device */
+ if (pnp_activate_dev(pdev) < 0) {
+ dev_err(&pdev->dev, "Could not activate PNP device!\n");
+ goto exit_free_dev_rdev;
+ }
+
/* validate pnp resources */
if (!pnp_port_valid(pdev, 0) ||
pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) {
@@ -1042,7 +1044,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
/* Set up the rc device */
rdev->priv = nvt;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
rdev->open = nvt_open;
rdev->close = nvt_close;
rdev->tx_ir = nvt_tx_ir;
diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h
index 07e83108df0f..e1cf23c3875b 100644
--- a/drivers/media/rc/nuvoton-cir.h
+++ b/drivers/media/rc/nuvoton-cir.h
@@ -363,7 +363,6 @@ struct nvt_dev {
#define LOGICAL_DEV_ENABLE 0x01
#define CIR_WAKE_ENABLE_BIT 0x08
-#define CIR_INTR_MOUSE_IRQ_BIT 0x80
#define PME_INTR_CIR_PASS_BIT 0x08
/* w83677hg CIR pin config */
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index 70a180bb0bd0..da536c93c978 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -1,7 +1,7 @@
/*
* Remote Controller core raw events header
*
- * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -88,6 +88,12 @@ struct ir_raw_event_ctrl {
unsigned count;
u64 bits;
} sanyo;
+ struct sharp_dec {
+ int state;
+ unsigned count;
+ u32 bits;
+ unsigned int pulse_len;
+ } sharp;
struct mce_kbd_dec {
struct input_dev *idev;
struct timer_list rx_timeout;
@@ -204,6 +210,13 @@ static inline void load_sony_decode(void) { }
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")
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index 53d02827a472..0a88e0cf964f 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -195,7 +195,7 @@ static int __init loop_init(void)
rc->map_name = RC_MAP_EMPTY;
rc->priv = &loopdev;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rc, RC_BIT_ALL);
rc->timeout = 100 * 1000 * 1000; /* 100 ms */
rc->min_timeout = 1;
rc->max_timeout = UINT_MAX;
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 02e2f38c9c85..99697aae92ff 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -1,6 +1,6 @@
/* rc-main.c - Remote Controller core module
*
- * Copyright (C) 2009-2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2009-2010 by Mauro Carvalho Chehab
*
* 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
@@ -24,7 +24,7 @@
/* Bitmap to store allocated device numbers from 0 to IRRCV_NUM_DEVICES - 1 */
#define IRRCV_NUM_DEVICES 256
-DECLARE_BITMAP(ir_core_dev_number, IRRCV_NUM_DEVICES);
+static DECLARE_BITMAP(ir_core_dev_number, IRRCV_NUM_DEVICES);
/* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */
#define IR_TAB_MIN_SIZE 256
@@ -62,7 +62,7 @@ struct rc_map *rc_map_get(const char *name)
map = seek_rc_map(name);
#ifdef MODULE
if (!map) {
- int rc = request_module(name);
+ int rc = request_module("%s", name);
if (rc < 0) {
printk(KERN_ERR "Couldn't load IR keymap %s\n", name);
return NULL;
@@ -633,6 +633,7 @@ EXPORT_SYMBOL_GPL(rc_repeat);
static void ir_do_keydown(struct rc_dev *dev, int scancode,
u32 keycode, u8 toggle)
{
+ struct rc_scancode_filter *filter;
bool new_event = !dev->keypressed ||
dev->last_scancode != scancode ||
dev->last_toggle != toggle;
@@ -640,6 +641,11 @@ static void ir_do_keydown(struct rc_dev *dev, int scancode,
if (new_event && dev->keypressed)
ir_do_keyup(dev, false);
+ /* Generic scancode filtering */
+ filter = &dev->scancode_filters[RC_FILTER_NORMAL];
+ if (filter->mask && ((scancode ^ filter->data) & filter->mask))
+ return;
+
input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode);
if (new_event && keycode != KEY_RESERVED) {
@@ -653,9 +659,10 @@ static void ir_do_keydown(struct rc_dev *dev, int scancode,
"key 0x%04x, scancode 0x%04x\n",
dev->input_name, keycode, scancode);
input_report_key(dev->input_dev, keycode, 1);
+
+ led_trigger_event(led_feedback, LED_FULL);
}
- led_trigger_event(led_feedback, LED_FULL);
input_sync(dev->input_dev);
}
@@ -790,18 +797,44 @@ static struct {
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_LIRC, "lirc" },
};
/**
- * show_protocols() - shows the current IR protocol(s)
+ * struct rc_filter_attribute - Device attribute relating to a filter type.
+ * @attr: Device attribute.
+ * @type: Filter type.
+ * @mask: false for filter value, true for filter mask.
+ */
+struct rc_filter_attribute {
+ struct device_attribute attr;
+ enum rc_filter_type type;
+ bool mask;
+};
+#define to_rc_filter_attr(a) container_of(a, struct rc_filter_attribute, attr)
+
+#define RC_PROTO_ATTR(_name, _mode, _show, _store, _type) \
+ struct rc_filter_attribute dev_attr_##_name = { \
+ .attr = __ATTR(_name, _mode, _show, _store), \
+ .type = (_type), \
+ }
+#define RC_FILTER_ATTR(_name, _mode, _show, _store, _type, _mask) \
+ struct rc_filter_attribute dev_attr_##_name = { \
+ .attr = __ATTR(_name, _mode, _show, _store), \
+ .type = (_type), \
+ .mask = (_mask), \
+ }
+
+/**
+ * show_protocols() - shows the current/wakeup IR protocol(s)
* @device: the device descriptor
* @mattr: the device attribute struct (unused)
* @buf: a pointer to the output buffer
*
* This routine is a callback routine for input read the IR protocol type(s).
- * it is trigged by reading /sys/class/rc/rc?/protocols.
+ * it is trigged by reading /sys/class/rc/rc?/[wakeup_]protocols.
* It returns the protocol names of supported protocols.
* Enabled protocols are printed in brackets.
*
@@ -812,6 +845,7 @@ static ssize_t show_protocols(struct device *device,
struct device_attribute *mattr, char *buf)
{
struct rc_dev *dev = to_rc_dev(device);
+ struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr);
u64 allowed, enabled;
char *tmp = buf;
int i;
@@ -822,9 +856,10 @@ static ssize_t show_protocols(struct device *device,
mutex_lock(&dev->lock);
- enabled = dev->enabled_protocols;
- if (dev->driver_type == RC_DRIVER_SCANCODE)
- allowed = dev->allowed_protos;
+ enabled = dev->enabled_protocols[fattr->type];
+ if (dev->driver_type == RC_DRIVER_SCANCODE ||
+ fattr->type == RC_FILTER_WAKEUP)
+ allowed = dev->allowed_protocols[fattr->type];
else if (dev->raw)
allowed = ir_raw_get_allowed_protocols();
else {
@@ -856,14 +891,14 @@ static ssize_t show_protocols(struct device *device,
}
/**
- * store_protocols() - changes the current IR protocol(s)
+ * store_protocols() - changes the current/wakeup IR protocol(s)
* @device: the device descriptor
* @mattr: the device attribute struct (unused)
* @buf: a pointer to the input buffer
* @len: length of the input buffer
*
* This routine is for changing the IR protocol type.
- * It is trigged by writing to /sys/class/rc/rc?/protocols.
+ * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]protocols.
* Writing "+proto" will add a protocol to the list of enabled protocols.
* Writing "-proto" will remove a protocol from the list of enabled protocols.
* Writing "proto" will enable only "proto".
@@ -880,12 +915,15 @@ static ssize_t store_protocols(struct device *device,
size_t len)
{
struct rc_dev *dev = to_rc_dev(device);
+ struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr);
bool enable, disable;
const char *tmp;
- u64 type;
+ u64 old_type, type;
u64 mask;
int rc, i, count = 0;
ssize_t ret;
+ int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
+ struct rc_scancode_filter local_filter, *filter;
/* Device is being removed */
if (!dev)
@@ -898,7 +936,8 @@ static ssize_t store_protocols(struct device *device,
ret = -EINVAL;
goto out;
}
- type = dev->enabled_protocols;
+ old_type = dev->enabled_protocols[fattr->type];
+ type = old_type;
while ((tmp = strsep((char **) &data, " \n")) != NULL) {
if (!*tmp)
@@ -946,8 +985,10 @@ static ssize_t store_protocols(struct device *device,
goto out;
}
- if (dev->change_protocol) {
- rc = dev->change_protocol(dev, &type);
+ change_protocol = (fattr->type == RC_FILTER_NORMAL)
+ ? dev->change_protocol : dev->change_wakeup_protocol;
+ if (change_protocol) {
+ rc = change_protocol(dev, &type);
if (rc < 0) {
IR_dprintk(1, "Error setting protocols to 0x%llx\n",
(long long)type);
@@ -956,10 +997,40 @@ static ssize_t store_protocols(struct device *device,
}
}
- dev->enabled_protocols = type;
+ dev->enabled_protocols[fattr->type] = type;
IR_dprintk(1, "Current protocol(s): 0x%llx\n",
(long long)type);
+ /*
+ * If the protocol is changed the filter needs updating.
+ * Try setting the same filter with the new protocol (if any).
+ * Fall back to clearing the filter.
+ */
+ filter = &dev->scancode_filters[fattr->type];
+ if (old_type != type && filter->mask) {
+ local_filter = *filter;
+ if (!type) {
+ /* no protocol => clear filter */
+ ret = -1;
+ } else if (!dev->s_filter) {
+ /* generic filtering => accept any filter */
+ ret = 0;
+ } else {
+ /* hardware filtering => try setting, otherwise clear */
+ ret = dev->s_filter(dev, fattr->type, &local_filter);
+ }
+ if (ret < 0) {
+ /* clear the filter */
+ local_filter.data = 0;
+ local_filter.mask = 0;
+ if (dev->s_filter)
+ dev->s_filter(dev, fattr->type, &local_filter);
+ }
+
+ /* commit the new filter */
+ *filter = local_filter;
+ }
+
ret = len;
out:
@@ -967,6 +1038,115 @@ out:
return ret;
}
+/**
+ * show_filter() - shows the current scancode filter value or mask
+ * @device: the device descriptor
+ * @attr: the device attribute struct
+ * @buf: a pointer to the output buffer
+ *
+ * This routine is a callback routine to read a scancode filter value or mask.
+ * It is trigged by reading /sys/class/rc/rc?/[wakeup_]filter[_mask].
+ * It prints the current scancode filter value or mask of the appropriate filter
+ * type in hexadecimal into @buf and returns the size of the buffer.
+ *
+ * Bits of the filter value corresponding to set bits in the filter mask are
+ * compared against input scancodes and non-matching scancodes are discarded.
+ *
+ * dev->lock is taken to guard against races between device registration,
+ * store_filter and show_filter.
+ */
+static ssize_t show_filter(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rc_dev *dev = to_rc_dev(device);
+ struct rc_filter_attribute *fattr = to_rc_filter_attr(attr);
+ u32 val;
+
+ /* Device is being removed */
+ if (!dev)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+ if (fattr->mask)
+ val = dev->scancode_filters[fattr->type].mask;
+ else
+ val = dev->scancode_filters[fattr->type].data;
+ mutex_unlock(&dev->lock);
+
+ return sprintf(buf, "%#x\n", val);
+}
+
+/**
+ * store_filter() - changes the scancode filter value
+ * @device: the device descriptor
+ * @attr: the device attribute struct
+ * @buf: a pointer to the input buffer
+ * @len: length of the input buffer
+ *
+ * This routine is for changing a scancode filter value or mask.
+ * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]filter[_mask].
+ * Returns -EINVAL if an invalid filter value for the current protocol was
+ * specified or if scancode filtering is not supported by the driver, otherwise
+ * returns @len.
+ *
+ * Bits of the filter value corresponding to set bits in the filter mask are
+ * compared against input scancodes and non-matching scancodes are discarded.
+ *
+ * dev->lock is taken to guard against races between device registration,
+ * store_filter and show_filter.
+ */
+static ssize_t store_filter(struct device *device,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rc_dev *dev = to_rc_dev(device);
+ struct rc_filter_attribute *fattr = to_rc_filter_attr(attr);
+ struct rc_scancode_filter local_filter, *filter;
+ int ret;
+ unsigned long val;
+
+ /* Device is being removed */
+ if (!dev)
+ return -EINVAL;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ /* Scancode filter not supported (but still accept 0) */
+ if (!dev->s_filter && fattr->type != RC_FILTER_NORMAL)
+ return val ? -EINVAL : count;
+
+ mutex_lock(&dev->lock);
+
+ /* Tell the driver about the new filter */
+ filter = &dev->scancode_filters[fattr->type];
+ local_filter = *filter;
+ if (fattr->mask)
+ local_filter.mask = val;
+ else
+ local_filter.data = val;
+ if (!dev->enabled_protocols[fattr->type] && local_filter.mask) {
+ /* refuse to set a filter unless a protocol is enabled */
+ ret = -EINVAL;
+ goto unlock;
+ }
+ if (dev->s_filter) {
+ ret = dev->s_filter(dev, fattr->type, &local_filter);
+ if (ret < 0)
+ goto unlock;
+ }
+
+ /* Success, commit the new filter */
+ *filter = local_filter;
+
+unlock:
+ mutex_unlock(&dev->lock);
+ return (ret < 0) ? ret : count;
+}
+
static void rc_dev_release(struct device *device)
{
}
@@ -996,11 +1176,26 @@ static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
/*
* Static device attribute struct with the sysfs attributes for IR's
*/
-static DEVICE_ATTR(protocols, S_IRUGO | S_IWUSR,
- show_protocols, store_protocols);
+static RC_PROTO_ATTR(protocols, S_IRUGO | S_IWUSR,
+ show_protocols, store_protocols, RC_FILTER_NORMAL);
+static RC_PROTO_ATTR(wakeup_protocols, S_IRUGO | S_IWUSR,
+ show_protocols, store_protocols, RC_FILTER_WAKEUP);
+static RC_FILTER_ATTR(filter, S_IRUGO|S_IWUSR,
+ show_filter, store_filter, RC_FILTER_NORMAL, false);
+static RC_FILTER_ATTR(filter_mask, S_IRUGO|S_IWUSR,
+ show_filter, store_filter, RC_FILTER_NORMAL, true);
+static RC_FILTER_ATTR(wakeup_filter, S_IRUGO|S_IWUSR,
+ show_filter, store_filter, RC_FILTER_WAKEUP, false);
+static RC_FILTER_ATTR(wakeup_filter_mask, S_IRUGO|S_IWUSR,
+ show_filter, store_filter, RC_FILTER_WAKEUP, true);
static struct attribute *rc_dev_attrs[] = {
- &dev_attr_protocols.attr,
+ &dev_attr_protocols.attr.attr,
+ &dev_attr_wakeup_protocols.attr.attr,
+ &dev_attr_filter.attr.attr,
+ &dev_attr_filter_mask.attr.attr,
+ &dev_attr_wakeup_filter.attr.attr,
+ &dev_attr_wakeup_filter_mask.attr.attr,
NULL,
};
@@ -1091,14 +1286,6 @@ int rc_register_device(struct rc_dev *dev)
if (dev->close)
dev->input_dev->close = ir_close;
- /*
- * Take the lock here, as the device sysfs node will appear
- * when device_add() is called, which may trigger an ir-keytable udev
- * rule, which will in turn call show_protocols and access
- * dev->enabled_protocols before it has been initialized.
- */
- mutex_lock(&dev->lock);
-
do {
devno = find_first_zero_bit(ir_core_dev_number,
IRRCV_NUM_DEVICES);
@@ -1107,6 +1294,14 @@ int rc_register_device(struct rc_dev *dev)
return -ENOMEM;
} while (test_and_set_bit(devno, ir_core_dev_number));
+ /*
+ * Take the lock here, as the device sysfs node will appear
+ * when device_add() is called, which may trigger an ir-keytable udev
+ * rule, which will in turn call show_protocols and access
+ * dev->enabled_protocols before it has been initialized.
+ */
+ mutex_lock(&dev->lock);
+
dev->devno = devno;
dev_set_name(&dev->dev, "rc%ld", dev->devno);
dev_set_drvdata(&dev->dev, dev);
@@ -1172,7 +1367,7 @@ int rc_register_device(struct rc_dev *dev)
rc = dev->change_protocol(dev, &rc_type);
if (rc < 0)
goto out_raw;
- dev->enabled_protocols = rc_type;
+ dev->enabled_protocols[RC_FILTER_NORMAL] = rc_type;
}
mutex_unlock(&dev->lock);
@@ -1260,5 +1455,5 @@ int rc_core_debug; /* ir_debug level (0,1,2) */
EXPORT_SYMBOL_GPL(rc_core_debug);
module_param_named(debug, rc_core_debug, int, 0644);
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index a5d4f883d053..47cd373e2295 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -922,7 +922,7 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3)
rc->dev.parent = dev;
rc->priv = rr3;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rc, RC_BIT_ALL);
rc->timeout = US_TO_NS(2750);
rc->tx_ir = redrat3_transmit_ir;
rc->s_tx_carrier = redrat3_set_tx_carrier;
diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
index 8f0cddb9e8f2..22e4c1f28ab4 100644
--- a/drivers/media/rc/st_rc.c
+++ b/drivers/media/rc/st_rc.c
@@ -287,7 +287,7 @@ static int st_rc_probe(struct platform_device *pdev)
st_rc_hardware_init(rc_dev);
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
/* rx sampling rate is 10Mhz */
rdev->rx_resolution = 100;
rdev->timeout = US_TO_NS(MAX_SYMB_TIME);
diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
index d7b11e6a9982..f4e0bc3d382c 100644
--- a/drivers/media/rc/streamzap.c
+++ b/drivers/media/rc/streamzap.c
@@ -322,7 +322,7 @@ static struct rc_dev *streamzap_init_rc_dev(struct streamzap_ir *sz)
rdev->dev.parent = dev;
rdev->priv = sz;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rdev, RC_BIT_ALL);
rdev->driver_name = DRIVER_NAME;
rdev->map_name = RC_MAP_STREAMZAP;
diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
index d8de2056a4f6..c5be38e2a2fe 100644
--- a/drivers/media/rc/ttusbir.c
+++ b/drivers/media/rc/ttusbir.c
@@ -318,7 +318,7 @@ static int ttusbir_probe(struct usb_interface *intf,
usb_to_input_id(tt->udev, &rc->input_id);
rc->dev.parent = &intf->dev;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(rc, RC_BIT_ALL);
rc->priv = tt;
rc->driver_name = DRIVER_NAME;
rc->map_name = RC_MAP_TT_1500;
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 904baf4eec28..a8b981f5ce2e 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -1082,7 +1082,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
data->dev->dev.parent = &device->dev;
data->dev->timeout = MS_TO_NS(100);
data->dev->rx_resolution = US_TO_NS(2);
- data->dev->allowed_protos = RC_BIT_ALL;
+ rc_set_allowed_protocols(data->dev, RC_BIT_ALL);
err = rc_register_device(data->dev);
if (err)
diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig
index ba2e365296cf..a1284889cd15 100644
--- a/drivers/media/tuners/Kconfig
+++ b/drivers/media/tuners/Kconfig
@@ -204,6 +204,7 @@ config MEDIA_TUNER_TDA18212
config MEDIA_TUNER_E4000
tristate "Elonics E4000 silicon tuner"
depends on MEDIA_SUPPORT && I2C
+ select REGMAP_I2C
default m if !MEDIA_SUBDRV_AUTOSELECT
help
Elonics E4000 silicon tuner driver.
diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
index 40c1da707d15..90d93348f20c 100644
--- a/drivers/media/tuners/e4000.c
+++ b/drivers/media/tuners/e4000.c
@@ -21,220 +21,113 @@
#include "e4000_priv.h"
#include <linux/math64.h>
-/* Max transfer size done by I2C transfer functions */
-#define MAX_XFER_SIZE 64
-
-/* write multiple registers */
-static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
-{
- int ret;
- u8 buf[MAX_XFER_SIZE];
- struct i2c_msg msg[1] = {
- {
- .addr = priv->cfg->i2c_addr,
- .flags = 0,
- .len = 1 + len,
- .buf = buf,
- }
- };
-
- if (1 + len > sizeof(buf)) {
- dev_warn(&priv->i2c->dev,
- "%s: i2c wr reg=%04x: len=%d is too big!\n",
- KBUILD_MODNAME, reg, len);
- return -EINVAL;
- }
-
- buf[0] = reg;
- memcpy(&buf[1], val, len);
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- dev_warn(&priv->i2c->dev,
- "%s: i2c wr failed=%d reg=%02x len=%d\n",
- KBUILD_MODNAME, ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* read multiple registers */
-static int e4000_rd_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
-{
- int ret;
- u8 buf[MAX_XFER_SIZE];
- struct i2c_msg msg[2] = {
- {
- .addr = priv->cfg->i2c_addr,
- .flags = 0,
- .len = 1,
- .buf = &reg,
- }, {
- .addr = priv->cfg->i2c_addr,
- .flags = I2C_M_RD,
- .len = len,
- .buf = buf,
- }
- };
-
- if (len > sizeof(buf)) {
- dev_warn(&priv->i2c->dev,
- "%s: i2c rd reg=%04x: len=%d is too big!\n",
- KBUILD_MODNAME, reg, len);
- return -EINVAL;
- }
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret == 2) {
- memcpy(val, buf, len);
- ret = 0;
- } else {
- dev_warn(&priv->i2c->dev,
- "%s: i2c rd failed=%d reg=%02x len=%d\n",
- KBUILD_MODNAME, ret, reg, len);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-/* write single register */
-static int e4000_wr_reg(struct e4000_priv *priv, u8 reg, u8 val)
-{
- return e4000_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-static int e4000_rd_reg(struct e4000_priv *priv, u8 reg, u8 *val)
-{
- return e4000_rd_regs(priv, reg, val, 1);
-}
-
static int e4000_init(struct dvb_frontend *fe)
{
- struct e4000_priv *priv = fe->tuner_priv;
+ struct e4000 *s = fe->tuner_priv;
int ret;
- dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
+ dev_dbg(&s->client->dev, "%s:\n", __func__);
/* dummy I2C to ensure I2C wakes up */
- ret = e4000_wr_reg(priv, 0x02, 0x40);
+ ret = regmap_write(s->regmap, 0x02, 0x40);
/* reset */
- ret = e4000_wr_reg(priv, 0x00, 0x01);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x00, 0x01);
+ if (ret)
goto err;
/* disable output clock */
- ret = e4000_wr_reg(priv, 0x06, 0x00);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x06, 0x00);
+ if (ret)
goto err;
- ret = e4000_wr_reg(priv, 0x7a, 0x96);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x7a, 0x96);
+ if (ret)
goto err;
/* configure gains */
- ret = e4000_wr_regs(priv, 0x7e, "\x01\xfe", 2);
- if (ret < 0)
+ ret = regmap_bulk_write(s->regmap, 0x7e, "\x01\xfe", 2);
+ if (ret)
goto err;
- ret = e4000_wr_reg(priv, 0x82, 0x00);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x82, 0x00);
+ if (ret)
goto err;
- ret = e4000_wr_reg(priv, 0x24, 0x05);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x24, 0x05);
+ if (ret)
goto err;
- ret = e4000_wr_regs(priv, 0x87, "\x20\x01", 2);
- if (ret < 0)
+ ret = regmap_bulk_write(s->regmap, 0x87, "\x20\x01", 2);
+ if (ret)
goto err;
- ret = e4000_wr_regs(priv, 0x9f, "\x7f\x07", 2);
- if (ret < 0)
+ ret = regmap_bulk_write(s->regmap, 0x9f, "\x7f\x07", 2);
+ if (ret)
goto err;
/* DC offset control */
- ret = e4000_wr_reg(priv, 0x2d, 0x1f);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x2d, 0x1f);
+ if (ret)
goto err;
- ret = e4000_wr_regs(priv, 0x70, "\x01\x01", 2);
- if (ret < 0)
+ ret = regmap_bulk_write(s->regmap, 0x70, "\x01\x01", 2);
+ if (ret)
goto err;
/* gain control */
- ret = e4000_wr_reg(priv, 0x1a, 0x17);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x1a, 0x17);
+ if (ret)
goto err;
- ret = e4000_wr_reg(priv, 0x1f, 0x1a);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x1f, 0x1a);
+ if (ret)
goto err;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
-
- return 0;
+ s->active = true;
err:
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
+ if (ret)
+ dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
static int e4000_sleep(struct dvb_frontend *fe)
{
- struct e4000_priv *priv = fe->tuner_priv;
+ struct e4000 *s = fe->tuner_priv;
int ret;
- dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+ dev_dbg(&s->client->dev, "%s:\n", __func__);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
+ s->active = false;
- ret = e4000_wr_reg(priv, 0x00, 0x00);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x00, 0x00);
+ if (ret)
goto err;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
-
- return 0;
err:
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
+ if (ret)
+ dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
static int e4000_set_params(struct dvb_frontend *fe)
{
- struct e4000_priv *priv = fe->tuner_priv;
+ struct e4000 *s = fe->tuner_priv;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret, i, sigma_delta;
- unsigned int f_vco;
+ unsigned int pll_n, pll_f;
+ u64 f_vco;
u8 buf[5], i_data[4], q_data[4];
- dev_dbg(&priv->i2c->dev,
- "%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n",
+ dev_dbg(&s->client->dev,
+ "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n",
__func__, c->delivery_system, c->frequency,
c->bandwidth_hz);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
/* gain control manual */
- ret = e4000_wr_reg(priv, 0x1a, 0x00);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x1a, 0x00);
+ if (ret)
goto err;
/* PLL */
@@ -248,23 +141,21 @@ static int e4000_set_params(struct dvb_frontend *fe)
goto err;
}
- /*
- * Note: Currently f_vco overflows when c->frequency is 1 073 741 824 Hz
- * or more.
- */
- f_vco = c->frequency * e4000_pll_lut[i].mul;
- sigma_delta = div_u64(0x10000ULL * (f_vco % priv->cfg->clock), priv->cfg->clock);
- buf[0] = f_vco / priv->cfg->clock;
+ f_vco = 1ull * c->frequency * e4000_pll_lut[i].mul;
+ pll_n = div_u64_rem(f_vco, s->clock, &pll_f);
+ sigma_delta = div_u64(0x10000ULL * pll_f, s->clock);
+ buf[0] = pll_n;
buf[1] = (sigma_delta >> 0) & 0xff;
buf[2] = (sigma_delta >> 8) & 0xff;
buf[3] = 0x00;
buf[4] = e4000_pll_lut[i].div;
- dev_dbg(&priv->i2c->dev, "%s: f_vco=%u pll div=%d sigma_delta=%04x\n",
+ dev_dbg(&s->client->dev,
+ "%s: f_vco=%llu pll div=%d sigma_delta=%04x\n",
__func__, f_vco, buf[0], sigma_delta);
- ret = e4000_wr_regs(priv, 0x09, buf, 5);
- if (ret < 0)
+ ret = regmap_bulk_write(s->regmap, 0x09, buf, 5);
+ if (ret)
goto err;
/* LNA filter (RF filter) */
@@ -278,8 +169,8 @@ static int e4000_set_params(struct dvb_frontend *fe)
goto err;
}
- ret = e4000_wr_reg(priv, 0x10, e400_lna_filter_lut[i].val);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x10, e400_lna_filter_lut[i].val);
+ if (ret)
goto err;
/* IF filters */
@@ -296,8 +187,8 @@ static int e4000_set_params(struct dvb_frontend *fe)
buf[0] = e4000_if_filter_lut[i].reg11_val;
buf[1] = e4000_if_filter_lut[i].reg12_val;
- ret = e4000_wr_regs(priv, 0x11, buf, 2);
- if (ret < 0)
+ ret = regmap_bulk_write(s->regmap, 0x11, buf, 2);
+ if (ret)
goto err;
/* frequency band */
@@ -311,34 +202,34 @@ static int e4000_set_params(struct dvb_frontend *fe)
goto err;
}
- ret = e4000_wr_reg(priv, 0x07, e4000_band_lut[i].reg07_val);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x07, e4000_band_lut[i].reg07_val);
+ if (ret)
goto err;
- ret = e4000_wr_reg(priv, 0x78, e4000_band_lut[i].reg78_val);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x78, e4000_band_lut[i].reg78_val);
+ if (ret)
goto err;
/* DC offset */
for (i = 0; i < 4; i++) {
if (i == 0)
- ret = e4000_wr_regs(priv, 0x15, "\x00\x7e\x24", 3);
+ ret = regmap_bulk_write(s->regmap, 0x15, "\x00\x7e\x24", 3);
else if (i == 1)
- ret = e4000_wr_regs(priv, 0x15, "\x00\x7f", 2);
+ ret = regmap_bulk_write(s->regmap, 0x15, "\x00\x7f", 2);
else if (i == 2)
- ret = e4000_wr_regs(priv, 0x15, "\x01", 1);
+ ret = regmap_bulk_write(s->regmap, 0x15, "\x01", 1);
else
- ret = e4000_wr_regs(priv, 0x16, "\x7e", 1);
+ ret = regmap_bulk_write(s->regmap, 0x16, "\x7e", 1);
- if (ret < 0)
+ if (ret)
goto err;
- ret = e4000_wr_reg(priv, 0x29, 0x01);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x29, 0x01);
+ if (ret)
goto err;
- ret = e4000_rd_regs(priv, 0x2a, buf, 3);
- if (ret < 0)
+ ret = regmap_bulk_read(s->regmap, 0x2a, buf, 3);
+ if (ret)
goto err;
i_data[i] = (((buf[2] >> 0) & 0x3) << 6) | (buf[0] & 0x3f);
@@ -348,53 +239,226 @@ static int e4000_set_params(struct dvb_frontend *fe)
swap(q_data[2], q_data[3]);
swap(i_data[2], i_data[3]);
- ret = e4000_wr_regs(priv, 0x50, q_data, 4);
- if (ret < 0)
+ ret = regmap_bulk_write(s->regmap, 0x50, q_data, 4);
+ if (ret)
goto err;
- ret = e4000_wr_regs(priv, 0x60, i_data, 4);
- if (ret < 0)
+ ret = regmap_bulk_write(s->regmap, 0x60, i_data, 4);
+ if (ret)
goto err;
/* gain control auto */
- ret = e4000_wr_reg(priv, 0x1a, 0x17);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x1a, 0x17);
+ if (ret)
goto err;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
-
- return 0;
err:
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
+ if (ret)
+ dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
static int e4000_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
{
- struct e4000_priv *priv = fe->tuner_priv;
+ struct e4000 *s = fe->tuner_priv;
- dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+ dev_dbg(&s->client->dev, "%s:\n", __func__);
*frequency = 0; /* Zero-IF */
return 0;
}
-static int e4000_release(struct dvb_frontend *fe)
+#if IS_ENABLED(CONFIG_VIDEO_V4L2)
+static int e4000_set_lna_gain(struct dvb_frontend *fe)
{
- struct e4000_priv *priv = fe->tuner_priv;
+ struct e4000 *s = fe->tuner_priv;
+ int ret;
+ u8 u8tmp;
+
+ dev_dbg(&s->client->dev, "%s: lna auto=%d->%d val=%d->%d\n",
+ __func__, s->lna_gain_auto->cur.val,
+ s->lna_gain_auto->val, s->lna_gain->cur.val,
+ s->lna_gain->val);
+
+ if (s->lna_gain_auto->val && s->if_gain_auto->cur.val)
+ u8tmp = 0x17;
+ else if (s->lna_gain_auto->val)
+ u8tmp = 0x19;
+ else if (s->if_gain_auto->cur.val)
+ u8tmp = 0x16;
+ else
+ u8tmp = 0x10;
+
+ ret = regmap_write(s->regmap, 0x1a, u8tmp);
+ if (ret)
+ goto err;
+
+ if (s->lna_gain_auto->val == false) {
+ ret = regmap_write(s->regmap, 0x14, s->lna_gain->val);
+ if (ret)
+ goto err;
+ }
+err:
+ if (ret)
+ dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
- dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+ return ret;
+}
- kfree(fe->tuner_priv);
+static int e4000_set_mixer_gain(struct dvb_frontend *fe)
+{
+ struct e4000 *s = fe->tuner_priv;
+ int ret;
+ u8 u8tmp;
- return 0;
+ dev_dbg(&s->client->dev, "%s: mixer auto=%d->%d val=%d->%d\n",
+ __func__, s->mixer_gain_auto->cur.val,
+ s->mixer_gain_auto->val, s->mixer_gain->cur.val,
+ s->mixer_gain->val);
+
+ if (s->mixer_gain_auto->val)
+ u8tmp = 0x15;
+ else
+ u8tmp = 0x14;
+
+ ret = regmap_write(s->regmap, 0x20, u8tmp);
+ if (ret)
+ goto err;
+
+ if (s->mixer_gain_auto->val == false) {
+ ret = regmap_write(s->regmap, 0x15, s->mixer_gain->val);
+ if (ret)
+ goto err;
+ }
+err:
+ if (ret)
+ dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
}
+static int e4000_set_if_gain(struct dvb_frontend *fe)
+{
+ struct e4000 *s = fe->tuner_priv;
+ int ret;
+ u8 buf[2];
+ u8 u8tmp;
+
+ dev_dbg(&s->client->dev, "%s: if auto=%d->%d val=%d->%d\n",
+ __func__, s->if_gain_auto->cur.val,
+ s->if_gain_auto->val, s->if_gain->cur.val,
+ s->if_gain->val);
+
+ if (s->if_gain_auto->val && s->lna_gain_auto->cur.val)
+ u8tmp = 0x17;
+ else if (s->lna_gain_auto->cur.val)
+ u8tmp = 0x19;
+ else if (s->if_gain_auto->val)
+ u8tmp = 0x16;
+ else
+ u8tmp = 0x10;
+
+ ret = regmap_write(s->regmap, 0x1a, u8tmp);
+ if (ret)
+ goto err;
+
+ if (s->if_gain_auto->val == false) {
+ buf[0] = e4000_if_gain_lut[s->if_gain->val].reg16_val;
+ buf[1] = e4000_if_gain_lut[s->if_gain->val].reg17_val;
+ ret = regmap_bulk_write(s->regmap, 0x16, buf, 2);
+ if (ret)
+ goto err;
+ }
+err:
+ if (ret)
+ dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int e4000_pll_lock(struct dvb_frontend *fe)
+{
+ struct e4000 *s = fe->tuner_priv;
+ int ret;
+ unsigned int utmp;
+
+ ret = regmap_read(s->regmap, 0x07, &utmp);
+ if (ret)
+ goto err;
+
+ s->pll_lock->val = (utmp & 0x01);
+err:
+ if (ret)
+ dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct e4000 *s = container_of(ctrl->handler, struct e4000, hdl);
+ int ret;
+
+ if (s->active == false)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_RF_TUNER_PLL_LOCK:
+ ret = e4000_pll_lock(s->fe);
+ break;
+ default:
+ dev_dbg(&s->client->dev, "%s: unknown ctrl: id=%d name=%s\n",
+ __func__, ctrl->id, ctrl->name);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int e4000_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct e4000 *s = container_of(ctrl->handler, struct e4000, hdl);
+ struct dvb_frontend *fe = s->fe;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+
+ if (s->active == false)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
+ case V4L2_CID_RF_TUNER_BANDWIDTH:
+ c->bandwidth_hz = s->bandwidth->val;
+ ret = e4000_set_params(s->fe);
+ break;
+ case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO:
+ case V4L2_CID_RF_TUNER_LNA_GAIN:
+ ret = e4000_set_lna_gain(s->fe);
+ break;
+ case V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO:
+ case V4L2_CID_RF_TUNER_MIXER_GAIN:
+ ret = e4000_set_mixer_gain(s->fe);
+ break;
+ case V4L2_CID_RF_TUNER_IF_GAIN_AUTO:
+ case V4L2_CID_RF_TUNER_IF_GAIN:
+ ret = e4000_set_if_gain(s->fe);
+ break;
+ default:
+ dev_dbg(&s->client->dev, "%s: unknown ctrl: id=%d name=%s\n",
+ __func__, ctrl->id, ctrl->name);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops e4000_ctrl_ops = {
+ .g_volatile_ctrl = e4000_g_volatile_ctrl,
+ .s_ctrl = e4000_s_ctrl,
+};
+#endif
+
static const struct dvb_tuner_ops e4000_tuner_ops = {
.info = {
.name = "Elonics E4000",
@@ -402,8 +466,6 @@ static const struct dvb_tuner_ops e4000_tuner_ops = {
.frequency_max = 862000000,
},
- .release = e4000_release,
-
.init = e4000_init,
.sleep = e4000_sleep,
.set_params = e4000_set_params,
@@ -411,62 +473,148 @@ static const struct dvb_tuner_ops e4000_tuner_ops = {
.get_if_frequency = e4000_get_if_frequency,
};
-struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, const struct e4000_config *cfg)
+/*
+ * Use V4L2 subdev to carry V4L2 control handler, even we don't implement
+ * subdev itself, just to avoid reinventing the wheel.
+ */
+static int e4000_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- struct e4000_priv *priv;
+ struct e4000_config *cfg = client->dev.platform_data;
+ struct dvb_frontend *fe = cfg->fe;
+ struct e4000 *s;
int ret;
- u8 chip_id;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
+ unsigned int utmp;
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ };
- priv = kzalloc(sizeof(struct e4000_priv), GFP_KERNEL);
- if (!priv) {
+ s = kzalloc(sizeof(struct e4000), GFP_KERNEL);
+ if (!s) {
ret = -ENOMEM;
- dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+ dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
goto err;
}
- priv->cfg = cfg;
- priv->i2c = i2c;
+ s->clock = cfg->clock;
+ s->client = client;
+ s->fe = cfg->fe;
+ s->regmap = devm_regmap_init_i2c(client, &regmap_config);
+ if (IS_ERR(s->regmap)) {
+ ret = PTR_ERR(s->regmap);
+ goto err;
+ }
/* check if the tuner is there */
- ret = e4000_rd_reg(priv, 0x02, &chip_id);
- if (ret < 0)
+ ret = regmap_read(s->regmap, 0x02, &utmp);
+ if (ret)
goto err;
- dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
+ dev_dbg(&s->client->dev, "%s: chip id=%02x\n", __func__, utmp);
- if (chip_id != 0x40)
+ if (utmp != 0x40) {
+ ret = -ENODEV;
goto err;
+ }
/* put sleep as chip seems to be in normal mode by default */
- ret = e4000_wr_reg(priv, 0x00, 0x00);
- if (ret < 0)
+ ret = regmap_write(s->regmap, 0x00, 0x00);
+ if (ret)
goto err;
- dev_info(&priv->i2c->dev,
+#if IS_ENABLED(CONFIG_VIDEO_V4L2)
+ /* Register controls */
+ v4l2_ctrl_handler_init(&s->hdl, 9);
+ s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
+ V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
+ s->bandwidth = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
+ V4L2_CID_RF_TUNER_BANDWIDTH, 4300000, 11000000, 100000, 4300000);
+ v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+ s->lna_gain_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
+ V4L2_CID_RF_TUNER_LNA_GAIN_AUTO, 0, 1, 1, 1);
+ s->lna_gain = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
+ V4L2_CID_RF_TUNER_LNA_GAIN, 0, 15, 1, 10);
+ v4l2_ctrl_auto_cluster(2, &s->lna_gain_auto, 0, false);
+ s->mixer_gain_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
+ V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO, 0, 1, 1, 1);
+ s->mixer_gain = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
+ V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1);
+ v4l2_ctrl_auto_cluster(2, &s->mixer_gain_auto, 0, false);
+ s->if_gain_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
+ V4L2_CID_RF_TUNER_IF_GAIN_AUTO, 0, 1, 1, 1);
+ s->if_gain = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
+ V4L2_CID_RF_TUNER_IF_GAIN, 0, 54, 1, 0);
+ v4l2_ctrl_auto_cluster(2, &s->if_gain_auto, 0, false);
+ s->pll_lock = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
+ V4L2_CID_RF_TUNER_PLL_LOCK, 0, 1, 1, 0);
+ if (s->hdl.error) {
+ ret = s->hdl.error;
+ dev_err(&s->client->dev, "Could not initialize controls\n");
+ v4l2_ctrl_handler_free(&s->hdl);
+ goto err;
+ }
+
+ s->sd.ctrl_handler = &s->hdl;
+#endif
+
+ dev_info(&s->client->dev,
"%s: Elonics E4000 successfully identified\n",
KBUILD_MODNAME);
- fe->tuner_priv = priv;
+ fe->tuner_priv = s;
memcpy(&fe->ops.tuner_ops, &e4000_tuner_ops,
sizeof(struct dvb_tuner_ops));
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
+ v4l2_set_subdevdata(&s->sd, client);
+ i2c_set_clientdata(client, &s->sd);
- return fe;
+ return 0;
err:
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
+ if (ret) {
+ dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
+ kfree(s);
+ }
+
+ return ret;
+}
+
+static int e4000_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct e4000 *s = container_of(sd, struct e4000, sd);
+ struct dvb_frontend *fe = s->fe;
+
+ dev_dbg(&client->dev, "%s:\n", __func__);
- dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
- kfree(priv);
- return NULL;
+#if IS_ENABLED(CONFIG_VIDEO_V4L2)
+ v4l2_ctrl_handler_free(&s->hdl);
+#endif
+ memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = NULL;
+ kfree(s);
+
+ return 0;
}
-EXPORT_SYMBOL(e4000_attach);
+
+static const struct i2c_device_id e4000_id[] = {
+ {"e4000", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, e4000_id);
+
+static struct i2c_driver e4000_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "e4000",
+ },
+ .probe = e4000_probe,
+ .remove = e4000_remove,
+ .id_table = e4000_id,
+};
+
+module_i2c_driver(e4000_driver);
MODULE_DESCRIPTION("Elonics E4000 silicon tuner driver");
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/tuners/e4000.h b/drivers/media/tuners/e4000.h
index 25ee7c07abff..e74b8b2f2fc3 100644
--- a/drivers/media/tuners/e4000.h
+++ b/drivers/media/tuners/e4000.h
@@ -24,12 +24,15 @@
#include <linux/kconfig.h>
#include "dvb_frontend.h"
+/*
+ * I2C address
+ * 0x64, 0x65, 0x66, 0x67
+ */
struct e4000_config {
/*
- * I2C address
- * 0x64, 0x65, 0x66, 0x67
+ * frontend
*/
- u8 i2c_addr;
+ struct dvb_frontend *fe;
/*
* clock
@@ -37,16 +40,4 @@ struct e4000_config {
u32 clock;
};
-#if IS_ENABLED(CONFIG_MEDIA_TUNER_E4000)
-extern struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, const struct e4000_config *cfg);
-#else
-static inline struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, const struct e4000_config *cfg)
-{
- dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
#endif
diff --git a/drivers/media/tuners/e4000_priv.h b/drivers/media/tuners/e4000_priv.h
index a3855053e78f..cb0070483e65 100644
--- a/drivers/media/tuners/e4000_priv.h
+++ b/drivers/media/tuners/e4000_priv.h
@@ -22,10 +22,29 @@
#define E4000_PRIV_H
#include "e4000.h"
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <linux/regmap.h>
-struct e4000_priv {
- const struct e4000_config *cfg;
- struct i2c_adapter *i2c;
+struct e4000 {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ u32 clock;
+ struct dvb_frontend *fe;
+ struct v4l2_subdev sd;
+ bool active;
+
+ /* Controls */
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *bandwidth_auto;
+ struct v4l2_ctrl *bandwidth;
+ struct v4l2_ctrl *lna_gain_auto;
+ struct v4l2_ctrl *lna_gain;
+ struct v4l2_ctrl *mixer_gain_auto;
+ struct v4l2_ctrl *mixer_gain;
+ struct v4l2_ctrl *if_gain_auto;
+ struct v4l2_ctrl *if_gain;
+ struct v4l2_ctrl *pll_lock;
};
struct e4000_pll {
@@ -144,4 +163,67 @@ static const struct e4000_if_filter e4000_if_filter_lut[] = {
{ 0xffffffff, 0x00, 0x20 },
};
+struct e4000_if_gain {
+ u8 reg16_val;
+ u8 reg17_val;
+};
+
+static const struct e4000_if_gain e4000_if_gain_lut[] = {
+ {0x00, 0x00},
+ {0x20, 0x00},
+ {0x40, 0x00},
+ {0x02, 0x00},
+ {0x22, 0x00},
+ {0x42, 0x00},
+ {0x04, 0x00},
+ {0x24, 0x00},
+ {0x44, 0x00},
+ {0x01, 0x00},
+ {0x21, 0x00},
+ {0x41, 0x00},
+ {0x03, 0x00},
+ {0x23, 0x00},
+ {0x43, 0x00},
+ {0x05, 0x00},
+ {0x25, 0x00},
+ {0x45, 0x00},
+ {0x07, 0x00},
+ {0x27, 0x00},
+ {0x47, 0x00},
+ {0x0f, 0x00},
+ {0x2f, 0x00},
+ {0x4f, 0x00},
+ {0x17, 0x00},
+ {0x37, 0x00},
+ {0x57, 0x00},
+ {0x1f, 0x00},
+ {0x3f, 0x00},
+ {0x5f, 0x00},
+ {0x1f, 0x01},
+ {0x3f, 0x01},
+ {0x5f, 0x01},
+ {0x1f, 0x02},
+ {0x3f, 0x02},
+ {0x5f, 0x02},
+ {0x1f, 0x03},
+ {0x3f, 0x03},
+ {0x5f, 0x03},
+ {0x1f, 0x04},
+ {0x3f, 0x04},
+ {0x5f, 0x04},
+ {0x1f, 0x0c},
+ {0x3f, 0x0c},
+ {0x5f, 0x0c},
+ {0x1f, 0x14},
+ {0x3f, 0x14},
+ {0x5f, 0x14},
+ {0x1f, 0x1c},
+ {0x3f, 0x1c},
+ {0x5f, 0x1c},
+ {0x1f, 0x24},
+ {0x3f, 0x24},
+ {0x5f, 0x24},
+ {0x7f, 0x24},
+};
+
#endif
diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c
index 20cca405bf45..f640dcf4a81d 100644
--- a/drivers/media/tuners/mt2063.c
+++ b/drivers/media/tuners/mt2063.c
@@ -1,7 +1,7 @@
/*
* Driver for mt2063 Micronas tuner
*
- * Copyright (c) 2011 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2011 Mauro Carvalho Chehab
*
* This driver came from a driver originally written by:
* Henry Wang <Henry.wang@AzureWave.com>
@@ -2298,6 +2298,6 @@ static int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe)
}
#endif
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_DESCRIPTION("MT2063 Silicon tuner");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c
index d9ee43fae62d..319adc4f0561 100644
--- a/drivers/media/tuners/r820t.c
+++ b/drivers/media/tuners/r820t.c
@@ -1,7 +1,7 @@
/*
* Rafael Micro R820T driver
*
- * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2013 Mauro Carvalho Chehab
*
* This driver was written from scratch, based on an existing driver
* that it is part of rtl-sdr git tree, released under GPLv2:
@@ -2351,5 +2351,5 @@ err_no_gate:
EXPORT_SYMBOL_GPL(r820t_attach);
MODULE_DESCRIPTION("Rafael Micro r820t silicon tuner driver");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c
index abe256e1f843..05a4ac9edb6b 100644
--- a/drivers/media/tuners/tda18212.c
+++ b/drivers/media/tuners/tda18212.c
@@ -150,6 +150,8 @@ static int tda18212_set_params(struct dvb_frontend *fe)
#define DVBT2_8 5
#define DVBC_6 6
#define DVBC_8 7
+ #define ATSC_VSB 8
+ #define ATSC_QAM 9
static const u8 bw_params[][3] = {
/* reg: 0f 13 23 */
[DVBT_6] = { 0xb3, 0x20, 0x03 },
@@ -160,6 +162,8 @@ static int tda18212_set_params(struct dvb_frontend *fe)
[DVBT2_8] = { 0xbc, 0x22, 0x01 },
[DVBC_6] = { 0x92, 0x50, 0x03 },
[DVBC_8] = { 0x92, 0x53, 0x03 },
+ [ATSC_VSB] = { 0x7d, 0x20, 0x63 },
+ [ATSC_QAM] = { 0x7d, 0x20, 0x63 },
};
dev_dbg(&priv->i2c->dev,
@@ -171,6 +175,14 @@ static int tda18212_set_params(struct dvb_frontend *fe)
fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
switch (c->delivery_system) {
+ case SYS_ATSC:
+ if_khz = priv->cfg->if_atsc_vsb;
+ i = ATSC_VSB;
+ break;
+ case SYS_DVBC_ANNEX_B:
+ if_khz = priv->cfg->if_atsc_qam;
+ i = ATSC_QAM;
+ break;
case SYS_DVBT:
switch (c->bandwidth_hz) {
case 6000000:
diff --git a/drivers/media/tuners/tda18212.h b/drivers/media/tuners/tda18212.h
index 7e0d503baf05..c36b49e4b274 100644
--- a/drivers/media/tuners/tda18212.h
+++ b/drivers/media/tuners/tda18212.h
@@ -35,6 +35,8 @@ struct tda18212_config {
u16 if_dvbt2_7;
u16 if_dvbt2_8;
u16 if_dvbc;
+ u16 if_atsc_vsb;
+ u16 if_atsc_qam;
};
#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA18212)
diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c
index cca508d4aafb..76a816511f2f 100644
--- a/drivers/media/tuners/tuner-xc2028.c
+++ b/drivers/media/tuners/tuner-xc2028.c
@@ -1107,6 +1107,9 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
offset += 200000;
}
#endif
+ default:
+ tuner_err("Unsupported tuner type %d.\n", new_type);
+ break;
}
div = (freq - offset + DIV / 2) / DIV;
diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c
index dd32decb237d..7fdadf9bc90b 100644
--- a/drivers/media/usb/au0828/au0828-cards.c
+++ b/drivers/media/usb/au0828/au0828-cards.c
@@ -108,7 +108,7 @@ struct au0828_board au0828_boards[] = {
.name = "DViCO FusionHDTV USB",
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
- .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
+ .i2c_clk_divider = AU0828_I2C_CLK_20KHZ,
},
[AU0828_BOARD_HAUPPAUGE_WOODBURY] = {
.name = "Hauppauge Woodbury",
@@ -270,18 +270,25 @@ void au0828_gpio_setup(struct au0828_dev *dev)
* 9 - XC5000 Tuner
*/
- /* Into reset */
+ /* Set relevant GPIOs as outputs (leave the EEPROM W/P
+ as an input since we will never touch it and it has
+ a pullup) */
au0828_write(dev, REG_003, 0x02);
au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
+
+ /* Into reset */
au0828_write(dev, REG_001, 0x0);
au0828_write(dev, REG_000, 0x0);
- msleep(100);
+ msleep(50);
- /* Out of reset (leave the cs5340 in reset until needed) */
- au0828_write(dev, REG_003, 0x02);
- au0828_write(dev, REG_001, 0x02);
- au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
- au0828_write(dev, REG_000, 0x80 | 0x40 | 0x20);
+ /* Bring power supply out of reset */
+ au0828_write(dev, REG_000, 0x80);
+ msleep(50);
+
+ /* Bring xc5000 and au8522 out of reset (leave the
+ cs5340 in reset until needed) */
+ au0828_write(dev, REG_001, 0x02); /* xc5000 */
+ au0828_write(dev, REG_000, 0x80 | 0x20); /* PS + au8522 */
msleep(250);
break;
diff --git a/drivers/media/usb/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c
index 0f7b42446826..46d52fac8680 100644
--- a/drivers/media/usb/cx231xx/cx231xx-input.c
+++ b/drivers/media/usb/cx231xx/cx231xx-input.c
@@ -1,7 +1,7 @@
/*
* cx231xx IR glue driver
*
- * Copyright (C) 2010 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 Mauro Carvalho Chehab
*
* Polaris (cx231xx) has its support for IR's with a design close to MCE.
* however, a few designs are using an external I2C chip for IR, instead
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
index 2059d0c86ad3..037e519bbaa2 100644
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -100,13 +100,6 @@ config DVB_USB_GL861
Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0
receiver with USB ID 0db0:5581.
-config DVB_USB_IT913X
- tristate "ITE IT913X DVB-T USB2.0 support"
- depends on DVB_USB_V2
- select DVB_IT913X_FE
- help
- Say Y here to support the ITE IT913X DVB-T USB2.0
-
config DVB_USB_LME2510
tristate "LME DM04/QQBOX DVB-S USB2.0 support"
depends on DVB_USB_V2
@@ -133,7 +126,7 @@ config DVB_USB_MXL111SF
config DVB_USB_RTL28XXU
tristate "Realtek RTL28xxU DVB USB support"
- depends on DVB_USB_V2
+ depends on DVB_USB_V2 && I2C_MUX
select DVB_RTL2830
select DVB_RTL2832
select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile
index 2c06714b9ef0..7407b8338ccf 100644
--- a/drivers/media/usb/dvb-usb-v2/Makefile
+++ b/drivers/media/usb/dvb-usb-v2/Makefile
@@ -22,9 +22,6 @@ obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o
dvb-usb-ec168-objs := ec168.o
obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o
-dvb-usb-it913x-objs := it913x.o
-obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o
-
dvb-usb-lmedm04-objs := lmedm04.o
obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o
@@ -44,3 +41,4 @@ ccflags-y += -I$(srctree)/drivers/media/dvb-core
ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
ccflags-y += -I$(srctree)/drivers/media/tuners
ccflags-y += -I$(srctree)/drivers/media/common
+ccflags-y += -I$(srctree)/drivers/staging/media/rtl2832u_sdr
diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c
index 8ede8ea762e6..021e4d35e4d7 100644
--- a/drivers/media/usb/dvb-usb-v2/af9035.c
+++ b/drivers/media/usb/dvb-usb-v2/af9035.c
@@ -575,6 +575,10 @@ static int af9035_download_firmware(struct dvb_usb_device *d,
if (ret < 0)
goto err;
+ /* use default I2C address if eeprom has no address set */
+ if (!tmp)
+ tmp = 0x3a;
+
if (state->chip_type == 0x9135) {
ret = af9035_wr_reg(d, 0x004bfb, tmp);
if (ret < 0)
@@ -637,6 +641,7 @@ static int af9035_read_config(struct dvb_usb_device *d)
/* demod I2C "address" */
state->af9033_config[0].i2c_addr = 0x38;
+ state->af9033_config[1].i2c_addr = 0x3a;
state->af9033_config[0].adc_multiplier = AF9033_ADC_MULTIPLIER_2X;
state->af9033_config[1].adc_multiplier = AF9033_ADC_MULTIPLIER_2X;
state->af9033_config[0].ts_mode = AF9033_TS_MODE_USB;
@@ -684,7 +689,9 @@ static int af9035_read_config(struct dvb_usb_device *d)
if (ret < 0)
goto err;
- state->af9033_config[1].i2c_addr = tmp;
+ if (tmp)
+ state->af9033_config[1].i2c_addr = tmp;
+
dev_dbg(&d->udev->dev, "%s: 2nd demod I2C addr=%02x\n",
__func__, tmp);
}
@@ -938,12 +945,7 @@ static int af9035_frontend_callback(void *adapter_priv, int component,
static int af9035_get_adapter_count(struct dvb_usb_device *d)
{
struct state *state = d_to_priv(d);
-
- /* disable 2nd adapter as we don't have PID filters implemented */
- if (d->udev->speed == USB_SPEED_FULL)
- return 1;
- else
- return state->dual_mode + 1;
+ return state->dual_mode + 1;
}
static int af9035_frontend_attach(struct dvb_usb_adapter *adap)
@@ -961,7 +963,7 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap)
/* attach demodulator */
adap->fe[0] = dvb_attach(af9033_attach, &state->af9033_config[adap->id],
- &d->i2c_adap);
+ &d->i2c_adap, &state->ops);
if (adap->fe[0] == NULL) {
ret = -ENODEV;
goto err;
@@ -1369,58 +1371,19 @@ static int af9035_get_stream_config(struct dvb_frontend *fe, u8 *ts_type,
return 0;
}
-/*
- * FIXME: PID filter is property of demodulator and should be moved to the
- * correct driver. Also we support only adapter #0 PID filter and will
- * disable adapter #1 if USB1.1 is used.
- */
static int af9035_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
- struct dvb_usb_device *d = adap_to_d(adap);
- int ret;
-
- dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
-
- ret = af9035_wr_reg_mask(d, 0x80f993, onoff, 0x01);
- if (ret < 0)
- goto err;
-
- return 0;
-
-err:
- dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ struct state *state = adap_to_priv(adap);
- return ret;
+ return state->ops.pid_filter_ctrl(adap->fe[0], onoff);
}
static int af9035_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
int onoff)
{
- struct dvb_usb_device *d = adap_to_d(adap);
- int ret;
- u8 wbuf[2] = {(pid >> 0) & 0xff, (pid >> 8) & 0xff};
-
- dev_dbg(&d->udev->dev, "%s: index=%d pid=%04x onoff=%d\n",
- __func__, index, pid, onoff);
-
- ret = af9035_wr_regs(d, 0x80f996, wbuf, 2);
- if (ret < 0)
- goto err;
-
- ret = af9035_wr_reg(d, 0x80f994, onoff);
- if (ret < 0)
- goto err;
-
- ret = af9035_wr_reg(d, 0x80f995, index);
- if (ret < 0)
- goto err;
-
- return 0;
-
-err:
- dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ struct state *state = adap_to_priv(adap);
- return ret;
+ return state->ops.pid_filter(adap->fe[0], index, pid, onoff);
}
static int af9035_probe(struct usb_interface *intf,
@@ -1494,6 +1457,13 @@ static const struct dvb_usb_device_properties af9035_props = {
.stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188),
}, {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+ .pid_filter_count = 32,
+ .pid_filter_ctrl = af9035_pid_filter_ctrl,
+ .pid_filter = af9035_pid_filter,
+
.stream = DVB_USB_STREAM_BULK(0x85, 6, 87 * 188),
},
},
@@ -1528,12 +1498,30 @@ static const struct usb_device_id af9035_id_table[] = {
{ DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00aa,
&af9035_props, "TerraTec Cinergy T Stick (rev. 2)", NULL) },
/* IT9135 devices */
-#if 0
- { DVB_USB_DEVICE(0x048d, 0x9135,
- &af9035_props, "IT9135 reference design", NULL) },
- { DVB_USB_DEVICE(0x048d, 0x9006,
- &af9035_props, "IT9135 reference design", NULL) },
-#endif
+ { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135,
+ &af9035_props, "ITE 9135 Generic", RC_MAP_IT913X_V1) },
+ { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9005,
+ &af9035_props, "ITE 9135(9005) Generic", RC_MAP_IT913X_V2) },
+ { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9006,
+ &af9035_props, "ITE 9135(9006) Generic", RC_MAP_IT913X_V1) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_1835,
+ &af9035_props, "Avermedia A835B(1835)", RC_MAP_IT913X_V2) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_2835,
+ &af9035_props, "Avermedia A835B(2835)", RC_MAP_IT913X_V2) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_3835,
+ &af9035_props, "Avermedia A835B(3835)", RC_MAP_IT913X_V2) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_4835,
+ &af9035_props, "Avermedia A835B(4835)", RC_MAP_IT913X_V2) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_H335,
+ &af9035_props, "Avermedia H335", RC_MAP_IT913X_V2) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09,
+ &af9035_props, "Kworld UB499-2T T09", RC_MAP_IT913X_V1) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22_IT9137,
+ &af9035_props, "Sveon STV22 Dual DVB-T HDTV",
+ RC_MAP_IT913X_V1) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CTVDIGDUAL_V2,
+ &af9035_props, "Digital Dual TV Receiver CTVDIGDUAL_V2",
+ RC_MAP_IT913X_V1) },
/* XXX: that same ID [0ccd:0099] is used by af9015 driver too */
{ DVB_USB_DEVICE(USB_VID_TERRATEC, 0x0099,
&af9035_props, "TerraTec Cinergy T Stick Dual RC (rev. 2)", NULL) },
diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h
index a1c68d829b8c..c21902fdd4c4 100644
--- a/drivers/media/usb/dvb-usb-v2/af9035.h
+++ b/drivers/media/usb/dvb-usb-v2/af9035.h
@@ -62,6 +62,8 @@ struct state {
u8 dual_mode:1;
u16 eeprom_addr;
struct af9033_config af9033_config[2];
+
+ struct af9033_ops ops;
};
static const u32 clock_lut_af9035[] = {
diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c
index c1051c347744..c3c4b98733bf 100644
--- a/drivers/media/usb/dvb-usb-v2/az6007.c
+++ b/drivers/media/usb/dvb-usb-v2/az6007.c
@@ -7,7 +7,7 @@
* http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
* The original driver's license is GPL, as declared with MODULE_LICENSE()
*
- * Copyright (c) 2010-2012 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010-2012 Mauro Carvalho Chehab
* Driver modified by in order to work with upstream drxk driver, and
* tons of bugs got fixed, and converted to use dvb-usb-v2.
*
@@ -975,7 +975,7 @@ static struct usb_driver az6007_usb_driver = {
module_usb_driver(az6007_usb_driver);
MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
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 8a054d66e708..de02db802ace 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
@@ -164,7 +164,7 @@ static int dvb_usbv2_remote_init(struct dvb_usb_device *d)
dev->driver_name = (char *) d->props->driver_name;
dev->map_name = d->rc.map_name;
dev->driver_type = d->rc.driver_type;
- dev->allowed_protos = d->rc.allowed_protos;
+ rc_set_allowed_protocols(dev, d->rc.allowed_protos);
dev->change_protocol = d->rc.change_protocol;
dev->priv = d;
diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c
deleted file mode 100644
index fe95a586dd5d..000000000000
--- a/drivers/media/usb/dvb-usb-v2/it913x.c
+++ /dev/null
@@ -1,828 +0,0 @@
-/*
- * DVB USB compliant linux driver for ITE IT9135 and IT9137
- *
- * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
- * IT9135 (C) ITE Tech Inc.
- * IT9137 (C) ITE Tech 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- * see Documentation/dvb/README.dvb-usb for more information
- * see Documentation/dvb/it9137.txt for firmware information
- *
- */
-#define DVB_USB_LOG_PREFIX "it913x"
-
-#include <linux/usb.h>
-#include <linux/usb/input.h>
-#include <media/rc-core.h>
-
-#include "dvb_usb.h"
-#include "it913x-fe.h"
-
-/* debug */
-static int dvb_usb_it913x_debug;
-#define it_debug(var, level, args...) \
- do { if ((var & level)) pr_debug(DVB_USB_LOG_PREFIX": " args); \
-} while (0)
-#define deb_info(level, args...) it_debug(dvb_usb_it913x_debug, level, args)
-#define info(args...) pr_info(DVB_USB_LOG_PREFIX": " args)
-
-module_param_named(debug, dvb_usb_it913x_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
-
-static int dvb_usb_it913x_firmware;
-module_param_named(firmware, dvb_usb_it913x_firmware, int, 0644);
-MODULE_PARM_DESC(firmware, "set firmware 0=auto "\
- "1=IT9137 2=IT9135 V1 3=IT9135 V2");
-#define FW_IT9137 "dvb-usb-it9137-01.fw"
-#define FW_IT9135_V1 "dvb-usb-it9135-01.fw"
-#define FW_IT9135_V2 "dvb-usb-it9135-02.fw"
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-struct it913x_state {
- struct ite_config it913x_config;
- u8 pid_filter_onoff;
- bool proprietary_ir;
- int cmd_counter;
-};
-
-static u16 check_sum(u8 *p, u8 len)
-{
- u16 sum = 0;
- u8 i = 1;
- while (i < len)
- sum += (i++ & 1) ? (*p++) << 8 : *p++;
- return ~sum;
-}
-
-static int it913x_io(struct dvb_usb_device *d, u8 mode, u8 pro,
- u8 cmd, u32 reg, u8 addr, u8 *data, u8 len)
-{
- struct it913x_state *st = d->priv;
- int ret = 0, i, buf_size = 1;
- u8 *buff;
- u8 rlen;
- u16 chk_sum;
-
- buff = kzalloc(256, GFP_KERNEL);
- if (!buff) {
- info("USB Buffer Failed");
- return -ENOMEM;
- }
-
- buff[buf_size++] = pro;
- buff[buf_size++] = cmd;
- buff[buf_size++] = st->cmd_counter;
-
- switch (mode) {
- case READ_LONG:
- case WRITE_LONG:
- buff[buf_size++] = len;
- buff[buf_size++] = 2;
- buff[buf_size++] = (reg >> 24);
- buff[buf_size++] = (reg >> 16) & 0xff;
- buff[buf_size++] = (reg >> 8) & 0xff;
- buff[buf_size++] = reg & 0xff;
- break;
- case READ_SHORT:
- buff[buf_size++] = addr;
- break;
- case WRITE_SHORT:
- buff[buf_size++] = len;
- buff[buf_size++] = addr;
- buff[buf_size++] = (reg >> 8) & 0xff;
- buff[buf_size++] = reg & 0xff;
- break;
- case READ_DATA:
- case WRITE_DATA:
- break;
- case WRITE_CMD:
- mode = 7;
- break;
- default:
- kfree(buff);
- return -EINVAL;
- }
-
- if (mode & 1) {
- for (i = 0; i < len ; i++)
- buff[buf_size++] = data[i];
- }
- chk_sum = check_sum(&buff[1], buf_size);
-
- buff[buf_size++] = chk_sum >> 8;
- buff[0] = buf_size;
- buff[buf_size++] = (chk_sum & 0xff);
-
- ret = dvb_usbv2_generic_rw(d, buff, buf_size, buff, (mode & 1) ?
- 5 : len + 5);
- if (ret < 0)
- goto error;
-
- rlen = (mode & 0x1) ? 0x1 : len;
-
- if (mode & 1)
- ret = buff[2];
- else
- memcpy(data, &buff[3], rlen);
-
- st->cmd_counter++;
-
-error: kfree(buff);
-
- return ret;
-}
-
-static int it913x_wr_reg(struct dvb_usb_device *d, u8 pro, u32 reg , u8 data)
-{
- int ret;
- u8 b[1];
- b[0] = data;
- ret = it913x_io(d, WRITE_LONG, pro,
- CMD_DEMOD_WRITE, reg, 0, b, sizeof(b));
-
- return ret;
-}
-
-static int it913x_read_reg(struct dvb_usb_device *d, u32 reg)
-{
- int ret;
- u8 data[1];
-
- ret = it913x_io(d, READ_LONG, DEV_0,
- CMD_DEMOD_READ, reg, 0, &data[0], sizeof(data));
-
- return (ret < 0) ? ret : data[0];
-}
-
-static int it913x_query(struct dvb_usb_device *d, u8 pro)
-{
- struct it913x_state *st = d->priv;
- int ret, i;
- u8 data[4];
- u8 ver;
-
- for (i = 0; i < 5; i++) {
- ret = it913x_io(d, READ_LONG, pro, CMD_DEMOD_READ,
- 0x1222, 0, &data[0], 3);
- ver = data[0];
- if (ver > 0 && ver < 3)
- break;
- msleep(100);
- }
-
- if (ver < 1 || ver > 2) {
- info("Failed to identify chip version applying 1");
- st->it913x_config.chip_ver = 0x1;
- st->it913x_config.chip_type = 0x9135;
- return 0;
- }
-
- st->it913x_config.chip_ver = ver;
- st->it913x_config.chip_type = (u16)(data[2] << 8) + data[1];
-
- info("Chip Version=%02x Chip Type=%04x", st->it913x_config.chip_ver,
- st->it913x_config.chip_type);
-
- ret = it913x_io(d, READ_SHORT, pro,
- CMD_QUERYINFO, 0, 0x1, &data[0], 4);
-
- st->it913x_config.firmware = (data[0] << 24) | (data[1] << 16) |
- (data[2] << 8) | data[3];
-
- return ret;
-}
-
-static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- struct dvb_usb_device *d = adap_to_d(adap);
- struct it913x_state *st = adap_to_priv(adap);
- int ret;
- u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
-
- mutex_lock(&d->i2c_mutex);
-
- deb_info(1, "PID_C (%02x)", onoff);
-
- st->pid_filter_onoff = adap->pid_filtering;
- ret = it913x_wr_reg(d, pro, PID_EN, st->pid_filter_onoff);
-
- mutex_unlock(&d->i2c_mutex);
- return ret;
-}
-
-static int it913x_pid_filter(struct dvb_usb_adapter *adap,
- int index, u16 pid, int onoff)
-{
- struct dvb_usb_device *d = adap_to_d(adap);
- struct it913x_state *st = adap_to_priv(adap);
- int ret;
- u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
-
- mutex_lock(&d->i2c_mutex);
-
- deb_info(1, "PID_F (%02x)", onoff);
-
- ret = it913x_wr_reg(d, pro, PID_LSB, (u8)(pid & 0xff));
-
- ret |= it913x_wr_reg(d, pro, PID_MSB, (u8)(pid >> 8));
-
- ret |= it913x_wr_reg(d, pro, PID_INX_EN, (u8)onoff);
-
- ret |= it913x_wr_reg(d, pro, PID_INX, (u8)(index & 0x1f));
-
- if (d->udev->speed == USB_SPEED_HIGH && pid == 0x2000) {
- ret |= it913x_wr_reg(d , pro, PID_EN, !onoff);
- st->pid_filter_onoff = !onoff;
- } else
- st->pid_filter_onoff =
- adap->pid_filtering;
-
- mutex_unlock(&d->i2c_mutex);
- return 0;
-}
-
-
-static int it913x_return_status(struct dvb_usb_device *d)
-{
- struct it913x_state *st = d->priv;
- int ret = it913x_query(d, DEV_0);
- if (st->it913x_config.firmware > 0)
- info("Firmware Version %d", st->it913x_config.firmware);
-
- return ret;
-}
-
-static int it913x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- static u8 data[256];
- int ret;
- u32 reg;
- u8 pro;
-
- mutex_lock(&d->i2c_mutex);
-
- deb_info(2, "num of messages %d address %02x", num, msg[0].addr);
-
- pro = (msg[0].addr & 0x2) ? DEV_0_DMOD : 0x0;
- pro |= (msg[0].addr & 0x20) ? DEV_1 : DEV_0;
- memcpy(data, msg[0].buf, msg[0].len);
- reg = (data[0] << 24) + (data[1] << 16) +
- (data[2] << 8) + data[3];
- if (num == 2) {
- ret = it913x_io(d, READ_LONG, pro,
- CMD_DEMOD_READ, reg, 0, data, msg[1].len);
- memcpy(msg[1].buf, data, msg[1].len);
- } else
- ret = it913x_io(d, WRITE_LONG, pro, CMD_DEMOD_WRITE,
- reg, 0, &data[4], msg[0].len - 4);
-
- mutex_unlock(&d->i2c_mutex);
-
- return ret;
-}
-
-static u32 it913x_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm it913x_i2c_algo = {
- .master_xfer = it913x_i2c_xfer,
- .functionality = it913x_i2c_func,
-};
-
-/* Callbacks for DVB USB */
-#if IS_ENABLED(CONFIG_RC_CORE)
-static int it913x_rc_query(struct dvb_usb_device *d)
-{
- u8 ibuf[4];
- int ret;
- u32 key;
- /* Avoid conflict with frontends*/
- mutex_lock(&d->i2c_mutex);
-
- ret = it913x_io(d, READ_LONG, PRO_LINK, CMD_IR_GET,
- 0, 0, &ibuf[0], sizeof(ibuf));
-
- if ((ibuf[2] + ibuf[3]) == 0xff) {
- key = ibuf[2];
- key += ibuf[0] << 16;
- key += ibuf[1] << 8;
- deb_info(1, "NEC Extended Key =%08x", key);
- if (d->rc_dev != NULL)
- rc_keydown(d->rc_dev, key, 0);
- }
-
- mutex_unlock(&d->i2c_mutex);
-
- return ret;
-}
-
-static int it913x_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
-{
- struct it913x_state *st = d->priv;
-
- if (st->proprietary_ir == false) {
- rc->map_name = NULL;
- return 0;
- }
-
- rc->allowed_protos = RC_BIT_NEC;
- rc->query = it913x_rc_query;
- rc->interval = 250;
-
- return 0;
-}
-#else
- #define it913x_get_rc_config NULL
-#endif
-
-/* Firmware sets raw */
-static const char fw_it9135_v1[] = FW_IT9135_V1;
-static const char fw_it9135_v2[] = FW_IT9135_V2;
-static const char fw_it9137[] = FW_IT9137;
-
-static void ite_get_firmware_name(struct dvb_usb_device *d,
- const char **name)
-{
- struct it913x_state *st = d->priv;
- int sw;
- /* auto switch */
- if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_KWORLD_2)
- sw = IT9137_FW;
- else if (st->it913x_config.chip_ver == 1)
- sw = IT9135_V1_FW;
- else
- sw = IT9135_V2_FW;
-
- /* force switch */
- if (dvb_usb_it913x_firmware != IT9135_AUTO)
- sw = dvb_usb_it913x_firmware;
-
- switch (sw) {
- case IT9135_V1_FW:
- st->it913x_config.firmware_ver = 1;
- st->it913x_config.adc_x2 = 1;
- st->it913x_config.read_slevel = false;
- *name = fw_it9135_v1;
- break;
- case IT9135_V2_FW:
- st->it913x_config.firmware_ver = 1;
- st->it913x_config.adc_x2 = 1;
- st->it913x_config.read_slevel = false;
- *name = fw_it9135_v2;
- switch (st->it913x_config.tuner_id_0) {
- case IT9135_61:
- case IT9135_62:
- break;
- default:
- info("Unknown tuner ID applying default 0x60");
- case IT9135_60:
- st->it913x_config.tuner_id_0 = IT9135_60;
- }
- break;
- case IT9137_FW:
- default:
- st->it913x_config.firmware_ver = 0;
- st->it913x_config.adc_x2 = 0;
- st->it913x_config.read_slevel = true;
- *name = fw_it9137;
- }
-
- return;
-}
-
-#define TS_MPEG_PKT_SIZE 188
-#define EP_LOW 21
-#define TS_BUFFER_SIZE_PID (EP_LOW*TS_MPEG_PKT_SIZE)
-#define EP_HIGH 348
-#define TS_BUFFER_SIZE_MAX (EP_HIGH*TS_MPEG_PKT_SIZE)
-
-static int it913x_get_stream_config(struct dvb_frontend *fe, u8 *ts_type,
- struct usb_data_stream_properties *stream)
-{
- struct dvb_usb_adapter *adap = fe_to_adap(fe);
- if (adap->pid_filtering)
- stream->u.bulk.buffersize = TS_BUFFER_SIZE_PID;
- else
- stream->u.bulk.buffersize = TS_BUFFER_SIZE_MAX;
-
- return 0;
-}
-
-static int it913x_select_config(struct dvb_usb_device *d)
-{
- struct it913x_state *st = d->priv;
- int ret, reg;
-
- ret = it913x_return_status(d);
- if (ret < 0)
- return ret;
-
- if (st->it913x_config.chip_ver == 0x02
- && st->it913x_config.chip_type == 0x9135)
- reg = it913x_read_reg(d, 0x461d);
- else
- reg = it913x_read_reg(d, 0x461b);
-
- if (reg < 0)
- return reg;
-
- if (reg == 0) {
- st->it913x_config.dual_mode = 0;
- st->it913x_config.tuner_id_0 = IT9135_38;
- st->proprietary_ir = true;
- } else {
- /* TS mode */
- reg = it913x_read_reg(d, 0x49c5);
- if (reg < 0)
- return reg;
- st->it913x_config.dual_mode = reg;
-
- /* IR mode type */
- reg = it913x_read_reg(d, 0x49ac);
- if (reg < 0)
- return reg;
- if (reg == 5) {
- info("Remote propriety (raw) mode");
- st->proprietary_ir = true;
- } else if (reg == 1) {
- info("Remote HID mode NOT SUPPORTED");
- st->proprietary_ir = false;
- }
-
- /* Tuner_id */
- reg = it913x_read_reg(d, 0x49d0);
- if (reg < 0)
- return reg;
- st->it913x_config.tuner_id_0 = reg;
- }
-
- info("Dual mode=%x Tuner Type=%x", st->it913x_config.dual_mode,
- st->it913x_config.tuner_id_0);
-
- return ret;
-}
-
-static int it913x_streaming_ctrl(struct dvb_frontend *fe, int onoff)
-{
- struct dvb_usb_adapter *adap = fe_to_adap(fe);
- struct dvb_usb_device *d = adap_to_d(adap);
- struct it913x_state *st = fe_to_priv(fe);
- int ret = 0;
- u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
-
- deb_info(1, "STM (%02x)", onoff);
-
- if (!onoff) {
- mutex_lock(&d->i2c_mutex);
-
- ret = it913x_wr_reg(d, pro, PID_RST, 0x1);
-
- mutex_unlock(&d->i2c_mutex);
- st->pid_filter_onoff =
- adap->pid_filtering;
-
- }
-
- return ret;
-}
-
-static int it913x_identify_state(struct dvb_usb_device *d, const char **name)
-{
- struct it913x_state *st = d->priv;
- int ret;
- u8 reg;
-
- /* Read and select config */
- ret = it913x_select_config(d);
- if (ret < 0)
- return ret;
-
- ite_get_firmware_name(d, name);
-
- if (st->it913x_config.firmware > 0)
- return WARM;
-
- if (st->it913x_config.dual_mode) {
- st->it913x_config.tuner_id_1 = it913x_read_reg(d, 0x49e0);
- ret = it913x_wr_reg(d, DEV_0, GPIOH1_EN, 0x1);
- ret |= it913x_wr_reg(d, DEV_0, GPIOH1_ON, 0x1);
- ret |= it913x_wr_reg(d, DEV_0, GPIOH1_O, 0x1);
- msleep(50);
- ret |= it913x_wr_reg(d, DEV_0, GPIOH1_O, 0x0);
- msleep(50);
- reg = it913x_read_reg(d, GPIOH1_O);
- if (reg == 0) {
- ret |= it913x_wr_reg(d, DEV_0, GPIOH1_O, 0x1);
- ret |= it913x_return_status(d);
- if (ret != 0)
- ret = it913x_wr_reg(d, DEV_0,
- GPIOH1_O, 0x0);
- }
- }
-
- reg = it913x_read_reg(d, IO_MUX_POWER_CLK);
-
- if (st->it913x_config.dual_mode) {
- ret |= it913x_wr_reg(d, DEV_0, 0x4bfb, CHIP2_I2C_ADDR);
- if (st->it913x_config.firmware_ver == 1)
- ret |= it913x_wr_reg(d, DEV_0, 0xcfff, 0x1);
- else
- ret |= it913x_wr_reg(d, DEV_0, CLK_O_EN, 0x1);
- } else {
- ret |= it913x_wr_reg(d, DEV_0, 0x4bfb, 0x0);
- if (st->it913x_config.firmware_ver == 1)
- ret |= it913x_wr_reg(d, DEV_0, 0xcfff, 0x0);
- else
- ret |= it913x_wr_reg(d, DEV_0, CLK_O_EN, 0x0);
- }
-
- ret |= it913x_wr_reg(d, DEV_0, I2C_CLK, I2C_CLK_100);
-
- return (ret < 0) ? ret : COLD;
-}
-
-static int it913x_download_firmware(struct dvb_usb_device *d,
- const struct firmware *fw)
-{
- struct it913x_state *st = d->priv;
- int ret = 0, i = 0, pos = 0;
- u8 packet_size, min_pkt;
- u8 *fw_data;
-
- ret = it913x_wr_reg(d, DEV_0, I2C_CLK, I2C_CLK_100);
-
- info("FRM Starting Firmware Download");
-
- /* Multi firmware loader */
- /* This uses scatter write firmware headers */
- /* The firmware must start with 03 XX 00 */
- /* and be the extact firmware length */
-
- if (st->it913x_config.chip_ver == 2)
- min_pkt = 0x11;
- else
- min_pkt = 0x19;
-
- while (i <= fw->size) {
- if (((fw->data[i] == 0x3) && (fw->data[i + 2] == 0x0))
- || (i == fw->size)) {
- packet_size = i - pos;
- if ((packet_size > min_pkt) || (i == fw->size)) {
- fw_data = (u8 *)(fw->data + pos);
- pos += packet_size;
- if (packet_size > 0) {
- ret = it913x_io(d, WRITE_DATA,
- DEV_0, CMD_SCATTER_WRITE, 0,
- 0, fw_data, packet_size);
- if (ret < 0)
- break;
- }
- udelay(1000);
- }
- }
- i++;
- }
-
- if (ret < 0)
- info("FRM Firmware Download Failed (%d)" , ret);
- else
- info("FRM Firmware Download Completed - Resetting Device");
-
- msleep(30);
-
- ret = it913x_io(d, WRITE_CMD, DEV_0, CMD_BOOT, 0, 0, NULL, 0);
- if (ret < 0)
- info("FRM Device not responding to reboot");
-
- ret = it913x_return_status(d);
- if (st->it913x_config.firmware == 0) {
- info("FRM Failed to reboot device");
- return -ENODEV;
- }
-
- msleep(30);
-
- ret = it913x_wr_reg(d, DEV_0, I2C_CLK, I2C_CLK_400);
-
- msleep(30);
-
- /* Tuner function */
- if (st->it913x_config.dual_mode)
- ret |= it913x_wr_reg(d, DEV_0_DMOD , 0xec4c, 0xa0);
- else
- ret |= it913x_wr_reg(d, DEV_0_DMOD , 0xec4c, 0x68);
-
- if ((st->it913x_config.chip_ver == 1) &&
- (st->it913x_config.chip_type == 0x9135)) {
- ret |= it913x_wr_reg(d, DEV_0, PADODPU, 0x0);
- ret |= it913x_wr_reg(d, DEV_0, AGC_O_D, 0x0);
- if (st->it913x_config.dual_mode) {
- ret |= it913x_wr_reg(d, DEV_1, PADODPU, 0x0);
- ret |= it913x_wr_reg(d, DEV_1, AGC_O_D, 0x0);
- }
- }
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int it913x_name(struct dvb_usb_adapter *adap)
-{
- struct dvb_usb_device *d = adap_to_d(adap);
- const char *desc = d->name;
- char *fe_name[] = {"_1", "_2", "_3", "_4"};
- char *name = adap->fe[0]->ops.info.name;
-
- strlcpy(name, desc, 128);
- strlcat(name, fe_name[adap->id], 128);
-
- return 0;
-}
-
-static int it913x_frontend_attach(struct dvb_usb_adapter *adap)
-{
- struct dvb_usb_device *d = adap_to_d(adap);
- struct it913x_state *st = d->priv;
- int ret = 0;
- u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5);
- u16 ep_size = (adap->pid_filtering) ? TS_BUFFER_SIZE_PID / 4 :
- TS_BUFFER_SIZE_MAX / 4;
- u8 pkt_size = 0x80;
-
- if (d->udev->speed != USB_SPEED_HIGH)
- pkt_size = 0x10;
-
- st->it913x_config.adf = it913x_read_reg(d, IO_MUX_POWER_CLK);
-
- adap->fe[0] = dvb_attach(it913x_fe_attach,
- &d->i2c_adap, adap_addr, &st->it913x_config);
-
- if (adap->id == 0 && adap->fe[0]) {
- it913x_wr_reg(d, DEV_0_DMOD, MP2_SW_RST, 0x1);
- it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_SW_RST, 0x1);
- it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x0f);
- it913x_wr_reg(d, DEV_0, EP0_TX_NAK, 0x1b);
- if (st->proprietary_ir == false) /* Enable endpoint 3 */
- it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x3f);
- else
- it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x2f);
- it913x_wr_reg(d, DEV_0, EP4_TX_LEN_LSB,
- ep_size & 0xff);
- it913x_wr_reg(d, DEV_0, EP4_TX_LEN_MSB, ep_size >> 8);
- ret = it913x_wr_reg(d, DEV_0, EP4_MAX_PKT, pkt_size);
- } else if (adap->id == 1 && adap->fe[0]) {
- if (st->proprietary_ir == false)
- it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x7f);
- else
- it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x6f);
- it913x_wr_reg(d, DEV_0, EP5_TX_LEN_LSB,
- ep_size & 0xff);
- it913x_wr_reg(d, DEV_0, EP5_TX_LEN_MSB, ep_size >> 8);
- it913x_wr_reg(d, DEV_0, EP5_MAX_PKT, pkt_size);
- it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_EN, 0x1);
- it913x_wr_reg(d, DEV_1_DMOD, MP2IF_SERIAL, 0x1);
- it913x_wr_reg(d, DEV_1, TOP_HOSTB_SER_MODE, 0x1);
- it913x_wr_reg(d, DEV_0_DMOD, TSIS_ENABLE, 0x1);
- it913x_wr_reg(d, DEV_0_DMOD, MP2_SW_RST, 0x0);
- it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_SW_RST, 0x0);
- it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_HALF_PSB, 0x0);
- it913x_wr_reg(d, DEV_0_DMOD, MP2IF_STOP_EN, 0x1);
- it913x_wr_reg(d, DEV_1_DMOD, MPEG_FULL_SPEED, 0x0);
- ret = it913x_wr_reg(d, DEV_1_DMOD, MP2IF_STOP_EN, 0x0);
- } else
- return -ENODEV;
-
- ret |= it913x_name(adap);
-
- return ret;
-}
-
-/* DVB USB Driver */
-static int it913x_get_adapter_count(struct dvb_usb_device *d)
-{
- struct it913x_state *st = d->priv;
- if (st->it913x_config.dual_mode)
- return 2;
- return 1;
-}
-
-static struct dvb_usb_device_properties it913x_properties = {
- .driver_name = KBUILD_MODNAME,
- .owner = THIS_MODULE,
- .bInterfaceNumber = 0,
- .generic_bulk_ctrl_endpoint = 0x02,
- .generic_bulk_ctrl_endpoint_response = 0x81,
-
- .adapter_nr = adapter_nr,
- .size_of_priv = sizeof(struct it913x_state),
-
- .identify_state = it913x_identify_state,
- .i2c_algo = &it913x_i2c_algo,
-
- .download_firmware = it913x_download_firmware,
-
- .frontend_attach = it913x_frontend_attach,
- .get_rc_config = it913x_get_rc_config,
- .get_stream_config = it913x_get_stream_config,
- .get_adapter_count = it913x_get_adapter_count,
- .streaming_ctrl = it913x_streaming_ctrl,
-
-
- .adapter = {
- {
- .caps = DVB_USB_ADAP_HAS_PID_FILTER|
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
- .pid_filter_count = 32,
- .pid_filter = it913x_pid_filter,
- .pid_filter_ctrl = it913x_pid_filter_ctrl,
- .stream =
- DVB_USB_STREAM_BULK(0x84, 10, TS_BUFFER_SIZE_MAX),
- },
- {
- .caps = DVB_USB_ADAP_HAS_PID_FILTER|
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
- .pid_filter_count = 32,
- .pid_filter = it913x_pid_filter,
- .pid_filter_ctrl = it913x_pid_filter_ctrl,
- .stream =
- DVB_USB_STREAM_BULK(0x85, 10, TS_BUFFER_SIZE_MAX),
- }
- }
-};
-
-static const struct usb_device_id it913x_id_table[] = {
- { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09,
- &it913x_properties, "Kworld UB499-2T T09(IT9137)",
- RC_MAP_IT913X_V1) },
- { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135,
- &it913x_properties, "ITE 9135 Generic",
- RC_MAP_IT913X_V1) },
- { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22_IT9137,
- &it913x_properties, "Sveon STV22 Dual DVB-T HDTV(IT9137)",
- RC_MAP_IT913X_V1) },
- { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9005,
- &it913x_properties, "ITE 9135(9005) Generic",
- RC_MAP_IT913X_V2) },
- { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9006,
- &it913x_properties, "ITE 9135(9006) Generic",
- RC_MAP_IT913X_V1) },
- { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_1835,
- &it913x_properties, "Avermedia A835B(1835)",
- RC_MAP_IT913X_V2) },
- { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_2835,
- &it913x_properties, "Avermedia A835B(2835)",
- RC_MAP_IT913X_V2) },
- { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_3835,
- &it913x_properties, "Avermedia A835B(3835)",
- RC_MAP_IT913X_V2) },
- { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_4835,
- &it913x_properties, "Avermedia A835B(4835)",
- RC_MAP_IT913X_V2) },
- { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CTVDIGDUAL_V2,
- &it913x_properties, "Digital Dual TV Receiver CTVDIGDUAL_V2",
- RC_MAP_IT913X_V1) },
- { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_H335,
- &it913x_properties, "Avermedia H335",
- RC_MAP_IT913X_V2) },
- {} /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, it913x_id_table);
-
-static struct usb_driver it913x_driver = {
- .name = KBUILD_MODNAME,
- .probe = dvb_usbv2_probe,
- .disconnect = dvb_usbv2_disconnect,
- .suspend = dvb_usbv2_suspend,
- .resume = dvb_usbv2_resume,
- .id_table = it913x_id_table,
-};
-
-module_usb_driver(it913x_driver);
-
-MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
-MODULE_DESCRIPTION("it913x USB 2 Driver");
-MODULE_VERSION("1.33");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(FW_IT9135_V1);
-MODULE_FIRMWARE(FW_IT9135_V2);
-MODULE_FIRMWARE(FW_IT9137);
-
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index fda5c64ba0e8..c83c16cece01 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -24,6 +24,7 @@
#include "rtl2830.h"
#include "rtl2832.h"
+#include "rtl2832_sdr.h"
#include "qt1010.h"
#include "mt2060.h"
@@ -35,6 +36,9 @@
#include "tua9001.h"
#include "r820t.h"
+static int rtl28xxu_disable_rc;
+module_param_named(disable_rc, rtl28xxu_disable_rc, int, 0644);
+MODULE_PARM_DESC(disable_rc, "disable RTL2832U remote controller");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req)
@@ -513,7 +517,7 @@ err:
return ret;
}
-static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
+static const struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
.i2c_addr = 0x10, /* 0x20 */
.xtal = 28800000,
.ts_mode = 0,
@@ -524,7 +528,7 @@ static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
};
-static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
+static const struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
.i2c_addr = 0x10, /* 0x20 */
.xtal = 28800000,
.ts_mode = 0,
@@ -534,7 +538,7 @@ static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
.agc_targ_val = 0x2d,
};
-static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
+static const struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
.i2c_addr = 0x10, /* 0x20 */
.xtal = 28800000,
.ts_mode = 0,
@@ -548,7 +552,7 @@ static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
{
struct dvb_usb_device *d = adap_to_d(adap);
struct rtl28xxu_priv *priv = d_to_priv(d);
- struct rtl2830_config *rtl2830_config;
+ const struct rtl2830_config *rtl2830_config;
int ret;
dev_dbg(&d->udev->dev, "%s:\n", __func__);
@@ -583,33 +587,31 @@ err:
return ret;
}
-static struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = {
+static const struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = {
.i2c_addr = 0x10, /* 0x20 */
.xtal = 28800000,
- .if_dvbt = 0,
.tuner = TUNER_RTL2832_FC0012
};
-static struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = {
+static const struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = {
.i2c_addr = 0x10, /* 0x20 */
.xtal = 28800000,
- .if_dvbt = 0,
.tuner = TUNER_RTL2832_FC0013
};
-static struct rtl2832_config rtl28xxu_rtl2832_tua9001_config = {
+static const struct rtl2832_config rtl28xxu_rtl2832_tua9001_config = {
.i2c_addr = 0x10, /* 0x20 */
.xtal = 28800000,
.tuner = TUNER_RTL2832_TUA9001,
};
-static struct rtl2832_config rtl28xxu_rtl2832_e4000_config = {
+static const struct rtl2832_config rtl28xxu_rtl2832_e4000_config = {
.i2c_addr = 0x10, /* 0x20 */
.xtal = 28800000,
.tuner = TUNER_RTL2832_E4000,
};
-static struct rtl2832_config rtl28xxu_rtl2832_r820t_config = {
+static const struct rtl2832_config rtl28xxu_rtl2832_r820t_config = {
.i2c_addr = 0x10,
.xtal = 28800000,
.tuner = TUNER_RTL2832_R820T,
@@ -733,7 +735,7 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
int ret;
struct dvb_usb_device *d = adap_to_d(adap);
struct rtl28xxu_priv *priv = d_to_priv(d);
- struct rtl2832_config *rtl2832_config;
+ const struct rtl2832_config *rtl2832_config;
dev_dbg(&d->udev->dev, "%s:\n", __func__);
@@ -772,6 +774,9 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
goto err;
}
+ /* RTL2832 I2C repeater */
+ priv->demod_i2c_adapter = rtl2832_get_i2c_adapter(adap->fe[0]);
+
/* set fe callback */
adap->fe[0]->callback = rtl2832u_frontend_callback;
@@ -851,11 +856,6 @@ err:
return ret;
}
-static const struct e4000_config rtl2832u_e4000_config = {
- .i2c_addr = 0x64,
- .clock = 28800000,
-};
-
static const struct fc2580_config rtl2832u_fc2580_config = {
.i2c_addr = 0x56,
.clock = 16384000,
@@ -889,10 +889,14 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
int ret;
struct dvb_usb_device *d = adap_to_d(adap);
struct rtl28xxu_priv *priv = d_to_priv(d);
- struct dvb_frontend *fe;
+ struct dvb_frontend *fe = NULL;
+ struct i2c_board_info info;
+ struct i2c_client *client;
dev_dbg(&d->udev->dev, "%s:\n", __func__);
+ memset(&info, 0, sizeof(struct i2c_board_info));
+
switch (priv->tuner) {
case TUNER_RTL2832_FC0012:
fe = dvb_attach(fc0012_attach, adap->fe[0],
@@ -902,7 +906,10 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
* that to the tuner driver */
adap->fe[0]->ops.read_signal_strength =
adap->fe[0]->ops.tuner_ops.get_rf_strength;
- return 0;
+
+ /* attach SDR */
+ dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
+ &rtl28xxu_rtl2832_fc0012_config, NULL);
break;
case TUNER_RTL2832_FC0013:
fe = dvb_attach(fc0013_attach, adap->fe[0],
@@ -911,10 +918,43 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
/* fc0013 also supports signal strength reading */
adap->fe[0]->ops.read_signal_strength =
adap->fe[0]->ops.tuner_ops.get_rf_strength;
- return 0;
- case TUNER_RTL2832_E4000:
- fe = dvb_attach(e4000_attach, adap->fe[0], &d->i2c_adap,
- &rtl2832u_e4000_config);
+
+ /* attach SDR */
+ dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
+ &rtl28xxu_rtl2832_fc0013_config, NULL);
+ break;
+ case TUNER_RTL2832_E4000: {
+ struct v4l2_subdev *sd;
+ struct i2c_adapter *i2c_adap_internal =
+ rtl2832_get_private_i2c_adapter(adap->fe[0]);
+ struct e4000_config e4000_config = {
+ .fe = adap->fe[0],
+ .clock = 28800000,
+ };
+
+ strlcpy(info.type, "e4000", I2C_NAME_SIZE);
+ info.addr = 0x64;
+ info.platform_data = &e4000_config;
+
+ request_module(info.type);
+ client = i2c_new_device(priv->demod_i2c_adapter, &info);
+ if (client == NULL || client->dev.driver == NULL)
+ break;
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ break;
+ }
+
+ priv->client = client;
+ sd = i2c_get_clientdata(client);
+ i2c_set_adapdata(i2c_adap_internal, d);
+
+ /* attach SDR */
+ dvb_attach(rtl2832_sdr_attach, adap->fe[0],
+ i2c_adap_internal,
+ &rtl28xxu_rtl2832_e4000_config, sd);
+ }
break;
case TUNER_RTL2832_FC2580:
fe = dvb_attach(fc2580_attach, adap->fe[0], &d->i2c_adap,
@@ -940,6 +980,10 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
/* Use tuner to get the signal strength */
adap->fe[0]->ops.read_signal_strength =
adap->fe[0]->ops.tuner_ops.get_rf_strength;
+
+ /* attach SDR */
+ dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
+ &rtl28xxu_rtl2832_r820t_config, NULL);
break;
case TUNER_RTL2832_R828D:
/* power off mn88472 demod on GPIO0 */
@@ -963,12 +1007,11 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
adap->fe[0]->ops.tuner_ops.get_rf_strength;
break;
default:
- fe = NULL;
dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME,
priv->tuner);
}
- if (fe == NULL) {
+ if (fe == NULL && priv->client == NULL) {
ret = -ENODEV;
goto err;
}
@@ -1013,6 +1056,22 @@ err:
return ret;
}
+static void rtl28xxu_exit(struct dvb_usb_device *d)
+{
+ struct rtl28xxu_priv *priv = d->priv;
+ struct i2c_client *client = priv->client;
+
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ /* remove I2C tuner */
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+
+ return;
+}
+
static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff)
{
int ret;
@@ -1322,6 +1381,10 @@ err:
static int rtl2832u_get_rc_config(struct dvb_usb_device *d,
struct dvb_usb_rc *rc)
{
+ /* disable IR interrupts in order to avoid SDR sample loss */
+ if (rtl28xxu_disable_rc)
+ return rtl28xx_wr_reg(d, IR_RX_IE, 0x00);
+
/* load empty to enable rc */
if (!rc->map_name)
rc->map_name = RC_MAP_EMPTY;
@@ -1371,6 +1434,7 @@ static const struct dvb_usb_device_properties rtl2832u_props = {
.frontend_attach = rtl2832u_frontend_attach,
.tuner_attach = rtl2832u_tuner_attach,
.init = rtl28xxu_init,
+ .exit = rtl28xxu_exit,
.get_rc_config = rtl2832u_get_rc_config,
.num_adapters = 1,
@@ -1382,6 +1446,7 @@ static const struct dvb_usb_device_properties rtl2832u_props = {
};
static const struct usb_device_id rtl28xxu_id_table[] = {
+ /* RTL2831U devices: */
{ DVB_USB_DEVICE(USB_VID_REALTEK, USB_PID_REALTEK_RTL2831U,
&rtl2831u_props, "Realtek RTL2831U reference design", NULL) },
{ DVB_USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT,
@@ -1389,6 +1454,7 @@ static const struct usb_device_id rtl28xxu_id_table[] = {
{ DVB_USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2,
&rtl2831u_props, "Freecom USB2.0 DVB-T", NULL) },
+ /* RTL2832U devices: */
{ DVB_USB_DEVICE(USB_VID_REALTEK, 0x2832,
&rtl2832u_props, "Realtek RTL2832U reference design", NULL) },
{ DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838,
@@ -1401,6 +1467,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = {
&rtl2832u_props, "TerraTec NOXON DAB Stick", NULL) },
{ DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK_REV2,
&rtl2832u_props, "TerraTec NOXON DAB Stick (rev 2)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK_REV3,
+ &rtl2832u_props, "TerraTec NOXON DAB Stick (rev 3)", NULL) },
{ DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TREKSTOR_TERRES_2_0,
&rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) },
{ DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1101,
@@ -1429,9 +1497,14 @@ static const struct usb_device_id rtl28xxu_id_table[] = {
&rtl2832u_props, "Leadtek WinFast DTV Dongle mini", NULL) },
{ DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_CPYTO_REDI_PC50A,
&rtl2832u_props, "Crypto ReDi PC 50 A", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KYE, 0x707f,
+ &rtl2832u_props, "Genius TVGo DVB-T03", NULL) },
+ /* RTL2832P devices: */
{ DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131,
&rtl2832u_props, "Astrometa DVB-T2", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KYE, 0x707f,
+ &rtl2832u_props, "Genius TVGo DVB-T03", NULL) },
{ }
};
MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table);
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
index 2142bcb41b41..a26cab10f382 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
@@ -55,7 +55,9 @@ struct rtl28xxu_priv {
u8 tuner;
char *tuner_name;
u8 page; /* integrated demod active register page */
+ struct i2c_adapter *demod_i2c_adapter;
bool rc_active;
+ struct i2c_client *client;
};
enum rtl28xxu_chip_id {
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
index 41bacff24960..4058aea9272f 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
@@ -272,7 +272,7 @@ static int rc_core_dvb_usb_remote_init(struct dvb_usb_device *d)
dev->driver_name = d->props.rc.core.module_name;
dev->map_name = d->props.rc.core.rc_codes;
dev->change_protocol = d->props.rc.core.change_protocol;
- dev->allowed_protos = d->props.rc.core.allowed_protos;
+ rc_set_allowed_protocols(dev, d->props.rc.core.allowed_protos);
dev->driver_type = d->props.rc.core.driver_type;
usb_to_input_id(d->udev, &dev->input_id);
dev->input_name = "IR-receiver inside an USB DVB receiver";
diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig
index a1fccf3096de..d23a912096f7 100644
--- a/drivers/media/usb/em28xx/Kconfig
+++ b/drivers/media/usb/em28xx/Kconfig
@@ -53,8 +53,10 @@ config VIDEO_EM28XX_DVB
select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DRX39XYJ if MEDIA_SUBDRV_AUTOSELECT
---help---
This adds support for DVB cards based on the
Empiatech em28xx chips.
diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c
index 1a28897af183..342490f44ed2 100644
--- a/drivers/media/usb/em28xx/em28xx-audio.c
+++ b/drivers/media/usb/em28xx/em28xx-audio.c
@@ -252,7 +252,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- int ret = 0;
+ int nonblock, ret = 0;
if (!dev) {
em28xx_err("BUG: em28xx can't find device struct."
@@ -265,45 +265,48 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
dprintk("opening device and trying to acquire exclusive lock\n");
+ nonblock = !!(substream->f_flags & O_NONBLOCK);
+ if (nonblock) {
+ if (!mutex_trylock(&dev->lock))
+ return -EAGAIN;
+ } else
+ mutex_lock(&dev->lock);
+
runtime->hw = snd_em28xx_hw_capture;
- if ((dev->alt == 0 || dev->is_audio_only) && dev->adev.users == 0) {
- int nonblock = !!(substream->f_flags & O_NONBLOCK);
-
- if (nonblock) {
- if (!mutex_trylock(&dev->lock))
- return -EAGAIN;
- } else
- mutex_lock(&dev->lock);
- if (dev->is_audio_only)
- /* vendor audio is on a separate interface */
- dev->alt = 1;
- else
- /* vendor audio is on the same interface as video */
- dev->alt = 7;
- /*
- * FIXME: The intention seems to be to select the alt
- * setting with the largest wMaxPacketSize for the video
- * endpoint.
- * At least dev->alt should be used instead, but we
- * should probably not touch it at all if it is
- * already >0, because wMaxPacketSize of the audio
- * endpoints seems to be the same for all.
- */
-
- dprintk("changing alternate number on interface %d to %d\n",
- dev->ifnum, dev->alt);
- usb_set_interface(dev->udev, dev->ifnum, dev->alt);
+
+ if (dev->adev.users == 0) {
+ if (dev->alt == 0 || dev->is_audio_only) {
+ if (dev->is_audio_only)
+ /* audio is on a separate interface */
+ dev->alt = 1;
+ else
+ /* audio is on the same interface as video */
+ dev->alt = 7;
+ /*
+ * FIXME: The intention seems to be to select
+ * the alt setting with the largest
+ * wMaxPacketSize for the video endpoint.
+ * At least dev->alt should be used instead, but
+ * we should probably not touch it at all if it
+ * is already >0, because wMaxPacketSize of the
+ * audio endpoints seems to be the same for all.
+ */
+ dprintk("changing alternate number on interface %d to %d\n",
+ dev->ifnum, dev->alt);
+ usb_set_interface(dev->udev, dev->ifnum, dev->alt);
+ }
/* Sets volume, mute, etc */
dev->mute = 0;
ret = em28xx_audio_analog_set(dev);
if (ret < 0)
goto err;
-
- dev->adev.users++;
- mutex_unlock(&dev->lock);
}
+ kref_get(&dev->ref);
+ dev->adev.users++;
+ mutex_unlock(&dev->lock);
+
/* Dynamically adjust the period size */
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
@@ -341,6 +344,7 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
substream->runtime->dma_area = NULL;
}
mutex_unlock(&dev->lock);
+ kref_put(&dev->ref, em28xx_free_device);
return 0;
}
@@ -895,6 +899,8 @@ static int em28xx_audio_init(struct em28xx *dev)
em28xx_info("Binding audio extension\n");
+ kref_get(&dev->ref);
+
printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
"Rechberger\n");
printk(KERN_INFO
@@ -966,7 +972,7 @@ static int em28xx_audio_fini(struct em28xx *dev)
if (dev == NULL)
return 0;
- if (dev->has_alsa_audio != 1) {
+ if (!dev->has_alsa_audio) {
/* This device does not support the extension (in this case
the device is expecting the snd-usb-audio module or
doesn't have analog audio support at all) */
@@ -985,6 +991,35 @@ static int em28xx_audio_fini(struct em28xx *dev)
dev->adev.sndcard = NULL;
}
+ kref_put(&dev->ref, em28xx_free_device);
+ return 0;
+}
+
+static int em28xx_audio_suspend(struct em28xx *dev)
+{
+ if (dev == NULL)
+ return 0;
+
+ if (!dev->has_alsa_audio)
+ return 0;
+
+ em28xx_info("Suspending audio extension");
+ em28xx_deinit_isoc_audio(dev);
+ atomic_set(&dev->stream_started, 0);
+ return 0;
+}
+
+static int em28xx_audio_resume(struct em28xx *dev)
+{
+ if (dev == NULL)
+ return 0;
+
+ if (!dev->has_alsa_audio)
+ return 0;
+
+ em28xx_info("Resuming audio extension");
+ /* Nothing to do other than schedule_work() ?? */
+ schedule_work(&dev->wq_trigger);
return 0;
}
@@ -993,6 +1028,8 @@ static struct em28xx_ops audio_ops = {
.name = "Em28xx Audio Extension",
.init = em28xx_audio_init,
.fini = em28xx_audio_fini,
+ .suspend = em28xx_audio_suspend,
+ .resume = em28xx_audio_resume,
};
static int __init em28xx_alsa_register(void)
@@ -1007,7 +1044,7 @@ static void __exit em28xx_alsa_unregister(void)
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_DESCRIPTION(DRIVER_DESC " - audio interface");
MODULE_VERSION(EM28XX_VERSION);
diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c
index c29f5c4e7b40..505e0505be04 100644
--- a/drivers/media/usb/em28xx/em28xx-camera.c
+++ b/drivers/media/usb/em28xx/em28xx-camera.c
@@ -120,7 +120,7 @@ static int em28xx_probe_sensor_micron(struct em28xx *dev)
reg = 0x00;
ret = i2c_master_send(&client, &reg, 1);
if (ret < 0) {
- if (ret != -ENODEV)
+ if (ret != -ENXIO)
em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n",
client.addr << 1, ret);
continue;
@@ -218,7 +218,7 @@ static int em28xx_probe_sensor_omnivision(struct em28xx *dev)
reg = 0x1c;
ret = i2c_smbus_read_byte_data(&client, reg);
if (ret < 0) {
- if (ret != -ENODEV)
+ if (ret != -ENXIO)
em28xx_errdev("couldn't read from i2c device 0x%02x: error %i\n",
client.addr << 1, ret);
continue;
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 4d97a76cc3b0..50aa5a5317f2 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -66,7 +66,7 @@ MODULE_PARM_DESC(usb_xfer_mode,
/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */
-DECLARE_BITMAP(em28xx_devused, EM28XX_MAXBOARDS);
+static DECLARE_BITMAP(em28xx_devused, EM28XX_MAXBOARDS);
struct em28xx_hash_table {
unsigned long hash;
@@ -189,6 +189,14 @@ static struct em28xx_reg_seq kworld_a340_digital[] = {
{ -1, -1, -1, -1},
};
+static struct em28xx_reg_seq kworld_ub435q_v3_digital[] = {
+ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xfe, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xbe, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xfe, 0xff, 100},
+ { -1, -1, -1, -1},
+};
+
/* Pinnacle Hybrid Pro eb1a:2881 */
static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
{EM2820_R08_GPIO_CTRL, 0xfd, ~EM_GPIO_4, 10},
@@ -214,6 +222,17 @@ static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = {
{ -1, -1, -1, -1},
};
+/* PCTV HD Mini (80e) GPIOs
+ 0-5: not used
+ 6: demod reset, active low
+ 7: LED on, active high */
+static struct em28xx_reg_seq em2874_pctv_80e_digital[] = {
+ {EM28XX_R06_I2C_CLK, 0x45, 0xff, 10}, /*400 KHz*/
+ {EM2874_R80_GPIO_P0_CTRL, 0x00, 0xff, 100},/*Demod reset*/
+ {EM2874_R80_GPIO_P0_CTRL, 0x40, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
/* eb1a:2868 Reddo DVB-C USB TV Box
GPIO4 - CU1216L NIM
Other GPIOs seems to be don't care. */
@@ -497,6 +516,27 @@ static struct em28xx_led speedlink_vad_laplace_leds[] = {
{-1, 0, 0, 0},
};
+static struct em28xx_led kworld_ub435q_v3_leds[] = {
+ {
+ .role = EM28XX_LED_DIGITAL_CAPTURING,
+ .gpio_reg = EM2874_R80_GPIO_P0_CTRL,
+ .gpio_mask = 0x80,
+ .inverted = 1,
+ },
+ {-1, 0, 0, 0},
+};
+
+static struct em28xx_led pctv_80e_leds[] = {
+ {
+ .role = EM28XX_LED_DIGITAL_CAPTURING,
+ .gpio_reg = EM2874_R80_GPIO_P0_CTRL,
+ .gpio_mask = 0x80,
+ .inverted = 0,
+ },
+ {-1, 0, 0, 0},
+};
+
+
/*
* Board definitions
*/
@@ -2128,6 +2168,29 @@ struct em28xx_board em28xx_boards[] = {
.tuner_gpio = default_tuner_gpio,
.def_i2c_bus = 1,
},
+ /*
+ * 1b80:e34c KWorld USB ATSC TV Stick UB435-Q V3
+ * Empia EM2874B + LG DT3305 + NXP TDA18271HDC2
+ */
+ [EM2874_BOARD_KWORLD_UB435Q_V3] = {
+ .name = "KWorld USB ATSC TV Stick UB435-Q V3",
+ .tuner_type = TUNER_ABSENT,
+ .has_dvb = 1,
+ .tuner_gpio = kworld_ub435q_v3_digital,
+ .def_i2c_bus = 1,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_100_KHZ,
+ .leds = kworld_ub435q_v3_leds,
+ },
+ [EM2874_BOARD_PCTV_HD_MINI_80E] = {
+ .name = "Pinnacle PCTV HD Mini",
+ .tuner_type = TUNER_ABSENT,
+ .has_dvb = 1,
+ .dvb_gpio = em2874_pctv_80e_digital,
+ .decoder = EM28XX_NODECODER,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ .leds = pctv_80e_leds,
+ },
/* 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam
* Empia EM2765 + OmniVision OV2640 */
[EM2765_BOARD_SPEEDLINK_VAD_LAPLACE] = {
@@ -2290,6 +2353,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO_330E },
{ USB_DEVICE(0x2304, 0x0227),
.driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO },
+ { USB_DEVICE(0x2304, 0x023f),
+ .driver_info = EM2874_BOARD_PCTV_HD_MINI_80E },
{ USB_DEVICE(0x0413, 0x6023),
.driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII },
{ USB_DEVICE(0x093b, 0xa003),
@@ -2304,6 +2369,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2870_BOARD_KWORLD_A340 },
{ USB_DEVICE(0x1b80, 0xe346),
.driver_info = EM2874_BOARD_KWORLD_UB435Q_V2 },
+ { USB_DEVICE(0x1b80, 0xe34c),
+ .driver_info = EM2874_BOARD_KWORLD_UB435Q_V3 },
{ USB_DEVICE(0x2013, 0x024f),
.driver_info = EM28174_BOARD_PCTV_290E },
{ USB_DEVICE(0x2013, 0x024c),
@@ -2872,7 +2939,7 @@ static void flush_request_modules(struct em28xx *dev)
* unregisters the v4l2,i2c and usb devices
* called when the device gets disconnected or at module unload
*/
-void em28xx_release_resources(struct em28xx *dev)
+static void em28xx_release_resources(struct em28xx *dev)
{
/*FIXME: I2C IR should be disconnected */
@@ -2889,7 +2956,27 @@ void em28xx_release_resources(struct em28xx *dev)
mutex_unlock(&dev->lock);
};
-EXPORT_SYMBOL_GPL(em28xx_release_resources);
+
+/**
+ * em28xx_free_device() - Free em28xx device
+ *
+ * @ref: struct kref for em28xx device
+ *
+ * This is called when all extensions and em28xx core unregisters a device
+ */
+void em28xx_free_device(struct kref *ref)
+{
+ struct em28xx *dev = kref_to_dev(ref);
+
+ em28xx_info("Freeing device\n");
+
+ if (!dev->disconnected)
+ em28xx_release_resources(dev);
+
+ kfree(dev->alt_max_pkt_size_isoc);
+ kfree(dev);
+}
+EXPORT_SYMBOL_GPL(em28xx_free_device);
/*
* em28xx_init_dev()
@@ -3331,8 +3418,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
if (has_video) {
if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk))
dev->analog_xfer_bulk = 1;
- em28xx_info("analog set to %s mode.\n",
- dev->analog_xfer_bulk ? "bulk" : "isoc");
+ em28xx_info("analog set to %s mode.\n",
+ dev->analog_xfer_bulk ? "bulk" : "isoc");
}
if (has_dvb) {
if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk))
@@ -3342,6 +3429,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
dev->dvb_xfer_bulk ? "bulk" : "isoc");
}
+ kref_init(&dev->ref);
+
request_modules(dev);
/* Should be the last thing to do, to avoid newer udev's to
@@ -3386,17 +3475,39 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
em28xx_close_extension(dev);
em28xx_release_resources(dev);
+ kref_put(&dev->ref, em28xx_free_device);
+}
- if (!dev->users) {
- kfree(dev->alt_max_pkt_size_isoc);
- kfree(dev);
- }
+static int em28xx_usb_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ struct em28xx *dev;
+
+ dev = usb_get_intfdata(interface);
+ if (!dev)
+ return 0;
+ em28xx_suspend_extension(dev);
+ return 0;
+}
+
+static int em28xx_usb_resume(struct usb_interface *interface)
+{
+ struct em28xx *dev;
+
+ dev = usb_get_intfdata(interface);
+ if (!dev)
+ return 0;
+ em28xx_resume_extension(dev);
+ return 0;
}
static struct usb_driver em28xx_usb_driver = {
.name = "em28xx",
.probe = em28xx_usb_probe,
.disconnect = em28xx_usb_disconnect,
+ .suspend = em28xx_usb_suspend,
+ .resume = em28xx_usb_resume,
+ .reset_resume = em28xx_usb_resume,
.id_table = em28xx_id_table,
};
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index 898fb9bd88a2..523d7e92bf47 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -619,6 +619,7 @@ EXPORT_SYMBOL_GPL(em28xx_find_led);
int em28xx_capture_start(struct em28xx *dev, int start)
{
int rc;
+ const struct em28xx_led *led = NULL;
if (dev->chip_id == CHIP_ID_EM2874 ||
dev->chip_id == CHIP_ID_EM2884 ||
@@ -643,6 +644,8 @@ int em28xx_capture_start(struct em28xx *dev, int start)
/* Enable video capture */
rc = em28xx_write_reg(dev, 0x48, 0x00);
+ if (rc < 0)
+ return rc;
if (dev->mode == EM28XX_ANALOG_MODE)
rc = em28xx_write_reg(dev,
@@ -650,6 +653,8 @@ int em28xx_capture_start(struct em28xx *dev, int start)
else
rc = em28xx_write_reg(dev,
EM28XX_R12_VINENABLE, 0x37);
+ if (rc < 0)
+ return rc;
msleep(6);
} else {
@@ -658,19 +663,16 @@ int em28xx_capture_start(struct em28xx *dev, int start)
}
}
- if (rc < 0)
- return rc;
-
- /* Switch (explicitly controlled) analog capturing LED on/off */
- if (dev->mode == EM28XX_ANALOG_MODE) {
- const struct em28xx_led *led;
+ if (dev->mode == EM28XX_ANALOG_MODE)
led = em28xx_find_led(dev, EM28XX_LED_ANALOG_CAPTURING);
- if (led)
- em28xx_write_reg_bits(dev, led->gpio_reg,
- (!start ^ led->inverted) ?
- ~led->gpio_mask : led->gpio_mask,
- led->gpio_mask);
- }
+ else
+ led = em28xx_find_led(dev, EM28XX_LED_DIGITAL_CAPTURING);
+
+ if (led)
+ em28xx_write_reg_bits(dev, led->gpio_reg,
+ (!start ^ led->inverted) ?
+ ~led->gpio_mask : led->gpio_mask,
+ led->gpio_mask);
return rc;
}
@@ -1106,3 +1108,31 @@ void em28xx_close_extension(struct em28xx *dev)
list_del(&dev->devlist);
mutex_unlock(&em28xx_devlist_mutex);
}
+
+int em28xx_suspend_extension(struct em28xx *dev)
+{
+ const struct em28xx_ops *ops = NULL;
+
+ em28xx_info("Suspending extensions");
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->suspend)
+ ops->suspend(dev);
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+ return 0;
+}
+
+int em28xx_resume_extension(struct em28xx *dev)
+{
+ const struct em28xx_ops *ops = NULL;
+
+ em28xx_info("Resuming extensions");
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->resume)
+ ops->resume(dev);
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+ return 0;
+}
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index a0a669e81362..f599b18ef7ca 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -41,6 +41,7 @@
#include "mt352.h"
#include "mt352_priv.h" /* FIXME */
#include "tda1002x.h"
+#include "drx39xyj/drx39xxj.h"
#include "tda18271.h"
#include "s921.h"
#include "drxd.h"
@@ -48,6 +49,7 @@
#include "tda18271c2dd.h"
#include "drxk.h"
#include "tda10071.h"
+#include "tda18212.h"
#include "a8293.h"
#include "qt1010.h"
#include "mb86a20s.h"
@@ -161,6 +163,8 @@ static inline int em28xx_dvb_urb_data_copy(struct em28xx *dev, struct urb *urb)
if (urb->status != -EPROTO)
continue;
}
+ if (!urb->actual_length)
+ continue;
dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer,
urb->actual_length);
} else {
@@ -170,6 +174,8 @@ static inline int em28xx_dvb_urb_data_copy(struct em28xx *dev, struct urb *urb)
if (urb->iso_frame_desc[i].status != -EPROTO)
continue;
}
+ if (!urb->iso_frame_desc[i].actual_length)
+ continue;
dvb_dmx_swfilter(&dev->dvb->demux,
urb->transfer_buffer +
urb->iso_frame_desc[i].offset,
@@ -208,10 +214,10 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb)
if (rc < 0)
return rc;
- dprintk(1, "Using %d buffers each with %d x %d bytes\n",
+ dprintk(1, "Using %d buffers each with %d x %d bytes, alternate %d\n",
EM28XX_DVB_NUM_BUFS,
packet_multiplier,
- dvb_max_packet_size);
+ dvb_max_packet_size, dvb_alt);
return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE,
dev->dvb_xfer_bulk,
@@ -315,6 +321,18 @@ static struct lgdt3305_config em2874_lgdt3305_dev = {
.qam_if_khz = 4000,
};
+static struct lgdt3305_config em2874_lgdt3305_nogate_dev = {
+ .i2c_addr = 0x0e,
+ .demod_chip = LGDT3305,
+ .spectral_inversion = 1,
+ .deny_i2c_rptr = 1,
+ .mpeg_mode = LGDT3305_MPEG_SERIAL,
+ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
+ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
+ .vsb_if_khz = 3600,
+ .qam_if_khz = 3600,
+};
+
static struct s921_config sharp_isdbt = {
.demod_address = 0x30 >> 1
};
@@ -351,6 +369,12 @@ static struct tda18271_config kworld_ub435q_v2_config = {
.gate = TDA18271_GATE_DIGITAL,
};
+static struct tda18212_config kworld_ub435q_v3_config = {
+ .i2c_address = 0x60,
+ .if_atsc_vsb = 3600,
+ .if_atsc_qam = 3600,
+};
+
static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = {
.demod_address = (0x1e >> 1),
.no_tuner = 1,
@@ -693,7 +717,8 @@ static void pctv_520e_init(struct em28xx *dev)
static int em28xx_pctv_290e_set_lna(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct em28xx *dev = fe->dvb->priv;
+ struct em28xx_i2c_bus *i2c_bus = fe->dvb->priv;
+ struct em28xx *dev = i2c_bus->dev;
#ifdef CONFIG_GPIOLIB
struct em28xx_dvb *dvb = dev->dvb;
int ret;
@@ -817,6 +842,20 @@ static const struct m88ds3103_config pctv_461e_m88ds3103_config = {
.agc = 0x99,
};
+
+static struct tda18271_std_map drx_j_std_map = {
+ .atsc_6 = { .if_freq = 5000, .agc_mode = 3, .std = 0, .if_lvl = 1,
+ .rfagc_top = 0x37, },
+ .qam_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3, .if_lvl = 1,
+ .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config pinnacle_80e_dvb_config = {
+ .std_map = &drx_j_std_map,
+ .gate = TDA18271_GATE_DIGITAL,
+ .role = TDA18271_MASTER,
+};
+
/* ------------------------------------------------------------------ */
static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev)
@@ -1005,7 +1044,6 @@ static int em28xx_dvb_init(struct em28xx *dev)
em28xx_info("Binding DVB extension\n");
dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL);
-
if (dvb == NULL) {
em28xx_info("em28xx_dvb: memory allocation failed\n");
return -ENOMEM;
@@ -1370,10 +1408,40 @@ static int em28xx_dvb_init(struct em28xx *dev)
goto out_free;
}
break;
+ case EM2874_BOARD_KWORLD_UB435Q_V3:
+ dvb->fe[0] = dvb_attach(lgdt3305_attach,
+ &em2874_lgdt3305_nogate_dev,
+ &dev->i2c_adap[dev->def_i2c_bus]);
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* Attach the demodulator. */
+ if (!dvb_attach(tda18212_attach, dvb->fe[0],
+ &dev->i2c_adap[dev->def_i2c_bus],
+ &kworld_ub435q_v3_config)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2874_BOARD_PCTV_HD_MINI_80E:
+ dvb->fe[0] = dvb_attach(drx39xxj_attach, &dev->i2c_adap[dev->def_i2c_bus]);
+ if (dvb->fe[0] != NULL) {
+ dvb->fe[0] = dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ &pinnacle_80e_dvb_config);
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+ break;
case EM28178_BOARD_PCTV_461E:
{
/* demod I2C adapter */
struct i2c_adapter *i2c_adapter;
+ struct i2c_client *client;
struct i2c_board_info info;
struct m88ts2022_config m88ts2022_config = {
.clock = 27000000,
@@ -1396,7 +1464,19 @@ static int em28xx_dvb_init(struct em28xx *dev)
info.addr = 0x60;
info.platform_data = &m88ts2022_config;
request_module("m88ts2022");
- dvb->i2c_client_tuner = i2c_new_device(i2c_adapter, &info);
+ client = i2c_new_device(i2c_adapter, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ dvb_frontend_detach(dvb->fe[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ dvb_frontend_detach(dvb->fe[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
/* delegate signal strength measurement to tuner */
dvb->fe[0]->ops.read_signal_strength =
@@ -1406,10 +1486,14 @@ static int em28xx_dvb_init(struct em28xx *dev)
if (!dvb_attach(a8293_attach, dvb->fe[0],
&dev->i2c_adap[dev->def_i2c_bus],
&em28xx_a8293_config)) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
dvb_frontend_detach(dvb->fe[0]);
result = -ENODEV;
goto out_free;
}
+
+ dvb->i2c_client_tuner = client;
}
break;
default:
@@ -1437,6 +1521,9 @@ static int em28xx_dvb_init(struct em28xx *dev)
dvb->adapter.mfe_shared = mfe_shared;
em28xx_info("DVB extension successfully initialized\n");
+
+ kref_get(&dev->ref);
+
ret:
em28xx_set_mode(dev, EM28XX_SUSPEND);
mutex_unlock(&dev->lock);
@@ -1457,6 +1544,9 @@ static inline void prevent_sleep(struct dvb_frontend_ops *ops)
static int em28xx_dvb_fini(struct em28xx *dev)
{
+ struct em28xx_dvb *dvb;
+ struct i2c_client *client;
+
if (dev->is_audio_only) {
/* Shouldn't initialize IR for this interface */
return 0;
@@ -1467,23 +1557,96 @@ static int em28xx_dvb_fini(struct em28xx *dev)
return 0;
}
+ if (!dev->dvb)
+ return 0;
+
em28xx_info("Closing DVB extension");
+ dvb = dev->dvb;
+ client = dvb->i2c_client_tuner;
+
+ em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE);
+
+ if (dev->disconnected) {
+ /* We cannot tell the device to sleep
+ * once it has been unplugged. */
+ if (dvb->fe[0])
+ prevent_sleep(&dvb->fe[0]->ops);
+ if (dvb->fe[1])
+ prevent_sleep(&dvb->fe[1]->ops);
+ }
+
+ /* remove I2C tuner */
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+
+ em28xx_unregister_dvb(dvb);
+ kfree(dvb);
+ dev->dvb = NULL;
+ kref_put(&dev->ref, em28xx_free_device);
+
+ return 0;
+}
+
+static int em28xx_dvb_suspend(struct em28xx *dev)
+{
+ int ret = 0;
+
+ if (dev->is_audio_only)
+ return 0;
+
+ if (!dev->board.has_dvb)
+ return 0;
+
+ em28xx_info("Suspending DVB extension");
if (dev->dvb) {
struct em28xx_dvb *dvb = dev->dvb;
- em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE);
+ if (dvb->fe[0]) {
+ ret = dvb_frontend_suspend(dvb->fe[0]);
+ em28xx_info("fe0 suspend %d", ret);
+ }
+ if (dvb->fe[1]) {
+ dvb_frontend_suspend(dvb->fe[1]);
+ em28xx_info("fe1 suspend %d", ret);
+ }
+ }
+
+ return 0;
+}
+
+static int em28xx_dvb_resume(struct em28xx *dev)
+{
+ int ret = 0;
- if (dev->disconnected) {
- /* We cannot tell the device to sleep
- * once it has been unplugged. */
- if (dvb->fe[0])
- prevent_sleep(&dvb->fe[0]->ops);
- if (dvb->fe[1])
- prevent_sleep(&dvb->fe[1]->ops);
+ if (dev->is_audio_only)
+ return 0;
+
+ if (!dev->board.has_dvb)
+ return 0;
+
+ em28xx_info("Resuming DVB extension");
+ if (dev->dvb) {
+ struct em28xx_dvb *dvb = dev->dvb;
+ struct i2c_client *client = dvb->i2c_client_tuner;
+
+ if (dvb->fe[0]) {
+ ret = dvb_frontend_resume(dvb->fe[0]);
+ em28xx_info("fe0 resume %d", ret);
+ }
+
+ if (dvb->fe[1]) {
+ ret = dvb_frontend_resume(dvb->fe[1]);
+ em28xx_info("fe1 resume %d", ret);
+ }
+ /* remove I2C tuner */
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
}
- i2c_release_client(dvb->i2c_client_tuner);
em28xx_unregister_dvb(dvb);
kfree(dvb);
dev->dvb = NULL;
@@ -1497,6 +1660,8 @@ static struct em28xx_ops dvb_ops = {
.name = "Em28xx dvb Extension",
.init = em28xx_dvb_init,
.fini = em28xx_dvb_fini,
+ .suspend = em28xx_dvb_suspend,
+ .resume = em28xx_dvb_resume,
};
static int __init em28xx_dvb_register(void)
diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c
index 7e1724076ac4..ba6433c3a643 100644
--- a/drivers/media/usb/em28xx/em28xx-i2c.c
+++ b/drivers/media/usb/em28xx/em28xx-i2c.c
@@ -81,7 +81,7 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len)
return len;
if (ret == 0x94 + len - 1) {
if (i2c_debug == 1)
- em28xx_warn("R05 returned 0x%02x: I2C timeout",
+ em28xx_warn("R05 returned 0x%02x: I2C ACK error\n",
ret);
return -ENXIO;
}
@@ -128,7 +128,7 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len)
break;
if (ret == 0x94 + len - 1) {
if (i2c_debug == 1)
- em28xx_warn("R05 returned 0x%02x: I2C timeout",
+ em28xx_warn("R05 returned 0x%02x: I2C ACK error\n",
ret);
return -ENXIO;
}
@@ -210,7 +210,7 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf,
return len;
if (ret == 0x10) {
if (i2c_debug == 1)
- em28xx_warn("I2C transfer timeout on writing to addr 0x%02x",
+ em28xx_warn("I2C ACK error on writing to addr 0x%02x\n",
addr);
return -ENXIO;
}
@@ -226,10 +226,18 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf,
* (even with high payload) ...
*/
}
- if (i2c_debug)
- em28xx_warn("write to i2c device at 0x%x timed out (status=%i)\n",
- addr, ret);
- return -ETIMEDOUT;
+
+ if (ret == 0x02 || ret == 0x04) {
+ /* NOTE: these errors seem to be related to clock stretching */
+ if (i2c_debug)
+ em28xx_warn("write to i2c device at 0x%x timed out (status=%i)\n",
+ addr, ret);
+ return -ETIMEDOUT;
+ }
+
+ em28xx_warn("write to i2c device at 0x%x failed with unknown error (status=%i)\n",
+ addr, ret);
+ return -EIO;
}
/*
@@ -274,13 +282,22 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len)
}
if (ret == 0x10) {
if (i2c_debug == 1)
- em28xx_warn("I2C transfer timeout on writing to addr 0x%02x",
+ em28xx_warn("I2C ACK error on writing to addr 0x%02x\n",
addr);
return -ENXIO;
}
- em28xx_warn("unknown i2c error (status=%i)\n", ret);
- return -ETIMEDOUT;
+ if (ret == 0x02 || ret == 0x04) {
+ /* NOTE: these errors seem to be related to clock stretching */
+ if (i2c_debug)
+ em28xx_warn("write to i2c device at 0x%x timed out (status=%i)\n",
+ addr, ret);
+ return -ETIMEDOUT;
+ }
+
+ em28xx_warn("write to i2c device at 0x%x failed with unknown error (status=%i)\n",
+ addr, ret);
+ return -EIO;
}
/*
@@ -337,7 +354,7 @@ static int em25xx_bus_B_send_bytes(struct em28xx *dev, u16 addr, u8 *buf,
return len;
else if (ret > 0) {
if (i2c_debug == 1)
- em28xx_warn("Bus B R08 returned 0x%02x: I2C timeout",
+ em28xx_warn("Bus B R08 returned 0x%02x: I2C ACK error\n",
ret);
return -ENXIO;
}
@@ -392,7 +409,7 @@ static int em25xx_bus_B_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf,
return len;
else if (ret > 0) {
if (i2c_debug == 1)
- em28xx_warn("Bus B R08 returned 0x%02x: I2C timeout",
+ em28xx_warn("Bus B R08 returned 0x%02x: I2C ACK error\n",
ret);
return -ENXIO;
}
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
index 18f65d89d4bc..56ef49df4f8d 100644
--- a/drivers/media/usb/em28xx/em28xx-input.c
+++ b/drivers/media/usb/em28xx/em28xx-input.c
@@ -676,6 +676,8 @@ static int em28xx_ir_init(struct em28xx *dev)
return 0;
}
+ kref_get(&dev->ref);
+
if (dev->board.buttons)
em28xx_init_buttons(dev);
@@ -725,7 +727,7 @@ static int em28xx_ir_init(struct em28xx *dev)
case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
rc->map_name = RC_MAP_HAUPPAUGE;
ir->get_key_i2c = em28xx_get_key_em_haup;
- rc->allowed_protos = RC_BIT_RC5;
+ rc_set_allowed_protocols(rc, RC_BIT_RC5);
break;
case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE:
rc->map_name = RC_MAP_WINFAST_USBII_DELUXE;
@@ -741,7 +743,7 @@ static int em28xx_ir_init(struct em28xx *dev)
switch (dev->chip_id) {
case CHIP_ID_EM2860:
case CHIP_ID_EM2883:
- rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC;
+ rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC);
ir->get_key = default_polling_getkey;
break;
case CHIP_ID_EM2884:
@@ -749,8 +751,8 @@ static int em28xx_ir_init(struct em28xx *dev)
case CHIP_ID_EM28174:
case CHIP_ID_EM28178:
ir->get_key = em2874_polling_getkey;
- rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC |
- RC_BIT_RC6_0;
+ rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC |
+ RC_BIT_RC6_0);
break;
default:
err = -ENODEV;
@@ -816,7 +818,7 @@ static int em28xx_ir_fini(struct em28xx *dev)
/* skip detach on non attached boards */
if (!ir)
- return 0;
+ goto ref_put;
if (ir->rc)
rc_unregister_device(ir->rc);
@@ -824,6 +826,45 @@ static int em28xx_ir_fini(struct em28xx *dev)
/* done */
kfree(ir);
dev->ir = NULL;
+
+ref_put:
+ kref_put(&dev->ref, em28xx_free_device);
+
+ return 0;
+}
+
+static int em28xx_ir_suspend(struct em28xx *dev)
+{
+ struct em28xx_IR *ir = dev->ir;
+
+ if (dev->is_audio_only)
+ return 0;
+
+ em28xx_info("Suspending input extension");
+ if (ir)
+ cancel_delayed_work_sync(&ir->work);
+ cancel_delayed_work_sync(&dev->buttons_query_work);
+ /* is canceling delayed work sufficient or does the rc event
+ kthread needs stopping? kthread is stopped in
+ ir_raw_event_unregister() */
+ return 0;
+}
+
+static int em28xx_ir_resume(struct em28xx *dev)
+{
+ struct em28xx_IR *ir = dev->ir;
+
+ if (dev->is_audio_only)
+ return 0;
+
+ em28xx_info("Resuming input extension");
+ /* if suspend calls ir_raw_event_unregister(), the should call
+ ir_raw_event_register() */
+ if (ir)
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
+ if (dev->num_button_polling_addresses)
+ schedule_delayed_work(&dev->buttons_query_work,
+ msecs_to_jiffies(dev->button_polling_interval));
return 0;
}
@@ -832,6 +873,8 @@ static struct em28xx_ops rc_ops = {
.name = "Em28xx Input Extension",
.init = em28xx_ir_init,
.fini = em28xx_ir_fini,
+ .suspend = em28xx_ir_suspend,
+ .resume = em28xx_ir_resume,
};
static int __init em28xx_rc_register(void)
@@ -845,7 +888,7 @@ static void __exit em28xx_rc_unregister(void)
}
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_DESCRIPTION(DRIVER_DESC " - input interface");
MODULE_VERSION(EM28XX_VERSION);
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index c3c928937dcd..0856e5d367b6 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -1029,7 +1029,7 @@ static int em28xx_vb2_setup(struct em28xx *dev)
q = &dev->vb_vidq;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct em28xx_buffer);
q->ops = &em28xx_video_qops;
@@ -1043,7 +1043,7 @@ static int em28xx_vb2_setup(struct em28xx *dev)
q = &dev->vb_vbiq;
q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct em28xx_buffer);
q->ops = &em28xx_vbi_qops;
@@ -1837,7 +1837,6 @@ static int em28xx_v4l2_open(struct file *filp)
video_device_node_name(vdev), v4l2_type_names[fh_type],
dev->users);
-
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
@@ -1869,6 +1868,7 @@ static int em28xx_v4l2_open(struct file *filp)
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
}
+ kref_get(&dev->ref);
dev->users++;
mutex_unlock(&dev->lock);
@@ -1918,18 +1918,43 @@ static int em28xx_v4l2_fini(struct em28xx *dev)
video_unregister_device(dev->vdev);
}
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ v4l2_device_unregister(&dev->v4l2_dev);
+
if (dev->clk) {
v4l2_clk_unregister_fixed(dev->clk);
dev->clk = NULL;
}
- v4l2_ctrl_handler_free(&dev->ctrl_handler);
- v4l2_device_unregister(&dev->v4l2_dev);
-
- if (dev->users)
- em28xx_warn("Device is open ! Memory deallocation is deferred on last close.\n");
mutex_unlock(&dev->lock);
+ kref_put(&dev->ref, em28xx_free_device);
+
+ return 0;
+}
+
+static int em28xx_v4l2_suspend(struct em28xx *dev)
+{
+ if (dev->is_audio_only)
+ return 0;
+ if (!dev->has_video)
+ return 0;
+
+ em28xx_info("Suspending video extension");
+ em28xx_stop_urbs(dev);
+ return 0;
+}
+
+static int em28xx_v4l2_resume(struct em28xx *dev)
+{
+ if (dev->is_audio_only)
+ return 0;
+
+ if (!dev->has_video)
+ return 0;
+
+ em28xx_info("Resuming video extension");
+ /* what do we do here */
return 0;
}
@@ -1950,11 +1975,9 @@ static int em28xx_v4l2_close(struct file *filp)
mutex_lock(&dev->lock);
if (dev->users == 1) {
- /* free the remaining resources if device is disconnected */
- if (dev->disconnected) {
- kfree(dev->alt_max_pkt_size_isoc);
+ /* No sense to try to write to the device */
+ if (dev->disconnected)
goto exit;
- }
/* Save some power by putting tuner to sleep */
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
@@ -1975,6 +1998,8 @@ static int em28xx_v4l2_close(struct file *filp)
exit:
dev->users--;
mutex_unlock(&dev->lock);
+ kref_put(&dev->ref, em28xx_free_device);
+
return 0;
}
@@ -2273,7 +2298,8 @@ static int em28xx_v4l2_init(struct em28xx *dev)
}
em28xx_tuner_setup(dev);
- em28xx_init_camera(dev);
+ if (dev->em28xx_sensor != EM28XX_NOSENSOR)
+ em28xx_init_camera(dev);
/* Configure audio */
ret = em28xx_audio_setup(dev);
@@ -2488,6 +2514,8 @@ static int em28xx_v4l2_init(struct em28xx *dev)
em28xx_info("V4L2 extension successfully initialized\n");
+ kref_get(&dev->ref);
+
mutex_unlock(&dev->lock);
return 0;
@@ -2504,6 +2532,8 @@ static struct em28xx_ops v4l2_ops = {
.name = "Em28xx v4l2 Extension",
.init = em28xx_v4l2_init,
.fini = em28xx_v4l2_fini,
+ .suspend = em28xx_v4l2_suspend,
+ .resume = em28xx_v4l2_resume,
};
static int __init em28xx_video_register(void)
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index 32d8a4bb7961..2051fc9fb932 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -32,6 +32,7 @@
#include <linux/workqueue.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
+#include <linux/kref.h>
#include <linux/videodev2.h>
#include <media/videobuf2-vmalloc.h>
@@ -104,6 +105,7 @@
#define EM2882_BOARD_PINNACLE_HYBRID_PRO_330E 56
#define EM2883_BOARD_KWORLD_HYBRID_330U 57
#define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58
+#define EM2874_BOARD_PCTV_HD_MINI_80E 59
#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60
#define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61
#define EM2820_BOARD_GADMEI_TVR200 62
@@ -137,6 +139,7 @@
#define EM2874_BOARD_KWORLD_UB435Q_V2 90
#define EM2765_BOARD_SPEEDLINK_VAD_LAPLACE 91
#define EM28178_BOARD_PCTV_461E 92
+#define EM2874_BOARD_KWORLD_UB435Q_V3 93
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -399,6 +402,7 @@ enum em28xx_adecoder {
enum em28xx_led_role {
EM28XX_LED_ANALOG_CAPTURING = 0,
+ EM28XX_LED_DIGITAL_CAPTURING,
EM28XX_LED_ILLUMINATION,
EM28XX_NUM_LED_ROLES, /* must be the last */
};
@@ -533,9 +537,10 @@ struct em28xx_i2c_bus {
enum em28xx_i2c_algo_type algo_type;
};
-
/* main device struct */
struct em28xx {
+ struct kref ref;
+
/* generic device properties */
char name[30]; /* name (including minor) of the device */
int model; /* index in the device_data struct */
@@ -707,12 +712,16 @@ struct em28xx {
struct em28xx_dvb *dvb;
};
+#define kref_to_dev(d) container_of(d, struct em28xx, ref)
+
struct em28xx_ops {
struct list_head next;
char *name;
int id;
int (*init)(struct em28xx *);
int (*fini)(struct em28xx *);
+ int (*suspend)(struct em28xx *);
+ int (*resume)(struct em28xx *);
};
/* Provided by em28xx-i2c.c */
@@ -758,13 +767,15 @@ int em28xx_register_extension(struct em28xx_ops *dev);
void em28xx_unregister_extension(struct em28xx_ops *dev);
void em28xx_init_extension(struct em28xx *dev);
void em28xx_close_extension(struct em28xx *dev);
+int em28xx_suspend_extension(struct em28xx *dev);
+int em28xx_resume_extension(struct em28xx *dev);
/* Provided by em28xx-cards.c */
extern struct em28xx_board em28xx_boards[];
extern struct usb_device_id em28xx_id_table[];
int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl);
-void em28xx_release_resources(struct em28xx *dev);
+void em28xx_free_device(struct kref *ref);
/* Provided by em28xx-camera.c */
int em28xx_detect_sensor(struct em28xx *dev);
diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c
index 3773a8a745df..081f05162809 100644
--- a/drivers/media/usb/gspca/kinect.c
+++ b/drivers/media/usb/gspca/kinect.c
@@ -155,10 +155,11 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
do {
actual_len = kinect_read(udev, ibuf, 0x200);
} while (actual_len == 0);
- PDEBUG(D_USBO, "Control reply: %d", res);
+ PDEBUG(D_USBO, "Control reply: %d", actual_len);
if (actual_len < sizeof(*rhdr)) {
- pr_err("send_cmd: Input control transfer failed (%d)\n", res);
- return res;
+ pr_err("send_cmd: Input control transfer failed (%d)\n",
+ actual_len);
+ return actual_len < 0 ? actual_len : -EREMOTEIO;
}
actual_len -= sizeof(*rhdr);
diff --git a/drivers/media/usb/gspca/sn9c20x.c b/drivers/media/usb/gspca/sn9c20x.c
index 2a38621cf718..41a9a892f79c 100644
--- a/drivers/media/usb/gspca/sn9c20x.c
+++ b/drivers/media/usb/gspca/sn9c20x.c
@@ -2359,6 +2359,7 @@ static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)},
{USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)},
{USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0x0458, 0x7045), SN9C20X(MT9M112, 0x5d, LED_REVERSE)},
{USB_DEVICE(0x0458, 0x704a), SN9C20X(MT9M112, 0x5d, 0)},
{USB_DEVICE(0x0458, 0x704c), SN9C20X(MT9M112, 0x5d, 0)},
{USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)},
diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c
index bf3e5c317a26..e60cbb3aa609 100644
--- a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c
@@ -178,7 +178,7 @@ static int vv6410_stop(struct sd *sd)
PDEBUG(D_STREAM, "Halting stream");
- return (err < 0) ? err : 0;
+ return 0;
}
static int vv6410_dump(struct sd *sd)
diff --git a/drivers/media/usb/gspca/topro.c b/drivers/media/usb/gspca/topro.c
index 640c2fe760b3..5fcd1eec2004 100644
--- a/drivers/media/usb/gspca/topro.c
+++ b/drivers/media/usb/gspca/topro.c
@@ -4631,8 +4631,16 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
data++;
len--;
+ if (len < 2) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
if (*data == 0xff && data[1] == 0xd8) {
/*fixme: there may be information in the 4 high bits*/
+ if (len < 7) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
if ((data[6] & 0x0f) != sd->quality)
set_dqt(gspca_dev, data[6] & 0x0f);
gspca_frame_add(gspca_dev, FIRST_PACKET,
@@ -4672,7 +4680,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_dev->last_packet_type = DISCARD_PACKET;
break;
case 0xcc:
- if (data[1] != 0xff || data[2] != 0xd8)
+ if (len >= 3 && (data[1] != 0xff || data[2] != 0xd8))
gspca_frame_add(gspca_dev, INTER_PACKET,
data + 1, len - 1);
else
diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
index abf365ab025d..84a6720b1d00 100644
--- a/drivers/media/usb/pwc/pwc-if.c
+++ b/drivers/media/usb/pwc/pwc-if.c
@@ -614,17 +614,20 @@ static int buffer_prepare(struct vb2_buffer *vb)
return 0;
}
-static int buffer_finish(struct vb2_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
{
struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
- /*
- * Application has called dqbuf and is getting back a buffer we've
- * filled, take the pwc data we've stored in buf->data and decompress
- * it into a usable format, storing the result in the vb2_buffer
- */
- return pwc_decompress(pdev, buf);
+ if (vb->state == VB2_BUF_STATE_DONE) {
+ /*
+ * Application has called dqbuf and is getting back a buffer
+ * we've filled, take the pwc data we've stored in buf->data
+ * and decompress it into a usable format, storing the result
+ * in the vb2_buffer.
+ */
+ pwc_decompress(pdev, buf);
+ }
}
static void buffer_cleanup(struct vb2_buffer *vb)
@@ -1001,7 +1004,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf);
pdev->vb_queue.ops = &pwc_vb_queue_ops;
pdev->vb_queue.mem_ops = &vb2_vmalloc_memops;
- pdev->vb_queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ pdev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
rc = vb2_queue_init(&pdev->vb_queue);
if (rc < 0) {
PWC_ERROR("Oops, could not initialize vb2 queue.\n");
diff --git a/drivers/media/usb/s2255/Kconfig b/drivers/media/usb/s2255/Kconfig
index 7e8ee1f864ab..8c3fceef9a09 100644
--- a/drivers/media/usb/s2255/Kconfig
+++ b/drivers/media/usb/s2255/Kconfig
@@ -1,7 +1,7 @@
config USB_S2255
tristate "USB Sensoray 2255 video capture device"
depends on VIDEO_V4L2
- select VIDEOBUF_VMALLOC
+ select VIDEOBUF2_VMALLOC
default n
help
Say Y here if you want support for the Sensoray 2255 USB device.
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index 6bc9b8e19e20..1d4ba2b80490 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -1,7 +1,7 @@
/*
* s2255drv.c - a driver for the Sensoray 2255 USB video capture device
*
- * Copyright (C) 2007-2013 by Sensoray Company Inc.
+ * Copyright (C) 2007-2014 by Sensoray Company Inc.
* Dean Anderson
*
* Some video buffer code based on vivi driver:
@@ -45,14 +45,14 @@
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/usb.h>
-#include <media/videobuf-vmalloc.h>
+#include <media/videobuf2-vmalloc.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
-#define S2255_VERSION "1.23.1"
+#define S2255_VERSION "1.25.1"
#define FIRMWARE_FILE_NAME "f2255usb.bin"
/* default JPEG quality */
@@ -69,7 +69,7 @@
#define S2255_DSP_BOOTTIME 800
/* maximum time to wait for firmware to load (ms) */
#define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME)
-#define S2255_DEF_BUFS 16
+#define S2255_MIN_BUFS 2
#define S2255_SETMODE_TIMEOUT 500
#define S2255_VIDSTATUS_TIMEOUT 350
#define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL)
@@ -178,11 +178,6 @@ struct s2255_bufferi {
DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
DEF_HUE, 0, DEF_USB_BLOCK, 0}
-struct s2255_dmaqueue {
- struct list_head active;
- struct s2255_dev *dev;
-};
-
/* for firmware loading, fw_state */
#define S2255_FW_NOTLOADED 0
#define S2255_FW_LOADED_DSPWAIT 1
@@ -217,12 +212,14 @@ struct s2255_pipeinfo {
struct s2255_fmt; /*forward declaration */
struct s2255_dev;
-struct s2255_channel {
+/* 2255 video channel */
+struct s2255_vc {
+ struct s2255_dev *dev;
struct video_device vdev;
struct v4l2_ctrl_handler hdl;
struct v4l2_ctrl *jpegqual_ctrl;
int resources;
- struct s2255_dmaqueue vidq;
+ struct list_head buf_list;
struct s2255_bufferi buffer;
struct s2255_mode mode;
v4l2_std_id std;
@@ -232,8 +229,6 @@ struct s2255_channel {
struct v4l2_captureparm cap_parm;
int cur_frame;
int last_frame;
-
- int b_acquire;
/* allocated image size */
unsigned long req_image_size;
/* received packet size */
@@ -252,17 +247,22 @@ struct s2255_channel {
int vidstatus_ready;
unsigned int width;
unsigned int height;
+ enum v4l2_field field;
const struct s2255_fmt *fmt;
int idx; /* channel number on device, 0-3 */
+ struct vb2_queue vb_vidq;
+ struct mutex vb_lock; /* streaming lock */
+ spinlock_t qlock;
};
struct s2255_dev {
- struct s2255_channel channel[MAX_CHANNELS];
- struct v4l2_device v4l2_dev;
+ struct s2255_vc vc[MAX_CHANNELS];
+ struct v4l2_device v4l2_dev;
atomic_t num_channels;
int frames;
struct mutex lock; /* channels[].vdev.lock */
+ struct mutex cmdlock; /* protects cmdbuf */
struct usb_device *udev;
struct usb_interface *interface;
u8 read_endpoint;
@@ -272,10 +272,11 @@ struct s2255_dev {
u32 cc; /* current channel */
int frame_ready;
int chn_ready;
- spinlock_t slock;
/* dsp firmware version (f2255usb.bin) */
int dsp_fw_ver;
u16 pid; /* product id */
+#define S2255_CMDBUF_SIZE 512
+ __le32 *cmdbuf;
};
static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev)
@@ -292,19 +293,10 @@ struct s2255_fmt {
/* buffer for one video frame */
struct s2255_buffer {
/* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
- const struct s2255_fmt *fmt;
+ struct vb2_buffer vb;
+ struct list_head list;
};
-struct s2255_fh {
- /* this must be the first field in this struct */
- struct v4l2_fh fh;
- struct s2255_dev *dev;
- struct videobuf_queue vb_vidq;
- enum v4l2_buf_type type;
- struct s2255_channel *channel;
- int resources;
-};
/* current cypress EEPROM firmware version */
#define S2255_CUR_USB_FWVER ((3 << 8) | 12)
@@ -352,15 +344,14 @@ struct s2255_fh {
static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
static int debug;
-static int *s2255_debug = &debug;
static int s2255_start_readpipe(struct s2255_dev *dev);
static void s2255_stop_readpipe(struct s2255_dev *dev);
-static int s2255_start_acquire(struct s2255_channel *channel);
-static int s2255_stop_acquire(struct s2255_channel *channel);
-static void s2255_fillbuff(struct s2255_channel *chn, struct s2255_buffer *buf,
+static int s2255_start_acquire(struct s2255_vc *vc);
+static int s2255_stop_acquire(struct s2255_vc *vc);
+static void s2255_fillbuff(struct s2255_vc *vc, struct s2255_buffer *buf,
int jpgsize);
-static int s2255_set_mode(struct s2255_channel *chan, struct s2255_mode *mode);
+static int s2255_set_mode(struct s2255_vc *vc, struct s2255_mode *mode);
static int s2255_board_shutdown(struct s2255_dev *dev);
static void s2255_fwload_start(struct s2255_dev *dev, int reset);
static void s2255_destroy(struct s2255_dev *dev);
@@ -373,19 +364,11 @@ static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
#define s2255_dev_err(dev, fmt, arg...) \
dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg)
-#define dprintk(level, fmt, arg...) \
- do { \
- if (*s2255_debug >= (level)) { \
- printk(KERN_DEBUG S2255_DRIVER_NAME \
- ": " fmt, ##arg); \
- } \
- } while (0)
+#define dprintk(dev, level, fmt, arg...) \
+ v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
static struct usb_driver s2255_driver;
-/* Declare static vars that will be used as parameters */
-static unsigned int vid_limit = 16; /* Video memory limit, in Mb */
-
/* start video number */
static int video_nr = -1; /* /dev/videoN, -1 for autodetect */
@@ -394,8 +377,6 @@ static int jpeg_enable = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
-module_param(vid_limit, int, 0644);
-MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)");
module_param(video_nr, int, 0644);
MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
module_param(jpeg_enable, int, 0644);
@@ -444,27 +425,27 @@ static const struct s2255_fmt formats[] = {
}
};
-static int norm_maxw(struct s2255_channel *channel)
+static int norm_maxw(struct s2255_vc *vc)
{
- return (channel->std & V4L2_STD_525_60) ?
+ return (vc->std & V4L2_STD_525_60) ?
LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
}
-static int norm_maxh(struct s2255_channel *channel)
+static int norm_maxh(struct s2255_vc *vc)
{
- return (channel->std & V4L2_STD_525_60) ?
+ return (vc->std & V4L2_STD_525_60) ?
(NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
}
-static int norm_minw(struct s2255_channel *channel)
+static int norm_minw(struct s2255_vc *vc)
{
- return (channel->std & V4L2_STD_525_60) ?
+ return (vc->std & V4L2_STD_525_60) ?
LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
}
-static int norm_minh(struct s2255_channel *channel)
+static int norm_minh(struct s2255_vc *vc)
{
- return (channel->std & V4L2_STD_525_60) ?
+ return (vc->std & V4L2_STD_525_60) ?
(NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
}
@@ -498,7 +479,7 @@ static void planar422p_to_yuv_packed(const unsigned char *in,
static void s2255_reset_dsppower(struct s2255_dev *dev)
{
s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
- msleep(10);
+ msleep(20);
s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
msleep(600);
s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1);
@@ -510,9 +491,8 @@ static void s2255_reset_dsppower(struct s2255_dev *dev)
static void s2255_timer(unsigned long user_data)
{
struct s2255_fw *data = (struct s2255_fw *)user_data;
- dprintk(100, "%s\n", __func__);
if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
- printk(KERN_ERR "s2255: can't submit urb\n");
+ pr_err("s2255: can't submit urb\n");
atomic_set(&data->fw_state, S2255_FW_FAILED);
/* wake up anything waiting for the firmware */
wake_up(&data->wait_fw);
@@ -532,7 +512,6 @@ static void s2255_fwchunk_complete(struct urb *urb)
struct s2255_fw *data = urb->context;
struct usb_device *udev = urb->dev;
int len;
- dprintk(100, "%s: udev %p urb %p", __func__, udev, urb);
if (urb->status) {
dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
atomic_set(&data->fw_state, S2255_FW_FAILED);
@@ -559,9 +538,6 @@ static void s2255_fwchunk_complete(struct urb *urb)
if (len < CHUNK_SIZE)
memset(data->pfw_data, 0, CHUNK_SIZE);
- dprintk(100, "completed len %d, loaded %d \n", len,
- data->fw_loaded);
-
memcpy(data->pfw_data,
(char *) data->fw->data + data->fw_loaded, len);
@@ -576,36 +552,32 @@ static void s2255_fwchunk_complete(struct urb *urb)
return;
}
data->fw_loaded += len;
- } else {
+ } else
atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
- dprintk(100, "%s: firmware upload complete\n", __func__);
- }
return;
}
-static int s2255_got_frame(struct s2255_channel *channel, int jpgsize)
+static int s2255_got_frame(struct s2255_vc *vc, int jpgsize)
{
- struct s2255_dmaqueue *dma_q = &channel->vidq;
struct s2255_buffer *buf;
- struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
unsigned long flags = 0;
int rc = 0;
- spin_lock_irqsave(&dev->slock, flags);
- if (list_empty(&dma_q->active)) {
- dprintk(1, "No active queue to serve\n");
+ spin_lock_irqsave(&vc->qlock, flags);
+ if (list_empty(&vc->buf_list)) {
+ dprintk(dev, 1, "No active queue to serve\n");
rc = -1;
goto unlock;
}
- buf = list_entry(dma_q->active.next,
- struct s2255_buffer, vb.queue);
- list_del(&buf->vb.queue);
- v4l2_get_timestamp(&buf->vb.ts);
- s2255_fillbuff(channel, buf, jpgsize);
- wake_up(&buf->vb.done);
- dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i);
+ buf = list_entry(vc->buf_list.next,
+ struct s2255_buffer, list);
+ list_del(&buf->list);
+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+ s2255_fillbuff(vc, buf, jpgsize);
+ dprintk(dev, 2, "%s: [buf] [%p]\n", __func__, buf);
unlock:
- spin_unlock_irqrestore(&dev->slock, flags);
+ spin_unlock_irqrestore(&vc->qlock, flags);
return rc;
}
@@ -615,9 +587,9 @@ static const struct s2255_fmt *format_by_fourcc(int fourcc)
for (i = 0; i < ARRAY_SIZE(formats); i++) {
if (-1 == formats[i].fourcc)
continue;
- if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) ||
- (formats[i].fourcc == V4L2_PIX_FMT_MJPEG)))
- continue;
+ if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) ||
+ (formats[i].fourcc == V4L2_PIX_FMT_MJPEG)))
+ continue;
if (formats[i].fourcc == fourcc)
return formats + i;
}
@@ -632,56 +604,56 @@ static const struct s2255_fmt *format_by_fourcc(int fourcc)
* http://v4l.videotechnology.com/
*
*/
-static void s2255_fillbuff(struct s2255_channel *channel,
+static void s2255_fillbuff(struct s2255_vc *vc,
struct s2255_buffer *buf, int jpgsize)
{
int pos = 0;
const char *tmpbuf;
- char *vbuf = videobuf_to_vmalloc(&buf->vb);
+ char *vbuf = vb2_plane_vaddr(&buf->vb, 0);
unsigned long last_frame;
+ struct s2255_dev *dev = vc->dev;
if (!vbuf)
return;
- last_frame = channel->last_frame;
+ last_frame = vc->last_frame;
if (last_frame != -1) {
tmpbuf =
- (const char *)channel->buffer.frame[last_frame].lpvbits;
- switch (buf->fmt->fourcc) {
+ (const char *)vc->buffer.frame[last_frame].lpvbits;
+ switch (vc->fmt->fourcc) {
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
planar422p_to_yuv_packed((const unsigned char *)tmpbuf,
- vbuf, buf->vb.width,
- buf->vb.height,
- buf->fmt->fourcc);
+ vbuf, vc->width,
+ vc->height,
+ vc->fmt->fourcc);
break;
case V4L2_PIX_FMT_GREY:
- memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height);
+ memcpy(vbuf, tmpbuf, vc->width * vc->height);
break;
case V4L2_PIX_FMT_JPEG:
case V4L2_PIX_FMT_MJPEG:
- buf->vb.size = jpgsize;
- memcpy(vbuf, tmpbuf, buf->vb.size);
+ buf->vb.v4l2_buf.length = jpgsize;
+ memcpy(vbuf, tmpbuf, jpgsize);
break;
case V4L2_PIX_FMT_YUV422P:
memcpy(vbuf, tmpbuf,
- buf->vb.width * buf->vb.height * 2);
+ vc->width * vc->height * 2);
break;
default:
- printk(KERN_DEBUG "s2255: unknown format?\n");
+ pr_info("s2255: unknown format?\n");
}
- channel->last_frame = -1;
+ vc->last_frame = -1;
} else {
- printk(KERN_ERR "s2255: =======no frame\n");
+ pr_err("s2255: =======no frame\n");
return;
-
}
- dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n",
+ dprintk(dev, 2, "s2255fill at : Buffer 0x%08lx size= %d\n",
(unsigned long)vbuf, pos);
/* tell v4l buffer was filled */
-
- buf->vb.field_count = channel->frame_count * 2;
- v4l2_get_timestamp(&buf->vb.ts);
- buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.v4l2_buf.field = vc->field;
+ buf->vb.v4l2_buf.sequence = vc->frame_count;
+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
}
@@ -689,144 +661,82 @@ static void s2255_fillbuff(struct s2255_channel *channel,
Videobuf operations
------------------------------------------------------------------*/
-static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- struct s2255_fh *fh = vq->priv_data;
- struct s2255_channel *channel = fh->channel;
- *size = channel->width * channel->height * (channel->fmt->depth >> 3);
-
- if (0 == *count)
- *count = S2255_DEF_BUFS;
-
- if (*size * *count > vid_limit * 1024 * 1024)
- *count = (vid_limit * 1024 * 1024) / *size;
-
+ struct s2255_vc *vc = vb2_get_drv_priv(vq);
+ if (*nbuffers < S2255_MIN_BUFS)
+ *nbuffers = S2255_MIN_BUFS;
+ *nplanes = 1;
+ sizes[0] = vc->width * vc->height * (vc->fmt->depth >> 3);
return 0;
}
-static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf)
-{
- dprintk(4, "%s\n", __func__);
-
- videobuf_vmalloc_free(&buf->vb);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int buffer_prepare(struct vb2_buffer *vb)
{
- struct s2255_fh *fh = vq->priv_data;
- struct s2255_channel *channel = fh->channel;
+ struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue);
struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
- int rc;
- int w = channel->width;
- int h = channel->height;
- dprintk(4, "%s, field=%d\n", __func__, field);
- if (channel->fmt == NULL)
+ int w = vc->width;
+ int h = vc->height;
+ unsigned long size;
+
+ dprintk(vc->dev, 4, "%s\n", __func__);
+ if (vc->fmt == NULL)
return -EINVAL;
- if ((w < norm_minw(channel)) ||
- (w > norm_maxw(channel)) ||
- (h < norm_minh(channel)) ||
- (h > norm_maxh(channel))) {
- dprintk(4, "invalid buffer prepare\n");
+ if ((w < norm_minw(vc)) ||
+ (w > norm_maxw(vc)) ||
+ (h < norm_minh(vc)) ||
+ (h > norm_maxh(vc))) {
+ dprintk(vc->dev, 4, "invalid buffer prepare\n");
return -EINVAL;
}
- buf->vb.size = w * h * (channel->fmt->depth >> 3);
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) {
- dprintk(4, "invalid buffer prepare\n");
+ size = w * h * (vc->fmt->depth >> 3);
+ if (vb2_plane_size(vb, 0) < size) {
+ dprintk(vc->dev, 4, "invalid buffer prepare\n");
return -EINVAL;
}
- buf->fmt = channel->fmt;
- buf->vb.width = w;
- buf->vb.height = h;
- buf->vb.field = field;
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- rc = videobuf_iolock(vq, &buf->vb, NULL);
- if (rc < 0)
- goto fail;
- }
-
- buf->vb.state = VIDEOBUF_PREPARED;
+ vb2_set_plane_payload(&buf->vb, 0, size);
return 0;
-fail:
- free_buffer(vq, buf);
- return rc;
}
-static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+static void buffer_queue(struct vb2_buffer *vb)
{
struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
- struct s2255_fh *fh = vq->priv_data;
- struct s2255_channel *channel = fh->channel;
- struct s2255_dmaqueue *vidq = &channel->vidq;
- dprintk(1, "%s\n", __func__);
- buf->vb.state = VIDEOBUF_QUEUED;
- list_add_tail(&buf->vb.queue, &vidq->active);
+ struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long flags = 0;
+ dprintk(vc->dev, 1, "%s\n", __func__);
+ spin_lock_irqsave(&vc->qlock, flags);
+ list_add_tail(&buf->list, &vc->buf_list);
+ spin_unlock_irqrestore(&vc->qlock, flags);
}
-static void buffer_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
- struct s2255_fh *fh = vq->priv_data;
- dprintk(4, "%s %d\n", __func__, fh->channel->idx);
- free_buffer(vq, buf);
-}
+static int start_streaming(struct vb2_queue *vq, unsigned int count);
+static int stop_streaming(struct vb2_queue *vq);
-static struct videobuf_queue_ops s2255_video_qops = {
- .buf_setup = buffer_setup,
+static struct vb2_ops s2255_video_qops = {
+ .queue_setup = queue_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
- .buf_release = buffer_release,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
-
-static int res_get(struct s2255_fh *fh)
-{
- struct s2255_channel *channel = fh->channel;
- /* is it free? */
- if (channel->resources)
- return 0; /* no, someone else uses it */
- /* it's free, grab it */
- channel->resources = 1;
- fh->resources = 1;
- dprintk(1, "s2255: res: get\n");
- return 1;
-}
-
-static int res_locked(struct s2255_fh *fh)
-{
- return fh->channel->resources;
-}
-
-static int res_check(struct s2255_fh *fh)
-{
- return fh->resources;
-}
-
-
-static void res_free(struct s2255_fh *fh)
-{
- struct s2255_channel *channel = fh->channel;
- channel->resources = 0;
- fh->resources = 0;
- dprintk(1, "res: put\n");
-}
-
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct s2255_fh *fh = file->private_data;
- struct s2255_dev *dev = fh->dev;
+ struct s2255_vc *vc = video_drvdata(file);
+ struct s2255_dev *dev = vc->dev;
strlcpy(cap->driver, "s2255", sizeof(cap->driver));
strlcpy(cap->card, "s2255", sizeof(cap->card));
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -841,7 +751,6 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
(formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
return -EINVAL;
- dprintk(4, "name %s\n", formats[index].name);
strlcpy(f->description, formats[index].name, sizeof(f->description));
f->pixelformat = formats[index].fourcc;
return 0;
@@ -850,19 +759,18 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
- int is_ntsc = channel->std & V4L2_STD_525_60;
+ struct s2255_vc *vc = video_drvdata(file);
+ int is_ntsc = vc->std & V4L2_STD_525_60;
- f->fmt.pix.width = channel->width;
- f->fmt.pix.height = channel->height;
+ f->fmt.pix.width = vc->width;
+ f->fmt.pix.height = vc->height;
if (f->fmt.pix.height >=
(is_ntsc ? NUM_LINES_1CIFS_NTSC : NUM_LINES_1CIFS_PAL) * 2)
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
else
f->fmt.pix.field = V4L2_FIELD_TOP;
- f->fmt.pix.pixelformat = channel->fmt->fourcc;
- f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->fmt->depth >> 3);
+ f->fmt.pix.pixelformat = vc->fmt->fourcc;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * (vc->fmt->depth >> 3);
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
f->fmt.pix.priv = 0;
@@ -874,9 +782,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
{
const struct s2255_fmt *fmt;
enum v4l2_field field;
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
- int is_ntsc = channel->std & V4L2_STD_525_60;
+ struct s2255_vc *vc = video_drvdata(file);
+ int is_ntsc = vc->std & V4L2_STD_525_60;
fmt = format_by_fourcc(f->fmt.pix.pixelformat);
@@ -885,7 +792,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
field = f->fmt.pix.field;
- dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n",
+ dprintk(vc->dev, 50, "%s NTSC: %d suggested width: %d, height: %d\n",
__func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height);
if (is_ntsc) {
/* NTSC */
@@ -927,7 +834,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
f->fmt.pix.priv = 0;
- dprintk(50, "%s: set width %d height %d field %d\n", __func__,
+ dprintk(vc->dev, 50, "%s: set width %d height %d field %d\n", __func__,
f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
return 0;
}
@@ -935,14 +842,13 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
+ struct s2255_vc *vc = video_drvdata(file);
const struct s2255_fmt *fmt;
- struct videobuf_queue *q = &fh->vb_vidq;
+ struct vb2_queue *q = &vc->vb_vidq;
struct s2255_mode mode;
int ret;
- ret = vidioc_try_fmt_vid_cap(file, fh, f);
+ ret = vidioc_try_fmt_vid_cap(file, vc, f);
if (ret < 0)
return ret;
@@ -952,28 +858,19 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
if (fmt == NULL)
return -EINVAL;
- mutex_lock(&q->vb_lock);
-
- if (videobuf_queue_is_busy(&fh->vb_vidq)) {
- dprintk(1, "queue busy\n");
- ret = -EBUSY;
- goto out_s_fmt;
+ if (vb2_is_busy(q)) {
+ dprintk(vc->dev, 1, "queue busy\n");
+ return -EBUSY;
}
- if (res_locked(fh)) {
- dprintk(1, "%s: channel busy\n", __func__);
- ret = -EBUSY;
- goto out_s_fmt;
- }
- mode = channel->mode;
- channel->fmt = fmt;
- channel->width = f->fmt.pix.width;
- channel->height = f->fmt.pix.height;
- fh->vb_vidq.field = f->fmt.pix.field;
- fh->type = f->type;
- if (channel->width > norm_minw(channel)) {
- if (channel->height > norm_minh(channel)) {
- if (channel->cap_parm.capturemode &
+ mode = vc->mode;
+ vc->fmt = fmt;
+ vc->width = f->fmt.pix.width;
+ vc->height = f->fmt.pix.height;
+ vc->field = f->fmt.pix.field;
+ if (vc->width > norm_minw(vc)) {
+ if (vc->height > norm_minh(vc)) {
+ if (vc->cap_parm.capturemode &
V4L2_MODE_HIGHQUALITY)
mode.scale = SCALE_4CIFSI;
else
@@ -985,7 +882,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
mode.scale = SCALE_1CIFS;
}
/* color mode */
- switch (channel->fmt->fourcc) {
+ switch (vc->fmt->fourcc) {
case V4L2_PIX_FMT_GREY:
mode.color &= ~MASK_COLOR;
mode.color |= COLOR_Y8;
@@ -994,7 +891,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
case V4L2_PIX_FMT_MJPEG:
mode.color &= ~MASK_COLOR;
mode.color |= COLOR_JPG;
- mode.color |= (channel->jpegqual << 8);
+ mode.color |= (vc->jpegqual << 8);
break;
case V4L2_PIX_FMT_YUV422P:
mode.color &= ~MASK_COLOR;
@@ -1007,52 +904,17 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
mode.color |= COLOR_YUVPK;
break;
}
- if ((mode.color & MASK_COLOR) != (channel->mode.color & MASK_COLOR))
+ if ((mode.color & MASK_COLOR) != (vc->mode.color & MASK_COLOR))
mode.restart = 1;
- else if (mode.scale != channel->mode.scale)
+ else if (mode.scale != vc->mode.scale)
mode.restart = 1;
- else if (mode.format != channel->mode.format)
+ else if (mode.format != vc->mode.format)
mode.restart = 1;
- channel->mode = mode;
- (void) s2255_set_mode(channel, &mode);
- ret = 0;
-out_s_fmt:
- mutex_unlock(&q->vb_lock);
- return ret;
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- int rc;
- struct s2255_fh *fh = priv;
- rc = videobuf_reqbufs(&fh->vb_vidq, p);
- return rc;
-}
-
-static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- int rc;
- struct s2255_fh *fh = priv;
- rc = videobuf_querybuf(&fh->vb_vidq, p);
- return rc;
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- int rc;
- struct s2255_fh *fh = priv;
- rc = videobuf_qbuf(&fh->vb_vidq, p);
- return rc;
+ vc->mode = mode;
+ (void) s2255_set_mode(vc, &mode);
+ return 0;
}
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- int rc;
- struct s2255_fh *fh = priv;
- rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK);
- return rc;
-}
/* write to the configuration pipe, synchronously */
static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
@@ -1150,201 +1012,166 @@ static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode)
* When the restart parameter is set, we sleep for ONE frame to allow the
* DSP time to get the new frame
*/
-static int s2255_set_mode(struct s2255_channel *channel,
+static int s2255_set_mode(struct s2255_vc *vc,
struct s2255_mode *mode)
{
int res;
- __le32 *buffer;
unsigned long chn_rev;
- struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
int i;
+ __le32 *buffer = dev->cmdbuf;
- chn_rev = G_chnmap[channel->idx];
- dprintk(3, "%s channel: %d\n", __func__, channel->idx);
+ mutex_lock(&dev->cmdlock);
+ chn_rev = G_chnmap[vc->idx];
+ dprintk(dev, 3, "%s channel: %d\n", __func__, vc->idx);
/* if JPEG, set the quality */
if ((mode->color & MASK_COLOR) == COLOR_JPG) {
mode->color &= ~MASK_COLOR;
mode->color |= COLOR_JPG;
mode->color &= ~MASK_JPG_QUALITY;
- mode->color |= (channel->jpegqual << 8);
+ mode->color |= (vc->jpegqual << 8);
}
/* save the mode */
- channel->mode = *mode;
- channel->req_image_size = get_transfer_size(mode);
- dprintk(1, "%s: reqsize %ld\n", __func__, channel->req_image_size);
- buffer = kzalloc(512, GFP_KERNEL);
- if (buffer == NULL) {
- dev_err(&dev->udev->dev, "out of mem\n");
- return -ENOMEM;
- }
+ vc->mode = *mode;
+ vc->req_image_size = get_transfer_size(mode);
+ dprintk(dev, 1, "%s: reqsize %ld\n", __func__, vc->req_image_size);
/* set the mode */
buffer[0] = IN_DATA_TOKEN;
buffer[1] = (__le32) cpu_to_le32(chn_rev);
buffer[2] = CMD_SET_MODE;
for (i = 0; i < sizeof(struct s2255_mode) / sizeof(u32); i++)
- buffer[3 + i] = cpu_to_le32(((u32 *)&channel->mode)[i]);
- channel->setmode_ready = 0;
+ buffer[3 + i] = cpu_to_le32(((u32 *)&vc->mode)[i]);
+ vc->setmode_ready = 0;
res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
if (debug)
s2255_print_cfg(dev, mode);
- kfree(buffer);
/* wait at least 3 frames before continuing */
if (mode->restart) {
- wait_event_timeout(channel->wait_setmode,
- (channel->setmode_ready != 0),
+ wait_event_timeout(vc->wait_setmode,
+ (vc->setmode_ready != 0),
msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
- if (channel->setmode_ready != 1) {
- printk(KERN_DEBUG "s2255: no set mode response\n");
+ if (vc->setmode_ready != 1) {
+ dprintk(dev, 0, "s2255: no set mode response\n");
res = -EFAULT;
}
}
/* clear the restart flag */
- channel->mode.restart = 0;
- dprintk(1, "%s chn %d, result: %d\n", __func__, channel->idx, res);
+ vc->mode.restart = 0;
+ dprintk(dev, 1, "%s chn %d, result: %d\n", __func__, vc->idx, res);
+ mutex_unlock(&dev->cmdlock);
return res;
}
-static int s2255_cmd_status(struct s2255_channel *channel, u32 *pstatus)
+static int s2255_cmd_status(struct s2255_vc *vc, u32 *pstatus)
{
int res;
- __le32 *buffer;
u32 chn_rev;
- struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
- chn_rev = G_chnmap[channel->idx];
- dprintk(4, "%s chan %d\n", __func__, channel->idx);
- buffer = kzalloc(512, GFP_KERNEL);
- if (buffer == NULL) {
- dev_err(&dev->udev->dev, "out of mem\n");
- return -ENOMEM;
- }
+ struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
+ __le32 *buffer = dev->cmdbuf;
+
+ mutex_lock(&dev->cmdlock);
+ chn_rev = G_chnmap[vc->idx];
+ dprintk(dev, 4, "%s chan %d\n", __func__, vc->idx);
/* form the get vid status command */
buffer[0] = IN_DATA_TOKEN;
buffer[1] = (__le32) cpu_to_le32(chn_rev);
buffer[2] = CMD_STATUS;
*pstatus = 0;
- channel->vidstatus_ready = 0;
+ vc->vidstatus_ready = 0;
res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
- kfree(buffer);
- wait_event_timeout(channel->wait_vidstatus,
- (channel->vidstatus_ready != 0),
+ wait_event_timeout(vc->wait_vidstatus,
+ (vc->vidstatus_ready != 0),
msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT));
- if (channel->vidstatus_ready != 1) {
- printk(KERN_DEBUG "s2255: no vidstatus response\n");
+ if (vc->vidstatus_ready != 1) {
+ dprintk(dev, 0, "s2255: no vidstatus response\n");
res = -EFAULT;
}
- *pstatus = channel->vidstatus;
- dprintk(4, "%s, vid status %d\n", __func__, *pstatus);
+ *pstatus = vc->vidstatus;
+ dprintk(dev, 4, "%s, vid status %d\n", __func__, *pstatus);
+ mutex_unlock(&dev->cmdlock);
return res;
}
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
- int res;
- struct s2255_fh *fh = priv;
- struct s2255_dev *dev = fh->dev;
- struct s2255_channel *channel = fh->channel;
+ struct s2255_vc *vc = vb2_get_drv_priv(vq);
int j;
- dprintk(4, "%s\n", __func__);
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_err(&dev->udev->dev, "invalid fh type0\n");
- return -EINVAL;
- }
- if (i != fh->type) {
- dev_err(&dev->udev->dev, "invalid fh type1\n");
- return -EINVAL;
- }
- if (!res_get(fh)) {
- s2255_dev_err(&dev->udev->dev, "stream busy\n");
- return -EBUSY;
- }
- channel->last_frame = -1;
- channel->bad_payload = 0;
- channel->cur_frame = 0;
- channel->frame_count = 0;
+ vc->last_frame = -1;
+ vc->bad_payload = 0;
+ vc->cur_frame = 0;
+ vc->frame_count = 0;
for (j = 0; j < SYS_FRAMES; j++) {
- channel->buffer.frame[j].ulState = S2255_READ_IDLE;
- channel->buffer.frame[j].cur_size = 0;
+ vc->buffer.frame[j].ulState = S2255_READ_IDLE;
+ vc->buffer.frame[j].cur_size = 0;
}
- res = videobuf_streamon(&fh->vb_vidq);
- if (res == 0) {
- s2255_start_acquire(channel);
- channel->b_acquire = 1;
- } else
- res_free(fh);
-
- return res;
+ return s2255_start_acquire(vc);
}
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+/* abort streaming and wait for last buffer */
+static int stop_streaming(struct vb2_queue *vq)
{
- struct s2255_fh *fh = priv;
- dprintk(4, "%s\n, channel: %d", __func__, fh->channel->idx);
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- printk(KERN_ERR "invalid fh type0\n");
- return -EINVAL;
+ struct s2255_vc *vc = vb2_get_drv_priv(vq);
+ struct s2255_buffer *buf, *node;
+ unsigned long flags;
+ (void) s2255_stop_acquire(vc);
+ spin_lock_irqsave(&vc->qlock, flags);
+ list_for_each_entry_safe(buf, node, &vc->buf_list, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ dprintk(vc->dev, 2, "[%p/%d] done\n",
+ buf, buf->vb.v4l2_buf.index);
}
- if (i != fh->type) {
- printk(KERN_ERR "invalid type i\n");
- return -EINVAL;
- }
- s2255_stop_acquire(fh->channel);
- videobuf_streamoff(&fh->vb_vidq);
- res_free(fh);
+ spin_unlock_irqrestore(&vc->qlock, flags);
return 0;
}
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i)
{
- struct s2255_fh *fh = priv;
+ struct s2255_vc *vc = video_drvdata(file);
struct s2255_mode mode;
- struct videobuf_queue *q = &fh->vb_vidq;
- struct s2255_channel *channel = fh->channel;
- int ret = 0;
+ struct vb2_queue *q = &vc->vb_vidq;
- mutex_lock(&q->vb_lock);
- if (res_locked(fh)) {
- dprintk(1, "can't change standard after started\n");
- ret = -EBUSY;
- goto out_s_std;
- }
- mode = fh->channel->mode;
+ /*
+ * Changing the standard implies a format change, which is not allowed
+ * while buffers for use with streaming have already been allocated.
+ */
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ mode = vc->mode;
if (i & V4L2_STD_525_60) {
- dprintk(4, "%s 60 Hz\n", __func__);
+ dprintk(vc->dev, 4, "%s 60 Hz\n", __func__);
/* if changing format, reset frame decimation/intervals */
if (mode.format != FORMAT_NTSC) {
mode.restart = 1;
mode.format = FORMAT_NTSC;
mode.fdec = FDEC_1;
- channel->width = LINE_SZ_4CIFS_NTSC;
- channel->height = NUM_LINES_4CIFS_NTSC * 2;
+ vc->width = LINE_SZ_4CIFS_NTSC;
+ vc->height = NUM_LINES_4CIFS_NTSC * 2;
}
} else if (i & V4L2_STD_625_50) {
- dprintk(4, "%s 50 Hz\n", __func__);
+ dprintk(vc->dev, 4, "%s 50 Hz\n", __func__);
if (mode.format != FORMAT_PAL) {
mode.restart = 1;
mode.format = FORMAT_PAL;
mode.fdec = FDEC_1;
- channel->width = LINE_SZ_4CIFS_PAL;
- channel->height = NUM_LINES_4CIFS_PAL * 2;
+ vc->width = LINE_SZ_4CIFS_PAL;
+ vc->height = NUM_LINES_4CIFS_PAL * 2;
}
- } else {
- ret = -EINVAL;
- goto out_s_std;
- }
- fh->channel->std = i;
+ } else
+ return -EINVAL;
+ vc->std = i;
if (mode.restart)
- s2255_set_mode(fh->channel, &mode);
-out_s_std:
- mutex_unlock(&q->vb_lock);
- return ret;
+ s2255_set_mode(vc, &mode);
+ return 0;
}
static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *i)
{
- struct s2255_fh *fh = priv;
+ struct s2255_vc *vc = video_drvdata(file);
- *i = fh->channel->std;
+ *i = vc->std;
return 0;
}
@@ -1358,10 +1185,10 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *i)
static int vidioc_enum_input(struct file *file, void *priv,
struct v4l2_input *inp)
{
- struct s2255_fh *fh = priv;
- struct s2255_dev *dev = fh->dev;
- struct s2255_channel *channel = fh->channel;
+ struct s2255_vc *vc = video_drvdata(file);
+ struct s2255_dev *dev = vc->dev;
u32 status = 0;
+
if (inp->index != 0)
return -EINVAL;
inp->type = V4L2_INPUT_TYPE_CAMERA;
@@ -1369,8 +1196,9 @@ static int vidioc_enum_input(struct file *file, void *priv,
inp->status = 0;
if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) {
int rc;
- rc = s2255_cmd_status(fh->channel, &status);
- dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status);
+ rc = s2255_cmd_status(vc, &status);
+ dprintk(dev, 4, "s2255_cmd_status rc: %d status %x\n",
+ rc, status);
if (rc == 0)
inp->status = (status & 0x01) ? 0
: V4L2_IN_ST_NO_SIGNAL;
@@ -1381,7 +1209,7 @@ static int vidioc_enum_input(struct file *file, void *priv,
strlcpy(inp->name, "Composite", sizeof(inp->name));
break;
case 0x2257:
- strlcpy(inp->name, (channel->idx < 2) ? "Composite" : "S-Video",
+ strlcpy(inp->name, (vc->idx < 2) ? "Composite" : "S-Video",
sizeof(inp->name));
break;
}
@@ -1402,13 +1230,10 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
static int s2255_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct s2255_channel *channel =
- container_of(ctrl->handler, struct s2255_channel, hdl);
+ struct s2255_vc *vc =
+ container_of(ctrl->handler, struct s2255_vc, hdl);
struct s2255_mode mode;
-
- mode = channel->mode;
- dprintk(4, "%s\n", __func__);
-
+ mode = vc->mode;
/* update the mode to the corresponding value */
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
@@ -1428,7 +1253,7 @@ static int s2255_s_ctrl(struct v4l2_ctrl *ctrl)
mode.color |= !ctrl->val << 16;
break;
case V4L2_CID_JPEG_COMPRESSION_QUALITY:
- channel->jpegqual = ctrl->val;
+ vc->jpegqual = ctrl->val;
return 0;
default:
return -EINVAL;
@@ -1438,48 +1263,48 @@ static int s2255_s_ctrl(struct v4l2_ctrl *ctrl)
some V4L programs restart stream unnecessarily
after a s_crtl.
*/
- s2255_set_mode(channel, &mode);
+ s2255_set_mode(vc, &mode);
return 0;
}
static int vidioc_g_jpegcomp(struct file *file, void *priv,
struct v4l2_jpegcompression *jc)
{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
+ struct s2255_vc *vc = video_drvdata(file);
memset(jc, 0, sizeof(*jc));
- jc->quality = channel->jpegqual;
- dprintk(2, "%s: quality %d\n", __func__, jc->quality);
+ jc->quality = vc->jpegqual;
+ dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
return 0;
}
static int vidioc_s_jpegcomp(struct file *file, void *priv,
const struct v4l2_jpegcompression *jc)
{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
+ struct s2255_vc *vc = video_drvdata(file);
+
if (jc->quality < 0 || jc->quality > 100)
return -EINVAL;
- v4l2_ctrl_s_ctrl(channel->jpegqual_ctrl, jc->quality);
- dprintk(2, "%s: quality %d\n", __func__, jc->quality);
+ v4l2_ctrl_s_ctrl(vc->jpegqual_ctrl, jc->quality);
+ dprintk(vc->dev, 2, "%s: quality %d\n", __func__, jc->quality);
return 0;
}
static int vidioc_g_parm(struct file *file, void *priv,
struct v4l2_streamparm *sp)
{
- struct s2255_fh *fh = priv;
__u32 def_num, def_dem;
- struct s2255_channel *channel = fh->channel;
+ struct s2255_vc *vc = video_drvdata(file);
+
if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- sp->parm.capture.capturemode = channel->cap_parm.capturemode;
- def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000;
- def_dem = (channel->mode.format == FORMAT_NTSC) ? 30000 : 25000;
+ sp->parm.capture.capturemode = vc->cap_parm.capturemode;
+ sp->parm.capture.readbuffers = S2255_MIN_BUFS;
+ def_num = (vc->mode.format == FORMAT_NTSC) ? 1001 : 1000;
+ def_dem = (vc->mode.format == FORMAT_NTSC) ? 30000 : 25000;
sp->parm.capture.timeperframe.denominator = def_dem;
- switch (channel->mode.fdec) {
+ switch (vc->mode.fdec) {
default:
case FDEC_1:
sp->parm.capture.timeperframe.numerator = def_num;
@@ -1494,7 +1319,8 @@ static int vidioc_g_parm(struct file *file, void *priv,
sp->parm.capture.timeperframe.numerator = def_num * 5;
break;
}
- dprintk(4, "%s capture mode, %d timeperframe %d/%d\n", __func__,
+ dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d\n",
+ __func__,
sp->parm.capture.capturemode,
sp->parm.capture.timeperframe.numerator,
sp->parm.capture.timeperframe.denominator);
@@ -1504,17 +1330,16 @@ static int vidioc_g_parm(struct file *file, void *priv,
static int vidioc_s_parm(struct file *file, void *priv,
struct v4l2_streamparm *sp)
{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
+ struct s2255_vc *vc = video_drvdata(file);
struct s2255_mode mode;
int fdec = FDEC_1;
__u32 def_num, def_dem;
if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- mode = channel->mode;
+ mode = vc->mode;
/* high quality capture mode requires a stream restart */
- if (channel->cap_parm.capturemode
- != sp->parm.capture.capturemode && res_locked(fh))
+ if ((vc->cap_parm.capturemode != sp->parm.capture.capturemode)
+ && vb2_is_streaming(&vc->vb_vidq))
return -EBUSY;
def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000;
def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000;
@@ -1534,8 +1359,9 @@ static int vidioc_s_parm(struct file *file, void *priv,
}
mode.fdec = fdec;
sp->parm.capture.timeperframe.denominator = def_dem;
- s2255_set_mode(channel, &mode);
- dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n",
+ sp->parm.capture.readbuffers = S2255_MIN_BUFS;
+ s2255_set_mode(vc, &mode);
+ dprintk(vc->dev, 4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n",
__func__,
sp->parm.capture.capturemode,
sp->parm.capture.timeperframe.numerator,
@@ -1558,9 +1384,8 @@ static const struct v4l2_frmsize_discrete pal_sizes[] = {
static int vidioc_enum_framesizes(struct file *file, void *priv,
struct v4l2_frmsizeenum *fe)
{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
- int is_ntsc = channel->std & V4L2_STD_525_60;
+ struct s2255_vc *vc = video_drvdata(file);
+ int is_ntsc = vc->std & V4L2_STD_525_60;
const struct s2255_fmt *fmt;
if (fe->index >= NUM_SIZE_ENUMS)
@@ -1577,11 +1402,10 @@ static int vidioc_enum_framesizes(struct file *file, void *priv,
static int vidioc_enum_frameintervals(struct file *file, void *priv,
struct v4l2_frmivalenum *fe)
{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
+ struct s2255_vc *vc = video_drvdata(file);
const struct s2255_fmt *fmt;
const struct v4l2_frmsize_discrete *sizes;
- int is_ntsc = channel->std & V4L2_STD_525_60;
+ int is_ntsc = vc->std & V4L2_STD_525_60;
#define NUM_FRAME_ENUMS 4
int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5};
int i;
@@ -1604,21 +1428,24 @@ static int vidioc_enum_frameintervals(struct file *file, void *priv,
fe->type = V4L2_FRMIVAL_TYPE_DISCRETE;
fe->discrete.denominator = is_ntsc ? 30000 : 25000;
fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index];
- dprintk(4, "%s discrete %d/%d\n", __func__, fe->discrete.numerator,
+ dprintk(vc->dev, 4, "%s discrete %d/%d\n", __func__,
+ fe->discrete.numerator,
fe->discrete.denominator);
return 0;
}
-static int __s2255_open(struct file *file)
+static int s2255_open(struct file *file)
{
- struct video_device *vdev = video_devdata(file);
- struct s2255_channel *channel = video_drvdata(file);
- struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
- struct s2255_fh *fh;
- enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ struct s2255_vc *vc = video_drvdata(file);
+ struct s2255_dev *dev = vc->dev;
int state;
- dprintk(1, "s2255: open called (dev=%s)\n",
- video_device_node_name(vdev));
+ int rc = 0;
+
+ rc = v4l2_fh_open(file);
+ if (rc != 0)
+ return rc;
+
+ dprintk(dev, 1, "s2255: %s\n", __func__);
state = atomic_read(&dev->fw_data->fw_state);
switch (state) {
case S2255_FW_DISCONNECTING:
@@ -1640,7 +1467,7 @@ static int __s2255_open(struct file *file)
case S2255_FW_LOADED_DSPWAIT:
/* give S2255_LOAD_TIMEOUT time for firmware to load in case
driver loaded and then device immediately opened */
- printk(KERN_INFO "%s waiting for firmware load\n", __func__);
+ pr_info("%s waiting for firmware load\n", __func__);
wait_event_timeout(dev->fw_data->wait_fw,
((atomic_read(&dev->fw_data->fw_state)
== S2255_FW_SUCCESS) ||
@@ -1659,16 +1486,15 @@ static int __s2255_open(struct file *file)
case S2255_FW_SUCCESS:
break;
case S2255_FW_FAILED:
- printk(KERN_INFO "2255 firmware load failed.\n");
+ pr_info("2255 firmware load failed.\n");
return -ENODEV;
case S2255_FW_DISCONNECTING:
- printk(KERN_INFO "%s: disconnecting\n", __func__);
+ pr_info("%s: disconnecting\n", __func__);
return -ENODEV;
case S2255_FW_LOADED_DSPWAIT:
case S2255_FW_NOTLOADED:
- printk(KERN_INFO "%s: firmware not loaded yet"
- "please try again later\n",
- __func__);
+ pr_info("%s: firmware not loaded, please retry\n",
+ __func__);
/*
* Timeout on firmware load means device unusable.
* Set firmware failure state.
@@ -1678,71 +1504,21 @@ static int __s2255_open(struct file *file)
S2255_FW_FAILED);
return -EAGAIN;
default:
- printk(KERN_INFO "%s: unknown state\n", __func__);
+ pr_info("%s: unknown state\n", __func__);
return -EFAULT;
}
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh)
- return -ENOMEM;
- v4l2_fh_init(&fh->fh, vdev);
- v4l2_fh_add(&fh->fh);
- file->private_data = &fh->fh;
- fh->dev = dev;
- fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- fh->channel = channel;
- if (!channel->configured) {
+ if (!vc->configured) {
/* configure channel to default state */
- channel->fmt = &formats[0];
- s2255_set_mode(channel, &channel->mode);
- channel->configured = 1;
- }
- dprintk(1, "%s: dev=%s type=%s\n", __func__,
- video_device_node_name(vdev), v4l2_type_names[type]);
- dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__,
- (unsigned long)fh, (unsigned long)dev,
- (unsigned long)&channel->vidq);
- dprintk(4, "%s: list_empty active=%d\n", __func__,
- list_empty(&channel->vidq.active));
- videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops,
- NULL, &dev->slock,
- fh->type,
- V4L2_FIELD_INTERLACED,
- sizeof(struct s2255_buffer),
- fh, vdev->lock);
+ vc->fmt = &formats[0];
+ s2255_set_mode(vc, &vc->mode);
+ vc->configured = 1;
+ }
return 0;
}
-static int s2255_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- int ret;
-
- if (mutex_lock_interruptible(vdev->lock))
- return -ERESTARTSYS;
- ret = __s2255_open(file);
- mutex_unlock(vdev->lock);
- return ret;
-}
-
-static unsigned int s2255_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct s2255_fh *fh = file->private_data;
- struct s2255_dev *dev = fh->dev;
- int rc = v4l2_ctrl_poll(file, wait);
-
- dprintk(100, "%s\n", __func__);
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
- return POLLERR;
- mutex_lock(&dev->lock);
- rc |= videobuf_poll_stream(file, &fh->vb_vidq, wait);
- mutex_unlock(&dev->lock);
- return rc;
-}
-
static void s2255_destroy(struct s2255_dev *dev)
{
+ dprintk(dev, 1, "%s", __func__);
/* board shutdown stops the read pipe if it is running */
s2255_board_shutdown(dev);
/* make sure firmware still not trying to load */
@@ -1760,62 +1536,18 @@ static void s2255_destroy(struct s2255_dev *dev)
mutex_destroy(&dev->lock);
usb_put_dev(dev->udev);
v4l2_device_unregister(&dev->v4l2_dev);
- dprintk(1, "%s", __func__);
+ kfree(dev->cmdbuf);
kfree(dev);
}
-static int s2255_release(struct file *file)
-{
- struct s2255_fh *fh = file->private_data;
- struct s2255_dev *dev = fh->dev;
- struct video_device *vdev = video_devdata(file);
- struct s2255_channel *channel = fh->channel;
- if (!dev)
- return -ENODEV;
- mutex_lock(&dev->lock);
- /* turn off stream */
- if (res_check(fh)) {
- if (channel->b_acquire)
- s2255_stop_acquire(fh->channel);
- videobuf_streamoff(&fh->vb_vidq);
- res_free(fh);
- }
- videobuf_mmap_free(&fh->vb_vidq);
- mutex_unlock(&dev->lock);
- dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev));
- v4l2_fh_del(&fh->fh);
- v4l2_fh_exit(&fh->fh);
- kfree(fh);
- return 0;
-}
-
-static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma)
-{
- struct s2255_fh *fh = file->private_data;
- struct s2255_dev *dev;
- int ret;
-
- if (!fh)
- return -ENODEV;
- dev = fh->dev;
- dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma);
- if (mutex_lock_interruptible(&dev->lock))
- return -ERESTARTSYS;
- ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
- mutex_unlock(&dev->lock);
- dprintk(4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__,
- (unsigned long)vma->vm_start,
- (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret);
- return ret;
-}
-
static const struct v4l2_file_operations s2255_fops_v4l = {
.owner = THIS_MODULE,
.open = s2255_open,
- .release = s2255_release,
- .poll = s2255_poll,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = s2255_mmap_v4l,
+ .mmap = vb2_fop_mmap,
+ .read = vb2_fop_read,
};
static const struct v4l2_ioctl_ops s2255_ioctl_ops = {
@@ -1824,17 +1556,17 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = {
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_s_std = vidioc_s_std,
.vidioc_g_std = vidioc_g_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_s_jpegcomp = vidioc_s_jpegcomp,
.vidioc_g_jpegcomp = vidioc_g_jpegcomp,
.vidioc_s_parm = vidioc_s_parm,
@@ -1849,13 +1581,14 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = {
static void s2255_video_device_release(struct video_device *vdev)
{
struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
- struct s2255_channel *channel =
- container_of(vdev, struct s2255_channel, vdev);
+ struct s2255_vc *vc =
+ container_of(vdev, struct s2255_vc, vdev);
- v4l2_ctrl_handler_free(&channel->hdl);
- dprintk(4, "%s, chnls: %d\n", __func__,
+ dprintk(dev, 4, "%s, chnls: %d\n", __func__,
atomic_read(&dev->num_channels));
+ v4l2_ctrl_handler_free(&vc->hdl);
+
if (atomic_dec_and_test(&dev->num_channels))
s2255_destroy(dev);
return;
@@ -1888,52 +1621,70 @@ static int s2255_probe_v4l(struct s2255_dev *dev)
int ret;
int i;
int cur_nr = video_nr;
- struct s2255_channel *channel;
+ struct s2255_vc *vc;
+ struct vb2_queue *q;
+
ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev);
if (ret)
return ret;
/* initialize all video 4 linux */
/* register 4 video devices */
for (i = 0; i < MAX_CHANNELS; i++) {
- channel = &dev->channel[i];
- INIT_LIST_HEAD(&channel->vidq.active);
+ vc = &dev->vc[i];
+ INIT_LIST_HEAD(&vc->buf_list);
- v4l2_ctrl_handler_init(&channel->hdl, 6);
- v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops,
+ v4l2_ctrl_handler_init(&vc->hdl, 6);
+ v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT);
- v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops,
+ v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST);
- v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops,
+ v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION);
- v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops,
+ v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops,
V4L2_CID_HUE, 0, 255, 1, DEF_HUE);
- channel->jpegqual_ctrl = v4l2_ctrl_new_std(&channel->hdl,
+ vc->jpegqual_ctrl = v4l2_ctrl_new_std(&vc->hdl,
&s2255_ctrl_ops,
V4L2_CID_JPEG_COMPRESSION_QUALITY,
0, 100, 1, S2255_DEF_JPEG_QUAL);
if (dev->dsp_fw_ver >= S2255_MIN_DSP_COLORFILTER &&
- (dev->pid != 0x2257 || channel->idx <= 1))
- v4l2_ctrl_new_custom(&channel->hdl, &color_filter_ctrl, NULL);
- if (channel->hdl.error) {
- ret = channel->hdl.error;
- v4l2_ctrl_handler_free(&channel->hdl);
+ (dev->pid != 0x2257 || vc->idx <= 1))
+ v4l2_ctrl_new_custom(&vc->hdl, &color_filter_ctrl,
+ NULL);
+ if (vc->hdl.error) {
+ ret = vc->hdl.error;
+ v4l2_ctrl_handler_free(&vc->hdl);
dev_err(&dev->udev->dev, "couldn't register control\n");
break;
}
- channel->vidq.dev = dev;
- /* register 4 video devices */
- channel->vdev = template;
- channel->vdev.ctrl_handler = &channel->hdl;
- channel->vdev.lock = &dev->lock;
- channel->vdev.v4l2_dev = &dev->v4l2_dev;
- set_bit(V4L2_FL_USE_FH_PRIO, &channel->vdev.flags);
- video_set_drvdata(&channel->vdev, channel);
+ q = &vc->vb_vidq;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_READ | VB2_USERPTR;
+ q->drv_priv = vc;
+ q->lock = &vc->vb_lock;
+ q->buf_struct_size = sizeof(struct s2255_buffer);
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->ops = &s2255_video_qops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ ret = vb2_queue_init(q);
+ if (ret != 0) {
+ dev_err(&dev->udev->dev,
+ "%s vb2_queue_init 0x%x\n", __func__, ret);
+ break;
+ }
+ /* register video devices */
+ vc->vdev = template;
+ vc->vdev.queue = q;
+ vc->vdev.ctrl_handler = &vc->hdl;
+ vc->vdev.lock = &dev->lock;
+ vc->vdev.v4l2_dev = &dev->v4l2_dev;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vc->vdev.flags);
+ video_set_drvdata(&vc->vdev, vc);
if (video_nr == -1)
- ret = video_register_device(&channel->vdev,
+ ret = video_register_device(&vc->vdev,
VFL_TYPE_GRABBER,
video_nr);
else
- ret = video_register_device(&channel->vdev,
+ ret = video_register_device(&vc->vdev,
VFL_TYPE_GRABBER,
cur_nr + i);
@@ -1944,18 +1695,18 @@ static int s2255_probe_v4l(struct s2255_dev *dev)
}
atomic_inc(&dev->num_channels);
v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
- video_device_node_name(&channel->vdev));
+ video_device_node_name(&vc->vdev));
}
- printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %s\n",
- S2255_VERSION);
+ pr_info("Sensoray 2255 V4L driver Revision: %s\n",
+ S2255_VERSION);
/* if no channels registered, return error and probe will fail*/
if (atomic_read(&dev->num_channels) == 0) {
v4l2_device_unregister(&dev->v4l2_dev);
return ret;
}
if (atomic_read(&dev->num_channels) != MAX_CHANNELS)
- printk(KERN_WARNING "s2255: Not all channels available.\n");
+ pr_warn("s2255: Not all channels available.\n");
return 0;
}
@@ -1981,11 +1732,11 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
s32 idx = -1;
struct s2255_framei *frm;
unsigned char *pdata;
- struct s2255_channel *channel;
- dprintk(100, "buffer to user\n");
- channel = &dev->channel[dev->cc];
- idx = channel->cur_frame;
- frm = &channel->buffer.frame[idx];
+ struct s2255_vc *vc;
+ dprintk(dev, 100, "buffer to user\n");
+ vc = &dev->vc[dev->cc];
+ idx = vc->cur_frame;
+ frm = &vc->buffer.frame[idx];
if (frm->ulState == S2255_READ_IDLE) {
int jj;
unsigned int cc;
@@ -1997,28 +1748,27 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) {
switch (*pdword) {
case S2255_MARKER_FRAME:
- dprintk(4, "found frame marker at offset:"
- " %d [%x %x]\n", jj, pdata[0],
- pdata[1]);
+ dprintk(dev, 4, "marker @ offset: %d [%x %x]\n",
+ jj, pdata[0], pdata[1]);
offset = jj + PREFIX_SIZE;
bframe = 1;
cc = le32_to_cpu(pdword[1]);
if (cc >= MAX_CHANNELS) {
- printk(KERN_ERR
- "bad channel\n");
+ dprintk(dev, 0,
+ "bad channel\n");
return -EINVAL;
}
/* reverse it */
dev->cc = G_chnmap[cc];
- channel = &dev->channel[dev->cc];
+ vc = &dev->vc[dev->cc];
payload = le32_to_cpu(pdword[3]);
- if (payload > channel->req_image_size) {
- channel->bad_payload++;
+ if (payload > vc->req_image_size) {
+ vc->bad_payload++;
/* discard the bad frame */
return -EINVAL;
}
- channel->pkt_size = payload;
- channel->jpg_size = le32_to_cpu(pdword[4]);
+ vc->pkt_size = payload;
+ vc->jpg_size = le32_to_cpu(pdword[4]);
break;
case S2255_MARKER_RESPONSE:
@@ -2029,34 +1779,34 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
cc = G_chnmap[le32_to_cpu(pdword[1])];
if (cc >= MAX_CHANNELS)
break;
- channel = &dev->channel[cc];
+ vc = &dev->vc[cc];
switch (pdword[2]) {
case S2255_RESPONSE_SETMODE:
/* check if channel valid */
/* set mode ready */
- channel->setmode_ready = 1;
- wake_up(&channel->wait_setmode);
- dprintk(5, "setmode ready %d\n", cc);
+ vc->setmode_ready = 1;
+ wake_up(&vc->wait_setmode);
+ dprintk(dev, 5, "setmode rdy %d\n", cc);
break;
case S2255_RESPONSE_FW:
dev->chn_ready |= (1 << cc);
if ((dev->chn_ready & 0x0f) != 0x0f)
break;
/* all channels ready */
- printk(KERN_INFO "s2255: fw loaded\n");
+ pr_info("s2255: fw loaded\n");
atomic_set(&dev->fw_data->fw_state,
S2255_FW_SUCCESS);
wake_up(&dev->fw_data->wait_fw);
break;
case S2255_RESPONSE_STATUS:
- channel->vidstatus = le32_to_cpu(pdword[3]);
- channel->vidstatus_ready = 1;
- wake_up(&channel->wait_vidstatus);
- dprintk(5, "got vidstatus %x chan %d\n",
+ vc->vidstatus = le32_to_cpu(pdword[3]);
+ vc->vidstatus_ready = 1;
+ wake_up(&vc->wait_vidstatus);
+ dprintk(dev, 5, "vstat %x chan %d\n",
le32_to_cpu(pdword[3]), cc);
break;
default:
- printk(KERN_INFO "s2255 unknown resp\n");
+ pr_info("s2255 unknown resp\n");
}
default:
pdata++;
@@ -2068,11 +1818,11 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
if (!bframe)
return -EINVAL;
}
- channel = &dev->channel[dev->cc];
- idx = channel->cur_frame;
- frm = &channel->buffer.frame[idx];
+ vc = &dev->vc[dev->cc];
+ idx = vc->cur_frame;
+ frm = &vc->buffer.frame[idx];
/* search done. now find out if should be acquiring on this channel */
- if (!channel->b_acquire) {
+ if (!vb2_is_streaming(&vc->vb_vidq)) {
/* we found a frame, but this channel is turned off */
frm->ulState = S2255_READ_IDLE;
return -EINVAL;
@@ -2088,7 +1838,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
if (frm->lpvbits == NULL) {
- dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d",
+ dprintk(dev, 1, "s2255 frame buffer == NULL.%p %p %d %d",
frm, dev, dev->cc, idx);
return -ENOMEM;
}
@@ -2097,28 +1847,28 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
copy_size = (pipe_info->cur_transfer_size - offset);
- size = channel->pkt_size - PREFIX_SIZE;
+ size = vc->pkt_size - PREFIX_SIZE;
/* sanity check on pdest */
- if ((copy_size + frm->cur_size) < channel->req_image_size)
+ if ((copy_size + frm->cur_size) < vc->req_image_size)
memcpy(pdest, psrc, copy_size);
frm->cur_size += copy_size;
- dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size);
+ dprintk(dev, 4, "cur_size: %lu, size: %lu\n", frm->cur_size, size);
if (frm->cur_size >= size) {
- dprintk(2, "****************[%d]Buffer[%d]full*************\n",
+ dprintk(dev, 2, "******[%d]Buffer[%d]full*******\n",
dev->cc, idx);
- channel->last_frame = channel->cur_frame;
- channel->cur_frame++;
+ vc->last_frame = vc->cur_frame;
+ vc->cur_frame++;
/* end of system frame ring buffer, start at zero */
- if ((channel->cur_frame == SYS_FRAMES) ||
- (channel->cur_frame == channel->buffer.dwFrames))
- channel->cur_frame = 0;
+ if ((vc->cur_frame == SYS_FRAMES) ||
+ (vc->cur_frame == vc->buffer.dwFrames))
+ vc->cur_frame = 0;
/* frame ready */
- if (channel->b_acquire)
- s2255_got_frame(channel, channel->jpg_size);
- channel->frame_count++;
+ if (vb2_is_streaming(&vc->vb_vidq))
+ s2255_got_frame(vc, vc->jpg_size);
+ vc->frame_count++;
frm->ulState = S2255_READ_IDLE;
frm->cur_size = 0;
@@ -2131,7 +1881,7 @@ static void s2255_read_video_callback(struct s2255_dev *dev,
struct s2255_pipeinfo *pipe_info)
{
int res;
- dprintk(50, "callback read video \n");
+ dprintk(dev, 50, "callback read video\n");
if (dev->cc >= MAX_CHANNELS) {
dev->cc = 0;
@@ -2141,9 +1891,9 @@ static void s2255_read_video_callback(struct s2255_dev *dev,
/* otherwise copy to the system buffers */
res = save_frame(dev, pipe_info);
if (res != 0)
- dprintk(4, "s2255: read callback failed\n");
+ dprintk(dev, 4, "s2255: read callback failed\n");
- dprintk(50, "callback read video done\n");
+ dprintk(dev, 50, "callback read video done\n");
return;
}
@@ -2181,9 +1931,9 @@ static int s2255_get_fx2fw(struct s2255_dev *dev)
ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2,
S2255_VR_IN);
if (ret < 0)
- dprintk(2, "get fw error: %x\n", ret);
+ dprintk(dev, 2, "get fw error: %x\n", ret);
fw = transBuffer[0] + (transBuffer[1] << 8);
- dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
+ dprintk(dev, 2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
return fw;
}
@@ -2191,12 +1941,11 @@ static int s2255_get_fx2fw(struct s2255_dev *dev)
* Create the system ring buffer to copy frames into from the
* usb read pipe.
*/
-static int s2255_create_sys_buffers(struct s2255_channel *channel)
+static int s2255_create_sys_buffers(struct s2255_vc *vc)
{
unsigned long i;
unsigned long reqsize;
- dprintk(1, "create sys buffers\n");
- channel->buffer.dwFrames = SYS_FRAMES;
+ vc->buffer.dwFrames = SYS_FRAMES;
/* always allocate maximum size(PAL) for system buffers */
reqsize = SYS_FRAMES_MAXSIZE;
@@ -2205,40 +1954,33 @@ static int s2255_create_sys_buffers(struct s2255_channel *channel)
for (i = 0; i < SYS_FRAMES; i++) {
/* allocate the frames */
- channel->buffer.frame[i].lpvbits = vmalloc(reqsize);
- dprintk(1, "valloc %p chan %d, idx %lu, pdata %p\n",
- &channel->buffer.frame[i], channel->idx, i,
- channel->buffer.frame[i].lpvbits);
- channel->buffer.frame[i].size = reqsize;
- if (channel->buffer.frame[i].lpvbits == NULL) {
- printk(KERN_INFO "out of memory. using less frames\n");
- channel->buffer.dwFrames = i;
+ vc->buffer.frame[i].lpvbits = vmalloc(reqsize);
+ vc->buffer.frame[i].size = reqsize;
+ if (vc->buffer.frame[i].lpvbits == NULL) {
+ pr_info("out of memory. using less frames\n");
+ vc->buffer.dwFrames = i;
break;
}
}
/* make sure internal states are set */
for (i = 0; i < SYS_FRAMES; i++) {
- channel->buffer.frame[i].ulState = 0;
- channel->buffer.frame[i].cur_size = 0;
+ vc->buffer.frame[i].ulState = 0;
+ vc->buffer.frame[i].cur_size = 0;
}
- channel->cur_frame = 0;
- channel->last_frame = -1;
+ vc->cur_frame = 0;
+ vc->last_frame = -1;
return 0;
}
-static int s2255_release_sys_buffers(struct s2255_channel *channel)
+static int s2255_release_sys_buffers(struct s2255_vc *vc)
{
unsigned long i;
- dprintk(1, "release sys buffers\n");
for (i = 0; i < SYS_FRAMES; i++) {
- if (channel->buffer.frame[i].lpvbits) {
- dprintk(1, "vfree %p\n",
- channel->buffer.frame[i].lpvbits);
- vfree(channel->buffer.frame[i].lpvbits);
- }
- channel->buffer.frame[i].lpvbits = NULL;
+ if (vc->buffer.frame[i].lpvbits)
+ vfree(vc->buffer.frame[i].lpvbits);
+ vc->buffer.frame[i].lpvbits = NULL;
}
return 0;
}
@@ -2249,7 +1991,7 @@ static int s2255_board_init(struct s2255_dev *dev)
int fw_ver;
int j;
struct s2255_pipeinfo *pipe = &dev->pipe;
- dprintk(4, "board init: %p", dev);
+ dprintk(dev, 4, "board init: %p", dev);
memset(pipe, 0, sizeof(*pipe));
pipe->dev = dev;
pipe->cur_transfer_size = S2255_USB_XFER_SIZE;
@@ -2258,54 +2000,53 @@ static int s2255_board_init(struct s2255_dev *dev)
pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
GFP_KERNEL);
if (pipe->transfer_buffer == NULL) {
- dprintk(1, "out of memory!\n");
+ dprintk(dev, 1, "out of memory!\n");
return -ENOMEM;
}
/* query the firmware */
fw_ver = s2255_get_fx2fw(dev);
- printk(KERN_INFO "s2255: usb firmware version %d.%d\n",
- (fw_ver >> 8) & 0xff,
- fw_ver & 0xff);
+ pr_info("s2255: usb firmware version %d.%d\n",
+ (fw_ver >> 8) & 0xff,
+ fw_ver & 0xff);
if (fw_ver < S2255_CUR_USB_FWVER)
- printk(KERN_INFO "s2255: newer USB firmware available\n");
+ pr_info("s2255: newer USB firmware available\n");
for (j = 0; j < MAX_CHANNELS; j++) {
- struct s2255_channel *channel = &dev->channel[j];
- channel->b_acquire = 0;
- channel->mode = mode_def;
+ struct s2255_vc *vc = &dev->vc[j];
+ vc->mode = mode_def;
if (dev->pid == 0x2257 && j > 1)
- channel->mode.color |= (1 << 16);
- channel->jpegqual = S2255_DEF_JPEG_QUAL;
- channel->width = LINE_SZ_4CIFS_NTSC;
- channel->height = NUM_LINES_4CIFS_NTSC * 2;
- channel->std = V4L2_STD_NTSC_M;
- channel->fmt = &formats[0];
- channel->mode.restart = 1;
- channel->req_image_size = get_transfer_size(&mode_def);
- channel->frame_count = 0;
+ vc->mode.color |= (1 << 16);
+ vc->jpegqual = S2255_DEF_JPEG_QUAL;
+ vc->width = LINE_SZ_4CIFS_NTSC;
+ vc->height = NUM_LINES_4CIFS_NTSC * 2;
+ vc->std = V4L2_STD_NTSC_M;
+ vc->fmt = &formats[0];
+ vc->mode.restart = 1;
+ vc->req_image_size = get_transfer_size(&mode_def);
+ vc->frame_count = 0;
/* create the system buffers */
- s2255_create_sys_buffers(channel);
+ s2255_create_sys_buffers(vc);
}
/* start read pipe */
s2255_start_readpipe(dev);
- dprintk(1, "%s: success\n", __func__);
+ dprintk(dev, 1, "%s: success\n", __func__);
return 0;
}
static int s2255_board_shutdown(struct s2255_dev *dev)
{
u32 i;
- dprintk(1, "%s: dev: %p", __func__, dev);
+ dprintk(dev, 1, "%s: dev: %p", __func__, dev);
for (i = 0; i < MAX_CHANNELS; i++) {
- if (dev->channel[i].b_acquire)
- s2255_stop_acquire(&dev->channel[i]);
+ if (vb2_is_streaming(&dev->vc[i].vb_vidq))
+ s2255_stop_acquire(&dev->vc[i]);
}
s2255_stop_readpipe(dev);
for (i = 0; i < MAX_CHANNELS; i++)
- s2255_release_sys_buffers(&dev->channel[i]);
+ s2255_release_sys_buffers(&dev->vc[i]);
/* release transfer buffer */
kfree(dev->pipe.transfer_buffer);
return 0;
@@ -2318,13 +2059,10 @@ static void read_pipe_completion(struct urb *purb)
int status;
int pipe;
pipe_info = purb->context;
- dprintk(100, "%s: urb:%p, status %d\n", __func__, purb,
- purb->status);
if (pipe_info == NULL) {
dev_err(&purb->dev->dev, "no context!\n");
return;
}
-
dev = pipe_info->dev;
if (dev == NULL) {
dev_err(&purb->dev->dev, "no context!\n");
@@ -2333,13 +2071,13 @@ static void read_pipe_completion(struct urb *purb)
status = purb->status;
/* if shutting down, do not resubmit, exit immediately */
if (status == -ESHUTDOWN) {
- dprintk(2, "%s: err shutdown\n", __func__);
+ dprintk(dev, 2, "%s: err shutdown\n", __func__);
pipe_info->err_count++;
return;
}
if (pipe_info->state == 0) {
- dprintk(2, "%s: exiting USB pipe", __func__);
+ dprintk(dev, 2, "%s: exiting USB pipe", __func__);
return;
}
@@ -2347,7 +2085,7 @@ static void read_pipe_completion(struct urb *purb)
s2255_read_video_callback(dev, pipe_info);
else {
pipe_info->err_count++;
- dprintk(1, "%s: failed URB %d\n", __func__, status);
+ dprintk(dev, 1, "%s: failed URB %d\n", __func__, status);
}
pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
@@ -2359,11 +2097,10 @@ static void read_pipe_completion(struct urb *purb)
read_pipe_completion, pipe_info);
if (pipe_info->state != 0) {
- if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) {
+ if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC))
dev_err(&dev->udev->dev, "error submitting urb\n");
- }
} else {
- dprintk(2, "%s :complete state 0\n", __func__);
+ dprintk(dev, 2, "%s :complete state 0\n", __func__);
}
return;
}
@@ -2374,7 +2111,7 @@ static int s2255_start_readpipe(struct s2255_dev *dev)
int retval;
struct s2255_pipeinfo *pipe_info = &dev->pipe;
pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
- dprintk(2, "%s: IN %d\n", __func__, dev->read_endpoint);
+ dprintk(dev, 2, "%s: IN %d\n", __func__, dev->read_endpoint);
pipe_info->state = 1;
pipe_info->err_count = 0;
pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
@@ -2391,70 +2128,64 @@ static int s2255_start_readpipe(struct s2255_dev *dev)
read_pipe_completion, pipe_info);
retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
if (retval) {
- printk(KERN_ERR "s2255: start read pipe failed\n");
+ pr_err("s2255: start read pipe failed\n");
return retval;
}
return 0;
}
/* starts acquisition process */
-static int s2255_start_acquire(struct s2255_channel *channel)
+static int s2255_start_acquire(struct s2255_vc *vc)
{
- unsigned char *buffer;
int res;
unsigned long chn_rev;
int j;
- struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
- chn_rev = G_chnmap[channel->idx];
- buffer = kzalloc(512, GFP_KERNEL);
- if (buffer == NULL) {
- dev_err(&dev->udev->dev, "out of mem\n");
- return -ENOMEM;
- }
-
- channel->last_frame = -1;
- channel->bad_payload = 0;
- channel->cur_frame = 0;
+ struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
+ __le32 *buffer = dev->cmdbuf;
+
+ mutex_lock(&dev->cmdlock);
+ chn_rev = G_chnmap[vc->idx];
+ vc->last_frame = -1;
+ vc->bad_payload = 0;
+ vc->cur_frame = 0;
for (j = 0; j < SYS_FRAMES; j++) {
- channel->buffer.frame[j].ulState = 0;
- channel->buffer.frame[j].cur_size = 0;
+ vc->buffer.frame[j].ulState = 0;
+ vc->buffer.frame[j].cur_size = 0;
}
/* send the start command */
- *(__le32 *) buffer = IN_DATA_TOKEN;
- *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev);
- *((__le32 *) buffer + 2) = CMD_START;
+ buffer[0] = IN_DATA_TOKEN;
+ buffer[1] = (__le32) cpu_to_le32(chn_rev);
+ buffer[2] = CMD_START;
res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
if (res != 0)
dev_err(&dev->udev->dev, "CMD_START error\n");
- dprintk(2, "start acquire exit[%d] %d \n", channel->idx, res);
- kfree(buffer);
- return 0;
+ dprintk(dev, 2, "start acquire exit[%d] %d\n", vc->idx, res);
+ mutex_unlock(&dev->cmdlock);
+ return res;
}
-static int s2255_stop_acquire(struct s2255_channel *channel)
+static int s2255_stop_acquire(struct s2255_vc *vc)
{
- unsigned char *buffer;
int res;
unsigned long chn_rev;
- struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
- chn_rev = G_chnmap[channel->idx];
- buffer = kzalloc(512, GFP_KERNEL);
- if (buffer == NULL) {
- dev_err(&dev->udev->dev, "out of mem\n");
- return -ENOMEM;
- }
+ struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
+ __le32 *buffer = dev->cmdbuf;
+
+ mutex_lock(&dev->cmdlock);
+ chn_rev = G_chnmap[vc->idx];
/* send the stop command */
- *(__le32 *) buffer = IN_DATA_TOKEN;
- *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev);
- *((__le32 *) buffer + 2) = CMD_STOP;
+ buffer[0] = IN_DATA_TOKEN;
+ buffer[1] = (__le32) cpu_to_le32(chn_rev);
+ buffer[2] = CMD_STOP;
+
res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
if (res != 0)
dev_err(&dev->udev->dev, "CMD_STOP error\n");
- kfree(buffer);
- channel->b_acquire = 0;
- dprintk(4, "%s: chn %d, res %d\n", __func__, channel->idx, res);
+
+ dprintk(dev, 4, "%s: chn %d, res %d\n", __func__, vc->idx, res);
+ mutex_unlock(&dev->cmdlock);
return res;
}
@@ -2469,7 +2200,7 @@ static void s2255_stop_readpipe(struct s2255_dev *dev)
usb_free_urb(pipe->stream_urb);
pipe->stream_urb = NULL;
}
- dprintk(4, "%s", __func__);
+ dprintk(dev, 4, "%s", __func__);
return;
}
@@ -2501,19 +2232,27 @@ static int s2255_probe(struct usb_interface *interface,
int retval = -ENOMEM;
__le32 *pdata;
int fw_size;
- dprintk(2, "%s\n", __func__);
+
/* allocate memory for our device state and initialize it to zero */
dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
if (dev == NULL) {
s2255_dev_err(&interface->dev, "out of memory\n");
return -ENOMEM;
}
+
+ dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL);
+ if (dev->cmdbuf == NULL) {
+ s2255_dev_err(&interface->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+
atomic_set(&dev->num_channels, 0);
dev->pid = le16_to_cpu(id->idProduct);
dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
if (!dev->fw_data)
goto errorFWDATA1;
mutex_init(&dev->lock);
+ mutex_init(&dev->cmdlock);
/* grab usb_device and save it */
dev->udev = usb_get_dev(interface_to_usbdev(interface));
if (dev->udev == NULL) {
@@ -2521,12 +2260,13 @@ static int s2255_probe(struct usb_interface *interface,
retval = -ENODEV;
goto errorUDEV;
}
- dprintk(1, "dev: %p, udev %p interface %p\n", dev,
- dev->udev, interface);
+ dev_dbg(&interface->dev, "dev: %p, udev %p interface %p\n",
+ dev, dev->udev, interface);
dev->interface = interface;
/* set up the endpoint information */
iface_desc = interface->cur_altsetting;
- dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints);
+ dev_dbg(&interface->dev, "num EP: %d\n",
+ iface_desc->desc.bNumEndpoints);
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
@@ -2544,10 +2284,13 @@ static int s2255_probe(struct usb_interface *interface,
dev->timer.data = (unsigned long)dev->fw_data;
init_waitqueue_head(&dev->fw_data->wait_fw);
for (i = 0; i < MAX_CHANNELS; i++) {
- struct s2255_channel *channel = &dev->channel[i];
- dev->channel[i].idx = i;
- init_waitqueue_head(&channel->wait_setmode);
- init_waitqueue_head(&channel->wait_vidstatus);
+ struct s2255_vc *vc = &dev->vc[i];
+ vc->idx = i;
+ vc->dev = dev;
+ init_waitqueue_head(&vc->wait_setmode);
+ init_waitqueue_head(&vc->wait_vidstatus);
+ spin_lock_init(&vc->qlock);
+ mutex_init(&vc->vb_lock);
}
dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
@@ -2564,7 +2307,7 @@ static int s2255_probe(struct usb_interface *interface,
/* load the first chunk */
if (request_firmware(&dev->fw_data->fw,
FIRMWARE_FILE_NAME, &dev->udev->dev)) {
- printk(KERN_ERR "sensoray 2255 failed to get firmware\n");
+ dev_err(&interface->dev, "sensoray 2255 failed to get firmware\n");
goto errorREQFW;
}
/* check the firmware is valid */
@@ -2572,28 +2315,27 @@ static int s2255_probe(struct usb_interface *interface,
pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
if (*pdata != S2255_FW_MARKER) {
- printk(KERN_INFO "Firmware invalid.\n");
+ dev_err(&interface->dev, "Firmware invalid.\n");
retval = -ENODEV;
goto errorFWMARKER;
} else {
/* make sure firmware is the latest */
__le32 *pRel;
pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
- printk(KERN_INFO "s2255 dsp fw version %x\n", le32_to_cpu(*pRel));
+ pr_info("s2255 dsp fw version %x\n", le32_to_cpu(*pRel));
dev->dsp_fw_ver = le32_to_cpu(*pRel);
if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
- printk(KERN_INFO "s2255: f2255usb.bin out of date.\n");
+ pr_info("s2255: f2255usb.bin out of date.\n");
if (dev->pid == 0x2257 &&
dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
- printk(KERN_WARNING "s2255: 2257 requires firmware %d"
- " or above.\n", S2255_MIN_DSP_COLORFILTER);
+ pr_warn("2257 needs firmware %d or above.\n",
+ S2255_MIN_DSP_COLORFILTER);
}
usb_reset_device(dev->udev);
/* load 2255 board specific */
retval = s2255_board_init(dev);
if (retval)
goto errorBOARDINIT;
- spin_lock_init(&dev->slock);
s2255_fwload_start(dev, 0);
/* loads v4l specific */
retval = s2255_probe_v4l(dev);
@@ -2617,8 +2359,9 @@ errorUDEV:
kfree(dev->fw_data);
mutex_destroy(&dev->lock);
errorFWDATA1:
+ kfree(dev->cmdbuf);
kfree(dev);
- printk(KERN_WARNING "Sensoray 2255 driver load failed: 0x%x\n", retval);
+ pr_warn("Sensoray 2255 driver load failed: 0x%x\n", retval);
return retval;
}
@@ -2635,15 +2378,15 @@ static void s2255_disconnect(struct usb_interface *interface)
atomic_inc(&dev->num_channels);
/* unregister each video device. */
for (i = 0; i < channels; i++)
- video_unregister_device(&dev->channel[i].vdev);
+ video_unregister_device(&dev->vc[i].vdev);
/* wake up any of our timers */
atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
wake_up(&dev->fw_data->wait_fw);
for (i = 0; i < MAX_CHANNELS; i++) {
- dev->channel[i].setmode_ready = 1;
- wake_up(&dev->channel[i].wait_setmode);
- dev->channel[i].vidstatus_ready = 1;
- wake_up(&dev->channel[i].wait_vidstatus);
+ dev->vc[i].setmode_ready = 1;
+ wake_up(&dev->vc[i].wait_setmode);
+ dev->vc[i].vidstatus_ready = 1;
+ wake_up(&dev->vc[i].wait_vidstatus);
}
if (atomic_dec_and_test(&dev->num_channels))
s2255_destroy(dev);
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
index 05bd91a60c09..1836a416d806 100644
--- a/drivers/media/usb/siano/smsusb.c
+++ b/drivers/media/usb/siano/smsusb.c
@@ -653,6 +653,8 @@ static const struct usb_device_id smsusb_id_table[] = {
.driver_info = SMS1XXX_BOARD_ZTE_DVB_DATA_CARD },
{ USB_DEVICE(0x19D2, 0x0078),
.driver_info = SMS1XXX_BOARD_ONDA_MDTV_DATA_CARD },
+ { USB_DEVICE(0x3275, 0x0080),
+ .driver_info = SMS1XXX_BOARD_SIANO_RIO },
{ } /* Terminating entry */
};
diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c
index c45c9881bb5f..37bc00f418f1 100644
--- a/drivers/media/usb/stk1160/stk1160-v4l.c
+++ b/drivers/media/usb/stk1160/stk1160-v4l.c
@@ -641,7 +641,7 @@ int stk1160_vb2_setup(struct stk1160 *dev)
q->buf_struct_size = sizeof(struct stk1160_buffer);
q->ops = &stk1160_video_qops;
q->mem_ops = &vb2_vmalloc_memops;
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
rc = vb2_queue_init(q);
if (rc < 0)
diff --git a/drivers/media/usb/tm6000/tm6000-alsa.c b/drivers/media/usb/tm6000/tm6000-alsa.c
index 3239cd62e452..74e5697d8678 100644
--- a/drivers/media/usb/tm6000/tm6000-alsa.c
+++ b/drivers/media/usb/tm6000/tm6000-alsa.c
@@ -1,7 +1,7 @@
/*
*
* Support for audio capture for tm5600/6000/6010
- * (c) 2007-2008 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * (c) 2007-2008 Mauro Carvalho Chehab
*
* Based on cx88-alsa.c
*
@@ -56,7 +56,7 @@ MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s).");
****************************************************************************/
MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Trident,tm5600},"
"{{Trident,tm6000},"
diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c
index 9fc1e940a82b..095f5db1a790 100644
--- a/drivers/media/usb/tm6000/tm6000-dvb.c
+++ b/drivers/media/usb/tm6000/tm6000-dvb.c
@@ -32,7 +32,7 @@
#include "xc5000.h"
MODULE_DESCRIPTION("DVB driver extension module for tm5600/6000/6010 based TV cards");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Trident, tm5600},"
diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c
index 8a6bbf1d80e1..d1af5438c168 100644
--- a/drivers/media/usb/tm6000/tm6000-input.c
+++ b/drivers/media/usb/tm6000/tm6000-input.c
@@ -422,7 +422,7 @@ int tm6000_ir_init(struct tm6000_core *dev)
ir->rc = rc;
/* input setup */
- rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC;
+ rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC);
/* Neded, in order to support NEC remotes with 24 or 32 bits */
rc->scanmask = 0xffff;
rc->priv = ir;
diff --git a/drivers/media/usb/tm6000/tm6000-stds.c b/drivers/media/usb/tm6000/tm6000-stds.c
index 5e28d6a2412f..93a4b2434b6e 100644
--- a/drivers/media/usb/tm6000/tm6000-stds.c
+++ b/drivers/media/usb/tm6000/tm6000-stds.c
@@ -1,7 +1,7 @@
/*
* tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices
*
- * Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2007 Mauro Carvalho Chehab
*
* 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
diff --git a/drivers/media/usb/usbtv/Makefile b/drivers/media/usb/usbtv/Makefile
index 28b872fa94e1..775316a88ea6 100644
--- a/drivers/media/usb/usbtv/Makefile
+++ b/drivers/media/usb/usbtv/Makefile
@@ -1 +1,4 @@
+usbtv-y := usbtv-core.o \
+ usbtv-video.o
+
obj-$(CONFIG_VIDEO_USBTV) += usbtv.o
diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c
new file mode 100644
index 000000000000..2f87ddfa469f
--- /dev/null
+++ b/drivers/media/usb/usbtv/usbtv-core.c
@@ -0,0 +1,134 @@
+/*
+ * Fushicai USBTV007 Video Grabber Driver
+ *
+ * Product web site:
+ * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
+ *
+ * Following LWN articles were very useful in construction of this driver:
+ * Video4Linux2 API series: http://lwn.net/Articles/203924/
+ * videobuf2 API explanation: http://lwn.net/Articles/447435/
+ * Thanks go to Jonathan Corbet for providing this quality documentation.
+ * He is awesome.
+ *
+ * Copyright (c) 2013 Lubomir Rintel
+ * All rights reserved.
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ */
+
+#include "usbtv.h"
+
+int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size)
+{
+ int ret;
+ int pipe = usb_rcvctrlpipe(usbtv->udev, 0);
+ int i;
+
+ for (i = 0; i < size; i++) {
+ u16 index = regs[i][0];
+ u16 value = regs[i][1];
+
+ ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int usbtv_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret;
+ int size;
+ struct device *dev = &intf->dev;
+ struct usbtv *usbtv;
+
+ /* Checks that the device is what we think it is. */
+ if (intf->num_altsetting != 2)
+ return -ENODEV;
+ if (intf->altsetting[1].desc.bNumEndpoints != 4)
+ return -ENODEV;
+
+ /* Packet size is split into 11 bits of base size and count of
+ * extra multiplies of it.*/
+ size = usb_endpoint_maxp(&intf->altsetting[1].endpoint[0].desc);
+ size = (size & 0x07ff) * (((size & 0x1800) >> 11) + 1);
+
+ /* Device structure */
+ usbtv = kzalloc(sizeof(struct usbtv), GFP_KERNEL);
+ if (usbtv == NULL)
+ return -ENOMEM;
+ usbtv->dev = dev;
+ usbtv->udev = usb_get_dev(interface_to_usbdev(intf));
+
+ usbtv->iso_size = size;
+
+ usb_set_intfdata(intf, usbtv);
+
+ ret = usbtv_video_init(usbtv);
+ if (ret < 0)
+ goto usbtv_video_fail;
+
+ /* for simplicity we exploit the v4l2_device reference counting */
+ v4l2_device_get(&usbtv->v4l2_dev);
+
+ dev_info(dev, "Fushicai USBTV007 Video Grabber\n");
+ return 0;
+
+usbtv_video_fail:
+ kfree(usbtv);
+
+ return ret;
+}
+
+static void usbtv_disconnect(struct usb_interface *intf)
+{
+ struct usbtv *usbtv = usb_get_intfdata(intf);
+ usb_set_intfdata(intf, NULL);
+
+ if (!usbtv)
+ return;
+
+ usbtv_video_free(usbtv);
+
+ usb_put_dev(usbtv->udev);
+ usbtv->udev = NULL;
+
+ /* the usbtv structure will be deallocated when v4l2 will be
+ done using it */
+ v4l2_device_put(&usbtv->v4l2_dev);
+}
+
+static struct usb_device_id usbtv_id_table[] = {
+ { USB_DEVICE(0x1b71, 0x3002) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, usbtv_id_table);
+
+MODULE_AUTHOR("Lubomir Rintel");
+MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+static struct usb_driver usbtv_usb_driver = {
+ .name = "usbtv",
+ .id_table = usbtv_id_table,
+ .probe = usbtv_probe,
+ .disconnect = usbtv_disconnect,
+};
+
+module_usb_driver(usbtv_usb_driver);
diff --git a/drivers/media/usb/usbtv/usbtv.c b/drivers/media/usb/usbtv/usbtv-video.c
index 6222a4ab1e00..20365bd69d05 100644
--- a/drivers/media/usb/usbtv/usbtv.c
+++ b/drivers/media/usb/usbtv/usbtv-video.c
@@ -28,45 +28,10 @@
* GNU General Public License ("GPL").
*/
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/videodev2.h>
-
-#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-core.h>
-#include <media/videobuf2-vmalloc.h>
-
-/* Hardware. */
-#define USBTV_VIDEO_ENDP 0x81
-#define USBTV_BASE 0xc000
-#define USBTV_REQUEST_REG 12
-
-/* Number of concurrent isochronous urbs submitted.
- * Higher numbers was seen to overly saturate the USB bus. */
-#define USBTV_ISOC_TRANSFERS 16
-#define USBTV_ISOC_PACKETS 8
-
-#define USBTV_CHUNK_SIZE 256
-#define USBTV_CHUNK 240
-
-/* Chunk header. */
-#define USBTV_MAGIC_OK(chunk) ((be32_to_cpu(chunk[0]) & 0xff000000) \
- == 0x88000000)
-#define USBTV_FRAME_ID(chunk) ((be32_to_cpu(chunk[0]) & 0x00ff0000) >> 16)
-#define USBTV_ODD(chunk) ((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15)
-#define USBTV_CHUNK_NO(chunk) (be32_to_cpu(chunk[0]) & 0x00000fff)
-
-#define USBTV_TV_STD (V4L2_STD_525_60 | V4L2_STD_PAL)
-
-/* parameters for supported TV norms */
-struct usbtv_norm_params {
- v4l2_std_id norm;
- int cap_width, cap_height;
-};
+
+#include "usbtv.h"
static struct usbtv_norm_params norm_params[] = {
{
@@ -81,43 +46,6 @@ static struct usbtv_norm_params norm_params[] = {
}
};
-/* A single videobuf2 frame buffer. */
-struct usbtv_buf {
- struct vb2_buffer vb;
- struct list_head list;
-};
-
-/* Per-device structure. */
-struct usbtv {
- struct device *dev;
- struct usb_device *udev;
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- struct vb2_queue vb2q;
- struct mutex v4l2_lock;
- struct mutex vb2q_lock;
-
- /* List of videobuf2 buffers protected by a lock. */
- spinlock_t buflock;
- struct list_head bufs;
-
- /* Number of currently processed frame, useful find
- * out when a new one begins. */
- u32 frame_id;
- int chunks_done;
-
- enum {
- USBTV_COMPOSITE_INPUT,
- USBTV_SVIDEO_INPUT,
- } input;
- v4l2_std_id norm;
- int width, height;
- int n_chunks;
- int iso_size;
- unsigned int sequence;
- struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
-};
-
static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm)
{
int i, ret = 0;
@@ -142,26 +70,6 @@ static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm)
return ret;
}
-static int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size)
-{
- int ret;
- int pipe = usb_rcvctrlpipe(usbtv->udev, 0);
- int i;
-
- for (i = 0; i < size; i++) {
- u16 index = regs[i][0];
- u16 value = regs[i][1];
-
- ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, NULL, 0, 0);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
static int usbtv_select_input(struct usbtv *usbtv, int input)
{
int ret;
@@ -560,12 +468,6 @@ start_fail:
return ret;
}
-struct usb_device_id usbtv_id_table[] = {
- { USB_DEVICE(0x1b71, 0x3002) },
- {}
-};
-MODULE_DEVICE_TABLE(usb, usbtv_id_table);
-
static int usbtv_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -660,7 +562,7 @@ static int usbtv_s_input(struct file *file, void *priv, unsigned int i)
return usbtv_select_input(usbtv, i);
}
-struct v4l2_ioctl_ops usbtv_ioctl_ops = {
+static struct v4l2_ioctl_ops usbtv_ioctl_ops = {
.vidioc_querycap = usbtv_querycap,
.vidioc_enum_input = usbtv_enum_input,
.vidioc_enum_fmt_vid_cap = usbtv_enum_fmt_vid_cap,
@@ -682,7 +584,7 @@ struct v4l2_ioctl_ops usbtv_ioctl_ops = {
.vidioc_streamoff = vb2_ioctl_streamoff,
};
-struct v4l2_file_operations usbtv_fops = {
+static struct v4l2_file_operations usbtv_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = video_ioctl2,
.mmap = vb2_fop_mmap,
@@ -743,7 +645,7 @@ static int usbtv_stop_streaming(struct vb2_queue *vq)
return 0;
}
-struct vb2_ops usbtv_vb2_ops = {
+static struct vb2_ops usbtv_vb2_ops = {
.queue_setup = usbtv_queue_setup,
.buf_queue = usbtv_buf_queue,
.start_streaming = usbtv_start_streaming,
@@ -759,33 +661,9 @@ static void usbtv_release(struct v4l2_device *v4l2_dev)
kfree(usbtv);
}
-static int usbtv_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
+int usbtv_video_init(struct usbtv *usbtv)
{
int ret;
- int size;
- struct device *dev = &intf->dev;
- struct usbtv *usbtv;
-
- /* Checks that the device is what we think it is. */
- if (intf->num_altsetting != 2)
- return -ENODEV;
- if (intf->altsetting[1].desc.bNumEndpoints != 4)
- return -ENODEV;
-
- /* Packet size is split into 11 bits of base size and count of
- * extra multiplies of it.*/
- size = usb_endpoint_maxp(&intf->altsetting[1].endpoint[0].desc);
- size = (size & 0x07ff) * (((size & 0x1800) >> 11) + 1);
-
- /* Device structure */
- usbtv = kzalloc(sizeof(struct usbtv), GFP_KERNEL);
- if (usbtv == NULL)
- return -ENOMEM;
- usbtv->dev = dev;
- usbtv->udev = usb_get_dev(interface_to_usbdev(intf));
-
- usbtv->iso_size = size;
(void)usbtv_configure_for_norm(usbtv, V4L2_STD_525_60);
@@ -801,24 +679,22 @@ static int usbtv_probe(struct usb_interface *intf,
usbtv->vb2q.buf_struct_size = sizeof(struct usbtv_buf);
usbtv->vb2q.ops = &usbtv_vb2_ops;
usbtv->vb2q.mem_ops = &vb2_vmalloc_memops;
- usbtv->vb2q.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ usbtv->vb2q.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
usbtv->vb2q.lock = &usbtv->vb2q_lock;
ret = vb2_queue_init(&usbtv->vb2q);
if (ret < 0) {
- dev_warn(dev, "Could not initialize videobuf2 queue\n");
- goto usbtv_fail;
+ dev_warn(usbtv->dev, "Could not initialize videobuf2 queue\n");
+ return ret;
}
/* v4l2 structure */
usbtv->v4l2_dev.release = usbtv_release;
- ret = v4l2_device_register(dev, &usbtv->v4l2_dev);
+ ret = v4l2_device_register(usbtv->dev, &usbtv->v4l2_dev);
if (ret < 0) {
- dev_warn(dev, "Could not register v4l2 device\n");
+ dev_warn(usbtv->dev, "Could not register v4l2 device\n");
goto v4l2_fail;
}
- usb_set_intfdata(intf, usbtv);
-
/* Video structure */
strlcpy(usbtv->vdev.name, "usbtv", sizeof(usbtv->vdev.name));
usbtv->vdev.v4l2_dev = &usbtv->v4l2_dev;
@@ -832,52 +708,31 @@ static int usbtv_probe(struct usb_interface *intf,
video_set_drvdata(&usbtv->vdev, usbtv);
ret = video_register_device(&usbtv->vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
- dev_warn(dev, "Could not register video device\n");
+ dev_warn(usbtv->dev, "Could not register video device\n");
goto vdev_fail;
}
- dev_info(dev, "Fushicai USBTV007 Video Grabber\n");
return 0;
vdev_fail:
v4l2_device_unregister(&usbtv->v4l2_dev);
v4l2_fail:
vb2_queue_release(&usbtv->vb2q);
-usbtv_fail:
- kfree(usbtv);
return ret;
}
-static void usbtv_disconnect(struct usb_interface *intf)
+void usbtv_video_free(struct usbtv *usbtv)
{
- struct usbtv *usbtv = usb_get_intfdata(intf);
-
mutex_lock(&usbtv->vb2q_lock);
mutex_lock(&usbtv->v4l2_lock);
usbtv_stop(usbtv);
- usb_set_intfdata(intf, NULL);
video_unregister_device(&usbtv->vdev);
v4l2_device_disconnect(&usbtv->v4l2_dev);
- usb_put_dev(usbtv->udev);
- usbtv->udev = NULL;
mutex_unlock(&usbtv->v4l2_lock);
mutex_unlock(&usbtv->vb2q_lock);
v4l2_device_put(&usbtv->v4l2_dev);
}
-
-MODULE_AUTHOR("Lubomir Rintel");
-MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver");
-MODULE_LICENSE("Dual BSD/GPL");
-
-struct usb_driver usbtv_usb_driver = {
- .name = "usbtv",
- .id_table = usbtv_id_table,
- .probe = usbtv_probe,
- .disconnect = usbtv_disconnect,
-};
-
-module_usb_driver(usbtv_usb_driver);
diff --git a/drivers/media/usb/usbtv/usbtv.h b/drivers/media/usb/usbtv/usbtv.h
new file mode 100644
index 000000000000..cb1d388cc647
--- /dev/null
+++ b/drivers/media/usb/usbtv/usbtv.h
@@ -0,0 +1,99 @@
+/*
+ * Fushicai USBTV007 Video Grabber Driver
+ *
+ * Copyright (c) 2013 Lubomir Rintel
+ * All rights reserved.
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include <media/v4l2-device.h>
+#include <media/videobuf2-vmalloc.h>
+
+/* Hardware. */
+#define USBTV_VIDEO_ENDP 0x81
+#define USBTV_BASE 0xc000
+#define USBTV_REQUEST_REG 12
+
+/* Number of concurrent isochronous urbs submitted.
+ * Higher numbers was seen to overly saturate the USB bus. */
+#define USBTV_ISOC_TRANSFERS 16
+#define USBTV_ISOC_PACKETS 8
+
+#define USBTV_CHUNK_SIZE 256
+#define USBTV_CHUNK 240
+
+/* Chunk header. */
+#define USBTV_MAGIC_OK(chunk) ((be32_to_cpu(chunk[0]) & 0xff000000) \
+ == 0x88000000)
+#define USBTV_FRAME_ID(chunk) ((be32_to_cpu(chunk[0]) & 0x00ff0000) >> 16)
+#define USBTV_ODD(chunk) ((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15)
+#define USBTV_CHUNK_NO(chunk) (be32_to_cpu(chunk[0]) & 0x00000fff)
+
+#define USBTV_TV_STD (V4L2_STD_525_60 | V4L2_STD_PAL)
+
+/* parameters for supported TV norms */
+struct usbtv_norm_params {
+ v4l2_std_id norm;
+ int cap_width, cap_height;
+};
+
+/* A single videobuf2 frame buffer. */
+struct usbtv_buf {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+/* Per-device structure. */
+struct usbtv {
+ struct device *dev;
+ struct usb_device *udev;
+
+ /* video */
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ struct vb2_queue vb2q;
+ struct mutex v4l2_lock;
+ struct mutex vb2q_lock;
+
+ /* List of videobuf2 buffers protected by a lock. */
+ spinlock_t buflock;
+ struct list_head bufs;
+
+ /* Number of currently processed frame, useful find
+ * out when a new one begins. */
+ u32 frame_id;
+ int chunks_done;
+
+ enum {
+ USBTV_COMPOSITE_INPUT,
+ USBTV_SVIDEO_INPUT,
+ } input;
+ v4l2_std_id norm;
+ int width, height;
+ int n_chunks;
+ int iso_size;
+ unsigned int sequence;
+ struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
+};
+
+int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size);
+
+int usbtv_video_init(struct usbtv *usbtv);
+void usbtv_video_free(struct usbtv *usbtv);
diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h
index 8a25876d72c6..a0c73cf1517c 100644
--- a/drivers/media/usb/usbvision/usbvision.h
+++ b/drivers/media/usb/usbvision/usbvision.h
@@ -203,14 +203,6 @@ enum {
mr = LIMIT_RGB(mm_r); \
}
-/* Debugging aid */
-#define USBVISION_SAY_AND_WAIT(what) { \
- wait_queue_head_t wq; \
- init_waitqueue_head(&wq); \
- printk(KERN_INFO "Say: %s\n", what); \
- interruptible_sleep_on_timeout(&wq, HZ * 3); \
-}
-
/*
* This macro checks if usbvision is still operational. The 'usbvision'
* pointer must be valid, usbvision->dev must be valid, we are not
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index c3bb2502225b..ad47c5cb539a 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -108,11 +108,31 @@ static struct uvc_format_desc uvc_fmts[] = {
.fcc = V4L2_PIX_FMT_Y16,
},
{
- .name = "RGB Bayer",
+ .name = "BGGR Bayer (BY8 )",
.guid = UVC_GUID_FORMAT_BY8,
.fcc = V4L2_PIX_FMT_SBGGR8,
},
{
+ .name = "BGGR Bayer (BA81)",
+ .guid = UVC_GUID_FORMAT_BA81,
+ .fcc = V4L2_PIX_FMT_SBGGR8,
+ },
+ {
+ .name = "GBRG Bayer (GBRG)",
+ .guid = UVC_GUID_FORMAT_GBRG,
+ .fcc = V4L2_PIX_FMT_SGBRG8,
+ },
+ {
+ .name = "GRBG Bayer (GRBG)",
+ .guid = UVC_GUID_FORMAT_GRBG,
+ .fcc = V4L2_PIX_FMT_SGRBG8,
+ },
+ {
+ .name = "RGGB Bayer (RGGB)",
+ .guid = UVC_GUID_FORMAT_RGGB,
+ .fcc = V4L2_PIX_FMT_SRGGB8,
+ },
+ {
.name = "RGB565",
.guid = UVC_GUID_FORMAT_RGBP,
.fcc = V4L2_PIX_FMT_RGB565,
@@ -925,7 +945,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
case UVC_VC_HEADER:
n = buflen >= 12 ? buffer[11] : 0;
- if (buflen < 12 || buflen < 12 + n) {
+ if (buflen < 12 + n) {
uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
"interface %d HEADER error\n", udev->devnum,
alts->desc.bInterfaceNumber);
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index cd962be860ca..6e92d2080255 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -48,12 +48,14 @@ static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
struct uvc_streaming *stream =
container_of(queue, struct uvc_streaming, queue);
- if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
- *nbuffers = UVC_MAX_VIDEO_BUFFERS;
+ /* Make sure the image size is large enough. */
+ if (fmt && fmt->fmt.pix.sizeimage < stream->ctrl.dwMaxVideoFrameSize)
+ return -EINVAL;
*nplanes = 1;
- sizes[0] = stream->ctrl.dwMaxVideoFrameSize;
+ sizes[0] = fmt ? fmt->fmt.pix.sizeimage
+ : stream->ctrl.dwMaxVideoFrameSize;
return 0;
}
@@ -104,15 +106,15 @@ static void uvc_buffer_queue(struct vb2_buffer *vb)
spin_unlock_irqrestore(&queue->irqlock, flags);
}
-static int uvc_buffer_finish(struct vb2_buffer *vb)
+static void uvc_buffer_finish(struct vb2_buffer *vb)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
struct uvc_streaming *stream =
container_of(queue, struct uvc_streaming, queue);
struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
- uvc_video_clock_update(stream, &vb->v4l2_buf, buf);
- return 0;
+ if (vb->state == VB2_BUF_STATE_DONE)
+ uvc_video_clock_update(stream, &vb->v4l2_buf, buf);
}
static void uvc_wait_prepare(struct vb2_queue *vq)
@@ -149,7 +151,8 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
queue->queue.ops = &uvc_queue_qops;
queue->queue.mem_ops = &vb2_vmalloc_memops;
- queue->queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
+ | V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
ret = vb2_queue_init(&queue->queue);
if (ret)
return ret;
@@ -196,6 +199,18 @@ int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
return ret;
}
+int uvc_create_buffers(struct uvc_video_queue *queue,
+ struct v4l2_create_buffers *cb)
+{
+ int ret;
+
+ mutex_lock(&queue->mutex);
+ ret = vb2_create_bufs(&queue->queue, cb);
+ mutex_unlock(&queue->mutex);
+
+ return ret;
+}
+
int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
int ret;
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 3afff92804d3..378ae02e593b 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1000,6 +1000,17 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return uvc_query_buffer(&stream->queue, buf);
}
+ case VIDIOC_CREATE_BUFS:
+ {
+ struct v4l2_create_buffers *cb = arg;
+
+ ret = uvc_acquire_privileges(handle);
+ if (ret < 0)
+ return ret;
+
+ return uvc_create_buffers(&stream->queue, cb);
+ }
+
case VIDIOC_QBUF:
if (!uvc_has_privileges(handle))
return -EBUSY;
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 898c208889cd..8d52baf5952b 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1453,6 +1453,9 @@ static unsigned int uvc_endpoint_max_bpi(struct usb_device *dev,
case USB_SPEED_HIGH:
psize = usb_endpoint_maxp(&ep->desc);
return (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ case USB_SPEED_WIRELESS:
+ psize = usb_endpoint_maxp(&ep->desc);
+ return psize;
default:
psize = usb_endpoint_maxp(&ep->desc);
return psize & 0x07ff;
@@ -1847,7 +1850,25 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable)
if (!enable) {
uvc_uninit_video(stream, 1);
- usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+ if (stream->intf->num_altsetting > 1) {
+ usb_set_interface(stream->dev->udev,
+ stream->intfnum, 0);
+ } else {
+ /* UVC doesn't specify how to inform a bulk-based device
+ * when the video stream is stopped. Windows sends a
+ * CLEAR_FEATURE(HALT) request to the video streaming
+ * bulk endpoint, mimic the same behaviour.
+ */
+ unsigned int epnum = stream->header.bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK;
+ unsigned int dir = stream->header.bEndpointAddress
+ & USB_ENDPOINT_DIR_MASK;
+ unsigned int pipe;
+
+ pipe = usb_sndbulkpipe(stream->dev->udev, epnum) | dir;
+ usb_clear_halt(stream->dev->udev, pipe);
+ }
+
uvc_queue_enable(&stream->queue, 0);
uvc_video_clock_cleanup(stream);
return 0;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 9e35982d099a..b1f69a6d4068 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -94,6 +94,18 @@
#define UVC_GUID_FORMAT_BY8 \
{ 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_BA81 \
+ { 'B', 'A', '8', '1', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_GBRG \
+ { 'G', 'B', 'R', 'G', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_GRBG \
+ { 'G', 'R', 'B', 'G', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_RGGB \
+ { 'R', 'G', 'G', 'B', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
#define UVC_GUID_FORMAT_RGBP \
{ 'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00, \
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
@@ -115,8 +127,6 @@
#define UVC_URBS 5
/* Maximum number of packets per URB. */
#define UVC_MAX_PACKETS 32
-/* Maximum number of video buffers. */
-#define UVC_MAX_VIDEO_BUFFERS 32
/* Maximum status buffer size in bytes of interrupt URB. */
#define UVC_MAX_STATUS_SIZE 16
@@ -616,6 +626,8 @@ extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
extern void uvc_free_buffers(struct uvc_video_queue *queue);
extern int uvc_query_buffer(struct uvc_video_queue *queue,
struct v4l2_buffer *v4l2_buf);
+extern int uvc_create_buffers(struct uvc_video_queue *queue,
+ struct v4l2_create_buffers *v4l2_cb);
extern int uvc_queue_buffer(struct uvc_video_queue *queue,
struct v4l2_buffer *v4l2_buf);
extern int uvc_dequeue_buffer(struct uvc_video_queue *queue,
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 6191968db8fa..04b2daf567be 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -740,7 +740,7 @@ static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *u
return 0;
}
-struct v4l2_subdev_edid32 {
+struct v4l2_edid32 {
__u32 pad;
__u32 start_block;
__u32 blocks;
@@ -748,11 +748,11 @@ struct v4l2_subdev_edid32 {
compat_caddr_t edid;
};
-static int get_v4l2_subdev_edid32(struct v4l2_subdev_edid *kp, struct v4l2_subdev_edid32 __user *up)
+static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
{
u32 tmp;
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_subdev_edid32)) ||
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_edid32)) ||
get_user(kp->pad, &up->pad) ||
get_user(kp->start_block, &up->start_block) ||
get_user(kp->blocks, &up->blocks) ||
@@ -763,11 +763,11 @@ static int get_v4l2_subdev_edid32(struct v4l2_subdev_edid *kp, struct v4l2_subde
return 0;
}
-static int put_v4l2_subdev_edid32(struct v4l2_subdev_edid *kp, struct v4l2_subdev_edid32 __user *up)
+static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
{
u32 tmp = (u32)((unsigned long)kp->edid);
- if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_subdev_edid32)) ||
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_edid32)) ||
put_user(kp->pad, &up->pad) ||
put_user(kp->start_block, &up->start_block) ||
put_user(kp->blocks, &up->blocks) ||
@@ -787,8 +787,8 @@ static int put_v4l2_subdev_edid32(struct v4l2_subdev_edid *kp, struct v4l2_subde
#define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32)
#define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32)
#define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32)
-#define VIDIOC_SUBDEV_G_EDID32 _IOWR('V', 63, struct v4l2_subdev_edid32)
-#define VIDIOC_SUBDEV_S_EDID32 _IOWR('V', 64, struct v4l2_subdev_edid32)
+#define VIDIOC_G_EDID32 _IOWR('V', 40, struct v4l2_edid32)
+#define VIDIOC_S_EDID32 _IOWR('V', 41, struct v4l2_edid32)
#define VIDIOC_TRY_FMT32 _IOWR('V', 64, struct v4l2_format32)
#define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32)
#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32)
@@ -816,7 +816,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
struct v4l2_ext_controls v2ecs;
struct v4l2_event v2ev;
struct v4l2_create_buffers v2crt;
- struct v4l2_subdev_edid v2edid;
+ struct v4l2_edid v2edid;
unsigned long vx;
int vi;
} karg;
@@ -849,8 +849,8 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break;
case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break;
case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break;
- case VIDIOC_SUBDEV_G_EDID32: cmd = VIDIOC_SUBDEV_G_EDID; break;
- case VIDIOC_SUBDEV_S_EDID32: cmd = VIDIOC_SUBDEV_S_EDID; break;
+ case VIDIOC_G_EDID32: cmd = VIDIOC_G_EDID; break;
+ case VIDIOC_S_EDID32: cmd = VIDIOC_S_EDID; break;
}
switch (cmd) {
@@ -868,9 +868,9 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
compatible_arg = 0;
break;
- case VIDIOC_SUBDEV_G_EDID:
- case VIDIOC_SUBDEV_S_EDID:
- err = get_v4l2_subdev_edid32(&karg.v2edid, up);
+ case VIDIOC_G_EDID:
+ case VIDIOC_S_EDID:
+ err = get_v4l2_edid32(&karg.v2edid, up);
compatible_arg = 0;
break;
@@ -966,9 +966,9 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
err = put_v4l2_event32(&karg.v2ev, up);
break;
- case VIDIOC_SUBDEV_G_EDID:
- case VIDIOC_SUBDEV_S_EDID:
- err = put_v4l2_subdev_edid32(&karg.v2edid, up);
+ case VIDIOC_G_EDID:
+ case VIDIOC_S_EDID:
+ err = put_v4l2_edid32(&karg.v2edid, up);
break;
case VIDIOC_G_FMT:
@@ -1006,103 +1006,14 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
if (!file->f_op->unlocked_ioctl)
return ret;
- switch (cmd) {
- case VIDIOC_QUERYCAP:
- case VIDIOC_RESERVED:
- case VIDIOC_ENUM_FMT:
- case VIDIOC_G_FMT32:
- case VIDIOC_S_FMT32:
- case VIDIOC_REQBUFS:
- case VIDIOC_QUERYBUF32:
- case VIDIOC_G_FBUF32:
- case VIDIOC_S_FBUF32:
- case VIDIOC_OVERLAY32:
- case VIDIOC_QBUF32:
- case VIDIOC_EXPBUF:
- case VIDIOC_DQBUF32:
- case VIDIOC_STREAMON32:
- case VIDIOC_STREAMOFF32:
- case VIDIOC_G_PARM:
- case VIDIOC_S_PARM:
- case VIDIOC_G_STD:
- case VIDIOC_S_STD:
- case VIDIOC_ENUMSTD32:
- case VIDIOC_ENUMINPUT32:
- case VIDIOC_G_CTRL:
- case VIDIOC_S_CTRL:
- case VIDIOC_G_TUNER:
- case VIDIOC_S_TUNER:
- case VIDIOC_G_AUDIO:
- case VIDIOC_S_AUDIO:
- case VIDIOC_QUERYCTRL:
- case VIDIOC_QUERYMENU:
- case VIDIOC_G_INPUT32:
- case VIDIOC_S_INPUT32:
- case VIDIOC_G_OUTPUT32:
- case VIDIOC_S_OUTPUT32:
- case VIDIOC_ENUMOUTPUT:
- case VIDIOC_G_AUDOUT:
- case VIDIOC_S_AUDOUT:
- case VIDIOC_G_MODULATOR:
- case VIDIOC_S_MODULATOR:
- case VIDIOC_S_FREQUENCY:
- case VIDIOC_G_FREQUENCY:
- case VIDIOC_CROPCAP:
- case VIDIOC_G_CROP:
- case VIDIOC_S_CROP:
- case VIDIOC_G_SELECTION:
- case VIDIOC_S_SELECTION:
- case VIDIOC_G_JPEGCOMP:
- case VIDIOC_S_JPEGCOMP:
- case VIDIOC_QUERYSTD:
- case VIDIOC_TRY_FMT32:
- case VIDIOC_ENUMAUDIO:
- case VIDIOC_ENUMAUDOUT:
- case VIDIOC_G_PRIORITY:
- case VIDIOC_S_PRIORITY:
- case VIDIOC_G_SLICED_VBI_CAP:
- case VIDIOC_LOG_STATUS:
- case VIDIOC_G_EXT_CTRLS32:
- case VIDIOC_S_EXT_CTRLS32:
- case VIDIOC_TRY_EXT_CTRLS32:
- case VIDIOC_ENUM_FRAMESIZES:
- case VIDIOC_ENUM_FRAMEINTERVALS:
- case VIDIOC_G_ENC_INDEX:
- case VIDIOC_ENCODER_CMD:
- case VIDIOC_TRY_ENCODER_CMD:
- case VIDIOC_DECODER_CMD:
- case VIDIOC_TRY_DECODER_CMD:
- case VIDIOC_DBG_S_REGISTER:
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_S_HW_FREQ_SEEK:
- case VIDIOC_S_DV_TIMINGS:
- case VIDIOC_G_DV_TIMINGS:
- case VIDIOC_DQEVENT:
- case VIDIOC_DQEVENT32:
- case VIDIOC_SUBSCRIBE_EVENT:
- case VIDIOC_UNSUBSCRIBE_EVENT:
- case VIDIOC_CREATE_BUFS32:
- case VIDIOC_PREPARE_BUF32:
- case VIDIOC_ENUM_DV_TIMINGS:
- case VIDIOC_QUERY_DV_TIMINGS:
- case VIDIOC_DV_TIMINGS_CAP:
- case VIDIOC_ENUM_FREQ_BANDS:
- case VIDIOC_SUBDEV_G_EDID32:
- case VIDIOC_SUBDEV_S_EDID32:
+ if (_IOC_TYPE(cmd) == 'V' && _IOC_NR(cmd) < BASE_VIDIOC_PRIVATE)
ret = do_video_ioctl(file, cmd, arg);
- break;
+ else if (vdev->fops->compat_ioctl32)
+ ret = vdev->fops->compat_ioctl32(file, cmd, arg);
- default:
- if (vdev->fops->compat_ioctl32)
- ret = vdev->fops->compat_ioctl32(file, cmd, arg);
-
- if (ret == -ENOIOCTLCMD)
- printk(KERN_WARNING "compat_ioctl32: "
- "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
- _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd),
- cmd);
- break;
- }
+ if (ret == -ENOIOCTLCMD)
+ pr_warn("compat_ioctl32: unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
+ _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), cmd);
return ret;
}
EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32);
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 6ff002bd5909..55c683254102 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -735,6 +735,8 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS";
case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count";
case V4L2_CID_MPEG_VIDEO_VBV_DELAY: return "Initial Delay for VBV Control";
+ case V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE: return "Horizontal MV Search Range";
+ case V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE: return "Vertical MV Search Range";
case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: return "Repeat Sequence Header";
/* VPX controls */
@@ -857,6 +859,17 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_FM_RX_CLASS: return "FM Radio Receiver Controls";
case V4L2_CID_TUNE_DEEMPHASIS: return "De-Emphasis";
case V4L2_CID_RDS_RECEPTION: return "RDS Reception";
+
+ case V4L2_CID_RF_TUNER_CLASS: return "RF Tuner Controls";
+ case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: return "LNA Gain, Auto";
+ case V4L2_CID_RF_TUNER_LNA_GAIN: return "LNA Gain";
+ case V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO: return "Mixer Gain, Auto";
+ case V4L2_CID_RF_TUNER_MIXER_GAIN: return "Mixer Gain";
+ case V4L2_CID_RF_TUNER_IF_GAIN_AUTO: return "IF Gain, Auto";
+ case V4L2_CID_RF_TUNER_IF_GAIN: return "IF Gain";
+ case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: return "Bandwidth, Auto";
+ case V4L2_CID_RF_TUNER_BANDWIDTH: return "Bandwidth";
+ case V4L2_CID_RF_TUNER_PLL_LOCK: return "PLL Lock";
default:
return NULL;
}
@@ -906,10 +919,19 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_WIDE_DYNAMIC_RANGE:
case V4L2_CID_IMAGE_STABILIZATION:
case V4L2_CID_RDS_RECEPTION:
+ case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO:
+ case V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO:
+ case V4L2_CID_RF_TUNER_IF_GAIN_AUTO:
+ case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
+ case V4L2_CID_RF_TUNER_PLL_LOCK:
*type = V4L2_CTRL_TYPE_BOOLEAN;
*min = 0;
*max = *step = 1;
break;
+ case V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE:
+ case V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ break;
case V4L2_CID_PAN_RESET:
case V4L2_CID_TILT_RESET:
case V4L2_CID_FLASH_STROBE:
@@ -991,6 +1013,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_IMAGE_PROC_CLASS:
case V4L2_CID_DV_CLASS:
case V4L2_CID_FM_RX_CLASS:
+ case V4L2_CID_RF_TUNER_CLASS:
*type = V4L2_CTRL_TYPE_CTRL_CLASS;
/* You can neither read not write these */
*flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
@@ -1063,6 +1086,10 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_PILOT_TONE_FREQUENCY:
case V4L2_CID_TUNE_POWER_LEVEL:
case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+ case V4L2_CID_RF_TUNER_LNA_GAIN:
+ case V4L2_CID_RF_TUNER_MIXER_GAIN:
+ case V4L2_CID_RF_TUNER_IF_GAIN:
+ case V4L2_CID_RF_TUNER_BANDWIDTH:
*flags |= V4L2_CTRL_FLAG_SLIDER;
break;
case V4L2_CID_PAN_RELATIVE:
@@ -1081,6 +1108,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_DV_RX_POWER_PRESENT:
*flags |= V4L2_CTRL_FLAG_READ_ONLY;
break;
+ case V4L2_CID_RF_TUNER_PLL_LOCK:
+ *flags |= V4L2_CTRL_FLAG_VOLATILE;
+ break;
}
}
EXPORT_SYMBOL(v4l2_ctrl_fill);
@@ -1921,7 +1951,8 @@ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
int i;
/* The first control is the master control and it must not be NULL */
- BUG_ON(ncontrols == 0 || controls[0] == NULL);
+ if (WARN_ON(ncontrols == 0 || controls[0] == NULL))
+ return;
for (i = 0; i < ncontrols; i++) {
if (controls[i]) {
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 0a30dbf3d05c..634d863c05b4 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -554,6 +554,7 @@ static void determine_valid_ioctls(struct video_device *vdev)
bool is_vid = vdev->vfl_type == VFL_TYPE_GRABBER;
bool is_vbi = vdev->vfl_type == VFL_TYPE_VBI;
bool is_radio = vdev->vfl_type == VFL_TYPE_RADIO;
+ bool is_sdr = vdev->vfl_type == VFL_TYPE_SDR;
bool is_rx = vdev->vfl_dir != VFL_DIR_TX;
bool is_tx = vdev->vfl_dir != VFL_DIR_RX;
@@ -662,9 +663,20 @@ static void determine_valid_ioctls(struct video_device *vdev)
ops->vidioc_try_fmt_sliced_vbi_out)))
set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap);
+ } else if (is_sdr) {
+ /* SDR specific ioctls */
+ if (ops->vidioc_enum_fmt_sdr_cap)
+ set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
+ if (ops->vidioc_g_fmt_sdr_cap)
+ set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
+ if (ops->vidioc_s_fmt_sdr_cap)
+ set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
+ if (ops->vidioc_try_fmt_sdr_cap)
+ set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
}
- if (!is_radio) {
- /* ioctls valid for video or vbi */
+
+ if (is_vid || is_vbi || is_sdr) {
+ /* ioctls valid for video, vbi or sdr */
SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
@@ -672,6 +684,10 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
+ }
+
+ if (is_vid || is_vbi) {
+ /* ioctls valid for video or vbi */
if (ops->vidioc_s_std)
set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std);
@@ -685,6 +701,7 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio);
SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio);
SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings);
+ SET_VALID_IOCTL(ops, VIDIOC_S_EDID, vidioc_s_edid);
}
if (is_tx) {
SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output);
@@ -710,9 +727,10 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings);
SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings);
SET_VALID_IOCTL(ops, VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_G_EDID, vidioc_g_edid);
}
- if (is_tx) {
- /* transmitter only ioctls */
+ if (is_tx && (is_radio || is_sdr)) {
+ /* radio transmitter only ioctls */
SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator);
SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator);
}
@@ -758,6 +776,8 @@ static void determine_valid_ioctls(struct video_device *vdev)
* %VFL_TYPE_RADIO - A radio card
*
* %VFL_TYPE_SUBDEV - A subdevice
+ *
+ * %VFL_TYPE_SDR - Software Defined Radio
*/
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
@@ -797,6 +817,10 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
case VFL_TYPE_SUBDEV:
name_base = "v4l-subdev";
break;
+ case VFL_TYPE_SDR:
+ /* Use device name 'swradio' because 'sdr' was already taken. */
+ name_base = "swradio";
+ break;
default:
printk(KERN_ERR "%s called with unknown type: %d\n",
__func__, type);
diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c
index f7902fe8a526..48b20dfcc4d0 100644
--- a/drivers/media/v4l2-core/v4l2-dv-timings.c
+++ b/drivers/media/v4l2-core/v4l2-dv-timings.c
@@ -26,6 +26,10 @@
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-dv-timings.h>
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_DESCRIPTION("V4L2 DV Timings Helper Functions");
+MODULE_LICENSE("GPL");
+
const struct v4l2_dv_timings v4l2_dv_timings_presets[] = {
V4L2_DV_BT_CEA_640X480P59_94,
V4L2_DV_BT_CEA_720X480I59_94,
@@ -324,6 +328,10 @@ EXPORT_SYMBOL_GPL(v4l2_print_dv_timings);
* This function will attempt to detect if the given values correspond to a
* valid CVT format. If so, then it will return true, and fmt will be filled
* in with the found CVT timings.
+ *
+ * TODO: VESA defined a new version 2 of their reduced blanking
+ * formula. Support for that is currently missing in this CVT
+ * detection function.
*/
bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync,
u32 polarities, struct v4l2_dv_timings *fmt)
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 707aef705a47..d9113cc71c77 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -152,6 +152,7 @@ const char *v4l2_type_names[] = {
[V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay",
[V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE] = "vid-cap-mplane",
[V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE] = "vid-out-mplane",
+ [V4L2_BUF_TYPE_SDR_CAPTURE] = "sdr-cap",
};
EXPORT_SYMBOL(v4l2_type_names);
@@ -245,6 +246,7 @@ static void v4l_print_format(const void *arg, bool write_only)
const struct v4l2_vbi_format *vbi;
const struct v4l2_sliced_vbi_format *sliced;
const struct v4l2_window *win;
+ const struct v4l2_sdr_format *sdr;
unsigned i;
pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
@@ -318,6 +320,14 @@ static void v4l_print_format(const void *arg, bool write_only)
sliced->service_lines[0][i],
sliced->service_lines[1][i]);
break;
+ case V4L2_BUF_TYPE_SDR_CAPTURE:
+ sdr = &p->fmt.sdr;
+ pr_cont(", pixelformat=%c%c%c%c\n",
+ (sdr->pixelformat >> 0) & 0xff,
+ (sdr->pixelformat >> 8) & 0xff,
+ (sdr->pixelformat >> 16) & 0xff,
+ (sdr->pixelformat >> 24) & 0xff);
+ break;
}
}
@@ -834,6 +844,14 @@ static void v4l_print_freq_band(const void *arg, bool write_only)
p->rangehigh, p->modulation);
}
+static void v4l_print_edid(const void *arg, bool write_only)
+{
+ const struct v4l2_edid *p = arg;
+
+ pr_cont("pad=%u, start_block=%u, blocks=%u\n",
+ p->pad, p->start_block, p->blocks);
+}
+
static void v4l_print_u32(const void *arg, bool write_only)
{
pr_cont("value=%u\n", *(const u32 *)arg);
@@ -881,6 +899,7 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
bool is_vbi = vfd->vfl_type == VFL_TYPE_VBI;
+ bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
@@ -930,6 +949,10 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
if (is_vbi && is_tx && ops->vidioc_g_fmt_sliced_vbi_out)
return 0;
break;
+ case V4L2_BUF_TYPE_SDR_CAPTURE:
+ if (is_sdr && is_rx && ops->vidioc_g_fmt_sdr_cap)
+ return 0;
+ break;
default:
break;
}
@@ -1049,6 +1072,10 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
if (unlikely(!is_tx || !ops->vidioc_enum_fmt_vid_out_mplane))
break;
return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_SDR_CAPTURE:
+ if (unlikely(!is_rx || !ops->vidioc_enum_fmt_sdr_cap))
+ break;
+ return ops->vidioc_enum_fmt_sdr_cap(file, fh, arg);
}
return -EINVAL;
}
@@ -1059,6 +1086,7 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
struct v4l2_format *p = arg;
struct video_device *vfd = video_devdata(file);
bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
+ bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
@@ -1103,6 +1131,10 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_out))
break;
return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_SDR_CAPTURE:
+ if (unlikely(!is_rx || !is_sdr || !ops->vidioc_g_fmt_sdr_cap))
+ break;
+ return ops->vidioc_g_fmt_sdr_cap(file, fh, arg);
}
return -EINVAL;
}
@@ -1113,6 +1145,7 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
struct v4l2_format *p = arg;
struct video_device *vfd = video_devdata(file);
bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
+ bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
@@ -1167,6 +1200,11 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
break;
CLEAR_AFTER_FIELD(p, fmt.sliced);
return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_SDR_CAPTURE:
+ if (unlikely(!is_rx || !is_sdr || !ops->vidioc_s_fmt_sdr_cap))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.sdr);
+ return ops->vidioc_s_fmt_sdr_cap(file, fh, arg);
}
return -EINVAL;
}
@@ -1177,6 +1215,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
struct v4l2_format *p = arg;
struct video_device *vfd = video_devdata(file);
bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
+ bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
@@ -1231,6 +1270,11 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
break;
CLEAR_AFTER_FIELD(p, fmt.sliced);
return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_SDR_CAPTURE:
+ if (unlikely(!is_rx || !is_sdr || !ops->vidioc_try_fmt_sdr_cap))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.sdr);
+ return ops->vidioc_try_fmt_sdr_cap(file, fh, arg);
}
return -EINVAL;
}
@@ -1291,8 +1335,11 @@ static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops,
struct video_device *vfd = video_devdata(file);
struct v4l2_frequency *p = arg;
- p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ if (vfd->vfl_type == VFL_TYPE_SDR)
+ p->type = V4L2_TUNER_ADC;
+ else
+ p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
return ops->vidioc_g_frequency(file, fh, p);
}
@@ -1303,10 +1350,15 @@ static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops,
const struct v4l2_frequency *p = arg;
enum v4l2_tuner_type type;
- type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- if (p->type != type)
- return -EINVAL;
+ if (vfd->vfl_type == VFL_TYPE_SDR) {
+ if (p->type != V4L2_TUNER_ADC && p->type != V4L2_TUNER_RF)
+ return -EINVAL;
+ } else {
+ type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ if (type != p->type)
+ return -EINVAL;
+ }
return ops->vidioc_s_frequency(file, fh, p);
}
@@ -1386,6 +1438,10 @@ static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops,
struct v4l2_hw_freq_seek *p = arg;
enum v4l2_tuner_type type;
+ /* s_hw_freq_seek is not supported for SDR for now */
+ if (vfd->vfl_type == VFL_TYPE_SDR)
+ return -EINVAL;
+
type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
if (p->type != type)
@@ -1885,11 +1941,16 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
enum v4l2_tuner_type type;
int err;
- type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-
- if (type != p->type)
- return -EINVAL;
+ if (vfd->vfl_type == VFL_TYPE_SDR) {
+ if (p->type != V4L2_TUNER_ADC && p->type != V4L2_TUNER_RF)
+ return -EINVAL;
+ type = p->type;
+ } else {
+ type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ if (type != p->type)
+ return -EINVAL;
+ }
if (ops->vidioc_enum_freq_bands)
return ops->vidioc_enum_freq_bands(file, fh, p);
if (is_valid_ioctl(vfd, VIDIOC_G_TUNER)) {
@@ -2009,6 +2070,8 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, INFO_FL_CLEAR(v4l2_edid, edid)),
+ IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_edid, edid)),
IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
@@ -2221,9 +2284,9 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
break;
}
- case VIDIOC_SUBDEV_G_EDID:
- case VIDIOC_SUBDEV_S_EDID: {
- struct v4l2_subdev_edid *edid = parg;
+ case VIDIOC_G_EDID:
+ case VIDIOC_S_EDID: {
+ struct v4l2_edid *edid = parg;
if (edid->blocks) {
if (edid->blocks > 256) {
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 996c248dea42..aea84ac5688a 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -349,10 +349,10 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
sd, pad, set_selection, subdev_fh, sel);
}
- case VIDIOC_SUBDEV_G_EDID:
+ case VIDIOC_G_EDID:
return v4l2_subdev_call(sd, pad, get_edid, arg);
- case VIDIOC_SUBDEV_S_EDID:
+ case VIDIOC_S_EDID:
return v4l2_subdev_call(sd, pad, set_edid, arg);
#endif
default:
@@ -368,6 +368,17 @@ static long subdev_ioctl(struct file *file, unsigned int cmd,
return video_usercopy(file, cmd, arg, subdev_do_ioctl);
}
+#ifdef CONFIG_COMPAT
+static long subdev_compat_ioctl32(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ return v4l2_subdev_call(sd, core, compat_ioctl32, cmd, arg);
+}
+#endif
+
static unsigned int subdev_poll(struct file *file, poll_table *wait)
{
struct video_device *vdev = video_devdata(file);
@@ -389,6 +400,9 @@ const struct v4l2_file_operations v4l2_subdev_fops = {
.owner = THIS_MODULE,
.open = subdev_open,
.unlocked_ioctl = subdev_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = subdev_compat_ioctl32,
+#endif
.release = subdev_close,
.poll = subdev_poll,
};
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index a127925c9d61..f9059bb73840 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -33,17 +33,74 @@ module_param(debug, int, 0644);
printk(KERN_DEBUG "vb2: " fmt, ## arg); \
} while (0)
-#define call_memop(q, op, args...) \
- (((q)->mem_ops->op) ? \
- ((q)->mem_ops->op(args)) : 0)
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+
+/*
+ * If advanced debugging is on, then count how often each op is called,
+ * which can either be per-buffer or per-queue.
+ *
+ * If the op failed then the 'fail_' variant is called to decrease the
+ * counter. That makes it easy to check that the 'init' and 'cleanup'
+ * (and variations thereof) stay balanced.
+ */
+
+#define call_memop(vb, op, args...) \
+({ \
+ struct vb2_queue *_q = (vb)->vb2_queue; \
+ dprintk(2, "call_memop(%p, %d, %s)%s\n", \
+ _q, (vb)->v4l2_buf.index, #op, \
+ _q->mem_ops->op ? "" : " (nop)"); \
+ (vb)->cnt_mem_ ## op++; \
+ _q->mem_ops->op ? _q->mem_ops->op(args) : 0; \
+})
+#define fail_memop(vb, op) ((vb)->cnt_mem_ ## op--)
+
+#define call_qop(q, op, args...) \
+({ \
+ dprintk(2, "call_qop(%p, %s)%s\n", q, #op, \
+ (q)->ops->op ? "" : " (nop)"); \
+ (q)->cnt_ ## op++; \
+ (q)->ops->op ? (q)->ops->op(args) : 0; \
+})
+#define fail_qop(q, op) ((q)->cnt_ ## op--)
+
+#define call_vb_qop(vb, op, args...) \
+({ \
+ struct vb2_queue *_q = (vb)->vb2_queue; \
+ dprintk(2, "call_vb_qop(%p, %d, %s)%s\n", \
+ _q, (vb)->v4l2_buf.index, #op, \
+ _q->ops->op ? "" : " (nop)"); \
+ (vb)->cnt_ ## op++; \
+ _q->ops->op ? _q->ops->op(args) : 0; \
+})
+#define fail_vb_qop(vb, op) ((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 fail_memop(vb, op)
#define call_qop(q, op, args...) \
- (((q)->ops->op) ? ((q)->ops->op(args)) : 0)
+ ((q)->ops->op ? (q)->ops->op(args) : 0)
+#define fail_qop(q, op)
+#define call_vb_qop(vb, op, args...) \
+ ((vb)->vb2_queue->ops->op ? (vb)->vb2_queue->ops->op(args) : 0)
+#define fail_vb_qop(vb, op)
+
+#endif
+
+/* Flags that are set by the vb2 core */
#define V4L2_BUFFER_MASK_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
V4L2_BUF_FLAG_PREPARED | \
V4L2_BUF_FLAG_TIMESTAMP_MASK)
+/* Output buffer flags that should be passed on to the driver */
+#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | \
+ V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_TIMECODE)
+
+static void __vb2_queue_cancel(struct vb2_queue *q);
/**
* __vb2_buf_mem_alloc() - allocate video memory for the given buffer
@@ -61,7 +118,7 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
for (plane = 0; plane < vb->num_planes; ++plane) {
unsigned long size = PAGE_ALIGN(q->plane_sizes[plane]);
- mem_priv = call_memop(q, alloc, q->alloc_ctx[plane],
+ mem_priv = call_memop(vb, alloc, q->alloc_ctx[plane],
size, q->gfp_flags);
if (IS_ERR_OR_NULL(mem_priv))
goto free;
@@ -73,9 +130,10 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
return 0;
free:
+ fail_memop(vb, alloc);
/* Free already allocated memory if one of the allocations failed */
for (; plane > 0; --plane) {
- call_memop(q, put, vb->planes[plane - 1].mem_priv);
+ call_memop(vb, put, vb->planes[plane - 1].mem_priv);
vb->planes[plane - 1].mem_priv = NULL;
}
@@ -87,11 +145,10 @@ free:
*/
static void __vb2_buf_mem_free(struct vb2_buffer *vb)
{
- struct vb2_queue *q = vb->vb2_queue;
unsigned int plane;
for (plane = 0; plane < vb->num_planes; ++plane) {
- call_memop(q, put, vb->planes[plane].mem_priv);
+ call_memop(vb, put, vb->planes[plane].mem_priv);
vb->planes[plane].mem_priv = NULL;
dprintk(3, "Freed plane %d of buffer %d\n", plane,
vb->v4l2_buf.index);
@@ -104,12 +161,11 @@ static void __vb2_buf_mem_free(struct vb2_buffer *vb)
*/
static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
{
- struct vb2_queue *q = vb->vb2_queue;
unsigned int plane;
for (plane = 0; plane < vb->num_planes; ++plane) {
if (vb->planes[plane].mem_priv)
- call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+ call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
vb->planes[plane].mem_priv = NULL;
}
}
@@ -118,15 +174,15 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
* __vb2_plane_dmabuf_put() - release memory associated with
* a DMABUF shared plane
*/
-static void __vb2_plane_dmabuf_put(struct vb2_queue *q, struct vb2_plane *p)
+static void __vb2_plane_dmabuf_put(struct vb2_buffer *vb, struct vb2_plane *p)
{
if (!p->mem_priv)
return;
if (p->dbuf_mapped)
- call_memop(q, unmap_dmabuf, p->mem_priv);
+ call_memop(vb, unmap_dmabuf, p->mem_priv);
- call_memop(q, detach_dmabuf, p->mem_priv);
+ call_memop(vb, detach_dmabuf, p->mem_priv);
dma_buf_put(p->dbuf);
memset(p, 0, sizeof(*p));
}
@@ -137,11 +193,10 @@ static void __vb2_plane_dmabuf_put(struct vb2_queue *q, struct vb2_plane *p)
*/
static void __vb2_buf_dmabuf_put(struct vb2_buffer *vb)
{
- struct vb2_queue *q = vb->vb2_queue;
unsigned int plane;
for (plane = 0; plane < vb->num_planes; ++plane)
- __vb2_plane_dmabuf_put(q, &vb->planes[plane]);
+ __vb2_plane_dmabuf_put(vb, &vb->planes[plane]);
}
/**
@@ -246,10 +301,11 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
* callback, if given. An error in initialization
* results in queue setup failure.
*/
- ret = call_qop(q, buf_init, vb);
+ ret = call_vb_qop(vb, buf_init, vb);
if (ret) {
dprintk(1, "Buffer %d %p initialization"
" failed\n", buffer, vb);
+ fail_vb_qop(vb, buf_init);
__vb2_buf_mem_free(vb);
kfree(vb);
break;
@@ -321,18 +377,79 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
}
/* Call driver-provided cleanup function for each buffer, if provided */
- if (q->ops->buf_cleanup) {
- for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
- ++buffer) {
- if (NULL == q->bufs[buffer])
- continue;
- q->ops->buf_cleanup(q->bufs[buffer]);
- }
+ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+ ++buffer) {
+ struct vb2_buffer *vb = q->bufs[buffer];
+
+ if (vb && vb->planes[0].mem_priv)
+ call_vb_qop(vb, buf_cleanup, vb);
}
/* Release video buffer memory */
__vb2_free_mem(q, buffers);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ /*
+ * Check that all the calls were balances during the life-time of this
+ * queue. If not (or if the debug level is 1 or up), then dump the
+ * counters to the kernel log.
+ */
+ if (q->num_buffers) {
+ bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming ||
+ q->cnt_wait_prepare != q->cnt_wait_finish;
+
+ 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",
+ q->cnt_queue_setup, q->cnt_start_streaming,
+ q->cnt_stop_streaming);
+ pr_info("vb2: wait_prepare: %u wait_finish: %u\n",
+ q->cnt_wait_prepare, q->cnt_wait_finish);
+ }
+ q->cnt_queue_setup = 0;
+ q->cnt_wait_prepare = 0;
+ q->cnt_wait_finish = 0;
+ q->cnt_start_streaming = 0;
+ q->cnt_stop_streaming = 0;
+ }
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ struct vb2_buffer *vb = q->bufs[buffer];
+ bool unbalanced = vb->cnt_mem_alloc != vb->cnt_mem_put ||
+ vb->cnt_mem_prepare != vb->cnt_mem_finish ||
+ vb->cnt_mem_get_userptr != vb->cnt_mem_put_userptr ||
+ vb->cnt_mem_attach_dmabuf != vb->cnt_mem_detach_dmabuf ||
+ vb->cnt_mem_map_dmabuf != vb->cnt_mem_unmap_dmabuf ||
+ vb->cnt_buf_queue != vb->cnt_buf_done ||
+ vb->cnt_buf_prepare != vb->cnt_buf_finish ||
+ vb->cnt_buf_init != vb->cnt_buf_cleanup;
+
+ 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",
+ vb->cnt_buf_init, vb->cnt_buf_cleanup,
+ vb->cnt_buf_prepare, vb->cnt_buf_finish);
+ pr_info("vb2: buf_queue: %u buf_done: %u\n",
+ vb->cnt_buf_queue, vb->cnt_buf_done);
+ pr_info("vb2: alloc: %u put: %u prepare: %u finish: %u mmap: %u\n",
+ vb->cnt_mem_alloc, vb->cnt_mem_put,
+ vb->cnt_mem_prepare, vb->cnt_mem_finish,
+ vb->cnt_mem_mmap);
+ pr_info("vb2: get_userptr: %u put_userptr: %u\n",
+ vb->cnt_mem_get_userptr, vb->cnt_mem_put_userptr);
+ pr_info("vb2: attach_dmabuf: %u detach_dmabuf: %u map_dmabuf: %u unmap_dmabuf: %u\n",
+ vb->cnt_mem_attach_dmabuf, vb->cnt_mem_detach_dmabuf,
+ vb->cnt_mem_map_dmabuf, vb->cnt_mem_unmap_dmabuf);
+ pr_info("vb2: get_dmabuf: %u num_users: %u vaddr: %u cookie: %u\n",
+ vb->cnt_mem_get_dmabuf,
+ vb->cnt_mem_num_users,
+ vb->cnt_mem_vaddr,
+ vb->cnt_mem_cookie);
+ }
+ }
+#endif
+
/* Free videobuf buffers */
for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
++buffer) {
@@ -341,9 +458,10 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
}
q->num_buffers -= buffers;
- if (!q->num_buffers)
+ if (!q->num_buffers) {
q->memory = 0;
- INIT_LIST_HEAD(&q->queued_list);
+ INIT_LIST_HEAD(&q->queued_list);
+ }
return 0;
}
@@ -424,7 +542,7 @@ static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
* case anyway. If num_users() returns more than 1,
* we are not the only user of the plane's memory.
*/
- if (mem_priv && call_memop(q, num_users, mem_priv) > 1)
+ if (mem_priv && call_memop(vb, num_users, mem_priv) > 1)
return true;
}
return false;
@@ -484,7 +602,16 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
* Clear any buffer state related flags.
*/
b->flags &= ~V4L2_BUFFER_MASK_FLAGS;
- b->flags |= q->timestamp_type;
+ b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK;
+ if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) !=
+ V4L2_BUF_FLAG_TIMESTAMP_COPY) {
+ /*
+ * For non-COPY timestamps, drop timestamp source bits
+ * and obtain the timestamp source from the queue.
+ */
+ b->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ }
switch (vb->state) {
case VB2_BUF_STATE_QUEUED:
@@ -677,6 +804,12 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
return -EBUSY;
}
+ /*
+ * Call queue_cancel to clean up any buffers in the PREPARED or
+ * QUEUED state which is possible if buffers were prepared or
+ * queued without ever calling STREAMON.
+ */
+ __vb2_queue_cancel(q);
ret = __vb2_queue_free(q, q->num_buffers);
if (ret)
return ret;
@@ -693,6 +826,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
* Make sure the requested values and current defaults are sane.
*/
num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
+ num_buffers = max_t(unsigned int, req->count, q->min_buffers_needed);
memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
q->memory = req->memory;
@@ -703,26 +837,35 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
*/
ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
q->plane_sizes, q->alloc_ctx);
- if (ret)
+ if (ret) {
+ fail_qop(q, queue_setup);
return ret;
+ }
/* Finally, allocate buffers and video memory */
- ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
- if (ret == 0) {
+ allocated_buffers = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
+ if (allocated_buffers == 0) {
dprintk(1, "Memory allocation failed\n");
return -ENOMEM;
}
- allocated_buffers = ret;
+ /*
+ * There is no point in continuing if we can't allocate the minimum
+ * number of buffers needed by this vb2_queue.
+ */
+ if (allocated_buffers < q->min_buffers_needed)
+ ret = -ENOMEM;
/*
* Check if driver can handle the allocated number of buffers.
*/
- if (allocated_buffers < num_buffers) {
+ if (!ret && allocated_buffers < num_buffers) {
num_buffers = allocated_buffers;
ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
&num_planes, q->plane_sizes, q->alloc_ctx);
+ if (ret)
+ fail_qop(q, queue_setup);
if (!ret && allocated_buffers < num_buffers)
ret = -ENOMEM;
@@ -736,6 +879,10 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
q->num_buffers = allocated_buffers;
if (ret < 0) {
+ /*
+ * Note: __vb2_queue_free() will subtract 'allocated_buffers'
+ * from q->num_buffers.
+ */
__vb2_queue_free(q, allocated_buffers);
return ret;
}
@@ -803,24 +950,24 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create
*/
ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
&num_planes, q->plane_sizes, q->alloc_ctx);
- if (ret)
+ if (ret) {
+ fail_qop(q, queue_setup);
return ret;
+ }
/* Finally, allocate buffers and video memory */
- ret = __vb2_queue_alloc(q, create->memory, num_buffers,
+ allocated_buffers = __vb2_queue_alloc(q, create->memory, num_buffers,
num_planes);
- if (ret == 0) {
+ if (allocated_buffers == 0) {
dprintk(1, "Memory allocation failed\n");
return -ENOMEM;
}
- allocated_buffers = ret;
-
/*
* Check if driver can handle the so far allocated number of buffers.
*/
- if (ret < num_buffers) {
- num_buffers = ret;
+ if (allocated_buffers < num_buffers) {
+ num_buffers = allocated_buffers;
/*
* q->num_buffers contains the total number of buffers, that the
@@ -828,6 +975,8 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create
*/
ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
&num_planes, q->plane_sizes, q->alloc_ctx);
+ if (ret)
+ fail_qop(q, queue_setup);
if (!ret && allocated_buffers < num_buffers)
ret = -ENOMEM;
@@ -841,6 +990,10 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create
q->num_buffers += allocated_buffers;
if (ret < 0) {
+ /*
+ * Note: __vb2_queue_free() will subtract 'allocated_buffers'
+ * from q->num_buffers.
+ */
__vb2_queue_free(q, allocated_buffers);
return -ENOMEM;
}
@@ -882,12 +1035,10 @@ EXPORT_SYMBOL_GPL(vb2_create_bufs);
*/
void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
{
- struct vb2_queue *q = vb->vb2_queue;
-
if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
return NULL;
- return call_memop(q, vaddr, vb->planes[plane_no].mem_priv);
+ return call_memop(vb, vaddr, vb->planes[plane_no].mem_priv);
}
EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
@@ -905,12 +1056,10 @@ EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
*/
void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no)
{
- struct vb2_queue *q = vb->vb2_queue;
-
if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
return NULL;
- return call_memop(q, cookie, vb->planes[plane_no].mem_priv);
+ return call_memop(vb, cookie, vb->planes[plane_no].mem_priv);
}
EXPORT_SYMBOL_GPL(vb2_plane_cookie);
@@ -918,13 +1067,20 @@ EXPORT_SYMBOL_GPL(vb2_plane_cookie);
* vb2_buffer_done() - inform videobuf that an operation on a buffer is finished
* @vb: vb2_buffer returned from the driver
* @state: either VB2_BUF_STATE_DONE if the operation finished successfully
- * or VB2_BUF_STATE_ERROR if the operation finished with an error
+ * or VB2_BUF_STATE_ERROR if the operation finished with an error.
+ * If start_streaming fails then it should return buffers with state
+ * VB2_BUF_STATE_QUEUED to put them back into the queue.
*
* This function should be called by the driver after a hardware operation on
* a buffer is finished and the buffer may be returned to userspace. The driver
* cannot use this buffer anymore until it is queued back to it by videobuf
* by the means of buf_queue callback. Only buffers previously queued to the
* driver by buf_queue can be passed to this function.
+ *
+ * While streaming a buffer can only be returned in state DONE or ERROR.
+ * The start_streaming op can also return them in case the DMA engine cannot
+ * be started for some reason. In that case the buffers should be returned with
+ * state QUEUED.
*/
void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
{
@@ -932,26 +1088,43 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
unsigned long flags;
unsigned int plane;
- if (vb->state != VB2_BUF_STATE_ACTIVE)
+ if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE))
return;
- if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
- return;
+ if (!q->start_streaming_called) {
+ if (WARN_ON(state != VB2_BUF_STATE_QUEUED))
+ state = VB2_BUF_STATE_QUEUED;
+ } else if (!WARN_ON(!q->start_streaming_called)) {
+ if (WARN_ON(state != VB2_BUF_STATE_DONE &&
+ state != VB2_BUF_STATE_ERROR))
+ state = VB2_BUF_STATE_ERROR;
+ }
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ /*
+ * Although this is not a callback, it still does have to balance
+ * with the buf_queue op. So update this counter manually.
+ */
+ vb->cnt_buf_done++;
+#endif
dprintk(4, "Done processing on buffer %d, state: %d\n",
vb->v4l2_buf.index, state);
/* sync buffers */
for (plane = 0; plane < vb->num_planes; ++plane)
- call_memop(q, finish, vb->planes[plane].mem_priv);
+ call_memop(vb, finish, vb->planes[plane].mem_priv);
/* Add the buffer to the done buffers list */
spin_lock_irqsave(&q->done_lock, flags);
vb->state = state;
- list_add_tail(&vb->done_entry, &q->done_list);
- atomic_dec(&q->queued_count);
+ if (state != VB2_BUF_STATE_QUEUED)
+ list_add_tail(&vb->done_entry, &q->done_list);
+ atomic_dec(&q->owned_by_drv_count);
spin_unlock_irqrestore(&q->done_lock, flags);
+ if (state == VB2_BUF_STATE_QUEUED)
+ return;
+
/* Inform any processes that may be waiting for buffers */
wake_up(&q->done_wq);
}
@@ -1025,9 +1198,31 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
}
- vb->v4l2_buf.field = b->field;
- vb->v4l2_buf.timestamp = b->timestamp;
+ /* Zero flags that the vb2 core handles */
vb->v4l2_buf.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)) {
+ /*
+ * Non-COPY timestamps and non-OUTPUT queues will get
+ * their timestamp and timestamp source flags from the
+ * queue.
+ */
+ vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ }
+
+ if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+ /*
+ * For output buffers mask out the timecode flag:
+ * this will be handled later in vb2_internal_qbuf().
+ * The 'field' is valid metadata for this output buffer
+ * and so that needs to be copied here.
+ */
+ vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TIMECODE;
+ vb->v4l2_buf.field = b->field;
+ } else {
+ /* Zero any output buffer flags as this is a capture buffer */
+ vb->v4l2_buf.flags &= ~V4L2_BUFFER_OUT_FLAGS;
+ }
}
/**
@@ -1041,6 +1236,7 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
unsigned int plane;
int ret;
int write = !V4L2_TYPE_IS_OUTPUT(q->type);
+ bool reacquired = vb->planes[0].mem_priv == NULL;
/* Copy relevant information provided by the userspace */
__fill_vb2_buffer(vb, b, planes);
@@ -1066,20 +1262,25 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
}
/* Release previously acquired memory if present */
- if (vb->planes[plane].mem_priv)
- call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+ if (vb->planes[plane].mem_priv) {
+ if (!reacquired) {
+ reacquired = true;
+ call_vb_qop(vb, buf_cleanup, vb);
+ }
+ call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
+ }
vb->planes[plane].mem_priv = NULL;
- vb->v4l2_planes[plane].m.userptr = 0;
- vb->v4l2_planes[plane].length = 0;
+ memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
/* Acquire each plane's memory */
- mem_priv = call_memop(q, get_userptr, q->alloc_ctx[plane],
+ mem_priv = call_memop(vb, get_userptr, q->alloc_ctx[plane],
planes[plane].m.userptr,
planes[plane].length, write);
if (IS_ERR_OR_NULL(mem_priv)) {
dprintk(1, "qbuf: failed acquiring userspace "
"memory for plane %d\n", plane);
+ fail_memop(vb, get_userptr);
ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL;
goto err;
}
@@ -1087,28 +1288,40 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
}
/*
- * Call driver-specific initialization on the newly acquired buffer,
- * if provided.
- */
- ret = call_qop(q, buf_init, vb);
- if (ret) {
- dprintk(1, "qbuf: buffer initialization failed\n");
- goto err;
- }
-
- /*
* Now that everything is in order, copy relevant information
* provided by userspace.
*/
for (plane = 0; plane < vb->num_planes; ++plane)
vb->v4l2_planes[plane] = planes[plane];
+ if (reacquired) {
+ /*
+ * One or more planes changed, so we must call buf_init to do
+ * the driver-specific initialization on the newly acquired
+ * buffer, if provided.
+ */
+ ret = call_vb_qop(vb, buf_init, vb);
+ if (ret) {
+ dprintk(1, "qbuf: buffer initialization failed\n");
+ fail_vb_qop(vb, buf_init);
+ goto err;
+ }
+ }
+
+ ret = call_vb_qop(vb, buf_prepare, vb);
+ if (ret) {
+ dprintk(1, "qbuf: buffer preparation failed\n");
+ fail_vb_qop(vb, buf_prepare);
+ call_vb_qop(vb, buf_cleanup, vb);
+ goto err;
+ }
+
return 0;
err:
/* In case of errors, release planes that were already acquired */
for (plane = 0; plane < vb->num_planes; ++plane) {
if (vb->planes[plane].mem_priv)
- call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+ call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
vb->planes[plane].mem_priv = NULL;
vb->v4l2_planes[plane].m.userptr = 0;
vb->v4l2_planes[plane].length = 0;
@@ -1122,8 +1335,13 @@ err:
*/
static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
{
+ int ret;
+
__fill_vb2_buffer(vb, b, vb->v4l2_planes);
- return 0;
+ ret = call_vb_qop(vb, buf_prepare, vb);
+ if (ret)
+ fail_vb_qop(vb, buf_prepare);
+ return ret;
}
/**
@@ -1137,6 +1355,7 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
unsigned int plane;
int ret;
int write = !V4L2_TYPE_IS_OUTPUT(q->type);
+ bool reacquired = vb->planes[0].mem_priv == NULL;
/* Copy relevant information provided by the userspace */
__fill_vb2_buffer(vb, b, planes);
@@ -1172,15 +1391,21 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
dprintk(1, "qbuf: buffer for plane %d changed\n", plane);
+ if (!reacquired) {
+ reacquired = true;
+ call_vb_qop(vb, buf_cleanup, vb);
+ }
+
/* Release previously acquired memory if present */
- __vb2_plane_dmabuf_put(q, &vb->planes[plane]);
+ __vb2_plane_dmabuf_put(vb, &vb->planes[plane]);
memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
/* Acquire each plane's memory */
- mem_priv = call_memop(q, attach_dmabuf, q->alloc_ctx[plane],
+ mem_priv = call_memop(vb, attach_dmabuf, q->alloc_ctx[plane],
dbuf, planes[plane].length, write);
if (IS_ERR(mem_priv)) {
dprintk(1, "qbuf: failed to attach dmabuf\n");
+ fail_memop(vb, attach_dmabuf);
ret = PTR_ERR(mem_priv);
dma_buf_put(dbuf);
goto err;
@@ -1195,32 +1420,44 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
* the buffer(s)..
*/
for (plane = 0; plane < vb->num_planes; ++plane) {
- ret = call_memop(q, map_dmabuf, vb->planes[plane].mem_priv);
+ ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv);
if (ret) {
dprintk(1, "qbuf: failed to map dmabuf for plane %d\n",
plane);
+ fail_memop(vb, map_dmabuf);
goto err;
}
vb->planes[plane].dbuf_mapped = 1;
}
/*
- * Call driver-specific initialization on the newly acquired buffer,
- * if provided.
- */
- ret = call_qop(q, buf_init, vb);
- if (ret) {
- dprintk(1, "qbuf: buffer initialization failed\n");
- goto err;
- }
-
- /*
* Now that everything is in order, copy relevant information
* provided by userspace.
*/
for (plane = 0; plane < vb->num_planes; ++plane)
vb->v4l2_planes[plane] = planes[plane];
+ if (reacquired) {
+ /*
+ * Call driver-specific initialization on the newly acquired buffer,
+ * if provided.
+ */
+ ret = call_vb_qop(vb, buf_init, vb);
+ if (ret) {
+ dprintk(1, "qbuf: buffer initialization failed\n");
+ fail_vb_qop(vb, buf_init);
+ goto err;
+ }
+ }
+
+ ret = call_vb_qop(vb, buf_prepare, vb);
+ if (ret) {
+ dprintk(1, "qbuf: buffer preparation failed\n");
+ fail_vb_qop(vb, buf_prepare);
+ call_vb_qop(vb, buf_cleanup, vb);
+ goto err;
+ }
+
return 0;
err:
/* In case of errors, release planes that were already acquired */
@@ -1238,13 +1475,13 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
unsigned int plane;
vb->state = VB2_BUF_STATE_ACTIVE;
- atomic_inc(&q->queued_count);
+ atomic_inc(&q->owned_by_drv_count);
/* sync buffers */
for (plane = 0; plane < vb->num_planes; ++plane)
- call_memop(q, prepare, vb->planes[plane].mem_priv);
+ call_memop(vb, prepare, vb->planes[plane].mem_priv);
- q->ops->buf_queue(vb);
+ call_vb_qop(vb, buf_queue, vb);
}
static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
@@ -1261,6 +1498,10 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
}
vb->state = VB2_BUF_STATE_PREPARING;
+ vb->v4l2_buf.timestamp.tv_sec = 0;
+ vb->v4l2_buf.timestamp.tv_usec = 0;
+ vb->v4l2_buf.sequence = 0;
+
switch (q->memory) {
case V4L2_MEMORY_MMAP:
ret = __qbuf_mmap(vb, b);
@@ -1295,8 +1536,6 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
ret = -EINVAL;
}
- if (!ret)
- ret = call_qop(q, buf_prepare, vb);
if (ret)
dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED;
@@ -1382,32 +1621,49 @@ EXPORT_SYMBOL_GPL(vb2_prepare_buf);
* vb2_start_streaming() - Attempt to start streaming.
* @q: videobuf2 queue
*
- * If there are not enough buffers, then retry_start_streaming is set to
- * 1 and 0 is returned. The next time a buffer is queued and
- * retry_start_streaming is 1, this function will be called again to
- * retry starting the DMA engine.
+ * Attempt to start streaming. When this function is called there must be
+ * at least q->min_buffers_needed buffers queued up (i.e. the minimum
+ * number of buffers required for the DMA engine to function). If the
+ * @start_streaming op fails it is supposed to return all the driver-owned
+ * buffers back to vb2 in state QUEUED. Check if that happened and if
+ * not warn and reclaim them forcefully.
*/
static int vb2_start_streaming(struct vb2_queue *q)
{
+ struct vb2_buffer *vb;
int ret;
- /* Tell the driver to start streaming */
- ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
-
/*
- * If there are not enough buffers queued to start streaming, then
- * the start_streaming operation will return -ENOBUFS and you have to
- * retry when the next buffer is queued.
+ * If any buffers were queued before streamon,
+ * we can now pass them to driver for processing.
*/
- if (ret == -ENOBUFS) {
- dprintk(1, "qbuf: not enough buffers, retry when more buffers are queued.\n");
- q->retry_start_streaming = 1;
+ list_for_each_entry(vb, &q->queued_list, queued_entry)
+ __enqueue_in_driver(vb);
+
+ /* Tell the driver to start streaming */
+ ret = call_qop(q, start_streaming, q,
+ atomic_read(&q->owned_by_drv_count));
+ q->start_streaming_called = ret == 0;
+ if (!ret)
return 0;
+
+ fail_qop(q, start_streaming);
+ dprintk(1, "qbuf: driver refused to start streaming\n");
+ if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
+ unsigned i;
+
+ /*
+ * Forcefully reclaim buffers if the driver did not
+ * correctly return them to vb2.
+ */
+ for (i = 0; i < q->num_buffers; ++i) {
+ vb = q->bufs[i];
+ if (vb->state == VB2_BUF_STATE_ACTIVE)
+ vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
+ }
+ /* Must be zero now */
+ WARN_ON(atomic_read(&q->owned_by_drv_count));
}
- if (ret)
- dprintk(1, "qbuf: driver refused to start streaming\n");
- else
- q->retry_start_streaming = 0;
return ret;
}
@@ -1420,11 +1676,6 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
return ret;
vb = q->bufs[b->index];
- if (vb->state != VB2_BUF_STATE_DEQUEUED) {
- dprintk(1, "%s(): invalid buffer state %d\n", __func__,
- vb->state);
- return -EINVAL;
- }
switch (vb->state) {
case VB2_BUF_STATE_DEQUEUED:
@@ -1438,7 +1689,8 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
dprintk(1, "qbuf: buffer still being prepared\n");
return -EINVAL;
default:
- dprintk(1, "qbuf: buffer already in use\n");
+ dprintk(1, "%s(): invalid buffer state %d\n", __func__,
+ vb->state);
return -EINVAL;
}
@@ -1447,19 +1699,39 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
* dequeued in dqbuf.
*/
list_add_tail(&vb->queued_entry, &q->queued_list);
+ q->queued_count++;
vb->state = VB2_BUF_STATE_QUEUED;
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ /*
+ * 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)
+ vb->v4l2_buf.timestamp = b->timestamp;
+ vb->v4l2_buf.flags |= b->flags & V4L2_BUF_FLAG_TIMECODE;
+ if (b->flags & V4L2_BUF_FLAG_TIMECODE)
+ vb->v4l2_buf.timecode = b->timecode;
+ }
/*
* If already streaming, give the buffer to driver for processing.
* If not, the buffer will be given to driver on next streamon.
*/
- if (q->streaming)
+ if (q->start_streaming_called)
__enqueue_in_driver(vb);
/* Fill buffer information for the userspace */
__fill_v4l2_buffer(vb, b);
- if (q->retry_start_streaming) {
+ /*
+ * If streamon has been called, and we haven't yet called
+ * start_streaming() since not enough buffers were queued, and
+ * we now have reached the minimum number of queued buffers,
+ * then we can finally call start_streaming().
+ */
+ if (q->streaming && !q->start_streaming_called &&
+ q->queued_count >= q->min_buffers_needed) {
ret = vb2_start_streaming(q);
if (ret)
return ret;
@@ -1614,8 +1886,8 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q)
return -EINVAL;
}
- if (!q->retry_start_streaming)
- wait_event(q->done_wq, !atomic_read(&q->queued_count));
+ if (q->start_streaming_called)
+ wait_event(q->done_wq, !atomic_read(&q->owned_by_drv_count));
return 0;
}
EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);
@@ -1639,7 +1911,7 @@ static void __vb2_dqbuf(struct vb2_buffer *vb)
for (i = 0; i < vb->num_planes; ++i) {
if (!vb->planes[i].dbuf_mapped)
continue;
- call_memop(q, unmap_dmabuf, vb->planes[i].mem_priv);
+ call_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv);
vb->planes[i].dbuf_mapped = 0;
}
}
@@ -1657,12 +1929,6 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
if (ret < 0)
return ret;
- ret = call_qop(q, buf_finish, vb);
- if (ret) {
- dprintk(1, "dqbuf: buffer finish failed\n");
- return ret;
- }
-
switch (vb->state) {
case VB2_BUF_STATE_DONE:
dprintk(3, "dqbuf: Returning done buffer\n");
@@ -1675,10 +1941,13 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
return -EINVAL;
}
+ call_vb_qop(vb, buf_finish, vb);
+
/* Fill buffer information for the userspace */
__fill_v4l2_buffer(vb, b);
/* Remove from videobuf queue */
list_del(&vb->queued_entry);
+ q->queued_count--;
/* go back to dequeued state */
__vb2_dqbuf(vb);
@@ -1729,18 +1998,23 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
{
unsigned int i;
- if (q->retry_start_streaming) {
- q->retry_start_streaming = 0;
- q->streaming = 0;
- }
-
/*
* Tell driver to stop all transactions and release all queued
* buffers.
*/
- if (q->streaming)
+ if (q->start_streaming_called)
call_qop(q, stop_streaming, q);
q->streaming = 0;
+ q->start_streaming_called = 0;
+ q->queued_count = 0;
+
+ if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
+ for (i = 0; i < q->num_buffers; ++i)
+ if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE)
+ vb2_buffer_done(q->bufs[i], VB2_BUF_STATE_ERROR);
+ /* Must be zero now */
+ WARN_ON(atomic_read(&q->owned_by_drv_count));
+ }
/*
* Remove all buffers from videobuf's list...
@@ -1751,19 +2025,31 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
* has not already dequeued before initiating cancel.
*/
INIT_LIST_HEAD(&q->done_list);
- atomic_set(&q->queued_count, 0);
+ atomic_set(&q->owned_by_drv_count, 0);
wake_up_all(&q->done_wq);
/*
* Reinitialize all buffers for next use.
+ * Make sure to call buf_finish for any queued buffers. Normally
+ * that's done in dqbuf, but that's not going to happen when we
+ * cancel the whole queue. Note: this code belongs here, not in
+ * __vb2_dqbuf() since in vb2_internal_dqbuf() there is a critical
+ * call to __fill_v4l2_buffer() after buf_finish(). That order can't
+ * be changed, so we can't move the buf_finish() to __vb2_dqbuf().
*/
- for (i = 0; i < q->num_buffers; ++i)
- __vb2_dqbuf(q->bufs[i]);
+ for (i = 0; i < q->num_buffers; ++i) {
+ struct vb2_buffer *vb = q->bufs[i];
+
+ if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ vb->state = VB2_BUF_STATE_PREPARED;
+ call_vb_qop(vb, buf_finish, vb);
+ }
+ __vb2_dqbuf(vb);
+ }
}
static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
{
- struct vb2_buffer *vb;
int ret;
if (type != q->type) {
@@ -1781,18 +2067,26 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
return -EINVAL;
}
+ if (!q->num_buffers) {
+ dprintk(1, "streamon: no buffers have been allocated\n");
+ return -EINVAL;
+ }
+ if (q->num_buffers < q->min_buffers_needed) {
+ dprintk(1, "streamon: need at least %u allocated buffers\n",
+ q->min_buffers_needed);
+ return -EINVAL;
+ }
+
/*
- * If any buffers were queued before streamon,
- * we can now pass them to driver for processing.
+ * Tell driver to start streaming provided sufficient buffers
+ * are available.
*/
- list_for_each_entry(vb, &q->queued_list, queued_entry)
- __enqueue_in_driver(vb);
-
- /* Tell driver to start streaming. */
- ret = vb2_start_streaming(q);
- if (ret) {
- __vb2_queue_cancel(q);
- return ret;
+ if (q->queued_count >= q->min_buffers_needed) {
+ ret = vb2_start_streaming(q);
+ if (ret) {
+ __vb2_queue_cancel(q);
+ return ret;
+ }
}
q->streaming = 1;
@@ -1831,14 +2125,14 @@ static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
return -EINVAL;
}
- if (!q->streaming) {
- dprintk(3, "streamoff successful: not streaming\n");
- return 0;
- }
-
/*
* Cancel will pause streaming and remove all buffers from the driver
* and videobuf, effectively returning control over them to userspace.
+ *
+ * Note that we do this even if q->streaming == 0: if you prepare or
+ * queue buffers, and then call streamoff without ever having called
+ * streamon, you would still expect those buffers to be returned to
+ * their normal dequeued state.
*/
__vb2_queue_cancel(q);
@@ -1950,10 +2244,11 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb)
vb_plane = &vb->planes[eb->plane];
- dbuf = call_memop(q, get_dmabuf, vb_plane->mem_priv, eb->flags & O_ACCMODE);
+ dbuf = call_memop(vb, get_dmabuf, vb_plane->mem_priv, eb->flags & O_ACCMODE);
if (IS_ERR_OR_NULL(dbuf)) {
dprintk(1, "Failed to export buffer %d, plane %d\n",
eb->index, eb->plane);
+ fail_memop(vb, get_dmabuf);
return -EINVAL;
}
@@ -2045,9 +2340,11 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
return -EINVAL;
}
- ret = call_memop(q, mmap, vb->planes[plane].mem_priv, vma);
- if (ret)
+ ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma);
+ if (ret) {
+ fail_memop(vb, mmap);
return ret;
+ }
dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
return 0;
@@ -2200,11 +2497,14 @@ int vb2_queue_init(struct vb2_queue *q)
WARN_ON(!q->io_modes) ||
WARN_ON(!q->ops->queue_setup) ||
WARN_ON(!q->ops->buf_queue) ||
- WARN_ON(q->timestamp_type & ~V4L2_BUF_FLAG_TIMESTAMP_MASK))
+ WARN_ON(q->timestamp_flags &
+ ~(V4L2_BUF_FLAG_TIMESTAMP_MASK |
+ V4L2_BUF_FLAG_TSTAMP_SRC_MASK)))
return -EINVAL;
/* Warn that the driver should choose an appropriate timestamp type */
- WARN_ON(q->timestamp_type == V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN);
+ WARN_ON((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) ==
+ V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN);
INIT_LIST_HEAD(&q->queued_list);
INIT_LIST_HEAD(&q->done_list);
@@ -2251,6 +2551,22 @@ struct vb2_fileio_buf {
/**
* 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
@@ -2260,7 +2576,8 @@ struct vb2_fileio_data {
struct v4l2_requestbuffers req;
struct v4l2_buffer b;
struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME];
- unsigned int index;
+ unsigned int cur_index;
+ unsigned int initial_index;
unsigned int q_count;
unsigned int dq_count;
unsigned int flags;
@@ -2280,9 +2597,9 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
/*
* Sanity check
*/
- if ((read && !(q->io_modes & VB2_READ)) ||
- (!read && !(q->io_modes & VB2_WRITE)))
- BUG();
+ 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.
@@ -2360,7 +2677,12 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
goto err_reqbufs;
fileio->bufs[i].queued = 1;
}
- fileio->index = q->num_buffers;
+ /*
+ * 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;
}
/*
@@ -2439,7 +2761,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
/*
* Check if we need to dequeue the buffer.
*/
- index = fileio->index;
+ index = fileio->cur_index;
if (index >= q->num_buffers) {
/*
* Call vb2_dqbuf to get buffer back.
@@ -2453,7 +2775,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
return ret;
fileio->dq_count += 1;
- index = fileio->b.index;
+ fileio->cur_index = index = fileio->b.index;
buf = &fileio->bufs[index];
/*
@@ -2529,8 +2851,20 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
buf->queued = 1;
buf->size = vb2_plane_size(q->bufs[index], 0);
fileio->q_count += 1;
- if (fileio->index < q->num_buffers)
- fileio->index++;
+ /*
+ * 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;
}
/*
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c
index 7dca1e640970..841717a2842c 100644
--- a/drivers/mfd/88pm800.c
+++ b/drivers/mfd/88pm800.c
@@ -571,7 +571,7 @@ static int pm800_probe(struct i2c_client *client,
ret = pm800_pages_init(chip);
if (ret) {
dev_err(&client->dev, "pm800_pages_init failed!\n");
- goto err_page_init;
+ goto err_device_init;
}
ret = device_800_init(chip, pdata);
@@ -587,7 +587,6 @@ static int pm800_probe(struct i2c_client *client,
err_device_init:
pm800_pages_exit(chip);
-err_page_init:
err_subchip_alloc:
pm80x_deinit();
out_init:
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index c9b1f6422941..bcfc9e85b4a0 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -1179,12 +1179,18 @@ static int pm860x_probe(struct i2c_client *client,
chip->companion_addr = pdata->companion_addr;
chip->companion = i2c_new_dummy(chip->client->adapter,
chip->companion_addr);
+ if (!chip->companion) {
+ dev_err(&client->dev,
+ "Failed to allocate I2C companion device\n");
+ return -ENODEV;
+ }
chip->regmap_companion = regmap_init_i2c(chip->companion,
&pm860x_regmap_config);
if (IS_ERR(chip->regmap_companion)) {
ret = PTR_ERR(chip->regmap_companion);
dev_err(&chip->companion->dev,
"Failed to allocate register map: %d\n", ret);
+ i2c_unregister_device(chip->companion);
return ret;
}
i2c_set_clientdata(chip->companion, chip);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 49bb445d846a..33834120d057 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -59,6 +59,14 @@ config MFD_AAT2870_CORE
additional drivers must be enabled in order to use the
functionality of the device.
+config MFD_BCM590XX
+ tristate "Broadcom BCM590xx PMUs"
+ select MFD_CORE
+ select REGMAP_I2C
+ depends on I2C
+ help
+ Support for the BCM590xx PMUs from Broadcom
+
config MFD_CROS_EC
tristate "ChromeOS Embedded Controller"
select MFD_CORE
@@ -100,7 +108,7 @@ config PMIC_DA903X
bool "Dialog Semiconductor DA9030/DA9034 PMIC Support"
depends on I2C=y
help
- Say yes here to support for Dialog Semiconductor DA9030 (a.k.a
+ Say yes here to add support for Dialog Semiconductor DA9030 (a.k.a
ARAVA) and DA9034 (a.k.a MICCO), these are Power Management IC
usually found on PXA processors-based platforms. This includes
the I2C driver and the core APIs _only_, you have to select
@@ -270,13 +278,18 @@ config MFD_KEMPLD
device may provide functions like watchdog, GPIO, UART and I2C bus.
The following modules are supported:
+ * COMe-bHL6
* COMe-bIP#
* COMe-bPC2 (ETXexpress-PC)
* COMe-bSC# (ETXexpress-SC T#)
+ * COMe-cBT6
* COMe-cCT6
* COMe-cDC2 (microETXexpress-DC)
+ * COMe-cHL6
* COMe-cPC2 (microETXexpress-PC)
+ * COMe-mBT10
* COMe-mCT10
+ * COMe-mTT10 (nanoETXexpress-TT)
* ETX-OH
This driver can also be built as a module. If so, the module
@@ -322,9 +335,10 @@ config MFD_MAX14577
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
+ select REGMAP_IRQ
select IRQ_DOMAIN
help
- Say yes here to support for Maxim Semiconductor MAX14577.
+ Say yes here to add support for Maxim Semiconductor MAX14577.
This is a Micro-USB IC with Charger controls on chip.
This driver provides common support for accessing the device;
additional drivers must be enabled in order to use the functionality
@@ -337,7 +351,7 @@ config MFD_MAX77686
select REGMAP_I2C
select IRQ_DOMAIN
help
- Say yes here to support for Maxim Semiconductor MAX77686.
+ Say yes here to add support for Maxim Semiconductor MAX77686.
This is a Power Management IC with RTC on chip.
This driver provides common support for accessing the device;
additional drivers must be enabled in order to use the functionality
@@ -349,7 +363,7 @@ config MFD_MAX77693
select MFD_CORE
select REGMAP_I2C
help
- Say yes here to support for Maxim Semiconductor MAX77693.
+ Say yes here to add support for Maxim Semiconductor MAX77693.
This is a companion Power Management IC with Flash, Haptic, Charger,
and MUIC(Micro USB Interface Controller) controls on chip.
This driver provides common support for accessing the device;
@@ -363,7 +377,7 @@ config MFD_MAX8907
select REGMAP_I2C
select REGMAP_IRQ
help
- Say yes here to support for Maxim Semiconductor MAX8907. This is
+ Say yes here to add support for Maxim Semiconductor MAX8907. This is
a Power Management IC. This driver provides common support for
accessing the device; additional drivers must be enabled in order
to use the functionality of the device.
@@ -373,7 +387,7 @@ config MFD_MAX8925
depends on I2C=y
select MFD_CORE
help
- Say yes here to support for Maxim Semiconductor MAX8925. This is
+ Say yes here to add support for Maxim Semiconductor MAX8925. This is
a Power Management IC. This driver provides common support for
accessing the device, additional drivers must be enabled in order
to use the functionality of the device.
@@ -384,7 +398,7 @@ config MFD_MAX8997
select MFD_CORE
select IRQ_DOMAIN
help
- Say yes here to support for Maxim Semiconductor MAX8997/8966.
+ Say yes here to add support for Maxim Semiconductor MAX8997/8966.
This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,
MUIC controls on chip.
This driver provides common support for accessing the device;
@@ -397,7 +411,7 @@ config MFD_MAX8998
select MFD_CORE
select IRQ_DOMAIN
help
- Say yes here to support for Maxim Semiconductor MAX8998 and
+ Say yes here to add support for Maxim Semiconductor MAX8998 and
National Semiconductor LP3974. This is a Power Management IC.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the functionality
@@ -473,10 +487,11 @@ config MFD_PM8XXX
config MFD_PM8921_CORE
tristate "Qualcomm PM8921 PMIC chip"
- depends on (ARCH_MSM || HEXAGON)
- depends on BROKEN
+ depends on (ARM || HEXAGON)
+ select IRQ_DOMAIN
select MFD_CORE
select MFD_PM8XXX
+ select REGMAP
help
If you say yes to this option, support will be included for the
built-in PM8921 PMIC chip.
@@ -487,16 +502,6 @@ config MFD_PM8921_CORE
Say M here if you want to include support for PM8921 chip as a module.
This will build a module called "pm8921-core".
-config MFD_PM8XXX_IRQ
- bool "Qualcomm PM8xxx IRQ features"
- depends on MFD_PM8XXX
- default y if MFD_PM8XXX
- help
- This is the IRQ driver for Qualcomm PM 8xxx PMIC chips.
-
- This is required to use certain other PM 8xxx features, such as GPIO
- and MPP.
-
config MFD_RDC321X
tristate "RDC R-321x southbridge"
select MFD_CORE
@@ -516,6 +521,16 @@ config MFD_RTSX_PCI
types of memory cards, such as Memory Stick, Memory Stick Pro,
Secure Digital and MultiMediaCard.
+config MFD_RTSX_USB
+ tristate "Realtek USB card reader"
+ depends on USB
+ select MFD_CORE
+ help
+ Select this option to get support for Realtek USB 2.0 card readers
+ including RTS5129, RTS5139, RTS5179 and RTS5170.
+ Realtek card reader supports access to many types of memory cards,
+ such as Memory Stick Pro, Secure Digital and MultiMediaCard.
+
config MFD_RC5T583
bool "Ricoh RC5T583 Power Management system device"
depends on I2C=y
@@ -774,17 +789,6 @@ config MFD_PALMAS
If you say yes here you get support for the Palmas
series of PMIC chips from Texas Instruments.
-config MFD_TI_SSP
- tristate "TI Sequencer Serial Port support"
- depends on ARCH_DAVINCI_TNETV107X
- select MFD_CORE
- ---help---
- Say Y here if you want support for the Sequencer Serial Port
- in a Texas Instruments TNETV107X SoC.
-
- To compile this driver as a module, choose M here: the
- module will be called ti-ssp.
-
config TPS6105X
tristate "TI TPS61050/61052 Boost Converters"
depends on I2C
@@ -853,6 +857,22 @@ config MFD_TPS65217
This driver can also be built as a module. If so, the module
will be called tps65217.
+config MFD_TPS65218
+ tristate "TI TPS65218 Power Management chips"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ If you say yes here you get support for the TPS65218 series of
+ Power Management chips.
+ These include voltage regulators, gpio and other features
+ that are often used in portable devices. Only regulator
+ component is currently supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called tps65218.
+
config MFD_TPS6586X
bool "TI TPS6586x Power Management chips"
depends on I2C=y
@@ -935,16 +955,6 @@ config TWL4030_CORE
high speed USB OTG transceiver, an audio codec (on most
versions) and many other features.
-config TWL4030_MADC
- tristate "TI TWL4030 MADC"
- depends on TWL4030_CORE
- help
- This driver provides support for triton TWL4030-MADC. The
- driver supports both RT and SW conversion methods.
-
- This driver can be built as a module. If so it will be
- named twl4030-madc
-
config TWL4030_POWER
bool "TI TWL4030 power resources"
depends on TWL4030_CORE && ARM
@@ -1193,9 +1203,6 @@ config MFD_STW481X
in various ST Microelectronics and ST-Ericsson embedded
Nomadik series.
-endmenu
-endif
-
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
@@ -1226,3 +1233,6 @@ config VEXPRESS_CONFIG
help
Platform configuration infrastructure for the ARM Ltd.
Versatile Express.
+
+endmenu
+endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5aea5ef0a62f..2851275e2656 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -8,12 +8,14 @@ obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
+obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o
obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o
obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o
rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
+obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o
obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
@@ -21,7 +23,6 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
-obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o
obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o
obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o
@@ -62,6 +63,7 @@ obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
obj-$(CONFIG_MFD_TPS65217) += tps65217.o
+obj-$(CONFIG_MFD_TPS65218) += tps65218.o
obj-$(CONFIG_MFD_TPS65910) += tps65910.o
tps65912-objs := tps65912-core.o tps65912-irq.o
obj-$(CONFIG_MFD_TPS65912) += tps65912.o
@@ -71,7 +73,6 @@ obj-$(CONFIG_MFD_TPS80031) += tps80031.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
-obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
obj-$(CONFIG_TWL6040_CORE) += twl6040.o
@@ -150,7 +151,6 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o
obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
-obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index aaff683cd37d..a8ee4a36a1d8 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -592,7 +592,7 @@ 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(NULL,
- num_irqs, ab8500->irq_base,
+ num_irqs, 0,
&ab8500_irq_ops, ab8500);
if (!ab8500->domain) {
@@ -1583,14 +1583,13 @@ static int ab8500_probe(struct platform_device *pdev)
if (!ab8500)
return -ENOMEM;
- if (plat)
- ab8500->irq_base = plat->irq_base;
-
ab8500->dev = &pdev->dev;
resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!resource)
+ if (!resource) {
+ dev_err(&pdev->dev, "no IRQ resource\n");
return -ENODEV;
+ }
ab8500->irq = resource->start;
@@ -1612,8 +1611,10 @@ static int ab8500_probe(struct platform_device *pdev)
else {
ret = get_register_interruptible(ab8500, AB8500_MISC,
AB8500_IC_NAME_REG, &value);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not probe HW\n");
return ret;
+ }
ab8500->version = value;
}
@@ -1759,30 +1760,30 @@ static int ab8500_probe(struct platform_device *pdev)
if (is_ab9540(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
ARRAY_SIZE(ab9540_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ 0, ab8500->domain);
else if (is_ab8540(ab8500)) {
ret = mfd_add_devices(ab8500->dev, 0, ab8540_devs,
ARRAY_SIZE(ab8540_devs), NULL,
- ab8500->irq_base, NULL);
+ 0, ab8500->domain);
if (ret)
return ret;
if (is_ab8540_1p2_or_earlier(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut1_devs,
ARRAY_SIZE(ab8540_cut1_devs), NULL,
- ab8500->irq_base, NULL);
+ 0, ab8500->domain);
else /* ab8540 >= cut2 */
ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut2_devs,
ARRAY_SIZE(ab8540_cut2_devs), NULL,
- ab8500->irq_base, NULL);
+ 0, ab8500->domain);
} else if (is_ab8505(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab8505_devs,
ARRAY_SIZE(ab8505_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ 0, ab8500->domain);
else
ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
ARRAY_SIZE(ab8500_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ 0, ab8500->domain);
if (ret)
return ret;
@@ -1790,7 +1791,7 @@ static int ab8500_probe(struct platform_device *pdev)
/* Add battery management devices */
ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
ARRAY_SIZE(ab8500_bm_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ 0, ab8500->domain);
if (ret)
dev_err(ab8500->dev, "error adding bm devices\n");
}
diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c
index 62501553d63c..f495b8b57dd7 100644
--- a/drivers/mfd/adp5520.c
+++ b/drivers/mfd/adp5520.c
@@ -20,7 +20,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
diff --git a/drivers/mfd/as3722.c b/drivers/mfd/as3722.c
index c71ff0af1547..39fa554f13bb 100644
--- a/drivers/mfd/as3722.c
+++ b/drivers/mfd/as3722.c
@@ -277,6 +277,7 @@ static const struct regmap_range as3722_readable_ranges[] = {
regmap_reg_range(AS3722_ADC0_CONTROL_REG, AS3722_ADC_CONFIGURATION_REG),
regmap_reg_range(AS3722_ASIC_ID1_REG, AS3722_ASIC_ID2_REG),
regmap_reg_range(AS3722_LOCK_REG, AS3722_LOCK_REG),
+ regmap_reg_range(AS3722_FUSE7_REG, AS3722_FUSE7_REG),
};
static const struct regmap_access_table as3722_readable_table = {
diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c
new file mode 100644
index 000000000000..e9a33c79431b
--- /dev/null
+++ b/drivers/mfd/bcm590xx.c
@@ -0,0 +1,93 @@
+/*
+ * Broadcom BCM590xx PMU
+ *
+ * Copyright 2014 Linaro Limited
+ * Author: Matt Porter <mporter@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mfd/bcm590xx.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+static const struct mfd_cell bcm590xx_devs[] = {
+ {
+ .name = "bcm590xx-vregs",
+ },
+};
+
+static const struct regmap_config bcm590xx_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = BCM590XX_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int bcm590xx_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct bcm590xx *bcm590xx;
+ int ret;
+
+ bcm590xx = devm_kzalloc(&i2c->dev, sizeof(*bcm590xx), GFP_KERNEL);
+ if (!bcm590xx)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, bcm590xx);
+ bcm590xx->dev = &i2c->dev;
+ bcm590xx->i2c_client = i2c;
+
+ bcm590xx->regmap = devm_regmap_init_i2c(i2c, &bcm590xx_regmap_config);
+ if (IS_ERR(bcm590xx->regmap)) {
+ ret = PTR_ERR(bcm590xx->regmap);
+ dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = mfd_add_devices(&i2c->dev, -1, bcm590xx_devs,
+ ARRAY_SIZE(bcm590xx_devs), NULL, 0, NULL);
+ if (ret < 0)
+ dev_err(&i2c->dev, "failed to add sub-devices: %d\n", ret);
+
+ return ret;
+}
+
+static const struct of_device_id bcm590xx_of_match[] = {
+ { .compatible = "brcm,bcm59056" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bcm590xx_of_match);
+
+static const struct i2c_device_id bcm590xx_i2c_id[] = {
+ { "bcm59056" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bcm590xx_i2c_id);
+
+static struct i2c_driver bcm590xx_i2c_driver = {
+ .driver = {
+ .name = "bcm590xx",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(bcm590xx_of_match),
+ },
+ .probe = bcm590xx_i2c_probe,
+ .id_table = bcm590xx_i2c_id,
+};
+module_i2c_driver(bcm590xx_i2c_driver);
+
+MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
+MODULE_DESCRIPTION("BCM590xx multi-function driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bcm590xx");
diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c
index 17c13012686a..be91cb5d6e78 100644
--- a/drivers/mfd/cs5535-mfd.c
+++ b/drivers/mfd/cs5535-mfd.c
@@ -23,7 +23,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/pci.h>
diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c
index 25838f10b35b..e8af816d73a9 100644
--- a/drivers/mfd/da9052-core.c
+++ b/drivers/mfd/da9052-core.c
@@ -279,6 +279,9 @@ static bool da9052_reg_volatile(struct device *dev, unsigned int reg)
case DA9052_EVENT_B_REG:
case DA9052_EVENT_C_REG:
case DA9052_EVENT_D_REG:
+ case DA9052_CONTROL_B_REG:
+ case DA9052_CONTROL_D_REG:
+ case DA9052_SUPPLY_REG:
case DA9052_FAULTLOG_REG:
case DA9052_CHG_TIME_REG:
case DA9052_ADC_RES_L_REG:
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
index c319c4ef5d49..6da8ec8ff800 100644
--- a/drivers/mfd/da9052-i2c.c
+++ b/drivers/mfd/da9052-i2c.c
@@ -75,6 +75,7 @@ static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
DA9052_PARK_REGISTER,
&val);
break;
+ case DA9053_BC:
default:
/*
* For other chips parking of I2C register
@@ -114,6 +115,7 @@ static const struct i2c_device_id da9052_i2c_id[] = {
{"da9053-aa", DA9053_AA},
{"da9053-ba", DA9053_BA},
{"da9053-bb", DA9053_BB},
+ {"da9053-bc", DA9053_BC},
{}
};
@@ -121,8 +123,9 @@ static const struct i2c_device_id da9052_i2c_id[] = {
static const struct of_device_id dialog_dt_ids[] = {
{ .compatible = "dlg,da9052", .data = &da9052_i2c_id[0] },
{ .compatible = "dlg,da9053-aa", .data = &da9052_i2c_id[1] },
- { .compatible = "dlg,da9053-ab", .data = &da9052_i2c_id[2] },
+ { .compatible = "dlg,da9053-ba", .data = &da9052_i2c_id[2] },
{ .compatible = "dlg,da9053-bb", .data = &da9052_i2c_id[3] },
+ { .compatible = "dlg,da9053-bc", .data = &da9052_i2c_id[4] },
{ /* sentinel */ }
};
#endif
diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c
index 0680bcbc53de..17666b40b70c 100644
--- a/drivers/mfd/da9052-spi.c
+++ b/drivers/mfd/da9052-spi.c
@@ -71,6 +71,7 @@ static struct spi_device_id da9052_spi_id[] = {
{"da9053-aa", DA9053_AA},
{"da9053-ba", DA9053_BA},
{"da9053-bb", DA9053_BB},
+ {"da9053-bc", DA9053_BC},
{}
};
diff --git a/drivers/mfd/da9055-i2c.c b/drivers/mfd/da9055-i2c.c
index 8103e4362132..d4d4c165eb95 100644
--- a/drivers/mfd/da9055-i2c.c
+++ b/drivers/mfd/da9055-i2c.c
@@ -15,6 +15,8 @@
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/mfd/da9055/core.h>
@@ -66,6 +68,11 @@ static struct i2c_device_id da9055_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, da9055_i2c_id);
+static const struct of_device_id da9055_of_match[] = {
+ { .compatible = "dlg,da9055-pmic", },
+ { }
+};
+
static struct i2c_driver da9055_i2c_driver = {
.probe = da9055_i2c_probe,
.remove = da9055_i2c_remove,
@@ -73,6 +80,7 @@ static struct i2c_driver da9055_i2c_driver = {
.driver = {
.name = "da9055-pmic",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(da9055_of_match),
},
};
diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c
index 26937cd01071..e70ae315abc7 100644
--- a/drivers/mfd/da9063-core.c
+++ b/drivers/mfd/da9063-core.c
@@ -110,7 +110,7 @@ static const struct mfd_cell da9063_devs[] = {
int da9063_device_init(struct da9063 *da9063, unsigned int irq)
{
struct da9063_pdata *pdata = da9063->dev->platform_data;
- int model, revision;
+ int model, variant_id, variant_code;
int ret;
if (pdata) {
@@ -141,23 +141,26 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq)
return -ENODEV;
}
- ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &revision);
+ ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &variant_id);
if (ret < 0) {
- dev_err(da9063->dev, "Cannot read chip revision id.\n");
+ dev_err(da9063->dev, "Cannot read chip variant id.\n");
return -EIO;
}
- revision >>= DA9063_CHIP_VARIANT_SHIFT;
- if (revision != 3) {
- dev_err(da9063->dev, "Unknown chip revision: %d\n", revision);
+
+ variant_code = variant_id >> DA9063_CHIP_VARIANT_SHIFT;
+
+ dev_info(da9063->dev,
+ "Device detected (chip-ID: 0x%02X, var-ID: 0x%02X)\n",
+ model, variant_id);
+
+ if (variant_code != PMIC_DA9063_BB) {
+ dev_err(da9063->dev, "Unknown chip variant code: 0x%02X\n",
+ variant_code);
return -ENODEV;
}
da9063->model = model;
- da9063->revision = revision;
-
- dev_info(da9063->dev,
- "Device detected (model-ID: 0x%02X rev-ID: 0x%02X)\n",
- model, revision);
+ da9063->variant_code = variant_code;
ret = da9063_irq_init(da9063);
if (ret) {
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index e43e6e821117..7694e0700d34 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -25,6 +25,7 @@
#include <linux/bitops.h>
#include <linux/fs.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/mfd/core.h>
@@ -2678,16 +2679,12 @@ static struct irq_domain_ops db8500_irq_ops = {
.xlate = irq_domain_xlate_twocell,
};
-static int db8500_irq_init(struct device_node *np, int irq_base)
+static int db8500_irq_init(struct device_node *np)
{
int i;
- /* In the device tree case, just take some IRQs */
- if (np)
- irq_base = 0;
-
db8500_irq_domain = irq_domain_add_simple(
- np, NUM_PRCMU_WAKEUPS, irq_base,
+ np, NUM_PRCMU_WAKEUPS, 0,
&db8500_irq_ops, NULL);
if (!db8500_irq_domain) {
@@ -3114,10 +3111,10 @@ static void db8500_prcmu_update_cpufreq(void)
}
static int db8500_prcmu_register_ab8500(struct device *parent,
- struct ab8500_platform_data *pdata,
- int irq)
+ struct ab8500_platform_data *pdata)
{
- struct resource ab8500_resource = DEFINE_RES_IRQ(irq);
+ struct device_node *np;
+ struct resource ab8500_resource;
struct mfd_cell ab8500_cell = {
.name = "ab8500-core",
.of_compatible = "stericsson,ab8500",
@@ -3128,6 +3125,20 @@ static int db8500_prcmu_register_ab8500(struct device *parent,
.num_resources = 1,
};
+ if (!parent->of_node)
+ return -ENODEV;
+
+ /* Look up the device node, sneak the IRQ out of it */
+ for_each_child_of_node(parent->of_node, np) {
+ if (of_device_is_compatible(np, ab8500_cell.of_compatible))
+ break;
+ }
+ if (!np) {
+ dev_info(parent, "could not find AB8500 node in the device tree\n");
+ return -ENODEV;
+ }
+ of_irq_to_resource_table(np, &ab8500_resource, 1);
+
return mfd_add_devices(parent, 0, &ab8500_cell, 1, NULL, 0, NULL);
}
@@ -3180,7 +3191,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev)
goto no_irq_return;
}
- db8500_irq_init(np, pdata->irq_base);
+ db8500_irq_init(np);
prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
@@ -3205,8 +3216,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev)
}
}
- err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata,
- pdata->ab_irq);
+ err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata);
if (err) {
mfd_remove_devices(&pdev->dev);
pr_err("prcmu: Failed to add ab8500 subdevice\n");
diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c
index 81b7d88af313..433f823037dd 100644
--- a/drivers/mfd/janz-cmodio.c
+++ b/drivers/mfd/janz-cmodio.c
@@ -13,7 +13,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c
index d3e23278d299..07692604e119 100644
--- a/drivers/mfd/kempld-core.c
+++ b/drivers/mfd/kempld-core.c
@@ -322,9 +322,12 @@ static int kempld_detect_device(struct kempld_device_data *pld)
return -ENODEV;
}
- /* Release hardware mutex if aquired */
- if (!(index_reg & KEMPLD_MUTEX_KEY))
+ /* Release hardware mutex if acquired */
+ if (!(index_reg & KEMPLD_MUTEX_KEY)) {
iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
+ /* PXT and COMe-cPC2 boards may require a second release */
+ iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
+ }
mutex_unlock(&pld->lock);
@@ -438,6 +441,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = {
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
+ .ident = "CHL6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cHL6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
.ident = "CHR2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
@@ -510,6 +521,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = {
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
+ .ident = "CVV6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cBT"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
.ident = "FRI2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
@@ -533,6 +552,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = {
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
+ .ident = "MVV1",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-mBT"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
.ident = "NTC1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index be93fa261ded..3f10ea3f45d1 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -58,7 +58,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
@@ -72,9 +71,11 @@
#define ACPIBASE_GPE_END 0x2f
#define ACPIBASE_SMI_OFF 0x30
#define ACPIBASE_SMI_END 0x33
+#define ACPIBASE_PMC_OFF 0x08
+#define ACPIBASE_PMC_END 0x0c
#define ACPIBASE_TCO_OFF 0x60
#define ACPIBASE_TCO_END 0x7f
-#define ACPICTRL 0x44
+#define ACPICTRL_PMCBASE 0x44
#define ACPIBASE_GCS_OFF 0x3410
#define ACPIBASE_GCS_END 0x3414
@@ -90,16 +91,17 @@
#define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i)
#define wdt_res(b, i) (&wdt_ich_res[(b) + (i)])
-struct lpc_ich_cfg {
- int base;
- int ctrl;
- int save;
-};
-
struct lpc_ich_priv {
int chipset;
- struct lpc_ich_cfg acpi;
- struct lpc_ich_cfg gpio;
+
+ int abase; /* ACPI base */
+ int actrl_pbase; /* ACPI control or PMC base */
+ int gbase; /* GPIO base */
+ int gctrl; /* GPIO control */
+
+ int abase_save; /* Cached ACPI base value */
+ int actrl_pbase_save; /* Cached ACPI control or PMC base value */
+ int gctrl_save; /* Cached GPIO control value */
};
static struct resource wdt_ich_res[] = {
@@ -111,7 +113,7 @@ static struct resource wdt_ich_res[] = {
{
.flags = IORESOURCE_IO,
},
- /* GCS */
+ /* GCS or PMC */
{
.flags = IORESOURCE_MEM,
},
@@ -211,6 +213,7 @@ enum lpc_chipsets {
LPC_LPT_LP, /* Lynx Point-LP */
LPC_WBG, /* Wellsburg */
LPC_AVN, /* Avoton SoC */
+ LPC_BAYTRAIL, /* Bay Trail SoC */
LPC_COLETO, /* Coleto Creek */
LPC_WPT_LP, /* Wildcat Point-LP */
};
@@ -303,6 +306,7 @@ static struct lpc_ich_info lpc_chipset_info[] = {
[LPC_NM10] = {
.name = "NM10",
.iTCO_version = 2,
+ .gpio_version = ICH_V7_GPIO,
},
[LPC_ICH8] = {
.name = "ICH8 or ICH8R",
@@ -499,7 +503,12 @@ static struct lpc_ich_info lpc_chipset_info[] = {
},
[LPC_AVN] = {
.name = "Avoton SoC",
- .iTCO_version = 1,
+ .iTCO_version = 3,
+ .gpio_version = AVOTON_GPIO,
+ },
+ [LPC_BAYTRAIL] = {
+ .name = "Bay Trail SoC",
+ .iTCO_version = 3,
},
[LPC_COLETO] = {
.name = "Coleto Creek",
@@ -726,6 +735,7 @@ static const struct pci_device_id lpc_ich_ids[] = {
{ PCI_VDEVICE(INTEL, 0x1f39), LPC_AVN},
{ PCI_VDEVICE(INTEL, 0x1f3a), LPC_AVN},
{ PCI_VDEVICE(INTEL, 0x1f3b), LPC_AVN},
+ { PCI_VDEVICE(INTEL, 0x0f1c), LPC_BAYTRAIL},
{ PCI_VDEVICE(INTEL, 0x2390), LPC_COLETO},
{ PCI_VDEVICE(INTEL, 0x9cc1), LPC_WPT_LP},
{ PCI_VDEVICE(INTEL, 0x9cc2), LPC_WPT_LP},
@@ -742,14 +752,20 @@ static void lpc_ich_restore_config_space(struct pci_dev *dev)
{
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
- if (priv->acpi.save >= 0) {
- pci_write_config_byte(dev, priv->acpi.ctrl, priv->acpi.save);
- priv->acpi.save = -1;
+ if (priv->abase_save >= 0) {
+ pci_write_config_byte(dev, priv->abase, priv->abase_save);
+ priv->abase_save = -1;
+ }
+
+ if (priv->actrl_pbase_save >= 0) {
+ pci_write_config_byte(dev, priv->actrl_pbase,
+ priv->actrl_pbase_save);
+ priv->actrl_pbase_save = -1;
}
- if (priv->gpio.save >= 0) {
- pci_write_config_byte(dev, priv->gpio.ctrl, priv->gpio.save);
- priv->gpio.save = -1;
+ if (priv->gctrl_save >= 0) {
+ pci_write_config_byte(dev, priv->gctrl, priv->gctrl_save);
+ priv->gctrl_save = -1;
}
}
@@ -758,9 +774,26 @@ static void lpc_ich_enable_acpi_space(struct pci_dev *dev)
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u8 reg_save;
- pci_read_config_byte(dev, priv->acpi.ctrl, &reg_save);
- pci_write_config_byte(dev, priv->acpi.ctrl, reg_save | 0x10);
- priv->acpi.save = reg_save;
+ switch (lpc_chipset_info[priv->chipset].iTCO_version) {
+ case 3:
+ /*
+ * Some chipsets (eg Avoton) enable the ACPI space in the
+ * ACPI BASE register.
+ */
+ pci_read_config_byte(dev, priv->abase, &reg_save);
+ pci_write_config_byte(dev, priv->abase, reg_save | 0x2);
+ priv->abase_save = reg_save;
+ break;
+ default:
+ /*
+ * Most chipsets enable the ACPI space in the ACPI control
+ * register.
+ */
+ pci_read_config_byte(dev, priv->actrl_pbase, &reg_save);
+ pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x80);
+ priv->actrl_pbase_save = reg_save;
+ break;
+ }
}
static void lpc_ich_enable_gpio_space(struct pci_dev *dev)
@@ -768,9 +801,20 @@ static void lpc_ich_enable_gpio_space(struct pci_dev *dev)
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u8 reg_save;
- pci_read_config_byte(dev, priv->gpio.ctrl, &reg_save);
- pci_write_config_byte(dev, priv->gpio.ctrl, reg_save | 0x10);
- priv->gpio.save = reg_save;
+ pci_read_config_byte(dev, priv->gctrl, &reg_save);
+ pci_write_config_byte(dev, priv->gctrl, reg_save | 0x10);
+ priv->gctrl_save = reg_save;
+}
+
+static void lpc_ich_enable_pmc_space(struct pci_dev *dev)
+{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+ u8 reg_save;
+
+ pci_read_config_byte(dev, priv->actrl_pbase, &reg_save);
+ pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x2);
+
+ priv->actrl_pbase_save = reg_save;
}
static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell)
@@ -815,7 +859,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev)
struct resource *res;
/* Setup power management base register */
- pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->abase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
@@ -841,7 +885,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev)
gpe0_done:
/* Setup GPIO base register */
- pci_read_config_dword(dev, priv->gpio.base, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->gbase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n");
@@ -891,7 +935,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
struct resource *res;
/* Setup power management base register */
- pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->abase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
@@ -910,14 +954,20 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
lpc_ich_enable_acpi_space(dev);
/*
+ * iTCO v2:
* Get the Memory-Mapped GCS register. To get access to it
* we have to read RCBA from PCI Config space 0xf0 and use
* it as base. GCS = RCBA + ICH6_GCS(0x3410).
+ *
+ * iTCO v3:
+ * Get the Power Management Configuration register. To get access
+ * to it we have to read the PMC BASE from config space and address
+ * the register at offset 0x8.
*/
if (lpc_chipset_info[priv->chipset].iTCO_version == 1) {
/* Don't register iomem for TCO ver 1 */
lpc_ich_cells[LPC_WDT].num_resources--;
- } else {
+ } else if (lpc_chipset_info[priv->chipset].iTCO_version == 2) {
pci_read_config_dword(dev, RCBABASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0xffffc000;
if (!(base_addr_cfg & 1)) {
@@ -926,9 +976,17 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
ret = -ENODEV;
goto wdt_done;
}
- res = wdt_mem_res(ICH_RES_MEM_GCS);
+ res = wdt_mem_res(ICH_RES_MEM_GCS_PMC);
res->start = base_addr + ACPIBASE_GCS_OFF;
res->end = base_addr + ACPIBASE_GCS_END;
+ } else if (lpc_chipset_info[priv->chipset].iTCO_version == 3) {
+ lpc_ich_enable_pmc_space(dev);
+ pci_read_config_dword(dev, ACPICTRL_PMCBASE, &base_addr_cfg);
+ base_addr = base_addr_cfg & 0xfffffe00;
+
+ res = wdt_mem_res(ICH_RES_MEM_GCS_PMC);
+ res->start = base_addr + ACPIBASE_PMC_OFF;
+ res->end = base_addr + ACPIBASE_PMC_END;
}
lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
@@ -952,28 +1010,35 @@ static int lpc_ich_probe(struct pci_dev *dev,
return -ENOMEM;
priv->chipset = id->driver_data;
- priv->acpi.save = -1;
- priv->acpi.base = ACPIBASE;
- priv->acpi.ctrl = ACPICTRL;
- priv->gpio.save = -1;
+ priv->actrl_pbase_save = -1;
+ priv->abase_save = -1;
+
+ priv->abase = ACPIBASE;
+ priv->actrl_pbase = ACPICTRL_PMCBASE;
+
+ priv->gctrl_save = -1;
if (priv->chipset <= LPC_ICH5) {
- priv->gpio.base = GPIOBASE_ICH0;
- priv->gpio.ctrl = GPIOCTRL_ICH0;
+ priv->gbase = GPIOBASE_ICH0;
+ priv->gctrl = GPIOCTRL_ICH0;
} else {
- priv->gpio.base = GPIOBASE_ICH6;
- priv->gpio.ctrl = GPIOCTRL_ICH6;
+ priv->gbase = GPIOBASE_ICH6;
+ priv->gctrl = GPIOCTRL_ICH6;
}
pci_set_drvdata(dev, priv);
- ret = lpc_ich_init_wdt(dev);
- if (!ret)
- cell_added = true;
+ if (lpc_chipset_info[priv->chipset].iTCO_version) {
+ ret = lpc_ich_init_wdt(dev);
+ if (!ret)
+ cell_added = true;
+ }
- ret = lpc_ich_init_gpio(dev);
- if (!ret)
- cell_added = true;
+ if (lpc_chipset_info[priv->chipset].gpio_version) {
+ ret = lpc_ich_init_gpio(dev);
+ if (!ret)
+ cell_added = true;
+ }
/*
* We only care if at least one or none of the cells registered
diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c
index 3bb05c03c68d..4ee755034f3b 100644
--- a/drivers/mfd/lpc_sch.c
+++ b/drivers/mfd/lpc_sch.c
@@ -23,7 +23,6 @@
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
index 71aa14a6bfbb..5f13cefe8def 100644
--- a/drivers/mfd/max14577.c
+++ b/drivers/mfd/max14577.c
@@ -18,6 +18,7 @@
* This driver is based on max8997.c
*/
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
@@ -25,7 +26,10 @@
#include <linux/mfd/max14577-private.h>
static struct mfd_cell max14577_devs[] = {
- { .name = "max14577-muic", },
+ {
+ .name = "max14577-muic",
+ .of_compatible = "maxim,max14577-muic",
+ },
{
.name = "max14577-regulator",
.of_compatible = "maxim,max14577-regulator",
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
index f53d5823a3f7..e5fce765accb 100644
--- a/drivers/mfd/max77686.c
+++ b/drivers/mfd/max77686.c
@@ -121,6 +121,10 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
dev_info(max77686->dev, "device found\n");
max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+ if (!max77686->rtc) {
+ dev_err(max77686->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max77686->rtc, max77686);
max77686_irq_init(max77686);
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
index e0859987ab6b..c5535f018466 100644
--- a/drivers/mfd/max77693.c
+++ b/drivers/mfd/max77693.c
@@ -148,9 +148,18 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
dev_info(max77693->dev, "device ID: 0x%x\n", reg_data);
max77693->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
+ if (!max77693->muic) {
+ dev_err(max77693->dev, "Failed to allocate I2C device for MUIC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max77693->muic, max77693);
max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
+ if (!max77693->haptic) {
+ dev_err(max77693->dev, "Failed to allocate I2C device for Haptic\n");
+ ret = -ENODEV;
+ goto err_i2c_haptic;
+ }
i2c_set_clientdata(max77693->haptic, max77693);
/*
@@ -184,8 +193,9 @@ err_mfd:
max77693_irq_exit(max77693);
err_irq:
err_regmap_muic:
- i2c_unregister_device(max77693->muic);
i2c_unregister_device(max77693->haptic);
+err_i2c_haptic:
+ i2c_unregister_device(max77693->muic);
return ret;
}
diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c
index 176aa26fc787..a83eed5c15ca 100644
--- a/drivers/mfd/max8925-i2c.c
+++ b/drivers/mfd/max8925-i2c.c
@@ -181,9 +181,18 @@ static int max8925_probe(struct i2c_client *client,
mutex_init(&chip->io_lock);
chip->rtc = i2c_new_dummy(chip->i2c->adapter, RTC_I2C_ADDR);
+ if (!chip->rtc) {
+ dev_err(chip->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(chip->rtc, chip);
chip->adc = i2c_new_dummy(chip->i2c->adapter, ADC_I2C_ADDR);
+ if (!chip->adc) {
+ dev_err(chip->dev, "Failed to allocate I2C device for ADC\n");
+ i2c_unregister_device(chip->rtc);
+ return -ENODEV;
+ }
i2c_set_clientdata(chip->adc, chip);
device_init_wakeup(&client->dev, 1);
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index 5adede0fb04c..8cf7a015cfe5 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -208,10 +208,26 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
mutex_init(&max8997->iolock);
max8997->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+ if (!max8997->rtc) {
+ dev_err(max8997->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max8997->rtc, max8997);
+
max8997->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
+ if (!max8997->haptic) {
+ dev_err(max8997->dev, "Failed to allocate I2C device for Haptic\n");
+ ret = -ENODEV;
+ goto err_i2c_haptic;
+ }
i2c_set_clientdata(max8997->haptic, max8997);
+
max8997->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
+ if (!max8997->muic) {
+ dev_err(max8997->dev, "Failed to allocate I2C device for MUIC\n");
+ ret = -ENODEV;
+ goto err_i2c_muic;
+ }
i2c_set_clientdata(max8997->muic, max8997);
pm_runtime_set_active(max8997->dev);
@@ -239,7 +255,9 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
err_mfd:
mfd_remove_devices(max8997->dev);
i2c_unregister_device(max8997->muic);
+err_i2c_muic:
i2c_unregister_device(max8997->haptic);
+err_i2c_haptic:
i2c_unregister_device(max8997->rtc);
return ret;
}
diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c
index 5d5e186b5d8b..592db06098e6 100644
--- a/drivers/mfd/max8998.c
+++ b/drivers/mfd/max8998.c
@@ -215,6 +215,10 @@ static int max8998_i2c_probe(struct i2c_client *i2c,
mutex_init(&max8998->iolock);
max8998->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+ if (!max8998->rtc) {
+ dev_err(&i2c->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max8998->rtc, max8998);
max8998_irq_init(max8998);
diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c
index 38ab67829791..702925e242c9 100644
--- a/drivers/mfd/mc13xxx-spi.c
+++ b/drivers/mfd/mc13xxx-spi.c
@@ -140,6 +140,11 @@ static int mc13xxx_spi_probe(struct spi_device *spi)
mc13xxx->irq = spi->irq;
+ spi->max_speed_hz = spi->max_speed_hz ? : 26000000;
+ ret = spi_setup(spi);
+ if (ret)
+ return ret;
+
mc13xxx->regmap = devm_regmap_init(&spi->dev, &regmap_mc13xxx_bus,
&spi->dev,
&mc13xxx_regmap_spi_config);
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c
index 41c31b3ac940..29d76986b40b 100644
--- a/drivers/mfd/mcp-sa11x0.c
+++ b/drivers/mfd/mcp-sa11x0.c
@@ -12,7 +12,6 @@
* MCP read/write timeouts from Jordi Colomer, rehacked by rmk.
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/kernel.h>
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 90b630ccc8bc..651e249287dc 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -665,55 +665,78 @@ static int usbhs_omap_probe(struct platform_device *pdev)
goto err_mem;
}
- need_logic_fck = false;
+ /* Set all clocks as invalid to begin with */
+ omap->ehci_logic_fck = ERR_PTR(-ENODEV);
+ omap->init_60m_fclk = ERR_PTR(-ENODEV);
+ omap->utmi_p1_gfclk = ERR_PTR(-ENODEV);
+ omap->utmi_p2_gfclk = ERR_PTR(-ENODEV);
+ omap->xclk60mhsp1_ck = ERR_PTR(-ENODEV);
+ omap->xclk60mhsp2_ck = ERR_PTR(-ENODEV);
+
for (i = 0; i < omap->nports; i++) {
- if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
- is_ehci_hsic_mode(i))
- need_logic_fck |= true;
+ omap->utmi_clk[i] = ERR_PTR(-ENODEV);
+ omap->hsic480m_clk[i] = ERR_PTR(-ENODEV);
+ omap->hsic60m_clk[i] = ERR_PTR(-ENODEV);
}
- omap->ehci_logic_fck = ERR_PTR(-EINVAL);
- if (need_logic_fck) {
- omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
- if (IS_ERR(omap->ehci_logic_fck)) {
- ret = PTR_ERR(omap->ehci_logic_fck);
- dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret);
+ /* for OMAP3 i.e. USBHS REV1 */
+ if (omap->usbhs_rev == OMAP_USBHS_REV1) {
+ need_logic_fck = false;
+ for (i = 0; i < omap->nports; i++) {
+ if (is_ehci_phy_mode(pdata->port_mode[i]) ||
+ is_ehci_tll_mode(pdata->port_mode[i]) ||
+ is_ehci_hsic_mode(pdata->port_mode[i]))
+
+ need_logic_fck |= true;
}
+
+ if (need_logic_fck) {
+ omap->ehci_logic_fck = devm_clk_get(dev,
+ "usbhost_120m_fck");
+ if (IS_ERR(omap->ehci_logic_fck)) {
+ ret = PTR_ERR(omap->ehci_logic_fck);
+ dev_err(dev, "usbhost_120m_fck failed:%d\n",
+ ret);
+ goto err_mem;
+ }
+ }
+ goto initialize;
}
- omap->utmi_p1_gfclk = clk_get(dev, "utmi_p1_gfclk");
+ /* for OMAP4+ i.e. USBHS REV2+ */
+ omap->utmi_p1_gfclk = devm_clk_get(dev, "utmi_p1_gfclk");
if (IS_ERR(omap->utmi_p1_gfclk)) {
ret = PTR_ERR(omap->utmi_p1_gfclk);
dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
- goto err_p1_gfclk;
+ goto err_mem;
}
- omap->utmi_p2_gfclk = clk_get(dev, "utmi_p2_gfclk");
+ omap->utmi_p2_gfclk = devm_clk_get(dev, "utmi_p2_gfclk");
if (IS_ERR(omap->utmi_p2_gfclk)) {
ret = PTR_ERR(omap->utmi_p2_gfclk);
dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
- goto err_p2_gfclk;
+ goto err_mem;
}
- omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
+ omap->xclk60mhsp1_ck = devm_clk_get(dev, "refclk_60m_ext_p1");
if (IS_ERR(omap->xclk60mhsp1_ck)) {
ret = PTR_ERR(omap->xclk60mhsp1_ck);
- dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
- goto err_xclk60mhsp1;
+ dev_err(dev, "refclk_60m_ext_p1 failed error:%d\n", ret);
+ goto err_mem;
}
- omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
+ omap->xclk60mhsp2_ck = devm_clk_get(dev, "refclk_60m_ext_p2");
if (IS_ERR(omap->xclk60mhsp2_ck)) {
ret = PTR_ERR(omap->xclk60mhsp2_ck);
- dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
- goto err_xclk60mhsp2;
+ dev_err(dev, "refclk_60m_ext_p2 failed error:%d\n", ret);
+ goto err_mem;
}
- omap->init_60m_fclk = clk_get(dev, "init_60m_fclk");
+ omap->init_60m_fclk = devm_clk_get(dev, "refclk_60m_int");
if (IS_ERR(omap->init_60m_fclk)) {
ret = PTR_ERR(omap->init_60m_fclk);
- dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
- goto err_init60m;
+ dev_err(dev, "refclk_60m_int failed error:%d\n", ret);
+ goto err_mem;
}
for (i = 0; i < omap->nports; i++) {
@@ -727,55 +750,72 @@ static int usbhs_omap_probe(struct platform_device *pdev)
* platforms have all clocks and we can function without
* them
*/
- omap->utmi_clk[i] = clk_get(dev, clkname);
- if (IS_ERR(omap->utmi_clk[i]))
- dev_dbg(dev, "Failed to get clock : %s : %ld\n",
- clkname, PTR_ERR(omap->utmi_clk[i]));
+ omap->utmi_clk[i] = devm_clk_get(dev, clkname);
+ if (IS_ERR(omap->utmi_clk[i])) {
+ ret = PTR_ERR(omap->utmi_clk[i]);
+ dev_err(dev, "Failed to get clock : %s : %d\n",
+ clkname, ret);
+ goto err_mem;
+ }
snprintf(clkname, sizeof(clkname),
"usb_host_hs_hsic480m_p%d_clk", i + 1);
- omap->hsic480m_clk[i] = clk_get(dev, clkname);
- if (IS_ERR(omap->hsic480m_clk[i]))
- dev_dbg(dev, "Failed to get clock : %s : %ld\n",
- clkname, PTR_ERR(omap->hsic480m_clk[i]));
+ omap->hsic480m_clk[i] = devm_clk_get(dev, clkname);
+ if (IS_ERR(omap->hsic480m_clk[i])) {
+ ret = PTR_ERR(omap->hsic480m_clk[i]);
+ dev_err(dev, "Failed to get clock : %s : %d\n",
+ clkname, ret);
+ goto err_mem;
+ }
snprintf(clkname, sizeof(clkname),
"usb_host_hs_hsic60m_p%d_clk", i + 1);
- omap->hsic60m_clk[i] = clk_get(dev, clkname);
- if (IS_ERR(omap->hsic60m_clk[i]))
- dev_dbg(dev, "Failed to get clock : %s : %ld\n",
- clkname, PTR_ERR(omap->hsic60m_clk[i]));
+ omap->hsic60m_clk[i] = devm_clk_get(dev, clkname);
+ if (IS_ERR(omap->hsic60m_clk[i])) {
+ ret = PTR_ERR(omap->hsic60m_clk[i]);
+ dev_err(dev, "Failed to get clock : %s : %d\n",
+ clkname, ret);
+ goto err_mem;
+ }
}
if (is_ehci_phy_mode(pdata->port_mode[0])) {
- /* for OMAP3, clk_set_parent fails */
ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->xclk60mhsp1_ck);
- if (ret != 0)
- dev_dbg(dev, "xclk60mhsp1_ck set parent failed: %d\n",
- ret);
+ if (ret != 0) {
+ dev_err(dev, "xclk60mhsp1_ck set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
} else if (is_ehci_tll_mode(pdata->port_mode[0])) {
ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->init_60m_fclk);
- if (ret != 0)
- dev_dbg(dev, "P0 init_60m_fclk set parent failed: %d\n",
- ret);
+ if (ret != 0) {
+ dev_err(dev, "P0 init_60m_fclk set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
}
if (is_ehci_phy_mode(pdata->port_mode[1])) {
ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->xclk60mhsp2_ck);
- if (ret != 0)
- dev_dbg(dev, "xclk60mhsp2_ck set parent failed: %d\n",
- ret);
+ if (ret != 0) {
+ dev_err(dev, "xclk60mhsp2_ck set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
} else if (is_ehci_tll_mode(pdata->port_mode[1])) {
ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->init_60m_fclk);
- if (ret != 0)
- dev_dbg(dev, "P1 init_60m_fclk set parent failed: %d\n",
- ret);
+ if (ret != 0) {
+ dev_err(dev, "P1 init_60m_fclk set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
}
+initialize:
omap_usbhs_init(dev);
if (dev->of_node) {
@@ -784,7 +824,7 @@ static int usbhs_omap_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "Failed to create DT children: %d\n", ret);
- goto err_alloc;
+ goto err_mem;
}
} else {
@@ -792,40 +832,12 @@ static int usbhs_omap_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "omap_usbhs_alloc_children failed: %d\n",
ret);
- goto err_alloc;
+ goto err_mem;
}
}
return 0;
-err_alloc:
- for (i = 0; i < omap->nports; i++) {
- if (!IS_ERR(omap->utmi_clk[i]))
- clk_put(omap->utmi_clk[i]);
- if (!IS_ERR(omap->hsic60m_clk[i]))
- clk_put(omap->hsic60m_clk[i]);
- if (!IS_ERR(omap->hsic480m_clk[i]))
- clk_put(omap->hsic480m_clk[i]);
- }
-
- clk_put(omap->init_60m_fclk);
-
-err_init60m:
- clk_put(omap->xclk60mhsp2_ck);
-
-err_xclk60mhsp2:
- clk_put(omap->xclk60mhsp1_ck);
-
-err_xclk60mhsp1:
- clk_put(omap->utmi_p2_gfclk);
-
-err_p2_gfclk:
- clk_put(omap->utmi_p1_gfclk);
-
-err_p1_gfclk:
- if (!IS_ERR(omap->ehci_logic_fck))
- clk_put(omap->ehci_logic_fck);
-
err_mem:
pm_runtime_disable(dev);
@@ -847,27 +859,6 @@ static int usbhs_omap_remove_child(struct device *dev, void *data)
*/
static int usbhs_omap_remove(struct platform_device *pdev)
{
- struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev);
- int i;
-
- for (i = 0; i < omap->nports; i++) {
- if (!IS_ERR(omap->utmi_clk[i]))
- clk_put(omap->utmi_clk[i]);
- if (!IS_ERR(omap->hsic60m_clk[i]))
- clk_put(omap->hsic60m_clk[i]);
- if (!IS_ERR(omap->hsic480m_clk[i]))
- clk_put(omap->hsic480m_clk[i]);
- }
-
- clk_put(omap->init_60m_fclk);
- clk_put(omap->utmi_p1_gfclk);
- clk_put(omap->utmi_p2_gfclk);
- clk_put(omap->xclk60mhsp2_ck);
- clk_put(omap->xclk60mhsp1_ck);
-
- if (!IS_ERR(omap->ehci_logic_fck))
- clk_put(omap->ehci_logic_fck);
-
pm_runtime_disable(&pdev->dev);
/* remove children */
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 5ee50f779ef6..532eacab6b46 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -252,7 +252,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
break;
}
- tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]),
+ tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk *) * tll->nch,
GFP_KERNEL);
if (!tll->ch_clk) {
ret = -ENOMEM;
diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c
index b8941a556d71..c1984b0d1b65 100644
--- a/drivers/mfd/pcf50633-adc.c
+++ b/drivers/mfd/pcf50633-adc.c
@@ -19,7 +19,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index 484fe66e6c88..b97a97187ae9 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -14,23 +14,316 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/ssbi.h>
+#include <linux/regmap.h>
+#include <linux/of_platform.h>
#include <linux/mfd/core.h>
-#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/mfd/pm8xxx/core.h>
+#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
+
+#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
+#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
+#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
+#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
+#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
+#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
+#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
+#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
+#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
+
+#define PM_IRQF_LVL_SEL 0x01 /* level select */
+#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
+#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
+#define PM_IRQF_CLR 0x08 /* clear interrupt */
+#define PM_IRQF_BITS_MASK 0x70
+#define PM_IRQF_BITS_SHIFT 4
+#define PM_IRQF_WRITE 0x80
+
+#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
+ PM_IRQF_MASK_RE)
+
#define REG_HWREV 0x002 /* PMIC4 revision */
#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
+#define PM8921_NR_IRQS 256
+
+struct pm_irq_chip {
+ struct device *dev;
+ struct regmap *regmap;
+ spinlock_t pm_irq_lock;
+ struct irq_domain *irqdomain;
+ unsigned int num_irqs;
+ unsigned int num_blocks;
+ unsigned int num_masters;
+ u8 config[0];
+};
+
struct pm8921 {
struct device *dev;
struct pm_irq_chip *irq_chip;
};
+static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
+ unsigned int *ip)
+{
+ int rc;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+ goto bail;
+ }
+
+ rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
+ if (rc)
+ pr_err("Failed Reading Status rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+ return rc;
+}
+
+static int
+pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp)
+{
+ int rc;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+ goto bail;
+ }
+
+ cp |= PM_IRQF_WRITE;
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_CONFIG, cp);
+ if (rc)
+ pr_err("Failed Configuring IRQ rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+ return rc;
+}
+
+static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
+{
+ int pmirq, irq, i, ret = 0;
+ unsigned int bits;
+
+ ret = pm8xxx_read_block_irq(chip, block, &bits);
+ if (ret) {
+ pr_err("Failed reading %d block ret=%d", block, ret);
+ return ret;
+ }
+ if (!bits) {
+ pr_err("block bit set in master but no irqs: %d", block);
+ return 0;
+ }
+
+ /* Check IRQ bits */
+ for (i = 0; i < 8; i++) {
+ if (bits & (1 << i)) {
+ pmirq = block * 8 + i;
+ irq = irq_find_mapping(chip->irqdomain, pmirq);
+ generic_handle_irq(irq);
+ }
+ }
+ return 0;
+}
+
+static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
+{
+ unsigned int blockbits;
+ int block_number, i, ret = 0;
+
+ ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_M_STATUS1 + master,
+ &blockbits);
+ if (ret) {
+ pr_err("Failed to read master %d ret=%d\n", master, ret);
+ return ret;
+ }
+ if (!blockbits) {
+ pr_err("master bit set in root but no blocks: %d", master);
+ return 0;
+ }
+
+ for (i = 0; i < 8; i++)
+ if (blockbits & (1 << i)) {
+ block_number = master * 8 + i; /* block # */
+ ret |= pm8xxx_irq_block_handler(chip, block_number);
+ }
+ return ret;
+}
+
+static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
+ struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+ unsigned int root;
+ int i, ret, masters = 0;
+
+ chained_irq_enter(irq_chip, desc);
+
+ ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_ROOT, &root);
+ if (ret) {
+ pr_err("Can't read root status ret=%d\n", ret);
+ return;
+ }
+
+ /* on pm8xxx series masters start from bit 1 of the root */
+ masters = root >> 1;
+
+ /* Read allowed masters for blocks. */
+ for (i = 0; i < chip->num_masters; i++)
+ if (masters & (1 << i))
+ pm8xxx_irq_master_handler(chip, i);
+
+ chained_irq_exit(irq_chip, desc);
+}
+
+static void pm8xxx_irq_mask_ack(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ irq_bit = pmirq % 8;
+
+ config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static void pm8xxx_irq_unmask(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ irq_bit = pmirq % 8;
+
+ config = chip->config[pmirq];
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ irq_bit = pmirq % 8;
+
+ chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
+ | PM_IRQF_MASK_ALL;
+ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+ if (flow_type & IRQF_TRIGGER_RISING)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+ if (flow_type & IRQF_TRIGGER_FALLING)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
+ } else {
+ chip->config[pmirq] |= PM_IRQF_LVL_SEL;
+
+ if (flow_type & IRQF_TRIGGER_HIGH)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+ else
+ chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
+ }
+
+ config = chip->config[pmirq] | PM_IRQF_CLR;
+ return pm8xxx_config_irq(chip, block, config);
+}
+
+static struct irq_chip pm8xxx_irq_chip = {
+ .name = "pm8xxx",
+ .irq_mask_ack = pm8xxx_irq_mask_ack,
+ .irq_unmask = pm8xxx_irq_unmask,
+ .irq_set_type = pm8xxx_irq_set_type,
+ .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
+};
+
+/**
+ * pm8xxx_get_irq_stat - get the status of the irq line
+ * @chip: pointer to identify a pmic irq controller
+ * @irq: the irq number
+ *
+ * The pm8xxx gpio and mpp rely on the interrupt block to read
+ * the values on their pins. This function is to facilitate reading
+ * the status of a gpio or an mpp line. The caller has to convert the
+ * gpio number to irq number.
+ *
+ * RETURNS:
+ * an int indicating the value read on that line
+ */
+static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
+{
+ int pmirq, rc;
+ unsigned int block, bits, bit;
+ unsigned long flags;
+ struct irq_data *irq_data = irq_get_irq_data(irq);
+
+ pmirq = irq_data->hwirq;
+
+ block = pmirq / 8;
+ bit = pmirq % 8;
+
+ spin_lock_irqsave(&chip->pm_irq_lock, flags);
+
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+ if (rc) {
+ pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
+ irq, pmirq, block, rc);
+ goto bail_out;
+ }
+
+ rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
+ if (rc) {
+ pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
+ irq, pmirq, block, rc);
+ goto bail_out;
+ }
+
+ rc = (bits & (1 << bit)) ? 1 : 0;
+
+bail_out:
+ spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
+
+ return rc;
+}
+
+static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct pm_irq_chip *chip = d->host_data;
+
+ irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, chip);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ return 0;
+}
+
+static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
+ .xlate = irq_domain_xlate_twocell,
+ .map = pm8xxx_irq_domain_map,
+};
+
static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
{
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
@@ -81,42 +374,35 @@ static struct pm8xxx_drvdata pm8921_drvdata = {
.pmic_read_irq_stat = pm8921_read_irq_stat,
};
-static int pm8921_add_subdevices(const struct pm8921_platform_data
- *pdata,
- struct pm8921 *pmic,
- u32 rev)
-{
- int ret = 0, irq_base = 0;
- struct pm_irq_chip *irq_chip;
-
- if (pdata->irq_pdata) {
- pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
- pdata->irq_pdata->irq_cdata.rev = rev;
- irq_base = pdata->irq_pdata->irq_base;
- irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+static const struct regmap_config ssbi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0x3ff,
+ .fast_io = true,
+ .reg_read = ssbi_reg_read,
+ .reg_write = ssbi_reg_write
+};
- if (IS_ERR(irq_chip)) {
- pr_err("Failed to init interrupts ret=%ld\n",
- PTR_ERR(irq_chip));
- return PTR_ERR(irq_chip);
- }
- pmic->irq_chip = irq_chip;
- }
- return ret;
-}
+static const struct of_device_id pm8921_id_table[] = {
+ { .compatible = "qcom,pm8058", },
+ { .compatible = "qcom,pm8921", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm8921_id_table);
static int pm8921_probe(struct platform_device *pdev)
{
- const struct pm8921_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct pm8921 *pmic;
- int rc;
- u8 val;
+ struct regmap *regmap;
+ int irq, rc;
+ unsigned int val;
u32 rev;
+ struct pm_irq_chip *chip;
+ unsigned int nirqs = PM8921_NR_IRQS;
- if (!pdata) {
- pr_err("missing platform data\n");
- return -EINVAL;
- }
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8921), GFP_KERNEL);
if (!pmic) {
@@ -124,8 +410,13 @@ static int pm8921_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ regmap = devm_regmap_init(&pdev->dev, NULL, pdev->dev.parent,
+ &ssbi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
/* Read PMIC chip revision */
- rc = ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+ rc = regmap_read(regmap, REG_HWREV, &val);
if (rc) {
pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
return rc;
@@ -134,7 +425,7 @@ static int pm8921_probe(struct platform_device *pdev)
rev = val;
/* Read PMIC chip revision 2 */
- rc = ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+ rc = regmap_read(regmap, REG_HWREV_2, &val);
if (rc) {
pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
REG_HWREV_2, rc);
@@ -147,37 +438,56 @@ static int pm8921_probe(struct platform_device *pdev)
pm8921_drvdata.pm_chip_data = pmic;
platform_set_drvdata(pdev, &pm8921_drvdata);
- rc = pm8921_add_subdevices(pdata, pmic, rev);
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip) +
+ sizeof(chip->config[0]) * nirqs,
+ GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ pmic->irq_chip = chip;
+ chip->dev = &pdev->dev;
+ chip->regmap = regmap;
+ chip->num_irqs = nirqs;
+ chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
+ chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
+ spin_lock_init(&chip->pm_irq_lock);
+
+ chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, nirqs,
+ &pm8xxx_irq_domain_ops,
+ chip);
+ if (!chip->irqdomain)
+ return -ENODEV;
+
+ irq_set_handler_data(irq, chip);
+ irq_set_chained_handler(irq, pm8xxx_irq_handler);
+ irq_set_irq_wake(irq, 1);
+
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
if (rc) {
- pr_err("Cannot add subdevices rc=%d\n", rc);
- goto err;
+ irq_set_chained_handler(irq, NULL);
+ irq_set_handler_data(irq, NULL);
+ irq_domain_remove(chip->irqdomain);
}
- /* gpio might not work if no irq device is found */
- WARN_ON(pmic->irq_chip == NULL);
+ return rc;
+}
+static int pm8921_remove_child(struct device *dev, void *unused)
+{
+ platform_device_unregister(to_platform_device(dev));
return 0;
-
-err:
- mfd_remove_devices(pmic->dev);
- return rc;
}
static int pm8921_remove(struct platform_device *pdev)
{
- struct pm8xxx_drvdata *drvdata;
- struct pm8921 *pmic = NULL;
-
- drvdata = platform_get_drvdata(pdev);
- if (drvdata)
- pmic = drvdata->pm_chip_data;
- if (pmic) {
- mfd_remove_devices(pmic->dev);
- if (pmic->irq_chip) {
- pm8xxx_irq_exit(pmic->irq_chip);
- pmic->irq_chip = NULL;
- }
- }
+ int irq = platform_get_irq(pdev, 0);
+ struct pm8921 *pmic = pm8921_drvdata.pm_chip_data;
+ struct pm_irq_chip *chip = pmic->irq_chip;
+
+ device_for_each_child(&pdev->dev, NULL, pm8921_remove_child);
+ irq_set_chained_handler(irq, NULL);
+ irq_set_handler_data(irq, NULL);
+ irq_domain_remove(chip->irqdomain);
return 0;
}
@@ -188,6 +498,7 @@ static struct platform_driver pm8921_driver = {
.driver = {
.name = "pm8921-core",
.owner = THIS_MODULE,
+ .of_match_table = pm8921_id_table,
},
};
diff --git a/drivers/mfd/pm8xxx-irq.c b/drivers/mfd/pm8xxx-irq.c
deleted file mode 100644
index 1360e20adf11..000000000000
--- a/drivers/mfd/pm8xxx-irq.c
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#define pr_fmt(fmt) "%s: " fmt, __func__
-
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/mfd/pm8xxx/core.h>
-#include <linux/mfd/pm8xxx/irq.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-/* PMIC8xxx IRQ */
-
-#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
-
-#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
-#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
-#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
-#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
-#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
-#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
-#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
-#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
-#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
-
-#define PM_IRQF_LVL_SEL 0x01 /* level select */
-#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
-#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
-#define PM_IRQF_CLR 0x08 /* clear interrupt */
-#define PM_IRQF_BITS_MASK 0x70
-#define PM_IRQF_BITS_SHIFT 4
-#define PM_IRQF_WRITE 0x80
-
-#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
- PM_IRQF_MASK_RE)
-
-struct pm_irq_chip {
- struct device *dev;
- spinlock_t pm_irq_lock;
- unsigned int devirq;
- unsigned int irq_base;
- unsigned int num_irqs;
- unsigned int num_blocks;
- unsigned int num_masters;
- u8 config[0];
-};
-
-static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
-{
- return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
-}
-
-static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
-{
- return pm8xxx_readb(chip->dev,
- SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
-}
-
-static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
-{
- int rc;
-
- spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
- if (rc) {
- pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
- goto bail;
- }
-
- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
- if (rc)
- pr_err("Failed Reading Status rc=%d\n", rc);
-bail:
- spin_unlock(&chip->pm_irq_lock);
- return rc;
-}
-
-static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
-{
- int rc;
-
- spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
- if (rc) {
- pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
- goto bail;
- }
-
- cp |= PM_IRQF_WRITE;
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
- if (rc)
- pr_err("Failed Configuring IRQ rc=%d\n", rc);
-bail:
- spin_unlock(&chip->pm_irq_lock);
- return rc;
-}
-
-static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
-{
- int pmirq, irq, i, ret = 0;
- u8 bits;
-
- ret = pm8xxx_read_block_irq(chip, block, &bits);
- if (ret) {
- pr_err("Failed reading %d block ret=%d", block, ret);
- return ret;
- }
- if (!bits) {
- pr_err("block bit set in master but no irqs: %d", block);
- return 0;
- }
-
- /* Check IRQ bits */
- for (i = 0; i < 8; i++) {
- if (bits & (1 << i)) {
- pmirq = block * 8 + i;
- irq = pmirq + chip->irq_base;
- generic_handle_irq(irq);
- }
- }
- return 0;
-}
-
-static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
-{
- u8 blockbits;
- int block_number, i, ret = 0;
-
- ret = pm8xxx_read_master_irq(chip, master, &blockbits);
- if (ret) {
- pr_err("Failed to read master %d ret=%d\n", master, ret);
- return ret;
- }
- if (!blockbits) {
- pr_err("master bit set in root but no blocks: %d", master);
- return 0;
- }
-
- for (i = 0; i < 8; i++)
- if (blockbits & (1 << i)) {
- block_number = master * 8 + i; /* block # */
- ret |= pm8xxx_irq_block_handler(chip, block_number);
- }
- return ret;
-}
-
-static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
-{
- struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
- struct irq_chip *irq_chip = irq_desc_get_chip(desc);
- u8 root;
- int i, ret, masters = 0;
-
- ret = pm8xxx_read_root_irq(chip, &root);
- if (ret) {
- pr_err("Can't read root status ret=%d\n", ret);
- return;
- }
-
- /* on pm8xxx series masters start from bit 1 of the root */
- masters = root >> 1;
-
- /* Read allowed masters for blocks. */
- for (i = 0; i < chip->num_masters; i++)
- if (masters & (1 << i))
- pm8xxx_irq_master_handler(chip, i);
-
- irq_chip->irq_ack(&desc->irq_data);
-}
-
-static void pm8xxx_irq_mask_ack(struct irq_data *d)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
- pm8xxx_config_irq(chip, block, config);
-}
-
-static void pm8xxx_irq_unmask(struct irq_data *d)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- config = chip->config[pmirq];
- pm8xxx_config_irq(chip, block, config);
-}
-
-static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
- | PM_IRQF_MASK_ALL;
- if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
- if (flow_type & IRQF_TRIGGER_RISING)
- chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
- if (flow_type & IRQF_TRIGGER_FALLING)
- chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
- } else {
- chip->config[pmirq] |= PM_IRQF_LVL_SEL;
-
- if (flow_type & IRQF_TRIGGER_HIGH)
- chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
- else
- chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
- }
-
- config = chip->config[pmirq] | PM_IRQF_CLR;
- return pm8xxx_config_irq(chip, block, config);
-}
-
-static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
-{
- return 0;
-}
-
-static struct irq_chip pm8xxx_irq_chip = {
- .name = "pm8xxx",
- .irq_mask_ack = pm8xxx_irq_mask_ack,
- .irq_unmask = pm8xxx_irq_unmask,
- .irq_set_type = pm8xxx_irq_set_type,
- .irq_set_wake = pm8xxx_irq_set_wake,
- .flags = IRQCHIP_MASK_ON_SUSPEND,
-};
-
-/**
- * pm8xxx_get_irq_stat - get the status of the irq line
- * @chip: pointer to identify a pmic irq controller
- * @irq: the irq number
- *
- * The pm8xxx gpio and mpp rely on the interrupt block to read
- * the values on their pins. This function is to facilitate reading
- * the status of a gpio or an mpp line. The caller has to convert the
- * gpio number to irq number.
- *
- * RETURNS:
- * an int indicating the value read on that line
- */
-int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
-{
- int pmirq, rc;
- u8 block, bits, bit;
- unsigned long flags;
-
- if (chip == NULL || irq < chip->irq_base ||
- irq >= chip->irq_base + chip->num_irqs)
- return -EINVAL;
-
- pmirq = irq - chip->irq_base;
-
- block = pmirq / 8;
- bit = pmirq % 8;
-
- spin_lock_irqsave(&chip->pm_irq_lock, flags);
-
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
- if (rc) {
- pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
- irq, pmirq, block, rc);
- goto bail_out;
- }
-
- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
- if (rc) {
- pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
- irq, pmirq, block, rc);
- goto bail_out;
- }
-
- rc = (bits & (1 << bit)) ? 1 : 0;
-
-bail_out:
- spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat);
-
-struct pm_irq_chip * pm8xxx_irq_init(struct device *dev,
- const struct pm8xxx_irq_platform_data *pdata)
-{
- struct pm_irq_chip *chip;
- int devirq, rc;
- unsigned int pmirq;
-
- if (!pdata) {
- pr_err("No platform data\n");
- return ERR_PTR(-EINVAL);
- }
-
- devirq = pdata->devirq;
- if (devirq < 0) {
- pr_err("missing devirq\n");
- rc = devirq;
- return ERR_PTR(-EINVAL);
- }
-
- chip = kzalloc(sizeof(struct pm_irq_chip)
- + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
- if (!chip) {
- pr_err("Cannot alloc pm_irq_chip struct\n");
- return ERR_PTR(-EINVAL);
- }
-
- chip->dev = dev;
- chip->devirq = devirq;
- chip->irq_base = pdata->irq_base;
- chip->num_irqs = pdata->irq_cdata.nirqs;
- chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
- chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
- spin_lock_init(&chip->pm_irq_lock);
-
- for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
- irq_set_chip_and_handler(chip->irq_base + pmirq,
- &pm8xxx_irq_chip,
- handle_level_irq);
- irq_set_chip_data(chip->irq_base + pmirq, chip);
-#ifdef CONFIG_ARM
- set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
-#else
- irq_set_noprobe(chip->irq_base + pmirq);
-#endif
- }
-
- irq_set_irq_type(devirq, pdata->irq_trigger_flag);
- irq_set_handler_data(devirq, chip);
- irq_set_chained_handler(devirq, pm8xxx_irq_handler);
- set_irq_wake(devirq, 1);
-
- return chip;
-}
-
-int pm8xxx_irq_exit(struct pm_irq_chip *chip)
-{
- irq_set_chained_handler(chip->devirq, NULL);
- kfree(chip);
- return 0;
-}
diff --git a/drivers/mfd/rc5t583-irq.c b/drivers/mfd/rc5t583-irq.c
index b41db5968706..bb8502020274 100644
--- a/drivers/mfd/rc5t583-irq.c
+++ b/drivers/mfd/rc5t583-irq.c
@@ -22,7 +22,6 @@
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
-#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/mfd/rc5t583.h>
diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c
index d346146249a2..c79569750be9 100644
--- a/drivers/mfd/rdc321x-southbridge.c
+++ b/drivers/mfd/rdc321x-southbridge.c
@@ -19,7 +19,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c
index c8f345f7e9a2..663f8a37aa6b 100644
--- a/drivers/mfd/retu-mfd.c
+++ b/drivers/mfd/retu-mfd.c
@@ -19,7 +19,6 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/irq.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/module.h>
diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c
new file mode 100644
index 000000000000..b53b9d46cc45
--- /dev/null
+++ b/drivers/mfd/rtsx_usb.c
@@ -0,0 +1,760 @@
+/* Driver for Realtek USB card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Roger Tseng <rogerable@realtek.com>
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/usb.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rtsx_usb.h>
+
+static int polling_pipe = 1;
+module_param(polling_pipe, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)");
+
+static struct mfd_cell rtsx_usb_cells[] = {
+ [RTSX_USB_SD_CARD] = {
+ .name = "rtsx_usb_sdmmc",
+ .pdata_size = 0,
+ },
+ [RTSX_USB_MS_CARD] = {
+ .name = "rtsx_usb_ms",
+ .pdata_size = 0,
+ },
+};
+
+static void rtsx_usb_sg_timed_out(unsigned long data)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)data;
+
+ dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__);
+ usb_sg_cancel(&ucr->current_sg);
+
+ /* we know the cancellation is caused by time-out */
+ ucr->current_sg.status = -ETIMEDOUT;
+}
+
+static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr,
+ unsigned int pipe, struct scatterlist *sg, int num_sg,
+ unsigned int length, unsigned int *act_len, int timeout)
+{
+ int ret;
+
+ dev_dbg(&ucr->pusb_intf->dev, "%s: xfer %u bytes, %d entries\n",
+ __func__, length, num_sg);
+ ret = usb_sg_init(&ucr->current_sg, ucr->pusb_dev, pipe, 0,
+ sg, num_sg, length, GFP_NOIO);
+ if (ret)
+ return ret;
+
+ ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout);
+ add_timer(&ucr->sg_timer);
+ usb_sg_wait(&ucr->current_sg);
+ del_timer(&ucr->sg_timer);
+
+ if (act_len)
+ *act_len = ucr->current_sg.bytes;
+
+ return ucr->current_sg.status;
+}
+
+int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe,
+ void *buf, unsigned int len, int num_sg,
+ unsigned int *act_len, int timeout)
+{
+ if (timeout < 600)
+ timeout = 600;
+
+ if (num_sg)
+ return rtsx_usb_bulk_transfer_sglist(ucr, pipe,
+ (struct scatterlist *)buf, num_sg, len, act_len,
+ timeout);
+ else
+ return usb_bulk_msg(ucr->pusb_dev, pipe, buf, len, act_len,
+ timeout);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_transfer_data);
+
+static inline void rtsx_usb_seq_cmd_hdr(struct rtsx_ucr *ucr,
+ u16 addr, u16 len, u8 seq_type)
+{
+ rtsx_usb_cmd_hdr_tag(ucr);
+
+ ucr->cmd_buf[PACKET_TYPE] = seq_type;
+ ucr->cmd_buf[5] = (u8)(len >> 8);
+ ucr->cmd_buf[6] = (u8)len;
+ ucr->cmd_buf[8] = (u8)(addr >> 8);
+ ucr->cmd_buf[9] = (u8)addr;
+
+ if (seq_type == SEQ_WRITE)
+ ucr->cmd_buf[STAGE_FLAG] = 0;
+ else
+ ucr->cmd_buf[STAGE_FLAG] = STAGE_R;
+}
+
+static int rtsx_usb_seq_write_register(struct rtsx_ucr *ucr,
+ u16 addr, u16 len, u8 *data)
+{
+ u16 cmd_len = ALIGN(SEQ_WRITE_DATA_OFFSET + len, 4);
+
+ if (!data)
+ return -EINVAL;
+
+ if (cmd_len > IOBUF_SIZE)
+ return -EINVAL;
+
+ rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_WRITE);
+ memcpy(ucr->cmd_buf + SEQ_WRITE_DATA_OFFSET, data, len);
+
+ return rtsx_usb_transfer_data(ucr,
+ usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT),
+ ucr->cmd_buf, cmd_len, 0, NULL, 100);
+}
+
+static int rtsx_usb_seq_read_register(struct rtsx_ucr *ucr,
+ u16 addr, u16 len, u8 *data)
+{
+ int i, ret;
+ u16 rsp_len = round_down(len, 4);
+ u16 res_len = len - rsp_len;
+
+ if (!data)
+ return -EINVAL;
+
+ /* 4-byte aligned part */
+ if (rsp_len) {
+ rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_READ);
+ ret = rtsx_usb_transfer_data(ucr,
+ usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT),
+ ucr->cmd_buf, 12, 0, NULL, 100);
+ if (ret)
+ return ret;
+
+ ret = rtsx_usb_transfer_data(ucr,
+ usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN),
+ data, rsp_len, 0, NULL, 100);
+ if (ret)
+ return ret;
+ }
+
+ /* unaligned part */
+ for (i = 0; i < res_len; i++) {
+ ret = rtsx_usb_read_register(ucr, addr + rsp_len + i,
+ data + rsp_len + i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int rtsx_usb_read_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len)
+{
+ return rtsx_usb_seq_read_register(ucr, PPBUF_BASE2, (u16)buf_len, buf);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_read_ppbuf);
+
+int rtsx_usb_write_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len)
+{
+ return rtsx_usb_seq_write_register(ucr, PPBUF_BASE2, (u16)buf_len, buf);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_write_ppbuf);
+
+int rtsx_usb_ep0_write_register(struct rtsx_ucr *ucr, u16 addr,
+ u8 mask, u8 data)
+{
+ u16 value, index;
+
+ addr |= EP0_WRITE_REG_CMD << EP0_OP_SHIFT;
+ value = swab16(addr);
+ index = mask | data << 8;
+
+ return usb_control_msg(ucr->pusb_dev,
+ usb_sndctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 100);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register);
+
+int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data)
+{
+ u16 value;
+
+ if (!data)
+ return -EINVAL;
+ *data = 0;
+
+ addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT;
+ value = swab16(addr);
+
+ return usb_control_msg(ucr->pusb_dev,
+ usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, 0, data, 1, 100);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register);
+
+void rtsx_usb_add_cmd(struct rtsx_ucr *ucr, u8 cmd_type, u16 reg_addr,
+ u8 mask, u8 data)
+{
+ int i;
+
+ if (ucr->cmd_idx < (IOBUF_SIZE - CMD_OFFSET) / 4) {
+ i = CMD_OFFSET + ucr->cmd_idx * 4;
+
+ ucr->cmd_buf[i++] = ((cmd_type & 0x03) << 6) |
+ (u8)((reg_addr >> 8) & 0x3F);
+ ucr->cmd_buf[i++] = (u8)reg_addr;
+ ucr->cmd_buf[i++] = mask;
+ ucr->cmd_buf[i++] = data;
+
+ ucr->cmd_idx++;
+ }
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_add_cmd);
+
+int rtsx_usb_send_cmd(struct rtsx_ucr *ucr, u8 flag, int timeout)
+{
+ int ret;
+
+ ucr->cmd_buf[CNT_H] = (u8)(ucr->cmd_idx >> 8);
+ ucr->cmd_buf[CNT_L] = (u8)(ucr->cmd_idx);
+ ucr->cmd_buf[STAGE_FLAG] = flag;
+
+ ret = rtsx_usb_transfer_data(ucr,
+ usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT),
+ ucr->cmd_buf, ucr->cmd_idx * 4 + CMD_OFFSET,
+ 0, NULL, timeout);
+ if (ret) {
+ rtsx_usb_clear_fsm_err(ucr);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_send_cmd);
+
+int rtsx_usb_get_rsp(struct rtsx_ucr *ucr, int rsp_len, int timeout)
+{
+ if (rsp_len <= 0)
+ return -EINVAL;
+
+ rsp_len = ALIGN(rsp_len, 4);
+
+ return rtsx_usb_transfer_data(ucr,
+ usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN),
+ ucr->rsp_buf, rsp_len, 0, NULL, timeout);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_get_rsp);
+
+static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status)
+{
+ int ret;
+
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_EXIST, 0x00, 0x00);
+ rtsx_usb_add_cmd(ucr, READ_REG_CMD, OCPSTAT, 0x00, 0x00);
+ ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
+ if (ret)
+ return ret;
+
+ ret = rtsx_usb_get_rsp(ucr, 2, 100);
+ if (ret)
+ return ret;
+
+ *status = ((ucr->rsp_buf[0] >> 2) & 0x0f) |
+ ((ucr->rsp_buf[1] & 0x03) << 4);
+
+ return 0;
+}
+
+int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status)
+{
+ int ret;
+
+ if (!status)
+ return -EINVAL;
+
+ if (polling_pipe == 0)
+ ret = usb_control_msg(ucr->pusb_dev,
+ usb_rcvctrlpipe(ucr->pusb_dev, 0),
+ RTSX_USB_REQ_POLL,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, status, 2, 100);
+ else
+ ret = rtsx_usb_get_status_with_bulk(ucr, status);
+
+ /* usb_control_msg may return positive when success */
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_get_card_status);
+
+static int rtsx_usb_write_phy_register(struct rtsx_ucr *ucr, u8 addr, u8 val)
+{
+ dev_dbg(&ucr->pusb_intf->dev, "Write 0x%x to phy register 0x%x\n",
+ val, addr);
+
+ rtsx_usb_init_cmd(ucr);
+
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VSTAIN, 0xFF, val);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, 0xFF, addr & 0x0F);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL,
+ 0xFF, (addr >> 4) & 0x0F);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01);
+
+ return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+int rtsx_usb_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask, u8 data)
+{
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, addr, mask, data);
+ return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_write_register);
+
+int rtsx_usb_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data)
+{
+ int ret;
+
+ if (data != NULL)
+ *data = 0;
+
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, READ_REG_CMD, addr, 0, 0);
+ ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
+ if (ret)
+ return ret;
+
+ ret = rtsx_usb_get_rsp(ucr, 1, 100);
+ if (ret)
+ return ret;
+
+ if (data != NULL)
+ *data = ucr->rsp_buf[0];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_read_register);
+
+static inline u8 double_ssc_depth(u8 depth)
+{
+ return (depth > 1) ? (depth - 1) : depth;
+}
+
+static u8 revise_ssc_depth(u8 ssc_depth, u8 div)
+{
+ if (div > CLK_DIV_1) {
+ if (ssc_depth > div - 1)
+ ssc_depth -= (div - 1);
+ else
+ ssc_depth = SSC_DEPTH_2M;
+ }
+
+ return ssc_depth;
+}
+
+int rtsx_usb_switch_clock(struct rtsx_ucr *ucr, unsigned int card_clock,
+ u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk)
+{
+ int ret;
+ u8 n, clk_divider, mcu_cnt, div;
+
+ if (!card_clock) {
+ ucr->cur_clk = 0;
+ return 0;
+ }
+
+ if (initial_mode) {
+ /* We use 250k(around) here, in initial stage */
+ clk_divider = SD_CLK_DIVIDE_128;
+ card_clock = 30000000;
+ } else {
+ clk_divider = SD_CLK_DIVIDE_0;
+ }
+
+ ret = rtsx_usb_write_register(ucr, SD_CFG1,
+ SD_CLK_DIVIDE_MASK, clk_divider);
+ if (ret < 0)
+ return ret;
+
+ card_clock /= 1000000;
+ dev_dbg(&ucr->pusb_intf->dev,
+ "Switch card clock to %dMHz\n", card_clock);
+
+ if (!initial_mode && double_clk)
+ card_clock *= 2;
+ dev_dbg(&ucr->pusb_intf->dev,
+ "Internal SSC clock: %dMHz (cur_clk = %d)\n",
+ card_clock, ucr->cur_clk);
+
+ if (card_clock == ucr->cur_clk)
+ return 0;
+
+ /* Converting clock value into internal settings: n and div */
+ n = card_clock - 2;
+ if ((card_clock <= 2) || (n > MAX_DIV_N))
+ return -EINVAL;
+
+ mcu_cnt = 60/card_clock + 3;
+ if (mcu_cnt > 15)
+ mcu_cnt = 15;
+
+ /* Make sure that the SSC clock div_n is not less than MIN_DIV_N */
+
+ div = CLK_DIV_1;
+ while (n < MIN_DIV_N && div < CLK_DIV_4) {
+ n = (n + 2) * 2 - 2;
+ div++;
+ }
+ dev_dbg(&ucr->pusb_intf->dev, "n = %d, div = %d\n", n, div);
+
+ if (double_clk)
+ ssc_depth = double_ssc_depth(ssc_depth);
+
+ ssc_depth = revise_ssc_depth(ssc_depth, div);
+ dev_dbg(&ucr->pusb_intf->dev, "ssc_depth = %d\n", ssc_depth);
+
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV,
+ 0x3F, (div << 4) | mcu_cnt);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL2,
+ SSC_DEPTH_MASK, ssc_depth);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB);
+ if (vpclk) {
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, 0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, PHASE_NOT_RESET);
+ }
+
+ ret = rtsx_usb_send_cmd(ucr, MODE_C, 2000);
+ if (ret < 0)
+ return ret;
+
+ ret = rtsx_usb_write_register(ucr, SSC_CTL1, 0xff,
+ SSC_RSTB | SSC_8X_EN | SSC_SEL_4M);
+ if (ret < 0)
+ return ret;
+
+ /* Wait SSC clock stable */
+ usleep_range(100, 1000);
+
+ ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0);
+ if (ret < 0)
+ return ret;
+
+ ucr->cur_clk = card_clock;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_switch_clock);
+
+int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card)
+{
+ int ret;
+ u16 val;
+ u16 cd_mask[] = {
+ [RTSX_USB_SD_CARD] = (CD_MASK & ~SD_CD),
+ [RTSX_USB_MS_CARD] = (CD_MASK & ~MS_CD)
+ };
+
+ ret = rtsx_usb_get_card_status(ucr, &val);
+ /*
+ * If get status fails, return 0 (ok) for the exclusive check
+ * and let the flow fail at somewhere else.
+ */
+ if (ret)
+ return 0;
+
+ if (val & cd_mask[card])
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_card_exclusive_check);
+
+static int rtsx_usb_reset_chip(struct rtsx_ucr *ucr)
+{
+ int ret;
+ u8 val;
+
+ rtsx_usb_init_cmd(ucr);
+
+ if (CHECK_PKG(ucr, LQFP48)) {
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+ LDO3318_PWR_MASK, LDO_SUSPEND);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+ FORCE_LDO_POWERB, FORCE_LDO_POWERB);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1,
+ 0x30, 0x10);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5,
+ 0x03, 0x01);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6,
+ 0x0C, 0x04);
+ }
+
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SYS_DUMMY0, NYET_MSAK, NYET_EN);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CD_DEGLITCH_WIDTH, 0xFF, 0x08);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+ CD_DEGLITCH_EN, XD_CD_DEGLITCH_EN, 0x0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD30_DRIVE_SEL,
+ SD30_DRIVE_MASK, DRIVER_TYPE_D);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+ CARD_DRIVE_SEL, SD20_DRIVE_MASK, 0x0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, 0xE0, 0x0);
+
+ if (ucr->is_rts5179)
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+ CARD_PULL_CTL5, 0x03, 0x01);
+
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DMA1_CTL,
+ EXTEND_DMA1_ASYNC_SIGNAL, EXTEND_DMA1_ASYNC_SIGNAL);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_INT_PEND,
+ XD_INT | MS_INT | SD_INT,
+ XD_INT | MS_INT | SD_INT);
+
+ ret = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+ if (ret)
+ return ret;
+
+ /* config non-crystal mode */
+ rtsx_usb_read_register(ucr, CFG_MODE, &val);
+ if ((val & XTAL_FREE) || ((val & CLK_MODE_MASK) == CLK_MODE_NON_XTAL)) {
+ ret = rtsx_usb_write_phy_register(ucr, 0xC2, 0x7C);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtsx_usb_init_chip(struct rtsx_ucr *ucr)
+{
+ int ret;
+ u8 val;
+
+ rtsx_usb_clear_fsm_err(ucr);
+
+ /* power on SSC */
+ ret = rtsx_usb_write_register(ucr,
+ FPDCTL, SSC_POWER_MASK, SSC_POWER_ON);
+ if (ret)
+ return ret;
+
+ usleep_range(100, 1000);
+ ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0x00);
+ if (ret)
+ return ret;
+
+ /* determine IC version */
+ ret = rtsx_usb_read_register(ucr, HW_VERSION, &val);
+ if (ret)
+ return ret;
+
+ ucr->ic_version = val & HW_VER_MASK;
+
+ /* determine package */
+ ret = rtsx_usb_read_register(ucr, CARD_SHARE_MODE, &val);
+ if (ret)
+ return ret;
+
+ if (val & CARD_SHARE_LQFP_SEL) {
+ ucr->package = LQFP48;
+ dev_dbg(&ucr->pusb_intf->dev, "Package: LQFP48\n");
+ } else {
+ ucr->package = QFN24;
+ dev_dbg(&ucr->pusb_intf->dev, "Package: QFN24\n");
+ }
+
+ /* determine IC variations */
+ rtsx_usb_read_register(ucr, CFG_MODE_1, &val);
+ if (val & RTS5179) {
+ ucr->is_rts5179 = true;
+ dev_dbg(&ucr->pusb_intf->dev, "Device is rts5179\n");
+ } else {
+ ucr->is_rts5179 = false;
+ }
+
+ return rtsx_usb_reset_chip(ucr);
+}
+
+static int rtsx_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(intf);
+ struct rtsx_ucr *ucr;
+ int ret;
+
+ dev_dbg(&intf->dev,
+ ": Realtek USB Card Reader found at bus %03d address %03d\n",
+ usb_dev->bus->busnum, usb_dev->devnum);
+
+ ucr = devm_kzalloc(&intf->dev, sizeof(*ucr), GFP_KERNEL);
+ if (!ucr)
+ return -ENOMEM;
+
+ ucr->pusb_dev = usb_dev;
+
+ ucr->iobuf = usb_alloc_coherent(ucr->pusb_dev, IOBUF_SIZE,
+ GFP_KERNEL, &ucr->iobuf_dma);
+ if (!ucr->iobuf)
+ return -ENOMEM;
+
+ usb_set_intfdata(intf, ucr);
+
+ ucr->vendor_id = id->idVendor;
+ ucr->product_id = id->idProduct;
+ ucr->cmd_buf = ucr->rsp_buf = ucr->iobuf;
+
+ mutex_init(&ucr->dev_mutex);
+
+ ucr->pusb_intf = intf;
+
+ /* initialize */
+ ret = rtsx_usb_init_chip(ucr);
+ if (ret)
+ goto out_init_fail;
+
+ ret = mfd_add_devices(&intf->dev, usb_dev->devnum, rtsx_usb_cells,
+ ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL);
+ if (ret)
+ goto out_init_fail;
+
+ /* initialize USB SG transfer timer */
+ init_timer(&ucr->sg_timer);
+ setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr);
+#ifdef CONFIG_PM
+ intf->needs_remote_wakeup = 1;
+ usb_enable_autosuspend(usb_dev);
+#endif
+
+ return 0;
+
+out_init_fail:
+ usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf,
+ ucr->iobuf_dma);
+ return ret;
+}
+
+static void rtsx_usb_disconnect(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ dev_dbg(&intf->dev, "%s called\n", __func__);
+
+ mfd_remove_devices(&intf->dev);
+
+ usb_set_intfdata(ucr->pusb_intf, NULL);
+ usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf,
+ ucr->iobuf_dma);
+}
+
+#ifdef CONFIG_PM
+static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct rtsx_ucr *ucr =
+ (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ dev_dbg(&intf->dev, "%s called with pm message 0x%04u\n",
+ __func__, message.event);
+
+ mutex_lock(&ucr->dev_mutex);
+ rtsx_usb_turn_off_led(ucr);
+ mutex_unlock(&ucr->dev_mutex);
+ return 0;
+}
+
+static int rtsx_usb_resume(struct usb_interface *intf)
+{
+ return 0;
+}
+
+static int rtsx_usb_reset_resume(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr =
+ (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ rtsx_usb_reset_chip(ucr);
+ return 0;
+}
+
+#else /* CONFIG_PM */
+
+#define rtsx_usb_suspend NULL
+#define rtsx_usb_resume NULL
+#define rtsx_usb_reset_resume NULL
+
+#endif /* CONFIG_PM */
+
+
+static int rtsx_usb_pre_reset(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ mutex_lock(&ucr->dev_mutex);
+ return 0;
+}
+
+static int rtsx_usb_post_reset(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ mutex_unlock(&ucr->dev_mutex);
+ return 0;
+}
+
+static struct usb_device_id rtsx_usb_usb_ids[] = {
+ { USB_DEVICE(0x0BDA, 0x0129) },
+ { USB_DEVICE(0x0BDA, 0x0139) },
+ { USB_DEVICE(0x0BDA, 0x0140) },
+ { }
+};
+
+static struct usb_driver rtsx_usb_driver = {
+ .name = "rtsx_usb",
+ .probe = rtsx_usb_probe,
+ .disconnect = rtsx_usb_disconnect,
+ .suspend = rtsx_usb_suspend,
+ .resume = rtsx_usb_resume,
+ .reset_resume = rtsx_usb_reset_resume,
+ .pre_reset = rtsx_usb_pre_reset,
+ .post_reset = rtsx_usb_post_reset,
+ .id_table = rtsx_usb_usb_ids,
+ .supports_autosuspend = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(rtsx_usb_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>");
+MODULE_DESCRIPTION("Realtek USB Card Reader Driver");
diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c
index 281a82747275..1cf27521fff4 100644
--- a/drivers/mfd/sec-core.c
+++ b/drivers/mfd/sec-core.c
@@ -60,6 +60,7 @@ static const struct mfd_cell s5m8767_devs[] = {
.name = "s5m-rtc",
}, {
.name = "s5m8767-clk",
+ .of_compatible = "samsung,s5m8767-clk",
}
};
@@ -68,6 +69,7 @@ static const struct mfd_cell s2mps11_devs[] = {
.name = "s2mps11-pmic",
}, {
.name = "s2mps11-clk",
+ .of_compatible = "samsung,s2mps11-clk",
}
};
@@ -78,6 +80,7 @@ static const struct mfd_cell s2mps14_devs[] = {
.name = "s2mps14-rtc",
}, {
.name = "s2mps14-clk",
+ .of_compatible = "samsung,s2mps14-clk",
}
};
@@ -295,6 +298,13 @@ static int sec_pmic_probe(struct i2c_client *i2c,
switch (sec_pmic->device_type) {
case S2MPA01:
regmap = &s2mpa01_regmap_config;
+ /*
+ * The rtc-s5m driver does not support S2MPA01 and there
+ * is no mfd_cell for S2MPA01 RTC device.
+ * However we must pass something to devm_regmap_init_i2c()
+ * so use S5M-like regmap config even though it wouldn't work.
+ */
+ regmap_rtc = &s5m_rtc_regmap_config;
break;
case S2MPS11X:
regmap = &s2mps11_regmap_config;
@@ -344,7 +354,7 @@ static int sec_pmic_probe(struct i2c_client *i2c,
ret = PTR_ERR(sec_pmic->regmap_rtc);
dev_err(&i2c->dev, "Failed to allocate RTC register map: %d\n",
ret);
- return ret;
+ goto err_regmap_rtc;
}
if (pdata && pdata->cfg_pmic_irq)
@@ -385,14 +395,15 @@ static int sec_pmic_probe(struct i2c_client *i2c,
}
if (ret)
- goto err;
+ goto err_mfd;
device_init_wakeup(sec_pmic->dev, sec_pmic->wakeup);
return ret;
-err:
+err_mfd:
sec_irq_exit(sec_pmic);
+err_regmap_rtc:
i2c_unregister_device(sec_pmic->rtc);
return ret;
}
diff --git a/drivers/mfd/smsc-ece1099.c b/drivers/mfd/smsc-ece1099.c
index 24ae3d8421c5..90112d4cc905 100644
--- a/drivers/mfd/smsc-ece1099.c
+++ b/drivers/mfd/smsc-ece1099.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 42ccd0544513..4a91f6771fb8 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -706,7 +706,7 @@ static int stmpe1801_reset(struct stmpe *stmpe)
if (!(ret & STMPE1801_MSK_SYS_CTRL_RESET))
return 0;
usleep_range(100, 200);
- };
+ }
return -EIO;
}
diff --git a/drivers/mfd/stw481x.c b/drivers/mfd/stw481x.c
index 1243d5c6a448..7ceb3df09e25 100644
--- a/drivers/mfd/stw481x.c
+++ b/drivers/mfd/stw481x.c
@@ -167,7 +167,7 @@ static struct mfd_cell stw481x_cells[] = {
},
};
-const struct regmap_config stw481x_regmap_config = {
+static const struct regmap_config stw481x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
@@ -186,6 +186,12 @@ static int stw481x_probe(struct i2c_client *client,
i2c_set_clientdata(client, stw481x);
stw481x->client = client;
stw481x->map = devm_regmap_init_i2c(client, &stw481x_regmap_config);
+ if (IS_ERR(stw481x->map)) {
+ ret = PTR_ERR(stw481x->map);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
ret = stw481x_startup(stw481x);
if (ret) {
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 71841f9181bd..dbea55de4397 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -69,13 +69,6 @@ EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);
static int syscon_match_pdevname(struct device *dev, void *data)
{
- struct platform_device *pdev = to_platform_device(dev);
- const struct platform_device_id *id = platform_get_device_id(pdev);
-
- if (id)
- if (!strcmp(id->name, (const char *)data))
- return 1;
-
return !strcmp(dev_name(dev), (const char *)data);
}
@@ -152,7 +145,7 @@ static int syscon_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, syscon);
- dev_info(dev, "regmap %pR registered\n", res);
+ dev_dbg(dev, "regmap %pR registered\n", res);
return 0;
}
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index 2cf636c267d9..bd83accc0f6d 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -13,8 +13,10 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tc3589x.h>
+#include <linux/err.h>
/**
* enum tc3589x_version - indicates the TC3589x version
@@ -160,7 +162,7 @@ static const struct mfd_cell tc3589x_dev_gpio[] = {
.name = "tc3589x-gpio",
.num_resources = ARRAY_SIZE(gpio_resources),
.resources = &gpio_resources[0],
- .of_compatible = "tc3589x-gpio",
+ .of_compatible = "toshiba,tc3589x-gpio",
},
};
@@ -169,7 +171,7 @@ static const struct mfd_cell tc3589x_dev_keypad[] = {
.name = "tc3589x-keypad",
.num_resources = ARRAY_SIZE(keypad_resources),
.resources = &keypad_resources[0],
- .of_compatible = "tc3589x-keypad",
+ .of_compatible = "toshiba,tc3589x-keypad",
},
};
@@ -318,45 +320,74 @@ static int tc3589x_device_init(struct tc3589x *tc3589x)
return ret;
}
-static int tc3589x_of_probe(struct device_node *np,
- struct tc3589x_platform_data *pdata)
+#ifdef CONFIG_OF
+static const struct of_device_id tc3589x_match[] = {
+ /* Legacy compatible string */
+ { .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN },
+ { .compatible = "toshiba,tc35890", .data = (void *) TC3589X_TC35890 },
+ { .compatible = "toshiba,tc35892", .data = (void *) TC3589X_TC35892 },
+ { .compatible = "toshiba,tc35893", .data = (void *) TC3589X_TC35893 },
+ { .compatible = "toshiba,tc35894", .data = (void *) TC3589X_TC35894 },
+ { .compatible = "toshiba,tc35895", .data = (void *) TC3589X_TC35895 },
+ { .compatible = "toshiba,tc35896", .data = (void *) TC3589X_TC35896 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, tc3589x_match);
+
+static struct tc3589x_platform_data *
+tc3589x_of_probe(struct device *dev, enum tc3589x_version *version)
{
+ struct device_node *np = dev->of_node;
+ struct tc3589x_platform_data *pdata;
struct device_node *child;
+ const struct of_device_id *of_id;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ of_id = of_match_device(tc3589x_match, dev);
+ if (!of_id)
+ return ERR_PTR(-ENODEV);
+ *version = (enum tc3589x_version) of_id->data;
for_each_child_of_node(np, child) {
- if (!strcmp(child->name, "tc3589x_gpio")) {
+ if (of_device_is_compatible(child, "toshiba,tc3589x-gpio"))
pdata->block |= TC3589x_BLOCK_GPIO;
- }
- if (!strcmp(child->name, "tc3589x_keypad")) {
+ if (of_device_is_compatible(child, "toshiba,tc3589x-keypad"))
pdata->block |= TC3589x_BLOCK_KEYPAD;
- }
}
- return 0;
+ return pdata;
}
+#else
+static inline struct tc3589x_platform_data *
+tc3589x_of_probe(struct device *dev, enum tc3589x_version *version)
+{
+ dev_err(dev, "no device tree support\n");
+ return ERR_PTR(-ENODEV);
+}
+#endif
static int tc3589x_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct device_node *np = i2c->dev.of_node;
+ struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct tc3589x *tc3589x;
+ enum tc3589x_version version;
int ret;
if (!pdata) {
- if (np) {
- pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- ret = tc3589x_of_probe(np, pdata);
- if (ret)
- return ret;
- }
- else {
+ pdata = tc3589x_of_probe(&i2c->dev, &version);
+ if (IS_ERR(pdata)) {
dev_err(&i2c->dev, "No platform data or DT found\n");
- return -EINVAL;
+ return PTR_ERR(pdata);
}
+ } else {
+ /* When not probing from device tree we have this ID */
+ version = id->driver_data;
}
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
@@ -375,7 +406,7 @@ static int tc3589x_probe(struct i2c_client *i2c,
tc3589x->pdata = pdata;
tc3589x->irq_base = pdata->irq_base;
- switch (id->driver_data) {
+ switch (version) {
case TC3589X_TC35893:
case TC3589X_TC35895:
case TC3589X_TC35896:
@@ -471,9 +502,12 @@ static const struct i2c_device_id tc3589x_id[] = {
MODULE_DEVICE_TABLE(i2c, tc3589x_id);
static struct i2c_driver tc3589x_driver = {
- .driver.name = "tc3589x",
- .driver.owner = THIS_MODULE,
- .driver.pm = &tc3589x_dev_pm_ops,
+ .driver = {
+ .name = "tc3589x",
+ .owner = THIS_MODULE,
+ .pm = &tc3589x_dev_pm_ops,
+ .of_match_table = of_match_ptr(tc3589x_match),
+ },
.probe = tc3589x_probe,
.remove = tc3589x_remove,
.id_table = tc3589x_id,
diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c
deleted file mode 100644
index a5424579679c..000000000000
--- a/drivers/mfd/ti-ssp.c
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs
- *
- * Copyright (C) 2010 Texas Instruments Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/wait.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/spinlock.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/sched.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/ti_ssp.h>
-
-/* Register Offsets */
-#define REG_REV 0x00
-#define REG_IOSEL_1 0x04
-#define REG_IOSEL_2 0x08
-#define REG_PREDIV 0x0c
-#define REG_INTR_ST 0x10
-#define REG_INTR_EN 0x14
-#define REG_TEST_CTRL 0x18
-
-/* Per port registers */
-#define PORT_CFG_2 0x00
-#define PORT_ADDR 0x04
-#define PORT_DATA 0x08
-#define PORT_CFG_1 0x0c
-#define PORT_STATE 0x10
-
-#define SSP_PORT_CONFIG_MASK (SSP_EARLY_DIN | SSP_DELAY_DOUT)
-#define SSP_PORT_CLKRATE_MASK 0x0f
-
-#define SSP_SEQRAM_WR_EN BIT(4)
-#define SSP_SEQRAM_RD_EN BIT(5)
-#define SSP_START BIT(15)
-#define SSP_BUSY BIT(10)
-#define SSP_PORT_ASL BIT(7)
-#define SSP_PORT_CFO1 BIT(6)
-
-#define SSP_PORT_SEQRAM_SIZE 32
-
-static const int ssp_port_base[] = {0x040, 0x080};
-static const int ssp_port_seqram[] = {0x100, 0x180};
-
-struct ti_ssp {
- struct resource *res;
- struct device *dev;
- void __iomem *regs;
- spinlock_t lock;
- struct clk *clk;
- int irq;
- wait_queue_head_t wqh;
-
- /*
- * Some of the iosel2 register bits always read-back as 0, we need to
- * remember these values so that we don't clobber previously set
- * values.
- */
- u32 iosel2;
-};
-
-static inline struct ti_ssp *dev_to_ssp(struct device *dev)
-{
- return dev_get_drvdata(dev->parent);
-}
-
-static inline int dev_to_port(struct device *dev)
-{
- return to_platform_device(dev)->id;
-}
-
-/* Register Access Helpers, rmw() functions need to run locked */
-static inline u32 ssp_read(struct ti_ssp *ssp, int reg)
-{
- return __raw_readl(ssp->regs + reg);
-}
-
-static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val)
-{
- __raw_writel(val, ssp->regs + reg);
-}
-
-static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits)
-{
- ssp_write(ssp, reg, (ssp_read(ssp, reg) & ~mask) | bits);
-}
-
-static inline u32 ssp_port_read(struct ti_ssp *ssp, int port, int reg)
-{
- return ssp_read(ssp, ssp_port_base[port] + reg);
-}
-
-static inline void ssp_port_write(struct ti_ssp *ssp, int port, int reg,
- u32 val)
-{
- ssp_write(ssp, ssp_port_base[port] + reg, val);
-}
-
-static inline void ssp_port_rmw(struct ti_ssp *ssp, int port, int reg,
- u32 mask, u32 bits)
-{
- ssp_rmw(ssp, ssp_port_base[port] + reg, mask, bits);
-}
-
-static inline void ssp_port_clr_bits(struct ti_ssp *ssp, int port, int reg,
- u32 bits)
-{
- ssp_port_rmw(ssp, port, reg, bits, 0);
-}
-
-static inline void ssp_port_set_bits(struct ti_ssp *ssp, int port, int reg,
- u32 bits)
-{
- ssp_port_rmw(ssp, port, reg, 0, bits);
-}
-
-/* Called to setup port clock mode, caller must hold ssp->lock */
-static int __set_mode(struct ti_ssp *ssp, int port, int mode)
-{
- mode &= SSP_PORT_CONFIG_MASK;
- ssp_port_rmw(ssp, port, PORT_CFG_1, SSP_PORT_CONFIG_MASK, mode);
-
- return 0;
-}
-
-int ti_ssp_set_mode(struct device *dev, int mode)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int ret;
-
- spin_lock(&ssp->lock);
- ret = __set_mode(ssp, port, mode);
- spin_unlock(&ssp->lock);
-
- return ret;
-}
-EXPORT_SYMBOL(ti_ssp_set_mode);
-
-/* Called to setup iosel2, caller must hold ssp->lock */
-static void __set_iosel2(struct ti_ssp *ssp, u32 mask, u32 val)
-{
- ssp->iosel2 = (ssp->iosel2 & ~mask) | val;
- ssp_write(ssp, REG_IOSEL_2, ssp->iosel2);
-}
-
-/* Called to setup port iosel, caller must hold ssp->lock */
-static void __set_iosel(struct ti_ssp *ssp, int port, u32 iosel)
-{
- unsigned val, shift = port ? 16 : 0;
-
- /* IOSEL1 gets the least significant 16 bits */
- val = ssp_read(ssp, REG_IOSEL_1);
- val &= 0xffff << (port ? 0 : 16);
- val |= (iosel & 0xffff) << (port ? 16 : 0);
- ssp_write(ssp, REG_IOSEL_1, val);
-
- /* IOSEL2 gets the most significant 16 bits */
- val = (iosel >> 16) & 0x7;
- __set_iosel2(ssp, 0x7 << shift, val << shift);
-}
-
-int ti_ssp_set_iosel(struct device *dev, u32 iosel)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
-
- spin_lock(&ssp->lock);
- __set_iosel(ssp, port, iosel);
- spin_unlock(&ssp->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(ti_ssp_set_iosel);
-
-int ti_ssp_load(struct device *dev, int offs, u32* prog, int len)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int i;
-
- if (len > SSP_PORT_SEQRAM_SIZE)
- return -ENOSPC;
-
- spin_lock(&ssp->lock);
-
- /* Enable SeqRAM access */
- ssp_port_set_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN);
-
- /* Copy code */
- for (i = 0; i < len; i++) {
- __raw_writel(prog[i], ssp->regs + offs + 4*i +
- ssp_port_seqram[port]);
- }
-
- /* Disable SeqRAM access */
- ssp_port_clr_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN);
-
- spin_unlock(&ssp->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(ti_ssp_load);
-
-int ti_ssp_raw_read(struct device *dev)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int shift = port ? 27 : 11;
-
- return (ssp_read(ssp, REG_IOSEL_2) >> shift) & 0xf;
-}
-EXPORT_SYMBOL(ti_ssp_raw_read);
-
-int ti_ssp_raw_write(struct device *dev, u32 val)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev), shift;
-
- spin_lock(&ssp->lock);
-
- shift = port ? 22 : 6;
- val &= 0xf;
- __set_iosel2(ssp, 0xf << shift, val << shift);
-
- spin_unlock(&ssp->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(ti_ssp_raw_write);
-
-static inline int __xfer_done(struct ti_ssp *ssp, int port)
-{
- return !(ssp_port_read(ssp, port, PORT_CFG_1) & SSP_BUSY);
-}
-
-int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int ret;
-
- if (pc & ~(0x3f))
- return -EINVAL;
-
- /* Grab ssp->lock to serialize rmw on ssp registers */
- spin_lock(&ssp->lock);
-
- ssp_port_write(ssp, port, PORT_ADDR, input >> 16);
- ssp_port_write(ssp, port, PORT_DATA, input & 0xffff);
- ssp_port_rmw(ssp, port, PORT_CFG_1, 0x3f, pc);
-
- /* grab wait queue head lock to avoid race with the isr */
- spin_lock_irq(&ssp->wqh.lock);
-
- /* kick off sequence execution in hardware */
- ssp_port_set_bits(ssp, port, PORT_CFG_1, SSP_START);
-
- /* drop ssp lock; no register writes beyond this */
- spin_unlock(&ssp->lock);
-
- ret = wait_event_interruptible_locked_irq(ssp->wqh,
- __xfer_done(ssp, port));
- spin_unlock_irq(&ssp->wqh.lock);
-
- if (ret < 0)
- return ret;
-
- if (output) {
- *output = (ssp_port_read(ssp, port, PORT_ADDR) << 16) |
- (ssp_port_read(ssp, port, PORT_DATA) & 0xffff);
- }
-
- ret = ssp_port_read(ssp, port, PORT_STATE) & 0x3f; /* stop address */
-
- return ret;
-}
-EXPORT_SYMBOL(ti_ssp_run);
-
-static irqreturn_t ti_ssp_interrupt(int irq, void *dev_data)
-{
- struct ti_ssp *ssp = dev_data;
-
- spin_lock(&ssp->wqh.lock);
-
- ssp_write(ssp, REG_INTR_ST, 0x3);
- wake_up_locked(&ssp->wqh);
-
- spin_unlock(&ssp->wqh.lock);
-
- return IRQ_HANDLED;
-}
-
-static int ti_ssp_probe(struct platform_device *pdev)
-{
- static struct ti_ssp *ssp;
- const struct ti_ssp_data *pdata = dev_get_platdata(&pdev->dev);
- int error = 0, prediv = 0xff, id;
- unsigned long sysclk;
- struct device *dev = &pdev->dev;
- struct mfd_cell cells[2];
-
- ssp = kzalloc(sizeof(*ssp), GFP_KERNEL);
- if (!ssp) {
- dev_err(dev, "cannot allocate device info\n");
- return -ENOMEM;
- }
-
- ssp->dev = dev;
- dev_set_drvdata(dev, ssp);
-
- ssp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!ssp->res) {
- error = -ENODEV;
- dev_err(dev, "cannot determine register area\n");
- goto error_res;
- }
-
- if (!request_mem_region(ssp->res->start, resource_size(ssp->res),
- pdev->name)) {
- error = -ENOMEM;
- dev_err(dev, "cannot claim register memory\n");
- goto error_res;
- }
-
- ssp->regs = ioremap(ssp->res->start, resource_size(ssp->res));
- if (!ssp->regs) {
- error = -ENOMEM;
- dev_err(dev, "cannot map register memory\n");
- goto error_map;
- }
-
- ssp->clk = clk_get(dev, NULL);
- if (IS_ERR(ssp->clk)) {
- error = PTR_ERR(ssp->clk);
- dev_err(dev, "cannot claim device clock\n");
- goto error_clk;
- }
-
- ssp->irq = platform_get_irq(pdev, 0);
- if (ssp->irq < 0) {
- error = -ENODEV;
- dev_err(dev, "unknown irq\n");
- goto error_irq;
- }
-
- error = request_threaded_irq(ssp->irq, NULL, ti_ssp_interrupt, 0,
- dev_name(dev), ssp);
- if (error < 0) {
- dev_err(dev, "cannot acquire irq\n");
- goto error_irq;
- }
-
- spin_lock_init(&ssp->lock);
- init_waitqueue_head(&ssp->wqh);
-
- /* Power on and initialize SSP */
- error = clk_enable(ssp->clk);
- if (error) {
- dev_err(dev, "cannot enable device clock\n");
- goto error_enable;
- }
-
- /* Reset registers to a sensible known state */
- ssp_write(ssp, REG_IOSEL_1, 0);
- ssp_write(ssp, REG_IOSEL_2, 0);
- ssp_write(ssp, REG_INTR_EN, 0x3);
- ssp_write(ssp, REG_INTR_ST, 0x3);
- ssp_write(ssp, REG_TEST_CTRL, 0);
- ssp_port_write(ssp, 0, PORT_CFG_1, SSP_PORT_ASL);
- ssp_port_write(ssp, 1, PORT_CFG_1, SSP_PORT_ASL);
- ssp_port_write(ssp, 0, PORT_CFG_2, SSP_PORT_CFO1);
- ssp_port_write(ssp, 1, PORT_CFG_2, SSP_PORT_CFO1);
-
- sysclk = clk_get_rate(ssp->clk);
- if (pdata && pdata->out_clock)
- prediv = (sysclk / pdata->out_clock) - 1;
- prediv = clamp(prediv, 0, 0xff);
- ssp_rmw(ssp, REG_PREDIV, 0xff, prediv);
-
- memset(cells, 0, sizeof(cells));
- for (id = 0; id < 2; id++) {
- const struct ti_ssp_dev_data *data = &pdata->dev_data[id];
-
- cells[id].id = id;
- cells[id].name = data->dev_name;
- cells[id].platform_data = data->pdata;
- }
-
- error = mfd_add_devices(dev, 0, cells, 2, NULL, 0, NULL);
- if (error < 0) {
- dev_err(dev, "cannot add mfd cells\n");
- goto error_enable;
- }
-
- return 0;
-
-error_enable:
- free_irq(ssp->irq, ssp);
-error_irq:
- clk_put(ssp->clk);
-error_clk:
- iounmap(ssp->regs);
-error_map:
- release_mem_region(ssp->res->start, resource_size(ssp->res));
-error_res:
- kfree(ssp);
- return error;
-}
-
-static int ti_ssp_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct ti_ssp *ssp = dev_get_drvdata(dev);
-
- mfd_remove_devices(dev);
- clk_disable(ssp->clk);
- free_irq(ssp->irq, ssp);
- clk_put(ssp->clk);
- iounmap(ssp->regs);
- release_mem_region(ssp->res->start, resource_size(ssp->res));
- kfree(ssp);
- return 0;
-}
-
-static struct platform_driver ti_ssp_driver = {
- .probe = ti_ssp_probe,
- .remove = ti_ssp_remove,
- .driver = {
- .name = "ti-ssp",
- .owner = THIS_MODULE,
- }
-};
-
-module_platform_driver(ti_ssp_driver);
-
-MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver");
-MODULE_AUTHOR("Cyril Chemparathy");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ti-ssp");
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index d4e860413bb5..dd4bf5816221 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -14,7 +14,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/io.h>
@@ -184,12 +183,6 @@ static int ti_tscadc_probe(struct platform_device *pdev)
return -EINVAL;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "no memory resource defined.\n");
- return -EINVAL;
- }
-
/* Allocate memory for device */
tscadc = devm_kzalloc(&pdev->dev,
sizeof(struct ti_tscadc_dev), GFP_KERNEL);
@@ -206,19 +199,10 @@ static int ti_tscadc_probe(struct platform_device *pdev)
} else
tscadc->irq = err;
- res = devm_request_mem_region(&pdev->dev,
- res->start, resource_size(res), pdev->name);
- if (!res) {
- dev_err(&pdev->dev, "failed to reserve registers.\n");
- return -EBUSY;
- }
-
- tscadc->tscadc_base = devm_ioremap(&pdev->dev,
- res->start, resource_size(res));
- if (!tscadc->tscadc_base) {
- dev_err(&pdev->dev, "failed to map registers.\n");
- return -ENOMEM;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(tscadc->tscadc_base))
+ return PTR_ERR(tscadc->tscadc_base);
tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev,
tscadc->tscadc_base, &tscadc_regmap_config);
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index 2bc5cfb85204..6ce36d6970a4 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -715,7 +715,7 @@ static int timb_probe(struct pci_dev *dev,
for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
msix_entries[i].entry = i;
- err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS);
+ err = pci_enable_msix_exact(dev, msix_entries, TIMBERDALE_NR_IRQS);
if (err) {
dev_err(&dev->dev,
"MSI-X init failed: %d, expected entries: %d\n",
diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c
new file mode 100644
index 000000000000..a74bfb59f18f
--- /dev/null
+++ b/drivers/mfd/tps65218.c
@@ -0,0 +1,282 @@
+/*
+ * Driver for TPS65218 Integrated power management chipsets
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65218.h>
+
+#define TPS65218_PASSWORD_REGS_UNLOCK 0x7D
+
+/**
+ * tps65218_reg_read: Read a single tps65218 register.
+ *
+ * @tps: Device to read from.
+ * @reg: Register to read.
+ * @val: Contians the value
+ */
+int tps65218_reg_read(struct tps65218 *tps, unsigned int reg,
+ unsigned int *val)
+{
+ return regmap_read(tps->regmap, reg, val);
+}
+EXPORT_SYMBOL_GPL(tps65218_reg_read);
+
+/**
+ * tps65218_reg_write: Write a single tps65218 register.
+ *
+ * @tps65218: Device to write to.
+ * @reg: Register to write to.
+ * @val: Value to write.
+ * @level: Password protected level
+ */
+int tps65218_reg_write(struct tps65218 *tps, unsigned int reg,
+ unsigned int val, unsigned int level)
+{
+ int ret;
+ unsigned int xor_reg_val;
+
+ switch (level) {
+ case TPS65218_PROTECT_NONE:
+ return regmap_write(tps->regmap, reg, val);
+ case TPS65218_PROTECT_L1:
+ xor_reg_val = reg ^ TPS65218_PASSWORD_REGS_UNLOCK;
+ ret = regmap_write(tps->regmap, TPS65218_REG_PASSWORD,
+ xor_reg_val);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write(tps->regmap, reg, val);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(tps65218_reg_write);
+
+/**
+ * tps65218_update_bits: Modify bits w.r.t mask, val and level.
+ *
+ * @tps65218: Device to write to.
+ * @reg: Register to read-write to.
+ * @mask: Mask.
+ * @val: Value to write.
+ * @level: Password protected level
+ */
+static int tps65218_update_bits(struct tps65218 *tps, unsigned int reg,
+ unsigned int mask, unsigned int val, unsigned int level)
+{
+ int ret;
+ unsigned int data;
+
+ ret = tps65218_reg_read(tps, reg, &data);
+ if (ret) {
+ dev_err(tps->dev, "Read from reg 0x%x failed\n", reg);
+ return ret;
+ }
+
+ data &= ~mask;
+ data |= val & mask;
+
+ mutex_lock(&tps->tps_lock);
+ ret = tps65218_reg_write(tps, reg, data, level);
+ if (ret)
+ dev_err(tps->dev, "Write for reg 0x%x failed\n", reg);
+ mutex_unlock(&tps->tps_lock);
+
+ return ret;
+}
+
+int tps65218_set_bits(struct tps65218 *tps, unsigned int reg,
+ unsigned int mask, unsigned int val, unsigned int level)
+{
+ return tps65218_update_bits(tps, reg, mask, val, level);
+}
+EXPORT_SYMBOL_GPL(tps65218_set_bits);
+
+int tps65218_clear_bits(struct tps65218 *tps, unsigned int reg,
+ unsigned int mask, unsigned int level)
+{
+ return tps65218_update_bits(tps, reg, mask, 0, level);
+}
+EXPORT_SYMBOL_GPL(tps65218_clear_bits);
+
+static struct regmap_config tps65218_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_irq tps65218_irqs[] = {
+ /* INT1 IRQs */
+ [TPS65218_PRGC_IRQ] = {
+ .mask = TPS65218_INT1_PRGC,
+ },
+ [TPS65218_CC_AQC_IRQ] = {
+ .mask = TPS65218_INT1_CC_AQC,
+ },
+ [TPS65218_HOT_IRQ] = {
+ .mask = TPS65218_INT1_HOT,
+ },
+ [TPS65218_PB_IRQ] = {
+ .mask = TPS65218_INT1_PB,
+ },
+ [TPS65218_AC_IRQ] = {
+ .mask = TPS65218_INT1_AC,
+ },
+ [TPS65218_VPRG_IRQ] = {
+ .mask = TPS65218_INT1_VPRG,
+ },
+ [TPS65218_INVALID1_IRQ] = {
+ },
+ [TPS65218_INVALID2_IRQ] = {
+ },
+ /* INT2 IRQs*/
+ [TPS65218_LS1_I_IRQ] = {
+ .mask = TPS65218_INT2_LS1_I,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS2_I_IRQ] = {
+ .mask = TPS65218_INT2_LS2_I,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS3_I_IRQ] = {
+ .mask = TPS65218_INT2_LS3_I,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS1_F_IRQ] = {
+ .mask = TPS65218_INT2_LS1_F,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS2_F_IRQ] = {
+ .mask = TPS65218_INT2_LS2_F,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS3_F_IRQ] = {
+ .mask = TPS65218_INT2_LS3_F,
+ .reg_offset = 1,
+ },
+ [TPS65218_INVALID3_IRQ] = {
+ },
+ [TPS65218_INVALID4_IRQ] = {
+ },
+};
+
+static struct regmap_irq_chip tps65218_irq_chip = {
+ .name = "tps65218",
+ .irqs = tps65218_irqs,
+ .num_irqs = ARRAY_SIZE(tps65218_irqs),
+
+ .num_regs = 2,
+ .mask_base = TPS65218_REG_INT_MASK1,
+};
+
+static const struct of_device_id of_tps65218_match_table[] = {
+ { .compatible = "ti,tps65218", },
+};
+
+static int tps65218_probe(struct i2c_client *client,
+ const struct i2c_device_id *ids)
+{
+ struct tps65218 *tps;
+ const struct of_device_id *match;
+ int ret;
+
+ match = of_match_device(of_tps65218_match_table, &client->dev);
+ if (!match) {
+ dev_err(&client->dev,
+ "Failed to find matching dt id\n");
+ return -EINVAL;
+ }
+
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, tps);
+ tps->dev = &client->dev;
+ tps->irq = client->irq;
+ tps->regmap = devm_regmap_init_i2c(client, &tps65218_regmap_config);
+ if (IS_ERR(tps->regmap)) {
+ ret = PTR_ERR(tps->regmap);
+ dev_err(tps->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ mutex_init(&tps->tps_lock);
+
+ ret = regmap_add_irq_chip(tps->regmap, tps->irq,
+ IRQF_ONESHOT, 0, &tps65218_irq_chip,
+ &tps->irq_data);
+ if (ret < 0)
+ return ret;
+
+ ret = of_platform_populate(client->dev.of_node, NULL, NULL,
+ &client->dev);
+ if (ret < 0)
+ goto err_irq;
+
+ return 0;
+
+err_irq:
+ regmap_del_irq_chip(tps->irq, tps->irq_data);
+
+ return ret;
+}
+
+static int tps65218_remove(struct i2c_client *client)
+{
+ struct tps65218 *tps = i2c_get_clientdata(client);
+
+ regmap_del_irq_chip(tps->irq, tps->irq_data);
+
+ return 0;
+}
+
+static const struct i2c_device_id tps65218_id_table[] = {
+ { "tps65218", TPS65218 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, tps65218_id_table);
+
+static struct i2c_driver tps65218_driver = {
+ .driver = {
+ .name = "tps65218",
+ .owner = THIS_MODULE,
+ .of_match_table = of_tps65218_match_table,
+ },
+ .probe = tps65218_probe,
+ .remove = tps65218_remove,
+ .id_table = tps65218_id_table,
+};
+
+module_i2c_driver(tps65218_driver);
+
+MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
+MODULE_DESCRIPTION("TPS65218 chip family multi-function driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index 1f142d76cbbc..460a014ca629 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -255,8 +255,10 @@ static int tps65910_irq_init(struct tps65910 *tps65910, int irq,
ret = regmap_add_irq_chip(tps65910->regmap, tps65910->chip_irq,
IRQF_ONESHOT, pdata->irq_base,
tps6591x_irqs_chip, &tps65910->irq_data);
- if (ret < 0)
+ if (ret < 0) {
dev_warn(tps65910->dev, "Failed to add irq_chip %d\n", ret);
+ tps65910->chip_irq = 0;
+ }
return ret;
}
@@ -509,6 +511,7 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
regmap_irq_get_domain(tps65910->irq_data));
if (ret < 0) {
dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret);
+ tps65910_irq_exit(tps65910);
return ret;
}
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
index 27a518e0eec6..1f82d60b1d0f 100644
--- a/drivers/mfd/tps65912-core.c
+++ b/drivers/mfd/tps65912-core.c
@@ -15,7 +15,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
diff --git a/drivers/mfd/tps65912-irq.c b/drivers/mfd/tps65912-irq.c
index d360a83a2738..fbecec7f1e3d 100644
--- a/drivers/mfd/tps65912-irq.c
+++ b/drivers/mfd/tps65912-irq.c
@@ -15,7 +15,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/interrupt.h>
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index ed718328eff1..e87140bef667 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -282,11 +282,11 @@ static struct reg_default twl4030_49_defaults[] = {
static bool twl4030_49_nop_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case 0:
- case 3:
- case 40:
- case 41:
- case 42:
+ case 0x00:
+ case 0x03:
+ case 0x40:
+ case 0x41:
+ case 0x42:
return false;
default:
return true;
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index 9aa6d1efa241..596b1f657e21 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -27,7 +27,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/init.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index 18a607e2ca06..a6bb17d908b8 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -31,7 +31,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/init.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c
index 75316fb33448..6e88f25832fb 100644
--- a/drivers/mfd/twl6040.c
+++ b/drivers/mfd/twl6040.c
@@ -661,6 +661,11 @@ static int twl6040_probe(struct i2c_client *client,
init_completion(&twl6040->ready);
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
+ if (twl6040->rev < 0) {
+ dev_err(&client->dev, "Failed to read revision register: %d\n",
+ twl6040->rev);
+ goto gpio_err;
+ }
/* ERRATA: Automatic power-up is not possible in ES1.0 */
if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
@@ -703,7 +708,6 @@ static int twl6040_probe(struct i2c_client *client,
}
/* dual-access registers controlled by I2C only */
- twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL);
regmap_register_patch(twl6040->regmap, twl6040_patch,
ARRAY_SIZE(twl6040_patch));
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index 0313f839e8fa..153d595afaac 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -742,9 +742,7 @@ static int ucb1x00_resume(struct device *dev)
}
#endif
-static const struct dev_pm_ops ucb1x00_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume)
-};
+static SIMPLE_DEV_PM_OPS(ucb1x00_pm_ops, ucb1x00_suspend, ucb1x00_resume);
static struct mcp_driver ucb1x00_driver = {
.drv = {
diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c
index 84ce6b9daa3d..d0db89d13e01 100644
--- a/drivers/mfd/vexpress-config.c
+++ b/drivers/mfd/vexpress-config.c
@@ -16,7 +16,6 @@
#include <linux/bitops.h>
#include <linux/completion.h>
#include <linux/export.h>
-#include <linux/init.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -27,7 +26,7 @@
#define VEXPRESS_CONFIG_MAX_BRIDGES 2
-struct vexpress_config_bridge {
+static struct vexpress_config_bridge {
struct device_node *node;
struct vexpress_config_bridge_info *info;
struct list_head transactions;
diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c
index 981bef4b7ebc..35281e804e7e 100644
--- a/drivers/mfd/vexpress-sysreg.c
+++ b/drivers/mfd/vexpress-sysreg.c
@@ -168,7 +168,7 @@ static void *vexpress_sysreg_config_func_get(struct device *dev,
struct device_node *node)
{
struct vexpress_sysreg_config_func *config_func;
- u32 site;
+ u32 site = 0;
u32 position = 0;
u32 dcc = 0;
u32 func_device[2];
diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c
index bffc584e4a43..070f8cfbbd7a 100644
--- a/drivers/mfd/wm5102-tables.c
+++ b/drivers/mfd/wm5102-tables.c
@@ -73,6 +73,7 @@ static const struct reg_default wm5102_revb_patch[] = {
{ 0x171, 0x0000 },
{ 0x35E, 0x000C },
{ 0x2D4, 0x0000 },
+ { 0x4DC, 0x0900 },
{ 0x80, 0x0000 },
};
@@ -1839,6 +1840,23 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
case ARIZONA_DSP1_SCRATCH_0:
case ARIZONA_DSP1_SCRATCH_1:
case ARIZONA_DSP1_SCRATCH_2:
@@ -1894,9 +1912,27 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg)
case ARIZONA_AOD_IRQ1:
case ARIZONA_AOD_IRQ2:
case ARIZONA_AOD_IRQ_RAW_STATUS:
+ case ARIZONA_DSP1_CLOCKING_1:
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
case ARIZONA_DSP1_SCRATCH_0:
case ARIZONA_DSP1_SCRATCH_1:
case ARIZONA_DSP1_SCRATCH_2:
diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c
index 11632f135e8c..1942b6f231da 100644
--- a/drivers/mfd/wm5110-tables.c
+++ b/drivers/mfd/wm5110-tables.c
@@ -538,7 +538,7 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
{ 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
{ 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */
- { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */
+ { 0x0000029B, 0x0028 }, /* R667 - Headphone Detect 1 */
{ 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */
{ 0x000002A2, 0x0000 }, /* R674 - Micd clamp control */
{ 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
@@ -2461,6 +2461,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_STATUS_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_WDMA_OFFSET_1:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
+ case ARIZONA_DSP1_RDMA_OFFSET_1:
+ case ARIZONA_DSP1_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP1_SCRATCH_0:
case ARIZONA_DSP1_SCRATCH_1:
case ARIZONA_DSP1_SCRATCH_2:
@@ -2470,6 +2491,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
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:
@@ -2479,6 +2521,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
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:
@@ -2488,6 +2551,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP4_STATUS_1:
case ARIZONA_DSP4_STATUS_2:
case ARIZONA_DSP4_STATUS_3:
+ case ARIZONA_DSP4_STATUS_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_1:
+ case ARIZONA_DSP4_WDMA_BUFFER_2:
+ case ARIZONA_DSP4_WDMA_BUFFER_3:
+ case ARIZONA_DSP4_WDMA_BUFFER_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_5:
+ case ARIZONA_DSP4_WDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_BUFFER_7:
+ case ARIZONA_DSP4_WDMA_BUFFER_8:
+ case ARIZONA_DSP4_RDMA_BUFFER_1:
+ case ARIZONA_DSP4_RDMA_BUFFER_2:
+ case ARIZONA_DSP4_RDMA_BUFFER_3:
+ case ARIZONA_DSP4_RDMA_BUFFER_4:
+ case ARIZONA_DSP4_RDMA_BUFFER_5:
+ case ARIZONA_DSP4_RDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_CONFIG_1:
+ case ARIZONA_DSP4_WDMA_CONFIG_2:
+ case ARIZONA_DSP4_WDMA_OFFSET_1:
+ case ARIZONA_DSP4_RDMA_CONFIG_1:
+ case ARIZONA_DSP4_RDMA_OFFSET_1:
+ case ARIZONA_DSP4_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP4_SCRATCH_0:
case ARIZONA_DSP4_SCRATCH_1:
case ARIZONA_DSP4_SCRATCH_2:
@@ -2543,31 +2627,119 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_STATUS_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_WDMA_OFFSET_1:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
+ case ARIZONA_DSP1_RDMA_OFFSET_1:
+ case ARIZONA_DSP1_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP1_SCRATCH_0:
case ARIZONA_DSP1_SCRATCH_1:
case ARIZONA_DSP1_SCRATCH_2:
case ARIZONA_DSP1_SCRATCH_3:
+ case ARIZONA_DSP1_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_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:
case ARIZONA_DSP4_STATUS_1:
case ARIZONA_DSP4_STATUS_2:
case ARIZONA_DSP4_STATUS_3:
+ case ARIZONA_DSP4_STATUS_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_1:
+ case ARIZONA_DSP4_WDMA_BUFFER_2:
+ case ARIZONA_DSP4_WDMA_BUFFER_3:
+ case ARIZONA_DSP4_WDMA_BUFFER_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_5:
+ case ARIZONA_DSP4_WDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_BUFFER_7:
+ case ARIZONA_DSP4_WDMA_BUFFER_8:
+ case ARIZONA_DSP4_RDMA_BUFFER_1:
+ case ARIZONA_DSP4_RDMA_BUFFER_2:
+ case ARIZONA_DSP4_RDMA_BUFFER_3:
+ case ARIZONA_DSP4_RDMA_BUFFER_4:
+ case ARIZONA_DSP4_RDMA_BUFFER_5:
+ case ARIZONA_DSP4_RDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_CONFIG_1:
+ case ARIZONA_DSP4_WDMA_CONFIG_2:
+ case ARIZONA_DSP4_WDMA_OFFSET_1:
+ case ARIZONA_DSP4_RDMA_CONFIG_1:
+ case ARIZONA_DSP4_RDMA_OFFSET_1:
+ case ARIZONA_DSP4_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP4_SCRATCH_0:
case ARIZONA_DSP4_SCRATCH_1:
case ARIZONA_DSP4_SCRATCH_2:
case ARIZONA_DSP4_SCRATCH_3:
+ case ARIZONA_DSP4_CLOCKING_1:
return true;
default:
return wm5110_is_adsp_memory(dev, reg);
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index 7c1ae24605d9..4ab527f5c53b 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -14,7 +14,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/bug.h>
#include <linux/device.h>
diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c
index 624ff90501cd..cd01f7962dfd 100644
--- a/drivers/mfd/wm8350-irq.c
+++ b/drivers/mfd/wm8350-irq.c
@@ -14,7 +14,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/interrupt.h>
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
index d66d256551fb..e5eae751aa1b 100644
--- a/drivers/mfd/wm8400-core.c
+++ b/drivers/mfd/wm8400-core.c
@@ -161,31 +161,19 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8400 *wm8400;
- int ret;
wm8400 = devm_kzalloc(&i2c->dev, sizeof(struct wm8400), GFP_KERNEL);
- if (wm8400 == NULL) {
- ret = -ENOMEM;
- goto err;
- }
+ if (!wm8400)
+ return -ENOMEM;
wm8400->regmap = devm_regmap_init_i2c(i2c, &wm8400_regmap_config);
- if (IS_ERR(wm8400->regmap)) {
- ret = PTR_ERR(wm8400->regmap);
- goto err;
- }
+ if (IS_ERR(wm8400->regmap))
+ return PTR_ERR(wm8400->regmap);
wm8400->dev = &i2c->dev;
i2c_set_clientdata(i2c, wm8400);
- ret = wm8400_init(wm8400, dev_get_platdata(&i2c->dev));
- if (ret != 0)
- goto err;
-
- return 0;
-
-err:
- return ret;
+ return wm8400_init(wm8400, dev_get_platdata(&i2c->dev));
}
static int wm8400_i2c_remove(struct i2c_client *i2c)
diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig
index e42b331edbc6..462a5b1d8651 100644
--- a/drivers/misc/mic/Kconfig
+++ b/drivers/misc/mic/Kconfig
@@ -4,7 +4,6 @@ config INTEL_MIC_HOST
tristate "Intel MIC Host Driver"
depends on 64BIT && PCI && X86
select VHOST_RING
- default N
help
This enables Host Driver support for the Intel Many Integrated
Core (MIC) family of PCIe form factor coprocessor devices that
@@ -25,7 +24,6 @@ config INTEL_MIC_CARD
tristate "Intel MIC Card Driver"
depends on 64BIT && X86
select VIRTIO
- default N
help
This enables card driver support for the Intel Many Integrated
Core (MIC) device family. The card driver communicates shutdown/
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 49bc403e31f0..114f6bdfbef3 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -337,7 +337,7 @@ int mmc_of_parse(struct mmc_host *host)
break;
default:
dev_err(host->parent,
- "Invalid \"bus-width\" value %ud!\n", bus_width);
+ "Invalid \"bus-width\" value %u!\n", bus_width);
return -EINVAL;
}
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 168bc72f7a94..84c0e59b792a 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -38,10 +38,11 @@
#define MCI_CPSM_INTERRUPT (1 << 8)
#define MCI_CPSM_PENDING (1 << 9)
#define MCI_CPSM_ENABLE (1 << 10)
-#define MCI_SDIO_SUSP (1 << 11)
-#define MCI_ENCMD_COMPL (1 << 12)
-#define MCI_NIEN (1 << 13)
-#define MCI_CE_ATACMD (1 << 14)
+/* Argument flag extenstions in the ST Micro versions */
+#define MCI_ST_SDIO_SUSP (1 << 11)
+#define MCI_ST_ENCMD_COMPL (1 << 12)
+#define MCI_ST_NIEN (1 << 13)
+#define MCI_ST_CE_ATACMD (1 << 14)
#define MMCIRESPCMD 0x010
#define MMCIRESPONSE0 0x014
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 5ebcda39f554..5d49a2129618 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -150,7 +150,7 @@ config MTD_BCM63XX_PARTS
config MTD_BCM47XX_PARTS
tristate "BCM47XX partitioning support"
- depends on BCM47XX
+ depends on BCM47XX || ARCH_BCM_5301X
help
This provides partitions parser for devices based on BCM47xx
boards.
diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c
index de1eb92e42f5..adfa74c1bc45 100644
--- a/drivers/mtd/bcm47xxpart.c
+++ b/drivers/mtd/bcm47xxpart.c
@@ -14,7 +14,6 @@
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
-#include <bcm47xx_nvram.h>
/* 10 parts were found on sflash on Netgear WNDR4500 */
#define BCM47XXPART_MAX_PARTS 12
@@ -30,6 +29,7 @@
#define BOARD_DATA_MAGIC2 0xBD0D0BBD
#define CFE_MAGIC 0x43464531 /* 1EFC */
#define FACTORY_MAGIC 0x59544346 /* FCTY */
+#define NVRAM_HEADER 0x48534C46 /* FLSH */
#define POT_MAGIC1 0x54544f50 /* POTT */
#define POT_MAGIC2 0x504f /* OP */
#define ML_MAGIC1 0x39685a42
@@ -91,7 +91,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
if (offset >= 0x2000000)
break;
- if (curr_part > BCM47XXPART_MAX_PARTS) {
+ if (curr_part >= BCM47XXPART_MAX_PARTS) {
pr_warn("Reached maximum number of partitions, scanning stopped!\n");
break;
}
@@ -147,6 +147,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
/* TRX */
if (buf[0x000 / 4] == TRX_MAGIC) {
+ if (BCM47XXPART_MAX_PARTS - curr_part < 4) {
+ pr_warn("Not enough partitions left to register trx, scanning stopped!\n");
+ break;
+ }
+
trx = (struct trx_header *)buf;
trx_part = curr_part;
@@ -212,7 +217,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
/* Look for NVRAM at the end of the last block. */
for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) {
- if (curr_part > BCM47XXPART_MAX_PARTS) {
+ if (curr_part >= BCM47XXPART_MAX_PARTS) {
pr_warn("Reached maximum number of partitions, scanning stopped!\n");
break;
}
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 77514430f1fe..e4ec355704a6 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -21,7 +21,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
-#include <linux/init.h>
#include <asm/io.h>
#include <asm/byteorder.h>
@@ -69,10 +68,10 @@ static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, s
static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t);
-static int cfi_intelext_get_fact_prot_info (struct mtd_info *,
- struct otp_info *, size_t);
-static int cfi_intelext_get_user_prot_info (struct mtd_info *,
- struct otp_info *, size_t);
+static int cfi_intelext_get_fact_prot_info(struct mtd_info *, size_t,
+ size_t *, struct otp_info *);
+static int cfi_intelext_get_user_prot_info(struct mtd_info *, size_t,
+ size_t *, struct otp_info *);
#endif
static int cfi_intelext_suspend (struct mtd_info *);
static void cfi_intelext_resume (struct mtd_info *);
@@ -435,10 +434,8 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
int i;
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
- if (!mtd) {
- printk(KERN_ERR "Failed to allocate memory for MTD device\n");
+ if (!mtd)
return NULL;
- }
mtd->priv = map;
mtd->type = MTD_NORFLASH;
@@ -564,10 +561,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
* mtd->numeraseregions, GFP_KERNEL);
- if (!mtd->eraseregions) {
- printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n");
+ if (!mtd->eraseregions)
goto setup_err;
- }
for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
unsigned long ernum, ersize;
@@ -2399,24 +2394,19 @@ static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd,
NULL, do_otp_lock, 1);
}
-static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd,
- struct otp_info *buf, size_t len)
-{
- size_t retlen;
- int ret;
+static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
- ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0);
- return ret ? : retlen;
+{
+ return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
+ NULL, 0);
}
-static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd,
- struct otp_info *buf, size_t len)
+static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
{
- size_t retlen;
- int ret;
-
- ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1);
- return ret ? : retlen;
+ return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
+ NULL, 1);
}
#endif
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 89b9d6891532..e21fde9d4d7e 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -24,7 +24,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
-#include <linux/init.h>
#include <asm/io.h>
#include <asm/byteorder.h>
@@ -507,10 +506,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
int i;
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
- if (!mtd) {
- printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
+ if (!mtd)
return NULL;
- }
mtd->priv = map;
mtd->type = MTD_NORFLASH;
@@ -661,10 +658,8 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
* mtd->numeraseregions, GFP_KERNEL);
- if (!mtd->eraseregions) {
- printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n");
+ if (!mtd->eraseregions)
goto setup_err;
- }
for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
unsigned long ernum, ersize;
diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c
index 096993f9711e..6293855fb5ee 100644
--- a/drivers/mtd/chips/cfi_cmdset_0020.c
+++ b/drivers/mtd/chips/cfi_cmdset_0020.c
@@ -22,7 +22,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
-#include <linux/init.h>
#include <asm/io.h>
#include <asm/byteorder.h>
@@ -176,7 +175,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
//printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
if (!mtd) {
- printk(KERN_ERR "Failed to allocate memory for MTD device\n");
kfree(cfi->cmdset_priv);
return NULL;
}
@@ -189,7 +187,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
* mtd->numeraseregions, GFP_KERNEL);
if (!mtd->eraseregions) {
- printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n");
kfree(cfi->cmdset_priv);
kfree(mtd);
return NULL;
diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c
index d25535279404..e8d0164498b0 100644
--- a/drivers/mtd/chips/cfi_probe.c
+++ b/drivers/mtd/chips/cfi_probe.c
@@ -168,10 +168,8 @@ static int __xipram cfi_chip_setup(struct map_info *map,
return 0;
cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL);
- if (!cfi->cfiq) {
- printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure\n", map->name);
+ if (!cfi->cfiq)
return 0;
- }
memset(cfi->cfiq,0,sizeof(struct cfi_ident));
diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c
index f992418f40a8..08049f6eea60 100644
--- a/drivers/mtd/chips/cfi_util.c
+++ b/drivers/mtd/chips/cfi_util.c
@@ -116,10 +116,8 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n
printk(KERN_INFO "%s Extended Query Table at 0x%4.4X\n", name, adr);
extp = kmalloc(size, GFP_KERNEL);
- if (!extp) {
- printk(KERN_ERR "Failed to allocate memory\n");
+ if (!extp)
goto out;
- }
#ifdef CONFIG_MTD_XIP
local_irq_disable();
diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c
index ffb36ba8a6e0..b57ceea21513 100644
--- a/drivers/mtd/chips/gen_probe.c
+++ b/drivers/mtd/chips/gen_probe.c
@@ -114,7 +114,6 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
mapsize = sizeof(long) * DIV_ROUND_UP(max_chips, BITS_PER_LONG);
chip_map = kzalloc(mapsize, GFP_KERNEL);
if (!chip_map) {
- printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
kfree(cfi.cfiq);
return NULL;
}
@@ -139,7 +138,6 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL);
if (!retcfi) {
- printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name);
kfree(cfi.cfiq);
kfree(chip_map);
return NULL;
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 01281382180b..1210bc2923b7 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -210,6 +210,14 @@ config MTD_DOCG3
M-Systems and now Sandisk. The support is very experimental,
and doesn't give access to any write operations.
+config MTD_ST_SPI_FSM
+ tristate "ST Microelectronics SPI FSM Serial Flash Controller"
+ depends on ARM || SH
+ help
+ This provides an MTD device driver for the ST Microelectronics
+ SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
+ for a subset of connected Serial Flash devices.
+
if MTD_DOCG3
config BCH_CONST_M
default 14
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index d83bd73096f6..c68868f60588 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_NAND_OMAP_BCH) += elm.o
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
+obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o
CFLAGS_docg3.o += -I$(src)
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index d9fd87a4c8dc..66f0405f7e53 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -209,7 +209,6 @@ static void block2mtd_free_device(struct block2mtd_dev *dev)
}
-/* FIXME: ensure that mtd->size % erase_size == 0 */
static struct block2mtd_dev *add_device(char *devname, int erase_size)
{
const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
@@ -240,13 +239,18 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
if (IS_ERR(bdev)) {
pr_err("error: cannot open device %s\n", devname);
- goto devinit_err;
+ goto err_free_block2mtd;
}
dev->blkdev = bdev;
if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
pr_err("attempting to use an MTD device as a block device\n");
- goto devinit_err;
+ goto err_free_block2mtd;
+ }
+
+ if ((long)dev->blkdev->bd_inode->i_size % erase_size) {
+ pr_err("erasesize must be a divisor of device size\n");
+ goto err_free_block2mtd;
}
mutex_init(&dev->write_mutex);
@@ -255,7 +259,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
/* make the name contain the block device in */
name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname);
if (!name)
- goto devinit_err;
+ goto err_destroy_mutex;
dev->mtd.name = name;
@@ -274,7 +278,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
if (mtd_device_register(&dev->mtd, NULL, 0)) {
/* Device didn't get added, so free the entry */
- goto devinit_err;
+ goto err_destroy_mutex;
}
list_add(&dev->list, &blkmtd_device_list);
pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n",
@@ -283,7 +287,9 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
dev->mtd.erasesize >> 10, dev->mtd.erasesize);
return dev;
-devinit_err:
+err_destroy_mutex:
+ mutex_destroy(&dev->write_mutex);
+err_free_block2mtd:
block2mtd_free_device(dev);
return NULL;
}
@@ -448,6 +454,7 @@ static void block2mtd_exit(void)
struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
block2mtd_sync(&dev->mtd);
mtd_device_unregister(&dev->mtd);
+ mutex_destroy(&dev->write_mutex);
pr_info("mtd%d: [%s] removed\n",
dev->mtd.index,
dev->mtd.name + strlen("block2mtd: "));
diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c
index d1dd6a33a050..1fd4a0f77967 100644
--- a/drivers/mtd/devices/elm.c
+++ b/drivers/mtd/devices/elm.c
@@ -15,6 +15,8 @@
*
*/
+#define DRIVER_NAME "omap-elm"
+
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/interrupt.h>
@@ -84,6 +86,8 @@ struct elm_info {
struct list_head list;
enum bch_ecc bch_type;
struct elm_registers elm_regs;
+ int ecc_steps;
+ int ecc_syndrome_size;
};
static LIST_HEAD(elm_devices);
@@ -103,7 +107,8 @@ static u32 elm_read_reg(struct elm_info *info, int offset)
* @dev: ELM device
* @bch_type: Type of BCH ecc
*/
-int elm_config(struct device *dev, enum bch_ecc bch_type)
+int elm_config(struct device *dev, enum bch_ecc bch_type,
+ int ecc_steps, int ecc_step_size, int ecc_syndrome_size)
{
u32 reg_val;
struct elm_info *info = dev_get_drvdata(dev);
@@ -112,10 +117,22 @@ int elm_config(struct device *dev, enum bch_ecc bch_type)
dev_err(dev, "Unable to configure elm - device not probed?\n");
return -ENODEV;
}
+ /* ELM cannot detect ECC errors for chunks > 1KB */
+ if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) {
+ dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size);
+ return -EINVAL;
+ }
+ /* ELM support 8 error syndrome process */
+ if (ecc_steps > ERROR_VECTOR_MAX) {
+ dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps);
+ return -EINVAL;
+ }
reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
- info->bch_type = bch_type;
+ info->bch_type = bch_type;
+ info->ecc_steps = ecc_steps;
+ info->ecc_syndrome_size = ecc_syndrome_size;
return 0;
}
@@ -157,17 +174,15 @@ static void elm_load_syndrome(struct elm_info *info,
int i, offset;
u32 val;
- for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+ for (i = 0; i < info->ecc_steps; i++) {
/* Check error reported */
if (err_vec[i].error_reported) {
elm_configure_page_mode(info, i, true);
offset = ELM_SYNDROME_FRAGMENT_0 +
SYNDROME_FRAGMENT_REG_SIZE * i;
-
- /* BCH8 */
- if (info->bch_type) {
-
+ switch (info->bch_type) {
+ case BCH8_ECC:
/* syndrome fragment 0 = ecc[9-12B] */
val = cpu_to_be32(*(u32 *) &ecc[9]);
elm_write_reg(info, offset, val);
@@ -186,7 +201,8 @@ static void elm_load_syndrome(struct elm_info *info,
offset += 4;
val = ecc[0];
elm_write_reg(info, offset, val);
- } else {
+ break;
+ case BCH4_ECC:
/* syndrome fragment 0 = ecc[20-52b] bits */
val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
((ecc[2] & 0xf) << 28);
@@ -196,11 +212,14 @@ static void elm_load_syndrome(struct elm_info *info,
offset += 4;
val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
elm_write_reg(info, offset, val);
+ break;
+ default:
+ pr_err("invalid config bch_type\n");
}
}
/* Update ecc pointer with ecc byte size */
- ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE;
+ ecc += info->ecc_syndrome_size;
}
}
@@ -223,7 +242,7 @@ static void elm_start_processing(struct elm_info *info,
* Set syndrome vector valid, so that ELM module
* will process it for vectors error is reported
*/
- for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+ for (i = 0; i < info->ecc_steps; i++) {
if (err_vec[i].error_reported) {
offset = ELM_SYNDROME_FRAGMENT_6 +
SYNDROME_FRAGMENT_REG_SIZE * i;
@@ -252,7 +271,7 @@ static void elm_error_correction(struct elm_info *info,
int offset;
u32 reg_val;
- for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+ for (i = 0; i < info->ecc_steps; i++) {
/* Check error reported */
if (err_vec[i].error_reported) {
@@ -354,10 +373,8 @@ static int elm_probe(struct platform_device *pdev)
struct elm_info *info;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (!info) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
+ if (!info)
return -ENOMEM;
- }
info->dev = &pdev->dev;
@@ -380,7 +397,7 @@ static int elm_probe(struct platform_device *pdev)
}
pm_runtime_enable(&pdev->dev);
- if (pm_runtime_get_sync(&pdev->dev)) {
+ if (pm_runtime_get_sync(&pdev->dev) < 0) {
ret = -EINVAL;
pm_runtime_disable(&pdev->dev);
dev_err(&pdev->dev, "can't enable clock\n");
@@ -505,7 +522,7 @@ MODULE_DEVICE_TABLE(of, elm_of_match);
static struct platform_driver elm_driver = {
.driver = {
- .name = "elm",
+ .name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(elm_of_match),
.pm = &elm_pm_ops,
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index ad1913909702..524dab3ac938 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -15,7 +15,6 @@
*
*/
-#include <linux/init.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
@@ -41,7 +40,8 @@
#define OPCODE_WRSR 0x01 /* Write status register 1 byte */
#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */
#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
-#define OPCODE_QUAD_READ 0x6b /* Read data bytes */
+#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */
+#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */
#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
@@ -54,7 +54,8 @@
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */
#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */
-#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes */
+#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */
+#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */
#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */
#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */
@@ -95,6 +96,7 @@
enum read_type {
M25P80_NORMAL = 0,
M25P80_FAST,
+ M25P80_DUAL,
M25P80_QUAD,
};
@@ -479,6 +481,7 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash)
{
switch (flash->flash_read) {
case M25P80_FAST:
+ case M25P80_DUAL:
case M25P80_QUAD:
return 1;
case M25P80_NORMAL:
@@ -492,6 +495,8 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash)
static inline unsigned int m25p80_rx_nbits(const struct m25p *flash)
{
switch (flash->flash_read) {
+ case M25P80_DUAL:
+ return 2;
case M25P80_QUAD:
return 4;
default:
@@ -855,7 +860,8 @@ struct flash_info {
#define SST_WRITE 0x04 /* use SST byte programming */
#define M25P_NO_FR 0x08 /* Can't do fastread */
#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */
-#define M25P80_QUAD_READ 0x20 /* Flash supports Quad Read */
+#define M25P80_DUAL_READ 0x20 /* Flash supports Dual Read */
+#define M25P80_QUAD_READ 0x40 /* Flash supports Quad Read */
};
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
@@ -934,6 +940,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) },
+ { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, M25P80_QUAD_READ) },
/* Micron */
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
@@ -953,8 +960,8 @@ static const struct spi_device_id m25p_ids[] = {
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) },
{ "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) },
{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
- { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_QUAD_READ) },
- { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_QUAD_READ) },
+ { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_DUAL_READ | M25P80_QUAD_READ) },
+ { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_DUAL_READ | M25P80_QUAD_READ) },
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
@@ -965,6 +972,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
+ { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
@@ -1072,9 +1080,8 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi)
for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) {
info = (void *)m25p_ids[tmp].driver_data;
if (info->jedec_id == jedec) {
- if (info->ext_id != 0 && info->ext_id != ext_jedec)
- continue;
- return &m25p_ids[tmp];
+ if (info->ext_id == 0 || info->ext_id == ext_jedec)
+ return &m25p_ids[tmp];
}
}
dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
@@ -1226,7 +1233,7 @@ static int m25p_probe(struct spi_device *spi)
if (info->flags & M25P_NO_FR)
flash->flash_read = M25P80_NORMAL;
- /* Quad-read mode takes precedence over fast/normal */
+ /* Quad/Dual-read mode takes precedence over fast/normal */
if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) {
ret = set_quad_mode(flash, info->jedec_id);
if (ret) {
@@ -1234,6 +1241,8 @@ static int m25p_probe(struct spi_device *spi)
return ret;
}
flash->flash_read = M25P80_QUAD;
+ } else if (spi->mode & SPI_RX_DUAL && info->flags & M25P80_DUAL_READ) {
+ flash->flash_read = M25P80_DUAL;
}
/* Default commands */
@@ -1241,6 +1250,9 @@ static int m25p_probe(struct spi_device *spi)
case M25P80_QUAD:
flash->read_opcode = OPCODE_QUAD_READ;
break;
+ case M25P80_DUAL:
+ flash->read_opcode = OPCODE_DUAL_READ;
+ break;
case M25P80_FAST:
flash->read_opcode = OPCODE_FAST_READ;
break;
@@ -1265,6 +1277,9 @@ static int m25p_probe(struct spi_device *spi)
case M25P80_QUAD:
flash->read_opcode = OPCODE_QUAD_READ_4B;
break;
+ case M25P80_DUAL:
+ flash->read_opcode = OPCODE_DUAL_READ_4B;
+ break;
case M25P80_FAST:
flash->read_opcode = OPCODE_FAST_READ_4B;
break;
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 624069de4f28..dd22ce2cc9ad 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -10,7 +10,6 @@
* 2 of the License, or (at your option) any later version.
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -440,8 +439,8 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
#ifdef CONFIG_MTD_DATAFLASH_OTP
-static int dataflash_get_otp_info(struct mtd_info *mtd,
- struct otp_info *info, size_t len)
+static int dataflash_get_otp_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *info)
{
/* Report both blocks as identical: bytes 0..64, locked.
* Unless the user block changed from all-ones, we can't
@@ -450,7 +449,8 @@ static int dataflash_get_otp_info(struct mtd_info *mtd,
info->start = 0;
info->length = 64;
info->locked = 1;
- return sizeof(*info);
+ *retlen = sizeof(*info);
+ return 0;
}
static ssize_t otp_read(struct spi_device *spi, unsigned base,
@@ -542,14 +542,18 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,
struct dataflash *priv = mtd->priv;
int status;
- if (len > 64)
- return -EINVAL;
+ if (from >= 64) {
+ /*
+ * Attempting to write beyond the end of OTP memory,
+ * no data can be written.
+ */
+ *retlen = 0;
+ return 0;
+ }
- /* Strictly speaking, we *could* truncate the write ... but
- * let's not do that for the only write that's ever possible.
- */
+ /* Truncate the write to fit into OTP memory. */
if ((from + len) > 64)
- return -EINVAL;
+ len = 64 - from;
/* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes
* IN: ignore all
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
index e1f2aebaa489..2cceebfb251e 100644
--- a/drivers/mtd/devices/phram.c
+++ b/drivers/mtd/devices/phram.c
@@ -205,6 +205,8 @@ static inline void kill_final_newline(char *str)
return 1; \
} while (0)
+#ifndef MODULE
+static int phram_init_called;
/*
* This shall contain the module parameter if any. It is of the form:
* - phram=<device>,<address>,<size> for module case
@@ -213,9 +215,10 @@ static inline void kill_final_newline(char *str)
* size.
* Example: phram.phram=rootfs,0xa0000000,512Mi
*/
-static __initdata char phram_paramline[64 + 20 + 20];
+static char phram_paramline[64 + 20 + 20];
+#endif
-static int __init phram_setup(const char *val)
+static int phram_setup(const char *val)
{
char buf[64 + 20 + 20], *str = buf;
char *token[3];
@@ -264,17 +267,36 @@ static int __init phram_setup(const char *val)
return ret;
}
-static int __init phram_param_call(const char *val, struct kernel_param *kp)
+static int phram_param_call(const char *val, struct kernel_param *kp)
{
+#ifdef MODULE
+ return phram_setup(val);
+#else
/*
- * This function is always called before 'init_phram()', whether
- * built-in or module.
+ * If more parameters are later passed in via
+ * /sys/module/phram/parameters/phram
+ * and init_phram() has already been called,
+ * we can parse the argument now.
*/
+
+ if (phram_init_called)
+ return phram_setup(val);
+
+ /*
+ * During early boot stage, we only save the parameters
+ * here. We must parse them later: if the param passed
+ * from kernel boot command line, phram_param_call() is
+ * called so early that it is not possible to resolve
+ * the device (even kmalloc() fails). Defer that work to
+ * phram_setup().
+ */
+
if (strlen(val) >= sizeof(phram_paramline))
return -ENOSPC;
strcpy(phram_paramline, val);
return 0;
+#endif
}
module_param_call(phram, phram_param_call, NULL, NULL, 000);
@@ -283,10 +305,15 @@ MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"
static int __init init_phram(void)
{
+ int ret = 0;
+
+#ifndef MODULE
if (phram_paramline[0])
- return phram_setup(phram_paramline);
+ ret = phram_setup(phram_paramline);
+ phram_init_called = 1;
+#endif
- return 0;
+ return ret;
}
static void __exit cleanup_phram(void)
diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c
index 0c51b988e1f8..f02603e1bfeb 100644
--- a/drivers/mtd/devices/pmc551.c
+++ b/drivers/mtd/devices/pmc551.c
@@ -725,16 +725,11 @@ static int __init init_pmc551(void)
}
mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
- if (!mtd) {
- printk(KERN_NOTICE "pmc551: Cannot allocate new MTD "
- "device.\n");
+ if (!mtd)
break;
- }
priv = kzalloc(sizeof(struct mypriv), GFP_KERNEL);
if (!priv) {
- printk(KERN_NOTICE "pmc551: Cannot allocate new MTD "
- "device.\n");
kfree(mtd);
break;
}
diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h
new file mode 100644
index 000000000000..4f0c2c7c898e
--- /dev/null
+++ b/drivers/mtd/devices/serial_flash_cmds.h
@@ -0,0 +1,81 @@
+/*
+ * Generic/SFDP Flash Commands and Device Capabilities
+ *
+ * Copyright (C) 2013 Lee Jones <lee.jones@lianro.org>
+ *
+ * This code 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 _MTD_SERIAL_FLASH_CMDS_H
+#define _MTD_SERIAL_FLASH_CMDS_H
+
+/* Generic Flash Commands/OPCODEs */
+#define FLASH_CMD_WREN 0x06
+#define FLASH_CMD_WRDI 0x04
+#define FLASH_CMD_RDID 0x9f
+#define FLASH_CMD_RDSR 0x05
+#define FLASH_CMD_RDSR2 0x35
+#define FLASH_CMD_WRSR 0x01
+#define FLASH_CMD_SE_4K 0x20
+#define FLASH_CMD_SE_32K 0x52
+#define FLASH_CMD_SE 0xd8
+#define FLASH_CMD_CHIPERASE 0xc7
+#define FLASH_CMD_WRVCR 0x81
+#define FLASH_CMD_RDVCR 0x85
+
+/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
+#define FLASH_CMD_READ 0x03 /* READ */
+#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */
+#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */
+#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */
+#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */
+#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */
+
+#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */
+#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
+#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
+
+#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */
+#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */
+
+/* READ commands with 32-bit addressing */
+#define FLASH_CMD_READ4 0x13
+#define FLASH_CMD_READ4_FAST 0x0c
+#define FLASH_CMD_READ4_1_1_2 0x3c
+#define FLASH_CMD_READ4_1_2_2 0xbc
+#define FLASH_CMD_READ4_1_1_4 0x6c
+#define FLASH_CMD_READ4_1_4_4 0xec
+
+/* Configuration flags */
+#define FLASH_FLAG_SINGLE 0x000000ff
+#define FLASH_FLAG_READ_WRITE 0x00000001
+#define FLASH_FLAG_READ_FAST 0x00000002
+#define FLASH_FLAG_SE_4K 0x00000004
+#define FLASH_FLAG_SE_32K 0x00000008
+#define FLASH_FLAG_CE 0x00000010
+#define FLASH_FLAG_32BIT_ADDR 0x00000020
+#define FLASH_FLAG_RESET 0x00000040
+#define FLASH_FLAG_DYB_LOCKING 0x00000080
+
+#define FLASH_FLAG_DUAL 0x0000ff00
+#define FLASH_FLAG_READ_1_1_2 0x00000100
+#define FLASH_FLAG_READ_1_2_2 0x00000200
+#define FLASH_FLAG_READ_2_2_2 0x00000400
+#define FLASH_FLAG_WRITE_1_1_2 0x00001000
+#define FLASH_FLAG_WRITE_1_2_2 0x00002000
+#define FLASH_FLAG_WRITE_2_2_2 0x00004000
+
+#define FLASH_FLAG_QUAD 0x00ff0000
+#define FLASH_FLAG_READ_1_1_4 0x00010000
+#define FLASH_FLAG_READ_1_4_4 0x00020000
+#define FLASH_FLAG_READ_4_4_4 0x00040000
+#define FLASH_FLAG_WRITE_1_1_4 0x00100000
+#define FLASH_FLAG_WRITE_1_4_4 0x00200000
+#define FLASH_FLAG_WRITE_4_4_4 0x00400000
+
+#endif /* _MTD_SERIAL_FLASH_CMDS_H */
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c
index 423821412062..363da96e6891 100644
--- a/drivers/mtd/devices/spear_smi.c
+++ b/drivers/mtd/devices/spear_smi.c
@@ -913,7 +913,6 @@ static int spear_smi_probe(struct platform_device *pdev)
if (np) {
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
- pr_err("%s: ERROR: no memory", __func__);
ret = -ENOMEM;
goto err;
}
@@ -943,7 +942,6 @@ static int spear_smi_probe(struct platform_device *pdev)
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
if (!dev) {
ret = -ENOMEM;
- dev_err(&pdev->dev, "mem alloc fail\n");
goto err;
}
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c
index 687bf27ec850..c63ecbcad0b7 100644
--- a/drivers/mtd/devices/sst25l.c
+++ b/drivers/mtd/devices/sst25l.c
@@ -15,7 +15,6 @@
*
*/
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/mutex.h>
diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c
new file mode 100644
index 000000000000..1957d7c8e185
--- /dev/null
+++ b/drivers/mtd/devices/st_spi_fsm.c
@@ -0,0 +1,2108 @@
+/*
+ * st_spi_fsm.c - ST Fast Sequence Mode (FSM) Serial Flash Controller
+ *
+ * Author: Angus Clark <angus.clark@st.com>
+ *
+ * Copyright (C) 2010-2014 STMicroelectronics Limited
+ *
+ * JEDEC probe based on drivers/mtd/devices/m25p80.c
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include "serial_flash_cmds.h"
+
+/*
+ * FSM SPI Controller Registers
+ */
+#define SPI_CLOCKDIV 0x0010
+#define SPI_MODESELECT 0x0018
+#define SPI_CONFIGDATA 0x0020
+#define SPI_STA_MODE_CHANGE 0x0028
+#define SPI_FAST_SEQ_TRANSFER_SIZE 0x0100
+#define SPI_FAST_SEQ_ADD1 0x0104
+#define SPI_FAST_SEQ_ADD2 0x0108
+#define SPI_FAST_SEQ_ADD_CFG 0x010c
+#define SPI_FAST_SEQ_OPC1 0x0110
+#define SPI_FAST_SEQ_OPC2 0x0114
+#define SPI_FAST_SEQ_OPC3 0x0118
+#define SPI_FAST_SEQ_OPC4 0x011c
+#define SPI_FAST_SEQ_OPC5 0x0120
+#define SPI_MODE_BITS 0x0124
+#define SPI_DUMMY_BITS 0x0128
+#define SPI_FAST_SEQ_FLASH_STA_DATA 0x012c
+#define SPI_FAST_SEQ_1 0x0130
+#define SPI_FAST_SEQ_2 0x0134
+#define SPI_FAST_SEQ_3 0x0138
+#define SPI_FAST_SEQ_4 0x013c
+#define SPI_FAST_SEQ_CFG 0x0140
+#define SPI_FAST_SEQ_STA 0x0144
+#define SPI_QUAD_BOOT_SEQ_INIT_1 0x0148
+#define SPI_QUAD_BOOT_SEQ_INIT_2 0x014c
+#define SPI_QUAD_BOOT_READ_SEQ_1 0x0150
+#define SPI_QUAD_BOOT_READ_SEQ_2 0x0154
+#define SPI_PROGRAM_ERASE_TIME 0x0158
+#define SPI_MULT_PAGE_REPEAT_SEQ_1 0x015c
+#define SPI_MULT_PAGE_REPEAT_SEQ_2 0x0160
+#define SPI_STATUS_WR_TIME_REG 0x0164
+#define SPI_FAST_SEQ_DATA_REG 0x0300
+
+/*
+ * Register: SPI_MODESELECT
+ */
+#define SPI_MODESELECT_CONTIG 0x01
+#define SPI_MODESELECT_FASTREAD 0x02
+#define SPI_MODESELECT_DUALIO 0x04
+#define SPI_MODESELECT_FSM 0x08
+#define SPI_MODESELECT_QUADBOOT 0x10
+
+/*
+ * Register: SPI_CONFIGDATA
+ */
+#define SPI_CFG_DEVICE_ST 0x1
+#define SPI_CFG_DEVICE_ATMEL 0x4
+#define SPI_CFG_MIN_CS_HIGH(x) (((x) & 0xfff) << 4)
+#define SPI_CFG_CS_SETUPHOLD(x) (((x) & 0xff) << 16)
+#define SPI_CFG_DATA_HOLD(x) (((x) & 0xff) << 24)
+
+#define SPI_CFG_DEFAULT_MIN_CS_HIGH SPI_CFG_MIN_CS_HIGH(0x0AA)
+#define SPI_CFG_DEFAULT_CS_SETUPHOLD SPI_CFG_CS_SETUPHOLD(0xA0)
+#define SPI_CFG_DEFAULT_DATA_HOLD SPI_CFG_DATA_HOLD(0x00)
+
+/*
+ * Register: SPI_FAST_SEQ_TRANSFER_SIZE
+ */
+#define TRANSFER_SIZE(x) ((x) * 8)
+
+/*
+ * Register: SPI_FAST_SEQ_ADD_CFG
+ */
+#define ADR_CFG_CYCLES_ADD1(x) ((x) << 0)
+#define ADR_CFG_PADS_1_ADD1 (0x0 << 6)
+#define ADR_CFG_PADS_2_ADD1 (0x1 << 6)
+#define ADR_CFG_PADS_4_ADD1 (0x3 << 6)
+#define ADR_CFG_CSDEASSERT_ADD1 (1 << 8)
+#define ADR_CFG_CYCLES_ADD2(x) ((x) << (0+16))
+#define ADR_CFG_PADS_1_ADD2 (0x0 << (6+16))
+#define ADR_CFG_PADS_2_ADD2 (0x1 << (6+16))
+#define ADR_CFG_PADS_4_ADD2 (0x3 << (6+16))
+#define ADR_CFG_CSDEASSERT_ADD2 (1 << (8+16))
+
+/*
+ * Register: SPI_FAST_SEQ_n
+ */
+#define SEQ_OPC_OPCODE(x) ((x) << 0)
+#define SEQ_OPC_CYCLES(x) ((x) << 8)
+#define SEQ_OPC_PADS_1 (0x0 << 14)
+#define SEQ_OPC_PADS_2 (0x1 << 14)
+#define SEQ_OPC_PADS_4 (0x3 << 14)
+#define SEQ_OPC_CSDEASSERT (1 << 16)
+
+/*
+ * Register: SPI_FAST_SEQ_CFG
+ */
+#define SEQ_CFG_STARTSEQ (1 << 0)
+#define SEQ_CFG_SWRESET (1 << 5)
+#define SEQ_CFG_CSDEASSERT (1 << 6)
+#define SEQ_CFG_READNOTWRITE (1 << 7)
+#define SEQ_CFG_ERASE (1 << 8)
+#define SEQ_CFG_PADS_1 (0x0 << 16)
+#define SEQ_CFG_PADS_2 (0x1 << 16)
+#define SEQ_CFG_PADS_4 (0x3 << 16)
+
+/*
+ * Register: SPI_MODE_BITS
+ */
+#define MODE_DATA(x) (x & 0xff)
+#define MODE_CYCLES(x) ((x & 0x3f) << 16)
+#define MODE_PADS_1 (0x0 << 22)
+#define MODE_PADS_2 (0x1 << 22)
+#define MODE_PADS_4 (0x3 << 22)
+#define DUMMY_CSDEASSERT (1 << 24)
+
+/*
+ * Register: SPI_DUMMY_BITS
+ */
+#define DUMMY_CYCLES(x) ((x & 0x3f) << 16)
+#define DUMMY_PADS_1 (0x0 << 22)
+#define DUMMY_PADS_2 (0x1 << 22)
+#define DUMMY_PADS_4 (0x3 << 22)
+#define DUMMY_CSDEASSERT (1 << 24)
+
+/*
+ * Register: SPI_FAST_SEQ_FLASH_STA_DATA
+ */
+#define STA_DATA_BYTE1(x) ((x & 0xff) << 0)
+#define STA_DATA_BYTE2(x) ((x & 0xff) << 8)
+#define STA_PADS_1 (0x0 << 16)
+#define STA_PADS_2 (0x1 << 16)
+#define STA_PADS_4 (0x3 << 16)
+#define STA_CSDEASSERT (0x1 << 20)
+#define STA_RDNOTWR (0x1 << 21)
+
+/*
+ * FSM SPI Instruction Opcodes
+ */
+#define STFSM_OPC_CMD 0x1
+#define STFSM_OPC_ADD 0x2
+#define STFSM_OPC_STA 0x3
+#define STFSM_OPC_MODE 0x4
+#define STFSM_OPC_DUMMY 0x5
+#define STFSM_OPC_DATA 0x6
+#define STFSM_OPC_WAIT 0x7
+#define STFSM_OPC_JUMP 0x8
+#define STFSM_OPC_GOTO 0x9
+#define STFSM_OPC_STOP 0xF
+
+/*
+ * FSM SPI Instructions (== opcode + operand).
+ */
+#define STFSM_INSTR(cmd, op) ((cmd) | ((op) << 4))
+
+#define STFSM_INST_CMD1 STFSM_INSTR(STFSM_OPC_CMD, 1)
+#define STFSM_INST_CMD2 STFSM_INSTR(STFSM_OPC_CMD, 2)
+#define STFSM_INST_CMD3 STFSM_INSTR(STFSM_OPC_CMD, 3)
+#define STFSM_INST_CMD4 STFSM_INSTR(STFSM_OPC_CMD, 4)
+#define STFSM_INST_CMD5 STFSM_INSTR(STFSM_OPC_CMD, 5)
+#define STFSM_INST_ADD1 STFSM_INSTR(STFSM_OPC_ADD, 1)
+#define STFSM_INST_ADD2 STFSM_INSTR(STFSM_OPC_ADD, 2)
+
+#define STFSM_INST_DATA_WRITE STFSM_INSTR(STFSM_OPC_DATA, 1)
+#define STFSM_INST_DATA_READ STFSM_INSTR(STFSM_OPC_DATA, 2)
+
+#define STFSM_INST_STA_RD1 STFSM_INSTR(STFSM_OPC_STA, 0x1)
+#define STFSM_INST_STA_WR1 STFSM_INSTR(STFSM_OPC_STA, 0x1)
+#define STFSM_INST_STA_RD2 STFSM_INSTR(STFSM_OPC_STA, 0x2)
+#define STFSM_INST_STA_WR1_2 STFSM_INSTR(STFSM_OPC_STA, 0x3)
+
+#define STFSM_INST_MODE STFSM_INSTR(STFSM_OPC_MODE, 0)
+#define STFSM_INST_DUMMY STFSM_INSTR(STFSM_OPC_DUMMY, 0)
+#define STFSM_INST_WAIT STFSM_INSTR(STFSM_OPC_WAIT, 0)
+#define STFSM_INST_STOP STFSM_INSTR(STFSM_OPC_STOP, 0)
+
+#define STFSM_DEFAULT_EMI_FREQ 100000000UL /* 100 MHz */
+#define STFSM_DEFAULT_WR_TIME (STFSM_DEFAULT_EMI_FREQ * (15/1000)) /* 15ms */
+
+#define STFSM_FLASH_SAFE_FREQ 10000000UL /* 10 MHz */
+
+#define STFSM_MAX_WAIT_SEQ_MS 1000 /* FSM execution time */
+
+/* Flash Commands */
+#define FLASH_CMD_WREN 0x06
+#define FLASH_CMD_WRDI 0x04
+#define FLASH_CMD_RDID 0x9f
+#define FLASH_CMD_RDSR 0x05
+#define FLASH_CMD_RDSR2 0x35
+#define FLASH_CMD_WRSR 0x01
+#define FLASH_CMD_SE_4K 0x20
+#define FLASH_CMD_SE_32K 0x52
+#define FLASH_CMD_SE 0xd8
+#define FLASH_CMD_CHIPERASE 0xc7
+#define FLASH_CMD_WRVCR 0x81
+#define FLASH_CMD_RDVCR 0x85
+
+#define FLASH_CMD_READ 0x03 /* READ */
+#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */
+#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */
+#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */
+#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */
+#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */
+
+#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */
+#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
+#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
+
+#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */
+#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */
+
+/* READ commands with 32-bit addressing (N25Q256 and S25FLxxxS) */
+#define FLASH_CMD_READ4 0x13
+#define FLASH_CMD_READ4_FAST 0x0c
+#define FLASH_CMD_READ4_1_1_2 0x3c
+#define FLASH_CMD_READ4_1_2_2 0xbc
+#define FLASH_CMD_READ4_1_1_4 0x6c
+#define FLASH_CMD_READ4_1_4_4 0xec
+
+/* S25FLxxxS commands */
+#define S25FL_CMD_WRITE4_1_1_4 0x34
+#define S25FL_CMD_SE4 0xdc
+#define S25FL_CMD_CLSR 0x30
+#define S25FL_CMD_DYBWR 0xe1
+#define S25FL_CMD_DYBRD 0xe0
+#define S25FL_CMD_WRITE4 0x12 /* Note, opcode clashes with
+ * 'FLASH_CMD_WRITE_1_4_4'
+ * as found on N25Qxxx devices! */
+
+/* Status register */
+#define FLASH_STATUS_BUSY 0x01
+#define FLASH_STATUS_WEL 0x02
+#define FLASH_STATUS_BP0 0x04
+#define FLASH_STATUS_BP1 0x08
+#define FLASH_STATUS_BP2 0x10
+#define FLASH_STATUS_SRWP0 0x80
+#define FLASH_STATUS_TIMEOUT 0xff
+/* S25FL Error Flags */
+#define S25FL_STATUS_E_ERR 0x20
+#define S25FL_STATUS_P_ERR 0x40
+
+#define FLASH_PAGESIZE 256 /* In Bytes */
+#define FLASH_PAGESIZE_32 (FLASH_PAGESIZE / 4) /* In uint32_t */
+#define FLASH_MAX_BUSY_WAIT (300 * HZ) /* Maximum 'CHIPERASE' time */
+
+/*
+ * Flags to tweak operation of default read/write/erase routines
+ */
+#define CFG_READ_TOGGLE_32BIT_ADDR 0x00000001
+#define CFG_WRITE_TOGGLE_32BIT_ADDR 0x00000002
+#define CFG_WRITE_EX_32BIT_ADDR_DELAY 0x00000004
+#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008
+#define CFG_S25FL_CHECK_ERROR_FLAGS 0x00000010
+
+struct stfsm_seq {
+ uint32_t data_size;
+ uint32_t addr1;
+ uint32_t addr2;
+ uint32_t addr_cfg;
+ uint32_t seq_opc[5];
+ uint32_t mode;
+ uint32_t dummy;
+ uint32_t status;
+ uint8_t seq[16];
+ uint32_t seq_cfg;
+} __packed __aligned(4);
+
+struct stfsm {
+ struct device *dev;
+ void __iomem *base;
+ struct resource *region;
+ struct mtd_info mtd;
+ struct mutex lock;
+ struct flash_info *info;
+
+ uint32_t configuration;
+ uint32_t fifo_dir_delay;
+ bool booted_from_spi;
+ bool reset_signal;
+ bool reset_por;
+
+ struct stfsm_seq stfsm_seq_read;
+ struct stfsm_seq stfsm_seq_write;
+ struct stfsm_seq stfsm_seq_en_32bit_addr;
+};
+
+/* Parameters to configure a READ or WRITE FSM sequence */
+struct seq_rw_config {
+ uint32_t flags; /* flags to support config */
+ uint8_t cmd; /* FLASH command */
+ int write; /* Write Sequence */
+ uint8_t addr_pads; /* No. of addr pads (MODE & DUMMY) */
+ uint8_t data_pads; /* No. of data pads */
+ uint8_t mode_data; /* MODE data */
+ uint8_t mode_cycles; /* No. of MODE cycles */
+ uint8_t dummy_cycles; /* No. of DUMMY cycles */
+};
+
+/* SPI Flash Device Table */
+struct flash_info {
+ char *name;
+ /*
+ * JEDEC id zero means "no ID" (most older chips); otherwise it has
+ * a high byte of zero plus three data bytes: the manufacturer id,
+ * then a two byte device id.
+ */
+ u32 jedec_id;
+ u16 ext_id;
+ /*
+ * The size listed here is what works with FLASH_CMD_SE, which isn't
+ * necessarily called a "sector" by the vendor.
+ */
+ unsigned sector_size;
+ u16 n_sectors;
+ u32 flags;
+ /*
+ * Note, where FAST_READ is supported, freq_max specifies the
+ * FAST_READ frequency, not the READ frequency.
+ */
+ u32 max_freq;
+ int (*config)(struct stfsm *);
+};
+
+static int stfsm_n25q_config(struct stfsm *fsm);
+static int stfsm_mx25_config(struct stfsm *fsm);
+static int stfsm_s25fl_config(struct stfsm *fsm);
+static int stfsm_w25q_config(struct stfsm *fsm);
+
+static struct flash_info flash_types[] = {
+ /*
+ * ST Microelectronics/Numonyx --
+ * (newer production versions may have feature updates
+ * (eg faster operating frequency)
+ */
+#define M25P_FLAG (FLASH_FLAG_READ_WRITE | FLASH_FLAG_READ_FAST)
+ { "m25p40", 0x202013, 0, 64 * 1024, 8, M25P_FLAG, 25, NULL },
+ { "m25p80", 0x202014, 0, 64 * 1024, 16, M25P_FLAG, 25, NULL },
+ { "m25p16", 0x202015, 0, 64 * 1024, 32, M25P_FLAG, 25, NULL },
+ { "m25p32", 0x202016, 0, 64 * 1024, 64, M25P_FLAG, 50, NULL },
+ { "m25p64", 0x202017, 0, 64 * 1024, 128, M25P_FLAG, 50, NULL },
+ { "m25p128", 0x202018, 0, 256 * 1024, 64, M25P_FLAG, 50, NULL },
+
+#define M25PX_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_FAST | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_WRITE_1_1_2)
+ { "m25px32", 0x207116, 0, 64 * 1024, 64, M25PX_FLAG, 75, NULL },
+ { "m25px64", 0x207117, 0, 64 * 1024, 128, M25PX_FLAG, 75, NULL },
+
+#define MX25_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_FAST | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_READ_1_2_2 | \
+ FLASH_FLAG_READ_1_1_4 | \
+ FLASH_FLAG_READ_1_4_4 | \
+ FLASH_FLAG_SE_4K | \
+ FLASH_FLAG_SE_32K)
+ { "mx25l25635e", 0xc22019, 0, 64*1024, 512,
+ (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
+ stfsm_mx25_config },
+
+#define N25Q_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_FAST | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_READ_1_2_2 | \
+ FLASH_FLAG_READ_1_1_4 | \
+ FLASH_FLAG_READ_1_4_4 | \
+ FLASH_FLAG_WRITE_1_1_2 | \
+ FLASH_FLAG_WRITE_1_2_2 | \
+ FLASH_FLAG_WRITE_1_1_4 | \
+ FLASH_FLAG_WRITE_1_4_4)
+ { "n25q128", 0x20ba18, 0, 64 * 1024, 256, N25Q_FLAG, 108,
+ stfsm_n25q_config },
+ { "n25q256", 0x20ba19, 0, 64 * 1024, 512,
+ N25Q_FLAG | FLASH_FLAG_32BIT_ADDR, 108, stfsm_n25q_config },
+
+ /*
+ * Spansion S25FLxxxP
+ * - 256KiB and 64KiB sector variants (identified by ext. JEDEC)
+ */
+#define S25FLXXXP_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_READ_1_2_2 | \
+ FLASH_FLAG_READ_1_1_4 | \
+ FLASH_FLAG_READ_1_4_4 | \
+ FLASH_FLAG_WRITE_1_1_4 | \
+ FLASH_FLAG_READ_FAST)
+ { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, S25FLXXXP_FLAG, 80,
+ stfsm_s25fl_config },
+ { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, S25FLXXXP_FLAG, 80,
+ stfsm_s25fl_config },
+
+ /*
+ * Spansion S25FLxxxS
+ * - 256KiB and 64KiB sector variants (identified by ext. JEDEC)
+ * - RESET# signal supported by die but not bristled out on all
+ * package types. The package type is a function of board design,
+ * so this information is captured in the board's flags.
+ * - Supports 'DYB' sector protection. Depending on variant, sectors
+ * may default to locked state on power-on.
+ */
+#define S25FLXXXS_FLAG (S25FLXXXP_FLAG | \
+ FLASH_FLAG_RESET | \
+ FLASH_FLAG_DYB_LOCKING)
+ { "s25fl128s0", 0x012018, 0x0300, 256 * 1024, 64, S25FLXXXS_FLAG, 80,
+ stfsm_s25fl_config },
+ { "s25fl128s1", 0x012018, 0x0301, 64 * 1024, 256, S25FLXXXS_FLAG, 80,
+ stfsm_s25fl_config },
+ { "s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128,
+ S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config },
+ { "s25fl256s1", 0x010219, 0x4d01, 64 * 1024, 512,
+ S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config },
+
+ /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
+#define W25X_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_FAST | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_WRITE_1_1_2)
+ { "w25x40", 0xef3013, 0, 64 * 1024, 8, W25X_FLAG, 75, NULL },
+ { "w25x80", 0xef3014, 0, 64 * 1024, 16, W25X_FLAG, 75, NULL },
+ { "w25x16", 0xef3015, 0, 64 * 1024, 32, W25X_FLAG, 75, NULL },
+ { "w25x32", 0xef3016, 0, 64 * 1024, 64, W25X_FLAG, 75, NULL },
+ { "w25x64", 0xef3017, 0, 64 * 1024, 128, W25X_FLAG, 75, NULL },
+
+ /* Winbond -- w25q "blocks" are 64K, "sectors" are 4KiB */
+#define W25Q_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_FAST | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_READ_1_2_2 | \
+ FLASH_FLAG_READ_1_1_4 | \
+ FLASH_FLAG_READ_1_4_4 | \
+ FLASH_FLAG_WRITE_1_1_4)
+ { "w25q80", 0xef4014, 0, 64 * 1024, 16, W25Q_FLAG, 80,
+ stfsm_w25q_config },
+ { "w25q16", 0xef4015, 0, 64 * 1024, 32, W25Q_FLAG, 80,
+ stfsm_w25q_config },
+ { "w25q32", 0xef4016, 0, 64 * 1024, 64, W25Q_FLAG, 80,
+ stfsm_w25q_config },
+ { "w25q64", 0xef4017, 0, 64 * 1024, 128, W25Q_FLAG, 80,
+ stfsm_w25q_config },
+
+ /* Sentinel */
+ { NULL, 0x000000, 0, 0, 0, 0, 0, NULL },
+};
+
+/*
+ * FSM message sequence configurations:
+ *
+ * All configs are presented in order of preference
+ */
+
+/* Default READ configurations, in order of preference */
+static struct seq_rw_config default_read_configs[] = {
+ {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 2, 4},
+ {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 4, 0},
+ {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 4, 0},
+ {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+/* Default WRITE configurations */
+static struct seq_rw_config default_write_configs[] = {
+ {FLASH_FLAG_WRITE_1_4_4, FLASH_CMD_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0},
+ {FLASH_FLAG_WRITE_1_1_4, FLASH_CMD_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0},
+ {FLASH_FLAG_WRITE_1_2_2, FLASH_CMD_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0},
+ {FLASH_FLAG_WRITE_1_1_2, FLASH_CMD_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0},
+ {FLASH_FLAG_READ_WRITE, FLASH_CMD_WRITE, 1, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+/*
+ * [N25Qxxx] Configuration
+ */
+#define N25Q_VCR_DUMMY_CYCLES(x) (((x) & 0xf) << 4)
+#define N25Q_VCR_XIP_DISABLED ((uint8_t)0x1 << 3)
+#define N25Q_VCR_WRAP_CONT 0x3
+
+/* N25Q 3-byte Address READ configurations
+ * - 'FAST' variants configured for 8 dummy cycles.
+ *
+ * Note, the number of dummy cycles used for 'FAST' READ operations is
+ * configurable and would normally be tuned according to the READ command and
+ * operating frequency. However, this applies universally to all 'FAST' READ
+ * commands, including those used by the SPIBoot controller, and remains in
+ * force until the device is power-cycled. Since the SPIBoot controller is
+ * hard-wired to use 8 dummy cycles, we must configure the device to also use 8
+ * cycles.
+ */
+static struct seq_rw_config n25q_read3_configs[] = {
+ {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+/* N25Q 4-byte Address READ configurations
+ * - use special 4-byte address READ commands (reduces overheads, and
+ * reduces risk of hitting watchdog reset issues).
+ * - 'FAST' variants configured for 8 dummy cycles (see note above.)
+ */
+static struct seq_rw_config n25q_read4_configs[] = {
+ {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+/*
+ * [MX25xxx] Configuration
+ */
+#define MX25_STATUS_QE (0x1 << 6)
+
+static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
+{
+ seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR) |
+ SEQ_OPC_CSDEASSERT);
+
+ seq->seq[0] = STFSM_INST_CMD1;
+ seq->seq[1] = STFSM_INST_WAIT;
+ seq->seq[2] = STFSM_INST_STOP;
+
+ seq->seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_ERASE |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ);
+
+ return 0;
+}
+
+/*
+ * [S25FLxxx] Configuration
+ */
+#define STFSM_S25FL_CONFIG_QE (0x1 << 1)
+
+/*
+ * S25FLxxxS devices provide three ways of supporting 32-bit addressing: Bank
+ * Register, Extended Address Modes, and a 32-bit address command set. The
+ * 32-bit address command set is used here, since it avoids any problems with
+ * entering a state that is incompatible with the SPIBoot Controller.
+ */
+static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
+ {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4},
+ {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0},
+ {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
+ {FLASH_FLAG_WRITE_1_1_4, S25FL_CMD_WRITE4_1_1_4, 1, 1, 4, 0x00, 0, 0},
+ {FLASH_FLAG_READ_WRITE, S25FL_CMD_WRITE4, 1, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+/*
+ * [W25Qxxx] Configuration
+ */
+#define W25Q_STATUS_QE (0x1 << 9)
+
+static struct stfsm_seq stfsm_seq_read_jedec = {
+ .data_size = TRANSFER_SIZE(8),
+ .seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_RDID)),
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_DATA_READ,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_read_status_fifo = {
+ .data_size = TRANSFER_SIZE(4),
+ .seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_RDSR)),
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_DATA_READ,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_erase_sector = {
+ /* 'addr_cfg' configured during initialisation */
+ .seq_opc = {
+ (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+
+ (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_SE)),
+ },
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_ADD1,
+ STFSM_INST_ADD2,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_erase_chip = {
+ .seq_opc = {
+ (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+
+ (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_CHIPERASE) | SEQ_OPC_CSDEASSERT),
+ },
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_WAIT,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_ERASE |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_write_status = {
+ .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+ .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WRSR)),
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_STA_WR1,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_wrvcr = {
+ .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+ .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WRVCR)),
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_STA_WR1,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq)
+{
+ seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR));
+ seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
+ SEQ_OPC_CSDEASSERT);
+
+ seq->seq[0] = STFSM_INST_CMD2;
+ seq->seq[1] = STFSM_INST_CMD1;
+ seq->seq[2] = STFSM_INST_WAIT;
+ seq->seq[3] = STFSM_INST_STOP;
+
+ seq->seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_ERASE |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ);
+
+ return 0;
+}
+
+static inline int stfsm_is_idle(struct stfsm *fsm)
+{
+ return readl(fsm->base + SPI_FAST_SEQ_STA) & 0x10;
+}
+
+static inline uint32_t stfsm_fifo_available(struct stfsm *fsm)
+{
+ return (readl(fsm->base + SPI_FAST_SEQ_STA) >> 5) & 0x7f;
+}
+
+static void stfsm_clear_fifo(struct stfsm *fsm)
+{
+ uint32_t avail;
+
+ for (;;) {
+ avail = stfsm_fifo_available(fsm);
+ if (!avail)
+ break;
+
+ while (avail) {
+ readl(fsm->base + SPI_FAST_SEQ_DATA_REG);
+ avail--;
+ }
+ }
+}
+
+static inline void stfsm_load_seq(struct stfsm *fsm,
+ const struct stfsm_seq *seq)
+{
+ void __iomem *dst = fsm->base + SPI_FAST_SEQ_TRANSFER_SIZE;
+ const uint32_t *src = (const uint32_t *)seq;
+ int words = sizeof(*seq) / sizeof(*src);
+
+ BUG_ON(!stfsm_is_idle(fsm));
+
+ while (words--) {
+ writel(*src, dst);
+ src++;
+ dst += 4;
+ }
+}
+
+static void stfsm_wait_seq(struct stfsm *fsm)
+{
+ unsigned long deadline;
+ int timeout = 0;
+
+ deadline = jiffies + msecs_to_jiffies(STFSM_MAX_WAIT_SEQ_MS);
+
+ while (!timeout) {
+ if (time_after_eq(jiffies, deadline))
+ timeout = 1;
+
+ if (stfsm_is_idle(fsm))
+ return;
+
+ cond_resched();
+ }
+
+ dev_err(fsm->dev, "timeout on sequence completion\n");
+}
+
+static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size)
+{
+ uint32_t remaining = size >> 2;
+ uint32_t avail;
+ uint32_t words;
+
+ dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size);
+
+ BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
+
+ while (remaining) {
+ for (;;) {
+ avail = stfsm_fifo_available(fsm);
+ if (avail)
+ break;
+ udelay(1);
+ }
+ words = min(avail, remaining);
+ remaining -= words;
+
+ readsl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words);
+ buf += words;
+ }
+}
+
+static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
+ uint32_t size)
+{
+ uint32_t words = size >> 2;
+
+ dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size);
+
+ BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
+
+ writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words);
+
+ return size;
+}
+
+static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter)
+{
+ struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr;
+ uint32_t cmd = enter ? FLASH_CMD_EN4B_ADDR : FLASH_CMD_EX4B_ADDR;
+
+ seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(cmd) |
+ SEQ_OPC_CSDEASSERT);
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_wait_seq(fsm);
+
+ return 0;
+}
+
+static uint8_t stfsm_wait_busy(struct stfsm *fsm)
+{
+ struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
+ unsigned long deadline;
+ uint32_t status;
+ int timeout = 0;
+
+ /* Use RDRS1 */
+ seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_RDSR));
+
+ /* Load read_status sequence */
+ stfsm_load_seq(fsm, seq);
+
+ /*
+ * Repeat until busy bit is deasserted, or timeout, or error (S25FLxxxS)
+ */
+ deadline = jiffies + FLASH_MAX_BUSY_WAIT;
+ while (!timeout) {
+ if (time_after_eq(jiffies, deadline))
+ timeout = 1;
+
+ stfsm_wait_seq(fsm);
+
+ stfsm_read_fifo(fsm, &status, 4);
+
+ if ((status & FLASH_STATUS_BUSY) == 0)
+ return 0;
+
+ if ((fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) &&
+ ((status & S25FL_STATUS_P_ERR) ||
+ (status & S25FL_STATUS_E_ERR)))
+ return (uint8_t)(status & 0xff);
+
+ if (!timeout)
+ /* Restart */
+ writel(seq->seq_cfg, fsm->base + SPI_FAST_SEQ_CFG);
+
+ cond_resched();
+ }
+
+ dev_err(fsm->dev, "timeout on wait_busy\n");
+
+ return FLASH_STATUS_TIMEOUT;
+}
+
+static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd,
+ uint8_t *status)
+{
+ struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
+ uint32_t tmp;
+
+ dev_dbg(fsm->dev, "reading STA[%s]\n",
+ (cmd == FLASH_CMD_RDSR) ? "1" : "2");
+
+ seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(cmd)),
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_read_fifo(fsm, &tmp, 4);
+
+ *status = (uint8_t)(tmp >> 24);
+
+ stfsm_wait_seq(fsm);
+
+ return 0;
+}
+
+static int stfsm_write_status(struct stfsm *fsm, uint16_t status,
+ int sta_bytes)
+{
+ struct stfsm_seq *seq = &stfsm_seq_write_status;
+
+ dev_dbg(fsm->dev, "writing STA[%s] 0x%04x\n",
+ (sta_bytes == 1) ? "1" : "1+2", status);
+
+ seq->status = (uint32_t)status | STA_PADS_1 | STA_CSDEASSERT;
+ seq->seq[2] = (sta_bytes == 1) ?
+ STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2;
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_wait_seq(fsm);
+
+ return 0;
+};
+
+static int stfsm_wrvcr(struct stfsm *fsm, uint8_t data)
+{
+ struct stfsm_seq *seq = &stfsm_seq_wrvcr;
+
+ dev_dbg(fsm->dev, "writing VCR 0x%02x\n", data);
+
+ seq->status = (STA_DATA_BYTE1(data) | STA_PADS_1 | STA_CSDEASSERT);
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_wait_seq(fsm);
+
+ return 0;
+}
+
+/*
+ * SoC reset on 'boot-from-spi' systems
+ *
+ * Certain modes of operation cause the Flash device to enter a particular state
+ * for a period of time (e.g. 'Erase Sector', 'Quad Enable', and 'Enter 32-bit
+ * Addr' commands). On boot-from-spi systems, it is important to consider what
+ * happens if a warm reset occurs during this period. The SPIBoot controller
+ * assumes that Flash device is in its default reset state, 24-bit address mode,
+ * and ready to accept commands. This can be achieved using some form of
+ * on-board logic/controller to force a device POR in response to a SoC-level
+ * reset or by making use of the device reset signal if available (limited
+ * number of devices only).
+ *
+ * Failure to take such precautions can cause problems following a warm reset.
+ * For some operations (e.g. ERASE), there is little that can be done. For
+ * other modes of operation (e.g. 32-bit addressing), options are often
+ * available that can help minimise the window in which a reset could cause a
+ * problem.
+ *
+ */
+static bool stfsm_can_handle_soc_reset(struct stfsm *fsm)
+{
+ /* Reset signal is available on the board and supported by the device */
+ if (fsm->reset_signal && fsm->info->flags & FLASH_FLAG_RESET)
+ return true;
+
+ /* Board-level logic forces a power-on-reset */
+ if (fsm->reset_por)
+ return true;
+
+ /* Reset is not properly handled and may result in failure to reboot */
+ return false;
+}
+
+/* Configure 'addr_cfg' according to addressing mode */
+static void stfsm_prepare_erasesec_seq(struct stfsm *fsm,
+ struct stfsm_seq *seq)
+{
+ int addr1_cycles = fsm->info->flags & FLASH_FLAG_32BIT_ADDR ? 16 : 8;
+
+ seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(addr1_cycles) |
+ ADR_CFG_PADS_1_ADD1 |
+ ADR_CFG_CYCLES_ADD2(16) |
+ ADR_CFG_PADS_1_ADD2 |
+ ADR_CFG_CSDEASSERT_ADD2);
+}
+
+/* Search for preferred configuration based on available flags */
+static struct seq_rw_config *
+stfsm_search_seq_rw_configs(struct stfsm *fsm,
+ struct seq_rw_config cfgs[])
+{
+ struct seq_rw_config *config;
+ int flags = fsm->info->flags;
+
+ for (config = cfgs; config->cmd != 0; config++)
+ if ((config->flags & flags) == config->flags)
+ return config;
+
+ return NULL;
+}
+
+/* Prepare a READ/WRITE sequence according to configuration parameters */
+static void stfsm_prepare_rw_seq(struct stfsm *fsm,
+ struct stfsm_seq *seq,
+ struct seq_rw_config *cfg)
+{
+ int addr1_cycles, addr2_cycles;
+ int i = 0;
+
+ memset(seq, 0, sizeof(*seq));
+
+ /* Add READ/WRITE OPC */
+ seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(cfg->cmd));
+
+ /* Add WREN OPC for a WRITE sequence */
+ if (cfg->write)
+ seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
+ SEQ_OPC_CSDEASSERT);
+
+ /* Address configuration (24 or 32-bit addresses) */
+ addr1_cycles = (fsm->info->flags & FLASH_FLAG_32BIT_ADDR) ? 16 : 8;
+ addr1_cycles /= cfg->addr_pads;
+ addr2_cycles = 16 / cfg->addr_pads;
+ seq->addr_cfg = ((addr1_cycles & 0x3f) << 0 | /* ADD1 cycles */
+ (cfg->addr_pads - 1) << 6 | /* ADD1 pads */
+ (addr2_cycles & 0x3f) << 16 | /* ADD2 cycles */
+ ((cfg->addr_pads - 1) << 22)); /* ADD2 pads */
+
+ /* Data/Sequence configuration */
+ seq->seq_cfg = ((cfg->data_pads - 1) << 16 |
+ SEQ_CFG_STARTSEQ |
+ SEQ_CFG_CSDEASSERT);
+ if (!cfg->write)
+ seq->seq_cfg |= SEQ_CFG_READNOTWRITE;
+
+ /* Mode configuration (no. of pads taken from addr cfg) */
+ seq->mode = ((cfg->mode_data & 0xff) << 0 | /* data */
+ (cfg->mode_cycles & 0x3f) << 16 | /* cycles */
+ (cfg->addr_pads - 1) << 22); /* pads */
+
+ /* Dummy configuration (no. of pads taken from addr cfg) */
+ seq->dummy = ((cfg->dummy_cycles & 0x3f) << 16 | /* cycles */
+ (cfg->addr_pads - 1) << 22); /* pads */
+
+
+ /* Instruction sequence */
+ i = 0;
+ if (cfg->write)
+ seq->seq[i++] = STFSM_INST_CMD2;
+
+ seq->seq[i++] = STFSM_INST_CMD1;
+
+ seq->seq[i++] = STFSM_INST_ADD1;
+ seq->seq[i++] = STFSM_INST_ADD2;
+
+ if (cfg->mode_cycles)
+ seq->seq[i++] = STFSM_INST_MODE;
+
+ if (cfg->dummy_cycles)
+ seq->seq[i++] = STFSM_INST_DUMMY;
+
+ seq->seq[i++] =
+ cfg->write ? STFSM_INST_DATA_WRITE : STFSM_INST_DATA_READ;
+ seq->seq[i++] = STFSM_INST_STOP;
+}
+
+static int stfsm_search_prepare_rw_seq(struct stfsm *fsm,
+ struct stfsm_seq *seq,
+ struct seq_rw_config *cfgs)
+{
+ struct seq_rw_config *config;
+
+ config = stfsm_search_seq_rw_configs(fsm, cfgs);
+ if (!config) {
+ dev_err(fsm->dev, "failed to find suitable config\n");
+ return -EINVAL;
+ }
+
+ stfsm_prepare_rw_seq(fsm, seq, config);
+
+ return 0;
+}
+
+/* Prepare a READ/WRITE/ERASE 'default' sequences */
+static int stfsm_prepare_rwe_seqs_default(struct stfsm *fsm)
+{
+ uint32_t flags = fsm->info->flags;
+ int ret;
+
+ /* Configure 'READ' sequence */
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+ default_read_configs);
+ if (ret) {
+ dev_err(fsm->dev,
+ "failed to prep READ sequence with flags [0x%08x]\n",
+ flags);
+ return ret;
+ }
+
+ /* Configure 'WRITE' sequence */
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write,
+ default_write_configs);
+ if (ret) {
+ dev_err(fsm->dev,
+ "failed to prep WRITE sequence with flags [0x%08x]\n",
+ flags);
+ return ret;
+ }
+
+ /* Configure 'ERASE_SECTOR' sequence */
+ stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector);
+
+ return 0;
+}
+
+static int stfsm_mx25_config(struct stfsm *fsm)
+{
+ uint32_t flags = fsm->info->flags;
+ uint32_t data_pads;
+ uint8_t sta;
+ int ret;
+ bool soc_reset;
+
+ /*
+ * Use default READ/WRITE sequences
+ */
+ ret = stfsm_prepare_rwe_seqs_default(fsm);
+ if (ret)
+ return ret;
+
+ /*
+ * Configure 32-bit Address Support
+ */
+ if (flags & FLASH_FLAG_32BIT_ADDR) {
+ /* Configure 'enter_32bitaddr' FSM sequence */
+ stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
+
+ soc_reset = stfsm_can_handle_soc_reset(fsm);
+ if (soc_reset || !fsm->booted_from_spi) {
+ /* If we can handle SoC resets, we enable 32-bit address
+ * mode pervasively */
+ stfsm_enter_32bit_addr(fsm, 1);
+
+ } else {
+ /* Else, enable/disable 32-bit addressing before/after
+ * each operation */
+ fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR |
+ CFG_WRITE_TOGGLE_32BIT_ADDR |
+ CFG_ERASESEC_TOGGLE_32BIT_ADDR);
+ /* It seems a small delay is required after exiting
+ * 32-bit mode following a write operation. The issue
+ * is under investigation.
+ */
+ fsm->configuration |= CFG_WRITE_EX_32BIT_ADDR_DELAY;
+ }
+ }
+
+ /* For QUAD mode, set 'QE' STATUS bit */
+ data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
+ if (data_pads == 4) {
+ stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta);
+ sta |= MX25_STATUS_QE;
+ stfsm_write_status(fsm, sta, 1);
+ }
+
+ return 0;
+}
+
+static int stfsm_n25q_config(struct stfsm *fsm)
+{
+ uint32_t flags = fsm->info->flags;
+ uint8_t vcr;
+ int ret = 0;
+ bool soc_reset;
+
+ /* Configure 'READ' sequence */
+ if (flags & FLASH_FLAG_32BIT_ADDR)
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+ n25q_read4_configs);
+ else
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+ n25q_read3_configs);
+ if (ret) {
+ dev_err(fsm->dev,
+ "failed to prepare READ sequence with flags [0x%08x]\n",
+ flags);
+ return ret;
+ }
+
+ /* Configure 'WRITE' sequence (default configs) */
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write,
+ default_write_configs);
+ if (ret) {
+ dev_err(fsm->dev,
+ "preparing WRITE sequence using flags [0x%08x] failed\n",
+ flags);
+ return ret;
+ }
+
+ /* * Configure 'ERASE_SECTOR' sequence */
+ stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector);
+
+ /* Configure 32-bit address support */
+ if (flags & FLASH_FLAG_32BIT_ADDR) {
+ stfsm_n25q_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
+
+ soc_reset = stfsm_can_handle_soc_reset(fsm);
+ if (soc_reset || !fsm->booted_from_spi) {
+ /*
+ * If we can handle SoC resets, we enable 32-bit
+ * address mode pervasively
+ */
+ stfsm_enter_32bit_addr(fsm, 1);
+ } else {
+ /*
+ * If not, enable/disable for WRITE and ERASE
+ * operations (READ uses special commands)
+ */
+ fsm->configuration = (CFG_WRITE_TOGGLE_32BIT_ADDR |
+ CFG_ERASESEC_TOGGLE_32BIT_ADDR);
+ }
+ }
+
+ /*
+ * Configure device to use 8 dummy cycles
+ */
+ vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED |
+ N25Q_VCR_WRAP_CONT);
+ stfsm_wrvcr(fsm, vcr);
+
+ return 0;
+}
+
+static void stfsm_s25fl_prepare_erasesec_seq_32(struct stfsm_seq *seq)
+{
+ seq->seq_opc[1] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(S25FL_CMD_SE4));
+
+ seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(16) |
+ ADR_CFG_PADS_1_ADD1 |
+ ADR_CFG_CYCLES_ADD2(16) |
+ ADR_CFG_PADS_1_ADD2 |
+ ADR_CFG_CSDEASSERT_ADD2);
+}
+
+static void stfsm_s25fl_read_dyb(struct stfsm *fsm, uint32_t offs, uint8_t *dby)
+{
+ uint32_t tmp;
+ struct stfsm_seq seq = {
+ .data_size = TRANSFER_SIZE(4),
+ .seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(S25FL_CMD_DYBRD)),
+ .addr_cfg = (ADR_CFG_CYCLES_ADD1(16) |
+ ADR_CFG_PADS_1_ADD1 |
+ ADR_CFG_CYCLES_ADD2(16) |
+ ADR_CFG_PADS_1_ADD2),
+ .addr1 = (offs >> 16) & 0xffff,
+ .addr2 = offs & 0xffff,
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_ADD1,
+ STFSM_INST_ADD2,
+ STFSM_INST_DATA_READ,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+ };
+
+ stfsm_load_seq(fsm, &seq);
+
+ stfsm_read_fifo(fsm, &tmp, 4);
+
+ *dby = (uint8_t)(tmp >> 24);
+
+ stfsm_wait_seq(fsm);
+}
+
+static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby)
+{
+ struct stfsm_seq seq = {
+ .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
+ SEQ_OPC_CSDEASSERT),
+ .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)),
+ .addr_cfg = (ADR_CFG_CYCLES_ADD1(16) |
+ ADR_CFG_PADS_1_ADD1 |
+ ADR_CFG_CYCLES_ADD2(16) |
+ ADR_CFG_PADS_1_ADD2),
+ .status = (uint32_t)dby | STA_PADS_1 | STA_CSDEASSERT,
+ .addr1 = (offs >> 16) & 0xffff,
+ .addr2 = offs & 0xffff,
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_ADD1,
+ STFSM_INST_ADD2,
+ STFSM_INST_STA_WR1,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+ };
+
+ stfsm_load_seq(fsm, &seq);
+ stfsm_wait_seq(fsm);
+
+ stfsm_wait_busy(fsm);
+}
+
+static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm)
+{
+ struct stfsm_seq seq = {
+ .seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(S25FL_CMD_CLSR) |
+ SEQ_OPC_CSDEASSERT),
+ .seq_opc[1] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WRDI) |
+ SEQ_OPC_CSDEASSERT),
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_WAIT,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_ERASE |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+ };
+
+ stfsm_load_seq(fsm, &seq);
+
+ stfsm_wait_seq(fsm);
+
+ return 0;
+}
+
+static int stfsm_s25fl_config(struct stfsm *fsm)
+{
+ struct flash_info *info = fsm->info;
+ uint32_t flags = info->flags;
+ uint32_t data_pads;
+ uint32_t offs;
+ uint16_t sta_wr;
+ uint8_t sr1, cr1, dyb;
+ int ret;
+
+ if (flags & FLASH_FLAG_32BIT_ADDR) {
+ /*
+ * Prepare Read/Write/Erase sequences according to S25FLxxx
+ * 32-bit address command set
+ */
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+ stfsm_s25fl_read4_configs);
+ if (ret)
+ return ret;
+
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write,
+ stfsm_s25fl_write4_configs);
+ if (ret)
+ return ret;
+
+ stfsm_s25fl_prepare_erasesec_seq_32(&stfsm_seq_erase_sector);
+
+ } else {
+ /* Use default configurations for 24-bit addressing */
+ ret = stfsm_prepare_rwe_seqs_default(fsm);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * For devices that support 'DYB' sector locking, check lock status and
+ * unlock sectors if necessary (some variants power-on with sectors
+ * locked by default)
+ */
+ if (flags & FLASH_FLAG_DYB_LOCKING) {
+ offs = 0;
+ for (offs = 0; offs < info->sector_size * info->n_sectors;) {
+ stfsm_s25fl_read_dyb(fsm, offs, &dyb);
+ if (dyb == 0x00)
+ stfsm_s25fl_write_dyb(fsm, offs, 0xff);
+
+ /* Handle bottom/top 4KiB parameter sectors */
+ if ((offs < info->sector_size * 2) ||
+ (offs >= (info->sector_size - info->n_sectors * 4)))
+ offs += 0x1000;
+ else
+ offs += 0x10000;
+ }
+ }
+
+ /* Check status of 'QE' bit */
+ data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
+ stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1);
+ if (data_pads == 4) {
+ if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
+ /* Set 'QE' */
+ cr1 |= STFSM_S25FL_CONFIG_QE;
+
+ stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
+ sta_wr = ((uint16_t)cr1 << 8) | sr1;
+
+ stfsm_write_status(fsm, sta_wr, 2);
+
+ stfsm_wait_busy(fsm);
+ }
+ } else {
+ if ((cr1 & STFSM_S25FL_CONFIG_QE)) {
+ /* Clear 'QE' */
+ cr1 &= ~STFSM_S25FL_CONFIG_QE;
+
+ stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
+ sta_wr = ((uint16_t)cr1 << 8) | sr1;
+
+ stfsm_write_status(fsm, sta_wr, 2);
+
+ stfsm_wait_busy(fsm);
+ }
+
+ }
+
+ /*
+ * S25FLxxx devices support Program and Error error flags.
+ * Configure driver to check flags and clear if necessary.
+ */
+ fsm->configuration |= CFG_S25FL_CHECK_ERROR_FLAGS;
+
+ return 0;
+}
+
+static int stfsm_w25q_config(struct stfsm *fsm)
+{
+ uint32_t data_pads;
+ uint16_t sta_wr;
+ uint8_t sta1, sta2;
+ int ret;
+
+ ret = stfsm_prepare_rwe_seqs_default(fsm);
+ if (ret)
+ return ret;
+
+ /* If using QUAD mode, set QE STATUS bit */
+ data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
+ if (data_pads == 4) {
+ stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta1);
+ stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sta2);
+
+ sta_wr = ((uint16_t)sta2 << 8) | sta1;
+
+ sta_wr |= W25Q_STATUS_QE;
+
+ stfsm_write_status(fsm, sta_wr, 2);
+
+ stfsm_wait_busy(fsm);
+ }
+
+ return 0;
+}
+
+static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
+ uint32_t offset)
+{
+ struct stfsm_seq *seq = &fsm->stfsm_seq_read;
+ uint32_t data_pads;
+ uint32_t read_mask;
+ uint32_t size_ub;
+ uint32_t size_lb;
+ uint32_t size_mop;
+ uint32_t tmp[4];
+ uint32_t page_buf[FLASH_PAGESIZE_32];
+ uint8_t *p;
+
+ dev_dbg(fsm->dev, "reading %d bytes from 0x%08x\n", size, offset);
+
+ /* Enter 32-bit address mode, if required */
+ if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR)
+ stfsm_enter_32bit_addr(fsm, 1);
+
+ /* Must read in multiples of 32 cycles (or 32*pads/8 Bytes) */
+ data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1;
+ read_mask = (data_pads << 2) - 1;
+
+ /* Handle non-aligned buf */
+ p = ((uint32_t)buf & 0x3) ? (uint8_t *)page_buf : buf;
+
+ /* Handle non-aligned size */
+ size_ub = (size + read_mask) & ~read_mask;
+ size_lb = size & ~read_mask;
+ size_mop = size & read_mask;
+
+ seq->data_size = TRANSFER_SIZE(size_ub);
+ seq->addr1 = (offset >> 16) & 0xffff;
+ seq->addr2 = offset & 0xffff;
+
+ stfsm_load_seq(fsm, seq);
+
+ if (size_lb)
+ stfsm_read_fifo(fsm, (uint32_t *)p, size_lb);
+
+ if (size_mop) {
+ stfsm_read_fifo(fsm, tmp, read_mask + 1);
+ memcpy(p + size_lb, &tmp, size_mop);
+ }
+
+ /* Handle non-aligned buf */
+ if ((uint32_t)buf & 0x3)
+ memcpy(buf, page_buf, size);
+
+ /* Wait for sequence to finish */
+ stfsm_wait_seq(fsm);
+
+ stfsm_clear_fifo(fsm);
+
+ /* Exit 32-bit address mode, if required */
+ if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR)
+ stfsm_enter_32bit_addr(fsm, 0);
+
+ return 0;
+}
+
+static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
+ uint32_t size, uint32_t offset)
+{
+ struct stfsm_seq *seq = &fsm->stfsm_seq_write;
+ uint32_t data_pads;
+ uint32_t write_mask;
+ uint32_t size_ub;
+ uint32_t size_lb;
+ uint32_t size_mop;
+ uint32_t tmp[4];
+ uint32_t page_buf[FLASH_PAGESIZE_32];
+ uint8_t *t = (uint8_t *)&tmp;
+ const uint8_t *p;
+ int ret;
+ int i;
+
+ dev_dbg(fsm->dev, "writing %d bytes to 0x%08x\n", size, offset);
+
+ /* Enter 32-bit address mode, if required */
+ if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR)
+ stfsm_enter_32bit_addr(fsm, 1);
+
+ /* Must write in multiples of 32 cycles (or 32*pads/8 bytes) */
+ data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1;
+ write_mask = (data_pads << 2) - 1;
+
+ /* Handle non-aligned buf */
+ if ((uint32_t)buf & 0x3) {
+ memcpy(page_buf, buf, size);
+ p = (uint8_t *)page_buf;
+ } else {
+ p = buf;
+ }
+
+ /* Handle non-aligned size */
+ size_ub = (size + write_mask) & ~write_mask;
+ size_lb = size & ~write_mask;
+ size_mop = size & write_mask;
+
+ seq->data_size = TRANSFER_SIZE(size_ub);
+ seq->addr1 = (offset >> 16) & 0xffff;
+ seq->addr2 = offset & 0xffff;
+
+ /* Need to set FIFO to write mode, before writing data to FIFO (see
+ * GNBvb79594)
+ */
+ writel(0x00040000, fsm->base + SPI_FAST_SEQ_CFG);
+
+ /*
+ * Before writing data to the FIFO, apply a small delay to allow a
+ * potential change of FIFO direction to complete.
+ */
+ if (fsm->fifo_dir_delay == 0)
+ readl(fsm->base + SPI_FAST_SEQ_CFG);
+ else
+ udelay(fsm->fifo_dir_delay);
+
+
+ /* Write data to FIFO, before starting sequence (see GNBvd79593) */
+ if (size_lb) {
+ stfsm_write_fifo(fsm, (uint32_t *)p, size_lb);
+ p += size_lb;
+ }
+
+ /* Handle non-aligned size */
+ if (size_mop) {
+ memset(t, 0xff, write_mask + 1); /* fill with 0xff's */
+ for (i = 0; i < size_mop; i++)
+ t[i] = *p++;
+
+ stfsm_write_fifo(fsm, tmp, write_mask + 1);
+ }
+
+ /* Start sequence */
+ stfsm_load_seq(fsm, seq);
+
+ /* Wait for sequence to finish */
+ stfsm_wait_seq(fsm);
+
+ /* Wait for completion */
+ ret = stfsm_wait_busy(fsm);
+ if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS)
+ stfsm_s25fl_clear_status_reg(fsm);
+
+ /* Exit 32-bit address mode, if required */
+ if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) {
+ stfsm_enter_32bit_addr(fsm, 0);
+ if (fsm->configuration & CFG_WRITE_EX_32BIT_ADDR_DELAY)
+ udelay(1);
+ }
+
+ return 0;
+}
+
+/*
+ * Read an address range from the flash chip. The address range
+ * may be any size provided it is within the physical boundaries.
+ */
+static int stfsm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent);
+ uint32_t bytes;
+
+ dev_dbg(fsm->dev, "%s from 0x%08x, len %zd\n",
+ __func__, (u32)from, len);
+
+ mutex_lock(&fsm->lock);
+
+ while (len > 0) {
+ bytes = min_t(size_t, len, FLASH_PAGESIZE);
+
+ stfsm_read(fsm, buf, bytes, from);
+
+ buf += bytes;
+ from += bytes;
+ len -= bytes;
+
+ *retlen += bytes;
+ }
+
+ mutex_unlock(&fsm->lock);
+
+ return 0;
+}
+
+static int stfsm_erase_sector(struct stfsm *fsm, uint32_t offset)
+{
+ struct stfsm_seq *seq = &stfsm_seq_erase_sector;
+ int ret;
+
+ dev_dbg(fsm->dev, "erasing sector at 0x%08x\n", offset);
+
+ /* Enter 32-bit address mode, if required */
+ if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR)
+ stfsm_enter_32bit_addr(fsm, 1);
+
+ seq->addr1 = (offset >> 16) & 0xffff;
+ seq->addr2 = offset & 0xffff;
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_wait_seq(fsm);
+
+ /* Wait for completion */
+ ret = stfsm_wait_busy(fsm);
+ if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS)
+ stfsm_s25fl_clear_status_reg(fsm);
+
+ /* Exit 32-bit address mode, if required */
+ if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR)
+ stfsm_enter_32bit_addr(fsm, 0);
+
+ return ret;
+}
+
+static int stfsm_erase_chip(struct stfsm *fsm)
+{
+ const struct stfsm_seq *seq = &stfsm_seq_erase_chip;
+
+ dev_dbg(fsm->dev, "erasing chip\n");
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_wait_seq(fsm);
+
+ return stfsm_wait_busy(fsm);
+}
+
+/*
+ * Write an address range to the flash chip. Data must be written in
+ * FLASH_PAGESIZE chunks. The address range may be any size provided
+ * it is within the physical boundaries.
+ */
+static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent);
+
+ u32 page_offs;
+ u32 bytes;
+ uint8_t *b = (uint8_t *)buf;
+ int ret = 0;
+
+ dev_dbg(fsm->dev, "%s to 0x%08x, len %zd\n", __func__, (u32)to, len);
+
+ /* Offset within page */
+ page_offs = to % FLASH_PAGESIZE;
+
+ mutex_lock(&fsm->lock);
+
+ while (len) {
+ /* Write up to page boundary */
+ bytes = min(FLASH_PAGESIZE - page_offs, len);
+
+ ret = stfsm_write(fsm, b, bytes, to);
+ if (ret)
+ goto out1;
+
+ b += bytes;
+ len -= bytes;
+ to += bytes;
+
+ /* We are now page-aligned */
+ page_offs = 0;
+
+ *retlen += bytes;
+
+ }
+
+out1:
+ mutex_unlock(&fsm->lock);
+
+ return ret;
+}
+
+/*
+ * Erase an address range on the flash chip. The address range may extend
+ * one or more erase sectors. Return an error is there is a problem erasing.
+ */
+static int stfsm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent);
+ u32 addr, len;
+ int ret;
+
+ dev_dbg(fsm->dev, "%s at 0x%llx, len %lld\n", __func__,
+ (long long)instr->addr, (long long)instr->len);
+
+ addr = instr->addr;
+ len = instr->len;
+
+ mutex_lock(&fsm->lock);
+
+ /* Whole-chip erase? */
+ if (len == mtd->size) {
+ ret = stfsm_erase_chip(fsm);
+ if (ret)
+ goto out1;
+ } else {
+ while (len) {
+ ret = stfsm_erase_sector(fsm, addr);
+ if (ret)
+ goto out1;
+
+ addr += mtd->erasesize;
+ len -= mtd->erasesize;
+ }
+ }
+
+ mutex_unlock(&fsm->lock);
+
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+
+ return 0;
+
+out1:
+ instr->state = MTD_ERASE_FAILED;
+ mutex_unlock(&fsm->lock);
+
+ return ret;
+}
+
+static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *jedec)
+{
+ const struct stfsm_seq *seq = &stfsm_seq_read_jedec;
+ uint32_t tmp[2];
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_read_fifo(fsm, tmp, 8);
+
+ memcpy(jedec, tmp, 5);
+
+ stfsm_wait_seq(fsm);
+}
+
+static struct flash_info *stfsm_jedec_probe(struct stfsm *fsm)
+{
+ struct flash_info *info;
+ u16 ext_jedec;
+ u32 jedec;
+ u8 id[5];
+
+ stfsm_read_jedec(fsm, id);
+
+ jedec = id[0] << 16 | id[1] << 8 | id[2];
+ /*
+ * JEDEC also defines an optional "extended device information"
+ * string for after vendor-specific data, after the three bytes
+ * we use here. Supporting some chips might require using it.
+ */
+ ext_jedec = id[3] << 8 | id[4];
+
+ dev_dbg(fsm->dev, "JEDEC = 0x%08x [%02x %02x %02x %02x %02x]\n",
+ jedec, id[0], id[1], id[2], id[3], id[4]);
+
+ for (info = flash_types; info->name; info++) {
+ if (info->jedec_id == jedec) {
+ if (info->ext_id && info->ext_id != ext_jedec)
+ continue;
+ return info;
+ }
+ }
+ dev_err(fsm->dev, "Unrecognized JEDEC id %06x\n", jedec);
+
+ return NULL;
+}
+
+static int stfsm_set_mode(struct stfsm *fsm, uint32_t mode)
+{
+ int ret, timeout = 10;
+
+ /* Wait for controller to accept mode change */
+ while (--timeout) {
+ ret = readl(fsm->base + SPI_STA_MODE_CHANGE);
+ if (ret & 0x1)
+ break;
+ udelay(1);
+ }
+
+ if (!timeout)
+ return -EBUSY;
+
+ writel(mode, fsm->base + SPI_MODESELECT);
+
+ return 0;
+}
+
+static void stfsm_set_freq(struct stfsm *fsm, uint32_t spi_freq)
+{
+ uint32_t emi_freq;
+ uint32_t clk_div;
+
+ /* TODO: Make this dynamic */
+ emi_freq = STFSM_DEFAULT_EMI_FREQ;
+
+ /*
+ * Calculate clk_div - values between 2 and 128
+ * Multiple of 2, rounded up
+ */
+ clk_div = 2 * DIV_ROUND_UP(emi_freq, 2 * spi_freq);
+ if (clk_div < 2)
+ clk_div = 2;
+ else if (clk_div > 128)
+ clk_div = 128;
+
+ /*
+ * Determine a suitable delay for the IP to complete a change of
+ * direction of the FIFO. The required delay is related to the clock
+ * divider used. The following heuristics are based on empirical tests,
+ * using a 100MHz EMI clock.
+ */
+ if (clk_div <= 4)
+ fsm->fifo_dir_delay = 0;
+ else if (clk_div <= 10)
+ fsm->fifo_dir_delay = 1;
+ else
+ fsm->fifo_dir_delay = DIV_ROUND_UP(clk_div, 10);
+
+ dev_dbg(fsm->dev, "emi_clk = %uHZ, spi_freq = %uHZ, clk_div = %u\n",
+ emi_freq, spi_freq, clk_div);
+
+ writel(clk_div, fsm->base + SPI_CLOCKDIV);
+}
+
+static int stfsm_init(struct stfsm *fsm)
+{
+ int ret;
+
+ /* Perform a soft reset of the FSM controller */
+ writel(SEQ_CFG_SWRESET, fsm->base + SPI_FAST_SEQ_CFG);
+ udelay(1);
+ writel(0, fsm->base + SPI_FAST_SEQ_CFG);
+
+ /* Set clock to 'safe' frequency initially */
+ stfsm_set_freq(fsm, STFSM_FLASH_SAFE_FREQ);
+
+ /* Switch to FSM */
+ ret = stfsm_set_mode(fsm, SPI_MODESELECT_FSM);
+ if (ret)
+ return ret;
+
+ /* Set timing parameters */
+ writel(SPI_CFG_DEVICE_ST |
+ SPI_CFG_DEFAULT_MIN_CS_HIGH |
+ SPI_CFG_DEFAULT_CS_SETUPHOLD |
+ SPI_CFG_DEFAULT_DATA_HOLD,
+ fsm->base + SPI_CONFIGDATA);
+ writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG);
+
+ /* Clear FIFO, just in case */
+ stfsm_clear_fifo(fsm);
+
+ return 0;
+}
+
+static void stfsm_fetch_platform_configs(struct platform_device *pdev)
+{
+ struct stfsm *fsm = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
+ struct regmap *regmap;
+ uint32_t boot_device_reg;
+ uint32_t boot_device_spi;
+ uint32_t boot_device; /* Value we read from *boot_device_reg */
+ int ret;
+
+ /* Booting from SPI NOR Flash is the default */
+ fsm->booted_from_spi = true;
+
+ regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(regmap))
+ goto boot_device_fail;
+
+ fsm->reset_signal = of_property_read_bool(np, "st,reset-signal");
+
+ fsm->reset_por = of_property_read_bool(np, "st,reset-por");
+
+ /* Where in the syscon the boot device information lives */
+ ret = of_property_read_u32(np, "st,boot-device-reg", &boot_device_reg);
+ if (ret)
+ goto boot_device_fail;
+
+ /* Boot device value when booted from SPI NOR */
+ ret = of_property_read_u32(np, "st,boot-device-spi", &boot_device_spi);
+ if (ret)
+ goto boot_device_fail;
+
+ ret = regmap_read(regmap, boot_device_reg, &boot_device);
+ if (ret)
+ goto boot_device_fail;
+
+ if (boot_device != boot_device_spi)
+ fsm->booted_from_spi = false;
+
+ return;
+
+boot_device_fail:
+ dev_warn(&pdev->dev,
+ "failed to fetch boot device, assuming boot from SPI\n");
+}
+
+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;
+ int ret;
+
+ if (!np) {
+ 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)
+ return -ENOMEM;
+
+ fsm->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, fsm);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Resource not found\n");
+ return -ENODEV;
+ }
+
+ fsm->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsm->base)) {
+ dev_err(&pdev->dev,
+ "Failed to reserve memory region %pR\n", res);
+ return PTR_ERR(fsm->base);
+ }
+
+ mutex_init(&fsm->lock);
+
+ ret = stfsm_init(fsm);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to initialise FSM Controller\n");
+ return ret;
+ }
+
+ stfsm_fetch_platform_configs(pdev);
+
+ /* Detect SPI FLASH device */
+ info = stfsm_jedec_probe(fsm);
+ if (!info)
+ return -ENODEV;
+ fsm->info = info;
+
+ /* Use device size to determine address width */
+ if (info->sector_size * info->n_sectors > 0x1000000)
+ info->flags |= FLASH_FLAG_32BIT_ADDR;
+
+ /*
+ * Configure READ/WRITE/ERASE sequences according to platform and
+ * device flags.
+ */
+ if (info->config) {
+ ret = info->config(fsm);
+ if (ret)
+ return ret;
+ } else {
+ ret = stfsm_prepare_rwe_seqs_default(fsm);
+ if (ret)
+ return ret;
+ }
+
+ fsm->mtd.name = info->name;
+ fsm->mtd.dev.parent = &pdev->dev;
+ fsm->mtd.type = MTD_NORFLASH;
+ fsm->mtd.writesize = 4;
+ fsm->mtd.writebufsize = fsm->mtd.writesize;
+ fsm->mtd.flags = MTD_CAP_NORFLASH;
+ fsm->mtd.size = info->sector_size * info->n_sectors;
+ fsm->mtd.erasesize = info->sector_size;
+
+ fsm->mtd._read = stfsm_mtd_read;
+ fsm->mtd._write = stfsm_mtd_write;
+ fsm->mtd._erase = stfsm_mtd_erase;
+
+ dev_info(&pdev->dev,
+ "Found serial flash device: %s\n"
+ " size = %llx (%lldMiB) erasesize = 0x%08x (%uKiB)\n",
+ info->name,
+ (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);
+}
+
+static int stfsm_remove(struct platform_device *pdev)
+{
+ struct stfsm *fsm = platform_get_drvdata(pdev);
+
+ return mtd_device_unregister(&fsm->mtd);
+}
+
+static struct of_device_id stfsm_match[] = {
+ { .compatible = "st,spi-fsm", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stfsm_match);
+
+static struct platform_driver stfsm_driver = {
+ .probe = stfsm_probe,
+ .remove = stfsm_remove,
+ .driver = {
+ .name = "st-spi-fsm",
+ .owner = THIS_MODULE,
+ .of_match_table = stfsm_match,
+ },
+};
+module_platform_driver(stfsm_driver);
+
+MODULE_AUTHOR("Angus Clark <angus.clark@st.com>");
+MODULE_DESCRIPTION("ST SPI FSM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c
index 4adc0374fb6b..487e64f411a5 100644
--- a/drivers/mtd/inftlmount.c
+++ b/drivers/mtd/inftlmount.c
@@ -30,7 +30,6 @@
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nftl.h>
#include <linux/mtd/inftl.h>
diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c
index d38b6460d505..018c75faadb3 100644
--- a/drivers/mtd/lpddr/lpddr_cmds.c
+++ b/drivers/mtd/lpddr/lpddr_cmds.c
@@ -55,10 +55,8 @@ struct mtd_info *lpddr_cmdset(struct map_info *map)
int i, j;
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
- if (!mtd) {
- printk(KERN_ERR "Failed to allocate memory for MTD device\n");
+ if (!mtd)
return NULL;
- }
mtd->priv = map;
mtd->type = MTD_NORFLASH;
diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c
index 45abed67f1ef..69f2112340b1 100644
--- a/drivers/mtd/lpddr/qinfo_probe.c
+++ b/drivers/mtd/lpddr/qinfo_probe.c
@@ -135,11 +135,8 @@ static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr)
{
lpddr->qinfo = kzalloc(sizeof(struct qinfo_chip), GFP_KERNEL);
- if (!lpddr->qinfo) {
- printk(KERN_WARNING "%s: no memory for LPDDR qinfo structure\n",
- map->name);
+ if (!lpddr->qinfo)
return 0;
- }
/* Get the ManuID */
lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID));
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 310dc7c93425..fce23fe043f7 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -66,11 +66,11 @@ config MTD_PHYSMAP_BANKWIDTH
used internally by the CFI drivers.
config MTD_PHYSMAP_OF
- tristate "Flash device in physical memory map based on OF description"
- depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM)
+ tristate "Memory device in physical memory map based on OF description"
+ depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_RAM)
help
- This provides a 'mapping' driver which allows the NOR Flash and
- ROM driver code to communicate with chips which are mapped
+ This provides a 'mapping' driver which allows the NOR Flash, ROM
+ and RAM driver code to communicate with chips which are mapped
physically into the CPU's memory. The mapping description here is
taken from OF device tree.
@@ -124,7 +124,7 @@ config MTD_NETSC520
config MTD_TS5500
tristate "JEDEC Flash device mapped on Technologic Systems TS-5500"
- depends on X86
+ depends on TS5500 || COMPILE_TEST
select MTD_JEDECPROBE
select MTD_CFI_AMDSTD
help
diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c
index 5434d8ded015..6ea51e549045 100644
--- a/drivers/mtd/maps/bfin-async-flash.c
+++ b/drivers/mtd/maps/bfin-async-flash.c
@@ -14,7 +14,6 @@
* Licensed under the GPL-2 or later.
*/
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index 1adba86474a5..a4c477b9fdd6 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -14,7 +14,6 @@
*/
#include <linux/gpio.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c
index 46d195fca942..5ab71f0e1bcd 100644
--- a/drivers/mtd/maps/intel_vr_nor.c
+++ b/drivers/mtd/maps/intel_vr_nor.c
@@ -31,7 +31,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c
index d6b2451eab1d..6a589f1e2880 100644
--- a/drivers/mtd/maps/ixp4xx.c
+++ b/drivers/mtd/maps/ixp4xx.c
@@ -16,7 +16,6 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c
index 93c507a6f862..7aa682cd4d7e 100644
--- a/drivers/mtd/maps/lantiq-flash.c
+++ b/drivers/mtd/maps/lantiq-flash.c
@@ -13,7 +13,6 @@
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c
index 98bb5d5375d7..cadfbe051873 100644
--- a/drivers/mtd/maps/latch-addr-flash.c
+++ b/drivers/mtd/maps/latch-addr-flash.c
@@ -10,7 +10,6 @@
* kind, whether express or implied.
*/
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c
index 36da518915b5..eb0242e0b2d9 100644
--- a/drivers/mtd/maps/pci.c
+++ b/drivers/mtd/maps/pci.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index d11109762ac5..217c25d7381b 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -15,7 +15,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c
index 10196f5a897d..d597e89f2692 100644
--- a/drivers/mtd/maps/plat-ram.c
+++ b/drivers/mtd/maps/plat-ram.c
@@ -23,7 +23,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
@@ -138,7 +137,6 @@ static int platram_probe(struct platform_device *pdev)
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
- dev_err(&pdev->dev, "no memory for flash info\n");
err = -ENOMEM;
goto exit_error;
}
diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c
index 9aad854fe912..cb4d92eea9fe 100644
--- a/drivers/mtd/maps/pxa2xx-flash.c
+++ b/drivers/mtd/maps/pxa2xx-flash.c
@@ -13,7 +13,6 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c
index 93525121d69d..146b6047ed2b 100644
--- a/drivers/mtd/maps/rbtx4939-flash.c
+++ b/drivers/mtd/maps/rbtx4939-flash.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c
index 3051c4c36240..b7a22a612a46 100644
--- a/drivers/mtd/maps/scb2_flash.c
+++ b/drivers/mtd/maps/scb2_flash.c
@@ -47,7 +47,6 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c
index 39cc4181f025..b6f1aac3510c 100644
--- a/drivers/mtd/maps/sun_uflash.c
+++ b/drivers/mtd/maps/sun_uflash.c
@@ -11,7 +11,6 @@
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
-#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_device.h>
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 5073cbc796d8..0b2ccb68c0d0 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -30,7 +30,6 @@
#include <linux/blkpg.h>
#include <linux/spinlock.h>
#include <linux/hdreg.h>
-#include <linux/init.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 2147e733533b..7d4e7b9da3a1 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -324,6 +324,15 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c
default:
ret = mtd_write(mtd, *ppos, len, &retlen, kbuf);
}
+
+ /*
+ * Return -ENOSPC only if no data could be written at all.
+ * Otherwise just return the number of bytes that actually
+ * have been written.
+ */
+ if ((ret == -ENOSPC) && (total_retlen))
+ break;
+
if (!ret) {
*ppos += retlen;
total_retlen += retlen;
@@ -889,25 +898,26 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
case OTPGETREGIONINFO:
{
struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
+ size_t retlen;
if (!buf)
return -ENOMEM;
switch (mfi->mode) {
case MTD_FILE_MODE_OTP_FACTORY:
- ret = mtd_get_fact_prot_info(mtd, buf, 4096);
+ ret = mtd_get_fact_prot_info(mtd, 4096, &retlen, buf);
break;
case MTD_FILE_MODE_OTP_USER:
- ret = mtd_get_user_prot_info(mtd, buf, 4096);
+ ret = mtd_get_user_prot_info(mtd, 4096, &retlen, buf);
break;
default:
ret = -EINVAL;
break;
}
- if (ret >= 0) {
+ if (!ret) {
if (cmd == OTPGETREGIONCOUNT) {
- int nbr = ret / sizeof(struct otp_info);
+ int nbr = retlen / sizeof(struct otp_info);
ret = copy_to_user(argp, &nbr, sizeof(int));
} else
- ret = copy_to_user(argp, buf, ret);
+ ret = copy_to_user(argp, buf, retlen);
if (ret)
ret = -EFAULT;
}
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 34c0b16aed5c..d201feeb3ca6 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -883,14 +883,14 @@ EXPORT_SYMBOL_GPL(mtd_read_oob);
* devices. The user data is one time programmable but the factory data is read
* only.
*/
-int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
- size_t len)
+int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+ struct otp_info *buf)
{
if (!mtd->_get_fact_prot_info)
return -EOPNOTSUPP;
if (!len)
return 0;
- return mtd->_get_fact_prot_info(mtd, buf, len);
+ return mtd->_get_fact_prot_info(mtd, len, retlen, buf);
}
EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info);
@@ -906,14 +906,14 @@ int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
}
EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg);
-int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
- size_t len)
+int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+ struct otp_info *buf)
{
if (!mtd->_get_user_prot_info)
return -EOPNOTSUPP;
if (!len)
return 0;
- return mtd->_get_user_prot_info(mtd, buf, len);
+ return mtd->_get_user_prot_info(mtd, len, retlen, buf);
}
EXPORT_SYMBOL_GPL(mtd_get_user_prot_info);
@@ -932,12 +932,22 @@ EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg);
int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, u_char *buf)
{
+ int ret;
+
*retlen = 0;
if (!mtd->_write_user_prot_reg)
return -EOPNOTSUPP;
if (!len)
return 0;
- return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
+ ret = mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
+ if (ret)
+ return ret;
+
+ /*
+ * If no data could be written at all, we are out of memory and
+ * must return -ENOSPC.
+ */
+ return (*retlen) ? 0 : -ENOSPC;
}
EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 3c7d6d7623c1..1ca9aec141ff 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -150,11 +150,12 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
retlen, buf);
}
-static int part_get_user_prot_info(struct mtd_info *mtd,
- struct otp_info *buf, size_t len)
+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);
- return part->master->_get_user_prot_info(part->master, buf, len);
+ return part->master->_get_user_prot_info(part->master, len, retlen,
+ buf);
}
static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
@@ -165,11 +166,12 @@ static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
retlen, buf);
}
-static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
- size_t len)
+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);
- return part->master->_get_fact_prot_info(part->master, buf, len);
+ return part->master->_get_fact_prot_info(part->master, len, retlen,
+ buf);
}
static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index a4bee41ad5cb..f1cf503517fd 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -460,6 +460,8 @@ 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 HAS_IOMEM
+ depends on HAS_DMA
help
Several Renesas SuperH CPU has FLCTL. This option enables support
for NAND Flash using FLCTL.
diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c
index 8611eb4b45fc..4936e9e0002f 100644
--- a/drivers/mtd/nand/ams-delta.c
+++ b/drivers/mtd/nand/ams-delta.c
@@ -17,7 +17,6 @@
*/
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index c36e9b84487c..4ce181a35bcd 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -430,7 +430,7 @@ err_dma:
dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
err_buf:
if (err != 0)
- dev_warn(host->dev, "Fall back to CPU I/O\n");
+ dev_dbg(host->dev, "Fall back to CPU I/O\n");
return err;
}
@@ -1220,6 +1220,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
goto err;
}
+ nand_chip->options |= NAND_NO_SUBPAGE_WRITE;
nand_chip->ecc.read_page = atmel_nand_pmecc_read_page;
nand_chip->ecc.write_page = atmel_nand_pmecc_write_page;
@@ -1659,8 +1660,8 @@ static void nfc_select_chip(struct mtd_info *mtd, int chip)
nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_ENABLE);
}
-static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr,
- unsigned int *addr1234, unsigned int *cycle0)
+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;
@@ -1674,7 +1675,8 @@ static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr,
*addr1234 = 0;
if (column != -1) {
- if (chip->options & NAND_BUSWIDTH_16)
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
addr_bytes[acycle++] = column & 0xff;
if (mtd->writesize > 512)
@@ -1787,8 +1789,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
}
if (do_addr)
- acycle = nfc_make_addr(mtd, column, page_addr, &addr1234,
- &cycle0);
+ acycle = nfc_make_addr(mtd, command, column, page_addr,
+ &addr1234, &cycle0);
nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr;
nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index 2880d888cfc5..bc5c518828d2 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -11,7 +11,6 @@
#include <linux/slab.h>
#include <linux/gpio.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
@@ -308,7 +307,8 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
- if (this->options & NAND_BUSWIDTH_16)
+ if (this->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
ctx->write_byte(mtd, column);
}
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
index 94f55dbde995..b7a24946ca26 100644
--- a/drivers/mtd/nand/bf5xx_nand.c
+++ b/drivers/mtd/nand/bf5xx_nand.c
@@ -37,7 +37,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
index f2f64addb5e8..4e66726da9aa 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -627,6 +627,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
struct cafe_priv *cafe;
uint32_t ctrl;
int err = 0;
+ int old_dma;
+ struct nand_buffers *nbuf;
/* Very old versions shared the same PCI ident for all three
functions on the chip. Verify the class too... */
@@ -655,13 +657,6 @@ static int cafe_nand_probe(struct pci_dev *pdev,
err = -ENOMEM;
goto out_free_mtd;
}
- cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers),
- &cafe->dmaaddr, GFP_KERNEL);
- if (!cafe->dmabuf) {
- err = -ENOMEM;
- goto out_ior;
- }
- cafe->nand.buffers = (void *)cafe->dmabuf + 2112;
cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
if (!cafe->rs) {
@@ -721,7 +716,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
"CAFE NAND", mtd);
if (err) {
dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
- goto out_free_dma;
+ goto out_ior;
}
/* Disable master reset, enable NAND clock */
@@ -735,6 +730,32 @@ static int cafe_nand_probe(struct pci_dev *pdev,
cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
+ /* Enable NAND IRQ in global IRQ mask register */
+ cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
+ cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
+ cafe_readl(cafe, GLOBAL_CTRL),
+ cafe_readl(cafe, GLOBAL_IRQ_MASK));
+
+ /* Do not use the DMA for the nand_scan_ident() */
+ old_dma = usedma;
+ usedma = 0;
+
+ /* Scan to find existence of the device */
+ if (nand_scan_ident(mtd, 2, NULL)) {
+ err = -ENXIO;
+ goto out_irq;
+ }
+
+ cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev,
+ 2112 + sizeof(struct nand_buffers) +
+ mtd->writesize + mtd->oobsize,
+ &cafe->dmaaddr, GFP_KERNEL);
+ if (!cafe->dmabuf) {
+ err = -ENOMEM;
+ goto out_irq;
+ }
+ cafe->nand.buffers = nbuf = (void *)cafe->dmabuf + 2112;
+
/* Set up DMA address */
cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
if (sizeof(cafe->dmaaddr) > 4)
@@ -746,16 +767,13 @@ static int cafe_nand_probe(struct pci_dev *pdev,
cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n",
cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf);
- /* Enable NAND IRQ in global IRQ mask register */
- cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
- cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
- cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
+ /* this driver does not need the @ecccalc and @ecccode */
+ nbuf->ecccalc = NULL;
+ nbuf->ecccode = NULL;
+ nbuf->databuf = (uint8_t *)(nbuf + 1);
- /* Scan to find existence of the device */
- if (nand_scan_ident(mtd, 2, NULL)) {
- err = -ENXIO;
- goto out_irq;
- }
+ /* Restore the DMA flag */
+ usedma = old_dma;
cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */
if (mtd->writesize == 2048)
@@ -773,7 +791,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
} else {
printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n",
mtd->writesize);
- goto out_irq;
+ goto out_free_dma;
}
cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
cafe->nand.ecc.size = mtd->writesize;
@@ -790,7 +808,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
err = nand_scan_tail(mtd);
if (err)
- goto out_irq;
+ goto out_free_dma;
pci_set_drvdata(pdev, mtd);
@@ -799,12 +817,15 @@ static int cafe_nand_probe(struct pci_dev *pdev,
goto out;
+ out_free_dma:
+ dma_free_coherent(&cafe->pdev->dev,
+ 2112 + sizeof(struct nand_buffers) +
+ mtd->writesize + mtd->oobsize,
+ cafe->dmabuf, cafe->dmaaddr);
out_irq:
/* Disable NAND IRQ in global IRQ mask register */
cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
free_irq(pdev->irq, mtd);
- out_free_dma:
- dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
out_ior:
pci_iounmap(pdev, cafe->mmio);
out_free_mtd:
@@ -824,7 +845,10 @@ static void cafe_nand_remove(struct pci_dev *pdev)
nand_release(mtd);
free_rs(cafe->rs);
pci_iounmap(pdev, cafe->mmio);
- dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
+ dma_free_coherent(&cafe->pdev->dev,
+ 2112 + sizeof(struct nand_buffers) +
+ mtd->writesize + mtd->oobsize,
+ cafe->dmabuf, cafe->dmaaddr);
kfree(mtd);
}
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index a4989ec6292e..4615d79fc93f 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -24,7 +24,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
@@ -746,28 +745,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
goto err_clk_enable;
}
- /*
- * Setup Async configuration register in case we did not boot from
- * NAND and so bootloader did not bother to set it up.
- */
- val = davinci_nand_readl(info, A1CR_OFFSET + info->core_chipsel * 4);
-
- /* Extended Wait is not valid and Select Strobe mode is not used */
- val &= ~(ACR_ASIZE_MASK | ACR_EW_MASK | ACR_SS_MASK);
- if (info->chip.options & NAND_BUSWIDTH_16)
- val |= 0x1;
-
- davinci_nand_writel(info, A1CR_OFFSET + info->core_chipsel * 4, val);
-
- ret = 0;
- if (info->timing)
- ret = davinci_aemif_setup_timing(info->timing, info->base,
- info->core_chipsel);
- if (ret < 0) {
- dev_dbg(&pdev->dev, "NAND timing values setup fail\n");
- goto err;
- }
-
spin_lock_irq(&davinci_nand_lock);
/* put CSxNAND into NAND mode */
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index babb02c4b220..35cb17f57800 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -30,24 +30,6 @@ struct denali_dt {
struct clk *clk;
};
-static void __iomem *request_and_map(struct device *dev,
- const struct resource *res)
-{
- void __iomem *ptr;
-
- if (!devm_request_mem_region(dev, res->start, resource_size(res),
- "denali-dt")) {
- dev_err(dev, "unable to request %s\n", res->name);
- return NULL;
- }
-
- ptr = devm_ioremap_nocache(dev, res->start, resource_size(res));
- if (!ptr)
- dev_err(dev, "ioremap_nocache of %s failed!", res->name);
-
- return ptr;
-}
-
static const struct of_device_id denali_nand_dt_ids[] = {
{ .compatible = "denali,denali-nand-dt" },
{ /* sentinel */ }
@@ -78,13 +60,6 @@ static int denali_dt_probe(struct platform_device *ofdev)
return -ENOMEM;
denali = &dt->denali;
- denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg");
- nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data");
- if (!denali_reg || !nand_data) {
- dev_err(&ofdev->dev, "resources not completely defined\n");
- return -EINVAL;
- }
-
denali->platform = DT;
denali->dev = &ofdev->dev;
denali->irq = platform_get_irq(ofdev, 0);
@@ -93,13 +68,15 @@ static int denali_dt_probe(struct platform_device *ofdev)
return denali->irq;
}
- denali->flash_reg = request_and_map(&ofdev->dev, denali_reg);
- if (!denali->flash_reg)
- return -ENOMEM;
+ denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg");
+ denali->flash_reg = devm_ioremap_resource(&ofdev->dev, denali_reg);
+ if (IS_ERR(denali->flash_reg))
+ return PTR_ERR(denali->flash_reg);
- denali->flash_mem = request_and_map(&ofdev->dev, nand_data);
- if (!denali->flash_mem)
- return -ENOMEM;
+ nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data");
+ denali->flash_mem = devm_ioremap_resource(&ofdev->dev, nand_data);
+ if (IS_ERR(denali->flash_mem))
+ return PTR_ERR(denali->flash_mem);
if (!of_property_read_u32(ofdev->dev.of_node,
"dma-mask", (u32 *)&denali_dma_mask)) {
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index fec31d71b84e..f68a7bccecdc 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -698,7 +698,8 @@ static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int colu
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
- if (this->options & NAND_BUSWIDTH_16)
+ if (this->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
WriteDOC(column, docptr, Mplus_FlashAddress);
}
@@ -1438,7 +1439,7 @@ static int __init doc_probe(unsigned long physadr)
int reg, len, numchips;
int ret = 0;
- if (!request_mem_region(physadr, DOC_IOREMAP_LEN, NULL))
+ if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip"))
return -EBUSY;
virtadr = ioremap(physadr, DOC_IOREMAP_LEN);
if (!virtadr) {
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index bcf60800c3ce..ec549cd9849f 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -24,7 +24,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index 50d9161c4faf..cb45d2f8e208 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -22,7 +22,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/slab.h>
diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c
index 8e6148aa4539..117ce333fdd4 100644
--- a/drivers/mtd/nand/gpio.c
+++ b/drivers/mtd/nand/gpio.c
@@ -18,7 +18,6 @@
#include <linux/kernel.h>
#include <linux/err.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index ca6369fe91ff..bb77f750e75a 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -27,6 +27,7 @@
#include <linux/of_device.h>
#include <linux/of_mtd.h>
#include "gpmi-nand.h"
+#include "bch-regs.h"
/* Resource names for the GPMI NAND driver. */
#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand"
@@ -985,7 +986,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
int ret;
dev_dbg(this->dev, "page number is : %d\n", page);
- ret = read_page_prepare(this, buf, mtd->writesize,
+ ret = read_page_prepare(this, buf, nfc_geo->payload_size,
this->payload_virt, this->payload_phys,
nfc_geo->payload_size,
&payload_virt, &payload_phys);
@@ -999,7 +1000,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
/* go! */
ret = gpmi_read_page(this, payload_phys, auxiliary_phys);
- read_page_end(this, buf, mtd->writesize,
+ read_page_end(this, buf, nfc_geo->payload_size,
this->payload_virt, this->payload_phys,
nfc_geo->payload_size,
payload_virt, payload_phys);
@@ -1041,7 +1042,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
}
- read_page_swap_end(this, buf, mtd->writesize,
+ read_page_swap_end(this, buf, nfc_geo->payload_size,
this->payload_virt, this->payload_phys,
nfc_geo->payload_size,
payload_virt, payload_phys);
@@ -1049,6 +1050,90 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
return max_bitflips;
}
+/* Fake a virtual small page for the subpage read */
+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;
+ void __iomem *bch_regs = this->resources.bch_regs;
+ struct bch_geometry old_geo = this->bch_geometry;
+ struct bch_geometry *geo = &this->bch_geometry;
+ int size = chip->ecc.size; /* ECC chunk size */
+ int meta, n, page_size;
+ u32 r1_old, r2_old, r1_new, r2_new;
+ unsigned int max_bitflips;
+ int first, last, marker_pos;
+ int ecc_parity_size;
+ int col = 0;
+
+ /* The size of ECC parity */
+ ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
+
+ /* Align it with the chunk size */
+ first = offs / size;
+ last = (offs + len - 1) / size;
+
+ /*
+ * Find the chunk which contains the Block Marker. If this chunk is
+ * in the range of [first, last], we have to read out the whole page.
+ * Why? since we had swapped the data at the position of Block Marker
+ * to the metadata which is bound with the chunk 0.
+ */
+ marker_pos = geo->block_mark_byte_offset / size;
+ if (last >= marker_pos && first <= marker_pos) {
+ dev_dbg(this->dev, "page:%d, first:%d, last:%d, marker at:%d\n",
+ page, first, last, marker_pos);
+ return gpmi_ecc_read_page(mtd, chip, buf, 0, page);
+ }
+
+ meta = geo->metadata_size;
+ if (first) {
+ col = meta + (size + ecc_parity_size) * first;
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
+
+ meta = 0;
+ buf = buf + first * size;
+ }
+
+ /* Save the old environment */
+ r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0);
+ r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ /* change the BCH registers and bch_geometry{} */
+ n = last - first + 1;
+ page_size = meta + (size + ecc_parity_size) * n;
+
+ r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS |
+ BM_BCH_FLASH0LAYOUT0_META_SIZE);
+ r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1)
+ | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta);
+ writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+ r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE;
+ r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size);
+ writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ geo->ecc_chunk_count = n;
+ geo->payload_size = n * size;
+ geo->page_size = page_size;
+ geo->auxiliary_status_offset = ALIGN(meta, 4);
+
+ dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n",
+ page, offs, len, col, first, n, page_size);
+
+ /* Read the subpage now */
+ this->swap_block_mark = false;
+ max_bitflips = gpmi_ecc_read_page(mtd, chip, buf, 0, page);
+
+ /* Restore */
+ writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0);
+ writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1);
+ this->bch_geometry = old_geo;
+ this->swap_block_mark = true;
+
+ return max_bitflips;
+}
+
static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
@@ -1566,6 +1651,17 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
ecc->layout = &gpmi_hw_ecclayout;
/*
+ * We only enable the subpage read when:
+ * (1) the chip is imx6, and
+ * (2) the size of the ECC parity is byte aligned.
+ */
+ if (GPMI_IS_MX6Q(this) &&
+ ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
+ ecc->read_subpage = gpmi_ecc_read_subpage;
+ chip->options |= NAND_SUBPAGE_READ;
+ }
+
+ /*
* Can we enable the extra features? such as EDO or Sync mode.
*
* We do not check the return value now. That's means if we fail in
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index 31ee7cfbc12b..e78841a2dcc3 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -30,7 +30,6 @@
#include <linux/gfp.h>
#include <linux/delay.h>
#include <linux/err.h>
-#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index e9a4835c4dd9..dba262bf766f 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -1501,6 +1501,8 @@ static int mxcnd_probe(struct platform_device *pdev)
init_completion(&host->op_completion);
host->irq = platform_get_irq(pdev, 0);
+ if (host->irq < 0)
+ return host->irq;
/*
* Use host->devtype_data->irq_control() here instead of irq_control()
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 9715a7ba164a..9d01c4df838c 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -589,7 +589,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
- if (chip->options & NAND_BUSWIDTH_16)
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
chip->cmd_ctrl(mtd, column, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
@@ -680,7 +681,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
- if (chip->options & NAND_BUSWIDTH_16)
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
chip->cmd_ctrl(mtd, column, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
@@ -1160,9 +1162,11 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
* @data_offs: offset of requested data within the page
* @readlen: data length
* @bufpoi: buffer to store read data
+ * @page: page number to read
*/
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
+ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
+ int page)
{
int start_step, end_step, num_steps;
uint32_t *eccpos = chip->ecc.layout->eccpos;
@@ -1170,13 +1174,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
int data_col_addr, i, gaps = 0;
int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
- int index = 0;
+ int index;
unsigned int max_bitflips = 0;
/* Column address within the page aligned to ECC size (256bytes) */
start_step = data_offs / chip->ecc.size;
end_step = (data_offs + readlen - 1) / chip->ecc.size;
num_steps = end_step - start_step + 1;
+ index = start_step * chip->ecc.bytes;
/* Data size aligned to ECC ecc.size */
datafrag_len = num_steps * chip->ecc.size;
@@ -1213,8 +1218,6 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
* Send the command to read the particular ECC bytes take care
* about buswidth alignment in read_buf.
*/
- index = start_step * chip->ecc.bytes;
-
aligned_pos = eccpos[index] & ~(busw - 1);
aligned_len = eccfrag_len;
if (eccpos[index] & (busw - 1))
@@ -1538,7 +1541,8 @@ read_retry:
else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
!oob)
ret = chip->ecc.read_subpage(mtd, chip,
- col, bytes, bufpoi);
+ col, bytes, bufpoi,
+ page);
else
ret = chip->ecc.read_page(mtd, chip, bufpoi,
oob_required, page);
@@ -2000,7 +2004,7 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
oob += chip->ecc.prepad;
}
- chip->read_buf(mtd, oob, eccbytes);
+ chip->write_buf(mtd, oob, eccbytes);
oob += eccbytes;
if (chip->ecc.postpad) {
@@ -3063,7 +3067,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
int *busw)
{
struct nand_onfi_params *p = &chip->onfi_params;
- int i;
+ int i, j;
int val;
/* Try ONFI for unknown chip or LP */
@@ -3072,18 +3076,10 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
return 0;
- /*
- * ONFI must be probed in 8-bit mode or with NAND_BUSWIDTH_AUTO, not
- * with NAND_BUSWIDTH_16
- */
- if (chip->options & NAND_BUSWIDTH_16) {
- pr_err("ONFI cannot be probed in 16-bit mode; aborting\n");
- return 0;
- }
-
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
for (i = 0; i < 3; i++) {
- chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+ for (j = 0; j < sizeof(*p); j++)
+ ((uint8_t *)p)[j] = chip->read_byte(mtd);
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
le16_to_cpu(p->crc)) {
break;
@@ -3169,6 +3165,87 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
}
/*
+ * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
+ */
+static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip,
+ int *busw)
+{
+ struct nand_jedec_params *p = &chip->jedec_params;
+ struct jedec_ecc_info *ecc;
+ int val;
+ int i, j;
+
+ /* Try JEDEC for unknown chip or LP */
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1);
+ if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' ||
+ chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' ||
+ chip->read_byte(mtd) != 'C')
+ return 0;
+
+ chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1);
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < sizeof(*p); j++)
+ ((uint8_t *)p)[j] = chip->read_byte(mtd);
+
+ if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) ==
+ le16_to_cpu(p->crc))
+ break;
+ }
+
+ if (i == 3) {
+ pr_err("Could not find valid JEDEC parameter page; aborting\n");
+ return 0;
+ }
+
+ /* Check version */
+ val = le16_to_cpu(p->revision);
+ if (val & (1 << 2))
+ chip->jedec_version = 10;
+ else if (val & (1 << 1))
+ chip->jedec_version = 1; /* vendor specific version */
+
+ if (!chip->jedec_version) {
+ pr_info("unsupported JEDEC version: %d\n", val);
+ return 0;
+ }
+
+ sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+ sanitize_string(p->model, sizeof(p->model));
+ if (!mtd->name)
+ mtd->name = p->model;
+
+ mtd->writesize = le32_to_cpu(p->byte_per_page);
+
+ /* Please reference to the comment for nand_flash_detect_onfi. */
+ mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
+ mtd->erasesize *= mtd->writesize;
+
+ mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+
+ /* Please reference to the comment for nand_flash_detect_onfi. */
+ chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
+ chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
+ chip->bits_per_cell = p->bits_per_cell;
+
+ if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS)
+ *busw = NAND_BUSWIDTH_16;
+ else
+ *busw = 0;
+
+ /* ECC info */
+ ecc = &p->ecc_info[0];
+
+ if (ecc->codeword_size >= 9) {
+ chip->ecc_strength_ds = ecc->ecc_bits;
+ chip->ecc_step_ds = 1 << ecc->codeword_size;
+ } else {
+ pr_warn("Invalid codeword size\n");
+ }
+
+ return 1;
+}
+
+/*
* nand_id_has_period - Check if an ID string has a given wraparound period
* @id_data: the ID string
* @arrlen: the length of the @id_data array
@@ -3474,10 +3551,10 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
*/
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
- int busw,
int *maf_id, int *dev_id,
struct nand_flash_dev *type)
{
+ int busw;
int i, maf_idx;
u8 id_data[8];
@@ -3533,6 +3610,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
/* Check is chip is ONFI compliant */
if (nand_flash_detect_onfi(mtd, chip, &busw))
goto ident_done;
+
+ /* Check if the chip is JEDEC compliant */
+ if (nand_flash_detect_jedec(mtd, chip, &busw))
+ goto ident_done;
}
if (!type->name)
@@ -3612,8 +3693,17 @@ ident_done:
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
*maf_id, *dev_id);
- pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
- chip->onfi_version ? chip->onfi_params.model : type->name);
+
+ if (chip->onfi_version)
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ chip->onfi_params.model);
+ else if (chip->jedec_version)
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ chip->jedec_params.model);
+ else
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ type->name);
+
pr_info("%dMiB, %s, page size: %d, OOB size: %d\n",
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
mtd->writesize, mtd->oobsize);
@@ -3634,18 +3724,16 @@ ident_done:
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
struct nand_flash_dev *table)
{
- int i, busw, nand_maf_id, nand_dev_id;
+ int i, nand_maf_id, nand_dev_id;
struct nand_chip *chip = mtd->priv;
struct nand_flash_dev *type;
- /* Get buswidth to select the correct functions */
- busw = chip->options & NAND_BUSWIDTH_16;
/* Set the default functions */
- nand_set_defaults(chip, busw);
+ nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
/* Read the flash type */
- type = nand_get_flash_type(mtd, chip, busw,
- &nand_maf_id, &nand_dev_id, table);
+ type = nand_get_flash_type(mtd, chip, &nand_maf_id,
+ &nand_dev_id, table);
if (IS_ERR(type)) {
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
@@ -3696,15 +3784,26 @@ int nand_scan_tail(struct mtd_info *mtd)
int i;
struct nand_chip *chip = mtd->priv;
struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct nand_buffers *nbuf;
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
!(chip->bbt_options & NAND_BBT_USE_FLASH));
- if (!(chip->options & NAND_OWN_BUFFERS))
- chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
- if (!chip->buffers)
- return -ENOMEM;
+ if (!(chip->options & NAND_OWN_BUFFERS)) {
+ nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
+ + mtd->oobsize * 3, GFP_KERNEL);
+ if (!nbuf)
+ return -ENOMEM;
+ nbuf->ecccalc = (uint8_t *)(nbuf + 1);
+ nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
+ nbuf->databuf = nbuf->ecccode + mtd->oobsize;
+
+ chip->buffers = nbuf;
+ } else {
+ if (!chip->buffers)
+ return -ENOMEM;
+ }
/* Set the internal oob buffer location, just after the page data */
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
@@ -3825,7 +3924,7 @@ int nand_scan_tail(struct mtd_info *mtd)
case NAND_ECC_SOFT_BCH:
if (!mtd_nand_has_bch()) {
- pr_warn("CONFIG_MTD_ECC_BCH not enabled\n");
+ pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n");
BUG();
}
ecc->calculate = nand_bch_calculate_ecc;
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index daa2faacd7d0..3d7c89fc1031 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -43,6 +43,9 @@ struct nand_flash_dev nand_flash_ids[] = {
{"TC58NVG6D2 64G 3.3V 8-bit",
{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
+ {"SDTNRGAMA 64G 3.3V 8-bit",
+ { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} },
+ SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c
index 9ee09a8177c6..e8a5fffd6ab2 100644
--- a/drivers/mtd/nand/nuc900_nand.c
+++ b/drivers/mtd/nand/nuc900_nand.c
@@ -10,7 +10,6 @@
*/
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -152,7 +151,8 @@ static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command,
if (column != -1 || page_addr != -1) {
if (column != -1) {
- if (chip->options & NAND_BUSWIDTH_16)
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
write_addr_reg(nand, column);
write_addr_reg(nand, column >> 8 | ENDADDR);
@@ -225,7 +225,7 @@ static void nuc900_nand_enable(struct nuc900_nand *nand)
val = __raw_readl(nand->reg + REG_FMICSR);
if (!(val & NAND_EN))
- __raw_writel(val | NAND_EN, REG_FMICSR);
+ __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR);
val = __raw_readl(nand->reg + REG_SMCSR);
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index bf642ceef681..1ff49b80bdaf 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -118,14 +118,9 @@
#define OMAP24XX_DMA_GPMC 4
-#define BCH8_MAX_ERROR 8 /* upto 8 bit correctable */
-#define BCH4_MAX_ERROR 4 /* upto 4 bit correctable */
-
#define SECTOR_BYTES 512
/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
#define BCH4_BIT_PAD 4
-#define BCH8_ECC_MAX ((SECTOR_BYTES + BCH8_ECC_OOB_BYTES) * 8)
-#define BCH4_ECC_MAX ((SECTOR_BYTES + BCH4_ECC_OOB_BYTES) * 8)
/* GPMC ecc engine settings for read */
#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */
@@ -159,7 +154,7 @@ struct omap_nand_info {
int gpmc_cs;
unsigned long phys_base;
- unsigned long mem_size;
+ enum omap_ecc ecc_opt;
struct completion comp;
struct dma_chan *dma;
int gpmc_irq_fifo;
@@ -172,7 +167,6 @@ struct omap_nand_info {
int buf_len;
struct gpmc_nand_regs reg;
/* fields specific for BCHx_HW ECC scheme */
- bool is_elm_used;
struct device *elm_dev;
struct device_node *of_node;
};
@@ -1043,9 +1037,8 @@ static int omap_dev_ready(struct mtd_info *mtd)
}
}
-#if defined(CONFIG_MTD_NAND_ECC_BCH) || defined(CONFIG_MTD_NAND_OMAP_BCH)
/**
- * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction
+ * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
* @mtd: MTD device structure
* @mode: Read/Write mode
*
@@ -1056,50 +1049,73 @@ static int omap_dev_ready(struct mtd_info *mtd)
* eccsize0 = 0 (no additional protected byte in spare area)
* eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
*/
-static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
+static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
{
- int nerrors;
+ unsigned int bch_type;
unsigned int dev_width, nsectors;
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
+ enum omap_ecc ecc_opt = info->ecc_opt;
struct nand_chip *chip = mtd->priv;
u32 val, wr_mode;
unsigned int ecc_size1, ecc_size0;
- /* Using wrapping mode 6 for writing */
- wr_mode = BCH_WRAPMODE_6;
-
- /*
- * ECC engine enabled for valid ecc_size0 nibbles
- * and disabled for ecc_size1 nibbles.
- */
- ecc_size0 = BCH_ECC_SIZE0;
- ecc_size1 = BCH_ECC_SIZE1;
-
- /* Perform ecc calculation on 512-byte sector */
- nsectors = 1;
-
- /* Update number of error correction */
- nerrors = info->nand.ecc.strength;
-
- /* Multi sector reading/writing for NAND flash with page size < 4096 */
- if (info->is_elm_used && (mtd->writesize <= 4096)) {
+ /* GPMC configurations for calculating ECC */
+ switch (ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ bch_type = 0;
+ nsectors = 1;
+ if (mode == NAND_ECC_READ) {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ } else {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ }
+ break;
+ case OMAP_ECC_BCH4_CODE_HW:
+ bch_type = 0;
+ nsectors = chip->ecc.steps;
if (mode == NAND_ECC_READ) {
- /* Using wrapping mode 1 for reading */
- wr_mode = BCH_WRAPMODE_1;
-
- /*
- * ECC engine enabled for ecc_size0 nibbles
- * and disabled for ecc_size1 nibbles.
- */
- ecc_size0 = (nerrors == 8) ?
- BCH8R_ECC_SIZE0 : BCH4R_ECC_SIZE0;
- ecc_size1 = (nerrors == 8) ?
- BCH8R_ECC_SIZE1 : BCH4R_ECC_SIZE1;
+ wr_mode = BCH_WRAPMODE_1;
+ ecc_size0 = BCH4R_ECC_SIZE0;
+ ecc_size1 = BCH4R_ECC_SIZE1;
+ } else {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
}
-
- /* Perform ecc calculation for one page (< 4096) */
- nsectors = info->nand.ecc.steps;
+ break;
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ bch_type = 1;
+ nsectors = 1;
+ if (mode == NAND_ECC_READ) {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ } else {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ }
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ bch_type = 1;
+ nsectors = chip->ecc.steps;
+ if (mode == NAND_ECC_READ) {
+ wr_mode = BCH_WRAPMODE_1;
+ ecc_size0 = BCH8R_ECC_SIZE0;
+ ecc_size1 = BCH8R_ECC_SIZE1;
+ } else {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ }
+ break;
+ default:
+ return;
}
writel(ECC1, info->reg.gpmc_ecc_control);
@@ -1112,7 +1128,7 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
/* BCH configuration */
val = ((1 << 16) | /* enable BCH */
- (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
+ (bch_type << 12) | /* BCH4/BCH8/BCH16 */
(wr_mode << 8) | /* wrap mode */
(dev_width << 7) | /* bus width */
(((nsectors-1) & 0x7) << 4) | /* number of sectors */
@@ -1124,132 +1140,40 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
/* Clear ecc and enable bits */
writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
}
-#endif
-
-#ifdef CONFIG_MTD_NAND_ECC_BCH
-/**
- * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- */
-static int omap3_calculate_ecc_bch4(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);
- unsigned long nsectors, val1, val2;
- int i;
-
- nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-
- for (i = 0; i < nsectors; i++) {
- /* Read hw-computed remainder */
- val1 = readl(info->reg.gpmc_bch_result0[i]);
- val2 = readl(info->reg.gpmc_bch_result1[i]);
-
- /*
- * Add constant polynomial to remainder, in order to get an ecc
- * sequence of 0xFFs for a buffer filled with 0xFFs; and
- * left-justify the resulting polynomial.
- */
- *ecc_code++ = 0x28 ^ ((val2 >> 12) & 0xFF);
- *ecc_code++ = 0x13 ^ ((val2 >> 4) & 0xFF);
- *ecc_code++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF));
- *ecc_code++ = 0x39 ^ ((val1 >> 20) & 0xFF);
- *ecc_code++ = 0x96 ^ ((val1 >> 12) & 0xFF);
- *ecc_code++ = 0xac ^ ((val1 >> 4) & 0xFF);
- *ecc_code++ = 0x7f ^ ((val1 & 0xF) << 4);
- }
-
- return 0;
-}
+static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f};
+static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
+ 0x97, 0x79, 0xe5, 0x24, 0xb5};
/**
- * omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- */
-static int omap3_calculate_ecc_bch8(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);
- unsigned long nsectors, val1, val2, val3, val4;
- int i;
-
- nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-
- for (i = 0; i < nsectors; i++) {
-
- /* Read hw-computed remainder */
- val1 = readl(info->reg.gpmc_bch_result0[i]);
- val2 = readl(info->reg.gpmc_bch_result1[i]);
- val3 = readl(info->reg.gpmc_bch_result2[i]);
- val4 = readl(info->reg.gpmc_bch_result3[i]);
-
- /*
- * Add constant polynomial to remainder, in order to get an ecc
- * sequence of 0xFFs for a buffer filled with 0xFFs.
- */
- *ecc_code++ = 0xef ^ (val4 & 0xFF);
- *ecc_code++ = 0x51 ^ ((val3 >> 24) & 0xFF);
- *ecc_code++ = 0x2e ^ ((val3 >> 16) & 0xFF);
- *ecc_code++ = 0x09 ^ ((val3 >> 8) & 0xFF);
- *ecc_code++ = 0xed ^ (val3 & 0xFF);
- *ecc_code++ = 0x93 ^ ((val2 >> 24) & 0xFF);
- *ecc_code++ = 0x9a ^ ((val2 >> 16) & 0xFF);
- *ecc_code++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
- *ecc_code++ = 0x97 ^ (val2 & 0xFF);
- *ecc_code++ = 0x79 ^ ((val1 >> 24) & 0xFF);
- *ecc_code++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
- *ecc_code++ = 0x24 ^ ((val1 >> 8) & 0xFF);
- *ecc_code++ = 0xb5 ^ (val1 & 0xFF);
- }
-
- return 0;
-}
-#endif /* CONFIG_MTD_NAND_ECC_BCH */
-
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
-/**
- * omap3_calculate_ecc_bch - Generate bytes of ECC bytes
+ * omap_calculate_ecc_bch - Generate bytes of ECC bytes
* @mtd: MTD device structure
* @dat: The pointer to data on which ecc is computed
* @ecc_code: The ecc_code buffer
*
* Support calculating of BCH4/8 ecc vectors for the page
*/
-static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
+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);
+ int eccbytes = info->nand.ecc.bytes;
+ struct gpmc_nand_regs *gpmc_regs = &info->reg;
+ u8 *ecc_code;
unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
- int i, eccbchtsel;
+ int i;
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
- /*
- * find BCH scheme used
- * 0 -> BCH4
- * 1 -> BCH8
- */
- eccbchtsel = ((readl(info->reg.gpmc_ecc_config) >> 12) & 0x3);
-
for (i = 0; i < nsectors; i++) {
-
- /* Read hw-computed remainder */
- bch_val1 = readl(info->reg.gpmc_bch_result0[i]);
- bch_val2 = readl(info->reg.gpmc_bch_result1[i]);
- if (eccbchtsel) {
- bch_val3 = readl(info->reg.gpmc_bch_result2[i]);
- bch_val4 = readl(info->reg.gpmc_bch_result3[i]);
- }
-
- if (eccbchtsel) {
- /* BCH8 ecc scheme */
+ ecc_code = ecc_calc;
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ case OMAP_ECC_BCH8_CODE_HW:
+ bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
+ bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
+ bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]);
+ bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]);
*ecc_code++ = (bch_val4 & 0xFF);
*ecc_code++ = ((bch_val3 >> 24) & 0xFF);
*ecc_code++ = ((bch_val3 >> 16) & 0xFF);
@@ -1263,14 +1187,11 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
*ecc_code++ = ((bch_val1 >> 16) & 0xFF);
*ecc_code++ = ((bch_val1 >> 8) & 0xFF);
*ecc_code++ = (bch_val1 & 0xFF);
- /*
- * Setting 14th byte to zero to handle
- * erased page & maintain compatibility
- * with RBL
- */
- *ecc_code++ = 0x0;
- } else {
- /* BCH4 ecc scheme */
+ break;
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ case OMAP_ECC_BCH4_CODE_HW:
+ bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
+ bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
*ecc_code++ = ((bch_val2 >> 12) & 0xFF);
*ecc_code++ = ((bch_val2 >> 4) & 0xFF);
*ecc_code++ = ((bch_val2 & 0xF) << 4) |
@@ -1279,12 +1200,38 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
*ecc_code++ = ((bch_val1 >> 12) & 0xFF);
*ecc_code++ = ((bch_val1 >> 4) & 0xFF);
*ecc_code++ = ((bch_val1 & 0xF) << 4);
- /*
- * Setting 8th byte to zero to handle
- * erased page
- */
- *ecc_code++ = 0x0;
+ break;
+ default:
+ return -EINVAL;
}
+
+ /* ECC scheme specific syndrome customizations */
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ /* Add constant polynomial to remainder, so that
+ * ECC of blank pages results in 0x0 on reading back */
+ for (i = 0; i < eccbytes; i++)
+ ecc_calc[i] ^= bch4_polynomial[i];
+ break;
+ case OMAP_ECC_BCH4_CODE_HW:
+ /* Set 8th ECC byte as 0x0 for ROM compatibility */
+ ecc_calc[eccbytes - 1] = 0x0;
+ break;
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ /* Add constant polynomial to remainder, so that
+ * ECC of blank pages results in 0x0 on reading back */
+ for (i = 0; i < eccbytes; i++)
+ ecc_calc[i] ^= bch8_polynomial[i];
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ /* Set 14th ECC byte as 0x0 for ROM compatibility */
+ ecc_calc[eccbytes - 1] = 0x0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ecc_calc += eccbytes;
}
return 0;
@@ -1329,6 +1276,7 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
return flip_bits;
}
+#ifdef CONFIG_MTD_NAND_OMAP_BCH
/**
* omap_elm_correct_data - corrects page data area in case error reported
* @mtd: MTD device structure
@@ -1337,55 +1285,46 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
* @calc_ecc: ecc read from HW ECC registers
*
* Calculated ecc vector reported as zero in case of non-error pages.
- * In case of error/erased pages non-zero error vector is reported.
- * In case of non-zero ecc vector, check read_ecc at fixed offset
- * (x = 13/7 in case of BCH8/4 == 0) to find page programmed or not.
- * To handle bit flips in this data, count the number of 0's in
- * read_ecc[x] and check if it greater than 4. If it is less, it is
- * programmed page, else erased page.
- *
- * 1. If page is erased, check with standard ecc vector (ecc vector
- * for erased page to find any bit flip). If check fails, bit flip
- * is present in erased page. Count the bit flips in erased page and
- * if it falls under correctable level, report page with 0xFF and
- * update the correctable bit information.
- * 2. If error is reported on programmed page, update elm error
- * vector and correct the page with ELM error correction routine.
- *
+ * In case of non-zero ecc vector, first filter out erased-pages, and
+ * then process data via ELM to detect bit-flips.
*/
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 nand_ecc_ctrl *ecc = &info->nand.ecc;
int eccsteps = info->nand.ecc.steps;
int i , j, stat = 0;
- int eccsize, eccflag, ecc_vector_size;
+ int eccflag, actual_eccbytes;
struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
u_char *ecc_vec = calc_ecc;
u_char *spare_ecc = read_ecc;
u_char *erased_ecc_vec;
- enum bch_ecc type;
+ u_char *buf;
+ int bitflip_count;
bool is_error_reported = false;
+ u32 bit_pos, byte_pos, error_max, pos;
+ int err;
- /* Initialize elm error vector to zero */
- memset(err_vec, 0, sizeof(err_vec));
-
- if (info->nand.ecc.strength == BCH8_MAX_ERROR) {
- type = BCH8_ECC;
- erased_ecc_vec = bch8_vector;
- } else {
- type = BCH4_ECC;
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW:
+ /* omit 7th ECC byte reserved for ROM code compatibility */
+ actual_eccbytes = ecc->bytes - 1;
erased_ecc_vec = bch4_vector;
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ /* omit 14th ECC byte reserved for ROM code compatibility */
+ actual_eccbytes = ecc->bytes - 1;
+ erased_ecc_vec = bch8_vector;
+ break;
+ default:
+ pr_err("invalid driver configuration\n");
+ return -EINVAL;
}
- ecc_vector_size = info->nand.ecc.bytes;
-
- /*
- * Remove extra byte padding for BCH8 RBL
- * compatibility and erased page handling
- */
- eccsize = ecc_vector_size - 1;
+ /* Initialize elm error vector to zero */
+ memset(err_vec, 0, sizeof(err_vec));
for (i = 0; i < eccsteps ; i++) {
eccflag = 0; /* initialize eccflag */
@@ -1394,8 +1333,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
* Check any error reported,
* In case of error, non zero ecc reported.
*/
-
- for (j = 0; (j < eccsize); j++) {
+ for (j = 0; j < actual_eccbytes; j++) {
if (calc_ecc[j] != 0) {
eccflag = 1; /* non zero ecc, error present */
break;
@@ -1403,50 +1341,43 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
}
if (eccflag == 1) {
- /*
- * Set threshold to minimum of 4, half of ecc.strength/2
- * to allow max bit flip in byte to 4
- */
- unsigned int threshold = min_t(unsigned int, 4,
- info->nand.ecc.strength / 2);
-
- /*
- * Check data area is programmed by counting
- * number of 0's at fixed offset in spare area.
- * Checking count of 0's against threshold.
- * In case programmed page expects at least threshold
- * zeros in byte.
- * If zeros are less than threshold for programmed page/
- * zeros are more than threshold erased page, either
- * case page reported as uncorrectable.
- */
- if (hweight8(~read_ecc[eccsize]) >= threshold) {
+ if (memcmp(calc_ecc, erased_ecc_vec,
+ actual_eccbytes) == 0) {
/*
- * Update elm error vector as
- * data area is programmed
+ * calc_ecc[] matches pattern for ECC(all 0xff)
+ * so this is definitely an erased-page
*/
- err_vec[i].error_reported = true;
- is_error_reported = true;
} else {
- /* Error reported in erased page */
- int bitflip_count;
- u_char *buf = &data[info->nand.ecc.size * i];
-
- if (memcmp(calc_ecc, erased_ecc_vec, eccsize)) {
- bitflip_count = erased_sector_bitflips(
- buf, read_ecc, info);
-
- if (bitflip_count)
- stat += bitflip_count;
- else
- return -EINVAL;
+ buf = &data[info->nand.ecc.size * i];
+ /*
+ * count number of 0-bits in read_buf.
+ * This check can be removed once a similar
+ * check is introduced in generic NAND driver
+ */
+ bitflip_count = erased_sector_bitflips(
+ buf, read_ecc, info);
+ if (bitflip_count) {
+ /*
+ * number of 0-bits within ECC limits
+ * So this may be an erased-page
+ */
+ stat += bitflip_count;
+ } else {
+ /*
+ * Too many 0-bits. It may be a
+ * - programmed-page, OR
+ * - erased-page with many bit-flips
+ * So this page requires check by ELM
+ */
+ err_vec[i].error_reported = true;
+ is_error_reported = true;
}
}
}
/* Update the ecc vector */
- calc_ecc += ecc_vector_size;
- read_ecc += ecc_vector_size;
+ calc_ecc += ecc->bytes;
+ read_ecc += ecc->bytes;
}
/* Check if any error reported */
@@ -1456,23 +1387,26 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
/* Decode BCH error using ELM module */
elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
+ err = 0;
for (i = 0; i < eccsteps; i++) {
- if (err_vec[i].error_reported) {
+ if (err_vec[i].error_uncorrectable) {
+ pr_err("nand: uncorrectable bit-flips found\n");
+ err = -EBADMSG;
+ } else if (err_vec[i].error_reported) {
for (j = 0; j < err_vec[i].error_count; j++) {
- u32 bit_pos, byte_pos, error_max, pos;
-
- if (type == BCH8_ECC)
- error_max = BCH8_ECC_MAX;
- else
- error_max = BCH4_ECC_MAX;
-
- if (info->nand.ecc.strength == BCH8_MAX_ERROR)
- pos = err_vec[i].error_loc[j];
- else
- /* Add 4 to take care 4 bit padding */
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW:
+ /* Add 4 bits to take care of padding */
pos = err_vec[i].error_loc[j] +
BCH4_BIT_PAD;
-
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ pos = err_vec[i].error_loc[j];
+ break;
+ default:
+ return -EINVAL;
+ }
+ error_max = (ecc->size + actual_eccbytes) * 8;
/* Calculate bit position of error */
bit_pos = pos % 8;
@@ -1480,13 +1414,22 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
byte_pos = (error_max - pos - 1) / 8;
if (pos < error_max) {
- if (byte_pos < 512)
+ if (byte_pos < 512) {
+ pr_debug("bitflip@dat[%d]=%x\n",
+ byte_pos, data[byte_pos]);
data[byte_pos] ^= 1 << bit_pos;
- else
+ } else {
+ pr_debug("bitflip@oob[%d]=%x\n",
+ (byte_pos - 512),
+ spare_ecc[byte_pos - 512]);
spare_ecc[byte_pos - 512] ^=
1 << bit_pos;
+ }
+ } else {
+ pr_err("invalid bit-flip @ %d:%d\n",
+ byte_pos, bit_pos);
+ err = -EBADMSG;
}
- /* else, not interested to correct ecc */
}
}
@@ -1494,16 +1437,11 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
stat += err_vec[i].error_count;
/* Update page data with sector size */
- data += info->nand.ecc.size;
- spare_ecc += ecc_vector_size;
+ data += ecc->size;
+ spare_ecc += ecc->bytes;
}
- for (i = 0; i < eccsteps; i++)
- /* Return error if uncorrectable error present */
- if (err_vec[i].error_uncorrectable)
- return -EINVAL;
-
- return stat;
+ return (err) ? err : stat;
}
/**
@@ -1601,7 +1539,8 @@ static int is_elm_present(struct omap_nand_info *info,
struct device_node *elm_node, enum bch_ecc bch_type)
{
struct platform_device *pdev;
- info->is_elm_used = false;
+ struct nand_ecc_ctrl *ecc = &info->nand.ecc;
+ int err;
/* check whether elm-id is passed via DT */
if (!elm_node) {
pr_err("nand: error: ELM DT node not found\n");
@@ -1615,10 +1554,10 @@ static int is_elm_present(struct omap_nand_info *info,
}
/* ELM module available, now configure it */
info->elm_dev = &pdev->dev;
- if (elm_config(info->elm_dev, bch_type))
- return -ENODEV;
- info->is_elm_used = true;
- return 0;
+ err = elm_config(info->elm_dev, bch_type,
+ (info->mtd.writesize / ecc->size), ecc->size, ecc->bytes);
+
+ return err;
}
#endif /* CONFIG_MTD_NAND_ECC_BCH */
@@ -1657,6 +1596,7 @@ static int omap_nand_probe(struct platform_device *pdev)
info->gpmc_cs = pdata->cs;
info->reg = pdata->reg;
info->of_node = pdata->of_node;
+ info->ecc_opt = pdata->ecc_opt;
mtd = &info->mtd;
mtd->priv = &info->nand;
mtd->name = dev_name(&pdev->dev);
@@ -1666,27 +1606,11 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_SKIP_BBTSCAN;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- err = -EINVAL;
- dev_err(&pdev->dev, "error getting memory resource\n");
- goto return_error;
- }
+ nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nand_chip->IO_ADDR_R))
+ return PTR_ERR(nand_chip->IO_ADDR_R);
info->phys_base = res->start;
- info->mem_size = resource_size(res);
-
- if (!devm_request_mem_region(&pdev->dev, info->phys_base,
- info->mem_size, pdev->dev.driver->name)) {
- err = -EBUSY;
- goto return_error;
- }
-
- nand_chip->IO_ADDR_R = devm_ioremap(&pdev->dev, info->phys_base,
- info->mem_size);
- if (!nand_chip->IO_ADDR_R) {
- err = -ENOMEM;
- goto return_error;
- }
nand_chip->controller = &info->controller;
@@ -1812,7 +1736,7 @@ static int omap_nand_probe(struct platform_device *pdev)
/* populate MTD interface based on ECC scheme */
nand_chip->ecc.layout = &omap_oobinfo;
ecclayout = &omap_oobinfo;
- switch (pdata->ecc_opt) {
+ switch (info->ecc_opt) {
case OMAP_ECC_HAM1_CODE_HW:
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
nand_chip->ecc.mode = NAND_ECC_HW;
@@ -1844,9 +1768,9 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.size = 512;
nand_chip->ecc.bytes = 7;
nand_chip->ecc.strength = 4;
- nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data;
- nand_chip->ecc.calculate = omap3_calculate_ecc_bch4;
+ nand_chip->ecc.calculate = omap_calculate_ecc_bch;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
@@ -1884,9 +1808,9 @@ static int omap_nand_probe(struct platform_device *pdev)
/* 14th bit is kept reserved for ROM-code compatibility */
nand_chip->ecc.bytes = 7 + 1;
nand_chip->ecc.strength = 4;
- nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = omap_elm_correct_data;
- nand_chip->ecc.calculate = omap3_calculate_ecc_bch;
+ nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch;
/* define ECC layout */
@@ -1919,9 +1843,9 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.size = 512;
nand_chip->ecc.bytes = 13;
nand_chip->ecc.strength = 8;
- nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data;
- nand_chip->ecc.calculate = omap3_calculate_ecc_bch8;
+ nand_chip->ecc.calculate = omap_calculate_ecc_bch;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
@@ -1960,9 +1884,9 @@ static int omap_nand_probe(struct platform_device *pdev)
/* 14th bit is kept reserved for ROM-code compatibility */
nand_chip->ecc.bytes = 13 + 1;
nand_chip->ecc.strength = 8;
- nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = omap_elm_correct_data;
- nand_chip->ecc.calculate = omap3_calculate_ecc_bch;
+ nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch;
/* This ECC scheme requires ELM H/W block */
diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c
index 90f871acb0ef..2c98f9da7471 100644
--- a/drivers/mtd/nand/pasemi_nand.c
+++ b/drivers/mtd/nand/pasemi_nand.c
@@ -23,7 +23,6 @@
#undef DEBUG
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 2a7a0b27ac38..7588fe2c127f 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -38,7 +38,6 @@
#include <linux/platform_data/mtd-nand-pxa3xx.h>
-#define NAND_DEV_READY_TIMEOUT 50
#define CHIP_DELAY_TIMEOUT (2 * HZ/10)
#define NAND_STOP_DELAY (2 * HZ/50)
#define PAGE_CHUNK_SIZE (2048)
@@ -1531,7 +1530,7 @@ KEEP_CONFIG:
if (!ret) {
dev_err(&info->pdev->dev,
"ECC strength %d at page size %d is not supported\n",
- chip->ecc_strength_ds, mtd->writesize);
+ ecc_strength, mtd->writesize);
return -ENODEV;
}
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index f0918e7411d9..79acbb8691b5 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -29,7 +29,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/io.h>
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c
index 8e1919b6f074..093c29ac1a13 100644
--- a/drivers/mtd/onenand/generic.c
+++ b/drivers/mtd/onenand/generic.c
@@ -13,7 +13,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index 6547c84afc3a..d945473c3882 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -25,7 +25,6 @@
#include <linux/device.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 1de33b5d3903..635ee0027691 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -24,7 +24,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -3238,20 +3237,17 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
/**
* onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info
* @param mtd MTD device structure
- * @param buf the databuffer to put/get data
* @param len number of bytes to read
+ * @param retlen pointer to variable to store the number of read bytes
+ * @param buf the databuffer to put/get data
*
* Read factory OTP info.
*/
-static int onenand_get_fact_prot_info(struct mtd_info *mtd,
- struct otp_info *buf, size_t len)
+static int onenand_get_fact_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
{
- size_t retlen;
- int ret;
-
- ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_FACTORY);
-
- return ret ? : retlen;
+ return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
+ MTD_OTP_FACTORY);
}
/**
@@ -3273,20 +3269,17 @@ static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
/**
* onenand_get_user_prot_info - [MTD Interface] Read user OTP info
* @param mtd MTD device structure
- * @param buf the databuffer to put/get data
+ * @param retlen pointer to variable to store the number of read bytes
* @param len number of bytes to read
+ * @param buf the databuffer to put/get data
*
* Read user OTP info.
*/
-static int onenand_get_user_prot_info(struct mtd_info *mtd,
- struct otp_info *buf, size_t len)
+static int onenand_get_user_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
{
- size_t retlen;
- int ret;
-
- ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_USER);
-
- return ret ? : retlen;
+ return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
+ MTD_OTP_USER);
}
/**
@@ -3995,11 +3988,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
/* Allocate buffers, if necessary */
if (!this->page_buf) {
this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
- if (!this->page_buf) {
- printk(KERN_ERR "%s: Can't allocate page_buf\n",
- __func__);
+ if (!this->page_buf)
return -ENOMEM;
- }
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL);
if (!this->verify_buf) {
@@ -4012,8 +4002,6 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
if (!this->oob_buf) {
this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
if (!this->oob_buf) {
- printk(KERN_ERR "%s: Can't allocate oob_buf\n",
- __func__);
if (this->options & ONENAND_PAGEBUF_ALLOC) {
this->options &= ~ONENAND_PAGEBUF_ALLOC;
kfree(this->page_buf);
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c
index df7400dd4df8..b1a792fd1c23 100644
--- a/drivers/mtd/onenand/samsung.c
+++ b/drivers/mtd/onenand/samsung.c
@@ -872,10 +872,8 @@ static int s3c_onenand_probe(struct platform_device *pdev)
size = sizeof(struct mtd_info) + sizeof(struct onenand_chip);
mtd = kzalloc(size, GFP_KERNEL);
- if (!mtd) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
+ if (!mtd)
return -ENOMEM;
- }
onenand = kzalloc(sizeof(struct s3c_onenand), GFP_KERNEL);
if (!onenand) {
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c
index 233b946e5d66..d1cbf26db2c0 100644
--- a/drivers/mtd/rfd_ftl.c
+++ b/drivers/mtd/rfd_ftl.c
@@ -602,8 +602,7 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr)
if (rc) {
printk(KERN_ERR PREFIX "error writing '%s' at "
"0x%lx\n", part->mbd.mtd->name, addr);
- if (rc)
- goto err;
+ goto err;
}
if (block == part->current_block)
part->header_cache[offset + HEADER_MAP_OFFSET] = del;
@@ -675,8 +674,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
if (rc) {
printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
part->mbd.mtd->name, addr);
- if (rc)
- goto err;
+ goto err;
}
part->sector_map[sector] = addr;
@@ -695,8 +693,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
if (rc) {
printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
part->mbd.mtd->name, addr);
- if (rc)
- goto err;
+ goto err;
}
block->used_sectors++;
block->free_sectors--;
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index 4b8e89583f2a..cf49c22673b9 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -59,15 +59,12 @@ static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
struct attribute_group *attr_group;
struct attribute **attributes;
struct sm_sysfs_attribute *vendor_attribute;
+ char *vendor;
- int vendor_len = strnlen(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET,
- SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET);
-
- char *vendor = kmalloc(vendor_len, GFP_KERNEL);
+ vendor = kstrndup(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET,
+ SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET, GFP_KERNEL);
if (!vendor)
goto error1;
- memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len);
- vendor[vendor_len] = 0;
/* Initialize sysfs attributes */
vendor_attribute =
@@ -78,7 +75,7 @@ static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
sysfs_attr_init(&vendor_attribute->dev_attr.attr);
vendor_attribute->data = vendor;
- vendor_attribute->len = vendor_len;
+ vendor_attribute->len = strlen(vendor);
vendor_attribute->dev_attr.attr.name = "vendor";
vendor_attribute->dev_attr.attr.mode = S_IRUGO;
vendor_attribute->dev_attr.show = sm_attr_show;
diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c
index c818a63532e7..111ee46a7428 100644
--- a/drivers/mtd/tests/mtd_test.c
+++ b/drivers/mtd/tests/mtd_test.c
@@ -1,6 +1,5 @@
#define pr_fmt(fmt) "mtd_test: " fmt
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/printk.h>
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 36663af56d89..f0855ce08ed9 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -87,4 +87,20 @@ config MTD_UBI_GLUEBI
work on top of UBI. Do not enable this unless you use legacy
software.
+config MTD_UBI_BLOCK
+ bool "Read-only block devices on top of UBI volumes"
+ default n
+ depends on BLOCK
+ help
+ This option enables read-only UBI block devices support. UBI block
+ devices will be layered on top of UBI volumes, which means that the
+ UBI driver will transparently handle things like bad eraseblocks and
+ bit-flips. You can put any block-oriented file system on top of UBI
+ volumes in read-only mode (e.g., ext4), but it is probably most
+ practical for read-only file systems, like squashfs.
+
+ When selected, this feature will be built in the UBI driver.
+
+ If in doubt, say "N".
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index b46b0c978581..4e3c3d70d8c3 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -3,5 +3,6 @@ obj-$(CONFIG_MTD_UBI) += ubi.o
ubi-y += vtbl.o vmt.o upd.o build.o cdev.o kapi.o eba.o io.o wl.o attach.o
ubi-y += misc.o debug.o
ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap.o
+ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c
new file mode 100644
index 000000000000..7ff473c871a9
--- /dev/null
+++ b/drivers/mtd/ubi/block.c
@@ -0,0 +1,647 @@
+/*
+ * Copyright (c) 2014 Ezequiel Garcia
+ * Copyright (c) 2011 Free Electrons
+ *
+ * Driver parameter handling strongly based on drivers/mtd/ubi/build.c
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2007
+ * Authors: Artem Bityutskiy, Frank Haverkamp
+ *
+ * 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 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.
+ */
+
+/*
+ * Read-only block devices on top of UBI volumes
+ *
+ * A simple implementation to allow a block device to be layered on top of a
+ * UBI volume. The implementation is provided by creating a static 1-to-1
+ * mapping between the block device and the UBI volume.
+ *
+ * The addressed byte is obtained from the addressed block sector, which is
+ * mapped linearly into the corresponding LEB:
+ *
+ * LEB number = addressed byte / LEB size
+ *
+ * This feature is compiled in the UBI core, and adds a 'block' parameter
+ * to allow early creation of block devices on top of UBI volumes. Runtime
+ * block creation/removal for UBI volumes is provided through two UBI ioctls:
+ * UBI_IOCVOLCRBLK and UBI_IOCVOLRMBLK.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/workqueue.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/div64.h>
+
+#include "ubi-media.h"
+#include "ubi.h"
+
+/* Maximum number of supported devices */
+#define UBIBLOCK_MAX_DEVICES 32
+
+/* Maximum length of the 'block=' parameter */
+#define UBIBLOCK_PARAM_LEN 63
+
+/* Maximum number of comma-separated items in the 'block=' parameter */
+#define UBIBLOCK_PARAM_COUNT 2
+
+struct ubiblock_param {
+ int ubi_num;
+ int vol_id;
+ char name[UBIBLOCK_PARAM_LEN+1];
+};
+
+/* Numbers of elements set in the @ubiblock_param array */
+static int ubiblock_devs __initdata;
+
+/* MTD devices specification parameters */
+static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata;
+
+struct ubiblock {
+ struct ubi_volume_desc *desc;
+ int ubi_num;
+ int vol_id;
+ int refcnt;
+ int leb_size;
+
+ struct gendisk *gd;
+ struct request_queue *rq;
+
+ struct workqueue_struct *wq;
+ struct work_struct work;
+
+ struct mutex dev_mutex;
+ spinlock_t queue_lock;
+ struct list_head list;
+};
+
+/* Linked list of all ubiblock instances */
+static LIST_HEAD(ubiblock_devices);
+static DEFINE_MUTEX(devices_mutex);
+static int ubiblock_major;
+
+static int __init ubiblock_set_param(const char *val,
+ const struct kernel_param *kp)
+{
+ int i, ret;
+ size_t len;
+ struct ubiblock_param *param;
+ char buf[UBIBLOCK_PARAM_LEN];
+ char *pbuf = &buf[0];
+ char *tokens[UBIBLOCK_PARAM_COUNT];
+
+ if (!val)
+ return -EINVAL;
+
+ len = strnlen(val, UBIBLOCK_PARAM_LEN);
+ if (len == 0) {
+ ubi_warn("block: empty 'block=' parameter - ignored\n");
+ return 0;
+ }
+
+ if (len == UBIBLOCK_PARAM_LEN) {
+ ubi_err("block: parameter \"%s\" is too long, max. is %d\n",
+ val, UBIBLOCK_PARAM_LEN);
+ return -EINVAL;
+ }
+
+ strcpy(buf, val);
+
+ /* Get rid of the final newline */
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ for (i = 0; i < UBIBLOCK_PARAM_COUNT; i++)
+ tokens[i] = strsep(&pbuf, ",");
+
+ param = &ubiblock_param[ubiblock_devs];
+ if (tokens[1]) {
+ /* Two parameters: can be 'ubi, vol_id' or 'ubi, vol_name' */
+ ret = kstrtoint(tokens[0], 10, &param->ubi_num);
+ if (ret < 0)
+ return -EINVAL;
+
+ /* Second param can be a number or a name */
+ ret = kstrtoint(tokens[1], 10, &param->vol_id);
+ if (ret < 0) {
+ param->vol_id = -1;
+ strcpy(param->name, tokens[1]);
+ }
+
+ } else {
+ /* One parameter: must be device path */
+ strcpy(param->name, tokens[0]);
+ param->ubi_num = -1;
+ param->vol_id = -1;
+ }
+
+ ubiblock_devs++;
+
+ return 0;
+}
+
+static struct kernel_param_ops ubiblock_param_ops = {
+ .set = ubiblock_set_param,
+};
+module_param_cb(block, &ubiblock_param_ops, NULL, 0);
+MODULE_PARM_DESC(block, "Attach block devices to UBI volumes. Parameter format: block=<path|dev,num|dev,name>.\n"
+ "Multiple \"block\" parameters may be specified.\n"
+ "UBI volumes may be specified by their number, name, or path to the device node.\n"
+ "Examples\n"
+ "Using the UBI volume path:\n"
+ "ubi.block=/dev/ubi0_0\n"
+ "Using the UBI device, and the volume name:\n"
+ "ubi.block=0,rootfs\n"
+ "Using both UBI device number and UBI volume number:\n"
+ "ubi.block=0,0\n");
+
+static struct ubiblock *find_dev_nolock(int ubi_num, int vol_id)
+{
+ struct ubiblock *dev;
+
+ list_for_each_entry(dev, &ubiblock_devices, list)
+ if (dev->ubi_num == ubi_num && dev->vol_id == vol_id)
+ return dev;
+ return NULL;
+}
+
+static int ubiblock_read_to_buf(struct ubiblock *dev, char *buffer,
+ int leb, int offset, int len)
+{
+ int ret;
+
+ ret = ubi_read(dev->desc, leb, buffer, offset, len);
+ if (ret) {
+ ubi_err("%s ubi_read error %d",
+ dev->gd->disk_name, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int ubiblock_read(struct ubiblock *dev, char *buffer,
+ sector_t sec, int len)
+{
+ int ret, leb, offset;
+ int bytes_left = len;
+ int to_read = len;
+ u64 pos = sec << 9;
+
+ /* Get LEB:offset address to read from */
+ offset = do_div(pos, dev->leb_size);
+ leb = pos;
+
+ while (bytes_left) {
+ /*
+ * We can only read one LEB at a time. Therefore if the read
+ * length is larger than one LEB size, we split the operation.
+ */
+ if (offset + to_read > dev->leb_size)
+ to_read = dev->leb_size - offset;
+
+ ret = ubiblock_read_to_buf(dev, buffer, leb, offset, to_read);
+ if (ret)
+ return ret;
+
+ buffer += to_read;
+ bytes_left -= to_read;
+ to_read = bytes_left;
+ leb += 1;
+ offset = 0;
+ }
+ return 0;
+}
+
+static int do_ubiblock_request(struct ubiblock *dev, struct request *req)
+{
+ int len, ret;
+ sector_t sec;
+
+ if (req->cmd_type != REQ_TYPE_FS)
+ return -EIO;
+
+ if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))
+ return -EIO;
+
+ if (rq_data_dir(req) != READ)
+ return -ENOSYS; /* Write not implemented */
+
+ sec = blk_rq_pos(req);
+ len = blk_rq_cur_bytes(req);
+
+ /*
+ * Let's prevent the device from being removed while we're doing I/O
+ * work. Notice that this means we serialize all the I/O operations,
+ * but it's probably of no impact given the NAND core serializes
+ * flash access anyway.
+ */
+ mutex_lock(&dev->dev_mutex);
+ ret = ubiblock_read(dev, req->buffer, sec, len);
+ mutex_unlock(&dev->dev_mutex);
+
+ return ret;
+}
+
+static void ubiblock_do_work(struct work_struct *work)
+{
+ struct ubiblock *dev =
+ container_of(work, struct ubiblock, work);
+ struct request_queue *rq = dev->rq;
+ struct request *req;
+ int res;
+
+ spin_lock_irq(rq->queue_lock);
+
+ req = blk_fetch_request(rq);
+ while (req) {
+
+ spin_unlock_irq(rq->queue_lock);
+ res = do_ubiblock_request(dev, req);
+ spin_lock_irq(rq->queue_lock);
+
+ /*
+ * If we're done with this request,
+ * we need to fetch a new one
+ */
+ if (!__blk_end_request_cur(req, res))
+ req = blk_fetch_request(rq);
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+}
+
+static void ubiblock_request(struct request_queue *rq)
+{
+ struct ubiblock *dev;
+ struct request *req;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ queue_work(dev->wq, &dev->work);
+}
+
+static int ubiblock_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblock *dev = bdev->bd_disk->private_data;
+ int ret;
+
+ mutex_lock(&dev->dev_mutex);
+ if (dev->refcnt > 0) {
+ /*
+ * The volume is already open, just increase the reference
+ * counter.
+ */
+ goto out_done;
+ }
+
+ /*
+ * We want users to be aware they should only mount us as read-only.
+ * It's just a paranoid check, as write requests will get rejected
+ * in any case.
+ */
+ if (mode & FMODE_WRITE) {
+ ret = -EPERM;
+ goto out_unlock;
+ }
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id, UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ ubi_err("%s failed to open ubi volume %d_%d",
+ dev->gd->disk_name, dev->ubi_num, dev->vol_id);
+ ret = PTR_ERR(dev->desc);
+ dev->desc = NULL;
+ goto out_unlock;
+ }
+
+out_done:
+ dev->refcnt++;
+ mutex_unlock(&dev->dev_mutex);
+ return 0;
+
+out_unlock:
+ mutex_unlock(&dev->dev_mutex);
+ return ret;
+}
+
+static void ubiblock_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblock *dev = gd->private_data;
+
+ mutex_lock(&dev->dev_mutex);
+ dev->refcnt--;
+ if (dev->refcnt == 0) {
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+ }
+ mutex_unlock(&dev->dev_mutex);
+}
+
+static int ubiblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+ /* Some tools might require this information */
+ geo->heads = 1;
+ geo->cylinders = 1;
+ geo->sectors = get_capacity(bdev->bd_disk);
+ geo->start = 0;
+ return 0;
+}
+
+static const struct block_device_operations ubiblock_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblock_open,
+ .release = ubiblock_release,
+ .getgeo = ubiblock_getgeo,
+};
+
+int ubiblock_create(struct ubi_volume_info *vi)
+{
+ struct ubiblock *dev;
+ struct gendisk *gd;
+ int disk_capacity;
+ int ret;
+
+ /* Check that the volume isn't already handled */
+ mutex_lock(&devices_mutex);
+ if (find_dev_nolock(vi->ubi_num, vi->vol_id)) {
+ mutex_unlock(&devices_mutex);
+ return -EEXIST;
+ }
+ mutex_unlock(&devices_mutex);
+
+ dev = kzalloc(sizeof(struct ubiblock), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ mutex_init(&dev->dev_mutex);
+
+ dev->ubi_num = vi->ubi_num;
+ dev->vol_id = vi->vol_id;
+ dev->leb_size = vi->usable_leb_size;
+
+ /* Initialize the gendisk of this ubiblock device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ ubi_err("block: alloc_disk failed");
+ ret = -ENODEV;
+ goto out_free_dev;
+ }
+
+ gd->fops = &ubiblock_ops;
+ gd->major = ubiblock_major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(gd, disk_capacity);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubiblock_request, &dev->queue_lock);
+ if (!dev->rq) {
+ ubi_err("block: blk_init_queue failed");
+ ret = -ENODEV;
+ goto out_put_disk;
+ }
+
+ dev->rq->queuedata = dev;
+ dev->gd->queue = dev->rq;
+
+ /*
+ * Create one workqueue per volume (per registered block device).
+ * Rembember workqueues are cheap, they're not threads.
+ */
+ dev->wq = alloc_workqueue(gd->disk_name, 0, 0);
+ if (!dev->wq)
+ goto out_free_queue;
+ INIT_WORK(&dev->work, ubiblock_do_work);
+
+ mutex_lock(&devices_mutex);
+ list_add_tail(&dev->list, &ubiblock_devices);
+ mutex_unlock(&devices_mutex);
+
+ /* Must be the last step: anyone can call file ops from now on */
+ add_disk(dev->gd);
+ ubi_msg("%s created from ubi%d:%d(%s)",
+ dev->gd->disk_name, dev->ubi_num, dev->vol_id, vi->name);
+ return 0;
+
+out_free_queue:
+ blk_cleanup_queue(dev->rq);
+out_put_disk:
+ put_disk(dev->gd);
+out_free_dev:
+ kfree(dev);
+
+ return ret;
+}
+
+static void ubiblock_cleanup(struct ubiblock *dev)
+{
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ ubi_msg("%s released", dev->gd->disk_name);
+ put_disk(dev->gd);
+}
+
+int ubiblock_remove(struct ubi_volume_info *vi)
+{
+ struct ubiblock *dev;
+
+ mutex_lock(&devices_mutex);
+ dev = find_dev_nolock(vi->ubi_num, vi->vol_id);
+ if (!dev) {
+ mutex_unlock(&devices_mutex);
+ return -ENODEV;
+ }
+
+ /* Found a device, let's lock it so we can check if it's busy */
+ mutex_lock(&dev->dev_mutex);
+ if (dev->refcnt > 0) {
+ mutex_unlock(&dev->dev_mutex);
+ mutex_unlock(&devices_mutex);
+ return -EBUSY;
+ }
+
+ /* Remove from device list */
+ list_del(&dev->list);
+ mutex_unlock(&devices_mutex);
+
+ /* Flush pending work and stop this workqueue */
+ destroy_workqueue(dev->wq);
+
+ ubiblock_cleanup(dev);
+ mutex_unlock(&dev->dev_mutex);
+ kfree(dev);
+ return 0;
+}
+
+static void ubiblock_resize(struct ubi_volume_info *vi)
+{
+ struct ubiblock *dev;
+ int disk_capacity;
+
+ /*
+ * Need to lock the device list until we stop using the device,
+ * otherwise the device struct might get released in
+ * 'ubiblock_remove()'.
+ */
+ mutex_lock(&devices_mutex);
+ dev = find_dev_nolock(vi->ubi_num, vi->vol_id);
+ if (!dev) {
+ mutex_unlock(&devices_mutex);
+ return;
+ }
+
+ mutex_lock(&dev->dev_mutex);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(dev->gd, disk_capacity);
+ ubi_msg("%s resized to %d LEBs", dev->gd->disk_name, vi->size);
+ mutex_unlock(&dev->dev_mutex);
+ mutex_unlock(&devices_mutex);
+}
+
+static int ubiblock_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_ADDED:
+ /*
+ * We want to enforce explicit block device creation for
+ * volumes, so when a volume is added we do nothing.
+ */
+ break;
+ case UBI_VOLUME_REMOVED:
+ ubiblock_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblock_resize(&nt->vi);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block ubiblock_notifier = {
+ .notifier_call = ubiblock_notify,
+};
+
+static struct ubi_volume_desc * __init
+open_volume_desc(const char *name, int ubi_num, int vol_id)
+{
+ if (ubi_num == -1)
+ /* No ubi num, name must be a vol device path */
+ return ubi_open_volume_path(name, UBI_READONLY);
+ else if (vol_id == -1)
+ /* No vol_id, must be vol_name */
+ return ubi_open_volume_nm(ubi_num, name, UBI_READONLY);
+ else
+ return ubi_open_volume(ubi_num, vol_id, UBI_READONLY);
+}
+
+static int __init ubiblock_create_from_param(void)
+{
+ int i, ret;
+ struct ubiblock_param *p;
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+
+ for (i = 0; i < ubiblock_devs; i++) {
+ p = &ubiblock_param[i];
+
+ desc = open_volume_desc(p->name, p->ubi_num, p->vol_id);
+ if (IS_ERR(desc)) {
+ ubi_err("block: can't open volume, err=%ld\n",
+ PTR_ERR(desc));
+ ret = PTR_ERR(desc);
+ break;
+ }
+
+ ubi_get_volume_info(desc, &vi);
+ ubi_close_volume(desc);
+
+ ret = ubiblock_create(&vi);
+ if (ret) {
+ ubi_err("block: can't add '%s' volume, err=%d\n",
+ vi.name, ret);
+ break;
+ }
+ }
+ return ret;
+}
+
+static void ubiblock_remove_all(void)
+{
+ struct ubiblock *next;
+ struct ubiblock *dev;
+
+ list_for_each_entry_safe(dev, next, &ubiblock_devices, list) {
+ /* Flush pending work and stop workqueue */
+ destroy_workqueue(dev->wq);
+ /* The module is being forcefully removed */
+ WARN_ON(dev->desc);
+ /* Remove from device list */
+ list_del(&dev->list);
+ ubiblock_cleanup(dev);
+ kfree(dev);
+ }
+}
+
+int __init ubiblock_init(void)
+{
+ int ret;
+
+ ubiblock_major = register_blkdev(0, "ubiblock");
+ if (ubiblock_major < 0)
+ return ubiblock_major;
+
+ /* Attach block devices from 'block=' module param */
+ ret = ubiblock_create_from_param();
+ if (ret)
+ goto err_remove;
+
+ /*
+ * Block devices are only created upon user requests, so we ignore
+ * existing volumes.
+ */
+ ret = ubi_register_volume_notifier(&ubiblock_notifier, 1);
+ if (ret)
+ goto err_unreg;
+ return 0;
+
+err_unreg:
+ unregister_blkdev(ubiblock_major, "ubiblock");
+err_remove:
+ ubiblock_remove_all();
+ return ret;
+}
+
+void __exit ubiblock_exit(void)
+{
+ ubi_unregister_volume_notifier(&ubiblock_notifier);
+ ubiblock_remove_all();
+ unregister_blkdev(ubiblock_major, "ubiblock");
+}
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 57deae961429..6e30a3c280d0 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -1298,6 +1298,15 @@ static int __init ubi_init(void)
}
}
+ err = ubiblock_init();
+ if (err) {
+ ubi_err("block: cannot initialize, error %d", err);
+
+ /* See comment above re-ubi_is_module(). */
+ if (ubi_is_module())
+ goto out_detach;
+ }
+
return 0;
out_detach:
@@ -1326,6 +1335,8 @@ static void __exit ubi_exit(void)
{
int i;
+ ubiblock_exit();
+
for (i = 0; i < UBI_MAX_DEVICES; i++)
if (ubi_devices[i]) {
mutex_lock(&ubi_devices_mutex);
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
index 8ca49f2043e4..f54562a5998e 100644
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -561,6 +561,26 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
break;
}
+ /* Create a R/O block device on top of the UBI volume */
+ case UBI_IOCVOLCRBLK:
+ {
+ struct ubi_volume_info vi;
+
+ ubi_get_volume_info(desc, &vi);
+ err = ubiblock_create(&vi);
+ break;
+ }
+
+ /* Remove the R/O block device */
+ case UBI_IOCVOLRMBLK:
+ {
+ struct ubi_volume_info vi;
+
+ ubi_get_volume_info(desc, &vi);
+ err = ubiblock_remove(&vi);
+ break;
+ }
+
default:
err = -ENOTTY;
break;
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 8ea6297a208f..7bf416329c19 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -22,7 +22,6 @@
#ifndef __UBI_UBI_H__
#define __UBI_UBI_H__
-#include <linux/init.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/rbtree.h>
@@ -864,6 +863,26 @@ int ubi_update_fastmap(struct ubi_device *ubi);
int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
int fm_anchor);
+/* block.c */
+#ifdef CONFIG_MTD_UBI_BLOCK
+int ubiblock_init(void);
+void ubiblock_exit(void);
+int ubiblock_create(struct ubi_volume_info *vi);
+int ubiblock_remove(struct ubi_volume_info *vi);
+#else
+static inline int ubiblock_init(void) { return 0; }
+static inline void ubiblock_exit(void) {}
+static inline int ubiblock_create(struct ubi_volume_info *vi)
+{
+ return -ENOSYS;
+}
+static inline int ubiblock_remove(struct ubi_volume_info *vi)
+{
+ return -ENOSYS;
+}
+#endif
+
+
/*
* ubi_rb_for_each_entry - walk an RB-tree.
* @rb: a pointer to type 'struct rb_node' to use as a loop counter
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 494b888a6568..89402c3b64f8 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -177,11 +177,6 @@ config NETCONSOLE_DYNAMIC
config NETPOLL
def_bool NETCONSOLE
-config NETPOLL_TRAP
- bool "Netpoll traffic trapping"
- default n
- depends on NETPOLL
-
config NET_POLL_CONTROLLER
def_bool NETPOLL
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index dcde56057fe1..b667a51ed215 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -768,11 +768,11 @@ static int ad_lacpdu_send(struct port *port)
lacpdu_header = (struct lacpdu_header *)skb_put(skb, length);
- memcpy(lacpdu_header->hdr.h_dest, lacpdu_mcast_addr, ETH_ALEN);
+ ether_addr_copy(lacpdu_header->hdr.h_dest, lacpdu_mcast_addr);
/* Note: source address is set to be the member's PERMANENT address,
* because we use it to identify loopback lacpdus in receive.
*/
- memcpy(lacpdu_header->hdr.h_source, slave->perm_hwaddr, ETH_ALEN);
+ ether_addr_copy(lacpdu_header->hdr.h_source, slave->perm_hwaddr);
lacpdu_header->hdr.h_proto = PKT_TYPE_LACPDU;
lacpdu_header->lacpdu = port->lacpdu;
@@ -810,11 +810,11 @@ static int ad_marker_send(struct port *port, struct bond_marker *marker)
marker_header = (struct bond_marker_header *)skb_put(skb, length);
- memcpy(marker_header->hdr.h_dest, lacpdu_mcast_addr, ETH_ALEN);
+ ether_addr_copy(marker_header->hdr.h_dest, lacpdu_mcast_addr);
/* Note: source address is set to be the member's PERMANENT address,
* because we use it to identify loopback MARKERs in receive.
*/
- memcpy(marker_header->hdr.h_source, slave->perm_hwaddr, ETH_ALEN);
+ ether_addr_copy(marker_header->hdr.h_source, slave->perm_hwaddr);
marker_header->hdr.h_proto = PKT_TYPE_LACPDU;
marker_header->marker = *marker;
@@ -1079,7 +1079,8 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
/* detect loopback situation */
if (MAC_ADDRESS_EQUAL(&(lacpdu->actor_system),
&(port->actor_system))) {
- pr_err("%s: An illegal loopback occurred on adapter (%s).\nCheck the configuration to verify that all adapters are connected to 802.3ad compliant switch ports\n",
+ pr_err("%s: An illegal loopback occurred on adapter (%s)\n"
+ "Check the configuration to verify that all adapters are connected to 802.3ad compliant switch ports\n",
port->slave->bond->dev->name,
port->slave->dev->name);
return;
@@ -1283,11 +1284,11 @@ static void ad_port_selection_logic(struct port *port)
/* meaning: the port was related to an aggregator
* but was not on the aggregator port list
*/
- pr_warn("%s: Warning: Port %d (on %s) was related to aggregator %d but was not on its port list\n",
- port->slave->bond->dev->name,
- port->actor_port_number,
- port->slave->dev->name,
- port->aggregator->aggregator_identifier);
+ pr_warn_ratelimited("%s: Warning: Port %d (on %s) was related to aggregator %d but was not on its port list\n",
+ port->slave->bond->dev->name,
+ port->actor_port_number,
+ port->slave->dev->name,
+ port->aggregator->aggregator_identifier);
}
}
/* search on all aggregators for a suitable aggregator for this port */
@@ -1444,9 +1445,9 @@ static struct aggregator *ad_agg_selection_test(struct aggregator *best,
break;
default:
- pr_warn("%s: Impossible agg select mode %d\n",
- curr->slave->bond->dev->name,
- __get_agg_selection_mode(curr->lag_ports));
+ pr_warn_ratelimited("%s: Impossible agg select mode %d\n",
+ curr->slave->bond->dev->name,
+ __get_agg_selection_mode(curr->lag_ports));
break;
}
@@ -1559,9 +1560,9 @@ static void ad_agg_selection_logic(struct aggregator *agg)
/* check if any partner replys */
if (best->is_individual) {
- pr_warn("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n",
- best->slave ?
- best->slave->bond->dev->name : "NULL");
+ pr_warn_ratelimited("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n",
+ best->slave ?
+ best->slave->bond->dev->name : "NULL");
}
best->is_active = 1;
@@ -1948,7 +1949,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
* new aggregator
*/
if ((new_aggregator) && ((!new_aggregator->lag_ports) || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator))) {
- pr_debug("Some port(s) related to LAG %d - replaceing with LAG %d\n",
+ pr_debug("Some port(s) related to LAG %d - replacing with LAG %d\n",
aggregator->aggregator_identifier,
new_aggregator->aggregator_identifier);
@@ -2080,8 +2081,8 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
/* select the active aggregator for the bond */
if (port) {
if (!port->slave) {
- pr_warn("%s: Warning: bond's first port is uninitialized\n",
- bond->dev->name);
+ pr_warn_ratelimited("%s: Warning: bond's first port is uninitialized\n",
+ bond->dev->name);
goto re_arm;
}
@@ -2095,8 +2096,8 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
bond_for_each_slave_rcu(bond, slave, iter) {
port = &(SLAVE_AD_INFO(slave).port);
if (!port->slave) {
- pr_warn("%s: Warning: Found an uninitialized port\n",
- bond->dev->name);
+ pr_warn_ratelimited("%s: Warning: Found an uninitialized port\n",
+ bond->dev->name);
goto re_arm;
}
@@ -2157,8 +2158,8 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave,
port = &(SLAVE_AD_INFO(slave).port);
if (!port->slave) {
- pr_warn("%s: Warning: port of slave %s is uninitialized\n",
- slave->dev->name, slave->bond->dev->name);
+ pr_warn_ratelimited("%s: Warning: port of slave %s is uninitialized\n",
+ slave->dev->name, slave->bond->dev->name);
return ret;
}
@@ -2310,9 +2311,9 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
port->actor_oper_port_key = (port->actor_admin_port_key &=
~AD_SPEED_KEY_BITS);
}
- pr_debug("Port %d changed link status to %s",
- port->actor_port_number,
- (link == BOND_LINK_UP) ? "UP" : "DOWN");
+ pr_debug("Port %d changed link status to %s\n",
+ port->actor_port_number,
+ link == BOND_LINK_UP ? "UP" : "DOWN");
/* there is no need to reselect a new aggregator, just signal the
* state machines to reinitialize
*/
@@ -2390,17 +2391,16 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond,
}
}
- if (aggregator) {
- ad_info->aggregator_id = aggregator->aggregator_identifier;
- ad_info->ports = aggregator->num_of_ports;
- ad_info->actor_key = aggregator->actor_oper_aggregator_key;
- ad_info->partner_key = aggregator->partner_oper_aggregator_key;
- memcpy(ad_info->partner_system,
- aggregator->partner_system.mac_addr_value, ETH_ALEN);
- return 0;
- }
+ if (!aggregator)
+ return -1;
- return -1;
+ ad_info->aggregator_id = aggregator->aggregator_identifier;
+ ad_info->ports = aggregator->num_of_ports;
+ ad_info->actor_key = aggregator->actor_oper_aggregator_key;
+ ad_info->partner_key = aggregator->partner_oper_aggregator_key;
+ ether_addr_copy(ad_info->partner_system,
+ aggregator->partner_system.mac_addr_value);
+ return 0;
}
/* Wrapper used to hold bond->lock so no slave manipulation can occur */
@@ -2479,7 +2479,7 @@ out:
return NETDEV_TX_OK;
err_free:
/* no suitable interface, frame not sent */
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
goto out;
}
diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h
index f4dd9592ac62..bb03b1df2f3e 100644
--- a/drivers/net/bonding/bond_3ad.h
+++ b/drivers/net/bonding/bond_3ad.h
@@ -28,7 +28,7 @@
#include <linux/netdevice.h>
#include <linux/if_ether.h>
-// General definitions
+/* General definitions */
#define PKT_TYPE_LACPDU cpu_to_be16(ETH_P_SLOW)
#define AD_TIMER_INTERVAL 100 /*msec*/
@@ -47,54 +47,54 @@ enum {
BOND_AD_COUNT = 2,
};
-// rx machine states(43.4.11 in the 802.3ad standard)
+/* rx machine states(43.4.11 in the 802.3ad standard) */
typedef enum {
AD_RX_DUMMY,
- AD_RX_INITIALIZE, // rx Machine
- AD_RX_PORT_DISABLED, // rx Machine
- AD_RX_LACP_DISABLED, // rx Machine
- AD_RX_EXPIRED, // rx Machine
- AD_RX_DEFAULTED, // rx Machine
- AD_RX_CURRENT // rx Machine
+ AD_RX_INITIALIZE, /* rx Machine */
+ AD_RX_PORT_DISABLED, /* rx Machine */
+ AD_RX_LACP_DISABLED, /* rx Machine */
+ AD_RX_EXPIRED, /* rx Machine */
+ AD_RX_DEFAULTED, /* rx Machine */
+ AD_RX_CURRENT /* rx Machine */
} rx_states_t;
-// periodic machine states(43.4.12 in the 802.3ad standard)
+/* periodic machine states(43.4.12 in the 802.3ad standard) */
typedef enum {
AD_PERIODIC_DUMMY,
- AD_NO_PERIODIC, // periodic machine
- AD_FAST_PERIODIC, // periodic machine
- AD_SLOW_PERIODIC, // periodic machine
- AD_PERIODIC_TX // periodic machine
+ AD_NO_PERIODIC, /* periodic machine */
+ AD_FAST_PERIODIC, /* periodic machine */
+ AD_SLOW_PERIODIC, /* periodic machine */
+ AD_PERIODIC_TX /* periodic machine */
} periodic_states_t;
-// mux machine states(43.4.13 in the 802.3ad standard)
+/* mux machine states(43.4.13 in the 802.3ad standard) */
typedef enum {
AD_MUX_DUMMY,
- AD_MUX_DETACHED, // mux machine
- AD_MUX_WAITING, // mux machine
- AD_MUX_ATTACHED, // mux machine
- AD_MUX_COLLECTING_DISTRIBUTING // mux machine
+ AD_MUX_DETACHED, /* mux machine */
+ AD_MUX_WAITING, /* mux machine */
+ AD_MUX_ATTACHED, /* mux machine */
+ AD_MUX_COLLECTING_DISTRIBUTING /* mux machine */
} mux_states_t;
-// tx machine states(43.4.15 in the 802.3ad standard)
+/* tx machine states(43.4.15 in the 802.3ad standard) */
typedef enum {
AD_TX_DUMMY,
- AD_TRANSMIT // tx Machine
+ AD_TRANSMIT /* tx Machine */
} tx_states_t;
-// rx indication types
+/* rx indication types */
typedef enum {
- AD_TYPE_LACPDU = 1, // type lacpdu
- AD_TYPE_MARKER // type marker
+ AD_TYPE_LACPDU = 1, /* type lacpdu */
+ AD_TYPE_MARKER /* type marker */
} pdu_type_t;
-// rx marker indication types
+/* rx marker indication types */
typedef enum {
- AD_MARKER_INFORMATION_SUBTYPE = 1, // marker imformation subtype
- AD_MARKER_RESPONSE_SUBTYPE // marker response subtype
+ AD_MARKER_INFORMATION_SUBTYPE = 1, /* marker imformation subtype */
+ AD_MARKER_RESPONSE_SUBTYPE /* marker response subtype */
} bond_marker_subtype_t;
-// timers types(43.4.9 in the 802.3ad standard)
+/* timers types(43.4.9 in the 802.3ad standard) */
typedef enum {
AD_CURRENT_WHILE_TIMER,
AD_ACTOR_CHURN_TIMER,
@@ -105,35 +105,35 @@ typedef enum {
#pragma pack(1)
-// Link Aggregation Control Protocol(LACP) data unit structure(43.4.2.2 in the 802.3ad standard)
+/* Link Aggregation Control Protocol(LACP) data unit structure(43.4.2.2 in the 802.3ad standard) */
typedef struct lacpdu {
- u8 subtype; // = LACP(= 0x01)
+ u8 subtype; /* = LACP(= 0x01) */
u8 version_number;
- u8 tlv_type_actor_info; // = actor information(type/length/value)
- u8 actor_information_length; // = 20
+ u8 tlv_type_actor_info; /* = actor information(type/length/value) */
+ u8 actor_information_length; /* = 20 */
__be16 actor_system_priority;
struct mac_addr actor_system;
__be16 actor_key;
__be16 actor_port_priority;
__be16 actor_port;
u8 actor_state;
- u8 reserved_3_1[3]; // = 0
- u8 tlv_type_partner_info; // = partner information
- u8 partner_information_length; // = 20
+ u8 reserved_3_1[3]; /* = 0 */
+ u8 tlv_type_partner_info; /* = partner information */
+ u8 partner_information_length; /* = 20 */
__be16 partner_system_priority;
struct mac_addr partner_system;
__be16 partner_key;
__be16 partner_port_priority;
__be16 partner_port;
u8 partner_state;
- u8 reserved_3_2[3]; // = 0
- u8 tlv_type_collector_info; // = collector information
- u8 collector_information_length; // = 16
+ u8 reserved_3_2[3]; /* = 0 */
+ u8 tlv_type_collector_info; /* = collector information */
+ u8 collector_information_length;/* = 16 */
__be16 collector_max_delay;
u8 reserved_12[12];
- u8 tlv_type_terminator; // = terminator
- u8 terminator_length; // = 0
- u8 reserved_50[50]; // = 0
+ u8 tlv_type_terminator; /* = terminator */
+ u8 terminator_length; /* = 0 */
+ u8 reserved_50[50]; /* = 0 */
} __packed lacpdu_t;
typedef struct lacpdu_header {
@@ -141,20 +141,20 @@ typedef struct lacpdu_header {
struct lacpdu lacpdu;
} __packed lacpdu_header_t;
-// Marker Protocol Data Unit(PDU) structure(43.5.3.2 in the 802.3ad standard)
+/* Marker Protocol Data Unit(PDU) structure(43.5.3.2 in the 802.3ad standard) */
typedef struct bond_marker {
- u8 subtype; // = 0x02 (marker PDU)
- u8 version_number; // = 0x01
- u8 tlv_type; // = 0x01 (marker information)
- // = 0x02 (marker response information)
- u8 marker_length; // = 0x16
- u16 requester_port; // The number assigned to the port by the requester
- struct mac_addr requester_system; // The requester's system id
- u32 requester_transaction_id; // The transaction id allocated by the requester,
- u16 pad; // = 0
- u8 tlv_type_terminator; // = 0x00
- u8 terminator_length; // = 0x00
- u8 reserved_90[90]; // = 0
+ u8 subtype; /* = 0x02 (marker PDU) */
+ u8 version_number; /* = 0x01 */
+ u8 tlv_type; /* = 0x01 (marker information) */
+ /* = 0x02 (marker response information) */
+ u8 marker_length; /* = 0x16 */
+ u16 requester_port; /* The number assigned to the port by the requester */
+ struct mac_addr requester_system; /* The requester's system id */
+ u32 requester_transaction_id; /* The transaction id allocated by the requester, */
+ u16 pad; /* = 0 */
+ u8 tlv_type_terminator; /* = 0x00 */
+ u8 terminator_length; /* = 0x00 */
+ u8 reserved_90[90]; /* = 0 */
} __packed bond_marker_t;
typedef struct bond_marker_header {
@@ -173,7 +173,7 @@ struct port;
#pragma pack(8)
#endif
-// aggregator structure(43.4.5 in the 802.3ad standard)
+/* aggregator structure(43.4.5 in the 802.3ad standard) */
typedef struct aggregator {
struct mac_addr aggregator_mac_address;
u16 aggregator_identifier;
@@ -183,12 +183,12 @@ typedef struct aggregator {
struct mac_addr partner_system;
u16 partner_system_priority;
u16 partner_oper_aggregator_key;
- u16 receive_state; // BOOLEAN
- u16 transmit_state; // BOOLEAN
+ u16 receive_state; /* BOOLEAN */
+ u16 transmit_state; /* BOOLEAN */
struct port *lag_ports;
- // ****** PRIVATE PARAMETERS ******
- struct slave *slave; // pointer to the bond slave that this aggregator belongs to
- u16 is_active; // BOOLEAN. Indicates if this aggregator is active
+ /* ****** PRIVATE PARAMETERS ****** */
+ struct slave *slave; /* pointer to the bond slave that this aggregator belongs to */
+ u16 is_active; /* BOOLEAN. Indicates if this aggregator is active */
u16 num_of_ports;
} aggregator_t;
@@ -201,12 +201,12 @@ struct port_params {
u16 port_state;
};
-// port structure(43.4.6 in the 802.3ad standard)
+/* port structure(43.4.6 in the 802.3ad standard) */
typedef struct port {
u16 actor_port_number;
u16 actor_port_priority;
- struct mac_addr actor_system; // This parameter is added here although it is not specified in the standard, just for simplification
- u16 actor_system_priority; // This parameter is added here although it is not specified in the standard, just for simplification
+ struct mac_addr actor_system; /* This parameter is added here although it is not specified in the standard, just for simplification */
+ u16 actor_system_priority; /* This parameter is added here although it is not specified in the standard, just for simplification */
u16 actor_port_aggregator_identifier;
bool ntt;
u16 actor_admin_port_key;
@@ -219,24 +219,24 @@ typedef struct port {
bool is_enabled;
- // ****** PRIVATE PARAMETERS ******
- u16 sm_vars; // all state machines variables for this port
- rx_states_t sm_rx_state; // state machine rx state
- u16 sm_rx_timer_counter; // state machine rx timer counter
- periodic_states_t sm_periodic_state;// state machine periodic state
- u16 sm_periodic_timer_counter; // state machine periodic timer counter
- mux_states_t sm_mux_state; // state machine mux state
- u16 sm_mux_timer_counter; // state machine mux timer counter
- tx_states_t sm_tx_state; // state machine tx state
- u16 sm_tx_timer_counter; // state machine tx timer counter(allways on - enter to transmit state 3 time per second)
- struct slave *slave; // pointer to the bond slave that this port belongs to
- struct aggregator *aggregator; // pointer to an aggregator that this port related to
- struct port *next_port_in_aggregator; // Next port on the linked list of the parent aggregator
- u32 transaction_id; // continuous number for identification of Marker PDU's;
- struct lacpdu lacpdu; // the lacpdu that will be sent for this port
+ /* ****** PRIVATE PARAMETERS ****** */
+ u16 sm_vars; /* all state machines variables for this port */
+ rx_states_t sm_rx_state; /* state machine rx state */
+ u16 sm_rx_timer_counter; /* state machine rx timer counter */
+ periodic_states_t sm_periodic_state; /* state machine periodic state */
+ u16 sm_periodic_timer_counter; /* state machine periodic timer counter */
+ mux_states_t sm_mux_state; /* state machine mux state */
+ u16 sm_mux_timer_counter; /* state machine mux timer counter */
+ tx_states_t sm_tx_state; /* state machine tx state */
+ u16 sm_tx_timer_counter; /* state machine tx timer counter(allways on - enter to transmit state 3 time per second) */
+ struct slave *slave; /* pointer to the bond slave that this port belongs to */
+ struct aggregator *aggregator; /* pointer to an aggregator that this port related to */
+ struct port *next_port_in_aggregator; /* Next port on the linked list of the parent aggregator */
+ u32 transaction_id; /* continuous number for identification of Marker PDU's; */
+ struct lacpdu lacpdu; /* the lacpdu that will be sent for this port */
} port_t;
-// system structure
+/* system structure */
struct ad_system {
u16 sys_priority;
struct mac_addr sys_mac_addr;
@@ -246,27 +246,26 @@ struct ad_system {
#pragma pack()
#endif
-// ================= AD Exported structures to the main bonding code ==================
+/* ========== AD Exported structures to the main bonding code ========== */
#define BOND_AD_INFO(bond) ((bond)->ad_info)
#define SLAVE_AD_INFO(slave) ((slave)->ad_info)
struct ad_bond_info {
- struct ad_system system; /* 802.3ad system structure */
- u32 agg_select_timer; // Timer to select aggregator after all adapter's hand shakes
+ struct ad_system system; /* 802.3ad system structure */
+ u32 agg_select_timer; /* Timer to select aggregator after all adapter's hand shakes */
u16 aggregator_identifier;
};
struct ad_slave_info {
- struct aggregator aggregator; // 802.3ad aggregator structure
- struct port port; // 802.3ad port structure
- spinlock_t state_machine_lock; /* mutex state machines vs.
- incoming LACPDU */
+ struct aggregator aggregator; /* 802.3ad aggregator structure */
+ struct port port; /* 802.3ad port structure */
+ spinlock_t state_machine_lock; /* mutex state machines vs. incoming LACPDU */
u16 id;
};
-// ================= AD Exported functions to the main bonding code ==================
+/* ========== AD Exported functions to the main bonding code ========== */
void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution);
-void bond_3ad_bind_slave(struct slave *slave);
+void bond_3ad_bind_slave(struct slave *slave);
void bond_3ad_unbind_slave(struct slave *slave);
void bond_3ad_state_machine_handler(struct work_struct *);
void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout);
@@ -281,5 +280,5 @@ int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
struct slave *slave);
int bond_3ad_set_carrier(struct bonding *bond);
void bond_3ad_update_lacp_rate(struct bonding *bond);
-#endif //__BOND_3AD_H__
+#endif /* __BOND_3AD_H__ */
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index e8f133e926aa..9f69e818b000 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -93,9 +93,8 @@ static inline u8 _simple_hash(const u8 *hash_start, int hash_size)
int i;
u8 hash = 0;
- for (i = 0; i < hash_size; i++) {
+ for (i = 0; i < hash_size; i++)
hash ^= hash_start[i];
- }
return hash;
}
@@ -190,9 +189,8 @@ static int tlb_initialize(struct bonding *bond)
bond_info->tx_hashtbl = new_hashtbl;
- for (i = 0; i < TLB_HASH_TABLE_SIZE; i++) {
+ for (i = 0; i < TLB_HASH_TABLE_SIZE; i++)
tlb_init_table_entry(&bond_info->tx_hashtbl[i], 0);
- }
_unlock_tx_hashtbl_bh(bond);
@@ -264,9 +262,8 @@ static struct slave *__tlb_choose_channel(struct bonding *bond, u32 hash_index,
hash_table[hash_index].next = next_index;
hash_table[hash_index].prev = TLB_NULL_INDEX;
- if (next_index != TLB_NULL_INDEX) {
+ if (next_index != TLB_NULL_INDEX)
hash_table[next_index].prev = hash_index;
- }
slave_info->head = hash_index;
slave_info->load +=
@@ -274,9 +271,8 @@ static struct slave *__tlb_choose_channel(struct bonding *bond, u32 hash_index,
}
}
- if (assigned_slave) {
+ if (assigned_slave)
hash_table[hash_index].tx_bytes += skb_len;
- }
return assigned_slave;
}
@@ -329,7 +325,7 @@ static void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp)
_lock_rx_hashtbl_bh(bond);
- hash_index = _simple_hash((u8*)&(arp->ip_src), sizeof(arp->ip_src));
+ hash_index = _simple_hash((u8 *)&(arp->ip_src), sizeof(arp->ip_src));
client_info = &(bond_info->rx_hashtbl[hash_index]);
if ((client_info->assigned) &&
@@ -337,7 +333,7 @@ static void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp)
(client_info->ip_dst == arp->ip_src) &&
(!ether_addr_equal_64bits(client_info->mac_dst, arp->mac_src))) {
/* update the clients MAC address */
- memcpy(client_info->mac_dst, arp->mac_src, ETH_ALEN);
+ ether_addr_copy(client_info->mac_dst, arp->mac_src);
client_info->ntt = 1;
bond_info->rx_ntt = 1;
}
@@ -451,9 +447,8 @@ static struct slave *__rlb_next_rx_slave(struct bonding *bond)
*/
static void rlb_teach_disabled_mac_on_primary(struct bonding *bond, u8 addr[])
{
- if (!bond->curr_active_slave) {
+ if (!bond->curr_active_slave)
return;
- }
if (!bond->alb_info.primary_is_promisc) {
if (!dev_set_promiscuity(bond->curr_active_slave->dev, 1))
@@ -513,9 +508,8 @@ static void rlb_clear_slave(struct bonding *bond, struct slave *slave)
write_lock_bh(&bond->curr_slave_lock);
- if (slave != bond->curr_active_slave) {
+ if (slave != bond->curr_active_slave)
rlb_teach_disabled_mac_on_primary(bond, slave->dev->dev_addr);
- }
write_unlock_bh(&bond->curr_slave_lock);
}
@@ -524,9 +518,8 @@ static void rlb_update_client(struct rlb_client_info *client_info)
{
int i;
- if (!client_info->slave) {
+ if (!client_info->slave)
return;
- }
for (i = 0; i < RLB_ARP_BURST_SIZE; i++) {
struct sk_buff *skb;
@@ -574,9 +567,8 @@ static void rlb_update_rx_clients(struct bonding *bond)
client_info = &(bond_info->rx_hashtbl[hash_index]);
if (client_info->ntt) {
rlb_update_client(client_info);
- if (bond_info->rlb_update_retry_counter == 0) {
+ if (bond_info->rlb_update_retry_counter == 0)
client_info->ntt = 0;
- }
}
}
@@ -610,10 +602,10 @@ static void rlb_req_update_slave_clients(struct bonding *bond, struct slave *sla
}
}
- // update the team's flag only after the whole iteration
+ /* update the team's flag only after the whole iteration */
if (ntt) {
bond_info->rx_ntt = 1;
- //fasten the change
+ /* fasten the change */
bond_info->rlb_update_retry_counter = RLB_UPDATE_RETRY;
}
@@ -677,9 +669,9 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
/* the entry is already assigned to this client */
if (!ether_addr_equal_64bits(arp->mac_dst, mac_bcast)) {
/* update mac address from arp */
- memcpy(client_info->mac_dst, arp->mac_dst, ETH_ALEN);
+ ether_addr_copy(client_info->mac_dst, arp->mac_dst);
}
- memcpy(client_info->mac_src, arp->mac_src, ETH_ALEN);
+ ether_addr_copy(client_info->mac_src, arp->mac_src);
assigned_slave = client_info->slave;
if (assigned_slave) {
@@ -719,8 +711,8 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
* will be updated with clients actual unicast mac address
* upon receiving an arp reply.
*/
- memcpy(client_info->mac_dst, arp->mac_dst, ETH_ALEN);
- memcpy(client_info->mac_src, arp->mac_src, ETH_ALEN);
+ ether_addr_copy(client_info->mac_dst, arp->mac_dst);
+ ether_addr_copy(client_info->mac_src, arp->mac_src);
client_info->slave = assigned_slave;
if (!ether_addr_equal_64bits(client_info->mac_dst, mac_bcast)) {
@@ -770,9 +762,8 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
* rx channel
*/
tx_slave = rlb_choose_channel(skb, bond);
- if (tx_slave) {
- memcpy(arp->mac_src,tx_slave->dev->dev_addr, ETH_ALEN);
- }
+ if (tx_slave)
+ ether_addr_copy(arp->mac_src, tx_slave->dev->dev_addr);
pr_debug("Server sent ARP Reply packet\n");
} else if (arp->op_code == htons(ARPOP_REQUEST)) {
/* Create an entry in the rx_hashtbl for this client as a
@@ -824,9 +815,8 @@ static void rlb_rebalance(struct bonding *bond)
}
/* update the team's flag only after the whole iteration */
- if (ntt) {
+ if (ntt)
bond_info->rx_ntt = 1;
- }
_unlock_rx_hashtbl_bh(bond);
}
@@ -923,7 +913,7 @@ static void rlb_src_link(struct bonding *bond, u32 ip_src_hash, u32 ip_dst_hash)
static void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp)
{
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
- u32 ip_src_hash = _simple_hash((u8*)&(arp->ip_src), sizeof(arp->ip_src));
+ u32 ip_src_hash = _simple_hash((u8 *)&(arp->ip_src), sizeof(arp->ip_src));
u32 index;
_lock_rx_hashtbl_bh(bond);
@@ -957,9 +947,8 @@ static int rlb_initialize(struct bonding *bond)
bond_info->rx_hashtbl_used_head = RLB_NULL_INDEX;
- for (i = 0; i < RLB_HASH_TABLE_SIZE; i++) {
+ for (i = 0; i < RLB_HASH_TABLE_SIZE; i++)
rlb_init_table_entry(bond_info->rx_hashtbl + i);
- }
_unlock_rx_hashtbl_bh(bond);
@@ -1014,9 +1003,9 @@ static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[],
char *data;
memset(&pkt, 0, size);
- memcpy(pkt.mac_dst, mac_addr, ETH_ALEN);
- memcpy(pkt.mac_src, mac_addr, ETH_ALEN);
- pkt.type = cpu_to_be16(ETH_P_LOOP);
+ ether_addr_copy(pkt.mac_dst, mac_addr);
+ ether_addr_copy(pkt.mac_src, mac_addr);
+ pkt.type = cpu_to_be16(ETH_P_LOOPBACK);
skb = dev_alloc_skb(size);
if (!skb)
@@ -1097,7 +1086,7 @@ static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2)
{
u8 tmp_mac_addr[ETH_ALEN];
- memcpy(tmp_mac_addr, slave1->dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(tmp_mac_addr, slave1->dev->dev_addr);
alb_set_slave_mac_addr(slave1, slave2->dev->dev_addr);
alb_set_slave_mac_addr(slave2, tmp_mac_addr);
@@ -1254,9 +1243,9 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav
if (free_mac_slave) {
alb_set_slave_mac_addr(slave, free_mac_slave->perm_hwaddr);
- pr_warning("%s: Warning: the hw address of slave %s is in use by the bond; giving it the hw address of %s\n",
- bond->dev->name, slave->dev->name,
- free_mac_slave->dev->name);
+ pr_warn("%s: Warning: the hw address of slave %s is in use by the bond; giving it the hw address of %s\n",
+ bond->dev->name, slave->dev->name,
+ free_mac_slave->dev->name);
} else if (has_bond_addr) {
pr_err("%s: Error: the hw address of slave %s is in use by the bond; couldn't find a slave with a free hw address to give it (this should not have happened)\n",
@@ -1294,12 +1283,12 @@ static int alb_set_mac_address(struct bonding *bond, void *addr)
bond_for_each_slave(bond, slave, iter) {
/* save net_device's current hw address */
- memcpy(tmp_addr, slave->dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(tmp_addr, slave->dev->dev_addr);
res = dev_set_mac_address(slave->dev, addr);
/* restore net_device's hw address */
- memcpy(slave->dev->dev_addr, tmp_addr, ETH_ALEN);
+ ether_addr_copy(slave->dev->dev_addr, tmp_addr);
if (res)
goto unwind;
@@ -1315,9 +1304,9 @@ unwind:
bond_for_each_slave(bond, rollback_slave, iter) {
if (rollback_slave == slave)
break;
- memcpy(tmp_addr, rollback_slave->dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(tmp_addr, rollback_slave->dev->dev_addr);
dev_set_mac_address(rollback_slave->dev, &sa);
- memcpy(rollback_slave->dev->dev_addr, tmp_addr, ETH_ALEN);
+ ether_addr_copy(rollback_slave->dev->dev_addr, tmp_addr);
}
return res;
@@ -1330,9 +1319,8 @@ int bond_alb_initialize(struct bonding *bond, int rlb_enabled)
int res;
res = tlb_initialize(bond);
- if (res) {
+ if (res)
return res;
- }
if (rlb_enabled) {
bond->alb_info.rlb_enabled = 1;
@@ -1355,9 +1343,8 @@ void bond_alb_deinitialize(struct bonding *bond)
tlb_deinitialize(bond);
- if (bond_info->rlb_enabled) {
+ if (bond_info->rlb_enabled)
rlb_deinitialize(bond);
- }
}
int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
@@ -1436,14 +1423,13 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
break;
}
- hash_start = (char*)eth_data->h_dest;
+ hash_start = (char *)eth_data->h_dest;
hash_size = ETH_ALEN;
break;
case ETH_P_ARP:
do_tx_balance = 0;
- if (bond_info->rlb_enabled) {
+ if (bond_info->rlb_enabled)
tx_slave = rlb_arp_xmit(skb, bond);
- }
break;
default:
do_tx_balance = 0;
@@ -1463,23 +1449,22 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
if (tx_slave && SLAVE_IS_OK(tx_slave)) {
if (tx_slave != rcu_dereference(bond->curr_active_slave)) {
- memcpy(eth_data->h_source,
- tx_slave->dev->dev_addr,
- ETH_ALEN);
+ ether_addr_copy(eth_data->h_source,
+ tx_slave->dev->dev_addr);
}
bond_dev_queue_xmit(bond, skb, tx_slave->dev);
goto out;
- } else {
- if (tx_slave) {
- _lock_tx_hashtbl(bond);
- __tlb_clear_slave(bond, tx_slave, 0);
- _unlock_tx_hashtbl(bond);
- }
+ }
+
+ if (tx_slave) {
+ _lock_tx_hashtbl(bond);
+ __tlb_clear_slave(bond, tx_slave, 0);
+ _unlock_tx_hashtbl(bond);
}
/* no suitable interface, frame not sent */
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
out:
return NETDEV_TX_OK;
}
@@ -1577,11 +1562,10 @@ void bond_alb_monitor(struct work_struct *work)
--bond_info->rlb_update_delay_counter;
} else {
rlb_update_rx_clients(bond);
- if (bond_info->rlb_update_retry_counter) {
+ if (bond_info->rlb_update_retry_counter)
--bond_info->rlb_update_retry_counter;
- } else {
+ else
bond_info->rx_ntt = 0;
- }
}
}
}
@@ -1598,23 +1582,20 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave)
int res;
res = alb_set_slave_mac_addr(slave, slave->perm_hwaddr);
- if (res) {
+ if (res)
return res;
- }
res = alb_handle_addr_collision_on_attach(bond, slave);
- if (res) {
+ if (res)
return res;
- }
tlb_init_slave(slave);
/* order a rebalance ASAP */
bond->alb_info.tx_rebalance_counter = BOND_TLB_REBALANCE_TICKS;
- if (bond->alb_info.rlb_enabled) {
+ if (bond->alb_info.rlb_enabled)
bond->alb_info.rlb_rebalance = 1;
- }
return 0;
}
@@ -1645,9 +1626,8 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char
if (link == BOND_LINK_DOWN) {
tlb_clear_slave(bond, slave, 0);
- if (bond->alb_info.rlb_enabled) {
+ if (bond->alb_info.rlb_enabled)
rlb_clear_slave(bond, slave);
- }
} else if (link == BOND_LINK_UP) {
/* order a rebalance ASAP */
bond_info->tx_rebalance_counter = BOND_TLB_REBALANCE_TICKS;
@@ -1723,14 +1703,14 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
struct sockaddr sa;
u8 tmp_addr[ETH_ALEN];
- memcpy(tmp_addr, new_slave->dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(tmp_addr, new_slave->dev->dev_addr);
memcpy(sa.sa_data, bond->dev->dev_addr, bond->dev->addr_len);
sa.sa_family = bond->dev->type;
/* we don't care if it can't change its mac, best effort */
dev_set_mac_address(new_slave->dev, &sa);
- memcpy(new_slave->dev->dev_addr, tmp_addr, ETH_ALEN);
+ ether_addr_copy(new_slave->dev->dev_addr, tmp_addr);
}
/* curr_active_slave must be set before calling alb_swap_mac_addr */
@@ -1759,14 +1739,12 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
struct slave *swap_slave;
int res;
- if (!is_valid_ether_addr(sa->sa_data)) {
+ if (!is_valid_ether_addr(sa->sa_data))
return -EADDRNOTAVAIL;
- }
res = alb_set_mac_address(bond, addr);
- if (res) {
+ if (res)
return res;
- }
memcpy(bond_dev->dev_addr, sa->sa_data, bond_dev->addr_len);
@@ -1774,9 +1752,8 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
* Otherwise we'll need to pass the new address to it and handle
* duplications.
*/
- if (!bond->curr_active_slave) {
+ if (!bond->curr_active_slave)
return 0;
- }
swap_slave = bond_slave_has_mac(bond, bond_dev->dev_addr);
@@ -1800,8 +1777,7 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
void bond_alb_clear_vlan(struct bonding *bond, unsigned short vlan_id)
{
- if (bond->alb_info.rlb_enabled) {
+ if (bond->alb_info.rlb_enabled)
rlb_clear_vlan(bond, vlan_id);
- }
}
diff --git a/drivers/net/bonding/bond_debugfs.c b/drivers/net/bonding/bond_debugfs.c
index 5fc4c2351478..2d3f7fa541ff 100644
--- a/drivers/net/bonding/bond_debugfs.c
+++ b/drivers/net/bonding/bond_debugfs.c
@@ -69,7 +69,7 @@ void bond_debug_register(struct bonding *bond)
debugfs_create_dir(bond->dev->name, bonding_debug_root);
if (!bond->debug_dir) {
- pr_warning("%s: Warning: failed to register to debugfs\n",
+ pr_warn("%s: Warning: failed to register to debugfs\n",
bond->dev->name);
return;
}
@@ -98,9 +98,8 @@ void bond_debug_reregister(struct bonding *bond)
if (d) {
bond->debug_dir = d;
} else {
- pr_warning("%s: Warning: failed to reregister, "
- "so just unregister old one\n",
- bond->dev->name);
+ pr_warn("%s: Warning: failed to reregister, so just unregister old one\n",
+ bond->dev->name);
bond_debug_unregister(bond);
}
}
@@ -110,8 +109,7 @@ void bond_create_debugfs(void)
bonding_debug_root = debugfs_create_dir("bonding", NULL);
if (!bonding_debug_root) {
- pr_warning("Warning: Cannot create bonding directory"
- " in debugfs\n");
+ pr_warn("Warning: Cannot create bonding directory in debugfs\n");
}
}
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index e5628fc725c3..95a6ca7d9e51 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -673,12 +673,12 @@ static void bond_do_fail_over_mac(struct bonding *bond,
write_unlock_bh(&bond->curr_slave_lock);
if (old_active) {
- memcpy(tmp_mac, new_active->dev->dev_addr, ETH_ALEN);
- memcpy(saddr.sa_data, old_active->dev->dev_addr,
- ETH_ALEN);
+ ether_addr_copy(tmp_mac, new_active->dev->dev_addr);
+ ether_addr_copy(saddr.sa_data,
+ old_active->dev->dev_addr);
saddr.sa_family = new_active->dev->type;
} else {
- memcpy(saddr.sa_data, bond->dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(saddr.sa_data, bond->dev->dev_addr);
saddr.sa_family = bond->dev->type;
}
@@ -692,7 +692,7 @@ static void bond_do_fail_over_mac(struct bonding *bond,
if (!old_active)
goto out;
- memcpy(saddr.sa_data, tmp_mac, ETH_ALEN);
+ ether_addr_copy(saddr.sa_data, tmp_mac);
saddr.sa_family = old_active->dev->type;
rv = dev_set_mac_address(old_active->dev, &saddr);
@@ -798,11 +798,11 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
return;
if (new_active) {
- new_active->jiffies = jiffies;
+ new_active->last_link_up = jiffies;
if (new_active->link == BOND_LINK_BACK) {
if (USES_PRIMARY(bond->params.mode)) {
- pr_info("%s: making interface %s the new active one %d ms earlier.\n",
+ pr_info("%s: making interface %s the new active one %d ms earlier\n",
bond->dev->name, new_active->dev->name,
(bond->params.updelay - new_active->delay) * bond->params.miimon);
}
@@ -817,7 +817,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP);
} else {
if (USES_PRIMARY(bond->params.mode)) {
- pr_info("%s: making interface %s the new active one.\n",
+ pr_info("%s: making interface %s the new active one\n",
bond->dev->name, new_active->dev->name);
}
}
@@ -910,7 +910,7 @@ void bond_select_active_slave(struct bonding *bond)
pr_info("%s: first active interface up!\n",
bond->dev->name);
} else {
- pr_info("%s: now running without any active interface !\n",
+ pr_info("%s: now running without any active interface!\n",
bond->dev->name);
}
}
@@ -922,12 +922,12 @@ static inline int slave_enable_netpoll(struct slave *slave)
struct netpoll *np;
int err = 0;
- np = kzalloc(sizeof(*np), GFP_ATOMIC);
+ np = kzalloc(sizeof(*np), GFP_KERNEL);
err = -ENOMEM;
if (!np)
goto out;
- err = __netpoll_setup(np, slave->dev, GFP_ATOMIC);
+ err = __netpoll_setup(np, slave->dev);
if (err) {
kfree(np);
goto out;
@@ -946,14 +946,6 @@ static inline void slave_disable_netpoll(struct slave *slave)
slave->np = NULL;
__netpoll_free_async(np);
}
-static inline bool slave_dev_support_netpoll(struct net_device *slave_dev)
-{
- if (slave_dev->priv_flags & IFF_DISABLE_NETPOLL)
- return false;
- if (!slave_dev->netdev_ops->ndo_poll_controller)
- return false;
- return true;
-}
static void bond_poll_controller(struct net_device *bond_dev)
{
@@ -970,7 +962,7 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
slave_disable_netpoll(slave);
}
-static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni, gfp_t gfp)
+static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni)
{
struct bonding *bond = netdev_priv(dev);
struct list_head *iter;
@@ -1119,9 +1111,6 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
slave = bond_slave_get_rcu(skb->dev);
bond = slave->bond;
- if (bond->params.arp_interval)
- slave->dev->last_rx = jiffies;
-
recv_probe = ACCESS_ONCE(bond->recv_probe);
if (recv_probe) {
ret = recv_probe(skb, bond, slave);
@@ -1146,7 +1135,7 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
kfree_skb(skb);
return RX_HANDLER_CONSUMED;
}
- memcpy(eth_hdr(skb)->h_dest, bond->dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(eth_hdr(skb)->h_dest, bond->dev->dev_addr);
}
return ret;
@@ -1187,13 +1176,13 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
if (!bond->params.use_carrier &&
slave_dev->ethtool_ops->get_link == NULL &&
slave_ops->ndo_do_ioctl == NULL) {
- pr_warning("%s: Warning: no link monitoring support for %s\n",
- bond_dev->name, slave_dev->name);
+ pr_warn("%s: Warning: no link monitoring support for %s\n",
+ bond_dev->name, slave_dev->name);
}
/* already enslaved */
if (slave_dev->flags & IFF_SLAVE) {
- pr_debug("Error, Device was already enslaved\n");
+ pr_debug("Error: Device was already enslaved\n");
return -EBUSY;
}
@@ -1211,9 +1200,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
bond_dev->name, slave_dev->name, bond_dev->name);
return -EPERM;
} else {
- pr_warning("%s: Warning: enslaved VLAN challenged slave %s. Adding VLANs will be blocked as long as %s is part of bond %s\n",
- bond_dev->name, slave_dev->name,
- slave_dev->name, bond_dev->name);
+ pr_warn("%s: Warning: enslaved VLAN challenged slave %s. Adding VLANs will be blocked as long as %s is part of bond %s\n",
+ bond_dev->name, slave_dev->name,
+ slave_dev->name, bond_dev->name);
}
} else {
pr_debug("%s: ! NETIF_F_VLAN_CHALLENGED\n", slave_dev->name);
@@ -1226,7 +1215,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
* enslaving it; the old ifenslave will not.
*/
if ((slave_dev->flags & IFF_UP)) {
- pr_err("%s is up. This may be due to an out of date ifenslave.\n",
+ pr_err("%s is up - this may be due to an out of date ifenslave\n",
slave_dev->name);
res = -EPERM;
goto err_undo_flags;
@@ -1270,24 +1259,23 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
bond_dev);
}
} else if (bond_dev->type != slave_dev->type) {
- pr_err("%s ether type (%d) is different from other slaves (%d), can not enslave it.\n",
- slave_dev->name,
- slave_dev->type, bond_dev->type);
+ pr_err("%s ether type (%d) is different from other slaves (%d), can not enslave it\n",
+ slave_dev->name, slave_dev->type, bond_dev->type);
res = -EINVAL;
goto err_undo_flags;
}
if (slave_ops->ndo_set_mac_address == NULL) {
if (!bond_has_slaves(bond)) {
- pr_warn("%s: Warning: The first slave device specified does not support setting the MAC address.\n",
+ pr_warn("%s: Warning: The first slave device specified does not support setting the MAC address\n",
bond_dev->name);
if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) {
bond->params.fail_over_mac = BOND_FOM_ACTIVE;
- pr_warn("%s: Setting fail_over_mac to active for active-backup mode.\n",
+ pr_warn("%s: Setting fail_over_mac to active for active-backup mode\n",
bond_dev->name);
}
} else if (bond->params.fail_over_mac != BOND_FOM_ACTIVE) {
- pr_err("%s: Error: The slave device specified does not support setting the MAC address, but fail_over_mac is not set to active.\n",
+ pr_err("%s: Error: The slave device specified does not support setting the MAC address, but fail_over_mac is not set to active\n",
bond_dev->name);
res = -EOPNOTSUPP;
goto err_undo_flags;
@@ -1326,7 +1314,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
* that need it, and for restoring it upon release, and then
* set it to the master's address
*/
- memcpy(new_slave->perm_hwaddr, slave_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(new_slave->perm_hwaddr, slave_dev->dev_addr);
if (!bond->params.fail_over_mac ||
bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
@@ -1410,10 +1398,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
bond_update_speed_duplex(new_slave);
- new_slave->last_arp_rx = jiffies -
+ new_slave->last_rx = jiffies -
(msecs_to_jiffies(bond->params.arp_interval) + 1);
for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
- new_slave->target_last_arp_rx[i] = new_slave->last_arp_rx;
+ new_slave->target_last_arp_rx[i] = new_slave->last_rx;
if (bond->params.miimon && !bond->params.use_carrier) {
link_reporting = bond_check_dev_link(bond, slave_dev, 1);
@@ -1428,12 +1416,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
* supported); thus, we don't need to change
* the messages for netif_carrier.
*/
- pr_warning("%s: Warning: MII and ETHTOOL support not available for interface %s, and arp_interval/arp_ip_target module parameters not specified, thus bonding will not detect link failures! see bonding.txt for details.\n",
- bond_dev->name, slave_dev->name);
+ pr_warn("%s: Warning: MII and ETHTOOL support not available for interface %s, and arp_interval/arp_ip_target module parameters not specified, thus bonding will not detect link failures! see bonding.txt for details\n",
+ bond_dev->name, slave_dev->name);
} else if (link_reporting == -1) {
/* unable get link status using mii/ethtool */
- pr_warning("%s: Warning: can't get link status from interface %s; the network driver associated with this interface does not support MII or ETHTOOL link status reporting, thus miimon has no effect on this interface.\n",
- bond_dev->name, slave_dev->name);
+ pr_warn("%s: Warning: can't get link status from interface %s; the network driver associated with this interface does not support MII or ETHTOOL link status reporting, thus miimon has no effect on this interface\n",
+ bond_dev->name, slave_dev->name);
}
}
@@ -1457,10 +1445,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
}
if (new_slave->link != BOND_LINK_DOWN)
- new_slave->jiffies = jiffies;
+ new_slave->last_link_up = jiffies;
pr_debug("Initial state of slave_dev is BOND_LINK_%s\n",
- new_slave->link == BOND_LINK_DOWN ? "DOWN" :
- (new_slave->link == BOND_LINK_UP ? "UP" : "BACK"));
+ new_slave->link == BOND_LINK_DOWN ? "DOWN" :
+ (new_slave->link == BOND_LINK_UP ? "UP" : "BACK"));
if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) {
/* if there is a primary slave, remember it */
@@ -1520,9 +1508,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
slave_dev->npinfo = bond->dev->npinfo;
if (slave_dev->npinfo) {
if (slave_enable_netpoll(new_slave)) {
- pr_info("Error, %s: master_dev is using netpoll, "
- "but new slave device does not support netpoll.\n",
- bond_dev->name);
+ pr_info("Error, %s: master_dev is using netpoll, but new slave device does not support netpoll\n",
+ bond_dev->name);
res = -EBUSY;
goto err_detach;
}
@@ -1560,10 +1547,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
unblock_netpoll_tx();
}
- pr_info("%s: enslaving %s as a%s interface with a%s link.\n",
+ pr_info("%s: Enslaving %s as %s interface with %s link\n",
bond_dev->name, slave_dev->name,
- bond_is_active_slave(new_slave) ? "n active" : " backup",
- new_slave->link != BOND_LINK_DOWN ? "n up" : " down");
+ bond_is_active_slave(new_slave) ? "an active" : "a backup",
+ new_slave->link != BOND_LINK_DOWN ? "an up" : "a down");
/* enslave is successful */
return 0;
@@ -1603,7 +1590,7 @@ err_restore_mac:
* MAC if this slave's MAC is in use by the bond, or at
* least print a warning.
*/
- memcpy(addr.sa_data, new_slave->perm_hwaddr, ETH_ALEN);
+ ether_addr_copy(addr.sa_data, new_slave->perm_hwaddr);
addr.sa_family = slave_dev->type;
dev_set_mac_address(slave_dev, &addr);
}
@@ -1648,7 +1635,7 @@ static int __bond_release_one(struct net_device *bond_dev,
/* slave is not a slave or master is not master of this slave */
if (!(slave_dev->flags & IFF_SLAVE) ||
!netdev_has_upper_dev(slave_dev, bond_dev)) {
- pr_err("%s: Error: cannot release %s.\n",
+ pr_err("%s: Error: cannot release %s\n",
bond_dev->name, slave_dev->name);
return -EINVAL;
}
@@ -1679,7 +1666,7 @@ static int __bond_release_one(struct net_device *bond_dev,
write_unlock_bh(&bond->lock);
- pr_info("%s: releasing %s interface %s\n",
+ pr_info("%s: Releasing %s interface %s\n",
bond_dev->name,
bond_is_active_slave(slave) ? "active" : "backup",
slave_dev->name);
@@ -1692,10 +1679,10 @@ static int __bond_release_one(struct net_device *bond_dev,
bond->params.mode != BOND_MODE_ACTIVEBACKUP)) {
if (ether_addr_equal_64bits(bond_dev->dev_addr, slave->perm_hwaddr) &&
bond_has_slaves(bond))
- pr_warn("%s: Warning: the permanent HWaddr of %s - %pM - is still in use by %s. Set the HWaddr of %s to a different address to avoid conflicts.\n",
- bond_dev->name, slave_dev->name,
- slave->perm_hwaddr,
- bond_dev->name, slave_dev->name);
+ pr_warn("%s: Warning: the permanent HWaddr of %s - %pM - is still in use by %s - set the HWaddr of %s to a different address to avoid conflicts\n",
+ bond_dev->name, slave_dev->name,
+ slave->perm_hwaddr,
+ bond_dev->name, slave_dev->name);
}
if (bond->primary_slave == slave)
@@ -1736,10 +1723,10 @@ static int __bond_release_one(struct net_device *bond_dev,
eth_hw_addr_random(bond_dev);
if (vlan_uses_dev(bond_dev)) {
- pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n",
- bond_dev->name, bond_dev->name);
- pr_warning("%s: When re-adding slaves, make sure the bond's HW address matches its VLANs'.\n",
- bond_dev->name);
+ pr_warn("%s: Warning: clearing HW address of %s while it still has VLANs\n",
+ bond_dev->name, bond_dev->name);
+ pr_warn("%s: When re-adding slaves, make sure the bond's HW address matches its VLANs\n",
+ bond_dev->name);
}
}
@@ -1755,7 +1742,7 @@ static int __bond_release_one(struct net_device *bond_dev,
bond_compute_features(bond);
if (!(bond_dev->features & NETIF_F_VLAN_CHALLENGED) &&
(old_features & NETIF_F_VLAN_CHALLENGED))
- pr_info("%s: last VLAN challenged slave %s left bond %s. VLAN blocking is removed\n",
+ pr_info("%s: last VLAN challenged slave %s left bond %s - VLAN blocking is removed\n",
bond_dev->name, slave_dev->name, bond_dev->name);
/* must do this from outside any spinlocks */
@@ -1790,7 +1777,7 @@ static int __bond_release_one(struct net_device *bond_dev,
if (bond->params.fail_over_mac != BOND_FOM_ACTIVE ||
bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
/* restore original ("permanent") mac address */
- memcpy(addr.sa_data, slave->perm_hwaddr, ETH_ALEN);
+ ether_addr_copy(addr.sa_data, slave->perm_hwaddr);
addr.sa_family = slave_dev->type;
dev_set_mac_address(slave_dev, &addr);
}
@@ -1823,7 +1810,7 @@ static int bond_release_and_destroy(struct net_device *bond_dev,
ret = bond_release(bond_dev, slave_dev);
if (ret == 0 && !bond_has_slaves(bond)) {
bond_dev->priv_flags |= IFF_DISABLE_NETPOLL;
- pr_info("%s: destroying bond %s.\n",
+ pr_info("%s: Destroying bond %s\n",
bond_dev->name, bond_dev->name);
unregister_netdevice(bond_dev);
}
@@ -1837,9 +1824,7 @@ static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
info->bond_mode = bond->params.mode;
info->miimon = bond->params.miimon;
- read_lock(&bond->lock);
info->num_slaves = bond->slave_cnt;
- read_unlock(&bond->lock);
return 0;
}
@@ -1851,7 +1836,6 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in
int i = 0, res = -ENODEV;
struct slave *slave;
- read_lock(&bond->lock);
bond_for_each_slave(bond, slave, iter) {
if (i++ == (int)info->slave_id) {
res = 0;
@@ -1862,7 +1846,6 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in
break;
}
}
- read_unlock(&bond->lock);
return res;
}
@@ -1892,7 +1875,7 @@ static int bond_miimon_inspect(struct bonding *bond)
slave->link = BOND_LINK_FAIL;
slave->delay = bond->params.downdelay;
if (slave->delay) {
- pr_info("%s: link status down for %sinterface %s, disabling it in %d ms.\n",
+ pr_info("%s: link status down for %sinterface %s, disabling it in %d ms\n",
bond->dev->name,
(bond->params.mode ==
BOND_MODE_ACTIVEBACKUP) ?
@@ -1908,8 +1891,8 @@ static int bond_miimon_inspect(struct bonding *bond)
* recovered before downdelay expired
*/
slave->link = BOND_LINK_UP;
- slave->jiffies = jiffies;
- pr_info("%s: link status up again after %d ms for interface %s.\n",
+ slave->last_link_up = jiffies;
+ pr_info("%s: link status up again after %d ms for interface %s\n",
bond->dev->name,
(bond->params.downdelay - slave->delay) *
bond->params.miimon,
@@ -1934,7 +1917,7 @@ static int bond_miimon_inspect(struct bonding *bond)
slave->delay = bond->params.updelay;
if (slave->delay) {
- pr_info("%s: link status up for interface %s, enabling it in %d ms.\n",
+ pr_info("%s: link status up for interface %s, enabling it in %d ms\n",
bond->dev->name, slave->dev->name,
ignore_updelay ? 0 :
bond->params.updelay *
@@ -1944,7 +1927,7 @@ static int bond_miimon_inspect(struct bonding *bond)
case BOND_LINK_BACK:
if (!link_state) {
slave->link = BOND_LINK_DOWN;
- pr_info("%s: link status down again after %d ms for interface %s.\n",
+ pr_info("%s: link status down again after %d ms for interface %s\n",
bond->dev->name,
(bond->params.updelay - slave->delay) *
bond->params.miimon,
@@ -1983,7 +1966,7 @@ static void bond_miimon_commit(struct bonding *bond)
case BOND_LINK_UP:
slave->link = BOND_LINK_UP;
- slave->jiffies = jiffies;
+ slave->last_link_up = jiffies;
if (bond->params.mode == BOND_MODE_8023AD) {
/* prevent it from being the active one */
@@ -1996,7 +1979,7 @@ static void bond_miimon_commit(struct bonding *bond)
bond_set_backup_slave(slave);
}
- pr_info("%s: link status definitely up for interface %s, %u Mbps %s duplex.\n",
+ pr_info("%s: link status definitely up for interface %s, %u Mbps %s duplex\n",
bond->dev->name, slave->dev->name,
slave->speed == SPEED_UNKNOWN ? 0 : slave->speed,
slave->duplex ? "full" : "half");
@@ -2141,24 +2124,40 @@ static bool bond_has_this_ip(struct bonding *bond, __be32 ip)
* switches in VLAN mode (especially if ports are configured as
* "native" to a VLAN) might not pass non-tagged frames.
*/
-static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_ip, __be32 src_ip, unsigned short vlan_id)
+static void bond_arp_send(struct net_device *slave_dev, int arp_op,
+ __be32 dest_ip, __be32 src_ip,
+ struct bond_vlan_tag *inner,
+ struct bond_vlan_tag *outer)
{
struct sk_buff *skb;
- pr_debug("arp %d on slave %s: dst %pI4 src %pI4 vid %d\n", arp_op,
- slave_dev->name, &dest_ip, &src_ip, vlan_id);
+ pr_debug("arp %d on slave %s: dst %pI4 src %pI4\n",
+ arp_op, slave_dev->name, &dest_ip, &src_ip);
skb = arp_create(arp_op, ETH_P_ARP, dest_ip, slave_dev, src_ip,
NULL, slave_dev->dev_addr, NULL);
if (!skb) {
- pr_err("ARP packet allocation failed\n");
+ net_err_ratelimited("ARP packet allocation failed\n");
return;
}
- if (vlan_id) {
- skb = vlan_put_tag(skb, htons(ETH_P_8021Q), vlan_id);
+ if (outer->vlan_id) {
+ if (inner->vlan_id) {
+ pr_debug("inner tag: proto %X vid %X\n",
+ ntohs(inner->vlan_proto), inner->vlan_id);
+ skb = __vlan_put_tag(skb, inner->vlan_proto,
+ inner->vlan_id);
+ if (!skb) {
+ net_err_ratelimited("failed to insert inner VLAN tag\n");
+ return;
+ }
+ }
+
+ pr_debug("outer reg: proto %X vid %X\n",
+ ntohs(outer->vlan_proto), outer->vlan_id);
+ skb = vlan_put_tag(skb, outer->vlan_proto, outer->vlan_id);
if (!skb) {
- pr_err("failed to insert VLAN tag\n");
+ net_err_ratelimited("failed to insert outer VLAN tag\n");
return;
}
}
@@ -2171,23 +2170,32 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
struct net_device *upper, *vlan_upper;
struct list_head *iter, *vlan_iter;
struct rtable *rt;
+ struct bond_vlan_tag inner, outer;
__be32 *targets = bond->params.arp_targets, addr;
- int i, vlan_id;
+ int i;
for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i]; i++) {
pr_debug("basa: target %pI4\n", &targets[i]);
+ inner.vlan_proto = 0;
+ inner.vlan_id = 0;
+ outer.vlan_proto = 0;
+ outer.vlan_id = 0;
/* Find out through which dev should the packet go */
rt = ip_route_output(dev_net(bond->dev), targets[i], 0,
RTO_ONLINK, 0);
if (IS_ERR(rt)) {
- pr_debug("%s: no route to arp_ip_target %pI4\n",
- bond->dev->name, &targets[i]);
+ /* there's no route to target - try to send arp
+ * probe to generate any traffic (arp_validate=0)
+ */
+ if (bond->params.arp_validate)
+ net_warn_ratelimited("%s: no route to arp_ip_target %pI4 and arp_validate is set\n",
+ bond->dev->name,
+ &targets[i]);
+ bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], 0, &inner, &outer);
continue;
}
- vlan_id = 0;
-
/* bond device itself */
if (rt->dst.dev == bond->dev)
goto found;
@@ -2197,17 +2205,30 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
* found we verify its upper dev list, searching for the
* rt->dst.dev. If found we save the tag of the vlan and
* proceed to send the packet.
- *
- * TODO: QinQ?
*/
netdev_for_each_all_upper_dev_rcu(bond->dev, vlan_upper,
vlan_iter) {
if (!is_vlan_dev(vlan_upper))
continue;
+
+ if (vlan_upper == rt->dst.dev) {
+ outer.vlan_proto = vlan_dev_vlan_proto(vlan_upper);
+ outer.vlan_id = vlan_dev_vlan_id(vlan_upper);
+ rcu_read_unlock();
+ goto found;
+ }
netdev_for_each_all_upper_dev_rcu(vlan_upper, upper,
iter) {
if (upper == rt->dst.dev) {
- vlan_id = vlan_dev_vlan_id(vlan_upper);
+ /* If the upper dev is a vlan dev too,
+ * set the vlan tag to inner tag.
+ */
+ if (is_vlan_dev(upper)) {
+ inner.vlan_proto = vlan_dev_vlan_proto(upper);
+ inner.vlan_id = vlan_dev_vlan_id(upper);
+ }
+ outer.vlan_proto = vlan_dev_vlan_proto(vlan_upper);
+ outer.vlan_id = vlan_dev_vlan_id(vlan_upper);
rcu_read_unlock();
goto found;
}
@@ -2220,10 +2241,6 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
*/
netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) {
if (upper == rt->dst.dev) {
- /* if it's a vlan - get its VID */
- if (is_vlan_dev(upper))
- vlan_id = vlan_dev_vlan_id(upper);
-
rcu_read_unlock();
goto found;
}
@@ -2242,7 +2259,7 @@ found:
addr = bond_confirm_addr(rt->dst.dev, targets[i], 0);
ip_rt_put(rt);
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
- addr, vlan_id);
+ addr, &inner, &outer);
}
}
@@ -2260,7 +2277,7 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
pr_debug("bva: sip %pI4 not found in targets\n", &sip);
return;
}
- slave->last_arp_rx = jiffies;
+ slave->last_rx = jiffies;
slave->target_last_arp_rx[i] = jiffies;
}
@@ -2268,17 +2285,19 @@ int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
struct slave *slave)
{
struct arphdr *arp = (struct arphdr *)skb->data;
+ struct slave *curr_active_slave;
unsigned char *arp_ptr;
__be32 sip, tip;
- int alen;
+ int alen, is_arp = skb->protocol == __cpu_to_be16(ETH_P_ARP);
- if (skb->protocol != __cpu_to_be16(ETH_P_ARP))
+ if (!slave_do_arp_validate(bond, slave)) {
+ if ((slave_do_arp_validate_only(bond, slave) && is_arp) ||
+ !slave_do_arp_validate_only(bond, slave))
+ slave->last_rx = jiffies;
return RX_HANDLER_ANOTHER;
-
- read_lock(&bond->lock);
-
- if (!slave_do_arp_validate(bond, slave))
- goto out_unlock;
+ } else if (!is_arp) {
+ return RX_HANDLER_ANOTHER;
+ }
alen = arp_hdr_len(bond->dev);
@@ -2312,6 +2331,8 @@ int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
bond->params.arp_validate, slave_do_arp_validate(bond, slave),
&sip, &tip);
+ curr_active_slave = rcu_dereference(bond->curr_active_slave);
+
/*
* Backup slaves won't see the ARP reply, but do come through
* here for each ARP probe (so we swap the sip/tip to validate
@@ -2325,15 +2346,15 @@ int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
* is done to avoid endless looping when we can't reach the
* arp_ip_target and fool ourselves with our own arp requests.
*/
+
if (bond_is_active_slave(slave))
bond_validate_arp(bond, slave, sip, tip);
- else if (bond->curr_active_slave &&
- time_after(slave_last_rx(bond, bond->curr_active_slave),
- bond->curr_active_slave->jiffies))
+ else if (curr_active_slave &&
+ time_after(slave_last_rx(bond, curr_active_slave),
+ curr_active_slave->last_link_up))
bond_validate_arp(bond, slave, tip, sip);
out_unlock:
- read_unlock(&bond->lock);
if (arp != (struct arphdr *)skb->data)
kfree(arp);
return RX_HANDLER_ANOTHER;
@@ -2376,9 +2397,9 @@ static void bond_loadbalance_arp_mon(struct work_struct *work)
oldcurrent = ACCESS_ONCE(bond->curr_active_slave);
/* see if any of the previous devices are up now (i.e. they have
* xmt and rcv traffic). the curr_active_slave does not come into
- * the picture unless it is null. also, slave->jiffies is not needed
- * here because we send an arp on each slave and give a slave as
- * long as it needs to get the tx/rx within the delta.
+ * the picture unless it is null. also, slave->last_link_up is not
+ * needed here because we send an arp on each slave and give a slave
+ * as long as it needs to get the tx/rx within the delta.
* TODO: what about up/down delay in arp mode? it wasn't here before
* so it can wait
*/
@@ -2387,7 +2408,7 @@ static void bond_loadbalance_arp_mon(struct work_struct *work)
if (slave->link != BOND_LINK_UP) {
if (bond_time_in_interval(bond, trans_start, 1) &&
- bond_time_in_interval(bond, slave->dev->last_rx, 1)) {
+ bond_time_in_interval(bond, slave->last_rx, 1)) {
slave->link = BOND_LINK_UP;
slave_state_changed = 1;
@@ -2398,7 +2419,7 @@ static void bond_loadbalance_arp_mon(struct work_struct *work)
* is closed.
*/
if (!oldcurrent) {
- pr_info("%s: link status definitely up for interface %s, ",
+ pr_info("%s: link status definitely up for interface %s\n",
bond->dev->name,
slave->dev->name);
do_failover = 1;
@@ -2416,7 +2437,7 @@ static void bond_loadbalance_arp_mon(struct work_struct *work)
* if we don't know our ip yet
*/
if (!bond_time_in_interval(bond, trans_start, 2) ||
- !bond_time_in_interval(bond, slave->dev->last_rx, 2)) {
+ !bond_time_in_interval(bond, slave->last_rx, 2)) {
slave->link = BOND_LINK_DOWN;
slave_state_changed = 1;
@@ -2424,9 +2445,8 @@ static void bond_loadbalance_arp_mon(struct work_struct *work)
if (slave->link_failure_count < UINT_MAX)
slave->link_failure_count++;
- pr_info("%s: interface %s is now down.\n",
- bond->dev->name,
- slave->dev->name);
+ pr_info("%s: interface %s is now down\n",
+ bond->dev->name, slave->dev->name);
if (slave == oldcurrent)
do_failover = 1;
@@ -2505,7 +2525,7 @@ static int bond_ab_arp_inspect(struct bonding *bond)
* active. This avoids bouncing, as the last receive
* times need a full ARP monitor cycle to be updated.
*/
- if (bond_time_in_interval(bond, slave->jiffies, 2))
+ if (bond_time_in_interval(bond, slave->last_link_up, 2))
continue;
/*
@@ -2576,7 +2596,7 @@ static void bond_ab_arp_commit(struct bonding *bond)
bond->current_arp_slave = NULL;
}
- pr_info("%s: link status definitely up for interface %s.\n",
+ pr_info("%s: link status definitely up for interface %s\n",
bond->dev->name, slave->dev->name);
if (!bond->curr_active_slave ||
@@ -2682,7 +2702,7 @@ static bool bond_ab_arp_probe(struct bonding *bond)
bond_set_slave_inactive_flags(slave,
BOND_SLAVE_NOTIFY_LATER);
- pr_info("%s: backup interface %s is now down.\n",
+ pr_info("%s: backup interface %s is now down\n",
bond->dev->name, slave->dev->name);
}
if (slave == curr_arp_slave)
@@ -2698,7 +2718,7 @@ static bool bond_ab_arp_probe(struct bonding *bond)
new_slave->link = BOND_LINK_BACK;
bond_set_slave_active_flags(new_slave, BOND_SLAVE_NOTIFY_LATER);
bond_arp_send_all(bond, new_slave);
- new_slave->jiffies = jiffies;
+ new_slave->last_link_up = jiffies;
rcu_assign_pointer(bond->current_arp_slave, new_slave);
check_state:
@@ -2879,9 +2899,9 @@ static int bond_slave_netdev_event(unsigned long event,
break;
}
- pr_info("%s: Primary slave changed to %s, reselecting active slave.\n",
- bond->dev->name, bond->primary_slave ? slave_dev->name :
- "none");
+ pr_info("%s: Primary slave changed to %s, reselecting active slave\n",
+ bond->dev->name,
+ bond->primary_slave ? slave_dev->name : "none");
block_netpoll_tx();
write_lock_bh(&bond->curr_slave_lock);
@@ -2917,8 +2937,7 @@ static int bond_netdev_event(struct notifier_block *this,
struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
pr_debug("event_dev: %s, event: %lx\n",
- event_dev ? event_dev->name : "None",
- event);
+ event_dev ? event_dev->name : "None", event);
if (!(event_dev->priv_flags & IFF_BONDING))
return NOTIFY_DONE;
@@ -2967,7 +2986,7 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
fk->ports = 0;
noff = skb_network_offset(skb);
if (skb->protocol == htons(ETH_P_IP)) {
- if (!pskb_may_pull(skb, noff + sizeof(*iph)))
+ if (unlikely(!pskb_may_pull(skb, noff + sizeof(*iph))))
return false;
iph = ip_hdr(skb);
fk->src = iph->saddr;
@@ -2976,7 +2995,7 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
if (!ip_is_fragment(iph))
proto = iph->protocol;
} else if (skb->protocol == htons(ETH_P_IPV6)) {
- if (!pskb_may_pull(skb, noff + sizeof(*iph6)))
+ if (unlikely(!pskb_may_pull(skb, noff + sizeof(*iph6))))
return false;
iph6 = ipv6_hdr(skb);
fk->src = (__force __be32)ipv6_addr_hash(&iph6->saddr);
@@ -3087,8 +3106,7 @@ static int bond_open(struct net_device *bond_dev)
if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
queue_delayed_work(bond->wq, &bond->arp_work, 0);
- if (bond->params.arp_validate)
- bond->recv_probe = bond_arp_rcv;
+ bond->recv_probe = bond_arp_rcv;
}
if (bond->params.mode == BOND_MODE_8023AD) {
@@ -3375,8 +3393,8 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
struct list_head *iter;
int res = 0;
- pr_debug("bond=%p, name=%s, new_mtu=%d\n", bond,
- (bond_dev ? bond_dev->name : "None"), new_mtu);
+ pr_debug("bond=%p, name=%s, new_mtu=%d\n",
+ bond, bond_dev ? bond_dev->name : "None", new_mtu);
/* Can't hold bond->lock with bh disabled here since
* some base drivers panic. On the other hand we can't
@@ -3395,8 +3413,7 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
bond_for_each_slave(bond, slave, iter) {
pr_debug("s %p c_m %p\n",
- slave,
- slave->dev->netdev_ops->ndo_change_mtu);
+ slave, slave->dev->netdev_ops->ndo_change_mtu);
res = dev_set_mtu(slave->dev, new_mtu);
@@ -3484,15 +3501,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
*/
bond_for_each_slave(bond, slave, iter) {
- const struct net_device_ops *slave_ops = slave->dev->netdev_ops;
pr_debug("slave %p %s\n", slave, slave->dev->name);
-
- if (slave_ops->ndo_set_mac_address == NULL) {
- res = -EOPNOTSUPP;
- pr_debug("EOPNOTSUPP %s\n", slave->dev->name);
- goto unwind;
- }
-
res = dev_set_mac_address(slave->dev, addr);
if (res) {
/* TODO: consider downing the slave
@@ -3568,7 +3577,7 @@ static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int sl
}
}
/* no slave that can tx has been found */
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
}
/**
@@ -3644,7 +3653,7 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d
if (slave)
bond_dev_queue_xmit(bond, skb, slave->dev);
else
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -3676,8 +3685,8 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (!skb2) {
- pr_err("%s: Error: bond_xmit_broadcast(): skb_clone() failed\n",
- bond_dev->name);
+ net_err_ratelimited("%s: Error: %s: skb_clone() failed\n",
+ bond_dev->name, __func__);
continue;
}
/* bond_dev_queue_xmit always returns 0 */
@@ -3687,7 +3696,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
if (slave && IS_UP(slave->dev) && slave->link == BOND_LINK_UP)
bond_dev_queue_xmit(bond, skb, slave->dev);
else
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -3774,7 +3783,7 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
pr_err("%s: Error: Unknown bonding mode %d\n",
dev->name, bond->params.mode);
WARN_ON_ONCE(1);
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
}
@@ -3788,14 +3797,14 @@ static netdev_tx_t bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
* If we risk deadlock from transmitting this in the
* netpoll path, tell netpoll to queue the frame for later tx
*/
- if (is_netpoll_tx_blocked(dev))
+ if (unlikely(is_netpoll_tx_blocked(dev)))
return NETDEV_TX_BUSY;
rcu_read_lock();
if (bond_has_slaves(bond))
ret = __bond_start_xmit(skb, dev);
else
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
rcu_read_unlock();
return ret;
@@ -3958,7 +3967,7 @@ static void bond_uninit(struct net_device *bond_dev)
/* Release the bonded slaves */
bond_for_each_slave(bond, slave, iter)
__bond_release_one(bond_dev, slave->dev, true);
- pr_info("%s: released all slaves\n", bond_dev->name);
+ pr_info("%s: Released all slaves\n", bond_dev->name);
list_del(&bond->bond_list);
@@ -3967,56 +3976,11 @@ static void bond_uninit(struct net_device *bond_dev)
/*------------------------- Module initialization ---------------------------*/
-int bond_parm_tbl_lookup(int mode, const struct bond_parm_tbl *tbl)
-{
- int i;
-
- for (i = 0; tbl[i].modename; i++)
- if (mode == tbl[i].mode)
- return tbl[i].mode;
-
- return -1;
-}
-
-static int bond_parm_tbl_lookup_name(const char *modename,
- const struct bond_parm_tbl *tbl)
-{
- int i;
-
- for (i = 0; tbl[i].modename; i++)
- if (strcmp(modename, tbl[i].modename) == 0)
- return tbl[i].mode;
-
- return -1;
-}
-
-/*
- * Convert string input module parms. Accept either the
- * number of the mode or its string name. A bit complicated because
- * some mode names are substrings of other names, and calls from sysfs
- * may have whitespace in the name (trailing newlines, for example).
- */
-int bond_parse_parm(const char *buf, const struct bond_parm_tbl *tbl)
-{
- int modeint;
- char *p, modestr[BOND_MAX_MODENAME_LEN + 1];
-
- for (p = (char *)buf; *p; p++)
- if (!(isdigit(*p) || isspace(*p)))
- break;
-
- if (*p && sscanf(buf, "%20s", modestr) != 0)
- return bond_parm_tbl_lookup_name(modestr, tbl);
- else if (sscanf(buf, "%d", &modeint) != 0)
- return bond_parm_tbl_lookup(modeint, tbl);
-
- return -1;
-}
-
static int bond_check_params(struct bond_params *params)
{
int arp_validate_value, fail_over_mac_value, primary_reselect_value, i;
- struct bond_opt_value newval, *valptr;
+ struct bond_opt_value newval;
+ const struct bond_opt_value *valptr;
int arp_all_targets_value;
/*
@@ -4036,7 +4000,7 @@ static int bond_check_params(struct bond_params *params)
if ((bond_mode != BOND_MODE_XOR) &&
(bond_mode != BOND_MODE_8023AD)) {
pr_info("xmit_hash_policy param is irrelevant in mode %s\n",
- bond_mode_name(bond_mode));
+ bond_mode_name(bond_mode));
} else {
bond_opt_initstr(&newval, xmit_hash_policy);
valptr = bond_opt_parse(bond_opt_get(BOND_OPT_XMIT_HASH),
@@ -4077,74 +4041,71 @@ static int bond_check_params(struct bond_params *params)
}
params->ad_select = valptr->value;
if (bond_mode != BOND_MODE_8023AD)
- pr_warning("ad_select param only affects 802.3ad mode\n");
+ pr_warn("ad_select param only affects 802.3ad mode\n");
} else {
params->ad_select = BOND_AD_STABLE;
}
if (max_bonds < 0) {
- pr_warning("Warning: max_bonds (%d) not in range %d-%d, so it was reset to BOND_DEFAULT_MAX_BONDS (%d)\n",
- max_bonds, 0, INT_MAX, BOND_DEFAULT_MAX_BONDS);
+ pr_warn("Warning: max_bonds (%d) not in range %d-%d, so it was reset to BOND_DEFAULT_MAX_BONDS (%d)\n",
+ max_bonds, 0, INT_MAX, BOND_DEFAULT_MAX_BONDS);
max_bonds = BOND_DEFAULT_MAX_BONDS;
}
if (miimon < 0) {
- pr_warning("Warning: miimon module parameter (%d), not in range 0-%d, so it was reset to 0\n",
- miimon, INT_MAX);
+ pr_warn("Warning: miimon module parameter (%d), not in range 0-%d, so it was reset to 0\n",
+ miimon, INT_MAX);
miimon = 0;
}
if (updelay < 0) {
- pr_warning("Warning: updelay module parameter (%d), not in range 0-%d, so it was reset to 0\n",
- updelay, INT_MAX);
+ pr_warn("Warning: updelay module parameter (%d), not in range 0-%d, so it was reset to 0\n",
+ updelay, INT_MAX);
updelay = 0;
}
if (downdelay < 0) {
- pr_warning("Warning: downdelay module parameter (%d), not in range 0-%d, so it was reset to 0\n",
- downdelay, INT_MAX);
+ pr_warn("Warning: downdelay module parameter (%d), not in range 0-%d, so it was reset to 0\n",
+ downdelay, INT_MAX);
downdelay = 0;
}
if ((use_carrier != 0) && (use_carrier != 1)) {
- pr_warning("Warning: use_carrier module parameter (%d), not of valid value (0/1), so it was set to 1\n",
- use_carrier);
+ pr_warn("Warning: use_carrier module parameter (%d), not of valid value (0/1), so it was set to 1\n",
+ use_carrier);
use_carrier = 1;
}
if (num_peer_notif < 0 || num_peer_notif > 255) {
- pr_warning("Warning: num_grat_arp/num_unsol_na (%d) not in range 0-255 so it was reset to 1\n",
- num_peer_notif);
+ pr_warn("Warning: num_grat_arp/num_unsol_na (%d) not in range 0-255 so it was reset to 1\n",
+ num_peer_notif);
num_peer_notif = 1;
}
/* reset values for 802.3ad/TLB/ALB */
if (BOND_NO_USES_ARP(bond_mode)) {
if (!miimon) {
- pr_warning("Warning: miimon must be specified, otherwise bonding will not detect link failure, speed and duplex which are essential for 802.3ad operation\n");
- pr_warning("Forcing miimon to 100msec\n");
+ pr_warn("Warning: miimon must be specified, otherwise bonding will not detect link failure, speed and duplex which are essential for 802.3ad operation\n");
+ pr_warn("Forcing miimon to 100msec\n");
miimon = BOND_DEFAULT_MIIMON;
}
}
if (tx_queues < 1 || tx_queues > 255) {
- pr_warning("Warning: tx_queues (%d) should be between "
- "1 and 255, resetting to %d\n",
- tx_queues, BOND_DEFAULT_TX_QUEUES);
+ pr_warn("Warning: tx_queues (%d) should be between 1 and 255, resetting to %d\n",
+ tx_queues, BOND_DEFAULT_TX_QUEUES);
tx_queues = BOND_DEFAULT_TX_QUEUES;
}
if ((all_slaves_active != 0) && (all_slaves_active != 1)) {
- pr_warning("Warning: all_slaves_active module parameter (%d), "
- "not of valid value (0/1), so it was set to "
- "0\n", all_slaves_active);
+ pr_warn("Warning: all_slaves_active module parameter (%d), not of valid value (0/1), so it was set to 0\n",
+ all_slaves_active);
all_slaves_active = 0;
}
if (resend_igmp < 0 || resend_igmp > 255) {
- pr_warning("Warning: resend_igmp (%d) should be between "
- "0 and 255, resetting to %d\n",
- resend_igmp, BOND_DEFAULT_RESEND_IGMP);
+ pr_warn("Warning: resend_igmp (%d) should be between 0 and 255, resetting to %d\n",
+ resend_igmp, BOND_DEFAULT_RESEND_IGMP);
resend_igmp = BOND_DEFAULT_RESEND_IGMP;
}
@@ -4165,37 +4126,36 @@ static int bond_check_params(struct bond_params *params)
/* just warn the user the up/down delay will have
* no effect since miimon is zero...
*/
- pr_warning("Warning: miimon module parameter not set and updelay (%d) or downdelay (%d) module parameter is set; updelay and downdelay have no effect unless miimon is set\n",
- updelay, downdelay);
+ pr_warn("Warning: miimon module parameter not set and updelay (%d) or downdelay (%d) module parameter is set; updelay and downdelay have no effect unless miimon is set\n",
+ updelay, downdelay);
}
} else {
/* don't allow arp monitoring */
if (arp_interval) {
- pr_warning("Warning: miimon (%d) and arp_interval (%d) can't be used simultaneously, disabling ARP monitoring\n",
- miimon, arp_interval);
+ pr_warn("Warning: miimon (%d) and arp_interval (%d) can't be used simultaneously, disabling ARP monitoring\n",
+ miimon, arp_interval);
arp_interval = 0;
}
if ((updelay % miimon) != 0) {
- pr_warning("Warning: updelay (%d) is not a multiple of miimon (%d), updelay rounded to %d ms\n",
- updelay, miimon,
- (updelay / miimon) * miimon);
+ pr_warn("Warning: updelay (%d) is not a multiple of miimon (%d), updelay rounded to %d ms\n",
+ updelay, miimon, (updelay / miimon) * miimon);
}
updelay /= miimon;
if ((downdelay % miimon) != 0) {
- pr_warning("Warning: downdelay (%d) is not a multiple of miimon (%d), downdelay rounded to %d ms\n",
- downdelay, miimon,
- (downdelay / miimon) * miimon);
+ pr_warn("Warning: downdelay (%d) is not a multiple of miimon (%d), downdelay rounded to %d ms\n",
+ downdelay, miimon,
+ (downdelay / miimon) * miimon);
}
downdelay /= miimon;
}
if (arp_interval < 0) {
- pr_warning("Warning: arp_interval module parameter (%d) , not in range 0-%d, so it was reset to 0\n",
- arp_interval, INT_MAX);
+ pr_warn("Warning: arp_interval module parameter (%d), not in range 0-%d, so it was reset to 0\n",
+ arp_interval, INT_MAX);
arp_interval = 0;
}
@@ -4206,30 +4166,26 @@ static int bond_check_params(struct bond_params *params)
__be32 ip;
if (!in4_pton(arp_ip_target[i], -1, (u8 *)&ip, -1, NULL) ||
IS_IP_TARGET_UNUSABLE_ADDRESS(ip)) {
- pr_warning("Warning: bad arp_ip_target module parameter (%s), ARP monitoring will not be performed\n",
- arp_ip_target[i]);
+ pr_warn("Warning: bad arp_ip_target module parameter (%s), ARP monitoring will not be performed\n",
+ arp_ip_target[i]);
arp_interval = 0;
} else {
if (bond_get_targets_ip(arp_target, ip) == -1)
arp_target[arp_ip_count++] = ip;
else
- pr_warning("Warning: duplicate address %pI4 in arp_ip_target, skipping\n",
- &ip);
+ pr_warn("Warning: duplicate address %pI4 in arp_ip_target, skipping\n",
+ &ip);
}
}
if (arp_interval && !arp_ip_count) {
/* don't allow arping if no arp_ip_target given... */
- pr_warning("Warning: arp_interval module parameter (%d) specified without providing an arp_ip_target parameter, arp_interval was reset to 0\n",
- arp_interval);
+ pr_warn("Warning: arp_interval module parameter (%d) specified without providing an arp_ip_target parameter, arp_interval was reset to 0\n",
+ arp_interval);
arp_interval = 0;
}
if (arp_validate) {
- if (bond_mode != BOND_MODE_ACTIVEBACKUP) {
- pr_err("arp_validate only supported in active-backup mode\n");
- return -EINVAL;
- }
if (!arp_interval) {
pr_err("arp_validate requires arp_interval\n");
return -EINVAL;
@@ -4271,23 +4227,23 @@ static int bond_check_params(struct bond_params *params)
arp_interval, valptr->string, arp_ip_count);
for (i = 0; i < arp_ip_count; i++)
- pr_info(" %s", arp_ip_target[i]);
+ pr_cont(" %s", arp_ip_target[i]);
- pr_info("\n");
+ pr_cont("\n");
} else if (max_bonds) {
/* miimon and arp_interval not set, we need one so things
* work as expected, see bonding.txt for details
*/
- pr_debug("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details.\n");
+ pr_debug("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details\n");
}
if (primary && !USES_PRIMARY(bond_mode)) {
/* currently, using a primary only makes sense
* in active backup, TLB or ALB modes
*/
- pr_warning("Warning: %s primary device specified but has no effect in %s mode\n",
- primary, bond_mode_name(bond_mode));
+ pr_warn("Warning: %s primary device specified but has no effect in %s mode\n",
+ primary, bond_mode_name(bond_mode));
primary = NULL;
}
@@ -4316,14 +4272,14 @@ static int bond_check_params(struct bond_params *params)
}
fail_over_mac_value = valptr->value;
if (bond_mode != BOND_MODE_ACTIVEBACKUP)
- pr_warning("Warning: fail_over_mac only affects active-backup mode.\n");
+ pr_warn("Warning: fail_over_mac only affects active-backup mode\n");
} else {
fail_over_mac_value = BOND_FOM_NONE;
}
if (lp_interval == 0) {
- pr_warning("Warning: ip_interval must be between 1 and %d, so it was reset to %d\n",
- INT_MAX, BOND_ALB_DEFAULT_LP_INTERVAL);
+ pr_warn("Warning: ip_interval must be between 1 and %d, so it was reset to %d\n",
+ INT_MAX, BOND_ALB_DEFAULT_LP_INTERVAL);
lp_interval = BOND_ALB_DEFAULT_LP_INTERVAL;
}
diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
index 70651f8e8e3b..f847e165d252 100644
--- a/drivers/net/bonding/bond_netlink.c
+++ b/drivers/net/bonding/bond_netlink.c
@@ -181,7 +181,7 @@ static int bond_changelink(struct net_device *bond_dev,
int arp_interval = nla_get_u32(data[IFLA_BOND_ARP_INTERVAL]);
if (arp_interval && miimon) {
- pr_err("%s: ARP monitoring cannot be used with MII monitoring.\n",
+ pr_err("%s: ARP monitoring cannot be used with MII monitoring\n",
bond->dev->name);
return -EINVAL;
}
@@ -199,7 +199,7 @@ static int bond_changelink(struct net_device *bond_dev,
nla_for_each_nested(attr, data[IFLA_BOND_ARP_IP_TARGET], rem) {
__be32 target = nla_get_be32(attr);
- bond_opt_initval(&newval, target);
+ bond_opt_initval(&newval, (__force u64)target);
err = __bond_opt_set(bond, BOND_OPT_ARP_TARGETS,
&newval);
if (err)
@@ -207,7 +207,7 @@ static int bond_changelink(struct net_device *bond_dev,
i++;
}
if (i == 0 && bond->params.arp_interval)
- pr_warn("%s: removing last arp target with arp_interval on\n",
+ pr_warn("%s: Removing last arp target with arp_interval on\n",
bond->dev->name);
if (err)
return err;
@@ -216,7 +216,7 @@ static int bond_changelink(struct net_device *bond_dev,
int arp_validate = nla_get_u32(data[IFLA_BOND_ARP_VALIDATE]);
if (arp_validate && miimon) {
- pr_err("%s: ARP validating cannot be used with MII monitoring.\n",
+ pr_err("%s: ARP validating cannot be used with MII monitoring\n",
bond->dev->name);
return -EINVAL;
}
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 298c26509095..724e30fa20b9 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -20,7 +20,59 @@
#include <linux/inet.h>
#include "bonding.h"
-static struct bond_opt_value bond_mode_tbl[] = {
+static int bond_option_active_slave_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_miimon_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_updelay_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_downdelay_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_use_carrier_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_arp_interval_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target);
+static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target);
+static int bond_option_arp_ip_targets_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_arp_validate_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_arp_all_targets_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_primary_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_primary_reselect_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_fail_over_mac_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_xmit_hash_policy_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_resend_igmp_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_num_peer_notif_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_all_slaves_active_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_min_links_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_lp_interval_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_pps_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_lacp_rate_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_ad_select_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_queue_id_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_mode_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+static int bond_option_slaves_set(struct bonding *bond,
+ const struct bond_opt_value *newval);
+
+
+static const struct bond_opt_value bond_mode_tbl[] = {
{ "balance-rr", BOND_MODE_ROUNDROBIN, BOND_VALFLAG_DEFAULT},
{ "active-backup", BOND_MODE_ACTIVEBACKUP, 0},
{ "balance-xor", BOND_MODE_XOR, 0},
@@ -31,13 +83,13 @@ static struct bond_opt_value bond_mode_tbl[] = {
{ NULL, -1, 0},
};
-static struct bond_opt_value bond_pps_tbl[] = {
+static const struct bond_opt_value bond_pps_tbl[] = {
{ "default", 1, BOND_VALFLAG_DEFAULT},
{ "maxval", USHRT_MAX, BOND_VALFLAG_MAX},
{ NULL, -1, 0},
};
-static struct bond_opt_value bond_xmit_hashtype_tbl[] = {
+static const struct bond_opt_value bond_xmit_hashtype_tbl[] = {
{ "layer2", BOND_XMIT_POLICY_LAYER2, BOND_VALFLAG_DEFAULT},
{ "layer3+4", BOND_XMIT_POLICY_LAYER34, 0},
{ "layer2+3", BOND_XMIT_POLICY_LAYER23, 0},
@@ -46,85 +98,88 @@ static struct bond_opt_value bond_xmit_hashtype_tbl[] = {
{ NULL, -1, 0},
};
-static struct bond_opt_value bond_arp_validate_tbl[] = {
- { "none", BOND_ARP_VALIDATE_NONE, BOND_VALFLAG_DEFAULT},
- { "active", BOND_ARP_VALIDATE_ACTIVE, 0},
- { "backup", BOND_ARP_VALIDATE_BACKUP, 0},
- { "all", BOND_ARP_VALIDATE_ALL, 0},
- { NULL, -1, 0},
+static const struct bond_opt_value bond_arp_validate_tbl[] = {
+ { "none", BOND_ARP_VALIDATE_NONE, BOND_VALFLAG_DEFAULT},
+ { "active", BOND_ARP_VALIDATE_ACTIVE, 0},
+ { "backup", BOND_ARP_VALIDATE_BACKUP, 0},
+ { "all", BOND_ARP_VALIDATE_ALL, 0},
+ { "filter", BOND_ARP_FILTER, 0},
+ { "filter_active", BOND_ARP_FILTER_ACTIVE, 0},
+ { "filter_backup", BOND_ARP_FILTER_BACKUP, 0},
+ { NULL, -1, 0},
};
-static struct bond_opt_value bond_arp_all_targets_tbl[] = {
+static const struct bond_opt_value bond_arp_all_targets_tbl[] = {
{ "any", BOND_ARP_TARGETS_ANY, BOND_VALFLAG_DEFAULT},
{ "all", BOND_ARP_TARGETS_ALL, 0},
{ NULL, -1, 0},
};
-static struct bond_opt_value bond_fail_over_mac_tbl[] = {
+static const struct bond_opt_value bond_fail_over_mac_tbl[] = {
{ "none", BOND_FOM_NONE, BOND_VALFLAG_DEFAULT},
{ "active", BOND_FOM_ACTIVE, 0},
{ "follow", BOND_FOM_FOLLOW, 0},
{ NULL, -1, 0},
};
-static struct bond_opt_value bond_intmax_tbl[] = {
+static const struct bond_opt_value bond_intmax_tbl[] = {
{ "off", 0, BOND_VALFLAG_DEFAULT},
{ "maxval", INT_MAX, BOND_VALFLAG_MAX},
};
-static struct bond_opt_value bond_lacp_rate_tbl[] = {
+static const struct bond_opt_value bond_lacp_rate_tbl[] = {
{ "slow", AD_LACP_SLOW, 0},
{ "fast", AD_LACP_FAST, 0},
{ NULL, -1, 0},
};
-static struct bond_opt_value bond_ad_select_tbl[] = {
+static const struct bond_opt_value bond_ad_select_tbl[] = {
{ "stable", BOND_AD_STABLE, BOND_VALFLAG_DEFAULT},
{ "bandwidth", BOND_AD_BANDWIDTH, 0},
{ "count", BOND_AD_COUNT, 0},
{ NULL, -1, 0},
};
-static struct bond_opt_value bond_num_peer_notif_tbl[] = {
+static const struct bond_opt_value bond_num_peer_notif_tbl[] = {
{ "off", 0, 0},
{ "maxval", 255, BOND_VALFLAG_MAX},
{ "default", 1, BOND_VALFLAG_DEFAULT},
{ NULL, -1, 0}
};
-static struct bond_opt_value bond_primary_reselect_tbl[] = {
+static const struct bond_opt_value bond_primary_reselect_tbl[] = {
{ "always", BOND_PRI_RESELECT_ALWAYS, BOND_VALFLAG_DEFAULT},
{ "better", BOND_PRI_RESELECT_BETTER, 0},
{ "failure", BOND_PRI_RESELECT_FAILURE, 0},
{ NULL, -1},
};
-static struct bond_opt_value bond_use_carrier_tbl[] = {
+static const struct bond_opt_value bond_use_carrier_tbl[] = {
{ "off", 0, 0},
{ "on", 1, BOND_VALFLAG_DEFAULT},
{ NULL, -1, 0}
};
-static struct bond_opt_value bond_all_slaves_active_tbl[] = {
+static const struct bond_opt_value bond_all_slaves_active_tbl[] = {
{ "off", 0, BOND_VALFLAG_DEFAULT},
{ "on", 1, 0},
{ NULL, -1, 0}
};
-static struct bond_opt_value bond_resend_igmp_tbl[] = {
+static const struct bond_opt_value bond_resend_igmp_tbl[] = {
{ "off", 0, 0},
{ "maxval", 255, BOND_VALFLAG_MAX},
{ "default", 1, BOND_VALFLAG_DEFAULT},
{ NULL, -1, 0}
};
-static struct bond_opt_value bond_lp_interval_tbl[] = {
+static const struct bond_opt_value bond_lp_interval_tbl[] = {
{ "minval", 1, BOND_VALFLAG_MIN | BOND_VALFLAG_DEFAULT},
{ "maxval", INT_MAX, BOND_VALFLAG_MAX},
{ NULL, -1, 0},
};
-static struct bond_option bond_opts[] = {
+static const struct bond_option bond_opts[] = {
[BOND_OPT_MODE] = {
.id = BOND_OPT_MODE,
.name = "mode",
@@ -152,7 +207,8 @@ static struct bond_option bond_opts[] = {
.id = BOND_OPT_ARP_VALIDATE,
.name = "arp_validate",
.desc = "validate src/dst of ARP probes",
- .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_ACTIVEBACKUP)),
+ .unsuppmodes = BIT(BOND_MODE_8023AD) | BIT(BOND_MODE_TLB) |
+ BIT(BOND_MODE_ALB),
.values = bond_arp_validate_tbl,
.set = bond_option_arp_validate_set
},
@@ -312,9 +368,9 @@ static struct bond_option bond_opts[] = {
};
/* Searches for a value in opt's values[] table */
-struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val)
+const struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val)
{
- struct bond_option *opt;
+ const struct bond_option *opt;
int i;
opt = bond_opt_get(option);
@@ -328,7 +384,7 @@ struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val)
}
/* Searches for a value in opt's values[] table which matches the flagmask */
-static struct bond_opt_value *bond_opt_get_flags(const struct bond_option *opt,
+static const struct bond_opt_value *bond_opt_get_flags(const struct bond_option *opt,
u32 flagmask)
{
int i;
@@ -345,7 +401,7 @@ static struct bond_opt_value *bond_opt_get_flags(const struct bond_option *opt,
*/
static bool bond_opt_check_range(const struct bond_option *opt, u64 val)
{
- struct bond_opt_value *minval, *maxval;
+ const struct bond_opt_value *minval, *maxval;
minval = bond_opt_get_flags(opt, BOND_VALFLAG_MIN);
maxval = bond_opt_get_flags(opt, BOND_VALFLAG_MAX);
@@ -365,11 +421,12 @@ static bool bond_opt_check_range(const struct bond_option *opt, u64 val)
* or the struct_opt_value that matched. It also strips the new line from
* @val->string if it's present.
*/
-struct bond_opt_value *bond_opt_parse(const struct bond_option *opt,
- struct bond_opt_value *val)
+const struct bond_opt_value *bond_opt_parse(const struct bond_option *opt,
+ struct bond_opt_value *val)
{
char *p, valstr[BOND_OPT_MAX_NAMELEN + 1] = { 0, };
- struct bond_opt_value *tbl, *ret = NULL;
+ const struct bond_opt_value *tbl;
+ const struct bond_opt_value *ret = NULL;
bool checkval;
int i, rv;
@@ -448,7 +505,7 @@ static int bond_opt_check_deps(struct bonding *bond,
static void bond_opt_dep_print(struct bonding *bond,
const struct bond_option *opt)
{
- struct bond_opt_value *modeval;
+ const struct bond_opt_value *modeval;
struct bond_params *params;
params = &bond->params;
@@ -461,9 +518,9 @@ static void bond_opt_dep_print(struct bonding *bond,
static void bond_opt_error_interpret(struct bonding *bond,
const struct bond_option *opt,
- int error, struct bond_opt_value *val)
+ int error, const struct bond_opt_value *val)
{
- struct bond_opt_value *minval, *maxval;
+ const struct bond_opt_value *minval, *maxval;
char *p;
switch (error) {
@@ -474,10 +531,10 @@ static void bond_opt_error_interpret(struct bonding *bond,
p = strchr(val->string, '\n');
if (p)
*p = '\0';
- pr_err("%s: option %s: invalid value (%s).\n",
+ pr_err("%s: option %s: invalid value (%s)\n",
bond->dev->name, opt->name, val->string);
} else {
- pr_err("%s: option %s: invalid value (%llu).\n",
+ pr_err("%s: option %s: invalid value (%llu)\n",
bond->dev->name, opt->name, val->value);
}
}
@@ -485,7 +542,7 @@ static void bond_opt_error_interpret(struct bonding *bond,
maxval = bond_opt_get_flags(opt, BOND_VALFLAG_MAX);
if (!maxval)
break;
- pr_err("%s: option %s: allowed values %llu - %llu.\n",
+ pr_err("%s: option %s: allowed values %llu - %llu\n",
bond->dev->name, opt->name, minval ? minval->value : 0,
maxval->value);
break;
@@ -493,11 +550,11 @@ static void bond_opt_error_interpret(struct bonding *bond,
bond_opt_dep_print(bond, opt);
break;
case -ENOTEMPTY:
- pr_err("%s: option %s: unable to set because the bond device has slaves.\n",
+ pr_err("%s: option %s: unable to set because the bond device has slaves\n",
bond->dev->name, opt->name);
break;
case -EBUSY:
- pr_err("%s: option %s: unable to set because the bond device is up.\n",
+ pr_err("%s: option %s: unable to set because the bond device is up\n",
bond->dev->name, opt->name);
break;
default:
@@ -518,7 +575,7 @@ static void bond_opt_error_interpret(struct bonding *bond,
int __bond_opt_set(struct bonding *bond,
unsigned int option, struct bond_opt_value *val)
{
- struct bond_opt_value *retval = NULL;
+ const struct bond_opt_value *retval = NULL;
const struct bond_option *opt;
int ret = -ENOENT;
@@ -573,7 +630,7 @@ int bond_opt_tryset_rtnl(struct bonding *bond, unsigned int option, char *buf)
* This function checks if option is valid and if so returns a pointer
* to its entry in the bond_opts[] option array.
*/
-struct bond_option *bond_opt_get(unsigned int option)
+const struct bond_option *bond_opt_get(unsigned int option)
{
if (!BOND_OPT_VALID(option))
return NULL;
@@ -581,7 +638,7 @@ struct bond_option *bond_opt_get(unsigned int option)
return &bond_opts[option];
}
-int bond_option_mode_set(struct bonding *bond, struct bond_opt_value *newval)
+int bond_option_mode_set(struct bonding *bond, const struct bond_opt_value *newval)
{
if (BOND_NO_USES_ARP(newval->value) && bond->params.arp_interval) {
pr_info("%s: %s mode is incompatible with arp monitoring, start mii monitoring\n",
@@ -590,7 +647,7 @@ int bond_option_mode_set(struct bonding *bond, struct bond_opt_value *newval)
bond->params.arp_interval = 0;
/* set miimon to default value */
bond->params.miimon = BOND_DEFAULT_MIIMON;
- pr_info("%s: Setting MII monitoring interval to %d.\n",
+ pr_info("%s: Setting MII monitoring interval to %d\n",
bond->dev->name, bond->params.miimon);
}
@@ -619,8 +676,8 @@ struct net_device *bond_option_active_slave_get(struct bonding *bond)
return __bond_option_active_slave_get(bond, bond->curr_active_slave);
}
-int bond_option_active_slave_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_active_slave_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
char ifname[IFNAMSIZ] = { 0, };
struct net_device *slave_dev;
@@ -637,13 +694,13 @@ int bond_option_active_slave_set(struct bonding *bond,
if (slave_dev) {
if (!netif_is_bond_slave(slave_dev)) {
- pr_err("Device %s is not bonding slave.\n",
+ pr_err("Device %s is not bonding slave\n",
slave_dev->name);
return -EINVAL;
}
if (bond->dev != netdev_master_upper_dev_get(slave_dev)) {
- pr_err("%s: Device %s is not our slave.\n",
+ pr_err("%s: Device %s is not our slave\n",
bond->dev->name, slave_dev->name);
return -EINVAL;
}
@@ -654,9 +711,8 @@ int bond_option_active_slave_set(struct bonding *bond,
/* check to see if we are clearing active */
if (!slave_dev) {
- pr_info("%s: Clearing current active slave.\n",
- bond->dev->name);
- rcu_assign_pointer(bond->curr_active_slave, NULL);
+ pr_info("%s: Clearing current active slave\n", bond->dev->name);
+ RCU_INIT_POINTER(bond->curr_active_slave, NULL);
bond_select_active_slave(bond);
} else {
struct slave *old_active = bond->curr_active_slave;
@@ -666,16 +722,16 @@ int bond_option_active_slave_set(struct bonding *bond,
if (new_active == old_active) {
/* do nothing */
- pr_info("%s: %s is already the current active slave.\n",
+ pr_info("%s: %s is already the current active slave\n",
bond->dev->name, new_active->dev->name);
} else {
if (old_active && (new_active->link == BOND_LINK_UP) &&
IS_UP(new_active->dev)) {
- pr_info("%s: Setting %s as active slave.\n",
+ pr_info("%s: Setting %s as active slave\n",
bond->dev->name, new_active->dev->name);
bond_change_active_slave(bond, new_active);
} else {
- pr_err("%s: Could not set %s as active slave; either %s is down or the link is down.\n",
+ pr_err("%s: Could not set %s as active slave; either %s is down or the link is down\n",
bond->dev->name, new_active->dev->name,
new_active->dev->name);
ret = -EINVAL;
@@ -689,21 +745,22 @@ int bond_option_active_slave_set(struct bonding *bond,
return ret;
}
-int bond_option_miimon_set(struct bonding *bond, struct bond_opt_value *newval)
+static int bond_option_miimon_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
- pr_info("%s: Setting MII monitoring interval to %llu.\n",
+ pr_info("%s: Setting MII monitoring interval to %llu\n",
bond->dev->name, newval->value);
bond->params.miimon = newval->value;
if (bond->params.updelay)
- pr_info("%s: Note: Updating updelay (to %d) since it is a multiple of the miimon value.\n",
+ pr_info("%s: Note: Updating updelay (to %d) since it is a multiple of the miimon value\n",
bond->dev->name,
bond->params.updelay * bond->params.miimon);
if (bond->params.downdelay)
- pr_info("%s: Note: Updating downdelay (to %d) since it is a multiple of the miimon value.\n",
+ pr_info("%s: Note: Updating downdelay (to %d) since it is a multiple of the miimon value\n",
bond->dev->name,
bond->params.downdelay * bond->params.miimon);
if (newval->value && bond->params.arp_interval) {
- pr_info("%s: MII monitoring cannot be used with ARP monitoring. Disabling ARP monitoring...\n",
+ pr_info("%s: MII monitoring cannot be used with ARP monitoring - disabling ARP monitoring...\n",
bond->dev->name);
bond->params.arp_interval = 0;
if (bond->params.arp_validate)
@@ -726,7 +783,8 @@ int bond_option_miimon_set(struct bonding *bond, struct bond_opt_value *newval)
return 0;
}
-int bond_option_updelay_set(struct bonding *bond, struct bond_opt_value *newval)
+static int bond_option_updelay_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
int value = newval->value;
@@ -743,15 +801,14 @@ int bond_option_updelay_set(struct bonding *bond, struct bond_opt_value *newval)
bond->params.miimon);
}
bond->params.updelay = value / bond->params.miimon;
- pr_info("%s: Setting up delay to %d.\n",
- bond->dev->name,
- bond->params.updelay * bond->params.miimon);
+ pr_info("%s: Setting up delay to %d\n",
+ bond->dev->name, bond->params.updelay * bond->params.miimon);
return 0;
}
-int bond_option_downdelay_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_downdelay_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
int value = newval->value;
@@ -768,37 +825,36 @@ int bond_option_downdelay_set(struct bonding *bond,
bond->params.miimon);
}
bond->params.downdelay = value / bond->params.miimon;
- pr_info("%s: Setting down delay to %d.\n",
- bond->dev->name,
- bond->params.downdelay * bond->params.miimon);
+ pr_info("%s: Setting down delay to %d\n",
+ bond->dev->name, bond->params.downdelay * bond->params.miimon);
return 0;
}
-int bond_option_use_carrier_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_use_carrier_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
- pr_info("%s: Setting use_carrier to %llu.\n",
+ pr_info("%s: Setting use_carrier to %llu\n",
bond->dev->name, newval->value);
bond->params.use_carrier = newval->value;
return 0;
}
-int bond_option_arp_interval_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_arp_interval_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
- pr_info("%s: Setting ARP monitoring interval to %llu.\n",
+ pr_info("%s: Setting ARP monitoring interval to %llu\n",
bond->dev->name, newval->value);
bond->params.arp_interval = newval->value;
if (newval->value) {
if (bond->params.miimon) {
- pr_info("%s: ARP monitoring cannot be used with MII monitoring. %s Disabling MII monitoring.\n",
+ pr_info("%s: ARP monitoring cannot be used with MII monitoring. %s Disabling MII monitoring\n",
bond->dev->name, bond->dev->name);
bond->params.miimon = 0;
}
if (!bond->params.arp_targets[0])
- pr_info("%s: ARP monitoring has been set up, but no ARP targets have been specified.\n",
+ pr_info("%s: ARP monitoring has been set up, but no ARP targets have been specified\n",
bond->dev->name);
}
if (bond->dev->flags & IFF_UP) {
@@ -813,8 +869,7 @@ int bond_option_arp_interval_set(struct bonding *bond,
cancel_delayed_work_sync(&bond->arp_work);
} else {
/* arp_validate can be set only in active-backup mode */
- if (bond->params.arp_validate)
- bond->recv_probe = bond_arp_rcv;
+ bond->recv_probe = bond_arp_rcv;
cancel_delayed_work_sync(&bond->mii_work);
queue_delayed_work(bond->wq, &bond->arp_work, 0);
}
@@ -857,19 +912,18 @@ static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
ind = bond_get_targets_ip(targets, 0); /* first free slot */
if (ind == -1) {
- pr_err("%s: ARP target table is full!\n",
- bond->dev->name);
+ pr_err("%s: ARP target table is full!\n", bond->dev->name);
return -EINVAL;
}
- pr_info("%s: adding ARP target %pI4.\n", bond->dev->name, &target);
+ pr_info("%s: Adding ARP target %pI4\n", bond->dev->name, &target);
_bond_options_arp_ip_target_set(bond, ind, target, jiffies);
return 0;
}
-int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
+static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
{
int ret;
@@ -881,7 +935,7 @@ int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
return ret;
}
-int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
+static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
{
__be32 *targets = bond->params.arp_targets;
struct list_head *iter;
@@ -897,17 +951,16 @@ int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
ind = bond_get_targets_ip(targets, target);
if (ind == -1) {
- pr_err("%s: unable to remove nonexistent ARP target %pI4.\n",
+ pr_err("%s: unable to remove nonexistent ARP target %pI4\n",
bond->dev->name, &target);
return -EINVAL;
}
if (ind == 0 && !targets[1] && bond->params.arp_interval)
- pr_warn("%s: removing last arp target with arp_interval on\n",
+ pr_warn("%s: Removing last arp target with arp_interval on\n",
bond->dev->name);
- pr_info("%s: removing ARP target %pI4.\n", bond->dev->name,
- &target);
+ pr_info("%s: Removing ARP target %pI4\n", bond->dev->name, &target);
/* not to race with bond_arp_rcv */
write_lock_bh(&bond->lock);
@@ -938,8 +991,8 @@ void bond_option_arp_ip_targets_clear(struct bonding *bond)
write_unlock_bh(&bond->lock);
}
-int bond_option_arp_ip_targets_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_arp_ip_targets_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
int ret = -EPERM;
__be32 target;
@@ -955,7 +1008,7 @@ int bond_option_arp_ip_targets_set(struct bonding *bond,
else if (newval->string[0] == '-')
ret = bond_option_arp_ip_target_rem(bond, target);
else
- pr_err("no command found in arp_ip_targets file for bond %s. Use +<addr> or -<addr>.\n",
+ pr_err("no command found in arp_ip_targets file for bond %s - use +<addr> or -<addr>\n",
bond->dev->name);
} else {
target = newval->value;
@@ -965,10 +1018,10 @@ int bond_option_arp_ip_targets_set(struct bonding *bond,
return ret;
}
-int bond_option_arp_validate_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_arp_validate_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
- pr_info("%s: setting arp_validate to %s (%llu).\n",
+ pr_info("%s: Setting arp_validate to %s (%llu)\n",
bond->dev->name, newval->string, newval->value);
if (bond->dev->flags & IFF_UP) {
@@ -982,17 +1035,18 @@ int bond_option_arp_validate_set(struct bonding *bond,
return 0;
}
-int bond_option_arp_all_targets_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_arp_all_targets_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
- pr_info("%s: setting arp_all_targets to %s (%llu).\n",
+ pr_info("%s: Setting arp_all_targets to %s (%llu)\n",
bond->dev->name, newval->string, newval->value);
bond->params.arp_all_targets = newval->value;
return 0;
}
-int bond_option_primary_set(struct bonding *bond, struct bond_opt_value *newval)
+static int bond_option_primary_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
char *p, *primary = newval->string;
struct list_head *iter;
@@ -1007,8 +1061,7 @@ int bond_option_primary_set(struct bonding *bond, struct bond_opt_value *newval)
*p = '\0';
/* check to see if we are clearing primary */
if (!strlen(primary)) {
- pr_info("%s: Setting primary slave to None.\n",
- bond->dev->name);
+ pr_info("%s: Setting primary slave to None\n", bond->dev->name);
bond->primary_slave = NULL;
memset(bond->params.primary, 0, sizeof(bond->params.primary));
bond_select_active_slave(bond);
@@ -1017,7 +1070,7 @@ int bond_option_primary_set(struct bonding *bond, struct bond_opt_value *newval)
bond_for_each_slave(bond, slave, iter) {
if (strncmp(slave->dev->name, primary, IFNAMSIZ) == 0) {
- pr_info("%s: Setting %s as primary slave.\n",
+ pr_info("%s: Setting %s as primary slave\n",
bond->dev->name, slave->dev->name);
bond->primary_slave = slave;
strcpy(bond->params.primary, slave->dev->name);
@@ -1027,15 +1080,14 @@ int bond_option_primary_set(struct bonding *bond, struct bond_opt_value *newval)
}
if (bond->primary_slave) {
- pr_info("%s: Setting primary slave to None.\n",
- bond->dev->name);
+ pr_info("%s: Setting primary slave to None\n", bond->dev->name);
bond->primary_slave = NULL;
bond_select_active_slave(bond);
}
strncpy(bond->params.primary, primary, IFNAMSIZ);
bond->params.primary[IFNAMSIZ - 1] = 0;
- pr_info("%s: Recording %s as primary, but it has not been enslaved to %s yet.\n",
+ pr_info("%s: Recording %s as primary, but it has not been enslaved to %s yet\n",
bond->dev->name, primary, bond->dev->name);
out:
@@ -1046,10 +1098,10 @@ out:
return 0;
}
-int bond_option_primary_reselect_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_primary_reselect_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
- pr_info("%s: setting primary_reselect to %s (%llu).\n",
+ pr_info("%s: Setting primary_reselect to %s (%llu)\n",
bond->dev->name, newval->string, newval->value);
bond->params.primary_reselect = newval->value;
@@ -1062,46 +1114,46 @@ int bond_option_primary_reselect_set(struct bonding *bond,
return 0;
}
-int bond_option_fail_over_mac_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_fail_over_mac_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
- pr_info("%s: Setting fail_over_mac to %s (%llu).\n",
+ pr_info("%s: Setting fail_over_mac to %s (%llu)\n",
bond->dev->name, newval->string, newval->value);
bond->params.fail_over_mac = newval->value;
return 0;
}
-int bond_option_xmit_hash_policy_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_xmit_hash_policy_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
- pr_info("%s: setting xmit hash policy to %s (%llu).\n",
+ pr_info("%s: Setting xmit hash policy to %s (%llu)\n",
bond->dev->name, newval->string, newval->value);
bond->params.xmit_policy = newval->value;
return 0;
}
-int bond_option_resend_igmp_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_resend_igmp_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
- pr_info("%s: Setting resend_igmp to %llu.\n",
+ pr_info("%s: Setting resend_igmp to %llu\n",
bond->dev->name, newval->value);
bond->params.resend_igmp = newval->value;
return 0;
}
-int bond_option_num_peer_notif_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_num_peer_notif_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
bond->params.num_peer_notif = newval->value;
return 0;
}
-int bond_option_all_slaves_active_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_all_slaves_active_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
struct list_head *iter;
struct slave *slave;
@@ -1121,8 +1173,8 @@ int bond_option_all_slaves_active_set(struct bonding *bond,
return 0;
}
-int bond_option_min_links_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_min_links_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
pr_info("%s: Setting min links value to %llu\n",
bond->dev->name, newval->value);
@@ -1131,15 +1183,16 @@ int bond_option_min_links_set(struct bonding *bond,
return 0;
}
-int bond_option_lp_interval_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_lp_interval_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
bond->params.lp_interval = newval->value;
return 0;
}
-int bond_option_pps_set(struct bonding *bond, struct bond_opt_value *newval)
+static int bond_option_pps_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
bond->params.packets_per_slave = newval->value;
if (newval->value > 0) {
@@ -1156,10 +1209,10 @@ int bond_option_pps_set(struct bonding *bond, struct bond_opt_value *newval)
return 0;
}
-int bond_option_lacp_rate_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_lacp_rate_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
- pr_info("%s: Setting LACP rate to %s (%llu).\n",
+ pr_info("%s: Setting LACP rate to %s (%llu)\n",
bond->dev->name, newval->string, newval->value);
bond->params.lacp_fast = newval->value;
bond_3ad_update_lacp_rate(bond);
@@ -1167,18 +1220,18 @@ int bond_option_lacp_rate_set(struct bonding *bond,
return 0;
}
-int bond_option_ad_select_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_ad_select_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
- pr_info("%s: Setting ad_select to %s (%llu).\n",
+ pr_info("%s: Setting ad_select to %s (%llu)\n",
bond->dev->name, newval->string, newval->value);
bond->params.ad_select = newval->value;
return 0;
}
-int bond_option_queue_id_set(struct bonding *bond,
- struct bond_opt_value *newval)
+static int bond_option_queue_id_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
struct slave *slave, *update_slave;
struct net_device *sdev;
@@ -1200,8 +1253,7 @@ int bond_option_queue_id_set(struct bonding *bond,
goto err_no_cmd;
/* Check buffer length, valid ifname and queue id */
- if (strlen(newval->string) > IFNAMSIZ ||
- !dev_valid_name(newval->string) ||
+ if (!dev_valid_name(newval->string) ||
qid > bond->dev->real_num_tx_queues)
goto err_no_cmd;
@@ -1233,14 +1285,14 @@ out:
return ret;
err_no_cmd:
- pr_info("invalid input for queue_id set for %s.\n",
- bond->dev->name);
+ pr_info("invalid input for queue_id set for %s\n", bond->dev->name);
ret = -EPERM;
goto out;
}
-int bond_option_slaves_set(struct bonding *bond, struct bond_opt_value *newval)
+static int bond_option_slaves_set(struct bonding *bond,
+ const struct bond_opt_value *newval)
{
char command[IFNAMSIZ + 1] = { 0, };
struct net_device *dev;
@@ -1255,7 +1307,7 @@ int bond_option_slaves_set(struct bonding *bond, struct bond_opt_value *newval)
dev = __dev_get_by_name(dev_net(bond->dev), ifname);
if (!dev) {
- pr_info("%s: Interface %s does not exist!\n",
+ pr_info("%s: interface %s does not exist!\n",
bond->dev->name, ifname);
ret = -ENODEV;
goto out;
@@ -1263,12 +1315,12 @@ int bond_option_slaves_set(struct bonding *bond, struct bond_opt_value *newval)
switch (command[0]) {
case '+':
- pr_info("%s: Adding slave %s.\n", bond->dev->name, dev->name);
+ pr_info("%s: Adding slave %s\n", bond->dev->name, dev->name);
ret = bond_enslave(bond->dev, dev);
break;
case '-':
- pr_info("%s: Removing slave %s.\n", bond->dev->name, dev->name);
+ pr_info("%s: Removing slave %s\n", bond->dev->name, dev->name);
ret = bond_release(bond->dev, dev);
break;
@@ -1280,7 +1332,7 @@ out:
return ret;
err_no_cmd:
- pr_err("no command found in slaves file for bond %s. Use +ifname or -ifname.\n",
+ pr_err("no command found in slaves file for bond %s - use +ifname or -ifname\n",
bond->dev->name);
ret = -EPERM;
goto out;
diff --git a/drivers/net/bonding/bond_options.h b/drivers/net/bonding/bond_options.h
index 433d37f6940b..12be9e1bfb0c 100644
--- a/drivers/net/bonding/bond_options.h
+++ b/drivers/net/bonding/bond_options.h
@@ -81,8 +81,8 @@ struct bonding;
struct bond_option {
int id;
- char *name;
- char *desc;
+ const char *name;
+ const char *desc;
u32 flags;
/* unsuppmodes is used to denote modes in which the option isn't
@@ -92,18 +92,19 @@ struct bond_option {
/* supported values which this option can have, can be a subset of
* BOND_OPTVAL_RANGE's value range
*/
- struct bond_opt_value *values;
+ const struct bond_opt_value *values;
- int (*set)(struct bonding *bond, struct bond_opt_value *val);
+ int (*set)(struct bonding *bond, const struct bond_opt_value *val);
};
int __bond_opt_set(struct bonding *bond, unsigned int option,
struct bond_opt_value *val);
int bond_opt_tryset_rtnl(struct bonding *bond, unsigned int option, char *buf);
-struct bond_opt_value *bond_opt_parse(const struct bond_option *opt,
- struct bond_opt_value *val);
-struct bond_option *bond_opt_get(unsigned int option);
-struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val);
+
+const struct bond_opt_value *bond_opt_parse(const struct bond_option *opt,
+ struct bond_opt_value *val);
+const struct bond_option *bond_opt_get(unsigned int option);
+const struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val);
/* This helper is used to initialize a bond_opt_value structure for parameter
* passing. There should be either a valid string or value, but not both.
@@ -122,49 +123,6 @@ static inline void __bond_opt_init(struct bond_opt_value *optval,
#define bond_opt_initval(optval, value) __bond_opt_init(optval, NULL, value)
#define bond_opt_initstr(optval, str) __bond_opt_init(optval, str, ULLONG_MAX)
-int bond_option_mode_set(struct bonding *bond, struct bond_opt_value *newval);
-int bond_option_pps_set(struct bonding *bond, struct bond_opt_value *newval);
-int bond_option_xmit_hash_policy_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_arp_validate_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_arp_all_targets_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_fail_over_mac_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_arp_interval_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_arp_ip_targets_set(struct bonding *bond,
- struct bond_opt_value *newval);
void bond_option_arp_ip_targets_clear(struct bonding *bond);
-int bond_option_downdelay_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_updelay_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_lacp_rate_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_min_links_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_ad_select_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_num_peer_notif_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_miimon_set(struct bonding *bond, struct bond_opt_value *newval);
-int bond_option_primary_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_primary_reselect_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_use_carrier_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_active_slave_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_queue_id_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_all_slaves_active_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_resend_igmp_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_lp_interval_set(struct bonding *bond,
- struct bond_opt_value *newval);
-int bond_option_slaves_set(struct bonding *bond, struct bond_opt_value *newval);
+
#endif /* _BOND_OPTIONS_H */
diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c
index 3ac20e78eafc..013fdd0f45e9 100644
--- a/drivers/net/bonding/bond_procfs.c
+++ b/drivers/net/bonding/bond_procfs.c
@@ -65,13 +65,11 @@ static void bond_info_seq_stop(struct seq_file *seq, void *v)
static void bond_info_show_master(struct seq_file *seq)
{
struct bonding *bond = seq->private;
- struct bond_opt_value *optval;
+ const struct bond_opt_value *optval;
struct slave *curr;
int i;
- read_lock(&bond->curr_slave_lock);
- curr = bond->curr_active_slave;
- read_unlock(&bond->curr_slave_lock);
+ curr = rcu_dereference(bond->curr_active_slave);
seq_printf(seq, "Bonding Mode: %s",
bond_mode_name(bond->params.mode));
@@ -254,8 +252,8 @@ void bond_create_proc_entry(struct bonding *bond)
S_IRUGO, bn->proc_dir,
&bond_info_fops, bond);
if (bond->proc_entry == NULL)
- pr_warning("Warning: Cannot create /proc/net/%s/%s\n",
- DRV_NAME, bond_dev->name);
+ pr_warn("Warning: Cannot create /proc/net/%s/%s\n",
+ DRV_NAME, bond_dev->name);
else
memcpy(bond->proc_file_name, bond_dev->name, IFNAMSIZ);
}
@@ -281,8 +279,8 @@ void __net_init bond_create_proc_dir(struct bond_net *bn)
if (!bn->proc_dir) {
bn->proc_dir = proc_mkdir(DRV_NAME, bn->net->proc_net);
if (!bn->proc_dir)
- pr_warning("Warning: cannot create /proc/net/%s\n",
- DRV_NAME);
+ pr_warn("Warning: Cannot create /proc/net/%s\n",
+ DRV_NAME);
}
}
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 643fcc110299..0e8b268da0a0 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -117,9 +117,9 @@ static ssize_t bonding_store_bonds(struct class *cls,
rv = bond_create(bn->net, ifname);
if (rv) {
if (rv == -EEXIST)
- pr_info("%s already exists.\n", ifname);
+ pr_info("%s already exists\n", ifname);
else
- pr_info("%s creation failed.\n", ifname);
+ pr_info("%s creation failed\n", ifname);
res = rv;
}
} else if (command[0] == '-') {
@@ -144,7 +144,7 @@ static ssize_t bonding_store_bonds(struct class *cls,
return res;
err_no_cmd:
- pr_err("no command found in bonding_masters. Use +ifname or -ifname.\n");
+ pr_err("no command found in bonding_masters - use +ifname or -ifname\n");
return -EPERM;
}
@@ -220,7 +220,7 @@ static ssize_t bonding_show_mode(struct device *d,
struct device_attribute *attr, char *buf)
{
struct bonding *bond = to_bond(d);
- struct bond_opt_value *val;
+ const struct bond_opt_value *val;
val = bond_opt_get_val(BOND_OPT_MODE, bond->params.mode);
@@ -251,7 +251,7 @@ static ssize_t bonding_show_xmit_hash(struct device *d,
char *buf)
{
struct bonding *bond = to_bond(d);
- struct bond_opt_value *val;
+ const struct bond_opt_value *val;
val = bond_opt_get_val(BOND_OPT_XMIT_HASH, bond->params.xmit_policy);
@@ -282,7 +282,7 @@ static ssize_t bonding_show_arp_validate(struct device *d,
char *buf)
{
struct bonding *bond = to_bond(d);
- struct bond_opt_value *val;
+ const struct bond_opt_value *val;
val = bond_opt_get_val(BOND_OPT_ARP_VALIDATE,
bond->params.arp_validate);
@@ -314,7 +314,7 @@ static ssize_t bonding_show_arp_all_targets(struct device *d,
char *buf)
{
struct bonding *bond = to_bond(d);
- struct bond_opt_value *val;
+ const struct bond_opt_value *val;
val = bond_opt_get_val(BOND_OPT_ARP_ALL_TARGETS,
bond->params.arp_all_targets);
@@ -348,7 +348,7 @@ static ssize_t bonding_show_fail_over_mac(struct device *d,
char *buf)
{
struct bonding *bond = to_bond(d);
- struct bond_opt_value *val;
+ const struct bond_opt_value *val;
val = bond_opt_get_val(BOND_OPT_FAIL_OVER_MAC,
bond->params.fail_over_mac);
@@ -505,7 +505,7 @@ static ssize_t bonding_show_lacp(struct device *d,
char *buf)
{
struct bonding *bond = to_bond(d);
- struct bond_opt_value *val;
+ const struct bond_opt_value *val;
val = bond_opt_get_val(BOND_OPT_LACP_RATE, bond->params.lacp_fast);
@@ -558,7 +558,7 @@ static ssize_t bonding_show_ad_select(struct device *d,
char *buf)
{
struct bonding *bond = to_bond(d);
- struct bond_opt_value *val;
+ const struct bond_opt_value *val;
val = bond_opt_get_val(BOND_OPT_AD_SELECT, bond->params.ad_select);
@@ -686,7 +686,7 @@ static ssize_t bonding_show_primary_reselect(struct device *d,
char *buf)
{
struct bonding *bond = to_bond(d);
- struct bond_opt_value *val;
+ const struct bond_opt_value *val;
val = bond_opt_get_val(BOND_OPT_PRIMARY_RESELECT,
bond->params.primary_reselect);
@@ -1135,7 +1135,7 @@ int bond_create_sysfs(struct bond_net *bn)
/* Is someone being kinky and naming a device bonding_master? */
if (__dev_get_by_name(bn->net,
class_attr_bonding_masters.attr.name))
- pr_err("network device named %s already exists in sysfs",
+ pr_err("network device named %s already exists in sysfs\n",
class_attr_bonding_masters.attr.name);
ret = 0;
}
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 2b0fdec695f7..b8bdd0acc8f3 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -188,8 +188,9 @@ struct slave {
struct net_device *dev; /* first - useful for panic debug */
struct bonding *bond; /* our master */
int delay;
- unsigned long jiffies;
- unsigned long last_arp_rx;
+ /* all three in jiffies */
+ unsigned long last_link_up;
+ unsigned long last_rx;
unsigned long target_last_arp_rx[BOND_MAX_ARP_TARGETS];
s8 link; /* one of BOND_LINK_XXXX */
s8 new_link;
@@ -265,6 +266,11 @@ struct bonding {
#define bond_slave_get_rtnl(dev) \
((struct slave *) rtnl_dereference(dev->rx_handler_data))
+struct bond_vlan_tag {
+ __be16 vlan_proto;
+ unsigned short vlan_id;
+};
+
/**
* Returns NULL if the net_device does not belong to any of the bond's slaves
*
@@ -292,7 +298,7 @@ static inline void bond_set_active_slave(struct slave *slave)
{
if (slave->backup) {
slave->backup = 0;
- rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_KERNEL);
+ rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
}
}
@@ -300,7 +306,7 @@ static inline void bond_set_backup_slave(struct slave *slave)
{
if (!slave->backup) {
slave->backup = 1;
- rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_KERNEL);
+ rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
}
}
@@ -312,7 +318,7 @@ static inline void bond_set_slave_state(struct slave *slave,
slave->backup = slave_state;
if (notify) {
- rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_KERNEL);
+ rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
slave->should_notify = 0;
} else {
if (slave->should_notify)
@@ -342,7 +348,7 @@ static inline void bond_slave_state_notify(struct bonding *bond)
bond_for_each_slave(bond, tmp, iter) {
if (tmp->should_notify) {
- rtmsg_ifinfo(RTM_NEWLINK, tmp->dev, 0, GFP_KERNEL);
+ rtmsg_ifinfo(RTM_NEWLINK, tmp->dev, 0, GFP_ATOMIC);
tmp->should_notify = 0;
}
}
@@ -374,6 +380,11 @@ static inline bool bond_is_active_slave(struct slave *slave)
#define BOND_ARP_VALIDATE_BACKUP (1 << BOND_STATE_BACKUP)
#define BOND_ARP_VALIDATE_ALL (BOND_ARP_VALIDATE_ACTIVE | \
BOND_ARP_VALIDATE_BACKUP)
+#define BOND_ARP_FILTER (BOND_ARP_VALIDATE_ALL + 1)
+#define BOND_ARP_FILTER_ACTIVE (BOND_ARP_VALIDATE_ACTIVE | \
+ BOND_ARP_FILTER)
+#define BOND_ARP_FILTER_BACKUP (BOND_ARP_VALIDATE_BACKUP | \
+ BOND_ARP_FILTER)
#define BOND_SLAVE_NOTIFY_NOW true
#define BOND_SLAVE_NOTIFY_LATER false
@@ -384,6 +395,12 @@ static inline int slave_do_arp_validate(struct bonding *bond,
return bond->params.arp_validate & (1 << bond_slave_state(slave));
}
+static inline int slave_do_arp_validate_only(struct bonding *bond,
+ struct slave *slave)
+{
+ return bond->params.arp_validate & BOND_ARP_FILTER;
+}
+
/* Get the oldest arp which we've received on this slave for bond's
* arp_targets.
*/
@@ -403,14 +420,10 @@ static inline unsigned long slave_oldest_target_arp_rx(struct bonding *bond,
static inline unsigned long slave_last_rx(struct bonding *bond,
struct slave *slave)
{
- if (slave_do_arp_validate(bond, slave)) {
- if (bond->params.arp_all_targets == BOND_ARP_TARGETS_ALL)
- return slave_oldest_target_arp_rx(bond, slave);
- else
- return slave->last_arp_rx;
- }
+ if (bond->params.arp_all_targets == BOND_ARP_TARGETS_ALL)
+ return slave_oldest_target_arp_rx(bond, slave);
- return slave->dev->last_rx;
+ return slave->last_rx;
}
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -487,8 +500,6 @@ void bond_sysfs_slave_del(struct slave *slave);
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev);
int bond_release(struct net_device *bond_dev, struct net_device *slave_dev);
int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count);
-int bond_parse_parm(const char *mode_arg, const struct bond_parm_tbl *tbl);
-int bond_parm_tbl_lookup(int mode, const struct bond_parm_tbl *tbl);
void bond_select_active_slave(struct bonding *bond);
void bond_change_active_slave(struct bonding *bond, struct slave *new_active);
void bond_create_debugfs(void);
@@ -501,8 +512,6 @@ void bond_setup(struct net_device *bond_dev);
unsigned int bond_get_num_tx_queues(void);
int bond_netlink_init(void);
void bond_netlink_fini(void);
-int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target);
-int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target);
struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond);
struct net_device *bond_option_active_slave_get(struct bonding *bond);
const char *bond_slave_link_status(s8 link);
diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c
index 88a6a5810ec6..fc73865bb83a 100644
--- a/drivers/net/caif/caif_serial.c
+++ b/drivers/net/caif/caif_serial.c
@@ -204,7 +204,6 @@ static void ldisc_receive(struct tty_struct *tty, const u8 *data,
skb->protocol = htons(ETH_P_CAIF);
skb_reset_mac_header(skb);
- skb->dev = ser->dev;
debugfs_rx(ser, data, count);
/* Push received packet up the stack. */
ret = netif_rx_ni(skb);
diff --git a/drivers/net/caif/caif_spi.c b/drivers/net/caif/caif_spi.c
index 155db68e13ba..ff54c0eb2052 100644
--- a/drivers/net/caif/caif_spi.c
+++ b/drivers/net/caif/caif_spi.c
@@ -554,7 +554,6 @@ int cfspi_rxfrm(struct cfspi *cfspi, u8 *buf, size_t len)
skb->protocol = htons(ETH_P_CAIF);
skb_reset_mac_header(skb);
- skb->dev = cfspi->ndev;
/*
* Push received packet up the stack.
diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c
index 6efe27458116..f07fa89b5fd5 100644
--- a/drivers/net/can/at91_can.c
+++ b/drivers/net/can/at91_can.c
@@ -420,7 +420,11 @@ static void at91_chip_start(struct net_device *dev)
at91_transceiver_switch(priv, 1);
/* enable chip */
- at91_write(priv, AT91_MR, AT91_MR_CANEN);
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ reg_mr = AT91_MR_CANEN | AT91_MR_ABM;
+ else
+ reg_mr = AT91_MR_CANEN;
+ at91_write(priv, AT91_MR, reg_mr);
priv->can.state = CAN_STATE_ERROR_ACTIVE;
@@ -1190,6 +1194,7 @@ static const struct net_device_ops at91_netdev_ops = {
.ndo_open = at91_open,
.ndo_stop = at91_close,
.ndo_start_xmit = at91_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static ssize_t at91_sysfs_show_mb0_id(struct device *dev,
@@ -1341,7 +1346,8 @@ static int at91_can_probe(struct platform_device *pdev)
priv->can.bittiming_const = &at91_bittiming_const;
priv->can.do_set_mode = at91_set_mode;
priv->can.do_get_berr_counter = at91_get_berr_counter;
- priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
+ CAN_CTRLMODE_LISTENONLY;
priv->dev = dev;
priv->reg_base = addr;
priv->devtype_data = *devtype_data;
diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c
index 8d2b89a12e09..543ecceb33e9 100644
--- a/drivers/net/can/bfin_can.c
+++ b/drivers/net/can/bfin_can.c
@@ -528,6 +528,7 @@ static const struct net_device_ops bfin_can_netdev_ops = {
.ndo_open = bfin_can_open,
.ndo_stop = bfin_can_close,
.ndo_start_xmit = bfin_can_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static int bfin_can_probe(struct platform_device *pdev)
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c
index 951bfede8f3d..a5c8dcfa8357 100644
--- a/drivers/net/can/c_can/c_can.c
+++ b/drivers/net/can/c_can/c_can.c
@@ -114,6 +114,14 @@
IF_COMM_CONTROL | IF_COMM_TXRQST | \
IF_COMM_DATAA | IF_COMM_DATAB)
+/* For the low buffers we clear the interrupt bit, but keep newdat */
+#define IF_COMM_RCV_LOW (IF_COMM_MASK | IF_COMM_ARB | \
+ IF_COMM_CONTROL | IF_COMM_CLR_INT_PND | \
+ IF_COMM_DATAA | IF_COMM_DATAB)
+
+/* For the high buffers we clear the interrupt bit and newdat */
+#define IF_COMM_RCV_HIGH (IF_COMM_RCV_LOW | IF_COMM_TXRQST)
+
/* IFx arbitration */
#define IF_ARB_MSGVAL BIT(15)
#define IF_ARB_MSGXTD BIT(14)
@@ -122,7 +130,6 @@
/* IFx message control */
#define IF_MCONT_NEWDAT BIT(15)
#define IF_MCONT_MSGLST BIT(14)
-#define IF_MCONT_CLR_MSGLST (0 << 14)
#define IF_MCONT_INTPND BIT(13)
#define IF_MCONT_UMASK BIT(12)
#define IF_MCONT_TXIE BIT(11)
@@ -133,31 +140,10 @@
#define IF_MCONT_DLC_MASK 0xf
/*
- * IFx register masks:
- * allow easy operation on 16-bit registers when the
- * argument is 32-bit instead
+ * Use IF1 for RX and IF2 for TX
*/
-#define IFX_WRITE_LOW_16BIT(x) ((x) & 0xFFFF)
-#define IFX_WRITE_HIGH_16BIT(x) (((x) & 0xFFFF0000) >> 16)
-
-/* message object split */
-#define C_CAN_NO_OF_OBJECTS 32
-#define C_CAN_MSG_OBJ_RX_NUM 16
-#define C_CAN_MSG_OBJ_TX_NUM 16
-
-#define C_CAN_MSG_OBJ_RX_FIRST 1
-#define C_CAN_MSG_OBJ_RX_LAST (C_CAN_MSG_OBJ_RX_FIRST + \
- C_CAN_MSG_OBJ_RX_NUM - 1)
-
-#define C_CAN_MSG_OBJ_TX_FIRST (C_CAN_MSG_OBJ_RX_LAST + 1)
-#define C_CAN_MSG_OBJ_TX_LAST (C_CAN_MSG_OBJ_TX_FIRST + \
- C_CAN_MSG_OBJ_TX_NUM - 1)
-
-#define C_CAN_MSG_OBJ_RX_SPLIT 9
-#define C_CAN_MSG_RX_LOW_LAST (C_CAN_MSG_OBJ_RX_SPLIT - 1)
-
-#define C_CAN_NEXT_MSG_OBJ_MASK (C_CAN_MSG_OBJ_TX_NUM - 1)
-#define RECEIVE_OBJECT_BITS 0x0000ffff
+#define IF_RX 0
+#define IF_TX 1
/* status interrupt */
#define STATUS_INTERRUPT 0x8000
@@ -246,10 +232,9 @@ static inline int get_tx_next_msg_obj(const struct c_can_priv *priv)
C_CAN_MSG_OBJ_TX_FIRST;
}
-static inline int get_tx_echo_msg_obj(const struct c_can_priv *priv)
+static inline int get_tx_echo_msg_obj(int txecho)
{
- return (priv->tx_echo & C_CAN_NEXT_MSG_OBJ_MASK) +
- C_CAN_MSG_OBJ_TX_FIRST;
+ return (txecho & C_CAN_NEXT_MSG_OBJ_MASK) + C_CAN_MSG_OBJ_TX_FIRST;
}
static u32 c_can_read_reg32(struct c_can_priv *priv, enum reg index)
@@ -366,18 +351,6 @@ static void c_can_write_msg_object(struct net_device *dev,
c_can_object_put(dev, iface, objno, IF_COMM_ALL);
}
-static inline void c_can_mark_rx_msg_obj(struct net_device *dev,
- int iface, int ctrl_mask,
- int obj)
-{
- struct c_can_priv *priv = netdev_priv(dev);
-
- priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface),
- ctrl_mask & ~(IF_MCONT_MSGLST | IF_MCONT_INTPND));
- c_can_object_put(dev, iface, obj, IF_COMM_CONTROL);
-
-}
-
static inline void c_can_activate_all_lower_rx_msg_obj(struct net_device *dev,
int iface,
int ctrl_mask)
@@ -387,45 +360,27 @@ static inline void c_can_activate_all_lower_rx_msg_obj(struct net_device *dev,
for (i = C_CAN_MSG_OBJ_RX_FIRST; i <= C_CAN_MSG_RX_LOW_LAST; i++) {
priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface),
- ctrl_mask & ~(IF_MCONT_MSGLST |
- IF_MCONT_INTPND | IF_MCONT_NEWDAT));
+ ctrl_mask & ~IF_MCONT_NEWDAT);
c_can_object_put(dev, iface, i, IF_COMM_CONTROL);
}
}
-static inline void c_can_activate_rx_msg_obj(struct net_device *dev,
- int iface, int ctrl_mask,
- int obj)
-{
- struct c_can_priv *priv = netdev_priv(dev);
-
- priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface),
- ctrl_mask & ~(IF_MCONT_MSGLST |
- IF_MCONT_INTPND | IF_MCONT_NEWDAT));
- c_can_object_put(dev, iface, obj, IF_COMM_CONTROL);
-}
-
-static void c_can_handle_lost_msg_obj(struct net_device *dev,
- int iface, int objno)
+static int c_can_handle_lost_msg_obj(struct net_device *dev,
+ int iface, int objno, u32 ctrl)
{
- struct c_can_priv *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
- struct sk_buff *skb;
+ struct c_can_priv *priv = netdev_priv(dev);
struct can_frame *frame;
+ struct sk_buff *skb;
- netdev_err(dev, "msg lost in buffer %d\n", objno);
-
- c_can_object_get(dev, iface, objno, IF_COMM_ALL & ~IF_COMM_TXRQST);
-
- priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface),
- IF_MCONT_CLR_MSGLST);
-
- c_can_object_put(dev, 0, objno, IF_COMM_CONTROL);
+ ctrl &= ~(IF_MCONT_MSGLST | IF_MCONT_INTPND | IF_MCONT_NEWDAT);
+ priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl);
+ c_can_object_put(dev, iface, objno, IF_COMM_CONTROL);
/* create an error msg */
skb = alloc_can_err_skb(dev, &frame);
if (unlikely(!skb))
- return;
+ return 0;
frame->can_id |= CAN_ERR_CRTL;
frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
@@ -433,6 +388,7 @@ static void c_can_handle_lost_msg_obj(struct net_device *dev,
stats->rx_over_errors++;
netif_receive_skb(skb);
+ return 1;
}
static int c_can_read_msg_object(struct net_device *dev, int iface, int ctrl)
@@ -477,9 +433,6 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, int ctrl)
stats->rx_packets++;
stats->rx_bytes += frame->can_dlc;
-
- can_led_event(dev, CAN_LED_EVENT_RX);
-
return 0;
}
@@ -548,10 +501,12 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb,
if (can_dropped_invalid_skb(dev, skb))
return NETDEV_TX_OK;
+ spin_lock_bh(&priv->xmit_lock);
msg_obj_no = get_tx_next_msg_obj(priv);
/* prepare message object for transmission */
- c_can_write_msg_object(dev, 0, frame, msg_obj_no);
+ c_can_write_msg_object(dev, IF_TX, frame, msg_obj_no);
+ priv->dlc[msg_obj_no - C_CAN_MSG_OBJ_TX_FIRST] = frame->can_dlc;
can_put_echo_skb(skb, dev, msg_obj_no - C_CAN_MSG_OBJ_TX_FIRST);
/*
@@ -562,10 +517,26 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb,
if (c_can_is_next_tx_obj_busy(priv, get_tx_next_msg_obj(priv)) ||
(priv->tx_next & C_CAN_NEXT_MSG_OBJ_MASK) == 0)
netif_stop_queue(dev);
+ spin_unlock_bh(&priv->xmit_lock);
return NETDEV_TX_OK;
}
+static int c_can_wait_for_ctrl_init(struct net_device *dev,
+ struct c_can_priv *priv, u32 init)
+{
+ int retry = 0;
+
+ while (init != (priv->read_reg(priv, C_CAN_CTRL_REG) & CONTROL_INIT)) {
+ udelay(10);
+ if (retry++ > 1000) {
+ netdev_err(dev, "CCTRL: set CONTROL_INIT failed\n");
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
static int c_can_set_bittiming(struct net_device *dev)
{
unsigned int reg_btr, reg_brpe, ctrl_save;
@@ -573,6 +544,7 @@ static int c_can_set_bittiming(struct net_device *dev)
u32 ten_bit_brp;
struct c_can_priv *priv = netdev_priv(dev);
const struct can_bittiming *bt = &priv->can.bittiming;
+ int res;
/* c_can provides a 6-bit brp and 4-bit brpe fields */
ten_bit_brp = bt->brp - 1;
@@ -590,13 +562,17 @@ static int c_can_set_bittiming(struct net_device *dev)
"setting BTR=%04x BRPE=%04x\n", reg_btr, reg_brpe);
ctrl_save = priv->read_reg(priv, C_CAN_CTRL_REG);
- priv->write_reg(priv, C_CAN_CTRL_REG,
- ctrl_save | CONTROL_CCE | CONTROL_INIT);
+ ctrl_save &= ~CONTROL_INIT;
+ priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_CCE | CONTROL_INIT);
+ res = c_can_wait_for_ctrl_init(dev, priv, CONTROL_INIT);
+ if (res)
+ return res;
+
priv->write_reg(priv, C_CAN_BTR_REG, reg_btr);
priv->write_reg(priv, C_CAN_BRPEXT_REG, reg_brpe);
priv->write_reg(priv, C_CAN_CTRL_REG, ctrl_save);
- return 0;
+ return c_can_wait_for_ctrl_init(dev, priv, 0);
}
/*
@@ -614,14 +590,14 @@ static void c_can_configure_msg_objects(struct net_device *dev)
/* first invalidate all message objects */
for (i = C_CAN_MSG_OBJ_RX_FIRST; i <= C_CAN_NO_OF_OBJECTS; i++)
- c_can_inval_msg_object(dev, 0, i);
+ c_can_inval_msg_object(dev, IF_RX, i);
/* setup receive message objects */
for (i = C_CAN_MSG_OBJ_RX_FIRST; i < C_CAN_MSG_OBJ_RX_LAST; i++)
- c_can_setup_receive_object(dev, 0, i, 0, 0,
+ c_can_setup_receive_object(dev, IF_RX, i, 0, 0,
(IF_MCONT_RXIE | IF_MCONT_UMASK) & ~IF_MCONT_EOB);
- c_can_setup_receive_object(dev, 0, C_CAN_MSG_OBJ_RX_LAST, 0, 0,
+ c_can_setup_receive_object(dev, IF_RX, C_CAN_MSG_OBJ_RX_LAST, 0, 0,
IF_MCONT_EOB | IF_MCONT_RXIE | IF_MCONT_UMASK);
}
@@ -631,7 +607,7 @@ static void c_can_configure_msg_objects(struct net_device *dev)
* - set operating mode
* - configure message objects
*/
-static void c_can_chip_config(struct net_device *dev)
+static int c_can_chip_config(struct net_device *dev)
{
struct c_can_priv *priv = netdev_priv(dev);
@@ -668,15 +644,18 @@ static void c_can_chip_config(struct net_device *dev)
priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED);
/* set bittiming params */
- c_can_set_bittiming(dev);
+ return c_can_set_bittiming(dev);
}
-static void c_can_start(struct net_device *dev)
+static int c_can_start(struct net_device *dev)
{
struct c_can_priv *priv = netdev_priv(dev);
+ int err;
/* basic c_can configuration */
- c_can_chip_config(dev);
+ err = c_can_chip_config(dev);
+ if (err)
+ return err;
priv->can.state = CAN_STATE_ERROR_ACTIVE;
@@ -685,6 +664,8 @@ static void c_can_start(struct net_device *dev)
/* enable status change, error and module interrupts */
c_can_enable_all_interrupts(priv, ENABLE_ALL_INTERRUPTS);
+
+ return 0;
}
static void c_can_stop(struct net_device *dev)
@@ -700,9 +681,13 @@ static void c_can_stop(struct net_device *dev)
static int c_can_set_mode(struct net_device *dev, enum can_mode mode)
{
+ int err;
+
switch (mode) {
case CAN_MODE_START:
- c_can_start(dev);
+ err = c_can_start(dev);
+ if (err)
+ return err;
netif_wake_queue(dev);
break;
default:
@@ -740,8 +725,6 @@ static int c_can_get_berr_counter(const struct net_device *dev,
}
/*
- * theory of operation:
- *
* priv->tx_echo holds the number of the oldest can_frame put for
* transmission into the hardware, but not yet ACKed by the CAN tx
* complete IRQ.
@@ -752,33 +735,113 @@ static int c_can_get_berr_counter(const struct net_device *dev,
*/
static void c_can_do_tx(struct net_device *dev)
{
- u32 val;
- u32 msg_obj_no;
struct c_can_priv *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
+ u32 val, obj, pkts = 0, bytes = 0;
- for (/* nix */; (priv->tx_next - priv->tx_echo) > 0; priv->tx_echo++) {
- msg_obj_no = get_tx_echo_msg_obj(priv);
+ spin_lock_bh(&priv->xmit_lock);
+
+ for (; (priv->tx_next - priv->tx_echo) > 0; priv->tx_echo++) {
+ obj = get_tx_echo_msg_obj(priv->tx_echo);
val = c_can_read_reg32(priv, C_CAN_TXRQST1_REG);
- if (!(val & (1 << (msg_obj_no - 1)))) {
- can_get_echo_skb(dev,
- msg_obj_no - C_CAN_MSG_OBJ_TX_FIRST);
- c_can_object_get(dev, 0, msg_obj_no, IF_COMM_ALL);
- stats->tx_bytes += priv->read_reg(priv,
- C_CAN_IFACE(MSGCTRL_REG, 0))
- & IF_MCONT_DLC_MASK;
- stats->tx_packets++;
- can_led_event(dev, CAN_LED_EVENT_TX);
- c_can_inval_msg_object(dev, 0, msg_obj_no);
- } else {
+
+ if (val & (1 << (obj - 1)))
break;
- }
+
+ can_get_echo_skb(dev, obj - C_CAN_MSG_OBJ_TX_FIRST);
+ bytes += priv->dlc[obj - C_CAN_MSG_OBJ_TX_FIRST];
+ pkts++;
+ c_can_inval_msg_object(dev, IF_TX, obj);
}
/* restart queue if wrap-up or if queue stalled on last pkt */
if (((priv->tx_next & C_CAN_NEXT_MSG_OBJ_MASK) != 0) ||
((priv->tx_echo & C_CAN_NEXT_MSG_OBJ_MASK) == 0))
netif_wake_queue(dev);
+
+ spin_unlock_bh(&priv->xmit_lock);
+
+ if (pkts) {
+ stats->tx_bytes += bytes;
+ stats->tx_packets += pkts;
+ can_led_event(dev, CAN_LED_EVENT_TX);
+ }
+}
+
+/*
+ * If we have a gap in the pending bits, that means we either
+ * raced with the hardware or failed to readout all upper
+ * objects in the last run due to quota limit.
+ */
+static u32 c_can_adjust_pending(u32 pend)
+{
+ u32 weight, lasts;
+
+ if (pend == RECEIVE_OBJECT_BITS)
+ return pend;
+
+ /*
+ * If the last set bit is larger than the number of pending
+ * bits we have a gap.
+ */
+ weight = hweight32(pend);
+ lasts = fls(pend);
+
+ /* If the bits are linear, nothing to do */
+ if (lasts == weight)
+ return pend;
+
+ /*
+ * Find the first set bit after the gap. We walk backwards
+ * from the last set bit.
+ */
+ for (lasts--; pend & (1 << (lasts - 1)); lasts--);
+
+ return pend & ~((1 << lasts) - 1);
+}
+
+static int c_can_read_objects(struct net_device *dev, struct c_can_priv *priv,
+ u32 pend, int quota)
+{
+ u32 pkts = 0, ctrl, obj, mcmd;
+
+ while ((obj = ffs(pend)) && quota > 0) {
+ pend &= ~BIT(obj - 1);
+
+ mcmd = obj < C_CAN_MSG_RX_LOW_LAST ?
+ IF_COMM_RCV_LOW : IF_COMM_RCV_HIGH;
+
+ c_can_object_get(dev, IF_RX, obj, mcmd);
+ ctrl = priv->read_reg(priv, C_CAN_IFACE(MSGCTRL_REG, IF_RX));
+
+ if (ctrl & IF_MCONT_MSGLST) {
+ int n = c_can_handle_lost_msg_obj(dev, IF_RX, obj, ctrl);
+
+ pkts += n;
+ quota -= n;
+ continue;
+ }
+
+ /*
+ * This really should not happen, but this covers some
+ * odd HW behaviour. Do not remove that unless you
+ * want to brick your machine.
+ */
+ if (!(ctrl & IF_MCONT_NEWDAT))
+ continue;
+
+ /* read the data from the message object */
+ c_can_read_msg_object(dev, IF_RX, ctrl);
+
+ if (obj == C_CAN_MSG_RX_LOW_LAST)
+ /* activate all lower message objects */
+ c_can_activate_all_lower_rx_msg_obj(dev, IF_RX, ctrl);
+
+ pkts++;
+ quota--;
+ }
+
+ return pkts;
}
/*
@@ -805,10 +868,8 @@ static void c_can_do_tx(struct net_device *dev)
*/
static int c_can_do_rx_poll(struct net_device *dev, int quota)
{
- u32 num_rx_pkts = 0;
- unsigned int msg_obj, msg_ctrl_save;
struct c_can_priv *priv = netdev_priv(dev);
- u16 val;
+ u32 pkts = 0, pend = 0, toread, n;
/*
* It is faster to read only one 16bit register. This is only possible
@@ -817,49 +878,31 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota)
BUILD_BUG_ON_MSG(C_CAN_MSG_OBJ_RX_LAST > 16,
"Implementation does not support more message objects than 16");
- while (quota > 0 && (val = priv->read_reg(priv, C_CAN_INTPND1_REG))) {
- while ((msg_obj = ffs(val)) && quota > 0) {
- val &= ~BIT(msg_obj - 1);
-
- c_can_object_get(dev, 0, msg_obj, IF_COMM_ALL &
- ~IF_COMM_TXRQST);
- msg_ctrl_save = priv->read_reg(priv,
- C_CAN_IFACE(MSGCTRL_REG, 0));
-
- if (msg_ctrl_save & IF_MCONT_MSGLST) {
- c_can_handle_lost_msg_obj(dev, 0, msg_obj);
- num_rx_pkts++;
- quota--;
- continue;
- }
-
- if (msg_ctrl_save & IF_MCONT_EOB)
- return num_rx_pkts;
-
- if (!(msg_ctrl_save & IF_MCONT_NEWDAT))
- continue;
-
- /* read the data from the message object */
- c_can_read_msg_object(dev, 0, msg_ctrl_save);
-
- if (msg_obj < C_CAN_MSG_RX_LOW_LAST)
- c_can_mark_rx_msg_obj(dev, 0,
- msg_ctrl_save, msg_obj);
- else if (msg_obj > C_CAN_MSG_RX_LOW_LAST)
- /* activate this msg obj */
- c_can_activate_rx_msg_obj(dev, 0,
- msg_ctrl_save, msg_obj);
- else if (msg_obj == C_CAN_MSG_RX_LOW_LAST)
- /* activate all lower message objects */
- c_can_activate_all_lower_rx_msg_obj(dev,
- 0, msg_ctrl_save);
-
- num_rx_pkts++;
- quota--;
+ while (quota > 0) {
+ if (!pend) {
+ pend = priv->read_reg(priv, C_CAN_INTPND1_REG);
+ if (!pend)
+ break;
+ /*
+ * If the pending field has a gap, handle the
+ * bits above the gap first.
+ */
+ toread = c_can_adjust_pending(pend);
+ } else {
+ toread = pend;
}
+ /* Remove the bits from pend */
+ pend &= ~toread;
+ /* Read the objects */
+ n = c_can_read_objects(dev, priv, toread, quota);
+ pkts += n;
+ quota -= n;
}
- return num_rx_pkts;
+ if (pkts)
+ can_led_event(dev, CAN_LED_EVENT_RX);
+
+ return pkts;
}
static inline int c_can_has_and_handle_berr(struct c_can_priv *priv)
@@ -1133,17 +1176,20 @@ static int c_can_open(struct net_device *dev)
goto exit_irq_fail;
}
- napi_enable(&priv->napi);
+ /* start the c_can controller */
+ err = c_can_start(dev);
+ if (err)
+ goto exit_start_fail;
can_led_event(dev, CAN_LED_EVENT_OPEN);
- /* start the c_can controller */
- c_can_start(dev);
-
+ napi_enable(&priv->napi);
netif_start_queue(dev);
return 0;
+exit_start_fail:
+ free_irq(dev->irq, dev);
exit_irq_fail:
close_candev(dev);
exit_open_fail:
@@ -1180,6 +1226,7 @@ struct net_device *alloc_c_can_dev(void)
return NULL;
priv = netdev_priv(dev);
+ spin_lock_init(&priv->xmit_lock);
netif_napi_add(dev, &priv->napi, c_can_poll, C_CAN_NAPI_WEIGHT);
priv->dev = dev;
@@ -1260,15 +1307,16 @@ int c_can_power_up(struct net_device *dev)
if (time_after(jiffies, time_out))
return -ETIMEDOUT;
- c_can_start(dev);
-
- return 0;
+ return c_can_start(dev);
}
EXPORT_SYMBOL_GPL(c_can_power_up);
#endif
void free_c_can_dev(struct net_device *dev)
{
+ struct c_can_priv *priv = netdev_priv(dev);
+
+ netif_napi_del(&priv->napi);
free_candev(dev);
}
EXPORT_SYMBOL_GPL(free_c_can_dev);
@@ -1277,6 +1325,7 @@ static const struct net_device_ops c_can_netdev_ops = {
.ndo_open = c_can_open,
.ndo_stop = c_can_close,
.ndo_start_xmit = c_can_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
int register_c_can_dev(struct net_device *dev)
diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h
index d2e1c21b143f..faa8404162b3 100644
--- a/drivers/net/can/c_can/c_can.h
+++ b/drivers/net/can/c_can/c_can.h
@@ -22,6 +22,33 @@
#ifndef C_CAN_H
#define C_CAN_H
+/*
+ * IFx register masks:
+ * allow easy operation on 16-bit registers when the
+ * argument is 32-bit instead
+ */
+#define IFX_WRITE_LOW_16BIT(x) ((x) & 0xFFFF)
+#define IFX_WRITE_HIGH_16BIT(x) (((x) & 0xFFFF0000) >> 16)
+
+/* message object split */
+#define C_CAN_NO_OF_OBJECTS 32
+#define C_CAN_MSG_OBJ_RX_NUM 16
+#define C_CAN_MSG_OBJ_TX_NUM 16
+
+#define C_CAN_MSG_OBJ_RX_FIRST 1
+#define C_CAN_MSG_OBJ_RX_LAST (C_CAN_MSG_OBJ_RX_FIRST + \
+ C_CAN_MSG_OBJ_RX_NUM - 1)
+
+#define C_CAN_MSG_OBJ_TX_FIRST (C_CAN_MSG_OBJ_RX_LAST + 1)
+#define C_CAN_MSG_OBJ_TX_LAST (C_CAN_MSG_OBJ_TX_FIRST + \
+ C_CAN_MSG_OBJ_TX_NUM - 1)
+
+#define C_CAN_MSG_OBJ_RX_SPLIT 9
+#define C_CAN_MSG_RX_LOW_LAST (C_CAN_MSG_OBJ_RX_SPLIT - 1)
+
+#define C_CAN_NEXT_MSG_OBJ_MASK (C_CAN_MSG_OBJ_TX_NUM - 1)
+#define RECEIVE_OBJECT_BITS 0x0000ffff
+
enum reg {
C_CAN_CTRL_REG = 0,
C_CAN_CTRL_EX_REG,
@@ -156,6 +183,7 @@ struct c_can_priv {
struct napi_struct napi;
struct net_device *dev;
struct device *device;
+ spinlock_t xmit_lock;
int tx_object;
int current_status;
int last_status;
@@ -172,6 +200,7 @@ struct c_can_priv {
u32 __iomem *raminit_ctrlreg;
unsigned int instance;
void (*raminit) (const struct c_can_priv *priv, bool enable);
+ u32 dlc[C_CAN_MSG_OBJ_TX_NUM];
};
struct net_device *alloc_c_can_dev(void);
diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c
index d66ac265269c..806d92753427 100644
--- a/drivers/net/can/c_can/c_can_platform.c
+++ b/drivers/net/can/c_can/c_can_platform.c
@@ -37,8 +37,10 @@
#include "c_can.h"
-#define CAN_RAMINIT_START_MASK(i) (1 << (i))
-
+#define CAN_RAMINIT_START_MASK(i) (0x001 << (i))
+#define CAN_RAMINIT_DONE_MASK(i) (0x100 << (i))
+#define CAN_RAMINIT_ALL_MASK(i) (0x101 << (i))
+static DEFINE_SPINLOCK(raminit_lock);
/*
* 16-bit c_can registers can be arranged differently in the memory
* architecture of different implementations. For example: 16-bit
@@ -69,16 +71,41 @@ static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv,
writew(val, priv->base + 2 * priv->regs[index]);
}
+static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask,
+ u32 val)
+{
+ /* We look only at the bits of our instance. */
+ val &= mask;
+ while ((readl(priv->raminit_ctrlreg) & mask) != val)
+ udelay(1);
+}
+
static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
{
- u32 val;
-
- val = readl(priv->raminit_ctrlreg);
- if (enable)
- val |= CAN_RAMINIT_START_MASK(priv->instance);
- else
- val &= ~CAN_RAMINIT_START_MASK(priv->instance);
- writel(val, priv->raminit_ctrlreg);
+ u32 mask = CAN_RAMINIT_ALL_MASK(priv->instance);
+ u32 ctrl;
+
+ spin_lock(&raminit_lock);
+
+ ctrl = readl(priv->raminit_ctrlreg);
+ /* We clear the done and start bit first. The start bit is
+ * looking at the 0 -> transition, but is not self clearing;
+ * And we clear the init done bit as well.
+ */
+ ctrl &= ~CAN_RAMINIT_START_MASK(priv->instance);
+ ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance);
+ writel(ctrl, priv->raminit_ctrlreg);
+ ctrl &= ~CAN_RAMINIT_DONE_MASK(priv->instance);
+ c_can_hw_raminit_wait(priv, ctrl, mask);
+
+ if (enable) {
+ /* Set start bit and wait for the done bit. */
+ ctrl |= CAN_RAMINIT_START_MASK(priv->instance);
+ writel(ctrl, priv->raminit_ctrlreg);
+ ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance);
+ c_can_hw_raminit_wait(priv, ctrl, mask);
+ }
+ spin_unlock(&raminit_lock);
}
static struct platform_device_id c_can_id_table[] = {
diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c
index 0f12abf6591c..d8379278d648 100644
--- a/drivers/net/can/cc770/cc770.c
+++ b/drivers/net/can/cc770/cc770.c
@@ -823,6 +823,7 @@ static const struct net_device_ops cc770_netdev_ops = {
.ndo_open = cc770_open,
.ndo_stop = cc770_close,
.ndo_start_xmit = cc770_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
int register_cc770dev(struct net_device *dev)
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index fc59bc6f040b..c7a260478749 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -99,10 +99,10 @@ static int can_update_spt(const struct can_bittiming_const *btc,
return 1000 * (tseg + 1 - *tseg2) / (tseg + 1);
}
-static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt)
+static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
+ const struct can_bittiming_const *btc)
{
struct can_priv *priv = netdev_priv(dev);
- const struct can_bittiming_const *btc = priv->bittiming_const;
long rate, best_rate = 0;
long best_error = 1000000000, error = 0;
int best_tseg = 0, best_brp = 0, brp = 0;
@@ -110,9 +110,6 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt)
int spt_error = 1000, spt = 0, sampl_pt;
u64 v64;
- if (!priv->bittiming_const)
- return -ENOTSUPP;
-
/* Use CIA recommended sample points */
if (bt->sample_point) {
sampl_pt = bt->sample_point;
@@ -204,7 +201,8 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt)
return 0;
}
#else /* !CONFIG_CAN_CALC_BITTIMING */
-static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt)
+static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
+ const struct can_bittiming_const *btc)
{
netdev_err(dev, "bit-timing calculation not available\n");
return -EINVAL;
@@ -217,16 +215,13 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt)
* prescaler value brp. You can find more information in the header
* file linux/can/netlink.h.
*/
-static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt)
+static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt,
+ const struct can_bittiming_const *btc)
{
struct can_priv *priv = netdev_priv(dev);
- const struct can_bittiming_const *btc = priv->bittiming_const;
int tseg1, alltseg;
u64 brp64;
- if (!priv->bittiming_const)
- return -ENOTSUPP;
-
tseg1 = bt->prop_seg + bt->phase_seg1;
if (!bt->sjw)
bt->sjw = 1;
@@ -254,26 +249,29 @@ static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt)
return 0;
}
-static int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt)
+static int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt,
+ const struct can_bittiming_const *btc)
{
- struct can_priv *priv = netdev_priv(dev);
int err;
/* Check if the CAN device has bit-timing parameters */
- if (priv->bittiming_const) {
+ if (!btc)
+ return -ENOTSUPP;
- /* Non-expert mode? Check if the bitrate has been pre-defined */
- if (!bt->tq)
- /* Determine bit-timing parameters */
- err = can_calc_bittiming(dev, bt);
- else
- /* Check bit-timing params and calculate proper brp */
- err = can_fixup_bittiming(dev, bt);
- if (err)
- return err;
- }
+ /*
+ * Depending on the given can_bittiming parameter structure the CAN
+ * timing parameters are calculated based on the provided bitrate OR
+ * alternatively the CAN timing parameters (tq, prop_seg, etc.) are
+ * provided directly which are then checked and fixed up.
+ */
+ if (!bt->tq && bt->bitrate)
+ err = can_calc_bittiming(dev, bt, btc);
+ else if (bt->tq && !bt->bitrate)
+ err = can_fixup_bittiming(dev, bt, btc);
+ else
+ err = -EINVAL;
- return 0;
+ return err;
}
/*
@@ -317,7 +315,9 @@ void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
BUG_ON(idx >= priv->echo_skb_max);
/* check flag whether this packet has to be looped back */
- if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK) {
+ if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK ||
+ (skb->protocol != htons(ETH_P_CAN) &&
+ skb->protocol != htons(ETH_P_CANFD))) {
kfree_skb(skb);
return;
}
@@ -329,7 +329,6 @@ void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
return;
/* make settings for echo to reduce code in irq context */
- skb->protocol = htons(ETH_P_CAN);
skb->pkt_type = PACKET_BROADCAST;
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->dev = dev;
@@ -512,6 +511,30 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
}
EXPORT_SYMBOL_GPL(alloc_can_skb);
+struct sk_buff *alloc_canfd_skb(struct net_device *dev,
+ struct canfd_frame **cfd)
+{
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
+ sizeof(struct canfd_frame));
+ if (unlikely(!skb))
+ return NULL;
+
+ skb->protocol = htons(ETH_P_CANFD);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ can_skb_reserve(skb);
+ can_skb_prv(skb)->ifindex = dev->ifindex;
+
+ *cfd = (struct canfd_frame *)skb_put(skb, sizeof(struct canfd_frame));
+ memset(*cfd, 0, sizeof(struct canfd_frame));
+
+ return skb;
+}
+EXPORT_SYMBOL_GPL(alloc_canfd_skb);
+
struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf)
{
struct sk_buff *skb;
@@ -572,6 +595,39 @@ void free_candev(struct net_device *dev)
EXPORT_SYMBOL_GPL(free_candev);
/*
+ * changing MTU and control mode for CAN/CANFD devices
+ */
+int can_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* Do not allow changing the MTU while running */
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ /* allow change of MTU according to the CANFD ability of the device */
+ switch (new_mtu) {
+ case CAN_MTU:
+ priv->ctrlmode &= ~CAN_CTRLMODE_FD;
+ break;
+
+ case CANFD_MTU:
+ if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD))
+ return -EINVAL;
+
+ priv->ctrlmode |= CAN_CTRLMODE_FD;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ dev->mtu = new_mtu;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(can_change_mtu);
+
+/*
* Common open function when the device gets opened.
*
* This function should be called in the open function of the device
@@ -581,11 +637,19 @@ int open_candev(struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
- if (!priv->bittiming.tq && !priv->bittiming.bitrate) {
+ if (!priv->bittiming.bitrate) {
netdev_err(dev, "bit-timing not yet defined\n");
return -EINVAL;
}
+ /* For CAN FD the data bitrate has to be >= the arbitration bitrate */
+ if ((priv->ctrlmode & CAN_CTRLMODE_FD) &&
+ (!priv->data_bittiming.bitrate ||
+ (priv->data_bittiming.bitrate < priv->bittiming.bitrate))) {
+ netdev_err(dev, "incorrect/missing data bit-timing\n");
+ return -EINVAL;
+ }
+
/* Switch carrier on if device was stopped while in bus-off state */
if (!netif_carrier_ok(dev))
netif_carrier_on(dev);
@@ -624,6 +688,10 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
= { .len = sizeof(struct can_bittiming_const) },
[IFLA_CAN_CLOCK] = { .len = sizeof(struct can_clock) },
[IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct can_berr_counter) },
+ [IFLA_CAN_DATA_BITTIMING]
+ = { .len = sizeof(struct can_bittiming) },
+ [IFLA_CAN_DATA_BITTIMING_CONST]
+ = { .len = sizeof(struct can_bittiming_const) },
};
static int can_changelink(struct net_device *dev,
@@ -642,9 +710,7 @@ static int can_changelink(struct net_device *dev,
if (dev->flags & IFF_UP)
return -EBUSY;
memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt));
- if ((!bt.bitrate && !bt.tq) || (bt.bitrate && bt.tq))
- return -EINVAL;
- err = can_get_bittiming(dev, &bt);
+ err = can_get_bittiming(dev, &bt, priv->bittiming_const);
if (err)
return err;
memcpy(&priv->bittiming, &bt, sizeof(bt));
@@ -668,6 +734,12 @@ static int can_changelink(struct net_device *dev,
return -EOPNOTSUPP;
priv->ctrlmode &= ~cm->mask;
priv->ctrlmode |= cm->flags;
+
+ /* CAN_CTRLMODE_FD can only be set when driver supports FD */
+ if (priv->ctrlmode & CAN_CTRLMODE_FD)
+ dev->mtu = CANFD_MTU;
+ else
+ dev->mtu = CAN_MTU;
}
if (data[IFLA_CAN_RESTART_MS]) {
@@ -686,6 +758,27 @@ static int can_changelink(struct net_device *dev,
return err;
}
+ if (data[IFLA_CAN_DATA_BITTIMING]) {
+ struct can_bittiming dbt;
+
+ /* Do not allow changing bittiming while running */
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+ memcpy(&dbt, nla_data(data[IFLA_CAN_DATA_BITTIMING]),
+ sizeof(dbt));
+ err = can_get_bittiming(dev, &dbt, priv->data_bittiming_const);
+ if (err)
+ return err;
+ memcpy(&priv->data_bittiming, &dbt, sizeof(dbt));
+
+ if (priv->do_set_data_bittiming) {
+ /* Finally, set the bit-timing registers */
+ err = priv->do_set_data_bittiming(dev);
+ if (err)
+ return err;
+ }
+ }
+
return 0;
}
@@ -694,7 +787,8 @@ static size_t can_get_size(const struct net_device *dev)
struct can_priv *priv = netdev_priv(dev);
size_t size = 0;
- size += nla_total_size(sizeof(struct can_bittiming)); /* IFLA_CAN_BITTIMING */
+ if (priv->bittiming.bitrate) /* IFLA_CAN_BITTIMING */
+ size += nla_total_size(sizeof(struct can_bittiming));
if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */
size += nla_total_size(sizeof(struct can_bittiming_const));
size += nla_total_size(sizeof(struct can_clock)); /* IFLA_CAN_CLOCK */
@@ -703,6 +797,10 @@ static size_t can_get_size(const struct net_device *dev)
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */
if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */
size += nla_total_size(sizeof(struct can_berr_counter));
+ if (priv->data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */
+ size += nla_total_size(sizeof(struct can_bittiming));
+ if (priv->data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */
+ size += nla_total_size(sizeof(struct can_bittiming_const));
return size;
}
@@ -716,19 +814,34 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
if (priv->do_get_state)
priv->do_get_state(dev, &state);
- if (nla_put(skb, IFLA_CAN_BITTIMING,
- sizeof(priv->bittiming), &priv->bittiming) ||
+
+ if ((priv->bittiming.bitrate &&
+ nla_put(skb, IFLA_CAN_BITTIMING,
+ sizeof(priv->bittiming), &priv->bittiming)) ||
+
(priv->bittiming_const &&
nla_put(skb, IFLA_CAN_BITTIMING_CONST,
sizeof(*priv->bittiming_const), priv->bittiming_const)) ||
+
nla_put(skb, IFLA_CAN_CLOCK, sizeof(cm), &priv->clock) ||
nla_put_u32(skb, IFLA_CAN_STATE, state) ||
nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) ||
nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) ||
+
(priv->do_get_berr_counter &&
!priv->do_get_berr_counter(dev, &bec) &&
- nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)))
+ nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) ||
+
+ (priv->data_bittiming.bitrate &&
+ nla_put(skb, IFLA_CAN_DATA_BITTIMING,
+ sizeof(priv->data_bittiming), &priv->data_bittiming)) ||
+
+ (priv->data_bittiming_const &&
+ nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST,
+ sizeof(*priv->data_bittiming_const),
+ priv->data_bittiming_const)))
return -EMSGSIZE;
+
return 0;
}
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 61376abdab39..f425ec2c7839 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -1011,6 +1011,7 @@ static const struct net_device_ops flexcan_netdev_ops = {
.ndo_open = flexcan_open,
.ndo_stop = flexcan_close,
.ndo_start_xmit = flexcan_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static int register_flexcandev(struct net_device *dev)
@@ -1132,9 +1133,9 @@ static int flexcan_probe(struct platform_device *pdev)
of_id = of_match_device(flexcan_of_match, &pdev->dev);
if (of_id) {
devtype_data = of_id->data;
- } else if (pdev->id_entry->driver_data) {
+ } else if (platform_get_device_id(pdev)->driver_data) {
devtype_data = (struct flexcan_devtype_data *)
- pdev->id_entry->driver_data;
+ platform_get_device_id(pdev)->driver_data;
} else {
return -ENODEV;
}
@@ -1201,8 +1202,7 @@ static int flexcan_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int flexcan_suspend(struct device *device)
+static int __maybe_unused flexcan_suspend(struct device *device)
{
struct net_device *dev = dev_get_drvdata(device);
struct flexcan_priv *priv = netdev_priv(dev);
@@ -1221,7 +1221,7 @@ static int flexcan_suspend(struct device *device)
return 0;
}
-static int flexcan_resume(struct device *device)
+static int __maybe_unused flexcan_resume(struct device *device)
{
struct net_device *dev = dev_get_drvdata(device);
struct flexcan_priv *priv = netdev_priv(dev);
@@ -1233,7 +1233,6 @@ static int flexcan_resume(struct device *device)
}
return flexcan_chip_enable(priv);
}
-#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume);
diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c
index ab506d6cab37..3fd9fd942c6e 100644
--- a/drivers/net/can/grcan.c
+++ b/drivers/net/can/grcan.c
@@ -1578,6 +1578,7 @@ static const struct net_device_ops grcan_netdev_ops = {
.ndo_open = grcan_open,
.ndo_stop = grcan_close,
.ndo_start_xmit = grcan_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static int grcan_setup_netdev(struct platform_device *ofdev,
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
index 71594e5676fd..2382c04dc780 100644
--- a/drivers/net/can/janz-ican3.c
+++ b/drivers/net/can/janz-ican3.c
@@ -198,9 +198,6 @@ struct ican3_dev {
struct net_device *ndev;
struct napi_struct napi;
- /* Device for printing */
- struct device *dev;
-
/* module number */
unsigned int num;
@@ -295,7 +292,7 @@ static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
xord = locl ^ peer;
if ((xord & MSYNC_RB_MASK) == 0x00) {
- dev_dbg(mod->dev, "no mbox for reading\n");
+ netdev_dbg(mod->ndev, "no mbox for reading\n");
return -ENOMEM;
}
@@ -340,7 +337,7 @@ static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
xord = locl ^ peer;
if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) {
- dev_err(mod->dev, "no mbox for writing\n");
+ netdev_err(mod->ndev, "no mbox for writing\n");
return -ENOMEM;
}
@@ -542,7 +539,7 @@ static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
memcpy_fromio(&desc, desc_addr, sizeof(desc));
if (!(desc.control & DESC_VALID)) {
- dev_dbg(mod->dev, "%s: no free buffers\n", __func__);
+ netdev_dbg(mod->ndev, "%s: no free buffers\n", __func__);
return -ENOMEM;
}
@@ -573,7 +570,7 @@ static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
memcpy_fromio(&desc, desc_addr, sizeof(desc));
if (!(desc.control & DESC_VALID)) {
- dev_dbg(mod->dev, "%s: no buffers to recv\n", __func__);
+ netdev_dbg(mod->ndev, "%s: no buffers to recv\n", __func__);
return -ENOMEM;
}
@@ -883,7 +880,7 @@ static void can_frame_to_ican3(struct ican3_dev *mod,
*/
static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg)
{
- dev_dbg(mod->dev, "IDVERS response: %s\n", msg->data);
+ netdev_dbg(mod->ndev, "IDVERS response: %s\n", msg->data);
}
static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg)
@@ -899,7 +896,7 @@ static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg)
* error frame for userspace
*/
if (msg->spec == MSG_MSGLOST) {
- dev_err(mod->dev, "lost %d control messages\n", msg->data[0]);
+ netdev_err(mod->ndev, "lost %d control messages\n", msg->data[0]);
return;
}
@@ -939,13 +936,13 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
/* we can only handle the SJA1000 part */
if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
- dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
+ netdev_err(mod->ndev, "unable to handle errors on non-SJA1000\n");
return -ENODEV;
}
/* check the message length for sanity */
if (le16_to_cpu(msg->len) < 6) {
- dev_err(mod->dev, "error message too short\n");
+ netdev_err(mod->ndev, "error message too short\n");
return -EINVAL;
}
@@ -967,7 +964,7 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
*/
if (isrc == CEVTIND_BEI) {
int ret;
- dev_dbg(mod->dev, "bus error interrupt\n");
+ netdev_dbg(mod->ndev, "bus error interrupt\n");
/* TX error */
if (!(ecc & ECC_DIR)) {
@@ -983,7 +980,7 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
*/
ret = ican3_set_buserror(mod, 1);
if (ret) {
- dev_err(mod->dev, "unable to re-enable bus-error\n");
+ netdev_err(mod->ndev, "unable to re-enable bus-error\n");
return ret;
}
@@ -998,7 +995,7 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
/* data overrun interrupt */
if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {
- dev_dbg(mod->dev, "data overrun interrupt\n");
+ netdev_dbg(mod->ndev, "data overrun interrupt\n");
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
stats->rx_over_errors++;
@@ -1007,7 +1004,7 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
/* error warning + passive interrupt */
if (isrc == CEVTIND_EI) {
- dev_dbg(mod->dev, "error warning + passive interrupt\n");
+ netdev_dbg(mod->ndev, "error warning + passive interrupt\n");
if (status & SR_BS) {
state = CAN_STATE_BUS_OFF;
cf->can_id |= CAN_ERR_BUSOFF;
@@ -1088,7 +1085,7 @@ static void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg)
complete(&mod->termination_comp);
break;
default:
- dev_err(mod->dev, "received an unknown inquiry response\n");
+ netdev_err(mod->ndev, "received an unknown inquiry response\n");
break;
}
}
@@ -1096,7 +1093,7 @@ static void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg)
static void ican3_handle_unknown_message(struct ican3_dev *mod,
struct ican3_msg *msg)
{
- dev_warn(mod->dev, "received unknown message: spec 0x%.2x length %d\n",
+ netdev_warn(mod->ndev, "received unknown message: spec 0x%.2x length %d\n",
msg->spec, le16_to_cpu(msg->len));
}
@@ -1105,7 +1102,7 @@ static void ican3_handle_unknown_message(struct ican3_dev *mod,
*/
static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg)
{
- dev_dbg(mod->dev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__,
+ netdev_dbg(mod->ndev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__,
mod->num, msg->spec, le16_to_cpu(msg->len));
switch (msg->spec) {
@@ -1406,7 +1403,7 @@ static int ican3_reset_module(struct ican3_dev *mod)
msleep(10);
} while (time_before(jiffies, start + HZ / 4));
- dev_err(mod->dev, "failed to reset CAN module\n");
+ netdev_err(mod->ndev, "failed to reset CAN module\n");
return -ETIMEDOUT;
}
@@ -1425,7 +1422,7 @@ static int ican3_startup_module(struct ican3_dev *mod)
ret = ican3_reset_module(mod);
if (ret) {
- dev_err(mod->dev, "unable to reset module\n");
+ netdev_err(mod->ndev, "unable to reset module\n");
return ret;
}
@@ -1434,41 +1431,41 @@ static int ican3_startup_module(struct ican3_dev *mod)
ret = ican3_msg_connect(mod);
if (ret) {
- dev_err(mod->dev, "unable to connect to module\n");
+ netdev_err(mod->ndev, "unable to connect to module\n");
return ret;
}
ican3_init_new_host_interface(mod);
ret = ican3_msg_newhostif(mod);
if (ret) {
- dev_err(mod->dev, "unable to switch to new-style interface\n");
+ netdev_err(mod->ndev, "unable to switch to new-style interface\n");
return ret;
}
/* default to "termination on" */
ret = ican3_set_termination(mod, true);
if (ret) {
- dev_err(mod->dev, "unable to enable termination\n");
+ netdev_err(mod->ndev, "unable to enable termination\n");
return ret;
}
/* default to "bus errors enabled" */
ret = ican3_set_buserror(mod, 1);
if (ret) {
- dev_err(mod->dev, "unable to set bus-error\n");
+ netdev_err(mod->ndev, "unable to set bus-error\n");
return ret;
}
ican3_init_fast_host_interface(mod);
ret = ican3_msg_fasthostif(mod);
if (ret) {
- dev_err(mod->dev, "unable to switch to fast host interface\n");
+ netdev_err(mod->ndev, "unable to switch to fast host interface\n");
return ret;
}
ret = ican3_set_id_filter(mod, true);
if (ret) {
- dev_err(mod->dev, "unable to set acceptance filter\n");
+ netdev_err(mod->ndev, "unable to set acceptance filter\n");
return ret;
}
@@ -1487,14 +1484,14 @@ static int ican3_open(struct net_device *ndev)
/* open the CAN layer */
ret = open_candev(ndev);
if (ret) {
- dev_err(mod->dev, "unable to start CAN layer\n");
+ netdev_err(mod->ndev, "unable to start CAN layer\n");
return ret;
}
/* bring the bus online */
ret = ican3_set_bus_state(mod, true);
if (ret) {
- dev_err(mod->dev, "unable to set bus-on\n");
+ netdev_err(mod->ndev, "unable to set bus-on\n");
close_candev(ndev);
return ret;
}
@@ -1518,7 +1515,7 @@ static int ican3_stop(struct net_device *ndev)
/* bring the bus offline, stop receiving packets */
ret = ican3_set_bus_state(mod, false);
if (ret) {
- dev_err(mod->dev, "unable to set bus-off\n");
+ netdev_err(mod->ndev, "unable to set bus-off\n");
return ret;
}
@@ -1545,7 +1542,7 @@ static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev)
/* check that we can actually transmit */
if (!ican3_txok(mod)) {
- dev_err(mod->dev, "BUG: no free descriptors\n");
+ netdev_err(mod->ndev, "BUG: no free descriptors\n");
spin_unlock_irqrestore(&mod->lock, flags);
return NETDEV_TX_BUSY;
}
@@ -1597,6 +1594,7 @@ static const struct net_device_ops ican3_netdev_ops = {
.ndo_open = ican3_open,
.ndo_stop = ican3_stop,
.ndo_start_xmit = ican3_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
/*
@@ -1657,7 +1655,7 @@ static int ican3_set_mode(struct net_device *ndev, enum can_mode mode)
/* bring the bus online */
ret = ican3_set_bus_state(mod, true);
if (ret) {
- dev_err(mod->dev, "unable to set bus-on\n");
+ netdev_err(ndev, "unable to set bus-on\n");
return ret;
}
@@ -1682,7 +1680,7 @@ static int ican3_get_berr_counter(const struct net_device *ndev,
ret = wait_for_completion_timeout(&mod->buserror_comp, HZ);
if (ret == 0) {
- dev_info(mod->dev, "%s timed out\n", __func__);
+ netdev_info(mod->ndev, "%s timed out\n", __func__);
return -ETIMEDOUT;
}
@@ -1708,7 +1706,7 @@ static ssize_t ican3_sysfs_show_term(struct device *dev,
ret = wait_for_completion_timeout(&mod->termination_comp, HZ);
if (ret == 0) {
- dev_info(mod->dev, "%s timed out\n", __func__);
+ netdev_info(mod->ndev, "%s timed out\n", __func__);
return -ETIMEDOUT;
}
@@ -1778,7 +1776,6 @@ static int ican3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ndev);
mod = netdev_priv(ndev);
mod->ndev = ndev;
- mod->dev = &pdev->dev;
mod->num = pdata->modno;
netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
skb_queue_head_init(&mod->echoq);
diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c
index cdb9808d12db..28c11f815245 100644
--- a/drivers/net/can/mcp251x.c
+++ b/drivers/net/can/mcp251x.c
@@ -601,10 +601,10 @@ static int mcp251x_do_set_bittiming(struct net_device *net)
(bt->prop_seg - 1));
mcp251x_write_bits(spi, CNF3, CNF3_PHSEG2_MASK,
(bt->phase_seg2 - 1));
- dev_info(&spi->dev, "CNF: 0x%02x 0x%02x 0x%02x\n",
- mcp251x_read_reg(spi, CNF1),
- mcp251x_read_reg(spi, CNF2),
- mcp251x_read_reg(spi, CNF3));
+ dev_dbg(&spi->dev, "CNF: 0x%02x 0x%02x 0x%02x\n",
+ mcp251x_read_reg(spi, CNF1),
+ mcp251x_read_reg(spi, CNF2),
+ mcp251x_read_reg(spi, CNF3));
return 0;
}
@@ -672,7 +672,7 @@ static int mcp251x_hw_probe(struct spi_device *spi)
static int mcp251x_power_enable(struct regulator *reg, int enable)
{
- if (IS_ERR(reg))
+ if (IS_ERR_OR_NULL(reg))
return 0;
if (enable)
@@ -996,6 +996,7 @@ static const struct net_device_ops mcp251x_netdev_ops = {
.ndo_open = mcp251x_open,
.ndo_stop = mcp251x_stop,
.ndo_start_xmit = mcp251x_hard_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static const struct of_device_id mcp251x_of_match[] = {
@@ -1155,8 +1156,6 @@ static int mcp251x_can_probe(struct spi_device *spi)
devm_can_led_init(net);
- dev_info(&spi->dev, "probed\n");
-
return ret;
error_probe:
@@ -1197,9 +1196,7 @@ static int mcp251x_can_remove(struct spi_device *spi)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-
-static int mcp251x_can_suspend(struct device *dev)
+static int __maybe_unused mcp251x_can_suspend(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
struct mcp251x_priv *priv = spi_get_drvdata(spi);
@@ -1221,7 +1218,7 @@ static int mcp251x_can_suspend(struct device *dev)
priv->after_suspend = AFTER_SUSPEND_DOWN;
}
- if (!IS_ERR(priv->power)) {
+ if (!IS_ERR_OR_NULL(priv->power)) {
regulator_disable(priv->power);
priv->after_suspend |= AFTER_SUSPEND_POWER;
}
@@ -1229,7 +1226,7 @@ static int mcp251x_can_suspend(struct device *dev)
return 0;
}
-static int mcp251x_can_resume(struct device *dev)
+static int __maybe_unused mcp251x_can_resume(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
struct mcp251x_priv *priv = spi_get_drvdata(spi);
@@ -1249,7 +1246,6 @@ static int mcp251x_can_resume(struct device *dev)
enable_irq(spi->irq);
return 0;
}
-#endif
static SIMPLE_DEV_PM_OPS(mcp251x_can_pm_ops, mcp251x_can_suspend,
mcp251x_can_resume);
diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c
index b9f3faabb0f3..e0c9be5e2ab7 100644
--- a/drivers/net/can/mscan/mscan.c
+++ b/drivers/net/can/mscan/mscan.c
@@ -647,9 +647,10 @@ static int mscan_close(struct net_device *dev)
}
static const struct net_device_ops mscan_netdev_ops = {
- .ndo_open = mscan_open,
- .ndo_stop = mscan_close,
- .ndo_start_xmit = mscan_start_xmit,
+ .ndo_open = mscan_open,
+ .ndo_stop = mscan_close,
+ .ndo_start_xmit = mscan_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
int register_mscandev(struct net_device *dev, int mscan_clksrc)
diff --git a/drivers/net/can/pch_can.c b/drivers/net/can/pch_can.c
index 6c077eb87b5e..6472562efedc 100644
--- a/drivers/net/can/pch_can.c
+++ b/drivers/net/can/pch_can.c
@@ -950,6 +950,7 @@ static const struct net_device_ops pch_can_netdev_ops = {
.ndo_open = pch_can_open,
.ndo_stop = pch_close,
.ndo_start_xmit = pch_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static void pch_can_remove(struct pci_dev *pdev)
diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig
index ff2ba86cd4a4..4b18b8765523 100644
--- a/drivers/net/can/sja1000/Kconfig
+++ b/drivers/net/can/sja1000/Kconfig
@@ -17,16 +17,9 @@ config CAN_SJA1000_PLATFORM
the "platform bus" (Linux abstraction for directly to the
processor attached devices). Which can be found on various
boards from Phytec (http://www.phytec.de) like the PCM027,
- PCM038.
-
-config CAN_SJA1000_OF_PLATFORM
- tristate "Generic OF Platform Bus based SJA1000 driver"
- depends on OF
- ---help---
- This driver adds support for the SJA1000 chips connected to
- the OpenFirmware "platform bus" found on embedded systems with
- OpenFirmware bindings, e.g. if you have a PowerPC based system
- you may want to enable this option.
+ PCM038. It also provides the OpenFirmware "platform bus" found
+ on embedded systems with OpenFirmware bindings, e.g. if you
+ have a PowerPC based system you may want to enable this option.
config CAN_EMS_PCMCIA
tristate "EMS CPC-CARD Card"
diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile
index b3d05cbfec36..531d5fcc97e5 100644
--- a/drivers/net/can/sja1000/Makefile
+++ b/drivers/net/can/sja1000/Makefile
@@ -5,7 +5,6 @@
obj-$(CONFIG_CAN_SJA1000) += sja1000.o
obj-$(CONFIG_CAN_SJA1000_ISA) += sja1000_isa.o
obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o
-obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o
obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o
obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o
obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o
diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c
index d790b874ca79..fd13dbf07d9c 100644
--- a/drivers/net/can/sja1000/ems_pci.c
+++ b/drivers/net/can/sja1000/ems_pci.c
@@ -323,6 +323,7 @@ static int ems_pci_add_card(struct pci_dev *pdev,
priv->cdr = EMS_PCI_CDR;
SET_NETDEV_DEV(dev, &pdev->dev);
+ dev->dev_id = i;
if (card->version == 1)
/* reset int flag of pita */
diff --git a/drivers/net/can/sja1000/ems_pcmcia.c b/drivers/net/can/sja1000/ems_pcmcia.c
index 9e535f2ef52b..381de998d2f1 100644
--- a/drivers/net/can/sja1000/ems_pcmcia.c
+++ b/drivers/net/can/sja1000/ems_pcmcia.c
@@ -211,6 +211,7 @@ static int ems_pcmcia_add_card(struct pcmcia_device *pdev, unsigned long base)
priv = netdev_priv(dev);
priv->priv = card;
SET_NETDEV_DEV(dev, &pdev->dev);
+ dev->dev_id = i;
priv->irq_flags = IRQF_SHARED;
dev->irq = pdev->irq;
diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c
index c96eb14699d5..23b8e1324e25 100644
--- a/drivers/net/can/sja1000/kvaser_pci.c
+++ b/drivers/net/can/sja1000/kvaser_pci.c
@@ -270,6 +270,7 @@ static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel,
priv->reg_base, board->conf_addr, dev->irq);
SET_NETDEV_DEV(dev, &pdev->dev);
+ dev->dev_id = channel;
/* Register SJA1000 device */
err = register_sja1000dev(dev);
diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c
index 065ca49eb45e..c540e3d12e3d 100644
--- a/drivers/net/can/sja1000/peak_pci.c
+++ b/drivers/net/can/sja1000/peak_pci.c
@@ -642,6 +642,7 @@ static int peak_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
icr |= chan->icr_mask;
SET_NETDEV_DEV(dev, &pdev->dev);
+ dev->dev_id = i;
/* Create chain of SJA1000 devices */
chan->prev_dev = pci_get_drvdata(pdev);
diff --git a/drivers/net/can/sja1000/peak_pcmcia.c b/drivers/net/can/sja1000/peak_pcmcia.c
index f7ad754dd2aa..dd56133cc461 100644
--- a/drivers/net/can/sja1000/peak_pcmcia.c
+++ b/drivers/net/can/sja1000/peak_pcmcia.c
@@ -550,6 +550,7 @@ static int pcan_add_channels(struct pcan_pccard *card)
priv = netdev_priv(netdev);
priv->priv = card;
SET_NETDEV_DEV(netdev, &pdev->dev);
+ netdev->dev_id = i;
priv->irq_flags = IRQF_SHARED;
netdev->irq = pdev->irq;
diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c
index fbb61a0d901f..ec39b7cb2287 100644
--- a/drivers/net/can/sja1000/plx_pci.c
+++ b/drivers/net/can/sja1000/plx_pci.c
@@ -587,6 +587,7 @@ static int plx_pci_add_card(struct pci_dev *pdev,
priv->cdr = ci->cdr;
SET_NETDEV_DEV(dev, &pdev->dev);
+ dev->dev_id = i;
/* Register SJA1000 device */
err = register_sja1000dev(dev);
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c
index f17c3018b7c7..f31499a32d7d 100644
--- a/drivers/net/can/sja1000/sja1000.c
+++ b/drivers/net/can/sja1000/sja1000.c
@@ -106,8 +106,7 @@ static int sja1000_probe_chip(struct net_device *dev)
struct sja1000_priv *priv = netdev_priv(dev);
if (priv->reg_base && sja1000_is_absent(priv)) {
- printk(KERN_INFO "%s: probing @0x%lX failed\n",
- DRV_NAME, dev->base_addr);
+ netdev_err(dev, "probing failed\n");
return 0;
}
return -1;
@@ -643,9 +642,10 @@ void free_sja1000dev(struct net_device *dev)
EXPORT_SYMBOL_GPL(free_sja1000dev);
static const struct net_device_ops sja1000_netdev_ops = {
- .ndo_open = sja1000_open,
- .ndo_stop = sja1000_close,
- .ndo_start_xmit = sja1000_start_xmit,
+ .ndo_open = sja1000_open,
+ .ndo_stop = sja1000_close,
+ .ndo_start_xmit = sja1000_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
int register_sja1000dev(struct net_device *dev)
diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c
deleted file mode 100644
index 2f6e24534231..000000000000
--- a/drivers/net/can/sja1000/sja1000_of_platform.c
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Driver for SJA1000 CAN controllers on the OpenFirmware platform bus
- *
- * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
- *
- * 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
- *
- * 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/>.
- */
-
-/* This is a generic driver for SJA1000 chips on the OpenFirmware platform
- * bus found on embedded PowerPC systems. You need a SJA1000 CAN node
- * definition in your flattened device tree source (DTS) file similar to:
- *
- * can@3,100 {
- * compatible = "nxp,sja1000";
- * reg = <3 0x100 0x80>;
- * interrupts = <2 0>;
- * interrupt-parent = <&mpic>;
- * nxp,external-clock-frequency = <16000000>;
- * };
- *
- * See "Documentation/devicetree/bindings/net/can/sja1000.txt" for further
- * information.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/netdevice.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/can/dev.h>
-
-#include <linux/of_platform.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-
-#include "sja1000.h"
-
-#define DRV_NAME "sja1000_of_platform"
-
-MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
-MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the OF platform bus");
-MODULE_LICENSE("GPL v2");
-
-#define SJA1000_OFP_CAN_CLOCK (16000000 / 2)
-
-#define SJA1000_OFP_OCR OCR_TX0_PULLDOWN
-#define SJA1000_OFP_CDR (CDR_CBP | CDR_CLK_OFF)
-
-static u8 sja1000_ofp_read_reg(const struct sja1000_priv *priv, int reg)
-{
- return ioread8(priv->reg_base + reg);
-}
-
-static void sja1000_ofp_write_reg(const struct sja1000_priv *priv,
- int reg, u8 val)
-{
- iowrite8(val, priv->reg_base + reg);
-}
-
-static int sja1000_ofp_remove(struct platform_device *ofdev)
-{
- struct net_device *dev = platform_get_drvdata(ofdev);
- struct sja1000_priv *priv = netdev_priv(dev);
- struct device_node *np = ofdev->dev.of_node;
- struct resource res;
-
- unregister_sja1000dev(dev);
- free_sja1000dev(dev);
- iounmap(priv->reg_base);
- irq_dispose_mapping(dev->irq);
-
- of_address_to_resource(np, 0, &res);
- release_mem_region(res.start, resource_size(&res));
-
- return 0;
-}
-
-static int sja1000_ofp_probe(struct platform_device *ofdev)
-{
- struct device_node *np = ofdev->dev.of_node;
- struct net_device *dev;
- struct sja1000_priv *priv;
- struct resource res;
- u32 prop;
- int err, irq, res_size;
- void __iomem *base;
-
- err = of_address_to_resource(np, 0, &res);
- if (err) {
- dev_err(&ofdev->dev, "invalid address\n");
- return err;
- }
-
- res_size = resource_size(&res);
-
- if (!request_mem_region(res.start, res_size, DRV_NAME)) {
- dev_err(&ofdev->dev, "couldn't request %pR\n", &res);
- return -EBUSY;
- }
-
- base = ioremap_nocache(res.start, res_size);
- if (!base) {
- dev_err(&ofdev->dev, "couldn't ioremap %pR\n", &res);
- err = -ENOMEM;
- goto exit_release_mem;
- }
-
- irq = irq_of_parse_and_map(np, 0);
- if (irq == 0) {
- dev_err(&ofdev->dev, "no irq found\n");
- err = -ENODEV;
- goto exit_unmap_mem;
- }
-
- dev = alloc_sja1000dev(0);
- if (!dev) {
- err = -ENOMEM;
- goto exit_dispose_irq;
- }
-
- priv = netdev_priv(dev);
-
- priv->read_reg = sja1000_ofp_read_reg;
- priv->write_reg = sja1000_ofp_write_reg;
-
- err = of_property_read_u32(np, "nxp,external-clock-frequency", &prop);
- if (!err)
- priv->can.clock.freq = prop / 2;
- else
- priv->can.clock.freq = SJA1000_OFP_CAN_CLOCK; /* default */
-
- err = of_property_read_u32(np, "nxp,tx-output-mode", &prop);
- if (!err)
- priv->ocr |= prop & OCR_MODE_MASK;
- else
- priv->ocr |= OCR_MODE_NORMAL; /* default */
-
- err = of_property_read_u32(np, "nxp,tx-output-config", &prop);
- if (!err)
- priv->ocr |= (prop << OCR_TX_SHIFT) & OCR_TX_MASK;
- else
- priv->ocr |= OCR_TX0_PULLDOWN; /* default */
-
- err = of_property_read_u32(np, "nxp,clock-out-frequency", &prop);
- if (!err && prop) {
- u32 divider = priv->can.clock.freq * 2 / prop;
-
- if (divider > 1)
- priv->cdr |= divider / 2 - 1;
- else
- priv->cdr |= CDR_CLKOUT_MASK;
- } else {
- priv->cdr |= CDR_CLK_OFF; /* default */
- }
-
- if (!of_property_read_bool(np, "nxp,no-comparator-bypass"))
- priv->cdr |= CDR_CBP; /* default */
-
- priv->irq_flags = IRQF_SHARED;
- priv->reg_base = base;
-
- dev->irq = irq;
-
- dev_info(&ofdev->dev,
- "reg_base=0x%p irq=%d clock=%d ocr=0x%02x cdr=0x%02x\n",
- priv->reg_base, dev->irq, priv->can.clock.freq,
- priv->ocr, priv->cdr);
-
- platform_set_drvdata(ofdev, dev);
- SET_NETDEV_DEV(dev, &ofdev->dev);
-
- err = register_sja1000dev(dev);
- if (err) {
- dev_err(&ofdev->dev, "registering %s failed (err=%d)\n",
- DRV_NAME, err);
- goto exit_free_sja1000;
- }
-
- return 0;
-
-exit_free_sja1000:
- free_sja1000dev(dev);
-exit_dispose_irq:
- irq_dispose_mapping(irq);
-exit_unmap_mem:
- iounmap(base);
-exit_release_mem:
- release_mem_region(res.start, res_size);
-
- return err;
-}
-
-static struct of_device_id sja1000_ofp_table[] = {
- {.compatible = "nxp,sja1000"},
- {},
-};
-MODULE_DEVICE_TABLE(of, sja1000_ofp_table);
-
-static struct platform_driver sja1000_ofp_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = DRV_NAME,
- .of_match_table = sja1000_ofp_table,
- },
- .probe = sja1000_ofp_probe,
- .remove = sja1000_ofp_remove,
-};
-
-module_platform_driver(sja1000_ofp_driver);
diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c
index 943df645b459..95a844a7ee7b 100644
--- a/drivers/net/can/sja1000/sja1000_platform.c
+++ b/drivers/net/can/sja1000/sja1000_platform.c
@@ -26,12 +26,16 @@
#include <linux/can/dev.h>
#include <linux/can/platform/sja1000.h>
#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
#include "sja1000.h"
#define DRV_NAME "sja1000_platform"
+#define SP_CAN_CLOCK (16000000 / 2)
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus");
MODULE_ALIAS("platform:" DRV_NAME);
MODULE_LICENSE("GPL v2");
@@ -66,59 +70,16 @@ static void sp_write_reg32(const struct sja1000_priv *priv, int reg, u8 val)
iowrite8(val, priv->reg_base + reg * 4);
}
-static int sp_probe(struct platform_device *pdev)
+static void sp_populate(struct sja1000_priv *priv,
+ struct sja1000_platform_data *pdata,
+ unsigned long resource_mem_flags)
{
- int err;
- void __iomem *addr;
- struct net_device *dev;
- struct sja1000_priv *priv;
- struct resource *res_mem, *res_irq;
- struct sja1000_platform_data *pdata;
-
- pdata = dev_get_platdata(&pdev->dev);
- if (!pdata) {
- dev_err(&pdev->dev, "No platform data provided!\n");
- err = -ENODEV;
- goto exit;
- }
-
- res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res_mem || !res_irq) {
- err = -ENODEV;
- goto exit;
- }
-
- if (!request_mem_region(res_mem->start, resource_size(res_mem),
- DRV_NAME)) {
- err = -EBUSY;
- goto exit;
- }
-
- addr = ioremap_nocache(res_mem->start, resource_size(res_mem));
- if (!addr) {
- err = -ENOMEM;
- goto exit_release;
- }
-
- dev = alloc_sja1000dev(0);
- if (!dev) {
- err = -ENOMEM;
- goto exit_iounmap;
- }
- priv = netdev_priv(dev);
-
- dev->irq = res_irq->start;
- priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
- if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE)
- priv->irq_flags |= IRQF_SHARED;
- priv->reg_base = addr;
/* The CAN clock frequency is half the oscillator clock frequency */
priv->can.clock.freq = pdata->osc_freq / 2;
priv->ocr = pdata->ocr;
priv->cdr = pdata->cdr;
- switch (res_mem->flags & IORESOURCE_MEM_TYPE_MASK) {
+ switch (resource_mem_flags & IORESOURCE_MEM_TYPE_MASK) {
case IORESOURCE_MEM_32BIT:
priv->read_reg = sp_read_reg32;
priv->write_reg = sp_write_reg32;
@@ -133,6 +94,124 @@ static int sp_probe(struct platform_device *pdev)
priv->write_reg = sp_write_reg8;
break;
}
+}
+
+static void sp_populate_of(struct sja1000_priv *priv, struct device_node *of)
+{
+ int err;
+ u32 prop;
+
+ err = of_property_read_u32(of, "reg-io-width", &prop);
+ if (err)
+ prop = 1; /* 8 bit is default */
+
+ switch (prop) {
+ case 4:
+ priv->read_reg = sp_read_reg32;
+ priv->write_reg = sp_write_reg32;
+ break;
+ case 2:
+ priv->read_reg = sp_read_reg16;
+ priv->write_reg = sp_write_reg16;
+ break;
+ case 1: /* fallthrough */
+ default:
+ priv->read_reg = sp_read_reg8;
+ priv->write_reg = sp_write_reg8;
+ }
+
+ err = of_property_read_u32(of, "nxp,external-clock-frequency", &prop);
+ if (!err)
+ priv->can.clock.freq = prop / 2;
+ else
+ priv->can.clock.freq = SP_CAN_CLOCK; /* default */
+
+ err = of_property_read_u32(of, "nxp,tx-output-mode", &prop);
+ if (!err)
+ priv->ocr |= prop & OCR_MODE_MASK;
+ else
+ priv->ocr |= OCR_MODE_NORMAL; /* default */
+
+ err = of_property_read_u32(of, "nxp,tx-output-config", &prop);
+ if (!err)
+ priv->ocr |= (prop << OCR_TX_SHIFT) & OCR_TX_MASK;
+ else
+ priv->ocr |= OCR_TX0_PULLDOWN; /* default */
+
+ err = of_property_read_u32(of, "nxp,clock-out-frequency", &prop);
+ if (!err && prop) {
+ u32 divider = priv->can.clock.freq * 2 / prop;
+
+ if (divider > 1)
+ priv->cdr |= divider / 2 - 1;
+ else
+ priv->cdr |= CDR_CLKOUT_MASK;
+ } else {
+ priv->cdr |= CDR_CLK_OFF; /* default */
+ }
+
+ if (!of_property_read_bool(of, "nxp,no-comparator-bypass"))
+ priv->cdr |= CDR_CBP; /* default */
+}
+
+static int sp_probe(struct platform_device *pdev)
+{
+ int err, irq = 0;
+ void __iomem *addr;
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct resource *res_mem, *res_irq = NULL;
+ struct sja1000_platform_data *pdata;
+ struct device_node *of = pdev->dev.of_node;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata && !of) {
+ dev_err(&pdev->dev, "No platform data provided!\n");
+ return -ENODEV;
+ }
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res_mem)
+ return -ENODEV;
+
+ if (!devm_request_mem_region(&pdev->dev, res_mem->start,
+ resource_size(res_mem), DRV_NAME))
+ return -EBUSY;
+
+ addr = devm_ioremap_nocache(&pdev->dev, res_mem->start,
+ resource_size(res_mem));
+ if (!addr)
+ return -ENOMEM;
+
+ if (of)
+ irq = irq_of_parse_and_map(of, 0);
+ else
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+
+ if (!irq && !res_irq)
+ return -ENODEV;
+
+ dev = alloc_sja1000dev(0);
+ if (!dev)
+ return -ENOMEM;
+ priv = netdev_priv(dev);
+
+ if (res_irq) {
+ irq = res_irq->start;
+ priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
+ if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE)
+ priv->irq_flags |= IRQF_SHARED;
+ } else {
+ priv->irq_flags = IRQF_SHARED;
+ }
+
+ dev->irq = irq;
+ priv->reg_base = addr;
+
+ if (of)
+ sp_populate_of(priv, of);
+ else
+ sp_populate(priv, pdata, res_mem->flags);
platform_set_drvdata(pdev, dev);
SET_NETDEV_DEV(dev, &pdev->dev);
@@ -150,39 +229,32 @@ static int sp_probe(struct platform_device *pdev)
exit_free:
free_sja1000dev(dev);
- exit_iounmap:
- iounmap(addr);
- exit_release:
- release_mem_region(res_mem->start, resource_size(res_mem));
- exit:
return err;
}
static int sp_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
- struct sja1000_priv *priv = netdev_priv(dev);
- struct resource *res;
unregister_sja1000dev(dev);
-
- if (priv->reg_base)
- iounmap(priv->reg_base);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-
free_sja1000dev(dev);
return 0;
}
+static struct of_device_id sp_of_table[] = {
+ {.compatible = "nxp,sja1000"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, sp_of_table);
+
static struct platform_driver sp_driver = {
.probe = sp_probe,
.remove = sp_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = sp_of_table,
},
};
diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c
index 3fcdae266377..f5b16e0e3a12 100644
--- a/drivers/net/can/slcan.c
+++ b/drivers/net/can/slcan.c
@@ -411,10 +411,16 @@ static void slc_free_netdev(struct net_device *dev)
slcan_devs[i] = NULL;
}
+static int slcan_change_mtu(struct net_device *dev, int new_mtu)
+{
+ return -EINVAL;
+}
+
static const struct net_device_ops slc_netdev_ops = {
.ndo_open = slc_open,
.ndo_stop = slc_close,
.ndo_start_xmit = slc_xmit,
+ .ndo_change_mtu = slcan_change_mtu,
};
static void slc_setup(struct net_device *dev)
diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c
index 9ea0dcde94ce..7d8c8f3672dd 100644
--- a/drivers/net/can/softing/softing_main.c
+++ b/drivers/net/can/softing/softing_main.c
@@ -628,6 +628,7 @@ static const struct net_device_ops softing_netdev_ops = {
.ndo_open = softing_netdev_open,
.ndo_stop = softing_netdev_stop,
.ndo_start_xmit = softing_netdev_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static const struct can_bittiming_const softing_btr_const = {
@@ -832,6 +833,7 @@ static int softing_pdev_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto netdev_failed;
}
+ netdev->dev_id = j;
priv = netdev_priv(card->net[j]);
priv->index = j;
ret = softing_netdev_register(netdev);
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index 2c62fe6c8fa9..258b9c4856ec 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -871,6 +871,7 @@ static const struct net_device_ops ti_hecc_netdev_ops = {
.ndo_open = ti_hecc_open,
.ndo_stop = ti_hecc_close,
.ndo_start_xmit = ti_hecc_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static int ti_hecc_probe(struct platform_device *pdev)
diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c
index 52c42fd49510..00f2534dde73 100644
--- a/drivers/net/can/usb/ems_usb.c
+++ b/drivers/net/can/usb/ems_usb.c
@@ -883,6 +883,7 @@ static const struct net_device_ops ems_usb_netdev_ops = {
.ndo_open = ems_usb_open,
.ndo_stop = ems_usb_close,
.ndo_start_xmit = ems_usb_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static const struct can_bittiming_const ems_usb_bittiming_const = {
diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c
index 7fbe85935f1d..b7c9e8b11460 100644
--- a/drivers/net/can/usb/esd_usb2.c
+++ b/drivers/net/can/usb/esd_usb2.c
@@ -888,6 +888,7 @@ static const struct net_device_ops esd_usb2_netdev_ops = {
.ndo_open = esd_usb2_open,
.ndo_stop = esd_usb2_close,
.ndo_start_xmit = esd_usb2_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static const struct can_bittiming_const esd_usb2_bittiming_const = {
@@ -1024,6 +1025,7 @@ static int esd_usb2_probe_one_net(struct usb_interface *intf, int index)
netdev->netdev_ops = &esd_usb2_netdev_ops;
SET_NETDEV_DEV(netdev, &intf->dev);
+ netdev->dev_id = index;
err = register_candev(netdev);
if (err) {
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
index e77d11049747..4ca46edc061d 100644
--- a/drivers/net/can/usb/kvaser_usb.c
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -1388,6 +1388,7 @@ static const struct net_device_ops kvaser_usb_netdev_ops = {
.ndo_open = kvaser_usb_open,
.ndo_stop = kvaser_usb_close,
.ndo_start_xmit = kvaser_usb_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static const struct can_bittiming_const kvaser_usb_bittiming_const = {
@@ -1529,6 +1530,7 @@ static int kvaser_usb_init_one(struct usb_interface *intf,
netdev->netdev_ops = &kvaser_usb_netdev_ops;
SET_NETDEV_DEV(netdev, &intf->dev);
+ netdev->dev_id = channel;
dev->nets[channel] = priv;
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index 0b7a4c3b01a2..644e6ab8a489 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -702,6 +702,7 @@ static const struct net_device_ops peak_usb_netdev_ops = {
.ndo_open = peak_usb_ndo_open,
.ndo_stop = peak_usb_ndo_stop,
.ndo_start_xmit = peak_usb_ndo_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
/*
@@ -769,6 +770,7 @@ static int peak_usb_create_dev(struct peak_usb_adapter *peak_usb_adapter,
usb_set_intfdata(intf, dev);
SET_NETDEV_DEV(netdev, &intf->dev);
+ netdev->dev_id = ctrl_idx;
err = register_candev(netdev);
if (err) {
diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c
index a0fa1fd5092b..ef674ecb82f8 100644
--- a/drivers/net/can/usb/usb_8dev.c
+++ b/drivers/net/can/usb/usb_8dev.c
@@ -697,8 +697,8 @@ static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
nofreecontext:
- usb_unanchor_urb(urb);
usb_free_coherent(priv->udev, size, buf, urb->transfer_dma);
+ usb_free_urb(urb);
netdev_warn(netdev, "couldn't find free context");
@@ -887,6 +887,7 @@ static const struct net_device_ops usb_8dev_netdev_ops = {
.ndo_open = usb_8dev_open,
.ndo_stop = usb_8dev_close,
.ndo_start_xmit = usb_8dev_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static const struct can_bittiming_const usb_8dev_bittiming_const = {
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index bd8f84b0b894..0932ffbf381b 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -63,10 +63,10 @@ static struct rtnl_link_stats64 *dummy_get_stats64(struct net_device *dev,
dstats = per_cpu_ptr(dev->dstats, i);
do {
- start = u64_stats_fetch_begin_bh(&dstats->syncp);
+ start = u64_stats_fetch_begin_irq(&dstats->syncp);
tbytes = dstats->tx_bytes;
tpackets = dstats->tx_packets;
- } while (u64_stats_fetch_retry_bh(&dstats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&dstats->syncp, start));
stats->tx_bytes += tbytes;
stats->tx_packets += tpackets;
}
@@ -88,16 +88,10 @@ static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev)
static int dummy_dev_init(struct net_device *dev)
{
- int i;
- dev->dstats = alloc_percpu(struct pcpu_dstats);
+ dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats);
if (!dev->dstats)
return -ENOMEM;
- for_each_possible_cpu(i) {
- struct pcpu_dstats *dstats;
- dstats = per_cpu_ptr(dev->dstats, i);
- u64_stats_init(&dstats->syncp);
- }
return 0;
}
diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c
index c53384d41c96..35df0b9e6848 100644
--- a/drivers/net/ethernet/3com/3c509.c
+++ b/drivers/net/ethernet/3com/3c509.c
@@ -749,7 +749,7 @@ el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
spin_unlock_irqrestore(&lp->lock, flags);
- dev_kfree_skb (skb);
+ dev_consume_skb_any (skb);
/* Clear the Tx status stack. */
{
diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c
index 5992860a39c9..063557e037f2 100644
--- a/drivers/net/ethernet/3com/3c589_cs.c
+++ b/drivers/net/ethernet/3com/3c589_cs.c
@@ -1,23 +1,24 @@
-/*======================================================================
-
- A PCMCIA ethernet driver for the 3com 3c589 card.
-
- Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
-
- 3c589_cs.c 1.162 2001/10/13 00:08:50
-
- The network driver code is based on Donald Becker's 3c589 code:
-
- Written 1994 by Donald Becker.
- Copyright 1993 United States Government as represented by the
- Director, National Security Agency. This software may be used and
- distributed according to the terms of the GNU General Public License,
- incorporated herein by reference.
- Donald Becker may be reached at becker@scyld.com
-
- Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk>
-
-======================================================================*/
+/* ======================================================================
+ *
+ * A PCMCIA ethernet driver for the 3com 3c589 card.
+ *
+ * Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
+ *
+ * 3c589_cs.c 1.162 2001/10/13 00:08:50
+ *
+ * The network driver code is based on Donald Becker's 3c589 code:
+ *
+ * Written 1994 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may be used and
+ * distributed according to the terms of the GNU General Public License,
+ * incorporated herein by reference.
+ * Donald Becker may be reached at becker@scyld.com
+ *
+ * Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * ======================================================================
+ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -41,18 +42,20 @@
#include <linux/ioport.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/ds.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
/* To minimize the size of the driver source I only define operating
- constants if they are used several times. You'll need the manual
- if you want to understand driver details. */
+ * constants if they are used several times. You'll need the manual
+ * if you want to understand driver details.
+ */
+
/* Offsets from base I/O address. */
#define EL3_DATA 0x00
#define EL3_TIMER 0x0a
@@ -65,7 +68,9 @@
#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
/* The top five bits written to EL3_CMD are a command, the lower
- 11 bits are the parameter, if applicable. */
+ * 11 bits are the parameter, if applicable.
+ */
+
enum c509cmd {
TotalReset = 0<<11,
SelectWindow = 1<<11,
@@ -190,138 +195,142 @@ static const struct net_device_ops el3_netdev_ops = {
static int tc589_probe(struct pcmcia_device *link)
{
- struct el3_private *lp;
- struct net_device *dev;
+ struct el3_private *lp;
+ struct net_device *dev;
- dev_dbg(&link->dev, "3c589_attach()\n");
+ dev_dbg(&link->dev, "3c589_attach()\n");
- /* Create new ethernet device */
- dev = alloc_etherdev(sizeof(struct el3_private));
- if (!dev)
- return -ENOMEM;
- lp = netdev_priv(dev);
- link->priv = dev;
- lp->p_dev = link;
+ /* Create new ethernet device */
+ dev = alloc_etherdev(sizeof(struct el3_private));
+ if (!dev)
+ return -ENOMEM;
+ lp = netdev_priv(dev);
+ link->priv = dev;
+ lp->p_dev = link;
- spin_lock_init(&lp->lock);
- link->resource[0]->end = 16;
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
+ spin_lock_init(&lp->lock);
+ link->resource[0]->end = 16;
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
- link->config_flags |= CONF_ENABLE_IRQ;
- link->config_index = 1;
+ link->config_flags |= CONF_ENABLE_IRQ;
+ link->config_index = 1;
- dev->netdev_ops = &el3_netdev_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
+ dev->netdev_ops = &el3_netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
- SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+ SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
- return tc589_config(link);
+ return tc589_config(link);
}
static void tc589_detach(struct pcmcia_device *link)
{
- struct net_device *dev = link->priv;
+ struct net_device *dev = link->priv;
- dev_dbg(&link->dev, "3c589_detach\n");
+ dev_dbg(&link->dev, "3c589_detach\n");
- unregister_netdev(dev);
+ unregister_netdev(dev);
- tc589_release(link);
+ tc589_release(link);
- free_netdev(dev);
+ free_netdev(dev);
} /* tc589_detach */
static int tc589_config(struct pcmcia_device *link)
{
- struct net_device *dev = link->priv;
- __be16 *phys_addr;
- int ret, i, j, multi = 0, fifo;
- unsigned int ioaddr;
- static const char * const ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
- u8 *buf;
- size_t len;
-
- dev_dbg(&link->dev, "3c589_config\n");
-
- phys_addr = (__be16 *)dev->dev_addr;
- /* Is this a 3c562? */
- if (link->manf_id != MANFID_3COM)
- dev_info(&link->dev, "hmmm, is this really a 3Com card??\n");
- multi = (link->card_id == PRODID_3COM_3C562);
-
- link->io_lines = 16;
-
- /* For the 3c562, the base address must be xx00-xx7f */
- for (i = j = 0; j < 0x400; j += 0x10) {
- if (multi && (j & 0x80)) continue;
- link->resource[0]->start = j ^ 0x300;
- i = pcmcia_request_io(link);
- if (i == 0)
- break;
- }
- if (i != 0)
- goto failed;
-
- ret = pcmcia_request_irq(link, el3_interrupt);
- if (ret)
- goto failed;
-
- ret = pcmcia_enable_device(link);
- if (ret)
- goto failed;
-
- dev->irq = link->irq;
- dev->base_addr = link->resource[0]->start;
- ioaddr = dev->base_addr;
- EL3WINDOW(0);
-
- /* The 3c589 has an extra EEPROM for configuration info, including
- the hardware address. The 3c562 puts the address in the CIS. */
- len = pcmcia_get_tuple(link, 0x88, &buf);
- if (buf && len >= 6) {
- for (i = 0; i < 3; i++)
- phys_addr[i] = htons(le16_to_cpu(buf[i*2]));
- kfree(buf);
- } else {
- kfree(buf); /* 0 < len < 6 */
- for (i = 0; i < 3; i++)
- phys_addr[i] = htons(read_eeprom(ioaddr, i));
- if (phys_addr[0] == htons(0x6060)) {
- dev_err(&link->dev, "IO port conflict at 0x%03lx-0x%03lx\n",
- dev->base_addr, dev->base_addr+15);
- goto failed;
+ struct net_device *dev = link->priv;
+ __be16 *phys_addr;
+ int ret, i, j, multi = 0, fifo;
+ unsigned int ioaddr;
+ static const char * const ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
+ u8 *buf;
+ size_t len;
+
+ dev_dbg(&link->dev, "3c589_config\n");
+
+ phys_addr = (__be16 *)dev->dev_addr;
+ /* Is this a 3c562? */
+ if (link->manf_id != MANFID_3COM)
+ dev_info(&link->dev, "hmmm, is this really a 3Com card??\n");
+ multi = (link->card_id == PRODID_3COM_3C562);
+
+ link->io_lines = 16;
+
+ /* For the 3c562, the base address must be xx00-xx7f */
+ for (i = j = 0; j < 0x400; j += 0x10) {
+ if (multi && (j & 0x80))
+ continue;
+ link->resource[0]->start = j ^ 0x300;
+ i = pcmcia_request_io(link);
+ if (i == 0)
+ break;
}
- }
-
- /* The address and resource configuration register aren't loaded from
- the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version. */
- outw(0x3f00, ioaddr + 8);
- fifo = inl(ioaddr);
-
- /* The if_port symbol can be set when the module is loaded */
- if ((if_port >= 0) && (if_port <= 3))
- dev->if_port = if_port;
- else
- dev_err(&link->dev, "invalid if_port requested\n");
-
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- dev_err(&link->dev, "register_netdev() failed\n");
- goto failed;
- }
-
- netdev_info(dev, "3Com 3c%s, io %#3lx, irq %d, hw_addr %pM\n",
- (multi ? "562" : "589"), dev->base_addr, dev->irq,
- dev->dev_addr);
- netdev_info(dev, " %dK FIFO split %s Rx:Tx, %s xcvr\n",
- (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
- if_names[dev->if_port]);
- return 0;
+ if (i != 0)
+ goto failed;
+
+ ret = pcmcia_request_irq(link, el3_interrupt);
+ if (ret)
+ goto failed;
+
+ ret = pcmcia_enable_device(link);
+ if (ret)
+ goto failed;
+
+ dev->irq = link->irq;
+ dev->base_addr = link->resource[0]->start;
+ ioaddr = dev->base_addr;
+ EL3WINDOW(0);
+
+ /* The 3c589 has an extra EEPROM for configuration info, including
+ * the hardware address. The 3c562 puts the address in the CIS.
+ */
+ len = pcmcia_get_tuple(link, 0x88, &buf);
+ if (buf && len >= 6) {
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(le16_to_cpu(buf[i*2]));
+ kfree(buf);
+ } else {
+ kfree(buf); /* 0 < len < 6 */
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i));
+ if (phys_addr[0] == htons(0x6060)) {
+ dev_err(&link->dev, "IO port conflict at 0x%03lx-0x%03lx\n",
+ dev->base_addr, dev->base_addr+15);
+ goto failed;
+ }
+ }
+
+ /* The address and resource configuration register aren't loaded from
+ * the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version.
+ */
+
+ outw(0x3f00, ioaddr + 8);
+ fifo = inl(ioaddr);
+
+ /* The if_port symbol can be set when the module is loaded */
+ if ((if_port >= 0) && (if_port <= 3))
+ dev->if_port = if_port;
+ else
+ dev_err(&link->dev, "invalid if_port requested\n");
+
+ SET_NETDEV_DEV(dev, &link->dev);
+
+ if (register_netdev(dev) != 0) {
+ dev_err(&link->dev, "register_netdev() failed\n");
+ goto failed;
+ }
+
+ netdev_info(dev, "3Com 3c%s, io %#3lx, irq %d, hw_addr %pM\n",
+ (multi ? "562" : "589"), dev->base_addr, dev->irq,
+ dev->dev_addr);
+ netdev_info(dev, " %dK FIFO split %s Rx:Tx, %s xcvr\n",
+ (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
+ if_names[dev->if_port]);
+ return 0;
failed:
- tc589_release(link);
- return -ENODEV;
+ tc589_release(link);
+ return -ENODEV;
} /* tc589_config */
static void tc589_release(struct pcmcia_device *link)
@@ -353,113 +362,120 @@ static int tc589_resume(struct pcmcia_device *link)
/*====================================================================*/
-/*
- Use this for commands that may take time to finish
-*/
+/* Use this for commands that may take time to finish */
+
static void tc589_wait_for_completion(struct net_device *dev, int cmd)
{
- int i = 100;
- outw(cmd, dev->base_addr + EL3_CMD);
- while (--i > 0)
- if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break;
- if (i == 0)
- netdev_warn(dev, "command 0x%04x did not complete!\n", cmd);
+ int i = 100;
+ outw(cmd, dev->base_addr + EL3_CMD);
+ while (--i > 0)
+ if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000))
+ break;
+ if (i == 0)
+ netdev_warn(dev, "command 0x%04x did not complete!\n", cmd);
}
-/*
- Read a word from the EEPROM using the regular EEPROM access register.
- Assume that we are in register window zero.
-*/
+/* Read a word from the EEPROM using the regular EEPROM access register.
+ * Assume that we are in register window zero.
+ */
+
static u16 read_eeprom(unsigned int ioaddr, int index)
{
- int i;
- outw(EEPROM_READ + index, ioaddr + 10);
- /* Reading the eeprom takes 162 us */
- for (i = 1620; i >= 0; i--)
- if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0)
- break;
- return inw(ioaddr + 12);
+ int i;
+ outw(EEPROM_READ + index, ioaddr + 10);
+ /* Reading the eeprom takes 162 us */
+ for (i = 1620; i >= 0; i--)
+ if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0)
+ break;
+ return inw(ioaddr + 12);
}
-/*
- Set transceiver type, perhaps to something other than what the user
- specified in dev->if_port.
-*/
+/* Set transceiver type, perhaps to something other than what the user
+ * specified in dev->if_port.
+ */
+
static void tc589_set_xcvr(struct net_device *dev, int if_port)
{
- struct el3_private *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
-
- EL3WINDOW(0);
- switch (if_port) {
- case 0: case 1: outw(0, ioaddr + 6); break;
- case 2: outw(3<<14, ioaddr + 6); break;
- case 3: outw(1<<14, ioaddr + 6); break;
- }
- /* On PCMCIA, this just turns on the LED */
- outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD);
- /* 10baseT interface, enable link beat and jabber check. */
- EL3WINDOW(4);
- outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA);
- EL3WINDOW(1);
- if (if_port == 2)
- lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000);
- else
- lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+
+ EL3WINDOW(0);
+ switch (if_port) {
+ case 0:
+ case 1:
+ outw(0, ioaddr + 6);
+ break;
+ case 2:
+ outw(3<<14, ioaddr + 6);
+ break;
+ case 3:
+ outw(1<<14, ioaddr + 6);
+ break;
+ }
+ /* On PCMCIA, this just turns on the LED */
+ outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD);
+ /* 10baseT interface, enable link beat and jabber check. */
+ EL3WINDOW(4);
+ outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA);
+ EL3WINDOW(1);
+ if (if_port == 2)
+ lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000);
+ else
+ lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);
}
static void dump_status(struct net_device *dev)
{
- unsigned int ioaddr = dev->base_addr;
- EL3WINDOW(1);
- netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x tx free %04x\n",
- inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS),
- inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE));
- EL3WINDOW(4);
- netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
- inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08),
- inw(ioaddr+0x0a));
- EL3WINDOW(1);
+ unsigned int ioaddr = dev->base_addr;
+ EL3WINDOW(1);
+ netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x tx free %04x\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS),
+ inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE));
+ EL3WINDOW(4);
+ netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
+ inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08),
+ inw(ioaddr+0x0a));
+ EL3WINDOW(1);
}
/* Reset and restore all of the 3c589 registers. */
static void tc589_reset(struct net_device *dev)
{
- unsigned int ioaddr = dev->base_addr;
- int i;
-
- EL3WINDOW(0);
- outw(0x0001, ioaddr + 4); /* Activate board. */
- outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */
-
- /* Set the station address in window 2. */
- EL3WINDOW(2);
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + i);
-
- tc589_set_xcvr(dev, dev->if_port);
-
- /* Switch to the stats window, and clear all stats by reading. */
- outw(StatsDisable, ioaddr + EL3_CMD);
- EL3WINDOW(6);
- for (i = 0; i < 9; i++)
- inb(ioaddr+i);
- inw(ioaddr + 10);
- inw(ioaddr + 12);
-
- /* Switch to register set 1 for normal use. */
- EL3WINDOW(1);
-
- set_rx_mode(dev);
- outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
- outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
- outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
- /* Allow status bits to be seen. */
- outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
- /* Ack all pending events, and set active indicator mask. */
- outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ unsigned int ioaddr = dev->base_addr;
+ int i;
+
+ EL3WINDOW(0);
+ outw(0x0001, ioaddr + 4); /* Activate board. */
+ outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */
+
+ /* Set the station address in window 2. */
+ EL3WINDOW(2);
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+
+ tc589_set_xcvr(dev, dev->if_port);
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ EL3WINDOW(6);
+ for (i = 0; i < 9; i++)
+ inb(ioaddr+i);
+ inw(ioaddr + 10);
+ inw(ioaddr + 12);
+
+ /* Switch to register set 1 for normal use. */
+ EL3WINDOW(1);
+
+ set_rx_mode(dev);
+ outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
ioaddr + EL3_CMD);
- outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
| AdapterFailure, ioaddr + EL3_CMD);
}
@@ -478,381 +494,406 @@ static const struct ethtool_ops netdev_ethtool_ops = {
static int el3_config(struct net_device *dev, struct ifmap *map)
{
- if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
- if (map->port <= 3) {
- dev->if_port = map->port;
- netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
- tc589_set_xcvr(dev, dev->if_port);
- } else
- return -EINVAL;
- }
- return 0;
+ if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+ if (map->port <= 3) {
+ dev->if_port = map->port;
+ netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
+ tc589_set_xcvr(dev, dev->if_port);
+ } else {
+ return -EINVAL;
+ }
+ }
+ return 0;
}
static int el3_open(struct net_device *dev)
{
- struct el3_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
+ struct el3_private *lp = netdev_priv(dev);
+ struct pcmcia_device *link = lp->p_dev;
- if (!pcmcia_dev_present(link))
- return -ENODEV;
+ if (!pcmcia_dev_present(link))
+ return -ENODEV;
- link->open++;
- netif_start_queue(dev);
+ link->open++;
+ netif_start_queue(dev);
- tc589_reset(dev);
- init_timer(&lp->media);
- lp->media.function = media_check;
- lp->media.data = (unsigned long) dev;
- lp->media.expires = jiffies + HZ;
- add_timer(&lp->media);
+ tc589_reset(dev);
+ init_timer(&lp->media);
+ lp->media.function = media_check;
+ lp->media.data = (unsigned long) dev;
+ lp->media.expires = jiffies + HZ;
+ add_timer(&lp->media);
- dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
+ dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
dev->name, inw(dev->base_addr + EL3_STATUS));
- return 0;
+ return 0;
}
static void el3_tx_timeout(struct net_device *dev)
{
- unsigned int ioaddr = dev->base_addr;
-
- netdev_warn(dev, "Transmit timed out!\n");
- dump_status(dev);
- dev->stats.tx_errors++;
- dev->trans_start = jiffies; /* prevent tx timeout */
- /* Issue TX_RESET and TX_START commands. */
- tc589_wait_for_completion(dev, TxReset);
- outw(TxEnable, ioaddr + EL3_CMD);
- netif_wake_queue(dev);
+ unsigned int ioaddr = dev->base_addr;
+
+ netdev_warn(dev, "Transmit timed out!\n");
+ dump_status(dev);
+ dev->stats.tx_errors++;
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ /* Issue TX_RESET and TX_START commands. */
+ tc589_wait_for_completion(dev, TxReset);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ netif_wake_queue(dev);
}
static void pop_tx_status(struct net_device *dev)
{
- unsigned int ioaddr = dev->base_addr;
- int i;
-
- /* Clear the Tx status stack. */
- for (i = 32; i > 0; i--) {
- u_char tx_status = inb(ioaddr + TX_STATUS);
- if (!(tx_status & 0x84)) break;
- /* reset transmitter on jabber error or underrun */
- if (tx_status & 0x30)
- tc589_wait_for_completion(dev, TxReset);
- if (tx_status & 0x38) {
- netdev_dbg(dev, "transmit error: status 0x%02x\n", tx_status);
- outw(TxEnable, ioaddr + EL3_CMD);
- dev->stats.tx_aborted_errors++;
+ unsigned int ioaddr = dev->base_addr;
+ int i;
+
+ /* Clear the Tx status stack. */
+ for (i = 32; i > 0; i--) {
+ u_char tx_status = inb(ioaddr + TX_STATUS);
+ if (!(tx_status & 0x84))
+ break;
+ /* reset transmitter on jabber error or underrun */
+ if (tx_status & 0x30)
+ tc589_wait_for_completion(dev, TxReset);
+ if (tx_status & 0x38) {
+ netdev_dbg(dev, "transmit error: status 0x%02x\n", tx_status);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->stats.tx_aborted_errors++;
+ }
+ outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
}
- outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
- }
}
static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
- unsigned int ioaddr = dev->base_addr;
- struct el3_private *priv = netdev_priv(dev);
- unsigned long flags;
+ unsigned int ioaddr = dev->base_addr;
+ struct el3_private *priv = netdev_priv(dev);
+ unsigned long flags;
- netdev_dbg(dev, "el3_start_xmit(length = %ld) called, status %4.4x.\n",
+ netdev_dbg(dev, "el3_start_xmit(length = %ld) called, status %4.4x.\n",
(long)skb->len, inw(ioaddr + EL3_STATUS));
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
- dev->stats.tx_bytes += skb->len;
+ dev->stats.tx_bytes += skb->len;
- /* Put out the doubleword header... */
- outw(skb->len, ioaddr + TX_FIFO);
- outw(0x00, ioaddr + TX_FIFO);
- /* ... and the packet rounded to a doubleword. */
- outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+ /* Put out the doubleword header... */
+ outw(skb->len, ioaddr + TX_FIFO);
+ outw(0x00, ioaddr + TX_FIFO);
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
- if (inw(ioaddr + TX_FREE) <= 1536) {
- netif_stop_queue(dev);
- /* Interrupt us when the FIFO has room for max-sized packet. */
- outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
- }
+ if (inw(ioaddr + TX_FREE) <= 1536) {
+ netif_stop_queue(dev);
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+ }
- pop_tx_status(dev);
- spin_unlock_irqrestore(&priv->lock, flags);
- dev_kfree_skb(skb);
+ pop_tx_status(dev);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_kfree_skb(skb);
- return NETDEV_TX_OK;
+ return NETDEV_TX_OK;
}
/* The EL3 interrupt handler. */
static irqreturn_t el3_interrupt(int irq, void *dev_id)
{
- struct net_device *dev = (struct net_device *) dev_id;
- struct el3_private *lp = netdev_priv(dev);
- unsigned int ioaddr;
- __u16 status;
- int i = 0, handled = 1;
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned int ioaddr;
+ __u16 status;
+ int i = 0, handled = 1;
- if (!netif_device_present(dev))
- return IRQ_NONE;
+ if (!netif_device_present(dev))
+ return IRQ_NONE;
- ioaddr = dev->base_addr;
+ ioaddr = dev->base_addr;
- netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS));
+ netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS));
- spin_lock(&lp->lock);
- while ((status = inw(ioaddr + EL3_STATUS)) &
+ spin_lock(&lp->lock);
+ while ((status = inw(ioaddr + EL3_STATUS)) &
(IntLatch | RxComplete | StatsFull)) {
- if ((status & 0xe000) != 0x2000) {
- netdev_dbg(dev, "interrupt from dead card\n");
- handled = 0;
- break;
- }
- if (status & RxComplete)
- el3_rx(dev);
- if (status & TxAvailable) {
- netdev_dbg(dev, " TX room bit was handled.\n");
- /* There's room in the FIFO for a full-sized packet. */
- outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
- netif_wake_queue(dev);
- }
- if (status & TxComplete)
- pop_tx_status(dev);
- if (status & (AdapterFailure | RxEarly | StatsFull)) {
- /* Handle all uncommon interrupts. */
- if (status & StatsFull) /* Empty statistics. */
- update_stats(dev);
- if (status & RxEarly) { /* Rx early is unused. */
- el3_rx(dev);
- outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
- }
- if (status & AdapterFailure) {
- u16 fifo_diag;
- EL3WINDOW(4);
- fifo_diag = inw(ioaddr + 4);
- EL3WINDOW(1);
- netdev_warn(dev, "adapter failure, FIFO diagnostic register %04x.\n",
+ if ((status & 0xe000) != 0x2000) {
+ netdev_dbg(dev, "interrupt from dead card\n");
+ handled = 0;
+ break;
+ }
+ if (status & RxComplete)
+ el3_rx(dev);
+ if (status & TxAvailable) {
+ netdev_dbg(dev, " TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+ netif_wake_queue(dev);
+ }
+ if (status & TxComplete)
+ pop_tx_status(dev);
+ if (status & (AdapterFailure | RxEarly | StatsFull)) {
+ /* Handle all uncommon interrupts. */
+ if (status & StatsFull) /* Empty statistics. */
+ update_stats(dev);
+ if (status & RxEarly) {
+ /* Rx early is unused. */
+ el3_rx(dev);
+ outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+ }
+ if (status & AdapterFailure) {
+ u16 fifo_diag;
+ EL3WINDOW(4);
+ fifo_diag = inw(ioaddr + 4);
+ EL3WINDOW(1);
+ netdev_warn(dev, "adapter failure, FIFO diagnostic register %04x.\n",
fifo_diag);
- if (fifo_diag & 0x0400) {
- /* Tx overrun */
- tc589_wait_for_completion(dev, TxReset);
- outw(TxEnable, ioaddr + EL3_CMD);
+ if (fifo_diag & 0x0400) {
+ /* Tx overrun */
+ tc589_wait_for_completion(dev, TxReset);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ }
+ if (fifo_diag & 0x2000) {
+ /* Rx underrun */
+ tc589_wait_for_completion(dev, RxReset);
+ set_rx_mode(dev);
+ outw(RxEnable, ioaddr + EL3_CMD);
+ }
+ outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+ }
}
- if (fifo_diag & 0x2000) {
- /* Rx underrun */
- tc589_wait_for_completion(dev, RxReset);
- set_rx_mode(dev);
- outw(RxEnable, ioaddr + EL3_CMD);
+ if (++i > 10) {
+ netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n",
+ status);
+ /* Clear all interrupts */
+ outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+ break;
}
- outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
- }
+ /* Acknowledge the IRQ. */
+ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
}
- if (++i > 10) {
- netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n",
- status);
- /* Clear all interrupts */
- outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
- break;
- }
- /* Acknowledge the IRQ. */
- outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
- }
- lp->last_irq = jiffies;
- spin_unlock(&lp->lock);
- netdev_dbg(dev, "exiting interrupt, status %4.4x.\n",
- inw(ioaddr + EL3_STATUS));
- return IRQ_RETVAL(handled);
+ lp->last_irq = jiffies;
+ spin_unlock(&lp->lock);
+ netdev_dbg(dev, "exiting interrupt, status %4.4x.\n",
+ inw(ioaddr + EL3_STATUS));
+ return IRQ_RETVAL(handled);
}
static void media_check(unsigned long arg)
{
- struct net_device *dev = (struct net_device *)(arg);
- struct el3_private *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u16 media, errs;
- unsigned long flags;
+ struct net_device *dev = (struct net_device *)(arg);
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ u16 media, errs;
+ unsigned long flags;
- if (!netif_device_present(dev)) goto reschedule;
+ if (!netif_device_present(dev))
+ goto reschedule;
- /* Check for pending interrupt with expired latency timer: with
- this, we can limp along even if the interrupt is blocked */
- if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
+ /* Check for pending interrupt with expired latency timer: with
+ * this, we can limp along even if the interrupt is blocked
+ */
+ if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
(inb(ioaddr + EL3_TIMER) == 0xff)) {
- if (!lp->fast_poll)
- netdev_warn(dev, "interrupt(s) dropped!\n");
-
- local_irq_save(flags);
- el3_interrupt(dev->irq, dev);
- local_irq_restore(flags);
-
- lp->fast_poll = HZ;
- }
- if (lp->fast_poll) {
- lp->fast_poll--;
- lp->media.expires = jiffies + HZ/100;
- add_timer(&lp->media);
- return;
- }
-
- /* lp->lock guards the EL3 window. Window should always be 1 except
- when the lock is held */
- spin_lock_irqsave(&lp->lock, flags);
- EL3WINDOW(4);
- media = inw(ioaddr+WN4_MEDIA) & 0xc810;
-
- /* Ignore collisions unless we've had no irq's recently */
- if (time_before(jiffies, lp->last_irq + HZ)) {
- media &= ~0x0010;
- } else {
- /* Try harder to detect carrier errors */
- EL3WINDOW(6);
- outw(StatsDisable, ioaddr + EL3_CMD);
- errs = inb(ioaddr + 0);
- outw(StatsEnable, ioaddr + EL3_CMD);
- dev->stats.tx_carrier_errors += errs;
- if (errs || (lp->media_status & 0x0010)) media |= 0x0010;
- }
+ if (!lp->fast_poll)
+ netdev_warn(dev, "interrupt(s) dropped!\n");
+
+ local_irq_save(flags);
+ el3_interrupt(dev->irq, dev);
+ local_irq_restore(flags);
+
+ lp->fast_poll = HZ;
+ }
+ if (lp->fast_poll) {
+ lp->fast_poll--;
+ lp->media.expires = jiffies + HZ/100;
+ add_timer(&lp->media);
+ return;
+ }
+
+ /* lp->lock guards the EL3 window. Window should always be 1 except
+ * when the lock is held
+ */
+
+ spin_lock_irqsave(&lp->lock, flags);
+ EL3WINDOW(4);
+ media = inw(ioaddr+WN4_MEDIA) & 0xc810;
+
+ /* Ignore collisions unless we've had no irq's recently */
+ if (time_before(jiffies, lp->last_irq + HZ)) {
+ media &= ~0x0010;
+ } else {
+ /* Try harder to detect carrier errors */
+ EL3WINDOW(6);
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ errs = inb(ioaddr + 0);
+ outw(StatsEnable, ioaddr + EL3_CMD);
+ dev->stats.tx_carrier_errors += errs;
+ if (errs || (lp->media_status & 0x0010))
+ media |= 0x0010;
+ }
- if (media != lp->media_status) {
- if ((media & lp->media_status & 0x8000) &&
- ((lp->media_status ^ media) & 0x0800))
+ if (media != lp->media_status) {
+ if ((media & lp->media_status & 0x8000) &&
+ ((lp->media_status ^ media) & 0x0800))
netdev_info(dev, "%s link beat\n",
- (lp->media_status & 0x0800 ? "lost" : "found"));
- else if ((media & lp->media_status & 0x4000) &&
+ (lp->media_status & 0x0800 ? "lost" : "found"));
+ else if ((media & lp->media_status & 0x4000) &&
((lp->media_status ^ media) & 0x0010))
netdev_info(dev, "coax cable %s\n",
- (lp->media_status & 0x0010 ? "ok" : "problem"));
- if (dev->if_port == 0) {
- if (media & 0x8000) {
- if (media & 0x0800)
- netdev_info(dev, "flipped to 10baseT\n");
- else
+ (lp->media_status & 0x0010 ? "ok" : "problem"));
+ if (dev->if_port == 0) {
+ if (media & 0x8000) {
+ if (media & 0x0800)
+ netdev_info(dev, "flipped to 10baseT\n");
+ else
tc589_set_xcvr(dev, 2);
- } else if (media & 0x4000) {
- if (media & 0x0010)
- tc589_set_xcvr(dev, 1);
- else
- netdev_info(dev, "flipped to 10base2\n");
- }
+ } else if (media & 0x4000) {
+ if (media & 0x0010)
+ tc589_set_xcvr(dev, 1);
+ else
+ netdev_info(dev, "flipped to 10base2\n");
+ }
+ }
+ lp->media_status = media;
}
- lp->media_status = media;
- }
- EL3WINDOW(1);
- spin_unlock_irqrestore(&lp->lock, flags);
+ EL3WINDOW(1);
+ spin_unlock_irqrestore(&lp->lock, flags);
reschedule:
- lp->media.expires = jiffies + HZ;
- add_timer(&lp->media);
+ lp->media.expires = jiffies + HZ;
+ add_timer(&lp->media);
}
static struct net_device_stats *el3_get_stats(struct net_device *dev)
{
- struct el3_private *lp = netdev_priv(dev);
- unsigned long flags;
- struct pcmcia_device *link = lp->p_dev;
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned long flags;
+ struct pcmcia_device *link = lp->p_dev;
- if (pcmcia_dev_present(link)) {
- spin_lock_irqsave(&lp->lock, flags);
- update_stats(dev);
- spin_unlock_irqrestore(&lp->lock, flags);
- }
- return &dev->stats;
+ if (pcmcia_dev_present(link)) {
+ spin_lock_irqsave(&lp->lock, flags);
+ update_stats(dev);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ return &dev->stats;
}
-/*
- Update statistics. We change to register window 6, so this should be run
- single-threaded if the device is active. This is expected to be a rare
- operation, and it's simpler for the rest of the driver to assume that
- window 1 is always valid rather than use a special window-state variable.
-
- Caller must hold the lock for this
+/* Update statistics. We change to register window 6, so this should be run
+* single-threaded if the device is active. This is expected to be a rare
+* operation, and it's simpler for the rest of the driver to assume that
+* window 1 is always valid rather than use a special window-state variable.
+*
+* Caller must hold the lock for this
*/
+
static void update_stats(struct net_device *dev)
{
- unsigned int ioaddr = dev->base_addr;
-
- netdev_dbg(dev, "updating the statistics.\n");
- /* Turn off statistics updates while reading. */
- outw(StatsDisable, ioaddr + EL3_CMD);
- /* Switch to the stats window, and read everything. */
- EL3WINDOW(6);
- dev->stats.tx_carrier_errors += inb(ioaddr + 0);
- dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
- /* Multiple collisions. */ inb(ioaddr + 2);
- dev->stats.collisions += inb(ioaddr + 3);
- dev->stats.tx_window_errors += inb(ioaddr + 4);
- dev->stats.rx_fifo_errors += inb(ioaddr + 5);
- dev->stats.tx_packets += inb(ioaddr + 6);
- /* Rx packets */ inb(ioaddr + 7);
- /* Tx deferrals */ inb(ioaddr + 8);
- /* Rx octets */ inw(ioaddr + 10);
- /* Tx octets */ inw(ioaddr + 12);
-
- /* Back to window 1, and turn statistics back on. */
- EL3WINDOW(1);
- outw(StatsEnable, ioaddr + EL3_CMD);
+ unsigned int ioaddr = dev->base_addr;
+
+ netdev_dbg(dev, "updating the statistics.\n");
+ /* Turn off statistics updates while reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ /* Switch to the stats window, and read everything. */
+ EL3WINDOW(6);
+ dev->stats.tx_carrier_errors += inb(ioaddr + 0);
+ dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */
+ inb(ioaddr + 2);
+ dev->stats.collisions += inb(ioaddr + 3);
+ dev->stats.tx_window_errors += inb(ioaddr + 4);
+ dev->stats.rx_fifo_errors += inb(ioaddr + 5);
+ dev->stats.tx_packets += inb(ioaddr + 6);
+ /* Rx packets */
+ inb(ioaddr + 7);
+ /* Tx deferrals */
+ inb(ioaddr + 8);
+ /* Rx octets */
+ inw(ioaddr + 10);
+ /* Tx octets */
+ inw(ioaddr + 12);
+
+ /* Back to window 1, and turn statistics back on. */
+ EL3WINDOW(1);
+ outw(StatsEnable, ioaddr + EL3_CMD);
}
static int el3_rx(struct net_device *dev)
{
- unsigned int ioaddr = dev->base_addr;
- int worklimit = 32;
- short rx_status;
+ unsigned int ioaddr = dev->base_addr;
+ int worklimit = 32;
+ short rx_status;
- netdev_dbg(dev, "in rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ netdev_dbg(dev, "in rx_packet(), status %4.4x, rx_status %4.4x.\n",
inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
- while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) &&
+ while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) &&
worklimit > 0) {
- worklimit--;
- if (rx_status & 0x4000) { /* Error, update stats. */
- short error = rx_status & 0x3800;
- dev->stats.rx_errors++;
- switch (error) {
- case 0x0000: dev->stats.rx_over_errors++; break;
- case 0x0800: dev->stats.rx_length_errors++; break;
- case 0x1000: dev->stats.rx_frame_errors++; break;
- case 0x1800: dev->stats.rx_length_errors++; break;
- case 0x2000: dev->stats.rx_frame_errors++; break;
- case 0x2800: dev->stats.rx_crc_errors++; break;
- }
- } else {
- short pkt_len = rx_status & 0x7ff;
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb(dev, pkt_len + 5);
-
- netdev_dbg(dev, " Receiving packet size %d status %4.4x.\n",
+ worklimit--;
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ short error = rx_status & 0x3800;
+ dev->stats.rx_errors++;
+ switch (error) {
+ case 0x0000:
+ dev->stats.rx_over_errors++;
+ break;
+ case 0x0800:
+ dev->stats.rx_length_errors++;
+ break;
+ case 0x1000:
+ dev->stats.rx_frame_errors++;
+ break;
+ case 0x1800:
+ dev->stats.rx_length_errors++;
+ break;
+ case 0x2000:
+ dev->stats.rx_frame_errors++;
+ break;
+ case 0x2800:
+ dev->stats.rx_crc_errors++;
+ break;
+ }
+ } else {
+ short pkt_len = rx_status & 0x7ff;
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, pkt_len + 5);
+
+ netdev_dbg(dev, " Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
- if (skb != NULL) {
- skb_reserve(skb, 2);
- insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
+ if (skb != NULL) {
+ skb_reserve(skb, 2);
+ insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
(pkt_len+3)>>2);
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- } else {
- netdev_dbg(dev, "couldn't allocate a sk_buff of size %d.\n",
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ } else {
+ netdev_dbg(dev, "couldn't allocate a sk_buff of size %d.\n",
pkt_len);
- dev->stats.rx_dropped++;
- }
+ dev->stats.rx_dropped++;
+ }
+ }
+ /* Pop the top of the Rx FIFO */
+ tc589_wait_for_completion(dev, RxDiscard);
}
- /* Pop the top of the Rx FIFO */
- tc589_wait_for_completion(dev, RxDiscard);
- }
- if (worklimit == 0)
- netdev_warn(dev, "too much work in el3_rx!\n");
- return 0;
+ if (worklimit == 0)
+ netdev_warn(dev, "too much work in el3_rx!\n");
+ return 0;
}
static void set_rx_mode(struct net_device *dev)
{
- unsigned int ioaddr = dev->base_addr;
- u16 opts = SetRxFilter | RxStation | RxBroadcast;
-
- if (dev->flags & IFF_PROMISC)
- opts |= RxMulticast | RxProm;
- else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI))
- opts |= RxMulticast;
- outw(opts, ioaddr + EL3_CMD);
+ unsigned int ioaddr = dev->base_addr;
+ u16 opts = SetRxFilter | RxStation | RxBroadcast;
+
+ if (dev->flags & IFF_PROMISC)
+ opts |= RxMulticast | RxProm;
+ else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI))
+ opts |= RxMulticast;
+ outw(opts, ioaddr + EL3_CMD);
}
static void set_multicast_list(struct net_device *dev)
@@ -867,44 +908,44 @@ static void set_multicast_list(struct net_device *dev)
static int el3_close(struct net_device *dev)
{
- struct el3_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
- unsigned int ioaddr = dev->base_addr;
-
- dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
+ struct el3_private *lp = netdev_priv(dev);
+ struct pcmcia_device *link = lp->p_dev;
+ unsigned int ioaddr = dev->base_addr;
+
+ dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
+
+ if (pcmcia_dev_present(link)) {
+ /* Turn off statistics ASAP. We update dev->stats below. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, ioaddr + EL3_CMD);
+ outw(TxDisable, ioaddr + EL3_CMD);
+
+ if (dev->if_port == 2)
+ /* Turn off thinnet power. Green! */
+ outw(StopCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == 1) {
+ /* Disable link beat and jabber */
+ EL3WINDOW(4);
+ outw(0, ioaddr + WN4_MEDIA);
+ }
- if (pcmcia_dev_present(link)) {
- /* Turn off statistics ASAP. We update dev->stats below. */
- outw(StatsDisable, ioaddr + EL3_CMD);
+ /* Switching back to window 0 disables the IRQ. */
+ EL3WINDOW(0);
+ /* But we explicitly zero the IRQ line select anyway. */
+ outw(0x0f00, ioaddr + WN0_IRQ);
- /* Disable the receiver and transmitter. */
- outw(RxDisable, ioaddr + EL3_CMD);
- outw(TxDisable, ioaddr + EL3_CMD);
-
- if (dev->if_port == 2)
- /* Turn off thinnet power. Green! */
- outw(StopCoax, ioaddr + EL3_CMD);
- else if (dev->if_port == 1) {
- /* Disable link beat and jabber */
- EL3WINDOW(4);
- outw(0, ioaddr + WN4_MEDIA);
+ /* Check if the card still exists */
+ if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000)
+ update_stats(dev);
}
- /* Switching back to window 0 disables the IRQ. */
- EL3WINDOW(0);
- /* But we explicitly zero the IRQ line select anyway. */
- outw(0x0f00, ioaddr + WN0_IRQ);
-
- /* Check if the card still exists */
- if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000)
- update_stats(dev);
- }
-
- link->open--;
- netif_stop_queue(dev);
- del_timer_sync(&lp->media);
+ link->open--;
+ netif_stop_queue(dev);
+ del_timer_sync(&lp->media);
- return 0;
+ return 0;
}
static const struct pcmcia_device_id tc589_ids[] = {
diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c
index 238ccea965c8..61477b8e8d24 100644
--- a/drivers/net/ethernet/3com/3c59x.c
+++ b/drivers/net/ethernet/3com/3c59x.c
@@ -2086,7 +2086,7 @@ vortex_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* ... and the packet rounded to a doubleword. */
skb_tx_timestamp(skb);
iowrite32_rep(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
- dev_kfree_skb (skb);
+ dev_consume_skb_any (skb);
if (ioread16(ioaddr + TxFree) > 1536) {
netif_start_queue (dev); /* AKPM: redundant? */
} else {
diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
index d2cd80444ade..599311f0e05c 100644
--- a/drivers/net/ethernet/8390/lib8390.c
+++ b/drivers/net/ethernet/8390/lib8390.c
@@ -404,7 +404,7 @@ static netdev_tx_t __ei_start_xmit(struct sk_buff *skb,
spin_unlock(&ei_local->page_lock);
enable_irq_lockdep_irqrestore(dev->irq, &flags);
skb_tx_timestamp(skb);
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
dev->stats.tx_bytes += send_length;
return NETDEV_TX_OK;
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 506b0248c400..39b26fe28d10 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -22,6 +22,7 @@ source "drivers/net/ethernet/adaptec/Kconfig"
source "drivers/net/ethernet/aeroflex/Kconfig"
source "drivers/net/ethernet/allwinner/Kconfig"
source "drivers/net/ethernet/alteon/Kconfig"
+source "drivers/net/ethernet/altera/Kconfig"
source "drivers/net/ethernet/amd/Kconfig"
source "drivers/net/ethernet/apple/Kconfig"
source "drivers/net/ethernet/arc/Kconfig"
@@ -149,6 +150,7 @@ config S6GMAC
To compile this driver as a module, choose M here. The module
will be called s6gmac.
+source "drivers/net/ethernet/samsung/Kconfig"
source "drivers/net/ethernet/seeq/Kconfig"
source "drivers/net/ethernet/silan/Kconfig"
source "drivers/net/ethernet/sis/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index c0b8789952e7..545d0b3b9cb4 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/
obj-$(CONFIG_GRETH) += aeroflex/
obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/
obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/
+obj-$(CONFIG_ALTERA_TSE) += altera/
obj-$(CONFIG_NET_VENDOR_AMD) += amd/
obj-$(CONFIG_NET_VENDOR_APPLE) += apple/
obj-$(CONFIG_NET_VENDOR_ARC) += arc/
@@ -60,6 +61,7 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/
obj-$(CONFIG_SH_ETH) += renesas/
obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
obj-$(CONFIG_S6GMAC) += s6gmac.o
+obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/
obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
obj-$(CONFIG_NET_VENDOR_SILAN) += silan/
obj-$(CONFIG_NET_VENDOR_SIS) += sis/
diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c
index c0f68dcd1dc1..7ae74d450e8f 100644
--- a/drivers/net/ethernet/adi/bfin_mac.c
+++ b/drivers/net/ethernet/adi/bfin_mac.c
@@ -307,11 +307,6 @@ static int bfin_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum,
return bfin_mdio_poll();
}
-static int bfin_mdiobus_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
static void bfin_mac_adjust_link(struct net_device *dev)
{
struct bfin_mac_local *lp = netdev_priv(dev);
@@ -1040,6 +1035,7 @@ static struct ptp_clock_info bfin_ptp_caps = {
.n_alarm = 0,
.n_ext_ts = 0,
.n_per_out = 0,
+ .n_pins = 0,
.pps = 0,
.adjfreq = bfin_ptp_adjfreq,
.adjtime = bfin_ptp_adjtime,
@@ -1086,7 +1082,7 @@ static inline void _tx_reclaim_skb(void)
tx_list_head->desc_a.config &= ~DMAEN;
tx_list_head->status.status_word = 0;
if (tx_list_head->skb) {
- dev_kfree_skb(tx_list_head->skb);
+ dev_consume_skb_any(tx_list_head->skb);
tx_list_head->skb = NULL;
}
tx_list_head = tx_list_head->next;
@@ -1823,7 +1819,6 @@ static int bfin_mii_bus_probe(struct platform_device *pdev)
goto out_err_alloc;
miibus->read = bfin_mdiobus_read;
miibus->write = bfin_mdiobus_write;
- miibus->reset = bfin_mdiobus_reset;
miibus->parent = &pdev->dev;
miibus->name = "bfin_mii_bus";
diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c
index c5d75e7aeeb6..23578dfee249 100644
--- a/drivers/net/ethernet/aeroflex/greth.c
+++ b/drivers/net/ethernet/aeroflex/greth.c
@@ -1213,11 +1213,6 @@ static int greth_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
return 0;
}
-static int greth_mdio_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
static void greth_link_change(struct net_device *dev)
{
struct greth_private *greth = netdev_priv(dev);
@@ -1332,7 +1327,6 @@ static int greth_mdio_init(struct greth_private *greth)
snprintf(greth->mdio->id, MII_BUS_ID_SIZE, "%s-%d", greth->mdio->name, greth->irq);
greth->mdio->read = greth_mdio_read;
greth->mdio->write = greth_mdio_write;
- greth->mdio->reset = greth_mdio_reset;
greth->mdio->priv = greth;
greth->mdio->irq = greth->mdio_irqs;
diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c
index 511f6eecd58b..fcaeeb8a4929 100644
--- a/drivers/net/ethernet/allwinner/sun4i-emac.c
+++ b/drivers/net/ethernet/allwinner/sun4i-emac.c
@@ -476,7 +476,7 @@ static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev)
spin_unlock_irqrestore(&db->lock, flags);
/* free this SKB */
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/altera/Kconfig b/drivers/net/ethernet/altera/Kconfig
new file mode 100644
index 000000000000..80c1ab74a4b8
--- /dev/null
+++ b/drivers/net/ethernet/altera/Kconfig
@@ -0,0 +1,8 @@
+config ALTERA_TSE
+ tristate "Altera Triple-Speed Ethernet MAC support"
+ select PHYLIB
+ ---help---
+ This driver supports the Altera Triple-Speed (TSE) Ethernet MAC.
+
+ To compile this driver as a module, choose M here. The module
+ will be called alteratse.
diff --git a/drivers/net/ethernet/altera/Makefile b/drivers/net/ethernet/altera/Makefile
new file mode 100644
index 000000000000..d4a187e45369
--- /dev/null
+++ b/drivers/net/ethernet/altera/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Altera device drivers.
+#
+
+obj-$(CONFIG_ALTERA_TSE) += altera_tse.o
+altera_tse-objs := altera_tse_main.o altera_tse_ethtool.o \
+altera_msgdma.o altera_sgdma.o altera_utils.o
diff --git a/drivers/net/ethernet/altera/altera_msgdma.c b/drivers/net/ethernet/altera/altera_msgdma.c
new file mode 100644
index 000000000000..3df18669ea30
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_msgdma.c
@@ -0,0 +1,202 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. 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 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/netdevice.h>
+#include "altera_utils.h"
+#include "altera_tse.h"
+#include "altera_msgdmahw.h"
+
+/* No initialization work to do for MSGDMA */
+int msgdma_initialize(struct altera_tse_private *priv)
+{
+ return 0;
+}
+
+void msgdma_uninitialize(struct altera_tse_private *priv)
+{
+}
+
+void msgdma_reset(struct altera_tse_private *priv)
+{
+ int counter;
+ struct msgdma_csr *txcsr =
+ (struct msgdma_csr *)priv->tx_dma_csr;
+ struct msgdma_csr *rxcsr =
+ (struct msgdma_csr *)priv->rx_dma_csr;
+
+ /* Reset Rx mSGDMA */
+ iowrite32(MSGDMA_CSR_STAT_MASK, &rxcsr->status);
+ iowrite32(MSGDMA_CSR_CTL_RESET, &rxcsr->control);
+
+ counter = 0;
+ while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
+ if (tse_bit_is_clear(&rxcsr->status,
+ MSGDMA_CSR_STAT_RESETTING))
+ break;
+ udelay(1);
+ }
+
+ if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR)
+ netif_warn(priv, drv, priv->dev,
+ "TSE Rx mSGDMA resetting bit never cleared!\n");
+
+ /* clear all status bits */
+ iowrite32(MSGDMA_CSR_STAT_MASK, &rxcsr->status);
+
+ /* Reset Tx mSGDMA */
+ iowrite32(MSGDMA_CSR_STAT_MASK, &txcsr->status);
+ iowrite32(MSGDMA_CSR_CTL_RESET, &txcsr->control);
+
+ counter = 0;
+ while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
+ if (tse_bit_is_clear(&txcsr->status,
+ MSGDMA_CSR_STAT_RESETTING))
+ break;
+ udelay(1);
+ }
+
+ if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR)
+ netif_warn(priv, drv, priv->dev,
+ "TSE Tx mSGDMA resetting bit never cleared!\n");
+
+ /* clear all status bits */
+ iowrite32(MSGDMA_CSR_STAT_MASK, &txcsr->status);
+}
+
+void msgdma_disable_rxirq(struct altera_tse_private *priv)
+{
+ struct msgdma_csr *csr = priv->rx_dma_csr;
+ tse_clear_bit(&csr->control, MSGDMA_CSR_CTL_GLOBAL_INTR);
+}
+
+void msgdma_enable_rxirq(struct altera_tse_private *priv)
+{
+ struct msgdma_csr *csr = priv->rx_dma_csr;
+ tse_set_bit(&csr->control, MSGDMA_CSR_CTL_GLOBAL_INTR);
+}
+
+void msgdma_disable_txirq(struct altera_tse_private *priv)
+{
+ struct msgdma_csr *csr = priv->tx_dma_csr;
+ tse_clear_bit(&csr->control, MSGDMA_CSR_CTL_GLOBAL_INTR);
+}
+
+void msgdma_enable_txirq(struct altera_tse_private *priv)
+{
+ struct msgdma_csr *csr = priv->tx_dma_csr;
+ tse_set_bit(&csr->control, MSGDMA_CSR_CTL_GLOBAL_INTR);
+}
+
+void msgdma_clear_rxirq(struct altera_tse_private *priv)
+{
+ struct msgdma_csr *csr = priv->rx_dma_csr;
+ iowrite32(MSGDMA_CSR_STAT_IRQ, &csr->status);
+}
+
+void msgdma_clear_txirq(struct altera_tse_private *priv)
+{
+ struct msgdma_csr *csr = priv->tx_dma_csr;
+ iowrite32(MSGDMA_CSR_STAT_IRQ, &csr->status);
+}
+
+/* return 0 to indicate transmit is pending */
+int msgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer)
+{
+ struct msgdma_extended_desc *desc = priv->tx_dma_desc;
+
+ iowrite32(lower_32_bits(buffer->dma_addr), &desc->read_addr_lo);
+ iowrite32(upper_32_bits(buffer->dma_addr), &desc->read_addr_hi);
+ iowrite32(0, &desc->write_addr_lo);
+ iowrite32(0, &desc->write_addr_hi);
+ iowrite32(buffer->len, &desc->len);
+ iowrite32(0, &desc->burst_seq_num);
+ iowrite32(MSGDMA_DESC_TX_STRIDE, &desc->stride);
+ iowrite32(MSGDMA_DESC_CTL_TX_SINGLE, &desc->control);
+ return 0;
+}
+
+u32 msgdma_tx_completions(struct altera_tse_private *priv)
+{
+ u32 ready = 0;
+ u32 inuse;
+ u32 status;
+ struct msgdma_csr *txcsr =
+ (struct msgdma_csr *)priv->tx_dma_csr;
+
+ /* Get number of sent descriptors */
+ inuse = ioread32(&txcsr->rw_fill_level) & 0xffff;
+
+ if (inuse) { /* Tx FIFO is not empty */
+ ready = priv->tx_prod - priv->tx_cons - inuse - 1;
+ } else {
+ /* Check for buffered last packet */
+ status = ioread32(&txcsr->status);
+ if (status & MSGDMA_CSR_STAT_BUSY)
+ ready = priv->tx_prod - priv->tx_cons - 1;
+ else
+ ready = priv->tx_prod - priv->tx_cons;
+ }
+ return ready;
+}
+
+/* Put buffer to the mSGDMA RX FIFO
+ */
+int msgdma_add_rx_desc(struct altera_tse_private *priv,
+ struct tse_buffer *rxbuffer)
+{
+ struct msgdma_extended_desc *desc = priv->rx_dma_desc;
+ u32 len = priv->rx_dma_buf_sz;
+ dma_addr_t dma_addr = rxbuffer->dma_addr;
+ u32 control = (MSGDMA_DESC_CTL_END_ON_EOP
+ | MSGDMA_DESC_CTL_END_ON_LEN
+ | MSGDMA_DESC_CTL_TR_COMP_IRQ
+ | MSGDMA_DESC_CTL_EARLY_IRQ
+ | MSGDMA_DESC_CTL_TR_ERR_IRQ
+ | MSGDMA_DESC_CTL_GO);
+
+ iowrite32(0, &desc->read_addr_lo);
+ iowrite32(0, &desc->read_addr_hi);
+ iowrite32(lower_32_bits(dma_addr), &desc->write_addr_lo);
+ iowrite32(upper_32_bits(dma_addr), &desc->write_addr_hi);
+ iowrite32(len, &desc->len);
+ iowrite32(0, &desc->burst_seq_num);
+ iowrite32(0x00010001, &desc->stride);
+ iowrite32(control, &desc->control);
+ return 1;
+}
+
+/* status is returned on upper 16 bits,
+ * length is returned in lower 16 bits
+ */
+u32 msgdma_rx_status(struct altera_tse_private *priv)
+{
+ u32 rxstatus = 0;
+ u32 pktlength;
+ u32 pktstatus;
+ struct msgdma_csr *rxcsr =
+ (struct msgdma_csr *)priv->rx_dma_csr;
+ struct msgdma_response *rxresp =
+ (struct msgdma_response *)priv->rx_dma_resp;
+
+ if (ioread32(&rxcsr->resp_fill_level) & 0xffff) {
+ pktlength = ioread32(&rxresp->bytes_transferred);
+ pktstatus = ioread32(&rxresp->status);
+ rxstatus = pktstatus;
+ rxstatus = rxstatus << 16;
+ rxstatus |= (pktlength & 0xffff);
+ }
+ return rxstatus;
+}
diff --git a/drivers/net/ethernet/altera/altera_msgdma.h b/drivers/net/ethernet/altera/altera_msgdma.h
new file mode 100644
index 000000000000..7f0f5bf2bba2
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_msgdma.h
@@ -0,0 +1,34 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. 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 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 __ALTERA_MSGDMA_H__
+#define __ALTERA_MSGDMA_H__
+
+void msgdma_reset(struct altera_tse_private *);
+void msgdma_enable_txirq(struct altera_tse_private *);
+void msgdma_enable_rxirq(struct altera_tse_private *);
+void msgdma_disable_rxirq(struct altera_tse_private *);
+void msgdma_disable_txirq(struct altera_tse_private *);
+void msgdma_clear_rxirq(struct altera_tse_private *);
+void msgdma_clear_txirq(struct altera_tse_private *);
+u32 msgdma_tx_completions(struct altera_tse_private *);
+int msgdma_add_rx_desc(struct altera_tse_private *, struct tse_buffer *);
+int msgdma_tx_buffer(struct altera_tse_private *, struct tse_buffer *);
+u32 msgdma_rx_status(struct altera_tse_private *);
+int msgdma_initialize(struct altera_tse_private *);
+void msgdma_uninitialize(struct altera_tse_private *);
+
+#endif /* __ALTERA_MSGDMA_H__ */
diff --git a/drivers/net/ethernet/altera/altera_msgdmahw.h b/drivers/net/ethernet/altera/altera_msgdmahw.h
new file mode 100644
index 000000000000..d7b59ba4019c
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_msgdmahw.h
@@ -0,0 +1,167 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. 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 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 __ALTERA_MSGDMAHW_H__
+#define __ALTERA_MSGDMAHW_H__
+
+/* mSGDMA standard descriptor format
+ */
+struct msgdma_desc {
+ u32 read_addr; /* data buffer source address */
+ u32 write_addr; /* data buffer destination address */
+ u32 len; /* the number of bytes to transfer per descriptor */
+ u32 control; /* characteristics of the transfer */
+};
+
+/* mSGDMA extended descriptor format
+ */
+struct msgdma_extended_desc {
+ u32 read_addr_lo; /* data buffer source address low bits */
+ u32 write_addr_lo; /* data buffer destination address low bits */
+ u32 len; /* the number of bytes to transfer
+ * per descriptor
+ */
+ u32 burst_seq_num; /* bit 31:24 write burst
+ * bit 23:16 read burst
+ * bit 15:0 sequence number
+ */
+ u32 stride; /* bit 31:16 write stride
+ * bit 15:0 read stride
+ */
+ u32 read_addr_hi; /* data buffer source address high bits */
+ u32 write_addr_hi; /* data buffer destination address high bits */
+ u32 control; /* characteristics of the transfer */
+};
+
+/* mSGDMA descriptor control field bit definitions
+ */
+#define MSGDMA_DESC_CTL_SET_CH(x) ((x) & 0xff)
+#define MSGDMA_DESC_CTL_GEN_SOP BIT(8)
+#define MSGDMA_DESC_CTL_GEN_EOP BIT(9)
+#define MSGDMA_DESC_CTL_PARK_READS BIT(10)
+#define MSGDMA_DESC_CTL_PARK_WRITES BIT(11)
+#define MSGDMA_DESC_CTL_END_ON_EOP BIT(12)
+#define MSGDMA_DESC_CTL_END_ON_LEN BIT(13)
+#define MSGDMA_DESC_CTL_TR_COMP_IRQ BIT(14)
+#define MSGDMA_DESC_CTL_EARLY_IRQ BIT(15)
+#define MSGDMA_DESC_CTL_TR_ERR_IRQ (0xff << 16)
+#define MSGDMA_DESC_CTL_EARLY_DONE BIT(24)
+/* Writing ‘1’ to the ‘go’ bit commits the entire descriptor into the
+ * descriptor FIFO(s)
+ */
+#define MSGDMA_DESC_CTL_GO BIT(31)
+
+/* Tx buffer control flags
+ */
+#define MSGDMA_DESC_CTL_TX_FIRST (MSGDMA_DESC_CTL_GEN_SOP | \
+ MSGDMA_DESC_CTL_TR_ERR_IRQ | \
+ MSGDMA_DESC_CTL_GO)
+
+#define MSGDMA_DESC_CTL_TX_MIDDLE (MSGDMA_DESC_CTL_TR_ERR_IRQ | \
+ MSGDMA_DESC_CTL_GO)
+
+#define MSGDMA_DESC_CTL_TX_LAST (MSGDMA_DESC_CTL_GEN_EOP | \
+ MSGDMA_DESC_CTL_TR_COMP_IRQ | \
+ MSGDMA_DESC_CTL_TR_ERR_IRQ | \
+ MSGDMA_DESC_CTL_GO)
+
+#define MSGDMA_DESC_CTL_TX_SINGLE (MSGDMA_DESC_CTL_GEN_SOP | \
+ MSGDMA_DESC_CTL_GEN_EOP | \
+ MSGDMA_DESC_CTL_TR_COMP_IRQ | \
+ MSGDMA_DESC_CTL_TR_ERR_IRQ | \
+ MSGDMA_DESC_CTL_GO)
+
+#define MSGDMA_DESC_CTL_RX_SINGLE (MSGDMA_DESC_CTL_END_ON_EOP | \
+ MSGDMA_DESC_CTL_END_ON_LEN | \
+ MSGDMA_DESC_CTL_TR_COMP_IRQ | \
+ MSGDMA_DESC_CTL_EARLY_IRQ | \
+ MSGDMA_DESC_CTL_TR_ERR_IRQ | \
+ MSGDMA_DESC_CTL_GO)
+
+/* mSGDMA extended descriptor stride definitions
+ */
+#define MSGDMA_DESC_TX_STRIDE (0x00010001)
+#define MSGDMA_DESC_RX_STRIDE (0x00010001)
+
+/* mSGDMA dispatcher control and status register map
+ */
+struct msgdma_csr {
+ u32 status; /* Read/Clear */
+ u32 control; /* Read/Write */
+ u32 rw_fill_level; /* bit 31:16 - write fill level
+ * bit 15:0 - read fill level
+ */
+ u32 resp_fill_level; /* bit 15:0 */
+ u32 rw_seq_num; /* bit 31:16 - write sequence number
+ * bit 15:0 - read sequence number
+ */
+ u32 pad[3]; /* reserved */
+};
+
+/* mSGDMA CSR status register bit definitions
+ */
+#define MSGDMA_CSR_STAT_BUSY BIT(0)
+#define MSGDMA_CSR_STAT_DESC_BUF_EMPTY BIT(1)
+#define MSGDMA_CSR_STAT_DESC_BUF_FULL BIT(2)
+#define MSGDMA_CSR_STAT_RESP_BUF_EMPTY BIT(3)
+#define MSGDMA_CSR_STAT_RESP_BUF_FULL BIT(4)
+#define MSGDMA_CSR_STAT_STOPPED BIT(5)
+#define MSGDMA_CSR_STAT_RESETTING BIT(6)
+#define MSGDMA_CSR_STAT_STOPPED_ON_ERR BIT(7)
+#define MSGDMA_CSR_STAT_STOPPED_ON_EARLY BIT(8)
+#define MSGDMA_CSR_STAT_IRQ BIT(9)
+#define MSGDMA_CSR_STAT_MASK 0x3FF
+#define MSGDMA_CSR_STAT_MASK_WITHOUT_IRQ 0x1FF
+
+#define MSGDMA_CSR_STAT_BUSY_GET(v) GET_BIT_VALUE(v, 0)
+#define MSGDMA_CSR_STAT_DESC_BUF_EMPTY_GET(v) GET_BIT_VALUE(v, 1)
+#define MSGDMA_CSR_STAT_DESC_BUF_FULL_GET(v) GET_BIT_VALUE(v, 2)
+#define MSGDMA_CSR_STAT_RESP_BUF_EMPTY_GET(v) GET_BIT_VALUE(v, 3)
+#define MSGDMA_CSR_STAT_RESP_BUF_FULL_GET(v) GET_BIT_VALUE(v, 4)
+#define MSGDMA_CSR_STAT_STOPPED_GET(v) GET_BIT_VALUE(v, 5)
+#define MSGDMA_CSR_STAT_RESETTING_GET(v) GET_BIT_VALUE(v, 6)
+#define MSGDMA_CSR_STAT_STOPPED_ON_ERR_GET(v) GET_BIT_VALUE(v, 7)
+#define MSGDMA_CSR_STAT_STOPPED_ON_EARLY_GET(v) GET_BIT_VALUE(v, 8)
+#define MSGDMA_CSR_STAT_IRQ_GET(v) GET_BIT_VALUE(v, 9)
+
+/* mSGDMA CSR control register bit definitions
+ */
+#define MSGDMA_CSR_CTL_STOP BIT(0)
+#define MSGDMA_CSR_CTL_RESET BIT(1)
+#define MSGDMA_CSR_CTL_STOP_ON_ERR BIT(2)
+#define MSGDMA_CSR_CTL_STOP_ON_EARLY BIT(3)
+#define MSGDMA_CSR_CTL_GLOBAL_INTR BIT(4)
+#define MSGDMA_CSR_CTL_STOP_DESCS BIT(5)
+
+/* mSGDMA CSR fill level bits
+ */
+#define MSGDMA_CSR_WR_FILL_LEVEL_GET(v) (((v) & 0xffff0000) >> 16)
+#define MSGDMA_CSR_RD_FILL_LEVEL_GET(v) ((v) & 0x0000ffff)
+#define MSGDMA_CSR_RESP_FILL_LEVEL_GET(v) ((v) & 0x0000ffff)
+
+/* mSGDMA response register map
+ */
+struct msgdma_response {
+ u32 bytes_transferred;
+ u32 status;
+};
+
+/* mSGDMA response register bit definitions
+ */
+#define MSGDMA_RESP_EARLY_TERM BIT(8)
+#define MSGDMA_RESP_ERR_MASK 0xFF
+
+#endif /* __ALTERA_MSGDMA_H__*/
diff --git a/drivers/net/ethernet/altera/altera_sgdma.c b/drivers/net/ethernet/altera/altera_sgdma.c
new file mode 100644
index 000000000000..0ee96639ae44
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_sgdma.c
@@ -0,0 +1,509 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. 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 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/list.h>
+#include "altera_utils.h"
+#include "altera_tse.h"
+#include "altera_sgdmahw.h"
+#include "altera_sgdma.h"
+
+static void sgdma_descrip(struct sgdma_descrip *desc,
+ struct sgdma_descrip *ndesc,
+ dma_addr_t ndesc_phys,
+ dma_addr_t raddr,
+ dma_addr_t waddr,
+ u16 length,
+ int generate_eop,
+ int rfixed,
+ int wfixed);
+
+static int sgdma_async_write(struct altera_tse_private *priv,
+ struct sgdma_descrip *desc);
+
+static int sgdma_async_read(struct altera_tse_private *priv);
+
+static dma_addr_t
+sgdma_txphysaddr(struct altera_tse_private *priv,
+ struct sgdma_descrip *desc);
+
+static dma_addr_t
+sgdma_rxphysaddr(struct altera_tse_private *priv,
+ struct sgdma_descrip *desc);
+
+static int sgdma_txbusy(struct altera_tse_private *priv);
+
+static int sgdma_rxbusy(struct altera_tse_private *priv);
+
+static void
+queue_tx(struct altera_tse_private *priv, struct tse_buffer *buffer);
+
+static void
+queue_rx(struct altera_tse_private *priv, struct tse_buffer *buffer);
+
+static struct tse_buffer *
+dequeue_tx(struct altera_tse_private *priv);
+
+static struct tse_buffer *
+dequeue_rx(struct altera_tse_private *priv);
+
+static struct tse_buffer *
+queue_rx_peekhead(struct altera_tse_private *priv);
+
+int sgdma_initialize(struct altera_tse_private *priv)
+{
+ priv->txctrlreg = SGDMA_CTRLREG_ILASTD;
+
+ priv->rxctrlreg = SGDMA_CTRLREG_IDESCRIP |
+ SGDMA_CTRLREG_ILASTD;
+
+ INIT_LIST_HEAD(&priv->txlisthd);
+ INIT_LIST_HEAD(&priv->rxlisthd);
+
+ priv->rxdescphys = (dma_addr_t) 0;
+ priv->txdescphys = (dma_addr_t) 0;
+
+ priv->rxdescphys = dma_map_single(priv->device, priv->rx_dma_desc,
+ priv->rxdescmem, DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(priv->device, priv->rxdescphys)) {
+ sgdma_uninitialize(priv);
+ netdev_err(priv->dev, "error mapping rx descriptor memory\n");
+ return -EINVAL;
+ }
+
+ priv->txdescphys = dma_map_single(priv->device, priv->tx_dma_desc,
+ priv->txdescmem, DMA_TO_DEVICE);
+
+ if (dma_mapping_error(priv->device, priv->txdescphys)) {
+ sgdma_uninitialize(priv);
+ netdev_err(priv->dev, "error mapping tx descriptor memory\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void sgdma_uninitialize(struct altera_tse_private *priv)
+{
+ if (priv->rxdescphys)
+ dma_unmap_single(priv->device, priv->rxdescphys,
+ priv->rxdescmem, DMA_BIDIRECTIONAL);
+
+ if (priv->txdescphys)
+ dma_unmap_single(priv->device, priv->txdescphys,
+ priv->txdescmem, DMA_TO_DEVICE);
+}
+
+/* This function resets the SGDMA controller and clears the
+ * descriptor memory used for transmits and receives.
+ */
+void sgdma_reset(struct altera_tse_private *priv)
+{
+ u32 *ptxdescripmem = (u32 *)priv->tx_dma_desc;
+ u32 txdescriplen = priv->txdescmem;
+ u32 *prxdescripmem = (u32 *)priv->rx_dma_desc;
+ u32 rxdescriplen = priv->rxdescmem;
+ struct sgdma_csr *ptxsgdma = (struct sgdma_csr *)priv->tx_dma_csr;
+ struct sgdma_csr *prxsgdma = (struct sgdma_csr *)priv->rx_dma_csr;
+
+ /* Initialize descriptor memory to 0 */
+ memset(ptxdescripmem, 0, txdescriplen);
+ memset(prxdescripmem, 0, rxdescriplen);
+
+ iowrite32(SGDMA_CTRLREG_RESET, &ptxsgdma->control);
+ iowrite32(0, &ptxsgdma->control);
+
+ iowrite32(SGDMA_CTRLREG_RESET, &prxsgdma->control);
+ iowrite32(0, &prxsgdma->control);
+}
+
+void sgdma_enable_rxirq(struct altera_tse_private *priv)
+{
+ struct sgdma_csr *csr = (struct sgdma_csr *)priv->rx_dma_csr;
+ priv->rxctrlreg |= SGDMA_CTRLREG_INTEN;
+ tse_set_bit(&csr->control, SGDMA_CTRLREG_INTEN);
+}
+
+void sgdma_enable_txirq(struct altera_tse_private *priv)
+{
+ struct sgdma_csr *csr = (struct sgdma_csr *)priv->tx_dma_csr;
+ priv->txctrlreg |= SGDMA_CTRLREG_INTEN;
+ tse_set_bit(&csr->control, SGDMA_CTRLREG_INTEN);
+}
+
+/* for SGDMA, RX interrupts remain enabled after enabling */
+void sgdma_disable_rxirq(struct altera_tse_private *priv)
+{
+}
+
+/* for SGDMA, TX interrupts remain enabled after enabling */
+void sgdma_disable_txirq(struct altera_tse_private *priv)
+{
+}
+
+void sgdma_clear_rxirq(struct altera_tse_private *priv)
+{
+ struct sgdma_csr *csr = (struct sgdma_csr *)priv->rx_dma_csr;
+ tse_set_bit(&csr->control, SGDMA_CTRLREG_CLRINT);
+}
+
+void sgdma_clear_txirq(struct altera_tse_private *priv)
+{
+ struct sgdma_csr *csr = (struct sgdma_csr *)priv->tx_dma_csr;
+ tse_set_bit(&csr->control, SGDMA_CTRLREG_CLRINT);
+}
+
+/* transmits buffer through SGDMA. Returns number of buffers
+ * transmitted, 0 if not possible.
+ *
+ * tx_lock is held by the caller
+ */
+int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer)
+{
+ int pktstx = 0;
+ struct sgdma_descrip *descbase =
+ (struct sgdma_descrip *)priv->tx_dma_desc;
+
+ struct sgdma_descrip *cdesc = &descbase[0];
+ struct sgdma_descrip *ndesc = &descbase[1];
+
+ /* wait 'til the tx sgdma is ready for the next transmit request */
+ if (sgdma_txbusy(priv))
+ return 0;
+
+ sgdma_descrip(cdesc, /* current descriptor */
+ ndesc, /* next descriptor */
+ sgdma_txphysaddr(priv, ndesc),
+ buffer->dma_addr, /* address of packet to xmit */
+ 0, /* write addr 0 for tx dma */
+ buffer->len, /* length of packet */
+ SGDMA_CONTROL_EOP, /* Generate EOP */
+ 0, /* read fixed */
+ SGDMA_CONTROL_WR_FIXED); /* Generate SOP */
+
+ pktstx = sgdma_async_write(priv, cdesc);
+
+ /* enqueue the request to the pending transmit queue */
+ queue_tx(priv, buffer);
+
+ return 1;
+}
+
+
+/* tx_lock held to protect access to queued tx list
+ */
+u32 sgdma_tx_completions(struct altera_tse_private *priv)
+{
+ u32 ready = 0;
+ struct sgdma_descrip *desc = (struct sgdma_descrip *)priv->tx_dma_desc;
+
+ if (!sgdma_txbusy(priv) &&
+ ((desc->control & SGDMA_CONTROL_HW_OWNED) == 0) &&
+ (dequeue_tx(priv))) {
+ ready = 1;
+ }
+
+ return ready;
+}
+
+int sgdma_add_rx_desc(struct altera_tse_private *priv,
+ struct tse_buffer *rxbuffer)
+{
+ queue_rx(priv, rxbuffer);
+ return sgdma_async_read(priv);
+}
+
+/* status is returned on upper 16 bits,
+ * length is returned in lower 16 bits
+ */
+u32 sgdma_rx_status(struct altera_tse_private *priv)
+{
+ struct sgdma_csr *csr = (struct sgdma_csr *)priv->rx_dma_csr;
+ struct sgdma_descrip *base = (struct sgdma_descrip *)priv->rx_dma_desc;
+ struct sgdma_descrip *desc = NULL;
+ int pktsrx;
+ unsigned int rxstatus = 0;
+ unsigned int pktlength = 0;
+ unsigned int pktstatus = 0;
+ struct tse_buffer *rxbuffer = NULL;
+
+ dma_sync_single_for_cpu(priv->device,
+ priv->rxdescphys,
+ priv->rxdescmem,
+ DMA_BIDIRECTIONAL);
+
+ desc = &base[0];
+ if ((ioread32(&csr->status) & SGDMA_STSREG_EOP) ||
+ (desc->status & SGDMA_STATUS_EOP)) {
+ pktlength = desc->bytes_xferred;
+ pktstatus = desc->status & 0x3f;
+ rxstatus = pktstatus;
+ rxstatus = rxstatus << 16;
+ rxstatus |= (pktlength & 0xffff);
+
+ desc->status = 0;
+
+ rxbuffer = dequeue_rx(priv);
+ if (rxbuffer == NULL)
+ netdev_err(priv->dev,
+ "sgdma rx and rx queue empty!\n");
+
+ /* kick the rx sgdma after reaping this descriptor */
+ pktsrx = sgdma_async_read(priv);
+ }
+
+ return rxstatus;
+}
+
+
+/* Private functions */
+static void sgdma_descrip(struct sgdma_descrip *desc,
+ struct sgdma_descrip *ndesc,
+ dma_addr_t ndesc_phys,
+ dma_addr_t raddr,
+ dma_addr_t waddr,
+ u16 length,
+ int generate_eop,
+ int rfixed,
+ int wfixed)
+{
+ /* Clear the next descriptor as not owned by hardware */
+ u32 ctrl = ndesc->control;
+ ctrl &= ~SGDMA_CONTROL_HW_OWNED;
+ ndesc->control = ctrl;
+
+ ctrl = 0;
+ ctrl = SGDMA_CONTROL_HW_OWNED;
+ ctrl |= generate_eop;
+ ctrl |= rfixed;
+ ctrl |= wfixed;
+
+ /* Channel is implicitly zero, initialized to 0 by default */
+
+ desc->raddr = raddr;
+ desc->waddr = waddr;
+ desc->next = lower_32_bits(ndesc_phys);
+ desc->control = ctrl;
+ desc->status = 0;
+ desc->rburst = 0;
+ desc->wburst = 0;
+ desc->bytes = length;
+ desc->bytes_xferred = 0;
+}
+
+/* If hardware is busy, don't restart async read.
+ * if status register is 0 - meaning initial state, restart async read,
+ * probably for the first time when populating a receive buffer.
+ * If read status indicate not busy and a status, restart the async
+ * DMA read.
+ */
+static int sgdma_async_read(struct altera_tse_private *priv)
+{
+ struct sgdma_csr *csr = (struct sgdma_csr *)priv->rx_dma_csr;
+ struct sgdma_descrip *descbase =
+ (struct sgdma_descrip *)priv->rx_dma_desc;
+
+ struct sgdma_descrip *cdesc = &descbase[0];
+ struct sgdma_descrip *ndesc = &descbase[1];
+
+ unsigned int sts = ioread32(&csr->status);
+ struct tse_buffer *rxbuffer = NULL;
+
+ if (!sgdma_rxbusy(priv)) {
+ rxbuffer = queue_rx_peekhead(priv);
+ if (rxbuffer == NULL)
+ return 0;
+
+ sgdma_descrip(cdesc, /* current descriptor */
+ ndesc, /* next descriptor */
+ sgdma_rxphysaddr(priv, ndesc),
+ 0, /* read addr 0 for rx dma */
+ rxbuffer->dma_addr, /* write addr for rx dma */
+ 0, /* read 'til EOP */
+ 0, /* EOP: NA for rx dma */
+ 0, /* read fixed: NA for rx dma */
+ 0); /* SOP: NA for rx DMA */
+
+ /* clear control and status */
+ iowrite32(0, &csr->control);
+
+ /* If status available, clear those bits */
+ if (sts & 0xf)
+ iowrite32(0xf, &csr->status);
+
+ dma_sync_single_for_device(priv->device,
+ priv->rxdescphys,
+ priv->rxdescmem,
+ DMA_BIDIRECTIONAL);
+
+ iowrite32(lower_32_bits(sgdma_rxphysaddr(priv, cdesc)),
+ &csr->next_descrip);
+
+ iowrite32((priv->rxctrlreg | SGDMA_CTRLREG_START),
+ &csr->control);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int sgdma_async_write(struct altera_tse_private *priv,
+ struct sgdma_descrip *desc)
+{
+ struct sgdma_csr *csr = (struct sgdma_csr *)priv->tx_dma_csr;
+
+ if (sgdma_txbusy(priv))
+ return 0;
+
+ /* clear control and status */
+ iowrite32(0, &csr->control);
+ iowrite32(0x1f, &csr->status);
+
+ dma_sync_single_for_device(priv->device, priv->txdescphys,
+ priv->txdescmem, DMA_TO_DEVICE);
+
+ iowrite32(lower_32_bits(sgdma_txphysaddr(priv, desc)),
+ &csr->next_descrip);
+
+ iowrite32((priv->txctrlreg | SGDMA_CTRLREG_START),
+ &csr->control);
+
+ return 1;
+}
+
+static dma_addr_t
+sgdma_txphysaddr(struct altera_tse_private *priv,
+ struct sgdma_descrip *desc)
+{
+ dma_addr_t paddr = priv->txdescmem_busaddr;
+ uintptr_t offs = (uintptr_t)desc - (uintptr_t)priv->tx_dma_desc;
+ return (dma_addr_t)((uintptr_t)paddr + offs);
+}
+
+static dma_addr_t
+sgdma_rxphysaddr(struct altera_tse_private *priv,
+ struct sgdma_descrip *desc)
+{
+ dma_addr_t paddr = priv->rxdescmem_busaddr;
+ uintptr_t offs = (uintptr_t)desc - (uintptr_t)priv->rx_dma_desc;
+ return (dma_addr_t)((uintptr_t)paddr + offs);
+}
+
+#define list_remove_head(list, entry, type, member) \
+ do { \
+ entry = NULL; \
+ if (!list_empty(list)) { \
+ entry = list_entry((list)->next, type, member); \
+ list_del_init(&entry->member); \
+ } \
+ } while (0)
+
+#define list_peek_head(list, entry, type, member) \
+ do { \
+ entry = NULL; \
+ if (!list_empty(list)) { \
+ entry = list_entry((list)->next, type, member); \
+ } \
+ } while (0)
+
+/* adds a tse_buffer to the tail of a tx buffer list.
+ * assumes the caller is managing and holding a mutual exclusion
+ * primitive to avoid simultaneous pushes/pops to the list.
+ */
+static void
+queue_tx(struct altera_tse_private *priv, struct tse_buffer *buffer)
+{
+ list_add_tail(&buffer->lh, &priv->txlisthd);
+}
+
+
+/* adds a tse_buffer to the tail of a rx buffer list
+ * assumes the caller is managing and holding a mutual exclusion
+ * primitive to avoid simultaneous pushes/pops to the list.
+ */
+static void
+queue_rx(struct altera_tse_private *priv, struct tse_buffer *buffer)
+{
+ list_add_tail(&buffer->lh, &priv->rxlisthd);
+}
+
+/* dequeues a tse_buffer from the transmit buffer list, otherwise
+ * returns NULL if empty.
+ * assumes the caller is managing and holding a mutual exclusion
+ * primitive to avoid simultaneous pushes/pops to the list.
+ */
+static struct tse_buffer *
+dequeue_tx(struct altera_tse_private *priv)
+{
+ struct tse_buffer *buffer = NULL;
+ list_remove_head(&priv->txlisthd, buffer, struct tse_buffer, lh);
+ return buffer;
+}
+
+/* dequeues a tse_buffer from the receive buffer list, otherwise
+ * returns NULL if empty
+ * assumes the caller is managing and holding a mutual exclusion
+ * primitive to avoid simultaneous pushes/pops to the list.
+ */
+static struct tse_buffer *
+dequeue_rx(struct altera_tse_private *priv)
+{
+ struct tse_buffer *buffer = NULL;
+ list_remove_head(&priv->rxlisthd, buffer, struct tse_buffer, lh);
+ return buffer;
+}
+
+/* dequeues a tse_buffer from the receive buffer list, otherwise
+ * returns NULL if empty
+ * assumes the caller is managing and holding a mutual exclusion
+ * primitive to avoid simultaneous pushes/pops to the list while the
+ * head is being examined.
+ */
+static struct tse_buffer *
+queue_rx_peekhead(struct altera_tse_private *priv)
+{
+ struct tse_buffer *buffer = NULL;
+ list_peek_head(&priv->rxlisthd, buffer, struct tse_buffer, lh);
+ return buffer;
+}
+
+/* check and return rx sgdma status without polling
+ */
+static int sgdma_rxbusy(struct altera_tse_private *priv)
+{
+ struct sgdma_csr *csr = (struct sgdma_csr *)priv->rx_dma_csr;
+ return ioread32(&csr->status) & SGDMA_STSREG_BUSY;
+}
+
+/* waits for the tx sgdma to finish it's current operation, returns 0
+ * when it transitions to nonbusy, returns 1 if the operation times out
+ */
+static int sgdma_txbusy(struct altera_tse_private *priv)
+{
+ int delay = 0;
+ struct sgdma_csr *csr = (struct sgdma_csr *)priv->tx_dma_csr;
+
+ /* if DMA is busy, wait for current transactino to finish */
+ while ((ioread32(&csr->status) & SGDMA_STSREG_BUSY) && (delay++ < 100))
+ udelay(1);
+
+ if (ioread32(&csr->status) & SGDMA_STSREG_BUSY) {
+ netdev_err(priv->dev, "timeout waiting for tx dma\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/drivers/net/ethernet/altera/altera_sgdma.h b/drivers/net/ethernet/altera/altera_sgdma.h
new file mode 100644
index 000000000000..07d471729dc4
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_sgdma.h
@@ -0,0 +1,35 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. 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 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 __ALTERA_SGDMA_H__
+#define __ALTERA_SGDMA_H__
+
+void sgdma_reset(struct altera_tse_private *);
+void sgdma_enable_txirq(struct altera_tse_private *);
+void sgdma_enable_rxirq(struct altera_tse_private *);
+void sgdma_disable_rxirq(struct altera_tse_private *);
+void sgdma_disable_txirq(struct altera_tse_private *);
+void sgdma_clear_rxirq(struct altera_tse_private *);
+void sgdma_clear_txirq(struct altera_tse_private *);
+int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *);
+u32 sgdma_tx_completions(struct altera_tse_private *);
+int sgdma_add_rx_desc(struct altera_tse_private *priv, struct tse_buffer *);
+void sgdma_status(struct altera_tse_private *);
+u32 sgdma_rx_status(struct altera_tse_private *);
+int sgdma_initialize(struct altera_tse_private *);
+void sgdma_uninitialize(struct altera_tse_private *);
+
+#endif /* __ALTERA_SGDMA_H__ */
diff --git a/drivers/net/ethernet/altera/altera_sgdmahw.h b/drivers/net/ethernet/altera/altera_sgdmahw.h
new file mode 100644
index 000000000000..ba3334f35383
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_sgdmahw.h
@@ -0,0 +1,124 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. 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 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 __ALTERA_SGDMAHW_H__
+#define __ALTERA_SGDMAHW_H__
+
+/* SGDMA descriptor structure */
+struct sgdma_descrip {
+ unsigned int raddr; /* address of data to be read */
+ unsigned int pad1;
+ unsigned int waddr;
+ unsigned int pad2;
+ unsigned int next;
+ unsigned int pad3;
+ unsigned short bytes;
+ unsigned char rburst;
+ unsigned char wburst;
+ unsigned short bytes_xferred; /* 16 bits, bytes xferred */
+
+ /* bit 0: error
+ * bit 1: length error
+ * bit 2: crc error
+ * bit 3: truncated error
+ * bit 4: phy error
+ * bit 5: collision error
+ * bit 6: reserved
+ * bit 7: status eop for recv case
+ */
+ unsigned char status;
+
+ /* bit 0: eop
+ * bit 1: read_fixed
+ * bit 2: write fixed
+ * bits 3,4,5,6: Channel (always 0)
+ * bit 7: hardware owned
+ */
+ unsigned char control;
+} __packed;
+
+
+#define SGDMA_STATUS_ERR BIT(0)
+#define SGDMA_STATUS_LENGTH_ERR BIT(1)
+#define SGDMA_STATUS_CRC_ERR BIT(2)
+#define SGDMA_STATUS_TRUNC_ERR BIT(3)
+#define SGDMA_STATUS_PHY_ERR BIT(4)
+#define SGDMA_STATUS_COLL_ERR BIT(5)
+#define SGDMA_STATUS_EOP BIT(7)
+
+#define SGDMA_CONTROL_EOP BIT(0)
+#define SGDMA_CONTROL_RD_FIXED BIT(1)
+#define SGDMA_CONTROL_WR_FIXED BIT(2)
+
+/* Channel is always 0, so just zero initialize it */
+
+#define SGDMA_CONTROL_HW_OWNED BIT(7)
+
+/* SGDMA register space */
+struct sgdma_csr {
+ /* bit 0: error
+ * bit 1: eop
+ * bit 2: descriptor completed
+ * bit 3: chain completed
+ * bit 4: busy
+ * remainder reserved
+ */
+ u32 status;
+ u32 pad1[3];
+
+ /* bit 0: interrupt on error
+ * bit 1: interrupt on eop
+ * bit 2: interrupt after every descriptor
+ * bit 3: interrupt after last descrip in a chain
+ * bit 4: global interrupt enable
+ * bit 5: starts descriptor processing
+ * bit 6: stop core on dma error
+ * bit 7: interrupt on max descriptors
+ * bits 8-15: max descriptors to generate interrupt
+ * bit 16: Software reset
+ * bit 17: clears owned by hardware if 0, does not clear otherwise
+ * bit 18: enables descriptor polling mode
+ * bit 19-26: clocks before polling again
+ * bit 27-30: reserved
+ * bit 31: clear interrupt
+ */
+ u32 control;
+ u32 pad2[3];
+ u32 next_descrip;
+ u32 pad3[3];
+};
+
+
+#define SGDMA_STSREG_ERR BIT(0) /* Error */
+#define SGDMA_STSREG_EOP BIT(1) /* EOP */
+#define SGDMA_STSREG_DESCRIP BIT(2) /* Descriptor completed */
+#define SGDMA_STSREG_CHAIN BIT(3) /* Chain completed */
+#define SGDMA_STSREG_BUSY BIT(4) /* Controller busy */
+
+#define SGDMA_CTRLREG_IOE BIT(0) /* Interrupt on error */
+#define SGDMA_CTRLREG_IOEOP BIT(1) /* Interrupt on EOP */
+#define SGDMA_CTRLREG_IDESCRIP BIT(2) /* Interrupt after every descriptor */
+#define SGDMA_CTRLREG_ILASTD BIT(3) /* Interrupt after last descriptor */
+#define SGDMA_CTRLREG_INTEN BIT(4) /* Global Interrupt enable */
+#define SGDMA_CTRLREG_START BIT(5) /* starts descriptor processing */
+#define SGDMA_CTRLREG_STOPERR BIT(6) /* stop on dma error */
+#define SGDMA_CTRLREG_INTMAX BIT(7) /* Interrupt on max descriptors */
+#define SGDMA_CTRLREG_RESET BIT(16)/* Software reset */
+#define SGDMA_CTRLREG_COBHW BIT(17)/* Clears owned by hardware */
+#define SGDMA_CTRLREG_POLL BIT(18)/* enables descriptor polling mode */
+#define SGDMA_CTRLREG_CLRINT BIT(31)/* Clears interrupt */
+
+#endif /* __ALTERA_SGDMAHW_H__ */
diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h
new file mode 100644
index 000000000000..8feeed05de0e
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_tse.h
@@ -0,0 +1,486 @@
+/* Altera Triple-Speed Ethernet MAC driver
+ * Copyright (C) 2008-2014 Altera Corporation. All rights reserved
+ *
+ * Contributors:
+ * Dalon Westergreen
+ * Thomas Chou
+ * Ian Abbott
+ * Yuriy Kozlov
+ * Tobias Klauser
+ * Andriy Smolskyy
+ * Roman Bulgakov
+ * Dmytro Mytarchuk
+ * Matthew Gerlach
+ *
+ * Original driver contributed by SLS.
+ * Major updates contributed by GlobalLogic
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_TSE_H__
+#define __ALTERA_TSE_H__
+
+#define ALTERA_TSE_RESOURCE_NAME "altera_tse"
+
+#include <linux/bitops.h>
+#include <linux/if_vlan.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+
+#define ALTERA_TSE_SW_RESET_WATCHDOG_CNTR 10000
+#define ALTERA_TSE_MAC_FIFO_WIDTH 4 /* TX/RX FIFO width in
+ * bytes
+ */
+/* Rx FIFO default settings */
+#define ALTERA_TSE_RX_SECTION_EMPTY 16
+#define ALTERA_TSE_RX_SECTION_FULL 0
+#define ALTERA_TSE_RX_ALMOST_EMPTY 8
+#define ALTERA_TSE_RX_ALMOST_FULL 8
+
+/* Tx FIFO default settings */
+#define ALTERA_TSE_TX_SECTION_EMPTY 16
+#define ALTERA_TSE_TX_SECTION_FULL 0
+#define ALTERA_TSE_TX_ALMOST_EMPTY 8
+#define ALTERA_TSE_TX_ALMOST_FULL 3
+
+/* MAC function configuration default settings */
+#define ALTERA_TSE_TX_IPG_LENGTH 12
+
+#define GET_BIT_VALUE(v, bit) (((v) >> (bit)) & 0x1)
+
+/* MAC Command_Config Register Bit Definitions
+ */
+#define MAC_CMDCFG_TX_ENA BIT(0)
+#define MAC_CMDCFG_RX_ENA BIT(1)
+#define MAC_CMDCFG_XON_GEN BIT(2)
+#define MAC_CMDCFG_ETH_SPEED BIT(3)
+#define MAC_CMDCFG_PROMIS_EN BIT(4)
+#define MAC_CMDCFG_PAD_EN BIT(5)
+#define MAC_CMDCFG_CRC_FWD BIT(6)
+#define MAC_CMDCFG_PAUSE_FWD BIT(7)
+#define MAC_CMDCFG_PAUSE_IGNORE BIT(8)
+#define MAC_CMDCFG_TX_ADDR_INS BIT(9)
+#define MAC_CMDCFG_HD_ENA BIT(10)
+#define MAC_CMDCFG_EXCESS_COL BIT(11)
+#define MAC_CMDCFG_LATE_COL BIT(12)
+#define MAC_CMDCFG_SW_RESET BIT(13)
+#define MAC_CMDCFG_MHASH_SEL BIT(14)
+#define MAC_CMDCFG_LOOP_ENA BIT(15)
+#define MAC_CMDCFG_TX_ADDR_SEL(v) (((v) & 0x7) << 16)
+#define MAC_CMDCFG_MAGIC_ENA BIT(19)
+#define MAC_CMDCFG_SLEEP BIT(20)
+#define MAC_CMDCFG_WAKEUP BIT(21)
+#define MAC_CMDCFG_XOFF_GEN BIT(22)
+#define MAC_CMDCFG_CNTL_FRM_ENA BIT(23)
+#define MAC_CMDCFG_NO_LGTH_CHECK BIT(24)
+#define MAC_CMDCFG_ENA_10 BIT(25)
+#define MAC_CMDCFG_RX_ERR_DISC BIT(26)
+#define MAC_CMDCFG_DISABLE_READ_TIMEOUT BIT(27)
+#define MAC_CMDCFG_CNT_RESET BIT(31)
+
+#define MAC_CMDCFG_TX_ENA_GET(v) GET_BIT_VALUE(v, 0)
+#define MAC_CMDCFG_RX_ENA_GET(v) GET_BIT_VALUE(v, 1)
+#define MAC_CMDCFG_XON_GEN_GET(v) GET_BIT_VALUE(v, 2)
+#define MAC_CMDCFG_ETH_SPEED_GET(v) GET_BIT_VALUE(v, 3)
+#define MAC_CMDCFG_PROMIS_EN_GET(v) GET_BIT_VALUE(v, 4)
+#define MAC_CMDCFG_PAD_EN_GET(v) GET_BIT_VALUE(v, 5)
+#define MAC_CMDCFG_CRC_FWD_GET(v) GET_BIT_VALUE(v, 6)
+#define MAC_CMDCFG_PAUSE_FWD_GET(v) GET_BIT_VALUE(v, 7)
+#define MAC_CMDCFG_PAUSE_IGNORE_GET(v) GET_BIT_VALUE(v, 8)
+#define MAC_CMDCFG_TX_ADDR_INS_GET(v) GET_BIT_VALUE(v, 9)
+#define MAC_CMDCFG_HD_ENA_GET(v) GET_BIT_VALUE(v, 10)
+#define MAC_CMDCFG_EXCESS_COL_GET(v) GET_BIT_VALUE(v, 11)
+#define MAC_CMDCFG_LATE_COL_GET(v) GET_BIT_VALUE(v, 12)
+#define MAC_CMDCFG_SW_RESET_GET(v) GET_BIT_VALUE(v, 13)
+#define MAC_CMDCFG_MHASH_SEL_GET(v) GET_BIT_VALUE(v, 14)
+#define MAC_CMDCFG_LOOP_ENA_GET(v) GET_BIT_VALUE(v, 15)
+#define MAC_CMDCFG_TX_ADDR_SEL_GET(v) (((v) >> 16) & 0x7)
+#define MAC_CMDCFG_MAGIC_ENA_GET(v) GET_BIT_VALUE(v, 19)
+#define MAC_CMDCFG_SLEEP_GET(v) GET_BIT_VALUE(v, 20)
+#define MAC_CMDCFG_WAKEUP_GET(v) GET_BIT_VALUE(v, 21)
+#define MAC_CMDCFG_XOFF_GEN_GET(v) GET_BIT_VALUE(v, 22)
+#define MAC_CMDCFG_CNTL_FRM_ENA_GET(v) GET_BIT_VALUE(v, 23)
+#define MAC_CMDCFG_NO_LGTH_CHECK_GET(v) GET_BIT_VALUE(v, 24)
+#define MAC_CMDCFG_ENA_10_GET(v) GET_BIT_VALUE(v, 25)
+#define MAC_CMDCFG_RX_ERR_DISC_GET(v) GET_BIT_VALUE(v, 26)
+#define MAC_CMDCFG_DISABLE_READ_TIMEOUT_GET(v) GET_BIT_VALUE(v, 27)
+#define MAC_CMDCFG_CNT_RESET_GET(v) GET_BIT_VALUE(v, 31)
+
+/* MDIO registers within MAC register Space
+ */
+struct altera_tse_mdio {
+ u32 control; /* PHY device operation control register */
+ u32 status; /* PHY device operation status register */
+ u32 phy_id1; /* Bits 31:16 of PHY identifier */
+ u32 phy_id2; /* Bits 15:0 of PHY identifier */
+ u32 auto_negotiation_advertisement; /* Auto-negotiation
+ * advertisement
+ * register
+ */
+ u32 remote_partner_base_page_ability;
+
+ u32 reg6;
+ u32 reg7;
+ u32 reg8;
+ u32 reg9;
+ u32 rega;
+ u32 regb;
+ u32 regc;
+ u32 regd;
+ u32 rege;
+ u32 regf;
+ u32 reg10;
+ u32 reg11;
+ u32 reg12;
+ u32 reg13;
+ u32 reg14;
+ u32 reg15;
+ u32 reg16;
+ u32 reg17;
+ u32 reg18;
+ u32 reg19;
+ u32 reg1a;
+ u32 reg1b;
+ u32 reg1c;
+ u32 reg1d;
+ u32 reg1e;
+ u32 reg1f;
+};
+
+/* MAC register Space. Note that some of these registers may or may not be
+ * present depending upon options chosen by the user when the core was
+ * configured and built. Please consult the Altera Triple Speed Ethernet User
+ * Guide for details.
+ */
+struct altera_tse_mac {
+ /* Bits 15:0: MegaCore function revision (0x0800). Bit 31:16: Customer
+ * specific revision
+ */
+ u32 megacore_revision;
+ /* Provides a memory location for user applications to test the device
+ * memory operation.
+ */
+ u32 scratch_pad;
+ /* The host processor uses this register to control and configure the
+ * MAC block
+ */
+ u32 command_config;
+ /* 32-bit primary MAC address word 0 bits 0 to 31 of the primary
+ * MAC address
+ */
+ u32 mac_addr_0;
+ /* 32-bit primary MAC address word 1 bits 32 to 47 of the primary
+ * MAC address
+ */
+ u32 mac_addr_1;
+ /* 14-bit maximum frame length. The MAC receive logic */
+ u32 frm_length;
+ /* The pause quanta is used in each pause frame sent to a remote
+ * Ethernet device, in increments of 512 Ethernet bit times
+ */
+ u32 pause_quanta;
+ /* 12-bit receive FIFO section-empty threshold */
+ u32 rx_section_empty;
+ /* 12-bit receive FIFO section-full threshold */
+ u32 rx_section_full;
+ /* 12-bit transmit FIFO section-empty threshold */
+ u32 tx_section_empty;
+ /* 12-bit transmit FIFO section-full threshold */
+ u32 tx_section_full;
+ /* 12-bit receive FIFO almost-empty threshold */
+ u32 rx_almost_empty;
+ /* 12-bit receive FIFO almost-full threshold */
+ u32 rx_almost_full;
+ /* 12-bit transmit FIFO almost-empty threshold */
+ u32 tx_almost_empty;
+ /* 12-bit transmit FIFO almost-full threshold */
+ u32 tx_almost_full;
+ /* MDIO address of PHY Device 0. Bits 0 to 4 hold a 5-bit PHY address */
+ u32 mdio_phy0_addr;
+ /* MDIO address of PHY Device 1. Bits 0 to 4 hold a 5-bit PHY address */
+ u32 mdio_phy1_addr;
+
+ /* Bit[15:0]—16-bit holdoff quanta */
+ u32 holdoff_quant;
+
+ /* only if 100/1000 BaseX PCS, reserved otherwise */
+ u32 reserved1[5];
+
+ /* Minimum IPG between consecutive transmit frame in terms of bytes */
+ u32 tx_ipg_length;
+
+ /* IEEE 802.3 oEntity Managed Object Support */
+
+ /* The MAC addresses */
+ u32 mac_id_1;
+ u32 mac_id_2;
+
+ /* Number of frames transmitted without error including pause frames */
+ u32 frames_transmitted_ok;
+ /* Number of frames received without error including pause frames */
+ u32 frames_received_ok;
+ /* Number of frames received with a CRC error */
+ u32 frames_check_sequence_errors;
+ /* Frame received with an alignment error */
+ u32 alignment_errors;
+ /* Sum of payload and padding octets of frames transmitted without
+ * error
+ */
+ u32 octets_transmitted_ok;
+ /* Sum of payload and padding octets of frames received without error */
+ u32 octets_received_ok;
+
+ /* IEEE 802.3 oPausedEntity Managed Object Support */
+
+ /* Number of transmitted pause frames */
+ u32 tx_pause_mac_ctrl_frames;
+ /* Number of Received pause frames */
+ u32 rx_pause_mac_ctrl_frames;
+
+ /* IETF MIB (MIB-II) Object Support */
+
+ /* Number of frames received with error */
+ u32 if_in_errors;
+ /* Number of frames transmitted with error */
+ u32 if_out_errors;
+ /* Number of valid received unicast frames */
+ u32 if_in_ucast_pkts;
+ /* Number of valid received multicasts frames (without pause) */
+ u32 if_in_multicast_pkts;
+ /* Number of valid received broadcast frames */
+ u32 if_in_broadcast_pkts;
+ u32 if_out_discards;
+ /* The number of valid unicast frames transmitted */
+ u32 if_out_ucast_pkts;
+ /* The number of valid multicast frames transmitted,
+ * excluding pause frames
+ */
+ u32 if_out_multicast_pkts;
+ u32 if_out_broadcast_pkts;
+
+ /* IETF RMON MIB Object Support */
+
+ /* Counts the number of dropped packets due to internal errors
+ * of the MAC client.
+ */
+ u32 ether_stats_drop_events;
+ /* Total number of bytes received. Good and bad frames. */
+ u32 ether_stats_octets;
+ /* Total number of packets received. Counts good and bad packets. */
+ u32 ether_stats_pkts;
+ /* Number of packets received with less than 64 bytes. */
+ u32 ether_stats_undersize_pkts;
+ /* The number of frames received that are longer than the
+ * value configured in the frm_length register
+ */
+ u32 ether_stats_oversize_pkts;
+ /* Number of received packet with 64 bytes */
+ u32 ether_stats_pkts_64_octets;
+ /* Frames (good and bad) with 65 to 127 bytes */
+ u32 ether_stats_pkts_65to127_octets;
+ /* Frames (good and bad) with 128 to 255 bytes */
+ u32 ether_stats_pkts_128to255_octets;
+ /* Frames (good and bad) with 256 to 511 bytes */
+ u32 ether_stats_pkts_256to511_octets;
+ /* Frames (good and bad) with 512 to 1023 bytes */
+ u32 ether_stats_pkts_512to1023_octets;
+ /* Frames (good and bad) with 1024 to 1518 bytes */
+ u32 ether_stats_pkts_1024to1518_octets;
+
+ /* Any frame length from 1519 to the maximum length configured in the
+ * frm_length register, if it is greater than 1518
+ */
+ u32 ether_stats_pkts_1519tox_octets;
+ /* Too long frames with CRC error */
+ u32 ether_stats_jabbers;
+ /* Too short frames with CRC error */
+ u32 ether_stats_fragments;
+
+ u32 reserved2;
+
+ /* FIFO control register */
+ u32 tx_cmd_stat;
+ u32 rx_cmd_stat;
+
+ /* Extended Statistics Counters */
+ u32 msb_octets_transmitted_ok;
+ u32 msb_octets_received_ok;
+ u32 msb_ether_stats_octets;
+
+ u32 reserved3;
+
+ /* Multicast address resolution table, mapped in the controller address
+ * space
+ */
+ u32 hash_table[64];
+
+ /* Registers 0 to 31 within PHY device 0/1 connected to the MDIO PHY
+ * management interface
+ */
+ struct altera_tse_mdio mdio_phy0;
+ struct altera_tse_mdio mdio_phy1;
+
+ /* 4 Supplemental MAC Addresses */
+ u32 supp_mac_addr_0_0;
+ u32 supp_mac_addr_0_1;
+ u32 supp_mac_addr_1_0;
+ u32 supp_mac_addr_1_1;
+ u32 supp_mac_addr_2_0;
+ u32 supp_mac_addr_2_1;
+ u32 supp_mac_addr_3_0;
+ u32 supp_mac_addr_3_1;
+
+ u32 reserved4[8];
+
+ /* IEEE 1588v2 Feature */
+ u32 tx_period;
+ u32 tx_adjust_fns;
+ u32 tx_adjust_ns;
+ u32 rx_period;
+ u32 rx_adjust_fns;
+ u32 rx_adjust_ns;
+
+ u32 reserved5[42];
+};
+
+/* Transmit and Receive Command Registers Bit Definitions
+ */
+#define ALTERA_TSE_TX_CMD_STAT_OMIT_CRC BIT(17)
+#define ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 BIT(18)
+#define ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16 BIT(25)
+
+/* Wrapper around a pointer to a socket buffer,
+ * so a DMA handle can be stored along with the buffer
+ */
+struct tse_buffer {
+ struct list_head lh;
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+ u32 len;
+ int mapped_as_page;
+};
+
+struct altera_tse_private;
+
+#define ALTERA_DTYPE_SGDMA 1
+#define ALTERA_DTYPE_MSGDMA 2
+
+/* standard DMA interface for SGDMA and MSGDMA */
+struct altera_dmaops {
+ int altera_dtype;
+ int dmamask;
+ void (*reset_dma)(struct altera_tse_private *);
+ void (*enable_txirq)(struct altera_tse_private *);
+ void (*enable_rxirq)(struct altera_tse_private *);
+ void (*disable_txirq)(struct altera_tse_private *);
+ void (*disable_rxirq)(struct altera_tse_private *);
+ void (*clear_txirq)(struct altera_tse_private *);
+ void (*clear_rxirq)(struct altera_tse_private *);
+ int (*tx_buffer)(struct altera_tse_private *, struct tse_buffer *);
+ u32 (*tx_completions)(struct altera_tse_private *);
+ int (*add_rx_desc)(struct altera_tse_private *, struct tse_buffer *);
+ u32 (*get_rx_status)(struct altera_tse_private *);
+ int (*init_dma)(struct altera_tse_private *);
+ void (*uninit_dma)(struct altera_tse_private *);
+};
+
+/* This structure is private to each device.
+ */
+struct altera_tse_private {
+ struct net_device *dev;
+ struct device *device;
+ struct napi_struct napi;
+
+ /* MAC address space */
+ struct altera_tse_mac __iomem *mac_dev;
+
+ /* TSE Revision */
+ u32 revision;
+
+ /* mSGDMA Rx Dispatcher address space */
+ void __iomem *rx_dma_csr;
+ void __iomem *rx_dma_desc;
+ void __iomem *rx_dma_resp;
+
+ /* mSGDMA Tx Dispatcher address space */
+ void __iomem *tx_dma_csr;
+ void __iomem *tx_dma_desc;
+
+ /* Rx buffers queue */
+ struct tse_buffer *rx_ring;
+ u32 rx_cons;
+ u32 rx_prod;
+ u32 rx_ring_size;
+ u32 rx_dma_buf_sz;
+
+ /* Tx ring buffer */
+ struct tse_buffer *tx_ring;
+ u32 tx_prod;
+ u32 tx_cons;
+ u32 tx_ring_size;
+
+ /* Interrupts */
+ u32 tx_irq;
+ u32 rx_irq;
+
+ /* RX/TX MAC FIFO configs */
+ u32 tx_fifo_depth;
+ u32 rx_fifo_depth;
+ u32 max_mtu;
+
+ /* Hash filter settings */
+ u32 hash_filter;
+ u32 added_unicast;
+
+ /* Descriptor memory info for managing SGDMA */
+ u32 txdescmem;
+ u32 rxdescmem;
+ dma_addr_t rxdescmem_busaddr;
+ dma_addr_t txdescmem_busaddr;
+ u32 txctrlreg;
+ u32 rxctrlreg;
+ dma_addr_t rxdescphys;
+ dma_addr_t txdescphys;
+
+ struct list_head txlisthd;
+ struct list_head rxlisthd;
+
+ /* MAC command_config register protection */
+ spinlock_t mac_cfg_lock;
+ /* Tx path protection */
+ spinlock_t tx_lock;
+ /* Rx DMA & interrupt control protection */
+ spinlock_t rxdma_irq_lock;
+
+ /* PHY */
+ int phy_addr; /* PHY's MDIO address, -1 for autodetection */
+ phy_interface_t phy_iface;
+ struct mii_bus *mdio;
+ struct phy_device *phydev;
+ int oldspeed;
+ int oldduplex;
+ int oldlink;
+
+ /* ethtool msglvl option */
+ u32 msg_enable;
+
+ struct altera_dmaops *dmaops;
+};
+
+/* Function prototypes
+ */
+void altera_tse_set_ethtool_ops(struct net_device *);
+
+#endif /* __ALTERA_TSE_H__ */
diff --git a/drivers/net/ethernet/altera/altera_tse_ethtool.c b/drivers/net/ethernet/altera/altera_tse_ethtool.c
new file mode 100644
index 000000000000..319ca74f5e74
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_tse_ethtool.c
@@ -0,0 +1,235 @@
+/* Ethtool support for Altera Triple-Speed Ethernet MAC driver
+ * Copyright (C) 2008-2014 Altera Corporation. All rights reserved
+ *
+ * Contributors:
+ * Dalon Westergreen
+ * Thomas Chou
+ * Ian Abbott
+ * Yuriy Kozlov
+ * Tobias Klauser
+ * Andriy Smolskyy
+ * Roman Bulgakov
+ * Dmytro Mytarchuk
+ *
+ * Original driver contributed by SLS.
+ * Major updates contributed by GlobalLogic
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/ethtool.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+
+#include "altera_tse.h"
+
+#define TSE_STATS_LEN 31
+#define TSE_NUM_REGS 128
+
+static char const stat_gstrings[][ETH_GSTRING_LEN] = {
+ "tx_packets",
+ "rx_packets",
+ "rx_crc_errors",
+ "rx_align_errors",
+ "tx_bytes",
+ "rx_bytes",
+ "tx_pause",
+ "rx_pause",
+ "rx_errors",
+ "tx_errors",
+ "rx_unicast",
+ "rx_multicast",
+ "rx_broadcast",
+ "tx_discards",
+ "tx_unicast",
+ "tx_multicast",
+ "tx_broadcast",
+ "ether_drops",
+ "rx_total_bytes",
+ "rx_total_packets",
+ "rx_undersize",
+ "rx_oversize",
+ "rx_64_bytes",
+ "rx_65_127_bytes",
+ "rx_128_255_bytes",
+ "rx_256_511_bytes",
+ "rx_512_1023_bytes",
+ "rx_1024_1518_bytes",
+ "rx_gte_1519_bytes",
+ "rx_jabbers",
+ "rx_runts",
+};
+
+static void tse_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ u32 rev = ioread32(&priv->mac_dev->megacore_revision);
+
+ strcpy(info->driver, "Altera TSE MAC IP Driver");
+ strcpy(info->version, "v8.0");
+ snprintf(info->fw_version, ETHTOOL_FWVERS_LEN, "v%d.%d",
+ rev & 0xFFFF, (rev & 0xFFFF0000) >> 16);
+ sprintf(info->bus_info, "platform");
+}
+
+/* Fill in a buffer with the strings which correspond to the
+ * stats
+ */
+static void tse_gstrings(struct net_device *dev, u32 stringset, u8 *buf)
+{
+ memcpy(buf, stat_gstrings, TSE_STATS_LEN * ETH_GSTRING_LEN);
+}
+
+static void tse_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
+ u64 *buf)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct altera_tse_mac *mac = priv->mac_dev;
+ u64 ext;
+
+ buf[0] = ioread32(&mac->frames_transmitted_ok);
+ buf[1] = ioread32(&mac->frames_received_ok);
+ buf[2] = ioread32(&mac->frames_check_sequence_errors);
+ buf[3] = ioread32(&mac->alignment_errors);
+
+ /* Extended aOctetsTransmittedOK counter */
+ ext = (u64) ioread32(&mac->msb_octets_transmitted_ok) << 32;
+ ext |= ioread32(&mac->octets_transmitted_ok);
+ buf[4] = ext;
+
+ /* Extended aOctetsReceivedOK counter */
+ ext = (u64) ioread32(&mac->msb_octets_received_ok) << 32;
+ ext |= ioread32(&mac->octets_received_ok);
+ buf[5] = ext;
+
+ buf[6] = ioread32(&mac->tx_pause_mac_ctrl_frames);
+ buf[7] = ioread32(&mac->rx_pause_mac_ctrl_frames);
+ buf[8] = ioread32(&mac->if_in_errors);
+ buf[9] = ioread32(&mac->if_out_errors);
+ buf[10] = ioread32(&mac->if_in_ucast_pkts);
+ buf[11] = ioread32(&mac->if_in_multicast_pkts);
+ buf[12] = ioread32(&mac->if_in_broadcast_pkts);
+ buf[13] = ioread32(&mac->if_out_discards);
+ buf[14] = ioread32(&mac->if_out_ucast_pkts);
+ buf[15] = ioread32(&mac->if_out_multicast_pkts);
+ buf[16] = ioread32(&mac->if_out_broadcast_pkts);
+ buf[17] = ioread32(&mac->ether_stats_drop_events);
+
+ /* Extended etherStatsOctets counter */
+ ext = (u64) ioread32(&mac->msb_ether_stats_octets) << 32;
+ ext |= ioread32(&mac->ether_stats_octets);
+ buf[18] = ext;
+
+ buf[19] = ioread32(&mac->ether_stats_pkts);
+ buf[20] = ioread32(&mac->ether_stats_undersize_pkts);
+ buf[21] = ioread32(&mac->ether_stats_oversize_pkts);
+ buf[22] = ioread32(&mac->ether_stats_pkts_64_octets);
+ buf[23] = ioread32(&mac->ether_stats_pkts_65to127_octets);
+ buf[24] = ioread32(&mac->ether_stats_pkts_128to255_octets);
+ buf[25] = ioread32(&mac->ether_stats_pkts_256to511_octets);
+ buf[26] = ioread32(&mac->ether_stats_pkts_512to1023_octets);
+ buf[27] = ioread32(&mac->ether_stats_pkts_1024to1518_octets);
+ buf[28] = ioread32(&mac->ether_stats_pkts_1519tox_octets);
+ buf[29] = ioread32(&mac->ether_stats_jabbers);
+ buf[30] = ioread32(&mac->ether_stats_fragments);
+}
+
+static int tse_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return TSE_STATS_LEN;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static u32 tse_get_msglevel(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ return priv->msg_enable;
+}
+
+static void tse_set_msglevel(struct net_device *dev, uint32_t data)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ priv->msg_enable = data;
+}
+
+static int tse_reglen(struct net_device *dev)
+{
+ return TSE_NUM_REGS * sizeof(u32);
+}
+
+static void tse_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *regbuf)
+{
+ int i;
+ struct altera_tse_private *priv = netdev_priv(dev);
+ u32 *tse_mac_regs = (u32 *)priv->mac_dev;
+ u32 *buf = regbuf;
+
+ /* Set version to a known value, so ethtool knows
+ * how to do any special formatting of this data.
+ * This version number will need to change if and
+ * when this register table is changed.
+ */
+
+ regs->version = 1;
+
+ for (i = 0; i < TSE_NUM_REGS; i++)
+ buf[i] = ioread32(&tse_mac_regs[i]);
+}
+
+static int tse_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+
+ if (phydev == NULL)
+ return -ENODEV;
+
+ return phy_ethtool_gset(phydev, cmd);
+}
+
+static int tse_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+
+ if (phydev == NULL)
+ return -ENODEV;
+
+ return phy_ethtool_sset(phydev, cmd);
+}
+
+static const struct ethtool_ops tse_ethtool_ops = {
+ .get_drvinfo = tse_get_drvinfo,
+ .get_regs_len = tse_reglen,
+ .get_regs = tse_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_settings = tse_get_settings,
+ .set_settings = tse_set_settings,
+ .get_strings = tse_gstrings,
+ .get_sset_count = tse_sset_count,
+ .get_ethtool_stats = tse_fill_stats,
+ .get_msglevel = tse_get_msglevel,
+ .set_msglevel = tse_set_msglevel,
+};
+
+void altera_tse_set_ethtool_ops(struct net_device *netdev)
+{
+ SET_ETHTOOL_OPS(netdev, &tse_ethtool_ops);
+}
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
new file mode 100644
index 000000000000..c70a29e0b9f7
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -0,0 +1,1543 @@
+/* Altera Triple-Speed Ethernet MAC driver
+ * Copyright (C) 2008-2014 Altera Corporation. All rights reserved
+ *
+ * Contributors:
+ * Dalon Westergreen
+ * Thomas Chou
+ * Ian Abbott
+ * Yuriy Kozlov
+ * Tobias Klauser
+ * Andriy Smolskyy
+ * Roman Bulgakov
+ * Dmytro Mytarchuk
+ * Matthew Gerlach
+ *
+ * Original driver contributed by SLS.
+ * Major updates contributed by GlobalLogic
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_device.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <asm/cacheflush.h>
+
+#include "altera_utils.h"
+#include "altera_tse.h"
+#include "altera_sgdma.h"
+#include "altera_msgdma.h"
+
+static atomic_t instance_count = ATOMIC_INIT(~0);
+/* Module parameters */
+static int debug = -1;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Message Level (-1: default, 0: no output, 16: all)");
+
+static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
+ NETIF_MSG_LINK | NETIF_MSG_IFUP |
+ NETIF_MSG_IFDOWN);
+
+#define RX_DESCRIPTORS 64
+static int dma_rx_num = RX_DESCRIPTORS;
+module_param(dma_rx_num, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dma_rx_num, "Number of descriptors in the RX list");
+
+#define TX_DESCRIPTORS 64
+static int dma_tx_num = TX_DESCRIPTORS;
+module_param(dma_tx_num, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dma_tx_num, "Number of descriptors in the TX list");
+
+
+#define POLL_PHY (-1)
+
+/* Make sure DMA buffer size is larger than the max frame size
+ * plus some alignment offset and a VLAN header. If the max frame size is
+ * 1518, a VLAN header would be additional 4 bytes and additional
+ * headroom for alignment is 2 bytes, 2048 is just fine.
+ */
+#define ALTERA_RXDMABUFFER_SIZE 2048
+
+/* Allow network stack to resume queueing packets after we've
+ * finished transmitting at least 1/4 of the packets in the queue.
+ */
+#define TSE_TX_THRESH(x) (x->tx_ring_size / 4)
+
+#define TXQUEUESTOP_THRESHHOLD 2
+
+static struct of_device_id altera_tse_ids[];
+
+static inline u32 tse_tx_avail(struct altera_tse_private *priv)
+{
+ return priv->tx_cons + priv->tx_ring_size - priv->tx_prod - 1;
+}
+
+/* MDIO specific functions
+ */
+static int altera_tse_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ struct altera_tse_mac *mac = (struct altera_tse_mac *)bus->priv;
+ unsigned int *mdio_regs = (unsigned int *)&mac->mdio_phy0;
+ u32 data;
+
+ /* set MDIO address */
+ iowrite32((mii_id & 0x1f), &mac->mdio_phy0_addr);
+
+ /* get the data */
+ data = ioread32(&mdio_regs[regnum]) & 0xffff;
+ return data;
+}
+
+static int altera_tse_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
+ u16 value)
+{
+ struct altera_tse_mac *mac = (struct altera_tse_mac *)bus->priv;
+ unsigned int *mdio_regs = (unsigned int *)&mac->mdio_phy0;
+
+ /* set MDIO address */
+ iowrite32((mii_id & 0x1f), &mac->mdio_phy0_addr);
+
+ /* write the data */
+ iowrite32((u32) value, &mdio_regs[regnum]);
+ return 0;
+}
+
+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;
+
+ for_each_child_of_node(priv->device->of_node, child_node) {
+ if (of_device_is_compatible(child_node, "altr,tse-mdio")) {
+ mdio_node = child_node;
+ break;
+ }
+ }
+
+ if (mdio_node) {
+ netdev_dbg(dev, "FOUND MDIO subnode\n");
+ } else {
+ netdev_dbg(dev, "NO MDIO subnode\n");
+ return 0;
+ }
+
+ mdio = mdiobus_alloc();
+ if (mdio == NULL) {
+ netdev_err(dev, "Error allocating MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ mdio->name = ALTERA_TSE_RESOURCE_NAME;
+ mdio->read = &altera_tse_mdio_read;
+ 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 = priv->mac_dev;
+ mdio->parent = priv->device;
+
+ ret = of_mdiobus_register(mdio, mdio_node);
+ if (ret != 0) {
+ netdev_err(dev, "Cannot register MDIO bus %s\n",
+ mdio->id);
+ goto out_free_mdio_irq;
+ }
+
+ if (netif_msg_drv(priv))
+ netdev_info(dev, "MDIO bus %s: created\n", mdio->id);
+
+ priv->mdio = mdio;
+ return 0;
+out_free_mdio_irq:
+ kfree(mdio->irq);
+out_free_mdio:
+ mdiobus_free(mdio);
+ mdio = NULL;
+ return ret;
+}
+
+static void altera_tse_mdio_destroy(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+
+ if (priv->mdio == NULL)
+ return;
+
+ if (netif_msg_drv(priv))
+ netdev_info(dev, "MDIO bus %s: removed\n",
+ priv->mdio->id);
+
+ mdiobus_unregister(priv->mdio);
+ kfree(priv->mdio->irq);
+ mdiobus_free(priv->mdio);
+ priv->mdio = NULL;
+}
+
+static int tse_init_rx_buffer(struct altera_tse_private *priv,
+ struct tse_buffer *rxbuffer, int len)
+{
+ rxbuffer->skb = netdev_alloc_skb_ip_align(priv->dev, len);
+ if (!rxbuffer->skb)
+ return -ENOMEM;
+
+ rxbuffer->dma_addr = dma_map_single(priv->device, rxbuffer->skb->data,
+ len,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(priv->device, rxbuffer->dma_addr)) {
+ netdev_err(priv->dev, "%s: DMA mapping error\n", __func__);
+ dev_kfree_skb_any(rxbuffer->skb);
+ return -EINVAL;
+ }
+ rxbuffer->len = len;
+ return 0;
+}
+
+static void tse_free_rx_buffer(struct altera_tse_private *priv,
+ struct tse_buffer *rxbuffer)
+{
+ struct sk_buff *skb = rxbuffer->skb;
+ dma_addr_t dma_addr = rxbuffer->dma_addr;
+
+ if (skb != NULL) {
+ if (dma_addr)
+ dma_unmap_single(priv->device, dma_addr,
+ rxbuffer->len,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(skb);
+ rxbuffer->skb = NULL;
+ rxbuffer->dma_addr = 0;
+ }
+}
+
+/* Unmap and free Tx buffer resources
+ */
+static void tse_free_tx_buffer(struct altera_tse_private *priv,
+ struct tse_buffer *buffer)
+{
+ if (buffer->dma_addr) {
+ if (buffer->mapped_as_page)
+ dma_unmap_page(priv->device, buffer->dma_addr,
+ buffer->len, DMA_TO_DEVICE);
+ else
+ dma_unmap_single(priv->device, buffer->dma_addr,
+ buffer->len, DMA_TO_DEVICE);
+ buffer->dma_addr = 0;
+ }
+ if (buffer->skb) {
+ dev_kfree_skb_any(buffer->skb);
+ buffer->skb = NULL;
+ }
+}
+
+static int alloc_init_skbufs(struct altera_tse_private *priv)
+{
+ unsigned int rx_descs = priv->rx_ring_size;
+ unsigned int tx_descs = priv->tx_ring_size;
+ int ret = -ENOMEM;
+ int i;
+
+ /* Create Rx ring buffer */
+ priv->rx_ring = kcalloc(rx_descs, sizeof(struct tse_buffer),
+ GFP_KERNEL);
+ if (!priv->rx_ring)
+ goto err_rx_ring;
+
+ /* Create Tx ring buffer */
+ priv->tx_ring = kcalloc(tx_descs, sizeof(struct tse_buffer),
+ GFP_KERNEL);
+ if (!priv->tx_ring)
+ goto err_tx_ring;
+
+ priv->tx_cons = 0;
+ priv->tx_prod = 0;
+
+ /* Init Rx ring */
+ for (i = 0; i < rx_descs; i++) {
+ ret = tse_init_rx_buffer(priv, &priv->rx_ring[i],
+ priv->rx_dma_buf_sz);
+ if (ret)
+ goto err_init_rx_buffers;
+ }
+
+ priv->rx_cons = 0;
+ priv->rx_prod = 0;
+
+ return 0;
+err_init_rx_buffers:
+ while (--i >= 0)
+ tse_free_rx_buffer(priv, &priv->rx_ring[i]);
+ kfree(priv->tx_ring);
+err_tx_ring:
+ kfree(priv->rx_ring);
+err_rx_ring:
+ return ret;
+}
+
+static void free_skbufs(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ unsigned int rx_descs = priv->rx_ring_size;
+ unsigned int tx_descs = priv->tx_ring_size;
+ int i;
+
+ /* Release the DMA TX/RX socket buffers */
+ for (i = 0; i < rx_descs; i++)
+ tse_free_rx_buffer(priv, &priv->rx_ring[i]);
+ for (i = 0; i < tx_descs; i++)
+ tse_free_tx_buffer(priv, &priv->tx_ring[i]);
+
+
+ kfree(priv->tx_ring);
+}
+
+/* Reallocate the skb for the reception process
+ */
+static inline void tse_rx_refill(struct altera_tse_private *priv)
+{
+ unsigned int rxsize = priv->rx_ring_size;
+ unsigned int entry;
+ int ret;
+
+ for (; priv->rx_cons - priv->rx_prod > 0;
+ priv->rx_prod++) {
+ entry = priv->rx_prod % rxsize;
+ if (likely(priv->rx_ring[entry].skb == NULL)) {
+ ret = tse_init_rx_buffer(priv, &priv->rx_ring[entry],
+ priv->rx_dma_buf_sz);
+ if (unlikely(ret != 0))
+ break;
+ priv->dmaops->add_rx_desc(priv, &priv->rx_ring[entry]);
+ }
+ }
+}
+
+/* Pull out the VLAN tag and fix up the packet
+ */
+static inline void tse_rx_vlan(struct net_device *dev, struct sk_buff *skb)
+{
+ struct ethhdr *eth_hdr;
+ u16 vid;
+ if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
+ !__vlan_get_tag(skb, &vid)) {
+ eth_hdr = (struct ethhdr *)skb->data;
+ memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2);
+ skb_pull(skb, VLAN_HLEN);
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
+ }
+}
+
+/* Receive a packet: retrieve and pass over to upper levels
+ */
+static int tse_rx(struct altera_tse_private *priv, int limit)
+{
+ unsigned int count = 0;
+ unsigned int next_entry;
+ struct sk_buff *skb;
+ unsigned int entry = priv->rx_cons % priv->rx_ring_size;
+ u32 rxstatus;
+ u16 pktlength;
+ u16 pktstatus;
+
+ while ((rxstatus = priv->dmaops->get_rx_status(priv)) != 0) {
+ pktstatus = rxstatus >> 16;
+ pktlength = rxstatus & 0xffff;
+
+ if ((pktstatus & 0xFF) || (pktlength == 0))
+ netdev_err(priv->dev,
+ "RCV pktstatus %08X pktlength %08X\n",
+ pktstatus, pktlength);
+
+ count++;
+ next_entry = (++priv->rx_cons) % priv->rx_ring_size;
+
+ skb = priv->rx_ring[entry].skb;
+ if (unlikely(!skb)) {
+ netdev_err(priv->dev,
+ "%s: Inconsistent Rx descriptor chain\n",
+ __func__);
+ priv->dev->stats.rx_dropped++;
+ break;
+ }
+ priv->rx_ring[entry].skb = NULL;
+
+ skb_put(skb, pktlength);
+
+ /* make cache consistent with receive packet buffer */
+ dma_sync_single_for_cpu(priv->device,
+ priv->rx_ring[entry].dma_addr,
+ priv->rx_ring[entry].len,
+ DMA_FROM_DEVICE);
+
+ dma_unmap_single(priv->device, priv->rx_ring[entry].dma_addr,
+ priv->rx_ring[entry].len, DMA_FROM_DEVICE);
+
+ if (netif_msg_pktdata(priv)) {
+ netdev_info(priv->dev, "frame received %d bytes\n",
+ pktlength);
+ print_hex_dump(KERN_ERR, "data: ", DUMP_PREFIX_OFFSET,
+ 16, 1, skb->data, pktlength, true);
+ }
+
+ tse_rx_vlan(priv->dev, skb);
+
+ skb->protocol = eth_type_trans(skb, priv->dev);
+ skb_checksum_none_assert(skb);
+
+ napi_gro_receive(&priv->napi, skb);
+
+ priv->dev->stats.rx_packets++;
+ priv->dev->stats.rx_bytes += pktlength;
+
+ entry = next_entry;
+ }
+
+ tse_rx_refill(priv);
+ return count;
+}
+
+/* Reclaim resources after transmission completes
+ */
+static int tse_tx_complete(struct altera_tse_private *priv)
+{
+ unsigned int txsize = priv->tx_ring_size;
+ u32 ready;
+ unsigned int entry;
+ struct tse_buffer *tx_buff;
+ int txcomplete = 0;
+
+ spin_lock(&priv->tx_lock);
+
+ ready = priv->dmaops->tx_completions(priv);
+
+ /* Free sent buffers */
+ while (ready && (priv->tx_cons != priv->tx_prod)) {
+ entry = priv->tx_cons % txsize;
+ tx_buff = &priv->tx_ring[entry];
+
+ if (netif_msg_tx_done(priv))
+ netdev_dbg(priv->dev, "%s: curr %d, dirty %d\n",
+ __func__, priv->tx_prod, priv->tx_cons);
+
+ if (likely(tx_buff->skb))
+ priv->dev->stats.tx_packets++;
+
+ tse_free_tx_buffer(priv, tx_buff);
+ priv->tx_cons++;
+
+ txcomplete++;
+ ready--;
+ }
+
+ if (unlikely(netif_queue_stopped(priv->dev) &&
+ tse_tx_avail(priv) > TSE_TX_THRESH(priv))) {
+ netif_tx_lock(priv->dev);
+ if (netif_queue_stopped(priv->dev) &&
+ tse_tx_avail(priv) > TSE_TX_THRESH(priv)) {
+ if (netif_msg_tx_done(priv))
+ netdev_dbg(priv->dev, "%s: restart transmit\n",
+ __func__);
+ netif_wake_queue(priv->dev);
+ }
+ netif_tx_unlock(priv->dev);
+ }
+
+ spin_unlock(&priv->tx_lock);
+ return txcomplete;
+}
+
+/* NAPI polling function
+ */
+static int tse_poll(struct napi_struct *napi, int budget)
+{
+ struct altera_tse_private *priv =
+ container_of(napi, struct altera_tse_private, napi);
+ int rxcomplete = 0;
+ int txcomplete = 0;
+ unsigned long int flags;
+
+ txcomplete = tse_tx_complete(priv);
+
+ rxcomplete = tse_rx(priv, budget);
+
+ if (rxcomplete >= budget || txcomplete > 0)
+ return rxcomplete;
+
+ napi_gro_flush(napi, false);
+ __napi_complete(napi);
+
+ netdev_dbg(priv->dev,
+ "NAPI Complete, did %d packets with budget %d\n",
+ txcomplete+rxcomplete, budget);
+
+ spin_lock_irqsave(&priv->rxdma_irq_lock, flags);
+ priv->dmaops->enable_rxirq(priv);
+ priv->dmaops->enable_txirq(priv);
+ spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
+ return rxcomplete + txcomplete;
+}
+
+/* DMA TX & RX FIFO interrupt routing
+ */
+static irqreturn_t altera_isr(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct altera_tse_private *priv;
+ unsigned long int flags;
+
+
+ if (unlikely(!dev)) {
+ pr_err("%s: invalid dev pointer\n", __func__);
+ return IRQ_NONE;
+ }
+ priv = netdev_priv(dev);
+
+ /* turn off desc irqs and enable napi rx */
+ spin_lock_irqsave(&priv->rxdma_irq_lock, flags);
+
+ if (likely(napi_schedule_prep(&priv->napi))) {
+ priv->dmaops->disable_rxirq(priv);
+ priv->dmaops->disable_txirq(priv);
+ __napi_schedule(&priv->napi);
+ }
+
+ /* reset IRQs */
+ priv->dmaops->clear_rxirq(priv);
+ priv->dmaops->clear_txirq(priv);
+
+ spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/* Transmit a packet (called by the kernel). Dispatches
+ * either the SGDMA method for transmitting or the
+ * MSGDMA method, assumes no scatter/gather support,
+ * implying an assumption that there's only one
+ * physically contiguous fragment starting at
+ * skb->data, for length of skb_headlen(skb).
+ */
+static int tse_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ unsigned int txsize = priv->tx_ring_size;
+ unsigned int entry;
+ struct tse_buffer *buffer = NULL;
+ int nfrags = skb_shinfo(skb)->nr_frags;
+ unsigned int nopaged_len = skb_headlen(skb);
+ enum netdev_tx ret = NETDEV_TX_OK;
+ dma_addr_t dma_addr;
+ int txcomplete = 0;
+
+ spin_lock_bh(&priv->tx_lock);
+
+ if (unlikely(tse_tx_avail(priv) < nfrags + 1)) {
+ if (!netif_queue_stopped(dev)) {
+ netif_stop_queue(dev);
+ /* This is a hard error, log it. */
+ netdev_err(priv->dev,
+ "%s: Tx list full when queue awake\n",
+ __func__);
+ }
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ }
+
+ /* Map the first skb fragment */
+ entry = priv->tx_prod % txsize;
+ buffer = &priv->tx_ring[entry];
+
+ dma_addr = dma_map_single(priv->device, skb->data, nopaged_len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->device, dma_addr)) {
+ netdev_err(priv->dev, "%s: DMA mapping error\n", __func__);
+ ret = NETDEV_TX_OK;
+ goto out;
+ }
+
+ buffer->skb = skb;
+ buffer->dma_addr = dma_addr;
+ buffer->len = nopaged_len;
+
+ /* Push data out of the cache hierarchy into main memory */
+ dma_sync_single_for_device(priv->device, buffer->dma_addr,
+ buffer->len, DMA_TO_DEVICE);
+
+ txcomplete = priv->dmaops->tx_buffer(priv, buffer);
+
+ skb_tx_timestamp(skb);
+
+ priv->tx_prod++;
+ dev->stats.tx_bytes += skb->len;
+
+ if (unlikely(tse_tx_avail(priv) <= TXQUEUESTOP_THRESHHOLD)) {
+ if (netif_msg_hw(priv))
+ netdev_dbg(priv->dev, "%s: stop transmitted packets\n",
+ __func__);
+ netif_stop_queue(dev);
+ }
+
+out:
+ spin_unlock_bh(&priv->tx_lock);
+
+ return ret;
+}
+
+/* Called every time the controller might need to be made
+ * aware of new link state. The PHY code conveys this
+ * information through variables in the phydev structure, and this
+ * function converts those variables into the appropriate
+ * register values, and can bring down the device if needed.
+ */
+static void altera_tse_adjust_link(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+ int new_state = 0;
+
+ /* only change config if there is a link */
+ spin_lock(&priv->mac_cfg_lock);
+ if (phydev->link) {
+ /* Read old config */
+ u32 cfg_reg = ioread32(&priv->mac_dev->command_config);
+
+ /* Check duplex */
+ if (phydev->duplex != priv->oldduplex) {
+ new_state = 1;
+ if (!(phydev->duplex))
+ cfg_reg |= MAC_CMDCFG_HD_ENA;
+ else
+ cfg_reg &= ~MAC_CMDCFG_HD_ENA;
+
+ netdev_dbg(priv->dev, "%s: Link duplex = 0x%x\n",
+ dev->name, phydev->duplex);
+
+ priv->oldduplex = phydev->duplex;
+ }
+
+ /* Check speed */
+ if (phydev->speed != priv->oldspeed) {
+ new_state = 1;
+ switch (phydev->speed) {
+ case 1000:
+ cfg_reg |= MAC_CMDCFG_ETH_SPEED;
+ cfg_reg &= ~MAC_CMDCFG_ENA_10;
+ break;
+ case 100:
+ cfg_reg &= ~MAC_CMDCFG_ETH_SPEED;
+ cfg_reg &= ~MAC_CMDCFG_ENA_10;
+ break;
+ case 10:
+ cfg_reg &= ~MAC_CMDCFG_ETH_SPEED;
+ cfg_reg |= MAC_CMDCFG_ENA_10;
+ break;
+ default:
+ if (netif_msg_link(priv))
+ netdev_warn(dev, "Speed (%d) is not 10/100/1000!\n",
+ phydev->speed);
+ break;
+ }
+ priv->oldspeed = phydev->speed;
+ }
+ iowrite32(cfg_reg, &priv->mac_dev->command_config);
+
+ if (!priv->oldlink) {
+ new_state = 1;
+ priv->oldlink = 1;
+ }
+ } else if (priv->oldlink) {
+ new_state = 1;
+ priv->oldlink = 0;
+ priv->oldspeed = 0;
+ priv->oldduplex = -1;
+ }
+
+ if (new_state && netif_msg_link(priv))
+ phy_print_status(phydev);
+
+ spin_unlock(&priv->mac_cfg_lock);
+}
+static struct phy_device *connect_local_phy(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+ char phy_id_fmt[MII_BUS_ID_SIZE + 3];
+ int ret;
+
+ if (priv->phy_addr != POLL_PHY) {
+ snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT,
+ priv->mdio->id, priv->phy_addr);
+
+ netdev_dbg(dev, "trying to attach to %s\n", phy_id_fmt);
+
+ phydev = phy_connect(dev, phy_id_fmt, &altera_tse_adjust_link,
+ priv->phy_iface);
+ if (IS_ERR(phydev))
+ netdev_err(dev, "Could not attach to PHY\n");
+
+ } else {
+ phydev = phy_find_first(priv->mdio);
+ if (phydev == NULL) {
+ netdev_err(dev, "No PHY found\n");
+ return phydev;
+ }
+
+ ret = phy_connect_direct(dev, phydev, &altera_tse_adjust_link,
+ priv->phy_iface);
+ if (ret != 0) {
+ netdev_err(dev, "Could not attach to PHY\n");
+ phydev = NULL;
+ }
+ }
+ return phydev;
+}
+
+/* Initialize driver's PHY state, and attach to the PHY
+ */
+static int init_phy(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct phy_device *phydev;
+ struct device_node *phynode;
+
+ priv->oldlink = 0;
+ priv->oldspeed = 0;
+ priv->oldduplex = -1;
+
+ phynode = of_parse_phandle(priv->device->of_node, "phy-handle", 0);
+
+ if (!phynode) {
+ netdev_dbg(dev, "no phy-handle found\n");
+ if (!priv->mdio) {
+ netdev_err(dev,
+ "No phy-handle nor local mdio specified\n");
+ return -ENODEV;
+ }
+ phydev = connect_local_phy(dev);
+ } else {
+ netdev_dbg(dev, "phy-handle found\n");
+ phydev = of_phy_connect(dev, phynode,
+ &altera_tse_adjust_link, 0, priv->phy_iface);
+ }
+
+ if (!phydev) {
+ netdev_err(dev, "Could not find the PHY\n");
+ return -ENODEV;
+ }
+
+ /* Stop Advertising 1000BASE Capability if interface is not GMII
+ * Note: Checkpatch throws CHECKs for the camel case defines below,
+ * it's ok to ignore.
+ */
+ if ((priv->phy_iface == PHY_INTERFACE_MODE_MII) ||
+ (priv->phy_iface == PHY_INTERFACE_MODE_RMII))
+ phydev->advertising &= ~(SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full);
+
+ /* Broken HW is sometimes missing the pull-up resistor on the
+ * MDIO line, which results in reads to non-existent devices returning
+ * 0 rather than 0xffff. Catch this here and treat 0 as a non-existent
+ * device as well.
+ * Note: phydev->phy_id is the result of reading the UID PHY registers.
+ */
+ if (phydev->phy_id == 0) {
+ netdev_err(dev, "Bad PHY UID 0x%08x\n", phydev->phy_id);
+ phy_disconnect(phydev);
+ return -ENODEV;
+ }
+
+ netdev_dbg(dev, "attached to PHY %d UID 0x%08x Link = %d\n",
+ phydev->addr, phydev->phy_id, phydev->link);
+
+ priv->phydev = phydev;
+ return 0;
+}
+
+static void tse_update_mac_addr(struct altera_tse_private *priv, u8 *addr)
+{
+ struct altera_tse_mac *mac = priv->mac_dev;
+ u32 msb;
+ u32 lsb;
+
+ msb = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
+ lsb = ((addr[5] << 8) | addr[4]) & 0xffff;
+
+ /* Set primary MAC address */
+ iowrite32(msb, &mac->mac_addr_0);
+ iowrite32(lsb, &mac->mac_addr_1);
+}
+
+/* MAC software reset.
+ * When reset is triggered, the MAC function completes the current
+ * transmission or reception, and subsequently disables the transmit and
+ * receive logic, flushes the receive FIFO buffer, and resets the statistics
+ * counters.
+ */
+static int reset_mac(struct altera_tse_private *priv)
+{
+ void __iomem *cmd_cfg_reg = &priv->mac_dev->command_config;
+ int counter;
+ u32 dat;
+
+ dat = ioread32(cmd_cfg_reg);
+ dat &= ~(MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA);
+ dat |= MAC_CMDCFG_SW_RESET | MAC_CMDCFG_CNT_RESET;
+ iowrite32(dat, cmd_cfg_reg);
+
+ counter = 0;
+ while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
+ if (tse_bit_is_clear(cmd_cfg_reg, MAC_CMDCFG_SW_RESET))
+ break;
+ udelay(1);
+ }
+
+ if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
+ dat = ioread32(cmd_cfg_reg);
+ dat &= ~MAC_CMDCFG_SW_RESET;
+ iowrite32(dat, cmd_cfg_reg);
+ return -1;
+ }
+ return 0;
+}
+
+/* Initialize MAC core registers
+*/
+static int init_mac(struct altera_tse_private *priv)
+{
+ struct altera_tse_mac *mac = priv->mac_dev;
+ unsigned int cmd = 0;
+ u32 frm_length;
+
+ /* Setup Rx FIFO */
+ iowrite32(priv->rx_fifo_depth - ALTERA_TSE_RX_SECTION_EMPTY,
+ &mac->rx_section_empty);
+ iowrite32(ALTERA_TSE_RX_SECTION_FULL, &mac->rx_section_full);
+ iowrite32(ALTERA_TSE_RX_ALMOST_EMPTY, &mac->rx_almost_empty);
+ iowrite32(ALTERA_TSE_RX_ALMOST_FULL, &mac->rx_almost_full);
+
+ /* Setup Tx FIFO */
+ iowrite32(priv->tx_fifo_depth - ALTERA_TSE_TX_SECTION_EMPTY,
+ &mac->tx_section_empty);
+ iowrite32(ALTERA_TSE_TX_SECTION_FULL, &mac->tx_section_full);
+ iowrite32(ALTERA_TSE_TX_ALMOST_EMPTY, &mac->tx_almost_empty);
+ iowrite32(ALTERA_TSE_TX_ALMOST_FULL, &mac->tx_almost_full);
+
+ /* MAC Address Configuration */
+ tse_update_mac_addr(priv, priv->dev->dev_addr);
+
+ /* MAC Function Configuration */
+ frm_length = ETH_HLEN + priv->dev->mtu + ETH_FCS_LEN;
+ iowrite32(frm_length, &mac->frm_length);
+ iowrite32(ALTERA_TSE_TX_IPG_LENGTH, &mac->tx_ipg_length);
+
+ /* Disable RX/TX shift 16 for alignment of all received frames on 16-bit
+ * start address
+ */
+ tse_clear_bit(&mac->rx_cmd_stat, ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16);
+ tse_clear_bit(&mac->tx_cmd_stat, ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 |
+ ALTERA_TSE_TX_CMD_STAT_OMIT_CRC);
+
+ /* Set the MAC options */
+ cmd = ioread32(&mac->command_config);
+ cmd |= MAC_CMDCFG_PAD_EN; /* Padding Removal on Receive */
+ cmd &= ~MAC_CMDCFG_CRC_FWD; /* CRC Removal */
+ cmd |= MAC_CMDCFG_RX_ERR_DISC; /* Automatically discard frames
+ * with CRC errors
+ */
+ cmd |= MAC_CMDCFG_CNTL_FRM_ENA;
+ cmd &= ~MAC_CMDCFG_TX_ENA;
+ cmd &= ~MAC_CMDCFG_RX_ENA;
+ iowrite32(cmd, &mac->command_config);
+
+ if (netif_msg_hw(priv))
+ dev_dbg(priv->device,
+ "MAC post-initialization: CMD_CONFIG = 0x%08x\n", cmd);
+
+ return 0;
+}
+
+/* Start/stop MAC transmission logic
+ */
+static void tse_set_mac(struct altera_tse_private *priv, bool enable)
+{
+ struct altera_tse_mac *mac = priv->mac_dev;
+ u32 value = ioread32(&mac->command_config);
+
+ if (enable)
+ value |= MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA;
+ else
+ value &= ~(MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA);
+
+ iowrite32(value, &mac->command_config);
+}
+
+/* Change the MTU
+ */
+static int tse_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ unsigned int max_mtu = priv->max_mtu;
+ unsigned int min_mtu = ETH_ZLEN + ETH_FCS_LEN;
+
+ if (netif_running(dev)) {
+ netdev_err(dev, "must be stopped to change its MTU\n");
+ return -EBUSY;
+ }
+
+ if ((new_mtu < min_mtu) || (new_mtu > max_mtu)) {
+ netdev_err(dev, "invalid MTU, max MTU is: %u\n", max_mtu);
+ return -EINVAL;
+ }
+
+ dev->mtu = new_mtu;
+ netdev_update_features(dev);
+
+ return 0;
+}
+
+static void altera_tse_set_mcfilter(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct altera_tse_mac *mac = priv->mac_dev;
+ int i;
+ struct netdev_hw_addr *ha;
+
+ /* clear the hash filter */
+ for (i = 0; i < 64; i++)
+ iowrite32(0, &(mac->hash_table[i]));
+
+ netdev_for_each_mc_addr(ha, dev) {
+ unsigned int hash = 0;
+ int mac_octet;
+
+ for (mac_octet = 5; mac_octet >= 0; mac_octet--) {
+ unsigned char xor_bit = 0;
+ unsigned char octet = ha->addr[mac_octet];
+ unsigned int bitshift;
+
+ for (bitshift = 0; bitshift < 8; bitshift++)
+ xor_bit ^= ((octet >> bitshift) & 0x01);
+
+ hash = (hash << 1) | xor_bit;
+ }
+ iowrite32(1, &(mac->hash_table[hash]));
+ }
+}
+
+
+static void altera_tse_set_mcfilterall(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct altera_tse_mac *mac = priv->mac_dev;
+ int i;
+
+ /* set the hash filter */
+ for (i = 0; i < 64; i++)
+ iowrite32(1, &(mac->hash_table[i]));
+}
+
+/* Set or clear the multicast filter for this adaptor
+ */
+static void tse_set_rx_mode_hashfilter(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct altera_tse_mac *mac = priv->mac_dev;
+
+ spin_lock(&priv->mac_cfg_lock);
+
+ if (dev->flags & IFF_PROMISC)
+ tse_set_bit(&mac->command_config, MAC_CMDCFG_PROMIS_EN);
+
+ if (dev->flags & IFF_ALLMULTI)
+ altera_tse_set_mcfilterall(dev);
+ else
+ altera_tse_set_mcfilter(dev);
+
+ spin_unlock(&priv->mac_cfg_lock);
+}
+
+/* Set or clear the multicast filter for this adaptor
+ */
+static void tse_set_rx_mode(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct altera_tse_mac *mac = priv->mac_dev;
+
+ spin_lock(&priv->mac_cfg_lock);
+
+ if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
+ !netdev_mc_empty(dev) || !netdev_uc_empty(dev))
+ tse_set_bit(&mac->command_config, MAC_CMDCFG_PROMIS_EN);
+ else
+ tse_clear_bit(&mac->command_config, MAC_CMDCFG_PROMIS_EN);
+
+ spin_unlock(&priv->mac_cfg_lock);
+}
+
+/* Open and initialize the interface
+ */
+static int tse_open(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ int ret = 0;
+ int i;
+ unsigned long int flags;
+
+ /* Reset and configure TSE MAC and probe associated PHY */
+ ret = priv->dmaops->init_dma(priv);
+ if (ret != 0) {
+ netdev_err(dev, "Cannot initialize DMA\n");
+ goto phy_error;
+ }
+
+ if (netif_msg_ifup(priv))
+ netdev_warn(dev, "device MAC address %pM\n",
+ dev->dev_addr);
+
+ if ((priv->revision < 0xd00) || (priv->revision > 0xe00))
+ netdev_warn(dev, "TSE revision %x\n", priv->revision);
+
+ spin_lock(&priv->mac_cfg_lock);
+ ret = reset_mac(priv);
+ if (ret)
+ netdev_err(dev, "Cannot reset MAC core (error: %d)\n", ret);
+
+ ret = init_mac(priv);
+ spin_unlock(&priv->mac_cfg_lock);
+ if (ret) {
+ netdev_err(dev, "Cannot init MAC core (error: %d)\n", ret);
+ goto alloc_skbuf_error;
+ }
+
+ priv->dmaops->reset_dma(priv);
+
+ /* Create and initialize the TX/RX descriptors chains. */
+ priv->rx_ring_size = dma_rx_num;
+ priv->tx_ring_size = dma_tx_num;
+ ret = alloc_init_skbufs(priv);
+ if (ret) {
+ netdev_err(dev, "DMA descriptors initialization failed\n");
+ goto alloc_skbuf_error;
+ }
+
+
+ /* Register RX interrupt */
+ ret = request_irq(priv->rx_irq, altera_isr, IRQF_SHARED,
+ dev->name, dev);
+ if (ret) {
+ netdev_err(dev, "Unable to register RX interrupt %d\n",
+ priv->rx_irq);
+ goto init_error;
+ }
+
+ /* Register TX interrupt */
+ ret = request_irq(priv->tx_irq, altera_isr, IRQF_SHARED,
+ dev->name, dev);
+ if (ret) {
+ netdev_err(dev, "Unable to register TX interrupt %d\n",
+ priv->tx_irq);
+ goto tx_request_irq_error;
+ }
+
+ /* Enable DMA interrupts */
+ spin_lock_irqsave(&priv->rxdma_irq_lock, flags);
+ priv->dmaops->enable_rxirq(priv);
+ priv->dmaops->enable_txirq(priv);
+
+ /* Setup RX descriptor chain */
+ for (i = 0; i < priv->rx_ring_size; i++)
+ priv->dmaops->add_rx_desc(priv, &priv->rx_ring[i]);
+
+ spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
+
+ /* Start MAC Rx/Tx */
+ spin_lock(&priv->mac_cfg_lock);
+ tse_set_mac(priv, true);
+ spin_unlock(&priv->mac_cfg_lock);
+
+ if (priv->phydev)
+ phy_start(priv->phydev);
+
+ napi_enable(&priv->napi);
+ netif_start_queue(dev);
+
+ return 0;
+
+tx_request_irq_error:
+ free_irq(priv->rx_irq, dev);
+init_error:
+ free_skbufs(dev);
+alloc_skbuf_error:
+ if (priv->phydev) {
+ phy_disconnect(priv->phydev);
+ priv->phydev = NULL;
+ }
+phy_error:
+ return ret;
+}
+
+/* Stop TSE MAC interface and put the device in an inactive state
+ */
+static int tse_shutdown(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ int ret;
+ unsigned long int flags;
+
+ /* Stop and disconnect the PHY */
+ if (priv->phydev) {
+ phy_stop(priv->phydev);
+ phy_disconnect(priv->phydev);
+ priv->phydev = NULL;
+ }
+
+ netif_stop_queue(dev);
+ napi_disable(&priv->napi);
+
+ /* Disable DMA interrupts */
+ spin_lock_irqsave(&priv->rxdma_irq_lock, flags);
+ priv->dmaops->disable_rxirq(priv);
+ priv->dmaops->disable_txirq(priv);
+ spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
+
+ /* Free the IRQ lines */
+ free_irq(priv->rx_irq, dev);
+ free_irq(priv->tx_irq, dev);
+
+ /* disable and reset the MAC, empties fifo */
+ spin_lock(&priv->mac_cfg_lock);
+ spin_lock(&priv->tx_lock);
+
+ ret = reset_mac(priv);
+ if (ret)
+ netdev_err(dev, "Cannot reset MAC core (error: %d)\n", ret);
+ priv->dmaops->reset_dma(priv);
+ free_skbufs(dev);
+
+ spin_unlock(&priv->tx_lock);
+ spin_unlock(&priv->mac_cfg_lock);
+
+ priv->dmaops->uninit_dma(priv);
+
+ return 0;
+}
+
+static struct net_device_ops altera_tse_netdev_ops = {
+ .ndo_open = tse_open,
+ .ndo_stop = tse_shutdown,
+ .ndo_start_xmit = tse_start_xmit,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_rx_mode = tse_set_rx_mode,
+ .ndo_change_mtu = tse_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+
+static int request_and_map(struct platform_device *pdev, const char *name,
+ struct resource **res, void __iomem **ptr)
+{
+ struct resource *region;
+ struct device *device = &pdev->dev;
+
+ *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (*res == NULL) {
+ dev_err(device, "resource %s not defined\n", name);
+ return -ENODEV;
+ }
+
+ region = devm_request_mem_region(device, (*res)->start,
+ resource_size(*res), dev_name(device));
+ if (region == NULL) {
+ dev_err(device, "unable to request %s\n", name);
+ return -EBUSY;
+ }
+
+ *ptr = devm_ioremap_nocache(device, region->start,
+ resource_size(region));
+ if (*ptr == NULL) {
+ dev_err(device, "ioremap_nocache of %s failed!", name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* Probe Altera TSE MAC device
+ */
+static int altera_tse_probe(struct platform_device *pdev)
+{
+ struct net_device *ndev;
+ int ret = -ENODEV;
+ struct resource *control_port;
+ struct resource *dma_res;
+ struct altera_tse_private *priv;
+ const unsigned char *macaddr;
+ struct device_node *np = pdev->dev.of_node;
+ void __iomem *descmap;
+ const struct of_device_id *of_id = NULL;
+
+ ndev = alloc_etherdev(sizeof(struct altera_tse_private));
+ if (!ndev) {
+ dev_err(&pdev->dev, "Could not allocate network device\n");
+ return -ENODEV;
+ }
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ priv = netdev_priv(ndev);
+ priv->device = &pdev->dev;
+ priv->dev = ndev;
+ priv->msg_enable = netif_msg_init(debug, default_msg_level);
+
+ of_id = of_match_device(altera_tse_ids, &pdev->dev);
+
+ if (of_id)
+ priv->dmaops = (struct altera_dmaops *)of_id->data;
+
+
+ if (priv->dmaops &&
+ priv->dmaops->altera_dtype == ALTERA_DTYPE_SGDMA) {
+ /* Get the mapped address to the SGDMA descriptor memory */
+ ret = request_and_map(pdev, "s1", &dma_res, &descmap);
+ if (ret)
+ goto out_free;
+
+ /* Start of that memory is for transmit descriptors */
+ priv->tx_dma_desc = descmap;
+
+ /* First half is for tx descriptors, other half for tx */
+ priv->txdescmem = resource_size(dma_res)/2;
+
+ priv->txdescmem_busaddr = (dma_addr_t)dma_res->start;
+
+ priv->rx_dma_desc = (void __iomem *)((uintptr_t)(descmap +
+ priv->txdescmem));
+ priv->rxdescmem = resource_size(dma_res)/2;
+ priv->rxdescmem_busaddr = dma_res->start;
+ priv->rxdescmem_busaddr += priv->txdescmem;
+
+ if (upper_32_bits(priv->rxdescmem_busaddr)) {
+ dev_dbg(priv->device,
+ "SGDMA bus addresses greater than 32-bits\n");
+ goto out_free;
+ }
+ if (upper_32_bits(priv->txdescmem_busaddr)) {
+ dev_dbg(priv->device,
+ "SGDMA bus addresses greater than 32-bits\n");
+ goto out_free;
+ }
+ } else if (priv->dmaops &&
+ priv->dmaops->altera_dtype == ALTERA_DTYPE_MSGDMA) {
+ ret = request_and_map(pdev, "rx_resp", &dma_res,
+ &priv->rx_dma_resp);
+ if (ret)
+ goto out_free;
+
+ ret = request_and_map(pdev, "tx_desc", &dma_res,
+ &priv->tx_dma_desc);
+ if (ret)
+ goto out_free;
+
+ priv->txdescmem = resource_size(dma_res);
+ priv->txdescmem_busaddr = dma_res->start;
+
+ ret = request_and_map(pdev, "rx_desc", &dma_res,
+ &priv->rx_dma_desc);
+ if (ret)
+ goto out_free;
+
+ priv->rxdescmem = resource_size(dma_res);
+ priv->rxdescmem_busaddr = dma_res->start;
+
+ } else {
+ goto out_free;
+ }
+
+ if (!dma_set_mask(priv->device, DMA_BIT_MASK(priv->dmaops->dmamask)))
+ dma_set_coherent_mask(priv->device,
+ DMA_BIT_MASK(priv->dmaops->dmamask));
+ else if (!dma_set_mask(priv->device, DMA_BIT_MASK(32)))
+ dma_set_coherent_mask(priv->device, DMA_BIT_MASK(32));
+ else
+ goto out_free;
+
+ /* MAC address space */
+ ret = request_and_map(pdev, "control_port", &control_port,
+ (void __iomem **)&priv->mac_dev);
+ if (ret)
+ goto out_free;
+
+ /* xSGDMA Rx Dispatcher address space */
+ ret = request_and_map(pdev, "rx_csr", &dma_res,
+ &priv->rx_dma_csr);
+ if (ret)
+ goto out_free;
+
+
+ /* xSGDMA Tx Dispatcher address space */
+ ret = request_and_map(pdev, "tx_csr", &dma_res,
+ &priv->tx_dma_csr);
+ if (ret)
+ goto out_free;
+
+
+ /* Rx IRQ */
+ priv->rx_irq = platform_get_irq_byname(pdev, "rx_irq");
+ if (priv->rx_irq == -ENXIO) {
+ dev_err(&pdev->dev, "cannot obtain Rx IRQ\n");
+ ret = -ENXIO;
+ goto out_free;
+ }
+
+ /* Tx IRQ */
+ priv->tx_irq = platform_get_irq_byname(pdev, "tx_irq");
+ if (priv->tx_irq == -ENXIO) {
+ dev_err(&pdev->dev, "cannot obtain Tx IRQ\n");
+ ret = -ENXIO;
+ goto out_free;
+ }
+
+ /* get FIFO depths from device tree */
+ if (of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth",
+ &priv->rx_fifo_depth)) {
+ dev_err(&pdev->dev, "cannot obtain rx-fifo-depth\n");
+ ret = -ENXIO;
+ goto out_free;
+ }
+
+ if (of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth",
+ &priv->rx_fifo_depth)) {
+ dev_err(&pdev->dev, "cannot obtain tx-fifo-depth\n");
+ ret = -ENXIO;
+ goto out_free;
+ }
+
+ /* get hash filter settings for this instance */
+ priv->hash_filter =
+ of_property_read_bool(pdev->dev.of_node,
+ "altr,has-hash-multicast-filter");
+
+ /* get supplemental address settings for this instance */
+ priv->added_unicast =
+ of_property_read_bool(pdev->dev.of_node,
+ "altr,has-supplementary-unicast");
+
+ /* Max MTU is 1500, ETH_DATA_LEN */
+ priv->max_mtu = ETH_DATA_LEN;
+
+ /* Get the max mtu from the device tree. Note that the
+ * "max-frame-size" parameter is actually max mtu. Definition
+ * in the ePAPR v1.1 spec and usage differ, so go with usage.
+ */
+ of_property_read_u32(pdev->dev.of_node, "max-frame-size",
+ &priv->max_mtu);
+
+ /* The DMA buffer size already accounts for an alignment bias
+ * to avoid unaligned access exceptions for the NIOS processor,
+ */
+ priv->rx_dma_buf_sz = ALTERA_RXDMABUFFER_SIZE;
+
+ /* get default MAC address from device tree */
+ macaddr = of_get_mac_address(pdev->dev.of_node);
+ if (macaddr)
+ ether_addr_copy(ndev->dev_addr, macaddr);
+ else
+ eth_hw_addr_random(ndev);
+
+ priv->phy_iface = of_get_phy_mode(np);
+
+ /* try to get PHY address from device tree, use PHY autodetection if
+ * no valid address is given
+ */
+ if (of_property_read_u32(pdev->dev.of_node, "phy-addr",
+ &priv->phy_addr)) {
+ priv->phy_addr = POLL_PHY;
+ }
+
+ if (!((priv->phy_addr == POLL_PHY) ||
+ ((priv->phy_addr >= 0) && (priv->phy_addr < PHY_MAX_ADDR)))) {
+ dev_err(&pdev->dev, "invalid phy-addr specified %d\n",
+ priv->phy_addr);
+ goto out_free;
+ }
+
+ /* Create/attach to MDIO bus */
+ ret = altera_tse_mdio_create(ndev,
+ atomic_add_return(1, &instance_count));
+
+ if (ret)
+ goto out_free;
+
+ /* initialize netdev */
+ ether_setup(ndev);
+ ndev->mem_start = control_port->start;
+ ndev->mem_end = control_port->end;
+ ndev->netdev_ops = &altera_tse_netdev_ops;
+ altera_tse_set_ethtool_ops(ndev);
+
+ altera_tse_netdev_ops.ndo_set_rx_mode = tse_set_rx_mode;
+
+ if (priv->hash_filter)
+ altera_tse_netdev_ops.ndo_set_rx_mode =
+ tse_set_rx_mode_hashfilter;
+
+ /* Scatter/gather IO is not supported,
+ * so it is turned off
+ */
+ ndev->hw_features &= ~NETIF_F_SG;
+ ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA;
+
+ /* VLAN offloading of tagging, stripping and filtering is not
+ * supported by hardware, but driver will accommodate the
+ * extra 4-byte VLAN tag for processing by upper layers
+ */
+ ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
+
+ /* setup NAPI interface */
+ netif_napi_add(ndev, &priv->napi, tse_poll, NAPI_POLL_WEIGHT);
+
+ spin_lock_init(&priv->mac_cfg_lock);
+ spin_lock_init(&priv->tx_lock);
+ spin_lock_init(&priv->rxdma_irq_lock);
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register TSE net device\n");
+ goto out_free_mdio;
+ }
+
+ platform_set_drvdata(pdev, ndev);
+
+ priv->revision = ioread32(&priv->mac_dev->megacore_revision);
+
+ if (netif_msg_probe(priv))
+ dev_info(&pdev->dev, "Altera TSE MAC version %d.%d at 0x%08lx irq %d/%d\n",
+ (priv->revision >> 8) & 0xff,
+ priv->revision & 0xff,
+ (unsigned long) control_port->start, priv->rx_irq,
+ priv->tx_irq);
+
+ ret = init_phy(ndev);
+ if (ret != 0) {
+ netdev_err(ndev, "Cannot attach to PHY (error: %d)\n", ret);
+ goto out_free_mdio;
+ }
+ return 0;
+
+out_free_mdio:
+ altera_tse_mdio_destroy(ndev);
+out_free:
+ free_netdev(ndev);
+ return ret;
+}
+
+/* Remove Altera TSE MAC device
+ */
+static int altera_tse_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ altera_tse_mdio_destroy(ndev);
+ unregister_netdev(ndev);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+struct altera_dmaops altera_dtype_sgdma = {
+ .altera_dtype = ALTERA_DTYPE_SGDMA,
+ .dmamask = 32,
+ .reset_dma = sgdma_reset,
+ .enable_txirq = sgdma_enable_txirq,
+ .enable_rxirq = sgdma_enable_rxirq,
+ .disable_txirq = sgdma_disable_txirq,
+ .disable_rxirq = sgdma_disable_rxirq,
+ .clear_txirq = sgdma_clear_txirq,
+ .clear_rxirq = sgdma_clear_rxirq,
+ .tx_buffer = sgdma_tx_buffer,
+ .tx_completions = sgdma_tx_completions,
+ .add_rx_desc = sgdma_add_rx_desc,
+ .get_rx_status = sgdma_rx_status,
+ .init_dma = sgdma_initialize,
+ .uninit_dma = sgdma_uninitialize,
+};
+
+struct altera_dmaops altera_dtype_msgdma = {
+ .altera_dtype = ALTERA_DTYPE_MSGDMA,
+ .dmamask = 64,
+ .reset_dma = msgdma_reset,
+ .enable_txirq = msgdma_enable_txirq,
+ .enable_rxirq = msgdma_enable_rxirq,
+ .disable_txirq = msgdma_disable_txirq,
+ .disable_rxirq = msgdma_disable_rxirq,
+ .clear_txirq = msgdma_clear_txirq,
+ .clear_rxirq = msgdma_clear_rxirq,
+ .tx_buffer = msgdma_tx_buffer,
+ .tx_completions = msgdma_tx_completions,
+ .add_rx_desc = msgdma_add_rx_desc,
+ .get_rx_status = msgdma_rx_status,
+ .init_dma = msgdma_initialize,
+ .uninit_dma = msgdma_uninitialize,
+};
+
+static struct of_device_id altera_tse_ids[] = {
+ { .compatible = "altr,tse-msgdma-1.0", .data = &altera_dtype_msgdma, },
+ { .compatible = "altr,tse-1.0", .data = &altera_dtype_sgdma, },
+ { .compatible = "ALTR,tse-1.0", .data = &altera_dtype_sgdma, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, altera_tse_ids);
+
+static struct platform_driver altera_tse_driver = {
+ .probe = altera_tse_probe,
+ .remove = altera_tse_remove,
+ .suspend = NULL,
+ .resume = NULL,
+ .driver = {
+ .name = ALTERA_TSE_RESOURCE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = altera_tse_ids,
+ },
+};
+
+module_platform_driver(altera_tse_driver);
+
+MODULE_AUTHOR("Altera Corporation");
+MODULE_DESCRIPTION("Altera Triple Speed Ethernet MAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/altera/altera_utils.c b/drivers/net/ethernet/altera/altera_utils.c
new file mode 100644
index 000000000000..70fa13f486b2
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_utils.c
@@ -0,0 +1,44 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. 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 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 "altera_tse.h"
+#include "altera_utils.h"
+
+void tse_set_bit(void __iomem *ioaddr, u32 bit_mask)
+{
+ u32 value = ioread32(ioaddr);
+ value |= bit_mask;
+ iowrite32(value, ioaddr);
+}
+
+void tse_clear_bit(void __iomem *ioaddr, u32 bit_mask)
+{
+ u32 value = ioread32(ioaddr);
+ value &= ~bit_mask;
+ iowrite32(value, ioaddr);
+}
+
+int tse_bit_is_set(void __iomem *ioaddr, u32 bit_mask)
+{
+ u32 value = ioread32(ioaddr);
+ return (value & bit_mask) ? 1 : 0;
+}
+
+int tse_bit_is_clear(void __iomem *ioaddr, u32 bit_mask)
+{
+ u32 value = ioread32(ioaddr);
+ return (value & bit_mask) ? 0 : 1;
+}
diff --git a/drivers/net/ethernet/altera/altera_utils.h b/drivers/net/ethernet/altera/altera_utils.h
new file mode 100644
index 000000000000..ce1db36d3583
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_utils.h
@@ -0,0 +1,27 @@
+/* Altera TSE SGDMA and MSGDMA Linux driver
+ * Copyright (C) 2014 Altera Corporation. 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 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/kernel.h>
+
+#ifndef __ALTERA_UTILS_H__
+#define __ALTERA_UTILS_H__
+
+void tse_set_bit(void __iomem *ioaddr, u32 bit_mask);
+void tse_clear_bit(void __iomem *ioaddr, u32 bit_mask);
+int tse_bit_is_set(void __iomem *ioaddr, u32 bit_mask);
+int tse_bit_is_clear(void __iomem *ioaddr, u32 bit_mask);
+
+#endif /* __ALTERA_UTILS_H__*/
diff --git a/drivers/net/ethernet/amd/7990.c b/drivers/net/ethernet/amd/7990.c
index 18e542f7853d..98a10d555b79 100644
--- a/drivers/net/ethernet/amd/7990.c
+++ b/drivers/net/ethernet/amd/7990.c
@@ -578,7 +578,7 @@ int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
outs++;
/* Kick the lance: transmit now */
WRITERDP(lp, LE_C0_INEA | LE_C0_TDMD);
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
spin_lock_irqsave(&lp->devlock, flags);
if (TX_BUFFS_AVAIL)
diff --git a/drivers/net/ethernet/amd/am79c961a.c b/drivers/net/ethernet/amd/am79c961a.c
index 9793767996a2..87e727b921dc 100644
--- a/drivers/net/ethernet/amd/am79c961a.c
+++ b/drivers/net/ethernet/amd/am79c961a.c
@@ -472,7 +472,7 @@ am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev)
if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN)
netif_stop_queue(dev);
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c
index 2061b471fd16..26efaaa5e73f 100644
--- a/drivers/net/ethernet/amd/amd8111e.c
+++ b/drivers/net/ethernet/amd/amd8111e.c
@@ -720,6 +720,9 @@ static int amd8111e_rx_poll(struct napi_struct *napi, int budget)
int rx_pkt_limit = budget;
unsigned long flags;
+ if (rx_pkt_limit <= 0)
+ goto rx_not_empty;
+
do{
/* process receive packets until we use the quota*/
/* If we own the next entry, it's a new packet. Send it up. */
diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c
index 9339cccfe05a..e7cc9174e364 100644
--- a/drivers/net/ethernet/amd/pcnet32.c
+++ b/drivers/net/ethernet/amd/pcnet32.c
@@ -549,35 +549,35 @@ static void pcnet32_realloc_rx_ring(struct net_device *dev,
struct pcnet32_rx_head *new_rx_ring;
struct sk_buff **new_skb_list;
int new, overlap;
+ unsigned int entries = 1 << size;
new_rx_ring = pci_alloc_consistent(lp->pci_dev,
sizeof(struct pcnet32_rx_head) *
- (1 << size),
+ entries,
&new_ring_dma_addr);
if (new_rx_ring == NULL) {
netif_err(lp, drv, dev, "Consistent memory allocation failed\n");
return;
}
- memset(new_rx_ring, 0, sizeof(struct pcnet32_rx_head) * (1 << size));
+ memset(new_rx_ring, 0, sizeof(struct pcnet32_rx_head) * entries);
- new_dma_addr_list = kcalloc(1 << size, sizeof(dma_addr_t), GFP_ATOMIC);
+ new_dma_addr_list = kcalloc(entries, sizeof(dma_addr_t), GFP_ATOMIC);
if (!new_dma_addr_list)
goto free_new_rx_ring;
- new_skb_list = kcalloc(1 << size, sizeof(struct sk_buff *),
- GFP_ATOMIC);
+ new_skb_list = kcalloc(entries, sizeof(struct sk_buff *), GFP_ATOMIC);
if (!new_skb_list)
goto free_new_lists;
/* first copy the current receive buffers */
- overlap = min(size, lp->rx_ring_size);
+ overlap = min(entries, lp->rx_ring_size);
for (new = 0; new < overlap; new++) {
new_rx_ring[new] = lp->rx_ring[new];
new_dma_addr_list[new] = lp->rx_dma_addr[new];
new_skb_list[new] = lp->rx_skbuff[new];
}
/* now allocate any new buffers needed */
- for (; new < size; new++) {
+ for (; new < entries; new++) {
struct sk_buff *rx_skbuff;
new_skb_list[new] = netdev_alloc_skb(dev, PKT_BUF_SKB);
rx_skbuff = new_skb_list[new];
@@ -592,6 +592,13 @@ static void pcnet32_realloc_rx_ring(struct net_device *dev,
new_dma_addr_list[new] =
pci_map_single(lp->pci_dev, rx_skbuff->data,
PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(lp->pci_dev,
+ new_dma_addr_list[new])) {
+ netif_err(lp, drv, dev, "%s dma mapping failed\n",
+ __func__);
+ dev_kfree_skb(new_skb_list[new]);
+ goto free_all_new;
+ }
new_rx_ring[new].base = cpu_to_le32(new_dma_addr_list[new]);
new_rx_ring[new].buf_length = cpu_to_le16(NEG_BUF_SIZE);
new_rx_ring[new].status = cpu_to_le16(0x8000);
@@ -599,8 +606,12 @@ static void pcnet32_realloc_rx_ring(struct net_device *dev,
/* and free any unneeded buffers */
for (; new < lp->rx_ring_size; new++) {
if (lp->rx_skbuff[new]) {
- pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[new],
- PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ if (!pci_dma_mapping_error(lp->pci_dev,
+ lp->rx_dma_addr[new]))
+ pci_unmap_single(lp->pci_dev,
+ lp->rx_dma_addr[new],
+ PKT_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
dev_kfree_skb(lp->rx_skbuff[new]);
}
}
@@ -612,7 +623,7 @@ static void pcnet32_realloc_rx_ring(struct net_device *dev,
lp->rx_ring_size, lp->rx_ring,
lp->rx_ring_dma_addr);
- lp->rx_ring_size = (1 << size);
+ lp->rx_ring_size = entries;
lp->rx_mod_mask = lp->rx_ring_size - 1;
lp->rx_len_bits = (size << 4);
lp->rx_ring = new_rx_ring;
@@ -624,8 +635,12 @@ static void pcnet32_realloc_rx_ring(struct net_device *dev,
free_all_new:
while (--new >= lp->rx_ring_size) {
if (new_skb_list[new]) {
- pci_unmap_single(lp->pci_dev, new_dma_addr_list[new],
- PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ if (!pci_dma_mapping_error(lp->pci_dev,
+ new_dma_addr_list[new]))
+ pci_unmap_single(lp->pci_dev,
+ new_dma_addr_list[new],
+ PKT_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
dev_kfree_skb(new_skb_list[new]);
}
}
@@ -634,8 +649,7 @@ free_new_lists:
kfree(new_dma_addr_list);
free_new_rx_ring:
pci_free_consistent(lp->pci_dev,
- sizeof(struct pcnet32_rx_head) *
- (1 << size),
+ sizeof(struct pcnet32_rx_head) * entries,
new_rx_ring,
new_ring_dma_addr);
}
@@ -650,8 +664,12 @@ static void pcnet32_purge_rx_ring(struct net_device *dev)
lp->rx_ring[i].status = 0; /* CPU owns buffer */
wmb(); /* Make sure adapter sees owner change */
if (lp->rx_skbuff[i]) {
- pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i],
- PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ if (!pci_dma_mapping_error(lp->pci_dev,
+ lp->rx_dma_addr[i]))
+ pci_unmap_single(lp->pci_dev,
+ lp->rx_dma_addr[i],
+ PKT_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(lp->rx_skbuff[i]);
}
lp->rx_skbuff[i] = NULL;
@@ -930,6 +948,12 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1)
lp->tx_dma_addr[x] =
pci_map_single(lp->pci_dev, skb->data, skb->len,
PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(lp->pci_dev, lp->tx_dma_addr[x])) {
+ netif_printk(lp, hw, KERN_DEBUG, dev,
+ "DMA mapping error at line: %d!\n",
+ __LINE__);
+ goto clean_up;
+ }
lp->tx_ring[x].base = cpu_to_le32(lp->tx_dma_addr[x]);
wmb(); /* Make sure owner changes after all others are visible */
lp->tx_ring[x].status = cpu_to_le16(status);
@@ -1142,24 +1166,36 @@ static void pcnet32_rx_entry(struct net_device *dev,
if (pkt_len > rx_copybreak) {
struct sk_buff *newskb;
+ dma_addr_t new_dma_addr;
newskb = netdev_alloc_skb(dev, PKT_BUF_SKB);
+ /*
+ * map the new buffer, if mapping fails, drop the packet and
+ * reuse the old buffer
+ */
if (newskb) {
skb_reserve(newskb, NET_IP_ALIGN);
- skb = lp->rx_skbuff[entry];
- pci_unmap_single(lp->pci_dev,
- lp->rx_dma_addr[entry],
- PKT_BUF_SIZE,
- PCI_DMA_FROMDEVICE);
- skb_put(skb, pkt_len);
- lp->rx_skbuff[entry] = newskb;
- lp->rx_dma_addr[entry] =
- pci_map_single(lp->pci_dev,
- newskb->data,
- PKT_BUF_SIZE,
- PCI_DMA_FROMDEVICE);
- rxp->base = cpu_to_le32(lp->rx_dma_addr[entry]);
- rx_in_place = 1;
+ new_dma_addr = pci_map_single(lp->pci_dev,
+ newskb->data,
+ PKT_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(lp->pci_dev, new_dma_addr)) {
+ netif_err(lp, rx_err, dev,
+ "DMA mapping error.\n");
+ dev_kfree_skb(newskb);
+ skb = NULL;
+ } else {
+ skb = lp->rx_skbuff[entry];
+ pci_unmap_single(lp->pci_dev,
+ lp->rx_dma_addr[entry],
+ PKT_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+ skb_put(skb, pkt_len);
+ lp->rx_skbuff[entry] = newskb;
+ lp->rx_dma_addr[entry] = new_dma_addr;
+ rxp->base = cpu_to_le32(new_dma_addr);
+ rx_in_place = 1;
+ }
} else
skb = NULL;
} else
@@ -2229,9 +2265,12 @@ static void pcnet32_purge_tx_ring(struct net_device *dev)
lp->tx_ring[i].status = 0; /* CPU owns buffer */
wmb(); /* Make sure adapter sees owner change */
if (lp->tx_skbuff[i]) {
- pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i],
- lp->tx_skbuff[i]->len,
- PCI_DMA_TODEVICE);
+ if (!pci_dma_mapping_error(lp->pci_dev,
+ lp->tx_dma_addr[i]))
+ pci_unmap_single(lp->pci_dev,
+ lp->tx_dma_addr[i],
+ lp->tx_skbuff[i]->len,
+ PCI_DMA_TODEVICE);
dev_kfree_skb_any(lp->tx_skbuff[i]);
}
lp->tx_skbuff[i] = NULL;
@@ -2264,10 +2303,19 @@ static int pcnet32_init_ring(struct net_device *dev)
}
rmb();
- if (lp->rx_dma_addr[i] == 0)
+ if (lp->rx_dma_addr[i] == 0) {
lp->rx_dma_addr[i] =
pci_map_single(lp->pci_dev, rx_skbuff->data,
PKT_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(lp->pci_dev,
+ lp->rx_dma_addr[i])) {
+ /* there is not much we can do at this point */
+ netif_err(lp, drv, dev,
+ "%s pci dma mapping error\n",
+ __func__);
+ return -1;
+ }
+ }
lp->rx_ring[i].base = cpu_to_le32(lp->rx_dma_addr[i]);
lp->rx_ring[i].buf_length = cpu_to_le16(NEG_BUF_SIZE);
wmb(); /* Make sure owner changes after all others are visible */
@@ -2397,9 +2445,14 @@ static netdev_tx_t pcnet32_start_xmit(struct sk_buff *skb,
lp->tx_ring[entry].misc = 0x00000000;
- lp->tx_skbuff[entry] = skb;
lp->tx_dma_addr[entry] =
pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(lp->pci_dev, lp->tx_dma_addr[entry])) {
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
+ goto drop_packet;
+ }
+ lp->tx_skbuff[entry] = skb;
lp->tx_ring[entry].base = cpu_to_le32(lp->tx_dma_addr[entry]);
wmb(); /* Make sure owner changes after all others are visible */
lp->tx_ring[entry].status = cpu_to_le16(status);
@@ -2414,6 +2467,7 @@ static netdev_tx_t pcnet32_start_xmit(struct sk_buff *skb,
lp->tx_full = 1;
netif_stop_queue(dev);
}
+drop_packet:
spin_unlock_irqrestore(&lp->lock, flags);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c
index 380d24922049..17bb9ce96260 100644
--- a/drivers/net/ethernet/atheros/alx/main.c
+++ b/drivers/net/ethernet/atheros/alx/main.c
@@ -535,7 +535,7 @@ static int alx_alloc_descriptors(struct alx_priv *alx)
if (!alx->descmem.virt)
goto out_free;
- alx->txq.tpd = (void *)alx->descmem.virt;
+ alx->txq.tpd = alx->descmem.virt;
alx->txq.tpd_dma = alx->descmem.dma;
/* alignment requirement for next block */
@@ -1097,7 +1097,7 @@ static netdev_tx_t alx_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
drop:
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
index 4d3258dd0a88..e11bf18fbbd1 100644
--- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
@@ -832,7 +832,7 @@ static int atl1c_sw_init(struct atl1c_adapter *adapter)
}
static inline void atl1c_clean_buffer(struct pci_dev *pdev,
- struct atl1c_buffer *buffer_info, int in_irq)
+ struct atl1c_buffer *buffer_info)
{
u16 pci_driection;
if (buffer_info->flags & ATL1C_BUFFER_FREE)
@@ -850,12 +850,8 @@ static inline void atl1c_clean_buffer(struct pci_dev *pdev,
pci_unmap_page(pdev, buffer_info->dma,
buffer_info->length, pci_driection);
}
- if (buffer_info->skb) {
- if (in_irq)
- dev_kfree_skb_irq(buffer_info->skb);
- else
- dev_kfree_skb(buffer_info->skb);
- }
+ if (buffer_info->skb)
+ dev_consume_skb_any(buffer_info->skb);
buffer_info->dma = 0;
buffer_info->skb = NULL;
ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE);
@@ -875,7 +871,7 @@ static void atl1c_clean_tx_ring(struct atl1c_adapter *adapter,
ring_count = tpd_ring->count;
for (index = 0; index < ring_count; index++) {
buffer_info = &tpd_ring->buffer_info[index];
- atl1c_clean_buffer(pdev, buffer_info, 0);
+ atl1c_clean_buffer(pdev, buffer_info);
}
/* Zero out Tx-buffers */
@@ -899,7 +895,7 @@ static void atl1c_clean_rx_ring(struct atl1c_adapter *adapter)
for (j = 0; j < rfd_ring->count; j++) {
buffer_info = &rfd_ring->buffer_info[j];
- atl1c_clean_buffer(pdev, buffer_info, 0);
+ atl1c_clean_buffer(pdev, buffer_info);
}
/* zero out the descriptor ring */
memset(rfd_ring->desc, 0, rfd_ring->size);
@@ -1562,7 +1558,7 @@ static bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter,
while (next_to_clean != hw_next_to_clean) {
buffer_info = &tpd_ring->buffer_info[next_to_clean];
- atl1c_clean_buffer(pdev, buffer_info, 1);
+ atl1c_clean_buffer(pdev, buffer_info);
if (++next_to_clean == tpd_ring->count)
next_to_clean = 0;
atomic_set(&tpd_ring->next_to_clean, next_to_clean);
@@ -1977,17 +1973,17 @@ static int atl1c_tso_csum(struct atl1c_adapter *adapter,
enum atl1c_trans_queue type)
{
struct pci_dev *pdev = adapter->pdev;
+ unsigned short offload_type;
u8 hdr_len;
u32 real_len;
- unsigned short offload_type;
- int err;
if (skb_is_gso(skb)) {
- if (skb_header_cloned(skb)) {
- err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
- if (unlikely(err))
- return -1;
- }
+ int err;
+
+ err = skb_cow_head(skb, 0);
+ if (err < 0)
+ return err;
+
offload_type = skb_shinfo(skb)->gso_type;
if (offload_type & SKB_GSO_TCPV4) {
@@ -2085,7 +2081,7 @@ static void atl1c_tx_rollback(struct atl1c_adapter *adpt,
while (index != tpd_ring->next_to_use) {
tpd = ATL1C_TPD_DESC(tpd_ring, index);
buffer_info = &tpd_ring->buffer_info[index];
- atl1c_clean_buffer(adpt->pdev, buffer_info, 0);
+ atl1c_clean_buffer(adpt->pdev, buffer_info);
memset(tpd, 0, sizeof(struct atl1c_tpd_desc));
if (++index == tpd_ring->count)
index = 0;
@@ -2258,7 +2254,7 @@ static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb,
/* roll back tpd/buffer */
atl1c_tx_rollback(adapter, tpd, type);
spin_unlock_irqrestore(&adapter->tx_lock, flags);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
} else {
atl1c_tx_queue(adapter, skb, tpd, type);
spin_unlock_irqrestore(&adapter->tx_lock, flags);
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
index 422aab27ea1b..4345332533ad 100644
--- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
@@ -1641,17 +1641,17 @@ static u16 atl1e_cal_tdp_req(const struct sk_buff *skb)
static int atl1e_tso_csum(struct atl1e_adapter *adapter,
struct sk_buff *skb, struct atl1e_tpd_desc *tpd)
{
+ unsigned short offload_type;
u8 hdr_len;
u32 real_len;
- unsigned short offload_type;
- int err;
if (skb_is_gso(skb)) {
- if (skb_header_cloned(skb)) {
- err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
- if (unlikely(err))
- return -1;
- }
+ int err;
+
+ err = skb_cow_head(skb, 0);
+ if (err < 0)
+ return err;
+
offload_type = skb_shinfo(skb)->gso_type;
if (offload_type & SKB_GSO_TCPV4) {
diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c
index 287272dd69da..dfd0e91fa726 100644
--- a/drivers/net/ethernet/atheros/atlx/atl1.c
+++ b/drivers/net/ethernet/atheros/atlx/atl1.c
@@ -2118,18 +2118,17 @@ static u16 atl1_tpd_avail(struct atl1_tpd_ring *tpd_ring)
}
static int atl1_tso(struct atl1_adapter *adapter, struct sk_buff *skb,
- struct tx_packet_desc *ptpd)
+ struct tx_packet_desc *ptpd)
{
u8 hdr_len, ip_off;
u32 real_len;
- int err;
if (skb_shinfo(skb)->gso_size) {
- if (skb_header_cloned(skb)) {
- err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
- if (unlikely(err))
- return -1;
- }
+ int err;
+
+ err = skb_cow_head(skb, 0);
+ if (err < 0)
+ return err;
if (skb->protocol == htons(ETH_P_IP)) {
struct iphdr *iph = ip_hdr(skb);
@@ -2175,7 +2174,7 @@ static int atl1_tso(struct atl1_adapter *adapter, struct sk_buff *skb,
return 3;
}
}
- return false;
+ return 0;
}
static int atl1_tx_csum(struct atl1_adapter *adapter, struct sk_buff *skb,
diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c
index 265ce1b752ed..78befb522a52 100644
--- a/drivers/net/ethernet/atheros/atlx/atl2.c
+++ b/drivers/net/ethernet/atheros/atlx/atl2.c
@@ -55,6 +55,7 @@ static const char atl2_driver_name[] = "atl2";
static const char atl2_driver_string[] = "Atheros(R) L2 Ethernet Driver";
static const char atl2_copyright[] = "Copyright (c) 2007 Atheros Corporation.";
static const char atl2_driver_version[] = ATL2_DRV_VERSION;
+static const struct ethtool_ops atl2_ethtool_ops;
MODULE_AUTHOR("Atheros Corporation <xiong.huang@atheros.com>, Chris Snook <csnook@redhat.com>");
MODULE_DESCRIPTION("Atheros Fast Ethernet Network Driver");
@@ -71,8 +72,6 @@ static DEFINE_PCI_DEVICE_TABLE(atl2_pci_tbl) = {
};
MODULE_DEVICE_TABLE(pci, atl2_pci_tbl);
-static void atl2_set_ethtool_ops(struct net_device *netdev);
-
static void atl2_check_options(struct atl2_adapter *adapter);
/**
@@ -1397,7 +1396,7 @@ static int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
atl2_setup_pcicmd(pdev);
netdev->netdev_ops = &atl2_netdev_ops;
- atl2_set_ethtool_ops(netdev);
+ SET_ETHTOOL_OPS(netdev, &atl2_ethtool_ops);
netdev->watchdog_timeo = 5 * HZ;
strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
@@ -2105,11 +2104,6 @@ static const struct ethtool_ops atl2_ethtool_ops = {
.set_eeprom = atl2_set_eeprom,
};
-static void atl2_set_ethtool_ops(struct net_device *netdev)
-{
- SET_ETHTOOL_OPS(netdev, &atl2_ethtool_ops);
-}
-
#define LBYTESWAP(a) ((((a) & 0x00ff00ff) << 8) | \
(((a) & 0xff00ff00) >> 8))
#define LONGSWAP(a) ((LBYTESWAP(a) << 16) | (LBYTESWAP(a) >> 16))
diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig
index 3f97d9fd0a71..85dbddd03722 100644
--- a/drivers/net/ethernet/broadcom/Kconfig
+++ b/drivers/net/ethernet/broadcom/Kconfig
@@ -60,6 +60,17 @@ config BCM63XX_ENET
This driver supports the ethernet MACs in the Broadcom 63xx
MIPS chipset family (BCM63XX).
+config BCMGENET
+ tristate "Broadcom GENET internal MAC support"
+ depends on OF
+ select MII
+ select PHYLIB
+ select FIXED_PHY if BCMGENET=y
+ select BCM7XXX_PHY
+ help
+ This driver supports the built-in Ethernet MACs found in the
+ Broadcom BCM7xxx Set Top Box family chipset.
+
config BNX2
tristate "Broadcom NetXtremeII support"
depends on PCI
diff --git a/drivers/net/ethernet/broadcom/Makefile b/drivers/net/ethernet/broadcom/Makefile
index 68efa1a3fb88..fd639a0d4c7d 100644
--- a/drivers/net/ethernet/broadcom/Makefile
+++ b/drivers/net/ethernet/broadcom/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_B44) += b44.o
obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o
+obj-$(CONFIG_BCMGENET) += genet/
obj-$(CONFIG_BNX2) += bnx2.o
obj-$(CONFIG_CNIC) += cnic.o
obj-$(CONFIG_BNX2X) += bnx2x/
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
index 8a7bf7dad898..05ba62589017 100644
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -1685,7 +1685,7 @@ static struct rtnl_link_stats64 *b44_get_stats64(struct net_device *dev,
unsigned int start;
do {
- start = u64_stats_fetch_begin_bh(&hwstat->syncp);
+ start = u64_stats_fetch_begin_irq(&hwstat->syncp);
/* Convert HW stats into rtnl_link_stats64 stats. */
nstat->rx_packets = hwstat->rx_pkts;
@@ -1719,7 +1719,7 @@ static struct rtnl_link_stats64 *b44_get_stats64(struct net_device *dev,
/* Carrier lost counter seems to be broken for some devices */
nstat->tx_carrier_errors = hwstat->tx_carrier_lost;
#endif
- } while (u64_stats_fetch_retry_bh(&hwstat->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&hwstat->syncp, start));
return nstat;
}
@@ -2073,12 +2073,12 @@ static void b44_get_ethtool_stats(struct net_device *dev,
do {
data_src = &hwstat->tx_good_octets;
data_dst = data;
- start = u64_stats_fetch_begin_bh(&hwstat->syncp);
+ start = u64_stats_fetch_begin_irq(&hwstat->syncp);
for (i = 0; i < ARRAY_SIZE(b44_gstrings); i++)
*data_dst++ = *data_src++;
- } while (u64_stats_fetch_retry_bh(&hwstat->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&hwstat->syncp, start));
}
static void b44_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
index b9a5fb6400d3..a7d11f5565d6 100644
--- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c
+++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
@@ -1722,9 +1722,6 @@ static const struct net_device_ops bcm_enet_ops = {
.ndo_set_rx_mode = bcm_enet_set_multicast_list,
.ndo_do_ioctl = bcm_enet_ioctl,
.ndo_change_mtu = bcm_enet_change_mtu,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = bcm_enet_netpoll,
-#endif
};
/*
diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c
index 6c9e1c9bdeb8..a8efb18e42fa 100644
--- a/drivers/net/ethernet/broadcom/bnx2.c
+++ b/drivers/net/ethernet/broadcom/bnx2.c
@@ -2886,7 +2886,7 @@ bnx2_tx_int(struct bnx2 *bp, struct bnx2_napi *bnapi, int budget)
sw_cons = BNX2_NEXT_TX_BD(sw_cons);
tx_bytes += skb->len;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
tx_pkt++;
if (tx_pkt == budget)
break;
@@ -3133,6 +3133,9 @@ bnx2_rx_int(struct bnx2 *bp, struct bnx2_napi *bnapi, int budget)
struct l2_fhdr *rx_hdr;
int rx_pkt = 0, pg_ring_used = 0;
+ if (budget <= 0)
+ return rx_pkt;
+
hw_cons = bnx2_get_hw_rx_cons(bnapi);
sw_cons = rxr->rx_cons;
sw_prod = rxr->rx_prod;
@@ -6235,7 +6238,7 @@ bnx2_free_irq(struct bnx2 *bp)
static void
bnx2_enable_msix(struct bnx2 *bp, int msix_vecs)
{
- int i, total_vecs, rc;
+ int i, total_vecs;
struct msix_entry msix_ent[BNX2_MAX_MSIX_VEC];
struct net_device *dev = bp->dev;
const int len = sizeof(bp->irq_tbl[0].name);
@@ -6258,16 +6261,9 @@ bnx2_enable_msix(struct bnx2 *bp, int msix_vecs)
#ifdef BCM_CNIC
total_vecs++;
#endif
- rc = -ENOSPC;
- while (total_vecs >= BNX2_MIN_MSIX_VEC) {
- rc = pci_enable_msix(bp->pdev, msix_ent, total_vecs);
- if (rc <= 0)
- break;
- if (rc > 0)
- total_vecs = rc;
- }
-
- if (rc != 0)
+ total_vecs = pci_enable_msix_range(bp->pdev, msix_ent,
+ BNX2_MIN_MSIX_VEC, total_vecs);
+ if (total_vecs < 0)
return;
msix_vecs = total_vecs;
@@ -6640,7 +6636,7 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
mapping = dma_map_single(&bp->pdev->dev, skb->data, len, PCI_DMA_TODEVICE);
if (dma_mapping_error(&bp->pdev->dev, mapping)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -6733,7 +6729,7 @@ dma_error:
PCI_DMA_TODEVICE);
}
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index 391f29ef6d2e..4d8f8aba0ea5 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -26,8 +26,8 @@
* (you will need to reboot afterwards) */
/* #define BNX2X_STOP_ON_ERROR */
-#define DRV_MODULE_VERSION "1.78.17-0"
-#define DRV_MODULE_RELDATE "2013/04/11"
+#define DRV_MODULE_VERSION "1.78.19-0"
+#define DRV_MODULE_RELDATE "2014/02/10"
#define BNX2X_BC_VER 0x040200
#if defined(CONFIG_DCB)
@@ -75,13 +75,22 @@ enum bnx2x_int_mode {
#define BNX2X_MSG_DCB 0x8000000
/* regular debug print */
+#define DP_INNER(fmt, ...) \
+ pr_notice("[%s:%d(%s)]" fmt, \
+ __func__, __LINE__, \
+ bp->dev ? (bp->dev->name) : "?", \
+ ##__VA_ARGS__);
+
#define DP(__mask, fmt, ...) \
do { \
if (unlikely(bp->msg_enable & (__mask))) \
- pr_notice("[%s:%d(%s)]" fmt, \
- __func__, __LINE__, \
- bp->dev ? (bp->dev->name) : "?", \
- ##__VA_ARGS__); \
+ DP_INNER(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define DP_AND(__mask, fmt, ...) \
+do { \
+ if (unlikely((bp->msg_enable & (__mask)) == __mask)) \
+ DP_INNER(fmt, ##__VA_ARGS__); \
} while (0)
#define DP_CONT(__mask, fmt, ...) \
@@ -1146,10 +1155,6 @@ struct bnx2x_port {
(offsetof(struct bnx2x_eth_stats, stat_name) / 4)
/* slow path */
-
-/* slow path work-queue */
-extern struct workqueue_struct *bnx2x_wq;
-
#define BNX2X_MAX_NUM_OF_VFS 64
#define BNX2X_VF_CID_WND 4 /* log num of queues per VF. HW config. */
#define BNX2X_CIDS_PER_VF (1 << BNX2X_VF_CID_WND)
@@ -1261,6 +1266,7 @@ struct bnx2x_slowpath {
union {
struct client_init_ramrod_data init_data;
struct client_update_ramrod_data update_data;
+ struct tpa_update_ramrod_data tpa_data;
} q_rdata;
union {
@@ -1392,7 +1398,7 @@ struct bnx2x_fw_stats_data {
};
/* Public slow path states */
-enum {
+enum sp_rtnl_flag {
BNX2X_SP_RTNL_SETUP_TC,
BNX2X_SP_RTNL_TX_TIMEOUT,
BNX2X_SP_RTNL_FAN_FAILURE,
@@ -1403,6 +1409,12 @@ enum {
BNX2X_SP_RTNL_RX_MODE,
BNX2X_SP_RTNL_HYPERVISOR_VLAN,
BNX2X_SP_RTNL_TX_STOP,
+ BNX2X_SP_RTNL_GET_DRV_VERSION,
+};
+
+enum bnx2x_iov_flag {
+ BNX2X_IOV_HANDLE_VF_MSG,
+ BNX2X_IOV_HANDLE_FLR,
};
struct bnx2x_prev_path_list {
@@ -1603,6 +1615,8 @@ struct bnx2x {
int mrrs;
struct delayed_work sp_task;
+ struct delayed_work iov_task;
+
atomic_t interrupt_occurred;
struct delayed_work sp_rtnl_task;
@@ -1693,6 +1707,10 @@ struct bnx2x {
struct bnx2x_slowpath *slowpath;
dma_addr_t slowpath_mapping;
+ /* Mechanism protecting the drv_info_to_mcp */
+ struct mutex drv_info_mutex;
+ bool drv_info_mng_owner;
+
/* Total number of FW statistics requests */
u8 fw_stats_num;
@@ -1882,6 +1900,9 @@ struct bnx2x {
/* operation indication for the sp_rtnl task */
unsigned long sp_rtnl_state;
+ /* Indication of the IOV tasks */
+ unsigned long iov_task_state;
+
/* DCBX Negotiation results */
struct dcbx_features dcbx_local_feat;
u32 dcbx_error;
@@ -2525,6 +2546,8 @@ enum {
void bnx2x_set_local_cmng(struct bnx2x *bp);
+void bnx2x_update_mng_version(struct bnx2x *bp);
+
#define MCPR_SCRATCH_BASE(bp) \
(CHIP_IS_E1x(bp) ? MCP_REG_MCPR_SCRATCH : MCP_A_REG_MCPR_SCRATCH)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index dbcff509dc3f..9261d5313b5b 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -61,10 +61,14 @@ static void bnx2x_add_all_napi(struct bnx2x *bp)
static int bnx2x_calc_num_queues(struct bnx2x *bp)
{
- return bnx2x_num_queues ?
- min_t(int, bnx2x_num_queues, BNX2X_MAX_QUEUES(bp)) :
- min_t(int, netif_get_num_default_rss_queues(),
- BNX2X_MAX_QUEUES(bp));
+ int nq = bnx2x_num_queues ? : netif_get_num_default_rss_queues();
+
+ /* Reduce memory usage in kdump environment by using only one queue */
+ if (reset_devices)
+ nq = 1;
+
+ nq = clamp(nq, 1, BNX2X_MAX_QUEUES(bp));
+ return nq;
}
/**
@@ -868,6 +872,8 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
if (unlikely(bp->panic))
return 0;
#endif
+ if (budget <= 0)
+ return rx_pkt;
bd_cons = fp->rx_bd_cons;
bd_prod = fp->rx_bd_prod;
@@ -1638,36 +1644,16 @@ int bnx2x_enable_msix(struct bnx2x *bp)
DP(BNX2X_MSG_SP, "about to request enable msix with %d vectors\n",
msix_vec);
- rc = pci_enable_msix(bp->pdev, &bp->msix_table[0], msix_vec);
-
+ rc = pci_enable_msix_range(bp->pdev, &bp->msix_table[0],
+ BNX2X_MIN_MSIX_VEC_CNT(bp), msix_vec);
/*
* reconfigure number of tx/rx queues according to available
* MSI-X vectors
*/
- if (rc >= BNX2X_MIN_MSIX_VEC_CNT(bp)) {
- /* how less vectors we will have? */
- int diff = msix_vec - rc;
-
- BNX2X_DEV_INFO("Trying to use less MSI-X vectors: %d\n", rc);
-
- rc = pci_enable_msix(bp->pdev, &bp->msix_table[0], rc);
-
- if (rc) {
- BNX2X_DEV_INFO("MSI-X is not attainable rc %d\n", rc);
- goto no_msix;
- }
- /*
- * decrease number of queues by number of unallocated entries
- */
- bp->num_ethernet_queues -= diff;
- bp->num_queues = bp->num_ethernet_queues + bp->num_cnic_queues;
-
- BNX2X_DEV_INFO("New queue configuration set: %d\n",
- bp->num_queues);
- } else if (rc > 0) {
+ if (rc == -ENOSPC) {
/* Get by with single vector */
- rc = pci_enable_msix(bp->pdev, &bp->msix_table[0], 1);
- if (rc) {
+ rc = pci_enable_msix_range(bp->pdev, &bp->msix_table[0], 1, 1);
+ if (rc < 0) {
BNX2X_DEV_INFO("Single MSI-X is not attainable rc %d\n",
rc);
goto no_msix;
@@ -1680,8 +1666,22 @@ int bnx2x_enable_msix(struct bnx2x *bp)
bp->num_ethernet_queues = 1;
bp->num_queues = bp->num_ethernet_queues + bp->num_cnic_queues;
} else if (rc < 0) {
- BNX2X_DEV_INFO("MSI-X is not attainable rc %d\n", rc);
+ BNX2X_DEV_INFO("MSI-X is not attainable rc %d\n", rc);
goto no_msix;
+ } else if (rc < msix_vec) {
+ /* how less vectors we will have? */
+ int diff = msix_vec - rc;
+
+ BNX2X_DEV_INFO("Trying to use less MSI-X vectors: %d\n", rc);
+
+ /*
+ * decrease number of queues by number of unallocated entries
+ */
+ bp->num_ethernet_queues -= diff;
+ bp->num_queues = bp->num_ethernet_queues + bp->num_cnic_queues;
+
+ BNX2X_DEV_INFO("New queue configuration set: %d\n",
+ bp->num_queues);
}
bp->flags |= USING_MSIX_FLAG;
@@ -2234,8 +2234,10 @@ static int bnx2x_alloc_fw_stats_mem(struct bnx2x *bp)
sizeof(struct per_queue_stats) * num_queue_stats +
sizeof(struct stats_counter);
- BNX2X_PCI_ALLOC(bp->fw_stats, &bp->fw_stats_mapping,
- bp->fw_stats_data_sz + bp->fw_stats_req_sz);
+ bp->fw_stats = BNX2X_PCI_ALLOC(&bp->fw_stats_mapping,
+ bp->fw_stats_data_sz + bp->fw_stats_req_sz);
+ if (!bp->fw_stats)
+ goto alloc_mem_err;
/* Set shortcuts */
bp->fw_stats_req = (struct bnx2x_fw_stats_req *)bp->fw_stats;
@@ -2802,6 +2804,9 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
if (CNIC_ENABLED(bp))
bnx2x_load_cnic(bp);
+ if (IS_PF(bp))
+ bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_GET_DRV_VERSION, 0);
+
if (IS_PF(bp) && SHMEM2_HAS(bp, drv_capabilities_flag)) {
/* mark driver is loaded in shmem2 */
u32 val;
@@ -3028,6 +3033,10 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link)
bp->state = BNX2X_STATE_CLOSED;
bp->cnic_loaded = false;
+ /* Clear driver version indication in shmem */
+ if (IS_PF(bp))
+ bnx2x_update_mng_version(bp);
+
/* Check if there are pending parity attentions. If there are - set
* RECOVERY_IN_PROGRESS.
*/
@@ -4370,14 +4379,17 @@ static int bnx2x_alloc_fp_mem_at(struct bnx2x *bp, int index)
if (!IS_FCOE_IDX(index)) {
/* status blocks */
- if (!CHIP_IS_E1x(bp))
- BNX2X_PCI_ALLOC(sb->e2_sb,
- &bnx2x_fp(bp, index, status_blk_mapping),
- sizeof(struct host_hc_status_block_e2));
- else
- BNX2X_PCI_ALLOC(sb->e1x_sb,
- &bnx2x_fp(bp, index, status_blk_mapping),
- sizeof(struct host_hc_status_block_e1x));
+ if (!CHIP_IS_E1x(bp)) {
+ sb->e2_sb = BNX2X_PCI_ALLOC(&bnx2x_fp(bp, index, status_blk_mapping),
+ sizeof(struct host_hc_status_block_e2));
+ if (!sb->e2_sb)
+ goto alloc_mem_err;
+ } else {
+ sb->e1x_sb = BNX2X_PCI_ALLOC(&bnx2x_fp(bp, index, status_blk_mapping),
+ sizeof(struct host_hc_status_block_e1x));
+ if (!sb->e1x_sb)
+ goto alloc_mem_err;
+ }
}
/* FCoE Queue uses Default SB and doesn't ACK the SB, thus no need to
@@ -4396,35 +4408,49 @@ static int bnx2x_alloc_fp_mem_at(struct bnx2x *bp, int index)
"allocating tx memory of fp %d cos %d\n",
index, cos);
- BNX2X_ALLOC(txdata->tx_buf_ring,
- sizeof(struct sw_tx_bd) * NUM_TX_BD);
- BNX2X_PCI_ALLOC(txdata->tx_desc_ring,
- &txdata->tx_desc_mapping,
- sizeof(union eth_tx_bd_types) * NUM_TX_BD);
+ txdata->tx_buf_ring = kcalloc(NUM_TX_BD,
+ sizeof(struct sw_tx_bd),
+ GFP_KERNEL);
+ if (!txdata->tx_buf_ring)
+ goto alloc_mem_err;
+ txdata->tx_desc_ring = BNX2X_PCI_ALLOC(&txdata->tx_desc_mapping,
+ sizeof(union eth_tx_bd_types) * NUM_TX_BD);
+ if (!txdata->tx_desc_ring)
+ goto alloc_mem_err;
}
}
/* Rx */
if (!skip_rx_queue(bp, index)) {
/* fastpath rx rings: rx_buf rx_desc rx_comp */
- BNX2X_ALLOC(bnx2x_fp(bp, index, rx_buf_ring),
- sizeof(struct sw_rx_bd) * NUM_RX_BD);
- BNX2X_PCI_ALLOC(bnx2x_fp(bp, index, rx_desc_ring),
- &bnx2x_fp(bp, index, rx_desc_mapping),
- sizeof(struct eth_rx_bd) * NUM_RX_BD);
+ bnx2x_fp(bp, index, rx_buf_ring) =
+ kcalloc(NUM_RX_BD, sizeof(struct sw_rx_bd), GFP_KERNEL);
+ if (!bnx2x_fp(bp, index, rx_buf_ring))
+ goto alloc_mem_err;
+ bnx2x_fp(bp, index, rx_desc_ring) =
+ BNX2X_PCI_ALLOC(&bnx2x_fp(bp, index, rx_desc_mapping),
+ sizeof(struct eth_rx_bd) * NUM_RX_BD);
+ if (!bnx2x_fp(bp, index, rx_desc_ring))
+ goto alloc_mem_err;
/* Seed all CQEs by 1s */
- BNX2X_PCI_FALLOC(bnx2x_fp(bp, index, rx_comp_ring),
- &bnx2x_fp(bp, index, rx_comp_mapping),
- sizeof(struct eth_fast_path_rx_cqe) *
- NUM_RCQ_BD);
+ bnx2x_fp(bp, index, rx_comp_ring) =
+ BNX2X_PCI_FALLOC(&bnx2x_fp(bp, index, rx_comp_mapping),
+ sizeof(struct eth_fast_path_rx_cqe) * NUM_RCQ_BD);
+ if (!bnx2x_fp(bp, index, rx_comp_ring))
+ goto alloc_mem_err;
/* SGE ring */
- BNX2X_ALLOC(bnx2x_fp(bp, index, rx_page_ring),
- sizeof(struct sw_rx_page) * NUM_RX_SGE);
- BNX2X_PCI_ALLOC(bnx2x_fp(bp, index, rx_sge_ring),
- &bnx2x_fp(bp, index, rx_sge_mapping),
- BCM_PAGE_SIZE * NUM_RX_SGE_PAGES);
+ bnx2x_fp(bp, index, rx_page_ring) =
+ kcalloc(NUM_RX_SGE, sizeof(struct sw_rx_page),
+ GFP_KERNEL);
+ if (!bnx2x_fp(bp, index, rx_page_ring))
+ goto alloc_mem_err;
+ bnx2x_fp(bp, index, rx_sge_ring) =
+ BNX2X_PCI_ALLOC(&bnx2x_fp(bp, index, rx_sge_mapping),
+ BCM_PAGE_SIZE * NUM_RX_SGE_PAGES);
+ if (!bnx2x_fp(bp, index, rx_sge_ring))
+ goto alloc_mem_err;
/* RX BD ring */
bnx2x_set_next_page_rx_bd(fp);
@@ -4780,12 +4806,8 @@ void bnx2x_tx_timeout(struct net_device *dev)
bnx2x_panic();
#endif
- smp_mb__before_clear_bit();
- set_bit(BNX2X_SP_RTNL_TX_TIMEOUT, &bp->sp_rtnl_state);
- smp_mb__after_clear_bit();
-
/* This allows the netif to be shutdown gracefully before resetting */
- schedule_delayed_work(&bp->sp_rtnl_task, 0);
+ bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_TX_TIMEOUT, 0);
}
int bnx2x_suspend(struct pci_dev *pdev, pm_message_t state)
@@ -4913,3 +4935,15 @@ void bnx2x_update_coalesce_sb_index(struct bnx2x *bp, u8 fw_sb_id,
disable = disable ? 1 : (usec ? 0 : 1);
storm_memset_hc_disable(bp, port, fw_sb_id, sb_index, disable);
}
+
+void bnx2x_schedule_sp_rtnl(struct bnx2x *bp, enum sp_rtnl_flag flag,
+ u32 verbose)
+{
+ smp_mb__before_clear_bit();
+ set_bit(flag, &bp->sp_rtnl_state);
+ smp_mb__after_clear_bit();
+ DP((BNX2X_MSG_SP | verbose), "Scheduling sp_rtnl task [Flag: %d]\n",
+ flag);
+ schedule_delayed_work(&bp->sp_rtnl_task, 0);
+}
+EXPORT_SYMBOL(bnx2x_schedule_sp_rtnl);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
index a89a40f88c25..05f4f5f52635 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
@@ -47,31 +47,26 @@ extern int bnx2x_num_queues;
} \
} while (0)
-#define BNX2X_PCI_ALLOC(x, y, size) \
- do { \
- x = dma_zalloc_coherent(&bp->pdev->dev, size, y, GFP_KERNEL); \
- if (x == NULL) \
- goto alloc_mem_err; \
- DP(NETIF_MSG_HW, "BNX2X_PCI_ALLOC: Physical %Lx Virtual %p\n", \
- (unsigned long long)(*y), x); \
- } while (0)
-
-#define BNX2X_PCI_FALLOC(x, y, size) \
- do { \
- x = dma_alloc_coherent(&bp->pdev->dev, size, y, GFP_KERNEL); \
- if (x == NULL) \
- goto alloc_mem_err; \
- memset((void *)x, 0xFFFFFFFF, size); \
- DP(NETIF_MSG_HW, "BNX2X_PCI_FALLOC: Physical %Lx Virtual %p\n",\
- (unsigned long long)(*y), x); \
- } while (0)
-
-#define BNX2X_ALLOC(x, size) \
- do { \
- x = kzalloc(size, GFP_KERNEL); \
- if (x == NULL) \
- goto alloc_mem_err; \
- } while (0)
+#define BNX2X_PCI_ALLOC(y, size) \
+({ \
+ void *x = dma_zalloc_coherent(&bp->pdev->dev, size, y, GFP_KERNEL); \
+ if (x) \
+ DP(NETIF_MSG_HW, \
+ "BNX2X_PCI_ALLOC: Physical %Lx Virtual %p\n", \
+ (unsigned long long)(*y), x); \
+ x; \
+})
+#define BNX2X_PCI_FALLOC(y, size) \
+({ \
+ void *x = dma_alloc_coherent(&bp->pdev->dev, size, y, GFP_KERNEL); \
+ if (x) { \
+ memset(x, 0xff, size); \
+ DP(NETIF_MSG_HW, \
+ "BNX2X_PCI_FALLOC: Physical %Lx Virtual %p\n", \
+ (unsigned long long)(*y), x); \
+ } \
+ x; \
+})
/*********************** Interfaces ****************************
* Functions that need to be implemented by each driver version
@@ -1324,4 +1319,7 @@ void bnx2x_fill_fw_str(struct bnx2x *bp, char *buf, size_t buf_len);
int bnx2x_drain_tx_queues(struct bnx2x *bp);
void bnx2x_squeeze_objects(struct bnx2x *bp);
+void bnx2x_schedule_sp_rtnl(struct bnx2x*, enum sp_rtnl_flag,
+ u32 verbose);
+
#endif /* BNX2X_CMN_H */
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c
index fdace204b054..97ea5421dd96 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c
@@ -710,8 +710,7 @@ static inline void bnx2x_dcbx_update_tc_mapping(struct bnx2x *bp)
* as we are handling an attention on a work queue which must be
* flushed at some rtnl-locked contexts (e.g. if down)
*/
- if (!test_and_set_bit(BNX2X_SP_RTNL_SETUP_TC, &bp->sp_rtnl_state))
- schedule_delayed_work(&bp->sp_rtnl_task, 0);
+ bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_SETUP_TC, 0);
}
void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state)
@@ -764,10 +763,7 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state)
if (IS_MF(bp))
bnx2x_link_sync_notify(bp);
- set_bit(BNX2X_SP_RTNL_TX_STOP, &bp->sp_rtnl_state);
-
- schedule_delayed_work(&bp->sp_rtnl_task, 0);
-
+ bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_TX_STOP, 0);
return;
}
case BNX2X_DCBX_STATE_TX_PAUSED:
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
index 38fc794c1655..b6de05e3149b 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
@@ -2969,8 +2969,9 @@ 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 IS_MF_MODE_STAT(bp) \
- (IS_MF(bp) && !(bp->msg_enable & BNX2X_MSG_STATS))
+#define HIDE_PORT_STAT(bp) \
+ ((IS_MF(bp) && !(bp->msg_enable & BNX2X_MSG_STATS)) || \
+ IS_VF(bp))
/* ethtool statistics are displayed for all regular ethernet queues and the
* fcoe L2 queue if not disabled
@@ -2992,7 +2993,7 @@ static int bnx2x_get_sset_count(struct net_device *dev, int stringset)
BNX2X_NUM_Q_STATS;
} else
num_strings = 0;
- if (IS_MF_MODE_STAT(bp)) {
+ if (HIDE_PORT_STAT(bp)) {
for (i = 0; i < BNX2X_NUM_STATS; i++)
if (IS_FUNC_STAT(i))
num_strings++;
@@ -3047,7 +3048,7 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
}
for (i = 0, j = 0; i < BNX2X_NUM_STATS; i++) {
- if (IS_MF_MODE_STAT(bp) && IS_PORT_STAT(i))
+ if (HIDE_PORT_STAT(bp) && IS_PORT_STAT(i))
continue;
strcpy(buf + (k + j)*ETH_GSTRING_LEN,
bnx2x_stats_arr[i].string);
@@ -3105,7 +3106,7 @@ static void bnx2x_get_ethtool_stats(struct net_device *dev,
hw_stats = (u32 *)&bp->eth_stats;
for (i = 0, j = 0; i < BNX2X_NUM_STATS; i++) {
- if (IS_MF_MODE_STAT(bp) && IS_PORT_STAT(i))
+ if (HIDE_PORT_STAT(bp) && IS_PORT_STAT(i))
continue;
if (bnx2x_stats_arr[i].size == 0) {
/* skip this counter */
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h
index 84aecdf06f7a..95dc36543548 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h
@@ -87,7 +87,6 @@
(IRO[156].base + ((vfId) * IRO[156].m1))
#define CSTORM_VF_TO_PF_OFFSET(funcId) \
(IRO[150].base + ((funcId) * IRO[150].m1))
-#define TSTORM_ACCEPT_CLASSIFY_FAILED_OFFSET (IRO[204].base)
#define TSTORM_APPROXIMATE_MATCH_MULTICAST_FILTERING_OFFSET(pfId) \
(IRO[203].base + ((pfId) * IRO[203].m1))
#define TSTORM_ASSERT_LIST_INDEX_OFFSET (IRO[102].base)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
index cf1df8b62e2c..5ba8af50c84f 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
@@ -2003,6 +2003,23 @@ struct shmem_lfa {
#define SHMEM_LFA_DONT_CLEAR_STAT (1<<24)
};
+/* Used to support NSCI get OS driver version
+ * on driver load the version value will be set
+ * on driver unload driver value of 0x0 will be set.
+ */
+struct os_drv_ver {
+#define DRV_VER_NOT_LOADED 0
+
+ /* personalties order is important */
+#define DRV_PERS_ETHERNET 0
+#define DRV_PERS_ISCSI 1
+#define DRV_PERS_FCOE 2
+
+ /* shmem2 struct is constant can't add more personalties here */
+#define MAX_DRV_PERS 3
+ u32 versions[MAX_DRV_PERS];
+};
+
struct ncsi_oem_fcoe_features {
u32 fcoe_features1;
#define FCOE_FEATURES1_IOS_PER_CONNECTION_MASK 0x0000FFFF
@@ -2217,6 +2234,18 @@ struct shmem2_region {
u32 reserved4; /* Offset 0x150 */
u32 link_attr_sync[PORT_MAX]; /* Offset 0x154 */
#define LINK_ATTR_SYNC_KR2_ENABLE (1<<0)
+
+ u32 reserved5[2];
+ u32 reserved6[PORT_MAX];
+
+ /* driver version for each personality */
+ struct os_drv_ver func_os_drv_ver[E2_FUNC_MAX]; /* Offset 0x16c */
+
+ /* Flag to the driver that PF's drv_info_host_addr buffer was read */
+ u32 mfw_drv_indication;
+
+ /* We use indication for each PF (0..3) */
+#define MFW_DRV_IND_READ_DONE_OFFSET(_pf_) (1 << (_pf_))
};
@@ -2848,7 +2877,7 @@ struct afex_stats {
#define BCM_5710_FW_MAJOR_VERSION 7
#define BCM_5710_FW_MINOR_VERSION 8
-#define BCM_5710_FW_REVISION_VERSION 17
+#define BCM_5710_FW_REVISION_VERSION 19
#define BCM_5710_FW_ENGINEERING_VERSION 0
#define BCM_5710_FW_COMPILE_FLAGS 1
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 7d4382286457..a78edaccceee 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -120,7 +120,8 @@ static int debug;
module_param(debug, int, S_IRUGO);
MODULE_PARM_DESC(debug, " Default debug msglevel");
-struct workqueue_struct *bnx2x_wq;
+static struct workqueue_struct *bnx2x_wq;
+struct workqueue_struct *bnx2x_iov_wq;
struct bnx2x_mac_vals {
u32 xmac_addr;
@@ -918,7 +919,7 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int)
u16 start = 0, end = 0;
u8 cos;
#endif
- if (disable_int)
+ if (IS_PF(bp) && disable_int)
bnx2x_int_disable(bp);
bp->stats_state = STATS_STATE_DISABLED;
@@ -929,33 +930,41 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int)
/* Indices */
/* Common */
- BNX2X_ERR("def_idx(0x%x) def_att_idx(0x%x) attn_state(0x%x) spq_prod_idx(0x%x) next_stats_cnt(0x%x)\n",
- bp->def_idx, bp->def_att_idx, bp->attn_state,
- bp->spq_prod_idx, bp->stats_counter);
- BNX2X_ERR("DSB: attn bits(0x%x) ack(0x%x) id(0x%x) idx(0x%x)\n",
- bp->def_status_blk->atten_status_block.attn_bits,
- bp->def_status_blk->atten_status_block.attn_bits_ack,
- bp->def_status_blk->atten_status_block.status_block_id,
- bp->def_status_blk->atten_status_block.attn_bits_index);
- BNX2X_ERR(" def (");
- for (i = 0; i < HC_SP_SB_MAX_INDICES; i++)
- pr_cont("0x%x%s",
- bp->def_status_blk->sp_sb.index_values[i],
- (i == HC_SP_SB_MAX_INDICES - 1) ? ") " : " ");
-
- for (i = 0; i < sizeof(struct hc_sp_status_block_data)/sizeof(u32); i++)
- *((u32 *)&sp_sb_data + i) = REG_RD(bp, BAR_CSTRORM_INTMEM +
- CSTORM_SP_STATUS_BLOCK_DATA_OFFSET(func) +
- i*sizeof(u32));
-
- pr_cont("igu_sb_id(0x%x) igu_seg_id(0x%x) pf_id(0x%x) vnic_id(0x%x) vf_id(0x%x) vf_valid (0x%x) state(0x%x)\n",
- sp_sb_data.igu_sb_id,
- sp_sb_data.igu_seg_id,
- sp_sb_data.p_func.pf_id,
- sp_sb_data.p_func.vnic_id,
- sp_sb_data.p_func.vf_id,
- sp_sb_data.p_func.vf_valid,
- sp_sb_data.state);
+ if (IS_PF(bp)) {
+ struct host_sp_status_block *def_sb = bp->def_status_blk;
+ int data_size, cstorm_offset;
+
+ BNX2X_ERR("def_idx(0x%x) def_att_idx(0x%x) attn_state(0x%x) spq_prod_idx(0x%x) next_stats_cnt(0x%x)\n",
+ bp->def_idx, bp->def_att_idx, bp->attn_state,
+ bp->spq_prod_idx, bp->stats_counter);
+ BNX2X_ERR("DSB: attn bits(0x%x) ack(0x%x) id(0x%x) idx(0x%x)\n",
+ def_sb->atten_status_block.attn_bits,
+ def_sb->atten_status_block.attn_bits_ack,
+ def_sb->atten_status_block.status_block_id,
+ def_sb->atten_status_block.attn_bits_index);
+ BNX2X_ERR(" def (");
+ for (i = 0; i < HC_SP_SB_MAX_INDICES; i++)
+ pr_cont("0x%x%s",
+ def_sb->sp_sb.index_values[i],
+ (i == HC_SP_SB_MAX_INDICES - 1) ? ") " : " ");
+
+ data_size = sizeof(struct hc_sp_status_block_data) /
+ sizeof(u32);
+ cstorm_offset = CSTORM_SP_STATUS_BLOCK_DATA_OFFSET(func);
+ for (i = 0; i < data_size; i++)
+ *((u32 *)&sp_sb_data + i) =
+ REG_RD(bp, BAR_CSTRORM_INTMEM + cstorm_offset +
+ i * sizeof(u32));
+
+ pr_cont("igu_sb_id(0x%x) igu_seg_id(0x%x) pf_id(0x%x) vnic_id(0x%x) vf_id(0x%x) vf_valid (0x%x) state(0x%x)\n",
+ sp_sb_data.igu_sb_id,
+ sp_sb_data.igu_seg_id,
+ sp_sb_data.p_func.pf_id,
+ sp_sb_data.p_func.vnic_id,
+ sp_sb_data.p_func.vf_id,
+ sp_sb_data.p_func.vf_valid,
+ sp_sb_data.state);
+ }
for_each_eth_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
@@ -1013,6 +1022,11 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int)
pr_cont("0x%x%s",
fp->sb_index_values[j],
(j == loop - 1) ? ")" : " ");
+
+ /* VF cannot access FW refelection for status block */
+ if (IS_VF(bp))
+ continue;
+
/* fw sb data */
data_size = CHIP_IS_E1x(bp) ?
sizeof(struct hc_status_block_data_e1x) :
@@ -1064,16 +1078,18 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int)
}
#ifdef BNX2X_STOP_ON_ERROR
-
- /* event queue */
- BNX2X_ERR("eq cons %x prod %x\n", bp->eq_cons, bp->eq_prod);
- for (i = 0; i < NUM_EQ_DESC; i++) {
- u32 *data = (u32 *)&bp->eq_ring[i].message.data;
-
- BNX2X_ERR("event queue [%d]: header: opcode %d, error %d\n",
- i, bp->eq_ring[i].message.opcode,
- bp->eq_ring[i].message.error);
- BNX2X_ERR("data: %x %x %x\n", data[0], data[1], data[2]);
+ if (IS_PF(bp)) {
+ /* event queue */
+ BNX2X_ERR("eq cons %x prod %x\n", bp->eq_cons, bp->eq_prod);
+ for (i = 0; i < NUM_EQ_DESC; i++) {
+ u32 *data = (u32 *)&bp->eq_ring[i].message.data;
+
+ BNX2X_ERR("event queue [%d]: header: opcode %d, error %d\n",
+ i, bp->eq_ring[i].message.opcode,
+ bp->eq_ring[i].message.error);
+ BNX2X_ERR("data: %x %x %x\n",
+ data[0], data[1], data[2]);
+ }
}
/* Rings */
@@ -1140,8 +1156,10 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int)
}
}
#endif
- bnx2x_fw_dump(bp);
- bnx2x_mc_assert(bp);
+ if (IS_PF(bp)) {
+ bnx2x_fw_dump(bp);
+ bnx2x_mc_assert(bp);
+ }
BNX2X_ERR("end crash dump -----------------\n");
}
@@ -1814,6 +1832,11 @@ void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe)
drv_cmd = BNX2X_Q_CMD_EMPTY;
break;
+ case (RAMROD_CMD_ID_ETH_TPA_UPDATE):
+ DP(BNX2X_MSG_SP, "got tpa update ramrod CID=%d\n", cid);
+ drv_cmd = BNX2X_Q_CMD_UPDATE_TPA;
+ break;
+
default:
BNX2X_ERR("unexpected MC reply (%d) on fp[%d]\n",
command, fp->index);
@@ -1834,8 +1857,6 @@ void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe)
#else
return;
#endif
- /* SRIOV: reschedule any 'in_progress' operations */
- bnx2x_iov_sp_event(bp, cid, true);
smp_mb__before_atomic_inc();
atomic_inc(&bp->cq_spq_left);
@@ -3460,10 +3481,15 @@ static void bnx2x_handle_eee_event(struct bnx2x *bp)
bnx2x_fw_command(bp, DRV_MSG_CODE_EEE_RESULTS_ACK, 0);
}
+#define BNX2X_UPDATE_DRV_INFO_IND_LENGTH (20)
+#define BNX2X_UPDATE_DRV_INFO_IND_COUNT (25)
+
static void bnx2x_handle_drv_info_req(struct bnx2x *bp)
{
enum drv_info_opcode op_code;
u32 drv_info_ctl = SHMEM2_RD(bp, drv_info_control);
+ bool release = false;
+ int wait;
/* if drv_info version supported by MFW doesn't match - send NACK */
if ((drv_info_ctl & DRV_INFO_CONTROL_VER_MASK) != DRV_INFO_CUR_VER) {
@@ -3474,6 +3500,9 @@ static void bnx2x_handle_drv_info_req(struct bnx2x *bp)
op_code = (drv_info_ctl & DRV_INFO_CONTROL_OP_CODE_MASK) >>
DRV_INFO_CONTROL_OP_CODE_SHIFT;
+ /* Must prevent other flows from accessing drv_info_to_mcp */
+ mutex_lock(&bp->drv_info_mutex);
+
memset(&bp->slowpath->drv_info_to_mcp, 0,
sizeof(union drv_info_to_mcp));
@@ -3490,7 +3519,7 @@ static void bnx2x_handle_drv_info_req(struct bnx2x *bp)
default:
/* if op code isn't supported - send NACK */
bnx2x_fw_command(bp, DRV_MSG_CODE_DRV_INFO_NACK, 0);
- return;
+ goto out;
}
/* if we got drv_info attn from MFW then these fields are defined in
@@ -3502,6 +3531,106 @@ static void bnx2x_handle_drv_info_req(struct bnx2x *bp)
U64_HI(bnx2x_sp_mapping(bp, drv_info_to_mcp)));
bnx2x_fw_command(bp, DRV_MSG_CODE_DRV_INFO_ACK, 0);
+
+ /* Since possible management wants both this and get_driver_version
+ * need to wait until management notifies us it finished utilizing
+ * the buffer.
+ */
+ if (!SHMEM2_HAS(bp, mfw_drv_indication)) {
+ DP(BNX2X_MSG_MCP, "Management does not support indication\n");
+ } else if (!bp->drv_info_mng_owner) {
+ u32 bit = MFW_DRV_IND_READ_DONE_OFFSET((BP_ABS_FUNC(bp) >> 1));
+
+ for (wait = 0; wait < BNX2X_UPDATE_DRV_INFO_IND_COUNT; wait++) {
+ u32 indication = SHMEM2_RD(bp, mfw_drv_indication);
+
+ /* Management is done; need to clear indication */
+ if (indication & bit) {
+ SHMEM2_WR(bp, mfw_drv_indication,
+ indication & ~bit);
+ release = true;
+ break;
+ }
+
+ msleep(BNX2X_UPDATE_DRV_INFO_IND_LENGTH);
+ }
+ }
+ if (!release) {
+ DP(BNX2X_MSG_MCP, "Management did not release indication\n");
+ bp->drv_info_mng_owner = true;
+ }
+
+out:
+ mutex_unlock(&bp->drv_info_mutex);
+}
+
+static u32 bnx2x_update_mng_version_utility(u8 *version, bool bnx2x_format)
+{
+ u8 vals[4];
+ int i = 0;
+
+ if (bnx2x_format) {
+ i = sscanf(version, "1.%c%hhd.%hhd.%hhd",
+ &vals[0], &vals[1], &vals[2], &vals[3]);
+ if (i > 0)
+ vals[0] -= '0';
+ } else {
+ i = sscanf(version, "%hhd.%hhd.%hhd.%hhd",
+ &vals[0], &vals[1], &vals[2], &vals[3]);
+ }
+
+ while (i < 4)
+ vals[i++] = 0;
+
+ return (vals[0] << 24) | (vals[1] << 16) | (vals[2] << 8) | vals[3];
+}
+
+void bnx2x_update_mng_version(struct bnx2x *bp)
+{
+ u32 iscsiver = DRV_VER_NOT_LOADED;
+ u32 fcoever = DRV_VER_NOT_LOADED;
+ u32 ethver = DRV_VER_NOT_LOADED;
+ int idx = BP_FW_MB_IDX(bp);
+ u8 *version;
+
+ if (!SHMEM2_HAS(bp, func_os_drv_ver))
+ return;
+
+ mutex_lock(&bp->drv_info_mutex);
+ /* Must not proceed when `bnx2x_handle_drv_info_req' is feasible */
+ if (bp->drv_info_mng_owner)
+ goto out;
+
+ if (bp->state != BNX2X_STATE_OPEN)
+ goto out;
+
+ /* Parse ethernet driver version */
+ ethver = bnx2x_update_mng_version_utility(DRV_MODULE_VERSION, true);
+ if (!CNIC_LOADED(bp))
+ goto out;
+
+ /* Try getting storage driver version via cnic */
+ memset(&bp->slowpath->drv_info_to_mcp, 0,
+ sizeof(union drv_info_to_mcp));
+ bnx2x_drv_info_iscsi_stat(bp);
+ version = bp->slowpath->drv_info_to_mcp.iscsi_stat.version;
+ iscsiver = bnx2x_update_mng_version_utility(version, false);
+
+ memset(&bp->slowpath->drv_info_to_mcp, 0,
+ sizeof(union drv_info_to_mcp));
+ bnx2x_drv_info_fcoe_stat(bp);
+ version = bp->slowpath->drv_info_to_mcp.fcoe_stat.version;
+ fcoever = bnx2x_update_mng_version_utility(version, false);
+
+out:
+ SHMEM2_WR(bp, func_os_drv_ver[idx].versions[DRV_PERS_ETHERNET], ethver);
+ SHMEM2_WR(bp, func_os_drv_ver[idx].versions[DRV_PERS_ISCSI], iscsiver);
+ SHMEM2_WR(bp, func_os_drv_ver[idx].versions[DRV_PERS_FCOE], fcoever);
+
+ mutex_unlock(&bp->drv_info_mutex);
+
+ DP(BNX2X_MSG_MCP, "Setting driver version: ETH [%08x] iSCSI [%08x] FCoE [%08x]\n",
+ ethver, iscsiver, fcoever);
}
static void bnx2x_dcc_event(struct bnx2x *bp, u32 dcc_event)
@@ -3644,10 +3773,18 @@ int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
cpu_to_le32((command << SPE_HDR_CMD_ID_SHIFT) |
HW_CID(bp, cid));
- type = (cmd_type << SPE_HDR_CONN_TYPE_SHIFT) & SPE_HDR_CONN_TYPE;
-
- type |= ((BP_FUNC(bp) << SPE_HDR_FUNCTION_ID_SHIFT) &
- SPE_HDR_FUNCTION_ID);
+ /* In some cases, type may already contain the func-id
+ * mainly in SRIOV related use cases, so we add it here only
+ * if it's not already set.
+ */
+ if (!(cmd_type & SPE_HDR_FUNCTION_ID)) {
+ type = (cmd_type << SPE_HDR_CONN_TYPE_SHIFT) &
+ SPE_HDR_CONN_TYPE;
+ type |= ((BP_FUNC(bp) << SPE_HDR_FUNCTION_ID_SHIFT) &
+ SPE_HDR_FUNCTION_ID);
+ } else {
+ type = cmd_type;
+ }
spe->hdr.type = cpu_to_le16(type);
@@ -3878,10 +4015,7 @@ static void bnx2x_fan_failure(struct bnx2x *bp)
* This is due to some boards consuming sufficient power when driver is
* up to overheat if fan fails.
*/
- smp_mb__before_clear_bit();
- set_bit(BNX2X_SP_RTNL_FAN_FAILURE, &bp->sp_rtnl_state);
- smp_mb__after_clear_bit();
- schedule_delayed_work(&bp->sp_rtnl_task, 0);
+ bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_FAN_FAILURE, 0);
}
static void bnx2x_attn_int_deasserted0(struct bnx2x *bp, u32 attn)
@@ -4025,7 +4159,8 @@ static void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn)
bnx2x_handle_drv_info_req(bp);
if (val & DRV_STATUS_VF_DISABLED)
- bnx2x_vf_handle_flr_event(bp);
+ bnx2x_schedule_iov_task(bp,
+ BNX2X_IOV_HANDLE_FLR);
if ((bp->port.pmf == 0) && (val & DRV_STATUS_PMF))
bnx2x_pmf_update(bp);
@@ -5216,14 +5351,14 @@ static void bnx2x_eq_int(struct bnx2x *bp)
/* handle eq element */
switch (opcode) {
case EVENT_RING_OPCODE_VF_PF_CHANNEL:
- DP(BNX2X_MSG_IOV, "vf pf channel element on eq\n");
- bnx2x_vf_mbx(bp, &elem->message.data.vf_pf_event);
+ bnx2x_vf_mbx_schedule(bp,
+ &elem->message.data.vf_pf_event);
continue;
case EVENT_RING_OPCODE_STAT_QUERY:
- DP(BNX2X_MSG_SP | BNX2X_MSG_STATS,
- "got statistics comp event %d\n",
- bp->stats_comp++);
+ DP_AND((BNX2X_MSG_SP | BNX2X_MSG_STATS),
+ "got statistics comp event %d\n",
+ bp->stats_comp++);
/* nothing to do with stats comp */
goto next_spqe;
@@ -5273,6 +5408,8 @@ static void bnx2x_eq_int(struct bnx2x *bp)
break;
} else {
+ int cmd = BNX2X_SP_RTNL_AFEX_F_UPDATE;
+
DP(BNX2X_MSG_SP | BNX2X_MSG_MCP,
"AFEX: ramrod completed FUNCTION_UPDATE\n");
f_obj->complete_cmd(bp, f_obj,
@@ -5282,12 +5419,7 @@ static void bnx2x_eq_int(struct bnx2x *bp)
* sp_rtnl task as all Queue SP operations
* should run under rtnl_lock.
*/
- smp_mb__before_clear_bit();
- set_bit(BNX2X_SP_RTNL_AFEX_F_UPDATE,
- &bp->sp_rtnl_state);
- smp_mb__after_clear_bit();
-
- schedule_delayed_work(&bp->sp_rtnl_task, 0);
+ bnx2x_schedule_sp_rtnl(bp, cmd, 0);
}
goto next_spqe;
@@ -5435,13 +5567,6 @@ static void bnx2x_sp_task(struct work_struct *work)
le16_to_cpu(bp->def_att_idx), IGU_INT_ENABLE, 1);
}
- /* must be called after the EQ processing (since eq leads to sriov
- * ramrod completion flows).
- * This flow may have been scheduled by the arrival of a ramrod
- * completion, or by the sriov code rescheduling itself.
- */
- bnx2x_iov_sp_task(bp);
-
/* afex - poll to check if VIFSET_ACK should be sent to MFW */
if (test_and_clear_bit(BNX2X_AFEX_PENDING_VIFSET_MCP_ACK,
&bp->sp_state)) {
@@ -6005,18 +6130,6 @@ static void bnx2x_init_internal_common(struct bnx2x *bp)
{
int i;
- if (IS_MF_SI(bp))
- /*
- * In switch independent mode, the TSTORM needs to accept
- * packets that failed classification, since approximate match
- * mac addresses aren't written to NIG LLH
- */
- REG_WR8(bp, BAR_TSTRORM_INTMEM +
- TSTORM_ACCEPT_CLASSIFY_FAILED_OFFSET, 2);
- else if (!CHIP_IS_E1(bp)) /* 57710 doesn't support MF */
- REG_WR8(bp, BAR_TSTRORM_INTMEM +
- TSTORM_ACCEPT_CLASSIFY_FAILED_OFFSET, 0);
-
/* Zero this manually as its initialization is
currently missing in the initTool */
for (i = 0; i < (USTORM_AGG_DATA_SIZE >> 2); i++)
@@ -7989,19 +8102,25 @@ void bnx2x_free_mem(struct bnx2x *bp)
int bnx2x_alloc_mem_cnic(struct bnx2x *bp)
{
- if (!CHIP_IS_E1x(bp))
+ if (!CHIP_IS_E1x(bp)) {
/* size = the status block + ramrod buffers */
- BNX2X_PCI_ALLOC(bp->cnic_sb.e2_sb, &bp->cnic_sb_mapping,
- sizeof(struct host_hc_status_block_e2));
- else
- BNX2X_PCI_ALLOC(bp->cnic_sb.e1x_sb,
- &bp->cnic_sb_mapping,
- sizeof(struct
- host_hc_status_block_e1x));
+ bp->cnic_sb.e2_sb = BNX2X_PCI_ALLOC(&bp->cnic_sb_mapping,
+ sizeof(struct host_hc_status_block_e2));
+ if (!bp->cnic_sb.e2_sb)
+ goto alloc_mem_err;
+ } else {
+ bp->cnic_sb.e1x_sb = BNX2X_PCI_ALLOC(&bp->cnic_sb_mapping,
+ sizeof(struct host_hc_status_block_e1x));
+ if (!bp->cnic_sb.e1x_sb)
+ goto alloc_mem_err;
+ }
- if (CONFIGURE_NIC_MODE(bp) && !bp->t2)
+ if (CONFIGURE_NIC_MODE(bp) && !bp->t2) {
/* allocate searcher T2 table, as it wasn't allocated before */
- BNX2X_PCI_ALLOC(bp->t2, &bp->t2_mapping, SRC_T2_SZ);
+ bp->t2 = BNX2X_PCI_ALLOC(&bp->t2_mapping, SRC_T2_SZ);
+ if (!bp->t2)
+ goto alloc_mem_err;
+ }
/* write address to which L5 should insert its values */
bp->cnic_eth_dev.addr_drv_info_to_mcp =
@@ -8022,15 +8141,22 @@ int bnx2x_alloc_mem(struct bnx2x *bp)
{
int i, allocated, context_size;
- if (!CONFIGURE_NIC_MODE(bp) && !bp->t2)
+ if (!CONFIGURE_NIC_MODE(bp) && !bp->t2) {
/* allocate searcher T2 table */
- BNX2X_PCI_ALLOC(bp->t2, &bp->t2_mapping, SRC_T2_SZ);
+ bp->t2 = BNX2X_PCI_ALLOC(&bp->t2_mapping, SRC_T2_SZ);
+ if (!bp->t2)
+ goto alloc_mem_err;
+ }
- BNX2X_PCI_ALLOC(bp->def_status_blk, &bp->def_status_blk_mapping,
- sizeof(struct host_sp_status_block));
+ bp->def_status_blk = BNX2X_PCI_ALLOC(&bp->def_status_blk_mapping,
+ sizeof(struct host_sp_status_block));
+ if (!bp->def_status_blk)
+ goto alloc_mem_err;
- BNX2X_PCI_ALLOC(bp->slowpath, &bp->slowpath_mapping,
- sizeof(struct bnx2x_slowpath));
+ bp->slowpath = BNX2X_PCI_ALLOC(&bp->slowpath_mapping,
+ sizeof(struct bnx2x_slowpath));
+ if (!bp->slowpath)
+ goto alloc_mem_err;
/* Allocate memory for CDU context:
* This memory is allocated separately and not in the generic ILT
@@ -8050,12 +8176,16 @@ int bnx2x_alloc_mem(struct bnx2x *bp)
for (i = 0, allocated = 0; allocated < context_size; i++) {
bp->context[i].size = min(CDU_ILT_PAGE_SZ,
(context_size - allocated));
- BNX2X_PCI_ALLOC(bp->context[i].vcxt,
- &bp->context[i].cxt_mapping,
- bp->context[i].size);
+ bp->context[i].vcxt = BNX2X_PCI_ALLOC(&bp->context[i].cxt_mapping,
+ bp->context[i].size);
+ if (!bp->context[i].vcxt)
+ goto alloc_mem_err;
allocated += bp->context[i].size;
}
- BNX2X_ALLOC(bp->ilt->lines, sizeof(struct ilt_line) * ILT_MAX_LINES);
+ bp->ilt->lines = kcalloc(ILT_MAX_LINES, sizeof(struct ilt_line),
+ GFP_KERNEL);
+ if (!bp->ilt->lines)
+ goto alloc_mem_err;
if (bnx2x_ilt_mem_op(bp, ILT_MEMOP_ALLOC))
goto alloc_mem_err;
@@ -8064,11 +8194,15 @@ int bnx2x_alloc_mem(struct bnx2x *bp)
goto alloc_mem_err;
/* Slow path ring */
- BNX2X_PCI_ALLOC(bp->spq, &bp->spq_mapping, BCM_PAGE_SIZE);
+ bp->spq = BNX2X_PCI_ALLOC(&bp->spq_mapping, BCM_PAGE_SIZE);
+ if (!bp->spq)
+ goto alloc_mem_err;
/* EQ */
- BNX2X_PCI_ALLOC(bp->eq_ring, &bp->eq_mapping,
- BCM_PAGE_SIZE * NUM_EQ_PAGES);
+ bp->eq_ring = BNX2X_PCI_ALLOC(&bp->eq_mapping,
+ BCM_PAGE_SIZE * NUM_EQ_PAGES);
+ if (!bp->eq_ring)
+ goto alloc_mem_err;
return 0;
@@ -8849,6 +8983,7 @@ static int bnx2x_func_wait_started(struct bnx2x *bp)
synchronize_irq(bp->pdev->irq);
flush_workqueue(bnx2x_wq);
+ flush_workqueue(bnx2x_iov_wq);
while (bnx2x_func_get_state(bp, &bp->func_obj) !=
BNX2X_F_STATE_STARTED && tout--)
@@ -9774,6 +9909,10 @@ sp_rtnl_not_reset:
bnx2x_dcbx_resume_hw_tx(bp);
}
+ if (test_and_clear_bit(BNX2X_SP_RTNL_GET_DRV_VERSION,
+ &bp->sp_rtnl_state))
+ bnx2x_update_mng_version(bp);
+
/* work which needs rtnl lock not-taken (as it takes the lock itself and
* can be called from other contexts as well)
*/
@@ -11724,12 +11863,15 @@ static int bnx2x_init_bp(struct bnx2x *bp)
mutex_init(&bp->port.phy_mutex);
mutex_init(&bp->fw_mb_mutex);
+ mutex_init(&bp->drv_info_mutex);
+ bp->drv_info_mng_owner = false;
spin_lock_init(&bp->stats_lock);
sema_init(&bp->stats_sema, 1);
INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task);
INIT_DELAYED_WORK(&bp->sp_rtnl_task, bnx2x_sp_rtnl_task);
INIT_DELAYED_WORK(&bp->period_task, bnx2x_period_task);
+ INIT_DELAYED_WORK(&bp->iov_task, bnx2x_iov_task);
if (IS_PF(bp)) {
rc = bnx2x_get_hwinfo(bp);
if (rc)
@@ -11771,6 +11913,8 @@ static int bnx2x_init_bp(struct bnx2x *bp)
bp->disable_tpa = disable_tpa;
bp->disable_tpa |= IS_MF_STORAGE_SD(bp) || IS_MF_FCOE_AFEX(bp);
+ /* Reduce memory usage in kdump environment by disabling TPA */
+ bp->disable_tpa |= reset_devices;
/* Set TPA flags */
if (bp->disable_tpa) {
@@ -11942,7 +12086,7 @@ static int bnx2x_init_mcast_macs_list(struct bnx2x *bp,
{
int mc_count = netdev_mc_count(bp->dev);
struct bnx2x_mcast_list_elem *mc_mac =
- kzalloc(sizeof(*mc_mac) * mc_count, GFP_ATOMIC);
+ kcalloc(mc_count, sizeof(*mc_mac), GFP_ATOMIC);
struct netdev_hw_addr *ha;
if (!mc_mac)
@@ -12064,11 +12208,8 @@ static void bnx2x_set_rx_mode(struct net_device *dev)
return;
} else {
/* Schedule an SP task to handle rest of change */
- DP(NETIF_MSG_IFUP, "Scheduling an Rx mode change\n");
- smp_mb__before_clear_bit();
- set_bit(BNX2X_SP_RTNL_RX_MODE, &bp->sp_rtnl_state);
- smp_mb__after_clear_bit();
- schedule_delayed_work(&bp->sp_rtnl_task, 0);
+ bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_RX_MODE,
+ NETIF_MSG_IFUP);
}
}
@@ -12101,11 +12242,8 @@ void bnx2x_set_rx_mode_inner(struct bnx2x *bp)
/* configuring mcast to a vf involves sleeping (when we
* wait for the pf's response).
*/
- smp_mb__before_clear_bit();
- set_bit(BNX2X_SP_RTNL_VFPF_MCAST,
- &bp->sp_rtnl_state);
- smp_mb__after_clear_bit();
- schedule_delayed_work(&bp->sp_rtnl_task, 0);
+ bnx2x_schedule_sp_rtnl(bp,
+ BNX2X_SP_RTNL_VFPF_MCAST, 0);
}
}
@@ -13356,11 +13494,18 @@ static int __init bnx2x_init(void)
pr_err("Cannot create workqueue\n");
return -ENOMEM;
}
+ bnx2x_iov_wq = create_singlethread_workqueue("bnx2x_iov");
+ if (!bnx2x_iov_wq) {
+ pr_err("Cannot create iov workqueue\n");
+ destroy_workqueue(bnx2x_wq);
+ return -ENOMEM;
+ }
ret = pci_register_driver(&bnx2x_pci_driver);
if (ret) {
pr_err("Cannot register driver\n");
destroy_workqueue(bnx2x_wq);
+ destroy_workqueue(bnx2x_iov_wq);
}
return ret;
}
@@ -13372,6 +13517,7 @@ static void __exit bnx2x_cleanup(void)
pci_unregister_driver(&bnx2x_pci_driver);
destroy_workqueue(bnx2x_wq);
+ destroy_workqueue(bnx2x_iov_wq);
/* Free globally allocated resources */
list_for_each_safe(pos, q, &bnx2x_prev_list) {
@@ -13765,6 +13911,7 @@ static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl)
REG_WR(bp, scratch_offset + i,
*(host_addr + i/4));
}
+ bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_GET_DRV_VERSION, 0);
break;
}
@@ -13782,6 +13929,7 @@ static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl)
cap &= ~DRV_FLAGS_CAPABILITIES_LOADED_FCOE;
SHMEM2_WR(bp, drv_capabilities_flag[idx], cap);
}
+ bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_GET_DRV_VERSION, 0);
break;
}
@@ -13887,6 +14035,9 @@ static int bnx2x_register_cnic(struct net_device *dev, struct cnic_ops *ops,
rcu_assign_pointer(bp->cnic_ops, ops);
+ /* Schedule driver to read CNIC driver versions */
+ bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_GET_DRV_VERSION, 0);
+
return 0;
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
index 0fb6ff2ac8e3..31297266b743 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
@@ -2277,11 +2277,11 @@ static int bnx2x_set_rx_mode_e2(struct bnx2x *bp,
data->header.rule_cnt, p->rx_accept_flags,
p->tx_accept_flags);
- /* No need for an explicit memory barrier here as long we would
- * need to ensure the ordering of writing to the SPQ element
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
* and updating of the SPQ producer which involves a memory
- * read and we will have to put a full memory barrier there
- * (inside bnx2x_sp_post()).
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
*/
/* Send a ramrod */
@@ -2982,11 +2982,11 @@ static int bnx2x_mcast_setup_e2(struct bnx2x *bp,
raw->clear_pending(raw);
return 0;
} else {
- /* No need for an explicit memory barrier here as long we would
- * need to ensure the ordering of writing to the SPQ element
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
* and updating of the SPQ producer which involves a memory
- * read and we will have to put a full memory barrier there
- * (inside bnx2x_sp_post()).
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
*/
/* Send a ramrod */
@@ -3466,11 +3466,11 @@ static int bnx2x_mcast_setup_e1(struct bnx2x *bp,
raw->clear_pending(raw);
return 0;
} else {
- /* No need for an explicit memory barrier here as long we would
- * need to ensure the ordering of writing to the SPQ element
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
* and updating of the SPQ producer which involves a memory
- * read and we will have to put a full memory barrier there
- * (inside bnx2x_sp_post()).
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
*/
/* Send a ramrod */
@@ -4091,11 +4091,11 @@ static int bnx2x_setup_rss(struct bnx2x *bp,
data->capabilities |= ETH_RSS_UPDATE_RAMROD_DATA_UPDATE_RSS_KEY;
}
- /* No need for an explicit memory barrier here as long we would
- * need to ensure the ordering of writing to the SPQ element
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
* and updating of the SPQ producer which involves a memory
- * read and we will have to put a full memory barrier there
- * (inside bnx2x_sp_post()).
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
*/
/* Send a ramrod */
@@ -4158,16 +4158,6 @@ void bnx2x_init_rss_config_obj(struct bnx2x *bp,
rss_obj->config_rss = bnx2x_setup_rss;
}
-int validate_vlan_mac(struct bnx2x *bp,
- struct bnx2x_vlan_mac_obj *vlan_mac)
-{
- if (!vlan_mac->get_n_elements) {
- BNX2X_ERR("vlan mac object was not intialized\n");
- return -EINVAL;
- }
- return 0;
-}
-
/********************** Queue state object ***********************************/
/**
@@ -4587,13 +4577,12 @@ static inline int bnx2x_q_send_setup_e1x(struct bnx2x *bp,
/* Fill the ramrod data */
bnx2x_q_fill_setup_data_cmn(bp, params, rdata);
- /* No need for an explicit memory barrier here as long we would
- * need to ensure the ordering of writing to the SPQ element
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
* and updating of the SPQ producer which involves a memory
- * read and we will have to put a full memory barrier there
- * (inside bnx2x_sp_post()).
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
*/
-
return bnx2x_sp_post(bp, ramrod, o->cids[BNX2X_PRIMARY_CID_INDEX],
U64_HI(data_mapping),
U64_LO(data_mapping), ETH_CONNECTION_TYPE);
@@ -4615,13 +4604,12 @@ static inline int bnx2x_q_send_setup_e2(struct bnx2x *bp,
bnx2x_q_fill_setup_data_cmn(bp, params, rdata);
bnx2x_q_fill_setup_data_e2(bp, params, rdata);
- /* No need for an explicit memory barrier here as long we would
- * need to ensure the ordering of writing to the SPQ element
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
* and updating of the SPQ producer which involves a memory
- * read and we will have to put a full memory barrier there
- * (inside bnx2x_sp_post()).
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
*/
-
return bnx2x_sp_post(bp, ramrod, o->cids[BNX2X_PRIMARY_CID_INDEX],
U64_HI(data_mapping),
U64_LO(data_mapping), ETH_CONNECTION_TYPE);
@@ -4659,13 +4647,12 @@ static inline int bnx2x_q_send_setup_tx_only(struct bnx2x *bp,
o->cids[cid_index], rdata->general.client_id,
rdata->general.sp_client_id, rdata->general.cos);
- /* No need for an explicit memory barrier here as long we would
- * need to ensure the ordering of writing to the SPQ element
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
* and updating of the SPQ producer which involves a memory
- * read and we will have to put a full memory barrier there
- * (inside bnx2x_sp_post()).
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
*/
-
return bnx2x_sp_post(bp, ramrod, o->cids[cid_index],
U64_HI(data_mapping),
U64_LO(data_mapping), ETH_CONNECTION_TYPE);
@@ -4760,13 +4747,12 @@ static inline int bnx2x_q_send_update(struct bnx2x *bp,
/* Fill the ramrod data */
bnx2x_q_fill_update_data(bp, o, update_params, rdata);
- /* No need for an explicit memory barrier here as long we would
- * need to ensure the ordering of writing to the SPQ element
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
* and updating of the SPQ producer which involves a memory
- * read and we will have to put a full memory barrier there
- * (inside bnx2x_sp_post()).
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
*/
-
return bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_CLIENT_UPDATE,
o->cids[cid_index], U64_HI(data_mapping),
U64_LO(data_mapping), ETH_CONNECTION_TYPE);
@@ -4813,11 +4799,62 @@ static inline int bnx2x_q_send_activate(struct bnx2x *bp,
return bnx2x_q_send_update(bp, params);
}
+static void bnx2x_q_fill_update_tpa_data(struct bnx2x *bp,
+ struct bnx2x_queue_sp_obj *obj,
+ struct bnx2x_queue_update_tpa_params *params,
+ struct tpa_update_ramrod_data *data)
+{
+ data->client_id = obj->cl_id;
+ data->complete_on_both_clients = params->complete_on_both_clients;
+ data->dont_verify_rings_pause_thr_flg =
+ params->dont_verify_thr;
+ data->max_agg_size = cpu_to_le16(params->max_agg_sz);
+ data->max_sges_for_packet = params->max_sges_pkt;
+ data->max_tpa_queues = params->max_tpa_queues;
+ data->sge_buff_size = cpu_to_le16(params->sge_buff_sz);
+ data->sge_page_base_hi = cpu_to_le32(U64_HI(params->sge_map));
+ data->sge_page_base_lo = cpu_to_le32(U64_LO(params->sge_map));
+ data->sge_pause_thr_high = cpu_to_le16(params->sge_pause_thr_high);
+ data->sge_pause_thr_low = cpu_to_le16(params->sge_pause_thr_low);
+ data->tpa_mode = params->tpa_mode;
+ data->update_ipv4 = params->update_ipv4;
+ data->update_ipv6 = params->update_ipv6;
+}
+
static inline int bnx2x_q_send_update_tpa(struct bnx2x *bp,
struct bnx2x_queue_state_params *params)
{
- /* TODO: Not implemented yet. */
- return -1;
+ struct bnx2x_queue_sp_obj *o = params->q_obj;
+ struct tpa_update_ramrod_data *rdata =
+ (struct tpa_update_ramrod_data *)o->rdata;
+ dma_addr_t data_mapping = o->rdata_mapping;
+ struct bnx2x_queue_update_tpa_params *update_tpa_params =
+ &params->params.update_tpa;
+ u16 type;
+
+ /* Clear the ramrod data */
+ memset(rdata, 0, sizeof(*rdata));
+
+ /* Fill the ramrod data */
+ bnx2x_q_fill_update_tpa_data(bp, o, update_tpa_params, rdata);
+
+ /* Add the function id inside the type, so that sp post function
+ * doesn't automatically add the PF func-id, this is required
+ * for operations done by PFs on behalf of their VFs
+ */
+ type = ETH_CONNECTION_TYPE |
+ ((o->func_id) << SPE_HDR_FUNCTION_ID_SHIFT);
+
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
+ */
+ return bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_TPA_UPDATE,
+ o->cids[BNX2X_PRIMARY_CID_INDEX],
+ U64_HI(data_mapping),
+ U64_LO(data_mapping), type);
}
static inline int bnx2x_q_send_halt(struct bnx2x *bp,
@@ -5647,6 +5684,12 @@ static inline int bnx2x_func_send_switch_update(struct bnx2x *bp,
rdata->tx_switch_suspend = switch_update_params->suspend;
rdata->echo = SWITCH_UPDATE;
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
+ */
return bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_FUNCTION_UPDATE, 0,
U64_HI(data_mapping),
U64_LO(data_mapping), NONE_CONNECTION_TYPE);
@@ -5674,11 +5717,11 @@ static inline int bnx2x_func_send_afex_update(struct bnx2x *bp,
rdata->allowed_priorities = afex_update_params->allowed_priorities;
rdata->echo = AFEX_UPDATE;
- /* No need for an explicit memory barrier here as long we would
- * need to ensure the ordering of writing to the SPQ element
- * and updating of the SPQ producer which involves a memory
- * read and we will have to put a full memory barrier there
- * (inside bnx2x_sp_post()).
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
*/
DP(BNX2X_MSG_SP,
"afex: sending func_update vif_id 0x%x dvlan 0x%x prio 0x%x\n",
@@ -5763,6 +5806,12 @@ static inline int bnx2x_func_send_tx_start(struct bnx2x *bp,
rdata->traffic_type_to_priority_cos[i] =
tx_start_params->traffic_type_to_priority_cos[i];
+ /* No need for an explicit memory barrier here as long as we
+ * ensure the ordering of writing to the SPQ element
+ * and updating of the SPQ producer which involves a memory
+ * read. If the memory read is removed we will have to put a
+ * full memory barrier there (inside bnx2x_sp_post()).
+ */
return bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_START_TRAFFIC, 0,
U64_HI(data_mapping),
U64_LO(data_mapping), NONE_CONNECTION_TYPE);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
index 00d7f214a40a..80f6c790ed88 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
@@ -893,6 +893,24 @@ struct bnx2x_queue_update_params {
u8 cid_index;
};
+struct bnx2x_queue_update_tpa_params {
+ dma_addr_t sge_map;
+ u8 update_ipv4;
+ u8 update_ipv6;
+ u8 max_tpa_queues;
+ u8 max_sges_pkt;
+ u8 complete_on_both_clients;
+ u8 dont_verify_thr;
+ u8 tpa_mode;
+ u8 _pad;
+
+ u16 sge_buff_sz;
+ u16 max_agg_sz;
+
+ u16 sge_pause_thr_low;
+ u16 sge_pause_thr_high;
+};
+
struct rxq_pause_params {
u16 bd_th_lo;
u16 bd_th_hi;
@@ -987,6 +1005,7 @@ struct bnx2x_queue_state_params {
/* Params according to the current command */
union {
struct bnx2x_queue_update_params update;
+ struct bnx2x_queue_update_tpa_params update_tpa;
struct bnx2x_queue_setup_params setup;
struct bnx2x_queue_init_params init;
struct bnx2x_queue_setup_tx_only_params tx_only;
@@ -1403,6 +1422,4 @@ int bnx2x_config_rss(struct bnx2x *bp,
void bnx2x_get_rss_ind_table(struct bnx2x_rss_config_obj *rss_obj,
u8 *ind_table);
-int validate_vlan_mac(struct bnx2x *bp,
- struct bnx2x_vlan_mac_obj *vlan_mac);
#endif /* BNX2X_SP_VERBS */
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
index e42f48df6e94..5c523b32db70 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
@@ -102,82 +102,22 @@ static void bnx2x_vf_igu_ack_sb(struct bnx2x *bp, struct bnx2x_virtf *vf,
mmiowb();
barrier();
}
-/* VFOP - VF slow-path operation support */
-#define BNX2X_VFOP_FILTER_ADD_CNT_MAX 0x10000
+static bool bnx2x_validate_vf_sp_objs(struct bnx2x *bp,
+ struct bnx2x_virtf *vf,
+ bool print_err)
+{
+ if (!bnx2x_leading_vfq(vf, sp_initialized)) {
+ if (print_err)
+ BNX2X_ERR("Slowpath objects not yet initialized!\n");
+ else
+ DP(BNX2X_MSG_IOV, "Slowpath objects not yet initialized!\n");
+ return false;
+ }
+ return true;
+}
/* VFOP operations states */
-enum bnx2x_vfop_qctor_state {
- BNX2X_VFOP_QCTOR_INIT,
- BNX2X_VFOP_QCTOR_SETUP,
- BNX2X_VFOP_QCTOR_INT_EN
-};
-
-enum bnx2x_vfop_qdtor_state {
- BNX2X_VFOP_QDTOR_HALT,
- BNX2X_VFOP_QDTOR_TERMINATE,
- BNX2X_VFOP_QDTOR_CFCDEL,
- BNX2X_VFOP_QDTOR_DONE
-};
-
-enum bnx2x_vfop_vlan_mac_state {
- BNX2X_VFOP_VLAN_MAC_CONFIG_SINGLE,
- BNX2X_VFOP_VLAN_MAC_CLEAR,
- BNX2X_VFOP_VLAN_MAC_CHK_DONE,
- BNX2X_VFOP_MAC_CONFIG_LIST,
- BNX2X_VFOP_VLAN_CONFIG_LIST,
- BNX2X_VFOP_VLAN_CONFIG_LIST_0
-};
-
-enum bnx2x_vfop_qsetup_state {
- BNX2X_VFOP_QSETUP_CTOR,
- BNX2X_VFOP_QSETUP_VLAN0,
- BNX2X_VFOP_QSETUP_DONE
-};
-
-enum bnx2x_vfop_mcast_state {
- BNX2X_VFOP_MCAST_DEL,
- BNX2X_VFOP_MCAST_ADD,
- BNX2X_VFOP_MCAST_CHK_DONE
-};
-enum bnx2x_vfop_qflr_state {
- BNX2X_VFOP_QFLR_CLR_VLAN,
- BNX2X_VFOP_QFLR_CLR_MAC,
- BNX2X_VFOP_QFLR_TERMINATE,
- BNX2X_VFOP_QFLR_DONE
-};
-
-enum bnx2x_vfop_flr_state {
- BNX2X_VFOP_FLR_QUEUES,
- BNX2X_VFOP_FLR_HW
-};
-
-enum bnx2x_vfop_close_state {
- BNX2X_VFOP_CLOSE_QUEUES,
- BNX2X_VFOP_CLOSE_HW
-};
-
-enum bnx2x_vfop_rxmode_state {
- BNX2X_VFOP_RXMODE_CONFIG,
- BNX2X_VFOP_RXMODE_DONE
-};
-
-enum bnx2x_vfop_qteardown_state {
- BNX2X_VFOP_QTEARDOWN_RXMODE,
- BNX2X_VFOP_QTEARDOWN_CLR_VLAN,
- BNX2X_VFOP_QTEARDOWN_CLR_MAC,
- BNX2X_VFOP_QTEARDOWN_CLR_MCAST,
- BNX2X_VFOP_QTEARDOWN_QDTOR,
- BNX2X_VFOP_QTEARDOWN_DONE
-};
-
-enum bnx2x_vfop_rss_state {
- BNX2X_VFOP_RSS_CONFIG,
- BNX2X_VFOP_RSS_DONE
-};
-
-#define bnx2x_vfop_reset_wq(vf) atomic_set(&vf->op_in_progress, 0)
-
void bnx2x_vfop_qctor_dump_tx(struct bnx2x *bp, struct bnx2x_virtf *vf,
struct bnx2x_queue_init_params *init_params,
struct bnx2x_queue_setup_params *setup_params,
@@ -221,7 +161,7 @@ void bnx2x_vfop_qctor_dump_rx(struct bnx2x *bp, struct bnx2x_virtf *vf,
void bnx2x_vfop_qctor_prep(struct bnx2x *bp,
struct bnx2x_virtf *vf,
struct bnx2x_vf_queue *q,
- struct bnx2x_vfop_qctor_params *p,
+ struct bnx2x_vf_queue_construct_params *p,
unsigned long q_type)
{
struct bnx2x_queue_init_params *init_p = &p->qstate.params.init;
@@ -290,191 +230,85 @@ void bnx2x_vfop_qctor_prep(struct bnx2x *bp,
}
}
-/* VFOP queue construction */
-static void bnx2x_vfop_qctor(struct bnx2x *bp, struct bnx2x_virtf *vf)
+static int bnx2x_vf_queue_create(struct bnx2x *bp,
+ struct bnx2x_virtf *vf, int qid,
+ struct bnx2x_vf_queue_construct_params *qctor)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- struct bnx2x_vfop_args_qctor *args = &vfop->args.qctor;
- struct bnx2x_queue_state_params *q_params = &vfop->op_p->qctor.qstate;
- enum bnx2x_vfop_qctor_state state = vfop->state;
-
- bnx2x_vfop_reset_wq(vf);
-
- if (vfop->rc < 0)
- goto op_err;
-
- DP(BNX2X_MSG_IOV, "vf[%d] STATE: %d\n", vf->abs_vfid, state);
-
- switch (state) {
- case BNX2X_VFOP_QCTOR_INIT:
-
- /* has this queue already been opened? */
- if (bnx2x_get_q_logical_state(bp, q_params->q_obj) ==
- BNX2X_Q_LOGICAL_STATE_ACTIVE) {
- DP(BNX2X_MSG_IOV,
- "Entered qctor but queue was already up. Aborting gracefully\n");
- goto op_done;
- }
-
- /* next state */
- vfop->state = BNX2X_VFOP_QCTOR_SETUP;
-
- q_params->cmd = BNX2X_Q_CMD_INIT;
- vfop->rc = bnx2x_queue_state_change(bp, q_params);
-
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
-
- case BNX2X_VFOP_QCTOR_SETUP:
- /* next state */
- vfop->state = BNX2X_VFOP_QCTOR_INT_EN;
-
- /* copy pre-prepared setup params to the queue-state params */
- vfop->op_p->qctor.qstate.params.setup =
- vfop->op_p->qctor.prep_qsetup;
-
- q_params->cmd = BNX2X_Q_CMD_SETUP;
- vfop->rc = bnx2x_queue_state_change(bp, q_params);
+ struct bnx2x_queue_state_params *q_params;
+ int rc = 0;
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
+ DP(BNX2X_MSG_IOV, "vf[%d:%d]\n", vf->abs_vfid, qid);
- case BNX2X_VFOP_QCTOR_INT_EN:
+ /* Prepare ramrod information */
+ q_params = &qctor->qstate;
+ q_params->q_obj = &bnx2x_vfq(vf, qid, sp_obj);
+ set_bit(RAMROD_COMP_WAIT, &q_params->ramrod_flags);
- /* enable interrupts */
- bnx2x_vf_igu_ack_sb(bp, vf, vf_igu_sb(vf, args->sb_idx),
- USTORM_ID, 0, IGU_INT_ENABLE, 0);
- goto op_done;
- default:
- bnx2x_vfop_default(state);
+ if (bnx2x_get_q_logical_state(bp, q_params->q_obj) ==
+ BNX2X_Q_LOGICAL_STATE_ACTIVE) {
+ DP(BNX2X_MSG_IOV, "queue was already up. Aborting gracefully\n");
+ goto out;
}
-op_err:
- BNX2X_ERR("QCTOR[%d:%d] error: cmd %d, rc %d\n",
- vf->abs_vfid, args->qid, q_params->cmd, vfop->rc);
-op_done:
- bnx2x_vfop_end(bp, vf, vfop);
-op_pending:
- return;
-}
-static int bnx2x_vfop_qctor_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
-
- if (vfop) {
- vf->op_params.qctor.qstate.q_obj = &bnx2x_vfq(vf, qid, sp_obj);
+ /* Run Queue 'construction' ramrods */
+ q_params->cmd = BNX2X_Q_CMD_INIT;
+ rc = bnx2x_queue_state_change(bp, q_params);
+ if (rc)
+ goto out;
- vfop->args.qctor.qid = qid;
- vfop->args.qctor.sb_idx = bnx2x_vfq(vf, qid, sb_idx);
+ memcpy(&q_params->params.setup, &qctor->prep_qsetup,
+ sizeof(struct bnx2x_queue_setup_params));
+ q_params->cmd = BNX2X_Q_CMD_SETUP;
+ rc = bnx2x_queue_state_change(bp, q_params);
+ if (rc)
+ goto out;
- bnx2x_vfop_opset(BNX2X_VFOP_QCTOR_INIT,
- bnx2x_vfop_qctor, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_qctor,
- cmd->block);
- }
- return -ENOMEM;
+ /* enable interrupts */
+ bnx2x_vf_igu_ack_sb(bp, vf, vf_igu_sb(vf, bnx2x_vfq(vf, qid, sb_idx)),
+ USTORM_ID, 0, IGU_INT_ENABLE, 0);
+out:
+ return rc;
}
-/* VFOP queue destruction */
-static void bnx2x_vfop_qdtor(struct bnx2x *bp, struct bnx2x_virtf *vf)
+static int bnx2x_vf_queue_destroy(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ int qid)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- struct bnx2x_vfop_args_qdtor *qdtor = &vfop->args.qdtor;
- struct bnx2x_queue_state_params *q_params = &vfop->op_p->qctor.qstate;
- enum bnx2x_vfop_qdtor_state state = vfop->state;
-
- bnx2x_vfop_reset_wq(vf);
-
- if (vfop->rc < 0)
- goto op_err;
-
- DP(BNX2X_MSG_IOV, "vf[%d] STATE: %d\n", vf->abs_vfid, state);
-
- switch (state) {
- case BNX2X_VFOP_QDTOR_HALT:
-
- /* has this queue already been stopped? */
- if (bnx2x_get_q_logical_state(bp, q_params->q_obj) ==
- BNX2X_Q_LOGICAL_STATE_STOPPED) {
- DP(BNX2X_MSG_IOV,
- "Entered qdtor but queue was already stopped. Aborting gracefully\n");
-
- /* next state */
- vfop->state = BNX2X_VFOP_QDTOR_DONE;
-
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
- }
-
- /* next state */
- vfop->state = BNX2X_VFOP_QDTOR_TERMINATE;
-
- q_params->cmd = BNX2X_Q_CMD_HALT;
- vfop->rc = bnx2x_queue_state_change(bp, q_params);
-
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
-
- case BNX2X_VFOP_QDTOR_TERMINATE:
- /* next state */
- vfop->state = BNX2X_VFOP_QDTOR_CFCDEL;
-
- q_params->cmd = BNX2X_Q_CMD_TERMINATE;
- vfop->rc = bnx2x_queue_state_change(bp, q_params);
+ enum bnx2x_queue_cmd cmds[] = {BNX2X_Q_CMD_HALT,
+ BNX2X_Q_CMD_TERMINATE,
+ BNX2X_Q_CMD_CFC_DEL};
+ struct bnx2x_queue_state_params q_params;
+ int rc, i;
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
+ DP(BNX2X_MSG_IOV, "vf[%d]\n", vf->abs_vfid);
- case BNX2X_VFOP_QDTOR_CFCDEL:
- /* next state */
- vfop->state = BNX2X_VFOP_QDTOR_DONE;
+ /* Prepare ramrod information */
+ memset(&q_params, 0, sizeof(struct bnx2x_queue_state_params));
+ q_params.q_obj = &bnx2x_vfq(vf, qid, sp_obj);
+ set_bit(RAMROD_COMP_WAIT, &q_params.ramrod_flags);
- q_params->cmd = BNX2X_Q_CMD_CFC_DEL;
- vfop->rc = bnx2x_queue_state_change(bp, q_params);
+ if (bnx2x_get_q_logical_state(bp, q_params.q_obj) ==
+ BNX2X_Q_LOGICAL_STATE_STOPPED) {
+ DP(BNX2X_MSG_IOV, "queue was already stopped. Aborting gracefully\n");
+ goto out;
+ }
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_DONE);
-op_err:
- BNX2X_ERR("QDTOR[%d:%d] error: cmd %d, rc %d\n",
- vf->abs_vfid, qdtor->qid, q_params->cmd, vfop->rc);
-op_done:
- case BNX2X_VFOP_QDTOR_DONE:
- /* invalidate the context */
- if (qdtor->cxt) {
- qdtor->cxt->ustorm_ag_context.cdu_usage = 0;
- qdtor->cxt->xstorm_ag_context.cdu_reserved = 0;
+ /* Run Queue 'destruction' ramrods */
+ for (i = 0; i < ARRAY_SIZE(cmds); i++) {
+ q_params.cmd = cmds[i];
+ rc = bnx2x_queue_state_change(bp, &q_params);
+ if (rc) {
+ BNX2X_ERR("Failed to run Queue command %d\n", cmds[i]);
+ return rc;
}
- bnx2x_vfop_end(bp, vf, vfop);
- return;
- default:
- bnx2x_vfop_default(state);
}
-op_pending:
- return;
-}
-
-static int bnx2x_vfop_qdtor_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
-
- if (vfop) {
- struct bnx2x_queue_state_params *qstate =
- &vf->op_params.qctor.qstate;
-
- memset(qstate, 0, sizeof(*qstate));
- qstate->q_obj = &bnx2x_vfq(vf, qid, sp_obj);
-
- vfop->args.qdtor.qid = qid;
- vfop->args.qdtor.cxt = bnx2x_vfq(vf, qid, cxt);
-
- bnx2x_vfop_opset(BNX2X_VFOP_QDTOR_HALT,
- bnx2x_vfop_qdtor, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_qdtor,
- cmd->block);
- } else {
- BNX2X_ERR("VF[%d] failed to add a vfop\n", vf->abs_vfid);
- return -ENOMEM;
+out:
+ /* Clean Context */
+ if (bnx2x_vfq(vf, qid, cxt)) {
+ bnx2x_vfq(vf, qid, cxt)->ustorm_ag_context.cdu_usage = 0;
+ bnx2x_vfq(vf, qid, cxt)->xstorm_ag_context.cdu_reserved = 0;
}
+
+ return 0;
}
static void
@@ -496,751 +330,293 @@ bnx2x_vf_set_igu_info(struct bnx2x *bp, u8 igu_sb_id, u8 abs_vfid)
BP_VFDB(bp)->vf_sbs_pool++;
}
-/* VFOP MAC/VLAN helpers */
-static inline void bnx2x_vfop_credit(struct bnx2x *bp,
- struct bnx2x_vfop *vfop,
- struct bnx2x_vlan_mac_obj *obj)
+static inline void bnx2x_vf_vlan_credit(struct bnx2x *bp,
+ struct bnx2x_vlan_mac_obj *obj,
+ atomic_t *counter)
{
- struct bnx2x_vfop_args_filters *args = &vfop->args.filters;
-
- /* update credit only if there is no error
- * and a valid credit counter
- */
- if (!vfop->rc && args->credit) {
- struct list_head *pos;
- int read_lock;
- int cnt = 0;
-
- read_lock = bnx2x_vlan_mac_h_read_lock(bp, obj);
- if (read_lock)
- DP(BNX2X_MSG_SP, "Failed to take vlan mac read head; continuing anyway\n");
+ struct list_head *pos;
+ int read_lock;
+ int cnt = 0;
- list_for_each(pos, &obj->head)
- cnt++;
+ read_lock = bnx2x_vlan_mac_h_read_lock(bp, obj);
+ if (read_lock)
+ DP(BNX2X_MSG_SP, "Failed to take vlan mac read head; continuing anyway\n");
- if (!read_lock)
- bnx2x_vlan_mac_h_read_unlock(bp, obj);
+ list_for_each(pos, &obj->head)
+ cnt++;
- atomic_set(args->credit, cnt);
- }
-}
-
-static int bnx2x_vfop_set_user_req(struct bnx2x *bp,
- struct bnx2x_vfop_filter *pos,
- struct bnx2x_vlan_mac_data *user_req)
-{
- user_req->cmd = pos->add ? BNX2X_VLAN_MAC_ADD :
- BNX2X_VLAN_MAC_DEL;
+ if (!read_lock)
+ bnx2x_vlan_mac_h_read_unlock(bp, obj);
- switch (pos->type) {
- case BNX2X_VFOP_FILTER_MAC:
- memcpy(user_req->u.mac.mac, pos->mac, ETH_ALEN);
- break;
- case BNX2X_VFOP_FILTER_VLAN:
- user_req->u.vlan.vlan = pos->vid;
- break;
- default:
- BNX2X_ERR("Invalid filter type, skipping\n");
- return 1;
- }
- return 0;
+ atomic_set(counter, cnt);
}
-static int bnx2x_vfop_config_list(struct bnx2x *bp,
- struct bnx2x_vfop_filters *filters,
- struct bnx2x_vlan_mac_ramrod_params *vlan_mac)
+static int bnx2x_vf_vlan_mac_clear(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ int qid, bool drv_only, bool mac)
{
- struct bnx2x_vfop_filter *pos, *tmp;
- struct list_head rollback_list, *filters_list = &filters->head;
- struct bnx2x_vlan_mac_data *user_req = &vlan_mac->user_req;
- int rc = 0, cnt = 0;
-
- INIT_LIST_HEAD(&rollback_list);
-
- list_for_each_entry_safe(pos, tmp, filters_list, link) {
- if (bnx2x_vfop_set_user_req(bp, pos, user_req))
- continue;
+ struct bnx2x_vlan_mac_ramrod_params ramrod;
+ int rc;
- rc = bnx2x_config_vlan_mac(bp, vlan_mac);
- if (rc >= 0) {
- cnt += pos->add ? 1 : -1;
- list_move(&pos->link, &rollback_list);
- rc = 0;
- } else if (rc == -EEXIST) {
- rc = 0;
- } else {
- BNX2X_ERR("Failed to add a new vlan_mac command\n");
- break;
- }
- }
+ DP(BNX2X_MSG_IOV, "vf[%d] - deleting all %s\n", vf->abs_vfid,
+ mac ? "MACs" : "VLANs");
- /* rollback if error or too many rules added */
- if (rc || cnt > filters->add_cnt) {
- BNX2X_ERR("error or too many rules added. Performing rollback\n");
- list_for_each_entry_safe(pos, tmp, &rollback_list, link) {
- pos->add = !pos->add; /* reverse op */
- bnx2x_vfop_set_user_req(bp, pos, user_req);
- bnx2x_config_vlan_mac(bp, vlan_mac);
- list_del(&pos->link);
- }
- cnt = 0;
- if (!rc)
- rc = -EINVAL;
+ /* Prepare ramrod params */
+ memset(&ramrod, 0, sizeof(struct bnx2x_vlan_mac_ramrod_params));
+ if (mac) {
+ set_bit(BNX2X_ETH_MAC, &ramrod.user_req.vlan_mac_flags);
+ ramrod.vlan_mac_obj = &bnx2x_vfq(vf, qid, mac_obj);
+ } else {
+ set_bit(BNX2X_DONT_CONSUME_CAM_CREDIT,
+ &ramrod.user_req.vlan_mac_flags);
+ ramrod.vlan_mac_obj = &bnx2x_vfq(vf, qid, vlan_obj);
}
- filters->add_cnt = cnt;
- return rc;
-}
-
-/* VFOP set VLAN/MAC */
-static void bnx2x_vfop_vlan_mac(struct bnx2x *bp, struct bnx2x_virtf *vf)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- struct bnx2x_vlan_mac_ramrod_params *vlan_mac = &vfop->op_p->vlan_mac;
- struct bnx2x_vlan_mac_obj *obj = vlan_mac->vlan_mac_obj;
- struct bnx2x_vfop_filters *filters = vfop->args.filters.multi_filter;
-
- enum bnx2x_vfop_vlan_mac_state state = vfop->state;
-
- if (vfop->rc < 0)
- goto op_err;
-
- DP(BNX2X_MSG_IOV, "vf[%d] STATE: %d\n", vf->abs_vfid, state);
-
- bnx2x_vfop_reset_wq(vf);
-
- switch (state) {
- case BNX2X_VFOP_VLAN_MAC_CLEAR:
- /* next state */
- vfop->state = BNX2X_VFOP_VLAN_MAC_CHK_DONE;
-
- /* do delete */
- vfop->rc = obj->delete_all(bp, obj,
- &vlan_mac->user_req.vlan_mac_flags,
- &vlan_mac->ramrod_flags);
-
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
-
- case BNX2X_VFOP_VLAN_MAC_CONFIG_SINGLE:
- /* next state */
- vfop->state = BNX2X_VFOP_VLAN_MAC_CHK_DONE;
-
- /* do config */
- vfop->rc = bnx2x_config_vlan_mac(bp, vlan_mac);
- if (vfop->rc == -EEXIST)
- vfop->rc = 0;
+ ramrod.user_req.cmd = BNX2X_VLAN_MAC_DEL;
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
-
- case BNX2X_VFOP_VLAN_MAC_CHK_DONE:
- vfop->rc = !!obj->raw.check_pending(&obj->raw);
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_DONE);
-
- case BNX2X_VFOP_MAC_CONFIG_LIST:
- /* next state */
- vfop->state = BNX2X_VFOP_VLAN_MAC_CHK_DONE;
-
- /* do list config */
- vfop->rc = bnx2x_vfop_config_list(bp, filters, vlan_mac);
- if (vfop->rc)
- goto op_err;
-
- set_bit(RAMROD_CONT, &vlan_mac->ramrod_flags);
- vfop->rc = bnx2x_config_vlan_mac(bp, vlan_mac);
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
-
- case BNX2X_VFOP_VLAN_CONFIG_LIST:
- /* next state */
- vfop->state = BNX2X_VFOP_VLAN_MAC_CHK_DONE;
-
- /* do list config */
- vfop->rc = bnx2x_vfop_config_list(bp, filters, vlan_mac);
- if (!vfop->rc) {
- set_bit(RAMROD_CONT, &vlan_mac->ramrod_flags);
- vfop->rc = bnx2x_config_vlan_mac(bp, vlan_mac);
- }
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
+ set_bit(RAMROD_EXEC, &ramrod.ramrod_flags);
+ if (drv_only)
+ set_bit(RAMROD_DRV_CLR_ONLY, &ramrod.ramrod_flags);
+ else
+ set_bit(RAMROD_COMP_WAIT, &ramrod.ramrod_flags);
- default:
- bnx2x_vfop_default(state);
+ /* Start deleting */
+ rc = ramrod.vlan_mac_obj->delete_all(bp,
+ ramrod.vlan_mac_obj,
+ &ramrod.user_req.vlan_mac_flags,
+ &ramrod.ramrod_flags);
+ if (rc) {
+ BNX2X_ERR("Failed to delete all %s\n",
+ mac ? "MACs" : "VLANs");
+ return rc;
}
-op_err:
- BNX2X_ERR("VLAN-MAC error: rc %d\n", vfop->rc);
-op_done:
- kfree(filters);
- bnx2x_vfop_credit(bp, vfop, obj);
- bnx2x_vfop_end(bp, vf, vfop);
-op_pending:
- return;
-}
-
-struct bnx2x_vfop_vlan_mac_flags {
- bool drv_only;
- bool dont_consume;
- bool single_cmd;
- bool add;
-};
-
-static void
-bnx2x_vfop_vlan_mac_prep_ramrod(struct bnx2x_vlan_mac_ramrod_params *ramrod,
- struct bnx2x_vfop_vlan_mac_flags *flags)
-{
- struct bnx2x_vlan_mac_data *ureq = &ramrod->user_req;
-
- memset(ramrod, 0, sizeof(*ramrod));
- /* ramrod flags */
- if (flags->drv_only)
- set_bit(RAMROD_DRV_CLR_ONLY, &ramrod->ramrod_flags);
- if (flags->single_cmd)
- set_bit(RAMROD_EXEC, &ramrod->ramrod_flags);
+ /* Clear the vlan counters */
+ if (!mac)
+ atomic_set(&bnx2x_vfq(vf, qid, vlan_count), 0);
- /* mac_vlan flags */
- if (flags->dont_consume)
- set_bit(BNX2X_DONT_CONSUME_CAM_CREDIT, &ureq->vlan_mac_flags);
-
- /* cmd */
- ureq->cmd = flags->add ? BNX2X_VLAN_MAC_ADD : BNX2X_VLAN_MAC_DEL;
-}
-
-static inline void
-bnx2x_vfop_mac_prep_ramrod(struct bnx2x_vlan_mac_ramrod_params *ramrod,
- struct bnx2x_vfop_vlan_mac_flags *flags)
-{
- bnx2x_vfop_vlan_mac_prep_ramrod(ramrod, flags);
- set_bit(BNX2X_ETH_MAC, &ramrod->user_req.vlan_mac_flags);
+ return 0;
}
-static int bnx2x_vfop_mac_delall_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid, bool drv_only)
+static int bnx2x_vf_mac_vlan_config(struct bnx2x *bp,
+ struct bnx2x_virtf *vf, int qid,
+ struct bnx2x_vf_mac_vlan_filter *filter,
+ bool drv_only)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
+ struct bnx2x_vlan_mac_ramrod_params ramrod;
int rc;
- if (vfop) {
- struct bnx2x_vfop_args_filters filters = {
- .multi_filter = NULL, /* single */
- .credit = NULL, /* consume credit */
- };
- struct bnx2x_vfop_vlan_mac_flags flags = {
- .drv_only = drv_only,
- .dont_consume = (filters.credit != NULL),
- .single_cmd = true,
- .add = false /* don't care */,
- };
- struct bnx2x_vlan_mac_ramrod_params *ramrod =
- &vf->op_params.vlan_mac;
-
- /* set ramrod params */
- bnx2x_vfop_mac_prep_ramrod(ramrod, &flags);
-
- /* set object */
- rc = validate_vlan_mac(bp, &bnx2x_vfq(vf, qid, mac_obj));
- if (rc)
- return rc;
- ramrod->vlan_mac_obj = &bnx2x_vfq(vf, qid, mac_obj);
-
- /* set extra args */
- vfop->args.filters = filters;
-
- bnx2x_vfop_opset(BNX2X_VFOP_VLAN_MAC_CLEAR,
- bnx2x_vfop_vlan_mac, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_vlan_mac,
- cmd->block);
+ DP(BNX2X_MSG_IOV, "vf[%d] - %s a %s filter\n",
+ vf->abs_vfid, filter->add ? "Adding" : "Deleting",
+ filter->type == BNX2X_VF_FILTER_MAC ? "MAC" : "VLAN");
+
+ /* Prepare ramrod params */
+ memset(&ramrod, 0, sizeof(struct bnx2x_vlan_mac_ramrod_params));
+ if (filter->type == BNX2X_VF_FILTER_VLAN) {
+ set_bit(BNX2X_DONT_CONSUME_CAM_CREDIT,
+ &ramrod.user_req.vlan_mac_flags);
+ ramrod.vlan_mac_obj = &bnx2x_vfq(vf, qid, vlan_obj);
+ ramrod.user_req.u.vlan.vlan = filter->vid;
+ } else {
+ set_bit(BNX2X_ETH_MAC, &ramrod.user_req.vlan_mac_flags);
+ ramrod.vlan_mac_obj = &bnx2x_vfq(vf, qid, mac_obj);
+ memcpy(&ramrod.user_req.u.mac.mac, filter->mac, ETH_ALEN);
+ }
+ ramrod.user_req.cmd = filter->add ? BNX2X_VLAN_MAC_ADD :
+ BNX2X_VLAN_MAC_DEL;
+
+ /* Verify there are available vlan credits */
+ if (filter->add && filter->type == BNX2X_VF_FILTER_VLAN &&
+ (atomic_read(&bnx2x_vfq(vf, qid, vlan_count)) >=
+ vf_vlan_rules_cnt(vf))) {
+ BNX2X_ERR("No credits for vlan\n");
+ return -ENOMEM;
}
- return -ENOMEM;
-}
-
-int bnx2x_vfop_mac_list_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- struct bnx2x_vfop_filters *macs,
- int qid, bool drv_only)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
- int rc;
- if (vfop) {
- struct bnx2x_vfop_args_filters filters = {
- .multi_filter = macs,
- .credit = NULL, /* consume credit */
- };
- struct bnx2x_vfop_vlan_mac_flags flags = {
- .drv_only = drv_only,
- .dont_consume = (filters.credit != NULL),
- .single_cmd = false,
- .add = false, /* don't care since only the items in the
- * filters list affect the sp operation,
- * not the list itself
- */
- };
- struct bnx2x_vlan_mac_ramrod_params *ramrod =
- &vf->op_params.vlan_mac;
-
- /* set ramrod params */
- bnx2x_vfop_mac_prep_ramrod(ramrod, &flags);
-
- /* set object */
- rc = validate_vlan_mac(bp, &bnx2x_vfq(vf, qid, mac_obj));
- if (rc)
- return rc;
- ramrod->vlan_mac_obj = &bnx2x_vfq(vf, qid, mac_obj);
+ set_bit(RAMROD_EXEC, &ramrod.ramrod_flags);
+ if (drv_only)
+ set_bit(RAMROD_DRV_CLR_ONLY, &ramrod.ramrod_flags);
+ else
+ set_bit(RAMROD_COMP_WAIT, &ramrod.ramrod_flags);
+
+ /* Add/Remove the filter */
+ rc = bnx2x_config_vlan_mac(bp, &ramrod);
+ if (rc && rc != -EEXIST) {
+ BNX2X_ERR("Failed to %s %s\n",
+ filter->add ? "add" : "delete",
+ filter->type == BNX2X_VF_FILTER_MAC ? "MAC" :
+ "VLAN");
+ return rc;
+ }
- /* set extra args */
- filters.multi_filter->add_cnt = BNX2X_VFOP_FILTER_ADD_CNT_MAX;
- vfop->args.filters = filters;
+ /* Update the vlan counters */
+ if (filter->type == BNX2X_VF_FILTER_VLAN)
+ bnx2x_vf_vlan_credit(bp, ramrod.vlan_mac_obj,
+ &bnx2x_vfq(vf, qid, vlan_count));
- bnx2x_vfop_opset(BNX2X_VFOP_MAC_CONFIG_LIST,
- bnx2x_vfop_vlan_mac, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_vlan_mac,
- cmd->block);
- }
- return -ENOMEM;
+ return 0;
}
-static int bnx2x_vfop_vlan_set_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid, u16 vid, bool add)
+int bnx2x_vf_mac_vlan_config_list(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ struct bnx2x_vf_mac_vlan_filters *filters,
+ int qid, bool drv_only)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
- int rc;
+ int rc = 0, i;
- if (vfop) {
- struct bnx2x_vfop_args_filters filters = {
- .multi_filter = NULL, /* single command */
- .credit = &bnx2x_vfq(vf, qid, vlan_count),
- };
- struct bnx2x_vfop_vlan_mac_flags flags = {
- .drv_only = false,
- .dont_consume = (filters.credit != NULL),
- .single_cmd = true,
- .add = add,
- };
- struct bnx2x_vlan_mac_ramrod_params *ramrod =
- &vf->op_params.vlan_mac;
-
- /* set ramrod params */
- bnx2x_vfop_vlan_mac_prep_ramrod(ramrod, &flags);
- ramrod->user_req.u.vlan.vlan = vid;
-
- /* set object */
- rc = validate_vlan_mac(bp, &bnx2x_vfq(vf, qid, vlan_obj));
- if (rc)
- return rc;
- ramrod->vlan_mac_obj = &bnx2x_vfq(vf, qid, vlan_obj);
+ DP(BNX2X_MSG_IOV, "vf[%d]\n", vf->abs_vfid);
- /* set extra args */
- vfop->args.filters = filters;
+ if (!bnx2x_validate_vf_sp_objs(bp, vf, true))
+ return -EINVAL;
- bnx2x_vfop_opset(BNX2X_VFOP_VLAN_MAC_CONFIG_SINGLE,
- bnx2x_vfop_vlan_mac, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_vlan_mac,
- cmd->block);
+ /* Prepare ramrod params */
+ for (i = 0; i < filters->count; i++) {
+ rc = bnx2x_vf_mac_vlan_config(bp, vf, qid,
+ &filters->filters[i], drv_only);
+ if (rc)
+ break;
}
- return -ENOMEM;
-}
-
-static int bnx2x_vfop_vlan_delall_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid, bool drv_only)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
- int rc;
- if (vfop) {
- struct bnx2x_vfop_args_filters filters = {
- .multi_filter = NULL, /* single command */
- .credit = &bnx2x_vfq(vf, qid, vlan_count),
- };
- struct bnx2x_vfop_vlan_mac_flags flags = {
- .drv_only = drv_only,
- .dont_consume = (filters.credit != NULL),
- .single_cmd = true,
- .add = false, /* don't care */
- };
- struct bnx2x_vlan_mac_ramrod_params *ramrod =
- &vf->op_params.vlan_mac;
-
- /* set ramrod params */
- bnx2x_vfop_vlan_mac_prep_ramrod(ramrod, &flags);
-
- /* set object */
- rc = validate_vlan_mac(bp, &bnx2x_vfq(vf, qid, vlan_obj));
- if (rc)
- return rc;
- ramrod->vlan_mac_obj = &bnx2x_vfq(vf, qid, vlan_obj);
+ /* Rollback if needed */
+ if (i != filters->count) {
+ BNX2X_ERR("Managed only %d/%d filters - rolling back\n",
+ i, filters->count + 1);
+ while (--i >= 0) {
+ filters->filters[i].add = !filters->filters[i].add;
+ bnx2x_vf_mac_vlan_config(bp, vf, qid,
+ &filters->filters[i],
+ drv_only);
+ }
+ }
- /* set extra args */
- vfop->args.filters = filters;
+ /* It's our responsibility to free the filters */
+ kfree(filters);
- bnx2x_vfop_opset(BNX2X_VFOP_VLAN_MAC_CLEAR,
- bnx2x_vfop_vlan_mac, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_vlan_mac,
- cmd->block);
- }
- return -ENOMEM;
+ return rc;
}
-int bnx2x_vfop_vlan_list_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- struct bnx2x_vfop_filters *vlans,
- int qid, bool drv_only)
+int bnx2x_vf_queue_setup(struct bnx2x *bp, struct bnx2x_virtf *vf, int qid,
+ struct bnx2x_vf_queue_construct_params *qctor)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
int rc;
- if (vfop) {
- struct bnx2x_vfop_args_filters filters = {
- .multi_filter = vlans,
- .credit = &bnx2x_vfq(vf, qid, vlan_count),
- };
- struct bnx2x_vfop_vlan_mac_flags flags = {
- .drv_only = drv_only,
- .dont_consume = (filters.credit != NULL),
- .single_cmd = false,
- .add = false, /* don't care */
- };
- struct bnx2x_vlan_mac_ramrod_params *ramrod =
- &vf->op_params.vlan_mac;
-
- /* set ramrod params */
- bnx2x_vfop_vlan_mac_prep_ramrod(ramrod, &flags);
-
- /* set object */
- rc = validate_vlan_mac(bp, &bnx2x_vfq(vf, qid, vlan_obj));
- if (rc)
- return rc;
- ramrod->vlan_mac_obj = &bnx2x_vfq(vf, qid, vlan_obj);
-
- /* set extra args */
- filters.multi_filter->add_cnt = vf_vlan_rules_cnt(vf) -
- atomic_read(filters.credit);
-
- vfop->args.filters = filters;
+ DP(BNX2X_MSG_IOV, "vf[%d:%d]\n", vf->abs_vfid, qid);
- bnx2x_vfop_opset(BNX2X_VFOP_VLAN_CONFIG_LIST,
- bnx2x_vfop_vlan_mac, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_vlan_mac,
- cmd->block);
- }
- return -ENOMEM;
-}
-
-/* VFOP queue setup (queue constructor + set vlan 0) */
-static void bnx2x_vfop_qsetup(struct bnx2x *bp, struct bnx2x_virtf *vf)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- int qid = vfop->args.qctor.qid;
- enum bnx2x_vfop_qsetup_state state = vfop->state;
- struct bnx2x_vfop_cmd cmd = {
- .done = bnx2x_vfop_qsetup,
- .block = false,
- };
-
- if (vfop->rc < 0)
+ rc = bnx2x_vf_queue_create(bp, vf, qid, qctor);
+ if (rc)
goto op_err;
- DP(BNX2X_MSG_IOV, "vf[%d] STATE: %d\n", vf->abs_vfid, state);
+ /* Configure vlan0 for leading queue */
+ if (!qid) {
+ struct bnx2x_vf_mac_vlan_filter filter;
- switch (state) {
- case BNX2X_VFOP_QSETUP_CTOR:
- /* init the queue ctor command */
- vfop->state = BNX2X_VFOP_QSETUP_VLAN0;
- vfop->rc = bnx2x_vfop_qctor_cmd(bp, vf, &cmd, qid);
- if (vfop->rc)
+ memset(&filter, 0, sizeof(struct bnx2x_vf_mac_vlan_filter));
+ filter.type = BNX2X_VF_FILTER_VLAN;
+ filter.add = true;
+ filter.vid = 0;
+ rc = bnx2x_vf_mac_vlan_config(bp, vf, qid, &filter, false);
+ if (rc)
goto op_err;
- return;
-
- case BNX2X_VFOP_QSETUP_VLAN0:
- /* skip if non-leading or FPGA/EMU*/
- if (qid)
- goto op_done;
+ }
- /* init the queue set-vlan command (for vlan 0) */
- vfop->state = BNX2X_VFOP_QSETUP_DONE;
- vfop->rc = bnx2x_vfop_vlan_set_cmd(bp, vf, &cmd, qid, 0, true);
- if (vfop->rc)
- goto op_err;
- return;
+ /* Schedule the configuration of any pending vlan filters */
+ vf->cfg_flags |= VF_CFG_VLAN;
+ bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_HYPERVISOR_VLAN,
+ BNX2X_MSG_IOV);
+ return 0;
op_err:
- BNX2X_ERR("QSETUP[%d:%d] error: rc %d\n", vf->abs_vfid, qid, vfop->rc);
-op_done:
- case BNX2X_VFOP_QSETUP_DONE:
- vf->cfg_flags |= VF_CFG_VLAN;
- smp_mb__before_clear_bit();
- set_bit(BNX2X_SP_RTNL_HYPERVISOR_VLAN,
- &bp->sp_rtnl_state);
- smp_mb__after_clear_bit();
- schedule_delayed_work(&bp->sp_rtnl_task, 0);
- bnx2x_vfop_end(bp, vf, vfop);
- return;
- default:
- bnx2x_vfop_default(state);
- }
+ BNX2X_ERR("QSETUP[%d:%d] error: rc %d\n", vf->abs_vfid, qid, rc);
+ return rc;
}
-int bnx2x_vfop_qsetup_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid)
+static int bnx2x_vf_queue_flr(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ int qid)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
+ int rc;
- if (vfop) {
- vfop->args.qctor.qid = qid;
+ DP(BNX2X_MSG_IOV, "vf[%d:%d]\n", vf->abs_vfid, qid);
- bnx2x_vfop_opset(BNX2X_VFOP_QSETUP_CTOR,
- bnx2x_vfop_qsetup, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_qsetup,
- cmd->block);
+ /* If needed, clean the filtering data base */
+ if ((qid == LEADING_IDX) &&
+ bnx2x_validate_vf_sp_objs(bp, vf, false)) {
+ rc = bnx2x_vf_vlan_mac_clear(bp, vf, qid, true, false);
+ if (rc)
+ goto op_err;
+ rc = bnx2x_vf_vlan_mac_clear(bp, vf, qid, true, true);
+ if (rc)
+ goto op_err;
}
- return -ENOMEM;
-}
-
-/* VFOP queue FLR handling (clear vlans, clear macs, queue destructor) */
-static void bnx2x_vfop_qflr(struct bnx2x *bp, struct bnx2x_virtf *vf)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- int qid = vfop->args.qx.qid;
- enum bnx2x_vfop_qflr_state state = vfop->state;
- struct bnx2x_queue_state_params *qstate;
- struct bnx2x_vfop_cmd cmd;
-
- bnx2x_vfop_reset_wq(vf);
-
- if (vfop->rc < 0)
- goto op_err;
- DP(BNX2X_MSG_IOV, "VF[%d] STATE: %d\n", vf->abs_vfid, state);
+ /* Terminate queue */
+ if (bnx2x_vfq(vf, qid, sp_obj).state != BNX2X_Q_STATE_RESET) {
+ struct bnx2x_queue_state_params qstate;
- cmd.done = bnx2x_vfop_qflr;
- cmd.block = false;
-
- switch (state) {
- case BNX2X_VFOP_QFLR_CLR_VLAN:
- /* vlan-clear-all: driver-only, don't consume credit */
- vfop->state = BNX2X_VFOP_QFLR_CLR_MAC;
-
- if (!validate_vlan_mac(bp, &bnx2x_vfq(vf, qid, vlan_obj))) {
- /* the vlan_mac vfop will re-schedule us */
- vfop->rc = bnx2x_vfop_vlan_delall_cmd(bp, vf, &cmd,
- qid, true);
- if (vfop->rc)
- goto op_err;
- return;
-
- } else {
- /* need to reschedule ourselves */
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
- }
-
- case BNX2X_VFOP_QFLR_CLR_MAC:
- /* mac-clear-all: driver only consume credit */
- vfop->state = BNX2X_VFOP_QFLR_TERMINATE;
- if (!validate_vlan_mac(bp, &bnx2x_vfq(vf, qid, mac_obj))) {
- /* the vlan_mac vfop will re-schedule us */
- vfop->rc = bnx2x_vfop_mac_delall_cmd(bp, vf, &cmd,
- qid, true);
- if (vfop->rc)
- goto op_err;
- return;
-
- } else {
- /* need to reschedule ourselves */
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
- }
-
- case BNX2X_VFOP_QFLR_TERMINATE:
- qstate = &vfop->op_p->qctor.qstate;
- memset(qstate , 0, sizeof(*qstate));
- qstate->q_obj = &bnx2x_vfq(vf, qid, sp_obj);
- vfop->state = BNX2X_VFOP_QFLR_DONE;
-
- DP(BNX2X_MSG_IOV, "VF[%d] qstate during flr was %d\n",
- vf->abs_vfid, qstate->q_obj->state);
-
- if (qstate->q_obj->state != BNX2X_Q_STATE_RESET) {
- qstate->q_obj->state = BNX2X_Q_STATE_STOPPED;
- qstate->cmd = BNX2X_Q_CMD_TERMINATE;
- vfop->rc = bnx2x_queue_state_change(bp, qstate);
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_VERIFY_PEND);
- } else {
- goto op_done;
- }
-
-op_err:
- BNX2X_ERR("QFLR[%d:%d] error: rc %d\n",
- vf->abs_vfid, qid, vfop->rc);
-op_done:
- case BNX2X_VFOP_QFLR_DONE:
- bnx2x_vfop_end(bp, vf, vfop);
- return;
- default:
- bnx2x_vfop_default(state);
+ memset(&qstate, 0, sizeof(struct bnx2x_queue_state_params));
+ qstate.q_obj = &bnx2x_vfq(vf, qid, sp_obj);
+ qstate.q_obj->state = BNX2X_Q_STATE_STOPPED;
+ qstate.cmd = BNX2X_Q_CMD_TERMINATE;
+ set_bit(RAMROD_COMP_WAIT, &qstate.ramrod_flags);
+ rc = bnx2x_queue_state_change(bp, &qstate);
+ if (rc)
+ goto op_err;
}
-op_pending:
- return;
-}
-
-static int bnx2x_vfop_qflr_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
- if (vfop) {
- vfop->args.qx.qid = qid;
- bnx2x_vfop_opset(BNX2X_VFOP_QFLR_CLR_VLAN,
- bnx2x_vfop_qflr, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_qflr,
- cmd->block);
- }
- return -ENOMEM;
+ return 0;
+op_err:
+ BNX2X_ERR("vf[%d:%d] error: rc %d\n", vf->abs_vfid, qid, rc);
+ return rc;
}
-/* VFOP multi-casts */
-static void bnx2x_vfop_mcast(struct bnx2x *bp, struct bnx2x_virtf *vf)
+int bnx2x_vf_mcast(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ bnx2x_mac_addr_t *mcasts, int mc_num, bool drv_only)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- struct bnx2x_mcast_ramrod_params *mcast = &vfop->op_p->mcast;
- struct bnx2x_raw_obj *raw = &mcast->mcast_obj->raw;
- struct bnx2x_vfop_args_mcast *args = &vfop->args.mc_list;
- enum bnx2x_vfop_mcast_state state = vfop->state;
- int i;
-
- bnx2x_vfop_reset_wq(vf);
-
- if (vfop->rc < 0)
- goto op_err;
+ struct bnx2x_mcast_list_elem *mc = NULL;
+ struct bnx2x_mcast_ramrod_params mcast;
+ int rc, i;
- DP(BNX2X_MSG_IOV, "vf[%d] STATE: %d\n", vf->abs_vfid, state);
-
- switch (state) {
- case BNX2X_VFOP_MCAST_DEL:
- /* clear existing mcasts */
- vfop->state = (args->mc_num) ? BNX2X_VFOP_MCAST_ADD
- : BNX2X_VFOP_MCAST_CHK_DONE;
- mcast->mcast_list_len = vf->mcast_list_len;
- vf->mcast_list_len = args->mc_num;
- vfop->rc = bnx2x_config_mcast(bp, mcast, BNX2X_MCAST_CMD_DEL);
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
-
- case BNX2X_VFOP_MCAST_ADD:
- if (raw->check_pending(raw))
- goto op_pending;
-
- /* update mcast list on the ramrod params */
- INIT_LIST_HEAD(&mcast->mcast_list);
- for (i = 0; i < args->mc_num; i++)
- list_add_tail(&(args->mc[i].link),
- &mcast->mcast_list);
- mcast->mcast_list_len = args->mc_num;
+ DP(BNX2X_MSG_IOV, "vf[%d]\n", vf->abs_vfid);
- /* add new mcasts */
- vfop->state = BNX2X_VFOP_MCAST_CHK_DONE;
- vfop->rc = bnx2x_config_mcast(bp, mcast,
- BNX2X_MCAST_CMD_ADD);
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_DONE);
-
- case BNX2X_VFOP_MCAST_CHK_DONE:
- vfop->rc = raw->check_pending(raw) ? 1 : 0;
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_DONE);
- default:
- bnx2x_vfop_default(state);
+ /* Prepare Multicast command */
+ memset(&mcast, 0, sizeof(struct bnx2x_mcast_ramrod_params));
+ mcast.mcast_obj = &vf->mcast_obj;
+ if (drv_only)
+ set_bit(RAMROD_DRV_CLR_ONLY, &mcast.ramrod_flags);
+ else
+ set_bit(RAMROD_COMP_WAIT, &mcast.ramrod_flags);
+ if (mc_num) {
+ mc = kzalloc(mc_num * sizeof(struct bnx2x_mcast_list_elem),
+ GFP_KERNEL);
+ if (!mc) {
+ BNX2X_ERR("Cannot Configure mulicasts due to lack of memory\n");
+ return -ENOMEM;
+ }
}
-op_err:
- BNX2X_ERR("MCAST CONFIG error: rc %d\n", vfop->rc);
-op_done:
- kfree(args->mc);
- bnx2x_vfop_end(bp, vf, vfop);
-op_pending:
- return;
-}
-int bnx2x_vfop_mcast_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- bnx2x_mac_addr_t *mcasts,
- int mcast_num, bool drv_only)
-{
- struct bnx2x_vfop *vfop = NULL;
- size_t mc_sz = mcast_num * sizeof(struct bnx2x_mcast_list_elem);
- struct bnx2x_mcast_list_elem *mc = mc_sz ? kzalloc(mc_sz, GFP_KERNEL) :
- NULL;
-
- if (!mc_sz || mc) {
- vfop = bnx2x_vfop_add(bp, vf);
- if (vfop) {
- int i;
- struct bnx2x_mcast_ramrod_params *ramrod =
- &vf->op_params.mcast;
-
- /* set ramrod params */
- memset(ramrod, 0, sizeof(*ramrod));
- ramrod->mcast_obj = &vf->mcast_obj;
- if (drv_only)
- set_bit(RAMROD_DRV_CLR_ONLY,
- &ramrod->ramrod_flags);
-
- /* copy mcasts pointers */
- vfop->args.mc_list.mc_num = mcast_num;
- vfop->args.mc_list.mc = mc;
- for (i = 0; i < mcast_num; i++)
- mc[i].mac = mcasts[i];
-
- bnx2x_vfop_opset(BNX2X_VFOP_MCAST_DEL,
- bnx2x_vfop_mcast, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_mcast,
- cmd->block);
- } else {
+ /* clear existing mcasts */
+ mcast.mcast_list_len = vf->mcast_list_len;
+ vf->mcast_list_len = mc_num;
+ rc = bnx2x_config_mcast(bp, &mcast, BNX2X_MCAST_CMD_DEL);
+ if (rc) {
+ BNX2X_ERR("Failed to remove multicasts\n");
+ if (mc)
kfree(mc);
- }
+ return rc;
}
- return -ENOMEM;
-}
-
-/* VFOP rx-mode */
-static void bnx2x_vfop_rxmode(struct bnx2x *bp, struct bnx2x_virtf *vf)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- struct bnx2x_rx_mode_ramrod_params *ramrod = &vfop->op_p->rx_mode;
- enum bnx2x_vfop_rxmode_state state = vfop->state;
-
- bnx2x_vfop_reset_wq(vf);
-
- if (vfop->rc < 0)
- goto op_err;
- DP(BNX2X_MSG_IOV, "vf[%d] STATE: %d\n", vf->abs_vfid, state);
-
- switch (state) {
- case BNX2X_VFOP_RXMODE_CONFIG:
- /* next state */
- vfop->state = BNX2X_VFOP_RXMODE_DONE;
+ /* update mcast list on the ramrod params */
+ if (mc_num) {
+ INIT_LIST_HEAD(&mcast.mcast_list);
+ for (i = 0; i < mc_num; i++) {
+ mc[i].mac = mcasts[i];
+ list_add_tail(&mc[i].link,
+ &mcast.mcast_list);
+ }
- /* record the accept flags in vfdb so hypervisor can modify them
- * if necessary
- */
- bnx2x_vfq(vf, ramrod->cl_id - vf->igu_base_id, accept_flags) =
- ramrod->rx_accept_flags;
- vfop->rc = bnx2x_config_rx_mode(bp, ramrod);
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_DONE);
-op_err:
- BNX2X_ERR("RXMODE error: rc %d\n", vfop->rc);
-op_done:
- case BNX2X_VFOP_RXMODE_DONE:
- bnx2x_vfop_end(bp, vf, vfop);
- return;
- default:
- bnx2x_vfop_default(state);
+ /* add new mcasts */
+ rc = bnx2x_config_mcast(bp, &mcast, BNX2X_MCAST_CMD_ADD);
+ if (rc)
+ BNX2X_ERR("Faled to add multicasts\n");
+ kfree(mc);
}
-op_pending:
- return;
+
+ return rc;
}
static void bnx2x_vf_prep_rx_mode(struct bnx2x *bp, u8 qid,
@@ -1268,118 +644,56 @@ static void bnx2x_vf_prep_rx_mode(struct bnx2x *bp, u8 qid,
ramrod->rdata_mapping = bnx2x_vf_sp_map(bp, vf, rx_mode_rdata.e2);
}
-int bnx2x_vfop_rxmode_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid, unsigned long accept_flags)
+int bnx2x_vf_rxmode(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ int qid, unsigned long accept_flags)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
-
- if (vfop) {
- struct bnx2x_rx_mode_ramrod_params *ramrod =
- &vf->op_params.rx_mode;
+ struct bnx2x_rx_mode_ramrod_params ramrod;
- bnx2x_vf_prep_rx_mode(bp, qid, ramrod, vf, accept_flags);
+ DP(BNX2X_MSG_IOV, "vf[%d]\n", vf->abs_vfid);
- bnx2x_vfop_opset(BNX2X_VFOP_RXMODE_CONFIG,
- bnx2x_vfop_rxmode, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_rxmode,
- cmd->block);
- }
- return -ENOMEM;
+ bnx2x_vf_prep_rx_mode(bp, qid, &ramrod, vf, accept_flags);
+ set_bit(RAMROD_COMP_WAIT, &ramrod.ramrod_flags);
+ vfq_get(vf, qid)->accept_flags = ramrod.rx_accept_flags;
+ return bnx2x_config_rx_mode(bp, &ramrod);
}
-/* VFOP queue tear-down ('drop all' rx-mode, clear vlans, clear macs,
- * queue destructor)
- */
-static void bnx2x_vfop_qdown(struct bnx2x *bp, struct bnx2x_virtf *vf)
+int bnx2x_vf_queue_teardown(struct bnx2x *bp, struct bnx2x_virtf *vf, int qid)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- int qid = vfop->args.qx.qid;
- enum bnx2x_vfop_qteardown_state state = vfop->state;
- struct bnx2x_vfop_cmd cmd;
-
- if (vfop->rc < 0)
- goto op_err;
-
- DP(BNX2X_MSG_IOV, "vf[%d] STATE: %d\n", vf->abs_vfid, state);
-
- cmd.done = bnx2x_vfop_qdown;
- cmd.block = false;
-
- switch (state) {
- case BNX2X_VFOP_QTEARDOWN_RXMODE:
- /* Drop all */
- vfop->state = BNX2X_VFOP_QTEARDOWN_CLR_VLAN;
- vfop->rc = bnx2x_vfop_rxmode_cmd(bp, vf, &cmd, qid, 0);
- if (vfop->rc)
- goto op_err;
- return;
-
- case BNX2X_VFOP_QTEARDOWN_CLR_VLAN:
- /* vlan-clear-all: don't consume credit */
- vfop->state = BNX2X_VFOP_QTEARDOWN_CLR_MAC;
- vfop->rc = bnx2x_vfop_vlan_delall_cmd(bp, vf, &cmd, qid, false);
- if (vfop->rc)
- goto op_err;
- return;
-
- case BNX2X_VFOP_QTEARDOWN_CLR_MAC:
- /* mac-clear-all: consume credit */
- vfop->state = BNX2X_VFOP_QTEARDOWN_CLR_MCAST;
- vfop->rc = bnx2x_vfop_mac_delall_cmd(bp, vf, &cmd, qid, false);
- if (vfop->rc)
- goto op_err;
- return;
+ int rc;
- case BNX2X_VFOP_QTEARDOWN_CLR_MCAST:
- vfop->state = BNX2X_VFOP_QTEARDOWN_QDTOR;
- vfop->rc = bnx2x_vfop_mcast_cmd(bp, vf, &cmd, NULL, 0, false);
- if (vfop->rc)
- goto op_err;
- return;
+ DP(BNX2X_MSG_IOV, "vf[%d:%d]\n", vf->abs_vfid, qid);
- case BNX2X_VFOP_QTEARDOWN_QDTOR:
- /* run the queue destruction flow */
- DP(BNX2X_MSG_IOV, "case: BNX2X_VFOP_QTEARDOWN_QDTOR\n");
- vfop->state = BNX2X_VFOP_QTEARDOWN_DONE;
- DP(BNX2X_MSG_IOV, "new state: BNX2X_VFOP_QTEARDOWN_DONE\n");
- vfop->rc = bnx2x_vfop_qdtor_cmd(bp, vf, &cmd, qid);
- DP(BNX2X_MSG_IOV, "returned from cmd\n");
- if (vfop->rc)
+ /* Remove all classification configuration for leading queue */
+ if (qid == LEADING_IDX) {
+ rc = bnx2x_vf_rxmode(bp, vf, qid, 0);
+ if (rc)
goto op_err;
- return;
-op_err:
- BNX2X_ERR("QTEARDOWN[%d:%d] error: rc %d\n",
- vf->abs_vfid, qid, vfop->rc);
-
- case BNX2X_VFOP_QTEARDOWN_DONE:
- bnx2x_vfop_end(bp, vf, vfop);
- return;
- default:
- bnx2x_vfop_default(state);
- }
-}
-int bnx2x_vfop_qdown_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
-
- /* for non leading queues skip directly to qdown sate */
- if (vfop) {
- vfop->args.qx.qid = qid;
- bnx2x_vfop_opset(qid == LEADING_IDX ?
- BNX2X_VFOP_QTEARDOWN_RXMODE :
- BNX2X_VFOP_QTEARDOWN_QDTOR, bnx2x_vfop_qdown,
- cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_qdown,
- cmd->block);
+ /* Remove filtering if feasible */
+ if (bnx2x_validate_vf_sp_objs(bp, vf, true)) {
+ rc = bnx2x_vf_vlan_mac_clear(bp, vf, qid,
+ false, false);
+ if (rc)
+ goto op_err;
+ rc = bnx2x_vf_vlan_mac_clear(bp, vf, qid,
+ false, true);
+ if (rc)
+ goto op_err;
+ rc = bnx2x_vf_mcast(bp, vf, NULL, 0, false);
+ if (rc)
+ goto op_err;
+ }
}
- return -ENOMEM;
+ /* Destroy queue */
+ rc = bnx2x_vf_queue_destroy(bp, vf, qid);
+ if (rc)
+ goto op_err;
+ return rc;
+op_err:
+ BNX2X_ERR("vf[%d:%d] error: rc %d\n",
+ vf->abs_vfid, qid, rc);
+ return rc;
}
/* VF enable primitives
@@ -1579,120 +893,63 @@ static void bnx2x_vf_flr_clnup_hw(struct bnx2x *bp, struct bnx2x_virtf *vf)
bnx2x_tx_hw_flushed(bp, poll_cnt);
}
-static void bnx2x_vfop_flr(struct bnx2x *bp, struct bnx2x_virtf *vf)
+static void bnx2x_vf_flr(struct bnx2x *bp, struct bnx2x_virtf *vf)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- struct bnx2x_vfop_args_qx *qx = &vfop->args.qx;
- enum bnx2x_vfop_flr_state state = vfop->state;
- struct bnx2x_vfop_cmd cmd = {
- .done = bnx2x_vfop_flr,
- .block = false,
- };
-
- if (vfop->rc < 0)
- goto op_err;
-
- DP(BNX2X_MSG_IOV, "vf[%d] STATE: %d\n", vf->abs_vfid, state);
+ int rc, i;
- switch (state) {
- case BNX2X_VFOP_FLR_QUEUES:
- /* the cleanup operations are valid if and only if the VF
- * was first acquired.
- */
- if (++(qx->qid) < vf_rxq_count(vf)) {
- vfop->rc = bnx2x_vfop_qflr_cmd(bp, vf, &cmd,
- qx->qid);
- if (vfop->rc)
- goto op_err;
- return;
- }
- /* remove multicasts */
- vfop->state = BNX2X_VFOP_FLR_HW;
- vfop->rc = bnx2x_vfop_mcast_cmd(bp, vf, &cmd, NULL,
- 0, true);
- if (vfop->rc)
- goto op_err;
- return;
- case BNX2X_VFOP_FLR_HW:
+ DP(BNX2X_MSG_IOV, "vf[%d]\n", vf->abs_vfid);
- /* dispatch final cleanup and wait for HW queues to flush */
- bnx2x_vf_flr_clnup_hw(bp, vf);
+ /* the cleanup operations are valid if and only if the VF
+ * was first acquired.
+ */
+ for (i = 0; i < vf_rxq_count(vf); i++) {
+ rc = bnx2x_vf_queue_flr(bp, vf, i);
+ if (rc)
+ goto out;
+ }
- /* release VF resources */
- bnx2x_vf_free_resc(bp, vf);
+ /* remove multicasts */
+ bnx2x_vf_mcast(bp, vf, NULL, 0, true);
- /* re-open the mailbox */
- bnx2x_vf_enable_mbx(bp, vf->abs_vfid);
+ /* dispatch final cleanup and wait for HW queues to flush */
+ bnx2x_vf_flr_clnup_hw(bp, vf);
- goto op_done;
- default:
- bnx2x_vfop_default(state);
- }
-op_err:
- BNX2X_ERR("VF[%d] FLR error: rc %d\n", vf->abs_vfid, vfop->rc);
-op_done:
- vf->flr_clnup_stage = VF_FLR_ACK;
- bnx2x_vfop_end(bp, vf, vfop);
- bnx2x_unlock_vf_pf_channel(bp, vf, CHANNEL_TLV_FLR);
-}
+ /* release VF resources */
+ bnx2x_vf_free_resc(bp, vf);
-static int bnx2x_vfop_flr_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- vfop_handler_t done)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
- if (vfop) {
- vfop->args.qx.qid = -1; /* loop */
- bnx2x_vfop_opset(BNX2X_VFOP_FLR_QUEUES,
- bnx2x_vfop_flr, done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_flr, false);
- }
- return -ENOMEM;
+ /* re-open the mailbox */
+ bnx2x_vf_enable_mbx(bp, vf->abs_vfid);
+ return;
+out:
+ BNX2X_ERR("vf[%d:%d] failed flr: rc %d\n",
+ vf->abs_vfid, i, rc);
}
-static void bnx2x_vf_flr_clnup(struct bnx2x *bp, struct bnx2x_virtf *prev_vf)
+static void bnx2x_vf_flr_clnup(struct bnx2x *bp)
{
- int i = prev_vf ? prev_vf->index + 1 : 0;
struct bnx2x_virtf *vf;
+ int i;
- /* find next VF to cleanup */
-next_vf_to_clean:
- for (;
- i < BNX2X_NR_VIRTFN(bp) &&
- (bnx2x_vf(bp, i, state) != VF_RESET ||
- bnx2x_vf(bp, i, flr_clnup_stage) != VF_FLR_CLN);
- i++)
- ;
+ for (i = 0; i < BNX2X_NR_VIRTFN(bp); i++) {
+ /* VF should be RESET & in FLR cleanup states */
+ if (bnx2x_vf(bp, i, state) != VF_RESET ||
+ !bnx2x_vf(bp, i, flr_clnup_stage))
+ continue;
- DP(BNX2X_MSG_IOV, "next vf to cleanup: %d. Num of vfs: %d\n", i,
- BNX2X_NR_VIRTFN(bp));
+ DP(BNX2X_MSG_IOV, "next vf to cleanup: %d. Num of vfs: %d\n",
+ i, BNX2X_NR_VIRTFN(bp));
- if (i < BNX2X_NR_VIRTFN(bp)) {
vf = BP_VF(bp, i);
/* lock the vf pf channel */
bnx2x_lock_vf_pf_channel(bp, vf, CHANNEL_TLV_FLR);
/* invoke the VF FLR SM */
- if (bnx2x_vfop_flr_cmd(bp, vf, bnx2x_vf_flr_clnup)) {
- BNX2X_ERR("VF[%d]: FLR cleanup failed -ENOMEM\n",
- vf->abs_vfid);
+ bnx2x_vf_flr(bp, vf);
- /* mark the VF to be ACKED and continue */
- vf->flr_clnup_stage = VF_FLR_ACK;
- goto next_vf_to_clean;
- }
- return;
- }
-
- /* we are done, update vf records */
- for_each_vf(bp, i) {
- vf = BP_VF(bp, i);
-
- if (vf->flr_clnup_stage != VF_FLR_ACK)
- continue;
-
- vf->flr_clnup_stage = VF_FLR_EPILOG;
+ /* mark the VF to be ACKED and continue */
+ vf->flr_clnup_stage = false;
+ bnx2x_unlock_vf_pf_channel(bp, vf, CHANNEL_TLV_FLR);
}
/* Acknowledge the handled VFs.
@@ -1742,7 +999,7 @@ void bnx2x_vf_handle_flr_event(struct bnx2x *bp)
if (reset) {
/* set as reset and ready for cleanup */
vf->state = VF_RESET;
- vf->flr_clnup_stage = VF_FLR_CLN;
+ vf->flr_clnup_stage = true;
DP(BNX2X_MSG_IOV,
"Initiating Final cleanup for VF %d\n",
@@ -1751,7 +1008,7 @@ void bnx2x_vf_handle_flr_event(struct bnx2x *bp)
}
/* do the FLR cleanup for all marked VFs*/
- bnx2x_vf_flr_clnup(bp, NULL);
+ bnx2x_vf_flr_clnup(bp);
}
/* IOV global initialization routines */
@@ -2018,7 +1275,6 @@ int bnx2x_iov_init_one(struct bnx2x *bp, int int_mode_param,
bnx2x_vf(bp, i, index) = i;
bnx2x_vf(bp, i, abs_vfid) = iov->first_vf_in_pf + i;
bnx2x_vf(bp, i, state) = VF_FREE;
- INIT_LIST_HEAD(&bnx2x_vf(bp, i, op_list_head));
mutex_init(&bnx2x_vf(bp, i, op_mutex));
bnx2x_vf(bp, i, op_current) = CHANNEL_TLV_NONE;
}
@@ -2039,6 +1295,9 @@ int bnx2x_iov_init_one(struct bnx2x *bp, int int_mode_param,
goto failed;
}
+ /* Prepare the VFs event synchronization mechanism */
+ mutex_init(&bp->vfdb->event_mutex);
+
return 0;
failed:
DP(BNX2X_MSG_IOV, "Failed err=%d\n", err);
@@ -2117,7 +1376,9 @@ int bnx2x_iov_alloc_mem(struct bnx2x *bp)
cxt->size = min_t(size_t, tot_size, CDU_ILT_PAGE_SZ);
if (cxt->size) {
- BNX2X_PCI_ALLOC(cxt->addr, &cxt->mapping, cxt->size);
+ cxt->addr = BNX2X_PCI_ALLOC(&cxt->mapping, cxt->size);
+ if (!cxt->addr)
+ goto alloc_mem_err;
} else {
cxt->addr = NULL;
cxt->mapping = 0;
@@ -2127,20 +1388,28 @@ int bnx2x_iov_alloc_mem(struct bnx2x *bp)
/* allocate vfs ramrods dma memory - client_init and set_mac */
tot_size = BNX2X_NR_VIRTFN(bp) * sizeof(struct bnx2x_vf_sp);
- BNX2X_PCI_ALLOC(BP_VFDB(bp)->sp_dma.addr, &BP_VFDB(bp)->sp_dma.mapping,
- tot_size);
+ BP_VFDB(bp)->sp_dma.addr = BNX2X_PCI_ALLOC(&BP_VFDB(bp)->sp_dma.mapping,
+ tot_size);
+ if (!BP_VFDB(bp)->sp_dma.addr)
+ goto alloc_mem_err;
BP_VFDB(bp)->sp_dma.size = tot_size;
/* allocate mailboxes */
tot_size = BNX2X_NR_VIRTFN(bp) * MBX_MSG_ALIGNED_SIZE;
- BNX2X_PCI_ALLOC(BP_VF_MBX_DMA(bp)->addr, &BP_VF_MBX_DMA(bp)->mapping,
- tot_size);
+ BP_VF_MBX_DMA(bp)->addr = BNX2X_PCI_ALLOC(&BP_VF_MBX_DMA(bp)->mapping,
+ tot_size);
+ if (!BP_VF_MBX_DMA(bp)->addr)
+ goto alloc_mem_err;
+
BP_VF_MBX_DMA(bp)->size = tot_size;
/* allocate local bulletin boards */
tot_size = BNX2X_NR_VIRTFN(bp) * BULLETIN_CONTENT_SIZE;
- BNX2X_PCI_ALLOC(BP_VF_BULLETIN_DMA(bp)->addr,
- &BP_VF_BULLETIN_DMA(bp)->mapping, tot_size);
+ BP_VF_BULLETIN_DMA(bp)->addr = BNX2X_PCI_ALLOC(&BP_VF_BULLETIN_DMA(bp)->mapping,
+ tot_size);
+ if (!BP_VF_BULLETIN_DMA(bp)->addr)
+ goto alloc_mem_err;
+
BP_VF_BULLETIN_DMA(bp)->size = tot_size;
return 0;
@@ -2166,6 +1435,9 @@ static void bnx2x_vfq_init(struct bnx2x *bp, struct bnx2x_virtf *vf,
bnx2x_vf_sp_map(bp, vf, q_data),
q_type);
+ /* sp indication is set only when vlan/mac/etc. are initialized */
+ q->sp_initialized = false;
+
DP(BNX2X_MSG_IOV,
"initialized vf %d's queue object. func id set to %d. cid set to 0x%x\n",
vf->abs_vfid, q->sp_obj.func_id, q->cid);
@@ -2269,7 +1541,7 @@ int bnx2x_iov_chip_cleanup(struct bnx2x *bp)
/* release all the VFs */
for_each_vf(bp, i)
- bnx2x_vf_release(bp, BP_VF(bp, i), true); /* blocking */
+ bnx2x_vf_release(bp, BP_VF(bp, i));
return 0;
}
@@ -2359,6 +1631,12 @@ void bnx2x_vf_handle_filters_eqe(struct bnx2x *bp,
smp_mb__after_clear_bit();
}
+static void bnx2x_vf_handle_rss_update_eqe(struct bnx2x *bp,
+ struct bnx2x_virtf *vf)
+{
+ vf->rss_conf_obj.raw.clear_pending(&vf->rss_conf_obj.raw);
+}
+
int bnx2x_iov_eq_sp_event(struct bnx2x *bp, union event_ring_elem *elem)
{
struct bnx2x_virtf *vf;
@@ -2383,6 +1661,7 @@ int bnx2x_iov_eq_sp_event(struct bnx2x *bp, union event_ring_elem *elem)
case EVENT_RING_OPCODE_CLASSIFICATION_RULES:
case EVENT_RING_OPCODE_MULTICAST_RULES:
case EVENT_RING_OPCODE_FILTERS_RULES:
+ case EVENT_RING_OPCODE_RSS_UPDATE_RULES:
cid = (elem->message.data.eth_event.echo &
BNX2X_SWCID_MASK);
DP(BNX2X_MSG_IOV, "checking filtering comp cid=%d\n", cid);
@@ -2447,13 +1726,15 @@ get_vf:
vf->abs_vfid, qidx);
bnx2x_vf_handle_filters_eqe(bp, vf);
break;
+ case EVENT_RING_OPCODE_RSS_UPDATE_RULES:
+ DP(BNX2X_MSG_IOV, "got VF [%d:%d] RSS update ramrod\n",
+ vf->abs_vfid, qidx);
+ bnx2x_vf_handle_rss_update_eqe(bp, vf);
case EVENT_RING_OPCODE_VF_FLR:
case EVENT_RING_OPCODE_MALICIOUS_VF:
/* Do nothing for now */
return 0;
}
- /* SRIOV: reschedule any 'in_progress' operations */
- bnx2x_iov_sp_event(bp, cid, false);
return 0;
}
@@ -2490,23 +1771,6 @@ void bnx2x_iov_set_queue_sp_obj(struct bnx2x *bp, int vf_cid,
}
}
-void bnx2x_iov_sp_event(struct bnx2x *bp, int vf_cid, bool queue_work)
-{
- struct bnx2x_virtf *vf;
-
- /* check if the cid is the VF range */
- if (!IS_SRIOV(bp) || !bnx2x_iov_is_vf_cid(bp, vf_cid))
- return;
-
- vf = bnx2x_vf_by_cid(bp, vf_cid);
- if (vf) {
- /* set in_progress flag */
- atomic_set(&vf->op_in_progress, 1);
- if (queue_work)
- queue_delayed_work(bnx2x_wq, &bp->sp_task, 0);
- }
-}
-
void bnx2x_iov_adjust_stats_req(struct bnx2x *bp)
{
int i;
@@ -2527,10 +1791,10 @@ void bnx2x_iov_adjust_stats_req(struct bnx2x *bp)
first_queue_query_index = BNX2X_FIRST_QUEUE_QUERY_IDX -
(is_fcoe ? 0 : 1);
- DP(BNX2X_MSG_IOV,
- "BNX2X_NUM_ETH_QUEUES %d, is_fcoe %d, first_queue_query_index %d => determined the last non virtual statistics query index is %d. Will add queries on top of that\n",
- BNX2X_NUM_ETH_QUEUES(bp), is_fcoe, first_queue_query_index,
- first_queue_query_index + num_queues_req);
+ DP_AND((BNX2X_MSG_IOV | BNX2X_MSG_STATS),
+ "BNX2X_NUM_ETH_QUEUES %d, is_fcoe %d, first_queue_query_index %d => determined the last non virtual statistics query index is %d. Will add queries on top of that\n",
+ BNX2X_NUM_ETH_QUEUES(bp), is_fcoe, first_queue_query_index,
+ first_queue_query_index + num_queues_req);
cur_data_offset = bp->fw_stats_data_mapping +
offsetof(struct bnx2x_fw_stats_data, queue_stats) +
@@ -2544,9 +1808,9 @@ void bnx2x_iov_adjust_stats_req(struct bnx2x *bp)
struct bnx2x_virtf *vf = BP_VF(bp, i);
if (vf->state != VF_ENABLED) {
- DP(BNX2X_MSG_IOV,
- "vf %d not enabled so no stats for it\n",
- vf->abs_vfid);
+ DP_AND((BNX2X_MSG_IOV | BNX2X_MSG_STATS),
+ "vf %d not enabled so no stats for it\n",
+ vf->abs_vfid);
continue;
}
@@ -2588,32 +1852,6 @@ void bnx2x_iov_adjust_stats_req(struct bnx2x *bp)
bp->fw_stats_req->hdr.cmd_num = bp->fw_stats_num + stats_count;
}
-void bnx2x_iov_sp_task(struct bnx2x *bp)
-{
- int i;
-
- if (!IS_SRIOV(bp))
- return;
- /* Iterate over all VFs and invoke state transition for VFs with
- * 'in-progress' slow-path operations
- */
- DP(BNX2X_MSG_IOV, "searching for pending vf operations\n");
- for_each_vf(bp, i) {
- struct bnx2x_virtf *vf = BP_VF(bp, i);
-
- if (!vf) {
- BNX2X_ERR("VF was null! skipping...\n");
- continue;
- }
-
- if (!list_empty(&vf->op_list_head) &&
- atomic_read(&vf->op_in_progress)) {
- DP(BNX2X_MSG_IOV, "running pending op for vf %d\n", i);
- bnx2x_vfop_cur(bp, vf)->transition(bp, vf);
- }
- }
-}
-
static inline
struct bnx2x_virtf *__vf_from_stat_id(struct bnx2x *bp, u8 stat_id)
{
@@ -2849,52 +2087,26 @@ static void bnx2x_set_vf_state(void *cookie)
p->vf->state = p->state;
}
-/* VFOP close (teardown the queues, delete mcasts and close HW) */
-static void bnx2x_vfop_close(struct bnx2x *bp, struct bnx2x_virtf *vf)
+int bnx2x_vf_close(struct bnx2x *bp, struct bnx2x_virtf *vf)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- struct bnx2x_vfop_args_qx *qx = &vfop->args.qx;
- enum bnx2x_vfop_close_state state = vfop->state;
- struct bnx2x_vfop_cmd cmd = {
- .done = bnx2x_vfop_close,
- .block = false,
- };
+ int rc = 0, i;
- if (vfop->rc < 0)
- goto op_err;
-
- DP(BNX2X_MSG_IOV, "vf[%d] STATE: %d\n", vf->abs_vfid, state);
-
- switch (state) {
- case BNX2X_VFOP_CLOSE_QUEUES:
-
- if (++(qx->qid) < vf_rxq_count(vf)) {
- vfop->rc = bnx2x_vfop_qdown_cmd(bp, vf, &cmd, qx->qid);
- if (vfop->rc)
- goto op_err;
- return;
- }
- vfop->state = BNX2X_VFOP_CLOSE_HW;
- vfop->rc = 0;
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT);
+ DP(BNX2X_MSG_IOV, "vf[%d]\n", vf->abs_vfid);
- case BNX2X_VFOP_CLOSE_HW:
-
- /* disable the interrupts */
- DP(BNX2X_MSG_IOV, "disabling igu\n");
- bnx2x_vf_igu_disable(bp, vf);
+ /* Close all queues */
+ for (i = 0; i < vf_rxq_count(vf); i++) {
+ rc = bnx2x_vf_queue_teardown(bp, vf, i);
+ if (rc)
+ goto op_err;
+ }
- /* disable the VF */
- DP(BNX2X_MSG_IOV, "clearing qtbl\n");
- bnx2x_vf_clr_qtbl(bp, vf);
+ /* disable the interrupts */
+ DP(BNX2X_MSG_IOV, "disabling igu\n");
+ bnx2x_vf_igu_disable(bp, vf);
- goto op_done;
- default:
- bnx2x_vfop_default(state);
- }
-op_err:
- BNX2X_ERR("VF[%d] CLOSE error: rc %d\n", vf->abs_vfid, vfop->rc);
-op_done:
+ /* disable the VF */
+ DP(BNX2X_MSG_IOV, "clearing qtbl\n");
+ bnx2x_vf_clr_qtbl(bp, vf);
/* need to make sure there are no outstanding stats ramrods which may
* cause the device to access the VF's stats buffer which it will free
@@ -2909,43 +2121,20 @@ op_done:
}
DP(BNX2X_MSG_IOV, "set state to acquired\n");
- bnx2x_vfop_end(bp, vf, vfop);
-op_pending:
- /* Not supported at the moment; Exists for macros only */
- return;
-}
-int bnx2x_vfop_close_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
- if (vfop) {
- vfop->args.qx.qid = -1; /* loop */
- bnx2x_vfop_opset(BNX2X_VFOP_CLOSE_QUEUES,
- bnx2x_vfop_close, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_close,
- cmd->block);
- }
- return -ENOMEM;
+ return 0;
+op_err:
+ BNX2X_ERR("vf[%d] CLOSE error: rc %d\n", vf->abs_vfid, rc);
+ return rc;
}
/* VF release can be called either: 1. The VF was acquired but
* not enabled 2. the vf was enabled or in the process of being
* enabled
*/
-static void bnx2x_vfop_release(struct bnx2x *bp, struct bnx2x_virtf *vf)
+int bnx2x_vf_free(struct bnx2x *bp, struct bnx2x_virtf *vf)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- struct bnx2x_vfop_cmd cmd = {
- .done = bnx2x_vfop_release,
- .block = false,
- };
-
- DP(BNX2X_MSG_IOV, "vfop->rc %d\n", vfop->rc);
-
- if (vfop->rc < 0)
- goto op_err;
+ int rc;
DP(BNX2X_MSG_IOV, "VF[%d] STATE: %s\n", vf->abs_vfid,
vf->state == VF_FREE ? "Free" :
@@ -2956,116 +2145,87 @@ static void bnx2x_vfop_release(struct bnx2x *bp, struct bnx2x_virtf *vf)
switch (vf->state) {
case VF_ENABLED:
- vfop->rc = bnx2x_vfop_close_cmd(bp, vf, &cmd);
- if (vfop->rc)
+ rc = bnx2x_vf_close(bp, vf);
+ if (rc)
goto op_err;
- return;
-
+ /* Fallthrough to release resources */
case VF_ACQUIRED:
DP(BNX2X_MSG_IOV, "about to free resources\n");
bnx2x_vf_free_resc(bp, vf);
- DP(BNX2X_MSG_IOV, "vfop->rc %d\n", vfop->rc);
- goto op_done;
+ break;
case VF_FREE:
case VF_RESET:
- /* do nothing */
- goto op_done;
default:
- bnx2x_vfop_default(vf->state);
+ break;
}
+ return 0;
op_err:
- BNX2X_ERR("VF[%d] RELEASE error: rc %d\n", vf->abs_vfid, vfop->rc);
-op_done:
- bnx2x_vfop_end(bp, vf, vfop);
+ BNX2X_ERR("VF[%d] RELEASE error: rc %d\n", vf->abs_vfid, rc);
+ return rc;
}
-static void bnx2x_vfop_rss(struct bnx2x *bp, struct bnx2x_virtf *vf)
+int bnx2x_vf_rss_update(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ struct bnx2x_config_rss_params *rss)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- enum bnx2x_vfop_rss_state state;
-
- if (!vfop) {
- BNX2X_ERR("vfop was null\n");
- return;
- }
-
- state = vfop->state;
- bnx2x_vfop_reset_wq(vf);
-
- if (vfop->rc < 0)
- goto op_err;
-
- DP(BNX2X_MSG_IOV, "vf[%d] STATE: %d\n", vf->abs_vfid, state);
-
- switch (state) {
- case BNX2X_VFOP_RSS_CONFIG:
- /* next state */
- vfop->state = BNX2X_VFOP_RSS_DONE;
- bnx2x_config_rss(bp, &vfop->op_p->rss);
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_DONE);
-op_err:
- BNX2X_ERR("RSS error: rc %d\n", vfop->rc);
-op_done:
- case BNX2X_VFOP_RSS_DONE:
- bnx2x_vfop_end(bp, vf, vfop);
- return;
- default:
- bnx2x_vfop_default(state);
- }
-op_pending:
- return;
+ DP(BNX2X_MSG_IOV, "vf[%d]\n", vf->abs_vfid);
+ set_bit(RAMROD_COMP_WAIT, &rss->ramrod_flags);
+ return bnx2x_config_rss(bp, rss);
}
-int bnx2x_vfop_release_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd)
+int bnx2x_vf_tpa_update(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ struct vfpf_tpa_tlv *tlv,
+ struct bnx2x_queue_update_tpa_params *params)
{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
- if (vfop) {
- bnx2x_vfop_opset(-1, /* use vf->state */
- bnx2x_vfop_release, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_release,
- cmd->block);
- }
- return -ENOMEM;
-}
+ aligned_u64 *sge_addr = tlv->tpa_client_info.sge_addr;
+ struct bnx2x_queue_state_params qstate;
+ int qid, rc = 0;
-int bnx2x_vfop_rss_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
+ DP(BNX2X_MSG_IOV, "vf[%d]\n", vf->abs_vfid);
+
+ /* Set ramrod params */
+ memset(&qstate, 0, sizeof(struct bnx2x_queue_state_params));
+ memcpy(&qstate.params.update_tpa, params,
+ sizeof(struct bnx2x_queue_update_tpa_params));
+ qstate.cmd = BNX2X_Q_CMD_UPDATE_TPA;
+ set_bit(RAMROD_COMP_WAIT, &qstate.ramrod_flags);
- if (vfop) {
- bnx2x_vfop_opset(BNX2X_VFOP_RSS_CONFIG, bnx2x_vfop_rss,
- cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_rss,
- cmd->block);
+ for (qid = 0; qid < vf_rxq_count(vf); qid++) {
+ qstate.q_obj = &bnx2x_vfq(vf, qid, sp_obj);
+ qstate.params.update_tpa.sge_map = sge_addr[qid];
+ DP(BNX2X_MSG_IOV, "sge_addr[%d:%d] %08x:%08x\n",
+ vf->abs_vfid, qid, U64_HI(sge_addr[qid]),
+ U64_LO(sge_addr[qid]));
+ rc = bnx2x_queue_state_change(bp, &qstate);
+ if (rc) {
+ BNX2X_ERR("Failed to configure sge_addr %08x:%08x for [%d:%d]\n",
+ U64_HI(sge_addr[qid]), U64_LO(sge_addr[qid]),
+ vf->abs_vfid, qid);
+ return rc;
+ }
}
- return -ENOMEM;
+
+ return rc;
}
/* VF release ~ VF close + VF release-resources
* Release is the ultimate SW shutdown and is called whenever an
* irrecoverable error is encountered.
*/
-void bnx2x_vf_release(struct bnx2x *bp, struct bnx2x_virtf *vf, bool block)
+int bnx2x_vf_release(struct bnx2x *bp, struct bnx2x_virtf *vf)
{
- struct bnx2x_vfop_cmd cmd = {
- .done = NULL,
- .block = block,
- };
int rc;
DP(BNX2X_MSG_IOV, "PF releasing vf %d\n", vf->abs_vfid);
bnx2x_lock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_RELEASE_VF);
- rc = bnx2x_vfop_release_cmd(bp, vf, &cmd);
+ rc = bnx2x_vf_free(bp, vf);
if (rc)
WARN(rc,
"VF[%d] Failed to allocate resources for release op- rc=%d\n",
vf->abs_vfid, rc);
+ bnx2x_unlock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_RELEASE_VF);
+ return rc;
}
static inline void bnx2x_vf_get_sbdf(struct bnx2x *bp,
@@ -3074,16 +2234,6 @@ static inline void bnx2x_vf_get_sbdf(struct bnx2x *bp,
*sbdf = vf->devfn | (vf->bus << 8);
}
-static inline void bnx2x_vf_get_bars(struct bnx2x *bp, struct bnx2x_virtf *vf,
- struct bnx2x_vf_bar_info *bar_info)
-{
- int n;
-
- bar_info->nr_bars = bp->vfdb->sriov.nres;
- for (n = 0; n < bar_info->nr_bars; n++)
- bar_info->bars[n] = vf->bars[n];
-}
-
void bnx2x_lock_vf_pf_channel(struct bnx2x *bp, struct bnx2x_virtf *vf,
enum channel_tlvs tlv)
{
@@ -3405,13 +2555,13 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx,
ivi->spoofchk = 1; /*always enabled */
if (vf->state == VF_ENABLED) {
/* mac and vlan are in vlan_mac objects */
- if (validate_vlan_mac(bp, &bnx2x_leading_vfq(vf, mac_obj)))
+ if (bnx2x_validate_vf_sp_objs(bp, vf, false)) {
mac_obj->get_n_elements(bp, mac_obj, 1, (u8 *)&ivi->mac,
0, ETH_ALEN);
- if (validate_vlan_mac(bp, &bnx2x_leading_vfq(vf, vlan_obj)))
vlan_obj->get_n_elements(bp, vlan_obj, 1,
(u8 *)&ivi->vlan, 0,
VLAN_HLEN);
+ }
} else {
/* mac */
if (bulletin->valid_bitmap & (1 << MAC_ADDR_VALID))
@@ -3485,17 +2635,17 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac)
q_logical_state == BNX2X_Q_LOGICAL_STATE_ACTIVE) {
/* configure the mac in device on this vf's queue */
unsigned long ramrod_flags = 0;
- struct bnx2x_vlan_mac_obj *mac_obj =
- &bnx2x_leading_vfq(vf, mac_obj);
+ struct bnx2x_vlan_mac_obj *mac_obj;
- rc = validate_vlan_mac(bp, &bnx2x_leading_vfq(vf, mac_obj));
- if (rc)
- return rc;
+ /* User should be able to see failure reason in system logs */
+ if (!bnx2x_validate_vf_sp_objs(bp, vf, true))
+ return -EINVAL;
/* must lock vfpf channel to protect against vf flows */
bnx2x_lock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_SET_MAC);
/* remove existing eth macs */
+ mac_obj = &bnx2x_leading_vfq(vf, mac_obj);
rc = bnx2x_del_all_macs(bp, mac_obj, BNX2X_ETH_MAC, true);
if (rc) {
BNX2X_ERR("failed to delete eth macs\n");
@@ -3569,17 +2719,16 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos)
BNX2X_Q_LOGICAL_STATE_ACTIVE)
return rc;
- /* configure the vlan in device on this vf's queue */
- vlan_obj = &bnx2x_leading_vfq(vf, vlan_obj);
- rc = validate_vlan_mac(bp, &bnx2x_leading_vfq(vf, mac_obj));
- if (rc)
- return rc;
+ /* User should be able to see error in system logs */
+ if (!bnx2x_validate_vf_sp_objs(bp, vf, true))
+ return -EINVAL;
/* must lock vfpf channel to protect against vf flows */
bnx2x_lock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_SET_VLAN);
/* remove existing vlans */
__set_bit(RAMROD_COMP_WAIT, &ramrod_flags);
+ vlan_obj = &bnx2x_leading_vfq(vf, vlan_obj);
rc = vlan_obj->delete_all(bp, vlan_obj, &vlan_mac_flags,
&ramrod_flags);
if (rc) {
@@ -3736,13 +2885,9 @@ void bnx2x_timer_sriov(struct bnx2x *bp)
bnx2x_sample_bulletin(bp);
/* if channel is down we need to self destruct */
- if (bp->old_bulletin.valid_bitmap & 1 << CHANNEL_DOWN) {
- smp_mb__before_clear_bit();
- set_bit(BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN,
- &bp->sp_rtnl_state);
- smp_mb__after_clear_bit();
- schedule_delayed_work(&bp->sp_rtnl_task, 0);
- }
+ if (bp->old_bulletin.valid_bitmap & 1 << CHANNEL_DOWN)
+ bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN,
+ BNX2X_MSG_IOV);
}
void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
@@ -3756,12 +2901,16 @@ int bnx2x_vf_pci_alloc(struct bnx2x *bp)
mutex_init(&bp->vf2pf_mutex);
/* allocate vf2pf mailbox for vf to pf channel */
- BNX2X_PCI_ALLOC(bp->vf2pf_mbox, &bp->vf2pf_mbox_mapping,
- sizeof(struct bnx2x_vf_mbx_msg));
+ bp->vf2pf_mbox = BNX2X_PCI_ALLOC(&bp->vf2pf_mbox_mapping,
+ sizeof(struct bnx2x_vf_mbx_msg));
+ if (!bp->vf2pf_mbox)
+ goto alloc_mem_err;
/* allocate pf 2 vf bulletin board */
- BNX2X_PCI_ALLOC(bp->pf2vf_bulletin, &bp->pf2vf_bulletin_mapping,
- sizeof(union pf_vf_bulletin));
+ bp->pf2vf_bulletin = BNX2X_PCI_ALLOC(&bp->pf2vf_bulletin_mapping,
+ sizeof(union pf_vf_bulletin));
+ if (!bp->pf2vf_bulletin)
+ goto alloc_mem_err;
return 0;
@@ -3792,3 +2941,28 @@ void bnx2x_iov_channel_down(struct bnx2x *bp)
bnx2x_post_vf_bulletin(bp, vf_idx);
}
}
+
+void bnx2x_iov_task(struct work_struct *work)
+{
+ struct bnx2x *bp = container_of(work, struct bnx2x, iov_task.work);
+
+ if (!netif_running(bp->dev))
+ return;
+
+ if (test_and_clear_bit(BNX2X_IOV_HANDLE_FLR,
+ &bp->iov_task_state))
+ bnx2x_vf_handle_flr_event(bp);
+
+ if (test_and_clear_bit(BNX2X_IOV_HANDLE_VF_MSG,
+ &bp->iov_task_state))
+ bnx2x_vf_mbx(bp);
+}
+
+void bnx2x_schedule_iov_task(struct bnx2x *bp, enum bnx2x_iov_flag flag)
+{
+ smp_mb__before_clear_bit();
+ set_bit(flag, &bp->iov_task_state);
+ smp_mb__after_clear_bit();
+ DP(BNX2X_MSG_IOV, "Scheduling iov task [Flag: %d]\n", flag);
+ queue_delayed_work(bnx2x_iov_wq, &bp->iov_task, 0);
+}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
index d9fcca1b5a9d..8bf764570eef 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
@@ -30,6 +30,8 @@ enum sample_bulletin_result {
#ifdef CONFIG_BNX2X_SRIOV
+extern struct workqueue_struct *bnx2x_iov_wq;
+
/* The bnx2x device structure holds vfdb structure described below.
* The VF array is indexed by the relative vfid.
*/
@@ -83,108 +85,35 @@ struct bnx2x_vf_queue {
u16 index;
u16 sb_idx;
bool is_leading;
+ bool sp_initialized;
};
-/* struct bnx2x_vfop_qctor_params - prepare queue construction parameters:
- * q-init, q-setup and SB index
+/* struct bnx2x_vf_queue_construct_params - prepare queue construction
+ * parameters: q-init, q-setup and SB index
*/
-struct bnx2x_vfop_qctor_params {
+struct bnx2x_vf_queue_construct_params {
struct bnx2x_queue_state_params qstate;
struct bnx2x_queue_setup_params prep_qsetup;
};
-/* VFOP parameters (one copy per VF) */
-union bnx2x_vfop_params {
- struct bnx2x_vlan_mac_ramrod_params vlan_mac;
- struct bnx2x_rx_mode_ramrod_params rx_mode;
- struct bnx2x_mcast_ramrod_params mcast;
- struct bnx2x_config_rss_params rss;
- struct bnx2x_vfop_qctor_params qctor;
-};
-
/* forward */
struct bnx2x_virtf;
/* VFOP definitions */
-typedef void (*vfop_handler_t)(struct bnx2x *bp, struct bnx2x_virtf *vf);
-
-struct bnx2x_vfop_cmd {
- vfop_handler_t done;
- bool block;
-};
-/* VFOP queue filters command additional arguments */
-struct bnx2x_vfop_filter {
- struct list_head link;
+struct bnx2x_vf_mac_vlan_filter {
int type;
-#define BNX2X_VFOP_FILTER_MAC 1
-#define BNX2X_VFOP_FILTER_VLAN 2
+#define BNX2X_VF_FILTER_MAC 1
+#define BNX2X_VF_FILTER_VLAN 2
bool add;
u8 *mac;
u16 vid;
};
-struct bnx2x_vfop_filters {
- int add_cnt;
- struct list_head head;
- struct bnx2x_vfop_filter filters[];
-};
-
-/* transient list allocated, built and saved until its
- * passed to the SP-VERBs layer.
- */
-struct bnx2x_vfop_args_mcast {
- int mc_num;
- struct bnx2x_mcast_list_elem *mc;
-};
-
-struct bnx2x_vfop_args_qctor {
- int qid;
- u16 sb_idx;
-};
-
-struct bnx2x_vfop_args_qdtor {
- int qid;
- struct eth_context *cxt;
-};
-
-struct bnx2x_vfop_args_defvlan {
- int qid;
- bool enable;
- u16 vid;
- u8 prio;
-};
-
-struct bnx2x_vfop_args_qx {
- int qid;
- bool en_add;
-};
-
-struct bnx2x_vfop_args_filters {
- struct bnx2x_vfop_filters *multi_filter;
- atomic_t *credit; /* non NULL means 'don't consume credit' */
-};
-
-union bnx2x_vfop_args {
- struct bnx2x_vfop_args_mcast mc_list;
- struct bnx2x_vfop_args_qctor qctor;
- struct bnx2x_vfop_args_qdtor qdtor;
- struct bnx2x_vfop_args_defvlan defvlan;
- struct bnx2x_vfop_args_qx qx;
- struct bnx2x_vfop_args_filters filters;
-};
-
-struct bnx2x_vfop {
- struct list_head link;
- int rc; /* return code */
- int state; /* next state */
- union bnx2x_vfop_args args; /* extra arguments */
- union bnx2x_vfop_params *op_p; /* ramrod params */
-
- /* state machine callbacks */
- vfop_handler_t transition;
- vfop_handler_t done;
+struct bnx2x_vf_mac_vlan_filters {
+ int count;
+ struct bnx2x_vf_mac_vlan_filter filters[];
};
/* vf context */
@@ -204,15 +133,7 @@ struct bnx2x_virtf {
#define VF_ENABLED 2 /* VF Enabled */
#define VF_RESET 3 /* VF FLR'd, pending cleanup */
- /* non 0 during flr cleanup */
- u8 flr_clnup_stage;
-#define VF_FLR_CLN 1 /* reclaim resources and do 'final cleanup'
- * sans the end-wait
- */
-#define VF_FLR_ACK 2 /* ACK flr notification */
-#define VF_FLR_EPILOG 3 /* wait for VF remnants to dissipate in the HW
- * ~ final cleanup' end wait
- */
+ bool flr_clnup_stage; /* true during flr cleanup */
/* dma */
dma_addr_t fw_stat_map; /* valid iff VF_CFG_STATS */
@@ -276,11 +197,6 @@ struct bnx2x_virtf {
struct bnx2x_rss_config_obj rss_conf_obj;
/* slow-path operations */
- atomic_t op_in_progress;
- int op_rc;
- bool op_wait_blocking;
- struct list_head op_list_head;
- union bnx2x_vfop_params op_params;
struct mutex op_mutex; /* one vfop at a time mutex */
enum channel_tlvs op_current;
};
@@ -338,11 +254,6 @@ struct bnx2x_vf_mbx {
u32 vf_addr_hi;
struct vfpf_first_tlv first_tlv; /* saved VF request header */
-
- u8 flags;
-#define VF_MSG_INPROCESS 0x1 /* failsafe - the FW should prevent
- * more then one pending msg
- */
};
struct bnx2x_vf_sp {
@@ -419,6 +330,10 @@ struct bnx2x_vfdb {
/* the number of msix vectors belonging to this PF designated for VFs */
u16 vf_sbs_pool;
u16 first_vf_igu_entry;
+
+ /* sp_rtnl synchronization */
+ struct mutex event_mutex;
+ u64 event_occur;
};
/* queue access */
@@ -468,13 +383,13 @@ void bnx2x_iov_init_dq(struct bnx2x *bp);
void bnx2x_iov_init_dmae(struct bnx2x *bp);
void bnx2x_iov_set_queue_sp_obj(struct bnx2x *bp, int vf_cid,
struct bnx2x_queue_sp_obj **q_obj);
-void bnx2x_iov_sp_event(struct bnx2x *bp, int vf_cid, bool queue_work);
int bnx2x_iov_eq_sp_event(struct bnx2x *bp, union event_ring_elem *elem);
void bnx2x_iov_adjust_stats_req(struct bnx2x *bp);
void bnx2x_iov_storm_stats_update(struct bnx2x *bp);
-void bnx2x_iov_sp_task(struct bnx2x *bp);
/* global vf mailbox routines */
-void bnx2x_vf_mbx(struct bnx2x *bp, struct vf_pf_event_data *vfpf_event);
+void bnx2x_vf_mbx(struct bnx2x *bp);
+void bnx2x_vf_mbx_schedule(struct bnx2x *bp,
+ struct vf_pf_event_data *vfpf_event);
void bnx2x_vf_enable_mbx(struct bnx2x *bp, u8 abs_vfid);
/* CORE VF API */
@@ -487,162 +402,6 @@ int bnx2x_vf_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf,
int bnx2x_vf_init(struct bnx2x *bp, struct bnx2x_virtf *vf,
dma_addr_t *sb_map);
-/* VFOP generic helpers */
-#define bnx2x_vfop_default(state) do { \
- BNX2X_ERR("Bad state %d\n", (state)); \
- vfop->rc = -EINVAL; \
- goto op_err; \
- } while (0)
-
-enum {
- VFOP_DONE,
- VFOP_CONT,
- VFOP_VERIFY_PEND,
-};
-
-#define bnx2x_vfop_finalize(vf, rc, next) do { \
- if ((rc) < 0) \
- goto op_err; \
- else if ((rc) > 0) \
- goto op_pending; \
- else if ((next) == VFOP_DONE) \
- goto op_done; \
- else if ((next) == VFOP_VERIFY_PEND) \
- BNX2X_ERR("expected pending\n"); \
- else { \
- DP(BNX2X_MSG_IOV, "no ramrod. Scheduling\n"); \
- atomic_set(&vf->op_in_progress, 1); \
- queue_delayed_work(bnx2x_wq, &bp->sp_task, 0); \
- return; \
- } \
- } while (0)
-
-#define bnx2x_vfop_opset(first_state, trans_hndlr, done_hndlr) \
- do { \
- vfop->state = first_state; \
- vfop->op_p = &vf->op_params; \
- vfop->transition = trans_hndlr; \
- vfop->done = done_hndlr; \
- } while (0)
-
-static inline struct bnx2x_vfop *bnx2x_vfop_cur(struct bnx2x *bp,
- struct bnx2x_virtf *vf)
-{
- WARN(!mutex_is_locked(&vf->op_mutex), "about to access vf op linked list but mutex was not locked!");
- WARN_ON(list_empty(&vf->op_list_head));
- return list_first_entry(&vf->op_list_head, struct bnx2x_vfop, link);
-}
-
-static inline struct bnx2x_vfop *bnx2x_vfop_add(struct bnx2x *bp,
- struct bnx2x_virtf *vf)
-{
- struct bnx2x_vfop *vfop = kzalloc(sizeof(*vfop), GFP_KERNEL);
-
- WARN(!mutex_is_locked(&vf->op_mutex), "about to access vf op linked list but mutex was not locked!");
- if (vfop) {
- INIT_LIST_HEAD(&vfop->link);
- list_add(&vfop->link, &vf->op_list_head);
- }
- return vfop;
-}
-
-static inline void bnx2x_vfop_end(struct bnx2x *bp, struct bnx2x_virtf *vf,
- struct bnx2x_vfop *vfop)
-{
- /* rc < 0 - error, otherwise set to 0 */
- DP(BNX2X_MSG_IOV, "rc was %d\n", vfop->rc);
- if (vfop->rc >= 0)
- vfop->rc = 0;
- DP(BNX2X_MSG_IOV, "rc is now %d\n", vfop->rc);
-
- /* unlink the current op context and propagate error code
- * must be done before invoking the 'done()' handler
- */
- WARN(!mutex_is_locked(&vf->op_mutex),
- "about to access vf op linked list but mutex was not locked!");
- list_del(&vfop->link);
-
- if (list_empty(&vf->op_list_head)) {
- DP(BNX2X_MSG_IOV, "list was empty %d\n", vfop->rc);
- vf->op_rc = vfop->rc;
- DP(BNX2X_MSG_IOV, "copying rc vf->op_rc %d, vfop->rc %d\n",
- vf->op_rc, vfop->rc);
- } else {
- struct bnx2x_vfop *cur_vfop;
-
- DP(BNX2X_MSG_IOV, "list not empty %d\n", vfop->rc);
- cur_vfop = bnx2x_vfop_cur(bp, vf);
- cur_vfop->rc = vfop->rc;
- DP(BNX2X_MSG_IOV, "copying rc vf->op_rc %d, vfop->rc %d\n",
- vf->op_rc, vfop->rc);
- }
-
- /* invoke done handler */
- if (vfop->done) {
- DP(BNX2X_MSG_IOV, "calling done handler\n");
- vfop->done(bp, vf);
- } else {
- /* there is no done handler for the operation to unlock
- * the mutex. Must have gotten here from PF initiated VF RELEASE
- */
- bnx2x_unlock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_RELEASE_VF);
- }
-
- DP(BNX2X_MSG_IOV, "done handler complete. vf->op_rc %d, vfop->rc %d\n",
- vf->op_rc, vfop->rc);
-
- /* if this is the last nested op reset the wait_blocking flag
- * to release any blocking wrappers, only after 'done()' is invoked
- */
- if (list_empty(&vf->op_list_head)) {
- DP(BNX2X_MSG_IOV, "list was empty after done %d\n", vfop->rc);
- vf->op_wait_blocking = false;
- }
-
- kfree(vfop);
-}
-
-static inline int bnx2x_vfop_wait_blocking(struct bnx2x *bp,
- struct bnx2x_virtf *vf)
-{
- /* can take a while if any port is running */
- int cnt = 5000;
-
- might_sleep();
- while (cnt--) {
- if (vf->op_wait_blocking == false) {
-#ifdef BNX2X_STOP_ON_ERROR
- DP(BNX2X_MSG_IOV, "exit (cnt %d)\n", 5000 - cnt);
-#endif
- return 0;
- }
- usleep_range(1000, 2000);
-
- if (bp->panic)
- return -EIO;
- }
-
- /* timeout! */
-#ifdef BNX2X_STOP_ON_ERROR
- bnx2x_panic();
-#endif
-
- return -EBUSY;
-}
-
-static inline int bnx2x_vfop_transition(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- vfop_handler_t transition,
- bool block)
-{
- if (block)
- vf->op_wait_blocking = true;
- transition(bp, vf);
- if (block)
- return bnx2x_vfop_wait_blocking(bp, vf);
- return 0;
-}
-
/* VFOP queue construction helpers */
void bnx2x_vfop_qctor_dump_tx(struct bnx2x *bp, struct bnx2x_virtf *vf,
struct bnx2x_queue_init_params *init_params,
@@ -657,59 +416,41 @@ void bnx2x_vfop_qctor_dump_rx(struct bnx2x *bp, struct bnx2x_virtf *vf,
void bnx2x_vfop_qctor_prep(struct bnx2x *bp,
struct bnx2x_virtf *vf,
struct bnx2x_vf_queue *q,
- struct bnx2x_vfop_qctor_params *p,
+ struct bnx2x_vf_queue_construct_params *p,
unsigned long q_type);
-int bnx2x_vfop_mac_list_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- struct bnx2x_vfop_filters *macs,
- int qid, bool drv_only);
-
-int bnx2x_vfop_vlan_list_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- struct bnx2x_vfop_filters *vlans,
- int qid, bool drv_only);
-
-int bnx2x_vfop_qsetup_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid);
-
-int bnx2x_vfop_qdown_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid);
-
-int bnx2x_vfop_mcast_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- bnx2x_mac_addr_t *mcasts,
- int mcast_num, bool drv_only);
-
-int bnx2x_vfop_rxmode_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd,
- int qid, unsigned long accept_flags);
-
-int bnx2x_vfop_close_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd);
-
-int bnx2x_vfop_release_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd);
-int bnx2x_vfop_rss_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd);
+int bnx2x_vf_mac_vlan_config_list(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ struct bnx2x_vf_mac_vlan_filters *filters,
+ int qid, bool drv_only);
+
+int bnx2x_vf_queue_setup(struct bnx2x *bp, struct bnx2x_virtf *vf, int qid,
+ struct bnx2x_vf_queue_construct_params *qctor);
+
+int bnx2x_vf_queue_teardown(struct bnx2x *bp, struct bnx2x_virtf *vf, int qid);
+
+int bnx2x_vf_mcast(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ bnx2x_mac_addr_t *mcasts, int mc_num, bool drv_only);
+
+int bnx2x_vf_rxmode(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ int qid, unsigned long accept_flags);
+
+int bnx2x_vf_close(struct bnx2x *bp, struct bnx2x_virtf *vf);
+
+int bnx2x_vf_free(struct bnx2x *bp, struct bnx2x_virtf *vf);
+
+int bnx2x_vf_rss_update(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ struct bnx2x_config_rss_params *rss);
+
+int bnx2x_vf_tpa_update(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ struct vfpf_tpa_tlv *tlv,
+ struct bnx2x_queue_update_tpa_params *params);
/* VF release ~ VF close + VF release-resources
*
* Release is the ultimate SW shutdown and is called whenever an
* irrecoverable error is encountered.
*/
-void bnx2x_vf_release(struct bnx2x *bp, struct bnx2x_virtf *vf, bool block);
+int bnx2x_vf_release(struct bnx2x *bp, struct bnx2x_virtf *vf);
int bnx2x_vf_idx_by_abs_fid(struct bnx2x *bp, u16 abs_vfid);
u8 bnx2x_vf_max_queue_cnt(struct bnx2x *bp, struct bnx2x_virtf *vf);
@@ -772,18 +513,20 @@ void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp);
int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs);
void bnx2x_iov_channel_down(struct bnx2x *bp);
+void bnx2x_iov_task(struct work_struct *work);
+
+void bnx2x_schedule_iov_task(struct bnx2x *bp, enum bnx2x_iov_flag flag);
+
#else /* CONFIG_BNX2X_SRIOV */
static inline void bnx2x_iov_set_queue_sp_obj(struct bnx2x *bp, int vf_cid,
struct bnx2x_queue_sp_obj **q_obj) {}
-static inline void bnx2x_iov_sp_event(struct bnx2x *bp, int vf_cid,
- bool queue_work) {}
static inline void bnx2x_vf_handle_flr_event(struct bnx2x *bp) {}
static inline int bnx2x_iov_eq_sp_event(struct bnx2x *bp,
union event_ring_elem *elem) {return 1; }
-static inline void bnx2x_iov_sp_task(struct bnx2x *bp) {}
-static inline void bnx2x_vf_mbx(struct bnx2x *bp,
- struct vf_pf_event_data *vfpf_event) {}
+static inline void bnx2x_vf_mbx(struct bnx2x *bp) {}
+static inline void bnx2x_vf_mbx_schedule(struct bnx2x *bp,
+ struct vf_pf_event_data *vfpf_event) {}
static inline int bnx2x_iov_init_ilt(struct bnx2x *bp, u16 line) {return line; }
static inline void bnx2x_iov_init_dq(struct bnx2x *bp) {}
static inline int bnx2x_iov_alloc_mem(struct bnx2x *bp) {return 0; }
@@ -830,5 +573,8 @@ static inline void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp) {}
static inline int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs) {return 0; }
static inline void bnx2x_iov_channel_down(struct bnx2x *bp) {}
+static inline void bnx2x_iov_task(struct work_struct *work) {}
+static inline void bnx2x_schedule_iov_task(struct bnx2x *bp, enum bnx2x_iov_flag flag) {}
+
#endif /* CONFIG_BNX2X_SRIOV */
#endif /* bnx2x_sriov.h */
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
index 3fa6c2a2a5a9..0622884596b2 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
@@ -548,6 +548,7 @@ static void bnx2x_leading_vfq_init(struct bnx2x *bp, struct bnx2x_virtf *vf,
vf->leading_rss = cl_id;
q->is_leading = true;
+ q->sp_initialized = true;
}
/* ask the pf to open a queue for the vf */
@@ -672,6 +673,7 @@ static int bnx2x_vfpf_teardown_queue(struct bnx2x *bp, int qidx)
out:
bnx2x_vfpf_finalize(bp, &req->first_tlv);
+
return rc;
}
@@ -894,29 +896,16 @@ int bnx2x_vfpf_storm_rx_mode(struct bnx2x *bp)
DP(NETIF_MSG_IFUP, "Rx mode is %d\n", mode);
- switch (mode) {
- case BNX2X_RX_MODE_NONE: /* no Rx */
+ /* Ignore everything accept MODE_NONE */
+ if (mode == BNX2X_RX_MODE_NONE) {
req->rx_mask = VFPF_RX_MASK_ACCEPT_NONE;
- break;
- case BNX2X_RX_MODE_NORMAL:
+ } else {
+ /* Current PF driver will not look at the specific flags,
+ * but they are required when working with older drivers on hv.
+ */
req->rx_mask = VFPF_RX_MASK_ACCEPT_MATCHED_MULTICAST;
req->rx_mask |= VFPF_RX_MASK_ACCEPT_MATCHED_UNICAST;
req->rx_mask |= VFPF_RX_MASK_ACCEPT_BROADCAST;
- break;
- case BNX2X_RX_MODE_ALLMULTI:
- req->rx_mask = VFPF_RX_MASK_ACCEPT_ALL_MULTICAST;
- req->rx_mask |= VFPF_RX_MASK_ACCEPT_MATCHED_UNICAST;
- req->rx_mask |= VFPF_RX_MASK_ACCEPT_BROADCAST;
- break;
- case BNX2X_RX_MODE_PROMISC:
- req->rx_mask = VFPF_RX_MASK_ACCEPT_ALL_UNICAST;
- req->rx_mask |= VFPF_RX_MASK_ACCEPT_ALL_MULTICAST;
- req->rx_mask |= VFPF_RX_MASK_ACCEPT_BROADCAST;
- break;
- default:
- BNX2X_ERR("BAD rx mode (%d)\n", mode);
- rc = -EINVAL;
- goto out;
}
req->flags |= VFPF_SET_Q_FILTERS_RX_MASK_CHANGED;
@@ -937,7 +926,7 @@ int bnx2x_vfpf_storm_rx_mode(struct bnx2x *bp)
BNX2X_ERR("Set Rx mode failed: %d\n", resp->hdr.status);
rc = -EINVAL;
}
-out:
+
bnx2x_vfpf_finalize(bp, &req->first_tlv);
return rc;
@@ -1047,7 +1036,8 @@ static void bnx2x_vf_mbx_resp_single_tlv(struct bnx2x *bp,
}
static void bnx2x_vf_mbx_resp_send_msg(struct bnx2x *bp,
- struct bnx2x_virtf *vf)
+ struct bnx2x_virtf *vf,
+ int vf_rc)
{
struct bnx2x_vf_mbx *mbx = BP_VF_MBX(bp, vf->index);
struct pfvf_general_resp_tlv *resp = &mbx->msg->resp.general_resp;
@@ -1059,7 +1049,7 @@ static void bnx2x_vf_mbx_resp_send_msg(struct bnx2x *bp,
DP(BNX2X_MSG_IOV, "mailbox vf address hi 0x%x, lo 0x%x, offset 0x%x\n",
mbx->vf_addr_hi, mbx->vf_addr_lo, mbx->first_tlv.resp_msg_offset);
- resp->hdr.status = bnx2x_pfvf_status_codes(vf->op_rc);
+ resp->hdr.status = bnx2x_pfvf_status_codes(vf_rc);
/* send response */
vf_addr = HILO_U64(mbx->vf_addr_hi, mbx->vf_addr_lo) +
@@ -1088,9 +1078,6 @@ static void bnx2x_vf_mbx_resp_send_msg(struct bnx2x *bp,
storm_memset_vf_mbx_ack(bp, vf->abs_vfid);
mmiowb();
- /* initiate dmae to send the response */
- mbx->flags &= ~VF_MSG_INPROCESS;
-
/* copy the response header including status-done field,
* must be last dmae, must be after FW is acked
*/
@@ -1110,14 +1097,15 @@ static void bnx2x_vf_mbx_resp_send_msg(struct bnx2x *bp,
return;
mbx_error:
- bnx2x_vf_release(bp, vf, false); /* non blocking */
+ bnx2x_vf_release(bp, vf);
}
static void bnx2x_vf_mbx_resp(struct bnx2x *bp,
- struct bnx2x_virtf *vf)
+ struct bnx2x_virtf *vf,
+ int rc)
{
bnx2x_vf_mbx_resp_single_tlv(bp, vf);
- bnx2x_vf_mbx_resp_send_msg(bp, vf);
+ bnx2x_vf_mbx_resp_send_msg(bp, vf, rc);
}
static void bnx2x_vf_mbx_resp_phys_port(struct bnx2x *bp,
@@ -1159,7 +1147,8 @@ static void bnx2x_vf_mbx_acquire_resp(struct bnx2x *bp, struct bnx2x_virtf *vf,
resp->pfdev_info.db_size = bp->db_size;
resp->pfdev_info.indices_per_sb = HC_SB_MAX_INDICES_E2;
resp->pfdev_info.pf_cap = (PFVF_CAP_RSS |
- /* PFVF_CAP_DHC |*/ PFVF_CAP_TPA);
+ PFVF_CAP_TPA |
+ PFVF_CAP_TPA_UPDATE);
bnx2x_fill_fw_str(bp, resp->pfdev_info.fw_ver,
sizeof(resp->pfdev_info.fw_ver));
@@ -1240,8 +1229,7 @@ static void bnx2x_vf_mbx_acquire_resp(struct bnx2x *bp, struct bnx2x_virtf *vf,
sizeof(struct channel_list_end_tlv));
/* send the response */
- vf->op_rc = vfop_status;
- bnx2x_vf_mbx_resp_send_msg(bp, vf);
+ bnx2x_vf_mbx_resp_send_msg(bp, vf, vfop_status);
}
static void bnx2x_vf_mbx_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf,
@@ -1273,19 +1261,20 @@ static void bnx2x_vf_mbx_init_vf(struct bnx2x *bp, struct bnx2x_virtf *vf,
struct bnx2x_vf_mbx *mbx)
{
struct vfpf_init_tlv *init = &mbx->msg->req.init;
+ int rc;
/* record ghost addresses from vf message */
vf->spq_map = init->spq_addr;
vf->fw_stat_map = init->stats_addr;
vf->stats_stride = init->stats_stride;
- vf->op_rc = bnx2x_vf_init(bp, vf, (dma_addr_t *)init->sb_addr);
+ rc = bnx2x_vf_init(bp, vf, (dma_addr_t *)init->sb_addr);
/* set VF multiqueue statistics collection mode */
if (init->flags & VFPF_INIT_FLG_STATS_COALESCE)
vf->cfg_flags |= VF_CFG_STATS_COALESCE;
/* response */
- bnx2x_vf_mbx_resp(bp, vf);
+ bnx2x_vf_mbx_resp(bp, vf, rc);
}
/* convert MBX queue-flags to standard SP queue-flags */
@@ -1320,16 +1309,14 @@ static void bnx2x_vf_mbx_setup_q(struct bnx2x *bp, struct bnx2x_virtf *vf,
struct bnx2x_vf_mbx *mbx)
{
struct vfpf_setup_q_tlv *setup_q = &mbx->msg->req.setup_q;
- struct bnx2x_vfop_cmd cmd = {
- .done = bnx2x_vf_mbx_resp,
- .block = false,
- };
+ struct bnx2x_vf_queue_construct_params qctor;
+ int rc = 0;
/* verify vf_qid */
if (setup_q->vf_qid >= vf_rxq_count(vf)) {
BNX2X_ERR("vf_qid %d invalid, max queue count is %d\n",
setup_q->vf_qid, vf_rxq_count(vf));
- vf->op_rc = -EINVAL;
+ rc = -EINVAL;
goto response;
}
@@ -1347,9 +1334,10 @@ static void bnx2x_vf_mbx_setup_q(struct bnx2x *bp, struct bnx2x_virtf *vf,
bnx2x_leading_vfq_init(bp, vf, q);
/* re-init the VF operation context */
- memset(&vf->op_params.qctor, 0 , sizeof(vf->op_params.qctor));
- setup_p = &vf->op_params.qctor.prep_qsetup;
- init_p = &vf->op_params.qctor.qstate.params.init;
+ memset(&qctor, 0 ,
+ sizeof(struct bnx2x_vf_queue_construct_params));
+ setup_p = &qctor.prep_qsetup;
+ init_p = &qctor.qstate.params.init;
/* activate immediately */
__set_bit(BNX2X_Q_FLG_ACTIVE, &setup_p->flags);
@@ -1435,44 +1423,34 @@ static void bnx2x_vf_mbx_setup_q(struct bnx2x *bp, struct bnx2x_virtf *vf,
q->index, q->sb_idx);
}
/* complete the preparations */
- bnx2x_vfop_qctor_prep(bp, vf, q, &vf->op_params.qctor, q_type);
+ bnx2x_vfop_qctor_prep(bp, vf, q, &qctor, q_type);
- vf->op_rc = bnx2x_vfop_qsetup_cmd(bp, vf, &cmd, q->index);
- if (vf->op_rc)
+ rc = bnx2x_vf_queue_setup(bp, vf, q->index, &qctor);
+ if (rc)
goto response;
- return;
}
response:
- bnx2x_vf_mbx_resp(bp, vf);
+ bnx2x_vf_mbx_resp(bp, vf, rc);
}
-enum bnx2x_vfop_filters_state {
- BNX2X_VFOP_MBX_Q_FILTERS_MACS,
- BNX2X_VFOP_MBX_Q_FILTERS_VLANS,
- BNX2X_VFOP_MBX_Q_FILTERS_RXMODE,
- BNX2X_VFOP_MBX_Q_FILTERS_MCAST,
- BNX2X_VFOP_MBX_Q_FILTERS_DONE
-};
-
static int bnx2x_vf_mbx_macvlan_list(struct bnx2x *bp,
struct bnx2x_virtf *vf,
struct vfpf_set_q_filters_tlv *tlv,
- struct bnx2x_vfop_filters **pfl,
+ struct bnx2x_vf_mac_vlan_filters **pfl,
u32 type_flag)
{
int i, j;
- struct bnx2x_vfop_filters *fl = NULL;
+ struct bnx2x_vf_mac_vlan_filters *fl = NULL;
size_t fsz;
- fsz = tlv->n_mac_vlan_filters * sizeof(struct bnx2x_vfop_filter) +
- sizeof(struct bnx2x_vfop_filters);
+ fsz = tlv->n_mac_vlan_filters *
+ sizeof(struct bnx2x_vf_mac_vlan_filter) +
+ sizeof(struct bnx2x_vf_mac_vlan_filters);
fl = kzalloc(fsz, GFP_KERNEL);
if (!fl)
return -ENOMEM;
- INIT_LIST_HEAD(&fl->head);
-
for (i = 0, j = 0; i < tlv->n_mac_vlan_filters; i++) {
struct vfpf_q_mac_vlan_filter *msg_filter = &tlv->filters[i];
@@ -1480,17 +1458,17 @@ static int bnx2x_vf_mbx_macvlan_list(struct bnx2x *bp,
continue;
if (type_flag == VFPF_Q_FILTER_DEST_MAC_VALID) {
fl->filters[j].mac = msg_filter->mac;
- fl->filters[j].type = BNX2X_VFOP_FILTER_MAC;
+ fl->filters[j].type = BNX2X_VF_FILTER_MAC;
} else {
fl->filters[j].vid = msg_filter->vlan_tag;
- fl->filters[j].type = BNX2X_VFOP_FILTER_VLAN;
+ fl->filters[j].type = BNX2X_VF_FILTER_VLAN;
}
fl->filters[j].add =
(msg_filter->flags & VFPF_Q_FILTER_SET_MAC) ?
true : false;
- list_add_tail(&fl->filters[j++].link, &fl->head);
+ fl->count++;
}
- if (list_empty(&fl->head))
+ if (!fl->count)
kfree(fl);
else
*pfl = fl;
@@ -1530,180 +1508,96 @@ static void bnx2x_vf_mbx_dp_q_filters(struct bnx2x *bp, int msglvl,
#define VFPF_MAC_FILTER VFPF_Q_FILTER_DEST_MAC_VALID
#define VFPF_VLAN_FILTER VFPF_Q_FILTER_VLAN_TAG_VALID
-static void bnx2x_vfop_mbx_qfilters(struct bnx2x *bp, struct bnx2x_virtf *vf)
+static int bnx2x_vf_mbx_qfilters(struct bnx2x *bp, struct bnx2x_virtf *vf)
{
- int rc;
+ int rc = 0;
struct vfpf_set_q_filters_tlv *msg =
&BP_VF_MBX(bp, vf->index)->msg->req.set_q_filters;
- struct bnx2x_vfop *vfop = bnx2x_vfop_cur(bp, vf);
- enum bnx2x_vfop_filters_state state = vfop->state;
-
- struct bnx2x_vfop_cmd cmd = {
- .done = bnx2x_vfop_mbx_qfilters,
- .block = false,
- };
-
- DP(BNX2X_MSG_IOV, "STATE: %d\n", state);
-
- if (vfop->rc < 0)
- goto op_err;
+ /* check for any mac/vlan changes */
+ if (msg->flags & VFPF_SET_Q_FILTERS_MAC_VLAN_CHANGED) {
+ /* build mac list */
+ struct bnx2x_vf_mac_vlan_filters *fl = NULL;
- switch (state) {
- case BNX2X_VFOP_MBX_Q_FILTERS_MACS:
- /* next state */
- vfop->state = BNX2X_VFOP_MBX_Q_FILTERS_VLANS;
+ rc = bnx2x_vf_mbx_macvlan_list(bp, vf, msg, &fl,
+ VFPF_MAC_FILTER);
+ if (rc)
+ goto op_err;
- /* check for any vlan/mac changes */
- if (msg->flags & VFPF_SET_Q_FILTERS_MAC_VLAN_CHANGED) {
- /* build mac list */
- struct bnx2x_vfop_filters *fl = NULL;
+ if (fl) {
- vfop->rc = bnx2x_vf_mbx_macvlan_list(bp, vf, msg, &fl,
- VFPF_MAC_FILTER);
- if (vfop->rc)
+ /* set mac list */
+ rc = bnx2x_vf_mac_vlan_config_list(bp, vf, fl,
+ msg->vf_qid,
+ false);
+ if (rc)
goto op_err;
-
- if (fl) {
- /* set mac list */
- rc = bnx2x_vfop_mac_list_cmd(bp, vf, &cmd, fl,
- msg->vf_qid,
- false);
- if (rc) {
- vfop->rc = rc;
- goto op_err;
- }
- return;
- }
}
- /* fall through */
- case BNX2X_VFOP_MBX_Q_FILTERS_VLANS:
- /* next state */
- vfop->state = BNX2X_VFOP_MBX_Q_FILTERS_RXMODE;
+ /* build vlan list */
+ fl = NULL;
- /* check for any vlan/mac changes */
- if (msg->flags & VFPF_SET_Q_FILTERS_MAC_VLAN_CHANGED) {
- /* build vlan list */
- struct bnx2x_vfop_filters *fl = NULL;
-
- vfop->rc = bnx2x_vf_mbx_macvlan_list(bp, vf, msg, &fl,
- VFPF_VLAN_FILTER);
- if (vfop->rc)
+ rc = bnx2x_vf_mbx_macvlan_list(bp, vf, msg, &fl,
+ VFPF_VLAN_FILTER);
+ if (rc)
+ goto op_err;
+
+ if (fl) {
+ /* set vlan list */
+ rc = bnx2x_vf_mac_vlan_config_list(bp, vf, fl,
+ msg->vf_qid,
+ false);
+ if (rc)
goto op_err;
-
- if (fl) {
- /* set vlan list */
- rc = bnx2x_vfop_vlan_list_cmd(bp, vf, &cmd, fl,
- msg->vf_qid,
- false);
- if (rc) {
- vfop->rc = rc;
- goto op_err;
- }
- return;
- }
}
- /* fall through */
-
- case BNX2X_VFOP_MBX_Q_FILTERS_RXMODE:
- /* next state */
- vfop->state = BNX2X_VFOP_MBX_Q_FILTERS_MCAST;
-
- if (msg->flags & VFPF_SET_Q_FILTERS_RX_MASK_CHANGED) {
- unsigned long accept = 0;
- struct pf_vf_bulletin_content *bulletin =
- BP_VF_BULLETIN(bp, vf->index);
-
- /* covert VF-PF if mask to bnx2x accept flags */
- if (msg->rx_mask & VFPF_RX_MASK_ACCEPT_MATCHED_UNICAST)
- __set_bit(BNX2X_ACCEPT_UNICAST, &accept);
-
- if (msg->rx_mask &
- VFPF_RX_MASK_ACCEPT_MATCHED_MULTICAST)
- __set_bit(BNX2X_ACCEPT_MULTICAST, &accept);
-
- if (msg->rx_mask & VFPF_RX_MASK_ACCEPT_ALL_UNICAST)
- __set_bit(BNX2X_ACCEPT_ALL_UNICAST, &accept);
-
- if (msg->rx_mask & VFPF_RX_MASK_ACCEPT_ALL_MULTICAST)
- __set_bit(BNX2X_ACCEPT_ALL_MULTICAST, &accept);
+ }
- if (msg->rx_mask & VFPF_RX_MASK_ACCEPT_BROADCAST)
- __set_bit(BNX2X_ACCEPT_BROADCAST, &accept);
+ if (msg->flags & VFPF_SET_Q_FILTERS_RX_MASK_CHANGED) {
+ unsigned long accept = 0;
+ struct pf_vf_bulletin_content *bulletin =
+ BP_VF_BULLETIN(bp, vf->index);
- /* A packet arriving the vf's mac should be accepted
- * with any vlan, unless a vlan has already been
- * configured.
- */
- if (!(bulletin->valid_bitmap & (1 << VLAN_VALID)))
- __set_bit(BNX2X_ACCEPT_ANY_VLAN, &accept);
-
- /* set rx-mode */
- rc = bnx2x_vfop_rxmode_cmd(bp, vf, &cmd,
- msg->vf_qid, accept);
- if (rc) {
- vfop->rc = rc;
- goto op_err;
- }
- return;
+ /* Ignore VF requested mode; instead set a regular mode */
+ if (msg->rx_mask != VFPF_RX_MASK_ACCEPT_NONE) {
+ __set_bit(BNX2X_ACCEPT_UNICAST, &accept);
+ __set_bit(BNX2X_ACCEPT_MULTICAST, &accept);
+ __set_bit(BNX2X_ACCEPT_BROADCAST, &accept);
}
- /* fall through */
-
- case BNX2X_VFOP_MBX_Q_FILTERS_MCAST:
- /* next state */
- vfop->state = BNX2X_VFOP_MBX_Q_FILTERS_DONE;
-
- if (msg->flags & VFPF_SET_Q_FILTERS_MULTICAST_CHANGED) {
- /* set mcasts */
- rc = bnx2x_vfop_mcast_cmd(bp, vf, &cmd, msg->multicast,
- msg->n_multicast, false);
- if (rc) {
- vfop->rc = rc;
- goto op_err;
- }
- return;
- }
- /* fall through */
-op_done:
- case BNX2X_VFOP_MBX_Q_FILTERS_DONE:
- bnx2x_vfop_end(bp, vf, vfop);
- return;
-op_err:
- BNX2X_ERR("QFILTERS[%d:%d] error: rc %d\n",
- vf->abs_vfid, msg->vf_qid, vfop->rc);
- goto op_done;
- default:
- bnx2x_vfop_default(state);
+ /* A packet arriving the vf's mac should be accepted
+ * with any vlan, unless a vlan has already been
+ * configured.
+ */
+ if (!(bulletin->valid_bitmap & (1 << VLAN_VALID)))
+ __set_bit(BNX2X_ACCEPT_ANY_VLAN, &accept);
+
+ /* set rx-mode */
+ rc = bnx2x_vf_rxmode(bp, vf, msg->vf_qid, accept);
+ if (rc)
+ goto op_err;
}
-}
-static int bnx2x_vfop_mbx_qfilters_cmd(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vfop_cmd *cmd)
-{
- struct bnx2x_vfop *vfop = bnx2x_vfop_add(bp, vf);
- if (vfop) {
- bnx2x_vfop_opset(BNX2X_VFOP_MBX_Q_FILTERS_MACS,
- bnx2x_vfop_mbx_qfilters, cmd->done);
- return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_mbx_qfilters,
- cmd->block);
+ if (msg->flags & VFPF_SET_Q_FILTERS_MULTICAST_CHANGED) {
+ /* set mcasts */
+ rc = bnx2x_vf_mcast(bp, vf, msg->multicast,
+ msg->n_multicast, false);
+ if (rc)
+ goto op_err;
}
- return -ENOMEM;
+op_err:
+ if (rc)
+ BNX2X_ERR("QFILTERS[%d:%d] error: rc %d\n",
+ vf->abs_vfid, msg->vf_qid, rc);
+ return rc;
}
-static void bnx2x_vf_mbx_set_q_filters(struct bnx2x *bp,
- struct bnx2x_virtf *vf,
- struct bnx2x_vf_mbx *mbx)
+static int bnx2x_filters_validate_mac(struct bnx2x *bp,
+ struct bnx2x_virtf *vf,
+ struct vfpf_set_q_filters_tlv *filters)
{
- struct vfpf_set_q_filters_tlv *filters = &mbx->msg->req.set_q_filters;
struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vf->index);
- struct bnx2x_vfop_cmd cmd = {
- .done = bnx2x_vf_mbx_resp,
- .block = false,
- };
+ int rc = 0;
/* if a mac was already set for this VF via the set vf mac ndo, we only
* accept mac configurations of that mac. Why accept them at all?
@@ -1715,7 +1609,7 @@ static void bnx2x_vf_mbx_set_q_filters(struct bnx2x *bp,
if (filters->n_mac_vlan_filters > 1) {
BNX2X_ERR("VF[%d] requested the addition of multiple macs after set_vf_mac ndo was called\n",
vf->abs_vfid);
- vf->op_rc = -EPERM;
+ rc = -EPERM;
goto response;
}
@@ -1725,10 +1619,22 @@ static void bnx2x_vf_mbx_set_q_filters(struct bnx2x *bp,
BNX2X_ERR("VF[%d] requested the addition of a mac address not matching the one configured by set_vf_mac ndo\n",
vf->abs_vfid);
- vf->op_rc = -EPERM;
+ rc = -EPERM;
goto response;
}
}
+
+response:
+ return rc;
+}
+
+static int bnx2x_filters_validate_vlan(struct bnx2x *bp,
+ struct bnx2x_virtf *vf,
+ struct vfpf_set_q_filters_tlv *filters)
+{
+ struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vf->index);
+ int rc = 0;
+
/* if vlan was set by hypervisor we don't allow guest to config vlan */
if (bulletin->valid_bitmap & 1 << VLAN_VALID) {
int i;
@@ -1739,14 +1645,35 @@ static void bnx2x_vf_mbx_set_q_filters(struct bnx2x *bp,
VFPF_Q_FILTER_VLAN_TAG_VALID) {
BNX2X_ERR("VF[%d] attempted to configure vlan but one was already set by Hypervisor. Aborting request\n",
vf->abs_vfid);
- vf->op_rc = -EPERM;
+ rc = -EPERM;
goto response;
}
}
}
/* verify vf_qid */
- if (filters->vf_qid > vf_rxq_count(vf))
+ if (filters->vf_qid > vf_rxq_count(vf)) {
+ rc = -EPERM;
+ goto response;
+ }
+
+response:
+ return rc;
+}
+
+static void bnx2x_vf_mbx_set_q_filters(struct bnx2x *bp,
+ struct bnx2x_virtf *vf,
+ struct bnx2x_vf_mbx *mbx)
+{
+ struct vfpf_set_q_filters_tlv *filters = &mbx->msg->req.set_q_filters;
+ int rc;
+
+ rc = bnx2x_filters_validate_mac(bp, vf, filters);
+ if (rc)
+ goto response;
+
+ rc = bnx2x_filters_validate_vlan(bp, vf, filters);
+ if (rc)
goto response;
DP(BNX2X_MSG_IOV, "VF[%d] Q_FILTERS: queue[%d]\n",
@@ -1756,125 +1683,169 @@ static void bnx2x_vf_mbx_set_q_filters(struct bnx2x *bp,
/* print q_filter message */
bnx2x_vf_mbx_dp_q_filters(bp, BNX2X_MSG_IOV, filters);
- vf->op_rc = bnx2x_vfop_mbx_qfilters_cmd(bp, vf, &cmd);
- if (vf->op_rc)
- goto response;
- return;
-
+ rc = bnx2x_vf_mbx_qfilters(bp, vf);
response:
- bnx2x_vf_mbx_resp(bp, vf);
+ bnx2x_vf_mbx_resp(bp, vf, rc);
}
static void bnx2x_vf_mbx_teardown_q(struct bnx2x *bp, struct bnx2x_virtf *vf,
struct bnx2x_vf_mbx *mbx)
{
int qid = mbx->msg->req.q_op.vf_qid;
- struct bnx2x_vfop_cmd cmd = {
- .done = bnx2x_vf_mbx_resp,
- .block = false,
- };
+ int rc;
DP(BNX2X_MSG_IOV, "VF[%d] Q_TEARDOWN: vf_qid=%d\n",
vf->abs_vfid, qid);
- vf->op_rc = bnx2x_vfop_qdown_cmd(bp, vf, &cmd, qid);
- if (vf->op_rc)
- bnx2x_vf_mbx_resp(bp, vf);
+ rc = bnx2x_vf_queue_teardown(bp, vf, qid);
+ bnx2x_vf_mbx_resp(bp, vf, rc);
}
static void bnx2x_vf_mbx_close_vf(struct bnx2x *bp, struct bnx2x_virtf *vf,
struct bnx2x_vf_mbx *mbx)
{
- struct bnx2x_vfop_cmd cmd = {
- .done = bnx2x_vf_mbx_resp,
- .block = false,
- };
+ int rc;
DP(BNX2X_MSG_IOV, "VF[%d] VF_CLOSE\n", vf->abs_vfid);
- vf->op_rc = bnx2x_vfop_close_cmd(bp, vf, &cmd);
- if (vf->op_rc)
- bnx2x_vf_mbx_resp(bp, vf);
+ rc = bnx2x_vf_close(bp, vf);
+ bnx2x_vf_mbx_resp(bp, vf, rc);
}
static void bnx2x_vf_mbx_release_vf(struct bnx2x *bp, struct bnx2x_virtf *vf,
struct bnx2x_vf_mbx *mbx)
{
- struct bnx2x_vfop_cmd cmd = {
- .done = bnx2x_vf_mbx_resp,
- .block = false,
- };
+ int rc;
DP(BNX2X_MSG_IOV, "VF[%d] VF_RELEASE\n", vf->abs_vfid);
- vf->op_rc = bnx2x_vfop_release_cmd(bp, vf, &cmd);
- if (vf->op_rc)
- bnx2x_vf_mbx_resp(bp, vf);
+ rc = bnx2x_vf_free(bp, vf);
+ bnx2x_vf_mbx_resp(bp, vf, rc);
}
static void bnx2x_vf_mbx_update_rss(struct bnx2x *bp, struct bnx2x_virtf *vf,
struct bnx2x_vf_mbx *mbx)
{
- struct bnx2x_vfop_cmd cmd = {
- .done = bnx2x_vf_mbx_resp,
- .block = false,
- };
- struct bnx2x_config_rss_params *vf_op_params = &vf->op_params.rss;
+ struct bnx2x_config_rss_params rss;
struct vfpf_rss_tlv *rss_tlv = &mbx->msg->req.update_rss;
+ int rc = 0;
if (rss_tlv->ind_table_size != T_ETH_INDIRECTION_TABLE_SIZE ||
rss_tlv->rss_key_size != T_ETH_RSS_KEY) {
BNX2X_ERR("failing rss configuration of vf %d due to size mismatch\n",
vf->index);
- vf->op_rc = -EINVAL;
+ rc = -EINVAL;
goto mbx_resp;
}
+ memset(&rss, 0, sizeof(struct bnx2x_config_rss_params));
+
/* set vfop params according to rss tlv */
- memcpy(vf_op_params->ind_table, rss_tlv->ind_table,
+ memcpy(rss.ind_table, rss_tlv->ind_table,
T_ETH_INDIRECTION_TABLE_SIZE);
- memcpy(vf_op_params->rss_key, rss_tlv->rss_key,
- sizeof(rss_tlv->rss_key));
- vf_op_params->rss_obj = &vf->rss_conf_obj;
- vf_op_params->rss_result_mask = rss_tlv->rss_result_mask;
+ memcpy(rss.rss_key, rss_tlv->rss_key, sizeof(rss_tlv->rss_key));
+ rss.rss_obj = &vf->rss_conf_obj;
+ rss.rss_result_mask = rss_tlv->rss_result_mask;
/* flags handled individually for backward/forward compatability */
- vf_op_params->rss_flags = 0;
- vf_op_params->ramrod_flags = 0;
+ rss.rss_flags = 0;
+ rss.ramrod_flags = 0;
if (rss_tlv->rss_flags & VFPF_RSS_MODE_DISABLED)
- __set_bit(BNX2X_RSS_MODE_DISABLED, &vf_op_params->rss_flags);
+ __set_bit(BNX2X_RSS_MODE_DISABLED, &rss.rss_flags);
if (rss_tlv->rss_flags & VFPF_RSS_MODE_REGULAR)
- __set_bit(BNX2X_RSS_MODE_REGULAR, &vf_op_params->rss_flags);
+ __set_bit(BNX2X_RSS_MODE_REGULAR, &rss.rss_flags);
if (rss_tlv->rss_flags & VFPF_RSS_SET_SRCH)
- __set_bit(BNX2X_RSS_SET_SRCH, &vf_op_params->rss_flags);
+ __set_bit(BNX2X_RSS_SET_SRCH, &rss.rss_flags);
if (rss_tlv->rss_flags & VFPF_RSS_IPV4)
- __set_bit(BNX2X_RSS_IPV4, &vf_op_params->rss_flags);
+ __set_bit(BNX2X_RSS_IPV4, &rss.rss_flags);
if (rss_tlv->rss_flags & VFPF_RSS_IPV4_TCP)
- __set_bit(BNX2X_RSS_IPV4_TCP, &vf_op_params->rss_flags);
+ __set_bit(BNX2X_RSS_IPV4_TCP, &rss.rss_flags);
if (rss_tlv->rss_flags & VFPF_RSS_IPV4_UDP)
- __set_bit(BNX2X_RSS_IPV4_UDP, &vf_op_params->rss_flags);
+ __set_bit(BNX2X_RSS_IPV4_UDP, &rss.rss_flags);
if (rss_tlv->rss_flags & VFPF_RSS_IPV6)
- __set_bit(BNX2X_RSS_IPV6, &vf_op_params->rss_flags);
+ __set_bit(BNX2X_RSS_IPV6, &rss.rss_flags);
if (rss_tlv->rss_flags & VFPF_RSS_IPV6_TCP)
- __set_bit(BNX2X_RSS_IPV6_TCP, &vf_op_params->rss_flags);
+ __set_bit(BNX2X_RSS_IPV6_TCP, &rss.rss_flags);
if (rss_tlv->rss_flags & VFPF_RSS_IPV6_UDP)
- __set_bit(BNX2X_RSS_IPV6_UDP, &vf_op_params->rss_flags);
+ __set_bit(BNX2X_RSS_IPV6_UDP, &rss.rss_flags);
if ((!(rss_tlv->rss_flags & VFPF_RSS_IPV4_TCP) &&
rss_tlv->rss_flags & VFPF_RSS_IPV4_UDP) ||
(!(rss_tlv->rss_flags & VFPF_RSS_IPV6_TCP) &&
rss_tlv->rss_flags & VFPF_RSS_IPV6_UDP)) {
BNX2X_ERR("about to hit a FW assert. aborting...\n");
- vf->op_rc = -EINVAL;
+ rc = -EINVAL;
goto mbx_resp;
}
- vf->op_rc = bnx2x_vfop_rss_cmd(bp, vf, &cmd);
+ rc = bnx2x_vf_rss_update(bp, vf, &rss);
+mbx_resp:
+ bnx2x_vf_mbx_resp(bp, vf, rc);
+}
+
+static int bnx2x_validate_tpa_params(struct bnx2x *bp,
+ struct vfpf_tpa_tlv *tpa_tlv)
+{
+ int rc = 0;
+
+ if (tpa_tlv->tpa_client_info.max_sges_for_packet >
+ U_ETH_MAX_SGES_FOR_PACKET) {
+ rc = -EINVAL;
+ BNX2X_ERR("TPA update: max_sges received %d, max is %d\n",
+ tpa_tlv->tpa_client_info.max_sges_for_packet,
+ U_ETH_MAX_SGES_FOR_PACKET);
+ }
+
+ if (tpa_tlv->tpa_client_info.max_tpa_queues > MAX_AGG_QS(bp)) {
+ rc = -EINVAL;
+ BNX2X_ERR("TPA update: max_tpa_queues received %d, max is %d\n",
+ tpa_tlv->tpa_client_info.max_tpa_queues,
+ MAX_AGG_QS(bp));
+ }
+
+ return rc;
+}
+
+static void bnx2x_vf_mbx_update_tpa(struct bnx2x *bp, struct bnx2x_virtf *vf,
+ struct bnx2x_vf_mbx *mbx)
+{
+ struct bnx2x_queue_update_tpa_params vf_op_params;
+ struct vfpf_tpa_tlv *tpa_tlv = &mbx->msg->req.update_tpa;
+ int rc = 0;
+
+ memset(&vf_op_params, 0, sizeof(vf_op_params));
+
+ if (bnx2x_validate_tpa_params(bp, tpa_tlv))
+ goto mbx_resp;
+
+ vf_op_params.complete_on_both_clients =
+ tpa_tlv->tpa_client_info.complete_on_both_clients;
+ vf_op_params.dont_verify_thr =
+ tpa_tlv->tpa_client_info.dont_verify_thr;
+ vf_op_params.max_agg_sz =
+ tpa_tlv->tpa_client_info.max_agg_size;
+ vf_op_params.max_sges_pkt =
+ tpa_tlv->tpa_client_info.max_sges_for_packet;
+ vf_op_params.max_tpa_queues =
+ tpa_tlv->tpa_client_info.max_tpa_queues;
+ vf_op_params.sge_buff_sz =
+ tpa_tlv->tpa_client_info.sge_buff_size;
+ vf_op_params.sge_pause_thr_high =
+ tpa_tlv->tpa_client_info.sge_pause_thr_high;
+ vf_op_params.sge_pause_thr_low =
+ tpa_tlv->tpa_client_info.sge_pause_thr_low;
+ vf_op_params.tpa_mode =
+ tpa_tlv->tpa_client_info.tpa_mode;
+ vf_op_params.update_ipv4 =
+ tpa_tlv->tpa_client_info.update_ipv4;
+ vf_op_params.update_ipv6 =
+ tpa_tlv->tpa_client_info.update_ipv6;
+
+ rc = bnx2x_vf_tpa_update(bp, vf, tpa_tlv, &vf_op_params);
mbx_resp:
- if (vf->op_rc)
- bnx2x_vf_mbx_resp(bp, vf);
+ bnx2x_vf_mbx_resp(bp, vf, rc);
}
/* dispatch request */
@@ -1916,6 +1887,9 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf,
case CHANNEL_TLV_UPDATE_RSS:
bnx2x_vf_mbx_update_rss(bp, vf, mbx);
return;
+ case CHANNEL_TLV_UPDATE_TPA:
+ bnx2x_vf_mbx_update_tpa(bp, vf, mbx);
+ return;
}
} else {
@@ -1935,11 +1909,8 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf,
/* can we respond to VF (do we have an address for it?) */
if (vf->state == VF_ACQUIRED || vf->state == VF_ENABLED) {
- /* mbx_resp uses the op_rc of the VF */
- vf->op_rc = PFVF_STATUS_NOT_SUPPORTED;
-
/* notify the VF that we do not support this request */
- bnx2x_vf_mbx_resp(bp, vf);
+ bnx2x_vf_mbx_resp(bp, vf, PFVF_STATUS_NOT_SUPPORTED);
} else {
/* can't send a response since this VF is unknown to us
* just ack the FW to release the mailbox and unlock
@@ -1952,13 +1923,10 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf,
}
}
-/* handle new vf-pf message */
-void bnx2x_vf_mbx(struct bnx2x *bp, struct vf_pf_event_data *vfpf_event)
+void bnx2x_vf_mbx_schedule(struct bnx2x *bp,
+ struct vf_pf_event_data *vfpf_event)
{
- struct bnx2x_virtf *vf;
- struct bnx2x_vf_mbx *mbx;
u8 vf_idx;
- int rc;
DP(BNX2X_MSG_IOV,
"vf pf event received: vfid %d, address_hi %x, address lo %x",
@@ -1970,50 +1938,73 @@ void bnx2x_vf_mbx(struct bnx2x *bp, struct vf_pf_event_data *vfpf_event)
BNX2X_NR_VIRTFN(bp)) {
BNX2X_ERR("Illegal vf_id %d max allowed: %d\n",
vfpf_event->vf_id, BNX2X_NR_VIRTFN(bp));
- goto mbx_done;
+ return;
}
+
vf_idx = bnx2x_vf_idx_by_abs_fid(bp, vfpf_event->vf_id);
- mbx = BP_VF_MBX(bp, vf_idx);
- /* verify an event is not currently being processed -
- * debug failsafe only
- */
- if (mbx->flags & VF_MSG_INPROCESS) {
- BNX2X_ERR("Previous message is still being processed, vf_id %d\n",
- vfpf_event->vf_id);
- goto mbx_done;
- }
- vf = BP_VF(bp, vf_idx);
+ /* Update VFDB with current message and schedule its handling */
+ mutex_lock(&BP_VFDB(bp)->event_mutex);
+ BP_VF_MBX(bp, vf_idx)->vf_addr_hi = vfpf_event->msg_addr_hi;
+ BP_VF_MBX(bp, vf_idx)->vf_addr_lo = vfpf_event->msg_addr_lo;
+ BP_VFDB(bp)->event_occur |= (1ULL << vf_idx);
+ mutex_unlock(&BP_VFDB(bp)->event_mutex);
- /* save the VF message address */
- mbx->vf_addr_hi = vfpf_event->msg_addr_hi;
- mbx->vf_addr_lo = vfpf_event->msg_addr_lo;
- DP(BNX2X_MSG_IOV, "mailbox vf address hi 0x%x, lo 0x%x, offset 0x%x\n",
- mbx->vf_addr_hi, mbx->vf_addr_lo, mbx->first_tlv.resp_msg_offset);
+ bnx2x_schedule_iov_task(bp, BNX2X_IOV_HANDLE_VF_MSG);
+}
- /* dmae to get the VF request */
- rc = bnx2x_copy32_vf_dmae(bp, true, mbx->msg_mapping, vf->abs_vfid,
- mbx->vf_addr_hi, mbx->vf_addr_lo,
- sizeof(union vfpf_tlvs)/4);
- if (rc) {
- BNX2X_ERR("Failed to copy request VF %d\n", vf->abs_vfid);
- goto mbx_error;
- }
+/* handle new vf-pf messages */
+void bnx2x_vf_mbx(struct bnx2x *bp)
+{
+ struct bnx2x_vfdb *vfdb = BP_VFDB(bp);
+ u64 events;
+ u8 vf_idx;
+ int rc;
- /* process the VF message header */
- mbx->first_tlv = mbx->msg->req.first_tlv;
+ if (!vfdb)
+ return;
- /* Clean response buffer to refrain from falsely seeing chains */
- memset(&mbx->msg->resp, 0, sizeof(union pfvf_tlvs));
+ mutex_lock(&vfdb->event_mutex);
+ events = vfdb->event_occur;
+ vfdb->event_occur = 0;
+ mutex_unlock(&vfdb->event_mutex);
- /* dispatch the request (will prepare the response) */
- bnx2x_vf_mbx_request(bp, vf, mbx);
- goto mbx_done;
+ for_each_vf(bp, vf_idx) {
+ struct bnx2x_vf_mbx *mbx = BP_VF_MBX(bp, vf_idx);
+ struct bnx2x_virtf *vf = BP_VF(bp, vf_idx);
-mbx_error:
- bnx2x_vf_release(bp, vf, false); /* non blocking */
-mbx_done:
- return;
+ /* Handle VFs which have pending events */
+ if (!(events & (1ULL << vf_idx)))
+ continue;
+
+ DP(BNX2X_MSG_IOV,
+ "Handling vf pf event vfid %d, address: [%x:%x], resp_offset 0x%x\n",
+ vf_idx, mbx->vf_addr_hi, mbx->vf_addr_lo,
+ mbx->first_tlv.resp_msg_offset);
+
+ /* dmae to get the VF request */
+ rc = bnx2x_copy32_vf_dmae(bp, true, mbx->msg_mapping,
+ vf->abs_vfid, mbx->vf_addr_hi,
+ mbx->vf_addr_lo,
+ sizeof(union vfpf_tlvs)/4);
+ if (rc) {
+ BNX2X_ERR("Failed to copy request VF %d\n",
+ vf->abs_vfid);
+ bnx2x_vf_release(bp, vf);
+ return;
+ }
+
+ /* process the VF message header */
+ mbx->first_tlv = mbx->msg->req.first_tlv;
+
+ /* Clean response buffer to refrain from falsely
+ * seeing chains.
+ */
+ memset(&mbx->msg->resp, 0, sizeof(union pfvf_tlvs));
+
+ /* dispatch the request (will prepare the response) */
+ bnx2x_vf_mbx_request(bp, vf, mbx);
+ }
}
/* propagate local bulletin board to vf */
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h
index 208568bc7a71..c922b81170e5 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h
@@ -162,6 +162,7 @@ struct pfvf_acquire_resp_tlv {
#define PFVF_CAP_RSS 0x00000001
#define PFVF_CAP_DHC 0x00000002
#define PFVF_CAP_TPA 0x00000004
+#define PFVF_CAP_TPA_UPDATE 0x00000008
char fw_ver[32];
u16 db_size;
u8 indices_per_sb;
@@ -303,6 +304,25 @@ struct vfpf_set_q_filters_tlv {
u32 rx_mask; /* see mask constants at the top of the file */
};
+struct vfpf_tpa_tlv {
+ struct vfpf_first_tlv first_tlv;
+
+ struct vf_pf_tpa_client_info {
+ aligned_u64 sge_addr[PFVF_MAX_QUEUES_PER_VF];
+ u8 update_ipv4;
+ u8 update_ipv6;
+ u8 max_tpa_queues;
+ u8 max_sges_for_packet;
+ u8 complete_on_both_clients;
+ u8 dont_verify_thr;
+ u8 tpa_mode;
+ u16 sge_buff_size;
+ u16 max_agg_size;
+ u16 sge_pause_thr_low;
+ u16 sge_pause_thr_high;
+ } tpa_client_info;
+};
+
/* close VF (disable VF) */
struct vfpf_close_tlv {
struct vfpf_first_tlv first_tlv;
@@ -331,6 +351,7 @@ union vfpf_tlvs {
struct vfpf_set_q_filters_tlv set_q_filters;
struct vfpf_release_tlv release;
struct vfpf_rss_tlv update_rss;
+ struct vfpf_tpa_tlv update_tpa;
struct channel_list_end_tlv list_end;
struct tlv_buffer_size tlv_buf_size;
};
@@ -405,6 +426,7 @@ enum channel_tlvs {
CHANNEL_TLV_PF_SET_VLAN,
CHANNEL_TLV_UPDATE_RSS,
CHANNEL_TLV_PHYS_PORT_ID,
+ CHANNEL_TLV_UPDATE_TPA,
CHANNEL_TLV_MAX
};
diff --git a/drivers/net/ethernet/broadcom/genet/Makefile b/drivers/net/ethernet/broadcom/genet/Makefile
new file mode 100644
index 000000000000..31f55a90a197
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/genet/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_BCMGENET) += genet.o
+genet-objs := bcmgenet.o bcmmii.o
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
new file mode 100644
index 000000000000..adf8acbddf56
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -0,0 +1,2584 @@
+/*
+ * Broadcom GENET (Gigabit Ethernet) controller driver
+ *
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "bcmgenet: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm.h>
+#include <linux/clk.h>
+#include <linux/version.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <net/arp.h>
+
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/phy.h>
+
+#include <asm/unaligned.h>
+
+#include "bcmgenet.h"
+
+/* Maximum number of hardware queues, downsized if needed */
+#define GENET_MAX_MQ_CNT 4
+
+/* Default highest priority queue for multi queue support */
+#define GENET_Q0_PRIORITY 0
+
+#define GENET_DEFAULT_BD_CNT \
+ (TOTAL_DESC - priv->hw_params->tx_queues * priv->hw_params->bds_cnt)
+
+#define RX_BUF_LENGTH 2048
+#define SKB_ALIGNMENT 32
+
+/* Tx/Rx DMA register offset, skip 256 descriptors */
+#define WORDS_PER_BD(p) (p->hw_params->words_per_bd)
+#define DMA_DESC_SIZE (WORDS_PER_BD(priv) * sizeof(u32))
+
+#define GENET_TDMA_REG_OFF (priv->hw_params->tdma_offset + \
+ TOTAL_DESC * DMA_DESC_SIZE)
+
+#define GENET_RDMA_REG_OFF (priv->hw_params->rdma_offset + \
+ TOTAL_DESC * DMA_DESC_SIZE)
+
+static inline void dmadesc_set_length_status(struct bcmgenet_priv *priv,
+ void __iomem *d, u32 value)
+{
+ __raw_writel(value, d + DMA_DESC_LENGTH_STATUS);
+}
+
+static inline u32 dmadesc_get_length_status(struct bcmgenet_priv *priv,
+ void __iomem *d)
+{
+ return __raw_readl(d + DMA_DESC_LENGTH_STATUS);
+}
+
+static inline void dmadesc_set_addr(struct bcmgenet_priv *priv,
+ void __iomem *d,
+ dma_addr_t addr)
+{
+ __raw_writel(lower_32_bits(addr), d + DMA_DESC_ADDRESS_LO);
+
+ /* Register writes to GISB bus can take couple hundred nanoseconds
+ * and are done for each packet, save these expensive writes unless
+ * the platform is explicitely configured for 64-bits/LPAE.
+ */
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+ if (priv->hw_params->flags & GENET_HAS_40BITS)
+ __raw_writel(upper_32_bits(addr), d + DMA_DESC_ADDRESS_HI);
+#endif
+}
+
+/* Combined address + length/status setter */
+static inline void dmadesc_set(struct bcmgenet_priv *priv,
+ void __iomem *d, dma_addr_t addr, u32 val)
+{
+ dmadesc_set_length_status(priv, d, val);
+ dmadesc_set_addr(priv, d, addr);
+}
+
+static inline dma_addr_t dmadesc_get_addr(struct bcmgenet_priv *priv,
+ void __iomem *d)
+{
+ dma_addr_t addr;
+
+ addr = __raw_readl(d + DMA_DESC_ADDRESS_LO);
+
+ /* Register writes to GISB bus can take couple hundred nanoseconds
+ * and are done for each packet, save these expensive writes unless
+ * the platform is explicitely configured for 64-bits/LPAE.
+ */
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+ if (priv->hw_params->flags & GENET_HAS_40BITS)
+ addr |= (u64)__raw_readl(d + DMA_DESC_ADDRESS_HI) << 32;
+#endif
+ return addr;
+}
+
+#define GENET_VER_FMT "%1d.%1d EPHY: 0x%04x"
+
+#define GENET_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK)
+
+static inline u32 bcmgenet_rbuf_ctrl_get(struct bcmgenet_priv *priv)
+{
+ if (GENET_IS_V1(priv))
+ return bcmgenet_rbuf_readl(priv, RBUF_FLUSH_CTRL_V1);
+ else
+ return bcmgenet_sys_readl(priv, SYS_RBUF_FLUSH_CTRL);
+}
+
+static inline void bcmgenet_rbuf_ctrl_set(struct bcmgenet_priv *priv, u32 val)
+{
+ if (GENET_IS_V1(priv))
+ bcmgenet_rbuf_writel(priv, val, RBUF_FLUSH_CTRL_V1);
+ else
+ bcmgenet_sys_writel(priv, val, SYS_RBUF_FLUSH_CTRL);
+}
+
+/* These macros are defined to deal with register map change
+ * between GENET1.1 and GENET2. Only those currently being used
+ * by driver are defined.
+ */
+static inline u32 bcmgenet_tbuf_ctrl_get(struct bcmgenet_priv *priv)
+{
+ if (GENET_IS_V1(priv))
+ return bcmgenet_rbuf_readl(priv, TBUF_CTRL_V1);
+ else
+ return __raw_readl(priv->base +
+ priv->hw_params->tbuf_offset + TBUF_CTRL);
+}
+
+static inline void bcmgenet_tbuf_ctrl_set(struct bcmgenet_priv *priv, u32 val)
+{
+ if (GENET_IS_V1(priv))
+ bcmgenet_rbuf_writel(priv, val, TBUF_CTRL_V1);
+ else
+ __raw_writel(val, priv->base +
+ priv->hw_params->tbuf_offset + TBUF_CTRL);
+}
+
+static inline u32 bcmgenet_bp_mc_get(struct bcmgenet_priv *priv)
+{
+ if (GENET_IS_V1(priv))
+ return bcmgenet_rbuf_readl(priv, TBUF_BP_MC_V1);
+ else
+ return __raw_readl(priv->base +
+ priv->hw_params->tbuf_offset + TBUF_BP_MC);
+}
+
+static inline void bcmgenet_bp_mc_set(struct bcmgenet_priv *priv, u32 val)
+{
+ if (GENET_IS_V1(priv))
+ bcmgenet_rbuf_writel(priv, val, TBUF_BP_MC_V1);
+ else
+ __raw_writel(val, priv->base +
+ priv->hw_params->tbuf_offset + TBUF_BP_MC);
+}
+
+/* RX/TX DMA register accessors */
+enum dma_reg {
+ DMA_RING_CFG = 0,
+ DMA_CTRL,
+ DMA_STATUS,
+ DMA_SCB_BURST_SIZE,
+ DMA_ARB_CTRL,
+ DMA_PRIORITY,
+ DMA_RING_PRIORITY,
+};
+
+static const u8 bcmgenet_dma_regs_v3plus[] = {
+ [DMA_RING_CFG] = 0x00,
+ [DMA_CTRL] = 0x04,
+ [DMA_STATUS] = 0x08,
+ [DMA_SCB_BURST_SIZE] = 0x0C,
+ [DMA_ARB_CTRL] = 0x2C,
+ [DMA_PRIORITY] = 0x30,
+ [DMA_RING_PRIORITY] = 0x38,
+};
+
+static const u8 bcmgenet_dma_regs_v2[] = {
+ [DMA_RING_CFG] = 0x00,
+ [DMA_CTRL] = 0x04,
+ [DMA_STATUS] = 0x08,
+ [DMA_SCB_BURST_SIZE] = 0x0C,
+ [DMA_ARB_CTRL] = 0x30,
+ [DMA_PRIORITY] = 0x34,
+ [DMA_RING_PRIORITY] = 0x3C,
+};
+
+static const u8 bcmgenet_dma_regs_v1[] = {
+ [DMA_CTRL] = 0x00,
+ [DMA_STATUS] = 0x04,
+ [DMA_SCB_BURST_SIZE] = 0x0C,
+ [DMA_ARB_CTRL] = 0x30,
+ [DMA_PRIORITY] = 0x34,
+ [DMA_RING_PRIORITY] = 0x3C,
+};
+
+/* Set at runtime once bcmgenet version is known */
+static const u8 *bcmgenet_dma_regs;
+
+static inline struct bcmgenet_priv *dev_to_priv(struct device *dev)
+{
+ return netdev_priv(dev_get_drvdata(dev));
+}
+
+static inline u32 bcmgenet_tdma_readl(struct bcmgenet_priv *priv,
+ enum dma_reg r)
+{
+ return __raw_readl(priv->base + GENET_TDMA_REG_OFF +
+ DMA_RINGS_SIZE + bcmgenet_dma_regs[r]);
+}
+
+static inline void bcmgenet_tdma_writel(struct bcmgenet_priv *priv,
+ u32 val, enum dma_reg r)
+{
+ __raw_writel(val, priv->base + GENET_TDMA_REG_OFF +
+ DMA_RINGS_SIZE + bcmgenet_dma_regs[r]);
+}
+
+static inline u32 bcmgenet_rdma_readl(struct bcmgenet_priv *priv,
+ enum dma_reg r)
+{
+ return __raw_readl(priv->base + GENET_RDMA_REG_OFF +
+ DMA_RINGS_SIZE + bcmgenet_dma_regs[r]);
+}
+
+static inline void bcmgenet_rdma_writel(struct bcmgenet_priv *priv,
+ u32 val, enum dma_reg r)
+{
+ __raw_writel(val, priv->base + GENET_RDMA_REG_OFF +
+ DMA_RINGS_SIZE + bcmgenet_dma_regs[r]);
+}
+
+/* RDMA/TDMA ring registers and accessors
+ * we merge the common fields and just prefix with T/D the registers
+ * having different meaning depending on the direction
+ */
+enum dma_ring_reg {
+ TDMA_READ_PTR = 0,
+ RDMA_WRITE_PTR = TDMA_READ_PTR,
+ TDMA_READ_PTR_HI,
+ RDMA_WRITE_PTR_HI = TDMA_READ_PTR_HI,
+ TDMA_CONS_INDEX,
+ RDMA_PROD_INDEX = TDMA_CONS_INDEX,
+ TDMA_PROD_INDEX,
+ RDMA_CONS_INDEX = TDMA_PROD_INDEX,
+ DMA_RING_BUF_SIZE,
+ DMA_START_ADDR,
+ DMA_START_ADDR_HI,
+ DMA_END_ADDR,
+ DMA_END_ADDR_HI,
+ DMA_MBUF_DONE_THRESH,
+ TDMA_FLOW_PERIOD,
+ RDMA_XON_XOFF_THRESH = TDMA_FLOW_PERIOD,
+ TDMA_WRITE_PTR,
+ RDMA_READ_PTR = TDMA_WRITE_PTR,
+ TDMA_WRITE_PTR_HI,
+ RDMA_READ_PTR_HI = TDMA_WRITE_PTR_HI
+};
+
+/* GENET v4 supports 40-bits pointer addressing
+ * for obvious reasons the LO and HI word parts
+ * are contiguous, but this offsets the other
+ * registers.
+ */
+static const u8 genet_dma_ring_regs_v4[] = {
+ [TDMA_READ_PTR] = 0x00,
+ [TDMA_READ_PTR_HI] = 0x04,
+ [TDMA_CONS_INDEX] = 0x08,
+ [TDMA_PROD_INDEX] = 0x0C,
+ [DMA_RING_BUF_SIZE] = 0x10,
+ [DMA_START_ADDR] = 0x14,
+ [DMA_START_ADDR_HI] = 0x18,
+ [DMA_END_ADDR] = 0x1C,
+ [DMA_END_ADDR_HI] = 0x20,
+ [DMA_MBUF_DONE_THRESH] = 0x24,
+ [TDMA_FLOW_PERIOD] = 0x28,
+ [TDMA_WRITE_PTR] = 0x2C,
+ [TDMA_WRITE_PTR_HI] = 0x30,
+};
+
+static const u8 genet_dma_ring_regs_v123[] = {
+ [TDMA_READ_PTR] = 0x00,
+ [TDMA_CONS_INDEX] = 0x04,
+ [TDMA_PROD_INDEX] = 0x08,
+ [DMA_RING_BUF_SIZE] = 0x0C,
+ [DMA_START_ADDR] = 0x10,
+ [DMA_END_ADDR] = 0x14,
+ [DMA_MBUF_DONE_THRESH] = 0x18,
+ [TDMA_FLOW_PERIOD] = 0x1C,
+ [TDMA_WRITE_PTR] = 0x20,
+};
+
+/* Set at runtime once GENET version is known */
+static const u8 *genet_dma_ring_regs;
+
+static inline u32 bcmgenet_tdma_ring_readl(struct bcmgenet_priv *priv,
+ unsigned int ring,
+ enum dma_ring_reg r)
+{
+ return __raw_readl(priv->base + GENET_TDMA_REG_OFF +
+ (DMA_RING_SIZE * ring) +
+ genet_dma_ring_regs[r]);
+}
+
+static inline void bcmgenet_tdma_ring_writel(struct bcmgenet_priv *priv,
+ unsigned int ring,
+ u32 val,
+ enum dma_ring_reg r)
+{
+ __raw_writel(val, priv->base + GENET_TDMA_REG_OFF +
+ (DMA_RING_SIZE * ring) +
+ genet_dma_ring_regs[r]);
+}
+
+static inline u32 bcmgenet_rdma_ring_readl(struct bcmgenet_priv *priv,
+ unsigned int ring,
+ enum dma_ring_reg r)
+{
+ return __raw_readl(priv->base + GENET_RDMA_REG_OFF +
+ (DMA_RING_SIZE * ring) +
+ genet_dma_ring_regs[r]);
+}
+
+static inline void bcmgenet_rdma_ring_writel(struct bcmgenet_priv *priv,
+ unsigned int ring,
+ u32 val,
+ enum dma_ring_reg r)
+{
+ __raw_writel(val, priv->base + GENET_RDMA_REG_OFF +
+ (DMA_RING_SIZE * ring) +
+ genet_dma_ring_regs[r]);
+}
+
+static int bcmgenet_get_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!priv->phydev)
+ return -ENODEV;
+
+ return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int bcmgenet_set_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!priv->phydev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static int bcmgenet_set_rx_csum(struct net_device *dev,
+ netdev_features_t wanted)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ u32 rbuf_chk_ctrl;
+ bool rx_csum_en;
+
+ rx_csum_en = !!(wanted & NETIF_F_RXCSUM);
+
+ rbuf_chk_ctrl = bcmgenet_rbuf_readl(priv, RBUF_CHK_CTRL);
+
+ /* enable rx checksumming */
+ if (rx_csum_en)
+ rbuf_chk_ctrl |= RBUF_RXCHK_EN;
+ else
+ rbuf_chk_ctrl &= ~RBUF_RXCHK_EN;
+ priv->desc_rxchk_en = rx_csum_en;
+
+ /* If UniMAC forwards CRC, we need to skip over it to get
+ * a valid CHK bit to be set in the per-packet status word
+ */
+ if (rx_csum_en && priv->crc_fwd_en)
+ rbuf_chk_ctrl |= RBUF_SKIP_FCS;
+ else
+ rbuf_chk_ctrl &= ~RBUF_SKIP_FCS;
+
+ bcmgenet_rbuf_writel(priv, rbuf_chk_ctrl, RBUF_CHK_CTRL);
+
+ return 0;
+}
+
+static int bcmgenet_set_tx_csum(struct net_device *dev,
+ netdev_features_t wanted)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ bool desc_64b_en;
+ u32 tbuf_ctrl, rbuf_ctrl;
+
+ tbuf_ctrl = bcmgenet_tbuf_ctrl_get(priv);
+ rbuf_ctrl = bcmgenet_rbuf_readl(priv, RBUF_CTRL);
+
+ desc_64b_en = !!(wanted & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM));
+
+ /* enable 64 bytes descriptor in both directions (RBUF and TBUF) */
+ if (desc_64b_en) {
+ tbuf_ctrl |= RBUF_64B_EN;
+ rbuf_ctrl |= RBUF_64B_EN;
+ } else {
+ tbuf_ctrl &= ~RBUF_64B_EN;
+ rbuf_ctrl &= ~RBUF_64B_EN;
+ }
+ priv->desc_64b_en = desc_64b_en;
+
+ bcmgenet_tbuf_ctrl_set(priv, tbuf_ctrl);
+ bcmgenet_rbuf_writel(priv, rbuf_ctrl, RBUF_CTRL);
+
+ return 0;
+}
+
+static int bcmgenet_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ netdev_features_t changed = features ^ dev->features;
+ netdev_features_t wanted = dev->wanted_features;
+ int ret = 0;
+
+ if (changed & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
+ ret = bcmgenet_set_tx_csum(dev, wanted);
+ if (changed & (NETIF_F_RXCSUM))
+ ret = bcmgenet_set_rx_csum(dev, wanted);
+
+ return ret;
+}
+
+static u32 bcmgenet_get_msglevel(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+
+ return priv->msg_enable;
+}
+
+static void bcmgenet_set_msglevel(struct net_device *dev, u32 level)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+
+ priv->msg_enable = level;
+}
+
+/* standard ethtool support functions. */
+enum bcmgenet_stat_type {
+ BCMGENET_STAT_NETDEV = -1,
+ BCMGENET_STAT_MIB_RX,
+ BCMGENET_STAT_MIB_TX,
+ BCMGENET_STAT_RUNT,
+ BCMGENET_STAT_MISC,
+};
+
+struct bcmgenet_stats {
+ char stat_string[ETH_GSTRING_LEN];
+ int stat_sizeof;
+ int stat_offset;
+ enum bcmgenet_stat_type type;
+ /* reg offset from UMAC base for misc counters */
+ u16 reg_offset;
+};
+
+#define STAT_NETDEV(m) { \
+ .stat_string = __stringify(m), \
+ .stat_sizeof = sizeof(((struct net_device_stats *)0)->m), \
+ .stat_offset = offsetof(struct net_device_stats, m), \
+ .type = BCMGENET_STAT_NETDEV, \
+}
+
+#define STAT_GENET_MIB(str, m, _type) { \
+ .stat_string = str, \
+ .stat_sizeof = sizeof(((struct bcmgenet_priv *)0)->m), \
+ .stat_offset = offsetof(struct bcmgenet_priv, m), \
+ .type = _type, \
+}
+
+#define STAT_GENET_MIB_RX(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_MIB_RX)
+#define STAT_GENET_MIB_TX(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_MIB_TX)
+#define STAT_GENET_RUNT(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_RUNT)
+
+#define STAT_GENET_MISC(str, m, offset) { \
+ .stat_string = str, \
+ .stat_sizeof = sizeof(((struct bcmgenet_priv *)0)->m), \
+ .stat_offset = offsetof(struct bcmgenet_priv, m), \
+ .type = BCMGENET_STAT_MISC, \
+ .reg_offset = offset, \
+}
+
+
+/* There is a 0xC gap between the end of RX and beginning of TX stats and then
+ * between the end of TX stats and the beginning of the RX RUNT
+ */
+#define BCMGENET_STAT_OFFSET 0xc
+
+/* Hardware counters must be kept in sync because the order/offset
+ * is important here (order in structure declaration = order in hardware)
+ */
+static const struct bcmgenet_stats bcmgenet_gstrings_stats[] = {
+ /* general stats */
+ STAT_NETDEV(rx_packets),
+ STAT_NETDEV(tx_packets),
+ STAT_NETDEV(rx_bytes),
+ STAT_NETDEV(tx_bytes),
+ STAT_NETDEV(rx_errors),
+ STAT_NETDEV(tx_errors),
+ STAT_NETDEV(rx_dropped),
+ STAT_NETDEV(tx_dropped),
+ STAT_NETDEV(multicast),
+ /* UniMAC RSV counters */
+ STAT_GENET_MIB_RX("rx_64_octets", mib.rx.pkt_cnt.cnt_64),
+ STAT_GENET_MIB_RX("rx_65_127_oct", mib.rx.pkt_cnt.cnt_127),
+ STAT_GENET_MIB_RX("rx_128_255_oct", mib.rx.pkt_cnt.cnt_255),
+ STAT_GENET_MIB_RX("rx_256_511_oct", mib.rx.pkt_cnt.cnt_511),
+ STAT_GENET_MIB_RX("rx_512_1023_oct", mib.rx.pkt_cnt.cnt_1023),
+ STAT_GENET_MIB_RX("rx_1024_1518_oct", mib.rx.pkt_cnt.cnt_1518),
+ STAT_GENET_MIB_RX("rx_vlan_1519_1522_oct", mib.rx.pkt_cnt.cnt_mgv),
+ STAT_GENET_MIB_RX("rx_1522_2047_oct", mib.rx.pkt_cnt.cnt_2047),
+ STAT_GENET_MIB_RX("rx_2048_4095_oct", mib.rx.pkt_cnt.cnt_4095),
+ STAT_GENET_MIB_RX("rx_4096_9216_oct", mib.rx.pkt_cnt.cnt_9216),
+ STAT_GENET_MIB_RX("rx_pkts", mib.rx.pkt),
+ STAT_GENET_MIB_RX("rx_bytes", mib.rx.bytes),
+ STAT_GENET_MIB_RX("rx_multicast", mib.rx.mca),
+ STAT_GENET_MIB_RX("rx_broadcast", mib.rx.bca),
+ STAT_GENET_MIB_RX("rx_fcs", mib.rx.fcs),
+ STAT_GENET_MIB_RX("rx_control", mib.rx.cf),
+ STAT_GENET_MIB_RX("rx_pause", mib.rx.pf),
+ STAT_GENET_MIB_RX("rx_unknown", mib.rx.uo),
+ STAT_GENET_MIB_RX("rx_align", mib.rx.aln),
+ STAT_GENET_MIB_RX("rx_outrange", mib.rx.flr),
+ STAT_GENET_MIB_RX("rx_code", mib.rx.cde),
+ STAT_GENET_MIB_RX("rx_carrier", mib.rx.fcr),
+ STAT_GENET_MIB_RX("rx_oversize", mib.rx.ovr),
+ STAT_GENET_MIB_RX("rx_jabber", mib.rx.jbr),
+ STAT_GENET_MIB_RX("rx_mtu_err", mib.rx.mtue),
+ STAT_GENET_MIB_RX("rx_good_pkts", mib.rx.pok),
+ STAT_GENET_MIB_RX("rx_unicast", mib.rx.uc),
+ STAT_GENET_MIB_RX("rx_ppp", mib.rx.ppp),
+ STAT_GENET_MIB_RX("rx_crc", mib.rx.rcrc),
+ /* UniMAC TSV counters */
+ STAT_GENET_MIB_TX("tx_64_octets", mib.tx.pkt_cnt.cnt_64),
+ STAT_GENET_MIB_TX("tx_65_127_oct", mib.tx.pkt_cnt.cnt_127),
+ STAT_GENET_MIB_TX("tx_128_255_oct", mib.tx.pkt_cnt.cnt_255),
+ STAT_GENET_MIB_TX("tx_256_511_oct", mib.tx.pkt_cnt.cnt_511),
+ STAT_GENET_MIB_TX("tx_512_1023_oct", mib.tx.pkt_cnt.cnt_1023),
+ STAT_GENET_MIB_TX("tx_1024_1518_oct", mib.tx.pkt_cnt.cnt_1518),
+ STAT_GENET_MIB_TX("tx_vlan_1519_1522_oct", mib.tx.pkt_cnt.cnt_mgv),
+ STAT_GENET_MIB_TX("tx_1522_2047_oct", mib.tx.pkt_cnt.cnt_2047),
+ STAT_GENET_MIB_TX("tx_2048_4095_oct", mib.tx.pkt_cnt.cnt_4095),
+ STAT_GENET_MIB_TX("tx_4096_9216_oct", mib.tx.pkt_cnt.cnt_9216),
+ STAT_GENET_MIB_TX("tx_pkts", mib.tx.pkts),
+ STAT_GENET_MIB_TX("tx_multicast", mib.tx.mca),
+ STAT_GENET_MIB_TX("tx_broadcast", mib.tx.bca),
+ STAT_GENET_MIB_TX("tx_pause", mib.tx.pf),
+ STAT_GENET_MIB_TX("tx_control", mib.tx.cf),
+ STAT_GENET_MIB_TX("tx_fcs_err", mib.tx.fcs),
+ STAT_GENET_MIB_TX("tx_oversize", mib.tx.ovr),
+ STAT_GENET_MIB_TX("tx_defer", mib.tx.drf),
+ STAT_GENET_MIB_TX("tx_excess_defer", mib.tx.edf),
+ STAT_GENET_MIB_TX("tx_single_col", mib.tx.scl),
+ STAT_GENET_MIB_TX("tx_multi_col", mib.tx.mcl),
+ STAT_GENET_MIB_TX("tx_late_col", mib.tx.lcl),
+ STAT_GENET_MIB_TX("tx_excess_col", mib.tx.ecl),
+ STAT_GENET_MIB_TX("tx_frags", mib.tx.frg),
+ STAT_GENET_MIB_TX("tx_total_col", mib.tx.ncl),
+ STAT_GENET_MIB_TX("tx_jabber", mib.tx.jbr),
+ STAT_GENET_MIB_TX("tx_bytes", mib.tx.bytes),
+ STAT_GENET_MIB_TX("tx_good_pkts", mib.tx.pok),
+ STAT_GENET_MIB_TX("tx_unicast", mib.tx.uc),
+ /* UniMAC RUNT counters */
+ STAT_GENET_RUNT("rx_runt_pkts", mib.rx_runt_cnt),
+ STAT_GENET_RUNT("rx_runt_valid_fcs", mib.rx_runt_fcs),
+ STAT_GENET_RUNT("rx_runt_inval_fcs_align", mib.rx_runt_fcs_align),
+ STAT_GENET_RUNT("rx_runt_bytes", mib.rx_runt_bytes),
+ /* Misc UniMAC counters */
+ STAT_GENET_MISC("rbuf_ovflow_cnt", mib.rbuf_ovflow_cnt,
+ UMAC_RBUF_OVFL_CNT),
+ STAT_GENET_MISC("rbuf_err_cnt", mib.rbuf_err_cnt, UMAC_RBUF_ERR_CNT),
+ STAT_GENET_MISC("mdf_err_cnt", mib.mdf_err_cnt, UMAC_MDF_ERR_CNT),
+};
+
+#define BCMGENET_STATS_LEN ARRAY_SIZE(bcmgenet_gstrings_stats)
+
+static void bcmgenet_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, "bcmgenet", sizeof(info->driver));
+ strlcpy(info->version, "v2.0", sizeof(info->version));
+ info->n_stats = BCMGENET_STATS_LEN;
+
+}
+
+static int bcmgenet_get_sset_count(struct net_device *dev, int string_set)
+{
+ switch (string_set) {
+ case ETH_SS_STATS:
+ return BCMGENET_STATS_LEN;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void bcmgenet_get_strings(struct net_device *dev,
+ u32 stringset, u8 *data)
+{
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < BCMGENET_STATS_LEN; i++) {
+ memcpy(data + i * ETH_GSTRING_LEN,
+ bcmgenet_gstrings_stats[i].stat_string,
+ ETH_GSTRING_LEN);
+ }
+ break;
+ }
+}
+
+static void bcmgenet_update_mib_counters(struct bcmgenet_priv *priv)
+{
+ int i, j = 0;
+
+ for (i = 0; i < BCMGENET_STATS_LEN; i++) {
+ const struct bcmgenet_stats *s;
+ u8 offset = 0;
+ u32 val = 0;
+ char *p;
+
+ s = &bcmgenet_gstrings_stats[i];
+ switch (s->type) {
+ case BCMGENET_STAT_NETDEV:
+ continue;
+ case BCMGENET_STAT_MIB_RX:
+ case BCMGENET_STAT_MIB_TX:
+ case BCMGENET_STAT_RUNT:
+ if (s->type != BCMGENET_STAT_MIB_RX)
+ offset = BCMGENET_STAT_OFFSET;
+ val = bcmgenet_umac_readl(priv, UMAC_MIB_START +
+ j + offset);
+ break;
+ case BCMGENET_STAT_MISC:
+ val = bcmgenet_umac_readl(priv, s->reg_offset);
+ /* clear if overflowed */
+ if (val == ~0)
+ bcmgenet_umac_writel(priv, 0, s->reg_offset);
+ break;
+ }
+
+ j += s->stat_sizeof;
+ p = (char *)priv + s->stat_offset;
+ *(u32 *)p = val;
+ }
+}
+
+static void bcmgenet_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats,
+ u64 *data)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ int i;
+
+ if (netif_running(dev))
+ bcmgenet_update_mib_counters(priv);
+
+ for (i = 0; i < BCMGENET_STATS_LEN; i++) {
+ const struct bcmgenet_stats *s;
+ char *p;
+
+ s = &bcmgenet_gstrings_stats[i];
+ if (s->type == BCMGENET_STAT_NETDEV)
+ p = (char *)&dev->stats;
+ else
+ p = (char *)priv;
+ p += s->stat_offset;
+ data[i] = *(u32 *)p;
+ }
+}
+
+/* standard ethtool support functions. */
+static struct ethtool_ops bcmgenet_ethtool_ops = {
+ .get_strings = bcmgenet_get_strings,
+ .get_sset_count = bcmgenet_get_sset_count,
+ .get_ethtool_stats = bcmgenet_get_ethtool_stats,
+ .get_settings = bcmgenet_get_settings,
+ .set_settings = bcmgenet_set_settings,
+ .get_drvinfo = bcmgenet_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_msglevel = bcmgenet_get_msglevel,
+ .set_msglevel = bcmgenet_set_msglevel,
+};
+
+/* Power down the unimac, based on mode. */
+static void bcmgenet_power_down(struct bcmgenet_priv *priv,
+ enum bcmgenet_power_mode mode)
+{
+ u32 reg;
+
+ switch (mode) {
+ case GENET_POWER_CABLE_SENSE:
+ phy_detach(priv->phydev);
+ break;
+
+ case GENET_POWER_PASSIVE:
+ /* Power down LED */
+ bcmgenet_mii_reset(priv->dev);
+ if (priv->hw_params->flags & GENET_HAS_EXT) {
+ reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
+ reg |= (EXT_PWR_DOWN_PHY |
+ EXT_PWR_DOWN_DLL | EXT_PWR_DOWN_BIAS);
+ bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void bcmgenet_power_up(struct bcmgenet_priv *priv,
+ enum bcmgenet_power_mode mode)
+{
+ u32 reg;
+
+ if (!(priv->hw_params->flags & GENET_HAS_EXT))
+ return;
+
+ reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
+
+ switch (mode) {
+ case GENET_POWER_PASSIVE:
+ reg &= ~(EXT_PWR_DOWN_DLL | EXT_PWR_DOWN_PHY |
+ EXT_PWR_DOWN_BIAS);
+ /* fallthrough */
+ case GENET_POWER_CABLE_SENSE:
+ /* enable APD */
+ reg |= EXT_PWR_DN_EN_LD;
+ break;
+ default:
+ break;
+ }
+
+ bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
+ bcmgenet_mii_reset(priv->dev);
+}
+
+/* ioctl handle special commands that are not present in ethtool. */
+static int bcmgenet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ int val = 0;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ if (!priv->phydev)
+ val = -ENODEV;
+ else
+ val = phy_mii_ioctl(priv->phydev, rq, cmd);
+ break;
+
+ default:
+ val = -EINVAL;
+ break;
+ }
+
+ return val;
+}
+
+static struct enet_cb *bcmgenet_get_txcb(struct bcmgenet_priv *priv,
+ struct bcmgenet_tx_ring *ring)
+{
+ struct enet_cb *tx_cb_ptr;
+
+ tx_cb_ptr = ring->cbs;
+ tx_cb_ptr += ring->write_ptr - ring->cb_ptr;
+ tx_cb_ptr->bd_addr = priv->tx_bds + ring->write_ptr * DMA_DESC_SIZE;
+ /* Advancing local write pointer */
+ if (ring->write_ptr == ring->end_ptr)
+ ring->write_ptr = ring->cb_ptr;
+ else
+ ring->write_ptr++;
+
+ return tx_cb_ptr;
+}
+
+/* Simple helper to free a control block's resources */
+static void bcmgenet_free_cb(struct enet_cb *cb)
+{
+ dev_kfree_skb_any(cb->skb);
+ cb->skb = NULL;
+ dma_unmap_addr_set(cb, dma_addr, 0);
+}
+
+static inline void bcmgenet_tx_ring16_int_disable(struct bcmgenet_priv *priv,
+ struct bcmgenet_tx_ring *ring)
+{
+ bcmgenet_intrl2_0_writel(priv,
+ UMAC_IRQ_TXDMA_BDONE | UMAC_IRQ_TXDMA_PDONE,
+ INTRL2_CPU_MASK_SET);
+}
+
+static inline void bcmgenet_tx_ring16_int_enable(struct bcmgenet_priv *priv,
+ struct bcmgenet_tx_ring *ring)
+{
+ bcmgenet_intrl2_0_writel(priv,
+ UMAC_IRQ_TXDMA_BDONE | UMAC_IRQ_TXDMA_PDONE,
+ INTRL2_CPU_MASK_CLEAR);
+}
+
+static inline void bcmgenet_tx_ring_int_enable(struct bcmgenet_priv *priv,
+ struct bcmgenet_tx_ring *ring)
+{
+ bcmgenet_intrl2_1_writel(priv,
+ (1 << ring->index), INTRL2_CPU_MASK_CLEAR);
+ priv->int1_mask &= ~(1 << ring->index);
+}
+
+static inline void bcmgenet_tx_ring_int_disable(struct bcmgenet_priv *priv,
+ struct bcmgenet_tx_ring *ring)
+{
+ bcmgenet_intrl2_1_writel(priv,
+ (1 << ring->index), INTRL2_CPU_MASK_SET);
+ priv->int1_mask |= (1 << ring->index);
+}
+
+/* Unlocked version of the reclaim routine */
+static void __bcmgenet_tx_reclaim(struct net_device *dev,
+ struct bcmgenet_tx_ring *ring)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ int last_tx_cn, last_c_index, num_tx_bds;
+ struct enet_cb *tx_cb_ptr;
+ struct netdev_queue *txq;
+ unsigned int c_index;
+
+ /* Compute how many buffers are transmited since last xmit call */
+ c_index = bcmgenet_tdma_ring_readl(priv, ring->index, TDMA_CONS_INDEX);
+ txq = netdev_get_tx_queue(dev, ring->queue);
+
+ last_c_index = ring->c_index;
+ num_tx_bds = ring->size;
+
+ c_index &= (num_tx_bds - 1);
+
+ if (c_index >= last_c_index)
+ last_tx_cn = c_index - last_c_index;
+ else
+ last_tx_cn = num_tx_bds - last_c_index + c_index;
+
+ netif_dbg(priv, tx_done, dev,
+ "%s ring=%d index=%d last_tx_cn=%d last_index=%d\n",
+ __func__, ring->index,
+ c_index, last_tx_cn, last_c_index);
+
+ /* Reclaim transmitted buffers */
+ while (last_tx_cn-- > 0) {
+ tx_cb_ptr = ring->cbs + last_c_index;
+ if (tx_cb_ptr->skb) {
+ dev->stats.tx_bytes += tx_cb_ptr->skb->len;
+ dma_unmap_single(&dev->dev,
+ dma_unmap_addr(tx_cb_ptr, dma_addr),
+ tx_cb_ptr->skb->len,
+ DMA_TO_DEVICE);
+ bcmgenet_free_cb(tx_cb_ptr);
+ } else if (dma_unmap_addr(tx_cb_ptr, dma_addr)) {
+ dev->stats.tx_bytes +=
+ dma_unmap_len(tx_cb_ptr, dma_len);
+ dma_unmap_page(&dev->dev,
+ dma_unmap_addr(tx_cb_ptr, dma_addr),
+ dma_unmap_len(tx_cb_ptr, dma_len),
+ DMA_TO_DEVICE);
+ dma_unmap_addr_set(tx_cb_ptr, dma_addr, 0);
+ }
+ dev->stats.tx_packets++;
+ ring->free_bds += 1;
+
+ last_c_index++;
+ last_c_index &= (num_tx_bds - 1);
+ }
+
+ if (ring->free_bds > (MAX_SKB_FRAGS + 1))
+ ring->int_disable(priv, ring);
+
+ if (netif_tx_queue_stopped(txq))
+ netif_tx_wake_queue(txq);
+
+ ring->c_index = c_index;
+}
+
+static void bcmgenet_tx_reclaim(struct net_device *dev,
+ struct bcmgenet_tx_ring *ring)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ring->lock, flags);
+ __bcmgenet_tx_reclaim(dev, ring);
+ spin_unlock_irqrestore(&ring->lock, flags);
+}
+
+static void bcmgenet_tx_reclaim_all(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ int i;
+
+ if (netif_is_multiqueue(dev)) {
+ for (i = 0; i < priv->hw_params->tx_queues; i++)
+ bcmgenet_tx_reclaim(dev, &priv->tx_rings[i]);
+ }
+
+ bcmgenet_tx_reclaim(dev, &priv->tx_rings[DESC_INDEX]);
+}
+
+/* Transmits a single SKB (either head of a fragment or a single SKB)
+ * caller must hold priv->lock
+ */
+static int bcmgenet_xmit_single(struct net_device *dev,
+ struct sk_buff *skb,
+ u16 dma_desc_flags,
+ struct bcmgenet_tx_ring *ring)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ struct device *kdev = &priv->pdev->dev;
+ struct enet_cb *tx_cb_ptr;
+ unsigned int skb_len;
+ dma_addr_t mapping;
+ u32 length_status;
+ int ret;
+
+ tx_cb_ptr = bcmgenet_get_txcb(priv, ring);
+
+ if (unlikely(!tx_cb_ptr))
+ BUG();
+
+ tx_cb_ptr->skb = skb;
+
+ skb_len = skb_headlen(skb) < ETH_ZLEN ? ETH_ZLEN : skb_headlen(skb);
+
+ mapping = dma_map_single(kdev, skb->data, skb_len, DMA_TO_DEVICE);
+ ret = dma_mapping_error(kdev, mapping);
+ if (ret) {
+ netif_err(priv, tx_err, dev, "Tx DMA map failed\n");
+ dev_kfree_skb(skb);
+ return ret;
+ }
+
+ dma_unmap_addr_set(tx_cb_ptr, dma_addr, mapping);
+ dma_unmap_len_set(tx_cb_ptr, dma_len, skb->len);
+ length_status = (skb_len << DMA_BUFLENGTH_SHIFT) | dma_desc_flags |
+ (priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT) |
+ DMA_TX_APPEND_CRC;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ length_status |= DMA_TX_DO_CSUM;
+
+ dmadesc_set(priv, tx_cb_ptr->bd_addr, mapping, length_status);
+
+ /* Decrement total BD count and advance our write pointer */
+ ring->free_bds -= 1;
+ ring->prod_index += 1;
+ ring->prod_index &= DMA_P_INDEX_MASK;
+
+ return 0;
+}
+
+/* Transmit a SKB fragement */
+static int bcmgenet_xmit_frag(struct net_device *dev,
+ skb_frag_t *frag,
+ u16 dma_desc_flags,
+ struct bcmgenet_tx_ring *ring)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ struct device *kdev = &priv->pdev->dev;
+ struct enet_cb *tx_cb_ptr;
+ dma_addr_t mapping;
+ int ret;
+
+ tx_cb_ptr = bcmgenet_get_txcb(priv, ring);
+
+ if (unlikely(!tx_cb_ptr))
+ BUG();
+ tx_cb_ptr->skb = NULL;
+
+ mapping = skb_frag_dma_map(kdev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
+ ret = dma_mapping_error(kdev, mapping);
+ if (ret) {
+ netif_err(priv, tx_err, dev, "%s: Tx DMA map failed\n",
+ __func__);
+ return ret;
+ }
+
+ dma_unmap_addr_set(tx_cb_ptr, dma_addr, mapping);
+ dma_unmap_len_set(tx_cb_ptr, dma_len, frag->size);
+
+ dmadesc_set(priv, tx_cb_ptr->bd_addr, mapping,
+ (frag->size << DMA_BUFLENGTH_SHIFT) | dma_desc_flags |
+ (priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT));
+
+
+ ring->free_bds -= 1;
+ ring->prod_index += 1;
+ ring->prod_index &= DMA_P_INDEX_MASK;
+
+ return 0;
+}
+
+/* Reallocate the SKB to put enough headroom in front of it and insert
+ * the transmit checksum offsets in the descriptors
+ */
+static int bcmgenet_put_tx_csum(struct net_device *dev, struct sk_buff *skb)
+{
+ struct status_64 *status = NULL;
+ struct sk_buff *new_skb;
+ u16 offset;
+ u8 ip_proto;
+ u16 ip_ver;
+ u32 tx_csum_info;
+
+ if (unlikely(skb_headroom(skb) < sizeof(*status))) {
+ /* If 64 byte status block enabled, must make sure skb has
+ * enough headroom for us to insert 64B status block.
+ */
+ new_skb = skb_realloc_headroom(skb, sizeof(*status));
+ dev_kfree_skb(skb);
+ if (!new_skb) {
+ dev->stats.tx_errors++;
+ dev->stats.tx_dropped++;
+ return -ENOMEM;
+ }
+ skb = new_skb;
+ }
+
+ skb_push(skb, sizeof(*status));
+ status = (struct status_64 *)skb->data;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ ip_ver = htons(skb->protocol);
+ switch (ip_ver) {
+ case ETH_P_IP:
+ ip_proto = ip_hdr(skb)->protocol;
+ break;
+ case ETH_P_IPV6:
+ ip_proto = ipv6_hdr(skb)->nexthdr;
+ break;
+ default:
+ return 0;
+ }
+
+ offset = skb_checksum_start_offset(skb) - sizeof(*status);
+ tx_csum_info = (offset << STATUS_TX_CSUM_START_SHIFT) |
+ (offset + skb->csum_offset);
+
+ /* Set the length valid bit for TCP and UDP and just set
+ * the special UDP flag for IPv4, else just set to 0.
+ */
+ if (ip_proto == IPPROTO_TCP || ip_proto == IPPROTO_UDP) {
+ tx_csum_info |= STATUS_TX_CSUM_LV;
+ if (ip_proto == IPPROTO_UDP && ip_ver == ETH_P_IP)
+ tx_csum_info |= STATUS_TX_CSUM_PROTO_UDP;
+ } else
+ tx_csum_info = 0;
+
+ status->tx_csum_info = tx_csum_info;
+ }
+
+ return 0;
+}
+
+static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ struct bcmgenet_tx_ring *ring = NULL;
+ struct netdev_queue *txq;
+ unsigned long flags = 0;
+ int nr_frags, index;
+ u16 dma_desc_flags;
+ int ret;
+ int i;
+
+ index = skb_get_queue_mapping(skb);
+ /* Mapping strategy:
+ * queue_mapping = 0, unclassified, packet xmited through ring16
+ * queue_mapping = 1, goes to ring 0. (highest priority queue
+ * queue_mapping = 2, goes to ring 1.
+ * queue_mapping = 3, goes to ring 2.
+ * queue_mapping = 4, goes to ring 3.
+ */
+ if (index == 0)
+ index = DESC_INDEX;
+ else
+ index -= 1;
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ ring = &priv->tx_rings[index];
+ txq = netdev_get_tx_queue(dev, ring->queue);
+
+ spin_lock_irqsave(&ring->lock, flags);
+ if (ring->free_bds <= nr_frags + 1) {
+ netif_tx_stop_queue(txq);
+ netdev_err(dev, "%s: tx ring %d full when queue %d awake\n",
+ __func__, index, ring->queue);
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ }
+
+ /* set the SKB transmit checksum */
+ if (priv->desc_64b_en) {
+ ret = bcmgenet_put_tx_csum(dev, skb);
+ if (ret) {
+ ret = NETDEV_TX_OK;
+ goto out;
+ }
+ }
+
+ dma_desc_flags = DMA_SOP;
+ if (nr_frags == 0)
+ dma_desc_flags |= DMA_EOP;
+
+ /* Transmit single SKB or head of fragment list */
+ ret = bcmgenet_xmit_single(dev, skb, dma_desc_flags, ring);
+ if (ret) {
+ ret = NETDEV_TX_OK;
+ goto out;
+ }
+
+ /* xmit fragment */
+ for (i = 0; i < nr_frags; i++) {
+ ret = bcmgenet_xmit_frag(dev,
+ &skb_shinfo(skb)->frags[i],
+ (i == nr_frags - 1) ? DMA_EOP : 0, ring);
+ if (ret) {
+ ret = NETDEV_TX_OK;
+ goto out;
+ }
+ }
+
+ skb_tx_timestamp(skb);
+
+ /* we kept a software copy of how much we should advance the TDMA
+ * producer index, now write it down to the hardware
+ */
+ bcmgenet_tdma_ring_writel(priv, ring->index,
+ ring->prod_index, TDMA_PROD_INDEX);
+
+ if (ring->free_bds <= (MAX_SKB_FRAGS + 1)) {
+ netif_tx_stop_queue(txq);
+ ring->int_enable(priv, ring);
+ }
+
+out:
+ spin_unlock_irqrestore(&ring->lock, flags);
+
+ return ret;
+}
+
+
+static int bcmgenet_rx_refill(struct bcmgenet_priv *priv,
+ struct enet_cb *cb)
+{
+ struct device *kdev = &priv->pdev->dev;
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+ int ret;
+
+ skb = netdev_alloc_skb(priv->dev,
+ priv->rx_buf_len + SKB_ALIGNMENT);
+ if (!skb)
+ return -ENOMEM;
+
+ /* a caller did not release this control block */
+ WARN_ON(cb->skb != NULL);
+ cb->skb = skb;
+ mapping = dma_map_single(kdev, skb->data,
+ priv->rx_buf_len, DMA_FROM_DEVICE);
+ ret = dma_mapping_error(kdev, mapping);
+ if (ret) {
+ bcmgenet_free_cb(cb);
+ netif_err(priv, rx_err, priv->dev,
+ "%s DMA map failed\n", __func__);
+ return ret;
+ }
+
+ dma_unmap_addr_set(cb, dma_addr, mapping);
+ /* assign packet, prepare descriptor, and advance pointer */
+
+ dmadesc_set_addr(priv, priv->rx_bd_assign_ptr, mapping);
+
+ /* turn on the newly assigned BD for DMA to use */
+ priv->rx_bd_assign_index++;
+ priv->rx_bd_assign_index &= (priv->num_rx_bds - 1);
+
+ priv->rx_bd_assign_ptr = priv->rx_bds +
+ (priv->rx_bd_assign_index * DMA_DESC_SIZE);
+
+ return 0;
+}
+
+/* bcmgenet_desc_rx - descriptor based rx process.
+ * this could be called from bottom half, or from NAPI polling method.
+ */
+static unsigned int bcmgenet_desc_rx(struct bcmgenet_priv *priv,
+ unsigned int budget)
+{
+ struct net_device *dev = priv->dev;
+ struct enet_cb *cb;
+ struct sk_buff *skb;
+ u32 dma_length_status;
+ unsigned long dma_flag;
+ int len, err;
+ unsigned int rxpktprocessed = 0, rxpkttoprocess;
+ unsigned int p_index;
+ unsigned int chksum_ok = 0;
+
+ p_index = bcmgenet_rdma_ring_readl(priv,
+ DESC_INDEX, RDMA_PROD_INDEX);
+ p_index &= DMA_P_INDEX_MASK;
+
+ if (p_index < priv->rx_c_index)
+ rxpkttoprocess = (DMA_C_INDEX_MASK + 1) -
+ priv->rx_c_index + p_index;
+ else
+ rxpkttoprocess = p_index - priv->rx_c_index;
+
+ netif_dbg(priv, rx_status, dev,
+ "RDMA: rxpkttoprocess=%d\n", rxpkttoprocess);
+
+ while ((rxpktprocessed < rxpkttoprocess) &&
+ (rxpktprocessed < budget)) {
+
+ /* Unmap the packet contents such that we can use the
+ * RSV from the 64 bytes descriptor when enabled and save
+ * a 32-bits register read
+ */
+ cb = &priv->rx_cbs[priv->rx_read_ptr];
+ skb = cb->skb;
+ dma_unmap_single(&dev->dev, dma_unmap_addr(cb, dma_addr),
+ priv->rx_buf_len, DMA_FROM_DEVICE);
+
+ if (!priv->desc_64b_en) {
+ dma_length_status = dmadesc_get_length_status(priv,
+ priv->rx_bds +
+ (priv->rx_read_ptr *
+ DMA_DESC_SIZE));
+ } else {
+ struct status_64 *status;
+ status = (struct status_64 *)skb->data;
+ dma_length_status = status->length_status;
+ }
+
+ /* DMA flags and length are still valid no matter how
+ * we got the Receive Status Vector (64B RSB or register)
+ */
+ dma_flag = dma_length_status & 0xffff;
+ len = dma_length_status >> DMA_BUFLENGTH_SHIFT;
+
+ netif_dbg(priv, rx_status, dev,
+ "%s: p_ind=%d c_ind=%d read_ptr=%d len_stat=0x%08x\n",
+ __func__, p_index, priv->rx_c_index, priv->rx_read_ptr,
+ dma_length_status);
+
+ rxpktprocessed++;
+
+ priv->rx_read_ptr++;
+ priv->rx_read_ptr &= (priv->num_rx_bds - 1);
+
+ /* out of memory, just drop packets at the hardware level */
+ if (unlikely(!skb)) {
+ dev->stats.rx_dropped++;
+ dev->stats.rx_errors++;
+ goto refill;
+ }
+
+ if (unlikely(!(dma_flag & DMA_EOP) || !(dma_flag & DMA_SOP))) {
+ netif_err(priv, rx_status, dev,
+ "Droping fragmented packet!\n");
+ dev->stats.rx_dropped++;
+ dev->stats.rx_errors++;
+ dev_kfree_skb_any(cb->skb);
+ cb->skb = NULL;
+ goto refill;
+ }
+ /* report errors */
+ if (unlikely(dma_flag & (DMA_RX_CRC_ERROR |
+ DMA_RX_OV |
+ DMA_RX_NO |
+ DMA_RX_LG |
+ DMA_RX_RXER))) {
+ netif_err(priv, rx_status, dev, "dma_flag=0x%x\n",
+ (unsigned int)dma_flag);
+ if (dma_flag & DMA_RX_CRC_ERROR)
+ dev->stats.rx_crc_errors++;
+ if (dma_flag & DMA_RX_OV)
+ dev->stats.rx_over_errors++;
+ if (dma_flag & DMA_RX_NO)
+ dev->stats.rx_frame_errors++;
+ if (dma_flag & DMA_RX_LG)
+ dev->stats.rx_length_errors++;
+ dev->stats.rx_dropped++;
+ dev->stats.rx_errors++;
+
+ /* discard the packet and advance consumer index.*/
+ dev_kfree_skb_any(cb->skb);
+ cb->skb = NULL;
+ goto refill;
+ } /* error packet */
+
+ chksum_ok = (dma_flag & priv->dma_rx_chk_bit) &&
+ priv->desc_rxchk_en;
+
+ skb_put(skb, len);
+ if (priv->desc_64b_en) {
+ skb_pull(skb, 64);
+ len -= 64;
+ }
+
+ if (likely(chksum_ok))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ /* remove hardware 2bytes added for IP alignment */
+ skb_pull(skb, 2);
+ len -= 2;
+
+ if (priv->crc_fwd_en) {
+ skb_trim(skb, len - ETH_FCS_LEN);
+ len -= ETH_FCS_LEN;
+ }
+
+ /*Finish setting up the received SKB and send it to the kernel*/
+ skb->protocol = eth_type_trans(skb, priv->dev);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
+ if (dma_flag & DMA_RX_MULT)
+ dev->stats.multicast++;
+
+ /* Notify kernel */
+ napi_gro_receive(&priv->napi, skb);
+ cb->skb = NULL;
+ netif_dbg(priv, rx_status, dev, "pushed up to kernel\n");
+
+ /* refill RX path on the current control block */
+refill:
+ err = bcmgenet_rx_refill(priv, cb);
+ if (err)
+ netif_err(priv, rx_err, dev, "Rx refill failed\n");
+ }
+
+ return rxpktprocessed;
+}
+
+/* Assign skb to RX DMA descriptor. */
+static int bcmgenet_alloc_rx_buffers(struct bcmgenet_priv *priv)
+{
+ struct enet_cb *cb;
+ int ret = 0;
+ int i;
+
+ netif_dbg(priv, hw, priv->dev, "%s:\n", __func__);
+
+ /* loop here for each buffer needing assign */
+ for (i = 0; i < priv->num_rx_bds; i++) {
+ cb = &priv->rx_cbs[priv->rx_bd_assign_index];
+ if (cb->skb)
+ continue;
+
+ /* set the DMA descriptor length once and for all
+ * it will only change if we support dynamically sizing
+ * priv->rx_buf_len, but we do not
+ */
+ dmadesc_set_length_status(priv, priv->rx_bd_assign_ptr,
+ priv->rx_buf_len << DMA_BUFLENGTH_SHIFT);
+
+ ret = bcmgenet_rx_refill(priv, cb);
+ if (ret)
+ break;
+
+ }
+
+ return ret;
+}
+
+static void bcmgenet_free_rx_buffers(struct bcmgenet_priv *priv)
+{
+ struct enet_cb *cb;
+ int i;
+
+ for (i = 0; i < priv->num_rx_bds; i++) {
+ cb = &priv->rx_cbs[i];
+
+ if (dma_unmap_addr(cb, dma_addr)) {
+ dma_unmap_single(&priv->dev->dev,
+ dma_unmap_addr(cb, dma_addr),
+ priv->rx_buf_len, DMA_FROM_DEVICE);
+ dma_unmap_addr_set(cb, dma_addr, 0);
+ }
+
+ if (cb->skb)
+ bcmgenet_free_cb(cb);
+ }
+}
+
+static int reset_umac(struct bcmgenet_priv *priv)
+{
+ struct device *kdev = &priv->pdev->dev;
+ unsigned int timeout = 0;
+ u32 reg;
+
+ /* 7358a0/7552a0: bad default in RBUF_FLUSH_CTRL.umac_sw_rst */
+ bcmgenet_rbuf_ctrl_set(priv, 0);
+ udelay(10);
+
+ /* disable MAC while updating its registers */
+ bcmgenet_umac_writel(priv, 0, UMAC_CMD);
+
+ /* issue soft reset, wait for it to complete */
+ bcmgenet_umac_writel(priv, CMD_SW_RESET, UMAC_CMD);
+ while (timeout++ < 1000) {
+ reg = bcmgenet_umac_readl(priv, UMAC_CMD);
+ if (!(reg & CMD_SW_RESET))
+ return 0;
+
+ udelay(1);
+ }
+
+ if (timeout == 1000) {
+ dev_err(kdev,
+ "timeout waiting for MAC to come out of resetn\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int init_umac(struct bcmgenet_priv *priv)
+{
+ struct device *kdev = &priv->pdev->dev;
+ int ret;
+ u32 reg, cpu_mask_clear;
+
+ dev_dbg(&priv->pdev->dev, "bcmgenet: init_umac\n");
+
+ ret = reset_umac(priv);
+ if (ret)
+ return ret;
+
+ bcmgenet_umac_writel(priv, 0, UMAC_CMD);
+ /* clear tx/rx counter */
+ bcmgenet_umac_writel(priv,
+ MIB_RESET_RX | MIB_RESET_TX | MIB_RESET_RUNT, UMAC_MIB_CTRL);
+ bcmgenet_umac_writel(priv, 0, UMAC_MIB_CTRL);
+
+ bcmgenet_umac_writel(priv, ENET_MAX_MTU_SIZE, UMAC_MAX_FRAME_LEN);
+
+ /* init rx registers, enable ip header optimization */
+ reg = bcmgenet_rbuf_readl(priv, RBUF_CTRL);
+ reg |= RBUF_ALIGN_2B;
+ bcmgenet_rbuf_writel(priv, reg, RBUF_CTRL);
+
+ if (!GENET_IS_V1(priv) && !GENET_IS_V2(priv))
+ bcmgenet_rbuf_writel(priv, 1, RBUF_TBUF_SIZE_CTRL);
+
+ /* Mask all interrupts.*/
+ bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_MASK_SET);
+ bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_CLEAR);
+ bcmgenet_intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
+
+ cpu_mask_clear = UMAC_IRQ_RXDMA_BDONE;
+
+ dev_dbg(kdev, "%s:Enabling RXDMA_BDONE interrupt\n", __func__);
+
+ /* Monitor cable plug/unpluged event for internal PHY */
+ if (phy_is_internal(priv->phydev))
+ cpu_mask_clear |= (UMAC_IRQ_LINK_DOWN | UMAC_IRQ_LINK_UP);
+ else if (priv->ext_phy)
+ cpu_mask_clear |= (UMAC_IRQ_LINK_DOWN | UMAC_IRQ_LINK_UP);
+ else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) {
+ reg = bcmgenet_bp_mc_get(priv);
+ reg |= BIT(priv->hw_params->bp_in_en_shift);
+
+ /* bp_mask: back pressure mask */
+ if (netif_is_multiqueue(priv->dev))
+ reg |= priv->hw_params->bp_in_mask;
+ else
+ reg &= ~priv->hw_params->bp_in_mask;
+ bcmgenet_bp_mc_set(priv, reg);
+ }
+
+ /* Enable MDIO interrupts on GENET v3+ */
+ if (priv->hw_params->flags & GENET_HAS_MDIO_INTR)
+ cpu_mask_clear |= UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR;
+
+ bcmgenet_intrl2_0_writel(priv, cpu_mask_clear,
+ INTRL2_CPU_MASK_CLEAR);
+
+ /* Enable rx/tx engine.*/
+ dev_dbg(kdev, "done init umac\n");
+
+ return 0;
+}
+
+/* Initialize all house-keeping variables for a TX ring, along
+ * with corresponding hardware registers
+ */
+static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv,
+ unsigned int index, unsigned int size,
+ unsigned int write_ptr, unsigned int end_ptr)
+{
+ struct bcmgenet_tx_ring *ring = &priv->tx_rings[index];
+ u32 words_per_bd = WORDS_PER_BD(priv);
+ u32 flow_period_val = 0;
+ unsigned int first_bd;
+
+ spin_lock_init(&ring->lock);
+ ring->index = index;
+ if (index == DESC_INDEX) {
+ ring->queue = 0;
+ ring->int_enable = bcmgenet_tx_ring16_int_enable;
+ ring->int_disable = bcmgenet_tx_ring16_int_disable;
+ } else {
+ ring->queue = index + 1;
+ ring->int_enable = bcmgenet_tx_ring_int_enable;
+ ring->int_disable = bcmgenet_tx_ring_int_disable;
+ }
+ ring->cbs = priv->tx_cbs + write_ptr;
+ ring->size = size;
+ ring->c_index = 0;
+ ring->free_bds = size;
+ ring->write_ptr = write_ptr;
+ ring->cb_ptr = write_ptr;
+ ring->end_ptr = end_ptr - 1;
+ ring->prod_index = 0;
+
+ /* Set flow period for ring != 16 */
+ if (index != DESC_INDEX)
+ flow_period_val = ENET_MAX_MTU_SIZE << 16;
+
+ bcmgenet_tdma_ring_writel(priv, index, 0, TDMA_PROD_INDEX);
+ bcmgenet_tdma_ring_writel(priv, index, 0, TDMA_CONS_INDEX);
+ bcmgenet_tdma_ring_writel(priv, index, 1, DMA_MBUF_DONE_THRESH);
+ /* Disable rate control for now */
+ bcmgenet_tdma_ring_writel(priv, index, flow_period_val,
+ TDMA_FLOW_PERIOD);
+ /* Unclassified traffic goes to ring 16 */
+ bcmgenet_tdma_ring_writel(priv, index,
+ ((size << DMA_RING_SIZE_SHIFT) | RX_BUF_LENGTH),
+ DMA_RING_BUF_SIZE);
+
+ first_bd = write_ptr;
+
+ /* Set start and end address, read and write pointers */
+ bcmgenet_tdma_ring_writel(priv, index, first_bd * words_per_bd,
+ DMA_START_ADDR);
+ bcmgenet_tdma_ring_writel(priv, index, first_bd * words_per_bd,
+ TDMA_READ_PTR);
+ bcmgenet_tdma_ring_writel(priv, index, first_bd,
+ TDMA_WRITE_PTR);
+ bcmgenet_tdma_ring_writel(priv, index, end_ptr * words_per_bd - 1,
+ DMA_END_ADDR);
+}
+
+/* Initialize a RDMA ring */
+static int bcmgenet_init_rx_ring(struct bcmgenet_priv *priv,
+ unsigned int index, unsigned int size)
+{
+ u32 words_per_bd = WORDS_PER_BD(priv);
+ int ret;
+
+ priv->num_rx_bds = TOTAL_DESC;
+ priv->rx_bds = priv->base + priv->hw_params->rdma_offset;
+ priv->rx_bd_assign_ptr = priv->rx_bds;
+ priv->rx_bd_assign_index = 0;
+ priv->rx_c_index = 0;
+ priv->rx_read_ptr = 0;
+ priv->rx_cbs = kzalloc(priv->num_rx_bds * sizeof(struct enet_cb),
+ GFP_KERNEL);
+ if (!priv->rx_cbs)
+ return -ENOMEM;
+
+ ret = bcmgenet_alloc_rx_buffers(priv);
+ if (ret) {
+ kfree(priv->rx_cbs);
+ return ret;
+ }
+
+ bcmgenet_rdma_ring_writel(priv, index, 0, RDMA_WRITE_PTR);
+ bcmgenet_rdma_ring_writel(priv, index, 0, RDMA_PROD_INDEX);
+ bcmgenet_rdma_ring_writel(priv, index, 0, RDMA_CONS_INDEX);
+ bcmgenet_rdma_ring_writel(priv, index,
+ ((size << DMA_RING_SIZE_SHIFT) | RX_BUF_LENGTH),
+ DMA_RING_BUF_SIZE);
+ bcmgenet_rdma_ring_writel(priv, index, 0, DMA_START_ADDR);
+ bcmgenet_rdma_ring_writel(priv, index,
+ words_per_bd * size - 1, DMA_END_ADDR);
+ bcmgenet_rdma_ring_writel(priv, index,
+ (DMA_FC_THRESH_LO << DMA_XOFF_THRESHOLD_SHIFT) |
+ DMA_FC_THRESH_HI, RDMA_XON_XOFF_THRESH);
+ bcmgenet_rdma_ring_writel(priv, index, 0, RDMA_READ_PTR);
+
+ return ret;
+}
+
+/* init multi xmit queues, only available for GENET2+
+ * the queue is partitioned as follows:
+ *
+ * queue 0 - 3 is priority based, each one has 32 descriptors,
+ * with queue 0 being the highest priority queue.
+ *
+ * queue 16 is the default tx queue with GENET_DEFAULT_BD_CNT
+ * descriptors: 256 - (number of tx queues * bds per queues) = 128
+ * descriptors.
+ *
+ * The transmit control block pool is then partitioned as following:
+ * - tx_cbs[0...127] are for queue 16
+ * - tx_ring_cbs[0] points to tx_cbs[128..159]
+ * - tx_ring_cbs[1] points to tx_cbs[160..191]
+ * - tx_ring_cbs[2] points to tx_cbs[192..223]
+ * - tx_ring_cbs[3] points to tx_cbs[224..255]
+ */
+static void bcmgenet_init_multiq(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ unsigned int i, dma_enable;
+ u32 reg, dma_ctrl, ring_cfg = 0, dma_priority = 0;
+
+ if (!netif_is_multiqueue(dev)) {
+ netdev_warn(dev, "called with non multi queue aware HW\n");
+ return;
+ }
+
+ dma_ctrl = bcmgenet_tdma_readl(priv, DMA_CTRL);
+ dma_enable = dma_ctrl & DMA_EN;
+ dma_ctrl &= ~DMA_EN;
+ bcmgenet_tdma_writel(priv, dma_ctrl, DMA_CTRL);
+
+ /* Enable strict priority arbiter mode */
+ bcmgenet_tdma_writel(priv, DMA_ARBITER_SP, DMA_ARB_CTRL);
+
+ for (i = 0; i < priv->hw_params->tx_queues; i++) {
+ /* first 64 tx_cbs are reserved for default tx queue
+ * (ring 16)
+ */
+ bcmgenet_init_tx_ring(priv, i, priv->hw_params->bds_cnt,
+ i * priv->hw_params->bds_cnt,
+ (i + 1) * priv->hw_params->bds_cnt);
+
+ /* Configure ring as decriptor ring and setup priority */
+ ring_cfg |= 1 << i;
+ dma_priority |= ((GENET_Q0_PRIORITY + i) <<
+ (GENET_MAX_MQ_CNT + 1) * i);
+ dma_ctrl |= 1 << (i + DMA_RING_BUF_EN_SHIFT);
+ }
+
+ /* Enable rings */
+ reg = bcmgenet_tdma_readl(priv, DMA_RING_CFG);
+ reg |= ring_cfg;
+ bcmgenet_tdma_writel(priv, reg, DMA_RING_CFG);
+
+ /* Use configured rings priority and set ring #16 priority */
+ reg = bcmgenet_tdma_readl(priv, DMA_RING_PRIORITY);
+ reg |= ((GENET_Q0_PRIORITY + priv->hw_params->tx_queues) << 20);
+ reg |= dma_priority;
+ bcmgenet_tdma_writel(priv, reg, DMA_PRIORITY);
+
+ /* Configure ring as descriptor ring and re-enable DMA if enabled */
+ reg = bcmgenet_tdma_readl(priv, DMA_CTRL);
+ reg |= dma_ctrl;
+ if (dma_enable)
+ reg |= DMA_EN;
+ bcmgenet_tdma_writel(priv, reg, DMA_CTRL);
+}
+
+static void bcmgenet_fini_dma(struct bcmgenet_priv *priv)
+{
+ int i;
+
+ /* disable DMA */
+ bcmgenet_rdma_writel(priv, 0, DMA_CTRL);
+ bcmgenet_tdma_writel(priv, 0, DMA_CTRL);
+
+ for (i = 0; i < priv->num_tx_bds; i++) {
+ if (priv->tx_cbs[i].skb != NULL) {
+ dev_kfree_skb(priv->tx_cbs[i].skb);
+ priv->tx_cbs[i].skb = NULL;
+ }
+ }
+
+ bcmgenet_free_rx_buffers(priv);
+ kfree(priv->rx_cbs);
+ kfree(priv->tx_cbs);
+}
+
+/* init_edma: Initialize DMA control register */
+static int bcmgenet_init_dma(struct bcmgenet_priv *priv)
+{
+ int ret;
+
+ netif_dbg(priv, hw, priv->dev, "bcmgenet: init_edma\n");
+
+ /* by default, enable ring 16 (descriptor based) */
+ ret = bcmgenet_init_rx_ring(priv, DESC_INDEX, TOTAL_DESC);
+ if (ret) {
+ netdev_err(priv->dev, "failed to initialize RX ring\n");
+ return ret;
+ }
+
+ /* init rDma */
+ bcmgenet_rdma_writel(priv, DMA_MAX_BURST_LENGTH, DMA_SCB_BURST_SIZE);
+
+ /* Init tDma */
+ bcmgenet_tdma_writel(priv, DMA_MAX_BURST_LENGTH, DMA_SCB_BURST_SIZE);
+
+ /* Initialize commont TX ring structures */
+ priv->tx_bds = priv->base + priv->hw_params->tdma_offset;
+ priv->num_tx_bds = TOTAL_DESC;
+ priv->tx_cbs = kzalloc(priv->num_tx_bds * sizeof(struct enet_cb),
+ GFP_KERNEL);
+ if (!priv->tx_cbs) {
+ bcmgenet_fini_dma(priv);
+ return -ENOMEM;
+ }
+
+ /* initialize multi xmit queue */
+ bcmgenet_init_multiq(priv->dev);
+
+ /* initialize special ring 16 */
+ bcmgenet_init_tx_ring(priv, DESC_INDEX, GENET_DEFAULT_BD_CNT,
+ priv->hw_params->tx_queues * priv->hw_params->bds_cnt,
+ TOTAL_DESC);
+
+ return 0;
+}
+
+/* NAPI polling method*/
+static int bcmgenet_poll(struct napi_struct *napi, int budget)
+{
+ struct bcmgenet_priv *priv = container_of(napi,
+ struct bcmgenet_priv, napi);
+ unsigned int work_done;
+
+ /* tx reclaim */
+ bcmgenet_tx_reclaim(priv->dev, &priv->tx_rings[DESC_INDEX]);
+
+ work_done = bcmgenet_desc_rx(priv, budget);
+
+ /* Advancing our consumer index*/
+ priv->rx_c_index += work_done;
+ priv->rx_c_index &= DMA_C_INDEX_MASK;
+ bcmgenet_rdma_ring_writel(priv, DESC_INDEX,
+ priv->rx_c_index, RDMA_CONS_INDEX);
+ if (work_done < budget) {
+ napi_complete(napi);
+ bcmgenet_intrl2_0_writel(priv,
+ UMAC_IRQ_RXDMA_BDONE, INTRL2_CPU_MASK_CLEAR);
+ }
+
+ return work_done;
+}
+
+/* Interrupt bottom half */
+static void bcmgenet_irq_task(struct work_struct *work)
+{
+ struct bcmgenet_priv *priv = container_of(
+ work, struct bcmgenet_priv, bcmgenet_irq_work);
+
+ netif_dbg(priv, intr, priv->dev, "%s\n", __func__);
+
+ /* Link UP/DOWN event */
+ if ((priv->hw_params->flags & GENET_HAS_MDIO_INTR) &&
+ (priv->irq0_stat & (UMAC_IRQ_LINK_UP|UMAC_IRQ_LINK_DOWN))) {
+ phy_mac_interrupt(priv->phydev,
+ priv->irq0_stat & UMAC_IRQ_LINK_UP);
+ priv->irq0_stat &= ~(UMAC_IRQ_LINK_UP|UMAC_IRQ_LINK_DOWN);
+ }
+}
+
+/* bcmgenet_isr1: interrupt handler for ring buffer. */
+static irqreturn_t bcmgenet_isr1(int irq, void *dev_id)
+{
+ struct bcmgenet_priv *priv = dev_id;
+ unsigned int index;
+
+ /* Save irq status for bottom-half processing. */
+ priv->irq1_stat =
+ bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_STAT) &
+ ~priv->int1_mask;
+ /* clear inerrupts*/
+ bcmgenet_intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR);
+
+ netif_dbg(priv, intr, priv->dev,
+ "%s: IRQ=0x%x\n", __func__, priv->irq1_stat);
+ /* Check the MBDONE interrupts.
+ * packet is done, reclaim descriptors
+ */
+ if (priv->irq1_stat & 0x0000ffff) {
+ index = 0;
+ for (index = 0; index < 16; index++) {
+ if (priv->irq1_stat & (1 << index))
+ bcmgenet_tx_reclaim(priv->dev,
+ &priv->tx_rings[index]);
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/* bcmgenet_isr0: Handle various interrupts. */
+static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
+{
+ struct bcmgenet_priv *priv = dev_id;
+
+ /* Save irq status for bottom-half processing. */
+ priv->irq0_stat =
+ bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT) &
+ ~bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS);
+ /* clear inerrupts*/
+ bcmgenet_intrl2_0_writel(priv, priv->irq0_stat, INTRL2_CPU_CLEAR);
+
+ netif_dbg(priv, intr, priv->dev,
+ "IRQ=0x%x\n", priv->irq0_stat);
+
+ if (priv->irq0_stat & (UMAC_IRQ_RXDMA_BDONE | UMAC_IRQ_RXDMA_PDONE)) {
+ /* We use NAPI(software interrupt throttling, if
+ * Rx Descriptor throttling is not used.
+ * Disable interrupt, will be enabled in the poll method.
+ */
+ if (likely(napi_schedule_prep(&priv->napi))) {
+ bcmgenet_intrl2_0_writel(priv,
+ UMAC_IRQ_RXDMA_BDONE, INTRL2_CPU_MASK_SET);
+ __napi_schedule(&priv->napi);
+ }
+ }
+ if (priv->irq0_stat &
+ (UMAC_IRQ_TXDMA_BDONE | UMAC_IRQ_TXDMA_PDONE)) {
+ /* Tx reclaim */
+ bcmgenet_tx_reclaim(priv->dev, &priv->tx_rings[DESC_INDEX]);
+ }
+ if (priv->irq0_stat & (UMAC_IRQ_PHY_DET_R |
+ UMAC_IRQ_PHY_DET_F |
+ UMAC_IRQ_LINK_UP |
+ UMAC_IRQ_LINK_DOWN |
+ UMAC_IRQ_HFB_SM |
+ UMAC_IRQ_HFB_MM |
+ UMAC_IRQ_MPD_R)) {
+ /* all other interested interrupts handled in bottom half */
+ schedule_work(&priv->bcmgenet_irq_work);
+ }
+
+ if ((priv->hw_params->flags & GENET_HAS_MDIO_INTR) &&
+ priv->irq0_stat & (UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR)) {
+ priv->irq0_stat &= ~(UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR);
+ wake_up(&priv->wq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void bcmgenet_umac_reset(struct bcmgenet_priv *priv)
+{
+ u32 reg;
+
+ reg = bcmgenet_rbuf_ctrl_get(priv);
+ reg |= BIT(1);
+ bcmgenet_rbuf_ctrl_set(priv, reg);
+ udelay(10);
+
+ reg &= ~BIT(1);
+ bcmgenet_rbuf_ctrl_set(priv, reg);
+ udelay(10);
+}
+
+static void bcmgenet_set_hw_addr(struct bcmgenet_priv *priv,
+ unsigned char *addr)
+{
+ bcmgenet_umac_writel(priv, (addr[0] << 24) | (addr[1] << 16) |
+ (addr[2] << 8) | addr[3], UMAC_MAC0);
+ bcmgenet_umac_writel(priv, (addr[4] << 8) | addr[5], UMAC_MAC1);
+}
+
+static int bcmgenet_wol_resume(struct bcmgenet_priv *priv)
+{
+ int ret;
+
+ /* From WOL-enabled suspend, switch to regular clock */
+ clk_disable(priv->clk_wol);
+ /* init umac registers to synchronize s/w with h/w */
+ ret = init_umac(priv);
+ if (ret)
+ return ret;
+
+ phy_init_hw(priv->phydev);
+ /* Speed settings must be restored */
+ bcmgenet_mii_config(priv->dev);
+
+ return 0;
+}
+
+/* Returns a reusable dma control register value */
+static u32 bcmgenet_dma_disable(struct bcmgenet_priv *priv)
+{
+ u32 reg;
+ u32 dma_ctrl;
+
+ /* disable DMA */
+ dma_ctrl = 1 << (DESC_INDEX + DMA_RING_BUF_EN_SHIFT) | DMA_EN;
+ reg = bcmgenet_tdma_readl(priv, DMA_CTRL);
+ reg &= ~dma_ctrl;
+ bcmgenet_tdma_writel(priv, reg, DMA_CTRL);
+
+ reg = bcmgenet_rdma_readl(priv, DMA_CTRL);
+ reg &= ~dma_ctrl;
+ bcmgenet_rdma_writel(priv, reg, DMA_CTRL);
+
+ bcmgenet_umac_writel(priv, 1, UMAC_TX_FLUSH);
+ udelay(10);
+ bcmgenet_umac_writel(priv, 0, UMAC_TX_FLUSH);
+
+ return dma_ctrl;
+}
+
+static void bcmgenet_enable_dma(struct bcmgenet_priv *priv, u32 dma_ctrl)
+{
+ u32 reg;
+
+ reg = bcmgenet_rdma_readl(priv, DMA_CTRL);
+ reg |= dma_ctrl;
+ bcmgenet_rdma_writel(priv, reg, DMA_CTRL);
+
+ reg = bcmgenet_tdma_readl(priv, DMA_CTRL);
+ reg |= dma_ctrl;
+ bcmgenet_tdma_writel(priv, reg, DMA_CTRL);
+}
+
+static int bcmgenet_open(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ unsigned long dma_ctrl;
+ u32 reg;
+ int ret;
+
+ netif_dbg(priv, ifup, dev, "bcmgenet_open\n");
+
+ /* Turn on the clock */
+ if (!IS_ERR(priv->clk))
+ clk_prepare_enable(priv->clk);
+
+ /* take MAC out of reset */
+ bcmgenet_umac_reset(priv);
+
+ ret = init_umac(priv);
+ if (ret)
+ goto err_clk_disable;
+
+ /* disable ethernet MAC while updating its registers */
+ reg = bcmgenet_umac_readl(priv, UMAC_CMD);
+ reg &= ~(CMD_TX_EN | CMD_RX_EN);
+ bcmgenet_umac_writel(priv, reg, UMAC_CMD);
+
+ bcmgenet_set_hw_addr(priv, dev->dev_addr);
+
+ if (priv->wol_enabled) {
+ ret = bcmgenet_wol_resume(priv);
+ if (ret)
+ return ret;
+ }
+
+ if (phy_is_internal(priv->phydev)) {
+ reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
+ reg |= EXT_ENERGY_DET_MASK;
+ bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
+ }
+
+ /* Disable RX/TX DMA and flush TX queues */
+ dma_ctrl = bcmgenet_dma_disable(priv);
+
+ /* Reinitialize TDMA and RDMA and SW housekeeping */
+ ret = bcmgenet_init_dma(priv);
+ if (ret) {
+ netdev_err(dev, "failed to initialize DMA\n");
+ goto err_fini_dma;
+ }
+
+ /* Always enable ring 16 - descriptor ring */
+ bcmgenet_enable_dma(priv, dma_ctrl);
+
+ ret = request_irq(priv->irq0, bcmgenet_isr0, IRQF_SHARED,
+ dev->name, priv);
+ if (ret < 0) {
+ netdev_err(dev, "can't request IRQ %d\n", priv->irq0);
+ goto err_fini_dma;
+ }
+
+ ret = request_irq(priv->irq1, bcmgenet_isr1, IRQF_SHARED,
+ dev->name, priv);
+ if (ret < 0) {
+ netdev_err(dev, "can't request IRQ %d\n", priv->irq1);
+ goto err_irq0;
+ }
+
+ /* Start the network engine */
+ napi_enable(&priv->napi);
+
+ reg = bcmgenet_umac_readl(priv, UMAC_CMD);
+ reg |= (CMD_TX_EN | CMD_RX_EN);
+ bcmgenet_umac_writel(priv, reg, UMAC_CMD);
+
+ /* Make sure we reflect the value of CRC_CMD_FWD */
+ priv->crc_fwd_en = !!(reg & CMD_CRC_FWD);
+
+ device_set_wakeup_capable(&dev->dev, 1);
+
+ if (phy_is_internal(priv->phydev))
+ bcmgenet_power_up(priv, GENET_POWER_PASSIVE);
+
+ netif_tx_start_all_queues(dev);
+
+ phy_start(priv->phydev);
+
+ return 0;
+
+err_irq0:
+ free_irq(priv->irq0, dev);
+err_fini_dma:
+ bcmgenet_fini_dma(priv);
+err_clk_disable:
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+ return ret;
+}
+
+static int bcmgenet_dma_teardown(struct bcmgenet_priv *priv)
+{
+ int ret = 0;
+ int timeout = 0;
+ u32 reg;
+
+ /* Disable TDMA to stop add more frames in TX DMA */
+ reg = bcmgenet_tdma_readl(priv, DMA_CTRL);
+ reg &= ~DMA_EN;
+ bcmgenet_tdma_writel(priv, reg, DMA_CTRL);
+
+ /* Check TDMA status register to confirm TDMA is disabled */
+ while (timeout++ < DMA_TIMEOUT_VAL) {
+ reg = bcmgenet_tdma_readl(priv, DMA_STATUS);
+ if (reg & DMA_DISABLED)
+ break;
+
+ udelay(1);
+ }
+
+ if (timeout == DMA_TIMEOUT_VAL) {
+ netdev_warn(priv->dev,
+ "Timed out while disabling TX DMA\n");
+ ret = -ETIMEDOUT;
+ }
+
+ /* Wait 10ms for packet drain in both tx and rx dma */
+ usleep_range(10000, 20000);
+
+ /* Disable RDMA */
+ reg = bcmgenet_rdma_readl(priv, DMA_CTRL);
+ reg &= ~DMA_EN;
+ bcmgenet_rdma_writel(priv, reg, DMA_CTRL);
+
+ timeout = 0;
+ /* Check RDMA status register to confirm RDMA is disabled */
+ while (timeout++ < DMA_TIMEOUT_VAL) {
+ reg = bcmgenet_rdma_readl(priv, DMA_STATUS);
+ if (reg & DMA_DISABLED)
+ break;
+
+ udelay(1);
+ }
+
+ if (timeout == DMA_TIMEOUT_VAL) {
+ netdev_warn(priv->dev,
+ "Timed out while disabling RX DMA\n");
+ ret = -ETIMEDOUT;
+ }
+
+ return ret;
+}
+
+static int bcmgenet_close(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ int ret;
+ u32 reg;
+
+ netif_dbg(priv, ifdown, dev, "bcmgenet_close\n");
+
+ phy_stop(priv->phydev);
+
+ /* Disable MAC receive */
+ reg = bcmgenet_umac_readl(priv, UMAC_CMD);
+ reg &= ~CMD_RX_EN;
+ bcmgenet_umac_writel(priv, reg, UMAC_CMD);
+
+ netif_tx_stop_all_queues(dev);
+
+ ret = bcmgenet_dma_teardown(priv);
+ if (ret)
+ return ret;
+
+ /* Disable MAC transmit. TX DMA disabled have to done before this */
+ reg = bcmgenet_umac_readl(priv, UMAC_CMD);
+ reg &= ~CMD_TX_EN;
+ bcmgenet_umac_writel(priv, reg, UMAC_CMD);
+
+ napi_disable(&priv->napi);
+
+ /* tx reclaim */
+ bcmgenet_tx_reclaim_all(dev);
+ bcmgenet_fini_dma(priv);
+
+ free_irq(priv->irq0, priv);
+ free_irq(priv->irq1, priv);
+
+ /* Wait for pending work items to complete - we are stopping
+ * the clock now. Since interrupts are disabled, no new work
+ * will be scheduled.
+ */
+ cancel_work_sync(&priv->bcmgenet_irq_work);
+
+ if (phy_is_internal(priv->phydev))
+ bcmgenet_power_down(priv, GENET_POWER_PASSIVE);
+
+ if (priv->wol_enabled)
+ clk_enable(priv->clk_wol);
+
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
+static void bcmgenet_timeout(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+
+ netif_dbg(priv, tx_err, dev, "bcmgenet_timeout\n");
+
+ dev->trans_start = jiffies;
+
+ dev->stats.tx_errors++;
+
+ netif_tx_wake_all_queues(dev);
+}
+
+#define MAX_MC_COUNT 16
+
+static inline void bcmgenet_set_mdf_addr(struct bcmgenet_priv *priv,
+ unsigned char *addr,
+ int *i,
+ int *mc)
+{
+ u32 reg;
+
+ bcmgenet_umac_writel(priv,
+ addr[0] << 8 | addr[1], UMAC_MDF_ADDR + (*i * 4));
+ bcmgenet_umac_writel(priv,
+ addr[2] << 24 | addr[3] << 16 |
+ addr[4] << 8 | addr[5],
+ UMAC_MDF_ADDR + ((*i + 1) * 4));
+ reg = bcmgenet_umac_readl(priv, UMAC_MDF_CTRL);
+ reg |= (1 << (MAX_MC_COUNT - *mc));
+ bcmgenet_umac_writel(priv, reg, UMAC_MDF_CTRL);
+ *i += 2;
+ (*mc)++;
+}
+
+static void bcmgenet_set_rx_mode(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ struct netdev_hw_addr *ha;
+ int i, mc;
+ u32 reg;
+
+ netif_dbg(priv, hw, dev, "%s: %08X\n", __func__, dev->flags);
+
+ /* Promiscous mode */
+ reg = bcmgenet_umac_readl(priv, UMAC_CMD);
+ if (dev->flags & IFF_PROMISC) {
+ reg |= CMD_PROMISC;
+ bcmgenet_umac_writel(priv, reg, UMAC_CMD);
+ bcmgenet_umac_writel(priv, 0, UMAC_MDF_CTRL);
+ return;
+ } else {
+ reg &= ~CMD_PROMISC;
+ bcmgenet_umac_writel(priv, reg, UMAC_CMD);
+ }
+
+ /* UniMac doesn't support ALLMULTI */
+ if (dev->flags & IFF_ALLMULTI) {
+ netdev_warn(dev, "ALLMULTI is not supported\n");
+ return;
+ }
+
+ /* update MDF filter */
+ i = 0;
+ mc = 0;
+ /* Broadcast */
+ bcmgenet_set_mdf_addr(priv, dev->broadcast, &i, &mc);
+ /* my own address.*/
+ bcmgenet_set_mdf_addr(priv, dev->dev_addr, &i, &mc);
+ /* Unicast list*/
+ if (netdev_uc_count(dev) > (MAX_MC_COUNT - mc))
+ return;
+
+ if (!netdev_uc_empty(dev))
+ netdev_for_each_uc_addr(ha, dev)
+ bcmgenet_set_mdf_addr(priv, ha->addr, &i, &mc);
+ /* Multicast */
+ if (netdev_mc_empty(dev) || netdev_mc_count(dev) >= (MAX_MC_COUNT - mc))
+ return;
+
+ netdev_for_each_mc_addr(ha, dev)
+ bcmgenet_set_mdf_addr(priv, ha->addr, &i, &mc);
+}
+
+/* Set the hardware MAC address. */
+static int bcmgenet_set_mac_addr(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+
+ /* Setting the MAC address at the hardware level is not possible
+ * without disabling the UniMAC RX/TX enable bits.
+ */
+ if (netif_running(dev))
+ return -EBUSY;
+
+ ether_addr_copy(dev->dev_addr, addr->sa_data);
+
+ return 0;
+}
+
+static const struct net_device_ops bcmgenet_netdev_ops = {
+ .ndo_open = bcmgenet_open,
+ .ndo_stop = bcmgenet_close,
+ .ndo_start_xmit = bcmgenet_xmit,
+ .ndo_tx_timeout = bcmgenet_timeout,
+ .ndo_set_rx_mode = bcmgenet_set_rx_mode,
+ .ndo_set_mac_address = bcmgenet_set_mac_addr,
+ .ndo_do_ioctl = bcmgenet_ioctl,
+ .ndo_set_features = bcmgenet_set_features,
+};
+
+/* Array of GENET hardware parameters/characteristics */
+static struct bcmgenet_hw_params bcmgenet_hw_params[] = {
+ [GENET_V1] = {
+ .tx_queues = 0,
+ .rx_queues = 0,
+ .bds_cnt = 0,
+ .bp_in_en_shift = 16,
+ .bp_in_mask = 0xffff,
+ .hfb_filter_cnt = 16,
+ .qtag_mask = 0x1F,
+ .hfb_offset = 0x1000,
+ .rdma_offset = 0x2000,
+ .tdma_offset = 0x3000,
+ .words_per_bd = 2,
+ },
+ [GENET_V2] = {
+ .tx_queues = 4,
+ .rx_queues = 4,
+ .bds_cnt = 32,
+ .bp_in_en_shift = 16,
+ .bp_in_mask = 0xffff,
+ .hfb_filter_cnt = 16,
+ .qtag_mask = 0x1F,
+ .tbuf_offset = 0x0600,
+ .hfb_offset = 0x1000,
+ .hfb_reg_offset = 0x2000,
+ .rdma_offset = 0x3000,
+ .tdma_offset = 0x4000,
+ .words_per_bd = 2,
+ .flags = GENET_HAS_EXT,
+ },
+ [GENET_V3] = {
+ .tx_queues = 4,
+ .rx_queues = 4,
+ .bds_cnt = 32,
+ .bp_in_en_shift = 17,
+ .bp_in_mask = 0x1ffff,
+ .hfb_filter_cnt = 48,
+ .qtag_mask = 0x3F,
+ .tbuf_offset = 0x0600,
+ .hfb_offset = 0x8000,
+ .hfb_reg_offset = 0xfc00,
+ .rdma_offset = 0x10000,
+ .tdma_offset = 0x11000,
+ .words_per_bd = 2,
+ .flags = GENET_HAS_EXT | GENET_HAS_MDIO_INTR,
+ },
+ [GENET_V4] = {
+ .tx_queues = 4,
+ .rx_queues = 4,
+ .bds_cnt = 32,
+ .bp_in_en_shift = 17,
+ .bp_in_mask = 0x1ffff,
+ .hfb_filter_cnt = 48,
+ .qtag_mask = 0x3F,
+ .tbuf_offset = 0x0600,
+ .hfb_offset = 0x8000,
+ .hfb_reg_offset = 0xfc00,
+ .rdma_offset = 0x2000,
+ .tdma_offset = 0x4000,
+ .words_per_bd = 3,
+ .flags = GENET_HAS_40BITS | GENET_HAS_EXT | GENET_HAS_MDIO_INTR,
+ },
+};
+
+/* Infer hardware parameters from the detected GENET version */
+static void bcmgenet_set_hw_params(struct bcmgenet_priv *priv)
+{
+ struct bcmgenet_hw_params *params;
+ u32 reg;
+ u8 major;
+
+ if (GENET_IS_V4(priv)) {
+ bcmgenet_dma_regs = bcmgenet_dma_regs_v3plus;
+ genet_dma_ring_regs = genet_dma_ring_regs_v4;
+ priv->dma_rx_chk_bit = DMA_RX_CHK_V3PLUS;
+ priv->version = GENET_V4;
+ } else if (GENET_IS_V3(priv)) {
+ bcmgenet_dma_regs = bcmgenet_dma_regs_v3plus;
+ genet_dma_ring_regs = genet_dma_ring_regs_v123;
+ priv->dma_rx_chk_bit = DMA_RX_CHK_V3PLUS;
+ priv->version = GENET_V3;
+ } else if (GENET_IS_V2(priv)) {
+ bcmgenet_dma_regs = bcmgenet_dma_regs_v2;
+ genet_dma_ring_regs = genet_dma_ring_regs_v123;
+ priv->dma_rx_chk_bit = DMA_RX_CHK_V12;
+ priv->version = GENET_V2;
+ } else if (GENET_IS_V1(priv)) {
+ bcmgenet_dma_regs = bcmgenet_dma_regs_v1;
+ genet_dma_ring_regs = genet_dma_ring_regs_v123;
+ priv->dma_rx_chk_bit = DMA_RX_CHK_V12;
+ priv->version = GENET_V1;
+ }
+
+ /* enum genet_version starts at 1 */
+ priv->hw_params = &bcmgenet_hw_params[priv->version];
+ params = priv->hw_params;
+
+ /* Read GENET HW version */
+ reg = bcmgenet_sys_readl(priv, SYS_REV_CTRL);
+ major = (reg >> 24 & 0x0f);
+ if (major == 5)
+ major = 4;
+ else if (major == 0)
+ major = 1;
+ if (major != priv->version) {
+ dev_err(&priv->pdev->dev,
+ "GENET version mismatch, got: %d, configured for: %d\n",
+ major, priv->version);
+ }
+
+ /* Print the GENET core version */
+ dev_info(&priv->pdev->dev, "GENET " GENET_VER_FMT,
+ major, (reg >> 16) & 0x0f, reg & 0xffff);
+
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+ if (!(params->flags & GENET_HAS_40BITS))
+ pr_warn("GENET does not support 40-bits PA\n");
+#endif
+
+ pr_debug("Configuration for version: %d\n"
+ "TXq: %1d, RXq: %1d, BDs: %1d\n"
+ "BP << en: %2d, BP msk: 0x%05x\n"
+ "HFB count: %2d, QTAQ msk: 0x%05x\n"
+ "TBUF: 0x%04x, HFB: 0x%04x, HFBreg: 0x%04x\n"
+ "RDMA: 0x%05x, TDMA: 0x%05x\n"
+ "Words/BD: %d\n",
+ priv->version,
+ params->tx_queues, params->rx_queues, params->bds_cnt,
+ params->bp_in_en_shift, params->bp_in_mask,
+ params->hfb_filter_cnt, params->qtag_mask,
+ params->tbuf_offset, params->hfb_offset,
+ params->hfb_reg_offset,
+ params->rdma_offset, params->tdma_offset,
+ params->words_per_bd);
+}
+
+static const struct of_device_id bcmgenet_match[] = {
+ { .compatible = "brcm,genet-v1", .data = (void *)GENET_V1 },
+ { .compatible = "brcm,genet-v2", .data = (void *)GENET_V2 },
+ { .compatible = "brcm,genet-v3", .data = (void *)GENET_V3 },
+ { .compatible = "brcm,genet-v4", .data = (void *)GENET_V4 },
+ { },
+};
+
+static int bcmgenet_probe(struct platform_device *pdev)
+{
+ struct device_node *dn = pdev->dev.of_node;
+ const struct of_device_id *of_id;
+ struct bcmgenet_priv *priv;
+ struct net_device *dev;
+ const void *macaddr;
+ struct resource *r;
+ int err = -EIO;
+
+ /* Up to GENET_MAX_MQ_CNT + 1 TX queues and a single RX queue */
+ dev = alloc_etherdev_mqs(sizeof(*priv), GENET_MAX_MQ_CNT + 1, 1);
+ if (!dev) {
+ dev_err(&pdev->dev, "can't allocate net device\n");
+ return -ENOMEM;
+ }
+
+ of_id = of_match_node(bcmgenet_match, dn);
+ if (!of_id)
+ return -EINVAL;
+
+ priv = netdev_priv(dev);
+ priv->irq0 = platform_get_irq(pdev, 0);
+ priv->irq1 = platform_get_irq(pdev, 1);
+ if (!priv->irq0 || !priv->irq1) {
+ dev_err(&pdev->dev, "can't find IRQs\n");
+ err = -EINVAL;
+ goto err;
+ }
+
+ macaddr = of_get_mac_address(dn);
+ if (!macaddr) {
+ dev_err(&pdev->dev, "can't find MAC address\n");
+ err = -EINVAL;
+ goto err;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->base)) {
+ err = PTR_ERR(priv->base);
+ goto err;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ dev_set_drvdata(&pdev->dev, dev);
+ ether_addr_copy(dev->dev_addr, macaddr);
+ dev->watchdog_timeo = 2 * HZ;
+ SET_ETHTOOL_OPS(dev, &bcmgenet_ethtool_ops);
+ dev->netdev_ops = &bcmgenet_netdev_ops;
+ netif_napi_add(dev, &priv->napi, bcmgenet_poll, 64);
+
+ priv->msg_enable = netif_msg_init(-1, GENET_MSG_DEFAULT);
+
+ /* Set hardware features */
+ dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM;
+
+ /* Set the needed headroom to account for any possible
+ * features enabling/disabling at runtime
+ */
+ dev->needed_headroom += 64;
+
+ netdev_boot_setup_check(dev);
+
+ priv->dev = dev;
+ priv->pdev = pdev;
+ priv->version = (enum bcmgenet_version)of_id->data;
+
+ bcmgenet_set_hw_params(priv);
+
+ /* Mii wait queue */
+ init_waitqueue_head(&priv->wq);
+ /* Always use RX_BUF_LENGTH (2KB) buffer for all chips */
+ priv->rx_buf_len = RX_BUF_LENGTH;
+ INIT_WORK(&priv->bcmgenet_irq_work, bcmgenet_irq_task);
+
+ priv->clk = devm_clk_get(&priv->pdev->dev, "enet");
+ if (IS_ERR(priv->clk))
+ dev_warn(&priv->pdev->dev, "failed to get enet clock\n");
+
+ priv->clk_wol = devm_clk_get(&priv->pdev->dev, "enet-wol");
+ if (IS_ERR(priv->clk_wol))
+ dev_warn(&priv->pdev->dev, "failed to get enet-wol clock\n");
+
+ if (!IS_ERR(priv->clk))
+ clk_prepare_enable(priv->clk);
+
+ err = reset_umac(priv);
+ if (err)
+ goto err_clk_disable;
+
+ err = bcmgenet_mii_init(dev);
+ if (err)
+ goto err_clk_disable;
+
+ /* setup number of real queues + 1 (GENET_V1 has 0 hardware queues
+ * just the ring 16 descriptor based TX
+ */
+ netif_set_real_num_tx_queues(priv->dev, priv->hw_params->tx_queues + 1);
+ netif_set_real_num_rx_queues(priv->dev, priv->hw_params->rx_queues + 1);
+
+ err = register_netdev(dev);
+ if (err)
+ goto err_clk_disable;
+
+ /* Turn off the main clock, WOL clock is handled separately */
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+
+ return err;
+
+err_clk_disable:
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+err:
+ free_netdev(dev);
+ return err;
+}
+
+static int bcmgenet_remove(struct platform_device *pdev)
+{
+ struct bcmgenet_priv *priv = dev_to_priv(&pdev->dev);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ unregister_netdev(priv->dev);
+ bcmgenet_mii_exit(priv->dev);
+ free_netdev(priv->dev);
+
+ return 0;
+}
+
+
+static struct platform_driver bcmgenet_driver = {
+ .probe = bcmgenet_probe,
+ .remove = bcmgenet_remove,
+ .driver = {
+ .name = "bcmgenet",
+ .owner = THIS_MODULE,
+ .of_match_table = bcmgenet_match,
+ },
+};
+module_platform_driver(bcmgenet_driver);
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom GENET Ethernet controller driver");
+MODULE_ALIAS("platform:bcmgenet");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h
new file mode 100644
index 000000000000..0f117105fed1
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h
@@ -0,0 +1,628 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 __BCMGENET_H__
+#define __BCMGENET_H__
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/phy.h>
+
+/* total number of Buffer Descriptors, same for Rx/Tx */
+#define TOTAL_DESC 256
+
+/* which ring is descriptor based */
+#define DESC_INDEX 16
+
+/* Body(1500) + EH_SIZE(14) + VLANTAG(4) + BRCMTAG(6) + FCS(4) = 1528.
+ * 1536 is multiple of 256 bytes
+ */
+#define ENET_BRCM_TAG_LEN 6
+#define ENET_PAD 8
+#define ENET_MAX_MTU_SIZE (ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN + \
+ ENET_BRCM_TAG_LEN + ETH_FCS_LEN + ENET_PAD)
+#define DMA_MAX_BURST_LENGTH 0x10
+
+/* misc. configuration */
+#define CLEAR_ALL_HFB 0xFF
+#define DMA_FC_THRESH_HI (TOTAL_DESC >> 4)
+#define DMA_FC_THRESH_LO 5
+
+/* 64B receive/transmit status block */
+struct status_64 {
+ u32 length_status; /* length and peripheral status */
+ u32 ext_status; /* Extended status*/
+ u32 rx_csum; /* partial rx checksum */
+ u32 unused1[9]; /* unused */
+ u32 tx_csum_info; /* Tx checksum info. */
+ u32 unused2[3]; /* unused */
+};
+
+/* Rx status bits */
+#define STATUS_RX_EXT_MASK 0x1FFFFF
+#define STATUS_RX_CSUM_MASK 0xFFFF
+#define STATUS_RX_CSUM_OK 0x10000
+#define STATUS_RX_CSUM_FR 0x20000
+#define STATUS_RX_PROTO_TCP 0
+#define STATUS_RX_PROTO_UDP 1
+#define STATUS_RX_PROTO_ICMP 2
+#define STATUS_RX_PROTO_OTHER 3
+#define STATUS_RX_PROTO_MASK 3
+#define STATUS_RX_PROTO_SHIFT 18
+#define STATUS_FILTER_INDEX_MASK 0xFFFF
+/* Tx status bits */
+#define STATUS_TX_CSUM_START_MASK 0X7FFF
+#define STATUS_TX_CSUM_START_SHIFT 16
+#define STATUS_TX_CSUM_PROTO_UDP 0x8000
+#define STATUS_TX_CSUM_OFFSET_MASK 0x7FFF
+#define STATUS_TX_CSUM_LV 0x80000000
+
+/* DMA Descriptor */
+#define DMA_DESC_LENGTH_STATUS 0x00 /* in bytes of data in buffer */
+#define DMA_DESC_ADDRESS_LO 0x04 /* lower bits of PA */
+#define DMA_DESC_ADDRESS_HI 0x08 /* upper 32 bits of PA, GENETv4+ */
+
+/* Rx/Tx common counter group */
+struct bcmgenet_pkt_counters {
+ u32 cnt_64; /* RO Received/Transmited 64 bytes packet */
+ u32 cnt_127; /* RO Rx/Tx 127 bytes packet */
+ u32 cnt_255; /* RO Rx/Tx 65-255 bytes packet */
+ u32 cnt_511; /* RO Rx/Tx 256-511 bytes packet */
+ u32 cnt_1023; /* RO Rx/Tx 512-1023 bytes packet */
+ u32 cnt_1518; /* RO Rx/Tx 1024-1518 bytes packet */
+ u32 cnt_mgv; /* RO Rx/Tx 1519-1522 good VLAN packet */
+ u32 cnt_2047; /* RO Rx/Tx 1522-2047 bytes packet*/
+ u32 cnt_4095; /* RO Rx/Tx 2048-4095 bytes packet*/
+ u32 cnt_9216; /* RO Rx/Tx 4096-9216 bytes packet*/
+};
+
+/* RSV, Receive Status Vector */
+struct bcmgenet_rx_counters {
+ struct bcmgenet_pkt_counters pkt_cnt;
+ u32 pkt; /* RO (0x428) Received pkt count*/
+ u32 bytes; /* RO Received byte count */
+ u32 mca; /* RO # of Received multicast pkt */
+ u32 bca; /* RO # of Receive broadcast pkt */
+ u32 fcs; /* RO # of Received FCS error */
+ u32 cf; /* RO # of Received control frame pkt*/
+ u32 pf; /* RO # of Received pause frame pkt */
+ u32 uo; /* RO # of unknown op code pkt */
+ u32 aln; /* RO # of alignment error count */
+ u32 flr; /* RO # of frame length out of range count */
+ u32 cde; /* RO # of code error pkt */
+ u32 fcr; /* RO # of carrier sense error pkt */
+ u32 ovr; /* RO # of oversize pkt*/
+ u32 jbr; /* RO # of jabber count */
+ u32 mtue; /* RO # of MTU error pkt*/
+ u32 pok; /* RO # of Received good pkt */
+ u32 uc; /* RO # of unicast pkt */
+ u32 ppp; /* RO # of PPP pkt */
+ u32 rcrc; /* RO (0x470),# of CRC match pkt */
+};
+
+/* TSV, Transmit Status Vector */
+struct bcmgenet_tx_counters {
+ struct bcmgenet_pkt_counters pkt_cnt;
+ u32 pkts; /* RO (0x4a8) Transmited pkt */
+ u32 mca; /* RO # of xmited multicast pkt */
+ u32 bca; /* RO # of xmited broadcast pkt */
+ u32 pf; /* RO # of xmited pause frame count */
+ u32 cf; /* RO # of xmited control frame count */
+ u32 fcs; /* RO # of xmited FCS error count */
+ u32 ovr; /* RO # of xmited oversize pkt */
+ u32 drf; /* RO # of xmited deferral pkt */
+ u32 edf; /* RO # of xmited Excessive deferral pkt*/
+ u32 scl; /* RO # of xmited single collision pkt */
+ u32 mcl; /* RO # of xmited multiple collision pkt*/
+ u32 lcl; /* RO # of xmited late collision pkt */
+ u32 ecl; /* RO # of xmited excessive collision pkt*/
+ u32 frg; /* RO # of xmited fragments pkt*/
+ u32 ncl; /* RO # of xmited total collision count */
+ u32 jbr; /* RO # of xmited jabber count*/
+ u32 bytes; /* RO # of xmited byte count */
+ u32 pok; /* RO # of xmited good pkt */
+ u32 uc; /* RO (0x0x4f0)# of xmited unitcast pkt */
+};
+
+struct bcmgenet_mib_counters {
+ struct bcmgenet_rx_counters rx;
+ struct bcmgenet_tx_counters tx;
+ u32 rx_runt_cnt;
+ u32 rx_runt_fcs;
+ u32 rx_runt_fcs_align;
+ u32 rx_runt_bytes;
+ u32 rbuf_ovflow_cnt;
+ u32 rbuf_err_cnt;
+ u32 mdf_err_cnt;
+};
+
+#define UMAC_HD_BKP_CTRL 0x004
+#define HD_FC_EN (1 << 0)
+#define HD_FC_BKOFF_OK (1 << 1)
+#define IPG_CONFIG_RX_SHIFT 2
+#define IPG_CONFIG_RX_MASK 0x1F
+
+#define UMAC_CMD 0x008
+#define CMD_TX_EN (1 << 0)
+#define CMD_RX_EN (1 << 1)
+#define UMAC_SPEED_10 0
+#define UMAC_SPEED_100 1
+#define UMAC_SPEED_1000 2
+#define UMAC_SPEED_2500 3
+#define CMD_SPEED_SHIFT 2
+#define CMD_SPEED_MASK 3
+#define CMD_PROMISC (1 << 4)
+#define CMD_PAD_EN (1 << 5)
+#define CMD_CRC_FWD (1 << 6)
+#define CMD_PAUSE_FWD (1 << 7)
+#define CMD_RX_PAUSE_IGNORE (1 << 8)
+#define CMD_TX_ADDR_INS (1 << 9)
+#define CMD_HD_EN (1 << 10)
+#define CMD_SW_RESET (1 << 13)
+#define CMD_LCL_LOOP_EN (1 << 15)
+#define CMD_AUTO_CONFIG (1 << 22)
+#define CMD_CNTL_FRM_EN (1 << 23)
+#define CMD_NO_LEN_CHK (1 << 24)
+#define CMD_RMT_LOOP_EN (1 << 25)
+#define CMD_PRBL_EN (1 << 27)
+#define CMD_TX_PAUSE_IGNORE (1 << 28)
+#define CMD_TX_RX_EN (1 << 29)
+#define CMD_RUNT_FILTER_DIS (1 << 30)
+
+#define UMAC_MAC0 0x00C
+#define UMAC_MAC1 0x010
+#define UMAC_MAX_FRAME_LEN 0x014
+
+#define UMAC_TX_FLUSH 0x334
+
+#define UMAC_MIB_START 0x400
+
+#define UMAC_MDIO_CMD 0x614
+#define MDIO_START_BUSY (1 << 29)
+#define MDIO_READ_FAIL (1 << 28)
+#define MDIO_RD (2 << 26)
+#define MDIO_WR (1 << 26)
+#define MDIO_PMD_SHIFT 21
+#define MDIO_PMD_MASK 0x1F
+#define MDIO_REG_SHIFT 16
+#define MDIO_REG_MASK 0x1F
+
+#define UMAC_RBUF_OVFL_CNT 0x61C
+
+#define UMAC_MPD_CTRL 0x620
+#define MPD_EN (1 << 0)
+#define MPD_PW_EN (1 << 27)
+#define MPD_MSEQ_LEN_SHIFT 16
+#define MPD_MSEQ_LEN_MASK 0xFF
+
+#define UMAC_MPD_PW_MS 0x624
+#define UMAC_MPD_PW_LS 0x628
+#define UMAC_RBUF_ERR_CNT 0x634
+#define UMAC_MDF_ERR_CNT 0x638
+#define UMAC_MDF_CTRL 0x650
+#define UMAC_MDF_ADDR 0x654
+#define UMAC_MIB_CTRL 0x580
+#define MIB_RESET_RX (1 << 0)
+#define MIB_RESET_RUNT (1 << 1)
+#define MIB_RESET_TX (1 << 2)
+
+#define RBUF_CTRL 0x00
+#define RBUF_64B_EN (1 << 0)
+#define RBUF_ALIGN_2B (1 << 1)
+#define RBUF_BAD_DIS (1 << 2)
+
+#define RBUF_STATUS 0x0C
+#define RBUF_STATUS_WOL (1 << 0)
+#define RBUF_STATUS_MPD_INTR_ACTIVE (1 << 1)
+#define RBUF_STATUS_ACPI_INTR_ACTIVE (1 << 2)
+
+#define RBUF_CHK_CTRL 0x14
+#define RBUF_RXCHK_EN (1 << 0)
+#define RBUF_SKIP_FCS (1 << 4)
+
+#define RBUF_TBUF_SIZE_CTRL 0xb4
+
+#define RBUF_HFB_CTRL_V1 0x38
+#define RBUF_HFB_FILTER_EN_SHIFT 16
+#define RBUF_HFB_FILTER_EN_MASK 0xffff0000
+#define RBUF_HFB_EN (1 << 0)
+#define RBUF_HFB_256B (1 << 1)
+#define RBUF_ACPI_EN (1 << 2)
+
+#define RBUF_HFB_LEN_V1 0x3C
+#define RBUF_FLTR_LEN_MASK 0xFF
+#define RBUF_FLTR_LEN_SHIFT 8
+
+#define TBUF_CTRL 0x00
+#define TBUF_BP_MC 0x0C
+
+#define TBUF_CTRL_V1 0x80
+#define TBUF_BP_MC_V1 0xA0
+
+#define HFB_CTRL 0x00
+#define HFB_FLT_ENABLE_V3PLUS 0x04
+#define HFB_FLT_LEN_V2 0x04
+#define HFB_FLT_LEN_V3PLUS 0x1C
+
+/* uniMac intrl2 registers */
+#define INTRL2_CPU_STAT 0x00
+#define INTRL2_CPU_SET 0x04
+#define INTRL2_CPU_CLEAR 0x08
+#define INTRL2_CPU_MASK_STATUS 0x0C
+#define INTRL2_CPU_MASK_SET 0x10
+#define INTRL2_CPU_MASK_CLEAR 0x14
+
+/* INTRL2 instance 0 definitions */
+#define UMAC_IRQ_SCB (1 << 0)
+#define UMAC_IRQ_EPHY (1 << 1)
+#define UMAC_IRQ_PHY_DET_R (1 << 2)
+#define UMAC_IRQ_PHY_DET_F (1 << 3)
+#define UMAC_IRQ_LINK_UP (1 << 4)
+#define UMAC_IRQ_LINK_DOWN (1 << 5)
+#define UMAC_IRQ_UMAC (1 << 6)
+#define UMAC_IRQ_UMAC_TSV (1 << 7)
+#define UMAC_IRQ_TBUF_UNDERRUN (1 << 8)
+#define UMAC_IRQ_RBUF_OVERFLOW (1 << 9)
+#define UMAC_IRQ_HFB_SM (1 << 10)
+#define UMAC_IRQ_HFB_MM (1 << 11)
+#define UMAC_IRQ_MPD_R (1 << 12)
+#define UMAC_IRQ_RXDMA_MBDONE (1 << 13)
+#define UMAC_IRQ_RXDMA_PDONE (1 << 14)
+#define UMAC_IRQ_RXDMA_BDONE (1 << 15)
+#define UMAC_IRQ_TXDMA_MBDONE (1 << 16)
+#define UMAC_IRQ_TXDMA_PDONE (1 << 17)
+#define UMAC_IRQ_TXDMA_BDONE (1 << 18)
+/* Only valid for GENETv3+ */
+#define UMAC_IRQ_MDIO_DONE (1 << 23)
+#define UMAC_IRQ_MDIO_ERROR (1 << 24)
+
+/* Register block offsets */
+#define GENET_SYS_OFF 0x0000
+#define GENET_GR_BRIDGE_OFF 0x0040
+#define GENET_EXT_OFF 0x0080
+#define GENET_INTRL2_0_OFF 0x0200
+#define GENET_INTRL2_1_OFF 0x0240
+#define GENET_RBUF_OFF 0x0300
+#define GENET_UMAC_OFF 0x0800
+
+/* SYS block offsets and register definitions */
+#define SYS_REV_CTRL 0x00
+#define SYS_PORT_CTRL 0x04
+#define PORT_MODE_INT_EPHY 0
+#define PORT_MODE_INT_GPHY 1
+#define PORT_MODE_EXT_EPHY 2
+#define PORT_MODE_EXT_GPHY 3
+#define PORT_MODE_EXT_RVMII_25 (4 | BIT(4))
+#define PORT_MODE_EXT_RVMII_50 4
+#define LED_ACT_SOURCE_MAC (1 << 9)
+
+#define SYS_RBUF_FLUSH_CTRL 0x08
+#define SYS_TBUF_FLUSH_CTRL 0x0C
+#define RBUF_FLUSH_CTRL_V1 0x04
+
+/* Ext block register offsets and definitions */
+#define EXT_EXT_PWR_MGMT 0x00
+#define EXT_PWR_DOWN_BIAS (1 << 0)
+#define EXT_PWR_DOWN_DLL (1 << 1)
+#define EXT_PWR_DOWN_PHY (1 << 2)
+#define EXT_PWR_DN_EN_LD (1 << 3)
+#define EXT_ENERGY_DET (1 << 4)
+#define EXT_IDDQ_FROM_PHY (1 << 5)
+#define EXT_PHY_RESET (1 << 8)
+#define EXT_ENERGY_DET_MASK (1 << 12)
+
+#define EXT_RGMII_OOB_CTRL 0x0C
+#define RGMII_MODE_EN (1 << 0)
+#define RGMII_LINK (1 << 4)
+#define OOB_DISABLE (1 << 5)
+#define ID_MODE_DIS (1 << 16)
+
+#define EXT_GPHY_CTRL 0x1C
+#define EXT_CFG_IDDQ_BIAS (1 << 0)
+#define EXT_CFG_PWR_DOWN (1 << 1)
+#define EXT_GPHY_RESET (1 << 5)
+
+/* DMA rings size */
+#define DMA_RING_SIZE (0x40)
+#define DMA_RINGS_SIZE (DMA_RING_SIZE * (DESC_INDEX + 1))
+
+/* DMA registers common definitions */
+#define DMA_RW_POINTER_MASK 0x1FF
+#define DMA_P_INDEX_DISCARD_CNT_MASK 0xFFFF
+#define DMA_P_INDEX_DISCARD_CNT_SHIFT 16
+#define DMA_BUFFER_DONE_CNT_MASK 0xFFFF
+#define DMA_BUFFER_DONE_CNT_SHIFT 16
+#define DMA_P_INDEX_MASK 0xFFFF
+#define DMA_C_INDEX_MASK 0xFFFF
+
+/* DMA ring size register */
+#define DMA_RING_SIZE_MASK 0xFFFF
+#define DMA_RING_SIZE_SHIFT 16
+#define DMA_RING_BUFFER_SIZE_MASK 0xFFFF
+
+/* DMA interrupt threshold register */
+#define DMA_INTR_THRESHOLD_MASK 0x00FF
+
+/* DMA XON/XOFF register */
+#define DMA_XON_THREHOLD_MASK 0xFFFF
+#define DMA_XOFF_THRESHOLD_MASK 0xFFFF
+#define DMA_XOFF_THRESHOLD_SHIFT 16
+
+/* DMA flow period register */
+#define DMA_FLOW_PERIOD_MASK 0xFFFF
+#define DMA_MAX_PKT_SIZE_MASK 0xFFFF
+#define DMA_MAX_PKT_SIZE_SHIFT 16
+
+
+/* DMA control register */
+#define DMA_EN (1 << 0)
+#define DMA_RING_BUF_EN_SHIFT 0x01
+#define DMA_RING_BUF_EN_MASK 0xFFFF
+#define DMA_TSB_SWAP_EN (1 << 20)
+
+/* DMA status register */
+#define DMA_DISABLED (1 << 0)
+#define DMA_DESC_RAM_INIT_BUSY (1 << 1)
+
+/* DMA SCB burst size register */
+#define DMA_SCB_BURST_SIZE_MASK 0x1F
+
+/* DMA activity vector register */
+#define DMA_ACTIVITY_VECTOR_MASK 0x1FFFF
+
+/* DMA backpressure mask register */
+#define DMA_BACKPRESSURE_MASK 0x1FFFF
+#define DMA_PFC_ENABLE (1 << 31)
+
+/* DMA backpressure status register */
+#define DMA_BACKPRESSURE_STATUS_MASK 0x1FFFF
+
+/* DMA override register */
+#define DMA_LITTLE_ENDIAN_MODE (1 << 0)
+#define DMA_REGISTER_MODE (1 << 1)
+
+/* DMA timeout register */
+#define DMA_TIMEOUT_MASK 0xFFFF
+#define DMA_TIMEOUT_VAL 5000 /* micro seconds */
+
+/* TDMA rate limiting control register */
+#define DMA_RATE_LIMIT_EN_MASK 0xFFFF
+
+/* TDMA arbitration control register */
+#define DMA_ARBITER_MODE_MASK 0x03
+#define DMA_RING_BUF_PRIORITY_MASK 0x1F
+#define DMA_RING_BUF_PRIORITY_SHIFT 5
+#define DMA_RATE_ADJ_MASK 0xFF
+
+/* Tx/Rx Dma Descriptor common bits*/
+#define DMA_BUFLENGTH_MASK 0x0fff
+#define DMA_BUFLENGTH_SHIFT 16
+#define DMA_OWN 0x8000
+#define DMA_EOP 0x4000
+#define DMA_SOP 0x2000
+#define DMA_WRAP 0x1000
+/* Tx specific Dma descriptor bits */
+#define DMA_TX_UNDERRUN 0x0200
+#define DMA_TX_APPEND_CRC 0x0040
+#define DMA_TX_OW_CRC 0x0020
+#define DMA_TX_DO_CSUM 0x0010
+#define DMA_TX_QTAG_SHIFT 7
+
+/* Rx Specific Dma descriptor bits */
+#define DMA_RX_CHK_V3PLUS 0x8000
+#define DMA_RX_CHK_V12 0x1000
+#define DMA_RX_BRDCAST 0x0040
+#define DMA_RX_MULT 0x0020
+#define DMA_RX_LG 0x0010
+#define DMA_RX_NO 0x0008
+#define DMA_RX_RXER 0x0004
+#define DMA_RX_CRC_ERROR 0x0002
+#define DMA_RX_OV 0x0001
+#define DMA_RX_FI_MASK 0x001F
+#define DMA_RX_FI_SHIFT 0x0007
+#define DMA_DESC_ALLOC_MASK 0x00FF
+
+#define DMA_ARBITER_RR 0x00
+#define DMA_ARBITER_WRR 0x01
+#define DMA_ARBITER_SP 0x02
+
+struct enet_cb {
+ struct sk_buff *skb;
+ void __iomem *bd_addr;
+ DEFINE_DMA_UNMAP_ADDR(dma_addr);
+ DEFINE_DMA_UNMAP_LEN(dma_len);
+};
+
+/* power management mode */
+enum bcmgenet_power_mode {
+ GENET_POWER_CABLE_SENSE = 0,
+ GENET_POWER_PASSIVE,
+};
+
+struct bcmgenet_priv;
+
+/* We support both runtime GENET detection and compile-time
+ * to optimize code-paths for a given hardware
+ */
+enum bcmgenet_version {
+ GENET_V1 = 1,
+ GENET_V2,
+ GENET_V3,
+ GENET_V4
+};
+
+#define GENET_IS_V1(p) ((p)->version == GENET_V1)
+#define GENET_IS_V2(p) ((p)->version == GENET_V2)
+#define GENET_IS_V3(p) ((p)->version == GENET_V3)
+#define GENET_IS_V4(p) ((p)->version == GENET_V4)
+
+/* Hardware flags */
+#define GENET_HAS_40BITS (1 << 0)
+#define GENET_HAS_EXT (1 << 1)
+#define GENET_HAS_MDIO_INTR (1 << 2)
+
+/* BCMGENET hardware parameters, keep this structure nicely aligned
+ * since it is going to be used in hot paths
+ */
+struct bcmgenet_hw_params {
+ u8 tx_queues;
+ u8 rx_queues;
+ u8 bds_cnt;
+ u8 bp_in_en_shift;
+ u32 bp_in_mask;
+ u8 hfb_filter_cnt;
+ u8 qtag_mask;
+ u16 tbuf_offset;
+ u32 hfb_offset;
+ u32 hfb_reg_offset;
+ u32 rdma_offset;
+ u32 tdma_offset;
+ u32 words_per_bd;
+ u32 flags;
+};
+
+struct bcmgenet_tx_ring {
+ spinlock_t lock; /* ring lock */
+ unsigned int index; /* ring index */
+ unsigned int queue; /* queue index */
+ struct enet_cb *cbs; /* tx ring buffer control block*/
+ unsigned int size; /* size of each tx ring */
+ unsigned int c_index; /* last consumer index of each ring*/
+ unsigned int free_bds; /* # of free bds for each ring */
+ unsigned int write_ptr; /* Tx ring write pointer SW copy */
+ unsigned int prod_index; /* Tx ring producer index SW copy */
+ unsigned int cb_ptr; /* Tx ring initial CB ptr */
+ unsigned int end_ptr; /* Tx ring end CB ptr */
+ void (*int_enable)(struct bcmgenet_priv *priv,
+ struct bcmgenet_tx_ring *);
+ void (*int_disable)(struct bcmgenet_priv *priv,
+ struct bcmgenet_tx_ring *);
+};
+
+/* device context */
+struct bcmgenet_priv {
+ void __iomem *base;
+ enum bcmgenet_version version;
+ struct net_device *dev;
+ u32 int0_mask;
+ u32 int1_mask;
+
+ /* NAPI for descriptor based rx */
+ struct napi_struct napi ____cacheline_aligned;
+
+ /* transmit variables */
+ void __iomem *tx_bds;
+ struct enet_cb *tx_cbs;
+ unsigned int num_tx_bds;
+
+ struct bcmgenet_tx_ring tx_rings[DESC_INDEX + 1];
+
+ /* receive variables */
+ void __iomem *rx_bds;
+ void __iomem *rx_bd_assign_ptr;
+ int rx_bd_assign_index;
+ struct enet_cb *rx_cbs;
+ unsigned int num_rx_bds;
+ unsigned int rx_buf_len;
+ unsigned int rx_read_ptr;
+ unsigned int rx_c_index;
+
+ /* other misc variables */
+ struct bcmgenet_hw_params *hw_params;
+
+ /* MDIO bus variables */
+ wait_queue_head_t wq;
+ struct phy_device *phydev;
+ struct device_node *phy_dn;
+ struct mii_bus *mii_bus;
+
+ /* PHY device variables */
+ int old_duplex;
+ int old_link;
+ int old_pause;
+ phy_interface_t phy_interface;
+ int phy_addr;
+ int ext_phy;
+
+ /* Interrupt variables */
+ struct work_struct bcmgenet_irq_work;
+ int irq0;
+ int irq1;
+ unsigned int irq0_stat;
+ unsigned int irq1_stat;
+
+ /* HW descriptors/checksum variables */
+ bool desc_64b_en;
+ bool desc_rxchk_en;
+ bool crc_fwd_en;
+
+ unsigned int dma_rx_chk_bit;
+
+ u32 msg_enable;
+
+ struct clk *clk;
+ struct platform_device *pdev;
+
+ /* WOL */
+ unsigned long wol_enabled;
+ struct clk *clk_wol;
+ u32 wolopts;
+
+ struct bcmgenet_mib_counters mib;
+};
+
+#define GENET_IO_MACRO(name, offset) \
+static inline u32 bcmgenet_##name##_readl(struct bcmgenet_priv *priv, \
+ u32 off) \
+{ \
+ return __raw_readl(priv->base + offset + off); \
+} \
+static inline void bcmgenet_##name##_writel(struct bcmgenet_priv *priv, \
+ u32 val, u32 off) \
+{ \
+ __raw_writel(val, priv->base + offset + off); \
+}
+
+GENET_IO_MACRO(ext, GENET_EXT_OFF);
+GENET_IO_MACRO(umac, GENET_UMAC_OFF);
+GENET_IO_MACRO(sys, GENET_SYS_OFF);
+
+/* interrupt l2 registers accessors */
+GENET_IO_MACRO(intrl2_0, GENET_INTRL2_0_OFF);
+GENET_IO_MACRO(intrl2_1, GENET_INTRL2_1_OFF);
+
+/* HFB register accessors */
+GENET_IO_MACRO(hfb, priv->hw_params->hfb_offset);
+
+/* GENET v2+ HFB control and filter len helpers */
+GENET_IO_MACRO(hfb_reg, priv->hw_params->hfb_reg_offset);
+
+/* RBUF register accessors */
+GENET_IO_MACRO(rbuf, GENET_RBUF_OFF);
+
+/* MDIO routines */
+int bcmgenet_mii_init(struct net_device *dev);
+int bcmgenet_mii_config(struct net_device *dev);
+void bcmgenet_mii_exit(struct net_device *dev);
+void bcmgenet_mii_reset(struct net_device *dev);
+
+#endif /* __BCMGENET_H__ */
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
new file mode 100644
index 000000000000..4608673beaff
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -0,0 +1,464 @@
+/*
+ * Broadcom GENET MDIO routines
+ *
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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/types.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/bitops.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+#include <linux/phy_fixed.h>
+#include <linux/brcmphy.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+
+#include "bcmgenet.h"
+
+/* read a value from the MII */
+static int bcmgenet_mii_read(struct mii_bus *bus, int phy_id, int location)
+{
+ int ret;
+ struct net_device *dev = bus->priv;
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ u32 reg;
+
+ bcmgenet_umac_writel(priv, (MDIO_RD | (phy_id << MDIO_PMD_SHIFT) |
+ (location << MDIO_REG_SHIFT)), UMAC_MDIO_CMD);
+ /* Start MDIO transaction*/
+ reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD);
+ reg |= MDIO_START_BUSY;
+ bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD);
+ wait_event_timeout(priv->wq,
+ !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD)
+ & MDIO_START_BUSY),
+ HZ / 100);
+ ret = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD);
+
+ if (ret & MDIO_READ_FAIL)
+ return -EIO;
+
+ return ret & 0xffff;
+}
+
+/* write a value to the MII */
+static int bcmgenet_mii_write(struct mii_bus *bus, int phy_id,
+ int location, u16 val)
+{
+ struct net_device *dev = bus->priv;
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ u32 reg;
+
+ bcmgenet_umac_writel(priv, (MDIO_WR | (phy_id << MDIO_PMD_SHIFT) |
+ (location << MDIO_REG_SHIFT) | (0xffff & val)),
+ UMAC_MDIO_CMD);
+ reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD);
+ reg |= MDIO_START_BUSY;
+ bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD);
+ wait_event_timeout(priv->wq,
+ !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) &
+ MDIO_START_BUSY),
+ HZ / 100);
+
+ return 0;
+}
+
+/* setup netdev link state when PHY link status change and
+ * update UMAC and RGMII block when link up
+ */
+static void bcmgenet_mii_setup(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+ u32 reg, cmd_bits = 0;
+ unsigned int status_changed = 0;
+
+ if (priv->old_link != phydev->link) {
+ status_changed = 1;
+ priv->old_link = phydev->link;
+ }
+
+ if (phydev->link) {
+ /* program UMAC and RGMII block based on established link
+ * speed, pause, and duplex.
+ * the speed set in umac->cmd tell RGMII block which clock
+ * 25MHz(100Mbps)/125MHz(1Gbps) to use for transmit.
+ * receive clock is provided by PHY.
+ */
+ reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
+ reg &= ~OOB_DISABLE;
+ reg |= RGMII_LINK;
+ bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
+
+ /* speed */
+ if (phydev->speed == SPEED_1000)
+ cmd_bits = UMAC_SPEED_1000;
+ else if (phydev->speed == SPEED_100)
+ cmd_bits = UMAC_SPEED_100;
+ else
+ cmd_bits = UMAC_SPEED_10;
+ cmd_bits <<= CMD_SPEED_SHIFT;
+
+ if (priv->old_duplex != phydev->duplex) {
+ status_changed = 1;
+ priv->old_duplex = phydev->duplex;
+ }
+
+ /* duplex */
+ if (phydev->duplex != DUPLEX_FULL)
+ cmd_bits |= CMD_HD_EN;
+
+ if (priv->old_pause != phydev->pause) {
+ status_changed = 1;
+ priv->old_pause = phydev->pause;
+ }
+
+ /* pause capability */
+ if (!phydev->pause)
+ cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE;
+
+ reg = bcmgenet_umac_readl(priv, UMAC_CMD);
+ reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) |
+ CMD_HD_EN |
+ CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE);
+ reg |= cmd_bits;
+ bcmgenet_umac_writel(priv, reg, UMAC_CMD);
+ }
+
+ if (status_changed)
+ phy_print_status(phydev);
+}
+
+void bcmgenet_mii_reset(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+
+ if (priv->phydev) {
+ phy_init_hw(priv->phydev);
+ phy_start_aneg(priv->phydev);
+ }
+}
+
+static void bcmgenet_ephy_power_up(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ u32 reg = 0;
+
+ /* EXT_GPHY_CTRL is only valid for GENETv4 and onward */
+ if (!GENET_IS_V4(priv))
+ return;
+
+ reg = bcmgenet_ext_readl(priv, EXT_GPHY_CTRL);
+ reg &= ~(EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN);
+ reg |= EXT_GPHY_RESET;
+ bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
+ mdelay(2);
+
+ reg &= ~EXT_GPHY_RESET;
+ bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
+ udelay(20);
+}
+
+static void bcmgenet_internal_phy_setup(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ u32 reg;
+
+ /* Power up EPHY */
+ bcmgenet_ephy_power_up(dev);
+ /* enable APD */
+ reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
+ reg |= EXT_PWR_DN_EN_LD;
+ bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
+ bcmgenet_mii_reset(dev);
+}
+
+static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
+{
+ u32 reg;
+
+ /* Speed settings are set in bcmgenet_mii_setup() */
+ reg = bcmgenet_sys_readl(priv, SYS_PORT_CTRL);
+ reg |= LED_ACT_SOURCE_MAC;
+ bcmgenet_sys_writel(priv, reg, SYS_PORT_CTRL);
+}
+
+int bcmgenet_mii_config(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+ struct device *kdev = &priv->pdev->dev;
+ const char *phy_name = NULL;
+ u32 id_mode_dis = 0;
+ u32 port_ctrl;
+ u32 reg;
+
+ priv->ext_phy = !phy_is_internal(priv->phydev) &&
+ (priv->phy_interface != PHY_INTERFACE_MODE_MOCA);
+
+ if (phy_is_internal(priv->phydev))
+ priv->phy_interface = PHY_INTERFACE_MODE_NA;
+
+ switch (priv->phy_interface) {
+ case PHY_INTERFACE_MODE_NA:
+ case PHY_INTERFACE_MODE_MOCA:
+ /* Irrespective of the actually configured PHY speed (100 or
+ * 1000) GENETv4 only has an internal GPHY so we will just end
+ * up masking the Gigabit features from what we support, not
+ * switching to the EPHY
+ */
+ if (GENET_IS_V4(priv))
+ port_ctrl = PORT_MODE_INT_GPHY;
+ else
+ port_ctrl = PORT_MODE_INT_EPHY;
+
+ bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL);
+
+ if (phy_is_internal(priv->phydev)) {
+ phy_name = "internal PHY";
+ bcmgenet_internal_phy_setup(dev);
+ } else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) {
+ phy_name = "MoCA";
+ bcmgenet_moca_phy_setup(priv);
+ }
+ break;
+
+ case PHY_INTERFACE_MODE_MII:
+ phy_name = "external MII";
+ phydev->supported &= PHY_BASIC_FEATURES;
+ bcmgenet_sys_writel(priv,
+ PORT_MODE_EXT_EPHY, SYS_PORT_CTRL);
+ break;
+
+ case PHY_INTERFACE_MODE_REVMII:
+ phy_name = "external RvMII";
+ /* of_mdiobus_register took care of reading the 'max-speed'
+ * PHY property for us, effectively limiting the PHY supported
+ * capabilities, use that knowledge to also configure the
+ * Reverse MII interface correctly.
+ */
+ if ((priv->phydev->supported & PHY_BASIC_FEATURES) ==
+ PHY_BASIC_FEATURES)
+ port_ctrl = PORT_MODE_EXT_RVMII_25;
+ else
+ port_ctrl = PORT_MODE_EXT_RVMII_50;
+ bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL);
+ break;
+
+ case PHY_INTERFACE_MODE_RGMII:
+ /* RGMII_NO_ID: TXC transitions at the same time as TXD
+ * (requires PCB or receiver-side delay)
+ * RGMII: Add 2ns delay on TXC (90 degree shift)
+ *
+ * ID is implicitly disabled for 100Mbps (RG)MII operation.
+ */
+ id_mode_dis = BIT(16);
+ /* fall through */
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ if (id_mode_dis)
+ phy_name = "external RGMII (no delay)";
+ else
+ phy_name = "external RGMII (TX delay)";
+ reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
+ reg |= RGMII_MODE_EN | id_mode_dis;
+ bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
+ bcmgenet_sys_writel(priv,
+ PORT_MODE_EXT_GPHY, SYS_PORT_CTRL);
+ break;
+ default:
+ dev_err(kdev, "unknown phy mode: %d\n", priv->phy_interface);
+ return -EINVAL;
+ }
+
+ dev_info(kdev, "configuring instance for %s\n", phy_name);
+
+ return 0;
+}
+
+static int bcmgenet_mii_probe(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev;
+ unsigned int phy_flags;
+ int ret;
+
+ if (priv->phydev) {
+ pr_info("PHY already attached\n");
+ return 0;
+ }
+
+ if (priv->phy_dn)
+ phydev = of_phy_connect(dev, priv->phy_dn,
+ bcmgenet_mii_setup, 0,
+ priv->phy_interface);
+ else
+ phydev = of_phy_connect_fixed_link(dev,
+ bcmgenet_mii_setup,
+ priv->phy_interface);
+
+ if (!phydev) {
+ pr_err("could not attach to PHY\n");
+ return -ENODEV;
+ }
+
+ priv->old_link = -1;
+ priv->old_duplex = -1;
+ priv->old_pause = -1;
+ priv->phydev = phydev;
+
+ /* Configure port multiplexer based on what the probed PHY device since
+ * reading the 'max-speed' property determines the maximum supported
+ * PHY speed which is needed for bcmgenet_mii_config() to configure
+ * things appropriately.
+ */
+ ret = bcmgenet_mii_config(dev);
+ if (ret) {
+ phy_disconnect(priv->phydev);
+ return ret;
+ }
+
+ phy_flags = PHY_BRCM_100MBPS_WAR;
+
+ /* workarounds are only needed for 100Mpbs PHYs, and
+ * never on GENET V1 hardware
+ */
+ if ((phydev->supported & PHY_GBIT_FEATURES) || GENET_IS_V1(priv))
+ phy_flags = 0;
+
+ phydev->dev_flags |= phy_flags;
+ phydev->advertising = phydev->supported;
+
+ /* The internal PHY has its link interrupts routed to the
+ * Ethernet MAC ISRs
+ */
+ if (phy_is_internal(priv->phydev))
+ priv->mii_bus->irq[phydev->addr] = PHY_IGNORE_INTERRUPT;
+ else
+ priv->mii_bus->irq[phydev->addr] = PHY_POLL;
+
+ pr_info("attached PHY at address %d [%s]\n",
+ phydev->addr, phydev->drv->name);
+
+ return 0;
+}
+
+static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv)
+{
+ struct mii_bus *bus;
+
+ if (priv->mii_bus)
+ return 0;
+
+ priv->mii_bus = mdiobus_alloc();
+ if (!priv->mii_bus) {
+ pr_err("failed to allocate\n");
+ return -ENOMEM;
+ }
+
+ bus = priv->mii_bus;
+ bus->priv = priv->dev;
+ bus->name = "bcmgenet MII bus";
+ bus->parent = &priv->pdev->dev;
+ bus->read = bcmgenet_mii_read;
+ bus->write = bcmgenet_mii_write;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d",
+ priv->pdev->name, priv->pdev->id);
+
+ bus->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (!bus->irq) {
+ mdiobus_free(priv->mii_bus);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv)
+{
+ struct device_node *dn = priv->pdev->dev.of_node;
+ struct device *kdev = &priv->pdev->dev;
+ struct device_node *mdio_dn;
+ char *compat;
+ int ret;
+
+ compat = kasprintf(GFP_KERNEL, "brcm,genet-mdio-v%d", priv->version);
+ if (!compat)
+ return -ENOMEM;
+
+ mdio_dn = of_find_compatible_node(dn, NULL, compat);
+ kfree(compat);
+ if (!mdio_dn) {
+ dev_err(kdev, "unable to find MDIO bus node\n");
+ return -ENODEV;
+ }
+
+ ret = of_mdiobus_register(priv->mii_bus, mdio_dn);
+ if (ret) {
+ dev_err(kdev, "failed to register MDIO bus\n");
+ return ret;
+ }
+
+ /* Fetch the PHY phandle */
+ priv->phy_dn = of_parse_phandle(dn, "phy-handle", 0);
+
+ /* Get the link mode */
+ priv->phy_interface = of_get_phy_mode(dn);
+
+ return 0;
+}
+
+int bcmgenet_mii_init(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+ int ret;
+
+ ret = bcmgenet_mii_alloc(priv);
+ if (ret)
+ return ret;
+
+ ret = bcmgenet_mii_of_init(priv);
+ if (ret)
+ goto out_free;
+
+ ret = bcmgenet_mii_probe(dev);
+ if (ret)
+ goto out;
+
+ return 0;
+
+out:
+ mdiobus_unregister(priv->mii_bus);
+out_free:
+ kfree(priv->mii_bus->irq);
+ mdiobus_free(priv->mii_bus);
+ return ret;
+}
+
+void bcmgenet_mii_exit(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+
+ mdiobus_unregister(priv->mii_bus);
+ kfree(priv->mii_bus->irq);
+ mdiobus_free(priv->mii_bus);
+}
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 70a225c8df5c..b9f7022f4e81 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -1401,11 +1401,6 @@ static int tg3_mdio_write(struct mii_bus *bp, int mii_id, int reg, u16 val)
return ret;
}
-static int tg3_mdio_reset(struct mii_bus *bp)
-{
- return 0;
-}
-
static void tg3_mdio_config_5785(struct tg3 *tp)
{
u32 val;
@@ -1542,7 +1537,6 @@ static int tg3_mdio_init(struct tg3 *tp)
tp->mdio_bus->parent = &tp->pdev->dev;
tp->mdio_bus->read = &tg3_mdio_read;
tp->mdio_bus->write = &tg3_mdio_write;
- tp->mdio_bus->reset = &tg3_mdio_reset;
tp->mdio_bus->phy_mask = ~(1 << tp->phy_addr);
tp->mdio_bus->irq = &tp->mdio_irq[0];
@@ -6322,6 +6316,7 @@ static const struct ptp_clock_info tg3_ptp_caps = {
.n_alarm = 0,
.n_ext_ts = 0,
.n_per_out = 1,
+ .n_pins = 0,
.pps = 0,
.adjfreq = tg3_ptp_adjfreq,
.adjtime = tg3_ptp_adjtime,
@@ -6593,7 +6588,7 @@ static void tg3_tx(struct tg3_napi *tnapi)
pkts_compl++;
bytes_compl += skb->len;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
if (unlikely(tx_bug)) {
tg3_tx_recover(tp);
@@ -6924,7 +6919,7 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
if (len > (tp->dev->mtu + ETH_HLEN) &&
skb->protocol != htons(ETH_P_8021Q)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
goto drop_it_no_recycle;
}
@@ -7807,7 +7802,7 @@ static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi,
PCI_DMA_TODEVICE);
/* Make sure the mapping succeeded */
if (pci_dma_mapping_error(tp->pdev, new_addr)) {
- dev_kfree_skb(new_skb);
+ dev_kfree_skb_any(new_skb);
ret = -1;
} else {
u32 save_entry = *entry;
@@ -7822,13 +7817,13 @@ static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi,
new_skb->len, base_flags,
mss, vlan)) {
tg3_tx_skb_unmap(tnapi, save_entry, -1);
- dev_kfree_skb(new_skb);
+ dev_kfree_skb_any(new_skb);
ret = -1;
}
}
}
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
*pskb = new_skb;
return ret;
}
@@ -7871,7 +7866,7 @@ static int tg3_tso_bug(struct tg3 *tp, struct sk_buff *skb)
} while (segs);
tg3_tso_bug_end:
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -7923,8 +7918,7 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct iphdr *iph;
u32 tcp_opt_len, hdr_len;
- if (skb_header_cloned(skb) &&
- pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+ if (skb_cow_head(skb, 0))
goto drop;
iph = ip_hdr(skb);
@@ -8093,7 +8087,7 @@ dma_error:
tg3_tx_skb_unmap(tnapi, tnapi->tx_prod, --i);
tnapi->tx_buffers[tnapi->tx_prod].skb = NULL;
drop:
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
drop_nofree:
tp->tx_dropped++;
return NETDEV_TX_OK;
@@ -11361,12 +11355,10 @@ static bool tg3_enable_msix(struct tg3 *tp)
msix_ent[i].vector = 0;
}
- rc = pci_enable_msix(tp->pdev, msix_ent, tp->irq_cnt);
+ rc = pci_enable_msix_range(tp->pdev, msix_ent, 1, tp->irq_cnt);
if (rc < 0) {
return false;
- } else if (rc != 0) {
- if (pci_enable_msix(tp->pdev, msix_ent, rc))
- return false;
+ } else if (rc < tp->irq_cnt) {
netdev_notice(tp->dev, "Requested %d MSI-X vectors, received %d\n",
tp->irq_cnt, rc);
tp->irq_cnt = rc;
diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index 4ad1187e82fb..675550fe8ee9 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -2496,12 +2496,10 @@ bnad_tso_prepare(struct bnad *bnad, struct sk_buff *skb)
{
int err;
- if (skb_header_cloned(skb)) {
- err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
- if (err) {
- BNAD_UPDATE_CTR(bnad, tso_err);
- return err;
- }
+ err = skb_cow_head(skb, 0);
+ if (err < 0) {
+ BNAD_UPDATE_CTR(bnad, tso_err);
+ return err;
}
/*
@@ -2669,9 +2667,11 @@ bnad_enable_msix(struct bnad *bnad)
for (i = 0; i < bnad->msix_num; i++)
bnad->msix_table[i].entry = i;
- ret = pci_enable_msix(bnad->pcidev, bnad->msix_table, bnad->msix_num);
- if (ret > 0) {
- /* Not enough MSI-X vectors. */
+ ret = pci_enable_msix_range(bnad->pcidev, bnad->msix_table,
+ 1, bnad->msix_num);
+ if (ret < 0) {
+ goto intx_mode;
+ } else if (ret < bnad->msix_num) {
pr_warn("BNA: %d MSI-X vectors allocated < %d requested\n",
ret, bnad->msix_num);
@@ -2684,18 +2684,11 @@ bnad_enable_msix(struct bnad *bnad)
bnad->msix_num = BNAD_NUM_TXQ + BNAD_NUM_RXP +
BNAD_MAILBOX_MSIX_VECTORS;
- if (bnad->msix_num > ret)
+ if (bnad->msix_num > ret) {
+ pci_disable_msix(bnad->pcidev);
goto intx_mode;
-
- /* Try once more with adjusted numbers */
- /* If this fails, fall back to INTx */
- ret = pci_enable_msix(bnad->pcidev, bnad->msix_table,
- bnad->msix_num);
- if (ret)
- goto intx_mode;
-
- } else if (ret < 0)
- goto intx_mode;
+ }
+ }
pci_intx(bnad->pcidev, 0);
@@ -2850,13 +2843,11 @@ bnad_txq_wi_prepare(struct bnad *bnad, struct bna_tcb *tcb,
}
if (unlikely((gso_size + skb_transport_offset(skb) +
tcp_hdrlen(skb)) >= skb->len)) {
- txqent->hdr.wi.opcode =
- __constant_htons(BNA_TXQ_WI_SEND);
+ txqent->hdr.wi.opcode = htons(BNA_TXQ_WI_SEND);
txqent->hdr.wi.lso_mss = 0;
BNAD_UPDATE_CTR(bnad, tx_skb_tso_too_short);
} else {
- txqent->hdr.wi.opcode =
- __constant_htons(BNA_TXQ_WI_SEND_LSO);
+ txqent->hdr.wi.opcode = htons(BNA_TXQ_WI_SEND_LSO);
txqent->hdr.wi.lso_mss = htons(gso_size);
}
@@ -2870,7 +2861,7 @@ bnad_txq_wi_prepare(struct bnad *bnad, struct bna_tcb *tcb,
htons(BNA_TXQ_WI_L4_HDR_N_OFFSET(
tcp_hdrlen(skb) >> 2, skb_transport_offset(skb)));
} else {
- txqent->hdr.wi.opcode = __constant_htons(BNA_TXQ_WI_SEND);
+ txqent->hdr.wi.opcode = htons(BNA_TXQ_WI_SEND);
txqent->hdr.wi.lso_mss = 0;
if (unlikely(skb->len > (bnad->netdev->mtu + ETH_HLEN))) {
@@ -2881,11 +2872,10 @@ bnad_txq_wi_prepare(struct bnad *bnad, struct bna_tcb *tcb,
if (skb->ip_summed == CHECKSUM_PARTIAL) {
u8 proto = 0;
- if (skb->protocol == __constant_htons(ETH_P_IP))
+ if (skb->protocol == htons(ETH_P_IP))
proto = ip_hdr(skb)->protocol;
#ifdef NETIF_F_IPV6_CSUM
- else if (skb->protocol ==
- __constant_htons(ETH_P_IPV6)) {
+ else if (skb->protocol == htons(ETH_P_IPV6)) {
/* nexthdr may not be TCP immediately. */
proto = ipv6_hdr(skb)->nexthdr;
}
@@ -2954,17 +2944,17 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
/* Sanity checks for the skb */
if (unlikely(skb->len <= ETH_HLEN)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
BNAD_UPDATE_CTR(bnad, tx_skb_too_short);
return NETDEV_TX_OK;
}
if (unlikely(len > BFI_TX_MAX_DATA_PER_VECTOR)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
BNAD_UPDATE_CTR(bnad, tx_skb_headlen_zero);
return NETDEV_TX_OK;
}
if (unlikely(len == 0)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
BNAD_UPDATE_CTR(bnad, tx_skb_headlen_zero);
return NETDEV_TX_OK;
}
@@ -2976,7 +2966,7 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
* and the netif_tx_stop_all_queues() call.
*/
if (unlikely(!tcb || !test_bit(BNAD_TXQ_TX_STARTED, &tcb->flags))) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
BNAD_UPDATE_CTR(bnad, tx_skb_stopping);
return NETDEV_TX_OK;
}
@@ -2989,7 +2979,7 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
wis = BNA_TXQ_WI_NEEDED(vectors); /* 4 vectors per work item */
if (unlikely(vectors > BFI_TX_MAX_VECTORS_PER_PKT)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
BNAD_UPDATE_CTR(bnad, tx_skb_max_vectors);
return NETDEV_TX_OK;
}
@@ -3029,7 +3019,7 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
/* Program the opcode, flags, frame_len, num_vectors in WI */
if (bnad_txq_wi_prepare(bnad, tcb, skb, txqent)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
txqent->hdr.wi.reserved = 0;
@@ -3055,7 +3045,7 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
/* Undo the changes starting at tcb->producer_index */
bnad_tx_buff_unmap(bnad, unmap_q, q_depth,
tcb->producer_index);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
BNAD_UPDATE_CTR(bnad, tx_skb_frag_zero);
return NETDEV_TX_OK;
}
@@ -3067,8 +3057,7 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
vect_id = 0;
BNA_QE_INDX_INC(prod, q_depth);
txqent = &((struct bna_txq_entry *)tcb->sw_q)[prod];
- txqent->hdr.wi_ext.opcode =
- __constant_htons(BNA_TXQ_WI_EXTENSION);
+ txqent->hdr.wi_ext.opcode = htons(BNA_TXQ_WI_EXTENSION);
unmap = &unmap_q[prod];
}
@@ -3085,7 +3074,7 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
if (unlikely(len != skb->len)) {
/* Undo the changes starting at tcb->producer_index */
bnad_tx_buff_unmap(bnad, unmap_q, q_depth, tcb->producer_index);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
BNAD_UPDATE_CTR(bnad, tx_skb_len_mismatch);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index d0c38e01e99f..ca97005e24b4 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -199,11 +199,6 @@ static int macb_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
return 0;
}
-static int macb_mdio_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
/**
* macb_set_tx_clk() - Set a clock to a new frequency
* @clk Pointer to the clock to change
@@ -375,7 +370,6 @@ int macb_mii_init(struct macb *bp)
bp->mii_bus->name = "MACB_mii_bus";
bp->mii_bus->read = &macb_mdio_read;
bp->mii_bus->write = &macb_mdio_write;
- bp->mii_bus->reset = &macb_mdio_reset;
snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
bp->pdev->name, bp->pdev->id);
bp->mii_bus->priv = bp;
@@ -1045,7 +1039,7 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
mapping = dma_map_single(&bp->pdev->dev, skb->data,
len, DMA_TO_DEVICE);
if (dma_mapping_error(&bp->pdev->dev, mapping)) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
goto unlock;
}
diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c
index d2a183c3a6ce..521dfea44b83 100644
--- a/drivers/net/ethernet/calxeda/xgmac.c
+++ b/drivers/net/ethernet/calxeda/xgmac.c
@@ -897,7 +897,7 @@ static void xgmac_tx_complete(struct xgmac_priv *priv)
/* Check tx error on the last segment */
if (desc_get_tx_ls(p)) {
desc_get_tx_status(priv, p);
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
}
priv->tx_skbuff[entry] = NULL;
@@ -1105,7 +1105,7 @@ static netdev_tx_t xgmac_xmit(struct sk_buff *skb, struct net_device *dev)
len = skb_headlen(skb);
paddr = dma_map_single(priv->device, skb->data, len, DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, paddr)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
priv->tx_skbuff[entry] = skb;
@@ -1169,7 +1169,7 @@ dma_err:
desc = first;
dma_unmap_single(priv->device, desc_get_buf_addr(desc),
desc_get_buf_len(desc), DMA_TO_DEVICE);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
index 45d77334d7d9..07bbb711b7e5 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
@@ -3088,30 +3088,22 @@ static int cxgb_enable_msix(struct adapter *adap)
{
struct msix_entry entries[SGE_QSETS + 1];
int vectors;
- int i, err;
+ int i;
vectors = ARRAY_SIZE(entries);
for (i = 0; i < vectors; ++i)
entries[i].entry = i;
- while ((err = pci_enable_msix(adap->pdev, entries, vectors)) > 0)
- vectors = err;
-
- if (err < 0)
- pci_disable_msix(adap->pdev);
-
- if (!err && vectors < (adap->params.nports + 1)) {
- pci_disable_msix(adap->pdev);
- err = -1;
- }
+ vectors = pci_enable_msix_range(adap->pdev, entries,
+ adap->params.nports + 1, vectors);
+ if (vectors < 0)
+ return vectors;
- if (!err) {
- for (i = 0; i < vectors; ++i)
- adap->msix_info[i].vec = entries[i].vector;
- adap->msix_nvectors = vectors;
- }
+ for (i = 0; i < vectors; ++i)
+ adap->msix_info[i].vec = entries[i].vector;
+ adap->msix_nvectors = vectors;
- return err;
+ return 0;
}
static void print_port_info(struct adapter *adap, const struct adapter_info *ai)
diff --git a/drivers/net/ethernet/chelsio/cxgb3/sge.c b/drivers/net/ethernet/chelsio/cxgb3/sge.c
index 632b318eb38a..8b069f96e920 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c
@@ -298,7 +298,7 @@ static void free_tx_desc(struct adapter *adapter, struct sge_txq *q,
if (need_unmap)
unmap_skb(d->skb, q, cidx, pdev);
if (d->eop) {
- kfree_skb(d->skb);
+ dev_consume_skb_any(d->skb);
d->skb = NULL;
}
}
@@ -1188,7 +1188,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb,
cpl->wr.wr_lo = htonl(V_WR_LEN(flits) | V_WR_GEN(gen) |
V_WR_TID(q->token));
wr_gen2(d, gen);
- kfree_skb(skb);
+ dev_consume_skb_any(skb);
return;
}
@@ -1233,7 +1233,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev)
* anything shorter than an Ethernet header.
*/
if (unlikely(skb->len < ETH_HLEN)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 1f4b9b30b9ed..32db37709263 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -66,6 +66,7 @@ enum {
SERNUM_LEN = 24, /* Serial # length */
EC_LEN = 16, /* E/C length */
ID_LEN = 16, /* ID length */
+ PN_LEN = 16, /* Part Number length */
};
enum {
@@ -254,6 +255,7 @@ struct vpd_params {
u8 ec[EC_LEN + 1];
u8 sn[SERNUM_LEN + 1];
u8 id[ID_LEN + 1];
+ u8 pn[PN_LEN + 1];
};
struct pci_params {
@@ -306,6 +308,7 @@ struct adapter_params {
unsigned char bypass;
unsigned int ofldq_wr_cred;
+ bool ulptx_memwrite_dsgl; /* use of T5 DSGL allowed */
};
#include "t4fw_api.h"
@@ -497,6 +500,7 @@ struct sge_txq {
spinlock_t db_lock;
int db_disabled;
unsigned short db_pidx;
+ unsigned short db_pidx_inc;
u64 udb;
};
@@ -553,8 +557,13 @@ struct sge {
u32 pktshift; /* padding between CPL & packet data */
u32 fl_align; /* response queue message alignment */
u32 fl_starve_thres; /* Free List starvation threshold */
- unsigned int starve_thres;
- u8 idma_state[2];
+
+ /* State variables for detecting an SGE Ingress DMA hang */
+ unsigned int idma_1s_thresh;/* SGE same State Counter 1s threshold */
+ unsigned int idma_stalled[2];/* SGE synthesized stalled timers in HZ */
+ unsigned int idma_state[2]; /* SGE IDMA Hang detect state */
+ unsigned int idma_qid[2]; /* SGE IDMA Hung Ingress Queue ID */
+
unsigned int egr_start;
unsigned int ingr_start;
void *egr_map[MAX_EGRQ]; /* qid->queue egress queue map */
@@ -957,7 +966,7 @@ int t4_mc_read(struct adapter *adap, int idx, u32 addr, __be32 *data,
u64 *parity);
int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data,
u64 *parity);
-
+const char *t4_get_port_type_description(enum fw_port_type port_type);
void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p);
void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log);
void t4_tp_wr_bits_indirect(struct adapter *adap, unsigned int addr,
@@ -1029,4 +1038,5 @@ void t4_db_dropped(struct adapter *adapter);
int t4_mem_win_read_len(struct adapter *adap, u32 addr, __be32 *data, int len);
int t4_fwaddrspace_write(struct adapter *adap, unsigned int mbox,
u32 addr, u32 val);
+void t4_sge_decode_idma_state(struct adapter *adapter, int state);
#endif /* __CXGB4_H__ */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 34e2488767d9..6fe58913403a 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -254,6 +254,14 @@ static DEFINE_PCI_DEVICE_TABLE(cxgb4_pci_tbl) = {
CH_DEVICE(0x5011, 4),
CH_DEVICE(0x5012, 4),
CH_DEVICE(0x5013, 4),
+ CH_DEVICE(0x5014, 4),
+ CH_DEVICE(0x5015, 4),
+ CH_DEVICE(0x5080, 4),
+ CH_DEVICE(0x5081, 4),
+ CH_DEVICE(0x5082, 4),
+ CH_DEVICE(0x5083, 4),
+ CH_DEVICE(0x5084, 4),
+ CH_DEVICE(0x5085, 4),
CH_DEVICE(0x5401, 4),
CH_DEVICE(0x5402, 4),
CH_DEVICE(0x5403, 4),
@@ -273,6 +281,14 @@ static DEFINE_PCI_DEVICE_TABLE(cxgb4_pci_tbl) = {
CH_DEVICE(0x5411, 4),
CH_DEVICE(0x5412, 4),
CH_DEVICE(0x5413, 4),
+ CH_DEVICE(0x5414, 4),
+ CH_DEVICE(0x5415, 4),
+ CH_DEVICE(0x5480, 4),
+ CH_DEVICE(0x5481, 4),
+ CH_DEVICE(0x5482, 4),
+ CH_DEVICE(0x5483, 4),
+ CH_DEVICE(0x5484, 4),
+ CH_DEVICE(0x5485, 4),
{ 0, }
};
@@ -423,15 +439,18 @@ static void link_report(struct net_device *dev)
const struct port_info *p = netdev_priv(dev);
switch (p->link_cfg.speed) {
- case SPEED_10000:
+ case 10000:
s = "10Gbps";
break;
- case SPEED_1000:
+ case 1000:
s = "1000Mbps";
break;
- case SPEED_100:
+ case 100:
s = "100Mbps";
break;
+ case 40000:
+ s = "40Gbps";
+ break;
}
netdev_info(dev, "link up, %s, full-duplex, %s PAUSE\n", s,
@@ -2061,7 +2080,7 @@ static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
0x40200, 0x40298,
0x402ac, 0x4033c,
0x403f8, 0x403fc,
- 0x41300, 0x413c4,
+ 0x41304, 0x413c4,
0x41400, 0x4141c,
0x41480, 0x414d0,
0x44000, 0x44078,
@@ -2089,7 +2108,7 @@ static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
0x48200, 0x48298,
0x482ac, 0x4833c,
0x483f8, 0x483fc,
- 0x49300, 0x493c4,
+ 0x49304, 0x493c4,
0x49400, 0x4941c,
0x49480, 0x494d0,
0x4c000, 0x4c078,
@@ -2199,6 +2218,8 @@ static unsigned int from_fw_linkcaps(unsigned int type, unsigned int caps)
else if (type == FW_PORT_TYPE_FIBER_XFI ||
type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP)
v |= SUPPORTED_FIBRE;
+ else if (type == FW_PORT_TYPE_BP40_BA)
+ v |= SUPPORTED_40000baseSR4_Full;
if (caps & FW_PORT_CAP_ANEG)
v |= SUPPORTED_Autoneg;
@@ -2215,6 +2236,8 @@ static unsigned int to_fw_linkcaps(unsigned int caps)
v |= FW_PORT_CAP_SPEED_1G;
if (caps & ADVERTISED_10000baseT_Full)
v |= FW_PORT_CAP_SPEED_10G;
+ if (caps & ADVERTISED_40000baseSR4_Full)
+ v |= FW_PORT_CAP_SPEED_40G;
return v;
}
@@ -2263,12 +2286,14 @@ static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static unsigned int speed_to_caps(int speed)
{
- if (speed == SPEED_100)
+ if (speed == 100)
return FW_PORT_CAP_SPEED_100M;
- if (speed == SPEED_1000)
+ if (speed == 1000)
return FW_PORT_CAP_SPEED_1G;
- if (speed == SPEED_10000)
+ if (speed == 10000)
return FW_PORT_CAP_SPEED_10G;
+ if (speed == 40000)
+ return FW_PORT_CAP_SPEED_40G;
return 0;
}
@@ -2296,8 +2321,10 @@ static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
if (cmd->autoneg == AUTONEG_DISABLE) {
cap = speed_to_caps(speed);
- if (!(lc->supported & cap) || (speed == SPEED_1000) ||
- (speed == SPEED_10000))
+ if (!(lc->supported & cap) ||
+ (speed == 1000) ||
+ (speed == 10000) ||
+ (speed == 40000))
return -EINVAL;
lc->requested_speed = cap;
lc->advertising = 0;
@@ -3205,8 +3232,8 @@ static int cxgb4_clip_get(const struct net_device *dev,
c.op_to_write = htonl(FW_CMD_OP(FW_CLIP_CMD) |
FW_CMD_REQUEST | FW_CMD_WRITE);
c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
- *(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
- *(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
+ c.ip_hi = *(__be64 *)(lip->s6_addr);
+ c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
}
@@ -3221,8 +3248,8 @@ static int cxgb4_clip_release(const struct net_device *dev,
c.op_to_write = htonl(FW_CMD_OP(FW_CLIP_CMD) |
FW_CMD_REQUEST | FW_CMD_READ);
c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
- *(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
- *(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
+ c.ip_hi = *(__be64 *)(lip->s6_addr);
+ c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
}
@@ -3563,14 +3590,25 @@ static void drain_db_fifo(struct adapter *adap, int usecs)
static void disable_txq_db(struct sge_txq *q)
{
- spin_lock_irq(&q->db_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&q->db_lock, flags);
q->db_disabled = 1;
- spin_unlock_irq(&q->db_lock);
+ spin_unlock_irqrestore(&q->db_lock, flags);
}
-static void enable_txq_db(struct sge_txq *q)
+static void enable_txq_db(struct adapter *adap, struct sge_txq *q)
{
spin_lock_irq(&q->db_lock);
+ if (q->db_pidx_inc) {
+ /* Make sure that all writes to the TX descriptors
+ * are committed before we tell HW about them.
+ */
+ wmb();
+ t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL),
+ QID(q->cntxt_id) | PIDX(q->db_pidx_inc));
+ q->db_pidx_inc = 0;
+ }
q->db_disabled = 0;
spin_unlock_irq(&q->db_lock);
}
@@ -3592,11 +3630,32 @@ static void enable_dbs(struct adapter *adap)
int i;
for_each_ethrxq(&adap->sge, i)
- enable_txq_db(&adap->sge.ethtxq[i].q);
+ enable_txq_db(adap, &adap->sge.ethtxq[i].q);
for_each_ofldrxq(&adap->sge, i)
- enable_txq_db(&adap->sge.ofldtxq[i].q);
+ enable_txq_db(adap, &adap->sge.ofldtxq[i].q);
for_each_port(adap, i)
- enable_txq_db(&adap->sge.ctrlq[i].q);
+ enable_txq_db(adap, &adap->sge.ctrlq[i].q);
+}
+
+static void notify_rdma_uld(struct adapter *adap, enum cxgb4_control cmd)
+{
+ if (adap->uld_handle[CXGB4_ULD_RDMA])
+ ulds[CXGB4_ULD_RDMA].control(adap->uld_handle[CXGB4_ULD_RDMA],
+ cmd);
+}
+
+static void process_db_full(struct work_struct *work)
+{
+ struct adapter *adap;
+
+ adap = container_of(work, struct adapter, db_full_task);
+
+ drain_db_fifo(adap, dbfifo_drain_delay);
+ enable_dbs(adap);
+ notify_rdma_uld(adap, CXGB4_CONTROL_DB_EMPTY);
+ t4_set_reg_field(adap, SGE_INT_ENABLE3,
+ DBFIFO_HP_INT | DBFIFO_LP_INT,
+ DBFIFO_HP_INT | DBFIFO_LP_INT);
}
static void sync_txq_pidx(struct adapter *adap, struct sge_txq *q)
@@ -3604,7 +3663,7 @@ static void sync_txq_pidx(struct adapter *adap, struct sge_txq *q)
u16 hw_pidx, hw_cidx;
int ret;
- spin_lock_bh(&q->db_lock);
+ spin_lock_irq(&q->db_lock);
ret = read_eq_indices(adap, (u16)q->cntxt_id, &hw_pidx, &hw_cidx);
if (ret)
goto out;
@@ -3621,7 +3680,8 @@ static void sync_txq_pidx(struct adapter *adap, struct sge_txq *q)
}
out:
q->db_disabled = 0;
- spin_unlock_bh(&q->db_lock);
+ q->db_pidx_inc = 0;
+ spin_unlock_irq(&q->db_lock);
if (ret)
CH_WARN(adap, "DB drop recovery failed.\n");
}
@@ -3637,29 +3697,6 @@ static void recover_all_queues(struct adapter *adap)
sync_txq_pidx(adap, &adap->sge.ctrlq[i].q);
}
-static void notify_rdma_uld(struct adapter *adap, enum cxgb4_control cmd)
-{
- mutex_lock(&uld_mutex);
- if (adap->uld_handle[CXGB4_ULD_RDMA])
- ulds[CXGB4_ULD_RDMA].control(adap->uld_handle[CXGB4_ULD_RDMA],
- cmd);
- mutex_unlock(&uld_mutex);
-}
-
-static void process_db_full(struct work_struct *work)
-{
- struct adapter *adap;
-
- adap = container_of(work, struct adapter, db_full_task);
-
- notify_rdma_uld(adap, CXGB4_CONTROL_DB_FULL);
- drain_db_fifo(adap, dbfifo_drain_delay);
- t4_set_reg_field(adap, SGE_INT_ENABLE3,
- DBFIFO_HP_INT | DBFIFO_LP_INT,
- DBFIFO_HP_INT | DBFIFO_LP_INT);
- notify_rdma_uld(adap, CXGB4_CONTROL_DB_EMPTY);
-}
-
static void process_db_drop(struct work_struct *work)
{
struct adapter *adap;
@@ -3667,11 +3704,13 @@ static void process_db_drop(struct work_struct *work)
adap = container_of(work, struct adapter, db_drop_task);
if (is_t4(adap->params.chip)) {
- disable_dbs(adap);
+ drain_db_fifo(adap, dbfifo_drain_delay);
notify_rdma_uld(adap, CXGB4_CONTROL_DB_DROP);
- drain_db_fifo(adap, 1);
+ drain_db_fifo(adap, dbfifo_drain_delay);
recover_all_queues(adap);
+ drain_db_fifo(adap, dbfifo_drain_delay);
enable_dbs(adap);
+ notify_rdma_uld(adap, CXGB4_CONTROL_DB_EMPTY);
} else {
u32 dropped_db = t4_read_reg(adap, 0x010ac);
u16 qid = (dropped_db >> 15) & 0x1ffff;
@@ -3712,6 +3751,8 @@ static void process_db_drop(struct work_struct *work)
void t4_db_full(struct adapter *adap)
{
if (is_t4(adap->params.chip)) {
+ disable_dbs(adap);
+ notify_rdma_uld(adap, CXGB4_CONTROL_DB_FULL);
t4_set_reg_field(adap, SGE_INT_ENABLE3,
DBFIFO_HP_INT | DBFIFO_LP_INT, 0);
queue_work(workq, &adap->db_full_task);
@@ -3720,8 +3761,11 @@ void t4_db_full(struct adapter *adap)
void t4_db_dropped(struct adapter *adap)
{
- if (is_t4(adap->params.chip))
- queue_work(workq, &adap->db_drop_task);
+ if (is_t4(adap->params.chip)) {
+ disable_dbs(adap);
+ notify_rdma_uld(adap, CXGB4_CONTROL_DB_FULL);
+ }
+ queue_work(workq, &adap->db_drop_task);
}
static void uld_attach(struct adapter *adap, unsigned int uld)
@@ -3765,6 +3809,7 @@ static void uld_attach(struct adapter *adap, unsigned int uld)
lli.dbfifo_int_thresh = dbfifo_int_thresh;
lli.sge_pktshift = adap->sge.pktshift;
lli.enable_fw_ofld_conn = adap->flags & FW_OFLD_CONN;
+ lli.ulptx_memwrite_dsgl = adap->params.ulptx_memwrite_dsgl;
handle = ulds[uld].add(&lli);
if (IS_ERR(handle)) {
@@ -5370,6 +5415,21 @@ static int adap_init0(struct adapter *adap)
(void) t4_set_params(adap, adap->mbox, adap->fn, 0, 1, params, val);
/*
+ * Find out whether we're allowed to use the T5+ ULPTX MEMWRITE DSGL
+ * capability. Earlier versions of the firmware didn't have the
+ * ULPTX_MEMWRITE_DSGL so we'll interpret a query failure as no
+ * permission to use ULPTX MEMWRITE DSGL.
+ */
+ if (is_t4(adap->params.chip)) {
+ adap->params.ulptx_memwrite_dsgl = false;
+ } else {
+ params[0] = FW_PARAM_DEV(ULPTX_MEMWRITE_DSGL);
+ ret = t4_query_params(adap, adap->mbox, adap->fn, 0,
+ 1, params, val);
+ adap->params.ulptx_memwrite_dsgl = (ret == 0 && val[0] != 0);
+ }
+
+ /*
* Get device capabilities so we can determine what resources we need
* to manage.
*/
@@ -5603,9 +5663,10 @@ static const struct pci_error_handlers cxgb4_eeh = {
.resume = eeh_resume,
};
-static inline bool is_10g_port(const struct link_config *lc)
+static inline bool is_x_10g_port(const struct link_config *lc)
{
- return (lc->supported & FW_PORT_CAP_SPEED_10G) != 0;
+ return (lc->supported & FW_PORT_CAP_SPEED_10G) != 0 ||
+ (lc->supported & FW_PORT_CAP_SPEED_40G) != 0;
}
static inline void init_rspq(struct sge_rspq *q, u8 timer_idx, u8 pkt_cnt_idx,
@@ -5629,7 +5690,7 @@ static void cfg_queues(struct adapter *adap)
int i, q10g = 0, n10g = 0, qidx = 0;
for_each_port(adap, i)
- n10g += is_10g_port(&adap2pinfo(adap, i)->link_cfg);
+ n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg);
/*
* We default to 1 queue per non-10G port and up to # of cores queues
@@ -5644,7 +5705,7 @@ static void cfg_queues(struct adapter *adap)
struct port_info *pi = adap2pinfo(adap, i);
pi->first_qset = qidx;
- pi->nqsets = is_10g_port(&pi->link_cfg) ? q10g : 1;
+ pi->nqsets = is_x_10g_port(&pi->link_cfg) ? q10g : 1;
qidx += pi->nqsets;
}
@@ -5737,7 +5798,7 @@ static void reduce_ethqs(struct adapter *adap, int n)
static int enable_msix(struct adapter *adap)
{
int ofld_need = 0;
- int i, err, want, need;
+ int i, want, need;
struct sge *s = &adap->sge;
unsigned int nchan = adap->params.nports;
struct msix_entry entries[MAX_INGQ + 1];
@@ -5753,32 +5814,30 @@ static int enable_msix(struct adapter *adap)
}
need = adap->params.nports + EXTRA_VECS + ofld_need;
- while ((err = pci_enable_msix(adap->pdev, entries, want)) >= need)
- want = err;
+ want = pci_enable_msix_range(adap->pdev, entries, need, want);
+ if (want < 0)
+ return want;
- if (!err) {
- /*
- * Distribute available vectors to the various queue groups.
- * Every group gets its minimum requirement and NIC gets top
- * priority for leftovers.
- */
- i = want - EXTRA_VECS - ofld_need;
- if (i < s->max_ethqsets) {
- s->max_ethqsets = i;
- if (i < s->ethqsets)
- reduce_ethqs(adap, i);
- }
- if (is_offload(adap)) {
- i = want - EXTRA_VECS - s->max_ethqsets;
- i -= ofld_need - nchan;
- s->ofldqsets = (i / nchan) * nchan; /* round down */
- }
- for (i = 0; i < want; ++i)
- adap->msix_info[i].vec = entries[i].vector;
- } else if (err > 0)
- dev_info(adap->pdev_dev,
- "only %d MSI-X vectors left, not using MSI-X\n", err);
- return err;
+ /*
+ * Distribute available vectors to the various queue groups.
+ * Every group gets its minimum requirement and NIC gets top
+ * priority for leftovers.
+ */
+ i = want - EXTRA_VECS - ofld_need;
+ if (i < s->max_ethqsets) {
+ s->max_ethqsets = i;
+ if (i < s->ethqsets)
+ reduce_ethqs(adap, i);
+ }
+ if (is_offload(adap)) {
+ i = want - EXTRA_VECS - s->max_ethqsets;
+ i -= ofld_need - nchan;
+ s->ofldqsets = (i / nchan) * nchan; /* round down */
+ }
+ for (i = 0; i < want; ++i)
+ adap->msix_info[i].vec = entries[i].vector;
+
+ return 0;
}
#undef EXTRA_VECS
@@ -5801,11 +5860,6 @@ static int init_rss(struct adapter *adap)
static void print_port_info(const struct net_device *dev)
{
- static const char *base[] = {
- "R XFI", "R XAUI", "T SGMII", "T XFI", "T XAUI", "KX4", "CX4",
- "KX", "KR", "R SFP+", "KR/KX", "KR/KX/KX4"
- };
-
char buf[80];
char *bufp = buf;
const char *spd = "";
@@ -5823,9 +5877,11 @@ static void print_port_info(const struct net_device *dev)
bufp += sprintf(bufp, "1000/");
if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_10G)
bufp += sprintf(bufp, "10G/");
+ if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_40G)
+ bufp += sprintf(bufp, "40G/");
if (bufp != buf)
--bufp;
- sprintf(bufp, "BASE-%s", base[pi->port_type]);
+ 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",
adap->params.vpd.id,
@@ -5833,8 +5889,8 @@ static void print_port_info(const struct net_device *dev)
is_offload(adap) ? "R" : "", adap->params.pci.width, spd,
(adap->flags & USING_MSIX) ? " MSI-X" :
(adap->flags & USING_MSI) ? " MSI" : "");
- netdev_info(dev, "S/N: %s, E/C: %s\n",
- adap->params.vpd.sn, adap->params.vpd.ec);
+ netdev_info(dev, "S/N: %s, P/N: %s\n",
+ adap->params.vpd.sn, adap->params.vpd.pn);
}
static void enable_pcie_relaxed_ordering(struct pci_dev *dev)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
index 4dd0a82533e4..e274a047528f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
@@ -253,6 +253,7 @@ struct cxgb4_lld_info {
/* packet data */
bool enable_fw_ofld_conn; /* Enable connection through fw */
/* WR */
+ bool ulptx_memwrite_dsgl; /* use of T5 DSGL allowed */
};
struct cxgb4_uld_info {
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index 47ffa64fcf19..ca95cf2954eb 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -93,6 +93,16 @@
*/
#define TX_QCHECK_PERIOD (HZ / 2)
+/* SGE Hung Ingress DMA Threshold Warning time (in Hz) and Warning Repeat Rate
+ * (in RX_QCHECK_PERIOD multiples). If we find one of the SGE Ingress DMA
+ * State Machines in the same state for this amount of time (in HZ) then we'll
+ * issue a warning about a potential hang. We'll repeat the warning as the
+ * SGE Ingress DMA Channel appears to be hung every N RX_QCHECK_PERIODs till
+ * the situation clears. If the situation clears, we'll note that as well.
+ */
+#define SGE_IDMA_WARN_THRESH (1 * HZ)
+#define SGE_IDMA_WARN_REPEAT (20 * RX_QCHECK_PERIOD)
+
/*
* Max number of Tx descriptors to be reclaimed by the Tx timer.
*/
@@ -373,7 +383,7 @@ static void free_tx_desc(struct adapter *adap, struct sge_txq *q,
if (d->skb) { /* an SGL is present */
if (unmap)
unmap_sgl(dev, d->skb, d->sgl, q);
- kfree_skb(d->skb);
+ dev_consume_skb_any(d->skb);
d->skb = NULL;
}
++d;
@@ -706,11 +716,17 @@ static inline unsigned int flits_to_desc(unsigned int n)
* @skb: the packet
*
* Returns whether an Ethernet packet is small enough to fit as
- * immediate data.
+ * immediate data. Return value corresponds to headroom required.
*/
static inline int is_eth_imm(const struct sk_buff *skb)
{
- return skb->len <= MAX_IMM_TX_PKT_LEN - sizeof(struct cpl_tx_pkt);
+ int hdrlen = skb_shinfo(skb)->gso_size ?
+ sizeof(struct cpl_tx_pkt_lso_core) : 0;
+
+ hdrlen += sizeof(struct cpl_tx_pkt);
+ if (skb->len <= MAX_IMM_TX_PKT_LEN - hdrlen)
+ return hdrlen;
+ return 0;
}
/**
@@ -723,9 +739,10 @@ static inline int is_eth_imm(const struct sk_buff *skb)
static inline unsigned int calc_tx_flits(const struct sk_buff *skb)
{
unsigned int flits;
+ int hdrlen = is_eth_imm(skb);
- if (is_eth_imm(skb))
- return DIV_ROUND_UP(skb->len + sizeof(struct cpl_tx_pkt), 8);
+ if (hdrlen)
+ return DIV_ROUND_UP(skb->len + hdrlen, sizeof(__be64));
flits = sgl_len(skb_shinfo(skb)->nr_frags + 1) + 4;
if (skb_shinfo(skb)->gso_size)
@@ -843,9 +860,10 @@ static void cxgb_pio_copy(u64 __iomem *dst, u64 *src)
static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n)
{
unsigned int *wr, index;
+ unsigned long flags;
wmb(); /* write descriptors before telling HW */
- spin_lock(&q->db_lock);
+ spin_lock_irqsave(&q->db_lock, flags);
if (!q->db_disabled) {
if (is_t4(adap->params.chip)) {
t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL),
@@ -861,9 +879,10 @@ static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n)
writel(n, adap->bar2 + q->udb + 8);
wmb();
}
- }
+ } else
+ q->db_pidx_inc += n;
q->db_pidx = q->pidx;
- spin_unlock(&q->db_lock);
+ spin_unlock_irqrestore(&q->db_lock, flags);
}
/**
@@ -971,6 +990,7 @@ static inline void txq_advance(struct sge_txq *q, unsigned int n)
*/
netdev_tx_t t4_eth_xmit(struct sk_buff *skb, struct net_device *dev)
{
+ int len;
u32 wr_mid;
u64 cntrl, *end;
int qidx, credits;
@@ -982,13 +1002,14 @@ netdev_tx_t t4_eth_xmit(struct sk_buff *skb, struct net_device *dev)
struct cpl_tx_pkt_core *cpl;
const struct skb_shared_info *ssi;
dma_addr_t addr[MAX_SKB_FRAGS + 1];
+ bool immediate = false;
/*
* The chip min packet length is 10 octets but play safe and reject
* anything shorter than an Ethernet header.
*/
if (unlikely(skb->len < ETH_HLEN)) {
-out_free: dev_kfree_skb(skb);
+out_free: dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -1011,7 +1032,10 @@ out_free: dev_kfree_skb(skb);
return NETDEV_TX_BUSY;
}
- if (!is_eth_imm(skb) &&
+ if (is_eth_imm(skb))
+ immediate = true;
+
+ if (!immediate &&
unlikely(map_skb(adap->pdev_dev, skb, addr) < 0)) {
q->mapping_err++;
goto out_free;
@@ -1028,6 +1052,7 @@ out_free: dev_kfree_skb(skb);
wr->r3 = cpu_to_be64(0);
end = (u64 *)wr + flits;
+ len = immediate ? skb->len : 0;
ssi = skb_shinfo(skb);
if (ssi->gso_size) {
struct cpl_tx_pkt_lso *lso = (void *)wr;
@@ -1035,8 +1060,9 @@ out_free: dev_kfree_skb(skb);
int l3hdr_len = skb_network_header_len(skb);
int eth_xtra_len = skb_network_offset(skb) - ETH_HLEN;
+ len += sizeof(*lso);
wr->op_immdlen = htonl(FW_WR_OP(FW_ETH_TX_PKT_WR) |
- FW_WR_IMMDLEN(sizeof(*lso)));
+ FW_WR_IMMDLEN(len));
lso->c.lso_ctrl = htonl(LSO_OPCODE(CPL_TX_PKT_LSO) |
LSO_FIRST_SLICE | LSO_LAST_SLICE |
LSO_IPV6(v6) |
@@ -1054,9 +1080,7 @@ out_free: dev_kfree_skb(skb);
q->tso++;
q->tx_cso += ssi->gso_segs;
} else {
- int len;
-
- len = is_eth_imm(skb) ? skb->len + sizeof(*cpl) : sizeof(*cpl);
+ len += sizeof(*cpl);
wr->op_immdlen = htonl(FW_WR_OP(FW_ETH_TX_PKT_WR) |
FW_WR_IMMDLEN(len));
cpl = (void *)(wr + 1);
@@ -1078,9 +1102,9 @@ out_free: dev_kfree_skb(skb);
cpl->len = htons(skb->len);
cpl->ctrl1 = cpu_to_be64(cntrl);
- if (is_eth_imm(skb)) {
+ if (immediate) {
inline_tx_skb(skb, &q->q, cpl + 1);
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
} else {
int last_desc;
@@ -1467,8 +1491,12 @@ static inline int ofld_send(struct adapter *adap, struct sk_buff *skb)
{
unsigned int idx = skb_txq(skb);
- if (unlikely(is_ctrl_pkt(skb)))
+ if (unlikely(is_ctrl_pkt(skb))) {
+ /* Single ctrl queue is a requirement for LE workaround path */
+ if (adap->tids.nsftids)
+ idx = 0;
return ctrl_xmit(&adap->sge.ctrlq[idx], skb);
+ }
return ofld_xmit(&adap->sge.ofldtxq[idx], skb);
}
@@ -1992,7 +2020,7 @@ irq_handler_t t4_intr_handler(struct adapter *adap)
static void sge_rx_timer_cb(unsigned long data)
{
unsigned long m;
- unsigned int i, cnt[2];
+ unsigned int i, idma_same_state_cnt[2];
struct adapter *adap = (struct adapter *)data;
struct sge *s = &adap->sge;
@@ -2015,21 +2043,64 @@ static void sge_rx_timer_cb(unsigned long data)
}
t4_write_reg(adap, SGE_DEBUG_INDEX, 13);
- cnt[0] = t4_read_reg(adap, SGE_DEBUG_DATA_HIGH);
- cnt[1] = t4_read_reg(adap, SGE_DEBUG_DATA_LOW);
-
- for (i = 0; i < 2; i++)
- if (cnt[i] >= s->starve_thres) {
- if (s->idma_state[i] || cnt[i] == 0xffffffff)
- continue;
- s->idma_state[i] = 1;
- t4_write_reg(adap, SGE_DEBUG_INDEX, 11);
- m = t4_read_reg(adap, SGE_DEBUG_DATA_LOW) >> (i * 16);
- dev_warn(adap->pdev_dev,
- "SGE idma%u starvation detected for "
- "queue %lu\n", i, m & 0xffff);
- } else if (s->idma_state[i])
- s->idma_state[i] = 0;
+ idma_same_state_cnt[0] = t4_read_reg(adap, SGE_DEBUG_DATA_HIGH);
+ idma_same_state_cnt[1] = t4_read_reg(adap, SGE_DEBUG_DATA_LOW);
+
+ for (i = 0; i < 2; i++) {
+ u32 debug0, debug11;
+
+ /* If the Ingress DMA Same State Counter ("timer") is less
+ * than 1s, then we can reset our synthesized Stall Timer and
+ * continue. If we have previously emitted warnings about a
+ * potential stalled Ingress Queue, issue a note indicating
+ * that the Ingress Queue has resumed forward progress.
+ */
+ if (idma_same_state_cnt[i] < s->idma_1s_thresh) {
+ if (s->idma_stalled[i] >= SGE_IDMA_WARN_THRESH)
+ CH_WARN(adap, "SGE idma%d, queue%u,resumed after %d sec\n",
+ i, s->idma_qid[i],
+ s->idma_stalled[i]/HZ);
+ s->idma_stalled[i] = 0;
+ continue;
+ }
+
+ /* Synthesize an SGE Ingress DMA Same State Timer in the Hz
+ * domain. The first time we get here it'll be because we
+ * passed the 1s Threshold; each additional time it'll be
+ * because the RX Timer Callback is being fired on its regular
+ * schedule.
+ *
+ * If the stall is below our Potential Hung Ingress Queue
+ * Warning Threshold, continue.
+ */
+ if (s->idma_stalled[i] == 0)
+ s->idma_stalled[i] = HZ;
+ else
+ s->idma_stalled[i] += RX_QCHECK_PERIOD;
+
+ if (s->idma_stalled[i] < SGE_IDMA_WARN_THRESH)
+ continue;
+
+ /* We'll issue a warning every SGE_IDMA_WARN_REPEAT Hz */
+ if (((s->idma_stalled[i] - HZ) % SGE_IDMA_WARN_REPEAT) != 0)
+ continue;
+
+ /* Read and save the SGE IDMA State and Queue ID information.
+ * We do this every time in case it changes across time ...
+ */
+ t4_write_reg(adap, SGE_DEBUG_INDEX, 0);
+ debug0 = t4_read_reg(adap, SGE_DEBUG_DATA_LOW);
+ s->idma_state[i] = (debug0 >> (i * 9)) & 0x3f;
+
+ t4_write_reg(adap, SGE_DEBUG_INDEX, 11);
+ debug11 = t4_read_reg(adap, SGE_DEBUG_DATA_LOW);
+ s->idma_qid[i] = (debug11 >> (i * 16)) & 0xffff;
+
+ CH_WARN(adap, "SGE idma%u, queue%u, maybe stuck state%u %dsecs (debug0=%#x, debug11=%#x)\n",
+ i, s->idma_qid[i], s->idma_state[i],
+ s->idma_stalled[i]/HZ, debug0, debug11);
+ t4_sge_decode_idma_state(adap, s->idma_state[i]);
+ }
mod_timer(&s->rx_timer, jiffies + RX_QCHECK_PERIOD);
}
@@ -2580,11 +2651,19 @@ static int t4_sge_init_soft(struct adapter *adap)
fl_small_mtu = READ_FL_BUF(RX_SMALL_MTU_BUF);
fl_large_mtu = READ_FL_BUF(RX_LARGE_MTU_BUF);
+ /* We only bother using the Large Page logic if the Large Page Buffer
+ * is larger than our Page Size Buffer.
+ */
+ if (fl_large_pg <= fl_small_pg)
+ fl_large_pg = 0;
+
#undef READ_FL_BUF
+ /* The Page Size Buffer must be exactly equal to our Page Size and the
+ * Large Page Size Buffer should be 0 (per above) or a power of 2.
+ */
if (fl_small_pg != PAGE_SIZE ||
- (fl_large_pg != 0 && (fl_large_pg < fl_small_pg ||
- (fl_large_pg & (fl_large_pg-1)) != 0))) {
+ (fl_large_pg & (fl_large_pg-1)) != 0) {
dev_err(adap->pdev_dev, "bad SGE FL page buffer sizes [%d, %d]\n",
fl_small_pg, fl_large_pg);
return -EINVAL;
@@ -2699,8 +2778,8 @@ static int t4_sge_init_hard(struct adapter *adap)
int t4_sge_init(struct adapter *adap)
{
struct sge *s = &adap->sge;
- u32 sge_control;
- int ret;
+ u32 sge_control, sge_conm_ctrl;
+ int ret, egress_threshold;
/*
* Ingress Padding Boundary and Egress Status Page Size are set up by
@@ -2725,15 +2804,24 @@ int t4_sge_init(struct adapter *adap)
* SGE's Egress Congestion Threshold. If it isn't, then we can get
* stuck waiting for new packets while the SGE is waiting for us to
* give it more Free List entries. (Note that the SGE's Egress
- * Congestion Threshold is in units of 2 Free List pointers.)
+ * Congestion Threshold is in units of 2 Free List pointers.) For T4,
+ * there was only a single field to control this. For T5 there's the
+ * original field which now only applies to Unpacked Mode Free List
+ * buffers and a new field which only applies to Packed Mode Free List
+ * buffers.
*/
- s->fl_starve_thres
- = EGRTHRESHOLD_GET(t4_read_reg(adap, SGE_CONM_CTRL))*2 + 1;
+ sge_conm_ctrl = t4_read_reg(adap, SGE_CONM_CTRL);
+ if (is_t4(adap->params.chip))
+ egress_threshold = EGRTHRESHOLD_GET(sge_conm_ctrl);
+ else
+ egress_threshold = EGRTHRESHOLDPACKING_GET(sge_conm_ctrl);
+ s->fl_starve_thres = 2*egress_threshold + 1;
setup_timer(&s->rx_timer, sge_rx_timer_cb, (unsigned long)adap);
setup_timer(&s->tx_timer, sge_tx_timer_cb, (unsigned long)adap);
- s->starve_thres = core_ticks_per_usec(adap) * 1000000; /* 1 s */
- s->idma_state[0] = s->idma_state[1] = 0;
+ s->idma_1s_thresh = core_ticks_per_usec(adap) * 1000000; /* 1 s */
+ s->idma_stalled[0] = 0;
+ s->idma_stalled[1] = 0;
spin_lock_init(&s->intrq_lock);
return 0;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index 2c109343d570..fb2fe65903c2 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -573,7 +573,7 @@ int get_vpd_params(struct adapter *adapter, struct vpd_params *p)
{
u32 cclk_param, cclk_val;
int i, ret, addr;
- int ec, sn;
+ int ec, sn, pn;
u8 *vpd, csum;
unsigned int vpdr_len, kw_offset, id_len;
@@ -638,6 +638,7 @@ int get_vpd_params(struct adapter *adapter, struct vpd_params *p)
FIND_VPD_KW(ec, "EC");
FIND_VPD_KW(sn, "SN");
+ FIND_VPD_KW(pn, "PN");
#undef FIND_VPD_KW
memcpy(p->id, vpd + PCI_VPD_LRDT_TAG_SIZE, id_len);
@@ -647,6 +648,8 @@ int get_vpd_params(struct adapter *adapter, struct vpd_params *p)
i = pci_vpd_info_field_size(vpd + sn - PCI_VPD_INFO_FLD_HDR_SIZE);
memcpy(p->sn, vpd + sn, min(i, SERNUM_LEN));
strim(p->sn);
+ memcpy(p->pn, vpd + pn, min(i, PN_LEN));
+ strim(p->pn);
/*
* Ask firmware for the Core Clock since it knows how to translate the
@@ -1155,7 +1158,8 @@ out:
}
#define ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\
- FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_ANEG)
+ FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_SPEED_40G | \
+ FW_PORT_CAP_ANEG)
/**
* t4_link_start - apply link configuration to MAC/PHY
@@ -2247,6 +2251,36 @@ static unsigned int get_mps_bg_map(struct adapter *adap, int idx)
}
/**
+ * t4_get_port_type_description - return Port Type string description
+ * @port_type: firmware Port Type enumeration
+ */
+const char *t4_get_port_type_description(enum fw_port_type port_type)
+{
+ static const char *const port_type_description[] = {
+ "R XFI",
+ "R XAUI",
+ "T SGMII",
+ "T XFI",
+ "T XAUI",
+ "KX4",
+ "CX4",
+ "KX",
+ "KR",
+ "R SFP+",
+ "KR/KX",
+ "KR/KX/KX4",
+ "R QSFP_10G",
+ "",
+ "R QSFP",
+ "R BP40_BA",
+ };
+
+ if (port_type < ARRAY_SIZE(port_type_description))
+ return port_type_description[port_type];
+ return "UNKNOWN";
+}
+
+/**
* t4_get_port_stats - collect port statistics
* @adap: the adapter
* @idx: the port index
@@ -2563,6 +2597,112 @@ int t4_mdio_wr(struct adapter *adap, unsigned int mbox, unsigned int phy_addr,
}
/**
+ * t4_sge_decode_idma_state - decode the idma state
+ * @adap: the adapter
+ * @state: the state idma is stuck in
+ */
+void t4_sge_decode_idma_state(struct adapter *adapter, int state)
+{
+ static const char * const t4_decode[] = {
+ "IDMA_IDLE",
+ "IDMA_PUSH_MORE_CPL_FIFO",
+ "IDMA_PUSH_CPL_MSG_HEADER_TO_FIFO",
+ "Not used",
+ "IDMA_PHYSADDR_SEND_PCIEHDR",
+ "IDMA_PHYSADDR_SEND_PAYLOAD_FIRST",
+ "IDMA_PHYSADDR_SEND_PAYLOAD",
+ "IDMA_SEND_FIFO_TO_IMSG",
+ "IDMA_FL_REQ_DATA_FL_PREP",
+ "IDMA_FL_REQ_DATA_FL",
+ "IDMA_FL_DROP",
+ "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",
+ "IDMA_FL_SEND_FIFO_TO_IMSG",
+ "IDMA_FL_REQ_DATAFL_DONE",
+ "IDMA_FL_REQ_HEADERFL_DONE",
+ };
+ static const char * const t5_decode[] = {
+ "IDMA_IDLE",
+ "IDMA_ALMOST_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_SEND_FIFO_TO_IMSG",
+ "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,
+ SGE_DEBUG_DATA_LOW_INDEX_3,
+ SGE_DEBUG_DATA_HIGH_INDEX_10,
+ };
+ const char **sge_idma_decode;
+ int sge_idma_decode_nstates;
+ int i;
+
+ if (is_t4(adapter->params.chip)) {
+ sge_idma_decode = (const char **)t4_decode;
+ sge_idma_decode_nstates = ARRAY_SIZE(t4_decode);
+ } else {
+ sge_idma_decode = (const char **)t5_decode;
+ sge_idma_decode_nstates = ARRAY_SIZE(t5_decode);
+ }
+
+ if (state < sge_idma_decode_nstates)
+ CH_WARN(adapter, "idma state %s\n", sge_idma_decode[state]);
+ else
+ CH_WARN(adapter, "idma state %d unknown\n", state);
+
+ for (i = 0; i < ARRAY_SIZE(sge_regs); i++)
+ CH_WARN(adapter, "SGE register %#x value %#x\n",
+ sge_regs[i], t4_read_reg(adapter, sge_regs[i]));
+}
+
+/**
* t4_fw_hello - establish communication with FW
* @adap: the adapter
* @mbox: mailbox to use for the FW command
@@ -3533,11 +3673,13 @@ int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl)
if (stat & FW_PORT_CMD_TXPAUSE)
fc |= PAUSE_TX;
if (stat & FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_100M))
- speed = SPEED_100;
+ speed = 100;
else if (stat & FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_1G))
- speed = SPEED_1000;
+ speed = 1000;
else if (stat & FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_10G))
- speed = SPEED_10000;
+ speed = 10000;
+ else if (stat & FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_40G))
+ speed = 40000;
if (link_ok != lc->link_ok || speed != lc->speed ||
fc != lc->fc) { /* something changed */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
index cd6874b571ee..f2738c710789 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
@@ -116,6 +116,7 @@ enum CPL_error {
CPL_ERR_KEEPALIVE_TIMEDOUT = 34,
CPL_ERR_RTX_NEG_ADVICE = 35,
CPL_ERR_PERSIST_NEG_ADVICE = 36,
+ CPL_ERR_KEEPALV_NEG_ADVICE = 37,
CPL_ERR_ABORT_FAILED = 42,
CPL_ERR_IWARP_FLM = 50,
};
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
index 4082522d8140..225ad8a5722d 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
@@ -230,6 +230,12 @@
#define EGRTHRESHOLD(x) ((x) << EGRTHRESHOLDshift)
#define EGRTHRESHOLD_GET(x) (((x) & EGRTHRESHOLD_MASK) >> EGRTHRESHOLDshift)
+#define EGRTHRESHOLDPACKING_MASK 0x3fU
+#define EGRTHRESHOLDPACKING_SHIFT 14
+#define EGRTHRESHOLDPACKING(x) ((x) << EGRTHRESHOLDPACKING_SHIFT)
+#define EGRTHRESHOLDPACKING_GET(x) (((x) >> EGRTHRESHOLDPACKING_SHIFT) & \
+ EGRTHRESHOLDPACKING_MASK)
+
#define SGE_DBFIFO_STATUS 0x10a4
#define HP_INT_THRESH_SHIFT 28
#define HP_INT_THRESH_MASK 0xfU
@@ -278,6 +284,9 @@
#define SGE_DEBUG_INDEX 0x10cc
#define SGE_DEBUG_DATA_HIGH 0x10d0
#define SGE_DEBUG_DATA_LOW 0x10d4
+#define SGE_DEBUG_DATA_LOW_INDEX_2 0x12c8
+#define SGE_DEBUG_DATA_LOW_INDEX_3 0x12cc
+#define SGE_DEBUG_DATA_HIGH_INDEX_10 0x12a8
#define SGE_INGRESS_QUEUES_PER_PAGE_PF 0x10f4
#define S_HP_INT_THRESH 28
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
index 74fea74ce0aa..9cc973fbcf26 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
@@ -932,6 +932,7 @@ enum fw_params_param_dev {
FW_PARAMS_PARAM_DEV_FWREV = 0x0B,
FW_PARAMS_PARAM_DEV_TPREV = 0x0C,
FW_PARAMS_PARAM_DEV_CF = 0x0D,
+ FW_PARAMS_PARAM_DEV_ULPTX_MEMWRITE_DSGL = 0x17,
};
/*
@@ -1742,6 +1743,9 @@ enum fw_port_type {
FW_PORT_TYPE_SFP,
FW_PORT_TYPE_BP_AP,
FW_PORT_TYPE_BP4_AP,
+ FW_PORT_TYPE_QSFP_10G,
+ FW_PORT_TYPE_QSFP,
+ FW_PORT_TYPE_BP40_BA,
FW_PORT_TYPE_NONE = FW_PORT_CMD_PTYPE_MASK
};
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
index 0899c0983594..52859288de7b 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
@@ -2444,7 +2444,7 @@ static void reduce_ethqs(struct adapter *adapter, int n)
*/
static int enable_msix(struct adapter *adapter)
{
- int i, err, want, need;
+ int i, want, need, nqsets;
struct msix_entry entries[MSIX_ENTRIES];
struct sge *s = &adapter->sge;
@@ -2460,26 +2460,23 @@ static int enable_msix(struct adapter *adapter)
*/
want = s->max_ethqsets + MSIX_EXTRAS;
need = adapter->params.nports + MSIX_EXTRAS;
- while ((err = pci_enable_msix(adapter->pdev, entries, want)) >= need)
- want = err;
- if (err == 0) {
- int nqsets = want - MSIX_EXTRAS;
- if (nqsets < s->max_ethqsets) {
- dev_warn(adapter->pdev_dev, "only enough MSI-X vectors"
- " for %d Queue Sets\n", nqsets);
- s->max_ethqsets = nqsets;
- if (nqsets < s->ethqsets)
- reduce_ethqs(adapter, nqsets);
- }
- for (i = 0; i < want; ++i)
- adapter->msix_info[i].vec = entries[i].vector;
- } else if (err > 0) {
- pci_disable_msix(adapter->pdev);
- dev_info(adapter->pdev_dev, "only %d MSI-X vectors left,"
- " not using MSI-X\n", err);
+ want = pci_enable_msix_range(adapter->pdev, entries, need, want);
+ if (want < 0)
+ return want;
+
+ nqsets = want - MSIX_EXTRAS;
+ if (nqsets < s->max_ethqsets) {
+ dev_warn(adapter->pdev_dev, "only enough MSI-X vectors"
+ " for %d Queue Sets\n", nqsets);
+ s->max_ethqsets = nqsets;
+ if (nqsets < s->ethqsets)
+ reduce_ethqs(adapter, nqsets);
}
- return err;
+ for (i = 0; i < want; ++i)
+ adapter->msix_info[i].vec = entries[i].vector;
+
+ return 0;
}
static const struct net_device_ops cxgb4vf_netdev_ops = {
@@ -2947,6 +2944,14 @@ static DEFINE_PCI_DEVICE_TABLE(cxgb4vf_pci_tbl) = {
CH_DEVICE(0x5811, 0), /* T520-lp-cr */
CH_DEVICE(0x5812, 0), /* T560-cr */
CH_DEVICE(0x5813, 0), /* T580-cr */
+ CH_DEVICE(0x5814, 0), /* T580-so-cr */
+ CH_DEVICE(0x5815, 0), /* T502-bt */
+ CH_DEVICE(0x5880, 0),
+ CH_DEVICE(0x5881, 0),
+ CH_DEVICE(0x5882, 0),
+ CH_DEVICE(0x5883, 0),
+ CH_DEVICE(0x5884, 0),
+ CH_DEVICE(0x5885, 0),
{ 0, }
};
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
index 0a89963c48ce..9cfa4b4bb089 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
@@ -401,7 +401,7 @@ static void free_tx_desc(struct adapter *adapter, struct sge_txq *tq,
if (sdesc->skb) {
if (need_unmap)
unmap_sgl(dev, sdesc->skb, sdesc->sgl, tq);
- kfree_skb(sdesc->skb);
+ dev_consume_skb_any(sdesc->skb);
sdesc->skb = NULL;
}
@@ -1275,7 +1275,7 @@ int t4vf_eth_xmit(struct sk_buff *skb, struct net_device *dev)
* need it any longer.
*/
inline_tx_skb(skb, &txq->q, cpl + 1);
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
} else {
/*
* Write the skb's Scatter/Gather list into the TX Packet CPL
@@ -1354,7 +1354,7 @@ out_free:
* An error of some sort happened. Free the TX skb and tell the
* OS that we've "dealt" with the packet ...
*/
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c
index 19f642a45f40..fe84fbabc0d4 100644
--- a/drivers/net/ethernet/cirrus/cs89x0.c
+++ b/drivers/net/ethernet/cirrus/cs89x0.c
@@ -1174,7 +1174,7 @@ static netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev)
writewords(lp, TX_FRAME_PORT, skb->data, (skb->len + 1) >> 1);
spin_unlock_irqrestore(&lp->lock, flags);
dev->stats.tx_bytes += skb->len;
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
/* We DO NOT call netif_wake_queue() here.
* We also DO NOT call netif_start_queue().
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index b740bfce72ef..2945718ce806 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -521,7 +521,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,
unsigned int txq_map;
if (skb->len <= 0) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -536,7 +536,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb,
if (skb_shinfo(skb)->gso_size == 0 &&
skb_shinfo(skb)->nr_frags + 1 > ENIC_NON_TSO_MAX_DESC &&
skb_linearize(skb)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -1086,14 +1086,15 @@ static int enic_poll(struct napi_struct *napi, int budget)
unsigned int intr = enic_legacy_io_intr();
unsigned int rq_work_to_do = budget;
unsigned int wq_work_to_do = -1; /* no limit */
- unsigned int work_done, rq_work_done, wq_work_done;
+ unsigned int work_done, rq_work_done = 0, wq_work_done;
int err;
/* Service RQ (first) and WQ
*/
- rq_work_done = vnic_cq_service(&enic->cq[cq_rq],
- rq_work_to_do, enic_rq_service, NULL);
+ if (budget > 0)
+ rq_work_done = vnic_cq_service(&enic->cq[cq_rq],
+ rq_work_to_do, enic_rq_service, NULL);
wq_work_done = vnic_cq_service(&enic->cq[cq_wq],
wq_work_to_do, enic_wq_service, NULL);
@@ -1141,14 +1142,15 @@ static int enic_poll_msix(struct napi_struct *napi, int budget)
unsigned int cq = enic_cq_rq(enic, rq);
unsigned int intr = enic_msix_rq_intr(enic, rq);
unsigned int work_to_do = budget;
- unsigned int work_done;
+ unsigned int work_done = 0;
int err;
/* Service RQ
*/
- work_done = vnic_cq_service(&enic->cq[cq],
- work_to_do, enic_rq_service, NULL);
+ if (budget > 0)
+ work_done = vnic_cq_service(&enic->cq[cq],
+ work_to_do, enic_rq_service, NULL);
/* Return intr event credits for this polling
* cycle. An intr event is the completion of a
@@ -1796,7 +1798,8 @@ static int enic_set_intr_mode(struct enic *enic)
enic->cq_count >= n + m &&
enic->intr_count >= n + m + 2) {
- if (!pci_enable_msix(enic->pdev, enic->msix_entry, n + m + 2)) {
+ if (pci_enable_msix_range(enic->pdev, enic->msix_entry,
+ n + m + 2, n + m + 2) > 0) {
enic->rq_count = n;
enic->wq_count = m;
@@ -1815,7 +1818,8 @@ static int enic_set_intr_mode(struct enic *enic)
enic->wq_count >= m &&
enic->cq_count >= 1 + m &&
enic->intr_count >= 1 + m + 2) {
- if (!pci_enable_msix(enic->pdev, enic->msix_entry, 1 + m + 2)) {
+ if (pci_enable_msix_range(enic->pdev, enic->msix_entry,
+ 1 + m + 2, 1 + m + 2) > 0) {
enic->rq_count = 1;
enic->wq_count = m;
diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c
index a1a2b4028a5c..8c4b93be333b 100644
--- a/drivers/net/ethernet/davicom/dm9000.c
+++ b/drivers/net/ethernet/davicom/dm9000.c
@@ -1033,7 +1033,7 @@ dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
spin_unlock_irqrestore(&db->lock, flags);
/* free this SKB */
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/dec/tulip/dmfe.c b/drivers/net/ethernet/dec/tulip/dmfe.c
index 5ad9e3e3c0b8..53f0c618045c 100644
--- a/drivers/net/ethernet/dec/tulip/dmfe.c
+++ b/drivers/net/ethernet/dec/tulip/dmfe.c
@@ -696,7 +696,7 @@ static netdev_tx_t dmfe_start_xmit(struct sk_buff *skb,
/* Too large packet check */
if (skb->len > MAX_PACKET_SIZE) {
pr_err("big packet = %d\n", (u16)skb->len);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -743,7 +743,7 @@ static netdev_tx_t dmfe_start_xmit(struct sk_buff *skb,
dw32(DCR7, db->cr7_data);
/* free this SKB */
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/dec/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c
index aa4ee385091f..aa801a6af7b9 100644
--- a/drivers/net/ethernet/dec/tulip/uli526x.c
+++ b/drivers/net/ethernet/dec/tulip/uli526x.c
@@ -607,7 +607,7 @@ static netdev_tx_t uli526x_start_xmit(struct sk_buff *skb,
/* Too large packet check */
if (skb->len > MAX_PACKET_SIZE) {
netdev_err(dev, "big packet = %d\n", (u16)skb->len);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -648,7 +648,7 @@ static netdev_tx_t uli526x_start_xmit(struct sk_buff *skb,
uw32(DCR7, db->cr7_data);
/* free this SKB */
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/dlink/sundance.c b/drivers/net/ethernet/dlink/sundance.c
index 113cd799a131..d9e5ca0d48c1 100644
--- a/drivers/net/ethernet/dlink/sundance.c
+++ b/drivers/net/ethernet/dlink/sundance.c
@@ -1137,7 +1137,7 @@ start_tx (struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
drop_frame:
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
np->tx_skbuff[entry] = NULL;
dev->stats.tx_dropped++;
return NETDEV_TX_OK;
diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c
index 8a79a32a5674..e9b0faba3078 100644
--- a/drivers/net/ethernet/dnet.c
+++ b/drivers/net/ethernet/dnet.c
@@ -170,11 +170,6 @@ static int dnet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
return 0;
}
-static int dnet_mdio_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
static void dnet_handle_link_change(struct net_device *dev)
{
struct dnet *bp = netdev_priv(dev);
@@ -322,7 +317,6 @@ static int dnet_mii_init(struct dnet *bp)
bp->mii_bus->name = "dnet_mii_bus";
bp->mii_bus->read = &dnet_mdio_read;
bp->mii_bus->write = &dnet_mdio_write;
- bp->mii_bus->reset = &dnet_mdio_reset;
snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
bp->pdev->name, bp->pdev->id);
diff --git a/drivers/net/ethernet/emulex/benet/Kconfig b/drivers/net/ethernet/emulex/benet/Kconfig
index 231129dd1764..ea94a8eb6b35 100644
--- a/drivers/net/ethernet/emulex/benet/Kconfig
+++ b/drivers/net/ethernet/emulex/benet/Kconfig
@@ -4,3 +4,11 @@ config BE2NET
---help---
This driver implements the NIC functionality for ServerEngines'
10Gbps network adapter - BladeEngine.
+
+config BE2NET_VXLAN
+ bool "VXLAN offload support on be2net driver"
+ default y
+ depends on BE2NET && VXLAN && !(BE2NET=y && VXLAN=m)
+ ---help---
+ Say Y here if you want to enable VXLAN offload support on
+ be2net driver.
diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h
index 05529e273050..8ccaa2520dc3 100644
--- a/drivers/net/ethernet/emulex/benet/be.h
+++ b/drivers/net/ethernet/emulex/benet/be.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 - 2013 Emulex
+ * Copyright (C) 2005 - 2014 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -34,7 +34,7 @@
#include "be_hw.h"
#include "be_roce.h"
-#define DRV_VER "10.0.600.0u"
+#define DRV_VER "10.2u"
#define DRV_NAME "be2net"
#define BE_NAME "Emulex BladeEngine2"
#define BE3_NAME "Emulex BladeEngine3"
@@ -88,7 +88,6 @@ static inline char *nic_name(struct pci_dev *pdev)
#define BE_MIN_MTU 256
#define BE_NUM_VLANS_SUPPORTED 64
-#define BE_UMC_NUM_VLANS_SUPPORTED 15
#define BE_MAX_EQD 128u
#define BE_MAX_TX_FRAG_COUNT 30
@@ -262,9 +261,10 @@ struct be_tx_obj {
/* Struct to remember the pages posted for rx frags */
struct be_rx_page_info {
struct page *page;
+ /* set to page-addr for last frag of the page & frag-addr otherwise */
DEFINE_DMA_UNMAP_ADDR(bus);
u16 page_offset;
- bool last_page_user;
+ bool last_frag; /* last frag of the page */
};
struct be_rx_stats {
@@ -293,9 +293,10 @@ struct be_rx_compl_info {
u8 ip_csum;
u8 l4_csum;
u8 ipv6;
- u8 vtm;
+ u8 qnq;
u8 pkt_type;
u8 ip_frag;
+ u8 tunneled;
};
struct be_rx_obj {
@@ -359,6 +360,7 @@ struct be_vf_cfg {
int pmac_id;
u16 vlan_tag;
u32 tx_rate;
+ u32 plink_tracking;
};
enum vf_state {
@@ -370,10 +372,11 @@ enum vf_state {
#define BE_FLAGS_WORKER_SCHEDULED (1 << 3)
#define BE_FLAGS_VLAN_PROMISC (1 << 4)
#define BE_FLAGS_NAPI_ENABLED (1 << 9)
-#define BE_UC_PMAC_COUNT 30
-#define BE_VF_UC_PMAC_COUNT 2
#define BE_FLAGS_QNQ_ASYNC_EVT_RCVD (1 << 11)
+#define BE_FLAGS_VXLAN_OFFLOADS (1 << 12)
+#define BE_UC_PMAC_COUNT 30
+#define BE_VF_UC_PMAC_COUNT 2
/* Ethtool set_dump flags */
#define LANCER_INITIATE_FW_DUMP 0x1
@@ -467,6 +470,7 @@ struct be_adapter {
u32 port_num;
bool promiscuous;
+ u8 mc_type;
u32 function_mode;
u32 function_caps;
u32 rx_fc; /* Rx flow control */
@@ -492,6 +496,7 @@ struct be_adapter {
u32 sli_family;
u8 hba_port_num;
u16 pvid;
+ __be16 vxlan_port;
struct phy_info phy;
u8 wol_cap;
bool wol_en;
@@ -536,6 +541,14 @@ static inline u16 be_max_qs(struct be_adapter *adapter)
return min_t(u16, num, num_online_cpus());
}
+/* Is BE in pvid_tagging mode */
+#define be_pvid_tagging_enabled(adapter) (adapter->pvid)
+
+/* Is BE in QNQ multi-channel mode */
+#define be_is_qnq_mode(adapter) (adapter->mc_type == FLEX10 || \
+ adapter->mc_type == vNIC1 || \
+ adapter->mc_type == UFP)
+
#define lancer_chip(adapter) (adapter->pdev->device == OC_DEVICE_ID3 || \
adapter->pdev->device == OC_DEVICE_ID4)
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index 48076a6370c3..d1ec15af0d24 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 - 2013 Emulex
+ * Copyright (C) 2005 - 2014 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -202,8 +202,12 @@ static void be_async_link_state_process(struct be_adapter *adapter,
/* When link status changes, link speed must be re-queried from FW */
adapter->phy.link_speed = -1;
- /* Ignore physical link event */
- if (lancer_chip(adapter) &&
+ /* On BEx the FW does not send a separate link status
+ * notification for physical and logical link.
+ * On other chips just process the logical link
+ * status notification
+ */
+ if (!BEx_chip(adapter) &&
!(evt->port_link_status & LOGICAL_LINK_STATUS_MASK))
return;
@@ -211,7 +215,8 @@ static void be_async_link_state_process(struct be_adapter *adapter,
* it may not be received in some cases.
*/
if (adapter->flags & BE_FLAGS_LINK_STATUS_INIT)
- be_link_status_update(adapter, evt->port_link_status);
+ be_link_status_update(adapter,
+ evt->port_link_status & LINK_STATUS_MASK);
}
/* Grp5 CoS Priority evt */
@@ -239,10 +244,12 @@ static void be_async_grp5_qos_speed_process(struct be_adapter *adapter,
static void be_async_grp5_pvid_state_process(struct be_adapter *adapter,
struct be_async_event_grp5_pvid_state *evt)
{
- if (evt->enabled)
+ if (evt->enabled) {
adapter->pvid = le16_to_cpu(evt->tag) & VLAN_VID_MASK;
- else
+ dev_info(&adapter->pdev->dev, "LPVID: %d\n", adapter->pvid);
+ } else {
adapter->pvid = 0;
+ }
}
static void be_async_grp5_evt_process(struct be_adapter *adapter,
@@ -3296,6 +3303,21 @@ static struct be_pcie_res_desc *be_get_pcie_desc(u8 devfn, u8 *buf,
return NULL;
}
+static struct be_port_res_desc *be_get_port_desc(u8 *buf, u32 desc_count)
+{
+ struct be_res_desc_hdr *hdr = (struct be_res_desc_hdr *)buf;
+ int i;
+
+ for (i = 0; i < desc_count; i++) {
+ if (hdr->desc_type == PORT_RESOURCE_DESC_TYPE_V1)
+ return (struct be_port_res_desc *)hdr;
+
+ hdr->desc_len = hdr->desc_len ? : RESOURCE_DESC_SIZE_V0;
+ hdr = (void *)hdr + hdr->desc_len;
+ }
+ return NULL;
+}
+
static void be_copy_nic_desc(struct be_resources *res,
struct be_nic_res_desc *desc)
{
@@ -3439,6 +3461,7 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
{
struct be_cmd_resp_get_profile_config *resp;
struct be_pcie_res_desc *pcie;
+ struct be_port_res_desc *port;
struct be_nic_res_desc *nic;
struct be_queue_info *mccq = &adapter->mcc_obj.q;
struct be_dma_mem cmd;
@@ -3466,6 +3489,10 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
if (pcie)
res->max_vfs = le16_to_cpu(pcie->num_vfs);
+ port = be_get_port_desc(resp->func_param, desc_count);
+ if (port)
+ adapter->mc_type = port->mc_type;
+
nic = be_get_nic_desc(resp->func_param, desc_count);
if (nic)
be_copy_nic_desc(res, nic);
@@ -3476,14 +3503,11 @@ err:
return status;
}
-/* Currently only Lancer uses this command and it supports version 0 only
- * Uses sync mcc
- */
-int be_cmd_set_profile_config(struct be_adapter *adapter, u32 bps,
- u8 domain)
+int be_cmd_set_profile_config(struct be_adapter *adapter, void *desc,
+ int size, u8 version, u8 domain)
{
- struct be_mcc_wrb *wrb;
struct be_cmd_req_set_profile_config *req;
+ struct be_mcc_wrb *wrb;
int status;
spin_lock_bh(&adapter->mcc_lock);
@@ -3495,44 +3519,116 @@ int be_cmd_set_profile_config(struct be_adapter *adapter, u32 bps,
}
req = embedded_payload(wrb);
-
be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_SET_PROFILE_CONFIG, sizeof(*req),
wrb, NULL);
+ req->hdr.version = version;
req->hdr.domain = domain;
req->desc_count = cpu_to_le32(1);
- req->nic_desc.hdr.desc_type = NIC_RESOURCE_DESC_TYPE_V0;
- req->nic_desc.hdr.desc_len = RESOURCE_DESC_SIZE_V0;
- req->nic_desc.flags = (1 << QUN) | (1 << IMM) | (1 << NOSV);
- req->nic_desc.pf_num = adapter->pf_number;
- req->nic_desc.vf_num = domain;
-
- /* Mark fields invalid */
- req->nic_desc.unicast_mac_count = 0xFFFF;
- req->nic_desc.mcc_count = 0xFFFF;
- req->nic_desc.vlan_count = 0xFFFF;
- req->nic_desc.mcast_mac_count = 0xFFFF;
- req->nic_desc.txq_count = 0xFFFF;
- req->nic_desc.rq_count = 0xFFFF;
- req->nic_desc.rssq_count = 0xFFFF;
- req->nic_desc.lro_count = 0xFFFF;
- req->nic_desc.cq_count = 0xFFFF;
- req->nic_desc.toe_conn_count = 0xFFFF;
- req->nic_desc.eq_count = 0xFFFF;
- req->nic_desc.link_param = 0xFF;
- req->nic_desc.bw_min = 0xFFFFFFFF;
- req->nic_desc.acpi_params = 0xFF;
- req->nic_desc.wol_param = 0x0F;
-
- /* Change BW */
- req->nic_desc.bw_min = cpu_to_le32(bps);
- req->nic_desc.bw_max = cpu_to_le32(bps);
+ memcpy(req->desc, desc, size);
+
status = be_mcc_notify_wait(adapter);
err:
spin_unlock_bh(&adapter->mcc_lock);
return status;
}
+/* Mark all fields invalid */
+void be_reset_nic_desc(struct be_nic_res_desc *nic)
+{
+ memset(nic, 0, sizeof(*nic));
+ nic->unicast_mac_count = 0xFFFF;
+ nic->mcc_count = 0xFFFF;
+ nic->vlan_count = 0xFFFF;
+ nic->mcast_mac_count = 0xFFFF;
+ nic->txq_count = 0xFFFF;
+ nic->rq_count = 0xFFFF;
+ nic->rssq_count = 0xFFFF;
+ nic->lro_count = 0xFFFF;
+ nic->cq_count = 0xFFFF;
+ nic->toe_conn_count = 0xFFFF;
+ nic->eq_count = 0xFFFF;
+ nic->link_param = 0xFF;
+ nic->acpi_params = 0xFF;
+ nic->wol_param = 0x0F;
+ nic->bw_min = 0xFFFFFFFF;
+ nic->bw_max = 0xFFFFFFFF;
+}
+
+int be_cmd_config_qos(struct be_adapter *adapter, u32 bps, u8 domain)
+{
+ if (lancer_chip(adapter)) {
+ struct be_nic_res_desc nic_desc;
+
+ be_reset_nic_desc(&nic_desc);
+ nic_desc.hdr.desc_type = NIC_RESOURCE_DESC_TYPE_V0;
+ nic_desc.hdr.desc_len = RESOURCE_DESC_SIZE_V0;
+ nic_desc.flags = (1 << QUN_SHIFT) | (1 << IMM_SHIFT) |
+ (1 << NOSV_SHIFT);
+ nic_desc.pf_num = adapter->pf_number;
+ nic_desc.vf_num = domain;
+ nic_desc.bw_max = cpu_to_le32(bps);
+
+ return be_cmd_set_profile_config(adapter, &nic_desc,
+ RESOURCE_DESC_SIZE_V0,
+ 0, domain);
+ } else {
+ return be_cmd_set_qos(adapter, bps, domain);
+ }
+}
+
+int be_cmd_manage_iface(struct be_adapter *adapter, u32 iface, u8 op)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_manage_iface_filters *req;
+ int status;
+
+ if (iface == 0xFFFFFFFF)
+ return -1;
+
+ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+ req = embedded_payload(wrb);
+
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MANAGE_IFACE_FILTERS, sizeof(*req),
+ wrb, NULL);
+ req->op = op;
+ req->target_iface_id = cpu_to_le32(iface);
+
+ status = be_mcc_notify_wait(adapter);
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+}
+
+int be_cmd_set_vxlan_port(struct be_adapter *adapter, __be16 port)
+{
+ struct be_port_res_desc port_desc;
+
+ memset(&port_desc, 0, sizeof(port_desc));
+ port_desc.hdr.desc_type = PORT_RESOURCE_DESC_TYPE_V1;
+ port_desc.hdr.desc_len = RESOURCE_DESC_SIZE_V1;
+ port_desc.flags = (1 << IMM_SHIFT) | (1 << NOSV_SHIFT);
+ port_desc.link_num = adapter->hba_port_num;
+ if (port) {
+ port_desc.nv_flags = NV_TYPE_VXLAN | (1 << SOCVID_SHIFT) |
+ (1 << RCVID_SHIFT);
+ port_desc.nv_port = swab16(port);
+ } else {
+ port_desc.nv_flags = NV_TYPE_DISABLED;
+ port_desc.nv_port = 0;
+ }
+
+ return be_cmd_set_profile_config(adapter, &port_desc,
+ RESOURCE_DESC_SIZE_V1, 1, 0);
+}
+
int be_cmd_get_if_id(struct be_adapter *adapter, struct be_vf_cfg *vf_cfg,
int vf_num)
{
@@ -3723,6 +3819,45 @@ err:
return status;
}
+int be_cmd_set_logical_link_config(struct be_adapter *adapter,
+ int link_state, 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 0;
+
+ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+
+ req = embedded_payload(wrb);
+
+ be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_SET_LOGICAL_LINK_CONFIG,
+ sizeof(*req), wrb, NULL);
+
+ req->hdr.version = 1;
+ req->hdr.domain = domain;
+
+ if (link_state == IFLA_VF_LINK_STATE_ENABLE)
+ req->link_config |= 1;
+
+ if (link_state == IFLA_VF_LINK_STATE_AUTO)
+ req->link_config |= 1 << PLINK_TRACK_SHIFT;
+
+ status = be_mcc_notify_wait(adapter);
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
+ 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 fc4e076dc202..b60e4d53c1c9 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.h
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 - 2013 Emulex
+ * Copyright (C) 2005 - 2014 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -203,6 +203,7 @@ struct be_mcc_mailbox {
#define OPCODE_COMMON_GET_BEACON_STATE 70
#define OPCODE_COMMON_READ_TRANSRECV_DATA 73
#define OPCODE_COMMON_GET_PORT_NAME 77
+#define OPCODE_COMMON_SET_LOGICAL_LINK_CONFIG 80
#define OPCODE_COMMON_SET_INTERRUPT_ENABLE 89
#define OPCODE_COMMON_SET_FN_PRIVILEGES 100
#define OPCODE_COMMON_GET_PHY_DETAILS 102
@@ -221,6 +222,7 @@ struct be_mcc_mailbox {
#define OPCODE_COMMON_GET_FN_PRIVILEGES 170
#define OPCODE_COMMON_READ_OBJECT 171
#define OPCODE_COMMON_WRITE_OBJECT 172
+#define OPCODE_COMMON_MANAGE_IFACE_FILTERS 193
#define OPCODE_COMMON_GET_IFACE_LIST 194
#define OPCODE_COMMON_ENABLE_DISABLE_VF 196
@@ -1098,14 +1100,6 @@ struct be_cmd_resp_query_fw_cfg {
u32 function_caps;
};
-/* Is BE in a multi-channel mode */
-static inline bool be_is_mc(struct be_adapter *adapter)
-{
- return adapter->function_mode & FLEX10_MODE ||
- adapter->function_mode & VNIC_MODE ||
- adapter->function_mode & UMC_ENABLED;
-}
-
/******************** RSS Config ****************************************/
/* RSS type Input parameters used to compute RX hash
* RSS_ENABLE_IPV4 SRC IPv4, DST IPv4
@@ -1828,20 +1822,36 @@ struct be_cmd_req_set_ext_fat_caps {
#define NIC_RESOURCE_DESC_TYPE_V0 0x41
#define PCIE_RESOURCE_DESC_TYPE_V1 0x50
#define NIC_RESOURCE_DESC_TYPE_V1 0x51
+#define PORT_RESOURCE_DESC_TYPE_V1 0x55
#define MAX_RESOURCE_DESC 264
-/* QOS unit number */
-#define QUN 4
-/* Immediate */
-#define IMM 6
-/* No save */
-#define NOSV 7
+#define IMM_SHIFT 6 /* Immediate */
+#define NOSV_SHIFT 7 /* No save */
struct be_res_desc_hdr {
u8 desc_type;
u8 desc_len;
} __packed;
+struct be_port_res_desc {
+ struct be_res_desc_hdr hdr;
+ u8 rsvd0;
+ u8 flags;
+ u8 link_num;
+ u8 mc_type;
+ u16 rsvd1;
+
+#define NV_TYPE_MASK 0x3 /* bits 0-1 */
+#define NV_TYPE_DISABLED 1
+#define NV_TYPE_VXLAN 3
+#define SOCVID_SHIFT 2 /* Strip outer vlan */
+#define RCVID_SHIFT 4 /* Report vlan */
+ u8 nv_flags;
+ u8 rsvd2;
+ __le16 nv_port; /* vxlan/gre port */
+ u32 rsvd3[19];
+} __packed;
+
struct be_pcie_res_desc {
struct be_res_desc_hdr hdr;
u8 rsvd0;
@@ -1862,6 +1872,8 @@ struct be_pcie_res_desc {
struct be_nic_res_desc {
struct be_res_desc_hdr hdr;
u8 rsvd1;
+
+#define QUN_SHIFT 4 /* QoS is in absolute units */
u8 flags;
u8 vf_num;
u8 rsvd2;
@@ -1891,6 +1903,23 @@ struct be_nic_res_desc {
u32 rsvd8[7];
} __packed;
+/************ Multi-Channel type ***********/
+enum mc_type {
+ MC_NONE = 0x01,
+ UMC = 0x02,
+ FLEX10 = 0x03,
+ vNIC1 = 0x04,
+ nPAR = 0x05,
+ UFP = 0x06,
+ vNIC2 = 0x07
+};
+
+/* Is BE in a multi-channel mode */
+static inline bool be_is_mc(struct be_adapter *adapter)
+{
+ return adapter->mc_type > MC_NONE;
+}
+
struct be_cmd_req_get_func_config {
struct be_cmd_req_hdr hdr;
};
@@ -1919,7 +1948,7 @@ struct be_cmd_req_set_profile_config {
struct be_cmd_req_hdr hdr;
u32 rsvd;
u32 desc_count;
- struct be_nic_res_desc nic_desc;
+ u8 desc[RESOURCE_DESC_SIZE_V1];
};
struct be_cmd_resp_set_profile_config {
@@ -1971,6 +2000,33 @@ struct be_cmd_resp_get_iface_list {
struct be_if_desc if_desc;
};
+/*************** Set logical link ********************/
+#define PLINK_TRACK_SHIFT 8
+struct be_cmd_req_set_ll_link {
+ struct be_cmd_req_hdr hdr;
+ u32 link_config; /* Bit 0: UP_DOWN, Bit 9: PLINK */
+};
+
+/************** Manage IFACE Filters *******************/
+#define OP_CONVERT_NORMAL_TO_TUNNEL 0
+#define OP_CONVERT_TUNNEL_TO_NORMAL 1
+
+struct be_cmd_req_manage_iface_filters {
+ struct be_cmd_req_hdr hdr;
+ u8 op;
+ u8 rsvd0;
+ u8 flags;
+ u8 rsvd1;
+ u32 tunnel_iface_id;
+ u32 target_iface_id;
+ u8 mac[6];
+ u16 vlan_tag;
+ u32 tenant_id;
+ u32 filter_id;
+ u32 cap_flags;
+ u32 cap_control_flags;
+} __packed;
+
int be_pci_fnum_get(struct be_adapter *adapter);
int be_fw_wait_ready(struct be_adapter *adapter);
int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
@@ -2045,7 +2101,7 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter,
int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
u8 loopback_type, u8 enable);
int be_cmd_get_phy_info(struct be_adapter *adapter);
-int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain);
+int be_cmd_config_qos(struct be_adapter *adapter, u32 bps, u8 domain);
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);
@@ -2086,9 +2142,14 @@ int be_cmd_get_func_config(struct be_adapter *adapter,
struct be_resources *res);
int be_cmd_get_profile_config(struct be_adapter *adapter,
struct be_resources *res, u8 domain);
-int be_cmd_set_profile_config(struct be_adapter *adapter, u32 bps, u8 domain);
+int be_cmd_set_profile_config(struct be_adapter *adapter, void *desc,
+ int size, u8 version, u8 domain);
int be_cmd_get_active_profile(struct be_adapter *adapter, u16 *profile);
int be_cmd_get_if_id(struct be_adapter *adapter, struct be_vf_cfg *vf_cfg,
int vf_num);
int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain);
int be_cmd_intr_set(struct be_adapter *adapter, bool intr_enable);
+int be_cmd_set_logical_link_config(struct be_adapter *adapter,
+ int link_state, u8 domain);
+int be_cmd_set_vxlan_port(struct be_adapter *adapter, __be16 port);
+int be_cmd_manage_iface(struct be_adapter *adapter, u32 iface, u8 op);
diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c
index 05be0070f55f..15ba96cba65d 100644
--- a/drivers/net/ethernet/emulex/benet/be_ethtool.c
+++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 - 2013 Emulex
+ * Copyright (C) 2005 - 2014 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -357,10 +357,10 @@ be_get_ethtool_stats(struct net_device *netdev,
struct be_rx_stats *stats = rx_stats(rxo);
do {
- start = u64_stats_fetch_begin_bh(&stats->sync);
+ start = u64_stats_fetch_begin_irq(&stats->sync);
data[base] = stats->rx_bytes;
data[base + 1] = stats->rx_pkts;
- } while (u64_stats_fetch_retry_bh(&stats->sync, start));
+ } while (u64_stats_fetch_retry_irq(&stats->sync, start));
for (i = 2; i < ETHTOOL_RXSTATS_NUM; i++) {
p = (u8 *)stats + et_rx_stats[i].offset;
@@ -373,19 +373,19 @@ be_get_ethtool_stats(struct net_device *netdev,
struct be_tx_stats *stats = tx_stats(txo);
do {
- start = u64_stats_fetch_begin_bh(&stats->sync_compl);
+ start = u64_stats_fetch_begin_irq(&stats->sync_compl);
data[base] = stats->tx_compl;
- } while (u64_stats_fetch_retry_bh(&stats->sync_compl, start));
+ } while (u64_stats_fetch_retry_irq(&stats->sync_compl, start));
do {
- start = u64_stats_fetch_begin_bh(&stats->sync);
+ start = u64_stats_fetch_begin_irq(&stats->sync);
for (i = 1; i < ETHTOOL_TXSTATS_NUM; i++) {
p = (u8 *)stats + et_tx_stats[i].offset;
data[base + i] =
(et_tx_stats[i].size == sizeof(u64)) ?
*(u64 *)p : *(u32 *)p;
}
- } while (u64_stats_fetch_retry_bh(&stats->sync, start));
+ } while (u64_stats_fetch_retry_irq(&stats->sync, start));
base += ETHTOOL_TXSTATS_NUM;
}
}
@@ -802,16 +802,18 @@ be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
if (test->flags & ETH_TEST_FL_OFFLINE) {
if (be_loopback_test(adapter, BE_MAC_LOOPBACK,
- &data[0]) != 0) {
+ &data[0]) != 0)
test->flags |= ETH_TEST_FL_FAILED;
- }
+
if (be_loopback_test(adapter, BE_PHY_LOOPBACK,
- &data[1]) != 0) {
- test->flags |= ETH_TEST_FL_FAILED;
- }
- if (be_loopback_test(adapter, BE_ONE_PORT_EXT_LOOPBACK,
- &data[2]) != 0) {
+ &data[1]) != 0)
test->flags |= ETH_TEST_FL_FAILED;
+
+ if (test->flags & ETH_TEST_FL_EXTERNAL_LB) {
+ if (be_loopback_test(adapter, BE_ONE_PORT_EXT_LOOPBACK,
+ &data[2]) != 0)
+ test->flags |= ETH_TEST_FL_FAILED;
+ test->flags |= ETH_TEST_FL_EXTERNAL_LB_DONE;
}
}
diff --git a/drivers/net/ethernet/emulex/benet/be_hw.h b/drivers/net/ethernet/emulex/benet/be_hw.h
index dc88782185f2..3bd198550edb 100644
--- a/drivers/net/ethernet/emulex/benet/be_hw.h
+++ b/drivers/net/ethernet/emulex/benet/be_hw.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 - 2013 Emulex
+ * Copyright (C) 2005 - 2014 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -368,7 +368,7 @@ struct amap_eth_rx_compl_v0 {
u8 numfrags[3]; /* dword 1 */
u8 rss_flush; /* dword 2 */
u8 cast_enc[2]; /* dword 2 */
- u8 vtm; /* dword 2 */
+ u8 qnq; /* dword 2 */
u8 rss_bank; /* dword 2 */
u8 rsvd1[23]; /* dword 2 */
u8 lro_pkt; /* dword 2 */
@@ -401,13 +401,14 @@ struct amap_eth_rx_compl_v1 {
u8 numfrags[3]; /* dword 1 */
u8 rss_flush; /* dword 2 */
u8 cast_enc[2]; /* dword 2 */
- u8 vtm; /* dword 2 */
+ u8 qnq; /* dword 2 */
u8 rss_bank; /* dword 2 */
u8 port[2]; /* dword 2 */
u8 vntagp; /* dword 2 */
u8 header_len[8]; /* dword 2 */
u8 header_split[2]; /* dword 2 */
- u8 rsvd1[13]; /* dword 2 */
+ u8 rsvd1[12]; /* dword 2 */
+ u8 tunneled;
u8 valid; /* dword 2 */
u8 rsshash[32]; /* dword 3 */
} __packed;
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 36c80612e21a..3e6df47b6973 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 - 2013 Emulex
+ * Copyright (C) 2005 - 2014 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -23,6 +23,7 @@
#include <linux/aer.h>
#include <linux/if_bridge.h>
#include <net/busy_poll.h>
+#include <net/vxlan.h>
MODULE_VERSION(DRV_VER);
MODULE_DEVICE_TABLE(pci, be_dev_ids);
@@ -591,10 +592,10 @@ static struct rtnl_link_stats64 *be_get_stats64(struct net_device *netdev,
for_all_rx_queues(adapter, rxo, i) {
const struct be_rx_stats *rx_stats = rx_stats(rxo);
do {
- start = u64_stats_fetch_begin_bh(&rx_stats->sync);
+ start = u64_stats_fetch_begin_irq(&rx_stats->sync);
pkts = rx_stats(rxo)->rx_pkts;
bytes = rx_stats(rxo)->rx_bytes;
- } while (u64_stats_fetch_retry_bh(&rx_stats->sync, start));
+ } while (u64_stats_fetch_retry_irq(&rx_stats->sync, start));
stats->rx_packets += pkts;
stats->rx_bytes += bytes;
stats->multicast += rx_stats(rxo)->rx_mcast_pkts;
@@ -605,10 +606,10 @@ static struct rtnl_link_stats64 *be_get_stats64(struct net_device *netdev,
for_all_tx_queues(adapter, txo, i) {
const struct be_tx_stats *tx_stats = tx_stats(txo);
do {
- start = u64_stats_fetch_begin_bh(&tx_stats->sync);
+ start = u64_stats_fetch_begin_irq(&tx_stats->sync);
pkts = tx_stats(txo)->tx_pkts;
bytes = tx_stats(txo)->tx_bytes;
- } while (u64_stats_fetch_retry_bh(&tx_stats->sync, start));
+ } while (u64_stats_fetch_retry_irq(&tx_stats->sync, start));
stats->tx_packets += pkts;
stats->tx_bytes += bytes;
}
@@ -652,7 +653,7 @@ void be_link_status_update(struct be_adapter *adapter, u8 link_status)
adapter->flags |= BE_FLAGS_LINK_STATUS_INIT;
}
- if ((link_status & LINK_STATUS_MASK) == LINK_UP)
+ if (link_status)
netif_carrier_on(netdev);
else
netif_carrier_off(netdev);
@@ -718,10 +719,23 @@ static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter,
return vlan_tag;
}
+/* Used only for IP tunnel packets */
+static u16 skb_inner_ip_proto(struct sk_buff *skb)
+{
+ return (inner_ip_hdr(skb)->version == 4) ?
+ inner_ip_hdr(skb)->protocol : inner_ipv6_hdr(skb)->nexthdr;
+}
+
+static u16 skb_ip_proto(struct sk_buff *skb)
+{
+ return (ip_hdr(skb)->version == 4) ?
+ ip_hdr(skb)->protocol : ipv6_hdr(skb)->nexthdr;
+}
+
static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr,
struct sk_buff *skb, u32 wrb_cnt, u32 len, bool skip_hw_vlan)
{
- u16 vlan_tag;
+ u16 vlan_tag, proto;
memset(hdr, 0, sizeof(*hdr));
@@ -734,9 +748,15 @@ static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr,
if (skb_is_gso_v6(skb) && !lancer_chip(adapter))
AMAP_SET_BITS(struct amap_eth_hdr_wrb, lso6, hdr, 1);
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
- if (is_tcp_pkt(skb))
+ if (skb->encapsulation) {
+ AMAP_SET_BITS(struct amap_eth_hdr_wrb, ipcs, hdr, 1);
+ proto = skb_inner_ip_proto(skb);
+ } else {
+ proto = skb_ip_proto(skb);
+ }
+ if (proto == IPPROTO_TCP)
AMAP_SET_BITS(struct amap_eth_hdr_wrb, tcpcs, hdr, 1);
- else if (is_udp_pkt(skb))
+ else if (proto == IPPROTO_UDP)
AMAP_SET_BITS(struct amap_eth_hdr_wrb, udpcs, hdr, 1);
}
@@ -935,9 +955,9 @@ static struct sk_buff *be_lancer_xmit_workarounds(struct be_adapter *adapter,
}
/* If vlan tag is already inlined in the packet, skip HW VLAN
- * tagging in UMC mode
+ * tagging in pvid-tagging mode
*/
- if ((adapter->function_mode & UMC_ENABLED) &&
+ if (be_pvid_tagging_enabled(adapter) &&
veh->h_vlan_proto == htons(ETH_P_8021Q))
*skip_hw_vlan = true;
@@ -1138,7 +1158,10 @@ static int be_vlan_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
/* Packets with VID 0 are always received by Lancer by default */
if (lancer_chip(adapter) && vid == 0)
- goto ret;
+ return status;
+
+ if (adapter->vlan_tag[vid])
+ return status;
adapter->vlan_tag[vid] = 1;
adapter->vlans_added++;
@@ -1148,7 +1171,7 @@ static int be_vlan_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
adapter->vlans_added--;
adapter->vlan_tag[vid] = 0;
}
-ret:
+
return status;
}
@@ -1288,6 +1311,7 @@ static int be_get_vf_config(struct net_device *netdev, int vf,
vi->vlan = vf_cfg->vlan_tag & VLAN_VID_MASK;
vi->qos = vf_cfg->vlan_tag >> VLAN_PRIO_SHIFT;
memcpy(&vi->mac, vf_cfg->mac_addr, ETH_ALEN);
+ vi->linkstate = adapter->vf_cfg[vf].plink_tracking;
return 0;
}
@@ -1342,11 +1366,7 @@ static int be_set_vf_tx_rate(struct net_device *netdev,
return -EINVAL;
}
- if (lancer_chip(adapter))
- status = be_cmd_set_profile_config(adapter, rate / 10, vf + 1);
- else
- status = be_cmd_set_qos(adapter, rate / 10, vf + 1);
-
+ status = be_cmd_config_qos(adapter, rate / 10, vf + 1);
if (status)
dev_err(&adapter->pdev->dev,
"tx rate %d on VF %d failed\n", rate, vf);
@@ -1354,6 +1374,24 @@ static int be_set_vf_tx_rate(struct net_device *netdev,
adapter->vf_cfg[vf].tx_rate = rate;
return status;
}
+static int be_set_vf_link_state(struct net_device *netdev, int vf,
+ int link_state)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+ int status;
+
+ if (!sriov_enabled(adapter))
+ return -EPERM;
+
+ if (vf >= adapter->num_vfs)
+ return -EINVAL;
+
+ status = be_cmd_set_logical_link_config(adapter, link_state, vf+1);
+ if (!status)
+ adapter->vf_cfg[vf].plink_tracking = link_state;
+
+ return status;
+}
static void be_aic_update(struct be_aic_obj *aic, u64 rx_pkts, u64 tx_pkts,
ulong now)
@@ -1386,15 +1424,15 @@ static void be_eqd_update(struct be_adapter *adapter)
rxo = &adapter->rx_obj[eqo->idx];
do {
- start = u64_stats_fetch_begin_bh(&rxo->stats.sync);
+ start = u64_stats_fetch_begin_irq(&rxo->stats.sync);
rx_pkts = rxo->stats.rx_pkts;
- } while (u64_stats_fetch_retry_bh(&rxo->stats.sync, start));
+ } while (u64_stats_fetch_retry_irq(&rxo->stats.sync, start));
txo = &adapter->tx_obj[eqo->idx];
do {
- start = u64_stats_fetch_begin_bh(&txo->stats.sync);
+ start = u64_stats_fetch_begin_irq(&txo->stats.sync);
tx_pkts = txo->stats.tx_reqs;
- } while (u64_stats_fetch_retry_bh(&txo->stats.sync, start));
+ } while (u64_stats_fetch_retry_irq(&txo->stats.sync, start));
/* Skip, if wrapped around or first calculation */
@@ -1449,9 +1487,10 @@ static void be_rx_stats_update(struct be_rx_obj *rxo,
static inline bool csum_passed(struct be_rx_compl_info *rxcp)
{
/* L4 checksum is not reliable for non TCP/UDP packets.
- * Also ignore ipcksm for ipv6 pkts */
+ * Also ignore ipcksm for ipv6 pkts
+ */
return (rxcp->tcpf || rxcp->udpf) && rxcp->l4_csum &&
- (rxcp->ip_csum || rxcp->ipv6);
+ (rxcp->ip_csum || rxcp->ipv6) && !rxcp->err;
}
static struct be_rx_page_info *get_rx_page_info(struct be_rx_obj *rxo)
@@ -1464,11 +1503,15 @@ static struct be_rx_page_info *get_rx_page_info(struct be_rx_obj *rxo)
rx_page_info = &rxo->page_info_tbl[frag_idx];
BUG_ON(!rx_page_info->page);
- if (rx_page_info->last_page_user) {
+ if (rx_page_info->last_frag) {
dma_unmap_page(&adapter->pdev->dev,
dma_unmap_addr(rx_page_info, bus),
adapter->big_page_size, DMA_FROM_DEVICE);
- rx_page_info->last_page_user = false;
+ rx_page_info->last_frag = false;
+ } else {
+ dma_sync_single_for_cpu(&adapter->pdev->dev,
+ dma_unmap_addr(rx_page_info, bus),
+ rx_frag_size, DMA_FROM_DEVICE);
}
queue_tail_inc(rxq);
@@ -1590,6 +1633,8 @@ static void be_rx_compl_process(struct be_rx_obj *rxo, struct napi_struct *napi,
skb_record_rx_queue(skb, rxo - &adapter->rx_obj[0]);
if (netdev->features & NETIF_F_RXHASH)
skb_set_hash(skb, rxcp->rss_hash, PKT_HASH_TYPE_L3);
+
+ skb->encapsulation = rxcp->tunneled;
skb_mark_napi_id(skb, napi);
if (rxcp->vlanf)
@@ -1646,6 +1691,8 @@ static void be_rx_compl_process_gro(struct be_rx_obj *rxo,
skb_record_rx_queue(skb, rxo - &adapter->rx_obj[0]);
if (adapter->netdev->features & NETIF_F_RXHASH)
skb_set_hash(skb, rxcp->rss_hash, PKT_HASH_TYPE_L3);
+
+ skb->encapsulation = rxcp->tunneled;
skb_mark_napi_id(skb, napi);
if (rxcp->vlanf)
@@ -1676,12 +1723,14 @@ static void be_parse_rx_compl_v1(struct be_eth_rx_compl *compl,
rxcp->rss_hash =
AMAP_GET_BITS(struct amap_eth_rx_compl_v1, rsshash, compl);
if (rxcp->vlanf) {
- rxcp->vtm = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, vtm,
+ rxcp->qnq = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, qnq,
compl);
rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, vlan_tag,
compl);
}
rxcp->port = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, port, compl);
+ rxcp->tunneled =
+ AMAP_GET_BITS(struct amap_eth_rx_compl_v1, tunneled, compl);
}
static void be_parse_rx_compl_v0(struct be_eth_rx_compl *compl,
@@ -1706,7 +1755,7 @@ static void be_parse_rx_compl_v0(struct be_eth_rx_compl *compl,
rxcp->rss_hash =
AMAP_GET_BITS(struct amap_eth_rx_compl_v0, rsshash, compl);
if (rxcp->vlanf) {
- rxcp->vtm = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, vtm,
+ rxcp->qnq = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, qnq,
compl);
rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, vlan_tag,
compl);
@@ -1739,9 +1788,11 @@ static struct be_rx_compl_info *be_rx_compl_get(struct be_rx_obj *rxo)
rxcp->l4_csum = 0;
if (rxcp->vlanf) {
- /* vlanf could be wrongly set in some cards.
- * ignore if vtm is not set */
- if ((adapter->function_mode & FLEX10_MODE) && !rxcp->vtm)
+ /* In QNQ modes, if qnq bit is not set, then the packet was
+ * tagged only with the transparent outer vlan-tag and must
+ * not be treated as a vlan packet by host
+ */
+ if (be_is_qnq_mode(adapter) && !rxcp->qnq)
rxcp->vlanf = 0;
if (!lancer_chip(adapter))
@@ -1800,17 +1851,16 @@ static void be_post_rx_frags(struct be_rx_obj *rxo, gfp_t gfp)
rx_stats(rxo)->rx_post_fail++;
break;
}
- page_info->page_offset = 0;
+ page_offset = 0;
} else {
get_page(pagep);
- page_info->page_offset = page_offset + rx_frag_size;
+ page_offset += rx_frag_size;
}
- page_offset = page_info->page_offset;
+ page_info->page_offset = page_offset;
page_info->page = pagep;
- dma_unmap_addr_set(page_info, bus, page_dmaaddr);
- frag_dmaaddr = page_dmaaddr + page_info->page_offset;
rxd = queue_head_node(rxq);
+ frag_dmaaddr = page_dmaaddr + page_info->page_offset;
rxd->fragpa_lo = cpu_to_le32(frag_dmaaddr & 0xFFFFFFFF);
rxd->fragpa_hi = cpu_to_le32(upper_32_bits(frag_dmaaddr));
@@ -1818,15 +1868,24 @@ static void be_post_rx_frags(struct be_rx_obj *rxo, gfp_t gfp)
if ((page_offset + rx_frag_size + rx_frag_size) >
adapter->big_page_size) {
pagep = NULL;
- page_info->last_page_user = true;
+ page_info->last_frag = true;
+ dma_unmap_addr_set(page_info, bus, page_dmaaddr);
+ } else {
+ dma_unmap_addr_set(page_info, bus, frag_dmaaddr);
}
prev_page_info = page_info;
queue_head_inc(rxq);
page_info = &rxo->page_info_tbl[rxq->head];
}
- if (pagep)
- prev_page_info->last_page_user = true;
+
+ /* Mark the last frag of a page when we break out of the above loop
+ * with no more slots available in the RXQ
+ */
+ if (pagep) {
+ prev_page_info->last_frag = true;
+ dma_unmap_addr_set(prev_page_info, bus, page_dmaaddr);
+ }
if (posted) {
atomic_add(posted, &rxq->used);
@@ -1883,7 +1942,7 @@ static u16 be_tx_compl_process(struct be_adapter *adapter,
queue_tail_inc(txq);
} while (cur_index != last_index);
- kfree_skb(sent_skb);
+ dev_kfree_skb_any(sent_skb);
return num_wrbs;
}
@@ -2439,6 +2498,9 @@ void be_detect_error(struct be_adapter *adapter)
u32 ue_lo = 0, ue_hi = 0, ue_lo_mask = 0, ue_hi_mask = 0;
u32 sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0;
u32 i;
+ bool error_detected = false;
+ struct device *dev = &adapter->pdev->dev;
+ struct net_device *netdev = adapter->netdev;
if (be_hw_error(adapter))
return;
@@ -2450,6 +2512,21 @@ void be_detect_error(struct be_adapter *adapter)
SLIPORT_ERROR1_OFFSET);
sliport_err2 = ioread32(adapter->db +
SLIPORT_ERROR2_OFFSET);
+ adapter->hw_error = true;
+ /* Do not log error messages if its a FW reset */
+ if (sliport_err1 == SLIPORT_ERROR_FW_RESET1 &&
+ sliport_err2 == SLIPORT_ERROR_FW_RESET2) {
+ dev_info(dev, "Firmware update in progress\n");
+ } else {
+ error_detected = true;
+ dev_err(dev, "Error detected in the card\n");
+ dev_err(dev, "ERR: sliport status 0x%x\n",
+ sliport_status);
+ dev_err(dev, "ERR: sliport error1 0x%x\n",
+ sliport_err1);
+ dev_err(dev, "ERR: sliport error2 0x%x\n",
+ sliport_err2);
+ }
}
} else {
pci_read_config_dword(adapter->pdev,
@@ -2463,51 +2540,33 @@ void be_detect_error(struct be_adapter *adapter)
ue_lo = (ue_lo & ~ue_lo_mask);
ue_hi = (ue_hi & ~ue_hi_mask);
- }
-
- /* On certain platforms BE hardware can indicate spurious UEs.
- * Allow the h/w to stop working completely in case of a real UE.
- * Hence not setting the hw_error for UE detection.
- */
- if (sliport_status & SLIPORT_STATUS_ERR_MASK) {
- adapter->hw_error = true;
- /* Do not log error messages if its a FW reset */
- if (sliport_err1 == SLIPORT_ERROR_FW_RESET1 &&
- sliport_err2 == SLIPORT_ERROR_FW_RESET2) {
- dev_info(&adapter->pdev->dev,
- "Firmware update in progress\n");
- return;
- } else {
- dev_err(&adapter->pdev->dev,
- "Error detected in the card\n");
- }
- }
-
- if (sliport_status & SLIPORT_STATUS_ERR_MASK) {
- dev_err(&adapter->pdev->dev,
- "ERR: sliport status 0x%x\n", sliport_status);
- dev_err(&adapter->pdev->dev,
- "ERR: sliport error1 0x%x\n", sliport_err1);
- dev_err(&adapter->pdev->dev,
- "ERR: sliport error2 0x%x\n", sliport_err2);
- }
- if (ue_lo) {
- for (i = 0; ue_lo; ue_lo >>= 1, i++) {
- if (ue_lo & 1)
- dev_err(&adapter->pdev->dev,
- "UE: %s bit set\n", ue_status_low_desc[i]);
- }
- }
+ /* On certain platforms BE hardware can indicate spurious UEs.
+ * Allow HW to stop working completely in case of a real UE.
+ * Hence not setting the hw_error for UE detection.
+ */
- if (ue_hi) {
- for (i = 0; ue_hi; ue_hi >>= 1, i++) {
- if (ue_hi & 1)
- dev_err(&adapter->pdev->dev,
- "UE: %s bit set\n", ue_status_hi_desc[i]);
+ if (ue_lo || ue_hi) {
+ error_detected = true;
+ dev_err(dev,
+ "Unrecoverable Error detected in the adapter");
+ dev_err(dev, "Please reboot server to recover");
+ if (skyhawk_chip(adapter))
+ adapter->hw_error = true;
+ for (i = 0; ue_lo; ue_lo >>= 1, i++) {
+ if (ue_lo & 1)
+ dev_err(dev, "UE: %s bit set\n",
+ ue_status_low_desc[i]);
+ }
+ for (i = 0; ue_hi; ue_hi >>= 1, i++) {
+ if (ue_hi & 1)
+ dev_err(dev, "UE: %s bit set\n",
+ ue_status_hi_desc[i]);
+ }
}
}
-
+ if (error_detected)
+ netif_carrier_off(netdev);
}
static void be_msix_disable(struct be_adapter *adapter)
@@ -2521,7 +2580,7 @@ static void be_msix_disable(struct be_adapter *adapter)
static int be_msix_enable(struct be_adapter *adapter)
{
- int i, status, num_vec;
+ int i, num_vec;
struct device *dev = &adapter->pdev->dev;
/* If RoCE is supported, program the max number of NIC vectors that
@@ -2537,24 +2596,11 @@ static int be_msix_enable(struct be_adapter *adapter)
for (i = 0; i < num_vec; i++)
adapter->msix_entries[i].entry = i;
- status = pci_enable_msix(adapter->pdev, adapter->msix_entries, num_vec);
- if (status == 0) {
- goto done;
- } else if (status >= MIN_MSIX_VECTORS) {
- num_vec = status;
- status = pci_enable_msix(adapter->pdev, adapter->msix_entries,
- num_vec);
- if (!status)
- goto done;
- }
-
- dev_warn(dev, "MSIx enable failed\n");
+ num_vec = pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
+ MIN_MSIX_VECTORS, num_vec);
+ if (num_vec < 0)
+ goto fail;
- /* INTx is not supported in VFs, so fail probe if enable_msix fails */
- if (!be_physfn(adapter))
- return status;
- return 0;
-done:
if (be_roce_supported(adapter) && num_vec > MIN_MSIX_VECTORS) {
adapter->num_msix_roce_vec = num_vec / 2;
dev_info(dev, "enabled %d MSI-x vector(s) for RoCE\n",
@@ -2566,6 +2612,14 @@ done:
dev_info(dev, "enabled %d MSI-x vector(s) for NIC\n",
adapter->num_msix_vec);
return 0;
+
+fail:
+ dev_warn(dev, "MSIx enable failed\n");
+
+ /* INTx is not supported in VFs, so fail probe if enable_msix fails */
+ if (!be_physfn(adapter))
+ return num_vec;
+ return 0;
}
static inline int be_msix_vec_get(struct be_adapter *adapter,
@@ -2807,6 +2861,12 @@ static int be_open(struct net_device *netdev)
netif_tx_start_all_queues(netdev);
be_roce_dev_open(adapter);
+
+#ifdef CONFIG_BE2NET_VXLAN
+ if (skyhawk_chip(adapter))
+ vxlan_get_rx_port(netdev);
+#endif
+
return 0;
err:
be_close(adapter->netdev);
@@ -2962,6 +3022,21 @@ static void be_mac_clear(struct be_adapter *adapter)
}
}
+#ifdef CONFIG_BE2NET_VXLAN
+static void be_disable_vxlan_offloads(struct be_adapter *adapter)
+{
+ if (adapter->flags & BE_FLAGS_VXLAN_OFFLOADS)
+ be_cmd_manage_iface(adapter, adapter->if_handle,
+ OP_CONVERT_TUNNEL_TO_NORMAL);
+
+ if (adapter->vxlan_port)
+ be_cmd_set_vxlan_port(adapter, 0);
+
+ adapter->flags &= ~BE_FLAGS_VXLAN_OFFLOADS;
+ adapter->vxlan_port = 0;
+}
+#endif
+
static int be_clear(struct be_adapter *adapter)
{
be_cancel_worker(adapter);
@@ -2969,6 +3044,9 @@ static int be_clear(struct be_adapter *adapter)
if (sriov_enabled(adapter))
be_vf_clear(adapter);
+#ifdef CONFIG_BE2NET_VXLAN
+ be_disable_vxlan_offloads(adapter);
+#endif
/* delete the primary mac along with the uc-mac list */
be_mac_clear(adapter);
@@ -3093,15 +3171,19 @@ static int be_vf_setup(struct be_adapter *adapter)
* Allow full available bandwidth
*/
if (BE3_chip(adapter) && !old_vfs)
- be_cmd_set_qos(adapter, 1000, vf+1);
+ be_cmd_config_qos(adapter, 1000, vf + 1);
status = be_cmd_link_status_query(adapter, &lnk_speed,
NULL, vf + 1);
if (!status)
vf_cfg->tx_rate = lnk_speed;
- if (!old_vfs)
+ if (!old_vfs) {
be_cmd_enable_vf(adapter, vf + 1);
+ be_cmd_set_logical_link_config(adapter,
+ IFLA_VF_LINK_STATE_AUTO,
+ vf+1);
+ }
}
if (!old_vfs) {
@@ -3119,19 +3201,38 @@ err:
return status;
}
+/* Converting function_mode bits on BE3 to SH mc_type enums */
+
+static u8 be_convert_mc_type(u32 function_mode)
+{
+ if (function_mode & VNIC_MODE && function_mode & FLEX10_MODE)
+ return vNIC1;
+ else if (function_mode & FLEX10_MODE)
+ return FLEX10;
+ else if (function_mode & VNIC_MODE)
+ return vNIC2;
+ else if (function_mode & UMC_ENABLED)
+ return UMC;
+ else
+ return MC_NONE;
+}
+
/* On BE2/BE3 FW does not suggest the supported limits */
static void BEx_get_resources(struct be_adapter *adapter,
struct be_resources *res)
{
struct pci_dev *pdev = adapter->pdev;
bool use_sriov = false;
- int max_vfs;
-
- max_vfs = pci_sriov_get_totalvfs(pdev);
-
- if (BE3_chip(adapter) && sriov_want(adapter)) {
- res->max_vfs = max_vfs > 0 ? min(MAX_VFS, max_vfs) : 0;
- use_sriov = res->max_vfs;
+ int max_vfs = 0;
+
+ if (be_physfn(adapter) && BE3_chip(adapter)) {
+ be_cmd_get_profile_config(adapter, res, 0);
+ /* Some old versions of BE3 FW don't report max_vfs value */
+ if (res->max_vfs == 0) {
+ max_vfs = pci_sriov_get_totalvfs(pdev);
+ res->max_vfs = max_vfs > 0 ? min(MAX_VFS, max_vfs) : 0;
+ }
+ use_sriov = res->max_vfs && sriov_want(adapter);
}
if (be_physfn(adapter))
@@ -3139,17 +3240,32 @@ static void BEx_get_resources(struct be_adapter *adapter,
else
res->max_uc_mac = BE_VF_UC_PMAC_COUNT;
- if (adapter->function_mode & FLEX10_MODE)
- res->max_vlans = BE_NUM_VLANS_SUPPORTED/8;
- else if (adapter->function_mode & UMC_ENABLED)
- res->max_vlans = BE_UMC_NUM_VLANS_SUPPORTED;
- else
+ adapter->mc_type = be_convert_mc_type(adapter->function_mode);
+
+ if (be_is_mc(adapter)) {
+ /* Assuming that there are 4 channels per port,
+ * when multi-channel is enabled
+ */
+ if (be_is_qnq_mode(adapter))
+ res->max_vlans = BE_NUM_VLANS_SUPPORTED/8;
+ else
+ /* In a non-qnq multichannel mode, the pvid
+ * takes up one vlan entry
+ */
+ res->max_vlans = (BE_NUM_VLANS_SUPPORTED / 4) - 1;
+ } else {
res->max_vlans = BE_NUM_VLANS_SUPPORTED;
+ }
+
res->max_mcast_mac = BE_MAX_MC;
- /* For BE3 1Gb ports, F/W does not properly support multiple TXQs */
- if (BE2_chip(adapter) || use_sriov || be_is_mc(adapter) ||
- !be_physfn(adapter) || (adapter->port_num > 1))
+ /* 1) For BE3 1Gb ports, FW does not support multiple TXQs
+ * 2) Create multiple TX rings on a BE3-R multi-channel interface
+ * *only* if it is RSS-capable.
+ */
+ if (BE2_chip(adapter) || use_sriov || (adapter->port_num > 1) ||
+ !be_physfn(adapter) || (be_is_mc(adapter) &&
+ !(adapter->function_caps & BE_FUNCTION_CAPS_RSS)))
res->max_tx_qs = 1;
else
res->max_tx_qs = BE3_MAX_TX_QS;
@@ -3161,7 +3277,7 @@ static void BEx_get_resources(struct be_adapter *adapter,
res->max_rx_qs = res->max_rss_qs + 1;
if (be_physfn(adapter))
- res->max_evt_qs = (max_vfs > 0) ?
+ res->max_evt_qs = (res->max_vfs > 0) ?
BE3_SRIOV_MAX_EVT_QS : BE3_MAX_EVT_QS;
else
res->max_evt_qs = 1;
@@ -3252,9 +3368,8 @@ static int be_get_config(struct be_adapter *adapter)
if (status)
return status;
- /* primary mac needs 1 pmac entry */
- adapter->pmac_id = kcalloc(be_max_uc(adapter) + 1, sizeof(u32),
- GFP_KERNEL);
+ adapter->pmac_id = kcalloc(be_max_uc(adapter),
+ sizeof(*adapter->pmac_id), GFP_KERNEL);
if (!adapter->pmac_id)
return -ENOMEM;
@@ -3428,6 +3543,10 @@ static int be_setup(struct be_adapter *adapter)
be_cmd_set_flow_control(adapter, adapter->tx_fc,
adapter->rx_fc);
+ if (be_physfn(adapter))
+ be_cmd_set_logical_link_config(adapter,
+ IFLA_VF_LINK_STATE_AUTO, 0);
+
if (sriov_want(adapter)) {
if (be_max_vfs(adapter))
be_vf_setup(adapter);
@@ -4052,6 +4171,67 @@ static int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB);
}
+#ifdef CONFIG_BE2NET_VXLAN
+static void be_add_vxlan_port(struct net_device *netdev, sa_family_t sa_family,
+ __be16 port)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+ struct device *dev = &adapter->pdev->dev;
+ int status;
+
+ if (lancer_chip(adapter) || BEx_chip(adapter))
+ return;
+
+ if (adapter->flags & BE_FLAGS_VXLAN_OFFLOADS) {
+ dev_warn(dev, "Cannot add UDP port %d for VxLAN offloads\n",
+ be16_to_cpu(port));
+ dev_info(dev,
+ "Only one UDP port supported for VxLAN offloads\n");
+ return;
+ }
+
+ status = be_cmd_manage_iface(adapter, adapter->if_handle,
+ OP_CONVERT_NORMAL_TO_TUNNEL);
+ if (status) {
+ dev_warn(dev, "Failed to convert normal interface to tunnel\n");
+ goto err;
+ }
+
+ status = be_cmd_set_vxlan_port(adapter, port);
+ if (status) {
+ dev_warn(dev, "Failed to add VxLAN port\n");
+ goto err;
+ }
+ adapter->flags |= BE_FLAGS_VXLAN_OFFLOADS;
+ adapter->vxlan_port = port;
+
+ dev_info(dev, "Enabled VxLAN offloads for UDP port %d\n",
+ be16_to_cpu(port));
+ return;
+err:
+ be_disable_vxlan_offloads(adapter);
+ return;
+}
+
+static void be_del_vxlan_port(struct net_device *netdev, sa_family_t sa_family,
+ __be16 port)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+
+ if (lancer_chip(adapter) || BEx_chip(adapter))
+ return;
+
+ if (adapter->vxlan_port != port)
+ return;
+
+ be_disable_vxlan_offloads(adapter);
+
+ dev_info(&adapter->pdev->dev,
+ "Disabled VxLAN offloads for UDP port %d\n",
+ be16_to_cpu(port));
+}
+#endif
+
static const struct net_device_ops be_netdev_ops = {
.ndo_open = be_open,
.ndo_stop = be_close,
@@ -4067,13 +4247,18 @@ static const struct net_device_ops be_netdev_ops = {
.ndo_set_vf_vlan = be_set_vf_vlan,
.ndo_set_vf_tx_rate = be_set_vf_tx_rate,
.ndo_get_vf_config = be_get_vf_config,
+ .ndo_set_vf_link_state = be_set_vf_link_state,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = be_netpoll,
#endif
.ndo_bridge_setlink = be_ndo_bridge_setlink,
.ndo_bridge_getlink = be_ndo_bridge_getlink,
#ifdef CONFIG_NET_RX_BUSY_POLL
- .ndo_busy_poll = be_busy_poll
+ .ndo_busy_poll = be_busy_poll,
+#endif
+#ifdef CONFIG_BE2NET_VXLAN
+ .ndo_add_vxlan_port = be_add_vxlan_port,
+ .ndo_del_vxlan_port = be_del_vxlan_port,
#endif
};
@@ -4081,6 +4266,12 @@ static void be_netdev_init(struct net_device *netdev)
{
struct be_adapter *adapter = netdev_priv(netdev);
+ if (skyhawk_chip(adapter)) {
+ netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+ NETIF_F_TSO | NETIF_F_TSO6 |
+ NETIF_F_GSO_UDP_TUNNEL;
+ netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL;
+ }
netdev->hw_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |
NETIF_F_HW_VLAN_CTAG_TX;
@@ -4427,14 +4618,32 @@ static bool be_reset_required(struct be_adapter *adapter)
static char *mc_name(struct be_adapter *adapter)
{
- if (adapter->function_mode & FLEX10_MODE)
- return "FLEX10";
- else if (adapter->function_mode & VNIC_MODE)
- return "vNIC";
- else if (adapter->function_mode & UMC_ENABLED)
- return "UMC";
- else
- return "";
+ char *str = ""; /* default */
+
+ switch (adapter->mc_type) {
+ case UMC:
+ str = "UMC";
+ break;
+ case FLEX10:
+ str = "FLEX10";
+ break;
+ case vNIC1:
+ str = "vNIC-1";
+ break;
+ case nPAR:
+ str = "nPAR";
+ break;
+ case UFP:
+ str = "UFP";
+ break;
+ case vNIC2:
+ str = "vNIC-2";
+ break;
+ default:
+ str = "";
+ }
+
+ return str;
}
static inline char *func_name(struct be_adapter *adapter)
diff --git a/drivers/net/ethernet/emulex/benet/be_roce.c b/drivers/net/ethernet/emulex/benet/be_roce.c
index 9cd5415fe017..5bf16603a3e9 100644
--- a/drivers/net/ethernet/emulex/benet/be_roce.c
+++ b/drivers/net/ethernet/emulex/benet/be_roce.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 - 2013 Emulex
+ * Copyright (C) 2005 - 2014 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -35,6 +35,12 @@ static void _be_roce_dev_add(struct be_adapter *adapter)
if (!ocrdma_drv)
return;
+
+ if (ocrdma_drv->be_abi_version != BE_ROCE_ABI_VERSION) {
+ dev_warn(&pdev->dev, "Cannot initialize RoCE due to ocrdma ABI mismatch\n");
+ return;
+ }
+
if (pdev->device == OC_DEVICE_ID5) {
/* only msix is supported on these devices */
if (!msix_enabled(adapter))
diff --git a/drivers/net/ethernet/emulex/benet/be_roce.h b/drivers/net/ethernet/emulex/benet/be_roce.h
index 2cd1129e19af..a3d9e96c18eb 100644
--- a/drivers/net/ethernet/emulex/benet/be_roce.h
+++ b/drivers/net/ethernet/emulex/benet/be_roce.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 - 2013 Emulex
+ * Copyright (C) 2005 - 2014 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -21,6 +21,8 @@
#include <linux/pci.h>
#include <linux/netdevice.h>
+#define BE_ROCE_ABI_VERSION 1
+
struct ocrdma_dev;
enum be_interrupt_mode {
@@ -52,6 +54,7 @@ struct be_dev_info {
/* ocrdma driver register's the callback functions with nic driver. */
struct ocrdma_driver {
unsigned char name[32];
+ u32 be_abi_version;
struct ocrdma_dev *(*add) (struct be_dev_info *dev_info);
void (*remove) (struct ocrdma_dev *);
void (*state_change_handler) (struct ocrdma_dev *, u32 new_state);
diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c
index 55e0fa03dc90..8b70ca7e342b 100644
--- a/drivers/net/ethernet/ethoc.c
+++ b/drivers/net/ethernet/ethoc.c
@@ -660,11 +660,6 @@ static int ethoc_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
return -EBUSY;
}
-static int ethoc_mdio_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
static void ethoc_mdio_poll(struct net_device *dev)
{
}
@@ -1210,7 +1205,6 @@ static int ethoc_probe(struct platform_device *pdev)
priv->mdio->name, pdev->id);
priv->mdio->read = ethoc_mdio_read;
priv->mdio->write = ethoc_mdio_write;
- priv->mdio->reset = ethoc_mdio_reset;
priv->mdio->priv = priv;
priv->mdio->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index c11ecbc98149..68069eabc4f8 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -940,11 +940,6 @@ static int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr,
return -EIO;
}
-static int ftgmac100_mdiobus_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
/******************************************************************************
* struct ethtool_ops functions
*****************************************************************************/
@@ -1262,7 +1257,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->reset = ftgmac100_mdiobus_reset;
priv->mii_bus->irq = priv->phy_irq;
for (i = 0; i < PHY_MAX_ADDR; i++)
diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile
index 549ce13b92ac..71debd1c18c9 100644
--- a/drivers/net/ethernet/freescale/Makefile
+++ b/drivers/net/ethernet/freescale/Makefile
@@ -14,7 +14,6 @@ obj-$(CONFIG_FSL_XGMAC_MDIO) += xgmac_mdio.o
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o
gianfar_driver-objs := gianfar.o \
- gianfar_ethtool.o \
- gianfar_sysfs.o
+ gianfar_ethtool.o
obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o
ucc_geth_driver-objs := ucc_geth.o ucc_geth_ethtool.o
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 03a351300013..8d69e439f0c5 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -338,7 +338,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/* Protocol checksum off-load for TCP and UDP. */
if (fec_enet_clear_csum(skb, ndev)) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -1255,11 +1255,6 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
return 0;
}
-static int fec_enet_mdio_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
static int fec_enet_mii_probe(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
@@ -1384,7 +1379,6 @@ static int fec_enet_mii_init(struct platform_device *pdev)
fep->mii_bus->name = "fec_enet_mii_bus";
fep->mii_bus->read = fec_enet_mdio_read;
fep->mii_bus->write = fec_enet_mdio_write;
- fep->mii_bus->reset = fec_enet_mdio_reset;
snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, fep->dev_id + 1);
fep->mii_bus->priv = fep;
@@ -1904,10 +1898,11 @@ fec_set_mac_address(struct net_device *ndev, void *p)
struct fec_enet_private *fep = netdev_priv(ndev);
struct sockaddr *addr = p;
- if (!is_valid_ether_addr(addr->sa_data))
- return -EADDRNOTAVAIL;
-
- memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
+ if (addr) {
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+ memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
+ }
writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |
(ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),
@@ -2006,6 +2001,8 @@ static int fec_enet_init(struct net_device *ndev)
/* Get the Ethernet address */
fec_get_mac(ndev);
+ /* make sure MAC we just acquired is programmed into the hw */
+ fec_set_mac_address(ndev, NULL);
/* init the tx & rx ring size */
fep->tx_ring_size = TX_RING_SIZE;
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c
index 89ccb5b08708..82386b29914a 100644
--- a/drivers/net/ethernet/freescale/fec_ptp.c
+++ b/drivers/net/ethernet/freescale/fec_ptp.c
@@ -372,6 +372,7 @@ void fec_ptp_init(struct platform_device *pdev)
fep->ptp_caps.n_alarm = 0;
fep->ptp_caps.n_ext_ts = 0;
fep->ptp_caps.n_per_out = 0;
+ fep->ptp_caps.n_pins = 0;
fep->ptp_caps.pps = 0;
fep->ptp_caps.adjfreq = fec_ptp_adjfreq;
fep->ptp_caps.adjtime = fec_ptp_adjtime;
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 62f042d4aaa9..dc80db41d6b3 100644
--- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
+++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
@@ -91,6 +91,9 @@ static int fs_enet_rx_napi(struct napi_struct *napi, int budget)
u16 pkt_len, sc;
int curidx;
+ if (budget <= 0)
+ return received;
+
/*
* First, grab all of the stats for the incoming packet.
* These get messed up if we get called due to a busy condition.
diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
index 7e69c983d12a..ebf5d6429a8d 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
@@ -95,12 +95,6 @@ static int fs_enet_fec_mii_write(struct mii_bus *bus, int phy_id, int location,
}
-static int fs_enet_fec_mii_reset(struct mii_bus *bus)
-{
- /* nothing here - for now */
- return 0;
-}
-
static struct of_device_id fs_enet_mdio_fec_match[];
static int fs_enet_mdio_probe(struct platform_device *ofdev)
{
@@ -128,7 +122,6 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev)
new_bus->name = "FEC MII Bus";
new_bus->read = &fs_enet_fec_mii_read;
new_bus->write = &fs_enet_fec_mii_write;
- new_bus->reset = &fs_enet_fec_mii_reset;
ret = of_address_to_resource(ofdev->dev.of_node, 0, &res);
if (ret)
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index ad5a5aadc7e1..9125d9abf099 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -9,7 +9,7 @@
* Maintainer: Kumar Gala
* Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright 2002-2009, 2011 Freescale Semiconductor, Inc.
+ * Copyright 2002-2009, 2011-2013 Freescale Semiconductor, Inc.
* Copyright 2007 MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or modify it
@@ -121,7 +121,6 @@ static irqreturn_t gfar_error(int irq, void *dev_id);
static irqreturn_t gfar_transmit(int irq, void *dev_id);
static irqreturn_t gfar_interrupt(int irq, void *dev_id);
static void adjust_link(struct net_device *dev);
-static void init_registers(struct net_device *dev);
static int init_phy(struct net_device *dev);
static int gfar_probe(struct platform_device *ofdev);
static int gfar_remove(struct platform_device *ofdev);
@@ -129,8 +128,10 @@ static void free_skb_resources(struct gfar_private *priv);
static void gfar_set_multi(struct net_device *dev);
static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
static void gfar_configure_serdes(struct net_device *dev);
-static int gfar_poll(struct napi_struct *napi, int budget);
-static int gfar_poll_sq(struct napi_struct *napi, int budget);
+static int gfar_poll_rx(struct napi_struct *napi, int budget);
+static int gfar_poll_tx(struct napi_struct *napi, int budget);
+static int gfar_poll_rx_sq(struct napi_struct *napi, int budget);
+static int gfar_poll_tx_sq(struct napi_struct *napi, int budget);
#ifdef CONFIG_NET_POLL_CONTROLLER
static void gfar_netpoll(struct net_device *dev);
#endif
@@ -138,9 +139,7 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit);
static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue);
static void gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
int amount_pull, struct napi_struct *napi);
-void gfar_halt(struct net_device *dev);
-static void gfar_halt_nodisable(struct net_device *dev);
-void gfar_start(struct net_device *dev);
+static void gfar_halt_nodisable(struct gfar_private *priv);
static void gfar_clear_exact_match(struct net_device *dev);
static void gfar_set_mac_for_addr(struct net_device *dev, int num,
const u8 *addr);
@@ -332,72 +331,76 @@ static void gfar_init_tx_rx_base(struct gfar_private *priv)
}
}
-static void gfar_init_mac(struct net_device *ndev)
+static void gfar_rx_buff_size_config(struct gfar_private *priv)
{
- struct gfar_private *priv = netdev_priv(ndev);
- struct gfar __iomem *regs = priv->gfargrp[0].regs;
- u32 rctrl = 0;
- u32 tctrl = 0;
- u32 attrs = 0;
-
- /* write the tx/rx base registers */
- gfar_init_tx_rx_base(priv);
-
- /* Configure the coalescing support */
- gfar_configure_coalescing_all(priv);
+ int frame_size = priv->ndev->mtu + ETH_HLEN;
/* set this when rx hw offload (TOE) functions are being used */
priv->uses_rxfcb = 0;
+ if (priv->ndev->features & (NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_RX))
+ priv->uses_rxfcb = 1;
+
+ if (priv->hwts_rx_en)
+ priv->uses_rxfcb = 1;
+
+ if (priv->uses_rxfcb)
+ frame_size += GMAC_FCB_LEN;
+
+ frame_size += priv->padding;
+
+ frame_size = (frame_size & ~(INCREMENTAL_BUFFER_SIZE - 1)) +
+ INCREMENTAL_BUFFER_SIZE;
+
+ priv->rx_buffer_size = frame_size;
+}
+
+static void gfar_mac_rx_config(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 rctrl = 0;
+
if (priv->rx_filer_enable) {
rctrl |= RCTRL_FILREN;
/* Program the RIR0 reg with the required distribution */
- gfar_write(&regs->rir0, DEFAULT_RIR0);
+ if (priv->poll_mode == GFAR_SQ_POLLING)
+ gfar_write(&regs->rir0, DEFAULT_2RXQ_RIR0);
+ else /* GFAR_MQ_POLLING */
+ gfar_write(&regs->rir0, DEFAULT_8RXQ_RIR0);
}
/* Restore PROMISC mode */
- if (ndev->flags & IFF_PROMISC)
+ if (priv->ndev->flags & IFF_PROMISC)
rctrl |= RCTRL_PROM;
- if (ndev->features & NETIF_F_RXCSUM) {
+ if (priv->ndev->features & NETIF_F_RXCSUM)
rctrl |= RCTRL_CHECKSUMMING;
- priv->uses_rxfcb = 1;
- }
- if (priv->extended_hash) {
- rctrl |= RCTRL_EXTHASH;
-
- gfar_clear_exact_match(ndev);
- rctrl |= RCTRL_EMEN;
- }
+ if (priv->extended_hash)
+ rctrl |= RCTRL_EXTHASH | RCTRL_EMEN;
if (priv->padding) {
rctrl &= ~RCTRL_PAL_MASK;
rctrl |= RCTRL_PADDING(priv->padding);
}
- /* Insert receive time stamps into padding alignment bytes */
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER) {
- rctrl &= ~RCTRL_PAL_MASK;
- rctrl |= RCTRL_PADDING(8);
- priv->padding = 8;
- }
-
/* Enable HW time stamping if requested from user space */
- if (priv->hwts_rx_en) {
+ if (priv->hwts_rx_en)
rctrl |= RCTRL_PRSDEP_INIT | RCTRL_TS_ENABLE;
- priv->uses_rxfcb = 1;
- }
- if (ndev->features & NETIF_F_HW_VLAN_CTAG_RX) {
+ if (priv->ndev->features & NETIF_F_HW_VLAN_CTAG_RX)
rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
- priv->uses_rxfcb = 1;
- }
/* Init rctrl based on our settings */
gfar_write(&regs->rctrl, rctrl);
+}
+
+static void gfar_mac_tx_config(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 tctrl = 0;
- if (ndev->features & NETIF_F_IP_CSUM)
+ if (priv->ndev->features & NETIF_F_IP_CSUM)
tctrl |= TCTRL_INIT_CSUM;
if (priv->prio_sched_en)
@@ -408,30 +411,51 @@ static void gfar_init_mac(struct net_device *ndev)
gfar_write(&regs->tr47wt, DEFAULT_WRRS_WEIGHT);
}
- gfar_write(&regs->tctrl, tctrl);
+ if (priv->ndev->features & NETIF_F_HW_VLAN_CTAG_TX)
+ tctrl |= TCTRL_VLINS;
- /* Set the extraction length and index */
- attrs = ATTRELI_EL(priv->rx_stash_size) |
- ATTRELI_EI(priv->rx_stash_index);
+ gfar_write(&regs->tctrl, tctrl);
+}
- gfar_write(&regs->attreli, attrs);
+static void gfar_configure_coalescing(struct gfar_private *priv,
+ unsigned long tx_mask, unsigned long rx_mask)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 __iomem *baddr;
- /* Start with defaults, and add stashing or locking
- * depending on the approprate variables
- */
- attrs = ATTR_INIT_SETTINGS;
+ if (priv->mode == MQ_MG_MODE) {
+ int i = 0;
- if (priv->bd_stash_en)
- attrs |= ATTR_BDSTASH;
+ baddr = &regs->txic0;
+ for_each_set_bit(i, &tx_mask, priv->num_tx_queues) {
+ gfar_write(baddr + i, 0);
+ if (likely(priv->tx_queue[i]->txcoalescing))
+ gfar_write(baddr + i, priv->tx_queue[i]->txic);
+ }
- if (priv->rx_stash_size != 0)
- attrs |= ATTR_BUFSTASH;
+ baddr = &regs->rxic0;
+ for_each_set_bit(i, &rx_mask, priv->num_rx_queues) {
+ gfar_write(baddr + i, 0);
+ if (likely(priv->rx_queue[i]->rxcoalescing))
+ gfar_write(baddr + i, priv->rx_queue[i]->rxic);
+ }
+ } else {
+ /* Backward compatible case -- even if we enable
+ * multiple queues, there's only single reg to program
+ */
+ gfar_write(&regs->txic, 0);
+ if (likely(priv->tx_queue[0]->txcoalescing))
+ gfar_write(&regs->txic, priv->tx_queue[0]->txic);
- gfar_write(&regs->attr, attrs);
+ gfar_write(&regs->rxic, 0);
+ if (unlikely(priv->rx_queue[0]->rxcoalescing))
+ gfar_write(&regs->rxic, priv->rx_queue[0]->rxic);
+ }
+}
- gfar_write(&regs->fifo_tx_thr, priv->fifo_threshold);
- gfar_write(&regs->fifo_tx_starve, priv->fifo_starve);
- gfar_write(&regs->fifo_tx_starve_shutoff, priv->fifo_starve_off);
+void gfar_configure_coalescing_all(struct gfar_private *priv)
+{
+ gfar_configure_coalescing(priv, 0xFF, 0xFF);
}
static struct net_device_stats *gfar_get_stats(struct net_device *dev)
@@ -479,12 +503,27 @@ static const struct net_device_ops gfar_netdev_ops = {
#endif
};
-void lock_rx_qs(struct gfar_private *priv)
+static void gfar_ints_disable(struct gfar_private *priv)
{
int i;
+ for (i = 0; i < priv->num_grps; i++) {
+ struct gfar __iomem *regs = priv->gfargrp[i].regs;
+ /* Clear IEVENT */
+ gfar_write(&regs->ievent, IEVENT_INIT_CLEAR);
- for (i = 0; i < priv->num_rx_queues; i++)
- spin_lock(&priv->rx_queue[i]->rxlock);
+ /* Initialize IMASK */
+ gfar_write(&regs->imask, IMASK_INIT_CLEAR);
+ }
+}
+
+static void gfar_ints_enable(struct gfar_private *priv)
+{
+ int i;
+ for (i = 0; i < priv->num_grps; i++) {
+ struct gfar __iomem *regs = priv->gfargrp[i].regs;
+ /* Unmask the interrupts we look for */
+ gfar_write(&regs->imask, IMASK_DEFAULT);
+ }
}
void lock_tx_qs(struct gfar_private *priv)
@@ -495,23 +534,50 @@ void lock_tx_qs(struct gfar_private *priv)
spin_lock(&priv->tx_queue[i]->txlock);
}
-void unlock_rx_qs(struct gfar_private *priv)
+void unlock_tx_qs(struct gfar_private *priv)
{
int i;
- for (i = 0; i < priv->num_rx_queues; i++)
- spin_unlock(&priv->rx_queue[i]->rxlock);
+ for (i = 0; i < priv->num_tx_queues; i++)
+ spin_unlock(&priv->tx_queue[i]->txlock);
}
-void unlock_tx_qs(struct gfar_private *priv)
+static int gfar_alloc_tx_queues(struct gfar_private *priv)
{
int i;
- for (i = 0; i < priv->num_tx_queues; i++)
- spin_unlock(&priv->tx_queue[i]->txlock);
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ priv->tx_queue[i] = kzalloc(sizeof(struct gfar_priv_tx_q),
+ GFP_KERNEL);
+ if (!priv->tx_queue[i])
+ return -ENOMEM;
+
+ priv->tx_queue[i]->tx_skbuff = NULL;
+ priv->tx_queue[i]->qindex = i;
+ priv->tx_queue[i]->dev = priv->ndev;
+ spin_lock_init(&(priv->tx_queue[i]->txlock));
+ }
+ return 0;
}
-static void free_tx_pointers(struct gfar_private *priv)
+static int gfar_alloc_rx_queues(struct gfar_private *priv)
+{
+ int i;
+
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i] = kzalloc(sizeof(struct gfar_priv_rx_q),
+ GFP_KERNEL);
+ if (!priv->rx_queue[i])
+ return -ENOMEM;
+
+ priv->rx_queue[i]->rx_skbuff = NULL;
+ priv->rx_queue[i]->qindex = i;
+ priv->rx_queue[i]->dev = priv->ndev;
+ }
+ return 0;
+}
+
+static void gfar_free_tx_queues(struct gfar_private *priv)
{
int i;
@@ -519,7 +585,7 @@ static void free_tx_pointers(struct gfar_private *priv)
kfree(priv->tx_queue[i]);
}
-static void free_rx_pointers(struct gfar_private *priv)
+static void gfar_free_rx_queues(struct gfar_private *priv)
{
int i;
@@ -553,23 +619,26 @@ static void disable_napi(struct gfar_private *priv)
{
int i;
- for (i = 0; i < priv->num_grps; i++)
- napi_disable(&priv->gfargrp[i].napi);
+ for (i = 0; i < priv->num_grps; i++) {
+ napi_disable(&priv->gfargrp[i].napi_rx);
+ napi_disable(&priv->gfargrp[i].napi_tx);
+ }
}
static void enable_napi(struct gfar_private *priv)
{
int i;
- for (i = 0; i < priv->num_grps; i++)
- napi_enable(&priv->gfargrp[i].napi);
+ for (i = 0; i < priv->num_grps; i++) {
+ napi_enable(&priv->gfargrp[i].napi_rx);
+ napi_enable(&priv->gfargrp[i].napi_tx);
+ }
}
static int gfar_parse_group(struct device_node *np,
struct gfar_private *priv, const char *model)
{
struct gfar_priv_grp *grp = &priv->gfargrp[priv->num_grps];
- u32 *queue_mask;
int i;
for (i = 0; i < GFAR_NUM_IRQS; i++) {
@@ -598,16 +667,52 @@ static int gfar_parse_group(struct device_node *np,
grp->priv = priv;
spin_lock_init(&grp->grplock);
if (priv->mode == MQ_MG_MODE) {
- queue_mask = (u32 *)of_get_property(np, "fsl,rx-bit-map", NULL);
- grp->rx_bit_map = queue_mask ?
- *queue_mask : (DEFAULT_MAPPING >> priv->num_grps);
- queue_mask = (u32 *)of_get_property(np, "fsl,tx-bit-map", NULL);
- grp->tx_bit_map = queue_mask ?
- *queue_mask : (DEFAULT_MAPPING >> priv->num_grps);
+ u32 *rxq_mask, *txq_mask;
+ rxq_mask = (u32 *)of_get_property(np, "fsl,rx-bit-map", NULL);
+ txq_mask = (u32 *)of_get_property(np, "fsl,tx-bit-map", NULL);
+
+ if (priv->poll_mode == GFAR_SQ_POLLING) {
+ /* One Q per interrupt group: Q0 to G0, Q1 to G1 */
+ grp->rx_bit_map = (DEFAULT_MAPPING >> priv->num_grps);
+ grp->tx_bit_map = (DEFAULT_MAPPING >> priv->num_grps);
+ } else { /* GFAR_MQ_POLLING */
+ grp->rx_bit_map = rxq_mask ?
+ *rxq_mask : (DEFAULT_MAPPING >> priv->num_grps);
+ grp->tx_bit_map = txq_mask ?
+ *txq_mask : (DEFAULT_MAPPING >> priv->num_grps);
+ }
} else {
grp->rx_bit_map = 0xFF;
grp->tx_bit_map = 0xFF;
}
+
+ /* bit_map's MSB is q0 (from q0 to q7) but, for_each_set_bit parses
+ * right to left, so we need to revert the 8 bits to get the q index
+ */
+ grp->rx_bit_map = bitrev8(grp->rx_bit_map);
+ grp->tx_bit_map = bitrev8(grp->tx_bit_map);
+
+ /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values,
+ * also assign queues to groups
+ */
+ for_each_set_bit(i, &grp->rx_bit_map, priv->num_rx_queues) {
+ if (!grp->rx_queue)
+ grp->rx_queue = priv->rx_queue[i];
+ grp->num_rx_queues++;
+ grp->rstat |= (RSTAT_CLEAR_RHALT >> i);
+ priv->rqueue |= ((RQUEUE_EN0 | RQUEUE_EX0) >> i);
+ priv->rx_queue[i]->grp = grp;
+ }
+
+ for_each_set_bit(i, &grp->tx_bit_map, priv->num_tx_queues) {
+ if (!grp->tx_queue)
+ grp->tx_queue = priv->tx_queue[i];
+ grp->num_tx_queues++;
+ grp->tstat |= (TSTAT_CLEAR_THALT >> i);
+ priv->tqueue |= (TQUEUE_EN0 >> i);
+ priv->tx_queue[i]->grp = grp;
+ }
+
priv->num_grps++;
return 0;
@@ -628,13 +733,45 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
const u32 *stash_idx;
unsigned int num_tx_qs, num_rx_qs;
u32 *tx_queues, *rx_queues;
+ unsigned short mode, poll_mode;
if (!np || !of_device_is_available(np))
return -ENODEV;
- /* parse the num of tx and rx queues */
+ if (of_device_is_compatible(np, "fsl,etsec2")) {
+ mode = MQ_MG_MODE;
+ poll_mode = GFAR_SQ_POLLING;
+ } else {
+ mode = SQ_SG_MODE;
+ poll_mode = GFAR_SQ_POLLING;
+ }
+
+ /* parse the num of HW tx and rx queues */
tx_queues = (u32 *)of_get_property(np, "fsl,num_tx_queues", NULL);
- num_tx_qs = tx_queues ? *tx_queues : 1;
+ rx_queues = (u32 *)of_get_property(np, "fsl,num_rx_queues", NULL);
+
+ if (mode == SQ_SG_MODE) {
+ num_tx_qs = 1;
+ num_rx_qs = 1;
+ } else { /* MQ_MG_MODE */
+ /* get the actual number of supported groups */
+ unsigned int num_grps = of_get_available_child_count(np);
+
+ if (num_grps == 0 || num_grps > MAXGROUPS) {
+ dev_err(&ofdev->dev, "Invalid # of int groups(%d)\n",
+ num_grps);
+ pr_err("Cannot do alloc_etherdev, aborting\n");
+ return -EINVAL;
+ }
+
+ if (poll_mode == GFAR_SQ_POLLING) {
+ num_tx_qs = num_grps; /* one txq per int group */
+ num_rx_qs = num_grps; /* one rxq per int group */
+ } else { /* GFAR_MQ_POLLING */
+ num_tx_qs = tx_queues ? *tx_queues : 1;
+ num_rx_qs = rx_queues ? *rx_queues : 1;
+ }
+ }
if (num_tx_qs > MAX_TX_QS) {
pr_err("num_tx_qs(=%d) greater than MAX_TX_QS(=%d)\n",
@@ -643,9 +780,6 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
return -EINVAL;
}
- rx_queues = (u32 *)of_get_property(np, "fsl,num_rx_queues", NULL);
- num_rx_qs = rx_queues ? *rx_queues : 1;
-
if (num_rx_qs > MAX_RX_QS) {
pr_err("num_rx_qs(=%d) greater than MAX_RX_QS(=%d)\n",
num_rx_qs, MAX_RX_QS);
@@ -661,10 +795,20 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
priv = netdev_priv(dev);
priv->ndev = dev;
+ priv->mode = mode;
+ priv->poll_mode = poll_mode;
+
priv->num_tx_queues = num_tx_qs;
netif_set_real_num_rx_queues(dev, num_rx_qs);
priv->num_rx_queues = num_rx_qs;
- priv->num_grps = 0x0;
+
+ err = gfar_alloc_tx_queues(priv);
+ if (err)
+ goto tx_alloc_failed;
+
+ err = gfar_alloc_rx_queues(priv);
+ if (err)
+ goto rx_alloc_failed;
/* Init Rx queue filer rule set linked list */
INIT_LIST_HEAD(&priv->rx_list.list);
@@ -677,52 +821,18 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
priv->gfargrp[i].regs = NULL;
/* Parse and initialize group specific information */
- if (of_device_is_compatible(np, "fsl,etsec2")) {
- priv->mode = MQ_MG_MODE;
+ if (priv->mode == MQ_MG_MODE) {
for_each_child_of_node(np, child) {
err = gfar_parse_group(child, priv, model);
if (err)
goto err_grp_init;
}
- } else {
- priv->mode = SQ_SG_MODE;
+ } else { /* SQ_SG_MODE */
err = gfar_parse_group(np, priv, model);
if (err)
goto err_grp_init;
}
- for (i = 0; i < priv->num_tx_queues; i++)
- priv->tx_queue[i] = NULL;
- for (i = 0; i < priv->num_rx_queues; i++)
- priv->rx_queue[i] = NULL;
-
- for (i = 0; i < priv->num_tx_queues; i++) {
- priv->tx_queue[i] = kzalloc(sizeof(struct gfar_priv_tx_q),
- GFP_KERNEL);
- if (!priv->tx_queue[i]) {
- err = -ENOMEM;
- goto tx_alloc_failed;
- }
- priv->tx_queue[i]->tx_skbuff = NULL;
- priv->tx_queue[i]->qindex = i;
- priv->tx_queue[i]->dev = dev;
- spin_lock_init(&(priv->tx_queue[i]->txlock));
- }
-
- for (i = 0; i < priv->num_rx_queues; i++) {
- priv->rx_queue[i] = kzalloc(sizeof(struct gfar_priv_rx_q),
- GFP_KERNEL);
- if (!priv->rx_queue[i]) {
- err = -ENOMEM;
- goto rx_alloc_failed;
- }
- priv->rx_queue[i]->rx_skbuff = NULL;
- priv->rx_queue[i]->qindex = i;
- priv->rx_queue[i]->dev = dev;
- spin_lock_init(&(priv->rx_queue[i]->rxlock));
- }
-
-
stash = of_get_property(np, "bd-stash", NULL);
if (stash) {
@@ -749,17 +859,16 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
memcpy(dev->dev_addr, mac_addr, ETH_ALEN);
if (model && !strcasecmp(model, "TSEC"))
- priv->device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
+ priv->device_flags |= FSL_GIANFAR_DEV_HAS_GIGABIT |
FSL_GIANFAR_DEV_HAS_COALESCE |
FSL_GIANFAR_DEV_HAS_RMON |
FSL_GIANFAR_DEV_HAS_MULTI_INTR;
if (model && !strcasecmp(model, "eTSEC"))
- priv->device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
+ priv->device_flags |= FSL_GIANFAR_DEV_HAS_GIGABIT |
FSL_GIANFAR_DEV_HAS_COALESCE |
FSL_GIANFAR_DEV_HAS_RMON |
FSL_GIANFAR_DEV_HAS_MULTI_INTR |
- FSL_GIANFAR_DEV_HAS_PADDING |
FSL_GIANFAR_DEV_HAS_CSUM |
FSL_GIANFAR_DEV_HAS_VLAN |
FSL_GIANFAR_DEV_HAS_MAGIC_PACKET |
@@ -784,12 +893,12 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
return 0;
-rx_alloc_failed:
- free_rx_pointers(priv);
-tx_alloc_failed:
- free_tx_pointers(priv);
err_grp_init:
unmap_group_regs(priv);
+rx_alloc_failed:
+ gfar_free_rx_queues(priv);
+tx_alloc_failed:
+ gfar_free_tx_queues(priv);
free_gfar_dev(priv);
return err;
}
@@ -822,18 +931,16 @@ static int gfar_hwtstamp_set(struct net_device *netdev, struct ifreq *ifr)
switch (config.rx_filter) {
case HWTSTAMP_FILTER_NONE:
if (priv->hwts_rx_en) {
- stop_gfar(netdev);
priv->hwts_rx_en = 0;
- startup_gfar(netdev);
+ reset_gfar(netdev);
}
break;
default:
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER))
return -ERANGE;
if (!priv->hwts_rx_en) {
- stop_gfar(netdev);
priv->hwts_rx_en = 1;
- startup_gfar(netdev);
+ reset_gfar(netdev);
}
config.rx_filter = HWTSTAMP_FILTER_ALL;
break;
@@ -875,19 +982,6 @@ static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
return phy_mii_ioctl(priv->phydev, rq, cmd);
}
-static unsigned int reverse_bitmap(unsigned int bit_map, unsigned int max_qs)
-{
- unsigned int new_bit_map = 0x0;
- int mask = 0x1 << (max_qs - 1), i;
-
- for (i = 0; i < max_qs; i++) {
- if (bit_map & mask)
- new_bit_map = new_bit_map + (1 << i);
- mask = mask >> 0x1;
- }
- return new_bit_map;
-}
-
static u32 cluster_entry_per_class(struct gfar_private *priv, u32 rqfar,
u32 class)
{
@@ -1005,99 +1099,140 @@ static void gfar_detect_errata(struct gfar_private *priv)
priv->errata);
}
-/* Set up the ethernet device structure, private data,
- * and anything else we need before we start
- */
-static int gfar_probe(struct platform_device *ofdev)
+void gfar_mac_reset(struct gfar_private *priv)
{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
- struct net_device *dev = NULL;
- struct gfar_private *priv = NULL;
- struct gfar __iomem *regs = NULL;
- int err = 0, i, grp_idx = 0;
- u32 rstat = 0, tstat = 0, rqueue = 0, tqueue = 0;
- u32 isrg = 0;
- u32 __iomem *baddr;
-
- err = gfar_of_init(ofdev, &dev);
-
- if (err)
- return err;
-
- priv = netdev_priv(dev);
- priv->ndev = dev;
- priv->ofdev = ofdev;
- priv->dev = &ofdev->dev;
- SET_NETDEV_DEV(dev, &ofdev->dev);
-
- spin_lock_init(&priv->bflock);
- INIT_WORK(&priv->reset_task, gfar_reset_task);
-
- platform_set_drvdata(ofdev, priv);
- regs = priv->gfargrp[0].regs;
-
- gfar_detect_errata(priv);
-
- /* Stop the DMA engine now, in case it was running before
- * (The firmware could have used it, and left it running).
- */
- gfar_halt(dev);
/* Reset MAC layer */
gfar_write(&regs->maccfg1, MACCFG1_SOFT_RESET);
/* We need to delay at least 3 TX clocks */
- udelay(2);
+ udelay(3);
- tempval = 0;
- if (!priv->pause_aneg_en && priv->tx_pause_en)
- tempval |= MACCFG1_TX_FLOW;
- if (!priv->pause_aneg_en && priv->rx_pause_en)
- tempval |= MACCFG1_RX_FLOW;
/* the soft reset bit is not self-resetting, so we need to
* clear it before resuming normal operation
*/
- gfar_write(&regs->maccfg1, tempval);
+ gfar_write(&regs->maccfg1, 0);
+
+ udelay(3);
+
+ /* Compute rx_buff_size based on config flags */
+ gfar_rx_buff_size_config(priv);
+
+ /* Initialize the max receive frame/buffer lengths */
+ gfar_write(&regs->maxfrm, priv->rx_buffer_size);
+ gfar_write(&regs->mrblr, priv->rx_buffer_size);
+
+ /* Initialize the Minimum Frame Length Register */
+ gfar_write(&regs->minflr, MINFLR_INIT_SETTINGS);
/* Initialize MACCFG2. */
tempval = MACCFG2_INIT_SETTINGS;
- if (gfar_has_errata(priv, GFAR_ERRATA_74))
+
+ /* If the mtu is larger than the max size for standard
+ * ethernet frames (ie, a jumbo frame), then set maccfg2
+ * to allow huge frames, and to check the length
+ */
+ if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE ||
+ gfar_has_errata(priv, GFAR_ERRATA_74))
tempval |= MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK;
+
gfar_write(&regs->maccfg2, tempval);
+ /* Clear mac addr hash registers */
+ gfar_write(&regs->igaddr0, 0);
+ gfar_write(&regs->igaddr1, 0);
+ gfar_write(&regs->igaddr2, 0);
+ gfar_write(&regs->igaddr3, 0);
+ gfar_write(&regs->igaddr4, 0);
+ gfar_write(&regs->igaddr5, 0);
+ gfar_write(&regs->igaddr6, 0);
+ gfar_write(&regs->igaddr7, 0);
+
+ gfar_write(&regs->gaddr0, 0);
+ gfar_write(&regs->gaddr1, 0);
+ gfar_write(&regs->gaddr2, 0);
+ gfar_write(&regs->gaddr3, 0);
+ gfar_write(&regs->gaddr4, 0);
+ gfar_write(&regs->gaddr5, 0);
+ gfar_write(&regs->gaddr6, 0);
+ gfar_write(&regs->gaddr7, 0);
+
+ if (priv->extended_hash)
+ gfar_clear_exact_match(priv->ndev);
+
+ gfar_mac_rx_config(priv);
+
+ gfar_mac_tx_config(priv);
+
+ gfar_set_mac_address(priv->ndev);
+
+ gfar_set_multi(priv->ndev);
+
+ /* clear ievent and imask before configuring coalescing */
+ gfar_ints_disable(priv);
+
+ /* Configure the coalescing support */
+ gfar_configure_coalescing_all(priv);
+}
+
+static void gfar_hw_init(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 attrs;
+
+ /* Stop the DMA engine now, in case it was running before
+ * (The firmware could have used it, and left it running).
+ */
+ gfar_halt(priv);
+
+ gfar_mac_reset(priv);
+
+ /* Zero out the rmon mib registers if it has them */
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
+ memset_io(&(regs->rmon), 0, sizeof(struct rmon_mib));
+
+ /* Mask off the CAM interrupts */
+ gfar_write(&regs->rmon.cam1, 0xffffffff);
+ gfar_write(&regs->rmon.cam2, 0xffffffff);
+ }
+
/* Initialize ECNTRL */
gfar_write(&regs->ecntrl, ECNTRL_INIT_SETTINGS);
- /* Set the dev->base_addr to the gfar reg region */
- dev->base_addr = (unsigned long) regs;
+ /* Set the extraction length and index */
+ attrs = ATTRELI_EL(priv->rx_stash_size) |
+ ATTRELI_EI(priv->rx_stash_index);
- /* Fill in the dev structure */
- dev->watchdog_timeo = TX_TIMEOUT;
- dev->mtu = 1500;
- dev->netdev_ops = &gfar_netdev_ops;
- dev->ethtool_ops = &gfar_ethtool_ops;
+ gfar_write(&regs->attreli, attrs);
- /* Register for napi ...We are registering NAPI for each grp */
- if (priv->mode == SQ_SG_MODE)
- netif_napi_add(dev, &priv->gfargrp[0].napi, gfar_poll_sq,
- GFAR_DEV_WEIGHT);
- else
- for (i = 0; i < priv->num_grps; i++)
- netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll,
- GFAR_DEV_WEIGHT);
+ /* Start with defaults, and add stashing
+ * depending on driver parameters
+ */
+ attrs = ATTR_INIT_SETTINGS;
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
- dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
- NETIF_F_RXCSUM;
- dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG |
- NETIF_F_RXCSUM | NETIF_F_HIGHDMA;
- }
+ if (priv->bd_stash_en)
+ attrs |= ATTR_BDSTASH;
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
- dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX |
- NETIF_F_HW_VLAN_CTAG_RX;
- dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
- }
+ if (priv->rx_stash_size != 0)
+ attrs |= ATTR_BUFSTASH;
+
+ gfar_write(&regs->attr, attrs);
+
+ /* FIFO configs */
+ gfar_write(&regs->fifo_tx_thr, DEFAULT_FIFO_TX_THR);
+ gfar_write(&regs->fifo_tx_starve, DEFAULT_FIFO_TX_STARVE);
+ gfar_write(&regs->fifo_tx_starve_shutoff, DEFAULT_FIFO_TX_STARVE_OFF);
+
+ /* Program the interrupt steering regs, only for MG devices */
+ if (priv->num_grps > 1)
+ gfar_write_isrg(priv);
+}
+
+static void __init gfar_init_addr_hash_table(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_EXTENDED_HASH) {
priv->extended_hash = 1;
@@ -1133,68 +1268,81 @@ static int gfar_probe(struct platform_device *ofdev)
priv->hash_regs[6] = &regs->gaddr6;
priv->hash_regs[7] = &regs->gaddr7;
}
+}
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_PADDING)
- priv->padding = DEFAULT_PADDING;
- else
- priv->padding = 0;
+/* Set up the ethernet device structure, private data,
+ * and anything else we need before we start
+ */
+static int gfar_probe(struct platform_device *ofdev)
+{
+ struct net_device *dev = NULL;
+ struct gfar_private *priv = NULL;
+ int err = 0, i;
- if (dev->features & NETIF_F_IP_CSUM ||
- priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)
- dev->needed_headroom = GMAC_FCB_LEN;
+ err = gfar_of_init(ofdev, &dev);
- /* Program the isrg regs only if number of grps > 1 */
- if (priv->num_grps > 1) {
- baddr = &regs->isrg0;
- for (i = 0; i < priv->num_grps; i++) {
- isrg |= (priv->gfargrp[i].rx_bit_map << ISRG_SHIFT_RX);
- isrg |= (priv->gfargrp[i].tx_bit_map << ISRG_SHIFT_TX);
- gfar_write(baddr, isrg);
- baddr++;
- isrg = 0x0;
+ if (err)
+ return err;
+
+ priv = netdev_priv(dev);
+ priv->ndev = dev;
+ priv->ofdev = ofdev;
+ priv->dev = &ofdev->dev;
+ SET_NETDEV_DEV(dev, &ofdev->dev);
+
+ spin_lock_init(&priv->bflock);
+ INIT_WORK(&priv->reset_task, gfar_reset_task);
+
+ platform_set_drvdata(ofdev, priv);
+
+ gfar_detect_errata(priv);
+
+ /* Set the dev->base_addr to the gfar reg region */
+ dev->base_addr = (unsigned long) priv->gfargrp[0].regs;
+
+ /* Fill in the dev structure */
+ dev->watchdog_timeo = TX_TIMEOUT;
+ dev->mtu = 1500;
+ dev->netdev_ops = &gfar_netdev_ops;
+ dev->ethtool_ops = &gfar_ethtool_ops;
+
+ /* Register for napi ...We are registering NAPI for each grp */
+ for (i = 0; i < priv->num_grps; i++) {
+ 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,
+ 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,
+ gfar_poll_tx, 2);
}
}
- /* Need to reverse the bit maps as bit_map's MSB is q0
- * but, for_each_set_bit parses from right to left, which
- * basically reverses the queue numbers
- */
- for (i = 0; i< priv->num_grps; i++) {
- priv->gfargrp[i].tx_bit_map =
- reverse_bitmap(priv->gfargrp[i].tx_bit_map, MAX_TX_QS);
- priv->gfargrp[i].rx_bit_map =
- reverse_bitmap(priv->gfargrp[i].rx_bit_map, MAX_RX_QS);
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
+ dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
+ NETIF_F_RXCSUM;
+ dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG |
+ NETIF_F_RXCSUM | NETIF_F_HIGHDMA;
}
- /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values,
- * also assign queues to groups
- */
- for (grp_idx = 0; grp_idx < priv->num_grps; grp_idx++) {
- priv->gfargrp[grp_idx].num_rx_queues = 0x0;
-
- for_each_set_bit(i, &priv->gfargrp[grp_idx].rx_bit_map,
- priv->num_rx_queues) {
- priv->gfargrp[grp_idx].num_rx_queues++;
- priv->rx_queue[i]->grp = &priv->gfargrp[grp_idx];
- rstat = rstat | (RSTAT_CLEAR_RHALT >> i);
- rqueue = rqueue | ((RQUEUE_EN0 | RQUEUE_EX0) >> i);
- }
- priv->gfargrp[grp_idx].num_tx_queues = 0x0;
-
- for_each_set_bit(i, &priv->gfargrp[grp_idx].tx_bit_map,
- priv->num_tx_queues) {
- priv->gfargrp[grp_idx].num_tx_queues++;
- priv->tx_queue[i]->grp = &priv->gfargrp[grp_idx];
- tstat = tstat | (TSTAT_CLEAR_THALT >> i);
- tqueue = tqueue | (TQUEUE_EN0 >> i);
- }
- priv->gfargrp[grp_idx].rstat = rstat;
- priv->gfargrp[grp_idx].tstat = tstat;
- rstat = tstat =0;
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
+ dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX |
+ NETIF_F_HW_VLAN_CTAG_RX;
+ dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
}
- gfar_write(&regs->rqueue, rqueue);
- gfar_write(&regs->tqueue, tqueue);
+ gfar_init_addr_hash_table(priv);
+
+ /* Insert receive time stamps into padding alignment bytes */
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)
+ priv->padding = 8;
+
+ if (dev->features & NETIF_F_IP_CSUM ||
+ priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)
+ dev->needed_headroom = GMAC_FCB_LEN;
priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
@@ -1220,8 +1368,9 @@ static int gfar_probe(struct platform_device *ofdev)
if (priv->num_tx_queues == 1)
priv->prio_sched_en = 1;
- /* Carrier starts down, phylib will bring it up */
- netif_carrier_off(dev);
+ set_bit(GFAR_DOWN, &priv->state);
+
+ gfar_hw_init(priv);
err = register_netdev(dev);
@@ -1230,6 +1379,9 @@ static int gfar_probe(struct platform_device *ofdev)
goto register_fail;
}
+ /* Carrier starts down, phylib will bring it up */
+ netif_carrier_off(dev);
+
device_init_wakeup(&dev->dev,
priv->device_flags &
FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
@@ -1251,9 +1403,6 @@ static int gfar_probe(struct platform_device *ofdev)
/* Initialize the filer table */
gfar_init_filer_table(priv);
- /* Create all the sysfs files */
- gfar_init_sysfs(dev);
-
/* Print out the device info */
netdev_info(dev, "mac: %pM\n", dev->dev_addr);
@@ -1272,8 +1421,8 @@ static int gfar_probe(struct platform_device *ofdev)
register_fail:
unmap_group_regs(priv);
- free_tx_pointers(priv);
- free_rx_pointers(priv);
+ gfar_free_rx_queues(priv);
+ gfar_free_tx_queues(priv);
if (priv->phy_node)
of_node_put(priv->phy_node);
if (priv->tbi_node)
@@ -1293,6 +1442,8 @@ static int gfar_remove(struct platform_device *ofdev)
unregister_netdev(priv->ndev);
unmap_group_regs(priv);
+ gfar_free_rx_queues(priv);
+ gfar_free_tx_queues(priv);
free_gfar_dev(priv);
return 0;
@@ -1318,9 +1469,8 @@ static int gfar_suspend(struct device *dev)
local_irq_save(flags);
lock_tx_qs(priv);
- lock_rx_qs(priv);
- gfar_halt_nodisable(ndev);
+ gfar_halt_nodisable(priv);
/* Disable Tx, and Rx if wake-on-LAN is disabled. */
tempval = gfar_read(&regs->maccfg1);
@@ -1332,7 +1482,6 @@ static int gfar_suspend(struct device *dev)
gfar_write(&regs->maccfg1, tempval);
- unlock_rx_qs(priv);
unlock_tx_qs(priv);
local_irq_restore(flags);
@@ -1378,15 +1527,13 @@ static int gfar_resume(struct device *dev)
*/
local_irq_save(flags);
lock_tx_qs(priv);
- lock_rx_qs(priv);
tempval = gfar_read(&regs->maccfg2);
tempval &= ~MACCFG2_MPEN;
gfar_write(&regs->maccfg2, tempval);
- gfar_start(ndev);
+ gfar_start(priv);
- unlock_rx_qs(priv);
unlock_tx_qs(priv);
local_irq_restore(flags);
@@ -1413,10 +1560,11 @@ static int gfar_restore(struct device *dev)
return -ENOMEM;
}
- init_registers(ndev);
- gfar_set_mac_address(ndev);
- gfar_init_mac(ndev);
- gfar_start(ndev);
+ gfar_mac_reset(priv);
+
+ gfar_init_tx_rx_base(priv);
+
+ gfar_start(priv);
priv->oldlink = 0;
priv->oldspeed = 0;
@@ -1574,57 +1722,6 @@ static void gfar_configure_serdes(struct net_device *dev)
BMCR_SPEED1000);
}
-static void init_registers(struct net_device *dev)
-{
- struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = NULL;
- int i;
-
- for (i = 0; i < priv->num_grps; i++) {
- regs = priv->gfargrp[i].regs;
- /* Clear IEVENT */
- gfar_write(&regs->ievent, IEVENT_INIT_CLEAR);
-
- /* Initialize IMASK */
- gfar_write(&regs->imask, IMASK_INIT_CLEAR);
- }
-
- regs = priv->gfargrp[0].regs;
- /* Init hash registers to zero */
- gfar_write(&regs->igaddr0, 0);
- gfar_write(&regs->igaddr1, 0);
- gfar_write(&regs->igaddr2, 0);
- gfar_write(&regs->igaddr3, 0);
- gfar_write(&regs->igaddr4, 0);
- gfar_write(&regs->igaddr5, 0);
- gfar_write(&regs->igaddr6, 0);
- gfar_write(&regs->igaddr7, 0);
-
- gfar_write(&regs->gaddr0, 0);
- gfar_write(&regs->gaddr1, 0);
- gfar_write(&regs->gaddr2, 0);
- gfar_write(&regs->gaddr3, 0);
- gfar_write(&regs->gaddr4, 0);
- gfar_write(&regs->gaddr5, 0);
- gfar_write(&regs->gaddr6, 0);
- gfar_write(&regs->gaddr7, 0);
-
- /* Zero out the rmon mib registers if it has them */
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
- memset_io(&(regs->rmon), 0, sizeof (struct rmon_mib));
-
- /* Mask off the CAM interrupts */
- gfar_write(&regs->rmon.cam1, 0xffffffff);
- gfar_write(&regs->rmon.cam2, 0xffffffff);
- }
-
- /* Initialize the max receive buffer length */
- gfar_write(&regs->mrblr, priv->rx_buffer_size);
-
- /* Initialize the Minimum Frame Length Register */
- gfar_write(&regs->minflr, MINFLR_INIT_SETTINGS);
-}
-
static int __gfar_is_rx_idle(struct gfar_private *priv)
{
u32 res;
@@ -1648,23 +1745,13 @@ static int __gfar_is_rx_idle(struct gfar_private *priv)
}
/* Halt the receive and transmit queues */
-static void gfar_halt_nodisable(struct net_device *dev)
+static void gfar_halt_nodisable(struct gfar_private *priv)
{
- struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = NULL;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
- int i;
-
- for (i = 0; i < priv->num_grps; i++) {
- regs = priv->gfargrp[i].regs;
- /* Mask all interrupts */
- gfar_write(&regs->imask, IMASK_INIT_CLEAR);
- /* Clear all interrupts */
- gfar_write(&regs->ievent, IEVENT_INIT_CLEAR);
- }
+ gfar_ints_disable(priv);
- regs = priv->gfargrp[0].regs;
/* Stop the DMA, and wait for it to stop */
tempval = gfar_read(&regs->dmactrl);
if ((tempval & (DMACTRL_GRS | DMACTRL_GTS)) !=
@@ -1685,56 +1772,41 @@ static void gfar_halt_nodisable(struct net_device *dev)
}
/* Halt the receive and transmit queues */
-void gfar_halt(struct net_device *dev)
+void gfar_halt(struct gfar_private *priv)
{
- struct gfar_private *priv = netdev_priv(dev);
struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
- gfar_halt_nodisable(dev);
+ /* Dissable the Rx/Tx hw queues */
+ gfar_write(&regs->rqueue, 0);
+ gfar_write(&regs->tqueue, 0);
- /* Disable Rx and Tx */
+ mdelay(10);
+
+ gfar_halt_nodisable(priv);
+
+ /* Disable Rx/Tx DMA */
tempval = gfar_read(&regs->maccfg1);
tempval &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN);
gfar_write(&regs->maccfg1, tempval);
}
-static void free_grp_irqs(struct gfar_priv_grp *grp)
-{
- free_irq(gfar_irq(grp, TX)->irq, grp);
- free_irq(gfar_irq(grp, RX)->irq, grp);
- free_irq(gfar_irq(grp, ER)->irq, grp);
-}
-
void stop_gfar(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- unsigned long flags;
- int i;
-
- phy_stop(priv->phydev);
+ netif_tx_stop_all_queues(dev);
- /* Lock it down */
- local_irq_save(flags);
- lock_tx_qs(priv);
- lock_rx_qs(priv);
+ smp_mb__before_clear_bit();
+ set_bit(GFAR_DOWN, &priv->state);
+ smp_mb__after_clear_bit();
- gfar_halt(dev);
+ disable_napi(priv);
- unlock_rx_qs(priv);
- unlock_tx_qs(priv);
- local_irq_restore(flags);
+ /* disable ints and gracefully shut down Rx/Tx DMA */
+ gfar_halt(priv);
- /* Free the IRQs */
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- for (i = 0; i < priv->num_grps; i++)
- free_grp_irqs(&priv->gfargrp[i]);
- } else {
- for (i = 0; i < priv->num_grps; i++)
- free_irq(gfar_irq(&priv->gfargrp[i], TX)->irq,
- &priv->gfargrp[i]);
- }
+ phy_stop(priv->phydev);
free_skb_resources(priv);
}
@@ -1825,17 +1897,15 @@ static void free_skb_resources(struct gfar_private *priv)
priv->tx_queue[0]->tx_bd_dma_base);
}
-void gfar_start(struct net_device *dev)
+void gfar_start(struct gfar_private *priv)
{
- struct gfar_private *priv = netdev_priv(dev);
struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
int i = 0;
- /* Enable Rx and Tx in MACCFG1 */
- tempval = gfar_read(&regs->maccfg1);
- tempval |= (MACCFG1_RX_EN | MACCFG1_TX_EN);
- gfar_write(&regs->maccfg1, tempval);
+ /* Enable Rx/Tx hw queues */
+ gfar_write(&regs->rqueue, priv->rqueue);
+ gfar_write(&regs->tqueue, priv->tqueue);
/* Initialize DMACTRL to have WWR and WOP */
tempval = gfar_read(&regs->dmactrl);
@@ -1852,52 +1922,23 @@ void gfar_start(struct net_device *dev)
/* Clear THLT/RHLT, so that the DMA starts polling now */
gfar_write(&regs->tstat, priv->gfargrp[i].tstat);
gfar_write(&regs->rstat, priv->gfargrp[i].rstat);
- /* Unmask the interrupts we look for */
- gfar_write(&regs->imask, IMASK_DEFAULT);
}
- dev->trans_start = jiffies; /* prevent tx timeout */
-}
-
-static void gfar_configure_coalescing(struct gfar_private *priv,
- unsigned long tx_mask, unsigned long rx_mask)
-{
- struct gfar __iomem *regs = priv->gfargrp[0].regs;
- u32 __iomem *baddr;
-
- if (priv->mode == MQ_MG_MODE) {
- int i = 0;
-
- baddr = &regs->txic0;
- for_each_set_bit(i, &tx_mask, priv->num_tx_queues) {
- gfar_write(baddr + i, 0);
- if (likely(priv->tx_queue[i]->txcoalescing))
- gfar_write(baddr + i, priv->tx_queue[i]->txic);
- }
+ /* Enable Rx/Tx DMA */
+ tempval = gfar_read(&regs->maccfg1);
+ tempval |= (MACCFG1_RX_EN | MACCFG1_TX_EN);
+ gfar_write(&regs->maccfg1, tempval);
- baddr = &regs->rxic0;
- for_each_set_bit(i, &rx_mask, priv->num_rx_queues) {
- gfar_write(baddr + i, 0);
- if (likely(priv->rx_queue[i]->rxcoalescing))
- gfar_write(baddr + i, priv->rx_queue[i]->rxic);
- }
- } else {
- /* Backward compatible case -- even if we enable
- * multiple queues, there's only single reg to program
- */
- gfar_write(&regs->txic, 0);
- if (likely(priv->tx_queue[0]->txcoalescing))
- gfar_write(&regs->txic, priv->tx_queue[0]->txic);
+ gfar_ints_enable(priv);
- gfar_write(&regs->rxic, 0);
- if (unlikely(priv->rx_queue[0]->rxcoalescing))
- gfar_write(&regs->rxic, priv->rx_queue[0]->rxic);
- }
+ priv->ndev->trans_start = jiffies; /* prevent tx timeout */
}
-void gfar_configure_coalescing_all(struct gfar_private *priv)
+static void free_grp_irqs(struct gfar_priv_grp *grp)
{
- gfar_configure_coalescing(priv, 0xFF, 0xFF);
+ free_irq(gfar_irq(grp, TX)->irq, grp);
+ free_irq(gfar_irq(grp, RX)->irq, grp);
+ free_irq(gfar_irq(grp, ER)->irq, grp);
}
static int register_grp_irqs(struct gfar_priv_grp *grp)
@@ -1956,46 +1997,65 @@ err_irq_fail:
}
-/* Bring the controller up and running */
-int startup_gfar(struct net_device *ndev)
+static void gfar_free_irq(struct gfar_private *priv)
{
- struct gfar_private *priv = netdev_priv(ndev);
- struct gfar __iomem *regs = NULL;
- int err, i, j;
+ int i;
- for (i = 0; i < priv->num_grps; i++) {
- regs= priv->gfargrp[i].regs;
- gfar_write(&regs->imask, IMASK_INIT_CLEAR);
+ /* Free the IRQs */
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+ for (i = 0; i < priv->num_grps; i++)
+ free_grp_irqs(&priv->gfargrp[i]);
+ } else {
+ for (i = 0; i < priv->num_grps; i++)
+ free_irq(gfar_irq(&priv->gfargrp[i], TX)->irq,
+ &priv->gfargrp[i]);
}
+}
- regs= priv->gfargrp[0].regs;
- err = gfar_alloc_skb_resources(ndev);
- if (err)
- return err;
-
- gfar_init_mac(ndev);
+static int gfar_request_irq(struct gfar_private *priv)
+{
+ int err, i, j;
for (i = 0; i < priv->num_grps; i++) {
err = register_grp_irqs(&priv->gfargrp[i]);
if (err) {
for (j = 0; j < i; j++)
free_grp_irqs(&priv->gfargrp[j]);
- goto irq_fail;
+ return err;
}
}
- /* Start the controller */
- gfar_start(ndev);
+ return 0;
+}
+
+/* Bring the controller up and running */
+int startup_gfar(struct net_device *ndev)
+{
+ struct gfar_private *priv = netdev_priv(ndev);
+ int err;
+
+ gfar_mac_reset(priv);
+
+ err = gfar_alloc_skb_resources(ndev);
+ if (err)
+ return err;
+
+ gfar_init_tx_rx_base(priv);
+
+ smp_mb__before_clear_bit();
+ clear_bit(GFAR_DOWN, &priv->state);
+ smp_mb__after_clear_bit();
+
+ /* Start Rx/Tx DMA and enable the interrupts */
+ gfar_start(priv);
phy_start(priv->phydev);
- gfar_configure_coalescing_all(priv);
+ enable_napi(priv);
- return 0;
+ netif_tx_wake_all_queues(ndev);
-irq_fail:
- free_skb_resources(priv);
- return err;
+ return 0;
}
/* Called when something needs to use the ethernet device
@@ -2006,27 +2066,17 @@ static int gfar_enet_open(struct net_device *dev)
struct gfar_private *priv = netdev_priv(dev);
int err;
- enable_napi(priv);
-
- /* Initialize a bunch of registers */
- init_registers(dev);
-
- gfar_set_mac_address(dev);
-
err = init_phy(dev);
+ if (err)
+ return err;
- if (err) {
- disable_napi(priv);
+ err = gfar_request_irq(priv);
+ if (err)
return err;
- }
err = startup_gfar(dev);
- if (err) {
- disable_napi(priv);
+ if (err)
return err;
- }
-
- netif_tx_start_all_queues(dev);
device_set_wakeup_enable(&dev->dev, priv->wol_en);
@@ -2152,13 +2202,13 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
skb_new = skb_realloc_headroom(skb, fcb_len);
if (!skb_new) {
dev->stats.tx_errors++;
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
if (skb->sk)
skb_set_owner_w(skb_new, skb->sk);
- consume_skb(skb);
+ dev_consume_skb_any(skb);
skb = skb_new;
}
@@ -2351,8 +2401,6 @@ static int gfar_close(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- disable_napi(priv);
-
cancel_work_sync(&priv->reset_task);
stop_gfar(dev);
@@ -2360,7 +2408,7 @@ static int gfar_close(struct net_device *dev)
phy_disconnect(priv->phydev);
priv->phydev = NULL;
- netif_tx_stop_all_queues(dev);
+ gfar_free_irq(priv);
return 0;
}
@@ -2373,77 +2421,9 @@ static int gfar_set_mac_address(struct net_device *dev)
return 0;
}
-/* Check if rx parser should be activated */
-void gfar_check_rx_parser_mode(struct gfar_private *priv)
-{
- struct gfar __iomem *regs;
- u32 tempval;
-
- regs = priv->gfargrp[0].regs;
-
- tempval = gfar_read(&regs->rctrl);
- /* If parse is no longer required, then disable parser */
- if (tempval & RCTRL_REQ_PARSER) {
- tempval |= RCTRL_PRSDEP_INIT;
- priv->uses_rxfcb = 1;
- } else {
- tempval &= ~RCTRL_PRSDEP_INIT;
- priv->uses_rxfcb = 0;
- }
- gfar_write(&regs->rctrl, tempval);
-}
-
-/* Enables and disables VLAN insertion/extraction */
-void gfar_vlan_mode(struct net_device *dev, netdev_features_t features)
-{
- struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = NULL;
- unsigned long flags;
- u32 tempval;
-
- regs = priv->gfargrp[0].regs;
- local_irq_save(flags);
- lock_rx_qs(priv);
-
- if (features & NETIF_F_HW_VLAN_CTAG_TX) {
- /* Enable VLAN tag insertion */
- tempval = gfar_read(&regs->tctrl);
- tempval |= TCTRL_VLINS;
- gfar_write(&regs->tctrl, tempval);
- } else {
- /* Disable VLAN tag insertion */
- tempval = gfar_read(&regs->tctrl);
- tempval &= ~TCTRL_VLINS;
- gfar_write(&regs->tctrl, tempval);
- }
-
- if (features & NETIF_F_HW_VLAN_CTAG_RX) {
- /* Enable VLAN tag extraction */
- tempval = gfar_read(&regs->rctrl);
- tempval |= (RCTRL_VLEX | RCTRL_PRSDEP_INIT);
- gfar_write(&regs->rctrl, tempval);
- priv->uses_rxfcb = 1;
- } else {
- /* Disable VLAN tag extraction */
- tempval = gfar_read(&regs->rctrl);
- tempval &= ~RCTRL_VLEX;
- gfar_write(&regs->rctrl, tempval);
-
- gfar_check_rx_parser_mode(priv);
- }
-
- gfar_change_mtu(dev, dev->mtu);
-
- unlock_rx_qs(priv);
- local_irq_restore(flags);
-}
-
static int gfar_change_mtu(struct net_device *dev, int new_mtu)
{
- int tempsize, tempval;
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp[0].regs;
- int oldsize = priv->rx_buffer_size;
int frame_size = new_mtu + ETH_HLEN;
if ((frame_size < 64) || (frame_size > JUMBO_FRAME_SIZE)) {
@@ -2451,45 +2431,33 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
return -EINVAL;
}
- if (priv->uses_rxfcb)
- frame_size += GMAC_FCB_LEN;
+ while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state))
+ cpu_relax();
- frame_size += priv->padding;
-
- tempsize = (frame_size & ~(INCREMENTAL_BUFFER_SIZE - 1)) +
- INCREMENTAL_BUFFER_SIZE;
-
- /* Only stop and start the controller if it isn't already
- * stopped, and we changed something
- */
- if ((oldsize != tempsize) && (dev->flags & IFF_UP))
+ if (dev->flags & IFF_UP)
stop_gfar(dev);
- priv->rx_buffer_size = tempsize;
-
dev->mtu = new_mtu;
- gfar_write(&regs->mrblr, priv->rx_buffer_size);
- gfar_write(&regs->maxfrm, priv->rx_buffer_size);
+ if (dev->flags & IFF_UP)
+ startup_gfar(dev);
- /* If the mtu is larger than the max size for standard
- * ethernet frames (ie, a jumbo frame), then set maccfg2
- * to allow huge frames, and to check the length
- */
- tempval = gfar_read(&regs->maccfg2);
+ clear_bit_unlock(GFAR_RESETTING, &priv->state);
- if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE ||
- gfar_has_errata(priv, GFAR_ERRATA_74))
- tempval |= (MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
- else
- tempval &= ~(MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
+ return 0;
+}
- gfar_write(&regs->maccfg2, tempval);
+void reset_gfar(struct net_device *ndev)
+{
+ struct gfar_private *priv = netdev_priv(ndev);
- if ((oldsize != tempsize) && (dev->flags & IFF_UP))
- startup_gfar(dev);
+ while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state))
+ cpu_relax();
- return 0;
+ stop_gfar(ndev);
+ startup_gfar(ndev);
+
+ clear_bit_unlock(GFAR_RESETTING, &priv->state);
}
/* gfar_reset_task gets scheduled when a packet has not been
@@ -2501,16 +2469,7 @@ static void gfar_reset_task(struct work_struct *work)
{
struct gfar_private *priv = container_of(work, struct gfar_private,
reset_task);
- struct net_device *dev = priv->ndev;
-
- if (dev->flags & IFF_UP) {
- netif_tx_stop_all_queues(dev);
- stop_gfar(dev);
- startup_gfar(dev);
- netif_tx_start_all_queues(dev);
- }
-
- netif_tx_schedule_all(dev);
+ reset_gfar(priv->ndev);
}
static void gfar_timeout(struct net_device *dev)
@@ -2623,8 +2582,10 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
}
/* If we freed a buffer, we can restart transmission, if necessary */
- if (netif_tx_queue_stopped(txq) && tx_queue->num_txbdfree)
- netif_wake_subqueue(dev, tqi);
+ if (tx_queue->num_txbdfree &&
+ netif_tx_queue_stopped(txq) &&
+ !(test_bit(GFAR_DOWN, &priv->state)))
+ netif_wake_subqueue(priv->ndev, tqi);
/* Update dirty indicators */
tx_queue->skb_dirtytx = skb_dirtytx;
@@ -2633,31 +2594,6 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
netdev_tx_completed_queue(txq, howmany, bytes_sent);
}
-static void gfar_schedule_cleanup(struct gfar_priv_grp *gfargrp)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&gfargrp->grplock, flags);
- if (napi_schedule_prep(&gfargrp->napi)) {
- gfar_write(&gfargrp->regs->imask, IMASK_RTX_DISABLED);
- __napi_schedule(&gfargrp->napi);
- } else {
- /* Clear IEVENT, so interrupts aren't called again
- * because of the packets that have already arrived.
- */
- gfar_write(&gfargrp->regs->ievent, IEVENT_RTX_MASK);
- }
- spin_unlock_irqrestore(&gfargrp->grplock, flags);
-
-}
-
-/* Interrupt Handler for Transmit complete */
-static irqreturn_t gfar_transmit(int irq, void *grp_id)
-{
- gfar_schedule_cleanup((struct gfar_priv_grp *)grp_id);
- return IRQ_HANDLED;
-}
-
static void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp,
struct sk_buff *skb)
{
@@ -2728,7 +2664,48 @@ static inline void count_errors(unsigned short status, struct net_device *dev)
irqreturn_t gfar_receive(int irq, void *grp_id)
{
- gfar_schedule_cleanup((struct gfar_priv_grp *)grp_id);
+ struct gfar_priv_grp *grp = (struct gfar_priv_grp *)grp_id;
+ unsigned long flags;
+ u32 imask;
+
+ if (likely(napi_schedule_prep(&grp->napi_rx))) {
+ spin_lock_irqsave(&grp->grplock, flags);
+ imask = gfar_read(&grp->regs->imask);
+ imask &= IMASK_RX_DISABLED;
+ gfar_write(&grp->regs->imask, imask);
+ spin_unlock_irqrestore(&grp->grplock, flags);
+ __napi_schedule(&grp->napi_rx);
+ } else {
+ /* Clear IEVENT, so interrupts aren't called again
+ * because of the packets that have already arrived.
+ */
+ gfar_write(&grp->regs->ievent, IEVENT_RX_MASK);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* Interrupt Handler for Transmit complete */
+static irqreturn_t gfar_transmit(int irq, void *grp_id)
+{
+ struct gfar_priv_grp *grp = (struct gfar_priv_grp *)grp_id;
+ unsigned long flags;
+ u32 imask;
+
+ if (likely(napi_schedule_prep(&grp->napi_tx))) {
+ spin_lock_irqsave(&grp->grplock, flags);
+ imask = gfar_read(&grp->regs->imask);
+ imask &= IMASK_TX_DISABLED;
+ gfar_write(&grp->regs->imask, imask);
+ spin_unlock_irqrestore(&grp->grplock, flags);
+ __napi_schedule(&grp->napi_tx);
+ } else {
+ /* Clear IEVENT, so interrupts aren't called again
+ * because of the packets that have already arrived.
+ */
+ gfar_write(&grp->regs->ievent, IEVENT_TX_MASK);
+ }
+
return IRQ_HANDLED;
}
@@ -2852,7 +2829,7 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
rx_queue->stats.rx_bytes += pkt_len;
skb_record_rx_queue(skb, rx_queue->qindex);
gfar_process_frame(dev, skb, amount_pull,
- &rx_queue->grp->napi);
+ &rx_queue->grp->napi_rx);
} else {
netif_warn(priv, rx_err, dev, "Missing skb!\n");
@@ -2881,66 +2858,81 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
return howmany;
}
-static int gfar_poll_sq(struct napi_struct *napi, int budget)
+static int gfar_poll_rx_sq(struct napi_struct *napi, int budget)
{
struct gfar_priv_grp *gfargrp =
- container_of(napi, struct gfar_priv_grp, napi);
+ container_of(napi, struct gfar_priv_grp, napi_rx);
struct gfar __iomem *regs = gfargrp->regs;
- struct gfar_priv_tx_q *tx_queue = gfargrp->priv->tx_queue[0];
- struct gfar_priv_rx_q *rx_queue = gfargrp->priv->rx_queue[0];
+ struct gfar_priv_rx_q *rx_queue = gfargrp->rx_queue;
int work_done = 0;
/* Clear IEVENT, so interrupts aren't called again
* because of the packets that have already arrived
*/
- gfar_write(&regs->ievent, IEVENT_RTX_MASK);
-
- /* run Tx cleanup to completion */
- if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx])
- gfar_clean_tx_ring(tx_queue);
+ gfar_write(&regs->ievent, IEVENT_RX_MASK);
work_done = gfar_clean_rx_ring(rx_queue, budget);
if (work_done < budget) {
+ u32 imask;
napi_complete(napi);
/* Clear the halt bit in RSTAT */
gfar_write(&regs->rstat, gfargrp->rstat);
- gfar_write(&regs->imask, IMASK_DEFAULT);
-
- /* If we are coalescing interrupts, update the timer
- * Otherwise, clear it
- */
- gfar_write(&regs->txic, 0);
- if (likely(tx_queue->txcoalescing))
- gfar_write(&regs->txic, tx_queue->txic);
-
- gfar_write(&regs->rxic, 0);
- if (unlikely(rx_queue->rxcoalescing))
- gfar_write(&regs->rxic, rx_queue->rxic);
+ spin_lock_irq(&gfargrp->grplock);
+ imask = gfar_read(&regs->imask);
+ imask |= IMASK_RX_DEFAULT;
+ gfar_write(&regs->imask, imask);
+ spin_unlock_irq(&gfargrp->grplock);
}
return work_done;
}
-static int gfar_poll(struct napi_struct *napi, int budget)
+static int gfar_poll_tx_sq(struct napi_struct *napi, int budget)
+{
+ struct gfar_priv_grp *gfargrp =
+ container_of(napi, struct gfar_priv_grp, napi_tx);
+ struct gfar __iomem *regs = gfargrp->regs;
+ struct gfar_priv_tx_q *tx_queue = gfargrp->tx_queue;
+ u32 imask;
+
+ /* Clear IEVENT, so interrupts aren't called again
+ * because of the packets that have already arrived
+ */
+ gfar_write(&regs->ievent, IEVENT_TX_MASK);
+
+ /* run Tx cleanup to completion */
+ if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx])
+ gfar_clean_tx_ring(tx_queue);
+
+ napi_complete(napi);
+
+ spin_lock_irq(&gfargrp->grplock);
+ imask = gfar_read(&regs->imask);
+ imask |= IMASK_TX_DEFAULT;
+ gfar_write(&regs->imask, imask);
+ spin_unlock_irq(&gfargrp->grplock);
+
+ return 0;
+}
+
+static int gfar_poll_rx(struct napi_struct *napi, int budget)
{
struct gfar_priv_grp *gfargrp =
- container_of(napi, struct gfar_priv_grp, napi);
+ container_of(napi, struct gfar_priv_grp, napi_rx);
struct gfar_private *priv = gfargrp->priv;
struct gfar __iomem *regs = gfargrp->regs;
- struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
int work_done = 0, work_done_per_q = 0;
int i, budget_per_q = 0;
- int has_tx_work = 0;
unsigned long rstat_rxf;
int num_act_queues;
/* Clear IEVENT, so interrupts aren't called again
* because of the packets that have already arrived
*/
- gfar_write(&regs->ievent, IEVENT_RTX_MASK);
+ gfar_write(&regs->ievent, IEVENT_RX_MASK);
rstat_rxf = gfar_read(&regs->rstat) & RSTAT_RXF_MASK;
@@ -2948,15 +2940,6 @@ static int gfar_poll(struct napi_struct *napi, int budget)
if (num_act_queues)
budget_per_q = budget/num_act_queues;
- for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) {
- tx_queue = priv->tx_queue[i];
- /* run Tx cleanup to completion */
- if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) {
- gfar_clean_tx_ring(tx_queue);
- has_tx_work = 1;
- }
- }
-
for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
/* skip queue if not active */
if (!(rstat_rxf & (RSTAT_CLEAR_RXF0 >> i)))
@@ -2979,25 +2962,62 @@ static int gfar_poll(struct napi_struct *napi, int budget)
}
}
- if (!num_act_queues && !has_tx_work) {
-
+ if (!num_act_queues) {
+ u32 imask;
napi_complete(napi);
/* Clear the halt bit in RSTAT */
gfar_write(&regs->rstat, gfargrp->rstat);
- gfar_write(&regs->imask, IMASK_DEFAULT);
-
- /* If we are coalescing interrupts, update the timer
- * Otherwise, clear it
- */
- gfar_configure_coalescing(priv, gfargrp->rx_bit_map,
- gfargrp->tx_bit_map);
+ spin_lock_irq(&gfargrp->grplock);
+ imask = gfar_read(&regs->imask);
+ imask |= IMASK_RX_DEFAULT;
+ gfar_write(&regs->imask, imask);
+ spin_unlock_irq(&gfargrp->grplock);
}
return work_done;
}
+static int gfar_poll_tx(struct napi_struct *napi, int budget)
+{
+ struct gfar_priv_grp *gfargrp =
+ container_of(napi, struct gfar_priv_grp, napi_tx);
+ struct gfar_private *priv = gfargrp->priv;
+ struct gfar __iomem *regs = gfargrp->regs;
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ int has_tx_work = 0;
+ int i;
+
+ /* Clear IEVENT, so interrupts aren't called again
+ * because of the packets that have already arrived
+ */
+ gfar_write(&regs->ievent, IEVENT_TX_MASK);
+
+ for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) {
+ tx_queue = priv->tx_queue[i];
+ /* run Tx cleanup to completion */
+ if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) {
+ gfar_clean_tx_ring(tx_queue);
+ has_tx_work = 1;
+ }
+ }
+
+ if (!has_tx_work) {
+ u32 imask;
+ napi_complete(napi);
+
+ spin_lock_irq(&gfargrp->grplock);
+ imask = gfar_read(&regs->imask);
+ imask |= IMASK_TX_DEFAULT;
+ gfar_write(&regs->imask, imask);
+ spin_unlock_irq(&gfargrp->grplock);
+ }
+
+ return 0;
+}
+
+
#ifdef CONFIG_NET_POLL_CONTROLLER
/* Polling 'interrupt' - used by things like netconsole to send skbs
* without having to re-enable interrupts. It's not called while
@@ -3101,12 +3121,11 @@ static void adjust_link(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar __iomem *regs = priv->gfargrp[0].regs;
- unsigned long flags;
struct phy_device *phydev = priv->phydev;
int new_state = 0;
- local_irq_save(flags);
- lock_tx_qs(priv);
+ if (test_bit(GFAR_RESETTING, &priv->state))
+ return;
if (phydev->link) {
u32 tempval1 = gfar_read(&regs->maccfg1);
@@ -3178,8 +3197,6 @@ static void adjust_link(struct net_device *dev)
if (new_state && netif_msg_link(priv))
phy_print_status(phydev);
- unlock_tx_qs(priv);
- local_irq_restore(flags);
}
/* Update the hash table based on the current list of multicast
diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h
index 52bb2b0195cc..84632c569f2c 100644
--- a/drivers/net/ethernet/freescale/gianfar.h
+++ b/drivers/net/ethernet/freescale/gianfar.h
@@ -9,7 +9,7 @@
* Maintainer: Kumar Gala
* Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright 2002-2009, 2011 Freescale Semiconductor, Inc.
+ * Copyright 2002-2009, 2011-2013 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
@@ -377,8 +377,11 @@ extern const char gfar_driver_version[];
IMASK_RXFEN0 | IMASK_BSY | IMASK_EBERR | IMASK_BABR | \
IMASK_XFUN | IMASK_RXC | IMASK_BABT | IMASK_DPE \
| IMASK_PERR)
-#define IMASK_RTX_DISABLED ((~(IMASK_RXFEN0 | IMASK_TXFEN | IMASK_BSY)) \
- & IMASK_DEFAULT)
+#define IMASK_RX_DEFAULT (IMASK_RXFEN0 | IMASK_BSY)
+#define IMASK_TX_DEFAULT (IMASK_TXFEN | IMASK_TXBEN)
+
+#define IMASK_RX_DISABLED ((~(IMASK_RX_DEFAULT)) & IMASK_DEFAULT)
+#define IMASK_TX_DISABLED ((~(IMASK_TX_DEFAULT)) & IMASK_DEFAULT)
/* Fifo management */
#define FIFO_TX_THR_MASK 0x01ff
@@ -409,7 +412,9 @@ extern const char gfar_driver_version[];
/* This default RIR value directly corresponds
* to the 3-bit hash value generated */
-#define DEFAULT_RIR0 0x05397700
+#define DEFAULT_8RXQ_RIR0 0x05397700
+/* Map even hash values to Q0, and odd ones to Q1 */
+#define DEFAULT_2RXQ_RIR0 0x04104100
/* RQFCR register bits */
#define RQFCR_GPI 0x80000000
@@ -880,7 +885,6 @@ struct gfar {
#define FSL_GIANFAR_DEV_HAS_CSUM 0x00000010
#define FSL_GIANFAR_DEV_HAS_VLAN 0x00000020
#define FSL_GIANFAR_DEV_HAS_EXTENDED_HASH 0x00000040
-#define FSL_GIANFAR_DEV_HAS_PADDING 0x00000080
#define FSL_GIANFAR_DEV_HAS_MAGIC_PACKET 0x00000100
#define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200
#define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400
@@ -892,8 +896,8 @@ struct gfar {
#define DEFAULT_MAPPING 0xFF
#endif
-#define ISRG_SHIFT_TX 0x10
-#define ISRG_SHIFT_RX 0x18
+#define ISRG_RR0 0x80000000
+#define ISRG_TR0 0x00800000
/* The same driver can operate in two modes */
/* SQ_SG_MODE: Single Queue Single Group Mode
@@ -905,6 +909,22 @@ enum {
MQ_MG_MODE
};
+/* GFAR_SQ_POLLING: Single Queue NAPI polling mode
+ * The driver supports a single pair of RX/Tx queues
+ * per interrupt group (Rx/Tx int line). MQ_MG mode
+ * devices have 2 interrupt groups, so the device will
+ * have a total of 2 Tx and 2 Rx queues in this case.
+ * GFAR_MQ_POLLING: Multi Queue NAPI polling mode
+ * The driver supports all the 8 Rx and Tx HW queues
+ * each queue mapped by the Device Tree to one of
+ * the 2 interrupt groups. This mode implies significant
+ * processing overhead (CPU and controller level).
+ */
+enum gfar_poll_mode {
+ GFAR_SQ_POLLING = 0,
+ GFAR_MQ_POLLING
+};
+
/*
* Per TX queue stats
*/
@@ -966,7 +986,6 @@ struct rx_q_stats {
/**
* struct gfar_priv_rx_q - per rx queue structure
- * @rxlock: per queue rx spin lock
* @rx_skbuff: skb pointers
* @skb_currx: currently use skb pointer
* @rx_bd_base: First rx buffer descriptor
@@ -979,8 +998,7 @@ struct rx_q_stats {
*/
struct gfar_priv_rx_q {
- spinlock_t rxlock __attribute__ ((aligned (SMP_CACHE_BYTES)));
- struct sk_buff ** rx_skbuff;
+ struct sk_buff **rx_skbuff __aligned(SMP_CACHE_BYTES);
dma_addr_t rx_bd_dma_base;
struct rxbd8 *rx_bd_base;
struct rxbd8 *cur_rx;
@@ -1016,17 +1034,20 @@ struct gfar_irqinfo {
*/
struct gfar_priv_grp {
- spinlock_t grplock __attribute__ ((aligned (SMP_CACHE_BYTES)));
- struct napi_struct napi;
- struct gfar_private *priv;
+ spinlock_t grplock __aligned(SMP_CACHE_BYTES);
+ struct napi_struct napi_rx;
+ struct napi_struct napi_tx;
struct gfar __iomem *regs;
- unsigned int rstat;
- unsigned long num_rx_queues;
- unsigned long rx_bit_map;
- /* cacheline 3 */
+ struct gfar_priv_tx_q *tx_queue;
+ struct gfar_priv_rx_q *rx_queue;
unsigned int tstat;
+ unsigned int rstat;
+
+ struct gfar_private *priv;
unsigned long num_tx_queues;
unsigned long tx_bit_map;
+ unsigned long num_rx_queues;
+ unsigned long rx_bit_map;
struct gfar_irqinfo *irqinfo[GFAR_NUM_IRQS];
};
@@ -1041,6 +1062,11 @@ enum gfar_errata {
GFAR_ERRATA_12 = 0x08, /* a.k.a errata eTSEC49 */
};
+enum gfar_dev_state {
+ GFAR_DOWN = 1,
+ GFAR_RESETTING
+};
+
/* Struct stolen almost completely (and shamelessly) from the FCC enet source
* (Ok, that's not so true anymore, but there is a family resemblance)
* The GFAR buffer descriptors track the ring buffers. The rx_bd_base
@@ -1051,8 +1077,6 @@ enum gfar_errata {
* the buffer descriptor determines the actual condition.
*/
struct gfar_private {
- unsigned int num_rx_queues;
-
struct device *dev;
struct net_device *ndev;
enum gfar_errata errata;
@@ -1060,6 +1084,7 @@ struct gfar_private {
u16 uses_rxfcb;
u16 padding;
+ u32 device_flags;
/* HW time stamping enabled flag */
int hwts_rx_en;
@@ -1069,10 +1094,12 @@ struct gfar_private {
struct gfar_priv_rx_q *rx_queue[MAX_RX_QS];
struct gfar_priv_grp gfargrp[MAXGROUPS];
- u32 device_flags;
+ unsigned long state;
- unsigned int mode;
+ unsigned short mode;
+ unsigned short poll_mode;
unsigned int num_tx_queues;
+ unsigned int num_rx_queues;
unsigned int num_grps;
/* Network Statistics */
@@ -1113,6 +1140,9 @@ struct gfar_private {
unsigned int total_tx_ring_size;
unsigned int total_rx_ring_size;
+ u32 rqueue;
+ u32 tqueue;
+
/* RX per device parameters */
unsigned int rx_stash_size;
unsigned int rx_stash_index;
@@ -1127,11 +1157,6 @@ struct gfar_private {
u32 __iomem *hash_regs[16];
int hash_width;
- /* global parameters */
- unsigned int fifo_threshold;
- unsigned int fifo_starve;
- unsigned int fifo_starve_off;
-
/*Filer table*/
unsigned int ftp_rqfpr[MAX_FILER_IDX + 1];
unsigned int ftp_rqfcr[MAX_FILER_IDX + 1];
@@ -1176,21 +1201,42 @@ static inline void gfar_read_filer(struct gfar_private *priv,
*fpr = gfar_read(&regs->rqfpr);
}
-void lock_rx_qs(struct gfar_private *priv);
-void lock_tx_qs(struct gfar_private *priv);
-void unlock_rx_qs(struct gfar_private *priv);
-void unlock_tx_qs(struct gfar_private *priv);
+static inline void gfar_write_isrg(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 __iomem *baddr = &regs->isrg0;
+ u32 isrg = 0;
+ int grp_idx, i;
+
+ for (grp_idx = 0; grp_idx < priv->num_grps; grp_idx++) {
+ struct gfar_priv_grp *grp = &priv->gfargrp[grp_idx];
+
+ for_each_set_bit(i, &grp->rx_bit_map, priv->num_rx_queues) {
+ isrg |= (ISRG_RR0 >> i);
+ }
+
+ for_each_set_bit(i, &grp->tx_bit_map, priv->num_tx_queues) {
+ isrg |= (ISRG_TR0 >> i);
+ }
+
+ gfar_write(baddr, isrg);
+
+ baddr++;
+ isrg = 0;
+ }
+}
+
irqreturn_t gfar_receive(int irq, void *dev_id);
int startup_gfar(struct net_device *dev);
void stop_gfar(struct net_device *dev);
-void gfar_halt(struct net_device *dev);
+void reset_gfar(struct net_device *dev);
+void gfar_mac_reset(struct gfar_private *priv);
+void gfar_halt(struct gfar_private *priv);
+void gfar_start(struct gfar_private *priv);
void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable,
u32 regnum, u32 read);
void gfar_configure_coalescing_all(struct gfar_private *priv);
-void gfar_init_sysfs(struct net_device *dev);
int gfar_set_features(struct net_device *dev, netdev_features_t features);
-void gfar_check_rx_parser_mode(struct gfar_private *priv);
-void gfar_vlan_mode(struct net_device *dev, netdev_features_t features);
extern const struct ethtool_ops gfar_ethtool_ops;
diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
index 63d234419cc1..891dbee6e6c1 100644
--- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
+++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
@@ -44,10 +44,6 @@
#include "gianfar.h"
-extern void gfar_start(struct net_device *dev);
-extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue,
- int rx_work_limit);
-
#define GFAR_MAX_COAL_USECS 0xffff
#define GFAR_MAX_COAL_FRAMES 0xff
static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
@@ -364,25 +360,11 @@ static int gfar_scoalesce(struct net_device *dev,
struct ethtool_coalesce *cvals)
{
struct gfar_private *priv = netdev_priv(dev);
- int i = 0;
+ int i, err = 0;
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
- /* Set up rx coalescing */
- /* As of now, we will enable/disable coalescing for all
- * queues together in case of eTSEC2, this will be modified
- * along with the ethtool interface
- */
- if ((cvals->rx_coalesce_usecs == 0) ||
- (cvals->rx_max_coalesced_frames == 0)) {
- for (i = 0; i < priv->num_rx_queues; i++)
- priv->rx_queue[i]->rxcoalescing = 0;
- } else {
- for (i = 0; i < priv->num_rx_queues; i++)
- priv->rx_queue[i]->rxcoalescing = 1;
- }
-
if (NULL == priv->phydev)
return -ENODEV;
@@ -399,6 +381,32 @@ static int gfar_scoalesce(struct net_device *dev,
return -EINVAL;
}
+ /* Check the bounds of the values */
+ if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
+ netdev_info(dev, "Coalescing is limited to %d microseconds\n",
+ GFAR_MAX_COAL_USECS);
+ return -EINVAL;
+ }
+
+ if (cvals->tx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
+ netdev_info(dev, "Coalescing is limited to %d frames\n",
+ GFAR_MAX_COAL_FRAMES);
+ return -EINVAL;
+ }
+
+ while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state))
+ cpu_relax();
+
+ /* Set up rx coalescing */
+ if ((cvals->rx_coalesce_usecs == 0) ||
+ (cvals->rx_max_coalesced_frames == 0)) {
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i]->rxcoalescing = 0;
+ } else {
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i]->rxcoalescing = 1;
+ }
+
for (i = 0; i < priv->num_rx_queues; i++) {
priv->rx_queue[i]->rxic = mk_ic_value(
cvals->rx_max_coalesced_frames,
@@ -415,28 +423,22 @@ static int gfar_scoalesce(struct net_device *dev,
priv->tx_queue[i]->txcoalescing = 1;
}
- /* Check the bounds of the values */
- if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
- netdev_info(dev, "Coalescing is limited to %d microseconds\n",
- GFAR_MAX_COAL_USECS);
- return -EINVAL;
- }
-
- if (cvals->tx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
- netdev_info(dev, "Coalescing is limited to %d frames\n",
- GFAR_MAX_COAL_FRAMES);
- return -EINVAL;
- }
-
for (i = 0; i < priv->num_tx_queues; i++) {
priv->tx_queue[i]->txic = mk_ic_value(
cvals->tx_max_coalesced_frames,
gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
}
- gfar_configure_coalescing_all(priv);
+ if (dev->flags & IFF_UP) {
+ stop_gfar(dev);
+ err = startup_gfar(dev);
+ } else {
+ gfar_mac_reset(priv);
+ }
+
+ clear_bit_unlock(GFAR_RESETTING, &priv->state);
- return 0;
+ return err;
}
/* Fills in rvals with the current ring parameters. Currently,
@@ -467,15 +469,13 @@ static void gfar_gringparam(struct net_device *dev,
}
/* Change the current ring parameters, stopping the controller if
- * necessary so that we don't mess things up while we're in
- * motion. We wait for the ring to be clean before reallocating
- * the rings.
+ * necessary so that we don't mess things up while we're in motion.
*/
static int gfar_sringparam(struct net_device *dev,
struct ethtool_ringparam *rvals)
{
struct gfar_private *priv = netdev_priv(dev);
- int err = 0, i = 0;
+ int err = 0, i;
if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
return -EINVAL;
@@ -493,44 +493,25 @@ static int gfar_sringparam(struct net_device *dev,
return -EINVAL;
}
+ while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state))
+ cpu_relax();
- if (dev->flags & IFF_UP) {
- unsigned long flags;
-
- /* Halt TX and RX, and process the frames which
- * have already been received
- */
- local_irq_save(flags);
- lock_tx_qs(priv);
- lock_rx_qs(priv);
-
- gfar_halt(dev);
-
- unlock_rx_qs(priv);
- unlock_tx_qs(priv);
- local_irq_restore(flags);
-
- for (i = 0; i < priv->num_rx_queues; i++)
- gfar_clean_rx_ring(priv->rx_queue[i],
- priv->rx_queue[i]->rx_ring_size);
-
- /* Now we take down the rings to rebuild them */
+ if (dev->flags & IFF_UP)
stop_gfar(dev);
- }
- /* Change the size */
- for (i = 0; i < priv->num_rx_queues; i++) {
+ /* Change the sizes */
+ for (i = 0; i < priv->num_rx_queues; i++)
priv->rx_queue[i]->rx_ring_size = rvals->rx_pending;
+
+ for (i = 0; i < priv->num_tx_queues; i++)
priv->tx_queue[i]->tx_ring_size = rvals->tx_pending;
- priv->tx_queue[i]->num_txbdfree =
- priv->tx_queue[i]->tx_ring_size;
- }
/* Rebuild the rings with the new size */
- if (dev->flags & IFF_UP) {
+ if (dev->flags & IFF_UP)
err = startup_gfar(dev);
- netif_tx_wake_all_queues(dev);
- }
+
+ clear_bit_unlock(GFAR_RESETTING, &priv->state);
+
return err;
}
@@ -608,43 +589,29 @@ static int gfar_spauseparam(struct net_device *dev,
int gfar_set_features(struct net_device *dev, netdev_features_t features)
{
- struct gfar_private *priv = netdev_priv(dev);
- unsigned long flags;
- int err = 0, i = 0;
netdev_features_t changed = dev->features ^ features;
+ struct gfar_private *priv = netdev_priv(dev);
+ int err = 0;
- if (changed & (NETIF_F_HW_VLAN_CTAG_TX|NETIF_F_HW_VLAN_CTAG_RX))
- gfar_vlan_mode(dev, features);
-
- if (!(changed & NETIF_F_RXCSUM))
+ if (!(changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_RXCSUM)))
return 0;
- if (dev->flags & IFF_UP) {
- /* Halt TX and RX, and process the frames which
- * have already been received
- */
- local_irq_save(flags);
- lock_tx_qs(priv);
- lock_rx_qs(priv);
-
- gfar_halt(dev);
+ while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state))
+ cpu_relax();
- unlock_tx_qs(priv);
- unlock_rx_qs(priv);
- local_irq_restore(flags);
-
- for (i = 0; i < priv->num_rx_queues; i++)
- gfar_clean_rx_ring(priv->rx_queue[i],
- priv->rx_queue[i]->rx_ring_size);
+ dev->features = features;
+ if (dev->flags & IFF_UP) {
/* Now we take down the rings to rebuild them */
stop_gfar(dev);
-
- dev->features = features;
-
err = startup_gfar(dev);
- netif_tx_wake_all_queues(dev);
+ } else {
+ gfar_mac_reset(priv);
}
+
+ clear_bit_unlock(GFAR_RESETTING, &priv->state);
+
return err;
}
@@ -1610,9 +1577,6 @@ static int gfar_write_filer_table(struct gfar_private *priv,
if (tab->index > MAX_FILER_IDX - 1)
return -EBUSY;
- /* Avoid inconsistent filer table to be processed */
- lock_rx_qs(priv);
-
/* Fill regular entries */
for (; i < MAX_FILER_IDX - 1 && (tab->fe[i].ctrl | tab->fe[i].ctrl);
i++)
@@ -1625,8 +1589,6 @@ static int gfar_write_filer_table(struct gfar_private *priv,
*/
gfar_write_filer(priv, i, 0x20, 0x0);
- unlock_rx_qs(priv);
-
return 0;
}
@@ -1831,6 +1793,9 @@ static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
struct gfar_private *priv = netdev_priv(dev);
int ret = 0;
+ if (test_bit(GFAR_RESETTING, &priv->state))
+ return -EBUSY;
+
mutex_lock(&priv->rx_queue_access);
switch (cmd->cmd) {
diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c
index abc28da27042..bb568006f37d 100644
--- a/drivers/net/ethernet/freescale/gianfar_ptp.c
+++ b/drivers/net/ethernet/freescale/gianfar_ptp.c
@@ -414,6 +414,7 @@ static struct ptp_clock_info ptp_gianfar_caps = {
.n_alarm = 0,
.n_ext_ts = N_EXT_TS,
.n_per_out = 0,
+ .n_pins = 0,
.pps = 1,
.adjfreq = ptp_gianfar_adjfreq,
.adjtime = ptp_gianfar_adjtime,
diff --git a/drivers/net/ethernet/freescale/gianfar_sysfs.c b/drivers/net/ethernet/freescale/gianfar_sysfs.c
deleted file mode 100644
index e02dd1378751..000000000000
--- a/drivers/net/ethernet/freescale/gianfar_sysfs.c
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * drivers/net/ethernet/freescale/gianfar_sysfs.c
- *
- * Gianfar Ethernet Driver
- * This driver is designed for the non-CPM ethernet controllers
- * on the 85xx and 83xx family of integrated processors
- * Based on 8260_io/fcc_enet.c
- *
- * Author: Andy Fleming
- * Maintainer: Kumar Gala (galak@kernel.crashing.org)
- * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
- *
- * Copyright 2002-2009 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.
- *
- * Sysfs file creation and management
- */
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/unistd.h>
-#include <linux/delay.h>
-#include <linux/etherdevice.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/device.h>
-
-#include <asm/uaccess.h>
-#include <linux/module.h>
-
-#include "gianfar.h"
-
-static ssize_t gfar_show_bd_stash(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
-
- return sprintf(buf, "%s\n", priv->bd_stash_en ? "on" : "off");
-}
-
-static ssize_t gfar_set_bd_stash(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp[0].regs;
- int new_setting = 0;
- u32 temp;
- unsigned long flags;
-
- if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BD_STASHING))
- return count;
-
-
- /* Find out the new setting */
- if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
- new_setting = 1;
- else if (!strncmp("off", buf, count - 1) ||
- !strncmp("0", buf, count - 1))
- new_setting = 0;
- else
- return count;
-
-
- local_irq_save(flags);
- lock_rx_qs(priv);
-
- /* Set the new stashing value */
- priv->bd_stash_en = new_setting;
-
- temp = gfar_read(&regs->attr);
-
- if (new_setting)
- temp |= ATTR_BDSTASH;
- else
- temp &= ~(ATTR_BDSTASH);
-
- gfar_write(&regs->attr, temp);
-
- unlock_rx_qs(priv);
- local_irq_restore(flags);
-
- return count;
-}
-
-static DEVICE_ATTR(bd_stash, 0644, gfar_show_bd_stash, gfar_set_bd_stash);
-
-static ssize_t gfar_show_rx_stash_size(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
-
- return sprintf(buf, "%d\n", priv->rx_stash_size);
-}
-
-static ssize_t gfar_set_rx_stash_size(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp[0].regs;
- unsigned int length = simple_strtoul(buf, NULL, 0);
- u32 temp;
- unsigned long flags;
-
- if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
- return count;
-
- local_irq_save(flags);
- lock_rx_qs(priv);
-
- if (length > priv->rx_buffer_size)
- goto out;
-
- if (length == priv->rx_stash_size)
- goto out;
-
- priv->rx_stash_size = length;
-
- temp = gfar_read(&regs->attreli);
- temp &= ~ATTRELI_EL_MASK;
- temp |= ATTRELI_EL(length);
- gfar_write(&regs->attreli, temp);
-
- /* Turn stashing on/off as appropriate */
- temp = gfar_read(&regs->attr);
-
- if (length)
- temp |= ATTR_BUFSTASH;
- else
- temp &= ~(ATTR_BUFSTASH);
-
- gfar_write(&regs->attr, temp);
-
-out:
- unlock_rx_qs(priv);
- local_irq_restore(flags);
-
- return count;
-}
-
-static DEVICE_ATTR(rx_stash_size, 0644, gfar_show_rx_stash_size,
- gfar_set_rx_stash_size);
-
-/* Stashing will only be enabled when rx_stash_size != 0 */
-static ssize_t gfar_show_rx_stash_index(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
-
- return sprintf(buf, "%d\n", priv->rx_stash_index);
-}
-
-static ssize_t gfar_set_rx_stash_index(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp[0].regs;
- unsigned short index = simple_strtoul(buf, NULL, 0);
- u32 temp;
- unsigned long flags;
-
- if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
- return count;
-
- local_irq_save(flags);
- lock_rx_qs(priv);
-
- if (index > priv->rx_stash_size)
- goto out;
-
- if (index == priv->rx_stash_index)
- goto out;
-
- priv->rx_stash_index = index;
-
- temp = gfar_read(&regs->attreli);
- temp &= ~ATTRELI_EI_MASK;
- temp |= ATTRELI_EI(index);
- gfar_write(&regs->attreli, temp);
-
-out:
- unlock_rx_qs(priv);
- local_irq_restore(flags);
-
- return count;
-}
-
-static DEVICE_ATTR(rx_stash_index, 0644, gfar_show_rx_stash_index,
- gfar_set_rx_stash_index);
-
-static ssize_t gfar_show_fifo_threshold(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
-
- return sprintf(buf, "%d\n", priv->fifo_threshold);
-}
-
-static ssize_t gfar_set_fifo_threshold(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp[0].regs;
- unsigned int length = simple_strtoul(buf, NULL, 0);
- u32 temp;
- unsigned long flags;
-
- if (length > GFAR_MAX_FIFO_THRESHOLD)
- return count;
-
- local_irq_save(flags);
- lock_tx_qs(priv);
-
- priv->fifo_threshold = length;
-
- temp = gfar_read(&regs->fifo_tx_thr);
- temp &= ~FIFO_TX_THR_MASK;
- temp |= length;
- gfar_write(&regs->fifo_tx_thr, temp);
-
- unlock_tx_qs(priv);
- local_irq_restore(flags);
-
- return count;
-}
-
-static DEVICE_ATTR(fifo_threshold, 0644, gfar_show_fifo_threshold,
- gfar_set_fifo_threshold);
-
-static ssize_t gfar_show_fifo_starve(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
-
- return sprintf(buf, "%d\n", priv->fifo_starve);
-}
-
-static ssize_t gfar_set_fifo_starve(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp[0].regs;
- unsigned int num = simple_strtoul(buf, NULL, 0);
- u32 temp;
- unsigned long flags;
-
- if (num > GFAR_MAX_FIFO_STARVE)
- return count;
-
- local_irq_save(flags);
- lock_tx_qs(priv);
-
- priv->fifo_starve = num;
-
- temp = gfar_read(&regs->fifo_tx_starve);
- temp &= ~FIFO_TX_STARVE_MASK;
- temp |= num;
- gfar_write(&regs->fifo_tx_starve, temp);
-
- unlock_tx_qs(priv);
- local_irq_restore(flags);
-
- return count;
-}
-
-static DEVICE_ATTR(fifo_starve, 0644, gfar_show_fifo_starve,
- gfar_set_fifo_starve);
-
-static ssize_t gfar_show_fifo_starve_off(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
-
- return sprintf(buf, "%d\n", priv->fifo_starve_off);
-}
-
-static ssize_t gfar_set_fifo_starve_off(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp[0].regs;
- unsigned int num = simple_strtoul(buf, NULL, 0);
- u32 temp;
- unsigned long flags;
-
- if (num > GFAR_MAX_FIFO_STARVE_OFF)
- return count;
-
- local_irq_save(flags);
- lock_tx_qs(priv);
-
- priv->fifo_starve_off = num;
-
- temp = gfar_read(&regs->fifo_tx_starve_shutoff);
- temp &= ~FIFO_TX_STARVE_OFF_MASK;
- temp |= num;
- gfar_write(&regs->fifo_tx_starve_shutoff, temp);
-
- unlock_tx_qs(priv);
- local_irq_restore(flags);
-
- return count;
-}
-
-static DEVICE_ATTR(fifo_starve_off, 0644, gfar_show_fifo_starve_off,
- gfar_set_fifo_starve_off);
-
-void gfar_init_sysfs(struct net_device *dev)
-{
- struct gfar_private *priv = netdev_priv(dev);
- int rc;
-
- /* Initialize the default values */
- priv->fifo_threshold = DEFAULT_FIFO_TX_THR;
- priv->fifo_starve = DEFAULT_FIFO_TX_STARVE;
- priv->fifo_starve_off = DEFAULT_FIFO_TX_STARVE_OFF;
-
- /* Create our sysfs files */
- rc = device_create_file(&dev->dev, &dev_attr_bd_stash);
- rc |= device_create_file(&dev->dev, &dev_attr_rx_stash_size);
- rc |= device_create_file(&dev->dev, &dev_attr_rx_stash_index);
- rc |= device_create_file(&dev->dev, &dev_attr_fifo_threshold);
- rc |= device_create_file(&dev->dev, &dev_attr_fifo_starve);
- rc |= device_create_file(&dev->dev, &dev_attr_fifo_starve_off);
- if (rc)
- dev_err(&dev->dev, "Error creating gianfar sysfs files\n");
-}
diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index 72291a8904a9..c8299c31b21f 100644
--- a/drivers/net/ethernet/freescale/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -3261,7 +3261,7 @@ static int ucc_geth_tx(struct net_device *dev, u8 txQ)
dev->stats.tx_packets++;
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
ugeth->tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]] = NULL;
ugeth->skb_dirtytx[txQ] =
diff --git a/drivers/net/ethernet/i825xx/lib82596.c b/drivers/net/ethernet/i825xx/lib82596.c
index 17fca323c143..c984998b34a0 100644
--- a/drivers/net/ethernet/i825xx/lib82596.c
+++ b/drivers/net/ethernet/i825xx/lib82596.c
@@ -993,7 +993,7 @@ static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->name));
dev->stats.tx_dropped++;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
} else {
if (++lp->next_tx_cmd == TX_RING_SIZE)
lp->next_tx_cmd = 0;
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c
index 7628e0fd8455..538903bf13bc 100644
--- a/drivers/net/ethernet/ibm/ehea/ehea_main.c
+++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c
@@ -490,7 +490,7 @@ static int ehea_refill_rq_def(struct ehea_port_res *pr,
skb_arr[index] = skb;
tmp_addr = ehea_map_vaddr(skb->data);
if (tmp_addr == -1) {
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
q_skba->os_skbs = fill_wqes - i;
ret = 0;
break;
@@ -856,7 +856,7 @@ static struct ehea_cqe *ehea_proc_cqes(struct ehea_port_res *pr, int my_quota)
index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
skb = pr->sq_skba.arr[index];
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
pr->sq_skba.arr[index] = NULL;
}
@@ -2044,7 +2044,7 @@ static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
skb_copy_bits(skb, 0, imm_data, skb->len);
swqe->immediate_data_length = skb->len;
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
}
static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 1fc8334fc181..c9127562bd22 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -1044,7 +1044,7 @@ retry_bounce:
DMA_TO_DEVICE);
out:
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
return NETDEV_TX_OK;
map_failed_frags:
@@ -1072,7 +1072,7 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
unsigned long lpar_rc;
restart_poll:
- do {
+ while (frames_processed < budget) {
if (!ibmveth_rxq_pending_buffer(adapter))
break;
@@ -1121,7 +1121,7 @@ restart_poll:
netdev->stats.rx_bytes += length;
frames_processed++;
}
- } while (frames_processed < budget);
+ }
ibmveth_replenish_task(adapter);
diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c
index bf7a01ef9a57..b56461ce674c 100644
--- a/drivers/net/ethernet/intel/e100.c
+++ b/drivers/net/ethernet/intel/e100.c
@@ -1778,9 +1778,9 @@ static int e100_xmit_prepare(struct nic *nic, struct cb *cb,
* testing, ie sending frames with bad CRC.
*/
if (unlikely(skb->no_fcs))
- cb->command |= __constant_cpu_to_le16(cb_tx_nc);
+ cb->command |= cpu_to_le16(cb_tx_nc);
else
- cb->command &= ~__constant_cpu_to_le16(cb_tx_nc);
+ cb->command &= ~cpu_to_le16(cb_tx_nc);
/* interrupt every 16 packets regardless of delay */
if ((nic->cbs_avail & ~15) == nic->cbs_avail)
diff --git a/drivers/net/ethernet/intel/e1000e/80003es2lan.c b/drivers/net/ethernet/intel/e1000e/80003es2lan.c
index ff2d806eaef7..a5f6b11d6992 100644
--- a/drivers/net/ethernet/intel/e1000e/80003es2lan.c
+++ b/drivers/net/ethernet/intel/e1000e/80003es2lan.c
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
/* 80003ES2LAN Gigabit Ethernet Controller (Copper)
* 80003ES2LAN Gigabit Ethernet Controller (Serdes)
diff --git a/drivers/net/ethernet/intel/e1000e/80003es2lan.h b/drivers/net/ethernet/intel/e1000e/80003es2lan.h
index 90d363b2d280..535a9430976d 100644
--- a/drivers/net/ethernet/intel/e1000e/80003es2lan.h
+++ b/drivers/net/ethernet/intel/e1000e/80003es2lan.h
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#ifndef _E1000E_80003ES2LAN_H_
#define _E1000E_80003ES2LAN_H_
diff --git a/drivers/net/ethernet/intel/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c
index 8fed74e3fa53..e0aa7f1efb08 100644
--- a/drivers/net/ethernet/intel/e1000e/82571.c
+++ b/drivers/net/ethernet/intel/e1000e/82571.c
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
/* 82571EB Gigabit Ethernet Controller
* 82571EB Gigabit Ethernet Controller (Copper)
diff --git a/drivers/net/ethernet/intel/e1000e/82571.h b/drivers/net/ethernet/intel/e1000e/82571.h
index 08e24dc3dc0e..2e758f796d60 100644
--- a/drivers/net/ethernet/intel/e1000e/82571.h
+++ b/drivers/net/ethernet/intel/e1000e/82571.h
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#ifndef _E1000E_82571_H_
#define _E1000E_82571_H_
diff --git a/drivers/net/ethernet/intel/e1000e/Makefile b/drivers/net/ethernet/intel/e1000e/Makefile
index c2dcfcc10857..106de493373c 100644
--- a/drivers/net/ethernet/intel/e1000e/Makefile
+++ b/drivers/net/ethernet/intel/e1000e/Makefile
@@ -1,7 +1,7 @@
################################################################################
#
# Intel PRO/1000 Linux driver
-# Copyright(c) 1999 - 2013 Intel Corporation.
+# Copyright(c) 1999 - 2014 Intel Corporation.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
@@ -12,9 +12,8 @@
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# The full GNU General Public License is included in this distribution in
# the file called "COPYING".
diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h
index 351c94a0cf74..d18e89212575 100644
--- a/drivers/net/ethernet/intel/e1000e/defines.h
+++ b/drivers/net/ethernet/intel/e1000e/defines.h
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#ifndef _E1000_DEFINES_H_
#define _E1000_DEFINES_H_
@@ -35,9 +28,11 @@
/* Definitions for power management and wakeup registers */
/* Wake Up Control */
-#define E1000_WUC_APME 0x00000001 /* APM Enable */
-#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */
-#define E1000_WUC_PHY_WAKE 0x00000100 /* if PHY supports wakeup */
+#define E1000_WUC_APME 0x00000001 /* APM Enable */
+#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */
+#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */
+#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */
+#define E1000_WUC_PHY_WAKE 0x00000100 /* if PHY supports wakeup */
/* Wake Up Filter Control */
#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index 0150f7fc893d..1471c5464a89 100644
--- a/drivers/net/ethernet/intel/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
/* Linux PRO/1000 Ethernet Driver main header file */
@@ -269,6 +262,7 @@ struct e1000_adapter {
u32 tx_head_addr;
u32 tx_fifo_size;
u32 tx_dma_failed;
+ u32 tx_hwtstamp_timeouts;
/* Rx */
bool (*clean_rx) (struct e1000_ring *ring, int *work_done,
@@ -333,7 +327,6 @@ struct e1000_adapter {
struct work_struct update_phy_task;
struct work_struct print_hang_task;
- bool idle_check;
int phy_hang_count;
u16 tx_ring_count;
@@ -342,6 +335,7 @@ struct e1000_adapter {
struct hwtstamp_config hwtstamp_config;
struct delayed_work systim_overflow_work;
struct sk_buff *tx_hwtstamp_skb;
+ unsigned long tx_hwtstamp_start;
struct work_struct tx_hwtstamp_work;
spinlock_t systim_lock; /* protects SYSTIML/H regsters */
struct cyclecounter cc;
@@ -476,7 +470,7 @@ 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_down(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);
void e1000e_power_up_phy(struct e1000_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c
index d14c8f53384c..cad250bc1b99 100644
--- a/drivers/net/ethernet/intel/e1000e/ethtool.c
+++ b/drivers/net/ethernet/intel/e1000e/ethtool.c
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
/* ethtool support for e1000 */
@@ -111,6 +104,7 @@ static const struct e1000_stats e1000_gstrings_stats[] = {
E1000_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
E1000_STAT("uncorr_ecc_errors", uncorr_errors),
E1000_STAT("corr_ecc_errors", corr_errors),
+ E1000_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
};
#define E1000_GLOBAL_STATS_LEN ARRAY_SIZE(e1000_gstrings_stats)
@@ -332,7 +326,7 @@ static int e1000_set_settings(struct net_device *netdev,
/* reset the link */
if (netif_running(adapter->netdev)) {
- e1000e_down(adapter);
+ e1000e_down(adapter, true);
e1000e_up(adapter);
} else {
e1000e_reset(adapter);
@@ -380,7 +374,7 @@ static int e1000_set_pauseparam(struct net_device *netdev,
if (adapter->fc_autoneg == AUTONEG_ENABLE) {
hw->fc.requested_mode = e1000_fc_default;
if (netif_running(adapter->netdev)) {
- e1000e_down(adapter);
+ e1000e_down(adapter, true);
e1000e_up(adapter);
} else {
e1000e_reset(adapter);
@@ -726,7 +720,7 @@ static int e1000_set_ringparam(struct net_device *netdev,
pm_runtime_get_sync(netdev->dev.parent);
- e1000e_down(adapter);
+ e1000e_down(adapter, true);
/* We can't just free everything and then setup again, because the
* ISRs in MSI-X mode get passed pointers to the Tx and Rx ring
@@ -924,15 +918,21 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data)
}
if (mac->type == e1000_pch2lan) {
/* SHRAH[0,1,2] different than previous */
- if (i == 7)
+ if (i == 1)
mask &= 0xFFF4FFFF;
/* SHRAH[3] different than SHRAH[0,1,2] */
- if (i == 10)
+ if (i == 4)
mask |= (1 << 30);
+ /* RAR[1-6] owned by management engine - skipping */
+ if (i > 0)
+ i += 6;
}
REG_PATTERN_TEST_ARRAY(E1000_RA, ((i << 1) + 1), mask,
0xFFFFFFFF);
+ /* reset index to actual value */
+ if ((mac->type == e1000_pch2lan) && (i > 6))
+ i -= 6;
}
for (i = 0; i < mac->mta_reg_count; i++)
diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h
index b7f38435d1fd..6b3de5f39a97 100644
--- a/drivers/net/ethernet/intel/e1000e/hw.h
+++ b/drivers/net/ethernet/intel/e1000e/hw.h
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#ifndef _E1000_HW_H_
#define _E1000_HW_H_
@@ -655,12 +648,20 @@ struct e1000_shadow_ram {
#define E1000_ICH8_SHADOW_RAM_WORDS 2048
+/* I218 PHY Ultra Low Power (ULP) states */
+enum e1000_ulp_state {
+ e1000_ulp_state_unknown,
+ e1000_ulp_state_off,
+ e1000_ulp_state_on,
+};
+
struct e1000_dev_spec_ich8lan {
bool kmrn_lock_loss_workaround_enabled;
struct e1000_shadow_ram shadow_ram[E1000_ICH8_SHADOW_RAM_WORDS];
bool nvm_k1_enabled;
bool eee_disable;
u16 eee_lp_ability;
+ enum e1000_ulp_state ulp_state;
};
struct e1000_hw {
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index 42f0f6717511..9866f264f55e 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
/* 82562G 10/100 Network Connection
* 82562G-2 10/100 Network Connection
@@ -53,6 +46,14 @@
* 82578DC Gigabit Network Connection
* 82579LM Gigabit Network Connection
* 82579V Gigabit Network Connection
+ * Ethernet Connection I217-LM
+ * Ethernet Connection I217-V
+ * Ethernet Connection I218-V
+ * Ethernet Connection I218-LM
+ * Ethernet Connection (2) I218-LM
+ * Ethernet Connection (2) I218-V
+ * Ethernet Connection (3) I218-LM
+ * Ethernet Connection (3) I218-V
*/
#include "e1000.h"
@@ -142,7 +143,9 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index);
static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index);
static s32 e1000_k1_workaround_lv(struct e1000_hw *hw);
static void e1000_gate_hw_phy_config_ich8lan(struct e1000_hw *hw, bool gate);
+static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force);
static s32 e1000_setup_copper_link_pch_lpt(struct e1000_hw *hw);
+static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state);
static inline u16 __er16flash(struct e1000_hw *hw, unsigned long reg)
{
@@ -239,6 +242,47 @@ out:
}
/**
+ * e1000_toggle_lanphypc_pch_lpt - toggle the LANPHYPC pin value
+ * @hw: pointer to the HW structure
+ *
+ * Toggling the LANPHYPC pin value fully power-cycles the PHY and is
+ * used to reset the PHY to a quiescent state when necessary.
+ **/
+static void e1000_toggle_lanphypc_pch_lpt(struct e1000_hw *hw)
+{
+ u32 mac_reg;
+
+ /* Set Phy Config Counter to 50msec */
+ mac_reg = er32(FEXTNVM3);
+ mac_reg &= ~E1000_FEXTNVM3_PHY_CFG_COUNTER_MASK;
+ mac_reg |= E1000_FEXTNVM3_PHY_CFG_COUNTER_50MSEC;
+ ew32(FEXTNVM3, mac_reg);
+
+ /* Toggle LANPHYPC Value bit */
+ mac_reg = er32(CTRL);
+ mac_reg |= E1000_CTRL_LANPHYPC_OVERRIDE;
+ mac_reg &= ~E1000_CTRL_LANPHYPC_VALUE;
+ ew32(CTRL, mac_reg);
+ e1e_flush();
+ usleep_range(10, 20);
+ mac_reg &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
+ ew32(CTRL, mac_reg);
+ e1e_flush();
+
+ if (hw->mac.type < e1000_pch_lpt) {
+ msleep(50);
+ } else {
+ u16 count = 20;
+
+ do {
+ usleep_range(5000, 10000);
+ } while (!(er32(CTRL_EXT) & E1000_CTRL_EXT_LPCD) && count--);
+
+ msleep(30);
+ }
+}
+
+/**
* e1000_init_phy_workarounds_pchlan - PHY initialization workarounds
* @hw: pointer to the HW structure
*
@@ -247,6 +291,7 @@ out:
**/
static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
{
+ struct e1000_adapter *adapter = hw->adapter;
u32 mac_reg, fwsm = er32(FWSM);
s32 ret_val;
@@ -255,6 +300,12 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
*/
e1000_gate_hw_phy_config_ich8lan(hw, true);
+ /* It is not possible to be certain of the current state of ULP
+ * so forcibly disable it.
+ */
+ hw->dev_spec.ich8lan.ulp_state = e1000_ulp_state_unknown;
+ e1000_disable_ulp_lpt_lp(hw, true);
+
ret_val = hw->phy.ops.acquire(hw);
if (ret_val) {
e_dbg("Failed to initialize PHY flow\n");
@@ -300,33 +351,9 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
break;
}
- e_dbg("Toggling LANPHYPC\n");
-
- /* Set Phy Config Counter to 50msec */
- mac_reg = er32(FEXTNVM3);
- mac_reg &= ~E1000_FEXTNVM3_PHY_CFG_COUNTER_MASK;
- mac_reg |= E1000_FEXTNVM3_PHY_CFG_COUNTER_50MSEC;
- ew32(FEXTNVM3, mac_reg);
-
/* Toggle LANPHYPC Value bit */
- mac_reg = er32(CTRL);
- mac_reg |= E1000_CTRL_LANPHYPC_OVERRIDE;
- mac_reg &= ~E1000_CTRL_LANPHYPC_VALUE;
- ew32(CTRL, mac_reg);
- e1e_flush();
- usleep_range(10, 20);
- mac_reg &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
- ew32(CTRL, mac_reg);
- e1e_flush();
- if (hw->mac.type < e1000_pch_lpt) {
- msleep(50);
- } else {
- u16 count = 20;
- do {
- usleep_range(5000, 10000);
- } while (!(er32(CTRL_EXT) &
- E1000_CTRL_EXT_LPCD) && count--);
- usleep_range(30000, 60000);
+ e1000_toggle_lanphypc_pch_lpt(hw);
+ if (hw->mac.type >= e1000_pch_lpt) {
if (e1000_phy_is_accessible_pchlan(hw))
break;
@@ -349,12 +376,31 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
hw->phy.ops.release(hw);
if (!ret_val) {
+
+ /* Check to see if able to reset PHY. Print error if not */
+ if (hw->phy.ops.check_reset_block(hw)) {
+ e_err("Reset blocked by ME\n");
+ goto out;
+ }
+
/* Reset the PHY before any access to it. Doing so, ensures
* that the PHY is in a known good state before we read/write
* PHY registers. The generic reset is sufficient here,
* because we haven't determined the PHY type yet.
*/
ret_val = e1000e_phy_hw_reset_generic(hw);
+ if (ret_val)
+ goto out;
+
+ /* On a successful reset, possibly need to wait for the PHY
+ * to quiesce to an accessible state before returning control
+ * to the calling function. If the PHY does not quiesce, then
+ * return E1000E_BLK_PHY_RESET, as this is the condition that
+ * the PHY is in.
+ */
+ ret_val = hw->phy.ops.check_reset_block(hw);
+ if (ret_val)
+ e_err("ME blocked access to PHY after reset\n");
}
out:
@@ -724,8 +770,14 @@ s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data)
* Enable/disable EEE based on setting in dev_spec structure, the duplex of
* the link and the EEE capabilities of the link partner. The LPI Control
* register bits will remain set only if/when link is up.
+ *
+ * EEE LPI must not be asserted earlier than one second after link is up.
+ * On 82579, EEE LPI should not be enabled until such time otherwise there
+ * can be link issues with some switches. Other devices can have EEE LPI
+ * enabled immediately upon link up since they have a timer in hardware which
+ * prevents LPI from being asserted too early.
**/
-static s32 e1000_set_eee_pchlan(struct e1000_hw *hw)
+s32 e1000_set_eee_pchlan(struct e1000_hw *hw)
{
struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
s32 ret_val;
@@ -979,6 +1031,253 @@ static s32 e1000_platform_pm_pch_lpt(struct e1000_hw *hw, bool link)
}
/**
+ * e1000_enable_ulp_lpt_lp - configure Ultra Low Power mode for LynxPoint-LP
+ * @hw: pointer to the HW structure
+ * @to_sx: boolean indicating a system power state transition to Sx
+ *
+ * When link is down, configure ULP mode to significantly reduce the power
+ * to the PHY. If on a Manageability Engine (ME) enabled system, tell the
+ * ME firmware to start the ULP configuration. If not on an ME enabled
+ * system, configure the ULP mode by software.
+ */
+s32 e1000_enable_ulp_lpt_lp(struct e1000_hw *hw, bool to_sx)
+{
+ u32 mac_reg;
+ s32 ret_val = 0;
+ u16 phy_reg;
+
+ if ((hw->mac.type < e1000_pch_lpt) ||
+ (hw->adapter->pdev->device == E1000_DEV_ID_PCH_LPT_I217_LM) ||
+ (hw->adapter->pdev->device == E1000_DEV_ID_PCH_LPT_I217_V) ||
+ (hw->adapter->pdev->device == E1000_DEV_ID_PCH_I218_LM2) ||
+ (hw->adapter->pdev->device == E1000_DEV_ID_PCH_I218_V2) ||
+ (hw->dev_spec.ich8lan.ulp_state == e1000_ulp_state_on))
+ return 0;
+
+ if (er32(FWSM) & E1000_ICH_FWSM_FW_VALID) {
+ /* Request ME configure ULP mode in the PHY */
+ mac_reg = er32(H2ME);
+ mac_reg |= E1000_H2ME_ULP | E1000_H2ME_ENFORCE_SETTINGS;
+ ew32(H2ME, mac_reg);
+
+ goto out;
+ }
+
+ if (!to_sx) {
+ int i = 0;
+
+ /* Poll up to 5 seconds for Cable Disconnected indication */
+ while (!(er32(FEXT) & E1000_FEXT_PHY_CABLE_DISCONNECTED)) {
+ /* Bail if link is re-acquired */
+ if (er32(STATUS) & E1000_STATUS_LU)
+ return -E1000_ERR_PHY;
+
+ if (i++ == 100)
+ break;
+
+ msleep(50);
+ }
+ e_dbg("CABLE_DISCONNECTED %s set after %dmsec\n",
+ (er32(FEXT) &
+ E1000_FEXT_PHY_CABLE_DISCONNECTED) ? "" : "not", i * 50);
+ }
+
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+
+ /* Force SMBus mode in PHY */
+ ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL, &phy_reg);
+ if (ret_val)
+ goto release;
+ phy_reg |= CV_SMB_CTRL_FORCE_SMBUS;
+ e1000_write_phy_reg_hv_locked(hw, CV_SMB_CTRL, phy_reg);
+
+ /* Force SMBus mode in MAC */
+ mac_reg = er32(CTRL_EXT);
+ mac_reg |= E1000_CTRL_EXT_FORCE_SMBUS;
+ ew32(CTRL_EXT, mac_reg);
+
+ /* Set Inband ULP Exit, Reset to SMBus mode and
+ * Disable SMBus Release on PERST# in PHY
+ */
+ ret_val = e1000_read_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, &phy_reg);
+ if (ret_val)
+ goto release;
+ phy_reg |= (I218_ULP_CONFIG1_RESET_TO_SMBUS |
+ I218_ULP_CONFIG1_DISABLE_SMB_PERST);
+ if (to_sx) {
+ if (er32(WUFC) & E1000_WUFC_LNKC)
+ phy_reg |= I218_ULP_CONFIG1_WOL_HOST;
+
+ phy_reg |= I218_ULP_CONFIG1_STICKY_ULP;
+ } else {
+ phy_reg |= I218_ULP_CONFIG1_INBAND_EXIT;
+ }
+ e1000_write_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, phy_reg);
+
+ /* Set Disable SMBus Release on PERST# in MAC */
+ mac_reg = er32(FEXTNVM7);
+ mac_reg |= E1000_FEXTNVM7_DISABLE_SMB_PERST;
+ ew32(FEXTNVM7, mac_reg);
+
+ /* Commit ULP changes in PHY by starting auto ULP configuration */
+ phy_reg |= I218_ULP_CONFIG1_START;
+ e1000_write_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, phy_reg);
+release:
+ hw->phy.ops.release(hw);
+out:
+ if (ret_val)
+ e_dbg("Error in ULP enable flow: %d\n", ret_val);
+ else
+ hw->dev_spec.ich8lan.ulp_state = e1000_ulp_state_on;
+
+ return ret_val;
+}
+
+/**
+ * e1000_disable_ulp_lpt_lp - unconfigure Ultra Low Power mode for LynxPoint-LP
+ * @hw: pointer to the HW structure
+ * @force: boolean indicating whether or not to force disabling ULP
+ *
+ * Un-configure ULP mode when link is up, the system is transitioned from
+ * Sx or the driver is unloaded. If on a Manageability Engine (ME) enabled
+ * system, poll for an indication from ME that ULP has been un-configured.
+ * If not on an ME enabled system, un-configure the ULP mode by software.
+ *
+ * During nominal operation, this function is called when link is acquired
+ * to disable ULP mode (force=false); otherwise, for example when unloading
+ * the driver or during Sx->S0 transitions, this is called with force=true
+ * to forcibly disable ULP.
+ */
+static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force)
+{
+ s32 ret_val = 0;
+ u32 mac_reg;
+ u16 phy_reg;
+ int i = 0;
+
+ if ((hw->mac.type < e1000_pch_lpt) ||
+ (hw->adapter->pdev->device == E1000_DEV_ID_PCH_LPT_I217_LM) ||
+ (hw->adapter->pdev->device == E1000_DEV_ID_PCH_LPT_I217_V) ||
+ (hw->adapter->pdev->device == E1000_DEV_ID_PCH_I218_LM2) ||
+ (hw->adapter->pdev->device == E1000_DEV_ID_PCH_I218_V2) ||
+ (hw->dev_spec.ich8lan.ulp_state == e1000_ulp_state_off))
+ return 0;
+
+ if (er32(FWSM) & E1000_ICH_FWSM_FW_VALID) {
+ if (force) {
+ /* Request ME un-configure ULP mode in the PHY */
+ mac_reg = er32(H2ME);
+ mac_reg &= ~E1000_H2ME_ULP;
+ mac_reg |= E1000_H2ME_ENFORCE_SETTINGS;
+ ew32(H2ME, mac_reg);
+ }
+
+ /* Poll up to 100msec for ME to clear ULP_CFG_DONE */
+ while (er32(FWSM) & E1000_FWSM_ULP_CFG_DONE) {
+ if (i++ == 10) {
+ ret_val = -E1000_ERR_PHY;
+ goto out;
+ }
+
+ usleep_range(10000, 20000);
+ }
+ e_dbg("ULP_CONFIG_DONE cleared after %dmsec\n", i * 10);
+
+ if (force) {
+ mac_reg = er32(H2ME);
+ mac_reg &= ~E1000_H2ME_ENFORCE_SETTINGS;
+ ew32(H2ME, mac_reg);
+ } else {
+ /* Clear H2ME.ULP after ME ULP configuration */
+ mac_reg = er32(H2ME);
+ mac_reg &= ~E1000_H2ME_ULP;
+ ew32(H2ME, mac_reg);
+ }
+
+ goto out;
+ }
+
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+
+ if (force)
+ /* Toggle LANPHYPC Value bit */
+ e1000_toggle_lanphypc_pch_lpt(hw);
+
+ /* Unforce SMBus mode in PHY */
+ ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL, &phy_reg);
+ if (ret_val) {
+ /* The MAC might be in PCIe mode, so temporarily force to
+ * SMBus mode in order to access the PHY.
+ */
+ mac_reg = er32(CTRL_EXT);
+ mac_reg |= E1000_CTRL_EXT_FORCE_SMBUS;
+ ew32(CTRL_EXT, mac_reg);
+
+ msleep(50);
+
+ ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL,
+ &phy_reg);
+ if (ret_val)
+ goto release;
+ }
+ phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS;
+ e1000_write_phy_reg_hv_locked(hw, CV_SMB_CTRL, phy_reg);
+
+ /* Unforce SMBus mode in MAC */
+ mac_reg = er32(CTRL_EXT);
+ mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS;
+ ew32(CTRL_EXT, mac_reg);
+
+ /* When ULP mode was previously entered, K1 was disabled by the
+ * hardware. Re-Enable K1 in the PHY when exiting ULP.
+ */
+ ret_val = e1000_read_phy_reg_hv_locked(hw, HV_PM_CTRL, &phy_reg);
+ if (ret_val)
+ goto release;
+ phy_reg |= HV_PM_CTRL_K1_ENABLE;
+ e1000_write_phy_reg_hv_locked(hw, HV_PM_CTRL, phy_reg);
+
+ /* Clear ULP enabled configuration */
+ ret_val = e1000_read_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, &phy_reg);
+ if (ret_val)
+ goto release;
+ phy_reg &= ~(I218_ULP_CONFIG1_IND |
+ I218_ULP_CONFIG1_STICKY_ULP |
+ I218_ULP_CONFIG1_RESET_TO_SMBUS |
+ I218_ULP_CONFIG1_WOL_HOST |
+ I218_ULP_CONFIG1_INBAND_EXIT |
+ I218_ULP_CONFIG1_DISABLE_SMB_PERST);
+ e1000_write_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, phy_reg);
+
+ /* Commit ULP changes by starting auto ULP configuration */
+ phy_reg |= I218_ULP_CONFIG1_START;
+ e1000_write_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, phy_reg);
+
+ /* Clear Disable SMBus Release on PERST# in MAC */
+ mac_reg = er32(FEXTNVM7);
+ mac_reg &= ~E1000_FEXTNVM7_DISABLE_SMB_PERST;
+ ew32(FEXTNVM7, mac_reg);
+
+release:
+ hw->phy.ops.release(hw);
+ if (force) {
+ e1000_phy_hw_reset(hw);
+ msleep(50);
+ }
+out:
+ if (ret_val)
+ e_dbg("Error in ULP disable flow: %d\n", ret_val);
+ else
+ hw->dev_spec.ich8lan.ulp_state = e1000_ulp_state_off;
+
+ return ret_val;
+}
+
+/**
* e1000_check_for_copper_link_ich8lan - Check for link (Copper)
* @hw: pointer to the HW structure
*
@@ -1106,9 +1405,11 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
e1000e_check_downshift(hw);
/* Enable/Disable EEE after link up */
- ret_val = e1000_set_eee_pchlan(hw);
- if (ret_val)
- return ret_val;
+ if (hw->phy.type > e1000_phy_82579) {
+ ret_val = e1000_set_eee_pchlan(hw);
+ if (ret_val)
+ return ret_val;
+ }
/* If we are forcing speed/duplex, then we simply return since
* we have already determined whether we have link or not.
@@ -1374,7 +1675,7 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
/* RAR[1-6] are owned by manageability. Skip those and program the
* next address into the SHRA register array.
*/
- if (index < (u32)(hw->mac.rar_entry_count - 6)) {
+ if (index < (u32)(hw->mac.rar_entry_count)) {
s32 ret_val;
ret_val = e1000_acquire_swflag_ich8lan(hw);
@@ -1484,11 +1785,13 @@ out:
**/
static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw)
{
- u32 fwsm;
+ bool blocked = false;
+ int i = 0;
- fwsm = er32(FWSM);
-
- return (fwsm & E1000_ICH_FWSM_RSPCIPHY) ? 0 : E1000_BLK_PHY_RESET;
+ while ((blocked = !(er32(FWSM) & E1000_ICH_FWSM_RSPCIPHY)) &&
+ (i++ < 10))
+ usleep_range(10000, 20000);
+ return blocked ? E1000_BLK_PHY_RESET : 0;
}
/**
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.h b/drivers/net/ethernet/intel/e1000e/ich8lan.h
index 217090df33e7..bead50f9187b 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.h
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.h
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#ifndef _E1000E_ICH8LAN_H_
#define _E1000E_ICH8LAN_H_
@@ -65,11 +58,16 @@
#define E1000_FWSM_WLOCK_MAC_MASK 0x0380
#define E1000_FWSM_WLOCK_MAC_SHIFT 7
+#define E1000_FWSM_ULP_CFG_DONE 0x00000400 /* Low power cfg done */
/* Shared Receive Address Registers */
#define E1000_SHRAL_PCH_LPT(_i) (0x05408 + ((_i) * 8))
#define E1000_SHRAH_PCH_LPT(_i) (0x0540C + ((_i) * 8))
+#define E1000_H2ME 0x05B50 /* Host to ME */
+#define E1000_H2ME_ULP 0x00000800 /* ULP Indication Bit */
+#define E1000_H2ME_ENFORCE_SETTINGS 0x00001000 /* Enforce Settings */
+
#define ID_LED_DEFAULT_ICH8LAN ((ID_LED_DEF1_DEF2 << 12) | \
(ID_LED_OFF1_OFF2 << 8) | \
(ID_LED_OFF1_ON2 << 4) | \
@@ -82,6 +80,9 @@
#define E1000_ICH8_LAN_INIT_TIMEOUT 1500
+/* FEXT register bit definition */
+#define E1000_FEXT_PHY_CABLE_DISCONNECTED 0x00000004
+
#define E1000_FEXTNVM_SW_CONFIG 1
#define E1000_FEXTNVM_SW_CONFIG_ICH8M (1 << 27) /* different on ICH8M */
@@ -95,10 +96,12 @@
#define E1000_FEXTNVM6_REQ_PLL_CLK 0x00000100
#define E1000_FEXTNVM6_ENABLE_K1_ENTRY_CONDITION 0x00000200
+#define E1000_FEXTNVM7_DISABLE_SMB_PERST 0x00000020
+
#define PCIE_ICH8_SNOOP_ALL PCIE_NO_SNOOP_ALL
#define E1000_ICH_RAR_ENTRIES 7
-#define E1000_PCH2_RAR_ENTRIES 11 /* RAR[0-6], SHRA[0-3] */
+#define E1000_PCH2_RAR_ENTRIES 5 /* RAR[0], SHRA[0-3] */
#define E1000_PCH_LPT_RAR_ENTRIES 12 /* RAR[0], SHRA[0-10] */
#define PHY_PAGE_SHIFT 5
@@ -161,6 +164,16 @@
#define CV_SMB_CTRL PHY_REG(769, 23)
#define CV_SMB_CTRL_FORCE_SMBUS 0x0001
+/* I218 Ultra Low Power Configuration 1 Register */
+#define I218_ULP_CONFIG1 PHY_REG(779, 16)
+#define I218_ULP_CONFIG1_START 0x0001 /* Start auto ULP config */
+#define I218_ULP_CONFIG1_IND 0x0004 /* Pwr up from ULP indication */
+#define I218_ULP_CONFIG1_STICKY_ULP 0x0010 /* Set sticky ULP mode */
+#define I218_ULP_CONFIG1_INBAND_EXIT 0x0020 /* Inband on ULP exit */
+#define I218_ULP_CONFIG1_WOL_HOST 0x0040 /* WoL Host on ULP exit */
+#define I218_ULP_CONFIG1_RESET_TO_SMBUS 0x0100 /* Reset to SMBus mode */
+#define I218_ULP_CONFIG1_DISABLE_SMB_PERST 0x1000 /* Disable on PERST# */
+
/* SMBus Address Phy Register */
#define HV_SMB_ADDR PHY_REG(768, 26)
#define HV_SMB_ADDR_MASK 0x007F
@@ -195,6 +208,7 @@
/* PHY Power Management Control */
#define HV_PM_CTRL PHY_REG(770, 17)
#define HV_PM_CTRL_PLL_STOP_IN_K1_GIGA 0x100
+#define HV_PM_CTRL_K1_ENABLE 0x4000
#define SW_FLAG_TIMEOUT 1000 /* SW Semaphore flag timeout in ms */
@@ -268,4 +282,6 @@ void e1000_copy_rx_addrs_to_phy_ich8lan(struct e1000_hw *hw);
s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable);
s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data);
s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data);
+s32 e1000_set_eee_pchlan(struct e1000_hw *hw);
+s32 e1000_enable_ulp_lpt_lp(struct e1000_hw *hw, bool to_sx);
#endif /* _E1000E_ICH8LAN_H_ */
diff --git a/drivers/net/ethernet/intel/e1000e/mac.c b/drivers/net/ethernet/intel/e1000e/mac.c
index 2480c1091873..baa0a466d1d0 100644
--- a/drivers/net/ethernet/intel/e1000e/mac.c
+++ b/drivers/net/ethernet/intel/e1000e/mac.c
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#include "e1000.h"
diff --git a/drivers/net/ethernet/intel/e1000e/mac.h b/drivers/net/ethernet/intel/e1000e/mac.h
index a61fee404ebe..4e81c2825b7a 100644
--- a/drivers/net/ethernet/intel/e1000e/mac.h
+++ b/drivers/net/ethernet/intel/e1000e/mac.h
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#ifndef _E1000E_MAC_H_
#define _E1000E_MAC_H_
diff --git a/drivers/net/ethernet/intel/e1000e/manage.c b/drivers/net/ethernet/intel/e1000e/manage.c
index e4b0f1ef92f6..cb37ff1f1321 100644
--- a/drivers/net/ethernet/intel/e1000e/manage.c
+++ b/drivers/net/ethernet/intel/e1000e/manage.c
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#include "e1000.h"
diff --git a/drivers/net/ethernet/intel/e1000e/manage.h b/drivers/net/ethernet/intel/e1000e/manage.h
index 326897c29ea8..a8c27f98f7b0 100644
--- a/drivers/net/ethernet/intel/e1000e/manage.h
+++ b/drivers/net/ethernet/intel/e1000e/manage.h
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#ifndef _E1000E_MANAGE_H_
#define _E1000E_MANAGE_H_
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 6d91933c4cdd..dce377b59b2c 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -885,7 +878,7 @@ static inline void e1000_rx_hash(struct net_device *netdev, __le32 rss,
struct sk_buff *skb)
{
if (netdev->features & NETIF_F_RXHASH)
- skb->rxhash = le32_to_cpu(rss);
+ skb_set_hash(skb, le32_to_cpu(rss), PKT_HASH_TYPE_L3);
}
/**
@@ -1097,8 +1090,14 @@ static void e1000_print_hw_hang(struct work_struct *work)
adapter->tx_hang_recheck = true;
return;
}
- /* Real hang detected */
adapter->tx_hang_recheck = false;
+
+ if (er32(TDH(0)) == er32(TDT(0))) {
+ e_dbg("false hang detected, ignoring\n");
+ return;
+ }
+
+ /* Real hang detected */
netif_stop_queue(netdev);
e1e_rphy(hw, MII_BMSR, &phy_status);
@@ -1128,6 +1127,8 @@ static void e1000_print_hw_hang(struct work_struct *work)
eop, jiffies, eop_desc->upper.fields.status, er32(STATUS),
phy_status, phy_1000t_status, phy_ext_status, pci_status);
+ e1000e_dump(adapter);
+
/* Suggest workaround for known h/w issue */
if ((hw->mac.type == e1000_pchlan) && (er32(CTRL) & E1000_CTRL_TFCE))
e_err("Try turning off Tx pause (flow control) via ethtool\n");
@@ -1147,9 +1148,6 @@ static void e1000e_tx_hwtstamp_work(struct work_struct *work)
tx_hwtstamp_work);
struct e1000_hw *hw = &adapter->hw;
- if (!adapter->tx_hwtstamp_skb)
- return;
-
if (er32(TSYNCTXCTL) & E1000_TSYNCTXCTL_VALID) {
struct skb_shared_hwtstamps shhwtstamps;
u64 txstmp;
@@ -1162,6 +1160,12 @@ static void e1000e_tx_hwtstamp_work(struct work_struct *work)
skb_tstamp_tx(adapter->tx_hwtstamp_skb, &shhwtstamps);
dev_kfree_skb_any(adapter->tx_hwtstamp_skb);
adapter->tx_hwtstamp_skb = NULL;
+ } else if (time_after(jiffies, adapter->tx_hwtstamp_start
+ + adapter->tx_timeout_factor * HZ)) {
+ dev_kfree_skb_any(adapter->tx_hwtstamp_skb);
+ adapter->tx_hwtstamp_skb = NULL;
+ adapter->tx_hwtstamp_timeouts++;
+ e_warn("clearing Tx timestamp hang");
} else {
/* reschedule to check later */
schedule_work(&adapter->tx_hwtstamp_work);
@@ -1701,7 +1705,7 @@ static void e1000_clean_rx_ring(struct e1000_ring *rx_ring)
adapter->flags2 &= ~FLAG2_IS_DISCARDING;
writel(0, rx_ring->head);
- if (rx_ring->adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
+ if (adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
e1000e_update_rdt_wa(rx_ring, 0);
else
writel(0, rx_ring->tail);
@@ -2038,13 +2042,16 @@ void e1000e_set_interrupt_capability(struct e1000_adapter *adapter)
msix_entry),
GFP_KERNEL);
if (adapter->msix_entries) {
+ struct e1000_adapter *a = adapter;
+
for (i = 0; i < adapter->num_vectors; i++)
adapter->msix_entries[i].entry = i;
- err = pci_enable_msix(adapter->pdev,
- adapter->msix_entries,
- adapter->num_vectors);
- if (err == 0)
+ err = pci_enable_msix_range(a->pdev,
+ a->msix_entries,
+ a->num_vectors,
+ a->num_vectors);
+ if (err > 0)
return;
}
/* MSI-X failed, so fall through and try MSI */
@@ -2402,7 +2409,7 @@ static void e1000_clean_tx_ring(struct e1000_ring *tx_ring)
tx_ring->next_to_clean = 0;
writel(0, tx_ring->head);
- if (tx_ring->adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
+ if (adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
e1000e_update_tdt_wa(tx_ring, 0);
else
writel(0, tx_ring->tail);
@@ -2894,7 +2901,7 @@ static void e1000_configure_tx(struct e1000_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
struct e1000_ring *tx_ring = adapter->tx_ring;
u64 tdba;
- u32 tdlen, tarc;
+ u32 tdlen, tctl, tarc;
/* Setup the HW Tx Head and Tail descriptor pointers */
tdba = tx_ring->dma;
@@ -2931,6 +2938,12 @@ static void e1000_configure_tx(struct e1000_adapter *adapter)
/* erratum work around: set txdctl the same for both queues */
ew32(TXDCTL(1), er32(TXDCTL(0)));
+ /* Program the Transmit Control Register */
+ tctl = er32(TCTL);
+ tctl &= ~E1000_TCTL_CT;
+ tctl |= E1000_TCTL_PSP | E1000_TCTL_RTLC |
+ (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT);
+
if (adapter->flags & FLAG_TARC_SPEED_MODE_BIT) {
tarc = er32(TARC(0));
/* set the speed mode bit, we'll clear it if we're not at
@@ -2961,6 +2974,8 @@ static void e1000_configure_tx(struct e1000_adapter *adapter)
/* enable Report Status bit */
adapter->txd_cmd |= E1000_TXD_CMD_RS;
+ ew32(TCTL, tctl);
+
hw->mac.ops.config_collision_dist(hw);
}
@@ -2976,11 +2991,21 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter)
u32 rctl, rfctl;
u32 pages = 0;
- /* Workaround Si errata on PCHx - configure jumbo frame flow */
- if ((hw->mac.type >= e1000_pch2lan) &&
- (adapter->netdev->mtu > ETH_DATA_LEN) &&
- e1000_lv_jumbo_workaround_ich8lan(hw, true))
- e_dbg("failed to enable jumbo frame workaround mode\n");
+ /* Workaround Si errata on PCHx - configure jumbo frame flow.
+ * If jumbo frames not set, program related MAC/PHY registers
+ * to h/w defaults
+ */
+ if (hw->mac.type >= e1000_pch2lan) {
+ s32 ret_val;
+
+ if (adapter->netdev->mtu > ETH_DATA_LEN)
+ ret_val = e1000_lv_jumbo_workaround_ich8lan(hw, true);
+ else
+ ret_val = e1000_lv_jumbo_workaround_ich8lan(hw, false);
+
+ if (ret_val)
+ e_dbg("failed to enable|disable jumbo frame workaround mode\n");
+ }
/* Program MC offset vector base */
rctl = er32(RCTL);
@@ -3331,6 +3356,9 @@ static void e1000e_set_rx_mode(struct net_device *netdev)
struct e1000_hw *hw = &adapter->hw;
u32 rctl;
+ if (pm_runtime_suspended(netdev->dev.parent))
+ return;
+
/* Check for Promiscuous and All Multicast modes */
rctl = er32(RCTL);
@@ -3691,10 +3719,6 @@ void e1000e_power_up_phy(struct e1000_adapter *adapter)
*/
static void e1000_power_down_phy(struct e1000_adapter *adapter)
{
- /* WoL is enabled */
- if (adapter->wol)
- return;
-
if (adapter->hw.phy.ops.power_down)
adapter->hw.phy.ops.power_down(&adapter->hw);
}
@@ -3911,10 +3935,8 @@ void e1000e_reset(struct e1000_adapter *adapter)
}
if (!netif_running(adapter->netdev) &&
- !test_bit(__E1000_TESTING, &adapter->state)) {
+ !test_bit(__E1000_TESTING, &adapter->state))
e1000_power_down_phy(adapter);
- return;
- }
e1000_get_phy_info(hw);
@@ -3981,7 +4003,12 @@ static void e1000e_flush_descriptors(struct e1000_adapter *adapter)
static void e1000e_update_stats(struct e1000_adapter *adapter);
-void e1000e_down(struct e1000_adapter *adapter)
+/**
+ * e1000e_down - quiesce the device and optionally reset the hardware
+ * @adapter: board private structure
+ * @reset: boolean flag to reset the hardware or not
+ */
+void e1000e_down(struct e1000_adapter *adapter, bool reset)
{
struct net_device *netdev = adapter->netdev;
struct e1000_hw *hw = &adapter->hw;
@@ -4035,12 +4062,8 @@ void e1000e_down(struct e1000_adapter *adapter)
e1000_lv_jumbo_workaround_ich8lan(hw, false))
e_dbg("failed to disable jumbo frame workaround mode\n");
- if (!pci_channel_offline(adapter->pdev))
+ if (reset && !pci_channel_offline(adapter->pdev))
e1000e_reset(adapter);
-
- /* TODO: for power management, we could drop the link and
- * pci_disable_device here.
- */
}
void e1000e_reinit_locked(struct e1000_adapter *adapter)
@@ -4048,7 +4071,7 @@ void e1000e_reinit_locked(struct e1000_adapter *adapter)
might_sleep();
while (test_and_set_bit(__E1000_RESETTING, &adapter->state))
usleep_range(1000, 2000);
- e1000e_down(adapter);
+ e1000e_down(adapter, true);
e1000e_up(adapter);
clear_bit(__E1000_RESETTING, &adapter->state);
}
@@ -4326,7 +4349,6 @@ static int e1000_open(struct net_device *netdev)
adapter->tx_hang_recheck = false;
netif_start_queue(netdev);
- adapter->idle_check = true;
hw->mac.get_link_status = true;
pm_runtime_put(&pdev->dev);
@@ -4376,14 +4398,15 @@ static int e1000_close(struct net_device *netdev)
pm_runtime_get_sync(&pdev->dev);
if (!test_bit(__E1000_DOWN, &adapter->state)) {
- e1000e_down(adapter);
+ e1000e_down(adapter, true);
e1000_free_irq(adapter);
+
+ /* Link status message must follow this format */
+ pr_info("%s NIC Link is Down\n", adapter->netdev->name);
}
napi_disable(&adapter->napi);
- e1000_power_down_phy(adapter);
-
e1000e_free_tx_resources(adapter->tx_ring);
e1000e_free_rx_resources(adapter->rx_ring);
@@ -4460,11 +4483,16 @@ static void e1000e_update_phy_task(struct work_struct *work)
struct e1000_adapter *adapter = container_of(work,
struct e1000_adapter,
update_phy_task);
+ struct e1000_hw *hw = &adapter->hw;
if (test_bit(__E1000_DOWN, &adapter->state))
return;
- e1000_get_phy_info(&adapter->hw);
+ e1000_get_phy_info(hw);
+
+ /* Enable EEE on 82579 after link up */
+ if (hw->phy.type == e1000_phy_82579)
+ e1000_set_eee_pchlan(hw);
}
/**
@@ -4799,6 +4827,7 @@ static void e1000e_check_82574_phy_workaround(struct e1000_adapter *adapter)
if (adapter->phy_hang_count > 1) {
adapter->phy_hang_count = 0;
+ e_dbg("PHY appears hung - resetting\n");
schedule_work(&adapter->reset_task);
}
}
@@ -4957,15 +4986,11 @@ static void e1000_watchdog_task(struct work_struct *work)
mod_timer(&adapter->phy_info_timer,
round_jiffies(jiffies + 2 * HZ));
- /* The link is lost so the controller stops DMA.
- * If there is queued Tx work that cannot be done
- * or if on an 8000ES2LAN which requires a Rx packet
- * buffer work-around on link down event, reset the
- * controller to flush the Tx/Rx packet buffers.
- * (Do the reset outside of interrupt context).
+ /* 8000ES2LAN requires a Rx packet buffer work-around
+ * on link down event; reset the controller to flush
+ * the Rx packet buffer.
*/
- if ((adapter->flags & FLAG_RX_NEEDS_RESTART) ||
- (e1000_desc_unused(tx_ring) + 1 < tx_ring->count))
+ if (adapter->flags & FLAG_RX_NEEDS_RESTART)
adapter->flags |= FLAG_RESTART_NOW;
else
pm_schedule_suspend(netdev->dev.parent,
@@ -4988,6 +5013,15 @@ link_up:
adapter->gotc_old = adapter->stats.gotc;
spin_unlock(&adapter->stats64_lock);
+ /* If the link is lost the controller stops DMA, but
+ * if there is queued Tx work it cannot be done. So
+ * reset the controller to flush the Tx packet buffers.
+ */
+ if (!netif_carrier_ok(netdev) &&
+ (e1000_desc_unused(tx_ring) + 1 < tx_ring->count))
+ adapter->flags |= FLAG_RESTART_NOW;
+
+ /* If reset is necessary, do it outside of interrupt context. */
if (adapter->flags & FLAG_RESTART_NOW) {
schedule_work(&adapter->reset_task);
/* return immediately since reset is imminent */
@@ -5546,6 +5580,7 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
tx_flags |= E1000_TX_FLAGS_HWTSTAMP;
adapter->tx_hwtstamp_skb = skb_get(skb);
+ adapter->tx_hwtstamp_start = jiffies;
schedule_work(&adapter->tx_hwtstamp_work);
} else {
skb_tx_timestamp(skb);
@@ -5684,8 +5719,11 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu)
adapter->max_frame_size = max_frame;
e_info("changing MTU from %d to %d\n", netdev->mtu, new_mtu);
netdev->mtu = new_mtu;
+
+ pm_runtime_get_sync(netdev->dev.parent);
+
if (netif_running(netdev))
- e1000e_down(adapter);
+ e1000e_down(adapter, true);
/* NOTE: netdev_alloc_skb reserves 16 bytes, and typically NET_IP_ALIGN
* means we reserve 2 more, this pushes us to allocate from the next
@@ -5711,6 +5749,8 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu)
else
e1000e_reset(adapter);
+ pm_runtime_put_sync(netdev->dev.parent);
+
clear_bit(__E1000_RESETTING, &adapter->state);
return 0;
@@ -5852,7 +5892,7 @@ static int e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
static int e1000_init_phy_wakeup(struct e1000_adapter *adapter, u32 wufc)
{
struct e1000_hw *hw = &adapter->hw;
- u32 i, mac_reg;
+ u32 i, mac_reg, wuc;
u16 phy_reg, wuc_enable;
int retval;
@@ -5899,13 +5939,18 @@ static int e1000_init_phy_wakeup(struct e1000_adapter *adapter, u32 wufc)
phy_reg |= BM_RCTL_RFCE;
hw->phy.ops.write_reg_page(&adapter->hw, BM_RCTL, phy_reg);
+ wuc = E1000_WUC_PME_EN;
+ if (wufc & (E1000_WUFC_MAG | E1000_WUFC_LNKC))
+ wuc |= E1000_WUC_APME;
+
/* enable PHY wakeup in MAC register */
ew32(WUFC, wufc);
- ew32(WUC, E1000_WUC_PHY_WAKE | E1000_WUC_PME_EN);
+ ew32(WUC, (E1000_WUC_PHY_WAKE | E1000_WUC_APMPME |
+ E1000_WUC_PME_STATUS | wuc));
/* configure and enable PHY wakeup in PHY registers */
hw->phy.ops.write_reg_page(&adapter->hw, BM_WUFC, wufc);
- hw->phy.ops.write_reg_page(&adapter->hw, BM_WUC, E1000_WUC_PME_EN);
+ hw->phy.ops.write_reg_page(&adapter->hw, BM_WUC, wuc);
/* activate PHY wakeup */
wuc_enable |= BM_WUC_ENABLE_BIT | BM_WUC_HOST_WU_BIT;
@@ -5918,15 +5963,10 @@ release:
return retval;
}
-static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)
+static int e1000e_pm_freeze(struct device *dev)
{
- struct net_device *netdev = pci_get_drvdata(pdev);
+ struct net_device *netdev = pci_get_drvdata(to_pci_dev(dev));
struct e1000_adapter *adapter = netdev_priv(netdev);
- struct e1000_hw *hw = &adapter->hw;
- u32 ctrl, ctrl_ext, rctl, status;
- /* Runtime suspend should only enable wakeup for link changes */
- u32 wufc = runtime ? E1000_WUFC_LNKC : adapter->wol;
- int retval = 0;
netif_device_detach(netdev);
@@ -5937,11 +5977,29 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)
usleep_range(10000, 20000);
WARN_ON(test_bit(__E1000_RESETTING, &adapter->state));
- e1000e_down(adapter);
+
+ /* Quiesce the device without resetting the hardware */
+ e1000e_down(adapter, false);
e1000_free_irq(adapter);
}
e1000e_reset_interrupt_capability(adapter);
+ /* Allow time for pending master requests to run */
+ e1000e_disable_pcie_master(&adapter->hw);
+
+ return 0;
+}
+
+static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+ struct e1000_hw *hw = &adapter->hw;
+ u32 ctrl, ctrl_ext, rctl, status;
+ /* Runtime suspend should only enable wakeup for link changes */
+ u32 wufc = runtime ? E1000_WUFC_LNKC : adapter->wol;
+ int retval = 0;
+
status = er32(STATUS);
if (status & E1000_STATUS_LU)
wufc &= ~E1000_WUFC_LNKC;
@@ -5972,12 +6030,12 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)
ew32(CTRL_EXT, ctrl_ext);
}
+ if (!runtime)
+ e1000e_power_up_phy(adapter);
+
if (adapter->flags & FLAG_IS_ICH)
e1000_suspend_workarounds_ich8lan(&adapter->hw);
- /* Allow time for pending master requests to run */
- e1000e_disable_pcie_master(&adapter->hw);
-
if (adapter->flags2 & FLAG2_HAS_PHY_WAKEUP) {
/* enable wakeup by the PHY */
retval = e1000_init_phy_wakeup(adapter, wufc);
@@ -5991,10 +6049,23 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)
} else {
ew32(WUC, 0);
ew32(WUFC, 0);
+
+ e1000_power_down_phy(adapter);
}
- if (adapter->hw.phy.type == e1000_phy_igp_3)
+ if (adapter->hw.phy.type == e1000_phy_igp_3) {
e1000e_igp3_phy_powerdown_workaround_ich8lan(&adapter->hw);
+ } else if (hw->mac.type == e1000_pch_lpt) {
+ if (!(wufc & (E1000_WUFC_EX | E1000_WUFC_MC | E1000_WUFC_BC)))
+ /* ULP does not support wake from unicast, multicast
+ * or broadcast.
+ */
+ retval = e1000_enable_ulp_lpt_lp(hw, !runtime);
+
+ if (retval)
+ return retval;
+ }
+
/* Release control of h/w to f/w. If f/w is AMT enabled, this
* would have already happened in close and is redundant.
@@ -6102,18 +6173,12 @@ static void e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
}
#ifdef CONFIG_PM
-static bool e1000e_pm_ready(struct e1000_adapter *adapter)
-{
- return !!adapter->tx_ring->buffer_info;
-}
-
static int __e1000_resume(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
u16 aspm_disable_flag = 0;
- u32 err;
if (adapter->flags2 & FLAG2_DISABLE_ASPM_L0S)
aspm_disable_flag = PCIE_LINK_STATE_L0S;
@@ -6124,13 +6189,6 @@ static int __e1000_resume(struct pci_dev *pdev)
pci_set_master(pdev);
- e1000e_set_interrupt_capability(adapter);
- if (netif_running(netdev)) {
- err = e1000_request_irq(adapter);
- if (err)
- return err;
- }
-
if (hw->mac.type >= e1000_pch2lan)
e1000_resume_workarounds_pchlan(&adapter->hw);
@@ -6169,11 +6227,6 @@ static int __e1000_resume(struct pci_dev *pdev)
e1000_init_manageability_pt(adapter);
- if (netif_running(netdev))
- e1000e_up(adapter);
-
- netif_device_attach(netdev);
-
/* If the controller has AMT, do not set DRV_LOAD until the interface
* is up. For all other cases, let the f/w know that the h/w is now
* under the control of the driver.
@@ -6184,75 +6237,111 @@ static int __e1000_resume(struct pci_dev *pdev)
return 0;
}
+static int e1000e_pm_thaw(struct device *dev)
+{
+ struct net_device *netdev = pci_get_drvdata(to_pci_dev(dev));
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+
+ e1000e_set_interrupt_capability(adapter);
+ if (netif_running(netdev)) {
+ u32 err = e1000_request_irq(adapter);
+
+ if (err)
+ return err;
+
+ e1000e_up(adapter);
+ }
+
+ netif_device_attach(netdev);
+
+ return 0;
+}
+
#ifdef CONFIG_PM_SLEEP
-static int e1000_suspend(struct device *dev)
+static int e1000e_pm_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
+ e1000e_pm_freeze(dev);
+
return __e1000_shutdown(pdev, false);
}
-static int e1000_resume(struct device *dev)
+static int e1000e_pm_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
- struct net_device *netdev = pci_get_drvdata(pdev);
- struct e1000_adapter *adapter = netdev_priv(netdev);
+ int rc;
- if (e1000e_pm_ready(adapter))
- adapter->idle_check = true;
+ rc = __e1000_resume(pdev);
+ if (rc)
+ return rc;
- return __e1000_resume(pdev);
+ return e1000e_pm_thaw(dev);
}
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM_RUNTIME
-static int e1000_runtime_suspend(struct device *dev)
+static int e1000e_pm_runtime_idle(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct net_device *netdev = pci_get_drvdata(pdev);
struct e1000_adapter *adapter = netdev_priv(netdev);
- if (!e1000e_pm_ready(adapter))
- return 0;
+ if (!e1000e_has_link(adapter))
+ pm_schedule_suspend(dev, 5 * MSEC_PER_SEC);
- return __e1000_shutdown(pdev, true);
+ return -EBUSY;
}
-static int e1000_idle(struct device *dev)
+static int e1000e_pm_runtime_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct net_device *netdev = pci_get_drvdata(pdev);
struct e1000_adapter *adapter = netdev_priv(netdev);
+ int rc;
- if (!e1000e_pm_ready(adapter))
- return 0;
+ rc = __e1000_resume(pdev);
+ if (rc)
+ return rc;
- if (adapter->idle_check) {
- adapter->idle_check = false;
- if (!e1000e_has_link(adapter))
- pm_schedule_suspend(dev, MSEC_PER_SEC);
- }
+ if (netdev->flags & IFF_UP)
+ rc = e1000e_up(adapter);
- return -EBUSY;
+ return rc;
}
-static int e1000_runtime_resume(struct device *dev)
+static int e1000e_pm_runtime_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct net_device *netdev = pci_get_drvdata(pdev);
struct e1000_adapter *adapter = netdev_priv(netdev);
- if (!e1000e_pm_ready(adapter))
- return 0;
+ if (netdev->flags & IFF_UP) {
+ int count = E1000_CHECK_RESET_COUNT;
+
+ while (test_bit(__E1000_RESETTING, &adapter->state) && count--)
+ usleep_range(10000, 20000);
+
+ WARN_ON(test_bit(__E1000_RESETTING, &adapter->state));
+
+ /* Down the device without resetting the hardware */
+ e1000e_down(adapter, false);
+ }
- adapter->idle_check = !dev->power.runtime_auto;
- return __e1000_resume(pdev);
+ if (__e1000_shutdown(pdev, true)) {
+ e1000e_pm_runtime_resume(dev);
+ return -EBUSY;
+ }
+
+ return 0;
}
#endif /* CONFIG_PM_RUNTIME */
#endif /* CONFIG_PM */
static void e1000_shutdown(struct pci_dev *pdev)
{
+ e1000e_pm_freeze(&pdev->dev);
+
__e1000_shutdown(pdev, false);
}
@@ -6338,7 +6427,7 @@ static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev,
return PCI_ERS_RESULT_DISCONNECT;
if (netif_running(netdev))
- e1000e_down(adapter);
+ e1000e_down(adapter, true);
pci_disable_device(pdev);
/* Request a slot slot reset. */
@@ -6350,7 +6439,7 @@ static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev,
* @pdev: Pointer to PCI device
*
* Restart the card from scratch, as if from a cold-boot. Implementation
- * resembles the first-half of the e1000_resume routine.
+ * resembles the first-half of the e1000e_pm_resume routine.
*/
static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev)
{
@@ -6397,7 +6486,7 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev)
*
* This callback is called when the error recovery driver tells us that
* its OK to resume normal operation. Implementation resembles the
- * second-half of the e1000_resume routine.
+ * second-half of the e1000e_pm_resume routine.
*/
static void e1000_io_resume(struct pci_dev *pdev)
{
@@ -6902,9 +6991,6 @@ static void e1000_remove(struct pci_dev *pdev)
}
}
- if (!(netdev->flags & IFF_UP))
- e1000_power_down_phy(adapter);
-
/* Don't lie to e1000_close() down the road. */
if (!down)
clear_bit(__E1000_DOWN, &adapter->state);
@@ -7026,9 +7112,16 @@ static DEFINE_PCI_DEVICE_TABLE(e1000_pci_tbl) = {
MODULE_DEVICE_TABLE(pci, e1000_pci_tbl);
static const struct dev_pm_ops e1000_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(e1000_suspend, e1000_resume)
- SET_RUNTIME_PM_OPS(e1000_runtime_suspend, e1000_runtime_resume,
- e1000_idle)
+#ifdef CONFIG_PM_SLEEP
+ .suspend = e1000e_pm_suspend,
+ .resume = e1000e_pm_resume,
+ .freeze = e1000e_pm_freeze,
+ .thaw = e1000e_pm_thaw,
+ .poweroff = e1000e_pm_suspend,
+ .restore = e1000e_pm_resume,
+#endif
+ SET_RUNTIME_PM_OPS(e1000e_pm_runtime_suspend, e1000e_pm_runtime_resume,
+ e1000e_pm_runtime_idle)
};
/* PCI Device API Driver */
@@ -7055,7 +7148,7 @@ 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 - 2013 Intel Corporation.\n");
+ pr_info("Copyright(c) 1999 - 2014 Intel Corporation.\n");
ret = pci_register_driver(&e1000_driver);
return ret;
diff --git a/drivers/net/ethernet/intel/e1000e/nvm.c b/drivers/net/ethernet/intel/e1000e/nvm.c
index d70a03906ac0..a9a976f04bff 100644
--- a/drivers/net/ethernet/intel/e1000e/nvm.c
+++ b/drivers/net/ethernet/intel/e1000e/nvm.c
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#include "e1000.h"
diff --git a/drivers/net/ethernet/intel/e1000e/nvm.h b/drivers/net/ethernet/intel/e1000e/nvm.h
index 45fc69561627..342bf69efab5 100644
--- a/drivers/net/ethernet/intel/e1000e/nvm.h
+++ b/drivers/net/ethernet/intel/e1000e/nvm.h
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#ifndef _E1000E_NVM_H_
#define _E1000E_NVM_H_
diff --git a/drivers/net/ethernet/intel/e1000e/param.c b/drivers/net/ethernet/intel/e1000e/param.c
index c16bd75b6caa..d0ac0f3249c8 100644
--- a/drivers/net/ethernet/intel/e1000e/param.c
+++ b/drivers/net/ethernet/intel/e1000e/param.c
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#include <linux/netdevice.h>
#include <linux/module.h>
@@ -381,6 +374,12 @@ void e1000e_check_options(struct e1000_adapter *adapter)
"%s set to dynamic mode\n", opt.name);
adapter->itr = 20000;
break;
+ case 2:
+ dev_info(&adapter->pdev->dev,
+ "%s Invalid mode - setting default\n",
+ opt.name);
+ adapter->itr_setting = opt.def;
+ /* fall-through */
case 3:
dev_info(&adapter->pdev->dev,
"%s set to dynamic conservative mode\n",
diff --git a/drivers/net/ethernet/intel/e1000e/phy.c b/drivers/net/ethernet/intel/e1000e/phy.c
index 20e71f4ca426..00b3fc98bf30 100644
--- a/drivers/net/ethernet/intel/e1000e/phy.c
+++ b/drivers/net/ethernet/intel/e1000e/phy.c
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#include "e1000.h"
diff --git a/drivers/net/ethernet/intel/e1000e/phy.h b/drivers/net/ethernet/intel/e1000e/phy.h
index f4f71b9991e3..3841bccf058c 100644
--- a/drivers/net/ethernet/intel/e1000e/phy.h
+++ b/drivers/net/ethernet/intel/e1000e/phy.h
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#ifndef _E1000E_PHY_H_
#define _E1000E_PHY_H_
diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c
index 065f8c80d4f2..fb1a914a3ad4 100644
--- a/drivers/net/ethernet/intel/e1000e/ptp.c
+++ b/drivers/net/ethernet/intel/e1000e/ptp.c
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
/* PTP 1588 Hardware Clock (PHC)
* Derived from PTP Hardware Clock driver for Intel 82576 and 82580 (igb)
@@ -47,6 +40,7 @@ static int e1000e_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta)
ptp_clock_info);
struct e1000_hw *hw = &adapter->hw;
bool neg_adj = false;
+ unsigned long flags;
u64 adjustment;
u32 timinca, incvalue;
s32 ret_val;
@@ -64,6 +58,8 @@ static int e1000e_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta)
if (ret_val)
return ret_val;
+ spin_lock_irqsave(&adapter->systim_lock, flags);
+
incvalue = timinca & E1000_TIMINCA_INCVALUE_MASK;
adjustment = incvalue;
@@ -77,6 +73,8 @@ static int e1000e_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta)
ew32(TIMINCA, timinca);
+ spin_unlock_irqrestore(&adapter->systim_lock, flags);
+
return 0;
}
@@ -191,6 +189,7 @@ static const struct ptp_clock_info e1000e_ptp_clock_info = {
.n_alarm = 0,
.n_ext_ts = 0,
.n_per_out = 0,
+ .n_pins = 0,
.pps = 0,
.adjfreq = e1000e_phc_adjfreq,
.adjtime = e1000e_phc_adjtime,
diff --git a/drivers/net/ethernet/intel/e1000e/regs.h b/drivers/net/ethernet/intel/e1000e/regs.h
index a7e6a3e37257..ea235bbe50d3 100644
--- a/drivers/net/ethernet/intel/e1000e/regs.h
+++ b/drivers/net/ethernet/intel/e1000e/regs.h
@@ -1,30 +1,23 @@
-/*******************************************************************************
-
- Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
-
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
-
- The full GNU General Public License is included in this distribution in
- the file called "COPYING".
-
- Contact Information:
- Linux NICS <linux.nics@intel.com>
- e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel PRO/1000 Linux driver
+ * Copyright(c) 1999 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Linux NICS <linux.nics@intel.com>
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
#ifndef _E1000E_REGS_H_
#define _E1000E_REGS_H_
@@ -39,6 +32,7 @@
#define E1000_SCTL 0x00024 /* SerDes Control - RW */
#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */
#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */
+#define E1000_FEXT 0x0002C /* Future Extended - RW */
#define E1000_FEXTNVM 0x00028 /* Future Extended NVM - RW */
#define E1000_FEXTNVM3 0x0003C /* Future Extended NVM 3 - RW */
#define E1000_FEXTNVM4 0x00024 /* Future Extended NVM 4 - RW */
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 72dae4d97b43..beb7b4393a6c 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -86,12 +86,12 @@
#define I40E_NVM_VERSION_LO_SHIFT 0
#define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT)
-#define I40E_NVM_VERSION_HI_SHIFT 8
-#define I40E_NVM_VERSION_HI_MASK (0xff << I40E_NVM_VERSION_HI_SHIFT)
+#define I40E_NVM_VERSION_HI_SHIFT 12
+#define I40E_NVM_VERSION_HI_MASK (0xf << I40E_NVM_VERSION_HI_SHIFT)
/* The values in here are decimal coded as hex as is the case in the NVM map*/
#define I40E_CURRENT_NVM_VERSION_HI 0x2
-#define I40E_CURRENT_NVM_VERSION_LO 0x30
+#define I40E_CURRENT_NVM_VERSION_LO 0x40
/* magic for getting defines into strings */
#define STRINGIFY(foo) #foo
@@ -136,6 +136,7 @@ enum i40e_state_t {
__I40E_EMP_RESET_REQUESTED,
__I40E_FILTER_OVERFLOW_PROMISC,
__I40E_SUSPENDED,
+ __I40E_BAD_EEPROM,
};
enum i40e_interrupt_policy {
@@ -152,8 +153,21 @@ struct i40e_lump_tracking {
};
#define I40E_DEFAULT_ATR_SAMPLE_RATE 20
-#define I40E_FDIR_MAX_RAW_PACKET_LOOKUP 512
-struct i40e_fdir_data {
+#define I40E_FDIR_MAX_RAW_PACKET_SIZE 512
+#define I40E_FDIR_BUFFER_FULL_MARGIN 10
+#define I40E_FDIR_BUFFER_HEAD_ROOM 200
+
+struct i40e_fdir_filter {
+ struct hlist_node fdir_node;
+ /* filter ipnut set */
+ u8 flow_type;
+ u8 ip4_proto;
+ __be32 dst_ip[4];
+ __be32 src_ip[4];
+ __be16 src_port;
+ __be16 dst_port;
+ __be32 sctp_v_tag;
+ /* filter control */
u16 q_index;
u8 flex_off;
u8 pctype;
@@ -162,7 +176,6 @@ struct i40e_fdir_data {
u8 fd_status;
u16 cnt_index;
u32 fd_id;
- u8 *raw_packet;
};
#define I40E_ETH_P_LLDP 0x88cc
@@ -196,7 +209,7 @@ struct i40e_pf {
bool fc_autoneg_status;
u16 eeprom_version;
- u16 num_vmdq_vsis; /* num vmdq pools this pf has set up */
+ u16 num_vmdq_vsis; /* num vmdq vsis this pf has set up */
u16 num_vmdq_qps; /* num queue pairs per vmdq pool */
u16 num_vmdq_msix; /* num queue vectors per vmdq pool */
u16 num_req_vfs; /* num vfs requested for this vf */
@@ -210,6 +223,9 @@ struct i40e_pf {
u8 atr_sample_rate;
bool wol_en;
+ struct hlist_head fdir_filter_list;
+ u16 fdir_pf_active_filters;
+
#ifdef CONFIG_I40E_VXLAN
__be16 vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
u16 pending_vxlan_bitmap;
@@ -251,6 +267,9 @@ struct i40e_pf {
#define I40E_FLAG_VXLAN_FILTER_SYNC (u64)(1 << 27)
#endif
+ /* tracks features that get auto disabled by errors */
+ u64 auto_disable_flags;
+
bool stat_offsets_loaded;
struct i40e_hw_port_stats stats;
struct i40e_hw_port_stats stats_offsets;
@@ -477,10 +496,10 @@ static inline char *i40e_fw_version_str(struct i40e_hw *hw)
"f%d.%d a%d.%d n%02x.%02x e%08x",
hw->aq.fw_maj_ver, hw->aq.fw_min_ver,
hw->aq.api_maj_ver, hw->aq.api_min_ver,
- (hw->nvm.version & I40E_NVM_VERSION_HI_MASK)
- >> I40E_NVM_VERSION_HI_SHIFT,
- (hw->nvm.version & I40E_NVM_VERSION_LO_MASK)
- >> I40E_NVM_VERSION_LO_SHIFT,
+ (hw->nvm.version & I40E_NVM_VERSION_HI_MASK) >>
+ I40E_NVM_VERSION_HI_SHIFT,
+ (hw->nvm.version & I40E_NVM_VERSION_LO_MASK) >>
+ I40E_NVM_VERSION_LO_SHIFT,
hw->nvm.eetrack);
return buf;
@@ -534,9 +553,13 @@ struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi);
int i40e_fetch_switch_configuration(struct i40e_pf *pf,
bool printconfig);
-int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data,
+int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet,
struct i40e_pf *pf, bool add);
-
+int i40e_add_del_fdir(struct i40e_vsi *vsi,
+ struct i40e_fdir_filter *input, bool add);
+void i40e_fdir_check_and_reenable(struct i40e_pf *pf);
+int i40e_get_current_fd_count(struct i40e_pf *pf);
+bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features);
void i40e_set_ethtool_ops(struct net_device *netdev);
struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
u8 *macaddr, s16 vlan,
@@ -575,6 +598,7 @@ void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector);
void i40e_irq_dynamic_disable_icr0(struct i40e_pf *pf);
void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf);
int i40e_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
+int i40e_vsi_open(struct i40e_vsi *vsi);
void i40e_vlan_stripping_disable(struct i40e_vsi *vsi);
int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid);
int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c
index a50e6b3479ae..ed3902bf249b 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c
@@ -647,9 +647,8 @@ static u16 i40e_clean_asq(struct i40e_hw *hw)
desc_cb = *desc;
cb_func(hw, &desc_cb);
}
- memset((void *)desc, 0, sizeof(struct i40e_aq_desc));
- memset((void *)details, 0,
- sizeof(struct i40e_asq_cmd_details));
+ memset(desc, 0, sizeof(*desc));
+ memset(details, 0, sizeof(*details));
ntc++;
if (ntc == asq->count)
ntc = 0;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index e7f38b57834d..922cdcc45c54 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -162,6 +162,372 @@ i40e_status i40e_aq_queue_shutdown(struct i40e_hw *hw,
return status;
}
+/* The i40e_ptype_lookup table is used to convert from the 8-bit ptype in the
+ * hardware to a bit-field that can be used by SW to more easily determine the
+ * packet type.
+ *
+ * Macros are used to shorten the table lines and make this table human
+ * readable.
+ *
+ * We store the PTYPE in the top byte of the bit field - this is just so that
+ * we can check that the table doesn't have a row missing, as the index into
+ * the table should be the PTYPE.
+ *
+ * Typical work flow:
+ *
+ * IF NOT i40e_ptype_lookup[ptype].known
+ * THEN
+ * Packet is unknown
+ * ELSE IF i40e_ptype_lookup[ptype].outer_ip == I40E_RX_PTYPE_OUTER_IP
+ * Use the rest of the fields to look at the tunnels, inner protocols, etc
+ * ELSE
+ * Use the enum i40e_rx_l2_ptype to decode the packet type
+ * ENDIF
+ */
+
+/* macro to make the table lines short */
+#define I40E_PTT(PTYPE, OUTER_IP, OUTER_IP_VER, OUTER_FRAG, T, TE, TEF, I, PL)\
+ { PTYPE, \
+ 1, \
+ I40E_RX_PTYPE_OUTER_##OUTER_IP, \
+ I40E_RX_PTYPE_OUTER_##OUTER_IP_VER, \
+ I40E_RX_PTYPE_##OUTER_FRAG, \
+ I40E_RX_PTYPE_TUNNEL_##T, \
+ I40E_RX_PTYPE_TUNNEL_END_##TE, \
+ I40E_RX_PTYPE_##TEF, \
+ I40E_RX_PTYPE_INNER_PROT_##I, \
+ I40E_RX_PTYPE_PAYLOAD_LAYER_##PL }
+
+#define I40E_PTT_UNUSED_ENTRY(PTYPE) \
+ { PTYPE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+/* shorter macros makes the table fit but are terse */
+#define I40E_RX_PTYPE_NOF I40E_RX_PTYPE_NOT_FRAG
+#define I40E_RX_PTYPE_FRG I40E_RX_PTYPE_FRAG
+#define I40E_RX_PTYPE_INNER_PROT_TS I40E_RX_PTYPE_INNER_PROT_TIMESYNC
+
+/* Lookup table mapping the HW PTYPE to the bit field for decoding */
+struct i40e_rx_ptype_decoded i40e_ptype_lookup[] = {
+ /* L2 Packet types */
+ I40E_PTT_UNUSED_ENTRY(0),
+ I40E_PTT(1, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+ I40E_PTT(2, L2, NONE, NOF, NONE, NONE, NOF, TS, PAY2),
+ I40E_PTT(3, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+ I40E_PTT_UNUSED_ENTRY(4),
+ I40E_PTT_UNUSED_ENTRY(5),
+ I40E_PTT(6, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+ I40E_PTT(7, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+ I40E_PTT_UNUSED_ENTRY(8),
+ I40E_PTT_UNUSED_ENTRY(9),
+ I40E_PTT(10, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+ I40E_PTT(11, L2, NONE, NOF, NONE, NONE, NOF, NONE, NONE),
+ I40E_PTT(12, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(13, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(14, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(15, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(16, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(17, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(18, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(19, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(20, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(21, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+
+ /* Non Tunneled IPv4 */
+ I40E_PTT(22, IP, IPV4, FRG, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(23, IP, IPV4, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(24, IP, IPV4, NOF, NONE, NONE, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(25),
+ I40E_PTT(26, IP, IPV4, NOF, NONE, NONE, NOF, TCP, PAY4),
+ I40E_PTT(27, IP, IPV4, NOF, NONE, NONE, NOF, SCTP, PAY4),
+ I40E_PTT(28, IP, IPV4, NOF, NONE, NONE, NOF, ICMP, PAY4),
+
+ /* IPv4 --> IPv4 */
+ I40E_PTT(29, IP, IPV4, NOF, IP_IP, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(30, IP, IPV4, NOF, IP_IP, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(31, IP, IPV4, NOF, IP_IP, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(32),
+ I40E_PTT(33, IP, IPV4, NOF, IP_IP, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(34, IP, IPV4, NOF, IP_IP, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(35, IP, IPV4, NOF, IP_IP, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv4 --> IPv6 */
+ I40E_PTT(36, IP, IPV4, NOF, IP_IP, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(37, IP, IPV4, NOF, IP_IP, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(38, IP, IPV4, NOF, IP_IP, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(39),
+ I40E_PTT(40, IP, IPV4, NOF, IP_IP, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(41, IP, IPV4, NOF, IP_IP, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(42, IP, IPV4, NOF, IP_IP, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv4 --> GRE/NAT */
+ I40E_PTT(43, IP, IPV4, NOF, IP_GRENAT, NONE, NOF, NONE, PAY3),
+
+ /* IPv4 --> GRE/NAT --> IPv4 */
+ I40E_PTT(44, IP, IPV4, NOF, IP_GRENAT, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(45, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(46, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(47),
+ I40E_PTT(48, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(49, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(50, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv4 --> GRE/NAT --> IPv6 */
+ I40E_PTT(51, IP, IPV4, NOF, IP_GRENAT, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(52, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(53, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(54),
+ I40E_PTT(55, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(56, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(57, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv4 --> GRE/NAT --> MAC */
+ I40E_PTT(58, IP, IPV4, NOF, IP_GRENAT_MAC, NONE, NOF, NONE, PAY3),
+
+ /* IPv4 --> GRE/NAT --> MAC --> IPv4 */
+ I40E_PTT(59, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(60, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(61, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(62),
+ I40E_PTT(63, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(64, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(65, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv4 --> GRE/NAT -> MAC --> IPv6 */
+ I40E_PTT(66, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(67, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(68, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(69),
+ I40E_PTT(70, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(71, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(72, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv4 --> GRE/NAT --> MAC/VLAN */
+ I40E_PTT(73, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, NONE, NOF, NONE, PAY3),
+
+ /* IPv4 ---> GRE/NAT -> MAC/VLAN --> IPv4 */
+ I40E_PTT(74, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(75, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(76, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(77),
+ I40E_PTT(78, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(79, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(80, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv4 -> GRE/NAT -> MAC/VLAN --> IPv6 */
+ I40E_PTT(81, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(82, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(83, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(84),
+ I40E_PTT(85, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(86, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(87, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, ICMP, PAY4),
+
+ /* Non Tunneled IPv6 */
+ I40E_PTT(88, IP, IPV6, FRG, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(89, IP, IPV6, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(90, IP, IPV6, NOF, NONE, NONE, NOF, UDP, PAY3),
+ I40E_PTT_UNUSED_ENTRY(91),
+ I40E_PTT(92, IP, IPV6, NOF, NONE, NONE, NOF, TCP, PAY4),
+ I40E_PTT(93, IP, IPV6, NOF, NONE, NONE, NOF, SCTP, PAY4),
+ I40E_PTT(94, IP, IPV6, NOF, NONE, NONE, NOF, ICMP, PAY4),
+
+ /* IPv6 --> IPv4 */
+ I40E_PTT(95, IP, IPV6, NOF, IP_IP, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(96, IP, IPV6, NOF, IP_IP, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(97, IP, IPV6, NOF, IP_IP, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(98),
+ I40E_PTT(99, IP, IPV6, NOF, IP_IP, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(100, IP, IPV6, NOF, IP_IP, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(101, IP, IPV6, NOF, IP_IP, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv6 --> IPv6 */
+ I40E_PTT(102, IP, IPV6, NOF, IP_IP, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(103, IP, IPV6, NOF, IP_IP, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(104, IP, IPV6, NOF, IP_IP, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(105),
+ I40E_PTT(106, IP, IPV6, NOF, IP_IP, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(107, IP, IPV6, NOF, IP_IP, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(108, IP, IPV6, NOF, IP_IP, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT */
+ I40E_PTT(109, IP, IPV6, NOF, IP_GRENAT, NONE, NOF, NONE, PAY3),
+
+ /* IPv6 --> GRE/NAT -> IPv4 */
+ I40E_PTT(110, IP, IPV6, NOF, IP_GRENAT, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(111, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(112, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(113),
+ I40E_PTT(114, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(115, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(116, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT -> IPv6 */
+ I40E_PTT(117, IP, IPV6, NOF, IP_GRENAT, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(118, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(119, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(120),
+ I40E_PTT(121, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(122, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(123, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT -> MAC */
+ I40E_PTT(124, IP, IPV6, NOF, IP_GRENAT_MAC, NONE, NOF, NONE, PAY3),
+
+ /* IPv6 --> GRE/NAT -> MAC -> IPv4 */
+ I40E_PTT(125, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(126, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(127, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(128),
+ I40E_PTT(129, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(130, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(131, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT -> MAC -> IPv6 */
+ I40E_PTT(132, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(133, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(134, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(135),
+ I40E_PTT(136, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(137, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(138, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT -> MAC/VLAN */
+ I40E_PTT(139, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, NONE, NOF, NONE, PAY3),
+
+ /* IPv6 --> GRE/NAT -> MAC/VLAN --> IPv4 */
+ I40E_PTT(140, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(141, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(142, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(143),
+ I40E_PTT(144, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(145, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(146, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT -> MAC/VLAN --> IPv6 */
+ I40E_PTT(147, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(148, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(149, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(150),
+ I40E_PTT(151, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(152, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(153, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, ICMP, PAY4),
+
+ /* unused entries */
+ I40E_PTT_UNUSED_ENTRY(154),
+ I40E_PTT_UNUSED_ENTRY(155),
+ I40E_PTT_UNUSED_ENTRY(156),
+ I40E_PTT_UNUSED_ENTRY(157),
+ I40E_PTT_UNUSED_ENTRY(158),
+ I40E_PTT_UNUSED_ENTRY(159),
+
+ I40E_PTT_UNUSED_ENTRY(160),
+ I40E_PTT_UNUSED_ENTRY(161),
+ I40E_PTT_UNUSED_ENTRY(162),
+ I40E_PTT_UNUSED_ENTRY(163),
+ I40E_PTT_UNUSED_ENTRY(164),
+ I40E_PTT_UNUSED_ENTRY(165),
+ I40E_PTT_UNUSED_ENTRY(166),
+ I40E_PTT_UNUSED_ENTRY(167),
+ I40E_PTT_UNUSED_ENTRY(168),
+ I40E_PTT_UNUSED_ENTRY(169),
+
+ I40E_PTT_UNUSED_ENTRY(170),
+ I40E_PTT_UNUSED_ENTRY(171),
+ I40E_PTT_UNUSED_ENTRY(172),
+ I40E_PTT_UNUSED_ENTRY(173),
+ I40E_PTT_UNUSED_ENTRY(174),
+ I40E_PTT_UNUSED_ENTRY(175),
+ I40E_PTT_UNUSED_ENTRY(176),
+ I40E_PTT_UNUSED_ENTRY(177),
+ I40E_PTT_UNUSED_ENTRY(178),
+ I40E_PTT_UNUSED_ENTRY(179),
+
+ I40E_PTT_UNUSED_ENTRY(180),
+ I40E_PTT_UNUSED_ENTRY(181),
+ I40E_PTT_UNUSED_ENTRY(182),
+ I40E_PTT_UNUSED_ENTRY(183),
+ I40E_PTT_UNUSED_ENTRY(184),
+ I40E_PTT_UNUSED_ENTRY(185),
+ I40E_PTT_UNUSED_ENTRY(186),
+ I40E_PTT_UNUSED_ENTRY(187),
+ I40E_PTT_UNUSED_ENTRY(188),
+ I40E_PTT_UNUSED_ENTRY(189),
+
+ I40E_PTT_UNUSED_ENTRY(190),
+ I40E_PTT_UNUSED_ENTRY(191),
+ I40E_PTT_UNUSED_ENTRY(192),
+ I40E_PTT_UNUSED_ENTRY(193),
+ I40E_PTT_UNUSED_ENTRY(194),
+ I40E_PTT_UNUSED_ENTRY(195),
+ I40E_PTT_UNUSED_ENTRY(196),
+ I40E_PTT_UNUSED_ENTRY(197),
+ I40E_PTT_UNUSED_ENTRY(198),
+ I40E_PTT_UNUSED_ENTRY(199),
+
+ I40E_PTT_UNUSED_ENTRY(200),
+ I40E_PTT_UNUSED_ENTRY(201),
+ I40E_PTT_UNUSED_ENTRY(202),
+ I40E_PTT_UNUSED_ENTRY(203),
+ I40E_PTT_UNUSED_ENTRY(204),
+ I40E_PTT_UNUSED_ENTRY(205),
+ I40E_PTT_UNUSED_ENTRY(206),
+ I40E_PTT_UNUSED_ENTRY(207),
+ I40E_PTT_UNUSED_ENTRY(208),
+ I40E_PTT_UNUSED_ENTRY(209),
+
+ I40E_PTT_UNUSED_ENTRY(210),
+ I40E_PTT_UNUSED_ENTRY(211),
+ I40E_PTT_UNUSED_ENTRY(212),
+ I40E_PTT_UNUSED_ENTRY(213),
+ I40E_PTT_UNUSED_ENTRY(214),
+ I40E_PTT_UNUSED_ENTRY(215),
+ I40E_PTT_UNUSED_ENTRY(216),
+ I40E_PTT_UNUSED_ENTRY(217),
+ I40E_PTT_UNUSED_ENTRY(218),
+ I40E_PTT_UNUSED_ENTRY(219),
+
+ I40E_PTT_UNUSED_ENTRY(220),
+ I40E_PTT_UNUSED_ENTRY(221),
+ I40E_PTT_UNUSED_ENTRY(222),
+ I40E_PTT_UNUSED_ENTRY(223),
+ I40E_PTT_UNUSED_ENTRY(224),
+ I40E_PTT_UNUSED_ENTRY(225),
+ I40E_PTT_UNUSED_ENTRY(226),
+ I40E_PTT_UNUSED_ENTRY(227),
+ I40E_PTT_UNUSED_ENTRY(228),
+ I40E_PTT_UNUSED_ENTRY(229),
+
+ I40E_PTT_UNUSED_ENTRY(230),
+ I40E_PTT_UNUSED_ENTRY(231),
+ I40E_PTT_UNUSED_ENTRY(232),
+ I40E_PTT_UNUSED_ENTRY(233),
+ I40E_PTT_UNUSED_ENTRY(234),
+ I40E_PTT_UNUSED_ENTRY(235),
+ I40E_PTT_UNUSED_ENTRY(236),
+ I40E_PTT_UNUSED_ENTRY(237),
+ I40E_PTT_UNUSED_ENTRY(238),
+ I40E_PTT_UNUSED_ENTRY(239),
+
+ I40E_PTT_UNUSED_ENTRY(240),
+ I40E_PTT_UNUSED_ENTRY(241),
+ I40E_PTT_UNUSED_ENTRY(242),
+ I40E_PTT_UNUSED_ENTRY(243),
+ I40E_PTT_UNUSED_ENTRY(244),
+ I40E_PTT_UNUSED_ENTRY(245),
+ I40E_PTT_UNUSED_ENTRY(246),
+ I40E_PTT_UNUSED_ENTRY(247),
+ I40E_PTT_UNUSED_ENTRY(248),
+ I40E_PTT_UNUSED_ENTRY(249),
+
+ I40E_PTT_UNUSED_ENTRY(250),
+ I40E_PTT_UNUSED_ENTRY(251),
+ I40E_PTT_UNUSED_ENTRY(252),
+ I40E_PTT_UNUSED_ENTRY(253),
+ I40E_PTT_UNUSED_ENTRY(254),
+ I40E_PTT_UNUSED_ENTRY(255)
+};
+
+
/**
* i40e_init_shared_code - Initialize the shared code
* @hw: pointer to hardware structure
@@ -1409,9 +1775,9 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff,
cap = (struct i40e_aqc_list_capabilities_element_resp *) buff;
if (list_type_opc == i40e_aqc_opc_list_dev_capabilities)
- p = (struct i40e_hw_capabilities *)&hw->dev_caps;
+ p = &hw->dev_caps;
else if (list_type_opc == i40e_aqc_opc_list_func_capabilities)
- p = (struct i40e_hw_capabilities *)&hw->func_caps;
+ p = &hw->func_caps;
else
return;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c
index 50730141bb7b..036570d76176 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c
@@ -332,6 +332,7 @@ i40e_status i40e_lldp_to_dcb_config(u8 *lldpmib,
u16 type;
u16 length;
u16 typelength;
+ u16 offset = 0;
if (!lldpmib || !dcbcfg)
return I40E_ERR_PARAM;
@@ -339,15 +340,17 @@ i40e_status i40e_lldp_to_dcb_config(u8 *lldpmib,
/* set to the start of LLDPDU */
lldpmib += ETH_HLEN;
tlv = (struct i40e_lldp_org_tlv *)lldpmib;
- while (tlv) {
+ while (1) {
typelength = ntohs(tlv->typelength);
type = (u16)((typelength & I40E_LLDP_TLV_TYPE_MASK) >>
I40E_LLDP_TLV_TYPE_SHIFT);
length = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >>
I40E_LLDP_TLV_LEN_SHIFT);
+ offset += sizeof(typelength) + length;
- if (type == I40E_TLV_TYPE_END)
- break;/* END TLV break out */
+ /* END TLV or beyond LLDPDU size */
+ if ((type == I40E_TLV_TYPE_END) || (offset > I40E_LLDPDU_SIZE))
+ break;
switch (type) {
case I40E_TLV_TYPE_ORG:
diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
index da22c3fa2c00..3c37386fd138 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
@@ -1011,10 +1011,12 @@ static void i40e_dbg_dump_veb_all(struct i40e_pf *pf)
**/
static void i40e_dbg_cmd_fd_ctrl(struct i40e_pf *pf, u64 flag, bool enable)
{
- if (enable)
+ if (enable) {
pf->flags |= flag;
- else
+ } else {
pf->flags &= ~flag;
+ pf->auto_disable_flags |= flag;
+ }
dev_info(&pf->pdev->dev, "requesting a pf reset\n");
i40e_do_reset_safe(pf, (1 << __I40E_PF_RESET_REQUESTED));
}
@@ -1467,19 +1469,19 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
pf->msg_enable);
}
} else if (strncmp(cmd_buf, "pfr", 3) == 0) {
- dev_info(&pf->pdev->dev, "forcing PFR\n");
+ dev_info(&pf->pdev->dev, "debugfs: forcing PFR\n");
i40e_do_reset_safe(pf, (1 << __I40E_PF_RESET_REQUESTED));
} else if (strncmp(cmd_buf, "corer", 5) == 0) {
- dev_info(&pf->pdev->dev, "forcing CoreR\n");
+ dev_info(&pf->pdev->dev, "debugfs: forcing CoreR\n");
i40e_do_reset_safe(pf, (1 << __I40E_CORE_RESET_REQUESTED));
} else if (strncmp(cmd_buf, "globr", 5) == 0) {
- dev_info(&pf->pdev->dev, "forcing GlobR\n");
+ dev_info(&pf->pdev->dev, "debugfs: forcing GlobR\n");
i40e_do_reset_safe(pf, (1 << __I40E_GLOBAL_RESET_REQUESTED));
} else if (strncmp(cmd_buf, "empr", 4) == 0) {
- dev_info(&pf->pdev->dev, "forcing EMPR\n");
+ dev_info(&pf->pdev->dev, "debugfs: forcing EMPR\n");
i40e_do_reset_safe(pf, (1 << __I40E_EMP_RESET_REQUESTED));
} else if (strncmp(cmd_buf, "read", 4) == 0) {
@@ -1663,28 +1665,36 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
desc = NULL;
} else if ((strncmp(cmd_buf, "add fd_filter", 13) == 0) ||
(strncmp(cmd_buf, "rem fd_filter", 13) == 0)) {
- struct i40e_fdir_data fd_data;
+ struct i40e_fdir_filter fd_data;
u16 packet_len, i, j = 0;
char *asc_packet;
+ u8 *raw_packet;
bool add = false;
int ret;
- asc_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_LOOKUP,
+ if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
+ goto command_write_done;
+
+ if (strncmp(cmd_buf, "add", 3) == 0)
+ add = true;
+
+ if (add && (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED))
+ goto command_write_done;
+
+ asc_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE,
GFP_KERNEL);
if (!asc_packet)
goto command_write_done;
- fd_data.raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_LOOKUP,
- GFP_KERNEL);
+ raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE,
+ GFP_KERNEL);
- if (!fd_data.raw_packet) {
+ if (!raw_packet) {
kfree(asc_packet);
asc_packet = NULL;
goto command_write_done;
}
- if (strncmp(cmd_buf, "add", 3) == 0)
- add = true;
cnt = sscanf(&cmd_buf[13],
"%hx %2hhx %2hhx %hx %2hhx %2hhx %hx %x %hd %511s",
&fd_data.q_index,
@@ -1698,36 +1708,36 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
cnt);
kfree(asc_packet);
asc_packet = NULL;
- kfree(fd_data.raw_packet);
+ kfree(raw_packet);
goto command_write_done;
}
/* fix packet length if user entered 0 */
if (packet_len == 0)
- packet_len = I40E_FDIR_MAX_RAW_PACKET_LOOKUP;
+ packet_len = I40E_FDIR_MAX_RAW_PACKET_SIZE;
/* make sure to check the max as well */
packet_len = min_t(u16,
- packet_len, I40E_FDIR_MAX_RAW_PACKET_LOOKUP);
+ packet_len, I40E_FDIR_MAX_RAW_PACKET_SIZE);
for (i = 0; i < packet_len; i++) {
sscanf(&asc_packet[j], "%2hhx ",
- &fd_data.raw_packet[i]);
+ &raw_packet[i]);
j += 3;
}
dev_info(&pf->pdev->dev, "FD raw packet dump\n");
print_hex_dump(KERN_INFO, "FD raw packet: ",
DUMP_PREFIX_OFFSET, 16, 1,
- fd_data.raw_packet, packet_len, true);
- ret = i40e_program_fdir_filter(&fd_data, pf, add);
+ raw_packet, packet_len, true);
+ ret = i40e_program_fdir_filter(&fd_data, raw_packet, pf, add);
if (!ret) {
dev_info(&pf->pdev->dev, "Filter command send Status : Success\n");
} else {
dev_info(&pf->pdev->dev,
"Filter command send failed %d\n", ret);
}
- kfree(fd_data.raw_packet);
- fd_data.raw_packet = NULL;
+ kfree(raw_packet);
+ raw_packet = NULL;
kfree(asc_packet);
asc_packet = NULL;
} else if (strncmp(cmd_buf, "fd-atr off", 10) == 0) {
@@ -2077,9 +2087,13 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,
if (!vsi) {
dev_info(&pf->pdev->dev,
"tx_timeout: VSI %d not found\n", vsi_seid);
- goto netdev_ops_write_done;
- }
- if (rtnl_trylock()) {
+ } else if (!vsi->netdev) {
+ dev_info(&pf->pdev->dev, "tx_timeout: no netdev for VSI %d\n",
+ vsi_seid);
+ } else if (test_bit(__I40E_DOWN, &vsi->state)) {
+ dev_info(&pf->pdev->dev, "tx_timeout: VSI %d not UP\n",
+ vsi_seid);
+ } else if (rtnl_trylock()) {
vsi->netdev->netdev_ops->ndo_tx_timeout(vsi->netdev);
rtnl_unlock();
dev_info(&pf->pdev->dev, "tx_timeout called\n");
@@ -2098,9 +2112,10 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,
if (!vsi) {
dev_info(&pf->pdev->dev,
"change_mtu: VSI %d not found\n", vsi_seid);
- goto netdev_ops_write_done;
- }
- if (rtnl_trylock()) {
+ } else if (!vsi->netdev) {
+ dev_info(&pf->pdev->dev, "change_mtu: no netdev for VSI %d\n",
+ vsi_seid);
+ } else if (rtnl_trylock()) {
vsi->netdev->netdev_ops->ndo_change_mtu(vsi->netdev,
mtu);
rtnl_unlock();
@@ -2119,9 +2134,10 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,
if (!vsi) {
dev_info(&pf->pdev->dev,
"set_rx_mode: VSI %d not found\n", vsi_seid);
- goto netdev_ops_write_done;
- }
- if (rtnl_trylock()) {
+ } else if (!vsi->netdev) {
+ dev_info(&pf->pdev->dev, "set_rx_mode: no netdev for VSI %d\n",
+ vsi_seid);
+ } else if (rtnl_trylock()) {
vsi->netdev->netdev_ops->ndo_set_rx_mode(vsi->netdev);
rtnl_unlock();
dev_info(&pf->pdev->dev, "set_rx_mode called\n");
@@ -2139,11 +2155,14 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,
if (!vsi) {
dev_info(&pf->pdev->dev, "napi: VSI %d not found\n",
vsi_seid);
- goto netdev_ops_write_done;
+ } else if (!vsi->netdev) {
+ dev_info(&pf->pdev->dev, "napi: no netdev for VSI %d\n",
+ vsi_seid);
+ } else {
+ for (i = 0; i < vsi->num_q_vectors; i++)
+ napi_schedule(&vsi->q_vectors[i]->napi);
+ dev_info(&pf->pdev->dev, "napi called\n");
}
- for (i = 0; i < vsi->num_q_vectors; i++)
- napi_schedule(&vsi->q_vectors[i]->napi);
- dev_info(&pf->pdev->dev, "napi called\n");
} else {
dev_info(&pf->pdev->dev, "unknown command '%s'\n",
i40e_dbg_netdev_ops_buf);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index b1d7d8c5cb9b..03d99cbc5c25 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -62,6 +62,9 @@ static const struct i40e_stats i40e_gstrings_net_stats[] = {
I40E_NETDEV_STAT(rx_crc_errors),
};
+static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
+ struct ethtool_rxnfc *cmd);
+
/* These PF_STATs might look like duplicates of some NETDEV_STATs,
* but they are separate. This device supports Virtualization, and
* as such might have several netdevs supporting VMDq and FCoE going
@@ -84,6 +87,7 @@ static struct i40e_stats i40e_gstrings_stats[] = {
I40E_PF_STAT("illegal_bytes", stats.illegal_bytes),
I40E_PF_STAT("mac_local_faults", stats.mac_local_faults),
I40E_PF_STAT("mac_remote_faults", stats.mac_remote_faults),
+ I40E_PF_STAT("tx_timeout", tx_timeout_count),
I40E_PF_STAT("rx_length_errors", stats.rx_length_errors),
I40E_PF_STAT("link_xon_rx", stats.link_xon_rx),
I40E_PF_STAT("link_xoff_rx", stats.link_xoff_rx),
@@ -110,6 +114,11 @@ static struct i40e_stats i40e_gstrings_stats[] = {
I40E_PF_STAT("VF_admin_queue_requests", vf_aq_requests),
I40E_PF_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
I40E_PF_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
+ /* LPI stats */
+ I40E_PF_STAT("tx_lpi_status", stats.tx_lpi_status),
+ I40E_PF_STAT("rx_lpi_status", stats.rx_lpi_status),
+ I40E_PF_STAT("tx_lpi_count", stats.tx_lpi_count),
+ I40E_PF_STAT("rx_lpi_count", stats.rx_lpi_count),
};
#define I40E_QUEUE_STATS_LEN(n) \
@@ -387,7 +396,7 @@ static int i40e_get_eeprom(struct net_device *netdev,
ret_val = i40e_aq_read_nvm(hw, 0x0,
eeprom->offset + (I40E_NVM_SECTOR_SIZE * i),
len,
- (u8 *)eeprom_buff + (I40E_NVM_SECTOR_SIZE * i),
+ eeprom_buff + (I40E_NVM_SECTOR_SIZE * i),
last, NULL);
if (ret_val) {
dev_info(&pf->pdev->dev,
@@ -399,7 +408,7 @@ static int i40e_get_eeprom(struct net_device *netdev,
release_nvm:
i40e_release_nvm(hw);
- memcpy(bytes, (u8 *)eeprom_buff, eeprom->len);
+ memcpy(bytes, eeprom_buff, eeprom->len);
free_buff:
kfree(eeprom_buff);
return ret_val;
@@ -649,18 +658,18 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
/* process Tx ring statistics */
do {
- start = u64_stats_fetch_begin_bh(&tx_ring->syncp);
+ start = u64_stats_fetch_begin_irq(&tx_ring->syncp);
data[i] = tx_ring->stats.packets;
data[i + 1] = tx_ring->stats.bytes;
- } while (u64_stats_fetch_retry_bh(&tx_ring->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&tx_ring->syncp, start));
/* Rx ring is the 2nd half of the queue pair */
rx_ring = &tx_ring[1];
do {
- start = u64_stats_fetch_begin_bh(&rx_ring->syncp);
+ start = u64_stats_fetch_begin_irq(&rx_ring->syncp);
data[i + 2] = rx_ring->stats.packets;
data[i + 3] = rx_ring->stats.bytes;
- } while (u64_stats_fetch_retry_bh(&rx_ring->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&rx_ring->syncp, start));
}
rcu_read_unlock();
if (vsi == pf->vsi[pf->lan_vsi]) {
@@ -1112,6 +1121,84 @@ static int i40e_get_rss_hash_opts(struct i40e_pf *pf, struct ethtool_rxnfc *cmd)
}
/**
+ * i40e_get_ethtool_fdir_all - Populates the rule count of a command
+ * @pf: Pointer to the physical function struct
+ * @cmd: The command to get or set Rx flow classification rules
+ * @rule_locs: Array of used rule locations
+ *
+ * This function populates both the total and actual rule count of
+ * the ethtool flow classification command
+ *
+ * Returns 0 on success or -EMSGSIZE if entry not found
+ **/
+static int i40e_get_ethtool_fdir_all(struct i40e_pf *pf,
+ struct ethtool_rxnfc *cmd,
+ u32 *rule_locs)
+{
+ struct i40e_fdir_filter *rule;
+ struct hlist_node *node2;
+ int cnt = 0;
+
+ /* report total rule count */
+ cmd->data = pf->hw.fdir_shared_filter_count +
+ pf->fdir_pf_filter_count;
+
+ hlist_for_each_entry_safe(rule, node2,
+ &pf->fdir_filter_list, fdir_node) {
+ if (cnt == cmd->rule_cnt)
+ return -EMSGSIZE;
+
+ rule_locs[cnt] = rule->fd_id;
+ cnt++;
+ }
+
+ cmd->rule_cnt = cnt;
+
+ return 0;
+}
+
+/**
+ * i40e_get_ethtool_fdir_entry - Look up a filter based on Rx flow
+ * @pf: Pointer to the physical function struct
+ * @cmd: The command to get or set Rx flow classification rules
+ *
+ * This function looks up a filter based on the Rx flow classification
+ * command and fills the flow spec info for it if found
+ *
+ * Returns 0 on success or -EINVAL if filter not found
+ **/
+static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
+ struct ethtool_rxnfc *cmd)
+{
+ struct ethtool_rx_flow_spec *fsp =
+ (struct ethtool_rx_flow_spec *)&cmd->fs;
+ struct i40e_fdir_filter *rule = NULL;
+ struct hlist_node *node2;
+
+ /* report total rule count */
+ cmd->data = pf->hw.fdir_shared_filter_count +
+ pf->fdir_pf_filter_count;
+
+ hlist_for_each_entry_safe(rule, node2,
+ &pf->fdir_filter_list, fdir_node) {
+ if (fsp->location <= rule->fd_id)
+ break;
+ }
+
+ if (!rule || fsp->location != rule->fd_id)
+ return -EINVAL;
+
+ fsp->flow_type = rule->flow_type;
+ fsp->h_u.tcp_ip4_spec.psrc = rule->src_port;
+ fsp->h_u.tcp_ip4_spec.pdst = rule->dst_port;
+ fsp->h_u.tcp_ip4_spec.ip4src = rule->src_ip[0];
+ fsp->h_u.tcp_ip4_spec.ip4dst = rule->dst_ip[0];
+ fsp->ring_cookie = rule->q_index;
+
+ return 0;
+}
+
+/**
* i40e_get_rxnfc - command to get RX flow classification rules
* @netdev: network interface device structure
* @cmd: ethtool rxnfc command
@@ -1135,15 +1222,15 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
ret = i40e_get_rss_hash_opts(pf, cmd);
break;
case ETHTOOL_GRXCLSRLCNT:
- cmd->rule_cnt = 10;
+ cmd->rule_cnt = pf->fdir_pf_active_filters;
ret = 0;
break;
case ETHTOOL_GRXCLSRULE:
- ret = 0;
+ ret = i40e_get_ethtool_fdir_entry(pf, cmd);
break;
case ETHTOOL_GRXCLSRLALL:
- cmd->data = 500;
- ret = 0;
+ ret = i40e_get_ethtool_fdir_all(pf, cmd, rule_locs);
+ break;
default:
break;
}
@@ -1274,289 +1361,182 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc)
return 0;
}
-#define IP_HEADER_OFFSET 14
-#define I40E_UDPIP_DUMMY_PACKET_LEN 42
/**
- * i40e_add_del_fdir_udpv4 - Add/Remove UDPv4 Flow Director filters for
- * a specific flow spec
- * @vsi: pointer to the targeted VSI
- * @fd_data: the flow director data required from the FDir descriptor
- * @ethtool_rx_flow_spec: the flow spec
- * @add: true adds a filter, false removes it
+ * i40e_match_fdir_input_set - Match a new filter against an existing one
+ * @rule: The filter already added
+ * @input: The new filter to comapre against
*
- * Returns 0 if the filters were successfully added or removed
+ * Returns true if the two input set match
**/
-static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
- struct i40e_fdir_data *fd_data,
- struct ethtool_rx_flow_spec *fsp, bool add)
+static bool i40e_match_fdir_input_set(struct i40e_fdir_filter *rule,
+ struct i40e_fdir_filter *input)
{
- struct i40e_pf *pf = vsi->back;
- struct udphdr *udp;
- struct iphdr *ip;
- bool err = false;
- int ret;
- int i;
- char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
- 0x45, 0, 0, 0x1c, 0, 0, 0x40, 0, 0x40, 0x11,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0};
-
- memcpy(fd_data->raw_packet, packet, I40E_UDPIP_DUMMY_PACKET_LEN);
-
- ip = (struct iphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET);
- udp = (struct udphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET
- + sizeof(struct iphdr));
-
- ip->saddr = fsp->h_u.tcp_ip4_spec.ip4src;
- ip->daddr = fsp->h_u.tcp_ip4_spec.ip4dst;
- udp->source = fsp->h_u.tcp_ip4_spec.psrc;
- udp->dest = fsp->h_u.tcp_ip4_spec.pdst;
-
- for (i = I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP;
- i <= I40E_FILTER_PCTYPE_NONF_IPV4_UDP; i++) {
- fd_data->pctype = i;
- ret = i40e_program_fdir_filter(fd_data, pf, add);
-
- if (ret) {
- dev_info(&pf->pdev->dev,
- "Filter command send failed for PCTYPE %d (ret = %d)\n",
- fd_data->pctype, ret);
- err = true;
- } else {
- dev_info(&pf->pdev->dev,
- "Filter OK for PCTYPE %d (ret = %d)\n",
- fd_data->pctype, ret);
- }
- }
-
- return err ? -EOPNOTSUPP : 0;
+ if ((rule->dst_ip[0] != input->dst_ip[0]) ||
+ (rule->src_ip[0] != input->src_ip[0]) ||
+ (rule->dst_port != input->dst_port) ||
+ (rule->src_port != input->src_port))
+ return false;
+ return true;
}
-#define I40E_TCPIP_DUMMY_PACKET_LEN 54
/**
- * i40e_add_del_fdir_tcpv4 - Add/Remove TCPv4 Flow Director filters for
- * a specific flow spec
- * @vsi: pointer to the targeted VSI
- * @fd_data: the flow director data required from the FDir descriptor
- * @ethtool_rx_flow_spec: the flow spec
- * @add: true adds a filter, false removes it
+ * i40e_update_ethtool_fdir_entry - Updates the fdir filter entry
+ * @vsi: Pointer to the targeted VSI
+ * @input: The filter to update or NULL to indicate deletion
+ * @sw_idx: Software index to the filter
+ * @cmd: The command to get or set Rx flow classification rules
*
- * Returns 0 if the filters were successfully added or removed
+ * This function updates (or deletes) a Flow Director entry from
+ * the hlist of the corresponding PF
+ *
+ * Returns 0 on success
**/
-static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
- struct i40e_fdir_data *fd_data,
- struct ethtool_rx_flow_spec *fsp, bool add)
+static int i40e_update_ethtool_fdir_entry(struct i40e_vsi *vsi,
+ struct i40e_fdir_filter *input,
+ u16 sw_idx,
+ struct ethtool_rxnfc *cmd)
{
+ struct i40e_fdir_filter *rule, *parent;
struct i40e_pf *pf = vsi->back;
- struct tcphdr *tcp;
- struct iphdr *ip;
- bool err = false;
- int ret;
- /* Dummy packet */
- char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
- 0x45, 0, 0, 0x28, 0, 0, 0x40, 0, 0x40, 0x6,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0x80, 0x11, 0x0, 0x72, 0, 0, 0, 0};
-
- memcpy(fd_data->raw_packet, packet, I40E_TCPIP_DUMMY_PACKET_LEN);
-
- ip = (struct iphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET);
- tcp = (struct tcphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET
- + sizeof(struct iphdr));
-
- ip->daddr = fsp->h_u.tcp_ip4_spec.ip4dst;
- tcp->dest = fsp->h_u.tcp_ip4_spec.pdst;
- ip->saddr = fsp->h_u.tcp_ip4_spec.ip4src;
- tcp->source = fsp->h_u.tcp_ip4_spec.psrc;
-
- if (add) {
- if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
- dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n");
- pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
- }
- }
+ struct hlist_node *node2;
+ int err = -EINVAL;
- fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN;
- ret = i40e_program_fdir_filter(fd_data, pf, add);
+ parent = NULL;
+ rule = NULL;
- if (ret) {
- dev_info(&pf->pdev->dev,
- "Filter command send failed for PCTYPE %d (ret = %d)\n",
- fd_data->pctype, ret);
- err = true;
- } else {
- dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n",
- fd_data->pctype, ret);
+ hlist_for_each_entry_safe(rule, node2,
+ &pf->fdir_filter_list, fdir_node) {
+ /* hash found, or no matching entry */
+ if (rule->fd_id >= sw_idx)
+ break;
+ parent = rule;
}
- fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
-
- ret = i40e_program_fdir_filter(fd_data, pf, add);
- if (ret) {
- dev_info(&pf->pdev->dev,
- "Filter command send failed for PCTYPE %d (ret = %d)\n",
- fd_data->pctype, ret);
- err = true;
- } else {
- dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n",
- fd_data->pctype, ret);
+ /* if there is an old rule occupying our place remove it */
+ if (rule && (rule->fd_id == sw_idx)) {
+ if (input && !i40e_match_fdir_input_set(rule, input))
+ err = i40e_add_del_fdir(vsi, rule, false);
+ else if (!input)
+ err = i40e_add_del_fdir(vsi, rule, false);
+ hlist_del(&rule->fdir_node);
+ kfree(rule);
+ pf->fdir_pf_active_filters--;
}
- return err ? -EOPNOTSUPP : 0;
-}
+ /* If no input this was a delete, err should be 0 if a rule was
+ * successfully found and removed from the list else -EINVAL
+ */
+ if (!input)
+ return err;
-/**
- * i40e_add_del_fdir_sctpv4 - Add/Remove SCTPv4 Flow Director filters for
- * a specific flow spec
- * @vsi: pointer to the targeted VSI
- * @fd_data: the flow director data required from the FDir descriptor
- * @ethtool_rx_flow_spec: the flow spec
- * @add: true adds a filter, false removes it
- *
- * Returns 0 if the filters were successfully added or removed
- **/
-static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi,
- struct i40e_fdir_data *fd_data,
- struct ethtool_rx_flow_spec *fsp, bool add)
-{
- return -EOPNOTSUPP;
+ /* initialize node and set software index */
+ INIT_HLIST_NODE(&input->fdir_node);
+
+ /* add filter to the list */
+ if (parent)
+ hlist_add_after(&parent->fdir_node, &input->fdir_node);
+ else
+ hlist_add_head(&input->fdir_node,
+ &pf->fdir_filter_list);
+
+ /* update counts */
+ pf->fdir_pf_active_filters++;
+
+ return 0;
}
-#define I40E_IP_DUMMY_PACKET_LEN 34
/**
- * i40e_add_del_fdir_ipv4 - Add/Remove IPv4 Flow Director filters for
- * a specific flow spec
- * @vsi: pointer to the targeted VSI
- * @fd_data: the flow director data required for the FDir descriptor
- * @fsp: the ethtool flow spec
- * @add: true adds a filter, false removes it
+ * i40e_del_fdir_entry - Deletes a Flow Director filter entry
+ * @vsi: Pointer to the targeted VSI
+ * @cmd: The command to get or set Rx flow classification rules
*
- * Returns 0 if the filters were successfully added or removed
- **/
-static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi,
- struct i40e_fdir_data *fd_data,
- struct ethtool_rx_flow_spec *fsp, bool add)
+ * The function removes a Flow Director filter entry from the
+ * hlist of the corresponding PF
+ *
+ * Returns 0 on success
+ */
+static int i40e_del_fdir_entry(struct i40e_vsi *vsi,
+ struct ethtool_rxnfc *cmd)
{
+ struct ethtool_rx_flow_spec *fsp =
+ (struct ethtool_rx_flow_spec *)&cmd->fs;
struct i40e_pf *pf = vsi->back;
- struct iphdr *ip;
- bool err = false;
- int ret;
- int i;
- char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
- 0x45, 0, 0, 0x14, 0, 0, 0x40, 0, 0x40, 0x10,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
- memcpy(fd_data->raw_packet, packet, I40E_IP_DUMMY_PACKET_LEN);
- ip = (struct iphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET);
+ int ret = 0;
- ip->saddr = fsp->h_u.usr_ip4_spec.ip4src;
- ip->daddr = fsp->h_u.usr_ip4_spec.ip4dst;
- ip->protocol = fsp->h_u.usr_ip4_spec.proto;
+ ret = i40e_update_ethtool_fdir_entry(vsi, NULL, fsp->location, cmd);
- for (i = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
- i <= I40E_FILTER_PCTYPE_FRAG_IPV4; i++) {
- fd_data->pctype = i;
- ret = i40e_program_fdir_filter(fd_data, pf, add);
-
- if (ret) {
- dev_info(&pf->pdev->dev,
- "Filter command send failed for PCTYPE %d (ret = %d)\n",
- fd_data->pctype, ret);
- err = true;
- } else {
- dev_info(&pf->pdev->dev,
- "Filter OK for PCTYPE %d (ret = %d)\n",
- fd_data->pctype, ret);
- }
- }
-
- return err ? -EOPNOTSUPP : 0;
+ i40e_fdir_check_and_reenable(pf);
+ return ret;
}
/**
- * i40e_add_del_fdir_ethtool - Add/Remove Flow Director filters for
- * a specific flow spec based on their protocol
+ * i40e_add_fdir_ethtool - Add/Remove Flow Director filters
* @vsi: pointer to the targeted VSI
* @cmd: command to get or set RX flow classification rules
- * @add: true adds a filter, false removes it
*
- * Returns 0 if the filters were successfully added or removed
+ * Add Flow Director filters for a specific flow spec based on their
+ * protocol. Returns 0 if the filters were successfully added.
**/
-static int i40e_add_del_fdir_ethtool(struct i40e_vsi *vsi,
- struct ethtool_rxnfc *cmd, bool add)
+static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
+ struct ethtool_rxnfc *cmd)
{
- struct i40e_fdir_data fd_data;
- int ret = -EINVAL;
+ struct ethtool_rx_flow_spec *fsp;
+ struct i40e_fdir_filter *input;
struct i40e_pf *pf;
- struct ethtool_rx_flow_spec *fsp =
- (struct ethtool_rx_flow_spec *)&cmd->fs;
+ int ret = -EINVAL;
if (!vsi)
return -EINVAL;
pf = vsi->back;
- if ((fsp->ring_cookie != RX_CLS_FLOW_DISC) &&
- (fsp->ring_cookie >= vsi->num_queue_pairs))
- return -EINVAL;
+ if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
+ return -EOPNOTSUPP;
- /* Populate the Flow Director that we have at the moment
- * and allocate the raw packet buffer for the calling functions
- */
- fd_data.raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_LOOKUP,
- GFP_KERNEL);
+ if (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)
+ return -ENOSPC;
- if (!fd_data.raw_packet) {
- dev_info(&pf->pdev->dev, "Could not allocate memory\n");
- return -ENOMEM;
+ fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
+
+ if (fsp->location >= (pf->hw.func_caps.fd_filters_best_effort +
+ pf->hw.func_caps.fd_filters_guaranteed)) {
+ return -EINVAL;
}
- fd_data.q_index = fsp->ring_cookie;
- fd_data.flex_off = 0;
- fd_data.pctype = 0;
- fd_data.dest_vsi = vsi->id;
- fd_data.dest_ctl = I40E_FILTER_PROGRAM_DESC_DEST_DIRECT_PACKET_QINDEX;
- fd_data.fd_status = I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID;
- fd_data.cnt_index = 0;
- fd_data.fd_id = 0;
+ if (fsp->ring_cookie >= vsi->num_queue_pairs)
+ return -EINVAL;
- switch (fsp->flow_type & ~FLOW_EXT) {
- case TCP_V4_FLOW:
- ret = i40e_add_del_fdir_tcpv4(vsi, &fd_data, fsp, add);
- break;
- case UDP_V4_FLOW:
- ret = i40e_add_del_fdir_udpv4(vsi, &fd_data, fsp, add);
- break;
- case SCTP_V4_FLOW:
- ret = i40e_add_del_fdir_sctpv4(vsi, &fd_data, fsp, add);
- break;
- case IPV4_FLOW:
- ret = i40e_add_del_fdir_ipv4(vsi, &fd_data, fsp, add);
- break;
- case IP_USER_FLOW:
- switch (fsp->h_u.usr_ip4_spec.proto) {
- case IPPROTO_TCP:
- ret = i40e_add_del_fdir_tcpv4(vsi, &fd_data, fsp, add);
- break;
- case IPPROTO_UDP:
- ret = i40e_add_del_fdir_udpv4(vsi, &fd_data, fsp, add);
- break;
- case IPPROTO_SCTP:
- ret = i40e_add_del_fdir_sctpv4(vsi, &fd_data, fsp, add);
- break;
- default:
- ret = i40e_add_del_fdir_ipv4(vsi, &fd_data, fsp, add);
- break;
- }
- break;
- default:
- dev_info(&pf->pdev->dev, "Could not specify spec type\n");
- ret = -EINVAL;
- }
+ input = kzalloc(sizeof(*input), GFP_KERNEL);
+
+ if (!input)
+ return -ENOMEM;
- kfree(fd_data.raw_packet);
- fd_data.raw_packet = NULL;
+ input->fd_id = fsp->location;
+
+ if (fsp->ring_cookie == RX_CLS_FLOW_DISC)
+ input->dest_ctl = I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET;
+ else
+ input->dest_ctl =
+ I40E_FILTER_PROGRAM_DESC_DEST_DIRECT_PACKET_QINDEX;
+
+ input->q_index = fsp->ring_cookie;
+ input->flex_off = 0;
+ input->pctype = 0;
+ input->dest_vsi = vsi->id;
+ input->fd_status = I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID;
+ input->cnt_index = 0;
+ input->flow_type = fsp->flow_type;
+ input->ip4_proto = fsp->h_u.usr_ip4_spec.proto;
+ input->src_port = fsp->h_u.tcp_ip4_spec.psrc;
+ input->dst_port = fsp->h_u.tcp_ip4_spec.pdst;
+ input->src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
+ input->dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
+
+ ret = i40e_add_del_fdir(vsi, input, true);
+ if (ret)
+ kfree(input);
+ else
+ i40e_update_ethtool_fdir_entry(vsi, input, fsp->location, NULL);
return ret;
}
@@ -1580,10 +1560,10 @@ static int i40e_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
ret = i40e_set_rss_hash_opt(pf, cmd);
break;
case ETHTOOL_SRXCLSRLINS:
- ret = i40e_add_del_fdir_ethtool(vsi, cmd, true);
+ ret = i40e_add_fdir_ethtool(vsi, cmd);
break;
case ETHTOOL_SRXCLSRLDEL:
- ret = i40e_add_del_fdir_ethtool(vsi, cmd, false);
+ ret = i40e_del_fdir_entry(vsi, cmd);
break;
default:
break;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index b901371ca361..861b722c2672 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -26,6 +26,7 @@
/* Local includes */
#include "i40e.h"
+#include "i40e_diag.h"
#ifdef CONFIG_I40E_VXLAN
#include <net/vxlan.h>
#endif
@@ -38,7 +39,7 @@ static const char i40e_driver_string[] =
#define DRV_VERSION_MAJOR 0
#define DRV_VERSION_MINOR 3
-#define DRV_VERSION_BUILD 30
+#define DRV_VERSION_BUILD 36
#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
__stringify(DRV_VERSION_MINOR) "." \
__stringify(DRV_VERSION_BUILD) DRV_KERN
@@ -305,6 +306,7 @@ static void i40e_tx_timeout(struct net_device *netdev)
break;
default:
netdev_err(netdev, "tx_timeout recovery unsuccessful\n");
+ set_bit(__I40E_DOWN, &vsi->state);
i40e_down(vsi);
break;
}
@@ -375,20 +377,20 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct(
continue;
do {
- start = u64_stats_fetch_begin_bh(&tx_ring->syncp);
+ start = u64_stats_fetch_begin_irq(&tx_ring->syncp);
packets = tx_ring->stats.packets;
bytes = tx_ring->stats.bytes;
- } while (u64_stats_fetch_retry_bh(&tx_ring->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&tx_ring->syncp, start));
stats->tx_packets += packets;
stats->tx_bytes += bytes;
rx_ring = &tx_ring[1];
do {
- start = u64_stats_fetch_begin_bh(&rx_ring->syncp);
+ start = u64_stats_fetch_begin_irq(&rx_ring->syncp);
packets = rx_ring->stats.packets;
bytes = rx_ring->stats.bytes;
- } while (u64_stats_fetch_retry_bh(&rx_ring->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&rx_ring->syncp, start));
stats->rx_packets += packets;
stats->rx_bytes += bytes;
@@ -739,6 +741,7 @@ void i40e_update_stats(struct i40e_vsi *vsi)
u32 rx_page, rx_buf;
u64 rx_p, rx_b;
u64 tx_p, tx_b;
+ u32 val;
int i;
u16 q;
@@ -769,10 +772,10 @@ void i40e_update_stats(struct i40e_vsi *vsi)
p = ACCESS_ONCE(vsi->tx_rings[q]);
do {
- start = u64_stats_fetch_begin_bh(&p->syncp);
+ start = u64_stats_fetch_begin_irq(&p->syncp);
packets = p->stats.packets;
bytes = p->stats.bytes;
- } while (u64_stats_fetch_retry_bh(&p->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&p->syncp, start));
tx_b += bytes;
tx_p += packets;
tx_restart += p->tx_stats.restart_queue;
@@ -781,10 +784,10 @@ void i40e_update_stats(struct i40e_vsi *vsi)
/* Rx queue is part of the same block as Tx queue */
p = &p[1];
do {
- start = u64_stats_fetch_begin_bh(&p->syncp);
+ start = u64_stats_fetch_begin_irq(&p->syncp);
packets = p->stats.packets;
bytes = p->stats.bytes;
- } while (u64_stats_fetch_retry_bh(&p->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&p->syncp, start));
rx_b += bytes;
rx_p += packets;
rx_buf += p->rx_stats.alloc_buff_failed;
@@ -971,6 +974,20 @@ void i40e_update_stats(struct i40e_vsi *vsi)
i40e_stat_update32(hw, I40E_GLPRT_RJC(hw->port),
pf->stat_offsets_loaded,
&osd->rx_jabber, &nsd->rx_jabber);
+
+ val = rd32(hw, I40E_PRTPM_EEE_STAT);
+ nsd->tx_lpi_status =
+ (val & I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_MASK) >>
+ I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_SHIFT;
+ nsd->rx_lpi_status =
+ (val & I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_MASK) >>
+ I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_SHIFT;
+ i40e_stat_update32(hw, I40E_PRTPM_TLPIC,
+ pf->stat_offsets_loaded,
+ &osd->tx_lpi_count, &nsd->tx_lpi_count);
+ i40e_stat_update32(hw, I40E_PRTPM_RLPIC,
+ pf->stat_offsets_loaded,
+ &osd->rx_lpi_count, &nsd->rx_lpi_count);
}
pf->stat_offsets_loaded = true;
@@ -1964,11 +1981,14 @@ static int i40e_vlan_rx_add_vid(struct net_device *netdev,
netdev_info(netdev, "adding %pM vid=%d\n", netdev->dev_addr, vid);
- /* If the network stack called us with vid = 0, we should
- * indicate to i40e_vsi_add_vlan() that we want to receive
- * any traffic (i.e. with any vlan tag, or untagged)
+ /* If the network stack called us with vid = 0 then
+ * it is asking to receive priority tagged packets with
+ * vlan id 0. Our HW receives them by default when configured
+ * to receive untagged packets so there is no need to add an
+ * extra filter for vlan 0 tagged packets.
*/
- ret = i40e_vsi_add_vlan(vsi, vid ? vid : I40E_VLAN_ANY);
+ if (vid)
+ ret = i40e_vsi_add_vlan(vsi, vid);
if (!ret && (vid < VLAN_N_VID))
set_bit(vid, vsi->active_vlans);
@@ -1981,7 +2001,7 @@ static int i40e_vlan_rx_add_vid(struct net_device *netdev,
* @netdev: network interface to be adjusted
* @vid: vlan id to be removed
*
- * net_device_ops implementation for adding vlan ids
+ * net_device_ops implementation for removing vlan ids
**/
static int i40e_vlan_rx_kill_vid(struct net_device *netdev,
__always_unused __be16 proto, u16 vid)
@@ -2177,6 +2197,11 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring)
tx_ctx.fd_ena = !!(vsi->back->flags & (I40E_FLAG_FD_SB_ENABLED |
I40E_FLAG_FD_ATR_ENABLED));
tx_ctx.timesync_ena = !!(vsi->back->flags & I40E_FLAG_PTP);
+ /* FDIR VSI tx ring can still use RS bit and writebacks */
+ if (vsi->type != I40E_VSI_FDIR)
+ tx_ctx.head_wb_ena = 1;
+ tx_ctx.head_wb_addr = ring->dma +
+ (ring->count * sizeof(struct i40e_tx_desc));
/* As part of VSI creation/update, FW allocates certain
* Tx arbitration queue sets for each TC enabled for
@@ -2420,6 +2445,28 @@ static void i40e_set_vsi_rx_mode(struct i40e_vsi *vsi)
}
/**
+ * i40e_fdir_filter_restore - Restore the Sideband Flow Director filters
+ * @vsi: Pointer to the targeted VSI
+ *
+ * This function replays the hlist on the hw where all the SB Flow Director
+ * filters were saved.
+ **/
+static void i40e_fdir_filter_restore(struct i40e_vsi *vsi)
+{
+ struct i40e_fdir_filter *filter;
+ struct i40e_pf *pf = vsi->back;
+ struct hlist_node *node;
+
+ if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
+ return;
+
+ hlist_for_each_entry_safe(filter, node,
+ &pf->fdir_filter_list, fdir_node) {
+ i40e_add_del_fdir(vsi, filter, true);
+ }
+}
+
+/**
* i40e_vsi_configure - Set up the VSI for action
* @vsi: the VSI being configured
**/
@@ -2557,7 +2604,7 @@ static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi)
/* FIRSTQ_INDX = 0, FIRSTQ_TYPE = 0 (rx) */
wr32(hw, I40E_PFINT_LNKLST0, 0);
- /* Associate the queue pair to the vector and enable the q int */
+ /* Associate the queue pair to the vector and enable the queue int */
val = I40E_QINT_RQCTL_CAUSE_ENA_MASK |
(I40E_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) |
(I40E_QUEUE_TYPE_TX << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT);
@@ -2831,12 +2878,14 @@ static irqreturn_t i40e_intr(int irq, void *data)
val = rd32(hw, I40E_GLGEN_RSTAT);
val = (val & I40E_GLGEN_RSTAT_RESET_TYPE_MASK)
>> I40E_GLGEN_RSTAT_RESET_TYPE_SHIFT;
- if (val == I40E_RESET_CORER)
+ if (val == I40E_RESET_CORER) {
pf->corer_count++;
- else if (val == I40E_RESET_GLOBR)
+ } else if (val == I40E_RESET_GLOBR) {
pf->globr_count++;
- else if (val == I40E_RESET_EMPR)
+ } else if (val == I40E_RESET_EMPR) {
pf->empr_count++;
+ set_bit(__I40E_EMP_RESET_REQUESTED, &pf->state);
+ }
}
if (icr0 & I40E_PFINT_ICR0_HMC_ERR_MASK) {
@@ -2866,8 +2915,7 @@ static irqreturn_t i40e_intr(int irq, void *data)
icr0_remaining);
if ((icr0_remaining & I40E_PFINT_ICR0_PE_CRITERR_MASK) ||
(icr0_remaining & I40E_PFINT_ICR0_PCI_EXCEPTION_MASK) ||
- (icr0_remaining & I40E_PFINT_ICR0_ECC_ERR_MASK) ||
- (icr0_remaining & I40E_PFINT_ICR0_MAL_DETECT_MASK)) {
+ (icr0_remaining & I40E_PFINT_ICR0_ECC_ERR_MASK)) {
dev_info(&pf->pdev->dev, "device will be reset\n");
set_bit(__I40E_PF_RESET_REQUESTED, &pf->state);
i40e_service_event_schedule(pf);
@@ -3107,13 +3155,13 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable)
pf_q = vsi->base_queue;
for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) {
- j = 1000;
- do {
- usleep_range(1000, 2000);
+ for (j = 0; j < 50; j++) {
tx_reg = rd32(hw, I40E_QTX_ENA(pf_q));
- } while (j-- && ((tx_reg >> I40E_QTX_ENA_QENA_REQ_SHIFT)
- ^ (tx_reg >> I40E_QTX_ENA_QENA_STAT_SHIFT)) & 1);
-
+ if (((tx_reg >> I40E_QTX_ENA_QENA_REQ_SHIFT) & 1) ==
+ ((tx_reg >> I40E_QTX_ENA_QENA_STAT_SHIFT) & 1))
+ break;
+ usleep_range(1000, 2000);
+ }
/* Skip if the queue is already in the requested state */
if (enable && (tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
continue;
@@ -3123,8 +3171,7 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable)
/* turn on/off the queue */
if (enable) {
wr32(hw, I40E_QTX_HEAD(pf_q), 0);
- tx_reg |= I40E_QTX_ENA_QENA_REQ_MASK |
- I40E_QTX_ENA_QENA_STAT_MASK;
+ tx_reg |= I40E_QTX_ENA_QENA_REQ_MASK;
} else {
tx_reg &= ~I40E_QTX_ENA_QENA_REQ_MASK;
}
@@ -3171,12 +3218,13 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable)
pf_q = vsi->base_queue;
for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) {
- j = 1000;
- do {
- usleep_range(1000, 2000);
+ for (j = 0; j < 50; j++) {
rx_reg = rd32(hw, I40E_QRX_ENA(pf_q));
- } while (j-- && ((rx_reg >> I40E_QRX_ENA_QENA_REQ_SHIFT)
- ^ (rx_reg >> I40E_QRX_ENA_QENA_STAT_SHIFT)) & 1);
+ if (((rx_reg >> I40E_QRX_ENA_QENA_REQ_SHIFT) & 1) ==
+ ((rx_reg >> I40E_QRX_ENA_QENA_STAT_SHIFT) & 1))
+ break;
+ usleep_range(1000, 2000);
+ }
if (enable) {
/* is STAT set ? */
@@ -3190,11 +3238,9 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable)
/* turn on/off the queue */
if (enable)
- rx_reg |= I40E_QRX_ENA_QENA_REQ_MASK |
- I40E_QRX_ENA_QENA_STAT_MASK;
+ rx_reg |= I40E_QRX_ENA_QENA_REQ_MASK;
else
- rx_reg &= ~(I40E_QRX_ENA_QENA_REQ_MASK |
- I40E_QRX_ENA_QENA_STAT_MASK);
+ rx_reg &= ~I40E_QRX_ENA_QENA_REQ_MASK;
wr32(hw, I40E_QRX_ENA(pf_q), rx_reg);
/* wait for the change to finish */
@@ -3732,8 +3778,8 @@ static int i40e_vsi_configure_bw_alloc(struct i40e_vsi *vsi, u8 enabled_tc,
NULL);
if (aq_ret) {
dev_info(&vsi->back->pdev->dev,
- "%s: AQ command Config VSI BW allocation per TC failed = %d\n",
- __func__, vsi->back->hw.aq.asq_last_status);
+ "AQ command Config VSI BW allocation per TC failed = %d\n",
+ vsi->back->hw.aq.asq_last_status);
return -EINVAL;
}
@@ -4062,6 +4108,10 @@ static int i40e_up_complete(struct i40e_vsi *vsi)
} else if (vsi->netdev) {
netdev_info(vsi->netdev, "NIC Link is Down\n");
}
+
+ /* replay FDIR SB filters */
+ if (vsi->type == I40E_VSI_FDIR)
+ i40e_fdir_filter_restore(vsi);
i40e_service_event_schedule(pf);
return 0;
@@ -4208,15 +4258,40 @@ static int i40e_open(struct net_device *netdev)
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
- char int_name[IFNAMSIZ];
int err;
- /* disallow open during test */
- if (test_bit(__I40E_TESTING, &pf->state))
+ /* disallow open during test or if eeprom is broken */
+ if (test_bit(__I40E_TESTING, &pf->state) ||
+ test_bit(__I40E_BAD_EEPROM, &pf->state))
return -EBUSY;
netif_carrier_off(netdev);
+ err = i40e_vsi_open(vsi);
+ if (err)
+ return err;
+
+#ifdef CONFIG_I40E_VXLAN
+ vxlan_get_rx_port(netdev);
+#endif
+
+ return 0;
+}
+
+/**
+ * i40e_vsi_open -
+ * @vsi: the VSI to open
+ *
+ * Finish initialization of the VSI.
+ *
+ * Returns 0 on success, negative value on failure
+ **/
+int i40e_vsi_open(struct i40e_vsi *vsi)
+{
+ struct i40e_pf *pf = vsi->back;
+ char int_name[IFNAMSIZ];
+ int err;
+
/* allocate descriptors */
err = i40e_vsi_setup_tx_resources(vsi);
if (err)
@@ -4229,18 +4304,22 @@ static int i40e_open(struct net_device *netdev)
if (err)
goto err_setup_rx;
+ if (!vsi->netdev) {
+ err = EINVAL;
+ goto err_setup_rx;
+ }
snprintf(int_name, sizeof(int_name) - 1, "%s-%s",
- dev_driver_string(&pf->pdev->dev), netdev->name);
+ dev_driver_string(&pf->pdev->dev), vsi->netdev->name);
err = i40e_vsi_request_irq(vsi, int_name);
if (err)
goto err_setup_rx;
/* Notify the stack of the actual queue counts. */
- err = netif_set_real_num_tx_queues(netdev, vsi->num_queue_pairs);
+ err = netif_set_real_num_tx_queues(vsi->netdev, vsi->num_queue_pairs);
if (err)
goto err_set_queues;
- err = netif_set_real_num_rx_queues(netdev, vsi->num_queue_pairs);
+ err = netif_set_real_num_rx_queues(vsi->netdev, vsi->num_queue_pairs);
if (err)
goto err_set_queues;
@@ -4248,10 +4327,6 @@ static int i40e_open(struct net_device *netdev)
if (err)
goto err_up_complete;
-#ifdef CONFIG_I40E_VXLAN
- vxlan_get_rx_port(netdev);
-#endif
-
return 0;
err_up_complete:
@@ -4269,6 +4344,26 @@ err_setup_tx:
}
/**
+ * i40e_fdir_filter_exit - Cleans up the Flow Director accounting
+ * @pf: Pointer to pf
+ *
+ * This function destroys the hlist where all the Flow Director
+ * filters were saved.
+ **/
+static void i40e_fdir_filter_exit(struct i40e_pf *pf)
+{
+ struct i40e_fdir_filter *filter;
+ struct hlist_node *node2;
+
+ hlist_for_each_entry_safe(filter, node2,
+ &pf->fdir_filter_list, fdir_node) {
+ hlist_del(&filter->fdir_node);
+ kfree(filter);
+ }
+ pf->fdir_pf_active_filters = 0;
+}
+
+/**
* i40e_close - Disables a network interface
* @netdev: network interface device structure
*
@@ -4321,7 +4416,7 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
* for the warning interrupt will deal with the shutdown
* and recovery of the switch setup.
*/
- dev_info(&pf->pdev->dev, "GlobalR requested\n");
+ dev_dbg(&pf->pdev->dev, "GlobalR requested\n");
val = rd32(&pf->hw, I40E_GLGEN_RTRIG);
val |= I40E_GLGEN_RTRIG_GLOBR_MASK;
wr32(&pf->hw, I40E_GLGEN_RTRIG, val);
@@ -4332,7 +4427,7 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
*
* Same as Global Reset, except does *not* include the MAC/PHY
*/
- dev_info(&pf->pdev->dev, "CoreR requested\n");
+ dev_dbg(&pf->pdev->dev, "CoreR requested\n");
val = rd32(&pf->hw, I40E_GLGEN_RTRIG);
val |= I40E_GLGEN_RTRIG_CORER_MASK;
wr32(&pf->hw, I40E_GLGEN_RTRIG, val);
@@ -4366,7 +4461,7 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
* the switch, since we need to do all the recovery as
* for the Core Reset.
*/
- dev_info(&pf->pdev->dev, "PFR requested\n");
+ dev_dbg(&pf->pdev->dev, "PFR requested\n");
i40e_handle_reset_warning(pf);
} else if (reset_flags & (1 << __I40E_REINIT_REQUESTED)) {
@@ -4415,18 +4510,18 @@ bool i40e_dcb_need_reconfig(struct i40e_pf *pf,
&old_cfg->etscfg.prioritytable,
sizeof(new_cfg->etscfg.prioritytable))) {
need_reconfig = true;
- dev_info(&pf->pdev->dev, "ETS UP2TC changed.\n");
+ dev_dbg(&pf->pdev->dev, "ETS UP2TC changed.\n");
}
if (memcmp(&new_cfg->etscfg.tcbwtable,
&old_cfg->etscfg.tcbwtable,
sizeof(new_cfg->etscfg.tcbwtable)))
- dev_info(&pf->pdev->dev, "ETS TC BW Table changed.\n");
+ dev_dbg(&pf->pdev->dev, "ETS TC BW Table changed.\n");
if (memcmp(&new_cfg->etscfg.tsatable,
&old_cfg->etscfg.tsatable,
sizeof(new_cfg->etscfg.tsatable)))
- dev_info(&pf->pdev->dev, "ETS TSA Table changed.\n");
+ dev_dbg(&pf->pdev->dev, "ETS TSA Table changed.\n");
}
/* Check if PFC configuration has changed */
@@ -4434,7 +4529,7 @@ bool i40e_dcb_need_reconfig(struct i40e_pf *pf,
&old_cfg->pfc,
sizeof(new_cfg->pfc))) {
need_reconfig = true;
- dev_info(&pf->pdev->dev, "PFC config change detected.\n");
+ dev_dbg(&pf->pdev->dev, "PFC config change detected.\n");
}
/* Check if APP Table has changed */
@@ -4442,7 +4537,7 @@ bool i40e_dcb_need_reconfig(struct i40e_pf *pf,
&old_cfg->app,
sizeof(new_cfg->app))) {
need_reconfig = true;
- dev_info(&pf->pdev->dev, "APP Table change detected.\n");
+ dev_dbg(&pf->pdev->dev, "APP Table change detected.\n");
}
return need_reconfig;
@@ -4492,7 +4587,7 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf,
/* No change detected in DCBX configs */
if (!memcmp(&tmp_dcbx_cfg, dcbx_cfg, sizeof(tmp_dcbx_cfg))) {
- dev_info(&pf->pdev->dev, "No change detected in DCBX configuration.\n");
+ dev_dbg(&pf->pdev->dev, "No change detected in DCBX configuration.\n");
goto exit;
}
@@ -4550,8 +4645,8 @@ static void i40e_handle_lan_overflow_event(struct i40e_pf *pf,
struct i40e_vf *vf;
u16 vf_id;
- dev_info(&pf->pdev->dev, "%s: Rx Queue Number = %d QTX_CTL=0x%08x\n",
- __func__, queue, qtx_ctl);
+ dev_dbg(&pf->pdev->dev, "overflow Rx Queue Number = %d QTX_CTL=0x%08x\n",
+ queue, qtx_ctl);
/* Queue belongs to VF, find the VF and issue VF reset */
if (((qtx_ctl & I40E_QTX_CTL_PFVF_Q_MASK)
@@ -4581,6 +4676,54 @@ static void i40e_service_event_complete(struct i40e_pf *pf)
}
/**
+ * i40e_get_current_fd_count - Get the count of FD filters programmed in the HW
+ * @pf: board private structure
+ **/
+int i40e_get_current_fd_count(struct i40e_pf *pf)
+{
+ int val, fcnt_prog;
+ val = rd32(&pf->hw, I40E_PFQF_FDSTAT);
+ fcnt_prog = (val & I40E_PFQF_FDSTAT_GUARANT_CNT_MASK) +
+ ((val & I40E_PFQF_FDSTAT_BEST_CNT_MASK) >>
+ I40E_PFQF_FDSTAT_BEST_CNT_SHIFT);
+ return fcnt_prog;
+}
+
+/**
+ * i40e_fdir_check_and_reenable - Function to reenabe FD ATR or SB if disabled
+ * @pf: board private structure
+ **/
+void i40e_fdir_check_and_reenable(struct i40e_pf *pf)
+{
+ u32 fcnt_prog, fcnt_avail;
+
+ /* Check if, FD SB or ATR was auto disabled and if there is enough room
+ * to re-enable
+ */
+ if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
+ (pf->flags & I40E_FLAG_FD_SB_ENABLED))
+ return;
+ fcnt_prog = i40e_get_current_fd_count(pf);
+ fcnt_avail = pf->hw.fdir_shared_filter_count +
+ pf->fdir_pf_filter_count;
+ if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) {
+ if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
+ (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)) {
+ pf->auto_disable_flags &= ~I40E_FLAG_FD_SB_ENABLED;
+ dev_info(&pf->pdev->dev, "FD Sideband/ntuple is being enabled since we have space in the table now\n");
+ }
+ }
+ /* Wait for some more space to be available to turn on ATR */
+ if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM * 2)) {
+ if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
+ (pf->auto_disable_flags & I40E_FLAG_FD_ATR_ENABLED)) {
+ pf->auto_disable_flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+ dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table now\n");
+ }
+ }
+}
+
+/**
* i40e_fdir_reinit_subtask - Worker thread to reinit FDIR filter table
* @pf: board private structure
**/
@@ -4589,11 +4732,14 @@ static void i40e_fdir_reinit_subtask(struct i40e_pf *pf)
if (!(pf->flags & I40E_FLAG_FDIR_REQUIRES_REINIT))
return;
- pf->flags &= ~I40E_FLAG_FDIR_REQUIRES_REINIT;
-
/* if interface is down do nothing */
if (test_bit(__I40E_DOWN, &pf->state))
return;
+ i40e_fdir_check_and_reenable(pf);
+
+ if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
+ (pf->flags & I40E_FLAG_FD_SB_ENABLED))
+ pf->flags &= ~I40E_FLAG_FDIR_REQUIRES_REINIT;
}
/**
@@ -4903,7 +5049,7 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
event.msg_size);
break;
case i40e_aqc_opc_lldp_update_mib:
- dev_info(&pf->pdev->dev, "ARQ: Update LLDP MIB event received\n");
+ dev_dbg(&pf->pdev->dev, "ARQ: Update LLDP MIB event received\n");
#ifdef CONFIG_I40E_DCB
rtnl_lock();
ret = i40e_handle_lldp_event(pf, &event);
@@ -4911,7 +5057,7 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
#endif /* CONFIG_I40E_DCB */
break;
case i40e_aqc_opc_event_lan_overflow:
- dev_info(&pf->pdev->dev, "ARQ LAN queue overflow event received\n");
+ dev_dbg(&pf->pdev->dev, "ARQ LAN queue overflow event received\n");
i40e_handle_lan_overflow_event(pf, &event);
break;
case i40e_aqc_opc_send_msg_to_peer:
@@ -4936,6 +5082,31 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
}
/**
+ * i40e_verify_eeprom - make sure eeprom is good to use
+ * @pf: board private structure
+ **/
+static void i40e_verify_eeprom(struct i40e_pf *pf)
+{
+ int err;
+
+ err = i40e_diag_eeprom_test(&pf->hw);
+ if (err) {
+ /* retry in case of garbage read */
+ err = i40e_diag_eeprom_test(&pf->hw);
+ if (err) {
+ dev_info(&pf->pdev->dev, "eeprom check failed (%d), Tx/Rx traffic disabled\n",
+ err);
+ set_bit(__I40E_BAD_EEPROM, &pf->state);
+ }
+ }
+
+ if (!err && test_bit(__I40E_BAD_EEPROM, &pf->state)) {
+ dev_info(&pf->pdev->dev, "eeprom check passed, Tx/Rx traffic enabled\n");
+ clear_bit(__I40E_BAD_EEPROM, &pf->state);
+ }
+}
+
+/**
* i40e_reconstitute_veb - rebuild the VEB and anything connected to it
* @veb: pointer to the VEB instance
*
@@ -5053,6 +5224,12 @@ static int i40e_get_capabilities(struct i40e_pf *pf)
/* increment MSI-X count because current FW skips one */
pf->hw.func_caps.num_msix_vectors++;
+ if (((pf->hw.aq.fw_maj_ver == 2) && (pf->hw.aq.fw_min_ver < 22)) ||
+ (pf->hw.aq.fw_maj_ver < 2)) {
+ pf->hw.func_caps.num_msix_vectors++;
+ pf->hw.func_caps.num_msix_vectors_vf++;
+ }
+
if (pf->hw.debug_mask & I40E_DEBUG_USER)
dev_info(&pf->pdev->dev,
"pf=%d, num_vfs=%d, msix_pf=%d, msix_vf=%d, fd_g=%d, fd_b=%d, pf_max_q=%d num_vsi=%d\n",
@@ -5132,9 +5309,9 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf)
err = i40e_up_complete(vsi);
if (err)
goto err_up_complete;
+ clear_bit(__I40E_NEEDS_RESTART, &vsi->state);
}
- clear_bit(__I40E_NEEDS_RESTART, &vsi->state);
return;
err_up_complete:
@@ -5157,6 +5334,7 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
{
int i;
+ i40e_fdir_filter_exit(pf);
for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
i40e_vsi_release(pf->vsi[i]);
@@ -5181,7 +5359,7 @@ static int i40e_prep_for_reset(struct i40e_pf *pf)
if (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state))
return 0;
- dev_info(&pf->pdev->dev, "Tearing down internal switch for reset\n");
+ dev_dbg(&pf->pdev->dev, "Tearing down internal switch for reset\n");
if (i40e_check_asq_alive(hw))
i40e_vc_notify_reset(pf);
@@ -5228,7 +5406,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
if (test_bit(__I40E_DOWN, &pf->state))
goto end_core_reset;
- dev_info(&pf->pdev->dev, "Rebuilding internal switch\n");
+ dev_dbg(&pf->pdev->dev, "Rebuilding internal switch\n");
/* rebuild the basics for the AdminQ, HMC, and initial HW switch */
ret = i40e_init_adminq(&pf->hw);
@@ -5237,6 +5415,12 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
goto end_core_reset;
}
+ /* re-verify the eeprom if we just had an EMP reset */
+ if (test_bit(__I40E_EMP_RESET_REQUESTED, &pf->state)) {
+ clear_bit(__I40E_EMP_RESET_REQUESTED, &pf->state);
+ i40e_verify_eeprom(pf);
+ }
+
ret = i40e_get_capabilities(pf);
if (ret) {
dev_info(&pf->pdev->dev, "i40e_get_capabilities failed, %d\n",
@@ -5278,7 +5462,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
* try to recover minimal use by getting the basic PF VSI working.
*/
if (pf->vsi[pf->lan_vsi]->uplink_seid != pf->mac_seid) {
- dev_info(&pf->pdev->dev, "attempting to rebuild switch\n");
+ dev_dbg(&pf->pdev->dev, "attempting to rebuild switch\n");
/* find the one VEB connected to the MAC, and find orphans */
for (v = 0; v < I40E_MAX_VEB; v++) {
if (!pf->veb[v])
@@ -5331,6 +5515,11 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
/* restart the VSIs that were rebuilt and running before the reset */
i40e_pf_unquiesce_all_vsi(pf);
+ if (pf->num_alloc_vfs) {
+ for (v = 0; v < pf->num_alloc_vfs; v++)
+ i40e_reset_vf(&pf->vf[v], true);
+ }
+
/* tell the firmware that we're starting */
dv.major_version = DRV_VERSION_MAJOR;
dv.minor_version = DRV_VERSION_MINOR;
@@ -5338,7 +5527,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
dv.subbuild_version = 0;
i40e_aq_send_driver_version(&pf->hw, &dv, NULL);
- dev_info(&pf->pdev->dev, "PF reset done\n");
+ dev_info(&pf->pdev->dev, "reset complete\n");
end_core_reset:
clear_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state);
@@ -5387,7 +5576,7 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf)
u8 queue = (reg & I40E_GL_MDET_TX_QUEUE_MASK)
>> I40E_GL_MDET_TX_QUEUE_SHIFT;
dev_info(&pf->pdev->dev,
- "Malicious Driver Detection TX event 0x%02x on q %d of function 0x%02x\n",
+ "Malicious Driver Detection event 0x%02x on TX queue %d of function 0x%02x\n",
event, queue, func);
wr32(hw, I40E_GL_MDET_TX, 0xffffffff);
mdd_detected = true;
@@ -5401,7 +5590,7 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf)
u8 queue = (reg & I40E_GL_MDET_RX_QUEUE_MASK)
>> I40E_GL_MDET_RX_QUEUE_SHIFT;
dev_info(&pf->pdev->dev,
- "Malicious Driver Detection RX event 0x%02x on q %d of function 0x%02x\n",
+ "Malicious Driver Detection event 0x%02x on RX queue %d of function 0x%02x\n",
event, queue, func);
wr32(hw, I40E_GL_MDET_RX, 0xffffffff);
mdd_detected = true;
@@ -5850,37 +6039,16 @@ err_out:
**/
static int i40e_reserve_msix_vectors(struct i40e_pf *pf, int vectors)
{
- int err = 0;
-
- pf->num_msix_entries = 0;
- while (vectors >= I40E_MIN_MSIX) {
- err = pci_enable_msix(pf->pdev, pf->msix_entries, vectors);
- if (err == 0) {
- /* good to go */
- pf->num_msix_entries = vectors;
- break;
- } else if (err < 0) {
- /* total failure */
- dev_info(&pf->pdev->dev,
- "MSI-X vector reservation failed: %d\n", err);
- vectors = 0;
- break;
- } else {
- /* err > 0 is the hint for retry */
- dev_info(&pf->pdev->dev,
- "MSI-X vectors wanted %d, retrying with %d\n",
- vectors, err);
- vectors = err;
- }
- }
-
- if (vectors > 0 && vectors < I40E_MIN_MSIX) {
+ vectors = pci_enable_msix_range(pf->pdev, pf->msix_entries,
+ I40E_MIN_MSIX, vectors);
+ if (vectors < 0) {
dev_info(&pf->pdev->dev,
- "Couldn't get enough vectors, only %d available\n",
- vectors);
+ "MSI-X vector reservation failed: %d\n", vectors);
vectors = 0;
}
+ pf->num_msix_entries = vectors;
+
return vectors;
}
@@ -5942,7 +6110,7 @@ static int i40e_init_msix(struct i40e_pf *pf)
} else if (vec == I40E_MIN_MSIX) {
/* Adjust for minimal MSIX use */
- dev_info(&pf->pdev->dev, "Features disabled, not enough MSIX vectors\n");
+ dev_info(&pf->pdev->dev, "Features disabled, not enough MSI-X vectors\n");
pf->flags &= ~I40E_FLAG_VMDQ_ENABLED;
pf->num_vmdq_vsis = 0;
pf->num_vmdq_qps = 0;
@@ -5978,13 +6146,13 @@ static int i40e_init_msix(struct i40e_pf *pf)
}
/**
- * i40e_alloc_q_vector - Allocate memory for a single interrupt vector
+ * i40e_vsi_alloc_q_vector - Allocate memory for a single interrupt vector
* @vsi: the VSI being configured
* @v_idx: index of the vector in the vsi struct
*
* We allocate one q_vector. If allocation fails we return -ENOMEM.
**/
-static int i40e_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
+static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
{
struct i40e_q_vector *q_vector;
@@ -6010,13 +6178,13 @@ static int i40e_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
}
/**
- * i40e_alloc_q_vectors - Allocate memory for interrupt vectors
+ * i40e_vsi_alloc_q_vectors - Allocate memory for interrupt vectors
* @vsi: the VSI being configured
*
* We allocate one q_vector per queue interrupt. If allocation fails we
* return -ENOMEM.
**/
-static int i40e_alloc_q_vectors(struct i40e_vsi *vsi)
+static int i40e_vsi_alloc_q_vectors(struct i40e_vsi *vsi)
{
struct i40e_pf *pf = vsi->back;
int v_idx, num_q_vectors;
@@ -6031,7 +6199,7 @@ static int i40e_alloc_q_vectors(struct i40e_vsi *vsi)
return -EINVAL;
for (v_idx = 0; v_idx < num_q_vectors; v_idx++) {
- err = i40e_alloc_q_vector(vsi, v_idx);
+ err = i40e_vsi_alloc_q_vector(vsi, v_idx);
if (err)
goto err_out;
}
@@ -6071,7 +6239,7 @@ static void i40e_init_interrupt_scheme(struct i40e_pf *pf)
if (!(pf->flags & I40E_FLAG_MSIX_ENABLED) &&
(pf->flags & I40E_FLAG_MSI_ENABLED)) {
- dev_info(&pf->pdev->dev, "MSIX not available, trying MSI\n");
+ dev_info(&pf->pdev->dev, "MSI-X not available, trying MSI\n");
err = pci_enable_msi(pf->pdev);
if (err) {
dev_info(&pf->pdev->dev, "MSI init failed - %d\n", err);
@@ -6080,7 +6248,7 @@ static void i40e_init_interrupt_scheme(struct i40e_pf *pf)
}
if (!(pf->flags & (I40E_FLAG_MSIX_ENABLED | I40E_FLAG_MSI_ENABLED)))
- dev_info(&pf->pdev->dev, "MSIX and MSI not available, falling back to Legacy IRQ\n");
+ dev_info(&pf->pdev->dev, "MSI-X and MSI not available, falling back to Legacy IRQ\n");
/* track first vector for misc interrupts */
err = i40e_get_lump(pf, pf->irq_pile, 1, I40E_PILE_VALID_BIT-1);
@@ -6107,7 +6275,8 @@ static int i40e_setup_misc_vector(struct i40e_pf *pf)
i40e_intr, 0, pf->misc_int_name, pf);
if (err) {
dev_info(&pf->pdev->dev,
- "request_irq for msix_misc failed: %d\n", err);
+ "request_irq for %s failed: %d\n",
+ pf->misc_int_name, err);
return -EFAULT;
}
}
@@ -6258,15 +6427,11 @@ static int i40e_sw_init(struct i40e_pf *pf)
(pf->hw.func_caps.fd_filters_best_effort > 0)) {
pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE;
- dev_info(&pf->pdev->dev,
- "Flow Director ATR mode Enabled\n");
if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) {
pf->flags |= I40E_FLAG_FD_SB_ENABLED;
- dev_info(&pf->pdev->dev,
- "Flow Director Side Band mode Enabled\n");
} else {
dev_info(&pf->pdev->dev,
- "Flow Director Side Band mode Disabled in MFP mode\n");
+ "Flow Director Sideband mode Disabled in MFP mode\n");
}
pf->fdir_pf_filter_count =
pf->hw.func_caps.fd_filters_guaranteed;
@@ -6287,9 +6452,6 @@ static int i40e_sw_init(struct i40e_pf *pf)
pf->num_req_vfs = min_t(int,
pf->hw.func_caps.num_vfs,
I40E_MAX_VF_COUNT);
- dev_info(&pf->pdev->dev,
- "Number of VFs being requested for PF[%d] = %d\n",
- pf->hw.pf_id, pf->num_req_vfs);
}
#endif /* CONFIG_PCI_IOV */
pf->eeprom_version = 0xDEAD;
@@ -6326,6 +6488,39 @@ sw_init_done:
}
/**
+ * i40e_set_ntuple - set the ntuple feature flag and take action
+ * @pf: board private structure to initialize
+ * @features: the feature set that the stack is suggesting
+ *
+ * returns a bool to indicate if reset needs to happen
+ **/
+bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features)
+{
+ bool need_reset = false;
+
+ /* Check if Flow Director n-tuple support was enabled or disabled. If
+ * the state changed, we need to reset.
+ */
+ if (features & NETIF_F_NTUPLE) {
+ /* Enable filters and mark for reset */
+ if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
+ need_reset = true;
+ pf->flags |= I40E_FLAG_FD_SB_ENABLED;
+ } else {
+ /* turn off filters, mark for reset and clear SW filter list */
+ if (pf->flags & I40E_FLAG_FD_SB_ENABLED) {
+ need_reset = true;
+ i40e_fdir_filter_exit(pf);
+ }
+ pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
+ /* if ATR was disabled it can be re-enabled. */
+ if (!(pf->flags & I40E_FLAG_FD_ATR_ENABLED))
+ pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
+ }
+ return need_reset;
+}
+
+/**
* i40e_set_features - set the netdev feature flags
* @netdev: ptr to the netdev being adjusted
* @features: the feature set that the stack is suggesting
@@ -6335,12 +6530,19 @@ static int i40e_set_features(struct net_device *netdev,
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
+ struct i40e_pf *pf = vsi->back;
+ bool need_reset;
if (features & NETIF_F_HW_VLAN_CTAG_RX)
i40e_vlan_stripping_enable(vsi);
else
i40e_vlan_stripping_disable(vsi);
+ need_reset = i40e_set_ntuple(pf, features);
+
+ if (need_reset)
+ i40e_do_reset(pf, (1 << __I40E_PF_RESET_REQUESTED));
+
return 0;
}
@@ -6464,6 +6666,7 @@ static const struct net_device_ops i40e_netdev_ops = {
.ndo_set_vf_vlan = i40e_ndo_set_vf_port_vlan,
.ndo_set_vf_tx_rate = i40e_ndo_set_vf_bw,
.ndo_get_vf_config = i40e_ndo_get_vf_config,
+ .ndo_set_vf_link_state = i40e_ndo_set_vf_link_state,
#ifdef CONFIG_I40E_VXLAN
.ndo_add_vxlan_port = i40e_add_vxlan_port,
.ndo_del_vxlan_port = i40e_del_vxlan_port,
@@ -6495,10 +6698,9 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
np = netdev_priv(netdev);
np->vsi = vsi;
- netdev->hw_enc_features = NETIF_F_IP_CSUM |
+ netdev->hw_enc_features |= NETIF_F_IP_CSUM |
NETIF_F_GSO_UDP_TUNNEL |
- NETIF_F_TSO |
- NETIF_F_SG;
+ NETIF_F_TSO;
netdev->features = NETIF_F_SG |
NETIF_F_IP_CSUM |
@@ -6512,6 +6714,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
NETIF_F_TSO |
NETIF_F_TSO6 |
NETIF_F_RXCSUM |
+ NETIF_F_NTUPLE |
NETIF_F_RXHASH |
0;
@@ -6771,8 +6974,6 @@ int i40e_vsi_release(struct i40e_vsi *vsi)
if (vsi->netdev) {
/* results in a call to i40e_close() */
unregister_netdev(vsi->netdev);
- free_netdev(vsi->netdev);
- vsi->netdev = NULL;
}
} else {
if (!test_and_set_bit(__I40E_DOWN, &vsi->state))
@@ -6791,6 +6992,10 @@ int i40e_vsi_release(struct i40e_vsi *vsi)
i40e_vsi_delete(vsi);
i40e_vsi_free_q_vectors(vsi);
+ if (vsi->netdev) {
+ free_netdev(vsi->netdev);
+ vsi->netdev = NULL;
+ }
i40e_vsi_clear_rings(vsi);
i40e_vsi_clear(vsi);
@@ -6845,13 +7050,12 @@ static int i40e_vsi_setup_vectors(struct i40e_vsi *vsi)
}
if (vsi->base_vector) {
- dev_info(&pf->pdev->dev,
- "VSI %d has non-zero base vector %d\n",
+ dev_info(&pf->pdev->dev, "VSI %d has non-zero base vector %d\n",
vsi->seid, vsi->base_vector);
return -EEXIST;
}
- ret = i40e_alloc_q_vectors(vsi);
+ ret = i40e_vsi_alloc_q_vectors(vsi);
if (ret) {
dev_info(&pf->pdev->dev,
"failed to allocate %d q_vector for VSI %d, ret=%d\n",
@@ -6865,7 +7069,7 @@ static int i40e_vsi_setup_vectors(struct i40e_vsi *vsi)
vsi->num_q_vectors, vsi->idx);
if (vsi->base_vector < 0) {
dev_info(&pf->pdev->dev,
- "failed to get q tracking for VSI %d, err=%d\n",
+ "failed to get queue tracking for VSI %d, err=%d\n",
vsi->seid, vsi->base_vector);
i40e_vsi_free_q_vectors(vsi);
ret = -ENOENT;
@@ -7822,6 +8026,44 @@ static int i40e_setup_pf_filter_control(struct i40e_pf *pf)
return 0;
}
+#define INFO_STRING_LEN 255
+static void i40e_print_features(struct i40e_pf *pf)
+{
+ struct i40e_hw *hw = &pf->hw;
+ char *buf, *string;
+
+ string = kzalloc(INFO_STRING_LEN, GFP_KERNEL);
+ if (!string) {
+ dev_err(&pf->pdev->dev, "Features string allocation failed\n");
+ return;
+ }
+
+ buf = string;
+
+ buf += sprintf(string, "Features: PF-id[%d] ", hw->pf_id);
+#ifdef CONFIG_PCI_IOV
+ buf += sprintf(buf, "VFs: %d ", pf->num_req_vfs);
+#endif
+ buf += sprintf(buf, "VSIs: %d QP: %d ", pf->hw.func_caps.num_vsis,
+ pf->vsi[pf->lan_vsi]->num_queue_pairs);
+
+ if (pf->flags & I40E_FLAG_RSS_ENABLED)
+ buf += sprintf(buf, "RSS ");
+ buf += sprintf(buf, "FDir ");
+ if (pf->flags & I40E_FLAG_FD_ATR_ENABLED)
+ buf += sprintf(buf, "ATR ");
+ if (pf->flags & I40E_FLAG_FD_SB_ENABLED)
+ buf += sprintf(buf, "NTUPLE ");
+ if (pf->flags & I40E_FLAG_DCB_ENABLED)
+ buf += sprintf(buf, "DCB ");
+ if (pf->flags & I40E_FLAG_PTP)
+ buf += sprintf(buf, "PTP ");
+
+ BUG_ON(buf > (string + INFO_STRING_LEN));
+ dev_info(&pf->pdev->dev, "%s\n", string);
+ kfree(string);
+}
+
/**
* i40e_probe - Device initialization routine
* @pdev: PCI device information struct
@@ -7848,17 +8090,14 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return err;
/* set up for high or low dma */
- if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) {
- /* coherent mask for the same size will always succeed if
- * dma_set_mask does
- */
- dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
- } else if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
- dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
- } else {
- dev_err(&pdev->dev, "DMA configuration failed: %d\n", err);
- err = -EIO;
- goto err_dma;
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (err) {
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err) {
+ dev_err(&pdev->dev,
+ "DMA configuration failed: 0x%x\n", err);
+ goto err_dma;
+ }
}
/* set up pci connections */
@@ -7946,13 +8185,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err = i40e_init_adminq(hw);
dev_info(&pdev->dev, "%s\n", i40e_fw_version_str(hw));
- if (((hw->nvm.version & I40E_NVM_VERSION_HI_MASK)
- >> I40E_NVM_VERSION_HI_SHIFT) != I40E_CURRENT_NVM_VERSION_HI) {
- dev_info(&pdev->dev,
- "warning: NVM version not supported, supported version: %02x.%02x\n",
- I40E_CURRENT_NVM_VERSION_HI,
- I40E_CURRENT_NVM_VERSION_LO);
- }
if (err) {
dev_info(&pdev->dev,
"init_adminq failed: %d expecting API %02x.%02x\n",
@@ -7961,6 +8193,8 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_pf_reset;
}
+ i40e_verify_eeprom(pf);
+
i40e_clear_pxe_mode(hw);
err = i40e_get_capabilities(pf);
if (err)
@@ -8062,7 +8296,8 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* prep for VF support */
if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) &&
- (pf->flags & I40E_FLAG_MSIX_ENABLED)) {
+ (pf->flags & I40E_FLAG_MSIX_ENABLED) &&
+ !test_bit(__I40E_BAD_EEPROM, &pf->state)) {
u32 val;
/* disable link interrupts for VFs */
@@ -8070,6 +8305,16 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
val &= ~I40E_PFGEN_PORTMDIO_NUM_VFLINK_STAT_ENA_MASK;
wr32(hw, I40E_PFGEN_PORTMDIO_NUM, val);
i40e_flush(hw);
+
+ if (pci_num_vf(pdev)) {
+ dev_info(&pdev->dev,
+ "Active VFs found, allocating resources.\n");
+ err = i40e_alloc_vfs(pf, pci_num_vf(pdev));
+ if (err)
+ dev_info(&pdev->dev,
+ "Error %d allocating resources for existing VFs\n",
+ err);
+ }
}
pfs_found++;
@@ -8092,7 +8337,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
i40e_set_pci_config_data(hw, link_status);
- dev_info(&pdev->dev, "PCI Express: %s %s\n",
+ dev_info(&pdev->dev, "PCI-Express: %s %s\n",
(hw->bus.speed == i40e_bus_speed_8000 ? "Speed 8.0GT/s" :
hw->bus.speed == i40e_bus_speed_5000 ? "Speed 5.0GT/s" :
hw->bus.speed == i40e_bus_speed_2500 ? "Speed 2.5GT/s" :
@@ -8109,6 +8354,9 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_warn(&pdev->dev, "Please move the device to a different PCI-e link with more lanes and/or higher transfer rate.\n");
}
+ /* print a string summarizing features */
+ i40e_print_features(pf);
+
return 0;
/* Unwind what we've done if something failed in the setup */
@@ -8165,16 +8413,16 @@ static void i40e_remove(struct pci_dev *pdev)
i40e_ptp_stop(pf);
- if (pf->flags & I40E_FLAG_SRIOV_ENABLED) {
- i40e_free_vfs(pf);
- pf->flags &= ~I40E_FLAG_SRIOV_ENABLED;
- }
-
/* no more scheduling of any task */
set_bit(__I40E_DOWN, &pf->state);
del_timer_sync(&pf->service_timer);
cancel_work_sync(&pf->service_task);
+ if (pf->flags & I40E_FLAG_SRIOV_ENABLED) {
+ i40e_free_vfs(pf);
+ pf->flags &= ~I40E_FLAG_SRIOV_ENABLED;
+ }
+
i40e_fdir_teardown(pf);
/* If there is a switch structure or any orphans, remove them.
diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
index 73f95b081927..262bdf11d221 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
@@ -27,14 +27,14 @@
#include "i40e_prototype.h"
/**
- * i40e_init_nvm_ops - Initialize NVM function pointers.
- * @hw: pointer to the HW structure.
+ * i40e_init_nvm_ops - Initialize NVM function pointers
+ * @hw: pointer to the HW structure
*
- * Setups the function pointers and the NVM info structure. Should be called
- * once per NVM initialization, e.g. inside the i40e_init_shared_code().
- * Please notice that the NVM term is used here (& in all methods covered
- * in this file) as an equivalent of the FLASH part mapped into the SR.
- * We are accessing FLASH always thru the Shadow RAM.
+ * Setup the function pointers and the NVM info structure. Should be called
+ * once per NVM initialization, e.g. inside the i40e_init_shared_code().
+ * Please notice that the NVM term is used here (& in all methods covered
+ * in this file) as an equivalent of the FLASH part mapped into the SR.
+ * We are accessing FLASH always thru the Shadow RAM.
**/
i40e_status i40e_init_nvm(struct i40e_hw *hw)
{
@@ -49,16 +49,16 @@ i40e_status i40e_init_nvm(struct i40e_hw *hw)
gens = rd32(hw, I40E_GLNVM_GENS);
sr_size = ((gens & I40E_GLNVM_GENS_SR_SIZE_MASK) >>
I40E_GLNVM_GENS_SR_SIZE_SHIFT);
- /* Switching to words (sr_size contains power of 2KB). */
+ /* Switching to words (sr_size contains power of 2KB) */
nvm->sr_size = (1 << sr_size) * I40E_SR_WORDS_IN_1KB;
- /* Check if we are in the normal or blank NVM programming mode. */
+ /* Check if we are in the normal or blank NVM programming mode */
fla = rd32(hw, I40E_GLNVM_FLA);
- if (fla & I40E_GLNVM_FLA_LOCKED_MASK) { /* Normal programming mode. */
- /* Max NVM timeout. */
+ if (fla & I40E_GLNVM_FLA_LOCKED_MASK) { /* Normal programming mode */
+ /* Max NVM timeout */
nvm->timeout = I40E_MAX_NVM_TIMEOUT;
nvm->blank_nvm_mode = false;
- } else { /* Blank programming mode. */
+ } else { /* Blank programming mode */
nvm->blank_nvm_mode = true;
ret_code = I40E_ERR_NVM_BLANK_MODE;
hw_dbg(hw, "NVM init error: unsupported blank mode.\n");
@@ -68,12 +68,12 @@ i40e_status i40e_init_nvm(struct i40e_hw *hw)
}
/**
- * i40e_acquire_nvm - Generic request for acquiring the NVM ownership.
- * @hw: pointer to the HW structure.
- * @access: NVM access type (read or write).
+ * i40e_acquire_nvm - Generic request for acquiring the NVM ownership
+ * @hw: pointer to the HW structure
+ * @access: NVM access type (read or write)
*
- * This function will request NVM ownership for reading
- * via the proper Admin Command.
+ * This function will request NVM ownership for reading
+ * via the proper Admin Command.
**/
i40e_status i40e_acquire_nvm(struct i40e_hw *hw,
enum i40e_aq_resource_access_type access)
@@ -87,20 +87,20 @@ i40e_status i40e_acquire_nvm(struct i40e_hw *hw,
ret_code = i40e_aq_request_resource(hw, I40E_NVM_RESOURCE_ID, access,
0, &time, NULL);
- /* Reading the Global Device Timer. */
+ /* Reading the Global Device Timer */
gtime = rd32(hw, I40E_GLVFGEN_TIMER);
- /* Store the timeout. */
+ /* Store the timeout */
hw->nvm.hw_semaphore_timeout = I40E_MS_TO_GTIME(time) + gtime;
if (ret_code) {
- /* Set the polling timeout. */
+ /* Set the polling timeout */
if (time > I40E_MAX_NVM_TIMEOUT)
timeout = I40E_MS_TO_GTIME(I40E_MAX_NVM_TIMEOUT)
+ gtime;
else
timeout = hw->nvm.hw_semaphore_timeout;
- /* Poll until the current NVM owner timeouts. */
+ /* Poll until the current NVM owner timeouts */
while (gtime < timeout) {
usleep_range(10000, 20000);
ret_code = i40e_aq_request_resource(hw,
@@ -128,10 +128,10 @@ i40e_i40e_acquire_nvm_exit:
}
/**
- * i40e_release_nvm - Generic request for releasing the NVM ownership.
- * @hw: pointer to the HW structure.
+ * i40e_release_nvm - Generic request for releasing the NVM ownership
+ * @hw: pointer to the HW structure
*
- * This function will release NVM resource via the proper Admin Command.
+ * This function will release NVM resource via the proper Admin Command.
**/
void i40e_release_nvm(struct i40e_hw *hw)
{
@@ -140,17 +140,17 @@ void i40e_release_nvm(struct i40e_hw *hw)
}
/**
- * i40e_poll_sr_srctl_done_bit - Polls the GLNVM_SRCTL done bit.
- * @hw: pointer to the HW structure.
+ * i40e_poll_sr_srctl_done_bit - Polls the GLNVM_SRCTL done bit
+ * @hw: pointer to the HW structure
*
- * Polls the SRCTL Shadow RAM register done bit.
+ * Polls the SRCTL Shadow RAM register done bit.
**/
static i40e_status i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw)
{
i40e_status ret_code = I40E_ERR_TIMEOUT;
u32 srctl, wait_cnt;
- /* Poll the I40E_GLNVM_SRCTL until the done bit is set. */
+ /* Poll the I40E_GLNVM_SRCTL until the done bit is set */
for (wait_cnt = 0; wait_cnt < I40E_SRRD_SRCTL_ATTEMPTS; wait_cnt++) {
srctl = rd32(hw, I40E_GLNVM_SRCTL);
if (srctl & I40E_GLNVM_SRCTL_DONE_MASK) {
@@ -165,12 +165,12 @@ static i40e_status i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw)
}
/**
- * i40e_read_nvm_word - Reads Shadow RAM
- * @hw: pointer to the HW structure.
- * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF).
- * @data: word read from the Shadow RAM.
+ * i40e_read_nvm_word - Reads Shadow RAM
+ * @hw: pointer to the HW structure
+ * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
+ * @data: word read from the Shadow RAM
*
- * Reads 16 bit word from the Shadow RAM using the GLNVM_SRCTL register.
+ * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register.
**/
i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset,
u16 *data)
@@ -184,15 +184,15 @@ i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset,
goto read_nvm_exit;
}
- /* Poll the done bit first. */
+ /* Poll the done bit first */
ret_code = i40e_poll_sr_srctl_done_bit(hw);
if (!ret_code) {
- /* Write the address and start reading. */
+ /* Write the address and start reading */
sr_reg = (u32)(offset << I40E_GLNVM_SRCTL_ADDR_SHIFT) |
(1 << I40E_GLNVM_SRCTL_START_SHIFT);
wr32(hw, I40E_GLNVM_SRCTL, sr_reg);
- /* Poll I40E_GLNVM_SRCTL until the done bit is set. */
+ /* Poll I40E_GLNVM_SRCTL until the done bit is set */
ret_code = i40e_poll_sr_srctl_done_bit(hw);
if (!ret_code) {
sr_reg = rd32(hw, I40E_GLNVM_SRDATA);
@@ -210,16 +210,15 @@ read_nvm_exit:
}
/**
- * i40e_read_nvm_buffer - Reads Shadow RAM buffer.
- * @hw: pointer to the HW structure.
- * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF).
- * @words: number of words to read (in) &
- * number of words read before the NVM ownership timeout (out).
- * @data: words read from the Shadow RAM.
+ * i40e_read_nvm_buffer - Reads Shadow RAM buffer
+ * @hw: pointer to the HW structure
+ * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF).
+ * @words: (in) number of words to read; (out) number of words actually read
+ * @data: words read from the Shadow RAM
*
- * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd()
- * method. The buffer read is preceded by the NVM ownership take
- * and followed by the release.
+ * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd()
+ * method. The buffer read is preceded by the NVM ownership take
+ * and followed by the release.
**/
i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset,
u16 *words, u16 *data)
@@ -227,7 +226,7 @@ i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset,
i40e_status ret_code = 0;
u16 index, word;
- /* Loop thru the selected region. */
+ /* Loop thru the selected region */
for (word = 0; word < *words; word++) {
index = offset + word;
ret_code = i40e_read_nvm_word(hw, index, &data[word]);
@@ -235,21 +234,21 @@ i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset,
break;
}
- /* Update the number of words read from the Shadow RAM. */
+ /* Update the number of words read from the Shadow RAM */
*words = word;
return ret_code;
}
/**
- * i40e_calc_nvm_checksum - Calculates and returns the checksum
- * @hw: pointer to hardware structure
- * @checksum: pointer to the checksum
+ * i40e_calc_nvm_checksum - Calculates and returns the checksum
+ * @hw: pointer to hardware structure
+ * @checksum: pointer to the checksum
*
- * This function calculate SW Checksum that covers the whole 64kB shadow RAM
- * except the VPD and PCIe ALT Auto-load modules. The structure and size of VPD
- * is customer specific and unknown. Therefore, this function skips all maximum
- * possible size of VPD (1kB).
+ * This function calculates SW Checksum that covers the whole 64kB shadow RAM
+ * except the VPD and PCIe ALT Auto-load modules. The structure and size of VPD
+ * is customer specific and unknown. Therefore, this function skips all maximum
+ * possible size of VPD (1kB).
**/
static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw,
u16 *checksum)
@@ -311,12 +310,12 @@ i40e_calc_nvm_checksum_exit:
}
/**
- * i40e_validate_nvm_checksum - Validate EEPROM checksum
- * @hw: pointer to hardware structure
- * @checksum: calculated checksum
+ * i40e_validate_nvm_checksum - Validate EEPROM checksum
+ * @hw: pointer to hardware structure
+ * @checksum: calculated checksum
*
- * Performs checksum calculation and validates the NVM SW checksum. If the
- * caller does not need checksum, the value can be NULL.
+ * Performs checksum calculation and validates the NVM SW checksum. If the
+ * caller does not need checksum, the value can be NULL.
**/
i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw,
u16 *checksum)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index ed91f93ede2b..9cd57e617959 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -231,6 +231,13 @@ i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw,
u16 *checksum);
void i40e_set_pci_config_data(struct i40e_hw *hw, u16 link_status);
+extern struct i40e_rx_ptype_decoded i40e_ptype_lookup[];
+
+static inline struct i40e_rx_ptype_decoded decode_rx_desc_ptype(u8 ptype)
+{
+ return i40e_ptype_lookup[ptype];
+}
+
/* prototype for functions used for SW locks */
/* i40e_common for VF drivers*/
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index d4bb482b1a7f..0f5d96ad281d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -25,6 +25,7 @@
******************************************************************************/
#include "i40e.h"
+#include "i40e_prototype.h"
static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size,
u32 td_tag)
@@ -39,11 +40,12 @@ static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size,
#define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS)
/**
* i40e_program_fdir_filter - Program a Flow Director filter
- * @fdir_input: Packet data that will be filter parameters
+ * @fdir_data: Packet data that will be filter parameters
+ * @raw_packet: the pre-allocated packet buffer for FDir
* @pf: The pf pointer
* @add: True for add/update, False for remove
**/
-int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data,
+int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet,
struct i40e_pf *pf, bool add)
{
struct i40e_filter_program_desc *fdir_desc;
@@ -68,8 +70,8 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data,
tx_ring = vsi->tx_rings[0];
dev = tx_ring->dev;
- dma = dma_map_single(dev, fdir_data->raw_packet,
- I40E_FDIR_MAX_RAW_PACKET_LOOKUP, DMA_TO_DEVICE);
+ dma = dma_map_single(dev, raw_packet,
+ I40E_FDIR_MAX_RAW_PACKET_SIZE, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma))
goto dma_fail;
@@ -132,14 +134,14 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data,
tx_ring->next_to_use = (i + 1 < tx_ring->count) ? i + 1 : 0;
/* record length, and DMA address */
- dma_unmap_len_set(tx_buf, len, I40E_FDIR_MAX_RAW_PACKET_LOOKUP);
+ dma_unmap_len_set(tx_buf, len, I40E_FDIR_MAX_RAW_PACKET_SIZE);
dma_unmap_addr_set(tx_buf, dma, dma);
tx_desc->buffer_addr = cpu_to_le64(dma);
td_cmd = I40E_TXD_CMD | I40E_TX_DESC_CMD_DUMMY;
tx_desc->cmd_type_offset_bsz =
- build_ctob(td_cmd, 0, I40E_FDIR_MAX_RAW_PACKET_LOOKUP, 0);
+ build_ctob(td_cmd, 0, I40E_FDIR_MAX_RAW_PACKET_SIZE, 0);
/* set the timestamp */
tx_buf->time_stamp = jiffies;
@@ -161,26 +163,329 @@ dma_fail:
return -1;
}
+#define IP_HEADER_OFFSET 14
+#define I40E_UDPIP_DUMMY_PACKET_LEN 42
+/**
+ * i40e_add_del_fdir_udpv4 - Add/Remove UDPv4 filters
+ * @vsi: pointer to the targeted VSI
+ * @fd_data: the flow director data required for the FDir descriptor
+ * @raw_packet: the pre-allocated packet buffer for FDir
+ * @add: true adds a filter, false removes it
+ *
+ * Returns 0 if the filters were successfully added or removed
+ **/
+static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
+ struct i40e_fdir_filter *fd_data,
+ u8 *raw_packet, bool add)
+{
+ struct i40e_pf *pf = vsi->back;
+ struct udphdr *udp;
+ struct iphdr *ip;
+ bool err = false;
+ int ret;
+ int i;
+ static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
+ 0x45, 0, 0, 0x1c, 0, 0, 0x40, 0, 0x40, 0x11, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ memcpy(raw_packet, packet, I40E_UDPIP_DUMMY_PACKET_LEN);
+
+ ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET);
+ udp = (struct udphdr *)(raw_packet + IP_HEADER_OFFSET
+ + sizeof(struct iphdr));
+
+ ip->daddr = fd_data->dst_ip[0];
+ udp->dest = fd_data->dst_port;
+ ip->saddr = fd_data->src_ip[0];
+ udp->source = fd_data->src_port;
+
+ for (i = I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP;
+ i <= I40E_FILTER_PCTYPE_NONF_IPV4_UDP; i++) {
+ fd_data->pctype = i;
+ ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+
+ if (ret) {
+ dev_info(&pf->pdev->dev,
+ "Filter command send failed for PCTYPE %d (ret = %d)\n",
+ fd_data->pctype, ret);
+ err = true;
+ } else {
+ dev_info(&pf->pdev->dev,
+ "Filter OK for PCTYPE %d (ret = %d)\n",
+ fd_data->pctype, ret);
+ }
+ }
+
+ return err ? -EOPNOTSUPP : 0;
+}
+
+#define I40E_TCPIP_DUMMY_PACKET_LEN 54
+/**
+ * i40e_add_del_fdir_tcpv4 - Add/Remove TCPv4 filters
+ * @vsi: pointer to the targeted VSI
+ * @fd_data: the flow director data required for the FDir descriptor
+ * @raw_packet: the pre-allocated packet buffer for FDir
+ * @add: true adds a filter, false removes it
+ *
+ * Returns 0 if the filters were successfully added or removed
+ **/
+static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
+ struct i40e_fdir_filter *fd_data,
+ u8 *raw_packet, bool add)
+{
+ struct i40e_pf *pf = vsi->back;
+ struct tcphdr *tcp;
+ struct iphdr *ip;
+ bool err = false;
+ int ret;
+ /* Dummy packet */
+ static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
+ 0x45, 0, 0, 0x28, 0, 0, 0x40, 0, 0x40, 0x6, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x11,
+ 0x0, 0x72, 0, 0, 0, 0};
+
+ memcpy(raw_packet, packet, I40E_TCPIP_DUMMY_PACKET_LEN);
+
+ ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET);
+ tcp = (struct tcphdr *)(raw_packet + IP_HEADER_OFFSET
+ + sizeof(struct iphdr));
+
+ ip->daddr = fd_data->dst_ip[0];
+ tcp->dest = fd_data->dst_port;
+ ip->saddr = fd_data->src_ip[0];
+ tcp->source = fd_data->src_port;
+
+ if (add) {
+ if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
+ dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n");
+ pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+ }
+ }
+
+ fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN;
+ ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+
+ if (ret) {
+ dev_info(&pf->pdev->dev,
+ "Filter command send failed for PCTYPE %d (ret = %d)\n",
+ fd_data->pctype, ret);
+ err = true;
+ } else {
+ dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n",
+ fd_data->pctype, ret);
+ }
+
+ fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
+
+ ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+ if (ret) {
+ dev_info(&pf->pdev->dev,
+ "Filter command send failed for PCTYPE %d (ret = %d)\n",
+ fd_data->pctype, ret);
+ err = true;
+ } else {
+ dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n",
+ fd_data->pctype, ret);
+ }
+
+ return err ? -EOPNOTSUPP : 0;
+}
+
+/**
+ * i40e_add_del_fdir_sctpv4 - Add/Remove SCTPv4 Flow Director filters for
+ * a specific flow spec
+ * @vsi: pointer to the targeted VSI
+ * @fd_data: the flow director data required for the FDir descriptor
+ * @raw_packet: the pre-allocated packet buffer for FDir
+ * @add: true adds a filter, false removes it
+ *
+ * Always returns -EOPNOTSUPP
+ **/
+static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi,
+ struct i40e_fdir_filter *fd_data,
+ u8 *raw_packet, bool add)
+{
+ return -EOPNOTSUPP;
+}
+
+#define I40E_IP_DUMMY_PACKET_LEN 34
+/**
+ * i40e_add_del_fdir_ipv4 - Add/Remove IPv4 Flow Director filters for
+ * a specific flow spec
+ * @vsi: pointer to the targeted VSI
+ * @fd_data: the flow director data required for the FDir descriptor
+ * @raw_packet: the pre-allocated packet buffer for FDir
+ * @add: true adds a filter, false removes it
+ *
+ * Returns 0 if the filters were successfully added or removed
+ **/
+static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi,
+ struct i40e_fdir_filter *fd_data,
+ u8 *raw_packet, bool add)
+{
+ struct i40e_pf *pf = vsi->back;
+ struct iphdr *ip;
+ bool err = false;
+ int ret;
+ int i;
+ static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
+ 0x45, 0, 0, 0x14, 0, 0, 0x40, 0, 0x40, 0x10, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0};
+
+ memcpy(raw_packet, packet, I40E_IP_DUMMY_PACKET_LEN);
+ ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET);
+
+ ip->saddr = fd_data->src_ip[0];
+ ip->daddr = fd_data->dst_ip[0];
+ ip->protocol = 0;
+
+ for (i = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
+ i <= I40E_FILTER_PCTYPE_FRAG_IPV4; i++) {
+ fd_data->pctype = i;
+ ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+
+ if (ret) {
+ dev_info(&pf->pdev->dev,
+ "Filter command send failed for PCTYPE %d (ret = %d)\n",
+ fd_data->pctype, ret);
+ err = true;
+ } else {
+ dev_info(&pf->pdev->dev,
+ "Filter OK for PCTYPE %d (ret = %d)\n",
+ fd_data->pctype, ret);
+ }
+ }
+
+ return err ? -EOPNOTSUPP : 0;
+}
+
+/**
+ * i40e_add_del_fdir - Build raw packets to add/del fdir filter
+ * @vsi: pointer to the targeted VSI
+ * @cmd: command to get or set RX flow classification rules
+ * @add: true adds a filter, false removes it
+ *
+ **/
+int i40e_add_del_fdir(struct i40e_vsi *vsi,
+ struct i40e_fdir_filter *input, bool add)
+{
+ struct i40e_pf *pf = vsi->back;
+ u8 *raw_packet;
+ int ret;
+
+ /* Populate the Flow Director that we have at the moment
+ * and allocate the raw packet buffer for the calling functions
+ */
+ raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, GFP_KERNEL);
+ if (!raw_packet)
+ return -ENOMEM;
+
+ switch (input->flow_type & ~FLOW_EXT) {
+ case TCP_V4_FLOW:
+ ret = i40e_add_del_fdir_tcpv4(vsi, input, raw_packet,
+ add);
+ break;
+ case UDP_V4_FLOW:
+ ret = i40e_add_del_fdir_udpv4(vsi, input, raw_packet,
+ add);
+ break;
+ case SCTP_V4_FLOW:
+ ret = i40e_add_del_fdir_sctpv4(vsi, input, raw_packet,
+ add);
+ break;
+ case IPV4_FLOW:
+ ret = i40e_add_del_fdir_ipv4(vsi, input, raw_packet,
+ add);
+ break;
+ case IP_USER_FLOW:
+ switch (input->ip4_proto) {
+ case IPPROTO_TCP:
+ ret = i40e_add_del_fdir_tcpv4(vsi, input,
+ raw_packet, add);
+ break;
+ case IPPROTO_UDP:
+ ret = i40e_add_del_fdir_udpv4(vsi, input,
+ raw_packet, add);
+ break;
+ case IPPROTO_SCTP:
+ ret = i40e_add_del_fdir_sctpv4(vsi, input,
+ raw_packet, add);
+ break;
+ default:
+ ret = i40e_add_del_fdir_ipv4(vsi, input,
+ raw_packet, add);
+ break;
+ }
+ break;
+ default:
+ dev_info(&pf->pdev->dev, "Could not specify spec type %d",
+ input->flow_type);
+ ret = -EINVAL;
+ }
+
+ kfree(raw_packet);
+ return ret;
+}
+
/**
* i40e_fd_handle_status - check the Programming Status for FD
* @rx_ring: the Rx ring for this descriptor
- * @qw: the descriptor data
+ * @rx_desc: the Rx descriptor for programming Status, not a packet descriptor.
* @prog_id: the id originally used for programming
*
* This is used to verify if the FD programming or invalidation
* requested by SW to the HW is successful or not and take actions accordingly.
**/
-static void i40e_fd_handle_status(struct i40e_ring *rx_ring, u32 qw, u8 prog_id)
+static void i40e_fd_handle_status(struct i40e_ring *rx_ring,
+ union i40e_rx_desc *rx_desc, u8 prog_id)
{
- struct pci_dev *pdev = rx_ring->vsi->back->pdev;
+ struct i40e_pf *pf = rx_ring->vsi->back;
+ struct pci_dev *pdev = pf->pdev;
+ u32 fcnt_prog, fcnt_avail;
u32 error;
+ u64 qw;
+ qw = le64_to_cpu(rx_desc->wb.qword1.status_error_len);
error = (qw & I40E_RX_PROG_STATUS_DESC_QW1_ERROR_MASK) >>
I40E_RX_PROG_STATUS_DESC_QW1_ERROR_SHIFT;
- /* for now just print the Status */
- dev_info(&pdev->dev, "FD programming id %02x, Status %08x\n",
- prog_id, error);
+ if (error == (0x1 << I40E_RX_PROG_STATUS_DESC_FD_TBL_FULL_SHIFT)) {
+ dev_warn(&pdev->dev, "ntuple filter loc = %d, could not be added\n",
+ rx_desc->wb.qword0.hi_dword.fd_id);
+
+ /* filter programming failed most likely due to table full */
+ fcnt_prog = i40e_get_current_fd_count(pf);
+ fcnt_avail = pf->hw.fdir_shared_filter_count +
+ pf->fdir_pf_filter_count;
+
+ /* If ATR is running fcnt_prog can quickly change,
+ * if we are very close to full, it makes sense to disable
+ * FD ATR/SB and then re-enable it when there is room.
+ */
+ if (fcnt_prog >= (fcnt_avail - I40E_FDIR_BUFFER_FULL_MARGIN)) {
+ /* Turn off ATR first */
+ if (pf->flags | I40E_FLAG_FD_ATR_ENABLED) {
+ pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+ dev_warn(&pdev->dev, "FD filter space full, ATR for further flows will be turned off\n");
+ pf->auto_disable_flags |=
+ I40E_FLAG_FD_ATR_ENABLED;
+ pf->flags |= I40E_FLAG_FDIR_REQUIRES_REINIT;
+ } else if (pf->flags | I40E_FLAG_FD_SB_ENABLED) {
+ pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
+ dev_warn(&pdev->dev, "FD filter space full, new ntuple rules will not be added\n");
+ pf->auto_disable_flags |=
+ I40E_FLAG_FD_SB_ENABLED;
+ pf->flags |= I40E_FLAG_FDIR_REQUIRES_REINIT;
+ }
+ } else {
+ dev_info(&pdev->dev, "FD filter programming error");
+ }
+ } else if (error ==
+ (0x1 << I40E_RX_PROG_STATUS_DESC_NO_FD_ENTRY_SHIFT)) {
+ if (I40E_DEBUG_FD & pf->hw.debug_mask)
+ dev_info(&pdev->dev, "ntuple filter loc = %d, could not be removed\n",
+ rx_desc->wb.qword0.hi_dword.fd_id);
+ }
}
/**
@@ -315,6 +620,20 @@ static bool i40e_check_tx_hang(struct i40e_ring *tx_ring)
}
/**
+ * i40e_get_head - Retrieve head from head writeback
+ * @tx_ring: tx ring to fetch head of
+ *
+ * Returns value of Tx ring head based on value stored
+ * in head write-back location
+ **/
+static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
+{
+ void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
+
+ return le32_to_cpu(*(volatile __le32 *)head);
+}
+
+/**
* i40e_clean_tx_irq - Reclaim resources after transmit completes
* @tx_ring: tx ring to clean
* @budget: how many cleans we're allowed
@@ -325,6 +644,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
{
u16 i = tx_ring->next_to_clean;
struct i40e_tx_buffer *tx_buf;
+ struct i40e_tx_desc *tx_head;
struct i40e_tx_desc *tx_desc;
unsigned int total_packets = 0;
unsigned int total_bytes = 0;
@@ -333,6 +653,8 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
tx_desc = I40E_TX_DESC(tx_ring, i);
i -= tx_ring->count;
+ tx_head = I40E_TX_DESC(tx_ring, i40e_get_head(tx_ring));
+
do {
struct i40e_tx_desc *eop_desc = tx_buf->next_to_watch;
@@ -343,9 +665,8 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
/* prevent any other reads prior to eop_desc */
read_barrier_depends();
- /* if the descriptor isn't done, no work yet to do */
- if (!(eop_desc->cmd_type_offset_bsz &
- cpu_to_le64(I40E_TX_DESC_DTYPE_DESC_DONE)))
+ /* we have caught up to head, no work left to do */
+ if (tx_head == tx_desc)
break;
/* clear next_to_watch to prevent false hangs */
@@ -577,7 +898,7 @@ static void i40e_clean_programming_status(struct i40e_ring *rx_ring,
I40E_RX_PROG_STATUS_DESC_QW1_PROGID_SHIFT;
if (id == I40E_RX_PROG_STATUS_DESC_FD_FILTER_STATUS)
- i40e_fd_handle_status(rx_ring, qw, id);
+ i40e_fd_handle_status(rx_ring, rx_desc, id);
}
/**
@@ -601,6 +922,10 @@ int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring)
/* round up to nearest 4K */
tx_ring->size = tx_ring->count * sizeof(struct i40e_tx_desc);
+ /* add u32 for head writeback, align after this takes care of
+ * guaranteeing this is at least one cache line in size
+ */
+ tx_ring->size += sizeof(u32);
tx_ring->size = ALIGN(tx_ring->size, 4096);
tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
&tx_ring->dma, GFP_KERNEL);
@@ -892,7 +1217,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
return;
- /* likely incorrect csum if alternate IP extention headers found */
+ /* likely incorrect csum if alternate IP extension headers found */
if (rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
return;
@@ -956,6 +1281,29 @@ static inline u32 i40e_rx_hash(struct i40e_ring *ring,
}
/**
+ * i40e_ptype_to_hash - 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)
+{
+ struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(ptype);
+
+ if (!decoded.known)
+ return PKT_HASH_TYPE_NONE;
+
+ if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+ decoded.payload_layer == I40E_RX_PTYPE_PAYLOAD_LAYER_PAY4)
+ return PKT_HASH_TYPE_L4;
+ else if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+ decoded.payload_layer == I40E_RX_PTYPE_PAYLOAD_LAYER_PAY3)
+ return PKT_HASH_TYPE_L3;
+ else
+ return PKT_HASH_TYPE_L2;
+}
+
+/**
* i40e_clean_rx_irq - Reclaim resources after receive completes
* @rx_ring: rx ring to clean
* @budget: how many cleans we're allowed
@@ -972,8 +1320,11 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
u16 i = rx_ring->next_to_clean;
union i40e_rx_desc *rx_desc;
u32 rx_error, rx_status;
+ u8 rx_ptype;
u64 qword;
- u16 rx_ptype;
+
+ if (budget <= 0)
+ return 0;
rx_desc = I40E_RX_DESC(rx_ring, i);
qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len);
@@ -1087,7 +1438,8 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
goto next_desc;
}
- skb->rxhash = i40e_rx_hash(rx_ring, rx_desc);
+ skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc),
+ i40e_ptype_to_hash(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) >>
@@ -1246,8 +1598,6 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
if (!tx_ring->atr_sample_rate)
return;
- tx_ring->atr_count++;
-
/* snag network header to get L4 type and address */
hdr.network = skb_network_header(skb);
@@ -1269,8 +1619,17 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
th = (struct tcphdr *)(hdr.network + hlen);
- /* sample on all syn/fin packets or once every atr sample rate */
- if (!th->fin && !th->syn && (tx_ring->atr_count < tx_ring->atr_sample_rate))
+ /* Due to lack of space, no more new filters can be programmed */
+ if (th->syn && (pf->auto_disable_flags & I40E_FLAG_FD_ATR_ENABLED))
+ return;
+
+ tx_ring->atr_count++;
+
+ /* sample on all syn/fin/rst packets or once every atr sample rate */
+ if (!th->fin &&
+ !th->syn &&
+ !th->rst &&
+ (tx_ring->atr_count < tx_ring->atr_sample_rate))
return;
tx_ring->atr_count = 0;
@@ -1294,7 +1653,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
dtype_cmd = I40E_TX_DESC_DTYPE_FILTER_PROG;
- dtype_cmd |= th->fin ?
+ dtype_cmd |= (th->fin || th->rst) ?
(I40E_FILTER_PROGRAM_DESC_PCMD_REMOVE <<
I40E_TXD_FLTR_QW1_PCMD_SHIFT) :
(I40E_FILTER_PROGRAM_DESC_PCMD_ADD_UPDATE <<
@@ -1596,7 +1955,8 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring,
struct i40e_tx_context_desc *context_desc;
int i = tx_ring->next_to_use;
- if (!cd_type_cmd_tso_mss && !cd_tunneling && !cd_l2tag2)
+ if ((cd_type_cmd_tso_mss == I40E_TX_DESC_DTYPE_CONTEXT) &&
+ !cd_tunneling && !cd_l2tag2)
return;
/* grab the next descriptor */
@@ -1707,9 +2067,23 @@ static void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_bi = &tx_ring->tx_bi[i];
}
- 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);
+ /* 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),
@@ -1812,7 +2186,7 @@ static int i40e_xmit_descriptor_count(struct sk_buff *skb,
/* need: 1 descriptor per page * PAGE_SIZE/I40E_MAX_DATA_PER_TXD,
* + 1 desc for skb_head_len/I40E_MAX_DATA_PER_TXD,
- * + 2 desc gap to keep tail from touching head,
+ * + 4 desc gap to avoid the cache line where head is,
* + 1 desc for context descriptor,
* otherwise try next time
*/
@@ -1823,7 +2197,7 @@ static int i40e_xmit_descriptor_count(struct sk_buff *skb,
count += skb_shinfo(skb)->nr_frags;
#endif
count += TXD_USE_COUNT(skb_headlen(skb));
- if (i40e_maybe_stop_tx(tx_ring, count + 3)) {
+ if (i40e_maybe_stop_tx(tx_ring, count + 4 + 1)) {
tx_ring->tx_stats.tx_busy++;
return 0;
}
diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h
index 181a825d3160..71a968fe557f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_type.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_type.h
@@ -91,6 +91,7 @@ enum i40e_debug_mask {
I40E_DEBUG_FLOW = 0x00000200,
I40E_DEBUG_DCB = 0x00000400,
I40E_DEBUG_DIAG = 0x00000800,
+ I40E_DEBUG_FD = 0x00001000,
I40E_DEBUG_AQ_MESSAGE = 0x01000000,
I40E_DEBUG_AQ_DESCRIPTOR = 0x02000000,
@@ -458,6 +459,10 @@ union i40e_32byte_rx_desc {
union {
__le32 rss; /* RSS Hash */
__le32 fcoe_param; /* FCoE DDP Context id */
+ /* Flow director filter id in case of
+ * Programming status desc WB
+ */
+ __le32 fd_id;
} hi_dword;
} qword0;
struct {
@@ -698,7 +703,7 @@ enum i40e_rx_prog_status_desc_prog_id_masks {
enum i40e_rx_prog_status_desc_error_bits {
/* Note: These are predefined bit offsets */
I40E_RX_PROG_STATUS_DESC_FD_TBL_FULL_SHIFT = 0,
- I40E_RX_PROG_STATUS_DESC_NO_FD_QUOTA_SHIFT = 1,
+ I40E_RX_PROG_STATUS_DESC_NO_FD_ENTRY_SHIFT = 1,
I40E_RX_PROG_STATUS_DESC_FCOE_TBL_FULL_SHIFT = 2,
I40E_RX_PROG_STATUS_DESC_FCOE_CONFLICT_SHIFT = 3
};
@@ -1010,6 +1015,11 @@ struct i40e_hw_port_stats {
u64 tx_size_big; /* ptc9522 */
u64 mac_short_packet_dropped; /* mspdc */
u64 checksum_error; /* xec */
+ /* EEE LPI */
+ bool tx_lpi_status;
+ bool rx_lpi_status;
+ u64 tx_lpi_count; /* etlpic */
+ u64 rx_lpi_count; /* erlpic */
};
/* Checksum and Shadow RAM pointers */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index b9d1c1c8ca5a..02c11a7f7d29 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -69,7 +69,7 @@ static inline bool i40e_vc_isvalid_vector_id(struct i40e_vf *vf, u8 vector_id)
{
struct i40e_pf *pf = vf->pf;
- return vector_id <= pf->hw.func_caps.num_msix_vectors_vf;
+ return vector_id < pf->hw.func_caps.num_msix_vectors_vf;
}
/***********************vf resource mgmt routines*****************/
@@ -126,8 +126,8 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_idx,
reg_idx = I40E_VPINT_LNKLST0(vf->vf_id);
else
reg_idx = I40E_VPINT_LNKLSTN(
- (pf->hw.func_caps.num_msix_vectors_vf
- * vf->vf_id) + (vector_id - 1));
+ ((pf->hw.func_caps.num_msix_vectors_vf - 1) * vf->vf_id) +
+ (vector_id - 1));
if (vecmap->rxq_map == 0 && vecmap->txq_map == 0) {
/* Special case - No queues mapped on this vector */
@@ -230,6 +230,9 @@ static int i40e_config_vsi_tx_queue(struct i40e_vf *vf, u16 vsi_idx,
tx_ctx.qlen = info->ring_len;
tx_ctx.rdylist = le16_to_cpu(pf->vsi[vsi_idx]->info.qs_handle[0]);
tx_ctx.rdylist_act = 0;
+ tx_ctx.head_wb_ena = 1;
+ tx_ctx.head_wb_addr = info->dma_ring_addr +
+ (info->ring_len * sizeof(struct i40e_tx_desc));
/* clear the context in the HMC */
ret = i40e_clear_lan_tx_queue_context(hw, pf_queue_id);
@@ -408,18 +411,10 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
"Could not allocate VF broadcast filter\n");
}
- if (!f) {
- dev_err(&pf->pdev->dev, "Unable to add ucast filter\n");
- ret = -ENOMEM;
- goto error_alloc_vsi_res;
- }
-
/* program mac filter */
ret = i40e_sync_vsi_filters(vsi);
- if (ret) {
+ if (ret)
dev_err(&pf->pdev->dev, "Unable to program ucast filters\n");
- goto error_alloc_vsi_res;
- }
error_alloc_vsi_res:
return ret;
@@ -514,7 +509,8 @@ static void i40e_free_vf_res(struct i40e_vf *vf)
vf->lan_vsi_index = 0;
vf->lan_vsi_id = 0;
}
- msix_vf = pf->hw.func_caps.num_msix_vectors_vf + 1;
+ msix_vf = pf->hw.func_caps.num_msix_vectors_vf;
+
/* disable interrupts so the VF starts in a known state */
for (i = 0; i < msix_vf; i++) {
/* format is same for both registers */
@@ -679,9 +675,9 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr)
complete_reset:
/* reallocate vf resources to reset the VSI state */
i40e_free_vf_res(vf);
- mdelay(10);
i40e_alloc_vf_res(vf);
i40e_enable_vf_mappings(vf);
+ set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states);
/* tell the VF the reset is done */
wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), I40E_VFR_VFACTIVE);
@@ -847,7 +843,7 @@ void i40e_free_vfs(struct i40e_pf *pf)
*
* allocate vf resources
**/
-static int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs)
+int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs)
{
struct i40e_vf *vfs;
int i, ret = 0;
@@ -855,16 +851,18 @@ static int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs)
/* Disable interrupt 0 so we don't try to handle the VFLR. */
i40e_irq_dynamic_disable_icr0(pf);
- ret = pci_enable_sriov(pf->pdev, num_alloc_vfs);
- if (ret) {
- dev_err(&pf->pdev->dev,
- "pci_enable_sriov failed with error %d!\n", ret);
- pf->num_alloc_vfs = 0;
- goto err_iov;
+ /* Check to see if we're just allocating resources for extant VFs */
+ if (pci_num_vf(pf->pdev) != num_alloc_vfs) {
+ ret = pci_enable_sriov(pf->pdev, num_alloc_vfs);
+ if (ret) {
+ dev_err(&pf->pdev->dev,
+ "Failed to enable SR-IOV, error %d.\n", ret);
+ pf->num_alloc_vfs = 0;
+ goto err_iov;
+ }
}
-
/* allocate memory */
- vfs = kzalloc(num_alloc_vfs * sizeof(struct i40e_vf), GFP_KERNEL);
+ vfs = kcalloc(num_alloc_vfs, sizeof(struct i40e_vf), GFP_KERNEL);
if (!vfs) {
ret = -ENOMEM;
goto err_alloc;
@@ -1776,7 +1774,7 @@ int i40e_vc_process_vf_msg(struct i40e_pf *pf, u16 vf_id, u32 v_opcode,
u32 v_retval, u8 *msg, u16 msglen)
{
struct i40e_hw *hw = &pf->hw;
- int local_vf_id = vf_id - hw->func_caps.vf_base_id;
+ unsigned int local_vf_id = vf_id - hw->func_caps.vf_base_id;
struct i40e_vf *vf;
int ret;
@@ -1873,7 +1871,8 @@ int i40e_vc_process_vflr_event(struct i40e_pf *pf)
/* clear the bit in GLGEN_VFLRSTAT */
wr32(hw, I40E_GLGEN_VFLRSTAT(reg_idx), (1 << bit_idx));
- i40e_reset_vf(vf, true);
+ if (!test_bit(__I40E_DOWN, &pf->state))
+ i40e_reset_vf(vf, true);
}
}
@@ -1924,15 +1923,28 @@ static void i40e_vc_vf_broadcast(struct i40e_pf *pf,
void i40e_vc_notify_link_state(struct i40e_pf *pf)
{
struct i40e_virtchnl_pf_event pfe;
+ struct i40e_hw *hw = &pf->hw;
+ struct i40e_vf *vf = pf->vf;
+ struct i40e_link_status *ls = &pf->hw.phy.link_info;
+ int i;
pfe.event = I40E_VIRTCHNL_EVENT_LINK_CHANGE;
pfe.severity = I40E_PF_EVENT_SEVERITY_INFO;
- pfe.event_data.link_event.link_status =
- pf->hw.phy.link_info.link_info & I40E_AQ_LINK_UP;
- pfe.event_data.link_event.link_speed = pf->hw.phy.link_info.link_speed;
-
- i40e_vc_vf_broadcast(pf, I40E_VIRTCHNL_OP_EVENT, I40E_SUCCESS,
- (u8 *)&pfe, sizeof(struct i40e_virtchnl_pf_event));
+ for (i = 0; i < pf->num_alloc_vfs; i++) {
+ if (vf->link_forced) {
+ pfe.event_data.link_event.link_status = vf->link_up;
+ pfe.event_data.link_event.link_speed =
+ (vf->link_up ? I40E_LINK_SPEED_40GB : 0);
+ } else {
+ pfe.event_data.link_event.link_status =
+ ls->link_info & I40E_AQ_LINK_UP;
+ pfe.event_data.link_event.link_speed = ls->link_speed;
+ }
+ i40e_aq_send_msg_to_vf(hw, vf->vf_id, I40E_VIRTCHNL_OP_EVENT,
+ 0, (u8 *)&pfe, sizeof(pfe),
+ NULL);
+ vf++;
+ }
}
/**
@@ -2197,3 +2209,64 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
error_param:
return ret;
}
+
+/**
+ * i40e_ndo_set_vf_link_state
+ * @netdev: network interface device structure
+ * @vf_id: vf identifier
+ * @link: required link state
+ *
+ * Set the link state of a specified VF, regardless of physical link state
+ **/
+int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
+{
+ struct i40e_netdev_priv *np = netdev_priv(netdev);
+ struct i40e_pf *pf = np->vsi->back;
+ struct i40e_virtchnl_pf_event pfe;
+ struct i40e_hw *hw = &pf->hw;
+ struct i40e_vf *vf;
+ int ret = 0;
+
+ /* validate the request */
+ if (vf_id >= pf->num_alloc_vfs) {
+ dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
+ ret = -EINVAL;
+ goto error_out;
+ }
+
+ vf = &pf->vf[vf_id];
+
+ pfe.event = I40E_VIRTCHNL_EVENT_LINK_CHANGE;
+ pfe.severity = I40E_PF_EVENT_SEVERITY_INFO;
+
+ switch (link) {
+ case IFLA_VF_LINK_STATE_AUTO:
+ vf->link_forced = false;
+ pfe.event_data.link_event.link_status =
+ pf->hw.phy.link_info.link_info & I40E_AQ_LINK_UP;
+ pfe.event_data.link_event.link_speed =
+ pf->hw.phy.link_info.link_speed;
+ break;
+ case IFLA_VF_LINK_STATE_ENABLE:
+ vf->link_forced = true;
+ vf->link_up = true;
+ pfe.event_data.link_event.link_status = true;
+ pfe.event_data.link_event.link_speed = I40E_LINK_SPEED_40GB;
+ break;
+ case IFLA_VF_LINK_STATE_DISABLE:
+ vf->link_forced = true;
+ vf->link_up = false;
+ pfe.event_data.link_event.link_status = false;
+ pfe.event_data.link_event.link_speed = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error_out;
+ }
+ /* Notify the VF of its new link state */
+ i40e_aq_send_msg_to_vf(hw, vf->vf_id, I40E_VIRTCHNL_OP_EVENT,
+ 0, (u8 *)&pfe, sizeof(pfe), NULL);
+
+error_out:
+ return ret;
+}
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
index cc1feee36e12..389c47f396d5 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
@@ -98,10 +98,13 @@ struct i40e_vf {
unsigned long vf_caps; /* vf's adv. capabilities */
unsigned long vf_states; /* vf's runtime states */
+ bool link_forced;
+ bool link_up; /* only valid if vf link is forced */
};
void i40e_free_vfs(struct i40e_pf *pf);
int i40e_pci_sriov_configure(struct pci_dev *dev, int num_vfs);
+int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs);
int i40e_vc_process_vf_msg(struct i40e_pf *pf, u16 vf_id, u32 v_opcode,
u32 v_retval, u8 *msg, u16 msglen);
int i40e_vc_process_vflr_event(struct i40e_pf *pf);
@@ -115,6 +118,8 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate);
int i40e_ndo_get_vf_config(struct net_device *netdev,
int vf_id, struct ifla_vf_info *ivi);
+int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link);
+
void i40e_vc_notify_link_state(struct i40e_pf *pf);
void i40e_vc_notify_reset(struct i40e_pf *pf);
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
index f7cea1bca38d..97662b6bd98a 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
@@ -1229,7 +1229,7 @@ struct i40e_aqc_add_remove_cloud_filters_element_data {
#define I40E_AQC_ADD_CLOUD_TNL_TYPE_NGE 2
#define I40E_AQC_ADD_CLOUD_TNL_TYPE_IP 3
- __le32 tenant_id ;
+ __le32 tenant_id;
u8 reserved[4];
__le16 queue_number;
#define I40E_AQC_ADD_CLOUD_QUEUE_SHIFT 0
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c
index 7b13953b28c4..ae084378faab 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c
@@ -160,6 +160,372 @@ i40e_status i40evf_aq_queue_shutdown(struct i40e_hw *hw,
}
+/* The i40evf_ptype_lookup table is used to convert from the 8-bit ptype in the
+ * hardware to a bit-field that can be used by SW to more easily determine the
+ * packet type.
+ *
+ * Macros are used to shorten the table lines and make this table human
+ * readable.
+ *
+ * We store the PTYPE in the top byte of the bit field - this is just so that
+ * we can check that the table doesn't have a row missing, as the index into
+ * the table should be the PTYPE.
+ *
+ * Typical work flow:
+ *
+ * IF NOT i40evf_ptype_lookup[ptype].known
+ * THEN
+ * Packet is unknown
+ * ELSE IF i40evf_ptype_lookup[ptype].outer_ip == I40E_RX_PTYPE_OUTER_IP
+ * Use the rest of the fields to look at the tunnels, inner protocols, etc
+ * ELSE
+ * Use the enum i40e_rx_l2_ptype to decode the packet type
+ * ENDIF
+ */
+
+/* macro to make the table lines short */
+#define I40E_PTT(PTYPE, OUTER_IP, OUTER_IP_VER, OUTER_FRAG, T, TE, TEF, I, PL)\
+ { PTYPE, \
+ 1, \
+ I40E_RX_PTYPE_OUTER_##OUTER_IP, \
+ I40E_RX_PTYPE_OUTER_##OUTER_IP_VER, \
+ I40E_RX_PTYPE_##OUTER_FRAG, \
+ I40E_RX_PTYPE_TUNNEL_##T, \
+ I40E_RX_PTYPE_TUNNEL_END_##TE, \
+ I40E_RX_PTYPE_##TEF, \
+ I40E_RX_PTYPE_INNER_PROT_##I, \
+ I40E_RX_PTYPE_PAYLOAD_LAYER_##PL }
+
+#define I40E_PTT_UNUSED_ENTRY(PTYPE) \
+ { PTYPE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+/* shorter macros makes the table fit but are terse */
+#define I40E_RX_PTYPE_NOF I40E_RX_PTYPE_NOT_FRAG
+#define I40E_RX_PTYPE_FRG I40E_RX_PTYPE_FRAG
+#define I40E_RX_PTYPE_INNER_PROT_TS I40E_RX_PTYPE_INNER_PROT_TIMESYNC
+
+/* Lookup table mapping the HW PTYPE to the bit field for decoding */
+struct i40e_rx_ptype_decoded i40evf_ptype_lookup[] = {
+ /* L2 Packet types */
+ I40E_PTT_UNUSED_ENTRY(0),
+ I40E_PTT(1, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+ I40E_PTT(2, L2, NONE, NOF, NONE, NONE, NOF, TS, PAY2),
+ I40E_PTT(3, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+ I40E_PTT_UNUSED_ENTRY(4),
+ I40E_PTT_UNUSED_ENTRY(5),
+ I40E_PTT(6, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+ I40E_PTT(7, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+ I40E_PTT_UNUSED_ENTRY(8),
+ I40E_PTT_UNUSED_ENTRY(9),
+ I40E_PTT(10, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+ I40E_PTT(11, L2, NONE, NOF, NONE, NONE, NOF, NONE, NONE),
+ I40E_PTT(12, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(13, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(14, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(15, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(16, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(17, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(18, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(19, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(20, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(21, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+
+ /* Non Tunneled IPv4 */
+ I40E_PTT(22, IP, IPV4, FRG, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(23, IP, IPV4, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(24, IP, IPV4, NOF, NONE, NONE, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(25),
+ I40E_PTT(26, IP, IPV4, NOF, NONE, NONE, NOF, TCP, PAY4),
+ I40E_PTT(27, IP, IPV4, NOF, NONE, NONE, NOF, SCTP, PAY4),
+ I40E_PTT(28, IP, IPV4, NOF, NONE, NONE, NOF, ICMP, PAY4),
+
+ /* IPv4 --> IPv4 */
+ I40E_PTT(29, IP, IPV4, NOF, IP_IP, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(30, IP, IPV4, NOF, IP_IP, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(31, IP, IPV4, NOF, IP_IP, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(32),
+ I40E_PTT(33, IP, IPV4, NOF, IP_IP, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(34, IP, IPV4, NOF, IP_IP, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(35, IP, IPV4, NOF, IP_IP, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv4 --> IPv6 */
+ I40E_PTT(36, IP, IPV4, NOF, IP_IP, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(37, IP, IPV4, NOF, IP_IP, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(38, IP, IPV4, NOF, IP_IP, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(39),
+ I40E_PTT(40, IP, IPV4, NOF, IP_IP, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(41, IP, IPV4, NOF, IP_IP, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(42, IP, IPV4, NOF, IP_IP, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv4 --> GRE/NAT */
+ I40E_PTT(43, IP, IPV4, NOF, IP_GRENAT, NONE, NOF, NONE, PAY3),
+
+ /* IPv4 --> GRE/NAT --> IPv4 */
+ I40E_PTT(44, IP, IPV4, NOF, IP_GRENAT, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(45, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(46, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(47),
+ I40E_PTT(48, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(49, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(50, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv4 --> GRE/NAT --> IPv6 */
+ I40E_PTT(51, IP, IPV4, NOF, IP_GRENAT, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(52, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(53, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(54),
+ I40E_PTT(55, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(56, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(57, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv4 --> GRE/NAT --> MAC */
+ I40E_PTT(58, IP, IPV4, NOF, IP_GRENAT_MAC, NONE, NOF, NONE, PAY3),
+
+ /* IPv4 --> GRE/NAT --> MAC --> IPv4 */
+ I40E_PTT(59, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(60, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(61, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(62),
+ I40E_PTT(63, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(64, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(65, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv4 --> GRE/NAT -> MAC --> IPv6 */
+ I40E_PTT(66, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(67, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(68, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(69),
+ I40E_PTT(70, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(71, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(72, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv4 --> GRE/NAT --> MAC/VLAN */
+ I40E_PTT(73, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, NONE, NOF, NONE, PAY3),
+
+ /* IPv4 ---> GRE/NAT -> MAC/VLAN --> IPv4 */
+ I40E_PTT(74, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(75, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(76, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(77),
+ I40E_PTT(78, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(79, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(80, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv4 -> GRE/NAT -> MAC/VLAN --> IPv6 */
+ I40E_PTT(81, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(82, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(83, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(84),
+ I40E_PTT(85, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(86, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(87, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, ICMP, PAY4),
+
+ /* Non Tunneled IPv6 */
+ I40E_PTT(88, IP, IPV6, FRG, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(89, IP, IPV6, NOF, NONE, NONE, NOF, NONE, PAY3),
+ I40E_PTT(90, IP, IPV6, NOF, NONE, NONE, NOF, UDP, PAY3),
+ I40E_PTT_UNUSED_ENTRY(91),
+ I40E_PTT(92, IP, IPV6, NOF, NONE, NONE, NOF, TCP, PAY4),
+ I40E_PTT(93, IP, IPV6, NOF, NONE, NONE, NOF, SCTP, PAY4),
+ I40E_PTT(94, IP, IPV6, NOF, NONE, NONE, NOF, ICMP, PAY4),
+
+ /* IPv6 --> IPv4 */
+ I40E_PTT(95, IP, IPV6, NOF, IP_IP, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(96, IP, IPV6, NOF, IP_IP, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(97, IP, IPV6, NOF, IP_IP, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(98),
+ I40E_PTT(99, IP, IPV6, NOF, IP_IP, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(100, IP, IPV6, NOF, IP_IP, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(101, IP, IPV6, NOF, IP_IP, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv6 --> IPv6 */
+ I40E_PTT(102, IP, IPV6, NOF, IP_IP, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(103, IP, IPV6, NOF, IP_IP, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(104, IP, IPV6, NOF, IP_IP, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(105),
+ I40E_PTT(106, IP, IPV6, NOF, IP_IP, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(107, IP, IPV6, NOF, IP_IP, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(108, IP, IPV6, NOF, IP_IP, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT */
+ I40E_PTT(109, IP, IPV6, NOF, IP_GRENAT, NONE, NOF, NONE, PAY3),
+
+ /* IPv6 --> GRE/NAT -> IPv4 */
+ I40E_PTT(110, IP, IPV6, NOF, IP_GRENAT, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(111, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(112, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(113),
+ I40E_PTT(114, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(115, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(116, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT -> IPv6 */
+ I40E_PTT(117, IP, IPV6, NOF, IP_GRENAT, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(118, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(119, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(120),
+ I40E_PTT(121, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(122, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(123, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT -> MAC */
+ I40E_PTT(124, IP, IPV6, NOF, IP_GRENAT_MAC, NONE, NOF, NONE, PAY3),
+
+ /* IPv6 --> GRE/NAT -> MAC -> IPv4 */
+ I40E_PTT(125, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(126, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(127, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(128),
+ I40E_PTT(129, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(130, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(131, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT -> MAC -> IPv6 */
+ I40E_PTT(132, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(133, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(134, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(135),
+ I40E_PTT(136, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(137, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(138, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT -> MAC/VLAN */
+ I40E_PTT(139, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, NONE, NOF, NONE, PAY3),
+
+ /* IPv6 --> GRE/NAT -> MAC/VLAN --> IPv4 */
+ I40E_PTT(140, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, FRG, NONE, PAY3),
+ I40E_PTT(141, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, NONE, PAY3),
+ I40E_PTT(142, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(143),
+ I40E_PTT(144, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, TCP, PAY4),
+ I40E_PTT(145, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, SCTP, PAY4),
+ I40E_PTT(146, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, ICMP, PAY4),
+
+ /* IPv6 --> GRE/NAT -> MAC/VLAN --> IPv6 */
+ I40E_PTT(147, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, FRG, NONE, PAY3),
+ I40E_PTT(148, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, NONE, PAY3),
+ I40E_PTT(149, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, UDP, PAY4),
+ I40E_PTT_UNUSED_ENTRY(150),
+ I40E_PTT(151, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, TCP, PAY4),
+ I40E_PTT(152, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, SCTP, PAY4),
+ I40E_PTT(153, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, ICMP, PAY4),
+
+ /* unused entries */
+ I40E_PTT_UNUSED_ENTRY(154),
+ I40E_PTT_UNUSED_ENTRY(155),
+ I40E_PTT_UNUSED_ENTRY(156),
+ I40E_PTT_UNUSED_ENTRY(157),
+ I40E_PTT_UNUSED_ENTRY(158),
+ I40E_PTT_UNUSED_ENTRY(159),
+
+ I40E_PTT_UNUSED_ENTRY(160),
+ I40E_PTT_UNUSED_ENTRY(161),
+ I40E_PTT_UNUSED_ENTRY(162),
+ I40E_PTT_UNUSED_ENTRY(163),
+ I40E_PTT_UNUSED_ENTRY(164),
+ I40E_PTT_UNUSED_ENTRY(165),
+ I40E_PTT_UNUSED_ENTRY(166),
+ I40E_PTT_UNUSED_ENTRY(167),
+ I40E_PTT_UNUSED_ENTRY(168),
+ I40E_PTT_UNUSED_ENTRY(169),
+
+ I40E_PTT_UNUSED_ENTRY(170),
+ I40E_PTT_UNUSED_ENTRY(171),
+ I40E_PTT_UNUSED_ENTRY(172),
+ I40E_PTT_UNUSED_ENTRY(173),
+ I40E_PTT_UNUSED_ENTRY(174),
+ I40E_PTT_UNUSED_ENTRY(175),
+ I40E_PTT_UNUSED_ENTRY(176),
+ I40E_PTT_UNUSED_ENTRY(177),
+ I40E_PTT_UNUSED_ENTRY(178),
+ I40E_PTT_UNUSED_ENTRY(179),
+
+ I40E_PTT_UNUSED_ENTRY(180),
+ I40E_PTT_UNUSED_ENTRY(181),
+ I40E_PTT_UNUSED_ENTRY(182),
+ I40E_PTT_UNUSED_ENTRY(183),
+ I40E_PTT_UNUSED_ENTRY(184),
+ I40E_PTT_UNUSED_ENTRY(185),
+ I40E_PTT_UNUSED_ENTRY(186),
+ I40E_PTT_UNUSED_ENTRY(187),
+ I40E_PTT_UNUSED_ENTRY(188),
+ I40E_PTT_UNUSED_ENTRY(189),
+
+ I40E_PTT_UNUSED_ENTRY(190),
+ I40E_PTT_UNUSED_ENTRY(191),
+ I40E_PTT_UNUSED_ENTRY(192),
+ I40E_PTT_UNUSED_ENTRY(193),
+ I40E_PTT_UNUSED_ENTRY(194),
+ I40E_PTT_UNUSED_ENTRY(195),
+ I40E_PTT_UNUSED_ENTRY(196),
+ I40E_PTT_UNUSED_ENTRY(197),
+ I40E_PTT_UNUSED_ENTRY(198),
+ I40E_PTT_UNUSED_ENTRY(199),
+
+ I40E_PTT_UNUSED_ENTRY(200),
+ I40E_PTT_UNUSED_ENTRY(201),
+ I40E_PTT_UNUSED_ENTRY(202),
+ I40E_PTT_UNUSED_ENTRY(203),
+ I40E_PTT_UNUSED_ENTRY(204),
+ I40E_PTT_UNUSED_ENTRY(205),
+ I40E_PTT_UNUSED_ENTRY(206),
+ I40E_PTT_UNUSED_ENTRY(207),
+ I40E_PTT_UNUSED_ENTRY(208),
+ I40E_PTT_UNUSED_ENTRY(209),
+
+ I40E_PTT_UNUSED_ENTRY(210),
+ I40E_PTT_UNUSED_ENTRY(211),
+ I40E_PTT_UNUSED_ENTRY(212),
+ I40E_PTT_UNUSED_ENTRY(213),
+ I40E_PTT_UNUSED_ENTRY(214),
+ I40E_PTT_UNUSED_ENTRY(215),
+ I40E_PTT_UNUSED_ENTRY(216),
+ I40E_PTT_UNUSED_ENTRY(217),
+ I40E_PTT_UNUSED_ENTRY(218),
+ I40E_PTT_UNUSED_ENTRY(219),
+
+ I40E_PTT_UNUSED_ENTRY(220),
+ I40E_PTT_UNUSED_ENTRY(221),
+ I40E_PTT_UNUSED_ENTRY(222),
+ I40E_PTT_UNUSED_ENTRY(223),
+ I40E_PTT_UNUSED_ENTRY(224),
+ I40E_PTT_UNUSED_ENTRY(225),
+ I40E_PTT_UNUSED_ENTRY(226),
+ I40E_PTT_UNUSED_ENTRY(227),
+ I40E_PTT_UNUSED_ENTRY(228),
+ I40E_PTT_UNUSED_ENTRY(229),
+
+ I40E_PTT_UNUSED_ENTRY(230),
+ I40E_PTT_UNUSED_ENTRY(231),
+ I40E_PTT_UNUSED_ENTRY(232),
+ I40E_PTT_UNUSED_ENTRY(233),
+ I40E_PTT_UNUSED_ENTRY(234),
+ I40E_PTT_UNUSED_ENTRY(235),
+ I40E_PTT_UNUSED_ENTRY(236),
+ I40E_PTT_UNUSED_ENTRY(237),
+ I40E_PTT_UNUSED_ENTRY(238),
+ I40E_PTT_UNUSED_ENTRY(239),
+
+ I40E_PTT_UNUSED_ENTRY(240),
+ I40E_PTT_UNUSED_ENTRY(241),
+ I40E_PTT_UNUSED_ENTRY(242),
+ I40E_PTT_UNUSED_ENTRY(243),
+ I40E_PTT_UNUSED_ENTRY(244),
+ I40E_PTT_UNUSED_ENTRY(245),
+ I40E_PTT_UNUSED_ENTRY(246),
+ I40E_PTT_UNUSED_ENTRY(247),
+ I40E_PTT_UNUSED_ENTRY(248),
+ I40E_PTT_UNUSED_ENTRY(249),
+
+ I40E_PTT_UNUSED_ENTRY(250),
+ I40E_PTT_UNUSED_ENTRY(251),
+ I40E_PTT_UNUSED_ENTRY(252),
+ I40E_PTT_UNUSED_ENTRY(253),
+ I40E_PTT_UNUSED_ENTRY(254),
+ I40E_PTT_UNUSED_ENTRY(255)
+};
+
+
/**
* i40e_aq_send_msg_to_pf
* @hw: pointer to the hardware structure
@@ -199,8 +565,7 @@ i40e_status i40e_aq_send_msg_to_pf(struct i40e_hw *hw,
details.async = true;
cmd_details = &details;
}
- status = i40evf_asq_send_command(hw, (struct i40e_aq_desc *)&desc, msg,
- msglen, cmd_details);
+ status = i40evf_asq_send_command(hw, &desc, msg, msglen, cmd_details);
return status;
}
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h
index 7841573a58c9..97ab8c2b76f8 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h
@@ -63,6 +63,13 @@ i40e_status i40evf_aq_queue_shutdown(struct i40e_hw *hw,
i40e_status i40e_set_mac_type(struct i40e_hw *hw);
+extern struct i40e_rx_ptype_decoded i40evf_ptype_lookup[];
+
+static inline struct i40e_rx_ptype_decoded decode_rx_desc_ptype(u8 ptype)
+{
+ return i40evf_ptype_lookup[ptype];
+}
+
/* prototype for functions used for SW locks */
/* i40e_common for VF drivers*/
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
index ffdb01d853db..53be5f44d015 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -24,6 +24,7 @@
#include <linux/prefetch.h>
#include "i40evf.h"
+#include "i40e_prototype.h"
static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size,
u32 td_tag)
@@ -169,6 +170,20 @@ static bool i40e_check_tx_hang(struct i40e_ring *tx_ring)
}
/**
+ * i40e_get_head - Retrieve head from head writeback
+ * @tx_ring: tx ring to fetch head of
+ *
+ * Returns value of Tx ring head based on value stored
+ * in head write-back location
+ **/
+static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
+{
+ void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
+
+ return le32_to_cpu(*(volatile __le32 *)head);
+}
+
+/**
* i40e_clean_tx_irq - Reclaim resources after transmit completes
* @tx_ring: tx ring to clean
* @budget: how many cleans we're allowed
@@ -179,6 +194,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
{
u16 i = tx_ring->next_to_clean;
struct i40e_tx_buffer *tx_buf;
+ struct i40e_tx_desc *tx_head;
struct i40e_tx_desc *tx_desc;
unsigned int total_packets = 0;
unsigned int total_bytes = 0;
@@ -187,6 +203,8 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
tx_desc = I40E_TX_DESC(tx_ring, i);
i -= tx_ring->count;
+ tx_head = I40E_TX_DESC(tx_ring, i40e_get_head(tx_ring));
+
do {
struct i40e_tx_desc *eop_desc = tx_buf->next_to_watch;
@@ -197,9 +215,8 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
/* prevent any other reads prior to eop_desc */
read_barrier_depends();
- /* if the descriptor isn't done, no work yet to do */
- if (!(eop_desc->cmd_type_offset_bsz &
- cpu_to_le64(I40E_TX_DESC_DTYPE_DESC_DONE)))
+ /* we have caught up to head, no work left to do */
+ if (tx_head == tx_desc)
break;
/* clear next_to_watch to prevent false hangs */
@@ -431,6 +448,10 @@ int i40evf_setup_tx_descriptors(struct i40e_ring *tx_ring)
/* round up to nearest 4K */
tx_ring->size = tx_ring->count * sizeof(struct i40e_tx_desc);
+ /* add u32 for head writeback, align after this takes care of
+ * guaranteeing this is at least one cache line in size
+ */
+ tx_ring->size += sizeof(u32);
tx_ring->size = ALIGN(tx_ring->size, 4096);
tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
&tx_ring->dma, GFP_KERNEL);
@@ -722,7 +743,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
return;
- /* likely incorrect csum if alternate IP extention headers found */
+ /* likely incorrect csum if alternate IP extension headers found */
if (rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
return;
@@ -786,6 +807,29 @@ static inline u32 i40e_rx_hash(struct i40e_ring *ring,
}
/**
+ * i40e_ptype_to_hash - 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)
+{
+ struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(ptype);
+
+ if (!decoded.known)
+ return PKT_HASH_TYPE_NONE;
+
+ if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+ decoded.payload_layer == I40E_RX_PTYPE_PAYLOAD_LAYER_PAY4)
+ return PKT_HASH_TYPE_L4;
+ else if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+ decoded.payload_layer == I40E_RX_PTYPE_PAYLOAD_LAYER_PAY3)
+ return PKT_HASH_TYPE_L3;
+ else
+ return PKT_HASH_TYPE_L2;
+}
+
+/**
* i40e_clean_rx_irq - Reclaim resources after receive completes
* @rx_ring: rx ring to clean
* @budget: how many cleans we're allowed
@@ -802,13 +846,13 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
u16 i = rx_ring->next_to_clean;
union i40e_rx_desc *rx_desc;
u32 rx_error, rx_status;
+ u8 rx_ptype;
u64 qword;
- u16 rx_ptype;
rx_desc = I40E_RX_DESC(rx_ring, i);
qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len);
- rx_status = (qword & I40E_RXD_QW1_STATUS_MASK)
- >> I40E_RXD_QW1_STATUS_SHIFT;
+ rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >>
+ I40E_RXD_QW1_STATUS_SHIFT;
while (rx_status & (1 << I40E_RX_DESC_STATUS_DD_SHIFT)) {
union i40e_rx_desc *next_rxd;
@@ -912,7 +956,8 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
goto next_desc;
}
- skb->rxhash = i40e_rx_hash(rx_ring, rx_desc);
+ skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc),
+ i40e_ptype_to_hash(rx_ptype));
/* probably a little skewed due to removing CRC */
total_rx_bytes += skb->len;
total_rx_packets++;
@@ -1241,7 +1286,8 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring,
struct i40e_tx_context_desc *context_desc;
int i = tx_ring->next_to_use;
- if (!cd_type_cmd_tso_mss && !cd_tunneling && !cd_l2tag2)
+ if ((cd_type_cmd_tso_mss == I40E_TX_DESC_DTYPE_CONTEXT) &&
+ !cd_tunneling && !cd_l2tag2)
return;
/* grab the next descriptor */
@@ -1352,9 +1398,23 @@ static void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_bi = &tx_ring->tx_bi[i];
}
- 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);
+ /* 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),
@@ -1457,7 +1517,7 @@ static int i40e_xmit_descriptor_count(struct sk_buff *skb,
/* need: 1 descriptor per page * PAGE_SIZE/I40E_MAX_DATA_PER_TXD,
* + 1 desc for skb_head_len/I40E_MAX_DATA_PER_TXD,
- * + 2 desc gap to keep tail from touching head,
+ * + 4 desc gap to avoid the cache line where head is,
* + 1 desc for context descriptor,
* otherwise try next time
*/
@@ -1468,7 +1528,7 @@ static int i40e_xmit_descriptor_count(struct sk_buff *skb,
count += skb_shinfo(skb)->nr_frags;
#endif
count += TXD_USE_COUNT(skb_headlen(skb));
- if (i40e_maybe_stop_tx(tx_ring, count + 3)) {
+ if (i40e_maybe_stop_tx(tx_ring, count + 4 + 1)) {
tx_ring->tx_stats.tx_busy++;
return 0;
}
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h
index 3bffac06592f..4673b3381edd 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_type.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -64,8 +64,6 @@
struct i40e_hw;
typedef void (*I40E_ADMINQ_CALLBACK)(struct i40e_hw *, struct i40e_aq_desc *);
-#define ETH_ALEN 6
-
/* Data type manipulation macros. */
#define I40E_DESC_UNUSED(R) \
@@ -90,6 +88,7 @@ enum i40e_debug_mask {
I40E_DEBUG_FLOW = 0x00000200,
I40E_DEBUG_DCB = 0x00000400,
I40E_DEBUG_DIAG = 0x00000800,
+ I40E_DEBUG_FD = 0x00001000,
I40E_DEBUG_AQ_MESSAGE = 0x01000000,
I40E_DEBUG_AQ_DESCRIPTOR = 0x02000000,
@@ -466,6 +465,10 @@ union i40e_32byte_rx_desc {
union {
__le32 rss; /* RSS Hash */
__le32 fcoe_param; /* FCoE DDP Context id */
+ /* Flow director filter id in case of
+ * Programming status desc WB
+ */
+ __le32 fd_id;
} hi_dword;
} qword0;
struct {
@@ -706,7 +709,7 @@ enum i40e_rx_prog_status_desc_prog_id_masks {
enum i40e_rx_prog_status_desc_error_bits {
/* Note: These are predefined bit offsets */
I40E_RX_PROG_STATUS_DESC_FD_TBL_FULL_SHIFT = 0,
- I40E_RX_PROG_STATUS_DESC_NO_FD_QUOTA_SHIFT = 1,
+ I40E_RX_PROG_STATUS_DESC_NO_FD_ENTRY_SHIFT = 1,
I40E_RX_PROG_STATUS_DESC_FCOE_TBL_FULL_SHIFT = 2,
I40E_RX_PROG_STATUS_DESC_FCOE_CONFLICT_SHIFT = 3
};
@@ -1018,6 +1021,11 @@ struct i40e_hw_port_stats {
u64 tx_size_big; /* ptc9522 */
u64 mac_short_packet_dropped; /* mspdc */
u64 checksum_error; /* xec */
+ /* EEE LPI */
+ bool tx_lpi_status;
+ bool rx_lpi_status;
+ u64 tx_lpi_count; /* etlpic */
+ u64 rx_lpi_count; /* erlpic */
};
/* Checksum and Shadow RAM pointers */
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h
index ff6529b288a1..807807d62387 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf.h
+++ b/drivers/net/ethernet/intel/i40evf/i40evf.h
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -38,8 +38,6 @@
#include <linux/ipv6.h>
#include <net/ip6_checksum.h>
#include <net/udp.h>
-#include <linux/sctp.h>
-
#include "i40e_type.h"
#include "i40e_virtchnl.h"
@@ -164,15 +162,14 @@ struct i40evf_vlan_filter {
/* Driver state. The order of these is important! */
enum i40evf_state_t {
__I40EVF_STARTUP, /* driver loaded, probe complete */
- __I40EVF_FAILED, /* PF communication failed. Fatal. */
__I40EVF_REMOVE, /* driver is being unloaded */
__I40EVF_INIT_VERSION_CHECK, /* aq msg sent, awaiting reply */
__I40EVF_INIT_GET_RESOURCES, /* aq msg sent, awaiting reply */
__I40EVF_INIT_SW, /* got resources, setting up structs */
+ __I40EVF_RESETTING, /* in reset */
/* Below here, watchdog is running */
__I40EVF_DOWN, /* ready, can be opened */
__I40EVF_TESTING, /* in ethtool self-test */
- __I40EVF_RESETTING, /* in reset */
__I40EVF_RUNNING, /* opened, working */
};
@@ -185,47 +182,25 @@ enum i40evf_critical_section_t {
/* board specific private data structure */
struct i40evf_adapter {
struct timer_list watchdog_timer;
- struct vlan_group *vlgrp;
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 list_head vlan_filter_list;
- char name[MAX_MSIX_COUNT][IFNAMSIZ + 9];
-
- /* Interrupt Throttle Rate */
- u32 itr_setting;
- u16 eitr_low;
- u16 eitr_high;
+ char misc_vector_name[IFNAMSIZ + 9];
/* TX */
struct i40e_ring *tx_rings[I40E_MAX_VSI_QP];
- u64 restart_queue;
- u64 hw_csum_tx_good;
- u64 lsc_int;
- u64 hw_tso_ctxt;
- u64 hw_tso6_ctxt;
u32 tx_timeout_count;
struct list_head mac_filter_list;
-#ifdef DEBUG
- bool detect_tx_hung;
-#endif /* DEBUG */
/* RX */
struct i40e_ring *rx_rings[I40E_MAX_VSI_QP];
- int txd_count;
- int rxd_count;
u64 hw_csum_rx_error;
- u64 hw_rx_no_dma_resources;
- u64 hw_csum_rx_good;
- u64 non_eop_descs;
int num_msix_vectors;
struct msix_entry *msix_entries;
- u64 rx_hdr_split;
-
- u32 init_state;
- volatile unsigned long flags;
+ u32 flags;
#define I40EVF_FLAG_RX_CSUM_ENABLED (u32)(1)
#define I40EVF_FLAG_RX_1BUF_CAPABLE (u32)(1 << 1)
#define I40EVF_FLAG_RX_PS_CAPABLE (u32)(1 << 2)
@@ -234,6 +209,9 @@ struct i40evf_adapter {
#define I40EVF_FLAG_IMIR_ENABLED (u32)(1 << 5)
#define I40EVF_FLAG_MQ_CAPABLE (u32)(1 << 6)
#define I40EVF_FLAG_NEED_LINK_UPDATE (u32)(1 << 7)
+#define I40EVF_FLAG_PF_COMMS_FAILED (u32)(1 << 8)
+#define I40EVF_FLAG_RESET_PENDING (u32)(1 << 9)
+#define I40EVF_FLAG_RESET_NEEDED (u32)(1 << 10)
/* duplcates for common code */
#define I40E_FLAG_FDIR_ATR_ENABLED 0
#define I40E_FLAG_DCB_ENABLED 0
@@ -251,21 +229,19 @@ struct i40evf_adapter {
#define I40EVF_FLAG_AQ_CONFIGURE_QUEUES (u32)(1 << 6)
#define I40EVF_FLAG_AQ_MAP_VECTORS (u32)(1 << 7)
#define I40EVF_FLAG_AQ_HANDLE_RESET (u32)(1 << 8)
+
/* OS defined structs */
struct net_device *netdev;
struct pci_dev *pdev;
struct net_device_stats net_stats;
- /* structs defined in i40e_vf.h */
- struct i40e_hw hw;
+ struct i40e_hw hw; /* defined in i40e_type.h */
enum i40evf_state_t state;
volatile unsigned long crit_section;
- u64 tx_busy;
struct work_struct watchdog_task;
bool netdev_registered;
- bool dev_closed;
bool link_up;
enum i40e_virtchnl_ops current_op;
struct i40e_virtchnl_vf_resource *vf_res; /* incl. all VSIs */
@@ -276,11 +252,6 @@ struct i40evf_adapter {
u32 aq_wait_count;
};
-struct i40evf_info {
- enum i40e_mac_type mac;
- unsigned int flags;
-};
-
/* needed by i40evf_ethtool.c */
extern char i40evf_driver_name[];
@@ -315,6 +286,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter);
void i40evf_del_vlans(struct i40evf_adapter *adapter);
void i40evf_set_promiscuous(struct i40evf_adapter *adapter, int flags);
void i40evf_request_stats(struct i40evf_adapter *adapter);
+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);
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
index b0b1f4bf5ac0..8b0db1ce179c 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -241,6 +241,7 @@ static int i40evf_set_ringparam(struct net_device *netdev,
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
u32 new_rx_count, new_tx_count;
+ int i;
if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
return -EINVAL;
@@ -256,12 +257,14 @@ static int i40evf_set_ringparam(struct net_device *netdev,
new_rx_count = ALIGN(new_rx_count, I40EVF_REQ_DESCRIPTOR_MULTIPLE);
/* if nothing to do return success */
- if ((new_tx_count == adapter->txd_count) &&
- (new_rx_count == adapter->rxd_count))
+ if ((new_tx_count == adapter->tx_rings[0]->count) &&
+ (new_rx_count == adapter->rx_rings[0]->count))
return 0;
- adapter->txd_count = new_tx_count;
- adapter->rxd_count = new_rx_count;
+ for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) {
+ adapter->tx_rings[0]->count = new_tx_count;
+ adapter->rx_rings[0]->count = new_rx_count;
+ }
if (netif_running(netdev))
i40evf_reinit_locked(adapter);
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
index f5caf4419243..e35e66ffa782 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -31,10 +31,10 @@ char i40evf_driver_name[] = "i40evf";
static const char i40evf_driver_string[] =
"Intel(R) XL710 X710 Virtual Function Network Driver";
-#define DRV_VERSION "0.9.11"
+#define DRV_VERSION "0.9.16"
const char i40evf_driver_version[] = DRV_VERSION;
static const char i40evf_copyright[] =
- "Copyright (c) 2013 Intel Corporation.";
+ "Copyright (c) 2013 - 2014 Intel Corporation.";
/* i40evf_pci_tbl - PCI Device ID Table
*
@@ -167,9 +167,11 @@ static void i40evf_tx_timeout(struct net_device *netdev)
struct i40evf_adapter *adapter = netdev_priv(netdev);
adapter->tx_timeout_count++;
-
- /* Do the reset outside of interrupt context */
- schedule_work(&adapter->reset_task);
+ dev_info(&adapter->pdev->dev, "TX timeout detected.\n");
+ if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING)) {
+ adapter->flags |= I40EVF_FLAG_RESET_NEEDED;
+ schedule_work(&adapter->reset_task);
+ }
}
/**
@@ -211,6 +213,9 @@ static void i40evf_irq_disable(struct i40evf_adapter *adapter)
int i;
struct i40e_hw *hw = &adapter->hw;
+ if (!adapter->msix_entries)
+ return;
+
for (i = 1; i < adapter->num_msix_vectors; i++) {
wr32(hw, I40E_VFINT_DYN_CTLN1(i - 1), 0);
synchronize_irq(adapter->msix_entries[i].vector);
@@ -511,12 +516,14 @@ static int i40evf_request_misc_irq(struct i40evf_adapter *adapter)
struct net_device *netdev = adapter->netdev;
int err;
- sprintf(adapter->name[0], "i40evf:mbx");
+ sprintf(adapter->misc_vector_name, "i40evf:mbx");
err = request_irq(adapter->msix_entries[0].vector,
- &i40evf_msix_aq, 0, adapter->name[0], netdev);
+ &i40evf_msix_aq, 0,
+ adapter->misc_vector_name, netdev);
if (err) {
dev_err(&adapter->pdev->dev,
- "request_irq for msix_aq failed: %d\n", err);
+ "request_irq for %s failed: %d\n",
+ adapter->misc_vector_name, err);
free_irq(adapter->msix_entries[0].vector, netdev);
}
return err;
@@ -963,16 +970,23 @@ void i40evf_down(struct i40evf_adapter *adapter)
struct net_device *netdev = adapter->netdev;
struct i40evf_mac_filter *f;
- /* remove all MAC filters from the VSI */
+ /* remove all MAC filters */
list_for_each_entry(f, &adapter->mac_filter_list, list) {
f->remove = true;
}
- adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER;
- /* disable receives */
- adapter->aq_required |= I40EVF_FLAG_AQ_DISABLE_QUEUES;
- mod_timer_pending(&adapter->watchdog_timer, jiffies + 1);
- msleep(20);
-
+ /* remove all VLAN filters */
+ list_for_each_entry(f, &adapter->vlan_filter_list, list) {
+ f->remove = true;
+ }
+ if (!(adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) &&
+ adapter->state != __I40EVF_RESETTING) {
+ adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER;
+ adapter->aq_required |= I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
+ /* disable receives */
+ adapter->aq_required |= I40EVF_FLAG_AQ_DISABLE_QUEUES;
+ mod_timer_pending(&adapter->watchdog_timer, jiffies + 1);
+ msleep(20);
+ }
netif_tx_disable(netdev);
netif_tx_stop_all_queues(netdev);
@@ -1124,8 +1138,8 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter)
* than CPU's. So let's be conservative and only ask for
* (roughly) twice the number of vectors as there are CPU's.
*/
- v_budget = min(pairs, (int)(num_online_cpus() * 2)) + NONQ_VECS;
- v_budget = min(v_budget, (int)adapter->vf_res->max_vectors + 1);
+ v_budget = min_t(int, pairs, (int)(num_online_cpus() * 2)) + NONQ_VECS;
+ v_budget = min_t(int, v_budget, (int)adapter->vf_res->max_vectors);
/* A failure in MSI-X entry allocation isn't fatal, but it does
* mean we disable MSI-X capabilities of the adapter.
@@ -1291,19 +1305,47 @@ static void i40evf_watchdog_task(struct work_struct *work)
watchdog_task);
struct i40e_hw *hw = &adapter->hw;
- if (adapter->state < __I40EVF_DOWN)
+ if (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section))
+ goto restart_watchdog;
+
+ if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) {
+ dev_info(&adapter->pdev->dev, "Checking for redemption\n");
+ if ((rd32(hw, I40E_VFGEN_RSTAT) & 0x3) == I40E_VFR_VFACTIVE) {
+ /* A chance for redemption! */
+ dev_err(&adapter->pdev->dev, "Hardware came out of reset. Attempting reinit.\n");
+ adapter->state = __I40EVF_STARTUP;
+ adapter->flags &= ~I40EVF_FLAG_PF_COMMS_FAILED;
+ schedule_delayed_work(&adapter->init_task, 10);
+ clear_bit(__I40EVF_IN_CRITICAL_TASK,
+ &adapter->crit_section);
+ /* Don't reschedule the watchdog, since we've restarted
+ * the init task. When init_task contacts the PF and
+ * gets everything set up again, it'll restart the
+ * watchdog for us. Down, boy. Sit. Stay. Woof.
+ */
+ return;
+ }
+ adapter->aq_pending = 0;
+ adapter->aq_required = 0;
+ adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
goto watchdog_done;
+ }
- if (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section))
+ if ((adapter->state < __I40EVF_DOWN) ||
+ (adapter->flags & I40EVF_FLAG_RESET_PENDING))
goto watchdog_done;
- /* check for unannounced reset */
- if ((adapter->state != __I40EVF_RESETTING) &&
+ /* check for reset */
+ if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING) &&
(rd32(hw, I40E_VFGEN_RSTAT) & 0x3) != I40E_VFR_VFACTIVE) {
adapter->state = __I40EVF_RESETTING;
+ adapter->flags |= I40EVF_FLAG_RESET_PENDING;
+ dev_err(&adapter->pdev->dev, "Hardware reset detected.\n");
+ dev_info(&adapter->pdev->dev, "Scheduling reset task\n");
schedule_work(&adapter->reset_task);
- dev_info(&adapter->pdev->dev, "%s: hardware reset detected\n",
- __func__);
+ adapter->aq_pending = 0;
+ adapter->aq_required = 0;
+ adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
goto watchdog_done;
}
@@ -1358,16 +1400,25 @@ static void i40evf_watchdog_task(struct work_struct *work)
i40evf_irq_enable(adapter, true);
i40evf_fire_sw_int(adapter, 0xFF);
+
watchdog_done:
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
+restart_watchdog:
if (adapter->aq_required)
mod_timer(&adapter->watchdog_timer,
jiffies + msecs_to_jiffies(20));
else
mod_timer(&adapter->watchdog_timer, jiffies + (HZ * 2));
- clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
schedule_work(&adapter->adminq_task);
}
+static int next_queue(struct i40evf_adapter *adapter, int j)
+{
+ j += 1;
+
+ return j >= adapter->vsi_res->num_queue_pairs ? 0 : j;
+}
+
/**
* i40evf_configure_rss - Prepare for RSS if used
* @adapter: board private structure
@@ -1398,19 +1449,19 @@ static void i40evf_configure_rss(struct i40evf_adapter *adapter)
wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32));
/* Populate the LUT with max no. of queues in round robin fashion */
- for (i = 0, j = 0; i < I40E_VFQF_HLUT_MAX_INDEX; i++, j++) {
- if (j == adapter->vsi_res->num_queue_pairs)
- j = 0;
- /* lut = 4-byte sliding window of 4 lut entries */
- lut = (lut << 8) | (j &
- ((0x1 << 8) - 1));
- /* On i = 3, we have 4 entries in lut; write to the register */
- if ((i & 3) == 3)
- wr32(hw, I40E_VFQF_HLUT(i >> 2), lut);
+ j = adapter->vsi_res->num_queue_pairs;
+ for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) {
+ lut = next_queue(adapter, j);
+ lut |= next_queue(adapter, j) << 8;
+ lut |= next_queue(adapter, j) << 16;
+ lut |= next_queue(adapter, j) << 24;
+ wr32(hw, I40E_VFQF_HLUT(i), lut);
}
i40e_flush(hw);
}
+#define I40EVF_RESET_WAIT_MS 100
+#define I40EVF_RESET_WAIT_COUNT 200
/**
* i40evf_reset_task - Call-back task to handle hardware reset
* @work: pointer to work_struct
@@ -1421,8 +1472,9 @@ static void i40evf_configure_rss(struct i40evf_adapter *adapter)
**/
static void i40evf_reset_task(struct work_struct *work)
{
- struct i40evf_adapter *adapter =
- container_of(work, struct i40evf_adapter, reset_task);
+ struct i40evf_adapter *adapter = container_of(work,
+ struct i40evf_adapter,
+ reset_task);
struct i40e_hw *hw = &adapter->hw;
int i = 0, err;
uint32_t rstat_val;
@@ -1431,21 +1483,61 @@ static void i40evf_reset_task(struct work_struct *work)
&adapter->crit_section))
udelay(500);
- /* wait until the reset is complete */
- for (i = 0; i < 20; i++) {
+ if (adapter->flags & I40EVF_FLAG_RESET_NEEDED) {
+ dev_info(&adapter->pdev->dev, "Requesting reset from PF\n");
+ i40evf_request_reset(adapter);
+ }
+
+ /* poll until we see the reset actually happen */
+ for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) {
rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
I40E_VFGEN_RSTAT_VFR_STATE_MASK;
- if (rstat_val == I40E_VFR_COMPLETED)
+ if (rstat_val != I40E_VFR_VFACTIVE) {
+ dev_info(&adapter->pdev->dev, "Reset now occurring\n");
break;
- else
- mdelay(100);
+ } else {
+ msleep(I40EVF_RESET_WAIT_MS);
+ }
+ }
+ if (i == I40EVF_RESET_WAIT_COUNT) {
+ dev_err(&adapter->pdev->dev, "Reset was not detected\n");
+ adapter->flags &= ~I40EVF_FLAG_RESET_PENDING;
+ goto continue_reset; /* act like the reset happened */
+ }
+
+ /* wait until the reset is complete and the PF is responding to us */
+ for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) {
+ rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
+ I40E_VFGEN_RSTAT_VFR_STATE_MASK;
+ if (rstat_val == I40E_VFR_VFACTIVE) {
+ dev_info(&adapter->pdev->dev, "Reset is complete. Reinitializing.\n");
+ break;
+ } else {
+ msleep(I40EVF_RESET_WAIT_MS);
+ }
}
- if (i == 20) {
+ if (i == I40EVF_RESET_WAIT_COUNT) {
/* reset never finished */
- dev_info(&adapter->pdev->dev, "%s: reset never finished: %x\n",
- __func__, rstat_val);
- /* carry on anyway */
+ dev_err(&adapter->pdev->dev, "Reset never finished (%x). PF driver is dead, and so am I.\n",
+ rstat_val);
+ adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED;
+
+ if (netif_running(adapter->netdev))
+ i40evf_close(adapter->netdev);
+
+ i40evf_free_misc_irq(adapter);
+ i40evf_reset_interrupt_capability(adapter);
+ i40evf_free_queues(adapter);
+ kfree(adapter->vf_res);
+ i40evf_shutdown_adminq(hw);
+ adapter->netdev->flags &= ~IFF_UP;
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
+ return; /* Do not attempt to reinit. It's dead, Jim. */
}
+
+continue_reset:
+ adapter->flags &= ~I40EVF_FLAG_RESET_PENDING;
+
i40evf_down(adapter);
adapter->state = __I40EVF_RESETTING;
@@ -1505,6 +1597,9 @@ static void i40evf_adminq_task(struct work_struct *work)
i40e_status ret;
u16 pending;
+ if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED)
+ return;
+
event.msg_size = I40EVF_MAX_AQ_BUF_SIZE;
event.msg_buf = kzalloc(event.msg_size, GFP_KERNEL);
if (!event.msg_buf) {
@@ -1636,6 +1731,10 @@ static int i40evf_open(struct net_device *netdev)
struct i40evf_adapter *adapter = netdev_priv(netdev);
int err;
+ if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) {
+ dev_err(&adapter->pdev->dev, "Unable to open device due to PF driver failure.\n");
+ return -EIO;
+ }
if (adapter->state != __I40EVF_DOWN)
return -EBUSY;
@@ -1690,8 +1789,12 @@ static int i40evf_close(struct net_device *netdev)
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
+ if (adapter->state <= __I40EVF_DOWN)
+ return 0;
+
/* signal that we are down to the interrupt handler */
adapter->state = __I40EVF_DOWN;
+
set_bit(__I40E_DOWN, &adapter->vsi.state);
i40evf_down(adapter);
@@ -1842,16 +1945,18 @@ static void i40evf_init_task(struct work_struct *work)
switch (adapter->state) {
case __I40EVF_STARTUP:
/* driver loaded, probe complete */
+ adapter->flags &= ~I40EVF_FLAG_PF_COMMS_FAILED;
+ adapter->flags &= ~I40EVF_FLAG_RESET_PENDING;
err = i40e_set_mac_type(hw);
if (err) {
- dev_info(&pdev->dev, "%s: set_mac_type failed: %d\n",
- __func__, err);
+ dev_err(&pdev->dev, "Failed to set MAC type (%d)\n",
+ err);
goto err;
}
err = i40evf_check_reset_complete(hw);
if (err) {
- dev_info(&pdev->dev, "%s: device is still in reset (%d).\n",
- __func__, err);
+ dev_err(&pdev->dev, "Device is still in reset (%d)\n",
+ err);
goto err;
}
hw->aq.num_arq_entries = I40EVF_AQ_LEN;
@@ -1861,14 +1966,13 @@ static void i40evf_init_task(struct work_struct *work)
err = i40evf_init_adminq(hw);
if (err) {
- dev_info(&pdev->dev, "%s: init_adminq failed: %d\n",
- __func__, err);
+ dev_err(&pdev->dev, "Failed to init Admin Queue (%d)\n",
+ err);
goto err;
}
err = i40evf_send_api_ver(adapter);
if (err) {
- dev_info(&pdev->dev, "%s: unable to send to PF (%d)\n",
- __func__, err);
+ dev_err(&pdev->dev, "Unable to send to PF (%d)\n", err);
i40evf_shutdown_adminq(hw);
goto err;
}
@@ -1876,19 +1980,21 @@ static void i40evf_init_task(struct work_struct *work)
goto restart;
break;
case __I40EVF_INIT_VERSION_CHECK:
- if (!i40evf_asq_done(hw))
+ if (!i40evf_asq_done(hw)) {
+ dev_err(&pdev->dev, "Admin queue command never completed.\n");
goto err;
+ }
/* aq msg sent, awaiting reply */
err = i40evf_verify_api_ver(adapter);
if (err) {
- dev_err(&pdev->dev, "Unable to verify API version, error %d\n",
+ dev_err(&pdev->dev, "Unable to verify API version (%d)\n",
err);
goto err;
}
err = i40evf_send_vf_config_msg(adapter);
if (err) {
- dev_err(&pdev->dev, "Unable send config request, error %d\n",
+ dev_err(&pdev->dev, "Unable send config request (%d)\n",
err);
goto err;
}
@@ -1902,18 +2008,15 @@ static void i40evf_init_task(struct work_struct *work)
(I40E_MAX_VF_VSI *
sizeof(struct i40e_virtchnl_vsi_resource));
adapter->vf_res = kzalloc(bufsz, GFP_KERNEL);
- if (!adapter->vf_res) {
- dev_err(&pdev->dev, "%s: unable to allocate memory\n",
- __func__);
+ if (!adapter->vf_res)
goto err;
- }
}
err = i40evf_get_vf_config(adapter);
if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK)
goto restart;
if (err) {
- dev_info(&pdev->dev, "%s: unable to get VF config (%d)\n",
- __func__, err);
+ dev_err(&pdev->dev, "Unable to get VF config (%d)\n",
+ err);
goto err_alloc;
}
adapter->state = __I40EVF_INIT_SW;
@@ -1927,25 +2030,23 @@ static void i40evf_init_task(struct work_struct *work)
adapter->vsi_res = &adapter->vf_res->vsi_res[i];
}
if (!adapter->vsi_res) {
- dev_info(&pdev->dev, "%s: no LAN VSI found\n", __func__);
+ dev_err(&pdev->dev, "No LAN VSI found\n");
goto err_alloc;
}
adapter->flags |= I40EVF_FLAG_RX_CSUM_ENABLED;
- adapter->txd_count = I40EVF_DEFAULT_TXD;
- adapter->rxd_count = I40EVF_DEFAULT_RXD;
-
netdev->netdev_ops = &i40evf_netdev_ops;
i40evf_set_ethtool_ops(netdev);
netdev->watchdog_timeo = 5 * HZ;
-
- netdev->features |= NETIF_F_SG |
+ netdev->features |= NETIF_F_HIGHDMA |
+ NETIF_F_SG |
NETIF_F_IP_CSUM |
NETIF_F_SCTP_CSUM |
NETIF_F_IPV6_CSUM |
NETIF_F_TSO |
NETIF_F_TSO6 |
+ NETIF_F_RXCSUM |
NETIF_F_GRO;
if (adapter->vf_res->vf_offload_flags
@@ -1956,11 +2057,13 @@ static void i40evf_init_task(struct work_struct *work)
NETIF_F_HW_VLAN_CTAG_FILTER;
}
- /* The HW MAC address was set and/or determined in sw_init */
+ /* copy netdev features into list of user selectable features */
+ netdev->hw_features |= netdev->features;
+ netdev->hw_features &= ~NETIF_F_RXCSUM;
+
if (!is_valid_ether_addr(adapter->hw.mac.addr)) {
- dev_info(&pdev->dev,
- "Invalid MAC address %pMAC, using random\n",
- adapter->hw.mac.addr);
+ dev_info(&pdev->dev, "Invalid MAC address %pMAC, using random\n",
+ adapter->hw.mac.addr);
random_ether_addr(adapter->hw.mac.addr);
}
memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len);
@@ -1994,8 +2097,6 @@ static void i40evf_init_task(struct work_struct *work)
netif_carrier_off(netdev);
- strcpy(netdev->name, "eth%d");
-
adapter->vsi.id = adapter->vsi_res->vsi_id;
adapter->vsi.seid = adapter->vsi_res->vsi_id; /* dummy */
adapter->vsi.back = adapter;
@@ -2005,9 +2106,11 @@ static void i40evf_init_task(struct work_struct *work)
adapter->vsi.tx_itr_setting = I40E_ITR_DYNAMIC;
adapter->vsi.netdev = adapter->netdev;
- err = register_netdev(netdev);
- if (err)
- goto err_register;
+ if (!adapter->netdev_registered) {
+ err = register_netdev(netdev);
+ if (err)
+ goto err_register;
+ }
adapter->netdev_registered = true;
@@ -2031,7 +2134,6 @@ err_register:
i40evf_free_misc_irq(adapter);
err_sw_init:
i40evf_reset_interrupt_capability(adapter);
- adapter->state = __I40EVF_FAILED;
err_alloc:
kfree(adapter->vf_res);
adapter->vf_res = NULL;
@@ -2039,9 +2141,7 @@ err:
/* Things went into the weeds, so try again later */
if (++adapter->aq_wait_count > I40EVF_AQ_MAX_ERR) {
dev_err(&pdev->dev, "Failed to communicate with PF; giving up.\n");
- if (hw->aq.asq.count)
- i40evf_shutdown_adminq(hw); /* ignore error */
- adapter->state = __I40EVF_FAILED;
+ adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED;
return; /* do not reschedule */
}
schedule_delayed_work(&adapter->init_task, HZ * 3);
@@ -2084,26 +2184,20 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
struct net_device *netdev;
struct i40evf_adapter *adapter = NULL;
struct i40e_hw *hw = NULL;
- int err, pci_using_dac;
+ int err;
err = pci_enable_device(pdev);
if (err)
return err;
- if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) {
- pci_using_dac = true;
- /* coherent mask for the same size will always succeed if
- * dma_set_mask does
- */
- dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
- } else if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
- pci_using_dac = false;
- dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
- } else {
- dev_err(&pdev->dev, "%s: DMA configuration failed: %d\n",
- __func__, err);
- err = -EIO;
- goto err_dma;
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (err) {
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err) {
+ dev_err(&pdev->dev,
+ "DMA configuration failed: 0x%x\n", err);
+ goto err_dma;
+ }
}
err = pci_request_regions(pdev, i40evf_driver_name);
@@ -2128,8 +2222,6 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_drvdata(pdev, netdev);
adapter = netdev_priv(netdev);
- if (pci_using_dac)
- netdev->features |= NETIF_F_HIGHDMA;
adapter->netdev = netdev;
adapter->pdev = pdev;
@@ -2271,6 +2363,7 @@ static void i40evf_remove(struct pci_dev *pdev)
struct i40e_hw *hw = &adapter->hw;
cancel_delayed_work_sync(&adapter->init_task);
+ cancel_work_sync(&adapter->reset_task);
if (adapter->netdev_registered) {
unregister_netdev(netdev);
@@ -2278,17 +2371,15 @@ static void i40evf_remove(struct pci_dev *pdev)
}
adapter->state = __I40EVF_REMOVE;
- if (adapter->num_msix_vectors) {
+ if (adapter->msix_entries) {
i40evf_misc_irq_disable(adapter);
- del_timer_sync(&adapter->watchdog_timer);
-
- flush_scheduled_work();
-
i40evf_free_misc_irq(adapter);
-
i40evf_reset_interrupt_capability(adapter);
}
+ del_timer_sync(&adapter->watchdog_timer);
+ flush_scheduled_work();
+
if (hw->aq.asq.count)
i40evf_shutdown_adminq(hw);
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
index e6978d79e62b..e294f012647d 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -43,6 +43,9 @@ static int i40evf_send_pf_msg(struct i40evf_adapter *adapter,
struct i40e_hw *hw = &adapter->hw;
i40e_status err;
+ if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED)
+ return 0; /* nothing to see here, move along */
+
err = i40e_aq_send_msg_to_pf(hw, op, 0, msg, len, NULL);
if (err)
dev_err(&adapter->pdev->dev, "Unable to send opcode %d to PF, error %d, aq status %d\n",
@@ -651,6 +654,18 @@ void i40evf_request_stats(struct i40evf_adapter *adapter)
/* if the request failed, don't lock out others */
adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
}
+/**
+ * i40evf_request_reset
+ * @adapter: adapter structure
+ *
+ * Request that the PF reset this VF. No response is expected.
+ **/
+void i40evf_request_reset(struct i40evf_adapter *adapter)
+{
+ /* Don't check CURRENT_OP - this is always higher priority */
+ i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_RESET_VF, NULL, 0);
+ adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
+}
/**
* i40evf_virtchnl_completion
@@ -689,10 +704,12 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
}
break;
case I40E_VIRTCHNL_EVENT_RESET_IMPENDING:
- adapter->state = __I40EVF_RESETTING;
- schedule_work(&adapter->reset_task);
- dev_info(&adapter->pdev->dev,
- "%s: hardware reset pending\n", __func__);
+ dev_info(&adapter->pdev->dev, "PF reset warning received\n");
+ if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING)) {
+ adapter->flags |= I40EVF_FLAG_RESET_PENDING;
+ dev_info(&adapter->pdev->dev, "Scheduling reset task\n");
+ schedule_work(&adapter->reset_task);
+ }
break;
default:
dev_err(&adapter->pdev->dev,
diff --git a/drivers/net/ethernet/intel/igb/Makefile b/drivers/net/ethernet/intel/igb/Makefile
index f19700e285bb..5bcb2de75933 100644
--- a/drivers/net/ethernet/intel/igb/Makefile
+++ b/drivers/net/ethernet/intel/igb/Makefile
@@ -1,7 +1,7 @@
################################################################################
#
# Intel 82575 PCI-Express Ethernet Linux driver
-# Copyright(c) 1999 - 2013 Intel Corporation.
+# Copyright(c) 1999 - 2014 Intel Corporation.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
# more details.
#
# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+# this program; if not, see <http://www.gnu.org/licenses/>.
#
# The full GNU General Public License is included in this distribution in
# the file called "COPYING".
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c
index 06df6928f44c..fa36fe12e775 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.c
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
@@ -77,8 +76,6 @@ static s32 igb_update_nvm_checksum_i350(struct e1000_hw *hw);
static const u16 e1000_82580_rxpbs_table[] =
{ 36, 72, 144, 1, 2, 4, 8, 16,
35, 70, 140 };
-#define E1000_82580_RXPBS_TABLE_SIZE \
- (sizeof(e1000_82580_rxpbs_table)/sizeof(u16))
/**
* igb_sgmii_uses_mdio_82575 - Determine if I2C pins are for external MDIO
@@ -2308,7 +2305,7 @@ u16 igb_rxpbs_adjust_82580(u32 data)
{
u16 ret_val = 0;
- if (data < E1000_82580_RXPBS_TABLE_SIZE)
+ if (data < ARRAY_SIZE(e1000_82580_rxpbs_table))
ret_val = e1000_82580_rxpbs_table[data];
return ret_val;
@@ -2714,13 +2711,14 @@ static const u8 e1000_emc_therm_limit[4] = {
E1000_EMC_DIODE3_THERM_LIMIT
};
+#ifdef CONFIG_IGB_HWMON
/**
* igb_get_thermal_sensor_data_generic - Gathers thermal sensor data
* @hw: pointer to hardware structure
*
* Updates the temperatures in mac.thermal_sensor_data
**/
-s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
+static s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
{
s32 status = E1000_SUCCESS;
u16 ets_offset;
@@ -2774,7 +2772,7 @@ s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
* Sets the thermal sensor thresholds according to the NVM map
* and save off the threshold and location values into mac.thermal_sensor_data
**/
-s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *hw)
+static s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *hw)
{
s32 status = E1000_SUCCESS;
u16 ets_offset;
@@ -2836,6 +2834,7 @@ s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *hw)
return status;
}
+#endif
static struct e1000_mac_operations e1000_mac_ops_82575 = {
.init_hw = igb_init_hw_82575,
.check_for_link = igb_check_for_link_82575,
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.h b/drivers/net/ethernet/intel/igb/e1000_82575.h
index 8c2437722aad..09d78be72416 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.h
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
@@ -231,6 +230,10 @@ struct e1000_adv_tx_context_desc {
#define E1000_VMOLR_STRVLAN 0x40000000 /* Vlan stripping enable */
#define E1000_VMOLR_STRCRC 0x80000000 /* CRC stripping enable */
+#define E1000_DVMOLR_HIDEVLAN 0x20000000 /* Hide vlan enable */
+#define E1000_DVMOLR_STRVLAN 0x40000000 /* Vlan stripping enable */
+#define E1000_DVMOLR_STRCRC 0x80000000 /* CRC stripping enable */
+
#define E1000_VLVF_ARRAY_SIZE 32
#define E1000_VLVF_VLANID_MASK 0x00000FFF
#define E1000_VLVF_POOLSEL_SHIFT 12
@@ -266,8 +269,7 @@ u16 igb_rxpbs_adjust_82580(u32 data);
s32 igb_read_emi_reg(struct e1000_hw *, u16 addr, u16 *data);
s32 igb_set_eee_i350(struct e1000_hw *);
s32 igb_set_eee_i354(struct e1000_hw *);
-s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *);
-s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw);
+s32 igb_get_eee_status_i354(struct e1000_hw *hw, bool *status);
#define E1000_I2C_THERMAL_SENSOR_ADDR 0xF8
#define E1000_EMC_INTERNAL_DATA 0x00
diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h
index 0571b973be80..b05bf925ac72 100644
--- a/drivers/net/ethernet/intel/igb/e1000_defines.h
+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
@@ -44,7 +43,11 @@
#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */
/* Extended Device Control */
+#define E1000_CTRL_EXT_SDP2_DATA 0x00000040 /* Value of SW Defineable Pin 2 */
#define E1000_CTRL_EXT_SDP3_DATA 0x00000080 /* Value of SW Defineable Pin 3 */
+#define E1000_CTRL_EXT_SDP2_DIR 0x00000400 /* SDP2 Data direction */
+#define E1000_CTRL_EXT_SDP3_DIR 0x00000800 /* SDP3 Data direction */
+
/* Physical Func Reset Done Indication */
#define E1000_CTRL_EXT_PFRSTD 0x00004000
#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000
@@ -191,7 +194,8 @@
/* enable link status from external LINK_0 and LINK_1 pins */
#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */
#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */
-#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */
+#define E1000_CTRL_SDP0_DIR 0x00400000 /* SDP0 Data direction */
+#define E1000_CTRL_SDP1_DIR 0x00800000 /* SDP1 Data direction */
#define E1000_CTRL_RST 0x04000000 /* Global reset */
#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */
#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */
@@ -529,8 +533,67 @@
#define E1000_TIMINCA_16NS_SHIFT 24
-#define E1000_TSICR_TXTS 0x00000002
-#define E1000_TSIM_TXTS 0x00000002
+/* Time Sync Interrupt Cause/Mask Register Bits */
+
+#define TSINTR_SYS_WRAP (1 << 0) /* SYSTIM Wrap around. */
+#define TSINTR_TXTS (1 << 1) /* Transmit Timestamp. */
+#define TSINTR_RXTS (1 << 2) /* Receive Timestamp. */
+#define TSINTR_TT0 (1 << 3) /* Target Time 0 Trigger. */
+#define TSINTR_TT1 (1 << 4) /* Target Time 1 Trigger. */
+#define TSINTR_AUTT0 (1 << 5) /* Auxiliary Timestamp 0 Taken. */
+#define TSINTR_AUTT1 (1 << 6) /* Auxiliary Timestamp 1 Taken. */
+#define TSINTR_TADJ (1 << 7) /* Time Adjust Done. */
+
+#define TSYNC_INTERRUPTS TSINTR_TXTS
+#define E1000_TSICR_TXTS TSINTR_TXTS
+
+/* TSAUXC Configuration Bits */
+#define TSAUXC_EN_TT0 (1 << 0) /* Enable target time 0. */
+#define TSAUXC_EN_TT1 (1 << 1) /* Enable target time 1. */
+#define TSAUXC_EN_CLK0 (1 << 2) /* Enable Configurable Frequency Clock 0. */
+#define TSAUXC_SAMP_AUT0 (1 << 3) /* Latch SYSTIML/H into AUXSTMPL/0. */
+#define TSAUXC_ST0 (1 << 4) /* Start Clock 0 Toggle on Target Time 0. */
+#define TSAUXC_EN_CLK1 (1 << 5) /* Enable Configurable Frequency Clock 1. */
+#define TSAUXC_SAMP_AUT1 (1 << 6) /* Latch SYSTIML/H into AUXSTMPL/1. */
+#define TSAUXC_ST1 (1 << 7) /* Start Clock 1 Toggle on Target Time 1. */
+#define TSAUXC_EN_TS0 (1 << 8) /* Enable hardware timestamp 0. */
+#define TSAUXC_AUTT0 (1 << 9) /* Auxiliary Timestamp Taken. */
+#define TSAUXC_EN_TS1 (1 << 10) /* Enable hardware timestamp 0. */
+#define TSAUXC_AUTT1 (1 << 11) /* Auxiliary Timestamp Taken. */
+#define TSAUXC_PLSG (1 << 17) /* Generate a pulse. */
+#define TSAUXC_DISABLE (1 << 31) /* Disable SYSTIM Count Operation. */
+
+/* SDP Configuration Bits */
+#define AUX0_SEL_SDP0 (0 << 0) /* Assign SDP0 to auxiliary time stamp 0. */
+#define AUX0_SEL_SDP1 (1 << 0) /* Assign SDP1 to auxiliary time stamp 0. */
+#define AUX0_SEL_SDP2 (2 << 0) /* Assign SDP2 to auxiliary time stamp 0. */
+#define AUX0_SEL_SDP3 (3 << 0) /* Assign SDP3 to auxiliary time stamp 0. */
+#define AUX0_TS_SDP_EN (1 << 2) /* Enable auxiliary time stamp trigger 0. */
+#define AUX1_SEL_SDP0 (0 << 3) /* Assign SDP0 to auxiliary time stamp 1. */
+#define AUX1_SEL_SDP1 (1 << 3) /* Assign SDP1 to auxiliary time stamp 1. */
+#define AUX1_SEL_SDP2 (2 << 3) /* Assign SDP2 to auxiliary time stamp 1. */
+#define AUX1_SEL_SDP3 (3 << 3) /* Assign SDP3 to auxiliary time stamp 1. */
+#define AUX1_TS_SDP_EN (1 << 5) /* Enable auxiliary time stamp trigger 1. */
+#define TS_SDP0_SEL_TT0 (0 << 6) /* Target time 0 is output on SDP0. */
+#define TS_SDP0_SEL_TT1 (1 << 6) /* Target time 1 is output on SDP0. */
+#define TS_SDP0_SEL_FC0 (2 << 6) /* Freq clock 0 is output on SDP0. */
+#define TS_SDP0_SEL_FC1 (3 << 6) /* Freq clock 1 is output on SDP0. */
+#define TS_SDP0_EN (1 << 8) /* SDP0 is assigned to Tsync. */
+#define TS_SDP1_SEL_TT0 (0 << 9) /* Target time 0 is output on SDP1. */
+#define TS_SDP1_SEL_TT1 (1 << 9) /* Target time 1 is output on SDP1. */
+#define TS_SDP1_SEL_FC0 (2 << 9) /* Freq clock 0 is output on SDP1. */
+#define TS_SDP1_SEL_FC1 (3 << 9) /* Freq clock 1 is output on SDP1. */
+#define TS_SDP1_EN (1 << 11) /* SDP1 is assigned to Tsync. */
+#define TS_SDP2_SEL_TT0 (0 << 12) /* Target time 0 is output on SDP2. */
+#define TS_SDP2_SEL_TT1 (1 << 12) /* Target time 1 is output on SDP2. */
+#define TS_SDP2_SEL_FC0 (2 << 12) /* Freq clock 0 is output on SDP2. */
+#define TS_SDP2_SEL_FC1 (3 << 12) /* Freq clock 1 is output on SDP2. */
+#define TS_SDP2_EN (1 << 14) /* SDP2 is assigned to Tsync. */
+#define TS_SDP3_SEL_TT0 (0 << 15) /* Target time 0 is output on SDP3. */
+#define TS_SDP3_SEL_TT1 (1 << 15) /* Target time 1 is output on SDP3. */
+#define TS_SDP3_SEL_FC0 (2 << 15) /* Freq clock 0 is output on SDP3. */
+#define TS_SDP3_SEL_FC1 (3 << 15) /* Freq clock 1 is output on SDP3. */
+#define TS_SDP3_EN (1 << 17) /* SDP3 is assigned to Tsync. */
#define E1000_MDICNFG_EXT_MDIO 0x80000000 /* MDI ext/int destination */
#define E1000_MDICNFG_COM_MDIO 0x40000000 /* MDI shared w/ lan 0 */
diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h
index ab99e2b582a8..10741d170f2d 100644
--- a/drivers/net/ethernet/intel/igb/e1000_hw.h
+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.c b/drivers/net/ethernet/intel/igb/e1000_i210.c
index 0c0393316a3a..db963397cc27 100644
--- a/drivers/net/ethernet/intel/igb/e1000_i210.c
+++ b/drivers/net/ethernet/intel/igb/e1000_i210.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
@@ -35,6 +34,8 @@
#include "e1000_hw.h"
#include "e1000_i210.h"
+static s32 igb_update_flash_i210(struct e1000_hw *hw);
+
/**
* igb_get_hw_semaphore_i210 - Acquire hardware semaphore
* @hw: pointer to the HW structure
@@ -111,7 +112,7 @@ static s32 igb_get_hw_semaphore_i210(struct e1000_hw *hw)
* Return successful if access grant bit set, else clear the request for
* EEPROM access and return -E1000_ERR_NVM (-1).
**/
-s32 igb_acquire_nvm_i210(struct e1000_hw *hw)
+static s32 igb_acquire_nvm_i210(struct e1000_hw *hw)
{
return igb_acquire_swfw_sync_i210(hw, E1000_SWFW_EEP_SM);
}
@@ -123,7 +124,7 @@ s32 igb_acquire_nvm_i210(struct e1000_hw *hw)
* Stop any current commands to the EEPROM and clear the EEPROM request bit,
* then release the semaphores acquired.
**/
-void igb_release_nvm_i210(struct e1000_hw *hw)
+static void igb_release_nvm_i210(struct e1000_hw *hw)
{
igb_release_swfw_sync_i210(hw, E1000_SWFW_EEP_SM);
}
@@ -206,8 +207,8 @@ void igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
* Reads a 16 bit word from the Shadow Ram using the EERD register.
* Uses necessary synchronization semaphores.
**/
-s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
- u16 *data)
+static s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
+ u16 *data)
{
s32 status = E1000_SUCCESS;
u16 i, count;
@@ -306,8 +307,8 @@ out:
* If error code is returned, data and Shadow RAM may be inconsistent - buffer
* partially written.
**/
-s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
- u16 *data)
+static s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
+ u16 *data)
{
s32 status = E1000_SUCCESS;
u16 i, count;
@@ -555,7 +556,7 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
* Calculates the EEPROM checksum by reading/adding each word of the EEPROM
* and then verifies that the sum of the EEPROM is equal to 0xBABA.
**/
-s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw)
+static s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw)
{
s32 status = E1000_SUCCESS;
s32 (*read_op_ptr)(struct e1000_hw *, u16, u16, u16 *);
@@ -590,7 +591,7 @@ s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw)
* up to the checksum. Then calculates the EEPROM checksum and writes the
* value to the EEPROM. Next commit EEPROM data onto the Flash.
**/
-s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw)
+static s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw)
{
s32 ret_val = E1000_SUCCESS;
u16 checksum = 0;
@@ -684,7 +685,7 @@ bool igb_get_flash_presence_i210(struct e1000_hw *hw)
* @hw: pointer to the HW structure
*
**/
-s32 igb_update_flash_i210(struct e1000_hw *hw)
+static s32 igb_update_flash_i210(struct e1000_hw *hw)
{
s32 ret_val = E1000_SUCCESS;
u32 flup;
diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.h b/drivers/net/ethernet/intel/igb/e1000_i210.h
index 2d913716573a..907fe99a9813 100644
--- a/drivers/net/ethernet/intel/igb/e1000_i210.h
+++ b/drivers/net/ethernet/intel/igb/e1000_i210.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
@@ -28,17 +27,8 @@
#ifndef _E1000_I210_H_
#define _E1000_I210_H_
-s32 igb_update_flash_i210(struct e1000_hw *hw);
-s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw);
-s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw);
-s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
- u16 *data);
-s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
- u16 *data);
s32 igb_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask);
void igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask);
-s32 igb_acquire_nvm_i210(struct e1000_hw *hw);
-void igb_release_nvm_i210(struct e1000_hw *hw);
s32 igb_valid_led_default_i210(struct e1000_hw *hw, u16 *data);
s32 igb_read_invm_version(struct e1000_hw *hw,
struct e1000_fw_version *invm_ver);
diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c
index 298f0ed50670..5910a932ea7c 100644
--- a/drivers/net/ethernet/intel/igb/e1000_mac.c
+++ b/drivers/net/ethernet/intel/igb/e1000_mac.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.h b/drivers/net/ethernet/intel/igb/e1000_mac.h
index e4cbe8ef67b3..99299ba8ee3a 100644
--- a/drivers/net/ethernet/intel/igb/e1000_mac.h
+++ b/drivers/net/ethernet/intel/igb/e1000_mac.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.c b/drivers/net/ethernet/intel/igb/e1000_mbx.c
index dac1447fabf7..d5b121771c31 100644
--- a/drivers/net/ethernet/intel/igb/e1000_mbx.c
+++ b/drivers/net/ethernet/intel/igb/e1000_mbx.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.h b/drivers/net/ethernet/intel/igb/e1000_mbx.h
index de9bba41acf3..f52f5515e5a8 100644
--- a/drivers/net/ethernet/intel/igb/e1000_mbx.h
+++ b/drivers/net/ethernet/intel/igb/e1000_mbx.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c
index a7db7f3db914..9abf82919c65 100644
--- a/drivers/net/ethernet/intel/igb/e1000_nvm.c
+++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.h b/drivers/net/ethernet/intel/igb/e1000_nvm.h
index 433b7419cb98..5b101170b17e 100644
--- a/drivers/net/ethernet/intel/igb/e1000_nvm.h
+++ b/drivers/net/ethernet/intel/igb/e1000_nvm.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c
index ad2b74d95138..4009bbab7407 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.c
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
@@ -394,77 +393,6 @@ s32 igb_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data)
}
/**
- * e1000_write_sfp_data_byte - Writes SFP module data.
- * @hw: pointer to the HW structure
- * @offset: byte location offset to write to
- * @data: data to write
- *
- * Writes one byte to SFP module data stored
- * in SFP resided EEPROM memory or SFP diagnostic area.
- * Function should be called with
- * E1000_I2CCMD_SFP_DATA_ADDR(<byte offset>) for SFP module database access
- * E1000_I2CCMD_SFP_DIAG_ADDR(<byte offset>) for SFP diagnostics parameters
- * access
- **/
-s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data)
-{
- u32 i = 0;
- u32 i2ccmd = 0;
- u32 data_local = 0;
-
- if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) {
- hw_dbg("I2CCMD command address exceeds upper limit\n");
- return -E1000_ERR_PHY;
- }
- /* The programming interface is 16 bits wide
- * so we need to read the whole word first
- * then update appropriate byte lane and write
- * the updated word back.
- */
- /* Set up Op-code, EEPROM Address,in the I2CCMD
- * register. The MAC will take care of interfacing
- * with an EEPROM to write the data given.
- */
- i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
- E1000_I2CCMD_OPCODE_READ);
- /* Set a command to read single word */
- wr32(E1000_I2CCMD, i2ccmd);
- for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
- udelay(50);
- /* Poll the ready bit to see if lastly
- * launched I2C operation completed
- */
- i2ccmd = rd32(E1000_I2CCMD);
- if (i2ccmd & E1000_I2CCMD_READY) {
- /* Check if this is READ or WRITE phase */
- if ((i2ccmd & E1000_I2CCMD_OPCODE_READ) ==
- E1000_I2CCMD_OPCODE_READ) {
- /* Write the selected byte
- * lane and update whole word
- */
- data_local = i2ccmd & 0xFF00;
- data_local |= data;
- i2ccmd = ((offset <<
- E1000_I2CCMD_REG_ADDR_SHIFT) |
- E1000_I2CCMD_OPCODE_WRITE | data_local);
- wr32(E1000_I2CCMD, i2ccmd);
- } else {
- break;
- }
- }
- }
- if (!(i2ccmd & E1000_I2CCMD_READY)) {
- hw_dbg("I2CCMD Write did not complete\n");
- return -E1000_ERR_PHY;
- }
- if (i2ccmd & E1000_I2CCMD_ERROR) {
- hw_dbg("I2CCMD Error bit set\n");
- return -E1000_ERR_PHY;
- }
- return 0;
-}
-
-/**
* igb_read_phy_reg_igp - Read igp PHY register
* @hw: pointer to the HW structure
* @offset: register offset to be read
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h
index 6a0873f2095a..4c2c36c46a73 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.h
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
@@ -70,7 +69,6 @@ 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);
s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data);
s32 igb_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data);
-s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data);
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);
diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h
index 82632c6c53af..bdb246e848e1 100644
--- a/drivers/net/ethernet/intel/igb/e1000_regs.h
+++ b/drivers/net/ethernet/intel/igb/e1000_regs.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
@@ -41,6 +40,7 @@
#define E1000_FCT 0x00030 /* Flow Control Type - RW */
#define E1000_CONNSW 0x00034 /* Copper/Fiber switch control - RW */
#define E1000_VET 0x00038 /* VLAN Ether Type - RW */
+#define E1000_TSSDP 0x0003C /* Time Sync SDP Configuration Register - RW */
#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */
#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */
#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */
@@ -102,6 +102,14 @@
#define E1000_SYSTIMH 0x0B604 /* System time register High - RO */
#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */
#define E1000_TSAUXC 0x0B640 /* Timesync Auxiliary Control register */
+#define E1000_TRGTTIML0 0x0B644 /* Target Time Register 0 Low - RW */
+#define E1000_TRGTTIMH0 0x0B648 /* Target Time Register 0 High - RW */
+#define E1000_TRGTTIML1 0x0B64C /* Target Time Register 1 Low - RW */
+#define E1000_TRGTTIMH1 0x0B650 /* Target Time Register 1 High - RW */
+#define E1000_AUXSTMPL0 0x0B65C /* Auxiliary Time Stamp 0 Register Low - RO */
+#define E1000_AUXSTMPH0 0x0B660 /* Auxiliary Time Stamp 0 Register High - RO */
+#define E1000_AUXSTMPL1 0x0B664 /* Auxiliary Time Stamp 1 Register Low - RO */
+#define E1000_AUXSTMPH1 0x0B668 /* Auxiliary Time Stamp 1 Register High - RO */
#define E1000_SYSTIMR 0x0B6F8 /* System time register Residue */
#define E1000_TSICR 0x0B66C /* Interrupt Cause Register */
#define E1000_TSIM 0x0B674 /* Interrupt Mask Register */
@@ -349,16 +357,30 @@
#define E1000_P2VMAILBOX(_n) (0x00C00 + (4 * (_n)))
#define E1000_VMBMEM(_n) (0x00800 + (64 * (_n)))
#define E1000_VMOLR(_n) (0x05AD0 + (4 * (_n)))
+#define E1000_DVMOLR(_n) (0x0C038 + (64 * (_n)))
#define E1000_VLVF(_n) (0x05D00 + (4 * (_n))) /* VLAN Virtual Machine
* Filter - RW */
#define E1000_VMVIR(_n) (0x03700 + (4 * (_n)))
-#define wr32(reg, value) (writel(value, hw->hw_addr + reg))
-#define rd32(reg) (readl(hw->hw_addr + reg))
+struct e1000_hw;
+
+u32 igb_rd32(struct e1000_hw *hw, u32 reg);
+
+/* write operations, indexed using DWORDS */
+#define wr32(reg, val) \
+do { \
+ u8 __iomem *hw_addr = ACCESS_ONCE((hw)->hw_addr); \
+ if (!E1000_REMOVED(hw_addr)) \
+ writel((val), &hw_addr[(reg)]); \
+} while (0)
+
+#define rd32(reg) (igb_rd32(hw, reg))
+
#define wrfl() ((void)rd32(E1000_STATUS))
#define array_wr32(reg, offset, value) \
- (writel(value, hw->hw_addr + reg + ((offset) << 2)))
+ wr32((reg) + ((offset) << 2), (value))
+
#define array_rd32(reg, offset) \
(readl(hw->hw_addr + reg + ((offset) << 2)))
@@ -397,4 +419,6 @@
#define E1000_INVM_DATA_REG(_n) (0x12120 + 4*(_n))
#define E1000_INVM_SIZE 64 /* Number of INVM Data Registers */
+#define E1000_REMOVED(h) unlikely(!(h))
+
#endif
diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
index ccf472f073dd..7fbe1e925143 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
@@ -42,6 +41,7 @@
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/pci.h>
+#include <linux/mdio.h>
struct igb_adapter;
@@ -434,6 +434,7 @@ struct igb_adapter {
struct delayed_work ptp_overflow_work;
struct work_struct ptp_tx_work;
struct sk_buff *ptp_tx_skb;
+ struct hwtstamp_config tstamp_config;
unsigned long ptp_tx_start;
unsigned long last_rx_ptp_check;
spinlock_t tmreg_lock;
@@ -456,6 +457,7 @@ struct igb_adapter {
unsigned long link_check_timeout;
int copper_tries;
struct e1000_info ei;
+ u16 eee_advert;
};
#define IGB_FLAG_HAS_MSI (1 << 0)
@@ -472,6 +474,7 @@ struct igb_adapter {
#define IGB_FLAG_MAS_CAPABLE (1 << 11)
#define IGB_FLAG_MAS_ENABLE (1 << 12)
#define IGB_FLAG_HAS_MSIX (1 << 13)
+#define IGB_FLAG_EEE (1 << 14)
/* Media Auto Sense */
#define IGB_MAS_ENABLE_0 0X0001
@@ -489,7 +492,8 @@ struct igb_adapter {
enum e1000_state_t {
__IGB_TESTING,
__IGB_RESETTING,
- __IGB_DOWN
+ __IGB_DOWN,
+ __IGB_PTP_TX_IN_PROGRESS,
};
enum igb_boards {
@@ -525,9 +529,7 @@ void igb_set_fw_version(struct igb_adapter *);
void igb_ptp_init(struct igb_adapter *adapter);
void igb_ptp_stop(struct igb_adapter *adapter);
void igb_ptp_reset(struct igb_adapter *adapter);
-void igb_ptp_tx_work(struct work_struct *work);
void igb_ptp_rx_hang(struct igb_adapter *adapter);
-void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter);
void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, struct sk_buff *skb);
void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, unsigned char *va,
struct sk_buff *skb);
@@ -545,8 +547,8 @@ static inline void igb_ptp_rx_hwtstamp(struct igb_ring *rx_ring,
rx_ring->last_rx_timestamp = jiffies;
}
-int igb_ptp_hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr,
- int cmd);
+int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr);
+int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr);
#ifdef CONFIG_IGB_HWMON
void igb_sysfs_exit(struct igb_adapter *adapter);
int igb_sysfs_init(struct igb_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index 1df02378de69..e5570acbeea8 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
@@ -2274,15 +2273,15 @@ static void igb_get_ethtool_stats(struct net_device *netdev,
ring = adapter->tx_ring[j];
do {
- start = u64_stats_fetch_begin_bh(&ring->tx_syncp);
+ start = u64_stats_fetch_begin_irq(&ring->tx_syncp);
data[i] = ring->tx_stats.packets;
data[i+1] = ring->tx_stats.bytes;
data[i+2] = ring->tx_stats.restart_queue;
- } while (u64_stats_fetch_retry_bh(&ring->tx_syncp, start));
+ } while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start));
do {
- start = u64_stats_fetch_begin_bh(&ring->tx_syncp2);
+ start = u64_stats_fetch_begin_irq(&ring->tx_syncp2);
restart2 = ring->tx_stats.restart_queue2;
- } while (u64_stats_fetch_retry_bh(&ring->tx_syncp2, start));
+ } while (u64_stats_fetch_retry_irq(&ring->tx_syncp2, start));
data[i+2] += restart2;
i += IGB_TX_QUEUE_STATS_LEN;
@@ -2290,13 +2289,13 @@ static void igb_get_ethtool_stats(struct net_device *netdev,
for (j = 0; j < adapter->num_rx_queues; j++) {
ring = adapter->rx_ring[j];
do {
- start = u64_stats_fetch_begin_bh(&ring->rx_syncp);
+ start = u64_stats_fetch_begin_irq(&ring->rx_syncp);
data[i] = ring->rx_stats.packets;
data[i+1] = ring->rx_stats.bytes;
data[i+2] = ring->rx_stats.drops;
data[i+3] = ring->rx_stats.csum_err;
data[i+4] = ring->rx_stats.alloc_failed;
- } while (u64_stats_fetch_retry_bh(&ring->rx_syncp, start));
+ } while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start));
i += IGB_RX_QUEUE_STATS_LEN;
}
spin_unlock(&adapter->stats64_lock);
@@ -2354,6 +2353,11 @@ static int igb_get_ts_info(struct net_device *dev,
{
struct igb_adapter *adapter = netdev_priv(dev);
+ if (adapter->ptp_clock)
+ info->phc_index = ptp_clock_index(adapter->ptp_clock);
+ else
+ info->phc_index = -1;
+
switch (adapter->hw.mac.type) {
case e1000_82575:
info->so_timestamping =
@@ -2375,11 +2379,6 @@ static int igb_get_ts_info(struct net_device *dev,
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
- if (adapter->ptp_clock)
- info->phc_index = ptp_clock_index(adapter->ptp_clock);
- else
- info->phc_index = -1;
-
info->tx_types =
(1 << HWTSTAMP_TX_OFF) |
(1 << HWTSTAMP_TX_ON);
@@ -2588,7 +2587,7 @@ static int igb_get_eee(struct net_device *netdev, struct ethtool_eee *edata)
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
- u32 ipcnfg, eeer, ret_val;
+ u32 ret_val;
u16 phy_data;
if ((hw->mac.type < e1000_i350) ||
@@ -2597,16 +2596,25 @@ static int igb_get_eee(struct net_device *netdev, struct ethtool_eee *edata)
edata->supported = (SUPPORTED_1000baseT_Full |
SUPPORTED_100baseT_Full);
+ if (!hw->dev_spec._82575.eee_disable)
+ edata->advertised =
+ mmd_eee_adv_to_ethtool_adv_t(adapter->eee_advert);
+
+ /* The IPCNFG and EEER registers are not supported on I354. */
+ if (hw->mac.type == e1000_i354) {
+ igb_get_eee_status_i354(hw, (bool *)&edata->eee_active);
+ } else {
+ u32 eeer;
- ipcnfg = rd32(E1000_IPCNFG);
- eeer = rd32(E1000_EEER);
+ eeer = rd32(E1000_EEER);
- /* EEE status on negotiated link */
- if (ipcnfg & E1000_IPCNFG_EEE_1G_AN)
- edata->advertised = ADVERTISED_1000baseT_Full;
+ /* EEE status on negotiated link */
+ if (eeer & E1000_EEER_EEE_NEG)
+ edata->eee_active = true;
- if (ipcnfg & E1000_IPCNFG_EEE_100M_AN)
- edata->advertised |= ADVERTISED_100baseT_Full;
+ if (eeer & E1000_EEER_TX_LPI_EN)
+ edata->tx_lpi_enabled = true;
+ }
/* EEE Link Partner Advertised */
switch (hw->mac.type) {
@@ -2617,8 +2625,8 @@ static int igb_get_eee(struct net_device *netdev, struct ethtool_eee *edata)
return -ENODATA;
edata->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(phy_data);
-
break;
+ case e1000_i354:
case e1000_i210:
case e1000_i211:
ret_val = igb_read_xmdio_reg(hw, E1000_EEE_LP_ADV_ADDR_I210,
@@ -2634,12 +2642,10 @@ static int igb_get_eee(struct net_device *netdev, struct ethtool_eee *edata)
break;
}
- if (eeer & E1000_EEER_EEE_NEG)
- edata->eee_active = true;
-
edata->eee_enabled = !hw->dev_spec._82575.eee_disable;
- if (eeer & E1000_EEER_TX_LPI_EN)
+ if ((hw->mac.type == e1000_i354) &&
+ (edata->eee_enabled))
edata->tx_lpi_enabled = true;
/* Report correct negotiated EEE status for devices that
@@ -2687,9 +2693,10 @@ static int igb_set_eee(struct net_device *netdev,
return -EINVAL;
}
- if (eee_curr.advertised != edata->advertised) {
+ if (edata->advertised &
+ ~(ADVERTISE_100_FULL | ADVERTISE_1000_FULL)) {
dev_err(&adapter->pdev->dev,
- "Setting EEE Advertisement is not supported\n");
+ "EEE Advertisement supports only 100Tx and or 100T full duplex\n");
return -EINVAL;
}
@@ -2699,9 +2706,14 @@ static int igb_set_eee(struct net_device *netdev,
return -EINVAL;
}
+ adapter->eee_advert = ethtool_adv_to_mmd_eee_adv_t(edata->advertised);
if (hw->dev_spec._82575.eee_disable != !edata->eee_enabled) {
hw->dev_spec._82575.eee_disable = !edata->eee_enabled;
- igb_set_eee_i350(hw);
+ adapter->flags |= IGB_FLAG_EEE;
+ if (hw->mac.type == e1000_i350)
+ igb_set_eee_i350(hw);
+ else
+ igb_set_eee_i354(hw);
/* reset link */
if (netif_running(netdev))
@@ -2779,9 +2791,11 @@ static int igb_get_module_eeprom(struct net_device *netdev,
/* Read EEPROM block, SFF-8079/SFF-8472, word at a time */
for (i = 0; i < last_word - first_word + 1; i++) {
status = igb_read_phy_reg_i2c(hw, first_word + i, &dataword[i]);
- if (status != E1000_SUCCESS)
+ if (status != E1000_SUCCESS) {
/* Error occurred while reading module */
+ kfree(dataword);
return -EIO;
+ }
be16_to_cpus(&dataword[i]);
}
diff --git a/drivers/net/ethernet/intel/igb/igb_hwmon.c b/drivers/net/ethernet/intel/igb/igb_hwmon.c
index e0af5bc61613..8333f67acf96 100644
--- a/drivers/net/ethernet/intel/igb/igb_hwmon.c
+++ b/drivers/net/ethernet/intel/igb/igb_hwmon.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 46d31a49f5ea..30198185d19a 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel(R) Gigabit Ethernet Linux driver
- Copyright(c) 2007-2013 Intel Corporation.
+ Copyright(c) 2007-2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -13,8 +13,7 @@
more details.
You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ this program; if not, see <http://www.gnu.org/licenses/>.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
@@ -70,7 +69,7 @@ char igb_driver_version[] = DRV_VERSION;
static const char igb_driver_string[] =
"Intel(R) Gigabit Ethernet Network Driver";
static const char igb_copyright[] =
- "Copyright (c) 2007-2013 Intel Corporation.";
+ "Copyright (c) 2007-2014 Intel Corporation.";
static const struct e1000_info *igb_info_tbl[] = {
[board_82575] = &e1000_82575_info,
@@ -752,6 +751,28 @@ static void igb_cache_ring_register(struct igb_adapter *adapter)
}
}
+u32 igb_rd32(struct e1000_hw *hw, u32 reg)
+{
+ struct igb_adapter *igb = container_of(hw, struct igb_adapter, hw);
+ u8 __iomem *hw_addr = ACCESS_ONCE(hw->hw_addr);
+ u32 value = 0;
+
+ if (E1000_REMOVED(hw_addr))
+ return ~value;
+
+ value = readl(&hw_addr[reg]);
+
+ /* reads should not return all F's */
+ if (!(~value) && (!reg || !(~readl(hw_addr)))) {
+ struct net_device *netdev = igb->netdev;
+ hw->hw_addr = NULL;
+ netif_device_detach(netdev);
+ netdev_err(netdev, "PCIe link lost, device now detached\n");
+ }
+
+ return value;
+}
+
/**
* igb_write_ivar - configure ivar for given MSI-X vector
* @hw: pointer to the HW structure
@@ -1014,6 +1035,12 @@ static void igb_reset_q_vector(struct igb_adapter *adapter, int v_idx)
{
struct igb_q_vector *q_vector = adapter->q_vector[v_idx];
+ /* Coming from igb_set_interrupt_capability, the vectors are not yet
+ * allocated. So, q_vector is NULL so we should stop here.
+ */
+ if (!q_vector)
+ return;
+
if (q_vector->tx.ring)
adapter->tx_ring[q_vector->tx.ring->queue_index] = NULL;
@@ -1111,16 +1138,18 @@ static void igb_set_interrupt_capability(struct igb_adapter *adapter, bool msix)
for (i = 0; i < numvecs; i++)
adapter->msix_entries[i].entry = i;
- err = pci_enable_msix(adapter->pdev,
- adapter->msix_entries,
- numvecs);
- if (err == 0)
+ err = pci_enable_msix_range(adapter->pdev,
+ adapter->msix_entries,
+ numvecs,
+ numvecs);
+ if (err > 0)
return;
igb_reset_interrupt_capability(adapter);
/* If we can't do MSI-X, try MSI */
msi_only:
+ adapter->flags &= ~IGB_FLAG_HAS_MSIX;
#ifdef CONFIG_PCI_IOV
/* disable SR-IOV for non MSI-X configurations */
if (adapter->vf_data) {
@@ -1726,6 +1755,10 @@ int igb_up(struct igb_adapter *adapter)
hw->mac.get_link_status = 1;
schedule_work(&adapter->watchdog_task);
+ if ((adapter->flags & IGB_FLAG_EEE) &&
+ (!hw->dev_spec._82575.eee_disable))
+ adapter->eee_advert = MDIO_EEE_100TX | MDIO_EEE_1000T;
+
return 0;
}
@@ -1974,6 +2007,21 @@ void igb_reset(struct igb_adapter *adapter)
}
}
#endif
+ /* Re-establish EEE setting */
+ if (hw->phy.media_type == e1000_media_type_copper) {
+ switch (mac->type) {
+ case e1000_i350:
+ case e1000_i210:
+ case e1000_i211:
+ igb_set_eee_i350(hw);
+ break;
+ case e1000_i354:
+ igb_set_eee_i354(hw);
+ break;
+ default:
+ break;
+ }
+ }
if (!netif_running(adapter->netdev))
igb_power_down_link(adapter);
@@ -2560,23 +2608,36 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
(adapter->flags & IGB_FLAG_HAS_MSIX) ? "MSI-X" :
(adapter->flags & IGB_FLAG_HAS_MSI) ? "MSI" : "legacy",
adapter->num_rx_queues, adapter->num_tx_queues);
- switch (hw->mac.type) {
- case e1000_i350:
- case e1000_i210:
- case e1000_i211:
- igb_set_eee_i350(hw);
- break;
- case e1000_i354:
- if (hw->phy.media_type == e1000_media_type_copper) {
+ if (hw->phy.media_type == e1000_media_type_copper) {
+ switch (hw->mac.type) {
+ case e1000_i350:
+ case e1000_i210:
+ case e1000_i211:
+ /* Enable EEE for internal copper PHY devices */
+ err = igb_set_eee_i350(hw);
+ if ((!err) &&
+ (!hw->dev_spec._82575.eee_disable)) {
+ adapter->eee_advert =
+ MDIO_EEE_100TX | MDIO_EEE_1000T;
+ adapter->flags |= IGB_FLAG_EEE;
+ }
+ break;
+ case e1000_i354:
if ((rd32(E1000_CTRL_EXT) &
- E1000_CTRL_EXT_LINK_MODE_SGMII))
- igb_set_eee_i354(hw);
+ E1000_CTRL_EXT_LINK_MODE_SGMII)) {
+ err = igb_set_eee_i354(hw);
+ if ((!err) &&
+ (!hw->dev_spec._82575.eee_disable)) {
+ adapter->eee_advert =
+ MDIO_EEE_100TX | MDIO_EEE_1000T;
+ adapter->flags |= IGB_FLAG_EEE;
+ }
+ }
+ break;
+ default:
+ break;
}
- break;
- default:
- break;
}
-
pm_runtime_put_noidle(&pdev->dev);
return 0;
@@ -2591,7 +2652,7 @@ err_eeprom:
iounmap(hw->flash_address);
err_sw_init:
igb_clear_interrupt_scheme(adapter);
- iounmap(hw->hw_addr);
+ pci_iounmap(pdev, hw->hw_addr);
err_ioremap:
free_netdev(netdev);
err_alloc_etherdev:
@@ -2758,7 +2819,7 @@ static void igb_remove(struct pci_dev *pdev)
igb_disable_sriov(pdev);
#endif
- iounmap(hw->hw_addr);
+ pci_iounmap(pdev, hw->hw_addr);
if (hw->flash_address)
iounmap(hw->flash_address);
pci_release_selected_regions(pdev,
@@ -3510,6 +3571,13 @@ static inline void igb_set_vmolr(struct igb_adapter *adapter,
vmolr = rd32(E1000_VMOLR(vfn));
vmolr |= E1000_VMOLR_STRVLAN; /* Strip vlan tags */
+ if (hw->mac.type == e1000_i350) {
+ u32 dvmolr;
+
+ dvmolr = rd32(E1000_DVMOLR(vfn));
+ dvmolr |= E1000_DVMOLR_STRVLAN;
+ wr32(E1000_DVMOLR(vfn), dvmolr);
+ }
if (aupe)
vmolr |= E1000_VMOLR_AUPE; /* Accept untagged packets */
else
@@ -4158,6 +4226,15 @@ static void igb_watchdog_task(struct work_struct *work)
(ctrl & E1000_CTRL_RFCE) ? "RX" :
(ctrl & E1000_CTRL_TFCE) ? "TX" : "None");
+ /* disable EEE if enabled */
+ if ((adapter->flags & IGB_FLAG_EEE) &&
+ (adapter->link_duplex == HALF_DUPLEX)) {
+ dev_info(&adapter->pdev->dev,
+ "EEE Disabled: unsupported at half duplex. Re-enable using ethtool when at full duplex.\n");
+ adapter->hw.dev_spec._82575.eee_disable = true;
+ adapter->flags &= ~IGB_FLAG_EEE;
+ }
+
/* check if SmartSpeed worked */
igb_check_downshift(hw);
if (phy->speed_downgraded)
@@ -4306,8 +4383,7 @@ enum latency_range {
* were determined based on theoretical maximum wire speed and testing
* data, in order to minimize response time while increasing bulk
* throughput.
- * This functionality is controlled by the InterruptThrottleRate module
- * parameter (see igb_param.c)
+ * This functionality is controlled by ethtool's coalescing settings.
* NOTE: This function is called only when operating in a multiqueue
* receive environment.
**/
@@ -4381,8 +4457,7 @@ clear_counts:
* based on theoretical maximum wire speed and thresholds were set based
* on testing data as well as attempting to minimize response time
* while increasing bulk throughput.
- * this functionality is controlled by the InterruptThrottleRate module
- * parameter (see igb_param.c)
+ * This functionality is controlled by ethtool's coalescing settings.
* NOTE: These calculations are only valid when operating in a single-
* queue environment.
**/
@@ -4546,7 +4621,7 @@ static int igb_tso(struct igb_ring *tx_ring,
/* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */
type_tucmd = E1000_ADVTXD_TUCMD_L4T_TCP;
- if (first->protocol == __constant_htons(ETH_P_IP)) {
+ if (first->protocol == htons(ETH_P_IP)) {
struct iphdr *iph = ip_hdr(skb);
iph->tot_len = 0;
iph->check = 0;
@@ -4602,12 +4677,12 @@ static void igb_tx_csum(struct igb_ring *tx_ring, struct igb_tx_buffer *first)
} else {
u8 l4_hdr = 0;
switch (first->protocol) {
- case __constant_htons(ETH_P_IP):
+ case htons(ETH_P_IP):
vlan_macip_lens |= skb_network_header_len(skb);
type_tucmd |= E1000_ADVTXD_TUCMD_IPV4;
l4_hdr = ip_hdr(skb)->protocol;
break;
- case __constant_htons(ETH_P_IPV6):
+ case htons(ETH_P_IPV6):
vlan_macip_lens |= skb_network_header_len(skb);
l4_hdr = ipv6_hdr(skb)->nexthdr;
break;
@@ -4905,12 +4980,11 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
first->bytecount = skb->len;
first->gso_segs = 1;
- skb_tx_timestamp(skb);
-
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
struct igb_adapter *adapter = netdev_priv(tx_ring->netdev);
- if (!(adapter->ptp_tx_skb)) {
+ if (!test_and_set_bit_lock(__IGB_PTP_TX_IN_PROGRESS,
+ &adapter->state)) {
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
tx_flags |= IGB_TX_FLAGS_TSTAMP;
@@ -4921,6 +4995,8 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
}
}
+ skb_tx_timestamp(skb);
+
if (vlan_tx_tag_present(skb)) {
tx_flags |= IGB_TX_FLAGS_VLAN;
tx_flags |= (vlan_tx_tag_get(skb) << IGB_TX_FLAGS_VLAN_SHIFT);
@@ -5127,10 +5203,10 @@ void igb_update_stats(struct igb_adapter *adapter,
}
do {
- start = u64_stats_fetch_begin_bh(&ring->rx_syncp);
+ start = u64_stats_fetch_begin_irq(&ring->rx_syncp);
_bytes = ring->rx_stats.bytes;
_packets = ring->rx_stats.packets;
- } while (u64_stats_fetch_retry_bh(&ring->rx_syncp, start));
+ } while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start));
bytes += _bytes;
packets += _packets;
}
@@ -5143,10 +5219,10 @@ void igb_update_stats(struct igb_adapter *adapter,
for (i = 0; i < adapter->num_tx_queues; i++) {
struct igb_ring *ring = adapter->tx_ring[i];
do {
- start = u64_stats_fetch_begin_bh(&ring->tx_syncp);
+ start = u64_stats_fetch_begin_irq(&ring->tx_syncp);
_bytes = ring->tx_stats.bytes;
_packets = ring->tx_stats.packets;
- } while (u64_stats_fetch_retry_bh(&ring->tx_syncp, start));
+ } while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start));
bytes += _bytes;
packets += _packets;
}
@@ -6620,7 +6696,9 @@ static inline void igb_rx_hash(struct igb_ring *ring,
struct sk_buff *skb)
{
if (ring->netdev->features & NETIF_F_RXHASH)
- skb->rxhash = le32_to_cpu(rx_desc->wb.lower.hi_dword.rss);
+ skb_set_hash(skb,
+ le32_to_cpu(rx_desc->wb.lower.hi_dword.rss),
+ PKT_HASH_TYPE_L3);
}
/**
@@ -6690,7 +6768,7 @@ static unsigned int igb_get_headlen(unsigned char *data,
hdr.network += ETH_HLEN;
/* handle any vlan tag if present */
- if (protocol == __constant_htons(ETH_P_8021Q)) {
+ if (protocol == htons(ETH_P_8021Q)) {
if ((hdr.network - data) > (max_len - VLAN_HLEN))
return max_len;
@@ -6699,7 +6777,7 @@ static unsigned int igb_get_headlen(unsigned char *data,
}
/* handle L3 protocols */
- if (protocol == __constant_htons(ETH_P_IP)) {
+ if (protocol == htons(ETH_P_IP)) {
if ((hdr.network - data) > (max_len - sizeof(struct iphdr)))
return max_len;
@@ -6713,7 +6791,7 @@ static unsigned int igb_get_headlen(unsigned char *data,
/* record next protocol if header is present */
if (!(hdr.ipv4->frag_off & htons(IP_OFFSET)))
nexthdr = hdr.ipv4->protocol;
- } else if (protocol == __constant_htons(ETH_P_IPV6)) {
+ } else if (protocol == htons(ETH_P_IPV6)) {
if ((hdr.network - data) > (max_len - sizeof(struct ipv6hdr)))
return max_len;
@@ -6903,7 +6981,7 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
unsigned int total_bytes = 0, total_packets = 0;
u16 cleaned_count = igb_desc_unused(rx_ring);
- do {
+ while (likely(total_packets < budget)) {
union e1000_adv_rx_desc *rx_desc;
/* return some buffers to hardware, one at a time is too slow */
@@ -6955,7 +7033,7 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
/* update budget accounting */
total_packets++;
- } while (likely(total_packets < budget));
+ }
/* place incomplete frames back on ring for completion */
rx_ring->skb = skb;
@@ -7114,8 +7192,10 @@ static int igb_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
case SIOCGMIIREG:
case SIOCSMIIREG:
return igb_mii_ioctl(netdev, ifr, cmd);
+ case SIOCGHWTSTAMP:
+ return igb_ptp_get_ts_config(netdev, ifr);
case SIOCSHWTSTAMP:
- return igb_ptp_hwtstamp_ioctl(netdev, ifr, cmd);
+ return igb_ptp_set_ts_config(netdev, ifr);
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
index 5a54e3dc535d..2cca8fd5e574 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -12,9 +12,8 @@
* 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.
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/device.h>
@@ -75,6 +74,8 @@
#define INCVALUE_82576 (16 << IGB_82576_TSYNC_SHIFT)
#define IGB_NBITS_82580 40
+static void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter);
+
/* SYSTIM read access for the 82576 */
static cycle_t igb_ptp_read_82576(const struct cyclecounter *cc)
{
@@ -372,7 +373,7 @@ static int igb_ptp_enable(struct ptp_clock_info *ptp,
* This work function polls the TSYNCTXCTL valid bit to determine when a
* timestamp has been taken for the current stored skb.
**/
-void igb_ptp_tx_work(struct work_struct *work)
+static void igb_ptp_tx_work(struct work_struct *work)
{
struct igb_adapter *adapter = container_of(work, struct igb_adapter,
ptp_tx_work);
@@ -386,6 +387,7 @@ void igb_ptp_tx_work(struct work_struct *work)
IGB_PTP_TX_TIMEOUT)) {
dev_kfree_skb_any(adapter->ptp_tx_skb);
adapter->ptp_tx_skb = NULL;
+ clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state);
adapter->tx_hwtstamp_timeouts++;
dev_warn(&adapter->pdev->dev, "clearing Tx timestamp hang");
return;
@@ -466,7 +468,7 @@ void igb_ptp_rx_hang(struct igb_adapter *adapter)
* available, then it must have been for this skb here because we only
* allow only one such packet into the queue.
**/
-void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter)
+static void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
struct skb_shared_hwtstamps shhwtstamps;
@@ -479,6 +481,7 @@ void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter)
skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamps);
dev_kfree_skb_any(adapter->ptp_tx_skb);
adapter->ptp_tx_skb = NULL;
+ clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state);
}
/**
@@ -540,10 +543,26 @@ void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector,
}
/**
- * igb_ptp_hwtstamp_ioctl - control hardware time stamping
+ * igb_ptp_get_ts_config - get hardware time stamping config
+ * @netdev:
+ * @ifreq:
+ *
+ * Get the hwtstamp_config settings to return to the user. Rather than attempt
+ * to deconstruct the settings from the registers, just return a shadow copy
+ * of the last known settings.
+ **/
+int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
+{
+ struct igb_adapter *adapter = netdev_priv(netdev);
+ struct hwtstamp_config *config = &adapter->tstamp_config;
+
+ return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
+ -EFAULT : 0;
+}
+/**
+ * igb_ptp_set_ts_config - control hardware time stamping
* @netdev:
* @ifreq:
- * @cmd:
*
* Outgoing time stamping can be enabled and disabled. Play nice and
* disable it when requested, although it shouldn't case any overhead
@@ -557,12 +576,11 @@ void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector,
* not supported, with the exception of "all V2 events regardless of
* level 2 or 4".
**/
-int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
- struct ifreq *ifr, int cmd)
+int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
- struct hwtstamp_config config;
+ struct hwtstamp_config *config = &adapter->tstamp_config;
u32 tsync_tx_ctl = E1000_TSYNCTXCTL_ENABLED;
u32 tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
u32 tsync_rx_cfg = 0;
@@ -570,14 +588,14 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
bool is_l2 = false;
u32 regval;
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+ if (copy_from_user(config, ifr->ifr_data, sizeof(*config)))
return -EFAULT;
/* reserved for future extensions */
- if (config.flags)
+ if (config->flags)
return -EINVAL;
- switch (config.tx_type) {
+ switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
tsync_tx_ctl = 0;
case HWTSTAMP_TX_ON:
@@ -586,7 +604,7 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
return -ERANGE;
}
- switch (config.rx_filter) {
+ switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
tsync_rx_ctl = 0;
break;
@@ -610,7 +628,7 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_EVENT_V2;
- config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+ config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
is_l2 = true;
is_l4 = true;
break;
@@ -621,12 +639,12 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
*/
if (hw->mac.type != e1000_82576) {
tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL;
- config.rx_filter = HWTSTAMP_FILTER_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
break;
}
/* fall through */
default:
- config.rx_filter = HWTSTAMP_FILTER_NONE;
+ config->rx_filter = HWTSTAMP_FILTER_NONE;
return -ERANGE;
}
@@ -643,7 +661,7 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
if ((hw->mac.type >= e1000_82580) && tsync_rx_ctl) {
tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL;
- config.rx_filter = HWTSTAMP_FILTER_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
is_l2 = true;
is_l4 = true;
@@ -707,7 +725,7 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
regval = rd32(E1000_RXSTMPL);
regval = rd32(E1000_RXSTMPH);
- return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+ return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
-EFAULT : 0;
}
@@ -798,7 +816,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
/* Initialize the time sync interrupts for devices that support it. */
if (hw->mac.type >= e1000_82580) {
- wr32(E1000_TSIM, E1000_TSIM_TXTS);
+ wr32(E1000_TSIM, TSYNC_INTERRUPTS);
wr32(E1000_IMS, E1000_IMS_TS);
}
@@ -841,6 +859,7 @@ void igb_ptp_stop(struct igb_adapter *adapter)
if (adapter->ptp_tx_skb) {
dev_kfree_skb_any(adapter->ptp_tx_skb);
adapter->ptp_tx_skb = NULL;
+ clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state);
}
if (adapter->ptp_clock) {
@@ -864,6 +883,9 @@ void igb_ptp_reset(struct igb_adapter *adapter)
if (!(adapter->flags & IGB_FLAG_PTP))
return;
+ /* reset the tstamp_config */
+ memset(&adapter->tstamp_config, 0, sizeof(adapter->tstamp_config));
+
switch (adapter->hw.mac.type) {
case e1000_82576:
/* Dial the nominal frequency. */
@@ -876,7 +898,7 @@ void igb_ptp_reset(struct igb_adapter *adapter)
case e1000_i211:
/* Enable the timer functions and interrupts. */
wr32(E1000_TSAUXC, 0x0);
- wr32(E1000_TSIM, E1000_TSIM_TXTS);
+ wr32(E1000_TSIM, TSYNC_INTERRUPTS);
wr32(E1000_IMS, E1000_IMS_TS);
break;
default:
diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c
index 675435fc2e53..b7ab03a2f28f 100644
--- a/drivers/net/ethernet/intel/igbvf/netdev.c
+++ b/drivers/net/ethernet/intel/igbvf/netdev.c
@@ -1043,11 +1043,11 @@ static void igbvf_set_interrupt_capability(struct igbvf_adapter *adapter)
for (i = 0; i < 3; i++)
adapter->msix_entries[i].entry = i;
- err = pci_enable_msix(adapter->pdev,
- adapter->msix_entries, 3);
+ err = pci_enable_msix_range(adapter->pdev,
+ adapter->msix_entries, 3, 3);
}
- if (err) {
+ if (err < 0) {
/* MSI-X failed */
dev_err(&adapter->pdev->dev,
"Failed to initialize MSI-X interrupts.\n");
@@ -2014,12 +2014,12 @@ static inline bool igbvf_tx_csum(struct igbvf_adapter *adapter,
if (skb->ip_summed == CHECKSUM_PARTIAL) {
switch (skb->protocol) {
- case __constant_htons(ETH_P_IP):
+ case htons(ETH_P_IP):
tu_cmd |= E1000_ADVTXD_TUCMD_IPV4;
if (ip_hdr(skb)->protocol == IPPROTO_TCP)
tu_cmd |= E1000_ADVTXD_TUCMD_L4T_TCP;
break;
- case __constant_htons(ETH_P_IPV6):
+ case htons(ETH_P_IPV6):
if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP)
tu_cmd |= E1000_ADVTXD_TUCMD_L4T_TCP;
break;
diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c
index 57e390cbe6d0..f42c201f727f 100644
--- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c
@@ -1521,12 +1521,12 @@ ixgb_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
int tso;
if (test_bit(__IXGB_DOWN, &adapter->flags)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
if (skb->len <= 0) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -1543,7 +1543,7 @@ ixgb_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
tso = ixgb_tso(adapter, skb);
if (tso < 0) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index 0186ea2969fe..55c53a1cbb62 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -765,6 +766,7 @@ struct ixgbe_adapter {
struct ptp_clock_info ptp_caps;
struct work_struct ptp_tx_work;
struct sk_buff *ptp_tx_skb;
+ struct hwtstamp_config tstamp_config;
unsigned long ptp_tx_start;
unsigned long last_overflow_check;
unsigned long last_rx_ptp_check;
@@ -806,10 +808,12 @@ enum ixgbe_state_t {
__IXGBE_TESTING,
__IXGBE_RESETTING,
__IXGBE_DOWN,
+ __IXGBE_DISABLED,
__IXGBE_REMOVING,
__IXGBE_SERVICE_SCHED,
__IXGBE_IN_SFP_INIT,
__IXGBE_PTP_RUNNING,
+ __IXGBE_PTP_TX_IN_PROGRESS,
};
struct ixgbe_cb {
@@ -884,7 +888,6 @@ s32 ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw,
u16 soft_id);
void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
union ixgbe_atr_input *mask);
-bool ixgbe_verify_lesm_fw_enabled_82599(struct ixgbe_hw *hw);
void ixgbe_set_rx_mode(struct net_device *netdev);
#ifdef CONFIG_IXGBE_DCB
void ixgbe_set_rx_drop_en(struct ixgbe_adapter *adapter);
@@ -958,8 +961,8 @@ static inline void ixgbe_ptp_rx_hwtstamp(struct ixgbe_ring *rx_ring,
rx_ring->last_rx_timestamp = jiffies;
}
-int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter, struct ifreq *ifr,
- int cmd);
+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);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
index a26f3fee4f35..4c78ea8946c1 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 - 2013 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -57,10 +58,12 @@ static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset,
**/
static void ixgbe_set_pcie_completion_timeout(struct ixgbe_hw *hw)
{
- struct ixgbe_adapter *adapter = hw->back;
u32 gcr = IXGBE_READ_REG(hw, IXGBE_GCR);
u16 pcie_devctl2;
+ if (ixgbe_removed(hw->hw_addr))
+ return;
+
/* only take action if timeout value is defaulted to 0 */
if (gcr & IXGBE_GCR_CMPL_TMOUT_MASK)
goto out;
@@ -79,11 +82,9 @@ static void ixgbe_set_pcie_completion_timeout(struct ixgbe_hw *hw)
* directly in order to set the completion timeout value for
* 16ms to 55ms
*/
- pci_read_config_word(adapter->pdev,
- IXGBE_PCI_DEVICE_CONTROL2, &pcie_devctl2);
+ pcie_devctl2 = ixgbe_read_pci_cfg_word(hw, IXGBE_PCI_DEVICE_CONTROL2);
pcie_devctl2 |= IXGBE_PCI_DEVICE_CONTROL2_16ms;
- pci_write_config_word(adapter->pdev,
- IXGBE_PCI_DEVICE_CONTROL2, pcie_devctl2);
+ ixgbe_write_pci_cfg_word(hw, IXGBE_PCI_DEVICE_CONTROL2, pcie_devctl2);
out:
/* disable completion timeout resend */
gcr &= ~IXGBE_GCR_CMPL_TMOUT_RESEND;
@@ -100,6 +101,7 @@ static s32 ixgbe_get_invariants_82598(struct ixgbe_hw *hw)
mac->mcft_size = IXGBE_82598_MC_TBL_SIZE;
mac->vft_size = IXGBE_82598_VFT_TBL_SIZE;
mac->num_rar_entries = IXGBE_82598_RAR_ENTRIES;
+ mac->rx_pb_size = IXGBE_82598_RX_PB_SIZE;
mac->max_rx_queues = IXGBE_82598_MAX_RX_QUEUES;
mac->max_tx_queues = IXGBE_82598_MAX_TX_QUEUES;
mac->max_msix_vectors = ixgbe_get_pcie_msix_count_generic(hw);
@@ -201,8 +203,6 @@ static s32 ixgbe_start_hw_82598(struct ixgbe_hw *hw)
IXGBE_WRITE_REG(hw, IXGBE_DCA_RXCTRL(i), regval);
}
- hw->mac.rx_pb_size = IXGBE_82598_RX_PB_SIZE;
-
/* set the completion timeout for interface */
if (ret_val == 0)
ixgbe_set_pcie_completion_timeout(hw);
@@ -1237,14 +1237,14 @@ static void ixgbe_set_lan_id_multi_port_pcie_82598(struct ixgbe_hw *hw)
}
/**
- * ixgbe_set_rxpba_82598 - Configure packet buffers
+ * ixgbe_set_rxpba_82598 - Initialize RX packet buffer
* @hw: pointer to hardware structure
- * @dcb_config: pointer to ixgbe_dcb_config structure
- *
- * Configure packet buffers.
- */
-static void ixgbe_set_rxpba_82598(struct ixgbe_hw *hw, int num_pb, u32 headroom,
- int strategy)
+ * @num_pb: number of packet buffers to allocate
+ * @headroom: reserve n KB of headroom
+ * @strategy: packet buffer allocation strategy
+ **/
+static void ixgbe_set_rxpba_82598(struct ixgbe_hw *hw, int num_pb,
+ u32 headroom, int strategy)
{
u32 rxpktsize = IXGBE_RXPBSIZE_64KB;
u8 i = 0;
@@ -1315,7 +1315,8 @@ static struct ixgbe_mac_operations mac_ops_82598 = {
.release_swfw_sync = &ixgbe_release_swfw_sync,
.get_thermal_sensor_data = NULL,
.init_thermal_sensor_thresh = NULL,
- .mng_fw_enabled = NULL,
+ .prot_autoc_read = &prot_autoc_read_generic,
+ .prot_autoc_write = &prot_autoc_write_generic,
};
static struct ixgbe_eeprom_operations eeprom_ops_82598 = {
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
index edda6814108c..f32b3dd1ba8e 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -63,8 +64,10 @@ static s32 ixgbe_read_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset,
u8 dev_addr, u8 *data);
static s32 ixgbe_write_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset,
u8 dev_addr, u8 data);
+static s32 ixgbe_reset_pipeline_82599(struct ixgbe_hw *hw);
+static bool ixgbe_verify_lesm_fw_enabled_82599(struct ixgbe_hw *hw);
-static bool ixgbe_mng_enabled(struct ixgbe_hw *hw)
+bool ixgbe_mng_enabled(struct ixgbe_hw *hw)
{
u32 fwsm, manc, factps;
@@ -91,7 +94,7 @@ static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw)
* and MNG not enabled
*/
if ((mac->ops.get_media_type(hw) == ixgbe_media_type_fiber) &&
- !hw->mng_fw_enabled) {
+ !ixgbe_mng_enabled(hw)) {
mac->ops.disable_tx_laser =
&ixgbe_disable_tx_laser_multispeed_fiber;
mac->ops.enable_tx_laser =
@@ -122,7 +125,6 @@ static s32 ixgbe_setup_sfp_modules_82599(struct ixgbe_hw *hw)
{
s32 ret_val = 0;
u16 list_offset, data_offset, data_value;
- bool got_lock = false;
if (hw->phy.sfp_type != ixgbe_sfp_type_unknown) {
ixgbe_init_mac_link_ops_82599(hw);
@@ -160,30 +162,10 @@ static s32 ixgbe_setup_sfp_modules_82599(struct ixgbe_hw *hw)
usleep_range(hw->eeprom.semaphore_delay * 1000,
hw->eeprom.semaphore_delay * 2000);
- /* Need SW/FW semaphore around AUTOC writes if LESM on,
- * likewise reset_pipeline requires lock as it also writes
- * AUTOC.
- */
- if (ixgbe_verify_lesm_fw_enabled_82599(hw)) {
- ret_val = hw->mac.ops.acquire_swfw_sync(hw,
- IXGBE_GSSR_MAC_CSR_SM);
- if (ret_val)
- goto setup_sfp_out;
-
- got_lock = true;
- }
-
/* Restart DSP and set SFI mode */
- IXGBE_WRITE_REG(hw, IXGBE_AUTOC, ((hw->mac.orig_autoc) |
- IXGBE_AUTOC_LMS_10G_SERIAL));
- hw->mac.cached_autoc = IXGBE_READ_REG(hw, IXGBE_AUTOC);
- ret_val = ixgbe_reset_pipeline_82599(hw);
-
- if (got_lock) {
- hw->mac.ops.release_swfw_sync(hw,
- IXGBE_GSSR_MAC_CSR_SM);
- got_lock = false;
- }
+ ret_val = hw->mac.ops.prot_autoc_write(hw,
+ hw->mac.orig_autoc | IXGBE_AUTOC_LMS_10G_SERIAL,
+ false);
if (ret_val) {
hw_dbg(hw, " sfp module setup not complete\n");
@@ -207,6 +189,81 @@ setup_sfp_err:
return IXGBE_ERR_SFP_SETUP_NOT_COMPLETE;
}
+/**
+ * prot_autoc_read_82599 - Hides MAC differences needed for AUTOC read
+ * @hw: pointer to hardware structure
+ * @locked: Return the if we locked for this read.
+ * @reg_val: Value we read from AUTOC
+ *
+ * For this part (82599) we need to wrap read-modify-writes with a possible
+ * FW/SW lock. It is assumed this lock will be freed with the next
+ * prot_autoc_write_82599(). Note, that locked can only be true in cases
+ * where this function doesn't return an error.
+ **/
+static s32 prot_autoc_read_82599(struct ixgbe_hw *hw, bool *locked,
+ u32 *reg_val)
+{
+ s32 ret_val;
+
+ *locked = false;
+ /* If LESM is on then we need to hold the SW/FW semaphore. */
+ if (ixgbe_verify_lesm_fw_enabled_82599(hw)) {
+ ret_val = hw->mac.ops.acquire_swfw_sync(hw,
+ IXGBE_GSSR_MAC_CSR_SM);
+ if (ret_val)
+ return IXGBE_ERR_SWFW_SYNC;
+
+ *locked = true;
+ }
+
+ *reg_val = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+ return 0;
+}
+
+/**
+ * prot_autoc_write_82599 - Hides MAC differences needed for AUTOC write
+ * @hw: pointer to hardware structure
+ * @reg_val: value to write to AUTOC
+ * @locked: bool to indicate whether the SW/FW lock was already taken by
+ * previous proc_autoc_read_82599.
+ *
+ * This part (82599) may need to hold a the SW/FW lock around all writes to
+ * AUTOC. Likewise after a write we need to do a pipeline reset.
+ **/
+static s32 prot_autoc_write_82599(struct ixgbe_hw *hw, u32 autoc, bool locked)
+{
+ s32 ret_val = 0;
+
+ /* Blocked by MNG FW so bail */
+ if (ixgbe_check_reset_blocked(hw))
+ goto out;
+
+ /* We only need to get the lock if:
+ * - We didn't do it already (in the read part of a read-modify-write)
+ * - LESM is enabled.
+ */
+ if (!locked && ixgbe_verify_lesm_fw_enabled_82599(hw)) {
+ ret_val = hw->mac.ops.acquire_swfw_sync(hw,
+ IXGBE_GSSR_MAC_CSR_SM);
+ if (ret_val)
+ return IXGBE_ERR_SWFW_SYNC;
+
+ locked = true;
+ }
+
+ IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc);
+ ret_val = ixgbe_reset_pipeline_82599(hw);
+
+out:
+ /* Free the SW/FW semaphore as we either grabbed it here or
+ * already had it when this function was called.
+ */
+ if (locked)
+ hw->mac.ops.release_swfw_sync(hw, IXGBE_GSSR_MAC_CSR_SM);
+
+ return ret_val;
+}
+
static s32 ixgbe_get_invariants_82599(struct ixgbe_hw *hw)
{
struct ixgbe_mac_info *mac = &hw->mac;
@@ -216,6 +273,7 @@ static s32 ixgbe_get_invariants_82599(struct ixgbe_hw *hw)
mac->mcft_size = IXGBE_82599_MC_TBL_SIZE;
mac->vft_size = IXGBE_82599_VFT_TBL_SIZE;
mac->num_rar_entries = IXGBE_82599_RAR_ENTRIES;
+ mac->rx_pb_size = IXGBE_82599_RX_PB_SIZE;
mac->max_rx_queues = IXGBE_82599_MAX_RX_QUEUES;
mac->max_tx_queues = IXGBE_82599_MAX_TX_QUEUES;
mac->max_msix_vectors = ixgbe_get_pcie_msix_count_generic(hw);
@@ -456,12 +514,20 @@ out:
*
* Disables link, should be called during D3 power down sequence.
*
- */
+ **/
static void ixgbe_stop_mac_link_on_d3_82599(struct ixgbe_hw *hw)
{
- u32 autoc2_reg;
+ u32 autoc2_reg, fwsm;
+ u16 ee_ctrl_2 = 0;
+
+ hw->eeprom.ops.read(hw, IXGBE_EEPROM_CTRL_2, &ee_ctrl_2);
- if (!hw->mng_fw_enabled && !hw->wol_enabled) {
+ /* Check to see if MNG FW could be enabled */
+ fwsm = IXGBE_READ_REG(hw, IXGBE_FWSM);
+
+ if (((fwsm & IXGBE_FWSM_MODE_MASK) != IXGBE_FWSM_FW_MODE_PT) &&
+ !hw->wol_enabled &&
+ ee_ctrl_2 & IXGBE_EEPROM_CCD_BIT) {
autoc2_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC2);
autoc2_reg |= IXGBE_AUTOC2_LINK_DISABLE_ON_D3_MASK;
IXGBE_WRITE_REG(hw, IXGBE_AUTOC2, autoc2_reg);
@@ -542,6 +608,10 @@ static void ixgbe_disable_tx_laser_multispeed_fiber(struct ixgbe_hw *hw)
{
u32 esdp_reg = IXGBE_READ_REG(hw, IXGBE_ESDP);
+ /* Blocked by MNG FW so bail */
+ if (ixgbe_check_reset_blocked(hw))
+ return;
+
/* Disable tx laser; allow 100us to go dark per spec */
esdp_reg |= IXGBE_ESDP_SDP3;
IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
@@ -582,6 +652,10 @@ static void ixgbe_enable_tx_laser_multispeed_fiber(struct ixgbe_hw *hw)
**/
static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw)
{
+ /* Blocked by MNG FW so bail */
+ if (ixgbe_check_reset_blocked(hw))
+ return;
+
if (hw->mac.autotry_restart) {
ixgbe_disable_tx_laser_multispeed_fiber(hw);
ixgbe_enable_tx_laser_multispeed_fiber(hw);
@@ -590,75 +664,6 @@ static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw)
}
/**
- * ixgbe_set_fiber_fixed_speed - Set module link speed for fixed fiber
- * @hw: pointer to hardware structure
- * @speed: link speed to set
- *
- * We set the module speed differently for fixed fiber. For other
- * multi-speed devices we don't have an error value so here if we
- * detect an error we just log it and exit.
- */
-static void ixgbe_set_fiber_fixed_speed(struct ixgbe_hw *hw,
- ixgbe_link_speed speed)
-{
- s32 status;
- u8 rs, eeprom_data;
-
- switch (speed) {
- case IXGBE_LINK_SPEED_10GB_FULL:
- /* one bit mask same as setting on */
- rs = IXGBE_SFF_SOFT_RS_SELECT_10G;
- break;
- case IXGBE_LINK_SPEED_1GB_FULL:
- rs = IXGBE_SFF_SOFT_RS_SELECT_1G;
- break;
- default:
- hw_dbg(hw, "Invalid fixed module speed\n");
- return;
- }
-
- /* Set RS0 */
- status = hw->phy.ops.read_i2c_byte(hw, IXGBE_SFF_SFF_8472_OSCB,
- IXGBE_I2C_EEPROM_DEV_ADDR2,
- &eeprom_data);
- if (status) {
- hw_dbg(hw, "Failed to read Rx Rate Select RS0\n");
- goto out;
- }
-
- eeprom_data = (eeprom_data & ~IXGBE_SFF_SOFT_RS_SELECT_MASK) | rs;
-
- status = hw->phy.ops.write_i2c_byte(hw, IXGBE_SFF_SFF_8472_OSCB,
- IXGBE_I2C_EEPROM_DEV_ADDR2,
- eeprom_data);
- if (status) {
- hw_dbg(hw, "Failed to write Rx Rate Select RS0\n");
- goto out;
- }
-
- /* Set RS1 */
- status = hw->phy.ops.read_i2c_byte(hw, IXGBE_SFF_SFF_8472_ESCB,
- IXGBE_I2C_EEPROM_DEV_ADDR2,
- &eeprom_data);
- if (status) {
- hw_dbg(hw, "Failed to read Rx Rate Select RS1\n");
- goto out;
- }
-
- eeprom_data = (eeprom_data & ~IXGBE_SFF_SOFT_RS_SELECT_MASK) & rs;
-
- status = hw->phy.ops.write_i2c_byte(hw, IXGBE_SFF_SFF_8472_ESCB,
- IXGBE_I2C_EEPROM_DEV_ADDR2,
- eeprom_data);
- if (status) {
- hw_dbg(hw, "Failed to write Rx Rate Select RS1\n");
- goto out;
- }
-out:
- return;
-}
-
-/**
* ixgbe_setup_mac_link_multispeed_fiber - Set MAC link speed
* @hw: pointer to hardware structure
* @speed: new link speed
@@ -768,10 +773,6 @@ static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
/* Set the module link speed */
switch (hw->phy.media_type) {
- case ixgbe_media_type_fiber_fixed:
- ixgbe_set_fiber_fixed_speed(hw,
- IXGBE_LINK_SPEED_1GB_FULL);
- break;
case ixgbe_media_type_fiber:
esdp_reg &= ~IXGBE_ESDP_SDP5;
esdp_reg |= IXGBE_ESDP_SDP5_DIR;
@@ -941,8 +942,7 @@ static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw,
out:
if (link_up && (link_speed == IXGBE_LINK_SPEED_1GB_FULL))
- hw_dbg(hw, "Smartspeed has downgraded the link speed from "
- "the maximum advertised\n");
+ hw_dbg(hw, "Smartspeed has downgraded the link speed from the maximum advertised\n");
return status;
}
@@ -958,16 +958,19 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
ixgbe_link_speed speed,
bool autoneg_wait_to_complete)
{
+ bool autoneg = false;
s32 status = 0;
- u32 autoc, pma_pmd_1g, link_mode, start_autoc;
+ u32 pma_pmd_1g, link_mode, links_reg, i;
u32 autoc2 = IXGBE_READ_REG(hw, IXGBE_AUTOC2);
- u32 orig_autoc = 0;
u32 pma_pmd_10g_serial = autoc2 & IXGBE_AUTOC2_10G_SERIAL_PMA_PMD_MASK;
- u32 links_reg;
- u32 i;
ixgbe_link_speed link_capabilities = IXGBE_LINK_SPEED_UNKNOWN;
- bool got_lock = false;
- bool autoneg = false;
+
+ /* holds the value of AUTOC register at this current point in time */
+ u32 current_autoc = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+ /* holds the cached value of AUTOC register */
+ u32 orig_autoc = 0;
+ /* temporary variable used for comparison purposes */
+ u32 autoc = current_autoc;
/* Check to see if speed passed in is supported. */
status = hw->mac.ops.get_link_capabilities(hw, &link_capabilities,
@@ -984,12 +987,10 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
/* Use stored value (EEPROM defaults) of AUTOC to find KR/KX4 support*/
if (hw->mac.orig_link_settings_stored)
- autoc = hw->mac.orig_autoc;
+ orig_autoc = hw->mac.orig_autoc;
else
- autoc = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+ orig_autoc = autoc;
- orig_autoc = autoc;
- start_autoc = hw->mac.cached_autoc;
link_mode = autoc & IXGBE_AUTOC_LMS_MASK;
pma_pmd_1g = autoc & IXGBE_AUTOC_1G_PMA_PMD_MASK;
@@ -1029,28 +1030,11 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
}
}
- if (autoc != start_autoc) {
- /* Need SW/FW semaphore around AUTOC writes if LESM is on,
- * likewise reset_pipeline requires us to hold this lock as
- * it also writes to AUTOC.
- */
- if (ixgbe_verify_lesm_fw_enabled_82599(hw)) {
- status = hw->mac.ops.acquire_swfw_sync(hw,
- IXGBE_GSSR_MAC_CSR_SM);
- if (status != 0)
- goto out;
-
- got_lock = true;
- }
-
+ if (autoc != current_autoc) {
/* Restart link */
- IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc);
- hw->mac.cached_autoc = autoc;
- ixgbe_reset_pipeline_82599(hw);
-
- if (got_lock)
- hw->mac.ops.release_swfw_sync(hw,
- IXGBE_GSSR_MAC_CSR_SM);
+ status = hw->mac.ops.prot_autoc_write(hw, autoc, false);
+ if (status)
+ goto out;
/* Only poll for autoneg to complete if specified to do so */
if (autoneg_wait_to_complete) {
@@ -1068,8 +1052,7 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
if (!(links_reg & IXGBE_LINKS_KX_AN_COMP)) {
status =
IXGBE_ERR_AUTONEG_NOT_COMPLETE;
- hw_dbg(hw, "Autoneg did not "
- "complete.\n");
+ hw_dbg(hw, "Autoneg did not complete.\n");
}
}
}
@@ -1117,7 +1100,7 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw)
{
ixgbe_link_speed link_speed;
s32 status;
- u32 ctrl, i, autoc2;
+ u32 ctrl, i, autoc, autoc2;
u32 curr_lms;
bool link_up = false;
@@ -1151,11 +1134,7 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw)
hw->phy.ops.reset(hw);
/* remember AUTOC from before we reset */
- if (hw->mac.cached_autoc)
- curr_lms = hw->mac.cached_autoc & IXGBE_AUTOC_LMS_MASK;
- else
- curr_lms = IXGBE_READ_REG(hw, IXGBE_AUTOC) &
- IXGBE_AUTOC_LMS_MASK;
+ curr_lms = IXGBE_READ_REG(hw, IXGBE_AUTOC) & IXGBE_AUTOC_LMS_MASK;
mac_reset_top:
/*
@@ -1205,7 +1184,7 @@ mac_reset_top:
* stored off yet. Otherwise restore the stored original
* values since the reset operation sets back to defaults.
*/
- hw->mac.cached_autoc = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+ autoc = IXGBE_READ_REG(hw, IXGBE_AUTOC);
autoc2 = IXGBE_READ_REG(hw, IXGBE_AUTOC2);
/* Enable link if disabled in NVM */
@@ -1216,7 +1195,7 @@ mac_reset_top:
}
if (hw->mac.orig_link_settings_stored == false) {
- hw->mac.orig_autoc = hw->mac.cached_autoc;
+ hw->mac.orig_autoc = autoc;
hw->mac.orig_autoc2 = autoc2;
hw->mac.orig_link_settings_stored = true;
} else {
@@ -1227,34 +1206,18 @@ mac_reset_top:
* Likewise if we support WoL we don't want change the
* LMS state either.
*/
- if ((hw->phy.multispeed_fiber && hw->mng_fw_enabled) ||
+ if ((hw->phy.multispeed_fiber && ixgbe_mng_enabled(hw)) ||
hw->wol_enabled)
hw->mac.orig_autoc =
(hw->mac.orig_autoc & ~IXGBE_AUTOC_LMS_MASK) |
curr_lms;
- if (hw->mac.cached_autoc != hw->mac.orig_autoc) {
- /* Need SW/FW semaphore around AUTOC writes if LESM is
- * on, likewise reset_pipeline requires us to hold
- * this lock as it also writes to AUTOC.
- */
- bool got_lock = false;
- if (ixgbe_verify_lesm_fw_enabled_82599(hw)) {
- status = hw->mac.ops.acquire_swfw_sync(hw,
- IXGBE_GSSR_MAC_CSR_SM);
- if (status)
- goto reset_hw_out;
-
- got_lock = true;
- }
-
- IXGBE_WRITE_REG(hw, IXGBE_AUTOC, hw->mac.orig_autoc);
- hw->mac.cached_autoc = hw->mac.orig_autoc;
- ixgbe_reset_pipeline_82599(hw);
-
- if (got_lock)
- hw->mac.ops.release_swfw_sync(hw,
- IXGBE_GSSR_MAC_CSR_SM);
+ if (autoc != hw->mac.orig_autoc) {
+ status = hw->mac.ops.prot_autoc_write(hw,
+ hw->mac.orig_autoc,
+ false);
+ if (status)
+ goto reset_hw_out;
}
if ((autoc2 & IXGBE_AUTOC2_UPPER_MASK) !=
@@ -1634,35 +1597,20 @@ void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
{
u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;
- u32 bucket_hash = 0;
+ u32 bucket_hash = 0, hi_dword = 0;
+ int i;
/* Apply masks to input data */
- input->dword_stream[0] &= input_mask->dword_stream[0];
- input->dword_stream[1] &= input_mask->dword_stream[1];
- input->dword_stream[2] &= input_mask->dword_stream[2];
- input->dword_stream[3] &= input_mask->dword_stream[3];
- input->dword_stream[4] &= input_mask->dword_stream[4];
- input->dword_stream[5] &= input_mask->dword_stream[5];
- input->dword_stream[6] &= input_mask->dword_stream[6];
- input->dword_stream[7] &= input_mask->dword_stream[7];
- input->dword_stream[8] &= input_mask->dword_stream[8];
- input->dword_stream[9] &= input_mask->dword_stream[9];
- input->dword_stream[10] &= input_mask->dword_stream[10];
+ for (i = 0; i <= 10; i++)
+ input->dword_stream[i] &= input_mask->dword_stream[i];
/* record the flow_vm_vlan bits as they are a key part to the hash */
flow_vm_vlan = ntohl(input->dword_stream[0]);
/* generate common hash dword */
- hi_hash_dword = ntohl(input->dword_stream[1] ^
- input->dword_stream[2] ^
- input->dword_stream[3] ^
- input->dword_stream[4] ^
- input->dword_stream[5] ^
- input->dword_stream[6] ^
- input->dword_stream[7] ^
- input->dword_stream[8] ^
- input->dword_stream[9] ^
- input->dword_stream[10]);
+ for (i = 1; i <= 10; i++)
+ hi_dword ^= input->dword_stream[i];
+ hi_hash_dword = ntohl(hi_dword);
/* low dword is word swapped version of common */
lo_hash_dword = (hi_hash_dword >> 16) | (hi_hash_dword << 16);
@@ -1681,21 +1629,8 @@ void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
lo_hash_dword ^= flow_vm_vlan ^ (flow_vm_vlan << 16);
/* Process remaining 30 bit of the key */
- IXGBE_COMPUTE_BKT_HASH_ITERATION(1);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(2);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(3);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(4);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(5);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(6);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(7);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(8);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(9);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(10);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(11);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(12);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(13);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(14);
- IXGBE_COMPUTE_BKT_HASH_ITERATION(15);
+ for (i = 1; i <= 15; i++)
+ IXGBE_COMPUTE_BKT_HASH_ITERATION(i);
/*
* Limit hash to 13 bits since max bucket count is 8K.
@@ -2001,7 +1936,6 @@ static s32 ixgbe_start_hw_82599(struct ixgbe_hw *hw)
/* We need to run link autotry after the driver loads */
hw->mac.autotry_restart = true;
- hw->mac.rx_pb_size = IXGBE_82599_RX_PB_SIZE;
if (ret_val == 0)
ret_val = ixgbe_verify_fw_version_82599(hw);
@@ -2260,7 +2194,7 @@ fw_version_err:
* Returns true if the LESM FW module is present and enabled. Otherwise
* returns false. Smart Speed must be disabled if LESM FW module is enabled.
**/
-bool ixgbe_verify_lesm_fw_enabled_82599(struct ixgbe_hw *hw)
+static bool ixgbe_verify_lesm_fw_enabled_82599(struct ixgbe_hw *hw)
{
bool lesm_enabled = false;
u16 fw_offset, fw_lesm_param_offset, fw_lesm_state;
@@ -2366,7 +2300,7 @@ static s32 ixgbe_read_eeprom_82599(struct ixgbe_hw *hw,
* full pipeline reset. Note - We must hold the SW/FW semaphore before writing
* to AUTOC, so this function assumes the semaphore is held.
**/
-s32 ixgbe_reset_pipeline_82599(struct ixgbe_hw *hw)
+static s32 ixgbe_reset_pipeline_82599(struct ixgbe_hw *hw)
{
s32 ret_val;
u32 anlp1_reg = 0;
@@ -2380,11 +2314,12 @@ s32 ixgbe_reset_pipeline_82599(struct ixgbe_hw *hw)
IXGBE_WRITE_FLUSH(hw);
}
- autoc_reg = hw->mac.cached_autoc;
+ autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC);
autoc_reg |= IXGBE_AUTOC_AN_RESTART;
/* Write AUTOC register with toggled LMS[2] bit and Restart_AN */
- IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc_reg ^ IXGBE_AUTOC_LMS_1G_AN);
+ IXGBE_WRITE_REG(hw, IXGBE_AUTOC,
+ autoc_reg ^ (0x4 << IXGBE_AUTOC_LMS_SHIFT));
/* Wait for AN to leave state 0 */
for (i = 0; i < 10; i++) {
@@ -2565,7 +2500,8 @@ static struct ixgbe_mac_operations mac_ops_82599 = {
.release_swfw_sync = &ixgbe_release_swfw_sync,
.get_thermal_sensor_data = &ixgbe_get_thermal_sensor_data_generic,
.init_thermal_sensor_thresh = &ixgbe_init_thermal_sensor_thresh_generic,
- .mng_fw_enabled = &ixgbe_mng_enabled,
+ .prot_autoc_read = &prot_autoc_read_82599,
+ .prot_autoc_write = &prot_autoc_write_82599,
};
static struct ixgbe_eeprom_operations eeprom_ops_82599 = {
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
index b5c434b617b1..24fba39e194e 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 - 2013 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -72,7 +73,6 @@ bool ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw)
bool link_up;
switch (hw->phy.media_type) {
- case ixgbe_media_type_fiber_fixed:
case ixgbe_media_type_fiber:
hw->mac.ops.check_link(hw, &speed, &link_up, false);
/* if link is down, assume supported */
@@ -114,7 +114,7 @@ static s32 ixgbe_setup_fc(struct ixgbe_hw *hw)
s32 ret_val = 0;
u32 reg = 0, reg_bp = 0;
u16 reg_cu = 0;
- bool got_lock = false;
+ bool locked = false;
/*
* Validate the requested mode. Strict IEEE mode does not allow
@@ -139,11 +139,16 @@ static s32 ixgbe_setup_fc(struct ixgbe_hw *hw)
* we link at 10G, the 1G advertisement is harmless and vice versa.
*/
switch (hw->phy.media_type) {
- case ixgbe_media_type_fiber_fixed:
- case ixgbe_media_type_fiber:
case ixgbe_media_type_backplane:
+ /* some MAC's need RMW protection on AUTOC */
+ ret_val = hw->mac.ops.prot_autoc_read(hw, &locked, &reg_bp);
+ if (ret_val)
+ goto out;
+
+ /* only backplane uses autoc so fall though */
+ case ixgbe_media_type_fiber:
reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA);
- reg_bp = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+
break;
case ixgbe_media_type_copper:
hw->phy.ops.read_reg(hw, MDIO_AN_ADVERTISE,
@@ -240,27 +245,12 @@ static s32 ixgbe_setup_fc(struct ixgbe_hw *hw)
* LESM is on, likewise reset_pipeline requries the lock as
* it also writes AUTOC.
*/
- if ((hw->mac.type == ixgbe_mac_82599EB) &&
- ixgbe_verify_lesm_fw_enabled_82599(hw)) {
- ret_val = hw->mac.ops.acquire_swfw_sync(hw,
- IXGBE_GSSR_MAC_CSR_SM);
- if (ret_val)
- goto out;
-
- got_lock = true;
- }
-
- IXGBE_WRITE_REG(hw, IXGBE_AUTOC, reg_bp);
-
- if (hw->mac.type == ixgbe_mac_82599EB)
- ixgbe_reset_pipeline_82599(hw);
-
- if (got_lock)
- hw->mac.ops.release_swfw_sync(hw,
- IXGBE_GSSR_MAC_CSR_SM);
+ ret_val = hw->mac.ops.prot_autoc_write(hw, reg_bp, locked);
+ if (ret_val)
+ goto out;
} else if ((hw->phy.media_type == ixgbe_media_type_copper) &&
- ixgbe_device_supports_autoneg_fc(hw)) {
+ ixgbe_device_supports_autoneg_fc(hw)) {
hw->phy.ops.write_reg(hw, MDIO_AN_ADVERTISE,
MDIO_MMD_AN, reg_cu);
}
@@ -656,20 +646,17 @@ enum ixgbe_bus_speed ixgbe_convert_bus_speed(u16 link_status)
**/
s32 ixgbe_get_bus_info_generic(struct ixgbe_hw *hw)
{
- struct ixgbe_adapter *adapter = hw->back;
- struct ixgbe_mac_info *mac = &hw->mac;
u16 link_status;
hw->bus.type = ixgbe_bus_type_pci_express;
/* Get the negotiated link width and speed from PCI config space */
- pci_read_config_word(adapter->pdev, IXGBE_PCI_LINK_STATUS,
- &link_status);
+ link_status = ixgbe_read_pci_cfg_word(hw, IXGBE_PCI_LINK_STATUS);
hw->bus.width = ixgbe_convert_bus_width(link_status);
hw->bus.speed = ixgbe_convert_bus_speed(link_status);
- mac->ops.set_lan_id(hw);
+ hw->mac.ops.set_lan_id(hw);
return 0;
}
@@ -2406,7 +2393,6 @@ void ixgbe_fc_autoneg(struct ixgbe_hw *hw)
switch (hw->phy.media_type) {
/* Autoneg flow control on fiber adapters */
- case ixgbe_media_type_fiber_fixed:
case ixgbe_media_type_fiber:
if (speed == IXGBE_LINK_SPEED_1GB_FULL)
ret_val = ixgbe_fc_autoneg_fiber(hw);
@@ -2437,6 +2423,53 @@ out:
}
/**
+ * ixgbe_pcie_timeout_poll - Return number of times to poll for completion
+ * @hw: pointer to hardware structure
+ *
+ * System-wide timeout range is encoded in PCIe Device Control2 register.
+ *
+ * Add 10% to specified maximum and return the number of times to poll for
+ * completion timeout, in units of 100 microsec. Never return less than
+ * 800 = 80 millisec.
+ **/
+static u32 ixgbe_pcie_timeout_poll(struct ixgbe_hw *hw)
+{
+ s16 devctl2;
+ u32 pollcnt;
+
+ devctl2 = ixgbe_read_pci_cfg_word(hw, IXGBE_PCI_DEVICE_CONTROL2);
+ devctl2 &= IXGBE_PCIDEVCTRL2_TIMEO_MASK;
+
+ switch (devctl2) {
+ case IXGBE_PCIDEVCTRL2_65_130ms:
+ pollcnt = 1300; /* 130 millisec */
+ break;
+ case IXGBE_PCIDEVCTRL2_260_520ms:
+ pollcnt = 5200; /* 520 millisec */
+ break;
+ case IXGBE_PCIDEVCTRL2_1_2s:
+ pollcnt = 20000; /* 2 sec */
+ break;
+ case IXGBE_PCIDEVCTRL2_4_8s:
+ pollcnt = 80000; /* 8 sec */
+ break;
+ case IXGBE_PCIDEVCTRL2_17_34s:
+ pollcnt = 34000; /* 34 sec */
+ break;
+ case IXGBE_PCIDEVCTRL2_50_100us: /* 100 microsecs */
+ case IXGBE_PCIDEVCTRL2_1_2ms: /* 2 millisecs */
+ case IXGBE_PCIDEVCTRL2_16_32ms: /* 32 millisec */
+ case IXGBE_PCIDEVCTRL2_16_32ms_def: /* 32 millisec default */
+ default:
+ pollcnt = 800; /* 80 millisec minimum */
+ break;
+ }
+
+ /* add 10% to spec maximum */
+ return (pollcnt * 11) / 10;
+}
+
+/**
* ixgbe_disable_pcie_master - Disable PCI-express master access
* @hw: pointer to hardware structure
*
@@ -2447,16 +2480,16 @@ out:
**/
static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw)
{
- struct ixgbe_adapter *adapter = hw->back;
s32 status = 0;
- u32 i;
+ u32 i, poll;
u16 value;
/* Always set this bit to ensure any future transactions are blocked */
IXGBE_WRITE_REG(hw, IXGBE_CTRL, IXGBE_CTRL_GIO_DIS);
/* Exit if master requests are blocked */
- if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO))
+ if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO) ||
+ ixgbe_removed(hw->hw_addr))
goto out;
/* Poll for master request bit to clear */
@@ -2481,10 +2514,12 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw)
* Before proceeding, make sure that the PCIe block does not have
* transactions pending.
*/
- for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) {
+ poll = ixgbe_pcie_timeout_poll(hw);
+ for (i = 0; i < poll; i++) {
udelay(100);
- pci_read_config_word(adapter->pdev, IXGBE_PCI_DEVICE_STATUS,
- &value);
+ value = ixgbe_read_pci_cfg_word(hw, IXGBE_PCI_DEVICE_STATUS);
+ if (ixgbe_removed(hw->hw_addr))
+ goto out;
if (!(value & IXGBE_PCI_DEVICE_STATUS_TRANSACTION_PENDING))
goto out;
}
@@ -2564,6 +2599,35 @@ void ixgbe_release_swfw_sync(struct ixgbe_hw *hw, u16 mask)
}
/**
+ * prot_autoc_read_generic - Hides MAC differences needed for AUTOC read
+ * @hw: pointer to hardware structure
+ * @reg_val: Value we read from AUTOC
+ * @locked: bool to indicate whether the SW/FW lock should be taken. Never
+ * true in this the generic case.
+ *
+ * The default case requires no protection so just to the register read.
+ **/
+s32 prot_autoc_read_generic(struct ixgbe_hw *hw, bool *locked, u32 *reg_val)
+{
+ *locked = false;
+ *reg_val = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+ return 0;
+}
+
+/**
+ * prot_autoc_write_generic - Hides MAC differences needed for AUTOC write
+ * @hw: pointer to hardware structure
+ * @reg_val: value to write to AUTOC
+ * @locked: bool to indicate whether the SW/FW lock was already taken by
+ * previous read.
+ **/
+s32 prot_autoc_write_generic(struct ixgbe_hw *hw, u32 reg_val, bool locked)
+{
+ IXGBE_WRITE_REG(hw, IXGBE_AUTOC, reg_val);
+ return 0;
+}
+
+/**
* ixgbe_disable_rx_buff_generic - Stops the receive data path
* @hw: pointer to hardware structure
*
@@ -2641,6 +2705,7 @@ s32 ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index)
u32 autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC);
u32 led_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL);
s32 ret_val = 0;
+ bool locked = false;
/*
* Link must be up to auto-blink the LEDs;
@@ -2649,28 +2714,19 @@ s32 ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index)
hw->mac.ops.check_link(hw, &speed, &link_up, false);
if (!link_up) {
- /* Need the SW/FW semaphore around AUTOC writes if 82599 and
- * LESM is on.
- */
- bool got_lock = false;
-
- if ((hw->mac.type == ixgbe_mac_82599EB) &&
- ixgbe_verify_lesm_fw_enabled_82599(hw)) {
- ret_val = hw->mac.ops.acquire_swfw_sync(hw,
- IXGBE_GSSR_MAC_CSR_SM);
- if (ret_val)
- goto out;
+ ret_val = hw->mac.ops.prot_autoc_read(hw, &locked, &autoc_reg);
+ if (ret_val)
+ goto out;
- got_lock = true;
- }
autoc_reg |= IXGBE_AUTOC_AN_RESTART;
autoc_reg |= IXGBE_AUTOC_FLU;
- IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc_reg);
+
+ ret_val = hw->mac.ops.prot_autoc_write(hw, autoc_reg, locked);
+ if (ret_val)
+ goto out;
+
IXGBE_WRITE_FLUSH(hw);
- if (got_lock)
- hw->mac.ops.release_swfw_sync(hw,
- IXGBE_GSSR_MAC_CSR_SM);
usleep_range(10000, 20000);
}
@@ -2690,33 +2746,21 @@ out:
**/
s32 ixgbe_blink_led_stop_generic(struct ixgbe_hw *hw, u32 index)
{
- u32 autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+ u32 autoc_reg = 0;
u32 led_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL);
s32 ret_val = 0;
- bool got_lock = false;
+ bool locked = false;
- /* Need the SW/FW semaphore around AUTOC writes if 82599 and
- * LESM is on.
- */
- if ((hw->mac.type == ixgbe_mac_82599EB) &&
- ixgbe_verify_lesm_fw_enabled_82599(hw)) {
- ret_val = hw->mac.ops.acquire_swfw_sync(hw,
- IXGBE_GSSR_MAC_CSR_SM);
- if (ret_val)
- goto out;
-
- got_lock = true;
- }
+ ret_val = hw->mac.ops.prot_autoc_read(hw, &locked, &autoc_reg);
+ if (ret_val)
+ goto out;
autoc_reg &= ~IXGBE_AUTOC_FLU;
autoc_reg |= IXGBE_AUTOC_AN_RESTART;
- IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc_reg);
-
- if (hw->mac.type == ixgbe_mac_82599EB)
- ixgbe_reset_pipeline_82599(hw);
- if (got_lock)
- hw->mac.ops.release_swfw_sync(hw, IXGBE_GSSR_MAC_CSR_SM);
+ ret_val = hw->mac.ops.prot_autoc_write(hw, autoc_reg, locked);
+ if (ret_val)
+ goto out;
led_reg &= ~IXGBE_LED_MODE_MASK(index);
led_reg &= ~IXGBE_LED_BLINK(index);
@@ -2817,7 +2861,6 @@ san_mac_addr_clr:
**/
u16 ixgbe_get_pcie_msix_count_generic(struct ixgbe_hw *hw)
{
- struct ixgbe_adapter *adapter = hw->back;
u16 msix_count = 1;
u16 max_msix_count;
u16 pcie_offset;
@@ -2836,7 +2879,9 @@ u16 ixgbe_get_pcie_msix_count_generic(struct ixgbe_hw *hw)
return msix_count;
}
- pci_read_config_word(adapter->pdev, pcie_offset, &msix_count);
+ msix_count = ixgbe_read_pci_cfg_word(hw, pcie_offset);
+ if (ixgbe_removed(hw->hw_addr))
+ msix_count = 0;
msix_count &= IXGBE_PCIE_MSIX_TBL_SZ_MASK;
/* MSI-X count is zero-based in HW */
@@ -2868,6 +2913,9 @@ s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq)
mpsar_lo = IXGBE_READ_REG(hw, IXGBE_MPSAR_LO(rar));
mpsar_hi = IXGBE_READ_REG(hw, IXGBE_MPSAR_HI(rar));
+ if (ixgbe_removed(hw->hw_addr))
+ goto done;
+
if (!mpsar_lo && !mpsar_hi)
goto done;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
index f2e3919750ec..f12c40fb5537 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -98,6 +99,10 @@ s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw,
bool *link_up, bool link_up_wait_to_complete);
s32 ixgbe_get_wwn_prefix_generic(struct ixgbe_hw *hw, u16 *wwnn_prefix,
u16 *wwpn_prefix);
+
+s32 prot_autoc_read_generic(struct ixgbe_hw *hw, bool *, u32 *reg_val);
+s32 prot_autoc_write_generic(struct ixgbe_hw *hw, u32 reg_val, bool locked);
+
s32 ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index);
s32 ixgbe_blink_led_stop_generic(struct ixgbe_hw *hw, u32 index);
void ixgbe_set_mac_anti_spoofing(struct ixgbe_hw *hw, bool enable, int pf);
@@ -106,10 +111,10 @@ s32 ixgbe_get_device_caps_generic(struct ixgbe_hw *hw, u16 *device_caps);
s32 ixgbe_set_fw_drv_ver_generic(struct ixgbe_hw *hw, u8 maj, u8 min,
u8 build, u8 ver);
void ixgbe_clear_tx_pending(struct ixgbe_hw *hw);
+bool ixgbe_mng_enabled(struct ixgbe_hw *hw);
void ixgbe_set_rxpba_generic(struct ixgbe_hw *hw, int num_pb,
u32 headroom, int strategy);
-s32 ixgbe_reset_pipeline_82599(struct ixgbe_hw *hw);
#define IXGBE_I2C_THERMAL_SENSOR_ADDR 0xF8
#define IXGBE_EMC_INTERNAL_DATA 0x00
@@ -125,6 +130,11 @@ s32 ixgbe_get_thermal_sensor_data_generic(struct ixgbe_hw *hw);
s32 ixgbe_init_thermal_sensor_thresh_generic(struct ixgbe_hw *hw);
#define IXGBE_FAILED_READ_REG 0xffffffffU
+#define IXGBE_FAILED_READ_CFG_DWORD 0xffffffffU
+#define IXGBE_FAILED_READ_CFG_WORD 0xffffU
+
+u16 ixgbe_read_pci_cfg_word(struct ixgbe_hw *hw, u32 reg);
+void ixgbe_write_pci_cfg_word(struct ixgbe_hw *hw, u32 reg, u16 value);
static inline bool ixgbe_removed(void __iomem *addr)
{
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
index 05e23b80b5e3..bdb99b3b0f30 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h
index d71d9ce3e394..d5a1e3db0774 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c
index c5933f6dceee..472b0f450bf9 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
index 043307024c4a..6c55c14d082a 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -1127,10 +1128,10 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
}
do {
- start = u64_stats_fetch_begin_bh(&ring->syncp);
+ start = u64_stats_fetch_begin_irq(&ring->syncp);
data[i] = ring->stats.packets;
data[i+1] = ring->stats.bytes;
- } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
i += 2;
#ifdef BP_EXTENDED_STATS
data[i] = ring->stats.yields;
@@ -1155,10 +1156,10 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
}
do {
- start = u64_stats_fetch_begin_bh(&ring->syncp);
+ start = u64_stats_fetch_begin_irq(&ring->syncp);
data[i] = ring->stats.packets;
data[i+1] = ring->stats.bytes;
- } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
i += 2;
#ifdef BP_EXTENDED_STATS
data[i] = ring->stats.yields;
@@ -1247,6 +1248,11 @@ static int ixgbe_link_test(struct ixgbe_adapter *adapter, u64 *data)
struct ixgbe_hw *hw = &adapter->hw;
bool link_up;
u32 link_speed = 0;
+
+ if (ixgbe_removed(hw->hw_addr)) {
+ *data = 1;
+ return 1;
+ }
*data = 0;
hw->mac.ops.check_link(hw, &link_speed, &link_up, true);
@@ -1969,6 +1975,7 @@ static void ixgbe_diag_test(struct net_device *netdev,
data[1] = 1;
data[2] = 1;
data[3] = 1;
+ data[4] = 1;
eth_test->flags |= ETH_TEST_FL_FAILED;
return;
}
@@ -1988,6 +1995,7 @@ static void ixgbe_diag_test(struct net_device *netdev,
data[1] = 1;
data[2] = 1;
data[3] = 1;
+ data[4] = 1;
eth_test->flags |= ETH_TEST_FL_FAILED;
clear_bit(__IXGBE_TESTING,
&adapter->state);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
index f58db453a97e..25a3dfef33e8 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -407,13 +408,13 @@ int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
switch (ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_FCSTAT)) {
/* return 0 to bypass going to ULD for DDPed data */
- case __constant_cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_DDP):
+ case cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_DDP):
/* update length of DDPed data */
ddp->len = le32_to_cpu(rx_desc->wb.lower.hi_dword.rss);
rc = 0;
break;
/* unmap the sg list when FCPRSP is received */
- case __constant_cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_FCPRSP):
+ case cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_FCPRSP):
dma_unmap_sg(&adapter->pdev->dev, ddp->sgl,
ddp->sgc, DMA_FROM_DEVICE);
ddp->err = ddp_err;
@@ -421,14 +422,14 @@ int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
ddp->sgc = 0;
/* fall through */
/* if DDP length is present pass it through to ULD */
- case __constant_cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_NODDP):
+ case cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_NODDP):
/* update length of DDPed data */
ddp->len = le32_to_cpu(rx_desc->wb.lower.hi_dword.rss);
if (ddp->len)
rc = ddp->len;
break;
/* no match will return as an error */
- case __constant_cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_NOMTCH):
+ case cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_NOMTCH):
default:
break;
}
@@ -585,7 +586,7 @@ static int ixgbe_fcoe_dma_pool_alloc(struct ixgbe_fcoe *fcoe,
struct dma_pool *pool;
char pool_name[32];
- snprintf(pool_name, 32, "ixgbe_fcoe_ddp_%d", cpu);
+ snprintf(pool_name, 32, "ixgbe_fcoe_ddp_%u", cpu);
pool = dma_pool_create(pool_name, dev, IXGBE_FCPTR_MAX,
IXGBE_FCPTR_ALIGN, PAGE_SIZE);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
index 3a02759b5e95..b16cc786750d 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
index 32e3eaaa160a..2067d392cc3d 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -698,7 +699,7 @@ static void ixgbe_set_num_queues(struct ixgbe_adapter *adapter)
static void ixgbe_acquire_msix_vectors(struct ixgbe_adapter *adapter,
int vectors)
{
- int err, vector_threshold;
+ int vector_threshold;
/* We'll want at least 2 (vector_threshold):
* 1) TxQ[0] + RxQ[0] handler
@@ -712,18 +713,10 @@ static void ixgbe_acquire_msix_vectors(struct ixgbe_adapter *adapter,
* Right now, we simply care about how many we'll get; we'll
* set them up later while requesting irq's.
*/
- while (vectors >= vector_threshold) {
- err = pci_enable_msix(adapter->pdev, adapter->msix_entries,
- vectors);
- if (!err) /* Success in acquiring all requested vectors. */
- break;
- else if (err < 0)
- vectors = 0; /* Nasty failure, quit now */
- else /* err == number of vectors we should try again with */
- vectors = err;
- }
+ vectors = pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
+ vector_threshold, vectors);
- if (vectors < vector_threshold) {
+ if (vectors < 0) {
/* Can't allocate enough MSI-X interrupts? Oh well.
* This just means we'll go with either a single MSI
* vector or fall back to legacy interrupts.
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 18076c4178b4..8436c651b735 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -67,7 +68,7 @@ static char ixgbe_default_device_descr[] =
#define DRV_VERSION "3.19.1-k"
const char ixgbe_driver_version[] = DRV_VERSION;
static const char ixgbe_copyright[] =
- "Copyright (c) 1999-2013 Intel Corporation.";
+ "Copyright (c) 1999-2014 Intel Corporation.";
static const struct ixgbe_info *ixgbe_info_tbl[] = {
[board_82598] = &ixgbe_82598_info,
@@ -151,6 +152,8 @@ MODULE_DESCRIPTION("Intel(R) 10 Gigabit PCI Express Network Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
+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,
u32 reg, u16 *value)
{
@@ -169,6 +172,9 @@ static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter,
return -1;
pcie_capability_read_word(parent_dev, reg, value);
+ if (*value == IXGBE_FAILED_READ_CFG_WORD &&
+ ixgbe_check_cfg_remove(&adapter->hw, parent_dev))
+ return -1;
return 0;
}
@@ -313,6 +319,57 @@ void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg)
ixgbe_remove_adapter(hw);
}
+static bool ixgbe_check_cfg_remove(struct ixgbe_hw *hw, struct pci_dev *pdev)
+{
+ u16 value;
+
+ pci_read_config_word(pdev, PCI_VENDOR_ID, &value);
+ if (value == IXGBE_FAILED_READ_CFG_WORD) {
+ ixgbe_remove_adapter(hw);
+ return true;
+ }
+ return false;
+}
+
+u16 ixgbe_read_pci_cfg_word(struct ixgbe_hw *hw, u32 reg)
+{
+ struct ixgbe_adapter *adapter = hw->back;
+ u16 value;
+
+ if (ixgbe_removed(hw->hw_addr))
+ return IXGBE_FAILED_READ_CFG_WORD;
+ pci_read_config_word(adapter->pdev, reg, &value);
+ if (value == IXGBE_FAILED_READ_CFG_WORD &&
+ ixgbe_check_cfg_remove(hw, adapter->pdev))
+ return IXGBE_FAILED_READ_CFG_WORD;
+ return value;
+}
+
+#ifdef CONFIG_PCI_IOV
+static u32 ixgbe_read_pci_cfg_dword(struct ixgbe_hw *hw, u32 reg)
+{
+ struct ixgbe_adapter *adapter = hw->back;
+ u32 value;
+
+ if (ixgbe_removed(hw->hw_addr))
+ return IXGBE_FAILED_READ_CFG_DWORD;
+ pci_read_config_dword(adapter->pdev, reg, &value);
+ if (value == IXGBE_FAILED_READ_CFG_DWORD &&
+ ixgbe_check_cfg_remove(hw, adapter->pdev))
+ return IXGBE_FAILED_READ_CFG_DWORD;
+ return value;
+}
+#endif /* CONFIG_PCI_IOV */
+
+void ixgbe_write_pci_cfg_word(struct ixgbe_hw *hw, u32 reg, u16 value)
+{
+ struct ixgbe_adapter *adapter = hw->back;
+
+ if (ixgbe_removed(hw->hw_addr))
+ return;
+ pci_write_config_word(adapter->pdev, reg, value);
+}
+
static void ixgbe_service_event_complete(struct ixgbe_adapter *adapter)
{
BUG_ON(!test_bit(__IXGBE_SERVICE_SCHED, &adapter->state));
@@ -1264,7 +1321,9 @@ static inline void ixgbe_rx_hash(struct ixgbe_ring *ring,
struct sk_buff *skb)
{
if (ring->netdev->features & NETIF_F_RXHASH)
- skb->rxhash = le32_to_cpu(rx_desc->wb.lower.hi_dword.rss);
+ skb_set_hash(skb,
+ le32_to_cpu(rx_desc->wb.lower.hi_dword.rss),
+ PKT_HASH_TYPE_L3);
}
#ifdef IXGBE_FCOE
@@ -1480,7 +1539,7 @@ static unsigned int ixgbe_get_headlen(unsigned char *data,
hdr.network += ETH_HLEN;
/* handle any vlan tag if present */
- if (protocol == __constant_htons(ETH_P_8021Q)) {
+ if (protocol == htons(ETH_P_8021Q)) {
if ((hdr.network - data) > (max_len - VLAN_HLEN))
return max_len;
@@ -1489,7 +1548,7 @@ static unsigned int ixgbe_get_headlen(unsigned char *data,
}
/* handle L3 protocols */
- if (protocol == __constant_htons(ETH_P_IP)) {
+ if (protocol == htons(ETH_P_IP)) {
if ((hdr.network - data) > (max_len - sizeof(struct iphdr)))
return max_len;
@@ -1503,7 +1562,7 @@ static unsigned int ixgbe_get_headlen(unsigned char *data,
/* record next protocol if header is present */
if (!(hdr.ipv4->frag_off & htons(IP_OFFSET)))
nexthdr = hdr.ipv4->protocol;
- } else if (protocol == __constant_htons(ETH_P_IPV6)) {
+ } else if (protocol == htons(ETH_P_IPV6)) {
if ((hdr.network - data) > (max_len - sizeof(struct ipv6hdr)))
return max_len;
@@ -1511,7 +1570,7 @@ static unsigned int ixgbe_get_headlen(unsigned char *data,
nexthdr = hdr.ipv6->nexthdr;
hlen = sizeof(struct ipv6hdr);
#ifdef IXGBE_FCOE
- } else if (protocol == __constant_htons(ETH_P_FCOE)) {
+ } else if (protocol == htons(ETH_P_FCOE)) {
if ((hdr.network - data) > (max_len - FCOE_HEADER_LEN))
return max_len;
hlen = FCOE_HEADER_LEN;
@@ -2026,7 +2085,7 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
#endif /* IXGBE_FCOE */
u16 cleaned_count = ixgbe_desc_unused(rx_ring);
- do {
+ while (likely(total_rx_packets < budget)) {
union ixgbe_adv_rx_desc *rx_desc;
struct sk_buff *skb;
@@ -2101,7 +2160,7 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
/* update budget accounting */
total_rx_packets++;
- } while (likely(total_rx_packets < budget));
+ }
u64_stats_update_begin(&rx_ring->syncp);
rx_ring->stats.packets += total_rx_packets;
@@ -2630,9 +2689,12 @@ static irqreturn_t ixgbe_msix_other(int irq, void *data)
switch (hw->mac.type) {
case ixgbe_mac_82599EB:
case ixgbe_mac_X540:
- if (eicr & IXGBE_EICR_ECC)
- e_info(link, "Received unrecoverable ECC Err, please "
- "reboot\n");
+ if (eicr & IXGBE_EICR_ECC) {
+ e_info(link, "Received ECC Err, initiating reset\n");
+ adapter->flags2 |= IXGBE_FLAG2_RESET_REQUESTED;
+ ixgbe_service_event_schedule(adapter);
+ IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_ECC);
+ }
/* Handle Flow Director Full threshold interrupt */
if (eicr & IXGBE_EICR_FLOW_DIR) {
int reinit_count = 0;
@@ -2846,9 +2908,12 @@ static irqreturn_t ixgbe_intr(int irq, void *data)
ixgbe_check_sfp_event(adapter, eicr);
/* Fall through */
case ixgbe_mac_X540:
- if (eicr & IXGBE_EICR_ECC)
- e_info(link, "Received unrecoverable ECC err, please "
- "reboot\n");
+ if (eicr & IXGBE_EICR_ECC) {
+ e_info(link, "Received ECC Err, initiating reset\n");
+ adapter->flags2 |= IXGBE_FLAG2_RESET_REQUESTED;
+ ixgbe_service_event_schedule(adapter);
+ IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_ECC);
+ }
ixgbe_check_overtemp_event(adapter, eicr);
break;
default:
@@ -4590,8 +4655,6 @@ static void ixgbe_setup_gpie(struct ixgbe_adapter *adapter)
static void ixgbe_up_complete(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
- struct net_device *upper;
- struct list_head *iter;
int err;
u32 ctrl_ext;
@@ -4633,19 +4696,6 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter)
e_crit(drv, "Fan has stopped, replace the adapter\n");
}
- /* enable transmits */
- netif_tx_start_all_queues(adapter->netdev);
-
- /* enable any upper devices */
- netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) {
- if (netif_is_macvlan(upper)) {
- struct macvlan_dev *vlan = netdev_priv(upper);
-
- if (vlan->fwd_priv)
- netif_tx_start_all_queues(upper);
- }
- }
-
/* bring the link up in the watchdog, this could race with our first
* link up interrupt but shouldn't be a problem */
adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE;
@@ -5502,6 +5552,7 @@ static int ixgbe_resume(struct pci_dev *pdev)
struct net_device *netdev = adapter->netdev;
u32 err;
+ adapter->hw.hw_addr = adapter->io_addr;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
/*
@@ -5515,6 +5566,8 @@ static int ixgbe_resume(struct pci_dev *pdev)
e_dev_err("Cannot enable PCI device from suspend\n");
return err;
}
+ smp_mb__before_clear_bit();
+ clear_bit(__IXGBE_DISABLED, &adapter->state);
pci_set_master(pdev);
pci_wake_from_d3(pdev, false);
@@ -5612,7 +5665,8 @@ static int __ixgbe_shutdown(struct pci_dev *pdev, bool *enable_wake)
ixgbe_release_hw_control(adapter);
- pci_disable_device(pdev);
+ if (!test_and_set_bit(__IXGBE_DISABLED, &adapter->state))
+ pci_disable_device(pdev);
return 0;
}
@@ -6016,6 +6070,8 @@ static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
struct ixgbe_hw *hw = &adapter->hw;
+ struct net_device *upper;
+ struct list_head *iter;
u32 link_speed = adapter->link_speed;
bool flow_rx, flow_tx;
@@ -6067,6 +6123,21 @@ static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *adapter)
netif_carrier_on(netdev);
ixgbe_check_vf_rate_limit(adapter);
+ /* enable transmits */
+ netif_tx_wake_all_queues(adapter->netdev);
+
+ /* enable any upper devices */
+ rtnl_lock();
+ netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) {
+ if (netif_is_macvlan(upper)) {
+ struct macvlan_dev *vlan = netdev_priv(upper);
+
+ if (vlan->fwd_priv)
+ netif_tx_wake_all_queues(upper);
+ }
+ }
+ rtnl_unlock();
+
/* update the default user priority for VFs */
ixgbe_update_default_up(adapter);
@@ -6454,7 +6525,7 @@ static int ixgbe_tso(struct ixgbe_ring *tx_ring,
/* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */
type_tucmd = IXGBE_ADVTXD_TUCMD_L4T_TCP;
- if (first->protocol == __constant_htons(ETH_P_IP)) {
+ if (first->protocol == htons(ETH_P_IP)) {
struct iphdr *iph = ip_hdr(skb);
iph->tot_len = 0;
iph->check = 0;
@@ -6514,12 +6585,12 @@ static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring,
} else {
u8 l4_hdr = 0;
switch (first->protocol) {
- case __constant_htons(ETH_P_IP):
+ case htons(ETH_P_IP):
vlan_macip_lens |= skb_network_header_len(skb);
type_tucmd |= IXGBE_ADVTXD_TUCMD_IPV4;
l4_hdr = ip_hdr(skb)->protocol;
break;
- case __constant_htons(ETH_P_IPV6):
+ case htons(ETH_P_IPV6):
vlan_macip_lens |= skb_network_header_len(skb);
l4_hdr = ipv6_hdr(skb)->nexthdr;
break;
@@ -6794,9 +6865,9 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
hdr.network = skb_network_header(first->skb);
/* Currently only IPv4/IPv6 with TCP is supported */
- if ((first->protocol != __constant_htons(ETH_P_IPV6) ||
+ if ((first->protocol != htons(ETH_P_IPV6) ||
hdr.ipv6->nexthdr != IPPROTO_TCP) &&
- (first->protocol != __constant_htons(ETH_P_IP) ||
+ (first->protocol != htons(ETH_P_IP) ||
hdr.ipv4->protocol != IPPROTO_TCP))
return;
@@ -6829,12 +6900,12 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
* and write the value to source port portion of compressed dword
*/
if (first->tx_flags & (IXGBE_TX_FLAGS_SW_VLAN | IXGBE_TX_FLAGS_HW_VLAN))
- common.port.src ^= th->dest ^ __constant_htons(ETH_P_8021Q);
+ common.port.src ^= th->dest ^ htons(ETH_P_8021Q);
else
common.port.src ^= th->dest ^ first->protocol;
common.port.dst ^= th->source;
- if (first->protocol == __constant_htons(ETH_P_IP)) {
+ if (first->protocol == htons(ETH_P_IP)) {
input.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4;
common.ip ^= hdr.ipv4->saddr ^ hdr.ipv4->daddr;
} else {
@@ -6900,8 +6971,8 @@ static u16 ixgbe_select_queue(struct net_device *dev, struct sk_buff *skb,
* or FIP and we have FCoE enabled on the adapter
*/
switch (vlan_get_protocol(skb)) {
- case __constant_htons(ETH_P_FCOE):
- case __constant_htons(ETH_P_FIP):
+ case htons(ETH_P_FCOE):
+ case htons(ETH_P_FIP):
adapter = netdev_priv(dev);
if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED)
@@ -6962,7 +7033,7 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
tx_flags |= vlan_tx_tag_get(skb) << IXGBE_TX_FLAGS_VLAN_SHIFT;
tx_flags |= IXGBE_TX_FLAGS_HW_VLAN;
/* else if it is a SW VLAN check the next protocol and store the tag */
- } else if (protocol == __constant_htons(ETH_P_8021Q)) {
+ } else if (protocol == htons(ETH_P_8021Q)) {
struct vlan_hdr *vhdr, _vhdr;
vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), &_vhdr);
if (!vhdr)
@@ -6974,9 +7045,9 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
tx_flags |= IXGBE_TX_FLAGS_SW_VLAN;
}
- skb_tx_timestamp(skb);
-
- if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
+ !test_and_set_bit_lock(__IXGBE_PTP_TX_IN_PROGRESS,
+ &adapter->state))) {
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
tx_flags |= IXGBE_TX_FLAGS_TSTAMP;
@@ -6986,6 +7057,8 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
schedule_work(&adapter->ptp_tx_work);
}
+ skb_tx_timestamp(skb);
+
#ifdef CONFIG_PCI_IOV
/*
* Use the l2switch_enable flag - would be false if the DMA
@@ -7021,7 +7094,7 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
#ifdef IXGBE_FCOE
/* setup tx offload for FCoE */
- if ((protocol == __constant_htons(ETH_P_FCOE)) &&
+ if ((protocol == htons(ETH_P_FCOE)) &&
(tx_ring->netdev->features & (NETIF_F_FSO | NETIF_F_FCOE_CRC))) {
tso = ixgbe_fso(tx_ring, first, &hdr_len);
if (tso < 0)
@@ -7143,7 +7216,9 @@ static int ixgbe_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
switch (cmd) {
case SIOCSHWTSTAMP:
- return ixgbe_ptp_hwtstamp_ioctl(adapter, req, cmd);
+ return ixgbe_ptp_set_ts_config(adapter, req);
+ case SIOCGHWTSTAMP:
+ return ixgbe_ptp_get_ts_config(adapter, req);
default:
return mdio_mii_ioctl(&adapter->hw.phy.mdio, if_mii(req), cmd);
}
@@ -7234,10 +7309,10 @@ static struct rtnl_link_stats64 *ixgbe_get_stats64(struct net_device *netdev,
if (ring) {
do {
- start = u64_stats_fetch_begin_bh(&ring->syncp);
+ start = u64_stats_fetch_begin_irq(&ring->syncp);
packets = ring->stats.packets;
bytes = ring->stats.bytes;
- } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
stats->rx_packets += packets;
stats->rx_bytes += bytes;
}
@@ -7250,10 +7325,10 @@ static struct rtnl_link_stats64 *ixgbe_get_stats64(struct net_device *netdev,
if (ring) {
do {
- start = u64_stats_fetch_begin_bh(&ring->syncp);
+ start = u64_stats_fetch_begin_irq(&ring->syncp);
packets = ring->stats.packets;
bytes = ring->stats.bytes;
- } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
stats->tx_packets += packets;
stats->tx_bytes += bytes;
}
@@ -7792,6 +7867,7 @@ int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id,
case IXGBE_DEV_ID_82599_SFP:
/* Only these subdevices could supports WOL */
switch (subdevice_id) {
+ case IXGBE_SUBDEV_ID_82599_SFP_WOL0:
case IXGBE_SUBDEV_ID_82599_560FLR:
/* only support first port */
if (hw->bus.func != 0)
@@ -7969,10 +8045,6 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
goto err_sw_init;
- /* Cache if MNG FW is up so we don't have to read the REG later */
- if (hw->mac.ops.mng_fw_enabled)
- hw->mng_fw_enabled = hw->mac.ops.mng_fw_enabled(hw);
-
/* Make it possible the adapter to be woken up via WOL */
switch (adapter->hw.mac.type) {
case ixgbe_mac_82599EB:
@@ -8223,7 +8295,7 @@ skip_sriov:
ixgbe_dbg_adapter_init(adapter);
/* Need link setup for MNG FW, else wait for IXGBE_UP */
- if (hw->mng_fw_enabled && hw->mac.ops.setup_link)
+ if (ixgbe_mng_enabled(hw) && hw->mac.ops.setup_link)
hw->mac.ops.setup_link(hw,
IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL,
true);
@@ -8244,7 +8316,8 @@ err_alloc_etherdev:
pci_select_bars(pdev, IORESOURCE_MEM));
err_pci_reg:
err_dma:
- pci_disable_device(pdev);
+ if (!test_and_set_bit(__IXGBE_DISABLED, &adapter->state))
+ pci_disable_device(pdev);
return err;
}
@@ -8313,7 +8386,8 @@ static void ixgbe_remove(struct pci_dev *pdev)
pci_disable_pcie_error_reporting(pdev);
- pci_disable_device(pdev);
+ if (!test_and_set_bit(__IXGBE_DISABLED, &adapter->state))
+ pci_disable_device(pdev);
}
/**
@@ -8331,6 +8405,7 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
struct net_device *netdev = adapter->netdev;
#ifdef CONFIG_PCI_IOV
+ struct ixgbe_hw *hw = &adapter->hw;
struct pci_dev *bdev, *vfdev;
u32 dw0, dw1, dw2, dw3;
int vf, pos;
@@ -8351,10 +8426,12 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
if (!pos)
goto skip_bad_vf_detection;
- pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG, &dw0);
- pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 4, &dw1);
- pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 8, &dw2);
- pci_read_config_dword(bdev, pos + PCI_ERR_HEADER_LOG + 12, &dw3);
+ dw0 = ixgbe_read_pci_cfg_dword(hw, pos + PCI_ERR_HEADER_LOG);
+ dw1 = ixgbe_read_pci_cfg_dword(hw, pos + PCI_ERR_HEADER_LOG + 4);
+ dw2 = ixgbe_read_pci_cfg_dword(hw, pos + PCI_ERR_HEADER_LOG + 8);
+ dw3 = ixgbe_read_pci_cfg_dword(hw, pos + PCI_ERR_HEADER_LOG + 12);
+ if (ixgbe_removed(hw->hw_addr))
+ goto skip_bad_vf_detection;
req_id = dw1 >> 16;
/* On the 82599 if bit 7 of the requestor ID is set then it's a VF */
@@ -8417,14 +8494,20 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
skip_bad_vf_detection:
#endif /* CONFIG_PCI_IOV */
+ rtnl_lock();
netif_device_detach(netdev);
- if (state == pci_channel_io_perm_failure)
+ if (state == pci_channel_io_perm_failure) {
+ rtnl_unlock();
return PCI_ERS_RESULT_DISCONNECT;
+ }
if (netif_running(netdev))
ixgbe_down(adapter);
- pci_disable_device(pdev);
+
+ if (!test_and_set_bit(__IXGBE_DISABLED, &adapter->state))
+ pci_disable_device(pdev);
+ rtnl_unlock();
/* Request a slot reset. */
return PCI_ERS_RESULT_NEED_RESET;
@@ -8446,6 +8529,9 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev)
e_err(probe, "Cannot re-enable PCI device after reset.\n");
result = PCI_ERS_RESULT_DISCONNECT;
} else {
+ smp_mb__before_clear_bit();
+ clear_bit(__IXGBE_DISABLED, &adapter->state);
+ adapter->hw.hw_addr = adapter->io_addr;
pci_set_master(pdev);
pci_restore_state(pdev);
pci_save_state(pdev);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c
index cc3101afd29f..f5c6af2b891b 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
index e44ff47659b5..a9b9ad69ed0e 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
index 132557c318f8..23f765263f12 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -98,6 +99,32 @@ s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw)
}
/**
+ * ixgbe_check_reset_blocked - check status of MNG FW veto bit
+ * @hw: pointer to the hardware structure
+ *
+ * This function checks the MMNGC.MNG_VETO bit to see if there are
+ * any constraints on link from manageability. For MAC's that don't
+ * have this bit just return false since the link can not be blocked
+ * via this method.
+ **/
+bool ixgbe_check_reset_blocked(struct ixgbe_hw *hw)
+{
+ u32 mmngc;
+
+ /* If we don't have this bit, it can't be blocking */
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ return false;
+
+ mmngc = IXGBE_READ_REG(hw, IXGBE_MMNGC);
+ if (mmngc & IXGBE_MMNGC_MNG_VETO) {
+ hw_dbg(hw, "MNG_VETO bit detected.\n");
+ return true;
+ }
+
+ return false;
+}
+
+/**
* ixgbe_get_phy_id - Get the phy type
* @hw: pointer to hardware structure
*
@@ -172,6 +199,10 @@ s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw)
(IXGBE_ERR_OVERTEMP == hw->phy.ops.check_overtemp(hw)))
goto out;
+ /* Blocked by MNG FW so bail */
+ if (ixgbe_check_reset_blocked(hw))
+ goto out;
+
/*
* Perform soft PHY reset to the PHY_XS.
* This will cause a soft reset to the PHY
@@ -476,6 +507,10 @@ s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw)
autoneg_reg);
}
+ /* Blocked by MNG FW so don't reset PHY */
+ if (ixgbe_check_reset_blocked(hw))
+ return status;
+
/* Restart PHY autonegotiation and wait for completion */
hw->phy.ops.read_reg(hw, MDIO_CTRL1,
MDIO_MMD_AN, &autoneg_reg);
@@ -682,6 +717,10 @@ s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw)
autoneg_reg);
}
+ /* Blocked by MNG FW so don't reset PHY */
+ if (ixgbe_check_reset_blocked(hw))
+ return status;
+
/* Restart PHY autonegotiation and wait for completion */
hw->phy.ops.read_reg(hw, MDIO_CTRL1,
MDIO_MMD_AN, &autoneg_reg);
@@ -759,6 +798,10 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
s32 ret_val = 0;
u32 i;
+ /* Blocked by MNG FW so bail */
+ if (ixgbe_check_reset_blocked(hw))
+ goto out;
+
hw->phy.ops.read_reg(hw, MDIO_CTRL1, MDIO_MMD_PHYXS, &phy_data);
/* reset the PHY and poll for completion */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
index fffcbdd2bf0e..0bb047f751c2 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -65,9 +66,6 @@
#define IXGBE_SFF_1GBASET_CAPABLE 0x8
#define IXGBE_SFF_10GBASESR_CAPABLE 0x10
#define IXGBE_SFF_10GBASELR_CAPABLE 0x20
-#define IXGBE_SFF_SOFT_RS_SELECT_MASK 0x8
-#define IXGBE_SFF_SOFT_RS_SELECT_10G 0x8
-#define IXGBE_SFF_SOFT_RS_SELECT_1G 0x0
#define IXGBE_SFF_ADDRESSING_MODE 0x4
#define IXGBE_SFF_QSFP_DA_ACTIVE_CABLE 0x1
#define IXGBE_SFF_QSFP_DA_PASSIVE_CABLE 0x8
@@ -79,7 +77,6 @@
#define IXGBE_I2C_EEPROM_STATUS_PASS 0x1
#define IXGBE_I2C_EEPROM_STATUS_FAIL 0x2
#define IXGBE_I2C_EEPROM_STATUS_IN_PROGRESS 0x3
-
/* Flow control defines */
#define IXGBE_TAF_SYM_PAUSE 0x400
#define IXGBE_TAF_ASM_PAUSE 0x800
@@ -131,6 +128,7 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw,
ixgbe_link_speed *speed,
bool *autoneg);
+bool ixgbe_check_reset_blocked(struct ixgbe_hw *hw);
/* PHY specific */
s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw,
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
index 5184e2a1a7d8..63515a6f67fa 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -492,6 +493,7 @@ static void ixgbe_ptp_tx_hwtstamp(struct ixgbe_adapter *adapter)
dev_kfree_skb_any(adapter->ptp_tx_skb);
adapter->ptp_tx_skb = NULL;
+ clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state);
}
/**
@@ -511,13 +513,10 @@ static void ixgbe_ptp_tx_hwtstamp_work(struct work_struct *work)
IXGBE_PTP_TX_TIMEOUT);
u32 tsynctxctl;
- /* we have to have a valid skb */
- if (!adapter->ptp_tx_skb)
- return;
-
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");
return;
}
@@ -576,14 +575,21 @@ void __ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector,
shhwtstamps->hwtstamp = ns_to_ktime(ns);
}
+int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
+{
+ struct hwtstamp_config *config = &adapter->tstamp_config;
+
+ return copy_to_user(ifr->ifr_data, config,
+ sizeof(*config)) ? -EFAULT : 0;
+}
+
/**
- * ixgbe_ptp_hwtstamp_ioctl - control hardware time stamping
+ * ixgbe_ptp_set_ts_config - control hardware time stamping
* @adapter: pointer to adapter struct
* @ifreq: ioctl data
- * @cmd: particular ioctl requested
*
* Outgoing time stamping can be enabled and disabled. Play nice and
- * disable it when requested, although it shouldn't case any overhead
+ * disable it when requested, although it shouldn't cause any overhead
* when no packet needs it. At most one packet in the queue may be
* marked for time stamping, otherwise it would be impossible to tell
* for sure to which packet the hardware time stamp belongs.
@@ -599,8 +605,7 @@ void __ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector,
* Event mode. This more accurately tells the user what the hardware is going
* to do anyways.
*/
-int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter,
- struct ifreq *ifr, int cmd)
+int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
{
struct ixgbe_hw *hw = &adapter->hw;
struct hwtstamp_config config;
@@ -702,6 +707,10 @@ int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter,
regval = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
regval = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
+ /* save these settings for future reference */
+ memcpy(&adapter->tstamp_config, &config,
+ sizeof(adapter->tstamp_config));
+
return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
-EFAULT : 0;
}
@@ -809,6 +818,9 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
IXGBE_WRITE_REG(hw, IXGBE_SYSTIMH, 0x00000000);
IXGBE_WRITE_FLUSH(hw);
+ /* Reset the saved tstamp_config */
+ memset(&adapter->tstamp_config, 0, sizeof(adapter->tstamp_config));
+
ixgbe_ptp_start_cyclecounter(adapter);
spin_lock_irqsave(&adapter->tmreg_lock, flags);
@@ -840,7 +852,9 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
switch (adapter->hw.mac.type) {
case ixgbe_mac_X540:
- snprintf(adapter->ptp_caps.name, 16, "%s", netdev->name);
+ snprintf(adapter->ptp_caps.name,
+ sizeof(adapter->ptp_caps.name),
+ "%s", netdev->name);
adapter->ptp_caps.owner = THIS_MODULE;
adapter->ptp_caps.max_adj = 250000000;
adapter->ptp_caps.n_alarm = 0;
@@ -854,7 +868,9 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
adapter->ptp_caps.enable = ixgbe_ptp_enable;
break;
case ixgbe_mac_82599EB:
- snprintf(adapter->ptp_caps.name, 16, "%s", netdev->name);
+ snprintf(adapter->ptp_caps.name,
+ sizeof(adapter->ptp_caps.name),
+ "%s", netdev->name);
adapter->ptp_caps.owner = THIS_MODULE;
adapter->ptp_caps.max_adj = 250000000;
adapter->ptp_caps.n_alarm = 0;
@@ -911,6 +927,7 @@ void ixgbe_ptp_stop(struct ixgbe_adapter *adapter)
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);
}
if (adapter->ptp_clock) {
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
index dff0977876f7..e6c68d396c99 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
index 8bd29190514e..139eaddfb2ed 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c
index e74ae3682733..ef6df3d6437e 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
index 0d39cfc4a3bf..8a6ff2423f07 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -54,6 +55,7 @@
#define IXGBE_DEV_ID_82599_BACKPLANE_FCOE 0x152a
#define IXGBE_DEV_ID_82599_SFP_FCOE 0x1529
#define IXGBE_SUBDEV_ID_82599_SFP 0x11A9
+#define IXGBE_SUBDEV_ID_82599_SFP_WOL0 0x1071
#define IXGBE_SUBDEV_ID_82599_RNDC 0x1F72
#define IXGBE_SUBDEV_ID_82599_560FLR 0x17D0
#define IXGBE_SUBDEV_ID_82599_SP_560FLR 0x211B
@@ -1609,6 +1611,9 @@ enum {
#define IXGBE_MACC_FS 0x00040000
#define IXGBE_MAC_RX2TX_LPBK 0x00000002
+/* Veto Bit definiton */
+#define IXGBE_MMNGC_MNG_VETO 0x00000001
+
/* LINKS Bit Masks */
#define IXGBE_LINKS_KX_AN_COMP 0x80000000
#define IXGBE_LINKS_UP 0x40000000
@@ -1788,6 +1793,9 @@ enum {
#define IXGBE_EEPROM_RD_BUFFER_MAX_COUNT 512 /* EEPROM words # read in burst */
#define IXGBE_EEPROM_WR_BUFFER_MAX_COUNT 256 /* EEPROM words # wr in burst */
+#define IXGBE_EEPROM_CTRL_2 1 /* EEPROM CTRL word 2 */
+#define IXGBE_EEPROM_CCD_BIT 2 /* EEPROM Core Clock Disable bit */
+
#ifndef IXGBE_EEPROM_GRANT_ATTEMPTS
#define IXGBE_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */
#endif
@@ -1853,8 +1861,19 @@ enum {
#define IXGBE_PCI_HEADER_TYPE_MULTIFUNC 0x80
#define IXGBE_PCI_DEVICE_CONTROL2_16ms 0x0005
+#define IXGBE_PCIDEVCTRL2_TIMEO_MASK 0xf
+#define IXGBE_PCIDEVCTRL2_16_32ms_def 0x0
+#define IXGBE_PCIDEVCTRL2_50_100us 0x1
+#define IXGBE_PCIDEVCTRL2_1_2ms 0x2
+#define IXGBE_PCIDEVCTRL2_16_32ms 0x5
+#define IXGBE_PCIDEVCTRL2_65_130ms 0x6
+#define IXGBE_PCIDEVCTRL2_260_520ms 0x9
+#define IXGBE_PCIDEVCTRL2_1_2s 0xa
+#define IXGBE_PCIDEVCTRL2_4_8s 0xd
+#define IXGBE_PCIDEVCTRL2_17_34s 0xe
+
/* Number of 100 microseconds we wait for PCI Express master disable */
-#define IXGBE_PCI_MASTER_DISABLE_TIMEOUT 800
+#define IXGBE_PCI_MASTER_DISABLE_TIMEOUT 800
/* RAH */
#define IXGBE_RAH_VIND_MASK 0x003C0000
@@ -2645,7 +2664,6 @@ enum ixgbe_sfp_type {
enum ixgbe_media_type {
ixgbe_media_type_unknown = 0,
ixgbe_media_type_fiber,
- ixgbe_media_type_fiber_fixed,
ixgbe_media_type_fiber_qsfp,
ixgbe_media_type_fiber_lco,
ixgbe_media_type_copper,
@@ -2858,6 +2876,8 @@ struct ixgbe_mac_operations {
s32 (*enable_rx_dma)(struct ixgbe_hw *, u32);
s32 (*acquire_swfw_sync)(struct ixgbe_hw *, u16);
void (*release_swfw_sync)(struct ixgbe_hw *, u16);
+ s32 (*prot_autoc_read)(struct ixgbe_hw *, bool *, u32 *);
+ s32 (*prot_autoc_write)(struct ixgbe_hw *, u32, bool);
/* Link */
void (*disable_tx_laser)(struct ixgbe_hw *);
@@ -2901,7 +2921,6 @@ struct ixgbe_mac_operations {
s32 (*set_fw_drv_ver)(struct ixgbe_hw *, u8, u8, u8, u8);
s32 (*get_thermal_sensor_data)(struct ixgbe_hw *);
s32 (*init_thermal_sensor_thresh)(struct ixgbe_hw *hw);
- bool (*mng_fw_enabled)(struct ixgbe_hw *hw);
};
struct ixgbe_phy_operations {
@@ -2957,7 +2976,6 @@ struct ixgbe_mac_info {
u32 max_tx_queues;
u32 max_rx_queues;
u32 orig_autoc;
- u32 cached_autoc;
u32 orig_autoc2;
bool orig_link_settings_stored;
bool autotry_restart;
@@ -3033,7 +3051,6 @@ struct ixgbe_hw {
bool adapter_stopped;
bool force_full_reset;
bool allow_unsupported_sfp;
- bool mng_fw_enabled;
bool wol_enabled;
};
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
index 24b80a6cfca4..188a5974b85c 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
@@ -20,6 +20,7 @@
the file called "COPYING".
Contact Information:
+ Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
@@ -61,6 +62,7 @@ static s32 ixgbe_get_invariants_X540(struct ixgbe_hw *hw)
mac->mcft_size = IXGBE_X540_MC_TBL_SIZE;
mac->vft_size = IXGBE_X540_VFT_TBL_SIZE;
mac->num_rar_entries = IXGBE_X540_RAR_ENTRIES;
+ mac->rx_pb_size = IXGBE_X540_RX_PB_SIZE;
mac->max_rx_queues = IXGBE_X540_MAX_RX_QUEUES;
mac->max_tx_queues = IXGBE_X540_MAX_TX_QUEUES;
mac->max_msix_vectors = ixgbe_get_pcie_msix_count_generic(hw);
@@ -187,7 +189,6 @@ static s32 ixgbe_start_hw_X540(struct ixgbe_hw *hw)
goto out;
ret_val = ixgbe_start_hw_gen2(hw);
- hw->mac.rx_pb_size = IXGBE_X540_RX_PB_SIZE;
out:
return ret_val;
}
@@ -854,7 +855,8 @@ static struct ixgbe_mac_operations mac_ops_X540 = {
.enable_rx_buff = &ixgbe_enable_rx_buff_generic,
.get_thermal_sensor_data = NULL,
.init_thermal_sensor_thresh = NULL,
- .mng_fw_enabled = NULL,
+ .prot_autoc_read = &prot_autoc_read_generic,
+ .prot_autoc_write = &prot_autoc_write_generic,
};
static struct ixgbe_eeprom_operations eeprom_ops_X540 = {
diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
index f68b78c732a8..1baecb60f065 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 82599 Virtual Function driver
- Copyright(c) 1999 - 2012 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -530,41 +530,55 @@ static const u32 register_test_patterns[] = {
0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF
};
-#define REG_PATTERN_TEST(R, M, W) \
-{ \
- u32 pat, val, before; \
- for (pat = 0; pat < ARRAY_SIZE(register_test_patterns); pat++) { \
- before = readl(adapter->hw.hw_addr + R); \
- writel((register_test_patterns[pat] & W), \
- (adapter->hw.hw_addr + R)); \
- val = readl(adapter->hw.hw_addr + R); \
- if (val != (register_test_patterns[pat] & W & M)) { \
- hw_dbg(&adapter->hw, \
- "pattern test reg %04X failed: got " \
- "0x%08X expected 0x%08X\n", \
- R, val, (register_test_patterns[pat] & W & M)); \
- *data = R; \
- writel(before, adapter->hw.hw_addr + R); \
- return 1; \
- } \
- writel(before, adapter->hw.hw_addr + R); \
- } \
+static bool reg_pattern_test(struct ixgbevf_adapter *adapter, u64 *data,
+ int reg, u32 mask, u32 write)
+{
+ u32 pat, val, before;
+
+ if (IXGBE_REMOVED(adapter->hw.hw_addr)) {
+ *data = 1;
+ return true;
+ }
+ for (pat = 0; pat < ARRAY_SIZE(register_test_patterns); pat++) {
+ before = ixgbevf_read_reg(&adapter->hw, reg);
+ ixgbe_write_reg(&adapter->hw, reg,
+ register_test_patterns[pat] & write);
+ val = ixgbevf_read_reg(&adapter->hw, reg);
+ if (val != (register_test_patterns[pat] & write & mask)) {
+ hw_dbg(&adapter->hw,
+ "pattern test reg %04X failed: got 0x%08X expected 0x%08X\n",
+ reg, val,
+ register_test_patterns[pat] & write & mask);
+ *data = reg;
+ ixgbe_write_reg(&adapter->hw, reg, before);
+ return true;
+ }
+ ixgbe_write_reg(&adapter->hw, reg, before);
+ }
+ return false;
}
-#define REG_SET_AND_CHECK(R, M, W) \
-{ \
- u32 val, before; \
- before = readl(adapter->hw.hw_addr + R); \
- writel((W & M), (adapter->hw.hw_addr + R)); \
- val = readl(adapter->hw.hw_addr + R); \
- if ((W & M) != (val & M)) { \
- pr_err("set/check reg %04X test failed: got 0x%08X expected " \
- "0x%08X\n", R, (val & M), (W & M)); \
- *data = R; \
- writel(before, (adapter->hw.hw_addr + R)); \
- return 1; \
- } \
- writel(before, (adapter->hw.hw_addr + R)); \
+static bool reg_set_and_check(struct ixgbevf_adapter *adapter, u64 *data,
+ int reg, u32 mask, u32 write)
+{
+ u32 val, before;
+
+ if (IXGBE_REMOVED(adapter->hw.hw_addr)) {
+ *data = 1;
+ return true;
+ }
+ before = ixgbevf_read_reg(&adapter->hw, reg);
+ ixgbe_write_reg(&adapter->hw, reg, write & mask);
+ val = ixgbevf_read_reg(&adapter->hw, reg);
+ if ((write & mask) != (val & mask)) {
+ pr_err("set/check reg %04X test failed: got 0x%08X expected 0x%08X\n",
+ reg, (val & mask), write & mask);
+ *data = reg;
+ ixgbe_write_reg(&adapter->hw, reg, before);
+ return true;
+ }
+ ixgbe_write_reg(&adapter->hw, reg, before);
+ return false;
}
static int ixgbevf_reg_test(struct ixgbevf_adapter *adapter, u64 *data)
@@ -572,6 +586,12 @@ static int ixgbevf_reg_test(struct ixgbevf_adapter *adapter, u64 *data)
const struct ixgbevf_reg_test *test;
u32 i;
+ if (IXGBE_REMOVED(adapter->hw.hw_addr)) {
+ dev_err(&adapter->pdev->dev,
+ "Adapter removed - register test blocked\n");
+ *data = 1;
+ return 1;
+ }
test = reg_test_vf;
/*
@@ -580,38 +600,47 @@ static int ixgbevf_reg_test(struct ixgbevf_adapter *adapter, u64 *data)
*/
while (test->reg) {
for (i = 0; i < test->array_len; i++) {
+ bool b = false;
+
switch (test->test_type) {
case PATTERN_TEST:
- REG_PATTERN_TEST(test->reg + (i * 0x40),
- test->mask,
- test->write);
+ b = reg_pattern_test(adapter, data,
+ test->reg + (i * 0x40),
+ test->mask,
+ test->write);
break;
case SET_READ_TEST:
- REG_SET_AND_CHECK(test->reg + (i * 0x40),
- test->mask,
- test->write);
+ b = reg_set_and_check(adapter, data,
+ test->reg + (i * 0x40),
+ test->mask,
+ test->write);
break;
case WRITE_NO_TEST:
- writel(test->write,
- (adapter->hw.hw_addr + test->reg)
- + (i * 0x40));
+ ixgbe_write_reg(&adapter->hw,
+ test->reg + (i * 0x40),
+ test->write);
break;
case TABLE32_TEST:
- REG_PATTERN_TEST(test->reg + (i * 4),
- test->mask,
- test->write);
+ b = reg_pattern_test(adapter, data,
+ test->reg + (i * 4),
+ test->mask,
+ test->write);
break;
case TABLE64_TEST_LO:
- REG_PATTERN_TEST(test->reg + (i * 8),
- test->mask,
- test->write);
+ b = reg_pattern_test(adapter, data,
+ test->reg + (i * 8),
+ test->mask,
+ test->write);
break;
case TABLE64_TEST_HI:
- REG_PATTERN_TEST((test->reg + 4) + (i * 8),
- test->mask,
- test->write);
+ b = reg_pattern_test(adapter, data,
+ test->reg + 4 + (i * 8),
+ test->mask,
+ test->write);
break;
}
+ if (b)
+ return 1;
}
test++;
}
@@ -626,6 +655,14 @@ static void ixgbevf_diag_test(struct net_device *netdev,
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
bool if_running = netif_running(netdev);
+ if (IXGBE_REMOVED(adapter->hw.hw_addr)) {
+ dev_err(&adapter->pdev->dev,
+ "Adapter removed - test blocked\n");
+ data[0] = 1;
+ data[1] = 1;
+ eth_test->flags |= ETH_TEST_FL_FAILED;
+ return;
+ }
set_bit(__IXGBEVF_TESTING, &adapter->state);
if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
/* Offline tests */
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
index 54829326bb09..e7e7d695816b 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 82599 Virtual Function driver
- Copyright(c) 1999 - 2012 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -315,6 +315,11 @@ static inline u16 ixgbevf_desc_unused(struct ixgbevf_ring *ring)
return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1;
}
+static inline void ixgbevf_write_tail(struct ixgbevf_ring *ring, u32 value)
+{
+ writel(value, ring->tail);
+}
+
#define IXGBEVF_RX_DESC(R, i) \
(&(((union ixgbe_adv_rx_desc *)((R)->desc))[i]))
#define IXGBEVF_TX_DESC(R, i) \
@@ -401,6 +406,7 @@ struct ixgbevf_adapter {
u64 bp_tx_missed;
#endif
+ u8 __iomem *io_addr; /* Mainly for iounmap use */
u32 link_speed;
bool link_up;
@@ -412,7 +418,9 @@ struct ixgbevf_adapter {
enum ixbgevf_state_t {
__IXGBEVF_TESTING,
__IXGBEVF_RESETTING,
- __IXGBEVF_DOWN
+ __IXGBEVF_DOWN,
+ __IXGBEVF_DISABLED,
+ __IXGBEVF_REMOVING,
};
struct ixgbevf_cb {
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 9df28985eba7..4ba139b2d25a 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 82599 Virtual Function driver
- Copyright(c) 1999 - 2012 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -99,6 +99,49 @@ static void ixgbevf_queue_reset_subtask(struct ixgbevf_adapter *adapter);
static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector);
static void ixgbevf_free_all_rx_resources(struct ixgbevf_adapter *adapter);
+static void ixgbevf_remove_adapter(struct ixgbe_hw *hw)
+{
+ struct ixgbevf_adapter *adapter = hw->back;
+
+ if (!hw->hw_addr)
+ return;
+ hw->hw_addr = NULL;
+ dev_err(&adapter->pdev->dev, "Adapter removed\n");
+ schedule_work(&adapter->watchdog_task);
+}
+
+static void ixgbevf_check_remove(struct ixgbe_hw *hw, u32 reg)
+{
+ u32 value;
+
+ /* The following check not only optimizes a bit by not
+ * performing a read on the status register when the
+ * register just read was a status register read that
+ * returned IXGBE_FAILED_READ_REG. It also blocks any
+ * potential recursion.
+ */
+ if (reg == IXGBE_VFSTATUS) {
+ ixgbevf_remove_adapter(hw);
+ return;
+ }
+ value = ixgbevf_read_reg(hw, IXGBE_VFSTATUS);
+ if (value == IXGBE_FAILED_READ_REG)
+ ixgbevf_remove_adapter(hw);
+}
+
+u32 ixgbevf_read_reg(struct ixgbe_hw *hw, u32 reg)
+{
+ u8 __iomem *reg_addr = ACCESS_ONCE(hw->hw_addr);
+ u32 value;
+
+ if (IXGBE_REMOVED(reg_addr))
+ return IXGBE_FAILED_READ_REG;
+ value = readl(reg_addr + reg);
+ if (unlikely(value == IXGBE_FAILED_READ_REG))
+ ixgbevf_check_remove(hw, reg);
+ return value;
+}
+
static inline void ixgbevf_release_rx_desc(struct ixgbevf_ring *rx_ring,
u32 val)
{
@@ -111,7 +154,7 @@ static inline void ixgbevf_release_rx_desc(struct ixgbevf_ring *rx_ring,
* such as IA-64).
*/
wmb();
- writel(val, rx_ring->tail);
+ ixgbevf_write_tail(rx_ring, val);
}
/**
@@ -516,7 +559,8 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
/* Workaround hardware that can't do proper VEPA multicast
* source pruning.
*/
- if ((skb->pkt_type & (PACKET_BROADCAST | PACKET_MULTICAST)) &&
+ if ((skb->pkt_type == PACKET_BROADCAST ||
+ skb->pkt_type == PACKET_MULTICAST) &&
ether_addr_equal(rx_ring->netdev->dev_addr,
eth_hdr(skb)->h_source)) {
dev_kfree_skb_irq(skb);
@@ -607,7 +651,8 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
napi_complete(napi);
if (adapter->rx_itr_setting & 1)
ixgbevf_set_itr(q_vector);
- if (!test_bit(__IXGBEVF_DOWN, &adapter->state))
+ if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
+ !test_bit(__IXGBEVF_REMOVING, &adapter->state))
ixgbevf_irq_enable_queues(adapter,
1 << q_vector->v_idx);
@@ -832,7 +877,8 @@ static irqreturn_t ixgbevf_msix_other(int irq, void *data)
hw->mac.get_link_status = 1;
- if (!test_bit(__IXGBEVF_DOWN, &adapter->state))
+ if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
+ !test_bit(__IXGBEVF_REMOVING, &adapter->state))
mod_timer(&adapter->watchdog_timer, jiffies);
IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, adapter->eims_other);
@@ -1136,7 +1182,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter,
/* reset head and tail pointers */
IXGBE_WRITE_REG(hw, IXGBE_VFTDH(reg_idx), 0);
IXGBE_WRITE_REG(hw, IXGBE_VFTDT(reg_idx), 0);
- ring->tail = hw->hw_addr + IXGBE_VFTDT(reg_idx);
+ ring->tail = adapter->io_addr + IXGBE_VFTDT(reg_idx);
/* reset ntu and ntc to place SW in sync with hardwdare */
ring->next_to_clean = 0;
@@ -1256,6 +1302,8 @@ static void ixgbevf_disable_rx_queue(struct ixgbevf_adapter *adapter,
u32 rxdctl;
u8 reg_idx = ring->reg_idx;
+ if (IXGBE_REMOVED(hw->hw_addr))
+ return;
rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(reg_idx));
rxdctl &= ~IXGBE_RXDCTL_ENABLE;
@@ -1281,6 +1329,8 @@ static void ixgbevf_rx_desc_queue_enable(struct ixgbevf_adapter *adapter,
u32 rxdctl;
u8 reg_idx = ring->reg_idx;
+ if (IXGBE_REMOVED(hw->hw_addr))
+ return;
do {
usleep_range(1000, 2000);
rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(reg_idx));
@@ -1315,7 +1365,7 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter,
/* reset head and tail pointers */
IXGBE_WRITE_REG(hw, IXGBE_VFRDH(reg_idx), 0);
IXGBE_WRITE_REG(hw, IXGBE_VFRDT(reg_idx), 0);
- ring->tail = hw->hw_addr + IXGBE_VFRDT(reg_idx);
+ ring->tail = adapter->io_addr + IXGBE_VFRDT(reg_idx);
/* reset ntu and ntc to place SW in sync with hardwdare */
ring->next_to_clean = 0;
@@ -1617,6 +1667,7 @@ static void ixgbevf_up_complete(struct ixgbevf_adapter *adapter)
spin_unlock_bh(&adapter->mbx_lock);
+ smp_mb__before_clear_bit();
clear_bit(__IXGBEVF_DOWN, &adapter->state);
ixgbevf_napi_enable_all(adapter);
@@ -1741,7 +1792,8 @@ void ixgbevf_down(struct ixgbevf_adapter *adapter)
int i;
/* signal that we are down to the interrupt handler */
- set_bit(__IXGBEVF_DOWN, &adapter->state);
+ if (test_and_set_bit(__IXGBEVF_DOWN, &adapter->state))
+ return; /* do nothing if already down */
/* disable all enabled rx queues */
for (i = 0; i < adapter->num_rx_queues; i++)
@@ -1817,7 +1869,6 @@ void ixgbevf_reset(struct ixgbevf_adapter *adapter)
static int ixgbevf_acquire_msix_vectors(struct ixgbevf_adapter *adapter,
int vectors)
{
- int err = 0;
int vector_threshold;
/* We'll want at least 2 (vector_threshold):
@@ -1831,33 +1882,24 @@ static int ixgbevf_acquire_msix_vectors(struct ixgbevf_adapter *adapter,
* Right now, we simply care about how many we'll get; we'll
* set them up later while requesting irq's.
*/
- while (vectors >= vector_threshold) {
- err = pci_enable_msix(adapter->pdev, adapter->msix_entries,
- vectors);
- if (!err || err < 0) /* Success or a nasty failure. */
- break;
- else /* err == number of vectors we should try again with */
- vectors = err;
- }
-
- if (vectors < vector_threshold)
- err = -ENOMEM;
+ vectors = pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
+ vector_threshold, vectors);
- if (err) {
+ if (vectors < 0) {
dev_err(&adapter->pdev->dev,
"Unable to allocate MSI-X interrupts\n");
kfree(adapter->msix_entries);
adapter->msix_entries = NULL;
- } else {
- /*
- * Adjust for only the vectors we'll use, which is minimum
- * of max_msix_q_vectors + NON_Q_VECTORS, or the number of
- * vectors we were allocated.
- */
- adapter->num_msix_vectors = vectors;
+ return vectors;
}
- return err;
+ /* Adjust for only the vectors we'll use, which is minimum
+ * of max_msix_q_vectors + NON_Q_VECTORS, or the number of
+ * vectors we were allocated.
+ */
+ adapter->num_msix_vectors = vectors;
+
+ return 0;
}
/**
@@ -2338,6 +2380,7 @@ static void ixgbevf_reset_task(struct work_struct *work)
/* If we're already down or resetting, just bail */
if (test_bit(__IXGBEVF_DOWN, &adapter->state) ||
+ test_bit(__IXGBEVF_REMOVING, &adapter->state) ||
test_bit(__IXGBEVF_RESETTING, &adapter->state))
return;
@@ -2361,6 +2404,14 @@ static void ixgbevf_watchdog_task(struct work_struct *work)
bool link_up = adapter->link_up;
s32 need_reset;
+ if (IXGBE_REMOVED(hw->hw_addr)) {
+ if (!test_bit(__IXGBEVF_DOWN, &adapter->state)) {
+ rtnl_lock();
+ ixgbevf_down(adapter);
+ rtnl_unlock();
+ }
+ return;
+ }
ixgbevf_queue_reset_subtask(adapter);
adapter->flags |= IXGBE_FLAG_IN_WATCHDOG_TASK;
@@ -2422,7 +2473,8 @@ static void ixgbevf_watchdog_task(struct work_struct *work)
pf_has_reset:
/* Reset the timer */
- if (!test_bit(__IXGBEVF_DOWN, &adapter->state))
+ if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
+ !test_bit(__IXGBEVF_REMOVING, &adapter->state))
mod_timer(&adapter->watchdog_timer,
round_jiffies(jiffies + (2 * HZ)));
@@ -2787,6 +2839,9 @@ static int ixgbevf_tso(struct ixgbevf_ring *tx_ring,
u32 vlan_macip_lens, type_tucmd;
u32 mss_l4len_idx, l4len;
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return 0;
+
if (!skb_is_gso(skb))
return 0;
@@ -2857,12 +2912,12 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring,
if (skb->ip_summed == CHECKSUM_PARTIAL) {
u8 l4_hdr = 0;
switch (skb->protocol) {
- case __constant_htons(ETH_P_IP):
+ case htons(ETH_P_IP):
vlan_macip_lens |= skb_network_header_len(skb);
type_tucmd |= IXGBE_ADVTXD_TUCMD_IPV4;
l4_hdr = ip_hdr(skb)->protocol;
break;
- case __constant_htons(ETH_P_IPV6):
+ case htons(ETH_P_IPV6):
vlan_macip_lens |= skb_network_header_len(skb);
l4_hdr = ipv6_hdr(skb)->nexthdr;
break;
@@ -3060,7 +3115,7 @@ static void ixgbevf_tx_map(struct ixgbevf_ring *tx_ring,
tx_ring->next_to_use = i;
/* notify HW of packet */
- writel(i, tx_ring->tail);
+ ixgbevf_write_tail(tx_ring, i);
return;
dma_error:
@@ -3165,7 +3220,7 @@ static int ixgbevf_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
tso = ixgbevf_tso(tx_ring, first, &hdr_len);
if (tso < 0)
goto out_drop;
- else
+ else if (!tso)
ixgbevf_tx_csum(tx_ring, first);
ixgbevf_tx_map(tx_ring, first, hdr_len);
@@ -3274,7 +3329,8 @@ static int ixgbevf_suspend(struct pci_dev *pdev, pm_message_t state)
return retval;
#endif
- pci_disable_device(pdev);
+ if (!test_and_set_bit(__IXGBEVF_DISABLED, &adapter->state))
+ pci_disable_device(pdev);
return 0;
}
@@ -3286,7 +3342,6 @@ static int ixgbevf_resume(struct pci_dev *pdev)
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
u32 err;
- pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
/*
* pci_restore_state clears dev->state_saved so call
@@ -3299,6 +3354,8 @@ static int ixgbevf_resume(struct pci_dev *pdev)
dev_err(&pdev->dev, "Cannot enable PCI device from suspend\n");
return err;
}
+ smp_mb__before_clear_bit();
+ clear_bit(__IXGBEVF_DISABLED, &adapter->state);
pci_set_master(pdev);
ixgbevf_reset(adapter);
@@ -3344,10 +3401,10 @@ static struct rtnl_link_stats64 *ixgbevf_get_stats(struct net_device *netdev,
for (i = 0; i < adapter->num_rx_queues; i++) {
ring = adapter->rx_ring[i];
do {
- start = u64_stats_fetch_begin_bh(&ring->syncp);
+ start = u64_stats_fetch_begin_irq(&ring->syncp);
bytes = ring->stats.bytes;
packets = ring->stats.packets;
- } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
stats->rx_bytes += bytes;
stats->rx_packets += packets;
}
@@ -3355,10 +3412,10 @@ static struct rtnl_link_stats64 *ixgbevf_get_stats(struct net_device *netdev,
for (i = 0; i < adapter->num_tx_queues; i++) {
ring = adapter->tx_ring[i];
do {
- start = u64_stats_fetch_begin_bh(&ring->syncp);
+ start = u64_stats_fetch_begin_irq(&ring->syncp);
bytes = ring->stats.bytes;
packets = ring->stats.packets;
- } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
stats->tx_bytes += bytes;
stats->tx_packets += packets;
}
@@ -3460,6 +3517,7 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
hw->hw_addr = ioremap(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
+ adapter->io_addr = hw->hw_addr;
if (!hw->hw_addr) {
err = -EIO;
goto err_ioremap;
@@ -3545,14 +3603,15 @@ err_register:
ixgbevf_clear_interrupt_scheme(adapter);
err_sw_init:
ixgbevf_reset_interrupt_capability(adapter);
- iounmap(hw->hw_addr);
+ iounmap(adapter->io_addr);
err_ioremap:
free_netdev(netdev);
err_alloc_etherdev:
pci_release_regions(pdev);
err_pci_reg:
err_dma:
- pci_disable_device(pdev);
+ if (!test_and_set_bit(__IXGBEVF_DISABLED, &adapter->state))
+ pci_disable_device(pdev);
return err;
}
@@ -3570,7 +3629,7 @@ static void ixgbevf_remove(struct pci_dev *pdev)
struct net_device *netdev = pci_get_drvdata(pdev);
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
- set_bit(__IXGBEVF_DOWN, &adapter->state);
+ set_bit(__IXGBEVF_REMOVING, &adapter->state);
del_timer_sync(&adapter->watchdog_timer);
@@ -3583,14 +3642,15 @@ static void ixgbevf_remove(struct pci_dev *pdev)
ixgbevf_clear_interrupt_scheme(adapter);
ixgbevf_reset_interrupt_capability(adapter);
- iounmap(adapter->hw.hw_addr);
+ iounmap(adapter->io_addr);
pci_release_regions(pdev);
hw_dbg(&adapter->hw, "Remove complete\n");
free_netdev(netdev);
- pci_disable_device(pdev);
+ if (!test_and_set_bit(__IXGBEVF_DISABLED, &adapter->state))
+ pci_disable_device(pdev);
}
/**
@@ -3607,15 +3667,20 @@ static pci_ers_result_t ixgbevf_io_error_detected(struct pci_dev *pdev,
struct net_device *netdev = pci_get_drvdata(pdev);
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+ rtnl_lock();
netif_device_detach(netdev);
- if (state == pci_channel_io_perm_failure)
+ if (state == pci_channel_io_perm_failure) {
+ rtnl_unlock();
return PCI_ERS_RESULT_DISCONNECT;
+ }
if (netif_running(netdev))
ixgbevf_down(adapter);
- pci_disable_device(pdev);
+ if (!test_and_set_bit(__IXGBEVF_DISABLED, &adapter->state))
+ pci_disable_device(pdev);
+ rtnl_unlock();
/* Request a slot slot reset. */
return PCI_ERS_RESULT_NEED_RESET;
@@ -3639,6 +3704,8 @@ static pci_ers_result_t ixgbevf_io_slot_reset(struct pci_dev *pdev)
return PCI_ERS_RESULT_DISCONNECT;
}
+ smp_mb__before_clear_bit();
+ clear_bit(__IXGBEVF_DISABLED, &adapter->state);
pci_set_master(pdev);
ixgbevf_reset(adapter);
diff --git a/drivers/net/ethernet/intel/ixgbevf/regs.h b/drivers/net/ethernet/intel/ixgbevf/regs.h
index debd8c0e1f28..09dd8f698bea 100644
--- a/drivers/net/ethernet/intel/ixgbevf/regs.h
+++ b/drivers/net/ethernet/intel/ixgbevf/regs.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 82599 Virtual Function driver
- Copyright(c) 1999 - 2012 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -70,16 +70,6 @@
#define IXGBE_VFGOTC_MSB 0x02024
#define IXGBE_VFMPRC 0x01034
-#define IXGBE_WRITE_REG(a, reg, value) writel((value), ((a)->hw_addr + (reg)))
-
-#define IXGBE_READ_REG(a, reg) readl((a)->hw_addr + (reg))
-
-#define IXGBE_WRITE_REG_ARRAY(a, reg, offset, value) ( \
- writel((value), ((a)->hw_addr + (reg) + ((offset) << 2))))
-
-#define IXGBE_READ_REG_ARRAY(a, reg, offset) ( \
- readl((a)->hw_addr + (reg) + ((offset) << 2)))
-
#define IXGBE_WRITE_FLUSH(a) (IXGBE_READ_REG(a, IXGBE_VFSTATUS))
#endif /* _IXGBEVF_REGS_H_ */
diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.h b/drivers/net/ethernet/intel/ixgbevf/vf.h
index 7b1f502d1716..3061d1890471 100644
--- a/drivers/net/ethernet/intel/ixgbevf/vf.h
+++ b/drivers/net/ethernet/intel/ixgbevf/vf.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 82599 Virtual Function driver
- Copyright(c) 1999 - 2012 Intel Corporation.
+ Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -172,6 +172,37 @@ struct ixgbevf_info {
const struct ixgbe_mac_operations *mac_ops;
};
+#define IXGBE_FAILED_READ_REG 0xffffffffU
+
+#define IXGBE_REMOVED(a) unlikely(!(a))
+
+static inline void ixgbe_write_reg(struct ixgbe_hw *hw, u32 reg, u32 value)
+{
+ u8 __iomem *reg_addr = ACCESS_ONCE(hw->hw_addr);
+
+ if (IXGBE_REMOVED(reg_addr))
+ return;
+ writel(value, reg_addr + reg);
+}
+#define IXGBE_WRITE_REG(h, r, v) ixgbe_write_reg(h, r, v)
+
+u32 ixgbevf_read_reg(struct ixgbe_hw *hw, u32 reg);
+#define IXGBE_READ_REG(h, r) ixgbevf_read_reg(h, r)
+
+static inline void ixgbe_write_reg_array(struct ixgbe_hw *hw, u32 reg,
+ u32 offset, u32 value)
+{
+ ixgbe_write_reg(hw, reg + (offset << 2), value);
+}
+#define IXGBE_WRITE_REG_ARRAY(h, r, o, v) ixgbe_write_reg_array(h, r, o, v)
+
+static inline u32 ixgbe_read_reg_array(struct ixgbe_hw *hw, u32 reg,
+ u32 offset)
+{
+ return ixgbevf_read_reg(hw, reg + (offset << 2));
+}
+#define IXGBE_READ_REG_ARRAY(h, r, o) ixgbe_read_reg_array(h, r, o)
+
void ixgbevf_rlpml_set_vf(struct ixgbe_hw *hw, u16 max_size);
int ixgbevf_negotiate_api_version(struct ixgbe_hw *hw, int api);
int ixgbevf_get_queues(struct ixgbe_hw *hw, unsigned int *num_tcs,
diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c
index f5685c0d0579..b0c6050479eb 100644
--- a/drivers/net/ethernet/jme.c
+++ b/drivers/net/ethernet/jme.c
@@ -2054,19 +2054,6 @@ jme_map_tx_skb(struct jme_adapter *jme, struct sk_buff *skb, int idx)
}
static int
-jme_expand_header(struct jme_adapter *jme, struct sk_buff *skb)
-{
- if (unlikely(skb_shinfo(skb)->gso_size &&
- skb_header_cloned(skb) &&
- pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) {
- dev_kfree_skb(skb);
- return -1;
- }
-
- return 0;
-}
-
-static int
jme_tx_tso(struct sk_buff *skb, __le16 *mss, u8 *flags)
{
*mss = cpu_to_le16(skb_shinfo(skb)->gso_size << TXDESC_MSS_SHIFT);
@@ -2225,7 +2212,8 @@ jme_start_xmit(struct sk_buff *skb, struct net_device *netdev)
struct jme_adapter *jme = netdev_priv(netdev);
int idx;
- if (unlikely(jme_expand_header(jme, skb))) {
+ if (unlikely(skb_is_gso(skb) && skb_cow_head(skb, 0))) {
+ dev_kfree_skb_any(skb);
++(NET_STAT(jme).tx_dropped);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
index a2565ce22b7c..b7b8d74c22d9 100644
--- a/drivers/net/ethernet/marvell/mv643xx_eth.c
+++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
@@ -730,7 +730,7 @@ static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb)
unlikely(tag_bytes & ~12)) {
if (skb_checksum_help(skb) == 0)
goto no_csum;
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return 1;
}
@@ -819,7 +819,7 @@ static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev)
if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) {
if (net_ratelimit())
netdev_err(dev, "tx queue full?!\n");
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c
index fd409d76b811..b161a525fc5b 100644
--- a/drivers/net/ethernet/marvell/mvmdio.c
+++ b/drivers/net/ethernet/marvell/mvmdio.c
@@ -167,11 +167,6 @@ out:
return ret;
}
-static int orion_mdio_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id)
{
struct orion_mdio_dev *dev = dev_id;
@@ -209,7 +204,6 @@ static int orion_mdio_probe(struct platform_device *pdev)
bus->name = "orion_mdio_bus";
bus->read = orion_mdio_read;
bus->write = orion_mdio_write;
- bus->reset = orion_mdio_reset;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii",
dev_name(&pdev->dev));
bus->parent = &pdev->dev;
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 8d76fca7fde7..d04b1c3c9b85 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -510,12 +510,12 @@ struct rtnl_link_stats64 *mvneta_get_stats64(struct net_device *dev,
cpu_stats = per_cpu_ptr(pp->stats, cpu);
do {
- start = u64_stats_fetch_begin_bh(&cpu_stats->syncp);
+ start = u64_stats_fetch_begin_irq(&cpu_stats->syncp);
rx_packets = cpu_stats->rx_packets;
rx_bytes = cpu_stats->rx_bytes;
tx_packets = cpu_stats->tx_packets;
tx_bytes = cpu_stats->tx_bytes;
- } while (u64_stats_fetch_retry_bh(&cpu_stats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start));
stats->rx_packets += rx_packets;
stats->rx_bytes += rx_bytes;
@@ -2761,7 +2761,6 @@ static int mvneta_probe(struct platform_device *pdev)
const char *mac_from;
int phy_mode;
int err;
- int cpu;
/* Our multiqueue support is not complete, so for now, only
* allow the usage of the first RX queue
@@ -2816,30 +2815,19 @@ static int mvneta_probe(struct platform_device *pdev)
clk_prepare_enable(pp->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -ENODEV;
- goto err_clk;
- }
-
pp->base = devm_ioremap_resource(&pdev->dev, res);
- if (pp->base == NULL) {
+ if (IS_ERR(pp->base)) {
err = PTR_ERR(pp->base);
goto err_clk;
}
/* Alloc per-cpu stats */
- pp->stats = alloc_percpu(struct mvneta_pcpu_stats);
+ pp->stats = netdev_alloc_pcpu_stats(struct mvneta_pcpu_stats);
if (!pp->stats) {
err = -ENOMEM;
goto err_clk;
}
- for_each_possible_cpu(cpu) {
- struct mvneta_pcpu_stats *stats;
- stats = per_cpu_ptr(pp->stats, cpu);
- u64_stats_init(&stats->syncp);
- }
-
dt_mac_addr = of_get_mac_address(dn);
if (dt_mac_addr) {
mac_from = "device tree";
diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c
index 597846193869..7f81ae66cc89 100644
--- a/drivers/net/ethernet/marvell/skge.c
+++ b/drivers/net/ethernet/marvell/skge.c
@@ -2845,7 +2845,7 @@ mapping_unwind:
mapping_error:
if (net_ratelimit())
dev_warn(&hw->pdev->dev, "%s: tx mapping error\n", dev->name);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -3172,7 +3172,7 @@ static void skge_tx_done(struct net_device *dev)
pkts_compl++;
bytes_compl += e->skb->len;
- dev_kfree_skb(e->skb);
+ dev_consume_skb_any(e->skb);
}
}
netdev_completed_queue(dev, pkts_compl, bytes_compl);
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 55a37ae11440..b81106451a0a 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -44,6 +44,8 @@
#include <linux/prefetch.h>
#include <linux/debugfs.h>
#include <linux/mii.h>
+#include <linux/of_device.h>
+#include <linux/of_net.h>
#include <asm/irq.h>
@@ -2000,7 +2002,7 @@ mapping_unwind:
mapping_error:
if (net_ratelimit())
dev_warn(&hw->pdev->dev, "%s: tx mapping error\n", dev->name);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -2733,6 +2735,9 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
unsigned int total_bytes[2] = { 0 };
unsigned int total_packets[2] = { 0 };
+ if (to_do <= 0)
+ return work_done;
+
rmb();
do {
struct sky2_port *sky2;
@@ -3906,19 +3911,19 @@ static struct rtnl_link_stats64 *sky2_get_stats(struct net_device *dev,
u64 _bytes, _packets;
do {
- start = u64_stats_fetch_begin_bh(&sky2->rx_stats.syncp);
+ start = u64_stats_fetch_begin_irq(&sky2->rx_stats.syncp);
_bytes = sky2->rx_stats.bytes;
_packets = sky2->rx_stats.packets;
- } while (u64_stats_fetch_retry_bh(&sky2->rx_stats.syncp, start));
+ } while (u64_stats_fetch_retry_irq(&sky2->rx_stats.syncp, start));
stats->rx_packets = _packets;
stats->rx_bytes = _bytes;
do {
- start = u64_stats_fetch_begin_bh(&sky2->tx_stats.syncp);
+ start = u64_stats_fetch_begin_irq(&sky2->tx_stats.syncp);
_bytes = sky2->tx_stats.bytes;
_packets = sky2->tx_stats.packets;
- } while (u64_stats_fetch_retry_bh(&sky2->tx_stats.syncp, start));
+ } while (u64_stats_fetch_retry_irq(&sky2->tx_stats.syncp, start));
stats->tx_packets = _packets;
stats->tx_bytes = _bytes;
@@ -4748,6 +4753,7 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port,
{
struct sky2_port *sky2;
struct net_device *dev = alloc_etherdev(sizeof(*sky2));
+ const void *iap;
if (!dev)
return NULL;
@@ -4805,8 +4811,16 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port,
dev->features |= dev->hw_features;
- /* read the mac address */
- memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN);
+ /* try to get mac address in the following order:
+ * 1) from device tree data
+ * 2) from internal registers set by bootloader
+ */
+ iap = of_get_mac_address(hw->pdev->dev.of_node);
+ if (iap)
+ memcpy(dev->dev_addr, iap, ETH_ALEN);
+ else
+ memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8,
+ ETH_ALEN);
return dev;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/Kconfig b/drivers/net/ethernet/mellanox/mlx4/Kconfig
index 563495d8975a..1486ce902a56 100644
--- a/drivers/net/ethernet/mellanox/mlx4/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlx4/Kconfig
@@ -3,7 +3,7 @@
#
config MLX4_EN
- tristate "Mellanox Technologies 10Gbit Ethernet support"
+ tristate "Mellanox Technologies 1/10/40Gbit Ethernet support"
depends on PCI
select MLX4_CORE
select PTP_1588_CLOCK
@@ -23,6 +23,13 @@ config MLX4_EN_DCB
If unsure, set to Y
+config MLX4_EN_VXLAN
+ bool "VXLAN offloads Support"
+ default y
+ depends on MLX4_EN && VXLAN && !(MLX4_EN=y && VXLAN=m)
+ ---help---
+ Say Y here if you want to use VXLAN offloads in the driver.
+
config MLX4_CORE
tristate
depends on PCI
diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c
index 0d02fba94536..78099eab7673 100644
--- a/drivers/net/ethernet/mellanox/mlx4/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c
@@ -800,16 +800,7 @@ static int mlx4_MAD_IFC_wrapper(struct mlx4_dev *dev, int slave,
vhcr->op, MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE);
}
-static int MLX4_CMD_UPDATE_QP_wrapper(struct mlx4_dev *dev, int slave,
- struct mlx4_vhcr *vhcr,
- struct mlx4_cmd_mailbox *inbox,
- struct mlx4_cmd_mailbox *outbox,
- struct mlx4_cmd_info *cmd)
-{
- return -EPERM;
-}
-
-static int MLX4_CMD_GET_OP_REQ_wrapper(struct mlx4_dev *dev, int slave,
+static int mlx4_CMD_EPERM_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_vhcr *vhcr,
struct mlx4_cmd_mailbox *inbox,
struct mlx4_cmd_mailbox *outbox,
@@ -964,6 +955,15 @@ static struct mlx4_cmd_info cmd_info[] = {
.wrapper = NULL
},
{
+ .opcode = MLX4_CMD_CONFIG_DEV,
+ .has_inbox = false,
+ .has_outbox = false,
+ .out_is_imm = false,
+ .encode_slave_id = false,
+ .verify = NULL,
+ .wrapper = mlx4_CMD_EPERM_wrapper
+ },
+ {
.opcode = MLX4_CMD_ALLOC_RES,
.has_inbox = false,
.has_outbox = false,
@@ -1258,7 +1258,7 @@ static struct mlx4_cmd_info cmd_info[] = {
.out_is_imm = false,
.encode_slave_id = false,
.verify = NULL,
- .wrapper = MLX4_CMD_UPDATE_QP_wrapper
+ .wrapper = mlx4_CMD_EPERM_wrapper
},
{
.opcode = MLX4_CMD_GET_OP_REQ,
@@ -1267,7 +1267,7 @@ static struct mlx4_cmd_info cmd_info[] = {
.out_is_imm = false,
.encode_slave_id = false,
.verify = NULL,
- .wrapper = MLX4_CMD_GET_OP_REQ_wrapper,
+ .wrapper = mlx4_CMD_EPERM_wrapper,
},
{
.opcode = MLX4_CMD_CONF_SPECIAL_QP,
@@ -1378,7 +1378,7 @@ static struct mlx4_cmd_info cmd_info[] = {
.out_is_imm = false,
.encode_slave_id = false,
.verify = NULL,
- .wrapper = mlx4_FLOW_STEERING_IB_UC_QP_RANGE_wrapper
+ .wrapper = mlx4_CMD_EPERM_wrapper
},
};
@@ -1643,8 +1643,16 @@ static int mlx4_master_activate_admin_state(struct mlx4_priv *priv, int slave)
int port, err;
struct mlx4_vport_state *vp_admin;
struct mlx4_vport_oper_state *vp_oper;
-
- for (port = 1; port <= MLX4_MAX_PORTS; port++) {
+ struct mlx4_active_ports actv_ports = mlx4_get_active_ports(
+ &priv->dev, slave);
+ int min_port = find_first_bit(actv_ports.ports,
+ priv->dev.caps.num_ports) + 1;
+ int max_port = min_port - 1 +
+ bitmap_weight(actv_ports.ports, priv->dev.caps.num_ports);
+
+ for (port = min_port; port <= max_port; port++) {
+ if (!test_bit(port - 1, actv_ports.ports))
+ continue;
vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port];
vp_admin = &priv->mfunc.master.vf_admin[slave].vport[port];
vp_oper->state = *vp_admin;
@@ -1685,8 +1693,17 @@ static void mlx4_master_deactivate_admin_state(struct mlx4_priv *priv, int slave
{
int port;
struct mlx4_vport_oper_state *vp_oper;
+ struct mlx4_active_ports actv_ports = mlx4_get_active_ports(
+ &priv->dev, slave);
+ int min_port = find_first_bit(actv_ports.ports,
+ priv->dev.caps.num_ports) + 1;
+ int max_port = min_port - 1 +
+ bitmap_weight(actv_ports.ports, priv->dev.caps.num_ports);
+
- for (port = 1; port <= MLX4_MAX_PORTS; port++) {
+ for (port = min_port; port <= max_port; port++) {
+ if (!test_bit(port - 1, actv_ports.ports))
+ continue;
vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port];
if (NO_INDX != vp_oper->vlan_idx) {
__mlx4_unregister_vlan(&priv->dev,
@@ -2234,6 +2251,112 @@ static int mlx4_get_slave_indx(struct mlx4_dev *dev, int vf)
return vf+1;
}
+int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave)
+{
+ if (slave < 1 || slave > dev->num_vfs) {
+ mlx4_err(dev,
+ "Bad slave number:%d (number of activated slaves: %lu)\n",
+ slave, dev->num_slaves);
+ return -EINVAL;
+ }
+ return slave - 1;
+}
+
+struct mlx4_active_ports mlx4_get_active_ports(struct mlx4_dev *dev, int slave)
+{
+ struct mlx4_active_ports actv_ports;
+ int vf;
+
+ bitmap_zero(actv_ports.ports, MLX4_MAX_PORTS);
+
+ if (slave == 0) {
+ bitmap_fill(actv_ports.ports, dev->caps.num_ports);
+ return actv_ports;
+ }
+
+ vf = mlx4_get_vf_indx(dev, slave);
+ if (vf < 0)
+ return actv_ports;
+
+ bitmap_set(actv_ports.ports, dev->dev_vfs[vf].min_port - 1,
+ min((int)dev->dev_vfs[mlx4_get_vf_indx(dev, slave)].n_ports,
+ dev->caps.num_ports));
+
+ return actv_ports;
+}
+EXPORT_SYMBOL_GPL(mlx4_get_active_ports);
+
+int mlx4_slave_convert_port(struct mlx4_dev *dev, int slave, int port)
+{
+ unsigned n;
+ struct mlx4_active_ports actv_ports = mlx4_get_active_ports(dev, slave);
+ unsigned m = bitmap_weight(actv_ports.ports, dev->caps.num_ports);
+
+ if (port <= 0 || port > m)
+ return -EINVAL;
+
+ n = find_first_bit(actv_ports.ports, dev->caps.num_ports);
+ if (port <= n)
+ port = n + 1;
+
+ return port;
+}
+EXPORT_SYMBOL_GPL(mlx4_slave_convert_port);
+
+int mlx4_phys_to_slave_port(struct mlx4_dev *dev, int slave, int port)
+{
+ struct mlx4_active_ports actv_ports = mlx4_get_active_ports(dev, slave);
+ if (test_bit(port - 1, actv_ports.ports))
+ return port -
+ find_first_bit(actv_ports.ports, dev->caps.num_ports);
+
+ return -1;
+}
+EXPORT_SYMBOL_GPL(mlx4_phys_to_slave_port);
+
+struct mlx4_slaves_pport mlx4_phys_to_slaves_pport(struct mlx4_dev *dev,
+ int port)
+{
+ unsigned i;
+ struct mlx4_slaves_pport slaves_pport;
+
+ bitmap_zero(slaves_pport.slaves, MLX4_MFUNC_MAX);
+
+ if (port <= 0 || port > dev->caps.num_ports)
+ return slaves_pport;
+
+ for (i = 0; i < dev->num_vfs + 1; i++) {
+ struct mlx4_active_ports actv_ports =
+ mlx4_get_active_ports(dev, i);
+ if (test_bit(port - 1, actv_ports.ports))
+ set_bit(i, slaves_pport.slaves);
+ }
+
+ return slaves_pport;
+}
+EXPORT_SYMBOL_GPL(mlx4_phys_to_slaves_pport);
+
+struct mlx4_slaves_pport mlx4_phys_to_slaves_pport_actv(
+ struct mlx4_dev *dev,
+ const struct mlx4_active_ports *crit_ports)
+{
+ unsigned i;
+ struct mlx4_slaves_pport slaves_pport;
+
+ bitmap_zero(slaves_pport.slaves, MLX4_MFUNC_MAX);
+
+ for (i = 0; i < dev->num_vfs + 1; i++) {
+ struct mlx4_active_ports actv_ports =
+ mlx4_get_active_ports(dev, i);
+ if (bitmap_equal(crit_ports->ports, actv_ports.ports,
+ dev->caps.num_ports))
+ set_bit(i, slaves_pport.slaves);
+ }
+
+ return slaves_pport;
+}
+EXPORT_SYMBOL_GPL(mlx4_phys_to_slaves_pport_actv);
+
int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac)
{
struct mlx4_priv *priv = mlx4_priv(dev);
@@ -2289,6 +2412,30 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos)
}
EXPORT_SYMBOL_GPL(mlx4_set_vf_vlan);
+ /* mlx4_get_slave_default_vlan -
+ * return true if VST ( default vlan)
+ * if VST, will return vlan & qos (if not NULL)
+ */
+bool mlx4_get_slave_default_vlan(struct mlx4_dev *dev, int port, int slave,
+ u16 *vlan, u8 *qos)
+{
+ struct mlx4_vport_oper_state *vp_oper;
+ struct mlx4_priv *priv;
+
+ priv = mlx4_priv(dev);
+ vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port];
+
+ if (MLX4_VGT != vp_oper->state.default_vlan) {
+ if (vlan)
+ *vlan = vp_oper->state.default_vlan;
+ if (qos)
+ *qos = vp_oper->state.default_qos;
+ return true;
+ }
+ return false;
+}
+EXPORT_SYMBOL_GPL(mlx4_get_slave_default_vlan);
+
int mlx4_set_vf_spoofchk(struct mlx4_dev *dev, int port, int vf, bool setting)
{
struct mlx4_priv *priv = mlx4_priv(dev);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
index abaf6bb22416..57dda95b67d8 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
@@ -276,6 +276,7 @@ static const struct ptp_clock_info mlx4_en_ptp_clock_info = {
.n_alarm = 0,
.n_ext_ts = 0,
.n_per_out = 0,
+ .n_pins = 0,
.pps = 0,
.adjfreq = mlx4_en_phc_adjfreq,
.adjtime = mlx4_en_phc_adjtime,
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
index b4881b686159..c95ca252187c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
@@ -62,7 +62,7 @@ static int mlx4_en_ets_validate(struct mlx4_en_priv *priv, struct ieee_ets *ets)
int has_ets_tc = 0;
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
- if (ets->prio_tc[i] > MLX4_EN_NUM_UP) {
+ if (ets->prio_tc[i] >= MLX4_EN_NUM_UP) {
en_err(priv, "Bad priority in UP <=> TC mapping. TC: %d, UP: %d\n",
i, ets->prio_tc[i]);
return -EINVAL;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c
index d357bf5a4686..0c59d4fe7e3a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c
@@ -72,6 +72,12 @@ MLX4_EN_PARM_INT(pfctx, 0, "Priority based Flow Control policy on TX[7:0]."
MLX4_EN_PARM_INT(pfcrx, 0, "Priority based Flow Control policy on RX[7:0]."
" Per priority bit mask");
+MLX4_EN_PARM_INT(inline_thold, MAX_INLINE,
+ "Threshold for using inline data (range: 17-104, default: 104)");
+
+#define MAX_PFC_TX 0xff
+#define MAX_PFC_RX 0xff
+
int en_print(const char *level, const struct mlx4_en_priv *priv,
const char *format, ...)
{
@@ -140,6 +146,7 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev)
params->prof[i].tx_ring_num = params->num_tx_rings_p_up *
MLX4_EN_NUM_UP;
params->prof[i].rss_rings = 0;
+ params->prof[i].inline_thold = inline_thold;
}
return 0;
@@ -274,19 +281,8 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)
mlx4_en_init_timestamp(mdev);
- mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) {
- if (!dev->caps.comp_pool) {
- mdev->profile.prof[i].rx_ring_num =
- rounddown_pow_of_two(max_t(int, MIN_RX_RINGS,
- min_t(int,
- dev->caps.num_comp_vectors,
- DEF_RX_RINGS)));
- } else {
- mdev->profile.prof[i].rx_ring_num = rounddown_pow_of_two(
- min_t(int, dev->caps.comp_pool/
- dev->caps.num_ports - 1 , MAX_MSIX_P_PORT - 1));
- }
- }
+ /* Set default number of RX rings*/
+ mlx4_en_set_num_rx_rings(mdev);
/* Create our own workqueue for reset/multicast tasks
* Note: we cannot use the shared workqueue because of deadlocks caused
@@ -336,8 +332,31 @@ static struct mlx4_interface mlx4_en_interface = {
.protocol = MLX4_PROT_ETH,
};
+static void mlx4_en_verify_params(void)
+{
+ if (pfctx > MAX_PFC_TX) {
+ pr_warn("mlx4_en: WARNING: illegal module parameter pfctx 0x%x - should be in range 0-0x%x, will be changed to default (0)\n",
+ pfctx, MAX_PFC_TX);
+ pfctx = 0;
+ }
+
+ if (pfcrx > MAX_PFC_RX) {
+ pr_warn("mlx4_en: WARNING: illegal module parameter pfcrx 0x%x - should be in range 0-0x%x, will be changed to default (0)\n",
+ pfcrx, MAX_PFC_RX);
+ pfcrx = 0;
+ }
+
+ if (inline_thold < MIN_PKT_LEN || inline_thold > MAX_INLINE) {
+ pr_warn("mlx4_en: WARNING: illegal module parameter inline_thold %d - should be in range %d-%d, will be changed to default (%d)\n",
+ inline_thold, MIN_PKT_LEN, MAX_INLINE, MAX_INLINE);
+ inline_thold = MAX_INLINE;
+ }
+}
+
static int __init mlx4_en_init(void)
{
+ mlx4_en_verify_params();
+
return mlx4_register_interface(&mlx4_en_interface);
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 84a96f70dfb5..f085c2df5e69 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -39,6 +39,7 @@
#include <linux/hash.h>
#include <net/ip.h>
#include <net/busy_poll.h>
+#include <net/vxlan.h>
#include <linux/mlx4/driver.h>
#include <linux/mlx4/device.h>
@@ -603,7 +604,7 @@ static int mlx4_en_get_qp(struct mlx4_en_priv *priv)
int err = 0;
u64 reg_id;
int *qpn = &priv->base_qpn;
- u64 mac = mlx4_en_mac_to_u64(priv->dev->dev_addr);
+ u64 mac = mlx4_mac_to_u64(priv->dev->dev_addr);
en_dbg(DRV, priv, "Registering MAC: %pM for adding\n",
priv->dev->dev_addr);
@@ -672,7 +673,7 @@ static void mlx4_en_put_qp(struct mlx4_en_priv *priv)
u64 mac;
if (dev->caps.steering_mode == MLX4_STEERING_MODE_A0) {
- mac = mlx4_en_mac_to_u64(priv->dev->dev_addr);
+ mac = mlx4_mac_to_u64(priv->dev->dev_addr);
en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n",
priv->dev->dev_addr);
mlx4_unregister_mac(dev, priv->port, mac);
@@ -685,7 +686,7 @@ static void mlx4_en_put_qp(struct mlx4_en_priv *priv)
for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) {
bucket = &priv->mac_hash[i];
hlist_for_each_entry_safe(entry, tmp, bucket, hlist) {
- mac = mlx4_en_mac_to_u64(entry->mac);
+ mac = mlx4_mac_to_u64(entry->mac);
en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n",
entry->mac);
mlx4_en_uc_steer_release(priv, entry->mac,
@@ -715,14 +716,14 @@ static int mlx4_en_replace_mac(struct mlx4_en_priv *priv, int qpn,
struct mlx4_en_dev *mdev = priv->mdev;
struct mlx4_dev *dev = mdev->dev;
int err = 0;
- u64 new_mac_u64 = mlx4_en_mac_to_u64(new_mac);
+ u64 new_mac_u64 = mlx4_mac_to_u64(new_mac);
if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) {
struct hlist_head *bucket;
unsigned int mac_hash;
struct mlx4_mac_entry *entry;
struct hlist_node *tmp;
- u64 prev_mac_u64 = mlx4_en_mac_to_u64(prev_mac);
+ u64 prev_mac_u64 = mlx4_mac_to_u64(prev_mac);
bucket = &priv->mac_hash[prev_mac[MLX4_EN_MAC_HASH_IDX]];
hlist_for_each_entry_safe(entry, tmp, bucket, hlist) {
@@ -759,18 +760,6 @@ static int mlx4_en_replace_mac(struct mlx4_en_priv *priv, int qpn,
return __mlx4_replace_mac(dev, priv->port, qpn, new_mac_u64);
}
-u64 mlx4_en_mac_to_u64(u8 *addr)
-{
- u64 mac = 0;
- int i;
-
- for (i = 0; i < ETH_ALEN; i++) {
- mac <<= 8;
- mac |= addr[i];
- }
- return mac;
-}
-
static int mlx4_en_do_set_mac(struct mlx4_en_priv *priv)
{
int err = 0;
@@ -1089,7 +1078,7 @@ static void mlx4_en_do_multicast(struct mlx4_en_priv *priv,
mlx4_en_cache_mclist(dev);
netif_addr_unlock_bh(dev);
list_for_each_entry(mclist, &priv->mc_list, list) {
- mcast_addr = mlx4_en_mac_to_u64(mclist->addr);
+ mcast_addr = mlx4_mac_to_u64(mclist->addr);
mlx4_SET_MCAST_FLTR(mdev->dev, priv->port,
mcast_addr, 0, MLX4_MCAST_CONFIG);
}
@@ -1181,7 +1170,7 @@ static void mlx4_en_do_uc_filter(struct mlx4_en_priv *priv,
found = true;
if (!found) {
- mac = mlx4_en_mac_to_u64(entry->mac);
+ mac = mlx4_mac_to_u64(entry->mac);
mlx4_en_uc_steer_release(priv, entry->mac,
priv->base_qpn,
entry->reg_id);
@@ -1224,7 +1213,7 @@ static void mlx4_en_do_uc_filter(struct mlx4_en_priv *priv,
priv->flags |= MLX4_EN_FLAG_FORCE_PROMISC;
break;
}
- mac = mlx4_en_mac_to_u64(ha->addr);
+ mac = mlx4_mac_to_u64(ha->addr);
memcpy(entry->mac, ha->addr, ETH_ALEN);
err = mlx4_register_mac(mdev->dev, priv->port, mac);
if (err < 0) {
@@ -1677,7 +1666,7 @@ int mlx4_en_start_port(struct net_device *dev)
}
if (mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) {
- err = mlx4_SET_PORT_VXLAN(mdev->dev, priv->port, VXLAN_STEER_BY_OUTER_MAC);
+ err = mlx4_SET_PORT_VXLAN(mdev->dev, priv->port, VXLAN_STEER_BY_OUTER_MAC, 1);
if (err) {
en_err(priv, "Failed setting port L2 tunnel configuration, err %d\n",
err);
@@ -1709,6 +1698,10 @@ int mlx4_en_start_port(struct net_device *dev)
mlx4_set_stats_bitmap(mdev->dev, &priv->stats_bitmap);
+#ifdef CONFIG_MLX4_EN_VXLAN
+ if (priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_VXLAN_OFFLOADS)
+ vxlan_get_rx_port(dev);
+#endif
priv->port_up = true;
netif_tx_start_all_queues(dev);
netif_device_attach(dev);
@@ -2216,7 +2209,7 @@ static int mlx4_en_set_vf_mac(struct net_device *dev, int queue, u8 *mac)
{
struct mlx4_en_priv *en_priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = en_priv->mdev;
- u64 mac_u64 = mlx4_en_mac_to_u64(mac);
+ u64 mac_u64 = mlx4_mac_to_u64(mac);
if (!is_valid_ether_addr(mac))
return -EINVAL;
@@ -2276,6 +2269,83 @@ static int mlx4_en_get_phys_port_id(struct net_device *dev,
return 0;
}
+#ifdef CONFIG_MLX4_EN_VXLAN
+static void mlx4_en_add_vxlan_offloads(struct work_struct *work)
+{
+ int ret;
+ struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv,
+ vxlan_add_task);
+
+ ret = mlx4_config_vxlan_port(priv->mdev->dev, priv->vxlan_port);
+ if (ret)
+ goto out;
+
+ ret = mlx4_SET_PORT_VXLAN(priv->mdev->dev, priv->port,
+ VXLAN_STEER_BY_OUTER_MAC, 1);
+out:
+ if (ret)
+ en_err(priv, "failed setting L2 tunnel configuration ret %d\n", ret);
+}
+
+static void mlx4_en_del_vxlan_offloads(struct work_struct *work)
+{
+ int ret;
+ struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv,
+ vxlan_del_task);
+
+ ret = mlx4_SET_PORT_VXLAN(priv->mdev->dev, priv->port,
+ VXLAN_STEER_BY_OUTER_MAC, 0);
+ if (ret)
+ en_err(priv, "failed setting L2 tunnel configuration ret %d\n", ret);
+
+ priv->vxlan_port = 0;
+}
+
+static void mlx4_en_add_vxlan_port(struct net_device *dev,
+ sa_family_t sa_family, __be16 port)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ __be16 current_port;
+
+ if (!(priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_VXLAN_OFFLOADS))
+ return;
+
+ if (sa_family == AF_INET6)
+ return;
+
+ current_port = priv->vxlan_port;
+ if (current_port && current_port != port) {
+ en_warn(priv, "vxlan port %d configured, can't add port %d\n",
+ ntohs(current_port), ntohs(port));
+ return;
+ }
+
+ priv->vxlan_port = port;
+ queue_work(priv->mdev->workqueue, &priv->vxlan_add_task);
+}
+
+static void mlx4_en_del_vxlan_port(struct net_device *dev,
+ sa_family_t sa_family, __be16 port)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ __be16 current_port;
+
+ if (priv->mdev->dev->caps.tunnel_offload_mode != MLX4_TUNNEL_OFFLOAD_MODE_VXLAN)
+ return;
+
+ if (sa_family == AF_INET6)
+ return;
+
+ current_port = priv->vxlan_port;
+ if (current_port != port) {
+ en_dbg(DRV, priv, "vxlan port %d isn't configured, ignoring\n", ntohs(port));
+ return;
+ }
+
+ queue_work(priv->mdev->workqueue, &priv->vxlan_del_task);
+}
+#endif
+
static const struct net_device_ops mlx4_netdev_ops = {
.ndo_open = mlx4_en_open,
.ndo_stop = mlx4_en_close,
@@ -2302,6 +2372,10 @@ static const struct net_device_ops mlx4_netdev_ops = {
.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,
+ .ndo_del_vxlan_port = mlx4_en_del_vxlan_port,
+#endif
};
static const struct net_device_ops mlx4_netdev_ops_master = {
@@ -2351,7 +2425,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
netif_set_real_num_rx_queues(dev, prof->rx_ring_num);
SET_NETDEV_DEV(dev, &mdev->dev->pdev->dev);
- dev->dev_id = port - 1;
+ dev->dev_port = port - 1;
/*
* Initialize driver private data
@@ -2393,6 +2467,10 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
INIT_WORK(&priv->linkstate_task, mlx4_en_linkstate);
INIT_DELAYED_WORK(&priv->stats_task, mlx4_en_do_get_stats);
INIT_DELAYED_WORK(&priv->service_task, mlx4_en_service_task);
+#ifdef CONFIG_MLX4_EN_VXLAN
+ INIT_WORK(&priv->vxlan_add_task, mlx4_en_add_vxlan_offloads);
+ INIT_WORK(&priv->vxlan_del_task, mlx4_en_del_vxlan_offloads);
+#endif
#ifdef CONFIG_MLX4_EN_DCB
if (!mlx4_is_slave(priv->mdev->dev)) {
if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_SET_ETH_SCHED) {
@@ -2417,7 +2495,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
if (mlx4_is_slave(priv->mdev->dev)) {
eth_hw_addr_random(dev);
en_warn(priv, "Assigned random MAC address %pM\n", dev->dev_addr);
- mac_u64 = mlx4_en_mac_to_u64(dev->dev_addr);
+ mac_u64 = mlx4_mac_to_u64(dev->dev_addr);
mdev->dev->caps.def_mac[priv->port] = mac_u64;
} else {
en_err(priv, "Port: %d, invalid mac burned: %pM, quiting\n",
@@ -2526,7 +2604,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
}
if (mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) {
- err = mlx4_SET_PORT_VXLAN(mdev->dev, priv->port, VXLAN_STEER_BY_OUTER_MAC);
+ err = mlx4_SET_PORT_VXLAN(mdev->dev, priv->port, VXLAN_STEER_BY_OUTER_MAC, 1);
if (err) {
en_err(priv, "Failed setting port L2 tunnel configuration, err %d\n",
err);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c
index dae1a1f4ae55..c2cfb05e7290 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c
@@ -148,10 +148,16 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset)
stats->tx_packets = 0;
stats->tx_bytes = 0;
priv->port_stats.tx_chksum_offload = 0;
+ priv->port_stats.queue_stopped = 0;
+ priv->port_stats.wake_queue = 0;
+
for (i = 0; i < priv->tx_ring_num; i++) {
stats->tx_packets += priv->tx_ring[i]->packets;
stats->tx_bytes += priv->tx_ring[i]->bytes;
priv->port_stats.tx_chksum_offload += priv->tx_ring[i]->tx_csum;
+ priv->port_stats.queue_stopped +=
+ priv->tx_ring[i]->queue_stopped;
+ priv->port_stats.wake_queue += priv->tx_ring[i]->wake_queue;
}
stats->rx_errors = be64_to_cpu(mlx4_en_stats->PCS) +
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index 890922c1c8ee..ba049ae88749 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -318,6 +318,31 @@ static void mlx4_en_free_rx_buf(struct mlx4_en_priv *priv,
}
}
+void mlx4_en_set_num_rx_rings(struct mlx4_en_dev *mdev)
+{
+ int i;
+ int num_of_eqs;
+ int num_rx_rings;
+ struct mlx4_dev *dev = mdev->dev;
+
+ mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) {
+ if (!dev->caps.comp_pool)
+ num_of_eqs = max_t(int, MIN_RX_RINGS,
+ min_t(int,
+ dev->caps.num_comp_vectors,
+ DEF_RX_RINGS));
+ else
+ num_of_eqs = min_t(int, MAX_MSIX_P_PORT,
+ dev->caps.comp_pool/
+ dev->caps.num_ports) - 1;
+
+ num_rx_rings = min_t(int, num_of_eqs,
+ netif_get_num_default_rss_queues());
+ mdev->profile.prof[i].rx_ring_num =
+ rounddown_pow_of_two(num_rx_rings);
+ }
+}
+
int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv,
struct mlx4_en_rx_ring **pring,
u32 size, u16 stride, int node)
@@ -636,6 +661,9 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
if (!priv->port_up)
return 0;
+ if (budget <= 0)
+ return polled;
+
/* We assume a 1:1 mapping between CQEs and Rx descriptors, so Rx
* descriptor offset can be deduced from the CQE index instead of
* reading 'cqe->index' */
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_selftest.c b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c
index c11d063473e5..03e5f6ac67e7 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_selftest.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c
@@ -129,8 +129,10 @@ static int mlx4_en_test_speed(struct mlx4_en_priv *priv)
if (mlx4_en_QUERY_PORT(priv->mdev, priv->port))
return -ENOMEM;
- /* The device currently only supports 10G speed */
- if (priv->port_state.link_speed != SPEED_10000)
+ /* The device supports 1G, 10G and 40G speeds */
+ if (priv->port_state.link_speed != 1000 &&
+ priv->port_state.link_speed != 10000 &&
+ priv->port_state.link_speed != 40000)
return priv->port_state.link_speed;
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index 13457032d15f..dd1f6d346459 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -44,16 +44,6 @@
#include "mlx4_en.h"
-enum {
- MAX_INLINE = 104, /* 128 - 16 - 4 - 4 */
- MAX_BF = 256,
-};
-
-static int inline_thold __read_mostly = MAX_INLINE;
-
-module_param_named(inline_thold, inline_thold, int, 0444);
-MODULE_PARM_DESC(inline_thold, "threshold for using inline data");
-
int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
struct mlx4_en_tx_ring **pring, int qpn, u32 size,
u16 stride, int node, int queue_index)
@@ -75,8 +65,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
ring->size = size;
ring->size_mask = size - 1;
ring->stride = stride;
-
- inline_thold = min(inline_thold, MAX_INLINE);
+ ring->inline_thold = priv->prof->inline_thold;
tmp = size * sizeof(struct mlx4_en_tx_info);
ring->tx_info = vmalloc_node(tmp, node);
@@ -325,7 +314,7 @@ static u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv,
}
}
}
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return tx_info->nr_txbb;
}
@@ -456,7 +445,7 @@ static int mlx4_en_process_tx_cq(struct net_device *dev,
*/
if (netif_tx_queue_stopped(ring->tx_queue) && txbbs_skipped > 0) {
netif_tx_wake_queue(ring->tx_queue);
- priv->port_stats.wake_queue++;
+ ring->wake_queue++;
}
return done;
}
@@ -520,7 +509,7 @@ static struct mlx4_en_tx_desc *mlx4_en_bounce_to_desc(struct mlx4_en_priv *priv,
return ring->buf + index * TXBB_SIZE;
}
-static int is_inline(struct sk_buff *skb, void **pfrag)
+static int is_inline(int inline_thold, struct sk_buff *skb, void **pfrag)
{
void *ptr;
@@ -580,7 +569,7 @@ static int get_real_size(struct sk_buff *skb, struct net_device *dev,
}
} else {
*lso_header_size = 0;
- if (!is_inline(skb, NULL))
+ if (!is_inline(priv->prof->inline_thold, skb, NULL))
real_size = CTRL_SIZE + (skb_shinfo(skb)->nr_frags + 1) * DS_SIZE;
else
real_size = inline_size(skb);
@@ -596,7 +585,13 @@ static void build_inline_wqe(struct mlx4_en_tx_desc *tx_desc, struct sk_buff *sk
int spc = MLX4_INLINE_ALIGN - CTRL_SIZE - sizeof *inl;
if (skb->len <= spc) {
- inl->byte_count = cpu_to_be32(1 << 31 | skb->len);
+ if (likely(skb->len >= MIN_PKT_LEN)) {
+ inl->byte_count = cpu_to_be32(1 << 31 | skb->len);
+ } else {
+ inl->byte_count = cpu_to_be32(1 << 31 | MIN_PKT_LEN);
+ memset(((void *)(inl + 1)) + skb->len, 0,
+ MIN_PKT_LEN - skb->len);
+ }
skb_copy_from_linear_data(skb, inl + 1, skb_headlen(skb));
if (skb_shinfo(skb)->nr_frags)
memcpy(((void *)(inl + 1)) + skb_headlen(skb), fragptr,
@@ -696,7 +691,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
ring->size - HEADROOM - MAX_DESC_TXBBS)) {
/* every full Tx ring stops queue */
netif_tx_stop_queue(ring->tx_queue);
- priv->port_stats.queue_stopped++;
+ ring->queue_stopped++;
/* If queue was emptied after the if, and before the
* stop_queue - need to wake the queue, or else it will remain
@@ -709,7 +704,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(((int)(ring->prod - ring->cons)) <=
ring->size - HEADROOM - MAX_DESC_TXBBS)) {
netif_tx_wake_queue(ring->tx_queue);
- priv->port_stats.wake_queue++;
+ ring->wake_queue++;
} else {
return NETDEV_TX_BUSY;
}
@@ -747,11 +742,11 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
tx_info->data_offset = (void *)data - (void *)tx_desc;
tx_info->linear = (lso_header_size < skb_headlen(skb) &&
- !is_inline(skb, NULL)) ? 1 : 0;
+ !is_inline(ring->inline_thold, skb, NULL)) ? 1 : 0;
data += skb_shinfo(skb)->nr_frags + tx_info->linear - 1;
- if (is_inline(skb, &fragptr)) {
+ if (is_inline(ring->inline_thold, skb, &fragptr)) {
tx_info->inl = 1;
} else {
/* Map fragments */
@@ -881,7 +876,8 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
skb_tx_timestamp(skb);
if (ring->bf_enabled && desc_size <= MAX_BF && !bounce && !vlan_tx_tag_present(skb)) {
- *(__be32 *) (&tx_desc->ctrl.vlan_tag) |= cpu_to_be32(ring->doorbell_qpn);
+ tx_desc->ctrl.bf_qpn |= cpu_to_be32(ring->doorbell_qpn);
+
op_own |= htonl((bf_index & 0xffff) << 8);
/* Ensure new descirptor hits memory
* before setting ownership of this descriptor to HW */
diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c
index 8992b38578d5..d501a2b0fb79 100644
--- a/drivers/net/ethernet/mellanox/mlx4/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/eq.c
@@ -271,7 +271,10 @@ enum slave_port_state mlx4_get_slave_port_state(struct mlx4_dev *dev, int slave,
{
struct mlx4_priv *priv = mlx4_priv(dev);
struct mlx4_slave_state *s_state = priv->mfunc.master.slave_state;
- if (slave >= dev->num_slaves || port > MLX4_MAX_PORTS) {
+ struct mlx4_active_ports actv_ports = mlx4_get_active_ports(dev, slave);
+
+ if (slave >= dev->num_slaves || port > dev->caps.num_ports ||
+ port <= 0 || !test_bit(port - 1, actv_ports.ports)) {
pr_err("%s: Error: asking for slave:%d, port:%d\n",
__func__, slave, port);
return SLAVE_PORT_DOWN;
@@ -285,8 +288,10 @@ static int mlx4_set_slave_port_state(struct mlx4_dev *dev, int slave, u8 port,
{
struct mlx4_priv *priv = mlx4_priv(dev);
struct mlx4_slave_state *s_state = priv->mfunc.master.slave_state;
+ struct mlx4_active_ports actv_ports = mlx4_get_active_ports(dev, slave);
- if (slave >= dev->num_slaves || port > MLX4_MAX_PORTS || port == 0) {
+ if (slave >= dev->num_slaves || port > dev->caps.num_ports ||
+ port <= 0 || !test_bit(port - 1, actv_ports.ports)) {
pr_err("%s: Error: asking for slave:%d, port:%d\n",
__func__, slave, port);
return -1;
@@ -300,9 +305,13 @@ static void set_all_slave_state(struct mlx4_dev *dev, u8 port, int event)
{
int i;
enum slave_port_gen_event gen_event;
+ struct mlx4_slaves_pport slaves_pport = mlx4_phys_to_slaves_pport(dev,
+ port);
- for (i = 0; i < dev->num_slaves; i++)
- set_and_calc_slave_port_state(dev, i, port, event, &gen_event);
+ for (i = 0; i < dev->num_vfs + 1; i++)
+ if (test_bit(i, slaves_pport.slaves))
+ set_and_calc_slave_port_state(dev, i, port,
+ event, &gen_event);
}
/**************************************************************************
The function get as input the new event to that port,
@@ -321,12 +330,14 @@ int set_and_calc_slave_port_state(struct mlx4_dev *dev, int slave,
struct mlx4_slave_state *ctx = NULL;
unsigned long flags;
int ret = -1;
+ struct mlx4_active_ports actv_ports = mlx4_get_active_ports(dev, slave);
enum slave_port_state cur_state =
mlx4_get_slave_port_state(dev, slave, port);
*gen_event = SLAVE_PORT_GEN_EVENT_NONE;
- if (slave >= dev->num_slaves || port > MLX4_MAX_PORTS || port == 0) {
+ if (slave >= dev->num_slaves || port > dev->caps.num_ports ||
+ port <= 0 || !test_bit(port - 1, actv_ports.ports)) {
pr_err("%s: Error: asking for slave:%d, port:%d\n",
__func__, slave, port);
return ret;
@@ -542,15 +553,19 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
be64_to_cpu(eqe->event.cmd.out_param));
break;
- case MLX4_EVENT_TYPE_PORT_CHANGE:
+ case MLX4_EVENT_TYPE_PORT_CHANGE: {
+ struct mlx4_slaves_pport slaves_port;
port = be32_to_cpu(eqe->event.port_change.port) >> 28;
+ slaves_port = mlx4_phys_to_slaves_pport(dev, port);
if (eqe->subtype == MLX4_PORT_CHANGE_SUBTYPE_DOWN) {
mlx4_dispatch_event(dev, MLX4_DEV_EVENT_PORT_DOWN,
port);
mlx4_priv(dev)->sense.do_sense_port[port] = 1;
if (!mlx4_is_master(dev))
break;
- for (i = 0; i < dev->num_slaves; i++) {
+ for (i = 0; i < dev->num_vfs + 1; i++) {
+ if (!test_bit(i, slaves_port.slaves))
+ continue;
if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) {
if (i == mlx4_master_func_num(dev))
continue;
@@ -558,8 +573,13 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
" to slave: %d, port:%d\n",
__func__, i, port);
s_info = &priv->mfunc.master.vf_oper[slave].vport[port].state;
- if (IFLA_VF_LINK_STATE_AUTO == s_info->link_state)
+ if (IFLA_VF_LINK_STATE_AUTO == s_info->link_state) {
+ 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));
mlx4_slave_event(dev, i, eqe);
+ }
} else { /* IB port */
set_and_calc_slave_port_state(dev, i, port,
MLX4_PORT_STATE_DEV_EVENT_PORT_DOWN,
@@ -580,12 +600,19 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
if (!mlx4_is_master(dev))
break;
if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH)
- for (i = 0; i < dev->num_slaves; i++) {
+ for (i = 0; i < dev->num_vfs + 1; i++) {
+ if (!test_bit(i, slaves_port.slaves))
+ continue;
if (i == mlx4_master_func_num(dev))
continue;
s_info = &priv->mfunc.master.vf_oper[slave].vport[port].state;
- if (IFLA_VF_LINK_STATE_AUTO == s_info->link_state)
+ if (IFLA_VF_LINK_STATE_AUTO == s_info->link_state) {
+ 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));
mlx4_slave_event(dev, i, eqe);
+ }
}
else /* IB port */
/* port-up event will be sent to a slave when the
@@ -594,6 +621,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
set_all_slave_state(dev, port, MLX4_DEV_EVENT_PORT_UP);
}
break;
+ }
case MLX4_EVENT_TYPE_CQ_ERROR:
mlx4_warn(dev, "CQ %s on CQN %06x\n",
diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c
index 7e2995ecea6f..d16a4d118903 100644
--- a/drivers/net/ethernet/mellanox/mlx4/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.c
@@ -225,13 +225,25 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave,
#define QUERY_FUNC_CAP_FLAGS0_FORCE_PHY_WQE_GID 0x80
if (vhcr->op_modifier == 1) {
+ struct mlx4_active_ports actv_ports =
+ mlx4_get_active_ports(dev, slave);
+ int converted_port = mlx4_slave_convert_port(
+ dev, slave, vhcr->in_modifier);
+
+ if (converted_port < 0)
+ return -EINVAL;
+
+ vhcr->in_modifier = converted_port;
/* Set nic_info bit to mark new fields support */
field = QUERY_FUNC_CAP_FLAGS1_NIC_INFO;
MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_FLAGS1_OFFSET);
- field = vhcr->in_modifier; /* phys-port = logical-port */
+ /* phys-port = logical-port */
+ field = vhcr->in_modifier -
+ find_first_bit(actv_ports.ports, dev->caps.num_ports);
MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_PHYS_PORT_OFFSET);
+ field = vhcr->in_modifier;
/* size is now the QP number */
size = dev->phys_caps.base_tunnel_sqpn + 8 * slave + field - 1;
MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_QP0_TUNNEL);
@@ -249,12 +261,16 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave,
QUERY_FUNC_CAP_PHYS_PORT_ID);
} else if (vhcr->op_modifier == 0) {
+ struct mlx4_active_ports actv_ports =
+ mlx4_get_active_ports(dev, slave);
/* enable rdma and ethernet interfaces, and new quota locations */
field = (QUERY_FUNC_CAP_FLAG_ETH | QUERY_FUNC_CAP_FLAG_RDMA |
QUERY_FUNC_CAP_FLAG_QUOTAS);
MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_FLAGS_OFFSET);
- field = dev->caps.num_ports;
+ field = min(
+ bitmap_weight(actv_ports.ports, dev->caps.num_ports),
+ dev->caps.num_ports);
MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_NUM_PORTS_OFFSET);
size = dev->caps.function_caps; /* set PF behaviours */
@@ -840,6 +856,10 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave,
int err = 0;
u8 field;
u32 bmme_flags;
+ int real_port;
+ int slave_port;
+ int first_port;
+ struct mlx4_active_ports actv_ports;
err = mlx4_cmd_box(dev, 0, outbox->dma, 0, 0, MLX4_CMD_QUERY_DEV_CAP,
MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE);
@@ -852,8 +872,26 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave,
MLX4_GET(flags, outbox->buf, QUERY_DEV_CAP_EXT_FLAGS_OFFSET);
flags |= MLX4_DEV_CAP_FLAG_PORT_MNG_CHG_EV;
flags &= ~MLX4_DEV_CAP_FLAG_MEM_WINDOW;
+ actv_ports = mlx4_get_active_ports(dev, slave);
+ first_port = find_first_bit(actv_ports.ports, dev->caps.num_ports);
+ for (slave_port = 0, real_port = first_port;
+ real_port < first_port +
+ bitmap_weight(actv_ports.ports, dev->caps.num_ports);
+ ++real_port, ++slave_port) {
+ if (flags & (MLX4_DEV_CAP_FLAG_WOL_PORT1 << real_port))
+ flags |= MLX4_DEV_CAP_FLAG_WOL_PORT1 << slave_port;
+ else
+ flags &= ~(MLX4_DEV_CAP_FLAG_WOL_PORT1 << slave_port);
+ }
+ for (; slave_port < dev->caps.num_ports; ++slave_port)
+ flags &= ~(MLX4_DEV_CAP_FLAG_WOL_PORT1 << slave_port);
MLX4_PUT(outbox->buf, flags, QUERY_DEV_CAP_EXT_FLAGS_OFFSET);
+ MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_VL_PORT_OFFSET);
+ field &= ~0x0F;
+ field |= bitmap_weight(actv_ports.ports, dev->caps.num_ports) & 0x0F;
+ MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_VL_PORT_OFFSET);
+
/* For guests, disable timestamp */
MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_CQ_TS_SUPPORT_OFFSET);
field &= 0x7f;
@@ -903,12 +941,20 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave,
u16 short_field;
int err;
int admin_link_state;
+ int port = mlx4_slave_convert_port(dev, slave,
+ vhcr->in_modifier & 0xFF);
#define MLX4_VF_PORT_NO_LINK_SENSE_MASK 0xE0
#define MLX4_PORT_LINK_UP_MASK 0x80
#define QUERY_PORT_CUR_MAX_PKEY_OFFSET 0x0c
#define QUERY_PORT_CUR_MAX_GID_OFFSET 0x0e
+ if (port < 0)
+ return -EINVAL;
+
+ vhcr->in_modifier = (vhcr->in_modifier & ~0xFF) |
+ (port & 0xFF);
+
err = mlx4_cmd_box(dev, 0, outbox->dma, vhcr->in_modifier, 0,
MLX4_CMD_QUERY_PORT, MLX4_CMD_TIME_CLASS_B,
MLX4_CMD_NATIVE);
@@ -935,7 +981,10 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave,
MLX4_PUT(outbox->buf, port_type,
QUERY_PORT_SUPPORTED_TYPE_OFFSET);
- short_field = 1; /* slave max gids */
+ if (dev->caps.port_type[vhcr->in_modifier] == MLX4_PORT_TYPE_ETH)
+ short_field = mlx4_get_slave_num_gids(dev, slave, port);
+ else
+ short_field = 1; /* slave max gids */
MLX4_PUT(outbox->buf, short_field,
QUERY_PORT_CUR_MAX_GID_OFFSET);
@@ -1585,9 +1634,12 @@ int mlx4_INIT_PORT_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_cmd_info *cmd)
{
struct mlx4_priv *priv = mlx4_priv(dev);
- int port = vhcr->in_modifier;
+ int port = mlx4_slave_convert_port(dev, slave, vhcr->in_modifier);
int err;
+ if (port < 0)
+ return -EINVAL;
+
if (priv->mfunc.master.slave_state[slave].init_port_mask & (1 << port))
return 0;
@@ -1677,9 +1729,12 @@ int mlx4_CLOSE_PORT_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_cmd_info *cmd)
{
struct mlx4_priv *priv = mlx4_priv(dev);
- int port = vhcr->in_modifier;
+ int port = mlx4_slave_convert_port(dev, slave, vhcr->in_modifier);
int err;
+ if (port < 0)
+ return -EINVAL;
+
if (!(priv->mfunc.master.slave_state[slave].init_port_mask &
(1 << port)))
return 0;
@@ -1724,6 +1779,46 @@ int mlx4_CLOSE_HCA(struct mlx4_dev *dev, int panic)
MLX4_CMD_NATIVE);
}
+struct mlx4_config_dev {
+ __be32 update_flags;
+ __be32 rsdv1[3];
+ __be16 vxlan_udp_dport;
+ __be16 rsvd2;
+};
+
+#define MLX4_VXLAN_UDP_DPORT (1 << 0)
+
+static int mlx4_CONFIG_DEV(struct mlx4_dev *dev, struct mlx4_config_dev *config_dev)
+{
+ int err;
+ struct mlx4_cmd_mailbox *mailbox;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+
+ memcpy(mailbox->buf, config_dev, sizeof(*config_dev));
+
+ err = mlx4_cmd(dev, mailbox->dma, 0, 0, MLX4_CMD_CONFIG_DEV,
+ MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
+
+ mlx4_free_cmd_mailbox(dev, mailbox);
+ return err;
+}
+
+int mlx4_config_vxlan_port(struct mlx4_dev *dev, __be16 udp_port)
+{
+ struct mlx4_config_dev config_dev;
+
+ memset(&config_dev, 0, sizeof(config_dev));
+ config_dev.update_flags = cpu_to_be32(MLX4_VXLAN_UDP_DPORT);
+ config_dev.vxlan_udp_dport = udp_port;
+
+ return mlx4_CONFIG_DEV(dev, &config_dev);
+}
+EXPORT_SYMBOL_GPL(mlx4_config_vxlan_port);
+
+
int mlx4_SET_ICM_SIZE(struct mlx4_dev *dev, u64 icm_size, u64 *aux_pages)
{
int ret = mlx4_cmd_imm(dev, icm_size, aux_pages, 0, 0,
@@ -1891,7 +1986,8 @@ void mlx4_opreq_action(struct work_struct *work)
err = EINVAL;
break;
}
- err = mlx4_cmd(dev, 0, ((u32) err | cpu_to_be32(token) << 16),
+ err = mlx4_cmd(dev, 0, ((u32) err |
+ (__force u32)cpu_to_be32(token) << 16),
1, MLX4_CMD_GET_OP_REQ, MLX4_CMD_TIME_CLASS_A,
MLX4_CMD_NATIVE);
if (err) {
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index d413e60071d4..f0ae95f66ceb 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -41,7 +41,6 @@
#include <linux/slab.h>
#include <linux/io-mapping.h>
#include <linux/delay.h>
-#include <linux/netdevice.h>
#include <linux/kmod.h>
#include <linux/mlx4/device.h>
@@ -78,13 +77,17 @@ MODULE_PARM_DESC(msi_x, "attempt to use MSI-X if nonzero");
#endif /* CONFIG_PCI_MSI */
-static int num_vfs;
-module_param(num_vfs, int, 0444);
-MODULE_PARM_DESC(num_vfs, "enable #num_vfs functions if num_vfs > 0");
+static uint8_t num_vfs[3] = {0, 0, 0};
+static int num_vfs_argc = 3;
+module_param_array(num_vfs, byte , &num_vfs_argc, 0444);
+MODULE_PARM_DESC(num_vfs, "enable #num_vfs functions if num_vfs > 0\n"
+ "num_vfs=port1,port2,port1+2");
-static int probe_vf;
-module_param(probe_vf, int, 0644);
-MODULE_PARM_DESC(probe_vf, "number of vfs to probe by pf driver (num_vfs > 0)");
+static uint8_t probe_vf[3] = {0, 0, 0};
+static int probe_vfs_argc = 3;
+module_param_array(probe_vf, byte, &probe_vfs_argc, 0444);
+MODULE_PARM_DESC(probe_vf, "number of vfs to probe by pf driver (num_vfs > 0)\n"
+ "probe_vf=port1,port2,port1+2");
int mlx4_log_num_mgm_entry_size = MLX4_DEFAULT_MGM_LOG_ENTRY_SIZE;
module_param_named(log_num_mgm_entry_size,
@@ -1470,7 +1473,11 @@ static void mlx4_parav_master_pf_caps(struct mlx4_dev *dev)
int i;
for (i = 1; i <= dev->caps.num_ports; i++) {
- dev->caps.gid_table_len[i] = 1;
+ if (dev->caps.port_type[i] == MLX4_PORT_TYPE_ETH)
+ dev->caps.gid_table_len[i] =
+ mlx4_get_slave_num_gids(dev, 0, i);
+ else
+ dev->caps.gid_table_len[i] = 1;
dev->caps.pkey_table_len[i] =
dev->phys_caps.pkey_phys_table_len[i] - 1;
}
@@ -1495,7 +1502,7 @@ static void choose_steering_mode(struct mlx4_dev *dev,
if (mlx4_log_num_mgm_entry_size == -1 &&
dev_cap->flags2 & MLX4_DEV_CAP_FLAG2_FS_EN &&
(!mlx4_is_mfunc(dev) ||
- (dev_cap->fs_max_num_qp_per_entry >= (num_vfs + 1))) &&
+ (dev_cap->fs_max_num_qp_per_entry >= (dev->num_vfs + 1))) &&
choose_log_fs_mgm_entry_size(dev_cap->fs_max_num_qp_per_entry) >=
MLX4_MIN_MGM_LOG_ENTRY_SIZE) {
dev->oper_log_mgm_entry_size =
@@ -1981,9 +1988,8 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev)
struct mlx4_priv *priv = mlx4_priv(dev);
struct msix_entry *entries;
int nreq = min_t(int, dev->caps.num_ports *
- min_t(int, netif_get_num_default_rss_queues() + 1,
+ min_t(int, num_online_cpus() + 1,
MAX_MSIX_P_PORT) + MSIX_LEGACY_SZ, MAX_MSIX);
- int err;
int i;
if (msi_x) {
@@ -1997,23 +2003,13 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev)
for (i = 0; i < nreq; ++i)
entries[i].entry = i;
- retry:
- err = pci_enable_msix(dev->pdev, entries, nreq);
- if (err) {
- /* Try again if at least 2 vectors are available */
- if (err > 1) {
- mlx4_info(dev, "Requested %d vectors, "
- "but only %d MSI-X vectors available, "
- "trying again\n", nreq, err);
- nreq = err;
- goto retry;
- }
+ nreq = pci_enable_msix_range(dev->pdev, entries, 2, nreq);
+
+ if (nreq < 0) {
kfree(entries);
goto no_msi;
- }
-
- if (nreq <
- MSIX_LEGACY_SZ + dev->caps.num_ports * MIN_MSIX_P_PORT) {
+ } else if (nreq < MSIX_LEGACY_SZ +
+ dev->caps.num_ports * MIN_MSIX_P_PORT) {
/*Working in legacy mode , all EQ's shared*/
dev->caps.comp_pool = 0;
dev->caps.num_comp_vectors = nreq - 1;
@@ -2201,6 +2197,13 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
struct mlx4_dev *dev;
int err;
int port;
+ int nvfs[MLX4_MAX_PORTS + 1] = {0, 0, 0};
+ int prb_vf[MLX4_MAX_PORTS + 1] = {0, 0, 0};
+ const int param_map[MLX4_MAX_PORTS + 1][MLX4_MAX_PORTS + 1] = {
+ {2, 0, 0}, {0, 1, 2}, {0, 1, 2} };
+ unsigned total_vfs = 0;
+ int sriov_initialized = 0;
+ unsigned int i;
pr_info(DRV_NAME ": Initializing %s\n", pci_name(pdev));
@@ -2215,17 +2218,40 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
* per port, we must limit the number of VFs to 63 (since their are
* 128 MACs)
*/
- if (num_vfs >= MLX4_MAX_NUM_VF) {
+ for (i = 0; i < sizeof(nvfs)/sizeof(nvfs[0]) && i < num_vfs_argc;
+ total_vfs += nvfs[param_map[num_vfs_argc - 1][i]], i++) {
+ nvfs[param_map[num_vfs_argc - 1][i]] = num_vfs[i];
+ if (nvfs[i] < 0) {
+ dev_err(&pdev->dev, "num_vfs module parameter cannot be negative\n");
+ return -EINVAL;
+ }
+ }
+ for (i = 0; i < sizeof(prb_vf)/sizeof(prb_vf[0]) && i < probe_vfs_argc;
+ i++) {
+ prb_vf[param_map[probe_vfs_argc - 1][i]] = probe_vf[i];
+ if (prb_vf[i] < 0 || prb_vf[i] > nvfs[i]) {
+ dev_err(&pdev->dev, "probe_vf module parameter cannot be negative or greater than num_vfs\n");
+ return -EINVAL;
+ }
+ }
+ if (total_vfs >= MLX4_MAX_NUM_VF) {
dev_err(&pdev->dev,
"Requested more VF's (%d) than allowed (%d)\n",
- num_vfs, MLX4_MAX_NUM_VF - 1);
+ total_vfs, MLX4_MAX_NUM_VF - 1);
return -EINVAL;
}
- if (num_vfs < 0) {
- pr_err("num_vfs module parameter cannot be negative\n");
- return -EINVAL;
+ for (i = 0; i < MLX4_MAX_PORTS; i++) {
+ if (nvfs[i] + nvfs[2] >= MLX4_MAX_NUM_VF_P_PORT) {
+ dev_err(&pdev->dev,
+ "Requested more VF's (%d) for port (%d) than allowed (%d)\n",
+ nvfs[i] + nvfs[2], i + 1,
+ MLX4_MAX_NUM_VF_P_PORT - 1);
+ return -EINVAL;
+ }
}
+
+
/*
* Check for BARs.
*/
@@ -2300,11 +2326,23 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
if (pci_dev_data & MLX4_PCI_DEV_IS_VF) {
/* When acting as pf, we normally skip vfs unless explicitly
* requested to probe them. */
- if (num_vfs && extended_func_num(pdev) > probe_vf) {
- mlx4_warn(dev, "Skipping virtual function:%d\n",
- extended_func_num(pdev));
- err = -ENODEV;
- goto err_free_dev;
+ if (total_vfs) {
+ unsigned vfs_offset = 0;
+ for (i = 0; i < sizeof(nvfs)/sizeof(nvfs[0]) &&
+ vfs_offset + nvfs[i] < extended_func_num(pdev);
+ vfs_offset += nvfs[i], i++)
+ ;
+ if (i == sizeof(nvfs)/sizeof(nvfs[0])) {
+ err = -ENODEV;
+ goto err_free_dev;
+ }
+ if ((extended_func_num(pdev) - vfs_offset)
+ > prb_vf[i]) {
+ mlx4_warn(dev, "Skipping virtual function:%d\n",
+ extended_func_num(pdev));
+ err = -ENODEV;
+ goto err_free_dev;
+ }
}
mlx4_warn(dev, "Detected virtual function - running in slave mode\n");
dev->flags |= MLX4_FLAG_SLAVE;
@@ -2324,22 +2362,30 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
}
}
- if (num_vfs) {
- mlx4_warn(dev, "Enabling SR-IOV with %d VFs\n", num_vfs);
-
- atomic_inc(&pf_loading);
- err = pci_enable_sriov(pdev, num_vfs);
- atomic_dec(&pf_loading);
-
- if (err) {
- mlx4_err(dev, "Failed to enable SR-IOV, continuing without SR-IOV (err = %d).\n",
- err);
+ if (total_vfs) {
+ mlx4_warn(dev, "Enabling SR-IOV with %d VFs\n",
+ total_vfs);
+ dev->dev_vfs = kzalloc(
+ total_vfs * sizeof(*dev->dev_vfs),
+ GFP_KERNEL);
+ if (NULL == dev->dev_vfs) {
+ mlx4_err(dev, "Failed to allocate memory for VFs\n");
err = 0;
} else {
- mlx4_warn(dev, "Running in master mode\n");
- dev->flags |= MLX4_FLAG_SRIOV |
- MLX4_FLAG_MASTER;
- dev->num_vfs = num_vfs;
+ atomic_inc(&pf_loading);
+ err = pci_enable_sriov(pdev, total_vfs);
+ atomic_dec(&pf_loading);
+ if (err) {
+ mlx4_err(dev, "Failed to enable SR-IOV, continuing without SR-IOV (err = %d).\n",
+ err);
+ err = 0;
+ } else {
+ mlx4_warn(dev, "Running in master mode\n");
+ dev->flags |= MLX4_FLAG_SRIOV |
+ MLX4_FLAG_MASTER;
+ dev->num_vfs = total_vfs;
+ sriov_initialized = 1;
+ }
}
}
@@ -2404,12 +2450,37 @@ slave_start:
/* In master functions, the communication channel must be initialized
* after obtaining its address from fw */
if (mlx4_is_master(dev)) {
+ unsigned sum = 0;
err = mlx4_multi_func_init(dev);
if (err) {
mlx4_err(dev, "Failed to init master mfunc"
"interface, aborting.\n");
goto err_close;
}
+ if (sriov_initialized) {
+ int ib_ports = 0;
+ mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB)
+ ib_ports++;
+
+ if (ib_ports &&
+ (num_vfs_argc > 1 || probe_vfs_argc > 1)) {
+ mlx4_err(dev,
+ "Invalid syntax of num_vfs/probe_vfs "
+ "with IB port. Single port VFs syntax"
+ " is only supported when all ports "
+ "are configured as ethernet\n");
+ goto err_close;
+ }
+ for (i = 0; i < sizeof(nvfs)/sizeof(nvfs[0]); i++) {
+ unsigned j;
+ for (j = 0; j < nvfs[i]; ++sum, ++j) {
+ dev->dev_vfs[sum].min_port =
+ i < 2 ? i + 1 : 1;
+ dev->dev_vfs[sum].n_ports = i < 2 ? 1 :
+ dev->caps.num_ports;
+ }
+ }
+ }
}
err = mlx4_alloc_eq_table(dev);
@@ -2517,6 +2588,8 @@ err_rel_own:
if (!mlx4_is_slave(dev))
mlx4_free_ownership(dev);
+ kfree(priv->dev.dev_vfs);
+
err_free_dev:
kfree(priv);
@@ -2603,6 +2676,7 @@ static void mlx4_remove_one(struct pci_dev *pdev)
kfree(dev->caps.qp0_proxy);
kfree(dev->caps.qp1_tunnel);
kfree(dev->caps.qp1_proxy);
+ kfree(dev->dev_vfs);
kfree(priv);
pci_release_regions(pdev);
diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c
index db7dc0b6667d..80ccb4edf825 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mcg.c
+++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c
@@ -1387,9 +1387,12 @@ int mlx4_PROMISC_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_cmd_info *cmd)
{
u32 qpn = (u32) vhcr->in_param & 0xffffffff;
- u8 port = vhcr->in_param >> 62;
+ int port = mlx4_slave_convert_port(dev, slave, vhcr->in_param >> 62);
enum mlx4_steer_type steer = vhcr->in_modifier;
+ if (port < 0)
+ return -EINVAL;
+
/* Promiscuous unicast is not allowed in mfunc */
if (mlx4_is_mfunc(dev) && steer == MLX4_UC_STEER)
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
index 7aec6c833973..cf8be41abb36 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
@@ -788,6 +788,10 @@ enum {
MLX4_USE_RR = 1,
};
+struct mlx4_roce_gid_entry {
+ u8 raw[16];
+};
+
struct mlx4_priv {
struct mlx4_dev dev;
@@ -834,6 +838,7 @@ struct mlx4_priv {
int fs_hash_mode;
u8 virt2phys_pkey[MLX4_MFUNC_MAX][MLX4_MAX_PORTS][MLX4_MAX_PORT_PKEYS];
__be64 slave_node_guids[MLX4_MFUNC_MAX];
+ struct mlx4_roce_gid_entry roce_gids[MLX4_MAX_PORTS][MLX4_ROCE_MAX_GIDS];
atomic_t opreq_count;
struct work_struct opreq_task;
@@ -1242,11 +1247,6 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_cmd_mailbox *inbox,
struct mlx4_cmd_mailbox *outbox,
struct mlx4_cmd_info *cmd);
-int mlx4_FLOW_STEERING_IB_UC_QP_RANGE_wrapper(struct mlx4_dev *dev, int slave,
- struct mlx4_vhcr *vhcr,
- struct mlx4_cmd_mailbox *inbox,
- struct mlx4_cmd_mailbox *outbox,
- struct mlx4_cmd_info *cmd);
int mlx4_get_mgm_entry_size(struct mlx4_dev *dev);
int mlx4_get_qp_per_mgm(struct mlx4_dev *dev);
@@ -1282,4 +1282,8 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work);
void mlx4_init_quotas(struct mlx4_dev *dev);
+int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave, int port);
+/* Returns the VF index of slave */
+int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave);
+
#endif /* MLX4_H */
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index b57e8c87a34e..7a733c287744 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -187,6 +187,13 @@ enum {
#define GET_AVG_PERF_COUNTER(cnt) (0)
#endif /* MLX4_EN_PERF_STAT */
+/* Constants for TX flow */
+enum {
+ MAX_INLINE = 104, /* 128 - 16 - 4 - 4 */
+ MAX_BF = 256,
+ MIN_PKT_LEN = 17,
+};
+
/*
* Configurables
*/
@@ -267,10 +274,13 @@ struct mlx4_en_tx_ring {
unsigned long bytes;
unsigned long packets;
unsigned long tx_csum;
+ unsigned long queue_stopped;
+ unsigned long wake_queue;
struct mlx4_bf bf;
bool bf_enabled;
struct netdev_queue *tx_queue;
int hwtstamp_tx_type;
+ int inline_thold;
};
struct mlx4_en_rx_desc {
@@ -346,6 +356,7 @@ struct mlx4_en_port_profile {
u8 tx_pause;
u8 tx_ppp;
int rss_rings;
+ int inline_thold;
};
struct mlx4_en_profile {
@@ -548,6 +559,10 @@ struct mlx4_en_priv {
struct work_struct linkstate_task;
struct delayed_work stats_task;
struct delayed_work service_task;
+#ifdef CONFIG_MLX4_EN_VXLAN
+ struct work_struct vxlan_add_task;
+ struct work_struct vxlan_del_task;
+#endif
struct mlx4_en_perf_stats pstats;
struct mlx4_en_pkt_stats pkstats;
struct mlx4_en_port_stats port_stats;
@@ -574,6 +589,7 @@ struct mlx4_en_priv {
struct hlist_head filter_hash[1 << MLX4_EN_FILTER_HASH_SHIFT];
#endif
u64 tunnel_reg_id;
+ __be16 vxlan_port;
};
enum mlx4_en_wol {
@@ -737,7 +753,7 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
int cq, int user_prio);
void mlx4_en_deactivate_tx_ring(struct mlx4_en_priv *priv,
struct mlx4_en_tx_ring *ring);
-
+void mlx4_en_set_num_rx_rings(struct mlx4_en_dev *mdev);
int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv,
struct mlx4_en_rx_ring **pring,
u32 size, u16 stride, int node);
@@ -786,7 +802,6 @@ void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv);
#define MLX4_EN_NUM_SELF_TEST 5
void mlx4_en_ex_selftest(struct net_device *dev, u32 *flags, u64 *buf);
-u64 mlx4_en_mac_to_u64(u8 *addr);
void mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev);
/*
diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c
index a58bcbf1b806..cfcad26ed40f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/port.c
@@ -505,6 +505,84 @@ int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps)
mlx4_free_cmd_mailbox(dev, outmailbox);
return err;
}
+static struct mlx4_roce_gid_entry zgid_entry;
+
+int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave, int port)
+{
+ int vfs;
+ int slave_gid = slave;
+ unsigned i;
+ struct mlx4_slaves_pport slaves_pport;
+ struct mlx4_active_ports actv_ports;
+ unsigned max_port_p_one;
+
+ if (slave == 0)
+ return MLX4_ROCE_PF_GIDS;
+
+ /* Slave is a VF */
+ slaves_pport = mlx4_phys_to_slaves_pport(dev, port);
+ actv_ports = mlx4_get_active_ports(dev, slave);
+ max_port_p_one = find_first_bit(actv_ports.ports, dev->caps.num_ports) +
+ bitmap_weight(actv_ports.ports, dev->caps.num_ports) + 1;
+
+ for (i = 1; i < max_port_p_one; i++) {
+ struct mlx4_active_ports exclusive_ports;
+ struct mlx4_slaves_pport slaves_pport_actv;
+ bitmap_zero(exclusive_ports.ports, dev->caps.num_ports);
+ set_bit(i - 1, exclusive_ports.ports);
+ if (i == port)
+ continue;
+ slaves_pport_actv = mlx4_phys_to_slaves_pport_actv(
+ dev, &exclusive_ports);
+ slave_gid -= bitmap_weight(slaves_pport_actv.slaves,
+ dev->num_vfs + 1);
+ }
+ vfs = bitmap_weight(slaves_pport.slaves, dev->num_vfs + 1) - 1;
+ if (slave_gid <= ((MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) % vfs))
+ return ((MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) / vfs) + 1;
+ return (MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) / vfs;
+}
+
+int mlx4_get_base_gid_ix(struct mlx4_dev *dev, int slave, int port)
+{
+ int gids;
+ unsigned i;
+ int slave_gid = slave;
+ int vfs;
+
+ struct mlx4_slaves_pport slaves_pport;
+ struct mlx4_active_ports actv_ports;
+ unsigned max_port_p_one;
+
+ if (slave == 0)
+ return 0;
+
+ slaves_pport = mlx4_phys_to_slaves_pport(dev, port);
+ actv_ports = mlx4_get_active_ports(dev, slave);
+ max_port_p_one = find_first_bit(actv_ports.ports, dev->caps.num_ports) +
+ bitmap_weight(actv_ports.ports, dev->caps.num_ports) + 1;
+
+ for (i = 1; i < max_port_p_one; i++) {
+ struct mlx4_active_ports exclusive_ports;
+ struct mlx4_slaves_pport slaves_pport_actv;
+ bitmap_zero(exclusive_ports.ports, dev->caps.num_ports);
+ set_bit(i - 1, exclusive_ports.ports);
+ if (i == port)
+ continue;
+ slaves_pport_actv = mlx4_phys_to_slaves_pport_actv(
+ dev, &exclusive_ports);
+ slave_gid -= bitmap_weight(slaves_pport_actv.slaves,
+ dev->num_vfs + 1);
+ }
+ gids = MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS;
+ vfs = bitmap_weight(slaves_pport.slaves, dev->num_vfs + 1) - 1;
+ if (slave_gid <= gids % vfs)
+ return MLX4_ROCE_PF_GIDS + ((gids / vfs) + 1) * (slave_gid - 1);
+
+ return MLX4_ROCE_PF_GIDS + (gids % vfs) +
+ ((gids / vfs) * (slave_gid - 1));
+}
+EXPORT_SYMBOL_GPL(mlx4_get_base_gid_ix);
static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod,
u8 op_mod, struct mlx4_cmd_mailbox *inbox)
@@ -515,14 +593,18 @@ static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod,
struct mlx4_slave_state *slave_st = &master->slave_state[slave];
struct mlx4_set_port_rqp_calc_context *qpn_context;
struct mlx4_set_port_general_context *gen_context;
+ struct mlx4_roce_gid_entry *gid_entry_tbl, *gid_entry_mbox, *gid_entry_mb1;
int reset_qkey_viols;
int port;
int is_eth;
+ int num_gids;
+ int base;
u32 in_modifier;
u32 promisc;
u16 mtu, prev_mtu;
int err;
- int i;
+ int i, j;
+ int offset;
__be32 agg_cap_mask;
__be32 slave_cap_mask;
__be32 new_cap_mask;
@@ -535,7 +617,8 @@ static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod,
/* Slaves cannot perform SET_PORT operations except changing MTU */
if (is_eth) {
if (slave != dev->caps.function &&
- in_modifier != MLX4_SET_PORT_GENERAL) {
+ in_modifier != MLX4_SET_PORT_GENERAL &&
+ in_modifier != MLX4_SET_PORT_GID_TABLE) {
mlx4_warn(dev, "denying SET_PORT for slave:%d\n",
slave);
return -EINVAL;
@@ -581,6 +664,67 @@ static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod,
gen_context->mtu = cpu_to_be16(master->max_mtu[port]);
break;
+ case MLX4_SET_PORT_GID_TABLE:
+ /* change to MULTIPLE entries: number of guest's gids
+ * need a FOR-loop here over number of gids the guest has.
+ * 1. Check no duplicates in gids passed by slave
+ */
+ num_gids = mlx4_get_slave_num_gids(dev, slave, port);
+ base = mlx4_get_base_gid_ix(dev, slave, port);
+ gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf);
+ for (i = 0; i < num_gids; gid_entry_mbox++, i++) {
+ if (!memcmp(gid_entry_mbox->raw, zgid_entry.raw,
+ sizeof(zgid_entry)))
+ continue;
+ gid_entry_mb1 = gid_entry_mbox + 1;
+ for (j = i + 1; j < num_gids; gid_entry_mb1++, j++) {
+ if (!memcmp(gid_entry_mb1->raw,
+ zgid_entry.raw, sizeof(zgid_entry)))
+ continue;
+ if (!memcmp(gid_entry_mb1->raw, gid_entry_mbox->raw,
+ sizeof(gid_entry_mbox->raw))) {
+ /* found duplicate */
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* 2. Check that do not have duplicates in OTHER
+ * entries in the port GID table
+ */
+ for (i = 0; i < MLX4_ROCE_MAX_GIDS; i++) {
+ if (i >= base && i < base + num_gids)
+ continue; /* don't compare to slave's current gids */
+ gid_entry_tbl = &priv->roce_gids[port - 1][i];
+ if (!memcmp(gid_entry_tbl->raw, zgid_entry.raw, sizeof(zgid_entry)))
+ continue;
+ gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf);
+ for (j = 0; j < num_gids; gid_entry_mbox++, j++) {
+ if (!memcmp(gid_entry_mbox->raw, zgid_entry.raw,
+ sizeof(zgid_entry)))
+ continue;
+ if (!memcmp(gid_entry_mbox->raw, gid_entry_tbl->raw,
+ sizeof(gid_entry_tbl->raw))) {
+ /* found duplicate */
+ mlx4_warn(dev, "requested gid entry for slave:%d "
+ "is a duplicate of gid at index %d\n",
+ slave, i);
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* insert slave GIDs with memcpy, starting at slave's base index */
+ gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf);
+ for (i = 0, offset = base; i < num_gids; gid_entry_mbox++, offset++, i++)
+ memcpy(priv->roce_gids[port - 1][offset].raw, gid_entry_mbox->raw, 16);
+
+ /* Now, copy roce port gids table to current mailbox for passing to FW */
+ gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf);
+ for (i = 0; i < MLX4_ROCE_MAX_GIDS; gid_entry_mbox++, i++)
+ memcpy(gid_entry_mbox->raw, priv->roce_gids[port - 1][i].raw, 16);
+
+ break;
}
return mlx4_cmd(dev, inbox->dma, in_mod, op_mod,
MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
@@ -646,6 +790,15 @@ int mlx4_SET_PORT_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_cmd_mailbox *outbox,
struct mlx4_cmd_info *cmd)
{
+ int port = mlx4_slave_convert_port(
+ dev, slave, vhcr->in_modifier & 0xFF);
+
+ if (port < 0)
+ return -EINVAL;
+
+ vhcr->in_modifier = (vhcr->in_modifier & ~0xFF) |
+ (port & 0xFF);
+
return mlx4_common_set_port(dev, slave, vhcr->in_modifier,
vhcr->op_modifier, inbox);
}
@@ -835,7 +988,7 @@ struct mlx4_set_port_vxlan_context {
u8 steering;
};
-int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering)
+int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering, int enable)
{
int err;
u32 in_mod;
@@ -849,7 +1002,8 @@ int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering)
memset(context, 0, sizeof(*context));
context->modify_flags = VXLAN_ENABLE_MODIFY | VXLAN_STEERING_MODIFY;
- context->enable_flags = VXLAN_ENABLE;
+ if (enable)
+ context->enable_flags = VXLAN_ENABLE;
context->steering = steering;
in_mod = MLX4_SET_PORT_VXLAN << 8 | port;
@@ -927,3 +1081,108 @@ void mlx4_set_stats_bitmap(struct mlx4_dev *dev, u64 *stats_bitmap)
*stats_bitmap |= MLX4_STATS_ERROR_COUNTERS_MASK;
}
EXPORT_SYMBOL(mlx4_set_stats_bitmap);
+
+int mlx4_get_slave_from_roce_gid(struct mlx4_dev *dev, int port, u8 *gid,
+ int *slave_id)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+ int i, found_ix = -1;
+ int vf_gids = MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS;
+ struct mlx4_slaves_pport slaves_pport;
+ unsigned num_vfs;
+ int slave_gid;
+
+ if (!mlx4_is_mfunc(dev))
+ return -EINVAL;
+
+ slaves_pport = mlx4_phys_to_slaves_pport(dev, port);
+ num_vfs = bitmap_weight(slaves_pport.slaves, dev->num_vfs + 1) - 1;
+
+ for (i = 0; i < MLX4_ROCE_MAX_GIDS; i++) {
+ if (!memcmp(priv->roce_gids[port - 1][i].raw, gid, 16)) {
+ found_ix = i;
+ break;
+ }
+ }
+
+ if (found_ix >= 0) {
+ if (found_ix < MLX4_ROCE_PF_GIDS)
+ slave_gid = 0;
+ else if (found_ix < MLX4_ROCE_PF_GIDS + (vf_gids % num_vfs) *
+ (vf_gids / num_vfs + 1))
+ slave_gid = ((found_ix - MLX4_ROCE_PF_GIDS) /
+ (vf_gids / num_vfs + 1)) + 1;
+ else
+ slave_gid =
+ ((found_ix - MLX4_ROCE_PF_GIDS -
+ ((vf_gids % num_vfs) * ((vf_gids / num_vfs + 1)))) /
+ (vf_gids / num_vfs)) + vf_gids % num_vfs + 1;
+
+ if (slave_gid) {
+ struct mlx4_active_ports exclusive_ports;
+ struct mlx4_active_ports actv_ports;
+ struct mlx4_slaves_pport slaves_pport_actv;
+ unsigned max_port_p_one;
+ int num_slaves_before = 1;
+
+ for (i = 1; i < port; i++) {
+ bitmap_zero(exclusive_ports.ports, dev->caps.num_ports);
+ set_bit(i, exclusive_ports.ports);
+ slaves_pport_actv =
+ mlx4_phys_to_slaves_pport_actv(
+ dev, &exclusive_ports);
+ num_slaves_before += bitmap_weight(
+ slaves_pport_actv.slaves,
+ dev->num_vfs + 1);
+ }
+
+ if (slave_gid < num_slaves_before) {
+ bitmap_zero(exclusive_ports.ports, dev->caps.num_ports);
+ set_bit(port - 1, exclusive_ports.ports);
+ slaves_pport_actv =
+ mlx4_phys_to_slaves_pport_actv(
+ dev, &exclusive_ports);
+ slave_gid += bitmap_weight(
+ slaves_pport_actv.slaves,
+ dev->num_vfs + 1) -
+ num_slaves_before;
+ }
+ actv_ports = mlx4_get_active_ports(dev, slave_gid);
+ max_port_p_one = find_first_bit(
+ actv_ports.ports, dev->caps.num_ports) +
+ bitmap_weight(actv_ports.ports,
+ dev->caps.num_ports) + 1;
+
+ for (i = 1; i < max_port_p_one; i++) {
+ if (i == port)
+ continue;
+ bitmap_zero(exclusive_ports.ports,
+ dev->caps.num_ports);
+ set_bit(i - 1, exclusive_ports.ports);
+ slaves_pport_actv =
+ mlx4_phys_to_slaves_pport_actv(
+ dev, &exclusive_ports);
+ slave_gid += bitmap_weight(
+ slaves_pport_actv.slaves,
+ dev->num_vfs + 1);
+ }
+ }
+ *slave_id = slave_gid;
+ }
+
+ return (found_ix >= 0) ? 0 : -EINVAL;
+}
+EXPORT_SYMBOL(mlx4_get_slave_from_roce_gid);
+
+int mlx4_get_roce_gid_from_slave(struct mlx4_dev *dev, int port, int slave_id,
+ u8 *gid)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ if (!mlx4_is_master(dev))
+ return -EINVAL;
+
+ memcpy(gid, priv->roce_gids[port - 1][slave_id].raw, 16);
+ return 0;
+}
+EXPORT_SYMBOL(mlx4_get_roce_gid_from_slave);
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index 57428a0cb9dd..3b5f53ef29b2 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -52,6 +52,8 @@
struct mac_res {
struct list_head list;
u64 mac;
+ int ref_count;
+ u8 smac_index;
u8 port;
};
@@ -219,6 +221,11 @@ struct res_fs_rule {
int qpn;
};
+static int mlx4_is_eth(struct mlx4_dev *dev, int port)
+{
+ return dev->caps.port_mask[port] == MLX4_PORT_TYPE_IB ? 0 : 1;
+}
+
static void *res_tracker_lookup(struct rb_root *root, u64 res_id)
{
struct rb_node *node = root->rb_node;
@@ -461,6 +468,8 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev)
spin_lock_init(&res_alloc->alloc_lock);
for (t = 0; t < dev->num_vfs + 1; t++) {
+ struct mlx4_active_ports actv_ports =
+ mlx4_get_active_ports(dev, t);
switch (i) {
case RES_QP:
initialize_res_quotas(dev, res_alloc, RES_QP,
@@ -490,10 +499,27 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev)
break;
case RES_MAC:
if (t == mlx4_master_func_num(dev)) {
- res_alloc->quota[t] = MLX4_MAX_MAC_NUM;
+ int max_vfs_pport = 0;
+ /* Calculate the max vfs per port for */
+ /* both ports. */
+ for (j = 0; j < dev->caps.num_ports;
+ j++) {
+ struct mlx4_slaves_pport slaves_pport =
+ mlx4_phys_to_slaves_pport(dev, j + 1);
+ unsigned current_slaves =
+ bitmap_weight(slaves_pport.slaves,
+ dev->caps.num_ports) - 1;
+ if (max_vfs_pport < current_slaves)
+ max_vfs_pport =
+ current_slaves;
+ }
+ res_alloc->quota[t] =
+ MLX4_MAX_MAC_NUM -
+ 2 * max_vfs_pport;
res_alloc->guaranteed[t] = 2;
for (j = 0; j < MLX4_MAX_PORTS; j++)
- res_alloc->res_port_free[j] = MLX4_MAX_MAC_NUM;
+ res_alloc->res_port_free[j] =
+ MLX4_MAX_MAC_NUM;
} else {
res_alloc->quota[t] = MLX4_MAX_MAC_NUM;
res_alloc->guaranteed[t] = 2;
@@ -521,9 +547,10 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev)
break;
}
if (i == RES_MAC || i == RES_VLAN) {
- for (j = 0; j < MLX4_MAX_PORTS; j++)
- res_alloc->res_port_rsvd[j] +=
- res_alloc->guaranteed[t];
+ for (j = 0; j < dev->caps.num_ports; j++)
+ if (test_bit(j, actv_ports.ports))
+ res_alloc->res_port_rsvd[j] +=
+ res_alloc->guaranteed[t];
} else {
res_alloc->res_reserved += res_alloc->guaranteed[t];
}
@@ -600,15 +627,37 @@ static void update_gid(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *inbox,
struct mlx4_qp_context *qp_ctx = inbox->buf + 8;
enum mlx4_qp_optpar optpar = be32_to_cpu(*(__be32 *) inbox->buf);
u32 ts = (be32_to_cpu(qp_ctx->flags) >> 16) & 0xff;
+ int port;
- if (MLX4_QP_ST_UD == ts)
- qp_ctx->pri_path.mgid_index = 0x80 | slave;
-
- if (MLX4_QP_ST_RC == ts || MLX4_QP_ST_UC == ts) {
- if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH)
- qp_ctx->pri_path.mgid_index = slave & 0x7F;
- if (optpar & MLX4_QP_OPTPAR_ALT_ADDR_PATH)
- qp_ctx->alt_path.mgid_index = slave & 0x7F;
+ if (MLX4_QP_ST_UD == ts) {
+ port = (qp_ctx->pri_path.sched_queue >> 6 & 1) + 1;
+ if (mlx4_is_eth(dev, port))
+ qp_ctx->pri_path.mgid_index =
+ mlx4_get_base_gid_ix(dev, slave, port) | 0x80;
+ else
+ qp_ctx->pri_path.mgid_index = slave | 0x80;
+
+ } else if (MLX4_QP_ST_RC == ts || MLX4_QP_ST_XRC == ts || MLX4_QP_ST_UC == ts) {
+ if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH) {
+ port = (qp_ctx->pri_path.sched_queue >> 6 & 1) + 1;
+ if (mlx4_is_eth(dev, port)) {
+ qp_ctx->pri_path.mgid_index +=
+ mlx4_get_base_gid_ix(dev, slave, port);
+ qp_ctx->pri_path.mgid_index &= 0x7f;
+ } else {
+ qp_ctx->pri_path.mgid_index = slave & 0x7F;
+ }
+ }
+ if (optpar & MLX4_QP_OPTPAR_ALT_ADDR_PATH) {
+ port = (qp_ctx->alt_path.sched_queue >> 6 & 1) + 1;
+ if (mlx4_is_eth(dev, port)) {
+ qp_ctx->alt_path.mgid_index +=
+ mlx4_get_base_gid_ix(dev, slave, port);
+ qp_ctx->alt_path.mgid_index &= 0x7f;
+ } else {
+ qp_ctx->alt_path.mgid_index = slave & 0x7F;
+ }
+ }
}
}
@@ -619,7 +668,6 @@ static int update_vport_qp_param(struct mlx4_dev *dev,
struct mlx4_qp_context *qpc = inbox->buf + 8;
struct mlx4_vport_oper_state *vp_oper;
struct mlx4_priv *priv;
- u32 qp_type;
int port;
port = (qpc->pri_path.sched_queue & 0x40) ? 2 : 1;
@@ -627,12 +675,6 @@ static int update_vport_qp_param(struct mlx4_dev *dev,
vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port];
if (MLX4_VGT != vp_oper->state.default_vlan) {
- qp_type = (be32_to_cpu(qpc->flags) >> 16) & 0xff;
- if (MLX4_QP_ST_RC == qp_type ||
- (MLX4_QP_ST_UD == qp_type &&
- !mlx4_is_qp_reserved(dev, qpn)))
- return -EINVAL;
-
/* the reserved QPs (special, proxy, tunnel)
* do not operate over vlans
*/
@@ -1659,11 +1701,39 @@ static int srq_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
return err;
}
-static int mac_add_to_slave(struct mlx4_dev *dev, int slave, u64 mac, int port)
+static int mac_find_smac_ix_in_slave(struct mlx4_dev *dev, int slave, int port,
+ u8 smac_index, u64 *mac)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+ struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
+ struct list_head *mac_list =
+ &tracker->slave_list[slave].res_list[RES_MAC];
+ struct mac_res *res, *tmp;
+
+ list_for_each_entry_safe(res, tmp, mac_list, list) {
+ if (res->smac_index == smac_index && res->port == (u8) port) {
+ *mac = res->mac;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+static int mac_add_to_slave(struct mlx4_dev *dev, int slave, u64 mac, int port, u8 smac_index)
{
struct mlx4_priv *priv = mlx4_priv(dev);
struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
- struct mac_res *res;
+ struct list_head *mac_list =
+ &tracker->slave_list[slave].res_list[RES_MAC];
+ struct mac_res *res, *tmp;
+
+ list_for_each_entry_safe(res, tmp, mac_list, list) {
+ if (res->mac == mac && res->port == (u8) port) {
+ /* mac found. update ref count */
+ ++res->ref_count;
+ return 0;
+ }
+ }
if (mlx4_grant_resource(dev, slave, RES_MAC, 1, port))
return -EINVAL;
@@ -1674,6 +1744,8 @@ static int mac_add_to_slave(struct mlx4_dev *dev, int slave, u64 mac, int port)
}
res->mac = mac;
res->port = (u8) port;
+ res->smac_index = smac_index;
+ res->ref_count = 1;
list_add_tail(&res->list,
&tracker->slave_list[slave].res_list[RES_MAC]);
return 0;
@@ -1690,9 +1762,11 @@ static void mac_del_from_slave(struct mlx4_dev *dev, int slave, u64 mac,
list_for_each_entry_safe(res, tmp, mac_list, list) {
if (res->mac == mac && res->port == (u8) port) {
- list_del(&res->list);
- mlx4_release_resource(dev, slave, RES_MAC, 1, port);
- kfree(res);
+ if (!--res->ref_count) {
+ list_del(&res->list);
+ mlx4_release_resource(dev, slave, RES_MAC, 1, port);
+ kfree(res);
+ }
break;
}
}
@@ -1705,10 +1779,13 @@ static void rem_slave_macs(struct mlx4_dev *dev, int slave)
struct list_head *mac_list =
&tracker->slave_list[slave].res_list[RES_MAC];
struct mac_res *res, *tmp;
+ int i;
list_for_each_entry_safe(res, tmp, mac_list, list) {
list_del(&res->list);
- __mlx4_unregister_mac(dev, res->port, res->mac);
+ /* dereference the mac the num times the slave referenced it */
+ for (i = 0; i < res->ref_count; i++)
+ __mlx4_unregister_mac(dev, res->port, res->mac);
mlx4_release_resource(dev, slave, RES_MAC, 1, res->port);
kfree(res);
}
@@ -1720,21 +1797,28 @@ static int mac_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
int err = -EINVAL;
int port;
u64 mac;
+ u8 smac_index;
if (op != RES_OP_RESERVE_AND_MAP)
return err;
port = !in_port ? get_param_l(out_param) : in_port;
+ port = mlx4_slave_convert_port(
+ dev, slave, port);
+
+ if (port < 0)
+ return -EINVAL;
mac = in_param;
err = __mlx4_register_mac(dev, port, mac);
if (err >= 0) {
+ smac_index = err;
set_param_l(out_param, err);
err = 0;
}
if (!err) {
- err = mac_add_to_slave(dev, slave, mac, port);
+ err = mac_add_to_slave(dev, slave, mac, port, smac_index);
if (err)
__mlx4_unregister_mac(dev, port, mac);
}
@@ -1831,6 +1915,11 @@ static int vlan_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
if (!port || op != RES_OP_RESERVE_AND_MAP)
return -EINVAL;
+ port = mlx4_slave_convert_port(
+ dev, slave, port);
+
+ if (port < 0)
+ return -EINVAL;
/* upstream kernels had NOP for reg/unreg vlan. Continue this. */
if (!in_port && port > 0 && port <= dev->caps.num_ports) {
slave_state[slave].old_vlan_api = true;
@@ -2128,6 +2217,11 @@ static int mac_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
switch (op) {
case RES_OP_RESERVE_AND_MAP:
port = !in_port ? get_param_l(out_param) : in_port;
+ port = mlx4_slave_convert_port(
+ dev, slave, port);
+
+ if (port < 0)
+ return -EINVAL;
mac_del_from_slave(dev, slave, in_param, port);
__mlx4_unregister_mac(dev, port, in_param);
break;
@@ -2147,6 +2241,11 @@ static int vlan_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
struct mlx4_slave_state *slave_state = priv->mfunc.master.slave_state;
int err = 0;
+ port = mlx4_slave_convert_port(
+ dev, slave, port);
+
+ if (port < 0)
+ return -EINVAL;
switch (op) {
case RES_OP_RESERVE_AND_MAP:
if (slave_state[slave].old_vlan_api)
@@ -2734,6 +2833,8 @@ static int verify_qp_parameters(struct mlx4_dev *dev,
u32 qp_type;
struct mlx4_qp_context *qp_ctx;
enum mlx4_qp_optpar optpar;
+ int port;
+ int num_gids;
qp_ctx = inbox->buf + 8;
qp_type = (be32_to_cpu(qp_ctx->flags) >> 16) & 0xff;
@@ -2741,6 +2842,7 @@ static int verify_qp_parameters(struct mlx4_dev *dev,
switch (qp_type) {
case MLX4_QP_ST_RC:
+ case MLX4_QP_ST_XRC:
case MLX4_QP_ST_UC:
switch (transition) {
case QP_TRANS_INIT2RTR:
@@ -2749,13 +2851,24 @@ static int verify_qp_parameters(struct mlx4_dev *dev,
case QP_TRANS_SQD2SQD:
case QP_TRANS_SQD2RTS:
if (slave != mlx4_master_func_num(dev))
- /* slaves have only gid index 0 */
- if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH)
- if (qp_ctx->pri_path.mgid_index)
+ if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH) {
+ port = (qp_ctx->pri_path.sched_queue >> 6 & 1) + 1;
+ if (dev->caps.port_mask[port] != MLX4_PORT_TYPE_IB)
+ num_gids = mlx4_get_slave_num_gids(dev, slave, port);
+ else
+ num_gids = 1;
+ if (qp_ctx->pri_path.mgid_index >= num_gids)
return -EINVAL;
- if (optpar & MLX4_QP_OPTPAR_ALT_ADDR_PATH)
- if (qp_ctx->alt_path.mgid_index)
+ }
+ if (optpar & MLX4_QP_OPTPAR_ALT_ADDR_PATH) {
+ port = (qp_ctx->alt_path.sched_queue >> 6 & 1) + 1;
+ if (dev->caps.port_mask[port] != MLX4_PORT_TYPE_IB)
+ num_gids = mlx4_get_slave_num_gids(dev, slave, port);
+ else
+ num_gids = 1;
+ if (qp_ctx->alt_path.mgid_index >= num_gids)
return -EINVAL;
+ }
break;
default:
break;
@@ -3268,6 +3381,58 @@ int mlx4_INIT2INIT_QP_wrapper(struct mlx4_dev *dev, int slave,
return mlx4_GEN_QP_wrapper(dev, slave, vhcr, inbox, outbox, cmd);
}
+static int adjust_qp_sched_queue(struct mlx4_dev *dev, int slave,
+ struct mlx4_qp_context *qpc,
+ struct mlx4_cmd_mailbox *inbox)
+{
+ enum mlx4_qp_optpar optpar = be32_to_cpu(*(__be32 *)inbox->buf);
+ u8 pri_sched_queue;
+ int port = mlx4_slave_convert_port(
+ dev, slave, (qpc->pri_path.sched_queue >> 6 & 1) + 1) - 1;
+
+ if (port < 0)
+ return -EINVAL;
+
+ pri_sched_queue = (qpc->pri_path.sched_queue & ~(1 << 6)) |
+ ((port & 1) << 6);
+
+ if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH ||
+ mlx4_is_eth(dev, port + 1)) {
+ qpc->pri_path.sched_queue = pri_sched_queue;
+ }
+
+ if (optpar & MLX4_QP_OPTPAR_ALT_ADDR_PATH) {
+ port = mlx4_slave_convert_port(
+ dev, slave, (qpc->alt_path.sched_queue >> 6 & 1)
+ + 1) - 1;
+ if (port < 0)
+ return -EINVAL;
+ qpc->alt_path.sched_queue =
+ (qpc->alt_path.sched_queue & ~(1 << 6)) |
+ (port & 1) << 6;
+ }
+ return 0;
+}
+
+static int roce_verify_mac(struct mlx4_dev *dev, int slave,
+ struct mlx4_qp_context *qpc,
+ struct mlx4_cmd_mailbox *inbox)
+{
+ u64 mac;
+ int port;
+ u32 ts = (be32_to_cpu(qpc->flags) >> 16) & 0xff;
+ u8 sched = *(u8 *)(inbox->buf + 64);
+ u8 smac_ix;
+
+ port = (sched >> 6 & 1) + 1;
+ if (mlx4_is_eth(dev, port) && (ts != MLX4_QP_ST_MLX)) {
+ smac_ix = qpc->pri_path.grh_mylmc & 0x7f;
+ if (mac_find_smac_ix_in_slave(dev, slave, port, smac_ix, &mac))
+ return -ENOENT;
+ }
+ return 0;
+}
+
int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_vhcr *vhcr,
struct mlx4_cmd_mailbox *inbox,
@@ -3286,10 +3451,16 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave,
u8 orig_vlan_index = qpc->pri_path.vlan_index;
u8 orig_feup = qpc->pri_path.feup;
+ err = adjust_qp_sched_queue(dev, slave, qpc, inbox);
+ if (err)
+ return err;
err = verify_qp_parameters(dev, inbox, QP_TRANS_INIT2RTR, slave);
if (err)
return err;
+ if (roce_verify_mac(dev, slave, qpc, inbox))
+ return -EINVAL;
+
update_pkey_index(dev, slave, inbox);
update_gid(dev, inbox, (u8)slave);
adjust_proxy_tun_qkey(dev, vhcr, qpc);
@@ -3334,6 +3505,9 @@ int mlx4_RTR2RTS_QP_wrapper(struct mlx4_dev *dev, int slave,
int err;
struct mlx4_qp_context *context = inbox->buf + 8;
+ err = adjust_qp_sched_queue(dev, slave, context, inbox);
+ if (err)
+ return err;
err = verify_qp_parameters(dev, inbox, QP_TRANS_RTR2RTS, slave);
if (err)
return err;
@@ -3353,6 +3527,9 @@ int mlx4_RTS2RTS_QP_wrapper(struct mlx4_dev *dev, int slave,
int err;
struct mlx4_qp_context *context = inbox->buf + 8;
+ err = adjust_qp_sched_queue(dev, slave, context, inbox);
+ if (err)
+ return err;
err = verify_qp_parameters(dev, inbox, QP_TRANS_RTS2RTS, slave);
if (err)
return err;
@@ -3371,6 +3548,9 @@ int mlx4_SQERR2RTS_QP_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_cmd_info *cmd)
{
struct mlx4_qp_context *context = inbox->buf + 8;
+ int err = adjust_qp_sched_queue(dev, slave, context, inbox);
+ if (err)
+ return err;
adjust_proxy_tun_qkey(dev, vhcr, context);
return mlx4_GEN_QP_wrapper(dev, slave, vhcr, inbox, outbox, cmd);
}
@@ -3384,6 +3564,9 @@ int mlx4_SQD2SQD_QP_wrapper(struct mlx4_dev *dev, int slave,
int err;
struct mlx4_qp_context *context = inbox->buf + 8;
+ err = adjust_qp_sched_queue(dev, slave, context, inbox);
+ if (err)
+ return err;
err = verify_qp_parameters(dev, inbox, QP_TRANS_SQD2SQD, slave);
if (err)
return err;
@@ -3403,6 +3586,9 @@ int mlx4_SQD2RTS_QP_wrapper(struct mlx4_dev *dev, int slave,
int err;
struct mlx4_qp_context *context = inbox->buf + 8;
+ err = adjust_qp_sched_queue(dev, slave, context, inbox);
+ if (err)
+ return err;
err = verify_qp_parameters(dev, inbox, QP_TRANS_SQD2RTS, slave);
if (err)
return err;
@@ -3506,16 +3692,26 @@ static int rem_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp,
return err;
}
-static int qp_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
- int block_loopback, enum mlx4_protocol prot,
+static int qp_attach(struct mlx4_dev *dev, int slave, struct mlx4_qp *qp,
+ u8 gid[16], int block_loopback, enum mlx4_protocol prot,
enum mlx4_steer_type type, u64 *reg_id)
{
switch (dev->caps.steering_mode) {
- case MLX4_STEERING_MODE_DEVICE_MANAGED:
- return mlx4_trans_to_dmfs_attach(dev, qp, gid, gid[5],
+ case MLX4_STEERING_MODE_DEVICE_MANAGED: {
+ int port = mlx4_slave_convert_port(dev, slave, gid[5]);
+ if (port < 0)
+ return port;
+ return mlx4_trans_to_dmfs_attach(dev, qp, gid, port,
block_loopback, prot,
reg_id);
+ }
case MLX4_STEERING_MODE_B0:
+ if (prot == MLX4_PROT_ETH) {
+ int port = mlx4_slave_convert_port(dev, slave, gid[5]);
+ if (port < 0)
+ return port;
+ gid[5] = port;
+ }
return mlx4_qp_attach_common(dev, qp, gid,
block_loopback, prot, type);
default:
@@ -3523,9 +3719,9 @@ static int qp_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
}
}
-static int qp_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
- enum mlx4_protocol prot, enum mlx4_steer_type type,
- u64 reg_id)
+static int qp_detach(struct mlx4_dev *dev, struct mlx4_qp *qp,
+ u8 gid[16], enum mlx4_protocol prot,
+ enum mlx4_steer_type type, u64 reg_id)
{
switch (dev->caps.steering_mode) {
case MLX4_STEERING_MODE_DEVICE_MANAGED:
@@ -3562,7 +3758,7 @@ int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
qp.qpn = qpn;
if (attach) {
- err = qp_attach(dev, &qp, gid, block_loopback, prot,
+ err = qp_attach(dev, slave, &qp, gid, block_loopback, prot,
type, &reg_id);
if (err) {
pr_err("Fail to attach rule to qp 0x%x\n", qpn);
@@ -3698,6 +3894,9 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
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)
+ return -EINVAL;
qpn = be32_to_cpu(ctrl->qpn) & 0xffffff;
err = get_res(dev, slave, qpn, RES_QP, &rqp);
if (err) {
@@ -3816,16 +4015,6 @@ int mlx4_QUERY_IF_STAT_wrapper(struct mlx4_dev *dev, int slave,
return err;
}
-int mlx4_FLOW_STEERING_IB_UC_QP_RANGE_wrapper(struct mlx4_dev *dev, int slave,
- struct mlx4_vhcr *vhcr,
- struct mlx4_cmd_mailbox *inbox,
- struct mlx4_cmd_mailbox *outbox,
- struct mlx4_cmd_info *cmd)
-{
- return -EPERM;
-}
-
-
static void detach_qp(struct mlx4_dev *dev, int slave, struct res_qp *rqp)
{
struct res_gid *rgid;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 23b7e2d35a93..c3eee5f70051 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -116,7 +116,6 @@ static int mlx5_enable_msix(struct mlx5_core_dev *dev)
struct mlx5_eq_table *table = &dev->priv.eq_table;
int num_eqs = 1 << dev->caps.log_max_eq;
int nvec;
- int err;
int i;
nvec = dev->caps.num_ports * num_online_cpus() + MLX5_EQ_VEC_COMP_BASE;
@@ -131,17 +130,12 @@ static int mlx5_enable_msix(struct mlx5_core_dev *dev)
for (i = 0; i < nvec; i++)
table->msix_arr[i].entry = i;
-retry:
- table->num_comp_vectors = nvec - MLX5_EQ_VEC_COMP_BASE;
- err = pci_enable_msix(dev->pdev, table->msix_arr, nvec);
- if (err <= 0) {
- return err;
- } else if (err > 2) {
- nvec = err;
- goto retry;
- }
+ nvec = pci_enable_msix_range(dev->pdev, table->msix_arr,
+ MLX5_EQ_VEC_COMP_BASE, nvec);
+ if (nvec < 0)
+ return nvec;
- mlx5_core_dbg(dev, "received %d MSI vectors out of %d requested\n", err, nvec);
+ table->num_comp_vectors = nvec - MLX5_EQ_VEC_COMP_BASE;
return 0;
}
@@ -446,6 +440,7 @@ int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev)
mlx5_init_cq_table(dev);
mlx5_init_qp_table(dev);
mlx5_init_srq_table(dev);
+ mlx5_init_mr_table(dev);
return 0;
@@ -537,7 +532,6 @@ static int __init init(void)
return 0;
- mlx5_health_cleanup();
err_debug:
mlx5_unregister_debugfs();
return err;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mr.c b/drivers/net/ethernet/mellanox/mlx5/core/mr.c
index 35e514dc7b7d..4cc927649404 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mr.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mr.c
@@ -36,11 +36,24 @@
#include <linux/mlx5/cmd.h>
#include "mlx5_core.h"
+void mlx5_init_mr_table(struct mlx5_core_dev *dev)
+{
+ struct mlx5_mr_table *table = &dev->priv.mr_table;
+
+ rwlock_init(&table->lock);
+ INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
+}
+
+void mlx5_cleanup_mr_table(struct mlx5_core_dev *dev)
+{
+}
+
int mlx5_core_create_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
struct mlx5_create_mkey_mbox_in *in, int inlen,
mlx5_cmd_cbk_t callback, void *context,
struct mlx5_create_mkey_mbox_out *out)
{
+ struct mlx5_mr_table *table = &dev->priv.mr_table;
struct mlx5_create_mkey_mbox_out lout;
int err;
u8 key;
@@ -73,14 +86,21 @@ int mlx5_core_create_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
mlx5_core_dbg(dev, "out 0x%x, key 0x%x, mkey 0x%x\n",
be32_to_cpu(lout.mkey), key, mr->key);
+ /* connect to MR tree */
+ write_lock_irq(&table->lock);
+ err = radix_tree_insert(&table->tree, mlx5_base_mkey(mr->key), mr);
+ write_unlock_irq(&table->lock);
+
return err;
}
EXPORT_SYMBOL(mlx5_core_create_mkey);
int mlx5_core_destroy_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr)
{
+ struct mlx5_mr_table *table = &dev->priv.mr_table;
struct mlx5_destroy_mkey_mbox_in in;
struct mlx5_destroy_mkey_mbox_out out;
+ unsigned long flags;
int err;
memset(&in, 0, sizeof(in));
@@ -95,6 +115,10 @@ int mlx5_core_destroy_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr)
if (out.hdr.status)
return mlx5_cmd_status_to_err(&out.hdr);
+ write_lock_irqsave(&table->lock, flags);
+ radix_tree_delete(&table->tree, mlx5_base_mkey(mr->key));
+ write_unlock_irqrestore(&table->lock, flags);
+
return err;
}
EXPORT_SYMBOL(mlx5_core_destroy_mkey);
@@ -144,3 +168,64 @@ int mlx5_core_dump_fill_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
return err;
}
EXPORT_SYMBOL(mlx5_core_dump_fill_mkey);
+
+int mlx5_core_create_psv(struct mlx5_core_dev *dev, u32 pdn,
+ int npsvs, u32 *sig_index)
+{
+ struct mlx5_allocate_psv_in in;
+ struct mlx5_allocate_psv_out out;
+ int i, err;
+
+ if (npsvs > MLX5_MAX_PSVS)
+ return -EINVAL;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_PSV);
+ in.npsv_pd = cpu_to_be32((npsvs << 28) | pdn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err) {
+ mlx5_core_err(dev, "cmd exec failed %d\n", err);
+ return err;
+ }
+
+ if (out.hdr.status) {
+ mlx5_core_err(dev, "create_psv bad status %d\n", out.hdr.status);
+ return mlx5_cmd_status_to_err(&out.hdr);
+ }
+
+ for (i = 0; i < npsvs; i++)
+ sig_index[i] = be32_to_cpu(out.psv_idx[i]) & 0xffffff;
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_create_psv);
+
+int mlx5_core_destroy_psv(struct mlx5_core_dev *dev, int psv_num)
+{
+ struct mlx5_destroy_psv_in in;
+ struct mlx5_destroy_psv_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+
+ in.psv_number = cpu_to_be32(psv_num);
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_PSV);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err) {
+ mlx5_core_err(dev, "destroy_psv cmd exec failed %d\n", err);
+ goto out;
+ }
+
+ if (out.hdr.status) {
+ mlx5_core_err(dev, "destroy_psv bad status %d\n", out.hdr.status);
+ err = mlx5_cmd_status_to_err(&out.hdr);
+ goto out;
+ }
+
+out:
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_destroy_psv);
diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c
index ce84dc289c8f..14ac0e2bc09f 100644
--- a/drivers/net/ethernet/micrel/ksz884x.c
+++ b/drivers/net/ethernet/micrel/ksz884x.c
@@ -4832,7 +4832,7 @@ static inline void copy_old_skb(struct sk_buff *old, struct sk_buff *skb)
skb->csum = old->csum;
skb_set_network_header(skb, ETH_HLEN);
- dev_kfree_skb(old);
+ dev_consume_skb_any(old);
}
/**
diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
index 68026f7e8ba3..130f6b204efa 100644
--- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
@@ -2329,16 +2329,14 @@ static int myri10ge_request_irq(struct myri10ge_priv *mgp)
status = 0;
if (myri10ge_msi) {
if (mgp->num_slices > 1) {
- status =
- pci_enable_msix(pdev, mgp->msix_vectors,
- mgp->num_slices);
- if (status == 0) {
- mgp->msix_enabled = 1;
- } else {
+ status = pci_enable_msix_range(pdev, mgp->msix_vectors,
+ mgp->num_slices, mgp->num_slices);
+ if (status < 0) {
dev_err(&pdev->dev,
"Error %d setting up MSI-X\n", status);
return status;
}
+ mgp->msix_enabled = 1;
}
if (mgp->msix_enabled == 0) {
status = pci_enable_msi(pdev);
@@ -3895,32 +3893,34 @@ static void myri10ge_probe_slices(struct myri10ge_priv *mgp)
mgp->msix_vectors = kcalloc(mgp->num_slices, sizeof(*mgp->msix_vectors),
GFP_KERNEL);
if (mgp->msix_vectors == NULL)
- goto disable_msix;
+ goto no_msix;
for (i = 0; i < mgp->num_slices; i++) {
mgp->msix_vectors[i].entry = i;
}
while (mgp->num_slices > 1) {
- /* make sure it is a power of two */
- while (!is_power_of_2(mgp->num_slices))
- mgp->num_slices--;
+ mgp->num_slices = rounddown_pow_of_two(mgp->num_slices);
if (mgp->num_slices == 1)
- goto disable_msix;
- status = pci_enable_msix(pdev, mgp->msix_vectors,
- mgp->num_slices);
- if (status == 0) {
- pci_disable_msix(pdev);
+ goto no_msix;
+ status = pci_enable_msix_range(pdev,
+ mgp->msix_vectors,
+ mgp->num_slices,
+ mgp->num_slices);
+ if (status < 0)
+ goto no_msix;
+
+ pci_disable_msix(pdev);
+
+ if (status == mgp->num_slices) {
if (old_allocated)
kfree(old_fw);
return;
- }
- if (status > 0)
+ } else {
mgp->num_slices = status;
- else
- goto disable_msix;
+ }
}
-disable_msix:
+no_msix:
if (mgp->msix_vectors != NULL) {
kfree(mgp->msix_vectors);
mgp->msix_vectors = NULL;
diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c
index 9eeddbd0b2c7..a2844ff322c4 100644
--- a/drivers/net/ethernet/neterion/s2io.c
+++ b/drivers/net/ethernet/neterion/s2io.c
@@ -2914,6 +2914,9 @@ static int rx_intr_handler(struct ring_info *ring_data, int budget)
struct RxD1 *rxdp1;
struct RxD3 *rxdp3;
+ if (budget <= 0)
+ return napi_pkts;
+
get_info = ring_data->rx_curr_get_info;
get_block = get_info.block_index;
memcpy(&put_info, &ring_data->rx_curr_put_info, sizeof(put_info));
@@ -3792,9 +3795,10 @@ static int s2io_enable_msi_x(struct s2io_nic *nic)
writeq(rx_mat, &bar0->rx_mat);
readq(&bar0->rx_mat);
- ret = pci_enable_msix(nic->pdev, nic->entries, nic->num_entries);
+ ret = pci_enable_msix_range(nic->pdev, nic->entries,
+ nic->num_entries, nic->num_entries);
/* We fail init if error or we get less vectors than min required */
- if (ret) {
+ if (ret < 0) {
DBG_PRINT(ERR_DBG, "Enabling MSI-X failed\n");
kfree(nic->entries);
swstats->mem_freed += nic->num_entries *
@@ -4045,7 +4049,7 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev)
if (!is_s2io_card_up(sp)) {
DBG_PRINT(TX_DBG, "%s: Card going down for reset\n",
dev->name);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -4118,7 +4122,7 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev)
((put_off+1) == queue_len ? 0 : (put_off+1)) == get_off) {
DBG_PRINT(TX_DBG, "Error in xmit, No free TXDs.\n");
s2io_stop_tx_queue(sp, fifo->fifo_no);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
spin_unlock_irqrestore(&fifo->tx_lock, flags);
return NETDEV_TX_OK;
}
@@ -4240,7 +4244,7 @@ pci_map_failed:
swstats->pci_map_fail_cnt++;
s2io_stop_tx_queue(sp, fifo->fifo_no);
swstats->mem_freed += skb->truesize;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
spin_unlock_irqrestore(&fifo->tx_lock, flags);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c
index e46e8698e630..d107bcbb8543 100644
--- a/drivers/net/ethernet/neterion/vxge/vxge-main.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c
@@ -368,6 +368,9 @@ vxge_rx_1b_compl(struct __vxge_hw_ring *ringh, void *dtr,
vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d",
ring->ndev->name, __func__, __LINE__);
+ if (ring->budget <= 0)
+ goto out;
+
do {
prefetch((char *)dtr + L1_CACHE_BYTES);
rx_priv = vxge_hw_ring_rxd_private_get(dtr);
@@ -525,6 +528,7 @@ vxge_rx_1b_compl(struct __vxge_hw_ring *ringh, void *dtr,
if (first_dtr)
vxge_hw_ring_rxd_post_post_wmb(ringh, first_dtr);
+out:
vxge_debug_entryexit(VXGE_TRACE,
"%s:%d Exiting...",
__func__, __LINE__);
@@ -820,7 +824,7 @@ vxge_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(skb->len <= 0)) {
vxge_debug_tx(VXGE_ERR,
"%s: Buffer has no data..", dev->name);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -829,7 +833,7 @@ vxge_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(!is_vxge_card_up(vdev))) {
vxge_debug_tx(VXGE_ERR,
"%s: vdev not initialized", dev->name);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -839,7 +843,7 @@ vxge_xmit(struct sk_buff *skb, struct net_device *dev)
vxge_debug_tx(VXGE_ERR,
"%s: Failed to store the mac address",
dev->name);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
}
@@ -986,7 +990,7 @@ _exit1:
vxge_hw_fifo_txdl_free(fifo_hw, dtr);
_exit0:
netif_tx_stop_queue(fifo->txq);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -2349,12 +2353,18 @@ start:
vdev->vxge_entries[j].entry = VXGE_ALARM_MSIX_ID;
vdev->vxge_entries[j].in_use = 0;
- ret = pci_enable_msix(vdev->pdev, vdev->entries, vdev->intr_cnt);
- if (ret > 0) {
+ ret = pci_enable_msix_range(vdev->pdev,
+ vdev->entries, 3, vdev->intr_cnt);
+ if (ret < 0) {
+ ret = -ENODEV;
+ goto enable_msix_failed;
+ } else if (ret < vdev->intr_cnt) {
+ pci_disable_msix(vdev->pdev);
+
vxge_debug_init(VXGE_ERR,
"%s: MSI-X enable failed for %d vectors, ret: %d",
VXGE_DRIVER_NAME, vdev->intr_cnt, ret);
- if ((max_config_vpath != VXGE_USE_DEFAULT) || (ret < 3)) {
+ if (max_config_vpath != VXGE_USE_DEFAULT) {
ret = -ENODEV;
goto enable_msix_failed;
}
@@ -2368,9 +2378,6 @@ start:
vxge_close_vpaths(vdev, temp);
vdev->no_of_vpath = temp;
goto start;
- } else if (ret < 0) {
- ret = -ENODEV;
- goto enable_msix_failed;
}
return 0;
@@ -3131,12 +3138,12 @@ vxge_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *net_stats)
u64 packets, bytes, multicast;
do {
- start = u64_stats_fetch_begin_bh(&rxstats->syncp);
+ start = u64_stats_fetch_begin_irq(&rxstats->syncp);
packets = rxstats->rx_frms;
multicast = rxstats->rx_mcast;
bytes = rxstats->rx_bytes;
- } while (u64_stats_fetch_retry_bh(&rxstats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&rxstats->syncp, start));
net_stats->rx_packets += packets;
net_stats->rx_bytes += bytes;
@@ -3146,11 +3153,11 @@ vxge_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *net_stats)
net_stats->rx_dropped += rxstats->rx_dropped;
do {
- start = u64_stats_fetch_begin_bh(&txstats->syncp);
+ start = u64_stats_fetch_begin_irq(&txstats->syncp);
packets = txstats->tx_frms;
bytes = txstats->tx_bytes;
- } while (u64_stats_fetch_retry_bh(&txstats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&txstats->syncp, start));
net_stats->tx_packets += packets;
net_stats->tx_bytes += bytes;
diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
index 70cf97fe67f2..fddb464aeab3 100644
--- a/drivers/net/ethernet/nvidia/forcedeth.c
+++ b/drivers/net/ethernet/nvidia/forcedeth.c
@@ -1753,19 +1753,19 @@ nv_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *storage)
/* software stats */
do {
- syncp_start = u64_stats_fetch_begin_bh(&np->swstats_rx_syncp);
+ syncp_start = u64_stats_fetch_begin_irq(&np->swstats_rx_syncp);
storage->rx_packets = np->stat_rx_packets;
storage->rx_bytes = np->stat_rx_bytes;
storage->rx_dropped = np->stat_rx_dropped;
storage->rx_missed_errors = np->stat_rx_missed_errors;
- } while (u64_stats_fetch_retry_bh(&np->swstats_rx_syncp, syncp_start));
+ } while (u64_stats_fetch_retry_irq(&np->swstats_rx_syncp, syncp_start));
do {
- syncp_start = u64_stats_fetch_begin_bh(&np->swstats_tx_syncp);
+ syncp_start = u64_stats_fetch_begin_irq(&np->swstats_tx_syncp);
storage->tx_packets = np->stat_tx_packets;
storage->tx_bytes = np->stat_tx_bytes;
storage->tx_dropped = np->stat_tx_dropped;
- } while (u64_stats_fetch_retry_bh(&np->swstats_tx_syncp, syncp_start));
+ } while (u64_stats_fetch_retry_irq(&np->swstats_tx_syncp, syncp_start));
/* If the nic supports hw counters then retrieve latest values */
if (np->driver_data & DEV_HAS_STATISTICS_V123) {
@@ -2231,7 +2231,7 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (pci_dma_mapping_error(np->pci_dev,
np->put_tx_ctx->dma)) {
/* on DMA mapping error - drop the packet */
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
u64_stats_update_begin(&np->swstats_tx_syncp);
np->stat_tx_dropped++;
u64_stats_update_end(&np->swstats_tx_syncp);
@@ -2277,7 +2277,7 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(tmp_tx_ctx++ == np->last_tx_ctx))
tmp_tx_ctx = np->first_tx_ctx;
} while (tmp_tx_ctx != np->put_tx_ctx);
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
np->put_tx_ctx = start_tx_ctx;
u64_stats_update_begin(&np->swstats_tx_syncp);
np->stat_tx_dropped++;
@@ -2380,7 +2380,7 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,
if (pci_dma_mapping_error(np->pci_dev,
np->put_tx_ctx->dma)) {
/* on DMA mapping error - drop the packet */
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
u64_stats_update_begin(&np->swstats_tx_syncp);
np->stat_tx_dropped++;
u64_stats_update_end(&np->swstats_tx_syncp);
@@ -2427,7 +2427,7 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,
if (unlikely(tmp_tx_ctx++ == np->last_tx_ctx))
tmp_tx_ctx = np->first_tx_ctx;
} while (tmp_tx_ctx != np->put_tx_ctx);
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
np->put_tx_ctx = start_tx_ctx;
u64_stats_update_begin(&np->swstats_tx_syncp);
np->stat_tx_dropped++;
@@ -3930,7 +3930,7 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
{
struct fe_priv *np = get_nvpriv(dev);
u8 __iomem *base = get_hwbase(dev);
- int ret = 1;
+ int ret;
int i;
irqreturn_t (*handler)(int foo, void *data);
@@ -3946,14 +3946,18 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
if (np->msi_flags & NV_MSI_X_CAPABLE) {
for (i = 0; i < (np->msi_flags & NV_MSI_X_VECTORS_MASK); i++)
np->msi_x_entry[i].entry = i;
- ret = pci_enable_msix(np->pci_dev, np->msi_x_entry, (np->msi_flags & NV_MSI_X_VECTORS_MASK));
- if (ret == 0) {
+ ret = pci_enable_msix_range(np->pci_dev,
+ np->msi_x_entry,
+ np->msi_flags & NV_MSI_X_VECTORS_MASK,
+ np->msi_flags & NV_MSI_X_VECTORS_MASK);
+ if (ret > 0) {
np->msi_flags |= NV_MSI_X_ENABLED;
if (optimization_mode == NV_OPTIMIZATION_MODE_THROUGHPUT && !intr_test) {
/* Request irq for rx handling */
sprintf(np->name_rx, "%s-rx", dev->name);
- if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector,
- nv_nic_irq_rx, IRQF_SHARED, np->name_rx, dev) != 0) {
+ ret = request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector,
+ nv_nic_irq_rx, IRQF_SHARED, np->name_rx, dev);
+ if (ret) {
netdev_info(dev,
"request_irq failed for rx %d\n",
ret);
@@ -3963,8 +3967,9 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
}
/* Request irq for tx handling */
sprintf(np->name_tx, "%s-tx", dev->name);
- if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector,
- nv_nic_irq_tx, IRQF_SHARED, np->name_tx, dev) != 0) {
+ ret = request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector,
+ nv_nic_irq_tx, IRQF_SHARED, np->name_tx, dev);
+ if (ret) {
netdev_info(dev,
"request_irq failed for tx %d\n",
ret);
@@ -3974,8 +3979,9 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
}
/* Request irq for link and timer handling */
sprintf(np->name_other, "%s-other", dev->name);
- if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector,
- nv_nic_irq_other, IRQF_SHARED, np->name_other, dev) != 0) {
+ ret = request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector,
+ nv_nic_irq_other, IRQF_SHARED, np->name_other, dev);
+ if (ret) {
netdev_info(dev,
"request_irq failed for link %d\n",
ret);
@@ -3991,7 +3997,9 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
set_msix_vector_map(dev, NV_MSI_X_VECTOR_OTHER, NVREG_IRQ_OTHER);
} else {
/* Request irq for all interrupts */
- if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector, handler, IRQF_SHARED, dev->name, dev) != 0) {
+ ret = request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector,
+ handler, IRQF_SHARED, dev->name, dev);
+ if (ret) {
netdev_info(dev,
"request_irq failed %d\n",
ret);
@@ -4005,13 +4013,15 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
writel(0, base + NvRegMSIXMap1);
}
netdev_info(dev, "MSI-X enabled\n");
+ return 0;
}
}
- if (ret != 0 && np->msi_flags & NV_MSI_CAPABLE) {
+ if (np->msi_flags & NV_MSI_CAPABLE) {
ret = pci_enable_msi(np->pci_dev);
if (ret == 0) {
np->msi_flags |= NV_MSI_ENABLED;
- if (request_irq(np->pci_dev->irq, handler, IRQF_SHARED, dev->name, dev) != 0) {
+ ret = request_irq(np->pci_dev->irq, handler, IRQF_SHARED, dev->name, dev);
+ if (ret) {
netdev_info(dev, "request_irq failed %d\n",
ret);
pci_disable_msi(np->pci_dev);
@@ -4025,13 +4035,12 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
/* enable msi vector 0 */
writel(NVREG_MSI_VECTOR_0_ENABLED, base + NvRegMSIIrqMask);
netdev_info(dev, "MSI enabled\n");
+ return 0;
}
}
- if (ret != 0) {
- if (request_irq(np->pci_dev->irq, handler, IRQF_SHARED, dev->name, dev) != 0)
- goto out_err;
- }
+ if (request_irq(np->pci_dev->irq, handler, IRQF_SHARED, dev->name, dev) != 0)
+ goto out_err;
return 0;
out_free_tx:
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
index 464e91058c81..73e66838cfef 100644
--- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
@@ -120,10 +120,6 @@ static void pch_gbe_mdio_write(struct net_device *netdev, int addr, int reg,
int data);
static void pch_gbe_set_multi(struct net_device *netdev);
-static struct sock_filter ptp_filter[] = {
- PTP_FILTER
-};
-
static int pch_ptp_match(struct sk_buff *skb, u16 uid_hi, u32 uid_lo, u16 seqid)
{
u8 *data = skb->data;
@@ -131,7 +127,7 @@ static int pch_ptp_match(struct sk_buff *skb, u16 uid_hi, u32 uid_lo, u16 seqid)
u16 *hi, *id;
u32 lo;
- if (sk_run_filter(skb, ptp_filter) == PTP_CLASS_NONE)
+ if (ptp_classify_raw(skb) == PTP_CLASS_NONE)
return 0;
offset = ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN;
@@ -2635,11 +2631,6 @@ static int pch_gbe_probe(struct pci_dev *pdev,
adapter->ptp_pdev = pci_get_bus_and_slot(adapter->pdev->bus->number,
PCI_DEVFN(12, 4));
- if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) {
- dev_err(&pdev->dev, "Bad ptp filter\n");
- ret = -EINVAL;
- goto err_free_netdev;
- }
netdev->netdev_ops = &pch_gbe_netdev_ops;
netdev->watchdog_timeo = PCH_GBE_WATCHDOG_PERIOD;
diff --git a/drivers/net/ethernet/qlogic/Kconfig b/drivers/net/ethernet/qlogic/Kconfig
index f59e6be4a66e..c14bd3116e45 100644
--- a/drivers/net/ethernet/qlogic/Kconfig
+++ b/drivers/net/ethernet/qlogic/Kconfig
@@ -56,6 +56,16 @@ config QLCNIC_DCB
mode of DCB is supported. PG and PFC values are related only
to Tx.
+config QLCNIC_VXLAN
+ bool "Virtual eXtensible Local Area Network (VXLAN) offload support"
+ default n
+ depends on QLCNIC && VXLAN && !(QLCNIC=y && VXLAN=m)
+ ---help---
+ This enables hardware offload support for VXLAN protocol over QLogic's
+ 84XX series adapters.
+ Say Y here if you want to enable hardware offload support for
+ Virtual eXtensible Local Area Network (VXLAN) in the driver.
+
config QLGE
tristate "QLogic QLGE 10Gb Ethernet Driver Support"
depends on PCI
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
index 70849dea32b1..f09c35d669b3 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
@@ -643,8 +643,9 @@ static int netxen_setup_msi_interrupts(struct netxen_adapter *adapter,
if (adapter->msix_supported) {
netxen_init_msix_entries(adapter, num_msix);
- err = pci_enable_msix(pdev, adapter->msix_entries, num_msix);
- if (err == 0) {
+ err = pci_enable_msix_range(pdev, adapter->msix_entries,
+ num_msix, num_msix);
+ if (err > 0) {
adapter->flags |= NETXEN_NIC_MSIX_ENABLED;
netxen_set_msix_bit(pdev, 1);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index f19f81cde134..f31bb5e9d8a9 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -38,8 +38,8 @@
#define _QLCNIC_LINUX_MAJOR 5
#define _QLCNIC_LINUX_MINOR 3
-#define _QLCNIC_LINUX_SUBVERSION 55
-#define QLCNIC_LINUX_VERSIONID "5.3.55"
+#define _QLCNIC_LINUX_SUBVERSION 57
+#define QLCNIC_LINUX_VERSIONID "5.3.57"
#define QLCNIC_DRV_IDC_VER 0x01
#define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\
(_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
@@ -169,11 +169,20 @@ struct cmd_desc_type0 {
__le64 addr_buffer2;
- __le16 reference_handle;
+ __le16 encap_descr; /* 15:10 offset of outer L3 header,
+ * 9:6 number of 32bit words in outer L3 header,
+ * 5 offload outer L4 checksum,
+ * 4 offload outer L3 checksum,
+ * 3 Inner L4 type, TCP=0, UDP=1,
+ * 2 Inner L3 type, IPv4=0, IPv6=1,
+ * 1 Outer L3 type,IPv4=0, IPv6=1,
+ * 0 type of encapsulation, GRE=0, VXLAN=1
+ */
__le16 mss;
u8 port_ctxid; /* 7:4 ctxid 3:0 port */
- u8 total_hdr_length; /* LSO only : MAC+IP+TCP Hdr size */
- __le16 conn_id; /* IPSec offoad only */
+ u8 hdr_length; /* LSO only : MAC+IP+TCP Hdr size */
+ u8 outer_hdr_length; /* Encapsulation only */
+ u8 rsvd1;
__le64 addr_buffer3;
__le64 addr_buffer1;
@@ -183,7 +192,9 @@ struct cmd_desc_type0 {
__le64 addr_buffer4;
u8 eth_addr[ETH_ALEN];
- __le16 vlan_TCI;
+ __le16 vlan_TCI; /* In case of encapsulation,
+ * this is for outer VLAN
+ */
} __attribute__ ((aligned(64)));
@@ -394,7 +405,7 @@ struct qlcnic_nic_intr_coalesce {
u32 timer_out;
};
-struct qlcnic_dump_template_hdr {
+struct qlcnic_83xx_dump_template_hdr {
u32 type;
u32 offset;
u32 size;
@@ -411,15 +422,42 @@ struct qlcnic_dump_template_hdr {
u32 rsvd[0];
};
+struct qlcnic_82xx_dump_template_hdr {
+ u32 type;
+ u32 offset;
+ u32 size;
+ u32 cap_mask;
+ u32 num_entries;
+ u32 version;
+ u32 timestamp;
+ u32 checksum;
+ u32 drv_cap_mask;
+ u32 sys_info[3];
+ u32 saved_state[16];
+ u32 cap_sizes[8];
+ u32 rsvd[7];
+ u32 capabilities;
+ u32 rsvd1[0];
+};
+
struct qlcnic_fw_dump {
u8 clr; /* flag to indicate if dump is cleared */
bool enable; /* enable/disable dump */
u32 size; /* total size of the dump */
+ u32 cap_mask; /* Current capture mask */
void *data; /* dump data area */
- struct qlcnic_dump_template_hdr *tmpl_hdr;
+ void *tmpl_hdr;
dma_addr_t phys_addr;
void *dma_buffer;
bool use_pex_dma;
+ /* Read only elements which are common between 82xx and 83xx
+ * template header. Update these values immediately after we read
+ * template header from Firmware
+ */
+ u32 tmpl_hdr_size;
+ u32 version;
+ u32 num_entries;
+ u32 offset;
};
/*
@@ -497,6 +535,7 @@ struct qlcnic_hardware_context {
u8 extend_lb_time;
u8 phys_port_id[ETH_ALEN];
u8 lb_mode;
+ u16 vxlan_port;
};
struct qlcnic_adapter_stats {
@@ -511,6 +550,9 @@ struct qlcnic_adapter_stats {
u64 txbytes;
u64 lrobytes;
u64 lso_frames;
+ u64 encap_lso_frames;
+ u64 encap_tx_csummed;
+ u64 encap_rx_csummed;
u64 xmit_on;
u64 xmit_off;
u64 skb_alloc_failure;
@@ -872,6 +914,10 @@ struct qlcnic_mac_vlan_list {
#define QLCNIC_FW_CAPABILITY_2_BEACON BIT_7
#define QLCNIC_FW_CAPABILITY_2_PER_PORT_ESWITCH_CFG BIT_9
+#define QLCNIC_83XX_FW_CAPAB_ENCAP_RX_OFFLOAD BIT_0
+#define QLCNIC_83XX_FW_CAPAB_ENCAP_TX_OFFLOAD BIT_1
+#define QLCNIC_83XX_FW_CAPAB_ENCAP_CKO_OFFLOAD BIT_4
+
/* module types */
#define LINKEVENT_MODULE_NOT_PRESENT 1
#define LINKEVENT_MODULE_OPTICAL_UNKNOWN 2
@@ -966,6 +1012,11 @@ struct qlcnic_ipaddr {
#define QLCNIC_HAS_PHYS_PORT_ID 0x40000
#define QLCNIC_TSS_RSS 0x80000
+#ifdef CONFIG_QLCNIC_VXLAN
+#define QLCNIC_ADD_VXLAN_PORT 0x100000
+#define QLCNIC_DEL_VXLAN_PORT 0x200000
+#endif
+
#define QLCNIC_IS_MSI_FAMILY(adapter) \
((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED))
#define QLCNIC_IS_TSO_CAPABLE(adapter) \
@@ -1769,10 +1820,28 @@ struct qlcnic_hardware_ops {
struct qlcnic_host_tx_ring *);
void (*disable_tx_intr) (struct qlcnic_adapter *,
struct qlcnic_host_tx_ring *);
+ u32 (*get_saved_state)(void *, u32);
+ void (*set_saved_state)(void *, u32, u32);
+ void (*cache_tmpl_hdr_values)(struct qlcnic_fw_dump *);
+ u32 (*get_cap_size)(void *, int);
+ void (*set_sys_info)(void *, int, u32);
+ void (*store_cap_mask)(void *, u32);
};
extern struct qlcnic_nic_template qlcnic_vf_ops;
+static inline bool qlcnic_encap_tx_offload(struct qlcnic_adapter *adapter)
+{
+ return adapter->ahw->extra_capability[0] &
+ QLCNIC_83XX_FW_CAPAB_ENCAP_TX_OFFLOAD;
+}
+
+static inline bool qlcnic_encap_rx_offload(struct qlcnic_adapter *adapter)
+{
+ return adapter->ahw->extra_capability[0] &
+ QLCNIC_83XX_FW_CAPAB_ENCAP_RX_OFFLOAD;
+}
+
static inline int qlcnic_start_firmware(struct qlcnic_adapter *adapter)
{
return adapter->nic_ops->start_firmware(adapter);
@@ -2007,6 +2076,42 @@ static inline void qlcnic_read_phys_port_id(struct qlcnic_adapter *adapter)
adapter->ahw->hw_ops->read_phys_port_id(adapter);
}
+static inline u32 qlcnic_get_saved_state(struct qlcnic_adapter *adapter,
+ void *t_hdr, u32 index)
+{
+ return adapter->ahw->hw_ops->get_saved_state(t_hdr, index);
+}
+
+static inline void qlcnic_set_saved_state(struct qlcnic_adapter *adapter,
+ void *t_hdr, u32 index, u32 value)
+{
+ adapter->ahw->hw_ops->set_saved_state(t_hdr, index, value);
+}
+
+static inline void qlcnic_cache_tmpl_hdr_values(struct qlcnic_adapter *adapter,
+ struct qlcnic_fw_dump *fw_dump)
+{
+ adapter->ahw->hw_ops->cache_tmpl_hdr_values(fw_dump);
+}
+
+static inline u32 qlcnic_get_cap_size(struct qlcnic_adapter *adapter,
+ void *tmpl_hdr, int index)
+{
+ return adapter->ahw->hw_ops->get_cap_size(tmpl_hdr, index);
+}
+
+static inline void qlcnic_set_sys_info(struct qlcnic_adapter *adapter,
+ void *tmpl_hdr, int idx, u32 value)
+{
+ adapter->ahw->hw_ops->set_sys_info(tmpl_hdr, idx, value);
+}
+
+static inline void qlcnic_store_cap_mask(struct qlcnic_adapter *adapter,
+ void *tmpl_hdr, u32 mask)
+{
+ adapter->ahw->hw_ops->store_cap_mask(tmpl_hdr, mask);
+}
+
static inline void qlcnic_dev_request_reset(struct qlcnic_adapter *adapter,
u32 key)
{
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index 27c4f131863b..b7cffb46a75d 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -77,7 +77,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
{QLCNIC_CMD_GET_PORT_CONFIG, 2, 2},
{QLCNIC_CMD_GET_LINK_STATUS, 2, 4},
{QLCNIC_CMD_IDC_ACK, 5, 1},
- {QLCNIC_CMD_INIT_NIC_FUNC, 2, 1},
+ {QLCNIC_CMD_INIT_NIC_FUNC, 3, 1},
{QLCNIC_CMD_STOP_NIC_FUNC, 2, 1},
{QLCNIC_CMD_SET_LED_CONFIG, 5, 1},
{QLCNIC_CMD_GET_LED_CONFIG, 1, 5},
@@ -87,6 +87,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
{QLCNIC_CMD_BC_EVENT_SETUP, 2, 1},
{QLCNIC_CMD_DCB_QUERY_CAP, 1, 2},
{QLCNIC_CMD_DCB_QUERY_PARAM, 1, 50},
+ {QLCNIC_CMD_SET_INGRESS_ENCAP, 2, 1},
};
const u32 qlcnic_83xx_ext_reg_tbl[] = {
@@ -203,7 +204,12 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = {
.disable_sds_intr = qlcnic_83xx_disable_sds_intr,
.enable_tx_intr = qlcnic_83xx_enable_tx_intr,
.disable_tx_intr = qlcnic_83xx_disable_tx_intr,
-
+ .get_saved_state = qlcnic_83xx_get_saved_state,
+ .set_saved_state = qlcnic_83xx_set_saved_state,
+ .cache_tmpl_hdr_values = qlcnic_83xx_cache_tmpl_hdr_values,
+ .get_cap_size = qlcnic_83xx_get_cap_size,
+ .set_sys_info = qlcnic_83xx_set_sys_info,
+ .store_cap_mask = qlcnic_83xx_store_cap_mask,
};
static struct qlcnic_nic_template qlcnic_83xx_ops = {
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
index f92485ca21d1..88d809c35633 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
@@ -308,6 +308,8 @@ struct qlc_83xx_reset {
#define QLC_83XX_IDC_FLASH_PARAM_ADDR 0x3e8020
struct qlcnic_adapter;
+struct qlcnic_fw_dump;
+
struct qlc_83xx_idc {
int (*state_entry) (struct qlcnic_adapter *);
u64 sec_counter;
@@ -526,8 +528,9 @@ enum qlc_83xx_ext_regs {
};
/* Initialize/Stop NIC command bit definitions */
-#define QLC_REGISTER_DCB_AEN BIT_1
#define QLC_REGISTER_LB_IDC BIT_0
+#define QLC_REGISTER_DCB_AEN BIT_1
+#define QLC_83XX_MULTI_TENANCY_INFO BIT_29
#define QLC_INIT_FW_RESOURCES BIT_31
/* 83xx funcitons */
@@ -650,4 +653,10 @@ int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *);
void qlcnic_83xx_aer_stop_poll_work(struct qlcnic_adapter *);
int qlcnic_83xx_aer_reset(struct qlcnic_adapter *);
void qlcnic_83xx_aer_start_poll_work(struct qlcnic_adapter *);
+u32 qlcnic_83xx_get_saved_state(void *, u32);
+void qlcnic_83xx_set_saved_state(void *, u32, u32);
+void qlcnic_83xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *);
+u32 qlcnic_83xx_get_cap_size(void *, int);
+void qlcnic_83xx_set_sys_info(void *, int, u32);
+void qlcnic_83xx_store_cap_mask(void *, u32);
#endif
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
index 90a2dda351ec..b48737dcd3c5 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
@@ -1020,10 +1020,99 @@ static int qlcnic_83xx_idc_check_state_validity(struct qlcnic_adapter *adapter,
return 0;
}
+#ifdef CONFIG_QLCNIC_VXLAN
+#define QLC_83XX_ENCAP_TYPE_VXLAN BIT_1
+#define QLC_83XX_MATCH_ENCAP_ID BIT_2
+#define QLC_83XX_SET_VXLAN_UDP_DPORT BIT_3
+#define QLC_83XX_VXLAN_UDP_DPORT(PORT) ((PORT & 0xffff) << 16)
+
+#define QLCNIC_ENABLE_INGRESS_ENCAP_PARSING 1
+#define QLCNIC_DISABLE_INGRESS_ENCAP_PARSING 0
+
+static int qlcnic_set_vxlan_port(struct qlcnic_adapter *adapter)
+{
+ u16 port = adapter->ahw->vxlan_port;
+ struct qlcnic_cmd_args cmd;
+ int ret = 0;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ ret = qlcnic_alloc_mbx_args(&cmd, adapter,
+ QLCNIC_CMD_INIT_NIC_FUNC);
+ if (ret)
+ return ret;
+
+ cmd.req.arg[1] = QLC_83XX_MULTI_TENANCY_INFO;
+ cmd.req.arg[2] = QLC_83XX_ENCAP_TYPE_VXLAN |
+ QLC_83XX_SET_VXLAN_UDP_DPORT |
+ QLC_83XX_VXLAN_UDP_DPORT(port);
+
+ ret = qlcnic_issue_cmd(adapter, &cmd);
+ if (ret)
+ netdev_err(adapter->netdev,
+ "Failed to set VXLAN port %d in adapter\n",
+ port);
+
+ qlcnic_free_mbx_args(&cmd);
+
+ return ret;
+}
+
+static int qlcnic_set_vxlan_parsing(struct qlcnic_adapter *adapter,
+ bool state)
+{
+ u16 vxlan_port = adapter->ahw->vxlan_port;
+ struct qlcnic_cmd_args cmd;
+ int ret = 0;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ ret = qlcnic_alloc_mbx_args(&cmd, adapter,
+ QLCNIC_CMD_SET_INGRESS_ENCAP);
+ if (ret)
+ return ret;
+
+ cmd.req.arg[1] = state ? QLCNIC_ENABLE_INGRESS_ENCAP_PARSING :
+ QLCNIC_DISABLE_INGRESS_ENCAP_PARSING;
+
+ ret = qlcnic_issue_cmd(adapter, &cmd);
+ if (ret)
+ netdev_err(adapter->netdev,
+ "Failed to %s VXLAN parsing for port %d\n",
+ state ? "enable" : "disable", vxlan_port);
+ else
+ netdev_info(adapter->netdev,
+ "%s VXLAN parsing for port %d\n",
+ state ? "Enabled" : "Disabled", vxlan_port);
+
+ qlcnic_free_mbx_args(&cmd);
+
+ return ret;
+}
+#endif
+
static void qlcnic_83xx_periodic_tasks(struct qlcnic_adapter *adapter)
{
if (adapter->fhash.fnum)
qlcnic_prune_lb_filters(adapter);
+
+#ifdef CONFIG_QLCNIC_VXLAN
+ if (adapter->flags & QLCNIC_ADD_VXLAN_PORT) {
+ if (qlcnic_set_vxlan_port(adapter))
+ return;
+
+ if (qlcnic_set_vxlan_parsing(adapter, true))
+ return;
+
+ adapter->flags &= ~QLCNIC_ADD_VXLAN_PORT;
+ } else if (adapter->flags & QLCNIC_DEL_VXLAN_PORT) {
+ if (qlcnic_set_vxlan_parsing(adapter, false))
+ return;
+
+ adapter->ahw->vxlan_port = 0;
+ adapter->flags &= ~QLCNIC_DEL_VXLAN_PORT;
+ }
+#endif
}
/**
@@ -1301,7 +1390,7 @@ static int qlcnic_83xx_copy_fw_file(struct qlcnic_adapter *adapter)
addr = (u64)dest;
ret = qlcnic_83xx_ms_mem_write128(adapter, addr,
- (u32 *)p_cache, size / 16);
+ p_cache, size / 16);
if (ret) {
dev_err(&adapter->pdev->dev, "MS memory write failed\n");
release_firmware(fw);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index acee1a5d80c6..5bacf5210aed 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -47,6 +47,12 @@ static const struct qlcnic_stats qlcnic_gstrings_stats[] = {
{"lro_pkts", QLC_SIZEOF(stats.lro_pkts), QLC_OFF(stats.lro_pkts)},
{"lrobytes", QLC_SIZEOF(stats.lrobytes), QLC_OFF(stats.lrobytes)},
{"lso_frames", QLC_SIZEOF(stats.lso_frames), QLC_OFF(stats.lso_frames)},
+ {"encap_lso_frames", QLC_SIZEOF(stats.encap_lso_frames),
+ QLC_OFF(stats.encap_lso_frames)},
+ {"encap_tx_csummed", QLC_SIZEOF(stats.encap_tx_csummed),
+ QLC_OFF(stats.encap_tx_csummed)},
+ {"encap_rx_csummed", QLC_SIZEOF(stats.encap_rx_csummed),
+ QLC_OFF(stats.encap_rx_csummed)},
{"skb_alloc_failure", QLC_SIZEOF(stats.skb_alloc_failure),
QLC_OFF(stats.skb_alloc_failure)},
{"mac_filter_limit_overrun", QLC_SIZEOF(stats.mac_filter_limit_overrun),
@@ -1639,14 +1645,14 @@ qlcnic_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump)
}
if (fw_dump->clr)
- dump->len = fw_dump->tmpl_hdr->size + fw_dump->size;
+ dump->len = fw_dump->tmpl_hdr_size + fw_dump->size;
else
dump->len = 0;
if (!qlcnic_check_fw_dump_state(adapter))
dump->flag = ETH_FW_DUMP_DISABLE;
else
- dump->flag = fw_dump->tmpl_hdr->drv_cap_mask;
+ dump->flag = fw_dump->cap_mask;
dump->version = adapter->fw_version;
return 0;
@@ -1671,9 +1677,10 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
netdev_info(netdev, "Dump not available\n");
return -EINVAL;
}
+
/* Copy template header first */
- copy_sz = fw_dump->tmpl_hdr->size;
- hdr_ptr = (u32 *) fw_dump->tmpl_hdr;
+ copy_sz = fw_dump->tmpl_hdr_size;
+ hdr_ptr = (u32 *)fw_dump->tmpl_hdr;
data = buffer;
for (i = 0; i < copy_sz/sizeof(u32); i++)
*data++ = cpu_to_le32(*hdr_ptr++);
@@ -1681,7 +1688,7 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
/* Copy captured dump data */
memcpy(buffer + copy_sz, fw_dump->data, fw_dump->size);
dump->len = copy_sz + fw_dump->size;
- dump->flag = fw_dump->tmpl_hdr->drv_cap_mask;
+ dump->flag = fw_dump->cap_mask;
/* Free dump area once data has been captured */
vfree(fw_dump->data);
@@ -1703,7 +1710,11 @@ static int qlcnic_set_dump_mask(struct qlcnic_adapter *adapter, u32 mask)
return -EOPNOTSUPP;
}
- fw_dump->tmpl_hdr->drv_cap_mask = mask;
+ fw_dump->cap_mask = mask;
+
+ /* Store new capture mask in template header as well*/
+ qlcnic_store_cap_mask(adapter, fw_dump->tmpl_hdr, mask);
+
netdev_info(netdev, "Driver mask changed to: 0x%x\n", mask);
return 0;
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
index 03d18a0be6ce..9f3adf4e70b5 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
@@ -317,9 +317,7 @@ static void qlcnic_write_window_reg(u32 addr, void __iomem *bar0, u32 data)
int
qlcnic_pcie_sem_lock(struct qlcnic_adapter *adapter, int sem, u32 id_reg)
{
- int timeout = 0;
- int err = 0;
- u32 done = 0;
+ int timeout = 0, err = 0, done = 0;
while (!done) {
done = QLCRD32(adapter, QLCNIC_PCIE_REG(PCIE_SEM_LOCK(sem)),
@@ -327,10 +325,20 @@ qlcnic_pcie_sem_lock(struct qlcnic_adapter *adapter, int sem, u32 id_reg)
if (done == 1)
break;
if (++timeout >= QLCNIC_PCIE_SEM_TIMEOUT) {
- dev_err(&adapter->pdev->dev,
- "Failed to acquire sem=%d lock; holdby=%d\n",
- sem,
- id_reg ? QLCRD32(adapter, id_reg, &err) : -1);
+ if (id_reg) {
+ done = QLCRD32(adapter, id_reg, &err);
+ if (done != -1)
+ dev_err(&adapter->pdev->dev,
+ "Failed to acquire sem=%d lock held by=%d\n",
+ sem, done);
+ else
+ dev_err(&adapter->pdev->dev,
+ "Failed to acquire sem=%d lock",
+ sem);
+ } else {
+ dev_err(&adapter->pdev->dev,
+ "Failed to acquire sem=%d lock", sem);
+ }
return -EIO;
}
msleep(1);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
index 63d75617d445..cbe2399c30a0 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
@@ -98,6 +98,7 @@ enum qlcnic_regs {
#define QLCNIC_CMD_GET_LINK_EVENT 0x48
#define QLCNIC_CMD_CONFIGURE_MAC_RX_MODE 0x49
#define QLCNIC_CMD_CONFIGURE_HW_LRO 0x4A
+#define QLCNIC_CMD_SET_INGRESS_ENCAP 0x4E
#define QLCNIC_CMD_INIT_NIC_FUNC 0x60
#define QLCNIC_CMD_STOP_NIC_FUNC 0x61
#define QLCNIC_CMD_IDC_ACK 0x63
@@ -161,6 +162,7 @@ struct qlcnic_host_sds_ring;
struct qlcnic_host_tx_ring;
struct qlcnic_hardware_context;
struct qlcnic_adapter;
+struct qlcnic_fw_dump;
int qlcnic_82xx_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong, int *);
int qlcnic_82xx_hw_write_wx_2M(struct qlcnic_adapter *, ulong, u32);
@@ -213,4 +215,11 @@ int qlcnic_82xx_shutdown(struct pci_dev *);
int qlcnic_82xx_resume(struct qlcnic_adapter *);
void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed);
void qlcnic_fw_poll_work(struct work_struct *work);
+
+u32 qlcnic_82xx_get_saved_state(void *, u32);
+void qlcnic_82xx_set_saved_state(void *, u32, u32);
+void qlcnic_82xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *);
+u32 qlcnic_82xx_get_cap_size(void *, int);
+void qlcnic_82xx_set_sys_info(void *, int, u32);
+void qlcnic_82xx_store_cap_mask(void *, u32);
#endif /* __QLCNIC_HW_H_ */
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
index 54ebf300332a..173b3d12991f 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
@@ -13,16 +13,19 @@
#include "qlcnic.h"
-#define TX_ETHER_PKT 0x01
-#define TX_TCP_PKT 0x02
-#define TX_UDP_PKT 0x03
-#define TX_IP_PKT 0x04
-#define TX_TCP_LSO 0x05
-#define TX_TCP_LSO6 0x06
-#define TX_TCPV6_PKT 0x0b
-#define TX_UDPV6_PKT 0x0c
-#define FLAGS_VLAN_TAGGED 0x10
-#define FLAGS_VLAN_OOB 0x40
+#define QLCNIC_TX_ETHER_PKT 0x01
+#define QLCNIC_TX_TCP_PKT 0x02
+#define QLCNIC_TX_UDP_PKT 0x03
+#define QLCNIC_TX_IP_PKT 0x04
+#define QLCNIC_TX_TCP_LSO 0x05
+#define QLCNIC_TX_TCP_LSO6 0x06
+#define QLCNIC_TX_ENCAP_PKT 0x07
+#define QLCNIC_TX_ENCAP_LSO 0x08
+#define QLCNIC_TX_TCPV6_PKT 0x0b
+#define QLCNIC_TX_UDPV6_PKT 0x0c
+
+#define QLCNIC_FLAGS_VLAN_TAGGED 0x10
+#define QLCNIC_FLAGS_VLAN_OOB 0x40
#define qlcnic_set_tx_vlan_tci(cmd_desc, v) \
(cmd_desc)->vlan_TCI = cpu_to_le16(v);
@@ -364,6 +367,101 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
spin_unlock(&adapter->mac_learn_lock);
}
+#define QLCNIC_ENCAP_VXLAN_PKT BIT_0
+#define QLCNIC_ENCAP_OUTER_L3_IP6 BIT_1
+#define QLCNIC_ENCAP_INNER_L3_IP6 BIT_2
+#define QLCNIC_ENCAP_INNER_L4_UDP BIT_3
+#define QLCNIC_ENCAP_DO_L3_CSUM BIT_4
+#define QLCNIC_ENCAP_DO_L4_CSUM BIT_5
+
+static int qlcnic_tx_encap_pkt(struct qlcnic_adapter *adapter,
+ struct cmd_desc_type0 *first_desc,
+ struct sk_buff *skb,
+ struct qlcnic_host_tx_ring *tx_ring)
+{
+ u8 opcode = 0, inner_hdr_len = 0, outer_hdr_len = 0, total_hdr_len = 0;
+ int copied, copy_len, descr_size;
+ u32 producer = tx_ring->producer;
+ struct cmd_desc_type0 *hwdesc;
+ u16 flags = 0, encap_descr = 0;
+
+ opcode = QLCNIC_TX_ETHER_PKT;
+ encap_descr = QLCNIC_ENCAP_VXLAN_PKT;
+
+ if (skb_is_gso(skb)) {
+ inner_hdr_len = skb_inner_transport_header(skb) +
+ inner_tcp_hdrlen(skb) -
+ skb_inner_mac_header(skb);
+
+ /* VXLAN header size = 8 */
+ outer_hdr_len = skb_transport_offset(skb) + 8 +
+ sizeof(struct udphdr);
+ first_desc->outer_hdr_length = outer_hdr_len;
+ total_hdr_len = inner_hdr_len + outer_hdr_len;
+ encap_descr |= QLCNIC_ENCAP_DO_L3_CSUM |
+ QLCNIC_ENCAP_DO_L4_CSUM;
+ first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
+ first_desc->hdr_length = inner_hdr_len;
+
+ /* Copy inner and outer headers in Tx descriptor(s)
+ * If total_hdr_len > cmd_desc_type0, use multiple
+ * descriptors
+ */
+ copied = 0;
+ descr_size = (int)sizeof(struct cmd_desc_type0);
+ while (copied < total_hdr_len) {
+ copy_len = min(descr_size, (total_hdr_len - copied));
+ hwdesc = &tx_ring->desc_head[producer];
+ tx_ring->cmd_buf_arr[producer].skb = NULL;
+ skb_copy_from_linear_data_offset(skb, copied,
+ (char *)hwdesc,
+ copy_len);
+ copied += copy_len;
+ producer = get_next_index(producer, tx_ring->num_desc);
+ }
+
+ tx_ring->producer = producer;
+
+ /* Make sure updated tx_ring->producer is visible
+ * for qlcnic_tx_avail()
+ */
+ smp_mb();
+ adapter->stats.encap_lso_frames++;
+
+ opcode = QLCNIC_TX_ENCAP_LSO;
+ } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ if (inner_ip_hdr(skb)->version == 6) {
+ if (inner_ipv6_hdr(skb)->nexthdr == IPPROTO_UDP)
+ encap_descr |= QLCNIC_ENCAP_INNER_L4_UDP;
+ } else {
+ if (inner_ip_hdr(skb)->protocol == IPPROTO_UDP)
+ encap_descr |= QLCNIC_ENCAP_INNER_L4_UDP;
+ }
+
+ adapter->stats.encap_tx_csummed++;
+ opcode = QLCNIC_TX_ENCAP_PKT;
+ }
+
+ /* Prepare first 16 bits of byte offset 16 of Tx descriptor */
+ if (ip_hdr(skb)->version == 6)
+ encap_descr |= QLCNIC_ENCAP_OUTER_L3_IP6;
+
+ /* outer IP header's size in 32bit words size*/
+ encap_descr |= (skb_network_header_len(skb) >> 2) << 6;
+
+ /* outer IP header offset */
+ encap_descr |= skb_network_offset(skb) << 10;
+ first_desc->encap_descr = cpu_to_le16(encap_descr);
+
+ first_desc->tcp_hdr_offset = skb_inner_transport_header(skb) -
+ skb->data;
+ first_desc->ip_hdr_offset = skb_inner_network_offset(skb);
+
+ qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);
+
+ return 0;
+}
+
static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
struct cmd_desc_type0 *first_desc, struct sk_buff *skb,
struct qlcnic_host_tx_ring *tx_ring)
@@ -378,11 +476,11 @@ static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
if (protocol == ETH_P_8021Q) {
vh = (struct vlan_ethhdr *)skb->data;
- flags = FLAGS_VLAN_TAGGED;
+ flags = QLCNIC_FLAGS_VLAN_TAGGED;
vlan_tci = ntohs(vh->h_vlan_TCI);
protocol = ntohs(vh->h_vlan_encapsulated_proto);
} else if (vlan_tx_tag_present(skb)) {
- flags = FLAGS_VLAN_OOB;
+ flags = QLCNIC_FLAGS_VLAN_OOB;
vlan_tci = vlan_tx_tag_get(skb);
}
if (unlikely(adapter->tx_pvid)) {
@@ -391,7 +489,7 @@ static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
if (vlan_tci && (adapter->flags & QLCNIC_TAGGING_ENABLED))
goto set_flags;
- flags = FLAGS_VLAN_OOB;
+ flags = QLCNIC_FLAGS_VLAN_OOB;
vlan_tci = adapter->tx_pvid;
}
set_flags:
@@ -402,25 +500,26 @@ set_flags:
flags |= BIT_0;
memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN);
}
- opcode = TX_ETHER_PKT;
+ opcode = QLCNIC_TX_ETHER_PKT;
if (skb_is_gso(skb)) {
hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
- first_desc->total_hdr_length = hdr_len;
- opcode = (protocol == ETH_P_IPV6) ? TX_TCP_LSO6 : TX_TCP_LSO;
+ first_desc->hdr_length = hdr_len;
+ opcode = (protocol == ETH_P_IPV6) ? QLCNIC_TX_TCP_LSO6 :
+ QLCNIC_TX_TCP_LSO;
/* For LSO, we need to copy the MAC/IP/TCP headers into
* the descriptor ring */
copied = 0;
offset = 2;
- if (flags & FLAGS_VLAN_OOB) {
- first_desc->total_hdr_length += VLAN_HLEN;
+ if (flags & QLCNIC_FLAGS_VLAN_OOB) {
+ first_desc->hdr_length += VLAN_HLEN;
first_desc->tcp_hdr_offset = VLAN_HLEN;
first_desc->ip_hdr_offset = VLAN_HLEN;
/* Only in case of TSO on vlan device */
- flags |= FLAGS_VLAN_TAGGED;
+ flags |= QLCNIC_FLAGS_VLAN_TAGGED;
/* Create a TSO vlan header template for firmware */
hwdesc = &tx_ring->desc_head[producer];
@@ -464,16 +563,16 @@ set_flags:
l4proto = ip_hdr(skb)->protocol;
if (l4proto == IPPROTO_TCP)
- opcode = TX_TCP_PKT;
+ opcode = QLCNIC_TX_TCP_PKT;
else if (l4proto == IPPROTO_UDP)
- opcode = TX_UDP_PKT;
+ opcode = QLCNIC_TX_UDP_PKT;
} else if (protocol == ETH_P_IPV6) {
l4proto = ipv6_hdr(skb)->nexthdr;
if (l4proto == IPPROTO_TCP)
- opcode = TX_TCPV6_PKT;
+ opcode = QLCNIC_TX_TCPV6_PKT;
else if (l4proto == IPPROTO_UDP)
- opcode = TX_UDPV6_PKT;
+ opcode = QLCNIC_TX_UDPV6_PKT;
}
}
first_desc->tcp_hdr_offset += skb_transport_offset(skb);
@@ -563,6 +662,8 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
struct ethhdr *phdr;
int i, k, frag_count, delta = 0;
u32 producer, num_txd;
+ u16 protocol;
+ bool l4_is_udp = false;
if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
netif_tx_stop_all_queues(netdev);
@@ -653,8 +754,23 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
tx_ring->producer = get_next_index(producer, num_txd);
smp_mb();
- if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb, tx_ring)))
- goto unwind_buff;
+ protocol = ntohs(skb->protocol);
+ if (protocol == ETH_P_IP)
+ l4_is_udp = ip_hdr(skb)->protocol == IPPROTO_UDP;
+ else if (protocol == ETH_P_IPV6)
+ l4_is_udp = ipv6_hdr(skb)->nexthdr == IPPROTO_UDP;
+
+ /* Check if it is a VXLAN packet */
+ if (!skb->encapsulation || !l4_is_udp ||
+ !qlcnic_encap_tx_offload(adapter)) {
+ if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb,
+ tx_ring)))
+ goto unwind_buff;
+ } else {
+ if (unlikely(qlcnic_tx_encap_pkt(adapter, first_desc,
+ skb, tx_ring)))
+ goto unwind_buff;
+ }
if (adapter->drv_mac_learn)
qlcnic_send_filter(adapter, first_desc, skb);
@@ -1587,6 +1703,13 @@ static inline int qlcnic_83xx_is_lb_pkt(u64 sts_data, int lro_pkt)
return (sts_data & QLC_83XX_NORMAL_LB_PKT) ? 1 : 0;
}
+#define QLCNIC_ENCAP_LENGTH_MASK 0x7f
+
+static inline u8 qlcnic_encap_length(u64 sts_data)
+{
+ return sts_data & QLCNIC_ENCAP_LENGTH_MASK;
+}
+
static struct qlcnic_rx_buffer *
qlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter,
struct qlcnic_host_sds_ring *sds_ring,
@@ -1637,6 +1760,12 @@ qlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter,
skb->protocol = eth_type_trans(skb, netdev);
+ if (qlcnic_encap_length(sts_data[1]) &&
+ skb->ip_summed == CHECKSUM_UNNECESSARY) {
+ skb->encapsulation = 1;
+ adapter->stats.encap_rx_csummed++;
+ }
+
if (vid != 0xffff)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 1222865cfb73..309d05640883 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -21,6 +21,9 @@
#include <linux/aer.h>
#include <linux/log2.h>
#include <linux/pci.h>
+#ifdef CONFIG_QLCNIC_VXLAN
+#include <net/vxlan.h>
+#endif
MODULE_DESCRIPTION("QLogic 1/10 GbE Converged/Intelligent Ethernet Driver");
MODULE_LICENSE("GPL");
@@ -90,7 +93,6 @@ static void qlcnic_82xx_io_resume(struct pci_dev *);
static void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *);
static pci_ers_result_t qlcnic_82xx_io_error_detected(struct pci_dev *,
pci_channel_state_t);
-
static u32 qlcnic_vlan_tx_check(struct qlcnic_adapter *adapter)
{
struct qlcnic_hardware_context *ahw = adapter->ahw;
@@ -462,6 +464,37 @@ static int qlcnic_get_phys_port_id(struct net_device *netdev,
return 0;
}
+#ifdef CONFIG_QLCNIC_VXLAN
+static void qlcnic_add_vxlan_port(struct net_device *netdev,
+ sa_family_t sa_family, __be16 port)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_hardware_context *ahw = adapter->ahw;
+
+ /* Adapter supports only one VXLAN port. Use very first port
+ * for enabling offload
+ */
+ if (!qlcnic_encap_rx_offload(adapter) || ahw->vxlan_port)
+ return;
+
+ ahw->vxlan_port = ntohs(port);
+ adapter->flags |= QLCNIC_ADD_VXLAN_PORT;
+}
+
+static void qlcnic_del_vxlan_port(struct net_device *netdev,
+ sa_family_t sa_family, __be16 port)
+{
+ struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ struct qlcnic_hardware_context *ahw = adapter->ahw;
+
+ if (!qlcnic_encap_rx_offload(adapter) || !ahw->vxlan_port ||
+ (ahw->vxlan_port != ntohs(port)))
+ return;
+
+ adapter->flags |= QLCNIC_DEL_VXLAN_PORT;
+}
+#endif
+
static const struct net_device_ops qlcnic_netdev_ops = {
.ndo_open = qlcnic_open,
.ndo_stop = qlcnic_close,
@@ -480,6 +513,10 @@ static const struct net_device_ops qlcnic_netdev_ops = {
.ndo_fdb_del = qlcnic_fdb_del,
.ndo_fdb_dump = qlcnic_fdb_dump,
.ndo_get_phys_port_id = qlcnic_get_phys_port_id,
+#ifdef CONFIG_QLCNIC_VXLAN
+ .ndo_add_vxlan_port = qlcnic_add_vxlan_port,
+ .ndo_del_vxlan_port = qlcnic_del_vxlan_port,
+#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = qlcnic_poll_controller,
#endif
@@ -561,6 +598,12 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = {
.disable_sds_intr = qlcnic_82xx_disable_sds_intr,
.enable_tx_intr = qlcnic_82xx_enable_tx_intr,
.disable_tx_intr = qlcnic_82xx_disable_tx_intr,
+ .get_saved_state = qlcnic_82xx_get_saved_state,
+ .set_saved_state = qlcnic_82xx_set_saved_state,
+ .cache_tmpl_hdr_values = qlcnic_82xx_cache_tmpl_hdr_values,
+ .get_cap_size = qlcnic_82xx_get_cap_size,
+ .set_sys_info = qlcnic_82xx_set_sys_info,
+ .store_cap_mask = qlcnic_82xx_store_cap_mask,
};
static int qlcnic_check_multi_tx_capability(struct qlcnic_adapter *adapter)
@@ -684,7 +727,7 @@ restore:
int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
{
struct pci_dev *pdev = adapter->pdev;
- int err = -1, vector;
+ int err, vector;
if (!adapter->msix_entries) {
adapter->msix_entries = kcalloc(num_msix,
@@ -701,13 +744,17 @@ enable_msix:
for (vector = 0; vector < num_msix; vector++)
adapter->msix_entries[vector].entry = vector;
- err = pci_enable_msix(pdev, adapter->msix_entries, num_msix);
- if (err == 0) {
+ err = pci_enable_msix_range(pdev,
+ adapter->msix_entries, 1, num_msix);
+
+ if (err == num_msix) {
adapter->flags |= QLCNIC_MSIX_ENABLED;
adapter->ahw->num_msix = num_msix;
dev_info(&pdev->dev, "using msi-x interrupts\n");
- return err;
+ return 0;
} else if (err > 0) {
+ pci_disable_msix(pdev);
+
dev_info(&pdev->dev,
"Unable to allocate %d MSI-X vectors, Available vectors %d\n",
num_msix, err);
@@ -715,12 +762,12 @@ enable_msix:
if (qlcnic_82xx_check(adapter)) {
num_msix = rounddown_pow_of_two(err);
if (err < QLCNIC_82XX_MINIMUM_VECTOR)
- return -EIO;
+ return -ENOSPC;
} else {
num_msix = rounddown_pow_of_two(err - 1);
num_msix += 1;
if (err < QLCNIC_83XX_MINIMUM_VECTOR)
- return -EIO;
+ return -ENOSPC;
}
if (qlcnic_82xx_check(adapter) &&
@@ -747,7 +794,7 @@ enable_msix:
}
}
- return err;
+ return -EIO;
}
static int qlcnic_82xx_calculate_msix_vector(struct qlcnic_adapter *adapter)
@@ -1934,6 +1981,11 @@ qlcnic_attach(struct qlcnic_adapter *adapter)
qlcnic_create_sysfs_entries(adapter);
+#ifdef CONFIG_QLCNIC_VXLAN
+ if (qlcnic_encap_rx_offload(adapter))
+ vxlan_get_rx_port(netdev);
+#endif
+
adapter->is_up = QLCNIC_ADAPTER_UP_MAGIC;
return 0;
@@ -2196,6 +2248,19 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_HW_LRO)
netdev->features |= NETIF_F_LRO;
+ if (qlcnic_encap_tx_offload(adapter)) {
+ netdev->features |= NETIF_F_GSO_UDP_TUNNEL;
+
+ /* encapsulation Tx offload supported by Adapter */
+ netdev->hw_enc_features = NETIF_F_IP_CSUM |
+ NETIF_F_GSO_UDP_TUNNEL |
+ NETIF_F_TSO |
+ NETIF_F_TSO6;
+ }
+
+ if (qlcnic_encap_rx_offload(adapter))
+ netdev->hw_enc_features |= NETIF_F_RXCSUM;
+
netdev->hw_features = netdev->features;
netdev->priv_flags |= IFF_UNICAST_FLT;
netdev->irq = adapter->msix_entries[0].vector;
@@ -2442,8 +2507,8 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err) {
switch (err) {
case -ENOTRECOVERABLE:
- dev_err(&pdev->dev, "Adapter initialization failed due to a faulty hardware. Please reboot\n");
- dev_err(&pdev->dev, "If reboot doesn't help, please replace the adapter with new one and return the faulty adapter for repair\n");
+ dev_err(&pdev->dev, "Adapter initialization failed due to a faulty hardware\n");
+ dev_err(&pdev->dev, "Please replace the adapter with new one and return the faulty adapter for repair\n");
goto err_out_free_hw;
case -ENOMEM:
dev_err(&pdev->dev, "Adapter initialization failed. Please reboot\n");
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c
index 7763962e2ec4..37b979b1266b 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c
@@ -211,6 +211,107 @@ enum qlcnic_minidump_opcode {
QLCNIC_DUMP_RDEND = 255
};
+inline u32 qlcnic_82xx_get_saved_state(void *t_hdr, u32 index)
+{
+ struct qlcnic_82xx_dump_template_hdr *hdr = t_hdr;
+
+ return hdr->saved_state[index];
+}
+
+inline void qlcnic_82xx_set_saved_state(void *t_hdr, u32 index,
+ u32 value)
+{
+ struct qlcnic_82xx_dump_template_hdr *hdr = t_hdr;
+
+ hdr->saved_state[index] = value;
+}
+
+void qlcnic_82xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *fw_dump)
+{
+ struct qlcnic_82xx_dump_template_hdr *hdr;
+
+ hdr = fw_dump->tmpl_hdr;
+ fw_dump->tmpl_hdr_size = hdr->size;
+ fw_dump->version = hdr->version;
+ fw_dump->num_entries = hdr->num_entries;
+ fw_dump->offset = hdr->offset;
+
+ hdr->drv_cap_mask = hdr->cap_mask;
+ fw_dump->cap_mask = hdr->cap_mask;
+}
+
+inline u32 qlcnic_82xx_get_cap_size(void *t_hdr, int index)
+{
+ struct qlcnic_82xx_dump_template_hdr *hdr = t_hdr;
+
+ return hdr->cap_sizes[index];
+}
+
+void qlcnic_82xx_set_sys_info(void *t_hdr, int idx, u32 value)
+{
+ struct qlcnic_82xx_dump_template_hdr *hdr = t_hdr;
+
+ hdr->sys_info[idx] = value;
+}
+
+void qlcnic_82xx_store_cap_mask(void *tmpl_hdr, u32 mask)
+{
+ struct qlcnic_82xx_dump_template_hdr *hdr = tmpl_hdr;
+
+ hdr->drv_cap_mask = mask;
+}
+
+inline u32 qlcnic_83xx_get_saved_state(void *t_hdr, u32 index)
+{
+ struct qlcnic_83xx_dump_template_hdr *hdr = t_hdr;
+
+ return hdr->saved_state[index];
+}
+
+inline void qlcnic_83xx_set_saved_state(void *t_hdr, u32 index,
+ u32 value)
+{
+ struct qlcnic_83xx_dump_template_hdr *hdr = t_hdr;
+
+ hdr->saved_state[index] = value;
+}
+
+void qlcnic_83xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *fw_dump)
+{
+ struct qlcnic_83xx_dump_template_hdr *hdr;
+
+ hdr = fw_dump->tmpl_hdr;
+ fw_dump->tmpl_hdr_size = hdr->size;
+ fw_dump->version = hdr->version;
+ fw_dump->num_entries = hdr->num_entries;
+ fw_dump->offset = hdr->offset;
+
+ hdr->drv_cap_mask = hdr->cap_mask;
+ fw_dump->cap_mask = hdr->cap_mask;
+}
+
+inline u32 qlcnic_83xx_get_cap_size(void *t_hdr, int index)
+{
+ struct qlcnic_83xx_dump_template_hdr *hdr = t_hdr;
+
+ return hdr->cap_sizes[index];
+}
+
+void qlcnic_83xx_set_sys_info(void *t_hdr, int idx, u32 value)
+{
+ struct qlcnic_83xx_dump_template_hdr *hdr = t_hdr;
+
+ hdr->sys_info[idx] = value;
+}
+
+void qlcnic_83xx_store_cap_mask(void *tmpl_hdr, u32 mask)
+{
+ struct qlcnic_83xx_dump_template_hdr *hdr;
+
+ hdr = tmpl_hdr;
+ hdr->drv_cap_mask = mask;
+}
+
struct qlcnic_dump_operations {
enum qlcnic_minidump_opcode opcode;
u32 (*handler)(struct qlcnic_adapter *, struct qlcnic_dump_entry *,
@@ -238,11 +339,11 @@ static u32 qlcnic_dump_crb(struct qlcnic_adapter *adapter,
static u32 qlcnic_dump_ctrl(struct qlcnic_adapter *adapter,
struct qlcnic_dump_entry *entry, __le32 *buffer)
{
+ void *hdr = adapter->ahw->fw_dump.tmpl_hdr;
+ struct __ctrl *ctr = &entry->region.ctrl;
int i, k, timeout = 0;
- u32 addr, data;
+ u32 addr, data, temp;
u8 no_ops;
- struct __ctrl *ctr = &entry->region.ctrl;
- struct qlcnic_dump_template_hdr *t_hdr = adapter->ahw->fw_dump.tmpl_hdr;
addr = ctr->addr;
no_ops = ctr->no_ops;
@@ -285,29 +386,42 @@ static u32 qlcnic_dump_ctrl(struct qlcnic_adapter *adapter,
}
break;
case QLCNIC_DUMP_RD_SAVE:
- if (ctr->index_a)
- addr = t_hdr->saved_state[ctr->index_a];
+ temp = ctr->index_a;
+ if (temp)
+ addr = qlcnic_get_saved_state(adapter,
+ hdr,
+ temp);
data = qlcnic_ind_rd(adapter, addr);
- t_hdr->saved_state[ctr->index_v] = data;
+ qlcnic_set_saved_state(adapter, hdr,
+ ctr->index_v, data);
break;
case QLCNIC_DUMP_WRT_SAVED:
- if (ctr->index_v)
- data = t_hdr->saved_state[ctr->index_v];
+ temp = ctr->index_v;
+ if (temp)
+ data = qlcnic_get_saved_state(adapter,
+ hdr,
+ temp);
else
data = ctr->val1;
- if (ctr->index_a)
- addr = t_hdr->saved_state[ctr->index_a];
+
+ temp = ctr->index_a;
+ if (temp)
+ addr = qlcnic_get_saved_state(adapter,
+ hdr,
+ temp);
qlcnic_ind_wr(adapter, addr, data);
break;
case QLCNIC_DUMP_MOD_SAVE_ST:
- data = t_hdr->saved_state[ctr->index_v];
+ data = qlcnic_get_saved_state(adapter, hdr,
+ ctr->index_v);
data <<= ctr->shl_val;
data >>= ctr->shr_val;
if (ctr->val2)
data &= ctr->val2;
data |= ctr->val3;
data += ctr->val1;
- t_hdr->saved_state[ctr->index_v] = data;
+ qlcnic_set_saved_state(adapter, hdr,
+ ctr->index_v, data);
break;
default:
dev_info(&adapter->pdev->dev,
@@ -544,7 +658,7 @@ out:
static int qlcnic_start_pex_dma(struct qlcnic_adapter *adapter,
struct __mem *mem)
{
- struct qlcnic_dump_template_hdr *tmpl_hdr;
+ struct qlcnic_83xx_dump_template_hdr *tmpl_hdr;
struct device *dev = &adapter->pdev->dev;
u32 dma_no, dma_base_addr, temp_addr;
int i, ret, dma_sts;
@@ -596,7 +710,7 @@ static u32 qlcnic_read_memory_pexdma(struct qlcnic_adapter *adapter,
struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
u32 temp, dma_base_addr, size = 0, read_size = 0;
struct qlcnic_pex_dma_descriptor *dma_descr;
- struct qlcnic_dump_template_hdr *tmpl_hdr;
+ struct qlcnic_83xx_dump_template_hdr *tmpl_hdr;
struct device *dev = &adapter->pdev->dev;
dma_addr_t dma_phys_addr;
void *dma_buffer;
@@ -938,8 +1052,8 @@ static int
qlcnic_fw_flash_get_minidump_temp_size(struct qlcnic_adapter *adapter,
struct qlcnic_cmd_args *cmd)
{
- struct qlcnic_dump_template_hdr tmp_hdr;
- u32 size = sizeof(struct qlcnic_dump_template_hdr) / sizeof(u32);
+ struct qlcnic_83xx_dump_template_hdr tmp_hdr;
+ u32 size = sizeof(tmp_hdr) / sizeof(u32);
int ret = 0;
if (qlcnic_82xx_check(adapter))
@@ -1027,17 +1141,19 @@ free_mem:
return err;
}
+#define QLCNIC_TEMPLATE_VERSION (0x20001)
+
int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
{
- int err;
- u32 temp_size = 0;
- u32 version, csum, *tmp_buf;
struct qlcnic_hardware_context *ahw;
- struct qlcnic_dump_template_hdr *tmpl_hdr;
+ struct qlcnic_fw_dump *fw_dump;
+ u32 version, csum, *tmp_buf;
u8 use_flash_temp = 0;
+ u32 temp_size = 0;
+ int err;
ahw = adapter->ahw;
-
+ fw_dump = &ahw->fw_dump;
err = qlcnic_fw_get_minidump_temp_size(adapter, &version, &temp_size,
&use_flash_temp);
if (err) {
@@ -1046,11 +1162,11 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
return -EIO;
}
- ahw->fw_dump.tmpl_hdr = vzalloc(temp_size);
- if (!ahw->fw_dump.tmpl_hdr)
+ fw_dump->tmpl_hdr = vzalloc(temp_size);
+ if (!fw_dump->tmpl_hdr)
return -ENOMEM;
- tmp_buf = (u32 *)ahw->fw_dump.tmpl_hdr;
+ tmp_buf = (u32 *)fw_dump->tmpl_hdr;
if (use_flash_temp)
goto flash_temp;
@@ -1065,8 +1181,8 @@ flash_temp:
dev_err(&adapter->pdev->dev,
"Failed to get minidump template header %d\n",
err);
- vfree(ahw->fw_dump.tmpl_hdr);
- ahw->fw_dump.tmpl_hdr = NULL;
+ vfree(fw_dump->tmpl_hdr);
+ fw_dump->tmpl_hdr = NULL;
return -EIO;
}
}
@@ -1076,21 +1192,22 @@ flash_temp:
if (csum) {
dev_err(&adapter->pdev->dev,
"Template header checksum validation failed\n");
- vfree(ahw->fw_dump.tmpl_hdr);
- ahw->fw_dump.tmpl_hdr = NULL;
+ vfree(fw_dump->tmpl_hdr);
+ fw_dump->tmpl_hdr = NULL;
return -EIO;
}
- tmpl_hdr = ahw->fw_dump.tmpl_hdr;
- tmpl_hdr->drv_cap_mask = tmpl_hdr->cap_mask;
+ qlcnic_cache_tmpl_hdr_values(adapter, fw_dump);
+
dev_info(&adapter->pdev->dev,
"Default minidump capture mask 0x%x\n",
- tmpl_hdr->cap_mask);
+ fw_dump->cap_mask);
- if ((tmpl_hdr->version & 0xfffff) >= 0x20001)
- ahw->fw_dump.use_pex_dma = true;
+ if (qlcnic_83xx_check(adapter) &&
+ (fw_dump->version & 0xfffff) >= QLCNIC_TEMPLATE_VERSION)
+ fw_dump->use_pex_dma = true;
else
- ahw->fw_dump.use_pex_dma = false;
+ fw_dump->use_pex_dma = false;
qlcnic_enable_fw_dump_state(adapter);
@@ -1099,21 +1216,22 @@ flash_temp:
int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
{
- __le32 *buffer;
- u32 ocm_window;
- char mesg[64];
- char *msg[] = {mesg, NULL};
- int i, k, ops_cnt, ops_index, dump_size = 0;
- u32 entry_offset, dump, no_entries, buf_offset = 0;
- struct qlcnic_dump_entry *entry;
struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
- struct qlcnic_dump_template_hdr *tmpl_hdr = fw_dump->tmpl_hdr;
static const struct qlcnic_dump_operations *fw_dump_ops;
+ struct qlcnic_83xx_dump_template_hdr *hdr_83xx;
+ u32 entry_offset, dump, no_entries, buf_offset = 0;
+ int i, k, ops_cnt, ops_index, dump_size = 0;
struct device *dev = &adapter->pdev->dev;
struct qlcnic_hardware_context *ahw;
- void *temp_buffer;
+ struct qlcnic_dump_entry *entry;
+ void *temp_buffer, *tmpl_hdr;
+ u32 ocm_window;
+ __le32 *buffer;
+ char mesg[64];
+ char *msg[] = {mesg, NULL};
ahw = adapter->ahw;
+ tmpl_hdr = fw_dump->tmpl_hdr;
/* Return if we don't have firmware dump template header */
if (!tmpl_hdr)
@@ -1133,8 +1251,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
netif_info(adapter->ahw, drv, adapter->netdev, "Take FW dump\n");
/* Calculate the size for dump data area only */
for (i = 2, k = 1; (i & QLCNIC_DUMP_MASK_MAX); i <<= 1, k++)
- if (i & tmpl_hdr->drv_cap_mask)
- dump_size += tmpl_hdr->cap_sizes[k];
+ if (i & fw_dump->cap_mask)
+ dump_size += qlcnic_get_cap_size(adapter, tmpl_hdr, k);
+
if (!dump_size)
return -EIO;
@@ -1144,10 +1263,10 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
buffer = fw_dump->data;
fw_dump->size = dump_size;
- no_entries = tmpl_hdr->num_entries;
- entry_offset = tmpl_hdr->offset;
- tmpl_hdr->sys_info[0] = QLCNIC_DRIVER_VERSION;
- tmpl_hdr->sys_info[1] = adapter->fw_version;
+ no_entries = fw_dump->num_entries;
+ entry_offset = fw_dump->offset;
+ qlcnic_set_sys_info(adapter, tmpl_hdr, 0, QLCNIC_DRIVER_VERSION);
+ qlcnic_set_sys_info(adapter, tmpl_hdr, 1, adapter->fw_version);
if (fw_dump->use_pex_dma) {
temp_buffer = dma_alloc_coherent(dev, QLC_PEX_DMA_READ_SIZE,
@@ -1163,16 +1282,17 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
ops_cnt = ARRAY_SIZE(qlcnic_fw_dump_ops);
fw_dump_ops = qlcnic_fw_dump_ops;
} else {
+ hdr_83xx = tmpl_hdr;
ops_cnt = ARRAY_SIZE(qlcnic_83xx_fw_dump_ops);
fw_dump_ops = qlcnic_83xx_fw_dump_ops;
- ocm_window = tmpl_hdr->ocm_wnd_reg[adapter->ahw->pci_func];
- tmpl_hdr->saved_state[QLC_83XX_OCM_INDEX] = ocm_window;
- tmpl_hdr->saved_state[QLC_83XX_PCI_INDEX] = ahw->pci_func;
+ ocm_window = hdr_83xx->ocm_wnd_reg[ahw->pci_func];
+ hdr_83xx->saved_state[QLC_83XX_OCM_INDEX] = ocm_window;
+ hdr_83xx->saved_state[QLC_83XX_PCI_INDEX] = ahw->pci_func;
}
for (i = 0; i < no_entries; i++) {
- entry = (void *)tmpl_hdr + entry_offset;
- if (!(entry->hdr.mask & tmpl_hdr->drv_cap_mask)) {
+ entry = tmpl_hdr + entry_offset;
+ if (!(entry->hdr.mask & fw_dump->cap_mask)) {
entry->hdr.flags |= QLCNIC_DUMP_SKIP;
entry_offset += entry->hdr.offset;
continue;
@@ -1209,8 +1329,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
fw_dump->clr = 1;
snprintf(mesg, sizeof(mesg), "FW_DUMP=%s", adapter->netdev->name);
- dev_info(dev, "%s: Dump data %d bytes captured, template header size %d bytes\n",
- adapter->netdev->name, fw_dump->size, tmpl_hdr->size);
+ netdev_info(adapter->netdev,
+ "Dump data %d bytes captured, template header size %d bytes\n",
+ fw_dump->size, fw_dump->tmpl_hdr_size);
/* Send a udev event to notify availability of FW dump */
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, msg);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
index e5277a632671..14f748cbf0de 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
@@ -15,6 +15,7 @@
#define QLC_MAC_OPCODE_MASK 0x7
#define QLC_VF_FLOOD_BIT BIT_16
#define QLC_FLOOD_MODE 0x5
+#define QLC_SRIOV_ALLOW_VLAN0 BIT_19
static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *, u8);
@@ -335,8 +336,11 @@ static int qlcnic_sriov_pf_cfg_vlan_filtering(struct qlcnic_adapter *adapter,
return err;
cmd.req.arg[1] = 0x4;
- if (enable)
+ if (enable) {
cmd.req.arg[1] |= BIT_16;
+ if (qlcnic_84xx_check(adapter))
+ cmd.req.arg[1] |= QLC_SRIOV_ALLOW_VLAN0;
+ }
err = qlcnic_issue_cmd(adapter, &cmd);
if (err)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
index 3d64113a35af..448d156c3d08 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
@@ -350,33 +350,15 @@ static ssize_t qlcnic_sysfs_write_mem(struct file *filp, struct kobject *kobj,
return size;
}
-static u32 qlcnic_get_pci_func_count(struct qlcnic_adapter *adapter)
-{
- struct qlcnic_hardware_context *ahw = adapter->ahw;
- u32 count = 0;
-
- if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED))
- return ahw->total_nic_func;
-
- if (ahw->total_pci_func <= QLC_DEFAULT_VNIC_COUNT)
- count = QLC_DEFAULT_VNIC_COUNT;
- else
- count = ahw->max_vnic_func;
-
- return count;
-}
-
int qlcnic_is_valid_nic_func(struct qlcnic_adapter *adapter, u8 pci_func)
{
- u32 pci_func_count = qlcnic_get_pci_func_count(adapter);
int i;
- for (i = 0; i < pci_func_count; i++) {
+ for (i = 0; i < adapter->ahw->max_vnic_func; i++) {
if (adapter->npars[i].pci_func == pci_func)
return i;
}
-
- return -1;
+ return -EINVAL;
}
static int validate_pm_config(struct qlcnic_adapter *adapter,
@@ -464,23 +446,21 @@ static ssize_t qlcnic_sysfs_read_pm_config(struct file *filp,
{
struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
- u32 pci_func_count = qlcnic_get_pci_func_count(adapter);
struct qlcnic_pm_func_cfg *pm_cfg;
- int i, pm_cfg_size;
u8 pci_func;
+ u32 count;
+ int i;
- pm_cfg_size = pci_func_count * sizeof(*pm_cfg);
- if (size != pm_cfg_size)
- return QL_STATUS_INVALID_PARAM;
-
- memset(buf, 0, pm_cfg_size);
+ memset(buf, 0, size);
pm_cfg = (struct qlcnic_pm_func_cfg *)buf;
-
- for (i = 0; i < pci_func_count; i++) {
+ count = size / sizeof(struct qlcnic_pm_func_cfg);
+ for (i = 0; i < adapter->ahw->total_nic_func; i++) {
pci_func = adapter->npars[i].pci_func;
- if (!adapter->npars[i].active)
+ if (pci_func >= count) {
+ dev_dbg(dev, "%s: Total nic functions[%d], App sent function count[%d]\n",
+ __func__, adapter->ahw->total_nic_func, count);
continue;
-
+ }
if (!adapter->npars[i].eswitch_status)
continue;
@@ -494,7 +474,6 @@ static ssize_t qlcnic_sysfs_read_pm_config(struct file *filp,
static int validate_esw_config(struct qlcnic_adapter *adapter,
struct qlcnic_esw_func_cfg *esw_cfg, int count)
{
- u32 pci_func_count = qlcnic_get_pci_func_count(adapter);
struct qlcnic_hardware_context *ahw = adapter->ahw;
int i, ret;
u32 op_mode;
@@ -507,7 +486,7 @@ static int validate_esw_config(struct qlcnic_adapter *adapter,
for (i = 0; i < count; i++) {
pci_func = esw_cfg[i].pci_func;
- if (pci_func >= pci_func_count)
+ if (pci_func >= ahw->max_vnic_func)
return QL_STATUS_INVALID_PARAM;
if (adapter->ahw->op_mode == QLCNIC_MGMT_FUNC)
@@ -642,23 +621,21 @@ static ssize_t qlcnic_sysfs_read_esw_config(struct file *file,
{
struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
- u32 pci_func_count = qlcnic_get_pci_func_count(adapter);
struct qlcnic_esw_func_cfg *esw_cfg;
- size_t esw_cfg_size;
- u8 i, pci_func;
-
- esw_cfg_size = pci_func_count * sizeof(*esw_cfg);
- if (size != esw_cfg_size)
- return QL_STATUS_INVALID_PARAM;
+ u8 pci_func;
+ u32 count;
+ int i;
- memset(buf, 0, esw_cfg_size);
+ memset(buf, 0, size);
esw_cfg = (struct qlcnic_esw_func_cfg *)buf;
-
- for (i = 0; i < pci_func_count; i++) {
+ count = size / sizeof(struct qlcnic_esw_func_cfg);
+ for (i = 0; i < adapter->ahw->total_nic_func; i++) {
pci_func = adapter->npars[i].pci_func;
- if (!adapter->npars[i].active)
+ if (pci_func >= count) {
+ dev_dbg(dev, "%s: Total nic functions[%d], App sent function count[%d]\n",
+ __func__, adapter->ahw->total_nic_func, count);
continue;
-
+ }
if (!adapter->npars[i].eswitch_status)
continue;
@@ -741,23 +718,24 @@ static ssize_t qlcnic_sysfs_read_npar_config(struct file *file,
{
struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
- u32 pci_func_count = qlcnic_get_pci_func_count(adapter);
struct qlcnic_npar_func_cfg *np_cfg;
struct qlcnic_info nic_info;
- size_t np_cfg_size;
int i, ret;
-
- np_cfg_size = pci_func_count * sizeof(*np_cfg);
- if (size != np_cfg_size)
- return QL_STATUS_INVALID_PARAM;
+ u32 count;
memset(&nic_info, 0, sizeof(struct qlcnic_info));
- memset(buf, 0, np_cfg_size);
+ memset(buf, 0, size);
np_cfg = (struct qlcnic_npar_func_cfg *)buf;
- for (i = 0; i < pci_func_count; i++) {
+ count = size / sizeof(struct qlcnic_npar_func_cfg);
+ for (i = 0; i < adapter->ahw->total_nic_func; i++) {
if (qlcnic_is_valid_nic_func(adapter, i) < 0)
continue;
+ if (adapter->npars[i].pci_func >= count) {
+ dev_dbg(dev, "%s: Total nic functions[%d], App sent function count[%d]\n",
+ __func__, adapter->ahw->total_nic_func, count);
+ continue;
+ }
ret = qlcnic_get_nic_info(adapter, &nic_info, i);
if (ret)
return ret;
@@ -783,7 +761,6 @@ static ssize_t qlcnic_sysfs_get_port_stats(struct file *file,
{
struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
- u32 pci_func_count = qlcnic_get_pci_func_count(adapter);
struct qlcnic_esw_statistics port_stats;
int ret;
@@ -793,7 +770,7 @@ static ssize_t qlcnic_sysfs_get_port_stats(struct file *file,
if (size != sizeof(struct qlcnic_esw_statistics))
return QL_STATUS_INVALID_PARAM;
- if (offset >= pci_func_count)
+ if (offset >= adapter->ahw->max_vnic_func)
return QL_STATUS_INVALID_PARAM;
memset(&port_stats, 0, size);
@@ -884,13 +861,12 @@ static ssize_t qlcnic_sysfs_clear_port_stats(struct file *file,
struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
- u32 pci_func_count = qlcnic_get_pci_func_count(adapter);
int ret;
if (qlcnic_83xx_check(adapter))
return QLC_STATUS_UNSUPPORTED_CMD;
- if (offset >= pci_func_count)
+ if (offset >= adapter->ahw->max_vnic_func)
return QL_STATUS_INVALID_PARAM;
ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_PORT, offset,
@@ -914,17 +890,12 @@ static ssize_t qlcnic_sysfs_read_pci_config(struct file *file,
{
struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
- u32 pci_func_count = qlcnic_get_pci_func_count(adapter);
struct qlcnic_pci_func_cfg *pci_cfg;
struct qlcnic_pci_info *pci_info;
- size_t pci_cfg_sz;
int i, ret;
+ u32 count;
- pci_cfg_sz = pci_func_count * sizeof(*pci_cfg);
- if (size != pci_cfg_sz)
- return QL_STATUS_INVALID_PARAM;
-
- pci_info = kcalloc(pci_func_count, sizeof(*pci_info), GFP_KERNEL);
+ pci_info = kcalloc(size, sizeof(*pci_info), GFP_KERNEL);
if (!pci_info)
return -ENOMEM;
@@ -935,7 +906,8 @@ static ssize_t qlcnic_sysfs_read_pci_config(struct file *file,
}
pci_cfg = (struct qlcnic_pci_func_cfg *)buf;
- for (i = 0; i < pci_func_count; i++) {
+ count = size / sizeof(struct qlcnic_pci_func_cfg);
+ for (i = 0; i < count; i++) {
pci_cfg[i].pci_func = pci_info[i].id;
pci_cfg[i].func_type = pci_info[i].type;
pci_cfg[i].func_state = 0;
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
index 656c65ddadb4..0a1d76acab81 100644
--- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
@@ -2556,11 +2556,10 @@ static int ql_tso(struct sk_buff *skb, struct ob_mac_tso_iocb_req *mac_iocb_ptr)
if (skb_is_gso(skb)) {
int err;
- if (skb_header_cloned(skb)) {
- err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
- if (err)
- return err;
- }
+
+ err = skb_cow_head(skb, 0);
+ if (err < 0)
+ return err;
mac_iocb_ptr->opcode = OPCODE_OB_MAC_TSO_IOCB;
mac_iocb_ptr->flags3 |= OB_MAC_TSO_IOCB_IC;
@@ -3331,24 +3330,16 @@ static void ql_enable_msix(struct ql_adapter *qdev)
for (i = 0; i < qdev->intr_count; i++)
qdev->msi_x_entry[i].entry = i;
- /* Loop to get our vectors. We start with
- * what we want and settle for what we get.
- */
- do {
- err = pci_enable_msix(qdev->pdev,
- qdev->msi_x_entry, qdev->intr_count);
- if (err > 0)
- qdev->intr_count = err;
- } while (err > 0);
-
+ err = pci_enable_msix_range(qdev->pdev, qdev->msi_x_entry,
+ 1, qdev->intr_count);
if (err < 0) {
kfree(qdev->msi_x_entry);
qdev->msi_x_entry = NULL;
netif_warn(qdev, ifup, qdev->ndev,
"MSI-X Enable failed, trying MSI.\n");
- qdev->intr_count = 1;
qlge_irq_type = MSI_IRQ;
- } else if (err == 0) {
+ } else {
+ qdev->intr_count = err;
set_bit(QL_MSIX_ENABLED, &qdev->flags);
netif_info(qdev, ifup, qdev->ndev,
"MSI-X Enabled, got %d vectors.\n",
diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c
index 819b74cefd64..cd045ecb9816 100644
--- a/drivers/net/ethernet/rdc/r6040.c
+++ b/drivers/net/ethernet/rdc/r6040.c
@@ -270,11 +270,6 @@ static int r6040_mdiobus_write(struct mii_bus *bus, int phy_addr,
return r6040_phy_write(ioaddr, phy_addr, reg, value);
}
-static int r6040_mdiobus_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
static void r6040_free_txbufs(struct net_device *dev)
{
struct r6040_private *lp = netdev_priv(dev);
@@ -1191,7 +1186,6 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
lp->mii_bus->priv = dev;
lp->mii_bus->read = r6040_mdiobus_read;
lp->mii_bus->write = r6040_mdiobus_write;
- lp->mii_bus->reset = r6040_mdiobus_reset;
lp->mii_bus->name = "r6040_eth_mii";
snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
dev_name(&pdev->dev), card_idx);
diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c
index 737c1a881f78..2bc728e65e24 100644
--- a/drivers/net/ethernet/realtek/8139cp.c
+++ b/drivers/net/ethernet/realtek/8139cp.c
@@ -476,7 +476,7 @@ rx_status_loop:
rx = 0;
cpw16(IntrStatus, cp_rx_intr_mask);
- while (1) {
+ while (rx < budget) {
u32 status, len;
dma_addr_t mapping, new_mapping;
struct sk_buff *skb, *new_skb;
@@ -554,9 +554,6 @@ rx_next:
else
desc->opts1 = cpu_to_le32(DescOwn | cp->rx_buf_sz);
rx_tail = NEXT_RX(rx_tail);
-
- if (rx >= budget)
- break;
}
cp->rx_tail = rx_tail;
@@ -899,7 +896,7 @@ out_unlock:
return NETDEV_TX_OK;
out_dma_error:
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
cp->dev->stats.tx_dropped++;
goto out_unlock;
}
diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c
index da5972eefdd2..2e5df148af4c 100644
--- a/drivers/net/ethernet/realtek/8139too.c
+++ b/drivers/net/ethernet/realtek/8139too.c
@@ -1717,9 +1717,9 @@ static netdev_tx_t rtl8139_start_xmit (struct sk_buff *skb,
if (len < ETH_ZLEN)
memset(tp->tx_buf[entry], 0, ETH_ZLEN);
skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
} else {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
dev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
@@ -2522,16 +2522,16 @@ rtl8139_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
netdev_stats_to_stats64(stats, &dev->stats);
do {
- start = u64_stats_fetch_begin_bh(&tp->rx_stats.syncp);
+ start = u64_stats_fetch_begin_irq(&tp->rx_stats.syncp);
stats->rx_packets = tp->rx_stats.packets;
stats->rx_bytes = tp->rx_stats.bytes;
- } while (u64_stats_fetch_retry_bh(&tp->rx_stats.syncp, start));
+ } while (u64_stats_fetch_retry_irq(&tp->rx_stats.syncp, start));
do {
- start = u64_stats_fetch_begin_bh(&tp->tx_stats.syncp);
+ start = u64_stats_fetch_begin_irq(&tp->tx_stats.syncp);
stats->tx_packets = tp->tx_stats.packets;
stats->tx_bytes = tp->tx_stats.bytes;
- } while (u64_stats_fetch_retry_bh(&tp->tx_stats.syncp, start));
+ } while (u64_stats_fetch_retry_irq(&tp->tx_stats.syncp, start));
return stats;
}
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 3ff7bc3e7a23..aa1c079f231d 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -5834,7 +5834,7 @@ static void rtl8169_tx_clear_range(struct rtl8169_private *tp, u32 start,
tp->TxDescArray + entry);
if (skb) {
tp->dev->stats.tx_dropped++;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
tx_skb->skb = NULL;
}
}
@@ -6059,7 +6059,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
err_dma_1:
rtl8169_unmap_tx_skb(d, tp->tx_skb + entry, txd);
err_dma_0:
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
err_update_stats:
dev->stats.tx_dropped++;
return NETDEV_TX_OK;
@@ -6142,7 +6142,7 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
tp->tx_stats.packets++;
tp->tx_stats.bytes += tx_skb->skb->len;
u64_stats_update_end(&tp->tx_stats.syncp);
- dev_kfree_skb(tx_skb->skb);
+ dev_kfree_skb_any(tx_skb->skb);
tx_skb->skb = NULL;
}
dirty_tx++;
@@ -6590,17 +6590,17 @@ rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
rtl8169_rx_missed(dev, ioaddr);
do {
- start = u64_stats_fetch_begin_bh(&tp->rx_stats.syncp);
+ start = u64_stats_fetch_begin_irq(&tp->rx_stats.syncp);
stats->rx_packets = tp->rx_stats.packets;
stats->rx_bytes = tp->rx_stats.bytes;
- } while (u64_stats_fetch_retry_bh(&tp->rx_stats.syncp, start));
+ } while (u64_stats_fetch_retry_irq(&tp->rx_stats.syncp, start));
do {
- start = u64_stats_fetch_begin_bh(&tp->tx_stats.syncp);
+ start = u64_stats_fetch_begin_irq(&tp->tx_stats.syncp);
stats->tx_packets = tp->tx_stats.packets;
stats->tx_bytes = tp->tx_stats.bytes;
- } while (u64_stats_fetch_retry_bh(&tp->tx_stats.syncp, start));
+ } while (u64_stats_fetch_retry_irq(&tp->tx_stats.syncp, start));
stats->rx_dropped = dev->stats.rx_dropped;
stats->tx_dropped = dev->stats.tx_dropped;
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 040cb94e8219..6a9509ccd33b 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -1,8 +1,9 @@
/* SuperH Ethernet device driver
*
* Copyright (C) 2006-2012 Nobuhiro Iwamatsu
- * Copyright (C) 2008-2013 Renesas Solutions Corp.
- * Copyright (C) 2013 Cogent Embedded, Inc.
+ * Copyright (C) 2008-2014 Renesas Solutions Corp.
+ * Copyright (C) 2013-2014 Cogent Embedded, Inc.
+ * Copyright (C) 2014 Codethink Limited
*
* 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 +28,10 @@
#include <linux/platform_device.h>
#include <linux/mdio-bitbang.h>
#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/cache.h>
#include <linux/io.h>
@@ -36,6 +41,7 @@
#include <linux/if_vlan.h>
#include <linux/clk.h>
#include <linux/sh_eth.h>
+#include <linux/of_mdio.h>
#include "sh_eth.h"
@@ -394,7 +400,8 @@ static void sh_eth_select_mii(struct net_device *ndev)
value = 0x0;
break;
default:
- pr_warn("PHY interface mode was not setup. Set to MII.\n");
+ netdev_warn(ndev,
+ "PHY interface mode was not setup. Set to MII.\n");
value = 0x1;
break;
}
@@ -848,7 +855,7 @@ static int sh_eth_check_reset(struct net_device *ndev)
cnt--;
}
if (cnt <= 0) {
- pr_err("Device reset failed\n");
+ netdev_err(ndev, "Device reset failed\n");
ret = -ETIMEDOUT;
}
return ret;
@@ -866,7 +873,7 @@ static int sh_eth_reset(struct net_device *ndev)
ret = sh_eth_check_reset(ndev);
if (ret)
- goto out;
+ return ret;
/* Table Init */
sh_eth_write(ndev, 0x0, TDLAR);
@@ -893,7 +900,6 @@ static int sh_eth_reset(struct net_device *ndev)
EDMR);
}
-out:
return ret;
}
@@ -1257,7 +1263,7 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start)
/* Soft Reset */
ret = sh_eth_reset(ndev);
if (ret)
- goto out;
+ return ret;
if (mdp->cd->rmiimode)
sh_eth_write(ndev, 0x1, RMIIMODE);
@@ -1336,7 +1342,6 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start)
netif_start_queue(ndev);
}
-out:
return ret;
}
@@ -1550,8 +1555,7 @@ ignore_link:
/* Unused write back interrupt */
if (intr_status & EESR_TABT) { /* Transmit Abort int */
ndev->stats.tx_aborted_errors++;
- if (netif_msg_tx_err(mdp))
- dev_err(&ndev->dev, "Transmit Abort\n");
+ netif_err(mdp, tx_err, ndev, "Transmit Abort\n");
}
}
@@ -1560,45 +1564,38 @@ ignore_link:
if (intr_status & EESR_RFRMER) {
/* Receive Frame Overflow int */
ndev->stats.rx_frame_errors++;
- if (netif_msg_rx_err(mdp))
- dev_err(&ndev->dev, "Receive Abort\n");
+ netif_err(mdp, rx_err, ndev, "Receive Abort\n");
}
}
if (intr_status & EESR_TDE) {
/* Transmit Descriptor Empty int */
ndev->stats.tx_fifo_errors++;
- if (netif_msg_tx_err(mdp))
- dev_err(&ndev->dev, "Transmit Descriptor Empty\n");
+ netif_err(mdp, tx_err, ndev, "Transmit Descriptor Empty\n");
}
if (intr_status & EESR_TFE) {
/* FIFO under flow */
ndev->stats.tx_fifo_errors++;
- if (netif_msg_tx_err(mdp))
- dev_err(&ndev->dev, "Transmit FIFO Under flow\n");
+ netif_err(mdp, tx_err, ndev, "Transmit FIFO Under flow\n");
}
if (intr_status & EESR_RDE) {
/* Receive Descriptor Empty int */
ndev->stats.rx_over_errors++;
-
- if (netif_msg_rx_err(mdp))
- dev_err(&ndev->dev, "Receive Descriptor Empty\n");
+ netif_err(mdp, rx_err, ndev, "Receive Descriptor Empty\n");
}
if (intr_status & EESR_RFE) {
/* Receive FIFO Overflow int */
ndev->stats.rx_fifo_errors++;
- if (netif_msg_rx_err(mdp))
- dev_err(&ndev->dev, "Receive FIFO Overflow\n");
+ netif_err(mdp, rx_err, ndev, "Receive FIFO Overflow\n");
}
if (!mdp->cd->no_ade && (intr_status & EESR_ADE)) {
/* Address Error */
ndev->stats.tx_fifo_errors++;
- if (netif_msg_tx_err(mdp))
- dev_err(&ndev->dev, "Address Error\n");
+ netif_err(mdp, tx_err, ndev, "Address Error\n");
}
mask = EESR_TWB | EESR_TABT | EESR_ADE | EESR_TDE | EESR_TFE;
@@ -1609,9 +1606,9 @@ ignore_link:
u32 edtrr = sh_eth_read(ndev, EDTRR);
/* dmesg */
- dev_err(&ndev->dev, "TX error. status=%8.8x cur_tx=%8.8x dirty_tx=%8.8x state=%8.8x EDTRR=%8.8x.\n",
- intr_status, mdp->cur_tx, mdp->dirty_tx,
- (u32)ndev->state, edtrr);
+ netdev_err(ndev, "TX error. status=%8.8x cur_tx=%8.8x dirty_tx=%8.8x state=%8.8x EDTRR=%8.8x.\n",
+ intr_status, mdp->cur_tx, mdp->dirty_tx,
+ (u32)ndev->state, edtrr);
/* dirty buffer free */
sh_eth_txfree(ndev);
@@ -1656,9 +1653,9 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev)
EESIPR);
__napi_schedule(&mdp->napi);
} else {
- dev_warn(&ndev->dev,
- "ignoring interrupt, status 0x%08lx, mask 0x%08lx.\n",
- intr_status, intr_enable);
+ netdev_warn(ndev,
+ "ignoring interrupt, status 0x%08lx, mask 0x%08lx.\n",
+ intr_status, intr_enable);
}
}
@@ -1757,27 +1754,42 @@ static void sh_eth_adjust_link(struct net_device *ndev)
/* PHY init function */
static int sh_eth_phy_init(struct net_device *ndev)
{
+ struct device_node *np = ndev->dev.parent->of_node;
struct sh_eth_private *mdp = netdev_priv(ndev);
- char phy_id[MII_BUS_ID_SIZE + 3];
struct phy_device *phydev = NULL;
- snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT,
- mdp->mii_bus->id, mdp->phy_id);
-
mdp->link = 0;
mdp->speed = 0;
mdp->duplex = -1;
/* Try connect to PHY */
- phydev = phy_connect(ndev, phy_id, sh_eth_adjust_link,
- mdp->phy_interface);
+ if (np) {
+ struct device_node *pn;
+
+ pn = of_parse_phandle(np, "phy-handle", 0);
+ phydev = of_phy_connect(ndev, pn,
+ sh_eth_adjust_link, 0,
+ mdp->phy_interface);
+
+ if (!phydev)
+ phydev = ERR_PTR(-ENOENT);
+ } else {
+ char phy_id[MII_BUS_ID_SIZE + 3];
+
+ snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT,
+ mdp->mii_bus->id, mdp->phy_id);
+
+ phydev = phy_connect(ndev, phy_id, sh_eth_adjust_link,
+ mdp->phy_interface);
+ }
+
if (IS_ERR(phydev)) {
- dev_err(&ndev->dev, "phy_connect failed\n");
+ netdev_err(ndev, "failed to connect PHY\n");
return PTR_ERR(phydev);
}
- dev_info(&ndev->dev, "attached PHY %d (IRQ %d) to driver %s\n",
- phydev->addr, phydev->irq, phydev->drv->name);
+ netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n",
+ phydev->addr, phydev->irq, phydev->drv->name);
mdp->phydev = phydev;
@@ -1958,12 +1970,12 @@ static int sh_eth_set_ringparam(struct net_device *ndev,
ret = sh_eth_ring_init(ndev);
if (ret < 0) {
- dev_err(&ndev->dev, "%s: sh_eth_ring_init failed.\n", __func__);
+ netdev_err(ndev, "%s: sh_eth_ring_init failed.\n", __func__);
return ret;
}
ret = sh_eth_dev_init(ndev, false);
if (ret < 0) {
- dev_err(&ndev->dev, "%s: sh_eth_dev_init failed.\n", __func__);
+ netdev_err(ndev, "%s: sh_eth_dev_init failed.\n", __func__);
return ret;
}
@@ -2004,7 +2016,7 @@ static int sh_eth_open(struct net_device *ndev)
ret = request_irq(ndev->irq, sh_eth_interrupt,
mdp->cd->irq_flags, ndev->name, ndev);
if (ret) {
- dev_err(&ndev->dev, "Can not assign IRQ number\n");
+ netdev_err(ndev, "Can not assign IRQ number\n");
goto out_napi_off;
}
@@ -2042,10 +2054,9 @@ static void sh_eth_tx_timeout(struct net_device *ndev)
netif_stop_queue(ndev);
- if (netif_msg_timer(mdp)) {
- dev_err(&ndev->dev, "%s: transmit timed out, status %8.8x, resetting...\n",
- ndev->name, (int)sh_eth_read(ndev, EESR));
- }
+ netif_err(mdp, timer, ndev,
+ "transmit timed out, status %8.8x, resetting...\n",
+ (int)sh_eth_read(ndev, EESR));
/* tx_errors count up */
ndev->stats.tx_errors++;
@@ -2080,8 +2091,7 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
spin_lock_irqsave(&mdp->lock, flags);
if ((mdp->cur_tx - mdp->dirty_tx) >= (mdp->num_tx_ring - 4)) {
if (!sh_eth_txfree(ndev)) {
- if (netif_msg_tx_queued(mdp))
- dev_warn(&ndev->dev, "TxFD exhausted.\n");
+ netif_warn(mdp, tx_queued, ndev, "TxFD exhausted.\n");
netif_stop_queue(ndev);
spin_unlock_irqrestore(&mdp->lock, flags);
return NETDEV_TX_BUSY;
@@ -2098,8 +2108,8 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb->len + 2);
txdesc->addr = dma_map_single(&ndev->dev, skb->data, skb->len,
DMA_TO_DEVICE);
- if (skb->len < ETHERSMALL)
- txdesc->buffer_length = ETHERSMALL;
+ if (skb->len < ETH_ZLEN)
+ txdesc->buffer_length = ETH_ZLEN;
else
txdesc->buffer_length = skb->len;
@@ -2251,7 +2261,7 @@ static int sh_eth_tsu_busy(struct net_device *ndev)
udelay(10);
timeout--;
if (timeout <= 0) {
- dev_err(&ndev->dev, "%s: timeout\n", __func__);
+ netdev_err(ndev, "%s: timeout\n", __func__);
return -ETIMEDOUT;
}
}
@@ -2571,37 +2581,30 @@ static void sh_eth_tsu_init(struct sh_eth_private *mdp)
}
/* MDIO bus release function */
-static int sh_mdio_release(struct net_device *ndev)
+static int sh_mdio_release(struct sh_eth_private *mdp)
{
- struct mii_bus *bus = dev_get_drvdata(&ndev->dev);
-
/* unregister mdio bus */
- mdiobus_unregister(bus);
-
- /* remove mdio bus info from net_device */
- dev_set_drvdata(&ndev->dev, NULL);
+ mdiobus_unregister(mdp->mii_bus);
/* free bitbang info */
- free_mdio_bitbang(bus);
+ free_mdio_bitbang(mdp->mii_bus);
return 0;
}
/* MDIO bus init function */
-static int sh_mdio_init(struct net_device *ndev, int id,
+static int sh_mdio_init(struct sh_eth_private *mdp,
struct sh_eth_plat_data *pd)
{
int ret, i;
struct bb_info *bitbang;
- struct sh_eth_private *mdp = netdev_priv(ndev);
+ struct platform_device *pdev = mdp->pdev;
+ struct device *dev = &mdp->pdev->dev;
/* create bit control struct for PHY */
- bitbang = devm_kzalloc(&ndev->dev, sizeof(struct bb_info),
- GFP_KERNEL);
- if (!bitbang) {
- ret = -ENOMEM;
- goto out;
- }
+ bitbang = devm_kzalloc(dev, sizeof(struct bb_info), GFP_KERNEL);
+ if (!bitbang)
+ return -ENOMEM;
/* bitbang init */
bitbang->addr = mdp->addr + mdp->reg_offset[PIR];
@@ -2614,44 +2617,42 @@ static int sh_mdio_init(struct net_device *ndev, int id,
/* MII controller setting */
mdp->mii_bus = alloc_mdio_bitbang(&bitbang->ctrl);
- if (!mdp->mii_bus) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!mdp->mii_bus)
+ return -ENOMEM;
/* Hook up MII support for ethtool */
mdp->mii_bus->name = "sh_mii";
- mdp->mii_bus->parent = &ndev->dev;
+ mdp->mii_bus->parent = dev;
snprintf(mdp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
- mdp->pdev->name, id);
+ pdev->name, pdev->id);
/* PHY IRQ */
- mdp->mii_bus->irq = devm_kzalloc(&ndev->dev,
- sizeof(int) * PHY_MAX_ADDR,
+ mdp->mii_bus->irq = devm_kzalloc(dev, sizeof(int) * PHY_MAX_ADDR,
GFP_KERNEL);
if (!mdp->mii_bus->irq) {
ret = -ENOMEM;
goto out_free_bus;
}
- 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;
+ /* 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;
+
+ ret = mdiobus_register(mdp->mii_bus);
+ }
- /* register mdio bus */
- ret = mdiobus_register(mdp->mii_bus);
if (ret)
goto out_free_bus;
- dev_set_drvdata(&ndev->dev, mdp->mii_bus);
-
return 0;
out_free_bus:
free_mdio_bitbang(mdp->mii_bus);
-
-out:
return ret;
}
@@ -2676,7 +2677,6 @@ static const u16 *sh_eth_get_register_offset(int register_type)
reg_offset = sh_eth_offset_fast_sh3_sh2;
break;
default:
- pr_err("Unknown register type (%d)\n", register_type);
break;
}
@@ -2710,6 +2710,48 @@ static const struct net_device_ops sh_eth_netdev_ops_tsu = {
.ndo_change_mtu = eth_change_mtu,
};
+#ifdef CONFIG_OF
+static struct sh_eth_plat_data *sh_eth_parse_dt(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct sh_eth_plat_data *pdata;
+ const char *mac_addr;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->phy_interface = of_get_phy_mode(np);
+
+ mac_addr = of_get_mac_address(np);
+ if (mac_addr)
+ memcpy(pdata->mac_addr, mac_addr, ETH_ALEN);
+
+ pdata->no_ether_link =
+ of_property_read_bool(np, "renesas,no-ether-link");
+ pdata->ether_link_active_low =
+ of_property_read_bool(np, "renesas,ether-link-active-low");
+
+ return pdata;
+}
+
+static const struct of_device_id sh_eth_match_table[] = {
+ { .compatible = "renesas,gether-r8a7740", .data = &r8a7740_data },
+ { .compatible = "renesas,ether-r8a7778", .data = &r8a777x_data },
+ { .compatible = "renesas,ether-r8a7779", .data = &r8a777x_data },
+ { .compatible = "renesas,ether-r8a7790", .data = &r8a779x_data },
+ { .compatible = "renesas,ether-r8a7791", .data = &r8a779x_data },
+ { .compatible = "renesas,ether-r7s72100", .data = &r7s72100_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sh_eth_match_table);
+#else
+static inline struct sh_eth_plat_data *sh_eth_parse_dt(struct device *dev)
+{
+ return NULL;
+}
+#endif
+
static int sh_eth_drv_probe(struct platform_device *pdev)
{
int ret, devno = 0;
@@ -2723,15 +2765,15 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(res == NULL)) {
dev_err(&pdev->dev, "invalid resource\n");
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
ndev = alloc_etherdev(sizeof(struct sh_eth_private));
- if (!ndev) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!ndev)
+ return -ENOMEM;
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
/* The sh Ether-specific entries in the device structure. */
ndev->base_addr = res->start;
@@ -2760,9 +2802,9 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
spin_lock_init(&mdp->lock);
mdp->pdev = pdev;
- pm_runtime_enable(&pdev->dev);
- pm_runtime_resume(&pdev->dev);
+ if (pdev->dev.of_node)
+ pd = sh_eth_parse_dt(&pdev->dev);
if (!pd) {
dev_err(&pdev->dev, "no platform data\n");
ret = -EINVAL;
@@ -2778,8 +2820,22 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
mdp->ether_link_active_low = pd->ether_link_active_low;
/* set cpu data */
- mdp->cd = (struct sh_eth_cpu_data *)id->driver_data;
+ if (id) {
+ mdp->cd = (struct sh_eth_cpu_data *)id->driver_data;
+ } else {
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(sh_eth_match_table),
+ &pdev->dev);
+ mdp->cd = (struct sh_eth_cpu_data *)match->data;
+ }
mdp->reg_offset = sh_eth_get_register_offset(mdp->cd->register_type);
+ if (!mdp->reg_offset) {
+ dev_err(&pdev->dev, "Unknown register type (%d)\n",
+ mdp->cd->register_type);
+ ret = -EINVAL;
+ goto out_release;
+ }
sh_eth_set_default_cpu_data(mdp->cd);
/* set function */
@@ -2825,6 +2881,13 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
}
}
+ /* MDIO bus init */
+ ret = sh_mdio_init(mdp, pd);
+ if (ret) {
+ dev_err(&ndev->dev, "failed to initialise MDIO\n");
+ goto out_release;
+ }
+
netif_napi_add(ndev, &mdp->napi, sh_eth_poll, 64);
/* network device register */
@@ -2832,31 +2895,26 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
if (ret)
goto out_napi_del;
- /* mdio bus init */
- ret = sh_mdio_init(ndev, pdev->id, pd);
- if (ret)
- goto out_unregister;
-
/* print device information */
- pr_info("Base address at 0x%x, %pM, IRQ %d.\n",
- (u32)ndev->base_addr, ndev->dev_addr, ndev->irq);
+ netdev_info(ndev, "Base address at 0x%x, %pM, IRQ %d.\n",
+ (u32)ndev->base_addr, ndev->dev_addr, ndev->irq);
+ pm_runtime_put(&pdev->dev);
platform_set_drvdata(pdev, ndev);
return ret;
-out_unregister:
- unregister_netdev(ndev);
-
out_napi_del:
netif_napi_del(&mdp->napi);
+ sh_mdio_release(mdp);
out_release:
/* net_dev free */
if (ndev)
free_netdev(ndev);
-out:
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -2865,9 +2923,9 @@ static int sh_eth_drv_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct sh_eth_private *mdp = netdev_priv(ndev);
- sh_mdio_release(ndev);
unregister_netdev(ndev);
netif_napi_del(&mdp->napi);
+ sh_mdio_release(mdp);
pm_runtime_disable(&pdev->dev);
free_netdev(ndev);
@@ -2920,6 +2978,7 @@ static struct platform_driver sh_eth_driver = {
.driver = {
.name = CARDNAME,
.pm = SH_ETH_PM_OPS,
+ .of_match_table = of_match_ptr(sh_eth_match_table),
},
};
diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index 6075915b88ec..d55e37cd5fec 100644
--- a/drivers/net/ethernet/renesas/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -27,8 +27,7 @@
#define RX_RING_MIN 64
#define TX_RING_MAX 1024
#define RX_RING_MAX 1024
-#define ETHERSMALL 60
-#define PKT_BUF_SZ 1538
+#define PKT_BUF_SZ 1538
#define SH_ETH_TSU_TIMEOUT_MS 500
#define SH_ETH_TSU_CAM_ENTRIES 32
diff --git a/drivers/net/ethernet/samsung/Kconfig b/drivers/net/ethernet/samsung/Kconfig
new file mode 100644
index 000000000000..7902341f2623
--- /dev/null
+++ b/drivers/net/ethernet/samsung/Kconfig
@@ -0,0 +1,16 @@
+#
+# Samsung Ethernet device configuration
+#
+
+config NET_VENDOR_SAMSUNG
+ bool "Samsung Ethernet device"
+ default y
+ ---help---
+ This is the driver for the SXGBE 10G Ethernet IP block found on Samsung
+ platforms.
+
+if NET_VENDOR_SAMSUNG
+
+source "drivers/net/ethernet/samsung/sxgbe/Kconfig"
+
+endif # NET_VENDOR_SAMSUNG
diff --git a/drivers/net/ethernet/samsung/Makefile b/drivers/net/ethernet/samsung/Makefile
new file mode 100644
index 000000000000..1773c29b8d76
--- /dev/null
+++ b/drivers/net/ethernet/samsung/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Samsung Ethernet device drivers.
+#
+
+obj-$(CONFIG_SXGBE_ETH) += sxgbe/
diff --git a/drivers/net/ethernet/samsung/sxgbe/Kconfig b/drivers/net/ethernet/samsung/sxgbe/Kconfig
new file mode 100644
index 000000000000..d79288c51d0a
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/Kconfig
@@ -0,0 +1,9 @@
+config SXGBE_ETH
+ tristate "Samsung 10G/2.5G/1G SXGBE Ethernet driver"
+ depends on HAS_IOMEM && HAS_DMA
+ select PHYLIB
+ select CRC32
+ select PTP_1588_CLOCK
+ ---help---
+ This is the driver for the SXGBE 10G Ethernet IP block found on Samsung
+ platforms.
diff --git a/drivers/net/ethernet/samsung/sxgbe/Makefile b/drivers/net/ethernet/samsung/sxgbe/Makefile
new file mode 100644
index 000000000000..dcc80b9d4370
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_SXGBE_ETH) += samsung-sxgbe.o
+samsung-sxgbe-objs:= sxgbe_platform.o sxgbe_main.o sxgbe_desc.o \
+ sxgbe_dma.o sxgbe_core.o sxgbe_mtl.o sxgbe_mdio.o \
+ sxgbe_ethtool.o sxgbe_xpcs.o $(samsung-sxgbe-y)
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h b/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h
new file mode 100644
index 000000000000..6203c7d8550f
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h
@@ -0,0 +1,535 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __SXGBE_COMMON_H__
+#define __SXGBE_COMMON_H__
+
+/* forward references */
+struct sxgbe_desc_ops;
+struct sxgbe_dma_ops;
+struct sxgbe_mtl_ops;
+
+#define SXGBE_RESOURCE_NAME "sam_sxgbeeth"
+#define DRV_MODULE_VERSION "November_2013"
+
+/* MAX HW feature words */
+#define SXGBE_HW_WORDS 3
+
+#define SXGBE_RX_COE_NONE 0
+
+/* CSR Frequency Access Defines*/
+#define SXGBE_CSR_F_150M 150000000
+#define SXGBE_CSR_F_250M 250000000
+#define SXGBE_CSR_F_300M 300000000
+#define SXGBE_CSR_F_350M 350000000
+#define SXGBE_CSR_F_400M 400000000
+#define SXGBE_CSR_F_500M 500000000
+
+/* pause time */
+#define SXGBE_PAUSE_TIME 0x200
+
+/* tx queues */
+#define SXGBE_TX_QUEUES 8
+#define SXGBE_RX_QUEUES 16
+
+/* Calculated based how much time does it take to fill 256KB Rx memory
+ * at 10Gb speed at 156MHz clock rate and considered little less then
+ * the actual value.
+ */
+#define SXGBE_MAX_DMA_RIWT 0x70
+#define SXGBE_MIN_DMA_RIWT 0x01
+
+/* Tx coalesce parameters */
+#define SXGBE_COAL_TX_TIMER 40000
+#define SXGBE_MAX_COAL_TX_TICK 100000
+#define SXGBE_TX_MAX_FRAMES 512
+#define SXGBE_TX_FRAMES 128
+
+/* SXGBE TX FIFO is 8K, Rx FIFO is 16K */
+#define BUF_SIZE_16KiB 16384
+#define BUF_SIZE_8KiB 8192
+#define BUF_SIZE_4KiB 4096
+#define BUF_SIZE_2KiB 2048
+
+#define SXGBE_DEFAULT_LIT_LS 0x3E8
+#define SXGBE_DEFAULT_TWT_LS 0x0
+
+/* Flow Control defines */
+#define SXGBE_FLOW_OFF 0
+#define SXGBE_FLOW_RX 1
+#define SXGBE_FLOW_TX 2
+#define SXGBE_FLOW_AUTO (SXGBE_FLOW_TX | SXGBE_FLOW_RX)
+
+#define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */
+
+/* errors */
+#define RX_GMII_ERR 0x01
+#define RX_WATCHDOG_ERR 0x02
+#define RX_CRC_ERR 0x03
+#define RX_GAINT_ERR 0x04
+#define RX_IP_HDR_ERR 0x05
+#define RX_PAYLOAD_ERR 0x06
+#define RX_OVERFLOW_ERR 0x07
+
+/* pkt type */
+#define RX_LEN_PKT 0x00
+#define RX_MACCTL_PKT 0x01
+#define RX_DCBCTL_PKT 0x02
+#define RX_ARP_PKT 0x03
+#define RX_OAM_PKT 0x04
+#define RX_UNTAG_PKT 0x05
+#define RX_OTHER_PKT 0x07
+#define RX_SVLAN_PKT 0x08
+#define RX_CVLAN_PKT 0x09
+#define RX_DVLAN_OCVLAN_ICVLAN_PKT 0x0A
+#define RX_DVLAN_OSVLAN_ISVLAN_PKT 0x0B
+#define RX_DVLAN_OSVLAN_ICVLAN_PKT 0x0C
+#define RX_DVLAN_OCVLAN_ISVLAN_PKT 0x0D
+
+#define RX_NOT_IP_PKT 0x00
+#define RX_IPV4_TCP_PKT 0x01
+#define RX_IPV4_UDP_PKT 0x02
+#define RX_IPV4_ICMP_PKT 0x03
+#define RX_IPV4_UNKNOWN_PKT 0x07
+#define RX_IPV6_TCP_PKT 0x09
+#define RX_IPV6_UDP_PKT 0x0A
+#define RX_IPV6_ICMP_PKT 0x0B
+#define RX_IPV6_UNKNOWN_PKT 0x0F
+
+#define RX_NO_PTP 0x00
+#define RX_PTP_SYNC 0x01
+#define RX_PTP_FOLLOW_UP 0x02
+#define RX_PTP_DELAY_REQ 0x03
+#define RX_PTP_DELAY_RESP 0x04
+#define RX_PTP_PDELAY_REQ 0x05
+#define RX_PTP_PDELAY_RESP 0x06
+#define RX_PTP_PDELAY_FOLLOW_UP 0x07
+#define RX_PTP_ANNOUNCE 0x08
+#define RX_PTP_MGMT 0x09
+#define RX_PTP_SIGNAL 0x0A
+#define RX_PTP_RESV_MSG 0x0F
+
+/* EEE-LPI mode flags*/
+#define TX_ENTRY_LPI_MODE 0x10
+#define TX_EXIT_LPI_MODE 0x20
+#define RX_ENTRY_LPI_MODE 0x40
+#define RX_EXIT_LPI_MODE 0x80
+
+/* EEE-LPI Interrupt status flag */
+#define LPI_INT_STATUS BIT(5)
+
+/* EEE-LPI Default timer values */
+#define LPI_LINK_STATUS_TIMER 0x3E8
+#define LPI_MAC_WAIT_TIMER 0x00
+
+/* EEE-LPI Control and status definitions */
+#define LPI_CTRL_STATUS_TXA BIT(19)
+#define LPI_CTRL_STATUS_PLSDIS BIT(18)
+#define LPI_CTRL_STATUS_PLS BIT(17)
+#define LPI_CTRL_STATUS_LPIEN BIT(16)
+#define LPI_CTRL_STATUS_TXRSTP BIT(11)
+#define LPI_CTRL_STATUS_RXRSTP BIT(10)
+#define LPI_CTRL_STATUS_RLPIST BIT(9)
+#define LPI_CTRL_STATUS_TLPIST BIT(8)
+#define LPI_CTRL_STATUS_RLPIEX BIT(3)
+#define LPI_CTRL_STATUS_RLPIEN BIT(2)
+#define LPI_CTRL_STATUS_TLPIEX BIT(1)
+#define LPI_CTRL_STATUS_TLPIEN BIT(0)
+
+enum dma_irq_status {
+ tx_hard_error = BIT(0),
+ tx_bump_tc = BIT(1),
+ handle_tx = BIT(2),
+ rx_hard_error = BIT(3),
+ rx_bump_tc = BIT(4),
+ handle_rx = BIT(5),
+};
+
+#define NETIF_F_HW_VLAN_ALL (NETIF_F_HW_VLAN_CTAG_RX | \
+ NETIF_F_HW_VLAN_STAG_RX | \
+ NETIF_F_HW_VLAN_CTAG_TX | \
+ NETIF_F_HW_VLAN_STAG_TX | \
+ NETIF_F_HW_VLAN_CTAG_FILTER | \
+ NETIF_F_HW_VLAN_STAG_FILTER)
+
+/* MMC control defines */
+#define SXGBE_MMC_CTRL_CNT_FRZ 0x00000008
+
+/* SXGBE HW ADDR regs */
+#define SXGBE_ADDR_HIGH(reg) (((reg > 15) ? 0x00000800 : 0x00000040) + \
+ (reg * 8))
+#define SXGBE_ADDR_LOW(reg) (((reg > 15) ? 0x00000804 : 0x00000044) + \
+ (reg * 8))
+#define SXGBE_MAX_PERFECT_ADDRESSES 32 /* Maximum unicast perfect filtering */
+#define SXGBE_FRAME_FILTER 0x00000004 /* Frame Filter */
+
+/* SXGBE Frame Filter defines */
+#define SXGBE_FRAME_FILTER_PR 0x00000001 /* Promiscuous Mode */
+#define SXGBE_FRAME_FILTER_HUC 0x00000002 /* Hash Unicast */
+#define SXGBE_FRAME_FILTER_HMC 0x00000004 /* Hash Multicast */
+#define SXGBE_FRAME_FILTER_DAIF 0x00000008 /* DA Inverse Filtering */
+#define SXGBE_FRAME_FILTER_PM 0x00000010 /* Pass all multicast */
+#define SXGBE_FRAME_FILTER_DBF 0x00000020 /* Disable Broadcast frames */
+#define SXGBE_FRAME_FILTER_SAIF 0x00000100 /* Inverse Filtering */
+#define SXGBE_FRAME_FILTER_SAF 0x00000200 /* Source Address Filter */
+#define SXGBE_FRAME_FILTER_HPF 0x00000400 /* Hash or perfect Filter */
+#define SXGBE_FRAME_FILTER_RA 0x80000000 /* Receive all mode */
+
+#define SXGBE_HASH_TABLE_SIZE 64
+#define SXGBE_HASH_HIGH 0x00000008 /* Multicast Hash Table High */
+#define SXGBE_HASH_LOW 0x0000000c /* Multicast Hash Table Low */
+
+#define SXGBE_HI_REG_AE 0x80000000
+
+/* Minimum and maximum MTU */
+#define MIN_MTU 68
+#define MAX_MTU 9000
+
+#define SXGBE_FOR_EACH_QUEUE(max_queues, queue_num) \
+ for (queue_num = 0; queue_num < max_queues; queue_num++)
+
+#define DRV_VERSION "1.0.0"
+
+#define SXGBE_MAX_RX_CHANNELS 16
+#define SXGBE_MAX_TX_CHANNELS 16
+
+#define START_MAC_REG_OFFSET 0x0000
+#define MAX_MAC_REG_OFFSET 0x0DFC
+#define START_MTL_REG_OFFSET 0x1000
+#define MAX_MTL_REG_OFFSET 0x18FC
+#define START_DMA_REG_OFFSET 0x3000
+#define MAX_DMA_REG_OFFSET 0x38FC
+
+#define REG_SPACE_SIZE 0x2000
+
+/* sxgbe statistics counters */
+struct sxgbe_extra_stats {
+ /* TX/RX IRQ events */
+ unsigned long tx_underflow_irq;
+ unsigned long tx_process_stopped_irq;
+ unsigned long tx_ctxt_desc_err;
+ unsigned long tx_threshold;
+ unsigned long rx_threshold;
+ unsigned long tx_pkt_n;
+ unsigned long rx_pkt_n;
+ unsigned long normal_irq_n;
+ unsigned long tx_normal_irq_n;
+ unsigned long rx_normal_irq_n;
+ unsigned long napi_poll;
+ unsigned long tx_clean;
+ unsigned long tx_reset_ic_bit;
+ unsigned long rx_process_stopped_irq;
+ unsigned long rx_underflow_irq;
+
+ /* Bus access errors */
+ unsigned long fatal_bus_error_irq;
+ unsigned long tx_read_transfer_err;
+ unsigned long tx_write_transfer_err;
+ unsigned long tx_desc_access_err;
+ unsigned long tx_buffer_access_err;
+ unsigned long tx_data_transfer_err;
+ unsigned long rx_read_transfer_err;
+ unsigned long rx_write_transfer_err;
+ unsigned long rx_desc_access_err;
+ unsigned long rx_buffer_access_err;
+ unsigned long rx_data_transfer_err;
+
+ /* EEE-LPI stats */
+ unsigned long tx_lpi_entry_n;
+ unsigned long tx_lpi_exit_n;
+ unsigned long rx_lpi_entry_n;
+ unsigned long rx_lpi_exit_n;
+ unsigned long eee_wakeup_error_n;
+
+ /* RX specific */
+ /* L2 error */
+ unsigned long rx_code_gmii_err;
+ unsigned long rx_watchdog_err;
+ unsigned long rx_crc_err;
+ unsigned long rx_gaint_pkt_err;
+ unsigned long ip_hdr_err;
+ unsigned long ip_payload_err;
+ unsigned long overflow_error;
+
+ /* L2 Pkt type */
+ unsigned long len_pkt;
+ unsigned long mac_ctl_pkt;
+ unsigned long dcb_ctl_pkt;
+ unsigned long arp_pkt;
+ unsigned long oam_pkt;
+ unsigned long untag_okt;
+ unsigned long other_pkt;
+ unsigned long svlan_tag_pkt;
+ unsigned long cvlan_tag_pkt;
+ unsigned long dvlan_ocvlan_icvlan_pkt;
+ unsigned long dvlan_osvlan_isvlan_pkt;
+ unsigned long dvlan_osvlan_icvlan_pkt;
+ unsigned long dvan_ocvlan_icvlan_pkt;
+
+ /* L3/L4 Pkt type */
+ unsigned long not_ip_pkt;
+ unsigned long ip4_tcp_pkt;
+ unsigned long ip4_udp_pkt;
+ unsigned long ip4_icmp_pkt;
+ unsigned long ip4_unknown_pkt;
+ unsigned long ip6_tcp_pkt;
+ unsigned long ip6_udp_pkt;
+ unsigned long ip6_icmp_pkt;
+ unsigned long ip6_unknown_pkt;
+
+ /* Filter specific */
+ unsigned long vlan_filter_match;
+ unsigned long sa_filter_fail;
+ unsigned long da_filter_fail;
+ unsigned long hash_filter_pass;
+ unsigned long l3_filter_match;
+ unsigned long l4_filter_match;
+
+ /* RX context specific */
+ unsigned long timestamp_dropped;
+ unsigned long rx_msg_type_no_ptp;
+ unsigned long rx_ptp_type_sync;
+ unsigned long rx_ptp_type_follow_up;
+ unsigned long rx_ptp_type_delay_req;
+ unsigned long rx_ptp_type_delay_resp;
+ unsigned long rx_ptp_type_pdelay_req;
+ unsigned long rx_ptp_type_pdelay_resp;
+ unsigned long rx_ptp_type_pdelay_follow_up;
+ unsigned long rx_ptp_announce;
+ unsigned long rx_ptp_mgmt;
+ unsigned long rx_ptp_signal;
+ unsigned long rx_ptp_resv_msg_type;
+};
+
+struct mac_link {
+ int port;
+ int duplex;
+ int speed;
+};
+
+struct mii_regs {
+ unsigned int addr; /* MII Address */
+ unsigned int data; /* MII Data */
+};
+
+struct sxgbe_core_ops {
+ /* MAC core initialization */
+ void (*core_init)(void __iomem *ioaddr);
+ /* Dump MAC registers */
+ void (*dump_regs)(void __iomem *ioaddr);
+ /* Handle extra events on specific interrupts hw dependent */
+ int (*host_irq_status)(void __iomem *ioaddr,
+ struct sxgbe_extra_stats *x);
+ /* Set power management mode (e.g. magic frame) */
+ void (*pmt)(void __iomem *ioaddr, unsigned long mode);
+ /* Set/Get Unicast MAC addresses */
+ void (*set_umac_addr)(void __iomem *ioaddr, unsigned char *addr,
+ unsigned int reg_n);
+ void (*get_umac_addr)(void __iomem *ioaddr, unsigned char *addr,
+ unsigned int reg_n);
+ void (*enable_rx)(void __iomem *ioaddr, bool enable);
+ void (*enable_tx)(void __iomem *ioaddr, bool enable);
+
+ /* controller version specific operations */
+ int (*get_controller_version)(void __iomem *ioaddr);
+
+ /* If supported then get the optional core features */
+ unsigned int (*get_hw_feature)(void __iomem *ioaddr,
+ unsigned char feature_index);
+ /* adjust SXGBE speed */
+ void (*set_speed)(void __iomem *ioaddr, unsigned char speed);
+
+ /* EEE-LPI specific operations */
+ void (*set_eee_mode)(void __iomem *ioaddr);
+ void (*reset_eee_mode)(void __iomem *ioaddr);
+ void (*set_eee_timer)(void __iomem *ioaddr, const int ls,
+ const int tw);
+ void (*set_eee_pls)(void __iomem *ioaddr, const int link);
+
+ /* Enable disable checksum offload operations */
+ void (*enable_rx_csum)(void __iomem *ioaddr);
+ void (*disable_rx_csum)(void __iomem *ioaddr);
+};
+
+const struct sxgbe_core_ops *sxgbe_get_core_ops(void);
+
+struct sxgbe_ops {
+ const struct sxgbe_core_ops *mac;
+ const struct sxgbe_desc_ops *desc;
+ const struct sxgbe_dma_ops *dma;
+ const struct sxgbe_mtl_ops *mtl;
+ struct mii_regs mii; /* MII register Addresses */
+ struct mac_link link;
+ unsigned int ctrl_uid;
+ unsigned int ctrl_id;
+};
+
+/* SXGBE private data structures */
+struct sxgbe_tx_queue {
+ unsigned int irq_no;
+ struct sxgbe_priv_data *priv_ptr;
+ struct sxgbe_tx_norm_desc *dma_tx;
+ dma_addr_t dma_tx_phy;
+ dma_addr_t *tx_skbuff_dma;
+ struct sk_buff **tx_skbuff;
+ struct timer_list txtimer;
+ spinlock_t tx_lock; /* lock for tx queues */
+ unsigned int cur_tx;
+ unsigned int dirty_tx;
+ u32 tx_count_frames;
+ u32 tx_coal_frames;
+ u32 tx_coal_timer;
+ int hwts_tx_en;
+ u16 prev_mss;
+ u8 queue_no;
+};
+
+struct sxgbe_rx_queue {
+ struct sxgbe_priv_data *priv_ptr;
+ struct sxgbe_rx_norm_desc *dma_rx;
+ struct sk_buff **rx_skbuff;
+ unsigned int cur_rx;
+ unsigned int dirty_rx;
+ unsigned int irq_no;
+ u32 rx_riwt;
+ dma_addr_t *rx_skbuff_dma;
+ dma_addr_t dma_rx_phy;
+ u8 queue_no;
+};
+
+/* SXGBE HW capabilities */
+struct sxgbe_hw_features {
+ /****** CAP [0] *******/
+ unsigned int pmt_remote_wake_up;
+ unsigned int pmt_magic_frame;
+ /* IEEE 1588-2008 */
+ unsigned int atime_stamp;
+
+ unsigned int eee;
+
+ unsigned int tx_csum_offload;
+ unsigned int rx_csum_offload;
+ unsigned int multi_macaddr;
+ unsigned int tstamp_srcselect;
+ unsigned int sa_vlan_insert;
+
+ /****** CAP [1] *******/
+ unsigned int rxfifo_size;
+ unsigned int txfifo_size;
+ unsigned int atstmap_hword;
+ unsigned int dcb_enable;
+ unsigned int splithead_enable;
+ unsigned int tcpseg_offload;
+ unsigned int debug_mem;
+ unsigned int rss_enable;
+ unsigned int hash_tsize;
+ unsigned int l3l4_filer_size;
+
+ /* This value is in bytes and
+ * as mentioned in HW features
+ * of SXGBE data book
+ */
+ unsigned int rx_mtl_qsize;
+ unsigned int tx_mtl_qsize;
+
+ /****** CAP [2] *******/
+ /* TX and RX number of channels */
+ unsigned int rx_mtl_queues;
+ unsigned int tx_mtl_queues;
+ unsigned int rx_dma_channels;
+ unsigned int tx_dma_channels;
+ unsigned int pps_output_count;
+ unsigned int aux_input_count;
+};
+
+struct sxgbe_priv_data {
+ /* DMA descriptos */
+ struct sxgbe_tx_queue *txq[SXGBE_TX_QUEUES];
+ struct sxgbe_rx_queue *rxq[SXGBE_RX_QUEUES];
+ u8 cur_rx_qnum;
+
+ unsigned int dma_tx_size;
+ unsigned int dma_rx_size;
+ unsigned int dma_buf_sz;
+ u32 rx_riwt;
+
+ struct napi_struct napi;
+
+ void __iomem *ioaddr;
+ struct net_device *dev;
+ struct device *device;
+ struct sxgbe_ops *hw; /* sxgbe specific ops */
+ int no_csum_insertion;
+ int irq;
+ int rxcsum_insertion;
+ spinlock_t stats_lock; /* lock for tx/rx statatics */
+
+ struct phy_device *phydev;
+ int oldlink;
+ int speed;
+ int oldduplex;
+ struct mii_bus *mii;
+ int mii_irq[PHY_MAX_ADDR];
+ u8 rx_pause;
+ u8 tx_pause;
+
+ struct sxgbe_extra_stats xstats;
+ struct sxgbe_plat_data *plat;
+ struct sxgbe_hw_features hw_cap;
+
+ u32 msg_enable;
+
+ struct clk *sxgbe_clk;
+ int clk_csr;
+ unsigned int mode;
+ unsigned int default_addend;
+
+ /* advanced time stamp support */
+ u32 adv_ts;
+ int use_riwt;
+ struct ptp_clock *ptp_clock;
+
+ /* tc control */
+ int tx_tc;
+ int rx_tc;
+ /* EEE-LPI specific members */
+ struct timer_list eee_ctrl_timer;
+ bool tx_path_in_lpi_mode;
+ int lpi_irq;
+ int eee_enabled;
+ int eee_active;
+ int tx_lpi_timer;
+};
+
+/* Function prototypes */
+struct sxgbe_priv_data *sxgbe_drv_probe(struct device *device,
+ struct sxgbe_plat_data *plat_dat,
+ void __iomem *addr);
+int sxgbe_drv_remove(struct net_device *ndev);
+void sxgbe_set_ethtool_ops(struct net_device *netdev);
+int sxgbe_mdio_unregister(struct net_device *ndev);
+int sxgbe_mdio_register(struct net_device *ndev);
+int sxgbe_register_platform(void);
+void sxgbe_unregister_platform(void);
+
+#ifdef CONFIG_PM
+int sxgbe_suspend(struct net_device *ndev);
+int sxgbe_resume(struct net_device *ndev);
+int sxgbe_freeze(struct net_device *ndev);
+int sxgbe_restore(struct net_device *ndev);
+#endif /* CONFIG_PM */
+
+const struct sxgbe_mtl_ops *sxgbe_get_mtl_ops(void);
+
+void sxgbe_disable_eee_mode(struct sxgbe_priv_data * const priv);
+bool sxgbe_eee_init(struct sxgbe_priv_data * const priv);
+#endif /* __SXGBE_COMMON_H__ */
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_core.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_core.c
new file mode 100644
index 000000000000..c4da7a2b002a
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_core.c
@@ -0,0 +1,262 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+
+#include "sxgbe_common.h"
+#include "sxgbe_reg.h"
+
+/* MAC core initialization */
+static void sxgbe_core_init(void __iomem *ioaddr)
+{
+ u32 regval;
+
+ /* TX configuration */
+ regval = readl(ioaddr + SXGBE_CORE_TX_CONFIG_REG);
+ /* Other configurable parameters IFP, IPG, ISR, ISM
+ * needs to be set if needed
+ */
+ regval |= SXGBE_TX_JABBER_DISABLE;
+ writel(regval, ioaddr + SXGBE_CORE_TX_CONFIG_REG);
+
+ /* RX configuration */
+ regval = readl(ioaddr + SXGBE_CORE_RX_CONFIG_REG);
+ /* Other configurable parameters CST, SPEN, USP, GPSLCE
+ * WD, LM, S2KP, HDSMS, GPSL, ELEN, ARPEN needs to be
+ * set if needed
+ */
+ regval |= SXGBE_RX_JUMBPKT_ENABLE | SXGBE_RX_ACS_ENABLE;
+ writel(regval, ioaddr + SXGBE_CORE_RX_CONFIG_REG);
+}
+
+/* Dump MAC registers */
+static void sxgbe_core_dump_regs(void __iomem *ioaddr)
+{
+}
+
+static int sxgbe_get_lpi_status(void __iomem *ioaddr, const u32 irq_status)
+{
+ int status = 0;
+ int lpi_status;
+
+ /* Reading this register shall clear all the LPI status bits */
+ lpi_status = readl(ioaddr + SXGBE_CORE_LPI_CTRL_STATUS);
+
+ if (lpi_status & LPI_CTRL_STATUS_TLPIEN)
+ status |= TX_ENTRY_LPI_MODE;
+ if (lpi_status & LPI_CTRL_STATUS_TLPIEX)
+ status |= TX_EXIT_LPI_MODE;
+ if (lpi_status & LPI_CTRL_STATUS_RLPIEN)
+ status |= RX_ENTRY_LPI_MODE;
+ if (lpi_status & LPI_CTRL_STATUS_RLPIEX)
+ status |= RX_EXIT_LPI_MODE;
+
+ return status;
+}
+
+/* Handle extra events on specific interrupts hw dependent */
+static int sxgbe_core_host_irq_status(void __iomem *ioaddr,
+ struct sxgbe_extra_stats *x)
+{
+ int irq_status, status = 0;
+
+ irq_status = readl(ioaddr + SXGBE_CORE_INT_STATUS_REG);
+
+ if (unlikely(irq_status & LPI_INT_STATUS))
+ status |= sxgbe_get_lpi_status(ioaddr, irq_status);
+
+ return status;
+}
+
+/* Set power management mode (e.g. magic frame) */
+static void sxgbe_core_pmt(void __iomem *ioaddr, unsigned long mode)
+{
+}
+
+/* Set/Get Unicast MAC addresses */
+static void sxgbe_core_set_umac_addr(void __iomem *ioaddr, unsigned char *addr,
+ unsigned int reg_n)
+{
+ u32 high_word, low_word;
+
+ high_word = (addr[5] << 8) | (addr[4]);
+ low_word = (addr[3] << 24) | (addr[2] << 16) |
+ (addr[1] << 8) | (addr[0]);
+ writel(high_word, ioaddr + SXGBE_CORE_ADD_HIGHOFFSET(reg_n));
+ writel(low_word, ioaddr + SXGBE_CORE_ADD_LOWOFFSET(reg_n));
+}
+
+static void sxgbe_core_get_umac_addr(void __iomem *ioaddr, unsigned char *addr,
+ unsigned int reg_n)
+{
+ u32 high_word, low_word;
+
+ high_word = readl(ioaddr + SXGBE_CORE_ADD_HIGHOFFSET(reg_n));
+ low_word = readl(ioaddr + SXGBE_CORE_ADD_LOWOFFSET(reg_n));
+
+ /* extract and assign address */
+ addr[5] = (high_word & 0x0000FF00) >> 8;
+ addr[4] = (high_word & 0x000000FF);
+ addr[3] = (low_word & 0xFF000000) >> 24;
+ addr[2] = (low_word & 0x00FF0000) >> 16;
+ addr[1] = (low_word & 0x0000FF00) >> 8;
+ addr[0] = (low_word & 0x000000FF);
+}
+
+static void sxgbe_enable_tx(void __iomem *ioaddr, bool enable)
+{
+ u32 tx_config;
+
+ tx_config = readl(ioaddr + SXGBE_CORE_TX_CONFIG_REG);
+ tx_config &= ~SXGBE_TX_ENABLE;
+
+ if (enable)
+ tx_config |= SXGBE_TX_ENABLE;
+ writel(tx_config, ioaddr + SXGBE_CORE_TX_CONFIG_REG);
+}
+
+static void sxgbe_enable_rx(void __iomem *ioaddr, bool enable)
+{
+ u32 rx_config;
+
+ rx_config = readl(ioaddr + SXGBE_CORE_RX_CONFIG_REG);
+ rx_config &= ~SXGBE_RX_ENABLE;
+
+ if (enable)
+ rx_config |= SXGBE_RX_ENABLE;
+ writel(rx_config, ioaddr + SXGBE_CORE_RX_CONFIG_REG);
+}
+
+static int sxgbe_get_controller_version(void __iomem *ioaddr)
+{
+ return readl(ioaddr + SXGBE_CORE_VERSION_REG);
+}
+
+/* If supported then get the optional core features */
+static unsigned int sxgbe_get_hw_feature(void __iomem *ioaddr,
+ unsigned char feature_index)
+{
+ return readl(ioaddr + (SXGBE_CORE_HW_FEA_REG(feature_index)));
+}
+
+static void sxgbe_core_set_speed(void __iomem *ioaddr, unsigned char speed)
+{
+ u32 tx_cfg = readl(ioaddr + SXGBE_CORE_TX_CONFIG_REG);
+
+ /* clear the speed bits */
+ tx_cfg &= ~0x60000000;
+ tx_cfg |= (speed << SXGBE_SPEED_LSHIFT);
+
+ /* set the speed */
+ writel(tx_cfg, ioaddr + SXGBE_CORE_TX_CONFIG_REG);
+}
+
+static void sxgbe_set_eee_mode(void __iomem *ioaddr)
+{
+ u32 ctrl;
+
+ /* Enable the LPI mode for transmit path with Tx automate bit set.
+ * When Tx Automate bit is set, MAC internally handles the entry
+ * to LPI mode after all outstanding and pending packets are
+ * transmitted.
+ */
+ ctrl = readl(ioaddr + SXGBE_CORE_LPI_CTRL_STATUS);
+ ctrl |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_TXA;
+ writel(ctrl, ioaddr + SXGBE_CORE_LPI_CTRL_STATUS);
+}
+
+static void sxgbe_reset_eee_mode(void __iomem *ioaddr)
+{
+ u32 ctrl;
+
+ ctrl = readl(ioaddr + SXGBE_CORE_LPI_CTRL_STATUS);
+ ctrl &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_TXA);
+ writel(ctrl, ioaddr + SXGBE_CORE_LPI_CTRL_STATUS);
+}
+
+static void sxgbe_set_eee_pls(void __iomem *ioaddr, const int link)
+{
+ u32 ctrl;
+
+ ctrl = readl(ioaddr + SXGBE_CORE_LPI_CTRL_STATUS);
+
+ /* If the PHY link status is UP then set PLS */
+ if (link)
+ ctrl |= LPI_CTRL_STATUS_PLS;
+ else
+ ctrl &= ~LPI_CTRL_STATUS_PLS;
+
+ writel(ctrl, ioaddr + SXGBE_CORE_LPI_CTRL_STATUS);
+}
+
+static void sxgbe_set_eee_timer(void __iomem *ioaddr,
+ const int ls, const int tw)
+{
+ int value = ((tw & 0xffff)) | ((ls & 0x7ff) << 16);
+
+ /* Program the timers in the LPI timer control register:
+ * LS: minimum time (ms) for which the link
+ * status from PHY should be ok before transmitting
+ * the LPI pattern.
+ * TW: minimum time (us) for which the core waits
+ * after it has stopped transmitting the LPI pattern.
+ */
+ writel(value, ioaddr + SXGBE_CORE_LPI_TIMER_CTRL);
+}
+
+static void sxgbe_enable_rx_csum(void __iomem *ioaddr)
+{
+ u32 ctrl;
+
+ ctrl = readl(ioaddr + SXGBE_CORE_RX_CONFIG_REG);
+ ctrl |= SXGBE_RX_CSUMOFFLOAD_ENABLE;
+ writel(ctrl, ioaddr + SXGBE_CORE_RX_CONFIG_REG);
+}
+
+static void sxgbe_disable_rx_csum(void __iomem *ioaddr)
+{
+ u32 ctrl;
+
+ ctrl = readl(ioaddr + SXGBE_CORE_RX_CONFIG_REG);
+ ctrl &= ~SXGBE_RX_CSUMOFFLOAD_ENABLE;
+ writel(ctrl, ioaddr + SXGBE_CORE_RX_CONFIG_REG);
+}
+
+static const struct sxgbe_core_ops core_ops = {
+ .core_init = sxgbe_core_init,
+ .dump_regs = sxgbe_core_dump_regs,
+ .host_irq_status = sxgbe_core_host_irq_status,
+ .pmt = sxgbe_core_pmt,
+ .set_umac_addr = sxgbe_core_set_umac_addr,
+ .get_umac_addr = sxgbe_core_get_umac_addr,
+ .enable_rx = sxgbe_enable_rx,
+ .enable_tx = sxgbe_enable_tx,
+ .get_controller_version = sxgbe_get_controller_version,
+ .get_hw_feature = sxgbe_get_hw_feature,
+ .set_speed = sxgbe_core_set_speed,
+ .set_eee_mode = sxgbe_set_eee_mode,
+ .reset_eee_mode = sxgbe_reset_eee_mode,
+ .set_eee_timer = sxgbe_set_eee_timer,
+ .set_eee_pls = sxgbe_set_eee_pls,
+ .enable_rx_csum = sxgbe_enable_rx_csum,
+ .disable_rx_csum = sxgbe_disable_rx_csum,
+};
+
+const struct sxgbe_core_ops *sxgbe_get_core_ops(void)
+{
+ return &core_ops;
+}
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_desc.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_desc.c
new file mode 100644
index 000000000000..e896dbbd2e15
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_desc.c
@@ -0,0 +1,515 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+
+#include "sxgbe_common.h"
+#include "sxgbe_dma.h"
+#include "sxgbe_desc.h"
+
+/* DMA TX descriptor ring initialization */
+static void sxgbe_init_tx_desc(struct sxgbe_tx_norm_desc *p)
+{
+ p->tdes23.tx_rd_des23.own_bit = 0;
+}
+
+static void sxgbe_tx_desc_enable_tse(struct sxgbe_tx_norm_desc *p, u8 is_tse,
+ u32 total_hdr_len, u32 tcp_hdr_len,
+ u32 tcp_payload_len)
+{
+ p->tdes23.tx_rd_des23.tse_bit = is_tse;
+ p->tdes23.tx_rd_des23.buf1_size = total_hdr_len;
+ p->tdes23.tx_rd_des23.tcp_hdr_len = tcp_hdr_len / 4;
+ p->tdes23.tx_rd_des23.tx_pkt_len.tcp_payload_len = tcp_payload_len;
+}
+
+/* Assign buffer lengths for descriptor */
+static void sxgbe_prepare_tx_desc(struct sxgbe_tx_norm_desc *p, u8 is_fd,
+ int buf1_len, int pkt_len, int cksum)
+{
+ p->tdes23.tx_rd_des23.first_desc = is_fd;
+ p->tdes23.tx_rd_des23.buf1_size = buf1_len;
+
+ p->tdes23.tx_rd_des23.tx_pkt_len.cksum_pktlen.total_pkt_len = pkt_len;
+
+ if (cksum)
+ p->tdes23.tx_rd_des23.tx_pkt_len.cksum_pktlen.cksum_ctl = cic_full;
+}
+
+/* Set VLAN control information */
+static void sxgbe_tx_vlanctl_desc(struct sxgbe_tx_norm_desc *p, int vlan_ctl)
+{
+ p->tdes23.tx_rd_des23.vlan_tag_ctl = vlan_ctl;
+}
+
+/* Set the owner of Normal descriptor */
+static void sxgbe_set_tx_owner(struct sxgbe_tx_norm_desc *p)
+{
+ p->tdes23.tx_rd_des23.own_bit = 1;
+}
+
+/* Get the owner of Normal descriptor */
+static int sxgbe_get_tx_owner(struct sxgbe_tx_norm_desc *p)
+{
+ return p->tdes23.tx_rd_des23.own_bit;
+}
+
+/* Invoked by the xmit function to close the tx descriptor */
+static void sxgbe_close_tx_desc(struct sxgbe_tx_norm_desc *p)
+{
+ p->tdes23.tx_rd_des23.last_desc = 1;
+ p->tdes23.tx_rd_des23.int_on_com = 1;
+}
+
+/* Clean the tx descriptor as soon as the tx irq is received */
+static void sxgbe_release_tx_desc(struct sxgbe_tx_norm_desc *p)
+{
+ memset(p, 0, sizeof(*p));
+}
+
+/* Clear interrupt on tx frame completion. When this bit is
+ * set an interrupt happens as soon as the frame is transmitted
+ */
+static void sxgbe_clear_tx_ic(struct sxgbe_tx_norm_desc *p)
+{
+ p->tdes23.tx_rd_des23.int_on_com = 0;
+}
+
+/* Last tx segment reports the transmit status */
+static int sxgbe_get_tx_ls(struct sxgbe_tx_norm_desc *p)
+{
+ return p->tdes23.tx_rd_des23.last_desc;
+}
+
+/* Get the buffer size from the descriptor */
+static int sxgbe_get_tx_len(struct sxgbe_tx_norm_desc *p)
+{
+ return p->tdes23.tx_rd_des23.buf1_size;
+}
+
+/* Set tx timestamp enable bit */
+static void sxgbe_tx_enable_tstamp(struct sxgbe_tx_norm_desc *p)
+{
+ p->tdes23.tx_rd_des23.timestmp_enable = 1;
+}
+
+/* get tx timestamp status */
+static int sxgbe_get_tx_timestamp_status(struct sxgbe_tx_norm_desc *p)
+{
+ return p->tdes23.tx_rd_des23.timestmp_enable;
+}
+
+/* TX Context Descripto Specific */
+static void sxgbe_tx_ctxt_desc_set_ctxt(struct sxgbe_tx_ctxt_desc *p)
+{
+ p->ctxt_bit = 1;
+}
+
+/* Set the owner of TX context descriptor */
+static void sxgbe_tx_ctxt_desc_set_owner(struct sxgbe_tx_ctxt_desc *p)
+{
+ p->own_bit = 1;
+}
+
+/* Get the owner of TX context descriptor */
+static int sxgbe_tx_ctxt_desc_get_owner(struct sxgbe_tx_ctxt_desc *p)
+{
+ return p->own_bit;
+}
+
+/* Set TX mss in TX context Descriptor */
+static void sxgbe_tx_ctxt_desc_set_mss(struct sxgbe_tx_ctxt_desc *p, u16 mss)
+{
+ p->maxseg_size = mss;
+}
+
+/* Get TX mss from TX context Descriptor */
+static int sxgbe_tx_ctxt_desc_get_mss(struct sxgbe_tx_ctxt_desc *p)
+{
+ return p->maxseg_size;
+}
+
+/* Set TX tcmssv in TX context Descriptor */
+static void sxgbe_tx_ctxt_desc_set_tcmssv(struct sxgbe_tx_ctxt_desc *p)
+{
+ p->tcmssv = 1;
+}
+
+/* Reset TX ostc in TX context Descriptor */
+static void sxgbe_tx_ctxt_desc_reset_ostc(struct sxgbe_tx_ctxt_desc *p)
+{
+ p->ostc = 0;
+}
+
+/* Set IVLAN information */
+static void sxgbe_tx_ctxt_desc_set_ivlantag(struct sxgbe_tx_ctxt_desc *p,
+ int is_ivlanvalid, int ivlan_tag,
+ int ivlan_ctl)
+{
+ if (is_ivlanvalid) {
+ p->ivlan_tag_valid = is_ivlanvalid;
+ p->ivlan_tag = ivlan_tag;
+ p->ivlan_tag_ctl = ivlan_ctl;
+ }
+}
+
+/* Return IVLAN Tag */
+static int sxgbe_tx_ctxt_desc_get_ivlantag(struct sxgbe_tx_ctxt_desc *p)
+{
+ return p->ivlan_tag;
+}
+
+/* Set VLAN Tag */
+static void sxgbe_tx_ctxt_desc_set_vlantag(struct sxgbe_tx_ctxt_desc *p,
+ int is_vlanvalid, int vlan_tag)
+{
+ if (is_vlanvalid) {
+ p->vltag_valid = is_vlanvalid;
+ p->vlan_tag = vlan_tag;
+ }
+}
+
+/* Return VLAN Tag */
+static int sxgbe_tx_ctxt_desc_get_vlantag(struct sxgbe_tx_ctxt_desc *p)
+{
+ return p->vlan_tag;
+}
+
+/* Set Time stamp */
+static void sxgbe_tx_ctxt_desc_set_tstamp(struct sxgbe_tx_ctxt_desc *p,
+ u8 ostc_enable, u64 tstamp)
+{
+ if (ostc_enable) {
+ p->ostc = ostc_enable;
+ p->tstamp_lo = (u32) tstamp;
+ p->tstamp_hi = (u32) (tstamp>>32);
+ }
+}
+/* Close TX context descriptor */
+static void sxgbe_tx_ctxt_desc_close(struct sxgbe_tx_ctxt_desc *p)
+{
+ p->own_bit = 1;
+}
+
+/* WB status of context descriptor */
+static int sxgbe_tx_ctxt_desc_get_cde(struct sxgbe_tx_ctxt_desc *p)
+{
+ return p->ctxt_desc_err;
+}
+
+/* DMA RX descriptor ring initialization */
+static void sxgbe_init_rx_desc(struct sxgbe_rx_norm_desc *p, int disable_rx_ic,
+ int mode, int end)
+{
+ p->rdes23.rx_rd_des23.own_bit = 1;
+ if (disable_rx_ic)
+ p->rdes23.rx_rd_des23.int_on_com = disable_rx_ic;
+}
+
+/* Get RX own bit */
+static int sxgbe_get_rx_owner(struct sxgbe_rx_norm_desc *p)
+{
+ return p->rdes23.rx_rd_des23.own_bit;
+}
+
+/* Set RX own bit */
+static void sxgbe_set_rx_owner(struct sxgbe_rx_norm_desc *p)
+{
+ p->rdes23.rx_rd_des23.own_bit = 1;
+}
+
+/* Get the receive frame size */
+static int sxgbe_get_rx_frame_len(struct sxgbe_rx_norm_desc *p)
+{
+ return p->rdes23.rx_wb_des23.pkt_len;
+}
+
+/* Return first Descriptor status */
+static int sxgbe_get_rx_fd_status(struct sxgbe_rx_norm_desc *p)
+{
+ return p->rdes23.rx_wb_des23.first_desc;
+}
+
+/* Return Last Descriptor status */
+static int sxgbe_get_rx_ld_status(struct sxgbe_rx_norm_desc *p)
+{
+ return p->rdes23.rx_wb_des23.last_desc;
+}
+
+
+/* Return the RX status looking at the WB fields */
+static int sxgbe_rx_wbstatus(struct sxgbe_rx_norm_desc *p,
+ struct sxgbe_extra_stats *x, int *checksum)
+{
+ int status = 0;
+
+ *checksum = CHECKSUM_UNNECESSARY;
+ if (p->rdes23.rx_wb_des23.err_summary) {
+ switch (p->rdes23.rx_wb_des23.err_l2_type) {
+ case RX_GMII_ERR:
+ status = -EINVAL;
+ x->rx_code_gmii_err++;
+ break;
+ case RX_WATCHDOG_ERR:
+ status = -EINVAL;
+ x->rx_watchdog_err++;
+ break;
+ case RX_CRC_ERR:
+ status = -EINVAL;
+ x->rx_crc_err++;
+ break;
+ case RX_GAINT_ERR:
+ status = -EINVAL;
+ x->rx_gaint_pkt_err++;
+ break;
+ case RX_IP_HDR_ERR:
+ *checksum = CHECKSUM_NONE;
+ x->ip_hdr_err++;
+ break;
+ case RX_PAYLOAD_ERR:
+ *checksum = CHECKSUM_NONE;
+ x->ip_payload_err++;
+ break;
+ case RX_OVERFLOW_ERR:
+ status = -EINVAL;
+ x->overflow_error++;
+ break;
+ default:
+ pr_err("Invalid Error type\n");
+ break;
+ }
+ } else {
+ switch (p->rdes23.rx_wb_des23.err_l2_type) {
+ case RX_LEN_PKT:
+ x->len_pkt++;
+ break;
+ case RX_MACCTL_PKT:
+ x->mac_ctl_pkt++;
+ break;
+ case RX_DCBCTL_PKT:
+ x->dcb_ctl_pkt++;
+ break;
+ case RX_ARP_PKT:
+ x->arp_pkt++;
+ break;
+ case RX_OAM_PKT:
+ x->oam_pkt++;
+ break;
+ case RX_UNTAG_PKT:
+ x->untag_okt++;
+ break;
+ case RX_OTHER_PKT:
+ x->other_pkt++;
+ break;
+ case RX_SVLAN_PKT:
+ x->svlan_tag_pkt++;
+ break;
+ case RX_CVLAN_PKT:
+ x->cvlan_tag_pkt++;
+ break;
+ case RX_DVLAN_OCVLAN_ICVLAN_PKT:
+ x->dvlan_ocvlan_icvlan_pkt++;
+ break;
+ case RX_DVLAN_OSVLAN_ISVLAN_PKT:
+ x->dvlan_osvlan_isvlan_pkt++;
+ break;
+ case RX_DVLAN_OSVLAN_ICVLAN_PKT:
+ x->dvlan_osvlan_icvlan_pkt++;
+ break;
+ case RX_DVLAN_OCVLAN_ISVLAN_PKT:
+ x->dvlan_ocvlan_icvlan_pkt++;
+ break;
+ default:
+ pr_err("Invalid L2 Packet type\n");
+ break;
+ }
+ }
+
+ /* L3/L4 Pkt type */
+ switch (p->rdes23.rx_wb_des23.layer34_pkt_type) {
+ case RX_NOT_IP_PKT:
+ x->not_ip_pkt++;
+ break;
+ case RX_IPV4_TCP_PKT:
+ x->ip4_tcp_pkt++;
+ break;
+ case RX_IPV4_UDP_PKT:
+ x->ip4_udp_pkt++;
+ break;
+ case RX_IPV4_ICMP_PKT:
+ x->ip4_icmp_pkt++;
+ break;
+ case RX_IPV4_UNKNOWN_PKT:
+ x->ip4_unknown_pkt++;
+ break;
+ case RX_IPV6_TCP_PKT:
+ x->ip6_tcp_pkt++;
+ break;
+ case RX_IPV6_UDP_PKT:
+ x->ip6_udp_pkt++;
+ break;
+ case RX_IPV6_ICMP_PKT:
+ x->ip6_icmp_pkt++;
+ break;
+ case RX_IPV6_UNKNOWN_PKT:
+ x->ip6_unknown_pkt++;
+ break;
+ default:
+ pr_err("Invalid L3/L4 Packet type\n");
+ break;
+ }
+
+ /* Filter */
+ if (p->rdes23.rx_wb_des23.vlan_filter_match)
+ x->vlan_filter_match++;
+
+ if (p->rdes23.rx_wb_des23.sa_filter_fail) {
+ status = -EINVAL;
+ x->sa_filter_fail++;
+ }
+ if (p->rdes23.rx_wb_des23.da_filter_fail) {
+ status = -EINVAL;
+ x->da_filter_fail++;
+ }
+ if (p->rdes23.rx_wb_des23.hash_filter_pass)
+ x->hash_filter_pass++;
+
+ if (p->rdes23.rx_wb_des23.l3_filter_match)
+ x->l3_filter_match++;
+
+ if (p->rdes23.rx_wb_des23.l4_filter_match)
+ x->l4_filter_match++;
+
+ return status;
+}
+
+/* Get own bit of context descriptor */
+static int sxgbe_get_rx_ctxt_owner(struct sxgbe_rx_ctxt_desc *p)
+{
+ return p->own_bit;
+}
+
+/* Set own bit for context descriptor */
+static void sxgbe_set_ctxt_rx_owner(struct sxgbe_rx_ctxt_desc *p)
+{
+ p->own_bit = 1;
+}
+
+
+/* Return the reception status looking at Context control information */
+static void sxgbe_rx_ctxt_wbstatus(struct sxgbe_rx_ctxt_desc *p,
+ struct sxgbe_extra_stats *x)
+{
+ if (p->tstamp_dropped)
+ x->timestamp_dropped++;
+
+ /* ptp */
+ if (p->ptp_msgtype == RX_NO_PTP)
+ x->rx_msg_type_no_ptp++;
+ else if (p->ptp_msgtype == RX_PTP_SYNC)
+ x->rx_ptp_type_sync++;
+ else if (p->ptp_msgtype == RX_PTP_FOLLOW_UP)
+ x->rx_ptp_type_follow_up++;
+ else if (p->ptp_msgtype == RX_PTP_DELAY_REQ)
+ x->rx_ptp_type_delay_req++;
+ else if (p->ptp_msgtype == RX_PTP_DELAY_RESP)
+ x->rx_ptp_type_delay_resp++;
+ else if (p->ptp_msgtype == RX_PTP_PDELAY_REQ)
+ x->rx_ptp_type_pdelay_req++;
+ else if (p->ptp_msgtype == RX_PTP_PDELAY_RESP)
+ x->rx_ptp_type_pdelay_resp++;
+ else if (p->ptp_msgtype == RX_PTP_PDELAY_FOLLOW_UP)
+ x->rx_ptp_type_pdelay_follow_up++;
+ else if (p->ptp_msgtype == RX_PTP_ANNOUNCE)
+ x->rx_ptp_announce++;
+ else if (p->ptp_msgtype == RX_PTP_MGMT)
+ x->rx_ptp_mgmt++;
+ else if (p->ptp_msgtype == RX_PTP_SIGNAL)
+ x->rx_ptp_signal++;
+ else if (p->ptp_msgtype == RX_PTP_RESV_MSG)
+ x->rx_ptp_resv_msg_type++;
+}
+
+/* Get rx timestamp status */
+static int sxgbe_get_rx_ctxt_tstamp_status(struct sxgbe_rx_ctxt_desc *p)
+{
+ if ((p->tstamp_hi == 0xffffffff) && (p->tstamp_lo == 0xffffffff)) {
+ pr_err("Time stamp corrupted\n");
+ return 0;
+ }
+
+ return p->tstamp_available;
+}
+
+
+static u64 sxgbe_get_rx_timestamp(struct sxgbe_rx_ctxt_desc *p)
+{
+ u64 ns;
+
+ ns = p->tstamp_lo;
+ ns |= ((u64)p->tstamp_hi) << 32;
+
+ return ns;
+}
+
+static const struct sxgbe_desc_ops desc_ops = {
+ .init_tx_desc = sxgbe_init_tx_desc,
+ .tx_desc_enable_tse = sxgbe_tx_desc_enable_tse,
+ .prepare_tx_desc = sxgbe_prepare_tx_desc,
+ .tx_vlanctl_desc = sxgbe_tx_vlanctl_desc,
+ .set_tx_owner = sxgbe_set_tx_owner,
+ .get_tx_owner = sxgbe_get_tx_owner,
+ .close_tx_desc = sxgbe_close_tx_desc,
+ .release_tx_desc = sxgbe_release_tx_desc,
+ .clear_tx_ic = sxgbe_clear_tx_ic,
+ .get_tx_ls = sxgbe_get_tx_ls,
+ .get_tx_len = sxgbe_get_tx_len,
+ .tx_enable_tstamp = sxgbe_tx_enable_tstamp,
+ .get_tx_timestamp_status = sxgbe_get_tx_timestamp_status,
+ .tx_ctxt_desc_set_ctxt = sxgbe_tx_ctxt_desc_set_ctxt,
+ .tx_ctxt_desc_set_owner = sxgbe_tx_ctxt_desc_set_owner,
+ .get_tx_ctxt_owner = sxgbe_tx_ctxt_desc_get_owner,
+ .tx_ctxt_desc_set_mss = sxgbe_tx_ctxt_desc_set_mss,
+ .tx_ctxt_desc_get_mss = sxgbe_tx_ctxt_desc_get_mss,
+ .tx_ctxt_desc_set_tcmssv = sxgbe_tx_ctxt_desc_set_tcmssv,
+ .tx_ctxt_desc_reset_ostc = sxgbe_tx_ctxt_desc_reset_ostc,
+ .tx_ctxt_desc_set_ivlantag = sxgbe_tx_ctxt_desc_set_ivlantag,
+ .tx_ctxt_desc_get_ivlantag = sxgbe_tx_ctxt_desc_get_ivlantag,
+ .tx_ctxt_desc_set_vlantag = sxgbe_tx_ctxt_desc_set_vlantag,
+ .tx_ctxt_desc_get_vlantag = sxgbe_tx_ctxt_desc_get_vlantag,
+ .tx_ctxt_set_tstamp = sxgbe_tx_ctxt_desc_set_tstamp,
+ .close_tx_ctxt_desc = sxgbe_tx_ctxt_desc_close,
+ .get_tx_ctxt_cde = sxgbe_tx_ctxt_desc_get_cde,
+ .init_rx_desc = sxgbe_init_rx_desc,
+ .get_rx_owner = sxgbe_get_rx_owner,
+ .set_rx_owner = sxgbe_set_rx_owner,
+ .get_rx_frame_len = sxgbe_get_rx_frame_len,
+ .get_rx_fd_status = sxgbe_get_rx_fd_status,
+ .get_rx_ld_status = sxgbe_get_rx_ld_status,
+ .rx_wbstatus = sxgbe_rx_wbstatus,
+ .get_rx_ctxt_owner = sxgbe_get_rx_ctxt_owner,
+ .set_rx_ctxt_owner = sxgbe_set_ctxt_rx_owner,
+ .rx_ctxt_wbstatus = sxgbe_rx_ctxt_wbstatus,
+ .get_rx_ctxt_tstamp_status = sxgbe_get_rx_ctxt_tstamp_status,
+ .get_timestamp = sxgbe_get_rx_timestamp,
+};
+
+const struct sxgbe_desc_ops *sxgbe_get_desc_ops(void)
+{
+ return &desc_ops;
+}
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_desc.h b/drivers/net/ethernet/samsung/sxgbe/sxgbe_desc.h
new file mode 100644
index 000000000000..838cb9fb0ea9
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_desc.h
@@ -0,0 +1,298 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __SXGBE_DESC_H__
+#define __SXGBE_DESC_H__
+
+#define SXGBE_DESC_SIZE_BYTES 16
+
+/* forward declaration */
+struct sxgbe_extra_stats;
+
+/* Transmit checksum insertion control */
+enum tdes_csum_insertion {
+ cic_disabled = 0, /* Checksum Insertion Control */
+ cic_only_ip = 1, /* Only IP header */
+ /* IP header but pseudoheader is not calculated */
+ cic_no_pseudoheader = 2,
+ cic_full = 3, /* IP header and pseudoheader */
+};
+
+struct sxgbe_tx_norm_desc {
+ u64 tdes01; /* buf1 address */
+ union {
+ /* TX Read-Format Desc 2,3 */
+ struct {
+ /* TDES2 */
+ u32 buf1_size:14;
+ u32 vlan_tag_ctl:2;
+ u32 buf2_size:14;
+ u32 timestmp_enable:1;
+ u32 int_on_com:1;
+ /* TDES3 */
+ union {
+ u32 tcp_payload_len:18;
+ struct {
+ u32 total_pkt_len:15;
+ u32 reserved1:1;
+ u32 cksum_ctl:2;
+ } cksum_pktlen;
+ } tx_pkt_len;
+
+ u32 tse_bit:1;
+ u32 tcp_hdr_len:4;
+ u32 sa_insert_ctl:3;
+ u32 crc_pad_ctl:2;
+ u32 last_desc:1;
+ u32 first_desc:1;
+ u32 ctxt_bit:1;
+ u32 own_bit:1;
+ } tx_rd_des23;
+
+ /* tx write back Desc 2,3 */
+ struct {
+ /* WB TES2 */
+ u32 reserved1;
+ /* WB TES3 */
+ u32 reserved2:31;
+ u32 own_bit:1;
+ } tx_wb_des23;
+ } tdes23;
+};
+
+struct sxgbe_rx_norm_desc {
+ union {
+ u32 rdes0; /* buf1 address */
+ struct {
+ u32 out_vlan_tag:16;
+ u32 in_vlan_tag:16;
+ } wb_rx_des0;
+ } rd_wb_des0;
+
+ union {
+ u32 rdes1; /* buf2 address or buf1[63:32] */
+ u32 rss_hash; /* Write-back RX */
+ } rd_wb_des1;
+
+ union {
+ /* RX Read format Desc 2,3 */
+ struct{
+ /* RDES2 */
+ u32 buf2_addr;
+ /* RDES3 */
+ u32 buf2_hi_addr:30;
+ u32 int_on_com:1;
+ u32 own_bit:1;
+ } rx_rd_des23;
+
+ /* RX write back */
+ struct{
+ /* WB RDES2 */
+ u32 hdr_len:10;
+ u32 rdes2_reserved:2;
+ u32 elrd_val:1;
+ u32 iovt_sel:1;
+ u32 res_pkt:1;
+ u32 vlan_filter_match:1;
+ u32 sa_filter_fail:1;
+ u32 da_filter_fail:1;
+ u32 hash_filter_pass:1;
+ u32 macaddr_filter_match:8;
+ u32 l3_filter_match:1;
+ u32 l4_filter_match:1;
+ u32 l34_filter_num:3;
+
+ /* WB RDES3 */
+ u32 pkt_len:14;
+ u32 rdes3_reserved:1;
+ u32 err_summary:1;
+ u32 err_l2_type:4;
+ u32 layer34_pkt_type:4;
+ u32 no_coagulation_pkt:1;
+ u32 in_seq_pkt:1;
+ u32 rss_valid:1;
+ u32 context_des_avail:1;
+ u32 last_desc:1;
+ u32 first_desc:1;
+ u32 recv_context_desc:1;
+ u32 own_bit:1;
+ } rx_wb_des23;
+ } rdes23;
+};
+
+/* Context descriptor structure */
+struct sxgbe_tx_ctxt_desc {
+ u32 tstamp_lo;
+ u32 tstamp_hi;
+ u32 maxseg_size:15;
+ u32 reserved1:1;
+ u32 ivlan_tag:16;
+ u32 vlan_tag:16;
+ u32 vltag_valid:1;
+ u32 ivlan_tag_valid:1;
+ u32 ivlan_tag_ctl:2;
+ u32 reserved2:3;
+ u32 ctxt_desc_err:1;
+ u32 reserved3:2;
+ u32 ostc:1;
+ u32 tcmssv:1;
+ u32 reserved4:2;
+ u32 ctxt_bit:1;
+ u32 own_bit:1;
+};
+
+struct sxgbe_rx_ctxt_desc {
+ u32 tstamp_lo;
+ u32 tstamp_hi;
+ u32 reserved1;
+ u32 ptp_msgtype:4;
+ u32 tstamp_available:1;
+ u32 ptp_rsp_err:1;
+ u32 tstamp_dropped:1;
+ u32 reserved2:23;
+ u32 rx_ctxt_desc:1;
+ u32 own_bit:1;
+};
+
+struct sxgbe_desc_ops {
+ /* DMA TX descriptor ring initialization */
+ void (*init_tx_desc)(struct sxgbe_tx_norm_desc *p);
+
+ /* Invoked by the xmit function to prepare the tx descriptor */
+ void (*tx_desc_enable_tse)(struct sxgbe_tx_norm_desc *p, u8 is_tse,
+ u32 total_hdr_len, u32 tcp_hdr_len,
+ u32 tcp_payload_len);
+
+ /* Assign buffer lengths for descriptor */
+ void (*prepare_tx_desc)(struct sxgbe_tx_norm_desc *p, u8 is_fd,
+ int buf1_len, int pkt_len, int cksum);
+
+ /* Set VLAN control information */
+ void (*tx_vlanctl_desc)(struct sxgbe_tx_norm_desc *p, int vlan_ctl);
+
+ /* Set the owner of the descriptor */
+ void (*set_tx_owner)(struct sxgbe_tx_norm_desc *p);
+
+ /* Get the owner of the descriptor */
+ int (*get_tx_owner)(struct sxgbe_tx_norm_desc *p);
+
+ /* Invoked by the xmit function to close the tx descriptor */
+ void (*close_tx_desc)(struct sxgbe_tx_norm_desc *p);
+
+ /* Clean the tx descriptor as soon as the tx irq is received */
+ void (*release_tx_desc)(struct sxgbe_tx_norm_desc *p);
+
+ /* Clear interrupt on tx frame completion. When this bit is
+ * set an interrupt happens as soon as the frame is transmitted
+ */
+ void (*clear_tx_ic)(struct sxgbe_tx_norm_desc *p);
+
+ /* Last tx segment reports the transmit status */
+ int (*get_tx_ls)(struct sxgbe_tx_norm_desc *p);
+
+ /* Get the buffer size from the descriptor */
+ int (*get_tx_len)(struct sxgbe_tx_norm_desc *p);
+
+ /* Set tx timestamp enable bit */
+ void (*tx_enable_tstamp)(struct sxgbe_tx_norm_desc *p);
+
+ /* get tx timestamp status */
+ int (*get_tx_timestamp_status)(struct sxgbe_tx_norm_desc *p);
+
+ /* TX Context Descripto Specific */
+ void (*tx_ctxt_desc_set_ctxt)(struct sxgbe_tx_ctxt_desc *p);
+
+ /* Set the owner of the TX context descriptor */
+ void (*tx_ctxt_desc_set_owner)(struct sxgbe_tx_ctxt_desc *p);
+
+ /* Get the owner of the TX context descriptor */
+ int (*get_tx_ctxt_owner)(struct sxgbe_tx_ctxt_desc *p);
+
+ /* Set TX mss */
+ void (*tx_ctxt_desc_set_mss)(struct sxgbe_tx_ctxt_desc *p, u16 mss);
+
+ /* Set TX mss */
+ int (*tx_ctxt_desc_get_mss)(struct sxgbe_tx_ctxt_desc *p);
+
+ /* Set TX tcmssv */
+ void (*tx_ctxt_desc_set_tcmssv)(struct sxgbe_tx_ctxt_desc *p);
+
+ /* Reset TX ostc */
+ void (*tx_ctxt_desc_reset_ostc)(struct sxgbe_tx_ctxt_desc *p);
+
+ /* Set IVLAN information */
+ void (*tx_ctxt_desc_set_ivlantag)(struct sxgbe_tx_ctxt_desc *p,
+ int is_ivlanvalid, int ivlan_tag,
+ int ivlan_ctl);
+
+ /* Return IVLAN Tag */
+ int (*tx_ctxt_desc_get_ivlantag)(struct sxgbe_tx_ctxt_desc *p);
+
+ /* Set VLAN Tag */
+ void (*tx_ctxt_desc_set_vlantag)(struct sxgbe_tx_ctxt_desc *p,
+ int is_vlanvalid, int vlan_tag);
+
+ /* Return VLAN Tag */
+ int (*tx_ctxt_desc_get_vlantag)(struct sxgbe_tx_ctxt_desc *p);
+
+ /* Set Time stamp */
+ void (*tx_ctxt_set_tstamp)(struct sxgbe_tx_ctxt_desc *p,
+ u8 ostc_enable, u64 tstamp);
+
+ /* Close TX context descriptor */
+ void (*close_tx_ctxt_desc)(struct sxgbe_tx_ctxt_desc *p);
+
+ /* WB status of context descriptor */
+ int (*get_tx_ctxt_cde)(struct sxgbe_tx_ctxt_desc *p);
+
+ /* DMA RX descriptor ring initialization */
+ void (*init_rx_desc)(struct sxgbe_rx_norm_desc *p, int disable_rx_ic,
+ int mode, int end);
+
+ /* Get own bit */
+ int (*get_rx_owner)(struct sxgbe_rx_norm_desc *p);
+
+ /* Set own bit */
+ void (*set_rx_owner)(struct sxgbe_rx_norm_desc *p);
+
+ /* Get the receive frame size */
+ int (*get_rx_frame_len)(struct sxgbe_rx_norm_desc *p);
+
+ /* Return first Descriptor status */
+ int (*get_rx_fd_status)(struct sxgbe_rx_norm_desc *p);
+
+ /* Return first Descriptor status */
+ int (*get_rx_ld_status)(struct sxgbe_rx_norm_desc *p);
+
+ /* Return the reception status looking at the RDES1 */
+ int (*rx_wbstatus)(struct sxgbe_rx_norm_desc *p,
+ struct sxgbe_extra_stats *x, int *checksum);
+
+ /* Get own bit */
+ int (*get_rx_ctxt_owner)(struct sxgbe_rx_ctxt_desc *p);
+
+ /* Set own bit */
+ void (*set_rx_ctxt_owner)(struct sxgbe_rx_ctxt_desc *p);
+
+ /* Return the reception status looking at Context control information */
+ void (*rx_ctxt_wbstatus)(struct sxgbe_rx_ctxt_desc *p,
+ struct sxgbe_extra_stats *x);
+
+ /* Get rx timestamp status */
+ int (*get_rx_ctxt_tstamp_status)(struct sxgbe_rx_ctxt_desc *p);
+
+ /* Get timestamp value for rx, need to check this */
+ u64 (*get_timestamp)(struct sxgbe_rx_ctxt_desc *p);
+};
+
+const struct sxgbe_desc_ops *sxgbe_get_desc_ops(void);
+
+#endif /* __SXGBE_DESC_H__ */
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_dma.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_dma.c
new file mode 100644
index 000000000000..28f89c41d0cd
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_dma.c
@@ -0,0 +1,382 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+
+#include "sxgbe_common.h"
+#include "sxgbe_dma.h"
+#include "sxgbe_reg.h"
+#include "sxgbe_desc.h"
+
+/* DMA core initialization */
+static int sxgbe_dma_init(void __iomem *ioaddr, int fix_burst, int burst_map)
+{
+ int retry_count = 10;
+ u32 reg_val;
+
+ /* reset the DMA */
+ writel(SXGBE_DMA_SOFT_RESET, ioaddr + SXGBE_DMA_MODE_REG);
+ while (retry_count--) {
+ if (!(readl(ioaddr + SXGBE_DMA_MODE_REG) &
+ SXGBE_DMA_SOFT_RESET))
+ break;
+ mdelay(10);
+ }
+
+ if (retry_count < 0)
+ return -EBUSY;
+
+ reg_val = readl(ioaddr + SXGBE_DMA_SYSBUS_MODE_REG);
+
+ /* if fix_burst = 0, Set UNDEF = 1 of DMA_Sys_Mode Register.
+ * if fix_burst = 1, Set UNDEF = 0 of DMA_Sys_Mode Register.
+ * burst_map is bitmap for BLEN[4, 8, 16, 32, 64, 128 and 256].
+ * Set burst_map irrespective of fix_burst value.
+ */
+ if (!fix_burst)
+ reg_val |= SXGBE_DMA_AXI_UNDEF_BURST;
+
+ /* write burst len map */
+ reg_val |= (burst_map << SXGBE_DMA_BLENMAP_LSHIFT);
+
+ writel(reg_val, ioaddr + SXGBE_DMA_SYSBUS_MODE_REG);
+
+ return 0;
+}
+
+static void sxgbe_dma_channel_init(void __iomem *ioaddr, int cha_num,
+ int fix_burst, int pbl, dma_addr_t dma_tx,
+ dma_addr_t dma_rx, int t_rsize, int r_rsize)
+{
+ u32 reg_val;
+ dma_addr_t dma_addr;
+
+ reg_val = readl(ioaddr + SXGBE_DMA_CHA_CTL_REG(cha_num));
+ /* set the pbl */
+ if (fix_burst) {
+ reg_val |= SXGBE_DMA_PBL_X8MODE;
+ writel(reg_val, ioaddr + SXGBE_DMA_CHA_CTL_REG(cha_num));
+ /* program the TX pbl */
+ reg_val = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cha_num));
+ reg_val |= (pbl << SXGBE_DMA_TXPBL_LSHIFT);
+ writel(reg_val, ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cha_num));
+ /* program the RX pbl */
+ reg_val = readl(ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cha_num));
+ reg_val |= (pbl << SXGBE_DMA_RXPBL_LSHIFT);
+ writel(reg_val, ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cha_num));
+ }
+
+ /* program desc registers */
+ writel(upper_32_bits(dma_tx),
+ ioaddr + SXGBE_DMA_CHA_TXDESC_HADD_REG(cha_num));
+ writel(lower_32_bits(dma_tx),
+ ioaddr + SXGBE_DMA_CHA_TXDESC_LADD_REG(cha_num));
+
+ writel(upper_32_bits(dma_rx),
+ ioaddr + SXGBE_DMA_CHA_RXDESC_HADD_REG(cha_num));
+ writel(lower_32_bits(dma_rx),
+ ioaddr + SXGBE_DMA_CHA_RXDESC_LADD_REG(cha_num));
+
+ /* program tail pointers */
+ /* assumption: upper 32 bits are constant and
+ * same as TX/RX desc list
+ */
+ dma_addr = dma_tx + ((t_rsize - 1) * SXGBE_DESC_SIZE_BYTES);
+ writel(lower_32_bits(dma_addr),
+ ioaddr + SXGBE_DMA_CHA_TXDESC_TAILPTR_REG(cha_num));
+
+ dma_addr = dma_rx + ((r_rsize - 1) * SXGBE_DESC_SIZE_BYTES);
+ writel(lower_32_bits(dma_addr),
+ ioaddr + SXGBE_DMA_CHA_RXDESC_LADD_REG(cha_num));
+ /* program the ring sizes */
+ writel(t_rsize - 1, ioaddr + SXGBE_DMA_CHA_TXDESC_RINGLEN_REG(cha_num));
+ writel(r_rsize - 1, ioaddr + SXGBE_DMA_CHA_RXDESC_RINGLEN_REG(cha_num));
+
+ /* Enable TX/RX interrupts */
+ writel(SXGBE_DMA_ENA_INT,
+ ioaddr + SXGBE_DMA_CHA_INT_ENABLE_REG(cha_num));
+}
+
+static void sxgbe_enable_dma_transmission(void __iomem *ioaddr, int cha_num)
+{
+ u32 tx_config;
+
+ tx_config = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cha_num));
+ tx_config |= SXGBE_TX_START_DMA;
+ writel(tx_config, ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cha_num));
+}
+
+static void sxgbe_enable_dma_irq(void __iomem *ioaddr, int dma_cnum)
+{
+ /* Enable TX/RX interrupts */
+ writel(SXGBE_DMA_ENA_INT,
+ ioaddr + SXGBE_DMA_CHA_INT_ENABLE_REG(dma_cnum));
+}
+
+static void sxgbe_disable_dma_irq(void __iomem *ioaddr, int dma_cnum)
+{
+ /* Disable TX/RX interrupts */
+ writel(0, ioaddr + SXGBE_DMA_CHA_INT_ENABLE_REG(dma_cnum));
+}
+
+static void sxgbe_dma_start_tx(void __iomem *ioaddr, int tchannels)
+{
+ int cnum;
+ u32 tx_ctl_reg;
+
+ for (cnum = 0; cnum < tchannels; cnum++) {
+ tx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cnum));
+ tx_ctl_reg |= SXGBE_TX_ENABLE;
+ writel(tx_ctl_reg,
+ ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cnum));
+ }
+}
+
+static void sxgbe_dma_start_tx_queue(void __iomem *ioaddr, int dma_cnum)
+{
+ u32 tx_ctl_reg;
+
+ tx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(dma_cnum));
+ tx_ctl_reg |= SXGBE_TX_ENABLE;
+ writel(tx_ctl_reg, ioaddr + SXGBE_DMA_CHA_TXCTL_REG(dma_cnum));
+}
+
+static void sxgbe_dma_stop_tx_queue(void __iomem *ioaddr, int dma_cnum)
+{
+ u32 tx_ctl_reg;
+
+ tx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(dma_cnum));
+ tx_ctl_reg &= ~(SXGBE_TX_ENABLE);
+ writel(tx_ctl_reg, ioaddr + SXGBE_DMA_CHA_TXCTL_REG(dma_cnum));
+}
+
+static void sxgbe_dma_stop_tx(void __iomem *ioaddr, int tchannels)
+{
+ int cnum;
+ u32 tx_ctl_reg;
+
+ for (cnum = 0; cnum < tchannels; cnum++) {
+ tx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cnum));
+ tx_ctl_reg &= ~(SXGBE_TX_ENABLE);
+ writel(tx_ctl_reg, ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cnum));
+ }
+}
+
+static void sxgbe_dma_start_rx(void __iomem *ioaddr, int rchannels)
+{
+ int cnum;
+ u32 rx_ctl_reg;
+
+ for (cnum = 0; cnum < rchannels; cnum++) {
+ rx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cnum));
+ rx_ctl_reg |= SXGBE_RX_ENABLE;
+ writel(rx_ctl_reg,
+ ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cnum));
+ }
+}
+
+static void sxgbe_dma_stop_rx(void __iomem *ioaddr, int rchannels)
+{
+ int cnum;
+ u32 rx_ctl_reg;
+
+ for (cnum = 0; cnum < rchannels; cnum++) {
+ rx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cnum));
+ rx_ctl_reg &= ~(SXGBE_RX_ENABLE);
+ writel(rx_ctl_reg, ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cnum));
+ }
+}
+
+static int sxgbe_tx_dma_int_status(void __iomem *ioaddr, int channel_no,
+ struct sxgbe_extra_stats *x)
+{
+ u32 int_status = readl(ioaddr + SXGBE_DMA_CHA_STATUS_REG(channel_no));
+ u32 clear_val = 0;
+ u32 ret_val = 0;
+
+ /* TX Normal Interrupt Summary */
+ if (likely(int_status & SXGBE_DMA_INT_STATUS_NIS)) {
+ x->normal_irq_n++;
+ if (int_status & SXGBE_DMA_INT_STATUS_TI) {
+ ret_val |= handle_tx;
+ x->tx_normal_irq_n++;
+ clear_val |= SXGBE_DMA_INT_STATUS_TI;
+ }
+
+ if (int_status & SXGBE_DMA_INT_STATUS_TBU) {
+ x->tx_underflow_irq++;
+ ret_val |= tx_bump_tc;
+ clear_val |= SXGBE_DMA_INT_STATUS_TBU;
+ }
+ } else if (unlikely(int_status & SXGBE_DMA_INT_STATUS_AIS)) {
+ /* TX Abnormal Interrupt Summary */
+ if (int_status & SXGBE_DMA_INT_STATUS_TPS) {
+ ret_val |= tx_hard_error;
+ clear_val |= SXGBE_DMA_INT_STATUS_TPS;
+ x->tx_process_stopped_irq++;
+ }
+
+ if (int_status & SXGBE_DMA_INT_STATUS_FBE) {
+ ret_val |= tx_hard_error;
+ x->fatal_bus_error_irq++;
+
+ /* Assumption: FBE bit is the combination of
+ * all the bus access erros and cleared when
+ * the respective error bits cleared
+ */
+
+ /* check for actual cause */
+ if (int_status & SXGBE_DMA_INT_STATUS_TEB0) {
+ x->tx_read_transfer_err++;
+ clear_val |= SXGBE_DMA_INT_STATUS_TEB0;
+ } else {
+ x->tx_write_transfer_err++;
+ }
+
+ if (int_status & SXGBE_DMA_INT_STATUS_TEB1) {
+ x->tx_desc_access_err++;
+ clear_val |= SXGBE_DMA_INT_STATUS_TEB1;
+ } else {
+ x->tx_buffer_access_err++;
+ }
+
+ if (int_status & SXGBE_DMA_INT_STATUS_TEB2) {
+ x->tx_data_transfer_err++;
+ clear_val |= SXGBE_DMA_INT_STATUS_TEB2;
+ }
+ }
+
+ /* context descriptor error */
+ if (int_status & SXGBE_DMA_INT_STATUS_CTXTERR) {
+ x->tx_ctxt_desc_err++;
+ clear_val |= SXGBE_DMA_INT_STATUS_CTXTERR;
+ }
+ }
+
+ /* clear the served bits */
+ writel(clear_val, ioaddr + SXGBE_DMA_CHA_STATUS_REG(channel_no));
+
+ return ret_val;
+}
+
+static int sxgbe_rx_dma_int_status(void __iomem *ioaddr, int channel_no,
+ struct sxgbe_extra_stats *x)
+{
+ u32 int_status = readl(ioaddr + SXGBE_DMA_CHA_STATUS_REG(channel_no));
+ u32 clear_val = 0;
+ u32 ret_val = 0;
+
+ /* RX Normal Interrupt Summary */
+ if (likely(int_status & SXGBE_DMA_INT_STATUS_NIS)) {
+ x->normal_irq_n++;
+ if (int_status & SXGBE_DMA_INT_STATUS_RI) {
+ ret_val |= handle_rx;
+ x->rx_normal_irq_n++;
+ clear_val |= SXGBE_DMA_INT_STATUS_RI;
+ }
+ } else if (unlikely(int_status & SXGBE_DMA_INT_STATUS_AIS)) {
+ /* RX Abnormal Interrupt Summary */
+ if (int_status & SXGBE_DMA_INT_STATUS_RBU) {
+ ret_val |= rx_bump_tc;
+ clear_val |= SXGBE_DMA_INT_STATUS_RBU;
+ x->rx_underflow_irq++;
+ }
+
+ if (int_status & SXGBE_DMA_INT_STATUS_RPS) {
+ ret_val |= rx_hard_error;
+ clear_val |= SXGBE_DMA_INT_STATUS_RPS;
+ x->rx_process_stopped_irq++;
+ }
+
+ if (int_status & SXGBE_DMA_INT_STATUS_FBE) {
+ ret_val |= rx_hard_error;
+ x->fatal_bus_error_irq++;
+
+ /* Assumption: FBE bit is the combination of
+ * all the bus access erros and cleared when
+ * the respective error bits cleared
+ */
+
+ /* check for actual cause */
+ if (int_status & SXGBE_DMA_INT_STATUS_REB0) {
+ x->rx_read_transfer_err++;
+ clear_val |= SXGBE_DMA_INT_STATUS_REB0;
+ } else {
+ x->rx_write_transfer_err++;
+ }
+
+ if (int_status & SXGBE_DMA_INT_STATUS_REB1) {
+ x->rx_desc_access_err++;
+ clear_val |= SXGBE_DMA_INT_STATUS_REB1;
+ } else {
+ x->rx_buffer_access_err++;
+ }
+
+ if (int_status & SXGBE_DMA_INT_STATUS_REB2) {
+ x->rx_data_transfer_err++;
+ clear_val |= SXGBE_DMA_INT_STATUS_REB2;
+ }
+ }
+ }
+
+ /* clear the served bits */
+ writel(clear_val, ioaddr + SXGBE_DMA_CHA_STATUS_REG(channel_no));
+
+ return ret_val;
+}
+
+/* Program the HW RX Watchdog */
+static void sxgbe_dma_rx_watchdog(void __iomem *ioaddr, u32 riwt)
+{
+ u32 que_num;
+
+ SXGBE_FOR_EACH_QUEUE(SXGBE_RX_QUEUES, que_num) {
+ writel(riwt,
+ ioaddr + SXGBE_DMA_CHA_INT_RXWATCHTMR_REG(que_num));
+ }
+}
+
+static void sxgbe_enable_tso(void __iomem *ioaddr, u8 chan_num)
+{
+ u32 ctrl;
+
+ ctrl = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(chan_num));
+ ctrl |= SXGBE_DMA_CHA_TXCTL_TSE_ENABLE;
+ writel(ctrl, ioaddr + SXGBE_DMA_CHA_TXCTL_REG(chan_num));
+}
+
+static const struct sxgbe_dma_ops sxgbe_dma_ops = {
+ .init = sxgbe_dma_init,
+ .cha_init = sxgbe_dma_channel_init,
+ .enable_dma_transmission = sxgbe_enable_dma_transmission,
+ .enable_dma_irq = sxgbe_enable_dma_irq,
+ .disable_dma_irq = sxgbe_disable_dma_irq,
+ .start_tx = sxgbe_dma_start_tx,
+ .start_tx_queue = sxgbe_dma_start_tx_queue,
+ .stop_tx = sxgbe_dma_stop_tx,
+ .stop_tx_queue = sxgbe_dma_stop_tx_queue,
+ .start_rx = sxgbe_dma_start_rx,
+ .stop_rx = sxgbe_dma_stop_rx,
+ .tx_dma_int_status = sxgbe_tx_dma_int_status,
+ .rx_dma_int_status = sxgbe_rx_dma_int_status,
+ .rx_watchdog = sxgbe_dma_rx_watchdog,
+ .enable_tso = sxgbe_enable_tso,
+};
+
+const struct sxgbe_dma_ops *sxgbe_get_dma_ops(void)
+{
+ return &sxgbe_dma_ops;
+}
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_dma.h b/drivers/net/ethernet/samsung/sxgbe/sxgbe_dma.h
new file mode 100644
index 000000000000..1607b54c9bb0
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_dma.h
@@ -0,0 +1,50 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __SXGBE_DMA_H__
+#define __SXGBE_DMA_H__
+
+/* forward declaration */
+struct sxgbe_extra_stats;
+
+#define SXGBE_DMA_BLENMAP_LSHIFT 1
+#define SXGBE_DMA_TXPBL_LSHIFT 16
+#define SXGBE_DMA_RXPBL_LSHIFT 16
+#define DEFAULT_DMA_PBL 8
+
+struct sxgbe_dma_ops {
+ /* DMA core initialization */
+ int (*init)(void __iomem *ioaddr, int fix_burst, int burst_map);
+ void (*cha_init)(void __iomem *ioaddr, int cha_num, int fix_burst,
+ int pbl, dma_addr_t dma_tx, dma_addr_t dma_rx,
+ int t_rzie, int r_rsize);
+ void (*enable_dma_transmission)(void __iomem *ioaddr, int dma_cnum);
+ void (*enable_dma_irq)(void __iomem *ioaddr, int dma_cnum);
+ void (*disable_dma_irq)(void __iomem *ioaddr, int dma_cnum);
+ void (*start_tx)(void __iomem *ioaddr, int tchannels);
+ void (*start_tx_queue)(void __iomem *ioaddr, int dma_cnum);
+ void (*stop_tx)(void __iomem *ioaddr, int tchannels);
+ void (*stop_tx_queue)(void __iomem *ioaddr, int dma_cnum);
+ void (*start_rx)(void __iomem *ioaddr, int rchannels);
+ void (*stop_rx)(void __iomem *ioaddr, int rchannels);
+ int (*tx_dma_int_status)(void __iomem *ioaddr, int channel_no,
+ struct sxgbe_extra_stats *x);
+ int (*rx_dma_int_status)(void __iomem *ioaddr, int channel_no,
+ struct sxgbe_extra_stats *x);
+ /* Program the HW RX Watchdog */
+ void (*rx_watchdog)(void __iomem *ioaddr, u32 riwt);
+ /* Enable TSO for each DMA channel */
+ void (*enable_tso)(void __iomem *ioaddr, u8 chan_num);
+};
+
+const struct sxgbe_dma_ops *sxgbe_get_dma_ops(void);
+
+#endif /* __SXGBE_CORE_H__ */
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c
new file mode 100644
index 000000000000..0415fa50eeb7
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c
@@ -0,0 +1,524 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/net_tstamp.h>
+#include <linux/phy.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include "sxgbe_common.h"
+#include "sxgbe_reg.h"
+#include "sxgbe_dma.h"
+
+struct sxgbe_stats {
+ char stat_string[ETH_GSTRING_LEN];
+ int sizeof_stat;
+ int stat_offset;
+};
+
+#define SXGBE_STAT(m) \
+{ \
+ #m, \
+ FIELD_SIZEOF(struct sxgbe_extra_stats, m), \
+ offsetof(struct sxgbe_priv_data, xstats.m) \
+}
+
+static const struct sxgbe_stats sxgbe_gstrings_stats[] = {
+ /* TX/RX IRQ events */
+ SXGBE_STAT(tx_process_stopped_irq),
+ SXGBE_STAT(tx_ctxt_desc_err),
+ SXGBE_STAT(tx_threshold),
+ SXGBE_STAT(rx_threshold),
+ SXGBE_STAT(tx_pkt_n),
+ SXGBE_STAT(rx_pkt_n),
+ SXGBE_STAT(normal_irq_n),
+ SXGBE_STAT(tx_normal_irq_n),
+ SXGBE_STAT(rx_normal_irq_n),
+ SXGBE_STAT(napi_poll),
+ SXGBE_STAT(tx_clean),
+ SXGBE_STAT(tx_reset_ic_bit),
+ SXGBE_STAT(rx_process_stopped_irq),
+ SXGBE_STAT(rx_underflow_irq),
+
+ /* Bus access errors */
+ SXGBE_STAT(fatal_bus_error_irq),
+ SXGBE_STAT(tx_read_transfer_err),
+ SXGBE_STAT(tx_write_transfer_err),
+ SXGBE_STAT(tx_desc_access_err),
+ SXGBE_STAT(tx_buffer_access_err),
+ SXGBE_STAT(tx_data_transfer_err),
+ SXGBE_STAT(rx_read_transfer_err),
+ SXGBE_STAT(rx_write_transfer_err),
+ SXGBE_STAT(rx_desc_access_err),
+ SXGBE_STAT(rx_buffer_access_err),
+ SXGBE_STAT(rx_data_transfer_err),
+
+ /* EEE-LPI stats */
+ SXGBE_STAT(tx_lpi_entry_n),
+ SXGBE_STAT(tx_lpi_exit_n),
+ SXGBE_STAT(rx_lpi_entry_n),
+ SXGBE_STAT(rx_lpi_exit_n),
+ SXGBE_STAT(eee_wakeup_error_n),
+
+ /* RX specific */
+ /* L2 error */
+ SXGBE_STAT(rx_code_gmii_err),
+ SXGBE_STAT(rx_watchdog_err),
+ SXGBE_STAT(rx_crc_err),
+ SXGBE_STAT(rx_gaint_pkt_err),
+ SXGBE_STAT(ip_hdr_err),
+ SXGBE_STAT(ip_payload_err),
+ SXGBE_STAT(overflow_error),
+
+ /* L2 Pkt type */
+ SXGBE_STAT(len_pkt),
+ SXGBE_STAT(mac_ctl_pkt),
+ SXGBE_STAT(dcb_ctl_pkt),
+ SXGBE_STAT(arp_pkt),
+ SXGBE_STAT(oam_pkt),
+ SXGBE_STAT(untag_okt),
+ SXGBE_STAT(other_pkt),
+ SXGBE_STAT(svlan_tag_pkt),
+ SXGBE_STAT(cvlan_tag_pkt),
+ SXGBE_STAT(dvlan_ocvlan_icvlan_pkt),
+ SXGBE_STAT(dvlan_osvlan_isvlan_pkt),
+ SXGBE_STAT(dvlan_osvlan_icvlan_pkt),
+ SXGBE_STAT(dvan_ocvlan_icvlan_pkt),
+
+ /* L3/L4 Pkt type */
+ SXGBE_STAT(not_ip_pkt),
+ SXGBE_STAT(ip4_tcp_pkt),
+ SXGBE_STAT(ip4_udp_pkt),
+ SXGBE_STAT(ip4_icmp_pkt),
+ SXGBE_STAT(ip4_unknown_pkt),
+ SXGBE_STAT(ip6_tcp_pkt),
+ SXGBE_STAT(ip6_udp_pkt),
+ SXGBE_STAT(ip6_icmp_pkt),
+ SXGBE_STAT(ip6_unknown_pkt),
+
+ /* Filter specific */
+ SXGBE_STAT(vlan_filter_match),
+ SXGBE_STAT(sa_filter_fail),
+ SXGBE_STAT(da_filter_fail),
+ SXGBE_STAT(hash_filter_pass),
+ SXGBE_STAT(l3_filter_match),
+ SXGBE_STAT(l4_filter_match),
+
+ /* RX context specific */
+ SXGBE_STAT(timestamp_dropped),
+ SXGBE_STAT(rx_msg_type_no_ptp),
+ SXGBE_STAT(rx_ptp_type_sync),
+ SXGBE_STAT(rx_ptp_type_follow_up),
+ SXGBE_STAT(rx_ptp_type_delay_req),
+ SXGBE_STAT(rx_ptp_type_delay_resp),
+ SXGBE_STAT(rx_ptp_type_pdelay_req),
+ SXGBE_STAT(rx_ptp_type_pdelay_resp),
+ SXGBE_STAT(rx_ptp_type_pdelay_follow_up),
+ SXGBE_STAT(rx_ptp_announce),
+ SXGBE_STAT(rx_ptp_mgmt),
+ SXGBE_STAT(rx_ptp_signal),
+ SXGBE_STAT(rx_ptp_resv_msg_type),
+};
+#define SXGBE_STATS_LEN ARRAY_SIZE(sxgbe_gstrings_stats)
+
+static int sxgbe_get_eee(struct net_device *dev,
+ struct ethtool_eee *edata)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+
+ if (!priv->hw_cap.eee)
+ return -EOPNOTSUPP;
+
+ edata->eee_enabled = priv->eee_enabled;
+ edata->eee_active = priv->eee_active;
+ edata->tx_lpi_timer = priv->tx_lpi_timer;
+
+ return phy_ethtool_get_eee(priv->phydev, edata);
+}
+
+static int sxgbe_set_eee(struct net_device *dev,
+ struct ethtool_eee *edata)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+
+ priv->eee_enabled = edata->eee_enabled;
+
+ if (!priv->eee_enabled) {
+ sxgbe_disable_eee_mode(priv);
+ } else {
+ /* We are asking for enabling the EEE but it is safe
+ * to verify all by invoking the eee_init function.
+ * In case of failure it will return an error.
+ */
+ priv->eee_enabled = sxgbe_eee_init(priv);
+ if (!priv->eee_enabled)
+ return -EOPNOTSUPP;
+
+ /* Do not change tx_lpi_timer in case of failure */
+ priv->tx_lpi_timer = edata->tx_lpi_timer;
+ }
+
+ return phy_ethtool_set_eee(priv->phydev, edata);
+}
+
+static void sxgbe_getdrvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+}
+
+static int sxgbe_getsettings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+
+ if (priv->phydev)
+ return phy_ethtool_gset(priv->phydev, cmd);
+
+ return -EOPNOTSUPP;
+}
+
+static int sxgbe_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+
+ if (priv->phydev)
+ return phy_ethtool_sset(priv->phydev, cmd);
+
+ return -EOPNOTSUPP;
+}
+
+static u32 sxgbe_getmsglevel(struct net_device *dev)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ return priv->msg_enable;
+}
+
+static void sxgbe_setmsglevel(struct net_device *dev, u32 level)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ priv->msg_enable = level;
+}
+
+static void sxgbe_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ int i;
+ u8 *p = data;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < SXGBE_STATS_LEN; i++) {
+ memcpy(p, sxgbe_gstrings_stats[i].stat_string,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+static int sxgbe_get_sset_count(struct net_device *netdev, int sset)
+{
+ int len;
+
+ switch (sset) {
+ case ETH_SS_STATS:
+ len = SXGBE_STATS_LEN;
+ return len;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void sxgbe_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *dummy, u64 *data)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ int i;
+ char *p;
+
+ if (priv->eee_enabled) {
+ int val = phy_get_eee_err(priv->phydev);
+
+ if (val)
+ priv->xstats.eee_wakeup_error_n = val;
+ }
+
+ for (i = 0; i < SXGBE_STATS_LEN; i++) {
+ p = (char *)priv + sxgbe_gstrings_stats[i].stat_offset;
+ data[i] = (sxgbe_gstrings_stats[i].sizeof_stat == sizeof(u64))
+ ? (*(u64 *)p) : (*(u32 *)p);
+ }
+}
+
+static void sxgbe_get_channels(struct net_device *dev,
+ struct ethtool_channels *channel)
+{
+ channel->max_rx = SXGBE_MAX_RX_CHANNELS;
+ channel->max_tx = SXGBE_MAX_TX_CHANNELS;
+ channel->rx_count = SXGBE_RX_QUEUES;
+ channel->tx_count = SXGBE_TX_QUEUES;
+}
+
+static u32 sxgbe_riwt2usec(u32 riwt, struct sxgbe_priv_data *priv)
+{
+ unsigned long clk = clk_get_rate(priv->sxgbe_clk);
+
+ if (!clk)
+ return 0;
+
+ return (riwt * 256) / (clk / 1000000);
+}
+
+static u32 sxgbe_usec2riwt(u32 usec, struct sxgbe_priv_data *priv)
+{
+ unsigned long clk = clk_get_rate(priv->sxgbe_clk);
+
+ if (!clk)
+ return 0;
+
+ return (usec * (clk / 1000000)) / 256;
+}
+
+static int sxgbe_get_coalesce(struct net_device *dev,
+ struct ethtool_coalesce *ec)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+
+ if (priv->use_riwt)
+ ec->rx_coalesce_usecs = sxgbe_riwt2usec(priv->rx_riwt, priv);
+
+ return 0;
+}
+
+static int sxgbe_set_coalesce(struct net_device *dev,
+ struct ethtool_coalesce *ec)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ unsigned int rx_riwt;
+
+ if (!ec->rx_coalesce_usecs)
+ return -EINVAL;
+
+ rx_riwt = sxgbe_usec2riwt(ec->rx_coalesce_usecs, priv);
+
+ if ((rx_riwt > SXGBE_MAX_DMA_RIWT) || (rx_riwt < SXGBE_MIN_DMA_RIWT))
+ return -EINVAL;
+ else if (!priv->use_riwt)
+ return -EOPNOTSUPP;
+
+ priv->rx_riwt = rx_riwt;
+ priv->hw->dma->rx_watchdog(priv->ioaddr, priv->rx_riwt);
+
+ return 0;
+}
+
+static int sxgbe_get_rss_hash_opts(struct sxgbe_priv_data *priv,
+ struct ethtool_rxnfc *cmd)
+{
+ cmd->data = 0;
+
+ /* Report default options for RSS on sxgbe */
+ switch (cmd->flow_type) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ case SCTP_V4_FLOW:
+ case AH_ESP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ case IPV4_FLOW:
+ cmd->data |= RXH_IP_SRC | RXH_IP_DST;
+ break;
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW:
+ cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ case SCTP_V6_FLOW:
+ case AH_ESP_V6_FLOW:
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ case IPV6_FLOW:
+ cmd->data |= RXH_IP_SRC | RXH_IP_DST;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sxgbe_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
+ u32 *rule_locs)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ int ret = -EOPNOTSUPP;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_GRXFH:
+ ret = sxgbe_get_rss_hash_opts(priv, cmd);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int sxgbe_set_rss_hash_opt(struct sxgbe_priv_data *priv,
+ struct ethtool_rxnfc *cmd)
+{
+ u32 reg_val = 0;
+
+ /* RSS does not support anything other than hashing
+ * to queues on src and dst IPs and ports
+ */
+ if (cmd->data & ~(RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3))
+ return -EINVAL;
+
+ switch (cmd->flow_type) {
+ case TCP_V4_FLOW:
+ case TCP_V6_FLOW:
+ if (!(cmd->data & RXH_IP_SRC) ||
+ !(cmd->data & RXH_IP_DST) ||
+ !(cmd->data & RXH_L4_B_0_1) ||
+ !(cmd->data & RXH_L4_B_2_3))
+ return -EINVAL;
+ reg_val = SXGBE_CORE_RSS_CTL_TCP4TE;
+ break;
+ case UDP_V4_FLOW:
+ case UDP_V6_FLOW:
+ if (!(cmd->data & RXH_IP_SRC) ||
+ !(cmd->data & RXH_IP_DST) ||
+ !(cmd->data & RXH_L4_B_0_1) ||
+ !(cmd->data & RXH_L4_B_2_3))
+ return -EINVAL;
+ reg_val = SXGBE_CORE_RSS_CTL_UDP4TE;
+ break;
+ case SCTP_V4_FLOW:
+ case AH_ESP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ case AH_ESP_V6_FLOW:
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ case SCTP_V6_FLOW:
+ case IPV4_FLOW:
+ case IPV6_FLOW:
+ if (!(cmd->data & RXH_IP_SRC) ||
+ !(cmd->data & RXH_IP_DST) ||
+ (cmd->data & RXH_L4_B_0_1) ||
+ (cmd->data & RXH_L4_B_2_3))
+ return -EINVAL;
+ reg_val = SXGBE_CORE_RSS_CTL_IP2TE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Read SXGBE RSS control register and update */
+ reg_val |= readl(priv->ioaddr + SXGBE_CORE_RSS_CTL_REG);
+ writel(reg_val, priv->ioaddr + SXGBE_CORE_RSS_CTL_REG);
+ readl(priv->ioaddr + SXGBE_CORE_RSS_CTL_REG);
+
+ return 0;
+}
+
+static int sxgbe_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ int ret = -EOPNOTSUPP;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_SRXFH:
+ ret = sxgbe_set_rss_hash_opt(priv, cmd);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void sxgbe_get_regs(struct net_device *dev,
+ struct ethtool_regs *regs, void *space)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ u32 *reg_space = (u32 *)space;
+ int reg_offset;
+ int reg_ix = 0;
+ void __iomem *ioaddr = priv->ioaddr;
+
+ memset(reg_space, 0x0, REG_SPACE_SIZE);
+
+ /* MAC registers */
+ for (reg_offset = START_MAC_REG_OFFSET;
+ reg_offset <= MAX_MAC_REG_OFFSET; reg_offset += 4) {
+ reg_space[reg_ix] = readl(ioaddr + reg_offset);
+ reg_ix++;
+ }
+
+ /* MTL registers */
+ for (reg_offset = START_MTL_REG_OFFSET;
+ reg_offset <= MAX_MTL_REG_OFFSET; reg_offset += 4) {
+ reg_space[reg_ix] = readl(ioaddr + reg_offset);
+ reg_ix++;
+ }
+
+ /* DMA registers */
+ for (reg_offset = START_DMA_REG_OFFSET;
+ reg_offset <= MAX_DMA_REG_OFFSET; reg_offset += 4) {
+ reg_space[reg_ix] = readl(ioaddr + reg_offset);
+ reg_ix++;
+ }
+
+ BUG_ON(reg_ix * 4 > REG_SPACE_SIZE);
+}
+
+static int sxgbe_get_regs_len(struct net_device *dev)
+{
+ return REG_SPACE_SIZE;
+}
+
+static const struct ethtool_ops sxgbe_ethtool_ops = {
+ .get_drvinfo = sxgbe_getdrvinfo,
+ .get_settings = sxgbe_getsettings,
+ .set_settings = sxgbe_setsettings,
+ .get_msglevel = sxgbe_getmsglevel,
+ .set_msglevel = sxgbe_setmsglevel,
+ .get_link = ethtool_op_get_link,
+ .get_strings = sxgbe_get_strings,
+ .get_ethtool_stats = sxgbe_get_ethtool_stats,
+ .get_sset_count = sxgbe_get_sset_count,
+ .get_channels = sxgbe_get_channels,
+ .get_coalesce = sxgbe_get_coalesce,
+ .set_coalesce = sxgbe_set_coalesce,
+ .get_rxnfc = sxgbe_get_rxnfc,
+ .set_rxnfc = sxgbe_set_rxnfc,
+ .get_regs = sxgbe_get_regs,
+ .get_regs_len = sxgbe_get_regs_len,
+ .get_eee = sxgbe_get_eee,
+ .set_eee = sxgbe_set_eee,
+};
+
+void sxgbe_set_ethtool_ops(struct net_device *netdev)
+{
+ SET_ETHTOOL_OPS(netdev, &sxgbe_ethtool_ops);
+}
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
new file mode 100644
index 000000000000..a72688e8dc6c
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
@@ -0,0 +1,2317 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/crc32.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/net_tstamp.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/prefetch.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/sxgbe_platform.h>
+
+#include "sxgbe_common.h"
+#include "sxgbe_desc.h"
+#include "sxgbe_dma.h"
+#include "sxgbe_mtl.h"
+#include "sxgbe_reg.h"
+
+#define SXGBE_ALIGN(x) L1_CACHE_ALIGN(x)
+#define JUMBO_LEN 9000
+
+/* Module parameters */
+#define TX_TIMEO 5000
+#define DMA_TX_SIZE 512
+#define DMA_RX_SIZE 1024
+#define TC_DEFAULT 64
+#define DMA_BUFFER_SIZE BUF_SIZE_2KiB
+/* The default timer value as per the sxgbe specification 1 sec(1000 ms) */
+#define SXGBE_DEFAULT_LPI_TIMER 1000
+
+static int debug = -1;
+static int eee_timer = SXGBE_DEFAULT_LPI_TIMER;
+
+module_param(eee_timer, int, S_IRUGO | S_IWUSR);
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
+ NETIF_MSG_LINK | NETIF_MSG_IFUP |
+ NETIF_MSG_IFDOWN | NETIF_MSG_TIMER);
+
+static irqreturn_t sxgbe_common_interrupt(int irq, void *dev_id);
+static irqreturn_t sxgbe_tx_interrupt(int irq, void *dev_id);
+static irqreturn_t sxgbe_rx_interrupt(int irq, void *dev_id);
+
+#define SXGBE_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x))
+
+#define SXGBE_LPI_TIMER(x) (jiffies + msecs_to_jiffies(x))
+
+/**
+ * sxgbe_verify_args - verify the driver parameters.
+ * Description: it verifies if some wrong parameter is passed to the driver.
+ * Note that wrong parameters are replaced with the default values.
+ */
+static void sxgbe_verify_args(void)
+{
+ if (unlikely(eee_timer < 0))
+ eee_timer = SXGBE_DEFAULT_LPI_TIMER;
+}
+
+static void sxgbe_enable_eee_mode(const struct sxgbe_priv_data *priv)
+{
+ /* Check and enter in LPI mode */
+ if (!priv->tx_path_in_lpi_mode)
+ priv->hw->mac->set_eee_mode(priv->ioaddr);
+}
+
+void sxgbe_disable_eee_mode(struct sxgbe_priv_data * const priv)
+{
+ /* Exit and disable EEE in case of we are are in LPI state. */
+ priv->hw->mac->reset_eee_mode(priv->ioaddr);
+ del_timer_sync(&priv->eee_ctrl_timer);
+ priv->tx_path_in_lpi_mode = false;
+}
+
+/**
+ * sxgbe_eee_ctrl_timer
+ * @arg : data hook
+ * Description:
+ * If there is no data transfer and if we are not in LPI state,
+ * then MAC Transmitter can be moved to LPI state.
+ */
+static void sxgbe_eee_ctrl_timer(unsigned long arg)
+{
+ struct sxgbe_priv_data *priv = (struct sxgbe_priv_data *)arg;
+
+ sxgbe_enable_eee_mode(priv);
+ mod_timer(&priv->eee_ctrl_timer, SXGBE_LPI_TIMER(eee_timer));
+}
+
+/**
+ * sxgbe_eee_init
+ * @priv: private device pointer
+ * Description:
+ * If the EEE support has been enabled while configuring the driver,
+ * if the GMAC actually supports the EEE (from the HW cap reg) and the
+ * phy can also manage EEE, so enable the LPI state and start the timer
+ * to verify if the tx path can enter in LPI state.
+ */
+bool sxgbe_eee_init(struct sxgbe_priv_data * const priv)
+{
+ bool ret = false;
+
+ /* MAC core supports the EEE feature. */
+ if (priv->hw_cap.eee) {
+ /* Check if the PHY supports EEE */
+ if (phy_init_eee(priv->phydev, 1))
+ return false;
+
+ priv->eee_active = 1;
+ init_timer(&priv->eee_ctrl_timer);
+ priv->eee_ctrl_timer.function = sxgbe_eee_ctrl_timer;
+ priv->eee_ctrl_timer.data = (unsigned long)priv;
+ priv->eee_ctrl_timer.expires = SXGBE_LPI_TIMER(eee_timer);
+ add_timer(&priv->eee_ctrl_timer);
+
+ priv->hw->mac->set_eee_timer(priv->ioaddr,
+ SXGBE_DEFAULT_LPI_TIMER,
+ priv->tx_lpi_timer);
+
+ pr_info("Energy-Efficient Ethernet initialized\n");
+
+ ret = true;
+ }
+
+ return ret;
+}
+
+static void sxgbe_eee_adjust(const struct sxgbe_priv_data *priv)
+{
+ /* When the EEE has been already initialised we have to
+ * modify the PLS bit in the LPI ctrl & status reg according
+ * to the PHY link status. For this reason.
+ */
+ if (priv->eee_enabled)
+ priv->hw->mac->set_eee_pls(priv->ioaddr, priv->phydev->link);
+}
+
+/**
+ * sxgbe_clk_csr_set - dynamically set the MDC clock
+ * @priv: driver private structure
+ * Description: this is to dynamically set the MDC clock according to the csr
+ * clock input.
+ */
+static void sxgbe_clk_csr_set(struct sxgbe_priv_data *priv)
+{
+ u32 clk_rate = clk_get_rate(priv->sxgbe_clk);
+
+ /* assign the proper divider, this will be used during
+ * mdio communication
+ */
+ if (clk_rate < SXGBE_CSR_F_150M)
+ priv->clk_csr = SXGBE_CSR_100_150M;
+ else if (clk_rate <= SXGBE_CSR_F_250M)
+ priv->clk_csr = SXGBE_CSR_150_250M;
+ else if (clk_rate <= SXGBE_CSR_F_300M)
+ priv->clk_csr = SXGBE_CSR_250_300M;
+ else if (clk_rate <= SXGBE_CSR_F_350M)
+ priv->clk_csr = SXGBE_CSR_300_350M;
+ else if (clk_rate <= SXGBE_CSR_F_400M)
+ priv->clk_csr = SXGBE_CSR_350_400M;
+ else if (clk_rate <= SXGBE_CSR_F_500M)
+ priv->clk_csr = SXGBE_CSR_400_500M;
+}
+
+/* minimum number of free TX descriptors required to wake up TX process */
+#define SXGBE_TX_THRESH(x) (x->dma_tx_size/4)
+
+static inline u32 sxgbe_tx_avail(struct sxgbe_tx_queue *queue, int tx_qsize)
+{
+ return queue->dirty_tx + tx_qsize - queue->cur_tx - 1;
+}
+
+/**
+ * sxgbe_adjust_link
+ * @dev: net device structure
+ * Description: it adjusts the link parameters.
+ */
+static void sxgbe_adjust_link(struct net_device *dev)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+ u8 new_state = 0;
+ u8 speed = 0xff;
+
+ if (!phydev)
+ return;
+
+ /* SXGBE is not supporting auto-negotiation and
+ * half duplex mode. so, not handling duplex change
+ * in this function. only handling speed and link status
+ */
+ if (phydev->link) {
+ if (phydev->speed != priv->speed) {
+ new_state = 1;
+ switch (phydev->speed) {
+ case SPEED_10000:
+ speed = SXGBE_SPEED_10G;
+ break;
+ case SPEED_2500:
+ speed = SXGBE_SPEED_2_5G;
+ break;
+ case SPEED_1000:
+ speed = SXGBE_SPEED_1G;
+ break;
+ default:
+ netif_err(priv, link, dev,
+ "Speed (%d) not supported\n",
+ phydev->speed);
+ }
+
+ priv->speed = phydev->speed;
+ priv->hw->mac->set_speed(priv->ioaddr, speed);
+ }
+
+ if (!priv->oldlink) {
+ new_state = 1;
+ priv->oldlink = 1;
+ }
+ } else if (priv->oldlink) {
+ new_state = 1;
+ priv->oldlink = 0;
+ priv->speed = SPEED_UNKNOWN;
+ }
+
+ if (new_state & netif_msg_link(priv))
+ phy_print_status(phydev);
+
+ /* Alter the MAC settings for EEE */
+ sxgbe_eee_adjust(priv);
+}
+
+/**
+ * sxgbe_init_phy - PHY initialization
+ * @dev: net device structure
+ * Description: it initializes the driver's PHY state, and attaches the PHY
+ * to the mac driver.
+ * Return value:
+ * 0 on success
+ */
+static int sxgbe_init_phy(struct net_device *ndev)
+{
+ char phy_id_fmt[MII_BUS_ID_SIZE + 3];
+ char bus_id[MII_BUS_ID_SIZE];
+ struct phy_device *phydev;
+ struct sxgbe_priv_data *priv = netdev_priv(ndev);
+ int phy_iface = priv->plat->interface;
+
+ /* assign default link status */
+ priv->oldlink = 0;
+ priv->speed = SPEED_UNKNOWN;
+ priv->oldduplex = DUPLEX_UNKNOWN;
+
+ if (priv->plat->phy_bus_name)
+ snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x",
+ priv->plat->phy_bus_name, priv->plat->bus_id);
+ else
+ snprintf(bus_id, MII_BUS_ID_SIZE, "sxgbe-%x",
+ priv->plat->bus_id);
+
+ snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
+ priv->plat->phy_addr);
+ netdev_dbg(ndev, "%s: trying to attach to %s\n", __func__, phy_id_fmt);
+
+ phydev = phy_connect(ndev, phy_id_fmt, &sxgbe_adjust_link, phy_iface);
+
+ if (IS_ERR(phydev)) {
+ netdev_err(ndev, "Could not attach to PHY\n");
+ return PTR_ERR(phydev);
+ }
+
+ /* Stop Advertising 1000BASE Capability if interface is not GMII */
+ if ((phy_iface == PHY_INTERFACE_MODE_MII) ||
+ (phy_iface == PHY_INTERFACE_MODE_RMII))
+ phydev->advertising &= ~(SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full);
+ if (phydev->phy_id == 0) {
+ phy_disconnect(phydev);
+ return -ENODEV;
+ }
+
+ netdev_dbg(ndev, "%s: attached to PHY (UID 0x%x) Link = %d\n",
+ __func__, phydev->phy_id, phydev->link);
+
+ /* save phy device in private structure */
+ priv->phydev = phydev;
+
+ return 0;
+}
+
+/**
+ * sxgbe_clear_descriptors: clear descriptors
+ * @priv: driver private structure
+ * Description: this function is called to clear the tx and rx descriptors
+ * in case of both basic and extended descriptors are used.
+ */
+static void sxgbe_clear_descriptors(struct sxgbe_priv_data *priv)
+{
+ int i, j;
+ unsigned int txsize = priv->dma_tx_size;
+ unsigned int rxsize = priv->dma_rx_size;
+
+ /* Clear the Rx/Tx descriptors */
+ for (j = 0; j < SXGBE_RX_QUEUES; j++) {
+ for (i = 0; i < rxsize; i++)
+ priv->hw->desc->init_rx_desc(&priv->rxq[j]->dma_rx[i],
+ priv->use_riwt, priv->mode,
+ (i == rxsize - 1));
+ }
+
+ for (j = 0; j < SXGBE_TX_QUEUES; j++) {
+ for (i = 0; i < txsize; i++)
+ priv->hw->desc->init_tx_desc(&priv->txq[j]->dma_tx[i]);
+ }
+}
+
+static int sxgbe_init_rx_buffers(struct net_device *dev,
+ struct sxgbe_rx_norm_desc *p, int i,
+ unsigned int dma_buf_sz,
+ struct sxgbe_rx_queue *rx_ring)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ struct sk_buff *skb;
+
+ skb = __netdev_alloc_skb_ip_align(dev, dma_buf_sz, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ rx_ring->rx_skbuff[i] = skb;
+ rx_ring->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data,
+ dma_buf_sz, DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(priv->device, rx_ring->rx_skbuff_dma[i])) {
+ netdev_err(dev, "%s: DMA mapping error\n", __func__);
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+
+ p->rdes23.rx_rd_des23.buf2_addr = rx_ring->rx_skbuff_dma[i];
+
+ return 0;
+}
+/**
+ * init_tx_ring - init the TX descriptor ring
+ * @dev: net device structure
+ * @tx_ring: ring to be intialised
+ * @tx_rsize: ring size
+ * Description: this function initializes the DMA TX descriptor
+ */
+static int init_tx_ring(struct device *dev, u8 queue_no,
+ struct sxgbe_tx_queue *tx_ring, int tx_rsize)
+{
+ /* TX ring is not allcoated */
+ if (!tx_ring) {
+ dev_err(dev, "No memory for TX queue of SXGBE\n");
+ return -ENOMEM;
+ }
+
+ /* allocate memory for TX descriptors */
+ tx_ring->dma_tx = dma_zalloc_coherent(dev,
+ tx_rsize * sizeof(struct sxgbe_tx_norm_desc),
+ &tx_ring->dma_tx_phy, GFP_KERNEL);
+ if (!tx_ring->dma_tx)
+ return -ENOMEM;
+
+ /* allocate memory for TX skbuff array */
+ tx_ring->tx_skbuff_dma = devm_kcalloc(dev, tx_rsize,
+ sizeof(dma_addr_t), GFP_KERNEL);
+ if (!tx_ring->tx_skbuff_dma)
+ goto dmamem_err;
+
+ tx_ring->tx_skbuff = devm_kcalloc(dev, tx_rsize,
+ sizeof(struct sk_buff *), GFP_KERNEL);
+
+ if (!tx_ring->tx_skbuff)
+ goto dmamem_err;
+
+ /* assign queue number */
+ tx_ring->queue_no = queue_no;
+
+ /* initalise counters */
+ tx_ring->dirty_tx = 0;
+ tx_ring->cur_tx = 0;
+
+ /* initalise TX queue lock */
+ spin_lock_init(&tx_ring->tx_lock);
+
+ return 0;
+
+dmamem_err:
+ dma_free_coherent(dev, tx_rsize * sizeof(struct sxgbe_tx_norm_desc),
+ tx_ring->dma_tx, tx_ring->dma_tx_phy);
+ return -ENOMEM;
+}
+
+/**
+ * free_rx_ring - free the RX descriptor ring
+ * @dev: net device structure
+ * @rx_ring: ring to be intialised
+ * @rx_rsize: ring size
+ * Description: this function initializes the DMA RX descriptor
+ */
+void free_rx_ring(struct device *dev, struct sxgbe_rx_queue *rx_ring,
+ int rx_rsize)
+{
+ dma_free_coherent(dev, rx_rsize * sizeof(struct sxgbe_rx_norm_desc),
+ rx_ring->dma_rx, rx_ring->dma_rx_phy);
+ kfree(rx_ring->rx_skbuff_dma);
+ kfree(rx_ring->rx_skbuff);
+}
+
+/**
+ * init_rx_ring - init the RX descriptor ring
+ * @dev: net device structure
+ * @rx_ring: ring to be intialised
+ * @rx_rsize: ring size
+ * Description: this function initializes the DMA RX descriptor
+ */
+static int init_rx_ring(struct net_device *dev, u8 queue_no,
+ struct sxgbe_rx_queue *rx_ring, int rx_rsize)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ int desc_index;
+ unsigned int bfsize = 0;
+ unsigned int ret = 0;
+
+ /* Set the max buffer size according to the MTU. */
+ bfsize = ALIGN(dev->mtu + ETH_HLEN + ETH_FCS_LEN + NET_IP_ALIGN, 8);
+
+ netif_dbg(priv, probe, dev, "%s: bfsize %d\n", __func__, bfsize);
+
+ /* RX ring is not allcoated */
+ if (rx_ring == NULL) {
+ netdev_err(dev, "No memory for RX queue\n");
+ goto error;
+ }
+
+ /* assign queue number */
+ rx_ring->queue_no = queue_no;
+
+ /* allocate memory for RX descriptors */
+ rx_ring->dma_rx = dma_zalloc_coherent(priv->device,
+ rx_rsize * sizeof(struct sxgbe_rx_norm_desc),
+ &rx_ring->dma_rx_phy, GFP_KERNEL);
+
+ if (rx_ring->dma_rx == NULL)
+ goto error;
+
+ /* allocate memory for RX skbuff array */
+ rx_ring->rx_skbuff_dma = kmalloc_array(rx_rsize,
+ sizeof(dma_addr_t), GFP_KERNEL);
+ if (rx_ring->rx_skbuff_dma == NULL)
+ goto dmamem_err;
+
+ rx_ring->rx_skbuff = kmalloc_array(rx_rsize,
+ sizeof(struct sk_buff *), GFP_KERNEL);
+ if (rx_ring->rx_skbuff == NULL)
+ goto rxbuff_err;
+
+ /* initialise the buffers */
+ for (desc_index = 0; desc_index < rx_rsize; desc_index++) {
+ struct sxgbe_rx_norm_desc *p;
+ p = rx_ring->dma_rx + desc_index;
+ ret = sxgbe_init_rx_buffers(dev, p, desc_index,
+ bfsize, rx_ring);
+ if (ret)
+ goto err_init_rx_buffers;
+ }
+
+ /* initalise counters */
+ rx_ring->cur_rx = 0;
+ rx_ring->dirty_rx = (unsigned int)(desc_index - rx_rsize);
+ priv->dma_buf_sz = bfsize;
+
+ return 0;
+
+err_init_rx_buffers:
+ while (--desc_index >= 0)
+ free_rx_ring(priv->device, rx_ring, desc_index);
+ kfree(rx_ring->rx_skbuff);
+rxbuff_err:
+ kfree(rx_ring->rx_skbuff_dma);
+dmamem_err:
+ dma_free_coherent(priv->device,
+ rx_rsize * sizeof(struct sxgbe_rx_norm_desc),
+ rx_ring->dma_rx, rx_ring->dma_rx_phy);
+error:
+ return -ENOMEM;
+}
+/**
+ * free_tx_ring - free the TX descriptor ring
+ * @dev: net device structure
+ * @tx_ring: ring to be intialised
+ * @tx_rsize: ring size
+ * Description: this function initializes the DMA TX descriptor
+ */
+void free_tx_ring(struct device *dev, struct sxgbe_tx_queue *tx_ring,
+ int tx_rsize)
+{
+ dma_free_coherent(dev, tx_rsize * sizeof(struct sxgbe_tx_norm_desc),
+ tx_ring->dma_tx, tx_ring->dma_tx_phy);
+}
+
+/**
+ * init_dma_desc_rings - init the RX/TX descriptor rings
+ * @dev: net device structure
+ * Description: this function initializes the DMA RX/TX descriptors
+ * and allocates the socket buffers. It suppors the chained and ring
+ * modes.
+ */
+static int init_dma_desc_rings(struct net_device *netd)
+{
+ int queue_num, ret;
+ struct sxgbe_priv_data *priv = netdev_priv(netd);
+ int tx_rsize = priv->dma_tx_size;
+ int rx_rsize = priv->dma_rx_size;
+
+ /* Allocate memory for queue structures and TX descs */
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num) {
+ ret = init_tx_ring(priv->device, queue_num,
+ priv->txq[queue_num], tx_rsize);
+ if (ret) {
+ dev_err(&netd->dev, "TX DMA ring allocation failed!\n");
+ goto txalloc_err;
+ }
+
+ /* save private pointer in each ring this
+ * pointer is needed during cleaing TX queue
+ */
+ priv->txq[queue_num]->priv_ptr = priv;
+ }
+
+ /* Allocate memory for queue structures and RX descs */
+ SXGBE_FOR_EACH_QUEUE(SXGBE_RX_QUEUES, queue_num) {
+ ret = init_rx_ring(netd, queue_num,
+ priv->rxq[queue_num], rx_rsize);
+ if (ret) {
+ netdev_err(netd, "RX DMA ring allocation failed!!\n");
+ goto rxalloc_err;
+ }
+
+ /* save private pointer in each ring this
+ * pointer is needed during cleaing TX queue
+ */
+ priv->rxq[queue_num]->priv_ptr = priv;
+ }
+
+ sxgbe_clear_descriptors(priv);
+
+ return 0;
+
+txalloc_err:
+ while (queue_num--)
+ free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+ return ret;
+
+rxalloc_err:
+ while (queue_num--)
+ free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize);
+ return ret;
+}
+
+static void tx_free_ring_skbufs(struct sxgbe_tx_queue *txqueue)
+{
+ int dma_desc;
+ struct sxgbe_priv_data *priv = txqueue->priv_ptr;
+ int tx_rsize = priv->dma_tx_size;
+
+ for (dma_desc = 0; dma_desc < tx_rsize; dma_desc++) {
+ struct sxgbe_tx_norm_desc *tdesc = txqueue->dma_tx + dma_desc;
+
+ if (txqueue->tx_skbuff_dma[dma_desc])
+ dma_unmap_single(priv->device,
+ txqueue->tx_skbuff_dma[dma_desc],
+ priv->hw->desc->get_tx_len(tdesc),
+ DMA_TO_DEVICE);
+
+ dev_kfree_skb_any(txqueue->tx_skbuff[dma_desc]);
+ txqueue->tx_skbuff[dma_desc] = NULL;
+ txqueue->tx_skbuff_dma[dma_desc] = 0;
+ }
+}
+
+
+static void dma_free_tx_skbufs(struct sxgbe_priv_data *priv)
+{
+ int queue_num;
+
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num) {
+ struct sxgbe_tx_queue *tqueue = priv->txq[queue_num];
+ tx_free_ring_skbufs(tqueue);
+ }
+}
+
+static void free_dma_desc_resources(struct sxgbe_priv_data *priv)
+{
+ int queue_num;
+ int tx_rsize = priv->dma_tx_size;
+ int rx_rsize = priv->dma_rx_size;
+
+ /* Release the DMA TX buffers */
+ dma_free_tx_skbufs(priv);
+
+ /* Release the TX ring memory also */
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num) {
+ free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
+ }
+
+ /* Release the RX ring memory also */
+ SXGBE_FOR_EACH_QUEUE(SXGBE_RX_QUEUES, queue_num) {
+ free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize);
+ }
+}
+
+static int txring_mem_alloc(struct sxgbe_priv_data *priv)
+{
+ int queue_num;
+
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num) {
+ priv->txq[queue_num] = devm_kmalloc(priv->device,
+ sizeof(struct sxgbe_tx_queue), GFP_KERNEL);
+ if (!priv->txq[queue_num])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int rxring_mem_alloc(struct sxgbe_priv_data *priv)
+{
+ int queue_num;
+
+ SXGBE_FOR_EACH_QUEUE(SXGBE_RX_QUEUES, queue_num) {
+ priv->rxq[queue_num] = devm_kmalloc(priv->device,
+ sizeof(struct sxgbe_rx_queue), GFP_KERNEL);
+ if (!priv->rxq[queue_num])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * sxgbe_mtl_operation_mode - HW MTL operation mode
+ * @priv: driver private structure
+ * Description: it sets the MTL operation mode: tx/rx MTL thresholds
+ * or Store-And-Forward capability.
+ */
+static void sxgbe_mtl_operation_mode(struct sxgbe_priv_data *priv)
+{
+ int queue_num;
+
+ /* TX/RX threshold control */
+ if (likely(priv->plat->force_sf_dma_mode)) {
+ /* set TC mode for TX QUEUES */
+ SXGBE_FOR_EACH_QUEUE(priv->hw_cap.tx_mtl_queues, queue_num)
+ priv->hw->mtl->set_tx_mtl_mode(priv->ioaddr, queue_num,
+ SXGBE_MTL_SFMODE);
+ priv->tx_tc = SXGBE_MTL_SFMODE;
+
+ /* set TC mode for RX QUEUES */
+ SXGBE_FOR_EACH_QUEUE(priv->hw_cap.rx_mtl_queues, queue_num)
+ priv->hw->mtl->set_rx_mtl_mode(priv->ioaddr, queue_num,
+ SXGBE_MTL_SFMODE);
+ priv->rx_tc = SXGBE_MTL_SFMODE;
+ } else if (unlikely(priv->plat->force_thresh_dma_mode)) {
+ /* set TC mode for TX QUEUES */
+ SXGBE_FOR_EACH_QUEUE(priv->hw_cap.tx_mtl_queues, queue_num)
+ priv->hw->mtl->set_tx_mtl_mode(priv->ioaddr, queue_num,
+ priv->tx_tc);
+ /* set TC mode for RX QUEUES */
+ SXGBE_FOR_EACH_QUEUE(priv->hw_cap.rx_mtl_queues, queue_num)
+ priv->hw->mtl->set_rx_mtl_mode(priv->ioaddr, queue_num,
+ priv->rx_tc);
+ } else {
+ pr_err("ERROR: %s: Invalid TX threshold mode\n", __func__);
+ }
+}
+
+/**
+ * sxgbe_tx_queue_clean:
+ * @priv: driver private structure
+ * Description: it reclaims resources after transmission completes.
+ */
+static void sxgbe_tx_queue_clean(struct sxgbe_tx_queue *tqueue)
+{
+ struct sxgbe_priv_data *priv = tqueue->priv_ptr;
+ unsigned int tx_rsize = priv->dma_tx_size;
+ struct netdev_queue *dev_txq;
+ u8 queue_no = tqueue->queue_no;
+
+ dev_txq = netdev_get_tx_queue(priv->dev, queue_no);
+
+ spin_lock(&tqueue->tx_lock);
+
+ priv->xstats.tx_clean++;
+ while (tqueue->dirty_tx != tqueue->cur_tx) {
+ unsigned int entry = tqueue->dirty_tx % tx_rsize;
+ struct sk_buff *skb = tqueue->tx_skbuff[entry];
+ struct sxgbe_tx_norm_desc *p;
+
+ p = tqueue->dma_tx + entry;
+
+ /* Check if the descriptor is owned by the DMA. */
+ if (priv->hw->desc->get_tx_owner(p))
+ break;
+
+ if (netif_msg_tx_done(priv))
+ pr_debug("%s: curr %d, dirty %d\n",
+ __func__, tqueue->cur_tx, tqueue->dirty_tx);
+
+ if (likely(tqueue->tx_skbuff_dma[entry])) {
+ dma_unmap_single(priv->device,
+ tqueue->tx_skbuff_dma[entry],
+ priv->hw->desc->get_tx_len(p),
+ DMA_TO_DEVICE);
+ tqueue->tx_skbuff_dma[entry] = 0;
+ }
+
+ if (likely(skb)) {
+ dev_kfree_skb(skb);
+ tqueue->tx_skbuff[entry] = NULL;
+ }
+
+ priv->hw->desc->release_tx_desc(p);
+
+ tqueue->dirty_tx++;
+ }
+
+ /* wake up queue */
+ if (unlikely(netif_tx_queue_stopped(dev_txq) &&
+ sxgbe_tx_avail(tqueue, tx_rsize) > SXGBE_TX_THRESH(priv))) {
+ netif_tx_lock(priv->dev);
+ if (netif_tx_queue_stopped(dev_txq) &&
+ sxgbe_tx_avail(tqueue, tx_rsize) > SXGBE_TX_THRESH(priv)) {
+ if (netif_msg_tx_done(priv))
+ pr_debug("%s: restart transmit\n", __func__);
+ netif_tx_wake_queue(dev_txq);
+ }
+ netif_tx_unlock(priv->dev);
+ }
+
+ spin_unlock(&tqueue->tx_lock);
+}
+
+/**
+ * sxgbe_tx_clean:
+ * @priv: driver private structure
+ * Description: it reclaims resources after transmission completes.
+ */
+static void sxgbe_tx_all_clean(struct sxgbe_priv_data * const priv)
+{
+ u8 queue_num;
+
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num) {
+ struct sxgbe_tx_queue *tqueue = priv->txq[queue_num];
+
+ sxgbe_tx_queue_clean(tqueue);
+ }
+
+ if ((priv->eee_enabled) && (!priv->tx_path_in_lpi_mode)) {
+ sxgbe_enable_eee_mode(priv);
+ mod_timer(&priv->eee_ctrl_timer, SXGBE_LPI_TIMER(eee_timer));
+ }
+}
+
+/**
+ * sxgbe_restart_tx_queue: irq tx error mng function
+ * @priv: driver private structure
+ * Description: it cleans the descriptors and restarts the transmission
+ * in case of errors.
+ */
+static void sxgbe_restart_tx_queue(struct sxgbe_priv_data *priv, int queue_num)
+{
+ struct sxgbe_tx_queue *tx_ring = priv->txq[queue_num];
+ struct netdev_queue *dev_txq = netdev_get_tx_queue(priv->dev,
+ queue_num);
+
+ /* stop the queue */
+ netif_tx_stop_queue(dev_txq);
+
+ /* stop the tx dma */
+ priv->hw->dma->stop_tx_queue(priv->ioaddr, queue_num);
+
+ /* free the skbuffs of the ring */
+ tx_free_ring_skbufs(tx_ring);
+
+ /* initalise counters */
+ tx_ring->cur_tx = 0;
+ tx_ring->dirty_tx = 0;
+
+ /* start the tx dma */
+ priv->hw->dma->start_tx_queue(priv->ioaddr, queue_num);
+
+ priv->dev->stats.tx_errors++;
+
+ /* wakeup the queue */
+ netif_tx_wake_queue(dev_txq);
+}
+
+/**
+ * sxgbe_reset_all_tx_queues: irq tx error mng function
+ * @priv: driver private structure
+ * Description: it cleans all the descriptors and
+ * restarts the transmission on all queues in case of errors.
+ */
+static void sxgbe_reset_all_tx_queues(struct sxgbe_priv_data *priv)
+{
+ int queue_num;
+
+ /* On TX timeout of net device, resetting of all queues
+ * may not be proper way, revisit this later if needed
+ */
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num)
+ sxgbe_restart_tx_queue(priv, queue_num);
+}
+
+/**
+ * sxgbe_get_hw_features: get XMAC capabilities from the HW cap. register.
+ * @priv: driver private structure
+ * Description:
+ * new GMAC chip generations have a new register to indicate the
+ * presence of the optional feature/functions.
+ * This can be also used to override the value passed through the
+ * platform and necessary for old MAC10/100 and GMAC chips.
+ */
+static int sxgbe_get_hw_features(struct sxgbe_priv_data * const priv)
+{
+ int rval = 0;
+ struct sxgbe_hw_features *features = &priv->hw_cap;
+
+ /* Read First Capability Register CAP[0] */
+ rval = priv->hw->mac->get_hw_feature(priv->ioaddr, 0);
+ if (rval) {
+ features->pmt_remote_wake_up =
+ SXGBE_HW_FEAT_PMT_TEMOTE_WOP(rval);
+ features->pmt_magic_frame = SXGBE_HW_FEAT_PMT_MAGIC_PKT(rval);
+ features->atime_stamp = SXGBE_HW_FEAT_IEEE1500_2008(rval);
+ features->tx_csum_offload =
+ SXGBE_HW_FEAT_TX_CSUM_OFFLOAD(rval);
+ features->rx_csum_offload =
+ SXGBE_HW_FEAT_RX_CSUM_OFFLOAD(rval);
+ features->multi_macaddr = SXGBE_HW_FEAT_MACADDR_COUNT(rval);
+ features->tstamp_srcselect = SXGBE_HW_FEAT_TSTMAP_SRC(rval);
+ features->sa_vlan_insert = SXGBE_HW_FEAT_SRCADDR_VLAN(rval);
+ features->eee = SXGBE_HW_FEAT_EEE(rval);
+ }
+
+ /* Read First Capability Register CAP[1] */
+ rval = priv->hw->mac->get_hw_feature(priv->ioaddr, 1);
+ if (rval) {
+ features->rxfifo_size = SXGBE_HW_FEAT_RX_FIFO_SIZE(rval);
+ features->txfifo_size = SXGBE_HW_FEAT_TX_FIFO_SIZE(rval);
+ features->atstmap_hword = SXGBE_HW_FEAT_TX_FIFO_SIZE(rval);
+ features->dcb_enable = SXGBE_HW_FEAT_DCB(rval);
+ features->splithead_enable = SXGBE_HW_FEAT_SPLIT_HDR(rval);
+ features->tcpseg_offload = SXGBE_HW_FEAT_TSO(rval);
+ features->debug_mem = SXGBE_HW_FEAT_DEBUG_MEM_IFACE(rval);
+ features->rss_enable = SXGBE_HW_FEAT_RSS(rval);
+ features->hash_tsize = SXGBE_HW_FEAT_HASH_TABLE_SIZE(rval);
+ features->l3l4_filer_size = SXGBE_HW_FEAT_L3L4_FILTER_NUM(rval);
+ }
+
+ /* Read First Capability Register CAP[2] */
+ rval = priv->hw->mac->get_hw_feature(priv->ioaddr, 2);
+ if (rval) {
+ features->rx_mtl_queues = SXGBE_HW_FEAT_RX_MTL_QUEUES(rval);
+ features->tx_mtl_queues = SXGBE_HW_FEAT_TX_MTL_QUEUES(rval);
+ features->rx_dma_channels = SXGBE_HW_FEAT_RX_DMA_CHANNELS(rval);
+ features->tx_dma_channels = SXGBE_HW_FEAT_TX_DMA_CHANNELS(rval);
+ features->pps_output_count = SXGBE_HW_FEAT_PPS_OUTPUTS(rval);
+ features->aux_input_count = SXGBE_HW_FEAT_AUX_SNAPSHOTS(rval);
+ }
+
+ return rval;
+}
+
+/**
+ * sxgbe_check_ether_addr: check if the MAC addr is valid
+ * @priv: driver private structure
+ * Description:
+ * it is to verify if the MAC address is valid, in case of failures it
+ * generates a random MAC address
+ */
+static void sxgbe_check_ether_addr(struct sxgbe_priv_data *priv)
+{
+ if (!is_valid_ether_addr(priv->dev->dev_addr)) {
+ priv->hw->mac->get_umac_addr((void __iomem *)
+ priv->ioaddr,
+ priv->dev->dev_addr, 0);
+ if (!is_valid_ether_addr(priv->dev->dev_addr))
+ eth_hw_addr_random(priv->dev);
+ }
+ dev_info(priv->device, "device MAC address %pM\n",
+ priv->dev->dev_addr);
+}
+
+/**
+ * sxgbe_init_dma_engine: DMA init.
+ * @priv: driver private structure
+ * Description:
+ * It inits the DMA invoking the specific SXGBE callback.
+ * Some DMA parameters can be passed from the platform;
+ * in case of these are not passed a default is kept for the MAC or GMAC.
+ */
+static int sxgbe_init_dma_engine(struct sxgbe_priv_data *priv)
+{
+ int pbl = DEFAULT_DMA_PBL, fixed_burst = 0, burst_map = 0;
+ int queue_num;
+
+ if (priv->plat->dma_cfg) {
+ pbl = priv->plat->dma_cfg->pbl;
+ fixed_burst = priv->plat->dma_cfg->fixed_burst;
+ burst_map = priv->plat->dma_cfg->burst_map;
+ }
+
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num)
+ priv->hw->dma->cha_init(priv->ioaddr, queue_num,
+ fixed_burst, pbl,
+ (priv->txq[queue_num])->dma_tx_phy,
+ (priv->rxq[queue_num])->dma_rx_phy,
+ priv->dma_tx_size, priv->dma_rx_size);
+
+ return priv->hw->dma->init(priv->ioaddr, fixed_burst, burst_map);
+}
+
+/**
+ * sxgbe_init_mtl_engine: MTL init.
+ * @priv: driver private structure
+ * Description:
+ * It inits the MTL invoking the specific SXGBE callback.
+ */
+static void sxgbe_init_mtl_engine(struct sxgbe_priv_data *priv)
+{
+ int queue_num;
+
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num) {
+ priv->hw->mtl->mtl_set_txfifosize(priv->ioaddr, queue_num,
+ priv->hw_cap.tx_mtl_qsize);
+ priv->hw->mtl->mtl_enable_txqueue(priv->ioaddr, queue_num);
+ }
+}
+
+/**
+ * sxgbe_disable_mtl_engine: MTL disable.
+ * @priv: driver private structure
+ * Description:
+ * It disables the MTL queues by invoking the specific SXGBE callback.
+ */
+static void sxgbe_disable_mtl_engine(struct sxgbe_priv_data *priv)
+{
+ int queue_num;
+
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num)
+ priv->hw->mtl->mtl_disable_txqueue(priv->ioaddr, queue_num);
+}
+
+
+/**
+ * sxgbe_tx_timer: mitigation sw timer for tx.
+ * @data: data pointer
+ * Description:
+ * This is the timer handler to directly invoke the sxgbe_tx_clean.
+ */
+static void sxgbe_tx_timer(unsigned long data)
+{
+ struct sxgbe_tx_queue *p = (struct sxgbe_tx_queue *)data;
+ sxgbe_tx_queue_clean(p);
+}
+
+/**
+ * sxgbe_init_tx_coalesce: init tx mitigation options.
+ * @priv: driver private structure
+ * Description:
+ * This inits the transmit coalesce parameters: i.e. timer rate,
+ * timer handler and default threshold used for enabling the
+ * interrupt on completion bit.
+ */
+static void sxgbe_tx_init_coalesce(struct sxgbe_priv_data *priv)
+{
+ u8 queue_num;
+
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num) {
+ struct sxgbe_tx_queue *p = priv->txq[queue_num];
+ p->tx_coal_frames = SXGBE_TX_FRAMES;
+ p->tx_coal_timer = SXGBE_COAL_TX_TIMER;
+ init_timer(&p->txtimer);
+ p->txtimer.expires = SXGBE_COAL_TIMER(p->tx_coal_timer);
+ p->txtimer.data = (unsigned long)&priv->txq[queue_num];
+ p->txtimer.function = sxgbe_tx_timer;
+ add_timer(&p->txtimer);
+ }
+}
+
+static void sxgbe_tx_del_timer(struct sxgbe_priv_data *priv)
+{
+ u8 queue_num;
+
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num) {
+ struct sxgbe_tx_queue *p = priv->txq[queue_num];
+ del_timer_sync(&p->txtimer);
+ }
+}
+
+/**
+ * sxgbe_open - open entry point of the driver
+ * @dev : pointer to the device structure.
+ * Description:
+ * This function is the open entry point of the driver.
+ * Return value:
+ * 0 on success and an appropriate (-)ve integer as defined in errno.h
+ * file on failure.
+ */
+static int sxgbe_open(struct net_device *dev)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ int ret, queue_num;
+
+ clk_prepare_enable(priv->sxgbe_clk);
+
+ sxgbe_check_ether_addr(priv);
+
+ /* Init the phy */
+ ret = sxgbe_init_phy(dev);
+ if (ret) {
+ netdev_err(dev, "%s: Cannot attach to PHY (error: %d)\n",
+ __func__, ret);
+ goto phy_error;
+ }
+
+ /* Create and initialize the TX/RX descriptors chains. */
+ priv->dma_tx_size = SXGBE_ALIGN(DMA_TX_SIZE);
+ priv->dma_rx_size = SXGBE_ALIGN(DMA_RX_SIZE);
+ priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE);
+ priv->tx_tc = TC_DEFAULT;
+ priv->rx_tc = TC_DEFAULT;
+ init_dma_desc_rings(dev);
+
+ /* DMA initialization and SW reset */
+ ret = sxgbe_init_dma_engine(priv);
+ if (ret < 0) {
+ netdev_err(dev, "%s: DMA initialization failed\n", __func__);
+ goto init_error;
+ }
+
+ /* MTL initialization */
+ sxgbe_init_mtl_engine(priv);
+
+ /* Copy the MAC addr into the HW */
+ priv->hw->mac->set_umac_addr(priv->ioaddr, dev->dev_addr, 0);
+
+ /* Initialize the MAC Core */
+ priv->hw->mac->core_init(priv->ioaddr);
+
+ /* Request the IRQ lines */
+ ret = devm_request_irq(priv->device, priv->irq, sxgbe_common_interrupt,
+ IRQF_SHARED, dev->name, dev);
+ if (unlikely(ret < 0)) {
+ netdev_err(dev, "%s: ERROR: allocating the IRQ %d (error: %d)\n",
+ __func__, priv->irq, ret);
+ goto init_error;
+ }
+
+ /* If the LPI irq is different from the mac irq
+ * register a dedicated handler
+ */
+ if (priv->lpi_irq != dev->irq) {
+ ret = devm_request_irq(priv->device, priv->lpi_irq,
+ sxgbe_common_interrupt,
+ IRQF_SHARED, dev->name, dev);
+ if (unlikely(ret < 0)) {
+ netdev_err(dev, "%s: ERROR: allocating the LPI IRQ %d (%d)\n",
+ __func__, priv->lpi_irq, ret);
+ goto init_error;
+ }
+ }
+
+ /* Request TX DMA irq lines */
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num) {
+ ret = devm_request_irq(priv->device,
+ (priv->txq[queue_num])->irq_no,
+ sxgbe_tx_interrupt, 0,
+ dev->name, priv->txq[queue_num]);
+ if (unlikely(ret < 0)) {
+ netdev_err(dev, "%s: ERROR: allocating TX IRQ %d (error: %d)\n",
+ __func__, priv->irq, ret);
+ goto init_error;
+ }
+ }
+
+ /* Request RX DMA irq lines */
+ SXGBE_FOR_EACH_QUEUE(SXGBE_RX_QUEUES, queue_num) {
+ ret = devm_request_irq(priv->device,
+ (priv->rxq[queue_num])->irq_no,
+ sxgbe_rx_interrupt, 0,
+ dev->name, priv->rxq[queue_num]);
+ if (unlikely(ret < 0)) {
+ netdev_err(dev, "%s: ERROR: allocating TX IRQ %d (error: %d)\n",
+ __func__, priv->irq, ret);
+ goto init_error;
+ }
+ }
+
+ /* Enable the MAC Rx/Tx */
+ priv->hw->mac->enable_tx(priv->ioaddr, true);
+ priv->hw->mac->enable_rx(priv->ioaddr, true);
+
+ /* Set the HW DMA mode and the COE */
+ sxgbe_mtl_operation_mode(priv);
+
+ /* Extra statistics */
+ memset(&priv->xstats, 0, sizeof(struct sxgbe_extra_stats));
+
+ priv->xstats.tx_threshold = priv->tx_tc;
+ priv->xstats.rx_threshold = priv->rx_tc;
+
+ /* Start the ball rolling... */
+ netdev_dbg(dev, "DMA RX/TX processes started...\n");
+ priv->hw->dma->start_tx(priv->ioaddr, SXGBE_TX_QUEUES);
+ priv->hw->dma->start_rx(priv->ioaddr, SXGBE_RX_QUEUES);
+
+ if (priv->phydev)
+ phy_start(priv->phydev);
+
+ /* initalise TX coalesce parameters */
+ sxgbe_tx_init_coalesce(priv);
+
+ if ((priv->use_riwt) && (priv->hw->dma->rx_watchdog)) {
+ priv->rx_riwt = SXGBE_MAX_DMA_RIWT;
+ priv->hw->dma->rx_watchdog(priv->ioaddr, SXGBE_MAX_DMA_RIWT);
+ }
+
+ priv->tx_lpi_timer = SXGBE_DEFAULT_LPI_TIMER;
+ priv->eee_enabled = sxgbe_eee_init(priv);
+
+ napi_enable(&priv->napi);
+ netif_start_queue(dev);
+
+ return 0;
+
+init_error:
+ free_dma_desc_resources(priv);
+ if (priv->phydev)
+ phy_disconnect(priv->phydev);
+phy_error:
+ clk_disable_unprepare(priv->sxgbe_clk);
+
+ return ret;
+}
+
+/**
+ * sxgbe_release - close entry point of the driver
+ * @dev : device pointer.
+ * Description:
+ * This is the stop entry point of the driver.
+ */
+static int sxgbe_release(struct net_device *dev)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+
+ if (priv->eee_enabled)
+ del_timer_sync(&priv->eee_ctrl_timer);
+
+ /* Stop and disconnect the PHY */
+ if (priv->phydev) {
+ phy_stop(priv->phydev);
+ phy_disconnect(priv->phydev);
+ priv->phydev = NULL;
+ }
+
+ netif_tx_stop_all_queues(dev);
+
+ napi_disable(&priv->napi);
+
+ /* delete TX timers */
+ sxgbe_tx_del_timer(priv);
+
+ /* Stop TX/RX DMA and clear the descriptors */
+ priv->hw->dma->stop_tx(priv->ioaddr, SXGBE_TX_QUEUES);
+ priv->hw->dma->stop_rx(priv->ioaddr, SXGBE_RX_QUEUES);
+
+ /* disable MTL queue */
+ sxgbe_disable_mtl_engine(priv);
+
+ /* Release and free the Rx/Tx resources */
+ free_dma_desc_resources(priv);
+
+ /* Disable the MAC Rx/Tx */
+ priv->hw->mac->enable_tx(priv->ioaddr, false);
+ priv->hw->mac->enable_rx(priv->ioaddr, false);
+
+ clk_disable_unprepare(priv->sxgbe_clk);
+
+ return 0;
+}
+
+/* Prepare first Tx descriptor for doing TSO operation */
+void sxgbe_tso_prepare(struct sxgbe_priv_data *priv,
+ struct sxgbe_tx_norm_desc *first_desc,
+ struct sk_buff *skb)
+{
+ unsigned int total_hdr_len, tcp_hdr_len;
+
+ /* Write first Tx descriptor with appropriate value */
+ tcp_hdr_len = tcp_hdrlen(skb);
+ total_hdr_len = skb_transport_offset(skb) + tcp_hdr_len;
+
+ first_desc->tdes01 = dma_map_single(priv->device, skb->data,
+ total_hdr_len, DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->device, first_desc->tdes01))
+ pr_err("%s: TX dma mapping failed!!\n", __func__);
+
+ first_desc->tdes23.tx_rd_des23.first_desc = 1;
+ priv->hw->desc->tx_desc_enable_tse(first_desc, 1, total_hdr_len,
+ tcp_hdr_len,
+ skb->len - total_hdr_len);
+}
+
+/**
+ * sxgbe_xmit: Tx entry point of the driver
+ * @skb : the socket buffer
+ * @dev : device pointer
+ * Description : this is the tx entry point of the driver.
+ * It programs the chain or the ring and supports oversized frames
+ * and SG feature.
+ */
+static netdev_tx_t sxgbe_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ unsigned int entry, frag_num;
+ int cksum_flag = 0;
+ struct netdev_queue *dev_txq;
+ unsigned txq_index = skb_get_queue_mapping(skb);
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ unsigned int tx_rsize = priv->dma_tx_size;
+ struct sxgbe_tx_queue *tqueue = priv->txq[txq_index];
+ struct sxgbe_tx_norm_desc *tx_desc, *first_desc;
+ struct sxgbe_tx_ctxt_desc *ctxt_desc = NULL;
+ int nr_frags = skb_shinfo(skb)->nr_frags;
+ int no_pagedlen = skb_headlen(skb);
+ int is_jumbo = 0;
+ u16 cur_mss = skb_shinfo(skb)->gso_size;
+ u32 ctxt_desc_req = 0;
+
+ /* get the TX queue handle */
+ dev_txq = netdev_get_tx_queue(dev, txq_index);
+
+ if (unlikely(skb_is_gso(skb) && tqueue->prev_mss != cur_mss))
+ ctxt_desc_req = 1;
+
+ if (unlikely(vlan_tx_tag_present(skb) ||
+ ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+ tqueue->hwts_tx_en)))
+ ctxt_desc_req = 1;
+
+ /* get the spinlock */
+ spin_lock(&tqueue->tx_lock);
+
+ if (priv->tx_path_in_lpi_mode)
+ sxgbe_disable_eee_mode(priv);
+
+ if (unlikely(sxgbe_tx_avail(tqueue, tx_rsize) < nr_frags + 1)) {
+ if (!netif_tx_queue_stopped(dev_txq)) {
+ netif_tx_stop_queue(dev_txq);
+ netdev_err(dev, "%s: Tx Ring is full when %d queue is awake\n",
+ __func__, txq_index);
+ }
+ /* release the spin lock in case of BUSY */
+ spin_unlock(&tqueue->tx_lock);
+ return NETDEV_TX_BUSY;
+ }
+
+ entry = tqueue->cur_tx % tx_rsize;
+ tx_desc = tqueue->dma_tx + entry;
+
+ first_desc = tx_desc;
+ if (ctxt_desc_req)
+ ctxt_desc = (struct sxgbe_tx_ctxt_desc *)first_desc;
+
+ /* save the skb address */
+ tqueue->tx_skbuff[entry] = skb;
+
+ if (!is_jumbo) {
+ if (likely(skb_is_gso(skb))) {
+ /* TSO support */
+ if (unlikely(tqueue->prev_mss != cur_mss)) {
+ priv->hw->desc->tx_ctxt_desc_set_mss(
+ ctxt_desc, cur_mss);
+ priv->hw->desc->tx_ctxt_desc_set_tcmssv(
+ ctxt_desc);
+ priv->hw->desc->tx_ctxt_desc_reset_ostc(
+ ctxt_desc);
+ priv->hw->desc->tx_ctxt_desc_set_ctxt(
+ ctxt_desc);
+ priv->hw->desc->tx_ctxt_desc_set_owner(
+ ctxt_desc);
+
+ entry = (++tqueue->cur_tx) % tx_rsize;
+ first_desc = tqueue->dma_tx + entry;
+
+ tqueue->prev_mss = cur_mss;
+ }
+ sxgbe_tso_prepare(priv, first_desc, skb);
+ } else {
+ tx_desc->tdes01 = dma_map_single(priv->device,
+ skb->data, no_pagedlen, DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->device, tx_desc->tdes01))
+ netdev_err(dev, "%s: TX dma mapping failed!!\n",
+ __func__);
+
+ priv->hw->desc->prepare_tx_desc(tx_desc, 1, no_pagedlen,
+ no_pagedlen, cksum_flag);
+ }
+ }
+
+ for (frag_num = 0; frag_num < nr_frags; frag_num++) {
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[frag_num];
+ int len = skb_frag_size(frag);
+
+ entry = (++tqueue->cur_tx) % tx_rsize;
+ tx_desc = tqueue->dma_tx + entry;
+ tx_desc->tdes01 = skb_frag_dma_map(priv->device, frag, 0, len,
+ DMA_TO_DEVICE);
+
+ tqueue->tx_skbuff_dma[entry] = tx_desc->tdes01;
+ tqueue->tx_skbuff[entry] = NULL;
+
+ /* prepare the descriptor */
+ priv->hw->desc->prepare_tx_desc(tx_desc, 0, len,
+ len, cksum_flag);
+ /* memory barrier to flush descriptor */
+ wmb();
+
+ /* set the owner */
+ priv->hw->desc->set_tx_owner(tx_desc);
+ }
+
+ /* close the descriptors */
+ priv->hw->desc->close_tx_desc(tx_desc);
+
+ /* memory barrier to flush descriptor */
+ wmb();
+
+ tqueue->tx_count_frames += nr_frags + 1;
+ if (tqueue->tx_count_frames > tqueue->tx_coal_frames) {
+ priv->hw->desc->clear_tx_ic(tx_desc);
+ priv->xstats.tx_reset_ic_bit++;
+ mod_timer(&tqueue->txtimer,
+ SXGBE_COAL_TIMER(tqueue->tx_coal_timer));
+ } else {
+ tqueue->tx_count_frames = 0;
+ }
+
+ /* set owner for first desc */
+ priv->hw->desc->set_tx_owner(first_desc);
+
+ /* memory barrier to flush descriptor */
+ wmb();
+
+ tqueue->cur_tx++;
+
+ /* display current ring */
+ netif_dbg(priv, pktdata, dev, "%s: curr %d dirty=%d entry=%d, first=%p, nfrags=%d\n",
+ __func__, tqueue->cur_tx % tx_rsize,
+ tqueue->dirty_tx % tx_rsize, entry,
+ first_desc, nr_frags);
+
+ if (unlikely(sxgbe_tx_avail(tqueue, tx_rsize) <= (MAX_SKB_FRAGS + 1))) {
+ netif_dbg(priv, hw, dev, "%s: stop transmitted packets\n",
+ __func__);
+ netif_tx_stop_queue(dev_txq);
+ }
+
+ dev->stats.tx_bytes += skb->len;
+
+ if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+ tqueue->hwts_tx_en)) {
+ /* declare that device is doing timestamping */
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ priv->hw->desc->tx_enable_tstamp(first_desc);
+ }
+
+ if (!tqueue->hwts_tx_en)
+ skb_tx_timestamp(skb);
+
+ priv->hw->dma->enable_dma_transmission(priv->ioaddr, txq_index);
+
+ spin_unlock(&tqueue->tx_lock);
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * sxgbe_rx_refill: refill used skb preallocated buffers
+ * @priv: driver private structure
+ * Description : this is to reallocate the skb for the reception process
+ * that is based on zero-copy.
+ */
+static void sxgbe_rx_refill(struct sxgbe_priv_data *priv)
+{
+ unsigned int rxsize = priv->dma_rx_size;
+ int bfsize = priv->dma_buf_sz;
+ u8 qnum = priv->cur_rx_qnum;
+
+ for (; priv->rxq[qnum]->cur_rx - priv->rxq[qnum]->dirty_rx > 0;
+ priv->rxq[qnum]->dirty_rx++) {
+ unsigned int entry = priv->rxq[qnum]->dirty_rx % rxsize;
+ struct sxgbe_rx_norm_desc *p;
+
+ p = priv->rxq[qnum]->dma_rx + entry;
+
+ if (likely(priv->rxq[qnum]->rx_skbuff[entry] == NULL)) {
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb_ip_align(priv->dev, bfsize);
+
+ if (unlikely(skb == NULL))
+ break;
+
+ priv->rxq[qnum]->rx_skbuff[entry] = skb;
+ priv->rxq[qnum]->rx_skbuff_dma[entry] =
+ dma_map_single(priv->device, skb->data, bfsize,
+ DMA_FROM_DEVICE);
+
+ p->rdes23.rx_rd_des23.buf2_addr =
+ priv->rxq[qnum]->rx_skbuff_dma[entry];
+ }
+
+ /* Added memory barrier for RX descriptor modification */
+ wmb();
+ priv->hw->desc->set_rx_owner(p);
+ /* Added memory barrier for RX descriptor modification */
+ wmb();
+ }
+}
+
+/**
+ * sxgbe_rx: receive the frames from the remote host
+ * @priv: driver private structure
+ * @limit: napi bugget.
+ * Description : this the function called by the napi poll method.
+ * It gets all the frames inside the ring.
+ */
+static int sxgbe_rx(struct sxgbe_priv_data *priv, int limit)
+{
+ u8 qnum = priv->cur_rx_qnum;
+ unsigned int rxsize = priv->dma_rx_size;
+ unsigned int entry = priv->rxq[qnum]->cur_rx;
+ unsigned int next_entry = 0;
+ unsigned int count = 0;
+ int checksum;
+ int status;
+
+ while (count < limit) {
+ struct sxgbe_rx_norm_desc *p;
+ struct sk_buff *skb;
+ int frame_len;
+
+ p = priv->rxq[qnum]->dma_rx + entry;
+
+ if (priv->hw->desc->get_rx_owner(p))
+ break;
+
+ count++;
+
+ next_entry = (++priv->rxq[qnum]->cur_rx) % rxsize;
+ prefetch(priv->rxq[qnum]->dma_rx + next_entry);
+
+ /* Read the status of the incoming frame and also get checksum
+ * value based on whether it is enabled in SXGBE hardware or
+ * not.
+ */
+ status = priv->hw->desc->rx_wbstatus(p, &priv->xstats,
+ &checksum);
+ if (unlikely(status < 0)) {
+ entry = next_entry;
+ continue;
+ }
+ if (unlikely(!priv->rxcsum_insertion))
+ checksum = CHECKSUM_NONE;
+
+ skb = priv->rxq[qnum]->rx_skbuff[entry];
+
+ if (unlikely(!skb))
+ netdev_err(priv->dev, "rx descriptor is not consistent\n");
+
+ prefetch(skb->data - NET_IP_ALIGN);
+ priv->rxq[qnum]->rx_skbuff[entry] = NULL;
+
+ frame_len = priv->hw->desc->get_rx_frame_len(p);
+
+ skb_put(skb, frame_len);
+
+ skb->ip_summed = checksum;
+ if (checksum == CHECKSUM_NONE)
+ netif_receive_skb(skb);
+ else
+ napi_gro_receive(&priv->napi, skb);
+
+ entry = next_entry;
+ }
+
+ sxgbe_rx_refill(priv);
+
+ return count;
+}
+
+/**
+ * sxgbe_poll - sxgbe poll method (NAPI)
+ * @napi : pointer to the napi structure.
+ * @budget : maximum number of packets that the current CPU can receive from
+ * all interfaces.
+ * Description :
+ * To look at the incoming frames and clear the tx resources.
+ */
+static int sxgbe_poll(struct napi_struct *napi, int budget)
+{
+ struct sxgbe_priv_data *priv = container_of(napi,
+ struct sxgbe_priv_data, napi);
+ int work_done = 0;
+ u8 qnum = priv->cur_rx_qnum;
+
+ priv->xstats.napi_poll++;
+ /* first, clean the tx queues */
+ sxgbe_tx_all_clean(priv);
+
+ work_done = sxgbe_rx(priv, budget);
+ if (work_done < budget) {
+ napi_complete(napi);
+ priv->hw->dma->enable_dma_irq(priv->ioaddr, qnum);
+ }
+
+ return work_done;
+}
+
+/**
+ * sxgbe_tx_timeout
+ * @dev : Pointer to net device structure
+ * Description: this function is called when a packet transmission fails to
+ * complete within a reasonable time. The driver will mark the error in the
+ * netdev structure and arrange for the device to be reset to a sane state
+ * in order to transmit a new packet.
+ */
+static void sxgbe_tx_timeout(struct net_device *dev)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+
+ sxgbe_reset_all_tx_queues(priv);
+}
+
+/**
+ * sxgbe_common_interrupt - main ISR
+ * @irq: interrupt number.
+ * @dev_id: to pass the net device pointer.
+ * Description: this is the main driver interrupt service routine.
+ * It calls the DMA ISR and also the core ISR to manage PMT, MMC, LPI
+ * interrupts.
+ */
+static irqreturn_t sxgbe_common_interrupt(int irq, void *dev_id)
+{
+ struct net_device *netdev = (struct net_device *)dev_id;
+ struct sxgbe_priv_data *priv = netdev_priv(netdev);
+ int status;
+
+ status = priv->hw->mac->host_irq_status(priv->ioaddr, &priv->xstats);
+ /* For LPI we need to save the tx status */
+ if (status & TX_ENTRY_LPI_MODE) {
+ priv->xstats.tx_lpi_entry_n++;
+ priv->tx_path_in_lpi_mode = true;
+ }
+ if (status & TX_EXIT_LPI_MODE) {
+ priv->xstats.tx_lpi_exit_n++;
+ priv->tx_path_in_lpi_mode = false;
+ }
+ if (status & RX_ENTRY_LPI_MODE)
+ priv->xstats.rx_lpi_entry_n++;
+ if (status & RX_EXIT_LPI_MODE)
+ priv->xstats.rx_lpi_exit_n++;
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * sxgbe_tx_interrupt - TX DMA ISR
+ * @irq: interrupt number.
+ * @dev_id: to pass the net device pointer.
+ * Description: this is the tx dma interrupt service routine.
+ */
+static irqreturn_t sxgbe_tx_interrupt(int irq, void *dev_id)
+{
+ int status;
+ struct sxgbe_tx_queue *txq = (struct sxgbe_tx_queue *)dev_id;
+ struct sxgbe_priv_data *priv = txq->priv_ptr;
+
+ /* get the channel status */
+ status = priv->hw->dma->tx_dma_int_status(priv->ioaddr, txq->queue_no,
+ &priv->xstats);
+ /* check for normal path */
+ if (likely((status & handle_tx)))
+ napi_schedule(&priv->napi);
+
+ /* check for unrecoverable error */
+ if (unlikely((status & tx_hard_error)))
+ sxgbe_restart_tx_queue(priv, txq->queue_no);
+
+ /* check for TC configuration change */
+ if (unlikely((status & tx_bump_tc) &&
+ (priv->tx_tc != SXGBE_MTL_SFMODE) &&
+ (priv->tx_tc < 512))) {
+ /* step of TX TC is 32 till 128, otherwise 64 */
+ priv->tx_tc += (priv->tx_tc < 128) ? 32 : 64;
+ priv->hw->mtl->set_tx_mtl_mode(priv->ioaddr,
+ txq->queue_no, priv->tx_tc);
+ priv->xstats.tx_threshold = priv->tx_tc;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * sxgbe_rx_interrupt - RX DMA ISR
+ * @irq: interrupt number.
+ * @dev_id: to pass the net device pointer.
+ * Description: this is the rx dma interrupt service routine.
+ */
+static irqreturn_t sxgbe_rx_interrupt(int irq, void *dev_id)
+{
+ int status;
+ struct sxgbe_rx_queue *rxq = (struct sxgbe_rx_queue *)dev_id;
+ struct sxgbe_priv_data *priv = rxq->priv_ptr;
+
+ /* get the channel status */
+ status = priv->hw->dma->rx_dma_int_status(priv->ioaddr, rxq->queue_no,
+ &priv->xstats);
+
+ if (likely((status & handle_rx) && (napi_schedule_prep(&priv->napi)))) {
+ priv->hw->dma->disable_dma_irq(priv->ioaddr, rxq->queue_no);
+ __napi_schedule(&priv->napi);
+ }
+
+ /* check for TC configuration change */
+ if (unlikely((status & rx_bump_tc) &&
+ (priv->rx_tc != SXGBE_MTL_SFMODE) &&
+ (priv->rx_tc < 128))) {
+ /* step of TC is 32 */
+ priv->rx_tc += 32;
+ priv->hw->mtl->set_rx_mtl_mode(priv->ioaddr,
+ rxq->queue_no, priv->rx_tc);
+ priv->xstats.rx_threshold = priv->rx_tc;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static inline u64 sxgbe_get_stat64(void __iomem *ioaddr, int reg_lo, int reg_hi)
+{
+ u64 val = readl(ioaddr + reg_lo);
+
+ val |= ((u64)readl(ioaddr + reg_hi)) << 32;
+
+ return val;
+}
+
+
+/* sxgbe_get_stats64 - entry point to see statistical information of device
+ * @dev : device pointer.
+ * @stats : pointer to hold all the statistical information of device.
+ * Description:
+ * This function is a driver entry point whenever ifconfig command gets
+ * executed to see device statistics. Statistics are number of
+ * bytes sent or received, errors occured etc.
+ * Return value:
+ * This function returns various statistical information of device.
+ */
+static struct rtnl_link_stats64 *sxgbe_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ void __iomem *ioaddr = priv->ioaddr;
+ u64 count;
+
+ spin_lock(&priv->stats_lock);
+ /* Freeze the counter registers before reading value otherwise it may
+ * get updated by hardware while we are reading them
+ */
+ writel(SXGBE_MMC_CTRL_CNT_FRZ, ioaddr + SXGBE_MMC_CTL_REG);
+
+ stats->rx_bytes = sxgbe_get_stat64(ioaddr,
+ SXGBE_MMC_RXOCTETLO_GCNT_REG,
+ SXGBE_MMC_RXOCTETHI_GCNT_REG);
+
+ stats->rx_packets = sxgbe_get_stat64(ioaddr,
+ SXGBE_MMC_RXFRAMELO_GBCNT_REG,
+ SXGBE_MMC_RXFRAMEHI_GBCNT_REG);
+
+ stats->multicast = sxgbe_get_stat64(ioaddr,
+ SXGBE_MMC_RXMULTILO_GCNT_REG,
+ SXGBE_MMC_RXMULTIHI_GCNT_REG);
+
+ stats->rx_crc_errors = sxgbe_get_stat64(ioaddr,
+ SXGBE_MMC_RXCRCERRLO_REG,
+ SXGBE_MMC_RXCRCERRHI_REG);
+
+ stats->rx_length_errors = sxgbe_get_stat64(ioaddr,
+ SXGBE_MMC_RXLENERRLO_REG,
+ SXGBE_MMC_RXLENERRHI_REG);
+
+ stats->rx_missed_errors = sxgbe_get_stat64(ioaddr,
+ SXGBE_MMC_RXFIFOOVERFLOWLO_GBCNT_REG,
+ SXGBE_MMC_RXFIFOOVERFLOWHI_GBCNT_REG);
+
+ stats->tx_bytes = sxgbe_get_stat64(ioaddr,
+ SXGBE_MMC_TXOCTETLO_GCNT_REG,
+ SXGBE_MMC_TXOCTETHI_GCNT_REG);
+
+ count = sxgbe_get_stat64(ioaddr, SXGBE_MMC_TXFRAMELO_GBCNT_REG,
+ SXGBE_MMC_TXFRAMEHI_GBCNT_REG);
+
+ stats->tx_errors = sxgbe_get_stat64(ioaddr, SXGBE_MMC_TXFRAMELO_GCNT_REG,
+ SXGBE_MMC_TXFRAMEHI_GCNT_REG);
+ stats->tx_errors = count - stats->tx_errors;
+ stats->tx_packets = count;
+ stats->tx_fifo_errors = sxgbe_get_stat64(ioaddr, SXGBE_MMC_TXUFLWLO_GBCNT_REG,
+ SXGBE_MMC_TXUFLWHI_GBCNT_REG);
+ writel(0, ioaddr + SXGBE_MMC_CTL_REG);
+ spin_unlock(&priv->stats_lock);
+
+ return stats;
+}
+
+/* sxgbe_set_features - entry point to set offload features of the device.
+ * @dev : device pointer.
+ * @features : features which are required to be set.
+ * Description:
+ * This function is a driver entry point and called by Linux kernel whenever
+ * any device features are set or reset by user.
+ * Return value:
+ * This function returns 0 after setting or resetting device features.
+ */
+static int sxgbe_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ netdev_features_t changed = dev->features ^ features;
+
+ if (changed & NETIF_F_RXCSUM) {
+ if (features & NETIF_F_RXCSUM) {
+ priv->hw->mac->enable_rx_csum(priv->ioaddr);
+ priv->rxcsum_insertion = true;
+ } else {
+ priv->hw->mac->disable_rx_csum(priv->ioaddr);
+ priv->rxcsum_insertion = false;
+ }
+ }
+
+ return 0;
+}
+
+/* sxgbe_change_mtu - entry point to change MTU size for the device.
+ * @dev : device pointer.
+ * @new_mtu : the new MTU size for the device.
+ * Description: the Maximum Transfer Unit (MTU) is used by the network layer
+ * to drive packet transmission. Ethernet has an MTU of 1500 octets
+ * (ETH_DATA_LEN). This value can be changed with ifconfig.
+ * Return value:
+ * 0 on success and an appropriate (-)ve integer as defined in errno.h
+ * file on failure.
+ */
+static int sxgbe_change_mtu(struct net_device *dev, int new_mtu)
+{
+ /* RFC 791, page 25, "Every internet module must be able to forward
+ * a datagram of 68 octets without further fragmentation."
+ */
+ if (new_mtu < MIN_MTU || (new_mtu > MAX_MTU)) {
+ netdev_err(dev, "invalid MTU, MTU should be in between %d and %d\n",
+ MIN_MTU, MAX_MTU);
+ return -EINVAL;
+ }
+
+ /* Return if the buffer sizes will not change */
+ if (dev->mtu == new_mtu)
+ return 0;
+
+ dev->mtu = new_mtu;
+
+ if (!netif_running(dev))
+ return 0;
+
+ /* Recevice ring buffer size is needed to be set based on MTU. If MTU is
+ * changed then reinitilisation of the receive ring buffers need to be
+ * done. Hence bring interface down and bring interface back up
+ */
+ sxgbe_release(dev);
+ return sxgbe_open(dev);
+}
+
+static void sxgbe_set_umac_addr(void __iomem *ioaddr, unsigned char *addr,
+ unsigned int reg_n)
+{
+ unsigned long data;
+
+ data = (addr[5] << 8) | addr[4];
+ /* For MAC Addr registers se have to set the Address Enable (AE)
+ * bit that has no effect on the High Reg 0 where the bit 31 (MO)
+ * is RO.
+ */
+ writel(data | SXGBE_HI_REG_AE, ioaddr + SXGBE_ADDR_HIGH(reg_n));
+ data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
+ writel(data, ioaddr + SXGBE_ADDR_LOW(reg_n));
+}
+
+/**
+ * sxgbe_set_rx_mode - entry point for setting different receive mode of
+ * a device. unicast, multicast addressing
+ * @dev : pointer to the device structure
+ * Description:
+ * This function is a driver entry point which gets called by the kernel
+ * whenever different receive mode like unicast, multicast and promiscuous
+ * must be enabled/disabled.
+ * Return value:
+ * void.
+ */
+static void sxgbe_set_rx_mode(struct net_device *dev)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ void __iomem *ioaddr = (void __iomem *)priv->ioaddr;
+ unsigned int value = 0;
+ u32 mc_filter[2];
+ struct netdev_hw_addr *ha;
+ int reg = 1;
+
+ netdev_dbg(dev, "%s: # mcasts %d, # unicast %d\n",
+ __func__, netdev_mc_count(dev), netdev_uc_count(dev));
+
+ if (dev->flags & IFF_PROMISC) {
+ value = SXGBE_FRAME_FILTER_PR;
+
+ } else if ((netdev_mc_count(dev) > SXGBE_HASH_TABLE_SIZE) ||
+ (dev->flags & IFF_ALLMULTI)) {
+ value = SXGBE_FRAME_FILTER_PM; /* pass all multi */
+ writel(0xffffffff, ioaddr + SXGBE_HASH_HIGH);
+ writel(0xffffffff, ioaddr + SXGBE_HASH_LOW);
+
+ } else if (!netdev_mc_empty(dev)) {
+ /* Hash filter for multicast */
+ value = SXGBE_FRAME_FILTER_HMC;
+
+ memset(mc_filter, 0, sizeof(mc_filter));
+ netdev_for_each_mc_addr(ha, dev) {
+ /* The upper 6 bits of the calculated CRC are used to
+ * index the contens of the hash table
+ */
+ int bit_nr = bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26;
+
+ /* The most significant bit determines the register to
+ * use (H/L) while the other 5 bits determine the bit
+ * within the register.
+ */
+ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
+ }
+ writel(mc_filter[0], ioaddr + SXGBE_HASH_LOW);
+ writel(mc_filter[1], ioaddr + SXGBE_HASH_HIGH);
+ }
+
+ /* Handle multiple unicast addresses (perfect filtering) */
+ if (netdev_uc_count(dev) > SXGBE_MAX_PERFECT_ADDRESSES)
+ /* Switch to promiscuous mode if more than 16 addrs
+ * are required
+ */
+ value |= SXGBE_FRAME_FILTER_PR;
+ else {
+ netdev_for_each_uc_addr(ha, dev) {
+ sxgbe_set_umac_addr(ioaddr, ha->addr, reg);
+ reg++;
+ }
+ }
+#ifdef FRAME_FILTER_DEBUG
+ /* Enable Receive all mode (to debug filtering_fail errors) */
+ value |= SXGBE_FRAME_FILTER_RA;
+#endif
+ writel(value, ioaddr + SXGBE_FRAME_FILTER);
+
+ netdev_dbg(dev, "Filter: 0x%08x\n\tHash: HI 0x%08x, LO 0x%08x\n",
+ readl(ioaddr + SXGBE_FRAME_FILTER),
+ readl(ioaddr + SXGBE_HASH_HIGH),
+ readl(ioaddr + SXGBE_HASH_LOW));
+}
+
+/**
+ * sxgbe_config - entry point for changing configuration mode passed on by
+ * ifconfig
+ * @dev : pointer to the device structure
+ * @map : pointer to the device mapping structure
+ * Description:
+ * This function is a driver entry point which gets called by the kernel
+ * whenever some device configuration is changed.
+ * Return value:
+ * This function returns 0 if success and appropriate error otherwise.
+ */
+static int sxgbe_config(struct net_device *dev, struct ifmap *map)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+
+ /* Can't act on a running interface */
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ /* Don't allow changing the I/O address */
+ if (map->base_addr != (unsigned long)priv->ioaddr) {
+ netdev_warn(dev, "can't change I/O address\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* Don't allow changing the IRQ */
+ if (map->irq != priv->irq) {
+ netdev_warn(dev, "not change IRQ number %d\n", priv->irq);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/**
+ * sxgbe_poll_controller - entry point for polling receive by device
+ * @dev : pointer to the device structure
+ * Description:
+ * This function is used by NETCONSOLE and other diagnostic tools
+ * to allow network I/O with interrupts disabled.
+ * Return value:
+ * Void.
+ */
+static void sxgbe_poll_controller(struct net_device *dev)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+
+ disable_irq(priv->irq);
+ sxgbe_rx_interrupt(priv->irq, dev);
+ enable_irq(priv->irq);
+}
+#endif
+
+/* sxgbe_ioctl - Entry point for the Ioctl
+ * @dev: Device pointer.
+ * @rq: An IOCTL specefic structure, that can contain a pointer to
+ * a proprietary structure used to pass information to the driver.
+ * @cmd: IOCTL command
+ * Description:
+ * Currently it supports the phy_mii_ioctl(...) and HW time stamping.
+ */
+static int sxgbe_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(dev);
+ int ret = -EOPNOTSUPP;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ if (!priv->phydev)
+ return -EINVAL;
+ ret = phy_mii_ioctl(priv->phydev, rq, cmd);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static const struct net_device_ops sxgbe_netdev_ops = {
+ .ndo_open = sxgbe_open,
+ .ndo_start_xmit = sxgbe_xmit,
+ .ndo_stop = sxgbe_release,
+ .ndo_get_stats64 = sxgbe_get_stats64,
+ .ndo_change_mtu = sxgbe_change_mtu,
+ .ndo_set_features = sxgbe_set_features,
+ .ndo_set_rx_mode = sxgbe_set_rx_mode,
+ .ndo_tx_timeout = sxgbe_tx_timeout,
+ .ndo_do_ioctl = sxgbe_ioctl,
+ .ndo_set_config = sxgbe_config,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = sxgbe_poll_controller,
+#endif
+ .ndo_set_mac_address = eth_mac_addr,
+};
+
+/* Get the hardware ops */
+static void sxgbe_get_ops(struct sxgbe_ops * const ops_ptr)
+{
+ ops_ptr->mac = sxgbe_get_core_ops();
+ ops_ptr->desc = sxgbe_get_desc_ops();
+ ops_ptr->dma = sxgbe_get_dma_ops();
+ ops_ptr->mtl = sxgbe_get_mtl_ops();
+
+ /* set the MDIO communication Address/Data regisers */
+ ops_ptr->mii.addr = SXGBE_MDIO_SCMD_ADD_REG;
+ ops_ptr->mii.data = SXGBE_MDIO_SCMD_DATA_REG;
+
+ /* Assigning the default link settings
+ * no SXGBE defined default values to be set in registers,
+ * so assigning as 0 for port and duplex
+ */
+ ops_ptr->link.port = 0;
+ ops_ptr->link.duplex = 0;
+ ops_ptr->link.speed = SXGBE_SPEED_10G;
+}
+
+/**
+ * sxgbe_hw_init - Init the GMAC device
+ * @priv: driver private structure
+ * Description: this function checks the HW capability
+ * (if supported) and sets the driver's features.
+ */
+static int sxgbe_hw_init(struct sxgbe_priv_data * const priv)
+{
+ u32 ctrl_ids;
+
+ priv->hw = kmalloc(sizeof(*priv->hw), GFP_KERNEL);
+ if(!priv->hw)
+ return -ENOMEM;
+
+ /* get the hardware ops */
+ sxgbe_get_ops(priv->hw);
+
+ /* get the controller id */
+ ctrl_ids = priv->hw->mac->get_controller_version(priv->ioaddr);
+ priv->hw->ctrl_uid = (ctrl_ids & 0x00ff0000) >> 16;
+ priv->hw->ctrl_id = (ctrl_ids & 0x000000ff);
+ pr_info("user ID: 0x%x, Controller ID: 0x%x\n",
+ priv->hw->ctrl_uid, priv->hw->ctrl_id);
+
+ /* get the H/W features */
+ if (!sxgbe_get_hw_features(priv))
+ pr_info("Hardware features not found\n");
+
+ if (priv->hw_cap.tx_csum_offload)
+ pr_info("TX Checksum offload supported\n");
+
+ if (priv->hw_cap.rx_csum_offload)
+ pr_info("RX Checksum offload supported\n");
+
+ return 0;
+}
+
+/**
+ * sxgbe_drv_probe
+ * @device: device pointer
+ * @plat_dat: platform data pointer
+ * @addr: iobase memory address
+ * Description: this is the main probe function used to
+ * call the alloc_etherdev, allocate the priv structure.
+ */
+struct sxgbe_priv_data *sxgbe_drv_probe(struct device *device,
+ struct sxgbe_plat_data *plat_dat,
+ void __iomem *addr)
+{
+ struct sxgbe_priv_data *priv;
+ struct net_device *ndev;
+ int ret;
+ u8 queue_num;
+
+ ndev = alloc_etherdev_mqs(sizeof(struct sxgbe_priv_data),
+ SXGBE_TX_QUEUES, SXGBE_RX_QUEUES);
+ if (!ndev)
+ return NULL;
+
+ SET_NETDEV_DEV(ndev, device);
+
+ priv = netdev_priv(ndev);
+ priv->device = device;
+ priv->dev = ndev;
+
+ sxgbe_set_ethtool_ops(ndev);
+ priv->plat = plat_dat;
+ priv->ioaddr = addr;
+
+ /* Verify driver arguments */
+ sxgbe_verify_args();
+
+ /* Init MAC and get the capabilities */
+ ret = sxgbe_hw_init(priv);
+ if (ret)
+ goto error_free_netdev;
+
+ /* allocate memory resources for Descriptor rings */
+ ret = txring_mem_alloc(priv);
+ if (ret)
+ goto error_free_netdev;
+
+ ret = rxring_mem_alloc(priv);
+ if (ret)
+ goto error_free_netdev;
+
+ ndev->netdev_ops = &sxgbe_netdev_ops;
+
+ ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+ NETIF_F_RXCSUM | NETIF_F_TSO | NETIF_F_TSO6 |
+ NETIF_F_GRO;
+ ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA;
+ ndev->watchdog_timeo = msecs_to_jiffies(TX_TIMEO);
+
+ /* assign filtering support */
+ ndev->priv_flags |= IFF_UNICAST_FLT;
+
+ priv->msg_enable = netif_msg_init(debug, default_msg_level);
+
+ /* Enable TCP segmentation offload for all DMA channels */
+ if (priv->hw_cap.tcpseg_offload) {
+ SXGBE_FOR_EACH_QUEUE(SXGBE_TX_QUEUES, queue_num) {
+ priv->hw->dma->enable_tso(priv->ioaddr, queue_num);
+ }
+ }
+
+ /* Enable Rx checksum offload */
+ if (priv->hw_cap.rx_csum_offload) {
+ priv->hw->mac->enable_rx_csum(priv->ioaddr);
+ priv->rxcsum_insertion = true;
+ }
+
+ /* Initialise pause frame settings */
+ priv->rx_pause = 1;
+ priv->tx_pause = 1;
+
+ /* Rx Watchdog is available, enable depend on platform data */
+ if (!priv->plat->riwt_off) {
+ priv->use_riwt = 1;
+ pr_info("Enable RX Mitigation via HW Watchdog Timer\n");
+ }
+
+ netif_napi_add(ndev, &priv->napi, sxgbe_poll, 64);
+
+ spin_lock_init(&priv->stats_lock);
+
+ priv->sxgbe_clk = clk_get(priv->device, SXGBE_RESOURCE_NAME);
+ if (IS_ERR(priv->sxgbe_clk)) {
+ netdev_warn(ndev, "%s: warning: cannot get CSR clock\n",
+ __func__);
+ goto error_clk_get;
+ }
+
+ /* If a specific clk_csr value is passed from the platform
+ * this means that the CSR Clock Range selection cannot be
+ * changed at run-time and it is fixed. Viceversa the driver'll try to
+ * set the MDC clock dynamically according to the csr actual
+ * clock input.
+ */
+ if (!priv->plat->clk_csr)
+ sxgbe_clk_csr_set(priv);
+ else
+ priv->clk_csr = priv->plat->clk_csr;
+
+ /* MDIO bus Registration */
+ ret = sxgbe_mdio_register(ndev);
+ if (ret < 0) {
+ netdev_dbg(ndev, "%s: MDIO bus (id: %d) registration failed\n",
+ __func__, priv->plat->bus_id);
+ goto error_mdio_register;
+ }
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ pr_err("%s: ERROR %i registering the device\n", __func__, ret);
+ goto error_netdev_register;
+ }
+
+ sxgbe_check_ether_addr(priv);
+
+ return priv;
+
+error_mdio_register:
+ clk_put(priv->sxgbe_clk);
+error_clk_get:
+error_netdev_register:
+ netif_napi_del(&priv->napi);
+error_free_netdev:
+ free_netdev(ndev);
+
+ return NULL;
+}
+
+/**
+ * sxgbe_drv_remove
+ * @ndev: net device pointer
+ * Description: this function resets the TX/RX processes, disables the MAC RX/TX
+ * changes the link status, releases the DMA descriptor rings.
+ */
+int sxgbe_drv_remove(struct net_device *ndev)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(ndev);
+
+ netdev_info(ndev, "%s: removing driver\n", __func__);
+
+ priv->hw->dma->stop_rx(priv->ioaddr, SXGBE_RX_QUEUES);
+ priv->hw->dma->stop_tx(priv->ioaddr, SXGBE_TX_QUEUES);
+
+ priv->hw->mac->enable_tx(priv->ioaddr, false);
+ priv->hw->mac->enable_rx(priv->ioaddr, false);
+
+ netif_napi_del(&priv->napi);
+
+ sxgbe_mdio_unregister(ndev);
+
+ unregister_netdev(ndev);
+
+ free_netdev(ndev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+int sxgbe_suspend(struct net_device *ndev)
+{
+ return 0;
+}
+
+int sxgbe_resume(struct net_device *ndev)
+{
+ return 0;
+}
+
+int sxgbe_freeze(struct net_device *ndev)
+{
+ return -ENOSYS;
+}
+
+int sxgbe_restore(struct net_device *ndev)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_PM */
+
+/* Driver is configured as Platform driver */
+static int __init sxgbe_init(void)
+{
+ int ret;
+
+ ret = sxgbe_register_platform();
+ if (ret)
+ goto err;
+ return 0;
+err:
+ pr_err("driver registration failed\n");
+ return ret;
+}
+
+static void __exit sxgbe_exit(void)
+{
+ sxgbe_unregister_platform();
+}
+
+module_init(sxgbe_init);
+module_exit(sxgbe_exit);
+
+#ifndef MODULE
+static int __init sxgbe_cmdline_opt(char *str)
+{
+ char *opt;
+
+ if (!str || !*str)
+ return -EINVAL;
+ while ((opt = strsep(&str, ",")) != NULL) {
+ if (!strncmp(opt, "eee_timer:", 6)) {
+ if (kstrtoint(opt + 10, 0, &eee_timer))
+ goto err;
+ }
+ }
+ return 0;
+
+err:
+ pr_err("%s: ERROR broken module parameter conversion\n", __func__);
+ return -EINVAL;
+}
+
+__setup("sxgbeeth=", sxgbe_cmdline_opt);
+#endif /* MODULE */
+
+
+
+MODULE_DESCRIPTION("SAMSUNG 10G/2.5G/1G Ethernet PLATFORM driver");
+
+MODULE_PARM_DESC(debug, "Message Level (-1: default, 0: no output, 16: all)");
+MODULE_PARM_DESC(eee_timer, "EEE-LPI Default LS timer value");
+
+MODULE_AUTHOR("Siva Reddy Kallam <siva.kallam@samsung.com>");
+MODULE_AUTHOR("ByungHo An <bh74.an@samsung.com>");
+MODULE_AUTHOR("Girish K S <ks.giri@samsung.com>");
+MODULE_AUTHOR("Vipul Pandya <vipul.pandya@samsung.com>");
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c
new file mode 100644
index 000000000000..01af2cbb479d
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c
@@ -0,0 +1,244 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/io.h>
+#include <linux/mii.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+#include <linux/slab.h>
+#include <linux/sxgbe_platform.h>
+
+#include "sxgbe_common.h"
+#include "sxgbe_reg.h"
+
+#define SXGBE_SMA_WRITE_CMD 0x01 /* write command */
+#define SXGBE_SMA_PREAD_CMD 0x02 /* post read increament address */
+#define SXGBE_SMA_READ_CMD 0x03 /* read command */
+#define SXGBE_SMA_SKIP_ADDRFRM 0x00040000 /* skip the address frame */
+#define SXGBE_MII_BUSY 0x00800000 /* mii busy */
+
+static int sxgbe_mdio_busy_wait(void __iomem *ioaddr, unsigned int mii_data)
+{
+ unsigned long fin_time = jiffies + 3 * HZ; /* 3 seconds */
+
+ while (!time_after(jiffies, fin_time)) {
+ if (!(readl(ioaddr + mii_data) & SXGBE_MII_BUSY))
+ return 0;
+ cpu_relax();
+ }
+
+ return -EBUSY;
+}
+
+static void sxgbe_mdio_ctrl_data(struct sxgbe_priv_data *sp, u32 cmd,
+ u16 phydata)
+{
+ u32 reg = phydata;
+
+ reg |= (cmd << 16) | SXGBE_SMA_SKIP_ADDRFRM |
+ ((sp->clk_csr & 0x7) << 19) | SXGBE_MII_BUSY;
+ writel(reg, sp->ioaddr + sp->hw->mii.data);
+}
+
+static void sxgbe_mdio_c45(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr,
+ int phyreg, u16 phydata)
+{
+ u32 reg;
+
+ /* set mdio address register */
+ reg = ((phyreg >> 16) & 0x1f) << 21;
+ reg |= (phyaddr << 16) | (phyreg & 0xffff);
+ writel(reg, sp->ioaddr + sp->hw->mii.addr);
+
+ sxgbe_mdio_ctrl_data(sp, cmd, phydata);
+}
+
+static void sxgbe_mdio_c22(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr,
+ int phyreg, u16 phydata)
+{
+ u32 reg;
+
+ writel(1 << phyaddr, sp->ioaddr + SXGBE_MDIO_CLAUSE22_PORT_REG);
+
+ /* set mdio address register */
+ reg = (phyaddr << 16) | (phyreg & 0x1f);
+ writel(reg, sp->ioaddr + sp->hw->mii.addr);
+
+ sxgbe_mdio_ctrl_data(sp, cmd, phydata);
+}
+
+static int sxgbe_mdio_access(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr,
+ int phyreg, u16 phydata)
+{
+ const struct mii_regs *mii = &sp->hw->mii;
+ int rc;
+
+ rc = sxgbe_mdio_busy_wait(sp->ioaddr, mii->data);
+ if (rc < 0)
+ return rc;
+
+ if (phyreg & MII_ADDR_C45) {
+ sxgbe_mdio_c45(sp, cmd, phyaddr, phyreg, phydata);
+ } else {
+ /* Ports 0-3 only support C22. */
+ if (phyaddr >= 4)
+ return -ENODEV;
+
+ sxgbe_mdio_c22(sp, cmd, phyaddr, phyreg, phydata);
+ }
+
+ return sxgbe_mdio_busy_wait(sp->ioaddr, mii->data);
+}
+
+/**
+ * sxgbe_mdio_read
+ * @bus: points to the mii_bus structure
+ * @phyaddr: address of phy port
+ * @phyreg: address of register with in phy register
+ * Description: this function used for C45 and C22 MDIO Read
+ */
+static int sxgbe_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
+{
+ struct net_device *ndev = bus->priv;
+ struct sxgbe_priv_data *priv = netdev_priv(ndev);
+ int rc;
+
+ rc = sxgbe_mdio_access(priv, SXGBE_SMA_READ_CMD, phyaddr, phyreg, 0);
+ if (rc < 0)
+ return rc;
+
+ return readl(priv->ioaddr + priv->hw->mii.data) & 0xffff;
+}
+
+/**
+ * sxgbe_mdio_write
+ * @bus: points to the mii_bus structure
+ * @phyaddr: address of phy port
+ * @phyreg: address of phy registers
+ * @phydata: data to be written into phy register
+ * Description: this function is used for C45 and C22 MDIO write
+ */
+static int sxgbe_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg,
+ u16 phydata)
+{
+ struct net_device *ndev = bus->priv;
+ struct sxgbe_priv_data *priv = netdev_priv(ndev);
+
+ return sxgbe_mdio_access(priv, SXGBE_SMA_WRITE_CMD, phyaddr, phyreg,
+ phydata);
+}
+
+int sxgbe_mdio_register(struct net_device *ndev)
+{
+ struct mii_bus *mdio_bus;
+ struct sxgbe_priv_data *priv = netdev_priv(ndev);
+ struct sxgbe_mdio_bus_data *mdio_data = priv->plat->mdio_bus_data;
+ int err, phy_addr;
+ int *irqlist;
+ bool act;
+
+ /* allocate the new mdio bus */
+ mdio_bus = mdiobus_alloc();
+ if (!mdio_bus) {
+ netdev_err(ndev, "%s: mii bus allocation failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (mdio_data->irqs)
+ irqlist = mdio_data->irqs;
+ else
+ irqlist = priv->mii_irq;
+
+ /* assign mii bus fields */
+ mdio_bus->name = "samsxgbe";
+ mdio_bus->read = &sxgbe_mdio_read;
+ mdio_bus->write = &sxgbe_mdio_write;
+ snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%x",
+ mdio_bus->name, priv->plat->bus_id);
+ mdio_bus->priv = ndev;
+ mdio_bus->phy_mask = mdio_data->phy_mask;
+ mdio_bus->parent = priv->device;
+
+ /* register with kernel subsystem */
+ err = mdiobus_register(mdio_bus);
+ if (err != 0) {
+ netdev_err(ndev, "mdiobus register failed\n");
+ goto mdiobus_err;
+ }
+
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+ struct phy_device *phy = mdio_bus->phy_map[phy_addr];
+
+ if (phy) {
+ char irq_num[4];
+ char *irq_str;
+ /* If an IRQ was provided to be assigned after
+ * the bus probe, do it here.
+ */
+ if ((mdio_data->irqs == NULL) &&
+ (mdio_data->probed_phy_irq > 0)) {
+ irqlist[phy_addr] = mdio_data->probed_phy_irq;
+ phy->irq = mdio_data->probed_phy_irq;
+ }
+
+ /* If we're going to bind the MAC to this PHY bus,
+ * and no PHY number was provided to the MAC,
+ * use the one probed here.
+ */
+ if (priv->plat->phy_addr == -1)
+ priv->plat->phy_addr = phy_addr;
+
+ act = (priv->plat->phy_addr == phy_addr);
+ switch (phy->irq) {
+ case PHY_POLL:
+ irq_str = "POLL";
+ break;
+ case PHY_IGNORE_INTERRUPT:
+ irq_str = "IGNORE";
+ break;
+ default:
+ sprintf(irq_num, "%d", phy->irq);
+ irq_str = irq_num;
+ break;
+ }
+ 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" : "");
+ }
+ }
+
+ priv->mii = mdio_bus;
+
+ return 0;
+
+mdiobus_err:
+ mdiobus_free(mdio_bus);
+ return err;
+}
+
+int sxgbe_mdio_unregister(struct net_device *ndev)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(ndev);
+
+ if (!priv->mii)
+ return 0;
+
+ mdiobus_unregister(priv->mii);
+ priv->mii->priv = NULL;
+ mdiobus_free(priv->mii);
+ priv->mii = NULL;
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_mtl.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mtl.c
new file mode 100644
index 000000000000..324681c2bb74
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mtl.c
@@ -0,0 +1,254 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/jiffies.h>
+
+#include "sxgbe_mtl.h"
+#include "sxgbe_reg.h"
+
+static void sxgbe_mtl_init(void __iomem *ioaddr, unsigned int etsalg,
+ unsigned int raa)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_OP_MODE_REG);
+ reg_val &= ETS_RST;
+
+ /* ETS Algorith */
+ switch (etsalg & SXGBE_MTL_OPMODE_ESTMASK) {
+ case ETS_WRR:
+ reg_val &= ETS_WRR;
+ break;
+ case ETS_WFQ:
+ reg_val |= ETS_WFQ;
+ break;
+ case ETS_DWRR:
+ reg_val |= ETS_DWRR;
+ break;
+ }
+ writel(reg_val, ioaddr + SXGBE_MTL_OP_MODE_REG);
+
+ switch (raa & SXGBE_MTL_OPMODE_RAAMASK) {
+ case RAA_SP:
+ reg_val &= RAA_SP;
+ break;
+ case RAA_WSP:
+ reg_val |= RAA_WSP;
+ break;
+ }
+ writel(reg_val, ioaddr + SXGBE_MTL_OP_MODE_REG);
+}
+
+/* For Dynamic DMA channel mapping for Rx queue */
+static void sxgbe_mtl_dma_dm_rxqueue(void __iomem *ioaddr)
+{
+ writel(RX_QUEUE_DYNAMIC, ioaddr + SXGBE_MTL_RXQ_DMAMAP0_REG);
+ writel(RX_QUEUE_DYNAMIC, ioaddr + SXGBE_MTL_RXQ_DMAMAP1_REG);
+ writel(RX_QUEUE_DYNAMIC, ioaddr + SXGBE_MTL_RXQ_DMAMAP2_REG);
+}
+
+static void sxgbe_mtl_set_txfifosize(void __iomem *ioaddr, int queue_num,
+ int queue_fifo)
+{
+ u32 fifo_bits, reg_val;
+
+ /* 0 means 256 bytes */
+ fifo_bits = (queue_fifo / SXGBE_MTL_TX_FIFO_DIV) - 1;
+ reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
+ reg_val |= (fifo_bits << SXGBE_MTL_FIFO_LSHIFT);
+ writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
+}
+
+static void sxgbe_mtl_set_rxfifosize(void __iomem *ioaddr, int queue_num,
+ int queue_fifo)
+{
+ u32 fifo_bits, reg_val;
+
+ /* 0 means 256 bytes */
+ fifo_bits = (queue_fifo / SXGBE_MTL_RX_FIFO_DIV)-1;
+ reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+ reg_val |= (fifo_bits << SXGBE_MTL_FIFO_LSHIFT);
+ writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+}
+
+static void sxgbe_mtl_enable_txqueue(void __iomem *ioaddr, int queue_num)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
+ reg_val |= SXGBE_MTL_ENABLE_QUEUE;
+ writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
+}
+
+static void sxgbe_mtl_disable_txqueue(void __iomem *ioaddr, int queue_num)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
+ reg_val &= ~SXGBE_MTL_ENABLE_QUEUE;
+ writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
+}
+
+static void sxgbe_mtl_fc_active(void __iomem *ioaddr, int queue_num,
+ int threshold)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+ reg_val &= ~(SXGBE_MTL_FCMASK << RX_FC_ACTIVE);
+ reg_val |= (threshold << RX_FC_ACTIVE);
+
+ writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+}
+
+static void sxgbe_mtl_fc_enable(void __iomem *ioaddr, int queue_num)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+ reg_val |= SXGBE_MTL_ENABLE_FC;
+ writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+}
+
+static void sxgbe_mtl_fc_deactive(void __iomem *ioaddr, int queue_num,
+ int threshold)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+ reg_val &= ~(SXGBE_MTL_FCMASK << RX_FC_DEACTIVE);
+ reg_val |= (threshold << RX_FC_DEACTIVE);
+
+ writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+}
+
+static void sxgbe_mtl_fep_enable(void __iomem *ioaddr, int queue_num)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+ reg_val |= SXGBE_MTL_RXQ_OP_FEP;
+
+ writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+}
+
+static void sxgbe_mtl_fep_disable(void __iomem *ioaddr, int queue_num)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+ reg_val &= ~(SXGBE_MTL_RXQ_OP_FEP);
+
+ writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+}
+
+static void sxgbe_mtl_fup_enable(void __iomem *ioaddr, int queue_num)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+ reg_val |= SXGBE_MTL_RXQ_OP_FUP;
+
+ writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+}
+
+static void sxgbe_mtl_fup_disable(void __iomem *ioaddr, int queue_num)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+ reg_val &= ~(SXGBE_MTL_RXQ_OP_FUP);
+
+ writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+}
+
+
+static void sxgbe_set_tx_mtl_mode(void __iomem *ioaddr, int queue_num,
+ int tx_mode)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
+ /* TX specific MTL mode settings */
+ if (tx_mode == SXGBE_MTL_SFMODE) {
+ reg_val |= SXGBE_MTL_SFMODE;
+ } else {
+ /* set the TTC values */
+ if (tx_mode <= 64)
+ reg_val |= MTL_CONTROL_TTC_64;
+ else if (tx_mode <= 96)
+ reg_val |= MTL_CONTROL_TTC_96;
+ else if (tx_mode <= 128)
+ reg_val |= MTL_CONTROL_TTC_128;
+ else if (tx_mode <= 192)
+ reg_val |= MTL_CONTROL_TTC_192;
+ else if (tx_mode <= 256)
+ reg_val |= MTL_CONTROL_TTC_256;
+ else if (tx_mode <= 384)
+ reg_val |= MTL_CONTROL_TTC_384;
+ else
+ reg_val |= MTL_CONTROL_TTC_512;
+ }
+
+ /* write into TXQ operation register */
+ writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
+}
+
+static void sxgbe_set_rx_mtl_mode(void __iomem *ioaddr, int queue_num,
+ int rx_mode)
+{
+ u32 reg_val;
+
+ reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+ /* RX specific MTL mode settings */
+ if (rx_mode == SXGBE_RX_MTL_SFMODE) {
+ reg_val |= SXGBE_RX_MTL_SFMODE;
+ } else {
+ if (rx_mode <= 64)
+ reg_val |= MTL_CONTROL_RTC_64;
+ else if (rx_mode <= 96)
+ reg_val |= MTL_CONTROL_RTC_96;
+ else if (rx_mode <= 128)
+ reg_val |= MTL_CONTROL_RTC_128;
+ }
+
+ /* write into RXQ operation register */
+ writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
+}
+
+static const struct sxgbe_mtl_ops mtl_ops = {
+ .mtl_set_txfifosize = sxgbe_mtl_set_txfifosize,
+ .mtl_set_rxfifosize = sxgbe_mtl_set_rxfifosize,
+ .mtl_enable_txqueue = sxgbe_mtl_enable_txqueue,
+ .mtl_disable_txqueue = sxgbe_mtl_disable_txqueue,
+ .mtl_dynamic_dma_rxqueue = sxgbe_mtl_dma_dm_rxqueue,
+ .set_tx_mtl_mode = sxgbe_set_tx_mtl_mode,
+ .set_rx_mtl_mode = sxgbe_set_rx_mtl_mode,
+ .mtl_init = sxgbe_mtl_init,
+ .mtl_fc_active = sxgbe_mtl_fc_active,
+ .mtl_fc_deactive = sxgbe_mtl_fc_deactive,
+ .mtl_fc_enable = sxgbe_mtl_fc_enable,
+ .mtl_fep_enable = sxgbe_mtl_fep_enable,
+ .mtl_fep_disable = sxgbe_mtl_fep_disable,
+ .mtl_fup_enable = sxgbe_mtl_fup_enable,
+ .mtl_fup_disable = sxgbe_mtl_fup_disable
+};
+
+const struct sxgbe_mtl_ops *sxgbe_get_mtl_ops(void)
+{
+ return &mtl_ops;
+}
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_mtl.h b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mtl.h
new file mode 100644
index 000000000000..7e4810c4137e
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mtl.h
@@ -0,0 +1,104 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __SXGBE_MTL_H__
+#define __SXGBE_MTL_H__
+
+#define SXGBE_MTL_OPMODE_ESTMASK 0x3
+#define SXGBE_MTL_OPMODE_RAAMASK 0x1
+#define SXGBE_MTL_FCMASK 0x7
+#define SXGBE_MTL_TX_FIFO_DIV 256
+#define SXGBE_MTL_RX_FIFO_DIV 256
+
+#define SXGBE_MTL_RXQ_OP_FEP BIT(4)
+#define SXGBE_MTL_RXQ_OP_FUP BIT(3)
+#define SXGBE_MTL_ENABLE_FC 0x80
+
+#define ETS_WRR 0xFFFFFF9F
+#define ETS_RST 0xFFFFFF9F
+#define ETS_WFQ 0x00000020
+#define ETS_DWRR 0x00000040
+#define RAA_SP 0xFFFFFFFB
+#define RAA_WSP 0x00000004
+
+#define RX_QUEUE_DYNAMIC 0x80808080
+#define RX_FC_ACTIVE 8
+#define RX_FC_DEACTIVE 13
+
+enum ttc_control {
+ MTL_CONTROL_TTC_64 = 0x00000000,
+ MTL_CONTROL_TTC_96 = 0x00000020,
+ MTL_CONTROL_TTC_128 = 0x00000030,
+ MTL_CONTROL_TTC_192 = 0x00000040,
+ MTL_CONTROL_TTC_256 = 0x00000050,
+ MTL_CONTROL_TTC_384 = 0x00000060,
+ MTL_CONTROL_TTC_512 = 0x00000070,
+};
+
+enum rtc_control {
+ MTL_CONTROL_RTC_64 = 0x00000000,
+ MTL_CONTROL_RTC_96 = 0x00000002,
+ MTL_CONTROL_RTC_128 = 0x00000003,
+};
+
+enum flow_control_th {
+ MTL_FC_FULL_1K = 0x00000000,
+ MTL_FC_FULL_2K = 0x00000001,
+ MTL_FC_FULL_4K = 0x00000002,
+ MTL_FC_FULL_5K = 0x00000003,
+ MTL_FC_FULL_6K = 0x00000004,
+ MTL_FC_FULL_8K = 0x00000005,
+ MTL_FC_FULL_16K = 0x00000006,
+ MTL_FC_FULL_24K = 0x00000007,
+};
+
+struct sxgbe_mtl_ops {
+ void (*mtl_init)(void __iomem *ioaddr, unsigned int etsalg,
+ unsigned int raa);
+
+ void (*mtl_set_txfifosize)(void __iomem *ioaddr, int queue_num,
+ int mtl_fifo);
+
+ void (*mtl_set_rxfifosize)(void __iomem *ioaddr, int queue_num,
+ int queue_fifo);
+
+ void (*mtl_enable_txqueue)(void __iomem *ioaddr, int queue_num);
+
+ void (*mtl_disable_txqueue)(void __iomem *ioaddr, int queue_num);
+
+ void (*set_tx_mtl_mode)(void __iomem *ioaddr, int queue_num,
+ int tx_mode);
+
+ void (*set_rx_mtl_mode)(void __iomem *ioaddr, int queue_num,
+ int rx_mode);
+
+ void (*mtl_dynamic_dma_rxqueue)(void __iomem *ioaddr);
+
+ void (*mtl_fc_active)(void __iomem *ioaddr, int queue_num,
+ int threshold);
+
+ void (*mtl_fc_deactive)(void __iomem *ioaddr, int queue_num,
+ int threshold);
+
+ void (*mtl_fc_enable)(void __iomem *ioaddr, int queue_num);
+
+ void (*mtl_fep_enable)(void __iomem *ioaddr, int queue_num);
+
+ void (*mtl_fep_disable)(void __iomem *ioaddr, int queue_num);
+
+ void (*mtl_fup_enable)(void __iomem *ioaddr, int queue_num);
+
+ void (*mtl_fup_disable)(void __iomem *ioaddr, int queue_num);
+};
+
+const struct sxgbe_mtl_ops *sxgbe_get_mtl_ops(void);
+
+#endif /* __SXGBE_MTL_H__ */
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c
new file mode 100644
index 000000000000..b147d469a799
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c
@@ -0,0 +1,259 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/etherdevice.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_net.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/sxgbe_platform.h>
+
+#include "sxgbe_common.h"
+#include "sxgbe_reg.h"
+
+#ifdef CONFIG_OF
+static int sxgbe_probe_config_dt(struct platform_device *pdev,
+ struct sxgbe_plat_data *plat,
+ const char **mac)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct sxgbe_dma_cfg *dma_cfg;
+
+ if (!np)
+ return -ENODEV;
+
+ *mac = of_get_mac_address(np);
+ plat->interface = of_get_phy_mode(np);
+
+ plat->bus_id = of_alias_get_id(np, "ethernet");
+ if (plat->bus_id < 0)
+ plat->bus_id = 0;
+
+ plat->mdio_bus_data = devm_kzalloc(&pdev->dev,
+ sizeof(*plat->mdio_bus_data),
+ GFP_KERNEL);
+
+ dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), GFP_KERNEL);
+ if (!dma_cfg)
+ return -ENOMEM;
+
+ plat->dma_cfg = dma_cfg;
+ of_property_read_u32(np, "samsung,pbl", &dma_cfg->pbl);
+ if (of_property_read_u32(np, "samsung,burst-map", &dma_cfg->burst_map) == 0)
+ dma_cfg->fixed_burst = true;
+
+ return 0;
+}
+#else
+static int sxgbe_probe_config_dt(struct platform_device *pdev,
+ struct sxgbe_plat_data *plat,
+ const char **mac)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_OF */
+
+/**
+ * sxgbe_platform_probe
+ * @pdev: platform device pointer
+ * Description: platform_device probe function. It allocates
+ * the necessary resources and invokes the main to init
+ * the net device, register the mdio bus etc.
+ */
+static int sxgbe_platform_probe(struct platform_device *pdev)
+{
+ int ret;
+ int i, chan;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ void __iomem *addr;
+ struct sxgbe_priv_data *priv = NULL;
+ struct sxgbe_plat_data *plat_dat = NULL;
+ const char *mac = NULL;
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct device_node *node = dev->of_node;
+
+ /* Get memory resource */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ goto err_out;
+
+ addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(addr))
+ return PTR_ERR(addr);
+
+ if (pdev->dev.of_node) {
+ plat_dat = devm_kzalloc(&pdev->dev,
+ sizeof(struct sxgbe_plat_data),
+ GFP_KERNEL);
+ if (!plat_dat)
+ return -ENOMEM;
+
+ ret = sxgbe_probe_config_dt(pdev, plat_dat, &mac);
+ if (ret) {
+ pr_err("%s: main dt probe failed\n", __func__);
+ return ret;
+ }
+ }
+
+ /* Get MAC address if available (DT) */
+ if (mac)
+ ether_addr_copy(priv->dev->dev_addr, mac);
+
+ priv = sxgbe_drv_probe(&(pdev->dev), plat_dat, addr);
+ if (!priv) {
+ pr_err("%s: main driver probe failed\n", __func__);
+ goto err_out;
+ }
+
+ /* Get the SXGBE common INT information */
+ priv->irq = irq_of_parse_and_map(node, 0);
+ if (priv->irq <= 0) {
+ dev_err(dev, "sxgbe common irq parsing failed\n");
+ goto err_drv_remove;
+ }
+
+ /* Get the TX/RX IRQ numbers */
+ for (i = 0, chan = 1; i < SXGBE_TX_QUEUES; i++) {
+ priv->txq[i]->irq_no = irq_of_parse_and_map(node, chan++);
+ if (priv->txq[i]->irq_no <= 0) {
+ dev_err(dev, "sxgbe tx irq parsing failed\n");
+ goto err_tx_irq_unmap;
+ }
+ }
+
+ for (i = 0; i < SXGBE_RX_QUEUES; i++) {
+ priv->rxq[i]->irq_no = irq_of_parse_and_map(node, chan++);
+ if (priv->rxq[i]->irq_no <= 0) {
+ dev_err(dev, "sxgbe rx irq parsing failed\n");
+ goto err_rx_irq_unmap;
+ }
+ }
+
+ priv->lpi_irq = irq_of_parse_and_map(node, chan);
+ if (priv->lpi_irq <= 0) {
+ dev_err(dev, "sxgbe lpi irq parsing failed\n");
+ goto err_rx_irq_unmap;
+ }
+
+ platform_set_drvdata(pdev, priv->dev);
+
+ pr_debug("platform driver registration completed\n");
+
+ return 0;
+
+err_rx_irq_unmap:
+ while (--i)
+ irq_dispose_mapping(priv->rxq[i]->irq_no);
+ i = SXGBE_TX_QUEUES;
+err_tx_irq_unmap:
+ while (--i)
+ irq_dispose_mapping(priv->txq[i]->irq_no);
+ irq_dispose_mapping(priv->irq);
+err_drv_remove:
+ sxgbe_drv_remove(ndev);
+err_out:
+ return -ENODEV;
+}
+
+/**
+ * sxgbe_platform_remove
+ * @pdev: platform device pointer
+ * Description: this function calls the main to free the net resources
+ * and calls the platforms hook and release the resources (e.g. mem).
+ */
+static int sxgbe_platform_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ int ret = sxgbe_drv_remove(ndev);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int sxgbe_platform_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+
+ return sxgbe_suspend(ndev);
+}
+
+static int sxgbe_platform_resume(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+
+ return sxgbe_resume(ndev);
+}
+
+static int sxgbe_platform_freeze(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+
+ return sxgbe_freeze(ndev);
+}
+
+static int sxgbe_platform_restore(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+
+ return sxgbe_restore(ndev);
+}
+
+static const struct dev_pm_ops sxgbe_platform_pm_ops = {
+ .suspend = sxgbe_platform_suspend,
+ .resume = sxgbe_platform_resume,
+ .freeze = sxgbe_platform_freeze,
+ .thaw = sxgbe_platform_restore,
+ .restore = sxgbe_platform_restore,
+};
+#else
+static const struct dev_pm_ops sxgbe_platform_pm_ops;
+#endif /* CONFIG_PM */
+
+static const struct of_device_id sxgbe_dt_ids[] = {
+ { .compatible = "samsung,sxgbe-v2.0a"},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sxgbe_dt_ids);
+
+static struct platform_driver sxgbe_platform_driver = {
+ .probe = sxgbe_platform_probe,
+ .remove = sxgbe_platform_remove,
+ .driver = {
+ .name = SXGBE_RESOURCE_NAME,
+ .owner = THIS_MODULE,
+ .pm = &sxgbe_platform_pm_ops,
+ .of_match_table = of_match_ptr(sxgbe_dt_ids),
+ },
+};
+
+int sxgbe_register_platform(void)
+{
+ int err;
+
+ err = platform_driver_register(&sxgbe_platform_driver);
+ if (err)
+ pr_err("failed to register the platform driver\n");
+
+ return err;
+}
+
+void sxgbe_unregister_platform(void)
+{
+ platform_driver_unregister(&sxgbe_platform_driver);
+}
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_reg.h b/drivers/net/ethernet/samsung/sxgbe/sxgbe_reg.h
new file mode 100644
index 000000000000..5a89acb4c505
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_reg.h
@@ -0,0 +1,488 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __SXGBE_REGMAP_H__
+#define __SXGBE_REGMAP_H__
+
+/* SXGBE MAC Registers */
+#define SXGBE_CORE_TX_CONFIG_REG 0x0000
+#define SXGBE_CORE_RX_CONFIG_REG 0x0004
+#define SXGBE_CORE_PKT_FILTER_REG 0x0008
+#define SXGBE_CORE_WATCHDOG_TIMEOUT_REG 0x000C
+#define SXGBE_CORE_HASH_TABLE_REG0 0x0010
+#define SXGBE_CORE_HASH_TABLE_REG1 0x0014
+#define SXGBE_CORE_HASH_TABLE_REG2 0x0018
+#define SXGBE_CORE_HASH_TABLE_REG3 0x001C
+#define SXGBE_CORE_HASH_TABLE_REG4 0x0020
+#define SXGBE_CORE_HASH_TABLE_REG5 0x0024
+#define SXGBE_CORE_HASH_TABLE_REG6 0x0028
+#define SXGBE_CORE_HASH_TABLE_REG7 0x002C
+
+/* EEE-LPI Registers */
+#define SXGBE_CORE_LPI_CTRL_STATUS 0x00D0
+#define SXGBE_CORE_LPI_TIMER_CTRL 0x00D4
+
+/* VLAN Specific Registers */
+#define SXGBE_CORE_VLAN_TAG_REG 0x0050
+#define SXGBE_CORE_VLAN_HASHTAB_REG 0x0058
+#define SXGBE_CORE_VLAN_INSCTL_REG 0x0060
+#define SXGBE_CORE_VLAN_INNERCTL_REG 0x0064
+#define SXGBE_CORE_RX_ETHTYPE_MATCH_REG 0x006C
+
+/* Flow Contol Registers */
+#define SXGBE_CORE_TX_Q0_FLOWCTL_REG 0x0070
+#define SXGBE_CORE_TX_Q1_FLOWCTL_REG 0x0074
+#define SXGBE_CORE_TX_Q2_FLOWCTL_REG 0x0078
+#define SXGBE_CORE_TX_Q3_FLOWCTL_REG 0x007C
+#define SXGBE_CORE_TX_Q4_FLOWCTL_REG 0x0080
+#define SXGBE_CORE_TX_Q5_FLOWCTL_REG 0x0084
+#define SXGBE_CORE_TX_Q6_FLOWCTL_REG 0x0088
+#define SXGBE_CORE_TX_Q7_FLOWCTL_REG 0x008C
+#define SXGBE_CORE_RX_FLOWCTL_REG 0x0090
+#define SXGBE_CORE_RX_CTL0_REG 0x00A0
+#define SXGBE_CORE_RX_CTL1_REG 0x00A4
+#define SXGBE_CORE_RX_CTL2_REG 0x00A8
+#define SXGBE_CORE_RX_CTL3_REG 0x00AC
+
+/* Interrupt Registers */
+#define SXGBE_CORE_INT_STATUS_REG 0x00B0
+#define SXGBE_CORE_INT_ENABLE_REG 0x00B4
+#define SXGBE_CORE_RXTX_ERR_STATUS_REG 0x00B8
+#define SXGBE_CORE_PMT_CTL_STATUS_REG 0x00C0
+#define SXGBE_CORE_RWK_PKT_FILTER_REG 0x00C4
+#define SXGBE_CORE_VERSION_REG 0x0110
+#define SXGBE_CORE_DEBUG_REG 0x0114
+#define SXGBE_CORE_HW_FEA_REG(index) (0x011C + index * 4)
+
+/* SMA(MDIO) module registers */
+#define SXGBE_MDIO_SCMD_ADD_REG 0x0200
+#define SXGBE_MDIO_SCMD_DATA_REG 0x0204
+#define SXGBE_MDIO_CCMD_WADD_REG 0x0208
+#define SXGBE_MDIO_CCMD_WDATA_REG 0x020C
+#define SXGBE_MDIO_CSCAN_PORT_REG 0x0210
+#define SXGBE_MDIO_INT_STATUS_REG 0x0214
+#define SXGBE_MDIO_INT_ENABLE_REG 0x0218
+#define SXGBE_MDIO_PORT_CONDCON_REG 0x021C
+#define SXGBE_MDIO_CLAUSE22_PORT_REG 0x0220
+
+/* port specific, addr = 0-3 */
+#define SXGBE_MDIO_DEV_BASE_REG 0x0230
+#define SXGBE_MDIO_PORT_DEV_REG(addr) \
+ (SXGBE_MDIO_DEV_BASE_REG + (0x10 * addr) + 0x0)
+#define SXGBE_MDIO_PORT_LSTATUS_REG(addr) \
+ (SXGBE_MDIO_DEV_BASE_REG + (0x10 * addr) + 0x4)
+#define SXGBE_MDIO_PORT_ALIVE_REG(addr) \
+ (SXGBE_MDIO_DEV_BASE_REG + (0x10 * addr) + 0x8)
+
+#define SXGBE_CORE_GPIO_CTL_REG 0x0278
+#define SXGBE_CORE_GPIO_STATUS_REG 0x027C
+
+/* Address registers for filtering */
+#define SXGBE_CORE_ADD_BASE_REG 0x0300
+
+/* addr = 0-31 */
+#define SXGBE_CORE_ADD_HIGHOFFSET(addr) \
+ (SXGBE_CORE_ADD_BASE_REG + (0x8 * addr) + 0x0)
+#define SXGBE_CORE_ADD_LOWOFFSET(addr) \
+ (SXGBE_CORE_ADD_BASE_REG + (0x8 * addr) + 0x4)
+
+/* SXGBE MMC registers */
+#define SXGBE_MMC_CTL_REG 0x0800
+#define SXGBE_MMC_RXINT_STATUS_REG 0x0804
+#define SXGBE_MMC_TXINT_STATUS_REG 0x0808
+#define SXGBE_MMC_RXINT_ENABLE_REG 0x080C
+#define SXGBE_MMC_TXINT_ENABLE_REG 0x0810
+
+/* TX specific counters */
+#define SXGBE_MMC_TXOCTETHI_GBCNT_REG 0x0814
+#define SXGBE_MMC_TXOCTETLO_GBCNT_REG 0x0818
+#define SXGBE_MMC_TXFRAMELO_GBCNT_REG 0x081C
+#define SXGBE_MMC_TXFRAMEHI_GBCNT_REG 0x0820
+#define SXGBE_MMC_TXBROADLO_GCNT_REG 0x0824
+#define SXGBE_MMC_TXBROADHI_GCNT_REG 0x0828
+#define SXGBE_MMC_TXMULTILO_GCNT_REG 0x082C
+#define SXGBE_MMC_TXMULTIHI_GCNT_REG 0x0830
+#define SXGBE_MMC_TX64LO_GBCNT_REG 0x0834
+#define SXGBE_MMC_TX64HI_GBCNT_REG 0x0838
+#define SXGBE_MMC_TX65TO127LO_GBCNT_REG 0x083C
+#define SXGBE_MMC_TX65TO127HI_GBCNT_REG 0x0840
+#define SXGBE_MMC_TX128TO255LO_GBCNT_REG 0x0844
+#define SXGBE_MMC_TX128TO255HI_GBCNT_REG 0x0848
+#define SXGBE_MMC_TX256TO511LO_GBCNT_REG 0x084C
+#define SXGBE_MMC_TX256TO511HI_GBCNT_REG 0x0850
+#define SXGBE_MMC_TX512TO1023LO_GBCNT_REG 0x0854
+#define SXGBE_MMC_TX512TO1023HI_GBCNT_REG 0x0858
+#define SXGBE_MMC_TX1023TOMAXLO_GBCNT_REG 0x085C
+#define SXGBE_MMC_TX1023TOMAXHI_GBCNT_REG 0x0860
+#define SXGBE_MMC_TXUNICASTLO_GBCNT_REG 0x0864
+#define SXGBE_MMC_TXUNICASTHI_GBCNT_REG 0x0868
+#define SXGBE_MMC_TXMULTILO_GBCNT_REG 0x086C
+#define SXGBE_MMC_TXMULTIHI_GBCNT_REG 0x0870
+#define SXGBE_MMC_TXBROADLO_GBCNT_REG 0x0874
+#define SXGBE_MMC_TXBROADHI_GBCNT_REG 0x0878
+#define SXGBE_MMC_TXUFLWLO_GBCNT_REG 0x087C
+#define SXGBE_MMC_TXUFLWHI_GBCNT_REG 0x0880
+#define SXGBE_MMC_TXOCTETLO_GCNT_REG 0x0884
+#define SXGBE_MMC_TXOCTETHI_GCNT_REG 0x0888
+#define SXGBE_MMC_TXFRAMELO_GCNT_REG 0x088C
+#define SXGBE_MMC_TXFRAMEHI_GCNT_REG 0x0890
+#define SXGBE_MMC_TXPAUSELO_CNT_REG 0x0894
+#define SXGBE_MMC_TXPAUSEHI_CNT_REG 0x0898
+#define SXGBE_MMC_TXVLANLO_GCNT_REG 0x089C
+#define SXGBE_MMC_TXVLANHI_GCNT_REG 0x08A0
+
+/* RX specific counters */
+#define SXGBE_MMC_RXFRAMELO_GBCNT_REG 0x0900
+#define SXGBE_MMC_RXFRAMEHI_GBCNT_REG 0x0904
+#define SXGBE_MMC_RXOCTETLO_GBCNT_REG 0x0908
+#define SXGBE_MMC_RXOCTETHI_GBCNT_REG 0x090C
+#define SXGBE_MMC_RXOCTETLO_GCNT_REG 0x0910
+#define SXGBE_MMC_RXOCTETHI_GCNT_REG 0x0914
+#define SXGBE_MMC_RXBROADLO_GCNT_REG 0x0918
+#define SXGBE_MMC_RXBROADHI_GCNT_REG 0x091C
+#define SXGBE_MMC_RXMULTILO_GCNT_REG 0x0920
+#define SXGBE_MMC_RXMULTIHI_GCNT_REG 0x0924
+#define SXGBE_MMC_RXCRCERRLO_REG 0x0928
+#define SXGBE_MMC_RXCRCERRHI_REG 0x092C
+#define SXGBE_MMC_RXSHORT64BFRAME_ERR_REG 0x0930
+#define SXGBE_MMC_RXJABBERERR_REG 0x0934
+#define SXGBE_MMC_RXSHORT64BFRAME_COR_REG 0x0938
+#define SXGBE_MMC_RXOVERMAXFRAME_COR_REG 0x093C
+#define SXGBE_MMC_RX64LO_GBCNT_REG 0x0940
+#define SXGBE_MMC_RX64HI_GBCNT_REG 0x0944
+#define SXGBE_MMC_RX65TO127LO_GBCNT_REG 0x0948
+#define SXGBE_MMC_RX65TO127HI_GBCNT_REG 0x094C
+#define SXGBE_MMC_RX128TO255LO_GBCNT_REG 0x0950
+#define SXGBE_MMC_RX128TO255HI_GBCNT_REG 0x0954
+#define SXGBE_MMC_RX256TO511LO_GBCNT_REG 0x0958
+#define SXGBE_MMC_RX256TO511HI_GBCNT_REG 0x095C
+#define SXGBE_MMC_RX512TO1023LO_GBCNT_REG 0x0960
+#define SXGBE_MMC_RX512TO1023HI_GBCNT_REG 0x0964
+#define SXGBE_MMC_RX1023TOMAXLO_GBCNT_REG 0x0968
+#define SXGBE_MMC_RX1023TOMAXHI_GBCNT_REG 0x096C
+#define SXGBE_MMC_RXUNICASTLO_GCNT_REG 0x0970
+#define SXGBE_MMC_RXUNICASTHI_GCNT_REG 0x0974
+#define SXGBE_MMC_RXLENERRLO_REG 0x0978
+#define SXGBE_MMC_RXLENERRHI_REG 0x097C
+#define SXGBE_MMC_RXOUTOFRANGETYPELO_REG 0x0980
+#define SXGBE_MMC_RXOUTOFRANGETYPEHI_REG 0x0984
+#define SXGBE_MMC_RXPAUSELO_CNT_REG 0x0988
+#define SXGBE_MMC_RXPAUSEHI_CNT_REG 0x098C
+#define SXGBE_MMC_RXFIFOOVERFLOWLO_GBCNT_REG 0x0990
+#define SXGBE_MMC_RXFIFOOVERFLOWHI_GBCNT_REG 0x0994
+#define SXGBE_MMC_RXVLANLO_GBCNT_REG 0x0998
+#define SXGBE_MMC_RXVLANHI_GBCNT_REG 0x099C
+#define SXGBE_MMC_RXWATCHDOG_ERR_REG 0x09A0
+
+/* L3/L4 function registers */
+#define SXGBE_CORE_L34_ADDCTL_REG 0x0C00
+#define SXGBE_CORE_L34_ADDCTL_REG 0x0C00
+#define SXGBE_CORE_L34_DATA_REG 0x0C04
+
+/* ARP registers */
+#define SXGBE_CORE_ARP_ADD_REG 0x0C10
+
+/* RSS registers */
+#define SXGBE_CORE_RSS_CTL_REG 0x0C80
+#define SXGBE_CORE_RSS_ADD_REG 0x0C88
+#define SXGBE_CORE_RSS_DATA_REG 0x0C8C
+
+/* RSS control register bits */
+#define SXGBE_CORE_RSS_CTL_UDP4TE BIT(3)
+#define SXGBE_CORE_RSS_CTL_TCP4TE BIT(2)
+#define SXGBE_CORE_RSS_CTL_IP2TE BIT(1)
+#define SXGBE_CORE_RSS_CTL_RSSE BIT(0)
+
+/* IEEE 1588 registers */
+#define SXGBE_CORE_TSTAMP_CTL_REG 0x0D00
+#define SXGBE_CORE_SUBSEC_INC_REG 0x0D04
+#define SXGBE_CORE_SYSTIME_SEC_REG 0x0D0C
+#define SXGBE_CORE_SYSTIME_NSEC_REG 0x0D10
+#define SXGBE_CORE_SYSTIME_SECUP_REG 0x0D14
+#define SXGBE_CORE_TSTAMP_ADD_REG 0x0D18
+#define SXGBE_CORE_SYSTIME_HWORD_REG 0x0D1C
+#define SXGBE_CORE_TSTAMP_STATUS_REG 0x0D20
+#define SXGBE_CORE_TXTIME_STATUSNSEC_REG 0x0D30
+#define SXGBE_CORE_TXTIME_STATUSSEC_REG 0x0D34
+
+/* Auxiliary registers */
+#define SXGBE_CORE_AUX_CTL_REG 0x0D40
+#define SXGBE_CORE_AUX_TSTAMP_NSEC_REG 0x0D48
+#define SXGBE_CORE_AUX_TSTAMP_SEC_REG 0x0D4C
+#define SXGBE_CORE_AUX_TSTAMP_INGCOR_REG 0x0D50
+#define SXGBE_CORE_AUX_TSTAMP_ENGCOR_REG 0x0D54
+#define SXGBE_CORE_AUX_TSTAMP_INGCOR_NSEC_REG 0x0D58
+#define SXGBE_CORE_AUX_TSTAMP_INGCOR_SUBNSEC_REG 0x0D5C
+#define SXGBE_CORE_AUX_TSTAMP_ENGCOR_NSEC_REG 0x0D60
+#define SXGBE_CORE_AUX_TSTAMP_ENGCOR_SUBNSEC_REG 0x0D64
+
+/* PPS registers */
+#define SXGBE_CORE_PPS_CTL_REG 0x0D70
+#define SXGBE_CORE_PPS_BASE 0x0D80
+
+/* addr = 0 - 3 */
+#define SXGBE_CORE_PPS_TTIME_SEC_REG(addr) \
+ (SXGBE_CORE_PPS_BASE + (0x10 * addr) + 0x0)
+#define SXGBE_CORE_PPS_TTIME_NSEC_REG(addr) \
+ (SXGBE_CORE_PPS_BASE + (0x10 * addr) + 0x4)
+#define SXGBE_CORE_PPS_INTERVAL_REG(addr) \
+ (SXGBE_CORE_PPS_BASE + (0x10 * addr) + 0x8)
+#define SXGBE_CORE_PPS_WIDTH_REG(addr) \
+ (SXGBE_CORE_PPS_BASE + (0x10 * addr) + 0xC)
+#define SXGBE_CORE_PTO_CTL_REG 0x0DC0
+#define SXGBE_CORE_SRCPORT_ITY0_REG 0x0DC4
+#define SXGBE_CORE_SRCPORT_ITY1_REG 0x0DC8
+#define SXGBE_CORE_SRCPORT_ITY2_REG 0x0DCC
+#define SXGBE_CORE_LOGMSG_LEVEL_REG 0x0DD0
+
+/* SXGBE MTL Registers */
+#define SXGBE_MTL_BASE_REG 0x1000
+#define SXGBE_MTL_OP_MODE_REG (SXGBE_MTL_BASE_REG + 0x0000)
+#define SXGBE_MTL_DEBUG_CTL_REG (SXGBE_MTL_BASE_REG + 0x0008)
+#define SXGBE_MTL_DEBUG_STATUS_REG (SXGBE_MTL_BASE_REG + 0x000C)
+#define SXGBE_MTL_FIFO_DEBUGDATA_REG (SXGBE_MTL_BASE_REG + 0x0010)
+#define SXGBE_MTL_INT_STATUS_REG (SXGBE_MTL_BASE_REG + 0x0020)
+#define SXGBE_MTL_RXQ_DMAMAP0_REG (SXGBE_MTL_BASE_REG + 0x0030)
+#define SXGBE_MTL_RXQ_DMAMAP1_REG (SXGBE_MTL_BASE_REG + 0x0034)
+#define SXGBE_MTL_RXQ_DMAMAP2_REG (SXGBE_MTL_BASE_REG + 0x0038)
+#define SXGBE_MTL_TX_PRTYMAP0_REG (SXGBE_MTL_BASE_REG + 0x0040)
+#define SXGBE_MTL_TX_PRTYMAP1_REG (SXGBE_MTL_BASE_REG + 0x0044)
+
+/* TC/Queue registers, qnum=0-15 */
+#define SXGBE_MTL_TC_TXBASE_REG (SXGBE_MTL_BASE_REG + 0x0100)
+#define SXGBE_MTL_TXQ_OPMODE_REG(qnum) \
+ (SXGBE_MTL_TC_TXBASE_REG + (qnum * 0x80) + 0x00)
+#define SXGBE_MTL_SFMODE BIT(1)
+#define SXGBE_MTL_FIFO_LSHIFT 16
+#define SXGBE_MTL_ENABLE_QUEUE 0x00000008
+#define SXGBE_MTL_TXQ_UNDERFLOW_REG(qnum) \
+ (SXGBE_MTL_TC_TXBASE_REG + (qnum * 0x80) + 0x04)
+#define SXGBE_MTL_TXQ_DEBUG_REG(qnum) \
+ (SXGBE_MTL_TC_TXBASE_REG + (qnum * 0x80) + 0x08)
+#define SXGBE_MTL_TXQ_ETSCTL_REG(qnum) \
+ (SXGBE_MTL_TC_TXBASE_REG + (qnum * 0x80) + 0x10)
+#define SXGBE_MTL_TXQ_ETSSTATUS_REG(qnum) \
+ (SXGBE_MTL_TC_TXBASE_REG + (qnum * 0x80) + 0x14)
+#define SXGBE_MTL_TXQ_QUANTWEIGHT_REG(qnum) \
+ (SXGBE_MTL_TC_TXBASE_REG + (qnum * 0x80) + 0x18)
+
+#define SXGBE_MTL_TC_RXBASE_REG 0x1140
+#define SXGBE_RX_MTL_SFMODE BIT(5)
+#define SXGBE_MTL_RXQ_OPMODE_REG(qnum) \
+ (SXGBE_MTL_TC_RXBASE_REG + (qnum * 0x80) + 0x00)
+#define SXGBE_MTL_RXQ_MISPKTOVERFLOW_REG(qnum) \
+ (SXGBE_MTL_TC_RXBASE_REG + (qnum * 0x80) + 0x04)
+#define SXGBE_MTL_RXQ_DEBUG_REG(qnum) \
+ (SXGBE_MTL_TC_RXBASE_REG + (qnum * 0x80) + 0x08)
+#define SXGBE_MTL_RXQ_CTL_REG(qnum) \
+ (SXGBE_MTL_TC_RXBASE_REG + (qnum * 0x80) + 0x0C)
+#define SXGBE_MTL_RXQ_INTENABLE_REG(qnum) \
+ (SXGBE_MTL_TC_RXBASE_REG + (qnum * 0x80) + 0x30)
+#define SXGBE_MTL_RXQ_INTSTATUS_REG(qnum) \
+ (SXGBE_MTL_TC_RXBASE_REG + (qnum * 0x80) + 0x34)
+
+/* SXGBE DMA Registers */
+#define SXGBE_DMA_BASE_REG 0x3000
+#define SXGBE_DMA_MODE_REG (SXGBE_DMA_BASE_REG + 0x0000)
+#define SXGBE_DMA_SOFT_RESET BIT(0)
+#define SXGBE_DMA_SYSBUS_MODE_REG (SXGBE_DMA_BASE_REG + 0x0004)
+#define SXGBE_DMA_AXI_UNDEF_BURST BIT(0)
+#define SXGBE_DMA_ENHACE_ADDR_MODE BIT(11)
+#define SXGBE_DMA_INT_STATUS_REG (SXGBE_DMA_BASE_REG + 0x0008)
+#define SXGBE_DMA_AXI_ARCACHECTL_REG (SXGBE_DMA_BASE_REG + 0x0010)
+#define SXGBE_DMA_AXI_AWCACHECTL_REG (SXGBE_DMA_BASE_REG + 0x0018)
+#define SXGBE_DMA_DEBUG_STATUS0_REG (SXGBE_DMA_BASE_REG + 0x0020)
+#define SXGBE_DMA_DEBUG_STATUS1_REG (SXGBE_DMA_BASE_REG + 0x0024)
+#define SXGBE_DMA_DEBUG_STATUS2_REG (SXGBE_DMA_BASE_REG + 0x0028)
+#define SXGBE_DMA_DEBUG_STATUS3_REG (SXGBE_DMA_BASE_REG + 0x002C)
+#define SXGBE_DMA_DEBUG_STATUS4_REG (SXGBE_DMA_BASE_REG + 0x0030)
+#define SXGBE_DMA_DEBUG_STATUS5_REG (SXGBE_DMA_BASE_REG + 0x0034)
+
+/* Channel Registers, cha_num = 0-15 */
+#define SXGBE_DMA_CHA_BASE_REG \
+ (SXGBE_DMA_BASE_REG + 0x0100)
+#define SXGBE_DMA_CHA_CTL_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x00)
+#define SXGBE_DMA_PBL_X8MODE BIT(16)
+#define SXGBE_DMA_CHA_TXCTL_TSE_ENABLE BIT(12)
+#define SXGBE_DMA_CHA_TXCTL_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x04)
+#define SXGBE_DMA_CHA_RXCTL_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x08)
+#define SXGBE_DMA_CHA_TXDESC_HADD_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x10)
+#define SXGBE_DMA_CHA_TXDESC_LADD_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x14)
+#define SXGBE_DMA_CHA_RXDESC_HADD_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x18)
+#define SXGBE_DMA_CHA_RXDESC_LADD_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x1C)
+#define SXGBE_DMA_CHA_TXDESC_TAILPTR_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x24)
+#define SXGBE_DMA_CHA_RXDESC_TAILPTR_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x2C)
+#define SXGBE_DMA_CHA_TXDESC_RINGLEN_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x30)
+#define SXGBE_DMA_CHA_RXDESC_RINGLEN_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x34)
+#define SXGBE_DMA_CHA_INT_ENABLE_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x38)
+#define SXGBE_DMA_CHA_INT_RXWATCHTMR_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x3C)
+#define SXGBE_DMA_CHA_TXDESC_CURADDLO_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x44)
+#define SXGBE_DMA_CHA_RXDESC_CURADDLO_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x4C)
+#define SXGBE_DMA_CHA_CURTXBUF_ADDHI_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x50)
+#define SXGBE_DMA_CHA_CURTXBUF_ADDLO_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x54)
+#define SXGBE_DMA_CHA_CURRXBUF_ADDHI_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x58)
+#define SXGBE_DMA_CHA_CURRXBUF_ADDLO_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x5C)
+#define SXGBE_DMA_CHA_STATUS_REG(cha_num) \
+ (SXGBE_DMA_CHA_BASE_REG + (cha_num * 0x80) + 0x60)
+
+/* TX DMA control register specific */
+#define SXGBE_TX_START_DMA BIT(0)
+
+/* sxgbe tx configuration register bitfields */
+#define SXGBE_SPEED_10G 0x0
+#define SXGBE_SPEED_2_5G 0x1
+#define SXGBE_SPEED_1G 0x2
+#define SXGBE_SPEED_LSHIFT 29
+
+#define SXGBE_TX_ENABLE BIT(0)
+#define SXGBE_TX_DISDIC_ALGO BIT(1)
+#define SXGBE_TX_JABBER_DISABLE BIT(16)
+
+/* sxgbe rx configuration register bitfields */
+#define SXGBE_RX_ENABLE BIT(0)
+#define SXGBE_RX_ACS_ENABLE BIT(1)
+#define SXGBE_RX_WATCHDOG_DISABLE BIT(7)
+#define SXGBE_RX_JUMBPKT_ENABLE BIT(8)
+#define SXGBE_RX_CSUMOFFLOAD_ENABLE BIT(9)
+#define SXGBE_RX_LOOPBACK_ENABLE BIT(10)
+#define SXGBE_RX_ARPOFFLOAD_ENABLE BIT(31)
+
+/* sxgbe vlan Tag Register bitfields */
+#define SXGBE_VLAN_SVLAN_ENABLE BIT(18)
+#define SXGBE_VLAN_DOUBLEVLAN_ENABLE BIT(26)
+#define SXGBE_VLAN_INNERVLAN_ENABLE BIT(27)
+
+/* XMAC VLAN Tag Inclusion Register(0x0060) bitfields
+ * Below fields same for Inner VLAN Tag Inclusion
+ * Register(0x0064) register
+ */
+enum vlan_tag_ctl_tx {
+ VLAN_TAG_TX_NOP,
+ VLAN_TAG_TX_DEL,
+ VLAN_TAG_TX_INSERT,
+ VLAN_TAG_TX_REPLACE
+};
+#define SXGBE_VLAN_PRTY_CTL BIT(18)
+#define SXGBE_VLAN_CSVL_CTL BIT(19)
+
+/* SXGBE TX Q Flow Control Register bitfields */
+#define SXGBE_TX_FLOW_CTL_FCB BIT(0)
+#define SXGBE_TX_FLOW_CTL_TFB BIT(1)
+
+/* SXGBE RX Q Flow Control Register bitfields */
+#define SXGBE_RX_FLOW_CTL_ENABLE BIT(0)
+#define SXGBE_RX_UNICAST_DETECT BIT(1)
+#define SXGBE_RX_PRTYFLOW_CTL_ENABLE BIT(8)
+
+/* sxgbe rx Q control0 register bitfields */
+#define SXGBE_RX_Q_ENABLE 0x2
+
+/* SXGBE hardware features bitfield specific */
+/* Capability Register 0 */
+#define SXGBE_HW_FEAT_GMII(cap) ((cap & 0x00000002) >> 1)
+#define SXGBE_HW_FEAT_VLAN_HASH_FILTER(cap) ((cap & 0x00000010) >> 4)
+#define SXGBE_HW_FEAT_SMA(cap) ((cap & 0x00000020) >> 5)
+#define SXGBE_HW_FEAT_PMT_TEMOTE_WOP(cap) ((cap & 0x00000040) >> 6)
+#define SXGBE_HW_FEAT_PMT_MAGIC_PKT(cap) ((cap & 0x00000080) >> 7)
+#define SXGBE_HW_FEAT_RMON(cap) ((cap & 0x00000100) >> 8)
+#define SXGBE_HW_FEAT_ARP_OFFLOAD(cap) ((cap & 0x00000200) >> 9)
+#define SXGBE_HW_FEAT_IEEE1500_2008(cap) ((cap & 0x00001000) >> 12)
+#define SXGBE_HW_FEAT_EEE(cap) ((cap & 0x00002000) >> 13)
+#define SXGBE_HW_FEAT_TX_CSUM_OFFLOAD(cap) ((cap & 0x00004000) >> 14)
+#define SXGBE_HW_FEAT_RX_CSUM_OFFLOAD(cap) ((cap & 0x00010000) >> 16)
+#define SXGBE_HW_FEAT_MACADDR_COUNT(cap) ((cap & 0x007C0000) >> 18)
+#define SXGBE_HW_FEAT_TSTMAP_SRC(cap) ((cap & 0x06000000) >> 25)
+#define SXGBE_HW_FEAT_SRCADDR_VLAN(cap) ((cap & 0x08000000) >> 27)
+
+/* Capability Register 1 */
+#define SXGBE_HW_FEAT_RX_FIFO_SIZE(cap) ((cap & 0x0000001F))
+#define SXGBE_HW_FEAT_TX_FIFO_SIZE(cap) ((cap & 0x000007C0) >> 6)
+#define SXGBE_HW_FEAT_IEEE1588_HWORD(cap) ((cap & 0x00002000) >> 13)
+#define SXGBE_HW_FEAT_DCB(cap) ((cap & 0x00010000) >> 16)
+#define SXGBE_HW_FEAT_SPLIT_HDR(cap) ((cap & 0x00020000) >> 17)
+#define SXGBE_HW_FEAT_TSO(cap) ((cap & 0x00040000) >> 18)
+#define SXGBE_HW_FEAT_DEBUG_MEM_IFACE(cap) ((cap & 0x00080000) >> 19)
+#define SXGBE_HW_FEAT_RSS(cap) ((cap & 0x00100000) >> 20)
+#define SXGBE_HW_FEAT_HASH_TABLE_SIZE(cap) ((cap & 0x03000000) >> 24)
+#define SXGBE_HW_FEAT_L3L4_FILTER_NUM(cap) ((cap & 0x78000000) >> 27)
+
+/* Capability Register 2 */
+#define SXGBE_HW_FEAT_RX_MTL_QUEUES(cap) ((cap & 0x0000000F))
+#define SXGBE_HW_FEAT_TX_MTL_QUEUES(cap) ((cap & 0x000003C0) >> 6)
+#define SXGBE_HW_FEAT_RX_DMA_CHANNELS(cap) ((cap & 0x0000F000) >> 12)
+#define SXGBE_HW_FEAT_TX_DMA_CHANNELS(cap) ((cap & 0x003C0000) >> 18)
+#define SXGBE_HW_FEAT_PPS_OUTPUTS(cap) ((cap & 0x07000000) >> 24)
+#define SXGBE_HW_FEAT_AUX_SNAPSHOTS(cap) ((cap & 0x70000000) >> 28)
+
+/* DMAchannel interrupt enable specific */
+/* DMA Normal interrupt */
+#define SXGBE_DMA_INT_ENA_NIE BIT(16) /* Normal Summary */
+#define SXGBE_DMA_INT_ENA_TIE BIT(0) /* Transmit Interrupt */
+#define SXGBE_DMA_INT_ENA_TUE BIT(2) /* Transmit Buffer Unavailable */
+#define SXGBE_DMA_INT_ENA_RIE BIT(6) /* Receive Interrupt */
+
+#define SXGBE_DMA_INT_NORMAL \
+ (SXGBE_DMA_INT_ENA_NIE | SXGBE_DMA_INT_ENA_RIE | \
+ SXGBE_DMA_INT_ENA_TIE | SXGBE_DMA_INT_ENA_TUE)
+
+/* DMA Abnormal interrupt */
+#define SXGBE_DMA_INT_ENA_AIE BIT(15) /* Abnormal Summary */
+#define SXGBE_DMA_INT_ENA_TSE BIT(1) /* Transmit Stopped */
+#define SXGBE_DMA_INT_ENA_RUE BIT(7) /* Receive Buffer Unavailable */
+#define SXGBE_DMA_INT_ENA_RSE BIT(8) /* Receive Stopped */
+#define SXGBE_DMA_INT_ENA_FBE BIT(12) /* Fatal Bus Error */
+#define SXGBE_DMA_INT_ENA_CDEE BIT(13) /* Context Descriptor Error */
+
+#define SXGBE_DMA_INT_ABNORMAL \
+ (SXGBE_DMA_INT_ENA_AIE | SXGBE_DMA_INT_ENA_TSE | \
+ SXGBE_DMA_INT_ENA_RUE | SXGBE_DMA_INT_ENA_RSE | \
+ SXGBE_DMA_INT_ENA_FBE | SXGBE_DMA_INT_ENA_CDEE)
+
+#define SXGBE_DMA_ENA_INT (SXGBE_DMA_INT_NORMAL | SXGBE_DMA_INT_ABNORMAL)
+
+/* DMA channel interrupt status specific */
+#define SXGBE_DMA_INT_STATUS_REB2 BIT(21)
+#define SXGBE_DMA_INT_STATUS_REB1 BIT(20)
+#define SXGBE_DMA_INT_STATUS_REB0 BIT(19)
+#define SXGBE_DMA_INT_STATUS_TEB2 BIT(18)
+#define SXGBE_DMA_INT_STATUS_TEB1 BIT(17)
+#define SXGBE_DMA_INT_STATUS_TEB0 BIT(16)
+#define SXGBE_DMA_INT_STATUS_NIS BIT(15)
+#define SXGBE_DMA_INT_STATUS_AIS BIT(14)
+#define SXGBE_DMA_INT_STATUS_CTXTERR BIT(13)
+#define SXGBE_DMA_INT_STATUS_FBE BIT(12)
+#define SXGBE_DMA_INT_STATUS_RPS BIT(8)
+#define SXGBE_DMA_INT_STATUS_RBU BIT(7)
+#define SXGBE_DMA_INT_STATUS_RI BIT(6)
+#define SXGBE_DMA_INT_STATUS_TBU BIT(2)
+#define SXGBE_DMA_INT_STATUS_TPS BIT(1)
+#define SXGBE_DMA_INT_STATUS_TI BIT(0)
+
+#endif /* __SXGBE_REGMAP_H__ */
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_xpcs.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_xpcs.c
new file mode 100644
index 000000000000..51c32194ba88
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_xpcs.c
@@ -0,0 +1,91 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include "sxgbe_common.h"
+#include "sxgbe_xpcs.h"
+
+static int sxgbe_xpcs_read(struct net_device *ndev, unsigned int reg)
+{
+ u32 value;
+ struct sxgbe_priv_data *priv = netdev_priv(ndev);
+
+ value = readl(priv->ioaddr + XPCS_OFFSET + reg);
+
+ return value;
+}
+
+static int sxgbe_xpcs_write(struct net_device *ndev, int reg, int data)
+{
+ struct sxgbe_priv_data *priv = netdev_priv(ndev);
+
+ writel(data, priv->ioaddr + XPCS_OFFSET + reg);
+
+ return 0;
+}
+
+int sxgbe_xpcs_init(struct net_device *ndev)
+{
+ u32 value;
+
+ value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
+ /* 10G XAUI mode */
+ sxgbe_xpcs_write(ndev, SR_PCS_CONTROL2, XPCS_TYPE_SEL_X);
+ sxgbe_xpcs_write(ndev, VR_PCS_MMD_XAUI_MODE_CONTROL, XPCS_XAUI_MODE);
+ sxgbe_xpcs_write(ndev, VR_PCS_MMD_XAUI_MODE_CONTROL, value | BIT(13));
+ sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value | BIT(11));
+
+ do {
+ value = sxgbe_xpcs_read(ndev, VR_PCS_MMD_DIGITAL_STATUS);
+ } while ((value & XPCS_QSEQ_STATE_MPLLOFF) == XPCS_QSEQ_STATE_STABLE);
+
+ value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
+ sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value & ~BIT(11));
+
+ do {
+ value = sxgbe_xpcs_read(ndev, VR_PCS_MMD_DIGITAL_STATUS);
+ } while ((value & XPCS_QSEQ_STATE_MPLLOFF) != XPCS_QSEQ_STATE_STABLE);
+
+ return 0;
+}
+
+int sxgbe_xpcs_init_1G(struct net_device *ndev)
+{
+ int value;
+
+ /* 10GBASE-X PCS (1G) mode */
+ sxgbe_xpcs_write(ndev, SR_PCS_CONTROL2, XPCS_TYPE_SEL_X);
+ sxgbe_xpcs_write(ndev, VR_PCS_MMD_XAUI_MODE_CONTROL, XPCS_XAUI_MODE);
+ value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
+ sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value & ~BIT(13));
+
+ value = sxgbe_xpcs_read(ndev, SR_MII_MMD_CONTROL);
+ sxgbe_xpcs_write(ndev, SR_MII_MMD_CONTROL, value | BIT(6));
+ sxgbe_xpcs_write(ndev, SR_MII_MMD_CONTROL, value & ~BIT(13));
+ value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
+ sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value | BIT(11));
+
+ do {
+ value = sxgbe_xpcs_read(ndev, VR_PCS_MMD_DIGITAL_STATUS);
+ } while ((value & XPCS_QSEQ_STATE_MPLLOFF) != XPCS_QSEQ_STATE_STABLE);
+
+ value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
+ sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value & ~BIT(11));
+
+ /* Auto Negotiation cluase 37 enable */
+ value = sxgbe_xpcs_read(ndev, SR_MII_MMD_CONTROL);
+ sxgbe_xpcs_write(ndev, SR_MII_MMD_CONTROL, value | BIT(12));
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_xpcs.h b/drivers/net/ethernet/samsung/sxgbe/sxgbe_xpcs.h
new file mode 100644
index 000000000000..6b26a50724d3
--- /dev/null
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_xpcs.h
@@ -0,0 +1,38 @@
+/* 10G controller driver for Samsung SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Byungho An <bh74.an@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __SXGBE_XPCS_H__
+#define __SXGBE_XPCS_H__
+
+/* XPCS Registers */
+#define XPCS_OFFSET 0x1A060000
+#define SR_PCS_MMD_CONTROL1 0x030000
+#define SR_PCS_CONTROL2 0x030007
+#define VR_PCS_MMD_XAUI_MODE_CONTROL 0x038004
+#define VR_PCS_MMD_DIGITAL_STATUS 0x038010
+#define SR_MII_MMD_CONTROL 0x1F0000
+#define SR_MII_MMD_AN_ADV 0x1F0004
+#define SR_MII_MMD_AN_LINK_PARTNER_BA 0x1F0005
+#define VR_MII_MMD_AN_CONTROL 0x1F8001
+#define VR_MII_MMD_AN_INT_STATUS 0x1F8002
+
+#define XPCS_QSEQ_STATE_STABLE 0x10
+#define XPCS_QSEQ_STATE_MPLLOFF 0x1c
+#define XPCS_TYPE_SEL_R 0x00
+#define XPCS_TYPE_SEL_X 0x01
+#define XPCS_TYPE_SEL_W 0x02
+#define XPCS_XAUI_MODE 0x00
+#define XPCS_RXAUI_MODE 0x01
+
+int sxgbe_xpcs_init(struct net_device *ndev);
+int sxgbe_xpcs_init_1G(struct net_device *ndev);
+
+#endif /* __SXGBE_XPCS_H__ */
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index 174a92f5fe51..21c20ea0dad0 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -162,8 +162,8 @@ static int efx_ef10_get_mac_address(struct efx_nic *efx, u8 *mac_address)
if (outlen < MC_CMD_GET_MAC_ADDRESSES_OUT_LEN)
return -EIO;
- memcpy(mac_address,
- MCDI_PTR(outbuf, GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE), ETH_ALEN);
+ ether_addr_copy(mac_address,
+ MCDI_PTR(outbuf, GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE));
return 0;
}
@@ -172,8 +172,8 @@ static int efx_ef10_probe(struct efx_nic *efx)
struct efx_ef10_nic_data *nic_data;
int i, rc;
- /* We can have one VI for each 8K region. However we need
- * multiple TX queues per channel.
+ /* We can have one VI for each 8K region. However, until we
+ * use TX option descriptors we need two TX queues per channel.
*/
efx->max_channels =
min_t(unsigned int,
@@ -565,10 +565,17 @@ static int efx_ef10_dimension_resources(struct efx_nic *efx)
* several of each (in fact that's the only option if host
* page size is >4K). So we may allocate some extra VIs just
* for writing PIO buffers through.
+ *
+ * The UC mapping contains (min_vis - 1) complete VIs and the
+ * first half of the next VI. Then the WC mapping begins with
+ * the second half of this last VI.
*/
uc_mem_map_size = PAGE_ALIGN((min_vis - 1) * EFX_VI_PAGE_SIZE +
ER_DZ_TX_PIOBUF);
if (nic_data->n_piobufs) {
+ /* pio_write_vi_base rounds down to give the number of complete
+ * VIs inside the UC mapping.
+ */
pio_write_vi_base = uc_mem_map_size / EFX_VI_PAGE_SIZE;
wc_mem_map_size = (PAGE_ALIGN((pio_write_vi_base +
nic_data->n_piobufs) *
@@ -1955,6 +1962,9 @@ static int efx_ef10_ev_process(struct efx_channel *channel, int quota)
int tx_descs = 0;
int spent = 0;
+ if (quota <= 0)
+ return spent;
+
read_ptr = channel->eventq_read_ptr;
for (;;) {
@@ -3145,12 +3155,10 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
table->dev_uc_count = -1;
} else {
table->dev_uc_count = 1 + netdev_uc_count(net_dev);
- memcpy(table->dev_uc_list[0].addr, net_dev->dev_addr,
- ETH_ALEN);
+ ether_addr_copy(table->dev_uc_list[0].addr, net_dev->dev_addr);
i = 1;
netdev_for_each_uc_addr(uc, net_dev) {
- memcpy(table->dev_uc_list[i].addr,
- uc->addr, ETH_ALEN);
+ ether_addr_copy(table->dev_uc_list[i].addr, uc->addr);
i++;
}
}
@@ -3162,8 +3170,7 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
eth_broadcast_addr(table->dev_mc_list[0].addr);
i = 1;
netdev_for_each_mc_addr(mc, net_dev) {
- memcpy(table->dev_mc_list[i].addr,
- mc->addr, ETH_ALEN);
+ ether_addr_copy(table->dev_mc_list[i].addr, mc->addr);
i++;
}
}
diff --git a/drivers/net/ethernet/sfc/ef10_regs.h b/drivers/net/ethernet/sfc/ef10_regs.h
index 207ac9a1e3de..62a55dde61d5 100644
--- a/drivers/net/ethernet/sfc/ef10_regs.h
+++ b/drivers/net/ethernet/sfc/ef10_regs.h
@@ -227,36 +227,6 @@
#define ESF_DZ_RX_KER_BUF_ADDR_LBN 0
#define ESF_DZ_RX_KER_BUF_ADDR_WIDTH 48
-/* RX_USER_DESC */
-#define ESF_DZ_RX_USR_RESERVED_LBN 62
-#define ESF_DZ_RX_USR_RESERVED_WIDTH 2
-#define ESF_DZ_RX_USR_BYTE_CNT_LBN 48
-#define ESF_DZ_RX_USR_BYTE_CNT_WIDTH 14
-#define ESF_DZ_RX_USR_BUF_PAGE_SIZE_LBN 44
-#define ESF_DZ_RX_USR_BUF_PAGE_SIZE_WIDTH 4
-#define ESE_DZ_USR_BUF_PAGE_SZ_4MB 10
-#define ESE_DZ_USR_BUF_PAGE_SZ_1MB 8
-#define ESE_DZ_USR_BUF_PAGE_SZ_64KB 4
-#define ESE_DZ_USR_BUF_PAGE_SZ_4KB 0
-#define ESF_DZ_RX_USR_BUF_ID_OFFSET_LBN 0
-#define ESF_DZ_RX_USR_BUF_ID_OFFSET_WIDTH 44
-#define ESF_DZ_RX_USR_4KBPS_BUF_ID_LBN 12
-#define ESF_DZ_RX_USR_4KBPS_BUF_ID_WIDTH 32
-#define ESF_DZ_RX_USR_64KBPS_BUF_ID_LBN 16
-#define ESF_DZ_RX_USR_64KBPS_BUF_ID_WIDTH 28
-#define ESF_DZ_RX_USR_1MBPS_BUF_ID_LBN 20
-#define ESF_DZ_RX_USR_1MBPS_BUF_ID_WIDTH 24
-#define ESF_DZ_RX_USR_4MBPS_BUF_ID_LBN 22
-#define ESF_DZ_RX_USR_4MBPS_BUF_ID_WIDTH 22
-#define ESF_DZ_RX_USR_4MBPS_BYTE_OFFSET_LBN 0
-#define ESF_DZ_RX_USR_4MBPS_BYTE_OFFSET_WIDTH 22
-#define ESF_DZ_RX_USR_1MBPS_BYTE_OFFSET_LBN 0
-#define ESF_DZ_RX_USR_1MBPS_BYTE_OFFSET_WIDTH 20
-#define ESF_DZ_RX_USR_64KBPS_BYTE_OFFSET_LBN 0
-#define ESF_DZ_RX_USR_64KBPS_BYTE_OFFSET_WIDTH 16
-#define ESF_DZ_RX_USR_4KBPS_BYTE_OFFSET_LBN 0
-#define ESF_DZ_RX_USR_4KBPS_BYTE_OFFSET_WIDTH 12
-
/* TX_CSUM_TSTAMP_DESC */
#define ESF_DZ_TX_DESC_IS_OPT_LBN 63
#define ESF_DZ_TX_DESC_IS_OPT_WIDTH 1
@@ -338,37 +308,6 @@
#define ESF_DZ_TX_TSO_TCP_SEQNO_LBN 0
#define ESF_DZ_TX_TSO_TCP_SEQNO_WIDTH 32
-/* TX_USER_DESC */
-#define ESF_DZ_TX_USR_TYPE_LBN 63
-#define ESF_DZ_TX_USR_TYPE_WIDTH 1
-#define ESF_DZ_TX_USR_CONT_LBN 62
-#define ESF_DZ_TX_USR_CONT_WIDTH 1
-#define ESF_DZ_TX_USR_BYTE_CNT_LBN 48
-#define ESF_DZ_TX_USR_BYTE_CNT_WIDTH 14
-#define ESF_DZ_TX_USR_BUF_PAGE_SIZE_LBN 44
-#define ESF_DZ_TX_USR_BUF_PAGE_SIZE_WIDTH 4
-#define ESE_DZ_USR_BUF_PAGE_SZ_4MB 10
-#define ESE_DZ_USR_BUF_PAGE_SZ_1MB 8
-#define ESE_DZ_USR_BUF_PAGE_SZ_64KB 4
-#define ESE_DZ_USR_BUF_PAGE_SZ_4KB 0
-#define ESF_DZ_TX_USR_BUF_ID_OFFSET_LBN 0
-#define ESF_DZ_TX_USR_BUF_ID_OFFSET_WIDTH 44
-#define ESF_DZ_TX_USR_4KBPS_BUF_ID_LBN 12
-#define ESF_DZ_TX_USR_4KBPS_BUF_ID_WIDTH 32
-#define ESF_DZ_TX_USR_64KBPS_BUF_ID_LBN 16
-#define ESF_DZ_TX_USR_64KBPS_BUF_ID_WIDTH 28
-#define ESF_DZ_TX_USR_1MBPS_BUF_ID_LBN 20
-#define ESF_DZ_TX_USR_1MBPS_BUF_ID_WIDTH 24
-#define ESF_DZ_TX_USR_4MBPS_BUF_ID_LBN 22
-#define ESF_DZ_TX_USR_4MBPS_BUF_ID_WIDTH 22
-#define ESF_DZ_TX_USR_4MBPS_BYTE_OFFSET_LBN 0
-#define ESF_DZ_TX_USR_4MBPS_BYTE_OFFSET_WIDTH 22
-#define ESF_DZ_TX_USR_1MBPS_BYTE_OFFSET_LBN 0
-#define ESF_DZ_TX_USR_1MBPS_BYTE_OFFSET_WIDTH 20
-#define ESF_DZ_TX_USR_64KBPS_BYTE_OFFSET_LBN 0
-#define ESF_DZ_TX_USR_64KBPS_BYTE_OFFSET_WIDTH 16
-#define ESF_DZ_TX_USR_4KBPS_BYTE_OFFSET_LBN 0
-#define ESF_DZ_TX_USR_4KBPS_BYTE_OFFSET_WIDTH 12
/*************************************************************************/
/* TX_DESC_UPD_REG: Transmit descriptor update register.
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 83d464347021..57b971e5e6b2 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -503,8 +503,6 @@ static int efx_probe_channel(struct efx_channel *channel)
goto fail;
}
- channel->n_rx_frm_trunc = 0;
-
return 0;
fail:
@@ -1014,7 +1012,7 @@ static int efx_probe_port(struct efx_nic *efx)
return rc;
/* Initialise MAC address to permanent address */
- memcpy(efx->net_dev->dev_addr, efx->net_dev->perm_addr, ETH_ALEN);
+ ether_addr_copy(efx->net_dev->dev_addr, efx->net_dev->perm_addr);
return 0;
}
@@ -1346,20 +1344,23 @@ static int efx_probe_interrupts(struct efx_nic *efx)
for (i = 0; i < n_channels; i++)
xentries[i].entry = i;
- rc = pci_enable_msix(efx->pci_dev, xentries, n_channels);
- if (rc > 0) {
+ rc = pci_enable_msix_range(efx->pci_dev,
+ xentries, 1, n_channels);
+ if (rc < 0) {
+ /* Fall back to single channel MSI */
+ efx->interrupt_mode = EFX_INT_MODE_MSI;
+ netif_err(efx, drv, efx->net_dev,
+ "could not enable MSI-X\n");
+ } else if (rc < n_channels) {
netif_err(efx, drv, efx->net_dev,
"WARNING: Insufficient MSI-X vectors"
" available (%d < %u).\n", rc, n_channels);
netif_err(efx, drv, efx->net_dev,
"WARNING: Performance may be reduced.\n");
- EFX_BUG_ON_PARANOID(rc >= n_channels);
n_channels = rc;
- rc = pci_enable_msix(efx->pci_dev, xentries,
- n_channels);
}
- if (rc == 0) {
+ if (rc > 0) {
efx->n_channels = n_channels;
if (n_channels > extra_channels)
n_channels -= extra_channels;
@@ -1375,11 +1376,6 @@ static int efx_probe_interrupts(struct efx_nic *efx)
for (i = 0; i < efx->n_channels; i++)
efx_get_channel(efx, i)->irq =
xentries[i].vector;
- } else {
- /* Fall back to single channel MSI */
- efx->interrupt_mode = EFX_INT_MODE_MSI;
- netif_err(efx, drv, efx->net_dev,
- "could not enable MSI-X\n");
}
}
@@ -1603,6 +1599,8 @@ static int efx_probe_nic(struct efx_nic *efx)
if (rc)
goto fail1;
+ efx_set_channels(efx);
+
rc = efx->type->dimension_resources(efx);
if (rc)
goto fail2;
@@ -1613,7 +1611,6 @@ static int efx_probe_nic(struct efx_nic *efx)
efx->rx_indir_table[i] =
ethtool_rxfh_indir_default(i, efx->rss_spread);
- efx_set_channels(efx);
netif_set_real_num_tx_queues(efx->net_dev, efx->n_tx_channels);
netif_set_real_num_rx_queues(efx->net_dev, efx->n_rx_channels);
@@ -2115,7 +2112,7 @@ static int efx_set_mac_address(struct net_device *net_dev, void *data)
{
struct efx_nic *efx = netdev_priv(net_dev);
struct sockaddr *addr = data;
- char *new_addr = addr->sa_data;
+ u8 *new_addr = addr->sa_data;
if (!is_valid_ether_addr(new_addr)) {
netif_err(efx, drv, efx->net_dev,
@@ -2124,7 +2121,7 @@ static int efx_set_mac_address(struct net_device *net_dev, void *data)
return -EADDRNOTAVAIL;
}
- memcpy(net_dev->dev_addr, new_addr, net_dev->addr_len);
+ ether_addr_copy(net_dev->dev_addr, new_addr);
efx_sriov_mac_address_changed(efx);
/* Reconfigure the MAC */
@@ -3273,6 +3270,6 @@ module_exit(efx_exit_module);
MODULE_AUTHOR("Solarflare Communications and "
"Michael Brown <mbrown@fensystems.co.uk>");
-MODULE_DESCRIPTION("Solarflare Communications network driver");
+MODULE_DESCRIPTION("Solarflare network driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, efx_pci_table);
diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h
index dbd7b78fe01c..99032581336f 100644
--- a/drivers/net/ethernet/sfc/efx.h
+++ b/drivers/net/ethernet/sfc/efx.h
@@ -14,7 +14,7 @@
#include "net_driver.h"
#include "filter.h"
-/* Solarstorm controllers use BAR 0 for I/O space and BAR 2(&3) for memory */
+/* All controllers use BAR 0 for I/O space and BAR 2(&3) for memory */
#define EFX_MEM_BAR 2
/* TX */
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c
index 229428915aa8..0de8b07c24c2 100644
--- a/drivers/net/ethernet/sfc/ethtool.c
+++ b/drivers/net/ethernet/sfc/ethtool.c
@@ -251,6 +251,9 @@ static void efx_fill_test(unsigned int test_index, u8 *strings, u64 *data,
* @test_index: Starting index of the test
* @strings: Ethtool strings, or %NULL
* @data: Ethtool test results, or %NULL
+ *
+ * Fill in a block of loopback self-test entries. Return new test
+ * index.
*/
static int efx_fill_loopback_test(struct efx_nic *efx,
struct efx_loopback_self_tests *lb_tests,
@@ -290,6 +293,12 @@ static int efx_fill_loopback_test(struct efx_nic *efx,
* @tests: Efx self-test results structure, or %NULL
* @strings: Ethtool strings, or %NULL
* @data: Ethtool test results, or %NULL
+ *
+ * Get self-test number of strings, strings, and/or test results.
+ * Return number of strings (== number of test results).
+ *
+ * The reason for merging these three functions is to make sure that
+ * they can never be inconsistent.
*/
static int efx_ethtool_fill_self_tests(struct efx_nic *efx,
struct efx_self_tests *tests,
@@ -444,7 +453,7 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
{
struct efx_nic *efx = netdev_priv(net_dev);
struct efx_self_tests *efx_tests;
- int already_up;
+ bool already_up;
int rc = -ENOMEM;
efx_tests = kzalloc(sizeof(*efx_tests), GFP_KERNEL);
@@ -452,8 +461,8 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
goto fail;
if (efx->state != STATE_READY) {
- rc = -EIO;
- goto fail1;
+ rc = -EBUSY;
+ goto out;
}
netif_info(efx, drv, efx->net_dev, "starting %sline testing\n",
@@ -466,7 +475,7 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
if (rc) {
netif_err(efx, drv, efx->net_dev,
"failed opening device.\n");
- goto fail1;
+ goto out;
}
}
@@ -479,8 +488,7 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
rc == 0 ? "passed" : "failed",
(test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on");
-fail1:
- /* Fill ethtool results structures */
+out:
efx_ethtool_fill_self_tests(efx, efx_tests, NULL, data);
kfree(efx_tests);
fail:
@@ -691,7 +699,6 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO);
}
-
static void efx_ethtool_get_wol(struct net_device *net_dev,
struct ethtool_wolinfo *wol)
{
@@ -720,7 +727,7 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
}
/* MAC address mask including only I/G bit */
-static const u8 mac_addr_ig_mask[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
+static const u8 mac_addr_ig_mask[ETH_ALEN] __aligned(2) = {0x01, 0, 0, 0, 0, 0};
#define IP4_ADDR_FULL_MASK ((__force __be32)~0)
#define PORT_FULL_MASK ((__force __be16)~0)
@@ -780,16 +787,16 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx,
rule->flow_type = ETHER_FLOW;
if (spec.match_flags &
(EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG)) {
- memcpy(mac_entry->h_dest, spec.loc_mac, ETH_ALEN);
+ ether_addr_copy(mac_entry->h_dest, spec.loc_mac);
if (spec.match_flags & EFX_FILTER_MATCH_LOC_MAC)
- memset(mac_mask->h_dest, ~0, ETH_ALEN);
+ eth_broadcast_addr(mac_mask->h_dest);
else
- memcpy(mac_mask->h_dest, mac_addr_ig_mask,
- ETH_ALEN);
+ ether_addr_copy(mac_mask->h_dest,
+ mac_addr_ig_mask);
}
if (spec.match_flags & EFX_FILTER_MATCH_REM_MAC) {
- memcpy(mac_entry->h_source, spec.rem_mac, ETH_ALEN);
- memset(mac_mask->h_source, ~0, ETH_ALEN);
+ ether_addr_copy(mac_entry->h_source, spec.rem_mac);
+ eth_broadcast_addr(mac_mask->h_source);
}
if (spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE) {
mac_entry->h_proto = spec.ether_type;
@@ -961,13 +968,13 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx,
spec.match_flags |= EFX_FILTER_MATCH_LOC_MAC;
else
return -EINVAL;
- memcpy(spec.loc_mac, mac_entry->h_dest, ETH_ALEN);
+ ether_addr_copy(spec.loc_mac, mac_entry->h_dest);
}
if (!is_zero_ether_addr(mac_mask->h_source)) {
if (!is_broadcast_ether_addr(mac_mask->h_source))
return -EINVAL;
spec.match_flags |= EFX_FILTER_MATCH_REM_MAC;
- memcpy(spec.rem_mac, mac_entry->h_source, ETH_ALEN);
+ ether_addr_copy(spec.rem_mac, mac_entry->h_source);
}
if (mac_mask->h_proto) {
if (mac_mask->h_proto != ETHER_TYPE_FULL_MASK)
diff --git a/drivers/net/ethernet/sfc/falcon.c b/drivers/net/ethernet/sfc/falcon.c
index 18d6f761f4d0..8ec20b713cc6 100644
--- a/drivers/net/ethernet/sfc/falcon.c
+++ b/drivers/net/ethernet/sfc/falcon.c
@@ -422,7 +422,6 @@ static inline void falcon_irq_ack_a1(struct efx_nic *efx)
efx_readd(efx, &reg, FR_AA_WORK_AROUND_BROKEN_PCI_READS);
}
-
static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id)
{
struct efx_nic *efx = dev_id;
@@ -467,6 +466,7 @@ static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id)
efx_schedule_channel_irq(efx_get_channel(efx, 1));
return IRQ_HANDLED;
}
+
/**************************************************************************
*
* RSS
@@ -1358,6 +1358,7 @@ static void falcon_reconfigure_mac_wrapper(struct efx_nic *efx)
case 100: link_speed = 1; break;
default: link_speed = 0; break;
}
+
/* MAC_LINK_STATUS controls MAC backpressure but doesn't work
* as advertised. Disable to ensure packets are not
* indefinitely held and TX queue can be flushed at any point
@@ -2182,7 +2183,7 @@ static int falcon_probe_nvconfig(struct efx_nic *efx)
}
/* Read the MAC addresses */
- memcpy(efx->net_dev->perm_addr, nvconfig->mac_address[0], ETH_ALEN);
+ ether_addr_copy(efx->net_dev->perm_addr, nvconfig->mac_address[0]);
netif_dbg(efx, probe, efx->net_dev, "PHY is %d phy_id %d\n",
efx->phy_type, efx->mdio.prtad);
@@ -2868,4 +2869,3 @@ const struct efx_nic_type falcon_b0_nic_type = {
.mcdi_max_ver = -1,
.max_rx_ip_filters = FR_BZ_RX_FILTER_TBL0_ROWS,
};
-
diff --git a/drivers/net/ethernet/sfc/farch.c b/drivers/net/ethernet/sfc/farch.c
index f72489a105ca..a08761360cdf 100644
--- a/drivers/net/ethernet/sfc/farch.c
+++ b/drivers/net/ethernet/sfc/farch.c
@@ -311,7 +311,6 @@ static inline void efx_farch_push_tx_desc(struct efx_tx_queue *tx_queue,
*/
void efx_farch_tx_write(struct efx_tx_queue *tx_queue)
{
-
struct efx_tx_buffer *buffer;
efx_qword_t *txd;
unsigned write_ptr;
@@ -1249,6 +1248,9 @@ int efx_farch_ev_process(struct efx_channel *channel, int budget)
int tx_packets = 0;
int spent = 0;
+ if (budget <= 0)
+ return spent;
+
read_ptr = channel->eventq_read_ptr;
for (;;) {
@@ -1609,7 +1611,6 @@ irqreturn_t efx_farch_msi_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-
/* Setup RSS indirection table.
* This maps from the hash value of the packet to RXQ
*/
diff --git a/drivers/net/ethernet/sfc/filter.h b/drivers/net/ethernet/sfc/filter.h
index 3ef298d3c47e..d0ed7f71ea7e 100644
--- a/drivers/net/ethernet/sfc/filter.h
+++ b/drivers/net/ethernet/sfc/filter.h
@@ -243,7 +243,7 @@ static inline int efx_filter_set_eth_local(struct efx_filter_spec *spec,
}
if (addr != NULL) {
spec->match_flags |= EFX_FILTER_MATCH_LOC_MAC;
- memcpy(spec->loc_mac, addr, ETH_ALEN);
+ ether_addr_copy(spec->loc_mac, addr);
}
return 0;
}
diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c
index eb59abb57e85..7bd4b14bf3b3 100644
--- a/drivers/net/ethernet/sfc/mcdi.c
+++ b/drivers/net/ethernet/sfc/mcdi.c
@@ -1187,6 +1187,9 @@ int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address,
int rc;
BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_IN_LEN != 0);
+ /* we need __aligned(2) for ether_addr_copy */
+ BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST & 1);
+ BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST & 1);
rc = efx_mcdi_rpc(efx, MC_CMD_GET_BOARD_CFG, NULL, 0,
outbuf, sizeof(outbuf), &outlen);
@@ -1199,11 +1202,10 @@ int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address,
}
if (mac_address)
- memcpy(mac_address,
- port_num ?
- MCDI_PTR(outbuf, GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1) :
- MCDI_PTR(outbuf, GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0),
- ETH_ALEN);
+ ether_addr_copy(mac_address,
+ port_num ?
+ MCDI_PTR(outbuf, GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1) :
+ MCDI_PTR(outbuf, GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0));
if (fw_subtype_list) {
for (i = 0;
i < MCDI_VAR_ARRAY_LEN(outlen,
@@ -1532,7 +1534,7 @@ static int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type,
MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_WOL_TYPE, type);
MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_FILTER_MODE,
MC_CMD_FILTER_MODE_SIMPLE);
- memcpy(MCDI_PTR(inbuf, WOL_FILTER_SET_IN_MAGIC_MAC), mac, ETH_ALEN);
+ ether_addr_copy(MCDI_PTR(inbuf, WOL_FILTER_SET_IN_MAGIC_MAC), mac);
rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_SET, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &outlen);
diff --git a/drivers/net/ethernet/sfc/mcdi_port.c b/drivers/net/ethernet/sfc/mcdi_port.c
index 91d23252f8fa..e5fc4e1574b5 100644
--- a/drivers/net/ethernet/sfc/mcdi_port.c
+++ b/drivers/net/ethernet/sfc/mcdi_port.c
@@ -854,8 +854,8 @@ int efx_mcdi_set_mac(struct efx_nic *efx)
BUILD_BUG_ON(MC_CMD_SET_MAC_OUT_LEN != 0);
- memcpy(MCDI_PTR(cmdbytes, SET_MAC_IN_ADDR),
- efx->net_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(MCDI_PTR(cmdbytes, SET_MAC_IN_ADDR),
+ efx->net_dev->dev_addr);
MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_MTU,
EFX_MAX_FRAME_LEN(efx->net_dev->mtu));
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
index af2b8c59a903..8a400a0595eb 100644
--- a/drivers/net/ethernet/sfc/net_driver.h
+++ b/drivers/net/ethernet/sfc/net_driver.h
@@ -1323,7 +1323,6 @@ static inline struct efx_rx_buffer *efx_rx_buffer(struct efx_rx_queue *rx_queue,
return &rx_queue->buffer[index];
}
-
/**
* EFX_MAX_FRAME_LEN - calculate maximum frame length
*
diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c
index 79226b19e3c4..32d969e857f7 100644
--- a/drivers/net/ethernet/sfc/nic.c
+++ b/drivers/net/ethernet/sfc/nic.c
@@ -530,4 +530,3 @@ void efx_nic_fix_nodesc_drop_stat(struct efx_nic *efx, u64 *rx_nodesc_drops)
efx->rx_nodesc_drops_prev_state = !!(efx->net_dev->flags & IFF_UP);
*rx_nodesc_drops -= efx->rx_nodesc_drops_while_down;
}
-
diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c
index d7a36829649a..6b861e3de4b0 100644
--- a/drivers/net/ethernet/sfc/ptp.c
+++ b/drivers/net/ethernet/sfc/ptp.c
@@ -223,7 +223,6 @@ struct efx_ptp_timeset {
* @evt_list: List of MC receive events awaiting packets
* @evt_free_list: List of free events
* @evt_lock: Lock for manipulating evt_list and evt_free_list
- * @evt_overflow: Boolean indicating that event list has overflowed
* @rx_evts: Instantiated events (on evt_list and evt_free_list)
* @workwq: Work queue for processing pending PTP operations
* @work: Work task
@@ -275,7 +274,6 @@ struct efx_ptp_data {
struct list_head evt_list;
struct list_head evt_free_list;
spinlock_t evt_lock;
- bool evt_overflow;
struct efx_ptp_event_rx rx_evts[MAX_RECEIVE_EVENTS];
struct workqueue_struct *workwq;
struct work_struct work;
@@ -768,37 +766,36 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
return -EAGAIN;
}
- /* Convert the NIC time into kernel time. No correction is required-
- * this time is the output of a firmware process.
- */
- mc_time = ptp->nic_to_kernel_time(ptp->timeset[last_good].major,
- ptp->timeset[last_good].minor, 0);
-
- /* Calculate delay from actual PPS to last_time */
- delta = ktime_to_timespec(mc_time);
- delta.tv_nsec +=
- last_time->ts_real.tv_nsec -
- (ptp->timeset[last_good].host_start & MC_NANOSECOND_MASK);
-
- /* It is possible that the seconds rolled over between taking
+ /* Calculate delay from last good sync (host time) to last_time.
+ * It is possible that the seconds rolled over between taking
* the start reading and the last value written by the host. The
* timescales are such that a gap of more than one second is never
- * expected.
+ * expected. delta is *not* normalised.
*/
start_sec = ptp->timeset[last_good].host_start >> MC_NANOSECOND_BITS;
last_sec = last_time->ts_real.tv_sec & MC_SECOND_MASK;
- if (start_sec != last_sec) {
- if (((start_sec + 1) & MC_SECOND_MASK) != last_sec) {
- netif_warn(efx, hw, efx->net_dev,
- "PTP bad synchronisation seconds\n");
- return -EAGAIN;
- } else {
- delta.tv_sec = 1;
- }
- } else {
- delta.tv_sec = 0;
+ if (start_sec != last_sec &&
+ ((start_sec + 1) & MC_SECOND_MASK) != last_sec) {
+ netif_warn(efx, hw, efx->net_dev,
+ "PTP bad synchronisation seconds\n");
+ return -EAGAIN;
}
+ delta.tv_sec = (last_sec - start_sec) & 1;
+ delta.tv_nsec =
+ last_time->ts_real.tv_nsec -
+ (ptp->timeset[last_good].host_start & MC_NANOSECOND_MASK);
+
+ /* Convert the NIC time at last good sync into kernel time.
+ * No correction is required - this time is the output of a
+ * firmware process.
+ */
+ mc_time = ptp->nic_to_kernel_time(ptp->timeset[last_good].major,
+ ptp->timeset[last_good].minor, 0);
+
+ /* Calculate delay from NIC top of second to last_time */
+ delta.tv_nsec += ktime_to_timespec(mc_time).tv_nsec;
+ /* Set PPS timestamp to match NIC top of second */
ptp->host_time_pps = *last_time;
pps_sub_ts(&ptp->host_time_pps, delta);
@@ -941,11 +938,6 @@ static void efx_ptp_drop_time_expired_events(struct efx_nic *efx)
}
}
}
- /* If the event overflow flag is set and the event list is now empty
- * clear the flag to re-enable the overflow warning message.
- */
- if (ptp->evt_overflow && list_empty(&ptp->evt_list))
- ptp->evt_overflow = false;
spin_unlock_bh(&ptp->evt_lock);
}
@@ -989,11 +981,6 @@ static enum ptp_packet_state efx_ptp_match_rx(struct efx_nic *efx,
break;
}
}
- /* If the event overflow flag is set and the event list is now empty
- * clear the flag to re-enable the overflow warning message.
- */
- if (ptp->evt_overflow && list_empty(&ptp->evt_list))
- ptp->evt_overflow = false;
spin_unlock_bh(&ptp->evt_lock);
return rc;
@@ -1147,7 +1134,6 @@ static int efx_ptp_stop(struct efx_nic *efx)
list_for_each_safe(cursor, next, &efx->ptp_data->evt_list) {
list_move(cursor, &efx->ptp_data->evt_free_list);
}
- ptp->evt_overflow = false;
spin_unlock_bh(&efx->ptp_data->evt_lock);
return rc;
@@ -1208,6 +1194,7 @@ static const struct ptp_clock_info efx_phc_clock_info = {
.n_alarm = 0,
.n_ext_ts = 0,
.n_per_out = 0,
+ .n_pins = 0,
.pps = 1,
.adjfreq = efx_phc_adjfreq,
.adjtime = efx_phc_adjtime,
@@ -1253,7 +1240,6 @@ int efx_ptp_probe(struct efx_nic *efx, struct efx_channel *channel)
spin_lock_init(&ptp->evt_lock);
for (pos = 0; pos < MAX_RECEIVE_EVENTS; pos++)
list_add(&ptp->rx_evts[pos].link, &ptp->evt_free_list);
- ptp->evt_overflow = false;
/* Get the NIC PTP attributes and set up time conversions */
rc = efx_ptp_get_attributes(efx);
@@ -1380,6 +1366,7 @@ static bool efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
struct efx_ptp_match *match = (struct efx_ptp_match *)skb->cb;
u8 *match_data_012, *match_data_345;
unsigned int version;
+ u8 *data;
match->expiry = jiffies + msecs_to_jiffies(PKT_EVENT_LIFETIME_MS);
@@ -1388,7 +1375,8 @@ static bool efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
if (!pskb_may_pull(skb, PTP_V1_MIN_LENGTH)) {
return false;
}
- version = ntohs(*(__be16 *)&skb->data[PTP_V1_VERSION_OFFSET]);
+ data = skb->data;
+ version = ntohs(*(__be16 *)&data[PTP_V1_VERSION_OFFSET]);
if (version != PTP_VERSION_V1) {
return false;
}
@@ -1396,13 +1384,14 @@ static bool efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
/* PTP V1 uses all six bytes of the UUID to match the packet
* to the timestamp
*/
- match_data_012 = skb->data + PTP_V1_UUID_OFFSET;
- match_data_345 = skb->data + PTP_V1_UUID_OFFSET + 3;
+ match_data_012 = data + PTP_V1_UUID_OFFSET;
+ match_data_345 = data + PTP_V1_UUID_OFFSET + 3;
} else {
if (!pskb_may_pull(skb, PTP_V2_MIN_LENGTH)) {
return false;
}
- version = skb->data[PTP_V2_VERSION_OFFSET];
+ data = skb->data;
+ version = data[PTP_V2_VERSION_OFFSET];
if ((version & PTP_VERSION_V2_MASK) != PTP_VERSION_V2) {
return false;
}
@@ -1414,17 +1403,17 @@ static bool efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
* enhanced mode fixes this issue and uses bytes 0-2
* and byte 5-7 of the UUID.
*/
- match_data_345 = skb->data + PTP_V2_UUID_OFFSET + 5;
+ match_data_345 = data + PTP_V2_UUID_OFFSET + 5;
if (ptp->mode == MC_CMD_PTP_MODE_V2) {
- match_data_012 = skb->data + PTP_V2_UUID_OFFSET + 2;
+ match_data_012 = data + PTP_V2_UUID_OFFSET + 2;
} else {
- match_data_012 = skb->data + PTP_V2_UUID_OFFSET + 0;
+ match_data_012 = data + PTP_V2_UUID_OFFSET + 0;
BUG_ON(ptp->mode != MC_CMD_PTP_MODE_V2_ENHANCED);
}
}
/* Does this packet require timestamping? */
- if (ntohs(*(__be16 *)&skb->data[PTP_DPORT_OFFSET]) == PTP_EVENT_PORT) {
+ if (ntohs(*(__be16 *)&data[PTP_DPORT_OFFSET]) == PTP_EVENT_PORT) {
match->state = PTP_PACKET_STATE_UNMATCHED;
/* We expect the sequence number to be in the same position in
@@ -1440,8 +1429,8 @@ static bool efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
(match_data_345[0] << 24));
match->words[1] = (match_data_345[1] |
(match_data_345[2] << 8) |
- (skb->data[PTP_V1_SEQUENCE_OFFSET +
- PTP_V1_SEQUENCE_LENGTH - 1] <<
+ (data[PTP_V1_SEQUENCE_OFFSET +
+ PTP_V1_SEQUENCE_LENGTH - 1] <<
16));
} else {
match->state = PTP_PACKET_STATE_MATCH_UNWANTED;
@@ -1635,13 +1624,9 @@ static void ptp_event_rx(struct efx_nic *efx, struct efx_ptp_data *ptp)
list_add_tail(&evt->link, &ptp->evt_list);
queue_work(ptp->workwq, &ptp->work);
- } else if (!ptp->evt_overflow) {
- /* Log a warning message and set the event overflow flag.
- * The message won't be logged again until the event queue
- * becomes empty.
- */
+ } else if (net_ratelimit()) {
+ /* Log a rate-limited warning message. */
netif_err(efx, rx_err, efx->net_dev, "PTP event queue overflow\n");
- ptp->evt_overflow = true;
}
spin_unlock_bh(&ptp->evt_lock);
}
diff --git a/drivers/net/ethernet/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c
index 26641817a9c7..0fc5baef45b1 100644
--- a/drivers/net/ethernet/sfc/selftest.c
+++ b/drivers/net/ethernet/sfc/selftest.c
@@ -50,7 +50,7 @@ struct efx_loopback_payload {
} __packed;
/* Loopback test source MAC address */
-static const unsigned char payload_source[ETH_ALEN] = {
+static const u8 payload_source[ETH_ALEN] __aligned(2) = {
0x00, 0x0f, 0x53, 0x1b, 0x1b, 0x1b,
};
@@ -366,8 +366,8 @@ static void efx_iterate_state(struct efx_nic *efx)
struct efx_loopback_payload *payload = &state->payload;
/* Initialise the layerII header */
- memcpy(&payload->header.h_dest, net_dev->dev_addr, ETH_ALEN);
- memcpy(&payload->header.h_source, &payload_source, ETH_ALEN);
+ ether_addr_copy((u8 *)&payload->header.h_dest, net_dev->dev_addr);
+ ether_addr_copy((u8 *)&payload->header.h_source, payload_source);
payload->header.h_proto = htons(ETH_P_IP);
/* saddr set later and used as incrementing count */
diff --git a/drivers/net/ethernet/sfc/siena_sriov.c b/drivers/net/ethernet/sfc/siena_sriov.c
index 0c38f926871e..9a9205e77896 100644
--- a/drivers/net/ethernet/sfc/siena_sriov.c
+++ b/drivers/net/ethernet/sfc/siena_sriov.c
@@ -1095,7 +1095,7 @@ static void efx_sriov_peer_work(struct work_struct *data)
/* Fill the remaining addresses */
list_for_each_entry(local_addr, &efx->local_addr_list, link) {
- memcpy(peer->mac_addr, local_addr->addr, ETH_ALEN);
+ ether_addr_copy(peer->mac_addr, local_addr->addr);
peer->tci = 0;
++peer;
++peer_count;
@@ -1303,8 +1303,7 @@ int efx_sriov_init(struct efx_nic *efx)
goto fail_vfs;
rtnl_lock();
- memcpy(vfdi_status->peers[0].mac_addr,
- net_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(vfdi_status->peers[0].mac_addr, net_dev->dev_addr);
efx->vf_init_count = efx->vf_count;
rtnl_unlock();
@@ -1452,8 +1451,8 @@ void efx_sriov_mac_address_changed(struct efx_nic *efx)
if (!efx->vf_init_count)
return;
- memcpy(vfdi_status->peers[0].mac_addr,
- efx->net_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(vfdi_status->peers[0].mac_addr,
+ efx->net_dev->dev_addr);
queue_work(vfdi_workqueue, &efx->peer_work);
}
@@ -1570,7 +1569,7 @@ int efx_sriov_set_vf_mac(struct net_device *net_dev, int vf_i, u8 *mac)
vf = efx->vf + vf_i;
mutex_lock(&vf->status_lock);
- memcpy(vf->addr.mac_addr, mac, ETH_ALEN);
+ ether_addr_copy(vf->addr.mac_addr, mac);
__efx_sriov_update_vf_addr(vf);
mutex_unlock(&vf->status_lock);
@@ -1633,7 +1632,7 @@ int efx_sriov_get_vf_config(struct net_device *net_dev, int vf_i,
vf = efx->vf + vf_i;
ivi->vf = vf_i;
- memcpy(ivi->mac, vf->addr.mac_addr, ETH_ALEN);
+ ether_addr_copy(ivi->mac, vf->addr.mac_addr);
ivi->tx_rate = 0;
tci = ntohs(vf->addr.tci);
ivi->vlan = tci & VLAN_VID_MASK;
diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c
index 75d11fa4eb0a..fa9475300411 100644
--- a/drivers/net/ethernet/sfc/tx.c
+++ b/drivers/net/ethernet/sfc/tx.c
@@ -787,15 +787,6 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue)
* Requires TX checksum offload support.
*/
-/* Number of bytes inserted at the start of a TSO header buffer,
- * similar to NET_IP_ALIGN.
- */
-#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
-#define TSOH_OFFSET 0
-#else
-#define TSOH_OFFSET NET_IP_ALIGN
-#endif
-
#define PTR_DIFF(p1, p2) ((u8 *)(p1) - (u8 *)(p2))
/**
@@ -882,13 +873,13 @@ static u8 *efx_tsoh_get_buffer(struct efx_tx_queue *tx_queue,
EFX_BUG_ON_PARANOID(buffer->flags);
EFX_BUG_ON_PARANOID(buffer->unmap_len);
- if (likely(len <= TSOH_STD_SIZE - TSOH_OFFSET)) {
+ if (likely(len <= TSOH_STD_SIZE - NET_IP_ALIGN)) {
unsigned index =
(tx_queue->insert_count & tx_queue->ptr_mask) / 2;
struct efx_buffer *page_buf =
&tx_queue->tsoh_page[index / TSOH_PER_PAGE];
unsigned offset =
- TSOH_STD_SIZE * (index % TSOH_PER_PAGE) + TSOH_OFFSET;
+ TSOH_STD_SIZE * (index % TSOH_PER_PAGE) + NET_IP_ALIGN;
if (unlikely(!page_buf->addr) &&
efx_nic_alloc_buffer(tx_queue->efx, page_buf, PAGE_SIZE,
@@ -901,10 +892,10 @@ static u8 *efx_tsoh_get_buffer(struct efx_tx_queue *tx_queue,
} else {
tx_queue->tso_long_headers++;
- buffer->heap_buf = kmalloc(TSOH_OFFSET + len, GFP_ATOMIC);
+ buffer->heap_buf = kmalloc(NET_IP_ALIGN + len, GFP_ATOMIC);
if (unlikely(!buffer->heap_buf))
return NULL;
- result = (u8 *)buffer->heap_buf + TSOH_OFFSET;
+ result = (u8 *)buffer->heap_buf + NET_IP_ALIGN;
buffer->flags = EFX_TX_BUF_CONT | EFX_TX_BUF_HEAP;
}
@@ -1011,7 +1002,7 @@ static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue)
static int tso_start(struct tso_state *st, struct efx_nic *efx,
const struct sk_buff *skb)
{
- bool use_options = efx_nic_rev(efx) >= EFX_REV_HUNT_A0;
+ 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;
dma_addr_t dma_addr;
@@ -1037,7 +1028,7 @@ static int tso_start(struct tso_state *st, struct efx_nic *efx,
st->out_len = skb->len - header_len;
- if (!use_options) {
+ if (!use_opt_desc) {
st->header_unmap_len = 0;
if (likely(in_len == 0)) {
diff --git a/drivers/net/ethernet/silan/sc92031.c b/drivers/net/ethernet/silan/sc92031.c
index 5eb933c97bba..7daa7d433099 100644
--- a/drivers/net/ethernet/silan/sc92031.c
+++ b/drivers/net/ethernet/silan/sc92031.c
@@ -987,7 +987,7 @@ out_unlock:
spin_unlock(&priv->lock);
out:
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/sis/sis900.c b/drivers/net/ethernet/sis/sis900.c
index ff57a46388ee..6072f093e6b4 100644
--- a/drivers/net/ethernet/sis/sis900.c
+++ b/drivers/net/ethernet/sis/sis900.c
@@ -1614,7 +1614,7 @@ sis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
skb->data, skb->len, PCI_DMA_TODEVICE);
if (unlikely(pci_dma_mapping_error(sis_priv->pci_dev,
sis_priv->tx_ring[entry].bufptr))) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
sis_priv->tx_skbuff[entry] = NULL;
net_dev->stats.tx_dropped++;
spin_unlock_irqrestore(&sis_priv->lock, flags);
diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c
index c50fb08c9905..66b05e62f70a 100644
--- a/drivers/net/ethernet/smsc/smc911x.c
+++ b/drivers/net/ethernet/smsc/smc911x.c
@@ -551,7 +551,7 @@ static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->stats.tx_errors++;
dev->stats.tx_dropped++;
spin_unlock_irqrestore(&lp->lock, flags);
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
index 839c0e6cca01..d1b4dca53a9d 100644
--- a/drivers/net/ethernet/smsc/smc91x.c
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -621,7 +621,7 @@ static void smc_hardware_send_pkt(unsigned long data)
done: if (!THROTTLE_TX_PKTS)
netif_wake_queue(dev);
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
}
/*
@@ -657,7 +657,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
netdev_warn(dev, "Far too big packet error.\n");
dev->stats.tx_errors++;
dev->stats.tx_dropped++;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
index 6382b7c416f4..a0fc151da40d 100644
--- a/drivers/net/ethernet/smsc/smsc911x.c
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -439,7 +439,8 @@ static int smsc911x_request_resources(struct platform_device *pdev)
/* Request clock */
pdata->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(pdata->clk))
- netdev_warn(ndev, "couldn't get clock %li\n", PTR_ERR(pdata->clk));
+ dev_dbg(&pdev->dev, "couldn't get clock %li\n",
+ PTR_ERR(pdata->clk));
return ret;
}
@@ -1672,7 +1673,7 @@ static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
pdata->ops->tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
freespace -= (skb->len + 32);
skb_tx_timestamp(skb);
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
smsc911x_tx_update_txcounters(dev);
@@ -2379,8 +2380,6 @@ static int smsc911x_drv_probe(struct platform_device *pdev)
int res_size, irq_flags;
int retval;
- pr_info("Driver version %s\n", SMSC_DRV_VERSION);
-
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"smsc911x-memory");
if (!res)
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index f2d7c702c77f..2d09c116cbc8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -26,6 +26,16 @@ config STMMAC_PLATFORM
If unsure, say N.
+config DWMAC_SOCFPGA
+ bool "SOCFPGA dwmac support"
+ depends on STMMAC_PLATFORM && MFD_SYSCON && (ARCH_SOCFPGA || COMPILE_TEST)
+ help
+ Support for ethernet controller on Altera SOCFPGA
+
+ This selects the Altera SOCFPGA SoC glue layer support
+ for the stmmac device driver. This driver is used for
+ arria5 and cyclone5 FPGA SoCs.
+
config DWMAC_SUNXI
bool "Allwinner GMAC support"
depends on STMMAC_PLATFORM && ARCH_SUNXI
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index dcef28775dad..18695ebef7e4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -3,6 +3,7 @@ stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
stmmac-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o
stmmac-$(CONFIG_DWMAC_STI) += dwmac-sti.o
+stmmac-$(CONFIG_DWMAC_SOCFPGA) += dwmac-socfpga.o
stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
new file mode 100644
index 000000000000..fd8a217556a1
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -0,0 +1,130 @@
+/* Copyright Altera Corporation (C) 2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Adopted from dwmac-sti.c
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/phy.h>
+#include <linux/regmap.h>
+#include <linux/stmmac.h>
+
+#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0
+#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1
+#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2
+#define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2
+#define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003
+
+struct socfpga_dwmac {
+ int interface;
+ u32 reg_offset;
+ u32 reg_shift;
+ struct device *dev;
+ struct regmap *sys_mgr_base_addr;
+};
+
+static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct regmap *sys_mgr_base_addr;
+ u32 reg_offset, reg_shift;
+ int ret;
+
+ dwmac->interface = of_get_phy_mode(np);
+
+ sys_mgr_base_addr = syscon_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
+ if (IS_ERR(sys_mgr_base_addr)) {
+ dev_info(dev, "No sysmgr-syscon node found\n");
+ return PTR_ERR(sys_mgr_base_addr);
+ }
+
+ ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, &reg_offset);
+ if (ret) {
+ dev_info(dev, "Could not read reg_offset from sysmgr-syscon!\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, &reg_shift);
+ if (ret) {
+ dev_info(dev, "Could not read reg_shift from sysmgr-syscon!\n");
+ return -EINVAL;
+ }
+
+ dwmac->reg_offset = reg_offset;
+ dwmac->reg_shift = reg_shift;
+ dwmac->sys_mgr_base_addr = sys_mgr_base_addr;
+ dwmac->dev = dev;
+
+ return 0;
+}
+
+static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac)
+{
+ struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr;
+ int phymode = dwmac->interface;
+ u32 reg_offset = dwmac->reg_offset;
+ u32 reg_shift = dwmac->reg_shift;
+ u32 ctrl, val;
+
+ switch (phymode) {
+ case PHY_INTERFACE_MODE_RGMII:
+ val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ case PHY_INTERFACE_MODE_GMII:
+ val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
+ break;
+ default:
+ dev_err(dwmac->dev, "bad phy mode %d\n", phymode);
+ return -EINVAL;
+ }
+
+ regmap_read(sys_mgr_base_addr, reg_offset, &ctrl);
+ ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift);
+ ctrl |= val << reg_shift;
+
+ regmap_write(sys_mgr_base_addr, reg_offset, ctrl);
+ return 0;
+}
+
+static void *socfpga_dwmac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret;
+ struct socfpga_dwmac *dwmac;
+
+ dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL);
+ if (!dwmac)
+ return ERR_PTR(-ENOMEM);
+
+ ret = socfpga_dwmac_parse_data(dwmac, dev);
+ if (ret) {
+ dev_err(dev, "Unable to parse OF data\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = socfpga_dwmac_setup(dwmac);
+ if (ret) {
+ dev_err(dev, "couldn't setup SoC glue (%d)\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ return dwmac;
+}
+
+const struct stmmac_of_data socfpga_gmac_data = {
+ .setup = socfpga_dwmac_probe,
+};
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index f9e60d7918c4..ca01035634a7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -136,6 +136,9 @@ extern const struct stmmac_of_data sun7i_gmac_data;
#ifdef CONFIG_DWMAC_STI
extern const struct stmmac_of_data sti_gmac_data;
#endif
+#ifdef CONFIG_DWMAC_SOCFPGA
+extern const struct stmmac_of_data socfpga_gmac_data;
+#endif
extern struct platform_driver stmmac_pltfr_driver;
static inline int stmmac_register_platform(void)
{
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 8543e1cfd55e..d940034acdd4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1303,7 +1303,7 @@ static void stmmac_tx_clean(struct stmmac_priv *priv)
priv->hw->mode->clean_desc3(priv, p);
if (likely(skb != NULL)) {
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
priv->tx_skbuff[entry] = NULL;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 8fb32a80f1c1..46aef5108bea 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -38,6 +38,9 @@ static const struct of_device_id stmmac_dt_ids[] = {
{ .compatible = "st,stih416-dwmac", .data = &sti_gmac_data},
{ .compatible = "st,stid127-dwmac", .data = &sti_gmac_data},
#endif
+#ifdef CONFIG_DWMAC_SOCFPGA
+ { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gmac_data },
+#endif
/* SoC specific glue layers should come before generic bindings */
{ .compatible = "st,spear600-gmac"},
{ .compatible = "snps,dwmac-3.610"},
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
index 7680581ebe12..b7ad3565566c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
@@ -164,6 +164,7 @@ static struct ptp_clock_info stmmac_ptp_clock_ops = {
.n_alarm = 0,
.n_ext_ts = 0,
.n_per_out = 0,
+ .n_pins = 0,
.pps = 0,
.adjfreq = stmmac_adjust_freq,
.adjtime = stmmac_adjust_time,
diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c
index 8e2266e1f260..79606f47a08e 100644
--- a/drivers/net/ethernet/sun/niu.c
+++ b/drivers/net/ethernet/sun/niu.c
@@ -9041,7 +9041,7 @@ static void niu_try_msix(struct niu *np, u8 *ldg_num_map)
struct msix_entry msi_vec[NIU_NUM_LDG];
struct niu_parent *parent = np->parent;
struct pci_dev *pdev = np->pdev;
- int i, num_irqs, err;
+ int i, num_irqs;
u8 first_ldg;
first_ldg = (NIU_NUM_LDG / parent->num_ports) * np->port;
@@ -9053,21 +9053,16 @@ static void niu_try_msix(struct niu *np, u8 *ldg_num_map)
(np->port == 0 ? 3 : 1));
BUG_ON(num_irqs > (NIU_NUM_LDG / parent->num_ports));
-retry:
for (i = 0; i < num_irqs; i++) {
msi_vec[i].vector = 0;
msi_vec[i].entry = i;
}
- err = pci_enable_msix(pdev, msi_vec, num_irqs);
- if (err < 0) {
+ num_irqs = pci_enable_msix_range(pdev, msi_vec, 1, num_irqs);
+ if (num_irqs < 0) {
np->flags &= ~NIU_FLAGS_MSIX;
return;
}
- if (err > 0) {
- num_irqs = err;
- goto retry;
- }
np->flags |= NIU_FLAGS_MSIX;
for (i = 0; i < num_irqs; i++)
diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c
index c2799dc46325..102a66fc54a2 100644
--- a/drivers/net/ethernet/sun/sungem.c
+++ b/drivers/net/ethernet/sun/sungem.c
@@ -688,7 +688,7 @@ static __inline__ void gem_tx(struct net_device *dev, struct gem *gp, u32 gem_st
}
dev->stats.tx_packets++;
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
}
gp->tx_old = entry;
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 7d6d8ec676c8..5d5fec6c4eb0 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -378,7 +378,6 @@ struct cpsw_priv {
u32 version;
u32 coal_intvl;
u32 bus_freq_mhz;
- struct net_device_stats stats;
int rx_packet_max;
int host_port;
struct clk *clk;
@@ -673,8 +672,8 @@ static void cpsw_tx_handler(void *token, int len, int status)
if (unlikely(netif_queue_stopped(ndev)))
netif_wake_queue(ndev);
cpts_tx_timestamp(priv->cpts, skb);
- priv->stats.tx_packets++;
- priv->stats.tx_bytes += len;
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += len;
dev_kfree_skb_any(skb);
}
@@ -700,10 +699,10 @@ static void cpsw_rx_handler(void *token, int len, int status)
cpts_rx_timestamp(priv->cpts, skb);
skb->protocol = eth_type_trans(skb, ndev);
netif_receive_skb(skb);
- priv->stats.rx_bytes += len;
- priv->stats.rx_packets++;
+ ndev->stats.rx_bytes += len;
+ ndev->stats.rx_packets++;
} else {
- priv->stats.rx_dropped++;
+ ndev->stats.rx_dropped++;
new_skb = skb;
}
@@ -1313,7 +1312,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
if (skb_padto(skb, CPSW_MIN_PACKET_SIZE)) {
cpsw_err(priv, tx_err, "packet pad failed\n");
- priv->stats.tx_dropped++;
+ ndev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
@@ -1337,7 +1336,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
fail:
- priv->stats.tx_dropped++;
+ ndev->stats.tx_dropped++;
netif_stop_queue(ndev);
return NETDEV_TX_BUSY;
}
@@ -1477,7 +1476,6 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
{
struct cpsw_priv *priv = netdev_priv(dev);
- struct mii_ioctl_data *data = if_mii(req);
int slave_no = cpsw_slave_index(priv);
if (!netif_running(dev))
@@ -1490,14 +1488,11 @@ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
case SIOCGHWTSTAMP:
return cpsw_hwtstamp_get(dev, req);
#endif
- case SIOCGMIIPHY:
- data->phy_id = priv->slaves[slave_no].phy->addr;
- break;
- default:
- return -ENOTSUPP;
}
- return 0;
+ if (!priv->slaves[slave_no].phy)
+ return -EOPNOTSUPP;
+ return phy_mii_ioctl(priv->slaves[slave_no].phy, req, cmd);
}
static void cpsw_ndo_tx_timeout(struct net_device *ndev)
@@ -1505,7 +1500,7 @@ static void cpsw_ndo_tx_timeout(struct net_device *ndev)
struct cpsw_priv *priv = netdev_priv(ndev);
cpsw_err(priv, tx_err, "transmit timeout, restarting dma\n");
- priv->stats.tx_errors++;
+ ndev->stats.tx_errors++;
cpsw_intr_disable(priv);
cpdma_ctlr_int_ctrl(priv->dma, false);
cpdma_chan_stop(priv->txch);
@@ -1544,12 +1539,6 @@ static int cpsw_ndo_set_mac_address(struct net_device *ndev, void *p)
return 0;
}
-static struct net_device_stats *cpsw_ndo_get_stats(struct net_device *ndev)
-{
- struct cpsw_priv *priv = netdev_priv(ndev);
- return &priv->stats;
-}
-
#ifdef CONFIG_NET_POLL_CONTROLLER
static void cpsw_ndo_poll_controller(struct net_device *ndev)
{
@@ -1642,7 +1631,6 @@ static const struct net_device_ops cpsw_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
.ndo_tx_timeout = cpsw_ndo_tx_timeout,
- .ndo_get_stats = cpsw_ndo_get_stats,
.ndo_set_rx_mode = cpsw_ndo_set_rx_mode,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = cpsw_ndo_poll_controller,
diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c
index 8c351f100aca..a3bbf59eaafd 100644
--- a/drivers/net/ethernet/ti/cpts.c
+++ b/drivers/net/ethernet/ti/cpts.c
@@ -31,10 +31,6 @@
#ifdef CONFIG_TI_CPTS
-static struct sock_filter ptp_filter[] = {
- PTP_FILTER
-};
-
#define cpts_read32(c, r) __raw_readl(&c->reg->r)
#define cpts_write32(c, v, r) __raw_writel(v, &c->reg->r)
@@ -217,6 +213,7 @@ static struct ptp_clock_info cpts_info = {
.name = "CTPS timer",
.max_adj = 1000000,
.n_ext_ts = 0,
+ .n_pins = 0,
.pps = 0,
.adjfreq = cpts_ptp_adjfreq,
.adjtime = cpts_ptp_adjtime,
@@ -300,7 +297,7 @@ static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type)
u64 ns = 0;
struct cpts_event *event;
struct list_head *this, *next;
- unsigned int class = sk_run_filter(skb, ptp_filter);
+ unsigned int class = ptp_classify_raw(skb);
unsigned long flags;
u16 seqid;
u8 mtype;
@@ -371,10 +368,6 @@ int cpts_register(struct device *dev, struct cpts *cpts,
int err, i;
unsigned long flags;
- if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) {
- pr_err("cpts: bad ptp filter\n");
- return -EINVAL;
- }
cpts->info = cpts_info;
cpts->clock = ptp_clock_register(&cpts->info, dev);
if (IS_ERR(cpts->clock)) {
diff --git a/drivers/net/ethernet/tile/tilegx.c b/drivers/net/ethernet/tile/tilegx.c
index 17503da9f7a5..7e1c91d41a87 100644
--- a/drivers/net/ethernet/tile/tilegx.c
+++ b/drivers/net/ethernet/tile/tilegx.c
@@ -659,6 +659,9 @@ static int tile_net_poll(struct napi_struct *napi, int budget)
struct info_mpipe *info_mpipe =
container_of(napi, struct info_mpipe, napi);
+ if (budget <= 0)
+ goto done;
+
instance = info_mpipe->instance;
while ((n = gxio_mpipe_iqueue_try_peek(
&info_mpipe->iqueue,
@@ -870,6 +873,7 @@ static struct ptp_clock_info ptp_mpipe_caps = {
.name = "mPIPE clock",
.max_adj = 999999999,
.n_ext_ts = 0,
+ .n_pins = 0,
.pps = 0,
.adjfreq = ptp_mpipe_adjfreq,
.adjtime = ptp_mpipe_adjtime,
diff --git a/drivers/net/ethernet/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c
index edb2e12a0fe2..e5a5c5d4ce0c 100644
--- a/drivers/net/ethernet/tile/tilepro.c
+++ b/drivers/net/ethernet/tile/tilepro.c
@@ -831,6 +831,9 @@ static int tile_net_poll(struct napi_struct *napi, int budget)
unsigned int work = 0;
+ if (budget <= 0)
+ goto done;
+
while (priv->active) {
int index = qup->__packet_receive_read;
if (index == qsp->__packet_receive_queue.__packet_write)
@@ -1821,7 +1824,7 @@ busy:
/* Handle completions. */
for (i = 0; i < nolds; i++)
- kfree_skb(olds[i]);
+ dev_consume_skb_any(olds[i]);
/* Update stats. */
u64_stats_update_begin(&stats->syncp);
@@ -2005,7 +2008,7 @@ busy:
/* Handle completions. */
for (i = 0; i < nolds; i++)
- kfree_skb(olds[i]);
+ dev_consume_skb_any(olds[i]);
/* HACK: Track "expanded" size for short packets (e.g. 42 < 60). */
u64_stats_update_begin(&stats->syncp);
@@ -2068,14 +2071,14 @@ static struct rtnl_link_stats64 *tile_net_get_stats64(struct net_device *dev,
cpu_stats = &priv->cpu[i]->stats;
do {
- start = u64_stats_fetch_begin_bh(&cpu_stats->syncp);
+ start = u64_stats_fetch_begin_irq(&cpu_stats->syncp);
trx_packets = cpu_stats->rx_packets;
ttx_packets = cpu_stats->tx_packets;
trx_bytes = cpu_stats->rx_bytes;
ttx_bytes = cpu_stats->tx_bytes;
trx_errors = cpu_stats->rx_errors;
trx_dropped = cpu_stats->rx_dropped;
- } while (u64_stats_fetch_retry_bh(&cpu_stats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start));
rx_packets += trx_packets;
tx_packets += ttx_packets;
diff --git a/drivers/net/ethernet/toshiba/spider_net.c b/drivers/net/ethernet/toshiba/spider_net.c
index 3f4a32e39d27..0282d0161859 100644
--- a/drivers/net/ethernet/toshiba/spider_net.c
+++ b/drivers/net/ethernet/toshiba/spider_net.c
@@ -860,7 +860,7 @@ spider_net_release_tx_chain(struct spider_net_card *card, int brutal)
if (skb) {
pci_unmap_single(card->pdev, buf_addr, skb->len,
PCI_DMA_TODEVICE);
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
}
}
return 0;
diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c
index 88e9c73cebc0..fef5573dbfca 100644
--- a/drivers/net/ethernet/toshiba/tc35815.c
+++ b/drivers/net/ethernet/toshiba/tc35815.c
@@ -1645,6 +1645,9 @@ static int tc35815_poll(struct napi_struct *napi, int budget)
int received = 0, handled;
u32 status;
+ if (budget <= 0)
+ return received;
+
spin_lock(&lp->rx_lock);
status = tc_readl(&tr->Int_Src);
do {
diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c
index 6ac20a6738f4..f61dc2b72bb2 100644
--- a/drivers/net/ethernet/via/via-rhine.c
+++ b/drivers/net/ethernet/via/via-rhine.c
@@ -1022,7 +1022,7 @@ static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* The chip-specific entries in the device structure. */
dev->netdev_ops = &rhine_netdev_ops;
- dev->ethtool_ops = &netdev_ethtool_ops,
+ dev->ethtool_ops = &netdev_ethtool_ops;
dev->watchdog_timeo = TX_TIMEOUT;
netif_napi_add(dev, &rp->napi, rhine_napipoll, 64);
@@ -1678,7 +1678,7 @@ static netdev_tx_t rhine_start_tx(struct sk_buff *skb,
/* Must use alignment buffer. */
if (skb->len > PKT_BUF_SZ) {
/* packet too long, drop it */
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
rp->tx_skbuff[entry] = NULL;
dev->stats.tx_dropped++;
return NETDEV_TX_OK;
@@ -1698,7 +1698,7 @@ static netdev_tx_t rhine_start_tx(struct sk_buff *skb,
pci_map_single(rp->pdev, skb->data, skb->len,
PCI_DMA_TODEVICE);
if (dma_mapping_error(&rp->pdev->dev, rp->tx_skbuff_dma[entry])) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
rp->tx_skbuff_dma[entry] = 0;
dev->stats.tx_dropped++;
return NETDEV_TX_OK;
@@ -1836,7 +1836,7 @@ static void rhine_tx(struct net_device *dev)
rp->tx_skbuff[entry]->len,
PCI_DMA_TODEVICE);
}
- dev_kfree_skb(rp->tx_skbuff[entry]);
+ dev_consume_skb_any(rp->tx_skbuff[entry]);
rp->tx_skbuff[entry] = NULL;
entry = (++rp->dirty_tx) % TX_RING_SIZE;
}
@@ -2072,16 +2072,16 @@ rhine_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
netdev_stats_to_stats64(stats, &dev->stats);
do {
- start = u64_stats_fetch_begin_bh(&rp->rx_stats.syncp);
+ start = u64_stats_fetch_begin_irq(&rp->rx_stats.syncp);
stats->rx_packets = rp->rx_stats.packets;
stats->rx_bytes = rp->rx_stats.bytes;
- } while (u64_stats_fetch_retry_bh(&rp->rx_stats.syncp, start));
+ } while (u64_stats_fetch_retry_irq(&rp->rx_stats.syncp, start));
do {
- start = u64_stats_fetch_begin_bh(&rp->tx_stats.syncp);
+ start = u64_stats_fetch_begin_irq(&rp->tx_stats.syncp);
stats->tx_packets = rp->tx_stats.packets;
stats->tx_bytes = rp->tx_stats.bytes;
- } while (u64_stats_fetch_retry_bh(&rp->tx_stats.syncp, start));
+ } while (u64_stats_fetch_retry_irq(&rp->tx_stats.syncp, start));
return stats;
}
diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c
index ad61d26a44f3..de08e86db209 100644
--- a/drivers/net/ethernet/via/via-velocity.c
+++ b/drivers/net/ethernet/via/via-velocity.c
@@ -2565,7 +2565,7 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb,
/* The hardware can handle at most 7 memory segments, so merge
* the skb if there are more */
if (skb_shinfo(skb)->nr_frags > 6 && __skb_linearize(skb)) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c
index 0df36c6ec7f4..104d46f37969 100644
--- a/drivers/net/ethernet/wiznet/w5100.c
+++ b/drivers/net/ethernet/wiznet/w5100.c
@@ -641,11 +641,10 @@ static int w5100_hw_probe(struct platform_device *pdev)
if (!mem)
return -ENXIO;
mem_size = resource_size(mem);
- if (!devm_request_mem_region(&pdev->dev, mem->start, mem_size, name))
- return -EBUSY;
- priv->base = devm_ioremap(&pdev->dev, mem->start, mem_size);
- if (!priv->base)
- return -EBUSY;
+
+ priv->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
spin_lock_init(&priv->reg_lock);
priv->indirect = mem_size < W5100_BUS_DIRECT_SIZE;
diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c
index 71c27b3292f1..1f33c4c86c20 100644
--- a/drivers/net/ethernet/wiznet/w5300.c
+++ b/drivers/net/ethernet/wiznet/w5300.c
@@ -561,11 +561,10 @@ static int w5300_hw_probe(struct platform_device *pdev)
if (!mem)
return -ENXIO;
mem_size = resource_size(mem);
- if (!devm_request_mem_region(&pdev->dev, mem->start, mem_size, name))
- return -EBUSY;
- priv->base = devm_ioremap(&pdev->dev, mem->start, mem_size);
- if (!priv->base)
- return -EBUSY;
+
+ priv->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
spin_lock_init(&priv->reg_lock);
priv->indirect = mem_size < W5300_BUS_DIRECT_SIZE;
diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c
index a4347508031c..fa193c4688da 100644
--- a/drivers/net/ethernet/xilinx/ll_temac_main.c
+++ b/drivers/net/ethernet/xilinx/ll_temac_main.c
@@ -771,8 +771,8 @@ static void ll_temac_recv(struct net_device *ndev)
/* if we're doing rx csum offload, set it up */
if (((lp->temac_features & TEMAC_FEATURE_RX_CSUM) != 0) &&
- (skb->protocol == __constant_htons(ETH_P_IP)) &&
- (skb->len > 64)) {
+ (skb->protocol == htons(ETH_P_IP)) &&
+ (skb->len > 64)) {
skb->csum = cur_p->app3 & 0xFFFF;
skb->ip_summed = CHECKSUM_COMPLETE;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 4bfdf8c7ada0..7b0a73556264 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -756,7 +756,7 @@ static void axienet_recv(struct net_device *ndev)
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
} else if ((lp->features & XAE_FEATURE_PARTIAL_RX_CSUM) != 0 &&
- skb->protocol == __constant_htons(ETH_P_IP) &&
+ skb->protocol == htons(ETH_P_IP) &&
skb->len > 64) {
skb->csum = be32_to_cpu(cur_p->app3 & 0xFFFF);
skb->ip_summed = CHECKSUM_COMPLETE;
diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
index 36052b98b3fc..0d87c67a5ff7 100644
--- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
+++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
@@ -795,18 +795,6 @@ static int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg,
}
/**
- * xemaclite_mdio_reset - Reset the mdio bus.
- * @bus: Pointer to the MII bus
- *
- * This function is required(?) as per Documentation/networking/phy.txt.
- * There is no reset in this device; this function always returns 0.
- */
-static int xemaclite_mdio_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
-/**
* xemaclite_mdio_setup - Register mii_bus for the Emaclite device
* @lp: Pointer to the Emaclite device private data
* @ofdev: Pointer to OF device structure
@@ -861,7 +849,6 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev)
bus->name = "Xilinx Emaclite MDIO";
bus->read = xemaclite_mdio_read;
bus->write = xemaclite_mdio_write;
- bus->reset = xemaclite_mdio_reset;
bus->parent = dev;
bus->irq = lp->mdio_irqs; /* preallocated IRQ table */
@@ -1037,7 +1024,7 @@ static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev)
skb_tx_timestamp(new_skb);
dev->stats.tx_bytes += len;
- dev_kfree_skb(new_skb);
+ dev_consume_skb_any(new_skb);
return 0;
}
diff --git a/drivers/net/ethernet/xscale/Kconfig b/drivers/net/ethernet/xscale/Kconfig
index 3f431019e615..b81bc9fca378 100644
--- a/drivers/net/ethernet/xscale/Kconfig
+++ b/drivers/net/ethernet/xscale/Kconfig
@@ -23,6 +23,7 @@ config IXP4XX_ETH
tristate "Intel IXP4xx Ethernet support"
depends on ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR
select PHYLIB
+ select NET_PTP_CLASSIFY
---help---
Say Y here if you want to use built-in Ethernet ports
on IXP4xx processor.
diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c
index 25283f17d82f..f7e0f0f7c2e2 100644
--- a/drivers/net/ethernet/xscale/ixp4xx_eth.c
+++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c
@@ -256,10 +256,6 @@ static int ports_open;
static struct port *npe_port_tab[MAX_NPES];
static struct dma_pool *dma_pool;
-static struct sock_filter ptp_filter[] = {
- PTP_FILTER
-};
-
static int ixp_ptp_match(struct sk_buff *skb, u16 uid_hi, u32 uid_lo, u16 seqid)
{
u8 *data = skb->data;
@@ -267,7 +263,7 @@ static int ixp_ptp_match(struct sk_buff *skb, u16 uid_hi, u32 uid_lo, u16 seqid)
u16 *hi, *id;
u32 lo;
- if (sk_run_filter(skb, ptp_filter) != PTP_CLASS_V1_IPV4)
+ if (ptp_classify_raw(skb) != PTP_CLASS_V1_IPV4)
return 0;
offset = ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN;
@@ -1413,11 +1409,6 @@ static int eth_init_one(struct platform_device *pdev)
char phy_id[MII_BUS_ID_SIZE + 3];
int err;
- if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) {
- pr_err("ixp4xx_eth: bad ptp filter\n");
- return -EINVAL;
- }
-
if (!(dev = alloc_etherdev(sizeof(struct port))))
return -ENOMEM;
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
index 61dd2447e1bb..81901659cc9e 100644
--- a/drivers/net/hamradio/yam.c
+++ b/drivers/net/hamradio/yam.c
@@ -1184,7 +1184,7 @@ static void __exit yam_cleanup_driver(void)
struct yam_mcs *p;
int i;
- del_timer(&yam_timer);
+ del_timer_sync(&yam_timer);
for (i = 0; i < NR_PORTS; i++) {
struct net_device *dev = yam_devs[i];
if (dev) {
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index 7b594ce3f21d..13010b4dae5b 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -30,6 +30,7 @@
/* Fwd declaration */
struct hv_netvsc_packet;
+struct ndis_tcp_ip_checksum_info;
/* Represent the xfer page packet which contains 1 or more netvsc packet */
struct xferpage_packet {
@@ -73,7 +74,7 @@ struct hv_netvsc_packet {
} completion;
/* This points to the memory after page_buf */
- void *extension;
+ struct rndis_message *rndis_msg;
u32 total_data_buflen;
/* Points to the send/receive buffer where the ethernet frame is */
@@ -117,7 +118,8 @@ int netvsc_send(struct hv_device *device,
void netvsc_linkstatus_callback(struct hv_device *device_obj,
unsigned int status);
int netvsc_recv_callback(struct hv_device *device_obj,
- struct hv_netvsc_packet *packet);
+ struct hv_netvsc_packet *packet,
+ struct ndis_tcp_ip_checksum_info *csum_info);
int rndis_filter_open(struct hv_device *dev);
int rndis_filter_close(struct hv_device *dev);
int rndis_filter_device_add(struct hv_device *dev,
@@ -126,11 +128,6 @@ void rndis_filter_device_remove(struct hv_device *dev);
int rndis_filter_receive(struct hv_device *dev,
struct hv_netvsc_packet *pkt);
-
-
-int rndis_filter_send(struct hv_device *dev,
- struct hv_netvsc_packet *pkt);
-
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);
@@ -139,6 +136,8 @@ int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac);
#define NVSP_PROTOCOL_VERSION_1 2
#define NVSP_PROTOCOL_VERSION_2 0x30002
+#define NVSP_PROTOCOL_VERSION_4 0x40000
+#define NVSP_PROTOCOL_VERSION_5 0x50000
enum {
NVSP_MSG_TYPE_NONE = 0,
@@ -193,6 +192,23 @@ enum {
NVSP_MSG2_TYPE_ALLOC_CHIMNEY_HANDLE,
NVSP_MSG2_TYPE_ALLOC_CHIMNEY_HANDLE_COMP,
+
+ NVSP_MSG2_MAX = NVSP_MSG2_TYPE_ALLOC_CHIMNEY_HANDLE_COMP,
+
+ /* Version 4 messages */
+ NVSP_MSG4_TYPE_SEND_VF_ASSOCIATION,
+ NVSP_MSG4_TYPE_SWITCH_DATA_PATH,
+ NVSP_MSG4_TYPE_UPLINK_CONNECT_STATE_DEPRECATED,
+
+ NVSP_MSG4_MAX = NVSP_MSG4_TYPE_UPLINK_CONNECT_STATE_DEPRECATED,
+
+ /* Version 5 messages */
+ NVSP_MSG5_TYPE_OID_QUERY_EX,
+ NVSP_MSG5_TYPE_OID_QUERY_EX_COMP,
+ NVSP_MSG5_TYPE_SUBCHANNEL,
+ NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE,
+
+ NVSP_MSG5_MAX = NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE,
};
enum {
@@ -447,10 +463,44 @@ union nvsp_2_message_uber {
struct nvsp_2_free_rxbuf free_rxbuf;
} __packed;
+enum nvsp_subchannel_operation {
+ NVSP_SUBCHANNEL_NONE = 0,
+ NVSP_SUBCHANNEL_ALLOCATE,
+ NVSP_SUBCHANNEL_MAX
+};
+
+struct nvsp_5_subchannel_request {
+ u32 op;
+ u32 num_subchannels;
+} __packed;
+
+struct nvsp_5_subchannel_complete {
+ u32 status;
+ u32 num_subchannels; /* Actual number of subchannels allocated */
+} __packed;
+
+struct nvsp_5_send_indirect_table {
+ /* The number of entries in the send indirection table */
+ u32 count;
+
+ /* The offset of the send indireciton table from top of this struct.
+ * The send indirection table tells which channel to put the send
+ * traffic on. Each entry is a channel number.
+ */
+ u32 offset;
+} __packed;
+
+union nvsp_5_message_uber {
+ struct nvsp_5_subchannel_request subchn_req;
+ struct nvsp_5_subchannel_complete subchn_comp;
+ struct nvsp_5_send_indirect_table send_table;
+} __packed;
+
union nvsp_all_messages {
union nvsp_message_init_uber init_msg;
union nvsp_1_message_uber v1_msg;
union nvsp_2_message_uber v2_msg;
+ union nvsp_5_message_uber v5_msg;
} __packed;
/* ALL Messages */
@@ -463,6 +513,7 @@ struct nvsp_message {
#define NETVSC_MTU 65536
#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024*16) /* 16MB */
+#define NETVSC_RECEIVE_BUFFER_SIZE_LEGACY (1024*1024*15) /* 15MB */
#define NETVSC_RECEIVE_BUFFER_ID 0xcafe
@@ -506,6 +557,8 @@ struct netvsc_device {
/* Holds rndis device info */
void *extension;
+ /* The recive buffer for this device */
+ unsigned char cb_buffer[NETVSC_PACKET_SIZE];
};
/* NdisInitialize message */
@@ -671,9 +724,133 @@ struct ndis_pkt_8021q_info {
};
};
+struct ndis_oject_header {
+ u8 type;
+ u8 revision;
+ u16 size;
+};
+
+#define NDIS_OBJECT_TYPE_DEFAULT 0x80
+#define NDIS_OFFLOAD_PARAMETERS_REVISION_3 3
+#define NDIS_OFFLOAD_PARAMETERS_NO_CHANGE 0
+#define NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED 1
+#define NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED 2
+#define NDIS_OFFLOAD_PARAMETERS_LSOV1_ENABLED 2
+#define NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED 1
+#define NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED 2
+#define NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED 1
+#define NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED 2
+#define NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED 3
+#define NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED 4
+
+#define NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE 1
+#define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4 0
+#define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6 1
+
+/*
+ * New offload OIDs for NDIS 6
+ */
+#define OID_TCP_OFFLOAD_CURRENT_CONFIG 0xFC01020B /* query only */
+#define OID_TCP_OFFLOAD_PARAMETERS 0xFC01020C /* set only */
+#define OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES 0xFC01020D/* query only */
+#define OID_TCP_CONNECTION_OFFLOAD_CURRENT_CONFIG 0xFC01020E /* query only */
+#define OID_TCP_CONNECTION_OFFLOAD_HARDWARE_CAPABILITIES 0xFC01020F /* query */
+#define OID_OFFLOAD_ENCAPSULATION 0x0101010A /* set/query */
+
+struct ndis_offload_params {
+ struct ndis_oject_header header;
+ u8 ip_v4_csum;
+ u8 tcp_ip_v4_csum;
+ u8 udp_ip_v4_csum;
+ u8 tcp_ip_v6_csum;
+ u8 udp_ip_v6_csum;
+ u8 lso_v1;
+ u8 ip_sec_v1;
+ u8 lso_v2_ipv4;
+ u8 lso_v2_ipv6;
+ u8 tcp_connection_ip_v4;
+ u8 tcp_connection_ip_v6;
+ u32 flags;
+ u8 ip_sec_v2;
+ u8 ip_sec_v2_ip_v4;
+ struct {
+ u8 rsc_ip_v4;
+ u8 rsc_ip_v6;
+ };
+ struct {
+ u8 encapsulated_packet_task_offload;
+ u8 encapsulation_types;
+ };
+};
+
+struct ndis_tcp_ip_checksum_info {
+ union {
+ struct {
+ u32 is_ipv4:1;
+ u32 is_ipv6:1;
+ u32 tcp_checksum:1;
+ u32 udp_checksum:1;
+ u32 ip_header_checksum:1;
+ u32 reserved:11;
+ u32 tcp_header_offset:10;
+ } transmit;
+ struct {
+ u32 tcp_checksum_failed:1;
+ u32 udp_checksum_failed:1;
+ u32 ip_checksum_failed:1;
+ u32 tcp_checksum_succeeded:1;
+ u32 udp_checksum_succeeded:1;
+ u32 ip_checksum_succeeded:1;
+ u32 loopback:1;
+ u32 tcp_checksum_value_invalid:1;
+ u32 ip_checksum_value_invalid:1;
+ } receive;
+ u32 value;
+ };
+};
+
+struct ndis_tcp_lso_info {
+ union {
+ struct {
+ u32 unused:30;
+ u32 type:1;
+ u32 reserved2:1;
+ } transmit;
+ struct {
+ u32 mss:20;
+ u32 tcp_header_offset:10;
+ u32 type:1;
+ u32 reserved2:1;
+ } lso_v1_transmit;
+ struct {
+ u32 tcp_payload:30;
+ u32 type:1;
+ u32 reserved2:1;
+ } lso_v1_transmit_complete;
+ struct {
+ u32 mss:20;
+ u32 tcp_header_offset:10;
+ u32 type:1;
+ u32 ip_version:1;
+ } lso_v2_transmit;
+ struct {
+ u32 reserved:30;
+ u32 type:1;
+ u32 reserved2:1;
+ } lso_v2_transmit_complete;
+ u32 value;
+ };
+};
+
#define NDIS_VLAN_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
sizeof(struct ndis_pkt_8021q_info))
+#define NDIS_CSUM_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
+ sizeof(struct ndis_tcp_ip_checksum_info))
+
+#define NDIS_LSO_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
+ sizeof(struct ndis_tcp_lso_info))
+
/* Format of Information buffer passed in a SetRequest for the OID */
/* OID_GEN_RNDIS_CONFIG_PARAMETER. */
struct rndis_config_parameter_info {
@@ -846,12 +1023,6 @@ struct rndis_message {
};
-struct rndis_filter_packet {
- void *completion_ctx;
- void (*completion)(void *context);
- struct rndis_message msg;
-};
-
/* Handy macros */
/* get the size of an RNDIS message. Pass in the message type, */
@@ -905,6 +1076,16 @@ struct rndis_filter_packet {
#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
+#define INFO_IPV4 2
+#define INFO_IPV6 4
+#define INFO_TCP 2
+#define INFO_UDP 4
+
+#define TRANSPORT_INFO_NOT_IP 0
+#define TRANSPORT_INFO_IPV4_TCP ((INFO_IPV4 << 16) | INFO_TCP)
+#define TRANSPORT_INFO_IPV4_UDP ((INFO_IPV4 << 16) | INFO_UDP)
+#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 03a2c6e17158..daddea2654ce 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -290,7 +290,7 @@ static int negotiate_nvsp_ver(struct hv_device *device,
NVSP_STAT_SUCCESS)
return -EINVAL;
- if (nvsp_ver != NVSP_PROTOCOL_VERSION_2)
+ if (nvsp_ver == NVSP_PROTOCOL_VERSION_1)
return 0;
/* NVSPv2 only: Send NDIS config */
@@ -314,6 +314,9 @@ static int netvsc_connect_vsp(struct hv_device *device)
struct nvsp_message *init_packet;
int ndis_version;
struct net_device *ndev;
+ u32 ver_list[] = { NVSP_PROTOCOL_VERSION_1, NVSP_PROTOCOL_VERSION_2,
+ NVSP_PROTOCOL_VERSION_4, NVSP_PROTOCOL_VERSION_5 };
+ int i, num_ver = 4; /* number of different NVSP versions */
net_device = get_outbound_net_device(device);
if (!net_device)
@@ -323,13 +326,14 @@ static int netvsc_connect_vsp(struct hv_device *device)
init_packet = &net_device->channel_init_pkt;
/* Negotiate the latest NVSP protocol supported */
- if (negotiate_nvsp_ver(device, net_device, init_packet,
- NVSP_PROTOCOL_VERSION_2) == 0) {
- net_device->nvsp_version = NVSP_PROTOCOL_VERSION_2;
- } else if (negotiate_nvsp_ver(device, net_device, init_packet,
- NVSP_PROTOCOL_VERSION_1) == 0) {
- net_device->nvsp_version = NVSP_PROTOCOL_VERSION_1;
- } else {
+ for (i = num_ver - 1; i >= 0; i--)
+ if (negotiate_nvsp_ver(device, net_device, init_packet,
+ ver_list[i]) == 0) {
+ net_device->nvsp_version = ver_list[i];
+ break;
+ }
+
+ if (i < 0) {
ret = -EPROTO;
goto cleanup;
}
@@ -339,7 +343,10 @@ static int netvsc_connect_vsp(struct hv_device *device)
/* Send the ndis version */
memset(init_packet, 0, sizeof(struct nvsp_message));
- ndis_version = 0x00050001;
+ if (net_device->nvsp_version <= NVSP_PROTOCOL_VERSION_4)
+ ndis_version = 0x00050001;
+ else
+ ndis_version = 0x0006001e;
init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_NDIS_VER;
init_packet->msg.v1_msg.
@@ -358,6 +365,11 @@ static int netvsc_connect_vsp(struct hv_device *device)
goto cleanup;
/* Post the big receive buffer to NetVSP */
+ if (net_device->nvsp_version <= NVSP_PROTOCOL_VERSION_2)
+ net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY;
+ else
+ net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE;
+
ret = netvsc_init_recv_buf(device);
cleanup:
@@ -432,17 +444,14 @@ static inline u32 hv_ringbuf_avail_percent(
return avail_write * 100 / ring_info->ring_datasize;
}
-static void netvsc_send_completion(struct hv_device *device,
+static void netvsc_send_completion(struct netvsc_device *net_device,
+ struct hv_device *device,
struct vmpacket_descriptor *packet)
{
- struct netvsc_device *net_device;
struct nvsp_message *nvsp_packet;
struct hv_netvsc_packet *nvsc_packet;
struct net_device *ndev;
- net_device = get_inbound_net_device(device);
- if (!net_device)
- return;
ndev = net_device->ndev;
nvsp_packet = (struct nvsp_message *)((unsigned long)packet +
@@ -561,13 +570,13 @@ int netvsc_send(struct hv_device *device,
}
static void netvsc_send_recv_completion(struct hv_device *device,
+ struct netvsc_device *net_device,
u64 transaction_id, u32 status)
{
struct nvsp_message recvcompMessage;
int retries = 0;
int ret;
struct net_device *ndev;
- struct netvsc_device *net_device = hv_get_drvdata(device);
ndev = net_device->ndev;
@@ -653,14 +662,15 @@ static void netvsc_receive_completion(void *context)
/* Send a receive completion for the xfer page packet */
if (fsend_receive_comp)
- netvsc_send_recv_completion(device, transaction_id, status);
+ netvsc_send_recv_completion(device, net_device, transaction_id,
+ status);
}
-static void netvsc_receive(struct hv_device *device,
- struct vmpacket_descriptor *packet)
+static void netvsc_receive(struct netvsc_device *net_device,
+ struct hv_device *device,
+ struct vmpacket_descriptor *packet)
{
- struct netvsc_device *net_device;
struct vmtransfer_page_packet_header *vmxferpage_packet;
struct nvsp_message *nvsp_packet;
struct hv_netvsc_packet *netvsc_packet = NULL;
@@ -673,9 +683,6 @@ static void netvsc_receive(struct hv_device *device,
LIST_HEAD(listHead);
- net_device = get_inbound_net_device(device);
- if (!net_device)
- return;
ndev = net_device->ndev;
/*
@@ -741,7 +748,7 @@ static void netvsc_receive(struct hv_device *device,
spin_unlock_irqrestore(&net_device->recv_pkt_list_lock,
flags);
- netvsc_send_recv_completion(device,
+ netvsc_send_recv_completion(device, net_device,
vmxferpage_packet->d.trans_id,
NVSP_STAT_FAIL);
@@ -800,22 +807,16 @@ static void netvsc_channel_cb(void *context)
struct netvsc_device *net_device;
u32 bytes_recvd;
u64 request_id;
- unsigned char *packet;
struct vmpacket_descriptor *desc;
unsigned char *buffer;
int bufferlen = NETVSC_PACKET_SIZE;
struct net_device *ndev;
- packet = kzalloc(NETVSC_PACKET_SIZE * sizeof(unsigned char),
- GFP_ATOMIC);
- if (!packet)
- return;
- buffer = packet;
-
net_device = get_inbound_net_device(device);
if (!net_device)
- goto out;
+ return;
ndev = net_device->ndev;
+ buffer = net_device->cb_buffer;
do {
ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen,
@@ -825,11 +826,13 @@ static void netvsc_channel_cb(void *context)
desc = (struct vmpacket_descriptor *)buffer;
switch (desc->type) {
case VM_PKT_COMP:
- netvsc_send_completion(device, desc);
+ netvsc_send_completion(net_device,
+ device, desc);
break;
case VM_PKT_DATA_USING_XFER_PAGES:
- netvsc_receive(device, desc);
+ netvsc_receive(net_device,
+ device, desc);
break;
default:
@@ -841,23 +844,16 @@ static void netvsc_channel_cb(void *context)
break;
}
- /* reset */
- if (bufferlen > NETVSC_PACKET_SIZE) {
- kfree(buffer);
- buffer = packet;
- bufferlen = NETVSC_PACKET_SIZE;
- }
} else {
- /* reset */
- if (bufferlen > NETVSC_PACKET_SIZE) {
- kfree(buffer);
- buffer = packet;
- bufferlen = NETVSC_PACKET_SIZE;
- }
-
+ /*
+ * We are done for this pass.
+ */
break;
}
+
} else if (ret == -ENOBUFS) {
+ if (bufferlen > NETVSC_PACKET_SIZE)
+ kfree(buffer);
/* Handle large packet */
buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
if (buffer == NULL) {
@@ -872,8 +868,8 @@ static void netvsc_channel_cb(void *context)
}
} while (1);
-out:
- kfree(buffer);
+ if (bufferlen > NETVSC_PACKET_SIZE)
+ kfree(buffer);
return;
}
@@ -907,7 +903,6 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
ndev = net_device->ndev;
/* Initialize the NetVSC channel extension */
- net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE;
spin_lock_init(&net_device->recv_pkt_list_lock);
INIT_LIST_HEAD(&net_device->recv_pkt_list);
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index d6fce9750b95..4e4cf9e0c8d7 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -128,6 +128,27 @@ static int netvsc_close(struct net_device *net)
return ret;
}
+static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size,
+ int pkt_type)
+{
+ struct rndis_packet *rndis_pkt;
+ struct rndis_per_packet_info *ppi;
+
+ rndis_pkt = &msg->msg.pkt;
+ rndis_pkt->data_offset += ppi_size;
+
+ ppi = (struct rndis_per_packet_info *)((void *)rndis_pkt +
+ rndis_pkt->per_pkt_info_offset + rndis_pkt->per_pkt_info_len);
+
+ ppi->size = ppi_size;
+ ppi->type = pkt_type;
+ ppi->ppi_offset = sizeof(struct rndis_per_packet_info);
+
+ rndis_pkt->per_pkt_info_len += ppi_size;
+
+ return ppi;
+}
+
static void netvsc_xmit_completion(void *context)
{
struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context;
@@ -140,22 +161,164 @@ static void netvsc_xmit_completion(void *context)
dev_kfree_skb_any(skb);
}
+static u32 fill_pg_buf(struct page *page, u32 offset, u32 len,
+ struct hv_page_buffer *pb)
+{
+ int j = 0;
+
+ /* Deal with compund pages by ignoring unused part
+ * of the page.
+ */
+ page += (offset >> PAGE_SHIFT);
+ offset &= ~PAGE_MASK;
+
+ while (len > 0) {
+ unsigned long bytes;
+
+ bytes = PAGE_SIZE - offset;
+ if (bytes > len)
+ bytes = len;
+ pb[j].pfn = page_to_pfn(page);
+ pb[j].offset = offset;
+ pb[j].len = bytes;
+
+ offset += bytes;
+ len -= bytes;
+
+ if (offset == PAGE_SIZE && len) {
+ page++;
+ offset = 0;
+ j++;
+ }
+ }
+
+ return j + 1;
+}
+
+static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb,
+ struct hv_page_buffer *pb)
+{
+ u32 slots_used = 0;
+ char *data = skb->data;
+ int frags = skb_shinfo(skb)->nr_frags;
+ int i;
+
+ /* The packet is laid out thus:
+ * 1. hdr
+ * 2. skb linear data
+ * 3. skb fragment data
+ */
+ if (hdr != NULL)
+ slots_used += fill_pg_buf(virt_to_page(hdr),
+ offset_in_page(hdr),
+ len, &pb[slots_used]);
+
+ slots_used += fill_pg_buf(virt_to_page(data),
+ offset_in_page(data),
+ skb_headlen(skb), &pb[slots_used]);
+
+ for (i = 0; i < frags; i++) {
+ skb_frag_t *frag = skb_shinfo(skb)->frags + i;
+
+ slots_used += fill_pg_buf(skb_frag_page(frag),
+ frag->page_offset,
+ skb_frag_size(frag), &pb[slots_used]);
+ }
+ return slots_used;
+}
+
+static int count_skb_frag_slots(struct sk_buff *skb)
+{
+ int i, frags = skb_shinfo(skb)->nr_frags;
+ int pages = 0;
+
+ for (i = 0; i < frags; i++) {
+ skb_frag_t *frag = skb_shinfo(skb)->frags + i;
+ unsigned long size = skb_frag_size(frag);
+ unsigned long offset = frag->page_offset;
+
+ /* Skip unused frames from start of page */
+ offset &= ~PAGE_MASK;
+ pages += PFN_UP(offset + size);
+ }
+ return pages;
+}
+
+static int netvsc_get_slots(struct sk_buff *skb)
+{
+ char *data = skb->data;
+ unsigned int offset = offset_in_page(data);
+ unsigned int len = skb_headlen(skb);
+ int slots;
+ int frag_slots;
+
+ slots = DIV_ROUND_UP(offset + len, PAGE_SIZE);
+ frag_slots = count_skb_frag_slots(skb);
+ return slots + frag_slots;
+}
+
+static u32 get_net_transport_info(struct sk_buff *skb, u32 *trans_off)
+{
+ u32 ret_val = TRANSPORT_INFO_NOT_IP;
+
+ if ((eth_hdr(skb)->h_proto != htons(ETH_P_IP)) &&
+ (eth_hdr(skb)->h_proto != htons(ETH_P_IPV6))) {
+ goto not_ip;
+ }
+
+ *trans_off = skb_transport_offset(skb);
+
+ if ((eth_hdr(skb)->h_proto == htons(ETH_P_IP))) {
+ struct iphdr *iphdr = ip_hdr(skb);
+
+ if (iphdr->protocol == IPPROTO_TCP)
+ ret_val = TRANSPORT_INFO_IPV4_TCP;
+ else if (iphdr->protocol == IPPROTO_UDP)
+ ret_val = TRANSPORT_INFO_IPV4_UDP;
+ } else {
+ if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP)
+ ret_val = TRANSPORT_INFO_IPV6_TCP;
+ else if (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP)
+ ret_val = TRANSPORT_INFO_IPV6_UDP;
+ }
+
+not_ip:
+ return ret_val;
+}
+
static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
{
struct net_device_context *net_device_ctx = netdev_priv(net);
struct hv_netvsc_packet *packet;
int ret;
- unsigned int i, num_pages, npg_data;
-
- /* Add multipages for skb->data and additional 2 for RNDIS */
- npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1)
- >> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1;
- num_pages = skb_shinfo(skb)->nr_frags + npg_data + 2;
+ unsigned int num_data_pgs;
+ struct rndis_message *rndis_msg;
+ struct rndis_packet *rndis_pkt;
+ u32 rndis_msg_size;
+ bool isvlan;
+ struct rndis_per_packet_info *ppi;
+ struct ndis_tcp_ip_checksum_info *csum_info;
+ struct ndis_tcp_lso_info *lso_info;
+ int hdr_offset;
+ u32 net_trans_info;
+
+
+ /* We will atmost need two pages to describe the rndis
+ * header. We can only transmit MAX_PAGE_BUFFER_COUNT number
+ * of pages in a single packet.
+ */
+ num_data_pgs = netvsc_get_slots(skb) + 2;
+ if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) {
+ netdev_err(net, "Packet too big: %u\n", skb->len);
+ dev_kfree_skb(skb);
+ net->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
/* Allocate a netvsc packet based on # of frags. */
packet = kzalloc(sizeof(struct hv_netvsc_packet) +
- (num_pages * sizeof(struct hv_page_buffer)) +
- sizeof(struct rndis_filter_packet) +
+ (num_data_pgs * sizeof(struct hv_page_buffer)) +
+ sizeof(struct rndis_message) +
NDIS_VLAN_PPI_SIZE, GFP_ATOMIC);
if (!packet) {
/* out of memory, drop packet */
@@ -168,53 +331,111 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
packet->vlan_tci = skb->vlan_tci;
- packet->extension = (void *)(unsigned long)packet +
+ 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) +
- (num_pages * sizeof(struct hv_page_buffer));
+ (num_data_pgs * sizeof(struct hv_page_buffer)));
+
+ /* Set the completion routine */
+ packet->completion.send.send_completion = netvsc_xmit_completion;
+ packet->completion.send.send_completion_ctx = packet;
+ packet->completion.send.send_completion_tid = (unsigned long)skb;
- /* If the rndis msg goes beyond 1 page, we will add 1 later */
- packet->page_buf_cnt = num_pages - 1;
+ isvlan = packet->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;
+ rndis_pkt->data_offset = sizeof(struct rndis_packet);
+ rndis_pkt->data_len = packet->total_data_buflen;
+ rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet);
+
+ rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet);
+
+ if (isvlan) {
+ struct ndis_pkt_8021q_info *vlan;
+
+ rndis_msg_size += NDIS_VLAN_PPI_SIZE;
+ ppi = init_ppi_data(rndis_msg, NDIS_VLAN_PPI_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_PRIO_SHIFT;
+ }
- /* Initialize it from the skb */
- packet->total_data_buflen = skb->len;
+ net_trans_info = get_net_transport_info(skb, &hdr_offset);
+ if (net_trans_info == TRANSPORT_INFO_NOT_IP)
+ goto do_send;
+
+ /*
+ * Setup the sendside checksum offload only if this is not a
+ * GSO packet.
+ */
+ if (skb_is_gso(skb))
+ goto do_lso;
+
+ rndis_msg_size += NDIS_CSUM_PPI_SIZE;
+ ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE,
+ TCPIP_CHKSUM_PKTINFO);
+
+ csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi +
+ ppi->ppi_offset);
- /* Start filling in the page buffers starting after RNDIS buffer. */
- packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
- packet->page_buf[1].offset
- = (unsigned long)skb->data & (PAGE_SIZE - 1);
- if (npg_data == 1)
- packet->page_buf[1].len = skb_headlen(skb);
+ if (net_trans_info & (INFO_IPV4 << 16))
+ csum_info->transmit.is_ipv4 = 1;
else
- packet->page_buf[1].len = PAGE_SIZE
- - packet->page_buf[1].offset;
-
- for (i = 2; i <= npg_data; i++) {
- packet->page_buf[i].pfn = virt_to_phys(skb->data
- + PAGE_SIZE * (i-1)) >> PAGE_SHIFT;
- packet->page_buf[i].offset = 0;
- packet->page_buf[i].len = PAGE_SIZE;
+ csum_info->transmit.is_ipv6 = 1;
+
+ if (net_trans_info & INFO_TCP) {
+ csum_info->transmit.tcp_checksum = 1;
+ csum_info->transmit.tcp_header_offset = hdr_offset;
+ } else if (net_trans_info & INFO_UDP) {
+ csum_info->transmit.udp_checksum = 1;
}
- if (npg_data > 1)
- packet->page_buf[npg_data].len = (((unsigned long)skb->data
- + skb_headlen(skb) - 1) & (PAGE_SIZE - 1)) + 1;
-
- /* Additional fragments are after SKB data */
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- const skb_frag_t *f = &skb_shinfo(skb)->frags[i];
-
- packet->page_buf[i+npg_data+1].pfn =
- page_to_pfn(skb_frag_page(f));
- packet->page_buf[i+npg_data+1].offset = f->page_offset;
- packet->page_buf[i+npg_data+1].len = skb_frag_size(f);
+ goto do_send;
+
+do_lso:
+ rndis_msg_size += NDIS_LSO_PPI_SIZE;
+ ppi = init_ppi_data(rndis_msg, NDIS_LSO_PPI_SIZE,
+ TCP_LARGESEND_PKTINFO);
+
+ lso_info = (struct ndis_tcp_lso_info *)((void *)ppi +
+ ppi->ppi_offset);
+
+ lso_info->lso_v2_transmit.type = NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE;
+ if (net_trans_info & (INFO_IPV4 << 16)) {
+ lso_info->lso_v2_transmit.ip_version =
+ NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4;
+ ip_hdr(skb)->tot_len = 0;
+ ip_hdr(skb)->check = 0;
+ tcp_hdr(skb)->check =
+ ~csum_tcpudp_magic(ip_hdr(skb)->saddr,
+ ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0);
+ } else {
+ lso_info->lso_v2_transmit.ip_version =
+ NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6;
+ ipv6_hdr(skb)->payload_len = 0;
+ tcp_hdr(skb)->check =
+ ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0);
}
+ lso_info->lso_v2_transmit.tcp_header_offset = hdr_offset;
+ lso_info->lso_v2_transmit.mss = skb_shinfo(skb)->gso_size;
- /* Set the completion routine */
- packet->completion.send.send_completion = netvsc_xmit_completion;
- packet->completion.send.send_completion_ctx = packet;
- packet->completion.send.send_completion_tid = (unsigned long)skb;
+do_send:
+ /* Start filling in the page buffers with the rndis hdr */
+ rndis_msg->msg_len += rndis_msg_size;
+ packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size,
+ skb, &packet->page_buf[0]);
+
+ ret = netvsc_send(net_device_ctx->device_ctx, packet);
- ret = rndis_filter_send(net_device_ctx->device_ctx,
- packet);
if (ret == 0) {
net->stats.tx_bytes += skb->len;
net->stats.tx_packets++;
@@ -264,7 +485,8 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
* "wire" on the specified device.
*/
int netvsc_recv_callback(struct hv_device *device_obj,
- struct hv_netvsc_packet *packet)
+ struct hv_netvsc_packet *packet,
+ struct ndis_tcp_ip_checksum_info *csum_info)
{
struct net_device *net;
struct sk_buff *skb;
@@ -291,7 +513,17 @@ int netvsc_recv_callback(struct hv_device *device_obj,
packet->total_data_buflen);
skb->protocol = eth_type_trans(skb, net);
- skb->ip_summed = CHECKSUM_NONE;
+ if (csum_info) {
+ /* We only look at the IP checksum here.
+ * Should we be dropping the packet if checksum
+ * failed? How do we deal with other checksums - TCP/UDP?
+ */
+ if (csum_info->receive.ip_checksum_succeeded)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+
if (packet->vlan_tci & VLAN_TAG_PRESENT)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
packet->vlan_tci);
@@ -327,7 +559,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
if (nvdev == NULL || nvdev->destroy)
return -ENODEV;
- if (nvdev->nvsp_version == NVSP_PROTOCOL_VERSION_2)
+ if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2)
limit = NETVSC_MTU;
if (mtu < 68 || mtu > limit)
@@ -452,9 +684,10 @@ static int netvsc_probe(struct hv_device *dev,
net->netdev_ops = &device_ops;
- /* TODO: Add GSO and Checksum offload */
- net->hw_features = 0;
- net->features = NETIF_F_HW_VLAN_CTAG_TX;
+ net->hw_features = NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_IP_CSUM |
+ NETIF_F_TSO;
+ net->features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG | NETIF_F_RXCSUM |
+ NETIF_F_IP_CSUM | NETIF_F_TSO;
SET_ETHTOOL_OPS(net, &ethtool_ops);
SET_NETDEV_DEV(net, &dev->device);
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index b54fd257652b..4a37e3db9e32 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -58,9 +58,6 @@ struct rndis_request {
u8 request_ext[RNDIS_EXT_LEN];
};
-static void rndis_filter_send_completion(void *ctx);
-
-
static struct rndis_device *get_rndis_device(void)
{
struct rndis_device *device;
@@ -297,7 +294,7 @@ static void rndis_filter_receive_response(struct rndis_device *dev,
"rndis response buffer overflow "
"detected (size %u max %zu)\n",
resp->msg_len,
- sizeof(struct rndis_filter_packet));
+ sizeof(struct rndis_message));
if (resp->ndis_msg_type ==
RNDIS_MSG_RESET_C) {
@@ -373,6 +370,7 @@ static void rndis_filter_receive_data(struct rndis_device *dev,
struct rndis_packet *rndis_pkt;
u32 data_offset;
struct ndis_pkt_8021q_info *vlan;
+ struct ndis_tcp_ip_checksum_info *csum_info;
rndis_pkt = &msg->msg.pkt;
@@ -411,7 +409,8 @@ static void rndis_filter_receive_data(struct rndis_device *dev,
pkt->vlan_tci = 0;
}
- netvsc_recv_callback(dev->net_dev->dev, pkt);
+ csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
+ netvsc_recv_callback(dev->net_dev->dev, pkt, csum_info);
}
int rndis_filter_receive(struct hv_device *dev,
@@ -630,6 +629,61 @@ cleanup:
return ret;
}
+int rndis_filter_set_offload_params(struct hv_device *hdev,
+ struct ndis_offload_params *req_offloads)
+{
+ struct netvsc_device *nvdev = hv_get_drvdata(hdev);
+ struct rndis_device *rdev = nvdev->extension;
+ struct net_device *ndev = nvdev->ndev;
+ struct rndis_request *request;
+ struct rndis_set_request *set;
+ struct ndis_offload_params *offload_params;
+ struct rndis_set_complete *set_complete;
+ u32 extlen = sizeof(struct ndis_offload_params);
+ int ret, t;
+
+ request = get_rndis_request(rdev, RNDIS_MSG_SET,
+ RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
+ if (!request)
+ return -ENOMEM;
+
+ set = &request->request_msg.msg.set_req;
+ set->oid = OID_TCP_OFFLOAD_PARAMETERS;
+ set->info_buflen = extlen;
+ set->info_buf_offset = sizeof(struct rndis_set_request);
+ set->dev_vc_handle = 0;
+
+ offload_params = (struct ndis_offload_params *)((ulong)set +
+ set->info_buf_offset);
+ *offload_params = *req_offloads;
+ offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
+ offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
+ offload_params->header.size = extlen;
+
+ ret = rndis_filter_send_request(rdev, request);
+ if (ret != 0)
+ goto cleanup;
+
+ t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
+ if (t == 0) {
+ netdev_err(ndev, "timeout before we got aOFFLOAD set response...\n");
+ /* can't put_rndis_request, since we may still receive a
+ * send-completion.
+ */
+ return -EBUSY;
+ } else {
+ set_complete = &request->response_msg.msg.set_complete;
+ if (set_complete->status != RNDIS_STATUS_SUCCESS) {
+ netdev_err(ndev, "Fail to set MAC on host side:0x%x\n",
+ set_complete->status);
+ ret = -EINVAL;
+ }
+ }
+
+cleanup:
+ put_rndis_request(rdev, request);
+ return ret;
+}
static int rndis_filter_query_device_link_status(struct rndis_device *dev)
{
@@ -829,6 +883,7 @@ int rndis_filter_device_add(struct hv_device *dev,
struct netvsc_device *net_device;
struct rndis_device *rndis_device;
struct netvsc_device_info *device_info = additional_info;
+ struct ndis_offload_params offloads;
rndis_device = get_rndis_device();
if (!rndis_device)
@@ -868,6 +923,26 @@ int rndis_filter_device_add(struct hv_device *dev,
memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
+ /* Turn on the offloads; the host supports all of the relevant
+ * offloads.
+ */
+ memset(&offloads, 0, sizeof(struct ndis_offload_params));
+ /* A value of zero means "no change"; now turn on what we
+ * want.
+ */
+ offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
+ offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
+ offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
+ offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
+ offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
+ offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
+
+
+ ret = rndis_filter_set_offload_params(dev, &offloads);
+ if (ret)
+ goto err_dev_remv;
+
+
rndis_filter_query_device_link_status(rndis_device);
device_info->link_state = rndis_device->link_state;
@@ -877,6 +952,10 @@ int rndis_filter_device_add(struct hv_device *dev,
device_info->link_state ? "down" : "up");
return ret;
+
+err_dev_remv:
+ rndis_filter_device_remove(dev);
+ return ret;
}
void rndis_filter_device_remove(struct hv_device *dev)
@@ -913,101 +992,3 @@ int rndis_filter_close(struct hv_device *dev)
return rndis_filter_close_device(nvdev->extension);
}
-
-int rndis_filter_send(struct hv_device *dev,
- struct hv_netvsc_packet *pkt)
-{
- int ret;
- struct rndis_filter_packet *filter_pkt;
- struct rndis_message *rndis_msg;
- struct rndis_packet *rndis_pkt;
- u32 rndis_msg_size;
- bool isvlan = pkt->vlan_tci & VLAN_TAG_PRESENT;
-
- /* Add the rndis header */
- filter_pkt = (struct rndis_filter_packet *)pkt->extension;
-
- rndis_msg = &filter_pkt->msg;
- rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet);
- if (isvlan)
- rndis_msg_size += NDIS_VLAN_PPI_SIZE;
-
- rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET;
- rndis_msg->msg_len = pkt->total_data_buflen +
- rndis_msg_size;
-
- rndis_pkt = &rndis_msg->msg.pkt;
- rndis_pkt->data_offset = sizeof(struct rndis_packet);
- if (isvlan)
- rndis_pkt->data_offset += NDIS_VLAN_PPI_SIZE;
- rndis_pkt->data_len = pkt->total_data_buflen;
-
- if (isvlan) {
- struct rndis_per_packet_info *ppi;
- struct ndis_pkt_8021q_info *vlan;
-
- rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet);
- rndis_pkt->per_pkt_info_len = NDIS_VLAN_PPI_SIZE;
-
- ppi = (struct rndis_per_packet_info *)((ulong)rndis_pkt +
- rndis_pkt->per_pkt_info_offset);
- ppi->size = NDIS_VLAN_PPI_SIZE;
- ppi->type = IEEE_8021Q_INFO;
- ppi->ppi_offset = sizeof(struct rndis_per_packet_info);
-
- vlan = (struct ndis_pkt_8021q_info *)((ulong)ppi +
- ppi->ppi_offset);
- vlan->vlanid = pkt->vlan_tci & VLAN_VID_MASK;
- vlan->pri = (pkt->vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
- }
-
- pkt->is_data_pkt = true;
- pkt->page_buf[0].pfn = virt_to_phys(rndis_msg) >> PAGE_SHIFT;
- pkt->page_buf[0].offset =
- (unsigned long)rndis_msg & (PAGE_SIZE-1);
- pkt->page_buf[0].len = rndis_msg_size;
-
- /* Add one page_buf if the rndis msg goes beyond page boundary */
- if (pkt->page_buf[0].offset + rndis_msg_size > PAGE_SIZE) {
- int i;
- for (i = pkt->page_buf_cnt; i > 1; i--)
- pkt->page_buf[i] = pkt->page_buf[i-1];
- pkt->page_buf_cnt++;
- pkt->page_buf[0].len = PAGE_SIZE - pkt->page_buf[0].offset;
- pkt->page_buf[1].pfn = virt_to_phys((void *)((ulong)
- rndis_msg + pkt->page_buf[0].len)) >> PAGE_SHIFT;
- pkt->page_buf[1].offset = 0;
- pkt->page_buf[1].len = rndis_msg_size - pkt->page_buf[0].len;
- }
-
- /* Save the packet send completion and context */
- filter_pkt->completion = pkt->completion.send.send_completion;
- filter_pkt->completion_ctx =
- pkt->completion.send.send_completion_ctx;
-
- /* Use ours */
- pkt->completion.send.send_completion = rndis_filter_send_completion;
- pkt->completion.send.send_completion_ctx = filter_pkt;
-
- ret = netvsc_send(dev, pkt);
- if (ret != 0) {
- /*
- * Reset the completion to originals to allow retries from
- * above
- */
- pkt->completion.send.send_completion =
- filter_pkt->completion;
- pkt->completion.send.send_completion_ctx =
- filter_pkt->completion_ctx;
- }
-
- return ret;
-}
-
-static void rndis_filter_send_completion(void *ctx)
-{
- struct rndis_filter_packet *filter_pkt = ctx;
-
- /* Pass it back to the original handler */
- filter_pkt->completion(filter_pkt->completion_ctx);
-}
diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig
index 08ae4655423a..3e89beab64fd 100644
--- a/drivers/net/ieee802154/Kconfig
+++ b/drivers/net/ieee802154/Kconfig
@@ -15,9 +15,9 @@ config IEEE802154_FAKEHARD
depends on IEEE802154_DRIVERS
---help---
Say Y here to enable the fake driver that serves as an example
- of HardMAC device driver.
+ of HardMAC device driver.
- This driver can also be built as a module. To do so say M here.
+ This driver can also be built as a module. To do so say M here.
The module will be called 'fakehard'.
config IEEE802154_FAKELB
@@ -31,17 +31,23 @@ config IEEE802154_FAKELB
The module will be called 'fakelb'.
config IEEE802154_AT86RF230
- depends on IEEE802154_DRIVERS && MAC802154
- tristate "AT86RF230/231 transceiver driver"
- depends on SPI
+ depends on IEEE802154_DRIVERS && MAC802154
+ tristate "AT86RF230/231/233/212 transceiver driver"
+ depends on SPI
+ ---help---
+ Say Y here to enable the at86rf230/231/233/212 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 'at86rf230'.
config IEEE802154_MRF24J40
- tristate "Microchip MRF24J40 transceiver driver"
- depends on IEEE802154_DRIVERS && MAC802154
- depends on SPI
- ---help---
- Say Y here to enable the MRF24J20 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 'mrf24j40'.
+ tristate "Microchip MRF24J40 transceiver driver"
+ depends on IEEE802154_DRIVERS && MAC802154
+ depends on SPI
+ ---help---
+ Say Y here to enable the MRF24J20 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 'mrf24j40'.
diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c
index a30258aad139..89417ac41083 100644
--- a/drivers/net/ieee802154/at86rf230.c
+++ b/drivers/net/ieee802154/at86rf230.c
@@ -31,13 +31,13 @@
#include <linux/spi/spi.h>
#include <linux/spi/at86rf230.h>
#include <linux/skbuff.h>
+#include <linux/of_gpio.h>
#include <net/mac802154.h>
#include <net/wpan-phy.h>
struct at86rf230_local {
struct spi_device *spi;
- int rstn, slp_tr, dig2;
u8 part;
u8 vers;
@@ -53,8 +53,16 @@ struct at86rf230_local {
spinlock_t lock;
bool irq_busy;
bool is_tx;
+ bool tx_aret;
+
+ int rssi_base_val;
};
+static bool is_rf212(struct at86rf230_local *local)
+{
+ return local->part == 7;
+}
+
#define RG_TRX_STATUS (0x01)
#define SR_TRX_STATUS 0x01, 0x1f, 0
#define SR_RESERVED_01_3 0x01, 0x20, 5
@@ -100,7 +108,10 @@ struct at86rf230_local {
#define SR_SFD_VALUE 0x0b, 0xff, 0
#define RG_TRX_CTRL_2 (0x0c)
#define SR_OQPSK_DATA_RATE 0x0c, 0x03, 0
-#define SR_RESERVED_0c_2 0x0c, 0x7c, 2
+#define SR_SUB_MODE 0x0c, 0x04, 2
+#define SR_BPSK_QPSK 0x0c, 0x08, 3
+#define SR_OQPSK_SUB1_RC_EN 0x0c, 0x10, 4
+#define SR_RESERVED_0c_5 0x0c, 0x60, 5
#define SR_RX_SAFE_MODE 0x0c, 0x80, 7
#define RG_ANT_DIV (0x0d)
#define SR_ANT_CTRL 0x0d, 0x03, 0
@@ -145,7 +156,7 @@ struct at86rf230_local {
#define SR_RESERVED_17_5 0x17, 0x08, 3
#define SR_AACK_UPLD_RES_FT 0x17, 0x10, 4
#define SR_AACK_FLTR_RES_FT 0x17, 0x20, 5
-#define SR_RESERVED_17_2 0x17, 0x40, 6
+#define SR_CSMA_LBT_MODE 0x17, 0x40, 6
#define SR_RESERVED_17_1 0x17, 0x80, 7
#define RG_FTN_CTRL (0x18)
#define SR_RESERVED_18_2 0x18, 0x7f, 0
@@ -234,6 +245,7 @@ struct at86rf230_local {
#define STATE_TX_ON 0x09
/* 0x0a - 0x0e */ /* 0x0a - UNSUPPORTED_ATTRIBUTE */
#define STATE_SLEEP 0x0F
+#define STATE_PREP_DEEP_SLEEP 0x10
#define STATE_BUSY_RX_AACK 0x11
#define STATE_BUSY_TX_ARET 0x12
#define STATE_RX_AACK_ON 0x16
@@ -244,6 +256,57 @@ struct at86rf230_local {
#define STATE_TRANSITION_IN_PROGRESS 0x1F
static int
+__at86rf230_detect_device(struct spi_device *spi, u16 *man_id, u8 *part,
+ u8 *version)
+{
+ u8 data[4];
+ u8 *buf = kmalloc(2, GFP_KERNEL);
+ int status;
+ struct spi_message msg;
+ struct spi_transfer xfer = {
+ .len = 2,
+ .tx_buf = buf,
+ .rx_buf = buf,
+ };
+ u8 reg;
+
+ if (!buf)
+ return -ENOMEM;
+
+ for (reg = RG_PART_NUM; reg <= RG_MAN_ID_1; reg++) {
+ buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
+ buf[1] = 0xff;
+ dev_vdbg(&spi->dev, "buf[0] = %02x\n", buf[0]);
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ status = spi_sync(spi, &msg);
+ dev_vdbg(&spi->dev, "status = %d\n", status);
+ if (msg.status)
+ status = msg.status;
+
+ dev_vdbg(&spi->dev, "status = %d\n", status);
+ dev_vdbg(&spi->dev, "buf[0] = %02x\n", buf[0]);
+ dev_vdbg(&spi->dev, "buf[1] = %02x\n", buf[1]);
+
+ if (status == 0)
+ data[reg - RG_PART_NUM] = buf[1];
+ else
+ break;
+ }
+
+ if (status == 0) {
+ *part = data[0];
+ *version = data[1];
+ *man_id = (data[3] << 8) | data[2];
+ }
+
+ kfree(buf);
+
+ return status;
+}
+
+static int
__at86rf230_write(struct at86rf230_local *lp, u8 addr, u8 data)
{
u8 *buf = lp->buf;
@@ -489,7 +552,9 @@ at86rf230_state(struct ieee802154_dev *dev, int state)
} while (val == STATE_TRANSITION_IN_PROGRESS);
- if (val == desired_status)
+ if (val == desired_status ||
+ (desired_status == STATE_RX_ON && val == STATE_BUSY_RX) ||
+ (desired_status == STATE_RX_AACK_ON && val == STATE_BUSY_RX_AACK))
return 0;
pr_err("unexpected state change: %d, asked for %d\n", val, state);
@@ -510,7 +575,11 @@ at86rf230_start(struct ieee802154_dev *dev)
if (rc)
return rc;
- return at86rf230_state(dev, STATE_RX_ON);
+ rc = at86rf230_state(dev, STATE_TX_ON);
+ if (rc)
+ return rc;
+
+ return at86rf230_state(dev, STATE_RX_AACK_ON);
}
static void
@@ -520,6 +589,39 @@ at86rf230_stop(struct ieee802154_dev *dev)
}
static int
+at86rf230_set_channel(struct at86rf230_local *lp, int page, int channel)
+{
+ lp->rssi_base_val = -91;
+
+ return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
+}
+
+static int
+at86rf212_set_channel(struct at86rf230_local *lp, int page, int channel)
+{
+ int rc;
+
+ if (channel == 0)
+ rc = at86rf230_write_subreg(lp, SR_SUB_MODE, 0);
+ else
+ rc = at86rf230_write_subreg(lp, SR_SUB_MODE, 1);
+ if (rc < 0)
+ return rc;
+
+ if (page == 0) {
+ rc = at86rf230_write_subreg(lp, SR_BPSK_QPSK, 0);
+ lp->rssi_base_val = -100;
+ } else {
+ rc = at86rf230_write_subreg(lp, SR_BPSK_QPSK, 1);
+ lp->rssi_base_val = -98;
+ }
+ if (rc < 0)
+ return rc;
+
+ return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
+}
+
+static int
at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
{
struct at86rf230_local *lp = dev->priv;
@@ -527,14 +629,22 @@ at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
might_sleep();
- if (page != 0 || channel < 11 || channel > 26) {
+ if (page < 0 || page > 31 ||
+ !(lp->dev->phy->channels_supported[page] & BIT(channel))) {
WARN_ON(1);
return -EINVAL;
}
- rc = at86rf230_write_subreg(lp, SR_CHANNEL, channel);
+ if (is_rf212(lp))
+ rc = at86rf212_set_channel(lp, page, channel);
+ else
+ rc = at86rf230_set_channel(lp, page, channel);
+ if (rc < 0)
+ return rc;
+
msleep(1); /* Wait for PLL */
dev->phy->current_channel = channel;
+ dev->phy->current_page = page;
return 0;
}
@@ -568,6 +678,12 @@ at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
if (rc)
goto err_rx;
+ if (lp->tx_aret) {
+ rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TX_ARET_ON);
+ if (rc)
+ goto err_rx;
+ }
+
rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_BUSY_TX);
if (rc)
goto err_rx;
@@ -630,30 +746,31 @@ at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev,
struct at86rf230_local *lp = dev->priv;
if (changed & IEEE802515_AFILT_SADDR_CHANGED) {
+ u16 addr = le16_to_cpu(filt->short_addr);
+
dev_vdbg(&lp->spi->dev,
"at86rf230_set_hw_addr_filt called for saddr\n");
- __at86rf230_write(lp, RG_SHORT_ADDR_0, filt->short_addr);
- __at86rf230_write(lp, RG_SHORT_ADDR_1, filt->short_addr >> 8);
+ __at86rf230_write(lp, RG_SHORT_ADDR_0, addr);
+ __at86rf230_write(lp, RG_SHORT_ADDR_1, addr >> 8);
}
if (changed & IEEE802515_AFILT_PANID_CHANGED) {
+ u16 pan = le16_to_cpu(filt->pan_id);
+
dev_vdbg(&lp->spi->dev,
"at86rf230_set_hw_addr_filt called for pan id\n");
- __at86rf230_write(lp, RG_PAN_ID_0, filt->pan_id);
- __at86rf230_write(lp, RG_PAN_ID_1, filt->pan_id >> 8);
+ __at86rf230_write(lp, RG_PAN_ID_0, pan);
+ __at86rf230_write(lp, RG_PAN_ID_1, pan >> 8);
}
if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) {
+ u8 i, addr[8];
+
+ memcpy(addr, &filt->ieee_addr, 8);
dev_vdbg(&lp->spi->dev,
"at86rf230_set_hw_addr_filt called for IEEE addr\n");
- at86rf230_write_subreg(lp, SR_IEEE_ADDR_0, filt->ieee_addr[7]);
- at86rf230_write_subreg(lp, SR_IEEE_ADDR_1, filt->ieee_addr[6]);
- at86rf230_write_subreg(lp, SR_IEEE_ADDR_2, filt->ieee_addr[5]);
- at86rf230_write_subreg(lp, SR_IEEE_ADDR_3, filt->ieee_addr[4]);
- at86rf230_write_subreg(lp, SR_IEEE_ADDR_4, filt->ieee_addr[3]);
- at86rf230_write_subreg(lp, SR_IEEE_ADDR_5, filt->ieee_addr[2]);
- at86rf230_write_subreg(lp, SR_IEEE_ADDR_6, filt->ieee_addr[1]);
- at86rf230_write_subreg(lp, SR_IEEE_ADDR_7, filt->ieee_addr[0]);
+ for (i = 0; i < 8; i++)
+ __at86rf230_write(lp, RG_IEEE_ADDR_0 + i, addr[i]);
}
if (changed & IEEE802515_AFILT_PANC_CHANGED) {
@@ -668,6 +785,93 @@ at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev,
return 0;
}
+static int
+at86rf212_set_txpower(struct ieee802154_dev *dev, int db)
+{
+ struct at86rf230_local *lp = dev->priv;
+
+ /* typical maximum output is 5dBm with RG_PHY_TX_PWR 0x60, lower five
+ * bits decrease power in 1dB steps. 0x60 represents extra PA gain of
+ * 0dB.
+ * thus, supported values for db range from -26 to 5, for 31dB of
+ * reduction to 0dB of reduction.
+ */
+ if (db > 5 || db < -26)
+ return -EINVAL;
+
+ db = -(db - 5);
+
+ return __at86rf230_write(lp, RG_PHY_TX_PWR, 0x60 | db);
+}
+
+static int
+at86rf212_set_lbt(struct ieee802154_dev *dev, bool on)
+{
+ struct at86rf230_local *lp = dev->priv;
+
+ return at86rf230_write_subreg(lp, SR_CSMA_LBT_MODE, on);
+}
+
+static int
+at86rf212_set_cca_mode(struct ieee802154_dev *dev, u8 mode)
+{
+ struct at86rf230_local *lp = dev->priv;
+
+ return at86rf230_write_subreg(lp, SR_CCA_MODE, mode);
+}
+
+static int
+at86rf212_set_cca_ed_level(struct ieee802154_dev *dev, s32 level)
+{
+ struct at86rf230_local *lp = dev->priv;
+ int desens_steps;
+
+ if (level < lp->rssi_base_val || level > 30)
+ return -EINVAL;
+
+ desens_steps = (level - lp->rssi_base_val) * 100 / 207;
+
+ return at86rf230_write_subreg(lp, SR_CCA_ED_THRES, desens_steps);
+}
+
+static int
+at86rf212_set_csma_params(struct ieee802154_dev *dev, u8 min_be, u8 max_be,
+ u8 retries)
+{
+ struct at86rf230_local *lp = dev->priv;
+ int rc;
+
+ if (min_be > max_be || max_be > 8 || retries > 5)
+ return -EINVAL;
+
+ rc = at86rf230_write_subreg(lp, SR_MIN_BE, min_be);
+ if (rc)
+ return rc;
+
+ rc = at86rf230_write_subreg(lp, SR_MAX_BE, max_be);
+ if (rc)
+ return rc;
+
+ return at86rf230_write_subreg(lp, SR_MAX_CSMA_RETRIES, max_be);
+}
+
+static int
+at86rf212_set_frame_retries(struct ieee802154_dev *dev, s8 retries)
+{
+ struct at86rf230_local *lp = dev->priv;
+ int rc = 0;
+
+ if (retries < -1 || retries > 15)
+ return -EINVAL;
+
+ lp->tx_aret = retries >= 0;
+
+ if (retries >= 0)
+ rc = at86rf230_write_subreg(lp, SR_MAX_FRAME_RETRIES, retries);
+
+ return rc;
+}
+
static struct ieee802154_ops at86rf230_ops = {
.owner = THIS_MODULE,
.xmit = at86rf230_xmit,
@@ -678,6 +882,22 @@ static struct ieee802154_ops at86rf230_ops = {
.set_hw_addr_filt = at86rf230_set_hw_addr_filt,
};
+static struct ieee802154_ops at86rf212_ops = {
+ .owner = THIS_MODULE,
+ .xmit = at86rf230_xmit,
+ .ed = at86rf230_ed,
+ .set_channel = at86rf230_channel,
+ .start = at86rf230_start,
+ .stop = at86rf230_stop,
+ .set_hw_addr_filt = at86rf230_set_hw_addr_filt,
+ .set_txpower = at86rf212_set_txpower,
+ .set_lbt = at86rf212_set_lbt,
+ .set_cca_mode = at86rf212_set_cca_mode,
+ .set_cca_ed_level = at86rf212_set_cca_ed_level,
+ .set_csma_params = at86rf212_set_csma_params,
+ .set_frame_retries = at86rf212_set_frame_retries,
+};
+
static void at86rf230_irqwork(struct work_struct *work)
{
struct at86rf230_local *lp =
@@ -695,8 +915,8 @@ static void at86rf230_irqwork(struct work_struct *work)
status &= ~IRQ_TRX_UR; /* FIXME: possibly handle ???*/
if (status & IRQ_TRX_END) {
- spin_lock_irqsave(&lp->lock, flags);
status &= ~IRQ_TRX_END;
+ spin_lock_irqsave(&lp->lock, flags);
if (lp->is_tx) {
lp->is_tx = 0;
spin_unlock_irqrestore(&lp->lock, flags);
@@ -753,22 +973,15 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
struct at86rf230_platform_data *pdata = lp->spi->dev.platform_data;
int rc, irq_pol;
u8 status;
+ u8 csma_seed[2];
rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
if (rc)
return rc;
- dev_info(&lp->spi->dev, "Status: %02x\n", status);
- if (status == STATE_P_ON) {
- rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TRX_OFF);
- if (rc)
- return rc;
- msleep(1);
- rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
- if (rc)
- return rc;
- dev_info(&lp->spi->dev, "Status: %02x\n", status);
- }
+ rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_FORCE_TRX_OFF);
+ if (rc)
+ return rc;
/* configure irq polarity, defaults to high active */
if (pdata->irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
@@ -784,6 +997,14 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
if (rc)
return rc;
+ get_random_bytes(csma_seed, ARRAY_SIZE(csma_seed));
+ rc = at86rf230_write_subreg(lp, SR_CSMA_SEED_0, csma_seed[0]);
+ if (rc)
+ return rc;
+ rc = at86rf230_write_subreg(lp, SR_CSMA_SEED_1, csma_seed[1]);
+ if (rc)
+ return rc;
+
/* CLKM changes are applied immediately */
rc = at86rf230_write_subreg(lp, SR_CLKM_SHA_SEL, 0x00);
if (rc)
@@ -796,16 +1017,6 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
/* Wait the next SLEEP cycle */
msleep(100);
- rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TX_ON);
- if (rc)
- return rc;
- msleep(1);
-
- rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
- if (rc)
- return rc;
- dev_info(&lp->spi->dev, "Status: %02x\n", status);
-
rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &status);
if (rc)
return rc;
@@ -825,14 +1036,38 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
return 0;
}
-static void at86rf230_fill_data(struct spi_device *spi)
+static struct at86rf230_platform_data *
+at86rf230_get_pdata(struct spi_device *spi)
{
- struct at86rf230_local *lp = spi_get_drvdata(spi);
- struct at86rf230_platform_data *pdata = spi->dev.platform_data;
+ struct at86rf230_platform_data *pdata;
+ const char *irq_type;
+
+ if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node)
+ return spi->dev.platform_data;
+
+ pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ goto done;
+
+ pdata->rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0);
+ pdata->slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0);
+
+ pdata->irq_type = IRQF_TRIGGER_RISING;
+ of_property_read_string(spi->dev.of_node, "irq-type", &irq_type);
+ if (!strcmp(irq_type, "level-high"))
+ pdata->irq_type = IRQF_TRIGGER_HIGH;
+ else if (!strcmp(irq_type, "level-low"))
+ pdata->irq_type = IRQF_TRIGGER_LOW;
+ else if (!strcmp(irq_type, "edge-rising"))
+ pdata->irq_type = IRQF_TRIGGER_RISING;
+ else if (!strcmp(irq_type, "edge-falling"))
+ pdata->irq_type = IRQF_TRIGGER_FALLING;
+ else
+ dev_warn(&spi->dev, "wrong irq-type specified using edge-rising\n");
- lp->rstn = pdata->rstn;
- lp->slp_tr = pdata->slp_tr;
- lp->dig2 = pdata->dig2;
+ spi->dev.platform_data = pdata;
+done:
+ return pdata;
}
static int at86rf230_probe(struct spi_device *spi)
@@ -840,133 +1075,146 @@ static int at86rf230_probe(struct spi_device *spi)
struct at86rf230_platform_data *pdata;
struct ieee802154_dev *dev;
struct at86rf230_local *lp;
- u8 man_id_0, man_id_1, status;
+ u16 man_id = 0;
+ u8 part = 0, version = 0, status;
irq_handler_t irq_handler;
work_func_t irq_worker;
- int rc, supported = 0;
+ int rc;
const char *chip;
+ struct ieee802154_ops *ops = NULL;
if (!spi->irq) {
dev_err(&spi->dev, "no IRQ specified\n");
return -EINVAL;
}
- pdata = spi->dev.platform_data;
+ pdata = at86rf230_get_pdata(spi);
if (!pdata) {
dev_err(&spi->dev, "no platform_data\n");
return -EINVAL;
}
- dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops);
- if (!dev)
- return -ENOMEM;
-
- lp = dev->priv;
- lp->dev = dev;
-
- lp->spi = spi;
-
- dev->parent = &spi->dev;
- dev->extra_tx_headroom = 0;
- /* We do support only 2.4 Ghz */
- dev->phy->channels_supported[0] = 0x7FFF800;
- dev->flags = IEEE802154_HW_OMIT_CKSUM;
-
- if (pdata->irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
- irq_worker = at86rf230_irqwork;
- irq_handler = at86rf230_isr;
- } else {
- irq_worker = at86rf230_irqwork_level;
- irq_handler = at86rf230_isr_level;
+ if (gpio_is_valid(pdata->rstn)) {
+ rc = gpio_request(pdata->rstn, "rstn");
+ if (rc)
+ return rc;
}
- mutex_init(&lp->bmux);
- INIT_WORK(&lp->irqwork, irq_worker);
- spin_lock_init(&lp->lock);
- init_completion(&lp->tx_complete);
-
- spi_set_drvdata(spi, lp);
-
- at86rf230_fill_data(spi);
-
- rc = gpio_request(lp->rstn, "rstn");
- if (rc)
- goto err_rstn;
-
- if (gpio_is_valid(lp->slp_tr)) {
- rc = gpio_request(lp->slp_tr, "slp_tr");
+ if (gpio_is_valid(pdata->slp_tr)) {
+ rc = gpio_request(pdata->slp_tr, "slp_tr");
if (rc)
goto err_slp_tr;
}
- rc = gpio_direction_output(lp->rstn, 1);
- if (rc)
- goto err_gpio_dir;
+ if (gpio_is_valid(pdata->rstn)) {
+ rc = gpio_direction_output(pdata->rstn, 1);
+ if (rc)
+ goto err_gpio_dir;
+ }
- if (gpio_is_valid(lp->slp_tr)) {
- rc = gpio_direction_output(lp->slp_tr, 0);
+ if (gpio_is_valid(pdata->slp_tr)) {
+ rc = gpio_direction_output(pdata->slp_tr, 0);
if (rc)
goto err_gpio_dir;
}
/* Reset */
- msleep(1);
- gpio_set_value(lp->rstn, 0);
- msleep(1);
- gpio_set_value(lp->rstn, 1);
- msleep(1);
+ if (gpio_is_valid(pdata->rstn)) {
+ udelay(1);
+ gpio_set_value(pdata->rstn, 0);
+ udelay(1);
+ gpio_set_value(pdata->rstn, 1);
+ usleep_range(120, 240);
+ }
- rc = at86rf230_read_subreg(lp, SR_MAN_ID_0, &man_id_0);
- if (rc)
- goto err_gpio_dir;
- rc = at86rf230_read_subreg(lp, SR_MAN_ID_1, &man_id_1);
- if (rc)
+ rc = __at86rf230_detect_device(spi, &man_id, &part, &version);
+ if (rc < 0)
goto err_gpio_dir;
- if (man_id_1 != 0x00 || man_id_0 != 0x1f) {
+ if (man_id != 0x001f) {
dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n",
- man_id_1, man_id_0);
+ man_id >> 8, man_id & 0xFF);
rc = -EINVAL;
goto err_gpio_dir;
}
- rc = at86rf230_read_subreg(lp, SR_PART_NUM, &lp->part);
- if (rc)
- goto err_gpio_dir;
-
- rc = at86rf230_read_subreg(lp, SR_VERSION_NUM, &lp->vers);
- if (rc)
- goto err_gpio_dir;
-
- switch (lp->part) {
+ switch (part) {
case 2:
chip = "at86rf230";
- /* supported = 1; FIXME: should be easy to support; */
+ /* FIXME: should be easy to support; */
break;
case 3:
chip = "at86rf231";
- supported = 1;
+ ops = &at86rf230_ops;
+ break;
+ case 7:
+ chip = "at86rf212";
+ if (version == 1)
+ ops = &at86rf212_ops;
+ break;
+ case 11:
+ chip = "at86rf233";
+ ops = &at86rf230_ops;
break;
default:
chip = "UNKNOWN";
break;
}
- dev_info(&spi->dev, "Detected %s chip version %d\n", chip, lp->vers);
- if (!supported) {
+ dev_info(&spi->dev, "Detected %s chip version %d\n", chip, version);
+ if (!ops) {
rc = -ENOTSUPP;
goto err_gpio_dir;
}
+ dev = ieee802154_alloc_device(sizeof(*lp), ops);
+ if (!dev) {
+ rc = -ENOMEM;
+ goto err_gpio_dir;
+ }
+
+ lp = dev->priv;
+ lp->dev = dev;
+ lp->part = part;
+ lp->vers = version;
+
+ lp->spi = spi;
+
+ dev->parent = &spi->dev;
+ dev->extra_tx_headroom = 0;
+ dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK;
+
+ if (pdata->irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+ irq_worker = at86rf230_irqwork;
+ irq_handler = at86rf230_isr;
+ } else {
+ irq_worker = at86rf230_irqwork_level;
+ irq_handler = at86rf230_isr_level;
+ }
+
+ mutex_init(&lp->bmux);
+ INIT_WORK(&lp->irqwork, irq_worker);
+ spin_lock_init(&lp->lock);
+ init_completion(&lp->tx_complete);
+
+ spi_set_drvdata(spi, lp);
+
+ if (is_rf212(lp)) {
+ dev->phy->channels_supported[0] = 0x00007FF;
+ dev->phy->channels_supported[2] = 0x00007FF;
+ } else {
+ dev->phy->channels_supported[0] = 0x7FFF800;
+ }
+
rc = at86rf230_hw_init(lp);
if (rc)
- goto err_gpio_dir;
+ goto err_hw_init;
rc = request_irq(spi->irq, irq_handler,
IRQF_SHARED | pdata->irq_type,
dev_name(&spi->dev), lp);
if (rc)
- goto err_gpio_dir;
+ goto err_hw_init;
/* Read irq status register to reset irq line */
rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status);
@@ -981,30 +1229,37 @@ static int at86rf230_probe(struct spi_device *spi)
err_irq:
free_irq(spi->irq, lp);
+err_hw_init:
flush_work(&lp->irqwork);
-err_gpio_dir:
- if (gpio_is_valid(lp->slp_tr))
- gpio_free(lp->slp_tr);
-err_slp_tr:
- gpio_free(lp->rstn);
-err_rstn:
+ spi_set_drvdata(spi, NULL);
mutex_destroy(&lp->bmux);
ieee802154_free_device(lp->dev);
+
+err_gpio_dir:
+ if (gpio_is_valid(pdata->slp_tr))
+ gpio_free(pdata->slp_tr);
+err_slp_tr:
+ if (gpio_is_valid(pdata->rstn))
+ gpio_free(pdata->rstn);
return rc;
}
static int at86rf230_remove(struct spi_device *spi)
{
struct at86rf230_local *lp = spi_get_drvdata(spi);
+ struct at86rf230_platform_data *pdata = spi->dev.platform_data;
+ /* mask all at86rf230 irq's */
+ at86rf230_write_subreg(lp, SR_IRQ_MASK, 0);
ieee802154_unregister_device(lp->dev);
free_irq(spi->irq, lp);
flush_work(&lp->irqwork);
- if (gpio_is_valid(lp->slp_tr))
- gpio_free(lp->slp_tr);
- gpio_free(lp->rstn);
+ if (gpio_is_valid(pdata->slp_tr))
+ gpio_free(pdata->slp_tr);
+ if (gpio_is_valid(pdata->rstn))
+ gpio_free(pdata->rstn);
mutex_destroy(&lp->bmux);
ieee802154_free_device(lp->dev);
@@ -1013,8 +1268,19 @@ static int at86rf230_remove(struct spi_device *spi)
return 0;
}
+#if IS_ENABLED(CONFIG_OF)
+static struct of_device_id at86rf230_of_match[] = {
+ { .compatible = "atmel,at86rf230", },
+ { .compatible = "atmel,at86rf231", },
+ { .compatible = "atmel,at86rf233", },
+ { .compatible = "atmel,at86rf212", },
+ { },
+};
+#endif
+
static struct spi_driver at86rf230_driver = {
.driver = {
+ .of_match_table = of_match_ptr(at86rf230_of_match),
.name = "at86rf230",
.owner = THIS_MODULE,
},
diff --git a/drivers/net/ieee802154/fakehard.c b/drivers/net/ieee802154/fakehard.c
index bf0d55e2dd63..78f18be3bbf2 100644
--- a/drivers/net/ieee802154/fakehard.c
+++ b/drivers/net/ieee802154/fakehard.c
@@ -63,11 +63,11 @@ static struct wpan_phy *fake_get_phy(const struct net_device *dev)
*
* Return the ID of the PAN from the PIB.
*/
-static u16 fake_get_pan_id(const struct net_device *dev)
+static __le16 fake_get_pan_id(const struct net_device *dev)
{
BUG_ON(dev->type != ARPHRD_IEEE802154);
- return 0xeba1;
+ return cpu_to_le16(0xeba1);
}
/**
@@ -78,11 +78,11 @@ static u16 fake_get_pan_id(const struct net_device *dev)
* device. If the device has not yet had a short address assigned
* then this should return 0xFFFF to indicate a lack of association.
*/
-static u16 fake_get_short_addr(const struct net_device *dev)
+static __le16 fake_get_short_addr(const struct net_device *dev)
{
BUG_ON(dev->type != ARPHRD_IEEE802154);
- return 0x1;
+ return cpu_to_le16(0x1);
}
/**
@@ -149,7 +149,7 @@ static int fake_assoc_req(struct net_device *dev,
* 802.15.4-2006 document.
*/
static int fake_assoc_resp(struct net_device *dev,
- struct ieee802154_addr *addr, u16 short_addr, u8 status)
+ struct ieee802154_addr *addr, __le16 short_addr, u8 status)
{
return 0;
}
@@ -191,10 +191,10 @@ static int fake_disassoc_req(struct net_device *dev,
* Note: This is in section 7.5.2.3 of the IEEE 802.15.4-2006
* document, with 7.3.8 describing coordinator realignment.
*/
-static int fake_start_req(struct net_device *dev, struct ieee802154_addr *addr,
- u8 channel, u8 page,
- u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
- u8 coord_realign)
+static int fake_start_req(struct net_device *dev,
+ struct ieee802154_addr *addr, u8 channel, u8 page,
+ u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
+ u8 coord_realign)
{
struct wpan_phy *phy = fake_to_phy(dev);
@@ -281,8 +281,8 @@ static int ieee802154_fake_ioctl(struct net_device *dev, struct ifreq *ifr,
switch (cmd) {
case SIOCGIFADDR:
/* FIXME: fixed here, get from device IRL */
- pan_id = fake_get_pan_id(dev);
- short_addr = fake_get_short_addr(dev);
+ pan_id = le16_to_cpu(fake_get_pan_id(dev));
+ short_addr = le16_to_cpu(fake_get_short_addr(dev));
if (pan_id == IEEE802154_PANID_BROADCAST ||
short_addr == IEEE802154_ADDR_BROADCAST)
return -EADDRNOTAVAIL;
diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c
index 246befa4ba05..78a6552ed707 100644
--- a/drivers/net/ieee802154/mrf24j40.c
+++ b/drivers/net/ieee802154/mrf24j40.c
@@ -465,8 +465,8 @@ static int mrf24j40_filter(struct ieee802154_dev *dev,
if (changed & IEEE802515_AFILT_SADDR_CHANGED) {
/* Short Addr */
u8 addrh, addrl;
- addrh = filt->short_addr >> 8 & 0xff;
- addrl = filt->short_addr & 0xff;
+ addrh = le16_to_cpu(filt->short_addr) >> 8 & 0xff;
+ addrl = le16_to_cpu(filt->short_addr) & 0xff;
write_short_reg(devrec, REG_SADRH, addrh);
write_short_reg(devrec, REG_SADRL, addrl);
@@ -476,15 +476,16 @@ static int mrf24j40_filter(struct ieee802154_dev *dev,
if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) {
/* Device Address */
- int i;
+ u8 i, addr[8];
+
+ memcpy(addr, &filt->ieee_addr, 8);
for (i = 0; i < 8; i++)
- write_short_reg(devrec, REG_EADR0+i,
- filt->ieee_addr[7-i]);
+ write_short_reg(devrec, REG_EADR0 + i, addr[i]);
#ifdef DEBUG
printk(KERN_DEBUG "Set long addr to: ");
for (i = 0; i < 8; i++)
- printk("%02hhx ", filt->ieee_addr[i]);
+ printk("%02hhx ", addr[7 - i]);
printk(KERN_DEBUG "\n");
#endif
}
@@ -492,8 +493,8 @@ static int mrf24j40_filter(struct ieee802154_dev *dev,
if (changed & IEEE802515_AFILT_PANID_CHANGED) {
/* PAN ID */
u8 panidl, panidh;
- panidh = filt->pan_id >> 8 & 0xff;
- panidl = filt->pan_id & 0xff;
+ panidh = le16_to_cpu(filt->pan_id) >> 8 & 0xff;
+ panidl = le16_to_cpu(filt->pan_id) & 0xff;
write_short_reg(devrec, REG_PANIDH, panidh);
write_short_reg(devrec, REG_PANIDL, panidl);
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index d7b2e947184b..46a7790be004 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -136,18 +136,18 @@ static struct rtnl_link_stats64 *ifb_stats64(struct net_device *dev,
unsigned int start;
do {
- start = u64_stats_fetch_begin_bh(&dp->rsync);
+ start = u64_stats_fetch_begin_irq(&dp->rsync);
stats->rx_packets = dp->rx_packets;
stats->rx_bytes = dp->rx_bytes;
- } while (u64_stats_fetch_retry_bh(&dp->rsync, start));
+ } while (u64_stats_fetch_retry_irq(&dp->rsync, start));
do {
- start = u64_stats_fetch_begin_bh(&dp->tsync);
+ start = u64_stats_fetch_begin_irq(&dp->tsync);
stats->tx_packets = dp->tx_packets;
stats->tx_bytes = dp->tx_bytes;
- } while (u64_stats_fetch_retry_bh(&dp->tsync, start));
+ } while (u64_stats_fetch_retry_irq(&dp->tsync, start));
stats->rx_dropped = dev->stats.rx_dropped;
stats->tx_dropped = dev->stats.tx_dropped;
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index c5011e078e1b..bb96409f8c05 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -111,10 +111,10 @@ static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev,
lb_stats = per_cpu_ptr(dev->lstats, i);
do {
- start = u64_stats_fetch_begin_bh(&lb_stats->syncp);
+ start = u64_stats_fetch_begin_irq(&lb_stats->syncp);
tbytes = lb_stats->bytes;
tpackets = lb_stats->packets;
- } while (u64_stats_fetch_retry_bh(&lb_stats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&lb_stats->syncp, start));
bytes += tbytes;
packets += tpackets;
}
@@ -136,16 +136,9 @@ static const struct ethtool_ops loopback_ethtool_ops = {
static int loopback_dev_init(struct net_device *dev)
{
- int i;
- dev->lstats = alloc_percpu(struct pcpu_lstats);
+ dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats);
if (!dev->lstats)
return -ENOMEM;
-
- for_each_possible_cpu(i) {
- struct pcpu_lstats *lb_stats;
- lb_stats = per_cpu_ptr(dev->lstats, i);
- u64_stats_init(&lb_stats->syncp);
- }
return 0;
}
@@ -160,6 +153,7 @@ static const struct net_device_ops loopback_ops = {
.ndo_init = loopback_dev_init,
.ndo_start_xmit= loopback_xmit,
.ndo_get_stats64 = loopback_get_stats64,
+ .ndo_set_mac_address = eth_mac_addr,
};
/*
@@ -174,6 +168,7 @@ static void loopback_setup(struct net_device *dev)
dev->tx_queue_len = 0;
dev->type = ARPHRD_LOOPBACK; /* 0x0001*/
dev->flags = IFF_LOOPBACK;
+ dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
dev->hw_features = NETIF_F_ALL_TSO | NETIF_F_UFO;
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST
@@ -181,6 +176,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_HIGHDMA
| NETIF_F_LLTX
| NETIF_F_NETNS_LOCAL
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 1831fb7cd017..753a8c23d15d 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -537,7 +537,6 @@ static int macvlan_init(struct net_device *dev)
{
struct macvlan_dev *vlan = netdev_priv(dev);
const struct net_device *lowerdev = vlan->lowerdev;
- int i;
dev->state = (dev->state & ~MACVLAN_STATE_MASK) |
(lowerdev->state & MACVLAN_STATE_MASK);
@@ -549,16 +548,10 @@ static int macvlan_init(struct net_device *dev)
macvlan_set_lockdep_class(dev);
- vlan->pcpu_stats = alloc_percpu(struct vlan_pcpu_stats);
+ vlan->pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats);
if (!vlan->pcpu_stats)
return -ENOMEM;
- for_each_possible_cpu(i) {
- struct vlan_pcpu_stats *mvlstats;
- mvlstats = per_cpu_ptr(vlan->pcpu_stats, i);
- u64_stats_init(&mvlstats->syncp);
- }
-
return 0;
}
@@ -589,13 +582,13 @@ static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev,
for_each_possible_cpu(i) {
p = per_cpu_ptr(vlan->pcpu_stats, i);
do {
- start = u64_stats_fetch_begin_bh(&p->syncp);
+ start = u64_stats_fetch_begin_irq(&p->syncp);
rx_packets = p->rx_packets;
rx_bytes = p->rx_bytes;
rx_multicast = p->rx_multicast;
tx_packets = p->tx_packets;
tx_bytes = p->tx_bytes;
- } while (u64_stats_fetch_retry_bh(&p->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&p->syncp, start));
stats->rx_packets += rx_packets;
stats->rx_bytes += rx_bytes;
diff --git a/drivers/net/nlmon.c b/drivers/net/nlmon.c
index d2bb12bfabd5..34924dfadd00 100644
--- a/drivers/net/nlmon.c
+++ b/drivers/net/nlmon.c
@@ -47,16 +47,7 @@ static int nlmon_change_mtu(struct net_device *dev, int new_mtu)
static int nlmon_dev_init(struct net_device *dev)
{
- int i;
-
- dev->lstats = alloc_percpu(struct pcpu_lstats);
-
- for_each_possible_cpu(i) {
- struct pcpu_lstats *nlmstats;
- nlmstats = per_cpu_ptr(dev->lstats, i);
- u64_stats_init(&nlmstats->syncp);
- }
-
+ dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats);
return dev->lstats == NULL ? -ENOMEM : 0;
}
@@ -99,10 +90,10 @@ nlmon_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
nl_stats = per_cpu_ptr(dev->lstats, i);
do {
- start = u64_stats_fetch_begin_bh(&nl_stats->syncp);
+ start = u64_stats_fetch_begin_irq(&nl_stats->syncp);
tbytes = nl_stats->bytes;
tpackets = nl_stats->packets;
- } while (u64_stats_fetch_retry_bh(&nl_stats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&nl_stats->syncp, start));
packets += tpackets;
bytes += tbytes;
@@ -145,7 +136,8 @@ static void nlmon_setup(struct net_device *dev)
dev->ethtool_ops = &nlmon_ethtool_ops;
dev->destructor = free_netdev;
- dev->features = NETIF_F_FRAGLIST | NETIF_F_HIGHDMA;
+ dev->features = NETIF_F_SG | NETIF_F_FRAGLIST |
+ NETIF_F_HIGHDMA | NETIF_F_LLTX;
dev->flags = IFF_NOARP;
/* That's rather a softlimit here, which, of course,
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 9b5d46c03eed..6a17f92153b3 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -71,6 +71,12 @@ config BCM63XX_PHY
---help---
Currently supports the 6348 and 6358 PHYs.
+config BCM7XXX_PHY
+ tristate "Drivers for Broadcom 7xxx SOCs internal PHYs"
+ ---help---
+ Currently supports the BCM7366, BCM7439, BCM7445, and
+ 40nm and 65nm generation of BCM7xxx Set Top Box SoCs.
+
config BCM87XX_PHY
tristate "Driver for Broadcom BCM8706 and BCM8727 PHYs"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 9013dfa12aa3..07d24024863e 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_SMSC_PHY) += smsc.o
obj-$(CONFIG_VITESSE_PHY) += vitesse.o
obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o
+obj-$(CONFIG_BCM7XXX_PHY) += bcm7xxx.o
obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o
obj-$(CONFIG_ICPLUS_PHY) += icplus.o
obj-$(CONFIG_REALTEK_PHY) += realtek.o
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index bc71947b1ec3..643464d5a727 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -27,6 +27,9 @@
#define AT803X_MMD_ACCESS_CONTROL 0x0D
#define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E
#define AT803X_FUNC_DATA 0x4003
+#define AT803X_INER 0x0012
+#define AT803X_INER_INIT 0xec00
+#define AT803X_INSR 0x0013
#define AT803X_DEBUG_ADDR 0x1D
#define AT803X_DEBUG_DATA 0x1E
#define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05
@@ -191,6 +194,31 @@ static int at803x_config_init(struct phy_device *phydev)
return 0;
}
+static int at803x_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_read(phydev, AT803X_INSR);
+
+ return (err < 0) ? err : 0;
+}
+
+static int at803x_config_intr(struct phy_device *phydev)
+{
+ int err;
+ int value;
+
+ value = phy_read(phydev, AT803X_INER);
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, AT803X_INER,
+ value | AT803X_INER_INIT);
+ else
+ err = phy_write(phydev, AT803X_INER, 0);
+
+ return err;
+}
+
static struct phy_driver at803x_driver[] = {
{
/* ATHEROS 8035 */
@@ -240,6 +268,8 @@ static struct phy_driver at803x_driver[] = {
.flags = PHY_HAS_INTERRUPT,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
+ .ack_interrupt = &at803x_ack_interrupt,
+ .config_intr = &at803x_config_intr,
.driver = {
.owner = THIS_MODULE,
},
diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
new file mode 100644
index 000000000000..526b94cea569
--- /dev/null
+++ b/drivers/net/phy/bcm7xxx.c
@@ -0,0 +1,359 @@
+/*
+ * Broadcom BCM7xxx internal transceivers support.
+ *
+ * Copyright (C) 2014, 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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/brcmphy.h>
+
+/* Broadcom BCM7xxx internal PHY registers */
+#define MII_BCM7XXX_CHANNEL_WIDTH 0x2000
+
+/* 40nm only register definitions */
+#define MII_BCM7XXX_100TX_AUX_CTL 0x10
+#define MII_BCM7XXX_100TX_FALSE_CAR 0x13
+#define MII_BCM7XXX_100TX_DISC 0x14
+#define MII_BCM7XXX_AUX_MODE 0x1d
+#define MII_BCM7XX_64CLK_MDIO BIT(12)
+#define MII_BCM7XXX_CORE_BASE1E 0x1e
+#define MII_BCM7XXX_TEST 0x1f
+#define MII_BCM7XXX_SHD_MODE_2 BIT(2)
+
+/* 28nm only register definitions */
+#define MISC_ADDR(base, channel) base, channel
+
+#define DSP_TAP10 MISC_ADDR(0x0a, 0)
+#define PLL_PLLCTRL_1 MISC_ADDR(0x32, 1)
+#define PLL_PLLCTRL_2 MISC_ADDR(0x32, 2)
+#define PLL_PLLCTRL_4 MISC_ADDR(0x33, 0)
+
+#define AFE_RXCONFIG_0 MISC_ADDR(0x38, 0)
+#define AFE_RXCONFIG_1 MISC_ADDR(0x38, 1)
+#define AFE_RX_LP_COUNTER MISC_ADDR(0x38, 3)
+#define AFE_TX_CONFIG MISC_ADDR(0x39, 0)
+#define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0)
+
+#define CORE_EXPB0 0xb0
+
+static int bcm7445_config_init(struct phy_device *phydev)
+{
+ int ret;
+ const struct bcm7445_regs {
+ int reg;
+ u16 value;
+ } bcm7445_regs_cfg[] = {
+ /* increases ADC latency by 24ns */
+ { MII_BCM54XX_EXP_SEL, 0x0038 },
+ { MII_BCM54XX_EXP_DATA, 0xAB95 },
+ /* increases internal 1V LDO voltage by 5% */
+ { MII_BCM54XX_EXP_SEL, 0x2038 },
+ { MII_BCM54XX_EXP_DATA, 0xBB22 },
+ /* reduce RX low pass filter corner frequency */
+ { MII_BCM54XX_EXP_SEL, 0x6038 },
+ { MII_BCM54XX_EXP_DATA, 0xFFC5 },
+ /* reduce RX high pass filter corner frequency */
+ { MII_BCM54XX_EXP_SEL, 0x003a },
+ { MII_BCM54XX_EXP_DATA, 0x2002 },
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(bcm7445_regs_cfg); i++) {
+ ret = phy_write(phydev,
+ bcm7445_regs_cfg[i].reg,
+ bcm7445_regs_cfg[i].value);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void phy_write_exp(struct phy_device *phydev,
+ u16 reg, u16 value)
+{
+ phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg);
+ phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
+}
+
+static void phy_write_misc(struct phy_device *phydev,
+ u16 reg, u16 chl, u16 value)
+{
+ int tmp;
+
+ phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
+
+ tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
+ tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
+ phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
+
+ tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg;
+ phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp);
+
+ phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
+}
+
+static int bcm7xxx_28nm_afe_config_init(struct phy_device *phydev)
+{
+ /* Increase VCO range to prevent unlocking problem of PLL at low
+ * temp
+ */
+ phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
+
+ /* Change Ki to 011 */
+ phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
+
+ /* Disable loading of TVCO buffer to bandgap, set bandgap trim
+ * to 111
+ */
+ phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
+
+ /* Adjust bias current trim by -3 */
+ phy_write_misc(phydev, DSP_TAP10, 0x690b);
+
+ /* Switch to CORE_BASE1E */
+ phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd);
+
+ /* Reset R_CAL/RC_CAL Engine */
+ phy_write_exp(phydev, CORE_EXPB0, 0x0010);
+
+ /* Disable Reset R_CAL/RC_CAL Engine */
+ phy_write_exp(phydev, CORE_EXPB0, 0x0000);
+
+ /* write AFE_RXCONFIG_0 */
+ phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
+
+ /* write AFE_RXCONFIG_1 */
+ phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
+
+ /* write AFE_RX_LP_COUNTER */
+ phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
+
+ /* write AFE_HPF_TRIM_OTHERS */
+ phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
+
+ /* write AFTE_TX_CONFIG */
+ phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
+
+ return 0;
+}
+
+static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = bcm7445_config_init(phydev);
+ if (ret)
+ return ret;
+
+ return bcm7xxx_28nm_afe_config_init(phydev);
+}
+
+static int phy_set_clr_bits(struct phy_device *dev, int location,
+ int set_mask, int clr_mask)
+{
+ int v, ret;
+
+ v = phy_read(dev, location);
+ if (v < 0)
+ return v;
+
+ v &= ~clr_mask;
+ v |= set_mask;
+
+ ret = phy_write(dev, location, v);
+ if (ret < 0)
+ return ret;
+
+ return v;
+}
+
+static int bcm7xxx_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ /* Enable 64 clock MDIO */
+ phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO);
+ phy_read(phydev, MII_BCM7XXX_AUX_MODE);
+
+ /* Workaround only required for 100Mbits/sec */
+ if (!(phydev->dev_flags & PHY_BRCM_100MBPS_WAR))
+ return 0;
+
+ /* set shadow mode 2 */
+ ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
+ MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
+ if (ret < 0)
+ return ret;
+
+ /* set iddq_clkbias */
+ phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
+ udelay(10);
+
+ /* reset iddq_clkbias */
+ phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
+
+ phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
+
+ /* reset shadow mode 2 */
+ ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* Workaround for putting the PHY in IDDQ mode, required
+ * for all BCM7XXX PHYs
+ */
+static int bcm7xxx_suspend(struct phy_device *phydev)
+{
+ int ret;
+ const struct bcm7xxx_regs {
+ int reg;
+ u16 value;
+ } bcm7xxx_suspend_cfg[] = {
+ { MII_BCM7XXX_TEST, 0x008b },
+ { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
+ { MII_BCM7XXX_100TX_DISC, 0x7000 },
+ { MII_BCM7XXX_TEST, 0x000f },
+ { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
+ { MII_BCM7XXX_TEST, 0x000b },
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
+ ret = phy_write(phydev,
+ bcm7xxx_suspend_cfg[i].reg,
+ bcm7xxx_suspend_cfg[i].value);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static struct phy_driver bcm7xxx_driver[] = {
+{
+ .phy_id = PHY_ID_BCM7366,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM7366",
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = bcm7xxx_28nm_afe_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .suspend = bcm7xxx_suspend,
+ .resume = bcm7xxx_28nm_afe_config_init,
+ .driver = { .owner = THIS_MODULE },
+}, {
+ .phy_id = PHY_ID_BCM7439,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM7439",
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = bcm7xxx_28nm_afe_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .suspend = bcm7xxx_suspend,
+ .resume = bcm7xxx_28nm_afe_config_init,
+ .driver = { .owner = THIS_MODULE },
+}, {
+ .phy_id = PHY_ID_BCM7445,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM7445",
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = bcm7xxx_28nm_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .suspend = bcm7xxx_suspend,
+ .resume = bcm7xxx_28nm_config_init,
+ .driver = { .owner = THIS_MODULE },
+}, {
+ .name = "Broadcom BCM7XXX 28nm",
+ .phy_id = PHY_ID_BCM7XXX_28,
+ .phy_id_mask = PHY_BCM_OUI_MASK,
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = bcm7xxx_28nm_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .suspend = bcm7xxx_suspend,
+ .resume = bcm7xxx_28nm_config_init,
+ .driver = { .owner = THIS_MODULE },
+}, {
+ .phy_id = PHY_BCM_OUI_4,
+ .phy_id_mask = 0xffff0000,
+ .name = "Broadcom BCM7XXX 40nm",
+ .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,
+ .driver = { .owner = THIS_MODULE },
+}, {
+ .phy_id = PHY_BCM_OUI_5,
+ .phy_id_mask = 0xffffff00,
+ .name = "Broadcom BCM7XXX 65nm",
+ .features = PHY_BASIC_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = bcm7xxx_dummy_config_init,
+ .config_aneg = genphy_config_aneg,
+ .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[] = {
+ { PHY_ID_BCM7366, 0xfffffff0, },
+ { PHY_ID_BCM7439, 0xfffffff0, },
+ { PHY_ID_BCM7445, 0xfffffff0, },
+ { PHY_ID_BCM7XXX_28, 0xfffffc00 },
+ { PHY_BCM_OUI_4, 0xffff0000 },
+ { PHY_BCM_OUI_5, 0xffffff00 },
+ { }
+};
+
+static int __init bcm7xxx_phy_init(void)
+{
+ return phy_drivers_register(bcm7xxx_driver,
+ ARRAY_SIZE(bcm7xxx_driver));
+}
+
+static void __exit bcm7xxx_phy_exit(void)
+{
+ phy_drivers_unregister(bcm7xxx_driver,
+ ARRAY_SIZE(bcm7xxx_driver));
+}
+
+module_init(bcm7xxx_phy_init);
+module_exit(bcm7xxx_phy_exit);
+
+MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
+
+MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Broadcom Corporation");
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index f8c90ea75108..34088d60da74 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -25,58 +25,6 @@
#define BRCM_PHY_REV(phydev) \
((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
-
-#define MII_BCM54XX_ECR 0x10 /* BCM54xx extended control register */
-#define MII_BCM54XX_ECR_IM 0x1000 /* Interrupt mask */
-#define MII_BCM54XX_ECR_IF 0x0800 /* Interrupt force */
-
-#define MII_BCM54XX_ESR 0x11 /* BCM54xx extended status register */
-#define MII_BCM54XX_ESR_IS 0x1000 /* Interrupt status */
-
-#define MII_BCM54XX_EXP_DATA 0x15 /* Expansion register data */
-#define MII_BCM54XX_EXP_SEL 0x17 /* Expansion register select */
-#define MII_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */
-#define MII_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */
-
-#define MII_BCM54XX_AUX_CTL 0x18 /* Auxiliary control register */
-#define MII_BCM54XX_ISR 0x1a /* BCM54xx interrupt status register */
-#define MII_BCM54XX_IMR 0x1b /* BCM54xx interrupt mask register */
-#define MII_BCM54XX_INT_CRCERR 0x0001 /* CRC error */
-#define MII_BCM54XX_INT_LINK 0x0002 /* Link status changed */
-#define MII_BCM54XX_INT_SPEED 0x0004 /* Link speed change */
-#define MII_BCM54XX_INT_DUPLEX 0x0008 /* Duplex mode changed */
-#define MII_BCM54XX_INT_LRS 0x0010 /* Local receiver status changed */
-#define MII_BCM54XX_INT_RRS 0x0020 /* Remote receiver status changed */
-#define MII_BCM54XX_INT_SSERR 0x0040 /* Scrambler synchronization error */
-#define MII_BCM54XX_INT_UHCD 0x0080 /* Unsupported HCD negotiated */
-#define MII_BCM54XX_INT_NHCD 0x0100 /* No HCD */
-#define MII_BCM54XX_INT_NHCDL 0x0200 /* No HCD link */
-#define MII_BCM54XX_INT_ANPR 0x0400 /* Auto-negotiation page received */
-#define MII_BCM54XX_INT_LC 0x0800 /* All counters below 128 */
-#define MII_BCM54XX_INT_HC 0x1000 /* Counter above 32768 */
-#define MII_BCM54XX_INT_MDIX 0x2000 /* MDIX status change */
-#define MII_BCM54XX_INT_PSERR 0x4000 /* Pair swap error */
-
-#define MII_BCM54XX_SHD 0x1c /* 0x1c shadow registers */
-#define MII_BCM54XX_SHD_WRITE 0x8000
-#define MII_BCM54XX_SHD_VAL(x) ((x & 0x1f) << 10)
-#define MII_BCM54XX_SHD_DATA(x) ((x & 0x3ff) << 0)
-
-/*
- * AUXILIARY CONTROL SHADOW ACCESS REGISTERS. (PHY REG 0x18)
- */
-#define MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL 0x0000
-#define MII_BCM54XX_AUXCTL_ACTL_TX_6DB 0x0400
-#define MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA 0x0800
-
-#define MII_BCM54XX_AUXCTL_MISC_WREN 0x8000
-#define MII_BCM54XX_AUXCTL_MISC_FORCE_AMDIX 0x0200
-#define MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC 0x7000
-#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007
-
-#define MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL 0x0000
-
-
/*
* Broadcom LED source encodings. These are used in BCM5461, BCM5481,
* BCM5482, and possibly some others.
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 98e7cbf720a5..6a999e6814a0 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/net_tstamp.h>
#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
#include <linux/phy.h>
#include <linux/ptp_classify.h>
#include <linux/ptp_clock_kernel.h>
@@ -47,6 +48,7 @@
#define CAL_EVENT 7
#define CAL_TRIGGER 7
#define PER_TRIGGER 6
+#define DP83640_N_PINS 12
#define MII_DP83640_MICR 0x11
#define MII_DP83640_MISR 0x12
@@ -173,6 +175,37 @@ MODULE_PARM_DESC(chosen_phy, \
MODULE_PARM_DESC(gpio_tab, \
"Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6");
+static void dp83640_gpio_defaults(struct ptp_pin_desc *pd)
+{
+ int i, index;
+
+ for (i = 0; i < DP83640_N_PINS; i++) {
+ snprintf(pd[i].name, sizeof(pd[i].name), "GPIO%d", 1 + i);
+ pd[i].index = i;
+ }
+
+ for (i = 0; i < GPIO_TABLE_SIZE; i++) {
+ if (gpio_tab[i] < 1 || gpio_tab[i] > DP83640_N_PINS) {
+ pr_err("gpio_tab[%d]=%hu out of range", i, gpio_tab[i]);
+ return;
+ }
+ }
+
+ index = gpio_tab[CALIBRATE_GPIO] - 1;
+ pd[index].func = PTP_PF_PHYSYNC;
+ pd[index].chan = 0;
+
+ index = gpio_tab[PEROUT_GPIO] - 1;
+ pd[index].func = PTP_PF_PEROUT;
+ pd[index].chan = 0;
+
+ for (i = EXTTS0_GPIO; i < GPIO_TABLE_SIZE; i++) {
+ index = gpio_tab[i] - 1;
+ pd[index].func = PTP_PF_EXTTS;
+ pd[index].chan = i - EXTTS0_GPIO;
+ }
+}
+
/* a list of clocks and a mutex to protect it */
static LIST_HEAD(phyter_clocks);
static DEFINE_MUTEX(phyter_clocks_lock);
@@ -266,15 +299,22 @@ static u64 phy2txts(struct phy_txts *p)
return ns;
}
-static void periodic_output(struct dp83640_clock *clock,
- struct ptp_clock_request *clkreq, bool on)
+static int periodic_output(struct dp83640_clock *clock,
+ struct ptp_clock_request *clkreq, bool on)
{
struct dp83640_private *dp83640 = clock->chosen;
struct phy_device *phydev = dp83640->phydev;
- u32 sec, nsec, period;
+ u32 sec, nsec, pwidth;
u16 gpio, ptp_trig, trigger, val;
- gpio = on ? gpio_tab[PEROUT_GPIO] : 0;
+ if (on) {
+ gpio = 1 + ptp_find_pin(clock->ptp_clock, PTP_PF_PEROUT, 0);
+ if (gpio < 1)
+ return -EINVAL;
+ } else {
+ gpio = 0;
+ }
+
trigger = PER_TRIGGER;
ptp_trig = TRIG_WR |
@@ -291,13 +331,14 @@ static void periodic_output(struct dp83640_clock *clock,
ext_write(0, phydev, PAGE5, PTP_TRIG, ptp_trig);
ext_write(0, phydev, PAGE4, PTP_CTL, val);
mutex_unlock(&clock->extreg_lock);
- return;
+ return 0;
}
sec = clkreq->perout.start.sec;
nsec = clkreq->perout.start.nsec;
- period = clkreq->perout.period.sec * 1000000000UL;
- period += clkreq->perout.period.nsec;
+ pwidth = clkreq->perout.period.sec * 1000000000UL;
+ pwidth += clkreq->perout.period.nsec;
+ pwidth /= 2;
mutex_lock(&clock->extreg_lock);
@@ -310,8 +351,8 @@ static void periodic_output(struct dp83640_clock *clock,
ext_write(0, phydev, PAGE4, PTP_TDR, nsec >> 16); /* ns[31:16] */
ext_write(0, phydev, PAGE4, PTP_TDR, sec & 0xffff); /* sec[15:0] */
ext_write(0, phydev, PAGE4, PTP_TDR, sec >> 16); /* sec[31:16] */
- ext_write(0, phydev, PAGE4, PTP_TDR, period & 0xffff); /* ns[15:0] */
- ext_write(0, phydev, PAGE4, PTP_TDR, period >> 16); /* ns[31:16] */
+ ext_write(0, phydev, PAGE4, PTP_TDR, pwidth & 0xffff); /* ns[15:0] */
+ ext_write(0, phydev, PAGE4, PTP_TDR, pwidth >> 16); /* ns[31:16] */
/*enable trigger*/
val &= ~TRIG_LOAD;
@@ -319,6 +360,7 @@ static void periodic_output(struct dp83640_clock *clock,
ext_write(0, phydev, PAGE4, PTP_CTL, val);
mutex_unlock(&clock->extreg_lock);
+ return 0;
}
/* ptp clock methods */
@@ -424,18 +466,21 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
struct dp83640_clock *clock =
container_of(ptp, struct dp83640_clock, caps);
struct phy_device *phydev = clock->chosen->phydev;
- int index;
+ unsigned int index;
u16 evnt, event_num, gpio_num;
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
index = rq->extts.index;
- if (index < 0 || index >= N_EXT_TS)
+ if (index >= N_EXT_TS)
return -EINVAL;
event_num = EXT_EVENT + index;
evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
if (on) {
- gpio_num = gpio_tab[EXTTS0_GPIO + index];
+ gpio_num = 1 + ptp_find_pin(clock->ptp_clock,
+ PTP_PF_EXTTS, index);
+ if (gpio_num < 1)
+ return -EINVAL;
evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
if (rq->extts.flags & PTP_FALLING_EDGE)
evnt |= EVNT_FALL;
@@ -448,8 +493,7 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
case PTP_CLK_REQ_PEROUT:
if (rq->perout.index != 0)
return -EINVAL;
- periodic_output(clock, rq, on);
- return 0;
+ return periodic_output(clock, rq, on);
default:
break;
@@ -458,6 +502,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
return -EOPNOTSUPP;
}
+static int ptp_dp83640_verify(struct ptp_clock_info *ptp, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ return 0;
+}
+
static u8 status_frame_dst[6] = { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 };
static u8 status_frame_src[6] = { 0x08, 0x00, 0x17, 0x0B, 0x6B, 0x0F };
@@ -875,6 +925,7 @@ static void dp83640_free_clocks(void)
mutex_destroy(&clock->extreg_lock);
mutex_destroy(&clock->clock_lock);
put_device(&clock->bus->dev);
+ kfree(clock->caps.pin_config);
kfree(clock);
}
@@ -894,12 +945,18 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
clock->caps.n_alarm = 0;
clock->caps.n_ext_ts = N_EXT_TS;
clock->caps.n_per_out = 1;
+ clock->caps.n_pins = DP83640_N_PINS;
clock->caps.pps = 0;
clock->caps.adjfreq = ptp_dp83640_adjfreq;
clock->caps.adjtime = ptp_dp83640_adjtime;
clock->caps.gettime = ptp_dp83640_gettime;
clock->caps.settime = ptp_dp83640_settime;
clock->caps.enable = ptp_dp83640_enable;
+ clock->caps.verify = ptp_dp83640_verify;
+ /*
+ * Convert the module param defaults into a dynamic pin configuration.
+ */
+ dp83640_gpio_defaults(clock->caps.pin_config);
/*
* Get a reference to this bus instance.
*/
@@ -950,6 +1007,13 @@ static struct dp83640_clock *dp83640_clock_get_bus(struct mii_bus *bus)
if (!clock)
goto out;
+ clock->caps.pin_config = kzalloc(sizeof(struct ptp_pin_desc) *
+ DP83640_N_PINS, GFP_KERNEL);
+ if (!clock->caps.pin_config) {
+ kfree(clock);
+ clock = NULL;
+ goto out;
+ }
dp83640_clock_init(clock, bus);
list_add_tail(&phyter_clocks, &clock->list);
out:
@@ -1363,7 +1427,7 @@ static void __exit dp83640_exit(void)
}
MODULE_DESCRIPTION("National Semiconductor DP83640 PHY driver");
-MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.at>");
+MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
MODULE_LICENSE("GPL");
module_init(dp83640_init);
diff --git a/drivers/net/phy/mdio-sun4i.c b/drivers/net/phy/mdio-sun4i.c
index 9367acc84fbb..15bc7f9ea224 100644
--- a/drivers/net/phy/mdio-sun4i.c
+++ b/drivers/net/phy/mdio-sun4i.c
@@ -90,11 +90,6 @@ static int sun4i_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
return 0;
}
-static int sun4i_mdio_reset(struct mii_bus *bus)
-{
- return 0;
-}
-
static int sun4i_mdio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -110,7 +105,6 @@ static int sun4i_mdio_probe(struct platform_device *pdev)
bus->name = "sun4i_mii_bus";
bus->read = &sun4i_mdio_read;
bus->write = &sun4i_mdio_write;
- bus->reset = &sun4i_mdio_reset;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
bus->parent = &pdev->dev;
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 71e49000fbf3..76f54b32a120 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -432,8 +432,28 @@ phy_id_show(struct device *dev, struct device_attribute *attr, char *buf)
}
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);
+
+ return sprintf(buf, "%s\n", phy_modes(phydev->interface));
+}
+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);
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 5a8993b0cafc..5ad971a55c5d 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -148,15 +148,52 @@ static int ks8737_config_intr(struct phy_device *phydev)
return rc < 0 ? rc : 0;
}
+static int kszphy_setup_led(struct phy_device *phydev,
+ unsigned int reg, unsigned int shift)
+{
+
+ struct device *dev = &phydev->dev;
+ struct device_node *of_node = dev->of_node;
+ int rc, temp;
+ u32 val;
+
+ if (!of_node && dev->parent->of_node)
+ of_node = dev->parent->of_node;
+
+ if (of_property_read_u32(of_node, "micrel,led-mode", &val))
+ return 0;
+
+ temp = phy_read(phydev, reg);
+ if (temp < 0)
+ return temp;
+
+ temp &= ~(3 << shift);
+ temp |= val << shift;
+ rc = phy_write(phydev, reg, temp);
+
+ return rc < 0 ? rc : 0;
+}
+
static int kszphy_config_init(struct phy_device *phydev)
{
return 0;
}
+static int kszphy_config_init_led8041(struct phy_device *phydev)
+{
+ /* single led control, register 0x1e bits 15..14 */
+ return kszphy_setup_led(phydev, 0x1e, 14);
+}
+
static int ksz8021_config_init(struct phy_device *phydev)
{
- int rc;
const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE;
+ int rc;
+
+ rc = kszphy_setup_led(phydev, 0x1f, 4);
+ if (rc)
+ dev_err(&phydev->dev, "failed to set led mode\n");
+
phy_write(phydev, MII_KSZPHY_OMSO, val);
rc = ksz_config_flags(phydev);
return rc < 0 ? rc : 0;
@@ -166,6 +203,10 @@ static int ks8051_config_init(struct phy_device *phydev)
{
int rc;
+ rc = kszphy_setup_led(phydev, 0x1f, 4);
+ if (rc)
+ dev_err(&phydev->dev, "failed to set led mode\n");
+
rc = ksz_config_flags(phydev);
return rc < 0 ? rc : 0;
}
@@ -327,7 +368,7 @@ static struct phy_driver ksphy_driver[] = {
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
- .config_init = kszphy_config_init,
+ .config_init = kszphy_config_init_led8041,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
@@ -342,7 +383,7 @@ static struct phy_driver ksphy_driver[] = {
.features = PHY_BASIC_FEATURES |
SUPPORTED_Pause | SUPPORTED_Asym_Pause,
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
- .config_init = kszphy_config_init,
+ .config_init = kszphy_config_init_led8041,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
@@ -371,7 +412,7 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = 0x00ffffff,
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
- .config_init = kszphy_config_init,
+ .config_init = kszphy_config_init_led8041,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 76d96b9ebcdb..1d788f19135b 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -38,6 +38,26 @@
#include <asm/irq.h>
+static const char *phy_speed_to_str(int speed)
+{
+ switch (speed) {
+ case SPEED_10:
+ return "10Mbps";
+ case SPEED_100:
+ return "100Mbps";
+ case SPEED_1000:
+ return "1Gbps";
+ case SPEED_2500:
+ return "2.5Gbps";
+ case SPEED_10000:
+ return "10Gbps";
+ case SPEED_UNKNOWN:
+ return "Unknown";
+ default:
+ return "Unsupported (update phy.c)";
+ }
+}
+
/**
* phy_print_status - Convenience function to print out the current phy status
* @phydev: the phy_device struct
@@ -45,12 +65,13 @@
void phy_print_status(struct phy_device *phydev)
{
if (phydev->link) {
- pr_info("%s - Link is Up - %d/%s\n",
- dev_name(&phydev->dev),
- phydev->speed,
- DUPLEX_FULL == phydev->duplex ? "Full" : "Half");
+ netdev_info(phydev->attached_dev,
+ "Link is Up - %s/%s - flow control %s\n",
+ phy_speed_to_str(phydev->speed),
+ DUPLEX_FULL == phydev->duplex ? "Full" : "Half",
+ phydev->pause ? "rx/tx" : "off");
} else {
- pr_info("%s - Link is Down\n", dev_name(&phydev->dev));
+ netdev_info(phydev->attached_dev, "Link is Down\n");
}
}
EXPORT_SYMBOL(phy_print_status);
@@ -62,7 +83,7 @@ EXPORT_SYMBOL(phy_print_status);
* If the @phydev driver has an ack_interrupt function, call it to
* ack and clear the phy device's interrupt.
*
- * Returns 0 on success on < 0 on error.
+ * Returns 0 on success or < 0 on error.
*/
static int phy_clear_interrupt(struct phy_device *phydev)
{
@@ -77,7 +98,7 @@ static int phy_clear_interrupt(struct phy_device *phydev)
* @phydev: the phy_device struct
* @interrupts: interrupt flags to configure for this @phydev
*
- * Returns 0 on success on < 0 on error.
+ * Returns 0 on success or < 0 on error.
*/
static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts)
{
@@ -93,15 +114,16 @@ static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts)
* phy_aneg_done - return auto-negotiation status
* @phydev: target phy_device struct
*
- * Description: Reads the status register and returns 0 either if
- * auto-negotiation is incomplete, or if there was an error.
- * Returns BMSR_ANEGCOMPLETE if auto-negotiation is done.
+ * Description: Return the auto-negotiation status from this @phydev
+ * Returns > 0 on success or < 0 on error. 0 means that auto-negotiation
+ * is still pending.
*/
static inline int phy_aneg_done(struct phy_device *phydev)
{
- int retval = phy_read(phydev, MII_BMSR);
+ if (phydev->drv->aneg_done)
+ return phydev->drv->aneg_done(phydev);
- return (retval < 0) ? retval : (retval & BMSR_ANEGCOMPLETE);
+ return genphy_aneg_done(phydev);
}
/* A structure for mapping a particular speed and duplex
@@ -283,7 +305,10 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd)
ethtool_cmd_speed_set(cmd, phydev->speed);
cmd->duplex = phydev->duplex;
- cmd->port = PORT_MII;
+ if (phydev->interface == PHY_INTERFACE_MODE_MOCA)
+ cmd->port = PORT_BNC;
+ else
+ cmd->port = PORT_MII;
cmd->phy_address = phydev->addr;
cmd->transceiver = phy_is_internal(phydev) ?
XCVR_INTERNAL : XCVR_EXTERNAL;
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 2f6989b1e0dc..0ce606624296 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -139,6 +139,7 @@ static int phy_scan_fixups(struct phy_device *phydev)
mutex_unlock(&phy_fixup_lock);
return err;
}
+ phydev->has_fixups = true;
}
}
mutex_unlock(&phy_fixup_lock);
@@ -534,16 +535,16 @@ static int phy_poll_reset(struct phy_device *phydev)
int phy_init_hw(struct phy_device *phydev)
{
- int ret;
+ int ret = 0;
if (!phydev->drv || !phydev->drv->config_init)
return 0;
- ret = phy_write(phydev, MII_BMCR, BMCR_RESET);
- if (ret < 0)
- return ret;
+ if (phydev->drv->soft_reset)
+ ret = phydev->drv->soft_reset(phydev);
+ else
+ ret = genphy_soft_reset(phydev);
- ret = phy_poll_reset(phydev);
if (ret < 0)
return ret;
@@ -864,6 +865,22 @@ int genphy_config_aneg(struct phy_device *phydev)
}
EXPORT_SYMBOL(genphy_config_aneg);
+/**
+ * genphy_aneg_done - return auto-negotiation status
+ * @phydev: target phy_device struct
+ *
+ * Description: Reads the status register and returns 0 either if
+ * auto-negotiation is incomplete, or if there was an error.
+ * Returns BMSR_ANEGCOMPLETE if auto-negotiation is done.
+ */
+int genphy_aneg_done(struct phy_device *phydev)
+{
+ int retval = phy_read(phydev, MII_BMSR);
+
+ return (retval < 0) ? retval : (retval & BMSR_ANEGCOMPLETE);
+}
+EXPORT_SYMBOL(genphy_aneg_done);
+
static int gen10g_config_aneg(struct phy_device *phydev)
{
return 0;
@@ -1029,6 +1046,27 @@ static int gen10g_read_status(struct phy_device *phydev)
return 0;
}
+/**
+ * genphy_soft_reset - software reset the PHY via BMCR_RESET bit
+ * @phydev: target phy_device struct
+ *
+ * Description: Perform a software PHY reset using the standard
+ * BMCR_RESET bit and poll for the reset bit to be cleared.
+ *
+ * Returns: 0 on success, < 0 on failure
+ */
+int genphy_soft_reset(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_write(phydev, MII_BMCR, BMCR_RESET);
+ if (ret < 0)
+ return ret;
+
+ return phy_poll_reset(phydev);
+}
+EXPORT_SYMBOL(genphy_soft_reset);
+
static int genphy_config_init(struct phy_device *phydev)
{
int val;
@@ -1075,6 +1113,12 @@ static int genphy_config_init(struct phy_device *phydev)
return 0;
}
+static int gen10g_soft_reset(struct phy_device *phydev)
+{
+ /* Do nothing for now */
+ return 0;
+}
+
static int gen10g_config_init(struct phy_device *phydev)
{
/* Temporarily just say we support everything */
@@ -1249,9 +1293,11 @@ static struct phy_driver genphy_driver[] = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",
+ .soft_reset = genphy_soft_reset,
.config_init = genphy_config_init,
.features = 0,
.config_aneg = genphy_config_aneg,
+ .aneg_done = genphy_aneg_done,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
@@ -1260,6 +1306,7 @@ static struct phy_driver genphy_driver[] = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic 10G PHY",
+ .soft_reset = gen10g_soft_reset,
.config_init = gen10g_config_init,
.features = 0,
.config_aneg = gen10g_config_aneg,
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 72ff14b811c6..e3923ebb693f 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -143,9 +143,8 @@ struct ppp {
struct sk_buff_head mrq; /* MP: receive reconstruction queue */
#endif /* CONFIG_PPP_MULTILINK */
#ifdef CONFIG_PPP_FILTER
- struct sock_filter *pass_filter; /* filter for packets to pass */
- struct sock_filter *active_filter;/* filter for pkts to reset idle */
- unsigned pass_len, active_len;
+ struct sk_filter *pass_filter; /* filter for packets to pass */
+ struct sk_filter *active_filter;/* filter for pkts to reset idle */
#endif /* CONFIG_PPP_FILTER */
struct net *ppp_net; /* the net we belong to */
struct ppp_link_stats stats64; /* 64 bit network stats */
@@ -755,28 +754,42 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case PPPIOCSPASS:
{
struct sock_filter *code;
+
err = get_filter(argp, &code);
if (err >= 0) {
+ struct sock_fprog fprog = {
+ .len = err,
+ .filter = code,
+ };
+
ppp_lock(ppp);
- kfree(ppp->pass_filter);
- ppp->pass_filter = code;
- ppp->pass_len = err;
+ if (ppp->pass_filter)
+ sk_unattached_filter_destroy(ppp->pass_filter);
+ err = sk_unattached_filter_create(&ppp->pass_filter,
+ &fprog);
+ kfree(code);
ppp_unlock(ppp);
- err = 0;
}
break;
}
case PPPIOCSACTIVE:
{
struct sock_filter *code;
+
err = get_filter(argp, &code);
if (err >= 0) {
+ struct sock_fprog fprog = {
+ .len = err,
+ .filter = code,
+ };
+
ppp_lock(ppp);
- kfree(ppp->active_filter);
- ppp->active_filter = code;
- ppp->active_len = err;
+ if (ppp->active_filter)
+ sk_unattached_filter_destroy(ppp->active_filter);
+ err = sk_unattached_filter_create(&ppp->active_filter,
+ &fprog);
+ kfree(code);
ppp_unlock(ppp);
- err = 0;
}
break;
}
@@ -1184,7 +1197,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
a four-byte PPP header on each packet */
*skb_push(skb, 2) = 1;
if (ppp->pass_filter &&
- sk_run_filter(skb, ppp->pass_filter) == 0) {
+ SK_RUN_FILTER(ppp->pass_filter, skb) == 0) {
if (ppp->debug & 1)
netdev_printk(KERN_DEBUG, ppp->dev,
"PPP: outbound frame "
@@ -1194,7 +1207,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
}
/* if this packet passes the active filter, record the time */
if (!(ppp->active_filter &&
- sk_run_filter(skb, ppp->active_filter) == 0))
+ SK_RUN_FILTER(ppp->active_filter, skb) == 0))
ppp->last_xmit = jiffies;
skb_pull(skb, 2);
#else
@@ -1818,7 +1831,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
*skb_push(skb, 2) = 0;
if (ppp->pass_filter &&
- sk_run_filter(skb, ppp->pass_filter) == 0) {
+ SK_RUN_FILTER(ppp->pass_filter, skb) == 0) {
if (ppp->debug & 1)
netdev_printk(KERN_DEBUG, ppp->dev,
"PPP: inbound frame "
@@ -1827,7 +1840,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
return;
}
if (!(ppp->active_filter &&
- sk_run_filter(skb, ppp->active_filter) == 0))
+ SK_RUN_FILTER(ppp->active_filter, skb) == 0))
ppp->last_recv = jiffies;
__skb_pull(skb, 2);
} else
@@ -2672,6 +2685,10 @@ ppp_create_interface(struct net *net, int unit, int *retp)
ppp->minseq = -1;
skb_queue_head_init(&ppp->mrq);
#endif /* CONFIG_PPP_MULTILINK */
+#ifdef CONFIG_PPP_FILTER
+ ppp->pass_filter = NULL;
+ ppp->active_filter = NULL;
+#endif /* CONFIG_PPP_FILTER */
/*
* drum roll: don't forget to set
@@ -2802,10 +2819,15 @@ static void ppp_destroy_interface(struct ppp *ppp)
skb_queue_purge(&ppp->mrq);
#endif /* CONFIG_PPP_MULTILINK */
#ifdef CONFIG_PPP_FILTER
- kfree(ppp->pass_filter);
- ppp->pass_filter = NULL;
- kfree(ppp->active_filter);
- ppp->active_filter = NULL;
+ if (ppp->pass_filter) {
+ sk_unattached_filter_destroy(ppp->pass_filter);
+ ppp->pass_filter = NULL;
+ }
+
+ if (ppp->active_filter) {
+ sk_unattached_filter_destroy(ppp->active_filter);
+ ppp->active_filter = NULL;
+ }
#endif /* CONFIG_PPP_FILTER */
kfree_skb(ppp->xmit_pending);
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index c8624a8235ab..33008c1d1d67 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -1031,8 +1031,7 @@ static void team_port_leave(struct team *team, struct team_port *port)
}
#ifdef CONFIG_NET_POLL_CONTROLLER
-static int team_port_enable_netpoll(struct team *team, struct team_port *port,
- gfp_t gfp)
+static int team_port_enable_netpoll(struct team *team, struct team_port *port)
{
struct netpoll *np;
int err;
@@ -1040,11 +1039,11 @@ static int team_port_enable_netpoll(struct team *team, struct team_port *port,
if (!team->dev->npinfo)
return 0;
- np = kzalloc(sizeof(*np), gfp);
+ np = kzalloc(sizeof(*np), GFP_KERNEL);
if (!np)
return -ENOMEM;
- err = __netpoll_setup(np, port->dev, gfp);
+ err = __netpoll_setup(np, port->dev);
if (err) {
kfree(np);
return err;
@@ -1067,8 +1066,7 @@ static void team_port_disable_netpoll(struct team_port *port)
kfree(np);
}
#else
-static int team_port_enable_netpoll(struct team *team, struct team_port *port,
- gfp_t gfp)
+static int team_port_enable_netpoll(struct team *team, struct team_port *port)
{
return 0;
}
@@ -1156,7 +1154,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
goto err_vids_add;
}
- err = team_port_enable_netpoll(team, port, GFP_KERNEL);
+ err = team_port_enable_netpoll(team, port);
if (err) {
netdev_err(dev, "Failed to enable netpoll on device %s\n",
portname);
@@ -1540,16 +1538,10 @@ static int team_init(struct net_device *dev)
mutex_init(&team->lock);
team_set_no_mode(team);
- team->pcpu_stats = alloc_percpu(struct team_pcpu_stats);
+ team->pcpu_stats = netdev_alloc_pcpu_stats(struct team_pcpu_stats);
if (!team->pcpu_stats)
return -ENOMEM;
- for_each_possible_cpu(i) {
- struct team_pcpu_stats *team_stats;
- team_stats = per_cpu_ptr(team->pcpu_stats, i);
- u64_stats_init(&team_stats->syncp);
- }
-
for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
INIT_HLIST_HEAD(&team->en_port_hlist[i]);
INIT_LIST_HEAD(&team->port_list);
@@ -1767,13 +1759,13 @@ team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
for_each_possible_cpu(i) {
p = per_cpu_ptr(team->pcpu_stats, i);
do {
- start = u64_stats_fetch_begin_bh(&p->syncp);
+ start = u64_stats_fetch_begin_irq(&p->syncp);
rx_packets = p->rx_packets;
rx_bytes = p->rx_bytes;
rx_multicast = p->rx_multicast;
tx_packets = p->tx_packets;
tx_bytes = p->tx_bytes;
- } while (u64_stats_fetch_retry_bh(&p->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&p->syncp, start));
stats->rx_packets += rx_packets;
stats->rx_bytes += rx_bytes;
@@ -1856,7 +1848,7 @@ static void team_netpoll_cleanup(struct net_device *dev)
}
static int team_netpoll_setup(struct net_device *dev,
- struct netpoll_info *npifo, gfp_t gfp)
+ struct netpoll_info *npifo)
{
struct team *team = netdev_priv(dev);
struct team_port *port;
@@ -1864,7 +1856,7 @@ static int team_netpoll_setup(struct net_device *dev,
mutex_lock(&team->lock);
list_for_each_entry(port, &team->port_list, list) {
- err = team_port_enable_netpoll(team, port, gfp);
+ err = team_port_enable_netpoll(team, port);
if (err) {
__team_netpoll_cleanup(team);
break;
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
index d671fc3ac5ac..dbde3412ee5e 100644
--- a/drivers/net/team/team_mode_loadbalance.c
+++ b/drivers/net/team/team_mode_loadbalance.c
@@ -432,9 +432,9 @@ static void __lb_one_cpu_stats_add(struct lb_stats *acc_stats,
struct lb_stats tmp;
do {
- start = u64_stats_fetch_begin_bh(syncp);
+ start = u64_stats_fetch_begin_irq(syncp);
tmp.tx_bytes = cpu_stats->tx_bytes;
- } while (u64_stats_fetch_retry_bh(syncp, start));
+ } while (u64_stats_fetch_retry_irq(syncp, start));
acc_stats->tx_bytes += tmp.tx_bytes;
}
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 26f8635b027d..ee328ba101e7 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -452,7 +452,7 @@ static void __tun_detach(struct tun_file *tfile, bool clean)
--tun->numqueues;
if (clean) {
- rcu_assign_pointer(tfile->tun, NULL);
+ RCU_INIT_POINTER(tfile->tun, NULL);
sock_put(&tfile->sk);
} else
tun_disable_queue(tun, tfile);
@@ -499,12 +499,12 @@ static void tun_detach_all(struct net_device *dev)
tfile = rtnl_dereference(tun->tfiles[i]);
BUG_ON(!tfile);
wake_up_all(&tfile->wq.wait);
- rcu_assign_pointer(tfile->tun, NULL);
+ RCU_INIT_POINTER(tfile->tun, NULL);
--tun->numqueues;
}
list_for_each_entry(tfile, &tun->disabled, next) {
wake_up_all(&tfile->wq.wait);
- rcu_assign_pointer(tfile->tun, NULL);
+ RCU_INIT_POINTER(tfile->tun, NULL);
}
BUG_ON(tun->numqueues != 0);
@@ -2194,7 +2194,7 @@ static int tun_chr_open(struct inode *inode, struct file * file)
&tun_proto);
if (!tfile)
return -ENOMEM;
- rcu_assign_pointer(tfile->tun, NULL);
+ RCU_INIT_POINTER(tfile->tun, NULL);
tfile->net = get_net(current->nsproxy->net_ns);
tfile->flags = 0;
tfile->ifindex = 0;
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index bd363b27e854..9ea4bfe5d318 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -625,6 +625,13 @@ static const struct usb_device_id products[] = {
.driver_info = 0,
},
+/* Novatel Expedite E371 - handled by qmi_wwan */
+{
+ USB_DEVICE_AND_INTERFACE_INFO(NOVATEL_VENDOR_ID, 0x9011, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = 0,
+},
+
/* AnyDATA ADU960S - handled by qmi_wwan */
{
USB_DEVICE_AND_INTERFACE_INFO(0x16d5, 0x650a, USB_CLASS_COMM,
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index d350d2795e10..549dbac710ed 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -73,6 +73,7 @@ static int cdc_ncm_setup(struct usbnet *dev)
u8 iface_no;
int err;
int eth_hlen;
+ u16 mbim_mtu;
u16 ntb_fmt_supported;
__le16 max_datagram_size;
@@ -252,6 +253,14 @@ out:
/* set MTU to max supported by the device if necessary */
if (dev->net->mtu > ctx->max_datagram_size - eth_hlen)
dev->net->mtu = ctx->max_datagram_size - eth_hlen;
+
+ /* do not exceed operater preferred MTU */
+ if (ctx->mbim_extended_desc) {
+ mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU);
+ if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu)
+ dev->net->mtu = mbim_mtu;
+ }
+
return 0;
}
@@ -390,6 +399,14 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
ctx->mbim_desc = (const struct usb_cdc_mbim_desc *)buf;
break;
+ case USB_CDC_MBIM_EXTENDED_TYPE:
+ if (buf[0] < sizeof(*(ctx->mbim_extended_desc)))
+ break;
+
+ ctx->mbim_extended_desc =
+ (const struct usb_cdc_mbim_extended_desc *)buf;
+ break;
+
default:
break;
}
diff --git a/drivers/net/usb/lg-vl600.c b/drivers/net/usb/lg-vl600.c
index acfcc32b323d..8f37efd2d2fb 100644
--- a/drivers/net/usb/lg-vl600.c
+++ b/drivers/net/usb/lg-vl600.c
@@ -210,7 +210,7 @@ static int vl600_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
* (0x86dd) so Linux can understand it.
*/
if ((buf->data[sizeof(*ethhdr)] & 0xf0) == 0x60)
- ethhdr->h_proto = __constant_htons(ETH_P_IPV6);
+ ethhdr->h_proto = htons(ETH_P_IPV6);
}
if (count) {
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 313cb6cd4848..e3458e3c44f1 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -500,6 +500,13 @@ static const struct usb_device_id products[] = {
USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&qmi_wwan_info,
},
+ { /* Novatel Expedite E371 */
+ USB_DEVICE_AND_INTERFACE_INFO(0x1410, 0x9011,
+ USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&qmi_wwan_info,
+ },
{ /* Dell Wireless 5800 (Novatel E362) */
USB_DEVICE_AND_INTERFACE_INFO(0x413C, 0x8195,
USB_CLASS_COMM,
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index adb12f349a61..18e12a3f7fc3 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -21,9 +21,10 @@
#include <linux/list.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
+#include <net/ip6_checksum.h>
/* Version Information */
-#define DRIVER_VERSION "v1.04.0 (2014/01/15)"
+#define DRIVER_VERSION "v1.06.0 (2014/03/03)"
#define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>"
#define DRIVER_DESC "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters"
#define MODULENAME "r8152"
@@ -59,9 +60,11 @@
#define PLA_TCR0 0xe610
#define PLA_TCR1 0xe612
#define PLA_TXFIFO_CTRL 0xe618
-#define PLA_RSTTELLY 0xe800
+#define PLA_RSTTALLY 0xe800
#define PLA_CR 0xe813
#define PLA_CRWECR 0xe81c
+#define PLA_CONFIG12 0xe81e /* CONFIG1, CONFIG2 */
+#define PLA_CONFIG34 0xe820 /* CONFIG3, CONFIG4 */
#define PLA_CONFIG5 0xe822
#define PLA_PHY_PWR 0xe84c
#define PLA_OOB_CTRL 0xe84f
@@ -69,7 +72,7 @@
#define PLA_MISC_0 0xe858
#define PLA_MISC_1 0xe85a
#define PLA_OCP_GPHY_BASE 0xe86c
-#define PLA_TELLYCNT 0xe890
+#define PLA_TALLYCNT 0xe890
#define PLA_SFF_STS_7 0xe8de
#define PLA_PHYSTATUS 0xe908
#define PLA_BP_BA 0xfc26
@@ -177,6 +180,9 @@
/* PLA_TCR1 */
#define VERSION_MASK 0x7cf0
+/* PLA_RSTTALLY */
+#define TALLY_RESET 0x0001
+
/* PLA_CR */
#define CR_RST 0x10
#define CR_RE 0x08
@@ -216,7 +222,14 @@
/* PAL_BDC_CR */
#define ALDPS_PROXY_MODE 0x0001
+/* PLA_CONFIG34 */
+#define LINK_ON_WAKE_EN 0x0010
+#define LINK_OFF_WAKE_EN 0x0008
+
/* PLA_CONFIG5 */
+#define BWF_EN 0x0040
+#define MWF_EN 0x0020
+#define UWF_EN 0x0010
#define LAN_WAKE_EN 0x0002
/* PLA_LED_FEATURE */
@@ -436,6 +449,9 @@ enum rtl8152_flags {
RTL8152_SET_RX_MODE,
WORK_ENABLE,
RTL8152_LINK_CHG,
+ SELECTIVE_SUSPEND,
+ PHY_RESET,
+ SCHEDULE_TASKLET,
};
/* Define these values to match your device */
@@ -449,11 +465,40 @@ enum rtl8152_flags {
#define MCU_TYPE_PLA 0x0100
#define MCU_TYPE_USB 0x0000
+#define REALTEK_USB_DEVICE(vend, prod) \
+ USB_DEVICE_INTERFACE_CLASS(vend, prod, USB_CLASS_VENDOR_SPEC)
+
+struct tally_counter {
+ __le64 tx_packets;
+ __le64 rx_packets;
+ __le64 tx_errors;
+ __le32 rx_errors;
+ __le16 rx_missed;
+ __le16 align_errors;
+ __le32 tx_one_collision;
+ __le32 tx_multi_collision;
+ __le64 rx_unicast;
+ __le64 rx_broadcast;
+ __le32 rx_multicast;
+ __le16 tx_aborted;
+ __le16 tx_underun;
+};
+
struct rx_desc {
__le32 opts1;
#define RX_LEN_MASK 0x7fff
+
__le32 opts2;
+#define RD_UDP_CS (1 << 23)
+#define RD_TCP_CS (1 << 22)
+#define RD_IPV6_CS (1 << 20)
+#define RD_IPV4_CS (1 << 19)
+
__le32 opts3;
+#define IPF (1 << 23) /* IP checksum fail */
+#define UDPF (1 << 22) /* UDP checksum fail */
+#define TCPF (1 << 21) /* TCP checksum fail */
+
__le32 opts4;
__le32 opts5;
__le32 opts6;
@@ -463,13 +508,21 @@ struct tx_desc {
__le32 opts1;
#define TX_FS (1 << 31) /* First segment of a packet */
#define TX_LS (1 << 30) /* Final segment of a packet */
-#define TX_LEN_MASK 0x3ffff
+#define GTSENDV4 (1 << 28)
+#define GTSENDV6 (1 << 27)
+#define GTTCPHO_SHIFT 18
+#define GTTCPHO_MAX 0x7fU
+#define TX_LEN_MAX 0x3ffffU
__le32 opts2;
#define UDP_CS (1 << 31) /* Calculate UDP/IP checksum */
#define TCP_CS (1 << 30) /* Calculate TCP/IP checksum */
#define IPV4_CS (1 << 29) /* Calculate IPv4 checksum */
#define IPV6_CS (1 << 28) /* Calculate IPv6 checksum */
+#define MSS_SHIFT 17
+#define MSS_MAX 0x7ffU
+#define TCPHO_SHIFT 17
+#define TCPHO_MAX 0x7ffU
};
struct r8152;
@@ -511,11 +564,13 @@ struct r8152 {
void (*init)(struct r8152 *);
int (*enable)(struct r8152 *);
void (*disable)(struct r8152 *);
+ void (*up)(struct r8152 *);
void (*down)(struct r8152 *);
void (*unload)(struct r8152 *);
} rtl_ops;
int intr_interval;
+ u32 saved_wolopts;
u32 msg_enable;
u32 tx_qlen;
u16 ocp_base;
@@ -534,12 +589,21 @@ enum rtl_version {
RTL_VER_MAX
};
+enum tx_csum_stat {
+ TX_CSUM_SUCCESS = 0,
+ TX_CSUM_TSO,
+ TX_CSUM_NONE
+};
+
/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
* The RTL chips use a 64 element hash table based on the Ethernet CRC.
*/
static const int multicast_filter_limit = 32;
static unsigned int rx_buf_sz = 16384;
+#define RTL_LIMITED_TSO_SIZE (rx_buf_sz - sizeof(struct tx_desc) - \
+ VLAN_ETH_HLEN - VLAN_HLEN)
+
static
int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
{
@@ -577,6 +641,7 @@ int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
value, index, tmp, size, 500);
kfree(tmp);
+
return ret;
}
@@ -862,11 +927,21 @@ static u16 sram_read(struct r8152 *tp, u16 addr)
static int read_mii_word(struct net_device *netdev, int phy_id, int reg)
{
struct r8152 *tp = netdev_priv(netdev);
+ int ret;
if (phy_id != R8152_PHY_ID)
return -EINVAL;
- return r8152_mdio_read(tp, reg);
+ ret = usb_autopm_get_interface(tp->intf);
+ if (ret < 0)
+ goto out;
+
+ ret = r8152_mdio_read(tp, reg);
+
+ usb_autopm_put_interface(tp->intf);
+
+out:
+ return ret;
}
static
@@ -877,7 +952,12 @@ void write_mii_word(struct net_device *netdev, int phy_id, int reg, int val)
if (phy_id != R8152_PHY_ID)
return;
+ if (usb_autopm_get_interface(tp->intf) < 0)
+ return;
+
r8152_mdio_write(tp, reg, val);
+
+ usb_autopm_put_interface(tp->intf);
}
static
@@ -886,11 +966,26 @@ int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags);
static inline void set_ethernet_addr(struct r8152 *tp)
{
struct net_device *dev = tp->netdev;
+ int ret;
u8 node_id[8] = {0};
- if (pla_ocp_read(tp, PLA_IDR, sizeof(node_id), node_id) < 0)
+ if (tp->version == RTL_VER_01)
+ ret = pla_ocp_read(tp, PLA_IDR, sizeof(node_id), node_id);
+ else
+ ret = pla_ocp_read(tp, PLA_BACKUP, sizeof(node_id), node_id);
+
+ if (ret < 0) {
netif_notice(tp, probe, dev, "inet addr fail\n");
- else {
+ } else {
+ if (tp->version != RTL_VER_01) {
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR,
+ CRWECR_CONFIG);
+ pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES,
+ sizeof(node_id), node_id);
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR,
+ CRWECR_NORAML);
+ }
+
memcpy(dev->dev_addr, node_id, dev->addr_len);
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
}
@@ -913,15 +1008,9 @@ static int rtl8152_set_mac_address(struct net_device *netdev, void *p)
return 0;
}
-static struct net_device_stats *rtl8152_get_stats(struct net_device *dev)
-{
- return &dev->stats;
-}
-
static void read_bulk_callback(struct urb *urb)
{
struct net_device *netdev;
- unsigned long flags;
int status = urb->status;
struct rx_agg *agg;
struct r8152 *tp;
@@ -948,14 +1037,16 @@ static void read_bulk_callback(struct urb *urb)
if (!netif_carrier_ok(netdev))
return;
+ usb_mark_last_busy(tp->udev);
+
switch (status) {
case 0:
if (urb->actual_length < ETH_ZLEN)
break;
- spin_lock_irqsave(&tp->rx_lock, flags);
+ spin_lock(&tp->rx_lock);
list_add_tail(&agg->list, &tp->rx_done);
- spin_unlock_irqrestore(&tp->rx_lock, flags);
+ spin_unlock(&tp->rx_lock);
tasklet_schedule(&tp->tl);
return;
case -ESHUTDOWN:
@@ -978,9 +1069,9 @@ static void read_bulk_callback(struct urb *urb)
if (result == -ENODEV) {
netif_device_detach(tp->netdev);
} else if (result) {
- spin_lock_irqsave(&tp->rx_lock, flags);
+ spin_lock(&tp->rx_lock);
list_add_tail(&agg->list, &tp->rx_done);
- spin_unlock_irqrestore(&tp->rx_lock, flags);
+ spin_unlock(&tp->rx_lock);
tasklet_schedule(&tp->tl);
}
}
@@ -988,7 +1079,7 @@ static void read_bulk_callback(struct urb *urb)
static void write_bulk_callback(struct urb *urb)
{
struct net_device_stats *stats;
- unsigned long flags;
+ struct net_device *netdev;
struct tx_agg *agg;
struct r8152 *tp;
int status = urb->status;
@@ -1001,21 +1092,24 @@ static void write_bulk_callback(struct urb *urb)
if (!tp)
return;
- stats = rtl8152_get_stats(tp->netdev);
+ netdev = tp->netdev;
+ stats = &netdev->stats;
if (status) {
if (net_ratelimit())
- netdev_warn(tp->netdev, "Tx status %d\n", status);
+ netdev_warn(netdev, "Tx status %d\n", status);
stats->tx_errors += agg->skb_num;
} else {
stats->tx_packets += agg->skb_num;
stats->tx_bytes += agg->skb_len;
}
- spin_lock_irqsave(&tp->tx_lock, flags);
+ spin_lock(&tp->tx_lock);
list_add_tail(&agg->list, &tp->tx_free);
- spin_unlock_irqrestore(&tp->tx_lock, flags);
+ spin_unlock(&tp->tx_lock);
- if (!netif_carrier_ok(tp->netdev))
+ usb_autopm_put_interface_async(tp->intf);
+
+ if (!netif_carrier_ok(netdev))
return;
if (!test_bit(WORK_ENABLE, &tp->flags))
@@ -1220,6 +1314,9 @@ static struct tx_agg *r8152_get_tx_agg(struct r8152 *tp)
struct tx_agg *agg = NULL;
unsigned long flags;
+ if (list_empty(&tp->tx_free))
+ return NULL;
+
spin_lock_irqsave(&tp->tx_lock, flags);
if (!list_empty(&tp->tx_free)) {
struct list_head *cursor;
@@ -1233,24 +1330,138 @@ static struct tx_agg *r8152_get_tx_agg(struct r8152 *tp)
return agg;
}
-static void
-r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, struct sk_buff *skb)
+static inline __be16 get_protocol(struct sk_buff *skb)
+{
+ __be16 protocol;
+
+ if (skb->protocol == htons(ETH_P_8021Q))
+ protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
+ else
+ protocol = skb->protocol;
+
+ return protocol;
+}
+
+/*
+ * r8152_csum_workaround()
+ * The hw limites the value the transport offset. When the offset is out of the
+ * range, calculate the checksum by sw.
+ */
+static void r8152_csum_workaround(struct r8152 *tp, struct sk_buff *skb,
+ struct sk_buff_head *list)
+{
+ if (skb_shinfo(skb)->gso_size) {
+ netdev_features_t features = tp->netdev->features;
+ struct sk_buff_head seg_list;
+ struct sk_buff *segs, *nskb;
+
+ features &= ~(NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO);
+ segs = skb_gso_segment(skb, features);
+ if (IS_ERR(segs) || !segs)
+ goto drop;
+
+ __skb_queue_head_init(&seg_list);
+
+ do {
+ nskb = segs;
+ segs = segs->next;
+ nskb->next = NULL;
+ __skb_queue_tail(&seg_list, nskb);
+ } while (segs);
+
+ skb_queue_splice(&seg_list, list);
+ dev_kfree_skb(skb);
+ } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ if (skb_checksum_help(skb) < 0)
+ goto drop;
+
+ __skb_queue_head(list, skb);
+ } else {
+ struct net_device_stats *stats;
+
+drop:
+ stats = &tp->netdev->stats;
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ }
+}
+
+/*
+ * msdn_giant_send_check()
+ * According to the document of microsoft, the TCP Pseudo Header excludes the
+ * packet length for IPv6 TCP large packets.
+ */
+static int msdn_giant_send_check(struct sk_buff *skb)
+{
+ const struct ipv6hdr *ipv6h;
+ struct tcphdr *th;
+ int ret;
+
+ ret = skb_cow_head(skb, 0);
+ if (ret)
+ return ret;
+
+ ipv6h = ipv6_hdr(skb);
+ th = tcp_hdr(skb);
+
+ th->check = 0;
+ th->check = ~tcp_v6_check(0, &ipv6h->saddr, &ipv6h->daddr, 0);
+
+ return ret;
+}
+
+static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc,
+ struct sk_buff *skb, u32 len, u32 transport_offset)
{
- memset(desc, 0, sizeof(*desc));
+ u32 mss = skb_shinfo(skb)->gso_size;
+ u32 opts1, opts2 = 0;
+ int ret = TX_CSUM_SUCCESS;
+
+ WARN_ON_ONCE(len > TX_LEN_MAX);
+
+ opts1 = len | TX_FS | TX_LS;
+
+ if (mss) {
+ if (transport_offset > GTTCPHO_MAX) {
+ netif_warn(tp, tx_err, tp->netdev,
+ "Invalid transport offset 0x%x for TSO\n",
+ transport_offset);
+ ret = TX_CSUM_TSO;
+ goto unavailable;
+ }
- desc->opts1 = cpu_to_le32((skb->len & TX_LEN_MASK) | TX_FS | TX_LS);
+ switch (get_protocol(skb)) {
+ case htons(ETH_P_IP):
+ opts1 |= GTSENDV4;
+ break;
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- __be16 protocol;
+ case htons(ETH_P_IPV6):
+ if (msdn_giant_send_check(skb)) {
+ ret = TX_CSUM_TSO;
+ goto unavailable;
+ }
+ opts1 |= GTSENDV6;
+ break;
+
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ opts1 |= transport_offset << GTTCPHO_SHIFT;
+ opts2 |= min(mss, MSS_MAX) << MSS_SHIFT;
+ } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
u8 ip_protocol;
- u32 opts2 = 0;
- if (skb->protocol == htons(ETH_P_8021Q))
- protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
- else
- protocol = skb->protocol;
+ if (transport_offset > TCPHO_MAX) {
+ netif_warn(tp, tx_err, tp->netdev,
+ "Invalid transport offset 0x%x\n",
+ transport_offset);
+ ret = TX_CSUM_NONE;
+ goto unavailable;
+ }
- switch (protocol) {
+ switch (get_protocol(skb)) {
case htons(ETH_P_IP):
opts2 |= IPV4_CS;
ip_protocol = ip_hdr(skb)->protocol;
@@ -1266,24 +1477,34 @@ r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, struct sk_buff *skb)
break;
}
- if (ip_protocol == IPPROTO_TCP) {
+ if (ip_protocol == IPPROTO_TCP)
opts2 |= TCP_CS;
- opts2 |= (skb_transport_offset(skb) & 0x7fff) << 17;
- } else if (ip_protocol == IPPROTO_UDP) {
+ else if (ip_protocol == IPPROTO_UDP)
opts2 |= UDP_CS;
- } else {
+ else
WARN_ON_ONCE(1);
- }
- desc->opts2 = cpu_to_le32(opts2);
+ opts2 |= transport_offset << TCPHO_SHIFT;
}
+
+ desc->opts2 = cpu_to_le32(opts2);
+ desc->opts1 = cpu_to_le32(opts1);
+
+unavailable:
+ return ret;
}
static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
{
- int remain;
+ struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue;
+ int remain, ret;
u8 *tx_data;
+ __skb_queue_head_init(&skb_head);
+ spin_lock(&tx_queue->lock);
+ skb_queue_splice_init(tx_queue, &skb_head);
+ spin_unlock(&tx_queue->lock);
+
tx_data = agg->head;
agg->skb_num = agg->skb_len = 0;
remain = rx_buf_sz;
@@ -1292,32 +1513,56 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
struct tx_desc *tx_desc;
struct sk_buff *skb;
unsigned int len;
+ u32 offset;
- skb = skb_dequeue(&tp->tx_queue);
+ skb = __skb_dequeue(&skb_head);
if (!skb)
break;
- remain -= sizeof(*tx_desc);
- len = skb->len;
- if (remain < len) {
- skb_queue_head(&tp->tx_queue, skb);
+ len = skb->len + sizeof(*tx_desc);
+
+ if (len > remain) {
+ __skb_queue_head(&skb_head, skb);
break;
}
tx_data = tx_agg_align(tx_data);
tx_desc = (struct tx_desc *)tx_data;
+
+ offset = (u32)skb_transport_offset(skb);
+
+ if (r8152_tx_csum(tp, tx_desc, skb, skb->len, offset)) {
+ r8152_csum_workaround(tp, skb, &skb_head);
+ continue;
+ }
+
tx_data += sizeof(*tx_desc);
- r8152_tx_csum(tp, tx_desc, skb);
- memcpy(tx_data, skb->data, len);
- agg->skb_num++;
+ len = skb->len;
+ if (skb_copy_bits(skb, 0, tx_data, len) < 0) {
+ struct net_device_stats *stats = &tp->netdev->stats;
+
+ stats->tx_dropped++;
+ dev_kfree_skb_any(skb);
+ tx_data -= sizeof(*tx_desc);
+ continue;
+ }
+
+ tx_data += len;
agg->skb_len += len;
+ agg->skb_num++;
+
dev_kfree_skb_any(skb);
- tx_data += len;
remain = rx_buf_sz - (int)(tx_agg_align(tx_data) - agg->head);
}
+ if (!skb_queue_empty(&skb_head)) {
+ spin_lock(&tx_queue->lock);
+ skb_queue_splice(&skb_head, tx_queue);
+ spin_unlock(&tx_queue->lock);
+ }
+
netif_tx_lock(tp->netdev);
if (netif_queue_stopped(tp->netdev) &&
@@ -1326,20 +1571,67 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
netif_tx_unlock(tp->netdev);
+ ret = usb_autopm_get_interface_async(tp->intf);
+ if (ret < 0)
+ goto out_tx_fill;
+
usb_fill_bulk_urb(agg->urb, tp->udev, usb_sndbulkpipe(tp->udev, 2),
agg->head, (int)(tx_data - (u8 *)agg->head),
(usb_complete_t)write_bulk_callback, agg);
- return usb_submit_urb(agg->urb, GFP_ATOMIC);
+ ret = usb_submit_urb(agg->urb, GFP_ATOMIC);
+ if (ret < 0)
+ usb_autopm_put_interface_async(tp->intf);
+
+out_tx_fill:
+ return ret;
+}
+
+static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc)
+{
+ u8 checksum = CHECKSUM_NONE;
+ u32 opts2, opts3;
+
+ if (tp->version == RTL_VER_01)
+ goto return_result;
+
+ opts2 = le32_to_cpu(rx_desc->opts2);
+ opts3 = le32_to_cpu(rx_desc->opts3);
+
+ if (opts2 & RD_IPV4_CS) {
+ if (opts3 & IPF)
+ checksum = CHECKSUM_NONE;
+ else if ((opts2 & RD_UDP_CS) && (opts3 & UDPF))
+ checksum = CHECKSUM_NONE;
+ else if ((opts2 & RD_TCP_CS) && (opts3 & TCPF))
+ checksum = CHECKSUM_NONE;
+ else
+ checksum = CHECKSUM_UNNECESSARY;
+ } else if (RD_IPV6_CS) {
+ if ((opts2 & RD_UDP_CS) && !(opts3 & UDPF))
+ checksum = CHECKSUM_UNNECESSARY;
+ else if ((opts2 & RD_TCP_CS) && !(opts3 & TCPF))
+ checksum = CHECKSUM_UNNECESSARY;
+ }
+
+return_result:
+ return checksum;
}
static void rx_bottom(struct r8152 *tp)
{
unsigned long flags;
- struct list_head *cursor, *next;
+ struct list_head *cursor, *next, rx_queue;
+ if (list_empty(&tp->rx_done))
+ return;
+
+ INIT_LIST_HEAD(&rx_queue);
spin_lock_irqsave(&tp->rx_lock, flags);
- list_for_each_safe(cursor, next, &tp->rx_done) {
+ list_splice_init(&tp->rx_done, &rx_queue);
+ spin_unlock_irqrestore(&tp->rx_lock, flags);
+
+ list_for_each_safe(cursor, next, &rx_queue) {
struct rx_desc *rx_desc;
struct rx_agg *agg;
int len_used = 0;
@@ -1348,7 +1640,6 @@ static void rx_bottom(struct r8152 *tp)
int ret;
list_del_init(cursor);
- spin_unlock_irqrestore(&tp->rx_lock, flags);
agg = list_entry(cursor, struct rx_agg, list);
urb = agg->urb;
@@ -1361,7 +1652,7 @@ static void rx_bottom(struct r8152 *tp)
while (urb->actual_length > len_used) {
struct net_device *netdev = tp->netdev;
- struct net_device_stats *stats;
+ struct net_device_stats *stats = &netdev->stats;
unsigned int pkt_len;
struct sk_buff *skb;
@@ -1373,23 +1664,24 @@ static void rx_bottom(struct r8152 *tp)
if (urb->actual_length < len_used)
break;
- stats = rtl8152_get_stats(netdev);
-
pkt_len -= CRC_SIZE;
rx_data += sizeof(struct rx_desc);
skb = netdev_alloc_skb_ip_align(netdev, pkt_len);
if (!skb) {
stats->rx_dropped++;
- break;
+ goto find_next_rx;
}
+
+ skb->ip_summed = r8152_rx_csum(tp, rx_desc);
memcpy(skb->data, rx_data, pkt_len);
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, netdev);
- netif_rx(skb);
+ netif_receive_skb(skb);
stats->rx_packets++;
stats->rx_bytes += pkt_len;
+find_next_rx:
rx_data = rx_agg_align(rx_data + pkt_len + CRC_SIZE);
rx_desc = (struct rx_desc *)rx_data;
len_used = (int)(rx_data - (u8 *)agg->head);
@@ -1398,13 +1690,13 @@ static void rx_bottom(struct r8152 *tp)
submit:
ret = r8152_submit_rx(tp, agg, GFP_ATOMIC);
- spin_lock_irqsave(&tp->rx_lock, flags);
if (ret && ret != -ENODEV) {
- list_add_tail(&agg->list, next);
+ spin_lock_irqsave(&tp->rx_lock, flags);
+ list_add_tail(&agg->list, &tp->rx_done);
+ spin_unlock_irqrestore(&tp->rx_lock, flags);
tasklet_schedule(&tp->tl);
}
}
- spin_unlock_irqrestore(&tp->rx_lock, flags);
}
static void tx_bottom(struct r8152 *tp)
@@ -1423,19 +1715,18 @@ static void tx_bottom(struct r8152 *tp)
res = r8152_tx_agg_fill(tp, agg);
if (res) {
- struct net_device_stats *stats;
- struct net_device *netdev;
- unsigned long flags;
-
- netdev = tp->netdev;
- stats = rtl8152_get_stats(netdev);
+ struct net_device *netdev = tp->netdev;
if (res == -ENODEV) {
netif_device_detach(netdev);
} else {
+ struct net_device_stats *stats = &netdev->stats;
+ unsigned long flags;
+
netif_warn(tp, tx_err, netdev,
"failed tx_urb %d\n", res);
stats->tx_dropped += agg->skb_num;
+
spin_lock_irqsave(&tp->tx_lock, flags);
list_add_tail(&agg->list, &tp->tx_free);
spin_unlock_irqrestore(&tp->tx_lock, flags);
@@ -1475,6 +1766,26 @@ int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags)
return usb_submit_urb(agg->urb, mem_flags);
}
+static void rtl_drop_queued_tx(struct r8152 *tp)
+{
+ struct net_device_stats *stats = &tp->netdev->stats;
+ struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue;
+ struct sk_buff *skb;
+
+ if (skb_queue_empty(tx_queue))
+ return;
+
+ __skb_queue_head_init(&skb_head);
+ spin_lock_bh(&tx_queue->lock);
+ skb_queue_splice_init(tx_queue, &skb_head);
+ spin_unlock_bh(&tx_queue->lock);
+
+ while ((skb = __skb_dequeue(&skb_head))) {
+ dev_kfree_skb(skb);
+ stats->tx_dropped++;
+ }
+}
+
static void rtl8152_tx_timeout(struct net_device *netdev)
{
struct r8152 *tp = netdev_priv(netdev);
@@ -1538,7 +1849,7 @@ static void _rtl8152_set_rx_mode(struct net_device *netdev)
}
static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb,
- struct net_device *netdev)
+ struct net_device *netdev)
{
struct r8152 *tp = netdev_priv(netdev);
@@ -1546,13 +1857,17 @@ static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb,
skb_queue_tail(&tp->tx_queue, skb);
- if (list_empty(&tp->tx_free) &&
- skb_queue_len(&tp->tx_queue) > tp->tx_qlen)
+ if (!list_empty(&tp->tx_free)) {
+ if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
+ set_bit(SCHEDULE_TASKLET, &tp->flags);
+ schedule_delayed_work(&tp->schedule, 0);
+ } else {
+ usb_mark_last_busy(tp->udev);
+ tasklet_schedule(&tp->tl);
+ }
+ } else if (skb_queue_len(&tp->tx_queue) > tp->tx_qlen)
netif_stop_queue(netdev);
- if (!list_empty(&tp->tx_free))
- tasklet_schedule(&tp->tl);
-
return NETDEV_TX_OK;
}
@@ -1610,6 +1925,18 @@ static void rtl_set_eee_plus(struct r8152 *tp)
}
}
+static void rxdy_gated_en(struct r8152 *tp, bool enable)
+{
+ u32 ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
+ if (enable)
+ ocp_data |= RXDY_GATED_EN;
+ else
+ ocp_data &= ~RXDY_GATED_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
+}
+
static int rtl_enable(struct r8152 *tp)
{
u32 ocp_data;
@@ -1621,9 +1948,7 @@ static int rtl_enable(struct r8152 *tp)
ocp_data |= CR_RE | CR_TE;
ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data);
- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
- ocp_data &= ~RXDY_GATED_EN;
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
+ rxdy_gated_en(tp, false);
INIT_LIST_HEAD(&tp->rx_done);
ret = 0;
@@ -1678,8 +2003,6 @@ static int rtl8153_enable(struct r8152 *tp)
static void rtl8152_disable(struct r8152 *tp)
{
- struct net_device_stats *stats = rtl8152_get_stats(tp->netdev);
- struct sk_buff *skb;
u32 ocp_data;
int i;
@@ -1687,17 +2010,12 @@ static void rtl8152_disable(struct r8152 *tp)
ocp_data &= ~RCR_ACPT_ALL;
ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
- while ((skb = skb_dequeue(&tp->tx_queue))) {
- dev_kfree_skb(skb);
- stats->tx_dropped++;
- }
+ rtl_drop_queued_tx(tp);
for (i = 0; i < RTL8152_MAX_TX; i++)
usb_kill_urb(tp->tx_info[i].urb);
- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
- ocp_data |= RXDY_GATED_EN;
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
+ rxdy_gated_en(tp, true);
for (i = 0; i < 1000; i++) {
ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
@@ -1718,18 +2036,209 @@ static void rtl8152_disable(struct r8152 *tp)
rtl8152_nic_reset(tp);
}
+static void r8152_power_cut_en(struct r8152 *tp, bool enable)
+{
+ u32 ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL);
+ if (enable)
+ ocp_data |= POWER_CUT;
+ else
+ ocp_data &= ~POWER_CUT;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS);
+ ocp_data &= ~RESUME_INDICATE;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data);
+}
+
+#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)
+
+static u32 __rtl_get_wol(struct r8152 *tp)
+{
+ u32 ocp_data;
+ u32 wolopts = 0;
+
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5);
+ if (!(ocp_data & LAN_WAKE_EN))
+ return 0;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34);
+ if (ocp_data & LINK_ON_WAKE_EN)
+ wolopts |= WAKE_PHY;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5);
+ if (ocp_data & UWF_EN)
+ wolopts |= WAKE_UCAST;
+ if (ocp_data & BWF_EN)
+ wolopts |= WAKE_BCAST;
+ if (ocp_data & MWF_EN)
+ wolopts |= WAKE_MCAST;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL);
+ if (ocp_data & MAGIC_EN)
+ wolopts |= WAKE_MAGIC;
+
+ return wolopts;
+}
+
+static void __rtl_set_wol(struct r8152 *tp, u32 wolopts)
+{
+ u32 ocp_data;
+
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34);
+ ocp_data &= ~LINK_ON_WAKE_EN;
+ if (wolopts & WAKE_PHY)
+ ocp_data |= LINK_ON_WAKE_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5);
+ ocp_data &= ~(UWF_EN | BWF_EN | MWF_EN | LAN_WAKE_EN);
+ if (wolopts & WAKE_UCAST)
+ ocp_data |= UWF_EN;
+ if (wolopts & WAKE_BCAST)
+ ocp_data |= BWF_EN;
+ if (wolopts & WAKE_MCAST)
+ ocp_data |= MWF_EN;
+ if (wolopts & WAKE_ANY)
+ ocp_data |= LAN_WAKE_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG5, ocp_data);
+
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL);
+ ocp_data &= ~MAGIC_EN;
+ if (wolopts & WAKE_MAGIC)
+ ocp_data |= MAGIC_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data);
+
+ if (wolopts & WAKE_ANY)
+ device_set_wakeup_enable(&tp->udev->dev, true);
+ else
+ device_set_wakeup_enable(&tp->udev->dev, false);
+}
+
+static void rtl_runtime_suspend_enable(struct r8152 *tp, bool enable)
+{
+ if (enable) {
+ u32 ocp_data;
+
+ __rtl_set_wol(tp, WAKE_ANY);
+
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34);
+ ocp_data |= LINK_OFF_WAKE_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data);
+
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
+ } else {
+ __rtl_set_wol(tp, tp->saved_wolopts);
+ }
+}
+
+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 */
+ if (data & BMCR_RESET)
+ return;
+
+ data |= BMCR_RESET;
+ r8152_mdio_write(tp, MII_BMCR, data);
+
+ for (i = 0; i < 50; i++) {
+ msleep(20);
+ if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0)
+ break;
+ }
+}
+
+static void rtl_clear_bp(struct r8152 *tp)
+{
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_0, 0);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_2, 0);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_4, 0);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_6, 0);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_0, 0);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_2, 0);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_4, 0);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_6, 0);
+ mdelay(3);
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_BA, 0);
+ ocp_write_word(tp, MCU_TYPE_USB, USB_BP_BA, 0);
+}
+
+static void r8153_clear_bp(struct r8152 *tp)
+{
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_BP_EN, 0);
+ ocp_write_byte(tp, MCU_TYPE_USB, USB_BP_EN, 0);
+ rtl_clear_bp(tp);
+}
+
+static void r8153_teredo_off(struct r8152 *tp)
+{
+ u32 ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG);
+ ocp_data &= ~(TEREDO_SEL | TEREDO_RS_EVENT_MASK | OOB_TEREDO_EN);
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data);
+
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE);
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_REALWOW_TIMER, 0);
+ 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)
+{
+ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS |
+ LINKENA | DIS_SDSAVE);
+}
+
+static void r8152b_hw_phy_cfg(struct r8152 *tp)
+{
+ u16 data;
+
+ data = r8152_mdio_read(tp, MII_BMCR);
+ if (data & BMCR_PDOWN) {
+ data &= ~BMCR_PDOWN;
+ r8152_mdio_write(tp, MII_BMCR, data);
+ }
+
+ r8152b_disable_aldps(tp);
+
+ rtl_clear_bp(tp);
+
+ r8152b_enable_aldps(tp);
+ set_bit(PHY_RESET, &tp->flags);
+}
+
static void r8152b_exit_oob(struct r8152 *tp)
{
- u32 ocp_data;
- int i;
+ u32 ocp_data;
+ int i;
ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
ocp_data &= ~RCR_ACPT_ALL;
ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
- ocp_data |= RXDY_GATED_EN;
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
+ rxdy_gated_en(tp, true);
+ r8153_teredo_off(tp);
+ r8152b_hw_phy_cfg(tp);
ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, 0x00);
@@ -1835,10 +2344,6 @@ static void r8152b_enter_oob(struct r8152 *tp)
ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS);
- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL);
- ocp_data |= MAGIC_EN;
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data);
-
ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR);
ocp_data |= CPCR_RX_VLAN;
ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data);
@@ -1851,36 +2356,26 @@ static void r8152b_enter_oob(struct r8152 *tp)
ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB;
ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
- ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5, LAN_WAKE_EN);
-
- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
- ocp_data &= ~RXDY_GATED_EN;
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
+ rxdy_gated_en(tp, false);
ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
ocp_data |= RCR_APM | RCR_AM | RCR_AB;
ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
}
-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)
-{
- ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS |
- LINKENA | DIS_SDSAVE);
-}
-
static void r8153_hw_phy_cfg(struct r8152 *tp)
{
u32 ocp_data;
u16 data;
ocp_reg_write(tp, OCP_ADC_CFG, CKADSEL_L | ADC_EN | EN_EMI_L);
- r8152_mdio_write(tp, MII_BMCR, BMCR_ANENABLE);
+ data = r8152_mdio_read(tp, MII_BMCR);
+ if (data & BMCR_PDOWN) {
+ data &= ~BMCR_PDOWN;
+ r8152_mdio_write(tp, MII_BMCR, data);
+ }
+
+ r8153_clear_bp(tp);
if (tp->version == RTL_VER_03) {
data = ocp_reg_read(tp, OCP_EEE_CFG);
@@ -1916,9 +2411,11 @@ static void r8153_hw_phy_cfg(struct r8152 *tp)
data = sram_read(tp, SRAM_10M_AMP2);
data |= AMP_DN;
sram_write(tp, SRAM_10M_AMP2, data);
+
+ set_bit(PHY_RESET, &tp->flags);
}
-static void r8153_u1u2en(struct r8152 *tp, int enable)
+static void r8153_u1u2en(struct r8152 *tp, bool enable)
{
u8 u1u2[8];
@@ -1930,7 +2427,7 @@ static void r8153_u1u2en(struct r8152 *tp, int enable)
usb_ocp_write(tp, USB_TOLERANCE, BYTE_EN_SIX_BYTES, sizeof(u1u2), u1u2);
}
-static void r8153_u2p3en(struct r8152 *tp, int enable)
+static void r8153_u2p3en(struct r8152 *tp, bool enable)
{
u32 ocp_data;
@@ -1942,7 +2439,7 @@ static void r8153_u2p3en(struct r8152 *tp, int enable)
ocp_write_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL, ocp_data);
}
-static void r8153_power_cut_en(struct r8152 *tp, int enable)
+static void r8153_power_cut_en(struct r8152 *tp, bool enable)
{
u32 ocp_data;
@@ -1958,28 +2455,12 @@ static void r8153_power_cut_en(struct r8152 *tp, int enable)
ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
}
-static void r8153_teredo_off(struct r8152 *tp)
-{
- u32 ocp_data;
-
- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG);
- ocp_data &= ~(TEREDO_SEL | TEREDO_RS_EVENT_MASK | OOB_TEREDO_EN);
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data);
-
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE);
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_REALWOW_TIMER, 0);
- ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TEREDO_TIMER, 0);
-}
-
static void r8153_first_init(struct r8152 *tp)
{
u32 ocp_data;
int i;
- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
- ocp_data |= RXDY_GATED_EN;
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
-
+ rxdy_gated_en(tp, true);
r8153_teredo_off(tp);
ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
@@ -2072,10 +2553,6 @@ static void r8153_enter_oob(struct r8152 *tp)
ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS);
- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL);
- ocp_data |= MAGIC_EN;
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data);
-
ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG);
ocp_data &= ~TEREDO_WAKE_MASK;
ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data);
@@ -2092,11 +2569,7 @@ static void r8153_enter_oob(struct r8152 *tp)
ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB;
ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
- ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5, LAN_WAKE_EN);
-
- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
- ocp_data &= ~RXDY_GATED_EN;
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
+ rxdy_gated_en(tp, false);
ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
ocp_data |= RCR_APM | RCR_AM | RCR_AB;
@@ -2187,12 +2660,26 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex)
bmcr = BMCR_ANENABLE | BMCR_ANRESTART;
}
+ if (test_bit(PHY_RESET, &tp->flags))
+ bmcr |= BMCR_RESET;
+
if (tp->mii.supports_gmii)
r8152_mdio_write(tp, MII_CTRL1000, gbcr);
r8152_mdio_write(tp, MII_ADVERTISE, anar);
r8152_mdio_write(tp, MII_BMCR, bmcr);
+ if (test_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)
+ break;
+ }
+ }
+
out:
return ret;
@@ -2200,12 +2687,7 @@ out:
static void rtl8152_down(struct r8152 *tp)
{
- u32 ocp_data;
-
- ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL);
- ocp_data &= ~POWER_CUT;
- ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data);
-
+ r8152_power_cut_en(tp, false);
r8152b_disable_aldps(tp);
r8152b_enter_oob(tp);
r8152b_enable_aldps(tp);
@@ -2213,8 +2695,8 @@ static void rtl8152_down(struct r8152 *tp)
static void rtl8153_down(struct r8152 *tp)
{
- r8153_u1u2en(tp, 0);
- r8153_power_cut_en(tp, 0);
+ r8153_u1u2en(tp, false);
+ r8153_power_cut_en(tp, false);
r8153_disable_aldps(tp);
r8153_enter_oob(tp);
r8153_enable_aldps(tp);
@@ -2249,6 +2731,9 @@ static void rtl_work_func_t(struct work_struct *work)
{
struct r8152 *tp = container_of(work, struct r8152, schedule.work);
+ if (usb_autopm_get_interface(tp->intf) < 0)
+ return;
+
if (!test_bit(WORK_ENABLE, &tp->flags))
goto out1;
@@ -2261,8 +2746,17 @@ static void rtl_work_func_t(struct work_struct *work)
if (test_bit(RTL8152_SET_RX_MODE, &tp->flags))
_rtl8152_set_rx_mode(tp->netdev);
+ if (test_bit(SCHEDULE_TASKLET, &tp->flags) &&
+ (tp->speed & LINK_STATUS)) {
+ clear_bit(SCHEDULE_TASKLET, &tp->flags);
+ tasklet_schedule(&tp->tl);
+ }
+
+ if (test_bit(PHY_RESET, &tp->flags))
+ rtl_phy_reset(tp);
+
out1:
- return;
+ usb_autopm_put_interface(tp->intf);
}
static int rtl8152_open(struct net_device *netdev)
@@ -2270,6 +2764,27 @@ static int rtl8152_open(struct net_device *netdev)
struct r8152 *tp = netdev_priv(netdev);
int res = 0;
+ res = alloc_all_mem(tp);
+ if (res)
+ goto out;
+
+ res = usb_autopm_get_interface(tp->intf);
+ if (res < 0) {
+ free_all_mem(tp);
+ goto out;
+ }
+
+ /* 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);
+ if (tp->speed & LINK_STATUS)
+ tp->rtl_ops.disable(tp);
+ }
+
+ tp->rtl_ops.up(tp);
+
rtl8152_set_speed(tp, AUTONEG_ENABLE,
tp->mii.supports_gmii ? SPEED_1000 : SPEED_100,
DUPLEX_FULL);
@@ -2277,15 +2792,19 @@ static int rtl8152_open(struct net_device *netdev)
netif_carrier_off(netdev);
netif_start_queue(netdev);
set_bit(WORK_ENABLE, &tp->flags);
+
res = usb_submit_urb(tp->intr_urb, GFP_KERNEL);
if (res) {
if (res == -ENODEV)
netif_device_detach(tp->netdev);
netif_warn(tp, ifup, netdev, "intr_urb submit failed: %d\n",
res);
+ free_all_mem(tp);
}
+ usb_autopm_put_interface(tp->intf);
+out:
return res;
}
@@ -2298,33 +2817,30 @@ static int rtl8152_close(struct net_device *netdev)
usb_kill_urb(tp->intr_urb);
cancel_delayed_work_sync(&tp->schedule);
netif_stop_queue(netdev);
- tasklet_disable(&tp->tl);
- tp->rtl_ops.disable(tp);
- tasklet_enable(&tp->tl);
- return res;
-}
+ res = usb_autopm_get_interface(tp->intf);
+ if (res < 0) {
+ rtl_drop_queued_tx(tp);
+ } else {
+ /*
+ * The autosuspend may have been enabled and wouldn't
+ * be disable when autoresume occurs, because the
+ * netif_running() would be false.
+ */
+ if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
+ rtl_runtime_suspend_enable(tp, false);
+ clear_bit(SELECTIVE_SUSPEND, &tp->flags);
+ }
-static void rtl_clear_bp(struct r8152 *tp)
-{
- ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_0, 0);
- ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_2, 0);
- ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_4, 0);
- ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_6, 0);
- ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_0, 0);
- ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_2, 0);
- ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_4, 0);
- ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_6, 0);
- mdelay(3);
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_BA, 0);
- ocp_write_word(tp, MCU_TYPE_USB, USB_BP_BA, 0);
-}
+ tasklet_disable(&tp->tl);
+ tp->rtl_ops.down(tp);
+ tasklet_enable(&tp->tl);
+ usb_autopm_put_interface(tp->intf);
+ }
-static void r8153_clear_bp(struct r8152 *tp)
-{
- ocp_write_byte(tp, MCU_TYPE_PLA, PLA_BP_EN, 0);
- ocp_write_byte(tp, MCU_TYPE_USB, USB_BP_EN, 0);
- rtl_clear_bp(tp);
+ free_all_mem(tp);
+
+ return res;
}
static void r8152b_enable_eee(struct r8152 *tp)
@@ -2375,18 +2891,18 @@ static void r8152b_enable_fc(struct r8152 *tp)
r8152_mdio_write(tp, MII_ADVERTISE, anar);
}
-static void r8152b_hw_phy_cfg(struct r8152 *tp)
+static void rtl_tally_reset(struct r8152 *tp)
{
- r8152_mdio_write(tp, MII_BMCR, BMCR_ANENABLE);
- r8152b_disable_aldps(tp);
+ u32 ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY);
+ ocp_data |= TALLY_RESET;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data);
}
static void r8152b_init(struct r8152 *tp)
{
u32 ocp_data;
- int i;
-
- rtl_clear_bp(tp);
if (tp->version == RTL_VER_01) {
ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE);
@@ -2394,17 +2910,7 @@ static void r8152b_init(struct r8152 *tp)
ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data);
}
- r8152b_hw_phy_cfg(tp);
-
- ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL);
- ocp_data &= ~POWER_CUT;
- ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data);
-
- ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS);
- ocp_data &= ~RESUME_INDICATE;
- ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data);
-
- r8152b_exit_oob(tp);
+ r8152_power_cut_en(tp, false);
ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR);
ocp_data |= TX_10M_IDLE_EN | PFM_PWM_SWITCH;
@@ -2420,14 +2926,7 @@ static void r8152b_init(struct r8152 *tp)
r8152b_enable_eee(tp);
r8152b_enable_aldps(tp);
r8152b_enable_fc(tp);
-
- r8152_mdio_write(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE |
- BMCR_ANRESTART);
- for (i = 0; i < 100; i++) {
- udelay(100);
- if (!(r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET))
- break;
- }
+ rtl_tally_reset(tp);
/* enable rx aggregation */
ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
@@ -2440,7 +2939,7 @@ static void r8153_init(struct r8152 *tp)
u32 ocp_data;
int i;
- r8153_u1u2en(tp, 0);
+ r8153_u1u2en(tp, false);
for (i = 0; i < 500; i++) {
if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
@@ -2456,14 +2955,12 @@ static void r8153_init(struct r8152 *tp)
msleep(20);
}
- r8153_u2p3en(tp, 0);
+ r8153_u2p3en(tp, false);
ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL);
ocp_data &= ~TIMER11_EN;
ocp_write_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL, ocp_data);
- r8153_clear_bp(tp);
-
ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE);
ocp_data &= ~LED_MODE_MASK;
ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data);
@@ -2481,10 +2978,8 @@ static void r8153_init(struct r8152 *tp)
ocp_data |= SEN_VAL_NORMAL | SEL_RXIDLE;
ocp_write_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2, ocp_data);
- r8153_power_cut_en(tp, 0);
- r8153_u1u2en(tp, 1);
-
- r8153_first_init(tp);
+ r8153_power_cut_en(tp, false);
+ r8153_u1u2en(tp, true);
ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ALDPS_SPDWN_RATIO);
ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, EEE_SPDWN_RATIO);
@@ -2499,26 +2994,31 @@ static void r8153_init(struct r8152 *tp)
r8153_enable_eee(tp);
r8153_enable_aldps(tp);
r8152b_enable_fc(tp);
-
- r8152_mdio_write(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE |
- BMCR_ANRESTART);
+ rtl_tally_reset(tp);
}
static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
{
struct r8152 *tp = usb_get_intfdata(intf);
- netif_device_detach(tp->netdev);
+ if (PMSG_IS_AUTO(message))
+ set_bit(SELECTIVE_SUSPEND, &tp->flags);
+ else
+ netif_device_detach(tp->netdev);
if (netif_running(tp->netdev)) {
clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
cancel_delayed_work_sync(&tp->schedule);
- tasklet_disable(&tp->tl);
+ if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
+ rtl_runtime_suspend_enable(tp, true);
+ } else {
+ tasklet_disable(&tp->tl);
+ tp->rtl_ops.down(tp);
+ tasklet_enable(&tp->tl);
+ }
}
- tp->rtl_ops.down(tp);
-
return 0;
}
@@ -2526,22 +3026,77 @@ static int rtl8152_resume(struct usb_interface *intf)
{
struct r8152 *tp = usb_get_intfdata(intf);
- tp->rtl_ops.init(tp);
- netif_device_attach(tp->netdev);
+ if (!test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
+ tp->rtl_ops.init(tp);
+ netif_device_attach(tp->netdev);
+ }
+
if (netif_running(tp->netdev)) {
- rtl8152_set_speed(tp, AUTONEG_ENABLE,
+ if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
+ rtl_runtime_suspend_enable(tp, false);
+ clear_bit(SELECTIVE_SUSPEND, &tp->flags);
+ if (tp->speed & LINK_STATUS)
+ tp->rtl_ops.disable(tp);
+ } else {
+ tp->rtl_ops.up(tp);
+ rtl8152_set_speed(tp, AUTONEG_ENABLE,
tp->mii.supports_gmii ? SPEED_1000 : SPEED_100,
DUPLEX_FULL);
+ }
tp->speed = 0;
netif_carrier_off(tp->netdev);
set_bit(WORK_ENABLE, &tp->flags);
usb_submit_urb(tp->intr_urb, GFP_KERNEL);
- tasklet_enable(&tp->tl);
}
return 0;
}
+static void rtl8152_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct r8152 *tp = netdev_priv(dev);
+
+ if (usb_autopm_get_interface(tp->intf) < 0)
+ return;
+
+ wol->supported = WAKE_ANY;
+ wol->wolopts = __rtl_get_wol(tp);
+
+ usb_autopm_put_interface(tp->intf);
+}
+
+static int rtl8152_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct r8152 *tp = netdev_priv(dev);
+ int ret;
+
+ ret = usb_autopm_get_interface(tp->intf);
+ if (ret < 0)
+ goto out_set_wol;
+
+ __rtl_set_wol(tp, wol->wolopts);
+ tp->saved_wolopts = wol->wolopts & WAKE_ANY;
+
+ usb_autopm_put_interface(tp->intf);
+
+out_set_wol:
+ return ret;
+}
+
+static u32 rtl8152_get_msglevel(struct net_device *dev)
+{
+ struct r8152 *tp = netdev_priv(dev);
+
+ return tp->msg_enable;
+}
+
+static void rtl8152_set_msglevel(struct net_device *dev, u32 value)
+{
+ struct r8152 *tp = netdev_priv(dev);
+
+ tp->msg_enable = value;
+}
+
static void rtl8152_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
@@ -2566,8 +3121,76 @@ int rtl8152_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
static int rtl8152_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct r8152 *tp = netdev_priv(dev);
+ int ret;
+
+ ret = usb_autopm_get_interface(tp->intf);
+ if (ret < 0)
+ goto out;
- return rtl8152_set_speed(tp, cmd->autoneg, cmd->speed, cmd->duplex);
+ ret = rtl8152_set_speed(tp, cmd->autoneg, cmd->speed, cmd->duplex);
+
+ usb_autopm_put_interface(tp->intf);
+
+out:
+ return ret;
+}
+
+static const char rtl8152_gstrings[][ETH_GSTRING_LEN] = {
+ "tx_packets",
+ "rx_packets",
+ "tx_errors",
+ "rx_errors",
+ "rx_missed",
+ "align_errors",
+ "tx_single_collisions",
+ "tx_multi_collisions",
+ "rx_unicast",
+ "rx_broadcast",
+ "rx_multicast",
+ "tx_aborted",
+ "tx_underrun",
+};
+
+static int rtl8152_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(rtl8152_gstrings);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void rtl8152_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct r8152 *tp = netdev_priv(dev);
+ struct tally_counter tally;
+
+ generic_ocp_read(tp, PLA_TALLYCNT, sizeof(tally), &tally, MCU_TYPE_PLA);
+
+ data[0] = le64_to_cpu(tally.tx_packets);
+ data[1] = le64_to_cpu(tally.rx_packets);
+ data[2] = le64_to_cpu(tally.tx_errors);
+ data[3] = le32_to_cpu(tally.rx_errors);
+ data[4] = le16_to_cpu(tally.rx_missed);
+ data[5] = le16_to_cpu(tally.align_errors);
+ data[6] = le32_to_cpu(tally.tx_one_collision);
+ data[7] = le32_to_cpu(tally.tx_multi_collision);
+ data[8] = le64_to_cpu(tally.rx_unicast);
+ data[9] = le64_to_cpu(tally.rx_broadcast);
+ data[10] = le32_to_cpu(tally.rx_multicast);
+ data[11] = le16_to_cpu(tally.tx_aborted);
+ data[12] = le16_to_cpu(tally.tx_underun);
+}
+
+static void rtl8152_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ switch (stringset) {
+ case ETH_SS_STATS:
+ memcpy(data, *rtl8152_gstrings, sizeof(rtl8152_gstrings));
+ break;
+ }
}
static struct ethtool_ops ops = {
@@ -2575,13 +3198,24 @@ static struct ethtool_ops ops = {
.get_settings = rtl8152_get_settings,
.set_settings = rtl8152_set_settings,
.get_link = ethtool_op_get_link,
+ .get_msglevel = rtl8152_get_msglevel,
+ .set_msglevel = rtl8152_set_msglevel,
+ .get_wol = rtl8152_get_wol,
+ .set_wol = rtl8152_set_wol,
+ .get_strings = rtl8152_get_strings,
+ .get_sset_count = rtl8152_get_sset_count,
+ .get_ethtool_stats = rtl8152_get_ethtool_stats,
};
static int rtl8152_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
{
struct r8152 *tp = netdev_priv(netdev);
struct mii_ioctl_data *data = if_mii(rq);
- int res = 0;
+ int res;
+
+ res = usb_autopm_get_interface(tp->intf);
+ if (res < 0)
+ goto out;
switch (cmd) {
case SIOCGMIIPHY:
@@ -2604,6 +3238,9 @@ static int rtl8152_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
res = -EOPNOTSUPP;
}
+ usb_autopm_put_interface(tp->intf);
+
+out:
return res;
}
@@ -2656,22 +3293,13 @@ static void r8152b_get_version(struct r8152 *tp)
static void rtl8152_unload(struct r8152 *tp)
{
- u32 ocp_data;
-
- if (tp->version != RTL_VER_01) {
- ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL);
- ocp_data |= POWER_CUT;
- ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data);
- }
-
- ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS);
- ocp_data &= ~RESUME_INDICATE;
- ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data);
+ if (tp->version != RTL_VER_01)
+ r8152_power_cut_en(tp, true);
}
static void rtl8153_unload(struct r8152 *tp)
{
- r8153_power_cut_en(tp, 1);
+ r8153_power_cut_en(tp, true);
}
static int rtl_ops_init(struct r8152 *tp, const struct usb_device_id *id)
@@ -2686,6 +3314,7 @@ static int rtl_ops_init(struct r8152 *tp, const struct usb_device_id *id)
ops->init = r8152b_init;
ops->enable = rtl8152_enable;
ops->disable = rtl8152_disable;
+ ops->up = r8152b_exit_oob;
ops->down = rtl8152_down;
ops->unload = rtl8152_unload;
ret = 0;
@@ -2694,6 +3323,7 @@ static int rtl_ops_init(struct r8152 *tp, const struct usb_device_id *id)
ops->init = r8153_init;
ops->enable = rtl8153_enable;
ops->disable = rtl8152_disable;
+ ops->up = r8153_first_init;
ops->down = rtl8153_down;
ops->unload = rtl8153_unload;
ret = 0;
@@ -2709,6 +3339,7 @@ static int rtl_ops_init(struct r8152 *tp, const struct usb_device_id *id)
ops->init = r8153_init;
ops->enable = rtl8153_enable;
ops->disable = rtl8152_disable;
+ ops->up = r8153_first_init;
ops->down = rtl8153_down;
ops->unload = rtl8153_unload;
ret = 0;
@@ -2766,9 +3397,15 @@ static int rtl8152_probe(struct usb_interface *intf,
netdev->netdev_ops = &rtl8152_netdev_ops;
netdev->watchdog_timeo = RTL8152_TX_TIMEOUT;
- netdev->features |= NETIF_F_IP_CSUM;
- netdev->hw_features = NETIF_F_IP_CSUM;
+ netdev->features |= NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG |
+ NETIF_F_TSO | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM |
+ NETIF_F_TSO6;
+ netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG |
+ NETIF_F_TSO | NETIF_F_FRAGLIST |
+ NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
+
SET_ETHTOOL_OPS(netdev, &ops);
+ netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE);
tp->mii.dev = netdev;
tp->mii.mdio_read = read_mii_word;
@@ -2778,14 +3415,12 @@ static int rtl8152_probe(struct usb_interface *intf,
tp->mii.phy_id = R8152_PHY_ID;
tp->mii.supports_gmii = 0;
+ intf->needs_remote_wakeup = 1;
+
r8152b_get_version(tp);
tp->rtl_ops.init(tp);
set_ethernet_addr(tp);
- ret = alloc_all_mem(tp);
- if (ret)
- goto out;
-
usb_set_intfdata(intf, tp);
ret = register_netdev(netdev);
@@ -2794,6 +3429,12 @@ static int rtl8152_probe(struct usb_interface *intf,
goto out1;
}
+ tp->saved_wolopts = __rtl_get_wol(tp);
+ if (tp->saved_wolopts)
+ device_set_wakeup_enable(&udev->dev, true);
+ else
+ device_set_wakeup_enable(&udev->dev, false);
+
netif_info(tp, probe, netdev, "%s\n", DRIVER_VERSION);
return 0;
@@ -2815,7 +3456,6 @@ static void rtl8152_disconnect(struct usb_interface *intf)
tasklet_kill(&tp->tl);
unregister_netdev(tp->netdev);
tp->rtl_ops.unload(tp);
- free_all_mem(tp);
free_netdev(tp->netdev);
}
}
@@ -2838,6 +3478,8 @@ static struct usb_driver rtl8152_driver = {
.suspend = rtl8152_suspend,
.resume = rtl8152_resume,
.reset_resume = rtl8152_resume,
+ .supports_autosuspend = 1,
+ .disable_hub_initiated_lpm = 1,
};
module_usb_driver(rtl8152_driver);
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index c0e7c64765ab..b4a10bcb66a0 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -14,6 +14,7 @@
#include <linux/etherdevice.h>
#include <linux/u64_stats_sync.h>
+#include <net/rtnetlink.h>
#include <net/dst.h>
#include <net/xfrm.h>
#include <linux/veth.h>
@@ -155,10 +156,10 @@ static u64 veth_stats_one(struct pcpu_vstats *result, struct net_device *dev)
unsigned int start;
do {
- start = u64_stats_fetch_begin_bh(&stats->syncp);
+ start = u64_stats_fetch_begin_irq(&stats->syncp);
packets = stats->packets;
bytes = stats->bytes;
- } while (u64_stats_fetch_retry_bh(&stats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&stats->syncp, start));
result->packets += packets;
result->bytes += bytes;
}
@@ -235,18 +236,9 @@ static int veth_change_mtu(struct net_device *dev, int new_mtu)
static int veth_dev_init(struct net_device *dev)
{
- int i;
-
- dev->vstats = alloc_percpu(struct pcpu_vstats);
+ dev->vstats = netdev_alloc_pcpu_stats(struct pcpu_vstats);
if (!dev->vstats)
return -ENOMEM;
-
- for_each_possible_cpu(i) {
- struct pcpu_vstats *veth_stats;
- veth_stats = per_cpu_ptr(dev->vstats, i);
- u64_stats_init(&veth_stats->syncp);
- }
-
return 0;
}
@@ -336,10 +328,9 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
nla_peer = data[VETH_INFO_PEER];
ifmp = nla_data(nla_peer);
- err = nla_parse(peer_tb, IFLA_MAX,
- nla_data(nla_peer) + sizeof(struct ifinfomsg),
- nla_len(nla_peer) - sizeof(struct ifinfomsg),
- ifla_policy);
+ err = rtnl_nla_parse_ifla(peer_tb,
+ nla_data(nla_peer) + sizeof(struct ifinfomsg),
+ nla_len(nla_peer) - sizeof(struct ifinfomsg));
if (err < 0)
return err;
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 841b60831df1..7b687469199b 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -882,7 +882,7 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
dev_warn(&dev->dev,
"Unexpected TXQ (%d) queue failure: %d\n", qnum, err);
dev->stats.tx_dropped++;
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
virtqueue_kick(sq->vq);
@@ -938,7 +938,7 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd,
sgs[out_num] = &stat;
BUG_ON(out_num + 1 > ARRAY_SIZE(sgs));
- BUG_ON(virtqueue_add_sgs(vi->cvq, sgs, out_num, 1, vi, GFP_ATOMIC) < 0);
+ virtqueue_add_sgs(vi->cvq, sgs, out_num, 1, vi, GFP_ATOMIC);
if (unlikely(!virtqueue_kick(vi->cvq)))
return status == VIRTIO_NET_OK;
@@ -1000,16 +1000,16 @@ static struct rtnl_link_stats64 *virtnet_stats(struct net_device *dev,
u64 tpackets, tbytes, rpackets, rbytes;
do {
- start = u64_stats_fetch_begin_bh(&stats->tx_syncp);
+ start = u64_stats_fetch_begin_irq(&stats->tx_syncp);
tpackets = stats->tx_packets;
tbytes = stats->tx_bytes;
- } while (u64_stats_fetch_retry_bh(&stats->tx_syncp, start));
+ } while (u64_stats_fetch_retry_irq(&stats->tx_syncp, start));
do {
- start = u64_stats_fetch_begin_bh(&stats->rx_syncp);
+ start = u64_stats_fetch_begin_irq(&stats->rx_syncp);
rpackets = stats->rx_packets;
rbytes = stats->rx_bytes;
- } while (u64_stats_fetch_retry_bh(&stats->rx_syncp, start));
+ } while (u64_stats_fetch_retry_irq(&stats->rx_syncp, start));
tot->rx_packets += rpackets;
tot->tx_packets += tpackets;
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 0fa3b44f7342..97394345e5dd 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -1078,7 +1078,7 @@ unlock_drop_pkt:
spin_unlock_irqrestore(&tq->tx_lock, flags);
drop_pkt:
tq->stats.drop_total++;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -2738,47 +2738,35 @@ vmxnet3_read_mac_addr(struct vmxnet3_adapter *adapter, u8 *mac)
/*
* Enable MSIx vectors.
* Returns :
- * 0 on successful enabling of required vectors,
* VMXNET3_LINUX_MIN_MSIX_VECT when only minimum number of vectors required
- * could be enabled.
- * number of vectors which can be enabled otherwise (this number is smaller
+ * were enabled.
+ * number of vectors which were enabled otherwise (this number is greater
* than VMXNET3_LINUX_MIN_MSIX_VECT)
*/
static int
-vmxnet3_acquire_msix_vectors(struct vmxnet3_adapter *adapter,
- int vectors)
-{
- int err = 0, vector_threshold;
- vector_threshold = VMXNET3_LINUX_MIN_MSIX_VECT;
-
- while (vectors >= vector_threshold) {
- err = pci_enable_msix(adapter->pdev, adapter->intr.msix_entries,
- vectors);
- if (!err) {
- adapter->intr.num_intrs = vectors;
- return 0;
- } else if (err < 0) {
- dev_err(&adapter->netdev->dev,
- "Failed to enable MSI-X, error: %d\n", err);
- vectors = 0;
- } else if (err < vector_threshold) {
- break;
- } else {
- /* If fails to enable required number of MSI-x vectors
- * try enabling minimum number of vectors required.
- */
- dev_err(&adapter->netdev->dev,
- "Failed to enable %d MSI-X, trying %d instead\n",
- vectors, vector_threshold);
- vectors = vector_threshold;
- }
+vmxnet3_acquire_msix_vectors(struct vmxnet3_adapter *adapter, int nvec)
+{
+ int ret = pci_enable_msix_range(adapter->pdev,
+ adapter->intr.msix_entries, nvec, nvec);
+
+ if (ret == -ENOSPC && nvec > VMXNET3_LINUX_MIN_MSIX_VECT) {
+ dev_err(&adapter->netdev->dev,
+ "Failed to enable %d MSI-X, trying %d\n",
+ nvec, VMXNET3_LINUX_MIN_MSIX_VECT);
+
+ ret = pci_enable_msix_range(adapter->pdev,
+ adapter->intr.msix_entries,
+ VMXNET3_LINUX_MIN_MSIX_VECT,
+ VMXNET3_LINUX_MIN_MSIX_VECT);
}
- dev_info(&adapter->pdev->dev,
- "Number of MSI-X interrupts which can be allocated "
- "is lower than min threshold required.\n");
- return err;
+ if (ret < 0) {
+ dev_err(&adapter->netdev->dev,
+ "Failed to enable MSI-X, error: %d\n", ret);
+ }
+
+ return ret;
}
@@ -2805,56 +2793,50 @@ vmxnet3_alloc_intr_resources(struct vmxnet3_adapter *adapter)
#ifdef CONFIG_PCI_MSI
if (adapter->intr.type == VMXNET3_IT_MSIX) {
- int vector, err = 0;
-
- adapter->intr.num_intrs = (adapter->share_intr ==
- VMXNET3_INTR_TXSHARE) ? 1 :
- adapter->num_tx_queues;
- adapter->intr.num_intrs += (adapter->share_intr ==
- VMXNET3_INTR_BUDDYSHARE) ? 0 :
- adapter->num_rx_queues;
- adapter->intr.num_intrs += 1; /* for link event */
-
- adapter->intr.num_intrs = (adapter->intr.num_intrs >
- VMXNET3_LINUX_MIN_MSIX_VECT
- ? adapter->intr.num_intrs :
- VMXNET3_LINUX_MIN_MSIX_VECT);
-
- for (vector = 0; vector < adapter->intr.num_intrs; vector++)
- adapter->intr.msix_entries[vector].entry = vector;
-
- err = vmxnet3_acquire_msix_vectors(adapter,
- adapter->intr.num_intrs);
+ int i, nvec;
+
+ nvec = adapter->share_intr == VMXNET3_INTR_TXSHARE ?
+ 1 : adapter->num_tx_queues;
+ nvec += adapter->share_intr == VMXNET3_INTR_BUDDYSHARE ?
+ 0 : adapter->num_rx_queues;
+ nvec += 1; /* for link event */
+ nvec = nvec > VMXNET3_LINUX_MIN_MSIX_VECT ?
+ nvec : VMXNET3_LINUX_MIN_MSIX_VECT;
+
+ for (i = 0; i < nvec; i++)
+ adapter->intr.msix_entries[i].entry = i;
+
+ nvec = vmxnet3_acquire_msix_vectors(adapter, nvec);
+ if (nvec < 0)
+ goto msix_err;
+
/* If we cannot allocate one MSIx vector per queue
* then limit the number of rx queues to 1
*/
- if (err == VMXNET3_LINUX_MIN_MSIX_VECT) {
+ if (nvec == VMXNET3_LINUX_MIN_MSIX_VECT) {
if (adapter->share_intr != VMXNET3_INTR_BUDDYSHARE
|| adapter->num_rx_queues != 1) {
adapter->share_intr = VMXNET3_INTR_TXSHARE;
netdev_err(adapter->netdev,
"Number of rx queues : 1\n");
adapter->num_rx_queues = 1;
- adapter->intr.num_intrs =
- VMXNET3_LINUX_MIN_MSIX_VECT;
}
- return;
}
- if (!err)
- return;
+ adapter->intr.num_intrs = nvec;
+ return;
+
+msix_err:
/* If we cannot allocate MSIx vectors use only one rx queue */
dev_info(&adapter->pdev->dev,
"Failed to enable MSI-X, error %d. "
- "Limiting #rx queues to 1, try MSI.\n", err);
+ "Limiting #rx queues to 1, try MSI.\n", nvec);
adapter->intr.type = VMXNET3_IT_MSI;
}
if (adapter->intr.type == VMXNET3_IT_MSI) {
- int err;
- err = pci_enable_msi(adapter->pdev);
- if (!err) {
+ if (!pci_enable_msi(adapter->pdev)) {
adapter->num_rx_queues = 1;
adapter->intr.num_intrs = 1;
return;
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 1236812c7be6..0d862a5077ab 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -1132,7 +1132,6 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
{
struct vxlan_sock *vs;
struct vxlanhdr *vxh;
- __be16 port;
/* Need Vxlan and inner Ethernet header to be present */
if (!pskb_may_pull(skb, VXLAN_HLEN))
@@ -1150,8 +1149,6 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
if (iptunnel_pull_header(skb, VXLAN_HLEN, htons(ETH_P_TEB)))
goto drop;
- port = inet_sk(sk)->inet_sport;
-
vs = rcu_dereference_sk_user_data(sk);
if (!vs)
goto drop;
@@ -2080,19 +2077,11 @@ static int vxlan_init(struct net_device *dev)
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
struct vxlan_sock *vs;
- int i;
- dev->tstats = alloc_percpu(struct pcpu_sw_netstats);
+ dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!dev->tstats)
return -ENOMEM;
- for_each_possible_cpu(i) {
- struct pcpu_sw_netstats *vxlan_stats;
- vxlan_stats = per_cpu_ptr(dev->tstats, i);
- u64_stats_init(&vxlan_stats->syncp);
- }
-
-
spin_lock(&vn->sock_lock);
vs = vxlan_find_sock(dev_net(dev), vxlan->dst_port);
if (vs) {
diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c
index 48896138418f..a9970f1af976 100644
--- a/drivers/net/wimax/i2400m/netdev.c
+++ b/drivers/net/wimax/i2400m/netdev.c
@@ -374,8 +374,7 @@ netdev_tx_t i2400m_hard_start_xmit(struct sk_buff *skb,
d_fnstart(3, dev, "(skb %p net_dev %p)\n", skb, net_dev);
- if (skb_header_cloned(skb) &&
- pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+ if (skb_cow_head(skb, 0))
goto drop;
if (i2400m->state == I2400M_SS_IDLE)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 200020eb3005..b2137e8f7ca6 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -53,7 +53,7 @@ config LIBERTAS_THINFIRM_USB
config AIRO
tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
- depends on ISA_DMA_API && (PCI || BROKEN)
+ depends on CFG80211 && ISA_DMA_API && (PCI || BROKEN)
select WIRELESS_EXT
select CRYPTO
select WEXT_SPY
@@ -73,7 +73,7 @@ config AIRO
config ATMEL
tristate "Atmel at76c50x chipset 802.11b support"
- depends on (PCI || PCMCIA)
+ depends on CFG80211 && (PCI || PCMCIA)
select WIRELESS_EXT
select WEXT_PRIV
select FW_LOADER
@@ -116,7 +116,7 @@ config AT76C50X_USB
config AIRO_CS
tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
- depends on PCMCIA && (BROKEN || !M32R)
+ depends on CFG80211 && PCMCIA && (BROKEN || !M32R)
select WIRELESS_EXT
select WEXT_SPY
select WEXT_PRIV
@@ -138,7 +138,7 @@ config AIRO_CS
config PCMCIA_WL3501
tristate "Planet WL3501 PCMCIA cards"
- depends on PCMCIA
+ depends on CFG80211 && PCMCIA
select WIRELESS_EXT
select WEXT_SPY
help
@@ -168,7 +168,7 @@ config PRISM54
config USB_ZD1201
tristate "USB ZD1201 based Wireless device support"
- depends on USB
+ depends on CFG80211 && USB
select WIRELESS_EXT
select WEXT_PRIV
select FW_LOADER
@@ -281,5 +281,6 @@ 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 0fab227025be..0c8891686718 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -59,3 +59,4 @@ obj-$(CONFIG_BRCMFMAC) += brcm80211/
obj-$(CONFIG_BRCMSMAC) += brcm80211/
obj-$(CONFIG_CW1200) += cw1200/
+obj-$(CONFIG_RSI_91X) += rsi/
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index edf4b57c4aaa..64747d457bb3 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -36,7 +36,7 @@
#include <linux/bitops.h>
#include <linux/scatterlist.h>
#include <linux/crypto.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/unaligned.h>
#include <linux/netdevice.h>
@@ -45,11 +45,11 @@
#include <linux/if_arp.h>
#include <linux/ioport.h>
#include <linux/pci.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
-#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
#include <net/iw_handler.h>
#include "airo.h"
@@ -5797,7 +5797,7 @@ static int airo_set_freq(struct net_device *dev,
/* Hack to fall through... */
fwrq->e = 0;
- fwrq->m = ieee80211_freq_to_dsss_chan(f);
+ fwrq->m = ieee80211_frequency_to_channel(f);
}
/* Setting by channel number */
if((fwrq->m > 1000) || (fwrq->e > 0))
@@ -5841,7 +5841,8 @@ static int airo_get_freq(struct net_device *dev,
ch = le16_to_cpu(status_rid.channel);
if((ch > 0) && (ch < 15)) {
- fwrq->m = ieee80211_dsss_chan_to_freq(ch) * 100000;
+ fwrq->m = 100000 *
+ ieee80211_channel_to_frequency(ch, IEEE80211_BAND_2GHZ);
fwrq->e = 1;
} else {
fwrq->m = ch;
@@ -6898,7 +6899,8 @@ static int airo_get_range(struct net_device *dev,
k = 0;
for(i = 0; i < 14; i++) {
range->freq[k].i = i + 1; /* List index */
- range->freq[k].m = ieee80211_dsss_chan_to_freq(i + 1) * 100000;
+ range->freq[k].m = 100000 *
+ ieee80211_channel_to_frequency(i + 1, IEEE80211_BAND_2GHZ);
range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */
}
range->num_frequency = k;
@@ -7297,7 +7299,8 @@ static inline char *airo_translate_scan(struct net_device *dev,
/* Add frequency */
iwe.cmd = SIOCGIWFREQ;
iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
- iwe.u.freq.m = ieee80211_dsss_chan_to_freq(iwe.u.freq.m) * 100000;
+ iwe.u.freq.m = 100000 *
+ ieee80211_channel_to_frequency(iwe.u.freq.m, IEEE80211_BAND_2GHZ);
iwe.u.freq.e = 1;
current_ev = iwe_stream_add_event(info, current_ev, end_buf,
&iwe, IW_EV_FREQ_LEN);
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index b59cfbe0276b..a889fd66fc63 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -56,6 +56,15 @@ enum ath_device_state {
ATH_HW_INITIALIZED,
};
+enum ath_op_flags {
+ ATH_OP_INVALID,
+ ATH_OP_BEACONS,
+ ATH_OP_ANI_RUN,
+ ATH_OP_PRIM_STA_VIF,
+ ATH_OP_HW_RESET,
+ ATH_OP_SCANNING,
+};
+
enum ath_bus_type {
ATH_PCI,
ATH_AHB,
@@ -63,7 +72,7 @@ enum ath_bus_type {
};
struct reg_dmn_pair_mapping {
- u16 regDmnEnum;
+ u16 reg_domain;
u16 reg_5ghz_ctl;
u16 reg_2ghz_ctl;
};
@@ -130,6 +139,7 @@ struct ath_common {
struct ieee80211_hw *hw;
int debug_mask;
enum ath_device_state state;
+ unsigned long op_flags;
struct ath_ani ani;
@@ -161,6 +171,9 @@ struct ath_common {
bool btcoex_enabled;
bool disable_ani;
bool bt_ant_diversity;
+
+ int last_rssi;
+ struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
};
struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index d44d618b05f9..a79499c82350 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -266,12 +266,12 @@ static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar,
* ath10k_ce_sendlist_send.
* The caller takes responsibility for any needed locking.
*/
-static int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
- void *per_transfer_context,
- u32 buffer,
- unsigned int nbytes,
- unsigned int transfer_id,
- unsigned int flags)
+int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
+ void *per_transfer_context,
+ u32 buffer,
+ unsigned int nbytes,
+ unsigned int transfer_id,
+ unsigned int flags)
{
struct ath10k *ar = ce_state->ar;
struct ath10k_ce_ring *src_ring = ce_state->src_ring;
@@ -1067,9 +1067,9 @@ struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar,
*
* For the lack of a better place do the check here.
*/
- BUILD_BUG_ON(TARGET_NUM_MSDU_DESC >
+ BUILD_BUG_ON(2*TARGET_NUM_MSDU_DESC >
(CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
- BUILD_BUG_ON(TARGET_10X_NUM_MSDU_DESC >
+ BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC >
(CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
ret = ath10k_pci_wake(ar);
diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
index 67dbde6a5c74..8eb7f99ed992 100644
--- a/drivers/net/wireless/ath/ath10k/ce.h
+++ b/drivers/net/wireless/ath/ath10k/ce.h
@@ -23,7 +23,7 @@
/* Maximum number of Copy Engine's supported */
#define CE_COUNT_MAX 8
-#define CE_HTT_H2T_MSG_SRC_NENTRIES 2048
+#define CE_HTT_H2T_MSG_SRC_NENTRIES 4096
/* Descriptor rings must be aligned to this boundary */
#define CE_DESC_RING_ALIGN 8
@@ -152,6 +152,13 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
unsigned int transfer_id,
unsigned int flags);
+int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
+ void *per_transfer_context,
+ u32 buffer,
+ unsigned int nbytes,
+ unsigned int transfer_id,
+ unsigned int flags);
+
void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
void (*send_cb)(struct ath10k_ce_pipe *),
int disable_interrupts);
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 3b59af3bddf4..ebc5fc2ede75 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -55,8 +55,7 @@ static void ath10k_send_suspend_complete(struct ath10k *ar)
{
ath10k_dbg(ATH10K_DBG_BOOT, "boot suspend complete\n");
- ar->is_target_paused = true;
- wake_up(&ar->event_queue);
+ complete(&ar->target_suspend);
}
static int ath10k_init_connect_htc(struct ath10k *ar)
@@ -470,8 +469,12 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
if (index == ie_len)
break;
- if (data[index] & (1 << bit))
+ if (data[index] & (1 << bit)) {
+ ath10k_dbg(ATH10K_DBG_BOOT,
+ "Enabling feature bit: %i\n",
+ i);
__set_bit(i, ar->fw_features);
+ }
}
ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "",
@@ -699,6 +702,7 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
init_completion(&ar->scan.started);
init_completion(&ar->scan.completed);
init_completion(&ar->scan.on_channel);
+ init_completion(&ar->target_suspend);
init_completion(&ar->install_key_done);
init_completion(&ar->vdev_setup_done);
@@ -722,8 +726,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work);
skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
- init_waitqueue_head(&ar->event_queue);
-
INIT_WORK(&ar->restart_work, ath10k_core_restart);
return ar;
@@ -856,10 +858,34 @@ err:
}
EXPORT_SYMBOL(ath10k_core_start);
+int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt)
+{
+ int ret;
+
+ reinit_completion(&ar->target_suspend);
+
+ ret = ath10k_wmi_pdev_suspend_target(ar, suspend_opt);
+ if (ret) {
+ ath10k_warn("could not suspend target (%d)\n", ret);
+ return ret;
+ }
+
+ ret = wait_for_completion_timeout(&ar->target_suspend, 1 * HZ);
+
+ if (ret == 0) {
+ ath10k_warn("suspend timed out - target pause event never came\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
void ath10k_core_stop(struct ath10k *ar)
{
lockdep_assert_held(&ar->conf_mutex);
+ /* try to suspend target */
+ ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR);
ath10k_debug_stop(ar);
ath10k_htc_stop(&ar->htc);
ath10k_htt_detach(&ar->htt);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index ade1781c7186..0e71979d837c 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -46,21 +46,35 @@
#define ATH10K_MAX_NUM_MGMT_PENDING 128
+/* number of failed packets */
+#define ATH10K_KICKOUT_THRESHOLD 50
+
+/*
+ * Use insanely high numbers to make sure that the firmware implementation
+ * won't start, we have the same functionality already in hostapd. Unit
+ * is seconds.
+ */
+#define ATH10K_KEEPALIVE_MIN_IDLE 3747
+#define ATH10K_KEEPALIVE_MAX_IDLE 3895
+#define ATH10K_KEEPALIVE_MAX_UNRESPONSIVE 3900
+
struct ath10k;
struct ath10k_skb_cb {
dma_addr_t paddr;
- bool is_mapped;
- bool is_aborted;
u8 vdev_id;
struct {
u8 tid;
bool is_offchan;
-
- u8 frag_len;
- u8 pad_len;
+ struct ath10k_htt_txbuf *txbuf;
+ u32 txbuf_paddr;
} __packed htt;
+
+ struct {
+ bool dtim_zero;
+ bool deliver_cab;
+ } bcn;
} __packed;
static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
@@ -70,32 +84,6 @@ static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data;
}
-static inline int ath10k_skb_map(struct device *dev, struct sk_buff *skb)
-{
- if (ATH10K_SKB_CB(skb)->is_mapped)
- return -EINVAL;
-
- ATH10K_SKB_CB(skb)->paddr = dma_map_single(dev, skb->data, skb->len,
- DMA_TO_DEVICE);
-
- if (unlikely(dma_mapping_error(dev, ATH10K_SKB_CB(skb)->paddr)))
- return -EIO;
-
- ATH10K_SKB_CB(skb)->is_mapped = true;
- return 0;
-}
-
-static inline int ath10k_skb_unmap(struct device *dev, struct sk_buff *skb)
-{
- if (!ATH10K_SKB_CB(skb)->is_mapped)
- return -EINVAL;
-
- dma_unmap_single(dev, ATH10K_SKB_CB(skb)->paddr, skb->len,
- DMA_TO_DEVICE);
- ATH10K_SKB_CB(skb)->is_mapped = false;
- return 0;
-}
-
static inline u32 host_interest_item_address(u32 item_offset)
{
return QCA988X_HOST_INTEREST_ADDRESS + item_offset;
@@ -211,6 +199,18 @@ struct ath10k_peer {
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
};
+struct ath10k_sta {
+ struct ath10k_vif *arvif;
+
+ /* the following are protected by ar->data_lock */
+ u32 changed; /* IEEE80211_RC_* */
+ u32 bw;
+ u32 nss;
+ u32 smps;
+
+ struct work_struct update_wk;
+};
+
#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
struct ath10k_vif {
@@ -222,10 +222,17 @@ struct ath10k_vif {
u32 beacon_interval;
u32 dtim_period;
struct sk_buff *beacon;
+ /* protected by data_lock */
+ bool beacon_sent;
struct ath10k *ar;
struct ieee80211_vif *vif;
+ bool is_started;
+ bool is_up;
+ u32 aid;
+ u8 bssid[ETH_ALEN];
+
struct work_struct wep_key_work;
struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
u8 def_wep_key_idx;
@@ -235,7 +242,6 @@ struct ath10k_vif {
union {
struct {
- u8 bssid[ETH_ALEN];
u32 uapsd;
} sta;
struct {
@@ -249,13 +255,11 @@ struct ath10k_vif {
u32 noa_len;
u8 *noa_data;
} ap;
- struct {
- u8 bssid[ETH_ALEN];
- } ibss;
} u;
u8 fixed_rate;
u8 fixed_nss;
+ u8 force_sgi;
};
struct ath10k_vif_iter {
@@ -355,8 +359,7 @@ struct ath10k {
const struct ath10k_hif_ops *ops;
} hif;
- wait_queue_head_t event_queue;
- bool is_target_paused;
+ struct completion target_suspend;
struct ath10k_bmi bmi;
struct ath10k_wmi wmi;
@@ -412,6 +415,9 @@ struct ath10k {
/* valid during scan; needed for mgmt rx during scan */
struct ieee80211_channel *scan_channel;
+ /* current operating channel definition */
+ struct cfg80211_chan_def chandef;
+
int free_vdev_map;
int monitor_vdev_id;
bool monitor_enabled;
@@ -470,6 +476,7 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
void ath10k_core_destroy(struct ath10k *ar);
int ath10k_core_start(struct ath10k *ar);
+int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt);
void ath10k_core_stop(struct ath10k *ar);
int ath10k_core_register(struct ath10k *ar, u32 chip_id);
void ath10k_core_unregister(struct ath10k *ar);
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index 1773c36c71a0..a5824990bd2a 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -92,7 +92,7 @@ static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
#ifdef CONFIG_ATH10K_DEBUG
__printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask,
- const char *fmt, ...);
+ const char *fmt, ...);
void ath10k_dbg_dump(enum ath10k_debug_mask mask,
const char *msg, const char *prefix,
const void *buf, size_t len);
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index dcdea68bcc0a..2ac7beacddca 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -21,6 +21,14 @@
#include <linux/kernel.h>
#include "core.h"
+struct ath10k_hif_sg_item {
+ u16 transfer_id;
+ void *transfer_context; /* NULL = tx completion callback not called */
+ void *vaddr; /* for debugging mostly */
+ u32 paddr;
+ u16 len;
+};
+
struct ath10k_hif_cb {
int (*tx_completion)(struct ath10k *ar,
struct sk_buff *wbuf,
@@ -31,11 +39,9 @@ struct ath10k_hif_cb {
};
struct ath10k_hif_ops {
- /* Send the head of a buffer to HIF for transmission to the target. */
- int (*send_head)(struct ath10k *ar, u8 pipe_id,
- unsigned int transfer_id,
- unsigned int nbytes,
- struct sk_buff *buf);
+ /* send a scatter-gather list to the target */
+ int (*tx_sg)(struct ath10k *ar, u8 pipe_id,
+ struct ath10k_hif_sg_item *items, int n_items);
/*
* API to handle HIF-specific BMI message exchanges, this API is
@@ -86,12 +92,11 @@ struct ath10k_hif_ops {
};
-static inline int ath10k_hif_send_head(struct ath10k *ar, u8 pipe_id,
- unsigned int transfer_id,
- unsigned int nbytes,
- struct sk_buff *buf)
+static inline int ath10k_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
+ struct ath10k_hif_sg_item *items,
+ int n_items)
{
- return ar->hif.ops->send_head(ar, pipe_id, transfer_id, nbytes, buf);
+ return ar->hif.ops->tx_sg(ar, pipe_id, items, n_items);
}
static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index edc57ab505c8..7f1bccd3597f 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -63,7 +63,9 @@ static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar)
static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc,
struct sk_buff *skb)
{
- ath10k_skb_unmap(htc->ar->dev, skb);
+ struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+
+ dma_unmap_single(htc->ar->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);
skb_pull(skb, sizeof(struct ath10k_htc_hdr));
}
@@ -122,6 +124,9 @@ int ath10k_htc_send(struct ath10k_htc *htc,
struct sk_buff *skb)
{
struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+ struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+ struct ath10k_hif_sg_item sg_item;
+ struct device *dev = htc->ar->dev;
int credits = 0;
int ret;
@@ -157,19 +162,25 @@ int ath10k_htc_send(struct ath10k_htc *htc,
ath10k_htc_prepare_tx_skb(ep, skb);
- ret = ath10k_skb_map(htc->ar->dev, skb);
+ skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
+ ret = dma_mapping_error(dev, skb_cb->paddr);
if (ret)
goto err_credits;
- ret = ath10k_hif_send_head(htc->ar, ep->ul_pipe_id, ep->eid,
- skb->len, skb);
+ sg_item.transfer_id = ep->eid;
+ sg_item.transfer_context = skb;
+ sg_item.vaddr = skb->data;
+ sg_item.paddr = skb_cb->paddr;
+ sg_item.len = skb->len;
+
+ ret = ath10k_hif_tx_sg(htc->ar, ep->ul_pipe_id, &sg_item, 1);
if (ret)
goto err_unmap;
return 0;
err_unmap:
- ath10k_skb_unmap(htc->ar->dev, skb);
+ dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);
err_credits:
if (ep->tx_credit_flow_enabled) {
spin_lock_bh(&htc->tx_lock);
@@ -191,10 +202,8 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
struct ath10k_htc *htc = &ar->htc;
struct ath10k_htc_ep *ep = &htc->endpoint[eid];
- if (!skb) {
- ath10k_warn("invalid sk_buff completion - NULL pointer. firmware crashed?\n");
+ if (WARN_ON_ONCE(!skb))
return 0;
- }
ath10k_htc_notify_tx_completion(ep, skb);
/* the skb now belongs to the completion handler */
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index b93ae355bc08..654867fc1ae7 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -20,6 +20,7 @@
#include <linux/bug.h>
#include <linux/interrupt.h>
+#include <linux/dmapool.h>
#include "htc.h"
#include "rx_desc.h"
@@ -1181,11 +1182,20 @@ struct htt_rx_info {
u32 info1;
u32 info2;
} rate;
+
+ u32 tsf;
bool fcs_err;
bool amsdu_more;
bool mic_err;
};
+struct ath10k_htt_txbuf {
+ struct htt_data_tx_desc_frag frags[2];
+ struct ath10k_htc_hdr htc_hdr;
+ struct htt_cmd_hdr cmd_hdr;
+ struct htt_data_tx_desc cmd_tx;
+} __packed;
+
struct ath10k_htt {
struct ath10k *ar;
enum ath10k_htc_ep_id eid;
@@ -1267,11 +1277,18 @@ struct ath10k_htt {
struct sk_buff **pending_tx;
unsigned long *used_msdu_ids; /* bitmap */
wait_queue_head_t empty_tx_wq;
+ struct dma_pool *tx_pool;
/* set if host-fw communication goes haywire
* used to avoid further failures */
bool rx_confused;
struct tasklet_struct rx_replenish_task;
+
+ /* This is used to group tx/rx completions separately and process them
+ * in batches to reduce cache stalls */
+ struct tasklet_struct txrx_compl_task;
+ struct sk_buff_head tx_compl_q;
+ struct sk_buff_head rx_compl_q;
};
#define RX_HTT_HDR_STATUS_LEN 64
@@ -1343,4 +1360,5 @@ int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt);
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 *);
+
#endif
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index fe8bd1b59f0e..cdcbe2de95f9 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -43,7 +43,7 @@
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
-
+static void ath10k_htt_txrx_compl_task(unsigned long ptr);
static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
{
@@ -225,18 +225,16 @@ static void ath10k_htt_rx_ring_refill_retry(unsigned long arg)
ath10k_htt_rx_msdu_buff_replenish(htt);
}
-static unsigned ath10k_htt_rx_ring_elems(struct ath10k_htt *htt)
-{
- return (__le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr) -
- htt->rx_ring.sw_rd_idx.msdu_payld) & htt->rx_ring.size_mask;
-}
-
void ath10k_htt_rx_detach(struct ath10k_htt *htt)
{
int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
del_timer_sync(&htt->rx_ring.refill_retry_timer);
tasklet_kill(&htt->rx_replenish_task);
+ tasklet_kill(&htt->txrx_compl_task);
+
+ skb_queue_purge(&htt->tx_compl_q);
+ skb_queue_purge(&htt->rx_compl_q);
while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
struct sk_buff *skb =
@@ -270,10 +268,12 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
int idx;
struct sk_buff *msdu;
- spin_lock_bh(&htt->rx_ring.lock);
+ lockdep_assert_held(&htt->rx_ring.lock);
- if (ath10k_htt_rx_ring_elems(htt) == 0)
- ath10k_warn("htt rx ring is empty!\n");
+ if (htt->rx_ring.fill_cnt == 0) {
+ ath10k_warn("tried to pop sk_buff from an empty rx ring\n");
+ return NULL;
+ }
idx = htt->rx_ring.sw_rd_idx.msdu_payld;
msdu = htt->rx_ring.netbufs_ring[idx];
@@ -283,7 +283,6 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
htt->rx_ring.sw_rd_idx.msdu_payld = idx;
htt->rx_ring.fill_cnt--;
- spin_unlock_bh(&htt->rx_ring.lock);
return msdu;
}
@@ -307,8 +306,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
struct sk_buff *msdu;
struct htt_rx_desc *rx_desc;
- if (ath10k_htt_rx_ring_elems(htt) == 0)
- ath10k_warn("htt rx ring is empty!\n");
+ lockdep_assert_held(&htt->rx_ring.lock);
if (htt->rx_confused) {
ath10k_warn("htt is confused. refusing rx\n");
@@ -324,7 +322,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
msdu->len + skb_tailroom(msdu),
DMA_FROM_DEVICE);
- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
+ ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx pop: ",
msdu->data, msdu->len + skb_tailroom(msdu));
rx_desc = (struct htt_rx_desc *)msdu->data;
@@ -400,6 +398,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0),
RX_MSDU_START_INFO0_MSDU_LENGTH);
msdu_chained = rx_desc->frag_info.ring2_more_count;
+ msdu_chaining = msdu_chained;
if (msdu_len_invalid)
msdu_len = 0;
@@ -417,8 +416,8 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
next->len + skb_tailroom(next),
DMA_FROM_DEVICE);
- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
- next->data,
+ ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL,
+ "htt rx chained: ", next->data,
next->len + skb_tailroom(next));
skb_trim(next, 0);
@@ -427,13 +426,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
msdu->next = next;
msdu = next;
- msdu_chaining = 1;
- }
-
- if (msdu_len > 0) {
- /* This may suggest FW bug? */
- ath10k_warn("htt rx msdu len not consumed (%d)\n",
- msdu_len);
}
last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
@@ -535,6 +527,12 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt)
tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task,
(unsigned long)htt);
+ skb_queue_head_init(&htt->tx_compl_q);
+ skb_queue_head_init(&htt->rx_compl_q);
+
+ tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
+ (unsigned long)htt);
+
ath10k_dbg(ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n",
htt->rx_ring.size, htt->rx_ring.fill_level);
return 0;
@@ -638,6 +636,12 @@ struct amsdu_subframe_hdr {
__be16 len;
} __packed;
+static int ath10k_htt_rx_nwifi_hdrlen(struct ieee80211_hdr *hdr)
+{
+ /* nwifi header is padded to 4 bytes. this fixes 4addr rx */
+ return round_up(ieee80211_hdrlen(hdr->frame_control), 4);
+}
+
static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
struct htt_rx_info *info)
{
@@ -687,7 +691,7 @@ static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
case RX_MSDU_DECAP_NATIVE_WIFI:
/* pull decapped header and copy DA */
hdr = (struct ieee80211_hdr *)skb->data;
- hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
memcpy(addr, ieee80211_get_DA(hdr), ETH_ALEN);
skb_pull(skb, hdr_len);
@@ -751,7 +755,7 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
/* This shouldn't happen. If it does than it may be a FW bug. */
if (skb->next) {
- ath10k_warn("received chained non A-MSDU frame\n");
+ ath10k_warn("htt rx received chained non A-MSDU frame\n");
ath10k_htt_rx_free_msdu_chain(skb->next);
skb->next = NULL;
}
@@ -774,7 +778,7 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
case RX_MSDU_DECAP_NATIVE_WIFI:
/* Pull decapped header */
hdr = (struct ieee80211_hdr *)skb->data;
- hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
skb_pull(skb, hdr_len);
/* Push original header */
@@ -852,6 +856,20 @@ static bool ath10k_htt_rx_has_mic_err(struct sk_buff *skb)
return false;
}
+static bool ath10k_htt_rx_is_mgmt(struct sk_buff *skb)
+{
+ struct htt_rx_desc *rxd;
+ u32 flags;
+
+ rxd = (void *)skb->data - sizeof(*rxd);
+ flags = __le32_to_cpu(rxd->attention.flags);
+
+ if (flags & RX_ATTENTION_FLAGS_MGMT_TYPE)
+ return true;
+
+ return false;
+}
+
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
{
struct htt_rx_desc *rxd;
@@ -883,6 +901,57 @@ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
return CHECKSUM_UNNECESSARY;
}
+static int ath10k_unchain_msdu(struct sk_buff *msdu_head)
+{
+ struct sk_buff *next = msdu_head->next;
+ struct sk_buff *to_free = next;
+ int space;
+ int total_len = 0;
+
+ /* TODO: Might could optimize this by using
+ * skb_try_coalesce or similar method to
+ * decrease copying, or maybe get mac80211 to
+ * provide a way to just receive a list of
+ * skb?
+ */
+
+ msdu_head->next = NULL;
+
+ /* Allocate total length all at once. */
+ while (next) {
+ total_len += next->len;
+ next = next->next;
+ }
+
+ space = total_len - skb_tailroom(msdu_head);
+ if ((space > 0) &&
+ (pskb_expand_head(msdu_head, 0, space, GFP_ATOMIC) < 0)) {
+ /* TODO: bump some rx-oom error stat */
+ /* put it back together so we can free the
+ * whole list at once.
+ */
+ msdu_head->next = to_free;
+ return -1;
+ }
+
+ /* Walk list again, copying contents into
+ * msdu_head
+ */
+ next = to_free;
+ while (next) {
+ skb_copy_from_linear_data(next, skb_put(msdu_head, next->len),
+ next->len);
+ next = next->next;
+ }
+
+ /* If here, we have consolidated skb. Free the
+ * fragments and pass the main skb on up the
+ * stack.
+ */
+ ath10k_htt_rx_free_msdu_chain(to_free);
+ return 0;
+}
+
static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
struct htt_rx_indication *rx)
{
@@ -894,6 +963,8 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
u8 *fw_desc;
int i, j;
+ lockdep_assert_held(&htt->rx_ring.lock);
+
memset(&info, 0, sizeof(info));
fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
@@ -937,6 +1008,8 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
}
if (ath10k_htt_rx_has_decrypt_err(msdu_head)) {
+ ath10k_dbg(ATH10K_DBG_HTT,
+ "htt rx dropping due to decrypt-err\n");
ath10k_htt_rx_free_msdu_chain(msdu_head);
continue;
}
@@ -944,13 +1017,16 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
status = info.status;
/* Skip mgmt frames while we handle this in WMI */
- if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) {
+ if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL ||
+ ath10k_htt_rx_is_mgmt(msdu_head)) {
+ ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
ath10k_htt_rx_free_msdu_chain(msdu_head);
continue;
}
if (status != HTT_RX_IND_MPDU_STATUS_OK &&
status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
+ status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER &&
!htt->ar->monitor_enabled) {
ath10k_dbg(ATH10K_DBG_HTT,
"htt rx ignoring frame w/ status %d\n",
@@ -960,14 +1036,14 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
}
if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
+ ath10k_dbg(ATH10K_DBG_HTT,
+ "htt rx CAC running\n");
ath10k_htt_rx_free_msdu_chain(msdu_head);
continue;
}
- /* FIXME: we do not support chaining yet.
- * this needs investigation */
- if (msdu_chaining) {
- ath10k_warn("msdu_chaining is true\n");
+ if (msdu_chaining &&
+ (ath10k_unchain_msdu(msdu_head) < 0)) {
ath10k_htt_rx_free_msdu_chain(msdu_head);
continue;
}
@@ -975,12 +1051,22 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
info.skb = msdu_head;
info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
info.mic_err = ath10k_htt_rx_has_mic_err(msdu_head);
+
+ if (info.fcs_err)
+ ath10k_dbg(ATH10K_DBG_HTT,
+ "htt rx has FCS err\n");
+
+ if (info.mic_err)
+ ath10k_dbg(ATH10K_DBG_HTT,
+ "htt rx has MIC err\n");
+
info.signal = ATH10K_DEFAULT_NOISE_FLOOR;
info.signal += rx->ppdu.combined_rssi;
info.rate.info0 = rx->ppdu.info0;
info.rate.info1 = __le32_to_cpu(rx->ppdu.info1);
info.rate.info2 = __le32_to_cpu(rx->ppdu.info2);
+ info.tsf = __le32_to_cpu(rx->ppdu.tsf);
hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
@@ -1014,8 +1100,11 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
msdu_head = NULL;
msdu_tail = NULL;
+
+ spin_lock_bh(&htt->rx_ring.lock);
msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
&msdu_head, &msdu_tail);
+ spin_unlock_bh(&htt->rx_ring.lock);
ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
@@ -1095,7 +1184,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
skb_trim(info.skb, info.skb->len - trim);
- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt frag mpdu: ",
+ ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ",
info.skb->data, info.skb->len);
ath10k_process_rx(htt->ar, &info);
@@ -1107,6 +1196,45 @@ end:
}
}
+static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ struct ath10k_htt *htt = &ar->htt;
+ struct htt_resp *resp = (struct htt_resp *)skb->data;
+ struct htt_tx_done tx_done = {};
+ int status = MS(resp->data_tx_completion.flags, HTT_DATA_TX_STATUS);
+ __le16 msdu_id;
+ int i;
+
+ lockdep_assert_held(&htt->tx_lock);
+
+ switch (status) {
+ case HTT_DATA_TX_STATUS_NO_ACK:
+ tx_done.no_ack = true;
+ break;
+ case HTT_DATA_TX_STATUS_OK:
+ break;
+ case HTT_DATA_TX_STATUS_DISCARD:
+ case HTT_DATA_TX_STATUS_POSTPONE:
+ case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
+ tx_done.discard = true;
+ break;
+ default:
+ ath10k_warn("unhandled tx completion status %d\n", status);
+ tx_done.discard = true;
+ break;
+ }
+
+ ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
+ resp->data_tx_completion.num_msdus);
+
+ for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
+ msdu_id = resp->data_tx_completion.msdus[i];
+ tx_done.msdu_id = __le16_to_cpu(msdu_id);
+ ath10k_txrx_tx_unref(htt, &tx_done);
+ }
+}
+
void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
@@ -1116,7 +1244,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
if (!IS_ALIGNED((unsigned long)skb->data, 4))
ath10k_warn("unaligned htt message, expect trouble\n");
- ath10k_dbg(ATH10K_DBG_HTT, "HTT RX, msg_type: 0x%0X\n",
+ ath10k_dbg(ATH10K_DBG_HTT, "htt rx, msg_type: 0x%0X\n",
resp->hdr.msg_type);
switch (resp->hdr.msg_type) {
case HTT_T2H_MSG_TYPE_VERSION_CONF: {
@@ -1125,10 +1253,12 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
complete(&htt->target_version_received);
break;
}
- case HTT_T2H_MSG_TYPE_RX_IND: {
- ath10k_htt_rx_handler(htt, &resp->rx_ind);
- break;
- }
+ case HTT_T2H_MSG_TYPE_RX_IND:
+ spin_lock_bh(&htt->rx_ring.lock);
+ __skb_queue_tail(&htt->rx_compl_q, skb);
+ spin_unlock_bh(&htt->rx_ring.lock);
+ tasklet_schedule(&htt->txrx_compl_task);
+ return;
case HTT_T2H_MSG_TYPE_PEER_MAP: {
struct htt_peer_map_event ev = {
.vdev_id = resp->peer_map.vdev_id,
@@ -1163,44 +1293,17 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
break;
}
+ spin_lock_bh(&htt->tx_lock);
ath10k_txrx_tx_unref(htt, &tx_done);
+ spin_unlock_bh(&htt->tx_lock);
break;
}
- case HTT_T2H_MSG_TYPE_TX_COMPL_IND: {
- struct htt_tx_done tx_done = {};
- int status = MS(resp->data_tx_completion.flags,
- HTT_DATA_TX_STATUS);
- __le16 msdu_id;
- int i;
-
- switch (status) {
- case HTT_DATA_TX_STATUS_NO_ACK:
- tx_done.no_ack = true;
- break;
- case HTT_DATA_TX_STATUS_OK:
- break;
- case HTT_DATA_TX_STATUS_DISCARD:
- case HTT_DATA_TX_STATUS_POSTPONE:
- case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
- tx_done.discard = true;
- break;
- default:
- ath10k_warn("unhandled tx completion status %d\n",
- status);
- tx_done.discard = true;
- break;
- }
-
- ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
- resp->data_tx_completion.num_msdus);
-
- for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
- msdu_id = resp->data_tx_completion.msdus[i];
- tx_done.msdu_id = __le16_to_cpu(msdu_id);
- ath10k_txrx_tx_unref(htt, &tx_done);
- }
- break;
- }
+ case HTT_T2H_MSG_TYPE_TX_COMPL_IND:
+ spin_lock_bh(&htt->tx_lock);
+ __skb_queue_tail(&htt->tx_compl_q, skb);
+ spin_unlock_bh(&htt->tx_lock);
+ tasklet_schedule(&htt->txrx_compl_task);
+ return;
case HTT_T2H_MSG_TYPE_SEC_IND: {
struct ath10k *ar = htt->ar;
struct htt_security_indication *ev = &resp->security_indication;
@@ -1240,3 +1343,25 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
/* Free the indication buffer */
dev_kfree_skb_any(skb);
}
+
+static void ath10k_htt_txrx_compl_task(unsigned long ptr)
+{
+ struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
+ struct htt_resp *resp;
+ struct sk_buff *skb;
+
+ spin_lock_bh(&htt->tx_lock);
+ while ((skb = __skb_dequeue(&htt->tx_compl_q))) {
+ ath10k_htt_rx_frm_tx_compl(htt->ar, skb);
+ dev_kfree_skb_any(skb);
+ }
+ spin_unlock_bh(&htt->tx_lock);
+
+ spin_lock_bh(&htt->rx_ring.lock);
+ while ((skb = __skb_dequeue(&htt->rx_compl_q))) {
+ resp = (struct htt_resp *)skb->data;
+ ath10k_htt_rx_handler(htt, &resp->rx_ind);
+ dev_kfree_skb_any(skb);
+ }
+ spin_unlock_bh(&htt->rx_ring.lock);
+}
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index f1d36d2d2723..7a3e2e40dd5c 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -109,6 +109,14 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
return -ENOMEM;
}
+ htt->tx_pool = dma_pool_create("ath10k htt tx pool", htt->ar->dev,
+ sizeof(struct ath10k_htt_txbuf), 4, 0);
+ if (!htt->tx_pool) {
+ kfree(htt->used_msdu_ids);
+ kfree(htt->pending_tx);
+ return -ENOMEM;
+ }
+
return 0;
}
@@ -117,9 +125,7 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
struct htt_tx_done tx_done = {0};
int msdu_id;
- /* No locks needed. Called after communication with the device has
- * been stopped. */
-
+ spin_lock_bh(&htt->tx_lock);
for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) {
if (!test_bit(msdu_id, htt->used_msdu_ids))
continue;
@@ -132,6 +138,7 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
ath10k_txrx_tx_unref(htt, &tx_done);
}
+ spin_unlock_bh(&htt->tx_lock);
}
void ath10k_htt_tx_detach(struct ath10k_htt *htt)
@@ -139,6 +146,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt)
ath10k_htt_tx_cleanup_pending(htt);
kfree(htt->pending_tx);
kfree(htt->used_msdu_ids);
+ dma_pool_destroy(htt->tx_pool);
return;
}
@@ -334,7 +342,9 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
goto err_free_msdu_id;
}
- res = ath10k_skb_map(dev, msdu);
+ skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len,
+ DMA_TO_DEVICE);
+ res = dma_mapping_error(dev, skb_cb->paddr);
if (res)
goto err_free_txdesc;
@@ -348,8 +358,7 @@ 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.frag_len = 0;
- skb_cb->htt.pad_len = 0;
+ skb_cb->htt.txbuf = NULL;
res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
if (res)
@@ -358,7 +367,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
return 0;
err_unmap_msdu:
- ath10k_skb_unmap(dev, msdu);
+ dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
err_free_txdesc:
dev_kfree_skb_any(txdesc);
err_free_msdu_id:
@@ -375,19 +384,19 @@ err:
int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
{
struct device *dev = htt->ar->dev;
- struct htt_cmd *cmd;
- struct htt_data_tx_desc_frag *tx_frags;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
- struct sk_buff *txdesc = NULL;
- bool use_frags;
- u8 vdev_id = ATH10K_SKB_CB(msdu)->vdev_id;
- u8 tid;
- int prefetch_len, desc_len;
- int msdu_id = -1;
+ struct ath10k_hif_sg_item sg_items[2];
+ struct htt_data_tx_desc_frag *frags;
+ u8 vdev_id = skb_cb->vdev_id;
+ u8 tid = skb_cb->htt.tid;
+ int prefetch_len;
int res;
- u8 flags0;
- u16 flags1;
+ u8 flags0 = 0;
+ u16 msdu_id, flags1 = 0;
+ dma_addr_t paddr;
+ u32 frags_paddr;
+ bool use_frags;
res = ath10k_htt_tx_inc_pending(htt);
if (res)
@@ -406,114 +415,120 @@ 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);
- desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len;
-
- txdesc = ath10k_htc_alloc_skb(desc_len);
- if (!txdesc) {
- res = -ENOMEM;
- goto err_free_msdu_id;
- }
-
/* Since HTT 3.0 there is no separate mgmt tx command. However in case
* of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
* fragment list host driver specifies directly frame pointer. */
use_frags = htt->target_version_major < 3 ||
!ieee80211_is_mgmt(hdr->frame_control);
- if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) {
- ath10k_warn("htt alignment check failed. dropping packet.\n");
- res = -EIO;
- goto err_free_txdesc;
- }
+ skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC,
+ &paddr);
+ if (!skb_cb->htt.txbuf)
+ goto err_free_msdu_id;
+ skb_cb->htt.txbuf_paddr = paddr;
- if (use_frags) {
- skb_cb->htt.frag_len = sizeof(*tx_frags) * 2;
- skb_cb->htt.pad_len = (unsigned long)msdu->data -
- round_down((unsigned long)msdu->data, 4);
+ skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len,
+ DMA_TO_DEVICE);
+ res = dma_mapping_error(dev, skb_cb->paddr);
+ if (res)
+ goto err_free_txbuf;
- skb_push(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len);
- } else {
- skb_cb->htt.frag_len = 0;
- skb_cb->htt.pad_len = 0;
- }
+ if (likely(use_frags)) {
+ frags = skb_cb->htt.txbuf->frags;
- res = ath10k_skb_map(dev, msdu);
- if (res)
- goto err_pull_txfrag;
-
- if (use_frags) {
- dma_sync_single_for_cpu(dev, skb_cb->paddr, msdu->len,
- DMA_TO_DEVICE);
-
- /* tx fragment list must be terminated with zero-entry */
- tx_frags = (struct htt_data_tx_desc_frag *)msdu->data;
- tx_frags[0].paddr = __cpu_to_le32(skb_cb->paddr +
- skb_cb->htt.frag_len +
- skb_cb->htt.pad_len);
- tx_frags[0].len = __cpu_to_le32(msdu->len -
- skb_cb->htt.frag_len -
- skb_cb->htt.pad_len);
- tx_frags[1].paddr = __cpu_to_le32(0);
- tx_frags[1].len = __cpu_to_le32(0);
-
- dma_sync_single_for_device(dev, skb_cb->paddr, msdu->len,
- DMA_TO_DEVICE);
- }
+ frags[0].paddr = __cpu_to_le32(skb_cb->paddr);
+ frags[0].len = __cpu_to_le32(msdu->len);
+ frags[1].paddr = 0;
+ frags[1].len = 0;
- ath10k_dbg(ATH10K_DBG_HTT, "msdu 0x%llx\n",
- (unsigned long long) ATH10K_SKB_CB(msdu)->paddr);
- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ",
- msdu->data, msdu->len);
+ flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
+ HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
- skb_put(txdesc, desc_len);
- cmd = (struct htt_cmd *)txdesc->data;
+ frags_paddr = skb_cb->htt.txbuf_paddr;
+ } else {
+ flags0 |= SM(ATH10K_HW_TXRX_MGMT,
+ HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
- tid = ATH10K_SKB_CB(msdu)->htt.tid;
+ frags_paddr = skb_cb->paddr;
+ }
- ath10k_dbg(ATH10K_DBG_HTT, "htt data tx using tid %hhu\n", tid);
+ /* Normally all commands go through HTC which manages tx credits for
+ * each endpoint and notifies when tx is completed.
+ *
+ * HTT endpoint is creditless so there's no need to care about HTC
+ * flags. In that case it is trivial to fill the HTC header here.
+ *
+ * MSDU transmission is considered completed upon HTT event. This
+ * implies no relevant resources can be freed until after the event is
+ * received. That's why HTC tx completion handler itself is ignored by
+ * setting NULL to transfer_context for all sg items.
+ *
+ * There is simply no point in pushing HTT TX_FRM through HTC tx path
+ * as it's a waste of resources. By bypassing HTC it is possible to
+ * 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;
- flags0 = 0;
if (!ieee80211_has_protected(hdr->frame_control))
flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
- flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
- if (use_frags)
- flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
- HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
- else
- flags0 |= SM(ATH10K_HW_TXRX_MGMT,
- HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
+ flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
- flags1 = 0;
flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
- cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM;
- cmd->data_tx.flags0 = flags0;
- cmd->data_tx.flags1 = __cpu_to_le16(flags1);
- cmd->data_tx.len = __cpu_to_le16(msdu->len -
- skb_cb->htt.frag_len -
- skb_cb->htt.pad_len);
- cmd->data_tx.id = __cpu_to_le16(msdu_id);
- cmd->data_tx.frags_paddr = __cpu_to_le32(skb_cb->paddr);
- cmd->data_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID);
-
- memcpy(cmd->data_tx.prefetch, hdr, prefetch_len);
+ 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_le32(HTT_INVALID_PEERID);
+
+ ath10k_dbg(ATH10K_DBG_HTT,
+ "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu\n",
+ flags0, flags1, msdu->len, msdu_id, frags_paddr,
+ (u32)skb_cb->paddr, vdev_id, tid);
+ ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ",
+ msdu->data, msdu->len);
- res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
+ 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[1].transfer_id = 0;
+ sg_items[1].transfer_context = NULL;
+ sg_items[1].vaddr = msdu->data;
+ sg_items[1].paddr = skb_cb->paddr;
+ sg_items[1].len = prefetch_len;
+
+ res = ath10k_hif_tx_sg(htt->ar,
+ htt->ar->htc.endpoint[htt->eid].ul_pipe_id,
+ sg_items, ARRAY_SIZE(sg_items));
if (res)
goto err_unmap_msdu;
return 0;
err_unmap_msdu:
- ath10k_skb_unmap(dev, msdu);
-err_pull_txfrag:
- skb_pull(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len);
-err_free_txdesc:
- dev_kfree_skb_any(txdesc);
+ dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
+err_free_txbuf:
+ dma_pool_free(htt->tx_pool,
+ skb_cb->htt.txbuf,
+ skb_cb->htt.txbuf_paddr);
err_free_msdu_id:
spin_lock_bh(&htt->tx_lock);
htt->pending_tx[msdu_id] = NULL;
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index f1505a25d810..35fc44e281f5 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -205,8 +205,11 @@ enum ath10k_mcast2ucast_mode {
#define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS 0x0006c000
#define PCIE_LOCAL_BASE_ADDRESS 0x00080000
+#define SOC_RESET_CONTROL_ADDRESS 0x00000000
#define SOC_RESET_CONTROL_OFFSET 0x00000000
#define SOC_RESET_CONTROL_SI0_RST_MASK 0x00000001
+#define SOC_RESET_CONTROL_CE_RST_MASK 0x00040000
+#define SOC_RESET_CONTROL_CPU_WARM_RST_MASK 0x00000040
#define SOC_CPU_CLOCK_OFFSET 0x00000020
#define SOC_CPU_CLOCK_STANDARD_LSB 0
#define SOC_CPU_CLOCK_STANDARD_MASK 0x00000003
@@ -216,6 +219,8 @@ enum ath10k_mcast2ucast_mode {
#define SOC_LPO_CAL_OFFSET 0x000000e0
#define SOC_LPO_CAL_ENABLE_LSB 20
#define SOC_LPO_CAL_ENABLE_MASK 0x00100000
+#define SOC_LF_TIMER_CONTROL0_ADDRESS 0x00000050
+#define SOC_LF_TIMER_CONTROL0_ENABLE_MASK 0x00000004
#define SOC_CHIP_ID_ADDRESS 0x000000ec
#define SOC_CHIP_ID_REV_LSB 8
@@ -273,6 +278,7 @@ enum ath10k_mcast2ucast_mode {
#define PCIE_INTR_CAUSE_ADDRESS 0x000c
#define PCIE_INTR_CLR_ADDRESS 0x0014
#define SCRATCH_3_ADDRESS 0x0030
+#define CPU_INTR_ADDRESS 0x0010
/* Firmware indications to the Host via SCRATCH_3 register. */
#define FW_INDICATOR_ADDRESS (SOC_CORE_BASE_ADDRESS + SCRATCH_3_ADDRESS)
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 776e364eadcd..511a2f81e7af 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -323,13 +323,15 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
if (ret) {
- ath10k_warn("Failed to create wmi peer: %i\n", ret);
+ ath10k_warn("Failed to create wmi peer %pM on vdev %i: %i\n",
+ addr, vdev_id, ret);
return ret;
}
ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
if (ret) {
- ath10k_warn("Failed to wait for created wmi peer: %i\n", ret);
+ ath10k_warn("Failed to wait for created wmi peer %pM on vdev %i: %i\n",
+ addr, vdev_id, ret);
return ret;
}
spin_lock_bh(&ar->data_lock);
@@ -339,6 +341,51 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
return 0;
}
+static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
+{
+ struct ath10k *ar = arvif->ar;
+ u32 param;
+ int ret;
+
+ param = ar->wmi.pdev_param->sta_kickout_th;
+ ret = ath10k_wmi_pdev_set_param(ar, param,
+ ATH10K_KICKOUT_THRESHOLD);
+ if (ret) {
+ ath10k_warn("Failed to set kickout threshold on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+
+ param = ar->wmi.vdev_param->ap_keepalive_min_idle_inactive_time_secs;
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+ ATH10K_KEEPALIVE_MIN_IDLE);
+ if (ret) {
+ ath10k_warn("Failed to set keepalive minimum idle time on vdev %i : %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+
+ param = ar->wmi.vdev_param->ap_keepalive_max_idle_inactive_time_secs;
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+ ATH10K_KEEPALIVE_MAX_IDLE);
+ if (ret) {
+ ath10k_warn("Failed to set keepalive maximum idle time on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+
+ param = ar->wmi.vdev_param->ap_keepalive_max_unresponsive_time_secs;
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+ ATH10K_KEEPALIVE_MAX_UNRESPONSIVE);
+ if (ret) {
+ ath10k_warn("Failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
{
struct ath10k *ar = arvif->ar;
@@ -444,8 +491,7 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
static int ath10k_vdev_start(struct ath10k_vif *arvif)
{
struct ath10k *ar = arvif->ar;
- struct ieee80211_conf *conf = &ar->hw->conf;
- struct ieee80211_channel *channel = conf->chandef.chan;
+ struct cfg80211_chan_def *chandef = &ar->chandef;
struct wmi_vdev_start_request_arg arg = {};
int ret = 0;
@@ -457,16 +503,14 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
arg.dtim_period = arvif->dtim_period;
arg.bcn_intval = arvif->beacon_interval;
- arg.channel.freq = channel->center_freq;
-
- arg.channel.band_center_freq1 = conf->chandef.center_freq1;
-
- arg.channel.mode = chan_to_phymode(&conf->chandef);
+ arg.channel.freq = chandef->chan->center_freq;
+ arg.channel.band_center_freq1 = chandef->center_freq1;
+ arg.channel.mode = chan_to_phymode(chandef);
arg.channel.min_power = 0;
- arg.channel.max_power = channel->max_power * 2;
- arg.channel.max_reg_power = channel->max_reg_power * 2;
- arg.channel.max_antenna_gain = channel->max_antenna_gain * 2;
+ arg.channel.max_power = chandef->chan->max_power * 2;
+ arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
+ arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2;
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
arg.ssid = arvif->u.ap.ssid;
@@ -475,7 +519,7 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
/* For now allow DFS for AP mode */
arg.channel.chan_radar =
- !!(channel->flags & IEEE80211_CHAN_RADAR);
+ !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
} else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
arg.ssid = arvif->vif->bss_conf.ssid;
arg.ssid_len = arvif->vif->bss_conf.ssid_len;
@@ -488,13 +532,15 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
ret = ath10k_wmi_vdev_start(ar, &arg);
if (ret) {
- ath10k_warn("WMI vdev start failed: ret %d\n", ret);
+ ath10k_warn("WMI vdev %i start failed: ret %d\n",
+ arg.vdev_id, ret);
return ret;
}
ret = ath10k_vdev_setup_sync(ar);
if (ret) {
- ath10k_warn("vdev setup failed %d\n", ret);
+ ath10k_warn("vdev %i setup failed %d\n",
+ arg.vdev_id, ret);
return ret;
}
@@ -512,13 +558,15 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif)
ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
if (ret) {
- ath10k_warn("WMI vdev stop failed: ret %d\n", ret);
+ ath10k_warn("WMI vdev %i stop failed: ret %d\n",
+ arvif->vdev_id, ret);
return ret;
}
ret = ath10k_vdev_setup_sync(ar);
if (ret) {
- ath10k_warn("vdev setup failed %d\n", ret);
+ ath10k_warn("vdev %i setup sync failed %d\n",
+ arvif->vdev_id, ret);
return ret;
}
@@ -527,7 +575,8 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif)
static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
{
- struct ieee80211_channel *channel = ar->hw->conf.chandef.chan;
+ struct cfg80211_chan_def *chandef = &ar->chandef;
+ struct ieee80211_channel *channel = chandef->chan;
struct wmi_vdev_start_request_arg arg = {};
int ret = 0;
@@ -540,11 +589,11 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
arg.vdev_id = vdev_id;
arg.channel.freq = channel->center_freq;
- arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1;
+ arg.channel.band_center_freq1 = chandef->center_freq1;
/* TODO setup this dynamically, what in case we
don't have any vifs? */
- arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef);
+ arg.channel.mode = chan_to_phymode(chandef);
arg.channel.chan_radar =
!!(channel->flags & IEEE80211_CHAN_RADAR);
@@ -555,19 +604,22 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
ret = ath10k_wmi_vdev_start(ar, &arg);
if (ret) {
- ath10k_warn("Monitor vdev start failed: ret %d\n", ret);
+ ath10k_warn("Monitor vdev %i start failed: ret %d\n",
+ vdev_id, ret);
return ret;
}
ret = ath10k_vdev_setup_sync(ar);
if (ret) {
- ath10k_warn("Monitor vdev setup failed %d\n", ret);
+ ath10k_warn("Monitor vdev %i setup failed %d\n",
+ vdev_id, ret);
return ret;
}
ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr);
if (ret) {
- ath10k_warn("Monitor vdev up failed: %d\n", ret);
+ ath10k_warn("Monitor vdev %i up failed: %d\n",
+ vdev_id, ret);
goto vdev_stop;
}
@@ -579,7 +631,8 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
vdev_stop:
ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
if (ret)
- ath10k_warn("Monitor vdev stop failed: %d\n", ret);
+ ath10k_warn("Monitor vdev %i stop failed: %d\n",
+ ar->monitor_vdev_id, ret);
return ret;
}
@@ -602,15 +655,18 @@ static int ath10k_monitor_stop(struct ath10k *ar)
ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id);
if (ret)
- ath10k_warn("Monitor vdev down failed: %d\n", ret);
+ ath10k_warn("Monitor vdev %i down failed: %d\n",
+ ar->monitor_vdev_id, ret);
ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
if (ret)
- ath10k_warn("Monitor vdev stop failed: %d\n", ret);
+ ath10k_warn("Monitor vdev %i stop failed: %d\n",
+ ar->monitor_vdev_id, ret);
ret = ath10k_vdev_setup_sync(ar);
if (ret)
- ath10k_warn("Monitor_down sync failed: %d\n", ret);
+ ath10k_warn("Monitor_down sync failed, vdev %i: %d\n",
+ ar->monitor_vdev_id, ret);
ar->monitor_enabled = false;
return ret;
@@ -640,7 +696,8 @@ static int ath10k_monitor_create(struct ath10k *ar)
WMI_VDEV_TYPE_MONITOR,
0, ar->mac_addr);
if (ret) {
- ath10k_warn("WMI vdev monitor create failed: ret %d\n", ret);
+ ath10k_warn("WMI vdev %i monitor create failed: ret %d\n",
+ ar->monitor_vdev_id, ret);
goto vdev_fail;
}
@@ -669,7 +726,8 @@ static int ath10k_monitor_destroy(struct ath10k *ar)
ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
if (ret) {
- ath10k_warn("WMI vdev monitor delete failed: %d\n", ret);
+ ath10k_warn("WMI vdev %i monitor delete failed: %d\n",
+ ar->monitor_vdev_id, ret);
return ret;
}
@@ -791,6 +849,22 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
if (!info->enable_beacon) {
ath10k_vdev_stop(arvif);
+
+ arvif->is_started = false;
+ arvif->is_up = false;
+
+ spin_lock_bh(&arvif->ar->data_lock);
+ if (arvif->beacon) {
+ dma_unmap_single(arvif->ar->dev,
+ ATH10K_SKB_CB(arvif->beacon)->paddr,
+ arvif->beacon->len, DMA_TO_DEVICE);
+ dev_kfree_skb_any(arvif->beacon);
+
+ arvif->beacon = NULL;
+ arvif->beacon_sent = false;
+ }
+ spin_unlock_bh(&arvif->ar->data_lock);
+
return;
}
@@ -800,12 +874,21 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
if (ret)
return;
- ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, 0, info->bssid);
+ arvif->aid = 0;
+ memcpy(arvif->bssid, info->bssid, ETH_ALEN);
+
+ ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
+ arvif->bssid);
if (ret) {
- ath10k_warn("Failed to bring up VDEV: %d\n",
- arvif->vdev_id);
+ ath10k_warn("Failed to bring up vdev %d: %i\n",
+ arvif->vdev_id, ret);
+ ath10k_vdev_stop(arvif);
return;
}
+
+ arvif->is_started = true;
+ arvif->is_up = true;
+
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
}
@@ -824,18 +907,18 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n",
self_peer, arvif->vdev_id, ret);
- if (is_zero_ether_addr(arvif->u.ibss.bssid))
+ if (is_zero_ether_addr(arvif->bssid))
return;
ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
- arvif->u.ibss.bssid);
+ arvif->bssid);
if (ret) {
ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n",
- arvif->u.ibss.bssid, arvif->vdev_id, ret);
+ arvif->bssid, arvif->vdev_id, ret);
return;
}
- memset(arvif->u.ibss.bssid, 0, ETH_ALEN);
+ memset(arvif->bssid, 0, ETH_ALEN);
return;
}
@@ -878,8 +961,8 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
conf->dynamic_ps_timeout);
if (ret) {
- ath10k_warn("Failed to set inactivity time for VDEV: %d\n",
- arvif->vdev_id);
+ ath10k_warn("Failed to set inactivity time for vdev %d: %i\n",
+ arvif->vdev_id, ret);
return ret;
}
} else {
@@ -1017,7 +1100,6 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
struct wmi_peer_assoc_complete_arg *arg)
{
const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
- int smps;
int i, n;
lockdep_assert_held(&ar->conf_mutex);
@@ -1063,17 +1145,6 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
arg->peer_flags |= WMI_PEER_STBC;
}
- smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS;
- smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT;
-
- if (smps == WLAN_HT_CAP_SM_PS_STATIC) {
- arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
- arg->peer_flags |= WMI_PEER_STATIC_MIMOPS;
- } else if (smps == WLAN_HT_CAP_SM_PS_DYNAMIC) {
- arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
- arg->peer_flags |= WMI_PEER_DYN_MIMOPS;
- }
-
if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
arg->peer_rate_caps |= WMI_RC_TS_FLAG;
else if (ht_cap->mcs.rx_mask[1])
@@ -1083,8 +1154,23 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
if (ht_cap->mcs.rx_mask[i/8] & (1 << i%8))
arg->peer_ht_rates.rates[n++] = i;
- arg->peer_ht_rates.num_rates = n;
- arg->peer_num_spatial_streams = max((n+7) / 8, 1);
+ /*
+ * This is a workaround for HT-enabled STAs which break the spec
+ * and have no HT capabilities RX mask (no HT RX MCS map).
+ *
+ * As per spec, in section 20.3.5 Modulation and coding scheme (MCS),
+ * MCS 0 through 7 are mandatory in 20MHz with 800 ns GI at all STAs.
+ *
+ * Firmware asserts if such situation occurs.
+ */
+ if (n == 0) {
+ arg->peer_ht_rates.num_rates = 8;
+ for (i = 0; i < arg->peer_ht_rates.num_rates; i++)
+ arg->peer_ht_rates.rates[i] = i;
+ } else {
+ arg->peer_ht_rates.num_rates = n;
+ arg->peer_num_spatial_streams = sta->rx_nss;
+ }
ath10k_dbg(ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
arg->addr,
@@ -1092,27 +1178,20 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
arg->peer_num_spatial_streams);
}
-static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
- struct ath10k_vif *arvif,
- struct ieee80211_sta *sta,
- struct ieee80211_bss_conf *bss_conf,
- struct wmi_peer_assoc_complete_arg *arg)
+static int ath10k_peer_assoc_qos_ap(struct ath10k *ar,
+ struct ath10k_vif *arvif,
+ struct ieee80211_sta *sta)
{
u32 uapsd = 0;
u32 max_sp = 0;
+ int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
- if (sta->wme)
- arg->peer_flags |= WMI_PEER_QOS;
-
if (sta->wme && sta->uapsd_queues) {
ath10k_dbg(ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n",
sta->uapsd_queues, sta->max_sp);
- arg->peer_flags |= WMI_PEER_APSD;
- arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG;
-
if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
WMI_AP_PS_UAPSD_AC3_TRIGGER_EN;
@@ -1130,35 +1209,40 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP)
max_sp = sta->max_sp;
- ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
- sta->addr,
- WMI_AP_PS_PEER_PARAM_UAPSD,
- uapsd);
+ ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+ sta->addr,
+ WMI_AP_PS_PEER_PARAM_UAPSD,
+ uapsd);
+ if (ret) {
+ ath10k_warn("failed to set ap ps peer param uapsd for vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
- ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
- sta->addr,
- WMI_AP_PS_PEER_PARAM_MAX_SP,
- max_sp);
+ ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+ sta->addr,
+ WMI_AP_PS_PEER_PARAM_MAX_SP,
+ max_sp);
+ if (ret) {
+ ath10k_warn("failed to set ap ps peer param max sp for vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
/* TODO setup this based on STA listen interval and
beacon interval. Currently we don't know
sta->listen_interval - mac80211 patch required.
Currently use 10 seconds */
- ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
- sta->addr,
- WMI_AP_PS_PEER_PARAM_AGEOUT_TIME,
- 10);
+ ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, sta->addr,
+ WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, 10);
+ if (ret) {
+ ath10k_warn("failed to set ap ps peer param ageout time for vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
}
-}
-static void ath10k_peer_assoc_h_qos_sta(struct ath10k *ar,
- struct ath10k_vif *arvif,
- struct ieee80211_sta *sta,
- struct ieee80211_bss_conf *bss_conf,
- struct wmi_peer_assoc_complete_arg *arg)
-{
- if (bss_conf->qos)
- arg->peer_flags |= WMI_PEER_QOS;
+ return 0;
}
static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
@@ -1211,10 +1295,17 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
{
switch (arvif->vdev_type) {
case WMI_VDEV_TYPE_AP:
- ath10k_peer_assoc_h_qos_ap(ar, arvif, sta, bss_conf, arg);
+ if (sta->wme)
+ arg->peer_flags |= WMI_PEER_QOS;
+
+ if (sta->wme && sta->uapsd_queues) {
+ arg->peer_flags |= WMI_PEER_APSD;
+ arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG;
+ }
break;
case WMI_VDEV_TYPE_STA:
- ath10k_peer_assoc_h_qos_sta(ar, arvif, sta, bss_conf, arg);
+ if (bss_conf->qos)
+ arg->peer_flags |= WMI_PEER_QOS;
break;
default:
break;
@@ -1293,6 +1384,33 @@ static int ath10k_peer_assoc_prepare(struct ath10k *ar,
return 0;
}
+static const u32 ath10k_smps_map[] = {
+ [WLAN_HT_CAP_SM_PS_STATIC] = WMI_PEER_SMPS_STATIC,
+ [WLAN_HT_CAP_SM_PS_DYNAMIC] = WMI_PEER_SMPS_DYNAMIC,
+ [WLAN_HT_CAP_SM_PS_INVALID] = WMI_PEER_SMPS_PS_NONE,
+ [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE,
+};
+
+static int ath10k_setup_peer_smps(struct ath10k *ar, struct ath10k_vif *arvif,
+ const u8 *addr,
+ const struct ieee80211_sta_ht_cap *ht_cap)
+{
+ int smps;
+
+ if (!ht_cap->ht_supported)
+ return 0;
+
+ smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS;
+ smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+ if (smps >= ARRAY_SIZE(ath10k_smps_map))
+ return -EINVAL;
+
+ return ath10k_wmi_peer_set_param(ar, arvif->vdev_id, addr,
+ WMI_PEER_SMPS_STATE,
+ ath10k_smps_map[smps]);
+}
+
/* can be called only in mac80211 callbacks due to `key_count` usage */
static void ath10k_bss_assoc(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -1300,6 +1418,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct ieee80211_sta_ht_cap ht_cap;
struct wmi_peer_assoc_complete_arg peer_arg;
struct ieee80211_sta *ap_sta;
int ret;
@@ -1310,17 +1429,21 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
if (!ap_sta) {
- ath10k_warn("Failed to find station entry for %pM\n",
- bss_conf->bssid);
+ ath10k_warn("Failed to find station entry for %pM, vdev %i\n",
+ bss_conf->bssid, arvif->vdev_id);
rcu_read_unlock();
return;
}
+ /* ap_sta must be accessed only within rcu section which must be left
+ * before calling ath10k_setup_peer_smps() which might sleep. */
+ ht_cap = ap_sta->ht_cap;
+
ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta,
bss_conf, &peer_arg);
if (ret) {
- ath10k_warn("Peer assoc prepare failed for %pM\n: %d",
- bss_conf->bssid, ret);
+ ath10k_warn("Peer assoc prepare failed for %pM vdev %i\n: %d",
+ bss_conf->bssid, arvif->vdev_id, ret);
rcu_read_unlock();
return;
}
@@ -1329,8 +1452,15 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
if (ret) {
- ath10k_warn("Peer assoc failed for %pM\n: %d",
- bss_conf->bssid, ret);
+ ath10k_warn("Peer assoc failed for %pM vdev %i\n: %d",
+ bss_conf->bssid, arvif->vdev_id, ret);
+ return;
+ }
+
+ ret = ath10k_setup_peer_smps(ar, arvif, bss_conf->bssid, &ht_cap);
+ if (ret) {
+ ath10k_warn("failed to setup peer SMPS for vdev %i: %d\n",
+ arvif->vdev_id, ret);
return;
}
@@ -1338,11 +1468,17 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
"mac vdev %d up (associated) bssid %pM aid %d\n",
arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
- ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid,
- bss_conf->bssid);
- if (ret)
+ arvif->aid = bss_conf->aid;
+ memcpy(arvif->bssid, bss_conf->bssid, ETH_ALEN);
+
+ ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid);
+ if (ret) {
ath10k_warn("VDEV: %d up failed: ret %d\n",
arvif->vdev_id, ret);
+ return;
+ }
+
+ arvif->is_up = true;
}
/*
@@ -1382,6 +1518,9 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
arvif->def_wep_key_idx = 0;
+
+ arvif->is_started = false;
+ arvif->is_up = false;
}
static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
@@ -1394,21 +1533,35 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg);
if (ret) {
- ath10k_warn("WMI peer assoc prepare failed for %pM\n",
- sta->addr);
+ ath10k_warn("WMI peer assoc prepare failed for %pM vdev %i: %i\n",
+ sta->addr, arvif->vdev_id, ret);
return ret;
}
ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
if (ret) {
- ath10k_warn("Peer assoc failed for STA %pM\n: %d",
- sta->addr, ret);
+ ath10k_warn("Peer assoc failed for STA %pM vdev %i: %d\n",
+ sta->addr, arvif->vdev_id, ret);
+ return ret;
+ }
+
+ ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap);
+ if (ret) {
+ ath10k_warn("failed to setup peer SMPS for vdev: %d\n", ret);
return ret;
}
ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
if (ret) {
- ath10k_warn("could not install peer wep keys (%d)\n", ret);
+ ath10k_warn("could not install peer wep keys for vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+
+ ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
+ if (ret) {
+ ath10k_warn("could not set qos params for STA %pM for vdev %i: %d\n",
+ sta->addr, arvif->vdev_id, ret);
return ret;
}
@@ -1424,7 +1577,8 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
ret = ath10k_clear_peer_keys(arvif, sta->addr);
if (ret) {
- ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
+ ath10k_warn("could not clear all peer wep keys for vdev %i: %d\n",
+ arvif->vdev_id, ret);
return ret;
}
@@ -1547,9 +1701,9 @@ static void ath10k_regd_update(struct ath10k *ar)
/* Target allows setting up per-band regdomain but ath_common provides
* a combined one only */
ret = ath10k_wmi_pdev_set_regdomain(ar,
- regpair->regDmnEnum,
- regpair->regDmnEnum, /* 2ghz */
- regpair->regDmnEnum, /* 5ghz */
+ regpair->reg_domain,
+ regpair->reg_domain, /* 2ghz */
+ regpair->reg_domain, /* 5ghz */
regpair->reg_2ghz_ctl,
regpair->reg_5ghz_ctl);
if (ret)
@@ -2100,11 +2254,29 @@ static int ath10k_start(struct ieee80211_hw *hw)
ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
ret);
+ /*
+ * By default FW set ARP frames ac to voice (6). In that case ARP
+ * exchange is not working properly for UAPSD enabled AP. ARP requests
+ * which arrives with access category 0 are processed by network stack
+ * and send back with access category 0, but FW changes access category
+ * to 6. Set ARP frames access category to best effort (0) solves
+ * this problem.
+ */
+
+ ret = ath10k_wmi_pdev_set_param(ar,
+ ar->wmi.pdev_param->arp_ac_override, 0);
+ if (ret) {
+ ath10k_warn("could not set arp ac override parameter: %d\n",
+ ret);
+ goto exit;
+ }
+
ath10k_regd_update(ar);
+ ret = 0;
exit:
mutex_unlock(&ar->conf_mutex);
- return 0;
+ return ret;
}
static void ath10k_stop(struct ieee80211_hw *hw)
@@ -2145,6 +2317,98 @@ static int ath10k_config_ps(struct ath10k *ar)
return ret;
}
+static const char *chandef_get_width(enum nl80211_chan_width width)
+{
+ switch (width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ return "20 (noht)";
+ case NL80211_CHAN_WIDTH_20:
+ return "20";
+ case NL80211_CHAN_WIDTH_40:
+ return "40";
+ case NL80211_CHAN_WIDTH_80:
+ return "80";
+ case NL80211_CHAN_WIDTH_80P80:
+ return "80+80";
+ case NL80211_CHAN_WIDTH_160:
+ return "160";
+ case NL80211_CHAN_WIDTH_5:
+ return "5";
+ case NL80211_CHAN_WIDTH_10:
+ return "10";
+ }
+ return "?";
+}
+
+static void ath10k_config_chan(struct ath10k *ar)
+{
+ struct ath10k_vif *arvif;
+ bool monitor_was_enabled;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ ath10k_dbg(ATH10K_DBG_MAC,
+ "mac config channel to %dMHz (cf1 %dMHz cf2 %dMHz width %s)\n",
+ ar->chandef.chan->center_freq,
+ ar->chandef.center_freq1,
+ ar->chandef.center_freq2,
+ chandef_get_width(ar->chandef.width));
+
+ /* First stop monitor interface. Some FW versions crash if there's a
+ * lone monitor interface. */
+ monitor_was_enabled = ar->monitor_enabled;
+
+ if (ar->monitor_enabled)
+ ath10k_monitor_stop(ar);
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ if (!arvif->is_started)
+ continue;
+
+ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+ continue;
+
+ ret = ath10k_vdev_stop(arvif);
+ if (ret) {
+ ath10k_warn("could not stop vdev %d (%d)\n",
+ arvif->vdev_id, ret);
+ continue;
+ }
+ }
+
+ /* all vdevs are now stopped - now attempt to restart them */
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ if (!arvif->is_started)
+ continue;
+
+ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+ continue;
+
+ ret = ath10k_vdev_start(arvif);
+ if (ret) {
+ ath10k_warn("could not start vdev %d (%d)\n",
+ arvif->vdev_id, ret);
+ continue;
+ }
+
+ if (!arvif->is_up)
+ continue;
+
+ ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
+ arvif->bssid);
+ if (ret) {
+ ath10k_warn("could not bring vdev up %d (%d)\n",
+ arvif->vdev_id, ret);
+ continue;
+ }
+ }
+
+ if (monitor_was_enabled)
+ ath10k_monitor_start(ar, ar->monitor_vdev_id);
+}
+
static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
{
struct ath10k *ar = hw->priv;
@@ -2165,6 +2429,11 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
spin_unlock_bh(&ar->data_lock);
ath10k_config_radar_detection(ar);
+
+ if (!cfg80211_chandef_identical(&ar->chandef, &conf->chandef)) {
+ ar->chandef = conf->chandef;
+ ath10k_config_chan(ar);
+ }
}
if (changed & IEEE80211_CONF_CHANGE_POWER) {
@@ -2214,7 +2483,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
enum wmi_sta_powersave_param param;
int ret = 0;
- u32 value, param_id;
+ u32 value;
int bit;
u32 vdev_param;
@@ -2276,7 +2545,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
arvif->vdev_subtype, vif->addr);
if (ret) {
- ath10k_warn("WMI vdev create failed: ret %d\n", ret);
+ ath10k_warn("WMI vdev %i create failed: ret %d\n",
+ arvif->vdev_id, ret);
goto err;
}
@@ -2287,7 +2557,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
arvif->def_wep_key_idx);
if (ret) {
- ath10k_warn("Failed to set default keyid: %d\n", ret);
+ ath10k_warn("Failed to set vdev %i default keyid: %d\n",
+ arvif->vdev_id, ret);
goto err_vdev_delete;
}
@@ -2296,23 +2567,25 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
ATH10K_HW_TXRX_NATIVE_WIFI);
/* 10.X firmware does not support this VDEV parameter. Do not warn */
if (ret && ret != -EOPNOTSUPP) {
- ath10k_warn("Failed to set TX encap: %d\n", ret);
+ ath10k_warn("Failed to set vdev %i TX encap: %d\n",
+ arvif->vdev_id, ret);
goto err_vdev_delete;
}
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
if (ret) {
- ath10k_warn("Failed to create peer for AP: %d\n", ret);
+ ath10k_warn("Failed to create vdev %i peer for AP: %d\n",
+ arvif->vdev_id, ret);
goto err_vdev_delete;
}
- param_id = ar->wmi.pdev_param->sta_kickout_th;
-
- /* Disable STA KICKOUT functionality in FW */
- ret = ath10k_wmi_pdev_set_param(ar, param_id, 0);
- if (ret)
- ath10k_warn("Failed to disable STA KICKOUT\n");
+ ret = ath10k_mac_set_kickout(arvif);
+ if (ret) {
+ ath10k_warn("Failed to set vdev %i kickout parameters: %d\n",
+ arvif->vdev_id, ret);
+ goto err_peer_delete;
+ }
}
if (arvif->vdev_type == WMI_VDEV_TYPE_STA) {
@@ -2321,7 +2594,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
param, value);
if (ret) {
- ath10k_warn("Failed to set RX wake policy: %d\n", ret);
+ ath10k_warn("Failed to set vdev %i RX wake policy: %d\n",
+ arvif->vdev_id, ret);
goto err_peer_delete;
}
@@ -2330,7 +2604,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
param, value);
if (ret) {
- ath10k_warn("Failed to set TX wake thresh: %d\n", ret);
+ ath10k_warn("Failed to set vdev %i TX wake thresh: %d\n",
+ arvif->vdev_id, ret);
goto err_peer_delete;
}
@@ -2339,7 +2614,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
param, value);
if (ret) {
- ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
+ ath10k_warn("Failed to set vdev %i PSPOLL count: %d\n",
+ arvif->vdev_id, ret);
goto err_peer_delete;
}
}
@@ -2403,17 +2679,19 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
if (ret)
- ath10k_warn("Failed to remove peer for AP: %d\n", ret);
+ ath10k_warn("Failed to remove peer for AP vdev %i: %d\n",
+ arvif->vdev_id, ret);
kfree(arvif->u.ap.noa_data);
}
- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev delete %d (remove interface)\n",
+ ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %i delete (remove interface)\n",
arvif->vdev_id);
ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
if (ret)
- ath10k_warn("WMI vdev delete failed: %d\n", ret);
+ ath10k_warn("WMI vdev %i delete failed: %d\n",
+ arvif->vdev_id, ret);
if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
ar->monitor_present = false;
@@ -2502,8 +2780,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
arvif->vdev_id, arvif->beacon_interval);
if (ret)
- ath10k_warn("Failed to set beacon interval for VDEV: %d\n",
- arvif->vdev_id);
+ ath10k_warn("Failed to set beacon interval for vdev %d: %i\n",
+ arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_BEACON) {
@@ -2515,8 +2793,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
ret = ath10k_wmi_pdev_set_param(ar, pdev_param,
WMI_BEACON_STAGGERED_MODE);
if (ret)
- ath10k_warn("Failed to set beacon mode for VDEV: %d\n",
- arvif->vdev_id);
+ ath10k_warn("Failed to set beacon mode for vdev %d: %i\n",
+ arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_BEACON_INFO) {
@@ -2530,8 +2808,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
arvif->dtim_period);
if (ret)
- ath10k_warn("Failed to set dtim period for VDEV: %d\n",
- arvif->vdev_id);
+ ath10k_warn("Failed to set dtim period for vdev %d: %i\n",
+ arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_SSID &&
@@ -2551,7 +2829,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
ret = ath10k_peer_create(ar, arvif->vdev_id,
info->bssid);
if (ret)
- ath10k_warn("Failed to add peer %pM for vdev %d when changin bssid: %i\n",
+ ath10k_warn("Failed to add peer %pM for vdev %d when changing bssid: %i\n",
info->bssid, arvif->vdev_id, ret);
if (vif->type == NL80211_IFTYPE_STATION) {
@@ -2559,15 +2837,20 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
* this is never erased as we it for crypto key
* clearing; this is FW requirement
*/
- memcpy(arvif->u.sta.bssid, info->bssid,
- ETH_ALEN);
+ memcpy(arvif->bssid, info->bssid, ETH_ALEN);
ath10k_dbg(ATH10K_DBG_MAC,
"mac vdev %d start %pM\n",
arvif->vdev_id, info->bssid);
- /* FIXME: check return value */
ret = ath10k_vdev_start(arvif);
+ if (ret) {
+ ath10k_warn("failed to start vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ goto exit;
+ }
+
+ arvif->is_started = true;
}
/*
@@ -2576,7 +2859,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
* IBSS in order to remove BSSID peer.
*/
if (vif->type == NL80211_IFTYPE_ADHOC)
- memcpy(arvif->u.ibss.bssid, info->bssid,
+ memcpy(arvif->bssid, info->bssid,
ETH_ALEN);
}
}
@@ -2598,8 +2881,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
cts_prot);
if (ret)
- ath10k_warn("Failed to set CTS prot for VDEV: %d\n",
- arvif->vdev_id);
+ ath10k_warn("Failed to set CTS prot for vdev %d: %d\n",
+ arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_ERP_SLOT) {
@@ -2617,8 +2900,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
slottime);
if (ret)
- ath10k_warn("Failed to set erp slot for VDEV: %d\n",
- arvif->vdev_id);
+ ath10k_warn("Failed to set erp slot for vdev %d: %i\n",
+ arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
@@ -2636,8 +2919,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
preamble);
if (ret)
- ath10k_warn("Failed to set preamble for VDEV: %d\n",
- arvif->vdev_id);
+ ath10k_warn("Failed to set preamble for vdev %d: %i\n",
+ arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_ASSOC) {
@@ -2645,6 +2928,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
ath10k_bss_assoc(hw, vif, info);
}
+exit:
mutex_unlock(&ar->conf_mutex);
}
@@ -2767,8 +3051,8 @@ static void ath10k_set_key_h_def_keyidx(struct ath10k *ar,
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
key->keyidx);
if (ret)
- ath10k_warn("failed to set group key as default key: %d\n",
- ret);
+ ath10k_warn("failed to set vdev %i group key as default key: %d\n",
+ arvif->vdev_id, ret);
}
static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -2828,7 +3112,8 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ret = ath10k_install_key(arvif, key, cmd, peer_addr);
if (ret) {
- ath10k_warn("ath10k_install_key failed (%d)\n", ret);
+ ath10k_warn("key installation failed for vdev %i peer %pM: %d\n",
+ arvif->vdev_id, peer_addr, ret);
goto exit;
}
@@ -2850,6 +3135,69 @@ exit:
return ret;
}
+static void ath10k_sta_rc_update_wk(struct work_struct *wk)
+{
+ struct ath10k *ar;
+ struct ath10k_vif *arvif;
+ struct ath10k_sta *arsta;
+ struct ieee80211_sta *sta;
+ u32 changed, bw, nss, smps;
+ int err;
+
+ arsta = container_of(wk, struct ath10k_sta, update_wk);
+ sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv);
+ arvif = arsta->arvif;
+ ar = arvif->ar;
+
+ spin_lock_bh(&ar->data_lock);
+
+ changed = arsta->changed;
+ arsta->changed = 0;
+
+ bw = arsta->bw;
+ nss = arsta->nss;
+ smps = arsta->smps;
+
+ spin_unlock_bh(&ar->data_lock);
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (changed & IEEE80211_RC_BW_CHANGED) {
+ ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM peer bw %d\n",
+ sta->addr, bw);
+
+ err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+ WMI_PEER_CHAN_WIDTH, bw);
+ if (err)
+ ath10k_warn("failed to update STA %pM peer bw %d: %d\n",
+ sta->addr, bw, err);
+ }
+
+ if (changed & IEEE80211_RC_NSS_CHANGED) {
+ ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM nss %d\n",
+ sta->addr, nss);
+
+ err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+ WMI_PEER_NSS, nss);
+ if (err)
+ ath10k_warn("failed to update STA %pM nss %d: %d\n",
+ sta->addr, nss, err);
+ }
+
+ if (changed & IEEE80211_RC_SMPS_CHANGED) {
+ ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM smps %d\n",
+ sta->addr, smps);
+
+ err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+ WMI_PEER_SMPS_STATE, smps);
+ if (err)
+ ath10k_warn("failed to update STA %pM smps %d: %d\n",
+ sta->addr, smps, err);
+ }
+
+ mutex_unlock(&ar->conf_mutex);
+}
+
static int ath10k_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@@ -2858,9 +3206,22 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
int max_num_peers;
int ret = 0;
+ if (old_state == IEEE80211_STA_NOTEXIST &&
+ new_state == IEEE80211_STA_NONE) {
+ memset(arsta, 0, sizeof(*arsta));
+ arsta->arvif = arvif;
+ INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk);
+ }
+
+ /* cancel must be done outside the mutex to avoid deadlock */
+ if ((old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_NOTEXIST))
+ cancel_work_sync(&arsta->update_wk);
+
mutex_lock(&ar->conf_mutex);
if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -2899,8 +3260,8 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
arvif->vdev_id, sta->addr);
ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
if (ret)
- ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n",
- sta->addr, arvif->vdev_id);
+ ath10k_warn("Failed to delete peer %pM for vdev %d: %i\n",
+ sta->addr, arvif->vdev_id, ret);
if (vif->type == NL80211_IFTYPE_STATION)
ath10k_bss_disassoc(hw, vif);
@@ -2916,8 +3277,8 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
ret = ath10k_station_assoc(ar, arvif, sta);
if (ret)
- ath10k_warn("Failed to associate station: %pM\n",
- sta->addr);
+ ath10k_warn("Failed to associate station %pM for vdev %i: %i\n",
+ sta->addr, arvif->vdev_id, ret);
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH &&
(vif->type == NL80211_IFTYPE_AP ||
@@ -2930,8 +3291,8 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
ret = ath10k_station_disassoc(ar, arvif, sta);
if (ret)
- ath10k_warn("Failed to disassociate station: %pM\n",
- sta->addr);
+ ath10k_warn("Failed to disassociate station: %pM vdev %i ret %i\n",
+ sta->addr, arvif->vdev_id, ret);
}
exit:
mutex_unlock(&ar->conf_mutex);
@@ -3212,7 +3573,8 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
}), ATH10K_FLUSH_TIMEOUT_HZ);
if (ret <= 0 || skip)
- ath10k_warn("tx not flushed\n");
+ ath10k_warn("tx not flushed (skip %i ar-state %i): %i\n",
+ skip, ar->state, ret);
skip:
mutex_unlock(&ar->conf_mutex);
@@ -3234,23 +3596,14 @@ static int ath10k_suspend(struct ieee80211_hw *hw,
struct ath10k *ar = hw->priv;
int ret;
- ar->is_target_paused = false;
+ mutex_lock(&ar->conf_mutex);
- ret = ath10k_wmi_pdev_suspend_target(ar);
+ ret = ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND);
if (ret) {
- ath10k_warn("could not suspend target (%d)\n", ret);
- return 1;
- }
-
- ret = wait_event_interruptible_timeout(ar->event_queue,
- ar->is_target_paused == true,
- 1 * HZ);
- if (ret < 0) {
- ath10k_warn("suspend interrupted (%d)\n", ret);
- goto resume;
- } else if (ret == 0) {
- ath10k_warn("suspend timed out - target pause event never came\n");
- goto resume;
+ if (ret == -ETIMEDOUT)
+ goto resume;
+ ret = 1;
+ goto exit;
}
ret = ath10k_hif_suspend(ar);
@@ -3259,12 +3612,17 @@ static int ath10k_suspend(struct ieee80211_hw *hw,
goto resume;
}
- return 0;
+ ret = 0;
+ goto exit;
resume:
ret = ath10k_wmi_pdev_resume_target(ar);
if (ret)
ath10k_warn("could not resume target (%d)\n", ret);
- return 1;
+
+ ret = 1;
+exit:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
}
static int ath10k_resume(struct ieee80211_hw *hw)
@@ -3272,19 +3630,26 @@ static int ath10k_resume(struct ieee80211_hw *hw)
struct ath10k *ar = hw->priv;
int ret;
+ mutex_lock(&ar->conf_mutex);
+
ret = ath10k_hif_resume(ar);
if (ret) {
ath10k_warn("could not resume hif (%d)\n", ret);
- return 1;
+ ret = 1;
+ goto exit;
}
ret = ath10k_wmi_pdev_resume_target(ar);
if (ret) {
ath10k_warn("could not resume target (%d)\n", ret);
- return 1;
+ ret = 1;
+ goto exit;
}
- return 0;
+ ret = 0;
+exit:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
}
#endif
@@ -3575,7 +3940,8 @@ static bool ath10k_get_fixed_rate_nss(const struct cfg80211_bitrate_mask *mask,
static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
u8 fixed_rate,
- u8 fixed_nss)
+ u8 fixed_nss,
+ u8 force_sgi)
{
struct ath10k *ar = arvif->ar;
u32 vdev_param;
@@ -3584,12 +3950,16 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
mutex_lock(&ar->conf_mutex);
if (arvif->fixed_rate == fixed_rate &&
- arvif->fixed_nss == fixed_nss)
+ arvif->fixed_nss == fixed_nss &&
+ arvif->force_sgi == force_sgi)
goto exit;
if (fixed_rate == WMI_FIXED_RATE_NONE)
ath10k_dbg(ATH10K_DBG_MAC, "mac disable fixed bitrate mask\n");
+ if (force_sgi)
+ ath10k_dbg(ATH10K_DBG_MAC, "mac force sgi\n");
+
vdev_param = ar->wmi.vdev_param->fixed_rate;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
vdev_param, fixed_rate);
@@ -3615,6 +3985,19 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
arvif->fixed_nss = fixed_nss;
+ vdev_param = ar->wmi.vdev_param->sgi;
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+ force_sgi);
+
+ if (ret) {
+ ath10k_warn("Could not set sgi param %d: %d\n",
+ force_sgi, ret);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ arvif->force_sgi = force_sgi;
+
exit:
mutex_unlock(&ar->conf_mutex);
return ret;
@@ -3629,6 +4012,11 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
enum ieee80211_band band = ar->hw->conf.chandef.chan->band;
u8 fixed_rate = WMI_FIXED_RATE_NONE;
u8 fixed_nss = ar->num_rf_chains;
+ u8 force_sgi;
+
+ force_sgi = mask->control[band].gi;
+ if (force_sgi == NL80211_TXRATE_FORCE_LGI)
+ return -EINVAL;
if (!ath10k_default_bitrate_mask(ar, band, mask)) {
if (!ath10k_get_fixed_rate_nss(mask, band,
@@ -3637,7 +4025,113 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
return -EINVAL;
}
- return ath10k_set_fixed_rate_param(arvif, fixed_rate, fixed_nss);
+ if (fixed_rate == WMI_FIXED_RATE_NONE && force_sgi) {
+ ath10k_warn("Could not force SGI usage for default rate settings\n");
+ return -EINVAL;
+ }
+
+ return ath10k_set_fixed_rate_param(arvif, fixed_rate,
+ fixed_nss, force_sgi);
+}
+
+static void ath10k_channel_switch_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_chan_def *chandef)
+{
+ /* there's no need to do anything here. vif->csa_active is enough */
+ return;
+}
+
+static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 changed)
+{
+ struct ath10k *ar = hw->priv;
+ struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+ u32 bw, smps;
+
+ spin_lock_bh(&ar->data_lock);
+
+ ath10k_dbg(ATH10K_DBG_MAC,
+ "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n",
+ sta->addr, changed, sta->bandwidth, sta->rx_nss,
+ sta->smps_mode);
+
+ if (changed & IEEE80211_RC_BW_CHANGED) {
+ bw = WMI_PEER_CHWIDTH_20MHZ;
+
+ switch (sta->bandwidth) {
+ case IEEE80211_STA_RX_BW_20:
+ bw = WMI_PEER_CHWIDTH_20MHZ;
+ break;
+ case IEEE80211_STA_RX_BW_40:
+ bw = WMI_PEER_CHWIDTH_40MHZ;
+ break;
+ case IEEE80211_STA_RX_BW_80:
+ bw = WMI_PEER_CHWIDTH_80MHZ;
+ break;
+ case IEEE80211_STA_RX_BW_160:
+ ath10k_warn("mac sta rc update for %pM: invalid bw %d\n",
+ sta->addr, sta->bandwidth);
+ bw = WMI_PEER_CHWIDTH_20MHZ;
+ break;
+ }
+
+ arsta->bw = bw;
+ }
+
+ if (changed & IEEE80211_RC_NSS_CHANGED)
+ arsta->nss = sta->rx_nss;
+
+ if (changed & IEEE80211_RC_SMPS_CHANGED) {
+ smps = WMI_PEER_SMPS_PS_NONE;
+
+ switch (sta->smps_mode) {
+ case IEEE80211_SMPS_AUTOMATIC:
+ case IEEE80211_SMPS_OFF:
+ smps = WMI_PEER_SMPS_PS_NONE;
+ break;
+ case IEEE80211_SMPS_STATIC:
+ smps = WMI_PEER_SMPS_STATIC;
+ break;
+ case IEEE80211_SMPS_DYNAMIC:
+ smps = WMI_PEER_SMPS_DYNAMIC;
+ break;
+ case IEEE80211_SMPS_NUM_MODES:
+ ath10k_warn("mac sta rc update for %pM: invalid smps: %d\n",
+ sta->addr, sta->smps_mode);
+ smps = WMI_PEER_SMPS_PS_NONE;
+ break;
+ }
+
+ arsta->smps = smps;
+ }
+
+ if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
+ /* FIXME: Not implemented. Probably the only way to do it would
+ * be to re-assoc the peer. */
+ changed &= ~IEEE80211_RC_SUPP_RATES_CHANGED;
+ ath10k_dbg(ATH10K_DBG_MAC,
+ "mac sta rc update for %pM: changing supported rates not implemented\n",
+ sta->addr);
+ }
+
+ arsta->changed |= changed;
+
+ spin_unlock_bh(&ar->data_lock);
+
+ ieee80211_queue_work(hw, &arsta->update_wk);
+}
+
+static u64 ath10k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ /*
+ * FIXME: Return 0 for time being. Need to figure out whether FW
+ * has the API to fetch 64-bit local TSF
+ */
+
+ return 0;
}
static const struct ieee80211_ops ath10k_ops = {
@@ -3663,6 +4157,9 @@ static const struct ieee80211_ops ath10k_ops = {
.restart_complete = ath10k_restart_complete,
.get_survey = ath10k_get_survey,
.set_bitrate_mask = ath10k_set_bitrate_mask,
+ .channel_switch_beacon = ath10k_channel_switch_beacon,
+ .sta_rc_update = ath10k_sta_rc_update,
+ .get_tsf = ath10k_get_tsf,
#ifdef CONFIG_PM
.suspend = ath10k_suspend,
.resume = ath10k_resume,
@@ -3939,7 +4436,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id)
ath10k_get_arvif_iter,
&arvif_iter);
if (!arvif_iter.arvif) {
- ath10k_warn("No VIF found for VDEV: %d\n", vdev_id);
+ ath10k_warn("No VIF found for vdev %d\n", vdev_id);
return NULL;
}
@@ -4020,7 +4517,8 @@ int ath10k_mac_register(struct ath10k *ar)
IEEE80211_HW_HAS_RATE_CONTROL |
IEEE80211_HW_SUPPORTS_STATIC_SMPS |
IEEE80211_HW_WANT_MONITOR_VIF |
- IEEE80211_HW_AP_LINK_PS;
+ IEEE80211_HW_AP_LINK_PS |
+ IEEE80211_HW_SPECTRUM_MGMT;
/* MSDU can have HTT TX fragment pushed in front. The additional 4
* bytes is used for padding/alignment if necessary. */
@@ -4038,10 +4536,12 @@ int ath10k_mac_register(struct ath10k *ar)
ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN;
ar->hw->vif_data_size = sizeof(struct ath10k_vif);
+ ar->hw->sta_data_size = sizeof(struct ath10k_sta);
ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
ar->hw->wiphy->max_remain_on_channel_duration = 5000;
ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
@@ -4076,7 +4576,7 @@ int ath10k_mac_register(struct ath10k *ar)
ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
ath10k_reg_notifier);
if (ret) {
- ath10k_err("Regulatory initialization failed\n");
+ ath10k_err("Regulatory initialization failed: %i\n", ret);
goto err_free;
}
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 29fd197d1fd8..9d242d801d9d 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -58,13 +58,12 @@ static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = {
static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
u32 *data);
-static void ath10k_pci_process_ce(struct ath10k *ar);
static int ath10k_pci_post_rx(struct ath10k *ar);
static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info,
int num);
static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info);
-static void ath10k_pci_stop_ce(struct ath10k *ar);
-static int ath10k_pci_device_reset(struct ath10k *ar);
+static int ath10k_pci_cold_reset(struct ath10k *ar);
+static int ath10k_pci_warm_reset(struct ath10k *ar);
static int ath10k_pci_wait_for_target_init(struct ath10k *ar);
static int ath10k_pci_init_irq(struct ath10k *ar);
static int ath10k_pci_deinit_irq(struct ath10k *ar);
@@ -73,7 +72,6 @@ static void ath10k_pci_free_irq(struct ath10k *ar);
static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe,
struct ath10k_ce_pipe *rx_pipe,
struct bmi_xfer *xfer);
-static void ath10k_pci_cleanup_ce(struct ath10k *ar);
static const struct ce_attr host_ce_config_wlan[] = {
/* CE0: host->target HTC control and raw streams */
@@ -678,34 +676,12 @@ void ath10k_do_pci_sleep(struct ath10k *ar)
}
}
-/*
- * FIXME: Handle OOM properly.
- */
-static inline
-struct ath10k_pci_compl *get_free_compl(struct ath10k_pci_pipe *pipe_info)
-{
- struct ath10k_pci_compl *compl = NULL;
-
- spin_lock_bh(&pipe_info->pipe_lock);
- if (list_empty(&pipe_info->compl_free)) {
- ath10k_warn("Completion buffers are full\n");
- goto exit;
- }
- compl = list_first_entry(&pipe_info->compl_free,
- struct ath10k_pci_compl, list);
- list_del(&compl->list);
-exit:
- spin_unlock_bh(&pipe_info->pipe_lock);
- return compl;
-}
-
/* Called by lower (CE) layer when a send to Target completes. */
static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state)
{
struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id];
- struct ath10k_pci_compl *compl;
+ struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
void *transfer_context;
u32 ce_data;
unsigned int nbytes;
@@ -714,27 +690,12 @@ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state)
while (ath10k_ce_completed_send_next(ce_state, &transfer_context,
&ce_data, &nbytes,
&transfer_id) == 0) {
- compl = get_free_compl(pipe_info);
- if (!compl)
- break;
-
- compl->state = ATH10K_PCI_COMPL_SEND;
- compl->ce_state = ce_state;
- compl->pipe_info = pipe_info;
- compl->skb = transfer_context;
- compl->nbytes = nbytes;
- compl->transfer_id = transfer_id;
- compl->flags = 0;
+ /* no need to call tx completion for NULL pointers */
+ if (transfer_context == NULL)
+ continue;
- /*
- * Add the completion to the processing queue.
- */
- spin_lock_bh(&ar_pci->compl_lock);
- list_add_tail(&compl->list, &ar_pci->compl_process);
- spin_unlock_bh(&ar_pci->compl_lock);
+ cb->tx_completion(ar, transfer_context, transfer_id);
}
-
- ath10k_pci_process_ce(ar);
}
/* Called by lower (CE) layer when data is received from the Target. */
@@ -743,77 +704,100 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id];
- struct ath10k_pci_compl *compl;
+ struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
struct sk_buff *skb;
void *transfer_context;
u32 ce_data;
- unsigned int nbytes;
+ unsigned int nbytes, max_nbytes;
unsigned int transfer_id;
unsigned int flags;
+ int err;
while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
&ce_data, &nbytes, &transfer_id,
&flags) == 0) {
- compl = get_free_compl(pipe_info);
- if (!compl)
- break;
-
- compl->state = ATH10K_PCI_COMPL_RECV;
- compl->ce_state = ce_state;
- compl->pipe_info = pipe_info;
- compl->skb = transfer_context;
- compl->nbytes = nbytes;
- compl->transfer_id = transfer_id;
- compl->flags = flags;
+ err = ath10k_pci_post_rx_pipe(pipe_info, 1);
+ if (unlikely(err)) {
+ /* FIXME: retry */
+ ath10k_warn("failed to replenish CE rx ring %d: %d\n",
+ pipe_info->pipe_num, err);
+ }
skb = transfer_context;
+ max_nbytes = skb->len + skb_tailroom(skb);
dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
- skb->len + skb_tailroom(skb),
- DMA_FROM_DEVICE);
- /*
- * Add the completion to the processing queue.
- */
- spin_lock_bh(&ar_pci->compl_lock);
- list_add_tail(&compl->list, &ar_pci->compl_process);
- spin_unlock_bh(&ar_pci->compl_lock);
- }
+ max_nbytes, DMA_FROM_DEVICE);
+
+ if (unlikely(max_nbytes < nbytes)) {
+ ath10k_warn("rxed more than expected (nbytes %d, max %d)",
+ nbytes, max_nbytes);
+ dev_kfree_skb_any(skb);
+ continue;
+ }
- ath10k_pci_process_ce(ar);
+ skb_put(skb, nbytes);
+ cb->rx_completion(ar, skb, pipe_info->pipe_num);
+ }
}
-/* Send the first nbytes bytes of the buffer */
-static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
- unsigned int transfer_id,
- unsigned int bytes, struct sk_buff *nbuf)
+static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
+ struct ath10k_hif_sg_item *items, int n_items)
{
- struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(nbuf);
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- struct ath10k_pci_pipe *pipe_info = &(ar_pci->pipe_info[pipe_id]);
- struct ath10k_ce_pipe *ce_hdl = pipe_info->ce_hdl;
- unsigned int len;
- u32 flags = 0;
- int ret;
+ struct ath10k_pci_pipe *pci_pipe = &ar_pci->pipe_info[pipe_id];
+ struct ath10k_ce_pipe *ce_pipe = pci_pipe->ce_hdl;
+ struct ath10k_ce_ring *src_ring = ce_pipe->src_ring;
+ unsigned int nentries_mask = src_ring->nentries_mask;
+ unsigned int sw_index = src_ring->sw_index;
+ unsigned int write_index = src_ring->write_index;
+ int err, i;
- len = min(bytes, nbuf->len);
- bytes -= len;
+ spin_lock_bh(&ar_pci->ce_lock);
- if (len & 3)
- ath10k_warn("skb not aligned to 4-byte boundary (%d)\n", len);
+ if (unlikely(CE_RING_DELTA(nentries_mask,
+ write_index, sw_index - 1) < n_items)) {
+ err = -ENOBUFS;
+ goto unlock;
+ }
- ath10k_dbg(ATH10K_DBG_PCI,
- "pci send data vaddr %p paddr 0x%llx len %d as %d bytes\n",
- nbuf->data, (unsigned long long) skb_cb->paddr,
- nbuf->len, len);
- ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
- "ath10k tx: data: ",
- nbuf->data, nbuf->len);
-
- ret = ath10k_ce_send(ce_hdl, nbuf, skb_cb->paddr, len, transfer_id,
- flags);
- if (ret)
- ath10k_warn("failed to send sk_buff to CE: %p\n", nbuf);
+ for (i = 0; i < n_items - 1; i++) {
+ ath10k_dbg(ATH10K_DBG_PCI,
+ "pci tx item %d paddr 0x%08x len %d n_items %d\n",
+ i, items[i].paddr, items[i].len, n_items);
+ ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, "item data: ",
+ items[i].vaddr, items[i].len);
- return ret;
+ err = ath10k_ce_send_nolock(ce_pipe,
+ items[i].transfer_context,
+ items[i].paddr,
+ items[i].len,
+ items[i].transfer_id,
+ CE_SEND_FLAG_GATHER);
+ if (err)
+ goto unlock;
+ }
+
+ /* `i` is equal to `n_items -1` after for() */
+
+ ath10k_dbg(ATH10K_DBG_PCI,
+ "pci tx item %d paddr 0x%08x len %d n_items %d\n",
+ i, items[i].paddr, items[i].len, n_items);
+ ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, "item data: ",
+ items[i].vaddr, items[i].len);
+
+ err = ath10k_ce_send_nolock(ce_pipe,
+ items[i].transfer_context,
+ items[i].paddr,
+ items[i].len,
+ items[i].transfer_id,
+ 0);
+ if (err)
+ goto unlock;
+
+ err = 0;
+unlock:
+ spin_unlock_bh(&ar_pci->ce_lock);
+ return err;
}
static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
@@ -833,9 +817,7 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
ath10k_err("firmware crashed!\n");
ath10k_err("hardware name %s version 0x%x\n",
ar->hw_params.name, ar->target_version);
- ath10k_err("firmware version: %u.%u.%u.%u\n", ar->fw_version_major,
- ar->fw_version_minor, ar->fw_version_release,
- ar->fw_version_build);
+ ath10k_err("firmware version: %s\n", ar->hw->wiphy->fw_version);
host_addr = host_interest_item_address(HI_ITEM(hi_failure_state));
ret = ath10k_pci_diag_read_mem(ar, host_addr,
@@ -904,52 +886,6 @@ static void ath10k_pci_hif_set_callbacks(struct ath10k *ar,
sizeof(ar_pci->msg_callbacks_current));
}
-static int ath10k_pci_alloc_compl(struct ath10k *ar)
-{
- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- const struct ce_attr *attr;
- struct ath10k_pci_pipe *pipe_info;
- struct ath10k_pci_compl *compl;
- int i, pipe_num, completions;
-
- spin_lock_init(&ar_pci->compl_lock);
- INIT_LIST_HEAD(&ar_pci->compl_process);
-
- for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
- pipe_info = &ar_pci->pipe_info[pipe_num];
-
- spin_lock_init(&pipe_info->pipe_lock);
- INIT_LIST_HEAD(&pipe_info->compl_free);
-
- /* Handle Diagnostic CE specially */
- if (pipe_info->ce_hdl == ar_pci->ce_diag)
- continue;
-
- attr = &host_ce_config_wlan[pipe_num];
- completions = 0;
-
- if (attr->src_nentries)
- completions += attr->src_nentries;
-
- if (attr->dest_nentries)
- completions += attr->dest_nentries;
-
- for (i = 0; i < completions; i++) {
- compl = kmalloc(sizeof(*compl), GFP_KERNEL);
- if (!compl) {
- ath10k_warn("No memory for completion state\n");
- ath10k_pci_cleanup_ce(ar);
- return -ENOMEM;
- }
-
- compl->state = ATH10K_PCI_COMPL_FREE;
- list_add_tail(&compl->list, &pipe_info->compl_free);
- }
- }
-
- return 0;
-}
-
static int ath10k_pci_setup_ce_irq(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -994,147 +930,6 @@ static void ath10k_pci_kill_tasklet(struct ath10k *ar)
tasklet_kill(&ar_pci->pipe_info[i].intr);
}
-static void ath10k_pci_stop_ce(struct ath10k *ar)
-{
- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- struct ath10k_pci_compl *compl;
- struct sk_buff *skb;
-
- /* Mark pending completions as aborted, so that upper layers free up
- * their associated resources */
- spin_lock_bh(&ar_pci->compl_lock);
- list_for_each_entry(compl, &ar_pci->compl_process, list) {
- skb = compl->skb;
- ATH10K_SKB_CB(skb)->is_aborted = true;
- }
- spin_unlock_bh(&ar_pci->compl_lock);
-}
-
-static void ath10k_pci_cleanup_ce(struct ath10k *ar)
-{
- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- struct ath10k_pci_compl *compl, *tmp;
- struct ath10k_pci_pipe *pipe_info;
- struct sk_buff *netbuf;
- int pipe_num;
-
- /* Free pending completions. */
- spin_lock_bh(&ar_pci->compl_lock);
- if (!list_empty(&ar_pci->compl_process))
- ath10k_warn("pending completions still present! possible memory leaks.\n");
-
- list_for_each_entry_safe(compl, tmp, &ar_pci->compl_process, list) {
- list_del(&compl->list);
- netbuf = compl->skb;
- dev_kfree_skb_any(netbuf);
- kfree(compl);
- }
- spin_unlock_bh(&ar_pci->compl_lock);
-
- /* Free unused completions for each pipe. */
- for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
- pipe_info = &ar_pci->pipe_info[pipe_num];
-
- spin_lock_bh(&pipe_info->pipe_lock);
- list_for_each_entry_safe(compl, tmp,
- &pipe_info->compl_free, list) {
- list_del(&compl->list);
- kfree(compl);
- }
- spin_unlock_bh(&pipe_info->pipe_lock);
- }
-}
-
-static void ath10k_pci_process_ce(struct ath10k *ar)
-{
- struct ath10k_pci *ar_pci = ar->hif.priv;
- struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
- struct ath10k_pci_compl *compl;
- struct sk_buff *skb;
- unsigned int nbytes;
- int ret, send_done = 0;
-
- /* Upper layers aren't ready to handle tx/rx completions in parallel so
- * we must serialize all completion processing. */
-
- spin_lock_bh(&ar_pci->compl_lock);
- if (ar_pci->compl_processing) {
- spin_unlock_bh(&ar_pci->compl_lock);
- return;
- }
- ar_pci->compl_processing = true;
- spin_unlock_bh(&ar_pci->compl_lock);
-
- for (;;) {
- spin_lock_bh(&ar_pci->compl_lock);
- if (list_empty(&ar_pci->compl_process)) {
- spin_unlock_bh(&ar_pci->compl_lock);
- break;
- }
- compl = list_first_entry(&ar_pci->compl_process,
- struct ath10k_pci_compl, list);
- list_del(&compl->list);
- spin_unlock_bh(&ar_pci->compl_lock);
-
- switch (compl->state) {
- case ATH10K_PCI_COMPL_SEND:
- cb->tx_completion(ar,
- compl->skb,
- compl->transfer_id);
- send_done = 1;
- break;
- case ATH10K_PCI_COMPL_RECV:
- ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1);
- if (ret) {
- ath10k_warn("failed to post RX buffer for pipe %d: %d\n",
- compl->pipe_info->pipe_num, ret);
- break;
- }
-
- skb = compl->skb;
- nbytes = compl->nbytes;
-
- ath10k_dbg(ATH10K_DBG_PCI,
- "ath10k_pci_ce_recv_data netbuf=%p nbytes=%d\n",
- skb, nbytes);
- ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
- "ath10k rx: ", skb->data, nbytes);
-
- if (skb->len + skb_tailroom(skb) >= nbytes) {
- skb_trim(skb, 0);
- skb_put(skb, nbytes);
- cb->rx_completion(ar, skb,
- compl->pipe_info->pipe_num);
- } else {
- ath10k_warn("rxed more than expected (nbytes %d, max %d)",
- nbytes,
- skb->len + skb_tailroom(skb));
- }
- break;
- case ATH10K_PCI_COMPL_FREE:
- ath10k_warn("free completion cannot be processed\n");
- break;
- default:
- ath10k_warn("invalid completion state (%d)\n",
- compl->state);
- break;
- }
-
- compl->state = ATH10K_PCI_COMPL_FREE;
-
- /*
- * Add completion back to the pipe's free list.
- */
- spin_lock_bh(&compl->pipe_info->pipe_lock);
- list_add_tail(&compl->list, &compl->pipe_info->compl_free);
- spin_unlock_bh(&compl->pipe_info->pipe_lock);
- }
-
- spin_lock_bh(&ar_pci->compl_lock);
- ar_pci->compl_processing = false;
- spin_unlock_bh(&ar_pci->compl_lock);
-}
-
/* TODO - temporary mapping while we have too few CE's */
static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar,
u16 service_id, u8 *ul_pipe,
@@ -1306,17 +1101,11 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
ath10k_pci_free_early_irq(ar);
ath10k_pci_kill_tasklet(ar);
- ret = ath10k_pci_alloc_compl(ar);
- if (ret) {
- ath10k_warn("failed to allocate CE completions: %d\n", ret);
- goto err_early_irq;
- }
-
ret = ath10k_pci_request_irq(ar);
if (ret) {
ath10k_warn("failed to post RX buffers for all pipes: %d\n",
ret);
- goto err_free_compl;
+ goto err_early_irq;
}
ret = ath10k_pci_setup_ce_irq(ar);
@@ -1340,10 +1129,6 @@ err_stop:
ath10k_ce_disable_interrupts(ar);
ath10k_pci_free_irq(ar);
ath10k_pci_kill_tasklet(ar);
- ath10k_pci_stop_ce(ar);
- ath10k_pci_process_ce(ar);
-err_free_compl:
- ath10k_pci_cleanup_ce(ar);
err_early_irq:
/* Though there should be no interrupts (device was reset)
* power_down() expects the early IRQ to be installed as per the
@@ -1414,18 +1199,10 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf,
&ce_data, &nbytes, &id) == 0) {
- /*
- * Indicate the completion to higer layer to free
- * the buffer
- */
-
- if (!netbuf) {
- ath10k_warn("invalid sk_buff on CE %d - NULL pointer. firmware crashed?\n",
- ce_hdl->id);
+ /* no need to call tx completion for NULL pointers */
+ if (!netbuf)
continue;
- }
- ATH10K_SKB_CB(netbuf)->is_aborted = true;
ar_pci->msg_callbacks_current.tx_completion(ar,
netbuf,
id);
@@ -1483,7 +1260,6 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
ath10k_pci_free_irq(ar);
ath10k_pci_kill_tasklet(ar);
- ath10k_pci_stop_ce(ar);
ret = ath10k_pci_request_early_irq(ar);
if (ret)
@@ -1493,8 +1269,6 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
* not DMA nor interrupt. We process the leftovers and then free
* everything else up. */
- ath10k_pci_process_ce(ar);
- ath10k_pci_cleanup_ce(ar);
ath10k_pci_buffer_cleanup(ar);
/* Make the sure the device won't access any structures on the host by
@@ -1502,7 +1276,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
* configuration during init. If ringbuffers are freed and the device
* were to access them this could lead to memory corruption on the
* host. */
- ath10k_pci_device_reset(ar);
+ ath10k_pci_warm_reset(ar);
ar_pci->started = 0;
}
@@ -1993,7 +1767,94 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
ath10k_pci_sleep(ar);
}
-static int ath10k_pci_hif_power_up(struct ath10k *ar)
+static int ath10k_pci_warm_reset(struct ath10k *ar)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ int ret = 0;
+ u32 val;
+
+ ath10k_dbg(ATH10K_DBG_BOOT, "boot performing warm chip reset\n");
+
+ ret = ath10k_do_pci_wake(ar);
+ if (ret) {
+ ath10k_err("failed to wake up target: %d\n", ret);
+ return ret;
+ }
+
+ /* debug */
+ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+ PCIE_INTR_CAUSE_ADDRESS);
+ ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val);
+
+ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+ CPU_INTR_ADDRESS);
+ ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
+ val);
+
+ /* disable pending irqs */
+ ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
+ PCIE_INTR_ENABLE_ADDRESS, 0);
+
+ ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
+ PCIE_INTR_CLR_ADDRESS, ~0);
+
+ msleep(100);
+
+ /* clear fw indicator */
+ ath10k_pci_write32(ar, ar_pci->fw_indicator_address, 0);
+
+ /* clear target LF timer interrupts */
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_LF_TIMER_CONTROL0_ADDRESS);
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_LF_TIMER_CONTROL0_ADDRESS,
+ val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK);
+
+ /* reset CE */
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_RESET_CONTROL_ADDRESS);
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+ val | SOC_RESET_CONTROL_CE_RST_MASK);
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_RESET_CONTROL_ADDRESS);
+ msleep(10);
+
+ /* unreset CE */
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+ val & ~SOC_RESET_CONTROL_CE_RST_MASK);
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_RESET_CONTROL_ADDRESS);
+ msleep(10);
+
+ /* debug */
+ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+ PCIE_INTR_CAUSE_ADDRESS);
+ ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val);
+
+ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+ CPU_INTR_ADDRESS);
+ ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
+ val);
+
+ /* CPU warm reset */
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_RESET_CONTROL_ADDRESS);
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+ val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
+
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_RESET_CONTROL_ADDRESS);
+ ath10k_dbg(ATH10K_DBG_BOOT, "boot target reset state: 0x%08x\n", val);
+
+ msleep(100);
+
+ ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset complete\n");
+
+ ath10k_do_pci_sleep(ar);
+ return ret;
+}
+
+static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
const char *irq_mode;
@@ -2009,7 +1870,11 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar)
* is in an unexpected state. We try to catch that here in order to
* reset the Target and retry the probe.
*/
- ret = ath10k_pci_device_reset(ar);
+ if (cold_reset)
+ ret = ath10k_pci_cold_reset(ar);
+ else
+ ret = ath10k_pci_warm_reset(ar);
+
if (ret) {
ath10k_err("failed to reset target: %d\n", ret);
goto err;
@@ -2079,7 +1944,7 @@ err_deinit_irq:
ath10k_pci_deinit_irq(ar);
err_ce:
ath10k_pci_ce_deinit(ar);
- ath10k_pci_device_reset(ar);
+ ath10k_pci_warm_reset(ar);
err_ps:
if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
ath10k_do_pci_sleep(ar);
@@ -2087,6 +1952,34 @@ err:
return ret;
}
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
+{
+ int ret;
+
+ /*
+ * Hardware CUS232 version 2 has some issues with cold reset and the
+ * preferred (and safer) way to perform a device reset is through a
+ * warm reset.
+ *
+ * Warm reset doesn't always work though (notably after a firmware
+ * crash) so fall back to cold reset if necessary.
+ */
+ ret = __ath10k_pci_hif_power_up(ar, false);
+ if (ret) {
+ ath10k_warn("failed to power up target using warm reset (%d), trying cold reset\n",
+ ret);
+
+ ret = __ath10k_pci_hif_power_up(ar, true);
+ if (ret) {
+ ath10k_err("failed to power up target using cold reset too (%d)\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static void ath10k_pci_hif_power_down(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -2094,7 +1987,7 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar)
ath10k_pci_free_early_irq(ar);
ath10k_pci_kill_tasklet(ar);
ath10k_pci_deinit_irq(ar);
- ath10k_pci_device_reset(ar);
+ ath10k_pci_warm_reset(ar);
ath10k_pci_ce_deinit(ar);
if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
@@ -2151,7 +2044,7 @@ static int ath10k_pci_hif_resume(struct ath10k *ar)
#endif
static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
- .send_head = ath10k_pci_hif_send_head,
+ .tx_sg = ath10k_pci_hif_tx_sg,
.exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg,
.start = ath10k_pci_hif_start,
.stop = ath10k_pci_hif_stop,
@@ -2411,11 +2304,10 @@ static int ath10k_pci_init_irq(struct ath10k *ar)
/* Try MSI-X */
if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO && msix_supported) {
ar_pci->num_msi_intrs = MSI_NUM_REQUEST;
- ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs);
- if (ret == 0)
- return 0;
+ ret = pci_enable_msi_range(ar_pci->pdev, ar_pci->num_msi_intrs,
+ ar_pci->num_msi_intrs);
if (ret > 0)
- pci_disable_msi(ar_pci->pdev);
+ return 0;
/* fall-through */
}
@@ -2482,6 +2374,8 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar)
case MSI_NUM_REQUEST:
pci_disable_msi(ar_pci->pdev);
return 0;
+ default:
+ pci_disable_msi(ar_pci->pdev);
}
ath10k_warn("unknown irq configuration upon deinit\n");
@@ -2523,7 +2417,7 @@ out:
return ret;
}
-static int ath10k_pci_device_reset(struct ath10k *ar)
+static int ath10k_pci_cold_reset(struct ath10k *ar)
{
int i, ret;
u32 val;
diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h
index a4f32038c440..b43fdb4f7319 100644
--- a/drivers/net/wireless/ath/ath10k/pci.h
+++ b/drivers/net/wireless/ath/ath10k/pci.h
@@ -43,23 +43,6 @@ struct bmi_xfer {
u32 resp_len;
};
-enum ath10k_pci_compl_state {
- ATH10K_PCI_COMPL_FREE = 0,
- ATH10K_PCI_COMPL_SEND,
- ATH10K_PCI_COMPL_RECV,
-};
-
-struct ath10k_pci_compl {
- struct list_head list;
- enum ath10k_pci_compl_state state;
- struct ath10k_ce_pipe *ce_state;
- struct ath10k_pci_pipe *pipe_info;
- struct sk_buff *skb;
- unsigned int nbytes;
- unsigned int transfer_id;
- unsigned int flags;
-};
-
/*
* PCI-specific Target state
*
@@ -175,9 +158,6 @@ struct ath10k_pci_pipe {
/* protects compl_free and num_send_allowed */
spinlock_t pipe_lock;
- /* List of free CE completion slots */
- struct list_head compl_free;
-
struct ath10k_pci *ar_pci;
struct tasklet_struct intr;
};
@@ -205,14 +185,6 @@ struct ath10k_pci {
atomic_t keep_awake_count;
bool verified_awake;
- /* List of CE completions to be processed */
- struct list_head compl_process;
-
- /* protects compl_processing and compl_process */
- spinlock_t compl_lock;
-
- bool compl_processing;
-
struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX];
struct ath10k_hif_cb msg_callbacks_current;
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 74f45fa6f428..0541dd939ce9 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -51,7 +51,8 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
struct ieee80211_tx_info *info;
struct ath10k_skb_cb *skb_cb;
struct sk_buff *msdu;
- int ret;
+
+ lockdep_assert_held(&htt->tx_lock);
ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n",
tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack);
@@ -65,12 +66,12 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
msdu = htt->pending_tx[tx_done->msdu_id];
skb_cb = ATH10K_SKB_CB(msdu);
- ret = ath10k_skb_unmap(dev, msdu);
- if (ret)
- ath10k_warn("data skb unmap failed (%d)\n", ret);
+ dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
- if (skb_cb->htt.frag_len)
- skb_pull(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len);
+ if (skb_cb->htt.txbuf)
+ dma_pool_free(htt->tx_pool,
+ skb_cb->htt.txbuf,
+ skb_cb->htt.txbuf_paddr);
ath10k_report_offchan_tx(htt->ar, msdu);
@@ -92,13 +93,11 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
/* we do not own the msdu anymore */
exit:
- spin_lock_bh(&htt->tx_lock);
htt->pending_tx[tx_done->msdu_id] = NULL;
ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
__ath10k_htt_tx_dec_pending(htt);
if (htt->num_pending_tx == 0)
wake_up(&htt->empty_tx_wq);
- spin_unlock_bh(&htt->tx_lock);
}
static const u8 rx_legacy_rate_idx[] = {
@@ -204,7 +203,7 @@ static void process_rx_rates(struct ath10k *ar, struct htt_rx_info *info,
break;
/* 80MHZ */
case 2:
- status->flag |= RX_FLAG_80MHZ;
+ status->vht_flag |= RX_VHT_FLAG_80MHZ;
}
status->flag |= RX_FLAG_VHT;
@@ -258,20 +257,26 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
status->band = ch->band;
status->freq = ch->center_freq;
+ if (info->rate.info0 & HTT_RX_INDICATION_INFO0_END_VALID) {
+ /* TSF available only in 32-bit */
+ status->mactime = info->tsf & 0xffffffff;
+ status->flag |= RX_FLAG_MACTIME_END;
+ }
+
ath10k_dbg(ATH10K_DBG_DATA,
- "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u\n",
+ "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i\n",
info->skb,
info->skb->len,
status->flag == 0 ? "legacy" : "",
status->flag & RX_FLAG_HT ? "ht" : "",
status->flag & RX_FLAG_VHT ? "vht" : "",
status->flag & RX_FLAG_40MHZ ? "40" : "",
- status->flag & RX_FLAG_80MHZ ? "80" : "",
+ status->vht_flag & RX_VHT_FLAG_80MHZ ? "80" : "",
status->flag & RX_FLAG_SHORT_GI ? "sgi " : "",
status->rate_idx,
status->vht_nss,
status->freq,
- status->band);
+ status->band, status->flag, info->fcs_err);
ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
info->skb->data, info->skb->len);
@@ -378,7 +383,8 @@ void ath10k_peer_unmap_event(struct ath10k_htt *htt,
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find_by_id(ar, ev->peer_id);
if (!peer) {
- ath10k_warn("unknown peer id %d\n", ev->peer_id);
+ ath10k_warn("peer-unmap-event: unknown peer id %d\n",
+ ev->peer_id);
goto exit;
}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 712a606a080a..cb1f7b5bcf4c 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -213,7 +213,7 @@ static struct wmi_cmd_map wmi_10x_cmd_map = {
.p2p_go_set_beacon_ie = WMI_10X_P2P_GO_SET_BEACON_IE,
.p2p_go_set_probe_resp_ie = WMI_10X_P2P_GO_SET_PROBE_RESP_IE,
.p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED,
- .ap_ps_peer_param_cmdid = WMI_CMD_UNSUPPORTED,
+ .ap_ps_peer_param_cmdid = WMI_10X_AP_PS_PEER_PARAM_CMDID,
.ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED,
.peer_rate_retry_sched_cmdid = WMI_10X_PEER_RATE_RETRY_SCHED_CMDID,
.wlan_profile_trigger_cmdid = WMI_10X_WLAN_PROFILE_TRIGGER_CMDID,
@@ -420,7 +420,6 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
.bcnflt_stats_update_period = WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
.pmf_qos = WMI_PDEV_PARAM_PMF_QOS,
.arp_ac_override = WMI_PDEV_PARAM_ARP_AC_OVERRIDE,
- .arpdhcp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED,
.dcs = WMI_PDEV_PARAM_DCS,
.ani_enable = WMI_PDEV_PARAM_ANI_ENABLE,
.ani_poll_period = WMI_PDEV_PARAM_ANI_POLL_PERIOD,
@@ -472,8 +471,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
.bcnflt_stats_update_period =
WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
.pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS,
- .arp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED,
- .arpdhcp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
+ .arp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
.dcs = WMI_10X_PDEV_PARAM_DCS,
.ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE,
.ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD,
@@ -561,7 +559,6 @@ err_pull:
static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
{
- struct wmi_bcn_tx_arg arg = {0};
int ret;
lockdep_assert_held(&arvif->ar->data_lock);
@@ -569,18 +566,16 @@ static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
if (arvif->beacon == NULL)
return;
- arg.vdev_id = arvif->vdev_id;
- arg.tx_rate = 0;
- arg.tx_power = 0;
- arg.bcn = arvif->beacon->data;
- arg.bcn_len = arvif->beacon->len;
+ if (arvif->beacon_sent)
+ return;
- ret = ath10k_wmi_beacon_send_nowait(arvif->ar, &arg);
+ ret = ath10k_wmi_beacon_send_ref_nowait(arvif);
if (ret)
return;
- dev_kfree_skb_any(arvif->beacon);
- arvif->beacon = NULL;
+ /* We need to retain the arvif->beacon reference for DMA unmapping and
+ * freeing the skbuff later. */
+ arvif->beacon_sent = true;
}
static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac,
@@ -1116,7 +1111,27 @@ static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar,
static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar,
struct sk_buff *skb)
{
- ath10k_dbg(ATH10K_DBG_WMI, "WMI_PEER_STA_KICKOUT_EVENTID\n");
+ struct wmi_peer_sta_kickout_event *ev;
+ struct ieee80211_sta *sta;
+
+ ev = (struct wmi_peer_sta_kickout_event *)skb->data;
+
+ ath10k_dbg(ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n",
+ ev->peer_macaddr.addr);
+
+ rcu_read_lock();
+
+ sta = ieee80211_find_sta_by_ifaddr(ar->hw, ev->peer_macaddr.addr, NULL);
+ if (!sta) {
+ ath10k_warn("Spurious quick kickout for STA %pM\n",
+ ev->peer_macaddr.addr);
+ goto exit;
+ }
+
+ ieee80211_report_low_ack(sta, 10);
+
+exit:
+ rcu_read_unlock();
}
/*
@@ -1217,6 +1232,13 @@ static void ath10k_wmi_update_tim(struct ath10k *ar,
tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast);
memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
+ if (tim->dtim_count == 0) {
+ ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true;
+
+ if (__le32_to_cpu(bcn_info->tim_info.tim_mcast) == 1)
+ ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true;
+ }
+
ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n",
tim->dtim_count, tim->dtim_period,
tim->bitmap_ctrl, pvm_len);
@@ -1338,7 +1360,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
struct wmi_bcn_info *bcn_info;
struct ath10k_vif *arvif;
struct sk_buff *bcn;
- int vdev_id = 0;
+ int ret, vdev_id = 0;
ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n");
@@ -1385,6 +1407,17 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
continue;
}
+ /* There are no completions for beacons so wait for next SWBA
+ * before telling mac80211 to decrement CSA counter
+ *
+ * Once CSA counter is completed stop sending beacons until
+ * actual channel switch is done */
+ if (arvif->vif->csa_active &&
+ ieee80211_csa_is_complete(arvif->vif)) {
+ ieee80211_csa_finish(arvif->vif);
+ continue;
+ }
+
bcn = ieee80211_beacon_get(ar->hw, arvif->vif);
if (!bcn) {
ath10k_warn("could not get mac80211 beacon\n");
@@ -1396,15 +1429,33 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info);
spin_lock_bh(&ar->data_lock);
+
if (arvif->beacon) {
- ath10k_warn("SWBA overrun on vdev %d\n",
- arvif->vdev_id);
+ if (!arvif->beacon_sent)
+ ath10k_warn("SWBA overrun on vdev %d\n",
+ arvif->vdev_id);
+
+ dma_unmap_single(arvif->ar->dev,
+ ATH10K_SKB_CB(arvif->beacon)->paddr,
+ arvif->beacon->len, DMA_TO_DEVICE);
dev_kfree_skb_any(arvif->beacon);
}
+ ATH10K_SKB_CB(bcn)->paddr = dma_map_single(arvif->ar->dev,
+ bcn->data, bcn->len,
+ DMA_TO_DEVICE);
+ ret = dma_mapping_error(arvif->ar->dev,
+ ATH10K_SKB_CB(bcn)->paddr);
+ if (ret) {
+ ath10k_warn("failed to map beacon: %d\n", ret);
+ goto skip;
+ }
+
arvif->beacon = bcn;
+ arvif->beacon_sent = false;
ath10k_wmi_tx_beacon_nowait(arvif);
+skip:
spin_unlock_bh(&ar->data_lock);
}
}
@@ -2031,11 +2082,11 @@ static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN);
ath10k_dbg(ATH10K_DBG_WMI,
- "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n",
+ "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d skb->len %i ev-sz %zu\n",
__le32_to_cpu(ev->sw_version),
__le32_to_cpu(ev->abi_version),
ev->mac_addr.addr,
- __le32_to_cpu(ev->status));
+ __le32_to_cpu(ev->status), skb->len, sizeof(*ev));
complete(&ar->wmi.unified_ready);
return 0;
@@ -2403,7 +2454,7 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
ar->wmi.cmd->pdev_set_channel_cmdid);
}
-int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt)
{
struct wmi_pdev_suspend_cmd *cmd;
struct sk_buff *skb;
@@ -2413,7 +2464,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
return -ENOMEM;
cmd = (struct wmi_pdev_suspend_cmd *)skb->data;
- cmd->suspend_opt = WMI_PDEV_SUSPEND;
+ cmd->suspend_opt = __cpu_to_le32(suspend_opt);
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid);
}
@@ -3342,7 +3393,6 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar,
ci->max_power = ch->max_power;
ci->reg_power = ch->max_reg_power;
ci->antenna_max = ch->max_antenna_gain;
- ci->antenna_max = 0;
/* mode & flags share storage */
ci->mode = ch->mode;
@@ -3411,25 +3461,41 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid);
}
-int ath10k_wmi_beacon_send_nowait(struct ath10k *ar,
- const struct wmi_bcn_tx_arg *arg)
+/* This function assumes the beacon is already DMA mapped */
+int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif)
{
- struct wmi_bcn_tx_cmd *cmd;
+ struct wmi_bcn_tx_ref_cmd *cmd;
struct sk_buff *skb;
+ struct sk_buff *beacon = arvif->beacon;
+ struct ath10k *ar = arvif->ar;
+ struct ieee80211_hdr *hdr;
int ret;
+ u16 fc;
- skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len);
+ skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
if (!skb)
return -ENOMEM;
- cmd = (struct wmi_bcn_tx_cmd *)skb->data;
- cmd->hdr.vdev_id = __cpu_to_le32(arg->vdev_id);
- cmd->hdr.tx_rate = __cpu_to_le32(arg->tx_rate);
- cmd->hdr.tx_power = __cpu_to_le32(arg->tx_power);
- cmd->hdr.bcn_len = __cpu_to_le32(arg->bcn_len);
- memcpy(cmd->bcn, arg->bcn, arg->bcn_len);
+ hdr = (struct ieee80211_hdr *)beacon->data;
+ fc = le16_to_cpu(hdr->frame_control);
+
+ cmd = (struct wmi_bcn_tx_ref_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(arvif->vdev_id);
+ cmd->data_len = __cpu_to_le32(beacon->len);
+ cmd->data_ptr = __cpu_to_le32(ATH10K_SKB_CB(beacon)->paddr);
+ cmd->msdu_id = 0;
+ cmd->frame_control = __cpu_to_le32(fc);
+ cmd->flags = 0;
+
+ if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero)
+ cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO);
+
+ if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab)
+ cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB);
+
+ ret = ath10k_wmi_cmd_send_nowait(ar, skb,
+ ar->wmi.cmd->pdev_send_bcn_cmdid);
- ret = ath10k_wmi_cmd_send_nowait(ar, skb, ar->wmi.cmd->bcn_tx_cmdid);
if (ret)
dev_kfree_skb(skb);
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 4b5e7d3d32b6..4fcc96aa9513 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -2277,7 +2277,6 @@ struct wmi_pdev_param_map {
u32 bcnflt_stats_update_period;
u32 pmf_qos;
u32 arp_ac_override;
- u32 arpdhcp_ac_override;
u32 dcs;
u32 ani_enable;
u32 ani_poll_period;
@@ -3403,6 +3402,24 @@ struct wmi_bcn_tx_arg {
const void *bcn;
};
+enum wmi_bcn_tx_ref_flags {
+ WMI_BCN_TX_REF_FLAG_DTIM_ZERO = 0x1,
+ WMI_BCN_TX_REF_FLAG_DELIVER_CAB = 0x2,
+};
+
+struct wmi_bcn_tx_ref_cmd {
+ __le32 vdev_id;
+ __le32 data_len;
+ /* physical address of the frame - dma pointer */
+ __le32 data_ptr;
+ /* id for host to track */
+ __le32 msdu_id;
+ /* frame ctrl to setup PPDU desc */
+ __le32 frame_control;
+ /* to control CABQ traffic: WMI_BCN_TX_REF_FLAG_ */
+ __le32 flags;
+} __packed;
+
/* Beacon filter */
#define WMI_BCN_FILTER_ALL 0 /* Filter all beacons */
#define WMI_BCN_FILTER_NONE 1 /* Pass all beacons */
@@ -3859,6 +3876,12 @@ enum wmi_peer_smps_state {
WMI_PEER_SMPS_DYNAMIC = 0x2
};
+enum wmi_peer_chwidth {
+ WMI_PEER_CHWIDTH_20MHZ = 0,
+ WMI_PEER_CHWIDTH_40MHZ = 1,
+ WMI_PEER_CHWIDTH_80MHZ = 2,
+};
+
enum wmi_peer_param {
WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */
WMI_PEER_AMPDU = 0x2,
@@ -4039,6 +4062,10 @@ struct wmi_chan_info_event {
__le32 cycle_count;
} __packed;
+struct wmi_peer_sta_kickout_event {
+ struct wmi_mac_addr peer_macaddr;
+} __packed;
+
#define WMI_CHAN_INFO_FLAG_COMPLETE BIT(0)
/* FIXME: empirically extrapolated */
@@ -4172,7 +4199,7 @@ int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar);
int ath10k_wmi_connect_htc_service(struct ath10k *ar);
int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
const struct wmi_channel_arg *);
-int ath10k_wmi_pdev_suspend_target(struct ath10k *ar);
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt);
int ath10k_wmi_pdev_resume_target(struct ath10k *ar);
int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
u16 rd5g, u16 ctl2g, u16 ctl5g);
@@ -4219,8 +4246,7 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
enum wmi_ap_ps_peer_param param_id, u32 value);
int ath10k_wmi_scan_chan_list(struct ath10k *ar,
const struct wmi_scan_chan_list_arg *arg);
-int ath10k_wmi_beacon_send_nowait(struct ath10k *ar,
- const struct wmi_bcn_tx_arg *arg);
+int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif);
int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
const struct wmi_pdev_set_wmm_params_arg *arg);
int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index ef35da84f63b..4b18434ba697 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -751,6 +751,9 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len,
DMA_TO_DEVICE);
+ if (dma_mapping_error(ah->dev, bf->skbaddr))
+ return -ENOSPC;
+
ieee80211_get_tx_rates(info->control.vif, (control) ? control->sta : NULL, skb, bf->rates,
ARRAY_SIZE(bf->rates));
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index 4ee01f654235..afb23b3cc7be 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -681,6 +681,7 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
survey->channel = conf->chandef.chan;
survey->noise = ah->ah_noise_floor;
survey->filled = SURVEY_INFO_NOISE_DBM |
+ SURVEY_INFO_IN_USE |
SURVEY_INFO_CHANNEL_TIME |
SURVEY_INFO_CHANNEL_TIME_BUSY |
SURVEY_INFO_CHANNEL_TIME_RX |
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index fd4c89df67e1..c2c6f4604958 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -790,7 +790,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
if (nw_type & ADHOC_NETWORK) {
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
nw_type & ADHOC_CREATOR ? "creator" : "joiner");
- cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
+ cfg80211_ibss_joined(vif->ndev, bssid, chan, GFP_KERNEL);
cfg80211_put_bss(ar->wiphy, bss);
return;
}
@@ -861,13 +861,9 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
}
if (vif->nw_type & ADHOC_NETWORK) {
- if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
+ if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC)
ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: ath6k not in ibss mode\n", __func__);
- return;
- }
- memset(bssid, 0, ETH_ALEN);
- cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
return;
}
@@ -3256,6 +3252,15 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
struct ath6kl_vif *vif = netdev_priv(dev);
u16 interval;
int ret, rssi_thold;
+ int n_match_sets = request->n_match_sets;
+
+ /*
+ * If there's a matchset w/o an SSID, then assume it's just for
+ * the RSSI (nothing else is currently supported) and ignore it.
+ * The device only supports a global RSSI filter that we set below.
+ */
+ if (n_match_sets == 1 && !request->match_sets[0].ssid.ssid_len)
+ n_match_sets = 0;
if (ar->state != ATH6KL_STATE_ON)
return -EIO;
@@ -3268,11 +3273,11 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
request->n_ssids,
request->match_sets,
- request->n_match_sets);
+ n_match_sets);
if (ret < 0)
return ret;
- if (!request->n_match_sets) {
+ if (!n_match_sets) {
ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
ALL_BSS_FILTER, 0);
if (ret < 0)
@@ -3286,12 +3291,12 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD,
ar->fw_capabilities)) {
- if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)
+ if (request->min_rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)
rssi_thold = 0;
- else if (request->rssi_thold < -127)
+ else if (request->min_rssi_thold < -127)
rssi_thold = -127;
else
- rssi_thold = request->rssi_thold;
+ rssi_thold = request->min_rssi_thold;
ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx,
rssi_thold);
diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c
index f38ff6a6255e..56c3fd5cef65 100644
--- a/drivers/net/wireless/ath/ath6kl/usb.c
+++ b/drivers/net/wireless/ath/ath6kl/usb.c
@@ -24,7 +24,7 @@
/* constants */
#define TX_URB_COUNT 32
#define RX_URB_COUNT 32
-#define ATH6KL_USB_RX_BUFFER_SIZE 1700
+#define ATH6KL_USB_RX_BUFFER_SIZE 4096
/* tx/rx pipes for usb */
enum ATH6KL_USB_PIPE_ID {
@@ -481,8 +481,8 @@ static void ath6kl_usb_start_recv_pipes(struct ath6kl_usb *ar_usb)
* ATH6KL_USB_RX_BUFFER_SIZE);
*/
- ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh =
- ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_alloc / 2;
+ ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh = 1;
+
ath6kl_usb_post_recv_transfers(&ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA],
ATH6KL_USB_RX_BUFFER_SIZE);
}
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index 4f16d79c9eb1..8b4ce28e3ce8 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -914,7 +914,7 @@ ath6kl_get_regpair(u16 regdmn)
return NULL;
for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
- if (regDomainPairs[i].regDmnEnum == regdmn)
+ if (regDomainPairs[i].reg_domain == regdmn)
return &regDomainPairs[i];
}
@@ -954,7 +954,7 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
country = ath6kl_regd_find_country_by_rd((u16) reg_code);
if (regpair)
ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n",
- regpair->regDmnEnum);
+ regpair->reg_domain);
else
ath6kl_warn("Regpair not found reg_code 0x%0x\n",
reg_code);
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index 7b96b3e5712d..8fcc029a76a6 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -120,18 +120,6 @@ config ATH9K_WOW
This option enables Wake on Wireless LAN support for certain cards.
Currently, AR9462 is supported.
-config ATH9K_LEGACY_RATE_CONTROL
- bool "Atheros ath9k rate control"
- depends on ATH9K
- default n
- ---help---
- Say Y, if you want to use the ath9k specific rate control
- module instead of minstrel_ht. Be warned that there are various
- issues with the ath9k RC and minstrel is a more robust algorithm.
- Note that even if this option is selected, "ath9k_rate_control"
- has to be passed to mac80211 using the module parameter,
- ieee80211_default_rc_algo.
-
config ATH9K_RFKILL
bool "Atheros ath9k rfkill support" if EXPERT
depends on ATH9K
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index a40e5c5d7418..8e1c7b0fe76c 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -8,7 +8,6 @@ ath9k-y += beacon.o \
antenna.o
ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o
-ath9k-$(CONFIG_ATH9K_LEGACY_RATE_CONTROL) += rc.o
ath9k-$(CONFIG_ATH9K_PCI) += pci.o
ath9k-$(CONFIG_ATH9K_AHB) += ahb.o
ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
@@ -52,7 +51,9 @@ ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \
obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o
obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o
-ath9k_common-y:= common.o
+ath9k_common-y:= common.o \
+ common-init.o \
+ common-beacon.o
ath9k_htc-y += htc_hst.o \
hif_usb.o \
diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c
index 2dff2765769b..a0398fe3eb28 100644
--- a/drivers/net/wireless/ath/ath9k/ahb.c
+++ b/drivers/net/wireless/ath/ath9k/ahb.c
@@ -39,6 +39,10 @@ static const struct platform_device_id ath9k_platform_id_table[] = {
.name = "qca955x_wmac",
.driver_data = AR9300_DEVID_QCA955X,
},
+ {
+ .name = "qca953x_wmac",
+ .driver_data = AR9300_DEVID_AR953X,
+ },
{},
};
@@ -82,6 +86,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
int irq;
int ret = 0;
struct ath_hw *ah;
+ struct ath_common *common;
char hw_name[64];
if (!dev_get_platdata(&pdev->dev)) {
@@ -124,9 +129,6 @@ static int ath_ahb_probe(struct platform_device *pdev)
sc->mem = mem;
sc->irq = irq;
- /* Will be cleared in ath9k_start() */
- set_bit(SC_OP_INVALID, &sc->sc_flags);
-
ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc);
if (ret) {
dev_err(&pdev->dev, "request_irq failed\n");
@@ -144,6 +146,9 @@ static int ath_ahb_probe(struct platform_device *pdev)
wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n",
hw_name, (unsigned long)mem, irq);
+ common = ath9k_hw_common(sc->sc_ah);
+ /* Will be cleared in ath9k_start() */
+ set_bit(ATH_OP_INVALID, &common->op_flags);
return 0;
err_irq:
diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c
index d28923b7435b..6d47783f2e5b 100644
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -176,16 +176,26 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,
if (ah->opmode == NL80211_IFTYPE_STATION &&
BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH)
weak_sig = true;
-
/*
- * OFDM Weak signal detection is always enabled for AP mode.
+ * Newer chipsets are better at dealing with high PHY error counts -
+ * keep weak signal detection enabled when no RSSI threshold is
+ * available to determine if it is needed (mode != STA)
*/
- if (ah->opmode != NL80211_IFTYPE_AP &&
- aniState->ofdmWeakSigDetect != weak_sig) {
- ath9k_hw_ani_control(ah,
- ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
- entry_ofdm->ofdm_weak_signal_on);
- }
+ else if (AR_SREV_9300_20_OR_LATER(ah) &&
+ ah->opmode != NL80211_IFTYPE_STATION)
+ weak_sig = true;
+
+ /* Older chipsets are more sensitive to high PHY error counts */
+ else if (!AR_SREV_9300_20_OR_LATER(ah) &&
+ aniState->ofdmNoiseImmunityLevel >= 8)
+ weak_sig = false;
+
+ if (aniState->ofdmWeakSigDetect != weak_sig)
+ ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+ weak_sig);
+
+ if (!AR_SREV_9300_20_OR_LATER(ah))
+ return;
if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) {
ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
@@ -308,17 +318,6 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
BUG_ON(aniState == NULL);
ah->stats.ast_ani_reset++;
- /* only allow a subset of functions in AP mode */
- if (ah->opmode == NL80211_IFTYPE_AP) {
- if (IS_CHAN_2GHZ(chan)) {
- ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL |
- ATH9K_ANI_FIRSTEP_LEVEL);
- if (AR_SREV_9300_20_OR_LATER(ah))
- ah->ani_function |= ATH9K_ANI_MRC_CCK;
- } else
- ah->ani_function = 0;
- }
-
ofdm_nil = max_t(int, ATH9K_ANI_OFDM_DEF_LEVEL,
aniState->ofdmNoiseImmunityLevel);
cck_nil = max_t(int, ATH9K_ANI_CCK_DEF_LEVEL,
@@ -483,10 +482,17 @@ void ath9k_hw_ani_init(struct ath_hw *ah)
ath_dbg(common, ANI, "Initialize ANI\n");
- ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
- ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;
- ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH;
- ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW;
+ if (AR_SREV_9300_20_OR_LATER(ah)) {
+ ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
+ ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;
+ ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH;
+ ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW;
+ } else {
+ ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_OLD;
+ ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_OLD;
+ ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_OLD;
+ ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_OLD;
+ }
ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h
index 21e7b83c3f6a..c40965b4c1e2 100644
--- a/drivers/net/wireless/ath/ath9k/ani.h
+++ b/drivers/net/wireless/ath/ath9k/ani.h
@@ -22,12 +22,16 @@
/* units are errors per second */
#define ATH9K_ANI_OFDM_TRIG_HIGH 3500
#define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000
+#define ATH9K_ANI_OFDM_TRIG_HIGH_OLD 500
#define ATH9K_ANI_OFDM_TRIG_LOW 400
#define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900
+#define ATH9K_ANI_OFDM_TRIG_LOW_OLD 200
#define ATH9K_ANI_CCK_TRIG_HIGH 600
+#define ATH9K_ANI_CCK_TRIG_HIGH_OLD 200
#define ATH9K_ANI_CCK_TRIG_LOW 300
+#define ATH9K_ANI_CCK_TRIG_LOW_OLD 100
#define ATH9K_ANI_SPUR_IMMUNE_LVL 3
#define ATH9K_ANI_FIRSTEP_LVL 2
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
index ff415e863ee9..3b3e91057a4c 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -26,10 +26,6 @@ static const int firstep_table[] =
/* level: 0 1 2 3 4 5 6 7 8 */
{ -4, -2, 0, 2, 4, 6, 8, 10, 12 }; /* lvl 0-8, default 2 */
-static const int cycpwrThr1_table[] =
-/* level: 0 1 2 3 4 5 6 7 8 */
- { -6, -4, -2, 0, 2, 4, 6, 8 }; /* lvl 0-7, default 3 */
-
/*
* register values to turn OFDM weak signal detection OFF
*/
@@ -921,7 +917,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_channel *chan = ah->curchan;
struct ar5416AniState *aniState = &ah->ani;
- s32 value, value2;
+ s32 value;
switch (cmd & ah->ani_function) {
case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{
@@ -1008,42 +1004,11 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
case ATH9K_ANI_FIRSTEP_LEVEL:{
u32 level = param;
- if (level >= ARRAY_SIZE(firstep_table)) {
- ath_dbg(common, ANI,
- "ATH9K_ANI_FIRSTEP_LEVEL: level out of range (%u > %zu)\n",
- level, ARRAY_SIZE(firstep_table));
- return false;
- }
-
- /*
- * make register setting relative to default
- * from INI file & cap value
- */
- value = firstep_table[level] -
- firstep_table[ATH9K_ANI_FIRSTEP_LVL] +
- aniState->iniDef.firstep;
- if (value < ATH9K_SIG_FIRSTEP_SETTING_MIN)
- value = ATH9K_SIG_FIRSTEP_SETTING_MIN;
- if (value > ATH9K_SIG_FIRSTEP_SETTING_MAX)
- value = ATH9K_SIG_FIRSTEP_SETTING_MAX;
+ value = level * 2;
REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
- AR_PHY_FIND_SIG_FIRSTEP,
- value);
- /*
- * we need to set first step low register too
- * make register setting relative to default
- * from INI file & cap value
- */
- value2 = firstep_table[level] -
- firstep_table[ATH9K_ANI_FIRSTEP_LVL] +
- aniState->iniDef.firstepLow;
- if (value2 < ATH9K_SIG_FIRSTEP_SETTING_MIN)
- value2 = ATH9K_SIG_FIRSTEP_SETTING_MIN;
- if (value2 > ATH9K_SIG_FIRSTEP_SETTING_MAX)
- value2 = ATH9K_SIG_FIRSTEP_SETTING_MAX;
-
+ AR_PHY_FIND_SIG_FIRSTEP, value);
REG_RMW_FIELD(ah, AR_PHY_FIND_SIG_LOW,
- AR_PHY_FIND_SIG_FIRSTEP_LOW, value2);
+ AR_PHY_FIND_SIG_FIRSTEP_LOW, value);
if (level != aniState->firstepLevel) {
ath_dbg(common, ANI,
@@ -1060,7 +1025,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
aniState->firstepLevel,
level,
ATH9K_ANI_FIRSTEP_LVL,
- value2,
+ value,
aniState->iniDef.firstepLow);
if (level > aniState->firstepLevel)
ah->stats.ast_ani_stepup++;
@@ -1073,41 +1038,13 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{
u32 level = param;
- if (level >= ARRAY_SIZE(cycpwrThr1_table)) {
- ath_dbg(common, ANI,
- "ATH9K_ANI_SPUR_IMMUNITY_LEVEL: level out of range (%u > %zu)\n",
- level, ARRAY_SIZE(cycpwrThr1_table));
- return false;
- }
- /*
- * make register setting relative to default
- * from INI file & cap value
- */
- value = cycpwrThr1_table[level] -
- cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] +
- aniState->iniDef.cycpwrThr1;
- if (value < ATH9K_SIG_SPUR_IMM_SETTING_MIN)
- value = ATH9K_SIG_SPUR_IMM_SETTING_MIN;
- if (value > ATH9K_SIG_SPUR_IMM_SETTING_MAX)
- value = ATH9K_SIG_SPUR_IMM_SETTING_MAX;
+ value = (level + 1) * 2;
REG_RMW_FIELD(ah, AR_PHY_TIMING5,
- AR_PHY_TIMING5_CYCPWR_THR1,
- value);
+ AR_PHY_TIMING5_CYCPWR_THR1, value);
- /*
- * set AR_PHY_EXT_CCA for extension channel
- * make register setting relative to default
- * from INI file & cap value
- */
- value2 = cycpwrThr1_table[level] -
- cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] +
- aniState->iniDef.cycpwrThr1Ext;
- if (value2 < ATH9K_SIG_SPUR_IMM_SETTING_MIN)
- value2 = ATH9K_SIG_SPUR_IMM_SETTING_MIN;
- if (value2 > ATH9K_SIG_SPUR_IMM_SETTING_MAX)
- value2 = ATH9K_SIG_SPUR_IMM_SETTING_MAX;
- REG_RMW_FIELD(ah, AR_PHY_EXT_CCA,
- AR_PHY_EXT_TIMING5_CYCPWR_THR1, value2);
+ if (IS_CHAN_HT40(ah->curchan))
+ REG_RMW_FIELD(ah, AR_PHY_EXT_CCA,
+ AR_PHY_EXT_TIMING5_CYCPWR_THR1, value);
if (level != aniState->spurImmunityLevel) {
ath_dbg(common, ANI,
@@ -1124,7 +1061,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
aniState->spurImmunityLevel,
level,
ATH9K_ANI_SPUR_IMMUNE_LVL,
- value2,
+ value,
aniState->iniDef.cycpwrThr1Ext);
if (level > aniState->spurImmunityLevel)
ah->stats.ast_ani_spurup++;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
index a352128c40ad..ac8301ef5242 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
@@ -23,10 +23,11 @@
#define MAX_MEASUREMENT MAX_IQCAL_MEASUREMENT
#define MAX_MAG_DELTA 11
#define MAX_PHS_DELTA 10
+#define MAXIQCAL 3
struct coeff {
- int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
- int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
+ int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL];
+ int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL];
int iqc_coeff[2];
};
@@ -655,9 +656,6 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
if (i2_m_q2_a0_d1 > 0x800)
i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1);
- if (i2_p_q2_a0_d1 > 0x1000)
- i2_p_q2_a0_d1 = -((0x1fff - i2_p_q2_a0_d1) + 1);
-
if (iq_corr_a0_d1 > 0x800)
iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1);
@@ -800,7 +798,7 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
if (q_q_coff > 63)
q_q_coff = 63;
- iqc_coeff[0] = (q_q_coff * 128) + q_i_coff;
+ iqc_coeff[0] = (q_q_coff * 128) + (0x7f & q_i_coff);
ath_dbg(common, CALIBRATE, "tx chain %d: iq corr coeff=%x\n",
chain_idx, iqc_coeff[0]);
@@ -831,7 +829,7 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
if (q_q_coff > 63)
q_q_coff = 63;
- iqc_coeff[1] = (q_q_coff * 128) + q_i_coff;
+ iqc_coeff[1] = (q_q_coff * 128) + (0x7f & q_i_coff);
ath_dbg(common, CALIBRATE, "rx chain %d: iq corr coeff=%x\n",
chain_idx, iqc_coeff[1]);
@@ -839,7 +837,8 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
return true;
}
-static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
+static void ar9003_hw_detect_outlier(int mp_coeff[][MAXIQCAL],
+ int nmeasurement,
int max_delta)
{
int mp_max = -64, max_idx = 0;
@@ -848,20 +847,20 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
/* find min/max mismatch across all calibrated gains */
for (i = 0; i < nmeasurement; i++) {
- if (mp_coeff[i] > mp_max) {
- mp_max = mp_coeff[i];
+ if (mp_coeff[i][0] > mp_max) {
+ mp_max = mp_coeff[i][0];
max_idx = i;
- } else if (mp_coeff[i] < mp_min) {
- mp_min = mp_coeff[i];
+ } else if (mp_coeff[i][0] < mp_min) {
+ mp_min = mp_coeff[i][0];
min_idx = i;
}
}
/* find average (exclude max abs value) */
for (i = 0; i < nmeasurement; i++) {
- if ((abs(mp_coeff[i]) < abs(mp_max)) ||
- (abs(mp_coeff[i]) < abs(mp_min))) {
- mp_avg += mp_coeff[i];
+ if ((abs(mp_coeff[i][0]) < abs(mp_max)) ||
+ (abs(mp_coeff[i][0]) < abs(mp_min))) {
+ mp_avg += mp_coeff[i][0];
mp_count++;
}
}
@@ -873,7 +872,7 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
if (mp_count)
mp_avg /= mp_count;
else
- mp_avg = mp_coeff[nmeasurement - 1];
+ mp_avg = mp_coeff[nmeasurement - 1][0];
/* detect outlier */
if (abs(mp_max - mp_min) > max_delta) {
@@ -882,15 +881,16 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
else
outlier_idx = min_idx;
- mp_coeff[outlier_idx] = mp_avg;
+ mp_coeff[outlier_idx][0] = mp_avg;
}
}
-static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
- struct coeff *coeff,
- bool is_reusable)
+static void ar9003_hw_tx_iq_cal_outlier_detection(struct ath_hw *ah,
+ struct coeff *coeff,
+ bool is_reusable)
{
int i, im, nmeasurement;
+ int magnitude, phase;
u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS];
struct ath9k_hw_cal_data *caldata = ah->caldata;
@@ -920,21 +920,30 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
if (nmeasurement > MAX_MEASUREMENT)
nmeasurement = MAX_MEASUREMENT;
- /* detect outlier only if nmeasurement > 1 */
- if (nmeasurement > 1) {
- /* Detect magnitude outlier */
- ar9003_hw_detect_outlier(coeff->mag_coeff[i],
- nmeasurement, MAX_MAG_DELTA);
-
- /* Detect phase outlier */
- ar9003_hw_detect_outlier(coeff->phs_coeff[i],
- nmeasurement, MAX_PHS_DELTA);
+ /*
+ * Skip normal outlier detection for AR9550.
+ */
+ if (!AR_SREV_9550(ah)) {
+ /* detect outlier only if nmeasurement > 1 */
+ if (nmeasurement > 1) {
+ /* Detect magnitude outlier */
+ ar9003_hw_detect_outlier(coeff->mag_coeff[i],
+ nmeasurement,
+ MAX_MAG_DELTA);
+
+ /* Detect phase outlier */
+ ar9003_hw_detect_outlier(coeff->phs_coeff[i],
+ nmeasurement,
+ MAX_PHS_DELTA);
+ }
}
for (im = 0; im < nmeasurement; im++) {
+ magnitude = coeff->mag_coeff[i][im][0];
+ phase = coeff->phs_coeff[i][im][0];
- coeff->iqc_coeff[0] = (coeff->mag_coeff[i][im] & 0x7f) |
- ((coeff->phs_coeff[i][im] & 0x7f) << 7);
+ coeff->iqc_coeff[0] =
+ (phase & 0x7f) | ((magnitude & 0x7f) << 7);
if ((im % 2) == 0)
REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
@@ -991,7 +1000,63 @@ static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah)
return true;
}
-static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
+static void __ar955x_tx_iq_cal_sort(struct ath_hw *ah,
+ struct coeff *coeff,
+ int i, int nmeasurement)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+ int im, ix, iy, temp;
+
+ for (im = 0; im < nmeasurement; im++) {
+ for (ix = 0; ix < MAXIQCAL - 1; ix++) {
+ for (iy = ix + 1; iy <= MAXIQCAL - 1; iy++) {
+ if (coeff->mag_coeff[i][im][iy] <
+ coeff->mag_coeff[i][im][ix]) {
+ temp = coeff->mag_coeff[i][im][ix];
+ coeff->mag_coeff[i][im][ix] =
+ coeff->mag_coeff[i][im][iy];
+ coeff->mag_coeff[i][im][iy] = temp;
+ }
+ if (coeff->phs_coeff[i][im][iy] <
+ coeff->phs_coeff[i][im][ix]) {
+ temp = coeff->phs_coeff[i][im][ix];
+ coeff->phs_coeff[i][im][ix] =
+ coeff->phs_coeff[i][im][iy];
+ coeff->phs_coeff[i][im][iy] = temp;
+ }
+ }
+ }
+ coeff->mag_coeff[i][im][0] = coeff->mag_coeff[i][im][MAXIQCAL / 2];
+ coeff->phs_coeff[i][im][0] = coeff->phs_coeff[i][im][MAXIQCAL / 2];
+
+ ath_dbg(common, CALIBRATE,
+ "IQCAL: Median [ch%d][gain%d]: mag = %d phase = %d\n",
+ i, im,
+ coeff->mag_coeff[i][im][0],
+ coeff->phs_coeff[i][im][0]);
+ }
+}
+
+static bool ar955x_tx_iq_cal_median(struct ath_hw *ah,
+ struct coeff *coeff,
+ int iqcal_idx,
+ int nmeasurement)
+{
+ int i;
+
+ if ((iqcal_idx + 1) != MAXIQCAL)
+ return false;
+
+ for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+ __ar955x_tx_iq_cal_sort(ah, coeff, i, nmeasurement);
+ }
+
+ return true;
+}
+
+static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah,
+ int iqcal_idx,
+ bool is_reusable)
{
struct ath_common *common = ath9k_hw_common(ah);
const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
@@ -1004,10 +1069,11 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
AR_PHY_CHAN_INFO_TAB_1,
AR_PHY_CHAN_INFO_TAB_2,
};
- struct coeff coeff;
+ static struct coeff coeff;
s32 iq_res[6];
int i, im, j;
- int nmeasurement;
+ int nmeasurement = 0;
+ bool outlier_detect = true;
for (i = 0; i < AR9300_MAX_CHAINS; i++) {
if (!(ah->txchainmask & (1 << i)))
@@ -1065,17 +1131,23 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
goto tx_iqcal_fail;
}
- coeff.mag_coeff[i][im] = coeff.iqc_coeff[0] & 0x7f;
- coeff.phs_coeff[i][im] =
+ coeff.phs_coeff[i][im][iqcal_idx] =
+ coeff.iqc_coeff[0] & 0x7f;
+ coeff.mag_coeff[i][im][iqcal_idx] =
(coeff.iqc_coeff[0] >> 7) & 0x7f;
- if (coeff.mag_coeff[i][im] > 63)
- coeff.mag_coeff[i][im] -= 128;
- if (coeff.phs_coeff[i][im] > 63)
- coeff.phs_coeff[i][im] -= 128;
+ if (coeff.mag_coeff[i][im][iqcal_idx] > 63)
+ coeff.mag_coeff[i][im][iqcal_idx] -= 128;
+ if (coeff.phs_coeff[i][im][iqcal_idx] > 63)
+ coeff.phs_coeff[i][im][iqcal_idx] -= 128;
}
}
- ar9003_hw_tx_iqcal_load_avg_2_passes(ah, &coeff, is_reusable);
+
+ if (AR_SREV_9550(ah))
+ outlier_detect = ar955x_tx_iq_cal_median(ah, &coeff,
+ iqcal_idx, nmeasurement);
+ if (outlier_detect)
+ ar9003_hw_tx_iq_cal_outlier_detection(ah, &coeff, is_reusable);
return;
@@ -1409,7 +1481,7 @@ skip_tx_iqcal:
}
if (txiqcal_done)
- ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable);
+ ar9003_hw_tx_iq_cal_post_proc(ah, 0, is_reusable);
else if (caldata && test_bit(TXIQCAL_DONE, &caldata->cal_flags))
ar9003_hw_tx_iq_cal_reload(ah);
@@ -1455,14 +1527,38 @@ skip_tx_iqcal:
return true;
}
+static bool do_ar9003_agc_cal(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+ bool status;
+
+ REG_WRITE(ah, AR_PHY_AGC_CONTROL,
+ REG_READ(ah, AR_PHY_AGC_CONTROL) |
+ AR_PHY_AGC_CONTROL_CAL);
+
+ status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
+ AR_PHY_AGC_CONTROL_CAL,
+ 0, AH_WAIT_TIMEOUT);
+ if (!status) {
+ ath_dbg(common, CALIBRATE,
+ "offset calibration failed to complete in %d ms,"
+ "noisy environment?\n",
+ AH_WAIT_TIMEOUT / 1000);
+ return false;
+ }
+
+ return true;
+}
+
static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
struct ath9k_channel *chan)
{
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_hw_cal_data *caldata = ah->caldata;
bool txiqcal_done = false;
- bool is_reusable = true, status = true;
+ bool status = true;
bool run_agc_cal = false, sep_iq_cal = false;
+ int i = 0;
/* Use chip chainmask only for calibration */
ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask);
@@ -1485,7 +1581,12 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
* AGC calibration. Specifically, AR9550 in SoC chips.
*/
if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) {
- txiqcal_done = true;
+ if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0,
+ AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)) {
+ txiqcal_done = true;
+ } else {
+ txiqcal_done = false;
+ }
run_agc_cal = true;
} else {
sep_iq_cal = true;
@@ -1512,27 +1613,37 @@ skip_tx_iqcal:
if (AR_SREV_9330_11(ah))
ar9003_hw_manual_peak_cal(ah, 0, IS_CHAN_2GHZ(chan));
- /* Calibrate the AGC */
- REG_WRITE(ah, AR_PHY_AGC_CONTROL,
- REG_READ(ah, AR_PHY_AGC_CONTROL) |
- AR_PHY_AGC_CONTROL_CAL);
-
- /* Poll for offset calibration complete */
- status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
- AR_PHY_AGC_CONTROL_CAL,
- 0, AH_WAIT_TIMEOUT);
- }
+ /*
+ * For non-AR9550 chips, we just trigger AGC calibration
+ * in the HW, poll for completion and then process
+ * the results.
+ *
+ * For AR955x, we run it multiple times and use
+ * median IQ correction.
+ */
+ if (!AR_SREV_9550(ah)) {
+ status = do_ar9003_agc_cal(ah);
+ if (!status)
+ return false;
- if (!status) {
- ath_dbg(common, CALIBRATE,
- "offset calibration failed to complete in %d ms; noisy environment?\n",
- AH_WAIT_TIMEOUT / 1000);
- return false;
+ if (txiqcal_done)
+ ar9003_hw_tx_iq_cal_post_proc(ah, 0, false);
+ } else {
+ if (!txiqcal_done) {
+ status = do_ar9003_agc_cal(ah);
+ if (!status)
+ return false;
+ } else {
+ for (i = 0; i < MAXIQCAL; i++) {
+ status = do_ar9003_agc_cal(ah);
+ if (!status)
+ return false;
+ ar9003_hw_tx_iq_cal_post_proc(ah, i, false);
+ }
+ }
+ }
}
- if (txiqcal_done)
- ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable);
-
/* Revert chainmask to runtime parameters */
ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index b8daff78b9d1..235053ba7737 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -23,8 +23,8 @@
#define COMP_HDR_LEN 4
#define COMP_CKSUM_LEN 2
-#define LE16(x) __constant_cpu_to_le16(x)
-#define LE32(x) __constant_cpu_to_le32(x)
+#define LE16(x) cpu_to_le16(x)
+#define LE32(x) cpu_to_le32(x)
/* Local defines to distinguish between extension and control CTL's */
#define EXT_ADDITIVE (0x8000)
@@ -4792,43 +4792,54 @@ static void ar9003_hw_power_control_override(struct ath_hw *ah,
tempslope:
if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
+ u8 txmask = (eep->baseEepHeader.txrxMask & 0xf0) >> 4;
+
/*
* AR955x has tempSlope register for each chain.
* Check whether temp_compensation feature is enabled or not.
*/
if (eep->baseEepHeader.featureEnable & 0x1) {
if (frequency < 4000) {
- REG_RMW_FIELD(ah, AR_PHY_TPC_19,
- AR_PHY_TPC_19_ALPHA_THERM,
- eep->base_ext2.tempSlopeLow);
- REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
- AR_PHY_TPC_19_ALPHA_THERM,
- temp_slope);
- REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2,
- AR_PHY_TPC_19_ALPHA_THERM,
- eep->base_ext2.tempSlopeHigh);
+ if (txmask & BIT(0))
+ REG_RMW_FIELD(ah, AR_PHY_TPC_19,
+ AR_PHY_TPC_19_ALPHA_THERM,
+ eep->base_ext2.tempSlopeLow);
+ if (txmask & BIT(1))
+ REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
+ AR_PHY_TPC_19_ALPHA_THERM,
+ temp_slope);
+ if (txmask & BIT(2))
+ REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2,
+ AR_PHY_TPC_19_ALPHA_THERM,
+ eep->base_ext2.tempSlopeHigh);
} else {
- REG_RMW_FIELD(ah, AR_PHY_TPC_19,
- AR_PHY_TPC_19_ALPHA_THERM,
- temp_slope);
- REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
- AR_PHY_TPC_19_ALPHA_THERM,
- temp_slope1);
- REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2,
- AR_PHY_TPC_19_ALPHA_THERM,
- temp_slope2);
+ if (txmask & BIT(0))
+ REG_RMW_FIELD(ah, AR_PHY_TPC_19,
+ AR_PHY_TPC_19_ALPHA_THERM,
+ temp_slope);
+ if (txmask & BIT(1))
+ REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
+ AR_PHY_TPC_19_ALPHA_THERM,
+ temp_slope1);
+ if (txmask & BIT(2))
+ REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2,
+ AR_PHY_TPC_19_ALPHA_THERM,
+ temp_slope2);
}
} else {
/*
* If temp compensation is not enabled,
* set all registers to 0.
*/
- REG_RMW_FIELD(ah, AR_PHY_TPC_19,
- AR_PHY_TPC_19_ALPHA_THERM, 0);
- REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
- AR_PHY_TPC_19_ALPHA_THERM, 0);
- REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2,
- AR_PHY_TPC_19_ALPHA_THERM, 0);
+ if (txmask & BIT(0))
+ REG_RMW_FIELD(ah, AR_PHY_TPC_19,
+ AR_PHY_TPC_19_ALPHA_THERM, 0);
+ if (txmask & BIT(1))
+ REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
+ AR_PHY_TPC_19_ALPHA_THERM, 0);
+ if (txmask & BIT(2))
+ REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2,
+ AR_PHY_TPC_19_ALPHA_THERM, 0);
}
} else {
REG_RMW_FIELD(ah, AR_PHY_TPC_19,
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 09facba1dc6d..8927fc34d84c 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -868,10 +868,6 @@ static void ar9003_hw_set_rfmode(struct ath_hw *ah,
if (IS_CHAN_A_FAST_CLOCK(ah, chan))
rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE);
- if (IS_CHAN_QUARTER_RATE(chan))
- rfMode |= AR_PHY_MODE_QUARTER;
- if (IS_CHAN_HALF_RATE(chan))
- rfMode |= AR_PHY_MODE_HALF;
if (rfMode & (AR_PHY_MODE_QUARTER | AR_PHY_MODE_HALF))
REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL,
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index b5ac32cfbeb8..44d74495c4de 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -30,7 +30,6 @@
#include "spectral.h"
struct ath_node;
-struct ath_rate_table;
extern struct ieee80211_ops ath9k_ops;
extern int ath9k_modparam_nohwcrypt;
@@ -150,6 +149,11 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
#define IS_CCK_RATE(rate) ((rate >= 0x18) && (rate <= 0x1e))
#define IS_OFDM_RATE(rate) ((rate >= 0x8) && (rate <= 0xf))
+enum {
+ WLAN_RC_PHY_OFDM,
+ WLAN_RC_PHY_CCK,
+};
+
struct ath_txq {
int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */
u32 axq_qnum; /* ath9k hardware queue number */
@@ -399,21 +403,10 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
#define ATH_BCBUF 8
#define ATH_DEFAULT_BINTVAL 100 /* TU */
#define ATH_DEFAULT_BMISS_LIMIT 10
-#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024)
#define TSF_TO_TU(_h,_l) \
((((u32)(_h)) << 22) | (((u32)(_l)) >> 10))
-struct ath_beacon_config {
- int beacon_interval;
- u16 listen_interval;
- u16 dtim_period;
- u16 bmiss_timeout;
- u8 dtim_count;
- bool enable_beacon;
- bool ibss_creator;
-};
-
struct ath_beacon {
enum {
OK, /* no change needed */
@@ -423,11 +416,9 @@ struct ath_beacon {
u32 beaconq;
u32 bmisscnt;
- u32 bc_tstamp;
struct ieee80211_vif *bslot[ATH_BCBUF];
int slottime;
int slotupdate;
- struct ath9k_tx_queue_info beacon_qi;
struct ath_descdma bdma;
struct ath_txq *cabq;
struct list_head bbuf;
@@ -442,7 +433,8 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
void ath9k_set_beacon(struct ath_softc *sc);
-bool ath9k_csa_is_finished(struct ath_softc *sc);
+bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif);
+void ath9k_csa_update(struct ath_softc *sc);
/*******************/
/* Link Monitoring */
@@ -693,15 +685,6 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
#define ATH_TXPOWER_MAX 100 /* .5 dBm units */
#define MAX_GTT_CNT 5
-enum sc_op_flags {
- SC_OP_INVALID,
- SC_OP_BEACONS,
- SC_OP_ANI_RUN,
- SC_OP_PRIM_STA_VIF,
- SC_OP_HW_RESET,
- SC_OP_SCANNING,
-};
-
/* Powersave flags */
#define PS_WAIT_FOR_BEACON BIT(0)
#define PS_WAIT_FOR_CAB BIT(1)
@@ -731,7 +714,6 @@ struct ath_softc {
struct completion paprd_complete;
wait_queue_head_t tx_wait;
- unsigned long sc_flags;
unsigned long driver_data;
u8 gtt_cnt;
@@ -748,7 +730,6 @@ struct ath_softc {
struct ath_rx rx;
struct ath_tx tx;
struct ath_beacon beacon;
- struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
#ifdef CONFIG_MAC80211_LEDS
bool led_registered;
@@ -757,7 +738,6 @@ struct ath_softc {
#endif
struct ath9k_hw_cal_data caldata;
- int last_rssi;
#ifdef CONFIG_ATH9K_DEBUGFS
struct ath9k_debug debug;
@@ -774,7 +754,6 @@ struct ath_softc {
#endif
struct ath_descdma txsdma;
- struct ieee80211_vif *csa_vif;
struct ath_ant_comb ant_comb;
u8 ant_tx, ant_rx;
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 2e8bba0eb361..471e0f624e81 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -80,7 +80,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
u8 chainmask = ah->txchainmask;
u8 rate = 0;
- sband = &sc->sbands[common->hw->conf.chandef.chan->band];
+ sband = &common->sbands[common->hw->conf.chandef.chan->band];
rate = sband->bitrates[rateidx].hw_value;
if (vif->bss_conf.use_short_preamble)
rate |= sband->bitrates[rateidx].hw_value_short;
@@ -292,11 +292,8 @@ static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
(unsigned long long)tsfadjust, avp->av_bslot);
}
-bool ath9k_csa_is_finished(struct ath_softc *sc)
+bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif)
{
- struct ieee80211_vif *vif;
-
- vif = sc->csa_vif;
if (!vif || !vif->csa_active)
return false;
@@ -304,11 +301,23 @@ bool ath9k_csa_is_finished(struct ath_softc *sc)
return false;
ieee80211_csa_finish(vif);
-
- sc->csa_vif = NULL;
return true;
}
+static void ath9k_csa_update_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct ath_softc *sc = data;
+ ath9k_csa_is_finished(sc, vif);
+}
+
+void ath9k_csa_update(struct ath_softc *sc)
+{
+ ieee80211_iterate_active_interfaces(sc->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ ath9k_csa_update_vif,
+ sc);
+}
+
void ath9k_beacon_tasklet(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *)data;
@@ -319,7 +328,7 @@ void ath9k_beacon_tasklet(unsigned long data)
bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
int slot;
- if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) {
+ if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
ath_dbg(common, RESET,
"reset work is pending, skip beaconing now\n");
return;
@@ -362,13 +371,13 @@ void ath9k_beacon_tasklet(unsigned long data)
return;
}
- /* EDMA devices check that in the tx completion function. */
- if (!edma && ath9k_csa_is_finished(sc))
- return;
-
slot = ath9k_beacon_choose_slot(sc);
vif = sc->beacon.bslot[slot];
+ /* EDMA devices check that in the tx completion function. */
+ if (!edma && ath9k_csa_is_finished(sc, vif))
+ return;
+
if (!vif || !vif->bss_conf.enable_beacon)
return;
@@ -438,33 +447,6 @@ static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt,
ath9k_hw_enable_interrupts(ah);
}
-/* 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_softc *sc, u64 tsf,
- unsigned int interval)
-{
- struct ath_hw *ah = sc->sc_ah;
- unsigned int offset;
-
- tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time);
- offset = ath9k_mod_tsf64_tu(tsf, interval);
-
- return (u32) tsf + TU_TO_USEC(interval) - offset;
-}
-
/*
* For multi-bss ap support beacons are either staggered evenly over N slots or
* burst together. For the former arrange for the SWBA to be delivered for each
@@ -474,115 +456,18 @@ static void ath9k_beacon_config_ap(struct ath_softc *sc,
struct ath_beacon_config *conf)
{
struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- u32 nexttbtt, intval;
-
- /* NB: the beacon interval is kept internally in TU's */
- intval = TU_TO_USEC(conf->beacon_interval);
- intval /= ATH_BCBUF;
- nexttbtt = ath9k_get_next_tbtt(sc, ath9k_hw_gettsf64(ah),
- conf->beacon_interval);
-
- if (conf->enable_beacon)
- ah->imask |= ATH9K_INT_SWBA;
- else
- ah->imask &= ~ATH9K_INT_SWBA;
-
- ath_dbg(common, BEACON,
- "AP (%s) nexttbtt: %u intval: %u conf_intval: %u\n",
- (conf->enable_beacon) ? "Enable" : "Disable",
- nexttbtt, intval, conf->beacon_interval);
- ath9k_beacon_init(sc, nexttbtt, intval, false);
+ ath9k_cmn_beacon_config_ap(ah, conf, ATH_BCBUF);
+ ath9k_beacon_init(sc, conf->nexttbtt, conf->intval, false);
}
-/*
- * This sets up the beacon timers according to the timestamp of the last
- * received beacon and the current TSF, configures PCF and DTIM
- * handling, programs the sleep registers so the hardware will wakeup in
- * time to receive beacons, and configures the beacon miss handling so
- * we'll receive a BMISS interrupt when we stop seeing beacons from the AP
- * we've associated with.
- */
-static void ath9k_beacon_config_sta(struct ath_softc *sc,
+static void ath9k_beacon_config_sta(struct ath_hw *ah,
struct ath_beacon_config *conf)
{
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_beacon_state bs;
- int dtim_intval, sleepduration;
- u32 nexttbtt = 0, intval;
- u64 tsf;
- /* No need to configure beacon if we are not associated */
- if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
- ath_dbg(common, BEACON,
- "STA is not yet associated..skipping beacon config\n");
+ if (ath9k_cmn_beacon_config_sta(ah, conf, &bs) == -EPERM)
return;
- }
-
- memset(&bs, 0, sizeof(bs));
- intval = conf->beacon_interval;
-
- /*
- * Setup dtim parameters according to
- * last beacon we received (which may be none).
- */
- dtim_intval = intval * conf->dtim_period;
- sleepduration = conf->listen_interval * intval;
-
- /*
- * Pull nexttbtt forward to reflect the current
- * TSF and calculate dtim state for the result.
- */
- tsf = ath9k_hw_gettsf64(ah);
- nexttbtt = ath9k_get_next_tbtt(sc, tsf, intval);
-
- bs.bs_intval = TU_TO_USEC(intval);
- bs.bs_dtimperiod = conf->dtim_period * bs.bs_intval;
- bs.bs_nexttbtt = nexttbtt;
- bs.bs_nextdtim = nexttbtt;
- if (conf->dtim_period > 1)
- bs.bs_nextdtim = ath9k_get_next_tbtt(sc, tsf, dtim_intval);
-
- /*
- * Calculate the number of consecutive beacons to miss* before taking
- * a BMISS interrupt. The configuration is specified in TU so we only
- * need calculate based on the beacon interval. Note that we clamp the
- * result to at most 15 beacons.
- */
- if (sleepduration > intval) {
- bs.bs_bmissthreshold = conf->listen_interval *
- ATH_DEFAULT_BMISS_LIMIT / 2;
- } else {
- bs.bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, intval);
- if (bs.bs_bmissthreshold > 15)
- bs.bs_bmissthreshold = 15;
- else if (bs.bs_bmissthreshold <= 0)
- bs.bs_bmissthreshold = 1;
- }
-
- /*
- * Calculate sleep duration. The configuration is given in ms.
- * We ensure a multiple of the beacon period is used. Also, if the sleep
- * duration is greater than the DTIM period then it makes senses
- * to make it a multiple of that.
- *
- * XXX fixed at 100ms
- */
-
- bs.bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100),
- sleepduration));
- if (bs.bs_sleepduration > bs.bs_dtimperiod)
- bs.bs_sleepduration = bs.bs_dtimperiod;
-
- /* TSF out of range threshold fixed at 1 second */
- bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
-
- ath_dbg(common, BEACON, "bmiss: %u sleep: %u\n",
- bs.bs_bmissthreshold, bs.bs_sleepduration);
-
- /* Set the computed STA beacon timers */
ath9k_hw_disable_interrupts(ah);
ath9k_hw_set_sta_beacon_timers(ah, &bs);
@@ -597,36 +482,19 @@ static void ath9k_beacon_config_adhoc(struct ath_softc *sc,
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- u32 intval, nexttbtt;
ath9k_reset_beacon_status(sc);
- intval = TU_TO_USEC(conf->beacon_interval);
-
- if (conf->ibss_creator)
- nexttbtt = intval;
- else
- nexttbtt = ath9k_get_next_tbtt(sc, ath9k_hw_gettsf64(ah),
- conf->beacon_interval);
-
- if (conf->enable_beacon)
- ah->imask |= ATH9K_INT_SWBA;
- else
- ah->imask &= ~ATH9K_INT_SWBA;
-
- ath_dbg(common, BEACON,
- "IBSS (%s) nexttbtt: %u intval: %u conf_intval: %u\n",
- (conf->enable_beacon) ? "Enable" : "Disable",
- nexttbtt, intval, conf->beacon_interval);
+ ath9k_cmn_beacon_config_adhoc(ah, conf);
- ath9k_beacon_init(sc, nexttbtt, intval, conf->ibss_creator);
+ ath9k_beacon_init(sc, conf->nexttbtt, conf->intval, conf->ibss_creator);
/*
* Set the global 'beacon has been configured' flag for the
* joiner case in IBSS mode.
*/
if (!conf->ibss_creator && conf->enable_beacon)
- set_bit(SC_OP_BEACONS, &sc->sc_flags);
+ set_bit(ATH_OP_BEACONS, &common->op_flags);
}
static bool ath9k_allow_beacon_config(struct ath_softc *sc,
@@ -646,7 +514,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
if ((vif->type == NL80211_IFTYPE_STATION) &&
- test_bit(SC_OP_BEACONS, &sc->sc_flags) &&
+ test_bit(ATH_OP_BEACONS, &common->op_flags) &&
!avp->primary_sta_vif) {
ath_dbg(common, CONFIG,
"Beacon already configured for a station interface\n");
@@ -668,7 +536,6 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc,
cur_conf->beacon_interval = bss_conf->beacon_int;
cur_conf->dtim_period = bss_conf->dtim_period;
- cur_conf->listen_interval = 1;
cur_conf->dtim_count = 1;
cur_conf->ibss_creator = bss_conf->ibss_creator;
cur_conf->bmiss_timeout =
@@ -698,6 +565,8 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
{
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
unsigned long flags;
bool skip_beacon = false;
@@ -710,7 +579,7 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
ath9k_cache_beacon_config(sc, bss_conf);
ath9k_set_beacon(sc);
- set_bit(SC_OP_BEACONS, &sc->sc_flags);
+ set_bit(ATH_OP_BEACONS, &common->op_flags);
return;
}
@@ -749,13 +618,13 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
}
/*
- * Do not set the SC_OP_BEACONS flag for IBSS joiner mode
+ * Do not set the ATH_OP_BEACONS flag for IBSS joiner mode
* here, it is done in ath9k_beacon_config_adhoc().
*/
if (cur_conf->enable_beacon && !skip_beacon)
- set_bit(SC_OP_BEACONS, &sc->sc_flags);
+ set_bit(ATH_OP_BEACONS, &common->op_flags);
else
- clear_bit(SC_OP_BEACONS, &sc->sc_flags);
+ clear_bit(ATH_OP_BEACONS, &common->op_flags);
}
}
@@ -773,7 +642,7 @@ void ath9k_set_beacon(struct ath_softc *sc)
ath9k_beacon_config_adhoc(sc, cur_conf);
break;
case NL80211_IFTYPE_STATION:
- ath9k_beacon_config_sta(sc, cur_conf);
+ ath9k_beacon_config_sta(sc->sc_ah, cur_conf);
break;
default:
ath_dbg(common, CONFIG, "Unsupported beaconing mode\n");
diff --git a/drivers/net/wireless/ath/ath9k/common-beacon.c b/drivers/net/wireless/ath/ath9k/common-beacon.c
new file mode 100644
index 000000000000..775d1d20ce0b
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/common-beacon.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2008-2011 Atheros Communications 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 "common.h"
+
+#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;
+
+ tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time);
+ offset = ath9k_mod_tsf64_tu(tsf, interval);
+
+ return (u32) tsf + TU_TO_USEC(interval) - offset;
+}
+
+/*
+ * This sets up the beacon timers according to the timestamp of the last
+ * received beacon and the current TSF, configures PCF and DTIM
+ * handling, programs the sleep registers so the hardware will wakeup in
+ * time to receive beacons, and configures the beacon miss handling so
+ * we'll receive a BMISS interrupt when we stop seeing beacons from the AP
+ * we've associated with.
+ */
+int ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
+ struct ath_beacon_config *conf,
+ struct ath9k_beacon_state *bs)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+ int dtim_intval;
+ u64 tsf;
+
+ /* No need to configure beacon if we are not associated */
+ if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
+ ath_dbg(common, BEACON,
+ "STA is not yet associated..skipping beacon config\n");
+ return -EPERM;
+ }
+
+ memset(bs, 0, sizeof(*bs));
+ conf->intval = conf->beacon_interval;
+
+ /*
+ * Setup dtim parameters according to
+ * last beacon we received (which may be none).
+ */
+ dtim_intval = conf->intval * conf->dtim_period;
+
+ /*
+ * Pull nexttbtt forward to reflect the current
+ * TSF and calculate dtim state for the result.
+ */
+ tsf = ath9k_hw_gettsf64(ah);
+ conf->nexttbtt = ath9k_get_next_tbtt(ah, tsf, conf->intval);
+
+ bs->bs_intval = TU_TO_USEC(conf->intval);
+ bs->bs_dtimperiod = conf->dtim_period * bs->bs_intval;
+ bs->bs_nexttbtt = conf->nexttbtt;
+ bs->bs_nextdtim = conf->nexttbtt;
+ if (conf->dtim_period > 1)
+ bs->bs_nextdtim = ath9k_get_next_tbtt(ah, tsf, dtim_intval);
+
+ /*
+ * Calculate the number of consecutive beacons to miss* before taking
+ * a BMISS interrupt. The configuration is specified in TU so we only
+ * need calculate based on the beacon interval. Note that we clamp the
+ * result to at most 15 beacons.
+ */
+ bs->bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, conf->intval);
+ if (bs->bs_bmissthreshold > 15)
+ bs->bs_bmissthreshold = 15;
+ else if (bs->bs_bmissthreshold <= 0)
+ bs->bs_bmissthreshold = 1;
+
+ /*
+ * Calculate sleep duration. The configuration is given in ms.
+ * We ensure a multiple of the beacon period is used. Also, if the sleep
+ * duration is greater than the DTIM period then it makes senses
+ * to make it a multiple of that.
+ *
+ * XXX fixed at 100ms
+ */
+
+ bs->bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100),
+ conf->intval));
+ if (bs->bs_sleepduration > bs->bs_dtimperiod)
+ bs->bs_sleepduration = bs->bs_dtimperiod;
+
+ /* TSF out of range threshold fixed at 1 second */
+ bs->bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
+
+ ath_dbg(common, BEACON, "bmiss: %u sleep: %u\n",
+ bs->bs_bmissthreshold, bs->bs_sleepduration);
+ return 0;
+}
+EXPORT_SYMBOL(ath9k_cmn_beacon_config_sta);
+
+void ath9k_cmn_beacon_config_adhoc(struct ath_hw *ah,
+ struct ath_beacon_config *conf)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ conf->intval = TU_TO_USEC(conf->beacon_interval);
+
+ if (conf->ibss_creator)
+ conf->nexttbtt = conf->intval;
+ else
+ conf->nexttbtt = ath9k_get_next_tbtt(ah, ath9k_hw_gettsf64(ah),
+ conf->beacon_interval);
+
+ if (conf->enable_beacon)
+ ah->imask |= ATH9K_INT_SWBA;
+ else
+ ah->imask &= ~ATH9K_INT_SWBA;
+
+ ath_dbg(common, BEACON,
+ "IBSS (%s) nexttbtt: %u intval: %u conf_intval: %u\n",
+ (conf->enable_beacon) ? "Enable" : "Disable",
+ conf->nexttbtt, conf->intval, conf->beacon_interval);
+}
+EXPORT_SYMBOL(ath9k_cmn_beacon_config_adhoc);
+
+/*
+ * For multi-bss ap support beacons are either staggered evenly over N slots or
+ * burst together. For the former arrange for the SWBA to be delivered for each
+ * slot. Slots that are not occupied will generate nothing.
+ */
+void ath9k_cmn_beacon_config_ap(struct ath_hw *ah,
+ struct ath_beacon_config *conf,
+ unsigned int bc_buf)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ /* NB: the beacon interval is kept internally in TU's */
+ conf->intval = TU_TO_USEC(conf->beacon_interval);
+ conf->intval /= bc_buf;
+ conf->nexttbtt = ath9k_get_next_tbtt(ah, ath9k_hw_gettsf64(ah),
+ conf->beacon_interval);
+
+ if (conf->enable_beacon)
+ ah->imask |= ATH9K_INT_SWBA;
+ else
+ ah->imask &= ~ATH9K_INT_SWBA;
+
+ ath_dbg(common, BEACON,
+ "AP (%s) nexttbtt: %u intval: %u conf_intval: %u\n",
+ (conf->enable_beacon) ? "Enable" : "Disable",
+ conf->nexttbtt, conf->intval, conf->beacon_interval);
+}
+EXPORT_SYMBOL(ath9k_cmn_beacon_config_ap);
diff --git a/drivers/net/wireless/ath/ath9k/common-beacon.h b/drivers/net/wireless/ath/ath9k/common-beacon.h
new file mode 100644
index 000000000000..3665d27f0dc7
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/common-beacon.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2009-2011 Atheros Communications 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.
+ */
+
+struct ath_beacon_config;
+
+int ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
+ struct ath_beacon_config *conf,
+ struct ath9k_beacon_state *bs);
+void ath9k_cmn_beacon_config_adhoc(struct ath_hw *ah,
+ struct ath_beacon_config *conf);
+void ath9k_cmn_beacon_config_ap(struct ath_hw *ah,
+ struct ath_beacon_config *conf,
+ unsigned int bc_buf);
diff --git a/drivers/net/wireless/ath/ath9k/common-init.c b/drivers/net/wireless/ath/ath9k/common-init.c
new file mode 100644
index 000000000000..a006c1499728
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/common-init.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2008-2011 Atheros Communications 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.
+ */
+
+/* We use the hw_value as an index into our private channel structure */
+
+#include "common.h"
+
+#define CHAN2G(_freq, _idx) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_idx), \
+ .max_power = 20, \
+}
+
+#define CHAN5G(_freq, _idx) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_idx), \
+ .max_power = 20, \
+}
+
+/* Some 2 GHz radios are actually tunable on 2312-2732
+ * on 5 MHz steps, we support the channels which we know
+ * we have calibration data for all cards though to make
+ * this static */
+static const struct ieee80211_channel ath9k_2ghz_chantable[] = {
+ CHAN2G(2412, 0), /* Channel 1 */
+ CHAN2G(2417, 1), /* Channel 2 */
+ CHAN2G(2422, 2), /* Channel 3 */
+ CHAN2G(2427, 3), /* Channel 4 */
+ CHAN2G(2432, 4), /* Channel 5 */
+ CHAN2G(2437, 5), /* Channel 6 */
+ CHAN2G(2442, 6), /* Channel 7 */
+ CHAN2G(2447, 7), /* Channel 8 */
+ CHAN2G(2452, 8), /* Channel 9 */
+ CHAN2G(2457, 9), /* Channel 10 */
+ CHAN2G(2462, 10), /* Channel 11 */
+ CHAN2G(2467, 11), /* Channel 12 */
+ CHAN2G(2472, 12), /* Channel 13 */
+ CHAN2G(2484, 13), /* Channel 14 */
+};
+
+/* Some 5 GHz radios are actually tunable on XXXX-YYYY
+ * on 5 MHz steps, we support the channels which we know
+ * we have calibration data for all cards though to make
+ * this static */
+static const struct ieee80211_channel ath9k_5ghz_chantable[] = {
+ /* _We_ call this UNII 1 */
+ CHAN5G(5180, 14), /* Channel 36 */
+ CHAN5G(5200, 15), /* Channel 40 */
+ CHAN5G(5220, 16), /* Channel 44 */
+ CHAN5G(5240, 17), /* Channel 48 */
+ /* _We_ call this UNII 2 */
+ CHAN5G(5260, 18), /* Channel 52 */
+ CHAN5G(5280, 19), /* Channel 56 */
+ CHAN5G(5300, 20), /* Channel 60 */
+ CHAN5G(5320, 21), /* Channel 64 */
+ /* _We_ call this "Middle band" */
+ CHAN5G(5500, 22), /* Channel 100 */
+ CHAN5G(5520, 23), /* Channel 104 */
+ CHAN5G(5540, 24), /* Channel 108 */
+ CHAN5G(5560, 25), /* Channel 112 */
+ CHAN5G(5580, 26), /* Channel 116 */
+ CHAN5G(5600, 27), /* Channel 120 */
+ CHAN5G(5620, 28), /* Channel 124 */
+ CHAN5G(5640, 29), /* Channel 128 */
+ CHAN5G(5660, 30), /* Channel 132 */
+ CHAN5G(5680, 31), /* Channel 136 */
+ CHAN5G(5700, 32), /* Channel 140 */
+ /* _We_ call this UNII 3 */
+ CHAN5G(5745, 33), /* Channel 149 */
+ CHAN5G(5765, 34), /* Channel 153 */
+ CHAN5G(5785, 35), /* Channel 157 */
+ CHAN5G(5805, 36), /* Channel 161 */
+ CHAN5G(5825, 37), /* Channel 165 */
+};
+
+/* Atheros hardware rate code addition for short premble */
+#define SHPCHECK(__hw_rate, __flags) \
+ ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0)
+
+#define RATE(_bitrate, _hw_rate, _flags) { \
+ .bitrate = (_bitrate), \
+ .flags = (_flags), \
+ .hw_value = (_hw_rate), \
+ .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \
+}
+
+static struct ieee80211_rate ath9k_legacy_rates[] = {
+ RATE(10, 0x1b, 0),
+ RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(60, 0x0b, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(90, 0x0f, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(120, 0x0a, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(180, 0x0e, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(240, 0x09, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(360, 0x0d, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(480, 0x08, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+ RATE(540, 0x0c, (IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ)),
+};
+
+int ath9k_cmn_init_channels_rates(struct ath_common *common)
+{
+ struct ath_hw *ah = (struct ath_hw *)common->ah;
+ void *channels;
+
+ BUILD_BUG_ON(ARRAY_SIZE(ath9k_2ghz_chantable) +
+ ARRAY_SIZE(ath9k_5ghz_chantable) !=
+ ATH9K_NUM_CHANNELS);
+
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) {
+ channels = devm_kzalloc(ah->dev,
+ sizeof(ath9k_2ghz_chantable), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ memcpy(channels, ath9k_2ghz_chantable,
+ sizeof(ath9k_2ghz_chantable));
+ common->sbands[IEEE80211_BAND_2GHZ].channels = channels;
+ common->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
+ common->sbands[IEEE80211_BAND_2GHZ].n_channels =
+ ARRAY_SIZE(ath9k_2ghz_chantable);
+ common->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates;
+ common->sbands[IEEE80211_BAND_2GHZ].n_bitrates =
+ ARRAY_SIZE(ath9k_legacy_rates);
+ }
+
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) {
+ channels = devm_kzalloc(ah->dev,
+ sizeof(ath9k_5ghz_chantable), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ memcpy(channels, ath9k_5ghz_chantable,
+ sizeof(ath9k_5ghz_chantable));
+ common->sbands[IEEE80211_BAND_5GHZ].channels = channels;
+ common->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
+ common->sbands[IEEE80211_BAND_5GHZ].n_channels =
+ ARRAY_SIZE(ath9k_5ghz_chantable);
+ common->sbands[IEEE80211_BAND_5GHZ].bitrates =
+ ath9k_legacy_rates + 4;
+ common->sbands[IEEE80211_BAND_5GHZ].n_bitrates =
+ ARRAY_SIZE(ath9k_legacy_rates) - 4;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ath9k_cmn_init_channels_rates);
+
+void ath9k_cmn_setup_ht_cap(struct ath_hw *ah,
+ struct ieee80211_sta_ht_cap *ht_info)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+ u8 tx_streams, rx_streams;
+ int i, max_streams;
+
+ ht_info->ht_supported = true;
+ ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_SM_PS |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_DSSSCCK40;
+
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_LDPC)
+ ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING;
+
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_SGI_20)
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
+
+ ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+ ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
+
+ if (AR_SREV_9271(ah) || AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah))
+ max_streams = 1;
+ else if (AR_SREV_9462(ah))
+ max_streams = 2;
+ else if (AR_SREV_9300_20_OR_LATER(ah))
+ max_streams = 3;
+ else
+ max_streams = 2;
+
+ if (AR_SREV_9280_20_OR_LATER(ah)) {
+ if (max_streams >= 2)
+ ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
+ ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+ }
+
+ /* set up supported mcs set */
+ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+ tx_streams = ath9k_cmn_count_streams(ah->txchainmask, max_streams);
+ rx_streams = ath9k_cmn_count_streams(ah->rxchainmask, max_streams);
+
+ ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n",
+ tx_streams, rx_streams);
+
+ if (tx_streams != rx_streams) {
+ ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
+ ht_info->mcs.tx_params |= ((tx_streams - 1) <<
+ IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
+ }
+
+ for (i = 0; i < rx_streams; i++)
+ ht_info->mcs.rx_mask[i] = 0xff;
+
+ ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
+}
+EXPORT_SYMBOL(ath9k_cmn_setup_ht_cap);
+
+void ath9k_cmn_reload_chainmask(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (!(ah->caps.hw_caps & ATH9K_HW_CAP_HT))
+ return;
+
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
+ ath9k_cmn_setup_ht_cap(ah,
+ &common->sbands[IEEE80211_BAND_2GHZ].ht_cap);
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
+ ath9k_cmn_setup_ht_cap(ah,
+ &common->sbands[IEEE80211_BAND_5GHZ].ht_cap);
+}
+EXPORT_SYMBOL(ath9k_cmn_reload_chainmask);
diff --git a/drivers/net/wireless/ath/ath9k/common-init.h b/drivers/net/wireless/ath/ath9k/common-init.h
new file mode 100644
index 000000000000..ac03fca5ffdd
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/common-init.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2009-2011 Atheros Communications 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.
+ */
+
+int ath9k_cmn_init_channels_rates(struct ath_common *common);
+void ath9k_cmn_setup_ht_cap(struct ath_hw *ah,
+ struct ieee80211_sta_ht_cap *ht_info);
+void ath9k_cmn_reload_chainmask(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index 768c733cad31..c6dd7f1fed65 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -27,6 +27,250 @@ MODULE_AUTHOR("Atheros Communications");
MODULE_DESCRIPTION("Shared library for Atheros wireless 802.11n LAN cards.");
MODULE_LICENSE("Dual BSD/GPL");
+/* Assumes you've already done the endian to CPU conversion */
+bool ath9k_cmn_rx_accept(struct ath_common *common,
+ struct ieee80211_hdr *hdr,
+ struct ieee80211_rx_status *rxs,
+ struct ath_rx_status *rx_stats,
+ bool *decrypt_error,
+ unsigned int rxfilter)
+{
+ struct ath_hw *ah = common->ah;
+ bool is_mc, is_valid_tkip, strip_mic, mic_error;
+ __le16 fc;
+
+ fc = hdr->frame_control;
+
+ is_mc = !!is_multicast_ether_addr(hdr->addr1);
+ is_valid_tkip = rx_stats->rs_keyix != ATH9K_RXKEYIX_INVALID &&
+ test_bit(rx_stats->rs_keyix, common->tkip_keymap);
+ strip_mic = is_valid_tkip && ieee80211_is_data(fc) &&
+ ieee80211_has_protected(fc) &&
+ !(rx_stats->rs_status &
+ (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC |
+ ATH9K_RXERR_KEYMISS));
+
+ /*
+ * Key miss events are only relevant for pairwise keys where the
+ * descriptor does contain a valid key index. This has been observed
+ * mostly with CCMP encryption.
+ */
+ if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID ||
+ !test_bit(rx_stats->rs_keyix, common->ccmp_keymap))
+ rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS;
+
+ mic_error = is_valid_tkip && !ieee80211_is_ctl(fc) &&
+ !ieee80211_has_morefrags(fc) &&
+ !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) &&
+ (rx_stats->rs_status & ATH9K_RXERR_MIC);
+
+ /*
+ * The rx_stats->rs_status will not be set until the end of the
+ * chained descriptors so it can be ignored if rs_more is set. The
+ * rs_more will be false at the last element of the chained
+ * descriptors.
+ */
+ if (rx_stats->rs_status != 0) {
+ u8 status_mask;
+
+ if (rx_stats->rs_status & ATH9K_RXERR_CRC) {
+ rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
+ mic_error = false;
+ }
+
+ if ((rx_stats->rs_status & ATH9K_RXERR_DECRYPT) ||
+ (!is_mc && (rx_stats->rs_status & ATH9K_RXERR_KEYMISS))) {
+ *decrypt_error = true;
+ mic_error = false;
+ }
+
+
+ /*
+ * Reject error frames with the exception of
+ * decryption and MIC failures. For monitor mode,
+ * we also ignore the CRC error.
+ */
+ status_mask = ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
+ ATH9K_RXERR_KEYMISS;
+
+ if (ah->is_monitoring && (rxfilter & FIF_FCSFAIL))
+ status_mask |= ATH9K_RXERR_CRC;
+
+ if (rx_stats->rs_status & ~status_mask)
+ return false;
+ }
+
+ /*
+ * For unicast frames the MIC error bit can have false positives,
+ * so all MIC error reports need to be validated in software.
+ * False negatives are not common, so skip software verification
+ * if the hardware considers the MIC valid.
+ */
+ if (strip_mic)
+ rxs->flag |= RX_FLAG_MMIC_STRIPPED;
+ else if (is_mc && mic_error)
+ rxs->flag |= RX_FLAG_MMIC_ERROR;
+
+ return true;
+}
+EXPORT_SYMBOL(ath9k_cmn_rx_accept);
+
+void ath9k_cmn_rx_skb_postprocess(struct ath_common *common,
+ struct sk_buff *skb,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rxs,
+ bool decrypt_error)
+{
+ struct ath_hw *ah = common->ah;
+ struct ieee80211_hdr *hdr;
+ int hdrlen, padpos, padsize;
+ u8 keyix;
+ __le16 fc;
+
+ /* see if any padding is done by the hw and remove it */
+ hdr = (struct ieee80211_hdr *) skb->data;
+ hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+ fc = hdr->frame_control;
+ padpos = ieee80211_hdrlen(fc);
+
+ /* The MAC header is padded to have 32-bit boundary if the
+ * packet payload is non-zero. The general calculation for
+ * padsize would take into account odd header lengths:
+ * padsize = (4 - padpos % 4) % 4; However, since only
+ * even-length headers are used, padding can only be 0 or 2
+ * bytes and we can optimize this a bit. In addition, we must
+ * not try to remove padding from short control frames that do
+ * not have payload. */
+ padsize = padpos & 3;
+ if (padsize && skb->len>=padpos+padsize+FCS_LEN) {
+ memmove(skb->data + padsize, skb->data, padpos);
+ skb_pull(skb, padsize);
+ }
+
+ keyix = rx_stats->rs_keyix;
+
+ if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error &&
+ ieee80211_has_protected(fc)) {
+ rxs->flag |= RX_FLAG_DECRYPTED;
+ } else if (ieee80211_has_protected(fc)
+ && !decrypt_error && skb->len >= hdrlen + 4) {
+ keyix = skb->data[hdrlen + 3] >> 6;
+
+ if (test_bit(keyix, common->keymap))
+ rxs->flag |= RX_FLAG_DECRYPTED;
+ }
+ if (ah->sw_mgmt_crypto &&
+ (rxs->flag & RX_FLAG_DECRYPTED) &&
+ ieee80211_is_mgmt(fc))
+ /* Use software decrypt for management frames. */
+ rxs->flag &= ~RX_FLAG_DECRYPTED;
+}
+EXPORT_SYMBOL(ath9k_cmn_rx_skb_postprocess);
+
+int ath9k_cmn_process_rate(struct ath_common *common,
+ struct ieee80211_hw *hw,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rxs)
+{
+ struct ieee80211_supported_band *sband;
+ enum ieee80211_band band;
+ unsigned int i = 0;
+ struct ath_hw *ah = common->ah;
+
+ band = ah->curchan->chan->band;
+ sband = hw->wiphy->bands[band];
+
+ if (IS_CHAN_QUARTER_RATE(ah->curchan))
+ rxs->flag |= RX_FLAG_5MHZ;
+ else if (IS_CHAN_HALF_RATE(ah->curchan))
+ rxs->flag |= RX_FLAG_10MHZ;
+
+ if (rx_stats->rs_rate & 0x80) {
+ /* HT rate */
+ rxs->flag |= RX_FLAG_HT;
+ rxs->flag |= rx_stats->flag;
+ rxs->rate_idx = rx_stats->rs_rate & 0x7f;
+ return 0;
+ }
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (sband->bitrates[i].hw_value == rx_stats->rs_rate) {
+ rxs->rate_idx = i;
+ return 0;
+ }
+ if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) {
+ rxs->flag |= RX_FLAG_SHORTPRE;
+ rxs->rate_idx = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(ath9k_cmn_process_rate);
+
+void ath9k_cmn_process_rssi(struct ath_common *common,
+ struct ieee80211_hw *hw,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rxs)
+{
+ struct ath_hw *ah = common->ah;
+ int last_rssi;
+ int rssi = rx_stats->rs_rssi;
+ int i, j;
+
+ /*
+ * RSSI is not available for subframes in an A-MPDU.
+ */
+ if (rx_stats->rs_moreaggr) {
+ rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
+ return;
+ }
+
+ /*
+ * Check if the RSSI for the last subframe in an A-MPDU
+ * or an unaggregated frame is valid.
+ */
+ if (rx_stats->rs_rssi == ATH9K_RSSI_BAD) {
+ rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
+ return;
+ }
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(rx_stats->rs_rssi_ctl); i++) {
+ s8 rssi;
+
+ if (!(ah->rxchainmask & BIT(i)))
+ continue;
+
+ rssi = rx_stats->rs_rssi_ctl[i];
+ if (rssi != ATH9K_RSSI_BAD) {
+ rxs->chains |= BIT(j);
+ rxs->chain_signal[j] = ah->noise + rssi;
+ }
+ j++;
+ }
+
+ /*
+ * Update Beacon RSSI, this is used by ANI.
+ */
+ if (rx_stats->is_mybeacon &&
+ ((ah->opmode == NL80211_IFTYPE_STATION) ||
+ (ah->opmode == NL80211_IFTYPE_ADHOC))) {
+ ATH_RSSI_LPF(common->last_rssi, rx_stats->rs_rssi);
+ last_rssi = common->last_rssi;
+
+ if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
+ rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER);
+ if (rssi < 0)
+ rssi = 0;
+
+ ah->stats.avgbrssi = rssi;
+ }
+
+ rxs->signal = ah->noise + rx_stats->rs_rssi;
+}
+EXPORT_SYMBOL(ath9k_cmn_process_rssi);
+
int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb)
{
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h
index eb85e1bdca88..ca38116838f0 100644
--- a/drivers/net/wireless/ath/ath9k/common.h
+++ b/drivers/net/wireless/ath/ath9k/common.h
@@ -21,6 +21,9 @@
#include "hw.h"
#include "hw-ops.h"
+#include "common-init.h"
+#include "common-beacon.h"
+
/* Common header for Atheros 802.11n base driver cores */
#define WME_BA_BMP_SIZE 64
@@ -42,6 +45,38 @@
#define ATH_EP_RND(x, mul) \
(((x) + ((mul)/2)) / (mul))
+#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024)
+
+struct ath_beacon_config {
+ int beacon_interval;
+ u16 dtim_period;
+ u16 bmiss_timeout;
+ u8 dtim_count;
+ bool enable_beacon;
+ bool ibss_creator;
+ u32 nexttbtt;
+ u32 intval;
+};
+
+bool ath9k_cmn_rx_accept(struct ath_common *common,
+ struct ieee80211_hdr *hdr,
+ struct ieee80211_rx_status *rxs,
+ struct ath_rx_status *rx_stats,
+ bool *decrypt_error,
+ unsigned int rxfilter);
+void ath9k_cmn_rx_skb_postprocess(struct ath_common *common,
+ struct sk_buff *skb,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rxs,
+ bool decrypt_error);
+int ath9k_cmn_process_rate(struct ath_common *common,
+ struct ieee80211_hw *hw,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rxs);
+void ath9k_cmn_process_rssi(struct ath_common *common,
+ struct ieee80211_hw *hw,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rxs);
int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
struct ath_hw *ah,
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index ab7264c1d8f7..780ff1bee6f6 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -135,46 +135,45 @@ static ssize_t read_file_ani(struct file *file, char __user *user_buf,
struct ath_softc *sc = file->private_data;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_hw *ah = sc->sc_ah;
- unsigned int len = 0, size = 1024;
+ unsigned int len = 0;
+ const unsigned int size = 1024;
ssize_t retval = 0;
char *buf;
+ int i;
+ struct {
+ const char *name;
+ unsigned int val;
+ } ani_info[] = {
+ { "ANI RESET", ah->stats.ast_ani_reset },
+ { "OFDM LEVEL", ah->ani.ofdmNoiseImmunityLevel },
+ { "CCK LEVEL", ah->ani.cckNoiseImmunityLevel },
+ { "SPUR UP", ah->stats.ast_ani_spurup },
+ { "SPUR DOWN", ah->stats.ast_ani_spurup },
+ { "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon },
+ { "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff },
+ { "MRC-CCK ON", ah->stats.ast_ani_ccklow },
+ { "MRC-CCK OFF", ah->stats.ast_ani_cckhigh },
+ { "FIR-STEP UP", ah->stats.ast_ani_stepup },
+ { "FIR-STEP DOWN", ah->stats.ast_ani_stepdown },
+ { "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero },
+ { "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs },
+ { "CCK ERRORS", ah->stats.ast_ani_cckerrs },
+ };
buf = kzalloc(size, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
- if (common->disable_ani) {
- len += scnprintf(buf + len, size - len, "%s: %s\n",
- "ANI", "DISABLED");
+ len += scnprintf(buf + len, size - len, "%15s: %s\n", "ANI",
+ common->disable_ani ? "DISABLED" : "ENABLED");
+
+ if (common->disable_ani)
goto exit;
- }
- len += scnprintf(buf + len, size - len, "%15s: %s\n",
- "ANI", "ENABLED");
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "ANI RESET", ah->stats.ast_ani_reset);
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "SPUR UP", ah->stats.ast_ani_spurup);
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "SPUR DOWN", ah->stats.ast_ani_spurup);
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon);
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff);
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "MRC-CCK ON", ah->stats.ast_ani_ccklow);
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "MRC-CCK OFF", ah->stats.ast_ani_cckhigh);
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "FIR-STEP UP", ah->stats.ast_ani_stepup);
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "FIR-STEP DOWN", ah->stats.ast_ani_stepdown);
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero);
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs);
- len += scnprintf(buf + len, size - len, "%15s: %u\n",
- "CCK ERRORS", ah->stats.ast_ani_cckerrs);
+ for (i = 0; i < ARRAY_SIZE(ani_info); i++)
+ len += scnprintf(buf + len, size - len, "%15s: %u\n",
+ ani_info[i].name, ani_info[i].val);
+
exit:
if (len > size)
len = size;
@@ -209,7 +208,7 @@ static ssize_t write_file_ani(struct file *file,
common->disable_ani = !ani;
if (common->disable_ani) {
- clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
+ clear_bit(ATH_OP_ANI_RUN, &common->op_flags);
ath_stop_ani(sc);
} else {
ath_check_ani(sc);
@@ -307,13 +306,13 @@ static ssize_t read_file_antenna_diversity(struct file *file,
struct ath_antenna_stats *as_main = &sc->debug.stats.ant_stats[ANT_MAIN];
struct ath_antenna_stats *as_alt = &sc->debug.stats.ant_stats[ANT_ALT];
struct ath_hw_antcomb_conf div_ant_conf;
- unsigned int len = 0, size = 1024;
+ unsigned int len = 0;
+ const unsigned int size = 1024;
ssize_t retval = 0;
char *buf;
- char *lna_conf_str[4] = {"LNA1_MINUS_LNA2",
- "LNA2",
- "LNA1",
- "LNA1_PLUS_LNA2"};
+ static const char *lna_conf_str[4] = {
+ "LNA1_MINUS_LNA2", "LNA2", "LNA1", "LNA1_PLUS_LNA2"
+ };
buf = kzalloc(size, GFP_KERNEL);
if (buf == NULL)
@@ -716,10 +715,13 @@ static ssize_t read_file_queues(struct file *file, char __user *user_buf,
struct ath_softc *sc = file->private_data;
struct ath_txq *txq;
char *buf;
- unsigned int len = 0, size = 1024;
+ unsigned int len = 0;
+ const unsigned int size = 1024;
ssize_t retval = 0;
int i;
- char *qname[4] = {"VO", "VI", "BE", "BK"};
+ static const char *qname[4] = {
+ "VO", "VI", "BE", "BK"
+ };
buf = kzalloc(size, GFP_KERNEL);
if (buf == NULL)
@@ -866,6 +868,12 @@ static ssize_t read_file_reset(struct file *file, char __user *user_buf,
"%17s: %2d\n", "PLL RX Hang",
sc->debug.stats.reset[RESET_TYPE_PLL_HANG]);
len += scnprintf(buf + len, sizeof(buf) - len,
+ "%17s: %2d\n", "MAC Hang",
+ sc->debug.stats.reset[RESET_TYPE_MAC_HANG]);
+ len += scnprintf(buf + len, sizeof(buf) - len,
+ "%17s: %2d\n", "Stuck Beacon",
+ sc->debug.stats.reset[RESET_TYPE_BEACON_STUCK]);
+ len += scnprintf(buf + len, sizeof(buf) - len,
"%17s: %2d\n", "MCI Reset",
sc->debug.stats.reset[RESET_TYPE_MCI]);
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index cc7a025d833e..559a68c2709c 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -18,7 +18,6 @@
#define DEBUG_H
#include "hw.h"
-#include "rc.h"
#include "dfs_debug.h"
struct ath_txq;
diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.h b/drivers/net/wireless/ath/ath9k/dfs_debug.h
index 0a7ddf4c88c9..7936c9126a20 100644
--- a/drivers/net/wireless/ath/ath9k/dfs_debug.h
+++ b/drivers/net/wireless/ath/ath9k/dfs_debug.h
@@ -21,6 +21,8 @@
#include "hw.h"
+struct ath_softc;
+
/**
* struct ath_dfs_stats - DFS Statistics per wiphy
* @pulses_total: pulses reported by HW
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 6d5d716adc1b..8e7153b186ed 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -54,6 +54,8 @@ static struct usb_device_id ath9k_hif_usb_ids[] = {
.driver_info = AR9280_USB }, /* SMC Networks */
{ USB_DEVICE(0x0411, 0x017f),
.driver_info = AR9280_USB }, /* Sony UWA-BR100 */
+ { USB_DEVICE(0x0411, 0x0197),
+ .driver_info = AR9280_USB }, /* Buffalo WLI-UV-AG300P */
{ USB_DEVICE(0x04da, 0x3904),
.driver_info = AR9280_USB },
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 99a203174f45..dab1f0cab993 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -39,7 +39,6 @@
#define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */
#define ATH_DEFAULT_BMISS_LIMIT 10
-#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024)
#define TSF_TO_TU(_h, _l) \
((((u32)(_h)) << 22) | (((u32)(_l)) >> 10))
@@ -277,7 +276,6 @@ struct ath9k_htc_rxbuf {
};
struct ath9k_htc_rx {
- int last_rssi; /* FIXME: per-STA */
struct list_head rxbuf;
spinlock_t rxbuflock;
};
@@ -407,12 +405,18 @@ static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
#define DEFAULT_SWBA_RESPONSE 40 /* in TUs */
#define MIN_SWBA_RESPONSE 10 /* in TUs */
-struct htc_beacon_config {
+struct htc_beacon {
+ enum {
+ OK, /* no change needed */
+ UPDATE, /* update pending */
+ COMMIT /* beacon sent, commit change */
+ } updateslot; /* slot time update fsm */
+
struct ieee80211_vif *bslot[ATH9K_HTC_MAX_BCN_VIF];
- u16 beacon_interval;
- u16 dtim_period;
- u16 bmiss_timeout;
- u32 bmiss_cnt;
+ u32 bmisscnt;
+ u32 beaconq;
+ int slottime;
+ int slotupdate;
};
struct ath_btcoex {
@@ -440,12 +444,8 @@ static inline void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv)
}
#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */
-#define OP_INVALID BIT(0)
-#define OP_SCANNING BIT(1)
-#define OP_ENABLE_BEACON BIT(2)
#define OP_BT_PRIORITY_DETECTED BIT(3)
#define OP_BT_SCAN BIT(4)
-#define OP_ANI_RUNNING BIT(5)
#define OP_TSF_RESET BIT(6)
struct ath9k_htc_priv {
@@ -488,10 +488,10 @@ struct ath9k_htc_priv {
unsigned long op_flags;
struct ath9k_hw_cal_data caldata;
- struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
spinlock_t beacon_lock;
- struct htc_beacon_config cur_beacon_conf;
+ struct ath_beacon_config cur_beacon_conf;
+ struct htc_beacon beacon;
struct ath9k_htc_rx rx;
struct ath9k_htc_tx tx;
@@ -516,7 +516,6 @@ struct ath9k_htc_priv {
struct work_struct led_work;
#endif
- int beaconq;
int cabq;
int hwq_map[IEEE80211_NUM_ACS];
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
index 8b5757734596..e8b6ec3c1dbb 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
@@ -26,7 +26,7 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv)
memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
memset(&qi_be, 0, sizeof(struct ath9k_tx_queue_info));
- ath9k_hw_get_txq_props(ah, priv->beaconq, &qi);
+ ath9k_hw_get_txq_props(ah, priv->beacon.beaconq, &qi);
if (priv->ah->opmode == NL80211_IFTYPE_AP ||
priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) {
@@ -54,220 +54,78 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv)
}
- if (!ath9k_hw_set_txq_props(ah, priv->beaconq, &qi)) {
+ if (!ath9k_hw_set_txq_props(ah, priv->beacon.beaconq, &qi)) {
ath_err(ath9k_hw_common(ah),
- "Unable to update beacon queue %u!\n", priv->beaconq);
+ "Unable to update beacon queue %u!\n", priv->beacon.beaconq);
} else {
- ath9k_hw_resettxqueue(ah, priv->beaconq);
+ ath9k_hw_resettxqueue(ah, priv->beacon.beaconq);
}
}
-
-static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
- struct htc_beacon_config *bss_conf)
+/*
+ * Both nexttbtt and intval have to be in usecs.
+ */
+static void ath9k_htc_beacon_init(struct ath9k_htc_priv *priv,
+ struct ath_beacon_config *conf,
+ bool reset_tsf)
{
- struct ath_common *common = ath9k_hw_common(priv->ah);
- struct ath9k_beacon_state bs;
- enum ath9k_int imask = 0;
- int dtimperiod, dtimcount, sleepduration;
- int bmiss_timeout;
- u32 nexttbtt = 0, intval, tsftu;
- __be32 htc_imask = 0;
- u64 tsf;
- int num_beacons, offset, dtim_dec_count;
+ struct ath_hw *ah = priv->ah;
int ret __attribute__ ((unused));
+ __be32 htc_imask = 0;
u8 cmd_rsp;
- memset(&bs, 0, sizeof(bs));
-
- intval = bss_conf->beacon_interval;
- bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_interval);
-
- /*
- * Setup dtim parameters according to
- * last beacon we received (which may be none).
- */
- dtimperiod = bss_conf->dtim_period;
- if (dtimperiod <= 0) /* NB: 0 if not known */
- dtimperiod = 1;
- dtimcount = 1;
- if (dtimcount >= dtimperiod) /* NB: sanity check */
- dtimcount = 0;
-
- sleepduration = intval;
- if (sleepduration <= 0)
- sleepduration = intval;
-
- /*
- * Pull nexttbtt forward to reflect the current
- * TSF and calculate dtim state for the result.
- */
- tsf = ath9k_hw_gettsf64(priv->ah);
- tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
-
- num_beacons = tsftu / intval + 1;
- offset = tsftu % intval;
- nexttbtt = tsftu - offset;
- if (offset)
- nexttbtt += intval;
-
- /* DTIM Beacon every dtimperiod Beacon */
- dtim_dec_count = num_beacons % dtimperiod;
- dtimcount -= dtim_dec_count;
- if (dtimcount < 0)
- dtimcount += dtimperiod;
-
- bs.bs_intval = TU_TO_USEC(intval);
- bs.bs_nexttbtt = TU_TO_USEC(nexttbtt);
- bs.bs_dtimperiod = dtimperiod * bs.bs_intval;
- bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount * bs.bs_intval;
-
- /*
- * Calculate the number of consecutive beacons to miss* before taking
- * a BMISS interrupt. The configuration is specified in TU so we only
- * need calculate based on the beacon interval. Note that we clamp the
- * result to at most 15 beacons.
- */
- if (sleepduration > intval) {
- bs.bs_bmissthreshold = ATH_DEFAULT_BMISS_LIMIT / 2;
- } else {
- bs.bs_bmissthreshold = DIV_ROUND_UP(bmiss_timeout, intval);
- if (bs.bs_bmissthreshold > 15)
- bs.bs_bmissthreshold = 15;
- else if (bs.bs_bmissthreshold <= 0)
- bs.bs_bmissthreshold = 1;
- }
-
- /*
- * Calculate sleep duration. The configuration is given in ms.
- * We ensure a multiple of the beacon period is used. Also, if the sleep
- * duration is greater than the DTIM period then it makes senses
- * to make it a multiple of that.
- *
- * XXX fixed at 100ms
- */
-
- bs.bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100),
- sleepduration));
- if (bs.bs_sleepduration > bs.bs_dtimperiod)
- bs.bs_sleepduration = bs.bs_dtimperiod;
-
- /* TSF out of range threshold fixed at 1 second */
- bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
-
- ath_dbg(common, CONFIG, "intval: %u tsf: %llu tsftu: %u\n",
- intval, tsf, tsftu);
- ath_dbg(common, CONFIG, "bmiss: %u sleep: %u\n",
- bs.bs_bmissthreshold, bs.bs_sleepduration);
-
- /* Set the computed STA beacon timers */
+ if (conf->intval >= TU_TO_USEC(DEFAULT_SWBA_RESPONSE))
+ ah->config.sw_beacon_response_time = DEFAULT_SWBA_RESPONSE;
+ else
+ ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE;
WMI_CMD(WMI_DISABLE_INTR_CMDID);
- ath9k_hw_set_sta_beacon_timers(priv->ah, &bs);
- imask |= ATH9K_INT_BMISS;
- htc_imask = cpu_to_be32(imask);
+ if (reset_tsf)
+ ath9k_hw_reset_tsf(ah);
+ ath9k_htc_beaconq_config(priv);
+ ath9k_hw_beaconinit(ah, conf->nexttbtt, conf->intval);
+ priv->beacon.bmisscnt = 0;
+ htc_imask = cpu_to_be32(ah->imask);
WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
}
-static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv,
- struct htc_beacon_config *bss_conf)
+static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
+ struct ath_beacon_config *bss_conf)
{
- struct ath_common *common = ath9k_hw_common(priv->ah);
+ struct ath9k_beacon_state bs;
enum ath9k_int imask = 0;
- u32 nexttbtt, intval, tsftu;
__be32 htc_imask = 0;
int ret __attribute__ ((unused));
u8 cmd_rsp;
- u64 tsf;
- intval = bss_conf->beacon_interval;
- intval /= ATH9K_HTC_MAX_BCN_VIF;
- nexttbtt = intval;
-
- /*
- * To reduce beacon misses under heavy TX load,
- * set the beacon response time to a larger value.
- */
- if (intval > DEFAULT_SWBA_RESPONSE)
- priv->ah->config.sw_beacon_response_time = DEFAULT_SWBA_RESPONSE;
- else
- priv->ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE;
-
- if (test_bit(OP_TSF_RESET, &priv->op_flags)) {
- ath9k_hw_reset_tsf(priv->ah);
- clear_bit(OP_TSF_RESET, &priv->op_flags);
- } else {
- /*
- * Pull nexttbtt forward to reflect the current TSF.
- */
- tsf = ath9k_hw_gettsf64(priv->ah);
- tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE;
- do {
- nexttbtt += intval;
- } while (nexttbtt < tsftu);
- }
-
- if (test_bit(OP_ENABLE_BEACON, &priv->op_flags))
- imask |= ATH9K_INT_SWBA;
-
- ath_dbg(common, CONFIG,
- "AP Beacon config, intval: %d, nexttbtt: %u, resp_time: %d imask: 0x%x\n",
- bss_conf->beacon_interval, nexttbtt,
- priv->ah->config.sw_beacon_response_time, imask);
-
- ath9k_htc_beaconq_config(priv);
+ if (ath9k_cmn_beacon_config_sta(priv->ah, bss_conf, &bs) == -EPERM)
+ return;
WMI_CMD(WMI_DISABLE_INTR_CMDID);
- ath9k_hw_beaconinit(priv->ah, TU_TO_USEC(nexttbtt), TU_TO_USEC(intval));
- priv->cur_beacon_conf.bmiss_cnt = 0;
+ ath9k_hw_set_sta_beacon_timers(priv->ah, &bs);
+ imask |= ATH9K_INT_BMISS;
htc_imask = cpu_to_be32(imask);
WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
}
-static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv,
- struct htc_beacon_config *bss_conf)
+static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv,
+ struct ath_beacon_config *conf)
{
- struct ath_common *common = ath9k_hw_common(priv->ah);
- enum ath9k_int imask = 0;
- u32 nexttbtt, intval, tsftu;
- __be32 htc_imask = 0;
- int ret __attribute__ ((unused));
- u8 cmd_rsp;
- u64 tsf;
-
- intval = bss_conf->beacon_interval;
- nexttbtt = intval;
-
- /*
- * Pull nexttbtt forward to reflect the current TSF.
- */
- tsf = ath9k_hw_gettsf64(priv->ah);
- tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE;
- do {
- nexttbtt += intval;
- } while (nexttbtt < tsftu);
-
- /*
- * Only one IBSS interfce is allowed.
- */
- if (intval > DEFAULT_SWBA_RESPONSE)
- priv->ah->config.sw_beacon_response_time = DEFAULT_SWBA_RESPONSE;
- else
- priv->ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE;
+ struct ath_hw *ah = priv->ah;
+ ah->imask = 0;
- if (test_bit(OP_ENABLE_BEACON, &priv->op_flags))
- imask |= ATH9K_INT_SWBA;
+ ath9k_cmn_beacon_config_ap(ah, conf, ATH9K_HTC_MAX_BCN_VIF);
+ ath9k_htc_beacon_init(priv, conf, false);
+}
- ath_dbg(common, CONFIG,
- "IBSS Beacon config, intval: %d, nexttbtt: %u, resp_time: %d, imask: 0x%x\n",
- bss_conf->beacon_interval, nexttbtt,
- priv->ah->config.sw_beacon_response_time, imask);
+static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv,
+ struct ath_beacon_config *conf)
+{
+ struct ath_hw *ah = priv->ah;
+ ah->imask = 0;
- WMI_CMD(WMI_DISABLE_INTR_CMDID);
- ath9k_hw_beaconinit(priv->ah, TU_TO_USEC(nexttbtt), TU_TO_USEC(intval));
- priv->cur_beacon_conf.bmiss_cnt = 0;
- htc_imask = cpu_to_be32(imask);
- WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
+ ath9k_cmn_beacon_config_adhoc(ah, conf);
+ ath9k_htc_beacon_init(priv, conf, conf->ibss_creator);
}
void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb,
@@ -287,7 +145,7 @@ static void ath9k_htc_send_buffered(struct ath9k_htc_priv *priv,
spin_lock_bh(&priv->beacon_lock);
- vif = priv->cur_beacon_conf.bslot[slot];
+ vif = priv->beacon.bslot[slot];
skb = ieee80211_get_buffered_bc(priv->hw, vif);
@@ -348,10 +206,10 @@ static void ath9k_htc_send_beacon(struct ath9k_htc_priv *priv,
spin_lock_bh(&priv->beacon_lock);
- vif = priv->cur_beacon_conf.bslot[slot];
+ vif = priv->beacon.bslot[slot];
avp = (struct ath9k_htc_vif *)vif->drv_priv;
- if (unlikely(test_bit(OP_SCANNING, &priv->op_flags))) {
+ if (unlikely(test_bit(ATH_OP_SCANNING, &common->op_flags))) {
spin_unlock_bh(&priv->beacon_lock);
return;
}
@@ -431,8 +289,8 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv,
int slot;
if (swba->beacon_pending != 0) {
- priv->cur_beacon_conf.bmiss_cnt++;
- if (priv->cur_beacon_conf.bmiss_cnt > BSTUCK_THRESHOLD) {
+ priv->beacon.bmisscnt++;
+ if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) {
ath_dbg(common, BSTUCK, "Beacon stuck, HW reset\n");
ieee80211_queue_work(priv->hw,
&priv->fatal_work);
@@ -440,16 +298,16 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv,
return;
}
- if (priv->cur_beacon_conf.bmiss_cnt) {
+ if (priv->beacon.bmisscnt) {
ath_dbg(common, BSTUCK,
"Resuming beacon xmit after %u misses\n",
- priv->cur_beacon_conf.bmiss_cnt);
- priv->cur_beacon_conf.bmiss_cnt = 0;
+ priv->beacon.bmisscnt);
+ priv->beacon.bmisscnt = 0;
}
slot = ath9k_htc_choose_bslot(priv, swba);
spin_lock_bh(&priv->beacon_lock);
- if (priv->cur_beacon_conf.bslot[slot] == NULL) {
+ if (priv->beacon.bslot[slot] == NULL) {
spin_unlock_bh(&priv->beacon_lock);
return;
}
@@ -468,13 +326,13 @@ void ath9k_htc_assign_bslot(struct ath9k_htc_priv *priv,
spin_lock_bh(&priv->beacon_lock);
for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++) {
- if (priv->cur_beacon_conf.bslot[i] == NULL) {
+ if (priv->beacon.bslot[i] == NULL) {
avp->bslot = i;
break;
}
}
- priv->cur_beacon_conf.bslot[avp->bslot] = vif;
+ priv->beacon.bslot[avp->bslot] = vif;
spin_unlock_bh(&priv->beacon_lock);
ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n",
@@ -488,7 +346,7 @@ void ath9k_htc_remove_bslot(struct ath9k_htc_priv *priv,
struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv;
spin_lock_bh(&priv->beacon_lock);
- priv->cur_beacon_conf.bslot[avp->bslot] = NULL;
+ priv->beacon.bslot[avp->bslot] = NULL;
spin_unlock_bh(&priv->beacon_lock);
ath_dbg(common, CONFIG, "Removed interface at beacon slot: %d\n",
@@ -504,7 +362,7 @@ void ath9k_htc_set_tsfadjust(struct ath9k_htc_priv *priv,
{
struct ath_common *common = ath9k_hw_common(priv->ah);
struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv;
- struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf;
+ struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
u64 tsfadjust;
if (avp->bslot == 0)
@@ -536,7 +394,7 @@ static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv,
struct ieee80211_vif *vif)
{
struct ath_common *common = ath9k_hw_common(priv->ah);
- struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf;
+ struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
bool beacon_configured;
@@ -591,7 +449,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
struct ieee80211_vif *vif)
{
struct ath_common *common = ath9k_hw_common(priv->ah);
- struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf;
+ struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
@@ -627,7 +485,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv)
{
struct ath_common *common = ath9k_hw_common(priv->ah);
- struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf;
+ struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
switch (priv->ah->opmode) {
case NL80211_IFTYPE_STATION:
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index c57d6b859c04..8a3bd5fe3a54 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -38,93 +38,6 @@ static int ath9k_ps_enable;
module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
-#define CHAN2G(_freq, _idx) { \
- .center_freq = (_freq), \
- .hw_value = (_idx), \
- .max_power = 20, \
-}
-
-#define CHAN5G(_freq, _idx) { \
- .band = IEEE80211_BAND_5GHZ, \
- .center_freq = (_freq), \
- .hw_value = (_idx), \
- .max_power = 20, \
-}
-
-static struct ieee80211_channel ath9k_2ghz_channels[] = {
- CHAN2G(2412, 0), /* Channel 1 */
- CHAN2G(2417, 1), /* Channel 2 */
- CHAN2G(2422, 2), /* Channel 3 */
- CHAN2G(2427, 3), /* Channel 4 */
- CHAN2G(2432, 4), /* Channel 5 */
- CHAN2G(2437, 5), /* Channel 6 */
- CHAN2G(2442, 6), /* Channel 7 */
- CHAN2G(2447, 7), /* Channel 8 */
- CHAN2G(2452, 8), /* Channel 9 */
- CHAN2G(2457, 9), /* Channel 10 */
- CHAN2G(2462, 10), /* Channel 11 */
- CHAN2G(2467, 11), /* Channel 12 */
- CHAN2G(2472, 12), /* Channel 13 */
- CHAN2G(2484, 13), /* Channel 14 */
-};
-
-static struct ieee80211_channel ath9k_5ghz_channels[] = {
- /* _We_ call this UNII 1 */
- CHAN5G(5180, 14), /* Channel 36 */
- CHAN5G(5200, 15), /* Channel 40 */
- CHAN5G(5220, 16), /* Channel 44 */
- CHAN5G(5240, 17), /* Channel 48 */
- /* _We_ call this UNII 2 */
- CHAN5G(5260, 18), /* Channel 52 */
- CHAN5G(5280, 19), /* Channel 56 */
- CHAN5G(5300, 20), /* Channel 60 */
- CHAN5G(5320, 21), /* Channel 64 */
- /* _We_ call this "Middle band" */
- CHAN5G(5500, 22), /* Channel 100 */
- CHAN5G(5520, 23), /* Channel 104 */
- CHAN5G(5540, 24), /* Channel 108 */
- CHAN5G(5560, 25), /* Channel 112 */
- CHAN5G(5580, 26), /* Channel 116 */
- CHAN5G(5600, 27), /* Channel 120 */
- CHAN5G(5620, 28), /* Channel 124 */
- CHAN5G(5640, 29), /* Channel 128 */
- CHAN5G(5660, 30), /* Channel 132 */
- CHAN5G(5680, 31), /* Channel 136 */
- CHAN5G(5700, 32), /* Channel 140 */
- /* _We_ call this UNII 3 */
- CHAN5G(5745, 33), /* Channel 149 */
- CHAN5G(5765, 34), /* Channel 153 */
- CHAN5G(5785, 35), /* Channel 157 */
- CHAN5G(5805, 36), /* Channel 161 */
- CHAN5G(5825, 37), /* Channel 165 */
-};
-
-/* Atheros hardware rate code addition for short premble */
-#define SHPCHECK(__hw_rate, __flags) \
- ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04) : 0)
-
-#define RATE(_bitrate, _hw_rate, _flags) { \
- .bitrate = (_bitrate), \
- .flags = (_flags), \
- .hw_value = (_hw_rate), \
- .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \
-}
-
-static struct ieee80211_rate ath9k_legacy_rates[] = {
- RATE(10, 0x1b, 0),
- RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp : 0x1e */
- RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp: 0x1d */
- RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), /* short: 0x1c */
- RATE(60, 0x0b, 0),
- RATE(90, 0x0f, 0),
- RATE(120, 0x0a, 0),
- RATE(180, 0x0e, 0),
- RATE(240, 0x09, 0),
- RATE(360, 0x0d, 0),
- RATE(480, 0x08, 0),
- RATE(540, 0x0c, 0),
-};
-
#ifdef CONFIG_MAC80211_LEDS
static const struct ieee80211_tpt_blink ath9k_htc_tpt_blink[] = {
{ .throughput = 0 * 1024, .blink_time = 334 },
@@ -343,6 +256,25 @@ static void ath9k_multi_regread(void *hw_priv, u32 *addr,
}
}
+static void ath9k_regwrite_multi(struct ath_common *common)
+{
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+ u32 rsp_status;
+ int r;
+
+ r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID,
+ (u8 *) &priv->wmi->multi_write,
+ sizeof(struct register_write) * priv->wmi->multi_write_idx,
+ (u8 *) &rsp_status, sizeof(rsp_status),
+ 100);
+ if (unlikely(r)) {
+ ath_dbg(common, WMI,
+ "REGISTER WRITE FAILED, multi len: %d\n",
+ priv->wmi->multi_write_idx);
+ }
+ priv->wmi->multi_write_idx = 0;
+}
+
static void ath9k_regwrite_single(void *hw_priv, u32 val, u32 reg_offset)
{
struct ath_hw *ah = (struct ath_hw *) hw_priv;
@@ -369,8 +301,6 @@ static void ath9k_regwrite_buffer(void *hw_priv, u32 val, u32 reg_offset)
struct ath_hw *ah = (struct ath_hw *) hw_priv;
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
- u32 rsp_status;
- int r;
mutex_lock(&priv->wmi->multi_write_mutex);
@@ -383,19 +313,8 @@ static void ath9k_regwrite_buffer(void *hw_priv, u32 val, u32 reg_offset)
priv->wmi->multi_write_idx++;
/* If the buffer is full, send it out. */
- if (priv->wmi->multi_write_idx == MAX_CMD_NUMBER) {
- r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID,
- (u8 *) &priv->wmi->multi_write,
- sizeof(struct register_write) * priv->wmi->multi_write_idx,
- (u8 *) &rsp_status, sizeof(rsp_status),
- 100);
- if (unlikely(r)) {
- ath_dbg(common, WMI,
- "REGISTER WRITE FAILED, multi len: %d\n",
- priv->wmi->multi_write_idx);
- }
- priv->wmi->multi_write_idx = 0;
- }
+ if (priv->wmi->multi_write_idx == MAX_CMD_NUMBER)
+ ath9k_regwrite_multi(common);
mutex_unlock(&priv->wmi->multi_write_mutex);
}
@@ -426,26 +345,13 @@ static void ath9k_regwrite_flush(void *hw_priv)
struct ath_hw *ah = (struct ath_hw *) hw_priv;
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
- u32 rsp_status;
- int r;
atomic_dec(&priv->wmi->mwrite_cnt);
mutex_lock(&priv->wmi->multi_write_mutex);
- if (priv->wmi->multi_write_idx) {
- r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID,
- (u8 *) &priv->wmi->multi_write,
- sizeof(struct register_write) * priv->wmi->multi_write_idx,
- (u8 *) &rsp_status, sizeof(rsp_status),
- 100);
- if (unlikely(r)) {
- ath_dbg(common, WMI,
- "REGISTER WRITE FAILED, multi len: %d\n",
- priv->wmi->multi_write_idx);
- }
- priv->wmi->multi_write_idx = 0;
- }
+ if (priv->wmi->multi_write_idx)
+ ath9k_regwrite_multi(common);
mutex_unlock(&priv->wmi->multi_write_mutex);
}
@@ -491,51 +397,6 @@ static const struct ath_bus_ops ath9k_usb_bus_ops = {
.eeprom_read = ath_usb_eeprom_read,
};
-static void setup_ht_cap(struct ath9k_htc_priv *priv,
- struct ieee80211_sta_ht_cap *ht_info)
-{
- struct ath_common *common = ath9k_hw_common(priv->ah);
- u8 tx_streams, rx_streams;
- int i;
-
- ht_info->ht_supported = true;
- ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
- IEEE80211_HT_CAP_SM_PS |
- IEEE80211_HT_CAP_SGI_40 |
- IEEE80211_HT_CAP_DSSSCCK40;
-
- if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_SGI_20)
- ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
-
- ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
-
- ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
- ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
-
- memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
-
- /* ath9k_htc supports only 1 or 2 stream devices */
- tx_streams = ath9k_cmn_count_streams(priv->ah->txchainmask, 2);
- rx_streams = ath9k_cmn_count_streams(priv->ah->rxchainmask, 2);
-
- ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n",
- tx_streams, rx_streams);
-
- if (tx_streams >= 2)
- ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
-
- if (tx_streams != rx_streams) {
- ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
- ht_info->mcs.tx_params |= ((tx_streams - 1) <<
- IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
- }
-
- for (i = 0; i < rx_streams; i++)
- ht_info->mcs.rx_mask[i] = 0xff;
-
- ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
-}
-
static int ath9k_init_queues(struct ath9k_htc_priv *priv)
{
struct ath_common *common = ath9k_hw_common(priv->ah);
@@ -544,8 +405,8 @@ static int ath9k_init_queues(struct ath9k_htc_priv *priv)
for (i = 0; i < ARRAY_SIZE(priv->hwq_map); i++)
priv->hwq_map[i] = -1;
- priv->beaconq = ath9k_hw_beaconq_setup(priv->ah);
- if (priv->beaconq == -1) {
+ priv->beacon.beaconq = ath9k_hw_beaconq_setup(priv->ah);
+ if (priv->beacon.beaconq == -1) {
ath_err(common, "Unable to setup BEACON xmit queue\n");
goto err;
}
@@ -580,37 +441,13 @@ err:
return -EINVAL;
}
-static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv)
-{
- if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) {
- priv->sbands[IEEE80211_BAND_2GHZ].channels =
- ath9k_2ghz_channels;
- priv->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
- priv->sbands[IEEE80211_BAND_2GHZ].n_channels =
- ARRAY_SIZE(ath9k_2ghz_channels);
- priv->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates;
- priv->sbands[IEEE80211_BAND_2GHZ].n_bitrates =
- ARRAY_SIZE(ath9k_legacy_rates);
- }
-
- if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) {
- priv->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_channels;
- priv->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
- priv->sbands[IEEE80211_BAND_5GHZ].n_channels =
- ARRAY_SIZE(ath9k_5ghz_channels);
- priv->sbands[IEEE80211_BAND_5GHZ].bitrates =
- ath9k_legacy_rates + 4;
- priv->sbands[IEEE80211_BAND_5GHZ].n_bitrates =
- ARRAY_SIZE(ath9k_legacy_rates) - 4;
- }
-}
-
static void ath9k_init_misc(struct ath9k_htc_priv *priv)
{
struct ath_common *common = ath9k_hw_common(priv->ah);
memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
+ common->last_rssi = ATH_RSSI_DUMMY_MARKER;
priv->ah->opmode = NL80211_IFTYPE_STATION;
}
@@ -622,12 +459,11 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
struct ath_common *common;
int i, ret = 0, csz = 0;
- set_bit(OP_INVALID, &priv->op_flags);
-
ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL);
if (!ah)
return -ENOMEM;
+ ah->dev = priv->dev;
ah->hw_version.devid = devid;
ah->hw_version.usbdev = drv_info;
ah->ah_flags |= AH_USE_EEPROM;
@@ -647,6 +483,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
common->priv = priv;
common->debug_mask = ath9k_debug;
common->btcoex_enabled = ath9k_htc_btcoex_enable == 1;
+ set_bit(ATH_OP_INVALID, &common->op_flags);
spin_lock_init(&priv->beacon_lock);
spin_lock_init(&priv->tx.tx_lock);
@@ -682,10 +519,11 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
goto err_queues;
for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++)
- priv->cur_beacon_conf.bslot[i] = NULL;
+ priv->beacon.bslot[i] = NULL;
+ priv->beacon.slottime = ATH9K_SLOT_TIME_9;
+ ath9k_cmn_init_channels_rates(common);
ath9k_cmn_init_crypto(ah);
- ath9k_init_channels_rates(priv);
ath9k_init_misc(priv);
ath9k_htc_init_btcoex(priv, product);
@@ -721,6 +559,7 @@ static const struct ieee80211_iface_combination if_comb = {
static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
struct ieee80211_hw *hw)
{
+ struct ath_hw *ah = priv->ah;
struct ath_common *common = ath9k_hw_common(priv->ah);
struct base_eep_header *pBase;
@@ -765,19 +604,12 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
- &priv->sbands[IEEE80211_BAND_2GHZ];
+ &common->sbands[IEEE80211_BAND_2GHZ];
if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
- &priv->sbands[IEEE80211_BAND_5GHZ];
-
- if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
- if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
- setup_ht_cap(priv,
- &priv->sbands[IEEE80211_BAND_2GHZ].ht_cap);
- if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
- setup_ht_cap(priv,
- &priv->sbands[IEEE80211_BAND_5GHZ].ht_cap);
- }
+ &common->sbands[IEEE80211_BAND_5GHZ];
+
+ ath9k_cmn_reload_chainmask(ah);
pBase = ath9k_htc_get_eeprom_base(priv);
if (pBase) {
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index c9254a61ca52..f46cd0250e48 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -250,7 +250,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
u8 cmd_rsp;
int ret;
- if (test_bit(OP_INVALID, &priv->op_flags))
+ if (test_bit(ATH_OP_INVALID, &common->op_flags))
return -EIO;
fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
@@ -304,7 +304,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
htc_start(priv->htc);
- if (!test_bit(OP_SCANNING, &priv->op_flags) &&
+ if (!test_bit(ATH_OP_SCANNING, &common->op_flags) &&
!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
ath9k_htc_vif_reconfig(priv);
@@ -748,7 +748,7 @@ void ath9k_htc_start_ani(struct ath9k_htc_priv *priv)
common->ani.shortcal_timer = timestamp;
common->ani.checkani_timer = timestamp;
- set_bit(OP_ANI_RUNNING, &priv->op_flags);
+ set_bit(ATH_OP_ANI_RUN, &common->op_flags);
ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
@@ -756,8 +756,9 @@ void ath9k_htc_start_ani(struct ath9k_htc_priv *priv)
void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv)
{
+ struct ath_common *common = ath9k_hw_common(priv->ah);
cancel_delayed_work_sync(&priv->ani_work);
- clear_bit(OP_ANI_RUNNING, &priv->op_flags);
+ clear_bit(ATH_OP_ANI_RUN, &common->op_flags);
}
void ath9k_htc_ani_work(struct work_struct *work)
@@ -942,7 +943,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
ath_dbg(common, CONFIG,
"Failed to update capability in target\n");
- clear_bit(OP_INVALID, &priv->op_flags);
+ clear_bit(ATH_OP_INVALID, &common->op_flags);
htc_start(priv->htc);
spin_lock_bh(&priv->tx.tx_lock);
@@ -971,7 +972,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
mutex_lock(&priv->mutex);
- if (test_bit(OP_INVALID, &priv->op_flags)) {
+ if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
ath_dbg(common, ANY, "Device not present\n");
mutex_unlock(&priv->mutex);
return;
@@ -1013,7 +1014,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
ath9k_htc_ps_restore(priv);
ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
- set_bit(OP_INVALID, &priv->op_flags);
+ set_bit(ATH_OP_INVALID, &common->op_flags);
ath_dbg(common, CONFIG, "Driver halt\n");
mutex_unlock(&priv->mutex);
@@ -1087,7 +1088,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
ath9k_htc_set_opmode(priv);
if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
- !test_bit(OP_ANI_RUNNING, &priv->op_flags)) {
+ !test_bit(ATH_OP_ANI_RUN, &common->op_flags)) {
ath9k_hw_set_tsfadjust(priv->ah, true);
ath9k_htc_start_ani(priv);
}
@@ -1245,13 +1246,14 @@ static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
u64 multicast)
{
struct ath9k_htc_priv *priv = hw->priv;
+ struct ath_common *common = ath9k_hw_common(priv->ah);
u32 rfilt;
mutex_lock(&priv->mutex);
changed_flags &= SUPPORTED_FILTERS;
*total_flags &= SUPPORTED_FILTERS;
- if (test_bit(OP_INVALID, &priv->op_flags)) {
+ if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
ath_dbg(ath9k_hw_common(priv->ah), ANY,
"Unable to configure filter on invalid state\n");
mutex_unlock(&priv->mutex);
@@ -1474,7 +1476,9 @@ static void ath9k_htc_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
if ((vif->type == NL80211_IFTYPE_STATION) && bss_conf->assoc) {
common->curaid = bss_conf->aid;
+ common->last_rssi = ATH_RSSI_DUMMY_MARKER;
memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
+ set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
}
}
@@ -1496,6 +1500,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
struct ath9k_htc_priv *priv = hw->priv;
struct ath_hw *ah = priv->ah;
struct ath_common *common = ath9k_hw_common(ah);
+ int slottime;
mutex_lock(&priv->mutex);
ath9k_htc_ps_wakeup(priv);
@@ -1507,6 +1512,9 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
bss_conf->assoc ?
priv->num_sta_assoc_vif++ : priv->num_sta_assoc_vif--;
+ if (!bss_conf->assoc)
+ clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
+
if (priv->ah->opmode == NL80211_IFTYPE_STATION) {
ath9k_htc_choose_set_bssid(priv);
if (bss_conf->assoc && (priv->num_sta_assoc_vif == 1))
@@ -1528,7 +1536,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
ath_dbg(common, CONFIG, "Beacon enabled for BSS: %pM\n",
bss_conf->bssid);
ath9k_htc_set_tsfadjust(priv, vif);
- set_bit(OP_ENABLE_BEACON, &priv->op_flags);
+ priv->cur_beacon_conf.enable_beacon = 1;
ath9k_htc_beacon_config(priv, vif);
}
@@ -1542,7 +1550,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
ath_dbg(common, CONFIG,
"Beacon disabled for BSS: %pM\n",
bss_conf->bssid);
- clear_bit(OP_ENABLE_BEACON, &priv->op_flags);
+ priv->cur_beacon_conf.enable_beacon = 0;
ath9k_htc_beacon_config(priv, vif);
}
}
@@ -1568,11 +1576,21 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ERP_SLOT) {
if (bss_conf->use_short_slot)
- ah->slottime = 9;
+ slottime = 9;
else
- ah->slottime = 20;
-
- ath9k_hw_init_global_settings(ah);
+ slottime = 20;
+ if (vif->type == NL80211_IFTYPE_AP) {
+ /*
+ * Defer update, so that connected stations can adjust
+ * their settings at the same time.
+ * See beacon.c for more details
+ */
+ priv->beacon.slottime = slottime;
+ priv->beacon.updateslot = UPDATE;
+ } else {
+ ah->slottime = slottime;
+ ath9k_hw_init_global_settings(ah);
+ }
}
if (changed & BSS_CHANGED_HT)
@@ -1669,10 +1687,11 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
{
struct ath9k_htc_priv *priv = hw->priv;
+ struct ath_common *common = ath9k_hw_common(priv->ah);
mutex_lock(&priv->mutex);
spin_lock_bh(&priv->beacon_lock);
- set_bit(OP_SCANNING, &priv->op_flags);
+ set_bit(ATH_OP_SCANNING, &common->op_flags);
spin_unlock_bh(&priv->beacon_lock);
cancel_work_sync(&priv->ps_work);
ath9k_htc_stop_ani(priv);
@@ -1682,10 +1701,11 @@ static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
{
struct ath9k_htc_priv *priv = hw->priv;
+ struct ath_common *common = ath9k_hw_common(priv->ah);
mutex_lock(&priv->mutex);
spin_lock_bh(&priv->beacon_lock);
- clear_bit(OP_SCANNING, &priv->op_flags);
+ clear_bit(ATH_OP_SCANNING, &common->op_flags);
spin_unlock_bh(&priv->beacon_lock);
ath9k_htc_ps_wakeup(priv);
ath9k_htc_vif_reconfig(priv);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index 12e0f32a4905..e8149e3dbdd5 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -924,46 +924,43 @@ static void ath9k_htc_opmode_init(struct ath9k_htc_priv *priv)
void ath9k_host_rx_init(struct ath9k_htc_priv *priv)
{
+ struct ath_common *common = ath9k_hw_common(priv->ah);
ath9k_hw_rxena(priv->ah);
ath9k_htc_opmode_init(priv);
- ath9k_hw_startpcureceive(priv->ah, test_bit(OP_SCANNING, &priv->op_flags));
- priv->rx.last_rssi = ATH_RSSI_DUMMY_MARKER;
+ ath9k_hw_startpcureceive(priv->ah, test_bit(ATH_OP_SCANNING, &common->op_flags));
}
-static void ath9k_process_rate(struct ieee80211_hw *hw,
- struct ieee80211_rx_status *rxs,
- u8 rx_rate, u8 rs_flags)
+static inline void convert_htc_flag(struct ath_rx_status *rx_stats,
+ struct ath_htc_rx_status *rxstatus)
{
- struct ieee80211_supported_band *sband;
- enum ieee80211_band band;
- unsigned int i = 0;
-
- if (rx_rate & 0x80) {
- /* HT rate */
- rxs->flag |= RX_FLAG_HT;
- if (rs_flags & ATH9K_RX_2040)
- rxs->flag |= RX_FLAG_40MHZ;
- if (rs_flags & ATH9K_RX_GI)
- rxs->flag |= RX_FLAG_SHORT_GI;
- rxs->rate_idx = rx_rate & 0x7f;
- return;
- }
-
- band = hw->conf.chandef.chan->band;
- sband = hw->wiphy->bands[band];
-
- for (i = 0; i < sband->n_bitrates; i++) {
- if (sband->bitrates[i].hw_value == rx_rate) {
- rxs->rate_idx = i;
- return;
- }
- if (sband->bitrates[i].hw_value_short == rx_rate) {
- rxs->rate_idx = i;
- rxs->flag |= RX_FLAG_SHORTPRE;
- return;
- }
- }
+ rx_stats->flag = 0;
+ if (rxstatus->rs_flags & ATH9K_RX_2040)
+ rx_stats->flag |= RX_FLAG_40MHZ;
+ if (rxstatus->rs_flags & ATH9K_RX_GI)
+ rx_stats->flag |= RX_FLAG_SHORT_GI;
+}
+static void rx_status_htc_to_ath(struct ath_rx_status *rx_stats,
+ struct ath_htc_rx_status *rxstatus)
+{
+ rx_stats->rs_datalen = rxstatus->rs_datalen;
+ rx_stats->rs_status = rxstatus->rs_status;
+ rx_stats->rs_phyerr = rxstatus->rs_phyerr;
+ rx_stats->rs_rssi = rxstatus->rs_rssi;
+ rx_stats->rs_keyix = rxstatus->rs_keyix;
+ rx_stats->rs_rate = rxstatus->rs_rate;
+ rx_stats->rs_antenna = rxstatus->rs_antenna;
+ rx_stats->rs_more = rxstatus->rs_more;
+
+ memcpy(rx_stats->rs_rssi_ctl, rxstatus->rs_rssi_ctl,
+ sizeof(rx_stats->rs_rssi_ctl));
+ memcpy(rx_stats->rs_rssi_ext, rxstatus->rs_rssi_ext,
+ sizeof(rx_stats->rs_rssi_ext));
+
+ rx_stats->rs_isaggr = rxstatus->rs_isaggr;
+ rx_stats->rs_moreaggr = rxstatus->rs_moreaggr;
+ rx_stats->rs_num_delims = rxstatus->rs_num_delims;
+ convert_htc_flag(rx_stats, rxstatus);
}
static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
@@ -975,10 +972,10 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
struct ieee80211_hw *hw = priv->hw;
struct sk_buff *skb = rxbuf->skb;
struct ath_common *common = ath9k_hw_common(priv->ah);
+ struct ath_hw *ah = common->ah;
struct ath_htc_rx_status *rxstatus;
- int hdrlen, padsize;
- int last_rssi = ATH_RSSI_DUMMY_MARKER;
- __le16 fc;
+ struct ath_rx_status rx_stats;
+ bool decrypt_error;
if (skb->len < HTC_RX_FRAME_HEADER_SIZE) {
ath_err(common, "Corrupted RX frame, dropping (len: %d)\n",
@@ -999,103 +996,39 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
ath9k_htc_err_stat_rx(priv, rxstatus);
/* Get the RX status information */
- memcpy(&rxbuf->rxstatus, rxstatus, HTC_RX_FRAME_HEADER_SIZE);
- skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE);
-
- hdr = (struct ieee80211_hdr *)skb->data;
- fc = hdr->frame_control;
- hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-
- padsize = hdrlen & 3;
- if (padsize && skb->len >= hdrlen+padsize+FCS_LEN) {
- memmove(skb->data + padsize, skb->data, hdrlen);
- skb_pull(skb, padsize);
- }
memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
- if (rxbuf->rxstatus.rs_status != 0) {
- if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_CRC)
- rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
- if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_PHY)
- goto rx_next;
-
- if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT) {
- /* FIXME */
- } else if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_MIC) {
- if (ieee80211_is_ctl(fc))
- /*
- * Sometimes, we get invalid
- * MIC failures on valid control frames.
- * Remove these mic errors.
- */
- rxbuf->rxstatus.rs_status &= ~ATH9K_RXERR_MIC;
- else
- rx_status->flag |= RX_FLAG_MMIC_ERROR;
- }
-
- /*
- * Reject error frames with the exception of
- * decryption and MIC failures. For monitor mode,
- * we also ignore the CRC error.
- */
- if (priv->ah->opmode == NL80211_IFTYPE_MONITOR) {
- if (rxbuf->rxstatus.rs_status &
- ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
- ATH9K_RXERR_CRC))
- goto rx_next;
- } else {
- if (rxbuf->rxstatus.rs_status &
- ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) {
- goto rx_next;
- }
- }
- }
-
- if (!(rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT)) {
- u8 keyix;
- keyix = rxbuf->rxstatus.rs_keyix;
- if (keyix != ATH9K_RXKEYIX_INVALID) {
- rx_status->flag |= RX_FLAG_DECRYPTED;
- } else if (ieee80211_has_protected(fc) &&
- skb->len >= hdrlen + 4) {
- keyix = skb->data[hdrlen + 3] >> 6;
- if (test_bit(keyix, common->keymap))
- rx_status->flag |= RX_FLAG_DECRYPTED;
- }
- }
-
- ath9k_process_rate(hw, rx_status, rxbuf->rxstatus.rs_rate,
- rxbuf->rxstatus.rs_flags);
-
- if (rxbuf->rxstatus.rs_rssi != ATH9K_RSSI_BAD &&
- !rxbuf->rxstatus.rs_moreaggr)
- ATH_RSSI_LPF(priv->rx.last_rssi,
- rxbuf->rxstatus.rs_rssi);
-
- last_rssi = priv->rx.last_rssi;
+ /* Copy everything from ath_htc_rx_status (HTC_RX_FRAME_HEADER).
+ * After this, we can drop this part of skb. */
+ rx_status_htc_to_ath(&rx_stats, rxstatus);
+ rx_status->mactime = be64_to_cpu(rxstatus->rs_tstamp);
+ skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE);
- if (ath_is_mybeacon(common, hdr)) {
- s8 rssi = rxbuf->rxstatus.rs_rssi;
+ /*
+ * everything but the rate is checked here, the rate check is done
+ * separately to avoid doing two lookups for a rate for each frame.
+ */
+ hdr = (struct ieee80211_hdr *)skb->data;
+ if (!ath9k_cmn_rx_accept(common, hdr, rx_status, &rx_stats,
+ &decrypt_error, priv->rxfilter))
+ goto rx_next;
- if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
- rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER);
+ ath9k_cmn_rx_skb_postprocess(common, skb, &rx_stats,
+ rx_status, decrypt_error);
- if (rssi < 0)
- rssi = 0;
+ if (ath9k_cmn_process_rate(common, hw, &rx_stats, rx_status))
+ goto rx_next;
- priv->ah->stats.avgbrssi = rssi;
- }
+ rx_stats.is_mybeacon = ath_is_mybeacon(common, hdr);
+ ath9k_cmn_process_rssi(common, hw, &rx_stats, rx_status);
- rx_status->mactime = be64_to_cpu(rxbuf->rxstatus.rs_tstamp);
- rx_status->band = hw->conf.chandef.chan->band;
- rx_status->freq = hw->conf.chandef.chan->center_freq;
- rx_status->signal = rxbuf->rxstatus.rs_rssi + ATH_DEFAULT_NOISE_FLOOR;
- rx_status->antenna = rxbuf->rxstatus.rs_antenna;
+ rx_status->band = ah->curchan->chan->band;
+ rx_status->freq = ah->curchan->chan->center_freq;
+ rx_status->antenna = rx_stats.rs_antenna;
rx_status->flag |= RX_FLAG_MACTIME_END;
return true;
-
rx_next:
return false;
}
diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c
index aac4a406a513..a0ff5b637054 100644
--- a/drivers/net/wireless/ath/ath9k/htc_hst.c
+++ b/drivers/net/wireless/ath/ath9k/htc_hst.c
@@ -358,6 +358,36 @@ ret:
kfree_skb(skb);
}
+static void ath9k_htc_fw_panic_report(struct htc_target *htc_handle,
+ struct sk_buff *skb)
+{
+ uint32_t *pattern = (uint32_t *)skb->data;
+
+ switch (*pattern) {
+ case 0x33221199:
+ {
+ struct htc_panic_bad_vaddr *htc_panic;
+ htc_panic = (struct htc_panic_bad_vaddr *) skb->data;
+ dev_err(htc_handle->dev, "ath: firmware panic! "
+ "exccause: 0x%08x; pc: 0x%08x; badvaddr: 0x%08x.\n",
+ htc_panic->exccause, htc_panic->pc,
+ htc_panic->badvaddr);
+ break;
+ }
+ case 0x33221299:
+ {
+ struct htc_panic_bad_epid *htc_panic;
+ htc_panic = (struct htc_panic_bad_epid *) skb->data;
+ dev_err(htc_handle->dev, "ath: firmware panic! "
+ "bad epid: 0x%08x\n", htc_panic->epid);
+ break;
+ }
+ default:
+ dev_err(htc_handle->dev, "ath: uknown panic pattern!\n");
+ break;
+ }
+}
+
/*
* HTC Messages are handled directly here and the obtained SKB
* is freed.
@@ -379,6 +409,12 @@ void ath9k_htc_rx_msg(struct htc_target *htc_handle,
htc_hdr = (struct htc_frame_hdr *) skb->data;
epid = htc_hdr->endpoint_id;
+ if (epid == 0x99) {
+ ath9k_htc_fw_panic_report(htc_handle, skb);
+ kfree_skb(skb);
+ return;
+ }
+
if (epid >= ENDPOINT_MAX) {
if (pipe_id != USB_REG_IN_PIPE)
dev_kfree_skb_any(skb);
diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.h b/drivers/net/wireless/ath/ath9k/htc_hst.h
index e1ffbb6bd636..06474ccc7696 100644
--- a/drivers/net/wireless/ath/ath9k/htc_hst.h
+++ b/drivers/net/wireless/ath/ath9k/htc_hst.h
@@ -77,6 +77,18 @@ struct htc_config_pipe_msg {
u8 credits;
} __packed;
+struct htc_panic_bad_vaddr {
+ __be32 pattern;
+ __be32 exccause;
+ __be32 pc;
+ __be32 badvaddr;
+} __packed;
+
+struct htc_panic_bad_epid {
+ __be32 pattern;
+ __be32 epid;
+} __packed;
+
struct htc_ep_callbacks {
void *priv;
void (*tx) (void *, struct sk_buff *, enum htc_endpoint_id, bool txok);
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 9078a6c5a74e..c8a9dfab1fee 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -23,7 +23,6 @@
#include "hw.h"
#include "hw-ops.h"
-#include "rc.h"
#include "ar9003_mac.h"
#include "ar9003_mci.h"
#include "ar9003_phy.h"
@@ -883,7 +882,7 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
AR_IMR_RXORN |
AR_IMR_BCNMISC;
- if (AR_SREV_9340(ah) || AR_SREV_9550(ah))
+ if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah))
sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;
if (AR_SREV_9300_20_OR_LATER(ah)) {
@@ -3048,6 +3047,7 @@ static struct {
{ AR_SREV_VERSION_9462, "9462" },
{ AR_SREV_VERSION_9550, "9550" },
{ AR_SREV_VERSION_9565, "9565" },
+ { AR_SREV_VERSION_9531, "9531" },
};
/* For devices with external radios */
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 1fc2e5a26b52..c0a4e866edca 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -62,111 +62,6 @@ module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
bool is_ath9k_unloaded;
-/* We use the hw_value as an index into our private channel structure */
-
-#define CHAN2G(_freq, _idx) { \
- .band = IEEE80211_BAND_2GHZ, \
- .center_freq = (_freq), \
- .hw_value = (_idx), \
- .max_power = 20, \
-}
-
-#define CHAN5G(_freq, _idx) { \
- .band = IEEE80211_BAND_5GHZ, \
- .center_freq = (_freq), \
- .hw_value = (_idx), \
- .max_power = 20, \
-}
-
-/* Some 2 GHz radios are actually tunable on 2312-2732
- * on 5 MHz steps, we support the channels which we know
- * we have calibration data for all cards though to make
- * this static */
-static const struct ieee80211_channel ath9k_2ghz_chantable[] = {
- CHAN2G(2412, 0), /* Channel 1 */
- CHAN2G(2417, 1), /* Channel 2 */
- CHAN2G(2422, 2), /* Channel 3 */
- CHAN2G(2427, 3), /* Channel 4 */
- CHAN2G(2432, 4), /* Channel 5 */
- CHAN2G(2437, 5), /* Channel 6 */
- CHAN2G(2442, 6), /* Channel 7 */
- CHAN2G(2447, 7), /* Channel 8 */
- CHAN2G(2452, 8), /* Channel 9 */
- CHAN2G(2457, 9), /* Channel 10 */
- CHAN2G(2462, 10), /* Channel 11 */
- CHAN2G(2467, 11), /* Channel 12 */
- CHAN2G(2472, 12), /* Channel 13 */
- CHAN2G(2484, 13), /* Channel 14 */
-};
-
-/* Some 5 GHz radios are actually tunable on XXXX-YYYY
- * on 5 MHz steps, we support the channels which we know
- * we have calibration data for all cards though to make
- * this static */
-static const struct ieee80211_channel ath9k_5ghz_chantable[] = {
- /* _We_ call this UNII 1 */
- CHAN5G(5180, 14), /* Channel 36 */
- CHAN5G(5200, 15), /* Channel 40 */
- CHAN5G(5220, 16), /* Channel 44 */
- CHAN5G(5240, 17), /* Channel 48 */
- /* _We_ call this UNII 2 */
- CHAN5G(5260, 18), /* Channel 52 */
- CHAN5G(5280, 19), /* Channel 56 */
- CHAN5G(5300, 20), /* Channel 60 */
- CHAN5G(5320, 21), /* Channel 64 */
- /* _We_ call this "Middle band" */
- CHAN5G(5500, 22), /* Channel 100 */
- CHAN5G(5520, 23), /* Channel 104 */
- CHAN5G(5540, 24), /* Channel 108 */
- CHAN5G(5560, 25), /* Channel 112 */
- CHAN5G(5580, 26), /* Channel 116 */
- CHAN5G(5600, 27), /* Channel 120 */
- CHAN5G(5620, 28), /* Channel 124 */
- CHAN5G(5640, 29), /* Channel 128 */
- CHAN5G(5660, 30), /* Channel 132 */
- CHAN5G(5680, 31), /* Channel 136 */
- CHAN5G(5700, 32), /* Channel 140 */
- /* _We_ call this UNII 3 */
- CHAN5G(5745, 33), /* Channel 149 */
- CHAN5G(5765, 34), /* Channel 153 */
- CHAN5G(5785, 35), /* Channel 157 */
- CHAN5G(5805, 36), /* Channel 161 */
- CHAN5G(5825, 37), /* Channel 165 */
-};
-
-/* Atheros hardware rate code addition for short premble */
-#define SHPCHECK(__hw_rate, __flags) \
- ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0)
-
-#define RATE(_bitrate, _hw_rate, _flags) { \
- .bitrate = (_bitrate), \
- .flags = (_flags), \
- .hw_value = (_hw_rate), \
- .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \
-}
-
-static struct ieee80211_rate ath9k_legacy_rates[] = {
- RATE(10, 0x1b, 0),
- RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE),
- RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE),
- RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE),
- RATE(60, 0x0b, (IEEE80211_RATE_SUPPORTS_5MHZ |
- IEEE80211_RATE_SUPPORTS_10MHZ)),
- RATE(90, 0x0f, (IEEE80211_RATE_SUPPORTS_5MHZ |
- IEEE80211_RATE_SUPPORTS_10MHZ)),
- RATE(120, 0x0a, (IEEE80211_RATE_SUPPORTS_5MHZ |
- IEEE80211_RATE_SUPPORTS_10MHZ)),
- RATE(180, 0x0e, (IEEE80211_RATE_SUPPORTS_5MHZ |
- IEEE80211_RATE_SUPPORTS_10MHZ)),
- RATE(240, 0x09, (IEEE80211_RATE_SUPPORTS_5MHZ |
- IEEE80211_RATE_SUPPORTS_10MHZ)),
- RATE(360, 0x0d, (IEEE80211_RATE_SUPPORTS_5MHZ |
- IEEE80211_RATE_SUPPORTS_10MHZ)),
- RATE(480, 0x08, (IEEE80211_RATE_SUPPORTS_5MHZ |
- IEEE80211_RATE_SUPPORTS_10MHZ)),
- RATE(540, 0x0c, (IEEE80211_RATE_SUPPORTS_5MHZ |
- IEEE80211_RATE_SUPPORTS_10MHZ)),
-};
#ifdef CONFIG_MAC80211_LEDS
static const struct ieee80211_tpt_blink ath9k_tpt_blink[] = {
@@ -258,64 +153,6 @@ static unsigned int ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 cl
/* Initialization */
/**************************/
-static void setup_ht_cap(struct ath_softc *sc,
- struct ieee80211_sta_ht_cap *ht_info)
-{
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- u8 tx_streams, rx_streams;
- int i, max_streams;
-
- ht_info->ht_supported = true;
- ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
- IEEE80211_HT_CAP_SM_PS |
- IEEE80211_HT_CAP_SGI_40 |
- IEEE80211_HT_CAP_DSSSCCK40;
-
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_LDPC)
- ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING;
-
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_SGI_20)
- ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
-
- ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
- ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
-
- if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah))
- max_streams = 1;
- else if (AR_SREV_9462(ah))
- max_streams = 2;
- else if (AR_SREV_9300_20_OR_LATER(ah))
- max_streams = 3;
- else
- max_streams = 2;
-
- if (AR_SREV_9280_20_OR_LATER(ah)) {
- if (max_streams >= 2)
- ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
- ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
- }
-
- /* set up supported mcs set */
- memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
- tx_streams = ath9k_cmn_count_streams(ah->txchainmask, max_streams);
- rx_streams = ath9k_cmn_count_streams(ah->rxchainmask, max_streams);
-
- ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n",
- tx_streams, rx_streams);
-
- if (tx_streams != rx_streams) {
- ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
- ht_info->mcs.tx_params |= ((tx_streams - 1) <<
- IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
- }
-
- for (i = 0; i < rx_streams; i++)
- ht_info->mcs.rx_mask[i] = 0xff;
-
- ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
-}
-
static void ath9k_reg_notifier(struct wiphy *wiphy,
struct regulatory_request *request)
{
@@ -486,51 +323,6 @@ static int ath9k_init_queues(struct ath_softc *sc)
return 0;
}
-static int ath9k_init_channels_rates(struct ath_softc *sc)
-{
- void *channels;
-
- BUILD_BUG_ON(ARRAY_SIZE(ath9k_2ghz_chantable) +
- ARRAY_SIZE(ath9k_5ghz_chantable) !=
- ATH9K_NUM_CHANNELS);
-
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) {
- channels = devm_kzalloc(sc->dev,
- sizeof(ath9k_2ghz_chantable), GFP_KERNEL);
- if (!channels)
- return -ENOMEM;
-
- memcpy(channels, ath9k_2ghz_chantable,
- sizeof(ath9k_2ghz_chantable));
- sc->sbands[IEEE80211_BAND_2GHZ].channels = channels;
- sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
- sc->sbands[IEEE80211_BAND_2GHZ].n_channels =
- ARRAY_SIZE(ath9k_2ghz_chantable);
- sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates;
- sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates =
- ARRAY_SIZE(ath9k_legacy_rates);
- }
-
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) {
- channels = devm_kzalloc(sc->dev,
- sizeof(ath9k_5ghz_chantable), GFP_KERNEL);
- if (!channels)
- return -ENOMEM;
-
- memcpy(channels, ath9k_5ghz_chantable,
- sizeof(ath9k_5ghz_chantable));
- sc->sbands[IEEE80211_BAND_5GHZ].channels = channels;
- sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
- sc->sbands[IEEE80211_BAND_5GHZ].n_channels =
- ARRAY_SIZE(ath9k_5ghz_chantable);
- sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
- ath9k_legacy_rates + 4;
- sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates =
- ARRAY_SIZE(ath9k_legacy_rates) - 4;
- }
- return 0;
-}
-
static void ath9k_init_misc(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -538,7 +330,7 @@ static void ath9k_init_misc(struct ath_softc *sc)
setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
- sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
+ common->last_rssi = ATH_RSSI_DUMMY_MARKER;
sc->config.txpowlimit = ATH_TXPOWER_MAX;
memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
sc->beacon.slottime = ATH9K_SLOT_TIME_9;
@@ -793,7 +585,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
if (ret)
goto err_btcoex;
- ret = ath9k_init_channels_rates(sc);
+ ret = ath9k_cmn_init_channels_rates(common);
if (ret)
goto err_btcoex;
@@ -823,10 +615,11 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band)
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct cfg80211_chan_def chandef;
int i;
- sband = &sc->sbands[band];
+ sband = &common->sbands[band];
for (i = 0; i < sband->n_channels; i++) {
chan = &sband->channels[i];
ah->curchan = &ah->channels[chan->hw_value];
@@ -849,17 +642,6 @@ static void ath9k_init_txpower_limits(struct ath_softc *sc)
ah->curchan = curchan;
}
-void ath9k_reload_chainmask_settings(struct ath_softc *sc)
-{
- if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT))
- return;
-
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
-}
-
static const struct ieee80211_iface_limit if_limits[] = {
{ .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
@@ -949,6 +731,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ;
hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+ hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
hw->queues = 4;
hw->max_rates = 4;
@@ -969,13 +752,13 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
- &sc->sbands[IEEE80211_BAND_2GHZ];
+ &common->sbands[IEEE80211_BAND_2GHZ];
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
- &sc->sbands[IEEE80211_BAND_5GHZ];
+ &common->sbands[IEEE80211_BAND_5GHZ];
ath9k_init_wow(hw);
- ath9k_reload_chainmask_settings(sc);
+ ath9k_cmn_reload_chainmask(ah);
SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
}
@@ -1106,19 +889,11 @@ static int __init ath9k_init(void)
{
int error;
- /* Register rate control algorithm */
- error = ath_rate_control_register();
- if (error != 0) {
- pr_err("Unable to register rate control algorithm: %d\n",
- error);
- goto err_out;
- }
-
error = ath_pci_init();
if (error < 0) {
pr_err("No PCI devices found, driver not installed\n");
error = -ENODEV;
- goto err_rate_unregister;
+ goto err_out;
}
error = ath_ahb_init();
@@ -1131,9 +906,6 @@ static int __init ath9k_init(void)
err_pci_exit:
ath_pci_exit();
-
- err_rate_unregister:
- ath_rate_control_unregister();
err_out:
return error;
}
@@ -1144,7 +916,6 @@ static void __exit ath9k_exit(void)
is_ath9k_unloaded = true;
ath_ahb_exit();
ath_pci_exit();
- ath_rate_control_unregister();
pr_info("%s: Driver unloaded\n", dev_info);
}
module_exit(ath9k_exit);
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index 30dcef5aba10..72a715fe8f24 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -115,13 +115,14 @@ void ath_hw_pll_work(struct work_struct *work)
u32 pll_sqsum;
struct ath_softc *sc = container_of(work, struct ath_softc,
hw_pll_work.work);
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
/*
* ensure that the PLL WAR is executed only
* after the STA is associated (or) if the
* beaconing had started in interfaces that
* uses beacons.
*/
- if (!test_bit(SC_OP_BEACONS, &sc->sc_flags))
+ if (!test_bit(ATH_OP_BEACONS, &common->op_flags))
return;
if (sc->tx99_state)
@@ -414,7 +415,7 @@ void ath_start_ani(struct ath_softc *sc)
unsigned long timestamp = jiffies_to_msecs(jiffies);
if (common->disable_ani ||
- !test_bit(SC_OP_ANI_RUN, &sc->sc_flags) ||
+ !test_bit(ATH_OP_ANI_RUN, &common->op_flags) ||
(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
return;
@@ -438,6 +439,7 @@ void ath_stop_ani(struct ath_softc *sc)
void ath_check_ani(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
/*
@@ -453,23 +455,23 @@ void ath_check_ani(struct ath_softc *sc)
* Disable ANI only when there are no
* associated stations.
*/
- if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags))
+ if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
goto stop_ani;
}
} else if (ah->opmode == NL80211_IFTYPE_STATION) {
- if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags))
+ if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
goto stop_ani;
}
- if (!test_bit(SC_OP_ANI_RUN, &sc->sc_flags)) {
- set_bit(SC_OP_ANI_RUN, &sc->sc_flags);
+ if (!test_bit(ATH_OP_ANI_RUN, &common->op_flags)) {
+ set_bit(ATH_OP_ANI_RUN, &common->op_flags);
ath_start_ani(sc);
}
return;
stop_ani:
- clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
+ clear_bit(ATH_OP_ANI_RUN, &common->op_flags);
ath_stop_ani(sc);
}
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 5f727588ca27..51ce36f108f9 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -827,7 +827,7 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah)
return;
}
- if (AR_SREV_9340(ah) || AR_SREV_9550(ah))
+ if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah))
sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;
async_mask = AR_INTR_MAC_IRQ;
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 10271373a0cd..89df634e81f9 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -155,12 +155,8 @@ struct ath_htc_rx_status {
u8 rs_status;
u8 rs_phyerr;
int8_t rs_rssi;
- int8_t rs_rssi_ctl0;
- int8_t rs_rssi_ctl1;
- int8_t rs_rssi_ctl2;
- int8_t rs_rssi_ext0;
- int8_t rs_rssi_ext1;
- int8_t rs_rssi_ext2;
+ int8_t rs_rssi_ctl[3];
+ int8_t rs_rssi_ext[3];
u8 rs_keyix;
u8 rs_rate;
u8 rs_antenna;
@@ -170,6 +166,7 @@ struct ath_htc_rx_status {
u8 rs_num_delims;
u8 rs_flags;
u8 rs_dummy;
+ /* FIXME: evm* never used? */
__be32 evm0;
__be32 evm1;
__be32 evm2;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 5924f72dd493..d69853b848ce 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -229,16 +229,16 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
ath9k_cmn_update_txpow(ah, sc->curtxpow,
sc->config.txpowlimit, &sc->curtxpow);
- clear_bit(SC_OP_HW_RESET, &sc->sc_flags);
+ clear_bit(ATH_OP_HW_RESET, &common->op_flags);
ath9k_hw_set_interrupts(ah);
ath9k_hw_enable_interrupts(ah);
if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) {
- if (!test_bit(SC_OP_BEACONS, &sc->sc_flags))
+ if (!test_bit(ATH_OP_BEACONS, &common->op_flags))
goto work;
if (ah->opmode == NL80211_IFTYPE_STATION &&
- test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
+ test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
spin_lock_irqsave(&sc->sc_pm_lock, flags);
sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
@@ -336,7 +336,7 @@ static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chand
int old_pos = -1;
int r;
- if (test_bit(SC_OP_INVALID, &sc->sc_flags))
+ if (test_bit(ATH_OP_INVALID, &common->op_flags))
return -EIO;
offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
@@ -402,7 +402,7 @@ static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chand
chan->center_freq);
} else {
/* perform spectral scan if requested. */
- if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
+ if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
sc->spectral_mode == SPECTRAL_CHANSCAN)
ath9k_spectral_scan_trigger(hw);
}
@@ -451,7 +451,7 @@ void ath9k_tasklet(unsigned long data)
* interrupts are enabled in the reset routine.
*/
atomic_inc(&ah->intr_ref_cnt);
- ath_dbg(common, ANY, "FATAL: Skipping interrupts\n");
+ ath_dbg(common, RESET, "FATAL: Skipping interrupts\n");
goto out;
}
@@ -471,7 +471,7 @@ void ath9k_tasklet(unsigned long data)
* interrupts are enabled in the reset routine.
*/
atomic_inc(&ah->intr_ref_cnt);
- ath_dbg(common, ANY,
+ ath_dbg(common, RESET,
"BB_WATCHDOG: Skipping interrupts\n");
goto out;
}
@@ -484,7 +484,7 @@ void ath9k_tasklet(unsigned long data)
type = RESET_TYPE_TX_GTT;
ath9k_queue_reset(sc, type);
atomic_inc(&ah->intr_ref_cnt);
- ath_dbg(common, ANY,
+ ath_dbg(common, RESET,
"GTT: Skipping interrupts\n");
goto out;
}
@@ -566,6 +566,7 @@ irqreturn_t ath_isr(int irq, void *dev)
struct ath_softc *sc = dev;
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
enum ath9k_int status;
u32 sync_cause = 0;
bool sched = false;
@@ -575,7 +576,7 @@ irqreturn_t ath_isr(int irq, void *dev)
* touch anything. Note this can happen early
* on if the IRQ is shared.
*/
- if (test_bit(SC_OP_INVALID, &sc->sc_flags))
+ if (test_bit(ATH_OP_INVALID, &common->op_flags))
return IRQ_NONE;
/* shared irq, not for us */
@@ -583,7 +584,7 @@ irqreturn_t ath_isr(int irq, void *dev)
if (!ath9k_hw_intrpend(ah))
return IRQ_NONE;
- if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) {
+ if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
ath9k_hw_kill_interrupts(ah);
return IRQ_HANDLED;
}
@@ -684,10 +685,11 @@ int ath_reset(struct ath_softc *sc)
void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
#ifdef CONFIG_ATH9K_DEBUGFS
RESET_STAT_INC(sc, type);
#endif
- set_bit(SC_OP_HW_RESET, &sc->sc_flags);
+ set_bit(ATH_OP_HW_RESET, &common->op_flags);
ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
}
@@ -768,7 +770,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
ath_mci_enable(sc);
- clear_bit(SC_OP_INVALID, &sc->sc_flags);
+ clear_bit(ATH_OP_INVALID, &common->op_flags);
sc->sc_ah->is_monitoring = false;
if (!ath_complete_reset(sc, false))
@@ -885,7 +887,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
ath_cancel_work(sc);
- if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
+ if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
ath_dbg(common, ANY, "Device not present\n");
mutex_unlock(&sc->mutex);
return;
@@ -940,7 +942,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
ath9k_ps_restore(sc);
- set_bit(SC_OP_INVALID, &sc->sc_flags);
+ set_bit(ATH_OP_INVALID, &common->op_flags);
sc->ps_idle = prev_idle;
mutex_unlock(&sc->mutex);
@@ -1081,7 +1083,7 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
*/
if (ah->opmode == NL80211_IFTYPE_STATION &&
old_opmode == NL80211_IFTYPE_AP &&
- test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
+ test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
ieee80211_iterate_active_interfaces_atomic(
sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath9k_sta_vif_iter, sc);
@@ -1178,9 +1180,6 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
if (ath9k_uses_beacons(vif->type))
ath9k_beacon_remove_slot(sc, vif);
- if (sc->csa_vif == vif)
- sc->csa_vif = NULL;
-
ath9k_ps_wakeup(sc);
ath9k_calculate_summary_state(hw, NULL);
ath9k_ps_restore(sc);
@@ -1593,7 +1592,7 @@ static void ath9k_set_assoc_state(struct ath_softc *sc,
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
unsigned long flags;
- set_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags);
+ set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
avp->primary_sta_vif = true;
/*
@@ -1609,7 +1608,7 @@ static void ath9k_set_assoc_state(struct ath_softc *sc,
common->curaid = bss_conf->aid;
ath9k_hw_write_associd(sc->sc_ah);
- sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
+ common->last_rssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
spin_lock_irqsave(&sc->sc_pm_lock, flags);
@@ -1628,8 +1627,9 @@ static void ath9k_bss_assoc_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
{
struct ath_softc *sc = data;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- if (test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags))
+ if (test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
return;
if (bss_conf->assoc)
@@ -1660,18 +1660,18 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
bss_conf->bssid, bss_conf->assoc);
if (avp->primary_sta_vif && !bss_conf->assoc) {
- clear_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags);
+ clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
avp->primary_sta_vif = false;
if (ah->opmode == NL80211_IFTYPE_STATION)
- clear_bit(SC_OP_BEACONS, &sc->sc_flags);
+ clear_bit(ATH_OP_BEACONS, &common->op_flags);
}
ieee80211_iterate_active_interfaces_atomic(
sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath9k_bss_assoc_iter, sc);
- if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags) &&
+ if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags) &&
ah->opmode == NL80211_IFTYPE_STATION) {
memset(common->curbssid, 0, ETH_ALEN);
common->curaid = 0;
@@ -1866,7 +1866,7 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
static bool ath9k_has_tx_pending(struct ath_softc *sc)
{
- int i, npend;
+ int i, npend = 0;
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (!ATH_TXQ_SETUP(sc, i))
@@ -1900,7 +1900,7 @@ static void ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
return;
}
- if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
+ if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
ath_dbg(common, ANY, "Device not present\n");
mutex_unlock(&sc->mutex);
return;
@@ -2056,7 +2056,7 @@ static int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant);
ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant);
- ath9k_reload_chainmask_settings(sc);
+ ath9k_cmn_reload_chainmask(ah);
return 0;
}
@@ -2073,26 +2073,23 @@ static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
- set_bit(SC_OP_SCANNING, &sc->sc_flags);
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ set_bit(ATH_OP_SCANNING, &common->op_flags);
}
static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
- clear_bit(SC_OP_SCANNING, &sc->sc_flags);
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ clear_bit(ATH_OP_SCANNING, &common->op_flags);
}
static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_chan_def *chandef)
{
- struct ath_softc *sc = hw->priv;
-
- /* mac80211 does not support CSA in multi-if cases (yet) */
- if (WARN_ON(sc->csa_vif))
- return;
-
- sc->csa_vif = vif;
+ /* depend on vif->csa_active only */
+ return;
}
struct ieee80211_ops ath9k_ops = {
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c
index 71799fcade54..a0dbcc412384 100644
--- a/drivers/net/wireless/ath/ath9k/mci.c
+++ b/drivers/net/wireless/ath/ath9k/mci.c
@@ -555,7 +555,7 @@ void ath_mci_intr(struct ath_softc *sc)
mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_GPM;
while (more_data == MCI_GPM_MORE) {
- if (test_bit(SC_OP_HW_RESET, &sc->sc_flags))
+ if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
return;
pgpm = mci->gpm_buf.bf_addr;
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 55724b02316b..25304adece57 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -784,6 +784,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct ath_softc *sc;
struct ieee80211_hw *hw;
+ struct ath_common *common;
u8 csz;
u32 val;
int ret = 0;
@@ -858,9 +859,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
sc->mem = pcim_iomap_table(pdev)[0];
sc->driver_data = id->driver_data;
- /* Will be cleared in ath9k_start() */
- set_bit(SC_OP_INVALID, &sc->sc_flags);
-
ret = request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc);
if (ret) {
dev_err(&pdev->dev, "request_irq failed\n");
@@ -879,6 +877,10 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n",
hw_name, (unsigned long)sc->mem, pdev->irq);
+ /* Will be cleared in ath9k_start() */
+ common = ath9k_hw_common(sc->sc_ah);
+ set_bit(ATH_OP_INVALID, &common->op_flags);
+
return 0;
err_init:
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
deleted file mode 100644
index d829bb62a3fc..000000000000
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ /dev/null
@@ -1,1495 +0,0 @@
-/*
- * Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2004-2011 Atheros Communications, 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/slab.h>
-#include <linux/export.h>
-
-#include "ath9k.h"
-
-static const struct ath_rate_table ar5416_11na_ratetable = {
- 68,
- 8, /* MCS start */
- {
- [0] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 6000,
- 5400, 0, 12 }, /* 6 Mb */
- [1] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 9000,
- 7800, 1, 18 }, /* 9 Mb */
- [2] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000,
- 10000, 2, 24 }, /* 12 Mb */
- [3] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000,
- 13900, 3, 36 }, /* 18 Mb */
- [4] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000,
- 17300, 4, 48 }, /* 24 Mb */
- [5] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000,
- 23000, 5, 72 }, /* 36 Mb */
- [6] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000,
- 27400, 6, 96 }, /* 48 Mb */
- [7] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000,
- 29300, 7, 108 }, /* 54 Mb */
- [8] = { RC_HT_SDT_2040, WLAN_RC_PHY_HT_20_SS, 6500,
- 6400, 0, 0 }, /* 6.5 Mb */
- [9] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 13000,
- 12700, 1, 1 }, /* 13 Mb */
- [10] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 19500,
- 18800, 2, 2 }, /* 19.5 Mb */
- [11] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 26000,
- 25000, 3, 3 }, /* 26 Mb */
- [12] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 39000,
- 36700, 4, 4 }, /* 39 Mb */
- [13] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 52000,
- 48100, 5, 5 }, /* 52 Mb */
- [14] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 58500,
- 53500, 6, 6 }, /* 58.5 Mb */
- [15] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 65000,
- 59000, 7, 7 }, /* 65 Mb */
- [16] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS_HGI, 72200,
- 65400, 7, 7 }, /* 75 Mb */
- [17] = { RC_INVALID, WLAN_RC_PHY_HT_20_DS, 13000,
- 12700, 8, 8 }, /* 13 Mb */
- [18] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 26000,
- 24800, 9, 9 }, /* 26 Mb */
- [19] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 39000,
- 36600, 10, 10 }, /* 39 Mb */
- [20] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 52000,
- 48100, 11, 11 }, /* 52 Mb */
- [21] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 78000,
- 69500, 12, 12 }, /* 78 Mb */
- [22] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 104000,
- 89500, 13, 13 }, /* 104 Mb */
- [23] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 117000,
- 98900, 14, 14 }, /* 117 Mb */
- [24] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 130000,
- 108300, 15, 15 }, /* 130 Mb */
- [25] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS_HGI, 144400,
- 120000, 15, 15 }, /* 144.4 Mb */
- [26] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 19500,
- 17400, 16, 16 }, /* 19.5 Mb */
- [27] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 39000,
- 35100, 17, 17 }, /* 39 Mb */
- [28] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 58500,
- 52600, 18, 18 }, /* 58.5 Mb */
- [29] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 78000,
- 70400, 19, 19 }, /* 78 Mb */
- [30] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 117000,
- 104900, 20, 20 }, /* 117 Mb */
- [31] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS_HGI, 130000,
- 115800, 20, 20 }, /* 130 Mb*/
- [32] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 156000,
- 137200, 21, 21 }, /* 156 Mb */
- [33] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 173300,
- 151100, 21, 21 }, /* 173.3 Mb */
- [34] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 175500,
- 152800, 22, 22 }, /* 175.5 Mb */
- [35] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 195000,
- 168400, 22, 22 }, /* 195 Mb*/
- [36] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 195000,
- 168400, 23, 23 }, /* 195 Mb */
- [37] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 216700,
- 185000, 23, 23 }, /* 216.7 Mb */
- [38] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 13500,
- 13200, 0, 0 }, /* 13.5 Mb*/
- [39] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 27500,
- 25900, 1, 1 }, /* 27.0 Mb*/
- [40] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 40500,
- 38600, 2, 2 }, /* 40.5 Mb*/
- [41] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 54000,
- 49800, 3, 3 }, /* 54 Mb */
- [42] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 81500,
- 72200, 4, 4 }, /* 81 Mb */
- [43] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 108000,
- 92900, 5, 5 }, /* 108 Mb */
- [44] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 121500,
- 102700, 6, 6 }, /* 121.5 Mb*/
- [45] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 135000,
- 112000, 7, 7 }, /* 135 Mb */
- [46] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000,
- 122000, 7, 7 }, /* 150 Mb */
- [47] = { RC_INVALID, WLAN_RC_PHY_HT_40_DS, 27000,
- 25800, 8, 8 }, /* 27 Mb */
- [48] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 54000,
- 49800, 9, 9 }, /* 54 Mb */
- [49] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 81000,
- 71900, 10, 10 }, /* 81 Mb */
- [50] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 108000,
- 92500, 11, 11 }, /* 108 Mb */
- [51] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 162000,
- 130300, 12, 12 }, /* 162 Mb */
- [52] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 216000,
- 162800, 13, 13 }, /* 216 Mb */
- [53] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 243000,
- 178200, 14, 14 }, /* 243 Mb */
- [54] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 270000,
- 192100, 15, 15 }, /* 270 Mb */
- [55] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS_HGI, 300000,
- 207000, 15, 15 }, /* 300 Mb */
- [56] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 40500,
- 36100, 16, 16 }, /* 40.5 Mb */
- [57] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 81000,
- 72900, 17, 17 }, /* 81 Mb */
- [58] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 121500,
- 108300, 18, 18 }, /* 121.5 Mb */
- [59] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 162000,
- 142000, 19, 19 }, /* 162 Mb */
- [60] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 243000,
- 205100, 20, 20 }, /* 243 Mb */
- [61] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS_HGI, 270000,
- 224700, 20, 20 }, /* 270 Mb */
- [62] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 324000,
- 263100, 21, 21 }, /* 324 Mb */
- [63] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 360000,
- 288000, 21, 21 }, /* 360 Mb */
- [64] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 364500,
- 290700, 22, 22 }, /* 364.5 Mb */
- [65] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 405000,
- 317200, 22, 22 }, /* 405 Mb */
- [66] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 405000,
- 317200, 23, 23 }, /* 405 Mb */
- [67] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 450000,
- 346400, 23, 23 }, /* 450 Mb */
- },
- 50, /* probe interval */
- WLAN_RC_HT_FLAG, /* Phy rates allowed initially */
-};
-
-/* 4ms frame limit not used for NG mode. The values filled
- * for HT are the 64K max aggregate limit */
-
-static const struct ath_rate_table ar5416_11ng_ratetable = {
- 72,
- 12, /* MCS start */
- {
- [0] = { RC_ALL, WLAN_RC_PHY_CCK, 1000,
- 900, 0, 2 }, /* 1 Mb */
- [1] = { RC_ALL, WLAN_RC_PHY_CCK, 2000,
- 1900, 1, 4 }, /* 2 Mb */
- [2] = { RC_ALL, WLAN_RC_PHY_CCK, 5500,
- 4900, 2, 11 }, /* 5.5 Mb */
- [3] = { RC_ALL, WLAN_RC_PHY_CCK, 11000,
- 8100, 3, 22 }, /* 11 Mb */
- [4] = { RC_INVALID, WLAN_RC_PHY_OFDM, 6000,
- 5400, 4, 12 }, /* 6 Mb */
- [5] = { RC_INVALID, WLAN_RC_PHY_OFDM, 9000,
- 7800, 5, 18 }, /* 9 Mb */
- [6] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000,
- 10100, 6, 24 }, /* 12 Mb */
- [7] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000,
- 14100, 7, 36 }, /* 18 Mb */
- [8] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000,
- 17700, 8, 48 }, /* 24 Mb */
- [9] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000,
- 23700, 9, 72 }, /* 36 Mb */
- [10] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000,
- 27400, 10, 96 }, /* 48 Mb */
- [11] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000,
- 30900, 11, 108 }, /* 54 Mb */
- [12] = { RC_INVALID, WLAN_RC_PHY_HT_20_SS, 6500,
- 6400, 0, 0 }, /* 6.5 Mb */
- [13] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 13000,
- 12700, 1, 1 }, /* 13 Mb */
- [14] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 19500,
- 18800, 2, 2 }, /* 19.5 Mb*/
- [15] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 26000,
- 25000, 3, 3 }, /* 26 Mb */
- [16] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 39000,
- 36700, 4, 4 }, /* 39 Mb */
- [17] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 52000,
- 48100, 5, 5 }, /* 52 Mb */
- [18] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 58500,
- 53500, 6, 6 }, /* 58.5 Mb */
- [19] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 65000,
- 59000, 7, 7 }, /* 65 Mb */
- [20] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS_HGI, 72200,
- 65400, 7, 7 }, /* 65 Mb*/
- [21] = { RC_INVALID, WLAN_RC_PHY_HT_20_DS, 13000,
- 12700, 8, 8 }, /* 13 Mb */
- [22] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 26000,
- 24800, 9, 9 }, /* 26 Mb */
- [23] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 39000,
- 36600, 10, 10 }, /* 39 Mb */
- [24] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 52000,
- 48100, 11, 11 }, /* 52 Mb */
- [25] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 78000,
- 69500, 12, 12 }, /* 78 Mb */
- [26] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 104000,
- 89500, 13, 13 }, /* 104 Mb */
- [27] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 117000,
- 98900, 14, 14 }, /* 117 Mb */
- [28] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 130000,
- 108300, 15, 15 }, /* 130 Mb */
- [29] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS_HGI, 144400,
- 120000, 15, 15 }, /* 144.4 Mb */
- [30] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 19500,
- 17400, 16, 16 }, /* 19.5 Mb */
- [31] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 39000,
- 35100, 17, 17 }, /* 39 Mb */
- [32] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 58500,
- 52600, 18, 18 }, /* 58.5 Mb */
- [33] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 78000,
- 70400, 19, 19 }, /* 78 Mb */
- [34] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 117000,
- 104900, 20, 20 }, /* 117 Mb */
- [35] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS_HGI, 130000,
- 115800, 20, 20 }, /* 130 Mb */
- [36] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 156000,
- 137200, 21, 21 }, /* 156 Mb */
- [37] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 173300,
- 151100, 21, 21 }, /* 173.3 Mb */
- [38] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 175500,
- 152800, 22, 22 }, /* 175.5 Mb */
- [39] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 195000,
- 168400, 22, 22 }, /* 195 Mb */
- [40] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 195000,
- 168400, 23, 23 }, /* 195 Mb */
- [41] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 216700,
- 185000, 23, 23 }, /* 216.7 Mb */
- [42] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 13500,
- 13200, 0, 0 }, /* 13.5 Mb */
- [43] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 27500,
- 25900, 1, 1 }, /* 27.0 Mb */
- [44] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 40500,
- 38600, 2, 2 }, /* 40.5 Mb */
- [45] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 54000,
- 49800, 3, 3 }, /* 54 Mb */
- [46] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 81500,
- 72200, 4, 4 }, /* 81 Mb */
- [47] = { RC_HT_S_40 , WLAN_RC_PHY_HT_40_SS, 108000,
- 92900, 5, 5 }, /* 108 Mb */
- [48] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 121500,
- 102700, 6, 6 }, /* 121.5 Mb */
- [49] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 135000,
- 112000, 7, 7 }, /* 135 Mb */
- [50] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000,
- 122000, 7, 7 }, /* 150 Mb */
- [51] = { RC_INVALID, WLAN_RC_PHY_HT_40_DS, 27000,
- 25800, 8, 8 }, /* 27 Mb */
- [52] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 54000,
- 49800, 9, 9 }, /* 54 Mb */
- [53] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 81000,
- 71900, 10, 10 }, /* 81 Mb */
- [54] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 108000,
- 92500, 11, 11 }, /* 108 Mb */
- [55] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 162000,
- 130300, 12, 12 }, /* 162 Mb */
- [56] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 216000,
- 162800, 13, 13 }, /* 216 Mb */
- [57] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 243000,
- 178200, 14, 14 }, /* 243 Mb */
- [58] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 270000,
- 192100, 15, 15 }, /* 270 Mb */
- [59] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS_HGI, 300000,
- 207000, 15, 15 }, /* 300 Mb */
- [60] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 40500,
- 36100, 16, 16 }, /* 40.5 Mb */
- [61] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 81000,
- 72900, 17, 17 }, /* 81 Mb */
- [62] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 121500,
- 108300, 18, 18 }, /* 121.5 Mb */
- [63] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 162000,
- 142000, 19, 19 }, /* 162 Mb */
- [64] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 243000,
- 205100, 20, 20 }, /* 243 Mb */
- [65] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS_HGI, 270000,
- 224700, 20, 20 }, /* 270 Mb */
- [66] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 324000,
- 263100, 21, 21 }, /* 324 Mb */
- [67] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 360000,
- 288000, 21, 21 }, /* 360 Mb */
- [68] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 364500,
- 290700, 22, 22 }, /* 364.5 Mb */
- [69] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 405000,
- 317200, 22, 22 }, /* 405 Mb */
- [70] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 405000,
- 317200, 23, 23 }, /* 405 Mb */
- [71] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 450000,
- 346400, 23, 23 }, /* 450 Mb */
- },
- 50, /* probe interval */
- WLAN_RC_HT_FLAG, /* Phy rates allowed initially */
-};
-
-static const struct ath_rate_table ar5416_11a_ratetable = {
- 8,
- 0,
- {
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
- 5400, 0, 12},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
- 7800, 1, 18},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
- 10000, 2, 24},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
- 13900, 3, 36},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
- 17300, 4, 48},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
- 23000, 5, 72},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
- 27400, 6, 96},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
- 29300, 7, 108},
- },
- 50, /* probe interval */
- 0, /* Phy rates allowed initially */
-};
-
-static const struct ath_rate_table ar5416_11g_ratetable = {
- 12,
- 0,
- {
- { RC_L_SDT, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
- 900, 0, 2},
- { RC_L_SDT, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
- 1900, 1, 4},
- { RC_L_SDT, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
- 4900, 2, 11},
- { RC_L_SDT, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
- 8100, 3, 22},
- { RC_INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
- 5400, 4, 12},
- { RC_INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
- 7800, 5, 18},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
- 10000, 6, 24},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
- 13900, 7, 36},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
- 17300, 8, 48},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
- 23000, 9, 72},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
- 27400, 10, 96},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
- 29300, 11, 108},
- },
- 50, /* probe interval */
- 0, /* Phy rates allowed initially */
-};
-
-static int ath_rc_get_rateindex(struct ath_rate_priv *ath_rc_priv,
- struct ieee80211_tx_rate *rate)
-{
- const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
- int rix, i, idx = 0;
-
- if (!(rate->flags & IEEE80211_TX_RC_MCS))
- return rate->idx;
-
- for (i = 0; i < ath_rc_priv->max_valid_rate; i++) {
- idx = ath_rc_priv->valid_rate_index[i];
-
- if (WLAN_RC_PHY_HT(rate_table->info[idx].phy) &&
- rate_table->info[idx].ratecode == rate->idx)
- break;
- }
-
- rix = idx;
-
- if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
- rix++;
-
- return rix;
-}
-
-static void ath_rc_sort_validrates(struct ath_rate_priv *ath_rc_priv)
-{
- const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
- u8 i, j, idx, idx_next;
-
- for (i = ath_rc_priv->max_valid_rate - 1; i > 0; i--) {
- for (j = 0; j <= i-1; j++) {
- idx = ath_rc_priv->valid_rate_index[j];
- idx_next = ath_rc_priv->valid_rate_index[j+1];
-
- if (rate_table->info[idx].ratekbps >
- rate_table->info[idx_next].ratekbps) {
- ath_rc_priv->valid_rate_index[j] = idx_next;
- ath_rc_priv->valid_rate_index[j+1] = idx;
- }
- }
- }
-}
-
-static inline
-int ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table,
- struct ath_rate_priv *ath_rc_priv,
- u8 cur_valid_txrate,
- u8 *next_idx)
-{
- u8 i;
-
- for (i = 0; i < ath_rc_priv->max_valid_rate - 1; i++) {
- if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) {
- *next_idx = ath_rc_priv->valid_rate_index[i+1];
- return 1;
- }
- }
-
- /* No more valid rates */
- *next_idx = 0;
-
- return 0;
-}
-
-/* Return true only for single stream */
-
-static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw)
-{
- if (WLAN_RC_PHY_HT(phy) && !(capflag & WLAN_RC_HT_FLAG))
- return 0;
- if (WLAN_RC_PHY_DS(phy) && !(capflag & WLAN_RC_DS_FLAG))
- return 0;
- if (WLAN_RC_PHY_TS(phy) && !(capflag & WLAN_RC_TS_FLAG))
- return 0;
- if (WLAN_RC_PHY_SGI(phy) && !(capflag & WLAN_RC_SGI_FLAG))
- return 0;
- if (!ignore_cw && WLAN_RC_PHY_HT(phy))
- if (WLAN_RC_PHY_40(phy) && !(capflag & WLAN_RC_40_FLAG))
- return 0;
- return 1;
-}
-
-static inline int
-ath_rc_get_lower_rix(struct ath_rate_priv *ath_rc_priv,
- u8 cur_valid_txrate, u8 *next_idx)
-{
- int8_t i;
-
- for (i = 1; i < ath_rc_priv->max_valid_rate ; i++) {
- if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) {
- *next_idx = ath_rc_priv->valid_rate_index[i-1];
- return 1;
- }
- }
-
- return 0;
-}
-
-static u8 ath_rc_init_validrates(struct ath_rate_priv *ath_rc_priv)
-{
- const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
- u8 i, hi = 0;
-
- for (i = 0; i < rate_table->rate_cnt; i++) {
- if (rate_table->info[i].rate_flags & RC_LEGACY) {
- u32 phy = rate_table->info[i].phy;
- u8 valid_rate_count = 0;
-
- if (!ath_rc_valid_phyrate(phy, ath_rc_priv->ht_cap, 0))
- continue;
-
- valid_rate_count = ath_rc_priv->valid_phy_ratecnt[phy];
-
- ath_rc_priv->valid_phy_rateidx[phy][valid_rate_count] = i;
- ath_rc_priv->valid_phy_ratecnt[phy] += 1;
- ath_rc_priv->valid_rate_index[i] = true;
- hi = i;
- }
- }
-
- return hi;
-}
-
-static inline bool ath_rc_check_legacy(u8 rate, u8 dot11rate, u16 rate_flags,
- u32 phy, u32 capflag)
-{
- if (rate != dot11rate || WLAN_RC_PHY_HT(phy))
- return false;
-
- if ((rate_flags & WLAN_RC_CAP_MODE(capflag)) != WLAN_RC_CAP_MODE(capflag))
- return false;
-
- if (!(rate_flags & WLAN_RC_CAP_STREAM(capflag)))
- return false;
-
- return true;
-}
-
-static inline bool ath_rc_check_ht(u8 rate, u8 dot11rate, u16 rate_flags,
- u32 phy, u32 capflag)
-{
- if (rate != dot11rate || !WLAN_RC_PHY_HT(phy))
- return false;
-
- if (!WLAN_RC_PHY_HT_VALID(rate_flags, capflag))
- return false;
-
- if (!(rate_flags & WLAN_RC_CAP_STREAM(capflag)))
- return false;
-
- return true;
-}
-
-static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv, bool legacy)
-{
- const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
- struct ath_rateset *rateset;
- u32 phy, capflag = ath_rc_priv->ht_cap;
- u16 rate_flags;
- u8 i, j, hi = 0, rate, dot11rate, valid_rate_count;
-
- if (legacy)
- rateset = &ath_rc_priv->neg_rates;
- else
- rateset = &ath_rc_priv->neg_ht_rates;
-
- for (i = 0; i < rateset->rs_nrates; i++) {
- for (j = 0; j < rate_table->rate_cnt; j++) {
- phy = rate_table->info[j].phy;
- rate_flags = rate_table->info[j].rate_flags;
- rate = rateset->rs_rates[i];
- dot11rate = rate_table->info[j].dot11rate;
-
- if (legacy &&
- !ath_rc_check_legacy(rate, dot11rate,
- rate_flags, phy, capflag))
- continue;
-
- if (!legacy &&
- !ath_rc_check_ht(rate, dot11rate,
- rate_flags, phy, capflag))
- continue;
-
- if (!ath_rc_valid_phyrate(phy, capflag, 0))
- continue;
-
- valid_rate_count = ath_rc_priv->valid_phy_ratecnt[phy];
- ath_rc_priv->valid_phy_rateidx[phy][valid_rate_count] = j;
- ath_rc_priv->valid_phy_ratecnt[phy] += 1;
- ath_rc_priv->valid_rate_index[j] = true;
- hi = max(hi, j);
- }
- }
-
- return hi;
-}
-
-static u8 ath_rc_get_highest_rix(struct ath_rate_priv *ath_rc_priv,
- int *is_probing)
-{
- const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
- u32 best_thruput, this_thruput, now_msec;
- u8 rate, next_rate, best_rate, maxindex, minindex;
- int8_t index = 0;
-
- now_msec = jiffies_to_msecs(jiffies);
- *is_probing = 0;
- best_thruput = 0;
- maxindex = ath_rc_priv->max_valid_rate-1;
- minindex = 0;
- best_rate = minindex;
-
- /*
- * Try the higher rate first. It will reduce memory moving time
- * if we have very good channel characteristics.
- */
- for (index = maxindex; index >= minindex ; index--) {
- u8 per_thres;
-
- rate = ath_rc_priv->valid_rate_index[index];
- if (rate > ath_rc_priv->rate_max_phy)
- continue;
-
- /*
- * For TCP the average collision rate is around 11%,
- * so we ignore PERs less than this. This is to
- * prevent the rate we are currently using (whose
- * PER might be in the 10-15 range because of TCP
- * collisions) looking worse than the next lower
- * rate whose PER has decayed close to 0. If we
- * used to next lower rate, its PER would grow to
- * 10-15 and we would be worse off then staying
- * at the current rate.
- */
- per_thres = ath_rc_priv->per[rate];
- if (per_thres < 12)
- per_thres = 12;
-
- this_thruput = rate_table->info[rate].user_ratekbps *
- (100 - per_thres);
-
- if (best_thruput <= this_thruput) {
- best_thruput = this_thruput;
- best_rate = rate;
- }
- }
-
- rate = best_rate;
-
- /*
- * Must check the actual rate (ratekbps) to account for
- * non-monoticity of 11g's rate table
- */
-
- if (rate >= ath_rc_priv->rate_max_phy) {
- rate = ath_rc_priv->rate_max_phy;
-
- /* Probe the next allowed phy state */
- if (ath_rc_get_nextvalid_txrate(rate_table,
- ath_rc_priv, rate, &next_rate) &&
- (now_msec - ath_rc_priv->probe_time >
- rate_table->probe_interval) &&
- (ath_rc_priv->hw_maxretry_pktcnt >= 1)) {
- rate = next_rate;
- ath_rc_priv->probe_rate = rate;
- ath_rc_priv->probe_time = now_msec;
- ath_rc_priv->hw_maxretry_pktcnt = 0;
- *is_probing = 1;
- }
- }
-
- if (rate > (ath_rc_priv->rate_table_size - 1))
- rate = ath_rc_priv->rate_table_size - 1;
-
- if (RC_TS_ONLY(rate_table->info[rate].rate_flags) &&
- (ath_rc_priv->ht_cap & WLAN_RC_TS_FLAG))
- return rate;
-
- if (RC_DS_OR_LATER(rate_table->info[rate].rate_flags) &&
- (ath_rc_priv->ht_cap & (WLAN_RC_DS_FLAG | WLAN_RC_TS_FLAG)))
- return rate;
-
- if (RC_SS_OR_LEGACY(rate_table->info[rate].rate_flags))
- return rate;
-
- /* This should not happen */
- WARN_ON_ONCE(1);
-
- rate = ath_rc_priv->valid_rate_index[0];
-
- return rate;
-}
-
-static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table,
- struct ieee80211_tx_rate *rate,
- struct ieee80211_tx_rate_control *txrc,
- u8 tries, u8 rix, int rtsctsenable)
-{
- rate->count = tries;
- rate->idx = rate_table->info[rix].ratecode;
-
- if (txrc->rts || rtsctsenable)
- rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS;
-
- if (WLAN_RC_PHY_HT(rate_table->info[rix].phy)) {
- rate->flags |= IEEE80211_TX_RC_MCS;
- if (WLAN_RC_PHY_40(rate_table->info[rix].phy) &&
- conf_is_ht40(&txrc->hw->conf))
- rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
- if (WLAN_RC_PHY_SGI(rate_table->info[rix].phy))
- rate->flags |= IEEE80211_TX_RC_SHORT_GI;
- }
-}
-
-static void ath_rc_rate_set_rtscts(struct ath_softc *sc,
- const struct ath_rate_table *rate_table,
- struct ieee80211_tx_info *tx_info)
-{
- struct ieee80211_bss_conf *bss_conf;
-
- if (!tx_info->control.vif)
- return;
- /*
- * For legacy frames, mac80211 takes care of CTS protection.
- */
- if (!(tx_info->control.rates[0].flags & IEEE80211_TX_RC_MCS))
- return;
-
- bss_conf = &tx_info->control.vif->bss_conf;
-
- if (!bss_conf->basic_rates)
- return;
-
- /*
- * For now, use the lowest allowed basic rate for HT frames.
- */
- tx_info->control.rts_cts_rate_idx = __ffs(bss_conf->basic_rates);
-}
-
-static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
- struct ieee80211_tx_rate_control *txrc)
-{
- struct ath_softc *sc = priv;
- struct ath_rate_priv *ath_rc_priv = priv_sta;
- const struct ath_rate_table *rate_table;
- struct sk_buff *skb = txrc->skb;
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- struct ieee80211_tx_rate *rates = tx_info->control.rates;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- __le16 fc = hdr->frame_control;
- u8 try_per_rate, i = 0, rix;
- int is_probe = 0;
-
- if (rate_control_send_low(sta, priv_sta, txrc))
- return;
-
- /*
- * For Multi Rate Retry we use a different number of
- * retry attempt counts. This ends up looking like this:
- *
- * MRR[0] = 4
- * MRR[1] = 4
- * MRR[2] = 4
- * MRR[3] = 8
- *
- */
- try_per_rate = 4;
-
- rate_table = ath_rc_priv->rate_table;
- rix = ath_rc_get_highest_rix(ath_rc_priv, &is_probe);
-
- if (conf_is_ht(&sc->hw->conf) &&
- (sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
- tx_info->flags |= IEEE80211_TX_CTL_LDPC;
-
- if (conf_is_ht(&sc->hw->conf) &&
- (sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
- tx_info->flags |= (1 << IEEE80211_TX_CTL_STBC_SHIFT);
-
- if (is_probe) {
- /*
- * Set one try for probe rates. For the
- * probes don't enable RTS.
- */
- ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
- 1, rix, 0);
- /*
- * Get the next tried/allowed rate.
- * No RTS for the next series after the probe rate.
- */
- ath_rc_get_lower_rix(ath_rc_priv, rix, &rix);
- ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
- try_per_rate, rix, 0);
-
- tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- } else {
- /*
- * Set the chosen rate. No RTS for first series entry.
- */
- ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
- try_per_rate, rix, 0);
- }
-
- for ( ; i < 4; i++) {
- /*
- * Use twice the number of tries for the last MRR segment.
- */
- if (i + 1 == 4)
- try_per_rate = 8;
-
- ath_rc_get_lower_rix(ath_rc_priv, rix, &rix);
-
- /*
- * All other rates in the series have RTS enabled.
- */
- ath_rc_rate_set_series(rate_table, &rates[i], txrc,
- try_per_rate, rix, 1);
- }
-
- /*
- * NB:Change rate series to enable aggregation when operating
- * at lower MCS rates. When first rate in series is MCS2
- * in HT40 @ 2.4GHz, series should look like:
- *
- * {MCS2, MCS1, MCS0, MCS0}.
- *
- * When first rate in series is MCS3 in HT20 @ 2.4GHz, series should
- * look like:
- *
- * {MCS3, MCS2, MCS1, MCS1}
- *
- * So, set fourth rate in series to be same as third one for
- * above conditions.
- */
- if ((sc->hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) &&
- (conf_is_ht(&sc->hw->conf))) {
- u8 dot11rate = rate_table->info[rix].dot11rate;
- u8 phy = rate_table->info[rix].phy;
- if (i == 4 &&
- ((dot11rate == 2 && phy == WLAN_RC_PHY_HT_40_SS) ||
- (dot11rate == 3 && phy == WLAN_RC_PHY_HT_20_SS))) {
- rates[3].idx = rates[2].idx;
- rates[3].flags = rates[2].flags;
- }
- }
-
- /*
- * Force hardware to use computed duration for next
- * fragment by disabling multi-rate retry, which
- * updates duration based on the multi-rate duration table.
- *
- * FIXME: Fix duration
- */
- if (ieee80211_has_morefrags(fc) ||
- (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) {
- rates[1].count = rates[2].count = rates[3].count = 0;
- rates[1].idx = rates[2].idx = rates[3].idx = 0;
- rates[0].count = ATH_TXMAXTRY;
- }
-
- ath_rc_rate_set_rtscts(sc, rate_table, tx_info);
-}
-
-static void ath_rc_update_per(struct ath_softc *sc,
- const struct ath_rate_table *rate_table,
- struct ath_rate_priv *ath_rc_priv,
- struct ieee80211_tx_info *tx_info,
- int tx_rate, int xretries, int retries,
- u32 now_msec)
-{
- int count, n_bad_frames;
- u8 last_per;
- static const u32 nretry_to_per_lookup[10] = {
- 100 * 0 / 1,
- 100 * 1 / 4,
- 100 * 1 / 2,
- 100 * 3 / 4,
- 100 * 4 / 5,
- 100 * 5 / 6,
- 100 * 6 / 7,
- 100 * 7 / 8,
- 100 * 8 / 9,
- 100 * 9 / 10
- };
-
- last_per = ath_rc_priv->per[tx_rate];
- n_bad_frames = tx_info->status.ampdu_len - tx_info->status.ampdu_ack_len;
-
- if (xretries) {
- if (xretries == 1) {
- ath_rc_priv->per[tx_rate] += 30;
- if (ath_rc_priv->per[tx_rate] > 100)
- ath_rc_priv->per[tx_rate] = 100;
- } else {
- /* xretries == 2 */
- count = ARRAY_SIZE(nretry_to_per_lookup);
- if (retries >= count)
- retries = count - 1;
-
- /* new_PER = 7/8*old_PER + 1/8*(currentPER) */
- ath_rc_priv->per[tx_rate] =
- (u8)(last_per - (last_per >> 3) + (100 >> 3));
- }
-
- /* xretries == 1 or 2 */
-
- if (ath_rc_priv->probe_rate == tx_rate)
- ath_rc_priv->probe_rate = 0;
-
- } else { /* xretries == 0 */
- count = ARRAY_SIZE(nretry_to_per_lookup);
- if (retries >= count)
- retries = count - 1;
-
- if (n_bad_frames) {
- /* new_PER = 7/8*old_PER + 1/8*(currentPER)
- * Assuming that n_frames is not 0. The current PER
- * from the retries is 100 * retries / (retries+1),
- * since the first retries attempts failed, and the
- * next one worked. For the one that worked,
- * n_bad_frames subframes out of n_frames wored,
- * so the PER for that part is
- * 100 * n_bad_frames / n_frames, and it contributes
- * 100 * n_bad_frames / (n_frames * (retries+1)) to
- * the above PER. The expression below is a
- * simplified version of the sum of these two terms.
- */
- if (tx_info->status.ampdu_len > 0) {
- int n_frames, n_bad_tries;
- u8 cur_per, new_per;
-
- n_bad_tries = retries * tx_info->status.ampdu_len +
- n_bad_frames;
- n_frames = tx_info->status.ampdu_len * (retries + 1);
- cur_per = (100 * n_bad_tries / n_frames) >> 3;
- new_per = (u8)(last_per - (last_per >> 3) + cur_per);
- ath_rc_priv->per[tx_rate] = new_per;
- }
- } else {
- ath_rc_priv->per[tx_rate] =
- (u8)(last_per - (last_per >> 3) +
- (nretry_to_per_lookup[retries] >> 3));
- }
-
-
- /*
- * If we got at most one retry then increase the max rate if
- * this was a probe. Otherwise, ignore the probe.
- */
- if (ath_rc_priv->probe_rate && ath_rc_priv->probe_rate == tx_rate) {
- if (retries > 0 || 2 * n_bad_frames > tx_info->status.ampdu_len) {
- /*
- * Since we probed with just a single attempt,
- * any retries means the probe failed. Also,
- * if the attempt worked, but more than half
- * the subframes were bad then also consider
- * the probe a failure.
- */
- ath_rc_priv->probe_rate = 0;
- } else {
- u8 probe_rate = 0;
-
- ath_rc_priv->rate_max_phy =
- ath_rc_priv->probe_rate;
- probe_rate = ath_rc_priv->probe_rate;
-
- if (ath_rc_priv->per[probe_rate] > 30)
- ath_rc_priv->per[probe_rate] = 20;
-
- ath_rc_priv->probe_rate = 0;
-
- /*
- * Since this probe succeeded, we allow the next
- * probe twice as soon. This allows the maxRate
- * to move up faster if the probes are
- * successful.
- */
- ath_rc_priv->probe_time =
- now_msec - rate_table->probe_interval / 2;
- }
- }
-
- if (retries > 0) {
- /*
- * Don't update anything. We don't know if
- * this was because of collisions or poor signal.
- */
- ath_rc_priv->hw_maxretry_pktcnt = 0;
- } else {
- /*
- * It worked with no retries. First ignore bogus (small)
- * rssi_ack values.
- */
- if (tx_rate == ath_rc_priv->rate_max_phy &&
- ath_rc_priv->hw_maxretry_pktcnt < 255) {
- ath_rc_priv->hw_maxretry_pktcnt++;
- }
-
- }
- }
-}
-
-static void ath_rc_update_ht(struct ath_softc *sc,
- struct ath_rate_priv *ath_rc_priv,
- struct ieee80211_tx_info *tx_info,
- int tx_rate, int xretries, int retries)
-{
- u32 now_msec = jiffies_to_msecs(jiffies);
- int rate;
- u8 last_per;
- const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
- int size = ath_rc_priv->rate_table_size;
-
- if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt))
- return;
-
- last_per = ath_rc_priv->per[tx_rate];
-
- /* Update PER first */
- ath_rc_update_per(sc, rate_table, ath_rc_priv,
- tx_info, tx_rate, xretries,
- retries, now_msec);
-
- /*
- * If this rate looks bad (high PER) then stop using it for
- * a while (except if we are probing).
- */
- if (ath_rc_priv->per[tx_rate] >= 55 && tx_rate > 0 &&
- rate_table->info[tx_rate].ratekbps <=
- rate_table->info[ath_rc_priv->rate_max_phy].ratekbps) {
- ath_rc_get_lower_rix(ath_rc_priv, (u8)tx_rate,
- &ath_rc_priv->rate_max_phy);
-
- /* Don't probe for a little while. */
- ath_rc_priv->probe_time = now_msec;
- }
-
- /* Make sure the rates below this have lower PER */
- /* Monotonicity is kept only for rates below the current rate. */
- if (ath_rc_priv->per[tx_rate] < last_per) {
- for (rate = tx_rate - 1; rate >= 0; rate--) {
-
- if (ath_rc_priv->per[rate] >
- ath_rc_priv->per[rate+1]) {
- ath_rc_priv->per[rate] =
- ath_rc_priv->per[rate+1];
- }
- }
- }
-
- /* Maintain monotonicity for rates above the current rate */
- for (rate = tx_rate; rate < size - 1; rate++) {
- if (ath_rc_priv->per[rate+1] <
- ath_rc_priv->per[rate])
- ath_rc_priv->per[rate+1] =
- ath_rc_priv->per[rate];
- }
-
- /* Every so often, we reduce the thresholds
- * and PER (different for CCK and OFDM). */
- if (now_msec - ath_rc_priv->per_down_time >=
- rate_table->probe_interval) {
- for (rate = 0; rate < size; rate++) {
- ath_rc_priv->per[rate] =
- 7 * ath_rc_priv->per[rate] / 8;
- }
-
- ath_rc_priv->per_down_time = now_msec;
- }
-
- ath_debug_stat_retries(ath_rc_priv, tx_rate, xretries, retries,
- ath_rc_priv->per[tx_rate]);
-
-}
-
-static void ath_rc_tx_status(struct ath_softc *sc,
- struct ath_rate_priv *ath_rc_priv,
- struct sk_buff *skb)
-{
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- struct ieee80211_tx_rate *rates = tx_info->status.rates;
- struct ieee80211_tx_rate *rate;
- int final_ts_idx = 0, xretries = 0, long_retry = 0;
- u8 flags;
- u32 i = 0, rix;
-
- for (i = 0; i < sc->hw->max_rates; i++) {
- rate = &tx_info->status.rates[i];
- if (rate->idx < 0 || !rate->count)
- break;
-
- final_ts_idx = i;
- long_retry = rate->count - 1;
- }
-
- if (!(tx_info->flags & IEEE80211_TX_STAT_ACK))
- xretries = 1;
-
- /*
- * If the first rate is not the final index, there
- * are intermediate rate failures to be processed.
- */
- if (final_ts_idx != 0) {
- for (i = 0; i < final_ts_idx ; i++) {
- if (rates[i].count != 0 && (rates[i].idx >= 0)) {
- flags = rates[i].flags;
-
- /* If HT40 and we have switched mode from
- * 40 to 20 => don't update */
-
- if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
- !(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG))
- return;
-
- rix = ath_rc_get_rateindex(ath_rc_priv, &rates[i]);
- ath_rc_update_ht(sc, ath_rc_priv, tx_info,
- rix, xretries ? 1 : 2,
- rates[i].count);
- }
- }
- }
-
- flags = rates[final_ts_idx].flags;
-
- /* If HT40 and we have switched mode from 40 to 20 => don't update */
- if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
- !(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG))
- return;
-
- rix = ath_rc_get_rateindex(ath_rc_priv, &rates[final_ts_idx]);
- ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry);
- ath_debug_stat_rc(ath_rc_priv, rix);
-}
-
-static const
-struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc,
- enum ieee80211_band band,
- bool is_ht)
-{
- switch(band) {
- case IEEE80211_BAND_2GHZ:
- if (is_ht)
- return &ar5416_11ng_ratetable;
- return &ar5416_11g_ratetable;
- case IEEE80211_BAND_5GHZ:
- if (is_ht)
- return &ar5416_11na_ratetable;
- return &ar5416_11a_ratetable;
- default:
- return NULL;
- }
-}
-
-static void ath_rc_init(struct ath_softc *sc,
- struct ath_rate_priv *ath_rc_priv)
-{
- const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
- struct ath_rateset *rateset = &ath_rc_priv->neg_rates;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- u8 i, j, k, hi = 0, hthi = 0;
-
- ath_rc_priv->rate_table_size = RATE_TABLE_SIZE;
-
- for (i = 0 ; i < ath_rc_priv->rate_table_size; i++) {
- ath_rc_priv->per[i] = 0;
- ath_rc_priv->valid_rate_index[i] = 0;
- }
-
- for (i = 0; i < WLAN_RC_PHY_MAX; i++) {
- for (j = 0; j < RATE_TABLE_SIZE; j++)
- ath_rc_priv->valid_phy_rateidx[i][j] = 0;
- ath_rc_priv->valid_phy_ratecnt[i] = 0;
- }
-
- if (!rateset->rs_nrates) {
- hi = ath_rc_init_validrates(ath_rc_priv);
- } else {
- hi = ath_rc_setvalid_rates(ath_rc_priv, true);
-
- if (ath_rc_priv->ht_cap & WLAN_RC_HT_FLAG)
- hthi = ath_rc_setvalid_rates(ath_rc_priv, false);
-
- hi = max(hi, hthi);
- }
-
- ath_rc_priv->rate_table_size = hi + 1;
- ath_rc_priv->rate_max_phy = 0;
- WARN_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE);
-
- for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) {
- for (j = 0; j < ath_rc_priv->valid_phy_ratecnt[i]; j++) {
- ath_rc_priv->valid_rate_index[k++] =
- ath_rc_priv->valid_phy_rateidx[i][j];
- }
-
- if (!ath_rc_valid_phyrate(i, rate_table->initial_ratemax, 1) ||
- !ath_rc_priv->valid_phy_ratecnt[i])
- continue;
-
- ath_rc_priv->rate_max_phy = ath_rc_priv->valid_phy_rateidx[i][j-1];
- }
- WARN_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE);
- WARN_ON(k > RATE_TABLE_SIZE);
-
- ath_rc_priv->max_valid_rate = k;
- ath_rc_sort_validrates(ath_rc_priv);
- ath_rc_priv->rate_max_phy = (k > 4) ?
- ath_rc_priv->valid_rate_index[k-4] :
- ath_rc_priv->valid_rate_index[k-1];
-
- ath_dbg(common, CONFIG, "RC Initialized with capabilities: 0x%x\n",
- ath_rc_priv->ht_cap);
-}
-
-static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta)
-{
- u8 caps = 0;
-
- if (sta->ht_cap.ht_supported) {
- caps = WLAN_RC_HT_FLAG;
- if (sta->ht_cap.mcs.rx_mask[1] && sta->ht_cap.mcs.rx_mask[2])
- caps |= WLAN_RC_TS_FLAG | WLAN_RC_DS_FLAG;
- else if (sta->ht_cap.mcs.rx_mask[1])
- caps |= WLAN_RC_DS_FLAG;
- if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
- caps |= WLAN_RC_40_FLAG;
- if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
- caps |= WLAN_RC_SGI_FLAG;
- } else {
- if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
- caps |= WLAN_RC_SGI_FLAG;
- }
- }
-
- return caps;
-}
-
-static bool ath_tx_aggr_check(struct ath_softc *sc, struct ieee80211_sta *sta,
- u8 tidno)
-{
- struct ath_node *an = (struct ath_node *)sta->drv_priv;
- struct ath_atx_tid *txtid;
-
- if (!sta->ht_cap.ht_supported)
- return false;
-
- txtid = ATH_AN_2_TID(an, tidno);
- return !txtid->active;
-}
-
-
-/***********************************/
-/* mac80211 Rate Control callbacks */
-/***********************************/
-
-static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb)
-{
- struct ath_softc *sc = priv;
- struct ath_rate_priv *ath_rc_priv = priv_sta;
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- __le16 fc = hdr->frame_control;
-
- if (!priv_sta || !ieee80211_is_data(fc))
- return;
-
- /* This packet was aggregated but doesn't carry status info */
- if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
- !(tx_info->flags & IEEE80211_TX_STAT_AMPDU))
- return;
-
- if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED)
- return;
-
- ath_rc_tx_status(sc, ath_rc_priv, skb);
-
- /* Check if aggregation has to be enabled for this tid */
- if (conf_is_ht(&sc->hw->conf) &&
- !(skb->protocol == cpu_to_be16(ETH_P_PAE))) {
- if (ieee80211_is_data_qos(fc) &&
- skb_get_queue_mapping(skb) != IEEE80211_AC_VO) {
- u8 *qc, tid;
-
- qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & 0xf;
-
- if(ath_tx_aggr_check(sc, sta, tid))
- ieee80211_start_tx_ba_session(sta, tid, 0);
- }
- }
-}
-
-static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
- struct cfg80211_chan_def *chandef,
- struct ieee80211_sta *sta, void *priv_sta)
-{
- struct ath_softc *sc = priv;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- struct ath_rate_priv *ath_rc_priv = priv_sta;
- int i, j = 0;
- u32 rate_flags = ieee80211_chandef_rate_flags(&sc->hw->conf.chandef);
-
- for (i = 0; i < sband->n_bitrates; i++) {
- if (sta->supp_rates[sband->band] & BIT(i)) {
- if ((rate_flags & sband->bitrates[i].flags)
- != rate_flags)
- continue;
-
- ath_rc_priv->neg_rates.rs_rates[j]
- = (sband->bitrates[i].bitrate * 2) / 10;
- j++;
- }
- }
- ath_rc_priv->neg_rates.rs_nrates = j;
-
- if (sta->ht_cap.ht_supported) {
- for (i = 0, j = 0; i < 77; i++) {
- if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
- ath_rc_priv->neg_ht_rates.rs_rates[j++] = i;
- if (j == ATH_RATE_MAX)
- break;
- }
- ath_rc_priv->neg_ht_rates.rs_nrates = j;
- }
-
- ath_rc_priv->rate_table = ath_choose_rate_table(sc, sband->band,
- sta->ht_cap.ht_supported);
- if (!ath_rc_priv->rate_table) {
- ath_err(common, "No rate table chosen\n");
- return;
- }
-
- ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta);
- ath_rc_init(sc, priv_sta);
-}
-
-static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
- struct cfg80211_chan_def *chandef,
- struct ieee80211_sta *sta, void *priv_sta,
- u32 changed)
-{
- struct ath_softc *sc = priv;
- struct ath_rate_priv *ath_rc_priv = priv_sta;
-
- if (changed & IEEE80211_RC_BW_CHANGED) {
- ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta);
- ath_rc_init(sc, priv_sta);
-
- ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG,
- "Operating Bandwidth changed to: %d\n",
- sc->hw->conf.chandef.width);
- }
-}
-
-#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
-
-void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate)
-{
- struct ath_rc_stats *stats;
-
- stats = &rc->rcstats[final_rate];
- stats->success++;
-}
-
-void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix,
- int xretries, int retries, u8 per)
-{
- struct ath_rc_stats *stats = &rc->rcstats[rix];
-
- stats->xretries += xretries;
- stats->retries += retries;
- stats->per = per;
-}
-
-static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ath_rate_priv *rc = file->private_data;
- char *buf;
- unsigned int len = 0, max;
- int rix;
- ssize_t retval;
-
- if (rc->rate_table == NULL)
- return 0;
-
- max = 80 + rc->rate_table_size * 1024 + 1;
- buf = kmalloc(max, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
- len += sprintf(buf, "%6s %6s %6s "
- "%10s %10s %10s %10s\n",
- "HT", "MCS", "Rate",
- "Success", "Retries", "XRetries", "PER");
-
- for (rix = 0; rix < rc->max_valid_rate; rix++) {
- u8 i = rc->valid_rate_index[rix];
- u32 ratekbps = rc->rate_table->info[i].ratekbps;
- struct ath_rc_stats *stats = &rc->rcstats[i];
- char mcs[5];
- char htmode[5];
- int used_mcs = 0, used_htmode = 0;
-
- if (WLAN_RC_PHY_HT(rc->rate_table->info[i].phy)) {
- used_mcs = scnprintf(mcs, 5, "%d",
- rc->rate_table->info[i].ratecode);
-
- if (WLAN_RC_PHY_40(rc->rate_table->info[i].phy))
- used_htmode = scnprintf(htmode, 5, "HT40");
- else if (WLAN_RC_PHY_20(rc->rate_table->info[i].phy))
- used_htmode = scnprintf(htmode, 5, "HT20");
- else
- used_htmode = scnprintf(htmode, 5, "????");
- }
-
- mcs[used_mcs] = '\0';
- htmode[used_htmode] = '\0';
-
- len += scnprintf(buf + len, max - len,
- "%6s %6s %3u.%d: "
- "%10u %10u %10u %10u\n",
- htmode,
- mcs,
- ratekbps / 1000,
- (ratekbps % 1000) / 100,
- stats->success,
- stats->retries,
- stats->xretries,
- stats->per);
- }
-
- if (len > max)
- len = max;
-
- retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
- kfree(buf);
- return retval;
-}
-
-static const struct file_operations fops_rcstat = {
- .read = read_file_rcstat,
- .open = simple_open,
- .owner = THIS_MODULE
-};
-
-static void ath_rate_add_sta_debugfs(void *priv, void *priv_sta,
- struct dentry *dir)
-{
- struct ath_rate_priv *rc = priv_sta;
- rc->debugfs_rcstats = debugfs_create_file("rc_stats", S_IRUGO,
- dir, rc, &fops_rcstat);
-}
-
-static void ath_rate_remove_sta_debugfs(void *priv, void *priv_sta)
-{
- struct ath_rate_priv *rc = priv_sta;
- debugfs_remove(rc->debugfs_rcstats);
-}
-
-#endif /* CONFIG_MAC80211_DEBUGFS && CONFIG_ATH9K_DEBUGFS */
-
-static void *ath_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
-{
- return hw->priv;
-}
-
-static void ath_rate_free(void *priv)
-{
- return;
-}
-
-static void *ath_rate_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)
-{
- return kzalloc(sizeof(struct ath_rate_priv), gfp);
-}
-
-static void ath_rate_free_sta(void *priv, struct ieee80211_sta *sta,
- void *priv_sta)
-{
- struct ath_rate_priv *rate_priv = priv_sta;
- kfree(rate_priv);
-}
-
-static struct rate_control_ops ath_rate_ops = {
- .module = NULL,
- .name = "ath9k_rate_control",
- .tx_status = ath_tx_status,
- .get_rate = ath_get_rate,
- .rate_init = ath_rate_init,
- .rate_update = ath_rate_update,
- .alloc = ath_rate_alloc,
- .free = ath_rate_free,
- .alloc_sta = ath_rate_alloc_sta,
- .free_sta = ath_rate_free_sta,
-
-#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
- .add_sta_debugfs = ath_rate_add_sta_debugfs,
- .remove_sta_debugfs = ath_rate_remove_sta_debugfs,
-#endif
-};
-
-int ath_rate_control_register(void)
-{
- return ieee80211_rate_control_register(&ath_rate_ops);
-}
-
-void ath_rate_control_unregister(void)
-{
- ieee80211_rate_control_unregister(&ath_rate_ops);
-}
diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h
deleted file mode 100644
index b9a87383cb43..000000000000
--- a/drivers/net/wireless/ath/ath9k/rc.h
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (c) 2004 Sam Leffler, Errno Consulting
- * Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2008-2011 Atheros Communications 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.
- */
-
-#ifndef RC_H
-#define RC_H
-
-#include "hw.h"
-
-struct ath_softc;
-
-#define ATH_RATE_MAX 30
-#define RATE_TABLE_SIZE 72
-
-#define RC_INVALID 0x0000
-#define RC_LEGACY 0x0001
-#define RC_SS 0x0002
-#define RC_DS 0x0004
-#define RC_TS 0x0008
-#define RC_HT_20 0x0010
-#define RC_HT_40 0x0020
-
-#define RC_STREAM_MASK 0xe
-#define RC_DS_OR_LATER(f) ((((f) & RC_STREAM_MASK) == RC_DS) || \
- (((f) & RC_STREAM_MASK) == (RC_DS | RC_TS)))
-#define RC_TS_ONLY(f) (((f) & RC_STREAM_MASK) == RC_TS)
-#define RC_SS_OR_LEGACY(f) ((f) & (RC_SS | RC_LEGACY))
-
-#define RC_HT_2040 (RC_HT_20 | RC_HT_40)
-#define RC_ALL_STREAM (RC_SS | RC_DS | RC_TS)
-#define RC_L_SD (RC_LEGACY | RC_SS | RC_DS)
-#define RC_L_SDT (RC_LEGACY | RC_SS | RC_DS | RC_TS)
-#define RC_HT_S_20 (RC_HT_20 | RC_SS)
-#define RC_HT_D_20 (RC_HT_20 | RC_DS)
-#define RC_HT_T_20 (RC_HT_20 | RC_TS)
-#define RC_HT_S_40 (RC_HT_40 | RC_SS)
-#define RC_HT_D_40 (RC_HT_40 | RC_DS)
-#define RC_HT_T_40 (RC_HT_40 | RC_TS)
-
-#define RC_HT_SD_20 (RC_HT_20 | RC_SS | RC_DS)
-#define RC_HT_DT_20 (RC_HT_20 | RC_DS | RC_TS)
-#define RC_HT_SD_40 (RC_HT_40 | RC_SS | RC_DS)
-#define RC_HT_DT_40 (RC_HT_40 | RC_DS | RC_TS)
-
-#define RC_HT_SD_2040 (RC_HT_2040 | RC_SS | RC_DS)
-#define RC_HT_SDT_2040 (RC_HT_2040 | RC_SS | RC_DS | RC_TS)
-
-#define RC_HT_SDT_20 (RC_HT_20 | RC_SS | RC_DS | RC_TS)
-#define RC_HT_SDT_40 (RC_HT_40 | RC_SS | RC_DS | RC_TS)
-
-#define RC_ALL (RC_LEGACY | RC_HT_2040 | RC_ALL_STREAM)
-
-enum {
- WLAN_RC_PHY_OFDM,
- WLAN_RC_PHY_CCK,
- WLAN_RC_PHY_HT_20_SS,
- WLAN_RC_PHY_HT_20_DS,
- WLAN_RC_PHY_HT_20_TS,
- WLAN_RC_PHY_HT_40_SS,
- WLAN_RC_PHY_HT_40_DS,
- WLAN_RC_PHY_HT_40_TS,
- WLAN_RC_PHY_HT_20_SS_HGI,
- WLAN_RC_PHY_HT_20_DS_HGI,
- WLAN_RC_PHY_HT_20_TS_HGI,
- WLAN_RC_PHY_HT_40_SS_HGI,
- WLAN_RC_PHY_HT_40_DS_HGI,
- WLAN_RC_PHY_HT_40_TS_HGI,
- WLAN_RC_PHY_MAX
-};
-
-#define WLAN_RC_PHY_DS(_phy) ((_phy == WLAN_RC_PHY_HT_20_DS) \
- || (_phy == WLAN_RC_PHY_HT_40_DS) \
- || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \
- || (_phy == WLAN_RC_PHY_HT_40_DS_HGI))
-#define WLAN_RC_PHY_TS(_phy) ((_phy == WLAN_RC_PHY_HT_20_TS) \
- || (_phy == WLAN_RC_PHY_HT_40_TS) \
- || (_phy == WLAN_RC_PHY_HT_20_TS_HGI) \
- || (_phy == WLAN_RC_PHY_HT_40_TS_HGI))
-#define WLAN_RC_PHY_20(_phy) ((_phy == WLAN_RC_PHY_HT_20_SS) \
- || (_phy == WLAN_RC_PHY_HT_20_DS) \
- || (_phy == WLAN_RC_PHY_HT_20_TS) \
- || (_phy == WLAN_RC_PHY_HT_20_SS_HGI) \
- || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \
- || (_phy == WLAN_RC_PHY_HT_20_TS_HGI))
-#define WLAN_RC_PHY_40(_phy) ((_phy == WLAN_RC_PHY_HT_40_SS) \
- || (_phy == WLAN_RC_PHY_HT_40_DS) \
- || (_phy == WLAN_RC_PHY_HT_40_TS) \
- || (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \
- || (_phy == WLAN_RC_PHY_HT_40_DS_HGI) \
- || (_phy == WLAN_RC_PHY_HT_40_TS_HGI))
-#define WLAN_RC_PHY_SGI(_phy) ((_phy == WLAN_RC_PHY_HT_20_SS_HGI) \
- || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \
- || (_phy == WLAN_RC_PHY_HT_20_TS_HGI) \
- || (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \
- || (_phy == WLAN_RC_PHY_HT_40_DS_HGI) \
- || (_phy == WLAN_RC_PHY_HT_40_TS_HGI))
-
-#define WLAN_RC_PHY_HT(_phy) (_phy >= WLAN_RC_PHY_HT_20_SS)
-
-#define WLAN_RC_CAP_MODE(capflag) (((capflag & WLAN_RC_HT_FLAG) ? \
- ((capflag & WLAN_RC_40_FLAG) ? RC_HT_40 : RC_HT_20) : RC_LEGACY))
-
-#define WLAN_RC_CAP_STREAM(capflag) (((capflag & WLAN_RC_TS_FLAG) ? \
- (RC_TS) : ((capflag & WLAN_RC_DS_FLAG) ? RC_DS : RC_SS)))
-
-/* Return TRUE if flag supports HT20 && client supports HT20 or
- * return TRUE if flag supports HT40 && client supports HT40.
- * This is used becos some rates overlap between HT20/HT40.
- */
-#define WLAN_RC_PHY_HT_VALID(flag, capflag) \
- (((flag & RC_HT_20) && !(capflag & WLAN_RC_40_FLAG)) || \
- ((flag & RC_HT_40) && (capflag & WLAN_RC_40_FLAG)))
-
-#define WLAN_RC_DS_FLAG (0x01)
-#define WLAN_RC_TS_FLAG (0x02)
-#define WLAN_RC_40_FLAG (0x04)
-#define WLAN_RC_SGI_FLAG (0x08)
-#define WLAN_RC_HT_FLAG (0x10)
-
-/**
- * struct ath_rate_table - Rate Control table
- * @rate_cnt: total number of rates for the given wireless mode
- * @mcs_start: MCS rate index offset
- * @rate_flags: Rate Control flags
- * @phy: CCK/OFDM/HT20/HT40
- * @ratekbps: rate in Kbits per second
- * @user_ratekbps: user rate in Kbits per second
- * @ratecode: rate that goes into HW descriptors
- * @dot11rate: value that goes into supported
- * rates info element of MLME
- * @ctrl_rate: Index of next lower basic rate, used for duration computation
- * @cw40index: Index of rates having 40MHz channel width
- * @sgi_index: Index of rates having Short Guard Interval
- * @ht_index: high throughput rates having 40MHz channel width and
- * Short Guard Interval
- * @probe_interval: interval for rate control to probe for other rates
- * @initial_ratemax: initial ratemax value
- */
-struct ath_rate_table {
- int rate_cnt;
- int mcs_start;
- struct {
- u16 rate_flags;
- u8 phy;
- u32 ratekbps;
- u32 user_ratekbps;
- u8 ratecode;
- u8 dot11rate;
- } info[RATE_TABLE_SIZE];
- u32 probe_interval;
- u8 initial_ratemax;
-};
-
-struct ath_rateset {
- u8 rs_nrates;
- u8 rs_rates[ATH_RATE_MAX];
-};
-
-struct ath_rc_stats {
- u32 success;
- u32 retries;
- u32 xretries;
- u8 per;
-};
-
-/**
- * struct ath_rate_priv - Rate Control priv data
- * @state: RC state
- * @probe_rate: rate we are probing at
- * @probe_time: msec timestamp for last probe
- * @hw_maxretry_pktcnt: num of packets since we got HW max retry error
- * @max_valid_rate: maximum number of valid rate
- * @per_down_time: msec timestamp for last PER down step
- * @valid_phy_ratecnt: valid rate count
- * @rate_max_phy: phy index for the max rate
- * @per: PER for every valid rate in %
- * @probe_interval: interval for ratectrl to probe for other rates
- * @ht_cap: HT capabilities
- * @neg_rates: Negotatied rates
- * @neg_ht_rates: Negotiated HT rates
- */
-struct ath_rate_priv {
- u8 rate_table_size;
- u8 probe_rate;
- u8 hw_maxretry_pktcnt;
- u8 max_valid_rate;
- u8 valid_rate_index[RATE_TABLE_SIZE];
- u8 ht_cap;
- u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX];
- u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][RATE_TABLE_SIZE];
- u8 rate_max_phy;
- u8 per[RATE_TABLE_SIZE];
- u32 probe_time;
- u32 per_down_time;
- u32 probe_interval;
- struct ath_rateset neg_rates;
- struct ath_rateset neg_ht_rates;
- const struct ath_rate_table *rate_table;
-
-#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
- struct dentry *debugfs_rcstats;
- struct ath_rc_stats rcstats[RATE_TABLE_SIZE];
-#endif
-};
-
-#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
-void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate);
-void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix,
- int xretries, int retries, u8 per);
-#else
-static inline void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate)
-{
-}
-static inline void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix,
- int xretries, int retries, u8 per)
-{
-}
-#endif
-
-#ifdef CONFIG_ATH9K_LEGACY_RATE_CONTROL
-int ath_rate_control_register(void);
-void ath_rate_control_unregister(void);
-#else
-static inline int ath_rate_control_register(void)
-{
- return 0;
-}
-
-static inline void ath_rate_control_unregister(void)
-{
-}
-#endif
-
-#endif /* RC_H */
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 82e340d3ec60..6c9accdb52e4 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -762,204 +762,6 @@ static struct ath_rxbuf *ath_get_next_rx_buf(struct ath_softc *sc,
return bf;
}
-/* Assumes you've already done the endian to CPU conversion */
-static bool ath9k_rx_accept(struct ath_common *common,
- struct ieee80211_hdr *hdr,
- struct ieee80211_rx_status *rxs,
- struct ath_rx_status *rx_stats,
- bool *decrypt_error)
-{
- struct ath_softc *sc = (struct ath_softc *) common->priv;
- bool is_mc, is_valid_tkip, strip_mic, mic_error;
- struct ath_hw *ah = common->ah;
- __le16 fc;
-
- fc = hdr->frame_control;
-
- is_mc = !!is_multicast_ether_addr(hdr->addr1);
- is_valid_tkip = rx_stats->rs_keyix != ATH9K_RXKEYIX_INVALID &&
- test_bit(rx_stats->rs_keyix, common->tkip_keymap);
- strip_mic = is_valid_tkip && ieee80211_is_data(fc) &&
- ieee80211_has_protected(fc) &&
- !(rx_stats->rs_status &
- (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC |
- ATH9K_RXERR_KEYMISS));
-
- /*
- * Key miss events are only relevant for pairwise keys where the
- * descriptor does contain a valid key index. This has been observed
- * mostly with CCMP encryption.
- */
- if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID ||
- !test_bit(rx_stats->rs_keyix, common->ccmp_keymap))
- rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS;
-
- mic_error = is_valid_tkip && !ieee80211_is_ctl(fc) &&
- !ieee80211_has_morefrags(fc) &&
- !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) &&
- (rx_stats->rs_status & ATH9K_RXERR_MIC);
-
- /*
- * The rx_stats->rs_status will not be set until the end of the
- * chained descriptors so it can be ignored if rs_more is set. The
- * rs_more will be false at the last element of the chained
- * descriptors.
- */
- if (rx_stats->rs_status != 0) {
- u8 status_mask;
-
- if (rx_stats->rs_status & ATH9K_RXERR_CRC) {
- rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
- mic_error = false;
- }
-
- if ((rx_stats->rs_status & ATH9K_RXERR_DECRYPT) ||
- (!is_mc && (rx_stats->rs_status & ATH9K_RXERR_KEYMISS))) {
- *decrypt_error = true;
- mic_error = false;
- }
-
- /*
- * Reject error frames with the exception of
- * decryption and MIC failures. For monitor mode,
- * we also ignore the CRC error.
- */
- status_mask = ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
- ATH9K_RXERR_KEYMISS;
-
- if (ah->is_monitoring && (sc->rx.rxfilter & FIF_FCSFAIL))
- status_mask |= ATH9K_RXERR_CRC;
-
- if (rx_stats->rs_status & ~status_mask)
- return false;
- }
-
- /*
- * For unicast frames the MIC error bit can have false positives,
- * so all MIC error reports need to be validated in software.
- * False negatives are not common, so skip software verification
- * if the hardware considers the MIC valid.
- */
- if (strip_mic)
- rxs->flag |= RX_FLAG_MMIC_STRIPPED;
- else if (is_mc && mic_error)
- rxs->flag |= RX_FLAG_MMIC_ERROR;
-
- return true;
-}
-
-static int ath9k_process_rate(struct ath_common *common,
- struct ieee80211_hw *hw,
- struct ath_rx_status *rx_stats,
- struct ieee80211_rx_status *rxs)
-{
- struct ieee80211_supported_band *sband;
- enum ieee80211_band band;
- unsigned int i = 0;
- struct ath_softc __maybe_unused *sc = common->priv;
- struct ath_hw *ah = sc->sc_ah;
-
- band = ah->curchan->chan->band;
- sband = hw->wiphy->bands[band];
-
- if (IS_CHAN_QUARTER_RATE(ah->curchan))
- rxs->flag |= RX_FLAG_5MHZ;
- else if (IS_CHAN_HALF_RATE(ah->curchan))
- rxs->flag |= RX_FLAG_10MHZ;
-
- if (rx_stats->rs_rate & 0x80) {
- /* HT rate */
- rxs->flag |= RX_FLAG_HT;
- rxs->flag |= rx_stats->flag;
- rxs->rate_idx = rx_stats->rs_rate & 0x7f;
- return 0;
- }
-
- for (i = 0; i < sband->n_bitrates; i++) {
- if (sband->bitrates[i].hw_value == rx_stats->rs_rate) {
- rxs->rate_idx = i;
- return 0;
- }
- if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) {
- rxs->flag |= RX_FLAG_SHORTPRE;
- rxs->rate_idx = i;
- return 0;
- }
- }
-
- /*
- * No valid hardware bitrate found -- we should not get here
- * because hardware has already validated this frame as OK.
- */
- ath_dbg(common, ANY,
- "unsupported hw bitrate detected 0x%02x using 1 Mbit\n",
- rx_stats->rs_rate);
- RX_STAT_INC(rx_rate_err);
- return -EINVAL;
-}
-
-static void ath9k_process_rssi(struct ath_common *common,
- struct ieee80211_hw *hw,
- struct ath_rx_status *rx_stats,
- struct ieee80211_rx_status *rxs)
-{
- struct ath_softc *sc = hw->priv;
- struct ath_hw *ah = common->ah;
- int last_rssi;
- int rssi = rx_stats->rs_rssi;
- int i, j;
-
- /*
- * RSSI is not available for subframes in an A-MPDU.
- */
- if (rx_stats->rs_moreaggr) {
- rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
- return;
- }
-
- /*
- * Check if the RSSI for the last subframe in an A-MPDU
- * or an unaggregated frame is valid.
- */
- if (rx_stats->rs_rssi == ATH9K_RSSI_BAD) {
- rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
- return;
- }
-
- for (i = 0, j = 0; i < ARRAY_SIZE(rx_stats->rs_rssi_ctl); i++) {
- s8 rssi;
-
- if (!(ah->rxchainmask & BIT(i)))
- continue;
-
- rssi = rx_stats->rs_rssi_ctl[i];
- if (rssi != ATH9K_RSSI_BAD) {
- rxs->chains |= BIT(j);
- rxs->chain_signal[j] = ah->noise + rssi;
- }
- j++;
- }
-
- /*
- * Update Beacon RSSI, this is used by ANI.
- */
- if (rx_stats->is_mybeacon &&
- ((ah->opmode == NL80211_IFTYPE_STATION) ||
- (ah->opmode == NL80211_IFTYPE_ADHOC))) {
- ATH_RSSI_LPF(sc->last_rssi, rx_stats->rs_rssi);
- last_rssi = sc->last_rssi;
-
- if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
- rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER);
- if (rssi < 0)
- rssi = 0;
-
- ah->stats.avgbrssi = rssi;
- }
-
- rxs->signal = ah->noise + rx_stats->rs_rssi;
-}
-
static void ath9k_process_tsf(struct ath_rx_status *rs,
struct ieee80211_rx_status *rxs,
u64 tsf)
@@ -1055,7 +857,7 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
* everything but the rate is checked here, the rate check is done
* separately to avoid doing two lookups for a rate for each frame.
*/
- if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error))
+ if (!ath9k_cmn_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error, sc->rx.rxfilter))
return -EINVAL;
if (ath_is_mybeacon(common, hdr)) {
@@ -1069,10 +871,18 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
if (WARN_ON(!ah->curchan))
return -EINVAL;
- if (ath9k_process_rate(common, hw, rx_stats, rx_status))
+ if (ath9k_cmn_process_rate(common, hw, rx_stats, rx_status)) {
+ /*
+ * No valid hardware bitrate found -- we should not get here
+ * because hardware has already validated this frame as OK.
+ */
+ ath_dbg(common, ANY, "unsupported hw bitrate detected 0x%02x using 1 Mbit\n",
+ rx_stats->rs_rate);
+ RX_STAT_INC(rx_rate_err);
return -EINVAL;
+ }
- ath9k_process_rssi(common, hw, rx_stats, rx_status);
+ ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status);
rx_status->band = ah->curchan->chan->band;
rx_status->freq = ah->curchan->chan->center_freq;
@@ -1092,57 +902,6 @@ corrupt:
return -EINVAL;
}
-static void ath9k_rx_skb_postprocess(struct ath_common *common,
- struct sk_buff *skb,
- struct ath_rx_status *rx_stats,
- struct ieee80211_rx_status *rxs,
- bool decrypt_error)
-{
- struct ath_hw *ah = common->ah;
- struct ieee80211_hdr *hdr;
- int hdrlen, padpos, padsize;
- u8 keyix;
- __le16 fc;
-
- /* see if any padding is done by the hw and remove it */
- hdr = (struct ieee80211_hdr *) skb->data;
- hdrlen = ieee80211_get_hdrlen_from_skb(skb);
- fc = hdr->frame_control;
- padpos = ieee80211_hdrlen(fc);
-
- /* The MAC header is padded to have 32-bit boundary if the
- * packet payload is non-zero. The general calculation for
- * padsize would take into account odd header lengths:
- * padsize = (4 - padpos % 4) % 4; However, since only
- * even-length headers are used, padding can only be 0 or 2
- * bytes and we can optimize this a bit. In addition, we must
- * not try to remove padding from short control frames that do
- * not have payload. */
- padsize = padpos & 3;
- if (padsize && skb->len>=padpos+padsize+FCS_LEN) {
- memmove(skb->data + padsize, skb->data, padpos);
- skb_pull(skb, padsize);
- }
-
- keyix = rx_stats->rs_keyix;
-
- if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error &&
- ieee80211_has_protected(fc)) {
- rxs->flag |= RX_FLAG_DECRYPTED;
- } else if (ieee80211_has_protected(fc)
- && !decrypt_error && skb->len >= hdrlen + 4) {
- keyix = skb->data[hdrlen + 3] >> 6;
-
- if (test_bit(keyix, common->keymap))
- rxs->flag |= RX_FLAG_DECRYPTED;
- }
- if (ah->sw_mgmt_crypto &&
- (rxs->flag & RX_FLAG_DECRYPTED) &&
- ieee80211_is_mgmt(fc))
- /* Use software decrypt for management frames. */
- rxs->flag &= ~RX_FLAG_DECRYPTED;
-}
-
/*
* Run the LNA combining algorithm only in these cases:
*
@@ -1292,8 +1051,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
skb_pull(skb, ah->caps.rx_status_len);
if (!rs.rs_more)
- ath9k_rx_skb_postprocess(common, hdr_skb, &rs,
- rxs, decrypt_error);
+ ath9k_cmn_rx_skb_postprocess(common, hdr_skb, &rs,
+ rxs, decrypt_error);
if (rs.rs_more) {
RX_STAT_INC(rx_frags);
diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c
index b686a7498450..a65cfb91adca 100644
--- a/drivers/net/wireless/ath/ath9k/tx99.c
+++ b/drivers/net/wireless/ath/ath9k/tx99.c
@@ -108,7 +108,7 @@ static int ath9k_tx99_init(struct ath_softc *sc)
struct ath_tx_control txctl;
int r;
- if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
+ if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
ath_err(common,
"driver is in invalid state unable to use TX99");
return -EINVAL;
diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c
index 1b3230fa3651..2879887f5691 100644
--- a/drivers/net/wireless/ath/ath9k/wow.c
+++ b/drivers/net/wireless/ath/ath9k/wow.c
@@ -198,7 +198,7 @@ int ath9k_suspend(struct ieee80211_hw *hw,
ath_cancel_work(sc);
ath_stop_ani(sc);
- if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
+ if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
ath_dbg(common, ANY, "Device not present\n");
ret = -EINVAL;
goto fail_wow;
@@ -224,7 +224,7 @@ int ath9k_suspend(struct ieee80211_hw *hw,
* STA.
*/
- if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
+ if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
ath_dbg(common, WOW, "None of the STA vifs are associated\n");
ret = 1;
goto fail_wow;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 55897d508a76..87cbec47fb48 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1040,11 +1040,11 @@ static int ath_max_framelen(int usec, int mcs, bool ht40, bool sgi)
int symbols, bits;
int bytes = 0;
+ usec -= L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
symbols = sgi ? TIME_SYMBOLS_HALFGI(usec) : TIME_SYMBOLS(usec);
bits = symbols * bits_per_symbol[mcs % 8][ht40] * streams;
bits -= OFDM_PLCP_BITS;
bytes = bits / 8;
- bytes -= L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
if (bytes > 65532)
bytes = 65532;
@@ -1076,6 +1076,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_info *info, int len, bool rts)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct sk_buff *skb;
struct ieee80211_tx_info *tx_info;
struct ieee80211_tx_rate *rates;
@@ -1145,7 +1146,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
}
/* legacy rates */
- rate = &sc->sbands[tx_info->band].bitrates[rates[i].idx];
+ rate = &common->sbands[tx_info->band].bitrates[rates[i].idx];
if ((tx_info->band == IEEE80211_BAND_2GHZ) &&
!(rate->flags & IEEE80211_RATE_ERP_G))
phy = WLAN_RC_PHY_CCK;
@@ -1698,7 +1699,7 @@ int ath_cabq_update(struct ath_softc *sc)
ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
- qi.tqi_readyTime = (cur_conf->beacon_interval *
+ qi.tqi_readyTime = (TU_TO_USEC(cur_conf->beacon_interval) *
ATH_CABQ_READY_TIME) / 100;
ath_txq_update(sc, qnum, &qi);
@@ -1768,7 +1769,7 @@ bool ath_drain_all_txq(struct ath_softc *sc)
int i;
u32 npend = 0;
- if (test_bit(SC_OP_INVALID, &sc->sc_flags))
+ if (test_bit(ATH_OP_INVALID, &common->op_flags))
return true;
ath9k_hw_abort_tx_dma(ah);
@@ -1816,11 +1817,12 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
*/
void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_ac *ac, *last_ac;
struct ath_atx_tid *tid, *last_tid;
bool sent = false;
- if (test_bit(SC_OP_HW_RESET, &sc->sc_flags) ||
+ if (test_bit(ATH_OP_HW_RESET, &common->op_flags) ||
list_empty(&txq->axq_acq))
return;
@@ -2470,7 +2472,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
ath_txq_lock(sc, txq);
for (;;) {
- if (test_bit(SC_OP_HW_RESET, &sc->sc_flags))
+ if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
break;
if (list_empty(&txq->axq_q)) {
@@ -2553,7 +2555,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
int status;
for (;;) {
- if (test_bit(SC_OP_HW_RESET, &sc->sc_flags))
+ if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
break;
status = ath9k_hw_txprocdesc(ah, NULL, (void *)&ts);
@@ -2569,7 +2571,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
sc->beacon.tx_processed = true;
sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK);
- ath9k_csa_is_finished(sc);
+ ath9k_csa_update(sc);
continue;
}
diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
index 536bc46a2912..924135b8e575 100644
--- a/drivers/net/wireless/ath/carl9170/rx.c
+++ b/drivers/net/wireless/ath/carl9170/rx.c
@@ -572,7 +572,7 @@ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len)
static void carl9170_ba_check(struct ar9170 *ar, void *data, unsigned int len)
{
- struct ieee80211_bar *bar = (void *) data;
+ struct ieee80211_bar *bar = data;
struct carl9170_bar_list_entry *entry;
unsigned int queue;
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index e5e905910db4..415393dfb6fc 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -222,7 +222,7 @@ static const struct ieee80211_regdomain *ath_default_world_regdomain(void)
static const struct
ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg)
{
- switch (reg->regpair->regDmnEnum) {
+ switch (reg->regpair->reg_domain) {
case 0x60:
case 0x61:
case 0x62:
@@ -431,7 +431,7 @@ static void ath_reg_apply_world_flags(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator,
struct ath_regulatory *reg)
{
- switch (reg->regpair->regDmnEnum) {
+ switch (reg->regpair->reg_domain) {
case 0x60:
case 0x63:
case 0x66:
@@ -560,7 +560,7 @@ static bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg)
printk(KERN_DEBUG "ath: EEPROM indicates we "
"should expect a direct regpair map\n");
for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
- if (regDomainPairs[i].regDmnEnum == rd)
+ if (regDomainPairs[i].reg_domain == rd)
return true;
}
printk(KERN_DEBUG
@@ -617,7 +617,7 @@ ath_get_regpair(int regdmn)
if (regdmn == NO_ENUMRD)
return NULL;
for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
- if (regDomainPairs[i].regDmnEnum == regdmn)
+ if (regDomainPairs[i].reg_domain == regdmn)
return &regDomainPairs[i];
}
return NULL;
@@ -741,7 +741,7 @@ static int __ath_regd_init(struct ath_regulatory *reg)
printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n",
reg->alpha2[0], reg->alpha2[1]);
printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n",
- reg->regpair->regDmnEnum);
+ reg->regpair->reg_domain);
return 0;
}
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c
index ee25786b4447..73f12f196f14 100644
--- a/drivers/net/wireless/ath/wcn36xx/dxe.c
+++ b/drivers/net/wireless/ath/wcn36xx/dxe.c
@@ -44,6 +44,14 @@ static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data)
writel(data, wcn->mmio + addr);
}
+#define wcn36xx_dxe_write_register_x(wcn, reg, reg_data) \
+do { \
+ if (wcn->chip_version == WCN36XX_CHIP_3680) \
+ wcn36xx_dxe_write_register(wcn, reg ## _3680, reg_data); \
+ else \
+ wcn36xx_dxe_write_register(wcn, reg ## _3660, reg_data); \
+} while (0) \
+
static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data)
{
*data = readl(wcn->mmio + addr);
@@ -680,7 +688,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
/* Setting interrupt path */
reg_data = WCN36XX_DXE_CCU_INT;
- wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data);
+ wcn36xx_dxe_write_register_x(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data);
/***************************************/
/* Init descriptors for TX LOW channel */
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.h b/drivers/net/wireless/ath/wcn36xx/dxe.h
index c88562f85de1..35ee7e966bd2 100644
--- a/drivers/net/wireless/ath/wcn36xx/dxe.h
+++ b/drivers/net/wireless/ath/wcn36xx/dxe.h
@@ -28,11 +28,11 @@ H2H_TEST_RX_TX = DMA2
*/
/* DXE registers */
-#define WCN36XX_DXE_MEM_BASE 0x03000000
#define WCN36XX_DXE_MEM_REG 0x202000
#define WCN36XX_DXE_CCU_INT 0xA0011
-#define WCN36XX_DXE_REG_CCU_INT 0x200b10
+#define WCN36XX_DXE_REG_CCU_INT_3660 0x200b10
+#define WCN36XX_DXE_REG_CCU_INT_3680 0x2050dc
/* TODO This must calculated properly but not hardcoded */
#define WCN36XX_DXE_CTRL_TX_L 0x328a44
diff --git a/drivers/net/wireless/ath/wcn36xx/hal.h b/drivers/net/wireless/ath/wcn36xx/hal.h
index 3c2ef0c32f72..a1f1127d7808 100644
--- a/drivers/net/wireless/ath/wcn36xx/hal.h
+++ b/drivers/net/wireless/ath/wcn36xx/hal.h
@@ -4384,11 +4384,13 @@ enum place_holder_in_cap_bitmap {
MAX_FEATURE_SUPPORTED = 128,
};
+#define WCN36XX_HAL_CAPS_SIZE 4
+
struct wcn36xx_hal_feat_caps_msg {
struct wcn36xx_hal_msg_header header;
- u32 feat_caps[4];
+ u32 feat_caps[WCN36XX_HAL_CAPS_SIZE];
} __packed;
/* status codes to help debug rekey failures */
diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c
index e64a6784079e..4ab5370ab7a6 100644
--- a/drivers/net/wireless/ath/wcn36xx/main.c
+++ b/drivers/net/wireless/ath/wcn36xx/main.c
@@ -17,6 +17,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
+#include <linux/firmware.h>
#include <linux/platform_device.h>
#include "wcn36xx.h"
@@ -177,6 +178,60 @@ static inline u8 get_sta_index(struct ieee80211_vif *vif,
sta_priv->sta_index;
}
+static const char * const wcn36xx_caps_names[] = {
+ "MCC", /* 0 */
+ "P2P", /* 1 */
+ "DOT11AC", /* 2 */
+ "SLM_SESSIONIZATION", /* 3 */
+ "DOT11AC_OPMODE", /* 4 */
+ "SAP32STA", /* 5 */
+ "TDLS", /* 6 */
+ "P2P_GO_NOA_DECOUPLE_INIT_SCAN",/* 7 */
+ "WLANACTIVE_OFFLOAD", /* 8 */
+ "BEACON_OFFLOAD", /* 9 */
+ "SCAN_OFFLOAD", /* 10 */
+ "ROAM_OFFLOAD", /* 11 */
+ "BCN_MISS_OFFLOAD", /* 12 */
+ "STA_POWERSAVE", /* 13 */
+ "STA_ADVANCED_PWRSAVE", /* 14 */
+ "AP_UAPSD", /* 15 */
+ "AP_DFS", /* 16 */
+ "BLOCKACK", /* 17 */
+ "PHY_ERR", /* 18 */
+ "BCN_FILTER", /* 19 */
+ "RTT", /* 20 */
+ "RATECTRL", /* 21 */
+ "WOW" /* 22 */
+};
+
+static const char *wcn36xx_get_cap_name(enum place_holder_in_cap_bitmap x)
+{
+ if (x >= ARRAY_SIZE(wcn36xx_caps_names))
+ return "UNKNOWN";
+ return wcn36xx_caps_names[x];
+}
+
+static void wcn36xx_feat_caps_info(struct wcn36xx *wcn)
+{
+ int i;
+
+ for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) {
+ if (get_feat_caps(wcn->fw_feat_caps, i))
+ wcn36xx_info("FW Cap %s\n", wcn36xx_get_cap_name(i));
+ }
+}
+
+static void wcn36xx_detect_chip_version(struct wcn36xx *wcn)
+{
+ if (get_feat_caps(wcn->fw_feat_caps, DOT11AC)) {
+ wcn36xx_info("Chip is 3680\n");
+ wcn->chip_version = WCN36XX_CHIP_3680;
+ } else {
+ wcn36xx_info("Chip is 3660\n");
+ wcn->chip_version = WCN36XX_CHIP_3660;
+ }
+}
+
static int wcn36xx_start(struct ieee80211_hw *hw)
{
struct wcn36xx *wcn = hw->priv;
@@ -223,6 +278,16 @@ static int wcn36xx_start(struct ieee80211_hw *hw)
goto out_free_smd_buf;
}
+ if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
+ ret = wcn36xx_smd_feature_caps_exchange(wcn);
+ if (ret)
+ wcn36xx_warn("Exchange feature caps failed\n");
+ else
+ wcn36xx_feat_caps_info(wcn);
+ }
+
+ wcn36xx_detect_chip_version(wcn);
+
/* DMA channel initialization */
ret = wcn36xx_dxe_init(wcn);
if (ret) {
@@ -232,11 +297,6 @@ static int wcn36xx_start(struct ieee80211_hw *hw)
wcn36xx_debugfs_init(wcn);
- if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
- ret = wcn36xx_smd_feature_caps_exchange(wcn);
- if (ret)
- wcn36xx_warn("Exchange feature caps failed\n");
- }
INIT_LIST_HEAD(&wcn->vif_list);
return 0;
@@ -648,6 +708,7 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
bss_conf->enable_beacon);
if (bss_conf->enable_beacon) {
+ vif_priv->dtim_period = bss_conf->dtim_period;
vif_priv->bss_index = 0xff;
wcn36xx_smd_config_bss(wcn, vif, NULL,
vif->addr, false);
@@ -992,6 +1053,7 @@ static int wcn36xx_remove(struct platform_device *pdev)
struct wcn36xx *wcn = hw->priv;
wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
+ release_firmware(wcn->nv);
mutex_destroy(&wcn->hal_mutex);
ieee80211_unregister_hw(hw);
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c
index 750626b0e22d..7bf0ef8a1f56 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.c
+++ b/drivers/net/wireless/ath/wcn36xx/smd.c
@@ -195,9 +195,11 @@ static void wcn36xx_smd_set_sta_params(struct wcn36xx *wcn,
static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len)
{
int ret = 0;
+ unsigned long start;
wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "HAL >>> ", wcn->hal_buf, len);
init_completion(&wcn->hal_rsp_compl);
+ start = jiffies;
ret = wcn->ctrl_ops->tx(wcn->hal_buf, len);
if (ret) {
wcn36xx_err("HAL TX failed\n");
@@ -205,10 +207,13 @@ static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len)
}
if (wait_for_completion_timeout(&wcn->hal_rsp_compl,
msecs_to_jiffies(HAL_MSG_TIMEOUT)) <= 0) {
- wcn36xx_err("Timeout while waiting SMD response\n");
+ wcn36xx_err("Timeout! No SMD response in %dms\n",
+ HAL_MSG_TIMEOUT);
ret = -ETIME;
goto out;
}
+ wcn36xx_dbg(WCN36XX_DBG_SMD, "SMD command completed in %dms",
+ jiffies_to_msecs(jiffies - start));
out:
return ret;
}
@@ -246,21 +251,22 @@ static int wcn36xx_smd_rsp_status_check(void *buf, size_t len)
int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
{
- const struct firmware *nv;
struct nv_data *nv_d;
struct wcn36xx_hal_nv_img_download_req_msg msg_body;
int fw_bytes_left;
int ret;
u16 fm_offset = 0;
- ret = request_firmware(&nv, WLAN_NV_FILE, wcn->dev);
- if (ret) {
- wcn36xx_err("Failed to load nv file %s: %d\n",
- WLAN_NV_FILE, ret);
- goto out_free_nv;
+ if (!wcn->nv) {
+ ret = request_firmware(&wcn->nv, WLAN_NV_FILE, wcn->dev);
+ if (ret) {
+ wcn36xx_err("Failed to load nv file %s: %d\n",
+ WLAN_NV_FILE, ret);
+ goto out;
+ }
}
- nv_d = (struct nv_data *)nv->data;
+ nv_d = (struct nv_data *)wcn->nv->data;
INIT_HAL_MSG(msg_body, WCN36XX_HAL_DOWNLOAD_NV_REQ);
msg_body.header.len += WCN36XX_NV_FRAGMENT_SIZE;
@@ -270,7 +276,7 @@ int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
mutex_lock(&wcn->hal_mutex);
do {
- fw_bytes_left = nv->size - fm_offset - 4;
+ fw_bytes_left = wcn->nv->size - fm_offset - 4;
if (fw_bytes_left > WCN36XX_NV_FRAGMENT_SIZE) {
msg_body.last_fragment = 0;
msg_body.nv_img_buffer_size = WCN36XX_NV_FRAGMENT_SIZE;
@@ -308,10 +314,7 @@ int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
out_unlock:
mutex_unlock(&wcn->hal_mutex);
-out_free_nv:
- release_firmware(nv);
-
- return ret;
+out: return ret;
}
static int wcn36xx_smd_start_rsp(struct wcn36xx *wcn, void *buf, size_t len)
@@ -899,11 +902,12 @@ static int wcn36xx_smd_config_sta_rsp(struct wcn36xx *wcn,
sta_priv->sta_index = params->sta_index;
sta_priv->dpu_desc_index = params->dpu_index;
+ sta_priv->ucast_dpu_sign = params->uc_ucast_sig;
wcn36xx_dbg(WCN36XX_DBG_HAL,
- "hal config sta rsp status %d sta_index %d bssid_index %d p2p %d\n",
+ "hal config sta rsp status %d sta_index %d bssid_index %d uc_ucast_sig %d p2p %d\n",
params->status, params->sta_index, params->bssid_index,
- params->p2p);
+ params->uc_ucast_sig, params->p2p);
return 0;
}
@@ -1118,7 +1122,7 @@ static int wcn36xx_smd_config_bss_rsp(struct wcn36xx *wcn,
priv_vif->sta->bss_dpu_desc_index = params->dpu_desc_index;
}
- priv_vif->ucast_dpu_signature = params->ucast_dpu_signature;
+ priv_vif->self_ucast_dpu_sign = params->ucast_dpu_signature;
return 0;
}
@@ -1637,12 +1641,12 @@ int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn,
ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
if (ret) {
- wcn36xx_err("Sending hal_exit_bmps failed\n");
+ wcn36xx_err("Sending hal_keep_alive failed\n");
goto out;
}
ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
if (ret) {
- wcn36xx_err("hal_exit_bmps response failed err=%d\n", ret);
+ wcn36xx_err("hal_keep_alive response failed err=%d\n", ret);
goto out;
}
out:
@@ -1682,8 +1686,7 @@ out:
return ret;
}
-static inline void set_feat_caps(u32 *bitmap,
- enum place_holder_in_cap_bitmap cap)
+void set_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap)
{
int arr_idx, bit_idx;
@@ -1697,8 +1700,7 @@ static inline void set_feat_caps(u32 *bitmap,
bitmap[arr_idx] |= (1 << bit_idx);
}
-static inline int get_feat_caps(u32 *bitmap,
- enum place_holder_in_cap_bitmap cap)
+int get_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap)
{
int arr_idx, bit_idx;
int ret = 0;
@@ -1714,8 +1716,7 @@ static inline int get_feat_caps(u32 *bitmap,
return ret;
}
-static inline void clear_feat_caps(u32 *bitmap,
- enum place_holder_in_cap_bitmap cap)
+void clear_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap)
{
int arr_idx, bit_idx;
@@ -1731,8 +1732,8 @@ static inline void clear_feat_caps(u32 *bitmap,
int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn)
{
- struct wcn36xx_hal_feat_caps_msg msg_body;
- int ret = 0;
+ struct wcn36xx_hal_feat_caps_msg msg_body, *rsp;
+ int ret = 0, i;
mutex_lock(&wcn->hal_mutex);
INIT_HAL_MSG(msg_body, WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_REQ);
@@ -1746,12 +1747,15 @@ int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn)
wcn36xx_err("Sending hal_feature_caps_exchange failed\n");
goto out;
}
- ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
- if (ret) {
- wcn36xx_err("hal_feature_caps_exchange response failed err=%d\n",
- ret);
+ if (wcn->hal_rsp_len != sizeof(*rsp)) {
+ wcn36xx_err("Invalid hal_feature_caps_exchange response");
goto out;
}
+
+ rsp = (struct wcn36xx_hal_feat_caps_msg *) wcn->hal_buf;
+
+ for (i = 0; i < WCN36XX_HAL_CAPS_SIZE; i++)
+ wcn->fw_feat_caps[i] = rsp->feat_caps[i];
out:
mutex_unlock(&wcn->hal_mutex);
return ret;
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h
index e7c39019c6f1..008d03423dbf 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.h
+++ b/drivers/net/wireless/ath/wcn36xx/smd.h
@@ -24,7 +24,7 @@
#define WCN36XX_HAL_BUF_SIZE 4096
-#define HAL_MSG_TIMEOUT 200
+#define HAL_MSG_TIMEOUT 500
#define WCN36XX_SMSM_WLAN_TX_ENABLE 0x00000400
#define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY 0x00000200
/* The PNO version info be contained in the rsp msg */
@@ -112,6 +112,9 @@ int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn,
int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2,
u32 arg3, u32 arg4, u32 arg5);
int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn);
+void set_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap);
+int get_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap);
+void clear_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap);
int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn,
struct ieee80211_sta *sta,
diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c
index b2b60e30caaf..32bb26a0db2a 100644
--- a/drivers/net/wireless/ath/wcn36xx/txrx.c
+++ b/drivers/net/wireless/ath/wcn36xx/txrx.c
@@ -57,8 +57,7 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb)
RX_FLAG_MMIC_STRIPPED |
RX_FLAG_DECRYPTED;
- wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x status->vendor_radiotap_len=%x\n",
- status.flag, status.vendor_radiotap_len);
+ wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x\n", status.flag);
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
@@ -132,6 +131,7 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
struct ieee80211_vif,
drv_priv);
+ bd->dpu_sign = sta_priv->ucast_dpu_sign;
if (vif->type == NL80211_IFTYPE_STATION) {
bd->sta_index = sta_priv->bss_sta_index;
bd->dpu_desc_idx = sta_priv->bss_dpu_desc_index;
@@ -145,10 +145,9 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
__vif_priv = get_vif_by_addr(wcn, hdr->addr2);
bd->sta_index = __vif_priv->self_sta_index;
bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index;
+ bd->dpu_sign = __vif_priv->self_ucast_dpu_sign;
}
- bd->dpu_sign = __vif_priv->ucast_dpu_signature;
-
if (ieee80211_is_nullfunc(hdr->frame_control) ||
(sta_priv && !sta_priv->is_data_encrypted))
bd->dpu_ne = 1;
diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
index 8fa5cbace5ab..f0fb81dfd17b 100644
--- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
+++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
@@ -125,10 +125,10 @@ struct wcn36xx_vif {
enum wcn36xx_power_state pw_state;
u8 bss_index;
- u8 ucast_dpu_signature;
/* Returned from WCN36XX_HAL_ADD_STA_SELF_RSP */
u8 self_sta_index;
u8 self_dpu_desc_index;
+ u8 self_ucast_dpu_sign;
};
/**
@@ -159,6 +159,7 @@ struct wcn36xx_sta {
u16 tid;
u8 sta_index;
u8 dpu_desc_index;
+ u8 ucast_dpu_sign;
u8 bss_sta_index;
u8 bss_dpu_desc_index;
bool is_data_encrypted;
@@ -171,10 +172,14 @@ struct wcn36xx {
struct device *dev;
struct list_head vif_list;
+ const struct firmware *nv;
+
u8 fw_revision;
u8 fw_version;
u8 fw_minor;
u8 fw_major;
+ u32 fw_feat_caps[WCN36XX_HAL_CAPS_SIZE];
+ u32 chip_version;
/* extra byte for the NULL termination */
u8 crm_version[WCN36XX_HAL_VERSION_LENGTH + 1];
@@ -222,6 +227,9 @@ struct wcn36xx {
};
+#define WCN36XX_CHIP_3660 0
+#define WCN36XX_CHIP_3680 1
+
static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn,
u8 major,
u8 minor,
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
index 990dd42ae79e..c7a3465fd02a 100644
--- a/drivers/net/wireless/ath/wil6210/Makefile
+++ b/drivers/net/wireless/ath/wil6210/Makefile
@@ -9,6 +9,7 @@ wil6210-y += wmi.o
wil6210-y += interrupt.o
wil6210-y += txrx.o
wil6210-y += debug.o
+wil6210-y += rx_reorder.o
wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
# for tracing framework to find trace.h
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 5b340769d5bb..4806a49cb61b 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -104,41 +104,125 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type)
return -EOPNOTSUPP;
}
-static int wil_cfg80211_get_station(struct wiphy *wiphy,
- struct net_device *ndev,
- u8 *mac, struct station_info *sinfo)
+static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
+ struct station_info *sinfo)
{
- struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- int rc;
struct wmi_notify_req_cmd cmd = {
- .cid = 0,
+ .cid = cid,
.interval_usec = 0,
};
+ struct {
+ struct wil6210_mbox_hdr_wmi wmi;
+ struct wmi_notify_req_done_event evt;
+ } __packed reply;
+ struct wil_net_stats *stats = &wil->sta[cid].stats;
+ int rc;
- if (memcmp(mac, wil->dst_addr[0], ETH_ALEN))
- return -ENOENT;
-
- /* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */
rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd),
- WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20);
+ WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20);
if (rc)
return rc;
+ wil_dbg_wmi(wil, "Link status for CID %d: {\n"
+ " MCS %d TSF 0x%016llx\n"
+ " BF status 0x%08x SNR 0x%08x SQI %d%%\n"
+ " Tx Tpt %d goodput %d Rx goodput %d\n"
+ " Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n",
+ cid, le16_to_cpu(reply.evt.bf_mcs),
+ le64_to_cpu(reply.evt.tsf), reply.evt.status,
+ le32_to_cpu(reply.evt.snr_val),
+ reply.evt.sqi,
+ le32_to_cpu(reply.evt.tx_tpt),
+ le32_to_cpu(reply.evt.tx_goodput),
+ le32_to_cpu(reply.evt.rx_goodput),
+ le16_to_cpu(reply.evt.my_rx_sector),
+ le16_to_cpu(reply.evt.my_tx_sector),
+ le16_to_cpu(reply.evt.other_rx_sector),
+ le16_to_cpu(reply.evt.other_tx_sector));
+
sinfo->generation = wil->sinfo_gen;
- sinfo->filled |= STATION_INFO_TX_BITRATE;
+ sinfo->filled = STATION_INFO_RX_BYTES |
+ STATION_INFO_TX_BYTES |
+ STATION_INFO_RX_PACKETS |
+ STATION_INFO_TX_PACKETS |
+ STATION_INFO_RX_BITRATE |
+ STATION_INFO_TX_BITRATE |
+ STATION_INFO_RX_DROP_MISC |
+ STATION_INFO_TX_FAILED;
+
sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
- sinfo->txrate.mcs = wil->stats.bf_mcs;
- sinfo->filled |= STATION_INFO_RX_BITRATE;
+ sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs);
sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
- sinfo->rxrate.mcs = wil->stats.last_mcs_rx;
+ sinfo->rxrate.mcs = stats->last_mcs_rx;
+ sinfo->rx_bytes = stats->rx_bytes;
+ sinfo->rx_packets = stats->rx_packets;
+ sinfo->rx_dropped_misc = stats->rx_dropped;
+ sinfo->tx_bytes = stats->tx_bytes;
+ sinfo->tx_packets = stats->tx_packets;
+ sinfo->tx_failed = stats->tx_errors;
if (test_bit(wil_status_fwconnected, &wil->status)) {
sinfo->filled |= STATION_INFO_SIGNAL;
- sinfo->signal = 12; /* TODO: provide real value */
+ sinfo->signal = reply.evt.sqi;
}
- return 0;
+ return rc;
+}
+
+static int wil_cfg80211_get_station(struct wiphy *wiphy,
+ struct net_device *ndev,
+ u8 *mac, struct station_info *sinfo)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+
+ int cid = wil_find_cid(wil, mac);
+
+ wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid);
+ if (cid < 0)
+ return cid;
+
+ rc = wil_cid_fill_sinfo(wil, cid, sinfo);
+
+ return rc;
+}
+
+/*
+ * Find @idx-th active STA for station dump.
+ */
+static int wil_find_cid_by_idx(struct wil6210_priv *wil, int idx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
+ if (wil->sta[i].status == wil_sta_unused)
+ continue;
+ if (idx == 0)
+ return i;
+ idx--;
+ }
+
+ return -ENOENT;
+}
+
+static int wil_cfg80211_dump_station(struct wiphy *wiphy,
+ struct net_device *dev, int idx,
+ u8 *mac, struct station_info *sinfo)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+ int cid = wil_find_cid_by_idx(wil, idx);
+
+ if (cid < 0)
+ return -ENOENT;
+
+ memcpy(mac, wil->sta[cid].addr, ETH_ALEN);
+ wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid);
+
+ rc = wil_cid_fill_sinfo(wil, cid, sinfo);
+
+ return rc;
}
static int wil_cfg80211_change_iface(struct wiphy *wiphy,
@@ -181,6 +265,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
u16 chnl[4];
} __packed cmd;
uint i, n;
+ int rc;
if (wil->scan_request) {
wil_err(wil, "Already scanning\n");
@@ -198,7 +283,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
/* FW don't support scan after connection attempt */
if (test_bit(wil_status_dontscan, &wil->status)) {
- wil_err(wil, "Scan after connect attempt not supported\n");
+ wil_err(wil, "Can't scan now\n");
return -EBUSY;
}
@@ -221,8 +306,13 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
request->channels[i]->center_freq);
}
- return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
+ rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
+
+ if (rc)
+ wil->scan_request = NULL;
+
+ return rc;
}
static int wil_cfg80211_connect(struct wiphy *wiphy,
@@ -237,6 +327,10 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
int ch;
int rc = 0;
+ if (test_bit(wil_status_fwconnecting, &wil->status) ||
+ test_bit(wil_status_fwconnected, &wil->status))
+ return -EALREADY;
+
bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
sme->ssid, sme->ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
@@ -318,10 +412,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
memcpy(conn.bssid, bss->bssid, ETH_ALEN);
memcpy(conn.dst_mac, bss->bssid, ETH_ALEN);
- /*
- * FW don't support scan after connection attempt
- */
- set_bit(wil_status_dontscan, &wil->status);
+
set_bit(wil_status_fwconnecting, &wil->status);
rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
@@ -330,7 +421,6 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
mod_timer(&wil->connect_timer,
jiffies + msecs_to_jiffies(2000));
} else {
- clear_bit(wil_status_dontscan, &wil->status);
clear_bit(wil_status_fwconnecting, &wil->status);
}
@@ -352,6 +442,40 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,
return rc;
}
+static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie)
+{
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+ struct ieee80211_mgmt *mgmt_frame = (void *)buf;
+ struct wmi_sw_tx_req_cmd *cmd;
+ struct {
+ struct wil6210_mbox_hdr_wmi wmi;
+ struct wmi_sw_tx_complete_event evt;
+ } __packed evt;
+
+ cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN);
+ cmd->len = cpu_to_le16(len);
+ memcpy(cmd->payload, buf, len);
+
+ rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
+ WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
+ if (rc == 0)
+ rc = evt.evt.status;
+
+ kfree(cmd);
+
+ return rc;
+}
+
static int wil_cfg80211_set_channel(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
@@ -402,6 +526,41 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
return 0;
}
+static int wil_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration,
+ u64 *cookie)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+
+ /* TODO: handle duration */
+ wil_info(wil, "%s(%d, %d ms)\n", __func__, chan->center_freq, duration);
+
+ rc = wmi_set_channel(wil, chan->hw_value);
+ if (rc)
+ return rc;
+
+ rc = wmi_rxon(wil, true);
+
+ return rc;
+}
+
+static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+
+ wil_info(wil, "%s()\n", __func__);
+
+ rc = wmi_rxon(wil, false);
+
+ return rc;
+}
+
static int wil_fix_bcon(struct wil6210_priv *wil,
struct cfg80211_beacon_data *bcon)
{
@@ -450,18 +609,20 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
if (wil_fix_bcon(wil, bcon))
wil_dbg_misc(wil, "Fixed bcon\n");
+ mutex_lock(&wil->mutex);
+
rc = wil_reset(wil);
if (rc)
- return rc;
+ goto out;
/* Rx VRING. */
rc = wil_rx_init(wil);
if (rc)
- return rc;
+ goto out;
rc = wmi_set_ssid(wil, info->ssid_len, info->ssid);
if (rc)
- return rc;
+ goto out;
/* MAC address - pre-requisite for other commands */
wmi_set_mac_address(wil, ndev->dev_addr);
@@ -485,11 +646,13 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype,
channel->hw_value);
if (rc)
- return rc;
+ goto out;
netif_carrier_on(ndev);
+out:
+ mutex_unlock(&wil->mutex);
return rc;
}
@@ -499,17 +662,36 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
int rc = 0;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ mutex_lock(&wil->mutex);
+
rc = wmi_pcp_stop(wil);
+ mutex_unlock(&wil->mutex);
return rc;
}
+static int wil_cfg80211_del_station(struct wiphy *wiphy,
+ struct net_device *dev, u8 *mac)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+ mutex_lock(&wil->mutex);
+ wil6210_disconnect(wil, mac);
+ mutex_unlock(&wil->mutex);
+
+ return 0;
+}
+
static struct cfg80211_ops wil_cfg80211_ops = {
.scan = wil_cfg80211_scan,
.connect = wil_cfg80211_connect,
.disconnect = wil_cfg80211_disconnect,
.change_virtual_intf = wil_cfg80211_change_iface,
.get_station = wil_cfg80211_get_station,
+ .dump_station = wil_cfg80211_dump_station,
+ .remain_on_channel = wil_remain_on_channel,
+ .cancel_remain_on_channel = wil_cancel_remain_on_channel,
+ .mgmt_tx = wil_cfg80211_mgmt_tx,
.set_monitor_channel = wil_cfg80211_set_channel,
.add_key = wil_cfg80211_add_key,
.del_key = wil_cfg80211_del_key,
@@ -517,6 +699,7 @@ static struct cfg80211_ops wil_cfg80211_ops = {
/* AP mode */
.start_ap = wil_cfg80211_start_ap,
.stop_ap = wil_cfg80211_stop_ap,
+ .del_station = wil_cfg80211_del_station,
};
static void wil_wiphy_init(struct wiphy *wiphy)
@@ -542,7 +725,7 @@ static void wil_wiphy_init(struct wiphy *wiphy)
wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz;
/* TODO: figure this out */
- wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
wiphy->cipher_suites = wil_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 1caa31992a7e..ecdabe4adec3 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -26,9 +26,11 @@
/* Nasty hack. Better have per device instances */
static u32 mem_addr;
static u32 dbg_txdesc_index;
+static u32 dbg_vring_index; /* 24+ for Rx, 0..23 for Tx */
static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
- const char *name, struct vring *vring)
+ const char *name, struct vring *vring,
+ char _s, char _h)
{
void __iomem *x = wmi_addr(wil, vring->hwtail);
@@ -50,8 +52,8 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
volatile struct vring_tx_desc *d = &vring->va[i].tx;
if ((i % 64) == 0 && (i != 0))
seq_printf(s, "\n");
- seq_printf(s, "%s", (d->dma.status & BIT(0)) ?
- "S" : (vring->ctx[i].skb ? "H" : "h"));
+ seq_printf(s, "%c", (d->dma.status & BIT(0)) ?
+ _s : (vring->ctx[i].skb ? _h : 'h'));
}
seq_printf(s, "\n");
}
@@ -63,14 +65,19 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data)
uint i;
struct wil6210_priv *wil = s->private;
- wil_print_vring(s, wil, "rx", &wil->vring_rx);
+ wil_print_vring(s, wil, "rx", &wil->vring_rx, 'S', '_');
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
struct vring *vring = &(wil->vring_tx[i]);
if (vring->va) {
+ int cid = wil->vring2cid_tid[i][0];
+ int tid = wil->vring2cid_tid[i][1];
char name[10];
snprintf(name, sizeof(name), "tx_%2d", i);
- wil_print_vring(s, wil, name, vring);
+
+ seq_printf(s, "\n%pM CID %d TID %d\n",
+ wil->sta[cid].addr, cid, tid);
+ wil_print_vring(s, wil, name, vring, '_', 'H');
}
}
@@ -390,25 +397,78 @@ static const struct file_operations fops_reset = {
.write = wil_write_file_reset,
.open = simple_open,
};
-/*---------Tx descriptor------------*/
+static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
+ const char *prefix)
+{
+ char printbuf[16 * 3 + 2];
+ int i = 0;
+ while (i < len) {
+ int l = min(len - i, 16);
+ hex_dump_to_buffer(p + i, l, 16, 1, printbuf,
+ sizeof(printbuf), false);
+ seq_printf(s, "%s%s\n", prefix, printbuf);
+ i += l;
+ }
+}
+
+static void wil_seq_print_skb(struct seq_file *s, struct sk_buff *skb)
+{
+ int i = 0;
+ int len = skb_headlen(skb);
+ void *p = skb->data;
+ int nr_frags = skb_shinfo(skb)->nr_frags;
+
+ seq_printf(s, " len = %d\n", len);
+ wil_seq_hexdump(s, p, len, " : ");
+
+ if (nr_frags) {
+ seq_printf(s, " nr_frags = %d\n", nr_frags);
+ for (i = 0; i < nr_frags; i++) {
+ const struct skb_frag_struct *frag =
+ &skb_shinfo(skb)->frags[i];
+
+ len = skb_frag_size(frag);
+ p = skb_frag_address_safe(frag);
+ seq_printf(s, " [%2d] : len = %d\n", i, len);
+ wil_seq_hexdump(s, p, len, " : ");
+ }
+ }
+}
+
+/*---------Tx/Rx descriptor------------*/
static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
- struct vring *vring = &(wil->vring_tx[0]);
+ struct vring *vring;
+ bool tx = (dbg_vring_index < WIL6210_MAX_TX_RINGS);
+ if (tx)
+ vring = &(wil->vring_tx[dbg_vring_index]);
+ else
+ vring = &wil->vring_rx;
if (!vring->va) {
- seq_printf(s, "No Tx VRING\n");
+ if (tx)
+ seq_printf(s, "No Tx[%2d] VRING\n", dbg_vring_index);
+ else
+ seq_puts(s, "No Rx VRING\n");
return 0;
}
if (dbg_txdesc_index < vring->size) {
+ /* use struct vring_tx_desc for Rx as well,
+ * only field used, .dma.length, is the same
+ */
volatile struct vring_tx_desc *d =
&(vring->va[dbg_txdesc_index].tx);
volatile u32 *u = (volatile u32 *)d;
struct sk_buff *skb = vring->ctx[dbg_txdesc_index].skb;
- seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index);
+ if (tx)
+ seq_printf(s, "Tx[%2d][%3d] = {\n", dbg_vring_index,
+ dbg_txdesc_index);
+ else
+ seq_printf(s, "Rx[%3d] = {\n", dbg_txdesc_index);
seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",
u[0], u[1], u[2], u[3]);
seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n",
@@ -416,31 +476,19 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
seq_printf(s, " SKB = %p\n", skb);
if (skb) {
- char printbuf[16 * 3 + 2];
- int i = 0;
- int len = le16_to_cpu(d->dma.length);
- void *p = skb->data;
-
- if (len != skb_headlen(skb)) {
- seq_printf(s, "!!! len: desc = %d skb = %d\n",
- len, skb_headlen(skb));
- len = min_t(int, len, skb_headlen(skb));
- }
-
- seq_printf(s, " len = %d\n", len);
-
- while (i < len) {
- int l = min(len - i, 16);
- hex_dump_to_buffer(p + i, l, 16, 1, printbuf,
- sizeof(printbuf), false);
- seq_printf(s, " : %s\n", printbuf);
- i += l;
- }
+ skb_get(skb);
+ wil_seq_print_skb(s, skb);
+ kfree_skb(skb);
}
seq_printf(s, "}\n");
} else {
- seq_printf(s, "TxDesc index (%d) >= size (%d)\n",
- dbg_txdesc_index, vring->size);
+ if (tx)
+ seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n",
+ dbg_vring_index, dbg_txdesc_index,
+ vring->size);
+ else
+ seq_printf(s, "RxDesc index (%d) >= size (%d)\n",
+ dbg_txdesc_index, vring->size);
}
return 0;
@@ -570,6 +618,69 @@ static const struct file_operations fops_temp = {
.llseek = seq_lseek,
};
+/*---------Station matrix------------*/
+static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
+{
+ int i;
+ u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size;
+ seq_printf(s, "0x%03x [", r->head_seq_num);
+ for (i = 0; i < r->buf_size; i++) {
+ if (i == index)
+ seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|');
+ else
+ seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_');
+ }
+ seq_puts(s, "]\n");
+}
+
+static int wil_sta_debugfs_show(struct seq_file *s, void *data)
+{
+ struct wil6210_priv *wil = s->private;
+ int i, tid;
+
+ for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
+ struct wil_sta_info *p = &wil->sta[i];
+ char *status = "unknown";
+ switch (p->status) {
+ case wil_sta_unused:
+ status = "unused ";
+ break;
+ case wil_sta_conn_pending:
+ status = "pending ";
+ break;
+ case wil_sta_connected:
+ status = "connected";
+ break;
+ }
+ seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status,
+ (p->data_port_open ? " data_port_open" : ""));
+
+ if (p->status == wil_sta_connected) {
+ for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
+ struct wil_tid_ampdu_rx *r = p->tid_rx[tid];
+ if (r) {
+ seq_printf(s, "[%2d] ", tid);
+ wil_print_rxtid(s, r);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int wil_sta_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wil_sta_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_sta = {
+ .open = wil_sta_seq_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
/*----------------*/
int wil6210_debugfs_init(struct wil6210_priv *wil)
{
@@ -581,9 +692,13 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox);
debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring);
- debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc);
- debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg,
+ debugfs_create_file("stations", S_IRUGO, dbg, wil, &fops_sta);
+ debugfs_create_file("desc", S_IRUGO, dbg, wil, &fops_txdesc);
+ debugfs_create_u32("desc_index", S_IRUGO | S_IWUSR, dbg,
&dbg_txdesc_index);
+ debugfs_create_u32("vring_index", S_IRUGO | S_IWUSR, dbg,
+ &dbg_vring_index);
+
debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf);
debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid);
debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg,
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 10919f95a83c..5824cd41e4ba 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -195,8 +195,12 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
wil_dbg_irq(wil, "RX done\n");
isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
- wil_dbg_txrx(wil, "NAPI schedule\n");
- napi_schedule(&wil->napi_rx);
+ if (test_bit(wil_status_reset_done, &wil->status)) {
+ wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
+ napi_schedule(&wil->napi_rx);
+ } else {
+ wil_err(wil, "Got Rx interrupt while in reset\n");
+ }
}
if (isr)
@@ -226,10 +230,15 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
wil_dbg_irq(wil, "TX done\n");
- napi_schedule(&wil->napi_tx);
isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
/* clear also all VRING interrupts */
isr &= ~(BIT(25) - 1UL);
+ if (test_bit(wil_status_reset_done, &wil->status)) {
+ wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
+ napi_schedule(&wil->napi_tx);
+ } else {
+ wil_err(wil, "Got Tx interrupt while in reset\n");
+ }
}
if (isr)
@@ -319,6 +328,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
if (isr & ISR_MISC_FW_ERROR) {
wil_notify_fw_error(wil);
isr &= ~ISR_MISC_FW_ERROR;
+ wil_fw_error_recovery(wil);
}
if (isr & ISR_MISC_MBOX_EVT) {
@@ -493,6 +503,23 @@ free0:
return rc;
}
+/* can't use wil_ioread32_and_clear because ICC value is not ser yet */
+static inline void wil_clear32(void __iomem *addr)
+{
+ u32 x = ioread32(addr);
+
+ iowrite32(x, addr);
+}
+
+void wil6210_clear_irq(struct wil6210_priv *wil)
+{
+ wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) +
+ offsetof(struct RGF_ICR, ICR));
+ wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) +
+ offsetof(struct RGF_ICR, ICR));
+ wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
+ offsetof(struct RGF_ICR, ICR));
+}
int wil6210_init_irq(struct wil6210_priv *wil, int irq)
{
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index fd30cddd5882..95f4efe9ef37 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -16,8 +16,14 @@
#include <linux/moduleparam.h>
#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
#include "wil6210.h"
+#include "txrx.h"
+
+static bool no_fw_recovery;
+module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery");
/*
* Due to a hardware issue,
@@ -52,29 +58,74 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
__raw_writel(*s++, d++);
}
-static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
+static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
{
uint i;
- struct net_device *ndev = wil_to_ndev(wil);
+ struct wil_sta_info *sta = &wil->sta[cid];
- wil_dbg_misc(wil, "%s()\n", __func__);
+ sta->data_port_open = false;
+ if (sta->status != wil_sta_unused) {
+ wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING);
+ sta->status = wil_sta_unused;
+ }
- wil_link_off(wil);
- if (test_bit(wil_status_fwconnected, &wil->status)) {
- clear_bit(wil_status_fwconnected, &wil->status);
- cfg80211_disconnected(ndev,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- NULL, 0, GFP_KERNEL);
- } else if (test_bit(wil_status_fwconnecting, &wil->status)) {
- cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- GFP_KERNEL);
+ for (i = 0; i < WIL_STA_TID_NUM; i++) {
+ struct wil_tid_ampdu_rx *r = sta->tid_rx[i];
+ sta->tid_rx[i] = NULL;
+ wil_tid_ampdu_rx_free(wil, r);
+ }
+ for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
+ if (wil->vring2cid_tid[i][0] == cid)
+ wil_vring_fini_tx(wil, i);
}
- clear_bit(wil_status_fwconnecting, &wil->status);
- for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
- wil_vring_fini_tx(wil, i);
+ memset(&sta->stats, 0, sizeof(sta->stats));
+}
- clear_bit(wil_status_dontscan, &wil->status);
+static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
+{
+ int cid = -ENOENT;
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct wireless_dev *wdev = wil->wdev;
+
+ might_sleep();
+ if (bssid) {
+ cid = wil_find_cid(wil, bssid);
+ wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid);
+ } else {
+ wil_dbg_misc(wil, "%s(all)\n", __func__);
+ }
+
+ if (cid >= 0) /* disconnect 1 peer */
+ wil_disconnect_cid(wil, cid);
+ else /* disconnect all */
+ for (cid = 0; cid < WIL6210_MAX_CID; cid++)
+ wil_disconnect_cid(wil, cid);
+
+ /* link state */
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ wil_link_off(wil);
+ if (test_bit(wil_status_fwconnected, &wil->status)) {
+ clear_bit(wil_status_fwconnected, &wil->status);
+ cfg80211_disconnected(ndev,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ NULL, 0, GFP_KERNEL);
+ } else if (test_bit(wil_status_fwconnecting, &wil->status)) {
+ cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_KERNEL);
+ }
+ clear_bit(wil_status_fwconnecting, &wil->status);
+ break;
+ default:
+ /* AP-like interface and monitor:
+ * never scan, always connected
+ */
+ if (bssid)
+ cfg80211_del_sta(ndev, bssid, GFP_KERNEL);
+ break;
+ }
}
static void wil_disconnect_worker(struct work_struct *work)
@@ -82,7 +133,9 @@ static void wil_disconnect_worker(struct work_struct *work)
struct wil6210_priv *wil = container_of(work,
struct wil6210_priv, disconnect_worker);
+ mutex_lock(&wil->mutex);
_wil6210_disconnect(wil, NULL);
+ mutex_unlock(&wil->mutex);
}
static void wil_connect_timer_fn(ulong x)
@@ -97,12 +150,55 @@ static void wil_connect_timer_fn(ulong x)
schedule_work(&wil->disconnect_worker);
}
+static void wil_fw_error_worker(struct work_struct *work)
+{
+ struct wil6210_priv *wil = container_of(work,
+ struct wil6210_priv, fw_error_worker);
+ struct wireless_dev *wdev = wil->wdev;
+
+ wil_dbg_misc(wil, "fw error worker\n");
+
+ if (no_fw_recovery)
+ return;
+
+ mutex_lock(&wil->mutex);
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_MONITOR:
+ wil_info(wil, "fw error recovery started...\n");
+ wil_reset(wil);
+
+ /* need to re-allocate Rx ring after reset */
+ wil_rx_init(wil);
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ /* recovery in these modes is done by upper layers */
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&wil->mutex);
+}
+
+static int wil_find_free_vring(struct wil6210_priv *wil)
+{
+ int i;
+ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
+ if (!wil->vring_tx[i].va)
+ return i;
+ }
+ return -EINVAL;
+}
+
static void wil_connect_worker(struct work_struct *work)
{
int rc;
struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
connect_worker);
int cid = wil->pending_connect_cid;
+ int ringid = wil_find_free_vring(wil);
if (cid < 0) {
wil_err(wil, "No connection pending\n");
@@ -111,16 +207,22 @@ static void wil_connect_worker(struct work_struct *work)
wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid);
- rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, cid, 0);
+ rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0);
wil->pending_connect_cid = -1;
- if (rc == 0)
+ if (rc == 0) {
+ wil->sta[cid].status = wil_sta_connected;
wil_link_on(wil);
+ } else {
+ wil->sta[cid].status = wil_sta_unused;
+ }
}
int wil_priv_init(struct wil6210_priv *wil)
{
wil_dbg_misc(wil, "%s()\n", __func__);
+ memset(wil->sta, 0, sizeof(wil->sta));
+
mutex_init(&wil->mutex);
mutex_init(&wil->wmi_mutex);
@@ -132,6 +234,7 @@ int wil_priv_init(struct wil6210_priv *wil)
INIT_WORK(&wil->connect_worker, wil_connect_worker);
INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
+ INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
INIT_LIST_HEAD(&wil->pending_wmi_ev);
spin_lock_init(&wil->wmi_ev_lock);
@@ -158,7 +261,10 @@ void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
void wil_priv_deinit(struct wil6210_priv *wil)
{
cancel_work_sync(&wil->disconnect_worker);
+ cancel_work_sync(&wil->fw_error_worker);
+ mutex_lock(&wil->mutex);
wil6210_disconnect(wil, NULL);
+ mutex_unlock(&wil->mutex);
wmi_event_flush(wil);
destroy_workqueue(wil->wmi_wq_conn);
destroy_workqueue(wil->wmi_wq);
@@ -166,40 +272,78 @@ void wil_priv_deinit(struct wil6210_priv *wil)
static void wil_target_reset(struct wil6210_priv *wil)
{
+ int delay = 0;
+ u32 hw_state;
+ u32 rev_id;
+
wil_dbg_misc(wil, "Resetting...\n");
+ /* register read */
+#define R(a) ioread32(wil->csr + HOSTADDR(a))
/* register write */
#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a))
/* register set = read, OR, write */
-#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \
- wil->csr + HOSTADDR(a))
+#define S(a, v) W(a, R(a) | v)
+ /* register clear = read, AND with inverted, write */
+#define C(a, v) W(a, R(a) & ~v)
+ wil->hw_version = R(RGF_USER_FW_REV_ID);
+ rev_id = wil->hw_version & 0xff;
/* hpal_perst_from_pad_src_n_mask */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));
/* car_perst_rst_src_n_mask */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7));
+ wmb(); /* order is important here */
W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */
W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */
+ wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00);
+ wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
+ wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
- W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
+ if (rev_id == 1) {
+ W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
+ } else {
+ W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
+ W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
+ }
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
+ wmb(); /* order is important here */
+
+ /* wait until device ready */
+ do {
+ msleep(1);
+ hw_state = R(RGF_USER_HW_MACHINE_STATE);
+ if (delay++ > 100) {
+ wil_err(wil, "Reset not completed, hw_state 0x%08x\n",
+ hw_state);
+ return;
+ }
+ } while (hw_state != HW_MACHINE_BOOT_DONE);
+
+ if (rev_id == 2)
+ W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8));
+
+ C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
+ wmb(); /* order is important here */
- wil_dbg_misc(wil, "Reset completed\n");
+ wil_dbg_misc(wil, "Reset completed in %d ms\n", delay);
+#undef R
#undef W
#undef S
+#undef C
}
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
@@ -234,11 +378,24 @@ int wil_reset(struct wil6210_priv *wil)
{
int rc;
+ WARN_ON(!mutex_is_locked(&wil->mutex));
+
cancel_work_sync(&wil->disconnect_worker);
wil6210_disconnect(wil, NULL);
+ wil->status = 0; /* prevent NAPI from being scheduled */
+ if (test_bit(wil_status_napi_en, &wil->status)) {
+ napi_synchronize(&wil->napi_rx);
+ }
+
+ if (wil->scan_request) {
+ wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
+ wil->scan_request);
+ cfg80211_scan_done(wil->scan_request, true);
+ wil->scan_request = NULL;
+ }
+
wil6210_disable_irq(wil);
- wil->status = 0;
wmi_event_flush(wil);
@@ -248,6 +405,8 @@ int wil_reset(struct wil6210_priv *wil)
/* TODO: put MAC in reset */
wil_target_reset(wil);
+ wil_rx_fini(wil);
+
/* init after reset */
wil->pending_connect_cid = -1;
reinit_completion(&wil->wmi_ready);
@@ -261,6 +420,11 @@ int wil_reset(struct wil6210_priv *wil)
return rc;
}
+void wil_fw_error_recovery(struct wil6210_priv *wil)
+{
+ wil_dbg_misc(wil, "starting fw error recovery\n");
+ schedule_work(&wil->fw_error_worker);
+}
void wil_link_on(struct wil6210_priv *wil)
{
@@ -288,6 +452,8 @@ static int __wil_up(struct wil6210_priv *wil)
struct wireless_dev *wdev = wil->wdev;
int rc;
+ WARN_ON(!mutex_is_locked(&wil->mutex));
+
rc = wil_reset(wil);
if (rc)
return rc;
@@ -329,6 +495,7 @@ static int __wil_up(struct wil6210_priv *wil)
napi_enable(&wil->napi_rx);
napi_enable(&wil->napi_tx);
+ set_bit(wil_status_napi_en, &wil->status);
return 0;
}
@@ -346,6 +513,9 @@ int wil_up(struct wil6210_priv *wil)
static int __wil_down(struct wil6210_priv *wil)
{
+ WARN_ON(!mutex_is_locked(&wil->mutex));
+
+ clear_bit(wil_status_napi_en, &wil->status);
napi_disable(&wil->napi_rx);
napi_disable(&wil->napi_tx);
@@ -370,3 +540,19 @@ int wil_down(struct wil6210_priv *wil)
return rc;
}
+
+int wil_find_cid(struct wil6210_priv *wil, const u8 *mac)
+{
+ int i;
+ int rc = -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
+ if ((wil->sta[i].status != wil_sta_unused) &&
+ ether_addr_equal(wil->sta[i].addr, mac)) {
+ rc = i;
+ break;
+ }
+ }
+
+ return rc;
+}
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 717178f09aa8..fdcaeb820e75 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -127,8 +127,9 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
ndev->netdev_ops = &wil_netdev_ops;
ndev->ieee80211_ptr = wdev;
- ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
- ndev->features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
+ ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_SG | NETIF_F_GRO;
+ ndev->features |= ndev->hw_features;
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index eeceab39cda2..f1e1bb338d68 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -41,39 +41,41 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
switch (use_msi) {
case 3:
case 1:
+ wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi);
+ break;
case 0:
+ wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n");
break;
default:
- wil_err(wil, "Invalid use_msi=%d, default to 1\n",
- use_msi);
+ wil_err(wil, "Invalid use_msi=%d, default to 1\n", use_msi);
use_msi = 1;
}
- wil->n_msi = use_msi;
- if (wil->n_msi) {
- wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi);
- rc = pci_enable_msi_block(pdev, wil->n_msi);
- if (rc && (wil->n_msi == 3)) {
- wil_err(wil, "3 MSI mode failed, try 1 MSI\n");
- wil->n_msi = 1;
- rc = pci_enable_msi_block(pdev, wil->n_msi);
- }
- if (rc) {
- wil_err(wil, "pci_enable_msi failed, use INTx\n");
- wil->n_msi = 0;
- }
- } else {
- wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n");
+
+ if (use_msi == 3 && pci_enable_msi_range(pdev, 3, 3) < 0) {
+ wil_err(wil, "3 MSI mode failed, try 1 MSI\n");
+ use_msi = 1;
+ }
+
+ if (use_msi == 1 && pci_enable_msi(pdev)) {
+ wil_err(wil, "pci_enable_msi failed, use INTx\n");
+ use_msi = 0;
}
+ wil->n_msi = use_msi;
+
rc = wil6210_init_irq(wil, pdev->irq);
if (rc)
goto stop_master;
/* need reset here to obtain MAC */
+ mutex_lock(&wil->mutex);
rc = wil_reset(wil);
+ mutex_unlock(&wil->mutex);
if (rc)
goto release_irq;
+ wil_info(wil, "HW version: 0x%08x\n", wil->hw_version);
+
return 0;
release_irq:
@@ -151,6 +153,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_drvdata(pdev, wil);
wil->pdev = pdev;
+ wil6210_clear_irq(wil);
/* FW should raise IRQ when ready */
rc = wil_if_pcie_enable(wil);
if (rc) {
diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c
new file mode 100644
index 000000000000..d04629fe053f
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c
@@ -0,0 +1,177 @@
+#include "wil6210.h"
+#include "txrx.h"
+
+#define SEQ_MODULO 0x1000
+#define SEQ_MASK 0xfff
+
+static inline int seq_less(u16 sq1, u16 sq2)
+{
+ return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
+}
+
+static inline u16 seq_inc(u16 sq)
+{
+ return (sq + 1) & SEQ_MASK;
+}
+
+static inline u16 seq_sub(u16 sq1, u16 sq2)
+{
+ return (sq1 - sq2) & SEQ_MASK;
+}
+
+static inline int reorder_index(struct wil_tid_ampdu_rx *r, u16 seq)
+{
+ return seq_sub(seq, r->ssn) % r->buf_size;
+}
+
+static void wil_release_reorder_frame(struct wil6210_priv *wil,
+ struct wil_tid_ampdu_rx *r,
+ int index)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct sk_buff *skb = r->reorder_buf[index];
+
+ if (!skb)
+ goto no_frame;
+
+ /* release the frame from the reorder ring buffer */
+ r->stored_mpdu_num--;
+ r->reorder_buf[index] = NULL;
+ wil_netif_rx_any(skb, ndev);
+
+no_frame:
+ r->head_seq_num = seq_inc(r->head_seq_num);
+}
+
+static void wil_release_reorder_frames(struct wil6210_priv *wil,
+ struct wil_tid_ampdu_rx *r,
+ u16 hseq)
+{
+ int index;
+
+ while (seq_less(r->head_seq_num, hseq)) {
+ index = reorder_index(r, r->head_seq_num);
+ wil_release_reorder_frame(wil, r, index);
+ }
+}
+
+static void wil_reorder_release(struct wil6210_priv *wil,
+ struct wil_tid_ampdu_rx *r)
+{
+ int index = reorder_index(r, r->head_seq_num);
+
+ while (r->reorder_buf[index]) {
+ wil_release_reorder_frame(wil, r, index);
+ index = reorder_index(r, r->head_seq_num);
+ }
+}
+
+void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct vring_rx_desc *d = wil_skb_rxdesc(skb);
+ int tid = wil_rxdesc_tid(d);
+ int cid = wil_rxdesc_cid(d);
+ int mid = wil_rxdesc_mid(d);
+ u16 seq = wil_rxdesc_seq(d);
+ struct wil_sta_info *sta = &wil->sta[cid];
+ struct wil_tid_ampdu_rx *r = sta->tid_rx[tid];
+ u16 hseq;
+ int index;
+
+ wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x\n",
+ mid, cid, tid, seq);
+
+ if (!r) {
+ wil_netif_rx_any(skb, ndev);
+ return;
+ }
+
+ hseq = r->head_seq_num;
+
+ spin_lock(&r->reorder_lock);
+
+ /* frame with out of date sequence number */
+ if (seq_less(seq, r->head_seq_num)) {
+ dev_kfree_skb(skb);
+ goto out;
+ }
+
+ /*
+ * If frame the sequence number exceeds our buffering window
+ * size release some previous frames to make room for this one.
+ */
+ if (!seq_less(seq, r->head_seq_num + r->buf_size)) {
+ hseq = seq_inc(seq_sub(seq, r->buf_size));
+ /* release stored frames up to new head to stack */
+ wil_release_reorder_frames(wil, r, hseq);
+ }
+
+ /* Now the new frame is always in the range of the reordering buffer */
+
+ index = reorder_index(r, seq);
+
+ /* check if we already stored this frame */
+ if (r->reorder_buf[index]) {
+ dev_kfree_skb(skb);
+ goto out;
+ }
+
+ /*
+ * If the current MPDU is in the right order and nothing else
+ * is stored we can process it directly, no need to buffer it.
+ * If it is first but there's something stored, we may be able
+ * to release frames after this one.
+ */
+ if (seq == r->head_seq_num && r->stored_mpdu_num == 0) {
+ r->head_seq_num = seq_inc(r->head_seq_num);
+ wil_netif_rx_any(skb, ndev);
+ goto out;
+ }
+
+ /* put the frame in the reordering buffer */
+ r->reorder_buf[index] = skb;
+ r->reorder_time[index] = jiffies;
+ r->stored_mpdu_num++;
+ wil_reorder_release(wil, r);
+
+out:
+ spin_unlock(&r->reorder_lock);
+}
+
+struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
+ int size, u16 ssn)
+{
+ struct wil_tid_ampdu_rx *r = kzalloc(sizeof(*r), GFP_KERNEL);
+ if (!r)
+ return NULL;
+
+ r->reorder_buf =
+ kcalloc(size, sizeof(struct sk_buff *), GFP_KERNEL);
+ r->reorder_time =
+ kcalloc(size, sizeof(unsigned long), GFP_KERNEL);
+ if (!r->reorder_buf || !r->reorder_time) {
+ kfree(r->reorder_buf);
+ kfree(r->reorder_time);
+ kfree(r);
+ return NULL;
+ }
+
+ spin_lock_init(&r->reorder_lock);
+ r->ssn = ssn;
+ r->head_seq_num = ssn;
+ r->buf_size = size;
+ r->stored_mpdu_num = 0;
+ return r;
+}
+
+void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
+ struct wil_tid_ampdu_rx *r)
+{
+ if (!r)
+ return;
+ wil_release_reorder_frames(wil, r, r->head_seq_num + r->buf_size);
+ 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 0b0975d88b43..c8c547457eb4 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -104,6 +104,23 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
return 0;
}
+static void wil_txdesc_unmap(struct device *dev, struct vring_tx_desc *d,
+ struct wil_ctx *ctx)
+{
+ dma_addr_t pa = wil_desc_addr(&d->dma.addr);
+ u16 dmalen = le16_to_cpu(d->dma.length);
+ switch (ctx->mapped_as) {
+ case wil_mapped_as_single:
+ dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+ break;
+ case wil_mapped_as_page:
+ dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
+ break;
+ default:
+ break;
+ }
+}
+
static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
int tx)
{
@@ -122,15 +139,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
ctx = &vring->ctx[vring->swtail];
*d = *_d;
- pa = wil_desc_addr(&d->dma.addr);
- dmalen = le16_to_cpu(d->dma.length);
- if (vring->ctx[vring->swtail].mapped_as_page) {
- dma_unmap_page(dev, pa, dmalen,
- DMA_TO_DEVICE);
- } else {
- dma_unmap_single(dev, pa, dmalen,
- DMA_TO_DEVICE);
- }
+ wil_txdesc_unmap(dev, d, ctx);
if (ctx->skb)
dev_kfree_skb_any(ctx->skb);
vring->swtail = wil_vring_next_tail(vring);
@@ -344,6 +353,9 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
u16 dmalen;
u8 ftype;
u8 ds_bits;
+ int cid;
+ struct wil_net_stats *stats;
+
BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb));
@@ -383,8 +395,10 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb_headlen(skb), false);
-
- wil->stats.last_mcs_rx = wil_rxdesc_mcs(d);
+ cid = wil_rxdesc_cid(d);
+ stats = &wil->sta[cid].stats;
+ stats->last_mcs_rx = wil_rxdesc_mcs(d);
+ wil->stats.last_mcs_rx = stats->last_mcs_rx;
/* use radiotap header only if required */
if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
@@ -472,21 +486,28 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)
* Pass Rx packet to the netif. Update statistics.
* Called in softirq context (NAPI poll).
*/
-static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
+void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
{
- int rc;
+ gro_result_t rc;
+ struct wil6210_priv *wil = ndev_to_wil(ndev);
unsigned int len = skb->len;
+ struct vring_rx_desc *d = wil_skb_rxdesc(skb);
+ int cid = wil_rxdesc_cid(d);
+ struct wil_net_stats *stats = &wil->sta[cid].stats;
skb_orphan(skb);
- rc = netif_receive_skb(skb);
+ rc = napi_gro_receive(&wil->napi_rx, skb);
- if (likely(rc == NET_RX_SUCCESS)) {
+ if (unlikely(rc == GRO_DROP)) {
+ ndev->stats.rx_dropped++;
+ stats->rx_dropped++;
+ wil_dbg_txrx(wil, "Rx drop %d bytes\n", len);
+ } else {
ndev->stats.rx_packets++;
+ stats->rx_packets++;
ndev->stats.rx_bytes += len;
-
- } else {
- ndev->stats.rx_dropped++;
+ stats->rx_bytes += len;
}
}
@@ -515,12 +536,18 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota)
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
-
+ wil_netif_rx_any(skb, ndev);
} else {
+ struct ethhdr *eth = (void *)skb->data;
+
skb->protocol = eth_type_trans(skb, ndev);
+
+ if (is_unicast_ether_addr(eth->h_dest))
+ wil_rx_reorder(wil, skb);
+ else
+ wil_netif_rx_any(skb, ndev);
}
- wil_netif_rx_any(skb, ndev);
}
wil_rx_refill(wil, v->size);
}
@@ -530,6 +557,11 @@ int wil_rx_init(struct wil6210_priv *wil)
struct vring *vring = &wil->vring_rx;
int rc;
+ if (vring->va) {
+ wil_err(wil, "Rx ring already allocated\n");
+ return -EINVAL;
+ }
+
vring->size = WIL6210_RX_RING_SIZE;
rc = wil_vring_alloc(wil, vring);
if (rc)
@@ -570,7 +602,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
.ring_size = cpu_to_le16(size),
},
.ringid = id,
- .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4),
+ .cidxtid = mk_cidxtid(cid, tid),
.encap_trans_type = WMI_VRING_ENC_TYPE_802_3,
.mac_ctrl = 0,
.to_resolution = 0,
@@ -586,6 +618,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
struct wmi_vring_cfg_done_event cmd;
} __packed reply;
struct vring *vring = &wil->vring_tx[id];
+ struct vring_tx_data *txdata = &wil->vring_tx_data[id];
if (vring->va) {
wil_err(wil, "Tx ring [%d] already allocated\n", id);
@@ -593,11 +626,15 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
goto out;
}
+ memset(txdata, 0, sizeof(*txdata));
vring->size = size;
rc = wil_vring_alloc(wil, vring);
if (rc)
goto out;
+ wil->vring2cid_tid[id][0] = cid;
+ wil->vring2cid_tid[id][1] = tid;
+
cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd),
@@ -613,6 +650,8 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
}
vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
+ txdata->enabled = 1;
+
return 0;
out_free:
wil_vring_free(wil, vring, 1);
@@ -625,23 +664,116 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
{
struct vring *vring = &wil->vring_tx[id];
+ WARN_ON(!mutex_is_locked(&wil->mutex));
+
if (!vring->va)
return;
+ /* make sure NAPI won't touch this vring */
+ wil->vring_tx_data[id].enabled = 0;
+ if (test_bit(wil_status_napi_en, &wil->status))
+ napi_synchronize(&wil->napi_tx);
+
wil_vring_free(wil, vring, 1);
}
static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
struct sk_buff *skb)
{
- struct vring *v = &wil->vring_tx[0];
+ int i;
+ struct ethhdr *eth = (void *)skb->data;
+ int cid = wil_find_cid(wil, eth->h_dest);
+
+ if (cid < 0)
+ return NULL;
- if (v->va)
- return v;
+ if (!wil->sta[cid].data_port_open &&
+ (skb->protocol != cpu_to_be16(ETH_P_PAE)))
+ return NULL;
+
+ /* TODO: fix for multiple TID */
+ for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) {
+ if (wil->vring2cid_tid[i][0] == cid) {
+ struct vring *v = &wil->vring_tx[i];
+ wil_dbg_txrx(wil, "%s(%pM) -> [%d]\n",
+ __func__, eth->h_dest, i);
+ if (v->va) {
+ return v;
+ } else {
+ wil_dbg_txrx(wil, "vring[%d] not valid\n", i);
+ return NULL;
+ }
+ }
+ }
return NULL;
}
+static void wil_set_da_for_vring(struct wil6210_priv *wil,
+ struct sk_buff *skb, int vring_index)
+{
+ struct ethhdr *eth = (void *)skb->data;
+ int cid = wil->vring2cid_tid[vring_index][0];
+ memcpy(eth->h_dest, wil->sta[cid].addr, ETH_ALEN);
+}
+
+static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
+ struct sk_buff *skb);
+/*
+ * Find 1-st vring and return it; set dest address for this vring in skb
+ * duplicate skb and send it to other active vrings
+ */
+static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
+ struct sk_buff *skb)
+{
+ struct vring *v, *v2;
+ struct sk_buff *skb2;
+ int i;
+ u8 cid;
+
+ /* find 1-st vring eligible for data */
+ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
+ v = &wil->vring_tx[i];
+ if (!v->va)
+ continue;
+
+ cid = wil->vring2cid_tid[i][0];
+ if (!wil->sta[cid].data_port_open)
+ continue;
+
+ goto found;
+ }
+
+ wil_err(wil, "Tx while no vrings active?\n");
+
+ return NULL;
+
+found:
+ wil_dbg_txrx(wil, "BCAST -> ring %d\n", i);
+ wil_set_da_for_vring(wil, skb, i);
+
+ /* find other active vrings and duplicate skb for each */
+ for (i++; i < WIL6210_MAX_TX_RINGS; i++) {
+ v2 = &wil->vring_tx[i];
+ if (!v2->va)
+ continue;
+ cid = wil->vring2cid_tid[i][0];
+ if (!wil->sta[cid].data_port_open)
+ continue;
+
+ skb2 = skb_copy(skb, GFP_ATOMIC);
+ if (skb2) {
+ wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i);
+ wil_set_da_for_vring(wil, skb2, i);
+ wil_tx_vring(wil, v2, skb2);
+ } else {
+ wil_err(wil, "skb_copy failed\n");
+ }
+ }
+
+ return v;
+}
+
static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
int vring_index)
{
@@ -667,6 +799,13 @@ static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
return 0;
}
+static inline
+void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags)
+{
+ d->mac.d[2] |= ((nr_frags + 1) <<
+ MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
+}
+
static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,
struct vring_tx_desc *d,
struct sk_buff *skb)
@@ -731,8 +870,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_dbg_txrx(wil, "%s()\n", __func__);
- if (avail < vring->size/8)
- netif_tx_stop_all_queues(wil_to_ndev(wil));
if (avail < 1 + nr_frags) {
wil_err(wil, "Tx ring full. No space for %d fragments\n",
1 + nr_frags);
@@ -740,9 +877,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
}
_d = &(vring->va[i].tx);
- /* FIXME FW can accept only unicast frames for the peer */
- memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN);
-
pa = dma_map_single(dev, skb->data,
skb_headlen(skb), DMA_TO_DEVICE);
@@ -753,6 +887,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
if (unlikely(dma_mapping_error(dev, pa)))
return -EINVAL;
+ vring->ctx[i].mapped_as = wil_mapped_as_single;
/* 1-st segment */
wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);
/* Process TCP/UDP checksum offloading */
@@ -762,8 +897,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
goto dma_error;
}
- d->mac.d[2] |= ((nr_frags + 1) <<
- MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
+ vring->ctx[i].nr_frags = nr_frags;
+ wil_tx_desc_set_nr_frags(d, nr_frags);
if (nr_frags)
*_d = *d;
@@ -778,8 +913,13 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error;
+ vring->ctx[i].mapped_as = wil_mapped_as_page;
wil_tx_desc_map(d, pa, len, vring_index);
- vring->ctx[i].mapped_as_page = 1;
+ /* no need to check return code -
+ * if it succeeded for 1-st descriptor,
+ * it will succeed here too
+ */
+ wil_tx_desc_offload_cksum_set(wil, d, skb);
*_d = *d;
}
/* for the last seg only */
@@ -808,7 +948,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* unmap what we have mapped */
nr_frags = f + 1; /* frags mapped + one for skb head */
for (f = 0; f < nr_frags; f++) {
- u16 dmalen;
struct wil_ctx *ctx;
i = (swhead + f) % vring->size;
@@ -816,12 +955,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
_d = &(vring->va[i].tx);
*d = *_d;
_d->dma.status = TX_DMA_STATUS_DU;
- pa = wil_desc_addr(&d->dma.addr);
- dmalen = le16_to_cpu(d->dma.length);
- if (ctx->mapped_as_page)
- dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
- else
- dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+ wil_txdesc_unmap(dev, d, ctx);
if (ctx->skb)
dev_kfree_skb_any(ctx->skb);
@@ -836,12 +970,17 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
+ struct ethhdr *eth = (void *)skb->data;
struct vring *vring;
+ static bool pr_once_fw;
int rc;
wil_dbg_txrx(wil, "%s()\n", __func__);
if (!test_bit(wil_status_fwready, &wil->status)) {
- wil_err(wil, "FW not ready\n");
+ if (!pr_once_fw) {
+ wil_err(wil, "FW not ready\n");
+ pr_once_fw = true;
+ }
goto drop;
}
if (!test_bit(wil_status_fwconnected, &wil->status)) {
@@ -852,16 +991,25 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
wil_err(wil, "Xmit in monitor mode not supported\n");
goto drop;
}
+ pr_once_fw = false;
/* find vring */
- vring = wil_find_tx_vring(wil, skb);
+ if (is_unicast_ether_addr(eth->h_dest)) {
+ vring = wil_find_tx_vring(wil, skb);
+ } else {
+ vring = wil_tx_bcast(wil, skb);
+ }
if (!vring) {
- wil_err(wil, "No Tx VRING available\n");
+ wil_err(wil, "No Tx VRING found for %pM\n", eth->h_dest);
goto drop;
}
/* set up vring entry */
rc = wil_tx_vring(wil, vring, skb);
+ /* do we still have enough room in the vring? */
+ if (wil_vring_avail_tx(vring) < vring->size/8)
+ netif_tx_stop_all_queues(wil_to_ndev(wil));
+
switch (rc) {
case 0:
/* statistics will be updated on the tx_complete */
@@ -891,64 +1039,82 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
struct net_device *ndev = wil_to_ndev(wil);
struct device *dev = wil_to_dev(wil);
struct vring *vring = &wil->vring_tx[ringid];
+ struct vring_tx_data *txdata = &wil->vring_tx_data[ringid];
int done = 0;
+ int cid = wil->vring2cid_tid[ringid][0];
+ struct wil_net_stats *stats = &wil->sta[cid].stats;
+ volatile struct vring_tx_desc *_d;
if (!vring->va) {
wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
return 0;
}
+ if (!txdata->enabled) {
+ wil_info(wil, "Tx irq[%d]: vring disabled\n", ringid);
+ return 0;
+ }
+
wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
while (!wil_vring_is_empty(vring)) {
- volatile struct vring_tx_desc *_d =
- &vring->va[vring->swtail].tx;
- struct vring_tx_desc dd, *d = &dd;
- dma_addr_t pa;
- u16 dmalen;
+ int new_swtail;
struct wil_ctx *ctx = &vring->ctx[vring->swtail];
- struct sk_buff *skb = ctx->skb;
-
- *d = *_d;
+ /**
+ * For the fragmented skb, HW will set DU bit only for the
+ * last fragment. look for it
+ */
+ int lf = (vring->swtail + ctx->nr_frags) % vring->size;
+ /* TODO: check we are not past head */
- if (!(d->dma.status & TX_DMA_STATUS_DU))
+ _d = &vring->va[lf].tx;
+ if (!(_d->dma.status & TX_DMA_STATUS_DU))
break;
- dmalen = le16_to_cpu(d->dma.length);
- trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
- d->dma.error);
- wil_dbg_txrx(wil,
- "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
- vring->swtail, dmalen, d->dma.status,
- d->dma.error);
- wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
- (const void *)d, sizeof(*d), false);
-
- pa = wil_desc_addr(&d->dma.addr);
- if (ctx->mapped_as_page)
- dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
- else
- dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
-
- if (skb) {
- if (d->dma.error == 0) {
- ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += skb->len;
- } else {
- ndev->stats.tx_errors++;
- }
+ new_swtail = (lf + 1) % vring->size;
+ while (vring->swtail != new_swtail) {
+ struct vring_tx_desc dd, *d = &dd;
+ u16 dmalen;
+ struct wil_ctx *ctx = &vring->ctx[vring->swtail];
+ struct sk_buff *skb = ctx->skb;
+ _d = &vring->va[vring->swtail].tx;
+
+ *d = *_d;
+
+ dmalen = le16_to_cpu(d->dma.length);
+ trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
+ d->dma.error);
+ wil_dbg_txrx(wil,
+ "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
+ vring->swtail, dmalen, d->dma.status,
+ d->dma.error);
+ wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
+ (const void *)d, sizeof(*d), false);
+
+ wil_txdesc_unmap(dev, d, ctx);
+
+ if (skb) {
+ if (d->dma.error == 0) {
+ ndev->stats.tx_packets++;
+ stats->tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+ stats->tx_bytes += skb->len;
+ } else {
+ ndev->stats.tx_errors++;
+ stats->tx_errors++;
+ }
- dev_kfree_skb_any(skb);
+ dev_kfree_skb_any(skb);
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ /* There is no need to touch HW descriptor:
+ * - ststus bit TX_DMA_STATUS_DU is set by design,
+ * so hardware will not try to process this desc.,
+ * - rest of descriptor will be initialized on Tx.
+ */
+ vring->swtail = wil_vring_next_tail(vring);
+ done++;
}
- memset(ctx, 0, sizeof(*ctx));
- /*
- * There is no need to touch HW descriptor:
- * - ststus bit TX_DMA_STATUS_DU is set by design,
- * so hardware will not try to process this desc.,
- * - rest of descriptor will be initialized on Tx.
- */
- vring->swtail = wil_vring_next_tail(vring);
- done++;
}
if (wil_vring_avail_tx(vring) > vring->size/4)
netif_tx_wake_all_queues(wil_to_ndev(wil));
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index b3828279204c..bc5706a4f007 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -436,4 +436,11 @@ static inline struct vring_rx_desc *wil_skb_rxdesc(struct sk_buff *skb)
return (void *)skb->cb;
}
+void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev);
+void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb);
+struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
+ int size, u16 ssn);
+void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
+ struct wil_tid_ampdu_rx *r);
+
#endif /* WIL6210_TXRX_H */
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 1f91eaf95bbe..2a2dec75f026 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -74,23 +74,21 @@ struct RGF_ICR {
} __packed;
/* registers - FW addresses */
-#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
-#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */
- #define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
-#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14)
-#define RGF_USER_MAC_CPU_0 (0x8801fc)
+#define RGF_USER_HW_MACHINE_STATE (0x8801dc)
+ #define HW_MACHINE_BOOT_DONE (0x3fffffd)
#define RGF_USER_USER_CPU_0 (0x8801e0)
+#define RGF_USER_MAC_CPU_0 (0x8801fc)
+#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
+#define RGF_USER_FW_REV_ID (0x880a8c) /* chip revision */
+#define RGF_USER_CLKS_CTL_0 (0x880abc)
+ #define BIT_USER_CLKS_RST_PWGD BIT(11) /* reset on "power good" */
#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10)
-
-#define RGF_DMA_PSEUDO_CAUSE (0x881c68)
-#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c)
-#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70)
- #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0)
- #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1)
- #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2)
+#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14)
+#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */
+ #define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */
#define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0)
@@ -105,13 +103,22 @@ struct RGF_ICR {
/* Interrupt moderation control */
#define RGF_DMA_ITR_CNT_TRSH (0x881c5c)
#define RGF_DMA_ITR_CNT_DATA (0x881c60)
-#define RGF_DMA_ITR_CNT_CRL (0x881C64)
+#define RGF_DMA_ITR_CNT_CRL (0x881c64)
#define BIT_DMA_ITR_CNT_CRL_EN BIT(0)
#define BIT_DMA_ITR_CNT_CRL_EXT_TICK BIT(1)
#define BIT_DMA_ITR_CNT_CRL_FOREVER BIT(2)
#define BIT_DMA_ITR_CNT_CRL_CLR BIT(3)
#define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4)
+#define RGF_DMA_PSEUDO_CAUSE (0x881c68)
+#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c)
+#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70)
+ #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0)
+ #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1)
+ #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2)
+
+#define RGF_PCIE_LOS_COUNTER_CTL (0x882dc4)
+
/* popular locations */
#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD)
#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \
@@ -125,6 +132,31 @@ struct RGF_ICR {
/* Hardware definitions end */
+/**
+ * mk_cidxtid - construct @cidxtid field
+ * @cid: CID value
+ * @tid: TID value
+ *
+ * @cidxtid field encoded as bits 0..3 - CID; 4..7 - TID
+ */
+static inline u8 mk_cidxtid(u8 cid, u8 tid)
+{
+ return ((tid & 0xf) << 4) | (cid & 0xf);
+}
+
+/**
+ * parse_cidxtid - parse @cidxtid field
+ * @cid: store CID value here
+ * @tid: store TID value here
+ *
+ * @cidxtid field encoded as bits 0..3 - CID; 4..7 - TID
+ */
+static inline void parse_cidxtid(u8 cidxtid, u8 *cid, u8 *tid)
+{
+ *cid = cidxtid & 0xf;
+ *tid = (cidxtid >> 4) & 0xf;
+}
+
struct wil6210_mbox_ring {
u32 base;
u16 entry_size; /* max. size of mbox entry, incl. all headers */
@@ -184,12 +216,19 @@ struct pending_wmi_event {
} __packed event;
};
+enum { /* for wil_ctx.mapped_as */
+ wil_mapped_as_none = 0,
+ wil_mapped_as_single = 1,
+ wil_mapped_as_page = 2,
+};
+
/**
* struct wil_ctx - software context for Vring descriptor
*/
struct wil_ctx {
struct sk_buff *skb;
- u8 mapped_as_page:1;
+ u8 nr_frags;
+ u8 mapped_as;
};
union vring_desc;
@@ -204,6 +243,14 @@ struct vring {
struct wil_ctx *ctx; /* ctx[size] - software context */
};
+/**
+ * Additional data for Tx Vring
+ */
+struct vring_tx_data {
+ int enabled;
+
+};
+
enum { /* for wil6210_priv.status */
wil_status_fwready = 0,
wil_status_fwconnecting,
@@ -211,10 +258,51 @@ enum { /* for wil6210_priv.status */
wil_status_dontscan,
wil_status_reset_done,
wil_status_irqen, /* FIXME: interrupts enabled - for debug */
+ wil_status_napi_en, /* NAPI enabled protected by wil->mutex */
};
struct pci_dev;
+/**
+ * struct tid_ampdu_rx - TID aggregation information (Rx).
+ *
+ * @reorder_buf: buffer to reorder incoming aggregated MPDUs
+ * @reorder_time: jiffies when skb was added
+ * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
+ * @reorder_timer: releases expired frames from the reorder buffer.
+ * @last_rx: jiffies of last rx activity
+ * @head_seq_num: head sequence number in reordering buffer.
+ * @stored_mpdu_num: number of MPDUs in reordering buffer
+ * @ssn: Starting Sequence Number expected to be aggregated.
+ * @buf_size: buffer size for incoming A-MPDUs
+ * @timeout: reset timer value (in TUs).
+ * @dialog_token: dialog token for aggregation session
+ * @rcu_head: RCU head used for freeing this struct
+ * @reorder_lock: serializes access to reorder buffer, see below.
+ *
+ * This structure's lifetime is managed by RCU, assignments to
+ * the array holding it must hold the aggregation mutex.
+ *
+ * The @reorder_lock is used to protect the members of this
+ * struct, except for @timeout, @buf_size and @dialog_token,
+ * which are constant across the lifetime of the struct (the
+ * dialog token being used only for debugging).
+ */
+struct wil_tid_ampdu_rx {
+ spinlock_t reorder_lock; /* see above */
+ struct sk_buff **reorder_buf;
+ unsigned long *reorder_time;
+ struct timer_list session_timer;
+ struct timer_list reorder_timer;
+ unsigned long last_rx;
+ u16 head_seq_num;
+ u16 stored_mpdu_num;
+ u16 ssn;
+ u16 buf_size;
+ u16 timeout;
+ u8 dialog_token;
+};
+
struct wil6210_stats {
u64 tsf;
u32 snr;
@@ -226,6 +314,43 @@ struct wil6210_stats {
u16 peer_tx_sector;
};
+enum wil_sta_status {
+ wil_sta_unused = 0,
+ wil_sta_conn_pending = 1,
+ wil_sta_connected = 2,
+};
+
+#define WIL_STA_TID_NUM (16)
+
+struct wil_net_stats {
+ unsigned long rx_packets;
+ unsigned long tx_packets;
+ unsigned long rx_bytes;
+ unsigned long tx_bytes;
+ unsigned long tx_errors;
+ unsigned long rx_dropped;
+ u16 last_mcs_rx;
+};
+
+/**
+ * struct wil_sta_info - data for peer
+ *
+ * Peer identified by its CID (connection ID)
+ * NIC performs beam forming for each peer;
+ * if no beam forming done, frame exchange is not
+ * possible.
+ */
+struct wil_sta_info {
+ u8 addr[ETH_ALEN];
+ enum wil_sta_status status;
+ struct wil_net_stats stats;
+ bool data_port_open; /* can send any data, not only EAPOL */
+ /* Rx BACK */
+ struct wil_tid_ampdu_rx *tid_rx[WIL_STA_TID_NUM];
+ unsigned long tid_rx_timer_expired[BITS_TO_LONGS(WIL_STA_TID_NUM)];
+ unsigned long tid_rx_stop_requested[BITS_TO_LONGS(WIL_STA_TID_NUM)];
+};
+
struct wil6210_priv {
struct pci_dev *pdev;
int n_msi;
@@ -233,6 +358,7 @@ struct wil6210_priv {
void __iomem *csr;
ulong status;
u32 fw_version;
+ u32 hw_version;
u8 n_mids; /* number of additional MIDs as reported by FW */
/* profile */
u32 monitor_flags;
@@ -253,6 +379,7 @@ struct wil6210_priv {
struct workqueue_struct *wmi_wq_conn; /* for connect worker */
struct work_struct connect_worker;
struct work_struct disconnect_worker;
+ struct work_struct fw_error_worker; /* for FW error recovery */
struct timer_list connect_timer;
int pending_connect_cid;
struct list_head pending_wmi_ev;
@@ -267,7 +394,9 @@ struct wil6210_priv {
/* DMA related */
struct vring vring_rx;
struct vring vring_tx[WIL6210_MAX_TX_RINGS];
- u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN];
+ struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS];
+ u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
+ struct wil_sta_info sta[WIL6210_MAX_CID];
/* scan */
struct cfg80211_scan_request *scan_request;
@@ -329,11 +458,13 @@ void wil_if_remove(struct wil6210_priv *wil);
int wil_priv_init(struct wil6210_priv *wil);
void wil_priv_deinit(struct wil6210_priv *wil);
int wil_reset(struct wil6210_priv *wil);
+void wil_fw_error_recovery(struct wil6210_priv *wil);
void wil_link_on(struct wil6210_priv *wil);
void wil_link_off(struct wil6210_priv *wil);
int wil_up(struct wil6210_priv *wil);
int wil_down(struct wil6210_priv *wil);
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
+int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
@@ -357,8 +488,11 @@ int wmi_echo(struct wil6210_priv *wil);
int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie);
int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring);
int wmi_p2p_cfg(struct wil6210_priv *wil, int channel);
+int wmi_rxon(struct wil6210_priv *wil, bool on);
int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r);
+int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason);
+void wil6210_clear_irq(struct wil6210_priv *wil);
int wil6210_init_irq(struct wil6210_priv *wil, int irq);
void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
void wil6210_disable_irq(struct wil6210_priv *wil);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 063963ee422a..2ba56eef0c45 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -307,14 +307,14 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
u32 freq = ieee80211_channel_to_frequency(ch_no,
IEEE80211_BAND_60GHZ);
struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq);
- /* TODO convert LE to CPU */
- s32 signal = 0; /* TODO */
+ s32 signal = data->info.sqi;
__le16 fc = rx_mgmt_frame->frame_control;
u32 d_len = le32_to_cpu(data->info.len);
u16 d_status = le16_to_cpu(data->info.status);
- wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d\n",
- data->info.channel, data->info.mcs, data->info.snr);
+ wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d SQI %d%%\n",
+ data->info.channel, data->info.mcs, data->info.snr,
+ data->info.sqi);
wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len,
le16_to_cpu(fc));
wil_dbg_wmi(wil, "qid %d mid %d cid %d\n",
@@ -384,6 +384,11 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
evt->assoc_req_len, evt->assoc_resp_len);
return;
}
+ if (evt->cid >= WIL6210_MAX_CID) {
+ wil_err(wil, "Connect CID invalid : %d\n", evt->cid);
+ return;
+ }
+
ch = evt->channel + 1;
wil_dbg_wmi(wil, "Connect %pM channel [%d] cid %d\n",
evt->bssid, ch, evt->cid);
@@ -439,7 +444,8 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
/* FIXME FW can transmit only ucast frames to peer */
/* FIXME real ring_id instead of hard coded 0 */
- memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN);
+ memcpy(wil->sta[evt->cid].addr, evt->bssid, ETH_ALEN);
+ wil->sta[evt->cid].status = wil_sta_conn_pending;
wil->pending_connect_cid = evt->cid;
queue_work(wil->wmi_wq_conn, &wil->connect_worker);
@@ -456,7 +462,9 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
wil->sinfo_gen++;
+ mutex_lock(&wil->mutex);
wil6210_disconnect(wil, evt->bssid);
+ mutex_unlock(&wil->mutex);
}
static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
@@ -476,11 +484,11 @@ static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector);
wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector);
wil_dbg_wmi(wil, "Link status, MCS %d TSF 0x%016llx\n"
- "BF status 0x%08x SNR 0x%08x\n"
+ "BF status 0x%08x SNR 0x%08x SQI %d%%\n"
"Tx Tpt %d goodput %d Rx goodput %d\n"
"Sectors(rx:tx) my %d:%d peer %d:%d\n",
wil->stats.bf_mcs, wil->stats.tsf, evt->status,
- wil->stats.snr, le32_to_cpu(evt->tx_tpt),
+ wil->stats.snr, evt->sqi, le32_to_cpu(evt->tx_tpt),
le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput),
wil->stats.my_rx_sector, wil->stats.my_tx_sector,
wil->stats.peer_rx_sector, wil->stats.peer_tx_sector);
@@ -499,10 +507,16 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
int sz = eapol_len + ETH_HLEN;
struct sk_buff *skb;
struct ethhdr *eth;
+ int cid;
+ struct wil_net_stats *stats = NULL;
wil_dbg_wmi(wil, "EAPOL len %d from %pM\n", eapol_len,
evt->src_mac);
+ cid = wil_find_cid(wil, evt->src_mac);
+ if (cid >= 0)
+ stats = &wil->sta[cid].stats;
+
if (eapol_len > 196) { /* TODO: revisit size limit */
wil_err(wil, "EAPOL too large\n");
return;
@@ -513,6 +527,7 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
wil_err(wil, "Failed to allocate skb\n");
return;
}
+
eth = (struct ethhdr *)skb_put(skb, ETH_HLEN);
memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN);
memcpy(eth->h_source, evt->src_mac, ETH_ALEN);
@@ -521,9 +536,15 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
skb->protocol = eth_type_trans(skb, ndev);
if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) {
ndev->stats.rx_packets++;
- ndev->stats.rx_bytes += skb->len;
+ ndev->stats.rx_bytes += sz;
+ if (stats) {
+ stats->rx_packets++;
+ stats->rx_bytes += sz;
+ }
} else {
ndev->stats.rx_dropped++;
+ if (stats)
+ stats->rx_dropped++;
}
}
@@ -531,9 +552,16 @@ static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wmi_data_port_open_event *evt = d;
+ u8 cid = evt->cid;
- wil_dbg_wmi(wil, "Link UP for CID %d\n", evt->cid);
+ wil_dbg_wmi(wil, "Link UP for CID %d\n", cid);
+ if (cid >= ARRAY_SIZE(wil->sta)) {
+ wil_err(wil, "Link UP for invalid CID %d\n", cid);
+ return;
+ }
+
+ wil->sta[cid].data_port_open = true;
netif_carrier_on(ndev);
}
@@ -541,10 +569,17 @@ static void wmi_evt_linkdown(struct wil6210_priv *wil, int id, void *d, int len)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wmi_wbe_link_down_event *evt = d;
+ u8 cid = evt->cid;
wil_dbg_wmi(wil, "Link DOWN for CID %d, reason %d\n",
- evt->cid, le32_to_cpu(evt->reason));
+ cid, le32_to_cpu(evt->reason));
+
+ if (cid >= ARRAY_SIZE(wil->sta)) {
+ wil_err(wil, "Link DOWN for invalid CID %d\n", cid);
+ return;
+ }
+ wil->sta[cid].data_port_open = false;
netif_carrier_off(ndev);
}
@@ -552,10 +587,42 @@ static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d,
int len)
{
struct wmi_vring_ba_status_event *evt = d;
+ struct wil_sta_info *sta;
+ uint i, cid;
+
+ /* TODO: use Rx BA status, not Tx one */
wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d\n",
- evt->ringid, evt->status ? "N/A" : "OK", evt->agg_wsize,
- __le16_to_cpu(evt->ba_timeout));
+ evt->ringid,
+ evt->status == WMI_BA_AGREED ? "OK" : "N/A",
+ evt->agg_wsize, __le16_to_cpu(evt->ba_timeout));
+
+ if (evt->ringid >= WIL6210_MAX_TX_RINGS) {
+ wil_err(wil, "invalid ring id %d\n", evt->ringid);
+ return;
+ }
+
+ cid = wil->vring2cid_tid[evt->ringid][0];
+ if (cid >= WIL6210_MAX_CID) {
+ wil_err(wil, "invalid CID %d for vring %d\n", cid, evt->ringid);
+ return;
+ }
+
+ sta = &wil->sta[cid];
+ if (sta->status == wil_sta_unused) {
+ wil_err(wil, "CID %d unused\n", cid);
+ return;
+ }
+
+ wil_dbg_wmi(wil, "BACK for CID %d %pM\n", cid, sta->addr);
+ for (i = 0; i < WIL_STA_TID_NUM; i++) {
+ struct wil_tid_ampdu_rx *r = sta->tid_rx[i];
+ sta->tid_rx[i] = NULL;
+ wil_tid_ampdu_rx_free(wil, r);
+ if ((evt->status == WMI_BA_AGREED) && evt->agg_wsize)
+ sta->tid_rx[i] = wil_tid_ampdu_rx_alloc(wil,
+ evt->agg_wsize, 0);
+ }
}
static const struct {
@@ -893,6 +960,38 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
return rc;
}
+/**
+ * wmi_rxon - turn radio on/off
+ * @on: turn on if true, off otherwise
+ *
+ * Only switch radio. Channel should be set separately.
+ * No timeout for rxon - radio turned on forever unless some other call
+ * turns it off
+ */
+int wmi_rxon(struct wil6210_priv *wil, bool on)
+{
+ int rc;
+ struct {
+ struct wil6210_mbox_hdr_wmi wmi;
+ struct wmi_listen_started_event evt;
+ } __packed reply;
+
+ wil_info(wil, "%s(%s)\n", __func__, on ? "on" : "off");
+
+ if (on) {
+ rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0,
+ WMI_LISTEN_STARTED_EVENTID,
+ &reply, sizeof(reply), 100);
+ if ((rc == 0) && (reply.evt.status != WMI_FW_STATUS_SUCCESS))
+ rc = -EINVAL;
+ } else {
+ rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0,
+ WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 20);
+ }
+
+ return rc;
+}
+
int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
{
struct wireless_dev *wdev = wil->wdev;
@@ -906,6 +1005,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
},
.mid = 0, /* TODO - what is it? */
.decap_trans_type = WMI_DECAP_TYPE_802_3,
+ .reorder_type = WMI_RX_SW_REORDER,
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
@@ -973,6 +1073,18 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r)
return 0;
}
+int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason)
+{
+ struct wmi_disconnect_sta_cmd cmd = {
+ .disconnect_reason = cpu_to_le16(reason),
+ };
+ memcpy(cmd.dst_mac, mac, ETH_ALEN);
+
+ wil_dbg_wmi(wil, "%s(%pM, reason %d)\n", __func__, mac, reason);
+
+ return wmi_send(wil, WMI_DISCONNECT_STA_CMDID, &cmd, sizeof(cmd));
+}
+
void wmi_event_flush(struct wil6210_priv *wil)
{
struct pending_wmi_event *evt, *t;
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index bf93ea859f2d..1fe41af81a59 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -67,7 +67,7 @@
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#include <linux/jiffies.h>
-#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
#include "atmel.h"
#define DRIVER_MAJOR 0
@@ -2273,7 +2273,7 @@ static int atmel_set_freq(struct net_device *dev,
/* Hack to fall through... */
fwrq->e = 0;
- fwrq->m = ieee80211_freq_to_dsss_chan(f);
+ fwrq->m = ieee80211_frequency_to_channel(f);
}
/* Setting by channel number */
if ((fwrq->m > 1000) || (fwrq->e > 0))
@@ -2434,8 +2434,8 @@ static int atmel_get_range(struct net_device *dev,
range->freq[k].i = i; /* List index */
/* Values in MHz -> * 10^5 * 10 */
- range->freq[k].m = (ieee80211_dsss_chan_to_freq(i) *
- 100000);
+ range->freq[k].m = 100000 *
+ ieee80211_channel_to_frequency(i, IEEE80211_BAND_2GHZ);
range->freq[k++].e = 1;
}
range->num_frequency = k;
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig
index 51ff0b198d0a..088d544ec63f 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/b43/Kconfig
@@ -92,7 +92,7 @@ config B43_SDIO
# if we can do DMA.
config B43_BCMA_PIO
bool
- depends on B43_BCMA
+ depends on B43 && B43_BCMA
select BCMA_BLOCKIO
default y
diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/b43/debugfs.h
index 822aad8842f4..50517b801cb4 100644
--- a/drivers/net/wireless/b43/debugfs.h
+++ b/drivers/net/wireless/b43/debugfs.h
@@ -86,7 +86,7 @@ void b43_debugfs_log_txstat(struct b43_wldev *dev,
static inline bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature)
{
- return 0;
+ return false;
}
static inline void b43_debugfs_init(void)
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index c75237eb55a1..69fc3d65531a 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -1549,7 +1549,7 @@ static void b43_write_beacon_template(struct b43_wldev *dev,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon);
bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data);
- len = min((size_t) dev->wl->current_beacon->len,
+ len = min_t(size_t, dev->wl->current_beacon->len,
0x200 - sizeof(struct b43_plcp_hdr6));
rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value;
diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h
index abac25ee958d..f476fc337d64 100644
--- a/drivers/net/wireless/b43/main.h
+++ b/drivers/net/wireless/b43/main.h
@@ -58,41 +58,6 @@ enum b43_verbosity {
#endif
};
-
-/* Lightweight function to convert a frequency (in Mhz) to a channel number. */
-static inline u8 b43_freq_to_channel_5ghz(int freq)
-{
- return ((freq - 5000) / 5);
-}
-static inline u8 b43_freq_to_channel_2ghz(int freq)
-{
- u8 channel;
-
- if (freq == 2484)
- channel = 14;
- else
- channel = (freq - 2407) / 5;
-
- return channel;
-}
-
-/* Lightweight function to convert a channel number to a frequency (in Mhz). */
-static inline int b43_channel_to_freq_5ghz(u8 channel)
-{
- return (5000 + (5 * channel));
-}
-static inline int b43_channel_to_freq_2ghz(u8 channel)
-{
- int freq;
-
- if (channel == 14)
- freq = 2484;
- else
- freq = 2407 + (5 * channel);
-
- return freq;
-}
-
static inline int b43_is_cck_rate(int rate)
{
return (rate == B43_CCK_RATE_1MB ||
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c
index f01676ac481b..dbaa51890198 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/b43/phy_common.c
@@ -133,9 +133,9 @@ void b43_phy_exit(struct b43_wldev *dev)
bool b43_has_hardware_pctl(struct b43_wldev *dev)
{
if (!dev->phy.hardware_power_control)
- return 0;
+ return false;
if (!dev->phy.ops->supports_hwpctl)
- return 0;
+ return false;
return dev->phy.ops->supports_hwpctl(dev);
}
diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c
index a73ff8c9deb5..a4ff5e2a42b9 100644
--- a/drivers/net/wireless/b43/pio.c
+++ b/drivers/net/wireless/b43/pio.c
@@ -637,7 +637,7 @@ static bool pio_rx_frame(struct b43_pio_rxqueue *q)
ctl = b43_piorx_read32(q, B43_PIO8_RXCTL);
if (!(ctl & B43_PIO8_RXCTL_FRAMERDY))
- return 0;
+ return false;
b43_piorx_write32(q, B43_PIO8_RXCTL,
B43_PIO8_RXCTL_FRAMERDY);
for (i = 0; i < 10; i++) {
@@ -651,7 +651,7 @@ static bool pio_rx_frame(struct b43_pio_rxqueue *q)
ctl = b43_piorx_read16(q, B43_PIO_RXCTL);
if (!(ctl & B43_PIO_RXCTL_FRAMERDY))
- return 0;
+ return false;
b43_piorx_write16(q, B43_PIO_RXCTL,
B43_PIO_RXCTL_FRAMERDY);
for (i = 0; i < 10; i++) {
@@ -662,7 +662,7 @@ static bool pio_rx_frame(struct b43_pio_rxqueue *q)
}
}
b43dbg(q->dev->wl, "PIO RX timed out\n");
- return 1;
+ return true;
data_ready:
/* Get the preamble (RX header) */
@@ -759,7 +759,7 @@ data_ready:
b43_rx(q->dev, skb, rxhdr);
- return 1;
+ return true;
rx_error:
if (err_msg)
@@ -769,7 +769,7 @@ rx_error:
else
b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_DATARDY);
- return 1;
+ return true;
}
void b43_pio_rx(struct b43_pio_rxqueue *q)
diff --git a/drivers/net/wireless/b43/sysfs.c b/drivers/net/wireless/b43/sysfs.c
index 8e8431d4eb0c..3190493bd07f 100644
--- a/drivers/net/wireless/b43/sysfs.c
+++ b/drivers/net/wireless/b43/sysfs.c
@@ -40,7 +40,7 @@ static int get_integer(const char *buf, size_t count)
if (count == 0)
goto out;
- count = min(count, (size_t) 10);
+ count = min_t(size_t, count, 10);
memcpy(tmp, buf, count);
ret = simple_strtol(tmp, NULL, 10);
out:
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c
index 50e5ddb12fb3..31adb8cf0291 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/b43/xmit.c
@@ -337,7 +337,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
/* iv16 */
memcpy(txhdr->iv + 10, ((u8 *) wlhdr) + wlhdr_len, 3);
} else {
- iv_len = min((size_t) info->control.hw_key->iv_len,
+ iv_len = min_t(size_t, info->control.hw_key->iv_len,
ARRAY_SIZE(txhdr->iv));
memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len);
}
@@ -806,7 +806,8 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
B43_WARN_ON(1);
/* FIXME: We don't really know which value the "chanid" contains.
* So the following assignment might be wrong. */
- status.freq = b43_channel_to_freq_5ghz(chanid);
+ status.freq =
+ ieee80211_channel_to_frequency(chanid, status.band);
break;
case B43_PHYTYPE_G:
status.band = IEEE80211_BAND_2GHZ;
@@ -819,13 +820,12 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
case B43_PHYTYPE_HT:
/* chanid is the SHM channel cookie. Which is the plain
* channel number in b43. */
- if (chanstat & B43_RX_CHAN_5GHZ) {
+ if (chanstat & B43_RX_CHAN_5GHZ)
status.band = IEEE80211_BAND_5GHZ;
- status.freq = b43_channel_to_freq_5ghz(chanid);
- } else {
+ else
status.band = IEEE80211_BAND_2GHZ;
- status.freq = b43_channel_to_freq_2ghz(chanid);
- }
+ status.freq =
+ ieee80211_channel_to_frequency(chanid, status.band);
break;
default:
B43_WARN_ON(1);
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index 349c77605231..1aec2146a2bf 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -978,7 +978,7 @@ static void b43legacy_write_beacon_template(struct b43legacy_wldev *dev,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon);
bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data);
- len = min((size_t)dev->wl->current_beacon->len,
+ len = min_t(size_t, dev->wl->current_beacon->len,
0x200 - sizeof(struct b43legacy_plcp_hdr6));
rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value;
@@ -1155,7 +1155,7 @@ static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev,
b43legacy_write_probe_resp_plcp(dev, 0x350, size,
&b43legacy_b_ratetable[3]);
- size = min((size_t)size,
+ size = min_t(size_t, size,
0x200 - sizeof(struct b43legacy_plcp_hdr6));
b43legacy_write_template_common(dev, probe_resp_data,
size, ram_offset,
diff --git a/drivers/net/wireless/b43legacy/sysfs.c b/drivers/net/wireless/b43legacy/sysfs.c
index 57f8b089767c..2a1da15c913b 100644
--- a/drivers/net/wireless/b43legacy/sysfs.c
+++ b/drivers/net/wireless/b43legacy/sysfs.c
@@ -42,7 +42,7 @@ static int get_integer(const char *buf, size_t count)
if (count == 0)
goto out;
- count = min(count, (size_t)10);
+ count = min_t(size_t, count, 10);
memcpy(tmp, buf, count);
ret = simple_strtol(tmp, NULL, 10);
out:
diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c
index 86588c9ff0f2..34bf3f0b729f 100644
--- a/drivers/net/wireless/b43legacy/xmit.c
+++ b/drivers/net/wireless/b43legacy/xmit.c
@@ -254,7 +254,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev,
B43legacy_TX4_MAC_KEYALG_SHIFT) &
B43legacy_TX4_MAC_KEYALG;
wlhdr_len = ieee80211_hdrlen(wlhdr->frame_control);
- iv_len = min((size_t)info->control.hw_key->iv_len,
+ iv_len = min_t(size_t, info->control.hw_key->iv_len,
ARRAY_SIZE(txhdr->iv));
memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len);
} else {
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
index 57cddee03252..1d2ceac3a221 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
@@ -24,6 +24,7 @@ ccflags-y += -D__CHECK_ENDIAN__
obj-$(CONFIG_BRCMFMAC) += brcmfmac.o
brcmfmac-objs += \
wl_cfg80211.o \
+ chip.o \
fwil.o \
fweh.o \
fwsignal.o \
@@ -36,8 +37,7 @@ brcmfmac-objs += \
btcoex.o
brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
dhd_sdio.o \
- bcmsdh.o \
- sdio_chip.o
+ bcmsdh.o
brcmfmac-$(CONFIG_BRCMFMAC_USB) += \
usb.o
brcmfmac-$(CONFIG_BRCMDBG) += \
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
index fa35b23bbaa7..a16e644e7c08 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -43,7 +43,6 @@
#include "dhd_bus.h"
#include "dhd_dbg.h"
#include "sdio_host.h"
-#include "sdio_chip.h"
#define SDIOH_API_ACCESS_RETRY_LIMIT 2
@@ -54,6 +53,12 @@
/* 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 */
+
+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)
{
@@ -264,26 +269,17 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn,
break;
}
- if (ret) {
- /*
- * SleepCSR register access can fail when
- * waking up the device so reduce this noise
- * in the logs.
- */
- if (addr != SBSDIO_FUNC1_SLEEPCSR)
- brcmf_err("failed to %s data F%d@0x%05x, err: %d\n",
- write ? "write" : "read", fn, addr, ret);
- else
- brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
- write ? "write" : "read", fn, addr, ret);
- }
+ if (ret)
+ brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
+ write ? "write" : "read", fn, addr, ret);
+
return ret;
}
static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
u8 regsz, void *data, bool write)
{
- u8 func_num;
+ u8 func;
s32 retry = 0;
int ret;
@@ -297,9 +293,9 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
* The rest: function 1 silicon backplane core registers
*/
if ((addr & ~REG_F0_REG_MASK) == 0)
- func_num = SDIO_FUNC_0;
+ func = SDIO_FUNC_0;
else
- func_num = SDIO_FUNC_1;
+ func = SDIO_FUNC_1;
do {
if (!write)
@@ -307,16 +303,26 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
/* for retry wait for 1 ms till bus get settled down */
if (retry)
usleep_range(1000, 2000);
- ret = brcmf_sdiod_request_data(sdiodev, func_num, addr, regsz,
+ ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz,
data, write);
} while (ret != 0 && ret != -ENOMEDIUM &&
retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
if (ret == -ENOMEDIUM)
brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM);
- else if (ret != 0)
- brcmf_err("failed with %d\n", ret);
-
+ else if (ret != 0) {
+ /*
+ * SleepCSR register access can fail when
+ * waking up the device so reduce this noise
+ * in the logs.
+ */
+ if (addr != SBSDIO_FUNC1_SLEEPCSR)
+ brcmf_err("failed to %s data F%d@0x%05x, err: %d\n",
+ write ? "write" : "read", func, addr, ret);
+ else
+ brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
+ write ? "write" : "read", func, addr, ret);
+ }
return ret;
}
@@ -488,7 +494,6 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
struct mmc_request mmc_req;
struct mmc_command mmc_cmd;
struct mmc_data mmc_dat;
- struct sg_table st;
struct scatterlist *sgl;
int ret = 0;
@@ -533,16 +538,11 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
pkt_offset = 0;
pkt_next = target_list->next;
- if (sg_alloc_table(&st, max_seg_cnt, GFP_KERNEL)) {
- ret = -ENOMEM;
- goto exit;
- }
-
memset(&mmc_req, 0, sizeof(struct mmc_request));
memset(&mmc_cmd, 0, sizeof(struct mmc_command));
memset(&mmc_dat, 0, sizeof(struct mmc_data));
- mmc_dat.sg = st.sgl;
+ mmc_dat.sg = sdiodev->sgtable.sgl;
mmc_dat.blksz = func_blk_sz;
mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
mmc_cmd.opcode = SD_IO_RW_EXTENDED;
@@ -558,7 +558,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
while (seg_sz) {
req_sz = 0;
sg_cnt = 0;
- sgl = st.sgl;
+ sgl = sdiodev->sgtable.sgl;
/* prep sg table */
while (pkt_next != (struct sk_buff *)target_list) {
pkt_data = pkt_next->data + pkt_offset;
@@ -640,7 +640,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
}
exit:
- sg_free_table(&st);
+ sg_init_table(sdiodev->sgtable.sgl, sdiodev->sgtable.orig_nents);
while ((pkt_next = __skb_dequeue(&local_list)) != NULL)
brcmu_pkt_buf_free_skb(pkt_next);
@@ -827,7 +827,7 @@ brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
}
if (!write)
memcpy(data, pkt->data, dsize);
- skb_trim(pkt, dsize);
+ skb_trim(pkt, 0);
/* Adjust for next transfer (if any) */
size -= dsize;
@@ -864,6 +864,29 @@ int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
return 0;
}
+static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev)
+{
+ uint nents;
+ int err;
+
+ if (!sdiodev->sg_support)
+ return;
+
+ nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, brcmf_sdiod_txglomsz);
+ nents += (nents >> 4) + 1;
+
+ WARN_ON(nents > sdiodev->max_segment_count);
+
+ brcmf_dbg(TRACE, "nents=%d\n", nents);
+ err = sg_alloc_table(&sdiodev->sgtable, nents, GFP_KERNEL);
+ if (err < 0) {
+ brcmf_err("allocation failed: disable scatter-gather");
+ sdiodev->sg_support = false;
+ }
+
+ sdiodev->txglomsz = brcmf_sdiod_txglomsz;
+}
+
static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
{
if (sdiodev->bus) {
@@ -881,6 +904,7 @@ static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
sdio_disable_func(sdiodev->func[1]);
sdio_release_host(sdiodev->func[1]);
+ sg_free_table(&sdiodev->sgtable);
sdiodev->sbwad = 0;
return 0;
@@ -936,6 +960,11 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
SG_MAX_SINGLE_ALLOC);
sdiodev->max_segment_size = host->max_seg_size;
+ /* allocate scatter-gather table. sg support
+ * will be disabled upon allocation failure.
+ */
+ brcmf_sdiod_sgtable_alloc(sdiodev);
+
/* try to attach to the target device */
sdiodev->bus = brcmf_sdio_probe(sdiodev);
if (!sdiodev->bus) {
@@ -960,6 +989,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43362)},
{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM,
SDIO_DEVICE_ID_BROADCOM_4335_4339)},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4354)},
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
@@ -1073,9 +1103,7 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
int ret = 0;
- brcmf_dbg(SDIO, "\n");
-
- atomic_set(&sdiodev->suspend, true);
+ brcmf_dbg(SDIO, "Enter\n");
sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]);
if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
@@ -1083,9 +1111,12 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
return -EINVAL;
}
+ atomic_set(&sdiodev->suspend, true);
+
ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER);
if (ret) {
brcmf_err("Failed to set pm_flags\n");
+ atomic_set(&sdiodev->suspend, false);
return ret;
}
@@ -1099,6 +1130,7 @@ static int brcmf_ops_sdio_resume(struct device *dev)
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ brcmf_dbg(SDIO, "Enter\n");
brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS);
atomic_set(&sdiodev->suspend, false);
return 0;
@@ -1115,14 +1147,15 @@ static struct sdio_driver brcmf_sdmmc_driver = {
.remove = brcmf_ops_sdio_remove,
.name = BRCMFMAC_SDIO_PDATA_NAME,
.id_table = brcmf_sdmmc_ids,
-#ifdef CONFIG_PM_SLEEP
.drv = {
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM_SLEEP
.pm = &brcmf_sdio_pm_ops,
- },
#endif /* CONFIG_PM_SLEEP */
+ },
};
-static int brcmf_sdio_pd_probe(struct platform_device *pdev)
+static int __init brcmf_sdio_pd_probe(struct platform_device *pdev)
{
brcmf_dbg(SDIO, "Enter\n");
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
new file mode 100644
index 000000000000..df130ef53d1c
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
@@ -0,0 +1,1034 @@
+/*
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/ssb/ssb_regs.h>
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
+
+#include <defs.h>
+#include <soc.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_utils.h>
+#include <chipcommon.h>
+#include "dhd_dbg.h"
+#include "chip.h"
+
+/* SOC Interconnect types (aka chip types) */
+#define SOCI_SB 0
+#define SOCI_AI 1
+
+/* PL-368 DMP definitions */
+#define DMP_DESC_TYPE_MSK 0x0000000F
+#define DMP_DESC_EMPTY 0x00000000
+#define DMP_DESC_VALID 0x00000001
+#define DMP_DESC_COMPONENT 0x00000001
+#define DMP_DESC_MASTER_PORT 0x00000003
+#define DMP_DESC_ADDRESS 0x00000005
+#define DMP_DESC_ADDRSIZE_GT32 0x00000008
+#define DMP_DESC_EOT 0x0000000F
+
+#define DMP_COMP_DESIGNER 0xFFF00000
+#define DMP_COMP_DESIGNER_S 20
+#define DMP_COMP_PARTNUM 0x000FFF00
+#define DMP_COMP_PARTNUM_S 8
+#define DMP_COMP_CLASS 0x000000F0
+#define DMP_COMP_CLASS_S 4
+#define DMP_COMP_REVISION 0xFF000000
+#define DMP_COMP_REVISION_S 24
+#define DMP_COMP_NUM_SWRAP 0x00F80000
+#define DMP_COMP_NUM_SWRAP_S 19
+#define DMP_COMP_NUM_MWRAP 0x0007C000
+#define DMP_COMP_NUM_MWRAP_S 14
+#define DMP_COMP_NUM_SPORT 0x00003E00
+#define DMP_COMP_NUM_SPORT_S 9
+#define DMP_COMP_NUM_MPORT 0x000001F0
+#define DMP_COMP_NUM_MPORT_S 4
+
+#define DMP_MASTER_PORT_UID 0x0000FF00
+#define DMP_MASTER_PORT_UID_S 8
+#define DMP_MASTER_PORT_NUM 0x000000F0
+#define DMP_MASTER_PORT_NUM_S 4
+
+#define DMP_SLAVE_ADDR_BASE 0xFFFFF000
+#define DMP_SLAVE_ADDR_BASE_S 12
+#define DMP_SLAVE_PORT_NUM 0x00000F00
+#define DMP_SLAVE_PORT_NUM_S 8
+#define DMP_SLAVE_TYPE 0x000000C0
+#define DMP_SLAVE_TYPE_S 6
+#define DMP_SLAVE_TYPE_SLAVE 0
+#define DMP_SLAVE_TYPE_BRIDGE 1
+#define DMP_SLAVE_TYPE_SWRAP 2
+#define DMP_SLAVE_TYPE_MWRAP 3
+#define DMP_SLAVE_SIZE_TYPE 0x00000030
+#define DMP_SLAVE_SIZE_TYPE_S 4
+#define DMP_SLAVE_SIZE_4K 0
+#define DMP_SLAVE_SIZE_8K 1
+#define DMP_SLAVE_SIZE_16K 2
+#define DMP_SLAVE_SIZE_DESC 3
+
+/* EROM CompIdentB */
+#define CIB_REV_MASK 0xff000000
+#define CIB_REV_SHIFT 24
+
+/* ARM CR4 core specific control flag bits */
+#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020
+
+/* D11 core specific control flag bits */
+#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004
+#define D11_BCMA_IOCTL_PHYRESET 0x0008
+
+/* chip core base & ramsize */
+/* bcm4329 */
+/* SDIO device core, ID 0x829 */
+#define BCM4329_CORE_BUS_BASE 0x18011000
+/* internal memory core, ID 0x80e */
+#define BCM4329_CORE_SOCRAM_BASE 0x18003000
+/* ARM Cortex M3 core, ID 0x82a */
+#define BCM4329_CORE_ARM_BASE 0x18002000
+#define BCM4329_RAMSIZE 0x48000
+
+/* bcm43143 */
+/* SDIO device core */
+#define BCM43143_CORE_BUS_BASE 0x18002000
+/* internal memory core */
+#define BCM43143_CORE_SOCRAM_BASE 0x18004000
+/* ARM Cortex M3 core, ID 0x82a */
+#define BCM43143_CORE_ARM_BASE 0x18003000
+#define BCM43143_RAMSIZE 0x70000
+
+#define CORE_SB(base, field) \
+ (base + SBCONFIGOFF + offsetof(struct sbconfig, field))
+#define SBCOREREV(sbidh) \
+ ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \
+ ((sbidh) & SSB_IDHIGH_RCLO))
+
+struct sbconfig {
+ u32 PAD[2];
+ u32 sbipsflag; /* initiator port ocp slave flag */
+ u32 PAD[3];
+ u32 sbtpsflag; /* target port ocp slave flag */
+ u32 PAD[11];
+ u32 sbtmerrloga; /* (sonics >= 2.3) */
+ u32 PAD;
+ u32 sbtmerrlog; /* (sonics >= 2.3) */
+ u32 PAD[3];
+ u32 sbadmatch3; /* address match3 */
+ u32 PAD;
+ u32 sbadmatch2; /* address match2 */
+ u32 PAD;
+ u32 sbadmatch1; /* address match1 */
+ u32 PAD[7];
+ u32 sbimstate; /* initiator agent state */
+ u32 sbintvec; /* interrupt mask */
+ u32 sbtmstatelow; /* target state */
+ u32 sbtmstatehigh; /* target state */
+ u32 sbbwa0; /* bandwidth allocation table0 */
+ u32 PAD;
+ u32 sbimconfiglow; /* initiator configuration */
+ u32 sbimconfighigh; /* initiator configuration */
+ u32 sbadmatch0; /* address match0 */
+ u32 PAD;
+ u32 sbtmconfiglow; /* target configuration */
+ u32 sbtmconfighigh; /* target configuration */
+ u32 sbbconfig; /* broadcast configuration */
+ u32 PAD;
+ u32 sbbstate; /* broadcast state */
+ u32 PAD[3];
+ u32 sbactcnfg; /* activate configuration */
+ u32 PAD[3];
+ u32 sbflagst; /* current sbflags */
+ u32 PAD[3];
+ u32 sbidlow; /* identification */
+ u32 sbidhigh; /* identification */
+};
+
+struct brcmf_core_priv {
+ struct brcmf_core pub;
+ u32 wrapbase;
+ struct list_head list;
+ struct brcmf_chip_priv *chip;
+};
+
+/* ARM CR4 core specific control flag bits */
+#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020
+
+/* D11 core specific control flag bits */
+#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004
+#define D11_BCMA_IOCTL_PHYRESET 0x0008
+
+struct brcmf_chip_priv {
+ struct brcmf_chip pub;
+ const struct brcmf_buscore_ops *ops;
+ void *ctx;
+ /* assured first core is chipcommon, second core is buscore */
+ struct list_head cores;
+ u16 num_cores;
+
+ bool (*iscoreup)(struct brcmf_core_priv *core);
+ void (*coredisable)(struct brcmf_core_priv *core, u32 prereset,
+ u32 reset);
+ void (*resetcore)(struct brcmf_core_priv *core, u32 prereset, u32 reset,
+ u32 postreset);
+};
+
+static void brcmf_chip_sb_corerev(struct brcmf_chip_priv *ci,
+ struct brcmf_core *core)
+{
+ u32 regdata;
+
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(core->base, sbidhigh));
+ core->rev = SBCOREREV(regdata);
+}
+
+static bool brcmf_chip_sb_iscoreup(struct brcmf_core_priv *core)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+ u32 address;
+
+ ci = core->chip;
+ address = CORE_SB(core->pub.base, sbtmstatelow);
+ regdata = ci->ops->read32(ci->ctx, address);
+ regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
+ SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK);
+ return SSB_TMSLOW_CLOCK == regdata;
+}
+
+static bool brcmf_chip_ai_iscoreup(struct brcmf_core_priv *core)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+ bool ret;
+
+ ci = core->chip;
+ regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+ ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK;
+
+ regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL);
+ ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0);
+
+ return ret;
+}
+
+static void brcmf_chip_sb_coredisable(struct brcmf_core_priv *core,
+ u32 prereset, u32 reset)
+{
+ struct brcmf_chip_priv *ci;
+ u32 val, base;
+
+ ci = core->chip;
+ base = core->pub.base;
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ if (val & SSB_TMSLOW_RESET)
+ return;
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ if ((val & SSB_TMSLOW_CLOCK) != 0) {
+ /*
+ * set target reject and spin until busy is clear
+ * (preserve core-specific bits)
+ */
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ val | SSB_TMSLOW_REJECT);
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+ SPINWAIT((ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh))
+ & SSB_TMSHIGH_BUSY), 100000);
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh));
+ if (val & SSB_TMSHIGH_BUSY)
+ brcmf_err("core state still busy\n");
+
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow));
+ if (val & SSB_IDLOW_INITIATOR) {
+ val = ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate));
+ val |= SSB_IMSTATE_REJECT;
+ ci->ops->write32(ci->ctx,
+ CORE_SB(base, sbimstate), val);
+ val = ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate));
+ udelay(1);
+ SPINWAIT((ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate)) &
+ SSB_IMSTATE_BUSY), 100000);
+ }
+
+ /* set reset and reject while enabling the clocks */
+ val = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
+ SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET;
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), val);
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(10);
+
+ /* clear the initiator reject bit */
+ val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow));
+ if (val & SSB_IDLOW_INITIATOR) {
+ val = ci->ops->read32(ci->ctx,
+ CORE_SB(base, sbimstate));
+ val &= ~SSB_IMSTATE_REJECT;
+ ci->ops->write32(ci->ctx,
+ CORE_SB(base, sbimstate), val);
+ }
+ }
+
+ /* leave reset and reject asserted */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET));
+ udelay(1);
+}
+
+static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core,
+ u32 prereset, u32 reset)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+
+ ci = core->chip;
+
+ /* if core is already in reset, just return */
+ regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL);
+ if ((regdata & BCMA_RESET_CTL_RESET) != 0)
+ return;
+
+ /* configure reset */
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+ prereset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
+ ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+
+ /* put in reset */
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL,
+ BCMA_RESET_CTL_RESET);
+ usleep_range(10, 20);
+
+ /* wait till reset is 1 */
+ SPINWAIT(ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) !=
+ BCMA_RESET_CTL_RESET, 300);
+
+ /* in-reset configure */
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+ reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
+ ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+}
+
+static void brcmf_chip_sb_resetcore(struct brcmf_core_priv *core, u32 prereset,
+ u32 reset, u32 postreset)
+{
+ struct brcmf_chip_priv *ci;
+ u32 regdata;
+ u32 base;
+
+ ci = core->chip;
+ base = core->pub.base;
+ /*
+ * Must do the disable sequence first to work for
+ * arbitrary current core state.
+ */
+ brcmf_chip_sb_coredisable(core, 0, 0);
+
+ /*
+ * Now do the initialization sequence.
+ * set reset while enabling the clock and
+ * forcing them on throughout the core
+ */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
+ SSB_TMSLOW_RESET);
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+
+ /* clear any serror */
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh));
+ if (regdata & SSB_TMSHIGH_SERR)
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatehigh), 0);
+
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbimstate));
+ if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) {
+ regdata &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO);
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbimstate), regdata);
+ }
+
+ /* clear reset and allow it to propagate throughout the core */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK);
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+
+ /* leave clock enabled */
+ ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+ SSB_TMSLOW_CLOCK);
+ regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+ udelay(1);
+}
+
+static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
+ u32 reset, u32 postreset)
+{
+ struct brcmf_chip_priv *ci;
+ int count;
+
+ ci = core->chip;
+
+ /* must disable first to work for arbitrary current core state */
+ brcmf_chip_ai_coredisable(core, prereset, reset);
+
+ count = 0;
+ while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) &
+ BCMA_RESET_CTL_RESET) {
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, 0);
+ count++;
+ if (count > 50)
+ break;
+ usleep_range(40, 60);
+ }
+
+ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+ postreset | BCMA_IOCTL_CLK);
+ ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+}
+
+static char *brcmf_chip_name(uint chipid, char *buf, uint len)
+{
+ const char *fmt;
+
+ fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
+ snprintf(buf, len, fmt, chipid);
+ return buf;
+}
+
+static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci,
+ u16 coreid, u32 base,
+ u32 wrapbase)
+{
+ struct brcmf_core_priv *core;
+
+ core = kzalloc(sizeof(*core), GFP_KERNEL);
+ if (!core)
+ return ERR_PTR(-ENOMEM);
+
+ core->pub.id = coreid;
+ core->pub.base = base;
+ core->chip = ci;
+ core->wrapbase = wrapbase;
+
+ list_add_tail(&core->list, &ci->cores);
+ return &core->pub;
+}
+
+#ifdef DEBUG
+/* safety check for chipinfo */
+static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core_priv *core;
+ bool need_socram = false;
+ bool has_socram = false;
+ int idx = 1;
+
+ list_for_each_entry(core, &ci->cores, list) {
+ brcmf_dbg(INFO, " [%-2d] core 0x%x:%-2d base 0x%08x wrap 0x%08x\n",
+ idx++, core->pub.id, core->pub.rev, core->pub.base,
+ core->wrapbase);
+
+ switch (core->pub.id) {
+ case BCMA_CORE_ARM_CM3:
+ need_socram = true;
+ break;
+ case BCMA_CORE_INTERNAL_MEM:
+ has_socram = true;
+ break;
+ case BCMA_CORE_ARM_CR4:
+ if (ci->pub.rambase == 0) {
+ brcmf_err("RAM base not provided with ARM CR4 core\n");
+ return -ENOMEM;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* check RAM core presence for ARM CM3 core */
+ if (need_socram && !has_socram) {
+ brcmf_err("RAM core not provided with ARM CM3 core\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+#else /* DEBUG */
+static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
+{
+ return 0;
+}
+#endif
+
+static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
+{
+ switch (ci->pub.chip) {
+ case BCM4329_CHIP_ID:
+ ci->pub.ramsize = BCM4329_RAMSIZE;
+ break;
+ case BCM43143_CHIP_ID:
+ ci->pub.ramsize = BCM43143_RAMSIZE;
+ break;
+ case BCM43241_CHIP_ID:
+ ci->pub.ramsize = 0x90000;
+ break;
+ case BCM4330_CHIP_ID:
+ ci->pub.ramsize = 0x48000;
+ break;
+ case BCM4334_CHIP_ID:
+ ci->pub.ramsize = 0x80000;
+ break;
+ case BCM4335_CHIP_ID:
+ ci->pub.ramsize = 0xc0000;
+ ci->pub.rambase = 0x180000;
+ break;
+ case BCM43362_CHIP_ID:
+ ci->pub.ramsize = 0x3c000;
+ break;
+ case BCM4339_CHIP_ID:
+ case BCM4354_CHIP_ID:
+ ci->pub.ramsize = 0xc0000;
+ ci->pub.rambase = 0x180000;
+ break;
+ default:
+ brcmf_err("unknown chip: %s\n", ci->pub.name);
+ break;
+ }
+}
+
+static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr,
+ u8 *type)
+{
+ u32 val;
+
+ /* read next descriptor */
+ val = ci->ops->read32(ci->ctx, *eromaddr);
+ *eromaddr += 4;
+
+ if (!type)
+ return val;
+
+ /* determine descriptor type */
+ *type = (val & DMP_DESC_TYPE_MSK);
+ if ((*type & ~DMP_DESC_ADDRSIZE_GT32) == DMP_DESC_ADDRESS)
+ *type = DMP_DESC_ADDRESS;
+
+ return val;
+}
+
+static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr,
+ u32 *regbase, u32 *wrapbase)
+{
+ u8 desc;
+ u32 val;
+ u8 mpnum = 0;
+ u8 stype, sztype, wraptype;
+
+ *regbase = 0;
+ *wrapbase = 0;
+
+ val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc);
+ if (desc == DMP_DESC_MASTER_PORT) {
+ mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S;
+ wraptype = DMP_SLAVE_TYPE_MWRAP;
+ } else if (desc == DMP_DESC_ADDRESS) {
+ /* revert erom address */
+ *eromaddr -= 4;
+ wraptype = DMP_SLAVE_TYPE_SWRAP;
+ } else {
+ *eromaddr -= 4;
+ return -EILSEQ;
+ }
+
+ do {
+ /* locate address descriptor */
+ do {
+ val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc);
+ /* unexpected table end */
+ if (desc == DMP_DESC_EOT) {
+ *eromaddr -= 4;
+ return -EFAULT;
+ }
+ } while (desc != DMP_DESC_ADDRESS);
+
+ /* skip upper 32-bit address descriptor */
+ if (val & DMP_DESC_ADDRSIZE_GT32)
+ brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+
+ sztype = (val & DMP_SLAVE_SIZE_TYPE) >> DMP_SLAVE_SIZE_TYPE_S;
+
+ /* next size descriptor can be skipped */
+ if (sztype == DMP_SLAVE_SIZE_DESC) {
+ val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+ /* skip upper size descriptor if present */
+ if (val & DMP_DESC_ADDRSIZE_GT32)
+ brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+ }
+
+ /* only look for 4K register regions */
+ if (sztype != DMP_SLAVE_SIZE_4K)
+ continue;
+
+ stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S;
+
+ /* only regular slave and wrapper */
+ if (*regbase == 0 && stype == DMP_SLAVE_TYPE_SLAVE)
+ *regbase = val & DMP_SLAVE_ADDR_BASE;
+ if (*wrapbase == 0 && stype == wraptype)
+ *wrapbase = val & DMP_SLAVE_ADDR_BASE;
+ } while (*regbase == 0 || *wrapbase == 0);
+
+ return 0;
+}
+
+static
+int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core *core;
+ u32 eromaddr;
+ u8 desc_type = 0;
+ u32 val;
+ u16 id;
+ u8 nmp, nsp, nmw, nsw, rev;
+ u32 base, wrap;
+ int err;
+
+ eromaddr = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, eromptr));
+
+ while (desc_type != DMP_DESC_EOT) {
+ val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type);
+ if (!(val & DMP_DESC_VALID))
+ continue;
+
+ if (desc_type == DMP_DESC_EMPTY)
+ continue;
+
+ /* need a component descriptor */
+ if (desc_type != DMP_DESC_COMPONENT)
+ continue;
+
+ id = (val & DMP_COMP_PARTNUM) >> DMP_COMP_PARTNUM_S;
+
+ /* next descriptor must be component as well */
+ val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type);
+ if (WARN_ON((val & DMP_DESC_TYPE_MSK) != DMP_DESC_COMPONENT))
+ return -EFAULT;
+
+ /* only look at cores with master port(s) */
+ nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S;
+ nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S;
+ nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S;
+ nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S;
+ rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S;
+
+ /* need core with ports */
+ if (nmw + nsw == 0)
+ continue;
+
+ /* try to obtain register address info */
+ err = brcmf_chip_dmp_get_regaddr(ci, &eromaddr, &base, &wrap);
+ if (err)
+ continue;
+
+ /* finally a core to be added */
+ core = brcmf_chip_add_core(ci, id, base, wrap);
+ if (IS_ERR(core))
+ return PTR_ERR(core);
+
+ core->rev = rev;
+ }
+
+ return 0;
+}
+
+static int brcmf_chip_recognition(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core *core;
+ u32 regdata;
+ u32 socitype;
+
+ /* Get CC core rev
+ * Chipid is assume to be at offset 0 from SI_ENUM_BASE
+ * For different chiptypes or old sdio hosts w/o chipcommon,
+ * other ways of recognition should be added here.
+ */
+ regdata = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, chipid));
+ ci->pub.chip = regdata & CID_ID_MASK;
+ ci->pub.chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
+ socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
+
+ brcmf_chip_name(ci->pub.chip, ci->pub.name, sizeof(ci->pub.name));
+ brcmf_dbg(INFO, "found %s chip: BCM%s, rev=%d\n",
+ socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name,
+ ci->pub.chiprev);
+
+ if (socitype == SOCI_SB) {
+ if (ci->pub.chip != BCM4329_CHIP_ID) {
+ brcmf_err("SB chip is not supported\n");
+ return -ENODEV;
+ }
+ ci->iscoreup = brcmf_chip_sb_iscoreup;
+ ci->coredisable = brcmf_chip_sb_coredisable;
+ ci->resetcore = brcmf_chip_sb_resetcore;
+
+ core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON,
+ SI_ENUM_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV,
+ BCM4329_CORE_BUS_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM,
+ BCM4329_CORE_SOCRAM_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3,
+ BCM4329_CORE_ARM_BASE, 0);
+ brcmf_chip_sb_corerev(ci, core);
+
+ core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0);
+ brcmf_chip_sb_corerev(ci, core);
+ } else if (socitype == SOCI_AI) {
+ ci->iscoreup = brcmf_chip_ai_iscoreup;
+ ci->coredisable = brcmf_chip_ai_coredisable;
+ ci->resetcore = brcmf_chip_ai_resetcore;
+
+ brcmf_chip_dmp_erom_scan(ci);
+ } else {
+ brcmf_err("chip backplane type %u is not supported\n",
+ socitype);
+ return -ENODEV;
+ }
+
+ brcmf_chip_get_raminfo(ci);
+
+ return brcmf_chip_cores_check(ci);
+}
+
+static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id)
+{
+ struct brcmf_core *core;
+ struct brcmf_core_priv *cr4;
+ u32 val;
+
+
+ core = brcmf_chip_get_core(&chip->pub, id);
+ if (!core)
+ return;
+
+ switch (id) {
+ case BCMA_CORE_ARM_CM3:
+ brcmf_chip_coredisable(core, 0, 0);
+ break;
+ case BCMA_CORE_ARM_CR4:
+ cr4 = container_of(core, struct brcmf_core_priv, pub);
+
+ /* clear all IOCTL bits except HALT bit */
+ val = chip->ops->read32(chip->ctx, cr4->wrapbase + BCMA_IOCTL);
+ val &= ARMCR4_BCMA_IOCTL_CPUHALT;
+ brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT,
+ ARMCR4_BCMA_IOCTL_CPUHALT);
+ break;
+ default:
+ brcmf_err("unknown id: %u\n", id);
+ break;
+ }
+}
+
+static int brcmf_chip_setup(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_chip *pub;
+ struct brcmf_core_priv *cc;
+ u32 base;
+ u32 val;
+ int ret = 0;
+
+ pub = &chip->pub;
+ cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list);
+ base = cc->pub.base;
+
+ /* get chipcommon capabilites */
+ pub->cc_caps = chip->ops->read32(chip->ctx,
+ CORE_CC_REG(base, capabilities));
+
+ /* get pmu caps & rev */
+ if (pub->cc_caps & CC_CAP_PMU) {
+ val = chip->ops->read32(chip->ctx,
+ CORE_CC_REG(base, pmucapabilities));
+ pub->pmurev = val & PCAP_REV_MASK;
+ pub->pmucaps = val;
+ }
+
+ brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, pmucaps=0x%x\n",
+ cc->pub.rev, pub->pmurev, pub->pmucaps);
+
+ /* execute bus core specific setup */
+ if (chip->ops->setup)
+ ret = chip->ops->setup(chip->ctx, pub);
+
+ /*
+ * Make sure any on-chip ARM is off (in case strapping is wrong),
+ * or downloaded code was already running.
+ */
+ brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3);
+ brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4);
+ return ret;
+}
+
+struct brcmf_chip *brcmf_chip_attach(void *ctx,
+ const struct brcmf_buscore_ops *ops)
+{
+ struct brcmf_chip_priv *chip;
+ int err = 0;
+
+ if (WARN_ON(!ops->read32))
+ err = -EINVAL;
+ if (WARN_ON(!ops->write32))
+ err = -EINVAL;
+ if (WARN_ON(!ops->prepare))
+ err = -EINVAL;
+ if (WARN_ON(!ops->exit_dl))
+ err = -EINVAL;
+ if (err < 0)
+ return ERR_PTR(-EINVAL);
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&chip->cores);
+ chip->num_cores = 0;
+ chip->ops = ops;
+ chip->ctx = ctx;
+
+ err = ops->prepare(ctx);
+ if (err < 0)
+ goto fail;
+
+ err = brcmf_chip_recognition(chip);
+ if (err < 0)
+ goto fail;
+
+ err = brcmf_chip_setup(chip);
+ if (err < 0)
+ goto fail;
+
+ return &chip->pub;
+
+fail:
+ brcmf_chip_detach(&chip->pub);
+ return ERR_PTR(err);
+}
+
+void brcmf_chip_detach(struct brcmf_chip *pub)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core_priv *core;
+ struct brcmf_core_priv *tmp;
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ list_for_each_entry_safe(core, tmp, &chip->cores, list) {
+ list_del(&core->list);
+ kfree(core);
+ }
+ kfree(chip);
+}
+
+struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core_priv *core;
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ list_for_each_entry(core, &chip->cores, list)
+ if (core->pub.id == coreid)
+ return &core->pub;
+
+ return NULL;
+}
+
+struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *pub)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core_priv *cc;
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list);
+ if (WARN_ON(!cc || cc->pub.id != BCMA_CORE_CHIPCOMMON))
+ return brcmf_chip_get_core(pub, BCMA_CORE_CHIPCOMMON);
+ return &cc->pub;
+}
+
+bool brcmf_chip_iscoreup(struct brcmf_core *pub)
+{
+ struct brcmf_core_priv *core;
+
+ core = container_of(pub, struct brcmf_core_priv, pub);
+ return core->chip->iscoreup(core);
+}
+
+void brcmf_chip_coredisable(struct brcmf_core *pub, u32 prereset, u32 reset)
+{
+ struct brcmf_core_priv *core;
+
+ core = container_of(pub, struct brcmf_core_priv, pub);
+ core->chip->coredisable(core, prereset, reset);
+}
+
+void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset,
+ u32 postreset)
+{
+ struct brcmf_core_priv *core;
+
+ core = container_of(pub, struct brcmf_core_priv, pub);
+ core->chip->resetcore(core, prereset, reset, postreset);
+}
+
+static void
+brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_core *core;
+
+ brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3);
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
+ brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN);
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
+ brcmf_chip_resetcore(core, 0, 0, 0);
+}
+
+static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_core *core;
+
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
+ if (!brcmf_chip_iscoreup(core)) {
+ brcmf_err("SOCRAM core is down after reset?\n");
+ return false;
+ }
+
+ chip->ops->exit_dl(chip->ctx, &chip->pub, 0);
+
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3);
+ brcmf_chip_resetcore(core, 0, 0, 0);
+
+ return true;
+}
+
+static inline void
+brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip)
+{
+ struct brcmf_core *core;
+
+ brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4);
+
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
+ brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN,
+ D11_BCMA_IOCTL_PHYCLOCKEN);
+}
+
+static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec)
+{
+ struct brcmf_core *core;
+
+ chip->ops->exit_dl(chip->ctx, &chip->pub, rstvec);
+
+ /* restore ARM */
+ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4);
+ brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0);
+
+ return true;
+}
+
+void brcmf_chip_enter_download(struct brcmf_chip *pub)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core *arm;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
+ if (arm) {
+ brcmf_chip_cr4_enterdl(chip);
+ return;
+ }
+
+ brcmf_chip_cm3_enterdl(chip);
+}
+
+bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec)
+{
+ struct brcmf_chip_priv *chip;
+ struct brcmf_core *arm;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+ arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
+ if (arm)
+ return brcmf_chip_cr4_exitdl(chip, rstvec);
+
+ return brcmf_chip_cm3_exitdl(chip);
+}
+
+bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
+{
+ u32 base, addr, reg, pmu_cc3_mask = ~0;
+ struct brcmf_chip_priv *chip;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* old chips with PMU version less than 17 don't support save restore */
+ if (pub->pmurev < 17)
+ return false;
+
+ base = brcmf_chip_get_chipcommon(pub)->base;
+ chip = container_of(pub, struct brcmf_chip_priv, pub);
+
+ switch (pub->chip) {
+ case BCM4354_CHIP_ID:
+ /* explicitly check SR engine enable bit */
+ pmu_cc3_mask = BIT(2);
+ /* fall-through */
+ case BCM43241_CHIP_ID:
+ case BCM4335_CHIP_ID:
+ case BCM4339_CHIP_ID:
+ /* read PMU chipcontrol register 3 */
+ addr = CORE_CC_REG(base, chipcontrol_addr);
+ chip->ops->write32(chip->ctx, addr, 3);
+ addr = CORE_CC_REG(base, chipcontrol_data);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return (reg & pmu_cc3_mask) != 0;
+ default:
+ addr = CORE_CC_REG(base, pmucapabilities_ext);
+ reg = chip->ops->read32(chip->ctx, addr);
+ if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0)
+ return false;
+
+ addr = CORE_CC_REG(base, retention_ctl);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
+ PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
+ }
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h
new file mode 100644
index 000000000000..c32908da90c8
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h
@@ -0,0 +1,91 @@
+/*
+ * 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 BRCMF_CHIP_H
+#define BRCMF_CHIP_H
+
+#include <linux/types.h>
+
+#define CORE_CC_REG(base, field) \
+ (base + offsetof(struct chipcregs, field))
+
+/**
+ * struct brcmf_chip - chip level information.
+ *
+ * @chip: chip identifier.
+ * @chiprev: chip revision.
+ * @cc_caps: chipcommon core capabilities.
+ * @pmucaps: PMU capabilities.
+ * @pmurev: PMU revision.
+ * @rambase: RAM base address (only applicable for ARM CR4 chips).
+ * @ramsize: amount of RAM on chip.
+ * @name: string representation of the chip identifier.
+ */
+struct brcmf_chip {
+ u32 chip;
+ u32 chiprev;
+ u32 cc_caps;
+ u32 pmucaps;
+ u32 pmurev;
+ u32 rambase;
+ u32 ramsize;
+ char name[8];
+};
+
+/**
+ * struct brcmf_core - core related information.
+ *
+ * @id: core identifier.
+ * @rev: core revision.
+ * @base: base address of core register space.
+ */
+struct brcmf_core {
+ u16 id;
+ u16 rev;
+ u32 base;
+};
+
+/**
+ * struct brcmf_buscore_ops - buscore specific callbacks.
+ *
+ * @read32: read 32-bit value over bus.
+ * @write32: write 32-bit value over bus.
+ * @prepare: prepare bus for core configuration.
+ * @setup: bus-specific core setup.
+ * @exit_dl: exit download state.
+ * The callback should use the provided @rstvec when non-zero.
+ */
+struct brcmf_buscore_ops {
+ u32 (*read32)(void *ctx, u32 addr);
+ void (*write32)(void *ctx, u32 addr, u32 value);
+ int (*prepare)(void *ctx);
+ int (*setup)(void *ctx, struct brcmf_chip *chip);
+ void (*exit_dl)(void *ctx, struct brcmf_chip *chip, u32 rstvec);
+};
+
+struct brcmf_chip *brcmf_chip_attach(void *ctx,
+ const struct brcmf_buscore_ops *ops);
+void brcmf_chip_detach(struct brcmf_chip *chip);
+struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid);
+struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip);
+bool brcmf_chip_iscoreup(struct brcmf_core *core);
+void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset);
+void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset,
+ u32 postreset);
+void brcmf_chip_enter_download(struct brcmf_chip *ci);
+bool brcmf_chip_exit_download(struct brcmf_chip *ci, u32 rstvec);
+bool brcmf_chip_sr_capable(struct brcmf_chip *pub);
+
+#endif /* BRCMF_AXIDMP_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index d4d966beb840..7d28cd385092 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -1040,12 +1040,12 @@ void brcmf_detach(struct device *dev)
brcmf_cfg80211_detach(drvr->config);
+ brcmf_fws_deinit(drvr);
+
brcmf_bus_detach(drvr);
brcmf_proto_detach(drvr);
- brcmf_fws_deinit(drvr);
-
brcmf_debugfs_detach(drvr);
bus_if->drvr = NULL;
kfree(drvr);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index ddaa9efd053d..13c89a0c4ba7 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -23,6 +23,7 @@
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
#include <linux/semaphore.h>
@@ -40,7 +41,7 @@
#include <brcm_hw_ids.h>
#include <soc.h>
#include "sdio_host.h"
-#include "sdio_chip.h"
+#include "chip.h"
#include "nvram.h"
#define DCMD_RESP_TIMEOUT 2000 /* In milli second */
@@ -112,8 +113,6 @@ struct rte_console {
#define BRCMF_TXBOUND 20 /* Default for max tx frames in
one scheduling */
-#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */
-
#define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */
#define MEMBLOCK 2048 /* Block size used for downloading
@@ -156,6 +155,34 @@ struct rte_console {
/* manfid tuple length, include tuple, link bytes */
#define SBSDIO_CIS_MANFID_TUPLE_LEN 6
+#define CORE_BUS_REG(base, field) \
+ (base + offsetof(struct sdpcmd_regs, field))
+
+/* SDIO function 1 register CHIPCLKCSR */
+/* Force ALP request to backplane */
+#define SBSDIO_FORCE_ALP 0x01
+/* Force HT request to backplane */
+#define SBSDIO_FORCE_HT 0x02
+/* Force ILP request to backplane */
+#define SBSDIO_FORCE_ILP 0x04
+/* Make ALP ready (power up xtal) */
+#define SBSDIO_ALP_AVAIL_REQ 0x08
+/* Make HT ready (power up PLL) */
+#define SBSDIO_HT_AVAIL_REQ 0x10
+/* Squelch clock requests from HW */
+#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20
+/* Status: ALP is ready */
+#define SBSDIO_ALP_AVAIL 0x40
+/* Status: HT is ready */
+#define SBSDIO_HT_AVAIL 0x80
+#define SBSDIO_CSR_MASK 0x1F
+#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
+#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS)
+#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
+#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
+#define SBSDIO_CLKAV(regval, alponly) \
+ (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval)))
+
/* intstatus */
#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */
#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */
@@ -276,7 +303,6 @@ struct rte_console {
/* Flags for SDH calls */
#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
-#define BRCMF_IDLE_IMMEDIATE (-1) /* Enter idle immediately */
#define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change
* when idle
*/
@@ -433,10 +459,11 @@ struct brcmf_sdio {
bool alp_only; /* Don't use HT clock (ALP only) */
u8 *ctrl_frame_buf;
- u32 ctrl_frame_len;
+ u16 ctrl_frame_len;
bool ctrl_frame_stat;
- spinlock_t txqlock;
+ spinlock_t txq_lock; /* protect bus->txq */
+ struct semaphore tx_seq_lock; /* protect bus->tx_seq */
wait_queue_head_t ctrl_wait;
wait_queue_head_t dcmd_resp_wait;
@@ -483,16 +510,58 @@ static const uint max_roundup = 512;
#define ALIGNMENT 4
-static int brcmf_sdio_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE;
-module_param_named(txglomsz, brcmf_sdio_txglomsz, int, 0);
-MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]");
-
enum brcmf_sdio_frmtype {
BRCMF_SDIO_FT_NORMAL,
BRCMF_SDIO_FT_SUPER,
BRCMF_SDIO_FT_SUB,
};
+#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
+
+/* SDIO Pad drive strength to select value mappings */
+struct sdiod_drive_str {
+ u8 strength; /* Pad Drive Strength in mA */
+ u8 sel; /* Chip-specific select value */
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
+static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
+ {32, 0x6},
+ {26, 0x7},
+ {22, 0x4},
+ {16, 0x5},
+ {12, 0x2},
+ {8, 0x3},
+ {4, 0x0},
+ {0, 0x1}
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */
+static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = {
+ {6, 0x7},
+ {5, 0x6},
+ {4, 0x5},
+ {3, 0x4},
+ {2, 0x2},
+ {1, 0x1},
+ {0, 0x0}
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */
+static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = {
+ {3, 0x3},
+ {2, 0x2},
+ {1, 0x1},
+ {0, 0x0} };
+
+/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */
+static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
+ {16, 0x7},
+ {12, 0x5},
+ {8, 0x3},
+ {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"
@@ -511,6 +580,8 @@ enum brcmf_sdio_frmtype {
#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 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);
@@ -530,6 +601,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME);
MODULE_FIRMWARE(BCM43362_NVRAM_NAME);
MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME);
MODULE_FIRMWARE(BCM4339_NVRAM_NAME);
+MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM4354_NVRAM_NAME);
struct brcmf_firmware_names {
u32 chipid;
@@ -555,7 +628,8 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = {
{ BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
{ BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
{ BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
- { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }
+ { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
+ { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
};
@@ -618,27 +692,24 @@ static bool data_ok(struct brcmf_sdio *bus)
* Reads a register in the SDIO hardware block. This block occupies a series of
* adresses on the 32 bit backplane bus.
*/
-static int
-r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset)
+static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset)
{
- u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
+ struct brcmf_core *core;
int ret;
- *regvar = brcmf_sdiod_regrl(bus->sdiodev,
- bus->ci->c_inf[idx].base + offset, &ret);
+ core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+ *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret);
return ret;
}
-static int
-w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)
+static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)
{
- u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
+ struct brcmf_core *core;
int ret;
- brcmf_sdiod_regwl(bus->sdiodev,
- bus->ci->c_inf[idx].base + reg_offset,
- regval, &ret);
+ core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+ brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret);
return ret;
}
@@ -650,16 +721,12 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
int err = 0;
int try_cnt = 0;
- brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(TRACE, "Enter: on=%d\n", on);
wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
/* 1st KSO write goes to AOS wake up core if device is asleep */
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
wr_val, &err);
- if (err) {
- brcmf_err("SDIO_AOS KSO write error: %d\n", err);
- return err;
- }
if (on) {
/* device WAKEUP through KSO:
@@ -689,18 +756,22 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
&err);
if (((rd_val & bmask) == cmp_val) && !err)
break;
- brcmf_dbg(SDIO, "KSO wr/rd retry:%d (max: %d) ERR:%x\n",
- try_cnt, MAX_KSO_ATTEMPTS, err);
+
udelay(KSO_WAIT_US);
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
wr_val, &err);
} while (try_cnt++ < MAX_KSO_ATTEMPTS);
+ if (try_cnt > 2)
+ brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt,
+ rd_val, err);
+
+ if (try_cnt > MAX_KSO_ATTEMPTS)
+ brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err);
+
return err;
}
-#define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND)
-
#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
/* Turn backplane clock on or off */
@@ -799,7 +870,6 @@ static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
}
#endif /* defined (DEBUG) */
- bus->activity = true;
} else {
clkreq = 0;
@@ -899,8 +969,9 @@ static int
brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
{
int err = 0;
- brcmf_dbg(TRACE, "Enter\n");
- brcmf_dbg(SDIO, "request %s currently %s\n",
+ u8 clkcsr;
+
+ brcmf_dbg(SDIO, "Enter: request %s currently %s\n",
(sleep ? "SLEEP" : "WAKE"),
(bus->sleeping ? "SLEEP" : "WAKE"));
@@ -917,8 +988,20 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
atomic_read(&bus->ipend) > 0 ||
(!atomic_read(&bus->fcstate) &&
brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
- data_ok(bus)))
- return -EBUSY;
+ data_ok(bus))) {
+ err = -EBUSY;
+ goto done;
+ }
+
+ clkcsr = brcmf_sdiod_regrb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ &err);
+ if ((clkcsr & SBSDIO_CSR_MASK) == 0) {
+ brcmf_dbg(SDIO, "no clock, set ALP\n");
+ brcmf_sdiod_regwb(bus->sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR,
+ SBSDIO_ALP_AVAIL_REQ, &err);
+ }
err = brcmf_sdio_kso_control(bus, false);
/* disable watchdog */
if (!err)
@@ -935,7 +1018,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
} else {
brcmf_err("error while changing bus sleep state %d\n",
err);
- return err;
+ goto done;
}
}
@@ -947,11 +1030,92 @@ end:
} else {
brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
}
-
+done:
+ brcmf_dbg(SDIO, "Exit: err=%d\n", err);
return err;
}
+#ifdef DEBUG
+static inline bool brcmf_sdio_valid_shared_address(u32 addr)
+{
+ return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff));
+}
+
+static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
+{
+ u32 addr;
+ int rv;
+ u32 shaddr = 0;
+ struct sdpcm_shared_le sh_le;
+ __le32 addr_le;
+
+ shaddr = bus->ci->rambase + bus->ramsize - 4;
+
+ /*
+ * Read last word in socram to determine
+ * address of sdpcm_shared structure
+ */
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_bus_sleep(bus, false, false);
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4);
+ sdio_release_host(bus->sdiodev->func[1]);
+ if (rv < 0)
+ return rv;
+
+ addr = le32_to_cpu(addr_le);
+
+ brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr);
+
+ /*
+ * Check if addr is valid.
+ * NVRAM length at the end of memory should have been overwritten.
+ */
+ if (!brcmf_sdio_valid_shared_address(addr)) {
+ brcmf_err("invalid sdpcm_shared address 0x%08X\n",
+ addr);
+ return -EINVAL;
+ }
+
+ /* Read hndrte_shared structure */
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
+ sizeof(struct sdpcm_shared_le));
+ if (rv < 0)
+ return rv;
+
+ /* Endianness */
+ sh->flags = le32_to_cpu(sh_le.flags);
+ sh->trap_addr = le32_to_cpu(sh_le.trap_addr);
+ sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr);
+ sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr);
+ sh->assert_line = le32_to_cpu(sh_le.assert_line);
+ sh->console_addr = le32_to_cpu(sh_le.console_addr);
+ sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr);
+
+ if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) {
+ brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n",
+ SDPCM_SHARED_VERSION,
+ sh->flags & SDPCM_SHARED_VERSION_MASK);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
+{
+ struct sdpcm_shared sh;
+
+ if (brcmf_sdio_readshared(bus, &sh) == 0)
+ bus->console_addr = sh.console_addr;
+}
+#else
+static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
+{
+}
+#endif /* DEBUG */
+
static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
{
u32 intstatus = 0;
@@ -995,6 +1159,12 @@ static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
else
brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n",
bus->sdpcm_ver);
+
+ /*
+ * Retrieve console state address now that firmware should have
+ * updated it.
+ */
+ brcmf_sdio_get_console_addr(bus);
}
/*
@@ -1083,6 +1253,28 @@ static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
bus->cur_read.len = 0;
}
+static void brcmf_sdio_txfail(struct brcmf_sdio *bus)
+{
+ struct brcmf_sdio_dev *sdiodev = bus->sdiodev;
+ u8 i, hi, lo;
+
+ /* On failure, abort the command and terminate the frame */
+ brcmf_err("sdio error, abort command and terminate frame\n");
+ bus->sdcnt.tx_sderrs++;
+
+ brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2);
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL);
+ bus->sdcnt.f1regdata++;
+
+ for (i = 0; i < 3; i++) {
+ hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL);
+ lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL);
+ bus->sdcnt.f1regdata += 2;
+ if ((hi == 0) && (lo == 0))
+ break;
+ }
+}
+
/* return total length of buffer chain */
static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus)
{
@@ -1955,7 +2147,7 @@ static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus,
memcpy(pkt_pad->data,
pkt->data + pkt->len - tail_chop,
tail_chop);
- *(u32 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop;
+ *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop;
skb_trim(pkt, pkt->len - tail_chop);
skb_trim(pkt_pad, tail_pad + tail_chop);
__skb_queue_after(pktq, pkt, pkt_pad);
@@ -2003,7 +2195,7 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
* already properly aligned and does not
* need an sdpcm header.
*/
- if (*(u32 *)(pkt_next->cb) & ALIGN_SKB_FLAG)
+ if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG)
continue;
/* align packet data pointer */
@@ -2037,10 +2229,10 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
if (BRCMF_BYTES_ON() &&
((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
(BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)))
- brcmf_dbg_hex_dump(true, pkt_next, hd_info.len,
+ brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len,
"Tx Frame:\n");
else if (BRCMF_HDRS_ON())
- brcmf_dbg_hex_dump(true, pkt_next,
+ brcmf_dbg_hex_dump(true, pkt_next->data,
head_pad + bus->tx_hdrlen,
"Tx Header:\n");
}
@@ -2067,11 +2259,11 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
u8 *hdr;
u32 dat_offset;
u16 tail_pad;
- u32 dummy_flags, chop_len;
+ u16 dummy_flags, chop_len;
struct sk_buff *pkt_next, *tmp, *pkt_prev;
skb_queue_walk_safe(pktq, pkt_next, tmp) {
- dummy_flags = *(u32 *)(pkt_next->cb);
+ dummy_flags = *(u16 *)(pkt_next->cb);
if (dummy_flags & ALIGN_SKB_FLAG) {
chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK;
if (chop_len) {
@@ -2100,7 +2292,6 @@ static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
uint chan)
{
int ret;
- int i;
struct sk_buff *pkt_next, *tmp;
brcmf_dbg(TRACE, "Enter\n");
@@ -2113,28 +2304,9 @@ static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq);
bus->sdcnt.f2txdata++;
- if (ret < 0) {
- /* On failure, abort the command and terminate the frame */
- brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
- ret);
- bus->sdcnt.tx_sderrs++;
+ if (ret < 0)
+ brcmf_sdio_txfail(bus);
- brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
- SFC_WF_TERM, NULL);
- bus->sdcnt.f1regdata++;
-
- for (i = 0; i < 3; i++) {
- u8 hi, lo;
- hi = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_WFRAMEBCHI, NULL);
- lo = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_WFRAMEBCLO, NULL);
- bus->sdcnt.f1regdata += 2;
- if ((hi == 0) && (lo == 0))
- break;
- }
- }
sdio_release_host(bus->sdiodev->func[1]);
done:
@@ -2164,13 +2336,15 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
/* Send frames until the limit or some other event */
for (cnt = 0; (cnt < maxframes) && data_ok(bus);) {
pkt_num = 1;
- __skb_queue_head_init(&pktq);
+ if (down_interruptible(&bus->tx_seq_lock))
+ return cnt;
if (bus->txglom)
pkt_num = min_t(u8, bus->tx_max - bus->tx_seq,
- brcmf_sdio_txglomsz);
+ bus->sdiodev->txglomsz);
pkt_num = min_t(u32, pkt_num,
brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol));
- spin_lock_bh(&bus->txqlock);
+ __skb_queue_head_init(&pktq);
+ spin_lock_bh(&bus->txq_lock);
for (i = 0; i < pkt_num; i++) {
pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map,
&prec_out);
@@ -2178,15 +2352,19 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
break;
__skb_queue_tail(&pktq, pkt);
}
- spin_unlock_bh(&bus->txqlock);
- if (i == 0)
+ spin_unlock_bh(&bus->txq_lock);
+ if (i == 0) {
+ up(&bus->tx_seq_lock);
break;
+ }
ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL);
+ up(&bus->tx_seq_lock);
+
cnt += i;
/* In poll mode, need to check for other events */
- if (!bus->intr && cnt) {
+ if (!bus->intr) {
/* Check device status, signal pending interrupt */
sdio_claim_host(bus->sdiodev->func[1]);
ret = r_sdreg32(bus, &intstatus,
@@ -2211,6 +2389,68 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
return cnt;
}
+static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len)
+{
+ u8 doff;
+ u16 pad;
+ uint retries = 0;
+ struct brcmf_sdio_hdrinfo hd_info = {0};
+ int ret;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* Back the pointer to make room for bus header */
+ frame -= bus->tx_hdrlen;
+ len += bus->tx_hdrlen;
+
+ /* Add alignment padding (optional for ctl frames) */
+ doff = ((unsigned long)frame % bus->head_align);
+ if (doff) {
+ frame -= doff;
+ len += doff;
+ memset(frame + bus->tx_hdrlen, 0, doff);
+ }
+
+ /* Round send length to next SDIO block */
+ pad = 0;
+ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
+ pad = bus->blocksize - (len % bus->blocksize);
+ if ((pad > bus->roundup) || (pad >= bus->blocksize))
+ pad = 0;
+ } else if (len % bus->head_align) {
+ pad = bus->head_align - (len % bus->head_align);
+ }
+ len += pad;
+
+ hd_info.len = len - pad;
+ hd_info.channel = SDPCM_CONTROL_CHANNEL;
+ hd_info.dat_offset = doff + bus->tx_hdrlen;
+ hd_info.seq_num = bus->tx_seq;
+ hd_info.lastfrm = true;
+ hd_info.tail_pad = pad;
+ brcmf_sdio_hdpack(bus, frame, &hd_info);
+
+ if (bus->txglom)
+ brcmf_sdio_update_hwhdr(frame, len);
+
+ brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
+ frame, len, "Tx Frame:\n");
+ brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) &&
+ BRCMF_HDRS_ON(),
+ frame, min_t(u16, len, 16), "TxHdr:\n");
+
+ do {
+ ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len);
+
+ if (ret < 0)
+ brcmf_sdio_txfail(bus);
+ else
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
+ } while (ret < 0 && retries++ < TXRETRIES);
+
+ return ret;
+}
+
static void brcmf_sdio_bus_stop(struct device *dev)
{
u32 local_hostintmask;
@@ -2292,21 +2532,29 @@ static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus)
}
}
+static void atomic_orr(int val, atomic_t *v)
+{
+ int old_val;
+
+ old_val = atomic_read(v);
+ while (atomic_cmpxchg(v, old_val, val | old_val) != old_val)
+ old_val = atomic_read(v);
+}
+
static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
{
- u8 idx;
+ struct brcmf_core *buscore;
u32 addr;
unsigned long val;
- int n, ret;
+ int ret;
- idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
- addr = bus->ci->c_inf[idx].base +
- offsetof(struct sdpcmd_regs, intstatus);
+ buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+ addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus);
val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret);
bus->sdcnt.f1regdata++;
if (ret != 0)
- val = 0;
+ return ret;
val &= bus->hostintmask;
atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE));
@@ -2315,13 +2563,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
if (val) {
brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret);
bus->sdcnt.f1regdata++;
- }
-
- if (ret) {
- atomic_set(&bus->intstatus, 0);
- } else if (val) {
- for_each_set_bit(n, &val, 32)
- set_bit(n, (unsigned long *)&bus->intstatus.counter);
+ atomic_orr(val, &bus->intstatus);
}
return ret;
@@ -2331,10 +2573,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
{
u32 newstatus = 0;
unsigned long intstatus;
- uint rxlimit = bus->rxbound; /* Rx frames to read before resched */
uint txlimit = bus->txbound; /* Tx frames to send before resched */
- uint framecnt = 0; /* Temporary counter of tx/rx frames */
- int err = 0, n;
+ uint framecnt; /* Temporary counter of tx/rx frames */
+ int err = 0;
brcmf_dbg(TRACE, "Enter\n");
@@ -2431,70 +2672,38 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
intstatus &= ~I_HMB_FRAME_IND;
/* On frame indication, read available frames */
- if (PKT_AVAILABLE() && bus->clkstate == CLK_AVAIL) {
- framecnt = brcmf_sdio_readframes(bus, rxlimit);
+ if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) {
+ brcmf_sdio_readframes(bus, bus->rxbound);
if (!bus->rxpending)
intstatus &= ~I_HMB_FRAME_IND;
- rxlimit -= min(framecnt, rxlimit);
}
/* Keep still-pending events for next scheduling */
- if (intstatus) {
- for_each_set_bit(n, &intstatus, 32)
- set_bit(n, (unsigned long *)&bus->intstatus.counter);
- }
+ if (intstatus)
+ atomic_orr(intstatus, &bus->intstatus);
brcmf_sdio_clrintr(bus);
- if (data_ok(bus) && bus->ctrl_frame_stat &&
- (bus->clkstate == CLK_AVAIL)) {
- int i;
-
- sdio_claim_host(bus->sdiodev->func[1]);
- err = brcmf_sdiod_send_buf(bus->sdiodev, bus->ctrl_frame_buf,
- (u32)bus->ctrl_frame_len);
-
- if (err < 0) {
- /* On failure, abort the command and
- terminate the frame */
- brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
- err);
- bus->sdcnt.tx_sderrs++;
-
- brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
-
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
- SFC_WF_TERM, &err);
- bus->sdcnt.f1regdata++;
-
- for (i = 0; i < 3; i++) {
- u8 hi, lo;
- hi = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_WFRAMEBCHI,
- &err);
- lo = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_WFRAMEBCLO,
- &err);
- bus->sdcnt.f1regdata += 2;
- if ((hi == 0) && (lo == 0))
- break;
- }
+ if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) &&
+ (down_interruptible(&bus->tx_seq_lock) == 0)) {
+ if (data_ok(bus)) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
+ bus->ctrl_frame_len);
+ sdio_release_host(bus->sdiodev->func[1]);
- } else {
- bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
+ bus->ctrl_frame_stat = false;
+ brcmf_sdio_wait_event_wakeup(bus);
}
- sdio_release_host(bus->sdiodev->func[1]);
- bus->ctrl_frame_stat = false;
- brcmf_sdio_wait_event_wakeup(bus);
+ up(&bus->tx_seq_lock);
}
/* Send queued frames (limit 1 if rx may still be pending) */
- else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
- brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit
- && data_ok(bus)) {
+ if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
+ brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit &&
+ data_ok(bus)) {
framecnt = bus->rxpending ? min(txlimit, bus->txminmax) :
txlimit;
- framecnt = brcmf_sdio_sendfromq(bus, framecnt);
- txlimit -= framecnt;
+ brcmf_sdio_sendfromq(bus, framecnt);
}
if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) {
@@ -2504,19 +2713,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
atomic_read(&bus->ipend) > 0 ||
(!atomic_read(&bus->fcstate) &&
brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
- data_ok(bus)) || PKT_AVAILABLE()) {
+ data_ok(bus))) {
atomic_inc(&bus->dpc_tskcnt);
}
-
- /* If we're done for now, turn off clock request. */
- if ((bus->clkstate != CLK_PENDING)
- && bus->idletime == BRCMF_IDLE_IMMEDIATE) {
- bus->activity = false;
- brcmf_dbg(SDIO, "idle state\n");
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_bus_sleep(bus, true, false);
- sdio_release_host(bus->sdiodev->func[1]);
- }
}
static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev)
@@ -2531,15 +2730,12 @@ static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev)
static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
{
int ret = -EBADE;
- uint datalen, prec;
+ uint prec;
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct brcmf_sdio *bus = sdiodev->bus;
- ulong flags;
- brcmf_dbg(TRACE, "Enter\n");
-
- datalen = pkt->len;
+ brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len);
/* Add space for the header */
skb_push(pkt, bus->tx_hdrlen);
@@ -2553,7 +2749,9 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
bus->sdcnt.fcqueued++;
/* Priority based enq */
- spin_lock_irqsave(&bus->txqlock, flags);
+ spin_lock_bh(&bus->txq_lock);
+ /* reset bus_flags in packet cb */
+ *(u16 *)(pkt->cb) = 0;
if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) {
skb_pull(pkt, bus->tx_hdrlen);
brcmf_err("out of bus->txq !!!\n");
@@ -2566,7 +2764,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
bus->txoff = true;
brcmf_txflowblock(bus->sdiodev->dev, true);
}
- spin_unlock_irqrestore(&bus->txqlock, flags);
+ spin_unlock_bh(&bus->txq_lock);
#ifdef DEBUG
if (pktq_plen(&bus->txq, prec) > qcount[prec])
@@ -2661,110 +2859,27 @@ break2:
}
#endif /* DEBUG */
-static int brcmf_sdio_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len)
-{
- int i;
- int ret;
-
- bus->ctrl_frame_stat = false;
- ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len);
-
- if (ret < 0) {
- /* On failure, abort the command and terminate the frame */
- brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
- ret);
- bus->sdcnt.tx_sderrs++;
-
- brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
-
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
- SFC_WF_TERM, NULL);
- bus->sdcnt.f1regdata++;
-
- for (i = 0; i < 3; i++) {
- u8 hi, lo;
- hi = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_WFRAMEBCHI, NULL);
- lo = brcmf_sdiod_regrb(bus->sdiodev,
- SBSDIO_FUNC1_WFRAMEBCLO, NULL);
- bus->sdcnt.f1regdata += 2;
- if (hi == 0 && lo == 0)
- break;
- }
- return ret;
- }
-
- bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
-
- return ret;
-}
-
static int
brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
{
- u8 *frame;
- u16 len, pad;
- uint retries = 0;
- u8 doff = 0;
- int ret = -1;
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct brcmf_sdio *bus = sdiodev->bus;
- struct brcmf_sdio_hdrinfo hd_info = {0};
+ int ret = -1;
brcmf_dbg(TRACE, "Enter\n");
- /* Back the pointer to make a room for bus header */
- frame = msg - bus->tx_hdrlen;
- len = (msglen += bus->tx_hdrlen);
-
- /* Add alignment padding (optional for ctl frames) */
- doff = ((unsigned long)frame % bus->head_align);
- if (doff) {
- frame -= doff;
- len += doff;
- msglen += doff;
- memset(frame, 0, doff + bus->tx_hdrlen);
- }
- /* precondition: doff < bus->head_align */
- doff += bus->tx_hdrlen;
-
- /* Round send length to next SDIO block */
- pad = 0;
- if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
- pad = bus->blocksize - (len % bus->blocksize);
- if ((pad > bus->roundup) || (pad >= bus->blocksize))
- pad = 0;
- } else if (len % bus->head_align) {
- pad = bus->head_align - (len % bus->head_align);
- }
- len += pad;
-
- /* precondition: IS_ALIGNED((unsigned long)frame, 2) */
-
- /* Make sure backplane clock is on */
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_bus_sleep(bus, false, false);
- sdio_release_host(bus->sdiodev->func[1]);
-
- hd_info.len = (u16)msglen;
- hd_info.channel = SDPCM_CONTROL_CHANNEL;
- hd_info.dat_offset = doff;
- hd_info.seq_num = bus->tx_seq;
- hd_info.lastfrm = true;
- hd_info.tail_pad = pad;
- brcmf_sdio_hdpack(bus, frame, &hd_info);
-
- if (bus->txglom)
- brcmf_sdio_update_hwhdr(frame, len);
+ if (down_interruptible(&bus->tx_seq_lock))
+ return -EINTR;
if (!data_ok(bus)) {
brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n",
bus->tx_max, bus->tx_seq);
- bus->ctrl_frame_stat = true;
+ up(&bus->tx_seq_lock);
/* Send from dpc */
- bus->ctrl_frame_buf = frame;
- bus->ctrl_frame_len = len;
+ bus->ctrl_frame_buf = msg;
+ bus->ctrl_frame_len = msglen;
+ bus->ctrl_frame_stat = true;
wait_event_interruptible_timeout(bus->ctrl_wait,
!bus->ctrl_frame_stat,
@@ -2775,31 +2890,18 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
ret = 0;
} else {
brcmf_dbg(SDIO, "ctrl_frame_stat == true\n");
+ bus->ctrl_frame_stat = false;
+ if (down_interruptible(&bus->tx_seq_lock))
+ return -EINTR;
ret = -1;
}
}
-
if (ret == -1) {
- brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
- frame, len, "Tx Frame:\n");
- brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) &&
- BRCMF_HDRS_ON(),
- frame, min_t(u16, len, 16), "TxHdr:\n");
-
- do {
- sdio_claim_host(bus->sdiodev->func[1]);
- ret = brcmf_sdio_tx_frame(bus, frame, len);
- sdio_release_host(bus->sdiodev->func[1]);
- } while (ret < 0 && retries++ < TXRETRIES);
- }
-
- if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) &&
- atomic_read(&bus->dpc_tskcnt) == 0) {
- bus->activity = false;
sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_dbg(INFO, "idle\n");
- brcmf_sdio_clkctl(bus, CLK_NONE, true);
+ brcmf_sdio_bus_sleep(bus, false, false);
+ ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen);
sdio_release_host(bus->sdiodev->func[1]);
+ up(&bus->tx_seq_lock);
}
if (ret)
@@ -2811,72 +2913,6 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
}
#ifdef DEBUG
-static inline bool brcmf_sdio_valid_shared_address(u32 addr)
-{
- return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff));
-}
-
-static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
- struct sdpcm_shared *sh)
-{
- u32 addr;
- int rv;
- u32 shaddr = 0;
- struct sdpcm_shared_le sh_le;
- __le32 addr_le;
-
- shaddr = bus->ci->rambase + bus->ramsize - 4;
-
- /*
- * Read last word in socram to determine
- * address of sdpcm_shared structure
- */
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_bus_sleep(bus, false, false);
- rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4);
- sdio_release_host(bus->sdiodev->func[1]);
- if (rv < 0)
- return rv;
-
- addr = le32_to_cpu(addr_le);
-
- brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr);
-
- /*
- * Check if addr is valid.
- * NVRAM length at the end of memory should have been overwritten.
- */
- if (!brcmf_sdio_valid_shared_address(addr)) {
- brcmf_err("invalid sdpcm_shared address 0x%08X\n",
- addr);
- return -EINVAL;
- }
-
- /* Read hndrte_shared structure */
- rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
- sizeof(struct sdpcm_shared_le));
- if (rv < 0)
- return rv;
-
- /* Endianness */
- sh->flags = le32_to_cpu(sh_le.flags);
- sh->trap_addr = le32_to_cpu(sh_le.trap_addr);
- sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr);
- sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr);
- sh->assert_line = le32_to_cpu(sh_le.assert_line);
- sh->console_addr = le32_to_cpu(sh_le.console_addr);
- sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr);
-
- if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) {
- brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n",
- SDPCM_SHARED_VERSION,
- sh->flags & SDPCM_SHARED_VERSION_MASK);
- return -EPROTO;
- }
-
- return 0;
-}
-
static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
struct sdpcm_shared *sh, char __user *data,
size_t count)
@@ -3106,6 +3142,8 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
debugfs_create_file("forensics", S_IRUGO, dentry, bus,
&brcmf_sdio_forensic_ops);
brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt);
+ debugfs_create_u32("console_interval", 0644, dentry,
+ &bus->console_interval);
}
#else
static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
@@ -3224,32 +3262,17 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus,
const struct firmware *fw)
{
int err;
- int offset;
- int address;
- int len;
brcmf_dbg(TRACE, "Enter\n");
- err = 0;
- offset = 0;
- address = bus->ci->rambase;
- while (offset < fw->size) {
- len = ((offset + MEMBLOCK) < fw->size) ? MEMBLOCK :
- fw->size - offset;
- err = brcmf_sdiod_ramrw(bus->sdiodev, true, address,
- (u8 *)&fw->data[offset], len);
- if (err) {
- brcmf_err("error %d on writing %d membytes at 0x%08x\n",
- err, len, address);
- return err;
- }
- offset += len;
- address += len;
- }
- if (!err)
- if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase,
- (u8 *)fw->data, fw->size))
- err = -EIO;
+ err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase,
+ (u8 *)fw->data, fw->size);
+ if (err)
+ brcmf_err("error %d on writing %d membytes at 0x%08x\n",
+ err, (int)fw->size, bus->ci->rambase);
+ else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase,
+ (u8 *)fw->data, fw->size))
+ err = -EIO;
return err;
}
@@ -3292,7 +3315,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
/* Keep arm in reset */
- brcmf_sdio_chip_enter_download(bus->sdiodev, bus->ci);
+ brcmf_chip_enter_download(bus->ci);
fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN);
if (fw == NULL) {
@@ -3324,7 +3347,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
}
/* Take arm out of reset */
- if (!brcmf_sdio_chip_exit_download(bus->sdiodev, bus->ci, rstvec)) {
+ if (!brcmf_chip_exit_download(bus->ci, rstvec)) {
brcmf_err("error getting out of ARM core reset\n");
goto err;
}
@@ -3339,40 +3362,6 @@ err:
return bcmerror;
}
-static bool brcmf_sdio_sr_capable(struct brcmf_sdio *bus)
-{
- u32 addr, reg, pmu_cc3_mask = ~0;
- int err;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- /* old chips with PMU version less than 17 don't support save restore */
- if (bus->ci->pmurev < 17)
- return false;
-
- switch (bus->ci->chip) {
- case BCM43241_CHIP_ID:
- case BCM4335_CHIP_ID:
- case BCM4339_CHIP_ID:
- /* read PMU chipcontrol register 3 */
- addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr);
- brcmf_sdiod_regwl(bus->sdiodev, addr, 3, NULL);
- addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data);
- reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL);
- return (reg & pmu_cc3_mask) != 0;
- default:
- addr = CORE_CC_REG(bus->ci->c_inf[0].base, pmucapabilities_ext);
- reg = brcmf_sdiod_regrl(bus->sdiodev, addr, &err);
- if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0)
- return false;
-
- addr = CORE_CC_REG(bus->ci->c_inf[0].base, retention_ctl);
- reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL);
- return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
- PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
- }
-}
-
static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
{
int err = 0;
@@ -3424,7 +3413,7 @@ static int brcmf_sdio_kso_init(struct brcmf_sdio *bus)
brcmf_dbg(TRACE, "Enter\n");
/* KSO bit added in SDIO core rev 12 */
- if (bus->ci->c_inf[1].rev < 12)
+ if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12)
return 0;
val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err);
@@ -3455,15 +3444,13 @@ static int brcmf_sdio_bus_preinit(struct device *dev)
struct brcmf_sdio *bus = sdiodev->bus;
uint pad_size;
u32 value;
- u8 idx;
int err;
/* the commands below use the terms tx and rx from
* a device perspective, ie. bus:txglom affects the
* bus transfers from device to host.
*/
- idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
- if (bus->ci->c_inf[idx].rev < 12) {
+ if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) {
/* for sdio core rev < 12, disable txgloming */
value = 0;
err = brcmf_iovar_data_set(dev, "bus:txglom", &value,
@@ -3570,7 +3557,7 @@ static int brcmf_sdio_bus_init(struct device *dev)
ret = -ENODEV;
}
- if (brcmf_sdio_sr_capable(bus)) {
+ if (brcmf_chip_sr_capable(bus->ci)) {
brcmf_sdio_sr_init(bus);
} else {
/* Restore previous clock setting */
@@ -3714,11 +3701,175 @@ static void brcmf_sdio_dataworker(struct work_struct *work)
datawork);
while (atomic_read(&bus->dpc_tskcnt)) {
+ atomic_set(&bus->dpc_tskcnt, 0);
brcmf_sdio_dpc(bus);
- atomic_dec(&bus->dpc_tskcnt);
}
}
+static void
+brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
+ struct brcmf_chip *ci, u32 drivestrength)
+{
+ const struct sdiod_drive_str *str_tab = NULL;
+ u32 str_mask;
+ u32 str_shift;
+ u32 base;
+ u32 i;
+ u32 drivestrength_sel = 0;
+ u32 cc_data_temp;
+ u32 addr;
+
+ if (!(ci->cc_caps & CC_CAP_PMU))
+ return;
+
+ switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
+ case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12):
+ str_tab = sdiod_drvstr_tab1_1v8;
+ str_mask = 0x00003800;
+ str_shift = 11;
+ break;
+ case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17):
+ str_tab = sdiod_drvstr_tab6_1v8;
+ str_mask = 0x00001800;
+ str_shift = 11;
+ break;
+ case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17):
+ /* note: 43143 does not support tristate */
+ i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
+ if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
+ str_tab = sdiod_drvstr_tab2_3v3;
+ str_mask = 0x00000007;
+ str_shift = 0;
+ } else
+ brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
+ ci->name, drivestrength);
+ break;
+ case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13):
+ str_tab = sdiod_drive_strength_tab5_1v8;
+ str_mask = 0x00003800;
+ str_shift = 11;
+ break;
+ default:
+ brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
+ ci->name, ci->chiprev, ci->pmurev);
+ break;
+ }
+
+ if (str_tab != NULL) {
+ for (i = 0; str_tab[i].strength != 0; i++) {
+ if (drivestrength >= str_tab[i].strength) {
+ drivestrength_sel = str_tab[i].sel;
+ break;
+ }
+ }
+ base = brcmf_chip_get_chipcommon(ci)->base;
+ addr = CORE_CC_REG(base, chipcontrol_addr);
+ brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
+ cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
+ cc_data_temp &= ~str_mask;
+ drivestrength_sel <<= str_shift;
+ cc_data_temp |= drivestrength_sel;
+ brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL);
+
+ brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n",
+ str_tab[i].strength, drivestrength, cc_data_temp);
+ }
+}
+
+static int brcmf_sdio_buscoreprep(void *ctx)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+ int err = 0;
+ u8 clkval, clkset;
+
+ /* Try forcing SDIO core to do ALPAvail request only */
+ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+ if (err) {
+ brcmf_err("error writing for HT off\n");
+ return err;
+ }
+
+ /* If register supported, wait for ALPAvail and then force ALP */
+ /* This may take up to 15 milliseconds */
+ clkval = brcmf_sdiod_regrb(sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, NULL);
+
+ if ((clkval & ~SBSDIO_AVBITS) != clkset) {
+ brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
+ clkset, clkval);
+ return -EACCES;
+ }
+
+ SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev,
+ SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
+ !SBSDIO_ALPAV(clkval)),
+ PMU_MAX_TRANSITION_DLY);
+ if (!SBSDIO_ALPAV(clkval)) {
+ brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
+ clkval);
+ return -EBUSY;
+ }
+
+ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+ udelay(65);
+
+ /* Also, disable the extra SDIO pull-ups */
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
+
+ return 0;
+}
+
+static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip,
+ u32 rstvec)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+ struct brcmf_core *core;
+ u32 reg_addr;
+
+ /* clear all interrupts */
+ core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV);
+ reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus);
+ brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
+
+ if (rstvec)
+ /* Write reset vector to address 0 */
+ brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec,
+ sizeof(rstvec));
+}
+
+static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+ u32 val, rev;
+
+ val = brcmf_sdiod_regrl(sdiodev, addr, NULL);
+ if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
+ addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) {
+ rev = (val & CID_REV_MASK) >> CID_REV_SHIFT;
+ if (rev >= 2) {
+ val &= ~CID_ID_MASK;
+ val |= BCM4339_CHIP_ID;
+ }
+ }
+ return val;
+}
+
+static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val)
+{
+ struct brcmf_sdio_dev *sdiodev = ctx;
+
+ brcmf_sdiod_regwl(sdiodev, addr, val, NULL);
+}
+
+static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = {
+ .prepare = brcmf_sdio_buscoreprep,
+ .exit_dl = brcmf_sdio_buscore_exitdl,
+ .read32 = brcmf_sdio_buscore_read32,
+ .write32 = brcmf_sdio_buscore_write32,
+};
+
static bool
brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
{
@@ -3734,7 +3885,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL));
/*
- * Force PLL off until brcmf_sdio_chip_attach()
+ * Force PLL off until brcmf_chip_attach()
* programs PLL control regs
*/
@@ -3755,8 +3906,10 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
*/
brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN);
- if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci)) {
- brcmf_err("brcmf_sdio_chip_attach failed!\n");
+ bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops);
+ if (IS_ERR(bus->ci)) {
+ brcmf_err("brcmf_chip_attach failed!\n");
+ bus->ci = NULL;
goto fail;
}
@@ -3769,7 +3922,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
drivestrength = bus->sdiodev->pdata->drive_strength;
else
drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
- brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);
+ brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);
/* Get info on the SOCRAM cores... */
bus->ramsize = bus->ci->ramsize;
@@ -3792,24 +3945,18 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
goto fail;
/* set PMUControl so a backplane reset does PMU state reload */
- reg_addr = CORE_CC_REG(bus->ci->c_inf[0].base,
+ reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base,
pmucontrol);
- reg_val = brcmf_sdiod_regrl(bus->sdiodev,
- reg_addr,
- &err);
+ reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err);
if (err)
goto fail;
reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT);
- brcmf_sdiod_regwl(bus->sdiodev,
- reg_addr,
- reg_val,
- &err);
+ brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err);
if (err)
goto fail;
-
sdio_release_host(bus->sdiodev->func[1]);
brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
@@ -3849,6 +3996,7 @@ brcmf_sdio_watchdog_thread(void *data)
brcmf_sdio_bus_watchdog(bus);
/* Count the tick for reference */
bus->sdcnt.tickcnt++;
+ reinit_completion(&bus->watchdog_wait);
} else
break;
}
@@ -3925,7 +4073,8 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
}
spin_lock_init(&bus->rxctl_lock);
- spin_lock_init(&bus->txqlock);
+ spin_lock_init(&bus->txq_lock);
+ sema_init(&bus->tx_seq_lock, 1);
init_waitqueue_head(&bus->ctrl_wait);
init_waitqueue_head(&bus->dcmd_resp_wait);
@@ -4024,14 +4173,14 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
/* De-register interrupt handler */
brcmf_sdiod_intr_unregister(bus->sdiodev);
- cancel_work_sync(&bus->datawork);
- if (bus->brcmf_wq)
- destroy_workqueue(bus->brcmf_wq);
-
if (bus->sdiodev->bus_if->drvr) {
brcmf_detach(bus->sdiodev->dev);
}
+ cancel_work_sync(&bus->datawork);
+ if (bus->brcmf_wq)
+ destroy_workqueue(bus->brcmf_wq);
+
if (bus->ci) {
if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) {
sdio_claim_host(bus->sdiodev->func[1]);
@@ -4042,12 +4191,11 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
* all necessary cores.
*/
msleep(20);
- brcmf_sdio_chip_enter_download(bus->sdiodev,
- bus->ci);
+ brcmf_chip_enter_download(bus->ci);
brcmf_sdio_clkctl(bus, CLK_NONE, false);
sdio_release_host(bus->sdiodev->func[1]);
}
- brcmf_sdio_chip_detach(&bus->ci);
+ brcmf_chip_detach(bus->ci);
}
kfree(bus->rxbuf);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
index 22adbe311d20..59a5af5bf994 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
@@ -124,7 +124,8 @@ brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data)
}
static u32
-brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen)
+brcmf_create_iovar(char *name, const char *data, u32 datalen,
+ char *buf, u32 buflen)
{
u32 len;
@@ -144,7 +145,7 @@ brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen)
s32
-brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data,
+brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
u32 len)
{
struct brcmf_pub *drvr = ifp->drvr;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h
index 77eae86e55c2..a30be683f4a1 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h
@@ -83,7 +83,7 @@ s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len);
s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data);
s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data);
-s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data,
+s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
u32 len);
s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
u32 len);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
index af17a5bc8b83..614e4888504f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
@@ -48,6 +48,11 @@
#define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */
+/* OBSS Coex Auto/On/Off */
+#define BRCMF_OBSS_COEX_AUTO (-1)
+#define BRCMF_OBSS_COEX_OFF 0
+#define BRCMF_OBSS_COEX_ON 1
+
enum brcmf_fil_p2p_if_types {
BRCMF_FIL_P2P_IF_CLIENT,
BRCMF_FIL_P2P_IF_GO,
@@ -87,6 +92,11 @@ struct brcmf_fil_bss_enable_le {
__le32 enable;
};
+struct brcmf_fil_bwcap_le {
+ __le32 band;
+ __le32 bw_cap;
+};
+
/**
* struct tdls_iovar - common structure for tdls iovars.
*
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
index fc4f98b275d7..f3445ac627e4 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
@@ -797,7 +797,8 @@ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg,
/* SOCIAL CHANNELS 1, 6, 11 */
search_state = WL_P2P_DISC_ST_SEARCH;
brcmf_dbg(INFO, "P2P SEARCH PHASE START\n");
- } else if (dev != NULL && vif->mode == WL_MODE_AP) {
+ } else if (dev != NULL &&
+ vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
/* If you are already a GO, then do SEARCH only */
brcmf_dbg(INFO, "Already a GO. Do SEARCH Only\n");
search_state = WL_P2P_DISC_ST_SEARCH;
@@ -2256,7 +2257,6 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
struct brcmf_cfg80211_vif *vif;
enum brcmf_fil_p2p_if_types iftype;
- enum wl_mode mode;
int err;
if (brcmf_cfg80211_vif_event_armed(cfg))
@@ -2267,11 +2267,9 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
switch (type) {
case NL80211_IFTYPE_P2P_CLIENT:
iftype = BRCMF_FIL_P2P_IF_CLIENT;
- mode = WL_MODE_BSS;
break;
case NL80211_IFTYPE_P2P_GO:
iftype = BRCMF_FIL_P2P_IF_GO;
- mode = WL_MODE_AP;
break;
case NL80211_IFTYPE_P2P_DEVICE:
return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
deleted file mode 100644
index 82bf3c5d3cdc..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
+++ /dev/null
@@ -1,972 +0,0 @@
-/*
- * Copyright (c) 2011 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.
- */
-/* ***** SDIO interface chip backplane handle functions ***** */
-
-#include <linux/types.h>
-#include <linux/netdevice.h>
-#include <linux/mmc/card.h>
-#include <linux/mmc/sdio_func.h>
-#include <linux/mmc/sdio_ids.h>
-#include <linux/ssb/ssb_regs.h>
-#include <linux/bcma/bcma.h>
-
-#include <chipcommon.h>
-#include <brcm_hw_ids.h>
-#include <brcmu_wifi.h>
-#include <brcmu_utils.h>
-#include <soc.h>
-#include "dhd_dbg.h"
-#include "sdio_host.h"
-#include "sdio_chip.h"
-
-/* chip core base & ramsize */
-/* bcm4329 */
-/* SDIO device core, ID 0x829 */
-#define BCM4329_CORE_BUS_BASE 0x18011000
-/* internal memory core, ID 0x80e */
-#define BCM4329_CORE_SOCRAM_BASE 0x18003000
-/* ARM Cortex M3 core, ID 0x82a */
-#define BCM4329_CORE_ARM_BASE 0x18002000
-#define BCM4329_RAMSIZE 0x48000
-
-/* bcm43143 */
-/* SDIO device core */
-#define BCM43143_CORE_BUS_BASE 0x18002000
-/* internal memory core */
-#define BCM43143_CORE_SOCRAM_BASE 0x18004000
-/* ARM Cortex M3 core, ID 0x82a */
-#define BCM43143_CORE_ARM_BASE 0x18003000
-#define BCM43143_RAMSIZE 0x70000
-
-/* All D11 cores, ID 0x812 */
-#define BCM43xx_CORE_D11_BASE 0x18001000
-
-#define SBCOREREV(sbidh) \
- ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \
- ((sbidh) & SSB_IDHIGH_RCLO))
-
-/* SOC Interconnect types (aka chip types) */
-#define SOCI_SB 0
-#define SOCI_AI 1
-
-/* EROM CompIdentB */
-#define CIB_REV_MASK 0xff000000
-#define CIB_REV_SHIFT 24
-
-/* ARM CR4 core specific control flag bits */
-#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020
-
-/* D11 core specific control flag bits */
-#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004
-#define D11_BCMA_IOCTL_PHYRESET 0x0008
-
-#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
-/* SDIO Pad drive strength to select value mappings */
-struct sdiod_drive_str {
- u8 strength; /* Pad Drive Strength in mA */
- u8 sel; /* Chip-specific select value */
-};
-/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
-static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
- {32, 0x6},
- {26, 0x7},
- {22, 0x4},
- {16, 0x5},
- {12, 0x2},
- {8, 0x3},
- {4, 0x0},
- {0, 0x1}
-};
-
-/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */
-static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = {
- {6, 0x7},
- {5, 0x6},
- {4, 0x5},
- {3, 0x4},
- {2, 0x2},
- {1, 0x1},
- {0, 0x0}
-};
-
-/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */
-static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = {
- {3, 0x3},
- {2, 0x2},
- {1, 0x1},
- {0, 0x0} };
-
-/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */
-static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
- {16, 0x7},
- {12, 0x5},
- {8, 0x3},
- {4, 0x1}
-};
-
-u8
-brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid)
-{
- u8 idx;
-
- for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++)
- if (coreid == ci->c_inf[idx].id)
- return idx;
-
- return BRCMF_MAX_CORENUM;
-}
-
-static u32
-brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u16 coreid)
-{
- u32 regdata;
- u8 idx;
-
- idx = brcmf_sdio_chip_getinfidx(ci, coreid);
-
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(ci->c_inf[idx].base, sbidhigh),
- NULL);
- return SBCOREREV(regdata);
-}
-
-static u32
-brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u16 coreid)
-{
- u8 idx;
-
- idx = brcmf_sdio_chip_getinfidx(ci, coreid);
-
- return (ci->c_inf[idx].cib & CIB_REV_MASK) >> CIB_REV_SHIFT;
-}
-
-static bool
-brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u16 coreid)
-{
- u32 regdata;
- u8 idx;
-
- idx = brcmf_sdio_chip_getinfidx(ci, coreid);
- if (idx == BRCMF_MAX_CORENUM)
- return false;
-
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
- NULL);
- regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
- SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK);
- return (SSB_TMSLOW_CLOCK == regdata);
-}
-
-static bool
-brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u16 coreid)
-{
- u32 regdata;
- u8 idx;
- bool ret;
-
- idx = brcmf_sdio_chip_getinfidx(ci, coreid);
- if (idx == BRCMF_MAX_CORENUM)
- return false;
-
- regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
- NULL);
- ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK;
-
- regdata = brcmf_sdiod_regrl(sdiodev,
- ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
- NULL);
- ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0);
-
- return ret;
-}
-
-static void
-brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits,
- u32 in_resetbits)
-{
- u32 regdata, base;
- u8 idx;
-
- idx = brcmf_sdio_chip_getinfidx(ci, coreid);
- base = ci->c_inf[idx].base;
-
- regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
- if (regdata & SSB_TMSLOW_RESET)
- return;
-
- regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
- if ((regdata & SSB_TMSLOW_CLOCK) != 0) {
- /*
- * set target reject and spin until busy is clear
- * (preserve core-specific bits)
- */
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(base, sbtmstatelow), NULL);
- brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
- regdata | SSB_TMSLOW_REJECT, NULL);
-
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(base, sbtmstatelow), NULL);
- udelay(1);
- SPINWAIT((brcmf_sdiod_regrl(sdiodev,
- CORE_SB(base, sbtmstatehigh),
- NULL) &
- SSB_TMSHIGH_BUSY), 100000);
-
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(base, sbtmstatehigh),
- NULL);
- if (regdata & SSB_TMSHIGH_BUSY)
- brcmf_err("core state still busy\n");
-
- regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow),
- NULL);
- if (regdata & SSB_IDLOW_INITIATOR) {
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(base, sbimstate),
- NULL);
- regdata |= SSB_IMSTATE_REJECT;
- brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate),
- regdata, NULL);
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(base, sbimstate),
- NULL);
- udelay(1);
- SPINWAIT((brcmf_sdiod_regrl(sdiodev,
- CORE_SB(base, sbimstate),
- NULL) &
- SSB_IMSTATE_BUSY), 100000);
- }
-
- /* set reset and reject while enabling the clocks */
- regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
- SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET;
- brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
- regdata, NULL);
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(base, sbtmstatelow), NULL);
- udelay(10);
-
- /* clear the initiator reject bit */
- regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow),
- NULL);
- if (regdata & SSB_IDLOW_INITIATOR) {
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(base, sbimstate),
- NULL);
- regdata &= ~SSB_IMSTATE_REJECT;
- brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate),
- regdata, NULL);
- }
- }
-
- /* leave reset and reject asserted */
- brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
- (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL);
- udelay(1);
-}
-
-static void
-brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits,
- u32 in_resetbits)
-{
- u8 idx;
- u32 regdata;
- u32 wrapbase;
-
- idx = brcmf_sdio_chip_getinfidx(ci, coreid);
- if (idx == BRCMF_MAX_CORENUM)
- return;
-
- wrapbase = ci->c_inf[idx].wrapbase;
-
- /* if core is already in reset, just return */
- regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL);
- if ((regdata & BCMA_RESET_CTL_RESET) != 0)
- return;
-
- /* configure reset */
- brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits |
- BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
- regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL);
-
- /* put in reset */
- brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL,
- BCMA_RESET_CTL_RESET, NULL);
- usleep_range(10, 20);
-
- /* wait till reset is 1 */
- SPINWAIT(brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) !=
- BCMA_RESET_CTL_RESET, 300);
-
- /* post reset configure */
- brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits |
- BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
- regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL);
-}
-
-static void
-brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits,
- u32 in_resetbits, u32 post_resetbits)
-{
- u32 regdata;
- u8 idx;
-
- idx = brcmf_sdio_chip_getinfidx(ci, coreid);
- if (idx == BRCMF_MAX_CORENUM)
- return;
-
- /*
- * Must do the disable sequence first to work for
- * arbitrary current core state.
- */
- brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, pre_resetbits,
- in_resetbits);
-
- /*
- * Now do the initialization sequence.
- * set reset while enabling the clock and
- * forcing them on throughout the core
- */
- brcmf_sdiod_regwl(sdiodev,
- CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
- SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET,
- NULL);
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
- NULL);
- udelay(1);
-
- /* clear any serror */
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
- NULL);
- if (regdata & SSB_TMSHIGH_SERR)
- brcmf_sdiod_regwl(sdiodev,
- CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
- 0, NULL);
-
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(ci->c_inf[idx].base, sbimstate),
- NULL);
- if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO))
- brcmf_sdiod_regwl(sdiodev,
- CORE_SB(ci->c_inf[idx].base, sbimstate),
- regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO),
- NULL);
-
- /* clear reset and allow it to propagate throughout the core */
- brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
- SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL);
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
- NULL);
- udelay(1);
-
- /* leave clock enabled */
- brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
- SSB_TMSLOW_CLOCK, NULL);
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
- NULL);
- udelay(1);
-}
-
-static void
-brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits,
- u32 in_resetbits, u32 post_resetbits)
-{
- u8 idx;
- u32 regdata;
- u32 wrapbase;
-
- idx = brcmf_sdio_chip_getinfidx(ci, coreid);
- if (idx == BRCMF_MAX_CORENUM)
- return;
-
- wrapbase = ci->c_inf[idx].wrapbase;
-
- /* must disable first to work for arbitrary current core state */
- brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, pre_resetbits,
- in_resetbits);
-
- while (brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) &
- BCMA_RESET_CTL_RESET) {
- brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL, 0, NULL);
- usleep_range(40, 60);
- }
-
- brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, post_resetbits |
- BCMA_IOCTL_CLK, NULL);
- regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL);
-}
-
-#ifdef DEBUG
-/* safety check for chipinfo */
-static int brcmf_sdio_chip_cichk(struct brcmf_chip *ci)
-{
- u8 core_idx;
-
- /* check RAM core presence for ARM CM3 core */
- core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
- if (BRCMF_MAX_CORENUM != core_idx) {
- core_idx = brcmf_sdio_chip_getinfidx(ci,
- BCMA_CORE_INTERNAL_MEM);
- if (BRCMF_MAX_CORENUM == core_idx) {
- brcmf_err("RAM core not provided with ARM CM3 core\n");
- return -ENODEV;
- }
- }
-
- /* check RAM base for ARM CR4 core */
- core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4);
- if (BRCMF_MAX_CORENUM != core_idx) {
- if (ci->rambase == 0) {
- brcmf_err("RAM base not provided with ARM CR4 core\n");
- return -ENOMEM;
- }
- }
-
- return 0;
-}
-#else /* DEBUG */
-static inline int brcmf_sdio_chip_cichk(struct brcmf_chip *ci)
-{
- return 0;
-}
-#endif
-
-static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci)
-{
- u32 regdata;
- u32 socitype;
-
- /* Get CC core rev
- * Chipid is assume to be at offset 0 from SI_ENUM_BASE
- * For different chiptypes or old sdio hosts w/o chipcommon,
- * other ways of recognition should be added here.
- */
- regdata = brcmf_sdiod_regrl(sdiodev,
- CORE_CC_REG(SI_ENUM_BASE, chipid),
- NULL);
- ci->chip = regdata & CID_ID_MASK;
- ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
- if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
- ci->chiprev >= 2)
- ci->chip = BCM4339_CHIP_ID;
- socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
-
- brcmf_dbg(INFO, "found %s chip: id=0x%x, rev=%d\n",
- socitype == SOCI_SB ? "SB" : "AXI", ci->chip, ci->chiprev);
-
- if (socitype == SOCI_SB) {
- if (ci->chip != BCM4329_CHIP_ID) {
- brcmf_err("SB chip is not supported\n");
- return -ENODEV;
- }
- ci->iscoreup = brcmf_sdio_sb_iscoreup;
- ci->corerev = brcmf_sdio_sb_corerev;
- ci->coredisable = brcmf_sdio_sb_coredisable;
- ci->resetcore = brcmf_sdio_sb_resetcore;
-
- ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON;
- ci->c_inf[0].base = SI_ENUM_BASE;
- ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
- ci->c_inf[1].base = BCM4329_CORE_BUS_BASE;
- ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
- ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE;
- ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
- ci->c_inf[3].base = BCM4329_CORE_ARM_BASE;
- ci->c_inf[4].id = BCMA_CORE_80211;
- ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
- ci->ramsize = BCM4329_RAMSIZE;
- } else if (socitype == SOCI_AI) {
- ci->iscoreup = brcmf_sdio_ai_iscoreup;
- ci->corerev = brcmf_sdio_ai_corerev;
- ci->coredisable = brcmf_sdio_ai_coredisable;
- ci->resetcore = brcmf_sdio_ai_resetcore;
-
- ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON;
- ci->c_inf[0].base = SI_ENUM_BASE;
-
- /* Address of cores for new chips should be added here */
- switch (ci->chip) {
- case BCM43143_CHIP_ID:
- ci->c_inf[0].wrapbase = ci->c_inf[0].base + 0x00100000;
- ci->c_inf[0].cib = 0x2b000000;
- ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
- ci->c_inf[1].base = BCM43143_CORE_BUS_BASE;
- ci->c_inf[1].wrapbase = ci->c_inf[1].base + 0x00100000;
- ci->c_inf[1].cib = 0x18000000;
- ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
- ci->c_inf[2].base = BCM43143_CORE_SOCRAM_BASE;
- ci->c_inf[2].wrapbase = ci->c_inf[2].base + 0x00100000;
- ci->c_inf[2].cib = 0x14000000;
- ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
- ci->c_inf[3].base = BCM43143_CORE_ARM_BASE;
- ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000;
- ci->c_inf[3].cib = 0x07000000;
- ci->c_inf[4].id = BCMA_CORE_80211;
- ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
- ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000;
- ci->ramsize = BCM43143_RAMSIZE;
- break;
- case BCM43241_CHIP_ID:
- ci->c_inf[0].wrapbase = 0x18100000;
- ci->c_inf[0].cib = 0x2a084411;
- ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
- ci->c_inf[1].base = 0x18002000;
- ci->c_inf[1].wrapbase = 0x18102000;
- ci->c_inf[1].cib = 0x0e004211;
- ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
- ci->c_inf[2].base = 0x18004000;
- ci->c_inf[2].wrapbase = 0x18104000;
- ci->c_inf[2].cib = 0x14080401;
- ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
- ci->c_inf[3].base = 0x18003000;
- ci->c_inf[3].wrapbase = 0x18103000;
- ci->c_inf[3].cib = 0x07004211;
- ci->c_inf[4].id = BCMA_CORE_80211;
- ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
- ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000;
- ci->ramsize = 0x90000;
- break;
- case BCM4330_CHIP_ID:
- ci->c_inf[0].wrapbase = 0x18100000;
- ci->c_inf[0].cib = 0x27004211;
- ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
- ci->c_inf[1].base = 0x18002000;
- ci->c_inf[1].wrapbase = 0x18102000;
- ci->c_inf[1].cib = 0x07004211;
- ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
- ci->c_inf[2].base = 0x18004000;
- ci->c_inf[2].wrapbase = 0x18104000;
- ci->c_inf[2].cib = 0x0d080401;
- ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
- ci->c_inf[3].base = 0x18003000;
- ci->c_inf[3].wrapbase = 0x18103000;
- ci->c_inf[3].cib = 0x03004211;
- ci->c_inf[4].id = BCMA_CORE_80211;
- ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
- ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000;
- ci->ramsize = 0x48000;
- break;
- case BCM4334_CHIP_ID:
- ci->c_inf[0].wrapbase = 0x18100000;
- ci->c_inf[0].cib = 0x29004211;
- ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
- ci->c_inf[1].base = 0x18002000;
- ci->c_inf[1].wrapbase = 0x18102000;
- ci->c_inf[1].cib = 0x0d004211;
- ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
- ci->c_inf[2].base = 0x18004000;
- ci->c_inf[2].wrapbase = 0x18104000;
- ci->c_inf[2].cib = 0x13080401;
- ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
- ci->c_inf[3].base = 0x18003000;
- ci->c_inf[3].wrapbase = 0x18103000;
- ci->c_inf[3].cib = 0x07004211;
- ci->c_inf[4].id = BCMA_CORE_80211;
- ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
- ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000;
- ci->ramsize = 0x80000;
- break;
- case BCM4335_CHIP_ID:
- ci->c_inf[0].wrapbase = 0x18100000;
- ci->c_inf[0].cib = 0x2b084411;
- ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
- ci->c_inf[1].base = 0x18005000;
- ci->c_inf[1].wrapbase = 0x18105000;
- ci->c_inf[1].cib = 0x0f004211;
- ci->c_inf[2].id = BCMA_CORE_ARM_CR4;
- ci->c_inf[2].base = 0x18002000;
- ci->c_inf[2].wrapbase = 0x18102000;
- ci->c_inf[2].cib = 0x01084411;
- ci->c_inf[3].id = BCMA_CORE_80211;
- ci->c_inf[3].base = BCM43xx_CORE_D11_BASE;
- ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000;
- ci->ramsize = 0xc0000;
- ci->rambase = 0x180000;
- break;
- case BCM43362_CHIP_ID:
- ci->c_inf[0].wrapbase = 0x18100000;
- ci->c_inf[0].cib = 0x27004211;
- ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
- ci->c_inf[1].base = 0x18002000;
- ci->c_inf[1].wrapbase = 0x18102000;
- ci->c_inf[1].cib = 0x0a004211;
- ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
- ci->c_inf[2].base = 0x18004000;
- ci->c_inf[2].wrapbase = 0x18104000;
- ci->c_inf[2].cib = 0x08080401;
- ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
- ci->c_inf[3].base = 0x18003000;
- ci->c_inf[3].wrapbase = 0x18103000;
- ci->c_inf[3].cib = 0x03004211;
- ci->c_inf[4].id = BCMA_CORE_80211;
- ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
- ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000;
- ci->ramsize = 0x3C000;
- break;
- case BCM4339_CHIP_ID:
- ci->c_inf[0].wrapbase = 0x18100000;
- ci->c_inf[0].cib = 0x2e084411;
- ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
- ci->c_inf[1].base = 0x18005000;
- ci->c_inf[1].wrapbase = 0x18105000;
- ci->c_inf[1].cib = 0x15004211;
- ci->c_inf[2].id = BCMA_CORE_ARM_CR4;
- ci->c_inf[2].base = 0x18002000;
- ci->c_inf[2].wrapbase = 0x18102000;
- ci->c_inf[2].cib = 0x04084411;
- ci->c_inf[3].id = BCMA_CORE_80211;
- ci->c_inf[3].base = BCM43xx_CORE_D11_BASE;
- ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000;
- ci->ramsize = 0xc0000;
- ci->rambase = 0x180000;
- break;
- default:
- brcmf_err("AXI chip is not supported\n");
- return -ENODEV;
- }
- } else {
- brcmf_err("chip backplane type %u is not supported\n",
- socitype);
- return -ENODEV;
- }
-
- return brcmf_sdio_chip_cichk(ci);
-}
-
-static int
-brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev)
-{
- int err = 0;
- u8 clkval, clkset;
-
- /* Try forcing SDIO core to do ALPAvail request only */
- clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
- if (err) {
- brcmf_err("error writing for HT off\n");
- return err;
- }
-
- /* If register supported, wait for ALPAvail and then force ALP */
- /* This may take up to 15 milliseconds */
- clkval = brcmf_sdiod_regrb(sdiodev,
- SBSDIO_FUNC1_CHIPCLKCSR, NULL);
-
- if ((clkval & ~SBSDIO_AVBITS) != clkset) {
- brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
- clkset, clkval);
- return -EACCES;
- }
-
- SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev,
- SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
- !SBSDIO_ALPAV(clkval)),
- PMU_MAX_TRANSITION_DLY);
- if (!SBSDIO_ALPAV(clkval)) {
- brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
- clkval);
- return -EBUSY;
- }
-
- clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
- udelay(65);
-
- /* Also, disable the extra SDIO pull-ups */
- brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
-
- return 0;
-}
-
-static void
-brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci)
-{
- u32 base = ci->c_inf[0].base;
-
- /* get chipcommon rev */
- ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id);
-
- /* get chipcommon capabilites */
- ci->c_inf[0].caps = brcmf_sdiod_regrl(sdiodev,
- CORE_CC_REG(base, capabilities),
- NULL);
-
- /* get pmu caps & rev */
- if (ci->c_inf[0].caps & CC_CAP_PMU) {
- ci->pmucaps =
- brcmf_sdiod_regrl(sdiodev,
- CORE_CC_REG(base, pmucapabilities),
- NULL);
- ci->pmurev = ci->pmucaps & PCAP_REV_MASK;
- }
-
- ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id);
-
- brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n",
- ci->c_inf[0].rev, ci->pmurev,
- ci->c_inf[1].rev, ci->c_inf[1].id);
-
- /*
- * Make sure any on-chip ARM is off (in case strapping is wrong),
- * or downloaded code was already running.
- */
- ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0);
-}
-
-int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip **ci_ptr)
-{
- int ret;
- struct brcmf_chip *ci;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- ci = kzalloc(sizeof(*ci), GFP_ATOMIC);
- if (!ci)
- return -ENOMEM;
-
- ret = brcmf_sdio_chip_buscoreprep(sdiodev);
- if (ret != 0)
- goto err;
-
- ret = brcmf_sdio_chip_recognition(sdiodev, ci);
- if (ret != 0)
- goto err;
-
- brcmf_sdio_chip_buscoresetup(sdiodev, ci);
-
- brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup),
- 0, NULL);
- brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown),
- 0, NULL);
-
- *ci_ptr = ci;
- return 0;
-
-err:
- kfree(ci);
- return ret;
-}
-
-void
-brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr)
-{
- brcmf_dbg(TRACE, "Enter\n");
-
- kfree(*ci_ptr);
- *ci_ptr = NULL;
-}
-
-static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len)
-{
- const char *fmt;
-
- fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
- snprintf(buf, len, fmt, chipid);
- return buf;
-}
-
-void
-brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u32 drivestrength)
-{
- const struct sdiod_drive_str *str_tab = NULL;
- u32 str_mask;
- u32 str_shift;
- char chn[8];
- u32 base = ci->c_inf[0].base;
- u32 i;
- u32 drivestrength_sel = 0;
- u32 cc_data_temp;
- u32 addr;
-
- if (!(ci->c_inf[0].caps & CC_CAP_PMU))
- return;
-
- switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
- case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12):
- str_tab = sdiod_drvstr_tab1_1v8;
- str_mask = 0x00003800;
- str_shift = 11;
- break;
- case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17):
- str_tab = sdiod_drvstr_tab6_1v8;
- str_mask = 0x00001800;
- str_shift = 11;
- break;
- case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17):
- /* note: 43143 does not support tristate */
- i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
- if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
- str_tab = sdiod_drvstr_tab2_3v3;
- str_mask = 0x00000007;
- str_shift = 0;
- } else
- brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
- brcmf_sdio_chip_name(ci->chip, chn, 8),
- drivestrength);
- break;
- case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13):
- str_tab = sdiod_drive_strength_tab5_1v8;
- str_mask = 0x00003800;
- str_shift = 11;
- break;
- default:
- brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
- brcmf_sdio_chip_name(ci->chip, chn, 8),
- ci->chiprev, ci->pmurev);
- break;
- }
-
- if (str_tab != NULL) {
- for (i = 0; str_tab[i].strength != 0; i++) {
- if (drivestrength >= str_tab[i].strength) {
- drivestrength_sel = str_tab[i].sel;
- break;
- }
- }
- addr = CORE_CC_REG(base, chipcontrol_addr);
- brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
- cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
- cc_data_temp &= ~str_mask;
- drivestrength_sel <<= str_shift;
- cc_data_temp |= drivestrength_sel;
- brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL);
-
- brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n",
- str_tab[i].strength, drivestrength, cc_data_temp);
- }
-}
-
-static void
-brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci)
-{
- ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0);
- ci->resetcore(sdiodev, ci, BCMA_CORE_80211,
- D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN,
- D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN);
- ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0, 0, 0);
-}
-
-static bool brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci)
-{
- u8 core_idx;
- u32 reg_addr;
-
- if (!ci->iscoreup(sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) {
- brcmf_err("SOCRAM core is down after reset?\n");
- return false;
- }
-
- /* clear all interrupts */
- core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
- reg_addr = ci->c_inf[core_idx].base;
- reg_addr += offsetof(struct sdpcmd_regs, intstatus);
- brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
-
- ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0, 0);
-
- return true;
-}
-
-static inline void
-brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci)
-{
- u8 idx;
- u32 regdata;
- u32 wrapbase;
- idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4);
-
- if (idx == BRCMF_MAX_CORENUM)
- return;
-
- wrapbase = ci->c_inf[idx].wrapbase;
- regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL);
- regdata &= ARMCR4_BCMA_IOCTL_CPUHALT;
- ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, regdata,
- ARMCR4_BCMA_IOCTL_CPUHALT, ARMCR4_BCMA_IOCTL_CPUHALT);
- ci->resetcore(sdiodev, ci, BCMA_CORE_80211,
- D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN,
- D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN);
-}
-
-static bool brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u32 rstvec)
-{
- u8 core_idx;
- u32 reg_addr;
-
- /* clear all interrupts */
- core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
- reg_addr = ci->c_inf[core_idx].base;
- reg_addr += offsetof(struct sdpcmd_regs, intstatus);
- brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
-
- /* Write reset vector to address 0 */
- brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec,
- sizeof(rstvec));
-
- /* restore ARM */
- ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, ARMCR4_BCMA_IOCTL_CPUHALT,
- 0, 0);
-
- return true;
-}
-
-void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci)
-{
- u8 arm_core_idx;
-
- arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
- if (BRCMF_MAX_CORENUM != arm_core_idx) {
- brcmf_sdio_chip_cm3_enterdl(sdiodev, ci);
- return;
- }
-
- brcmf_sdio_chip_cr4_enterdl(sdiodev, ci);
-}
-
-bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u32 rstvec)
-{
- u8 arm_core_idx;
-
- arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
- if (BRCMF_MAX_CORENUM != arm_core_idx)
- return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci);
-
- return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, rstvec);
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h
deleted file mode 100644
index fb0614329ede..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (c) 2011 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_SDIO_CHIP_H_
-#define _BRCMFMAC_SDIO_CHIP_H_
-
-/*
- * Core reg address translation.
- * Both macro's returns a 32 bits byte address on the backplane bus.
- */
-#define CORE_CC_REG(base, field) \
- (base + offsetof(struct chipcregs, field))
-#define CORE_BUS_REG(base, field) \
- (base + offsetof(struct sdpcmd_regs, field))
-#define CORE_SB(base, field) \
- (base + SBCONFIGOFF + offsetof(struct sbconfig, field))
-
-/* SDIO function 1 register CHIPCLKCSR */
-/* Force ALP request to backplane */
-#define SBSDIO_FORCE_ALP 0x01
-/* Force HT request to backplane */
-#define SBSDIO_FORCE_HT 0x02
-/* Force ILP request to backplane */
-#define SBSDIO_FORCE_ILP 0x04
-/* Make ALP ready (power up xtal) */
-#define SBSDIO_ALP_AVAIL_REQ 0x08
-/* Make HT ready (power up PLL) */
-#define SBSDIO_HT_AVAIL_REQ 0x10
-/* Squelch clock requests from HW */
-#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20
-/* Status: ALP is ready */
-#define SBSDIO_ALP_AVAIL 0x40
-/* Status: HT is ready */
-#define SBSDIO_HT_AVAIL 0x80
-#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
-#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS)
-#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
-#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
-#define SBSDIO_CLKAV(regval, alponly) \
- (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval)))
-
-#define BRCMF_MAX_CORENUM 6
-
-struct brcmf_core {
- u16 id;
- u16 rev;
- u32 base;
- u32 wrapbase;
- u32 caps;
- u32 cib;
-};
-
-struct brcmf_chip {
- u32 chip;
- u32 chiprev;
- /* core info */
- /* always put chipcommon core at 0, bus core at 1 */
- struct brcmf_core c_inf[BRCMF_MAX_CORENUM];
- u32 pmurev;
- u32 pmucaps;
- u32 ramsize;
- u32 rambase;
- u32 rst_vec; /* reset vertor for ARM CR4 core */
-
- bool (*iscoreup)(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci,
- u16 coreid);
- u32 (*corerev)(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci,
- u16 coreid);
- void (*coredisable)(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits,
- u32 in_resetbits);
- void (*resetcore)(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits,
- u32 in_resetbits, u32 post_resetbits);
-};
-
-struct sbconfig {
- u32 PAD[2];
- u32 sbipsflag; /* initiator port ocp slave flag */
- u32 PAD[3];
- u32 sbtpsflag; /* target port ocp slave flag */
- u32 PAD[11];
- u32 sbtmerrloga; /* (sonics >= 2.3) */
- u32 PAD;
- u32 sbtmerrlog; /* (sonics >= 2.3) */
- u32 PAD[3];
- u32 sbadmatch3; /* address match3 */
- u32 PAD;
- u32 sbadmatch2; /* address match2 */
- u32 PAD;
- u32 sbadmatch1; /* address match1 */
- u32 PAD[7];
- u32 sbimstate; /* initiator agent state */
- u32 sbintvec; /* interrupt mask */
- u32 sbtmstatelow; /* target state */
- u32 sbtmstatehigh; /* target state */
- u32 sbbwa0; /* bandwidth allocation table0 */
- u32 PAD;
- u32 sbimconfiglow; /* initiator configuration */
- u32 sbimconfighigh; /* initiator configuration */
- u32 sbadmatch0; /* address match0 */
- u32 PAD;
- u32 sbtmconfiglow; /* target configuration */
- u32 sbtmconfighigh; /* target configuration */
- u32 sbbconfig; /* broadcast configuration */
- u32 PAD;
- u32 sbbstate; /* broadcast state */
- u32 PAD[3];
- u32 sbactcnfg; /* activate configuration */
- u32 PAD[3];
- u32 sbflagst; /* current sbflags */
- u32 PAD[3];
- u32 sbidlow; /* identification */
- u32 sbidhigh; /* identification */
-};
-
-/* sdio core registers */
-struct sdpcmd_regs {
- u32 corecontrol; /* 0x00, rev8 */
- u32 corestatus; /* rev8 */
- u32 PAD[1];
- u32 biststatus; /* rev8 */
-
- /* PCMCIA access */
- u16 pcmciamesportaladdr; /* 0x010, rev8 */
- u16 PAD[1];
- u16 pcmciamesportalmask; /* rev8 */
- u16 PAD[1];
- u16 pcmciawrframebc; /* rev8 */
- u16 PAD[1];
- u16 pcmciaunderflowtimer; /* rev8 */
- u16 PAD[1];
-
- /* interrupt */
- u32 intstatus; /* 0x020, rev8 */
- u32 hostintmask; /* rev8 */
- u32 intmask; /* rev8 */
- u32 sbintstatus; /* rev8 */
- u32 sbintmask; /* rev8 */
- u32 funcintmask; /* rev4 */
- u32 PAD[2];
- u32 tosbmailbox; /* 0x040, rev8 */
- u32 tohostmailbox; /* rev8 */
- u32 tosbmailboxdata; /* rev8 */
- u32 tohostmailboxdata; /* rev8 */
-
- /* synchronized access to registers in SDIO clock domain */
- u32 sdioaccess; /* 0x050, rev8 */
- u32 PAD[3];
-
- /* PCMCIA frame control */
- u8 pcmciaframectrl; /* 0x060, rev8 */
- u8 PAD[3];
- u8 pcmciawatermark; /* rev8 */
- u8 PAD[155];
-
- /* interrupt batching control */
- u32 intrcvlazy; /* 0x100, rev8 */
- u32 PAD[3];
-
- /* counters */
- u32 cmd52rd; /* 0x110, rev8 */
- u32 cmd52wr; /* rev8 */
- u32 cmd53rd; /* rev8 */
- u32 cmd53wr; /* rev8 */
- u32 abort; /* rev8 */
- u32 datacrcerror; /* rev8 */
- u32 rdoutofsync; /* rev8 */
- u32 wroutofsync; /* rev8 */
- u32 writebusy; /* rev8 */
- u32 readwait; /* rev8 */
- u32 readterm; /* rev8 */
- u32 writeterm; /* rev8 */
- u32 PAD[40];
- u32 clockctlstatus; /* rev8 */
- u32 PAD[7];
-
- u32 PAD[128]; /* DMA engines */
-
- /* SDIO/PCMCIA CIS region */
- char cis[512]; /* 0x400-0x5ff, rev6 */
-
- /* PCMCIA function control registers */
- char pcmciafcr[256]; /* 0x600-6ff, rev6 */
- u16 PAD[55];
-
- /* PCMCIA backplane access */
- u16 backplanecsr; /* 0x76E, rev6 */
- u16 backplaneaddr0; /* rev6 */
- u16 backplaneaddr1; /* rev6 */
- u16 backplaneaddr2; /* rev6 */
- u16 backplaneaddr3; /* rev6 */
- u16 backplanedata0; /* rev6 */
- u16 backplanedata1; /* rev6 */
- u16 backplanedata2; /* rev6 */
- u16 backplanedata3; /* rev6 */
- u16 PAD[31];
-
- /* sprom "size" & "blank" info */
- u16 spromstatus; /* 0x7BE, rev2 */
- u32 PAD[464];
-
- u16 PAD[0x80];
-};
-
-int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip **ci_ptr);
-void brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr);
-void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci,
- u32 drivestrength);
-u8 brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid);
-void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci);
-bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
- struct brcmf_chip *ci, u32 rstvec);
-
-#endif /* _BRCMFMAC_SDIO_CHIP_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
index 092e9c824992..3deab7959a0d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
@@ -180,6 +180,97 @@ struct brcmf_sdio_dev {
uint max_request_size;
ushort max_segment_count;
uint max_segment_size;
+ uint txglomsz;
+ struct sg_table sgtable;
+};
+
+/* sdio core registers */
+struct sdpcmd_regs {
+ u32 corecontrol; /* 0x00, rev8 */
+ u32 corestatus; /* rev8 */
+ u32 PAD[1];
+ u32 biststatus; /* rev8 */
+
+ /* PCMCIA access */
+ u16 pcmciamesportaladdr; /* 0x010, rev8 */
+ u16 PAD[1];
+ u16 pcmciamesportalmask; /* rev8 */
+ u16 PAD[1];
+ u16 pcmciawrframebc; /* rev8 */
+ u16 PAD[1];
+ u16 pcmciaunderflowtimer; /* rev8 */
+ u16 PAD[1];
+
+ /* interrupt */
+ u32 intstatus; /* 0x020, rev8 */
+ u32 hostintmask; /* rev8 */
+ u32 intmask; /* rev8 */
+ u32 sbintstatus; /* rev8 */
+ u32 sbintmask; /* rev8 */
+ u32 funcintmask; /* rev4 */
+ u32 PAD[2];
+ u32 tosbmailbox; /* 0x040, rev8 */
+ u32 tohostmailbox; /* rev8 */
+ u32 tosbmailboxdata; /* rev8 */
+ u32 tohostmailboxdata; /* rev8 */
+
+ /* synchronized access to registers in SDIO clock domain */
+ u32 sdioaccess; /* 0x050, rev8 */
+ u32 PAD[3];
+
+ /* PCMCIA frame control */
+ u8 pcmciaframectrl; /* 0x060, rev8 */
+ u8 PAD[3];
+ u8 pcmciawatermark; /* rev8 */
+ u8 PAD[155];
+
+ /* interrupt batching control */
+ u32 intrcvlazy; /* 0x100, rev8 */
+ u32 PAD[3];
+
+ /* counters */
+ u32 cmd52rd; /* 0x110, rev8 */
+ u32 cmd52wr; /* rev8 */
+ u32 cmd53rd; /* rev8 */
+ u32 cmd53wr; /* rev8 */
+ u32 abort; /* rev8 */
+ u32 datacrcerror; /* rev8 */
+ u32 rdoutofsync; /* rev8 */
+ u32 wroutofsync; /* rev8 */
+ u32 writebusy; /* rev8 */
+ u32 readwait; /* rev8 */
+ u32 readterm; /* rev8 */
+ u32 writeterm; /* rev8 */
+ u32 PAD[40];
+ u32 clockctlstatus; /* rev8 */
+ u32 PAD[7];
+
+ u32 PAD[128]; /* DMA engines */
+
+ /* SDIO/PCMCIA CIS region */
+ char cis[512]; /* 0x400-0x5ff, rev6 */
+
+ /* PCMCIA function control registers */
+ char pcmciafcr[256]; /* 0x600-6ff, rev6 */
+ u16 PAD[55];
+
+ /* PCMCIA backplane access */
+ u16 backplanecsr; /* 0x76E, rev6 */
+ u16 backplaneaddr0; /* rev6 */
+ u16 backplaneaddr1; /* rev6 */
+ u16 backplaneaddr2; /* rev6 */
+ u16 backplaneaddr3; /* rev6 */
+ u16 backplanedata0; /* rev6 */
+ u16 backplanedata1; /* rev6 */
+ u16 backplanedata2; /* rev6 */
+ u16 backplanedata3; /* rev6 */
+ u16 PAD[31];
+
+ /* sprom "size" & "blank" info */
+ u16 spromstatus; /* 0x7BE, rev2 */
+ u32 PAD[464];
+
+ u16 PAD[0x80];
};
/* Register/deregister interrupt handler. */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index d7718a5fa2f0..afb3d15e38ff 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -18,6 +18,7 @@
#include <linux/kernel.h>
#include <linux/etherdevice.h>
+#include <linux/module.h>
#include <net/cfg80211.h>
#include <net/netlink.h>
@@ -190,6 +191,7 @@ static struct ieee80211_supported_band __wl_band_2ghz = {
.n_channels = ARRAY_SIZE(__wl_2ghz_channels),
.bitrates = wl_g_rates,
.n_bitrates = wl_g_rates_size,
+ .ht_cap = {IEEE80211_HT_CAP_SUP_WIDTH_20_40, true},
};
static struct ieee80211_supported_band __wl_band_5ghz_a = {
@@ -251,6 +253,10 @@ 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");
+
/* Quarter dBm units to mW
* Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
* Table is offset so the last entry is largest mW value that fits in
@@ -351,13 +357,11 @@ u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
* triples, returning a pointer to the substring whose first element
* matches tag
*/
-struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
+const struct brcmf_tlv *
+brcmf_parse_tlvs(const void *buf, int buflen, uint key)
{
- struct brcmf_tlv *elt;
- int totlen;
-
- elt = (struct brcmf_tlv *)buf;
- totlen = buflen;
+ const struct brcmf_tlv *elt = buf;
+ int totlen = buflen;
/* find tagged parameter */
while (totlen >= TLV_HDR_LEN) {
@@ -378,8 +382,8 @@ struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
* not update the tlvs buffer pointer/length.
*/
static bool
-brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len,
- u8 *oui, u32 oui_len, u8 type)
+brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
+ const u8 *oui, u32 oui_len, u8 type)
{
/* If the contents match the OUI and the type */
if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
@@ -401,12 +405,12 @@ brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len,
}
static struct brcmf_vs_tlv *
-brcmf_find_wpaie(u8 *parse, u32 len)
+brcmf_find_wpaie(const u8 *parse, u32 len)
{
- struct brcmf_tlv *ie;
+ const struct brcmf_tlv *ie;
while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
- if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
+ if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
return (struct brcmf_vs_tlv *)ie;
}
@@ -414,9 +418,9 @@ brcmf_find_wpaie(u8 *parse, u32 len)
}
static struct brcmf_vs_tlv *
-brcmf_find_wpsie(u8 *parse, u32 len)
+brcmf_find_wpsie(const u8 *parse, u32 len)
{
- struct brcmf_tlv *ie;
+ const struct brcmf_tlv *ie;
while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
@@ -491,6 +495,19 @@ brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
return err;
}
+static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
+{
+ enum nl80211_iftype iftype;
+
+ iftype = vif->wdev.iftype;
+ return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
+}
+
+static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
+{
+ return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
+}
+
static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
const char *name,
enum nl80211_iftype type,
@@ -651,7 +668,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
type);
return -EOPNOTSUPP;
case NL80211_IFTYPE_ADHOC:
- vif->mode = WL_MODE_IBSS;
infra = 0;
break;
case NL80211_IFTYPE_STATION:
@@ -667,12 +683,10 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
*/
return 0;
}
- vif->mode = WL_MODE_BSS;
infra = 1;
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
- vif->mode = WL_MODE_AP;
ap = 1;
break;
default:
@@ -696,7 +710,7 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
err = -EAGAIN;
goto done;
}
- brcmf_dbg(INFO, "IF Type = %s\n", (vif->mode == WL_MODE_IBSS) ?
+ brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
"Adhoc" : "Infra");
}
ndev->ieee80211_ptr->iftype = type;
@@ -1340,13 +1354,14 @@ static s32 brcmf_set_auth_type(struct net_device *ndev,
}
static s32
-brcmf_set_set_cipher(struct net_device *ndev,
- struct cfg80211_connect_params *sme)
+brcmf_set_wsec_mode(struct net_device *ndev,
+ struct cfg80211_connect_params *sme, bool mfp)
{
struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
struct brcmf_cfg80211_security *sec;
s32 pval = 0;
s32 gval = 0;
+ s32 wsec;
s32 err = 0;
if (sme->crypto.n_ciphers_pairwise) {
@@ -1398,7 +1413,12 @@ brcmf_set_set_cipher(struct net_device *ndev,
if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
sme->privacy)
pval = AES_ENABLED;
- err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", pval | gval);
+
+ if (mfp)
+ wsec = pval | gval | MFP_CAPABLE;
+ else
+ wsec = pval | gval;
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
if (err) {
brcmf_err("error (%d)\n", err);
return err;
@@ -1562,13 +1582,12 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
struct ieee80211_channel *chan = sme->channel;
struct brcmf_join_params join_params;
size_t join_params_size;
- struct brcmf_tlv *rsn_ie;
- struct brcmf_vs_tlv *wpa_ie;
- void *ie;
+ const struct brcmf_tlv *rsn_ie;
+ const struct brcmf_vs_tlv *wpa_ie;
+ const void *ie;
u32 ie_len;
struct brcmf_ext_join_params_le *ext_join_params;
u16 chanspec;
-
s32 err = 0;
brcmf_dbg(TRACE, "Enter\n");
@@ -1591,7 +1610,8 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
ie_len = wpa_ie->len + TLV_HDR_LEN;
} else {
/* find the RSN_IE */
- rsn_ie = brcmf_parse_tlvs((u8 *)sme->ie, sme->ie_len,
+ rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
+ sme->ie_len,
WLAN_EID_RSN);
if (rsn_ie) {
ie = rsn_ie;
@@ -1636,7 +1656,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
goto done;
}
- err = brcmf_set_set_cipher(ndev, sme);
+ err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
if (err) {
brcmf_err("wl_set_set_cipher failed (%d)\n", err);
goto done;
@@ -1678,22 +1698,9 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
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);
- /*increase dwell time to receive probe response or detect Beacon
- * from target AP at a noisy air only during connect command
- */
- ext_join_params->scan_le.active_time =
- cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
- ext_join_params->scan_le.passive_time =
- cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
+
/* Set up join scan parameters */
ext_join_params->scan_le.scan_type = -1;
- /* to sync with presence period of VSDB GO.
- * Send probe request more frequently. Probe request will be stopped
- * when it gets probe response from target AP/GO.
- */
- ext_join_params->scan_le.nprobes =
- cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
- BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
ext_join_params->scan_le.home_time = cpu_to_le32(-1);
if (sme->bssid)
@@ -1706,6 +1713,25 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
ext_join_params->assoc_le.chanspec_list[0] =
cpu_to_le16(chanspec);
+ /* Increase dwell time to receive probe response or detect
+ * beacon from target AP at a noisy air only during connect
+ * command.
+ */
+ ext_join_params->scan_le.active_time =
+ cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
+ ext_join_params->scan_le.passive_time =
+ cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
+ /* To sync with presence period of VSDB GO send probe request
+ * more frequently. Probe request will be stopped when it gets
+ * probe response from target AP/GO.
+ */
+ ext_join_params->scan_le.nprobes =
+ cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
+ BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
+ } else {
+ ext_join_params->scan_le.active_time = cpu_to_le32(-1);
+ ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
+ ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
}
err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
@@ -1913,7 +1939,7 @@ brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
memcpy(key.data, params->key, key.len);
- if ((ifp->vif->mode != WL_MODE_AP) &&
+ if (!brcmf_is_apmode(ifp->vif) &&
(params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
memcpy(keybuf, &key.data[24], sizeof(keybuf));
@@ -1981,7 +2007,9 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
if (!check_vif_up(ifp->vif))
return -EIO;
- if (mac_addr) {
+ if (mac_addr &&
+ (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
+ (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
brcmf_dbg(TRACE, "Exit");
return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
}
@@ -2010,7 +2038,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
break;
case WLAN_CIPHER_SUITE_TKIP:
- if (ifp->vif->mode != WL_MODE_AP) {
+ if (!brcmf_is_apmode(ifp->vif)) {
brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
memcpy(keybuf, &key.data[24], sizeof(keybuf));
memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
@@ -2164,12 +2192,14 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
s32 err = 0;
u8 *bssid = profile->bssid;
struct brcmf_sta_info_le sta_info_le;
+ u32 beacon_period;
+ u32 dtim_period;
brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
if (!check_vif_up(ifp->vif))
return -EIO;
- if (ifp->vif->mode == WL_MODE_AP) {
+ if (brcmf_is_apmode(ifp->vif)) {
memcpy(&sta_info_le, mac, ETH_ALEN);
err = brcmf_fil_iovar_data_get(ifp, "sta_info",
&sta_info_le,
@@ -2186,7 +2216,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
}
brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n",
sinfo->inactive_time, sinfo->connected_time);
- } else if (ifp->vif->mode == WL_MODE_BSS) {
+ } else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) {
if (memcmp(mac, bssid, ETH_ALEN)) {
brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
mac, bssid);
@@ -2218,6 +2248,30 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
sinfo->signal = rssi;
brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
}
+ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD,
+ &beacon_period);
+ if (err) {
+ brcmf_err("Could not get beacon period (%d)\n",
+ err);
+ goto done;
+ } else {
+ sinfo->bss_param.beacon_interval =
+ beacon_period;
+ brcmf_dbg(CONN, "Beacon peroid %d\n",
+ beacon_period);
+ }
+ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD,
+ &dtim_period);
+ if (err) {
+ brcmf_err("Could not get DTIM period (%d)\n",
+ err);
+ goto done;
+ } else {
+ sinfo->bss_param.dtim_period = dtim_period;
+ brcmf_dbg(CONN, "DTIM peroid %d\n",
+ dtim_period);
+ }
+ sinfo->filled |= STATION_INFO_BSS_PARAM;
}
} else
err = -EPERM;
@@ -2444,18 +2498,13 @@ CleanUp:
return err;
}
-static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
-{
- return vif->mode == WL_MODE_IBSS;
-}
-
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;
- struct brcmf_tlv *tim;
+ const struct brcmf_tlv *tim;
u16 beacon_interval;
u8 dtim_period;
size_t ie_len;
@@ -3220,8 +3269,9 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
}
static s32
-brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
- bool is_rsn_ie)
+brcmf_configure_wpaie(struct net_device *ndev,
+ const struct brcmf_vs_tlv *wpa_ie,
+ bool is_rsn_ie)
{
struct brcmf_if *ifp = netdev_priv(ndev);
u32 auth = 0; /* d11 open authentication */
@@ -3707,11 +3757,11 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
s32 ie_offset;
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_tlv *ssid_ie;
+ const struct brcmf_tlv *ssid_ie;
struct brcmf_ssid_le ssid_le;
s32 err = -EPERM;
- struct brcmf_tlv *rsn_ie;
- struct brcmf_vs_tlv *wpa_ie;
+ const struct brcmf_tlv *rsn_ie;
+ const struct brcmf_vs_tlv *wpa_ie;
struct brcmf_join_params join_params;
enum nl80211_iftype dev_role;
struct brcmf_fil_bss_enable_le bss_enable;
@@ -4220,32 +4270,6 @@ static struct cfg80211_ops wl_cfg80211_ops = {
CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode)
};
-static s32 brcmf_nl80211_iftype_to_mode(enum nl80211_iftype type)
-{
- switch (type) {
- case NL80211_IFTYPE_AP_VLAN:
- case NL80211_IFTYPE_WDS:
- case NL80211_IFTYPE_MONITOR:
- case NL80211_IFTYPE_MESH_POINT:
- return -ENOTSUPP;
- case NL80211_IFTYPE_ADHOC:
- return WL_MODE_IBSS;
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_P2P_CLIENT:
- return WL_MODE_BSS;
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_P2P_GO:
- return WL_MODE_AP;
- case NL80211_IFTYPE_P2P_DEVICE:
- return WL_MODE_P2P;
- case NL80211_IFTYPE_UNSPECIFIED:
- default:
- break;
- }
-
- return -EINVAL;
-}
-
static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
{
/* scheduled scan settings */
@@ -4370,7 +4394,6 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
vif->wdev.wiphy = cfg->wiphy;
vif->wdev.iftype = type;
- vif->mode = brcmf_nl80211_iftype_to_mode(type);
vif->pm_block = pm_block;
vif->roam_off = -1;
@@ -4416,7 +4439,9 @@ static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
u32 event = e->event_code;
u16 flags = e->flags;
- if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) {
+ if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
+ (event == BRCMF_E_DISASSOC_IND) ||
+ ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
brcmf_dbg(CONN, "Processing link down\n");
return true;
}
@@ -4658,16 +4683,19 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
struct net_device *ndev = ifp->ndev;
struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+ struct ieee80211_channel *chan;
s32 err = 0;
+ u16 reason;
- if (ifp->vif->mode == WL_MODE_AP) {
+ if (brcmf_is_apmode(ifp->vif)) {
err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
} else if (brcmf_is_linkup(e)) {
brcmf_dbg(CONN, "Linkup\n");
if (brcmf_is_ibssmode(ifp->vif)) {
+ 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, GFP_KERNEL);
+ cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
clear_bit(BRCMF_VIF_STATUS_CONNECTING,
&ifp->vif->sme_state);
set_bit(BRCMF_VIF_STATUS_CONNECTED,
@@ -4679,9 +4707,15 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
if (!brcmf_is_ibssmode(ifp->vif)) {
brcmf_bss_connect_done(cfg, ndev, e, false);
if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED,
- &ifp->vif->sme_state))
- cfg80211_disconnected(ndev, 0, NULL, 0,
+ &ifp->vif->sme_state)) {
+ reason = 0;
+ if (((e->event_code == BRCMF_E_DEAUTH_IND) ||
+ (e->event_code == BRCMF_E_DISASSOC_IND)) &&
+ (e->reason != WLAN_REASON_UNSPECIFIED))
+ reason = e->reason;
+ cfg80211_disconnected(ndev, reason, NULL, 0,
GFP_KERNEL);
+ }
}
brcmf_link_down(ifp->vif);
brcmf_init_prof(ndev_to_prof(ndev));
@@ -4875,11 +4909,8 @@ static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
cfg->scan_request = NULL;
cfg->pwr_save = true;
- cfg->roam_on = true; /* roam on & off switch.
- we enable roam per default */
- cfg->active_scan = true; /* we do active scan for
- specific scan per default */
- cfg->dongle_up = false; /* dongle is not up yet */
+ cfg->active_scan = true; /* we do active scan per default */
+ cfg->dongle_up = false; /* dongle is not up yet */
err = brcmf_init_priv_mem(cfg);
if (err)
return err;
@@ -4904,6 +4935,30 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
mutex_init(&event->vif_event_lock);
}
+static int brcmf_enable_bw40_2g(struct brcmf_if *ifp)
+{
+ struct brcmf_fil_bwcap_le band_bwcap;
+ u32 val;
+ int err;
+
+ /* verify support for bw_cap command */
+ val = WLC_BAND_5G;
+ err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
+
+ if (!err) {
+ /* only set 2G bandwidth using bw_cap command */
+ band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
+ band_bwcap.bw_cap = cpu_to_le32(WLC_BW_40MHZ_BIT);
+ err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
+ sizeof(band_bwcap));
+ } else {
+ brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
+ val = WLC_N_BW_40ALL;
+ err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
+ }
+ return err;
+}
+
struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
struct device *busdev)
{
@@ -4961,6 +5016,17 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
goto cfg80211_p2p_attach_out;
}
+ /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
+ * setup 40MHz in 2GHz band and enable OBSS scanning.
+ */
+ if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap &
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+ err = brcmf_enable_bw40_2g(ifp);
+ if (!err)
+ err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
+ BRCMF_OBSS_COEX_AUTO);
+ }
+
err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
if (err) {
brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
@@ -4999,7 +5065,7 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
}
static s32
-brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout)
+brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
{
s32 err = 0;
__le32 roamtrigger[2];
@@ -5009,7 +5075,7 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout)
* Setup timeout if Beacons are lost and roam is
* off to report link down
*/
- if (roamvar) {
+ if (brcmf_roamoff) {
err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
if (err) {
brcmf_err("bcn_timeout error (%d)\n", err);
@@ -5021,8 +5087,9 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout)
* Enable/Disable built-in roaming to allow supplicant
* to take care of roaming
*/
- brcmf_dbg(INFO, "Internal Roaming = %s\n", roamvar ? "Off" : "On");
- err = brcmf_fil_iovar_int_set(ifp, "roam_off", roamvar);
+ brcmf_dbg(INFO, "Internal Roaming = %s\n",
+ brcmf_roamoff ? "Off" : "On");
+ err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
if (err) {
brcmf_err("roam_off error (%d)\n", err);
goto dongle_rom_out;
@@ -5164,9 +5231,6 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
ieee80211_channel_to_frequency(ch.chnum, band);
band_chan_arr[index].hw_value = ch.chnum;
- brcmf_err("channel %d: f=%d bw=%d sb=%d\n",
- ch.chnum, band_chan_arr[index].center_freq,
- ch.bw, ch.sb);
if (ch.bw == BRCMU_CHAN_BW_40) {
/* assuming the order is HT20, HT40 Upper,
* HT40 lower from chanspecs
@@ -5267,6 +5331,8 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
u32 band_list[3];
u32 nmode;
u32 bw_cap[2] = { 0, 0 };
+ u32 rxchain;
+ u32 nchain;
s8 phy;
s32 err;
u32 nband;
@@ -5303,6 +5369,16 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
brcmf_dbg(INFO, "nmode=%d, bw_cap=(%d, %d)\n", nmode,
bw_cap[IEEE80211_BAND_2GHZ], bw_cap[IEEE80211_BAND_5GHZ]);
+ err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
+ if (err) {
+ brcmf_err("rxchain error (%d)\n", err);
+ nchain = 1;
+ } else {
+ for (nchain = 0; rxchain; nchain++)
+ rxchain = rxchain & (rxchain - 1);
+ }
+ brcmf_dbg(INFO, "nchain=%d\n", nchain);
+
err = brcmf_construct_reginfo(cfg, bw_cap);
if (err) {
brcmf_err("brcmf_construct_reginfo failed (%d)\n", err);
@@ -5331,10 +5407,7 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
band->ht_cap.ht_supported = true;
band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
- /* An HT shall support all EQM rates for one spatial
- * stream
- */
- band->ht_cap.mcs.rx_mask[0] = 0xff;
+ memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
bands[band->band] = band;
}
@@ -5381,7 +5454,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, (cfg->roam_on ? 0 : 1), WL_BEACON_TIMEOUT);
+ err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT);
if (err)
goto default_conf_out;
err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
index 2dc6a074e8ed..283c525a44f7 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
@@ -89,21 +89,6 @@ enum brcmf_scan_status {
BRCMF_SCAN_STATUS_SUPPRESS,
};
-/**
- * enum wl_mode - driver mode of virtual interface.
- *
- * @WL_MODE_BSS: connects to BSS.
- * @WL_MODE_IBSS: operate as ad-hoc.
- * @WL_MODE_AP: operate as access-point.
- * @WL_MODE_P2P: provide P2P discovery.
- */
-enum wl_mode {
- WL_MODE_BSS,
- WL_MODE_IBSS,
- WL_MODE_AP,
- WL_MODE_P2P
-};
-
/* dongle configuration */
struct brcmf_cfg80211_conf {
u32 frag_threshold;
@@ -193,7 +178,6 @@ struct vif_saved_ie {
* @ifp: lower layer interface pointer
* @wdev: wireless device.
* @profile: profile information.
- * @mode: operating mode.
* @roam_off: roaming state.
* @sme_state: SME state using enum brcmf_vif_status bits.
* @pm_block: power-management blocked.
@@ -204,7 +188,6 @@ struct brcmf_cfg80211_vif {
struct brcmf_if *ifp;
struct wireless_dev wdev;
struct brcmf_cfg80211_profile profile;
- s32 mode;
s32 roam_off;
unsigned long sme_state;
bool pm_block;
@@ -402,7 +385,6 @@ struct brcmf_cfg80211_info {
bool ibss_starter;
bool pwr_save;
bool dongle_up;
- bool roam_on;
bool scan_tried;
u8 *dcmd_buf;
u8 *extra_buf;
@@ -491,7 +473,8 @@ void brcmf_free_vif(struct brcmf_cfg80211_vif *vif);
s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
const u8 *vndr_ie_buf, u32 vndr_ie_len);
s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif);
-struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key);
+const struct brcmf_tlv *
+brcmf_parse_tlvs(const void *buf, int buflen, uint key);
u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
struct ieee80211_channel *ch);
u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index 925034b80e9c..8c5fa4e58139 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -426,6 +426,12 @@ static int brcms_ops_start(struct ieee80211_hw *hw)
bool blocked;
int err;
+ if (!wl->ucode.bcm43xx_bomminor) {
+ err = brcms_request_fw(wl, wl->wlc->hw->d11core);
+ if (err)
+ return -ENOENT;
+ }
+
ieee80211_wake_queues(hw);
spin_lock_bh(&wl->lock);
blocked = brcms_rfkill_set_hw_state(wl);
@@ -433,14 +439,6 @@ static int brcms_ops_start(struct ieee80211_hw *hw)
if (!blocked)
wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
- if (!wl->ucode.bcm43xx_bomminor) {
- err = brcms_request_fw(wl, wl->wlc->hw->d11core);
- if (err) {
- brcms_remove(wl->wlc->hw->d11core);
- return -ENOENT;
- }
- }
-
spin_lock_bh(&wl->lock);
/* avoid acknowledging frames before a non-monitor device is added */
wl->mute_tx = true;
@@ -1094,12 +1092,6 @@ static int ieee_hw_init(struct ieee80211_hw *hw)
* Attach to the WL device identified by vendor and device parameters.
* regs is a host accessible memory address pointing to WL device registers.
*
- * brcms_attach is not defined as static because in the case where no bus
- * is defined, wl_attach will never be called, and thus, gcc will issue
- * a warning that this function is defined but not used if we declare
- * it as static.
- *
- *
* is called in brcms_bcma_probe() context, therefore no locking required.
*/
static struct brcms_info *brcms_attach(struct bcma_device *pdev)
diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
index 6fa5d4863782..d816270db3be 100644
--- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
+++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
@@ -43,5 +43,6 @@
#define BCM4335_CHIP_ID 0x4335
#define BCM43362_CHIP_ID 43362
#define BCM4339_CHIP_ID 0x4339
+#define BCM4354_CHIP_ID 0x4354
#endif /* _BRCM_HW_IDS_H_ */
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h
index 7ca2aa1035b2..74419d4bd123 100644
--- a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h
+++ b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h
@@ -217,6 +217,9 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec)
#define WSEC_SWFLAG 0x0008
/* to go into transition mode without setting wep */
#define SES_OW_ENABLED 0x0040
+/* MFP */
+#define MFP_CAPABLE 0x0200
+#define MFP_REQUIRED 0x0400
/* WPA authentication mode bitvec */
#define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */
diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c
index 5a9ffd3a6a6c..e23d67e0bfe6 100644
--- a/drivers/net/wireless/cw1200/fwio.c
+++ b/drivers/net/wireless/cw1200/fwio.c
@@ -202,8 +202,8 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
}
/* calculate the block size */
- tx_size = block_size = min((size_t)(firmware->size - put),
- (size_t)DOWNLOAD_BLOCK_SIZE);
+ tx_size = block_size = min_t(size_t, firmware->size - put,
+ DOWNLOAD_BLOCK_SIZE);
memcpy(buf, &firmware->data[put], block_size);
if (block_size < DOWNLOAD_BLOCK_SIZE) {
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
index 9f825f2620da..b6ec51923b20 100644
--- a/drivers/net/wireless/hostap/hostap_cs.c
+++ b/drivers/net/wireless/hostap/hostap_cs.c
@@ -677,6 +677,8 @@ static const struct pcmcia_device_id hostap_cs_ids[] = {
PCMCIA_DEVICE_PROD_ID12(
"ZoomAir 11Mbps High", "Rate wireless Networking",
0x273fe3db, 0x32a1eaee),
+ PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card",
+ 0xa37434e9, 0x9762e8f1),
PCMCIA_DEVICE_PROD_ID123(
"Pretec", "CompactWLAN Card 802.11b", "2.5",
0x1cadd3e5, 0xe697636c, 0x7a5bfcf1),
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index 3aba49259ef1..dfc6dfc56d52 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -7065,7 +7065,7 @@ static int ipw2100_wx_set_nick(struct net_device *dev,
if (wrqu->data.length > IW_ESSID_MAX_SIZE)
return -E2BIG;
- wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
+ wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick));
memset(priv->nick, 0, sizeof(priv->nick));
memcpy(priv->nick, extra, wrqu->data.length);
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c
index 139326065bd9..c5aa404069f3 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/ipw2x00/ipw2200.c
@@ -9169,7 +9169,7 @@ static int ipw_wx_set_nick(struct net_device *dev,
if (wrqu->data.length > IW_ESSID_MAX_SIZE)
return -E2BIG;
mutex_lock(&priv->mutex);
- wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
+ wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick));
memset(priv->nick, 0, sizeof(priv->nick));
memcpy(priv->nick, extra, wrqu->data.length);
IPW_DEBUG_TRACE("<<\n");
diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c
index 0487461ae4da..dc1d20cf64ee 100644
--- a/drivers/net/wireless/iwlegacy/3945-mac.c
+++ b/drivers/net/wireless/iwlegacy/3945-mac.c
@@ -1248,14 +1248,7 @@ il3945_rx_handle(struct il_priv *il)
len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK;
len += sizeof(u32); /* account for status word */
- /* Reclaim a command buffer only if this packet is a response
- * to a (driver-originated) command.
- * If the packet (e.g. Rx frame) originated from uCode,
- * there is no command buffer to reclaim.
- * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
- * but apparently a few don't get set; catch them here. */
- reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) &&
- pkt->hdr.cmd != N_STATS && pkt->hdr.cmd != C_TX;
+ reclaim = il_need_reclaim(il, pkt);
/* Based on type of command response or notification,
* handle those that need handling via function in
@@ -1495,12 +1488,14 @@ il3945_irq_tasklet(struct il_priv *il)
if (inta & CSR_INT_BIT_WAKEUP) {
D_ISR("Wakeup interrupt\n");
il_rx_queue_update_write_ptr(il, &il->rxq);
+
+ spin_lock_irqsave(&il->lock, flags);
il_txq_update_write_ptr(il, &il->txq[0]);
il_txq_update_write_ptr(il, &il->txq[1]);
il_txq_update_write_ptr(il, &il->txq[2]);
il_txq_update_write_ptr(il, &il->txq[3]);
il_txq_update_write_ptr(il, &il->txq[4]);
- il_txq_update_write_ptr(il, &il->txq[5]);
+ spin_unlock_irqrestore(&il->lock, flags);
il->isr_stats.wakeup++;
handled |= CSR_INT_BIT_WAKEUP;
diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/iwlegacy/3945-rs.c
index 9a45f6f626f6..76b0729ade17 100644
--- a/drivers/net/wireless/iwlegacy/3945-rs.c
+++ b/drivers/net/wireless/iwlegacy/3945-rs.c
@@ -891,8 +891,7 @@ il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
{
}
-static struct rate_control_ops rs_ops = {
- .module = NULL,
+static const struct rate_control_ops rs_ops = {
.name = RS_NAME,
.tx_status = il3945_rs_tx_status,
.get_rate = il3945_rs_get_rate,
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c
index 43f488a8cda2..888ad5c74639 100644
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/iwlegacy/4965-mac.c
@@ -92,7 +92,6 @@ il4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status)
* EEPROM
*/
struct il_mod_params il4965_mod_params = {
- .amsdu_size_8K = 1,
.restart_fw = 1,
/* the rest are 0 by default */
};
@@ -4274,17 +4273,7 @@ il4965_rx_handle(struct il_priv *il)
len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK;
len += sizeof(u32); /* account for status word */
- /* Reclaim a command buffer only if this packet is a response
- * to a (driver-originated) command.
- * If the packet (e.g. Rx frame) originated from uCode,
- * there is no command buffer to reclaim.
- * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
- * but apparently a few don't get set; catch them here. */
- reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) &&
- (pkt->hdr.cmd != N_RX_PHY) && (pkt->hdr.cmd != N_RX) &&
- (pkt->hdr.cmd != N_RX_MPDU) &&
- (pkt->hdr.cmd != N_COMPRESSED_BA) &&
- (pkt->hdr.cmd != N_STATS) && (pkt->hdr.cmd != C_TX);
+ reclaim = il_need_reclaim(il, pkt);
/* Based on type of command response or notification,
* handle those that need handling via function in
@@ -6876,6 +6865,6 @@ module_param_named(11n_disable, il4965_mod_params.disable_11n, int, S_IRUGO);
MODULE_PARM_DESC(11n_disable, "disable 11n functionality");
module_param_named(amsdu_size_8K, il4965_mod_params.amsdu_size_8K, int,
S_IRUGO);
-MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size");
+MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0 [disabled])");
module_param_named(fw_restart, il4965_mod_params.restart_fw, int, S_IRUGO);
MODULE_PARM_DESC(fw_restart, "restart firmware in case of error");
diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/iwlegacy/4965-rs.c
index 4d5e33259ca8..eaaeea19d8c5 100644
--- a/drivers/net/wireless/iwlegacy/4965-rs.c
+++ b/drivers/net/wireless/iwlegacy/4965-rs.c
@@ -2807,8 +2807,7 @@ il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
{
}
-static struct rate_control_ops rs_4965_ops = {
- .module = NULL,
+static const struct rate_control_ops rs_4965_ops = {
.name = IL4965_RS_NAME,
.tx_status = il4965_rs_tx_status,
.get_rate = il4965_rs_get_rate,
diff --git a/drivers/net/wireless/iwlegacy/commands.h b/drivers/net/wireless/iwlegacy/commands.h
index 048421511988..dd744135c956 100644
--- a/drivers/net/wireless/iwlegacy/commands.h
+++ b/drivers/net/wireless/iwlegacy/commands.h
@@ -2270,7 +2270,8 @@ struct il_spectrum_notification {
*/
#define IL_POWER_VEC_SIZE 5
-#define IL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0))
+#define IL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0))
+#define IL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2))
#define IL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3))
struct il3945_powertable_cmd {
diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c
index 02e8233ccf29..4f42174d9994 100644
--- a/drivers/net/wireless/iwlegacy/common.c
+++ b/drivers/net/wireless/iwlegacy/common.c
@@ -1078,29 +1078,82 @@ EXPORT_SYMBOL(il_get_channel_info);
* Setting power level allows the card to go to sleep when not busy.
*
* We calculate a sleep command based on the required latency, which
- * we get from mac80211. In order to handle thermal throttling, we can
- * also use pre-defined power levels.
+ * we get from mac80211.
*/
-/*
- * This defines the old power levels. They are still used by default
- * (level 1) and for thermal throttle (levels 3 through 5)
- */
-
-struct il_power_vec_entry {
- struct il_powertable_cmd cmd;
- u8 no_dtim; /* number of skip dtim */
-};
+#define SLP_VEC(X0, X1, X2, X3, X4) { \
+ cpu_to_le32(X0), \
+ cpu_to_le32(X1), \
+ cpu_to_le32(X2), \
+ cpu_to_le32(X3), \
+ cpu_to_le32(X4) \
+}
static void
-il_power_sleep_cam_cmd(struct il_priv *il, struct il_powertable_cmd *cmd)
+il_build_powertable_cmd(struct il_priv *il, struct il_powertable_cmd *cmd)
{
+ const __le32 interval[3][IL_POWER_VEC_SIZE] = {
+ SLP_VEC(2, 2, 4, 6, 0xFF),
+ SLP_VEC(2, 4, 7, 10, 10),
+ SLP_VEC(4, 7, 10, 10, 0xFF)
+ };
+ int i, dtim_period, no_dtim;
+ u32 max_sleep;
+ bool skip;
+
memset(cmd, 0, sizeof(*cmd));
if (il->power_data.pci_pm)
cmd->flags |= IL_POWER_PCI_PM_MSK;
- D_POWER("Sleep command for CAM\n");
+ /* if no Power Save, we are done */
+ if (il->power_data.ps_disabled)
+ return;
+
+ cmd->flags = IL_POWER_DRIVER_ALLOW_SLEEP_MSK;
+ cmd->keep_alive_seconds = 0;
+ cmd->debug_flags = 0;
+ cmd->rx_data_timeout = cpu_to_le32(25 * 1024);
+ cmd->tx_data_timeout = cpu_to_le32(25 * 1024);
+ cmd->keep_alive_beacons = 0;
+
+ dtim_period = il->vif ? il->vif->bss_conf.dtim_period : 0;
+
+ if (dtim_period <= 2) {
+ memcpy(cmd->sleep_interval, interval[0], sizeof(interval[0]));
+ no_dtim = 2;
+ } else if (dtim_period <= 10) {
+ memcpy(cmd->sleep_interval, interval[1], sizeof(interval[1]));
+ no_dtim = 2;
+ } else {
+ memcpy(cmd->sleep_interval, interval[2], sizeof(interval[2]));
+ no_dtim = 0;
+ }
+
+ if (dtim_period == 0) {
+ dtim_period = 1;
+ skip = false;
+ } else {
+ skip = !!no_dtim;
+ }
+
+ if (skip) {
+ __le32 tmp = cmd->sleep_interval[IL_POWER_VEC_SIZE - 1];
+
+ max_sleep = le32_to_cpu(tmp);
+ if (max_sleep == 0xFF)
+ max_sleep = dtim_period * (skip + 1);
+ else if (max_sleep > dtim_period)
+ max_sleep = (max_sleep / dtim_period) * dtim_period;
+ cmd->flags |= IL_POWER_SLEEP_OVER_DTIM_MSK;
+ } else {
+ max_sleep = dtim_period;
+ cmd->flags &= ~IL_POWER_SLEEP_OVER_DTIM_MSK;
+ }
+
+ for (i = 0; i < IL_POWER_VEC_SIZE; i++)
+ if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep)
+ cmd->sleep_interval[i] = cpu_to_le32(max_sleep);
}
static int
@@ -1173,7 +1226,8 @@ il_power_update_mode(struct il_priv *il, bool force)
{
struct il_powertable_cmd cmd;
- il_power_sleep_cam_cmd(il, &cmd);
+ il_build_powertable_cmd(il, &cmd);
+
return il_power_set_mode(il, &cmd, force);
}
EXPORT_SYMBOL(il_power_update_mode);
@@ -5081,6 +5135,7 @@ set_ch_out:
}
if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) {
+ il->power_data.ps_disabled = !(conf->flags & IEEE80211_CONF_PS);
ret = il_power_update_mode(il, false);
if (ret)
D_MAC80211("Error setting sleep level\n");
diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h
index ad123d66ab6c..dfb13c70efe8 100644
--- a/drivers/net/wireless/iwlegacy/common.h
+++ b/drivers/net/wireless/iwlegacy/common.h
@@ -1123,6 +1123,7 @@ struct il_power_mgr {
struct il_powertable_cmd sleep_cmd_next;
int debug_sleep_level_override;
bool pci_pm;
+ bool ps_disabled;
};
struct il_priv {
@@ -1597,7 +1598,7 @@ struct il_mod_params {
int disable_hw_scan; /* def: 0 = use h/w scan */
int num_of_queues; /* def: HW dependent */
int disable_11n; /* def: 0 = 11n capabilities enabled */
- int amsdu_size_8K; /* def: 1 = enable 8K amsdu size */
+ int amsdu_size_8K; /* def: 0 = disable 8K amsdu size */
int antenna; /* def: 0 = both antennas (use diversity) */
int restart_fw; /* def: 1 = restart firmware */
};
@@ -1978,6 +1979,20 @@ void il_wr_prph(struct il_priv *il, u32 addr, u32 val);
u32 il_read_targ_mem(struct il_priv *il, u32 addr);
void il_write_targ_mem(struct il_priv *il, u32 addr, u32 val);
+static inline bool il_need_reclaim(struct il_priv *il, struct il_rx_pkt *pkt)
+{
+ /* Reclaim a command buffer only if this packet is a response
+ * to a (driver-originated) command. If the packet (e.g. Rx frame)
+ * originated from uCode, there is no command buffer to reclaim.
+ * Ucode should set SEQ_RX_FRAME bit if ucode-originated, but
+ * apparently a few don't get set; catch them here.
+ */
+ return !(pkt->hdr.sequence & SEQ_RX_FRAME) &&
+ pkt->hdr.cmd != N_STATS && pkt->hdr.cmd != C_TX &&
+ pkt->hdr.cmd != N_RX_PHY && pkt->hdr.cmd != N_RX &&
+ pkt->hdr.cmd != N_RX_MPDU && pkt->hdr.cmd != N_COMPRESSED_BA;
+}
+
static inline void
_il_write8(struct il_priv *il, u32 ofs, u8 val)
{
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
index 3eb2102ce236..74b3b4de7bb7 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/iwlwifi/Kconfig
@@ -68,6 +68,19 @@ config IWLWIFI_OPMODE_MODULAR
comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM"
depends on IWLWIFI && IWLDVM=n && IWLMVM=n
+config IWLWIFI_BCAST_FILTERING
+ bool "Enable broadcast filtering"
+ depends on IWLMVM
+ help
+ Say Y here to enable default bcast filtering configuration.
+
+ Enabling broadcast filtering will drop any incoming wireless
+ broadcast frames, except some very specific predefined
+ patterns (e.g. incoming arp requests).
+
+ If unsure, don't enable this option, as some programs might
+ expect incoming broadcasts for their normal operations.
+
menu "Debugging Options"
depends on IWLWIFI
@@ -111,6 +124,7 @@ config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
Enable use of experimental ucode for testing and debugging.
config IWLWIFI_DEVICE_TRACING
+
bool "iwlwifi device access tracing"
depends on IWLWIFI
depends on EVENT_TRACING
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index 1fa64429bcc2..3d32f4120174 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/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
+iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o
iwlwifi-objs += $(iwlwifi-m)
diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h
index 562772d85102..c160dad03037 100644
--- a/drivers/net/wireless/iwlwifi/dvm/agn.h
+++ b/drivers/net/wireless/iwlwifi/dvm/agn.h
@@ -109,7 +109,7 @@ extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg;
struct iwl_ucode_capabilities;
-extern struct ieee80211_ops iwlagn_hw_ops;
+extern const struct ieee80211_ops iwlagn_hw_ops;
static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd)
{
@@ -480,7 +480,7 @@ do { \
} while (0)
#endif /* CONFIG_IWLWIFI_DEBUG */
-extern const char *iwl_dvm_cmd_strings[REPLY_MAX];
+extern const char *const iwl_dvm_cmd_strings[REPLY_MAX];
static inline const char *iwl_dvm_get_cmd_string(u8 cmd)
{
diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c
index 7b140e487deb..758c54eeb206 100644
--- a/drivers/net/wireless/iwlwifi/dvm/devices.c
+++ b/drivers/net/wireless/iwlwifi/dvm/devices.c
@@ -317,7 +317,7 @@ static const struct iwl_sensitivity_ranges iwl5000_sensitivity = {
.nrg_th_cca = 62,
};
-static struct iwl_sensitivity_ranges iwl5150_sensitivity = {
+static const struct iwl_sensitivity_ranges iwl5150_sensitivity = {
.min_nrg_cck = 95,
.auto_corr_min_ofdm = 90,
.auto_corr_min_ofdm_mrc = 170,
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index 73086c1629ca..dd55c9cf7ba8 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -1582,7 +1582,7 @@ static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw,
IWL_DEBUG_MAC80211(priv, "leave\n");
}
-struct ieee80211_ops iwlagn_hw_ops = {
+const struct ieee80211_ops iwlagn_hw_ops = {
.tx = iwlagn_mac_tx,
.start = iwlagn_mac_start,
.stop = iwlagn_mac_stop,
diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c
index ba1b1ea54252..6a6df71af1d7 100644
--- a/drivers/net/wireless/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/iwlwifi/dvm/main.c
@@ -252,13 +252,17 @@ static void iwl_bg_bt_runtime_config(struct work_struct *work)
struct iwl_priv *priv =
container_of(work, struct iwl_priv, bt_runtime_config);
+ mutex_lock(&priv->mutex);
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
+ goto out;
/* dont send host command if rf-kill is on */
if (!iwl_is_ready_rf(priv))
- return;
+ goto out;
+
iwlagn_send_advance_bt_config(priv);
+out:
+ mutex_unlock(&priv->mutex);
}
static void iwl_bg_bt_full_concurrency(struct work_struct *work)
@@ -2035,7 +2039,7 @@ static void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
ieee80211_free_txskb(priv->hw, skb);
}
-static void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
+static bool iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
{
struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
@@ -2045,6 +2049,8 @@ static void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
clear_bit(STATUS_RF_KILL_HW, &priv->status);
wiphy_rfkill_set_hw_state(priv->hw->wiphy, state);
+
+ return false;
}
static const struct iwl_op_mode_ops iwl_dvm_ops = {
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c
index 0977d93b529d..aa773a2da4ab 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rs.c
@@ -176,46 +176,46 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
* (2.4 GHz) band.
*/
-static s32 expected_tpt_legacy[IWL_RATE_COUNT] = {
+static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = {
7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0
};
-static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */
{0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */
{0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */
{0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */
};
-static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */
{0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */
{0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */
{0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */
};
-static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */
{0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */
{0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */
{0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/
};
-static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */
{0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */
{0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */
{0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */
};
-static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */
{0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */
{0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */
{0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */
};
-static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */
{0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */
{0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */
@@ -1111,7 +1111,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
struct iwl_scale_tbl_info *tbl)
{
/* Used to choose among HT tables */
- s32 (*ht_tbl_pointer)[IWL_RATE_COUNT];
+ const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT];
/* Check for invalid LQ type */
if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) {
@@ -1173,9 +1173,8 @@ static s32 rs_get_best_rate(struct iwl_priv *priv,
&(lq_sta->lq_info[lq_sta->active_tbl]);
s32 active_sr = active_tbl->win[index].success_ratio;
s32 active_tpt = active_tbl->expected_tpt[index];
-
/* expected "search" throughput */
- s32 *tpt_tbl = tbl->expected_tpt;
+ const u16 *tpt_tbl = tbl->expected_tpt;
s32 new_rate, high, low, start_hi;
u16 high_low;
@@ -3319,8 +3318,8 @@ static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sba
struct ieee80211_sta *sta, void *priv_sta)
{
}
-static struct rate_control_ops rs_ops = {
- .module = NULL,
+
+static const struct rate_control_ops rs_ops = {
.name = RS_NAME,
.tx_status = rs_tx_status,
.get_rate = rs_get_rate,
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.h b/drivers/net/wireless/iwlwifi/dvm/rs.h
index bdd5644a400b..f6bd25cad203 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rs.h
+++ b/drivers/net/wireless/iwlwifi/dvm/rs.h
@@ -315,7 +315,7 @@ struct iwl_scale_tbl_info {
u8 is_dup; /* 1 = duplicated data streams */
u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
u8 max_search; /* maximun number of tables we can search */
- s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
+ const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
u32 current_rate; /* rate_n_flags, uCode API format */
struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
};
diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c
index 7a1bc1c547e1..cd8377346aff 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rx.c
@@ -39,7 +39,7 @@
#define IWL_CMD_ENTRY(x) [x] = #x
-const char *iwl_dvm_cmd_strings[REPLY_MAX] = {
+const char *const iwl_dvm_cmd_strings[REPLY_MAX] = {
IWL_CMD_ENTRY(REPLY_ALIVE),
IWL_CMD_ENTRY(REPLY_ERROR),
IWL_CMD_ENTRY(REPLY_ECHO),
diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c
index 2a59da2ff87a..003a546571d4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-7000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-7000.c
@@ -71,8 +71,8 @@
#define IWL3160_UCODE_API_MAX 8
/* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK 7
-#define IWL3160_UCODE_API_OK 7
+#define IWL7260_UCODE_API_OK 8
+#define IWL3160_UCODE_API_OK 8
/* Lowest firmware API version supported */
#define IWL7260_UCODE_API_MIN 7
@@ -95,6 +95,8 @@
#define IWL7265_FW_PRE "iwlwifi-7265-"
#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
+#define NVM_HW_SECTION_NUM_FAMILY_7000 0
+
static const struct iwl_base_params iwl7000_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.num_of_queues = IWLAGN_NUM_QUEUES,
@@ -120,7 +122,8 @@ static const struct iwl_ht_params iwl7000_ht_params = {
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.base_params = &iwl7000_base_params, \
- .led_mode = IWL_LED_RF_STATE
+ .led_mode = IWL_LED_RF_STATE, \
+ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000
const struct iwl_cfg iwl7260_2ac_cfg = {
@@ -131,6 +134,7 @@ const struct iwl_cfg iwl7260_2ac_cfg = {
.nvm_ver = IWL7260_NVM_VERSION,
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
.host_interrupt_operation_mode = true,
+ .lp_xtal_workaround = true,
};
const struct iwl_cfg iwl7260_2ac_cfg_high_temp = {
@@ -142,6 +146,7 @@ const struct iwl_cfg iwl7260_2ac_cfg_high_temp = {
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
.high_temp = true,
.host_interrupt_operation_mode = true,
+ .lp_xtal_workaround = true,
};
const struct iwl_cfg iwl7260_2n_cfg = {
@@ -152,6 +157,7 @@ const struct iwl_cfg iwl7260_2n_cfg = {
.nvm_ver = IWL7260_NVM_VERSION,
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
.host_interrupt_operation_mode = true,
+ .lp_xtal_workaround = true,
};
const struct iwl_cfg iwl7260_n_cfg = {
@@ -162,6 +168,7 @@ const struct iwl_cfg iwl7260_n_cfg = {
.nvm_ver = IWL7260_NVM_VERSION,
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
.host_interrupt_operation_mode = true,
+ .lp_xtal_workaround = true,
};
const struct iwl_cfg iwl3160_2ac_cfg = {
@@ -194,6 +201,17 @@ const struct iwl_cfg iwl3160_n_cfg = {
.host_interrupt_operation_mode = true,
};
+static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = {
+ {.pwr = 1600, .backoff = 0},
+ {.pwr = 1300, .backoff = 467},
+ {.pwr = 900, .backoff = 1900},
+ {.pwr = 800, .backoff = 2630},
+ {.pwr = 700, .backoff = 3720},
+ {.pwr = 600, .backoff = 5550},
+ {.pwr = 500, .backoff = 9350},
+ {0},
+};
+
const struct iwl_cfg iwl7265_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 7265",
.fw_name_pre = IWL7265_FW_PRE,
@@ -201,6 +219,7 @@ const struct iwl_cfg iwl7265_2ac_cfg = {
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL7265_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
};
const struct iwl_cfg iwl7265_2n_cfg = {
@@ -210,6 +229,7 @@ const struct iwl_cfg iwl7265_2n_cfg = {
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL7265_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
};
const struct iwl_cfg iwl7265_n_cfg = {
@@ -219,6 +239,7 @@ const struct iwl_cfg iwl7265_n_cfg = {
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL7265_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
};
MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c
new file mode 100644
index 000000000000..f5bd82b88592
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-8000.c
@@ -0,0 +1,132 @@
+/******************************************************************************
+ *
+ * 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. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2014 Intel Corporation. All rights reserved.
+ * 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 IWL8000_UCODE_API_MAX 8
+
+/* Oldest version we won't warn about */
+#define IWL8000_UCODE_API_OK 8
+
+/* Lowest firmware API version supported */
+#define IWL8000_UCODE_API_MIN 8
+
+/* NVM versions */
+#define IWL8000_NVM_VERSION 0x0a1d
+#define IWL8000_TX_POWER_VERSION 0xffff /* meaningless */
+
+#define IWL8000_FW_PRE "iwlwifi-8000-"
+#define IWL8000_MODULE_FIRMWARE(api) IWL8000_FW_PRE __stringify(api) ".ucode"
+
+#define NVM_HW_SECTION_NUM_FAMILY_8000 10
+
+static const struct iwl_base_params iwl8000_base_params = {
+ .eeprom_size = OTP_LOW_IMAGE_SIZE,
+ .num_of_queues = IWLAGN_NUM_QUEUES,
+ .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 iwl8000_ht_params = {
+ .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+#define IWL_DEVICE_8000 \
+ .ucode_api_max = IWL8000_UCODE_API_MAX, \
+ .ucode_api_ok = IWL8000_UCODE_API_OK, \
+ .ucode_api_min = IWL8000_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 = &iwl8000_base_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000
+
+const struct iwl_cfg iwl8260_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 8260",
+ .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,
+};
+
+const struct iwl_cfg iwl8260_n_cfg = {
+ .name = "Intel(R) Dual Band Wireless-AC 8260",
+ .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,
+};
+
+MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h
index 1ced525157dc..3f17dc3f2c8a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/iwlwifi/iwl-config.h
@@ -84,6 +84,7 @@ enum iwl_device_family {
IWL_DEVICE_FAMILY_6050,
IWL_DEVICE_FAMILY_6150,
IWL_DEVICE_FAMILY_7000,
+ IWL_DEVICE_FAMILY_8000,
};
/*
@@ -192,6 +193,15 @@ struct iwl_eeprom_params {
bool enhanced_txpower;
};
+/* Tx-backoff power threshold
+ * @pwr: The power limit in mw
+ * @backoff: The tx-backoff in uSec
+ */
+struct iwl_pwr_tx_backoff {
+ u32 pwr;
+ u32 backoff;
+};
+
/**
* struct iwl_cfg
* @name: Offical name of the device
@@ -217,6 +227,9 @@ struct iwl_eeprom_params {
* @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
+ * @pwr_tx_backoffs: translation table between power limits and backoffs
*
* We enable the driver to be backward compatible wrt. hardware features.
* API differences in uCode shouldn't be handled here but through TLVs
@@ -247,6 +260,10 @@ 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;
};
/*
@@ -307,6 +324,8 @@ extern const struct iwl_cfg iwl3160_n_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;
+extern const struct iwl_cfg iwl8260_2ac_cfg;
+extern const struct iwl_cfg iwl8260_n_cfg;
#endif /* CONFIG_IWLMVM */
#endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
index 9d325516c42d..fe129c94ae3e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/iwlwifi/iwl-csr.h
@@ -139,6 +139,13 @@
#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c)
/*
+ * CSR HW resources monitor registers
+ */
+#define CSR_MONITOR_CFG_REG (CSR_BASE+0x214)
+#define CSR_MONITOR_STATUS_REG (CSR_BASE+0x228)
+#define CSR_MONITOR_XTAL_RESOURCES (0x00000010)
+
+/*
* CSR Hardware Revision Workaround Register. Indicates hardware rev;
* "step" determines CCK backoff for txpower calculation. Used for 4965 only.
* See also CSR_HW_REV register.
@@ -173,6 +180,7 @@
#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */
#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */
#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */
+#define CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */
#define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/
#define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/
@@ -240,6 +248,7 @@
* 001 -- MAC power-down
* 010 -- PHY (radio) power-down
* 011 -- Error
+ * 10: XTAL ON request
* 9-6: SYS_CONFIG
* Indicates current system configuration, reflecting pins on chip
* as forced high/low by device circuit board.
@@ -271,6 +280,7 @@
#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004)
#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008)
#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010)
+#define CSR_GP_CNTRL_REG_FLAG_XTAL_ON (0x00000400)
#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001)
@@ -395,37 +405,33 @@
#define CSR_DRAM_INT_TBL_ENABLE (1 << 31)
#define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27)
-/* SECURE boot registers */
-#define CSR_SECURE_BOOT_CONFIG_ADDR (0x100)
-enum secure_boot_config_reg {
- CSR_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP = 0x00000001,
- CSR_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ = 0x00000002,
-};
-
-#define CSR_SECURE_BOOT_CPU1_STATUS_ADDR (0x100)
-#define CSR_SECURE_BOOT_CPU2_STATUS_ADDR (0x100)
-enum secure_boot_status_reg {
- CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS = 0x00000003,
- CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED = 0x00000002,
- CSR_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS = 0x00000004,
- CSR_SECURE_BOOT_CPU_STATUS_VERF_FAIL = 0x00000008,
- CSR_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL = 0x00000010,
-};
-
-#define CSR_UCODE_LOAD_STATUS_ADDR (0x100)
-enum secure_load_status_reg {
- CSR_CPU_STATUS_LOADING_STARTED = 0x00000001,
- CSR_CPU_STATUS_LOADING_COMPLETED = 0x00000002,
- CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8,
- CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00,
-};
-
-#define CSR_SECURE_INSPECTOR_CODE_ADDR (0x100)
-#define CSR_SECURE_INSPECTOR_DATA_ADDR (0x100)
-
-#define CSR_SECURE_TIME_OUT (100)
+/*
+ * SHR target access (Shared block memory space)
+ *
+ * Shared internal registers can be accessed directly from PCI bus through SHR
+ * arbiter without need for the MAC HW to be powered up. This is possible due to
+ * indirect read/write via HEEP_CTRL_WRD_PCIEX_CTRL (0xEC) and
+ * HEEP_CTRL_WRD_PCIEX_DATA (0xF4) registers.
+ *
+ * Use iwl_write32()/iwl_read32() family to access these registers. The MAC HW
+ * need not be powered up so no "grab inc access" is required.
+ */
-#define FH_TCSR_0_REG0 (0x1D00)
+/*
+ * Registers for accessing shared registers (e.g. SHR_APMG_GP1,
+ * SHR_APMG_XTAL_CFG). For example, to read from SHR_APMG_GP1 register (0x1DC),
+ * first, write to the control register:
+ * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register)
+ * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 2 (read access)
+ * second, read from the data register HEEP_CTRL_WRD_PCIEX_DATA[31:0].
+ *
+ * To write the register, first, write to the data register
+ * HEEP_CTRL_WRD_PCIEX_DATA[31:0] and then:
+ * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register)
+ * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 3 (write access)
+ */
+#define HEEP_CTRL_WRD_PCIEX_CTRL_REG (CSR_BASE+0x0ec)
+#define HEEP_CTRL_WRD_PCIEX_DATA_REG (CSR_BASE+0x0f4)
/*
* HBUS (Host-side Bus)
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index a75aac986a23..c8cbdbe15924 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -126,6 +126,7 @@ do { \
/* 0x00000F00 - 0x00000100 */
#define IWL_DL_POWER 0x00000100
#define IWL_DL_TEMP 0x00000200
+#define IWL_DL_RPM 0x00000400
#define IWL_DL_SCAN 0x00000800
/* 0x0000F000 - 0x00001000 */
#define IWL_DL_ASSOC 0x00001000
@@ -189,5 +190,6 @@ do { \
#define IWL_DEBUG_RADIO(p, f, a...) IWL_DEBUG(p, IWL_DL_RADIO, f, ## a)
#define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a)
#define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a)
+#define IWL_DEBUG_RPM(p, f, a...) IWL_DEBUG(p, IWL_DL_RPM, f, ## a)
#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index 75103554cd63..0a3e841b44a9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -128,7 +128,7 @@ struct iwl_drv {
const struct iwl_cfg *cfg;
int fw_index; /* firmware we're trying to load */
- char firmware_name[25]; /* name of firmware file to load */
+ char firmware_name[32]; /* name of firmware file to load */
struct completion request_firmware_complete;
@@ -237,7 +237,8 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
return -ENOENT;
}
- sprintf(drv->firmware_name, "%s%s%s", name_pre, tag, ".ucode");
+ snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode",
+ name_pre, tag);
IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n",
(drv->fw_index == UCODE_EXPERIMENTAL_INDEX)
@@ -403,6 +404,38 @@ static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data)
return 0;
}
+static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data,
+ struct iwl_ucode_capabilities *capa)
+{
+ const struct iwl_ucode_api *ucode_api = (void *)data;
+ u32 api_index = le32_to_cpu(ucode_api->api_index);
+
+ if (api_index >= IWL_API_ARRAY_SIZE) {
+ IWL_ERR(drv, "api_index larger than supported by driver\n");
+ return -EINVAL;
+ }
+
+ capa->api[api_index] = le32_to_cpu(ucode_api->api_flags);
+
+ return 0;
+}
+
+static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data,
+ struct iwl_ucode_capabilities *capa)
+{
+ const struct iwl_ucode_capa *ucode_capa = (void *)data;
+ u32 api_index = le32_to_cpu(ucode_capa->api_index);
+
+ if (api_index >= IWL_CAPABILITIES_ARRAY_SIZE) {
+ IWL_ERR(drv, "api_index larger than supported by driver\n");
+ return -EINVAL;
+ }
+
+ capa->capa[api_index] = le32_to_cpu(ucode_capa->api_capa);
+
+ return 0;
+}
+
static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv,
const struct firmware *ucode_raw,
struct iwl_firmware_pieces *pieces)
@@ -637,6 +670,18 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
*/
capa->flags = le32_to_cpup((__le32 *)tlv_data);
break;
+ case IWL_UCODE_TLV_API_CHANGES_SET:
+ if (tlv_len != sizeof(struct iwl_ucode_api))
+ goto invalid_tlv_len;
+ if (iwl_set_ucode_api_flags(drv, tlv_data, capa))
+ goto tlv_error;
+ break;
+ case IWL_UCODE_TLV_ENABLED_CAPABILITIES:
+ if (tlv_len != sizeof(struct iwl_ucode_capa))
+ goto invalid_tlv_len;
+ if (iwl_set_ucode_capabilities(drv, tlv_data, capa))
+ goto tlv_error;
+ break;
case IWL_UCODE_TLV_INIT_EVTLOG_PTR:
if (tlv_len != sizeof(u32))
goto invalid_tlv_len;
@@ -727,6 +772,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
if (tlv_len != sizeof(u32))
goto invalid_tlv_len;
drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data);
+ drv->fw.valid_tx_ant = (drv->fw.phy_config &
+ FW_PHY_CFG_TX_CHAIN) >>
+ FW_PHY_CFG_TX_CHAIN_POS;
+ drv->fw.valid_rx_ant = (drv->fw.phy_config &
+ FW_PHY_CFG_RX_CHAIN) >>
+ FW_PHY_CFG_RX_CHAIN_POS;
break;
case IWL_UCODE_TLV_SECURE_SEC_RT:
iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR,
@@ -1300,8 +1351,7 @@ MODULE_PARM_DESC(antenna_coupling,
module_param_named(wd_disable, iwlwifi_mod_params.wd_disable, int, S_IRUGO);
MODULE_PARM_DESC(wd_disable,
- "Disable stuck queue watchdog timer 0=system default, "
- "1=disable, 2=enable (default: 0)");
+ "Disable stuck queue watchdog timer 0=system default, 1=disable (default: 1)");
module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
MODULE_PARM_DESC(nvm_file, "NVM file name");
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h
index 592c01e11013..3c72cb710b0c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.h
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.h
@@ -70,6 +70,20 @@
#define DRV_COPYRIGHT "Copyright(c) 2003- 2014 Intel Corporation"
#define DRV_AUTHOR "<ilw@linux.intel.com>"
+/* radio config bits (actual values from NVM definition) */
+#define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */
+#define NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */
+#define NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */
+#define NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */
+#define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */
+#define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
+
+#define NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(x) (x & 0xF)
+#define NVM_RF_CFG_DASH_MSK_FAMILY_8000(x) ((x >> 4) & 0xF)
+#define NVM_RF_CFG_STEP_MSK_FAMILY_8000(x) ((x >> 8) & 0xF)
+#define NVM_RF_CFG_TYPE_MSK_FAMILY_8000(x) ((x >> 12) & 0xFFF)
+#define NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(x) ((x >> 24) & 0xF)
+#define NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(x) ((x >> 28) & 0xF)
/**
* DOC: Driver system flows - drv component
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
index e3c7deafabe6..f0548b8a64b0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
@@ -81,16 +81,17 @@ struct iwl_nvm_data {
bool sku_cap_band_24GHz_enable;
bool sku_cap_band_52GHz_enable;
bool sku_cap_11n_enable;
+ bool sku_cap_11ac_enable;
bool sku_cap_amt_enable;
bool sku_cap_ipan_enable;
- u8 radio_cfg_type;
+ u16 radio_cfg_type;
u8 radio_cfg_step;
u8 radio_cfg_dash;
u8 radio_cfg_pnum;
u8 valid_tx_ant, valid_rx_ant;
- u16 nvm_version;
+ u32 nvm_version;
s8 max_tx_pwr_half_dbm;
struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
index 88e2d6eb569f..b45e576a4b57 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
@@ -126,6 +126,8 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26,
IWL_UCODE_TLV_NUM_OF_CPU = 27,
IWL_UCODE_TLV_CSCHEME = 28,
+ IWL_UCODE_TLV_API_CHANGES_SET = 29,
+ IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30,
};
struct iwl_ucode_tlv {
@@ -158,4 +160,19 @@ struct iwl_tlv_ucode_header {
u8 data[0];
};
+/*
+ * ucode TLVs
+ *
+ * ability to get extension for: flags & capabilities from ucode binaries files
+ */
+struct iwl_ucode_api {
+ __le32 api_index;
+ __le32 api_flags;
+} __packed;
+
+struct iwl_ucode_capa {
+ __le32 api_index;
+ __le32 api_capa;
+} __packed;
+
#endif /* __iwl_fw_file_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index 5f1493c44097..d14f19339d61 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -92,9 +92,11 @@
* @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API
* @IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command
* containing CAM (Continuous Active Mode) indication.
- * @IWL_UCODE_TLV_FLAGS_P2P_PS: P2P client power save is supported (only on a
- * single bound interface).
+ * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and
+ * P2P client interfaces simultaneously if they are in different bindings.
* @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
+ * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
+ * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
*/
enum iwl_ucode_tlv_flag {
IWL_UCODE_TLV_FLAGS_PAN = BIT(0),
@@ -116,9 +118,27 @@ enum iwl_ucode_tlv_flag {
IWL_UCODE_TLV_FLAGS_SCHED_SCAN = BIT(17),
IWL_UCODE_TLV_FLAGS_STA_KEY_CMD = BIT(19),
IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD = BIT(20),
- IWL_UCODE_TLV_FLAGS_P2P_PS = BIT(21),
+ IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM = BIT(22),
IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24),
IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26),
+ IWL_UCODE_TLV_FLAGS_BCAST_FILTERING = BIT(29),
+ IWL_UCODE_TLV_FLAGS_GO_UAPSD = BIT(30),
+};
+
+/**
+ * enum iwl_ucode_tlv_api - ucode api
+ * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
+ */
+enum iwl_ucode_tlv_api {
+ IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0),
+};
+
+/**
+ * enum iwl_ucode_tlv_capa - ucode capabilities
+ * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3
+ */
+enum iwl_ucode_tlv_capa {
+ IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0),
};
/* The default calibrate table size if not specified by firmware file */
@@ -160,13 +180,16 @@ enum iwl_ucode_sec {
* For 16.0 uCode and above, there is no differentiation between sections,
* just an offset to the HW address.
*/
-#define IWL_UCODE_SECTION_MAX 6
-#define IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU (IWL_UCODE_SECTION_MAX/2)
+#define IWL_UCODE_SECTION_MAX 12
+#define IWL_API_ARRAY_SIZE 1
+#define IWL_CAPABILITIES_ARRAY_SIZE 1
struct iwl_ucode_capabilities {
u32 max_probe_length;
u32 standard_phy_calibration_size;
u32 flags;
+ u32 api[IWL_API_ARRAY_SIZE];
+ u32 capa[IWL_CAPABILITIES_ARRAY_SIZE];
};
/* one for each uCode image (inst/data, init/runtime/wowlan) */
@@ -285,22 +308,12 @@ struct iwl_fw {
struct iwl_tlv_calib_ctrl default_calib[IWL_UCODE_TYPE_MAX];
u32 phy_config;
+ u8 valid_tx_ant;
+ u8 valid_rx_ant;
bool mvm_fw;
struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
};
-static inline u8 iwl_fw_valid_tx_ant(const struct iwl_fw *fw)
-{
- return (fw->phy_config & FW_PHY_CFG_TX_CHAIN) >>
- FW_PHY_CFG_TX_CHAIN_POS;
-}
-
-static inline u8 iwl_fw_valid_rx_ant(const struct iwl_fw *fw)
-{
- return (fw->phy_config & FW_PHY_CFG_RX_CHAIN) >>
- FW_PHY_CFG_RX_CHAIN_POS;
-}
-
#endif /* __iwl_fw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c
index f98175a0d35b..44cc3cf45762 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.c
+++ b/drivers/net/wireless/iwlwifi/iwl-io.c
@@ -93,14 +93,14 @@ int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
}
IWL_EXPORT_SYMBOL(iwl_poll_direct_bit);
-static inline u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs)
+u32 __iwl_read_prph(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;
}
-static inline void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
+void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
{
trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val);
iwl_trans_write_prph(trans, ofs, val);
@@ -130,6 +130,21 @@ void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
}
IWL_EXPORT_SYMBOL(iwl_write_prph);
+int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
+ u32 bits, u32 mask, int timeout)
+{
+ int t = 0;
+
+ do {
+ if ((iwl_read_prph(trans, addr) & mask) == (bits & mask))
+ return t;
+ udelay(IWL_POLL_INTERVAL);
+ t += IWL_POLL_INTERVAL;
+ } while (t < timeout);
+
+ return -ETIMEDOUT;
+}
+
void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
{
unsigned long flags;
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h
index c339c1bed080..665ddd9dbbc4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/iwlwifi/iwl-io.h
@@ -70,8 +70,12 @@ 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(struct iwl_trans *trans, u32 ofs);
+void __iwl_write_prph(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);
void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
u32 bits, u32 mask);
diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h
index b29075c3da8e..d994317db85b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-modparams.h
+++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h
@@ -96,7 +96,7 @@ enum iwl_disable_11n {
* use IWL_[DIS,EN]ABLE_HT_* constants
* @amsdu_size_8K: enable 8K amsdu size, default = 0
* @restart_fw: restart firmware, default = 1
- * @wd_disable: enable stuck queue check, default = 0
+ * @wd_disable: disable stuck queue check, default = 1
* @bt_coex_active: enable bt coex, default = true
* @led_mode: system default, default = 0
* @power_save: disable power save, default = false
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
index 725e954d8475..6be30c698506 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
@@ -71,7 +71,7 @@ enum wkp_nvm_offsets {
/* NVM HW-Section offset (in words) definitions */
HW_ADDR = 0x15,
-/* NVM SW-Section offset (in words) definitions */
+ /* NVM SW-Section offset (in words) definitions */
NVM_SW_SECTION = 0x1C0,
NVM_VERSION = 0,
RADIO_CFG = 1,
@@ -79,11 +79,32 @@ enum wkp_nvm_offsets {
N_HW_ADDRS = 3,
NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION,
-/* NVM calibration section offset (in words) definitions */
+ /* NVM calibration section offset (in words) definitions */
NVM_CALIB_SECTION = 0x2B8,
XTAL_CALIB = 0x316 - NVM_CALIB_SECTION
};
+enum family_8000_nvm_offsets {
+ /* NVM HW-Section offset (in words) definitions */
+ HW_ADDR0_FAMILY_8000 = 0x12,
+ HW_ADDR1_FAMILY_8000 = 0x16,
+ MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1,
+
+ /* NVM SW-Section offset (in words) definitions */
+ NVM_SW_SECTION_FAMILY_8000 = 0x1C0,
+ NVM_VERSION_FAMILY_8000 = 0,
+ RADIO_CFG_FAMILY_8000 = 2,
+ SKU_FAMILY_8000 = 4,
+ N_HW_ADDRS_FAMILY_8000 = 5,
+
+ /* NVM REGULATORY -Section offset (in words) definitions */
+ NVM_CHANNELS_FAMILY_8000 = 0,
+
+ /* NVM calibration section offset (in words) definitions */
+ NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8,
+ XTAL_CALIB_FAMILY_8000 = 0x316 - NVM_CALIB_SECTION_FAMILY_8000
+};
+
/* SKU Capabilities (actual values from NVM definition) */
enum nvm_sku_bits {
NVM_SKU_CAP_BAND_24GHZ = BIT(0),
@@ -92,14 +113,6 @@ enum nvm_sku_bits {
NVM_SKU_CAP_11AC_ENABLE = BIT(3),
};
-/* radio config bits (actual values from NVM definition) */
-#define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */
-#define NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */
-#define NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */
-#define NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */
-#define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */
-#define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
-
/*
* These are the channel numbers in the order that they are stored in the NVM
*/
@@ -112,7 +125,17 @@ static const u8 iwl_nvm_channels[] = {
149, 153, 157, 161, 165
};
+static const u8 iwl_nvm_channels_family_8000[] = {
+ /* 2.4 GHz */
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ /* 5 GHz */
+ 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
+ 149, 153, 157, 161, 165, 169, 173, 177, 181
+};
+
#define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels)
+#define IWL_NUM_CHANNELS_FAMILY_8000 ARRAY_SIZE(iwl_nvm_channels_family_8000)
#define NUM_2GHZ_CHANNELS 14
#define FIRST_2GHZ_HT_MINUS 5
#define LAST_2GHZ_HT_PLUS 9
@@ -179,8 +202,18 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
struct ieee80211_channel *channel;
u16 ch_flags;
bool is_5ghz;
+ int num_of_ch;
+ const u8 *nvm_chan;
+
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ num_of_ch = IWL_NUM_CHANNELS;
+ nvm_chan = &iwl_nvm_channels[0];
+ } else {
+ num_of_ch = IWL_NUM_CHANNELS_FAMILY_8000;
+ nvm_chan = &iwl_nvm_channels_family_8000[0];
+ }
- for (ch_idx = 0; ch_idx < IWL_NUM_CHANNELS; ch_idx++) {
+ for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
if (ch_idx >= NUM_2GHZ_CHANNELS &&
@@ -190,7 +223,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
if (!(ch_flags & NVM_CHANNEL_VALID)) {
IWL_DEBUG_EEPROM(dev,
"Ch. %d Flags %x [%sGHz] - No traffic\n",
- iwl_nvm_channels[ch_idx],
+ nvm_chan[ch_idx],
ch_flags,
(ch_idx >= NUM_2GHZ_CHANNELS) ?
"5.2" : "2.4");
@@ -200,7 +233,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
channel = &data->channels[n_channels];
n_channels++;
- channel->hw_value = iwl_nvm_channels[ch_idx];
+ channel->hw_value = nvm_chan[ch_idx];
channel->band = (ch_idx < NUM_2GHZ_CHANNELS) ?
IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
channel->center_freq =
@@ -211,11 +244,11 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
channel->flags = IEEE80211_CHAN_NO_HT40;
if (ch_idx < NUM_2GHZ_CHANNELS &&
(ch_flags & NVM_CHANNEL_40MHZ)) {
- if (iwl_nvm_channels[ch_idx] <= LAST_2GHZ_HT_PLUS)
+ if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS)
channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
- if (iwl_nvm_channels[ch_idx] >= FIRST_2GHZ_HT_MINUS)
+ if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS)
channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
- } else if (iwl_nvm_channels[ch_idx] <= LAST_5GHZ_HT &&
+ } else if (nvm_chan[ch_idx] <= LAST_5GHZ_HT &&
(ch_flags & NVM_CHANNEL_40MHZ)) {
if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
@@ -266,9 +299,11 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
- struct ieee80211_sta_vht_cap *vht_cap)
+ struct ieee80211_sta_vht_cap *vht_cap,
+ u8 tx_chains, u8 rx_chains)
{
- int num_ants = num_of_ant(data->valid_rx_ant);
+ int num_rx_ants = num_of_ant(rx_chains);
+ int num_tx_ants = num_of_ant(tx_chains);
vht_cap->vht_supported = true;
@@ -278,8 +313,10 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT |
7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
- if (num_ants > 1)
+ if (num_tx_ants > 1)
vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
+ else
+ vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
if (iwlwifi_mod_params.amsdu_size_8K)
vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
@@ -294,10 +331,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
IEEE80211_VHT_MCS_NOT_SUPPORTED << 14);
- if (num_ants == 1 ||
- cfg->rx_with_siso_diversity) {
- vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN |
- IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
+ if (num_rx_ants == 1 || cfg->rx_with_siso_diversity) {
+ vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN;
/* this works because NOT_SUPPORTED == 3 */
vht_cap->vht_mcs.rx_mcs_map |=
cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2);
@@ -307,14 +342,23 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
}
static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data, const __le16 *nvm_sw,
- bool enable_vht, u8 tx_chains, u8 rx_chains)
+ struct iwl_nvm_data *data,
+ const __le16 *ch_section, bool enable_vht,
+ u8 tx_chains, u8 rx_chains)
{
- int n_channels = iwl_init_channel_map(dev, cfg, data,
- &nvm_sw[NVM_CHANNELS]);
+ int n_channels;
int n_used = 0;
struct ieee80211_supported_band *sband;
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ n_channels = iwl_init_channel_map(
+ dev, cfg, data,
+ &ch_section[NVM_CHANNELS]);
+ else
+ n_channels = iwl_init_channel_map(
+ dev, cfg, data,
+ &ch_section[NVM_CHANNELS_FAMILY_8000]);
+
sband = &data->bands[IEEE80211_BAND_2GHZ];
sband->band = IEEE80211_BAND_2GHZ;
sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS];
@@ -333,80 +377,160 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
tx_chains, rx_chains);
if (enable_vht)
- iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap);
+ iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap,
+ tx_chains, rx_chains);
if (n_channels != n_used)
IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
n_used, n_channels);
}
+static int iwl_get_sku(const struct iwl_cfg *cfg,
+ const __le16 *nvm_sw)
+{
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ return le16_to_cpup(nvm_sw + SKU);
+ else
+ return le32_to_cpup((__le32 *)(nvm_sw + SKU_FAMILY_8000));
+}
+
+static int iwl_get_nvm_version(const struct iwl_cfg *cfg,
+ const __le16 *nvm_sw)
+{
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ return le16_to_cpup(nvm_sw + NVM_VERSION);
+ else
+ return le32_to_cpup((__le32 *)(nvm_sw +
+ NVM_VERSION_FAMILY_8000));
+}
+
+static int iwl_get_radio_cfg(const struct iwl_cfg *cfg,
+ const __le16 *nvm_sw)
+{
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ return le16_to_cpup(nvm_sw + RADIO_CFG);
+ else
+ return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000));
+}
+
+#define N_HW_ADDRS_MASK_FAMILY_8000 0xF
+static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg,
+ const __le16 *nvm_sw)
+{
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ return le16_to_cpup(nvm_sw + N_HW_ADDRS);
+ else
+ return le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000))
+ & N_HW_ADDRS_MASK_FAMILY_8000;
+}
+
+static void iwl_set_radio_cfg(const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ u32 radio_cfg)
+{
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg);
+ data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg);
+ data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg);
+ data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg);
+ return;
+ }
+
+ /* set the radio configuration for family 8000 */
+ data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK_FAMILY_8000(radio_cfg);
+ data->radio_cfg_step = NVM_RF_CFG_STEP_MSK_FAMILY_8000(radio_cfg);
+ data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK_FAMILY_8000(radio_cfg);
+ data->radio_cfg_pnum = NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(radio_cfg);
+}
+
+static void iwl_set_hw_address(const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data,
+ const __le16 *nvm_sec)
+{
+ u8 hw_addr[ETH_ALEN];
+
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ memcpy(hw_addr, nvm_sec + HW_ADDR, ETH_ALEN);
+ else
+ memcpy(hw_addr, nvm_sec + MAC_ADDRESS_OVERRIDE_FAMILY_8000,
+ ETH_ALEN);
+
+ /* The byte order is little endian 16 bit, meaning 214365 */
+ data->hw_addr[0] = hw_addr[1];
+ data->hw_addr[1] = hw_addr[0];
+ data->hw_addr[2] = hw_addr[3];
+ data->hw_addr[3] = hw_addr[2];
+ data->hw_addr[4] = hw_addr[5];
+ data->hw_addr[5] = hw_addr[4];
+}
+
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, u8 tx_chains, u8 rx_chains)
+ const __le16 *nvm_calib, const __le16 *regulatory,
+ const __le16 *mac_override, u8 tx_chains, u8 rx_chains)
{
struct iwl_nvm_data *data;
- u8 hw_addr[ETH_ALEN];
- u16 radio_cfg, sku;
-
- data = kzalloc(sizeof(*data) +
- sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS,
- GFP_KERNEL);
+ u32 sku;
+ u32 radio_cfg;
+
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ data = kzalloc(sizeof(*data) +
+ sizeof(struct ieee80211_channel) *
+ IWL_NUM_CHANNELS,
+ GFP_KERNEL);
+ else
+ data = kzalloc(sizeof(*data) +
+ sizeof(struct ieee80211_channel) *
+ IWL_NUM_CHANNELS_FAMILY_8000,
+ GFP_KERNEL);
if (!data)
return NULL;
- data->nvm_version = le16_to_cpup(nvm_sw + NVM_VERSION);
+ data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw);
- radio_cfg = le16_to_cpup(nvm_sw + RADIO_CFG);
- data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg);
- data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg);
- data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg);
- data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg);
- data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK(radio_cfg);
- data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK(radio_cfg);
+ radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw);
+ iwl_set_radio_cfg(cfg, data, radio_cfg);
- sku = le16_to_cpup(nvm_sw + SKU);
+ sku = iwl_get_sku(cfg, nvm_sw);
data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE;
+ data->sku_cap_11ac_enable = sku & NVM_SKU_CAP_11AC_ENABLE;
if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
data->sku_cap_11n_enable = false;
- /* check overrides (some devices have wrong NVM) */
- if (cfg->valid_tx_ant)
- data->valid_tx_ant = cfg->valid_tx_ant;
- if (cfg->valid_rx_ant)
- data->valid_rx_ant = cfg->valid_rx_ant;
+ data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw);
- if (!data->valid_tx_ant || !data->valid_rx_ant) {
- IWL_ERR_DEV(dev, "invalid antennas (0x%x, 0x%x)\n",
- data->valid_tx_ant, data->valid_rx_ant);
- kfree(data);
- return NULL;
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ /* Checking for required sections */
+ if (!nvm_calib) {
+ IWL_ERR_DEV(dev,
+ "Can't parse empty Calib NVM sections\n");
+ kfree(data);
+ return NULL;
+ }
+ /* in family 8000 Xtal calibration values moved to OTP */
+ data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB);
+ data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1);
}
- data->n_hw_addrs = le16_to_cpup(nvm_sw + N_HW_ADDRS);
+ if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ iwl_set_hw_address(cfg, data, nvm_hw);
- data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB);
- data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1);
+ iwl_init_sbands(dev, cfg, data, nvm_sw,
+ sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
+ rx_chains);
+ } else {
+ /* MAC address in family 8000 */
+ iwl_set_hw_address(cfg, data, mac_override);
- /* The byte order is little endian 16 bit, meaning 214365 */
- memcpy(hw_addr, nvm_hw + HW_ADDR, ETH_ALEN);
- data->hw_addr[0] = hw_addr[1];
- data->hw_addr[1] = hw_addr[0];
- data->hw_addr[2] = hw_addr[3];
- data->hw_addr[3] = hw_addr[2];
- data->hw_addr[4] = hw_addr[5];
- data->hw_addr[5] = hw_addr[4];
-
- iwl_init_sbands(dev, cfg, data, nvm_sw, sku & NVM_SKU_CAP_11AC_ENABLE,
- tx_chains, rx_chains);
+ iwl_init_sbands(dev, cfg, data, regulatory,
+ sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
+ rx_chains);
+ }
- data->calib_version = 255; /* TODO:
- this value will prevent some checks from
- failing, we need to check if this
- field is still needed, and if it does,
- where is it in the NVM*/
+ data->calib_version = 255;
return data;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
index 0c4399aba8c6..c9c45a39d212 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
@@ -75,6 +75,7 @@
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, u8 tx_chains, u8 rx_chains);
+ const __le16 *nvm_calib, const __le16 *regulatory,
+ const __le16 *mac_override, u8 tx_chains, u8 rx_chains);
#endif /* __iwl_nvm_parse_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
index b5be51f3cd3d..ea29504ac617 100644
--- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h
+++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
@@ -119,7 +119,8 @@ struct iwl_cfg;
* @queue_not_full: notifies that a HW queue is not full any more.
* Must be atomic and called with BH disabled.
* @hw_rf_kill:notifies of a change in the HW rf kill switch. True means that
- * the radio is killed. May sleep.
+ * the radio is killed. Return %true if the device should be stopped by
+ * the transport immediately after the call. May sleep.
* @free_skb: allows the transport layer to free skbs that haven't been
* reclaimed by the op_mode. This can happen when the driver is freed and
* there are Tx packets pending in the transport layer.
@@ -131,6 +132,8 @@ struct iwl_cfg;
* @nic_config: configure NIC, called before firmware is started.
* May sleep
* @wimax_active: invoked when WiMax becomes active. May sleep
+ * @enter_d0i3: configure the fw to enter d0i3. May sleep.
+ * @exit_d0i3: configure the fw to exit d0i3. May sleep.
*/
struct iwl_op_mode_ops {
struct iwl_op_mode *(*start)(struct iwl_trans *trans,
@@ -142,12 +145,14 @@ struct iwl_op_mode_ops {
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);
- void (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state);
+ bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state);
void (*free_skb)(struct iwl_op_mode *op_mode, struct sk_buff *skb);
void (*nic_error)(struct iwl_op_mode *op_mode);
void (*cmd_queue_full)(struct iwl_op_mode *op_mode);
void (*nic_config)(struct iwl_op_mode *op_mode);
void (*wimax_active)(struct iwl_op_mode *op_mode);
+ int (*enter_d0i3)(struct iwl_op_mode *op_mode);
+ int (*exit_d0i3)(struct iwl_op_mode *op_mode);
};
int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops);
@@ -155,7 +160,7 @@ void iwl_opmode_deregister(const char *name);
/**
* struct iwl_op_mode - operational mode
- * @ops - pointer to its own ops
+ * @ops: pointer to its own ops
*
* This holds an implementation of the mac80211 / fw API.
*/
@@ -191,11 +196,11 @@ static inline void iwl_op_mode_queue_not_full(struct iwl_op_mode *op_mode,
op_mode->ops->queue_not_full(op_mode, queue);
}
-static inline void iwl_op_mode_hw_rf_kill(struct iwl_op_mode *op_mode,
- bool state)
+static inline bool __must_check
+iwl_op_mode_hw_rf_kill(struct iwl_op_mode *op_mode, bool state)
{
might_sleep();
- op_mode->ops->hw_rf_kill(op_mode, state);
+ return op_mode->ops->hw_rf_kill(op_mode, state);
}
static inline void iwl_op_mode_free_skb(struct iwl_op_mode *op_mode,
@@ -226,4 +231,22 @@ static inline void iwl_op_mode_wimax_active(struct iwl_op_mode *op_mode)
op_mode->ops->wimax_active(op_mode);
}
+static inline int iwl_op_mode_enter_d0i3(struct iwl_op_mode *op_mode)
+{
+ might_sleep();
+
+ if (!op_mode->ops->enter_d0i3)
+ return 0;
+ return op_mode->ops->enter_d0i3(op_mode);
+}
+
+static inline int iwl_op_mode_exit_d0i3(struct iwl_op_mode *op_mode)
+{
+ might_sleep();
+
+ if (!op_mode->ops->exit_d0i3)
+ return 0;
+ return op_mode->ops->exit_d0i3(op_mode);
+}
+
#endif /* __iwl_op_mode_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
index fa77d63a277a..b761ac4822a3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-phy-db.c
+++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
@@ -72,7 +72,7 @@
#include "iwl-trans.h"
#define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */
-#define IWL_NUM_PAPD_CH_GROUPS 4
+#define IWL_NUM_PAPD_CH_GROUPS 7
#define IWL_NUM_TXP_CH_GROUPS 9
struct iwl_phy_db_entry {
@@ -383,7 +383,7 @@ static int iwl_phy_db_send_all_channel_groups(
if (!entry)
return -EINVAL;
- if (WARN_ON_ONCE(!entry->size))
+ if (!entry->size)
continue;
/* Send the requested PHY DB section */
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index 100bd0d79681..5f657c501406 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -95,7 +95,8 @@
#define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */
#define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060)
-#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800)
+#define APMG_PCIDEV_STT_VAL_PERSIST_DIS (0x00000200)
+#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800)
#define APMG_RTC_INT_STT_RFKILL (0x10000000)
@@ -105,6 +106,33 @@
/* Device NMI register */
#define DEVICE_SET_NMI_REG 0x00a01c30
+/* Shared registers (0x0..0x3ff, via target indirect or periphery */
+#define SHR_BASE 0x00a10000
+
+/* Shared GP1 register */
+#define SHR_APMG_GP1_REG 0x01dc
+#define SHR_APMG_GP1_REG_PRPH (SHR_BASE + SHR_APMG_GP1_REG)
+#define SHR_APMG_GP1_WF_XTAL_LP_EN 0x00000004
+#define SHR_APMG_GP1_CHICKEN_BIT_SELECT 0x80000000
+
+/* Shared DL_CFG register */
+#define SHR_APMG_DL_CFG_REG 0x01c4
+#define SHR_APMG_DL_CFG_REG_PRPH (SHR_BASE + SHR_APMG_DL_CFG_REG)
+#define SHR_APMG_DL_CFG_RTCS_CLK_SELECTOR_MSK 0x000000c0
+#define SHR_APMG_DL_CFG_RTCS_CLK_INTERNAL_XTAL 0x00000080
+#define SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP 0x00000100
+
+/* Shared APMG_XTAL_CFG register */
+#define SHR_APMG_XTAL_CFG_REG 0x1c0
+#define SHR_APMG_XTAL_CFG_XTAL_ON_REQ 0x80000000
+
+/*
+ * Device reset for family 8000
+ * write to bit 24 in order to reset the CPU
+*/
+#define RELEASE_CPU_RESET (0x300C)
+#define RELEASE_CPU_RESET_BIT BIT(24)
+
/*****************************************************************************
* 7000/3000 series SHR DTS addresses *
*****************************************************************************/
@@ -281,4 +309,43 @@ static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl)
#define OSC_CLK (0xa04068)
#define OSC_CLK_FORCE_CONTROL (0x8)
+/* SECURE boot registers */
+#define LMPM_SECURE_BOOT_CONFIG_ADDR (0x100)
+enum secure_boot_config_reg {
+ LMPM_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP = 0x00000001,
+ LMPM_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ = 0x00000002,
+};
+
+#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR (0x1E30)
+#define LMPM_SECURE_BOOT_CPU2_STATUS_ADDR (0x1E34)
+enum secure_boot_status_reg {
+ LMPM_SECURE_BOOT_CPU_STATUS_VERF_STATUS = 0x00000001,
+ LMPM_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED = 0x00000002,
+ LMPM_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS = 0x00000004,
+ LMPM_SECURE_BOOT_CPU_STATUS_VERF_FAIL = 0x00000008,
+ LMPM_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL = 0x00000010,
+ LMPM_SECURE_BOOT_STATUS_SUCCESS = 0x00000003,
+};
+
+#define CSR_UCODE_LOAD_STATUS_ADDR (0x1E70)
+enum secure_load_status_reg {
+ LMPM_CPU_UCODE_LOADING_STARTED = 0x00000001,
+ LMPM_CPU_HDRS_LOADING_COMPLETED = 0x00000003,
+ LMPM_CPU_UCODE_LOADING_COMPLETED = 0x00000007,
+ LMPM_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8,
+ LMPM_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00,
+};
+
+#define LMPM_SECURE_INSPECTOR_CODE_ADDR (0x1E38)
+#define LMPM_SECURE_INSPECTOR_DATA_ADDR (0x1E3C)
+#define LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR (0x1E78)
+#define LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR (0x1E7C)
+
+#define LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE (0x400000)
+#define LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE (0x402000)
+#define LMPM_SECURE_CPU1_HDR_MEM_SPACE (0x420000)
+#define LMPM_SECURE_CPU2_HDR_MEM_SPACE (0x420400)
+
+#define LMPM_SECURE_TIME_OUT (100)
+
#endif /* __iwl_prph_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 1f065cf4a4ba..8cdb0dd618a6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -193,12 +193,23 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
* @CMD_ASYNC: Return right away and don't wait for the response
* @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the
* response. The caller needs to call iwl_free_resp when done.
+ * @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the
+ * command queue, but after other high priority commands. valid only
+ * with CMD_ASYNC.
+ * @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle.
+ * @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).
*/
enum CMD_MODE {
CMD_SYNC = 0,
CMD_ASYNC = BIT(0),
CMD_WANT_SKB = BIT(1),
CMD_SEND_IN_RFKILL = BIT(2),
+ CMD_HIGH_PRIO = BIT(3),
+ CMD_SEND_IN_IDLE = BIT(4),
+ CMD_MAKE_TRANS_IDLE = BIT(5),
+ CMD_WAKE_UP_TRANS = BIT(6),
};
#define DEF_CMD_PAYLOAD_SIZE 320
@@ -335,6 +346,9 @@ enum iwl_d3_status {
* @STATUS_INT_ENABLED: interrupts are enabled
* @STATUS_RFKILL: the HW RFkill switch is in KILL position
* @STATUS_FW_ERROR: the fw is in error state
+ * @STATUS_TRANS_GOING_IDLE: shutting down the trans, only special commands
+ * are sent
+ * @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent
*/
enum iwl_trans_status {
STATUS_SYNC_HCMD_ACTIVE,
@@ -343,6 +357,8 @@ enum iwl_trans_status {
STATUS_INT_ENABLED,
STATUS_RFKILL,
STATUS_FW_ERROR,
+ STATUS_TRANS_GOING_IDLE,
+ STATUS_TRANS_IDLE,
};
/**
@@ -377,7 +393,7 @@ struct iwl_trans_config {
bool rx_buf_size_8k;
bool bc_table_dword;
unsigned int queue_watchdog_timeout;
- const char **command_names;
+ const char *const *command_names;
};
struct iwl_trans;
@@ -443,6 +459,11 @@ struct iwl_trans;
* @release_nic_access: let the NIC go to sleep. The "flags" parameter
* must be the same one that was sent before to the grab_nic_access.
* @set_bits_mask - set SRAM register according to value and mask.
+ * @ref: grab a reference to the transport/FW layers, disallowing
+ * certain low power states
+ * @unref: release a reference previously taken with @ref. Note that
+ * initially the reference count is 1, making an initial @unref
+ * necessary to allow low power states.
*/
struct iwl_trans_ops {
@@ -489,6 +510,8 @@ struct iwl_trans_ops {
unsigned long *flags);
void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask,
u32 value);
+ void (*ref)(struct iwl_trans *trans);
+ void (*unref)(struct iwl_trans *trans);
};
/**
@@ -523,6 +546,7 @@ enum iwl_trans_state {
* starting the firmware, used for tracing
* @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the
* start of the 802.11 header in the @rx_mpdu_cmd
+ * @dflt_pwr_limit: default power limit fetched from the platform (ACPI)
*/
struct iwl_trans {
const struct iwl_trans_ops *ops;
@@ -551,6 +575,8 @@ struct iwl_trans {
struct lockdep_map sync_cmd_lockdep_map;
#endif
+ u64 dflt_pwr_limit;
+
/* pointer to trans specific struct */
/*Ensure that this pointer will always be aligned to sizeof pointer */
char trans_specific[0] __aligned(sizeof(void *));
@@ -627,6 +653,18 @@ static inline int iwl_trans_d3_resume(struct iwl_trans *trans,
return trans->ops->d3_resume(trans, status, test);
}
+static inline void iwl_trans_ref(struct iwl_trans *trans)
+{
+ if (trans->ops->ref)
+ trans->ops->ref(trans);
+}
+
+static inline void iwl_trans_unref(struct iwl_trans *trans)
+{
+ if (trans->ops->unref)
+ trans->ops->unref(trans);
+}
+
static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
struct iwl_host_cmd *cmd)
{
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile
index f98ec2b23898..ccdd3b7c4cce 100644
--- a/drivers/net/wireless/iwlwifi/mvm/Makefile
+++ b/drivers/net/wireless/iwlwifi/mvm/Makefile
@@ -2,8 +2,8 @@ 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 += scan.o time-event.o rs.o
-iwlmvm-y += power.o power_legacy.o bt-coex.o
-iwlmvm-y += led.o tt.o
+iwlmvm-y += power.o coex.o
+iwlmvm-y += led.o tt.o offloading.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c
index 18a895a949d4..685f7e8e6943 100644
--- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
+++ b/drivers/net/wireless/iwlwifi/mvm/coex.c
@@ -61,9 +61,11 @@
*
*****************************************************************************/
+#include <linux/ieee80211.h>
+#include <linux/etherdevice.h>
#include <net/mac80211.h>
-#include "fw-api-bt-coex.h"
+#include "fw-api-coex.h"
#include "iwl-modparams.h"
#include "mvm.h"
#include "iwl-debug.h"
@@ -305,6 +307,215 @@ static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = {
cpu_to_le32(0x33113311),
};
+struct corunning_block_luts {
+ u8 range;
+ __le32 lut20[BT_COEX_CORUN_LUT_SIZE];
+};
+
+/*
+ * Ranges for the antenna coupling calibration / co-running block LUT:
+ * LUT0: [ 0, 12[
+ * LUT1: [12, 20[
+ * LUT2: [20, 21[
+ * LUT3: [21, 23[
+ * LUT4: [23, 27[
+ * LUT5: [27, 30[
+ * LUT6: [30, 32[
+ * LUT7: [32, 33[
+ * LUT8: [33, - [
+ */
+static const struct corunning_block_luts antenna_coupling_ranges[] = {
+ {
+ .range = 0,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 12,
+ .lut20 = {
+ cpu_to_le32(0x00000001), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 20,
+ .lut20 = {
+ cpu_to_le32(0x00000002), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 21,
+ .lut20 = {
+ cpu_to_le32(0x00000003), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 23,
+ .lut20 = {
+ cpu_to_le32(0x00000004), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 27,
+ .lut20 = {
+ cpu_to_le32(0x00000005), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 30,
+ .lut20 = {
+ cpu_to_le32(0x00000006), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 32,
+ .lut20 = {
+ cpu_to_le32(0x00000007), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 33,
+ .lut20 = {
+ cpu_to_le32(0x00000008), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+};
+
static enum iwl_bt_coex_lut_type
iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
{
@@ -378,7 +589,6 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
flags = iwlwifi_mod_params.bt_coex_active ?
BT_COEX_NW : BT_COEX_DISABLE;
- flags |= BT_CH_PRIMARY_EN | BT_CH_SECONDARY_EN | BT_SYNC_2_BT_DISABLE;
bt_cmd->flags = cpu_to_le32(flags);
bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
@@ -391,14 +601,26 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
BT_VALID_LUT |
BT_VALID_WIFI_RX_SW_PRIO_BOOST |
BT_VALID_WIFI_TX_SW_PRIO_BOOST |
- BT_VALID_CORUN_LUT_20 |
- BT_VALID_CORUN_LUT_40 |
BT_VALID_ANT_ISOLATION |
BT_VALID_ANT_ISOLATION_THRS |
BT_VALID_TXTX_DELTA_FREQ_THRS |
BT_VALID_TXRX_MAX_FREQ_0 |
BT_VALID_SYNC_TO_SCO);
+ if (IWL_MVM_BT_COEX_SYNC2SCO)
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
+
+ if (IWL_MVM_BT_COEX_CORUNNING) {
+ bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_CORUN_LUT_20 |
+ BT_VALID_CORUN_LUT_40);
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
+ }
+
+ if (IWL_MVM_BT_COEX_MPLUT) {
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
+ bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
+ }
+
if (mvm->cfg->bt_shared_single_ant)
memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
sizeof(iwl_single_shared_ant));
@@ -406,6 +628,12 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
sizeof(iwl_combined_lookup));
+ /* Take first Co-running block LUT to get started */
+ memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20,
+ sizeof(bt_cmd->bt4_corun_lut20));
+ memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20,
+ sizeof(bt_cmd->bt4_corun_lut40));
+
memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
sizeof(iwl_bt_prio_boost));
memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
@@ -489,36 +717,26 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
return ret;
}
-static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
- bool enable)
+int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable)
{
struct iwl_bt_coex_cmd *bt_cmd;
/* Send ASYNC since this can be sent from an atomic context */
struct iwl_host_cmd cmd = {
.id = BT_CONFIG,
.len = { sizeof(*bt_cmd), },
- .dataflags = { IWL_HCMD_DFL_DUP, },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
.flags = CMD_ASYNC,
};
-
- struct ieee80211_sta *sta;
struct iwl_mvm_sta *mvmsta;
int ret;
- if (sta_id == IWL_MVM_STATION_COUNT)
+ mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
+ if (!mvmsta)
return 0;
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
- lockdep_is_held(&mvm->mutex));
-
- /* This can happen if the station has been removed right now */
- if (IS_ERR_OR_NULL(sta))
- return 0;
-
- mvmsta = iwl_mvm_sta_from_mac80211(sta);
-
/* nothing to do */
- if (mvmsta->bt_reduced_txpower == enable)
+ if (mvmsta->bt_reduced_txpower_dbg ||
+ mvmsta->bt_reduced_txpower == enable)
return 0;
bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
@@ -552,6 +770,7 @@ struct iwl_bt_iterator_data {
bool reduced_tx_power;
struct ieee80211_chanctx_conf *primary;
struct ieee80211_chanctx_conf *secondary;
+ bool primary_ll;
};
static inline
@@ -577,72 +796,113 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
struct iwl_mvm *mvm = data->mvm;
struct ieee80211_chanctx_conf *chanctx_conf;
enum ieee80211_smps_mode smps_mode;
+ u32 bt_activity_grading;
int ave_rssi;
lockdep_assert_held(&mvm->mutex);
- if (vif->type != NL80211_IFTYPE_STATION &&
- vif->type != NL80211_IFTYPE_AP)
- return;
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ /* default smps_mode for BSS / P2P client is AUTOMATIC */
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+ data->num_bss_ifaces++;
- smps_mode = IEEE80211_SMPS_AUTOMATIC;
+ /*
+ * Count unassoc BSSes, relax SMSP constraints
+ * and disable reduced Tx Power
+ */
+ if (!vif->bss_conf.assoc) {
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+ smps_mode);
+ if (iwl_mvm_bt_coex_reduced_txp(mvm,
+ mvmvif->ap_sta_id,
+ false))
+ IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+ return;
+ }
+ break;
+ case NL80211_IFTYPE_AP:
+ /* default smps_mode for AP / GO is OFF */
+ smps_mode = IEEE80211_SMPS_OFF;
+ if (!mvmvif->ap_ibss_active) {
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+ smps_mode);
+ return;
+ }
+
+ /* the Ack / Cts kill mask must be default if AP / GO */
+ data->reduced_tx_power = false;
+ break;
+ default:
+ return;
+ }
chanctx_conf = rcu_dereference(vif->chanctx_conf);
/* If channel context is invalid or not on 2.4GHz .. */
if ((!chanctx_conf ||
chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
- /* ... and it is an associated STATION, relax constraints */
- if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc)
- iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
- smps_mode);
- iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+ /* ... relax constraints and disable rssi events */
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+ smps_mode);
+ if (vif->type == NL80211_IFTYPE_STATION)
+ iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
return;
}
- /* SoftAP / GO will always be primary */
- if (vif->type == NL80211_IFTYPE_AP) {
- if (!mvmvif->ap_ibss_active)
- return;
+ bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);
+ if (bt_activity_grading >= BT_HIGH_TRAFFIC)
+ smps_mode = IEEE80211_SMPS_STATIC;
+ else if (bt_activity_grading >= BT_LOW_TRAFFIC)
+ smps_mode = vif->type == NL80211_IFTYPE_AP ?
+ IEEE80211_SMPS_OFF :
+ IEEE80211_SMPS_DYNAMIC;
+ IWL_DEBUG_COEX(data->mvm,
+ "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
+ mvmvif->id, data->notif->bt_status, bt_activity_grading,
+ smps_mode);
- /* the Ack / Cts kill mask must be default if AP / GO */
- data->reduced_tx_power = false;
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
- if (chanctx_conf == data->primary)
- return;
+ /* low latency is always primary */
+ if (iwl_mvm_vif_low_latency(mvmvif)) {
+ data->primary_ll = true;
- /* downgrade the current primary no matter what its type is */
data->secondary = data->primary;
data->primary = chanctx_conf;
- return;
}
- data->num_bss_ifaces++;
+ if (vif->type == NL80211_IFTYPE_AP) {
+ if (!mvmvif->ap_ibss_active)
+ return;
- /* we are now a STA / P2P Client, and take associated ones only */
- if (!vif->bss_conf.assoc)
+ if (chanctx_conf == data->primary)
+ return;
+
+ if (!data->primary_ll) {
+ /*
+ * downgrade the current primary no matter what its
+ * type is.
+ */
+ data->secondary = data->primary;
+ data->primary = chanctx_conf;
+ } else {
+ /* there is low latency vif - we will be secondary */
+ data->secondary = chanctx_conf;
+ }
return;
+ }
- /* STA / P2P Client, try to be primary if first vif */
+ /*
+ * STA / P2P Client, try to be primary if first vif. If we are in low
+ * latency mode, we are already in primary and just don't do much
+ */
if (!data->primary || data->primary == chanctx_conf)
data->primary = chanctx_conf;
else if (!data->secondary)
/* if secondary is not NULL, it might be a GO */
data->secondary = chanctx_conf;
- if (le32_to_cpu(data->notif->bt_activity_grading) >= BT_HIGH_TRAFFIC)
- smps_mode = IEEE80211_SMPS_STATIC;
- else if (le32_to_cpu(data->notif->bt_activity_grading) >=
- BT_LOW_TRAFFIC)
- smps_mode = IEEE80211_SMPS_DYNAMIC;
-
- IWL_DEBUG_COEX(data->mvm,
- "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
- mvmvif->id, data->notif->bt_status,
- data->notif->bt_activity_grading, smps_mode);
-
- iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
-
/* don't reduce the Tx power if in loose scheme */
if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
mvm->cfg->bt_shared_single_ant) {
@@ -918,8 +1178,8 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000)
#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200)
-u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta)
+u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
enum iwl_bt_coex_lut_type lut_type;
@@ -955,6 +1215,38 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT;
}
+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;
+
+ if (info->band != IEEE80211_BAND_2GHZ)
+ return 0;
+
+ 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:
+ return 3;
+ case IEEE80211_AC_VI:
+ return 2;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
{
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
@@ -962,3 +1254,69 @@ void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
iwl_mvm_bt_coex_notif_handle(mvm);
}
+
+int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *dev_cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ u32 ant_isolation = le32_to_cpup((void *)pkt->data);
+ u8 __maybe_unused lower_bound, upper_bound;
+ u8 lut;
+
+ struct iwl_bt_coex_cmd *bt_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = BT_CONFIG,
+ .len = { sizeof(*bt_cmd), },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ .flags = CMD_SYNC,
+ };
+
+ if (!IWL_MVM_BT_COEX_CORUNNING)
+ return 0;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (ant_isolation == mvm->last_ant_isol)
+ return 0;
+
+ for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++)
+ if (ant_isolation < antenna_coupling_ranges[lut + 1].range)
+ break;
+
+ lower_bound = antenna_coupling_ranges[lut].range;
+
+ if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1)
+ upper_bound = antenna_coupling_ranges[lut + 1].range;
+ else
+ upper_bound = antenna_coupling_ranges[lut].range;
+
+ IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n",
+ ant_isolation, lower_bound, upper_bound, lut);
+
+ mvm->last_ant_isol = ant_isolation;
+
+ if (mvm->last_corun_lut == lut)
+ return 0;
+
+ mvm->last_corun_lut = lut;
+
+ bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+ if (!bt_cmd)
+ return 0;
+ cmd.data[0] = bt_cmd;
+
+ bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
+ bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
+ BT_VALID_CORUN_LUT_20 |
+ BT_VALID_CORUN_LUT_40);
+
+ /* For the moment, use the same LUT for 20GHz and 40GHz */
+ memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20,
+ sizeof(bt_cmd->bt4_corun_lut20));
+
+ memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
+ sizeof(bt_cmd->bt4_corun_lut40));
+
+ return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h
index 036857698565..51685693af2e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/constants.h
+++ b/drivers/net/wireless/iwlwifi/mvm/constants.h
@@ -78,5 +78,9 @@
#define IWL_MVM_PS_SNOOZE_INTERVAL 25
#define IWL_MVM_PS_SNOOZE_WINDOW 50
#define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25
+#define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64
+#define IWL_MVM_BT_COEX_SYNC2SCO 1
+#define IWL_MVM_BT_COEX_CORUNNING 1
+#define IWL_MVM_BT_COEX_MPLUT 1
#endif /* __MVM_CONSTANTS_H */
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
index f36a7ee0267f..e56f5a0edf85 100644
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/iwlwifi/mvm/d3.c
@@ -376,139 +376,6 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
return err;
}
-static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- union {
- struct iwl_proto_offload_cmd_v1 v1;
- struct iwl_proto_offload_cmd_v2 v2;
- struct iwl_proto_offload_cmd_v3_small v3s;
- struct iwl_proto_offload_cmd_v3_large v3l;
- } cmd = {};
- struct iwl_host_cmd hcmd = {
- .id = PROT_OFFLOAD_CONFIG_CMD,
- .flags = CMD_SYNC,
- .data[0] = &cmd,
- .dataflags[0] = IWL_HCMD_DFL_DUP,
- };
- struct iwl_proto_offload_cmd_common *common;
- u32 enabled = 0, size;
- u32 capa_flags = mvm->fw->ucode_capa.flags;
-#if IS_ENABLED(CONFIG_IPV6)
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- int i;
-
- if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
- capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
- struct iwl_ns_config *nsc;
- struct iwl_targ_addr *addrs;
- int n_nsc, n_addrs;
- int c;
-
- if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
- nsc = cmd.v3s.ns_config;
- n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;
- addrs = cmd.v3s.targ_addrs;
- n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;
- } else {
- nsc = cmd.v3l.ns_config;
- n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
- addrs = cmd.v3l.targ_addrs;
- 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
- * solicited node addresses.
- */
- for (i = 0, c = 0;
- i < mvmvif->num_target_ipv6_addrs &&
- i < n_addrs && c < n_nsc; i++) {
- struct in6_addr solicited_addr;
- int j;
-
- addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
- &solicited_addr);
- for (j = 0; j < c; j++)
- if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
- &solicited_addr) == 0)
- break;
- if (j == c)
- c++;
- addrs[i].addr = mvmvif->target_ipv6_addrs[i];
- addrs[i].config_num = cpu_to_le32(j);
- nsc[j].dest_ipv6_addr = solicited_addr;
- memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
- }
-
- if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
- cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i);
- else
- cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i);
- } 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);
- }
-
- 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++)
- 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);
- }
-
- 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++)
- memcpy(cmd.v1.target_ipv6_addr[i],
- &mvmvif->target_ipv6_addrs[i],
- sizeof(cmd.v1.target_ipv6_addr[i]));
- }
-#endif
-
- if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
- common = &cmd.v3s.common;
- size = sizeof(cmd.v3s);
- } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
- common = &cmd.v3l.common;
- size = sizeof(cmd.v3l);
- } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
- common = &cmd.v2.common;
- size = sizeof(cmd.v2);
- } else {
- common = &cmd.v1.common;
- size = sizeof(cmd.v1);
- }
-
- if (vif->bss_conf.arp_addr_cnt) {
- enabled |= IWL_D3_PROTO_OFFLOAD_ARP;
- common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
- memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
- }
-
- if (!enabled)
- return 0;
-
- common->enabled = cpu_to_le32(enabled);
-
- hcmd.len[0] = size;
- return iwl_mvm_send_cmd(mvm, &hcmd);
-}
-
enum iwl_mvm_tcp_packet_type {
MVM_TCP_TX_SYN,
MVM_TCP_RX_SYNACK,
@@ -846,8 +713,8 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
quota_cmd.quotas[0].id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
mvmvif->phy_ctxt->color));
- quota_cmd.quotas[0].quota = cpu_to_le32(100);
- quota_cmd.quotas[0].max_duration = cpu_to_le32(1000);
+ quota_cmd.quotas[0].quota = cpu_to_le32(IWL_MVM_MAX_QUOTA);
+ quota_cmd.quotas[0].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA);
for (i = 1; i < MAX_BINDINGS; i++)
quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
@@ -927,6 +794,20 @@ void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
IWL_ERR(mvm, "failed to set non-QoS seqno\n");
}
+static int
+iwl_mvm_send_wowlan_config_cmd(struct iwl_mvm *mvm,
+ const struct iwl_wowlan_config_cmd_v3 *cmd)
+{
+ /* start only with the v2 part of the command */
+ u16 cmd_len = sizeof(cmd->common);
+
+ if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID)
+ cmd_len = sizeof(*cmd);
+
+ return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, CMD_SYNC,
+ cmd_len, cmd);
+}
+
static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan,
bool test)
@@ -939,7 +820,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
struct iwl_mvm_vif *mvmvif;
struct ieee80211_sta *ap_sta;
struct iwl_mvm_sta *mvm_ap_sta;
- struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
+ struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = {};
struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
struct iwl_d3_manager_config d3_cfg_cmd_data = {
@@ -961,9 +842,8 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
.tkip = &tkip_cmd,
.use_tkip = false,
};
- int ret, i;
+ int ret;
int len __maybe_unused;
- u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT;
if (!wowlan) {
/*
@@ -980,8 +860,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
- old_aux_sta_id = mvm->aux_sta.sta_id;
-
/* see if there's only a single BSS vif and it's associated */
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
@@ -1005,49 +883,41 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
- /* TODO: wowlan_config_cmd.wowlan_ba_teardown_tids */
+ /* TODO: wowlan_config_cmd.common.wowlan_ba_teardown_tids */
- wowlan_config_cmd.is_11n_connection = ap_sta->ht_cap.ht_supported;
+ wowlan_config_cmd.common.is_11n_connection =
+ ap_sta->ht_cap.ht_supported;
/* Query the last used seqno and set it */
ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
if (ret < 0)
goto out_noreset;
- wowlan_config_cmd.non_qos_seq = cpu_to_le16(ret);
+ wowlan_config_cmd.common.non_qos_seq = cpu_to_le16(ret);
- /*
- * For QoS counters, we store the one to use next, so subtract 0x10
- * since the uCode will add 0x10 *before* using the value while we
- * increment after using the value (i.e. store the next value to use).
- */
- for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
- u16 seq = mvm_ap_sta->tid_data[i].seq_number;
- seq -= 0x10;
- wowlan_config_cmd.qos_seq[i] = cpu_to_le16(seq);
- }
+ iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &wowlan_config_cmd.common);
if (wowlan->disconnect)
- wowlan_config_cmd.wakeup_filter |=
+ wowlan_config_cmd.common.wakeup_filter |=
cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
IWL_WOWLAN_WAKEUP_LINK_CHANGE);
if (wowlan->magic_pkt)
- wowlan_config_cmd.wakeup_filter |=
+ wowlan_config_cmd.common.wakeup_filter |=
cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET);
if (wowlan->gtk_rekey_failure)
- wowlan_config_cmd.wakeup_filter |=
+ wowlan_config_cmd.common.wakeup_filter |=
cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
if (wowlan->eap_identity_req)
- wowlan_config_cmd.wakeup_filter |=
+ wowlan_config_cmd.common.wakeup_filter |=
cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ);
if (wowlan->four_way_handshake)
- wowlan_config_cmd.wakeup_filter |=
+ wowlan_config_cmd.common.wakeup_filter |=
cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
if (wowlan->n_patterns)
- wowlan_config_cmd.wakeup_filter |=
+ wowlan_config_cmd.common.wakeup_filter |=
cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
if (wowlan->rfkill_release)
- wowlan_config_cmd.wakeup_filter |=
+ wowlan_config_cmd.common.wakeup_filter |=
cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
if (wowlan->tcp) {
@@ -1055,7 +925,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
* Set the "link change" (really "link lost") flag as well
* since that implies losing the TCP connection.
*/
- wowlan_config_cmd.wakeup_filter |=
+ wowlan_config_cmd.common.wakeup_filter |=
cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS |
IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE |
IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET |
@@ -1067,16 +937,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
iwl_trans_stop_device(mvm->trans);
/*
- * The D3 firmware still hardcodes the AP station ID for the
- * BSS we're associated with as 0. Store the real STA ID here
- * and assign 0. When we leave this function, we'll restore
- * the original value for the resume code.
- */
- old_ap_sta_id = mvm_ap_sta->sta_id;
- mvm_ap_sta->sta_id = 0;
- mvmvif->ap_sta_id = 0;
-
- /*
* Set the HW restart bit -- this is mostly true as we're
* going to load new firmware and reprogram that, though
* the reprogramming is going to be manual to avoid adding
@@ -1096,16 +956,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
mvm->ptk_ivlen = 0;
mvm->ptk_icvlen = 0;
- /*
- * The D3 firmware still hardcodes the AP station ID for the
- * BSS we're associated with as 0. As a result, we have to move
- * the auxiliary station to ID 1 so the ID 0 remains free for
- * the AP station for later.
- * We set the sta_id to 1 here, and reset it to its previous
- * value (that we stored above) later.
- */
- mvm->aux_sta.sta_id = 1;
-
ret = iwl_mvm_load_d3_fw(mvm);
if (ret)
goto out;
@@ -1173,9 +1023,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
}
}
- ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION,
- CMD_SYNC, sizeof(wowlan_config_cmd),
- &wowlan_config_cmd);
+ ret = iwl_mvm_send_wowlan_config_cmd(mvm, &wowlan_config_cmd);
if (ret)
goto out;
@@ -1183,7 +1031,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
if (ret)
goto out;
- ret = iwl_mvm_send_proto_offload(mvm, vif);
+ ret = iwl_mvm_send_proto_offload(mvm, vif, false, CMD_SYNC);
if (ret)
goto out;
@@ -1191,11 +1039,11 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
if (ret)
goto out;
- ret = iwl_mvm_power_update_device_mode(mvm);
+ ret = iwl_mvm_power_update_device(mvm);
if (ret)
goto out;
- ret = iwl_mvm_power_update_mode(mvm, vif);
+ ret = iwl_mvm_power_update_mac(mvm, vif);
if (ret)
goto out;
@@ -1222,10 +1070,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
iwl_trans_d3_suspend(mvm->trans, test);
out:
- mvm->aux_sta.sta_id = old_aux_sta_id;
- mvm_ap_sta->sta_id = old_ap_sta_id;
- mvmvif->ap_sta_id = old_ap_sta_id;
-
if (ret < 0)
ieee80211_restart_hw(mvm->hw);
out_noreset:
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
index 0e29cd83a06a..9b59e1d7ae71 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
@@ -185,7 +185,7 @@ static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
mutex_lock(&mvm->mutex);
iwl_dbgfs_update_pm(mvm, vif, param, val);
- ret = iwl_mvm_power_update_mode(mvm, vif);
+ ret = iwl_mvm_power_update_mac(mvm, vif);
mutex_unlock(&mvm->mutex);
return ret ?: count;
@@ -202,7 +202,7 @@ static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
int bufsz = sizeof(buf);
int pos;
- pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz);
+ pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz);
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
@@ -225,6 +225,29 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
ap_sta_id = mvmvif->ap_sta_id;
+ switch (ieee80211_vif_type_p2p(vif)) {
+ case NL80211_IFTYPE_ADHOC:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n");
+ break;
+ case NL80211_IFTYPE_STATION:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n");
+ break;
+ case NL80211_IFTYPE_AP:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n");
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n");
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n");
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n");
+ break;
+ default:
+ break;
+ }
+
pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n",
mvmvif->id, mvmvif->color);
pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n",
@@ -249,9 +272,10 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
pos += scnprintf(buf+pos, bufsz-pos,
- "ap_sta_id %d - reduced Tx power %d\n",
+ "ap_sta_id %d - reduced Tx power %d force %d\n",
ap_sta_id,
- mvm_sta->bt_reduced_txpower);
+ mvm_sta->bt_reduced_txpower,
+ mvm_sta->bt_reduced_txpower_dbg);
}
}
@@ -269,6 +293,41 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
+static ssize_t iwl_dbgfs_reduced_txp_write(struct ieee80211_vif *vif,
+ char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ struct iwl_mvm_sta *mvmsta;
+ bool reduced_tx_power;
+ int ret;
+
+ if (mvmvif->ap_sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
+ return -ENOTCONN;
+
+ if (strtobool(buf, &reduced_tx_power) != 0)
+ return -EINVAL;
+
+ mutex_lock(&mvm->mutex);
+
+ mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
+ if (IS_ERR_OR_NULL(mvmsta)) {
+ mutex_unlock(&mvm->mutex);
+ return -ENOTCONN;
+ }
+
+ mvmsta->bt_reduced_txpower_dbg = false;
+ ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+ reduced_tx_power);
+ if (!ret)
+ mvmsta->bt_reduced_txpower_dbg = true;
+
+ mutex_unlock(&mvm->mutex);
+
+ return ret ? : count;
+}
+
static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
enum iwl_dbgfs_bf_mask param, int value)
{
@@ -403,9 +462,9 @@ static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
mutex_lock(&mvm->mutex);
iwl_dbgfs_update_bf(vif, param, value);
if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value)
- ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
else
- ret = iwl_mvm_enable_beacon_filter(mvm, vif);
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC);
mutex_unlock(&mvm->mutex);
return ret ?: count;
@@ -460,6 +519,41 @@ static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
+static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = mvmvif->mvm;
+ u8 value;
+ int ret;
+
+ ret = kstrtou8(buf, 0, &value);
+ if (ret)
+ return ret;
+ if (value > 1)
+ return -EINVAL;
+
+ mutex_lock(&mvm->mutex);
+ iwl_mvm_update_low_latency(mvm, vif, value);
+ mutex_unlock(&mvm->mutex);
+
+ return count;
+}
+
+static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_vif *vif = file->private_data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ char buf[3];
+
+ buf[0] = mvmvif->low_latency ? '1' : '0';
+ buf[1] = '\n';
+ buf[2] = '\0';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
+}
+
#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
@@ -473,6 +567,8 @@ static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
MVM_DEBUGFS_READ_FILE_OPS(mac_params);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
+MVM_DEBUGFS_WRITE_FILE_OPS(reduced_txp, 10);
void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
@@ -496,15 +592,18 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
return;
}
- if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
+ if ((mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT) &&
+ iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
(vif->type == NL80211_IFTYPE_STATION && vif->p2p &&
- mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS)))
+ mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)))
MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR |
S_IRUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir,
- S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(reduced_txp, mvmvif->dbgfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir,
+ S_IRUSR | S_IWUSR);
if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
mvmvif == mvm->bf_allowed_vif)
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
index 369d4c90e669..1b52deea6081 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
@@ -60,11 +60,14 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
+#include <linux/vmalloc.h>
+
#include "mvm.h"
#include "sta.h"
#include "iwl-io.h"
#include "iwl-prph.h"
#include "debugfs.h"
+#include "fw-error-dump.h"
static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
size_t count, loff_t *ppos)
@@ -90,7 +93,7 @@ static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf,
size_t count, loff_t *ppos)
{
- struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
int sta_id, drain, ret;
if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
@@ -105,19 +108,63 @@ static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf,
mutex_lock(&mvm->mutex);
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
- lockdep_is_held(&mvm->mutex));
- if (IS_ERR_OR_NULL(sta))
+ mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
+
+ if (!mvmsta)
ret = -ENOENT;
else
- ret = iwl_mvm_drain_sta(mvm, (void *)sta->drv_priv, drain) ? :
- count;
+ ret = iwl_mvm_drain_sta(mvm, mvmsta, drain) ? : count;
mutex_unlock(&mvm->mutex);
return ret;
}
+static int iwl_dbgfs_fw_error_dump_open(struct inode *inode, struct file *file)
+{
+ struct iwl_mvm *mvm = inode->i_private;
+ int ret;
+
+ if (!mvm)
+ return -EINVAL;
+
+ mutex_lock(&mvm->mutex);
+ if (!mvm->fw_error_dump) {
+ ret = -ENODATA;
+ goto out;
+ }
+
+ file->private_data = mvm->fw_error_dump;
+ mvm->fw_error_dump = NULL;
+ kfree(mvm->fw_error_sram);
+ mvm->fw_error_sram = NULL;
+ mvm->fw_error_sram_len = 0;
+ ret = 0;
+
+out:
+ mutex_unlock(&mvm->mutex);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_fw_error_dump_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_fw_error_dump_file *dump_file = file->private_data;
+
+ return simple_read_from_buffer(user_buf, count, ppos,
+ dump_file,
+ le32_to_cpu(dump_file->file_len));
+}
+
+static int iwl_dbgfs_fw_error_dump_release(struct inode *inode,
+ struct file *file)
+{
+ vfree(file->private_data);
+
+ return 0;
+}
+
static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -251,7 +298,7 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,
}
mutex_lock(&mvm->mutex);
- ret = iwl_mvm_power_update_device_mode(mvm);
+ ret = iwl_mvm_power_update_device(mvm);
mutex_unlock(&mvm->mutex);
return ret ?: count;
@@ -351,6 +398,9 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
le32_to_cpu(notif->secondary_ch_lut));
pos += scnprintf(buf+pos, bufsz-pos, "bt_activity_grading = %d\n",
le32_to_cpu(notif->bt_activity_grading));
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "antenna isolation = %d CORUN LUT index = %d\n",
+ mvm->last_ant_isol, mvm->last_corun_lut);
mutex_unlock(&mvm->mutex);
@@ -393,6 +443,22 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
+static ssize_t
+iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ u32 bt_tx_prio;
+
+ if (sscanf(buf, "%u", &bt_tx_prio) != 1)
+ return -EINVAL;
+ if (bt_tx_prio > 4)
+ return -EINVAL;
+
+ mvm->bt_tx_prio = bt_tx_prio;
+
+ return count;
+}
+
#define PRINT_STATS_LE32(_str, _val) \
pos += scnprintf(buf + pos, bufsz - pos, \
fmt_table, _str, \
@@ -532,6 +598,80 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
}
#undef PRINT_STAT_LE32
+static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm,
+ char __user *user_buf, size_t count,
+ loff_t *ppos,
+ struct iwl_mvm_frame_stats *stats)
+{
+ char *buff, *pos, *endpos;
+ int idx, i;
+ int ret;
+ static const size_t bufsz = 1024;
+
+ buff = kmalloc(bufsz, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ spin_lock_bh(&mvm->drv_stats_lock);
+
+ pos = buff;
+ endpos = pos + bufsz;
+
+ pos += scnprintf(pos, endpos - pos,
+ "Legacy/HT/VHT\t:\t%d/%d/%d\n",
+ stats->legacy_frames,
+ stats->ht_frames,
+ stats->vht_frames);
+ pos += scnprintf(pos, endpos - pos, "20/40/80\t:\t%d/%d/%d\n",
+ stats->bw_20_frames,
+ stats->bw_40_frames,
+ stats->bw_80_frames);
+ pos += scnprintf(pos, endpos - pos, "NGI/SGI\t\t:\t%d/%d\n",
+ stats->ngi_frames,
+ stats->sgi_frames);
+ pos += scnprintf(pos, endpos - pos, "SISO/MIMO2\t:\t%d/%d\n",
+ stats->siso_frames,
+ stats->mimo2_frames);
+ pos += scnprintf(pos, endpos - pos, "FAIL/SCSS\t:\t%d/%d\n",
+ stats->fail_frames,
+ stats->success_frames);
+ pos += scnprintf(pos, endpos - pos, "MPDUs agg\t:\t%d\n",
+ stats->agg_frames);
+ pos += scnprintf(pos, endpos - pos, "A-MPDUs\t\t:\t%d\n",
+ stats->ampdu_count);
+ pos += scnprintf(pos, endpos - pos, "Avg MPDUs/A-MPDU:\t%d\n",
+ stats->ampdu_count > 0 ?
+ (stats->agg_frames / stats->ampdu_count) : 0);
+
+ pos += scnprintf(pos, endpos - pos, "Last Rates\n");
+
+ idx = stats->last_frame_idx - 1;
+ for (i = 0; i < ARRAY_SIZE(stats->last_rates); i++) {
+ idx = (idx + 1) % ARRAY_SIZE(stats->last_rates);
+ if (stats->last_rates[idx] == 0)
+ continue;
+ pos += scnprintf(pos, endpos - pos, "Rate[%d]: ",
+ (int)(ARRAY_SIZE(stats->last_rates) - i));
+ pos += rs_pretty_print_rate(pos, stats->last_rates[idx]);
+ }
+ spin_unlock_bh(&mvm->drv_stats_lock);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);
+ kfree(buff);
+
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_drv_rx_stats_read(struct file *file,
+ char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+
+ return iwl_dbgfs_frame_stats_read(mvm, user_buf, count, ppos,
+ &mvm->drv_rx_stats);
+}
+
static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,
size_t count, loff_t *ppos)
{
@@ -592,7 +732,7 @@ iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf,
return -EINVAL;
if (scan_rx_ant > ANT_ABC)
return -EINVAL;
- if (scan_rx_ant & ~iwl_fw_valid_rx_ant(mvm->fw))
+ if (scan_rx_ant & ~mvm->fw->valid_rx_ant)
return -EINVAL;
mvm->scan_rx_ant = scan_rx_ant;
@@ -600,6 +740,187 @@ iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf,
return count;
}
+#define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__)
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ struct iwl_bcast_filter_cmd cmd;
+ const struct iwl_fw_bcast_filter *filter;
+ char *buf;
+ int bufsz = 1024;
+ int i, j, pos = 0;
+ ssize_t ret;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&mvm->mutex);
+ if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
+ ADD_TEXT("None\n");
+ mutex_unlock(&mvm->mutex);
+ goto out;
+ }
+ mutex_unlock(&mvm->mutex);
+
+ for (i = 0; cmd.filters[i].attrs[0].mask; i++) {
+ filter = &cmd.filters[i];
+
+ ADD_TEXT("Filter [%d]:\n", i);
+ ADD_TEXT("\tDiscard=%d\n", filter->discard);
+ ADD_TEXT("\tFrame Type: %s\n",
+ filter->frame_type ? "IPv4" : "Generic");
+
+ for (j = 0; j < ARRAY_SIZE(filter->attrs); j++) {
+ const struct iwl_fw_bcast_filter_attr *attr;
+
+ attr = &filter->attrs[j];
+ if (!attr->mask)
+ break;
+
+ ADD_TEXT("\tAttr [%d]: offset=%d (from %s), mask=0x%x, value=0x%x reserved=0x%x\n",
+ j, attr->offset,
+ attr->offset_type ? "IP End" :
+ "Payload Start",
+ be32_to_cpu(attr->mask),
+ be32_to_cpu(attr->val),
+ le16_to_cpu(attr->reserved1));
+ }
+ }
+out:
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ int pos, next_pos;
+ struct iwl_fw_bcast_filter filter = {};
+ struct iwl_bcast_filter_cmd cmd;
+ u32 filter_id, attr_id, mask, value;
+ int err = 0;
+
+ if (sscanf(buf, "%d %hhi %hhi %n", &filter_id, &filter.discard,
+ &filter.frame_type, &pos) != 3)
+ return -EINVAL;
+
+ if (filter_id >= ARRAY_SIZE(mvm->dbgfs_bcast_filtering.cmd.filters) ||
+ filter.frame_type > BCAST_FILTER_FRAME_TYPE_IPV4)
+ return -EINVAL;
+
+ for (attr_id = 0; attr_id < ARRAY_SIZE(filter.attrs);
+ attr_id++) {
+ struct iwl_fw_bcast_filter_attr *attr =
+ &filter.attrs[attr_id];
+
+ if (pos >= count)
+ break;
+
+ if (sscanf(&buf[pos], "%hhi %hhi %i %i %n",
+ &attr->offset, &attr->offset_type,
+ &mask, &value, &next_pos) != 4)
+ return -EINVAL;
+
+ attr->mask = cpu_to_be32(mask);
+ attr->val = cpu_to_be32(value);
+ if (mask)
+ filter.num_attrs++;
+
+ pos += next_pos;
+ }
+
+ mutex_lock(&mvm->mutex);
+ memcpy(&mvm->dbgfs_bcast_filtering.cmd.filters[filter_id],
+ &filter, sizeof(filter));
+
+ /* send updated bcast filtering configuration */
+ if (mvm->dbgfs_bcast_filtering.override &&
+ iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
+ err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+ sizeof(cmd), &cmd);
+ mutex_unlock(&mvm->mutex);
+
+ return err ?: count;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_macs_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ struct iwl_bcast_filter_cmd cmd;
+ char *buf;
+ int bufsz = 1024;
+ int i, pos = 0;
+ ssize_t ret;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&mvm->mutex);
+ if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
+ ADD_TEXT("None\n");
+ mutex_unlock(&mvm->mutex);
+ goto out;
+ }
+ mutex_unlock(&mvm->mutex);
+
+ for (i = 0; i < ARRAY_SIZE(cmd.macs); i++) {
+ const struct iwl_fw_bcast_mac *mac = &cmd.macs[i];
+
+ ADD_TEXT("Mac [%d]: discard=%d attached_filters=0x%x\n",
+ i, mac->default_discard, mac->attached_filters);
+ }
+out:
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm,
+ char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct iwl_bcast_filter_cmd cmd;
+ struct iwl_fw_bcast_mac mac = {};
+ u32 mac_id, attached_filters;
+ int err = 0;
+
+ if (!mvm->bcast_filters)
+ return -ENOENT;
+
+ if (sscanf(buf, "%d %hhi %i", &mac_id, &mac.default_discard,
+ &attached_filters) != 3)
+ return -EINVAL;
+
+ if (mac_id >= ARRAY_SIZE(cmd.macs) ||
+ mac.default_discard > 1 ||
+ attached_filters >= BIT(ARRAY_SIZE(cmd.filters)))
+ return -EINVAL;
+
+ mac.attached_filters = cpu_to_le16(attached_filters);
+
+ mutex_lock(&mvm->mutex);
+ memcpy(&mvm->dbgfs_bcast_filtering.cmd.macs[mac_id],
+ &mac, sizeof(mac));
+
+ /* send updated bcast filtering configuration */
+ if (mvm->dbgfs_bcast_filtering.override &&
+ iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
+ err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+ sizeof(cmd), &cmd);
+ mutex_unlock(&mvm->mutex);
+
+ return err ?: count;
+}
+#endif
+
#ifdef CONFIG_PM_SLEEP
static ssize_t iwl_dbgfs_d3_sram_write(struct iwl_mvm *mvm, char *buf,
size_t count, loff_t *ppos)
@@ -658,15 +979,117 @@ static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
}
#endif
+#define PRINT_MVM_REF(ref) do { \
+ if (test_bit(ref, mvm->ref_bitmap)) \
+ pos += scnprintf(buf + pos, bufsz - pos, \
+ "\t(0x%lx) %s\n", \
+ BIT(ref), #ref); \
+} while (0)
+
+static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ int pos = 0;
+ char buf[256];
+ const size_t bufsz = sizeof(buf);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%lx\n",
+ mvm->ref_bitmap[0]);
+
+ PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN);
+ PRINT_MVM_REF(IWL_MVM_REF_SCAN);
+ PRINT_MVM_REF(IWL_MVM_REF_ROC);
+ PRINT_MVM_REF(IWL_MVM_REF_P2P_CLIENT);
+ PRINT_MVM_REF(IWL_MVM_REF_AP_IBSS);
+ PRINT_MVM_REF(IWL_MVM_REF_USER);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long value;
+ int ret;
+ bool taken;
+
+ ret = kstrtoul(buf, 10, &value);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&mvm->mutex);
+
+ taken = test_bit(IWL_MVM_REF_USER, mvm->ref_bitmap);
+ if (value == 1 && !taken)
+ iwl_mvm_ref(mvm, IWL_MVM_REF_USER);
+ else if (value == 0 && taken)
+ iwl_mvm_unref(mvm, IWL_MVM_REF_USER);
+ else
+ ret = -EINVAL;
+
+ mutex_unlock(&mvm->mutex);
+
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
_MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
-#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) do { \
- if (!debugfs_create_file(#name, mode, parent, mvm, \
+#define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \
+ if (!debugfs_create_file(alias, mode, parent, mvm, \
&iwl_dbgfs_##name##_ops)) \
goto err; \
} while (0)
+#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \
+ MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
+
+static ssize_t
+iwl_dbgfs_prph_reg_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ int pos = 0;
+ char buf[32];
+ const size_t bufsz = sizeof(buf);
+
+ if (!mvm->dbgfs_prph_reg_addr)
+ return -EINVAL;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "Reg 0x%x: (0x%x)\n",
+ mvm->dbgfs_prph_reg_addr,
+ iwl_read_prph(mvm->trans, mvm->dbgfs_prph_reg_addr));
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t
+iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ u8 args;
+ u32 value;
+
+ args = sscanf(buf, "%i %i", &mvm->dbgfs_prph_reg_addr, &value);
+ /* if we only want to set the reg address - nothing more to do */
+ if (args == 1)
+ goto out;
+
+ /* otherwise, make sure we have both address and value */
+ if (args != 2)
+ return -EINVAL;
+
+ iwl_write_prph(mvm->trans, mvm->dbgfs_prph_reg_addr, value);
+out:
+ return count;
+}
+
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);
/* Device wide debugfs entries */
MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16);
@@ -677,9 +1100,23 @@ MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
MVM_DEBUGFS_READ_FILE_OPS(bt_cmd);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64);
MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats);
+MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);
+MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
+
+static const struct file_operations iwl_dbgfs_fw_error_dump_ops = {
+ .open = iwl_dbgfs_fw_error_dump_open,
+ .read = iwl_dbgfs_fw_error_dump_read,
+ .release = iwl_dbgfs_fw_error_dump_release,
+};
+
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
+#endif
#ifdef CONFIG_PM_SLEEP
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
@@ -687,24 +1124,52 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
{
+ struct dentry *bcast_dir __maybe_unused;
char buf[100];
+ spin_lock_init(&mvm->drv_stats_lock);
+
mvm->debugfs_dir = dbgfs_dir;
MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(fw_error_dump, dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR);
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)
MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir,
S_IRUSR | S_IWUSR);
MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
S_IWUSR | S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
+
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
+ bcast_dir = debugfs_create_dir("bcast_filtering",
+ mvm->debugfs_dir);
+ if (!bcast_dir)
+ goto err;
+
+ if (!debugfs_create_bool("override", S_IRUSR | S_IWUSR,
+ bcast_dir,
+ &mvm->dbgfs_bcast_filtering.override))
+ goto err;
+
+ MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters,
+ bcast_dir, S_IWUSR | S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs,
+ bcast_dir, S_IWUSR | S_IRUSR);
+ }
+#endif
+
#ifdef CONFIG_PM_SLEEP
MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR);
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
index 1b4e54d416b0..21877e5966a8 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
@@ -70,37 +70,28 @@
/**
* enum iwl_bt_coex_flags - flags for BT_COEX command
- * @BT_CH_PRIMARY_EN:
- * @BT_CH_SECONDARY_EN:
- * @BT_NOTIF_COEX_OFF:
* @BT_COEX_MODE_POS:
* @BT_COEX_MODE_MSK:
* @BT_COEX_DISABLE:
* @BT_COEX_2W:
* @BT_COEX_3W:
* @BT_COEX_NW:
- * @BT_USE_DEFAULTS:
- * @BT_SYNC_2_BT_DISABLE:
- * @BT_COEX_CORUNNING_TBL_EN:
+ * @BT_COEX_SYNC2SCO:
+ * @BT_COEX_CORUNNING:
+ * @BT_COEX_MPLUT:
*
* The COEX_MODE must be set for each command. Even if it is not changed.
*/
enum iwl_bt_coex_flags {
- BT_CH_PRIMARY_EN = BIT(0),
- BT_CH_SECONDARY_EN = BIT(1),
- BT_NOTIF_COEX_OFF = BIT(2),
BT_COEX_MODE_POS = 3,
BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS,
BT_COEX_DISABLE = 0x0 << BT_COEX_MODE_POS,
BT_COEX_2W = 0x1 << BT_COEX_MODE_POS,
BT_COEX_3W = 0x2 << BT_COEX_MODE_POS,
BT_COEX_NW = 0x3 << BT_COEX_MODE_POS,
- BT_USE_DEFAULTS = BIT(6),
- BT_SYNC_2_BT_DISABLE = BIT(7),
- BT_COEX_CORUNNING_TBL_EN = BIT(8),
- BT_COEX_MPLUT_TBL_EN = BIT(9),
- /* Bit 10 is reserved */
- BT_COEX_WF_PRIO_BOOST_CHECK_EN = BIT(11),
+ BT_COEX_SYNC2SCO = BIT(7),
+ BT_COEX_CORUNNING = BIT(8),
+ BT_COEX_MPLUT = BIT(9),
};
/*
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
index 8415ff312d0e..10fcc1a79ebd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
@@ -231,11 +231,15 @@ enum iwl_wowlan_wakeup_filters {
IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT = BIT(8),
IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS = BIT(9),
IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE = BIT(10),
- /* BIT(11) reserved */
+ IWL_WOWLAN_WAKEUP_REMOTE_TCP_EXTERNAL = BIT(11),
IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET = BIT(12),
+ IWL_WOWLAN_WAKEUP_IOAC_MAGIC_PACKET = BIT(13),
+ IWL_WOWLAN_WAKEUP_HOST_TIMER = BIT(14),
+ IWL_WOWLAN_WAKEUP_RX_FRAME = BIT(15),
+ IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16),
}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
-struct iwl_wowlan_config_cmd {
+struct iwl_wowlan_config_cmd_v2 {
__le32 wakeup_filter;
__le16 non_qos_seq;
__le16 qos_seq[8];
@@ -243,6 +247,12 @@ struct iwl_wowlan_config_cmd {
u8 is_11n_connection;
} __packed; /* WOWLAN_CONFIG_API_S_VER_2 */
+struct iwl_wowlan_config_cmd_v3 {
+ struct iwl_wowlan_config_cmd_v2 common;
+ u8 offloading_tid;
+ u8 reserved[3];
+} __packed; /* WOWLAN_CONFIG_API_S_VER_3 */
+
/*
* WOWLAN_TSC_RSC_PARAMS
*/
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
index 884c08725308..cbbcd8e284e4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
@@ -301,54 +301,65 @@ struct iwl_beacon_filter_cmd {
/* Beacon filtering and beacon abort */
#define IWL_BF_ENERGY_DELTA_DEFAULT 5
+#define IWL_BF_ENERGY_DELTA_D0I3 20
#define IWL_BF_ENERGY_DELTA_MAX 255
#define IWL_BF_ENERGY_DELTA_MIN 0
#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1
+#define IWL_BF_ROAMING_ENERGY_DELTA_D0I3 20
#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255
#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0
#define IWL_BF_ROAMING_STATE_DEFAULT 72
+#define IWL_BF_ROAMING_STATE_D0I3 72
#define IWL_BF_ROAMING_STATE_MAX 255
#define IWL_BF_ROAMING_STATE_MIN 0
#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112
+#define IWL_BF_TEMP_THRESHOLD_D0I3 112
#define IWL_BF_TEMP_THRESHOLD_MAX 255
#define IWL_BF_TEMP_THRESHOLD_MIN 0
#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1
+#define IWL_BF_TEMP_FAST_FILTER_D0I3 1
#define IWL_BF_TEMP_FAST_FILTER_MAX 255
#define IWL_BF_TEMP_FAST_FILTER_MIN 0
#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5
+#define IWL_BF_TEMP_SLOW_FILTER_D0I3 5
#define IWL_BF_TEMP_SLOW_FILTER_MAX 255
#define IWL_BF_TEMP_SLOW_FILTER_MIN 0
#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1
#define IWL_BF_DEBUG_FLAG_DEFAULT 0
+#define IWL_BF_DEBUG_FLAG_D0I3 0
#define IWL_BF_ESCAPE_TIMER_DEFAULT 50
+#define IWL_BF_ESCAPE_TIMER_D0I3 1024
#define IWL_BF_ESCAPE_TIMER_MAX 1024
#define IWL_BF_ESCAPE_TIMER_MIN 0
#define IWL_BA_ESCAPE_TIMER_DEFAULT 6
+#define IWL_BA_ESCAPE_TIMER_D0I3 6
#define IWL_BA_ESCAPE_TIMER_D3 9
#define IWL_BA_ESCAPE_TIMER_MAX 1024
#define IWL_BA_ESCAPE_TIMER_MIN 0
#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1
-#define IWL_BF_CMD_CONFIG_DEFAULTS \
- .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA_DEFAULT), \
- .bf_roaming_energy_delta = \
- cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT), \
- .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE_DEFAULT), \
- .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD_DEFAULT), \
- .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER_DEFAULT), \
- .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER_DEFAULT), \
- .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG_DEFAULT), \
- .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), \
- .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT)
+#define IWL_BF_CMD_CONFIG(mode) \
+ .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA ## mode), \
+ .bf_roaming_energy_delta = \
+ cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA ## mode), \
+ .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE ## mode), \
+ .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD ## mode), \
+ .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER ## mode), \
+ .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER ## mode), \
+ .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG ## mode), \
+ .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER ## mode), \
+ .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER ## mode)
+#define IWL_BF_CMD_CONFIG_DEFAULTS IWL_BF_CMD_CONFIG(_DEFAULT)
+#define IWL_BF_CMD_CONFIG_D0I3 IWL_BF_CMD_CONFIG(_D0I3)
#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
index 85057219cc43..39148b5bb332 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
@@ -257,7 +257,8 @@ enum {
/* Bit 17-18: (0) SS, (1) SS*2 */
#define RATE_MCS_STBC_POS 17
-#define RATE_MCS_STBC_MSK (1 << RATE_MCS_STBC_POS)
+#define RATE_MCS_HT_STBC_MSK (3 << RATE_MCS_STBC_POS)
+#define RATE_MCS_VHT_STBC_MSK (1 << RATE_MCS_STBC_POS)
/* Bit 19: (0) Beamforming is off, (1) Beamforming is on */
#define RATE_MCS_BF_POS 19
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
index 1b60fdff6a56..d63647867262 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
@@ -199,11 +199,14 @@ enum iwl_sta_modify_flag {
* @STA_SLEEP_STATE_AWAKE:
* @STA_SLEEP_STATE_PS_POLL:
* @STA_SLEEP_STATE_UAPSD:
+ * @STA_SLEEP_STATE_MOREDATA: set more-data bit on
+ * (last) released frame
*/
enum iwl_sta_sleep_flag {
- STA_SLEEP_STATE_AWAKE = 0,
- STA_SLEEP_STATE_PS_POLL = BIT(0),
- STA_SLEEP_STATE_UAPSD = BIT(1),
+ STA_SLEEP_STATE_AWAKE = 0,
+ STA_SLEEP_STATE_PS_POLL = BIT(0),
+ STA_SLEEP_STATE_UAPSD = BIT(1),
+ STA_SLEEP_STATE_MOREDATA = BIT(2),
};
/* STA ID and color bits definitions */
@@ -318,13 +321,15 @@ struct iwl_mvm_add_sta_cmd_v5 {
} __packed; /* ADD_STA_CMD_API_S_VER_5 */
/**
- * struct iwl_mvm_add_sta_cmd_v6 - Add / modify a station
- * VER_6 of this command is quite similar to VER_5 except
+ * struct iwl_mvm_add_sta_cmd_v7 - Add / modify a station
+ * VER_7 of this command is quite similar to VER_5 except
* exclusion of all fields related to the security key installation.
+ * It only differs from VER_6 by the "awake_acs" field that is
+ * reserved and ignored in VER_6.
*/
-struct iwl_mvm_add_sta_cmd_v6 {
+struct iwl_mvm_add_sta_cmd_v7 {
u8 add_modify;
- u8 reserved1;
+ u8 awake_acs;
__le16 tid_disable_tx;
__le32 mac_id_n_color;
u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */
@@ -342,7 +347,7 @@ struct iwl_mvm_add_sta_cmd_v6 {
__le16 assoc_id;
__le16 beamform_flags;
__le32 tfd_queue_msk;
-} __packed; /* ADD_STA_CMD_API_S_VER_6 */
+} __packed; /* ADD_STA_CMD_API_S_VER_7 */
/**
* struct iwl_mvm_add_sta_key_cmd - add/modify sta key
@@ -432,5 +437,15 @@ struct iwl_mvm_wep_key_cmd {
struct iwl_mvm_wep_key wep_key[0];
} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */
+/**
+ * struct iwl_mvm_eosp_notification - EOSP notification from firmware
+ * @remain_frame_count: # of frames remaining, non-zero if SP was cut
+ * short by GO absence
+ * @sta_id: station ID
+ */
+struct iwl_mvm_eosp_notification {
+ __le32 remain_frame_count;
+ __le32 sta_id;
+} __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */
#endif /* __fw_api_sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
index b674c2a2b51c..8e122f3a7a74 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
@@ -76,6 +76,8 @@
* @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence
* @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence
* @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC)
+ * @TX_CMD_FLG_BT_PRIO_POS: the position of the BT priority (bit 11 is ignored
+ * on old firmwares).
* @TX_CMD_FLG_BT_DIS: disable BT priority for this frame
* @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control.
* Should be set for mgmt, non-QOS data, mcast, bcast and in scan command
@@ -107,6 +109,7 @@ enum iwl_tx_flags {
TX_CMD_FLG_VHT_NDPA = BIT(8),
TX_CMD_FLG_HT_NDPA = BIT(9),
TX_CMD_FLG_CSI_FDBK2HOST = BIT(10),
+ TX_CMD_FLG_BT_PRIO_POS = 11,
TX_CMD_FLG_BT_DIS = BIT(12),
TX_CMD_FLG_SEQ_CTL = BIT(13),
TX_CMD_FLG_MORE_FRAG = BIT(14),
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index 989d7dbdca6c..6e75b52588de 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -70,7 +70,7 @@
#include "fw-api-mac.h"
#include "fw-api-power.h"
#include "fw-api-d3.h"
-#include "fw-api-bt-coex.h"
+#include "fw-api-coex.h"
/* maximal number of Tx queues in any platform */
#define IWL_MVM_MAX_QUEUES 20
@@ -95,6 +95,7 @@ enum {
/* PHY context commands */
PHY_CONTEXT_CMD = 0x8,
DBG_CFG = 0x9,
+ ANTENNA_COUPLING_NOTIFICATION = 0xa,
/* station table */
ADD_STA_KEY = 0x17,
@@ -163,6 +164,7 @@ enum {
TX_ANT_CONFIGURATION_CMD = 0x98,
BT_CONFIG = 0x9b,
STATISTICS_NOTIFICATION = 0x9d,
+ EOSP_NOTIFICATION = 0x9e,
REDUCE_TX_POWER_CMD = 0x9f,
/* RF-KILL commands and notifications */
@@ -190,6 +192,7 @@ enum {
REPLY_DEBUG_CMD = 0xf0,
DEBUG_LOG_MSG = 0xf7,
+ BCAST_FILTER_CMD = 0xcf,
MCAST_FILTER_CMD = 0xd0,
/* D3 commands/notifications */
@@ -197,6 +200,7 @@ enum {
PROT_OFFLOAD_CONFIG_CMD = 0xd4,
OFFLOADS_QUERY_CMD = 0xd5,
REMOTE_WAKE_CONFIG_CMD = 0xd6,
+ D0I3_END_CMD = 0xed,
/* for WoWLAN in particular */
WOWLAN_PATTERNS = 0xe0,
@@ -313,14 +317,12 @@ enum {
/* Section types for NVM_ACCESS_CMD */
enum {
- NVM_SECTION_TYPE_HW = 0,
- NVM_SECTION_TYPE_SW,
- NVM_SECTION_TYPE_PAPD,
- NVM_SECTION_TYPE_BT,
- NVM_SECTION_TYPE_CALIBRATION,
- NVM_SECTION_TYPE_PRODUCTION,
- NVM_SECTION_TYPE_POST_FCS_CALIB,
- NVM_NUM_OF_SECTIONS,
+ NVM_SECTION_TYPE_SW = 1,
+ NVM_SECTION_TYPE_REGULATORY = 3,
+ NVM_SECTION_TYPE_CALIBRATION = 4,
+ NVM_SECTION_TYPE_PRODUCTION = 5,
+ NVM_SECTION_TYPE_MAC_OVERRIDE = 11,
+ NVM_MAX_NUM_SECTIONS = 12,
};
/**
@@ -412,6 +414,35 @@ struct mvm_alive_resp {
__le32 scd_base_ptr; /* SRAM address for SCD */
} __packed; /* ALIVE_RES_API_S_VER_1 */
+struct mvm_alive_resp_ver2 {
+ __le16 status;
+ __le16 flags;
+ u8 ucode_minor;
+ u8 ucode_major;
+ __le16 id;
+ u8 api_minor;
+ u8 api_major;
+ u8 ver_subtype;
+ u8 ver_type;
+ u8 mac;
+ u8 opt;
+ __le16 reserved2;
+ __le32 timestamp;
+ __le32 error_event_table_ptr; /* SRAM address for error log */
+ __le32 log_event_table_ptr; /* SRAM address for LMAC event log */
+ __le32 cpu_register_ptr;
+ __le32 dbgm_config_ptr;
+ __le32 alive_counter_ptr;
+ __le32 scd_base_ptr; /* SRAM address for SCD */
+ __le32 st_fwrd_addr; /* pointer to Store and forward */
+ __le32 st_fwrd_size;
+ u8 umac_minor; /* UMAC version: minor */
+ u8 umac_major; /* UMAC version: major */
+ __le16 umac_id; /* UMAC version: id */
+ __le32 error_info_addr; /* SRAM address for UMAC error log */
+ __le32 dbg_print_buff_addr;
+} __packed; /* ALIVE_RES_API_S_VER_2 */
+
/* Error response/notification */
enum {
FW_ERR_UNKNOWN_CMD = 0x0,
@@ -682,6 +713,7 @@ enum {
TE_V2_NOTIF_HOST_FRAG_END = BIT(5),
TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6),
TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7),
+ T2_V2_START_IMMEDIATELY = BIT(11),
TE_V2_NOTIF_MSK = 0xff,
@@ -1159,6 +1191,90 @@ struct iwl_mcast_filter_cmd {
u8 addr_list[0];
} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */
+#define MAX_BCAST_FILTERS 8
+#define MAX_BCAST_FILTER_ATTRS 2
+
+/**
+ * enum iwl_mvm_bcast_filter_attr_offset - written by fw for each Rx packet
+ * @BCAST_FILTER_OFFSET_PAYLOAD_START: offset is from payload start.
+ * @BCAST_FILTER_OFFSET_IP_END: offset is from ip header end (i.e.
+ * start of ip payload).
+ */
+enum iwl_mvm_bcast_filter_attr_offset {
+ BCAST_FILTER_OFFSET_PAYLOAD_START = 0,
+ BCAST_FILTER_OFFSET_IP_END = 1,
+};
+
+/**
+ * struct iwl_fw_bcast_filter_attr - broadcast filter attribute
+ * @offset_type: &enum iwl_mvm_bcast_filter_attr_offset.
+ * @offset: starting offset of this pattern.
+ * @val: value to match - big endian (MSB is the first
+ * byte to match from offset pos).
+ * @mask: mask to match (big endian).
+ */
+struct iwl_fw_bcast_filter_attr {
+ u8 offset_type;
+ u8 offset;
+ __le16 reserved1;
+ __be32 val;
+ __be32 mask;
+} __packed; /* BCAST_FILTER_ATT_S_VER_1 */
+
+/**
+ * enum iwl_mvm_bcast_filter_frame_type - filter frame type
+ * @BCAST_FILTER_FRAME_TYPE_ALL: consider all frames.
+ * @BCAST_FILTER_FRAME_TYPE_IPV4: consider only ipv4 frames
+ */
+enum iwl_mvm_bcast_filter_frame_type {
+ BCAST_FILTER_FRAME_TYPE_ALL = 0,
+ BCAST_FILTER_FRAME_TYPE_IPV4 = 1,
+};
+
+/**
+ * struct iwl_fw_bcast_filter - broadcast filter
+ * @discard: discard frame (1) or let it pass (0).
+ * @frame_type: &enum iwl_mvm_bcast_filter_frame_type.
+ * @num_attrs: number of valid attributes in this filter.
+ * @attrs: attributes of this filter. a filter is considered matched
+ * only when all its attributes are matched (i.e. AND relationship)
+ */
+struct iwl_fw_bcast_filter {
+ u8 discard;
+ u8 frame_type;
+ u8 num_attrs;
+ u8 reserved1;
+ struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS];
+} __packed; /* BCAST_FILTER_S_VER_1 */
+
+/**
+ * struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration.
+ * @default_discard: default action for this mac (discard (1) / pass (0)).
+ * @attached_filters: bitmap of relevant filters for this mac.
+ */
+struct iwl_fw_bcast_mac {
+ u8 default_discard;
+ u8 reserved1;
+ __le16 attached_filters;
+} __packed; /* BCAST_MAC_CONTEXT_S_VER_1 */
+
+/**
+ * struct iwl_bcast_filter_cmd - broadcast filtering configuration
+ * @disable: enable (0) / disable (1)
+ * @max_bcast_filters: max number of filters (MAX_BCAST_FILTERS)
+ * @max_macs: max number of macs (NUM_MAC_INDEX_DRIVER)
+ * @filters: broadcast filters
+ * @macs: broadcast filtering configuration per-mac
+ */
+struct iwl_bcast_filter_cmd {
+ u8 disable;
+ u8 max_bcast_filters;
+ u8 max_macs;
+ u8 reserved1;
+ struct iwl_fw_bcast_filter filters[MAX_BCAST_FILTERS];
+ struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER];
+} __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */
+
struct mvm_statistics_dbg {
__le32 burst_check;
__le32 burst_count;
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h b/drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h
new file mode 100644
index 000000000000..58c8941c0d95
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h
@@ -0,0 +1,106 @@
+/******************************************************************************
+ *
+ * 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. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_error_dump_h__
+#define __fw_error_dump_h__
+
+#include <linux/types.h>
+
+#define IWL_FW_ERROR_DUMP_BARKER 0x14789632
+
+/**
+ * enum iwl_fw_error_dump_type - types of data in the dump file
+ * @IWL_FW_ERROR_DUMP_SRAM:
+ * @IWL_FW_ERROR_DUMP_REG:
+ */
+enum iwl_fw_error_dump_type {
+ IWL_FW_ERROR_DUMP_SRAM = 0,
+ IWL_FW_ERROR_DUMP_REG = 1,
+
+ IWL_FW_ERROR_DUMP_MAX,
+};
+
+/**
+ * struct iwl_fw_error_dump_data - data for one type
+ * @type: %enum iwl_fw_error_dump_type
+ * @len: the length starting from %data - must be a multiplier of 4.
+ * @data: the data itself padded to be a multiplier of 4.
+ */
+struct iwl_fw_error_dump_data {
+ __le32 type;
+ __le32 len;
+ __u8 data[];
+} __packed __aligned(4);
+
+/**
+ * struct iwl_fw_error_dump_file - the layout of the header of the file
+ * @barker: must be %IWL_FW_ERROR_DUMP_BARKER
+ * @file_len: the length of all the file starting from %barker
+ * @data: array of %struct iwl_fw_error_dump_data
+ */
+struct iwl_fw_error_dump_file {
+ __le32 barker;
+ __le32 file_len;
+ u8 data[0];
+} __packed __aligned(4);
+
+#endif /* __fw_error_dump_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index c03d39541f9e..7ce20062f32d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -110,18 +110,48 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
container_of(notif_wait, struct iwl_mvm, notif_wait);
struct iwl_mvm_alive_data *alive_data = data;
struct mvm_alive_resp *palive;
-
- palive = (void *)pkt->data;
-
- mvm->error_event_table = le32_to_cpu(palive->error_event_table_ptr);
- mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr);
- alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
-
- alive_data->valid = le16_to_cpu(palive->status) == IWL_ALIVE_STATUS_OK;
- IWL_DEBUG_FW(mvm,
- "Alive ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
- le16_to_cpu(palive->status), palive->ver_type,
- palive->ver_subtype, palive->flags);
+ struct mvm_alive_resp_ver2 *palive2;
+
+ if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) {
+ palive = (void *)pkt->data;
+
+ mvm->support_umac_log = false;
+ mvm->error_event_table =
+ le32_to_cpu(palive->error_event_table_ptr);
+ mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr);
+ alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
+
+ alive_data->valid = le16_to_cpu(palive->status) ==
+ IWL_ALIVE_STATUS_OK;
+ IWL_DEBUG_FW(mvm,
+ "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
+ le16_to_cpu(palive->status), palive->ver_type,
+ palive->ver_subtype, palive->flags);
+ } else {
+ palive2 = (void *)pkt->data;
+
+ mvm->error_event_table =
+ le32_to_cpu(palive2->error_event_table_ptr);
+ mvm->log_event_table =
+ le32_to_cpu(palive2->log_event_table_ptr);
+ alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr);
+ mvm->umac_error_event_table =
+ le32_to_cpu(palive2->error_info_addr);
+
+ alive_data->valid = le16_to_cpu(palive2->status) ==
+ IWL_ALIVE_STATUS_OK;
+ if (mvm->umac_error_event_table)
+ mvm->support_umac_log = true;
+
+ IWL_DEBUG_FW(mvm,
+ "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
+ le16_to_cpu(palive2->status), palive2->ver_type,
+ palive2->ver_subtype, palive2->flags);
+
+ IWL_DEBUG_FW(mvm,
+ "UMAC version: Major - 0x%x, Minor - 0x%x\n",
+ palive2->umac_major, palive2->umac_minor);
+ }
return true;
}
@@ -292,7 +322,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
}
/* Send TX valid antennas before triggering calibrations */
- ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
+ ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
if (ret)
goto error;
@@ -328,8 +358,6 @@ out:
GFP_KERNEL);
if (!mvm->nvm_data)
return -ENOMEM;
- mvm->nvm_data->valid_rx_ant = 1;
- mvm->nvm_data->valid_tx_ant = 1;
mvm->nvm_data->bands[0].channels = mvm->nvm_data->channels;
mvm->nvm_data->bands[0].n_channels = 1;
mvm->nvm_data->bands[0].n_bitrates = 1;
@@ -341,8 +369,6 @@ out:
return ret;
}
-#define UCODE_CALIB_TIMEOUT (2*HZ)
-
int iwl_mvm_up(struct iwl_mvm *mvm)
{
int ret, i;
@@ -394,7 +420,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
if (ret)
IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
- ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
+ ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
if (ret)
goto error;
@@ -439,10 +465,23 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
goto error;
}
- ret = iwl_mvm_power_update_device_mode(mvm);
+ /* Initialize tx backoffs to the minimal possible */
+ iwl_mvm_tt_tx_backoff(mvm, 0);
+
+ if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
+ ret = iwl_power_legacy_set_cam_mode(mvm);
+ if (ret)
+ goto error;
+ }
+
+ ret = iwl_mvm_power_update_device(mvm);
if (ret)
goto error;
+ /* allow FW/transport low power modes if not during restart */
+ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
return 0;
error:
@@ -466,7 +505,7 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm)
goto error;
}
- ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
+ ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
if (ret)
goto error;
diff --git a/drivers/net/wireless/iwlwifi/mvm/led.c b/drivers/net/wireless/iwlwifi/mvm/led.c
index 6b4ea6bf8ffe..e3b3cf4dbd77 100644
--- a/drivers/net/wireless/iwlwifi/mvm/led.c
+++ b/drivers/net/wireless/iwlwifi/mvm/led.c
@@ -94,6 +94,8 @@ int iwl_mvm_leds_init(struct iwl_mvm *mvm)
int ret;
switch (mode) {
+ case IWL_LED_BLINK:
+ IWL_ERR(mvm, "Blink led mode not supported, used default\n");
case IWL_LED_DEFAULT:
case IWL_LED_RF_STATE:
mode = IWL_LED_RF_STATE;
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
index ba723d50939a..9ccec10bba16 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
@@ -90,6 +90,7 @@ static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
{
struct iwl_mvm_mac_iface_iterator_data *data = _data;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ u16 min_bi;
/* Skip the interface for which we are trying to assign a tsf_id */
if (vif == data->vif)
@@ -114,42 +115,57 @@ static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
switch (data->vif->type) {
case NL80211_IFTYPE_STATION:
/*
- * The new interface is client, so if the existing one
- * we're iterating is an AP, and both interfaces have the
- * same beacon interval, the same TSF should be used to
- * avoid drift between the new client and existing AP,
- * the existing AP will get drift updates from the new
- * client context in this case
+ * The new interface is a client, so if the one we're iterating
+ * is an AP, and the beacon interval of the AP is a multiple or
+ * divisor of the beacon interval of the client, the same TSF
+ * should be used to avoid drift between the new client and
+ * existing AP. The existing AP will get drift updates from the
+ * new client context in this case.
*/
- if (vif->type == NL80211_IFTYPE_AP) {
- if (data->preferred_tsf == NUM_TSF_IDS &&
- test_bit(mvmvif->tsf_id, data->available_tsf_ids) &&
- (vif->bss_conf.beacon_int ==
- data->vif->bss_conf.beacon_int)) {
- data->preferred_tsf = mvmvif->tsf_id;
- return;
- }
+ if (vif->type != NL80211_IFTYPE_AP ||
+ data->preferred_tsf != NUM_TSF_IDS ||
+ !test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+ break;
+
+ min_bi = min(data->vif->bss_conf.beacon_int,
+ vif->bss_conf.beacon_int);
+
+ if (!min_bi)
+ break;
+
+ if ((data->vif->bss_conf.beacon_int -
+ vif->bss_conf.beacon_int) % min_bi == 0) {
+ data->preferred_tsf = mvmvif->tsf_id;
+ return;
}
break;
+
case NL80211_IFTYPE_AP:
/*
- * The new interface is AP/GO, so in case both interfaces
- * have the same beacon interval, it should get drift
- * updates from an existing client or use the same
- * TSF as an existing GO. There's no drift between
- * TSFs internally but if they used different TSFs
- * then a new client MAC could update one of them
- * and cause drift that way.
+ * The new interface is AP/GO, so if its beacon interval is a
+ * multiple or a divisor of the beacon interval of an existing
+ * interface, it should get drift updates from an existing
+ * client or use the same TSF as an existing GO. There's no
+ * drift between TSFs internally but if they used different
+ * TSFs then a new client MAC could update one of them and
+ * cause drift that way.
*/
- if (vif->type == NL80211_IFTYPE_STATION ||
- vif->type == NL80211_IFTYPE_AP) {
- if (data->preferred_tsf == NUM_TSF_IDS &&
- test_bit(mvmvif->tsf_id, data->available_tsf_ids) &&
- (vif->bss_conf.beacon_int ==
- data->vif->bss_conf.beacon_int)) {
- data->preferred_tsf = mvmvif->tsf_id;
- return;
- }
+ if ((vif->type != NL80211_IFTYPE_AP &&
+ vif->type != NL80211_IFTYPE_STATION) ||
+ data->preferred_tsf != NUM_TSF_IDS ||
+ !test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+ break;
+
+ min_bi = min(data->vif->bss_conf.beacon_int,
+ vif->bss_conf.beacon_int);
+
+ if (!min_bi)
+ break;
+
+ if ((data->vif->bss_conf.beacon_int -
+ vif->bss_conf.beacon_int) % min_bi == 0) {
+ data->preferred_tsf = mvmvif->tsf_id;
+ return;
}
break;
default:
@@ -936,7 +952,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
TX_CMD_FLG_TSF);
mvm->mgmt_last_antenna_idx =
- iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw),
+ iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant,
mvm->mgmt_last_antenna_idx);
beacon_cmd.tx.rate_n_flags =
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index c35b8661b395..4dd9ff43b8b6 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -66,7 +66,9 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ip.h>
+#include <linux/if_arp.h>
#include <net/mac80211.h>
+#include <net/ieee80211_radiotap.h>
#include <net/tcp.h>
#include "iwl-op-mode.h"
@@ -128,6 +130,117 @@ static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
};
#endif
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+/*
+ * Use the reserved field to indicate magic values.
+ * these values will only be used internally by the driver,
+ * and won't make it to the fw (reserved will be 0).
+ * BC_FILTER_MAGIC_IP - configure the val of this attribute to
+ * be the vif's ip address. in case there is not a single
+ * ip address (0, or more than 1), this attribute will
+ * be skipped.
+ * BC_FILTER_MAGIC_MAC - set the val of this attribute to
+ * the LSB bytes of the vif's mac address
+ */
+enum {
+ BC_FILTER_MAGIC_NONE = 0,
+ BC_FILTER_MAGIC_IP,
+ BC_FILTER_MAGIC_MAC,
+};
+
+static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = {
+ {
+ /* arp */
+ .discard = 0,
+ .frame_type = BCAST_FILTER_FRAME_TYPE_ALL,
+ .attrs = {
+ {
+ /* frame type - arp, hw type - ethernet */
+ .offset_type =
+ BCAST_FILTER_OFFSET_PAYLOAD_START,
+ .offset = sizeof(rfc1042_header),
+ .val = cpu_to_be32(0x08060001),
+ .mask = cpu_to_be32(0xffffffff),
+ },
+ {
+ /* arp dest ip */
+ .offset_type =
+ BCAST_FILTER_OFFSET_PAYLOAD_START,
+ .offset = sizeof(rfc1042_header) + 2 +
+ sizeof(struct arphdr) +
+ ETH_ALEN + sizeof(__be32) +
+ ETH_ALEN,
+ .mask = cpu_to_be32(0xffffffff),
+ /* mark it as special field */
+ .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP),
+ },
+ },
+ },
+ {
+ /* dhcp offer bcast */
+ .discard = 0,
+ .frame_type = BCAST_FILTER_FRAME_TYPE_IPV4,
+ .attrs = {
+ {
+ /* udp dest port - 68 (bootp client)*/
+ .offset_type = BCAST_FILTER_OFFSET_IP_END,
+ .offset = offsetof(struct udphdr, dest),
+ .val = cpu_to_be32(0x00440000),
+ .mask = cpu_to_be32(0xffff0000),
+ },
+ {
+ /* dhcp - lsb bytes of client hw address */
+ .offset_type = BCAST_FILTER_OFFSET_IP_END,
+ .offset = 38,
+ .mask = cpu_to_be32(0xffffffff),
+ /* mark it as special field */
+ .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC),
+ },
+ },
+ },
+ /* last filter must be empty */
+ {},
+};
+#endif
+
+void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+ if (!iwl_mvm_is_d0i3_supported(mvm))
+ return;
+
+ IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type);
+ WARN_ON(test_and_set_bit(ref_type, mvm->ref_bitmap));
+ iwl_trans_ref(mvm->trans);
+}
+
+void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+ if (!iwl_mvm_is_d0i3_supported(mvm))
+ return;
+
+ IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type);
+ WARN_ON(!test_and_clear_bit(ref_type, mvm->ref_bitmap));
+ iwl_trans_unref(mvm->trans);
+}
+
+static void
+iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref)
+{
+ int i;
+
+ if (!iwl_mvm_is_d0i3_supported(mvm))
+ return;
+
+ for_each_set_bit(i, mvm->ref_bitmap, IWL_MVM_REF_COUNT) {
+ if (ref == i)
+ continue;
+
+ IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d\n", i);
+ clear_bit(i, mvm->ref_bitmap);
+ iwl_trans_unref(mvm->trans);
+ }
+}
+
static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
{
int i;
@@ -168,6 +281,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->queues = mvm->first_agg_queue;
hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
+ hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC |
+ IEEE80211_RADIOTAP_MCS_HAVE_STBC;
+ hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC;
hw->rate_control_algorithm = "iwl-mvm-rs";
/*
@@ -179,7 +295,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
!iwlwifi_mod_params.sw_crypto)
hw->flags |= IEEE80211_HW_MFP_CAPABLE;
- if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT) {
+ if (0 && mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT) {
hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
hw->uapsd_queues = IWL_UAPSD_AC_INFO;
hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
@@ -203,6 +319,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
REGULATORY_DISABLE_BEACON_HINTS;
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
+ hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
hw->wiphy->n_iface_combinations =
ARRAY_SIZE(iwl_mvm_iface_combinations);
@@ -246,7 +365,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
else
hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
- if (0 && mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) {
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) {
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
@@ -256,8 +375,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
}
hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
- NL80211_FEATURE_P2P_GO_OPPPS |
- NL80211_FEATURE_LOW_PRIORITY_SCAN;
+ NL80211_FEATURE_P2P_GO_OPPPS;
mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
@@ -289,6 +407,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
}
#endif
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+ /* assign default bcast filtering configuration */
+ mvm->bcast_filters = iwl_mvm_default_bcast_filters;
+#endif
+
ret = iwl_mvm_leds_init(mvm);
if (ret)
return ret;
@@ -300,11 +423,55 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
return ret;
}
+static bool iwl_mvm_defer_tx(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ struct sk_buff *skb)
+{
+ struct iwl_mvm_sta *mvmsta;
+ bool defer = false;
+
+ /*
+ * double check the IN_D0I3 flag both before and after
+ * taking the spinlock, in order to prevent taking
+ * the spinlock when not needed.
+ */
+ if (likely(!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)))
+ return false;
+
+ spin_lock(&mvm->d0i3_tx_lock);
+ /*
+ * testing the flag again ensures the skb dequeue
+ * loop (on d0i3 exit) hasn't run yet.
+ */
+ if (!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status))
+ goto out;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ if (mvmsta->sta_id == IWL_MVM_STATION_COUNT ||
+ mvmsta->sta_id != mvm->d0i3_ap_sta_id)
+ goto out;
+
+ __skb_queue_tail(&mvm->d0i3_tx, skb);
+ ieee80211_stop_queues(mvm->hw);
+
+ /* trigger wakeup */
+ iwl_mvm_ref(mvm, IWL_MVM_REF_TX);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_TX);
+
+ defer = true;
+out:
+ spin_unlock(&mvm->d0i3_tx_lock);
+ return defer;
+}
+
static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_control *control,
struct sk_buff *skb)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct ieee80211_sta *sta = control->sta;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
if (iwl_mvm_is_radio_killed(mvm)) {
IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
@@ -315,8 +482,18 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
!test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
goto drop;
- if (control->sta) {
- if (iwl_mvm_tx_skb(mvm, skb, control->sta))
+ /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */
+ if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER &&
+ ieee80211_is_mgmt(hdr->frame_control) &&
+ !ieee80211_is_deauth(hdr->frame_control) &&
+ !ieee80211_is_disassoc(hdr->frame_control) &&
+ !ieee80211_is_action(hdr->frame_control)))
+ sta = NULL;
+
+ if (sta) {
+ if (iwl_mvm_defer_tx(mvm, sta, skb))
+ return;
+ if (iwl_mvm_tx_skb(mvm, skb, sta))
goto drop;
return;
}
@@ -354,6 +531,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
+ bool tx_agg_ref = false;
IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n",
sta->addr, tid, action);
@@ -361,6 +539,23 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
if (!(mvm->nvm_data->sku_cap_11n_enable))
return -EACCES;
+ /* return from D0i3 before starting a new Tx aggregation */
+ if (action == IEEE80211_AMPDU_TX_START) {
+ iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG);
+ tx_agg_ref = true;
+
+ /*
+ * wait synchronously until D0i3 exit to get the correct
+ * sequence number for the tid
+ */
+ if (!wait_event_timeout(mvm->d0i3_exit_waitq,
+ !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) {
+ WARN_ON_ONCE(1);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
+ return -EIO;
+ }
+ }
+
mutex_lock(&mvm->mutex);
switch (action) {
@@ -398,6 +593,13 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
}
mutex_unlock(&mvm->mutex);
+ /*
+ * If the tid is marked as started, we won't use it for offloaded
+ * traffic on the next D0i3 entry. It's safe to unref.
+ */
+ if (tx_agg_ref)
+ iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
+
return ret;
}
@@ -422,6 +624,15 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
{
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ static char *env[] = { "DRIVER=iwlwifi", "EVENT=error_dump", NULL };
+
+ iwl_mvm_fw_error_dump(mvm);
+
+ /* notify the userspace about the error we had */
+ kobject_uevent_env(&mvm->hw->wiphy->dev.kobj, KOBJ_CHANGE, env);
+#endif
+
iwl_trans_stop_device(mvm->trans);
mvm->scan_status = IWL_MVM_SCAN_NONE;
@@ -434,6 +645,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
iwl_mvm_cleanup_iterator, mvm);
mvm->p2p_device_vif = NULL;
+ 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));
@@ -441,6 +653,10 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
ieee80211_wake_queues(mvm->hw);
+ /* cleanup all stale references (scan, roc), but keep the
+ * ucode_down ref until reconfig is complete */
+ iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
+
mvm->vif_count = 0;
mvm->rx_ba_sessions = 0;
}
@@ -470,11 +686,15 @@ static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
mutex_lock(&mvm->mutex);
clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+ iwl_mvm_d0i3_enable_tx(mvm, NULL);
ret = iwl_mvm_update_quotas(mvm, NULL);
if (ret)
IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
ret);
+ /* allow transport/FW low power modes */
+ iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
mutex_unlock(&mvm->mutex);
}
@@ -482,9 +702,14 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ flush_work(&mvm->d0i3_exit_work);
flush_work(&mvm->async_handlers_wk);
mutex_lock(&mvm->mutex);
+
+ /* disallow low power states when the FW is down */
+ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
/* async_handlers_wk is now blocked */
/*
@@ -510,14 +735,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
cancel_work_sync(&mvm->async_handlers_wk);
}
-static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_mvm *mvm = data;
-
- iwl_mvm_power_update_mode(mvm, vif);
-}
-
static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
{
u16 i;
@@ -585,7 +802,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
vif->type == NL80211_IFTYPE_ADHOC) {
u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta,
- qmask);
+ qmask,
+ ieee80211_vif_type_p2p(vif));
if (ret) {
IWL_ERR(mvm, "Failed to allocate bcast sta\n");
goto out_release;
@@ -599,10 +817,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
if (ret)
goto out_release;
- iwl_mvm_power_disable(mvm, vif);
+ ret = iwl_mvm_power_update_mac(mvm, vif);
+ if (ret)
+ goto out_release;
/* beacon filtering */
- ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
if (ret)
goto out_remove_mac;
@@ -661,11 +881,6 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
mvm->vif_count--;
- /* TODO: remove this when legacy PM will be discarded */
- ieee80211_iterate_active_interfaces(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_power_update_iterator, mvm);
-
iwl_mvm_mac_ctxt_release(mvm, vif);
out_unlock:
mutex_unlock(&mvm->mutex);
@@ -754,11 +969,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
mvm->vif_count--;
- /* TODO: remove this when legacy PM will be discarded */
- ieee80211_iterate_active_interfaces(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_power_update_iterator, mvm);
-
+ iwl_mvm_power_update_mac(mvm, vif);
iwl_mvm_mac_ctxt_remove(mvm, vif);
out_release:
@@ -876,6 +1087,156 @@ out:
*total_flags = 0;
}
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+struct iwl_bcast_iter_data {
+ struct iwl_mvm *mvm;
+ struct iwl_bcast_filter_cmd *cmd;
+ u8 current_filter;
+};
+
+static void
+iwl_mvm_set_bcast_filter(struct ieee80211_vif *vif,
+ const struct iwl_fw_bcast_filter *in_filter,
+ struct iwl_fw_bcast_filter *out_filter)
+{
+ struct iwl_fw_bcast_filter_attr *attr;
+ int i;
+
+ memcpy(out_filter, in_filter, sizeof(*out_filter));
+
+ for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) {
+ attr = &out_filter->attrs[i];
+
+ if (!attr->mask)
+ break;
+
+ switch (attr->reserved1) {
+ case cpu_to_le16(BC_FILTER_MAGIC_IP):
+ if (vif->bss_conf.arp_addr_cnt != 1) {
+ attr->mask = 0;
+ continue;
+ }
+
+ attr->val = vif->bss_conf.arp_addr_list[0];
+ break;
+ case cpu_to_le16(BC_FILTER_MAGIC_MAC):
+ attr->val = *(__be32 *)&vif->addr[2];
+ break;
+ default:
+ break;
+ }
+ attr->reserved1 = 0;
+ out_filter->num_attrs++;
+ }
+}
+
+static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_bcast_iter_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+ struct iwl_bcast_filter_cmd *cmd = data->cmd;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_fw_bcast_mac *bcast_mac;
+ int i;
+
+ if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs)))
+ return;
+
+ bcast_mac = &cmd->macs[mvmvif->id];
+
+ /* enable filtering only for associated stations */
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc)
+ return;
+
+ bcast_mac->default_discard = 1;
+
+ /* copy all configured filters */
+ for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) {
+ /*
+ * Make sure we don't exceed our filters limit.
+ * if there is still a valid filter to be configured,
+ * be on the safe side and just allow bcast for this mac.
+ */
+ if (WARN_ON_ONCE(data->current_filter >=
+ ARRAY_SIZE(cmd->filters))) {
+ bcast_mac->default_discard = 0;
+ bcast_mac->attached_filters = 0;
+ break;
+ }
+
+ iwl_mvm_set_bcast_filter(vif,
+ &mvm->bcast_filters[i],
+ &cmd->filters[data->current_filter]);
+
+ /* skip current filter if it contains no attributes */
+ if (!cmd->filters[data->current_filter].num_attrs)
+ continue;
+
+ /* attach the filter to current mac */
+ bcast_mac->attached_filters |=
+ cpu_to_le16(BIT(data->current_filter));
+
+ data->current_filter++;
+ }
+}
+
+bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
+ struct iwl_bcast_filter_cmd *cmd)
+{
+ struct iwl_bcast_iter_data iter_data = {
+ .mvm = mvm,
+ .cmd = cmd,
+ };
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters);
+ cmd->max_macs = ARRAY_SIZE(cmd->macs);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ /* use debugfs filters/macs if override is configured */
+ if (mvm->dbgfs_bcast_filtering.override) {
+ memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters,
+ sizeof(cmd->filters));
+ memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs,
+ sizeof(cmd->macs));
+ return true;
+ }
+#endif
+
+ /* if no filters are configured, do nothing */
+ if (!mvm->bcast_filters)
+ return false;
+
+ /* configure and attach these filters for each associated sta vif */
+ ieee80211_iterate_active_interfaces(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bcast_filter_iterator, &iter_data);
+
+ return true;
+}
+static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_bcast_filter_cmd cmd;
+
+ if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
+ return 0;
+
+ if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
+ return 0;
+
+ return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+ sizeof(cmd), &cmd);
+}
+#else
+static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ return 0;
+}
+#endif
+
static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
@@ -928,6 +1289,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
iwl_mvm_sf_update(mvm, vif, false);
iwl_mvm_power_vif_assoc(mvm, vif);
+ if (vif->p2p)
+ iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT);
} else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
/*
* If update fails - SF might be running in associated
@@ -940,27 +1303,25 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
if (ret)
IWL_ERR(mvm, "failed to remove AP station\n");
+
+ if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
+ mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
/* remove quota for this interface */
ret = iwl_mvm_update_quotas(mvm, NULL);
if (ret)
IWL_ERR(mvm, "failed to update quotas\n");
+
+ if (vif->p2p)
+ iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT);
}
iwl_mvm_recalc_multicast(mvm);
+ iwl_mvm_configure_bcast_filter(mvm, vif);
/* reset rssi values */
mvmvif->bf_data.ave_beacon_signal = 0;
- if (!(mvm->fw->ucode_capa.flags &
- IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
- /* Workaround for FW bug, otherwise FW disables device
- * power save upon disassociation
- */
- ret = iwl_mvm_power_update_mode(mvm, vif);
- if (ret)
- IWL_ERR(mvm, "failed to update power mode\n");
- }
iwl_mvm_bt_coex_vif_change(mvm);
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT,
IEEE80211_SMPS_AUTOMATIC);
@@ -971,9 +1332,10 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
*/
iwl_mvm_remove_time_event(mvm, mvmvif,
&mvmvif->time_event_data);
+ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC));
} else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
BSS_CHANGED_QOS)) {
- ret = iwl_mvm_power_update_mode(mvm, vif);
+ ret = iwl_mvm_power_update_mac(mvm, vif);
if (ret)
IWL_ERR(mvm, "failed to update power mode\n");
}
@@ -987,10 +1349,15 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
IWL_DEBUG_MAC80211(mvm, "cqm info_changed");
/* reset cqm events tracking */
mvmvif->bf_data.last_cqm_event = 0;
- ret = iwl_mvm_update_beacon_filter(mvm, vif);
+ ret = iwl_mvm_update_beacon_filter(mvm, vif, false, CMD_SYNC);
if (ret)
IWL_ERR(mvm, "failed to update CQM thresholds\n");
}
+
+ if (changes & BSS_CHANGED_ARP_FILTER) {
+ IWL_DEBUG_MAC80211(mvm, "arp filter changed");
+ iwl_mvm_configure_bcast_filter(mvm, vif);
+ }
}
static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
@@ -1024,8 +1391,6 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
if (ret)
goto out_remove;
- mvmvif->ap_ibss_active = true;
-
/* Send the bcast station. At this stage the TBTT and DTIM time events
* are added and applied to the scheduler */
ret = iwl_mvm_send_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
@@ -1036,8 +1401,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
mvmvif->ap_ibss_active = true;
/* power updated needs to be done before quotas */
- mvm->bound_vif_cnt++;
- iwl_mvm_power_update_binding(mvm, vif, true);
+ iwl_mvm_power_update_mac(mvm, vif);
ret = iwl_mvm_update_quotas(mvm, vif);
if (ret)
@@ -1047,14 +1411,15 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
if (vif->p2p && mvm->p2p_device_vif)
iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
+ iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS);
+
iwl_mvm_bt_coex_vif_change(mvm);
mutex_unlock(&mvm->mutex);
return 0;
out_quota_failed:
- mvm->bound_vif_cnt--;
- iwl_mvm_power_update_binding(mvm, vif, false);
+ iwl_mvm_power_update_mac(mvm, vif);
mvmvif->ap_ibss_active = false;
iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
out_unbind:
@@ -1080,6 +1445,8 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
iwl_mvm_bt_coex_vif_change(mvm);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS);
+
/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
if (vif->p2p && mvm->p2p_device_vif)
iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
@@ -1088,8 +1455,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
iwl_mvm_binding_remove_vif(mvm, vif);
- mvm->bound_vif_cnt--;
- iwl_mvm_power_update_binding(mvm, vif, false);
+ iwl_mvm_power_update_mac(mvm, vif);
iwl_mvm_mac_ctxt_remove(mvm, vif);
@@ -1103,26 +1469,20 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
u32 changes)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- enum ieee80211_bss_change ht_change = BSS_CHANGED_ERP_CTS_PROT |
- BSS_CHANGED_HT |
- BSS_CHANGED_BANDWIDTH;
- int ret;
/* Changes will be applied when the AP/IBSS is started */
if (!mvmvif->ap_ibss_active)
return;
- if (changes & ht_change) {
- ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
- if (ret)
- IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
- }
+ if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT |
+ BSS_CHANGED_BANDWIDTH) &&
+ iwl_mvm_mac_ctxt_changed(mvm, vif))
+ IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
/* Need to send a new beacon template to the FW */
- if (changes & BSS_CHANGED_BEACON) {
- if (iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
- IWL_WARN(mvm, "Failed updating beacon data\n");
- }
+ if (changes & BSS_CHANGED_BEACON &&
+ iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
+ IWL_WARN(mvm, "Failed updating beacon data\n");
}
static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
@@ -1162,13 +1522,30 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
- if (mvm->scan_status == IWL_MVM_SCAN_NONE)
- ret = iwl_mvm_scan_request(mvm, vif, req);
- else
+ switch (mvm->scan_status) {
+ case IWL_MVM_SCAN_SCHED:
+ ret = iwl_mvm_sched_scan_stop(mvm);
+ if (ret) {
+ ret = -EBUSY;
+ goto out;
+ }
+ break;
+ case IWL_MVM_SCAN_NONE:
+ break;
+ default:
ret = -EBUSY;
+ goto out;
+ }
- mutex_unlock(&mvm->mutex);
+ iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
+ ret = iwl_mvm_scan_request(mvm, vif, req);
+ if (ret)
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+out:
+ mutex_unlock(&mvm->mutex);
+ /* make sure to flush the Rx handler before the next scan arrives */
+ iwl_mvm_wait_for_async_handlers(mvm);
return ret;
}
@@ -1186,20 +1563,32 @@ static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,
static void
iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,
- struct ieee80211_sta *sta, u16 tid,
+ struct ieee80211_sta *sta, u16 tids,
int num_frames,
enum ieee80211_frame_release_type reason,
bool more_data)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- /* TODO: how do we tell the fw to send frames for a specific TID */
+ /* Called when we need to transmit (a) frame(s) from mac80211 */
- /*
- * The fw will send EOSP notification when the last frame will be
- * transmitted.
- */
- iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames);
+ iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
+ tids, more_data, false);
+}
+
+static void
+iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta, u16 tids,
+ int num_frames,
+ enum ieee80211_frame_release_type reason,
+ bool more_data)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ /* Called when we need to transmit (a) frame(s) from agg queue */
+
+ iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
+ tids, more_data, true);
}
static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
@@ -1209,11 +1598,25 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ int tid;
switch (cmd) {
case STA_NOTIFY_SLEEP:
if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)
ieee80211_sta_block_awake(hw, sta, true);
+ spin_lock_bh(&mvmsta->lock);
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ struct iwl_mvm_tid_data *tid_data;
+
+ tid_data = &mvmsta->tid_data[tid];
+ if (tid_data->state != IWL_AGG_ON &&
+ tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
+ continue;
+ if (iwl_mvm_tid_queued(tid_data) == 0)
+ continue;
+ ieee80211_sta_set_buffered(sta, tid, true);
+ }
+ spin_unlock_bh(&mvmsta->lock);
/*
* The fw updates the STA to be asleep. Tx packets on the Tx
* queues to this station will not be transmitted. The fw will
@@ -1304,12 +1707,14 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED) {
/* enable beacon filtering */
- WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif));
+ if (vif->bss_conf.dtim_period)
+ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif,
+ CMD_SYNC));
ret = 0;
} else if (old_state == IEEE80211_STA_AUTHORIZED &&
new_state == IEEE80211_STA_ASSOC) {
/* disable beacon filtering */
- WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif));
+ WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC));
ret = 0;
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH) {
@@ -1401,9 +1806,26 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
- if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
- IWL_DEBUG_SCAN(mvm,
- "SCHED SCAN request during internal scan - abort\n");
+ switch (mvm->scan_status) {
+ case IWL_MVM_SCAN_OS:
+ IWL_DEBUG_SCAN(mvm, "Stopping previous scan for sched_scan\n");
+ ret = iwl_mvm_cancel_scan(mvm);
+ if (ret) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /*
+ * iwl_mvm_rx_scan_complete() will be called soon but will
+ * not reset the scan status as it won't be IWL_MVM_SCAN_OS
+ * any more since we queue the next scan immediately (below).
+ * We make sure it is called before the next scan starts by
+ * flushing the async-handlers work.
+ */
+ break;
+ case IWL_MVM_SCAN_NONE:
+ break;
+ default:
ret = -EBUSY;
goto out;
}
@@ -1425,17 +1847,23 @@ err:
mvm->scan_status = IWL_MVM_SCAN_NONE;
out:
mutex_unlock(&mvm->mutex);
+ /* make sure to flush the Rx handler before the next scan arrives */
+ iwl_mvm_wait_for_async_handlers(mvm);
return ret;
}
-static void iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
mutex_lock(&mvm->mutex);
- iwl_mvm_sched_scan_stop(mvm);
+ ret = iwl_mvm_sched_scan_stop(mvm);
mutex_unlock(&mvm->mutex);
+ iwl_mvm_wait_for_async_handlers(mvm);
+
+ return ret;
}
static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
@@ -1773,8 +2201,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
* Power state must be updated before quotas,
* otherwise fw will complain.
*/
- mvm->bound_vif_cnt++;
- iwl_mvm_power_update_binding(mvm, vif, true);
+ iwl_mvm_power_update_mac(mvm, vif);
/* Setting the quota at this stage is only required for monitor
* interfaces. For the other types, the bss_info changed flow
@@ -1791,8 +2218,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
out_remove_binding:
iwl_mvm_binding_remove_vif(mvm, vif);
- mvm->bound_vif_cnt--;
- iwl_mvm_power_update_binding(mvm, vif, false);
+ iwl_mvm_power_update_mac(mvm, vif);
out_unlock:
mutex_unlock(&mvm->mutex);
if (ret)
@@ -1824,8 +2250,7 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
}
iwl_mvm_binding_remove_vif(mvm, vif);
- mvm->bound_vif_cnt--;
- iwl_mvm_power_update_binding(mvm, vif, false);
+ iwl_mvm_power_update_mac(mvm, vif);
out_unlock:
mvmvif->phy_ctxt = NULL;
@@ -1892,8 +2317,9 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
return -EINVAL;
if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
- return iwl_mvm_enable_beacon_filter(mvm, vif);
- return iwl_mvm_disable_beacon_filter(mvm, vif);
+ return iwl_mvm_enable_beacon_filter(mvm, vif,
+ CMD_SYNC);
+ return iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
}
return -EOPNOTSUPP;
@@ -1914,7 +2340,7 @@ static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
}
#endif
-struct ieee80211_ops iwl_mvm_hw_ops = {
+const struct ieee80211_ops iwl_mvm_hw_ops = {
.tx = iwl_mvm_mac_tx,
.ampdu_action = iwl_mvm_mac_ampdu_action,
.start = iwl_mvm_mac_start,
@@ -1932,6 +2358,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
.sta_state = iwl_mvm_mac_sta_state,
.sta_notify = iwl_mvm_mac_sta_notify,
.allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
+ .release_buffered_frames = iwl_mvm_mac_release_buffered_frames,
.set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
.sta_rc_update = iwl_mvm_sta_rc_update,
.conf_tx = iwl_mvm_mac_conf_tx,
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 2b0ba1fc3c82..d564233a65da 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -91,9 +91,7 @@ enum iwl_mvm_tx_fifo {
IWL_MVM_TX_FIFO_MCAST = 5,
};
-extern struct ieee80211_ops iwl_mvm_hw_ops;
-extern const struct iwl_mvm_power_ops pm_legacy_ops;
-extern const struct iwl_mvm_power_ops pm_mac_ops;
+extern const struct ieee80211_ops iwl_mvm_hw_ops;
/**
* struct iwl_mvm_mod_params - module parameters for iwlmvm
@@ -159,20 +157,6 @@ enum iwl_power_scheme {
IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
#define IWL_UAPSD_MAX_SP IEEE80211_WMM_IE_STA_QOSINFO_SP_2
-struct iwl_mvm_power_ops {
- int (*power_update_mode)(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
- int (*power_update_device_mode)(struct iwl_mvm *mvm);
- int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
- void (*power_update_binding)(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif, bool assign);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- char *buf, int bufsz);
-#endif
-};
-
-
#ifdef CONFIG_IWLWIFI_DEBUGFS
enum iwl_dbgfs_pm_mask {
MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0),
@@ -239,6 +223,19 @@ enum iwl_mvm_smps_type_request {
NUM_IWL_MVM_SMPS_REQ,
};
+enum iwl_mvm_ref_type {
+ IWL_MVM_REF_UCODE_DOWN,
+ IWL_MVM_REF_SCAN,
+ IWL_MVM_REF_ROC,
+ IWL_MVM_REF_P2P_CLIENT,
+ IWL_MVM_REF_AP_IBSS,
+ IWL_MVM_REF_USER,
+ IWL_MVM_REF_TX,
+ IWL_MVM_REF_TX_AGG,
+
+ IWL_MVM_REF_COUNT,
+};
+
/**
* struct iwl_mvm_vif_bf_data - beacon filtering related data
* @bf_enabled: indicates if beacon filtering is enabled
@@ -269,7 +266,9 @@ struct iwl_mvm_vif_bf_data {
* @ap_ibss_active: indicates that AP/IBSS is configured and that the interface
* should get quota etc.
* @monitor_active: indicates that monitor context is configured, and that the
- * interface should get quota etc.
+ * interface should get quota etc.
+ * @low_latency: indicates that this interface is in low-latency mode
+ * (VMACLowLatencyMode)
* @queue_params: QoS params for this MAC
* @bcast_sta: station used for broadcast packets. Used by the following
* vifs: P2P_DEVICE, GO and AP.
@@ -285,6 +284,7 @@ struct iwl_mvm_vif {
bool uploaded;
bool ap_ibss_active;
bool monitor_active;
+ bool low_latency;
struct iwl_mvm_vif_bf_data bf_data;
u32 ap_beacon_time;
@@ -319,13 +319,13 @@ struct iwl_mvm_vif {
bool seqno_valid;
u16 seqno;
+#endif
#if IS_ENABLED(CONFIG_IPV6)
/* IPv6 addresses for WoWLAN */
struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
int num_target_ipv6_addrs;
#endif
-#endif
#ifdef CONFIG_IWLWIFI_DEBUGFS
struct iwl_mvm *mvm;
@@ -333,14 +333,13 @@ struct iwl_mvm_vif {
struct dentry *dbgfs_slink;
struct iwl_dbgfs_pm dbgfs_pm;
struct iwl_dbgfs_bf dbgfs_bf;
+ struct iwl_mac_power_cmd mac_pwr_cmd;
#endif
enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ];
/* FW identified misbehaving AP */
u8 uapsd_misbehaving_bssid[ETH_ALEN];
-
- bool pm_prevented;
};
static inline struct iwl_mvm_vif *
@@ -349,6 +348,8 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)
return (void *)vif->drv_priv;
}
+extern const u8 tid_to_mac80211_ac[];
+
enum iwl_scan_status {
IWL_MVM_SCAN_NONE,
IWL_MVM_SCAN_OS,
@@ -415,6 +416,7 @@ struct iwl_tt_params {
* @ct_kill_exit: worker to exit thermal kill
* @dynamic_smps: Is thermal throttling enabled dynamic_smps?
* @tx_backoff: The current thremal throttling tx backoff in uSec.
+ * @min_backoff: The minimal tx backoff due to power restrictions
* @params: Parameters to configure the thermal throttling algorithm.
* @throttle: Is thermal throttling is active?
*/
@@ -422,10 +424,33 @@ struct iwl_mvm_tt_mgmt {
struct delayed_work ct_kill_exit;
bool dynamic_smps;
u32 tx_backoff;
+ u32 min_backoff;
const struct iwl_tt_params *params;
bool throttle;
};
+#define IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES 8
+
+struct iwl_mvm_frame_stats {
+ u32 legacy_frames;
+ u32 ht_frames;
+ u32 vht_frames;
+ u32 bw_20_frames;
+ u32 bw_40_frames;
+ u32 bw_80_frames;
+ u32 bw_160_frames;
+ u32 sgi_frames;
+ u32 ngi_frames;
+ u32 siso_frames;
+ u32 mimo2_frames;
+ u32 agg_frames;
+ u32 ampdu_count;
+ u32 success_frames;
+ u32 fail_frames;
+ u32 last_rates[IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES];
+ int last_frame_idx;
+};
+
struct iwl_mvm {
/* for logger access */
struct device *dev;
@@ -457,6 +482,8 @@ struct iwl_mvm {
bool init_ucode_complete;
u32 error_event_table;
u32 log_event_table;
+ u32 umac_error_event_table;
+ bool support_umac_log;
u32 ampdu_ref;
@@ -470,7 +497,7 @@ struct iwl_mvm {
struct iwl_nvm_data *nvm_data;
/* NVM sections */
- struct iwl_nvm_section nvm_sections[NVM_NUM_OF_SECTIONS];
+ struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS];
/* EEPROM MAC addresses */
struct mac_address addresses[IWL_MVM_MAX_ADDRESSES];
@@ -494,6 +521,17 @@ struct iwl_mvm {
/* rx chain antennas set through debugfs for the scan command */
u8 scan_rx_ant;
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+ /* broadcast filters to configure for each associated station */
+ const struct iwl_fw_bcast_filter *bcast_filters;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ struct {
+ u32 override; /* u32 for debugfs_create_bool */
+ struct iwl_bcast_filter_cmd cmd;
+ } dbgfs_bcast_filtering;
+#endif
+#endif
+
/* Internal station */
struct iwl_mvm_int_sta aux_sta;
@@ -506,6 +544,7 @@ struct iwl_mvm {
#ifdef CONFIG_IWLWIFI_DEBUGFS
struct dentry *debugfs_dir;
u32 dbgfs_sram_offset, dbgfs_sram_len;
+ u32 dbgfs_prph_reg_addr;
bool disable_power_off;
bool disable_power_off_d3;
@@ -513,6 +552,9 @@ struct iwl_mvm {
struct debugfs_blob_wrapper nvm_sw_blob;
struct debugfs_blob_wrapper nvm_calib_blob;
struct debugfs_blob_wrapper nvm_prod_blob;
+
+ struct iwl_mvm_frame_stats drv_rx_stats;
+ spinlock_t drv_stats_lock;
#endif
struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX];
@@ -526,10 +568,16 @@ struct iwl_mvm {
*/
unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
+ /* A bitmap of reference types taken by the driver. */
+ unsigned long ref_bitmap[BITS_TO_LONGS(IWL_MVM_REF_COUNT)];
+
u8 vif_count;
/* -1 for always, 0 for never, >0 for that many times */
s8 restart_fw;
+ void *fw_error_dump;
+ void *fw_error_sram;
+ u32 fw_error_sram_len;
struct led_classdev led;
@@ -548,17 +596,27 @@ struct iwl_mvm {
#endif
#endif
+ /* d0i3 */
+ u8 d0i3_ap_sta_id;
+ bool d0i3_offloading;
+ struct work_struct d0i3_exit_work;
+ struct sk_buff_head d0i3_tx;
+ /* sync d0i3_tx queue and IWL_MVM_STATUS_IN_D0I3 status flag */
+ spinlock_t d0i3_tx_lock;
+ wait_queue_head_t d0i3_exit_waitq;
+
/* BT-Coex */
u8 bt_kill_msk;
struct iwl_bt_coex_profile_notif last_bt_notif;
struct iwl_bt_coex_ci_cmd last_bt_ci_cmd;
+ u32 last_ant_isol;
+ u8 last_corun_lut;
+ u8 bt_tx_prio;
/* Thermal Throttling and CTkill */
struct iwl_mvm_tt_mgmt thermal_throttle;
s32 temperature; /* Celsius */
- const struct iwl_mvm_power_ops *pm_ops;
-
#ifdef CONFIG_NL80211_TESTMODE
u32 noa_duration;
struct ieee80211_vif *noa_vif;
@@ -569,10 +627,10 @@ struct iwl_mvm {
u8 first_agg_queue;
u8 last_agg_queue;
- u8 bound_vif_cnt;
-
/* Indicate if device power save is allowed */
- bool ps_prevented;
+ bool ps_disabled;
+ /* Indicate if device power management is allowed */
+ bool pm_disabled;
};
/* Extract MVM priv from op_mode and _hw */
@@ -587,6 +645,7 @@ enum iwl_mvm_status {
IWL_MVM_STATUS_HW_CTKILL,
IWL_MVM_STATUS_ROC_RUNNING,
IWL_MVM_STATUS_IN_HW_RESTART,
+ IWL_MVM_STATUS_IN_D0I3,
};
static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
@@ -595,6 +654,30 @@ static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
}
+static inline struct iwl_mvm_sta *
+iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
+{
+ struct ieee80211_sta *sta;
+
+ if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
+ return NULL;
+
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex));
+
+ /* This can happen if the station has been removed right now */
+ if (IS_ERR_OR_NULL(sta))
+ return NULL;
+
+ return iwl_mvm_sta_from_mac80211(sta);
+}
+
+static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm)
+{
+ return mvm->trans->cfg->d0i3 &&
+ (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_D0I3_SUPPORT);
+}
+
extern const u8 iwl_mvm_ac_to_tx_fifo[];
struct iwl_rate_info {
@@ -619,7 +702,10 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
struct ieee80211_tx_rate *r);
u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
-void iwl_mvm_dump_sram(struct iwl_mvm *mvm);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
+void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm);
+#endif
u8 first_antenna(u8 mask);
u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
@@ -645,6 +731,11 @@ static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; }
int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync);
void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm);
+static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm)
+{
+ flush_work(&mvm->async_handlers_wk);
+}
+
/* Statistics */
int iwl_mvm_rx_reply_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
@@ -661,6 +752,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm);
int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm);
int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm);
+bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
+ struct iwl_bcast_filter_cmd *cmd);
/*
* FW notifications / CMD responses handlers
@@ -676,6 +769,9 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
int iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
@@ -730,7 +826,7 @@ int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
-void iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
+int iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
/* Scheduled scan */
int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
@@ -744,7 +840,7 @@ int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req);
int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req);
-void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm);
+int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm);
int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
@@ -772,49 +868,24 @@ iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
/* rate scaling */
int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init);
+void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm,
+ struct iwl_mvm_frame_stats *stats,
+ u32 rate, bool agg);
+int rs_pretty_print_rate(char *buf, const u32 rate);
-/* power managment */
-static inline int iwl_mvm_power_update_mode(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- return mvm->pm_ops->power_update_mode(mvm, vif);
-}
-
-static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- return mvm->pm_ops->power_disable(mvm, vif);
-}
-
-static inline int iwl_mvm_power_update_device_mode(struct iwl_mvm *mvm)
-{
- if (mvm->pm_ops->power_update_device_mode)
- return mvm->pm_ops->power_update_device_mode(mvm);
- return 0;
-}
+/* power management */
+int iwl_power_legacy_set_cam_mode(struct iwl_mvm *mvm);
-static inline void iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- bool assign)
-{
- if (mvm->pm_ops->power_update_binding)
- mvm->pm_ops->power_update_binding(mvm, vif, assign);
-}
+int iwl_mvm_power_update_device(struct iwl_mvm *mvm);
+int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ char *buf, int bufsz);
void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- char *buf, int bufsz)
-{
- return mvm->pm_ops->power_dbgfs_read(mvm, vif, buf, bufsz);
-}
-#endif
-
int iwl_mvm_leds_init(struct iwl_mvm *mvm);
void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
@@ -840,6 +911,17 @@ iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
}
#endif
+void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
+ struct iwl_wowlan_config_cmd_v2 *cmd);
+int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool disable_offloading,
+ u32 cmd_flags);
+
+/* D0i3 */
+void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
/* BT Coex */
int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
@@ -850,10 +932,13 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event);
void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm);
-u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta);
+u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta);
bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
struct ieee80211_sta *sta);
+u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
+ struct ieee80211_tx_info *info, u8 ac);
+int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable);
enum iwl_bt_kill_msk {
BT_KILL_MSK_DEFAULT,
@@ -875,25 +960,53 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
struct iwl_beacon_filter_cmd *cmd)
{}
#endif
+int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool enable, u32 flags);
int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
+ struct ieee80211_vif *vif,
+ u32 flags);
int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
-int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
- struct iwl_beacon_filter_cmd *cmd);
+ struct ieee80211_vif *vif,
+ u32 flags);
int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, bool enable);
int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
+ struct ieee80211_vif *vif,
+ bool force,
+ u32 flags);
/* SMPS */
void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum iwl_mvm_smps_type_request req_type,
enum ieee80211_smps_mode smps_request);
+/* Low latency */
+int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool value);
+/* get SystemLowLatencyMode - only needed for beacon threshold? */
+bool iwl_mvm_low_latency(struct iwl_mvm *mvm);
+/* get VMACLowLatencyMode */
+static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif)
+{
+ /*
+ * should this consider associated/active/... state?
+ *
+ * Normally low-latency should only be active on interfaces
+ * that are active, but at least with debugfs it can also be
+ * enabled on interfaces that aren't active. However, when
+ * interface aren't active then they aren't added into the
+ * binding, so this has no real impact. For now, just return
+ * the current desired low-latency state.
+ */
+
+ return mvmvif->low_latency;
+}
+
/* Thermal management and CT-kill */
+void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff);
void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
-void iwl_mvm_tt_initialize(struct iwl_mvm *mvm);
+void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff);
void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c
index 35b71af78d02..cf2d09f53782 100644
--- a/drivers/net/wireless/iwlwifi/mvm/nvm.c
+++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c
@@ -67,14 +67,6 @@
#include "iwl-eeprom-read.h"
#include "iwl-nvm-parse.h"
-/* list of NVM sections we are allowed/need to read */
-static const int nvm_to_read[] = {
- NVM_SECTION_TYPE_HW,
- NVM_SECTION_TYPE_SW,
- NVM_SECTION_TYPE_CALIBRATION,
- NVM_SECTION_TYPE_PRODUCTION,
-};
-
/* Default NVM size to read */
#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
#define IWL_MAX_NVM_SECTION_SIZE 7000
@@ -236,24 +228,39 @@ static struct iwl_nvm_data *
iwl_parse_nvm_sections(struct iwl_mvm *mvm)
{
struct iwl_nvm_section *sections = mvm->nvm_sections;
- const __le16 *hw, *sw, *calib;
+ const __le16 *hw, *sw, *calib, *regulatory, *mac_override;
/* Checking for required sections */
- if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
- !mvm->nvm_sections[NVM_SECTION_TYPE_HW].data) {
- IWL_ERR(mvm, "Can't parse empty NVM sections\n");
- return NULL;
+ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
+ !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) {
+ IWL_ERR(mvm, "Can't parse empty NVM sections\n");
+ return NULL;
+ }
+ } else {
+ if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
+ !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data ||
+ !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) {
+ IWL_ERR(mvm,
+ "Can't parse empty family 8000 NVM sections\n");
+ return NULL;
+ }
}
if (WARN_ON(!mvm->cfg))
return NULL;
- hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data;
+ hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data;
sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
+ regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data;
+ mac_override =
+ (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data;
+
return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
- iwl_fw_valid_tx_ant(mvm->fw),
- iwl_fw_valid_rx_ant(mvm->fw));
+ regulatory, mac_override,
+ mvm->fw->valid_tx_ant,
+ mvm->fw->valid_rx_ant);
}
#define MAX_NVM_FILE_LEN 16384
@@ -293,6 +300,8 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
#define NVM_WORD2_ID(x) (x >> 12)
+#define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8))
+#define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4)
IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");
@@ -343,8 +352,16 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
break;
}
- section_size = 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1));
- section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2));
+ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ section_size =
+ 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1));
+ section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2));
+ } else {
+ section_size = 2 * NVM_WORD2_LEN_FAMILY_8000(
+ le16_to_cpu(file_sec->word2));
+ section_id = NVM_WORD1_ID_FAMILY_8000(
+ le16_to_cpu(file_sec->word1));
+ }
if (section_size > IWL_MAX_NVM_SECTION_SIZE) {
IWL_ERR(mvm, "ERROR - section too large (%d)\n",
@@ -367,7 +384,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
break;
}
- if (WARN(section_id >= NVM_NUM_OF_SECTIONS,
+ if (WARN(section_id >= NVM_MAX_NUM_SECTIONS,
"Invalid NVM section ID %d\n", section_id)) {
ret = -EINVAL;
break;
@@ -414,6 +431,11 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
{
int ret, i, section;
u8 *nvm_buffer, *temp;
+ int nvm_to_read[NVM_MAX_NUM_SECTIONS];
+ int num_of_sections_to_read;
+
+ if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
+ return -EINVAL;
/* load external NVM if configured */
if (iwlwifi_mod_params.nvm_file) {
@@ -422,6 +444,22 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
if (ret)
return ret;
} else {
+ /* list of NVM sections we are allowed/need to read */
+ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ nvm_to_read[0] = mvm->cfg->nvm_hw_section_num;
+ nvm_to_read[1] = NVM_SECTION_TYPE_SW;
+ nvm_to_read[2] = NVM_SECTION_TYPE_CALIBRATION;
+ nvm_to_read[3] = NVM_SECTION_TYPE_PRODUCTION;
+ num_of_sections_to_read = 4;
+ } else {
+ nvm_to_read[0] = NVM_SECTION_TYPE_SW;
+ nvm_to_read[1] = NVM_SECTION_TYPE_CALIBRATION;
+ nvm_to_read[2] = NVM_SECTION_TYPE_PRODUCTION;
+ nvm_to_read[3] = NVM_SECTION_TYPE_REGULATORY;
+ nvm_to_read[4] = NVM_SECTION_TYPE_MAC_OVERRIDE;
+ num_of_sections_to_read = 5;
+ }
+
/* Read From FW NVM */
IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
@@ -430,7 +468,7 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
GFP_KERNEL);
if (!nvm_buffer)
return -ENOMEM;
- for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
+ for (i = 0; i < num_of_sections_to_read; i++) {
section = nvm_to_read[i];
/* we override the constness for initial read */
ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
@@ -446,10 +484,6 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
#ifdef CONFIG_IWLWIFI_DEBUGFS
switch (section) {
- case NVM_SECTION_TYPE_HW:
- mvm->nvm_hw_blob.data = temp;
- mvm->nvm_hw_blob.size = ret;
- break;
case NVM_SECTION_TYPE_SW:
mvm->nvm_sw_blob.data = temp;
mvm->nvm_sw_blob.size = ret;
@@ -463,6 +497,11 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
mvm->nvm_prod_blob.size = ret;
break;
default:
+ if (section == mvm->cfg->nvm_hw_section_num) {
+ mvm->nvm_hw_blob.data = temp;
+ mvm->nvm_hw_blob.size = ret;
+ break;
+ }
WARN(1, "section: %d", section);
}
#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/offloading.c b/drivers/net/wireless/iwlwifi/mvm/offloading.c
new file mode 100644
index 000000000000..9bfb95e89cfb
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/offloading.c
@@ -0,0 +1,215 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * 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 <net/ipv6.h>
+#include <net/addrconf.h>
+#include "mvm.h"
+
+void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
+ struct iwl_wowlan_config_cmd_v2 *cmd)
+{
+ int i;
+
+ /*
+ * For QoS counters, we store the one to use next, so subtract 0x10
+ * since the uCode will add 0x10 *before* using the value while we
+ * increment after using the value (i.e. store the next value to use).
+ */
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ u16 seq = mvm_ap_sta->tid_data[i].seq_number;
+ seq -= 0x10;
+ cmd->qos_seq[i] = cpu_to_le16(seq);
+ }
+}
+
+int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool disable_offloading,
+ u32 cmd_flags)
+{
+ union {
+ struct iwl_proto_offload_cmd_v1 v1;
+ struct iwl_proto_offload_cmd_v2 v2;
+ struct iwl_proto_offload_cmd_v3_small v3s;
+ struct iwl_proto_offload_cmd_v3_large v3l;
+ } cmd = {};
+ struct iwl_host_cmd hcmd = {
+ .id = PROT_OFFLOAD_CONFIG_CMD,
+ .flags = cmd_flags,
+ .data[0] = &cmd,
+ .dataflags[0] = IWL_HCMD_DFL_DUP,
+ };
+ struct iwl_proto_offload_cmd_common *common;
+ u32 enabled = 0, size;
+ u32 capa_flags = mvm->fw->ucode_capa.flags;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int i;
+
+ if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
+ capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
+ struct iwl_ns_config *nsc;
+ struct iwl_targ_addr *addrs;
+ int n_nsc, n_addrs;
+ int c;
+
+ if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
+ nsc = cmd.v3s.ns_config;
+ n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;
+ addrs = cmd.v3s.targ_addrs;
+ n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;
+ } else {
+ nsc = cmd.v3l.ns_config;
+ n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
+ addrs = cmd.v3l.targ_addrs;
+ 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
+ * solicited node addresses.
+ */
+ for (i = 0, c = 0;
+ i < mvmvif->num_target_ipv6_addrs &&
+ i < n_addrs && c < n_nsc; i++) {
+ struct in6_addr solicited_addr;
+ int j;
+
+ addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
+ &solicited_addr);
+ for (j = 0; j < c; j++)
+ if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
+ &solicited_addr) == 0)
+ break;
+ if (j == c)
+ c++;
+ addrs[i].addr = mvmvif->target_ipv6_addrs[i];
+ addrs[i].config_num = cpu_to_le32(j);
+ nsc[j].dest_ipv6_addr = solicited_addr;
+ memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
+ }
+
+ if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
+ cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i);
+ else
+ cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i);
+ } 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);
+ }
+
+ 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++)
+ 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);
+ }
+
+ 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++)
+ memcpy(cmd.v1.target_ipv6_addr[i],
+ &mvmvif->target_ipv6_addrs[i],
+ sizeof(cmd.v1.target_ipv6_addr[i]));
+ }
+#endif
+
+ if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
+ common = &cmd.v3s.common;
+ size = sizeof(cmd.v3s);
+ } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
+ common = &cmd.v3l.common;
+ size = sizeof(cmd.v3l);
+ } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+ common = &cmd.v2.common;
+ size = sizeof(cmd.v2);
+ } else {
+ common = &cmd.v1.common;
+ size = sizeof(cmd.v1);
+ }
+
+ if (vif->bss_conf.arp_addr_cnt) {
+ enabled |= IWL_D3_PROTO_OFFLOAD_ARP;
+ common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
+ memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
+ }
+
+ if (!disable_offloading)
+ common->enabled = cpu_to_le32(enabled);
+
+ hcmd.len[0] = size;
+ return iwl_mvm_send_cmd(mvm, &hcmd);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index a3d43de342d7..9545d7fdd4bf 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -61,6 +61,7 @@
*
*****************************************************************************/
#include <linux/module.h>
+#include <linux/vmalloc.h>
#include <net/mac80211.h>
#include "iwl-notif-wait.h"
@@ -78,6 +79,7 @@
#include "iwl-prph.h"
#include "rs.h"
#include "fw-api-scan.h"
+#include "fw-error-dump.h"
#include "time-event.h"
/*
@@ -185,9 +187,10 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
* (PCIe power is lost before PERST# is asserted), causing ME FW
* to lose ownership and not being able to obtain it back.
*/
- iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
- ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
+ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
+ APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
+ ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
}
struct iwl_rx_handlers {
@@ -219,13 +222,17 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false),
RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true),
+ RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION,
+ iwl_mvm_rx_ant_coupling_notif, true),
RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
+ RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false),
+
RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
- RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
+ RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true),
RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
- iwl_mvm_rx_scan_offload_complete_notif, false),
+ iwl_mvm_rx_scan_offload_complete_notif, true),
RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results,
false),
@@ -242,7 +249,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
#undef RX_HANDLER
#define CMD(x) [x] = #x
-static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
+static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(MVM_ALIVE),
CMD(REPLY_ERROR),
CMD(INIT_COMPLETE_NOTIF),
@@ -284,9 +291,11 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(BEACON_NOTIFICATION),
CMD(BEACON_TEMPLATE_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),
@@ -309,17 +318,37 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
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(REPLY_THERMAL_MNG_BACKOFF),
CMD(MAC_PM_POWER_TABLE),
CMD(BT_COEX_CI),
CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
+ CMD(ANTENNA_COUPLING_NOTIFICATION),
};
#undef CMD
/* this forward declaration can avoid to export the function */
static void iwl_mvm_async_handlers_wk(struct work_struct *wk);
+static void iwl_mvm_d0i3_exit_work(struct work_struct *wk);
+
+static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg)
+{
+ const struct iwl_pwr_tx_backoff *pwr_tx_backoff = cfg->pwr_tx_backoffs;
+
+ if (!pwr_tx_backoff)
+ return 0;
+
+ while (pwr_tx_backoff->pwr) {
+ if (trans->dflt_pwr_limit >= pwr_tx_backoff->pwr)
+ return pwr_tx_backoff->backoff;
+
+ pwr_tx_backoff++;
+ }
+
+ return 0;
+}
static struct iwl_op_mode *
iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
@@ -333,6 +362,14 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
TX_CMD,
};
int err, scan_size;
+ u32 min_backoff;
+
+ /*
+ * We use IWL_MVM_STATION_COUNT to check the validity of the station
+ * index all over the driver - check that its value corresponds to the
+ * array size.
+ */
+ BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT);
/********************************
* 1. Allocating and configuring HW data
@@ -373,6 +410,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
+ INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
+
+ spin_lock_init(&mvm->d0i3_tx_lock);
+ skb_queue_head_init(&mvm->d0i3_tx);
+ init_waitqueue_head(&mvm->d0i3_exit_waitq);
SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
@@ -421,7 +463,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
IWL_INFO(mvm, "Detected %s, REV=0x%X\n",
mvm->cfg->name, mvm->trans->hw_rev);
- iwl_mvm_tt_initialize(mvm);
+ min_backoff = calc_min_backoff(trans, cfg);
+ iwl_mvm_tt_initialize(mvm, min_backoff);
/*
* If the NVM exists in an external file,
@@ -462,13 +505,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
if (err)
goto out_unregister;
- if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)
- mvm->pm_ops = &pm_mac_ops;
- else
- mvm->pm_ops = &pm_legacy_ops;
-
memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx));
+ /* rpm starts with a taken ref. only set the appropriate bit here. */
+ set_bit(IWL_MVM_REF_UCODE_DOWN, mvm->ref_bitmap);
+
return op_mode;
out_unregister:
@@ -495,6 +536,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
ieee80211_unregister_hw(mvm->hw);
kfree(mvm->scan_cmd);
+ vfree(mvm->fw_error_dump);
+ kfree(mvm->fw_error_sram);
kfree(mvm->mcast_filter_cmd);
mvm->mcast_filter_cmd = NULL;
@@ -508,7 +551,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
mvm->phy_db = NULL;
iwl_free_nvm_data(mvm->nvm_data);
- for (i = 0; i < NVM_NUM_OF_SECTIONS; i++)
+ for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++)
kfree(mvm->nvm_sections[i].data);
ieee80211_free_hw(mvm->hw);
@@ -658,7 +701,7 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state)
wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
}
-static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
+static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
@@ -667,9 +710,9 @@ static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
else
clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
- if (state && mvm->cur_ucode != IWL_UCODE_INIT)
- iwl_trans_stop_device(mvm->trans);
wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
+
+ return state && mvm->cur_ucode != IWL_UCODE_INIT;
}
static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
@@ -703,6 +746,29 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
iwl_abort_notification_waits(&mvm->notif_wait);
/*
+ * This is a bit racy, but worst case we tell mac80211 about
+ * a stopped/aborted scan when that was already done which
+ * is not a problem. It is necessary to abort any os scan
+ * here because mac80211 requires having the scan cleared
+ * before restarting.
+ * We'll reset the scan_status to NONE in restart cleanup in
+ * the next start() call from mac80211. If restart isn't called
+ * (no fw restart) scan status will stay busy.
+ */
+ switch (mvm->scan_status) {
+ case IWL_MVM_SCAN_NONE:
+ break;
+ case IWL_MVM_SCAN_OS:
+ ieee80211_scan_completed(mvm->hw, true);
+ break;
+ case IWL_MVM_SCAN_SCHED:
+ /* Sched scan will be restarted by mac80211 in restart_hw. */
+ if (!mvm->restart_fw)
+ ieee80211_sched_scan_stopped(mvm->hw);
+ break;
+ }
+
+ /*
* If we're restarting already, don't cycle restarts.
* If INIT fw asserted, it will likely fail again.
* If WoWLAN fw asserted, don't restart either, mac80211
@@ -733,25 +799,8 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
schedule_work(&reprobe->work);
} else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) {
- /*
- * This is a bit racy, but worst case we tell mac80211 about
- * a stopped/aborted (sched) scan when that was already done
- * which is not a problem. It is necessary to abort any scan
- * here because mac80211 requires having the scan cleared
- * before restarting.
- * We'll reset the scan_status to NONE in restart cleanup in
- * the next start() call from mac80211.
- */
- switch (mvm->scan_status) {
- case IWL_MVM_SCAN_NONE:
- break;
- case IWL_MVM_SCAN_OS:
- ieee80211_scan_completed(mvm->hw, true);
- break;
- case IWL_MVM_SCAN_SCHED:
- ieee80211_sched_scan_stopped(mvm->hw);
- break;
- }
+ /* don't let the transport/FW power down */
+ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
if (mvm->restart_fw > 0)
mvm->restart_fw--;
@@ -759,13 +808,52 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
}
}
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+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;
+ u32 file_len;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (mvm->fw_error_dump)
+ return;
+
+ file_len = mvm->fw_error_sram_len +
+ sizeof(*dump_file) +
+ sizeof(*dump_data);
+
+ dump_file = vmalloc(file_len);
+ if (!dump_file)
+ return;
+
+ mvm->fw_error_dump = dump_file;
+
+ dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
+ dump_file->file_len = cpu_to_le32(file_len);
+ dump_data = (void *)dump_file->data;
+ dump_data->type = IWL_FW_ERROR_DUMP_SRAM;
+ dump_data->len = cpu_to_le32(mvm->fw_error_sram_len);
+
+ /*
+ * No need for lock since at the stage the FW isn't loaded. So it
+ * can't assert - we are the only one who can possibly be accessing
+ * mvm->fw_error_sram right now.
+ */
+ memcpy(dump_data->data, mvm->fw_error_sram, mvm->fw_error_sram_len);
+}
+#endif
+
static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
iwl_mvm_dump_nic_error_log(mvm);
- if (!mvm->restart_fw)
- iwl_mvm_dump_sram(mvm);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ iwl_mvm_fw_error_sram_dump(mvm);
+#endif
iwl_mvm_nic_restart(mvm);
}
@@ -778,6 +866,323 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
iwl_mvm_nic_restart(mvm);
}
+struct iwl_d0i3_iter_data {
+ struct iwl_mvm *mvm;
+ u8 ap_sta_id;
+ u8 vif_count;
+ u8 offloading_tid;
+ bool disable_offloading;
+};
+
+static bool iwl_mvm_disallow_offloading(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_d0i3_iter_data *iter_data)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct ieee80211_sta *ap_sta;
+ struct iwl_mvm_sta *mvmsta;
+ u32 available_tids = 0;
+ u8 tid;
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_STATION ||
+ mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT))
+ return false;
+
+ ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id]);
+ if (IS_ERR_OR_NULL(ap_sta))
+ return false;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(ap_sta);
+ spin_lock_bh(&mvmsta->lock);
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+
+ /*
+ * in case of pending tx packets, don't use this tid
+ * for offloading in order to prevent reuse of the same
+ * qos seq counters.
+ */
+ if (iwl_mvm_tid_queued(tid_data))
+ continue;
+
+ if (tid_data->state != IWL_AGG_OFF)
+ continue;
+
+ available_tids |= BIT(tid);
+ }
+ spin_unlock_bh(&mvmsta->lock);
+
+ /*
+ * disallow protocol offloading if we have no available tid
+ * (with no pending frames and no active aggregation,
+ * as we don't handle "holes" properly - the scheduler needs the
+ * frame's seq number and TFD index to match)
+ */
+ if (!available_tids)
+ return true;
+
+ /* for simplicity, just use the first available tid */
+ iter_data->offloading_tid = ffs(available_tids) - 1;
+ return false;
+}
+
+static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_d0i3_iter_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
+
+ IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr);
+ if (vif->type != NL80211_IFTYPE_STATION ||
+ !vif->bss_conf.assoc)
+ return;
+
+ /*
+ * in case of pending tx packets or active aggregations,
+ * avoid offloading features in order to prevent reuse of
+ * the same qos seq counters.
+ */
+ if (iwl_mvm_disallow_offloading(mvm, vif, data))
+ 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);
+
+ /*
+ * on init/association, mvm already configures POWER_TABLE_CMD
+ * and REPLY_MCAST_FILTER_CMD, so currently don't
+ * reconfigure them (we might want to use different
+ * params later on, though).
+ */
+ data->ap_sta_id = mvmvif->ap_sta_id;
+ data->vif_count++;
+}
+
+static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm,
+ struct iwl_wowlan_config_cmd_v3 *cmd,
+ struct iwl_d0i3_iter_data *iter_data)
+{
+ struct ieee80211_sta *ap_sta;
+ struct iwl_mvm_sta *mvm_ap_sta;
+
+ if (iter_data->ap_sta_id == IWL_MVM_STATION_COUNT)
+ return;
+
+ rcu_read_lock();
+
+ ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[iter_data->ap_sta_id]);
+ if (IS_ERR_OR_NULL(ap_sta))
+ goto out;
+
+ mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
+ cmd->common.is_11n_connection = ap_sta->ht_cap.ht_supported;
+ cmd->offloading_tid = iter_data->offloading_tid;
+
+ /*
+ * The d0i3 uCode takes care of the nonqos counters,
+ * so configure only the qos seq ones.
+ */
+ iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &cmd->common);
+out:
+ rcu_read_unlock();
+}
+static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
+ int ret;
+ struct iwl_d0i3_iter_data d0i3_iter_data = {
+ .mvm = mvm,
+ };
+ struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = {
+ .common = {
+ .wakeup_filter =
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
+ IWL_WOWLAN_WAKEUP_BEACON_MISS |
+ IWL_WOWLAN_WAKEUP_LINK_CHANGE |
+ IWL_WOWLAN_WAKEUP_BCN_FILTERING),
+ },
+ };
+ struct iwl_d3_manager_config d3_cfg_cmd = {
+ .min_sleep_time = cpu_to_le32(1000),
+ };
+
+ IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n");
+
+ /* make sure we have no running tx while configuring the qos */
+ set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
+ synchronize_net();
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_enter_d0i3_iterator,
+ &d0i3_iter_data);
+ if (d0i3_iter_data.vif_count == 1) {
+ mvm->d0i3_ap_sta_id = d0i3_iter_data.ap_sta_id;
+ mvm->d0i3_offloading = !d0i3_iter_data.disable_offloading;
+ } else {
+ WARN_ON_ONCE(d0i3_iter_data.vif_count > 1);
+ mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
+ mvm->d0i3_offloading = false;
+ }
+
+ iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, &d0i3_iter_data);
+ ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags,
+ sizeof(wowlan_config_cmd),
+ &wowlan_config_cmd);
+ if (ret)
+ return ret;
+
+ return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD,
+ flags | CMD_MAKE_TRANS_IDLE,
+ sizeof(d3_cfg_cmd), &d3_cfg_cmd);
+}
+
+static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = _data;
+ u32 flags = CMD_ASYNC | CMD_HIGH_PRIO;
+
+ IWL_DEBUG_RPM(mvm, "exiting D0i3 - vif %pM\n", vif->addr);
+ if (vif->type != NL80211_IFTYPE_STATION ||
+ !vif->bss_conf.assoc)
+ return;
+
+ iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags);
+}
+
+static void iwl_mvm_d0i3_disconnect_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc &&
+ mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
+ ieee80211_connection_loss(vif);
+}
+
+void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq)
+{
+ struct ieee80211_sta *sta = NULL;
+ struct iwl_mvm_sta *mvm_ap_sta;
+ int i;
+ bool wake_queues = false;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ spin_lock_bh(&mvm->d0i3_tx_lock);
+
+ if (mvm->d0i3_ap_sta_id == IWL_MVM_STATION_COUNT)
+ goto out;
+
+ IWL_DEBUG_RPM(mvm, "re-enqueue packets\n");
+
+ /* get the sta in order to update seq numbers and re-enqueue skbs */
+ sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id],
+ lockdep_is_held(&mvm->mutex));
+
+ if (IS_ERR_OR_NULL(sta)) {
+ sta = NULL;
+ goto out;
+ }
+
+ if (mvm->d0i3_offloading && qos_seq) {
+ /* update qos seq numbers if offloading was enabled */
+ mvm_ap_sta = (struct iwl_mvm_sta *)sta->drv_priv;
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ u16 seq = le16_to_cpu(qos_seq[i]);
+ /* firmware stores last-used one, we store next one */
+ seq += 0x10;
+ mvm_ap_sta->tid_data[i].seq_number = seq;
+ }
+ }
+out:
+ /* re-enqueue (or drop) all packets */
+ while (!skb_queue_empty(&mvm->d0i3_tx)) {
+ struct sk_buff *skb = __skb_dequeue(&mvm->d0i3_tx);
+
+ if (!sta || iwl_mvm_tx_skb(mvm, skb, sta))
+ ieee80211_free_txskb(mvm->hw, skb);
+
+ /* if the skb_queue is not empty, we need to wake queues */
+ wake_queues = true;
+ }
+ clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
+ wake_up(&mvm->d0i3_exit_waitq);
+ mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
+ if (wake_queues)
+ ieee80211_wake_queues(mvm->hw);
+
+ spin_unlock_bh(&mvm->d0i3_tx_lock);
+}
+
+static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
+{
+ struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work);
+ struct iwl_host_cmd get_status_cmd = {
+ .id = WOWLAN_GET_STATUSES,
+ .flags = CMD_SYNC | CMD_HIGH_PRIO | CMD_WANT_SKB,
+ };
+ struct iwl_wowlan_status_v6 *status;
+ int ret;
+ u32 disconnection_reasons, wakeup_reasons;
+ __le16 *qos_seq = NULL;
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_send_cmd(mvm, &get_status_cmd);
+ if (ret)
+ goto out;
+
+ if (!get_status_cmd.resp_pkt)
+ goto out;
+
+ status = (void *)get_status_cmd.resp_pkt->data;
+ wakeup_reasons = le32_to_cpu(status->wakeup_reasons);
+ qos_seq = status->qos_seq_ctr;
+
+ IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons);
+
+ disconnection_reasons =
+ IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
+ IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH;
+ if (wakeup_reasons & disconnection_reasons)
+ ieee80211_iterate_active_interfaces(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_d0i3_disconnect_iter, mvm);
+
+ iwl_free_resp(&get_status_cmd);
+out:
+ iwl_mvm_d0i3_enable_tx(mvm, qos_seq);
+ mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE |
+ CMD_WAKE_UP_TRANS;
+ int ret;
+
+ IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n");
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL);
+ if (ret)
+ goto out;
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_exit_d0i3_iterator,
+ mvm);
+out:
+ schedule_work(&mvm->d0i3_exit_work);
+ return ret;
+}
+
static const struct iwl_op_mode_ops iwl_mvm_ops = {
.start = iwl_op_mode_mvm_start,
.stop = iwl_op_mode_mvm_stop,
@@ -789,4 +1194,6 @@ static const struct iwl_op_mode_ops iwl_mvm_ops = {
.nic_error = iwl_mvm_nic_error,
.cmd_queue_full = iwl_mvm_cmd_queue_full,
.nic_config = iwl_mvm_nic_config,
+ .enter_d0i3 = iwl_mvm_enter_d0i3,
+ .exit_d0i3 = iwl_mvm_exit_d0i3,
};
diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
index b7268c0b3333..237efe0ac1c4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
@@ -156,13 +156,13 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
idle_cnt = chains_static;
active_cnt = chains_dynamic;
- cmd->rxchain_info = cpu_to_le32(iwl_fw_valid_rx_ant(mvm->fw) <<
+ cmd->rxchain_info = cpu_to_le32(mvm->fw->valid_rx_ant <<
PHY_RX_CHAIN_VALID_POS);
cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
cmd->rxchain_info |= cpu_to_le32(active_cnt <<
PHY_RX_CHAIN_MIMO_CNT_POS);
- cmd->txchain_info = cpu_to_le32(iwl_fw_valid_tx_ant(mvm->fw));
+ cmd->txchain_info = cpu_to_le32(mvm->fw->valid_tx_ant);
}
/*
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c
index d9eab3b7bb9f..6b636eab3339 100644
--- a/drivers/net/wireless/iwlwifi/mvm/power.c
+++ b/drivers/net/wireless/iwlwifi/mvm/power.c
@@ -74,39 +74,36 @@
#define POWER_KEEP_ALIVE_PERIOD_SEC 25
+static
int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
- struct iwl_beacon_filter_cmd *cmd)
+ struct iwl_beacon_filter_cmd *cmd,
+ u32 flags)
{
- int ret;
-
- ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC,
- sizeof(struct iwl_beacon_filter_cmd), cmd);
-
- if (!ret) {
- IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
- le32_to_cpu(cmd->ba_enable_beacon_abort));
- IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
- le32_to_cpu(cmd->ba_escape_timer));
- IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
- le32_to_cpu(cmd->bf_debug_flag));
- IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
- le32_to_cpu(cmd->bf_enable_beacon_filter));
- IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
- le32_to_cpu(cmd->bf_energy_delta));
- IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
- le32_to_cpu(cmd->bf_escape_timer));
- IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
- le32_to_cpu(cmd->bf_roaming_energy_delta));
- IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
- le32_to_cpu(cmd->bf_roaming_state));
- IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
- le32_to_cpu(cmd->bf_temp_threshold));
- IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
- le32_to_cpu(cmd->bf_temp_fast_filter));
- IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
- le32_to_cpu(cmd->bf_temp_slow_filter));
- }
- return ret;
+ IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
+ le32_to_cpu(cmd->ba_enable_beacon_abort));
+ IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
+ le32_to_cpu(cmd->ba_escape_timer));
+ IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
+ le32_to_cpu(cmd->bf_debug_flag));
+ IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
+ le32_to_cpu(cmd->bf_enable_beacon_filter));
+ IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
+ le32_to_cpu(cmd->bf_energy_delta));
+ IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
+ le32_to_cpu(cmd->bf_escape_timer));
+ IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
+ le32_to_cpu(cmd->bf_roaming_energy_delta));
+ IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
+ le32_to_cpu(cmd->bf_roaming_state));
+ IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
+ le32_to_cpu(cmd->bf_temp_threshold));
+ IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
+ le32_to_cpu(cmd->bf_temp_fast_filter));
+ IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
+ le32_to_cpu(cmd->bf_temp_slow_filter));
+
+ return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags,
+ sizeof(struct iwl_beacon_filter_cmd), cmd);
}
static
@@ -145,7 +142,7 @@ int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
mvmvif->bf_data.ba_enabled = enable;
iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
- return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+ return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, CMD_SYNC);
}
static void iwl_mvm_power_log(struct iwl_mvm *mvm,
@@ -301,8 +298,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
- if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
- mvm->ps_prevented)
+ if (mvm->ps_disabled)
return;
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
@@ -312,7 +308,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
mvmvif->dbgfs_pm.disable_power_off)
cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
#endif
- if (!vif->bss_conf.ps || mvmvif->pm_prevented)
+ if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) ||
+ mvm->pm_disabled)
return;
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@@ -419,72 +416,44 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
#endif /* CONFIG_IWLWIFI_DEBUGFS */
}
-static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
+static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
- int ret;
- bool ba_enable;
struct iwl_mac_power_cmd cmd = {};
if (vif->type != NL80211_IFTYPE_STATION)
return 0;
if (vif->p2p &&
- !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS))
+ !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM))
return 0;
iwl_mvm_power_build_cmd(mvm, vif, &cmd);
iwl_mvm_power_log(mvm, &cmd);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
- sizeof(cmd), &cmd);
- if (ret)
- return ret;
-
- ba_enable = !!(cmd.flags &
- cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
-
- return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
-}
-
-static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_mac_power_cmd cmd = {};
- struct iwl_mvm_vif *mvmvif __maybe_unused =
- iwl_mvm_vif_from_mac80211(vif);
-
- if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
- return 0;
-
- cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
- mvmvif->color));
-
- if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
- cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
-
#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
- mvmvif->dbgfs_pm.disable_power_off)
- cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
+ memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd));
#endif
- iwl_mvm_power_log(mvm, &cmd);
- return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_ASYNC,
+ return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
sizeof(cmd), &cmd);
}
-static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
+int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
{
struct iwl_device_power_cmd cmd = {
.flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
};
+ if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
+ return 0;
+
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
return 0;
- if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
- force_disable)
+ if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+ mvm->ps_disabled = true;
+
+ if (mvm->ps_disabled)
cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK);
#ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -501,11 +470,6 @@ static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
&cmd);
}
-static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
-{
- return _iwl_mvm_power_update_device(mvm, false);
-}
-
void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -544,44 +508,176 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
return 0;
}
-static void iwl_mvm_power_binding_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
+struct iwl_power_constraint {
+ struct ieee80211_vif *bf_vif;
+ struct ieee80211_vif *bss_vif;
+ struct ieee80211_vif *p2p_vif;
+ u16 bss_phyctx_id;
+ u16 p2p_phyctx_id;
+ bool pm_disabled;
+ bool ps_disabled;
+ struct iwl_mvm *mvm;
+};
+
+static void iwl_mvm_power_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = _data;
- int ret;
+ struct iwl_power_constraint *power_iterator = _data;
+ struct iwl_mvm *mvm = power_iterator->mvm;
+
+ switch (ieee80211_vif_type_p2p(vif)) {
+ case NL80211_IFTYPE_P2P_DEVICE:
+ break;
+
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_AP:
+ /* no BSS power mgmt if we have an active AP */
+ if (mvmvif->ap_ibss_active)
+ power_iterator->pm_disabled = true;
+ break;
+
+ case NL80211_IFTYPE_MONITOR:
+ /* no BSS power mgmt and no device power save */
+ power_iterator->pm_disabled = true;
+ power_iterator->ps_disabled = true;
+ break;
+
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (mvmvif->phy_ctxt)
+ power_iterator->p2p_phyctx_id = mvmvif->phy_ctxt->id;
+
+ /* we should have only one P2P vif */
+ WARN_ON(power_iterator->p2p_vif);
+ power_iterator->p2p_vif = vif;
+
+ IWL_DEBUG_POWER(mvm, "p2p: p2p_id=%d, bss_id=%d\n",
+ power_iterator->p2p_phyctx_id,
+ power_iterator->bss_phyctx_id);
+ if (!(mvm->fw->ucode_capa.flags &
+ IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) {
+ /* no BSS power mgmt if we have a P2P client*/
+ power_iterator->pm_disabled = true;
+ } else if (power_iterator->p2p_phyctx_id < MAX_PHYS &&
+ power_iterator->bss_phyctx_id < MAX_PHYS &&
+ power_iterator->p2p_phyctx_id ==
+ power_iterator->bss_phyctx_id) {
+ power_iterator->pm_disabled = true;
+ }
+ break;
+
+ case NL80211_IFTYPE_STATION:
+ if (mvmvif->phy_ctxt)
+ power_iterator->bss_phyctx_id = mvmvif->phy_ctxt->id;
+
+ /* we should have only one BSS vif */
+ WARN_ON(power_iterator->bss_vif);
+ power_iterator->bss_vif = vif;
+
+ if (mvmvif->bf_data.bf_enabled &&
+ !WARN_ON(power_iterator->bf_vif))
+ power_iterator->bf_vif = vif;
+
+ IWL_DEBUG_POWER(mvm, "bss: p2p_id=%d, bss_id=%d\n",
+ power_iterator->p2p_phyctx_id,
+ power_iterator->bss_phyctx_id);
+ if (mvm->fw->ucode_capa.flags &
+ IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM &&
+ (power_iterator->p2p_phyctx_id < MAX_PHYS &&
+ power_iterator->bss_phyctx_id < MAX_PHYS &&
+ power_iterator->p2p_phyctx_id ==
+ power_iterator->bss_phyctx_id))
+ power_iterator->pm_disabled = true;
+ break;
+
+ default:
+ break;
+ }
+}
- mvmvif->pm_prevented = (mvm->bound_vif_cnt <= 1) ? false : true;
+static void
+iwl_mvm_power_get_global_constraint(struct iwl_mvm *mvm,
+ struct iwl_power_constraint *constraint)
+{
+ lockdep_assert_held(&mvm->mutex);
+
+ if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) {
+ constraint->pm_disabled = true;
+ constraint->ps_disabled = true;
+ }
- ret = iwl_mvm_power_mac_update_mode(mvm, vif);
- WARN_ONCE(ret, "Failed to update power parameters on a specific vif\n");
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_power_iterator, constraint);
}
-static void _iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- bool assign)
+int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_power_constraint constraint = {
+ .p2p_phyctx_id = MAX_PHYS,
+ .bss_phyctx_id = MAX_PHYS,
+ .mvm = mvm,
+ };
+ bool ba_enable;
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
+ return 0;
+
+ iwl_mvm_power_get_global_constraint(mvm, &constraint);
+ mvm->ps_disabled = constraint.ps_disabled;
+ mvm->pm_disabled = constraint.pm_disabled;
+
+ /* don't update device power state unless we add / remove monitor */
if (vif->type == NL80211_IFTYPE_MONITOR) {
- int ret = _iwl_mvm_power_update_device(mvm, assign);
- mvm->ps_prevented = assign;
- WARN_ONCE(ret, "Failed to update power device state\n");
+ ret = iwl_mvm_power_update_device(mvm);
+ if (ret)
+ return ret;
}
- ieee80211_iterate_active_interfaces(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_power_binding_iterator,
- mvm);
+ if (constraint.bss_vif) {
+ ret = iwl_mvm_power_send_cmd(mvm, constraint.bss_vif);
+ if (ret)
+ return ret;
+ }
+
+ if (constraint.p2p_vif) {
+ ret = iwl_mvm_power_send_cmd(mvm, constraint.p2p_vif);
+ if (ret)
+ return ret;
+ }
+
+ if (!constraint.bf_vif)
+ return 0;
+
+ vif = constraint.bf_vif;
+ mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ ba_enable = !(constraint.pm_disabled || constraint.ps_disabled ||
+ !vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif));
+
+ return iwl_mvm_update_beacon_abort(mvm, constraint.bf_vif, ba_enable);
}
#ifdef CONFIG_IWLWIFI_DEBUGFS
-static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif, char *buf,
- int bufsz)
+int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif, char *buf,
+ int bufsz)
{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mac_power_cmd cmd = {};
int pos = 0;
- iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+ if (WARN_ON(!(mvm->fw->ucode_capa.flags &
+ IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)))
+ return 0;
+
+ mutex_lock(&mvm->mutex);
+ memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd));
+ mutex_unlock(&mvm->mutex);
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
@@ -685,32 +781,46 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
}
#endif
-int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
+static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_beacon_filter_cmd *cmd,
+ u32 cmd_flags,
+ bool d0i3)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_beacon_filter_cmd cmd = {
- IWL_BF_CMD_CONFIG_DEFAULTS,
- .bf_enable_beacon_filter = cpu_to_le32(1),
- };
int ret;
if (mvmvif != mvm->bf_allowed_vif ||
vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
- iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
- iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
- ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+ iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd);
+ if (!d0i3)
+ iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd);
+ ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags);
- if (!ret)
+ /* don't change bf_enabled in case of temporary d0i3 configuration */
+ if (!ret && !d0i3)
mvmvif->bf_data.bf_enabled = true;
return ret;
}
+int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 flags)
+{
+ struct iwl_beacon_filter_cmd cmd = {
+ IWL_BF_CMD_CONFIG_DEFAULTS,
+ .bf_enable_beacon_filter = cpu_to_le32(1),
+ };
+
+ return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false);
+}
+
int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
+ struct ieee80211_vif *vif,
+ u32 flags)
{
struct iwl_beacon_filter_cmd cmd = {};
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -720,7 +830,7 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
- ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+ ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags);
if (!ret)
mvmvif->bf_data.bf_enabled = false;
@@ -728,23 +838,89 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
return ret;
}
-int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
+int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool enable, u32 flags)
{
+ int ret;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mac_power_cmd cmd = {};
- if (!mvmvif->bf_data.bf_enabled)
+ if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
- return iwl_mvm_enable_beacon_filter(mvm, vif);
-}
+ if (!vif->bss_conf.assoc)
+ return 0;
-const struct iwl_mvm_power_ops pm_mac_ops = {
- .power_update_mode = iwl_mvm_power_mac_update_mode,
- .power_update_device_mode = iwl_mvm_power_update_device,
- .power_disable = iwl_mvm_power_mac_disable,
- .power_update_binding = _iwl_mvm_power_update_binding,
+ iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+ if (enable) {
+ /* configure skip over dtim up to 300 msec */
+ int dtimper = mvm->hw->conf.ps_dtim_period ?: 1;
+ int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+
+ if (WARN_ON(!dtimper_msec))
+ return 0;
+
+ cmd.flags |=
+ cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+ cmd.skip_dtim_periods = 300 / dtimper_msec;
+ }
+ iwl_mvm_power_log(mvm, &cmd);
#ifdef CONFIG_IWLWIFI_DEBUGFS
- .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
+ memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd));
#endif
-};
+ ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, flags,
+ sizeof(cmd), &cmd);
+ if (ret)
+ return ret;
+
+ /* configure beacon filtering */
+ if (mvmvif != mvm->bf_allowed_vif)
+ return 0;
+
+ if (enable) {
+ struct iwl_beacon_filter_cmd cmd_bf = {
+ IWL_BF_CMD_CONFIG_D0I3,
+ .bf_enable_beacon_filter = cpu_to_le32(1),
+ };
+ ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf,
+ flags, true);
+ } else {
+ if (mvmvif->bf_data.bf_enabled)
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags);
+ else
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif, flags);
+ }
+
+ return ret;
+}
+
+int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool force,
+ u32 flags)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (mvmvif != mvm->bf_allowed_vif)
+ return 0;
+
+ if (!mvmvif->bf_data.bf_enabled) {
+ /* disable beacon filtering explicitly if force is true */
+ if (force)
+ return iwl_mvm_disable_beacon_filter(mvm, vif, flags);
+ return 0;
+ }
+
+ return iwl_mvm_enable_beacon_filter(mvm, vif, flags);
+}
+
+int iwl_power_legacy_set_cam_mode(struct iwl_mvm *mvm)
+{
+ struct iwl_powertable_cmd cmd = {
+ .keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC,
+ };
+
+ return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+ sizeof(cmd), &cmd);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/power_legacy.c b/drivers/net/wireless/iwlwifi/mvm/power_legacy.c
deleted file mode 100644
index ef712ae5bc62..000000000000
--- a/drivers/net/wireless/iwlwifi/mvm/power_legacy.c
+++ /dev/null
@@ -1,319 +0,0 @@
-/******************************************************************************
- *
- * 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.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * 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/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-
-#include <net/mac80211.h>
-
-#include "iwl-debug.h"
-#include "mvm.h"
-#include "iwl-modparams.h"
-#include "fw-api-power.h"
-
-#define POWER_KEEP_ALIVE_PERIOD_SEC 25
-
-static void iwl_mvm_power_log(struct iwl_mvm *mvm,
- struct iwl_powertable_cmd *cmd)
-{
- IWL_DEBUG_POWER(mvm,
- "Sending power table command for power level %d, flags = 0x%X\n",
- iwlmvm_mod_params.power_scheme,
- le16_to_cpu(cmd->flags));
- IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds);
-
- if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
- IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
- le32_to_cpu(cmd->rx_data_timeout));
- IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
- le32_to_cpu(cmd->tx_data_timeout));
- if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
- IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
- le32_to_cpu(cmd->skip_dtim_periods));
- if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
- IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
- le32_to_cpu(cmd->lprx_rssi_threshold));
- }
-}
-
-static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_powertable_cmd *cmd)
-{
- struct ieee80211_hw *hw = mvm->hw;
- struct ieee80211_chanctx_conf *chanctx_conf;
- struct ieee80211_channel *chan;
- int dtimper, dtimper_msec;
- int keep_alive;
- bool radar_detect = false;
- struct iwl_mvm_vif *mvmvif __maybe_unused =
- iwl_mvm_vif_from_mac80211(vif);
-
- /*
- * Regardless of power management state the driver must set
- * keep alive period. FW will use it for sending keep alive NDPs
- * immediately after association.
- */
- cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
-
- if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
- return;
-
- cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
- if (!vif->bss_conf.assoc)
- cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
- mvmvif->dbgfs_pm.disable_power_off)
- cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
-#endif
- if (!vif->bss_conf.ps)
- return;
-
- cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
-
- if (vif->bss_conf.beacon_rate &&
- (vif->bss_conf.beacon_rate->bitrate == 10 ||
- vif->bss_conf.beacon_rate->bitrate == 60)) {
- cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
- cmd->lprx_rssi_threshold =
- cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD);
- }
-
- dtimper = hw->conf.ps_dtim_period ?: 1;
-
- /* Check if radar detection is required on current channel */
- rcu_read_lock();
- chanctx_conf = rcu_dereference(vif->chanctx_conf);
- WARN_ON(!chanctx_conf);
- if (chanctx_conf) {
- chan = chanctx_conf->def.chan;
- radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
- }
- rcu_read_unlock();
-
- /* Check skip over DTIM conditions */
- if (!radar_detect && (dtimper <= 10) &&
- (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
- mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
- cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
- cmd->skip_dtim_periods = cpu_to_le32(3);
- }
-
- /* Check that keep alive period is at least 3 * DTIM */
- dtimper_msec = dtimper * vif->bss_conf.beacon_int;
- keep_alive = max_t(int, 3 * dtimper_msec,
- MSEC_PER_SEC * cmd->keep_alive_seconds);
- keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
- cmd->keep_alive_seconds = keep_alive;
-
- if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
- cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
- cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
- } else {
- cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
- cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
- }
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
- cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
- if (mvmvif->dbgfs_pm.skip_over_dtim)
- cmd->flags |=
- cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
- else
- cmd->flags &=
- cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
- }
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
- cmd->rx_data_timeout =
- cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
- cmd->tx_data_timeout =
- cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
- cmd->skip_dtim_periods =
- cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
- if (mvmvif->dbgfs_pm.lprx_ena)
- cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
- else
- cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
- }
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
- cmd->lprx_rssi_threshold =
- cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold);
-#endif /* CONFIG_IWLWIFI_DEBUGFS */
-}
-
-static int iwl_mvm_power_legacy_update_mode(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- int ret;
- bool ba_enable;
- struct iwl_powertable_cmd cmd = {};
-
- if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
- return 0;
-
- /*
- * TODO: The following vif_count verification is temporary condition.
- * Avoid power mode update if more than one interface is currently
- * active. Remove this condition when FW will support power management
- * on multiple MACs.
- */
- IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n",
- mvm->vif_count);
- if (mvm->vif_count > 1)
- return 0;
-
- iwl_mvm_power_build_cmd(mvm, vif, &cmd);
- iwl_mvm_power_log(mvm, &cmd);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
- sizeof(cmd), &cmd);
- if (ret)
- return ret;
-
- ba_enable = !!(cmd.flags &
- cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
-
- return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
-}
-
-static int iwl_mvm_power_legacy_disable(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
-{
- struct iwl_powertable_cmd cmd = {};
- struct iwl_mvm_vif *mvmvif __maybe_unused =
- iwl_mvm_vif_from_mac80211(vif);
-
- if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
- return 0;
-
- if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
- cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
- mvmvif->dbgfs_pm.disable_power_off)
- cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
-#endif
- iwl_mvm_power_log(mvm, &cmd);
-
- return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
- sizeof(cmd), &cmd);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-static int iwl_mvm_power_legacy_dbgfs_read(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif, char *buf,
- int bufsz)
-{
- struct iwl_powertable_cmd cmd = {};
- int pos = 0;
-
- iwl_mvm_power_build_cmd(mvm, vif, &cmd);
-
- pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
- (cmd.flags &
- cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
- 0 : 1);
- pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
- le32_to_cpu(cmd.skip_dtim_periods));
- pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
- iwlmvm_mod_params.power_scheme);
- pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
- le16_to_cpu(cmd.flags));
- pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
- cmd.keep_alive_seconds);
-
- if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
- pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
- (cmd.flags &
- cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
- 1 : 0);
- pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
- le32_to_cpu(cmd.rx_data_timeout));
- pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
- le32_to_cpu(cmd.tx_data_timeout));
- if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
- pos += scnprintf(buf+pos, bufsz-pos,
- "lprx_rssi_threshold = %d\n",
- le32_to_cpu(cmd.lprx_rssi_threshold));
- }
- return pos;
-}
-#endif
-
-const struct iwl_mvm_power_ops pm_legacy_ops = {
- .power_update_mode = iwl_mvm_power_legacy_update_mode,
- .power_disable = iwl_mvm_power_legacy_disable,
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- .power_dbgfs_read = iwl_mvm_power_legacy_dbgfs_read,
-#endif
-};
diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c
index ce5db6c4ef7e..35e86e06dffd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/quota.c
+++ b/drivers/net/wireless/iwlwifi/mvm/quota.c
@@ -65,9 +65,14 @@
#include "fw-api.h"
#include "mvm.h"
+#define QUOTA_100 IWL_MVM_MAX_QUOTA
+#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100)
+
struct iwl_mvm_quota_iterator_data {
int n_interfaces[MAX_BINDINGS];
int colors[MAX_BINDINGS];
+ int low_latency[MAX_BINDINGS];
+ int n_low_latency_bindings;
struct ieee80211_vif *new_vif;
};
@@ -107,22 +112,29 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
switch (vif->type) {
case NL80211_IFTYPE_STATION:
if (vif->bss_conf.assoc)
- data->n_interfaces[id]++;
- break;
+ break;
+ return;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_ADHOC:
if (mvmvif->ap_ibss_active)
- data->n_interfaces[id]++;
- break;
+ break;
+ return;
case NL80211_IFTYPE_MONITOR:
if (mvmvif->monitor_active)
- data->n_interfaces[id]++;
- break;
+ break;
+ return;
case NL80211_IFTYPE_P2P_DEVICE:
- break;
+ return;
default:
WARN_ON_ONCE(1);
- break;
+ return;
+ }
+
+ data->n_interfaces[id]++;
+
+ if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
+ data->n_low_latency_bindings++;
+ data->low_latency[id] = true;
}
}
@@ -162,7 +174,7 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
{
struct iwl_time_quota_cmd cmd = {};
- int i, idx, ret, num_active_macs, quota, quota_rem;
+ int i, idx, ret, num_active_macs, quota, quota_rem, n_non_lowlat;
struct iwl_mvm_quota_iterator_data data = {
.n_interfaces = {},
.colors = { -1, -1, -1, -1 },
@@ -197,11 +209,39 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
num_active_macs += data.n_interfaces[i];
}
- quota = 0;
- quota_rem = 0;
- if (num_active_macs) {
- quota = IWL_MVM_MAX_QUOTA / num_active_macs;
- quota_rem = IWL_MVM_MAX_QUOTA % num_active_macs;
+ n_non_lowlat = num_active_macs;
+
+ if (data.n_low_latency_bindings == 1) {
+ for (i = 0; i < MAX_BINDINGS; i++) {
+ if (data.low_latency[i]) {
+ n_non_lowlat -= data.n_interfaces[i];
+ break;
+ }
+ }
+ }
+
+ if (data.n_low_latency_bindings == 1 && n_non_lowlat) {
+ /*
+ * Reserve quota for the low latency binding in case that
+ * there are several data bindings but only a single
+ * low latency one. Split the rest of the quota equally
+ * between the other data interfaces.
+ */
+ quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat;
+ quota_rem = QUOTA_100 - n_non_lowlat * quota -
+ QUOTA_LOWLAT_MIN;
+ } else if (num_active_macs) {
+ /*
+ * There are 0 or more than 1 low latency bindings, or all the
+ * data interfaces belong to the single low latency binding.
+ * Split the quota equally between the data interfaces.
+ */
+ quota = QUOTA_100 / num_active_macs;
+ quota_rem = QUOTA_100 % num_active_macs;
+ } else {
+ /* values don't really matter - won't be used */
+ quota = 0;
+ quota_rem = 0;
}
for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
@@ -211,19 +251,37 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
cmd.quotas[idx].id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
- if (data.n_interfaces[i] <= 0) {
+ if (data.n_interfaces[i] <= 0)
cmd.quotas[idx].quota = cpu_to_le32(0);
- cmd.quotas[idx].max_duration = cpu_to_le32(0);
- } else {
+ else if (data.n_low_latency_bindings == 1 && n_non_lowlat &&
+ data.low_latency[i])
+ /*
+ * There is more than one binding, but only one of the
+ * bindings is in low latency. For this case, allocate
+ * the minimal required quota for the low latency
+ * binding.
+ */
+ cmd.quotas[idx].quota = cpu_to_le32(QUOTA_LOWLAT_MIN);
+ else
cmd.quotas[idx].quota =
cpu_to_le32(quota * data.n_interfaces[i]);
- cmd.quotas[idx].max_duration = cpu_to_le32(0);
- }
+
+ WARN_ONCE(le32_to_cpu(cmd.quotas[idx].quota) > QUOTA_100,
+ "Binding=%d, quota=%u > max=%u\n",
+ idx, le32_to_cpu(cmd.quotas[idx].quota), QUOTA_100);
+
+ cmd.quotas[idx].max_duration = cpu_to_le32(0);
+
idx++;
}
- /* Give the remainder of the session to the first binding */
- le32_add_cpu(&cmd.quotas[0].quota, quota_rem);
+ /* Give the remainder of the session to the first data binding */
+ for (i = 0; i < MAX_BINDINGS; i++) {
+ if (le32_to_cpu(cmd.quotas[i].quota) != 0) {
+ le32_add_cpu(&cmd.quotas[i].quota, quota_rem);
+ break;
+ }
+ }
iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
index 6abf74e1351f..568abd61b14f 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.c
@@ -166,7 +166,7 @@ static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
if (sta->smps_mode == IEEE80211_SMPS_STATIC)
return false;
- if (num_of_ant(iwl_fw_valid_tx_ant(mvm->fw)) < 2)
+ if (num_of_ant(mvm->fw->valid_tx_ant) < 2)
return false;
if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
@@ -211,9 +211,9 @@ static const struct rs_tx_column rs_tx_columns[] = {
.next_columns = {
RS_COLUMN_LEGACY_ANT_B,
RS_COLUMN_SISO_ANT_A,
+ RS_COLUMN_SISO_ANT_B,
RS_COLUMN_MIMO2,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
+ RS_COLUMN_MIMO2_SGI,
},
},
[RS_COLUMN_LEGACY_ANT_B] = {
@@ -221,10 +221,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
.ant = ANT_B,
.next_columns = {
RS_COLUMN_LEGACY_ANT_A,
+ RS_COLUMN_SISO_ANT_A,
RS_COLUMN_SISO_ANT_B,
RS_COLUMN_MIMO2,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
+ RS_COLUMN_MIMO2_SGI,
},
},
[RS_COLUMN_SISO_ANT_A] = {
@@ -234,8 +234,8 @@ static const struct rs_tx_column rs_tx_columns[] = {
RS_COLUMN_SISO_ANT_B,
RS_COLUMN_MIMO2,
RS_COLUMN_SISO_ANT_A_SGI,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
+ RS_COLUMN_SISO_ANT_B_SGI,
+ RS_COLUMN_MIMO2_SGI,
},
.checks = {
rs_siso_allow,
@@ -248,8 +248,8 @@ static const struct rs_tx_column rs_tx_columns[] = {
RS_COLUMN_SISO_ANT_A,
RS_COLUMN_MIMO2,
RS_COLUMN_SISO_ANT_B_SGI,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
+ RS_COLUMN_SISO_ANT_A_SGI,
+ RS_COLUMN_MIMO2_SGI,
},
.checks = {
rs_siso_allow,
@@ -263,8 +263,8 @@ static const struct rs_tx_column rs_tx_columns[] = {
RS_COLUMN_SISO_ANT_B_SGI,
RS_COLUMN_MIMO2_SGI,
RS_COLUMN_SISO_ANT_A,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
+ RS_COLUMN_SISO_ANT_B,
+ RS_COLUMN_MIMO2,
},
.checks = {
rs_siso_allow,
@@ -279,8 +279,8 @@ static const struct rs_tx_column rs_tx_columns[] = {
RS_COLUMN_SISO_ANT_A_SGI,
RS_COLUMN_MIMO2_SGI,
RS_COLUMN_SISO_ANT_B,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
+ RS_COLUMN_SISO_ANT_A,
+ RS_COLUMN_MIMO2,
},
.checks = {
rs_siso_allow,
@@ -292,10 +292,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
.ant = ANT_AB,
.next_columns = {
RS_COLUMN_SISO_ANT_A,
+ RS_COLUMN_SISO_ANT_B,
+ RS_COLUMN_SISO_ANT_A_SGI,
+ RS_COLUMN_SISO_ANT_B_SGI,
RS_COLUMN_MIMO2_SGI,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
},
.checks = {
rs_mimo_allow,
@@ -307,10 +307,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
.sgi = true,
.next_columns = {
RS_COLUMN_SISO_ANT_A_SGI,
+ RS_COLUMN_SISO_ANT_B_SGI,
+ RS_COLUMN_SISO_ANT_A,
+ RS_COLUMN_SISO_ANT_B,
RS_COLUMN_MIMO2,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
- RS_COLUMN_INVALID,
},
.checks = {
rs_mimo_allow,
@@ -380,49 +380,49 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
* (2.4 GHz) band.
*/
-static s32 expected_tpt_legacy[IWL_RATE_COUNT] = {
+static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = {
7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0
};
/* Expected TpT tables. 4 indexes:
* 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI
*/
-static s32 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202, 216, 0},
{0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210, 225, 0},
{0, 0, 0, 0, 49, 0, 97, 145, 192, 285, 375, 420, 464, 551, 0},
{0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0},
};
-static s32 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257, 269, 275},
{0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264, 275, 280},
{0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828, 911, 1070, 1173},
{0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284},
};
-static s32 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 130, 0, 191, 223, 244, 273, 288, 294, 298, 305, 308},
{0, 0, 0, 0, 138, 0, 200, 231, 251, 279, 293, 298, 302, 308, 312},
{0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466},
{0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691},
};
-static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250, 261, 0},
{0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256, 267, 0},
{0, 0, 0, 0, 98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0},
{0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0},
};
-static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289, 296, 300},
{0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293, 300, 303},
{0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053},
{0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221},
};
-static s32 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 182, 0, 240, 264, 278, 299, 308, 311, 313, 317, 319},
{0, 0, 0, 0, 190, 0, 247, 269, 282, 302, 310, 313, 315, 319, 320},
{0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219},
@@ -503,6 +503,14 @@ static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window)
window->average_tpt = IWL_INVALID_VALUE;
}
+static void rs_rate_scale_clear_tbl_windows(struct iwl_scale_tbl_info *tbl)
+{
+ int i;
+
+ for (i = 0; i < IWL_RATE_COUNT; i++)
+ rs_rate_scale_clear_window(&tbl->win[i]);
+}
+
static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
{
return (ant_type & valid_antenna) == ant_type;
@@ -566,19 +574,13 @@ static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index)
* at this rate. window->data contains the bitmask of successful
* packets.
*/
-static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
- int scale_index, int attempts, int successes)
+static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
+ int scale_index, int attempts, int successes,
+ struct iwl_rate_scale_data *window)
{
- struct iwl_rate_scale_data *window = NULL;
static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1));
s32 fail_count, tpt;
- if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
- return -EINVAL;
-
- /* Select window for current tx bit rate */
- window = &(tbl->win[scale_index]);
-
/* Get expected throughput */
tpt = get_expected_tpt(tbl, scale_index);
@@ -636,6 +638,21 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
return 0;
}
+static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
+ int scale_index, int attempts, int successes)
+{
+ struct iwl_rate_scale_data *window = NULL;
+
+ if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
+ return -EINVAL;
+
+ /* Select window for current tx bit rate */
+ window = &(tbl->win[scale_index]);
+
+ return _rs_collect_tx_data(tbl, scale_index, attempts, successes,
+ window);
+}
+
/* Convert rs_rate object into ucode rate bitmask */
static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm,
struct rs_rate *rate)
@@ -905,7 +922,7 @@ static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta,
rate->bw = RATE_MCS_CHAN_WIDTH_20;
- WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX &&
+ WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX ||
rate->index > IWL_RATE_MCS_9_INDEX);
rate->index = rs_ht_to_legacy[rate->index];
@@ -917,7 +934,7 @@ static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta,
if (num_of_ant(rate->ant) > 1)
- rate->ant = first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
+ rate->ant = first_antenna(mvm->fw->valid_tx_ant);
/* Relevant in both switching to SISO or Legacy */
rate->sgi = false;
@@ -1169,12 +1186,12 @@ static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy,
lq_sta->visited_columns = 0;
}
-static s32 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta,
+static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta,
const struct rs_tx_column *column,
u32 bw)
{
/* Used to choose among HT tables */
- s32 (*ht_tbl_pointer)[IWL_RATE_COUNT];
+ const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT];
if (WARN_ON_ONCE(column->mode != RS_LEGACY &&
column->mode != RS_SISO &&
@@ -1262,9 +1279,8 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm,
&(lq_sta->lq_info[lq_sta->active_tbl]);
s32 active_sr = active_tbl->win[index].success_ratio;
s32 active_tpt = active_tbl->expected_tpt[index];
-
/* expected "search" throughput */
- s32 *tpt_tbl = tbl->expected_tpt;
+ const u16 *tpt_tbl = tbl->expected_tpt;
s32 new_rate, high, low, start_hi;
u16 high_low;
@@ -1362,7 +1378,6 @@ static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta)
static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
{
struct iwl_scale_tbl_info *tbl;
- int i;
int active_tbl;
int flush_interval_passed = 0;
struct iwl_mvm *mvm;
@@ -1423,9 +1438,7 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
IWL_DEBUG_RATE(mvm,
"LQ: stay in table clear win\n");
- for (i = 0; i < IWL_RATE_COUNT; i++)
- rs_rate_scale_clear_window(
- &(tbl->win[i]));
+ rs_rate_scale_clear_tbl_windows(tbl);
}
}
@@ -1434,8 +1447,7 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
* "search" table). */
if (lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED) {
IWL_DEBUG_RATE(mvm, "Clearing up window stats\n");
- for (i = 0; i < IWL_RATE_COUNT; i++)
- rs_rate_scale_clear_window(&(tbl->win[i]));
+ rs_rate_scale_clear_tbl_windows(tbl);
}
}
}
@@ -1478,8 +1490,8 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm,
const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column];
const struct rs_tx_column *next_col;
allow_column_func_t allow_func;
- u8 valid_ants = iwl_fw_valid_tx_ant(mvm->fw);
- s32 *expected_tpt_tbl;
+ u8 valid_ants = mvm->fw->valid_tx_ant;
+ const u16 *expected_tpt_tbl;
s32 tpt, max_expected_tpt;
for (i = 0; i < MAX_NEXT_COLUMNS; i++) {
@@ -1725,7 +1737,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
int low = IWL_RATE_INVALID;
int high = IWL_RATE_INVALID;
int index;
- int i;
struct iwl_rate_scale_data *window = NULL;
int current_tpt = IWL_INVALID_VALUE;
int low_tpt = IWL_INVALID_VALUE;
@@ -2010,8 +2021,7 @@ lq_update:
if (lq_sta->search_better_tbl) {
/* Access the "search" table, clear its history. */
tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
- for (i = 0; i < IWL_RATE_COUNT; i++)
- rs_rate_scale_clear_window(&(tbl->win[i]));
+ rs_rate_scale_clear_tbl_windows(tbl);
/* Use new "search" start rate */
index = tbl->rate.index;
@@ -2090,7 +2100,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
i = lq_sta->last_txrate_idx;
- valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
+ valid_tx_ant = mvm->fw->valid_tx_ant;
if (!lq_sta->search_better_tbl)
active_tbl = lq_sta->active_tbl;
@@ -2241,6 +2251,73 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
}
}
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm,
+ struct iwl_mvm_frame_stats *stats)
+{
+ spin_lock_bh(&mvm->drv_stats_lock);
+ memset(stats, 0, sizeof(*stats));
+ spin_unlock_bh(&mvm->drv_stats_lock);
+}
+
+void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm,
+ struct iwl_mvm_frame_stats *stats,
+ u32 rate, bool agg)
+{
+ u8 nss = 0, mcs = 0;
+
+ spin_lock(&mvm->drv_stats_lock);
+
+ if (agg)
+ stats->agg_frames++;
+
+ stats->success_frames++;
+
+ switch (rate & RATE_MCS_CHAN_WIDTH_MSK) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ stats->bw_20_frames++;
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ stats->bw_40_frames++;
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ stats->bw_80_frames++;
+ break;
+ default:
+ WARN_ONCE(1, "bad BW. rate 0x%x", rate);
+ }
+
+ if (rate & RATE_MCS_HT_MSK) {
+ stats->ht_frames++;
+ mcs = rate & RATE_HT_MCS_RATE_CODE_MSK;
+ nss = ((rate & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS) + 1;
+ } else if (rate & RATE_MCS_VHT_MSK) {
+ stats->vht_frames++;
+ mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK;
+ nss = ((rate & RATE_VHT_MCS_NSS_MSK) >>
+ RATE_VHT_MCS_NSS_POS) + 1;
+ } else {
+ stats->legacy_frames++;
+ }
+
+ if (nss == 1)
+ stats->siso_frames++;
+ else if (nss == 2)
+ stats->mimo2_frames++;
+
+ if (rate & RATE_MCS_SGI_MSK)
+ stats->sgi_frames++;
+ else
+ stats->ngi_frames++;
+
+ stats->last_rates[stats->last_frame_idx] = rate;
+ stats->last_frame_idx = (stats->last_frame_idx + 1) %
+ ARRAY_SIZE(stats->last_rates);
+
+ spin_unlock(&mvm->drv_stats_lock);
+}
+#endif
+
/*
* Called after adding a new station to initialize rate scaling
*/
@@ -2265,8 +2342,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
lq_sta->lq.sta_id = sta_priv->sta_id;
for (j = 0; j < LQ_SIZE; j++)
- for (i = 0; i < IWL_RATE_COUNT; i++)
- rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
+ rs_rate_scale_clear_tbl_windows(&lq_sta->lq_info[j]);
lq_sta->flush_timer = 0;
@@ -2320,7 +2396,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
/* These values will be overridden later */
lq_sta->lq.single_stream_ant_msk =
- first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
+ first_antenna(mvm->fw->valid_tx_ant);
lq_sta->lq.dual_stream_ant_msk = ANT_AB;
/* as default allow aggregation for all tids */
@@ -2335,7 +2411,9 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
#ifdef CONFIG_MAC80211_DEBUGFS
lq_sta->dbg_fixed_rate = 0;
#endif
-
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ iwl_mvm_reset_frame_stats(mvm, &mvm->drv_rx_stats);
+#endif
rs_initialize_lq(mvm, sta, lq_sta, band, init);
}
@@ -2446,7 +2524,7 @@ static void rs_build_rates_table(struct iwl_mvm *mvm,
memcpy(&rate, initial_rate, sizeof(rate));
- valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
+ valid_tx_ant = mvm->fw->valid_tx_ant;
if (is_siso(&rate)) {
num_rates = RS_INITIAL_SISO_NUM_RATES;
@@ -2523,7 +2601,7 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
if (sta)
lq_cmd->agg_time_limit =
- cpu_to_le16(iwl_mvm_bt_coex_agg_time_limit(mvm, sta));
+ cpu_to_le16(iwl_mvm_coex_agg_time_limit(mvm, sta));
}
static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
@@ -2547,7 +2625,7 @@ static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta,
}
#ifdef CONFIG_MAC80211_DEBUGFS
-static int rs_pretty_print_rate(char *buf, const u32 rate)
+int rs_pretty_print_rate(char *buf, const u32 rate)
{
char *type, *bw;
@@ -2596,7 +2674,7 @@ static int rs_pretty_print_rate(char *buf, const u32 rate)
return sprintf(buf, "%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s\n",
type, rs_pretty_ant(ant), bw, mcs, nss,
(rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ",
- (rate & RATE_MCS_STBC_MSK) ? "STBC " : "",
+ (rate & RATE_MCS_HT_STBC_MSK) ? "STBC " : "",
(rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "",
(rate & RATE_MCS_BF_MSK) ? "BF " : "",
(rate & RATE_MCS_ZLF_MSK) ? "ZLF " : "");
@@ -2677,9 +2755,9 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
desc += sprintf(buff+desc, "fixed rate 0x%X\n",
lq_sta->dbg_fixed_rate);
desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
- (iwl_fw_valid_tx_ant(mvm->fw) & ANT_A) ? "ANT_A," : "",
- (iwl_fw_valid_tx_ant(mvm->fw) & ANT_B) ? "ANT_B," : "",
- (iwl_fw_valid_tx_ant(mvm->fw) & ANT_C) ? "ANT_C" : "");
+ (mvm->fw->valid_tx_ant & ANT_A) ? "ANT_A," : "",
+ (mvm->fw->valid_tx_ant & ANT_B) ? "ANT_B," : "",
+ (mvm->fw->valid_tx_ant & ANT_C) ? "ANT_C" : "");
desc += sprintf(buff+desc, "lq type %s\n",
(is_legacy(rate)) ? "legacy" :
is_vht(rate) ? "VHT" : "HT");
@@ -2815,8 +2893,8 @@ static void rs_rate_init_stub(void *mvm_r,
struct ieee80211_sta *sta, void *mvm_sta)
{
}
-static struct rate_control_ops rs_mvm_ops = {
- .module = NULL,
+
+static const struct rate_control_ops rs_mvm_ops = {
.name = RS_NAME,
.tx_status = rs_tx_status,
.get_rate = rs_get_rate,
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h
index 7bc6404f6986..3332b396011e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.h
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.h
@@ -277,7 +277,7 @@ enum rs_column {
struct iwl_scale_tbl_info {
struct rs_rate rate;
enum rs_column column;
- s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
+ const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
};
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
index a85b60f7e67e..6061553a5e44 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -77,6 +77,15 @@ int iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info));
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
+
return 0;
}
@@ -129,22 +138,16 @@ static void iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
struct ieee80211_rx_status *rx_status)
{
int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm;
- int rssi_all_band_a, rssi_all_band_b;
- u32 agc_a, agc_b, max_agc;
+ u32 agc_a, agc_b;
u32 val;
val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]);
agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS;
agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS;
- max_agc = max_t(u32, agc_a, agc_b);
val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]);
rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS;
rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS;
- rssi_all_band_a = (val & IWL_OFDM_RSSI_ALLBAND_A_MSK) >>
- IWL_OFDM_RSSI_ALLBAND_A_POS;
- rssi_all_band_b = (val & IWL_OFDM_RSSI_ALLBAND_B_MSK) >>
- IWL_OFDM_RSSI_ALLBAND_B_POS;
/*
* dBm = rssi dB - agc dB - constant.
@@ -364,31 +367,43 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
rx_status.flag |= RX_FLAG_40MHZ;
break;
case RATE_MCS_CHAN_WIDTH_80:
- rx_status.flag |= RX_FLAG_80MHZ;
+ rx_status.vht_flag |= RX_VHT_FLAG_80MHZ;
break;
case RATE_MCS_CHAN_WIDTH_160:
- rx_status.flag |= RX_FLAG_160MHZ;
+ 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;
} else {
rx_status.rate_idx =
iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
rx_status.band);
}
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ iwl_mvm_update_frame_stats(mvm, &mvm->drv_rx_stats, rate_n_flags,
+ rx_status.flag & RX_FLAG_AMPDU_DETAILS);
+#endif
iwl_mvm_pass_packet_to_mac80211(mvm, hdr, len, ampdu_status,
rxb, &rx_status);
return 0;
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
index 742afc429c94..c91dc8498852 100644
--- a/drivers/net/wireless/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/mvm/scan.c
@@ -70,9 +70,16 @@
#define IWL_PLCP_QUIET_THRESH 1
#define IWL_ACTIVE_QUIET_TIME 10
-#define LONG_OUT_TIME_PERIOD 600
-#define SHORT_OUT_TIME_PERIOD 200
-#define SUSPEND_TIME_PERIOD 100
+
+struct iwl_mvm_scan_params {
+ u32 max_out_time;
+ u32 suspend_time;
+ bool passive_fragmented;
+ struct _dwell {
+ u16 passive;
+ u16 active;
+ } dwell[IEEE80211_NUM_BANDS];
+};
static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
{
@@ -82,7 +89,7 @@ static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
if (mvm->scan_rx_ant != ANT_NONE)
rx_ant = mvm->scan_rx_ant;
else
- rx_ant = iwl_fw_valid_rx_ant(mvm->fw);
+ rx_ant = mvm->fw->valid_rx_ant;
rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS;
@@ -90,24 +97,6 @@ static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
return cpu_to_le16(rx_chain);
}
-static inline __le32 iwl_mvm_scan_max_out_time(struct ieee80211_vif *vif,
- u32 flags, bool is_assoc)
-{
- if (!is_assoc)
- return 0;
- if (flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
- return cpu_to_le32(ieee80211_tu_to_usec(SHORT_OUT_TIME_PERIOD));
- return cpu_to_le32(ieee80211_tu_to_usec(LONG_OUT_TIME_PERIOD));
-}
-
-static inline __le32 iwl_mvm_scan_suspend_time(struct ieee80211_vif *vif,
- bool is_assoc)
-{
- if (!is_assoc)
- return 0;
- return cpu_to_le32(ieee80211_tu_to_usec(SUSPEND_TIME_PERIOD));
-}
-
static inline __le32
iwl_mvm_scan_rxon_flags(struct cfg80211_scan_request *req)
{
@@ -124,7 +113,7 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
u32 tx_ant;
mvm->scan_last_antenna_idx =
- iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw),
+ iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant,
mvm->scan_last_antenna_idx);
tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS;
@@ -181,15 +170,14 @@ static u16 iwl_mvm_get_passive_dwell(enum ieee80211_band band)
static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
struct cfg80211_scan_request *req,
- bool basic_ssid)
+ bool basic_ssid,
+ struct iwl_mvm_scan_params *params)
{
- u16 passive_dwell = iwl_mvm_get_passive_dwell(req->channels[0]->band);
- u16 active_dwell = iwl_mvm_get_active_dwell(req->channels[0]->band,
- req->n_ssids);
struct iwl_scan_channel *chan = (struct iwl_scan_channel *)
(cmd->data + le16_to_cpu(cmd->tx_cmd.len));
int i;
int type = BIT(req->n_ssids) - 1;
+ enum ieee80211_band band = req->channels[0]->band;
if (!basic_ssid)
type |= BIT(req->n_ssids);
@@ -199,8 +187,8 @@ static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
chan->type = cpu_to_le32(type);
if (req->channels[i]->flags & IEEE80211_CHAN_NO_IR)
chan->type &= cpu_to_le32(~SCAN_CHANNEL_TYPE_ACTIVE);
- chan->active_dwell = cpu_to_le16(active_dwell);
- chan->passive_dwell = cpu_to_le16(passive_dwell);
+ chan->active_dwell = cpu_to_le16(params->dwell[band].active);
+ chan->passive_dwell = cpu_to_le16(params->dwell[band].passive);
chan->iteration_count = cpu_to_le16(1);
chan++;
}
@@ -267,13 +255,76 @@ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
return (u16)len;
}
-static void iwl_mvm_vif_assoc_iterator(void *data, u8 *mac,
- struct ieee80211_vif *vif)
+static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
{
- bool *is_assoc = data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ bool *global_bound = data;
+
+ if (mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < MAX_PHYS)
+ *global_bound = true;
+}
+
+static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ int n_ssids,
+ struct iwl_mvm_scan_params *params)
+{
+ bool global_bound = false;
+ enum ieee80211_band band;
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_scan_condition_iterator,
+ &global_bound);
+ /*
+ * Under low latency traffic passive scan is fragmented meaning
+ * that dwell on a particular channel will be fragmented. Each fragment
+ * dwell time is 20ms and fragments period is 105ms. Skipping to next
+ * channel will be delayed by the same period - 105ms. So suspend_time
+ * parameter describing both fragments and channels skipping periods is
+ * set to 105ms. This value is chosen so that overall passive scan
+ * duration will not be too long. Max_out_time in this case is set to
+ * 70ms, so for active scanning operating channel will be left for 70ms
+ * while for passive still for 20ms (fragment dwell).
+ */
+ if (global_bound) {
+ if (!iwl_mvm_low_latency(mvm)) {
+ params->suspend_time = ieee80211_tu_to_usec(100);
+ params->max_out_time = ieee80211_tu_to_usec(600);
+ } else {
+ params->suspend_time = ieee80211_tu_to_usec(105);
+ /* P2P doesn't support fragmented passive scan, so
+ * configure max_out_time to be at least longest dwell
+ * time for passive scan.
+ */
+ if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {
+ params->max_out_time = ieee80211_tu_to_usec(70);
+ params->passive_fragmented = true;
+ } else {
+ u32 passive_dwell;
- if (vif->bss_conf.assoc)
- *is_assoc = true;
+ /*
+ * Use band G so that passive channel dwell time
+ * will be assigned with maximum value.
+ */
+ band = IEEE80211_BAND_2GHZ;
+ passive_dwell = iwl_mvm_get_passive_dwell(band);
+ params->max_out_time =
+ ieee80211_tu_to_usec(passive_dwell);
+ }
+ }
+ }
+
+ for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
+ if (params->passive_fragmented)
+ params->dwell[band].passive = 20;
+ else
+ params->dwell[band].passive =
+ iwl_mvm_get_passive_dwell(band);
+ params->dwell[band].active = iwl_mvm_get_active_dwell(band,
+ n_ssids);
+ }
}
int iwl_mvm_scan_request(struct iwl_mvm *mvm,
@@ -288,13 +339,13 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
.dataflags = { IWL_HCMD_DFL_NOCOPY, },
};
struct iwl_scan_cmd *cmd = mvm->scan_cmd;
- bool is_assoc = false;
int ret;
u32 status;
int ssid_len = 0;
u8 *ssid = NULL;
bool basic_ssid = !(mvm->fw->ucode_capa.flags &
IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID);
+ struct iwl_mvm_scan_params params = {};
lockdep_assert_held(&mvm->mutex);
BUG_ON(mvm->scan_cmd == NULL);
@@ -304,17 +355,18 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
memset(cmd, 0, sizeof(struct iwl_scan_cmd) +
mvm->fw->ucode_capa.max_probe_length +
(MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel)));
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_vif_assoc_iterator,
- &is_assoc);
+
cmd->channel_count = (u8)req->n_channels;
cmd->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME);
cmd->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH);
cmd->rxchain_sel_flags = iwl_mvm_scan_rx_chain(mvm);
- cmd->max_out_time = iwl_mvm_scan_max_out_time(vif, req->flags,
- is_assoc);
- cmd->suspend_time = iwl_mvm_scan_suspend_time(vif, is_assoc);
+
+ iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, &params);
+ cmd->max_out_time = cpu_to_le32(params.max_out_time);
+ cmd->suspend_time = cpu_to_le32(params.suspend_time);
+ if (params.passive_fragmented)
+ cmd->scan_flags |= SCAN_FLAGS_FRAGMENTED_SCAN;
+
cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req);
cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
MAC_FILTER_IN_BEACON);
@@ -360,7 +412,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
req->ie, req->ie_len,
mvm->fw->ucode_capa.max_probe_length));
- iwl_mvm_scan_fill_channels(cmd, req, basic_ssid);
+ iwl_mvm_scan_fill_channels(cmd, req, basic_ssid, &params);
cmd->len = cpu_to_le16(sizeof(struct iwl_scan_cmd) +
le16_to_cpu(cmd->tx_cmd.len) +
@@ -402,12 +454,17 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_scan_complete_notif *notif = (void *)pkt->data;
+ lockdep_assert_held(&mvm->mutex);
+
IWL_DEBUG_SCAN(mvm, "Scan complete: status=0x%x scanned channels=%d\n",
notif->status, notif->scanned_channels);
- mvm->scan_status = IWL_MVM_SCAN_NONE;
+ if (mvm->scan_status == IWL_MVM_SCAN_OS)
+ mvm->scan_status = IWL_MVM_SCAN_NONE;
ieee80211_scan_completed(mvm->hw, notif->status != SCAN_COMP_STATUS_OK);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+
return 0;
}
@@ -464,7 +521,7 @@ static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
};
}
-void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
+int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
{
struct iwl_notification_wait wait_scan_abort;
static const u8 scan_abort_notif[] = { SCAN_ABORT_CMD,
@@ -472,12 +529,13 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
int ret;
if (mvm->scan_status == IWL_MVM_SCAN_NONE)
- return;
+ return 0;
if (iwl_mvm_is_radio_killed(mvm)) {
ieee80211_scan_completed(mvm->hw, true);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
mvm->scan_status = IWL_MVM_SCAN_NONE;
- return;
+ return 0;
}
iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
@@ -488,18 +546,15 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL);
if (ret) {
IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
- /* mac80211's state will be cleaned in the fw_restart flow */
+ /* mac80211's state will be cleaned in the nic_restart flow */
goto out_remove_notif;
}
- ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_abort, 1 * HZ);
- if (ret)
- IWL_ERR(mvm, "%s - failed on timeout\n", __func__);
-
- return;
+ return iwl_wait_notification(&mvm->notif_wait, &wait_scan_abort, HZ);
out_remove_notif:
iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort);
+ return ret;
}
int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
@@ -509,12 +564,18 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data;
+ /* scan status must be locked for proper checking */
+ lockdep_assert_held(&mvm->mutex);
+
IWL_DEBUG_SCAN(mvm, "Scheduled scan completed, status %s\n",
scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
"completed" : "aborted");
- mvm->scan_status = IWL_MVM_SCAN_NONE;
- ieee80211_sched_scan_stopped(mvm->hw);
+ /* only call mac80211 completion if the stop was initiated by FW */
+ if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
+ mvm->scan_status = IWL_MVM_SCAN_NONE;
+ ieee80211_sched_scan_stopped(mvm->hw);
+ }
return 0;
}
@@ -545,14 +606,9 @@ static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
static void iwl_build_scan_cmd(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req,
- struct iwl_scan_offload_cmd *scan)
+ struct iwl_scan_offload_cmd *scan,
+ struct iwl_mvm_scan_params *params)
{
- bool is_assoc = false;
-
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_vif_assoc_iterator,
- &is_assoc);
scan->channel_count =
mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels +
mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
@@ -560,13 +616,17 @@ static void iwl_build_scan_cmd(struct iwl_mvm *mvm,
scan->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH);
scan->good_CRC_th = IWL_GOOD_CRC_TH_DEFAULT;
scan->rx_chain = iwl_mvm_scan_rx_chain(mvm);
- scan->max_out_time = iwl_mvm_scan_max_out_time(vif, req->flags,
- is_assoc);
- scan->suspend_time = iwl_mvm_scan_suspend_time(vif, is_assoc);
+
+ scan->max_out_time = cpu_to_le32(params->max_out_time);
+ scan->suspend_time = cpu_to_le32(params->suspend_time);
+
scan->filter_flags |= cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
MAC_FILTER_IN_BEACON);
scan->scan_type = cpu_to_le32(SCAN_TYPE_BACKGROUND);
scan->rep_count = cpu_to_le32(1);
+
+ if (params->passive_fragmented)
+ scan->scan_flags |= SCAN_FLAGS_FRAGMENTED_SCAN;
}
static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list)
@@ -596,6 +656,9 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
* config match list.
*/
for (i = 0; i < req->n_match_sets && i < PROBE_OPTION_MAX; i++) {
+ /* skip empty SSID matchsets */
+ if (!req->match_sets[i].ssid.ssid_len)
+ continue;
scan->direct_scan[i].id = WLAN_EID_SSID;
scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len;
memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid,
@@ -628,12 +691,11 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm,
struct iwl_scan_channel_cfg *channels,
enum ieee80211_band band,
int *head, int *tail,
- u32 ssid_bitmap)
+ u32 ssid_bitmap,
+ struct iwl_mvm_scan_params *params)
{
struct ieee80211_supported_band *s_band;
- int n_probes = req->n_ssids;
int n_channels = req->n_channels;
- u8 active_dwell, passive_dwell;
int i, j, index = 0;
bool partial;
@@ -643,8 +705,6 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm,
* to scan. So add requested channels to head of the list and others to
* the end.
*/
- active_dwell = iwl_mvm_get_active_dwell(band, n_probes);
- passive_dwell = iwl_mvm_get_passive_dwell(band);
s_band = &mvm->nvm_data->bands[band];
for (i = 0; i < s_band->n_channels && *head <= *tail; i++) {
@@ -668,8 +728,8 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm,
channels->channel_number[index] =
cpu_to_le16(ieee80211_frequency_to_channel(
s_band->channels[i].center_freq));
- channels->dwell_time[index][0] = active_dwell;
- channels->dwell_time[index][1] = passive_dwell;
+ channels->dwell_time[index][0] = params->dwell[band].active;
+ channels->dwell_time[index][1] = params->dwell[band].passive;
channels->iter_count[index] = cpu_to_le16(1);
channels->iter_interval[index] = 0;
@@ -698,7 +758,6 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies)
{
- int supported_bands = 0;
int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels;
int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
int head = 0;
@@ -712,22 +771,19 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
.id = SCAN_OFFLOAD_CONFIG_CMD,
.flags = CMD_SYNC,
};
+ struct iwl_mvm_scan_params params = {};
lockdep_assert_held(&mvm->mutex);
- if (band_2ghz)
- supported_bands++;
- if (band_5ghz)
- supported_bands++;
-
cmd_len = sizeof(struct iwl_scan_offload_cfg) +
- supported_bands * SCAN_OFFLOAD_PROBE_REQ_SIZE;
+ 2 * SCAN_OFFLOAD_PROBE_REQ_SIZE;
scan_cfg = kzalloc(cmd_len, GFP_KERNEL);
if (!scan_cfg)
return -ENOMEM;
- iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd);
+ iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, &params);
+ iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd, &params);
scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len);
iwl_scan_offload_build_ssid(req, &scan_cfg->scan_cmd, &ssid_bitmap);
@@ -739,7 +795,7 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
scan_cfg->data);
iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg,
IEEE80211_BAND_2GHZ, &head, &tail,
- ssid_bitmap);
+ ssid_bitmap, &params);
}
if (band_5ghz) {
iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
@@ -749,7 +805,7 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
SCAN_OFFLOAD_PROBE_REQ_SIZE);
iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg,
IEEE80211_BAND_5GHZ, &head, &tail,
- ssid_bitmap);
+ ssid_bitmap, &params);
}
cmd.data[0] = scan_cfg;
@@ -889,26 +945,49 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
* microcode has notified us that a scan is completed.
*/
IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status);
- ret = -EIO;
+ ret = -ENOENT;
}
return ret;
}
-void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm)
+int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm)
{
int ret;
+ struct iwl_notification_wait wait_scan_done;
+ static const u8 scan_done_notif[] = { SCAN_OFFLOAD_COMPLETE, };
lockdep_assert_held(&mvm->mutex);
if (mvm->scan_status != IWL_MVM_SCAN_SCHED) {
IWL_DEBUG_SCAN(mvm, "No offloaded scan to stop\n");
- return;
+ return 0;
}
+ iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done,
+ scan_done_notif,
+ ARRAY_SIZE(scan_done_notif),
+ NULL, NULL);
+
ret = iwl_mvm_send_sched_scan_abort(mvm);
- if (ret)
+ if (ret) {
IWL_DEBUG_SCAN(mvm, "Send stop offload scan failed %d\n", ret);
- else
- IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n");
+ iwl_remove_notification(&mvm->notif_wait, &wait_scan_done);
+ return ret;
+ }
+
+ IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n");
+
+ ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ);
+ if (ret)
+ return ret;
+
+ /*
+ * Clear the scan status so the next scan requests will succeed. This
+ * also ensures the Rx handler doesn't do anything, as the scan was
+ * stopped from above.
+ */
+ mvm->scan_status = IWL_MVM_SCAN_NONE;
+
+ return 0;
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index 3397f59cd4e4..f339ef884250 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -66,27 +66,27 @@
#include "sta.h"
#include "rs.h"
-static void iwl_mvm_add_sta_cmd_v6_to_v5(struct iwl_mvm_add_sta_cmd_v6 *cmd_v6,
+static void iwl_mvm_add_sta_cmd_v7_to_v5(struct iwl_mvm_add_sta_cmd_v7 *cmd_v7,
struct iwl_mvm_add_sta_cmd_v5 *cmd_v5)
{
memset(cmd_v5, 0, sizeof(*cmd_v5));
- cmd_v5->add_modify = cmd_v6->add_modify;
- cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx;
- cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color;
- memcpy(cmd_v5->addr, cmd_v6->addr, ETH_ALEN);
- cmd_v5->sta_id = cmd_v6->sta_id;
- cmd_v5->modify_mask = cmd_v6->modify_mask;
- cmd_v5->station_flags = cmd_v6->station_flags;
- cmd_v5->station_flags_msk = cmd_v6->station_flags_msk;
- cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid;
- cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid;
- cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn;
- cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count;
- cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags;
- cmd_v5->assoc_id = cmd_v6->assoc_id;
- cmd_v5->beamform_flags = cmd_v6->beamform_flags;
- cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk;
+ cmd_v5->add_modify = cmd_v7->add_modify;
+ cmd_v5->tid_disable_tx = cmd_v7->tid_disable_tx;
+ cmd_v5->mac_id_n_color = cmd_v7->mac_id_n_color;
+ memcpy(cmd_v5->addr, cmd_v7->addr, ETH_ALEN);
+ cmd_v5->sta_id = cmd_v7->sta_id;
+ cmd_v5->modify_mask = cmd_v7->modify_mask;
+ cmd_v5->station_flags = cmd_v7->station_flags;
+ cmd_v5->station_flags_msk = cmd_v7->station_flags_msk;
+ cmd_v5->add_immediate_ba_tid = cmd_v7->add_immediate_ba_tid;
+ cmd_v5->remove_immediate_ba_tid = cmd_v7->remove_immediate_ba_tid;
+ cmd_v5->add_immediate_ba_ssn = cmd_v7->add_immediate_ba_ssn;
+ cmd_v5->sleep_tx_count = cmd_v7->sleep_tx_count;
+ cmd_v5->sleep_state_flags = cmd_v7->sleep_state_flags;
+ cmd_v5->assoc_id = cmd_v7->assoc_id;
+ cmd_v5->beamform_flags = cmd_v7->beamform_flags;
+ cmd_v5->tfd_queue_msk = cmd_v7->tfd_queue_msk;
}
static void
@@ -110,7 +110,7 @@ iwl_mvm_add_sta_key_to_add_sta_cmd_v5(struct iwl_mvm_add_sta_key_cmd *key_cmd,
}
static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm,
- struct iwl_mvm_add_sta_cmd_v6 *cmd,
+ struct iwl_mvm_add_sta_cmd_v7 *cmd,
int *status)
{
struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
@@ -119,14 +119,14 @@ static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm,
return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(*cmd),
cmd, status);
- iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
+ iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5);
return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd_v5),
&cmd_v5, status);
}
static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags,
- struct iwl_mvm_add_sta_cmd_v6 *cmd)
+ struct iwl_mvm_add_sta_cmd_v7 *cmd)
{
struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
@@ -134,7 +134,7 @@ static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags,
return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags,
sizeof(*cmd), cmd);
- iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
+ iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5);
return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(cmd_v5),
&cmd_v5);
@@ -175,19 +175,30 @@ static int iwl_mvm_send_add_sta_key_cmd(struct iwl_mvm *mvm,
&sta_cmd);
}
-static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm)
+static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm,
+ enum nl80211_iftype iftype)
{
int sta_id;
+ u32 reserved_ids = 0;
+ BUILD_BUG_ON(IWL_MVM_STATION_COUNT > 32);
WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
lockdep_assert_held(&mvm->mutex);
+ /* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */
+ if (iftype != NL80211_IFTYPE_STATION)
+ reserved_ids = BIT(0);
+
/* Don't take rcu_read_lock() since we are protected by mvm->mutex */
- for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++)
+ for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) {
+ if (BIT(sta_id) & reserved_ids)
+ continue;
+
if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
lockdep_is_held(&mvm->mutex)))
return sta_id;
+ }
return IWL_MVM_STATION_COUNT;
}
@@ -196,7 +207,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
bool update)
{
struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
- struct iwl_mvm_add_sta_cmd_v6 add_sta_cmd;
+ struct iwl_mvm_add_sta_cmd_v7 add_sta_cmd;
int ret;
u32 status;
u32 agg_size = 0, mpdu_dens = 0;
@@ -312,7 +323,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
lockdep_assert_held(&mvm->mutex);
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- sta_id = iwl_mvm_find_free_sta_id(mvm);
+ sta_id = iwl_mvm_find_free_sta_id(mvm,
+ ieee80211_vif_type_p2p(vif));
else
sta_id = mvm_sta->sta_id;
@@ -368,7 +380,7 @@ int iwl_mvm_update_sta(struct iwl_mvm *mvm,
int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool drain)
{
- struct iwl_mvm_add_sta_cmd_v6 cmd = {};
+ struct iwl_mvm_add_sta_cmd_v7 cmd = {};
int ret;
u32 status;
@@ -522,6 +534,10 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
/* unassoc - go ahead - remove the AP STA now */
mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+
+ /* clear d0i3_ap_sta_id if no longer relevant */
+ if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id)
+ mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
}
/*
@@ -560,10 +576,10 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
}
int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
- u32 qmask)
+ 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);
+ sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype);
if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT))
return -ENOSPC;
}
@@ -587,13 +603,13 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
const u8 *addr,
u16 mac_id, u16 color)
{
- struct iwl_mvm_add_sta_cmd_v6 cmd;
+ struct iwl_mvm_add_sta_cmd_v7 cmd;
int ret;
u32 status;
lockdep_assert_held(&mvm->mutex);
- memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v6));
+ memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v7));
cmd.sta_id = sta->sta_id;
cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
color));
@@ -627,7 +643,8 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
lockdep_assert_held(&mvm->mutex);
/* Add the aux station, but without any queues */
- ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0);
+ ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0,
+ NL80211_IFTYPE_UNSPECIFIED);
if (ret)
return ret;
@@ -699,7 +716,8 @@ int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
lockdep_assert_held(&mvm->mutex);
qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
- ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask);
+ ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask,
+ ieee80211_vif_type_p2p(vif));
if (ret)
return ret;
@@ -735,7 +753,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int tid, u16 ssn, bool start)
{
struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
- struct iwl_mvm_add_sta_cmd_v6 cmd = {};
+ struct iwl_mvm_add_sta_cmd_v7 cmd = {};
int ret;
u32 status;
@@ -794,7 +812,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int tid, u8 queue, bool start)
{
struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
- struct iwl_mvm_add_sta_cmd_v6 cmd = {};
+ struct iwl_mvm_add_sta_cmd_v7 cmd = {};
int ret;
u32 status;
@@ -833,7 +851,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
return ret;
}
-static const u8 tid_to_ac[] = {
+const u8 tid_to_mac80211_ac[] = {
IEEE80211_AC_BE,
IEEE80211_AC_BK,
IEEE80211_AC_BK,
@@ -844,6 +862,17 @@ static const u8 tid_to_ac[] = {
IEEE80211_AC_VO,
};
+static const u8 tid_to_ucode_ac[] = {
+ AC_BE,
+ AC_BK,
+ AC_BK,
+ AC_BE,
+ AC_VI,
+ AC_VI,
+ AC_VO,
+ AC_VO,
+};
+
int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u16 *ssn)
{
@@ -873,10 +902,18 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
return -EIO;
}
+ spin_lock_bh(&mvmsta->lock);
+
+ /* possible race condition - we entered D0i3 while starting agg */
+ if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) {
+ spin_unlock_bh(&mvmsta->lock);
+ IWL_ERR(mvm, "Entered D0i3 while starting Tx agg\n");
+ return -EIO;
+ }
+
/* the new tx queue is still connected to the same mac80211 queue */
- mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_ac[tid]];
+ mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_mac80211_ac[tid]];
- spin_lock_bh(&mvmsta->lock);
tid_data = &mvmsta->tid_data[tid];
tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
tid_data->txq_id = txq_id;
@@ -916,7 +953,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
tid_data->ssn = 0xffff;
spin_unlock_bh(&mvmsta->lock);
- fifo = iwl_mvm_ac_to_tx_fifo[tid_to_ac[tid]];
+ fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true);
if (ret)
@@ -1411,7 +1448,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
struct ieee80211_sta *sta)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_add_sta_cmd_v6 cmd = {
+ struct iwl_mvm_add_sta_cmd_v7 cmd = {
.add_modify = STA_MODE_MODIFY,
.sta_id = mvmsta->sta_id,
.station_flags_msk = cpu_to_le32(STA_FLG_PS),
@@ -1427,28 +1464,102 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
enum ieee80211_frame_release_type reason,
- u16 cnt)
+ u16 cnt, u16 tids, bool more_data,
+ bool agg)
{
- u16 sleep_state_flags =
- (reason == IEEE80211_FRAME_RELEASE_UAPSD) ?
- STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL;
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_add_sta_cmd_v6 cmd = {
+ struct iwl_mvm_add_sta_cmd_v7 cmd = {
.add_modify = STA_MODE_MODIFY,
.sta_id = mvmsta->sta_id,
.modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
.sleep_tx_count = cpu_to_le16(cnt),
.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
- /*
- * Same modify mask for sleep_tx_count and sleep_state_flags so
- * we must set the sleep_state_flags too.
- */
- .sleep_state_flags = cpu_to_le16(sleep_state_flags),
};
- int ret;
+ int tid, ret;
+ unsigned long _tids = tids;
+
+ /* convert TIDs to ACs - we don't support TSPEC so that's OK
+ * Note that this field is reserved and unused by firmware not
+ * supporting GO uAPSD, so it's safe to always do this.
+ */
+ for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT)
+ cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]);
+
+ /* If we're releasing frames from aggregation queues then check if the
+ * all queues combined that we're releasing frames from have
+ * - more frames than the service period, in which case more_data
+ * needs to be set
+ * - fewer than 'cnt' frames, in which case we need to adjust the
+ * firmware command (but do that unconditionally)
+ */
+ if (agg) {
+ int remaining = cnt;
+
+ spin_lock_bh(&mvmsta->lock);
+ for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) {
+ struct iwl_mvm_tid_data *tid_data;
+ u16 n_queued;
+
+ tid_data = &mvmsta->tid_data[tid];
+ if (WARN(tid_data->state != IWL_AGG_ON &&
+ tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA,
+ "TID %d state is %d\n",
+ tid, tid_data->state)) {
+ spin_unlock_bh(&mvmsta->lock);
+ ieee80211_sta_eosp(sta);
+ return;
+ }
+
+ n_queued = iwl_mvm_tid_queued(tid_data);
+ if (n_queued > remaining) {
+ more_data = true;
+ remaining = 0;
+ break;
+ }
+ remaining -= n_queued;
+ }
+ spin_unlock_bh(&mvmsta->lock);
+
+ cmd.sleep_tx_count = cpu_to_le16(cnt - remaining);
+ if (WARN_ON(cnt - remaining == 0)) {
+ ieee80211_sta_eosp(sta);
+ return;
+ }
+ }
+
+ /* Note: this is ignored by firmware not supporting GO uAPSD */
+ if (more_data)
+ cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA);
+
+ if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) {
+ mvmsta->next_status_eosp = true;
+ cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL);
+ } else {
+ cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD);
+ }
- /* TODO: somehow the fw doesn't seem to take PS_POLL into account */
ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
if (ret)
IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
}
+
+int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mvm_eosp_notification *notif = (void *)pkt->data;
+ struct ieee80211_sta *sta;
+ u32 sta_id = le32_to_cpu(notif->sta_id);
+
+ if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT))
+ return 0;
+
+ rcu_read_lock();
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+ if (!IS_ERR_OR_NULL(sta))
+ ieee80211_sta_eosp(sta);
+ rcu_read_unlock();
+
+ return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
index 4968d0237dc5..2ed84c421481 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -195,24 +195,33 @@ struct iwl_mvm;
/**
* DOC: AP mode - PS
*
- * When a station is asleep, the fw will set it as "asleep". All the
- * non-aggregation frames to that station will be dropped by the fw
- * (%TX_STATUS_FAIL_DEST_PS failure code).
+ * When a station is asleep, the fw will set it as "asleep". All frames on
+ * shared queues (i.e. non-aggregation queues) to that station will be dropped
+ * by the fw (%TX_STATUS_FAIL_DEST_PS failure code).
+ *
* AMPDUs are in a separate queue that is stopped by the fw. We just need to
- * let mac80211 know how many frames we have in these queues so that it can
+ * let mac80211 know when there are frames in these queues so that it can
* properly handle trigger frames.
- * When the a trigger frame is received, mac80211 tells the driver to send
- * frames from the AMPDU queues or AC queue depending on which queue are
- * delivery-enabled and what TID has frames to transmit (Note that mac80211 has
- * all the knowledege since all the non-agg frames are buffered / filtered, and
- * the driver tells mac80211 about agg frames). The driver needs to tell the fw
- * to let frames out even if the station is asleep. This is done by
- * %iwl_mvm_sta_modify_sleep_tx_count.
- * When we receive a frame from that station with PM bit unset, the
- * driver needs to let the fw know that this station isn't alseep any more.
- * This is done by %iwl_mvm_sta_modify_ps_wake.
- *
- * TODO - EOSP handling
+ *
+ * When a trigger frame is received, mac80211 tells the driver to send frames
+ * from the AMPDU queues or sends frames to non-aggregation queues itself,
+ * depending on which ACs are delivery-enabled and what TID has frames to
+ * transmit. Note that mac80211 has all the knowledege since all the non-agg
+ * frames are buffered / filtered, and the driver tells mac80211 about agg
+ * frames). The driver needs to tell the fw to let frames out even if the
+ * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count.
+ *
+ * When we receive a frame from that station with PM bit unset, the driver
+ * needs to let the fw know that this station isn't asleep any more. This is
+ * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signalling the
+ * station's wakeup.
+ *
+ * For a GO, the Service Period might be cut short due to an absence period
+ * of the GO. In this (and all other cases) the firmware notifies us with the
+ * EOSP_NOTIFICATION, and we notify mac80211 of that. Further frames that we
+ * already sent to the device will be rejected again.
+ *
+ * See also "AP support for powersaving clients" in mac80211.h.
*/
/**
@@ -261,6 +270,12 @@ struct iwl_mvm_tid_data {
u16 ssn;
};
+static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
+{
+ return ieee80211_sn_sub(IEEE80211_SEQ_TO_SN(tid_data->seq_number),
+ tid_data->next_reclaimed);
+}
+
/**
* 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)
@@ -269,7 +284,11 @@ struct iwl_mvm_tid_data {
* @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
* tid.
* @max_agg_bufsize: the maximal size of the AGG buffer for this station
+ * @bt_reduced_txpower_dbg: debug mode in which %bt_reduced_txpower is forced
+ * by debugfs.
* @bt_reduced_txpower: is reduced tx power enabled for this station
+ * @next_status_eosp: the next reclaimed packet is a PS-Poll response and
+ * we need to signal the EOSP
* @lock: lock to protect the whole struct. Since %tid_data is access from Tx
* and from Tx response flow, it needs a spinlock.
* @tid_data: per tid data. Look at %iwl_mvm_tid_data.
@@ -287,7 +306,9 @@ struct iwl_mvm_sta {
u32 mac_id_n_color;
u16 tid_disable_agg;
u8 max_agg_bufsize;
+ bool bt_reduced_txpower_dbg;
bool bt_reduced_txpower;
+ bool next_status_eosp;
spinlock_t lock;
struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT];
struct iwl_lq_sta lq_sta;
@@ -345,6 +366,10 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
struct ieee80211_sta *sta, u32 iv32,
u16 *phase1key);
+int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
+
/* AMPDU */
int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int tid, u16 ssn, bool start);
@@ -359,7 +384,7 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm);
int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
- u32 qmask);
+ u32 qmask, enum nl80211_iftype iftype);
void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm,
struct iwl_mvm_int_sta *sta);
int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@@ -375,7 +400,8 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
enum ieee80211_frame_release_type reason,
- u16 cnt);
+ u16 cnt, u16 tids, bool more_data,
+ bool agg);
int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool drain);
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
index b4c2abaa297b..61331245ad93 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c
@@ -126,6 +126,7 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
* in iwl_mvm_te_handle_notif).
*/
clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
/*
* Of course, our status bit is just as racy as mac80211, so in
@@ -210,6 +211,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
+ iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
ieee80211_ready_on_channel(mvm->hw);
}
} else {
@@ -436,7 +438,8 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,
time_cmd.duration = cpu_to_le32(duration);
time_cmd.repeat = 1;
time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
- TE_V2_NOTIF_HOST_EVENT_END);
+ TE_V2_NOTIF_HOST_EVENT_END |
+ T2_V2_START_IMMEDIATELY);
iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
}
@@ -551,7 +554,8 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
time_cmd.repeat = 1;
time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
- TE_V2_NOTIF_HOST_EVENT_END);
+ TE_V2_NOTIF_HOST_EVENT_END |
+ T2_V2_START_IMMEDIATELY);
return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c
index 3afa6b6bf835..7a99fa361954 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tt.c
@@ -403,7 +403,7 @@ static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
}
}
-static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
+void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
{
struct iwl_host_cmd cmd = {
.id = REPLY_THERMAL_MNG_BACKOFF,
@@ -412,6 +412,8 @@ static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
.flags = CMD_SYNC,
};
+ backoff = max(backoff, mvm->thermal_throttle.min_backoff);
+
if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
backoff);
@@ -534,7 +536,7 @@ static const struct iwl_tt_params iwl7000_high_temp_tt_params = {
.support_tx_backoff = true,
};
-void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
+void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff)
{
struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
@@ -546,6 +548,7 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
tt->params = &iwl7000_tt_params;
tt->throttle = false;
+ tt->min_backoff = min_backoff;
INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
index 76ee486039d7..879aeac46cc1 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -79,6 +79,7 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
__le16 fc = hdr->frame_control;
u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags);
u32 len = skb->len + FCS_LEN;
+ u8 ac;
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
tx_flags |= TX_CMD_FLG_ACK;
@@ -90,13 +91,6 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
else if (ieee80211_is_back_req(fc))
tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR;
- /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */
- if (info->band == IEEE80211_BAND_2GHZ &&
- (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO ||
- is_multicast_ether_addr(hdr->addr1) ||
- ieee80211_is_back_req(fc) || ieee80211_is_mgmt(fc)))
- tx_flags |= TX_CMD_FLG_BT_DIS;
-
if (ieee80211_has_morefrags(fc))
tx_flags |= TX_CMD_FLG_MORE_FRAG;
@@ -112,6 +106,11 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
}
+ /* tid_tspec will default to 0 = BE when QOS isn't enabled */
+ ac = tid_to_mac80211_ac[tx_cmd->tid_tspec];
+ tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, hdr, info, ac) <<
+ TX_CMD_FLG_BT_PRIO_POS;
+
if (ieee80211_is_mgmt(fc)) {
if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
tx_cmd->pm_frame_timeout = cpu_to_le16(3);
@@ -122,15 +121,12 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
* it
*/
WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU);
- } else if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
+ } else if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) {
tx_cmd->pm_frame_timeout = cpu_to_le16(2);
} else {
tx_cmd->pm_frame_timeout = 0;
}
- if (info->flags & IEEE80211_TX_CTL_AMPDU)
- tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
-
if (ieee80211_is_data(fc) && len > mvm->rts_threshold &&
!is_multicast_ether_addr(ieee80211_get_DA(hdr)))
tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
@@ -207,7 +203,7 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx);
mvm->mgmt_last_antenna_idx =
- iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw),
+ iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant,
mvm->mgmt_last_antenna_idx);
rate_flags = BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
@@ -377,6 +373,13 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
/* From now on, we cannot access info->control */
+ /*
+ * we handle that entirely ourselves -- for uAPSD the firmware
+ * will always send a notification, and for PS-Poll responses
+ * we'll notify mac80211 when getting frame status
+ */
+ info->flags &= ~IEEE80211_TX_STATUS_EOSP;
+
spin_lock(&mvmsta->lock);
if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
@@ -437,6 +440,17 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
lockdep_assert_held(&mvmsta->lock);
+ if ((tid_data->state == IWL_AGG_ON ||
+ tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) &&
+ iwl_mvm_tid_queued(tid_data) == 0) {
+ /*
+ * Now that this aggregation queue is empty tell mac80211 so it
+ * knows we no longer have frames buffered for the station on
+ * this TID (for the TIM bitmap calculation.)
+ */
+ ieee80211_sta_set_buffered(sta, tid, false);
+ }
+
if (tid_data->ssn != tid_data->next_reclaimed)
return;
@@ -680,6 +694,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
iwl_mvm_check_ratid_empty(mvm, sta, tid);
spin_unlock_bh(&mvmsta->lock);
}
+
+ if (mvmsta->next_status_eosp) {
+ mvmsta->next_status_eosp = false;
+ ieee80211_sta_eosp(sta);
+ }
} else {
mvmsta = NULL;
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
index 86989df69356..d619851745a1 100644
--- a/drivers/net/wireless/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/iwlwifi/mvm/utils.c
@@ -289,8 +289,8 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
return last_idx;
}
-static struct {
- char *name;
+static const struct {
+ const char *name;
u8 num;
} advanced_lookup[] = {
{ "NMI_INTERRUPT_WDG", 0x34 },
@@ -376,9 +376,67 @@ struct iwl_error_event_table {
u32 flow_handler; /* FH read/write pointers, RX credit */
} __packed;
+/*
+ * UMAC error struct - relevant starting from family 8000 chip.
+ * Note: This structure is read from the device with IO accesses,
+ * and the reading already does the endian conversion. As it is
+ * read with u32-sized accesses, any members with a different size
+ * need to be ordered correctly though!
+ */
+struct iwl_umac_error_event_table {
+ u32 valid; /* (nonzero) valid, (0) log is empty */
+ u32 error_id; /* type of error */
+ u32 pc; /* program counter */
+ u32 blink1; /* branch link */
+ u32 blink2; /* branch link */
+ u32 ilink1; /* interrupt link */
+ u32 ilink2; /* interrupt link */
+ u32 data1; /* error-specific data */
+ u32 data2; /* error-specific data */
+ u32 line; /* source code line of error */
+ u32 umac_ver; /* umac version */
+} __packed;
+
#define ERROR_START_OFFSET (1 * sizeof(u32))
#define ERROR_ELEM_SIZE (7 * sizeof(u32))
+static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm)
+{
+ struct iwl_trans *trans = mvm->trans;
+ struct iwl_umac_error_event_table table;
+ u32 base;
+
+ base = mvm->umac_error_event_table;
+
+ if (base < 0x800000 || base >= 0x80C000) {
+ IWL_ERR(mvm,
+ "Not valid error log pointer 0x%08X for %s uCode\n",
+ base,
+ (mvm->cur_ucode == IWL_UCODE_INIT)
+ ? "Init" : "RT");
+ return;
+ }
+
+ iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
+
+ if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
+ IWL_ERR(trans, "Start IWL Error Log Dump:\n");
+ IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
+ mvm->status, table.valid);
+ }
+
+ IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
+ desc_lookup(table.error_id));
+ IWL_ERR(mvm, "0x%08X | umac uPc\n", table.pc);
+ IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1);
+ IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2);
+ IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1);
+ IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2);
+ IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1);
+ IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2);
+ IWL_ERR(mvm, "0x%08X | umac version\n", table.umac_ver);
+}
+
void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
{
struct iwl_trans *trans = mvm->trans;
@@ -394,7 +452,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
base = mvm->fw->inst_errlog_ptr;
}
- if (base < 0x800000 || base >= 0x80C000) {
+ if (base < 0x800000) {
IWL_ERR(mvm,
"Not valid error log pointer 0x%08X for %s uCode\n",
base,
@@ -453,29 +511,31 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
+
+ if (mvm->support_umac_log)
+ iwl_mvm_dump_umac_error_log(mvm);
}
-void iwl_mvm_dump_sram(struct iwl_mvm *mvm)
+void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)
{
const struct fw_img *img;
- int ofs, len = 0;
- u8 *buf;
+ u32 ofs, sram_len;
+ void *sram;
- if (!mvm->ucode_loaded)
+ if (!mvm->ucode_loaded || mvm->fw_error_sram)
return;
img = &mvm->fw->img[mvm->cur_ucode];
ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
- len = img->sec[IWL_UCODE_SECTION_DATA].len;
+ sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
- buf = kzalloc(len, GFP_ATOMIC);
- if (!buf)
+ sram = kzalloc(sram_len, GFP_ATOMIC);
+ if (!sram)
return;
- iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len);
- iwl_print_hex_error(mvm->trans, buf, len);
-
- kfree(buf);
+ iwl_trans_read_mem_bytes(mvm->trans, ofs, sram, sram_len);
+ mvm->fw_error_sram = sram;
+ mvm->fw_error_sram_len = sram_len;
}
/**
@@ -516,15 +576,20 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_smps_mode smps_request)
{
struct iwl_mvm_vif *mvmvif;
- enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC;
+ enum ieee80211_smps_mode smps_mode;
int i;
lockdep_assert_held(&mvm->mutex);
/* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */
- if (num_of_ant(iwl_fw_valid_rx_ant(mvm->fw)) == 1)
+ if (num_of_ant(mvm->fw->valid_rx_ant) == 1)
return;
+ if (vif->type == NL80211_IFTYPE_AP)
+ smps_mode = IEEE80211_SMPS_OFF;
+ else
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
mvmvif = iwl_mvm_vif_from_mac80211(vif);
mvmvif->smps_requests[req_type] = smps_request;
for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
@@ -538,3 +603,44 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
ieee80211_request_smps(vif, smps_mode);
}
+
+int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ bool value)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int res;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (mvmvif->low_latency == value)
+ return 0;
+
+ mvmvif->low_latency = value;
+
+ res = iwl_mvm_update_quotas(mvm, NULL);
+ if (res)
+ return res;
+
+ iwl_mvm_bt_coex_vif_change(mvm);
+
+ return iwl_mvm_power_update_mac(mvm, vif);
+}
+
+static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)
+{
+ bool *result = _data;
+
+ if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(vif)))
+ *result = true;
+}
+
+bool iwl_mvm_low_latency(struct iwl_mvm *mvm)
+{
+ bool result = false;
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_ll_iter, &result);
+
+ return result;
+}
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
index 3872ead75488..edb015c99049 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c
@@ -66,6 +66,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pci-aspm.h>
+#include <linux/acpi.h>
#include "iwl-trans.h"
#include "iwl-drv.h"
@@ -389,12 +390,92 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
{IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)},
+
+/* 8000 Series */
+ {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)},
#endif /* CONFIG_IWLMVM */
{0}
};
MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
+#ifdef CONFIG_ACPI
+#define SPL_METHOD "SPLC"
+#define SPL_DOMAINTYPE_MODULE BIT(0)
+#define SPL_DOMAINTYPE_WIFI BIT(1)
+#define SPL_DOMAINTYPE_WIGIG BIT(2)
+#define SPL_DOMAINTYPE_RFEM BIT(3)
+
+static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
+{
+ union acpi_object *limits, *domain_type, *power_limit;
+
+ if (splx->type != ACPI_TYPE_PACKAGE ||
+ splx->package.count != 2 ||
+ splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ splx->package.elements[0].integer.value != 0) {
+ IWL_ERR(trans, "Unsupported splx structure");
+ return 0;
+ }
+
+ limits = &splx->package.elements[1];
+ if (limits->type != ACPI_TYPE_PACKAGE ||
+ limits->package.count < 2 ||
+ limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
+ IWL_ERR(trans, "Invalid limits element");
+ return 0;
+ }
+
+ domain_type = &limits->package.elements[0];
+ power_limit = &limits->package.elements[1];
+ if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
+ IWL_DEBUG_INFO(trans, "WiFi power is not limited");
+ return 0;
+ }
+
+ return power_limit->integer.value;
+}
+
+static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
+{
+ acpi_handle pxsx_handle;
+ acpi_handle handle;
+ struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL};
+ acpi_status status;
+
+ pxsx_handle = ACPI_HANDLE(&pdev->dev);
+ if (!pxsx_handle) {
+ IWL_DEBUG_INFO(trans,
+ "Could not retrieve root port ACPI handle");
+ return;
+ }
+
+ /* Get the method's handle */
+ status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
+ if (ACPI_FAILURE(status)) {
+ IWL_DEBUG_INFO(trans, "SPL method not found");
+ return;
+ }
+
+ /* Call SPLC with no arguments */
+ status = acpi_evaluate_object(handle, NULL, NULL, &splx);
+ if (ACPI_FAILURE(status)) {
+ IWL_ERR(trans, "SPLC invocation failed (0x%x)", status);
+ return;
+ }
+
+ trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
+ IWL_DEBUG_INFO(trans, "Default power limit set to %lld",
+ trans->dflt_pwr_limit);
+ kfree(splx.pointer);
+}
+
+#else /* CONFIG_ACPI */
+static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) {}
+#endif
+
/* PCI registers */
#define PCI_CFG_RETRY_TIMEOUT 0x041
@@ -419,6 +500,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto out_free_trans;
}
+ set_dflt_pwr_limit(iwl_trans, pdev);
+
/* register transport layer debugfs here */
ret = iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir);
if (ret)
@@ -477,7 +560,7 @@ static int iwl_pci_resume(struct device *device)
iwl_enable_rfkill_int(trans);
hw_rfkill = iwl_is_rfkill_set(trans);
- iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
return 0;
}
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index e851f26fd44c..9091513ea738 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -304,7 +304,7 @@ struct iwl_trans_pcie {
bool bc_table_dword;
u32 rx_page_order;
- const char **command_names;
+ const char *const *command_names;
/* queue watchdog */
unsigned long wd_timeout;
@@ -488,4 +488,6 @@ static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
__iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask);
}
+void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state);
+
#endif /* __iwl_trans_int_pcie_h__ */
diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c
index 08c23d497a02..fdfa3969cac9 100644
--- a/drivers/net/wireless/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/rx.c
@@ -155,37 +155,26 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
if (rxq->need_update == 0)
goto exit_unlock;
- if (trans->cfg->base_params->shadow_reg_enable) {
- /* shadow register enabled */
- /* Device expects a multiple of 8 */
- rxq->write_actual = (rxq->write & ~0x7);
- iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
- } else {
- /* If power-saving is in use, make sure device is awake */
- if (test_bit(STATUS_TPOWER_PMI, &trans->status)) {
- reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
-
- if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
- IWL_DEBUG_INFO(trans,
- "Rx queue requesting wakeup,"
- " GP1 = 0x%x\n", reg);
- iwl_set_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- goto exit_unlock;
- }
-
- rxq->write_actual = (rxq->write & ~0x7);
- iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR,
- rxq->write_actual);
-
- /* Else device is assumed to be awake */
- } else {
- /* Device expects a multiple of 8 */
- rxq->write_actual = (rxq->write & ~0x7);
- iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR,
- rxq->write_actual);
+ /*
+ * explicitly wake up the NIC if:
+ * 1. shadow registers aren't enabled
+ * 2. there is a chance that the NIC is asleep
+ */
+ if (!trans->cfg->base_params->shadow_reg_enable &&
+ test_bit(STATUS_TPOWER_PMI, &trans->status)) {
+ reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
+
+ if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
+ IWL_DEBUG_INFO(trans, "Rx queue requesting wakeup, GP1 = 0x%x\n",
+ reg);
+ iwl_set_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ goto exit_unlock;
}
}
+
+ rxq->write_actual = round_down(rxq->write, 8);
+ iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
rxq->need_update = 0;
exit_unlock:
@@ -802,10 +791,9 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
static u32 iwl_pcie_int_cause_non_ict(struct iwl_trans *trans)
{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u32 inta;
- lockdep_assert_held(&trans_pcie->irq_lock);
+ lockdep_assert_held(&IWL_TRANS_GET_PCIE_TRANS(trans)->irq_lock);
trace_iwlwifi_dev_irq(trans->dev);
@@ -1006,7 +994,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
isr_stats->rfkill++;
- iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
if (hw_rfkill) {
set_bit(STATUS_RFKILL, &trans->status);
if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE,
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index f9507807b486..dcfd6d866d09 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -75,6 +75,20 @@
#include "iwl-agn-hw.h"
#include "internal.h"
+static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg)
+{
+ iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG,
+ ((reg & 0x0000ffff) | (2 << 28)));
+ return iwl_read32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG);
+}
+
+static void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val)
+{
+ iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG, val);
+ iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG,
+ ((reg & 0x0000ffff) | (3 << 28)));
+}
+
static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
{
if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold))
@@ -89,6 +103,7 @@ static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
/* PCI registers */
#define PCI_CFG_RETRY_TIMEOUT 0x041
+#define CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC
static void iwl_pcie_apm_config(struct iwl_trans *trans)
{
@@ -132,8 +147,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
*/
/* Disable L0S exit timer (platform NMI Work/Around) */
- iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
+ if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
+ CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
/*
* Disable L0s without affecting L1;
@@ -203,19 +219,23 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
/*
* Enable DMA clock and wait for it to stabilize.
*
- * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits
- * do not disable clocks. This preserves any hardware bits already
- * set by default in "CLK_CTRL_REG" after reset.
+ * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0"
+ * bits do not disable clocks. This preserves any hardware
+ * bits already set by default in "CLK_CTRL_REG" after reset.
*/
- iwl_write_prph(trans, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
- udelay(20);
+ if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ iwl_write_prph(trans, APMG_CLK_EN_REG,
+ APMG_CLK_VAL_DMA_CLK_RQT);
+ udelay(20);
- /* Disable L1-Active */
- iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
+ /* Disable L1-Active */
+ iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
+ APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
- /* Clear the interrupt in APMG if the NIC is in RFKILL */
- iwl_write_prph(trans, APMG_RTC_INT_STT_REG, APMG_RTC_INT_STT_RFKILL);
+ /* Clear the interrupt in APMG if the NIC is in RFKILL */
+ iwl_write_prph(trans, APMG_RTC_INT_STT_REG,
+ APMG_RTC_INT_STT_RFKILL);
+ }
set_bit(STATUS_DEVICE_ENABLED, &trans->status);
@@ -223,6 +243,116 @@ out:
return ret;
}
+/*
+ * Enable LP XTAL to avoid HW bug where device may consume much power if
+ * FW is not loaded after device reset. LP XTAL is disabled by default
+ * after device HW reset. Do it only if XTAL is fed by internal source.
+ * Configure device's "persistence" mode to avoid resetting XTAL again when
+ * SHRD_HW_RST occurs in S3.
+ */
+static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
+{
+ int ret;
+ u32 apmg_gp1_reg;
+ u32 apmg_xtal_cfg_reg;
+ u32 dl_cfg_reg;
+
+ /* Force XTAL ON */
+ __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
+
+ /* Reset entire device - do controller reset (results in SHRD_HW_RST) */
+ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+
+ udelay(10);
+
+ /*
+ * Set "initialization complete" bit to move adapter from
+ * D0U* --> D0A* (powered-up active) state.
+ */
+ iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+ /*
+ * Wait for clock stabilization; once stabilized, access to
+ * device-internal resources is possible.
+ */
+ ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ 25000);
+ if (WARN_ON(ret < 0)) {
+ IWL_ERR(trans, "Access time out - failed to enable LP XTAL\n");
+ /* Release XTAL ON request */
+ __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
+ return;
+ }
+
+ /*
+ * Clear "disable persistence" to avoid LP XTAL resetting when
+ * SHRD_HW_RST is applied in S3.
+ */
+ iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
+ APMG_PCIDEV_STT_VAL_PERSIST_DIS);
+
+ /*
+ * Force APMG XTAL to be active to prevent its disabling by HW
+ * caused by APMG idle state.
+ */
+ apmg_xtal_cfg_reg = iwl_trans_pcie_read_shr(trans,
+ SHR_APMG_XTAL_CFG_REG);
+ iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG,
+ apmg_xtal_cfg_reg |
+ SHR_APMG_XTAL_CFG_XTAL_ON_REQ);
+
+ /*
+ * Reset entire device again - do controller reset (results in
+ * SHRD_HW_RST). Turn MAC off before proceeding.
+ */
+ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+
+ udelay(10);
+
+ /* Enable LP XTAL by indirect access through CSR */
+ apmg_gp1_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_GP1_REG);
+ iwl_trans_pcie_write_shr(trans, SHR_APMG_GP1_REG, apmg_gp1_reg |
+ SHR_APMG_GP1_WF_XTAL_LP_EN |
+ SHR_APMG_GP1_CHICKEN_BIT_SELECT);
+
+ /* Clear delay line clock power up */
+ dl_cfg_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_DL_CFG_REG);
+ iwl_trans_pcie_write_shr(trans, SHR_APMG_DL_CFG_REG, dl_cfg_reg &
+ ~SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP);
+
+ /*
+ * Enable persistence mode to avoid LP XTAL resetting when
+ * SHRD_HW_RST is applied in S3.
+ */
+ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_PERSIST_MODE);
+
+ /*
+ * Clear "initialization complete" bit to move adapter from
+ * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
+ */
+ iwl_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+ /* Activates XTAL resources monitor */
+ __iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG,
+ CSR_MONITOR_XTAL_RESOURCES);
+
+ /* Release XTAL ON request */
+ __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
+ udelay(10);
+
+ /* Release APMG XTAL */
+ iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG,
+ apmg_xtal_cfg_reg &
+ ~SHR_APMG_XTAL_CFG_XTAL_ON_REQ);
+}
+
static int iwl_pcie_apm_stop_master(struct iwl_trans *trans)
{
int ret = 0;
@@ -250,6 +380,11 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans)
/* Stop device's DMA activity */
iwl_pcie_apm_stop_master(trans);
+ if (trans->cfg->lp_xtal_workaround) {
+ iwl_pcie_apm_lp_xtal_enable(trans);
+ return;
+ }
+
/* Reset the entire device */
iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
@@ -273,7 +408,8 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans)
spin_unlock(&trans_pcie->irq_lock);
- iwl_pcie_set_pwr(trans, false);
+ if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ iwl_pcie_set_pwr(trans, false);
iwl_op_mode_nic_config(trans->op_mode);
@@ -435,78 +571,106 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
return ret;
}
-static int iwl_pcie_secure_set(struct iwl_trans *trans, int cpu)
+static int iwl_pcie_load_cpu_secured_sections(struct iwl_trans *trans,
+ const struct fw_img *image,
+ int cpu,
+ int *first_ucode_section)
{
int shift_param;
- u32 address;
- int ret = 0;
+ int i, ret = 0;
+ u32 last_read_idx = 0;
if (cpu == 1) {
shift_param = 0;
- address = CSR_SECURE_BOOT_CPU1_STATUS_ADDR;
+ *first_ucode_section = 0;
} else {
shift_param = 16;
- address = CSR_SECURE_BOOT_CPU2_STATUS_ADDR;
+ (*first_ucode_section)++;
}
- /* set CPU to started */
- iwl_trans_set_bits_mask(trans,
- CSR_UCODE_LOAD_STATUS_ADDR,
- CSR_CPU_STATUS_LOADING_STARTED << shift_param,
- 1);
-
- /* set last complete descriptor number */
- iwl_trans_set_bits_mask(trans,
- CSR_UCODE_LOAD_STATUS_ADDR,
- CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED
- << shift_param,
- 1);
-
- /* set last loaded block */
- iwl_trans_set_bits_mask(trans,
- CSR_UCODE_LOAD_STATUS_ADDR,
- CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK
- << shift_param,
- 1);
+ for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
+ last_read_idx = i;
+ if (!image->sec[i].data ||
+ image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) {
+ IWL_DEBUG_FW(trans,
+ "Break since Data not valid or Empty section, sec = %d\n",
+ i);
+ break;
+ }
+
+ if (i == (*first_ucode_section) + 1)
+ /* set CPU to started */
+ iwl_set_bits_prph(trans,
+ CSR_UCODE_LOAD_STATUS_ADDR,
+ LMPM_CPU_HDRS_LOADING_COMPLETED
+ << shift_param);
+
+ ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
+ if (ret)
+ return ret;
+ }
/* image loading complete */
- iwl_trans_set_bits_mask(trans,
- CSR_UCODE_LOAD_STATUS_ADDR,
- CSR_CPU_STATUS_LOADING_COMPLETED
- << shift_param,
- 1);
-
- /* set FH_TCSR_0_REG */
- iwl_trans_set_bits_mask(trans, FH_TCSR_0_REG0, 0x00400000, 1);
-
- /* verify image verification started */
- ret = iwl_poll_bit(trans, address,
- CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS,
- CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS,
- CSR_SECURE_TIME_OUT);
- if (ret < 0) {
- IWL_ERR(trans, "secure boot process didn't start\n");
- return ret;
+ iwl_set_bits_prph(trans,
+ CSR_UCODE_LOAD_STATUS_ADDR,
+ LMPM_CPU_UCODE_LOADING_COMPLETED << shift_param);
+
+ *first_ucode_section = last_read_idx;
+
+ return 0;
+}
+
+static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,
+ const struct fw_img *image,
+ int cpu,
+ int *first_ucode_section)
+{
+ int shift_param;
+ int i, ret = 0;
+ u32 last_read_idx = 0;
+
+ if (cpu == 1) {
+ shift_param = 0;
+ *first_ucode_section = 0;
+ } else {
+ shift_param = 16;
+ (*first_ucode_section)++;
}
- /* wait for image verification to complete */
- ret = iwl_poll_bit(trans, address,
- CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED,
- CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED,
- CSR_SECURE_TIME_OUT);
+ for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
+ last_read_idx = i;
- if (ret < 0) {
- IWL_ERR(trans, "Time out on secure boot process\n");
- return ret;
+ if (!image->sec[i].data ||
+ image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) {
+ IWL_DEBUG_FW(trans,
+ "Break since Data not valid or Empty section, sec = %d\n",
+ i);
+ break;
+ }
+
+ ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
+ if (ret)
+ return ret;
}
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ iwl_set_bits_prph(trans,
+ CSR_UCODE_LOAD_STATUS_ADDR,
+ (LMPM_CPU_UCODE_LOADING_COMPLETED |
+ LMPM_CPU_HDRS_LOADING_COMPLETED |
+ LMPM_CPU_UCODE_LOADING_STARTED) <<
+ shift_param);
+
+ *first_ucode_section = last_read_idx;
+
return 0;
}
static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
const struct fw_img *image)
{
- int i, ret = 0;
+ int ret = 0;
+ int first_ucode_section;
IWL_DEBUG_FW(trans,
"working with %s image\n",
@@ -518,53 +682,68 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
/* configure the ucode to be ready to get the secured image */
if (image->is_secure) {
/* set secure boot inspector addresses */
- iwl_write32(trans, CSR_SECURE_INSPECTOR_CODE_ADDR, 0);
- iwl_write32(trans, CSR_SECURE_INSPECTOR_DATA_ADDR, 0);
-
- /* release CPU1 reset if secure inspector image burned in OTP */
- iwl_write32(trans, CSR_RESET, 0);
- }
-
- /* load to FW the binary sections of CPU1 */
- IWL_DEBUG_INFO(trans, "Loading CPU1\n");
- for (i = 0;
- i < IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU;
- i++) {
- if (!image->sec[i].data)
- break;
- ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
+ iwl_write_prph(trans,
+ LMPM_SECURE_INSPECTOR_CODE_ADDR,
+ LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE);
+
+ iwl_write_prph(trans,
+ LMPM_SECURE_INSPECTOR_DATA_ADDR,
+ LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE);
+
+ /* set CPU1 header address */
+ iwl_write_prph(trans,
+ LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR,
+ LMPM_SECURE_CPU1_HDR_MEM_SPACE);
+
+ /* load to FW the binary Secured sections of CPU1 */
+ ret = iwl_pcie_load_cpu_secured_sections(trans, image, 1,
+ &first_ucode_section);
if (ret)
return ret;
- }
- /* configure the ucode to start secure process on CPU1 */
- if (image->is_secure) {
- /* config CPU1 to start secure protocol */
- ret = iwl_pcie_secure_set(trans, 1);
+ } else {
+ /* load to FW the binary Non secured sections of CPU1 */
+ ret = iwl_pcie_load_cpu_sections(trans, image, 1,
+ &first_ucode_section);
if (ret)
return ret;
- } else {
- /* Remove all resets to allow NIC to operate */
- iwl_write32(trans, CSR_RESET, 0);
}
if (image->is_dual_cpus) {
+ /* set CPU2 header address */
+ iwl_write_prph(trans,
+ LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR,
+ LMPM_SECURE_CPU2_HDR_MEM_SPACE);
+
/* load to FW the binary sections of CPU2 */
- IWL_DEBUG_INFO(trans, "working w/ DUAL CPUs - Loading CPU2\n");
- for (i = IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU;
- i < IWL_UCODE_SECTION_MAX; i++) {
- if (!image->sec[i].data)
- break;
- ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
- if (ret)
- return ret;
- }
+ if (image->is_secure)
+ ret = iwl_pcie_load_cpu_secured_sections(
+ trans, image, 2,
+ &first_ucode_section);
+ else
+ ret = iwl_pcie_load_cpu_sections(trans, image, 2,
+ &first_ucode_section);
+ if (ret)
+ return ret;
+ }
+
+ /* release CPU reset */
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
+ else
+ iwl_write32(trans, CSR_RESET, 0);
- if (image->is_secure) {
- /* set CPU2 for secure protocol */
- ret = iwl_pcie_secure_set(trans, 2);
- if (ret)
- return ret;
+ if (image->is_secure) {
+ /* wait for image verification to complete */
+ ret = iwl_poll_prph_bit(trans,
+ LMPM_SECURE_BOOT_CPU1_STATUS_ADDR,
+ LMPM_SECURE_BOOT_STATUS_SUCCESS,
+ LMPM_SECURE_BOOT_STATUS_SUCCESS,
+ LMPM_SECURE_TIME_OUT);
+
+ if (ret < 0) {
+ IWL_ERR(trans, "Time out on secure boot process\n");
+ return ret;
}
}
@@ -591,7 +770,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
set_bit(STATUS_RFKILL, &trans->status);
else
clear_bit(STATUS_RFKILL, &trans->status);
- iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
if (hw_rfkill && !run_in_rfkill)
return -ERFKILL;
@@ -706,7 +885,13 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
else
clear_bit(STATUS_RFKILL, &trans->status);
if (hw_rfkill != was_hw_rfkill)
- iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+}
+
+void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
+{
+ if (iwl_op_mode_hw_rf_kill(trans->op_mode, state))
+ iwl_trans_pcie_stop_device(trans);
}
static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
@@ -815,7 +1000,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
set_bit(STATUS_RFKILL, &trans->status);
else
clear_bit(STATUS_RFKILL, &trans->status);
- iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
return 0;
}
@@ -1158,6 +1343,7 @@ static const char *get_csr_string(int cmd)
IWL_CMD(CSR_GIO_CHICKEN_BITS);
IWL_CMD(CSR_ANA_PLL_CFG);
IWL_CMD(CSR_HW_REV_WA_REG);
+ IWL_CMD(CSR_MONITOR_STATUS_REG);
IWL_CMD(CSR_DBG_HPET_MEM_REG);
default:
return "UNKNOWN";
@@ -1190,6 +1376,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans)
CSR_DRAM_INT_TBL_REG,
CSR_GIO_CHICKEN_BITS,
CSR_ANA_PLL_CFG,
+ CSR_MONITOR_STATUS_REG,
CSR_HW_REV_WA_REG,
CSR_DBG_HPET_MEM_REG
};
@@ -1407,16 +1594,15 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
{
struct iwl_trans *trans = file->private_data;
char *buf = NULL;
- int pos = 0;
- ssize_t ret = -EFAULT;
-
- ret = pos = iwl_dump_fh(trans, &buf);
- if (buf) {
- ret = simple_read_from_buffer(user_buf,
- count, ppos, buf, pos);
- kfree(buf);
- }
+ ssize_t ret;
+ ret = iwl_dump_fh(trans, &buf);
+ if (ret < 0)
+ return ret;
+ if (!buf)
+ return -EINVAL;
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+ kfree(buf);
return ret;
}
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index 3d549008b3e2..3b0c72c10054 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -207,7 +207,7 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data)
IWL_ERR(trans, "scratch %d = 0x%08x\n", i,
le32_to_cpu(txq->scratchbufs[i].scratch));
- iwl_trans_fw_error(trans);
+ iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1);
}
/*
@@ -296,43 +296,38 @@ void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
if (txq->need_update == 0)
return;
- if (trans->cfg->base_params->shadow_reg_enable ||
- txq_id == trans_pcie->cmd_queue) {
- /* shadow register enabled */
- iwl_write32(trans, HBUS_TARG_WRPTR,
- txq->q.write_ptr | (txq_id << 8));
- } else {
- /* if we're trying to save power */
- if (test_bit(STATUS_TPOWER_PMI, &trans->status)) {
- /* wake up nic if it's powered down ...
- * uCode will wake up, and interrupt us again, so next
- * time we'll skip this part. */
- reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
-
- if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
- IWL_DEBUG_INFO(trans,
- "Tx queue %d requesting wakeup,"
- " GP1 = 0x%x\n", txq_id, reg);
- iwl_set_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- return;
- }
-
- IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id,
- txq->q.write_ptr);
-
- iwl_write_direct32(trans, HBUS_TARG_WRPTR,
- txq->q.write_ptr | (txq_id << 8));
-
+ /*
+ * explicitly wake up the NIC if:
+ * 1. shadow registers aren't enabled
+ * 2. NIC is woken up for CMD regardless of shadow outside this function
+ * 3. there is a chance that the NIC is asleep
+ */
+ if (!trans->cfg->base_params->shadow_reg_enable &&
+ txq_id != trans_pcie->cmd_queue &&
+ test_bit(STATUS_TPOWER_PMI, &trans->status)) {
/*
- * else not in power-save mode,
- * uCode will never sleep when we're
- * trying to tx (during RFKILL, we're not trying to tx).
+ * wake up nic if it's powered down ...
+ * uCode will wake up, and interrupt us again, so next
+ * time we'll skip this part.
*/
- } else
- iwl_write32(trans, HBUS_TARG_WRPTR,
- txq->q.write_ptr | (txq_id << 8));
+ reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
+
+ if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
+ IWL_DEBUG_INFO(trans, "Tx queue %d requesting wakeup, GP1 = 0x%x\n",
+ txq_id, reg);
+ iwl_set_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ return;
+ }
}
+
+ /*
+ * if not in power-save mode, uCode will never sleep when we're
+ * 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));
+
txq->need_update = 0;
}
@@ -705,8 +700,9 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
/* Enable L1-Active */
- iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
+ if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
+ APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
}
void iwl_trans_pcie_tx_reset(struct iwl_trans *trans)
@@ -1028,7 +1024,7 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
if (nfreed++ > 0) {
IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n",
idx, q->write_ptr, q->read_ptr);
- iwl_trans_fw_error(trans);
+ iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1);
}
}
@@ -1587,6 +1583,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
get_cmd_string(trans_pcie, cmd->id));
ret = -ETIMEDOUT;
+ iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1);
iwl_trans_fw_error(trans);
goto cancel;
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index cb6d189bc3e6..54e344aed6e0 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -1766,7 +1766,8 @@ static void lbs_join_post(struct lbs_private *priv,
memcpy(priv->wdev->ssid, params->ssid, params->ssid_len);
priv->wdev->ssid_len = params->ssid_len;
- cfg80211_ibss_joined(priv->dev, bssid, GFP_KERNEL);
+ cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan,
+ GFP_KERNEL);
/* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */
priv->connect_status = LBS_CONNECTED;
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 58c6ee5de98f..33ceda296c9c 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -498,7 +498,7 @@ static int if_sdio_prog_helper(struct if_sdio_card *card,
*/
mdelay(2);
- chunk_size = min(size, (size_t)60);
+ chunk_size = min_t(size_t, size, 60);
*((__le32*)chunk_buffer) = cpu_to_le32(chunk_size);
memcpy(chunk_buffer + 4, firmware, chunk_size);
@@ -639,7 +639,7 @@ static int if_sdio_prog_real(struct if_sdio_card *card,
req_size = size;
while (req_size) {
- chunk_size = min(req_size, (size_t)512);
+ chunk_size = min_t(size_t, req_size, 512);
memcpy(chunk_buffer, firmware, chunk_size);
/*
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 69d4c3179d04..9d7a52f5a410 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -57,6 +57,10 @@ static bool rctbl = false;
module_param(rctbl, bool, 0444);
MODULE_PARM_DESC(rctbl, "Handle rate control table");
+static bool support_p2p_device = true;
+module_param(support_p2p_device, bool, 0444);
+MODULE_PARM_DESC(support_p2p_device, "Support P2P-Device interface type");
+
/**
* enum hwsim_regtest - the type of regulatory tests we offer
*
@@ -335,7 +339,8 @@ static const struct ieee80211_iface_limit hwsim_if_limits[] = {
#endif
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) },
- { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
+ /* must be last, see hwsim_if_comb */
+ { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }
};
static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = {
@@ -345,6 +350,27 @@ static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = {
static const struct ieee80211_iface_combination hwsim_if_comb[] = {
{
.limits = hwsim_if_limits,
+ /* remove the last entry which is P2P_DEVICE */
+ .n_limits = ARRAY_SIZE(hwsim_if_limits) - 1,
+ .max_interfaces = 2048,
+ .num_different_channels = 1,
+ },
+ {
+ .limits = hwsim_if_dfs_limits,
+ .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits),
+ .max_interfaces = 8,
+ .num_different_channels = 1,
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80) |
+ BIT(NL80211_CHAN_WIDTH_160),
+ }
+};
+
+static const struct ieee80211_iface_combination hwsim_if_comb_p2p_dev[] = {
+ {
+ .limits = hwsim_if_limits,
.n_limits = ARRAY_SIZE(hwsim_if_limits),
.max_interfaces = 2048,
.num_different_channels = 1,
@@ -385,6 +411,7 @@ struct mac80211_hwsim_data {
struct mac_address addresses[2];
int channels, idx;
+ bool use_chanctx;
struct ieee80211_channel *tmp_chan;
struct delayed_work roc_done;
@@ -451,7 +478,7 @@ static struct genl_family hwsim_genl_family = {
/* MAC80211_HWSIM netlink policy */
-static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
[HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
[HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
[HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
@@ -468,6 +495,7 @@ static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
[HWSIM_ATTR_REG_HINT_ALPHA2] = { .type = NLA_STRING, .len = 2 },
[HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 },
[HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG },
+ [HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG },
};
static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
@@ -1035,32 +1063,6 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
ack = true;
rx_status.mactime = now + data2->tsf_offset;
-#if 0
- /*
- * Don't enable this code by default as the OUI 00:00:00
- * is registered to Xerox so we shouldn't use it here, it
- * might find its way into pcap files.
- * Note that this code requires the headroom in the SKB
- * that was allocated earlier.
- */
- rx_status.vendor_radiotap_oui[0] = 0x00;
- rx_status.vendor_radiotap_oui[1] = 0x00;
- rx_status.vendor_radiotap_oui[2] = 0x00;
- rx_status.vendor_radiotap_subns = 127;
- /*
- * Radiotap vendor namespaces can (and should) also be
- * split into fields by using the standard radiotap
- * presence bitmap mechanism. Use just BIT(0) here for
- * the presence bitmap.
- */
- rx_status.vendor_radiotap_bitmap = BIT(0);
- /* We have 8 bytes of (dummy) data */
- rx_status.vendor_radiotap_len = 8;
- /* For testing, also require it to be aligned */
- rx_status.vendor_radiotap_align = 8;
- /* push the data */
- memcpy(skb_push(nskb, 8), "ABCDEFGH", 8);
-#endif
memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
ieee80211_rx_irqsafe(data2->hw, nskb);
@@ -1087,7 +1089,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
return;
}
- if (data->channels == 1) {
+ if (!data->use_chanctx) {
channel = data->channel;
} else if (txi->hw_queue == 4) {
channel = data->tmp_chan;
@@ -1275,6 +1277,9 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
mac80211_hwsim_tx_frame(hw, skb,
rcu_dereference(vif->chanctx_conf)->def.chan);
+
+ if (vif->csa_active && ieee80211_csa_is_complete(vif))
+ ieee80211_csa_finish(vif);
}
static enum hrtimer_restart
@@ -1350,7 +1355,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
data->channel = conf->chandef.chan;
- WARN_ON(data->channel && data->channels > 1);
+ WARN_ON(data->channel && data->use_chanctx);
data->power_level = conf->power_level;
if (!data->started || !data->beacon_int)
@@ -1936,7 +1941,8 @@ static struct ieee80211_ops mac80211_hwsim_mchan_ops;
static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
const struct ieee80211_regdomain *regd,
- bool reg_strict)
+ bool reg_strict, bool p2p_device,
+ bool use_chanctx)
{
int err;
u8 addr[ETH_ALEN];
@@ -1946,11 +1952,14 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
int idx;
+ if (WARN_ON(channels > 1 && !use_chanctx))
+ return -EINVAL;
+
spin_lock_bh(&hwsim_radio_lock);
idx = hwsim_radio_idx++;
spin_unlock_bh(&hwsim_radio_lock);
- if (channels > 1)
+ if (use_chanctx)
ops = &mac80211_hwsim_mchan_ops;
hw = ieee80211_alloc_hw(sizeof(*data), ops);
if (!hw) {
@@ -1991,17 +2000,25 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
hw->wiphy->addresses = data->addresses;
data->channels = channels;
+ data->use_chanctx = use_chanctx;
data->idx = idx;
- if (data->channels > 1) {
+ if (data->use_chanctx) {
hw->wiphy->max_scan_ssids = 255;
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
hw->wiphy->max_remain_on_channel_duration = 1000;
/* For channels > 1 DFS is not allowed */
hw->wiphy->n_iface_combinations = 1;
hw->wiphy->iface_combinations = &data->if_combination;
- data->if_combination = hwsim_if_comb[0];
+ if (p2p_device)
+ data->if_combination = hwsim_if_comb_p2p_dev[0];
+ else
+ data->if_combination = hwsim_if_comb[0];
data->if_combination.num_different_channels = data->channels;
+ } else if (p2p_device) {
+ hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev;
+ hw->wiphy->n_iface_combinations =
+ ARRAY_SIZE(hwsim_if_comb_p2p_dev);
} else {
hw->wiphy->iface_combinations = hwsim_if_comb;
hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb);
@@ -2017,8 +2034,10 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_MESH_POINT) |
- BIT(NL80211_IFTYPE_P2P_DEVICE);
+ BIT(NL80211_IFTYPE_MESH_POINT);
+
+ if (p2p_device)
+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
hw->flags = IEEE80211_HW_MFP_CAPABLE |
IEEE80211_HW_SIGNAL_DBM |
@@ -2027,13 +2046,15 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_WANT_MONITOR_VIF |
IEEE80211_HW_QUEUE_CONTROL |
- IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
+ IEEE80211_HW_SUPPORTS_HT_CCK_RATES |
+ IEEE80211_HW_CHANCTX_STA_CSA;
if (rctbl)
hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE;
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
- WIPHY_FLAG_AP_UAPSD;
+ WIPHY_FLAG_AP_UAPSD |
+ WIPHY_FLAG_HAS_CHANNEL_SWITCH;
hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
/* ask mac80211 to reserve space for magic */
@@ -2141,7 +2162,7 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps);
debugfs_create_file("group", 0666, data->debugfs, data,
&hwsim_fops_group);
- if (data->channels == 1)
+ if (!data->use_chanctx)
debugfs_create_file("dfs_simulate_radar", 0222,
data->debugfs,
data, &hwsim_simulate_radar);
@@ -2407,10 +2428,17 @@ static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
const char *alpha2 = NULL;
const struct ieee80211_regdomain *regd = NULL;
bool reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
+ bool p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
+ bool use_chanctx;
if (info->attrs[HWSIM_ATTR_CHANNELS])
chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
+ if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
+ use_chanctx = true;
+ else
+ use_chanctx = (chans > 1);
+
if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
@@ -2422,7 +2450,8 @@ static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
regd = hwsim_world_regdom_custom[idx];
}
- return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict);
+ return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict,
+ p2p_device, use_chanctx);
}
static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info)
@@ -2640,7 +2669,9 @@ static int __init init_mac80211_hwsim(void)
}
err = mac80211_hwsim_create_radio(channels, reg_alpha2,
- regd, reg_strict);
+ regd, reg_strict,
+ support_p2p_device,
+ channels > 1);
if (err < 0)
goto out_free_radios;
}
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
index 2747cce5a269..c9d0315575ba 100644
--- a/drivers/net/wireless/mac80211_hwsim.h
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -107,6 +107,10 @@ enum {
* (nla string, length 2)
* @HWSIM_ATTR_REG_CUSTOM_REG: custom regulatory domain index (u32 attribute)
* @HWSIM_ATTR_REG_STRICT_REG: request REGULATORY_STRICT_REG (flag attribute)
+ * @HWSIM_ATTR_SUPPORT_P2P_DEVICE: support P2P Device virtual interface (flag)
+ * @HWSIM_ATTR_USE_CHANCTX: used with the %HWSIM_CMD_CREATE_RADIO
+ * command to force use of channel contexts even when only a
+ * single channel is supported
* @__HWSIM_ATTR_MAX: enum limit
*/
@@ -126,6 +130,8 @@ enum {
HWSIM_ATTR_REG_HINT_ALPHA2,
HWSIM_ATTR_REG_CUSTOM_REG,
HWSIM_ATTR_REG_STRICT_REG,
+ HWSIM_ATTR_SUPPORT_P2P_DEVICE,
+ HWSIM_ATTR_USE_CHANCTX,
__HWSIM_ATTR_MAX,
};
#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c
index 5d9a8084665d..c92f27aa71ed 100644
--- a/drivers/net/wireless/mwifiex/11ac.c
+++ b/drivers/net/wireless/mwifiex/11ac.c
@@ -23,6 +23,31 @@
#include "main.h"
#include "11ac.h"
+/* Tables of the MCS map to the highest data rate (in Mbps) supported
+ * for long GI.
+ */
+static const u16 max_rate_lgi_80MHZ[8][3] = {
+ {0x124, 0x15F, 0x186}, /* NSS = 1 */
+ {0x249, 0x2BE, 0x30C}, /* NSS = 2 */
+ {0x36D, 0x41D, 0x492}, /* NSS = 3 */
+ {0x492, 0x57C, 0x618}, /* NSS = 4 */
+ {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */
+ {0x6DB, 0x83A, 0x0}, /* NSS = 6 */
+ {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */
+ {0x924, 0xAF8, 0xC30} /* NSS = 8 */
+};
+
+static const u16 max_rate_lgi_160MHZ[8][3] = {
+ {0x249, 0x2BE, 0x30C}, /* NSS = 1 */
+ {0x492, 0x57C, 0x618}, /* NSS = 2 */
+ {0x6DB, 0x83A, 0x0}, /* NSS = 3 */
+ {0x924, 0xAF8, 0xC30}, /* NSS = 4 */
+ {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */
+ {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */
+ {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */
+ {0x1248, 0x15F0, 0x1860} /* NSS = 8 */
+};
+
/* This function converts the 2-bit MCS map to the highest long GI
* VHT data rate.
*/
@@ -30,33 +55,10 @@ static u16
mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv,
u8 bands, u16 mcs_map)
{
- u8 i, nss, max_mcs;
+ u8 i, nss, mcs;
u16 max_rate = 0;
u32 usr_vht_cap_info = 0;
struct mwifiex_adapter *adapter = priv->adapter;
- /* tables of the MCS map to the highest data rate (in Mbps)
- * supported for long GI
- */
- u16 max_rate_lgi_80MHZ[8][3] = {
- {0x124, 0x15F, 0x186}, /* NSS = 1 */
- {0x249, 0x2BE, 0x30C}, /* NSS = 2 */
- {0x36D, 0x41D, 0x492}, /* NSS = 3 */
- {0x492, 0x57C, 0x618}, /* NSS = 4 */
- {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */
- {0x6DB, 0x83A, 0x0}, /* NSS = 6 */
- {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */
- {0x924, 0xAF8, 0xC30} /* NSS = 8 */
- };
- u16 max_rate_lgi_160MHZ[8][3] = {
- {0x249, 0x2BE, 0x30C}, /* NSS = 1 */
- {0x492, 0x57C, 0x618}, /* NSS = 2 */
- {0x6DB, 0x83A, 0x0}, /* NSS = 3 */
- {0x924, 0xAF8, 0xC30}, /* NSS = 4 */
- {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */
- {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */
- {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */
- {0x1248, 0x15F0, 0x1860} /* NSS = 8 */
- };
if (bands & BAND_AAC)
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
@@ -64,29 +66,29 @@ mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv,
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
/* find the max NSS supported */
- nss = 0;
- for (i = 0; i < 8; i++) {
- max_mcs = (mcs_map >> (2 * i)) & 0x3;
- if (max_mcs < 3)
+ nss = 1;
+ for (i = 1; i <= 8; i++) {
+ mcs = GET_VHTNSSMCS(mcs_map, i);
+ if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED)
nss = i;
}
- max_mcs = (mcs_map >> (2 * nss)) & 0x3;
+ mcs = GET_VHTNSSMCS(mcs_map, nss);
- /* if max_mcs is 3, nss must be 0 (SS = 1). Thus, max mcs is MCS 9 */
- if (max_mcs >= 3)
- max_mcs = 2;
+ /* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */
+ if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ mcs = IEEE80211_VHT_MCS_SUPPORT_0_9;
if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) {
/* support 160 MHz */
- max_rate = max_rate_lgi_160MHZ[nss][max_mcs];
+ max_rate = max_rate_lgi_160MHZ[nss - 1][mcs];
if (!max_rate)
/* MCS9 is not supported in NSS6 */
- max_rate = max_rate_lgi_160MHZ[nss][max_mcs - 1];
+ max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1];
} else {
- max_rate = max_rate_lgi_80MHZ[nss][max_mcs];
+ max_rate = max_rate_lgi_80MHZ[nss - 1][mcs];
if (!max_rate)
/* MCS9 is not supported in NSS3 */
- max_rate = max_rate_lgi_80MHZ[nss][max_mcs - 1];
+ max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1];
}
return max_rate;
@@ -94,21 +96,20 @@ mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv,
static void
mwifiex_fill_vht_cap_info(struct mwifiex_private *priv,
- struct mwifiex_ie_types_vhtcap *vht_cap, u8 bands)
+ struct ieee80211_vht_cap *vht_cap, u8 bands)
{
struct mwifiex_adapter *adapter = priv->adapter;
if (bands & BAND_A)
- vht_cap->vht_cap.vht_cap_info =
+ vht_cap->vht_cap_info =
cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a);
else
- vht_cap->vht_cap.vht_cap_info =
+ vht_cap->vht_cap_info =
cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg);
}
-static void
-mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv,
- struct mwifiex_ie_types_vhtcap *vht_cap, u8 bands)
+void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv,
+ struct ieee80211_vht_cap *vht_cap, u8 bands)
{
struct mwifiex_adapter *adapter = priv->adapter;
u16 mcs_map_user, mcs_map_resp, mcs_map_result;
@@ -119,46 +120,48 @@ mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv,
/* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */
mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support);
- mcs_map_resp = le16_to_cpu(vht_cap->vht_cap.supp_mcs.rx_mcs_map);
+ mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map);
mcs_map_result = 0;
for (nss = 1; nss <= 8; nss++) {
mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
- if ((mcs_user == NO_NSS_SUPPORT) ||
- (mcs_resp == NO_NSS_SUPPORT))
- SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT);
+ if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) ||
+ (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED))
+ SET_VHTNSSMCS(mcs_map_result, nss,
+ IEEE80211_VHT_MCS_NOT_SUPPORTED);
else
SET_VHTNSSMCS(mcs_map_result, nss,
min(mcs_user, mcs_resp));
}
- vht_cap->vht_cap.supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result);
+ vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result);
tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
- vht_cap->vht_cap.supp_mcs.rx_highest = cpu_to_le16(tmp);
+ vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp);
/* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */
mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support);
- mcs_map_resp = le16_to_cpu(vht_cap->vht_cap.supp_mcs.tx_mcs_map);
+ mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
mcs_map_result = 0;
for (nss = 1; nss <= 8; nss++) {
mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
- if ((mcs_user == NO_NSS_SUPPORT) ||
- (mcs_resp == NO_NSS_SUPPORT))
- SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT);
+ if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) ||
+ (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED))
+ SET_VHTNSSMCS(mcs_map_result, nss,
+ IEEE80211_VHT_MCS_NOT_SUPPORTED);
else
SET_VHTNSSMCS(mcs_map_result, nss,
min(mcs_user, mcs_resp));
}
- vht_cap->vht_cap.supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result);
+ vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result);
tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
- vht_cap->vht_cap.supp_mcs.tx_highest = cpu_to_le16(tmp);
+ vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp);
return;
}
@@ -192,7 +195,8 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
(u8 *)bss_desc->bcn_vht_cap,
le16_to_cpu(vht_cap->header.len));
- mwifiex_fill_vht_cap_tlv(priv, vht_cap, bss_desc->bss_band);
+ mwifiex_fill_vht_cap_tlv(priv, &vht_cap->vht_cap,
+ bss_desc->bss_band);
*buffer += sizeof(*vht_cap);
ret_len += sizeof(*vht_cap);
}
@@ -299,3 +303,81 @@ void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv)
return;
}
+
+bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv)
+{
+ struct mwifiex_bssdescriptor *bss_desc;
+ struct ieee80211_vht_operation *vht_oper;
+
+ bss_desc = &priv->curr_bss_params.bss_descriptor;
+ vht_oper = bss_desc->bcn_vht_oper;
+
+ if (!bss_desc->bcn_vht_cap || !vht_oper)
+ return false;
+
+ if (vht_oper->chan_width == IEEE80211_VHT_CHANWIDTH_USE_HT)
+ return false;
+
+ return true;
+}
+
+u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band,
+ u32 pri_chan, u8 chan_bw)
+{
+ u8 center_freq_idx = 0;
+
+ if (band & BAND_AAC) {
+ switch (pri_chan) {
+ case 36:
+ case 40:
+ case 44:
+ case 48:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 42;
+ break;
+ case 52:
+ case 56:
+ case 60:
+ case 64:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 58;
+ else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ)
+ center_freq_idx = 50;
+ break;
+ case 100:
+ case 104:
+ case 108:
+ case 112:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 106;
+ break;
+ case 116:
+ case 120:
+ case 124:
+ case 128:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 122;
+ else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ)
+ center_freq_idx = 114;
+ break;
+ case 132:
+ case 136:
+ case 140:
+ case 144:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 138;
+ break;
+ case 149:
+ case 153:
+ case 157:
+ case 161:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 155;
+ break;
+ default:
+ center_freq_idx = 42;
+ }
+ }
+
+ return center_freq_idx;
+}
diff --git a/drivers/net/wireless/mwifiex/11ac.h b/drivers/net/wireless/mwifiex/11ac.h
index 7c2c69b5b3eb..0b02cb6cfcb4 100644
--- a/drivers/net/wireless/mwifiex/11ac.h
+++ b/drivers/net/wireless/mwifiex/11ac.h
@@ -40,4 +40,6 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd, u16 cmd_action,
struct mwifiex_11ac_vht_cfg *cfg);
+void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv,
+ struct ieee80211_vht_cap *vht_cap, u8 bands);
#endif /* _MWIFIEX_11AC_H_ */
diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c
index 8d683070bdb3..e76b0db4e3e6 100644
--- a/drivers/net/wireless/mwifiex/11h.c
+++ b/drivers/net/wireless/mwifiex/11h.c
@@ -73,8 +73,8 @@ static int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag)
{
u32 enable = flag;
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_SET, DOT11H_I, &enable);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_SET, DOT11H_I, &enable, true);
}
/* This functions processes TLV buffer for a pending BSS Join command.
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c
index 7db1a89fdd95..d14ead8beca8 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/mwifiex/11n.c
@@ -34,22 +34,26 @@
*
* RD responder bit to set to clear in the extended capability header.
*/
-void
-mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type,
- struct mwifiex_ie_types_htcap *ht_cap)
+int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type,
+ struct ieee80211_ht_cap *ht_cap)
{
- uint16_t ht_ext_cap = le16_to_cpu(ht_cap->ht_cap.extended_ht_cap_info);
+ uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info);
struct ieee80211_supported_band *sband =
priv->wdev->wiphy->bands[radio_type];
- ht_cap->ht_cap.ampdu_params_info =
+ if (WARN_ON_ONCE(!sband)) {
+ dev_err(priv->adapter->dev, "Invalid radio type!\n");
+ return -EINVAL;
+ }
+
+ ht_cap->ampdu_params_info =
(sband->ht_cap.ampdu_factor &
IEEE80211_HT_AMPDU_PARM_FACTOR) |
((sband->ht_cap.ampdu_density <<
IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) &
IEEE80211_HT_AMPDU_PARM_DENSITY);
- memcpy((u8 *) &ht_cap->ht_cap.mcs, &sband->ht_cap.mcs,
+ memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs,
sizeof(sband->ht_cap.mcs));
if (priv->bss_mode == NL80211_IFTYPE_STATION ||
@@ -57,13 +61,18 @@ mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type,
(priv->adapter->sec_chan_offset !=
IEEE80211_HT_PARAM_CHA_SEC_NONE)))
/* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
- SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask);
+ SETHT_MCS32(ht_cap->mcs.rx_mask);
/* Clear RD responder bit */
ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER;
- ht_cap->ht_cap.cap_info = cpu_to_le16(sband->ht_cap.cap);
- ht_cap->ht_cap.extended_ht_cap_info = cpu_to_le16(ht_ext_cap);
+ ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap);
+ ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap);
+
+ if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap))
+ ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP);
+
+ return 0;
}
/*
@@ -150,28 +159,34 @@ int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv,
int tid;
struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;
+ u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn))
& SSN_MASK);
- tid = (le16_to_cpu(add_ba_rsp->block_ack_param_set)
- & IEEE80211_ADDBA_PARAM_TID_MASK)
- >> BLOCKACKPARAM_TID_POS;
- if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) {
- tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid,
- add_ba_rsp->peer_mac_addr);
- if (tx_ba_tbl) {
- dev_dbg(priv->adapter->dev, "info: BA stream complete\n");
- tx_ba_tbl->ba_status = BA_SETUP_COMPLETE;
- } else {
- dev_err(priv->adapter->dev, "BA stream not created\n");
- }
- } else {
+ tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
+ >> BLOCKACKPARAM_TID_POS;
+ if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr,
TYPE_DELBA_SENT, true);
if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT)
priv->aggr_prio_tbl[tid].ampdu_ap =
BA_STREAM_NOT_ALLOWED;
+ return 0;
+ }
+
+ tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr);
+ if (tx_ba_tbl) {
+ dev_dbg(priv->adapter->dev, "info: BA stream complete\n");
+ tx_ba_tbl->ba_status = BA_SETUP_COMPLETE;
+ if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
+ priv->add_ba_param.tx_amsdu &&
+ (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
+ tx_ba_tbl->amsdu = true;
+ else
+ tx_ba_tbl->amsdu = false;
+ } else {
+ dev_err(priv->adapter->dev, "BA stream not created\n");
}
return 0;
@@ -311,7 +326,7 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
(u8 *)bss_desc->bcn_ht_cap,
le16_to_cpu(ht_cap->header.len));
- mwifiex_fill_cap_info(priv, radio_type, ht_cap);
+ mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
*buffer += sizeof(struct mwifiex_ie_types_htcap);
ret_len += sizeof(struct mwifiex_ie_types_htcap);
@@ -527,16 +542,39 @@ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid,
int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
{
struct host_cmd_ds_11n_addba_req add_ba_req;
+ struct mwifiex_sta_node *sta_ptr;
+ u32 tx_win_size = priv->add_ba_param.tx_win_size;
static u8 dialog_tok;
int ret;
+ u16 block_ack_param_set;
dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid);
- add_ba_req.block_ack_param_set = cpu_to_le16(
- (u16) ((tid << BLOCKACKPARAM_TID_POS) |
- (priv->add_ba_param.
- tx_win_size << BLOCKACKPARAM_WINSIZE_POS) |
- IMMEDIATE_BLOCK_ACK));
+ if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+ ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ priv->adapter->is_hw_11ac_capable &&
+ memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) {
+ sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);
+ if (!sta_ptr) {
+ dev_warn(priv->adapter->dev,
+ "BA setup with unknown TDLS peer %pM!\n",
+ peer_mac);
+ return -1;
+ }
+ if (sta_ptr->is_11ac_enabled)
+ tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;
+ }
+
+ block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) |
+ tx_win_size << BLOCKACKPARAM_WINSIZE_POS |
+ IMMEDIATE_BLOCK_ACK);
+
+ /* enable AMSDU inside AMPDU */
+ if (priv->add_ba_param.tx_amsdu &&
+ (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
+ block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK;
+
+ add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set);
add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout);
++dialog_tok;
@@ -548,8 +586,8 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN);
/* We don't wait for the response of this command */
- ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_REQ,
- 0, 0, &add_ba_req);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ,
+ 0, 0, &add_ba_req, false);
return ret;
}
@@ -576,8 +614,8 @@ int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac,
memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN);
/* We don't wait for the response of this command */
- ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_DELBA,
- HostCmd_ACT_GEN_SET, 0, &delba);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA,
+ HostCmd_ACT_GEN_SET, 0, &delba, false);
return ret;
}
@@ -651,6 +689,7 @@ int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv,
dev_dbg(priv->adapter->dev, "data: %s tid=%d\n",
__func__, rx_reo_tbl->tid);
memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN);
+ rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu;
rx_reo_tbl++;
count++;
if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED)
@@ -706,5 +745,8 @@ void mwifiex_set_ba_params(struct mwifiex_private *priv)
MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;
}
+ priv->add_ba_param.tx_amsdu = true;
+ priv->add_ba_param.rx_amsdu = true;
+
return;
}
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h
index 375db01442bf..40b007a00f4b 100644
--- a/drivers/net/wireless/mwifiex/11n.h
+++ b/drivers/net/wireless/mwifiex/11n.h
@@ -34,8 +34,8 @@ int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv,
int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
struct mwifiex_bssdescriptor *bss_desc,
u8 **buffer);
-void mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type,
- struct mwifiex_ie_types_htcap *);
+int mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type,
+ struct ieee80211_ht_cap *);
int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv,
u16 action, int *htcap_cfg);
void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv,
@@ -64,14 +64,46 @@ int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl);
void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra);
-/*
- * This function checks whether AMPDU is allowed or not for a particular TID.
- */
static inline u8
-mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, int tid)
+mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv,
+ struct mwifiex_ra_list_tbl *ptr, int tid)
{
- return ((priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED)
- ? true : false);
+ struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ptr->ra);
+
+ if (unlikely(!node))
+ return false;
+
+ return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false;
+}
+
+/* This function checks whether AMSDU is allowed for BA stream. */
+static inline u8
+mwifiex_is_amsdu_in_ampdu_allowed(struct mwifiex_private *priv,
+ struct mwifiex_ra_list_tbl *ptr, int tid)
+{
+ struct mwifiex_tx_ba_stream_tbl *tx_tbl;
+
+ tx_tbl = mwifiex_get_ba_tbl(priv, tid, ptr->ra);
+ if (tx_tbl)
+ return tx_tbl->amsdu;
+
+ return false;
+}
+
+/* This function checks whether AMPDU is allowed or not for a particular TID. */
+static inline u8
+mwifiex_is_ampdu_allowed(struct mwifiex_private *priv,
+ struct mwifiex_ra_list_tbl *ptr, int tid)
+{
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+ return mwifiex_is_station_ampdu_allowed(priv, ptr, tid);
+ } else {
+ if (ptr->tdls_link)
+ return mwifiex_is_station_ampdu_allowed(priv, ptr, tid);
+
+ return (priv->aggr_prio_tbl[tid].ampdu_ap !=
+ BA_STREAM_NOT_ALLOWED) ? true : false;
+ }
}
/*
@@ -165,4 +197,14 @@ static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv,
return node->is_11n_enabled;
}
+
+static inline u8
+mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, u8 *ra)
+{
+ struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra);
+ if (node)
+ return node->is_11n_enabled;
+
+ return false;
+}
#endif /* !_MWIFIEX_11N_H_ */
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c
index ada809f576fe..0c3571f830b0 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c
@@ -26,6 +26,56 @@
#include "11n.h"
#include "11n_rxreorder.h"
+/* This function will dispatch amsdu packet and forward it to kernel/upper
+ * layer.
+ */
+static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct rxpd *local_rx_pd = (struct rxpd *)(skb->data);
+ int ret;
+
+ if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) {
+ struct sk_buff_head list;
+ struct sk_buff *rx_skb;
+
+ __skb_queue_head_init(&list);
+
+ skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset));
+ skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
+
+ ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
+ priv->wdev->iftype, 0, false);
+
+ while (!skb_queue_empty(&list)) {
+ rx_skb = __skb_dequeue(&list);
+ ret = mwifiex_recv_packet(priv, rx_skb);
+ if (ret == -1)
+ dev_err(priv->adapter->dev,
+ "Rx of A-MSDU failed");
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+/* This function will process the rx packet and forward it to kernel/upper
+ * layer.
+ */
+static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload)
+{
+ int ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload);
+
+ if (!ret)
+ return 0;
+
+ if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
+ return mwifiex_handle_uap_rx_forward(priv, payload);
+
+ return mwifiex_process_rx_packet(priv, payload);
+}
+
/*
* This function dispatches all packets in the Rx reorder table until the
* start window.
@@ -35,8 +85,9 @@
* circular buffer.
*/
static void
-mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv,
- struct mwifiex_rx_reorder_tbl *tbl, int start_win)
+mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv,
+ struct mwifiex_rx_reorder_tbl *tbl,
+ int start_win)
{
int pkt_to_send, i;
void *rx_tmp_ptr;
@@ -54,12 +105,8 @@ mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv,
tbl->rx_reorder_ptr[i] = NULL;
}
spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
- if (rx_tmp_ptr) {
- if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
- mwifiex_handle_uap_rx_forward(priv, rx_tmp_ptr);
- else
- mwifiex_process_rx_packet(priv, rx_tmp_ptr);
- }
+ if (rx_tmp_ptr)
+ mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr);
}
spin_lock_irqsave(&priv->rx_pkt_lock, flags);
@@ -101,11 +148,7 @@ mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv,
rx_tmp_ptr = tbl->rx_reorder_ptr[i];
tbl->rx_reorder_ptr[i] = NULL;
spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
-
- if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
- mwifiex_handle_uap_rx_forward(priv, rx_tmp_ptr);
- else
- mwifiex_process_rx_packet(priv, rx_tmp_ptr);
+ mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr);
}
spin_lock_irqsave(&priv->rx_pkt_lock, flags);
@@ -135,14 +178,15 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
struct mwifiex_rx_reorder_tbl *tbl)
{
unsigned long flags;
+ int start_win;
if (!tbl)
return;
- mwifiex_11n_dispatch_pkt(priv, tbl, (tbl->start_win + tbl->win_size) &
- (MAX_TID_VALUE - 1));
+ start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1);
+ mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
- del_timer(&tbl->timer_context.timer);
+ del_timer_sync(&tbl->timer_context.timer);
spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
list_del(&tbl->list);
@@ -228,17 +272,17 @@ mwifiex_flush_data(unsigned long context)
{
struct reorder_tmr_cnxt *ctx =
(struct reorder_tmr_cnxt *) context;
- int start_win;
+ int start_win, seq_num;
- start_win = mwifiex_11n_find_last_seq_num(ctx->ptr);
+ seq_num = mwifiex_11n_find_last_seq_num(ctx->ptr);
- if (start_win < 0)
+ if (seq_num < 0)
return;
- dev_dbg(ctx->priv->adapter->dev, "info: flush data %d\n", start_win);
- mwifiex_11n_dispatch_pkt(ctx->priv, ctx->ptr,
- (ctx->ptr->start_win + start_win + 1) &
- (MAX_TID_VALUE - 1));
+ dev_dbg(ctx->priv->adapter->dev, "info: flush data %d\n", seq_num);
+ start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1);
+ mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr,
+ start_win);
}
/*
@@ -267,7 +311,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
*/
tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
if (tbl) {
- mwifiex_11n_dispatch_pkt(priv, tbl, seq_num);
+ mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num);
return;
}
/* if !tbl then create one */
@@ -279,6 +323,8 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
new_node->tid = tid;
memcpy(new_node->ta, ta, ETH_ALEN);
new_node->start_win = seq_num;
+ new_node->init_win = seq_num;
+ new_node->flags = 0;
if (mwifiex_queuing_ra_based(priv)) {
dev_dbg(priv->adapter->dev,
@@ -290,15 +336,20 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
last_seq = node->rx_seq[tid];
}
} else {
- last_seq = priv->rx_seq[tid];
+ node = mwifiex_get_sta_entry(priv, ta);
+ if (node)
+ last_seq = node->rx_seq[tid];
+ else
+ last_seq = priv->rx_seq[tid];
}
if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM &&
- last_seq >= new_node->start_win)
+ last_seq >= new_node->start_win) {
new_node->start_win = last_seq + 1;
+ new_node->flags |= RXREOR_INIT_WINDOW_SHIFT;
+ }
new_node->win_size = win_size;
- new_node->flags = 0;
new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size,
GFP_KERNEL);
@@ -358,10 +409,28 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
*cmd_addba_req)
{
struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp;
+ struct mwifiex_sta_node *sta_ptr;
+ u32 rx_win_size = priv->add_ba_param.rx_win_size;
u8 tid;
int win_size;
uint16_t block_ack_param_set;
+ if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+ ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ priv->adapter->is_hw_11ac_capable &&
+ memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) {
+ sta_ptr = mwifiex_get_sta_entry(priv,
+ cmd_addba_req->peer_mac_addr);
+ if (!sta_ptr) {
+ dev_warn(priv->adapter->dev,
+ "BA setup with unknown TDLS peer %pM!\n",
+ cmd_addba_req->peer_mac_addr);
+ return -1;
+ }
+ if (sta_ptr->is_11ac_enabled)
+ rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
+ }
+
cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP);
cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN);
@@ -376,10 +445,12 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
>> BLOCKACKPARAM_TID_POS;
add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT);
block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK;
- /* We donot support AMSDU inside AMPDU, hence reset the bit */
- block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK;
- block_ack_param_set |= (priv->add_ba_param.rx_win_size <<
- BLOCKACKPARAM_WINSIZE_POS);
+
+ /* If we don't support AMSDU inside AMPDU, reset the bit */
+ if (!priv->add_ba_param.rx_amsdu ||
+ (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED))
+ block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK;
+ block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS;
add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set);
win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set)
& IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
@@ -431,33 +502,46 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
struct mwifiex_rx_reorder_tbl *tbl;
int start_win, end_win, win_size;
u16 pkt_index;
+ bool init_window_shift = false;
tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
if (!tbl) {
- if (pkt_type != PKT_TYPE_BAR) {
- if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
- mwifiex_handle_uap_rx_forward(priv, payload);
- else
- mwifiex_process_rx_packet(priv, payload);
- }
+ if (pkt_type != PKT_TYPE_BAR)
+ mwifiex_11n_dispatch_pkt(priv, payload);
return 0;
}
+
+ if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) {
+ mwifiex_11n_dispatch_pkt(priv, payload);
+ return 0;
+ }
+
start_win = tbl->start_win;
win_size = tbl->win_size;
end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
- del_timer(&tbl->timer_context.timer);
+ if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) {
+ init_window_shift = true;
+ tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT;
+ }
mod_timer(&tbl->timer_context.timer,
jiffies + msecs_to_jiffies(MIN_FLUSH_TIMER_MS * win_size));
- /*
- * If seq_num is less then starting win then ignore and drop the
- * packet
- */
if (tbl->flags & RXREOR_FORCE_NO_DROP) {
dev_dbg(priv->adapter->dev,
"RXREOR_FORCE_NO_DROP when HS is activated\n");
tbl->flags &= ~RXREOR_FORCE_NO_DROP;
+ } else if (init_window_shift && seq_num < start_win &&
+ seq_num >= tbl->init_win) {
+ dev_dbg(priv->adapter->dev,
+ "Sender TID sequence number reset %d->%d for SSN %d\n",
+ start_win, seq_num, tbl->init_win);
+ tbl->start_win = start_win = seq_num;
+ end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
} else {
+ /*
+ * If seq_num is less then starting win then ignore and drop
+ * the packet
+ */
if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {
if (seq_num >= ((start_win + TWOPOW11) &
(MAX_TID_VALUE - 1)) &&
@@ -485,7 +569,7 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
start_win = (end_win - win_size) + 1;
else
start_win = (MAX_TID_VALUE - (win_size - seq_num)) + 1;
- mwifiex_11n_dispatch_pkt(priv, tbl, start_win);
+ mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
}
if (pkt_type != PKT_TYPE_BAR) {
@@ -576,16 +660,7 @@ int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv,
* Check if we had rejected the ADDBA, if yes then do not create
* the stream
*/
- if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) {
- win_size = (block_ack_param_set &
- IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
- >> BLOCKACKPARAM_WINSIZE_POS;
-
- dev_dbg(priv->adapter->dev,
- "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n",
- add_ba_rsp->peer_mac_addr, tid,
- add_ba_rsp->ssn, win_size);
- } else {
+ if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
dev_err(priv->adapter->dev, "ADDBA RSP: failed %pM tid=%d)\n",
add_ba_rsp->peer_mac_addr, tid);
@@ -593,8 +668,28 @@ int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv,
add_ba_rsp->peer_mac_addr);
if (tbl)
mwifiex_del_rx_reorder_entry(priv, tbl);
+
+ return 0;
}
+ win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
+ >> BLOCKACKPARAM_WINSIZE_POS;
+
+ tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
+ add_ba_rsp->peer_mac_addr);
+ if (tbl) {
+ if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
+ priv->add_ba_param.rx_amsdu &&
+ (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
+ tbl->amsdu = true;
+ else
+ tbl->amsdu = false;
+ }
+
+ dev_dbg(priv->adapter->dev,
+ "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n",
+ add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size);
+
return 0;
}
@@ -615,7 +710,7 @@ void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv,
delba.del_ba_param_set |= cpu_to_le16(
(u16) event->origninator << DELBA_INITIATOR_POS);
delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT);
- mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba);
+ mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false);
}
/*
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h
index 4064041ac852..0fc76e4a60f8 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.h
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h
@@ -42,7 +42,8 @@
#define BA_SETUP_PACKET_OFFSET 16
enum mwifiex_rxreor_flags {
- RXREOR_FORCE_NO_DROP = 1<<0,
+ RXREOR_FORCE_NO_DROP = 1<<0,
+ RXREOR_INIT_WINDOW_SHIFT = 1<<1,
};
static inline void mwifiex_reset_11n_rx_seq_num(struct mwifiex_private *priv)
diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile
index a42a506fd32b..2aa208ffbe23 100644
--- a/drivers/net/wireless/mwifiex/Makefile
+++ b/drivers/net/wireless/mwifiex/Makefile
@@ -41,6 +41,7 @@ mwifiex-y += uap_txrx.o
mwifiex-y += cfg80211.o
mwifiex-y += ethtool.o
mwifiex-y += 11h.o
+mwifiex-y += tdls.o
mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o
obj-$(CONFIG_MWIFIEX) += mwifiex.o
diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/mwifiex/README
index 3d64613ebb29..b9242c3dca43 100644
--- a/drivers/net/wireless/mwifiex/README
+++ b/drivers/net/wireless/mwifiex/README
@@ -131,7 +131,7 @@ info
hs_configured = <0/1, host sleep not configured/configured>
hs_activated = <0/1, extended host sleep not activated/activated>
num_tx_timeout = <number of Tx timeout>
- num_cmd_timeout = <number of timeout commands>
+ is_cmd_timedout = <0/1 command timeout not occurred/occurred>
timeout_cmd_id = <command id of the last timeout command>
timeout_cmd_act = <command action of the last timeout command>
last_cmd_id = <command id of the last several commands sent to device>
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index 8bfc07cd330e..21ee27ab7b74 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -252,9 +252,9 @@ mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
if (mask != priv->mgmt_frame_mask) {
priv->mgmt_frame_mask = mask;
- mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG,
- HostCmd_ACT_GEN_SET, 0,
- &priv->mgmt_frame_mask);
+ mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG,
+ HostCmd_ACT_GEN_SET, 0,
+ &priv->mgmt_frame_mask, false);
wiphy_dbg(wiphy, "info: mgmt frame registered\n");
}
}
@@ -515,8 +515,8 @@ static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy)
priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
- if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
- HostCmd_ACT_GEN_SET, 0, NULL)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
+ HostCmd_ACT_GEN_SET, 0, NULL, false)) {
wiphy_err(wiphy, "11D: setting domain info in FW\n");
return -1;
}
@@ -580,9 +580,9 @@ mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr)
frag_thr > MWIFIEX_FRAG_MAX_VALUE)
frag_thr = MWIFIEX_FRAG_MAX_VALUE;
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_SET, FRAG_THRESH_I,
- &frag_thr);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_SET, FRAG_THRESH_I,
+ &frag_thr, true);
}
/*
@@ -597,9 +597,9 @@ mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr)
if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE)
rts_thr = MWIFIEX_RTS_MAX_VALUE;
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_SET, RTS_THRESH_I,
- &rts_thr);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_SET, RTS_THRESH_I,
+ &rts_thr, true);
}
/*
@@ -637,20 +637,19 @@ mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
bss_started = priv->bss_started;
- ret = mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_UAP_BSS_STOP,
- HostCmd_ACT_GEN_SET, 0,
- NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
+ HostCmd_ACT_GEN_SET, 0,
+ NULL, true);
if (ret) {
wiphy_err(wiphy, "Failed to stop the BSS\n");
kfree(bss_cfg);
return ret;
}
- ret = mwifiex_send_cmd_async(priv,
- HostCmd_CMD_UAP_SYS_CONFIG,
- HostCmd_ACT_GEN_SET,
- UAP_BSS_PARAMS_I, bss_cfg);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
+ HostCmd_ACT_GEN_SET,
+ UAP_BSS_PARAMS_I, bss_cfg,
+ false);
kfree(bss_cfg);
@@ -662,10 +661,9 @@ mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
if (!bss_started)
break;
- ret = mwifiex_send_cmd_async(priv,
- HostCmd_CMD_UAP_BSS_START,
- HostCmd_ACT_GEN_SET, 0,
- NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START,
+ HostCmd_ACT_GEN_SET, 0,
+ NULL, false);
if (ret) {
wiphy_err(wiphy, "Failed to start BSS\n");
return ret;
@@ -700,8 +698,8 @@ mwifiex_cfg80211_deinit_p2p(struct mwifiex_private *priv)
if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA)
mwifiex_set_bss_role(priv, MWIFIEX_BSS_ROLE_STA);
- if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG,
- HostCmd_ACT_GEN_SET, 0, &mode))
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &mode, true))
return -1;
return 0;
@@ -721,13 +719,13 @@ mwifiex_cfg80211_init_p2p_client(struct mwifiex_private *priv)
return -1;
mode = P2P_MODE_DEVICE;
- if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG,
- HostCmd_ACT_GEN_SET, 0, &mode))
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &mode, true))
return -1;
mode = P2P_MODE_CLIENT;
- if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG,
- HostCmd_ACT_GEN_SET, 0, &mode))
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &mode, true))
return -1;
return 0;
@@ -747,13 +745,13 @@ mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv)
return -1;
mode = P2P_MODE_DEVICE;
- if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG,
- HostCmd_ACT_GEN_SET, 0, &mode))
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &mode, true))
return -1;
mode = P2P_MODE_GO;
- if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG,
- HostCmd_ACT_GEN_SET, 0, &mode))
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &mode, true))
return -1;
if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP)
@@ -853,8 +851,8 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy,
priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_SET_BSS_MODE,
- HostCmd_ACT_GEN_SET, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+ HostCmd_ACT_GEN_SET, 0, NULL, true);
return ret;
}
@@ -942,8 +940,8 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
/* Get signal information from the firmware */
- if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_RSSI_INFO,
- HostCmd_ACT_GEN_GET, 0, NULL)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO,
+ HostCmd_ACT_GEN_GET, 0, NULL, true)) {
dev_err(priv->adapter->dev, "failed to get signal information\n");
return -EFAULT;
}
@@ -954,9 +952,9 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
}
/* Get DTIM period information from firmware */
- mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_GET, DTIM_PERIOD_I,
- &priv->dtim_period);
+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_GET, DTIM_PERIOD_I,
+ &priv->dtim_period, true);
mwifiex_parse_htinfo(priv, priv->tx_htinfo, &sinfo->txrate);
@@ -1160,9 +1158,10 @@ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy,
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
u16 bitmap_rates[MAX_BITMAP_RATES_SIZE];
enum ieee80211_band band;
+ struct mwifiex_adapter *adapter = priv->adapter;
if (!priv->media_connected) {
- dev_err(priv->adapter->dev,
+ dev_err(adapter->dev,
"Can not set Tx data rate in disconnected state\n");
return -EINVAL;
}
@@ -1183,11 +1182,18 @@ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy,
/* Fill HT MCS rates */
bitmap_rates[2] = mask->control[band].ht_mcs[0];
- if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2)
+ if (adapter->hw_dev_mcs_support == HT_STREAM_2X2)
bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8;
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TX_RATE_CFG,
- HostCmd_ACT_GEN_SET, 0, bitmap_rates);
+ /* Fill VHT MCS rates */
+ if (adapter->fw_api_ver == MWIFIEX_FW_V15) {
+ bitmap_rates[10] = mask->control[band].vht_mcs[0];
+ if (adapter->hw_dev_mcs_support == HT_STREAM_2X2)
+ bitmap_rates[11] = mask->control[band].vht_mcs[1];
+ }
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG,
+ HostCmd_ACT_GEN_SET, 0, bitmap_rates, true);
}
/*
@@ -1216,14 +1222,14 @@ static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold);
subsc_evt.bcn_l_rssi_cfg.evt_freq = 1;
subsc_evt.bcn_h_rssi_cfg.evt_freq = 1;
- return mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
- 0, 0, &subsc_evt);
+ return mwifiex_send_cmd(priv,
+ HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+ 0, 0, &subsc_evt, true);
} else {
subsc_evt.action = HostCmd_ACT_BITWISE_CLR;
- return mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
- 0, 0, &subsc_evt);
+ return mwifiex_send_cmd(priv,
+ HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+ 0, 0, &subsc_evt, true);
}
return 0;
@@ -1276,10 +1282,9 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
if (!mac || is_broadcast_ether_addr(mac)) {
wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
list_for_each_entry(sta_node, &priv->sta_list, list) {
- if (mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_UAP_STA_DEAUTH,
- HostCmd_ACT_GEN_SET, 0,
- sta_node->mac_addr))
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
+ HostCmd_ACT_GEN_SET, 0,
+ sta_node->mac_addr, true))
return -1;
mwifiex_uap_del_sta_data(priv, sta_node);
}
@@ -1289,10 +1294,9 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
sta_node = mwifiex_get_sta_entry(priv, mac);
spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
if (sta_node) {
- if (mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_UAP_STA_DEAUTH,
- HostCmd_ACT_GEN_SET, 0,
- sta_node->mac_addr))
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
+ HostCmd_ACT_GEN_SET, 0,
+ sta_node->mac_addr, true))
return -1;
mwifiex_uap_del_sta_data(priv, sta_node);
}
@@ -1328,13 +1332,40 @@ mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
tx_ant = RF_ANTENNA_AUTO;
rx_ant = RF_ANTENNA_AUTO;
}
+ } else {
+ struct ieee80211_sta_ht_cap *ht_info;
+ int rx_mcs_supp;
+ enum ieee80211_band band;
+
+ if ((tx_ant == 0x1 && rx_ant == 0x1)) {
+ adapter->user_dev_mcs_support = HT_STREAM_1X1;
+ if (adapter->is_hw_11ac_capable)
+ adapter->usr_dot_11ac_mcs_support =
+ MWIFIEX_11AC_MCS_MAP_1X1;
+ } else {
+ adapter->user_dev_mcs_support = HT_STREAM_2X2;
+ if (adapter->is_hw_11ac_capable)
+ adapter->usr_dot_11ac_mcs_support =
+ MWIFIEX_11AC_MCS_MAP_2X2;
+ }
+
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ if (!adapter->wiphy->bands[band])
+ continue;
+
+ ht_info = &adapter->wiphy->bands[band]->ht_cap;
+ rx_mcs_supp =
+ GET_RXMCSSUPP(adapter->user_dev_mcs_support);
+ memset(&ht_info->mcs, 0, adapter->number_of_antenna);
+ memset(&ht_info->mcs, 0xff, rx_mcs_supp);
+ }
}
ant_cfg.tx_ant = tx_ant;
ant_cfg.rx_ant = rx_ant;
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_RF_ANTENNA,
- HostCmd_ACT_GEN_SET, 0, &ant_cfg);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_RF_ANTENNA,
+ HostCmd_ACT_GEN_SET, 0, &ant_cfg, true);
}
/* cfg80211 operation handler for stop ap.
@@ -1349,8 +1380,8 @@ static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
priv->ap_11n_enabled = 0;
- if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP,
- HostCmd_ACT_GEN_SET, 0, NULL)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
+ HostCmd_ACT_GEN_SET, 0, NULL, true)) {
wiphy_err(wiphy, "Failed to stop the BSS\n");
return -1;
}
@@ -1416,9 +1447,6 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
config_bands |= BAND_GN;
-
- if (params->chandef.width > NL80211_CHAN_WIDTH_40)
- config_bands |= BAND_GAC;
} else {
bss_cfg->band_cfg = BAND_CONFIG_A;
config_bands = BAND_A;
@@ -1464,16 +1492,16 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout;
}
- if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP,
- HostCmd_ACT_GEN_SET, 0, NULL)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
+ HostCmd_ACT_GEN_SET, 0, NULL, true)) {
wiphy_err(wiphy, "Failed to stop the BSS\n");
kfree(bss_cfg);
return -1;
}
- if (mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_SYS_CONFIG,
- HostCmd_ACT_GEN_SET,
- UAP_BSS_PARAMS_I, bss_cfg)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
+ HostCmd_ACT_GEN_SET,
+ UAP_BSS_PARAMS_I, bss_cfg, false)) {
wiphy_err(wiphy, "Failed to set the SSID\n");
kfree(bss_cfg);
return -1;
@@ -1481,8 +1509,8 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
kfree(bss_cfg);
- if (mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_BSS_START,
- HostCmd_ACT_GEN_SET, 0, NULL)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START,
+ HostCmd_ACT_GEN_SET, 0, NULL, false)) {
wiphy_err(wiphy, "Failed to start the BSS\n");
return -1;
}
@@ -1492,9 +1520,9 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
else
priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
- if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_MAC_CONTROL,
- HostCmd_ACT_GEN_SET, 0,
- &priv->curr_pkt_filter))
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+ HostCmd_ACT_GEN_SET, 0,
+ &priv->curr_pkt_filter, true))
return -1;
return 0;
@@ -1583,8 +1611,9 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
* the function notifies the CFG802.11 subsystem of the new BSS connection.
*/
static int
-mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
- u8 *bssid, int mode, struct ieee80211_channel *channel,
+mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len,
+ const u8 *ssid, const u8 *bssid, int mode,
+ struct ieee80211_channel *channel,
struct cfg80211_connect_params *sme, bool privacy)
{
struct cfg80211_ssid req_ssid;
@@ -1881,7 +1910,8 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
params->privacy);
done:
if (!ret) {
- cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL);
+ cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid,
+ params->chandef.chan, GFP_KERNEL);
dev_dbg(priv->adapter->dev,
"info: joined/created adhoc network with bssid"
" %pM successfully\n", priv->cfg_bssid);
@@ -2070,10 +2100,10 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info,
else
ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40;
- if (ISSUPP_RXSTBC(adapter->hw_dot_11n_dev_cap))
- ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
+ if (adapter->user_dev_mcs_support == HT_STREAM_2X2)
+ ht_info->cap |= 3 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
else
- ht_info->cap &= ~(3 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+ ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap))
ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
@@ -2098,8 +2128,8 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info,
ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU;
ht_info->cap |= IEEE80211_HT_CAP_SM_PS;
- rx_mcs_supp = GET_RXMCSSUPP(adapter->hw_dev_mcs_support);
- /* Set MCS for 1x1 */
+ rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support);
+ /* Set MCS for 1x1/2x2 */
memset(mcs, 0xff, rx_mcs_supp);
/* Clear all the other values */
memset(&mcs[rx_mcs_supp], 0,
@@ -2460,9 +2490,8 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
MWIFIEX_CRITERIA_UNICAST |
MWIFIEX_CRITERIA_MULTICAST;
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_MEF_CFG,
- HostCmd_ACT_GEN_SET, 0,
- &mef_cfg);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG,
+ HostCmd_ACT_GEN_SET, 0, &mef_cfg, true);
kfree(mef_entry);
return ret;
@@ -2574,9 +2603,9 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
if (!coalesce) {
dev_dbg(adapter->dev,
"Disable coalesce and reset all previous rules\n");
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG,
- HostCmd_ACT_GEN_SET, 0,
- &coalesce_cfg);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG,
+ HostCmd_ACT_GEN_SET, 0,
+ &coalesce_cfg, true);
}
coalesce_cfg.num_of_rules = coalesce->n_rules;
@@ -2591,8 +2620,172 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
}
}
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG,
- HostCmd_ACT_GEN_SET, 0, &coalesce_cfg);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &coalesce_cfg, true);
+}
+
+/* cfg80211 ops handler for tdls_mgmt.
+ * Function prepares TDLS action frame packets and forwards them to FW
+ */
+static int
+mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+ u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability,
+ const u8 *extra_ies, size_t extra_ies_len)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ int ret;
+
+ if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+ return -ENOTSUPP;
+
+ /* make sure we are in station mode and connected */
+ if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected))
+ return -ENOTSUPP;
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ dev_dbg(priv->adapter->dev,
+ "Send TDLS Setup Request to %pM status_code=%d\n", peer,
+ status_code);
+ ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ case WLAN_TDLS_SETUP_RESPONSE:
+ dev_dbg(priv->adapter->dev,
+ "Send TDLS Setup Response to %pM status_code=%d\n",
+ peer, status_code);
+ ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ case WLAN_TDLS_SETUP_CONFIRM:
+ dev_dbg(priv->adapter->dev,
+ "Send TDLS Confirm to %pM status_code=%d\n", peer,
+ status_code);
+ ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ case WLAN_TDLS_TEARDOWN:
+ dev_dbg(priv->adapter->dev, "Send TDLS Tear down to %pM\n",
+ peer);
+ ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ dev_dbg(priv->adapter->dev,
+ "Send TDLS Discovery Request to %pM\n", peer);
+ ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ dev_dbg(priv->adapter->dev,
+ "Send TDLS Discovery Response to %pM\n", peer);
+ ret = mwifiex_send_tdls_action_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ extra_ies, extra_ies_len);
+ break;
+ default:
+ dev_warn(priv->adapter->dev,
+ "Unknown TDLS mgmt/action frame %pM\n", peer);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int
+mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+ u8 *peer, enum nl80211_tdls_operation action)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
+ !(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
+ return -ENOTSUPP;
+
+ /* make sure we are in station mode and connected */
+ if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected))
+ return -ENOTSUPP;
+
+ dev_dbg(priv->adapter->dev,
+ "TDLS peer=%pM, oper=%d\n", peer, action);
+
+ switch (action) {
+ case NL80211_TDLS_ENABLE_LINK:
+ action = MWIFIEX_TDLS_ENABLE_LINK;
+ break;
+ case NL80211_TDLS_DISABLE_LINK:
+ action = MWIFIEX_TDLS_DISABLE_LINK;
+ break;
+ case NL80211_TDLS_TEARDOWN:
+ /* shouldn't happen!*/
+ dev_warn(priv->adapter->dev,
+ "tdls_oper: teardown from driver not supported\n");
+ return -EINVAL;
+ case NL80211_TDLS_SETUP:
+ /* shouldn't happen!*/
+ dev_warn(priv->adapter->dev,
+ "tdls_oper: setup from driver not supported\n");
+ return -EINVAL;
+ case NL80211_TDLS_DISCOVERY_REQ:
+ /* shouldn't happen!*/
+ dev_warn(priv->adapter->dev,
+ "tdls_oper: discovery from driver not supported\n");
+ return -EINVAL;
+ default:
+ dev_err(priv->adapter->dev,
+ "tdls_oper: operation not supported\n");
+ return -ENOTSUPP;
+ }
+
+ return mwifiex_tdls_oper(priv, peer, action);
+}
+
+static int
+mwifiex_cfg80211_add_station(struct wiphy *wiphy,
+ struct net_device *dev,
+ u8 *mac, struct station_parameters *params)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+ return -ENOTSUPP;
+
+ /* make sure we are in station mode and connected */
+ if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected)
+ return -ENOTSUPP;
+
+ return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK);
+}
+
+static int
+mwifiex_cfg80211_change_station(struct wiphy *wiphy,
+ struct net_device *dev,
+ u8 *mac, struct station_parameters *params)
+{
+ int ret;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ /* we support change_station handler only for TDLS peers*/
+ if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+ return -ENOTSUPP;
+
+ /* make sure we are in station mode and connected */
+ if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected)
+ return -ENOTSUPP;
+
+ priv->sta_params = params;
+
+ ret = mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CONFIG_LINK);
+ priv->sta_params = NULL;
+
+ return ret;
}
/* station cfg80211 operations */
@@ -2630,6 +2823,10 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
.set_wakeup = mwifiex_cfg80211_set_wakeup,
#endif
.set_coalesce = mwifiex_cfg80211_set_coalesce,
+ .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt,
+ .tdls_oper = mwifiex_cfg80211_tdls_oper,
+ .add_station = mwifiex_cfg80211_add_station,
+ .change_station = mwifiex_cfg80211_change_station,
};
#ifdef CONFIG_PM
@@ -2715,6 +2912,11 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
WIPHY_FLAG_AP_UAPSD |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+
+ if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info))
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
+ WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
+
wiphy->regulatory_flags |=
REGULATORY_CUSTOM_REG |
REGULATORY_STRICT_REG;
@@ -2736,7 +2938,8 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
wiphy->features |= NL80211_FEATURE_HT_IBSS |
NL80211_FEATURE_INACTIVITY_TIMER |
- NL80211_FEATURE_LOW_PRIORITY_SCAN;
+ NL80211_FEATURE_LOW_PRIORITY_SCAN |
+ NL80211_FEATURE_NEED_OBSS_SCAN;
/* Reserve space for mwifiex specific private data for BSS */
wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv);
@@ -2767,17 +2970,17 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
country_code);
}
- mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_GET, FRAG_THRESH_I, &thr);
+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_GET, FRAG_THRESH_I, &thr, true);
wiphy->frag_threshold = thr;
- mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_GET, RTS_THRESH_I, &thr);
+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_GET, RTS_THRESH_I, &thr, true);
wiphy->rts_threshold = thr;
- mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry);
+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true);
wiphy->retry_short = (u8) retry;
- mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry);
+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry, true);
wiphy->retry_long = (u8) retry;
adapter->wiphy = wiphy;
diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c
index 9eefacbc844b..0ddec3d4b059 100644
--- a/drivers/net/wireless/mwifiex/cfp.c
+++ b/drivers/net/wireless/mwifiex/cfp.c
@@ -71,6 +71,95 @@ u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30,
static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 };
+/* For every mcs_rate line, the first 8 bytes are for stream 1x1,
+ * and all 16 bytes are for stream 2x2.
+ */
+static const u16 mcs_rate[4][16] = {
+ /* LGI 40M */
+ { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
+ 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
+
+ /* SGI 40M */
+ { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
+ 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
+
+ /* LGI 20M */
+ { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
+ 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
+
+ /* SGI 20M */
+ { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
+ 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
+};
+
+/* AC rates */
+static const u16 ac_mcs_rate_nss1[8][10] = {
+ /* LG 160M */
+ { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
+ 0x492, 0x57C, 0x618 },
+
+ /* SG 160M */
+ { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
+ 0x514, 0x618, 0x6C6 },
+
+ /* LG 80M */
+ { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F,
+ 0x249, 0x2BE, 0x30C },
+
+ /* SG 80M */
+ { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249,
+ 0x28A, 0x30C, 0x363 },
+
+ /* LG 40M */
+ { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3,
+ 0x10E, 0x144, 0x168 },
+
+ /* SG 40M */
+ { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E,
+ 0x12C, 0x168, 0x190 },
+
+ /* LG 20M */
+ { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 },
+
+ /* SG 20M */
+ { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 },
+};
+
+/* NSS2 note: the value in the table is 2 multiplier of the actual rate */
+static const u16 ac_mcs_rate_nss2[8][10] = {
+ /* LG 160M */
+ { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A,
+ 0x924, 0xAF8, 0xC30 },
+
+ /* SG 160M */
+ { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924,
+ 0xA28, 0xC30, 0xD8B },
+
+ /* LG 80M */
+ { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
+ 0x492, 0x57C, 0x618 },
+
+ /* SG 80M */
+ { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
+ 0x514, 0x618, 0x6C6 },
+
+ /* LG 40M */
+ { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6,
+ 0x21C, 0x288, 0x2D0 },
+
+ /* SG 40M */
+ { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C,
+ 0x258, 0x2D0, 0x320 },
+
+ /* LG 20M */
+ { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104,
+ 0x138, 0x00 },
+
+ /* SG 20M */
+ { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121,
+ 0x15B, 0x00 },
+};
+
struct region_code_mapping {
u8 code;
u8 region[IEEE80211_COUNTRY_STRING_LEN];
@@ -109,95 +198,6 @@ u8 *mwifiex_11d_code_2_region(u8 code)
u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv,
u8 index, u8 ht_info)
{
- /*
- * For every mcs_rate line, the first 8 bytes are for stream 1x1,
- * and all 16 bytes are for stream 2x2.
- */
- u16 mcs_rate[4][16] = {
- /* LGI 40M */
- { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
- 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
-
- /* SGI 40M */
- { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
- 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
-
- /* LGI 20M */
- { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
- 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
-
- /* SGI 20M */
- { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
- 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
- };
- /* AC rates */
- u16 ac_mcs_rate_nss1[8][10] = {
- /* LG 160M */
- { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
- 0x492, 0x57C, 0x618 },
-
- /* SG 160M */
- { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
- 0x514, 0x618, 0x6C6 },
-
- /* LG 80M */
- { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F,
- 0x249, 0x2BE, 0x30C },
-
- /* SG 80M */
- { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249,
- 0x28A, 0x30C, 0x363 },
-
- /* LG 40M */
- { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3,
- 0x10E, 0x144, 0x168 },
-
- /* SG 40M */
- { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E,
- 0x12C, 0x168, 0x190 },
-
- /* LG 20M */
- { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 },
-
- /* SG 20M */
- { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 },
- };
- /* NSS2 note: the value in the table is 2 multiplier of the actual
- * rate
- */
- u16 ac_mcs_rate_nss2[8][10] = {
- /* LG 160M */
- { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A,
- 0x924, 0xAF8, 0xC30 },
-
- /* SG 160M */
- { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924,
- 0xA28, 0xC30, 0xD8B },
-
- /* LG 80M */
- { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
- 0x492, 0x57C, 0x618 },
-
- /* SG 80M */
- { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
- 0x514, 0x618, 0x6C6 },
-
- /* LG 40M */
- { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6,
- 0x21C, 0x288, 0x2D0 },
-
- /* SG 40M */
- { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C,
- 0x258, 0x2D0, 0x320 },
-
- /* LG 20M */
- { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104,
- 0x138, 0x00 },
-
- /* SG 20M */
- { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121,
- 0x15B, 0x00 },
- };
u32 rate = 0;
u8 mcs_index = 0;
u8 bw = 0;
@@ -252,28 +252,8 @@ u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv,
u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv,
u8 index, u8 ht_info)
{
- /* For every mcs_rate line, the first 8 bytes are for stream 1x1,
- * and all 16 bytes are for stream 2x2.
- */
- u16 mcs_rate[4][16] = {
- /* LGI 40M */
- { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
- 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
-
- /* SGI 40M */
- { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
- 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
-
- /* LGI 20M */
- { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
- 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
-
- /* SGI 20M */
- { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
- 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
- };
u32 mcs_num_supp =
- (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8;
+ (priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8;
u32 rate;
if (priv->adapter->is_hw_11ac_capable)
@@ -458,7 +438,6 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
break;
case BAND_G:
case BAND_G | BAND_GN:
- case BAND_G | BAND_GN | BAND_GAC:
dev_dbg(adapter->dev, "info: infra band=%d "
"supported_rates_g\n", adapter->config_bands);
k = mwifiex_copy_rates(rates, k, supported_rates_g,
@@ -469,10 +448,7 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
case BAND_A | BAND_B:
case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN:
case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC:
- case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN |
- BAND_AAC | BAND_GAC:
case BAND_B | BAND_G | BAND_GN:
- case BAND_B | BAND_G | BAND_GN | BAND_GAC:
dev_dbg(adapter->dev, "info: infra band=%d "
"supported_rates_bg\n", adapter->config_bands);
k = mwifiex_copy_rates(rates, k, supported_rates_bg,
@@ -496,7 +472,6 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
sizeof(supported_rates_a));
break;
case BAND_GN:
- case BAND_GN | BAND_GAC:
dev_dbg(adapter->dev, "info: infra band=%d "
"supported_rates_n\n", adapter->config_bands);
k = mwifiex_copy_rates(rates, k, supported_rates_n,
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index 1ddc8b2e3722..1062c918a7bf 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -37,13 +37,12 @@
static void
mwifiex_init_cmd_node(struct mwifiex_private *priv,
struct cmd_ctrl_node *cmd_node,
- u32 cmd_oid, void *data_buf)
+ u32 cmd_oid, void *data_buf, bool sync)
{
cmd_node->priv = priv;
cmd_node->cmd_oid = cmd_oid;
- if (priv->adapter->cmd_wait_q_required) {
- cmd_node->wait_q_enabled = priv->adapter->cmd_wait_q_required;
- priv->adapter->cmd_wait_q_required = false;
+ if (sync) {
+ cmd_node->wait_q_enabled = true;
cmd_node->cmd_wait_q_woken = false;
cmd_node->condition = &cmd_node->cmd_wait_q_woken;
}
@@ -166,8 +165,10 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
dev_err(adapter->dev,
"DNLD_CMD: FW in reset state, ignore cmd %#x\n",
cmd_code);
- mwifiex_complete_cmd(adapter, cmd_node);
+ if (cmd_node->wait_q_enabled)
+ mwifiex_complete_cmd(adapter, cmd_node);
mwifiex_recycle_cmd_node(adapter, cmd_node);
+ queue_work(adapter->workqueue, &adapter->main_work);
return -1;
}
@@ -276,11 +277,11 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+ adapter->seq_num++;
sleep_cfm_buf->seq_num =
cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO
(adapter->seq_num, priv->bss_num,
priv->bss_type)));
- adapter->seq_num++;
if (adapter->iface_type == MWIFIEX_USB) {
sleep_cfm_tmp =
@@ -480,28 +481,7 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter)
}
/*
- * This function is used to send synchronous command to the firmware.
- *
- * it allocates a wait queue for the command and wait for the command
- * response.
- */
-int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no,
- u16 cmd_action, u32 cmd_oid, void *data_buf)
-{
- int ret = 0;
- struct mwifiex_adapter *adapter = priv->adapter;
-
- adapter->cmd_wait_q_required = true;
-
- ret = mwifiex_send_cmd_async(priv, cmd_no, cmd_action, cmd_oid,
- data_buf);
-
- return ret;
-}
-
-
-/*
- * This function prepares a command and asynchronously send it to the firmware.
+ * This function prepares a command and send it to the firmware.
*
* Preparation includes -
* - Sanity tests to make sure the card is still present or the FW
@@ -511,8 +491,8 @@ int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no,
* - Fill up the non-default parameters and buffer pointers
* - Add the command to pending queue
*/
-int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
- u16 cmd_action, u32 cmd_oid, void *data_buf)
+int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no,
+ u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync)
{
int ret;
struct mwifiex_adapter *adapter = priv->adapter;
@@ -529,11 +509,21 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
return -1;
}
+ if (adapter->hs_enabling && cmd_no != HostCmd_CMD_802_11_HS_CFG_ENH) {
+ dev_err(adapter->dev, "PREP_CMD: host entering sleep state\n");
+ return -1;
+ }
+
if (adapter->surprise_removed) {
dev_err(adapter->dev, "PREP_CMD: card is removed\n");
return -1;
}
+ if (adapter->is_cmd_timedout) {
+ dev_err(adapter->dev, "PREP_CMD: FW is in bad state\n");
+ return -1;
+ }
+
if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) {
if (cmd_no != HostCmd_CMD_FUNC_INIT) {
dev_err(adapter->dev, "PREP_CMD: FW in reset state\n");
@@ -550,7 +540,7 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
}
/* Initialize the command node */
- mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf);
+ mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf, sync);
if (!cmd_node->cmd_skb) {
dev_err(adapter->dev, "PREP_CMD: no free cmd buf\n");
@@ -595,7 +585,8 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
}
/* Send command */
- if (cmd_no == HostCmd_CMD_802_11_SCAN) {
+ if (cmd_no == HostCmd_CMD_802_11_SCAN ||
+ cmd_no == HostCmd_CMD_802_11_SCAN_EXT) {
mwifiex_queue_scan_cmd(priv, cmd_node);
} else {
mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true);
@@ -785,7 +776,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
unsigned long flags;
/* Now we got response from FW, cancel the command timer */
- del_timer(&adapter->cmd_timer);
+ del_timer_sync(&adapter->cmd_timer);
if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) {
resp = (struct host_cmd_ds_command *) adapter->upld_buf;
@@ -794,7 +785,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
return -1;
}
- adapter->num_cmd_timeout = 0;
+ adapter->is_cmd_timedout = 0;
resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data;
if (adapter->curr_cmd->cmd_flag & CMD_F_CANCELED) {
@@ -905,8 +896,7 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
struct cmd_ctrl_node *cmd_node;
struct timeval tstamp;
- adapter->num_cmd_timeout++;
- adapter->dbg.num_cmd_timeout++;
+ adapter->is_cmd_timedout = 1;
if (!adapter->curr_cmd) {
dev_dbg(adapter->dev, "cmd: empty curr_cmd\n");
return;
@@ -929,8 +919,8 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
dev_err(adapter->dev, "num_cmd_h2c_failure = %d\n",
adapter->dbg.num_cmd_host_to_card_failure);
- dev_err(adapter->dev, "num_cmd_timeout = %d\n",
- adapter->dbg.num_cmd_timeout);
+ dev_err(adapter->dev, "is_cmd_timedout = %d\n",
+ adapter->is_cmd_timedout);
dev_err(adapter->dev, "num_tx_timeout = %d\n",
adapter->dbg.num_tx_timeout);
@@ -987,13 +977,14 @@ void
mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
{
struct cmd_ctrl_node *cmd_node = NULL, *tmp_node;
- unsigned long flags;
+ unsigned long flags, cmd_flags;
+ struct mwifiex_private *priv;
+ int i;
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
/* Cancel current cmd */
if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) {
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
adapter->curr_cmd->wait_q_enabled = false;
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
adapter->cmd_wait_q.status = -1;
mwifiex_complete_cmd(adapter, adapter->curr_cmd);
}
@@ -1013,6 +1004,7 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
}
spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
/* Cancel all pending scan command */
spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
@@ -1027,9 +1019,21 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
}
spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
- adapter->scan_processing = false;
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+ if (adapter->scan_processing) {
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
+ adapter->scan_processing = false;
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (!priv)
+ continue;
+ if (priv->scan_request) {
+ dev_dbg(adapter->dev, "info: aborting scan\n");
+ cfg80211_scan_done(priv->scan_request, 1);
+ priv->scan_request = NULL;
+ }
+ }
+ }
}
/*
@@ -1048,7 +1052,8 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL;
unsigned long cmd_flags;
unsigned long scan_pending_q_flags;
- bool cancel_scan_cmd = false;
+ struct mwifiex_private *priv;
+ int i;
if ((adapter->curr_cmd) &&
(adapter->curr_cmd->wait_q_enabled)) {
@@ -1074,15 +1079,24 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
spin_lock_irqsave(&adapter->scan_pending_q_lock,
scan_pending_q_flags);
- cancel_scan_cmd = true;
}
spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
scan_pending_q_flags);
- if (cancel_scan_cmd) {
+ if (adapter->scan_processing) {
spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
adapter->scan_processing = false;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (!priv)
+ continue;
+ if (priv->scan_request) {
+ dev_dbg(adapter->dev, "info: aborting scan\n");
+ cfg80211_scan_done(priv->scan_request, 1);
+ priv->scan_request = NULL;
+ }
+ }
}
adapter->cmd_wait_q.status = -1;
}
@@ -1454,7 +1468,10 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
{
struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec;
struct mwifiex_adapter *adapter = priv->adapter;
- int i;
+ struct mwifiex_ie_types_header *tlv;
+ struct hw_spec_fw_api_rev *api_rev;
+ u16 resp_size, api_id;
+ int i, left_len, parsed_len = 0;
adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info);
@@ -1490,6 +1507,7 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
}
adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number);
+ adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff;
adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna);
if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) {
@@ -1498,8 +1516,10 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
/* Copy 11AC cap */
adapter->hw_dot_11ac_dev_cap =
le32_to_cpu(hw_spec->dot_11ac_dev_cap);
- adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap;
- adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap;
+ adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap
+ & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;
+ adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap
+ & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;
/* Copy 11AC mcs */
adapter->hw_dot_11ac_mcs_support =
@@ -1510,6 +1530,46 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
adapter->is_hw_11ac_capable = false;
}
+ resp_size = le16_to_cpu(resp->size) - S_DS_GEN;
+ if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) {
+ /* we have variable HW SPEC information */
+ left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec);
+ while (left_len > sizeof(struct mwifiex_ie_types_header)) {
+ tlv = (void *)&hw_spec->tlvs + parsed_len;
+ switch (le16_to_cpu(tlv->type)) {
+ case TLV_TYPE_FW_API_REV:
+ api_rev = (struct hw_spec_fw_api_rev *)tlv;
+ api_id = le16_to_cpu(api_rev->api_id);
+ switch (api_id) {
+ case KEY_API_VER_ID:
+ adapter->fw_key_api_major_ver =
+ api_rev->major_ver;
+ adapter->fw_key_api_minor_ver =
+ api_rev->minor_ver;
+ dev_dbg(adapter->dev,
+ "fw_key_api v%d.%d\n",
+ adapter->fw_key_api_major_ver,
+ adapter->fw_key_api_minor_ver);
+ break;
+ default:
+ dev_warn(adapter->dev,
+ "Unknown FW api_id: %d\n",
+ api_id);
+ break;
+ }
+ break;
+ default:
+ dev_warn(adapter->dev,
+ "Unknown GET_HW_SPEC TLV type: %#x\n",
+ le16_to_cpu(tlv->type));
+ break;
+ }
+ parsed_len += le16_to_cpu(tlv->len) +
+ sizeof(struct mwifiex_ie_types_header);
+ left_len -= parsed_len;
+ }
+ }
+
dev_dbg(adapter->dev, "info: GET_HW_SPEC: fw_release_number- %#x\n",
adapter->fw_release_number);
dev_dbg(adapter->dev, "info: GET_HW_SPEC: permanent addr: %pM\n",
@@ -1538,6 +1598,7 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap);
adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support;
+ adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support;
if (adapter->if_ops.update_mp_end_port)
adapter->if_ops.update_mp_end_port(adapter,
diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c
index a5f9875cfd6e..b8a49aad12fd 100644
--- a/drivers/net/wireless/mwifiex/debugfs.c
+++ b/drivers/net/wireless/mwifiex/debugfs.c
@@ -85,8 +85,8 @@ static struct mwifiex_debug_data items[] = {
item_addr(hs_activated), 1},
{"num_tx_timeout", item_size(num_tx_timeout),
item_addr(num_tx_timeout), 1},
- {"num_cmd_timeout", item_size(num_cmd_timeout),
- item_addr(num_cmd_timeout), 1},
+ {"is_cmd_timedout", item_size(is_cmd_timedout),
+ item_addr(is_cmd_timedout), 1},
{"timeout_cmd_id", item_size(timeout_cmd_id),
item_addr(timeout_cmd_id), 1},
{"timeout_cmd_act", item_size(timeout_cmd_act),
@@ -493,7 +493,7 @@ mwifiex_regrdwr_write(struct file *file,
{
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *) addr;
- size_t buf_size = min(count, (size_t) (PAGE_SIZE - 1));
+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
int ret;
u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX;
@@ -594,7 +594,7 @@ mwifiex_rdeeprom_write(struct file *file,
{
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *) addr;
- size_t buf_size = min(count, (size_t) (PAGE_SIZE - 1));
+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
int ret = 0;
int offset = -1, bytes = -1;
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
index 3a21bd03d6db..e7b3e16e5d34 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/mwifiex/decl.h
@@ -75,10 +75,16 @@
#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0)
#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1)
+#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2)
#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024
#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128
+#define MWIFIEX_TDLS_DISABLE_LINK 0x00
+#define MWIFIEX_TDLS_ENABLE_LINK 0x01
+#define MWIFIEX_TDLS_CREATE_LINK 0x02
+#define MWIFIEX_TDLS_CONFIG_LINK 0x03
+
enum mwifiex_bss_type {
MWIFIEX_BSS_TYPE_STA = 0,
MWIFIEX_BSS_TYPE_UAP = 1,
@@ -92,6 +98,23 @@ enum mwifiex_bss_role {
MWIFIEX_BSS_ROLE_ANY = 0xff,
};
+enum mwifiex_tdls_status {
+ TDLS_NOT_SETUP = 0,
+ TDLS_SETUP_INPROGRESS,
+ TDLS_SETUP_COMPLETE,
+ TDLS_SETUP_FAILURE,
+ TDLS_LINK_TEARDOWN,
+};
+
+enum mwifiex_tdls_error_code {
+ TDLS_ERR_NO_ERROR = 0,
+ TDLS_ERR_INTERNAL_ERROR,
+ TDLS_ERR_MAX_LINKS_EST,
+ TDLS_ERR_LINK_EXISTS,
+ TDLS_ERR_LINK_NONEXISTENT,
+ TDLS_ERR_PEER_STA_UNREACHABLE = 25,
+};
+
#define BSS_ROLE_BIT_MASK BIT(0)
#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK)
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index 5fa932d5f905..b485dc1ae5eb 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -50,21 +50,23 @@ struct tx_packet_hdr {
#define HOSTCMD_SUPPORTED_RATES 14
#define N_SUPPORTED_RATES 3
#define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN | \
- BAND_AN | BAND_GAC | BAND_AAC)
+ BAND_AN | BAND_AAC)
#define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \
- BIT(12) | BIT(13))
+ BIT(13))
#define IS_SUPPORT_MULTI_BANDS(adapter) \
(adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT)
-/* shift bit 12 and bit 13 in fw_cap_info from the firmware to bit 13 and 14
- * for 11ac so that bit 11 is for GN, bit 12 for AN, bit 13 for GAC, and bit
- * bit 14 for AAC, in order to be compatible with the band capability
- * defined in the driver after right shift of 8 bits.
+/* bit 13: 11ac BAND_AAC
+ * bit 12: reserved for lab testing, will be reused for BAND_AN
+ * bit 11: 11n BAND_GN
+ * bit 10: 11a BAND_A
+ * bit 9: 11g BAND_G
+ * bit 8: 11b BAND_B
+ * Map these bits to band capability by right shifting 8 bits.
*/
#define GET_FW_DEFAULT_BANDS(adapter) \
- (((((adapter->fw_cap_info & 0x3000) << 1) | \
- (adapter->fw_cap_info & ~0xF000)) >> 8) & \
+ (((adapter->fw_cap_info & 0x2f00) >> 8) & \
ALL_802_11_BANDS)
#define HostCmd_WEP_KEY_INDEX_MASK 0x3fff
@@ -77,12 +79,21 @@ enum KEY_TYPE_ID {
KEY_TYPE_ID_WAPI,
KEY_TYPE_ID_AES_CMAC,
};
+
+#define WPA_PN_SIZE 8
+#define KEY_PARAMS_FIXED_LEN 10
+#define KEY_INDEX_MASK 0xf
+#define FW_KEY_API_VER_MAJOR_V2 2
+
#define KEY_MCAST BIT(0)
#define KEY_UNICAST BIT(1)
#define KEY_ENABLED BIT(2)
+#define KEY_DEFAULT BIT(3)
+#define KEY_TX_KEY BIT(4)
+#define KEY_RX_KEY BIT(5)
#define KEY_IGTK BIT(10)
-#define WAPI_KEY_LEN 50
+#define WAPI_KEY_LEN (WLAN_KEY_LEN_SMS4 + PN_LEN + 2)
#define MAX_POLL_TRIES 100
#define MAX_FIRMWARE_POLL_TRIES 100
@@ -130,6 +141,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22)
#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31)
#define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32)
+#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35)
#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42)
#define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44)
#define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45)
@@ -144,6 +156,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82)
#define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83)
#define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84)
+#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86)
+#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87)
#define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93)
#define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94)
#define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104)
@@ -154,6 +168,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145)
#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146)
#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154)
+#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156)
+#define TLV_TYPE_FW_API_REV (PROPRIETARY_TLV_BASE_ID + 199)
#define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048
@@ -176,13 +192,21 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192
#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11))
+#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14))
#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \
IEEE80211_HT_CAP_SM_PS)
+#define MWIFIEX_DEF_11N_TX_BF_CAP 0x09E1E008
+
#define MWIFIEX_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR
+#define GET_RXSTBC(x) (x & IEEE80211_HT_CAP_RX_STBC)
+#define MWIFIEX_RX_STBC1 0x0100
+#define MWIFIEX_RX_STBC12 0x0200
+#define MWIFIEX_RX_STBC123 0x0300
+
/* dev_cap bitmap
* BIT
* 0-16 reserved
@@ -204,6 +228,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29))
#define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8))
#define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22))
+#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30))
/* httxcfg bitmap
* 0 reserved
@@ -216,8 +241,21 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
*/
#define MWIFIEX_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6))
+/* 11AC Tx and Rx MCS map for 1x1 mode:
+ * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1
+ * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 7 streams
+ */
+#define MWIFIEX_11AC_MCS_MAP_1X1 0xfffefffe
+
+/* 11AC Tx and Rx MCS map for 2x2 mode:
+ * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 and 2
+ * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 6 streams
+ */
+#define MWIFIEX_11AC_MCS_MAP_2X2 0xfffafffa
+
#define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f)
#define SETHT_MCS32(x) (x[4] |= 1)
+#define HT_STREAM_1X1 0x11
#define HT_STREAM_2X2 0x22
#define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4))
@@ -226,17 +264,24 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
/* HW_SPEC fw_cap_info */
-#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & (BIT(12)|BIT(13)))
+#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & BIT(13))
#define GET_VHTCAP_CHWDSET(vht_cap_info) ((vht_cap_info >> 2) & 0x3)
#define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3)
#define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \
(2 * (nss - 1)))
-#define NO_NSS_SUPPORT 0x3
-
#define GET_DEVTXMCSMAP(dev_mcs_map) (dev_mcs_map >> 16)
#define GET_DEVRXMCSMAP(dev_mcs_map) (dev_mcs_map & 0xFFFF)
+/* Clear SU Beanformer, MU beanformer, MU beanformee and
+ * sounding dimensions bits
+ */
+#define MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK \
+ (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \
+ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \
+ IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \
+ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK)
+
#define MOD_CLASS_HR_DSSS 0x03
#define MOD_CLASS_OFDM 0x07
#define MOD_CLASS_HT 0x08
@@ -295,10 +340,12 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed
#define HostCmd_CMD_SET_BSS_MODE 0x00f7
#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa
+#define HostCmd_CMD_802_11_SCAN_EXT 0x0107
#define HostCmd_CMD_COALESCE_CFG 0x010a
#define HostCmd_CMD_MGMT_FRAME_REG 0x010c
#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d
#define HostCmd_CMD_11AC_CFG 0x0112
+#define HostCmd_CMD_TDLS_OPER 0x0122
#define PROTOCOL_NO_SECURITY 0x01
#define PROTOCOL_STATIC_WEP 0x02
@@ -440,6 +487,7 @@ enum P2P_MODES {
#define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c
#define EVENT_HOSTWAKE_STAIE 0x0000004d
#define EVENT_CHANNEL_SWITCH_ANN 0x00000050
+#define EVENT_EXT_SCAN_REPORT 0x00000058
#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f
#define EVENT_ID_MASK 0xffff
@@ -468,6 +516,12 @@ enum P2P_MODES {
#define MWIFIEX_CRITERIA_UNICAST BIT(1)
#define MWIFIEX_CRITERIA_MULTICAST BIT(3)
+#define ACT_TDLS_DELETE 0x00
+#define ACT_TDLS_CREATE 0x01
+#define ACT_TDLS_CONFIG 0x02
+
+#define MWIFIEX_FW_V15 15
+
struct mwifiex_ie_types_header {
__le16 type;
__le16 len;
@@ -480,6 +534,7 @@ struct mwifiex_ie_types_data {
#define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01
#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
+#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10
struct txpd {
u8 bss_type;
@@ -676,6 +731,56 @@ struct mwifiex_cmac_param {
u8 key[WLAN_KEY_LEN_AES_CMAC];
} __packed;
+struct mwifiex_wep_param {
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_WEP104];
+} __packed;
+
+struct mwifiex_tkip_param {
+ u8 pn[WPA_PN_SIZE];
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_TKIP];
+} __packed;
+
+struct mwifiex_aes_param {
+ u8 pn[WPA_PN_SIZE];
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_CCMP];
+} __packed;
+
+struct mwifiex_wapi_param {
+ u8 pn[PN_LEN];
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_SMS4];
+} __packed;
+
+struct mwifiex_cmac_aes_param {
+ u8 ipn[IGTK_PN_LEN];
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_AES_CMAC];
+} __packed;
+
+struct mwifiex_ie_type_key_param_set_v2 {
+ __le16 type;
+ __le16 len;
+ u8 mac_addr[ETH_ALEN];
+ u8 key_idx;
+ u8 key_type;
+ __le16 key_info;
+ union {
+ struct mwifiex_wep_param wep;
+ struct mwifiex_tkip_param tkip;
+ struct mwifiex_aes_param aes;
+ struct mwifiex_wapi_param wapi;
+ struct mwifiex_cmac_aes_param cmac_aes;
+ } key_params;
+} __packed;
+
+struct host_cmd_ds_802_11_key_material_v2 {
+ __le16 action;
+ struct mwifiex_ie_type_key_param_set_v2 key_param_set;
+} __packed;
+
struct host_cmd_ds_802_11_key_material {
__le16 action;
struct mwifiex_ie_type_key_param_set key_param_set;
@@ -727,6 +832,17 @@ struct host_cmd_ds_802_11_ps_mode_enh {
} params;
} __packed;
+enum FW_API_VER_ID {
+ KEY_API_VER_ID = 1,
+};
+
+struct hw_spec_fw_api_rev {
+ struct mwifiex_ie_types_header header;
+ __le16 api_id;
+ u8 major_ver;
+ u8 minor_ver;
+} __packed;
+
struct host_cmd_ds_get_hw_spec {
__le16 hw_if_version;
__le16 version;
@@ -748,6 +864,7 @@ struct host_cmd_ds_get_hw_spec {
__le32 reserved_6;
__le32 dot_11ac_dev_cap;
__le32 dot_11ac_mcs_support;
+ u8 tlvs[0];
} __packed;
struct host_cmd_ds_802_11_rssi_info {
@@ -993,6 +1110,7 @@ struct mwifiex_rate_scope {
__le16 hr_dsss_rate_bitmap;
__le16 ofdm_rate_bitmap;
__le16 ht_mcs_rate_bitmap[8];
+ __le16 vht_mcs_rate_bitmap[8];
} __packed;
struct mwifiex_rate_drop_pattern {
@@ -1047,14 +1165,28 @@ struct host_cmd_ds_rf_ant_siso {
__le16 ant_mode;
};
-struct mwifiex_bcn_param {
- u8 bssid[ETH_ALEN];
- u8 rssi;
+struct host_cmd_ds_tdls_oper {
+ __le16 tdls_action;
+ __le16 reason;
+ u8 peer_mac[ETH_ALEN];
+} __packed;
+
+struct mwifiex_fixed_bcn_param {
__le64 timestamp;
__le16 beacon_period;
__le16 cap_info_bitmap;
} __packed;
+struct mwifiex_event_scan_result {
+ __le16 event_id;
+ u8 bss_index;
+ u8 bss_type;
+ u8 more_event;
+ u8 reserved[3];
+ __le16 buf_size;
+ u8 num_of_set;
+} __packed;
+
#define MWIFIEX_USER_SCAN_CHAN_MAX 50
#define MWIFIEX_MAX_SSID_LIST_LENGTH 10
@@ -1124,6 +1256,28 @@ struct host_cmd_ds_802_11_scan_rsp {
u8 bss_desc_and_tlv_buffer[1];
} __packed;
+struct host_cmd_ds_802_11_scan_ext {
+ u32 reserved;
+ u8 tlv_buffer[1];
+} __packed;
+
+struct mwifiex_ie_types_bss_scan_rsp {
+ struct mwifiex_ie_types_header header;
+ u8 bssid[ETH_ALEN];
+ u8 frame_body[1];
+} __packed;
+
+struct mwifiex_ie_types_bss_scan_info {
+ struct mwifiex_ie_types_header header;
+ __le16 rssi;
+ __le16 anpi;
+ u8 cca_busy_fraction;
+ u8 radio_type;
+ u8 channel;
+ u8 reserved;
+ __le64 tsf;
+} __packed;
+
struct host_cmd_ds_802_11_bg_scan_query {
u8 flush;
} __packed;
@@ -1296,6 +1450,11 @@ struct mwifiex_ie_types_vhtcap {
struct ieee80211_vht_cap vht_cap;
} __packed;
+struct mwifiex_ie_types_aid {
+ struct mwifiex_ie_types_header header;
+ __le16 aid;
+} __packed;
+
struct mwifiex_ie_types_oper_mode_ntf {
struct mwifiex_ie_types_header header;
u8 oper_mode;
@@ -1331,6 +1490,11 @@ struct mwifiex_ie_types_extcap {
u8 ext_capab[0];
} __packed;
+struct mwifiex_ie_types_qos_info {
+ struct mwifiex_ie_types_header header;
+ u8 qos_info;
+} __packed;
+
struct host_cmd_ds_mac_reg_access {
__le16 action;
__le16 offset;
@@ -1441,6 +1605,11 @@ struct host_cmd_tlv_rates {
u8 rates[0];
} __packed;
+struct mwifiex_ie_types_bssid_list {
+ struct mwifiex_ie_types_header header;
+ u8 bssid[ETH_ALEN];
+} __packed;
+
struct host_cmd_tlv_bcast_ssid {
struct mwifiex_ie_types_header header;
u8 bcast_ctl;
@@ -1634,6 +1803,7 @@ struct host_cmd_ds_command {
struct host_cmd_ds_802_11_ps_mode_enh psmode_enh;
struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg;
struct host_cmd_ds_802_11_scan scan;
+ struct host_cmd_ds_802_11_scan_ext ext_scan;
struct host_cmd_ds_802_11_scan_rsp scan_resp;
struct host_cmd_ds_802_11_bg_scan_query bg_scan_query;
struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp;
@@ -1653,6 +1823,7 @@ struct host_cmd_ds_command {
struct host_cmd_ds_11n_cfg htcfg;
struct host_cmd_ds_wmm_get_status get_wmm_status;
struct host_cmd_ds_802_11_key_material key_material;
+ struct host_cmd_ds_802_11_key_material_v2 key_material_v2;
struct host_cmd_ds_version_ext verext;
struct host_cmd_ds_mgmt_frame_reg reg_mask;
struct host_cmd_ds_remain_on_chan roc_cfg;
@@ -1671,6 +1842,7 @@ struct host_cmd_ds_command {
struct host_cmd_ds_sta_deauth sta_deauth;
struct host_cmd_11ac_vht_cfg vht_cfg;
struct host_cmd_ds_coalesce_cfg coalesce_cfg;
+ struct host_cmd_ds_tdls_oper tdls_oper;
} params;
} __packed;
diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c
index 81ac001ee741..3bf3d58bbc02 100644
--- a/drivers/net/wireless/mwifiex/ie.c
+++ b/drivers/net/wireless/mwifiex/ie.c
@@ -138,9 +138,9 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
}
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
- return mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_SYS_CONFIG,
- HostCmd_ACT_GEN_SET,
- UAP_CUSTOM_IE_I, ie_list);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
+ HostCmd_ACT_GEN_SET,
+ UAP_CUSTOM_IE_I, ie_list, false);
return 0;
}
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index 1d0a817f2bf0..4ecd0b208ac6 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -137,6 +137,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
priv->csa_expire_time = 0;
priv->del_list_idx = 0;
priv->hs2_enabled = false;
+ memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID);
return mwifiex_add_bss_prio_tbl(priv);
}
@@ -233,7 +234,6 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
adapter->pm_wakeup_fw_try = false;
- adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
adapter->is_hs_configured = false;
@@ -281,6 +281,9 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
adapter->arp_filter_size = 0;
adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX;
adapter->empty_tx_q_cnt = 0;
+ adapter->ext_scan = true;
+ adapter->fw_key_api_major_ver = 0;
+ adapter->fw_key_api_minor_ver = 0;
}
/*
@@ -450,6 +453,7 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
INIT_LIST_HEAD(&priv->sta_list);
+ skb_queue_head_init(&priv->tdls_txq);
spin_lock_init(&priv->tx_ba_stream_tbl_lock);
spin_lock_init(&priv->rx_reorder_tbl_lock);
@@ -615,7 +619,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
/* cancel current command */
if (adapter->curr_cmd) {
dev_warn(adapter->dev, "curr_cmd is still in processing\n");
- del_timer(&adapter->cmd_timer);
+ del_timer_sync(&adapter->cmd_timer);
mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
adapter->curr_cmd = NULL;
}
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h
index 00a95f4c6a6c..ee494db54060 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/mwifiex/ioctl.h
@@ -60,8 +60,7 @@ enum {
BAND_A = 4,
BAND_GN = 8,
BAND_AN = 16,
- BAND_GAC = 32,
- BAND_AAC = 64,
+ BAND_AAC = 32,
};
#define MWIFIEX_WPA_PASSHPHRASE_LEN 64
@@ -86,6 +85,10 @@ struct wep_key {
#define BAND_CONFIG_A 0x01
#define MWIFIEX_SUPPORTED_RATES 14
#define MWIFIEX_SUPPORTED_RATES_EXT 32
+#define MWIFIEX_TDLS_SUPPORTED_RATES 8
+#define MWIFIEX_TDLS_DEF_QOS_CAPAB 0xf
+#define MWIFIEX_PRIO_BK 2
+#define MWIFIEX_PRIO_VI 5
struct mwifiex_uap_bss_param {
u8 channel;
@@ -174,6 +177,7 @@ struct mwifiex_ds_rx_reorder_tbl {
struct mwifiex_ds_tx_ba_stream_tbl {
u16 tid;
u8 ra[ETH_ALEN];
+ u8 amsdu;
};
#define DBG_CMD_NUM 5
@@ -206,7 +210,7 @@ struct mwifiex_debug_info {
u32 num_cmd_assoc_success;
u32 num_cmd_assoc_failure;
u32 num_tx_timeout;
- u32 num_cmd_timeout;
+ u8 is_cmd_timedout;
u16 timeout_cmd_id;
u16 timeout_cmd_act;
u16 last_cmd_id[DBG_CMD_NUM];
@@ -233,7 +237,10 @@ struct mwifiex_ds_encrypt_key {
u8 mac_addr[ETH_ALEN];
u32 is_wapi_key;
u8 pn[PN_LEN]; /* packet number */
+ u8 pn_len;
u8 is_igtk_key;
+ u8 is_current_wep_key;
+ u8 is_rx_seq_valid;
};
struct mwifiex_power_cfg {
@@ -432,4 +439,16 @@ struct mwifiex_ds_coalesce_cfg {
struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES];
};
+struct mwifiex_ds_tdls_oper {
+ u16 tdls_action;
+ u8 peer_mac[ETH_ALEN];
+ u16 capability;
+ u8 qos_info;
+ u8 *ext_capab;
+ u8 ext_capab_len;
+ u8 *supp_rates;
+ u8 supp_rates_len;
+ u8 *ht_capab;
+};
+
#endif /* !_MWIFIEX_IOCTL_H_ */
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 4e4686e6ac09..89dc62a467f4 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -515,8 +515,7 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
!bss_desc->disable_11n && !bss_desc->disable_11ac &&
- (priv->adapter->config_bands & BAND_GAC ||
- priv->adapter->config_bands & BAND_AAC))
+ priv->adapter->config_bands & BAND_AAC)
mwifiex_cmd_append_11ac_tlv(priv, bss_desc, &pos);
/* Append vendor specific IE TLV */
@@ -902,9 +901,9 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
mwifiex_get_active_data_rates(priv, adhoc_start->data_rate);
if ((adapter->adhoc_start_band & BAND_G) &&
(priv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) {
- if (mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL,
- HostCmd_ACT_GEN_SET, 0,
- &priv->curr_pkt_filter)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+ HostCmd_ACT_GEN_SET, 0,
+ &priv->curr_pkt_filter, false)) {
dev_err(adapter->dev,
"ADHOC_S_CMD: G Protection config failed\n");
return -1;
@@ -983,7 +982,7 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
cpu_to_le16(sizeof(struct ieee80211_ht_cap));
radio_type = mwifiex_band_to_radio_type(
priv->adapter->config_bands);
- mwifiex_fill_cap_info(priv, radio_type, ht_cap);
+ mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
if (adapter->sec_chan_offset ==
IEEE80211_HT_PARAM_CHA_SEC_NONE) {
@@ -1074,9 +1073,9 @@ mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv,
priv->
curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON;
- if (mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL,
- HostCmd_ACT_GEN_SET, 0,
- &curr_pkt_filter)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+ HostCmd_ACT_GEN_SET, 0,
+ &curr_pkt_filter, false)) {
dev_err(priv->adapter->dev,
"ADHOC_J_CMD: G Protection config failed\n");
return -1;
@@ -1300,8 +1299,7 @@ int mwifiex_associate(struct mwifiex_private *priv,
if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
!bss_desc->disable_11n && !bss_desc->disable_11ac &&
- (priv->adapter->config_bands & BAND_GAC ||
- priv->adapter->config_bands & BAND_AAC))
+ priv->adapter->config_bands & BAND_AAC)
mwifiex_set_11ac_ba_params(priv);
else
mwifiex_set_ba_params(priv);
@@ -1314,8 +1312,8 @@ int mwifiex_associate(struct mwifiex_private *priv,
retrieval */
priv->assoc_rsp_size = 0;
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_ASSOCIATE,
- HostCmd_ACT_GEN_SET, 0, bss_desc);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_ASSOCIATE,
+ HostCmd_ACT_GEN_SET, 0, bss_desc, true);
}
/*
@@ -1335,14 +1333,13 @@ mwifiex_adhoc_start(struct mwifiex_private *priv,
priv->curr_bss_params.band);
if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
- (priv->adapter->config_bands & BAND_GAC ||
- priv->adapter->config_bands & BAND_AAC))
+ priv->adapter->config_bands & BAND_AAC)
mwifiex_set_11ac_ba_params(priv);
else
mwifiex_set_ba_params(priv);
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_AD_HOC_START,
- HostCmd_ACT_GEN_SET, 0, adhoc_ssid);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_START,
+ HostCmd_ACT_GEN_SET, 0, adhoc_ssid, true);
}
/*
@@ -1376,8 +1373,7 @@ int mwifiex_adhoc_join(struct mwifiex_private *priv,
if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
!bss_desc->disable_11n && !bss_desc->disable_11ac &&
- (priv->adapter->config_bands & BAND_GAC ||
- priv->adapter->config_bands & BAND_AAC))
+ priv->adapter->config_bands & BAND_AAC)
mwifiex_set_11ac_ba_params(priv);
else
mwifiex_set_ba_params(priv);
@@ -1387,8 +1383,8 @@ int mwifiex_adhoc_join(struct mwifiex_private *priv,
dev_dbg(priv->adapter->dev, "info: curr_bss_params.band = %c\n",
priv->curr_bss_params.band);
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_AD_HOC_JOIN,
- HostCmd_ACT_GEN_SET, 0, bss_desc);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_JOIN,
+ HostCmd_ACT_GEN_SET, 0, bss_desc, true);
}
/*
@@ -1407,8 +1403,8 @@ static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac)
else
memcpy(mac_address, mac, ETH_ALEN);
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_DEAUTHENTICATE,
- HostCmd_ACT_GEN_SET, 0, mac_address);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE,
+ HostCmd_ACT_GEN_SET, 0, mac_address, true);
return ret;
}
@@ -1436,19 +1432,31 @@ int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac)
GFP_KERNEL);
break;
case NL80211_IFTYPE_ADHOC:
- return mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_802_11_AD_HOC_STOP,
- HostCmd_ACT_GEN_SET, 0, NULL);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_STOP,
+ HostCmd_ACT_GEN_SET, 0, NULL, true);
case NL80211_IFTYPE_AP:
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP,
- HostCmd_ACT_GEN_SET, 0, NULL);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP,
+ HostCmd_ACT_GEN_SET, 0, NULL, true);
default:
break;
}
return ret;
}
-EXPORT_SYMBOL_GPL(mwifiex_deauthenticate);
+
+/* This function deauthenticates/disconnects from all BSS. */
+void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter)
+{
+ struct mwifiex_private *priv;
+ int i;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv)
+ mwifiex_deauthenticate(priv, NULL);
+ }
+}
+EXPORT_SYMBOL_GPL(mwifiex_deauthenticate_all);
/*
* This function converts band to radio type used in channel TLV.
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index 9d3d2758ec35..77db0886c6e2 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -38,7 +38,8 @@ static void scan_delay_timer_fn(unsigned long data)
if (adapter->surprise_removed)
return;
- if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
+ if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT ||
+ !adapter->scan_processing) {
/*
* Abort scan operation by cancelling all pending scan
* commands
@@ -194,7 +195,7 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter)
if (adapter->if_ops.cleanup_if)
adapter->if_ops.cleanup_if(adapter);
- del_timer(&adapter->cmd_timer);
+ del_timer_sync(&adapter->cmd_timer);
/* Free private structures */
for (i = 0; i < adapter->priv_num; i++) {
@@ -678,8 +679,8 @@ mwifiex_set_mac_address(struct net_device *dev, void *addr)
memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN);
/* Send request to firmware */
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_MAC_ADDRESS,
- HostCmd_ACT_GEN_SET, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS,
+ HostCmd_ACT_GEN_SET, 0, NULL, true);
if (!ret)
memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN);
@@ -871,7 +872,6 @@ mwifiex_add_card(void *card, struct semaphore *sem,
adapter->is_suspended = false;
adapter->hs_activated = false;
init_waitqueue_head(&adapter->hs_activate_wait_q);
- adapter->cmd_wait_q_required = false;
init_waitqueue_head(&adapter->cmd_wait_q.wait);
adapter->cmd_wait_q.status = 0;
adapter->scan_wait_q_woken = false;
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index d8ad554ce39f..d53e1e8c9467 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -59,7 +59,7 @@ enum {
#define MWIFIEX_UPLD_SIZE (2312)
-#define MAX_EVENT_SIZE 1024
+#define MAX_EVENT_SIZE 2048
#define ARP_FILTER_MAX_BUF_SIZE 68
@@ -116,7 +116,7 @@ enum {
#define MWIFIEX_TYPE_DATA 0
#define MWIFIEX_TYPE_EVENT 3
-#define MAX_BITMAP_RATES_SIZE 10
+#define MAX_BITMAP_RATES_SIZE 18
#define MAX_CHANNEL_BAND_BG 14
#define MAX_CHANNEL_BAND_A 165
@@ -145,7 +145,6 @@ struct mwifiex_dbg {
u32 num_cmd_assoc_success;
u32 num_cmd_assoc_failure;
u32 num_tx_timeout;
- u32 num_cmd_timeout;
u16 timeout_cmd_id;
u16 timeout_cmd_act;
u16 last_cmd_id[DBG_CMD_NUM];
@@ -193,6 +192,8 @@ struct mwifiex_add_ba_param {
u32 tx_win_size;
u32 rx_win_size;
u32 timeout;
+ u8 tx_amsdu;
+ u8 rx_amsdu;
};
struct mwifiex_tx_aggr {
@@ -210,6 +211,7 @@ struct mwifiex_ra_list_tbl {
u16 ba_pkt_count;
u8 ba_packet_thr;
u16 total_pkt_count;
+ bool tdls_link;
};
struct mwifiex_tid_tbl {
@@ -262,6 +264,31 @@ struct ieee_types_generic {
u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)];
} __packed;
+struct ieee_types_bss_co_2040 {
+ struct ieee_types_header ieee_hdr;
+ u8 bss_2040co;
+} __packed;
+
+struct ieee_types_extcap {
+ struct ieee_types_header ieee_hdr;
+ u8 ext_capab[8];
+} __packed;
+
+struct ieee_types_vht_cap {
+ struct ieee_types_header ieee_hdr;
+ struct ieee80211_vht_cap vhtcap;
+} __packed;
+
+struct ieee_types_vht_oper {
+ struct ieee_types_header ieee_hdr;
+ struct ieee80211_vht_operation vhtoper;
+} __packed;
+
+struct ieee_types_aid {
+ struct ieee_types_header ieee_hdr;
+ u16 aid;
+} __packed;
+
struct mwifiex_bssdescriptor {
u8 mac_address[ETH_ALEN];
struct cfg80211_ssid ssid;
@@ -443,6 +470,7 @@ struct mwifiex_private {
u8 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;
u8 *wps_ie;
@@ -461,6 +489,7 @@ struct mwifiex_private {
struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID];
struct mwifiex_add_ba_param add_ba_param;
u16 rx_seq[MAX_NUM_TID];
+ u8 tos_to_tid_inv[MAX_NUM_TID];
struct list_head rx_reorder_tbl_ptr;
/* spin lock for rx_reorder_tbl_ptr queue */
spinlock_t rx_reorder_tbl_lock;
@@ -518,6 +547,8 @@ struct mwifiex_private {
unsigned long csa_expire_time;
u8 del_list_idx;
bool hs2_enabled;
+ struct station_parameters *sta_params;
+ struct sk_buff_head tdls_txq;
};
enum mwifiex_ba_status {
@@ -531,6 +562,7 @@ struct mwifiex_tx_ba_stream_tbl {
int tid;
u8 ra[ETH_ALEN];
enum mwifiex_ba_status ba_status;
+ u8 amsdu;
};
struct mwifiex_rx_reorder_tbl;
@@ -545,10 +577,12 @@ struct mwifiex_rx_reorder_tbl {
struct list_head list;
int tid;
u8 ta[ETH_ALEN];
+ int init_win;
int start_win;
int win_size;
void **rx_reorder_ptr;
struct reorder_tmr_cnxt timer_context;
+ u8 amsdu;
u8 flags;
};
@@ -583,17 +617,35 @@ struct mwifiex_bss_priv {
u64 fw_tsf;
};
-/* This is AP specific structure which stores information
- * about associated STA
+struct mwifiex_tdls_capab {
+ __le16 capab;
+ u8 rates[32];
+ u8 rates_len;
+ u8 qos_info;
+ u8 coex_2040;
+ u16 aid;
+ struct ieee80211_ht_cap ht_capb;
+ struct ieee80211_ht_operation ht_oper;
+ struct ieee_types_extcap extcap;
+ struct ieee_types_generic rsn_ie;
+ struct ieee80211_vht_cap vhtcap;
+ struct ieee80211_vht_operation vhtoper;
+};
+
+/* This is AP/TDLS specific structure which stores information
+ * about associated/peer STA
*/
struct mwifiex_sta_node {
struct list_head list;
u8 mac_addr[ETH_ALEN];
u8 is_wmm_enabled;
u8 is_11n_enabled;
+ u8 is_11ac_enabled;
u8 ampdu_sta[MAX_NUM_TID];
u16 rx_seq[MAX_NUM_TID];
u16 max_amsdu;
+ u8 tdls_status;
+ struct mwifiex_tdls_capab tdls_cap;
};
struct mwifiex_if_ops {
@@ -671,7 +723,7 @@ struct mwifiex_adapter {
struct cmd_ctrl_node *curr_cmd;
/* spin lock for command */
spinlock_t mwifiex_cmd_lock;
- u32 num_cmd_timeout;
+ u8 is_cmd_timedout;
u16 last_init_cmd;
struct timer_list cmd_timer;
struct list_head cmd_free_q;
@@ -722,15 +774,16 @@ struct mwifiex_adapter {
u16 hs_activate_wait_q_woken;
wait_queue_head_t hs_activate_wait_q;
bool is_suspended;
+ bool hs_enabling;
u8 event_body[MAX_EVENT_SIZE];
u32 hw_dot_11n_dev_cap;
u8 hw_dev_mcs_support;
+ u8 user_dev_mcs_support;
u8 adhoc_11n_enabled;
u8 sec_chan_offset;
struct mwifiex_dbg dbg;
u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE];
u32 arp_filter_size;
- u16 cmd_wait_q_required;
struct mwifiex_wait_queue cmd_wait_q;
u8 scan_wait_q_woken;
spinlock_t queue_lock; /* lock for tx queues */
@@ -753,6 +806,9 @@ struct mwifiex_adapter {
atomic_t is_tx_received;
atomic_t pending_bridged_pkts;
struct semaphore *card_sem;
+ bool ext_scan;
+ u8 fw_api_ver;
+ u8 fw_key_api_major_ver, fw_key_api_minor_ver;
};
int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
@@ -788,11 +844,8 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter);
int mwifiex_complete_cmd(struct mwifiex_adapter *adapter,
struct cmd_ctrl_node *cmd_node);
-int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
- u16 cmd_action, u32 cmd_oid, void *data_buf);
-
-int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no,
- u16 cmd_action, u32 cmd_oid, void *data_buf);
+int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no,
+ u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync);
void mwifiex_cmd_timeout_func(unsigned long function_context);
@@ -880,6 +933,7 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
void mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason);
u8 mwifiex_band_to_radio_type(u8 band);
int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac);
+void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter);
int mwifiex_adhoc_start(struct mwifiex_private *priv,
struct cfg80211_ssid *adhoc_ssid);
int mwifiex_adhoc_join(struct mwifiex_private *priv,
@@ -938,6 +992,12 @@ mwifiex_set_wmm_params(struct mwifiex_private *priv,
struct cfg80211_ap_settings *params);
void mwifiex_set_ba_params(struct mwifiex_private *priv);
void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv);
+int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ void *data_buf);
+int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv);
+int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv,
+ void *buf);
/*
* This function checks if the queuing is RA based or not.
@@ -1078,7 +1138,7 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
const u8 *key, int key_len, u8 key_index,
const u8 *mac_addr, int disable);
-int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len);
+int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len);
int mwifiex_get_ver_ext(struct mwifiex_private *priv);
@@ -1159,6 +1219,32 @@ void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv);
extern const struct ethtool_ops mwifiex_ethtool_ops;
+void mwifiex_del_all_sta_list(struct mwifiex_private *priv);
+void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac);
+void
+mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
+ int ies_len, struct mwifiex_sta_node *node);
+struct mwifiex_sta_node *
+mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac);
+struct mwifiex_sta_node *
+mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac);
+int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, u8 *peer,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *extra_ies,
+ size_t extra_ies_len);
+int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv,
+ u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *extra_ies,
+ size_t extra_ies_len);
+void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
+ u8 *buf, int len);
+int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action);
+int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac);
+void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv);
+bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv);
+u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band,
+ u32 pri_chan, u8 chan_bw);
+
#ifdef CONFIG_DEBUG_FS
void mwifiex_debugfs_init(void);
void mwifiex_debugfs_remove(void);
diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c
index 7fe7b53fb17a..a7e8b96b2d90 100644
--- a/drivers/net/wireless/mwifiex/pcie.c
+++ b/drivers/net/wireless/mwifiex/pcie.c
@@ -39,20 +39,31 @@ static struct semaphore add_remove_card_sem;
static int
mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb,
- int size, int flags)
+ size_t size, int flags)
{
struct pcie_service_card *card = adapter->card;
- dma_addr_t buf_pa;
+ struct mwifiex_dma_mapping mapping;
- buf_pa = pci_map_single(card->dev, skb->data, size, flags);
- if (pci_dma_mapping_error(card->dev, buf_pa)) {
+ mapping.addr = pci_map_single(card->dev, skb->data, size, flags);
+ if (pci_dma_mapping_error(card->dev, mapping.addr)) {
dev_err(adapter->dev, "failed to map pci memory!\n");
return -1;
}
- memcpy(skb->cb, &buf_pa, sizeof(dma_addr_t));
+ mapping.len = size;
+ memcpy(skb->cb, &mapping, sizeof(mapping));
return 0;
}
+static void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb, int flags)
+{
+ struct pcie_service_card *card = adapter->card;
+ struct mwifiex_dma_mapping mapping;
+
+ MWIFIEX_SKB_PACB(skb, &mapping);
+ pci_unmap_single(card->dev, mapping.addr, mapping.len, flags);
+}
+
/*
* This function reads sleep cookie and checks if FW is ready
*/
@@ -109,6 +120,7 @@ static int mwifiex_pcie_suspend(struct device *dev)
/* Indicate device suspended */
adapter->is_suspended = true;
+ adapter->hs_enabling = false;
return 0;
}
@@ -179,6 +191,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
card->pcie.firmware = data->firmware;
card->pcie.reg = data->reg;
card->pcie.blksz_fw_dl = data->blksz_fw_dl;
+ card->pcie.tx_buf_size = data->tx_buf_size;
}
if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops,
@@ -199,7 +212,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
struct pcie_service_card *card;
struct mwifiex_adapter *adapter;
struct mwifiex_private *priv;
- int i;
card = pci_get_drvdata(pdev);
if (!card)
@@ -218,11 +230,7 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
mwifiex_pcie_resume(&pdev->dev);
#endif
- for (i = 0; i < adapter->priv_num; i++)
- if ((GET_BSS_ROLE(adapter->priv[i]) ==
- MWIFIEX_BSS_ROLE_STA) &&
- adapter->priv[i]->media_connected)
- mwifiex_deauthenticate(adapter->priv[i], NULL);
+ mwifiex_deauthenticate_all(adapter);
priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
@@ -320,6 +328,30 @@ static void mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter)
return;
}
+static void mwifiex_delay_for_sleep_cookie(struct mwifiex_adapter *adapter,
+ u32 max_delay_loop_cnt)
+{
+ struct pcie_service_card *card = adapter->card;
+ u8 *buffer;
+ u32 sleep_cookie, count;
+
+ for (count = 0; count < max_delay_loop_cnt; count++) {
+ buffer = card->cmdrsp_buf->data - INTF_HEADER_LEN;
+ sleep_cookie = *(u32 *)buffer;
+
+ if (sleep_cookie == MWIFIEX_DEF_SLEEP_COOKIE) {
+ dev_dbg(adapter->dev,
+ "sleep cookie found at count %d\n", count);
+ break;
+ }
+ usleep_range(20, 30);
+ }
+
+ if (count >= max_delay_loop_cnt)
+ dev_dbg(adapter->dev,
+ "max count reached while accessing sleep cookie\n");
+}
+
/* This function wakes up the card by reading fw_status register. */
static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
{
@@ -456,7 +488,7 @@ static int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter)
PCI_DMA_FROMDEVICE))
return -1;
- MWIFIEX_SKB_PACB(skb, &buf_pa);
+ buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
dev_dbg(adapter->dev,
"info: RX ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n",
@@ -513,7 +545,7 @@ static int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter)
PCI_DMA_FROMDEVICE))
return -1;
- MWIFIEX_SKB_PACB(skb, &buf_pa);
+ buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
dev_dbg(adapter->dev,
"info: EVT ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n",
@@ -549,8 +581,8 @@ static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter)
desc2 = card->txbd_ring[i];
if (card->tx_buf_list[i]) {
skb = card->tx_buf_list[i];
- pci_unmap_single(card->dev, desc2->paddr,
- skb->len, PCI_DMA_TODEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_TODEVICE);
dev_kfree_skb_any(skb);
}
memset(desc2, 0, sizeof(*desc2));
@@ -558,8 +590,8 @@ static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter)
desc = card->txbd_ring[i];
if (card->tx_buf_list[i]) {
skb = card->tx_buf_list[i];
- pci_unmap_single(card->dev, desc->paddr,
- skb->len, PCI_DMA_TODEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_TODEVICE);
dev_kfree_skb_any(skb);
}
memset(desc, 0, sizeof(*desc));
@@ -587,8 +619,8 @@ static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter)
desc2 = card->rxbd_ring[i];
if (card->rx_buf_list[i]) {
skb = card->rx_buf_list[i];
- pci_unmap_single(card->dev, desc2->paddr,
- skb->len, PCI_DMA_FROMDEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(skb);
}
memset(desc2, 0, sizeof(*desc2));
@@ -596,8 +628,8 @@ static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter)
desc = card->rxbd_ring[i];
if (card->rx_buf_list[i]) {
skb = card->rx_buf_list[i];
- pci_unmap_single(card->dev, desc->paddr,
- skb->len, PCI_DMA_FROMDEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(skb);
}
memset(desc, 0, sizeof(*desc));
@@ -622,8 +654,8 @@ static void mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter)
desc = card->evtbd_ring[i];
if (card->evt_buf_list[i]) {
skb = card->evt_buf_list[i];
- pci_unmap_single(card->dev, desc->paddr, MAX_EVENT_SIZE,
- PCI_DMA_FROMDEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(skb);
}
card->evt_buf_list[i] = NULL;
@@ -861,7 +893,6 @@ static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter)
static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter)
{
struct pcie_service_card *card;
- dma_addr_t buf_pa;
if (!adapter)
return 0;
@@ -869,16 +900,14 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter)
card = adapter->card;
if (card && card->cmdrsp_buf) {
- MWIFIEX_SKB_PACB(card->cmdrsp_buf, &buf_pa);
- pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
- PCI_DMA_FROMDEVICE);
+ mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf,
+ PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(card->cmdrsp_buf);
}
if (card && card->cmd_buf) {
- MWIFIEX_SKB_PACB(card->cmd_buf, &buf_pa);
- pci_unmap_single(card->dev, buf_pa, card->cmd_buf->len,
- PCI_DMA_TODEVICE);
+ mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
+ PCI_DMA_TODEVICE);
}
return 0;
}
@@ -956,7 +985,6 @@ static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter)
static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
{
struct sk_buff *skb;
- dma_addr_t buf_pa;
u32 wrdoneidx, rdptr, num_tx_buffs, unmap_count = 0;
struct mwifiex_pcie_buf_desc *desc;
struct mwifiex_pfu_buf_desc *desc2;
@@ -986,13 +1014,13 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
reg->tx_start_ptr;
skb = card->tx_buf_list[wrdoneidx];
+
if (skb) {
dev_dbg(adapter->dev,
"SEND COMP: Detach skb %p at txbd_rdidx=%d\n",
skb, wrdoneidx);
- MWIFIEX_SKB_PACB(skb, &buf_pa);
- pci_unmap_single(card->dev, buf_pa, skb->len,
- PCI_DMA_TODEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_TODEVICE);
unmap_count++;
@@ -1006,7 +1034,7 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
card->tx_buf_list[wrdoneidx] = NULL;
if (reg->pfu_enabled) {
- desc2 = (void *)card->txbd_ring[wrdoneidx];
+ desc2 = card->txbd_ring[wrdoneidx];
memset(desc2, 0, sizeof(*desc2));
} else {
desc = card->txbd_ring[wrdoneidx];
@@ -1082,16 +1110,16 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
tmp = (__le16 *)&payload[2];
*tmp = cpu_to_le16(MWIFIEX_TYPE_DATA);
- if (mwifiex_map_pci_memory(adapter, skb, skb->len ,
+ if (mwifiex_map_pci_memory(adapter, skb, skb->len,
PCI_DMA_TODEVICE))
return -1;
wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr;
- MWIFIEX_SKB_PACB(skb, &buf_pa);
+ buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
card->tx_buf_list[wrindx] = skb;
if (reg->pfu_enabled) {
- desc2 = (void *)card->txbd_ring[wrindx];
+ desc2 = card->txbd_ring[wrindx];
desc2->paddr = buf_pa;
desc2->len = (u16)skb->len;
desc2->frag_len = (u16)skb->len;
@@ -1162,8 +1190,7 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
return -EINPROGRESS;
done_unmap:
- MWIFIEX_SKB_PACB(skb, &buf_pa);
- pci_unmap_single(card->dev, buf_pa, skb->len, PCI_DMA_TODEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
card->tx_buf_list[wrindx] = NULL;
if (reg->pfu_enabled)
memset(desc2, 0, sizeof(*desc2));
@@ -1217,9 +1244,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
if (!skb_data)
return -ENOMEM;
- MWIFIEX_SKB_PACB(skb_data, &buf_pa);
- pci_unmap_single(card->dev, buf_pa, MWIFIEX_RX_DATA_BUF_SIZE,
- PCI_DMA_FROMDEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb_data, PCI_DMA_FROMDEVICE);
card->rx_buf_list[rd_index] = NULL;
/* Get data length from interface header -
@@ -1246,7 +1271,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
PCI_DMA_FROMDEVICE))
return -1;
- MWIFIEX_SKB_PACB(skb_tmp, &buf_pa);
+ buf_pa = MWIFIEX_SKB_DMA_ADDR(skb_tmp);
dev_dbg(adapter->dev,
"RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n",
@@ -1254,7 +1279,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
card->rx_buf_list[rd_index] = skb_tmp;
if (reg->pfu_enabled) {
- desc2 = (void *)card->rxbd_ring[rd_index];
+ desc2 = card->rxbd_ring[rd_index];
desc2->paddr = buf_pa;
desc2->len = skb_tmp->len;
desc2->frag_len = skb_tmp->len;
@@ -1322,7 +1347,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE))
return -1;
- MWIFIEX_SKB_PACB(skb, &buf_pa);
+ buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
/* Write the lower 32bits of the physical address to low command
* address scratch register
@@ -1331,8 +1356,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
dev_err(adapter->dev,
"%s: failed to write download command to boot code.\n",
__func__);
- pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
- PCI_DMA_TODEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
return -1;
}
@@ -1344,8 +1368,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
dev_err(adapter->dev,
"%s: failed to write download command to boot code.\n",
__func__);
- pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
- PCI_DMA_TODEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
return -1;
}
@@ -1354,8 +1377,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
dev_err(adapter->dev,
"%s: failed to write command len to cmd_size scratch reg\n",
__func__);
- pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
- PCI_DMA_TODEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
return -1;
}
@@ -1364,8 +1386,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
CPU_INTR_DOOR_BELL)) {
dev_err(adapter->dev,
"%s: failed to assert door-bell intr\n", __func__);
- pci_unmap_single(card->dev, buf_pa,
- MWIFIEX_UPLD_SIZE, PCI_DMA_TODEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
return -1;
}
@@ -1439,7 +1460,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
*/
if (card->cmdrsp_buf) {
- MWIFIEX_SKB_PACB(card->cmdrsp_buf, &cmdrsp_buf_pa);
+ cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf);
/* Write the lower 32bits of the cmdrsp buffer physical
address */
if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo,
@@ -1460,7 +1481,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
}
}
- MWIFIEX_SKB_PACB(card->cmd_buf, &cmd_buf_pa);
+ cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf);
/* Write the lower 32bits of the physical address to reg->cmd_addr_lo */
if (mwifiex_write_reg(adapter, reg->cmd_addr_lo,
(u32)cmd_buf_pa)) {
@@ -1514,13 +1535,17 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
int count = 0;
u16 rx_len;
__le16 pkt_len;
- dma_addr_t buf_pa;
dev_dbg(adapter->dev, "info: Rx CMD Response\n");
- MWIFIEX_SKB_PACB(skb, &buf_pa);
- pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
- PCI_DMA_FROMDEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_FROMDEVICE);
+
+ /* Unmap the command as a response has been received. */
+ if (card->cmd_buf) {
+ mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
+ PCI_DMA_TODEVICE);
+ card->cmd_buf = NULL;
+ }
pkt_len = *((__le16 *)skb->data);
rx_len = le16_to_cpu(pkt_len);
@@ -1539,6 +1564,8 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
"Write register failed\n");
return -1;
}
+ mwifiex_delay_for_sleep_cookie(adapter,
+ MWIFIEX_MAX_DELAY_COUNT);
while (reg->sleep_cookie && (count++ < 10) &&
mwifiex_pcie_ok_to_access_hw(adapter))
usleep_range(50, 60);
@@ -1552,8 +1579,6 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
PCI_DMA_FROMDEVICE))
return -1;
-
- MWIFIEX_SKB_PACB(skb, &buf_pa);
} else if (mwifiex_pcie_ok_to_access_hw(adapter)) {
adapter->curr_cmd->resp_skb = skb;
adapter->cmd_resp_received = true;
@@ -1588,8 +1613,6 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter,
struct sk_buff *skb)
{
struct pcie_service_card *card = adapter->card;
- dma_addr_t buf_pa;
- struct sk_buff *skb_tmp;
if (skb) {
card->cmdrsp_buf = skb;
@@ -1599,14 +1622,6 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter,
return -1;
}
- skb_tmp = card->cmd_buf;
- if (skb_tmp) {
- MWIFIEX_SKB_PACB(skb_tmp, &buf_pa);
- pci_unmap_single(card->dev, buf_pa, skb_tmp->len,
- PCI_DMA_FROMDEVICE);
- card->cmd_buf = NULL;
- }
-
return 0;
}
@@ -1619,7 +1634,6 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter)
const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
u32 wrptr, event;
- dma_addr_t buf_pa;
struct mwifiex_evt_buf_desc *desc;
if (!mwifiex_pcie_ok_to_access_hw(adapter))
@@ -1655,9 +1669,7 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter)
dev_dbg(adapter->dev, "info: Read Index: %d\n", rdptr);
skb_cmd = card->evt_buf_list[rdptr];
- MWIFIEX_SKB_PACB(skb_cmd, &buf_pa);
- pci_unmap_single(card->dev, buf_pa, MAX_EVENT_SIZE,
- PCI_DMA_FROMDEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb_cmd, PCI_DMA_FROMDEVICE);
/* Take the pointer and set it to event pointer in adapter
and will return back after event handling callback */
@@ -1703,7 +1715,6 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
int ret = 0;
u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
u32 wrptr;
- dma_addr_t buf_pa;
struct mwifiex_evt_buf_desc *desc;
if (!skb)
@@ -1728,11 +1739,9 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
MAX_EVENT_SIZE,
PCI_DMA_FROMDEVICE))
return -1;
- MWIFIEX_SKB_PACB(skb, &buf_pa);
card->evt_buf_list[rdptr] = skb;
- MWIFIEX_SKB_PACB(skb, &buf_pa);
desc = card->evtbd_ring[rdptr];
- desc->paddr = buf_pa;
+ desc->paddr = MWIFIEX_SKB_DMA_ADDR(skb);
desc->len = (u16)skb->len;
desc->flags = 0;
skb = NULL;
@@ -1782,7 +1791,6 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
struct sk_buff *skb;
u32 txlen, tx_blocks = 0, tries, len;
u32 block_retry_cnt = 0;
- dma_addr_t buf_pa;
struct pcie_service_card *card = adapter->card;
const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
@@ -1880,8 +1888,6 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
goto done;
}
- MWIFIEX_SKB_PACB(skb, &buf_pa);
-
/* Wait for the command done interrupt */
do {
if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS,
@@ -1889,16 +1895,15 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
dev_err(adapter->dev, "%s: Failed to read "
"interrupt status during fw dnld.\n",
__func__);
- pci_unmap_single(card->dev, buf_pa, skb->len,
- PCI_DMA_TODEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb,
+ PCI_DMA_TODEVICE);
ret = -1;
goto done;
}
} while ((ireg_intr & CPU_INTR_DOOR_BELL) ==
CPU_INTR_DOOR_BELL);
- pci_unmap_single(card->dev, buf_pa, skb->len,
- PCI_DMA_TODEVICE);
+ mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
offset += txlen;
} while (true);
@@ -2338,6 +2343,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
}
adapter->dev = &pdev->dev;
+ adapter->tx_buf_size = card->pcie.tx_buf_size;
strcpy(adapter->fw_name, card->pcie.firmware);
return 0;
diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h
index d322ab8604ea..e8ec561f8a64 100644
--- a/drivers/net/wireless/mwifiex/pcie.h
+++ b/drivers/net/wireless/mwifiex/pcie.h
@@ -97,6 +97,8 @@
#define MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD 256
/* FW awake cookie after FW ready */
#define FW_AWAKE_COOKIE (0xAA55AA55)
+#define MWIFIEX_DEF_SLEEP_COOKIE 0xBEEFBEEF
+#define MWIFIEX_MAX_DELAY_COUNT 5
struct mwifiex_pcie_card_reg {
u16 cmd_addr_lo;
@@ -195,18 +197,21 @@ struct mwifiex_pcie_device {
const char *firmware;
const struct mwifiex_pcie_card_reg *reg;
u16 blksz_fw_dl;
+ u16 tx_buf_size;
};
static const struct mwifiex_pcie_device mwifiex_pcie8766 = {
.firmware = PCIE8766_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_8766,
.blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
};
static const struct mwifiex_pcie_device mwifiex_pcie8897 = {
.firmware = PCIE8897_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_8897,
.blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
};
struct mwifiex_evt_buf_desc {
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index 668547c2de84..7b3af3d29ded 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -591,11 +591,13 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
*chan_tlv_out,
struct mwifiex_chan_scan_param_set *scan_chan_list)
{
+ struct mwifiex_adapter *adapter = priv->adapter;
int ret = 0;
struct mwifiex_chan_scan_param_set *tmp_chan_list;
struct mwifiex_chan_scan_param_set *start_chan;
-
- u32 tlv_idx, rates_size;
+ struct cmd_ctrl_node *cmd_node, *tmp_node;
+ unsigned long flags;
+ u32 tlv_idx, rates_size, cmd_no;
u32 total_scan_time;
u32 done_early;
u8 radio_type;
@@ -733,9 +735,13 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
/* Send the scan command to the firmware with the specified
cfg */
- ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SCAN,
- HostCmd_ACT_GEN_SET, 0,
- scan_cfg_out);
+ if (priv->adapter->ext_scan)
+ cmd_no = HostCmd_CMD_802_11_SCAN_EXT;
+ else
+ cmd_no = HostCmd_CMD_802_11_SCAN;
+
+ ret = mwifiex_send_cmd(priv, cmd_no, HostCmd_ACT_GEN_SET,
+ 0, scan_cfg_out, false);
/* rate IE is updated per scan command but same starting
* pointer is used each time so that rate IE from earlier
@@ -744,8 +750,19 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
scan_cfg_out->tlv_buf_len -=
sizeof(struct mwifiex_ie_types_header) + rates_size;
- if (ret)
+ if (ret) {
+ spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+ list_for_each_entry_safe(cmd_node, tmp_node,
+ &adapter->scan_pending_q,
+ list) {
+ list_del(&cmd_node->list);
+ cmd_node->wait_q_enabled = false;
+ mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+ }
+ spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+ flags);
break;
+ }
}
if (ret)
@@ -786,6 +803,7 @@ mwifiex_config_scan(struct mwifiex_private *priv,
struct mwifiex_adapter *adapter = priv->adapter;
struct mwifiex_ie_types_num_probes *num_probes_tlv;
struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
+ struct mwifiex_ie_types_bssid_list *bssid_tlv;
u8 *tlv_pos;
u32 num_probes;
u32 ssid_len;
@@ -848,6 +866,17 @@ mwifiex_config_scan(struct mwifiex_private *priv,
user_scan_in->specific_bssid,
sizeof(scan_cfg_out->specific_bssid));
+ if (adapter->ext_scan &&
+ !is_zero_ether_addr(scan_cfg_out->specific_bssid)) {
+ bssid_tlv =
+ (struct mwifiex_ie_types_bssid_list *)tlv_pos;
+ bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID);
+ bssid_tlv->header.len = cpu_to_le16(ETH_ALEN);
+ memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid,
+ ETH_ALEN);
+ tlv_pos += sizeof(struct mwifiex_ie_types_bssid_list);
+ }
+
for (i = 0; i < user_scan_in->num_ssids; i++) {
ssid_len = user_scan_in->ssid_list[i].ssid_len;
@@ -941,7 +970,7 @@ mwifiex_config_scan(struct mwifiex_private *priv,
cpu_to_le16(sizeof(struct ieee80211_ht_cap));
radio_type =
mwifiex_band_to_radio_type(priv->adapter->config_bands);
- mwifiex_fill_cap_info(priv, radio_type, ht_cap);
+ mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
tlv_pos += sizeof(struct mwifiex_ie_types_htcap);
}
@@ -1576,6 +1605,228 @@ done:
return 0;
}
+static int
+mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
+ u32 *bytes_left, u64 fw_tsf, u8 *radio_type,
+ bool ext_scan, s32 rssi_val)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct mwifiex_chan_freq_power *cfp;
+ struct cfg80211_bss *bss;
+ u8 bssid[ETH_ALEN];
+ s32 rssi;
+ const u8 *ie_buf;
+ size_t ie_len;
+ u16 channel = 0;
+ u16 beacon_size = 0;
+ u32 curr_bcn_bytes;
+ u32 freq;
+ u16 beacon_period;
+ u16 cap_info_bitmap;
+ u8 *current_ptr;
+ u64 timestamp;
+ struct mwifiex_fixed_bcn_param *bcn_param;
+ struct mwifiex_bss_priv *bss_priv;
+
+ if (*bytes_left >= sizeof(beacon_size)) {
+ /* Extract & convert beacon size from command buffer */
+ memcpy(&beacon_size, *bss_info, sizeof(beacon_size));
+ *bytes_left -= sizeof(beacon_size);
+ *bss_info += sizeof(beacon_size);
+ }
+
+ if (!beacon_size || beacon_size > *bytes_left) {
+ *bss_info += *bytes_left;
+ *bytes_left = 0;
+ return -EFAULT;
+ }
+
+ /* Initialize the current working beacon pointer for this BSS
+ * iteration
+ */
+ current_ptr = *bss_info;
+
+ /* Advance the return beacon pointer past the current beacon */
+ *bss_info += beacon_size;
+ *bytes_left -= beacon_size;
+
+ curr_bcn_bytes = beacon_size;
+
+ /* First 5 fields are bssid, RSSI(for legacy scan only),
+ * time stamp, beacon interval, and capability information
+ */
+ if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) +
+ sizeof(struct mwifiex_fixed_bcn_param)) {
+ dev_err(adapter->dev, "InterpretIE: not enough bytes left\n");
+ return -EFAULT;
+ }
+
+ memcpy(bssid, current_ptr, ETH_ALEN);
+ current_ptr += ETH_ALEN;
+ curr_bcn_bytes -= ETH_ALEN;
+
+ if (!ext_scan) {
+ rssi = (s32) *current_ptr;
+ rssi = (-rssi) * 100; /* Convert dBm to mBm */
+ current_ptr += sizeof(u8);
+ curr_bcn_bytes -= sizeof(u8);
+ dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi);
+ } else {
+ rssi = rssi_val;
+ }
+
+ bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr;
+ current_ptr += sizeof(*bcn_param);
+ curr_bcn_bytes -= sizeof(*bcn_param);
+
+ timestamp = le64_to_cpu(bcn_param->timestamp);
+ beacon_period = le16_to_cpu(bcn_param->beacon_period);
+
+ cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap);
+ dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n",
+ cap_info_bitmap);
+
+ /* Rest of the current buffer are IE's */
+ ie_buf = current_ptr;
+ ie_len = curr_bcn_bytes;
+ dev_dbg(adapter->dev, "info: InterpretIE: IELength for this AP = %d\n",
+ curr_bcn_bytes);
+
+ while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) {
+ u8 element_id, element_len;
+
+ element_id = *current_ptr;
+ element_len = *(current_ptr + 1);
+ if (curr_bcn_bytes < element_len +
+ sizeof(struct ieee_types_header)) {
+ dev_err(adapter->dev,
+ "%s: bytes left < IE length\n", __func__);
+ return -EFAULT;
+ }
+ if (element_id == WLAN_EID_DS_PARAMS) {
+ channel = *(current_ptr +
+ sizeof(struct ieee_types_header));
+ break;
+ }
+
+ current_ptr += element_len + sizeof(struct ieee_types_header);
+ curr_bcn_bytes -= element_len +
+ sizeof(struct ieee_types_header);
+ }
+
+ if (channel) {
+ struct ieee80211_channel *chan;
+ u8 band;
+
+ /* Skip entry if on csa closed channel */
+ if (channel == priv->csa_chan) {
+ dev_dbg(adapter->dev,
+ "Dropping entry on csa closed channel\n");
+ return 0;
+ }
+
+ band = BAND_G;
+ if (radio_type)
+ band = mwifiex_radio_type_to_band(*radio_type &
+ (BIT(0) | BIT(1)));
+
+ cfp = mwifiex_get_cfp(priv, band, channel, 0);
+
+ freq = cfp ? cfp->freq : 0;
+
+ chan = ieee80211_get_channel(priv->wdev->wiphy, freq);
+
+ if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
+ bss = cfg80211_inform_bss(priv->wdev->wiphy,
+ chan, bssid, timestamp,
+ cap_info_bitmap, beacon_period,
+ ie_buf, ie_len, rssi, GFP_KERNEL);
+ bss_priv = (struct mwifiex_bss_priv *)bss->priv;
+ bss_priv->band = band;
+ bss_priv->fw_tsf = fw_tsf;
+ if (priv->media_connected &&
+ !memcmp(bssid, priv->curr_bss_params.bss_descriptor
+ .mac_address, ETH_ALEN))
+ mwifiex_update_curr_bss_params(priv, bss);
+ cfg80211_put_bss(priv->wdev->wiphy, bss);
+ }
+ } else {
+ dev_dbg(adapter->dev, "missing BSS channel IE\n");
+ }
+
+ return 0;
+}
+
+static void mwifiex_check_next_scan_command(struct mwifiex_private *priv)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct cmd_ctrl_node *cmd_node;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+ if (list_empty(&adapter->scan_pending_q)) {
+ spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+ adapter->scan_processing = false;
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+
+ /* Need to indicate IOCTL complete */
+ if (adapter->curr_cmd->wait_q_enabled) {
+ adapter->cmd_wait_q.status = 0;
+ if (!priv->scan_request) {
+ dev_dbg(adapter->dev,
+ "complete internal scan\n");
+ mwifiex_complete_cmd(adapter,
+ adapter->curr_cmd);
+ }
+ }
+ if (priv->report_scan_result)
+ priv->report_scan_result = false;
+
+ if (priv->scan_request) {
+ dev_dbg(adapter->dev, "info: notifying scan done\n");
+ cfg80211_scan_done(priv->scan_request, 0);
+ priv->scan_request = NULL;
+ } else {
+ priv->scan_aborting = false;
+ dev_dbg(adapter->dev, "info: scan already aborted\n");
+ }
+ } else {
+ if ((priv->scan_aborting && !priv->scan_request) ||
+ priv->scan_block) {
+ spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+ flags);
+ adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT;
+ mod_timer(&priv->scan_delay_timer, jiffies);
+ dev_dbg(priv->adapter->dev,
+ "info: %s: triggerring scan abort\n", __func__);
+ } else if (!mwifiex_wmm_lists_empty(adapter) &&
+ (priv->scan_request && (priv->scan_request->flags &
+ NL80211_SCAN_FLAG_LOW_PRIORITY))) {
+ spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+ flags);
+ adapter->scan_delay_cnt = 1;
+ mod_timer(&priv->scan_delay_timer, jiffies +
+ msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
+ dev_dbg(priv->adapter->dev,
+ "info: %s: deferring scan\n", __func__);
+ } else {
+ /* Get scan command from scan_pending_q and put to
+ * cmd_pending_q
+ */
+ cmd_node = list_first_entry(&adapter->scan_pending_q,
+ struct cmd_ctrl_node, list);
+ list_del(&cmd_node->list);
+ spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+ flags);
+ mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
+ true);
+ }
+ }
+
+ return;
+}
+
/*
* This function handles the command response of scan.
*
@@ -1600,7 +1851,6 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
{
int ret = 0;
struct mwifiex_adapter *adapter = priv->adapter;
- struct cmd_ctrl_node *cmd_node;
struct host_cmd_ds_802_11_scan_rsp *scan_rsp;
struct mwifiex_ie_types_data *tlv_data;
struct mwifiex_ie_types_tsf_timestamp *tsf_tlv;
@@ -1609,12 +1859,11 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
u32 bytes_left;
u32 idx;
u32 tlv_buf_size;
- struct mwifiex_chan_freq_power *cfp;
struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv;
struct chan_band_param_set *chan_band;
u8 is_bgscan_resp;
- unsigned long flags;
- struct cfg80211_bss *bss;
+ __le64 fw_tsf = 0;
+ u8 *radio_type;
is_bgscan_resp = (le16_to_cpu(resp->command)
== HostCmd_CMD_802_11_BG_SCAN_QUERY);
@@ -1676,220 +1925,194 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
&chan_band_tlv);
for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) {
- u8 bssid[ETH_ALEN];
- s32 rssi;
- const u8 *ie_buf;
- size_t ie_len;
- u16 channel = 0;
- __le64 fw_tsf = 0;
- u16 beacon_size = 0;
- u32 curr_bcn_bytes;
- u32 freq;
- u16 beacon_period;
- u16 cap_info_bitmap;
- u8 *current_ptr;
- u64 timestamp;
- struct mwifiex_bcn_param *bcn_param;
- struct mwifiex_bss_priv *bss_priv;
-
- if (bytes_left >= sizeof(beacon_size)) {
- /* Extract & convert beacon size from command buffer */
- memcpy(&beacon_size, bss_info, sizeof(beacon_size));
- bytes_left -= sizeof(beacon_size);
- bss_info += sizeof(beacon_size);
- }
+ /*
+ * If the TSF TLV was appended to the scan results, save this
+ * entry's TSF value in the fw_tsf field. It is the firmware's
+ * TSF value at the time the beacon or probe response was
+ * received.
+ */
+ if (tsf_tlv)
+ memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE],
+ sizeof(fw_tsf));
- if (!beacon_size || beacon_size > bytes_left) {
- bss_info += bytes_left;
- bytes_left = 0;
- ret = -1;
- goto check_next_scan;
+ if (chan_band_tlv) {
+ chan_band = &chan_band_tlv->chan_band_param[idx];
+ radio_type = &chan_band->radio_type;
+ } else {
+ radio_type = NULL;
}
- /* Initialize the current working beacon pointer for this BSS
- * iteration */
- current_ptr = bss_info;
+ ret = mwifiex_parse_single_response_buf(priv, &bss_info,
+ &bytes_left,
+ le64_to_cpu(fw_tsf),
+ radio_type, false, 0);
+ if (ret)
+ goto check_next_scan;
+ }
- /* Advance the return beacon pointer past the current beacon */
- bss_info += beacon_size;
- bytes_left -= beacon_size;
+check_next_scan:
+ mwifiex_check_next_scan_command(priv);
+ return ret;
+}
- curr_bcn_bytes = beacon_size;
+/*
+ * This function prepares an extended scan command to be sent to the firmware
+ *
+ * This uses the scan command configuration sent to the command processing
+ * module in command preparation stage to configure a extended scan command
+ * structure to send to firmware.
+ */
+int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ void *data_buf)
+{
+ struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan;
+ struct mwifiex_scan_cmd_config *scan_cfg = data_buf;
- /*
- * First 5 fields are bssid, RSSI, time stamp, beacon interval,
- * and capability information
- */
- if (curr_bcn_bytes < sizeof(struct mwifiex_bcn_param)) {
- dev_err(adapter->dev,
- "InterpretIE: not enough bytes left\n");
- continue;
- }
- bcn_param = (struct mwifiex_bcn_param *)current_ptr;
- current_ptr += sizeof(*bcn_param);
- curr_bcn_bytes -= sizeof(*bcn_param);
+ memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len);
- memcpy(bssid, bcn_param->bssid, ETH_ALEN);
+ cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT);
- rssi = (s32) bcn_param->rssi;
- rssi = (-rssi) * 100; /* Convert dBm to mBm */
- dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi);
+ /* Size is equal to the sizeof(fixed portions) + the TLV len + header */
+ cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved)
+ + scan_cfg->tlv_buf_len + S_DS_GEN));
- timestamp = le64_to_cpu(bcn_param->timestamp);
- beacon_period = le16_to_cpu(bcn_param->beacon_period);
+ return 0;
+}
- cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap);
- dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n",
- cap_info_bitmap);
+/* This function handles the command response of extended scan */
+int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv)
+{
+ dev_dbg(priv->adapter->dev, "info: EXT scan returns successfully\n");
+ return 0;
+}
- /* Rest of the current buffer are IE's */
- ie_buf = current_ptr;
- ie_len = curr_bcn_bytes;
- dev_dbg(adapter->dev,
- "info: InterpretIE: IELength for this AP = %d\n",
- curr_bcn_bytes);
+/* This function This function handles the event extended scan report. It
+ * parses extended scan results and informs to cfg80211 stack.
+ */
+int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv,
+ void *buf)
+{
+ int ret = 0;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ u8 *bss_info;
+ u32 bytes_left, bytes_left_for_tlv, idx;
+ u16 type, len;
+ struct mwifiex_ie_types_data *tlv;
+ struct mwifiex_ie_types_bss_scan_rsp *scan_rsp_tlv;
+ struct mwifiex_ie_types_bss_scan_info *scan_info_tlv;
+ u8 *radio_type;
+ u64 fw_tsf = 0;
+ s32 rssi = 0;
+ struct mwifiex_event_scan_result *event_scan = buf;
+ u8 num_of_set = event_scan->num_of_set;
+ u8 *scan_resp = buf + sizeof(struct mwifiex_event_scan_result);
+ u16 scan_resp_size = le16_to_cpu(event_scan->buf_size);
+
+ if (num_of_set > MWIFIEX_MAX_AP) {
+ dev_err(adapter->dev,
+ "EXT_SCAN: Invalid number of AP returned (%d)!!\n",
+ num_of_set);
+ ret = -1;
+ goto check_next_scan;
+ }
- while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) {
- u8 element_id, element_len;
+ bytes_left = scan_resp_size;
+ dev_dbg(adapter->dev,
+ "EXT_SCAN: size %d, returned %d APs...",
+ scan_resp_size, num_of_set);
- element_id = *current_ptr;
- element_len = *(current_ptr + 1);
- if (curr_bcn_bytes < element_len +
- sizeof(struct ieee_types_header)) {
- dev_err(priv->adapter->dev,
- "%s: bytes left < IE length\n",
- __func__);
- goto check_next_scan;
- }
- if (element_id == WLAN_EID_DS_PARAMS) {
- channel = *(current_ptr + sizeof(struct ieee_types_header));
- break;
- }
+ tlv = (struct mwifiex_ie_types_data *)scan_resp;
- current_ptr += element_len +
- sizeof(struct ieee_types_header);
- curr_bcn_bytes -= element_len +
- sizeof(struct ieee_types_header);
+ for (idx = 0; idx < num_of_set && bytes_left; idx++) {
+ type = le16_to_cpu(tlv->header.type);
+ len = le16_to_cpu(tlv->header.len);
+ if (bytes_left < sizeof(struct mwifiex_ie_types_header) + len) {
+ dev_err(adapter->dev, "EXT_SCAN: Error bytes left < TLV length\n");
+ break;
}
+ scan_rsp_tlv = NULL;
+ scan_info_tlv = NULL;
+ bytes_left_for_tlv = bytes_left;
- /*
- * If the TSF TLV was appended to the scan results, save this
- * entry's TSF value in the fw_tsf field. It is the firmware's
- * TSF value at the time the beacon or probe response was
- * received.
+ /* BSS response TLV with beacon or probe response buffer
+ * at the initial position of each descriptor
*/
- if (tsf_tlv)
- memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE],
- sizeof(fw_tsf));
-
- if (channel) {
- struct ieee80211_channel *chan;
- u8 band;
+ if (type != TLV_TYPE_BSS_SCAN_RSP)
+ break;
- /* Skip entry if on csa closed channel */
- if (channel == priv->csa_chan) {
- dev_dbg(adapter->dev,
- "Dropping entry on csa closed channel\n");
+ bss_info = (u8 *)tlv;
+ scan_rsp_tlv = (struct mwifiex_ie_types_bss_scan_rsp *)tlv;
+ tlv = (struct mwifiex_ie_types_data *)(tlv->data + len);
+ bytes_left_for_tlv -=
+ (len + sizeof(struct mwifiex_ie_types_header));
+
+ while (bytes_left_for_tlv >=
+ sizeof(struct mwifiex_ie_types_header) &&
+ le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) {
+ type = le16_to_cpu(tlv->header.type);
+ len = le16_to_cpu(tlv->header.len);
+ if (bytes_left_for_tlv <
+ sizeof(struct mwifiex_ie_types_header) + len) {
+ dev_err(adapter->dev,
+ "EXT_SCAN: Error in processing TLV, bytes left < TLV length\n");
+ scan_rsp_tlv = NULL;
+ bytes_left_for_tlv = 0;
continue;
}
-
- band = BAND_G;
- if (chan_band_tlv) {
- chan_band =
- &chan_band_tlv->chan_band_param[idx];
- band = mwifiex_radio_type_to_band(
- chan_band->radio_type
- & (BIT(0) | BIT(1)));
- }
-
- cfp = mwifiex_get_cfp(priv, band, channel, 0);
-
- freq = cfp ? cfp->freq : 0;
-
- chan = ieee80211_get_channel(priv->wdev->wiphy, freq);
-
- if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
- bss = cfg80211_inform_bss(priv->wdev->wiphy,
- chan, bssid, timestamp,
- cap_info_bitmap, beacon_period,
- ie_buf, ie_len, rssi, GFP_KERNEL);
- bss_priv = (struct mwifiex_bss_priv *)bss->priv;
- bss_priv->band = band;
- bss_priv->fw_tsf = le64_to_cpu(fw_tsf);
- if (priv->media_connected &&
- !memcmp(bssid,
- priv->curr_bss_params.bss_descriptor
- .mac_address, ETH_ALEN))
- mwifiex_update_curr_bss_params(priv,
- bss);
- cfg80211_put_bss(priv->wdev->wiphy, bss);
+ switch (type) {
+ case TLV_TYPE_BSS_SCAN_INFO:
+ scan_info_tlv =
+ (struct mwifiex_ie_types_bss_scan_info *)tlv;
+ if (len !=
+ sizeof(struct mwifiex_ie_types_bss_scan_info) -
+ sizeof(struct mwifiex_ie_types_header)) {
+ bytes_left_for_tlv = 0;
+ continue;
+ }
+ break;
+ default:
+ break;
}
- } else {
- dev_dbg(adapter->dev, "missing BSS channel IE\n");
+ tlv = (struct mwifiex_ie_types_data *)(tlv->data + len);
+ bytes_left -=
+ (len + sizeof(struct mwifiex_ie_types_header));
+ bytes_left_for_tlv -=
+ (len + sizeof(struct mwifiex_ie_types_header));
}
- }
-check_next_scan:
- spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
- if (list_empty(&adapter->scan_pending_q)) {
- spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
- adapter->scan_processing = false;
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+ if (!scan_rsp_tlv)
+ break;
- /* Need to indicate IOCTL complete */
- if (adapter->curr_cmd->wait_q_enabled) {
- adapter->cmd_wait_q.status = 0;
- if (!priv->scan_request) {
- dev_dbg(adapter->dev,
- "complete internal scan\n");
- mwifiex_complete_cmd(adapter,
- adapter->curr_cmd);
- }
- }
- if (priv->report_scan_result)
- priv->report_scan_result = false;
+ /* Advance pointer to the beacon buffer length and
+ * update the bytes count so that the function
+ * wlan_interpret_bss_desc_with_ie() can handle the
+ * scan buffer withut any change
+ */
+ bss_info += sizeof(u16);
+ bytes_left -= sizeof(u16);
- if (priv->scan_request) {
- dev_dbg(adapter->dev, "info: notifying scan done\n");
- cfg80211_scan_done(priv->scan_request, 0);
- priv->scan_request = NULL;
- } else {
- priv->scan_aborting = false;
- dev_dbg(adapter->dev, "info: scan already aborted\n");
- }
- } else {
- if ((priv->scan_aborting && !priv->scan_request) ||
- priv->scan_block) {
- spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
- flags);
- adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT;
- mod_timer(&priv->scan_delay_timer, jiffies);
- dev_dbg(priv->adapter->dev,
- "info: %s: triggerring scan abort\n", __func__);
- } else if (!mwifiex_wmm_lists_empty(adapter) &&
- (priv->scan_request && (priv->scan_request->flags &
- NL80211_SCAN_FLAG_LOW_PRIORITY))) {
- spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
- flags);
- adapter->scan_delay_cnt = 1;
- mod_timer(&priv->scan_delay_timer, jiffies +
- msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
- dev_dbg(priv->adapter->dev,
- "info: %s: deferring scan\n", __func__);
+ if (scan_info_tlv) {
+ rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi));
+ rssi *= 100; /* Convert dBm to mBm */
+ dev_dbg(adapter->dev,
+ "info: InterpretIE: RSSI=%d\n", rssi);
+ fw_tsf = le64_to_cpu(scan_info_tlv->tsf);
+ radio_type = &scan_info_tlv->radio_type;
} else {
- /* Get scan command from scan_pending_q and put to
- cmd_pending_q */
- cmd_node = list_first_entry(&adapter->scan_pending_q,
- struct cmd_ctrl_node, list);
- list_del(&cmd_node->list);
- spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
- flags);
- mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
- true);
+ radio_type = NULL;
}
+ ret = mwifiex_parse_single_response_buf(priv, &bss_info,
+ &bytes_left, fw_tsf,
+ radio_type, true, rssi);
+ if (ret)
+ goto check_next_scan;
}
+check_next_scan:
+ if (!event_scan->more_event)
+ mwifiex_check_next_scan_command(priv);
+
return ret;
}
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
index b44a31523461..d206f04d4994 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/mwifiex/sdio.c
@@ -84,6 +84,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
card->supports_sdio_new_mode = data->supports_sdio_new_mode;
card->has_control_mask = data->has_control_mask;
+ card->tx_buf_size = data->tx_buf_size;
}
sdio_claim_host(func);
@@ -165,7 +166,6 @@ mwifiex_sdio_remove(struct sdio_func *func)
struct sdio_mmc_card *card;
struct mwifiex_adapter *adapter;
struct mwifiex_private *priv;
- int i;
pr_debug("info: SDIO func num=%d\n", func->num);
@@ -184,11 +184,7 @@ mwifiex_sdio_remove(struct sdio_func *func)
if (adapter->is_suspended)
mwifiex_sdio_resume(adapter->dev);
- for (i = 0; i < adapter->priv_num; i++)
- if ((GET_BSS_ROLE(adapter->priv[i]) ==
- MWIFIEX_BSS_ROLE_STA) &&
- adapter->priv[i]->media_connected)
- mwifiex_deauthenticate(adapter->priv[i], NULL);
+ mwifiex_deauthenticate_all(adapter);
priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
mwifiex_disable_auto_ds(priv);
@@ -241,6 +237,7 @@ static int mwifiex_sdio_suspend(struct device *dev)
/* Enable the Host Sleep */
if (!mwifiex_enable_hs(adapter)) {
dev_err(adapter->dev, "cmd: failed to suspend\n");
+ adapter->hs_enabling = false;
return -EFAULT;
}
@@ -249,6 +246,7 @@ static int mwifiex_sdio_suspend(struct device *dev)
/* Indicate device suspended */
adapter->is_suspended = true;
+ adapter->hs_enabling = false;
return ret;
}
@@ -1760,6 +1758,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
/* save adapter pointer in card */
card->adapter = adapter;
+ adapter->tx_buf_size = card->tx_buf_size;
sdio_claim_host(func);
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h
index 532ae0ac4dfb..c71201b2e2a3 100644
--- a/drivers/net/wireless/mwifiex/sdio.h
+++ b/drivers/net/wireless/mwifiex/sdio.h
@@ -233,6 +233,7 @@ struct sdio_mmc_card {
u8 mp_agg_pkt_limit;
bool supports_sdio_new_mode;
bool has_control_mask;
+ u16 tx_buf_size;
u32 mp_rd_bitmap;
u32 mp_wr_bitmap;
@@ -256,6 +257,7 @@ struct mwifiex_sdio_device {
u8 mp_agg_pkt_limit;
bool supports_sdio_new_mode;
bool has_control_mask;
+ u16 tx_buf_size;
};
static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = {
@@ -312,6 +314,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
.mp_agg_pkt_limit = 8,
.supports_sdio_new_mode = false,
.has_control_mask = true,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
@@ -321,6 +324,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
.mp_agg_pkt_limit = 8,
.supports_sdio_new_mode = false,
.has_control_mask = true,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
@@ -330,6 +334,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
.mp_agg_pkt_limit = 8,
.supports_sdio_new_mode = false,
.has_control_mask = true,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
@@ -339,6 +344,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
.mp_agg_pkt_limit = 16,
.supports_sdio_new_mode = true,
.has_control_mask = false,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
};
/*
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c
index 9208a8816b80..e3cac1495cc7 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/mwifiex/sta_cmd.c
@@ -185,6 +185,13 @@ static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv,
i++)
rate_scope->ht_mcs_rate_bitmap[i] =
cpu_to_le16(pbitmap_rates[2 + i]);
+ if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) {
+ for (i = 0;
+ i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap);
+ i++)
+ rate_scope->vht_mcs_rate_bitmap[i] =
+ cpu_to_le16(pbitmap_rates[10 + i]);
+ }
} else {
rate_scope->hr_dsss_rate_bitmap =
cpu_to_le16(priv->bitmap_rates[0]);
@@ -195,6 +202,13 @@ static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv,
i++)
rate_scope->ht_mcs_rate_bitmap[i] =
cpu_to_le16(priv->bitmap_rates[2 + i]);
+ if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) {
+ for (i = 0;
+ i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap);
+ i++)
+ rate_scope->vht_mcs_rate_bitmap[i] =
+ cpu_to_le16(priv->bitmap_rates[10 + i]);
+ }
}
rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope +
@@ -532,8 +546,228 @@ mwifiex_set_keyparamset_wep(struct mwifiex_private *priv,
return 0;
}
+/* This function populates key material v2 command
+ * to set network key for AES & CMAC AES.
+ */
+static int mwifiex_set_aes_key_v2(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_ds_encrypt_key *enc_key,
+ struct host_cmd_ds_802_11_key_material_v2 *km)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ u16 size, len = KEY_PARAMS_FIXED_LEN;
+
+ if (enc_key->is_igtk_key) {
+ dev_dbg(adapter->dev, "%s: Set CMAC AES Key\n", __func__);
+ if (enc_key->is_rx_seq_valid)
+ memcpy(km->key_param_set.key_params.cmac_aes.ipn,
+ enc_key->pn, enc_key->pn_len);
+ km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST);
+ km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK);
+ km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC;
+ km->key_param_set.key_params.cmac_aes.key_len =
+ cpu_to_le16(enc_key->key_len);
+ memcpy(km->key_param_set.key_params.cmac_aes.key,
+ enc_key->key_material, enc_key->key_len);
+ len += sizeof(struct mwifiex_cmac_aes_param);
+ } else {
+ dev_dbg(adapter->dev, "%s: Set AES Key\n", __func__);
+ if (enc_key->is_rx_seq_valid)
+ memcpy(km->key_param_set.key_params.aes.pn,
+ enc_key->pn, enc_key->pn_len);
+ km->key_param_set.key_type = KEY_TYPE_ID_AES;
+ km->key_param_set.key_params.aes.key_len =
+ cpu_to_le16(enc_key->key_len);
+ memcpy(km->key_param_set.key_params.aes.key,
+ enc_key->key_material, enc_key->key_len);
+ len += sizeof(struct mwifiex_aes_param);
+ }
+
+ km->key_param_set.len = cpu_to_le16(len);
+ size = len + sizeof(struct mwifiex_ie_types_header) +
+ sizeof(km->action) + S_DS_GEN;
+ cmd->size = cpu_to_le16(size);
+
+ return 0;
+}
+
+/* This function prepares command to set/get/reset network key(s).
+ * This function prepares key material command for V2 format.
+ * Preparation includes -
+ * - Setting command ID, action and proper size
+ * - Setting WEP keys, WAPI keys or WPA keys along with required
+ * encryption (TKIP, AES) (as required)
+ * - Ensuring correct endian-ness
+ */
+static int
+mwifiex_cmd_802_11_key_material_v2(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action, u32 cmd_oid,
+ struct mwifiex_ds_encrypt_key *enc_key)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ u8 *mac = enc_key->mac_addr;
+ u16 key_info, len = KEY_PARAMS_FIXED_LEN;
+ struct host_cmd_ds_802_11_key_material_v2 *km =
+ &cmd->params.key_material_v2;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL);
+ km->action = cpu_to_le16(cmd_action);
+
+ if (cmd_action == HostCmd_ACT_GEN_GET) {
+ dev_dbg(adapter->dev, "%s: Get key\n", __func__);
+ km->key_param_set.key_idx =
+ enc_key->key_index & KEY_INDEX_MASK;
+ km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
+ km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN);
+ memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);
+
+ if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST)
+ key_info = KEY_UNICAST;
+ else
+ key_info = KEY_MCAST;
+
+ if (enc_key->is_igtk_key)
+ key_info |= KEY_IGTK;
+
+ km->key_param_set.key_info = cpu_to_le16(key_info);
+
+ cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
+ S_DS_GEN + KEY_PARAMS_FIXED_LEN +
+ sizeof(km->action));
+ return 0;
+ }
+
+ memset(&km->key_param_set, 0,
+ sizeof(struct mwifiex_ie_type_key_param_set_v2));
+
+ if (enc_key->key_disable) {
+ dev_dbg(adapter->dev, "%s: Remove key\n", __func__);
+ km->action = cpu_to_le16(HostCmd_ACT_GEN_REMOVE);
+ km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
+ km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN);
+ km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK;
+ key_info = KEY_MCAST | KEY_UNICAST;
+ km->key_param_set.key_info = cpu_to_le16(key_info);
+ memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);
+ cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
+ S_DS_GEN + KEY_PARAMS_FIXED_LEN +
+ sizeof(km->action));
+ return 0;
+ }
+
+ km->action = cpu_to_le16(HostCmd_ACT_GEN_SET);
+ km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK;
+ km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
+ key_info = KEY_ENABLED;
+ memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);
+
+ if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) {
+ dev_dbg(adapter->dev, "%s: Set WEP Key\n", __func__);
+ len += sizeof(struct mwifiex_wep_param);
+ km->key_param_set.len = cpu_to_le16(len);
+ km->key_param_set.key_type = KEY_TYPE_ID_WEP;
+
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+ key_info |= KEY_MCAST | KEY_UNICAST;
+ } else {
+ if (enc_key->is_current_wep_key) {
+ key_info |= KEY_MCAST | KEY_UNICAST;
+ if (km->key_param_set.key_idx ==
+ (priv->wep_key_curr_index & KEY_INDEX_MASK))
+ key_info |= KEY_DEFAULT;
+ } else {
+ if (mac) {
+ if (is_broadcast_ether_addr(mac))
+ key_info |= KEY_MCAST;
+ else
+ key_info |= KEY_UNICAST |
+ KEY_DEFAULT;
+ } else {
+ key_info |= KEY_MCAST;
+ }
+ }
+ }
+ km->key_param_set.key_info = cpu_to_le16(key_info);
+
+ km->key_param_set.key_params.wep.key_len =
+ cpu_to_le16(enc_key->key_len);
+ memcpy(km->key_param_set.key_params.wep.key,
+ enc_key->key_material, enc_key->key_len);
+
+ cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
+ len + sizeof(km->action) + S_DS_GEN);
+ return 0;
+ }
+
+ if (is_broadcast_ether_addr(mac))
+ key_info |= KEY_MCAST | KEY_RX_KEY;
+ else
+ key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY;
+
+ if (enc_key->is_wapi_key) {
+ dev_dbg(adapter->dev, "%s: Set WAPI Key\n", __func__);
+ km->key_param_set.key_type = KEY_TYPE_ID_WAPI;
+ memcpy(km->key_param_set.key_params.wapi.pn, enc_key->pn,
+ PN_LEN);
+ km->key_param_set.key_params.wapi.key_len =
+ cpu_to_le16(enc_key->key_len);
+ memcpy(km->key_param_set.key_params.wapi.key,
+ enc_key->key_material, enc_key->key_len);
+ if (is_broadcast_ether_addr(mac))
+ priv->sec_info.wapi_key_on = true;
+
+ if (!priv->sec_info.wapi_key_on)
+ key_info |= KEY_DEFAULT;
+ km->key_param_set.key_info = cpu_to_le16(key_info);
+
+ len += sizeof(struct mwifiex_wapi_param);
+ km->key_param_set.len = cpu_to_le16(len);
+ cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
+ len + sizeof(km->action) + S_DS_GEN);
+ return 0;
+ }
+
+ if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
+ key_info |= KEY_DEFAULT;
+ /* Enable unicast bit for WPA-NONE/ADHOC_AES */
+ if (!priv->sec_info.wpa2_enabled &&
+ !is_broadcast_ether_addr(mac))
+ key_info |= KEY_UNICAST;
+ } else {
+ /* Enable default key for WPA/WPA2 */
+ if (!priv->wpa_is_gtk_set)
+ key_info |= KEY_DEFAULT;
+ }
+
+ km->key_param_set.key_info = cpu_to_le16(key_info);
+
+ if (enc_key->key_len == WLAN_KEY_LEN_CCMP)
+ return mwifiex_set_aes_key_v2(priv, cmd, enc_key, km);
+
+ if (enc_key->key_len == WLAN_KEY_LEN_TKIP) {
+ dev_dbg(adapter->dev, "%s: Set TKIP Key\n", __func__);
+ if (enc_key->is_rx_seq_valid)
+ memcpy(km->key_param_set.key_params.tkip.pn,
+ enc_key->pn, enc_key->pn_len);
+ km->key_param_set.key_type = KEY_TYPE_ID_TKIP;
+ km->key_param_set.key_params.tkip.key_len =
+ cpu_to_le16(enc_key->key_len);
+ memcpy(km->key_param_set.key_params.tkip.key,
+ enc_key->key_material, enc_key->key_len);
+
+ len += sizeof(struct mwifiex_tkip_param);
+ km->key_param_set.len = cpu_to_le16(len);
+ cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
+ len + sizeof(km->action) + S_DS_GEN);
+ }
+
+ return 0;
+}
+
/*
* This function prepares command to set/get/reset network key(s).
+ * This function prepares key material command for V1 format.
*
* Preparation includes -
* - Setting command ID, action and proper size
@@ -542,10 +776,10 @@ mwifiex_set_keyparamset_wep(struct mwifiex_private *priv,
* - Ensuring correct endian-ness
*/
static int
-mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
- struct host_cmd_ds_command *cmd,
- u16 cmd_action, u32 cmd_oid,
- struct mwifiex_ds_encrypt_key *enc_key)
+mwifiex_cmd_802_11_key_material_v1(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action, u32 cmd_oid,
+ struct mwifiex_ds_encrypt_key *enc_key)
{
struct host_cmd_ds_802_11_key_material *key_material =
&cmd->params.key_material;
@@ -724,6 +958,24 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
return ret;
}
+/* Wrapper function for setting network key depending upon FW KEY API version */
+static int
+mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action, u32 cmd_oid,
+ struct mwifiex_ds_encrypt_key *enc_key)
+{
+ if (priv->adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2)
+ return mwifiex_cmd_802_11_key_material_v2(priv, cmd,
+ cmd_action, cmd_oid,
+ enc_key);
+
+ else
+ return mwifiex_cmd_802_11_key_material_v1(priv, cmd,
+ cmd_action, cmd_oid,
+ enc_key);
+}
+
/*
* This function prepares command to set/get 11d domain information.
*
@@ -1173,9 +1425,9 @@ int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv,
/* property header is 6 bytes, data must fit in cmd buffer */
if (prop && prop->value && prop->length > 6 &&
prop->length <= MWIFIEX_SIZE_OF_CMD_BUFFER - S_DS_GEN) {
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA,
- HostCmd_ACT_GEN_SET, 0,
- prop);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA,
+ HostCmd_ACT_GEN_SET, 0,
+ prop, true);
if (ret)
return ret;
}
@@ -1280,6 +1532,127 @@ mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv,
return 0;
}
+static int
+mwifiex_cmd_tdls_oper(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ void *data_buf)
+{
+ struct host_cmd_ds_tdls_oper *tdls_oper = &cmd->params.tdls_oper;
+ struct mwifiex_ds_tdls_oper *oper = data_buf;
+ struct mwifiex_sta_node *sta_ptr;
+ struct host_cmd_tlv_rates *tlv_rates;
+ struct mwifiex_ie_types_htcap *ht_capab;
+ struct mwifiex_ie_types_qos_info *wmm_qos_info;
+ struct mwifiex_ie_types_extcap *extcap;
+ struct mwifiex_ie_types_vhtcap *vht_capab;
+ struct mwifiex_ie_types_aid *aid;
+ u8 *pos, qos_info;
+ u16 config_len = 0;
+ struct station_parameters *params = priv->sta_params;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_OPER);
+ cmd->size = cpu_to_le16(S_DS_GEN);
+ le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper));
+
+ tdls_oper->reason = 0;
+ memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN);
+ sta_ptr = mwifiex_get_sta_entry(priv, oper->peer_mac);
+
+ pos = (u8 *)tdls_oper + sizeof(struct host_cmd_ds_tdls_oper);
+
+ switch (oper->tdls_action) {
+ case MWIFIEX_TDLS_DISABLE_LINK:
+ tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_DELETE);
+ break;
+ case MWIFIEX_TDLS_CREATE_LINK:
+ tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CREATE);
+ break;
+ case MWIFIEX_TDLS_CONFIG_LINK:
+ tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CONFIG);
+
+ if (!params) {
+ dev_err(priv->adapter->dev,
+ "TDLS config params not available for %pM\n",
+ oper->peer_mac);
+ return -ENODATA;
+ }
+
+ *(__le16 *)pos = cpu_to_le16(params->capability);
+ config_len += sizeof(params->capability);
+
+ qos_info = params->uapsd_queues | (params->max_sp << 5);
+ wmm_qos_info = (struct mwifiex_ie_types_qos_info *)(pos +
+ config_len);
+ wmm_qos_info->header.type = cpu_to_le16(WLAN_EID_QOS_CAPA);
+ wmm_qos_info->header.len = cpu_to_le16(sizeof(qos_info));
+ wmm_qos_info->qos_info = qos_info;
+ config_len += sizeof(struct mwifiex_ie_types_qos_info);
+
+ if (params->ht_capa) {
+ ht_capab = (struct mwifiex_ie_types_htcap *)(pos +
+ config_len);
+ ht_capab->header.type =
+ cpu_to_le16(WLAN_EID_HT_CAPABILITY);
+ ht_capab->header.len =
+ cpu_to_le16(sizeof(struct ieee80211_ht_cap));
+ memcpy(&ht_capab->ht_cap, params->ht_capa,
+ sizeof(struct ieee80211_ht_cap));
+ config_len += sizeof(struct mwifiex_ie_types_htcap);
+ }
+
+ if (params->supported_rates && params->supported_rates_len) {
+ tlv_rates = (struct host_cmd_tlv_rates *)(pos +
+ config_len);
+ tlv_rates->header.type =
+ cpu_to_le16(WLAN_EID_SUPP_RATES);
+ tlv_rates->header.len =
+ cpu_to_le16(params->supported_rates_len);
+ memcpy(tlv_rates->rates, params->supported_rates,
+ params->supported_rates_len);
+ config_len += sizeof(struct host_cmd_tlv_rates) +
+ params->supported_rates_len;
+ }
+
+ if (params->ext_capab && params->ext_capab_len) {
+ extcap = (struct mwifiex_ie_types_extcap *)(pos +
+ config_len);
+ extcap->header.type =
+ cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
+ extcap->header.len = cpu_to_le16(params->ext_capab_len);
+ memcpy(extcap->ext_capab, params->ext_capab,
+ params->ext_capab_len);
+ config_len += sizeof(struct mwifiex_ie_types_extcap) +
+ params->ext_capab_len;
+ }
+ if (params->vht_capa) {
+ vht_capab = (struct mwifiex_ie_types_vhtcap *)(pos +
+ config_len);
+ vht_capab->header.type =
+ cpu_to_le16(WLAN_EID_VHT_CAPABILITY);
+ vht_capab->header.len =
+ cpu_to_le16(sizeof(struct ieee80211_vht_cap));
+ memcpy(&vht_capab->vht_cap, params->vht_capa,
+ sizeof(struct ieee80211_vht_cap));
+ config_len += sizeof(struct mwifiex_ie_types_vhtcap);
+ }
+ if (params->aid) {
+ aid = (struct mwifiex_ie_types_aid *)(pos + config_len);
+ aid->header.type = cpu_to_le16(WLAN_EID_AID);
+ aid->header.len = cpu_to_le16(sizeof(params->aid));
+ aid->aid = cpu_to_le16(params->aid);
+ config_len += sizeof(struct mwifiex_ie_types_aid);
+ }
+
+ break;
+ default:
+ dev_err(priv->adapter->dev, "Unknown TDLS operation\n");
+ return -ENOTSUPP;
+ }
+
+ le16_add_cpu(&cmd->size, config_len);
+
+ return 0;
+}
/*
* This function prepares the commands before sending them to the firmware.
*
@@ -1472,6 +1845,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
ret = mwifiex_cmd_ibss_coalescing_status(cmd_ptr, cmd_action,
data_buf);
break;
+ case HostCmd_CMD_802_11_SCAN_EXT:
+ ret = mwifiex_cmd_802_11_scan_ext(priv, cmd_ptr, data_buf);
+ break;
case HostCmd_CMD_MAC_REG_ACCESS:
case HostCmd_CMD_BBP_REG_ACCESS:
case HostCmd_CMD_RF_REG_ACCESS:
@@ -1507,6 +1883,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action,
data_buf);
break;
+ case HostCmd_CMD_TDLS_OPER:
+ ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf);
+ break;
default:
dev_err(priv->adapter->dev,
"PREP_CMD: unknown cmd- %#x\n", cmd_no);
@@ -1547,15 +1926,16 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
if (first_sta) {
if (priv->adapter->iface_type == MWIFIEX_PCIE) {
- ret = mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_PCIE_DESC_DETAILS,
- HostCmd_ACT_GEN_SET, 0, NULL);
+ ret = mwifiex_send_cmd(priv,
+ HostCmd_CMD_PCIE_DESC_DETAILS,
+ HostCmd_ACT_GEN_SET, 0, NULL,
+ true);
if (ret)
return -1;
}
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_FUNC_INIT,
- HostCmd_ACT_GEN_SET, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_FUNC_INIT,
+ HostCmd_ACT_GEN_SET, 0, NULL, true);
if (ret)
return -1;
@@ -1573,55 +1953,57 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
}
if (adapter->cal_data) {
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA,
- HostCmd_ACT_GEN_SET, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA,
+ HostCmd_ACT_GEN_SET, 0, NULL,
+ true);
if (ret)
return -1;
}
/* Read MAC address from HW */
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_GET_HW_SPEC,
- HostCmd_ACT_GEN_GET, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_GET_HW_SPEC,
+ HostCmd_ACT_GEN_GET, 0, NULL, true);
if (ret)
return -1;
/* Reconfigure tx buf size */
- ret = mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_RECONFIGURE_TX_BUFF,
- HostCmd_ACT_GEN_SET, 0,
- &priv->adapter->tx_buf_size);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
+ HostCmd_ACT_GEN_SET, 0,
+ &priv->adapter->tx_buf_size, true);
if (ret)
return -1;
if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) {
/* Enable IEEE PS by default */
priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;
- ret = mwifiex_send_cmd_sync(
- priv, HostCmd_CMD_802_11_PS_MODE_ENH,
- EN_AUTO_PS, BITMAP_STA_PS, NULL);
+ ret = mwifiex_send_cmd(priv,
+ HostCmd_CMD_802_11_PS_MODE_ENH,
+ EN_AUTO_PS, BITMAP_STA_PS, NULL,
+ true);
if (ret)
return -1;
}
}
/* get tx rate */
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_TX_RATE_CFG,
- HostCmd_ACT_GEN_GET, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG,
+ HostCmd_ACT_GEN_GET, 0, NULL, true);
if (ret)
return -1;
priv->data_rate = 0;
/* get tx power */
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_RF_TX_PWR,
- HostCmd_ACT_GEN_GET, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_RF_TX_PWR,
+ HostCmd_ACT_GEN_GET, 0, NULL, true);
if (ret)
return -1;
if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) {
/* set ibss coalescing_status */
- ret = mwifiex_send_cmd_sync(
- priv, HostCmd_CMD_802_11_IBSS_COALESCING_STATUS,
- HostCmd_ACT_GEN_SET, 0, &enable);
+ ret = mwifiex_send_cmd(
+ priv,
+ HostCmd_CMD_802_11_IBSS_COALESCING_STATUS,
+ HostCmd_ACT_GEN_SET, 0, &enable, true);
if (ret)
return -1;
}
@@ -1629,16 +2011,16 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl));
amsdu_aggr_ctrl.enable = true;
/* Send request to firmware */
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_AMSDU_AGGR_CTRL,
- HostCmd_ACT_GEN_SET, 0,
- &amsdu_aggr_ctrl);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_AMSDU_AGGR_CTRL,
+ HostCmd_ACT_GEN_SET, 0,
+ &amsdu_aggr_ctrl, true);
if (ret)
return -1;
/* MAC Control must be the last command in init_fw */
/* set MAC Control */
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_MAC_CONTROL,
- HostCmd_ACT_GEN_SET, 0,
- &priv->curr_pkt_filter);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+ HostCmd_ACT_GEN_SET, 0,
+ &priv->curr_pkt_filter, true);
if (ret)
return -1;
@@ -1647,10 +2029,9 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
/* Enable auto deep sleep */
auto_ds.auto_ds = DEEP_SLEEP_ON;
auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME;
- ret = mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_802_11_PS_MODE_ENH,
- EN_AUTO_PS, BITMAP_AUTO_DS,
- &auto_ds);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
+ EN_AUTO_PS, BITMAP_AUTO_DS,
+ &auto_ds, true);
if (ret)
return -1;
}
@@ -1658,9 +2039,9 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) {
/* Send cmd to FW to enable/disable 11D function */
state_11d = ENABLE_11D;
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
- HostCmd_ACT_GEN_SET, DOT11D_I,
- &state_11d);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_SET, DOT11D_I,
+ &state_11d, true);
if (ret)
dev_err(priv->adapter->dev,
"11D: failed to enable 11D\n");
@@ -1673,8 +2054,8 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
* (Short GI, Channel BW, Green field support etc.) for transmit
*/
tx_cfg.tx_htcap = MWIFIEX_FW_DEF_HTTXCFG;
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_11N_CFG,
- HostCmd_ACT_GEN_SET, 0, &tx_cfg);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_CFG,
+ HostCmd_ACT_GEN_SET, 0, &tx_cfg, true);
ret = -EINPROGRESS;
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c
index 24523e4015cb..bfebb0144df5 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -69,6 +69,7 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv,
break;
case HostCmd_CMD_802_11_SCAN:
+ case HostCmd_CMD_802_11_SCAN_EXT:
/* Cancel all pending scan command */
spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
list_for_each_entry_safe(cmd_node, tmp_node,
@@ -157,8 +158,8 @@ static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv,
priv->subsc_evt_rssi_state = EVENT_HANDLED;
- mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
- 0, 0, subsc_evt);
+ mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+ 0, 0, subsc_evt, false);
return 0;
}
@@ -303,6 +304,15 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv,
priv->bitmap_rates[2 + i] =
le16_to_cpu(rate_scope->
ht_mcs_rate_bitmap[i]);
+
+ if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) {
+ for (i = 0; i < ARRAY_SIZE(rate_scope->
+ vht_mcs_rate_bitmap);
+ i++)
+ priv->bitmap_rates[10 + i] =
+ le16_to_cpu(rate_scope->
+ vht_mcs_rate_bitmap[i]);
+ }
break;
/* Add RATE_DROP tlv here */
}
@@ -316,9 +326,8 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv,
if (priv->is_data_rate_auto)
priv->data_rate = 0;
else
- return mwifiex_send_cmd_async(priv,
- HostCmd_CMD_802_11_TX_RATE_QUERY,
- HostCmd_ACT_GEN_GET, 0, NULL);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY,
+ HostCmd_ACT_GEN_GET, 0, NULL, false);
return 0;
}
@@ -561,13 +570,13 @@ static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv,
}
/*
- * This function handles the command response of set/get key material.
+ * This function handles the command response of set/get v1 key material.
*
* Handling includes updating the driver parameters to reflect the
* changes.
*/
-static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp)
+static int mwifiex_ret_802_11_key_material_v1(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
{
struct host_cmd_ds_802_11_key_material *key =
&resp->params.key_material;
@@ -590,6 +599,51 @@ static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv,
}
/*
+ * This function handles the command response of set/get v2 key material.
+ *
+ * Handling includes updating the driver parameters to reflect the
+ * changes.
+ */
+static int mwifiex_ret_802_11_key_material_v2(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct host_cmd_ds_802_11_key_material_v2 *key_v2;
+ __le16 len;
+
+ key_v2 = &resp->params.key_material_v2;
+ if (le16_to_cpu(key_v2->action) == HostCmd_ACT_GEN_SET) {
+ if ((le16_to_cpu(key_v2->key_param_set.key_info) & KEY_MCAST)) {
+ dev_dbg(priv->adapter->dev, "info: key: GTK is set\n");
+ priv->wpa_is_gtk_set = true;
+ priv->scan_block = false;
+ }
+ }
+
+ if (key_v2->key_param_set.key_type != KEY_TYPE_ID_AES)
+ return 0;
+
+ memset(priv->aes_key_v2.key_param_set.key_params.aes.key, 0,
+ WLAN_KEY_LEN_CCMP);
+ priv->aes_key_v2.key_param_set.key_params.aes.key_len =
+ key_v2->key_param_set.key_params.aes.key_len;
+ len = priv->aes_key_v2.key_param_set.key_params.aes.key_len;
+ memcpy(priv->aes_key_v2.key_param_set.key_params.aes.key,
+ key_v2->key_param_set.key_params.aes.key, le16_to_cpu(len));
+
+ return 0;
+}
+
+/* Wrapper function for processing response of key material command */
+static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ if (priv->adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2)
+ return mwifiex_ret_802_11_key_material_v2(priv, resp);
+ else
+ return mwifiex_ret_802_11_key_material_v1(priv, resp);
+}
+
+/*
* This function handles the command response of get 11d domain information.
*/
static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv,
@@ -800,7 +854,60 @@ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv,
return 0;
}
+static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct host_cmd_ds_tdls_oper *cmd_tdls_oper = &resp->params.tdls_oper;
+ u16 reason = le16_to_cpu(cmd_tdls_oper->reason);
+ u16 action = le16_to_cpu(cmd_tdls_oper->tdls_action);
+ struct mwifiex_sta_node *node =
+ mwifiex_get_sta_entry(priv, cmd_tdls_oper->peer_mac);
+ switch (action) {
+ case ACT_TDLS_DELETE:
+ if (reason)
+ dev_err(priv->adapter->dev,
+ "TDLS link delete for %pM failed: reason %d\n",
+ cmd_tdls_oper->peer_mac, reason);
+ else
+ dev_dbg(priv->adapter->dev,
+ "TDLS link config for %pM successful\n",
+ cmd_tdls_oper->peer_mac);
+ break;
+ case ACT_TDLS_CREATE:
+ if (reason) {
+ dev_err(priv->adapter->dev,
+ "TDLS link creation for %pM failed: reason %d",
+ cmd_tdls_oper->peer_mac, reason);
+ if (node && reason != TDLS_ERR_LINK_EXISTS)
+ node->tdls_status = TDLS_SETUP_FAILURE;
+ } else {
+ dev_dbg(priv->adapter->dev,
+ "TDLS link creation for %pM successful",
+ cmd_tdls_oper->peer_mac);
+ }
+ break;
+ case ACT_TDLS_CONFIG:
+ if (reason) {
+ dev_err(priv->adapter->dev,
+ "TDLS link config for %pM failed, reason %d\n",
+ cmd_tdls_oper->peer_mac, reason);
+ if (node)
+ node->tdls_status = TDLS_SETUP_FAILURE;
+ } else {
+ dev_dbg(priv->adapter->dev,
+ "TDLS link config for %pM successful\n",
+ cmd_tdls_oper->peer_mac);
+ }
+ break;
+ default:
+ dev_err(priv->adapter->dev,
+ "Unknown TDLS command action respnse %d", action);
+ return -1;
+ }
+
+ return 0;
+}
/*
* This function handles the command response for subscribe event command.
*/
@@ -871,6 +978,10 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
ret = mwifiex_ret_802_11_scan(priv, resp);
adapter->curr_cmd->wait_q_enabled = false;
break;
+ case HostCmd_CMD_802_11_SCAN_EXT:
+ ret = mwifiex_ret_802_11_scan_ext(priv);
+ adapter->curr_cmd->wait_q_enabled = false;
+ break;
case HostCmd_CMD_802_11_BG_SCAN_QUERY:
ret = mwifiex_ret_802_11_scan(priv, resp);
dev_dbg(adapter->dev,
@@ -999,6 +1110,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
break;
case HostCmd_CMD_COALESCE_CFG:
break;
+ case HostCmd_CMD_TDLS_OPER:
+ ret = mwifiex_ret_tdls_oper(priv, resp);
+ break;
default:
dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
resp->command);
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index 8c351f71f72f..368450cc56c7 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -54,6 +54,10 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code)
priv->scan_block = false;
+ if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+ ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
+ mwifiex_disable_all_tdls_links(priv);
+
/* Free Tx and Rx packets, report disconnect to upper layer */
mwifiex_clean_txrx(priv);
@@ -112,7 +116,7 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code)
adapter->tx_lock_flag = false;
adapter->pps_uapsd_mode = false;
- if (adapter->num_cmd_timeout && adapter->curr_cmd)
+ if (adapter->is_cmd_timedout && adapter->curr_cmd)
return;
priv->media_connected = false;
dev_dbg(adapter->dev,
@@ -289,9 +293,8 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
case EVENT_HS_ACT_REQ:
dev_dbg(adapter->dev, "event: HS_ACT_REQ\n");
- ret = mwifiex_send_cmd_async(priv,
- HostCmd_CMD_802_11_HS_CFG_ENH,
- 0, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_HS_CFG_ENH,
+ 0, 0, NULL, false);
break;
case EVENT_MIC_ERR_UNICAST:
@@ -322,27 +325,34 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
case EVENT_BG_SCAN_REPORT:
dev_dbg(adapter->dev, "event: BGS_REPORT\n");
- ret = mwifiex_send_cmd_async(priv,
- HostCmd_CMD_802_11_BG_SCAN_QUERY,
- HostCmd_ACT_GEN_GET, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_QUERY,
+ HostCmd_ACT_GEN_GET, 0, NULL, false);
break;
case EVENT_PORT_RELEASE:
dev_dbg(adapter->dev, "event: PORT RELEASE\n");
break;
+ case EVENT_EXT_SCAN_REPORT:
+ dev_dbg(adapter->dev, "event: EXT_SCAN Report\n");
+ if (adapter->ext_scan)
+ ret = mwifiex_handle_event_ext_scan_report(priv,
+ adapter->event_skb->data);
+
+ break;
+
case EVENT_WMM_STATUS_CHANGE:
dev_dbg(adapter->dev, "event: WMM status changed\n");
- ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_WMM_GET_STATUS,
- 0, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_WMM_GET_STATUS,
+ 0, 0, NULL, false);
break;
case EVENT_RSSI_LOW:
cfg80211_cqm_rssi_notify(priv->netdev,
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
GFP_KERNEL);
- mwifiex_send_cmd_async(priv, HostCmd_CMD_RSSI_INFO,
- HostCmd_ACT_GEN_GET, 0, NULL);
+ mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO,
+ HostCmd_ACT_GEN_GET, 0, NULL, false);
priv->subsc_evt_rssi_state = RSSI_LOW_RECVD;
dev_dbg(adapter->dev, "event: Beacon RSSI_LOW\n");
break;
@@ -356,8 +366,8 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
cfg80211_cqm_rssi_notify(priv->netdev,
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
GFP_KERNEL);
- mwifiex_send_cmd_async(priv, HostCmd_CMD_RSSI_INFO,
- HostCmd_ACT_GEN_GET, 0, NULL);
+ mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO,
+ HostCmd_ACT_GEN_GET, 0, NULL, false);
priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD;
dev_dbg(adapter->dev, "event: Beacon RSSI_HIGH\n");
break;
@@ -384,15 +394,15 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
break;
case EVENT_IBSS_COALESCED:
dev_dbg(adapter->dev, "event: IBSS_COALESCED\n");
- ret = mwifiex_send_cmd_async(priv,
+ ret = mwifiex_send_cmd(priv,
HostCmd_CMD_802_11_IBSS_COALESCING_STATUS,
- HostCmd_ACT_GEN_GET, 0, NULL);
+ HostCmd_ACT_GEN_GET, 0, NULL, false);
break;
case EVENT_ADDBA:
dev_dbg(adapter->dev, "event: ADDBA Request\n");
- mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_RSP,
- HostCmd_ACT_GEN_SET, 0,
- adapter->event_body);
+ mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP,
+ HostCmd_ACT_GEN_SET, 0,
+ adapter->event_body, false);
break;
case EVENT_DELBA:
dev_dbg(adapter->dev, "event: DELBA Request\n");
@@ -443,10 +453,10 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
priv->csa_expire_time =
jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME);
priv->csa_chan = priv->curr_bss_params.bss_descriptor.channel;
- ret = mwifiex_send_cmd_async(priv,
- HostCmd_CMD_802_11_DEAUTHENTICATE,
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE,
HostCmd_ACT_GEN_SET, 0,
- priv->curr_bss_params.bss_descriptor.mac_address);
+ priv->curr_bss_params.bss_descriptor.mac_address,
+ false);
break;
default:
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index c5cb2ed19ec2..894270611f2c 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -64,6 +64,7 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
*(cmd_queued->condition));
if (status) {
dev_err(adapter->dev, "cmd_wait_q terminated: %d\n", status);
+ mwifiex_cancel_all_pending_cmd(adapter);
return status;
}
@@ -108,19 +109,19 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
"info: Set multicast list=%d\n",
mcast_list->num_multicast_addr);
/* Send multicast addresses to firmware */
- ret = mwifiex_send_cmd_async(priv,
- HostCmd_CMD_MAC_MULTICAST_ADR,
- HostCmd_ACT_GEN_SET, 0,
- mcast_list);
+ ret = mwifiex_send_cmd(priv,
+ HostCmd_CMD_MAC_MULTICAST_ADR,
+ HostCmd_ACT_GEN_SET, 0,
+ mcast_list, false);
}
}
dev_dbg(priv->adapter->dev,
"info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n",
old_pkt_filter, priv->curr_pkt_filter);
if (old_pkt_filter != priv->curr_pkt_filter) {
- ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL,
- HostCmd_ACT_GEN_SET,
- 0, &priv->curr_pkt_filter);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+ HostCmd_ACT_GEN_SET,
+ 0, &priv->curr_pkt_filter, false);
}
return ret;
@@ -237,8 +238,8 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv,
rcu_read_unlock();
- if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
- HostCmd_ACT_GEN_SET, 0, NULL)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
+ HostCmd_ACT_GEN_SET, 0, NULL, false)) {
wiphy_err(priv->adapter->wiphy,
"11D: setting domain info in FW\n");
return -1;
@@ -290,7 +291,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
if (mwifiex_band_to_radio_type(bss_desc->bss_band) ==
HostCmd_SCAN_RADIO_TYPE_BG)
- config_bands = BAND_B | BAND_G | BAND_GN | BAND_GAC;
+ config_bands = BAND_B | BAND_G | BAND_GN;
else
config_bands = BAND_A | BAND_AN | BAND_AAC;
@@ -429,16 +430,13 @@ static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
status = -1;
break;
}
- if (cmd_type == MWIFIEX_SYNC_CMD)
- status = mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_802_11_HS_CFG_ENH,
- HostCmd_ACT_GEN_SET, 0,
- &adapter->hs_cfg);
- else
- status = mwifiex_send_cmd_async(priv,
- HostCmd_CMD_802_11_HS_CFG_ENH,
- HostCmd_ACT_GEN_SET, 0,
- &adapter->hs_cfg);
+
+ status = mwifiex_send_cmd(priv,
+ HostCmd_CMD_802_11_HS_CFG_ENH,
+ HostCmd_ACT_GEN_SET, 0,
+ &adapter->hs_cfg,
+ cmd_type == MWIFIEX_SYNC_CMD);
+
if (hs_cfg->conditions == HS_CFG_CANCEL)
/* Restore previous condition */
adapter->hs_cfg.conditions =
@@ -511,6 +509,9 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg));
hscfg.is_invoke_hostcmd = true;
+ adapter->hs_enabling = true;
+ mwifiex_cancel_all_pending_cmd(adapter);
+
if (mwifiex_set_hs_params(mwifiex_get_priv(adapter,
MWIFIEX_BSS_ROLE_STA),
HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD,
@@ -519,8 +520,9 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
return false;
}
- if (wait_event_interruptible(adapter->hs_activate_wait_q,
- adapter->hs_activate_wait_q_woken)) {
+ if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q,
+ adapter->hs_activate_wait_q_woken,
+ (10 * HZ)) <= 0) {
dev_err(adapter->dev, "hs_activate_wait_q terminated\n");
return false;
}
@@ -586,8 +588,8 @@ int mwifiex_disable_auto_ds(struct mwifiex_private *priv)
auto_ds.auto_ds = DEEP_SLEEP_OFF;
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
- DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
+ DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true);
}
EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds);
@@ -601,8 +603,8 @@ int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate)
{
int ret;
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_TX_RATE_QUERY,
- HostCmd_ACT_GEN_GET, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY,
+ HostCmd_ACT_GEN_GET, 0, NULL, true);
if (!ret) {
if (priv->is_data_rate_auto)
@@ -698,8 +700,8 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv,
pg->power_max = (s8) dbm;
pg->ht_bandwidth = HT_BW_40;
}
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_TXPWR_CFG,
- HostCmd_ACT_GEN_SET, 0, buf);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_TXPWR_CFG,
+ HostCmd_ACT_GEN_SET, 0, buf, true);
kfree(buf);
return ret;
@@ -722,12 +724,11 @@ int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode)
else
adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS;
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
- sub_cmd, BITMAP_STA_PS, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
+ sub_cmd, BITMAP_STA_PS, NULL, true);
if ((!ret) && (sub_cmd == DIS_AUTO_PS))
- ret = mwifiex_send_cmd_async(priv,
- HostCmd_CMD_802_11_PS_MODE_ENH,
- GET_PS, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
+ GET_PS, 0, NULL, false);
return ret;
}
@@ -851,9 +852,9 @@ static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv,
struct mwifiex_ds_encrypt_key *encrypt_key)
{
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
- HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED,
- encrypt_key);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
+ HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED,
+ encrypt_key, true);
}
/*
@@ -865,6 +866,7 @@ static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv,
static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
struct mwifiex_ds_encrypt_key *encrypt_key)
{
+ struct mwifiex_adapter *adapter = priv->adapter;
int ret;
struct mwifiex_wep_key *wep_key;
int index;
@@ -879,10 +881,17 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
/* Copy the required key as the current key */
wep_key = &priv->wep_key[index];
if (!wep_key->key_length) {
- dev_err(priv->adapter->dev,
+ dev_err(adapter->dev,
"key not set, so cannot enable it\n");
return -1;
}
+
+ if (adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) {
+ memcpy(encrypt_key->key_material,
+ wep_key->key_material, wep_key->key_length);
+ encrypt_key->key_len = wep_key->key_length;
+ }
+
priv->wep_key_curr_index = (u16) index;
priv->sec_info.wep_enabled = 1;
} else {
@@ -897,21 +906,32 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
priv->sec_info.wep_enabled = 1;
}
if (wep_key->key_length) {
+ void *enc_key;
+
+ if (encrypt_key->key_disable)
+ memset(&priv->wep_key[index], 0,
+ sizeof(struct mwifiex_wep_key));
+
+ if (adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2)
+ enc_key = encrypt_key;
+ else
+ enc_key = NULL;
+
/* Send request to firmware */
- ret = mwifiex_send_cmd_async(priv,
- HostCmd_CMD_802_11_KEY_MATERIAL,
- HostCmd_ACT_GEN_SET, 0, NULL);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
+ HostCmd_ACT_GEN_SET, 0, enc_key, false);
if (ret)
return ret;
}
+
if (priv->sec_info.wep_enabled)
priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
else
priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_MAC_CONTROL,
- HostCmd_ACT_GEN_SET, 0,
- &priv->curr_pkt_filter);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
+ HostCmd_ACT_GEN_SET, 0,
+ &priv->curr_pkt_filter, true);
return ret;
}
@@ -946,10 +966,9 @@ static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv,
*/
/* Send the key as PTK to firmware */
encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
- ret = mwifiex_send_cmd_async(priv,
- HostCmd_CMD_802_11_KEY_MATERIAL,
- HostCmd_ACT_GEN_SET,
- KEY_INFO_ENABLED, encrypt_key);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
+ HostCmd_ACT_GEN_SET,
+ KEY_INFO_ENABLED, encrypt_key, false);
if (ret)
return ret;
@@ -973,15 +992,13 @@ static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv,
encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
if (remove_key)
- ret = mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_802_11_KEY_MATERIAL,
- HostCmd_ACT_GEN_SET,
- !KEY_INFO_ENABLED, encrypt_key);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
+ HostCmd_ACT_GEN_SET,
+ !KEY_INFO_ENABLED, encrypt_key, true);
else
- ret = mwifiex_send_cmd_sync(priv,
- HostCmd_CMD_802_11_KEY_MATERIAL,
- HostCmd_ACT_GEN_SET,
- KEY_INFO_ENABLED, encrypt_key);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
+ HostCmd_ACT_GEN_SET,
+ KEY_INFO_ENABLED, encrypt_key, true);
return ret;
}
@@ -1044,19 +1061,27 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key));
encrypt_key.key_len = key_len;
+ encrypt_key.key_index = key_index;
if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
encrypt_key.is_igtk_key = true;
if (!disable) {
- encrypt_key.key_index = key_index;
if (key_len)
memcpy(encrypt_key.key_material, key, key_len);
+ else
+ encrypt_key.is_current_wep_key = true;
+
if (mac_addr)
memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
- if (kp && kp->seq && kp->seq_len)
+ if (kp && kp->seq && kp->seq_len) {
memcpy(encrypt_key.pn, kp->seq, kp->seq_len);
+ encrypt_key.pn_len = kp->seq_len;
+ encrypt_key.is_rx_seq_valid = true;
+ }
} else {
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
+ return 0;
encrypt_key.key_disable = true;
if (mac_addr)
memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
@@ -1077,8 +1102,8 @@ mwifiex_get_ver_ext(struct mwifiex_private *priv)
struct mwifiex_ver_ext ver_ext;
memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext));
- if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_VERSION_EXT,
- HostCmd_ACT_GEN_GET, 0, &ver_ext))
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT,
+ HostCmd_ACT_GEN_GET, 0, &ver_ext, true))
return -1;
return 0;
@@ -1103,8 +1128,8 @@ mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action,
ieee80211_frequency_to_channel(chan->center_freq);
roc_cfg.duration = cpu_to_le32(duration);
}
- if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_REMAIN_ON_CHAN,
- action, 0, &roc_cfg)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN,
+ action, 0, &roc_cfg, true)) {
dev_err(priv->adapter->dev, "failed to remain on channel\n");
return -1;
}
@@ -1136,8 +1161,8 @@ mwifiex_set_bss_role(struct mwifiex_private *priv, u8 bss_role)
break;
}
- mwifiex_send_cmd_sync(priv, HostCmd_CMD_SET_BSS_MODE,
- HostCmd_ACT_GEN_SET, 0, NULL);
+ mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
+ HostCmd_ACT_GEN_SET, 0, NULL, true);
return mwifiex_sta_init_cmd(priv, false);
}
@@ -1152,8 +1177,8 @@ int
mwifiex_get_stats_info(struct mwifiex_private *priv,
struct mwifiex_ds_get_stats *log)
{
- return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_GET_LOG,
- HostCmd_ACT_GEN_GET, 0, log);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_GET_LOG,
+ HostCmd_ACT_GEN_GET, 0, log, true);
}
/*
@@ -1195,8 +1220,7 @@ static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv,
return -1;
}
- return mwifiex_send_cmd_sync(priv, cmd_no, action, 0, reg_rw);
-
+ return mwifiex_send_cmd(priv, cmd_no, action, 0, reg_rw, true);
}
/*
@@ -1261,8 +1285,8 @@ mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes,
rd_eeprom.byte_count = cpu_to_le16((u16) bytes);
/* Send request to firmware */
- ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_EEPROM_ACCESS,
- HostCmd_ACT_GEN_GET, 0, &rd_eeprom);
+ ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS,
+ HostCmd_ACT_GEN_GET, 0, &rd_eeprom, true);
if (!ret)
memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA);
@@ -1391,7 +1415,7 @@ static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv,
* with requisite parameters and calls the IOCTL handler.
*/
int
-mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len)
+mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len)
{
struct mwifiex_ds_misc_gen_ie gen_ie;
diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c
index 4651d676df38..ed26387eccf5 100644
--- a/drivers/net/wireless/mwifiex/sta_rx.c
+++ b/drivers/net/wireless/mwifiex/sta_rx.c
@@ -88,11 +88,14 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv,
struct rxpd *local_rx_pd;
int hdr_chop;
struct ethhdr *eth;
+ u16 rx_pkt_off, rx_pkt_len;
+ u8 *offset;
local_rx_pd = (struct rxpd *) (skb->data);
- rx_pkt_hdr = (void *)local_rx_pd +
- le16_to_cpu(local_rx_pd->rx_pkt_offset);
+ rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset);
+ rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length);
+ rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off;
if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
sizeof(bridge_tunnel_header))) ||
@@ -142,6 +145,12 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv,
return 0;
}
+ if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
+ offset = (u8 *)local_rx_pd + rx_pkt_off;
+ mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len);
+ }
+
priv->rxpd_rate = local_rx_pd->rx_rate;
priv->rxpd_htinfo = local_rx_pd->ht_info;
@@ -192,26 +201,7 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv,
return ret;
}
- if (rx_pkt_type == PKT_TYPE_AMSDU) {
- struct sk_buff_head list;
- struct sk_buff *rx_skb;
-
- __skb_queue_head_init(&list);
-
- skb_pull(skb, rx_pkt_offset);
- skb_trim(skb, rx_pkt_length);
-
- ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
- priv->wdev->iftype, 0, false);
-
- while (!skb_queue_empty(&list)) {
- rx_skb = __skb_dequeue(&list);
- ret = mwifiex_recv_packet(priv, rx_skb);
- if (ret == -1)
- dev_err(adapter->dev, "Rx of A-MSDU failed");
- }
- return 0;
- } else if (rx_pkt_type == PKT_TYPE_MGMT) {
+ if (rx_pkt_type == PKT_TYPE_MGMT) {
ret = mwifiex_process_mgmt_packet(priv, skb);
if (ret)
dev_err(adapter->dev, "Rx of mgmt packet failed");
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c
index 354d64c9606f..1236a5de7bca 100644
--- a/drivers/net/wireless/mwifiex/sta_tx.c
+++ b/drivers/net/wireless/mwifiex/sta_tx.c
@@ -95,6 +95,9 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv,
}
}
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
+ local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET;
+
/* Offset of actual data */
pkt_offset = sizeof(struct txpd) + pad;
if (pkt_type == PKT_TYPE_MGMT) {
diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c
new file mode 100644
index 000000000000..97662a1ba58c
--- /dev/null
+++ b/drivers/net/wireless/mwifiex/tdls.c
@@ -0,0 +1,1044 @@
+/* Marvell Wireless LAN device driver: TDLS handling
+ *
+ * Copyright (C) 2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11n_rxreorder.h"
+#include "11ac.h"
+
+#define TDLS_REQ_FIX_LEN 6
+#define TDLS_RESP_FIX_LEN 8
+#define TDLS_CONFIRM_FIX_LEN 6
+
+static void
+mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status)
+{
+ struct mwifiex_ra_list_tbl *ra_list;
+ struct list_head *tid_list;
+ struct sk_buff *skb, *tmp;
+ struct mwifiex_txinfo *tx_info;
+ unsigned long flags;
+ u32 tid;
+ u8 tid_down;
+
+ dev_dbg(priv->adapter->dev, "%s: %pM\n", __func__, mac);
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+ skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) {
+ if (!ether_addr_equal(mac, skb->data))
+ continue;
+
+ __skb_unlink(skb, &priv->tdls_txq);
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ tid = skb->priority;
+ tid_down = mwifiex_wmm_downgrade_tid(priv, tid);
+
+ if (status == TDLS_SETUP_COMPLETE) {
+ ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac);
+ ra_list->tdls_link = true;
+ tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
+ } else {
+ tid_list = &priv->wmm.tid_tbl_ptr[tid_down].ra_list;
+ if (!list_empty(tid_list))
+ ra_list = list_first_entry(tid_list,
+ struct mwifiex_ra_list_tbl, list);
+ else
+ ra_list = NULL;
+ tx_info->flags &= ~MWIFIEX_BUF_FLAG_TDLS_PKT;
+ }
+
+ if (!ra_list) {
+ mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
+ continue;
+ }
+
+ skb_queue_tail(&ra_list->skb_head, skb);
+
+ ra_list->ba_pkt_count++;
+ ra_list->total_pkt_count++;
+
+ if (atomic_read(&priv->wmm.highest_queued_prio) <
+ tos_to_tid_inv[tid_down])
+ atomic_set(&priv->wmm.highest_queued_prio,
+ tos_to_tid_inv[tid_down]);
+
+ atomic_inc(&priv->wmm.tx_pkts_queued);
+ }
+
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+ return;
+}
+
+static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, u8 *mac)
+{
+ struct mwifiex_ra_list_tbl *ra_list;
+ struct list_head *ra_list_head;
+ struct sk_buff *skb, *tmp;
+ unsigned long flags;
+ int i;
+
+ dev_dbg(priv->adapter->dev, "%s: %pM\n", __func__, mac);
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+ for (i = 0; i < MAX_NUM_TID; i++) {
+ if (!list_empty(&priv->wmm.tid_tbl_ptr[i].ra_list)) {
+ ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list;
+ list_for_each_entry(ra_list, ra_list_head, list) {
+ skb_queue_walk_safe(&ra_list->skb_head, skb,
+ tmp) {
+ if (!ether_addr_equal(mac, skb->data))
+ continue;
+ __skb_unlink(skb, &ra_list->skb_head);
+ atomic_dec(&priv->wmm.tx_pkts_queued);
+ ra_list->total_pkt_count--;
+ skb_queue_tail(&priv->tdls_txq, skb);
+ }
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+ return;
+}
+
+/* This function appends rate TLV to scan config command. */
+static int
+mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ u8 rates[MWIFIEX_SUPPORTED_RATES], *pos;
+ u16 rates_size, supp_rates_size, ext_rates_size;
+
+ memset(rates, 0, sizeof(rates));
+ rates_size = mwifiex_get_supported_rates(priv, rates);
+
+ supp_rates_size = min_t(u16, rates_size, MWIFIEX_TDLS_SUPPORTED_RATES);
+
+ if (skb_tailroom(skb) < rates_size + 4) {
+ dev_err(priv->adapter->dev,
+ "Insuffient space while adding rates\n");
+ return -ENOMEM;
+ }
+
+ pos = skb_put(skb, supp_rates_size + 2);
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = supp_rates_size;
+ memcpy(pos, rates, supp_rates_size);
+
+ if (rates_size > MWIFIEX_TDLS_SUPPORTED_RATES) {
+ ext_rates_size = rates_size - MWIFIEX_TDLS_SUPPORTED_RATES;
+ pos = skb_put(skb, ext_rates_size + 2);
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
+ *pos++ = ext_rates_size;
+ memcpy(pos, rates + MWIFIEX_TDLS_SUPPORTED_RATES,
+ ext_rates_size);
+ }
+
+ return 0;
+}
+
+static void mwifiex_tdls_add_aid(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct ieee_types_assoc_rsp *assoc_rsp;
+ u8 *pos;
+
+ assoc_rsp = (struct ieee_types_assoc_rsp *)&priv->assoc_rsp_buf;
+ pos = (void *)skb_put(skb, 4);
+ *pos++ = WLAN_EID_AID;
+ *pos++ = 2;
+ *pos++ = le16_to_cpu(assoc_rsp->a_id);
+
+ return;
+}
+
+static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct ieee80211_vht_cap vht_cap;
+ u8 *pos;
+
+ pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
+ *pos++ = WLAN_EID_VHT_CAPABILITY;
+ *pos++ = sizeof(struct ieee80211_vht_cap);
+
+ memset(&vht_cap, 0, sizeof(struct ieee80211_vht_cap));
+
+ mwifiex_fill_vht_cap_tlv(priv, &vht_cap, priv->curr_bss_params.band);
+ memcpy(pos, &vht_cap, sizeof(vht_cap));
+
+ return 0;
+}
+
+static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv,
+ u8 *mac, struct sk_buff *skb)
+{
+ struct mwifiex_bssdescriptor *bss_desc;
+ struct ieee80211_vht_operation *vht_oper;
+ struct ieee80211_vht_cap *vht_cap, *ap_vht_cap = NULL;
+ struct mwifiex_sta_node *sta_ptr;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ u8 supp_chwd_set, peer_supp_chwd_set;
+ u8 *pos, ap_supp_chwd_set, chan_bw;
+ u16 mcs_map_user, mcs_map_resp, mcs_map_result;
+ u16 mcs_user, mcs_resp, nss;
+ u32 usr_vht_cap_info;
+
+ bss_desc = &priv->curr_bss_params.bss_descriptor;
+
+ sta_ptr = mwifiex_get_sta_entry(priv, mac);
+ if (unlikely(!sta_ptr)) {
+ dev_warn(adapter->dev, "TDLS peer station not found in list\n");
+ return -1;
+ }
+
+ if (!mwifiex_is_bss_in_11ac_mode(priv)) {
+ if (sta_ptr->tdls_cap.extcap.ext_capab[7] &
+ WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) {
+ dev_dbg(adapter->dev,
+ "TDLS peer doesn't support wider bandwitdh\n");
+ return 0;
+ }
+ } else {
+ ap_vht_cap = bss_desc->bcn_vht_cap;
+ }
+
+ pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_operation) + 2);
+ *pos++ = WLAN_EID_VHT_OPERATION;
+ *pos++ = sizeof(struct ieee80211_vht_operation);
+ vht_oper = (struct ieee80211_vht_operation *)pos;
+
+ if (bss_desc->bss_band & BAND_A)
+ usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
+ else
+ usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
+
+ /* find the minmum bandwith between AP/TDLS peers */
+ vht_cap = &sta_ptr->tdls_cap.vhtcap;
+ supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info);
+ peer_supp_chwd_set =
+ GET_VHTCAP_CHWDSET(le32_to_cpu(vht_cap->vht_cap_info));
+ supp_chwd_set = min_t(u8, supp_chwd_set, peer_supp_chwd_set);
+
+ /* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */
+
+ if (ap_vht_cap && sta_ptr->tdls_cap.extcap.ext_capab[7] &
+ WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) {
+ ap_supp_chwd_set =
+ GET_VHTCAP_CHWDSET(le32_to_cpu(ap_vht_cap->vht_cap_info));
+ supp_chwd_set = min_t(u8, supp_chwd_set, ap_supp_chwd_set);
+ }
+
+ switch (supp_chwd_set) {
+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ;
+ break;
+ default:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
+ break;
+ }
+
+ mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support);
+ mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map);
+ mcs_map_result = 0;
+
+ for (nss = 1; nss <= 8; nss++) {
+ mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
+ mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
+
+ if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) ||
+ (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED))
+ SET_VHTNSSMCS(mcs_map_result, nss,
+ IEEE80211_VHT_MCS_NOT_SUPPORTED);
+ else
+ SET_VHTNSSMCS(mcs_map_result, nss,
+ min_t(u16, mcs_user, mcs_resp));
+ }
+
+ vht_oper->basic_mcs_set = cpu_to_le16(mcs_map_result);
+
+ switch (vht_oper->chan_width) {
+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
+ chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
+ chan_bw = IEEE80211_VHT_CHANWIDTH_160MHZ;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+ chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ;
+ break;
+ default:
+ chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT;
+ break;
+ }
+ vht_oper->center_freq_seg1_idx =
+ mwifiex_get_center_freq_index(priv, BAND_AAC,
+ bss_desc->channel,
+ chan_bw);
+
+ return 0;
+}
+
+static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct ieee_types_extcap *extcap;
+
+ extcap = (void *)skb_put(skb, sizeof(struct ieee_types_extcap));
+ extcap->ieee_hdr.element_id = WLAN_EID_EXT_CAPABILITY;
+ extcap->ieee_hdr.len = 8;
+ memset(extcap->ext_capab, 0, 8);
+ extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED;
+
+ if (priv->adapter->is_hw_11ac_capable)
+ extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED;
+}
+
+static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb)
+{
+ u8 *pos = (void *)skb_put(skb, 3);
+
+ *pos++ = WLAN_EID_QOS_CAPA;
+ *pos++ = 1;
+ *pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB;
+}
+
+static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
+ u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, struct sk_buff *skb)
+{
+ struct ieee80211_tdls_data *tf;
+ int ret;
+ u16 capab;
+ struct ieee80211_ht_cap *ht_cap;
+ u8 radio, *pos;
+
+ capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap;
+
+ tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
+ memcpy(tf->da, peer, ETH_ALEN);
+ memcpy(tf->sa, priv->curr_addr, ETH_ALEN);
+ tf->ether_type = cpu_to_be16(ETH_P_TDLS);
+ tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ tf->category = WLAN_CATEGORY_TDLS;
+ tf->action_code = WLAN_TDLS_SETUP_REQUEST;
+ skb_put(skb, sizeof(tf->u.setup_req));
+ tf->u.setup_req.dialog_token = dialog_token;
+ tf->u.setup_req.capability = cpu_to_le16(capab);
+ ret = mwifiex_tdls_append_rates_ie(priv, skb);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+ *pos++ = WLAN_EID_HT_CAPABILITY;
+ *pos++ = sizeof(struct ieee80211_ht_cap);
+ ht_cap = (void *)pos;
+ radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
+ ret = mwifiex_fill_cap_info(priv, radio, ht_cap);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ if (priv->adapter->is_hw_11ac_capable) {
+ ret = mwifiex_tdls_add_vht_capab(priv, skb);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+ mwifiex_tdls_add_aid(priv, skb);
+ }
+
+ mwifiex_tdls_add_ext_capab(priv, skb);
+ mwifiex_tdls_add_qos_capab(skb);
+ break;
+
+ case WLAN_TDLS_SETUP_RESPONSE:
+ tf->category = WLAN_CATEGORY_TDLS;
+ tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
+ skb_put(skb, sizeof(tf->u.setup_resp));
+ tf->u.setup_resp.status_code = cpu_to_le16(status_code);
+ tf->u.setup_resp.dialog_token = dialog_token;
+ tf->u.setup_resp.capability = cpu_to_le16(capab);
+ ret = mwifiex_tdls_append_rates_ie(priv, skb);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+ *pos++ = WLAN_EID_HT_CAPABILITY;
+ *pos++ = sizeof(struct ieee80211_ht_cap);
+ ht_cap = (void *)pos;
+ radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
+ ret = mwifiex_fill_cap_info(priv, radio, ht_cap);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ if (priv->adapter->is_hw_11ac_capable) {
+ ret = mwifiex_tdls_add_vht_capab(priv, skb);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+ mwifiex_tdls_add_aid(priv, skb);
+ }
+
+ mwifiex_tdls_add_ext_capab(priv, skb);
+ mwifiex_tdls_add_qos_capab(skb);
+ break;
+
+ case WLAN_TDLS_SETUP_CONFIRM:
+ tf->category = WLAN_CATEGORY_TDLS;
+ tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
+ skb_put(skb, sizeof(tf->u.setup_cfm));
+ tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
+ tf->u.setup_cfm.dialog_token = dialog_token;
+ if (priv->adapter->is_hw_11ac_capable) {
+ ret = mwifiex_tdls_add_vht_oper(priv, peer, skb);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+ }
+ break;
+
+ case WLAN_TDLS_TEARDOWN:
+ tf->category = WLAN_CATEGORY_TDLS;
+ tf->action_code = WLAN_TDLS_TEARDOWN;
+ skb_put(skb, sizeof(tf->u.teardown));
+ tf->u.teardown.reason_code = cpu_to_le16(status_code);
+ break;
+
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ tf->category = WLAN_CATEGORY_TDLS;
+ tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
+ skb_put(skb, sizeof(tf->u.discover_req));
+ tf->u.discover_req.dialog_token = dialog_token;
+ break;
+ default:
+ dev_err(priv->adapter->dev, "Unknown TDLS frame type.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void
+mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid)
+{
+ struct ieee80211_tdls_lnkie *lnkid;
+
+ lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
+ lnkid->ie_type = WLAN_EID_LINK_ID;
+ lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) -
+ sizeof(struct ieee_types_header);
+
+ memcpy(lnkid->bssid, bssid, ETH_ALEN);
+ memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
+ memcpy(lnkid->resp_sta, peer, ETH_ALEN);
+}
+
+int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv,
+ u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *extra_ies,
+ size_t extra_ies_len)
+{
+ struct sk_buff *skb;
+ struct mwifiex_txinfo *tx_info;
+ struct timeval tv;
+ int ret;
+ u16 skb_len;
+
+ skb_len = MWIFIEX_MIN_DATA_HEADER_LEN +
+ max(sizeof(struct ieee80211_mgmt),
+ sizeof(struct ieee80211_tdls_data)) +
+ MWIFIEX_MGMT_FRAME_HEADER_SIZE +
+ MWIFIEX_SUPPORTED_RATES +
+ 3 + /* Qos Info */
+ sizeof(struct ieee_types_extcap) +
+ sizeof(struct ieee80211_ht_cap) +
+ sizeof(struct ieee_types_bss_co_2040) +
+ sizeof(struct ieee80211_ht_operation) +
+ sizeof(struct ieee80211_tdls_lnkie) +
+ extra_ies_len;
+
+ if (priv->adapter->is_hw_11ac_capable)
+ skb_len += sizeof(struct ieee_types_vht_cap) +
+ sizeof(struct ieee_types_vht_oper) +
+ sizeof(struct ieee_types_aid);
+
+ skb = dev_alloc_skb(skb_len);
+ if (!skb) {
+ dev_err(priv->adapter->dev,
+ "allocate skb failed for management frame\n");
+ return -ENOMEM;
+ }
+ skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_SETUP_CONFIRM:
+ case WLAN_TDLS_TEARDOWN:
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code,
+ dialog_token, status_code,
+ skb);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+ if (extra_ies_len)
+ memcpy(skb_put(skb, extra_ies_len), extra_ies,
+ extra_ies_len);
+ mwifiex_tdls_add_link_ie(skb, priv->curr_addr, peer,
+ priv->cfg_bssid);
+ break;
+ case WLAN_TDLS_SETUP_RESPONSE:
+ ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code,
+ dialog_token, status_code,
+ skb);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+ if (extra_ies_len)
+ memcpy(skb_put(skb, extra_ies_len), extra_ies,
+ extra_ies_len);
+ mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr,
+ priv->cfg_bssid);
+ break;
+ }
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_SETUP_RESPONSE:
+ skb->priority = MWIFIEX_PRIO_BK;
+ break;
+ default:
+ skb->priority = MWIFIEX_PRIO_VI;
+ break;
+ }
+
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+
+ do_gettimeofday(&tv);
+ skb->tstamp = timeval_to_ktime(tv);
+ mwifiex_queue_tx_pkt(priv, skb);
+
+ return 0;
+}
+
+static int
+mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt;
+ u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ int ret;
+ u16 capab;
+ struct ieee80211_ht_cap *ht_cap;
+ u8 radio, *pos;
+
+ capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap;
+
+ mgmt = (void *)skb_put(skb, offsetof(struct ieee80211_mgmt, u));
+
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, peer, ETH_ALEN);
+ memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
+
+ /* add address 4 */
+ pos = skb_put(skb, ETH_ALEN);
+
+ switch (action_code) {
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ skb_put(skb, sizeof(mgmt->u.action.u.tdls_discover_resp) + 1);
+ mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
+ mgmt->u.action.u.tdls_discover_resp.action_code =
+ WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
+ mgmt->u.action.u.tdls_discover_resp.dialog_token =
+ dialog_token;
+ mgmt->u.action.u.tdls_discover_resp.capability =
+ cpu_to_le16(capab);
+ /* move back for addr4 */
+ memmove(pos + ETH_ALEN, &mgmt->u.action.category,
+ sizeof(mgmt->u.action.u.tdls_discover_resp));
+ /* init address 4 */
+ memcpy(pos, bc_addr, ETH_ALEN);
+
+ ret = mwifiex_tdls_append_rates_ie(priv, skb);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+ *pos++ = WLAN_EID_HT_CAPABILITY;
+ *pos++ = sizeof(struct ieee80211_ht_cap);
+ ht_cap = (void *)pos;
+ radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
+ ret = mwifiex_fill_cap_info(priv, radio, ht_cap);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ if (priv->adapter->is_hw_11ac_capable) {
+ ret = mwifiex_tdls_add_vht_capab(priv, skb);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+ mwifiex_tdls_add_aid(priv, skb);
+ }
+
+ mwifiex_tdls_add_ext_capab(priv, skb);
+ mwifiex_tdls_add_qos_capab(skb);
+ break;
+ default:
+ dev_err(priv->adapter->dev, "Unknown TDLS action frame type\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv,
+ u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *extra_ies,
+ size_t extra_ies_len)
+{
+ struct sk_buff *skb;
+ struct mwifiex_txinfo *tx_info;
+ struct timeval tv;
+ u8 *pos;
+ u32 pkt_type, tx_control;
+ u16 pkt_len, skb_len;
+
+ skb_len = MWIFIEX_MIN_DATA_HEADER_LEN +
+ max(sizeof(struct ieee80211_mgmt),
+ sizeof(struct ieee80211_tdls_data)) +
+ MWIFIEX_MGMT_FRAME_HEADER_SIZE +
+ MWIFIEX_SUPPORTED_RATES +
+ sizeof(struct ieee_types_extcap) +
+ sizeof(struct ieee80211_ht_cap) +
+ sizeof(struct ieee_types_bss_co_2040) +
+ sizeof(struct ieee80211_ht_operation) +
+ sizeof(struct ieee80211_tdls_lnkie) +
+ extra_ies_len +
+ 3 + /* Qos Info */
+ ETH_ALEN; /* Address4 */
+
+ if (priv->adapter->is_hw_11ac_capable)
+ skb_len += sizeof(struct ieee_types_vht_cap) +
+ sizeof(struct ieee_types_vht_oper) +
+ sizeof(struct ieee_types_aid);
+
+ skb = dev_alloc_skb(skb_len);
+ if (!skb) {
+ dev_err(priv->adapter->dev,
+ "allocate skb failed for management frame\n");
+ return -ENOMEM;
+ }
+
+ skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
+
+ pkt_type = PKT_TYPE_MGMT;
+ tx_control = 0;
+ pos = skb_put(skb, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len));
+ memset(pos, 0, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len));
+ memcpy(pos, &pkt_type, sizeof(pkt_type));
+ memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control));
+
+ if (mwifiex_construct_tdls_action_frame(priv, peer, action_code,
+ dialog_token, status_code,
+ skb)) {
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+
+ if (extra_ies_len)
+ memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
+
+ /* the TDLS link IE is always added last we are the responder */
+
+ mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr,
+ priv->cfg_bssid);
+
+ skb->priority = MWIFIEX_PRIO_VI;
+
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+ tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
+
+ pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len);
+ memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len,
+ sizeof(pkt_len));
+ do_gettimeofday(&tv);
+ skb->tstamp = timeval_to_ktime(tv);
+ mwifiex_queue_tx_pkt(priv, skb);
+
+ return 0;
+}
+
+/* This function process tdls action frame from peer.
+ * Peer capabilities are stored into station node structure.
+ */
+void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
+ u8 *buf, int len)
+{
+ struct mwifiex_sta_node *sta_ptr;
+ u8 *peer, *pos, *end;
+ u8 i, action, basic;
+ int ie_len = 0;
+
+ if (len < (sizeof(struct ethhdr) + 3))
+ return;
+ if (*(buf + sizeof(struct ethhdr)) != WLAN_TDLS_SNAP_RFTYPE)
+ return;
+ if (*(buf + sizeof(struct ethhdr) + 1) != WLAN_CATEGORY_TDLS)
+ return;
+
+ peer = buf + ETH_ALEN;
+ action = *(buf + sizeof(struct ethhdr) + 2);
+
+ /* just handle TDLS setup request/response/confirm */
+ if (action > WLAN_TDLS_SETUP_CONFIRM)
+ return;
+
+ dev_dbg(priv->adapter->dev,
+ "rx:tdls action: peer=%pM, action=%d\n", peer, action);
+
+ sta_ptr = mwifiex_add_sta_entry(priv, peer);
+ if (!sta_ptr)
+ return;
+
+ switch (action) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN))
+ return;
+
+ pos = buf + sizeof(struct ethhdr) + 4;
+ /* payload 1+ category 1 + action 1 + dialog 1 */
+ sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos);
+ ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN;
+ pos += 2;
+ break;
+
+ case WLAN_TDLS_SETUP_RESPONSE:
+ if (len < (sizeof(struct ethhdr) + TDLS_RESP_FIX_LEN))
+ return;
+ /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/
+ pos = buf + sizeof(struct ethhdr) + 6;
+ sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos);
+ ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN;
+ pos += 2;
+ break;
+
+ case WLAN_TDLS_SETUP_CONFIRM:
+ if (len < (sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN))
+ return;
+ pos = buf + sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN;
+ ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN;
+ break;
+ default:
+ dev_warn(priv->adapter->dev, "Unknown TDLS frame type.\n");
+ return;
+ }
+
+ for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) {
+ if (pos + 2 + pos[1] > end)
+ break;
+
+ switch (*pos) {
+ case WLAN_EID_SUPP_RATES:
+ sta_ptr->tdls_cap.rates_len = pos[1];
+ for (i = 0; i < pos[1]; i++)
+ sta_ptr->tdls_cap.rates[i] = pos[i + 2];
+ break;
+
+ case WLAN_EID_EXT_SUPP_RATES:
+ basic = sta_ptr->tdls_cap.rates_len;
+ for (i = 0; i < pos[1]; i++)
+ sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2];
+ sta_ptr->tdls_cap.rates_len += pos[1];
+ break;
+ case WLAN_EID_HT_CAPABILITY:
+ memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos,
+ sizeof(struct ieee80211_ht_cap));
+ sta_ptr->is_11n_enabled = 1;
+ break;
+ case WLAN_EID_HT_OPERATION:
+ memcpy(&sta_ptr->tdls_cap.ht_oper, pos,
+ sizeof(struct ieee80211_ht_operation));
+ break;
+ case WLAN_EID_BSS_COEX_2040:
+ sta_ptr->tdls_cap.coex_2040 = pos[2];
+ break;
+ case WLAN_EID_EXT_CAPABILITY:
+ memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos,
+ sizeof(struct ieee_types_header) +
+ min_t(u8, pos[1], 8));
+ break;
+ case WLAN_EID_RSN:
+ memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos,
+ sizeof(struct ieee_types_header) + pos[1]);
+ break;
+ case WLAN_EID_QOS_CAPA:
+ sta_ptr->tdls_cap.qos_info = pos[2];
+ break;
+ case WLAN_EID_VHT_OPERATION:
+ if (priv->adapter->is_hw_11ac_capable)
+ memcpy(&sta_ptr->tdls_cap.vhtoper, pos,
+ sizeof(struct ieee80211_vht_operation));
+ break;
+ case WLAN_EID_VHT_CAPABILITY:
+ if (priv->adapter->is_hw_11ac_capable) {
+ memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos,
+ sizeof(struct ieee80211_vht_cap));
+ sta_ptr->is_11ac_enabled = 1;
+ }
+ break;
+ case WLAN_EID_AID:
+ if (priv->adapter->is_hw_11ac_capable)
+ sta_ptr->tdls_cap.aid =
+ le16_to_cpu(*(__le16 *)(pos + 2));
+ default:
+ break;
+ }
+ }
+
+ return;
+}
+
+static int
+mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer)
+{
+ struct mwifiex_sta_node *sta_ptr;
+ struct mwifiex_ds_tdls_oper tdls_oper;
+
+ memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper));
+ sta_ptr = mwifiex_get_sta_entry(priv, peer);
+
+ if (!sta_ptr || sta_ptr->tdls_status == TDLS_SETUP_FAILURE) {
+ dev_err(priv->adapter->dev,
+ "link absent for peer %pM; cannot config\n", peer);
+ return -EINVAL;
+ }
+
+ memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN);
+ tdls_oper.tdls_action = MWIFIEX_TDLS_CONFIG_LINK;
+ return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER,
+ HostCmd_ACT_GEN_SET, 0, &tdls_oper, true);
+}
+
+static int
+mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer)
+{
+ struct mwifiex_sta_node *sta_ptr;
+ struct mwifiex_ds_tdls_oper tdls_oper;
+
+ memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper));
+ sta_ptr = mwifiex_get_sta_entry(priv, peer);
+
+ if (sta_ptr && sta_ptr->tdls_status == TDLS_SETUP_INPROGRESS) {
+ dev_dbg(priv->adapter->dev,
+ "Setup already in progress for peer %pM\n", peer);
+ return 0;
+ }
+
+ sta_ptr = mwifiex_add_sta_entry(priv, peer);
+ if (!sta_ptr)
+ return -ENOMEM;
+
+ sta_ptr->tdls_status = TDLS_SETUP_INPROGRESS;
+ mwifiex_hold_tdls_packets(priv, peer);
+ memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN);
+ tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK;
+ return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER,
+ HostCmd_ACT_GEN_SET, 0, &tdls_oper, true);
+}
+
+static int
+mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer)
+{
+ struct mwifiex_sta_node *sta_ptr;
+ struct mwifiex_ds_tdls_oper tdls_oper;
+ unsigned long flags;
+
+ memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper));
+ sta_ptr = mwifiex_get_sta_entry(priv, peer);
+
+ if (sta_ptr) {
+ if (sta_ptr->is_11n_enabled) {
+ mwifiex_11n_cleanup_reorder_tbl(priv);
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock,
+ flags);
+ mwifiex_11n_delete_all_tx_ba_stream_tbl(priv);
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+ flags);
+ }
+ mwifiex_del_sta_entry(priv, peer);
+ }
+
+ mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN);
+ memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN);
+ tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK;
+ return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER,
+ HostCmd_ACT_GEN_SET, 0, &tdls_oper, true);
+}
+
+static int
+mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer)
+{
+ struct mwifiex_sta_node *sta_ptr;
+ struct ieee80211_mcs_info mcs;
+ unsigned long flags;
+ int i;
+
+ sta_ptr = mwifiex_get_sta_entry(priv, peer);
+
+ if (sta_ptr && (sta_ptr->tdls_status != TDLS_SETUP_FAILURE)) {
+ dev_dbg(priv->adapter->dev,
+ "tdls: enable link %pM success\n", peer);
+
+ sta_ptr->tdls_status = TDLS_SETUP_COMPLETE;
+
+ mcs = sta_ptr->tdls_cap.ht_capb.mcs;
+ if (mcs.rx_mask[0] != 0xff)
+ sta_ptr->is_11n_enabled = true;
+ if (sta_ptr->is_11n_enabled) {
+ if (le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info) &
+ IEEE80211_HT_CAP_MAX_AMSDU)
+ sta_ptr->max_amsdu =
+ MWIFIEX_TX_DATA_BUF_SIZE_8K;
+ else
+ sta_ptr->max_amsdu =
+ MWIFIEX_TX_DATA_BUF_SIZE_4K;
+
+ for (i = 0; i < MAX_NUM_TID; i++)
+ sta_ptr->ampdu_sta[i] =
+ priv->aggr_prio_tbl[i].ampdu_user;
+ } else {
+ for (i = 0; i < MAX_NUM_TID; i++)
+ sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED;
+ }
+
+ memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq));
+ mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE);
+ } else {
+ dev_dbg(priv->adapter->dev,
+ "tdls: enable link %pM failed\n", peer);
+ if (sta_ptr) {
+ mwifiex_11n_cleanup_reorder_tbl(priv);
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock,
+ flags);
+ mwifiex_11n_delete_all_tx_ba_stream_tbl(priv);
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+ flags);
+ mwifiex_del_sta_entry(priv, peer);
+ }
+ mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN);
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action)
+{
+ switch (action) {
+ case MWIFIEX_TDLS_ENABLE_LINK:
+ return mwifiex_tdls_process_enable_link(priv, peer);
+ case MWIFIEX_TDLS_DISABLE_LINK:
+ return mwifiex_tdls_process_disable_link(priv, peer);
+ case MWIFIEX_TDLS_CREATE_LINK:
+ return mwifiex_tdls_process_create_link(priv, peer);
+ case MWIFIEX_TDLS_CONFIG_LINK:
+ return mwifiex_tdls_process_config_link(priv, peer);
+ }
+ return 0;
+}
+
+int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac)
+{
+ struct mwifiex_sta_node *sta_ptr;
+
+ sta_ptr = mwifiex_get_sta_entry(priv, mac);
+ if (sta_ptr)
+ return sta_ptr->tdls_status;
+
+ return TDLS_NOT_SETUP;
+}
+
+void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv)
+{
+ struct mwifiex_sta_node *sta_ptr;
+ struct mwifiex_ds_tdls_oper tdls_oper;
+ unsigned long flags;
+
+ if (list_empty(&priv->sta_list))
+ return;
+
+ list_for_each_entry(sta_ptr, &priv->sta_list, list) {
+ memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper));
+
+ if (sta_ptr->is_11n_enabled) {
+ mwifiex_11n_cleanup_reorder_tbl(priv);
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock,
+ flags);
+ mwifiex_11n_delete_all_tx_ba_stream_tbl(priv);
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+ flags);
+ }
+
+ mwifiex_restore_tdls_packets(priv, sta_ptr->mac_addr,
+ TDLS_LINK_TEARDOWN);
+ memcpy(&tdls_oper.peer_mac, sta_ptr->mac_addr, ETH_ALEN);
+ tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK;
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER,
+ HostCmd_ACT_GEN_SET, 0, &tdls_oper, false))
+ dev_warn(priv->adapter->dev,
+ "Disable link failed for TDLS peer %pM",
+ sta_ptr->mac_addr);
+ }
+
+ mwifiex_del_all_sta_list(priv);
+}
diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c
index 64424c81b44f..9be6544bdded 100644
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/mwifiex/uap_cmd.c
@@ -159,6 +159,7 @@ mwifiex_set_ht_params(struct mwifiex_private *priv,
struct cfg80211_ap_settings *params)
{
const u8 *ht_ie;
+ u16 cap_info;
if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info))
return;
@@ -168,6 +169,25 @@ mwifiex_set_ht_params(struct mwifiex_private *priv,
if (ht_ie) {
memcpy(&bss_cfg->ht_cap, ht_ie + 2,
sizeof(struct ieee80211_ht_cap));
+ cap_info = le16_to_cpu(bss_cfg->ht_cap.cap_info);
+ memset(&bss_cfg->ht_cap.mcs, 0,
+ priv->adapter->number_of_antenna);
+ switch (GET_RXSTBC(cap_info)) {
+ case MWIFIEX_RX_STBC1:
+ /* HT_CAP 1X1 mode */
+ memset(&bss_cfg->ht_cap.mcs, 0xff, 1);
+ break;
+ case MWIFIEX_RX_STBC12: /* fall through */
+ case MWIFIEX_RX_STBC123:
+ /* HT_CAP 2X2 mode */
+ memset(&bss_cfg->ht_cap.mcs, 0xff, 2);
+ break;
+ default:
+ dev_warn(priv->adapter->dev,
+ "Unsupported RX-STBC, default to 2x2\n");
+ memset(&bss_cfg->ht_cap.mcs, 0xff, 2);
+ break;
+ }
priv->ap_11n_enabled = 1;
} else {
memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap));
@@ -226,8 +246,8 @@ void mwifiex_set_vht_width(struct mwifiex_private *priv,
if (ap_11ac_enable && width >= NL80211_CHAN_WIDTH_80)
vht_cfg.misc_config |= VHT_BW_80_160_80P80;
- mwifiex_send_cmd_sync(priv, HostCmd_CMD_11AC_CFG,
- HostCmd_ACT_GEN_SET, 0, &vht_cfg);
+ mwifiex_send_cmd(priv, HostCmd_CMD_11AC_CFG,
+ HostCmd_ACT_GEN_SET, 0, &vht_cfg, true);
return;
}
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c
index 718066577c6c..92e77a398ecf 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/mwifiex/uap_event.c
@@ -21,126 +21,8 @@
#include "main.h"
#include "11n.h"
-/*
- * This function will return the pointer to station entry in station list
- * table which matches specified mac address.
- * This function should be called after acquiring RA list spinlock.
- * NULL is returned if station entry is not found in associated STA list.
- */
-struct mwifiex_sta_node *
-mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac)
-{
- struct mwifiex_sta_node *node;
-
- if (!mac)
- return NULL;
-
- list_for_each_entry(node, &priv->sta_list, list) {
- if (!memcmp(node->mac_addr, mac, ETH_ALEN))
- return node;
- }
-
- return NULL;
-}
-
-/*
- * This function will add a sta_node entry to associated station list
- * table with the given mac address.
- * If entry exist already, existing entry is returned.
- * If received mac address is NULL, NULL is returned.
- */
-static struct mwifiex_sta_node *
-mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac)
-{
- struct mwifiex_sta_node *node;
- unsigned long flags;
-
- if (!mac)
- return NULL;
-
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
- node = mwifiex_get_sta_entry(priv, mac);
- if (node)
- goto done;
-
- node = kzalloc(sizeof(struct mwifiex_sta_node), GFP_ATOMIC);
- if (!node)
- goto done;
-
- memcpy(node->mac_addr, mac, ETH_ALEN);
- list_add_tail(&node->list, &priv->sta_list);
-
-done:
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
- return node;
-}
-
-/*
- * This function will search for HT IE in association request IEs
- * and set station HT parameters accordingly.
- */
-static void
-mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
- int ies_len, struct mwifiex_sta_node *node)
-{
- const struct ieee80211_ht_cap *ht_cap;
-
- if (!ies)
- return;
- ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len);
- if (ht_cap) {
- node->is_11n_enabled = 1;
- node->max_amsdu = le16_to_cpu(ht_cap->cap_info) &
- IEEE80211_HT_CAP_MAX_AMSDU ?
- MWIFIEX_TX_DATA_BUF_SIZE_8K :
- MWIFIEX_TX_DATA_BUF_SIZE_4K;
- } else {
- node->is_11n_enabled = 0;
- }
- return;
-}
-
-/*
- * This function will delete a station entry from station list
- */
-static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
-{
- struct mwifiex_sta_node *node;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
-
- node = mwifiex_get_sta_entry(priv, mac);
- if (node) {
- list_del(&node->list);
- kfree(node);
- }
-
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
- return;
-}
-
-/*
- * This function will delete all stations from associated station list.
- */
-static void mwifiex_del_all_sta_list(struct mwifiex_private *priv)
-{
- struct mwifiex_sta_node *node, *tmp;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
-
- list_for_each_entry_safe(node, tmp, &priv->sta_list, list) {
- list_del(&node->list);
- kfree(node);
- }
-
- INIT_LIST_HEAD(&priv->sta_list);
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
- return;
-}
/*
* This function handles AP interface specific events generated by firmware.
@@ -268,9 +150,9 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
case EVENT_ADDBA:
dev_dbg(adapter->dev, "event: ADDBA Request\n");
if (priv->media_connected)
- mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_RSP,
- HostCmd_ACT_GEN_SET, 0,
- adapter->event_body);
+ mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP,
+ HostCmd_ACT_GEN_SET, 0,
+ adapter->event_body, false);
break;
case EVENT_DELBA:
dev_dbg(adapter->dev, "event: DELBA Request\n");
@@ -284,6 +166,12 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
mwifiex_11n_ba_stream_timeout(priv, ba_timeout);
}
break;
+ case EVENT_EXT_SCAN_REPORT:
+ dev_dbg(adapter->dev, "event: EXT_SCAN Report\n");
+ if (adapter->ext_scan)
+ return mwifiex_handle_event_ext_scan_report(priv,
+ adapter->event_skb->data);
+ break;
default:
dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
eventcause);
diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c
index 3c74eb254927..9a56bc61cb1d 100644
--- a/drivers/net/wireless/mwifiex/uap_txrx.c
+++ b/drivers/net/wireless/mwifiex/uap_txrx.c
@@ -284,27 +284,7 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
return 0;
}
- if (le16_to_cpu(uap_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) {
- struct sk_buff_head list;
- struct sk_buff *rx_skb;
-
- __skb_queue_head_init(&list);
- skb_pull(skb, le16_to_cpu(uap_rx_pd->rx_pkt_offset));
- skb_trim(skb, le16_to_cpu(uap_rx_pd->rx_pkt_length));
-
- ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
- priv->wdev->iftype, 0, false);
-
- while (!skb_queue_empty(&list)) {
- rx_skb = __skb_dequeue(&list);
- ret = mwifiex_recv_packet(priv, rx_skb);
- if (ret)
- dev_err(adapter->dev,
- "AP:Rx A-MSDU failed");
- }
-
- return 0;
- } else if (rx_pkt_type == PKT_TYPE_MGMT) {
+ if (rx_pkt_type == PKT_TYPE_MGMT) {
ret = mwifiex_process_mgmt_packet(priv, skb);
if (ret)
dev_err(adapter->dev, "Rx of mgmt packet failed");
diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c
index 208748804a55..edbe4aff00d8 100644
--- a/drivers/net/wireless/mwifiex/usb.c
+++ b/drivers/net/wireless/mwifiex/usb.c
@@ -459,6 +459,7 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message)
* 'suspended' state and a 'disconnect' one.
*/
adapter->is_suspended = true;
+ adapter->hs_enabling = false;
if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb)
usb_kill_urb(card->rx_cmd.urb);
@@ -766,11 +767,13 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
switch (le16_to_cpu(card->udev->descriptor.idProduct)) {
case USB8897_PID_1:
case USB8897_PID_2:
+ adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K;
strcpy(adapter->fw_name, USB8897_DEFAULT_FW_NAME);
break;
case USB8797_PID_1:
case USB8797_PID_2:
default:
+ adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME);
break;
}
@@ -1024,7 +1027,6 @@ static void mwifiex_usb_cleanup_module(void)
if (usb_card && usb_card->adapter) {
struct mwifiex_adapter *adapter = usb_card->adapter;
- int i;
/* In case driver is removed when asynchronous FW downloading is
* in progress
@@ -1035,11 +1037,8 @@ static void mwifiex_usb_cleanup_module(void)
if (adapter->is_suspended)
mwifiex_usb_resume(usb_card->intf);
#endif
- for (i = 0; i < adapter->priv_num; i++)
- if ((GET_BSS_ROLE(adapter->priv[i]) ==
- MWIFIEX_BSS_ROLE_STA) &&
- adapter->priv[i]->media_connected)
- mwifiex_deauthenticate(adapter->priv[i], NULL);
+
+ mwifiex_deauthenticate_all(adapter);
mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter,
MWIFIEX_BSS_ROLE_ANY),
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index 9b82e225880c..c3824e37f3f2 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/mwifiex/util.c
@@ -72,7 +72,7 @@ int mwifiex_init_shutdown_fw(struct mwifiex_private *priv,
return -1;
}
- return mwifiex_send_cmd_sync(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL);
+ return mwifiex_send_cmd(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL, true);
}
EXPORT_SYMBOL_GPL(mwifiex_init_shutdown_fw);
@@ -104,6 +104,7 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv,
info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try;
info->is_hs_configured = adapter->is_hs_configured;
info->hs_activated = adapter->hs_activated;
+ info->is_cmd_timedout = adapter->is_cmd_timedout;
info->num_cmd_host_to_card_failure
= adapter->dbg.num_cmd_host_to_card_failure;
info->num_cmd_sleep_cfm_host_to_card_failure
@@ -119,7 +120,6 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv,
info->num_cmd_assoc_failure =
adapter->dbg.num_cmd_assoc_failure;
info->num_tx_timeout = adapter->dbg.num_tx_timeout;
- info->num_cmd_timeout = adapter->dbg.num_cmd_timeout;
info->timeout_cmd_id = adapter->dbg.timeout_cmd_id;
info->timeout_cmd_act = adapter->dbg.timeout_cmd_act;
memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id,
@@ -252,3 +252,117 @@ int mwifiex_complete_cmd(struct mwifiex_adapter *adapter,
return 0;
}
+
+/* This function will return the pointer to station entry in station list
+ * table which matches specified mac address.
+ * This function should be called after acquiring RA list spinlock.
+ * NULL is returned if station entry is not found in associated STA list.
+ */
+struct mwifiex_sta_node *
+mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac)
+{
+ struct mwifiex_sta_node *node;
+
+ if (!mac)
+ return NULL;
+
+ list_for_each_entry(node, &priv->sta_list, list) {
+ if (!memcmp(node->mac_addr, mac, ETH_ALEN))
+ return node;
+ }
+
+ return NULL;
+}
+
+/* This function will add a sta_node entry to associated station list
+ * table with the given mac address.
+ * If entry exist already, existing entry is returned.
+ * If received mac address is NULL, NULL is returned.
+ */
+struct mwifiex_sta_node *
+mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac)
+{
+ struct mwifiex_sta_node *node;
+ unsigned long flags;
+
+ if (!mac)
+ return NULL;
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ node = mwifiex_get_sta_entry(priv, mac);
+ if (node)
+ goto done;
+
+ node = kzalloc(sizeof(*node), GFP_ATOMIC);
+ if (!node)
+ goto done;
+
+ memcpy(node->mac_addr, mac, ETH_ALEN);
+ list_add_tail(&node->list, &priv->sta_list);
+
+done:
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+ return node;
+}
+
+/* This function will search for HT IE in association request IEs
+ * and set station HT parameters accordingly.
+ */
+void
+mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
+ int ies_len, struct mwifiex_sta_node *node)
+{
+ const struct ieee80211_ht_cap *ht_cap;
+
+ if (!ies)
+ return;
+
+ ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len);
+ if (ht_cap) {
+ node->is_11n_enabled = 1;
+ node->max_amsdu = le16_to_cpu(ht_cap->cap_info) &
+ IEEE80211_HT_CAP_MAX_AMSDU ?
+ MWIFIEX_TX_DATA_BUF_SIZE_8K :
+ MWIFIEX_TX_DATA_BUF_SIZE_4K;
+ } else {
+ node->is_11n_enabled = 0;
+ }
+
+ return;
+}
+
+/* This function will delete a station entry from station list */
+void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
+{
+ struct mwifiex_sta_node *node;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+
+ node = mwifiex_get_sta_entry(priv, mac);
+ if (node) {
+ list_del(&node->list);
+ kfree(node);
+ }
+
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+ return;
+}
+
+/* This function will delete all stations from associated station list. */
+void mwifiex_del_all_sta_list(struct mwifiex_private *priv)
+{
+ struct mwifiex_sta_node *node, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+
+ list_for_each_entry_safe(node, tmp, &priv->sta_list, list) {
+ list_del(&node->list);
+ kfree(node);
+ }
+
+ INIT_LIST_HEAD(&priv->sta_list);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+ return;
+}
diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h
index cb2d0582bd36..ddae57021397 100644
--- a/drivers/net/wireless/mwifiex/util.h
+++ b/drivers/net/wireless/mwifiex/util.h
@@ -30,8 +30,24 @@ static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb)
return (struct mwifiex_txinfo *)(skb->cb + sizeof(dma_addr_t));
}
-static inline void MWIFIEX_SKB_PACB(struct sk_buff *skb, dma_addr_t *buf_pa)
+struct mwifiex_dma_mapping {
+ dma_addr_t addr;
+ size_t len;
+};
+
+static inline void MWIFIEX_SKB_PACB(struct sk_buff *skb,
+ struct mwifiex_dma_mapping *mapping)
{
- memcpy(buf_pa, skb->cb, sizeof(dma_addr_t));
+ memcpy(mapping, skb->cb, sizeof(*mapping));
}
+
+static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb)
+{
+ struct mwifiex_dma_mapping mapping;
+
+ MWIFIEX_SKB_PACB(skb, &mapping);
+
+ return mapping.addr;
+}
+
#endif /* !_MWIFIEX_UTIL_H_ */
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c
index 981cf6e7c73b..0a7cc742aed7 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/mwifiex/wmm.c
@@ -37,8 +37,8 @@
/* Offset for TOS field in the IP header */
#define IPTOS_OFFSET 5
-static bool enable_tx_amsdu;
-module_param(enable_tx_amsdu, bool, 0644);
+static bool disable_tx_amsdu;
+module_param(disable_tx_amsdu, bool, 0644);
/* WMM information IE */
static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07,
@@ -64,21 +64,6 @@ static u8 tos_to_tid[] = {
0x07 /* 1 1 1 AC_VO */
};
-/*
- * This table inverses the tos_to_tid operation to get a priority
- * which is in sequential order, and can be compared.
- * Use this to compare the priority of two different TIDs.
- */
-static u8 tos_to_tid_inv[] = {
- 0x02, /* from tos_to_tid[2] = 0 */
- 0x00, /* from tos_to_tid[0] = 1 */
- 0x01, /* from tos_to_tid[1] = 2 */
- 0x03,
- 0x04,
- 0x05,
- 0x06,
- 0x07};
-
static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} };
/*
@@ -175,8 +160,15 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra)
break;
ra_list->is_11n_enabled = 0;
+ ra_list->tdls_link = false;
if (!mwifiex_queuing_ra_based(priv)) {
- ra_list->is_11n_enabled = IS_11N_ENABLED(priv);
+ if (mwifiex_get_tdls_link_status(priv, ra) ==
+ TDLS_SETUP_COMPLETE) {
+ ra_list->is_11n_enabled =
+ mwifiex_tdls_peer_11n_enabled(priv, ra);
+ } else {
+ ra_list->is_11n_enabled = IS_11N_ENABLED(priv);
+ }
} else {
ra_list->is_11n_enabled =
mwifiex_is_sta_11n_enabled(priv, node);
@@ -213,8 +205,9 @@ static void mwifiex_wmm_default_queue_priorities(struct mwifiex_private *priv)
* This function map ACs to TIDs.
*/
static void
-mwifiex_wmm_queue_priorities_tid(struct mwifiex_wmm_desc *wmm)
+mwifiex_wmm_queue_priorities_tid(struct mwifiex_private *priv)
{
+ struct mwifiex_wmm_desc *wmm = &priv->wmm;
u8 *queue_priority = wmm->queue_priority;
int i;
@@ -224,7 +217,7 @@ mwifiex_wmm_queue_priorities_tid(struct mwifiex_wmm_desc *wmm)
}
for (i = 0; i < MAX_NUM_TID; ++i)
- tos_to_tid_inv[tos_to_tid[i]] = (u8)i;
+ priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i;
atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID);
}
@@ -285,7 +278,7 @@ mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv,
}
}
- mwifiex_wmm_queue_priorities_tid(&priv->wmm);
+ mwifiex_wmm_queue_priorities_tid(priv);
}
/*
@@ -388,8 +381,7 @@ mwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos)
* AP is disabled (due to call admission control (ACM bit). Mapping
* of TID to AC is taken care of internally.
*/
-static u8
-mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid)
+u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid)
{
enum mwifiex_wmm_ac_e ac, ac_down;
u8 new_tid;
@@ -421,9 +413,17 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter)
continue;
for (i = 0; i < MAX_NUM_TID; ++i) {
- priv->aggr_prio_tbl[i].amsdu = tos_to_tid_inv[i];
- priv->aggr_prio_tbl[i].ampdu_ap = tos_to_tid_inv[i];
- priv->aggr_prio_tbl[i].ampdu_user = tos_to_tid_inv[i];
+ if (!disable_tx_amsdu &&
+ adapter->tx_buf_size > MWIFIEX_TX_DATA_BUF_SIZE_2K)
+ priv->aggr_prio_tbl[i].amsdu =
+ priv->tos_to_tid_inv[i];
+ else
+ priv->aggr_prio_tbl[i].amsdu =
+ BA_STREAM_NOT_ALLOWED;
+ priv->aggr_prio_tbl[i].ampdu_ap =
+ priv->tos_to_tid_inv[i];
+ priv->aggr_prio_tbl[i].ampdu_user =
+ priv->tos_to_tid_inv[i];
}
priv->aggr_prio_tbl[6].amsdu
@@ -546,6 +546,7 @@ void
mwifiex_clean_txrx(struct mwifiex_private *priv)
{
unsigned long flags;
+ struct sk_buff *skb, *tmp;
mwifiex_11n_cleanup_reorder_tbl(priv);
spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
@@ -563,6 +564,9 @@ mwifiex_clean_txrx(struct mwifiex_private *priv)
!priv->adapter->surprise_removed)
priv->adapter->if_ops.clean_pcie_ring(priv->adapter);
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+
+ skb_queue_walk_safe(&priv->tdls_txq, skb, tmp)
+ mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
}
/*
@@ -591,7 +595,7 @@ mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid,
* If no such node is found, a new node is added first and then
* retrieved.
*/
-static struct mwifiex_ra_list_tbl *
+struct mwifiex_ra_list_tbl *
mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr)
{
struct mwifiex_ra_list_tbl *ra_list;
@@ -641,6 +645,21 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
struct mwifiex_ra_list_tbl *ra_list;
u8 ra[ETH_ALEN], tid_down;
unsigned long flags;
+ struct list_head list_head;
+ int tdls_status = TDLS_NOT_SETUP;
+ struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
+ struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
+
+ memcpy(ra, eth_hdr->h_dest, ETH_ALEN);
+
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
+ ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) {
+ if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS)
+ dev_dbg(adapter->dev,
+ "TDLS setup packet for %pM. Don't block\n", ra);
+ else
+ tdls_status = mwifiex_get_tdls_link_status(priv, ra);
+ }
if (!priv->media_connected && !mwifiex_is_skb_mgmt_frame(skb)) {
dev_dbg(adapter->dev, "data: drop packet in disconnect\n");
@@ -659,12 +678,27 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
have only 1 raptr for a tid in case of infra */
if (!mwifiex_queuing_ra_based(priv) &&
!mwifiex_is_skb_mgmt_frame(skb)) {
- if (!list_empty(&priv->wmm.tid_tbl_ptr[tid_down].ra_list))
- ra_list = list_first_entry(
- &priv->wmm.tid_tbl_ptr[tid_down].ra_list,
- struct mwifiex_ra_list_tbl, list);
- else
- ra_list = NULL;
+ switch (tdls_status) {
+ case TDLS_SETUP_COMPLETE:
+ ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down,
+ ra);
+ tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
+ break;
+ case TDLS_SETUP_INPROGRESS:
+ skb_queue_tail(&priv->tdls_txq, skb);
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+ flags);
+ return;
+ default:
+ list_head = priv->wmm.tid_tbl_ptr[tid_down].ra_list;
+ if (!list_empty(&list_head))
+ ra_list = list_first_entry(
+ &list_head, struct mwifiex_ra_list_tbl,
+ list);
+ else
+ ra_list = NULL;
+ break;
+ }
} else {
memcpy(ra, skb->data, ETH_ALEN);
if (ra[0] & 0x01 || mwifiex_is_skb_mgmt_frame(skb))
@@ -684,9 +718,9 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
ra_list->total_pkt_count++;
if (atomic_read(&priv->wmm.highest_queued_prio) <
- tos_to_tid_inv[tid_down])
+ priv->tos_to_tid_inv[tid_down])
atomic_set(&priv->wmm.highest_queued_prio,
- tos_to_tid_inv[tid_down]);
+ priv->tos_to_tid_inv[tid_down]);
atomic_inc(&priv->wmm.tx_pkts_queued);
@@ -1219,15 +1253,24 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
if (!ptr->is_11n_enabled ||
mwifiex_is_ba_stream_setup(priv, ptr, tid) ||
- priv->wps.session_enable ||
- ((priv->sec_info.wpa_enabled ||
- priv->sec_info.wpa2_enabled) &&
- !priv->wpa_is_gtk_set)) {
- mwifiex_send_single_packet(priv, ptr, ptr_index, flags);
- /* ra_list_spinlock has been freed in
- mwifiex_send_single_packet() */
+ priv->wps.session_enable) {
+ if (ptr->is_11n_enabled &&
+ mwifiex_is_ba_stream_setup(priv, ptr, tid) &&
+ mwifiex_is_amsdu_in_ampdu_allowed(priv, ptr, tid) &&
+ mwifiex_is_amsdu_allowed(priv, tid) &&
+ mwifiex_is_11n_aggragation_possible(priv, ptr,
+ adapter->tx_buf_size))
+ mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags);
+ /* ra_list_spinlock has been freed in
+ * mwifiex_11n_aggregate_pkt()
+ */
+ else
+ mwifiex_send_single_packet(priv, ptr, ptr_index, flags);
+ /* ra_list_spinlock has been freed in
+ * mwifiex_send_single_packet()
+ */
} else {
- if (mwifiex_is_ampdu_allowed(priv, tid) &&
+ if (mwifiex_is_ampdu_allowed(priv, ptr, tid) &&
ptr->ba_pkt_count > ptr->ba_packet_thr) {
if (mwifiex_space_avail_for_new_ba_stream(adapter)) {
mwifiex_create_ba_tbl(priv, ptr->ra, tid,
@@ -1240,7 +1283,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
mwifiex_send_delba(priv, tid_del, ra, 1);
}
}
- if (enable_tx_amsdu && mwifiex_is_amsdu_allowed(priv, tid) &&
+ if (mwifiex_is_amsdu_allowed(priv, tid) &&
mwifiex_is_11n_aggragation_possible(priv, ptr,
adapter->tx_buf_size))
mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags);
diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h
index 0f129d498fb1..83e42083ebff 100644
--- a/drivers/net/wireless/mwifiex/wmm.h
+++ b/drivers/net/wireless/mwifiex/wmm.h
@@ -34,6 +34,21 @@ enum ieee_types_wmm_ecw_bitmasks {
static const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
/*
+ * This table inverses the tos_to_tid operation to get a priority
+ * which is in sequential order, and can be compared.
+ * Use this to compare the priority of two different TIDs.
+ */
+static const u8 tos_to_tid_inv[] = {
+ 0x02, /* from tos_to_tid[2] = 0 */
+ 0x00, /* from tos_to_tid[0] = 1 */
+ 0x01, /* from tos_to_tid[1] = 2 */
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07};
+
+/*
* This function retrieves the TID of the given RA list.
*/
static inline int
@@ -107,5 +122,8 @@ void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv,
void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv);
int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
const struct host_cmd_ds_command *resp);
+struct mwifiex_ra_list_tbl *
+mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr);
+u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid);
#endif /* !_MWIFIEX_WMM_H_ */
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 4987c3f942ce..3c0a0a86ba12 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -81,6 +81,9 @@ MODULE_PARM_DESC(ap_mode_default,
*/
#define MWL8K_HW_TIMER_REGISTER 0x0000a600
+#define BBU_RXRDY_CNT_REG 0x0000a860
+#define NOK_CCA_CNT_REG 0x0000a6a0
+#define BBU_AVG_NOISE_VAL 0x67
#define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \
MWL8K_A2H_INT_CHNL_SWITCHED | \
@@ -112,6 +115,8 @@ MODULE_PARM_DESC(ap_mode_default,
*/
#define MWL8K_NUM_AMPDU_STREAMS (TOTAL_HW_TX_QUEUES - 1)
+#define MWL8K_NUM_CHANS 18
+
struct rxd_ops {
int rxd_size;
void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr);
@@ -289,6 +294,12 @@ struct mwl8k_priv {
/* bitmap of running BSSes */
u32 running_bsses;
+
+ /* ACS related */
+ bool sw_scan_start;
+ struct ieee80211_channel *acs_chan;
+ unsigned long channel_time;
+ struct survey_info survey[MWL8K_NUM_CHANS];
};
#define MAX_WEP_KEY_LEN 13
@@ -396,6 +407,7 @@ static const struct ieee80211_rate mwl8k_rates_50[] = {
#define MWL8K_CMD_SET_HW_SPEC 0x0004
#define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010
#define MWL8K_CMD_GET_STAT 0x0014
+#define MWL8K_CMD_BBP_REG_ACCESS 0x001a
#define MWL8K_CMD_RADIO_CONTROL 0x001c
#define MWL8K_CMD_RF_TX_POWER 0x001e
#define MWL8K_CMD_TX_POWER 0x001f
@@ -2987,6 +2999,47 @@ static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw)
}
/*
+ * CMD_BBP_REG_ACCESS.
+ */
+struct mwl8k_cmd_bbp_reg_access {
+ struct mwl8k_cmd_pkt header;
+ __le16 action;
+ __le16 offset;
+ u8 value;
+ u8 rsrv[3];
+} __packed;
+
+static int
+mwl8k_cmd_bbp_reg_access(struct ieee80211_hw *hw,
+ u16 action,
+ u16 offset,
+ u8 *value)
+{
+ struct mwl8k_cmd_bbp_reg_access *cmd;
+ int rc;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+
+ cmd->header.code = cpu_to_le16(MWL8K_CMD_BBP_REG_ACCESS);
+ cmd->header.length = cpu_to_le16(sizeof(*cmd));
+ cmd->action = cpu_to_le16(action);
+ cmd->offset = cpu_to_le16(offset);
+
+ rc = mwl8k_post_cmd(hw, &cmd->header);
+
+ if (!rc)
+ *value = cmd->value;
+ else
+ *value = 0;
+
+ kfree(cmd);
+
+ return rc;
+}
+
+/*
* CMD_SET_POST_SCAN.
*/
struct mwl8k_cmd_set_post_scan {
@@ -3016,6 +3069,64 @@ mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac)
return rc;
}
+static int freq_to_idx(struct mwl8k_priv *priv, int freq)
+{
+ struct ieee80211_supported_band *sband;
+ int band, ch, idx = 0;
+
+ for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
+ sband = priv->hw->wiphy->bands[band];
+ if (!sband)
+ continue;
+
+ for (ch = 0; ch < sband->n_channels; ch++, idx++)
+ if (sband->channels[ch].center_freq == freq)
+ goto exit;
+ }
+
+exit:
+ return idx;
+}
+
+static void mwl8k_update_survey(struct mwl8k_priv *priv,
+ struct ieee80211_channel *channel)
+{
+ u32 cca_cnt, rx_rdy;
+ s8 nf = 0, idx;
+ struct survey_info *survey;
+
+ idx = freq_to_idx(priv, priv->acs_chan->center_freq);
+ if (idx >= MWL8K_NUM_CHANS) {
+ wiphy_err(priv->hw->wiphy, "Failed to update survey\n");
+ return;
+ }
+
+ survey = &priv->survey[idx];
+
+ cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG);
+ cca_cnt /= 1000; /* uSecs to mSecs */
+ survey->channel_time_busy = (u64) cca_cnt;
+
+ rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG);
+ rx_rdy /= 1000; /* uSecs to mSecs */
+ survey->channel_time_rx = (u64) rx_rdy;
+
+ priv->channel_time = jiffies - priv->channel_time;
+ survey->channel_time = jiffies_to_msecs(priv->channel_time);
+
+ survey->channel = channel;
+
+ mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &nf);
+
+ /* Make sure sign is negative else ACS at hostapd fails */
+ survey->noise = nf * -1;
+
+ survey->filled = SURVEY_INFO_NOISE_DBM |
+ SURVEY_INFO_CHANNEL_TIME |
+ SURVEY_INFO_CHANNEL_TIME_BUSY |
+ SURVEY_INFO_CHANNEL_TIME_RX;
+}
+
/*
* CMD_SET_RF_CHANNEL.
*/
@@ -3033,6 +3144,7 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
enum nl80211_channel_type channel_type =
cfg80211_get_chandef_type(&conf->chandef);
struct mwl8k_cmd_set_rf_channel *cmd;
+ struct mwl8k_priv *priv = hw->priv;
int rc;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -3049,13 +3161,29 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
else if (channel->band == IEEE80211_BAND_5GHZ)
cmd->channel_flags |= cpu_to_le32(0x00000004);
- if (channel_type == NL80211_CHAN_NO_HT ||
- channel_type == NL80211_CHAN_HT20)
+ if (!priv->sw_scan_start) {
+ if (channel_type == NL80211_CHAN_NO_HT ||
+ channel_type == NL80211_CHAN_HT20)
+ cmd->channel_flags |= cpu_to_le32(0x00000080);
+ else if (channel_type == NL80211_CHAN_HT40MINUS)
+ cmd->channel_flags |= cpu_to_le32(0x000001900);
+ else if (channel_type == NL80211_CHAN_HT40PLUS)
+ cmd->channel_flags |= cpu_to_le32(0x000000900);
+ } else {
cmd->channel_flags |= cpu_to_le32(0x00000080);
- else if (channel_type == NL80211_CHAN_HT40MINUS)
- cmd->channel_flags |= cpu_to_le32(0x000001900);
- else if (channel_type == NL80211_CHAN_HT40PLUS)
- cmd->channel_flags |= cpu_to_le32(0x000000900);
+ }
+
+ if (priv->sw_scan_start) {
+ /* Store current channel stats
+ * before switching to newer one.
+ * This will be processed only for AP fw.
+ */
+ if (priv->channel_time != 0)
+ mwl8k_update_survey(priv, priv->acs_chan);
+
+ priv->channel_time = jiffies;
+ priv->acs_chan = channel;
+ }
rc = mwl8k_post_cmd(hw, &cmd->header);
kfree(cmd);
@@ -5263,6 +5391,27 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx,
{
struct mwl8k_priv *priv = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
+ struct ieee80211_supported_band *sband;
+
+ if (priv->ap_fw) {
+ sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ];
+
+ if (sband && idx >= sband->n_channels) {
+ idx -= sband->n_channels;
+ sband = NULL;
+ }
+
+ if (!sband)
+ sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ];
+
+ if (!sband || idx >= sband->n_channels)
+ return -ENOENT;
+
+ memcpy(survey, &priv->survey[idx], sizeof(*survey));
+ survey->channel = &sband->channels[idx];
+
+ return 0;
+ }
if (idx != 0)
return -ENOENT;
@@ -5406,6 +5555,40 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return rc;
}
+static void mwl8k_sw_scan_start(struct ieee80211_hw *hw)
+{
+ struct mwl8k_priv *priv = hw->priv;
+ u8 tmp;
+
+ if (!priv->ap_fw)
+ return;
+
+ /* clear all stats */
+ priv->channel_time = 0;
+ ioread32(priv->regs + BBU_RXRDY_CNT_REG);
+ ioread32(priv->regs + NOK_CCA_CNT_REG);
+ mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp);
+
+ priv->sw_scan_start = true;
+}
+
+static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw)
+{
+ struct mwl8k_priv *priv = hw->priv;
+ u8 tmp;
+
+ if (!priv->ap_fw)
+ return;
+
+ priv->sw_scan_start = false;
+
+ /* clear all stats */
+ priv->channel_time = 0;
+ ioread32(priv->regs + BBU_RXRDY_CNT_REG);
+ ioread32(priv->regs + NOK_CCA_CNT_REG);
+ mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp);
+}
+
static const struct ieee80211_ops mwl8k_ops = {
.tx = mwl8k_tx,
.start = mwl8k_start,
@@ -5424,6 +5607,8 @@ static const struct ieee80211_ops mwl8k_ops = {
.get_stats = mwl8k_get_stats,
.get_survey = mwl8k_get_survey,
.ampdu_action = mwl8k_ampdu_action,
+ .sw_scan_start = mwl8k_sw_scan_start,
+ .sw_scan_complete = mwl8k_sw_scan_complete,
};
static void mwl8k_finalize_join_worker(struct work_struct *work)
diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c
index d01edd2c50c5..a9e94b6db5b7 100644
--- a/drivers/net/wireless/orinoco/cfg.c
+++ b/drivers/net/wireless/orinoco/cfg.c
@@ -59,7 +59,8 @@ int orinoco_wiphy_register(struct wiphy *wiphy)
for (i = 0; i < NUM_CHANNELS; i++) {
if (priv->channel_mask & (1 << i)) {
priv->channels[i].center_freq =
- ieee80211_dsss_chan_to_freq(i + 1);
+ ieee80211_channel_to_frequency(i + 1,
+ IEEE80211_BAND_2GHZ);
channels++;
}
}
@@ -177,7 +178,7 @@ static int orinoco_set_monitor_channel(struct wiphy *wiphy,
if (chandef->chan->band != IEEE80211_BAND_2GHZ)
return -EINVAL;
- channel = ieee80211_freq_to_dsss_chan(chandef->chan->center_freq);
+ channel = ieee80211_frequency_to_channel(chandef->chan->center_freq);
if ((channel < 1) || (channel > NUM_CHANNELS) ||
!(priv->channel_mask & (1 << (channel - 1))))
diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c
index c09c8437c0b8..49300d04efdf 100644
--- a/drivers/net/wireless/orinoco/hw.c
+++ b/drivers/net/wireless/orinoco/hw.c
@@ -1193,7 +1193,7 @@ int orinoco_hw_get_freq(struct orinoco_private *priv)
goto out;
}
- freq = ieee80211_dsss_chan_to_freq(channel);
+ freq = ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ);
out:
orinoco_unlock(priv, &flags);
diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/orinoco/scan.c
index e8c5714bfd11..e175b9b8561b 100644
--- a/drivers/net/wireless/orinoco/scan.c
+++ b/drivers/net/wireless/orinoco/scan.c
@@ -110,7 +110,8 @@ static void orinoco_add_hostscan_result(struct orinoco_private *priv,
break;
}
- freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel));
+ freq = ieee80211_channel_to_frequency(
+ le16_to_cpu(bss->a.channel), IEEE80211_BAND_2GHZ);
channel = ieee80211_get_channel(wiphy, freq);
if (!channel) {
printk(KERN_DEBUG "Invalid channel designation %04X(%04X)",
@@ -146,7 +147,7 @@ void orinoco_add_extscan_result(struct orinoco_private *priv,
ie_len = len - sizeof(*bss);
ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len);
chan = ie ? ie[2] : 0;
- freq = ieee80211_dsss_chan_to_freq(chan);
+ freq = ieee80211_channel_to_frequency(chan, IEEE80211_BAND_2GHZ);
channel = ieee80211_get_channel(wiphy, freq);
timestamp = le64_to_cpu(bss->timestamp);
diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c
index 3b5508f982e8..b7a867b50b94 100644
--- a/drivers/net/wireless/orinoco/wext.c
+++ b/drivers/net/wireless/orinoco/wext.c
@@ -444,7 +444,7 @@ static int orinoco_ioctl_setfreq(struct net_device *dev,
for (i = 0; i < (6 - frq->e); i++)
denom *= 10;
- chan = ieee80211_freq_to_dsss_chan(frq->m / denom);
+ chan = ieee80211_frequency_to_channel(frq->m / denom);
}
if ((chan < 1) || (chan > NUM_CHANNELS) ||
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
index 6e635cfa24c8..043bd1c23c19 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/p54/p54usb.c
@@ -513,7 +513,7 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
if (!buf)
return -ENOMEM;
- left = block_size = min((size_t)P54U_FW_BLOCK, priv->fw->size);
+ left = block_size = min_t(size_t, P54U_FW_BLOCK, priv->fw->size);
strcpy(buf, p54u_firmware_upload_3887);
left -= strlen(p54u_firmware_upload_3887);
tmp += strlen(p54u_firmware_upload_3887);
@@ -1053,6 +1053,10 @@ static int p54u_probe(struct usb_interface *intf,
priv->upload_fw = p54u_upload_firmware_net2280;
}
err = p54u_load_firmware(dev, intf);
+ if (err) {
+ usb_put_dev(udev);
+ p54_free_common(dev);
+ }
return err;
}
diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c
index 78fa64d3f223..ecbb0546cf3e 100644
--- a/drivers/net/wireless/prism54/isl_ioctl.c
+++ b/drivers/net/wireless/prism54/isl_ioctl.c
@@ -644,7 +644,7 @@ prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info,
wpa_ie_len = prism54_wpa_bss_ie_get(priv, bss->address, wpa_ie);
if (wpa_ie_len > 0) {
iwe.cmd = IWEVGENIE;
- iwe.u.data.length = min(wpa_ie_len, (size_t)MAX_WPA_IE_LEN);
+ iwe.u.data.length = min_t(size_t, wpa_ie_len, MAX_WPA_IE_LEN);
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, wpa_ie);
}
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 5028557aa18a..39d22a154341 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -1290,7 +1290,8 @@ static int set_channel(struct usbnet *usbdev, int channel)
if (is_associated(usbdev))
return 0;
- dsconfig = ieee80211_dsss_chan_to_freq(channel) * 1000;
+ dsconfig = 1000 *
+ ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ);
len = sizeof(config);
ret = rndis_query_oid(usbdev,
@@ -2835,7 +2836,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
bssid, req_ie, req_ie_len,
resp_ie, resp_ie_len, GFP_KERNEL);
} else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC)
- cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL);
+ cfg80211_ibss_joined(usbdev->net, bssid,
+ get_current_channel(usbdev, NULL),
+ GFP_KERNEL);
kfree(info);
diff --git a/drivers/net/wireless/rsi/Kconfig b/drivers/net/wireless/rsi/Kconfig
new file mode 100644
index 000000000000..35245f994c10
--- /dev/null
+++ b/drivers/net/wireless/rsi/Kconfig
@@ -0,0 +1,30 @@
+config RSI_91X
+ tristate "Redpine Signals Inc 91x WLAN driver support"
+ depends on MAC80211
+ ---help---
+ This option enabes support for RSI 1x1 devices.
+ Select M (recommended), if you have a RSI 1x1 wireless module.
+
+config RSI_DEBUGFS
+ bool "Redpine Signals Inc debug support"
+ depends on RSI_91X
+ default y
+ ---help---
+ Say Y, if you would like to enable debug support. This option
+ creates debugfs entries
+
+config RSI_SDIO
+ tristate "Redpine Signals SDIO bus support"
+ depends on MMC && RSI_91X
+ default m
+ ---help---
+ This option enables the SDIO bus support in rsi drivers.
+ Select M (recommended), if you have a RSI 1x1 wireless module.
+
+config RSI_USB
+ tristate "Redpine Signals USB bus support"
+ depends on USB && RSI_91X
+ default m
+ ---help---
+ This option enables the USB bus support in rsi drivers.
+ Select M (recommended), if you have a RSI 1x1 wireless module.
diff --git a/drivers/net/wireless/rsi/Makefile b/drivers/net/wireless/rsi/Makefile
new file mode 100644
index 000000000000..25828b692756
--- /dev/null
+++ b/drivers/net/wireless/rsi/Makefile
@@ -0,0 +1,12 @@
+rsi_91x-y += rsi_91x_main.o
+rsi_91x-y += rsi_91x_core.o
+rsi_91x-y += rsi_91x_mac80211.o
+rsi_91x-y += rsi_91x_mgmt.o
+rsi_91x-y += rsi_91x_pkt.o
+rsi_91x-$(CONFIG_RSI_DEBUGFS) += rsi_91x_debugfs.o
+
+rsi_usb-y += rsi_91x_usb.o rsi_91x_usb_ops.o
+rsi_sdio-y += rsi_91x_sdio.o rsi_91x_sdio_ops.o
+obj-$(CONFIG_RSI_91X) += rsi_91x.o
+obj-$(CONFIG_RSI_SDIO) += rsi_sdio.o
+obj-$(CONFIG_RSI_USB) += rsi_usb.o
diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c
new file mode 100644
index 000000000000..e89535e86caf
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_core.c
@@ -0,0 +1,342 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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 "rsi_mgmt.h"
+#include "rsi_common.h"
+
+/**
+ * rsi_determine_min_weight_queue() - This function determines the queue with
+ * the min weight.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: q_num: Corresponding queue number.
+ */
+static u8 rsi_determine_min_weight_queue(struct rsi_common *common)
+{
+ struct wmm_qinfo *tx_qinfo = common->tx_qinfo;
+ u32 q_len = 0;
+ u8 ii = 0;
+
+ for (ii = 0; ii < NUM_EDCA_QUEUES; ii++) {
+ q_len = skb_queue_len(&common->tx_queue[ii]);
+ if ((tx_qinfo[ii].pkt_contended) && q_len) {
+ common->min_weight = tx_qinfo[ii].weight;
+ break;
+ }
+ }
+ return ii;
+}
+
+/**
+ * rsi_recalculate_weights() - This function recalculates the weights
+ * corresponding to each queue.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: recontend_queue bool variable
+ */
+static bool rsi_recalculate_weights(struct rsi_common *common)
+{
+ struct wmm_qinfo *tx_qinfo = common->tx_qinfo;
+ bool recontend_queue = false;
+ u8 ii = 0;
+ u32 q_len = 0;
+
+ for (ii = 0; ii < NUM_EDCA_QUEUES; ii++) {
+ q_len = skb_queue_len(&common->tx_queue[ii]);
+ /* Check for the need of contention */
+ if (q_len) {
+ if (tx_qinfo[ii].pkt_contended) {
+ tx_qinfo[ii].weight =
+ ((tx_qinfo[ii].weight > common->min_weight) ?
+ tx_qinfo[ii].weight - common->min_weight : 0);
+ } else {
+ tx_qinfo[ii].pkt_contended = 1;
+ tx_qinfo[ii].weight = tx_qinfo[ii].wme_params;
+ recontend_queue = true;
+ }
+ } else { /* No packets so no contention */
+ tx_qinfo[ii].weight = 0;
+ tx_qinfo[ii].pkt_contended = 0;
+ }
+ }
+
+ return recontend_queue;
+}
+
+/**
+ * rsi_core_determine_hal_queue() - This function determines the queue from
+ * which packet has to be dequeued.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: q_num: Corresponding queue number on success.
+ */
+static u8 rsi_core_determine_hal_queue(struct rsi_common *common)
+{
+ bool recontend_queue = false;
+ u32 q_len = 0;
+ u8 q_num = INVALID_QUEUE;
+ u8 ii, min = 0;
+
+ if (skb_queue_len(&common->tx_queue[MGMT_SOFT_Q])) {
+ if (!common->mgmt_q_block)
+ q_num = MGMT_SOFT_Q;
+ return q_num;
+ }
+
+ if (common->pkt_cnt != 0) {
+ --common->pkt_cnt;
+ return common->selected_qnum;
+ }
+
+get_queue_num:
+ q_num = 0;
+ recontend_queue = false;
+
+ q_num = rsi_determine_min_weight_queue(common);
+ q_len = skb_queue_len(&common->tx_queue[ii]);
+ ii = q_num;
+
+ /* Selecting the queue with least back off */
+ for (; ii < NUM_EDCA_QUEUES; ii++) {
+ if (((common->tx_qinfo[ii].pkt_contended) &&
+ (common->tx_qinfo[ii].weight < min)) && q_len) {
+ min = common->tx_qinfo[ii].weight;
+ q_num = ii;
+ }
+ }
+
+ common->tx_qinfo[q_num].pkt_contended = 0;
+ /* Adjust the back off values for all queues again */
+ recontend_queue = rsi_recalculate_weights(common);
+
+ q_len = skb_queue_len(&common->tx_queue[q_num]);
+ if (!q_len) {
+ /* If any queues are freshly contended and the selected queue
+ * doesn't have any packets
+ * then get the queue number again with fresh values
+ */
+ if (recontend_queue)
+ goto get_queue_num;
+
+ q_num = INVALID_QUEUE;
+ return q_num;
+ }
+
+ common->selected_qnum = q_num;
+ q_len = skb_queue_len(&common->tx_queue[q_num]);
+
+ switch (common->selected_qnum) {
+ case VO_Q:
+ if (q_len > MAX_CONTINUOUS_VO_PKTS)
+ common->pkt_cnt = (MAX_CONTINUOUS_VO_PKTS - 1);
+ else
+ common->pkt_cnt = --q_len;
+ break;
+
+ case VI_Q:
+ if (q_len > MAX_CONTINUOUS_VI_PKTS)
+ common->pkt_cnt = (MAX_CONTINUOUS_VI_PKTS - 1);
+ else
+ common->pkt_cnt = --q_len;
+
+ break;
+
+ default:
+ common->pkt_cnt = 0;
+ break;
+ }
+
+ return q_num;
+}
+
+/**
+ * rsi_core_queue_pkt() - This functions enqueues the packet to the queue
+ * specified by the queue number.
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: None.
+ */
+static void rsi_core_queue_pkt(struct rsi_common *common,
+ struct sk_buff *skb)
+{
+ u8 q_num = skb->priority;
+ if (q_num >= NUM_SOFT_QUEUES) {
+ rsi_dbg(ERR_ZONE, "%s: Invalid Queue Number: q_num = %d\n",
+ __func__, q_num);
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ skb_queue_tail(&common->tx_queue[q_num], skb);
+}
+
+/**
+ * rsi_core_dequeue_pkt() - This functions dequeues the packet from the queue
+ * specified by the queue number.
+ * @common: Pointer to the driver private structure.
+ * @q_num: Queue number.
+ *
+ * Return: Pointer to sk_buff structure.
+ */
+static struct sk_buff *rsi_core_dequeue_pkt(struct rsi_common *common,
+ u8 q_num)
+{
+ if (q_num >= NUM_SOFT_QUEUES) {
+ rsi_dbg(ERR_ZONE, "%s: Invalid Queue Number: q_num = %d\n",
+ __func__, q_num);
+ return NULL;
+ }
+
+ return skb_dequeue(&common->tx_queue[q_num]);
+}
+
+/**
+ * rsi_core_qos_processor() - This function is used to determine the wmm queue
+ * based on the backoff procedure. Data packets are
+ * dequeued from the selected hal queue and sent to
+ * the below layers.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: None.
+ */
+void rsi_core_qos_processor(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct sk_buff *skb;
+ unsigned long tstamp_1, tstamp_2;
+ u8 q_num;
+ int status;
+
+ tstamp_1 = jiffies;
+ while (1) {
+ q_num = rsi_core_determine_hal_queue(common);
+ rsi_dbg(DATA_TX_ZONE,
+ "%s: Queue number = %d\n", __func__, q_num);
+
+ if (q_num == INVALID_QUEUE) {
+ rsi_dbg(DATA_TX_ZONE, "%s: No More Pkt\n", __func__);
+ break;
+ }
+
+ mutex_lock(&common->tx_rxlock);
+
+ status = adapter->check_hw_queue_status(adapter, q_num);
+ if ((status <= 0)) {
+ mutex_unlock(&common->tx_rxlock);
+ break;
+ }
+
+ if ((q_num < MGMT_SOFT_Q) &&
+ ((skb_queue_len(&common->tx_queue[q_num])) <=
+ MIN_DATA_QUEUE_WATER_MARK)) {
+ if (ieee80211_queue_stopped(adapter->hw, WME_AC(q_num)))
+ ieee80211_wake_queue(adapter->hw,
+ WME_AC(q_num));
+ }
+
+ skb = rsi_core_dequeue_pkt(common, q_num);
+ if (skb == NULL) {
+ mutex_unlock(&common->tx_rxlock);
+ break;
+ }
+
+ if (q_num == MGMT_SOFT_Q)
+ status = rsi_send_mgmt_pkt(common, skb);
+ else
+ status = rsi_send_data_pkt(common, skb);
+
+ if (status) {
+ mutex_unlock(&common->tx_rxlock);
+ break;
+ }
+
+ common->tx_stats.total_tx_pkt_send[q_num]++;
+
+ tstamp_2 = jiffies;
+ mutex_unlock(&common->tx_rxlock);
+
+ if (tstamp_2 > tstamp_1 + (300 * HZ / 1000))
+ schedule();
+ }
+}
+
+/**
+ * rsi_core_xmit() - This function transmits the packets received from mac80211
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: None.
+ */
+void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_tx_info *info;
+ struct skb_info *tx_params;
+ struct ieee80211_hdr *tmp_hdr = NULL;
+ u8 q_num, tid = 0;
+
+ if ((!skb) || (!skb->len)) {
+ rsi_dbg(ERR_ZONE, "%s: Null skb/zero Length packet\n",
+ __func__);
+ goto xmit_fail;
+ }
+ info = IEEE80211_SKB_CB(skb);
+ tx_params = (struct skb_info *)info->driver_data;
+ tmp_hdr = (struct ieee80211_hdr *)&skb->data[0];
+
+ if (common->fsm_state != FSM_MAC_INIT_DONE) {
+ rsi_dbg(ERR_ZONE, "%s: FSM state not open\n", __func__);
+ goto xmit_fail;
+ }
+
+ if ((ieee80211_is_mgmt(tmp_hdr->frame_control)) ||
+ (ieee80211_is_ctl(tmp_hdr->frame_control))) {
+ q_num = MGMT_SOFT_Q;
+ skb->priority = q_num;
+ } else {
+ if (ieee80211_is_data_qos(tmp_hdr->frame_control)) {
+ tid = (skb->data[24] & IEEE80211_QOS_TID);
+ skb->priority = TID_TO_WME_AC(tid);
+ } else {
+ tid = IEEE80211_NONQOS_TID;
+ skb->priority = BE_Q;
+ }
+ q_num = skb->priority;
+ tx_params->tid = tid;
+ tx_params->sta_id = 0;
+ }
+
+ if ((q_num != MGMT_SOFT_Q) &&
+ ((skb_queue_len(&common->tx_queue[q_num]) + 1) >=
+ DATA_QUEUE_WATER_MARK)) {
+ if (!ieee80211_queue_stopped(adapter->hw, WME_AC(q_num)))
+ ieee80211_stop_queue(adapter->hw, WME_AC(q_num));
+ rsi_set_event(&common->tx_thread.event);
+ goto xmit_fail;
+ }
+
+ rsi_core_queue_pkt(common, skb);
+ rsi_dbg(DATA_TX_ZONE, "%s: ===> Scheduling TX thead <===\n", __func__);
+ rsi_set_event(&common->tx_thread.event);
+
+ return;
+
+xmit_fail:
+ rsi_dbg(ERR_ZONE, "%s: Failed to queue packet\n", __func__);
+ /* Dropping pkt here */
+ ieee80211_free_txskb(common->priv->hw, skb);
+}
diff --git a/drivers/net/wireless/rsi/rsi_91x_debugfs.c b/drivers/net/wireless/rsi/rsi_91x_debugfs.c
new file mode 100644
index 000000000000..7e4ef4554411
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_debugfs.c
@@ -0,0 +1,339 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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 "rsi_debugfs.h"
+#include "rsi_sdio.h"
+
+/**
+ * rsi_sdio_stats_read() - This function returns the sdio status of the driver.
+ * @seq: Pointer to the sequence file structure.
+ * @data: Pointer to the data.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_sdio_stats_read(struct seq_file *seq, void *data)
+{
+ struct rsi_common *common = seq->private;
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+
+ seq_printf(seq, "total_sdio_interrupts: %d\n",
+ dev->rx_info.sdio_int_counter);
+ seq_printf(seq, "sdio_msdu_pending_intr_count: %d\n",
+ dev->rx_info.total_sdio_msdu_pending_intr);
+ seq_printf(seq, "sdio_buff_full_count : %d\n",
+ dev->rx_info.buf_full_counter);
+ seq_printf(seq, "sdio_buf_semi_full_count %d\n",
+ dev->rx_info.buf_semi_full_counter);
+ seq_printf(seq, "sdio_unknown_intr_count: %d\n",
+ dev->rx_info.total_sdio_unknown_intr);
+ /* RX Path Stats */
+ seq_printf(seq, "BUFFER FULL STATUS : %d\n",
+ dev->rx_info.buffer_full);
+ seq_printf(seq, "SEMI BUFFER FULL STATUS : %d\n",
+ dev->rx_info.semi_buffer_full);
+ seq_printf(seq, "MGMT BUFFER FULL STATUS : %d\n",
+ dev->rx_info.mgmt_buffer_full);
+ seq_printf(seq, "BUFFER FULL COUNTER : %d\n",
+ dev->rx_info.buf_full_counter);
+ seq_printf(seq, "BUFFER SEMI FULL COUNTER : %d\n",
+ dev->rx_info.buf_semi_full_counter);
+ seq_printf(seq, "MGMT BUFFER FULL COUNTER : %d\n",
+ dev->rx_info.mgmt_buf_full_counter);
+
+ return 0;
+}
+
+/**
+ * rsi_sdio_stats_open() - This funtion calls single open function of seq_file
+ * to open file and read contents from it.
+ * @inode: Pointer to the inode structure.
+ * @file: Pointer to the file structure.
+ *
+ * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure.
+ */
+static int rsi_sdio_stats_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, rsi_sdio_stats_read, inode->i_private);
+}
+
+/**
+ * rsi_version_read() - This function gives driver and firmware version number.
+ * @seq: Pointer to the sequence file structure.
+ * @data: Pointer to the data.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_version_read(struct seq_file *seq, void *data)
+{
+ struct rsi_common *common = seq->private;
+
+ common->driver_ver.major = 0;
+ common->driver_ver.minor = 1;
+ common->driver_ver.release_num = 0;
+ common->driver_ver.patch_num = 0;
+ seq_printf(seq, "Driver : %x.%d.%d.%d\nLMAC : %d.%d.%d.%d\n",
+ common->driver_ver.major,
+ common->driver_ver.minor,
+ common->driver_ver.release_num,
+ common->driver_ver.patch_num,
+ common->fw_ver.major,
+ common->fw_ver.minor,
+ common->fw_ver.release_num,
+ common->fw_ver.patch_num);
+ return 0;
+}
+
+/**
+ * rsi_version_open() - This funtion calls single open function of seq_file to
+ * open file and read contents from it.
+ * @inode: Pointer to the inode structure.
+ * @file: Pointer to the file structure.
+ *
+ * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure.
+ */
+static int rsi_version_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, rsi_version_read, inode->i_private);
+}
+
+/**
+ * rsi_stats_read() - This function return the status of the driver.
+ * @seq: Pointer to the sequence file structure.
+ * @data: Pointer to the data.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_stats_read(struct seq_file *seq, void *data)
+{
+ struct rsi_common *common = seq->private;
+
+ unsigned char fsm_state[][32] = {
+ "FSM_CARD_NOT_READY",
+ "FSM_BOOT_PARAMS_SENT",
+ "FSM_EEPROM_READ_MAC_ADDR",
+ "FSM_RESET_MAC_SENT",
+ "FSM_RADIO_CAPS_SENT",
+ "FSM_BB_RF_PROG_SENT",
+ "FSM_MAC_INIT_DONE"
+ };
+ seq_puts(seq, "==> RSI STA DRIVER STATUS <==\n");
+ seq_puts(seq, "DRIVER_FSM_STATE: ");
+
+ if (common->fsm_state <= FSM_MAC_INIT_DONE)
+ seq_printf(seq, "%s", fsm_state[common->fsm_state]);
+
+ seq_printf(seq, "(%d)\n\n", common->fsm_state);
+
+ /* Mgmt TX Path Stats */
+ seq_printf(seq, "total_mgmt_pkt_send : %d\n",
+ common->tx_stats.total_tx_pkt_send[MGMT_SOFT_Q]);
+ seq_printf(seq, "total_mgmt_pkt_queued : %d\n",
+ skb_queue_len(&common->tx_queue[4]));
+ seq_printf(seq, "total_mgmt_pkt_freed : %d\n",
+ common->tx_stats.total_tx_pkt_freed[MGMT_SOFT_Q]);
+
+ /* Data TX Path Stats */
+ seq_printf(seq, "total_data_vo_pkt_send: %8d\t",
+ common->tx_stats.total_tx_pkt_send[VO_Q]);
+ seq_printf(seq, "total_data_vo_pkt_queued: %8d\t",
+ skb_queue_len(&common->tx_queue[0]));
+ seq_printf(seq, "total_vo_pkt_freed: %8d\n",
+ common->tx_stats.total_tx_pkt_freed[VO_Q]);
+ seq_printf(seq, "total_data_vi_pkt_send: %8d\t",
+ common->tx_stats.total_tx_pkt_send[VI_Q]);
+ seq_printf(seq, "total_data_vi_pkt_queued: %8d\t",
+ skb_queue_len(&common->tx_queue[1]));
+ seq_printf(seq, "total_vi_pkt_freed: %8d\n",
+ common->tx_stats.total_tx_pkt_freed[VI_Q]);
+ seq_printf(seq, "total_data_be_pkt_send: %8d\t",
+ common->tx_stats.total_tx_pkt_send[BE_Q]);
+ seq_printf(seq, "total_data_be_pkt_queued: %8d\t",
+ skb_queue_len(&common->tx_queue[2]));
+ seq_printf(seq, "total_be_pkt_freed: %8d\n",
+ common->tx_stats.total_tx_pkt_freed[BE_Q]);
+ seq_printf(seq, "total_data_bk_pkt_send: %8d\t",
+ common->tx_stats.total_tx_pkt_send[BK_Q]);
+ seq_printf(seq, "total_data_bk_pkt_queued: %8d\t",
+ skb_queue_len(&common->tx_queue[3]));
+ seq_printf(seq, "total_bk_pkt_freed: %8d\n",
+ common->tx_stats.total_tx_pkt_freed[BK_Q]);
+
+ seq_puts(seq, "\n");
+ return 0;
+}
+
+/**
+ * rsi_stats_open() - This funtion calls single open function of seq_file to
+ * open file and read contents from it.
+ * @inode: Pointer to the inode structure.
+ * @file: Pointer to the file structure.
+ *
+ * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure.
+ */
+static int rsi_stats_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, rsi_stats_read, inode->i_private);
+}
+
+/**
+ * rsi_debug_zone_read() - This function display the currently enabled debug zones.
+ * @seq: Pointer to the sequence file structure.
+ * @data: Pointer to the data.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_debug_zone_read(struct seq_file *seq, void *data)
+{
+ rsi_dbg(FSM_ZONE, "%x: rsi_enabled zone", rsi_zone_enabled);
+ seq_printf(seq, "The zones available are %#x\n",
+ rsi_zone_enabled);
+ return 0;
+}
+
+/**
+ * rsi_debug_read() - This funtion calls single open function of seq_file to
+ * open file and read contents from it.
+ * @inode: Pointer to the inode structure.
+ * @file: Pointer to the file structure.
+ *
+ * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure.
+ */
+static int rsi_debug_read(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, rsi_debug_zone_read, inode->i_private);
+}
+
+/**
+ * rsi_debug_zone_write() - This function writes into hal queues as per user
+ * requirement.
+ * @filp: Pointer to the file structure.
+ * @buff: Pointer to the character buffer.
+ * @len: Length of the data to be written into buffer.
+ * @data: Pointer to the data.
+ *
+ * Return: len: Number of bytes read.
+ */
+static ssize_t rsi_debug_zone_write(struct file *filp,
+ const char __user *buff,
+ size_t len,
+ loff_t *data)
+{
+ unsigned long dbg_zone;
+ int ret;
+
+ if (!len)
+ return 0;
+
+ ret = kstrtoul_from_user(buff, len, 16, &dbg_zone);
+
+ if (ret)
+ return ret;
+
+ rsi_zone_enabled = dbg_zone;
+ return len;
+}
+
+#define FOPS(fopen) { \
+ .owner = THIS_MODULE, \
+ .open = (fopen), \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+}
+
+#define FOPS_RW(fopen, fwrite) { \
+ .owner = THIS_MODULE, \
+ .open = (fopen), \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .write = (fwrite), \
+}
+
+static const struct rsi_dbg_files dev_debugfs_files[] = {
+ {"version", 0644, FOPS(rsi_version_open),},
+ {"stats", 0644, FOPS(rsi_stats_open),},
+ {"debug_zone", 0666, FOPS_RW(rsi_debug_read, rsi_debug_zone_write),},
+ {"sdio_stats", 0644, FOPS(rsi_sdio_stats_open),},
+};
+
+/**
+ * rsi_init_dbgfs() - This function initializes the dbgfs entry.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_init_dbgfs(struct rsi_hw *adapter)
+{
+ struct rsi_common *common = adapter->priv;
+ struct rsi_debugfs *dev_dbgfs;
+ char devdir[6];
+ int ii;
+ const struct rsi_dbg_files *files;
+
+ dev_dbgfs = kzalloc(sizeof(*dev_dbgfs), GFP_KERNEL);
+ adapter->dfsentry = dev_dbgfs;
+
+ snprintf(devdir, sizeof(devdir), "%s",
+ wiphy_name(adapter->hw->wiphy));
+ dev_dbgfs->subdir = debugfs_create_dir(devdir, NULL);
+
+ if (IS_ERR(dev_dbgfs->subdir)) {
+ if (dev_dbgfs->subdir == ERR_PTR(-ENODEV))
+ rsi_dbg(ERR_ZONE,
+ "%s:Debugfs has not been mounted\n", __func__);
+ else
+ rsi_dbg(ERR_ZONE, "debugfs:%s not created\n", devdir);
+
+ adapter->dfsentry = NULL;
+ kfree(dev_dbgfs);
+ return (int)PTR_ERR(dev_dbgfs->subdir);
+ } else {
+ for (ii = 0; ii < adapter->num_debugfs_entries; ii++) {
+ files = &dev_debugfs_files[ii];
+ dev_dbgfs->rsi_files[ii] =
+ debugfs_create_file(files->name,
+ files->perms,
+ dev_dbgfs->subdir,
+ common,
+ &files->fops);
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rsi_init_dbgfs);
+
+/**
+ * rsi_remove_dbgfs() - Removes the previously created dbgfs file entries
+ * in the reverse order of creation.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: None.
+ */
+void rsi_remove_dbgfs(struct rsi_hw *adapter)
+{
+ struct rsi_debugfs *dev_dbgfs = adapter->dfsentry;
+
+ if (!dev_dbgfs)
+ return;
+
+ debugfs_remove_recursive(dev_dbgfs->subdir);
+}
+EXPORT_SYMBOL_GPL(rsi_remove_dbgfs);
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
new file mode 100644
index 000000000000..84164747ace0
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -0,0 +1,1008 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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/etherdevice.h>
+#include "rsi_debugfs.h"
+#include "rsi_mgmt.h"
+#include "rsi_common.h"
+
+static const struct ieee80211_channel rsi_2ghz_channels[] = {
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2412,
+ .hw_value = 1 }, /* Channel 1 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2417,
+ .hw_value = 2 }, /* Channel 2 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2422,
+ .hw_value = 3 }, /* Channel 3 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2427,
+ .hw_value = 4 }, /* Channel 4 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2432,
+ .hw_value = 5 }, /* Channel 5 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2437,
+ .hw_value = 6 }, /* Channel 6 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2442,
+ .hw_value = 7 }, /* Channel 7 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2447,
+ .hw_value = 8 }, /* Channel 8 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2452,
+ .hw_value = 9 }, /* Channel 9 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2457,
+ .hw_value = 10 }, /* Channel 10 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2462,
+ .hw_value = 11 }, /* Channel 11 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2467,
+ .hw_value = 12 }, /* Channel 12 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2472,
+ .hw_value = 13 }, /* Channel 13 */
+ { .band = IEEE80211_BAND_2GHZ, .center_freq = 2484,
+ .hw_value = 14 }, /* Channel 14 */
+};
+
+static const struct ieee80211_channel rsi_5ghz_channels[] = {
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5180,
+ .hw_value = 36, }, /* Channel 36 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5200,
+ .hw_value = 40, }, /* Channel 40 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5220,
+ .hw_value = 44, }, /* Channel 44 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5240,
+ .hw_value = 48, }, /* Channel 48 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5260,
+ .hw_value = 52, }, /* Channel 52 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5280,
+ .hw_value = 56, }, /* Channel 56 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5300,
+ .hw_value = 60, }, /* Channel 60 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5320,
+ .hw_value = 64, }, /* Channel 64 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5500,
+ .hw_value = 100, }, /* Channel 100 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5520,
+ .hw_value = 104, }, /* Channel 104 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5540,
+ .hw_value = 108, }, /* Channel 108 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5560,
+ .hw_value = 112, }, /* Channel 112 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5580,
+ .hw_value = 116, }, /* Channel 116 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5600,
+ .hw_value = 120, }, /* Channel 120 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5620,
+ .hw_value = 124, }, /* Channel 124 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5640,
+ .hw_value = 128, }, /* Channel 128 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5660,
+ .hw_value = 132, }, /* Channel 132 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5680,
+ .hw_value = 136, }, /* Channel 136 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5700,
+ .hw_value = 140, }, /* Channel 140 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5745,
+ .hw_value = 149, }, /* Channel 149 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5765,
+ .hw_value = 153, }, /* Channel 153 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5785,
+ .hw_value = 157, }, /* Channel 157 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5805,
+ .hw_value = 161, }, /* Channel 161 */
+ { .band = IEEE80211_BAND_5GHZ, .center_freq = 5825,
+ .hw_value = 165, }, /* Channel 165 */
+};
+
+struct ieee80211_rate rsi_rates[12] = {
+ { .bitrate = STD_RATE_01 * 5, .hw_value = RSI_RATE_1 },
+ { .bitrate = STD_RATE_02 * 5, .hw_value = RSI_RATE_2 },
+ { .bitrate = STD_RATE_5_5 * 5, .hw_value = RSI_RATE_5_5 },
+ { .bitrate = STD_RATE_11 * 5, .hw_value = RSI_RATE_11 },
+ { .bitrate = STD_RATE_06 * 5, .hw_value = RSI_RATE_6 },
+ { .bitrate = STD_RATE_09 * 5, .hw_value = RSI_RATE_9 },
+ { .bitrate = STD_RATE_12 * 5, .hw_value = RSI_RATE_12 },
+ { .bitrate = STD_RATE_18 * 5, .hw_value = RSI_RATE_18 },
+ { .bitrate = STD_RATE_24 * 5, .hw_value = RSI_RATE_24 },
+ { .bitrate = STD_RATE_36 * 5, .hw_value = RSI_RATE_36 },
+ { .bitrate = STD_RATE_48 * 5, .hw_value = RSI_RATE_48 },
+ { .bitrate = STD_RATE_54 * 5, .hw_value = RSI_RATE_54 },
+};
+
+const u16 rsi_mcsrates[8] = {
+ RSI_RATE_MCS0, RSI_RATE_MCS1, RSI_RATE_MCS2, RSI_RATE_MCS3,
+ RSI_RATE_MCS4, RSI_RATE_MCS5, RSI_RATE_MCS6, RSI_RATE_MCS7
+};
+
+/**
+ * rsi_is_cipher_wep() - This function determines if the cipher is WEP or not.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: If cipher type is WEP, a value of 1 is returned, else 0.
+ */
+
+bool rsi_is_cipher_wep(struct rsi_common *common)
+{
+ if (((common->secinfo.gtk_cipher == WLAN_CIPHER_SUITE_WEP104) ||
+ (common->secinfo.gtk_cipher == WLAN_CIPHER_SUITE_WEP40)) &&
+ (!common->secinfo.ptk_cipher))
+ return true;
+ else
+ return false;
+}
+
+/**
+ * rsi_register_rates_channels() - This function registers channels and rates.
+ * @adapter: Pointer to the adapter structure.
+ * @band: Operating band to be set.
+ *
+ * Return: None.
+ */
+static void rsi_register_rates_channels(struct rsi_hw *adapter, int band)
+{
+ struct ieee80211_supported_band *sbands = &adapter->sbands[band];
+ void *channels = NULL;
+
+ if (band == IEEE80211_BAND_2GHZ) {
+ channels = kmalloc(sizeof(rsi_2ghz_channels), GFP_KERNEL);
+ memcpy(channels,
+ rsi_2ghz_channels,
+ sizeof(rsi_2ghz_channels));
+ sbands->band = IEEE80211_BAND_2GHZ;
+ sbands->n_channels = ARRAY_SIZE(rsi_2ghz_channels);
+ sbands->bitrates = rsi_rates;
+ sbands->n_bitrates = ARRAY_SIZE(rsi_rates);
+ } else {
+ channels = kmalloc(sizeof(rsi_5ghz_channels), GFP_KERNEL);
+ memcpy(channels,
+ rsi_5ghz_channels,
+ sizeof(rsi_5ghz_channels));
+ sbands->band = IEEE80211_BAND_5GHZ;
+ sbands->n_channels = ARRAY_SIZE(rsi_5ghz_channels);
+ sbands->bitrates = &rsi_rates[4];
+ sbands->n_bitrates = ARRAY_SIZE(rsi_rates) - 4;
+ }
+
+ sbands->channels = channels;
+
+ memset(&sbands->ht_cap, 0, sizeof(struct ieee80211_sta_ht_cap));
+ sbands->ht_cap.ht_supported = true;
+ sbands->ht_cap.cap = (IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40);
+ sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K;
+ sbands->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
+ sbands->ht_cap.mcs.rx_mask[0] = 0xff;
+ sbands->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+ /* sbands->ht_cap.mcs.rx_highest = 0x82; */
+}
+
+/**
+ * rsi_mac80211_attach() - This function is used to de-initialize the
+ * Mac80211 stack.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: None.
+ */
+void rsi_mac80211_detach(struct rsi_hw *adapter)
+{
+ struct ieee80211_hw *hw = adapter->hw;
+
+ if (hw) {
+ ieee80211_stop_queues(hw);
+ ieee80211_unregister_hw(hw);
+ ieee80211_free_hw(hw);
+ }
+
+ rsi_remove_dbgfs(adapter);
+}
+EXPORT_SYMBOL_GPL(rsi_mac80211_detach);
+
+/**
+ * rsi_indicate_tx_status() - This function indicates the transmit status.
+ * @adapter: Pointer to the adapter structure.
+ * @skb: Pointer to the socket buffer structure.
+ * @status: Status
+ *
+ * Return: None.
+ */
+void rsi_indicate_tx_status(struct rsi_hw *adapter,
+ struct sk_buff *skb,
+ int status)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ memset(info->driver_data, 0, IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
+
+ if (!status)
+ info->flags |= IEEE80211_TX_STAT_ACK;
+
+ ieee80211_tx_status_irqsafe(adapter->hw, skb);
+}
+
+/**
+ * rsi_mac80211_tx() - This is the handler that 802.11 module calls for each
+ * transmitted frame.SKB contains the buffer starting
+ * from the IEEE 802.11 header.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @control: Pointer to the ieee80211_tx_control structure
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: None
+ */
+static void rsi_mac80211_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ rsi_core_xmit(common, skb);
+}
+
+/**
+ * rsi_mac80211_start() - This is first handler that 802.11 module calls, since
+ * the driver init is complete by then, just
+ * returns success.
+ * @hw: Pointer to the ieee80211_hw structure.
+ *
+ * Return: 0 as success.
+ */
+static int rsi_mac80211_start(struct ieee80211_hw *hw)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ common->iface_down = false;
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+/**
+ * rsi_mac80211_stop() - This is the last handler that 802.11 module calls.
+ * @hw: Pointer to the ieee80211_hw structure.
+ *
+ * Return: None.
+ */
+static void rsi_mac80211_stop(struct ieee80211_hw *hw)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ common->iface_down = true;
+ mutex_unlock(&common->mutex);
+}
+
+/**
+ * rsi_mac80211_add_interface() - This function is called when a netdevice
+ * attached to the hardware is enabled.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ *
+ * Return: ret: 0 on success, negative error code on failure.
+ */
+static int rsi_mac80211_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+ int ret = -EOPNOTSUPP;
+
+ mutex_lock(&common->mutex);
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ if (!adapter->sc_nvifs) {
+ ++adapter->sc_nvifs;
+ adapter->vifs[0] = vif;
+ ret = rsi_set_vap_capabilities(common, STA_OPMODE);
+ }
+ break;
+ default:
+ rsi_dbg(ERR_ZONE,
+ "%s: Interface type %d not supported\n", __func__,
+ vif->type);
+ }
+ mutex_unlock(&common->mutex);
+
+ return ret;
+}
+
+/**
+ * rsi_mac80211_remove_interface() - This function notifies driver that an
+ * interface is going down.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ *
+ * Return: None.
+ */
+static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ if (vif->type == NL80211_IFTYPE_STATION)
+ adapter->sc_nvifs--;
+
+ if (!memcmp(adapter->vifs[0], vif, sizeof(struct ieee80211_vif)))
+ adapter->vifs[0] = NULL;
+ mutex_unlock(&common->mutex);
+}
+
+/**
+ * rsi_mac80211_config() - This function is a handler for configuration
+ * requests. The stack calls this function to
+ * change hardware configuration, e.g., channel.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @changed: Changed flags set.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int rsi_mac80211_config(struct ieee80211_hw *hw,
+ u32 changed)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+ int status = -EOPNOTSUPP;
+
+ mutex_lock(&common->mutex);
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+ u16 channel = curchan->hw_value;
+
+ rsi_dbg(INFO_ZONE,
+ "%s: Set channel: %d MHz type: %d channel_no %d\n",
+ __func__, curchan->center_freq,
+ curchan->flags, channel);
+ common->band = curchan->band;
+ status = rsi_set_channel(adapter->priv, channel);
+ }
+ mutex_unlock(&common->mutex);
+
+ return status;
+}
+
+/**
+ * rsi_get_connected_channel() - This function is used to get the current
+ * connected channel number.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: Current connected AP's channel number is returned.
+ */
+u16 rsi_get_connected_channel(struct rsi_hw *adapter)
+{
+ struct ieee80211_vif *vif = adapter->vifs[0];
+ if (vif) {
+ struct ieee80211_bss_conf *bss = &vif->bss_conf;
+ struct ieee80211_channel *channel = bss->chandef.chan;
+ return channel->hw_value;
+ }
+
+ return 0;
+}
+
+/**
+ * rsi_mac80211_bss_info_changed() - This function is a handler for config
+ * requests related to BSS parameters that
+ * may vary during BSS's lifespan.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @bss_conf: Pointer to the ieee80211_bss_conf structure.
+ * @changed: Changed flags set.
+ *
+ * Return: None.
+ */
+static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 changed)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ if (changed & BSS_CHANGED_ASSOC) {
+ rsi_dbg(INFO_ZONE, "%s: Changed Association status: %d\n",
+ __func__, bss_conf->assoc);
+ rsi_inform_bss_status(common,
+ bss_conf->assoc,
+ bss_conf->bssid,
+ bss_conf->qos,
+ bss_conf->aid);
+ }
+ mutex_unlock(&common->mutex);
+}
+
+/**
+ * rsi_mac80211_conf_filter() - This function configure the device's RX filter.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @changed: Changed flags set.
+ * @total_flags: Total initial flags set.
+ * @multicast: Multicast.
+ *
+ * Return: None.
+ */
+static void rsi_mac80211_conf_filter(struct ieee80211_hw *hw,
+ u32 changed_flags,
+ u32 *total_flags,
+ u64 multicast)
+{
+ /* Not doing much here as of now */
+ *total_flags &= RSI_SUPP_FILTERS;
+}
+
+/**
+ * rsi_mac80211_conf_tx() - This function configures TX queue parameters
+ * (EDCF (aifs, cw_min, cw_max), bursting)
+ * for a hardware TX queue.
+ * @hw: Pointer to the ieee80211_hw structure
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @queue: Queue number.
+ * @params: Pointer to ieee80211_tx_queue_params structure.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int rsi_mac80211_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+ u8 idx = 0;
+
+ if (queue >= IEEE80211_NUM_ACS)
+ return 0;
+
+ rsi_dbg(INFO_ZONE,
+ "%s: Conf queue %d, aifs: %d, cwmin: %d cwmax: %d, txop: %d\n",
+ __func__, queue, params->aifs,
+ params->cw_min, params->cw_max, params->txop);
+
+ mutex_lock(&common->mutex);
+ /* Map into the way the f/w expects */
+ switch (queue) {
+ case IEEE80211_AC_VO:
+ idx = VO_Q;
+ break;
+ case IEEE80211_AC_VI:
+ idx = VI_Q;
+ break;
+ case IEEE80211_AC_BE:
+ idx = BE_Q;
+ break;
+ case IEEE80211_AC_BK:
+ idx = BK_Q;
+ break;
+ default:
+ idx = BE_Q;
+ break;
+ }
+
+ memcpy(&common->edca_params[idx],
+ params,
+ sizeof(struct ieee80211_tx_queue_params));
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+/**
+ * rsi_hal_key_config() - This function loads the keys into the firmware.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @key: Pointer to the ieee80211_key_conf structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_hal_key_config(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *key)
+{
+ struct rsi_hw *adapter = hw->priv;
+ int status;
+ u8 key_type;
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+ key_type = RSI_PAIRWISE_KEY;
+ else
+ key_type = RSI_GROUP_KEY;
+
+ rsi_dbg(ERR_ZONE, "%s: Cipher 0x%x key_type: %d key_len: %d\n",
+ __func__, key->cipher, key_type, key->keylen);
+
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP104) ||
+ (key->cipher == WLAN_CIPHER_SUITE_WEP40)) {
+ status = rsi_hal_load_key(adapter->priv,
+ key->key,
+ key->keylen,
+ RSI_PAIRWISE_KEY,
+ key->keyidx,
+ key->cipher);
+ if (status)
+ return status;
+ }
+ return rsi_hal_load_key(adapter->priv,
+ key->key,
+ key->keylen,
+ key_type,
+ key->keyidx,
+ key->cipher);
+}
+
+/**
+ * rsi_mac80211_set_key() - This function sets type of key to be loaded.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @cmd: enum set_key_cmd.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @sta: Pointer to the ieee80211_sta structure.
+ * @key: Pointer to the ieee80211_key_conf structure.
+ *
+ * Return: status: 0 on success, negative error code on failure.
+ */
+static int rsi_mac80211_set_key(struct ieee80211_hw *hw,
+ enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+ struct security_info *secinfo = &common->secinfo;
+ int status;
+
+ mutex_lock(&common->mutex);
+ switch (cmd) {
+ case SET_KEY:
+ secinfo->security_enable = true;
+ status = rsi_hal_key_config(hw, vif, key);
+ if (status) {
+ mutex_unlock(&common->mutex);
+ return status;
+ }
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+ secinfo->ptk_cipher = key->cipher;
+ else
+ secinfo->gtk_cipher = key->cipher;
+
+ key->hw_key_idx = key->keyidx;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ rsi_dbg(ERR_ZONE, "%s: RSI set_key\n", __func__);
+ break;
+
+ case DISABLE_KEY:
+ secinfo->security_enable = false;
+ rsi_dbg(ERR_ZONE, "%s: RSI del key\n", __func__);
+ memset(key, 0, sizeof(struct ieee80211_key_conf));
+ status = rsi_hal_key_config(hw, vif, key);
+ break;
+
+ default:
+ status = -EOPNOTSUPP;
+ break;
+ }
+
+ mutex_unlock(&common->mutex);
+ return status;
+}
+
+/**
+ * rsi_mac80211_ampdu_action() - This function selects the AMPDU action for
+ * the corresponding mlme_action flag and
+ * informs the f/w regarding this.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @action: ieee80211_ampdu_mlme_action enum.
+ * @sta: Pointer to the ieee80211_sta structure.
+ * @tid: Traffic identifier.
+ * @ssn: Pointer to ssn value.
+ * @buf_size: Buffer size (for kernel version > 2.6.38).
+ *
+ * Return: status: 0 on success, negative error code on failure.
+ */
+static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta,
+ unsigned short tid,
+ unsigned short *ssn,
+ unsigned char buf_size)
+{
+ int status = -EOPNOTSUPP;
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+ u16 seq_no = 0;
+ u8 ii = 0;
+
+ for (ii = 0; ii < RSI_MAX_VIFS; ii++) {
+ if (vif == adapter->vifs[ii])
+ break;
+ }
+
+ mutex_lock(&common->mutex);
+ rsi_dbg(INFO_ZONE, "%s: AMPDU action %d called\n", __func__, action);
+ if (ssn != NULL)
+ seq_no = *ssn;
+
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ status = rsi_send_aggregation_params_frame(common,
+ tid,
+ seq_no,
+ buf_size,
+ STA_RX_ADDBA_DONE);
+ break;
+
+ case IEEE80211_AMPDU_RX_STOP:
+ status = rsi_send_aggregation_params_frame(common,
+ tid,
+ 0,
+ buf_size,
+ STA_RX_DELBA);
+ break;
+
+ case IEEE80211_AMPDU_TX_START:
+ common->vif_info[ii].seq_start = seq_no;
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ break;
+
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH:
+ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+ status = rsi_send_aggregation_params_frame(common,
+ tid,
+ seq_no,
+ buf_size,
+ STA_TX_DELBA);
+ if (!status)
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ break;
+
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+ status = rsi_send_aggregation_params_frame(common,
+ tid,
+ common->vif_info[ii]
+ .seq_start,
+ buf_size,
+ STA_TX_ADDBA_DONE);
+ break;
+
+ default:
+ rsi_dbg(ERR_ZONE, "%s: Uknown AMPDU action\n", __func__);
+ break;
+ }
+
+ mutex_unlock(&common->mutex);
+ return status;
+}
+
+/**
+ * rsi_mac80211_set_rts_threshold() - This function sets rts threshold value.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @value: Rts threshold value.
+ *
+ * Return: 0 on success.
+ */
+static int rsi_mac80211_set_rts_threshold(struct ieee80211_hw *hw,
+ u32 value)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ common->rts_threshold = value;
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+/**
+ * rsi_mac80211_set_rate_mask() - This function sets bitrate_mask to be used.
+ * @hw: Pointer to the ieee80211_hw structure
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @mask: Pointer to the cfg80211_bitrate_mask structure.
+ *
+ * Return: 0 on success.
+ */
+static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+
+ common->fixedrate_mask[IEEE80211_BAND_2GHZ] = 0;
+
+ if (mask->control[IEEE80211_BAND_2GHZ].legacy == 0xfff) {
+ common->fixedrate_mask[IEEE80211_BAND_2GHZ] =
+ (mask->control[IEEE80211_BAND_2GHZ].ht_mcs[0] << 12);
+ } else {
+ common->fixedrate_mask[IEEE80211_BAND_2GHZ] =
+ mask->control[IEEE80211_BAND_2GHZ].legacy;
+ }
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+/**
+ * rsi_fill_rx_status() - This function fills rx status in
+ * ieee80211_rx_status structure.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @skb: Pointer to the socket buffer structure.
+ * @common: Pointer to the driver private structure.
+ * @rxs: Pointer to the ieee80211_rx_status structure.
+ *
+ * Return: None.
+ */
+static void rsi_fill_rx_status(struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct rsi_common *common,
+ struct ieee80211_rx_status *rxs)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct skb_info *rx_params = (struct skb_info *)info->driver_data;
+ struct ieee80211_hdr *hdr;
+ char rssi = rx_params->rssi;
+ u8 hdrlen = 0;
+ u8 channel = rx_params->channel;
+ s32 freq;
+
+ hdr = ((struct ieee80211_hdr *)(skb->data));
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ memset(info, 0, sizeof(struct ieee80211_tx_info));
+
+ rxs->signal = -(rssi);
+
+ if (channel <= 14)
+ rxs->band = IEEE80211_BAND_2GHZ;
+ else
+ rxs->band = IEEE80211_BAND_5GHZ;
+
+ freq = ieee80211_channel_to_frequency(channel, rxs->band);
+
+ if (freq)
+ rxs->freq = freq;
+
+ if (ieee80211_has_protected(hdr->frame_control)) {
+ if (rsi_is_cipher_wep(common)) {
+ memmove(skb->data + 4, skb->data, hdrlen);
+ skb_pull(skb, 4);
+ } else {
+ memmove(skb->data + 8, skb->data, hdrlen);
+ skb_pull(skb, 8);
+ rxs->flag |= RX_FLAG_MMIC_STRIPPED;
+ }
+ rxs->flag |= RX_FLAG_DECRYPTED;
+ rxs->flag |= RX_FLAG_IV_STRIPPED;
+ }
+}
+
+/**
+ * rsi_indicate_pkt_to_os() - This function sends recieved packet to mac80211.
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: None.
+ */
+void rsi_indicate_pkt_to_os(struct rsi_common *common,
+ struct sk_buff *skb)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_hw *hw = adapter->hw;
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+
+ if ((common->iface_down) || (!adapter->sc_nvifs)) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ /* filling in the ieee80211_rx_status flags */
+ rsi_fill_rx_status(hw, skb, common, rx_status);
+
+ ieee80211_rx_irqsafe(hw, skb);
+}
+
+static void rsi_set_min_rate(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ struct rsi_common *common)
+{
+ u8 band = hw->conf.chandef.chan->band;
+ u8 ii;
+ u32 rate_bitmap;
+ bool matched = false;
+
+ common->bitrate_mask[band] = sta->supp_rates[band];
+
+ rate_bitmap = (common->fixedrate_mask[band] & sta->supp_rates[band]);
+
+ if (rate_bitmap & 0xfff) {
+ /* Find out the min rate */
+ for (ii = 0; ii < ARRAY_SIZE(rsi_rates); ii++) {
+ if (rate_bitmap & BIT(ii)) {
+ common->min_rate = rsi_rates[ii].hw_value;
+ matched = true;
+ break;
+ }
+ }
+ }
+
+ common->vif_info[0].is_ht = sta->ht_cap.ht_supported;
+
+ if ((common->vif_info[0].is_ht) && (rate_bitmap >> 12)) {
+ for (ii = 0; ii < ARRAY_SIZE(rsi_mcsrates); ii++) {
+ if ((rate_bitmap >> 12) & BIT(ii)) {
+ common->min_rate = rsi_mcsrates[ii];
+ matched = true;
+ break;
+ }
+ }
+ }
+
+ if (!matched)
+ common->min_rate = 0xffff;
+}
+
+/**
+ * rsi_mac80211_sta_add() - This function notifies driver about a peer getting
+ * connected.
+ * @hw: pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @sta: Pointer to the ieee80211_sta structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_mac80211_sta_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+
+ rsi_set_min_rate(hw, sta, common);
+
+ if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ||
+ (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)) {
+ common->vif_info[0].sgi = true;
+ }
+
+ if (sta->ht_cap.ht_supported)
+ ieee80211_start_tx_ba_session(sta, 0, 0);
+
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+/**
+ * rsi_mac80211_sta_remove() - This function notifies driver about a peer
+ * getting disconnected.
+ * @hw: Pointer to the ieee80211_hw structure.
+ * @vif: Pointer to the ieee80211_vif structure.
+ * @sta: Pointer to the ieee80211_sta structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_mac80211_sta_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ mutex_lock(&common->mutex);
+ /* Resetting all the fields to default values */
+ common->bitrate_mask[IEEE80211_BAND_2GHZ] = 0;
+ common->bitrate_mask[IEEE80211_BAND_5GHZ] = 0;
+ common->min_rate = 0xffff;
+ common->vif_info[0].is_ht = false;
+ common->vif_info[0].sgi = false;
+ common->vif_info[0].seq_start = 0;
+ common->secinfo.ptk_cipher = 0;
+ common->secinfo.gtk_cipher = 0;
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+static struct ieee80211_ops mac80211_ops = {
+ .tx = rsi_mac80211_tx,
+ .start = rsi_mac80211_start,
+ .stop = rsi_mac80211_stop,
+ .add_interface = rsi_mac80211_add_interface,
+ .remove_interface = rsi_mac80211_remove_interface,
+ .config = rsi_mac80211_config,
+ .bss_info_changed = rsi_mac80211_bss_info_changed,
+ .conf_tx = rsi_mac80211_conf_tx,
+ .configure_filter = rsi_mac80211_conf_filter,
+ .set_key = rsi_mac80211_set_key,
+ .set_rts_threshold = rsi_mac80211_set_rts_threshold,
+ .set_bitrate_mask = rsi_mac80211_set_rate_mask,
+ .ampdu_action = rsi_mac80211_ampdu_action,
+ .sta_add = rsi_mac80211_sta_add,
+ .sta_remove = rsi_mac80211_sta_remove,
+};
+
+/**
+ * rsi_mac80211_attach() - This function is used to initialize Mac80211 stack.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_mac80211_attach(struct rsi_common *common)
+{
+ int status = 0;
+ struct ieee80211_hw *hw = NULL;
+ struct wiphy *wiphy = NULL;
+ struct rsi_hw *adapter = common->priv;
+ u8 addr_mask[ETH_ALEN] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x3};
+
+ rsi_dbg(INIT_ZONE, "%s: Performing mac80211 attach\n", __func__);
+
+ hw = ieee80211_alloc_hw(sizeof(struct rsi_hw), &mac80211_ops);
+ if (!hw) {
+ rsi_dbg(ERR_ZONE, "%s: ieee80211 hw alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ wiphy = hw->wiphy;
+
+ SET_IEEE80211_DEV(hw, adapter->device);
+
+ hw->priv = adapter;
+ adapter->hw = hw;
+
+ hw->flags = IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_HAS_RATE_CONTROL |
+ IEEE80211_HW_AMPDU_AGGREGATION |
+ 0;
+
+ hw->queues = MAX_HW_QUEUES;
+ hw->extra_tx_headroom = RSI_NEEDED_HEADROOM;
+
+ hw->max_rates = 1;
+ hw->max_rate_tries = MAX_RETRIES;
+
+ hw->max_tx_aggregation_subframes = 6;
+ rsi_register_rates_channels(adapter, IEEE80211_BAND_2GHZ);
+ hw->rate_control_algorithm = "AARF";
+
+ SET_IEEE80211_PERM_ADDR(hw, common->mac_addr);
+ ether_addr_copy(hw->wiphy->addr_mask, addr_mask);
+
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->retry_short = RETRY_SHORT;
+ wiphy->retry_long = RETRY_LONG;
+ wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
+ wiphy->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
+ wiphy->flags = 0;
+
+ wiphy->available_antennas_rx = 1;
+ wiphy->available_antennas_tx = 1;
+ wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &adapter->sbands[IEEE80211_BAND_2GHZ];
+
+ status = ieee80211_register_hw(hw);
+ if (status)
+ return status;
+
+ return rsi_init_dbgfs(adapter);
+}
diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c
new file mode 100644
index 000000000000..8810862ae826
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_main.c
@@ -0,0 +1,295 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include "rsi_mgmt.h"
+#include "rsi_common.h"
+
+u32 rsi_zone_enabled = /* INFO_ZONE |
+ INIT_ZONE |
+ MGMT_TX_ZONE |
+ MGMT_RX_ZONE |
+ DATA_TX_ZONE |
+ DATA_RX_ZONE |
+ FSM_ZONE |
+ ISR_ZONE | */
+ ERR_ZONE |
+ 0;
+EXPORT_SYMBOL_GPL(rsi_zone_enabled);
+
+/**
+ * rsi_dbg() - This function outputs informational messages.
+ * @zone: Zone of interest for output message.
+ * @fmt: printf-style format for output message.
+ *
+ * Return: none
+ */
+void rsi_dbg(u32 zone, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (zone & rsi_zone_enabled)
+ pr_info("%pV", &vaf);
+ va_end(args);
+}
+EXPORT_SYMBOL_GPL(rsi_dbg);
+
+/**
+ * rsi_prepare_skb() - This function prepares the skb.
+ * @common: Pointer to the driver private structure.
+ * @buffer: Pointer to the packet data.
+ * @pkt_len: Length of the packet.
+ * @extended_desc: Extended descriptor.
+ *
+ * Return: Successfully skb.
+ */
+static struct sk_buff *rsi_prepare_skb(struct rsi_common *common,
+ u8 *buffer,
+ u32 pkt_len,
+ u8 extended_desc)
+{
+ struct ieee80211_tx_info *info;
+ struct skb_info *rx_params;
+ struct sk_buff *skb = NULL;
+ u8 payload_offset;
+
+ if (WARN(!pkt_len, "%s: Dummy pkt received", __func__))
+ return NULL;
+
+ if (pkt_len > (RSI_RCV_BUFFER_LEN * 4)) {
+ rsi_dbg(ERR_ZONE, "%s: Pkt size > max rx buf size %d\n",
+ __func__, pkt_len);
+ pkt_len = RSI_RCV_BUFFER_LEN * 4;
+ }
+
+ pkt_len -= extended_desc;
+ skb = dev_alloc_skb(pkt_len + FRAME_DESC_SZ);
+ if (skb == NULL)
+ return NULL;
+
+ payload_offset = (extended_desc + FRAME_DESC_SZ);
+ skb_put(skb, pkt_len);
+ memcpy((skb->data), (buffer + payload_offset), skb->len);
+
+ info = IEEE80211_SKB_CB(skb);
+ rx_params = (struct skb_info *)info->driver_data;
+ rx_params->rssi = rsi_get_rssi(buffer);
+ rx_params->channel = rsi_get_connected_channel(common->priv);
+
+ return skb;
+}
+
+/**
+ * rsi_read_pkt() - This function reads frames from the card.
+ * @common: Pointer to the driver private structure.
+ * @rcv_pkt_len: Received pkt length. In case of USB it is 0.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len)
+{
+ u8 *frame_desc = NULL, extended_desc = 0;
+ u32 index, length = 0, queueno = 0;
+ u16 actual_length = 0, offset;
+ struct sk_buff *skb = NULL;
+
+ index = 0;
+ do {
+ frame_desc = &common->rx_data_pkt[index];
+ actual_length = *(u16 *)&frame_desc[0];
+ offset = *(u16 *)&frame_desc[2];
+
+ queueno = rsi_get_queueno(frame_desc, offset);
+ length = rsi_get_length(frame_desc, offset);
+ extended_desc = rsi_get_extended_desc(frame_desc, offset);
+
+ switch (queueno) {
+ case RSI_WIFI_DATA_Q:
+ skb = rsi_prepare_skb(common,
+ (frame_desc + offset),
+ length,
+ extended_desc);
+ if (skb == NULL)
+ goto fail;
+
+ rsi_indicate_pkt_to_os(common, skb);
+ break;
+
+ case RSI_WIFI_MGMT_Q:
+ rsi_mgmt_pkt_recv(common, (frame_desc + offset));
+ break;
+
+ default:
+ rsi_dbg(ERR_ZONE, "%s: pkt from invalid queue: %d\n",
+ __func__, queueno);
+ goto fail;
+ }
+
+ index += actual_length;
+ rcv_pkt_len -= actual_length;
+ } while (rcv_pkt_len > 0);
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(rsi_read_pkt);
+
+/**
+ * rsi_tx_scheduler_thread() - This function is a kernel thread to send the
+ * packets to the device.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: None.
+ */
+static void rsi_tx_scheduler_thread(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ u32 timeout = EVENT_WAIT_FOREVER;
+
+ do {
+ if (adapter->determine_event_timeout)
+ timeout = adapter->determine_event_timeout(adapter);
+ rsi_wait_event(&common->tx_thread.event, timeout);
+ rsi_reset_event(&common->tx_thread.event);
+
+ if (common->init_done)
+ rsi_core_qos_processor(common);
+ } while (atomic_read(&common->tx_thread.thread_done) == 0);
+ complete_and_exit(&common->tx_thread.completion, 0);
+}
+
+/**
+ * rsi_91x_init() - This function initializes os interface operations.
+ * @void: Void.
+ *
+ * Return: Pointer to the adapter structure on success, NULL on failure .
+ */
+struct rsi_hw *rsi_91x_init(void)
+{
+ struct rsi_hw *adapter = NULL;
+ struct rsi_common *common = NULL;
+ u8 ii = 0;
+
+ adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
+ if (!adapter)
+ return NULL;
+
+ adapter->priv = kzalloc(sizeof(*common), GFP_KERNEL);
+ if (adapter->priv == NULL) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of memory\n",
+ __func__);
+ kfree(adapter);
+ return NULL;
+ } else {
+ common = adapter->priv;
+ common->priv = adapter;
+ }
+
+ for (ii = 0; ii < NUM_SOFT_QUEUES; ii++)
+ skb_queue_head_init(&common->tx_queue[ii]);
+
+ rsi_init_event(&common->tx_thread.event);
+ mutex_init(&common->mutex);
+ mutex_init(&common->tx_rxlock);
+
+ if (rsi_create_kthread(common,
+ &common->tx_thread,
+ rsi_tx_scheduler_thread,
+ "Tx-Thread")) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__);
+ goto err;
+ }
+
+ common->init_done = true;
+ return adapter;
+
+err:
+ kfree(common);
+ kfree(adapter);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(rsi_91x_init);
+
+/**
+ * rsi_91x_deinit() - This function de-intializes os intf operations.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: None.
+ */
+void rsi_91x_deinit(struct rsi_hw *adapter)
+{
+ struct rsi_common *common = adapter->priv;
+ u8 ii;
+
+ rsi_dbg(INFO_ZONE, "%s: Performing deinit os ops\n", __func__);
+
+ rsi_kill_thread(&common->tx_thread);
+
+ for (ii = 0; ii < NUM_SOFT_QUEUES; ii++)
+ skb_queue_purge(&common->tx_queue[ii]);
+
+ common->init_done = false;
+
+ kfree(common);
+ kfree(adapter->rsi_dev);
+ kfree(adapter);
+}
+EXPORT_SYMBOL_GPL(rsi_91x_deinit);
+
+/**
+ * rsi_91x_hal_module_init() - This function is invoked when the module is
+ * loaded into the kernel.
+ * It registers the client driver.
+ * @void: Void.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_91x_hal_module_init(void)
+{
+ rsi_dbg(INIT_ZONE, "%s: Module init called\n", __func__);
+ return 0;
+}
+
+/**
+ * rsi_91x_hal_module_exit() - This function is called at the time of
+ * removing/unloading the module.
+ * It unregisters the client driver.
+ * @void: Void.
+ *
+ * Return: None.
+ */
+static void rsi_91x_hal_module_exit(void)
+{
+ rsi_dbg(INIT_ZONE, "%s: Module exit called\n", __func__);
+}
+
+module_init(rsi_91x_hal_module_init);
+module_exit(rsi_91x_hal_module_exit);
+MODULE_AUTHOR("Redpine Signals Inc");
+MODULE_DESCRIPTION("Station driver for RSI 91x devices");
+MODULE_SUPPORTED_DEVICE("RSI-91x");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
new file mode 100644
index 000000000000..2361a6849ad7
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
@@ -0,0 +1,1304 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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/etherdevice.h>
+#include "rsi_mgmt.h"
+#include "rsi_common.h"
+
+static struct bootup_params boot_params_20 = {
+ .magic_number = cpu_to_le16(0x5aa5),
+ .crystal_good_time = 0x0,
+ .valid = cpu_to_le32(VALID_20),
+ .reserved_for_valids = 0x0,
+ .bootup_mode_info = 0x0,
+ .digital_loop_back_params = 0x0,
+ .rtls_timestamp_en = 0x0,
+ .host_spi_intr_cfg = 0x0,
+ .device_clk_info = {{
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)|
+ (TA_PLL_M_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)|
+ (PLL960_N_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = cpu_to_le16(BIT(3)),
+ .bbp_lmac_clk_reg_val = cpu_to_le16(0x121),
+ .umac_clock_reg_config = 0x0,
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ },
+ {
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)|
+ (TA_PLL_M_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)|
+ (PLL960_N_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = 0x0,
+ .bbp_lmac_clk_reg_val = 0x0,
+ .umac_clock_reg_config = 0x0,
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ },
+ {
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)|
+ (TA_PLL_M_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)|
+ (PLL960_N_VAL_20)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = 0x0,
+ .bbp_lmac_clk_reg_val = 0x0,
+ .umac_clock_reg_config = 0x0,
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ } },
+ .buckboost_wakeup_cnt = 0x0,
+ .pmu_wakeup_wait = 0x0,
+ .shutdown_wait_time = 0x0,
+ .pmu_slp_clkout_sel = 0x0,
+ .wdt_prog_value = 0x0,
+ .wdt_soc_rst_delay = 0x0,
+ .dcdc_operation_mode = 0x0,
+ .soc_reset_wait_cnt = 0x0
+};
+
+static struct bootup_params boot_params_40 = {
+ .magic_number = cpu_to_le16(0x5aa5),
+ .crystal_good_time = 0x0,
+ .valid = cpu_to_le32(VALID_40),
+ .reserved_for_valids = 0x0,
+ .bootup_mode_info = 0x0,
+ .digital_loop_back_params = 0x0,
+ .rtls_timestamp_en = 0x0,
+ .host_spi_intr_cfg = 0x0,
+ .device_clk_info = {{
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)|
+ (TA_PLL_M_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)|
+ (PLL960_N_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = cpu_to_le16(0x09),
+ .bbp_lmac_clk_reg_val = cpu_to_le16(0x1121),
+ .umac_clock_reg_config = cpu_to_le16(0x48),
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ },
+ {
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)|
+ (TA_PLL_M_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)|
+ (PLL960_N_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = 0x0,
+ .bbp_lmac_clk_reg_val = 0x0,
+ .umac_clock_reg_config = 0x0,
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ },
+ {
+ .pll_config_g = {
+ .tapll_info_g = {
+ .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)|
+ (TA_PLL_M_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40),
+ },
+ .pll960_info_g = {
+ .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)|
+ (PLL960_N_VAL_40)),
+ .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40),
+ .pll_reg_3 = 0x0,
+ },
+ .afepll_info_g = {
+ .pll_reg = cpu_to_le16(0x9f0),
+ }
+ },
+ .switch_clk_g = {
+ .switch_clk_info = 0x0,
+ .bbp_lmac_clk_reg_val = 0x0,
+ .umac_clock_reg_config = 0x0,
+ .qspi_uart_clock_reg_config = 0x0
+ }
+ } },
+ .buckboost_wakeup_cnt = 0x0,
+ .pmu_wakeup_wait = 0x0,
+ .shutdown_wait_time = 0x0,
+ .pmu_slp_clkout_sel = 0x0,
+ .wdt_prog_value = 0x0,
+ .wdt_soc_rst_delay = 0x0,
+ .dcdc_operation_mode = 0x0,
+ .soc_reset_wait_cnt = 0x0
+};
+
+static u16 mcs[] = {13, 26, 39, 52, 78, 104, 117, 130};
+
+/**
+ * rsi_set_default_parameters() - This function sets default parameters.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: none
+ */
+static void rsi_set_default_parameters(struct rsi_common *common)
+{
+ common->band = IEEE80211_BAND_2GHZ;
+ common->channel_width = BW_20MHZ;
+ common->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
+ common->channel = 1;
+ common->min_rate = 0xffff;
+ common->fsm_state = FSM_CARD_NOT_READY;
+ common->iface_down = true;
+}
+
+/**
+ * rsi_set_contention_vals() - This function sets the contention values for the
+ * backoff procedure.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: None.
+ */
+static void rsi_set_contention_vals(struct rsi_common *common)
+{
+ u8 ii = 0;
+
+ for (; ii < NUM_EDCA_QUEUES; ii++) {
+ common->tx_qinfo[ii].wme_params =
+ (((common->edca_params[ii].cw_min / 2) +
+ (common->edca_params[ii].aifs)) *
+ WMM_SHORT_SLOT_TIME + SIFS_DURATION);
+ common->tx_qinfo[ii].weight = common->tx_qinfo[ii].wme_params;
+ common->tx_qinfo[ii].pkt_contended = 0;
+ }
+}
+
+/**
+ * rsi_send_internal_mgmt_frame() - This function sends management frames to
+ * firmware.Also schedules packet to queue
+ * for transmission.
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_send_internal_mgmt_frame(struct rsi_common *common,
+ struct sk_buff *skb)
+{
+ struct skb_info *tx_params;
+
+ if (skb == NULL) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to allocate skb\n", __func__);
+ return -ENOMEM;
+ }
+ tx_params = (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data;
+ tx_params->flags |= INTERNAL_MGMT_PKT;
+ skb_queue_tail(&common->tx_queue[MGMT_SOFT_Q], skb);
+ rsi_set_event(&common->tx_thread.event);
+ return 0;
+}
+
+/**
+ * rsi_load_radio_caps() - This function is used to send radio capabilities
+ * values to firmware.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding negative error code on failure.
+ */
+static int rsi_load_radio_caps(struct rsi_common *common)
+{
+ struct rsi_radio_caps *radio_caps;
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_hw *hw = adapter->hw;
+ u16 inx = 0;
+ u8 ii;
+ u8 radio_id = 0;
+ u16 gc[20] = {0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0};
+ struct ieee80211_conf *conf = &hw->conf;
+ struct sk_buff *skb;
+
+ rsi_dbg(INFO_ZONE, "%s: Sending rate symbol req frame\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct rsi_radio_caps));
+
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, sizeof(struct rsi_radio_caps));
+ radio_caps = (struct rsi_radio_caps *)skb->data;
+
+ radio_caps->desc_word[1] = cpu_to_le16(RADIO_CAPABILITIES);
+ radio_caps->desc_word[4] = cpu_to_le16(RSI_RF_TYPE << 8);
+
+ if (common->channel_width == BW_40MHZ) {
+ radio_caps->desc_word[7] |= cpu_to_le16(RSI_LMAC_CLOCK_80MHZ);
+ radio_caps->desc_word[7] |= cpu_to_le16(RSI_ENABLE_40MHZ);
+ if (common->channel_width) {
+ radio_caps->desc_word[5] =
+ cpu_to_le16(common->channel_width << 12);
+ radio_caps->desc_word[5] |= cpu_to_le16(FULL40M_ENABLE);
+ }
+
+ if (conf_is_ht40_minus(conf)) {
+ radio_caps->desc_word[5] = 0;
+ radio_caps->desc_word[5] |=
+ cpu_to_le16(LOWER_20_ENABLE);
+ radio_caps->desc_word[5] |=
+ cpu_to_le16(LOWER_20_ENABLE >> 12);
+ }
+
+ if (conf_is_ht40_plus(conf)) {
+ radio_caps->desc_word[5] = 0;
+ radio_caps->desc_word[5] |=
+ cpu_to_le16(UPPER_20_ENABLE);
+ radio_caps->desc_word[5] |=
+ cpu_to_le16(UPPER_20_ENABLE >> 12);
+ }
+ }
+
+ radio_caps->desc_word[7] |= cpu_to_le16(radio_id << 8);
+
+ for (ii = 0; ii < MAX_HW_QUEUES; ii++) {
+ radio_caps->qos_params[ii].cont_win_min_q = cpu_to_le16(3);
+ radio_caps->qos_params[ii].cont_win_max_q = cpu_to_le16(0x3f);
+ radio_caps->qos_params[ii].aifsn_val_q = cpu_to_le16(2);
+ radio_caps->qos_params[ii].txop_q = 0;
+ }
+
+ for (ii = 0; ii < MAX_HW_QUEUES - 4; ii++) {
+ radio_caps->qos_params[ii].cont_win_min_q =
+ cpu_to_le16(common->edca_params[ii].cw_min);
+ radio_caps->qos_params[ii].cont_win_max_q =
+ cpu_to_le16(common->edca_params[ii].cw_max);
+ radio_caps->qos_params[ii].aifsn_val_q =
+ cpu_to_le16((common->edca_params[ii].aifs) << 8);
+ radio_caps->qos_params[ii].txop_q =
+ cpu_to_le16(common->edca_params[ii].txop);
+ }
+
+ memcpy(&common->rate_pwr[0], &gc[0], 40);
+ for (ii = 0; ii < 20; ii++)
+ radio_caps->gcpd_per_rate[inx++] =
+ cpu_to_le16(common->rate_pwr[ii] & 0x00FF);
+
+ radio_caps->desc_word[0] = cpu_to_le16((sizeof(struct rsi_radio_caps) -
+ FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+
+
+ skb_put(skb, (sizeof(struct rsi_radio_caps)));
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_mgmt_pkt_to_core() - This function is the entry point for Mgmt module.
+ * @common: Pointer to the driver private structure.
+ * @msg: Pointer to received packet.
+ * @msg_len: Length of the recieved packet.
+ * @type: Type of recieved packet.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_mgmt_pkt_to_core(struct rsi_common *common,
+ u8 *msg,
+ s32 msg_len,
+ u8 type)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_tx_info *info;
+ struct skb_info *rx_params;
+ u8 pad_bytes = msg[4];
+ u8 pkt_recv;
+ struct sk_buff *skb;
+ char *buffer;
+
+ if (type == RX_DOT11_MGMT) {
+ if (!adapter->sc_nvifs)
+ return -ENOLINK;
+
+ msg_len -= pad_bytes;
+ if ((msg_len <= 0) || (!msg)) {
+ rsi_dbg(MGMT_RX_ZONE,
+ "%s: Invalid rx msg of len = %d\n",
+ __func__, msg_len);
+ return -EINVAL;
+ }
+
+ skb = dev_alloc_skb(msg_len);
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to allocate skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ buffer = skb_put(skb, msg_len);
+
+ memcpy(buffer,
+ (u8 *)(msg + FRAME_DESC_SZ + pad_bytes),
+ msg_len);
+
+ pkt_recv = buffer[0];
+
+ info = IEEE80211_SKB_CB(skb);
+ rx_params = (struct skb_info *)info->driver_data;
+ rx_params->rssi = rsi_get_rssi(msg);
+ rx_params->channel = rsi_get_channel(msg);
+ rsi_indicate_pkt_to_os(common, skb);
+ } else {
+ rsi_dbg(MGMT_TX_ZONE, "%s: Internal Packet\n", __func__);
+ }
+
+ return 0;
+}
+
+/**
+ * rsi_hal_send_sta_notify_frame() - This function sends the station notify
+ * frame to firmware.
+ * @common: Pointer to the driver private structure.
+ * @opmode: Operating mode of device.
+ * @notify_event: Notification about station connection.
+ * @bssid: bssid.
+ * @qos_enable: Qos is enabled.
+ * @aid: Aid (unique for all STA).
+ *
+ * Return: status: 0 on success, corresponding negative error code on failure.
+ */
+static int rsi_hal_send_sta_notify_frame(struct rsi_common *common,
+ u8 opmode,
+ u8 notify_event,
+ const unsigned char *bssid,
+ u8 qos_enable,
+ u16 aid)
+{
+ struct sk_buff *skb = NULL;
+ struct rsi_peer_notify *peer_notify;
+ u16 vap_id = 0;
+ int status;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending sta notify frame\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct rsi_peer_notify));
+
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, sizeof(struct rsi_peer_notify));
+ peer_notify = (struct rsi_peer_notify *)skb->data;
+
+ peer_notify->command = cpu_to_le16(opmode << 1);
+
+ switch (notify_event) {
+ case STA_CONNECTED:
+ peer_notify->command |= cpu_to_le16(RSI_ADD_PEER);
+ break;
+ case STA_DISCONNECTED:
+ peer_notify->command |= cpu_to_le16(RSI_DELETE_PEER);
+ break;
+ default:
+ break;
+ }
+
+ peer_notify->command |= cpu_to_le16((aid & 0xfff) << 4);
+ ether_addr_copy(peer_notify->mac_addr, bssid);
+
+ peer_notify->sta_flags = cpu_to_le32((qos_enable) ? 1 : 0);
+
+ peer_notify->desc_word[0] =
+ cpu_to_le16((sizeof(struct rsi_peer_notify) - FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+ peer_notify->desc_word[1] = cpu_to_le16(PEER_NOTIFY);
+ peer_notify->desc_word[7] |= cpu_to_le16(vap_id << 8);
+
+ skb_put(skb, sizeof(struct rsi_peer_notify));
+
+ status = rsi_send_internal_mgmt_frame(common, skb);
+
+ if (!status && qos_enable) {
+ rsi_set_contention_vals(common);
+ status = rsi_load_radio_caps(common);
+ }
+ return status;
+}
+
+/**
+ * rsi_send_aggregation_params_frame() - This function sends the ampdu
+ * indication frame to firmware.
+ * @common: Pointer to the driver private structure.
+ * @tid: traffic identifier.
+ * @ssn: ssn.
+ * @buf_size: buffer size.
+ * @event: notification about station connection.
+ *
+ * Return: 0 on success, corresponding negative error code on failure.
+ */
+int rsi_send_aggregation_params_frame(struct rsi_common *common,
+ u16 tid,
+ u16 ssn,
+ u8 buf_size,
+ u8 event)
+{
+ struct sk_buff *skb = NULL;
+ struct rsi_mac_frame *mgmt_frame;
+ u8 peer_id = 0;
+
+ skb = dev_alloc_skb(FRAME_DESC_SZ);
+
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending AMPDU indication frame\n", __func__);
+
+ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+ mgmt_frame->desc_word[1] = cpu_to_le16(AMPDU_IND);
+
+ if (event == STA_TX_ADDBA_DONE) {
+ mgmt_frame->desc_word[4] = cpu_to_le16(ssn);
+ mgmt_frame->desc_word[5] = cpu_to_le16(buf_size);
+ mgmt_frame->desc_word[7] =
+ cpu_to_le16((tid | (START_AMPDU_AGGR << 4) | (peer_id << 8)));
+ } else if (event == STA_RX_ADDBA_DONE) {
+ mgmt_frame->desc_word[4] = cpu_to_le16(ssn);
+ mgmt_frame->desc_word[7] = cpu_to_le16(tid |
+ (START_AMPDU_AGGR << 4) |
+ (RX_BA_INDICATION << 5) |
+ (peer_id << 8));
+ } else if (event == STA_TX_DELBA) {
+ mgmt_frame->desc_word[7] = cpu_to_le16(tid |
+ (STOP_AMPDU_AGGR << 4) |
+ (peer_id << 8));
+ } else if (event == STA_RX_DELBA) {
+ mgmt_frame->desc_word[7] = cpu_to_le16(tid |
+ (STOP_AMPDU_AGGR << 4) |
+ (RX_BA_INDICATION << 5) |
+ (peer_id << 8));
+ }
+
+ skb_put(skb, FRAME_DESC_SZ);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_program_bb_rf() - This function starts base band and RF programming.
+ * This is called after initial configurations are done.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding negative error code on failure.
+ */
+static int rsi_program_bb_rf(struct rsi_common *common)
+{
+ struct sk_buff *skb;
+ struct rsi_mac_frame *mgmt_frame;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending program BB/RF frame\n", __func__);
+
+ skb = dev_alloc_skb(FRAME_DESC_SZ);
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+ mgmt_frame->desc_word[1] = cpu_to_le16(BBP_PROG_IN_TA);
+ mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint << 8);
+
+ if (common->rf_reset) {
+ mgmt_frame->desc_word[7] = cpu_to_le16(RF_RESET_ENABLE);
+ rsi_dbg(MGMT_TX_ZONE, "%s: ===> RF RESET REQUEST SENT <===\n",
+ __func__);
+ common->rf_reset = 0;
+ }
+ common->bb_rf_prog_count = 1;
+ mgmt_frame->desc_word[7] |= cpu_to_le16(PUT_BBP_RESET |
+ BBP_REG_WRITE | (RSI_RF_TYPE << 4));
+ skb_put(skb, FRAME_DESC_SZ);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_set_vap_capabilities() - This function send vap capability to firmware.
+ * @common: Pointer to the driver private structure.
+ * @opmode: Operating mode of device.
+ *
+ * Return: 0 on success, corresponding negative error code on failure.
+ */
+int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode)
+{
+ struct sk_buff *skb = NULL;
+ struct rsi_vap_caps *vap_caps;
+ u16 vap_id = 0;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending VAP capabilities frame\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct rsi_vap_caps));
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, sizeof(struct rsi_vap_caps));
+ vap_caps = (struct rsi_vap_caps *)skb->data;
+
+ vap_caps->desc_word[0] = cpu_to_le16((sizeof(struct rsi_vap_caps) -
+ FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+ vap_caps->desc_word[1] = cpu_to_le16(VAP_CAPABILITIES);
+ vap_caps->desc_word[4] = cpu_to_le16(mode |
+ (common->channel_width << 8));
+ vap_caps->desc_word[7] = cpu_to_le16((vap_id << 8) |
+ (common->mac_id << 4) |
+ common->radio_id);
+
+ memcpy(vap_caps->mac_addr, common->mac_addr, IEEE80211_ADDR_LEN);
+ vap_caps->keep_alive_period = cpu_to_le16(90);
+ vap_caps->frag_threshold = cpu_to_le16(IEEE80211_MAX_FRAG_THRESHOLD);
+
+ vap_caps->rts_threshold = cpu_to_le16(common->rts_threshold);
+ vap_caps->default_mgmt_rate = 0;
+ if (conf_is_ht40(&common->priv->hw->conf)) {
+ vap_caps->default_ctrl_rate =
+ cpu_to_le32(RSI_RATE_6 | FULL40M_ENABLE << 16);
+ } else {
+ vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_6);
+ }
+ vap_caps->default_data_rate = 0;
+ vap_caps->beacon_interval = cpu_to_le16(200);
+ vap_caps->dtim_period = cpu_to_le16(4);
+
+ skb_put(skb, sizeof(*vap_caps));
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_hal_load_key() - This function is used to load keys within the firmware.
+ * @common: Pointer to the driver private structure.
+ * @data: Pointer to the key data.
+ * @key_len: Key length to be loaded.
+ * @key_type: Type of key: GROUP/PAIRWISE.
+ * @key_id: Key index.
+ * @cipher: Type of cipher used.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_hal_load_key(struct rsi_common *common,
+ u8 *data,
+ u16 key_len,
+ u8 key_type,
+ u8 key_id,
+ u32 cipher)
+{
+ struct sk_buff *skb = NULL;
+ struct rsi_set_key *set_key;
+ u16 key_descriptor = 0;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending load key frame\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct rsi_set_key));
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, sizeof(struct rsi_set_key));
+ set_key = (struct rsi_set_key *)skb->data;
+
+ if ((cipher == WLAN_CIPHER_SUITE_WEP40) ||
+ (cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ key_len += 1;
+ key_descriptor |= BIT(2);
+ if (key_len >= 13)
+ key_descriptor |= BIT(3);
+ } else if (cipher != KEY_TYPE_CLEAR) {
+ key_descriptor |= BIT(4);
+ if (key_type == RSI_PAIRWISE_KEY)
+ key_id = 0;
+ if (cipher == WLAN_CIPHER_SUITE_TKIP)
+ key_descriptor |= BIT(5);
+ }
+ key_descriptor |= (key_type | BIT(13) | (key_id << 14));
+
+ set_key->desc_word[0] = cpu_to_le16((sizeof(struct rsi_set_key) -
+ FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+ set_key->desc_word[1] = cpu_to_le16(SET_KEY_REQ);
+ set_key->desc_word[4] = cpu_to_le16(key_descriptor);
+
+ if ((cipher == WLAN_CIPHER_SUITE_WEP40) ||
+ (cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ memcpy(&set_key->key[key_id][1],
+ data,
+ key_len * 2);
+ } else {
+ memcpy(&set_key->key[0][0], data, key_len);
+ }
+
+ memcpy(set_key->tx_mic_key, &data[16], 8);
+ memcpy(set_key->rx_mic_key, &data[24], 8);
+
+ skb_put(skb, sizeof(struct rsi_set_key));
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/*
+ * rsi_load_bootup_params() - This function send bootup params to the firmware.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding error code on failure.
+ */
+static u8 rsi_load_bootup_params(struct rsi_common *common)
+{
+ struct sk_buff *skb;
+ struct rsi_boot_params *boot_params;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending boot params frame\n", __func__);
+ skb = dev_alloc_skb(sizeof(struct rsi_boot_params));
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, sizeof(struct rsi_boot_params));
+ boot_params = (struct rsi_boot_params *)skb->data;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s:\n", __func__);
+
+ if (common->channel_width == BW_40MHZ) {
+ memcpy(&boot_params->bootup_params,
+ &boot_params_40,
+ sizeof(struct bootup_params));
+ rsi_dbg(MGMT_TX_ZONE, "%s: Packet 40MHZ <=== %d\n", __func__,
+ UMAC_CLK_40BW);
+ boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_40BW);
+ } else {
+ memcpy(&boot_params->bootup_params,
+ &boot_params_20,
+ sizeof(struct bootup_params));
+ if (boot_params_20.valid != cpu_to_le32(VALID_20)) {
+ boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_20BW);
+ rsi_dbg(MGMT_TX_ZONE,
+ "%s: Packet 20MHZ <=== %d\n", __func__,
+ UMAC_CLK_20BW);
+ } else {
+ boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_40MHZ);
+ rsi_dbg(MGMT_TX_ZONE,
+ "%s: Packet 20MHZ <=== %d\n", __func__,
+ UMAC_CLK_40MHZ);
+ }
+ }
+
+ /**
+ * Bit{0:11} indicates length of the Packet
+ * Bit{12:15} indicates host queue number
+ */
+ boot_params->desc_word[0] = cpu_to_le16(sizeof(struct bootup_params) |
+ (RSI_WIFI_MGMT_Q << 12));
+ boot_params->desc_word[1] = cpu_to_le16(BOOTUP_PARAMS_REQUEST);
+
+ skb_put(skb, sizeof(struct rsi_boot_params));
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_send_reset_mac() - This function prepares reset MAC request and sends an
+ * internal management frame to indicate it to firmware.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding error code on failure.
+ */
+static int rsi_send_reset_mac(struct rsi_common *common)
+{
+ struct sk_buff *skb;
+ struct rsi_mac_frame *mgmt_frame;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending reset MAC frame\n", __func__);
+
+ skb = dev_alloc_skb(FRAME_DESC_SZ);
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+ mgmt_frame->desc_word[1] = cpu_to_le16(RESET_MAC_REQ);
+ mgmt_frame->desc_word[4] = cpu_to_le16(RETRY_COUNT << 8);
+
+ skb_put(skb, FRAME_DESC_SZ);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_set_channel() - This function programs the channel.
+ * @common: Pointer to the driver private structure.
+ * @channel: Channel value to be set.
+ *
+ * Return: 0 on success, corresponding error code on failure.
+ */
+int rsi_set_channel(struct rsi_common *common, u16 channel)
+{
+ struct sk_buff *skb = NULL;
+ struct rsi_mac_frame *mgmt_frame;
+
+ rsi_dbg(MGMT_TX_ZONE,
+ "%s: Sending scan req frame\n", __func__);
+
+ skb = dev_alloc_skb(FRAME_DESC_SZ);
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+ if (common->band == IEEE80211_BAND_5GHZ) {
+ if ((channel >= 36) && (channel <= 64))
+ channel = ((channel - 32) / 4);
+ else if ((channel > 64) && (channel <= 140))
+ channel = ((channel - 102) / 4) + 8;
+ else if (channel >= 149)
+ channel = ((channel - 151) / 4) + 18;
+ else
+ return -EINVAL;
+ } else {
+ if (channel > 14) {
+ rsi_dbg(ERR_ZONE, "%s: Invalid chno %d, band = %d\n",
+ __func__, channel, common->band);
+ return -EINVAL;
+ }
+ }
+
+ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+ mgmt_frame->desc_word[1] = cpu_to_le16(SCAN_REQUEST);
+ mgmt_frame->desc_word[4] = cpu_to_le16(channel);
+
+ mgmt_frame->desc_word[7] = cpu_to_le16(PUT_BBP_RESET |
+ BBP_REG_WRITE |
+ (RSI_RF_TYPE << 4));
+
+ mgmt_frame->desc_word[5] = cpu_to_le16(0x01);
+
+ if (common->channel_width == BW_40MHZ)
+ mgmt_frame->desc_word[5] |= cpu_to_le16(0x1 << 8);
+
+ common->channel = channel;
+
+ skb_put(skb, FRAME_DESC_SZ);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_compare() - This function is used to compare two integers
+ * @a: pointer to the first integer
+ * @b: pointer to the second integer
+ *
+ * Return: 0 if both are equal, -1 if the first is smaller, else 1
+ */
+static int rsi_compare(const void *a, const void *b)
+{
+ u16 _a = *(const u16 *)(a);
+ u16 _b = *(const u16 *)(b);
+
+ if (_a > _b)
+ return -1;
+
+ if (_a < _b)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * rsi_map_rates() - This function is used to map selected rates to hw rates.
+ * @rate: The standard rate to be mapped.
+ * @offset: Offset that will be returned.
+ *
+ * Return: 0 if it is a mcs rate, else 1
+ */
+static bool rsi_map_rates(u16 rate, int *offset)
+{
+ int kk;
+ for (kk = 0; kk < ARRAY_SIZE(rsi_mcsrates); kk++) {
+ if (rate == mcs[kk]) {
+ *offset = kk;
+ return false;
+ }
+ }
+
+ for (kk = 0; kk < ARRAY_SIZE(rsi_rates); kk++) {
+ if (rate == rsi_rates[kk].bitrate / 5) {
+ *offset = kk;
+ break;
+ }
+ }
+ return true;
+}
+
+/**
+ * rsi_send_auto_rate_request() - This function is to set rates for connection
+ * and send autorate request to firmware.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding error code on failure.
+ */
+static int rsi_send_auto_rate_request(struct rsi_common *common)
+{
+ struct sk_buff *skb;
+ struct rsi_auto_rate *auto_rate;
+ int ii = 0, jj = 0, kk = 0;
+ struct ieee80211_hw *hw = common->priv->hw;
+ u8 band = hw->conf.chandef.chan->band;
+ u8 num_supported_rates = 0;
+ u8 rate_offset = 0;
+ u32 rate_bitmap = common->bitrate_mask[band];
+
+ u16 *selected_rates, min_rate;
+
+ skb = dev_alloc_skb(sizeof(struct rsi_auto_rate));
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ selected_rates = kmalloc(2 * RSI_TBL_SZ, GFP_KERNEL);
+ if (!selected_rates) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of mem\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ 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;
+
+ auto_rate->aarf_rssi = cpu_to_le16(((u16)3 << 6) | (u16)(18 & 0x3f));
+ auto_rate->collision_tolerance = cpu_to_le16(3);
+ auto_rate->failure_limit = cpu_to_le16(3);
+ auto_rate->initial_boundary = cpu_to_le16(3);
+ auto_rate->max_threshold_limt = cpu_to_le16(27);
+
+ auto_rate->desc_word[1] = cpu_to_le16(AUTO_RATE_IND);
+
+ if (common->channel_width == BW_40MHZ)
+ auto_rate->desc_word[7] |= cpu_to_le16(1);
+
+ if (band == IEEE80211_BAND_2GHZ)
+ min_rate = STD_RATE_01;
+ else
+ min_rate = STD_RATE_06;
+
+ for (ii = 0, jj = 0; ii < ARRAY_SIZE(rsi_rates); ii++) {
+ if (rate_bitmap & BIT(ii)) {
+ selected_rates[jj++] = (rsi_rates[ii].bitrate / 5);
+ rate_offset++;
+ }
+ }
+ num_supported_rates = jj;
+
+ if (common->vif_info[0].is_ht) {
+ for (ii = 0; ii < ARRAY_SIZE(mcs); ii++)
+ selected_rates[jj++] = mcs[ii];
+ num_supported_rates += ARRAY_SIZE(mcs);
+ rate_offset += ARRAY_SIZE(mcs);
+ }
+
+ if (rate_offset < (RSI_TBL_SZ / 2) - 1) {
+ for (ii = jj; ii < (RSI_TBL_SZ / 2); ii++) {
+ selected_rates[jj++] = min_rate;
+ rate_offset++;
+ }
+ }
+
+ sort(selected_rates, jj, sizeof(u16), &rsi_compare, NULL);
+
+ /* mapping the rates to RSI rates */
+ for (ii = 0; ii < jj; ii++) {
+ if (rsi_map_rates(selected_rates[ii], &kk)) {
+ auto_rate->supported_rates[ii] =
+ cpu_to_le16(rsi_rates[kk].hw_value);
+ } else {
+ auto_rate->supported_rates[ii] =
+ cpu_to_le16(rsi_mcsrates[kk]);
+ }
+ }
+
+ /* loading HT rates in the bottom half of the auto rate table */
+ if (common->vif_info[0].is_ht) {
+ if (common->vif_info[0].sgi)
+ auto_rate->supported_rates[rate_offset++] =
+ cpu_to_le16(RSI_RATE_MCS7_SG);
+
+ for (ii = rate_offset, kk = ARRAY_SIZE(rsi_mcsrates) - 1;
+ ii < rate_offset + 2 * ARRAY_SIZE(rsi_mcsrates); ii++) {
+ if (common->vif_info[0].sgi)
+ auto_rate->supported_rates[ii++] =
+ cpu_to_le16(rsi_mcsrates[kk] | BIT(9));
+ auto_rate->supported_rates[ii] =
+ cpu_to_le16(rsi_mcsrates[kk--]);
+ }
+
+ for (; ii < RSI_TBL_SZ; ii++) {
+ auto_rate->supported_rates[ii] =
+ cpu_to_le16(rsi_mcsrates[0]);
+ }
+ }
+
+ auto_rate->num_supported_rates = cpu_to_le16(num_supported_rates * 2);
+ auto_rate->moderate_rate_inx = cpu_to_le16(num_supported_rates / 2);
+ auto_rate->desc_word[7] |= cpu_to_le16(0 << 8);
+ num_supported_rates *= 2;
+
+ auto_rate->desc_word[0] = cpu_to_le16((sizeof(*auto_rate) -
+ FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+
+ skb_put(skb,
+ sizeof(struct rsi_auto_rate));
+ kfree(selected_rates);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_inform_bss_status() - This function informs about bss status with the
+ * help of sta notify params by sending an internal
+ * management frame to firmware.
+ * @common: Pointer to the driver private structure.
+ * @status: Bss status type.
+ * @bssid: Bssid.
+ * @qos_enable: Qos is enabled.
+ * @aid: Aid (unique for all STAs).
+ *
+ * Return: None.
+ */
+void rsi_inform_bss_status(struct rsi_common *common,
+ u8 status,
+ const unsigned char *bssid,
+ u8 qos_enable,
+ u16 aid)
+{
+ if (status) {
+ rsi_hal_send_sta_notify_frame(common,
+ NL80211_IFTYPE_STATION,
+ STA_CONNECTED,
+ bssid,
+ qos_enable,
+ aid);
+ if (common->min_rate == 0xffff)
+ rsi_send_auto_rate_request(common);
+ } else {
+ rsi_hal_send_sta_notify_frame(common,
+ NL80211_IFTYPE_STATION,
+ STA_DISCONNECTED,
+ bssid,
+ qos_enable,
+ aid);
+ }
+}
+
+/**
+ * rsi_eeprom_read() - This function sends a frame to read the mac address
+ * from the eeprom.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_eeprom_read(struct rsi_common *common)
+{
+ struct rsi_mac_frame *mgmt_frame;
+ struct sk_buff *skb;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending EEPROM read req frame\n", __func__);
+
+ skb = dev_alloc_skb(FRAME_DESC_SZ);
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+ /* FrameType */
+ mgmt_frame->desc_word[1] = cpu_to_le16(EEPROM_READ_TYPE);
+ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+ /* Number of bytes to read */
+ mgmt_frame->desc_word[3] = cpu_to_le16(ETH_ALEN +
+ WLAN_MAC_MAGIC_WORD_LEN +
+ WLAN_HOST_MODE_LEN +
+ WLAN_FW_VERSION_LEN);
+ /* Address to read */
+ mgmt_frame->desc_word[4] = cpu_to_le16(WLAN_MAC_EEPROM_ADDR);
+
+ skb_put(skb, FRAME_DESC_SZ);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/**
+ * rsi_handle_ta_confirm_type() - This function handles the confirm frames.
+ * @common: Pointer to the driver private structure.
+ * @msg: Pointer to received packet.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_handle_ta_confirm_type(struct rsi_common *common,
+ u8 *msg)
+{
+ u8 sub_type = (msg[15] & 0xff);
+
+ switch (sub_type) {
+ case BOOTUP_PARAMS_REQUEST:
+ rsi_dbg(FSM_ZONE, "%s: Boot up params confirm received\n",
+ __func__);
+ if (common->fsm_state == FSM_BOOT_PARAMS_SENT) {
+ if (rsi_eeprom_read(common)) {
+ common->fsm_state = FSM_CARD_NOT_READY;
+ goto out;
+ } else {
+ common->fsm_state = FSM_EEPROM_READ_MAC_ADDR;
+ }
+ } else {
+ rsi_dbg(ERR_ZONE,
+ "%s: Received bootup params cfm in %d state\n",
+ __func__, common->fsm_state);
+ return 0;
+ }
+ break;
+
+ case EEPROM_READ_TYPE:
+ if (common->fsm_state == FSM_EEPROM_READ_MAC_ADDR) {
+ if (msg[16] == MAGIC_WORD) {
+ u8 offset = (FRAME_DESC_SZ + WLAN_HOST_MODE_LEN
+ + WLAN_MAC_MAGIC_WORD_LEN);
+ memcpy(common->mac_addr,
+ &msg[offset],
+ ETH_ALEN);
+ memcpy(&common->fw_ver,
+ &msg[offset + ETH_ALEN],
+ sizeof(struct version_info));
+
+ } else {
+ common->fsm_state = FSM_CARD_NOT_READY;
+ break;
+ }
+ if (rsi_send_reset_mac(common))
+ goto out;
+ else
+ common->fsm_state = FSM_RESET_MAC_SENT;
+ } else {
+ rsi_dbg(ERR_ZONE,
+ "%s: Received eeprom mac addr in %d state\n",
+ __func__, common->fsm_state);
+ return 0;
+ }
+ break;
+
+ case RESET_MAC_REQ:
+ if (common->fsm_state == FSM_RESET_MAC_SENT) {
+ rsi_dbg(FSM_ZONE, "%s: Reset MAC cfm received\n",
+ __func__);
+
+ if (rsi_load_radio_caps(common))
+ goto out;
+ else
+ common->fsm_state = FSM_RADIO_CAPS_SENT;
+ } else {
+ rsi_dbg(ERR_ZONE,
+ "%s: Received reset mac cfm in %d state\n",
+ __func__, common->fsm_state);
+ return 0;
+ }
+ break;
+
+ case RADIO_CAPABILITIES:
+ if (common->fsm_state == FSM_RADIO_CAPS_SENT) {
+ common->rf_reset = 1;
+ if (rsi_program_bb_rf(common)) {
+ goto out;
+ } else {
+ common->fsm_state = FSM_BB_RF_PROG_SENT;
+ rsi_dbg(FSM_ZONE, "%s: Radio cap cfm received\n",
+ __func__);
+ }
+ } else {
+ rsi_dbg(ERR_ZONE,
+ "%s: Received radio caps cfm in %d state\n",
+ __func__, common->fsm_state);
+ return 0;
+ }
+ break;
+
+ case BB_PROG_VALUES_REQUEST:
+ case RF_PROG_VALUES_REQUEST:
+ case BBP_PROG_IN_TA:
+ rsi_dbg(FSM_ZONE, "%s: BB/RF cfm received\n", __func__);
+ if (common->fsm_state == FSM_BB_RF_PROG_SENT) {
+ common->bb_rf_prog_count--;
+ if (!common->bb_rf_prog_count) {
+ common->fsm_state = FSM_MAC_INIT_DONE;
+ return rsi_mac80211_attach(common);
+ }
+ } else {
+ goto out;
+ }
+ break;
+
+ default:
+ rsi_dbg(INFO_ZONE, "%s: Invalid TA confirm pkt received\n",
+ __func__);
+ break;
+ }
+ return 0;
+out:
+ rsi_dbg(ERR_ZONE, "%s: Unable to send pkt/Invalid frame received\n",
+ __func__);
+ return -EINVAL;
+}
+
+/**
+ * rsi_mgmt_pkt_recv() - This function processes the management packets
+ * recieved from the hardware.
+ * @common: Pointer to the driver private structure.
+ * @msg: Pointer to the received packet.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg)
+{
+ s32 msg_len = (le16_to_cpu(*(__le16 *)&msg[0]) & 0x0fff);
+ u16 msg_type = (msg[2]);
+
+ rsi_dbg(FSM_ZONE, "%s: Msg Len: %d, Msg Type: %4x\n",
+ __func__, msg_len, msg_type);
+
+ if (msg_type == TA_CONFIRM_TYPE) {
+ return rsi_handle_ta_confirm_type(common, msg);
+ } else if (msg_type == CARD_READY_IND) {
+ rsi_dbg(FSM_ZONE, "%s: Card ready indication received\n",
+ __func__);
+ if (common->fsm_state == FSM_CARD_NOT_READY) {
+ rsi_set_default_parameters(common);
+
+ if (rsi_load_bootup_params(common))
+ return -ENOMEM;
+ else
+ common->fsm_state = FSM_BOOT_PARAMS_SENT;
+ } else {
+ return -EINVAL;
+ }
+ } else if (msg_type == TX_STATUS_IND) {
+ if (msg[15] == PROBEREQ_CONFIRM) {
+ common->mgmt_q_block = false;
+ rsi_dbg(FSM_ZONE, "%s: Probe confirm received\n",
+ __func__);
+ }
+ } else {
+ return rsi_mgmt_pkt_to_core(common, msg, msg_len, msg_type);
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/rsi/rsi_91x_pkt.c b/drivers/net/wireless/rsi/rsi_91x_pkt.c
new file mode 100644
index 000000000000..8e48e72bae20
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_pkt.c
@@ -0,0 +1,196 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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 "rsi_mgmt.h"
+
+/**
+ * rsi_send_data_pkt() - This function sends the recieved data packet from
+ * driver to device.
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_hdr *tmp_hdr = NULL;
+ struct ieee80211_tx_info *info;
+ struct skb_info *tx_params;
+ struct ieee80211_bss_conf *bss = NULL;
+ int status = -EINVAL;
+ u8 ieee80211_size = MIN_802_11_HDR_LEN;
+ u8 extnd_size = 0;
+ __le16 *frame_desc;
+ u16 seq_num = 0;
+
+ info = IEEE80211_SKB_CB(skb);
+ bss = &info->control.vif->bss_conf;
+ tx_params = (struct skb_info *)info->driver_data;
+
+ if (!bss->assoc)
+ goto err;
+
+ tmp_hdr = (struct ieee80211_hdr *)&skb->data[0];
+ seq_num = (le16_to_cpu(tmp_hdr->seq_ctrl) >> 4);
+
+ extnd_size = ((uintptr_t)skb->data & 0x3);
+
+ if ((FRAME_DESC_SZ + extnd_size) > skb_headroom(skb)) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__);
+ status = -ENOSPC;
+ goto err;
+ }
+
+ skb_push(skb, (FRAME_DESC_SZ + extnd_size));
+ frame_desc = (__le16 *)&skb->data[0];
+ memset((u8 *)frame_desc, 0, FRAME_DESC_SZ);
+
+ if (ieee80211_is_data_qos(tmp_hdr->frame_control)) {
+ ieee80211_size += 2;
+ frame_desc[6] |= cpu_to_le16(BIT(12));
+ }
+
+ if ((!(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) &&
+ (common->secinfo.security_enable)) {
+ if (rsi_is_cipher_wep(common))
+ ieee80211_size += 4;
+ else
+ ieee80211_size += 8;
+ frame_desc[6] |= cpu_to_le16(BIT(15));
+ }
+
+ frame_desc[0] = cpu_to_le16((skb->len - FRAME_DESC_SZ) |
+ (RSI_WIFI_DATA_Q << 12));
+ frame_desc[2] = cpu_to_le16((extnd_size) | (ieee80211_size) << 8);
+
+ if (common->min_rate != 0xffff) {
+ /* Send fixed rate */
+ frame_desc[3] = cpu_to_le16(RATE_INFO_ENABLE);
+ frame_desc[4] = cpu_to_le16(common->min_rate);
+ }
+
+ frame_desc[6] |= cpu_to_le16(seq_num & 0xfff);
+ frame_desc[7] = cpu_to_le16(((tx_params->tid & 0xf) << 4) |
+ (skb->priority & 0xf) |
+ (tx_params->sta_id << 8));
+
+ status = adapter->host_intf_write_pkt(common->priv,
+ skb->data,
+ skb->len);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n",
+ __func__);
+
+err:
+ ++common->tx_stats.total_tx_pkt_freed[skb->priority];
+ rsi_indicate_tx_status(common->priv, skb, status);
+ return status;
+}
+
+/**
+ * rsi_send_mgmt_pkt() - This functions sends the received management packet
+ * from driver to device.
+ * @common: Pointer to the driver private structure.
+ * @skb: Pointer to the socket buffer structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+int rsi_send_mgmt_pkt(struct rsi_common *common,
+ struct sk_buff *skb)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_hdr *wh = NULL;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_bss_conf *bss = NULL;
+ struct skb_info *tx_params;
+ int status = -E2BIG;
+ __le16 *msg = NULL;
+ u8 extnd_size = 0;
+ u8 vap_id = 0;
+
+ info = IEEE80211_SKB_CB(skb);
+ tx_params = (struct skb_info *)info->driver_data;
+ extnd_size = ((uintptr_t)skb->data & 0x3);
+
+ if (tx_params->flags & INTERNAL_MGMT_PKT) {
+ if ((extnd_size) > skb_headroom(skb)) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__);
+ dev_kfree_skb(skb);
+ return -ENOSPC;
+ }
+ skb_push(skb, extnd_size);
+ skb->data[extnd_size + 4] = extnd_size;
+ status = adapter->host_intf_write_pkt(common->priv,
+ (u8 *)skb->data,
+ skb->len);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write the packet\n", __func__);
+ }
+ dev_kfree_skb(skb);
+ return status;
+ }
+
+ bss = &info->control.vif->bss_conf;
+ wh = (struct ieee80211_hdr *)&skb->data[0];
+
+ if (FRAME_DESC_SZ > skb_headroom(skb))
+ goto err;
+
+ skb_push(skb, FRAME_DESC_SZ);
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ msg = (__le16 *)skb->data;
+
+ if (skb->len > MAX_MGMT_PKT_SIZE) {
+ rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__);
+ goto err;
+ }
+
+ msg[0] = cpu_to_le16((skb->len - FRAME_DESC_SZ) |
+ (RSI_WIFI_MGMT_Q << 12));
+ msg[1] = cpu_to_le16(TX_DOT11_MGMT);
+ msg[2] = cpu_to_le16(MIN_802_11_HDR_LEN << 8);
+ msg[3] = cpu_to_le16(RATE_INFO_ENABLE);
+ msg[6] = cpu_to_le16(le16_to_cpu(wh->seq_ctrl) >> 4);
+
+ if (wh->addr1[0] & BIT(0))
+ msg[3] |= cpu_to_le16(RSI_BROADCAST_PKT);
+
+ if (common->band == IEEE80211_BAND_2GHZ)
+ msg[4] = cpu_to_le16(RSI_11B_MODE);
+ else
+ msg[4] = cpu_to_le16((RSI_RATE_6 & 0x0f) | RSI_11G_MODE);
+
+ /* Indicate to firmware to give cfm */
+ if ((skb->data[16] == IEEE80211_STYPE_PROBE_REQ) && (!bss->assoc)) {
+ msg[1] |= cpu_to_le16(BIT(10));
+ msg[7] = cpu_to_le16(PROBEREQ_CONFIRM);
+ common->mgmt_q_block = true;
+ }
+
+ msg[7] |= cpu_to_le16(vap_id << 8);
+
+ status = adapter->host_intf_write_pkt(common->priv,
+ (u8 *)msg,
+ skb->len);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Failed to write the packet\n", __func__);
+
+err:
+ rsi_indicate_tx_status(common->priv, skb, status);
+ return status;
+}
diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c
new file mode 100644
index 000000000000..852453f386e2
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c
@@ -0,0 +1,850 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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/module.h>
+#include "rsi_sdio.h"
+#include "rsi_common.h"
+
+/**
+ * rsi_sdio_set_cmd52_arg() - This function prepares cmd 52 read/write arg.
+ * @rw: Read/write
+ * @func: function number
+ * @raw: indicates whether to perform read after write
+ * @address: address to which to read/write
+ * @writedata: data to write
+ *
+ * Return: argument
+ */
+static u32 rsi_sdio_set_cmd52_arg(bool rw,
+ u8 func,
+ u8 raw,
+ u32 address,
+ u8 writedata)
+{
+ return ((rw & 1) << 31) | ((func & 0x7) << 28) |
+ ((raw & 1) << 27) | (1 << 26) |
+ ((address & 0x1FFFF) << 9) | (1 << 8) |
+ (writedata & 0xFF);
+}
+
+/**
+ * rsi_cmd52writebyte() - This function issues cmd52 byte write onto the card.
+ * @card: Pointer to the mmc_card.
+ * @address: Address to write.
+ * @byte: Data to write.
+ *
+ * Return: Write status.
+ */
+static int rsi_cmd52writebyte(struct mmc_card *card,
+ u32 address,
+ u8 byte)
+{
+ struct mmc_command io_cmd;
+ u32 arg;
+
+ memset(&io_cmd, 0, sizeof(io_cmd));
+ arg = rsi_sdio_set_cmd52_arg(1, 0, 0, address, byte);
+ io_cmd.opcode = SD_IO_RW_DIRECT;
+ io_cmd.arg = arg;
+ io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+ return mmc_wait_for_cmd(card->host, &io_cmd, 0);
+}
+
+/**
+ * rsi_cmd52readbyte() - This function issues cmd52 byte read onto the card.
+ * @card: Pointer to the mmc_card.
+ * @address: Address to read from.
+ * @byte: Variable to store read value.
+ *
+ * Return: Read status.
+ */
+static int rsi_cmd52readbyte(struct mmc_card *card,
+ u32 address,
+ u8 *byte)
+{
+ struct mmc_command io_cmd;
+ u32 arg;
+ int err;
+
+ memset(&io_cmd, 0, sizeof(io_cmd));
+ arg = rsi_sdio_set_cmd52_arg(0, 0, 0, address, 0);
+ io_cmd.opcode = SD_IO_RW_DIRECT;
+ io_cmd.arg = arg;
+ io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &io_cmd, 0);
+ if ((!err) && (byte))
+ *byte = io_cmd.resp[0] & 0xFF;
+ return err;
+}
+
+/**
+ * rsi_issue_sdiocommand() - This function issues sdio commands.
+ * @func: Pointer to the sdio_func structure.
+ * @opcode: Opcode value.
+ * @arg: Arguments to pass.
+ * @flags: Flags which are set.
+ * @resp: Pointer to store response.
+ *
+ * Return: err: command status as 0 or -1.
+ */
+static int rsi_issue_sdiocommand(struct sdio_func *func,
+ u32 opcode,
+ u32 arg,
+ u32 flags,
+ u32 *resp)
+{
+ struct mmc_command cmd;
+ struct mmc_host *host;
+ int err;
+
+ host = func->card->host;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = opcode;
+ cmd.arg = arg;
+ cmd.flags = flags;
+ err = mmc_wait_for_cmd(host, &cmd, 3);
+
+ if ((!err) && (resp))
+ *resp = cmd.resp[0];
+
+ return err;
+}
+
+/**
+ * rsi_handle_interrupt() - This function is called upon the occurence
+ * of an interrupt.
+ * @function: Pointer to the sdio_func structure.
+ *
+ * Return: None.
+ */
+static void rsi_handle_interrupt(struct sdio_func *function)
+{
+ struct rsi_hw *adapter = sdio_get_drvdata(function);
+
+ sdio_release_host(function);
+ rsi_interrupt_handler(adapter);
+ sdio_claim_host(function);
+}
+
+/**
+ * rsi_reset_card() - This function resets and re-initializes the card.
+ * @pfunction: Pointer to the sdio_func structure.
+ *
+ * Return: None.
+ */
+static void rsi_reset_card(struct sdio_func *pfunction)
+{
+ int ret = 0;
+ int err;
+ struct mmc_card *card = pfunction->card;
+ struct mmc_host *host = card->host;
+ s32 bit = (fls(host->ocr_avail) - 1);
+ u8 cmd52_resp;
+ u32 clock, resp, i;
+ u16 rca;
+
+ /* Reset 9110 chip */
+ ret = rsi_cmd52writebyte(pfunction->card,
+ SDIO_CCCR_ABORT,
+ (1 << 3));
+
+ /* Card will not send any response as it is getting reset immediately
+ * Hence expect a timeout status from host controller
+ */
+ if (ret != -ETIMEDOUT)
+ rsi_dbg(ERR_ZONE, "%s: Reset failed : %d\n", __func__, ret);
+
+ /* Wait for few milli seconds to get rid of residue charges if any */
+ msleep(20);
+
+ /* Initialize the SDIO card */
+ host->ios.vdd = bit;
+ host->ios.chip_select = MMC_CS_DONTCARE;
+ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+ host->ios.power_mode = MMC_POWER_UP;
+ host->ios.bus_width = MMC_BUS_WIDTH_1;
+ host->ios.timing = MMC_TIMING_LEGACY;
+ host->ops->set_ios(host, &host->ios);
+
+ /*
+ * This delay should be sufficient to allow the power supply
+ * to reach the minimum voltage.
+ */
+ msleep(20);
+
+ host->ios.clock = host->f_min;
+ host->ios.power_mode = MMC_POWER_ON;
+ host->ops->set_ios(host, &host->ios);
+
+ /*
+ * This delay must be at least 74 clock sizes, or 1 ms, or the
+ * time required to reach a stable voltage.
+ */
+ msleep(20);
+
+ /* Issue CMD0. Goto idle state */
+ host->ios.chip_select = MMC_CS_HIGH;
+ host->ops->set_ios(host, &host->ios);
+ msleep(20);
+ err = rsi_issue_sdiocommand(pfunction,
+ MMC_GO_IDLE_STATE,
+ 0,
+ (MMC_RSP_NONE | MMC_CMD_BC),
+ NULL);
+ host->ios.chip_select = MMC_CS_DONTCARE;
+ host->ops->set_ios(host, &host->ios);
+ msleep(20);
+ host->use_spi_crc = 0;
+
+ if (err)
+ rsi_dbg(ERR_ZONE, "%s: CMD0 failed : %d\n", __func__, err);
+
+ if (!host->ocr_avail) {
+ /* Issue CMD5, arg = 0 */
+ err = rsi_issue_sdiocommand(pfunction,
+ SD_IO_SEND_OP_COND,
+ 0,
+ (MMC_RSP_R4 | MMC_CMD_BCR),
+ &resp);
+ if (err)
+ rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n",
+ __func__, err);
+ host->ocr_avail = resp;
+ }
+
+ /* Issue CMD5, arg = ocr. Wait till card is ready */
+ for (i = 0; i < 100; i++) {
+ err = rsi_issue_sdiocommand(pfunction,
+ SD_IO_SEND_OP_COND,
+ host->ocr_avail,
+ (MMC_RSP_R4 | MMC_CMD_BCR),
+ &resp);
+ if (err) {
+ rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n",
+ __func__, err);
+ break;
+ }
+
+ if (resp & MMC_CARD_BUSY)
+ break;
+ msleep(20);
+ }
+
+ if ((i == 100) || (err)) {
+ rsi_dbg(ERR_ZONE, "%s: card in not ready : %d %d\n",
+ __func__, i, err);
+ return;
+ }
+
+ /* Issue CMD3, get RCA */
+ err = rsi_issue_sdiocommand(pfunction,
+ SD_SEND_RELATIVE_ADDR,
+ 0,
+ (MMC_RSP_R6 | MMC_CMD_BCR),
+ &resp);
+ if (err) {
+ rsi_dbg(ERR_ZONE, "%s: CMD3 failed : %d\n", __func__, err);
+ return;
+ }
+ rca = resp >> 16;
+ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+ host->ops->set_ios(host, &host->ios);
+
+ /* Issue CMD7, select card */
+ err = rsi_issue_sdiocommand(pfunction,
+ MMC_SELECT_CARD,
+ (rca << 16),
+ (MMC_RSP_R1 | MMC_CMD_AC),
+ NULL);
+ if (err) {
+ rsi_dbg(ERR_ZONE, "%s: CMD7 failed : %d\n", __func__, err);
+ return;
+ }
+
+ /* Enable high speed */
+ if (card->host->caps & MMC_CAP_SD_HIGHSPEED) {
+ rsi_dbg(ERR_ZONE, "%s: Set high speed mode\n", __func__);
+ err = rsi_cmd52readbyte(card, SDIO_CCCR_SPEED, &cmd52_resp);
+ if (err) {
+ rsi_dbg(ERR_ZONE, "%s: CCCR speed reg read failed: %d\n",
+ __func__, err);
+ card->state &= ~MMC_STATE_HIGHSPEED;
+ } else {
+ err = rsi_cmd52writebyte(card,
+ SDIO_CCCR_SPEED,
+ (cmd52_resp | SDIO_SPEED_EHS));
+ if (err) {
+ rsi_dbg(ERR_ZONE,
+ "%s: CCR speed regwrite failed %d\n",
+ __func__, err);
+ return;
+ }
+ mmc_card_set_highspeed(card);
+ host->ios.timing = MMC_TIMING_SD_HS;
+ host->ops->set_ios(host, &host->ios);
+ }
+ }
+
+ /* Set clock */
+ if (mmc_card_highspeed(card))
+ clock = 50000000;
+ else
+ clock = card->cis.max_dtr;
+
+ if (clock > host->f_max)
+ clock = host->f_max;
+
+ host->ios.clock = clock;
+ host->ops->set_ios(host, &host->ios);
+
+ if (card->host->caps & MMC_CAP_4_BIT_DATA) {
+ /* CMD52: Set bus width & disable card detect resistor */
+ err = rsi_cmd52writebyte(card,
+ SDIO_CCCR_IF,
+ (SDIO_BUS_CD_DISABLE |
+ SDIO_BUS_WIDTH_4BIT));
+ if (err) {
+ rsi_dbg(ERR_ZONE, "%s: Set bus mode failed : %d\n",
+ __func__, err);
+ return;
+ }
+ host->ios.bus_width = MMC_BUS_WIDTH_4;
+ host->ops->set_ios(host, &host->ios);
+ }
+}
+
+/**
+ * rsi_setclock() - This function sets the clock frequency.
+ * @adapter: Pointer to the adapter structure.
+ * @freq: Clock frequency.
+ *
+ * Return: None.
+ */
+static void rsi_setclock(struct rsi_hw *adapter, u32 freq)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ struct mmc_host *host = dev->pfunction->card->host;
+ u32 clock;
+
+ clock = freq * 1000;
+ if (clock > host->f_max)
+ clock = host->f_max;
+ host->ios.clock = clock;
+ host->ops->set_ios(host, &host->ios);
+}
+
+/**
+ * rsi_setblocklength() - This function sets the host block length.
+ * @adapter: Pointer to the adapter structure.
+ * @length: Block length to be set.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_setblocklength(struct rsi_hw *adapter, u32 length)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ int status;
+ rsi_dbg(INIT_ZONE, "%s: Setting the block length\n", __func__);
+
+ status = sdio_set_block_size(dev->pfunction, length);
+ dev->pfunction->max_blksize = 256;
+
+ rsi_dbg(INFO_ZONE,
+ "%s: Operational blk length is %d\n", __func__, length);
+ return status;
+}
+
+/**
+ * rsi_setupcard() - This function queries and sets the card's features.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_setupcard(struct rsi_hw *adapter)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ int status = 0;
+
+ rsi_setclock(adapter, 50000);
+
+ dev->tx_blk_size = 256;
+ status = rsi_setblocklength(adapter, dev->tx_blk_size);
+ if (status)
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to set block length\n", __func__);
+ return status;
+}
+
+/**
+ * rsi_sdio_read_register() - This function reads one byte of information
+ * from a register.
+ * @adapter: Pointer to the adapter structure.
+ * @addr: Address of the register.
+ * @data: Pointer to the data that stores the data read.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_sdio_read_register(struct rsi_hw *adapter,
+ u32 addr,
+ u8 *data)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u8 fun_num = 0;
+ int status;
+
+ sdio_claim_host(dev->pfunction);
+
+ if (fun_num == 0)
+ *data = sdio_f0_readb(dev->pfunction, addr, &status);
+ else
+ *data = sdio_readb(dev->pfunction, addr, &status);
+
+ sdio_release_host(dev->pfunction);
+
+ return status;
+}
+
+/**
+ * rsi_sdio_write_register() - This function writes one byte of information
+ * into a register.
+ * @adapter: Pointer to the adapter structure.
+ * @function: Function Number.
+ * @addr: Address of the register.
+ * @data: Pointer to the data tha has to be written.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_sdio_write_register(struct rsi_hw *adapter,
+ u8 function,
+ u32 addr,
+ u8 *data)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ int status = 0;
+
+ sdio_claim_host(dev->pfunction);
+
+ if (function == 0)
+ sdio_f0_writeb(dev->pfunction, *data, addr, &status);
+ else
+ sdio_writeb(dev->pfunction, *data, addr, &status);
+
+ sdio_release_host(dev->pfunction);
+
+ return status;
+}
+
+/**
+ * rsi_sdio_ack_intr() - This function acks the interrupt received.
+ * @adapter: Pointer to the adapter structure.
+ * @int_bit: Interrupt bit to write into register.
+ *
+ * Return: None.
+ */
+void rsi_sdio_ack_intr(struct rsi_hw *adapter, u8 int_bit)
+{
+ int status;
+ status = rsi_sdio_write_register(adapter,
+ 1,
+ (SDIO_FUN1_INTR_CLR_REG |
+ RSI_SD_REQUEST_MASTER),
+ &int_bit);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: unable to send ack\n", __func__);
+}
+
+
+
+/**
+ * rsi_sdio_read_register_multiple() - This function read multiple bytes of
+ * information from the SD card.
+ * @adapter: Pointer to the adapter structure.
+ * @addr: Address of the register.
+ * @count: Number of multiple bytes to be read.
+ * @data: Pointer to the read data.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_sdio_read_register_multiple(struct rsi_hw *adapter,
+ u32 addr,
+ u32 count,
+ u8 *data)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u32 status;
+
+ sdio_claim_host(dev->pfunction);
+
+ status = sdio_readsb(dev->pfunction, data, addr, count);
+
+ sdio_release_host(dev->pfunction);
+
+ if (status != 0)
+ rsi_dbg(ERR_ZONE, "%s: Synch Cmd53 read failed\n", __func__);
+ return status;
+}
+
+/**
+ * rsi_sdio_write_register_multiple() - This function writes multiple bytes of
+ * information to the SD card.
+ * @adapter: Pointer to the adapter structure.
+ * @addr: Address of the register.
+ * @data: Pointer to the data that has to be written.
+ * @count: Number of multiple bytes to be written.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_sdio_write_register_multiple(struct rsi_hw *adapter,
+ u32 addr,
+ u8 *data,
+ u32 count)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ int status;
+
+ if (dev->write_fail > 1) {
+ rsi_dbg(ERR_ZONE, "%s: Stopping card writes\n", __func__);
+ return 0;
+ } else if (dev->write_fail == 1) {
+ /**
+ * Assuming it is a CRC failure, we want to allow another
+ * card write
+ */
+ rsi_dbg(ERR_ZONE, "%s: Continue card writes\n", __func__);
+ dev->write_fail++;
+ }
+
+ sdio_claim_host(dev->pfunction);
+
+ status = sdio_writesb(dev->pfunction, addr, data, count);
+
+ sdio_release_host(dev->pfunction);
+
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Synch Cmd53 write failed %d\n",
+ __func__, status);
+ dev->write_fail = 2;
+ } else {
+ memcpy(dev->prev_desc, data, FRAME_DESC_SZ);
+ }
+ return status;
+}
+
+/**
+ * rsi_sdio_host_intf_write_pkt() - This function writes the packet to device.
+ * @adapter: Pointer to the adapter structure.
+ * @pkt: Pointer to the data to be written on to the device.
+ * @len: length of the data to be written on to the device.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_sdio_host_intf_write_pkt(struct rsi_hw *adapter,
+ u8 *pkt,
+ u32 len)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u32 block_size = dev->tx_blk_size;
+ u32 num_blocks, address, length;
+ u32 queueno;
+ int status;
+
+ queueno = ((pkt[1] >> 4) & 0xf);
+
+ num_blocks = len / block_size;
+
+ if (len % block_size)
+ num_blocks++;
+
+ address = (num_blocks * block_size | (queueno << 12));
+ length = num_blocks * block_size;
+
+ status = rsi_sdio_write_register_multiple(adapter,
+ address,
+ (u8 *)pkt,
+ length);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Unable to write onto the card: %d\n",
+ __func__, status);
+ rsi_dbg(DATA_TX_ZONE, "%s: Successfully written onto card\n", __func__);
+ return status;
+}
+
+/**
+ * rsi_sdio_host_intf_read_pkt() - This function reads the packet
+ from the device.
+ * @adapter: Pointer to the adapter data structure.
+ * @pkt: Pointer to the packet data to be read from the the device.
+ * @length: Length of the data to be read from the device.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_sdio_host_intf_read_pkt(struct rsi_hw *adapter,
+ u8 *pkt,
+ u32 length)
+{
+ int status = -EINVAL;
+
+ if (!length) {
+ rsi_dbg(ERR_ZONE, "%s: Pkt size is zero\n", __func__);
+ return status;
+ }
+
+ status = rsi_sdio_read_register_multiple(adapter,
+ length,
+ length, /*num of bytes*/
+ (u8 *)pkt);
+
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Failed to read frame: %d\n", __func__,
+ status);
+ return status;
+}
+
+/**
+ * rsi_init_sdio_interface() - This function does init specific to SDIO.
+ *
+ * @adapter: Pointer to the adapter data structure.
+ * @pkt: Pointer to the packet data to be read from the the device.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+
+static int rsi_init_sdio_interface(struct rsi_hw *adapter,
+ struct sdio_func *pfunction)
+{
+ struct rsi_91x_sdiodev *rsi_91x_dev;
+ int status = -ENOMEM;
+
+ rsi_91x_dev = kzalloc(sizeof(*rsi_91x_dev), GFP_KERNEL);
+ if (!rsi_91x_dev)
+ return status;
+
+ adapter->rsi_dev = rsi_91x_dev;
+
+ sdio_claim_host(pfunction);
+
+ pfunction->enable_timeout = 100;
+ status = sdio_enable_func(pfunction);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to enable interface\n", __func__);
+ sdio_release_host(pfunction);
+ return status;
+ }
+
+ rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__);
+
+ rsi_91x_dev->pfunction = pfunction;
+ adapter->device = &pfunction->dev;
+
+ sdio_set_drvdata(pfunction, adapter);
+
+ status = rsi_setupcard(adapter);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to setup card\n", __func__);
+ goto fail;
+ }
+
+ rsi_dbg(INIT_ZONE, "%s: Setup card succesfully\n", __func__);
+
+ status = rsi_init_sdio_slave_regs(adapter);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to init slave regs\n", __func__);
+ goto fail;
+ }
+ sdio_release_host(pfunction);
+
+ adapter->host_intf_write_pkt = rsi_sdio_host_intf_write_pkt;
+ adapter->host_intf_read_pkt = rsi_sdio_host_intf_read_pkt;
+ adapter->determine_event_timeout = rsi_sdio_determine_event_timeout;
+ adapter->check_hw_queue_status = rsi_sdio_read_buffer_status_register;
+
+#ifdef CONFIG_RSI_DEBUGFS
+ adapter->num_debugfs_entries = MAX_DEBUGFS_ENTRIES;
+#endif
+ return status;
+fail:
+ sdio_disable_func(pfunction);
+ sdio_release_host(pfunction);
+ return status;
+}
+
+/**
+ * rsi_probe() - This function is called by kernel when the driver provided
+ * Vendor and device IDs are matched. All the initialization
+ * work is done here.
+ * @pfunction: Pointer to the sdio_func structure.
+ * @id: Pointer to sdio_device_id structure.
+ *
+ * Return: 0 on success, 1 on failure.
+ */
+static int rsi_probe(struct sdio_func *pfunction,
+ const struct sdio_device_id *id)
+{
+ struct rsi_hw *adapter;
+
+ rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__);
+
+ adapter = rsi_91x_init();
+ if (!adapter) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n",
+ __func__);
+ return 1;
+ }
+
+ if (rsi_init_sdio_interface(adapter, pfunction)) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to init sdio interface\n",
+ __func__);
+ goto fail;
+ }
+
+ if (rsi_sdio_device_init(adapter->priv)) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", __func__);
+ sdio_claim_host(pfunction);
+ sdio_disable_func(pfunction);
+ sdio_release_host(pfunction);
+ goto fail;
+ }
+
+ sdio_claim_host(pfunction);
+ if (sdio_claim_irq(pfunction, rsi_handle_interrupt)) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to request IRQ\n", __func__);
+ sdio_release_host(pfunction);
+ goto fail;
+ }
+
+ sdio_release_host(pfunction);
+ rsi_dbg(INIT_ZONE, "%s: Registered Interrupt handler\n", __func__);
+
+ return 0;
+fail:
+ rsi_91x_deinit(adapter);
+ rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__);
+ return 1;
+}
+
+/**
+ * rsi_disconnect() - This function performs the reverse of the probe function.
+ * @pfunction: Pointer to the sdio_func structure.
+ *
+ * Return: void.
+ */
+static void rsi_disconnect(struct sdio_func *pfunction)
+{
+ struct rsi_hw *adapter = sdio_get_drvdata(pfunction);
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+
+ if (!adapter)
+ return;
+
+ dev->write_fail = 2;
+ rsi_mac80211_detach(adapter);
+
+ sdio_claim_host(pfunction);
+ sdio_release_irq(pfunction);
+ sdio_disable_func(pfunction);
+ rsi_91x_deinit(adapter);
+ /* Resetting to take care of the case, where-in driver is re-loaded */
+ rsi_reset_card(pfunction);
+ sdio_release_host(pfunction);
+}
+
+#ifdef CONFIG_PM
+static int rsi_suspend(struct device *dev)
+{
+ /* Not yet implemented */
+ return -ENOSYS;
+}
+
+static int rsi_resume(struct device *dev)
+{
+ /* Not yet implemented */
+ return -ENOSYS;
+}
+
+static const struct dev_pm_ops rsi_pm_ops = {
+ .suspend = rsi_suspend,
+ .resume = rsi_resume,
+};
+#endif
+
+static const struct sdio_device_id rsi_dev_table[] = {
+ { SDIO_DEVICE(0x303, 0x100) },
+ { SDIO_DEVICE(0x041B, 0x0301) },
+ { SDIO_DEVICE(0x041B, 0x0201) },
+ { SDIO_DEVICE(0x041B, 0x9330) },
+ { /* Blank */},
+};
+
+static struct sdio_driver rsi_driver = {
+ .name = "RSI-SDIO WLAN",
+ .probe = rsi_probe,
+ .remove = rsi_disconnect,
+ .id_table = rsi_dev_table,
+#ifdef CONFIG_PM
+ .drv = {
+ .pm = &rsi_pm_ops,
+ }
+#endif
+};
+
+/**
+ * rsi_module_init() - This function registers the sdio module.
+ * @void: Void.
+ *
+ * Return: 0 on success.
+ */
+static int rsi_module_init(void)
+{
+ sdio_register_driver(&rsi_driver);
+ rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__);
+ return 0;
+}
+
+/**
+ * rsi_module_exit() - This function unregisters the sdio module.
+ * @void: Void.
+ *
+ * Return: None.
+ */
+static void rsi_module_exit(void)
+{
+ sdio_unregister_driver(&rsi_driver);
+ rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__);
+}
+
+module_init(rsi_module_init);
+module_exit(rsi_module_exit);
+
+MODULE_AUTHOR("Redpine Signals Inc");
+MODULE_DESCRIPTION("Common SDIO layer for RSI drivers");
+MODULE_SUPPORTED_DEVICE("RSI-91x");
+MODULE_DEVICE_TABLE(sdio, rsi_dev_table);
+MODULE_FIRMWARE(FIRMWARE_RSI9113);
+MODULE_VERSION("0.1");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
new file mode 100644
index 000000000000..f1cb99cafed8
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
@@ -0,0 +1,566 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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/firmware.h>
+#include "rsi_sdio.h"
+#include "rsi_common.h"
+
+/**
+ * rsi_sdio_master_access_msword() - This function sets the AHB master access
+ * MS word in the SDIO slave registers.
+ * @adapter: Pointer to the adapter structure.
+ * @ms_word: ms word need to be initialized.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_sdio_master_access_msword(struct rsi_hw *adapter,
+ u16 ms_word)
+{
+ u8 byte;
+ u8 function = 0;
+ int status = 0;
+
+ byte = (u8)(ms_word & 0x00FF);
+
+ rsi_dbg(INIT_ZONE,
+ "%s: MASTER_ACCESS_MSBYTE:0x%x\n", __func__, byte);
+
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_MASTER_ACCESS_MSBYTE,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: fail to access MASTER_ACCESS_MSBYTE\n",
+ __func__);
+ return -1;
+ }
+
+ byte = (u8)(ms_word >> 8);
+
+ rsi_dbg(INIT_ZONE, "%s:MASTER_ACCESS_LSBYTE:0x%x\n", __func__, byte);
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_MASTER_ACCESS_LSBYTE,
+ &byte);
+ return status;
+}
+
+/**
+ * rsi_copy_to_card() - This function includes the actual funtionality of
+ * copying the TA firmware to the card.Basically this
+ * function includes opening the TA file,reading the
+ * TA file and writing their values in blocks of data.
+ * @common: Pointer to the driver private structure.
+ * @fw: Pointer to the firmware value to be written.
+ * @len: length of firmware file.
+ * @num_blocks: Number of blocks to be written to the card.
+ *
+ * Return: 0 on success and -1 on failure.
+ */
+static int rsi_copy_to_card(struct rsi_common *common,
+ const u8 *fw,
+ u32 len,
+ u32 num_blocks)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u32 indx, ii;
+ u32 block_size = dev->tx_blk_size;
+ u32 lsb_address;
+ __le32 data[] = { TA_HOLD_THREAD_VALUE, TA_SOFT_RST_CLR,
+ TA_PC_ZERO, TA_RELEASE_THREAD_VALUE };
+ u32 address[] = { TA_HOLD_THREAD_REG, TA_SOFT_RESET_REG,
+ TA_TH0_PC_REG, TA_RELEASE_THREAD_REG };
+ u32 base_address;
+ u16 msb_address;
+
+ base_address = TA_LOAD_ADDRESS;
+ msb_address = base_address >> 16;
+
+ for (indx = 0, ii = 0; ii < num_blocks; ii++, indx += block_size) {
+ lsb_address = ((u16) base_address | RSI_SD_REQUEST_MASTER);
+ if (rsi_sdio_write_register_multiple(adapter,
+ lsb_address,
+ (u8 *)(fw + indx),
+ block_size)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to load %s blk\n", __func__,
+ FIRMWARE_RSI9113);
+ return -1;
+ }
+ rsi_dbg(INIT_ZONE, "%s: loading block: %d\n", __func__, ii);
+ base_address += block_size;
+ if ((base_address >> 16) != msb_address) {
+ msb_address += 1;
+ if (rsi_sdio_master_access_msword(adapter,
+ msb_address)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to set ms word reg\n",
+ __func__);
+ return -1;
+ }
+ }
+ }
+
+ if (len % block_size) {
+ lsb_address = ((u16) base_address | RSI_SD_REQUEST_MASTER);
+ if (rsi_sdio_write_register_multiple(adapter,
+ lsb_address,
+ (u8 *)(fw + indx),
+ len % block_size)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to load f/w\n", __func__);
+ return -1;
+ }
+ }
+ rsi_dbg(INIT_ZONE,
+ "%s: Succesfully loaded TA instructions\n", __func__);
+
+ if (rsi_sdio_master_access_msword(adapter, TA_BASE_ADDR)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to set ms word to common reg\n",
+ __func__);
+ return -1;
+ }
+
+ for (ii = 0; ii < ARRAY_SIZE(data); ii++) {
+ /* Bringing TA out of reset */
+ if (rsi_sdio_write_register_multiple(adapter,
+ (address[ii] |
+ RSI_SD_REQUEST_MASTER),
+ (u8 *)&data[ii],
+ 4)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to hold TA threads\n", __func__);
+ return -1;
+ }
+ }
+
+ rsi_dbg(INIT_ZONE, "%s: loaded firmware\n", __func__);
+ return 0;
+}
+
+/**
+ * rsi_load_ta_instructions() - This function includes the actual funtionality
+ * of loading the TA firmware.This function also
+ * includes opening the TA file,reading the TA
+ * file and writing their value in blocks of data.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_load_ta_instructions(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u32 len;
+ u32 num_blocks;
+ const u8 *fw;
+ const struct firmware *fw_entry = NULL;
+ u32 block_size = dev->tx_blk_size;
+ int status = 0;
+ u32 base_address;
+ u16 msb_address;
+
+ if (rsi_sdio_master_access_msword(adapter, TA_BASE_ADDR)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to set ms word to common reg\n",
+ __func__);
+ return -1;
+ }
+ base_address = TA_LOAD_ADDRESS;
+ msb_address = (base_address >> 16);
+
+ if (rsi_sdio_master_access_msword(adapter, msb_address)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to set ms word reg\n", __func__);
+ return -1;
+ }
+
+ status = request_firmware(&fw_entry, FIRMWARE_RSI9113, adapter->device);
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE, "%s Firmware file %s not found\n",
+ __func__, FIRMWARE_RSI9113);
+ return status;
+ }
+
+ fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL);
+ len = fw_entry->size;
+
+ if (len % 4)
+ len += (4 - (len % 4));
+
+ num_blocks = (len / block_size);
+
+ rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len);
+ rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks);
+
+ status = rsi_copy_to_card(common, fw, len, num_blocks);
+ release_firmware(fw_entry);
+ return status;
+}
+
+/**
+ * rsi_process_pkt() - This Function reads rx_blocks register and figures out
+ * the size of the rx pkt.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_process_pkt(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ u8 num_blks = 0;
+ u32 rcv_pkt_len = 0;
+ int status = 0;
+
+ status = rsi_sdio_read_register(adapter,
+ SDIO_RX_NUM_BLOCKS_REG,
+ &num_blks);
+
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read pkt length from the card:\n",
+ __func__);
+ return status;
+ }
+ rcv_pkt_len = (num_blks * 256);
+
+ common->rx_data_pkt = kmalloc(rcv_pkt_len, GFP_KERNEL);
+ if (!common->rx_data_pkt) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in memory allocation\n",
+ __func__);
+ return -1;
+ }
+
+ status = rsi_sdio_host_intf_read_pkt(adapter,
+ common->rx_data_pkt,
+ rcv_pkt_len);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to read packet from card\n",
+ __func__);
+ goto fail;
+ }
+
+ status = rsi_read_pkt(common, rcv_pkt_len);
+ kfree(common->rx_data_pkt);
+ return status;
+
+fail:
+ kfree(common->rx_data_pkt);
+ return -1;
+}
+
+/**
+ * rsi_init_sdio_slave_regs() - This function does the actual initialization
+ * of SDBUS slave registers.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+int rsi_init_sdio_slave_regs(struct rsi_hw *adapter)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u8 function = 0;
+ u8 byte;
+ int status = 0;
+
+ if (dev->next_read_delay) {
+ byte = dev->next_read_delay;
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_NXT_RD_DELAY2,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write SDIO_NXT_RD_DELAY2\n",
+ __func__);
+ return -1;
+ }
+ }
+
+ if (dev->sdio_high_speed_enable) {
+ rsi_dbg(INIT_ZONE, "%s: Enabling SDIO High speed\n", __func__);
+ byte = 0x3;
+
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_REG_HIGH_SPEED,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to enable SDIO high speed\n",
+ __func__);
+ return -1;
+ }
+ }
+
+ /* This tells SDIO FIFO when to start read to host */
+ rsi_dbg(INIT_ZONE, "%s: Initialzing SDIO read start level\n", __func__);
+ byte = 0x24;
+
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_READ_START_LVL,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write SDIO_READ_START_LVL\n", __func__);
+ return -1;
+ }
+
+ rsi_dbg(INIT_ZONE, "%s: Initialzing FIFO ctrl registers\n", __func__);
+ byte = (128 - 32);
+
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_READ_FIFO_CTL,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write SDIO_READ_FIFO_CTL\n", __func__);
+ return -1;
+ }
+
+ byte = 32;
+ status = rsi_sdio_write_register(adapter,
+ function,
+ SDIO_WRITE_FIFO_CTL,
+ &byte);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write SDIO_WRITE_FIFO_CTL\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * rsi_interrupt_handler() - This function read and process SDIO interrupts.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: None.
+ */
+void rsi_interrupt_handler(struct rsi_hw *adapter)
+{
+ struct rsi_common *common = adapter->priv;
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ int status;
+ enum sdio_interrupt_type isr_type;
+ u8 isr_status = 0;
+ u8 fw_status = 0;
+
+ dev->rx_info.sdio_int_counter++;
+
+ do {
+ mutex_lock(&common->tx_rxlock);
+ status = rsi_sdio_read_register(common->priv,
+ RSI_FN1_INT_REGISTER,
+ &isr_status);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to Read Intr Status Register\n",
+ __func__);
+ mutex_unlock(&common->tx_rxlock);
+ return;
+ }
+
+ if (isr_status == 0) {
+ rsi_set_event(&common->tx_thread.event);
+ dev->rx_info.sdio_intr_status_zero++;
+ mutex_unlock(&common->tx_rxlock);
+ return;
+ }
+
+ rsi_dbg(ISR_ZONE, "%s: Intr_status = %x %d %d\n",
+ __func__, isr_status, (1 << MSDU_PKT_PENDING),
+ (1 << FW_ASSERT_IND));
+
+ do {
+ RSI_GET_SDIO_INTERRUPT_TYPE(isr_status, isr_type);
+
+ switch (isr_type) {
+ case BUFFER_AVAILABLE:
+ dev->rx_info.watch_bufferfull_count = 0;
+ dev->rx_info.buffer_full = false;
+ dev->rx_info.mgmt_buffer_full = false;
+ rsi_sdio_ack_intr(common->priv,
+ (1 << PKT_BUFF_AVAILABLE));
+ rsi_set_event((&common->tx_thread.event));
+ rsi_dbg(ISR_ZONE,
+ "%s: ==> BUFFER_AVILABLE <==\n",
+ __func__);
+ dev->rx_info.buf_avilable_counter++;
+ break;
+
+ case FIRMWARE_ASSERT_IND:
+ rsi_dbg(ERR_ZONE,
+ "%s: ==> FIRMWARE Assert <==\n",
+ __func__);
+ status = rsi_sdio_read_register(common->priv,
+ SDIO_FW_STATUS_REG,
+ &fw_status);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read f/w reg\n",
+ __func__);
+ } else {
+ rsi_dbg(ERR_ZONE,
+ "%s: Firmware Status is 0x%x\n",
+ __func__ , fw_status);
+ rsi_sdio_ack_intr(common->priv,
+ (1 << FW_ASSERT_IND));
+ }
+
+ common->fsm_state = FSM_CARD_NOT_READY;
+ break;
+
+ case MSDU_PACKET_PENDING:
+ rsi_dbg(ISR_ZONE, "Pkt pending interrupt\n");
+ dev->rx_info.total_sdio_msdu_pending_intr++;
+
+ status = rsi_process_pkt(common);
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read pkt\n",
+ __func__);
+ mutex_unlock(&common->tx_rxlock);
+ return;
+ }
+ break;
+ default:
+ rsi_sdio_ack_intr(common->priv, isr_status);
+ dev->rx_info.total_sdio_unknown_intr++;
+ isr_status = 0;
+ rsi_dbg(ISR_ZONE,
+ "Unknown Interrupt %x\n",
+ isr_status);
+ break;
+ }
+ isr_status ^= BIT(isr_type - 1);
+ } while (isr_status);
+ mutex_unlock(&common->tx_rxlock);
+ } while (1);
+}
+
+/**
+ * rsi_device_init() - This Function Initializes The HAL.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_sdio_device_init(struct rsi_common *common)
+{
+ if (rsi_load_ta_instructions(common))
+ return -1;
+
+ if (rsi_sdio_master_access_msword(common->priv, MISC_CFG_BASE_ADDR)) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to set ms word reg\n",
+ __func__);
+ return -1;
+ }
+ rsi_dbg(INIT_ZONE,
+ "%s: Setting ms word to 0x41050000\n", __func__);
+
+ return 0;
+}
+
+/**
+ * rsi_sdio_read_buffer_status_register() - This function is used to the read
+ * buffer status register and set
+ * relevant fields in
+ * rsi_91x_sdiodev struct.
+ * @adapter: Pointer to the driver hw structure.
+ * @q_num: The Q number whose status is to be found.
+ *
+ * Return: status: -1 on failure or else queue full/stop is indicated.
+ */
+int rsi_sdio_read_buffer_status_register(struct rsi_hw *adapter, u8 q_num)
+{
+ struct rsi_common *common = adapter->priv;
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ u8 buf_status = 0;
+ int status = 0;
+
+ status = rsi_sdio_read_register(common->priv,
+ RSI_DEVICE_BUFFER_STATUS_REGISTER,
+ &buf_status);
+
+ if (status) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read status register\n", __func__);
+ return -1;
+ }
+
+ if (buf_status & (BIT(PKT_MGMT_BUFF_FULL))) {
+ if (!dev->rx_info.mgmt_buffer_full)
+ dev->rx_info.mgmt_buf_full_counter++;
+ dev->rx_info.mgmt_buffer_full = true;
+ } else {
+ dev->rx_info.mgmt_buffer_full = false;
+ }
+
+ if (buf_status & (BIT(PKT_BUFF_FULL))) {
+ if (!dev->rx_info.buffer_full)
+ dev->rx_info.buf_full_counter++;
+ dev->rx_info.buffer_full = true;
+ } else {
+ dev->rx_info.buffer_full = false;
+ }
+
+ if (buf_status & (BIT(PKT_BUFF_SEMI_FULL))) {
+ if (!dev->rx_info.semi_buffer_full)
+ dev->rx_info.buf_semi_full_counter++;
+ dev->rx_info.semi_buffer_full = true;
+ } else {
+ dev->rx_info.semi_buffer_full = false;
+ }
+
+ if ((q_num == MGMT_SOFT_Q) && (dev->rx_info.mgmt_buffer_full))
+ return QUEUE_FULL;
+
+ if (dev->rx_info.buffer_full)
+ return QUEUE_FULL;
+
+ return QUEUE_NOT_FULL;
+}
+
+/**
+ * rsi_sdio_determine_event_timeout() - This Function determines the event
+ * timeout duration.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: timeout duration is returned.
+ */
+int rsi_sdio_determine_event_timeout(struct rsi_hw *adapter)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+
+ /* Once buffer full is seen, event timeout to occur every 2 msecs */
+ if (dev->rx_info.buffer_full)
+ return 2;
+
+ return EVENT_WAIT_FOREVER;
+}
diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c
new file mode 100644
index 000000000000..bb1bf96670eb
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_usb.c
@@ -0,0 +1,575 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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/module.h>
+#include "rsi_usb.h"
+
+/**
+ * rsi_usb_card_write() - This function writes to the USB Card.
+ * @adapter: Pointer to the adapter structure.
+ * @buf: Pointer to the buffer from where the data has to be taken.
+ * @len: Length to be written.
+ * @endpoint: Type of endpoint.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_usb_card_write(struct rsi_hw *adapter,
+ void *buf,
+ u16 len,
+ u8 endpoint)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ int status;
+ s32 transfer;
+
+ status = usb_bulk_msg(dev->usbdev,
+ usb_sndbulkpipe(dev->usbdev,
+ dev->bulkout_endpoint_addr[endpoint - 1]),
+ buf,
+ len,
+ &transfer,
+ HZ * 5);
+
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE,
+ "Card write failed with error code :%10d\n", status);
+ dev->write_fail = 1;
+ }
+ return status;
+}
+
+/**
+ * rsi_write_multiple() - This function writes multiple bytes of information
+ * to the USB card.
+ * @adapter: Pointer to the adapter structure.
+ * @addr: Address of the register.
+ * @data: Pointer to the data that has to be written.
+ * @count: Number of multiple bytes to be written.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_write_multiple(struct rsi_hw *adapter,
+ u8 endpoint,
+ u8 *data,
+ u32 count)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ u8 *seg = dev->tx_buffer;
+
+ if (dev->write_fail)
+ return 0;
+
+ if (endpoint == MGMT_EP) {
+ memset(seg, 0, RSI_USB_TX_HEAD_ROOM);
+ memcpy(seg + RSI_USB_TX_HEAD_ROOM, data, count);
+ } else {
+ seg = ((u8 *)data - RSI_USB_TX_HEAD_ROOM);
+ }
+
+ return rsi_usb_card_write(adapter,
+ seg,
+ count + RSI_USB_TX_HEAD_ROOM,
+ endpoint);
+}
+
+/**
+ * rsi_find_bulk_in_and_out_endpoints() - This function initializes the bulk
+ * endpoints to the device.
+ * @interface: Pointer to the USB interface structure.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: ret_val: 0 on success, -ENOMEM on failure.
+ */
+static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface,
+ struct rsi_hw *adapter)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ __le16 buffer_size;
+ int ii, bep_found = 0;
+
+ iface_desc = &(interface->altsetting[0]);
+
+ for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) {
+ endpoint = &(iface_desc->endpoint[ii].desc);
+
+ if ((!(dev->bulkin_endpoint_addr)) &&
+ (endpoint->bEndpointAddress & USB_DIR_IN) &&
+ ((endpoint->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ buffer_size = endpoint->wMaxPacketSize;
+ dev->bulkin_size = buffer_size;
+ dev->bulkin_endpoint_addr =
+ endpoint->bEndpointAddress;
+ }
+
+ if (!dev->bulkout_endpoint_addr[bep_found] &&
+ !(endpoint->bEndpointAddress & USB_DIR_IN) &&
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ dev->bulkout_endpoint_addr[bep_found] =
+ endpoint->bEndpointAddress;
+ buffer_size = endpoint->wMaxPacketSize;
+ dev->bulkout_size[bep_found] = buffer_size;
+ bep_found++;
+ }
+
+ if (bep_found >= MAX_BULK_EP)
+ break;
+ }
+
+ if (!(dev->bulkin_endpoint_addr) &&
+ (dev->bulkout_endpoint_addr[0]))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* rsi_usb_reg_read() - This function reads data from given register address.
+ * @usbdev: Pointer to the usb_device structure.
+ * @reg: Address of the register to be read.
+ * @value: Value to be read.
+ * @len: length of data to be read.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_usb_reg_read(struct usb_device *usbdev,
+ u32 reg,
+ u16 *value,
+ u16 len)
+{
+ u8 temp_buf[4];
+ int status = 0;
+
+ status = usb_control_msg(usbdev,
+ usb_rcvctrlpipe(usbdev, 0),
+ USB_VENDOR_REGISTER_READ,
+ USB_TYPE_VENDOR,
+ ((reg & 0xffff0000) >> 16), (reg & 0xffff),
+ (void *)temp_buf,
+ len,
+ HZ * 5);
+
+ *value = (temp_buf[0] | (temp_buf[1] << 8));
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Reg read failed with error code :%d\n",
+ __func__, status);
+ }
+ return status;
+}
+
+/**
+ * rsi_usb_reg_write() - This function writes the given data into the given
+ * register address.
+ * @usbdev: Pointer to the usb_device structure.
+ * @reg: Address of the register.
+ * @value: Value to write.
+ * @len: Length of data to be written.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_usb_reg_write(struct usb_device *usbdev,
+ u32 reg,
+ u16 value,
+ u16 len)
+{
+ u8 usb_reg_buf[4];
+ int status = 0;
+
+ usb_reg_buf[0] = (value & 0x00ff);
+ usb_reg_buf[1] = (value & 0xff00) >> 8;
+ usb_reg_buf[2] = 0x0;
+ usb_reg_buf[3] = 0x0;
+
+ status = usb_control_msg(usbdev,
+ usb_sndctrlpipe(usbdev, 0),
+ USB_VENDOR_REGISTER_WRITE,
+ USB_TYPE_VENDOR,
+ ((reg & 0xffff0000) >> 16),
+ (reg & 0xffff),
+ (void *)usb_reg_buf,
+ len,
+ HZ * 5);
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Reg write failed with error code :%d\n",
+ __func__, status);
+ }
+ return status;
+}
+
+/**
+ * rsi_rx_done_handler() - This function is called when a packet is received
+ * from USB stack. This is callback to recieve done.
+ * @urb: Received URB.
+ *
+ * Return: None.
+ */
+static void rsi_rx_done_handler(struct urb *urb)
+{
+ struct rsi_hw *adapter = urb->context;
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+
+ if (urb->status)
+ return;
+
+ rsi_set_event(&dev->rx_thread.event);
+}
+
+/**
+ * rsi_rx_urb_submit() - This function submits the given URB to the USB stack.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_rx_urb_submit(struct rsi_hw *adapter)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ struct urb *urb = dev->rx_usb_urb[0];
+ int status;
+
+ usb_fill_bulk_urb(urb,
+ dev->usbdev,
+ usb_rcvbulkpipe(dev->usbdev,
+ dev->bulkin_endpoint_addr),
+ urb->transfer_buffer,
+ 3000,
+ rsi_rx_done_handler,
+ adapter);
+
+ status = usb_submit_urb(urb, GFP_KERNEL);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__);
+
+ return status;
+}
+
+/**
+ * rsi_usb_write_register_multiple() - This function writes multiple bytes of
+ * information to multiple registers.
+ * @adapter: Pointer to the adapter structure.
+ * @addr: Address of the register.
+ * @data: Pointer to the data that has to be written.
+ * @count: Number of multiple bytes to be written on to the registers.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+int rsi_usb_write_register_multiple(struct rsi_hw *adapter,
+ u32 addr,
+ u8 *data,
+ u32 count)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ u8 *buf;
+ u8 transfer;
+ int status = 0;
+
+ buf = kzalloc(4096, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ while (count) {
+ transfer = min_t(int, count, 4096);
+ memcpy(buf, data, transfer);
+ status = usb_control_msg(dev->usbdev,
+ usb_sndctrlpipe(dev->usbdev, 0),
+ USB_VENDOR_REGISTER_WRITE,
+ USB_TYPE_VENDOR,
+ ((addr & 0xffff0000) >> 16),
+ (addr & 0xffff),
+ (void *)buf,
+ transfer,
+ HZ * 5);
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE,
+ "Reg write failed with error code :%d\n",
+ status);
+ } else {
+ count -= transfer;
+ data += transfer;
+ addr += transfer;
+ }
+ }
+
+ kfree(buf);
+ return 0;
+}
+
+/**
+ *rsi_usb_host_intf_write_pkt() - This function writes the packet to the
+ * USB card.
+ * @adapter: Pointer to the adapter structure.
+ * @pkt: Pointer to the data to be written on to the card.
+ * @len: Length of the data to be written on to the card.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter,
+ u8 *pkt,
+ u32 len)
+{
+ u32 queueno = ((pkt[1] >> 4) & 0xf);
+ u8 endpoint;
+
+ endpoint = ((queueno == RSI_WIFI_MGMT_Q) ? MGMT_EP : DATA_EP);
+
+ return rsi_write_multiple(adapter,
+ endpoint,
+ (u8 *)pkt,
+ len);
+}
+
+/**
+ * rsi_deinit_usb_interface() - This function deinitializes the usb interface.
+ * @adapter: Pointer to the adapter structure.
+ *
+ * Return: None.
+ */
+static void rsi_deinit_usb_interface(struct rsi_hw *adapter)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+
+ rsi_kill_thread(&dev->rx_thread);
+ kfree(adapter->priv->rx_data_pkt);
+ kfree(dev->tx_buffer);
+}
+
+/**
+ * rsi_init_usb_interface() - This function initializes the usb interface.
+ * @adapter: Pointer to the adapter structure.
+ * @pfunction: Pointer to USB interface structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_init_usb_interface(struct rsi_hw *adapter,
+ struct usb_interface *pfunction)
+{
+ struct rsi_91x_usbdev *rsi_dev;
+ struct rsi_common *common = adapter->priv;
+ int status;
+
+ rsi_dev = kzalloc(sizeof(*rsi_dev), GFP_KERNEL);
+ if (!rsi_dev)
+ return -ENOMEM;
+
+ adapter->rsi_dev = rsi_dev;
+ rsi_dev->usbdev = interface_to_usbdev(pfunction);
+
+ if (rsi_find_bulk_in_and_out_endpoints(pfunction, adapter))
+ return -EINVAL;
+
+ adapter->device = &pfunction->dev;
+ usb_set_intfdata(pfunction, adapter);
+
+ common->rx_data_pkt = kmalloc(2048, GFP_KERNEL);
+ if (!common->rx_data_pkt) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to allocate memory\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ rsi_dev->tx_buffer = kmalloc(2048, GFP_ATOMIC);
+ rsi_dev->rx_usb_urb[0] = usb_alloc_urb(0, GFP_KERNEL);
+ rsi_dev->rx_usb_urb[0]->transfer_buffer = adapter->priv->rx_data_pkt;
+ rsi_dev->tx_blk_size = 252;
+
+ /* Initializing function callbacks */
+ adapter->rx_urb_submit = rsi_rx_urb_submit;
+ adapter->host_intf_write_pkt = rsi_usb_host_intf_write_pkt;
+ adapter->check_hw_queue_status = rsi_usb_check_queue_status;
+ adapter->determine_event_timeout = rsi_usb_event_timeout;
+
+ rsi_init_event(&rsi_dev->rx_thread.event);
+ status = rsi_create_kthread(common, &rsi_dev->rx_thread,
+ rsi_usb_rx_thread, "RX-Thread");
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__);
+ goto fail;
+ }
+
+#ifdef CONFIG_RSI_DEBUGFS
+ /* In USB, one less than the MAX_DEBUGFS_ENTRIES entries is required */
+ adapter->num_debugfs_entries = (MAX_DEBUGFS_ENTRIES - 1);
+#endif
+
+ rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__);
+ return 0;
+
+fail:
+ kfree(rsi_dev->tx_buffer);
+ kfree(common->rx_data_pkt);
+ return status;
+}
+
+/**
+ * rsi_probe() - This function is called by kernel when the driver provided
+ * Vendor and device IDs are matched. All the initialization
+ * work is done here.
+ * @pfunction: Pointer to the USB interface structure.
+ * @id: Pointer to the usb_device_id structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int rsi_probe(struct usb_interface *pfunction,
+ const struct usb_device_id *id)
+{
+ struct rsi_hw *adapter;
+ struct rsi_91x_usbdev *dev;
+ u16 fw_status;
+
+ rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__);
+
+ adapter = rsi_91x_init();
+ if (!adapter) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n",
+ __func__);
+ return 1;
+ }
+
+ if (rsi_init_usb_interface(adapter, pfunction)) {
+ rsi_dbg(ERR_ZONE, "%s: Failed to init usb interface\n",
+ __func__);
+ goto err;
+ }
+
+ rsi_dbg(ERR_ZONE, "%s: Initialized os intf ops\n", __func__);
+
+ dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+
+ if (rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2) < 0)
+ goto err1;
+ else
+ fw_status &= 1;
+
+ if (!fw_status) {
+ if (rsi_usb_device_init(adapter->priv)) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in device init\n",
+ __func__);
+ goto err1;
+ }
+
+ if (rsi_usb_reg_write(dev->usbdev,
+ USB_INTERNAL_REG_1,
+ RSI_USB_READY_MAGIC_NUM, 1) < 0)
+ goto err1;
+ rsi_dbg(INIT_ZONE, "%s: Performed device init\n", __func__);
+ }
+
+ if (rsi_rx_urb_submit(adapter))
+ goto err1;
+
+ return 0;
+err1:
+ rsi_deinit_usb_interface(adapter);
+err:
+ rsi_91x_deinit(adapter);
+ rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__);
+ return 1;
+}
+
+/**
+ * rsi_disconnect() - This function performs the reverse of the probe function,
+ * it deintialize the driver structure.
+ * @pfunction: Pointer to the USB interface structure.
+ *
+ * Return: None.
+ */
+static void rsi_disconnect(struct usb_interface *pfunction)
+{
+ struct rsi_hw *adapter = usb_get_intfdata(pfunction);
+
+ if (!adapter)
+ return;
+
+ rsi_mac80211_detach(adapter);
+ rsi_deinit_usb_interface(adapter);
+ rsi_91x_deinit(adapter);
+
+ rsi_dbg(INFO_ZONE, "%s: Deinitialization completed\n", __func__);
+}
+
+#ifdef CONFIG_PM
+static int rsi_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ /* Not yet implemented */
+ return -ENOSYS;
+}
+
+static int rsi_resume(struct usb_interface *intf)
+{
+ /* Not yet implemented */
+ return -ENOSYS;
+}
+#endif
+
+static const struct usb_device_id rsi_dev_table[] = {
+ { USB_DEVICE(0x0303, 0x0100) },
+ { USB_DEVICE(0x041B, 0x0301) },
+ { USB_DEVICE(0x041B, 0x0201) },
+ { USB_DEVICE(0x041B, 0x9330) },
+ { /* Blank */},
+};
+
+static struct usb_driver rsi_driver = {
+ .name = "RSI-USB WLAN",
+ .probe = rsi_probe,
+ .disconnect = rsi_disconnect,
+ .id_table = rsi_dev_table,
+#ifdef CONFIG_PM
+ .suspend = rsi_suspend,
+ .resume = rsi_resume,
+#endif
+};
+
+/**
+ * rsi_module_init() - This function registers the client driver.
+ * @void: Void.
+ *
+ * Return: 0 on success.
+ */
+static int rsi_module_init(void)
+{
+ usb_register(&rsi_driver);
+ rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__);
+ return 0;
+}
+
+/**
+ * rsi_module_exit() - This function unregisters the client driver.
+ * @void: Void.
+ *
+ * Return: None.
+ */
+static void rsi_module_exit(void)
+{
+ usb_deregister(&rsi_driver);
+ rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__);
+}
+
+module_init(rsi_module_init);
+module_exit(rsi_module_exit);
+
+MODULE_AUTHOR("Redpine Signals Inc");
+MODULE_DESCRIPTION("Common USB layer for RSI drivers");
+MODULE_SUPPORTED_DEVICE("RSI-91x");
+MODULE_DEVICE_TABLE(usb, rsi_dev_table);
+MODULE_FIRMWARE(FIRMWARE_RSI9113);
+MODULE_VERSION("0.1");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c
new file mode 100644
index 000000000000..1106ce76707e
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c
@@ -0,0 +1,177 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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/firmware.h>
+#include "rsi_usb.h"
+
+/**
+ * rsi_copy_to_card() - This function includes the actual funtionality of
+ * copying the TA firmware to the card.Basically this
+ * function includes opening the TA file,reading the TA
+ * file and writing their values in blocks of data.
+ * @common: Pointer to the driver private structure.
+ * @fw: Pointer to the firmware value to be written.
+ * @len: length of firmware file.
+ * @num_blocks: Number of blocks to be written to the card.
+ *
+ * Return: 0 on success and -1 on failure.
+ */
+static int rsi_copy_to_card(struct rsi_common *common,
+ const u8 *fw,
+ u32 len,
+ u32 num_blocks)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ u32 indx, ii;
+ u32 block_size = dev->tx_blk_size;
+ u32 lsb_address;
+ u32 base_address;
+
+ base_address = TA_LOAD_ADDRESS;
+
+ for (indx = 0, ii = 0; ii < num_blocks; ii++, indx += block_size) {
+ lsb_address = base_address;
+ if (rsi_usb_write_register_multiple(adapter,
+ lsb_address,
+ (u8 *)(fw + indx),
+ block_size)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to load %s blk\n", __func__,
+ FIRMWARE_RSI9113);
+ return -EIO;
+ }
+ rsi_dbg(INIT_ZONE, "%s: loading block: %d\n", __func__, ii);
+ base_address += block_size;
+ }
+
+ if (len % block_size) {
+ lsb_address = base_address;
+ if (rsi_usb_write_register_multiple(adapter,
+ lsb_address,
+ (u8 *)(fw + indx),
+ len % block_size)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Unable to load %s blk\n", __func__,
+ FIRMWARE_RSI9113);
+ return -EIO;
+ }
+ }
+ rsi_dbg(INIT_ZONE,
+ "%s: Succesfully loaded %s instructions\n", __func__,
+ FIRMWARE_RSI9113);
+
+ rsi_dbg(INIT_ZONE, "%s: loaded firmware\n", __func__);
+ return 0;
+}
+
+/**
+ * rsi_usb_rx_thread() - This is a kernel thread to receive the packets from
+ * the USB device.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: None.
+ */
+void rsi_usb_rx_thread(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ int status;
+
+ do {
+ rsi_wait_event(&dev->rx_thread.event, EVENT_WAIT_FOREVER);
+
+ if (atomic_read(&dev->rx_thread.thread_done))
+ goto out;
+
+ mutex_lock(&common->tx_rxlock);
+ status = rsi_read_pkt(common, 0);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Failed To read data", __func__);
+ mutex_unlock(&common->tx_rxlock);
+ return;
+ }
+ mutex_unlock(&common->tx_rxlock);
+ rsi_reset_event(&dev->rx_thread.event);
+ if (adapter->rx_urb_submit(adapter)) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed in urb submission", __func__);
+ return;
+ }
+ } while (1);
+
+out:
+ rsi_dbg(INFO_ZONE, "%s: Terminated thread\n", __func__);
+ complete_and_exit(&dev->rx_thread.completion, 0);
+}
+
+
+/**
+ * rsi_load_ta_instructions() - This function includes the actual funtionality
+ * of loading the TA firmware.This function also
+ * includes opening the TA file,reading the TA
+ * file and writing their value in blocks of data.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: status: 0 on success, -1 on failure.
+ */
+static int rsi_load_ta_instructions(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ const struct firmware *fw_entry = NULL;
+ u32 block_size = dev->tx_blk_size;
+ const u8 *fw;
+ u32 num_blocks, len;
+ int status = 0;
+
+ status = request_firmware(&fw_entry, FIRMWARE_RSI9113, adapter->device);
+ if (status < 0) {
+ rsi_dbg(ERR_ZONE, "%s Firmware file %s not found\n",
+ __func__, FIRMWARE_RSI9113);
+ return status;
+ }
+
+ fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL);
+ len = fw_entry->size;
+
+ if (len % 4)
+ len += (4 - (len % 4));
+
+ num_blocks = (len / block_size);
+
+ rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len);
+ rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks);
+
+ status = rsi_copy_to_card(common, fw, len, num_blocks);
+ release_firmware(fw_entry);
+ return status;
+}
+
+/**
+ * rsi_device_init() - This Function Initializes The HAL.
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int rsi_usb_device_init(struct rsi_common *common)
+{
+ if (rsi_load_ta_instructions(common))
+ return -EIO;
+
+ return 0;
+ }
diff --git a/drivers/net/wireless/rsi/rsi_boot_params.h b/drivers/net/wireless/rsi/rsi_boot_params.h
new file mode 100644
index 000000000000..5e2721f7909c
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_boot_params.h
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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.
+ */
+
+#ifndef __RSI_BOOTPARAMS_HEADER_H__
+#define __RSI_BOOTPARAMS_HEADER_H__
+
+#define CRYSTAL_GOOD_TIME BIT(0)
+#define BOOTUP_MODE_INFO BIT(1)
+#define WIFI_TAPLL_CONFIGS BIT(5)
+#define WIFI_PLL960_CONFIGS BIT(6)
+#define WIFI_AFEPLL_CONFIGS BIT(7)
+#define WIFI_SWITCH_CLK_CONFIGS BIT(8)
+
+#define TA_PLL_M_VAL_20 8
+#define TA_PLL_N_VAL_20 1
+#define TA_PLL_P_VAL_20 4
+
+#define PLL960_M_VAL_20 0x14
+#define PLL960_N_VAL_20 0
+#define PLL960_P_VAL_20 5
+
+#define UMAC_CLK_40MHZ 40
+
+#define TA_PLL_M_VAL_40 46
+#define TA_PLL_N_VAL_40 3
+#define TA_PLL_P_VAL_40 3
+
+#define PLL960_M_VAL_40 0x14
+#define PLL960_N_VAL_40 0
+#define PLL960_P_VAL_40 5
+
+#define UMAC_CLK_20BW \
+ (((TA_PLL_M_VAL_20 + 1) * 40) / \
+ ((TA_PLL_N_VAL_20 + 1) * (TA_PLL_P_VAL_20 + 1)))
+#define VALID_20 \
+ (WIFI_PLL960_CONFIGS | WIFI_AFEPLL_CONFIGS | WIFI_SWITCH_CLK_CONFIGS)
+#define UMAC_CLK_40BW \
+ (((TA_PLL_M_VAL_40 + 1) * 40) / \
+ ((TA_PLL_N_VAL_40 + 1) * (TA_PLL_P_VAL_40 + 1)))
+#define VALID_40 \
+ (WIFI_PLL960_CONFIGS | WIFI_AFEPLL_CONFIGS | WIFI_SWITCH_CLK_CONFIGS | \
+ WIFI_TAPLL_CONFIGS | CRYSTAL_GOOD_TIME | BOOTUP_MODE_INFO)
+
+/* structure to store configs related to TAPLL programming */
+struct tapll_info {
+ __le16 pll_reg_1;
+ __le16 pll_reg_2;
+} __packed;
+
+/* structure to store configs related to PLL960 programming */
+struct pll960_info {
+ __le16 pll_reg_1;
+ __le16 pll_reg_2;
+ __le16 pll_reg_3;
+} __packed;
+
+/* structure to store configs related to AFEPLL programming */
+struct afepll_info {
+ __le16 pll_reg;
+} __packed;
+
+/* structure to store configs related to pll configs */
+struct pll_config {
+ struct tapll_info tapll_info_g;
+ struct pll960_info pll960_info_g;
+ struct afepll_info afepll_info_g;
+} __packed;
+
+/* structure to store configs related to UMAC clk programming */
+struct switch_clk {
+ __le16 switch_clk_info;
+ /* If switch_bbp_lmac_clk_reg is set then this value will be programmed
+ * into reg
+ */
+ __le16 bbp_lmac_clk_reg_val;
+ /* if switch_umac_clk is set then this value will be programmed */
+ __le16 umac_clock_reg_config;
+ /* if switch_qspi_clk is set then this value will be programmed */
+ __le16 qspi_uart_clock_reg_config;
+} __packed;
+
+struct device_clk_info {
+ struct pll_config pll_config_g;
+ struct switch_clk switch_clk_g;
+} __packed;
+
+struct bootup_params {
+ __le16 magic_number;
+ __le16 crystal_good_time;
+ __le32 valid;
+ __le32 reserved_for_valids;
+ __le16 bootup_mode_info;
+ /* configuration used for digital loop back */
+ __le16 digital_loop_back_params;
+ __le16 rtls_timestamp_en;
+ __le16 host_spi_intr_cfg;
+ struct device_clk_info device_clk_info[3];
+ /* ulp buckboost wait time */
+ __le32 buckboost_wakeup_cnt;
+ /* pmu wakeup wait time & WDT EN info */
+ __le16 pmu_wakeup_wait;
+ u8 shutdown_wait_time;
+ /* Sleep clock source selection */
+ u8 pmu_slp_clkout_sel;
+ /* WDT programming values */
+ __le32 wdt_prog_value;
+ /* WDT soc reset delay */
+ __le32 wdt_soc_rst_delay;
+ /* dcdc modes configs */
+ __le32 dcdc_operation_mode;
+ __le32 soc_reset_wait_cnt;
+} __packed;
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h
new file mode 100644
index 000000000000..f2f70784d4ad
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_common.h
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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.
+ */
+
+#ifndef __RSI_COMMON_H__
+#define __RSI_COMMON_H__
+
+#include <linux/kthread.h>
+
+#define EVENT_WAIT_FOREVER 0
+#define TA_LOAD_ADDRESS 0x00
+#define FIRMWARE_RSI9113 "rsi_91x.fw"
+#define QUEUE_NOT_FULL 1
+#define QUEUE_FULL 0
+
+static inline int rsi_init_event(struct rsi_event *pevent)
+{
+ atomic_set(&pevent->event_condition, 1);
+ init_waitqueue_head(&pevent->event_queue);
+ return 0;
+}
+
+static inline int rsi_wait_event(struct rsi_event *event, u32 timeout)
+{
+ int status = 0;
+
+ if (!timeout)
+ status = wait_event_interruptible(event->event_queue,
+ (atomic_read(&event->event_condition) == 0));
+ else
+ status = wait_event_interruptible_timeout(event->event_queue,
+ (atomic_read(&event->event_condition) == 0),
+ timeout);
+ return status;
+}
+
+static inline void rsi_set_event(struct rsi_event *event)
+{
+ atomic_set(&event->event_condition, 0);
+ wake_up_interruptible(&event->event_queue);
+}
+
+static inline void rsi_reset_event(struct rsi_event *event)
+{
+ atomic_set(&event->event_condition, 1);
+}
+
+static inline int rsi_create_kthread(struct rsi_common *common,
+ struct rsi_thread *thread,
+ void *func_ptr,
+ u8 *name)
+{
+ init_completion(&thread->completion);
+ thread->task = kthread_run(func_ptr, common, name);
+ if (IS_ERR(thread->task))
+ return (int)PTR_ERR(thread->task);
+
+ return 0;
+}
+
+static inline int rsi_kill_thread(struct rsi_thread *handle)
+{
+ atomic_inc(&handle->thread_done);
+ rsi_set_event(&handle->event);
+
+ wait_for_completion(&handle->completion);
+ return kthread_stop(handle->task);
+}
+
+void rsi_mac80211_detach(struct rsi_hw *hw);
+u16 rsi_get_connected_channel(struct rsi_hw *adapter);
+struct rsi_hw *rsi_91x_init(void);
+void rsi_91x_deinit(struct rsi_hw *adapter);
+int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len);
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_debugfs.h b/drivers/net/wireless/rsi/rsi_debugfs.h
new file mode 100644
index 000000000000..580ad3b3f710
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_debugfs.h
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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.
+ */
+
+#ifndef __RSI_DEBUGFS_H__
+#define __RSI_DEBUGFS_H__
+
+#include "rsi_main.h"
+#include <linux/debugfs.h>
+
+#ifndef CONFIG_RSI_DEBUGFS
+static inline int rsi_init_dbgfs(struct rsi_hw *adapter)
+{
+ return 0;
+}
+
+static inline void rsi_remove_dbgfs(struct rsi_hw *adapter)
+{
+ return;
+}
+#else
+struct rsi_dbg_files {
+ const char *name;
+ umode_t perms;
+ const struct file_operations fops;
+};
+
+struct rsi_debugfs {
+ struct dentry *subdir;
+ struct rsi_dbg_ops *dfs_get_ops;
+ struct dentry *rsi_files[MAX_DEBUGFS_ENTRIES];
+};
+int rsi_init_dbgfs(struct rsi_hw *adapter);
+void rsi_remove_dbgfs(struct rsi_hw *adapter);
+#endif
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h
new file mode 100644
index 000000000000..2cb73e7edb98
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_main.h
@@ -0,0 +1,218 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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.
+ */
+
+#ifndef __RSI_MAIN_H__
+#define __RSI_MAIN_H__
+
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <net/mac80211.h>
+
+#define ERR_ZONE BIT(0) /* For Error Msgs */
+#define INFO_ZONE BIT(1) /* For General Status Msgs */
+#define INIT_ZONE BIT(2) /* For Driver Init Seq Msgs */
+#define MGMT_TX_ZONE BIT(3) /* For TX Mgmt Path Msgs */
+#define MGMT_RX_ZONE BIT(4) /* For RX Mgmt Path Msgs */
+#define DATA_TX_ZONE BIT(5) /* For TX Data Path Msgs */
+#define DATA_RX_ZONE BIT(6) /* For RX Data Path Msgs */
+#define FSM_ZONE BIT(7) /* For State Machine Msgs */
+#define ISR_ZONE BIT(8) /* For Interrupt Msgs */
+
+#define FSM_CARD_NOT_READY 0
+#define FSM_BOOT_PARAMS_SENT 1
+#define FSM_EEPROM_READ_MAC_ADDR 2
+#define FSM_RESET_MAC_SENT 3
+#define FSM_RADIO_CAPS_SENT 4
+#define FSM_BB_RF_PROG_SENT 5
+#define FSM_MAC_INIT_DONE 6
+
+extern u32 rsi_zone_enabled;
+extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...);
+
+#define RSI_MAX_VIFS 1
+#define NUM_EDCA_QUEUES 4
+#define IEEE80211_ADDR_LEN 6
+#define FRAME_DESC_SZ 16
+#define MIN_802_11_HDR_LEN 24
+
+#define DATA_QUEUE_WATER_MARK 400
+#define MIN_DATA_QUEUE_WATER_MARK 300
+#define MULTICAST_WATER_MARK 200
+#define MAC_80211_HDR_FRAME_CONTROL 0
+#define WME_NUM_AC 4
+#define NUM_SOFT_QUEUES 5
+#define MAX_HW_QUEUES 8
+#define INVALID_QUEUE 0xff
+#define MAX_CONTINUOUS_VO_PKTS 8
+#define MAX_CONTINUOUS_VI_PKTS 4
+
+/* Queue information */
+#define RSI_WIFI_MGMT_Q 0x4
+#define RSI_WIFI_DATA_Q 0x5
+#define IEEE80211_MGMT_FRAME 0x00
+#define IEEE80211_CTL_FRAME 0x04
+
+#define IEEE80211_QOS_TID 0x0f
+#define IEEE80211_NONQOS_TID 16
+
+#define MAX_DEBUGFS_ENTRIES 4
+
+#define TID_TO_WME_AC(_tid) ( \
+ ((_tid) == 0 || (_tid) == 3) ? BE_Q : \
+ ((_tid) < 3) ? BK_Q : \
+ ((_tid) < 6) ? VI_Q : \
+ VO_Q)
+
+#define WME_AC(_q) ( \
+ ((_q) == BK_Q) ? IEEE80211_AC_BK : \
+ ((_q) == BE_Q) ? IEEE80211_AC_BE : \
+ ((_q) == VI_Q) ? IEEE80211_AC_VI : \
+ IEEE80211_AC_VO)
+
+struct version_info {
+ u16 major;
+ u16 minor;
+ u16 release_num;
+ u16 patch_num;
+} __packed;
+
+struct skb_info {
+ s8 rssi;
+ u32 flags;
+ u16 channel;
+ s8 tid;
+ s8 sta_id;
+};
+
+enum edca_queue {
+ BK_Q,
+ BE_Q,
+ VI_Q,
+ VO_Q,
+ MGMT_SOFT_Q
+};
+
+struct security_info {
+ bool security_enable;
+ u32 ptk_cipher;
+ u32 gtk_cipher;
+};
+
+struct wmm_qinfo {
+ s32 weight;
+ s32 wme_params;
+ s32 pkt_contended;
+};
+
+struct transmit_q_stats {
+ u32 total_tx_pkt_send[NUM_EDCA_QUEUES + 1];
+ u32 total_tx_pkt_freed[NUM_EDCA_QUEUES + 1];
+};
+
+struct vif_priv {
+ bool is_ht;
+ bool sgi;
+ u16 seq_start;
+};
+
+struct rsi_event {
+ atomic_t event_condition;
+ wait_queue_head_t event_queue;
+};
+
+struct rsi_thread {
+ void (*thread_function)(void *);
+ struct completion completion;
+ struct task_struct *task;
+ struct rsi_event event;
+ atomic_t thread_done;
+};
+
+struct rsi_hw;
+
+struct rsi_common {
+ struct rsi_hw *priv;
+ struct vif_priv vif_info[RSI_MAX_VIFS];
+
+ bool mgmt_q_block;
+ struct version_info driver_ver;
+ struct version_info fw_ver;
+
+ struct rsi_thread tx_thread;
+ struct sk_buff_head tx_queue[NUM_EDCA_QUEUES + 1];
+ /* Mutex declaration */
+ struct mutex mutex;
+ /* Mutex used between tx/rx threads */
+ struct mutex tx_rxlock;
+ u8 endpoint;
+
+ /* Channel/band related */
+ u8 band;
+ u8 channel_width;
+
+ u16 rts_threshold;
+ u16 bitrate_mask[2];
+ u32 fixedrate_mask[2];
+
+ u8 rf_reset;
+ struct transmit_q_stats tx_stats;
+ struct security_info secinfo;
+ struct wmm_qinfo tx_qinfo[NUM_EDCA_QUEUES];
+ struct ieee80211_tx_queue_params edca_params[NUM_EDCA_QUEUES];
+ u8 mac_addr[IEEE80211_ADDR_LEN];
+
+ /* state related */
+ u32 fsm_state;
+ bool init_done;
+ u8 bb_rf_prog_count;
+ bool iface_down;
+
+ /* Generic */
+ u8 channel;
+ u8 *rx_data_pkt;
+ u8 mac_id;
+ u8 radio_id;
+ u16 rate_pwr[20];
+ u16 min_rate;
+
+ /* WMM algo related */
+ u8 selected_qnum;
+ u32 pkt_cnt;
+ u8 min_weight;
+};
+
+struct rsi_hw {
+ struct rsi_common *priv;
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vifs[RSI_MAX_VIFS];
+ struct ieee80211_tx_queue_params edca_params[NUM_EDCA_QUEUES];
+ struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+
+ struct device *device;
+ u8 sc_nvifs;
+
+#ifdef CONFIG_RSI_DEBUGFS
+ struct rsi_debugfs *dfsentry;
+ u8 num_debugfs_entries;
+#endif
+ void *rsi_dev;
+ int (*host_intf_read_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len);
+ int (*host_intf_write_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len);
+ int (*check_hw_queue_status)(struct rsi_hw *adapter, u8 q_num);
+ int (*rx_urb_submit)(struct rsi_hw *adapter);
+ int (*determine_event_timeout)(struct rsi_hw *adapter);
+};
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h
new file mode 100644
index 000000000000..ac67c4ad63c2
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_mgmt.h
@@ -0,0 +1,285 @@
+/**
+ * Copyright (c) 2014 Redpine Signals 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.
+ */
+
+#ifndef __RSI_MGMT_H__
+#define __RSI_MGMT_H__
+
+#include <linux/sort.h>
+#include "rsi_boot_params.h"
+#include "rsi_main.h"
+
+#define MAX_MGMT_PKT_SIZE 512
+#define RSI_NEEDED_HEADROOM 80
+#define RSI_RCV_BUFFER_LEN 2000
+
+#define RSI_11B_MODE 0
+#define RSI_11G_MODE BIT(7)
+#define RETRY_COUNT 8
+#define RETRY_LONG 4
+#define RETRY_SHORT 7
+#define WMM_SHORT_SLOT_TIME 9
+#define SIFS_DURATION 16
+
+#define KEY_TYPE_CLEAR 0
+#define RSI_PAIRWISE_KEY 1
+#define RSI_GROUP_KEY 2
+
+/* EPPROM_READ_ADDRESS */
+#define WLAN_MAC_EEPROM_ADDR 40
+#define WLAN_MAC_MAGIC_WORD_LEN 0x01
+#define WLAN_HOST_MODE_LEN 0x04
+#define WLAN_FW_VERSION_LEN 0x08
+#define MAGIC_WORD 0x5A
+
+/* Receive Frame Types */
+#define TA_CONFIRM_TYPE 0x01
+#define RX_DOT11_MGMT 0x02
+#define TX_STATUS_IND 0x04
+#define PROBEREQ_CONFIRM 2
+#define CARD_READY_IND 0x00
+
+#define RSI_DELETE_PEER 0x0
+#define RSI_ADD_PEER 0x1
+#define START_AMPDU_AGGR 0x1
+#define STOP_AMPDU_AGGR 0x0
+#define INTERNAL_MGMT_PKT 0x99
+
+#define PUT_BBP_RESET 0
+#define BBP_REG_WRITE 0
+#define RF_RESET_ENABLE BIT(3)
+#define RATE_INFO_ENABLE BIT(0)
+#define RSI_BROADCAST_PKT BIT(9)
+
+#define UPPER_20_ENABLE (0x2 << 12)
+#define LOWER_20_ENABLE (0x4 << 12)
+#define FULL40M_ENABLE 0x6
+
+#define RSI_LMAC_CLOCK_80MHZ 0x1
+#define RSI_ENABLE_40MHZ (0x1 << 3)
+
+#define RX_BA_INDICATION 1
+#define RSI_TBL_SZ 40
+#define MAX_RETRIES 8
+
+#define STD_RATE_MCS7 0x07
+#define STD_RATE_MCS6 0x06
+#define STD_RATE_MCS5 0x05
+#define STD_RATE_MCS4 0x04
+#define STD_RATE_MCS3 0x03
+#define STD_RATE_MCS2 0x02
+#define STD_RATE_MCS1 0x01
+#define STD_RATE_MCS0 0x00
+#define STD_RATE_54 0x6c
+#define STD_RATE_48 0x60
+#define STD_RATE_36 0x48
+#define STD_RATE_24 0x30
+#define STD_RATE_18 0x24
+#define STD_RATE_12 0x18
+#define STD_RATE_11 0x16
+#define STD_RATE_09 0x12
+#define STD_RATE_06 0x0C
+#define STD_RATE_5_5 0x0B
+#define STD_RATE_02 0x04
+#define STD_RATE_01 0x02
+
+#define RSI_RF_TYPE 1
+#define RSI_RATE_00 0x00
+#define RSI_RATE_1 0x0
+#define RSI_RATE_2 0x2
+#define RSI_RATE_5_5 0x4
+#define RSI_RATE_11 0x6
+#define RSI_RATE_6 0x8b
+#define RSI_RATE_9 0x8f
+#define RSI_RATE_12 0x8a
+#define RSI_RATE_18 0x8e
+#define RSI_RATE_24 0x89
+#define RSI_RATE_36 0x8d
+#define RSI_RATE_48 0x88
+#define RSI_RATE_54 0x8c
+#define RSI_RATE_MCS0 0x100
+#define RSI_RATE_MCS1 0x101
+#define RSI_RATE_MCS2 0x102
+#define RSI_RATE_MCS3 0x103
+#define RSI_RATE_MCS4 0x104
+#define RSI_RATE_MCS5 0x105
+#define RSI_RATE_MCS6 0x106
+#define RSI_RATE_MCS7 0x107
+#define RSI_RATE_MCS7_SG 0x307
+
+#define BW_20MHZ 0
+#define BW_40MHZ 1
+
+#define RSI_SUPP_FILTERS (FIF_ALLMULTI | FIF_PROBE_REQ |\
+ FIF_BCN_PRBRESP_PROMISC)
+enum opmode {
+ STA_OPMODE = 1,
+ AP_OPMODE = 2
+};
+
+extern struct ieee80211_rate rsi_rates[12];
+extern const u16 rsi_mcsrates[8];
+
+enum sta_notify_events {
+ STA_CONNECTED = 0,
+ STA_DISCONNECTED,
+ STA_TX_ADDBA_DONE,
+ STA_TX_DELBA,
+ STA_RX_ADDBA_DONE,
+ STA_RX_DELBA
+};
+
+/* Send Frames Types */
+enum cmd_frame_type {
+ TX_DOT11_MGMT,
+ RESET_MAC_REQ,
+ RADIO_CAPABILITIES,
+ BB_PROG_VALUES_REQUEST,
+ RF_PROG_VALUES_REQUEST,
+ WAKEUP_SLEEP_REQUEST,
+ SCAN_REQUEST,
+ TSF_UPDATE,
+ PEER_NOTIFY,
+ BLOCK_UNBLOCK,
+ SET_KEY_REQ,
+ AUTO_RATE_IND,
+ BOOTUP_PARAMS_REQUEST,
+ VAP_CAPABILITIES,
+ EEPROM_READ_TYPE ,
+ EEPROM_WRITE,
+ GPIO_PIN_CONFIG ,
+ SET_RX_FILTER,
+ AMPDU_IND,
+ STATS_REQUEST_FRAME,
+ BB_BUF_PROG_VALUES_REQ,
+ BBP_PROG_IN_TA,
+ BG_SCAN_PARAMS,
+ BG_SCAN_PROBE_REQ,
+ CW_MODE_REQ,
+ PER_CMD_PKT
+};
+
+struct rsi_mac_frame {
+ __le16 desc_word[8];
+} __packed;
+
+struct rsi_boot_params {
+ __le16 desc_word[8];
+ struct bootup_params bootup_params;
+} __packed;
+
+struct rsi_peer_notify {
+ __le16 desc_word[8];
+ u8 mac_addr[6];
+ __le16 command;
+ __le16 mpdu_density;
+ __le16 reserved;
+ __le32 sta_flags;
+} __packed;
+
+struct rsi_vap_caps {
+ __le16 desc_word[8];
+ u8 mac_addr[6];
+ __le16 keep_alive_period;
+ u8 bssid[6];
+ __le16 reserved;
+ __le32 flags;
+ __le16 frag_threshold;
+ __le16 rts_threshold;
+ __le32 default_mgmt_rate;
+ __le32 default_ctrl_rate;
+ __le32 default_data_rate;
+ __le16 beacon_interval;
+ __le16 dtim_period;
+} __packed;
+
+struct rsi_set_key {
+ __le16 desc_word[8];
+ u8 key[4][32];
+ u8 tx_mic_key[8];
+ u8 rx_mic_key[8];
+} __packed;
+
+struct rsi_auto_rate {
+ __le16 desc_word[8];
+ __le16 failure_limit;
+ __le16 initial_boundary;
+ __le16 max_threshold_limt;
+ __le16 num_supported_rates;
+ __le16 aarf_rssi;
+ __le16 moderate_rate_inx;
+ __le16 collision_tolerance;
+ __le16 supported_rates[40];
+} __packed;
+
+struct qos_params {
+ __le16 cont_win_min_q;
+ __le16 cont_win_max_q;
+ __le16 aifsn_val_q;
+ __le16 txop_q;
+} __packed;
+
+struct rsi_radio_caps {
+ __le16 desc_word[8];
+ struct qos_params qos_params[MAX_HW_QUEUES];
+ u8 num_11n_rates;
+ u8 num_11ac_rates;
+ __le16 gcpd_per_rate[20];
+} __packed;
+
+static inline u32 rsi_get_queueno(u8 *addr, u16 offset)
+{
+ return (le16_to_cpu(*(__le16 *)&addr[offset]) & 0x7000) >> 12;
+}
+
+static inline u32 rsi_get_length(u8 *addr, u16 offset)
+{
+ return (le16_to_cpu(*(__le16 *)&addr[offset])) & 0x0fff;
+}
+
+static inline u8 rsi_get_extended_desc(u8 *addr, u16 offset)
+{
+ return le16_to_cpu(*((__le16 *)&addr[offset + 4])) & 0x00ff;
+}
+
+static inline u8 rsi_get_rssi(u8 *addr)
+{
+ return *(u8 *)(addr + FRAME_DESC_SZ);
+}
+
+static inline u8 rsi_get_channel(u8 *addr)
+{
+ return *(char *)(addr + 15);
+}
+
+int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg);
+int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode);
+int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid,
+ u16 ssn, u8 buf_size, u8 event);
+int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len,
+ u8 key_type, u8 key_id, u32 cipher);
+int rsi_set_channel(struct rsi_common *common, u16 chno);
+void rsi_inform_bss_status(struct rsi_common *common, u8 status,
+ const u8 *bssid, u8 qos_enable, u16 aid);
+void rsi_indicate_pkt_to_os(struct rsi_common *common, struct sk_buff *skb);
+int rsi_mac80211_attach(struct rsi_common *common);
+void rsi_indicate_tx_status(struct rsi_hw *common, struct sk_buff *skb,
+ int status);
+bool rsi_is_cipher_wep(struct rsi_common *common);
+void rsi_core_qos_processor(struct rsi_common *common);
+void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb);
+int rsi_send_mgmt_pkt(struct rsi_common *common, struct sk_buff *skb);
+int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb);
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h
new file mode 100644
index 000000000000..df4b5e20e05f
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_sdio.h
@@ -0,0 +1,129 @@
+/**
+ * @section LICENSE
+ * Copyright (c) 2014 Redpine Signals 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.
+ *
+ */
+
+#ifndef __RSI_SDIO_INTF__
+#define __RSI_SDIO_INTF__
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio_ids.h>
+#include "rsi_main.h"
+
+enum sdio_interrupt_type {
+ BUFFER_FULL = 0x0,
+ BUFFER_AVAILABLE = 0x1,
+ FIRMWARE_ASSERT_IND = 0x3,
+ MSDU_PACKET_PENDING = 0x4,
+ UNKNOWN_INT = 0XE
+};
+
+/* Buffer status register related info */
+#define PKT_BUFF_SEMI_FULL 0
+#define PKT_BUFF_FULL 1
+#define PKT_MGMT_BUFF_FULL 2
+#define MSDU_PKT_PENDING 3
+/* Interrupt Bit Related Macros */
+#define PKT_BUFF_AVAILABLE 0
+#define FW_ASSERT_IND 2
+
+#define RSI_DEVICE_BUFFER_STATUS_REGISTER 0xf3
+#define RSI_FN1_INT_REGISTER 0xf9
+#define RSI_SD_REQUEST_MASTER 0x10000
+
+/* FOR SD CARD ONLY */
+#define SDIO_RX_NUM_BLOCKS_REG 0x000F1
+#define SDIO_FW_STATUS_REG 0x000F2
+#define SDIO_NXT_RD_DELAY2 0x000F5
+#define SDIO_MASTER_ACCESS_MSBYTE 0x000FA
+#define SDIO_MASTER_ACCESS_LSBYTE 0x000FB
+#define SDIO_READ_START_LVL 0x000FC
+#define SDIO_READ_FIFO_CTL 0x000FD
+#define SDIO_WRITE_FIFO_CTL 0x000FE
+#define SDIO_FUN1_INTR_CLR_REG 0x0008
+#define SDIO_REG_HIGH_SPEED 0x0013
+
+#define RSI_GET_SDIO_INTERRUPT_TYPE(_I, TYPE) \
+ { \
+ TYPE = \
+ (_I & (1 << PKT_BUFF_AVAILABLE)) ? \
+ BUFFER_AVAILABLE : \
+ (_I & (1 << MSDU_PKT_PENDING)) ? \
+ MSDU_PACKET_PENDING : \
+ (_I & (1 << FW_ASSERT_IND)) ? \
+ FIRMWARE_ASSERT_IND : UNKNOWN_INT; \
+ }
+
+/* common registers in SDIO function1 */
+#define TA_SOFT_RESET_REG 0x0004
+#define TA_TH0_PC_REG 0x0400
+#define TA_HOLD_THREAD_REG 0x0844
+#define TA_RELEASE_THREAD_REG 0x0848
+
+#define TA_SOFT_RST_CLR 0
+#define TA_SOFT_RST_SET BIT(0)
+#define TA_PC_ZERO 0
+#define TA_HOLD_THREAD_VALUE cpu_to_le32(0xF)
+#define TA_RELEASE_THREAD_VALUE cpu_to_le32(0xF)
+#define TA_BASE_ADDR 0x2200
+#define MISC_CFG_BASE_ADDR 0x4150
+
+struct receive_info {
+ bool buffer_full;
+ bool semi_buffer_full;
+ bool mgmt_buffer_full;
+ u32 mgmt_buf_full_counter;
+ u32 buf_semi_full_counter;
+ u8 watch_bufferfull_count;
+ u32 sdio_intr_status_zero;
+ u32 sdio_int_counter;
+ u32 total_sdio_msdu_pending_intr;
+ u32 total_sdio_unknown_intr;
+ u32 buf_full_counter;
+ u32 buf_avilable_counter;
+};
+
+struct rsi_91x_sdiodev {
+ struct sdio_func *pfunction;
+ struct task_struct *in_sdio_litefi_irq;
+ struct receive_info rx_info;
+ u32 next_read_delay;
+ u32 sdio_high_speed_enable;
+ u8 sdio_clock_speed;
+ u32 cardcapability;
+ u8 prev_desc[16];
+ u32 tx_blk_size;
+ u8 write_fail;
+};
+
+void rsi_interrupt_handler(struct rsi_hw *adapter);
+int rsi_init_sdio_slave_regs(struct rsi_hw *adapter);
+int rsi_sdio_device_init(struct rsi_common *common);
+int rsi_sdio_read_register(struct rsi_hw *adapter, u32 addr, u8 *data);
+int rsi_sdio_host_intf_read_pkt(struct rsi_hw *adapter, u8 *pkt, u32 length);
+int rsi_sdio_write_register(struct rsi_hw *adapter, u8 function,
+ u32 addr, u8 *data);
+int rsi_sdio_write_register_multiple(struct rsi_hw *adapter, u32 addr,
+ u8 *data, u32 count);
+void rsi_sdio_ack_intr(struct rsi_hw *adapter, u8 int_bit);
+int rsi_sdio_determine_event_timeout(struct rsi_hw *adapter);
+int rsi_sdio_read_buffer_status_register(struct rsi_hw *adapter, u8 q_num);
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_usb.h b/drivers/net/wireless/rsi/rsi_usb.h
new file mode 100644
index 000000000000..ebea0c411ead
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_usb.h
@@ -0,0 +1,68 @@
+/**
+ * @section LICENSE
+ * Copyright (c) 2014 Redpine Signals 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.
+ */
+
+#ifndef __RSI_USB_INTF__
+#define __RSI_USB_INTF__
+
+#include <linux/usb.h>
+#include "rsi_main.h"
+#include "rsi_common.h"
+
+#define USB_INTERNAL_REG_1 0x25000
+#define RSI_USB_READY_MAGIC_NUM 0xab
+#define FW_STATUS_REG 0x41050012
+
+#define USB_VENDOR_REGISTER_READ 0x15
+#define USB_VENDOR_REGISTER_WRITE 0x16
+#define RSI_USB_TX_HEAD_ROOM 128
+
+#define MAX_RX_URBS 1
+#define MAX_BULK_EP 8
+#define MGMT_EP 1
+#define DATA_EP 2
+
+struct rsi_91x_usbdev {
+ struct rsi_thread rx_thread;
+ u8 endpoint;
+ struct usb_device *usbdev;
+ struct usb_interface *pfunction;
+ struct urb *rx_usb_urb[MAX_RX_URBS];
+ u8 *tx_buffer;
+ __le16 bulkin_size;
+ u8 bulkin_endpoint_addr;
+ __le16 bulkout_size[MAX_BULK_EP];
+ u8 bulkout_endpoint_addr[MAX_BULK_EP];
+ u32 tx_blk_size;
+ u8 write_fail;
+};
+
+static inline int rsi_usb_check_queue_status(struct rsi_hw *adapter, u8 q_num)
+{
+ /* In USB, there isn't any need to check the queue status */
+ return QUEUE_NOT_FULL;
+}
+
+static inline int rsi_usb_event_timeout(struct rsi_hw *adapter)
+{
+ return EVENT_WAIT_FOREVER;
+}
+
+int rsi_usb_device_init(struct rsi_common *common);
+int rsi_usb_write_register_multiple(struct rsi_hw *adapter, u32 addr,
+ u8 *data, u32 count);
+void rsi_usb_rx_thread(struct rsi_common *common);
+#endif
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 42a2e06512f2..a49c3d73ea2c 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -125,9 +125,9 @@ static inline bool rt2800usb_entry_txstatus_timeout(struct queue_entry *entry)
tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(100));
if (unlikely(tout))
- rt2x00_warn(entry->queue->rt2x00dev,
- "TX status timeout for entry %d in queue %d\n",
- entry->entry_idx, entry->queue->qid);
+ rt2x00_dbg(entry->queue->rt2x00dev,
+ "TX status timeout for entry %d in queue %d\n",
+ entry->entry_idx, entry->queue->qid);
return tout;
}
@@ -566,8 +566,8 @@ static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev)
queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
if (unlikely(rt2x00queue_empty(queue))) {
- rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n",
- qid);
+ rt2x00_dbg(rt2x00dev, "Got TX status for an empty queue %u, dropping\n",
+ qid);
break;
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c
index 2e3d1645e68b..90fdb02b55e7 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/rt2x00/rt2x00debug.c
@@ -286,7 +286,7 @@ static ssize_t rt2x00debug_read_queue_dump(struct file *file,
if (retval)
return retval;
- status = min((size_t)skb->len, length);
+ status = min_t(size_t, skb->len, length);
if (copy_to_user(buf, skb->data, status)) {
status = -EFAULT;
goto exit;
diff --git a/drivers/net/wireless/rtl818x/Kconfig b/drivers/net/wireless/rtl818x/Kconfig
index 30332175bcd8..1ce1d55f0010 100644
--- a/drivers/net/wireless/rtl818x/Kconfig
+++ b/drivers/net/wireless/rtl818x/Kconfig
@@ -2,11 +2,11 @@
# RTL818X Wireless LAN device configuration
#
config RTL8180
- tristate "Realtek 8180/8185 PCI support"
+ tristate "Realtek 8180/8185/8187SE PCI support"
depends on MAC80211 && PCI
select EEPROM_93CX6
---help---
- This is a driver for RTL8180 and RTL8185 based cards.
+ This is a driver for RTL8180, RTL8185 and RTL8187SE based cards.
These are PCI based chips found in cards such as:
(RTL8185 802.11g)
diff --git a/drivers/net/wireless/rtl818x/rtl8180/Makefile b/drivers/net/wireless/rtl818x/rtl8180/Makefile
index cb4fb8596f0b..08b056db4a3b 100644
--- a/drivers/net/wireless/rtl818x/rtl8180/Makefile
+++ b/drivers/net/wireless/rtl818x/rtl8180/Makefile
@@ -1,4 +1,4 @@
-rtl8180-objs := dev.o rtl8225.o sa2400.o max2820.o grf5101.o
+rtl8180-objs := dev.o rtl8225.o sa2400.o max2820.o grf5101.o rtl8225se.o
obj-$(CONFIG_RTL8180) += rtl8180.o
diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c
index 3867d1470b36..98d8256f0377 100644
--- a/drivers/net/wireless/rtl818x/rtl8180/dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c
@@ -1,15 +1,42 @@
-/*
- * Linux device driver for RTL8180 / RTL8185
+/* Linux device driver for RTL8180 / RTL8185 / RTL8187SE
*
* Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
+ * Copyright 2007,2014 Andrea Merello <andrea.merello@gmail.com>
*
* Based on the r8180 driver, which is:
* Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
*
* Thanks to Realtek for their support!
*
+ ************************************************************************
+ *
+ * The driver was extended to the RTL8187SE in 2014 by
+ * Andrea Merello <andrea.merello@gmail.com>
+ *
+ * based also on:
+ * - portions of rtl8187se Linux staging driver, Copyright Realtek corp.
+ * - other GPL, unpublished (until now), Linux driver code,
+ * Copyright Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ * A huge thanks goes to Sara V. Nari who forgives me when I'm
+ * sitting in front of my laptop at evening, week-end, night...
+ *
+ * A special thanks goes to Antonio Cuni, who helped me with
+ * some python userspace stuff I used to debug RTL8187SE code, and who
+ * bought a laptop with an unsupported Wi-Fi card some years ago...
+ *
+ * Thanks to Larry Finger for writing some code for rtl8187se and for
+ * his suggestions.
+ *
+ * Thanks to Dan Carpenter for reviewing my initial patch and for his
+ * suggestions.
+ *
+ * Thanks to Bernhard Schiffner for his help in testing and for his
+ * suggestions.
+ *
+ ************************************************************************
+ *
* 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.
@@ -29,13 +56,18 @@
#include "sa2400.h"
#include "max2820.h"
#include "grf5101.h"
+#include "rtl8225se.h"
MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>");
-MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver");
+MODULE_DESCRIPTION("RTL8180 / RTL8185 / RTL8187SE PCI wireless driver");
MODULE_LICENSE("GPL");
static DEFINE_PCI_DEVICE_TABLE(rtl8180_table) = {
+
+ /* rtl8187se */
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8199) },
+
/* rtl8185 */
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8185) },
{ PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x700f) },
@@ -85,6 +117,76 @@ static const struct ieee80211_channel rtl818x_channels[] = {
{ .center_freq = 2484 },
};
+/* Queues for rtl8187se card
+ *
+ * name | reg | queue
+ * BC | 7 | 6
+ * MG | 1 | 0
+ * HI | 6 | 1
+ * VO | 5 | 2
+ * VI | 4 | 3
+ * BE | 3 | 4
+ * BK | 2 | 5
+ *
+ * The complete map for DMA kick reg using use all queue is:
+ * static const int rtl8187se_queues_map[RTL8187SE_NR_TX_QUEUES] =
+ * {1, 6, 5, 4, 3, 2, 7};
+ *
+ * .. but.. Because for mac80211 4 queues are enough for QoS we use this
+ *
+ * name | reg | queue
+ * BC | 7 | 4 <- currently not used yet
+ * MG | 1 | x <- Not used
+ * HI | 6 | x <- Not used
+ * VO | 5 | 0 <- used
+ * VI | 4 | 1 <- used
+ * BE | 3 | 2 <- used
+ * BK | 2 | 3 <- used
+ *
+ * Beacon queue could be used, but this is not finished yet.
+ *
+ * I thougth about using the other two queues but I decided not to do this:
+ *
+ * - I'm unsure whether the mac80211 will ever try to use more than 4 queues
+ * by itself.
+ *
+ * - I could route MGMT frames (currently sent over VO queue) to the MGMT
+ * queue but since mac80211 will do not know about it, I will probably gain
+ * some HW priority whenever the VO queue is not empty, but this gain is
+ * limited by the fact that I had to stop the mac80211 queue whenever one of
+ * the VO or MGMT queues is full, stopping also submitting of MGMT frame
+ * to the driver.
+ *
+ * - I don't know how to set in the HW the contention window params for MGMT
+ * and HI-prio queues.
+ */
+
+static const int rtl8187se_queues_map[RTL8187SE_NR_TX_QUEUES] = {5, 4, 3, 2, 7};
+
+/* Queues for rtl8180/rtl8185 cards
+ *
+ * name | reg | prio
+ * BC | 7 | 3
+ * HI | 6 | 0
+ * NO | 5 | 1
+ * LO | 4 | 2
+ *
+ * The complete map for DMA kick reg using all queue is:
+ * static const int rtl8180_queues_map[RTL8180_NR_TX_QUEUES] = {6, 5, 4, 7};
+ *
+ * .. but .. Because the mac80211 needs at least 4 queues for QoS or
+ * otherwise QoS can't be done, we use just one.
+ * Beacon queue could be used, but this is not finished yet.
+ * Actual map is:
+ *
+ * name | reg | prio
+ * BC | 7 | 1 <- currently not used yet.
+ * HI | 6 | x <- not used
+ * NO | 5 | x <- not used
+ * LO | 4 | 0 <- used
+ */
+
+static const int rtl8180_queues_map[RTL8180_NR_TX_QUEUES] = {4, 7};
void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data)
{
@@ -105,14 +207,30 @@ void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data)
static void rtl8180_handle_rx(struct ieee80211_hw *dev)
{
struct rtl8180_priv *priv = dev->priv;
+ struct rtl818x_rx_cmd_desc *cmd_desc;
unsigned int count = 32;
u8 signal, agc, sq;
dma_addr_t mapping;
while (count--) {
- struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx];
+ void *entry = priv->rx_ring + priv->rx_idx * priv->rx_ring_sz;
struct sk_buff *skb = priv->rx_buf[priv->rx_idx];
- u32 flags = le32_to_cpu(entry->flags);
+ u32 flags, flags2;
+ u64 tsft;
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+ struct rtl8187se_rx_desc *desc = entry;
+
+ flags = le32_to_cpu(desc->flags);
+ flags2 = le32_to_cpu(desc->flags2);
+ tsft = le64_to_cpu(desc->tsft);
+ } else {
+ struct rtl8180_rx_desc *desc = entry;
+
+ flags = le32_to_cpu(desc->flags);
+ flags2 = le32_to_cpu(desc->flags2);
+ tsft = le64_to_cpu(desc->tsft);
+ }
if (flags & RTL818X_RX_DESC_FLAG_OWN)
return;
@@ -122,7 +240,6 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
RTL818X_RX_DESC_FLAG_RX_ERR)))
goto done;
else {
- u32 flags2 = le32_to_cpu(entry->flags2);
struct ieee80211_rx_status rx_status = {0};
struct sk_buff *new_skb = dev_alloc_skb(MAX_RX_SIZE);
@@ -148,19 +265,24 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
rx_status.antenna = (flags2 >> 15) & 1;
rx_status.rate_idx = (flags >> 20) & 0xF;
agc = (flags2 >> 17) & 0x7F;
- if (priv->r8185) {
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) {
if (rx_status.rate_idx > 3)
signal = 90 - clamp_t(u8, agc, 25, 90);
else
signal = 95 - clamp_t(u8, agc, 30, 95);
- } else {
+ } else if (priv->chip_family ==
+ RTL818X_CHIP_FAMILY_RTL8180) {
sq = flags2 & 0xff;
signal = priv->rf->calc_rssi(agc, sq);
+ } else {
+ /* TODO: rtl8187se rssi */
+ signal = 10;
}
rx_status.signal = signal;
rx_status.freq = dev->conf.chandef.chan->center_freq;
rx_status.band = dev->conf.chandef.chan->band;
- rx_status.mactime = le64_to_cpu(entry->tsft);
+ rx_status.mactime = tsft;
rx_status.flag |= RX_FLAG_MACTIME_START;
if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR)
rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
@@ -174,11 +296,13 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
}
done:
- entry->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb));
- entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN |
+ cmd_desc = entry;
+ cmd_desc->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb));
+ cmd_desc->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN |
MAX_RX_SIZE);
if (priv->rx_idx == 31)
- entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR);
+ cmd_desc->flags |=
+ cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR);
priv->rx_idx = (priv->rx_idx + 1) % 32;
}
}
@@ -218,6 +342,55 @@ static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio)
}
}
+static irqreturn_t rtl8187se_interrupt(int irq, void *dev_id)
+{
+ struct ieee80211_hw *dev = dev_id;
+ struct rtl8180_priv *priv = dev->priv;
+ u32 reg;
+ unsigned long flags;
+ static int desc_err;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ /* Note: 32-bit interrupt status */
+ reg = rtl818x_ioread32(priv, &priv->map->INT_STATUS_SE);
+ if (unlikely(reg == 0xFFFFFFFF)) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ rtl818x_iowrite32(priv, &priv->map->INT_STATUS_SE, reg);
+
+ if (reg & IMR_TIMEOUT1)
+ rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0);
+
+ if (reg & (IMR_TBDOK | IMR_TBDER))
+ rtl8180_handle_tx(dev, 4);
+
+ if (reg & (IMR_TVODOK | IMR_TVODER))
+ rtl8180_handle_tx(dev, 0);
+
+ if (reg & (IMR_TVIDOK | IMR_TVIDER))
+ rtl8180_handle_tx(dev, 1);
+
+ if (reg & (IMR_TBEDOK | IMR_TBEDER))
+ rtl8180_handle_tx(dev, 2);
+
+ if (reg & (IMR_TBKDOK | IMR_TBKDER))
+ rtl8180_handle_tx(dev, 3);
+
+ if (reg & (IMR_ROK | IMR_RER | RTL818X_INT_SE_RX_DU | IMR_RQOSOK))
+ rtl8180_handle_rx(dev);
+ /* The interface sometimes generates several RX DMA descriptor errors
+ * at startup. Do not report these.
+ */
+ if ((reg & RTL818X_INT_SE_RX_DU) && desc_err++ > 2)
+ if (net_ratelimit())
+ wiphy_err(dev->wiphy, "No RX DMA Descriptor avail\n");
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return IRQ_HANDLED;
+}
+
static irqreturn_t rtl8180_interrupt(int irq, void *dev_id)
{
struct ieee80211_hw *dev = dev_id;
@@ -234,12 +407,6 @@ static irqreturn_t rtl8180_interrupt(int irq, void *dev_id)
rtl818x_iowrite16(priv, &priv->map->INT_STATUS, reg);
if (reg & (RTL818X_INT_TXB_OK | RTL818X_INT_TXB_ERR))
- rtl8180_handle_tx(dev, 3);
-
- if (reg & (RTL818X_INT_TXH_OK | RTL818X_INT_TXH_ERR))
- rtl8180_handle_tx(dev, 2);
-
- if (reg & (RTL818X_INT_TXN_OK | RTL818X_INT_TXN_ERR))
rtl8180_handle_tx(dev, 1);
if (reg & (RTL818X_INT_TXL_OK | RTL818X_INT_TXL_ERR))
@@ -263,12 +430,14 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
struct rtl8180_tx_ring *ring;
struct rtl8180_tx_desc *entry;
unsigned long flags;
- unsigned int idx, prio;
+ unsigned int idx, prio, hw_prio;
dma_addr_t mapping;
u32 tx_flags;
u8 rc_flags;
u16 plcp_len = 0;
__le16 rts_duration = 0;
+ /* do arithmetic and then convert to le16 */
+ u16 frame_duration = 0;
prio = skb_get_queue_mapping(skb);
ring = &priv->tx_ring[prio];
@@ -280,7 +449,6 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
kfree_skb(skb);
dev_err(&priv->pdev->dev, "TX DMA mapping error\n");
return;
-
}
tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS |
@@ -288,7 +456,7 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
(ieee80211_get_tx_rate(dev, info)->hw_value << 24) |
skb->len;
- if (priv->r8185)
+ if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180)
tx_flags |= RTL818X_TX_DESC_FLAG_DMA |
RTL818X_TX_DESC_FLAG_NO_ENC;
@@ -305,7 +473,7 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len,
info);
- if (!priv->r8185) {
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) {
unsigned int remainder;
plcp_len = DIV_ROUND_UP(16 * (skb->len + 4),
@@ -316,6 +484,18 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
plcp_len |= 1 << 15;
}
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+ __le16 duration;
+ /* SIFS time (required by HW) is already included by
+ * ieee80211_generic_frame_duration
+ */
+ duration = ieee80211_generic_frame_duration(dev, priv->vif,
+ IEEE80211_BAND_2GHZ, skb->len,
+ ieee80211_get_tx_rate(dev, info));
+
+ frame_duration = priv->ack_time + le16_to_cpu(duration);
+ }
+
spin_lock_irqsave(&priv->lock, flags);
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
@@ -328,21 +508,91 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries;
entry = &ring->desc[idx];
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+ entry->frame_duration = cpu_to_le16(frame_duration);
+ entry->frame_len_se = cpu_to_le16(skb->len);
+
+ /* tpc polarity */
+ entry->flags3 = cpu_to_le16(1<<4);
+ } else
+ entry->frame_len = cpu_to_le32(skb->len);
+
entry->rts_duration = rts_duration;
entry->plcp_len = cpu_to_le16(plcp_len);
entry->tx_buf = cpu_to_le32(mapping);
- entry->frame_len = cpu_to_le32(skb->len);
+
entry->flags2 = info->control.rates[1].idx >= 0 ?
ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0;
entry->retry_limit = info->control.rates[0].count;
+
+ /* We must be sure that tx_flags is written last because the HW
+ * looks at it to check if the rest of data is valid or not
+ */
+ wmb();
entry->flags = cpu_to_le32(tx_flags);
+ /* We must be sure this has been written before followings HW
+ * register write, because this write will made the HW attempts
+ * to DMA the just-written data
+ */
+ wmb();
+
__skb_queue_tail(&ring->queue, skb);
if (ring->entries - skb_queue_len(&ring->queue) < 2)
ieee80211_stop_queue(dev, prio);
spin_unlock_irqrestore(&priv->lock, flags);
- rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4)));
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+ /* just poll: rings are stopped with TPPollStop reg */
+ hw_prio = rtl8187se_queues_map[prio];
+ rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING,
+ (1 << hw_prio));
+ } else {
+ hw_prio = rtl8180_queues_map[prio];
+ rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING,
+ (1 << hw_prio) | /* ring to poll */
+ (1<<1) | (1<<2));/* stopped rings */
+ }
+}
+
+static void rtl8180_set_anaparam3(struct rtl8180_priv *priv, u16 anaparam3)
+{
+ u8 reg;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_CONFIG);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+
+ rtl818x_iowrite16(priv, &priv->map->ANAPARAM3, anaparam3);
+
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_NORMAL);
+}
+
+void rtl8180_set_anaparam2(struct rtl8180_priv *priv, u32 anaparam2)
+{
+ u8 reg;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_CONFIG);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, anaparam2);
+
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_NORMAL);
}
void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam)
@@ -359,17 +609,171 @@ void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam)
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
}
+static void rtl8187se_mac_config(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u8 reg;
+
+ rtl818x_iowrite32(priv, REG_ADDR4(0x1F0), 0);
+ rtl818x_ioread32(priv, REG_ADDR4(0x1F0));
+ rtl818x_iowrite32(priv, REG_ADDR4(0x1F4), 0);
+ rtl818x_ioread32(priv, REG_ADDR4(0x1F4));
+ rtl818x_iowrite8(priv, REG_ADDR1(0x1F8), 0);
+ rtl818x_ioread8(priv, REG_ADDR1(0x1F8));
+ /* Enable DA10 TX power saving */
+ reg = rtl818x_ioread8(priv, &priv->map->PHY_PR);
+ rtl818x_iowrite8(priv, &priv->map->PHY_PR, reg | 0x04);
+ /* Power */
+ rtl818x_iowrite16(priv, PI_DATA_REG, 0x1000);
+ rtl818x_iowrite16(priv, SI_DATA_REG, 0x1000);
+ /* AFE - default to power ON */
+ rtl818x_iowrite16(priv, REG_ADDR2(0x370), 0x0560);
+ rtl818x_iowrite16(priv, REG_ADDR2(0x372), 0x0560);
+ rtl818x_iowrite16(priv, REG_ADDR2(0x374), 0x0DA4);
+ rtl818x_iowrite16(priv, REG_ADDR2(0x376), 0x0DA4);
+ rtl818x_iowrite16(priv, REG_ADDR2(0x378), 0x0560);
+ rtl818x_iowrite16(priv, REG_ADDR2(0x37A), 0x0560);
+ rtl818x_iowrite16(priv, REG_ADDR2(0x37C), 0x00EC);
+ rtl818x_iowrite16(priv, REG_ADDR2(0x37E), 0x00EC);
+ rtl818x_iowrite8(priv, REG_ADDR1(0x24E), 0x01);
+ /* unknown, needed for suspend to RAM resume */
+ rtl818x_iowrite8(priv, REG_ADDR1(0x0A), 0x72);
+}
+
+static void rtl8187se_set_antenna_config(struct ieee80211_hw *dev, u8 def_ant,
+ bool diversity)
+{
+ struct rtl8180_priv *priv = dev->priv;
+
+ rtl8225_write_phy_cck(dev, 0x0C, 0x09);
+ if (diversity) {
+ if (def_ant == 1) {
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x00);
+ rtl8225_write_phy_cck(dev, 0x11, 0xBB);
+ rtl8225_write_phy_cck(dev, 0x01, 0xC7);
+ rtl8225_write_phy_ofdm(dev, 0x0D, 0x54);
+ rtl8225_write_phy_ofdm(dev, 0x18, 0xB2);
+ } else { /* main antenna */
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03);
+ rtl8225_write_phy_cck(dev, 0x11, 0x9B);
+ rtl8225_write_phy_cck(dev, 0x01, 0xC7);
+ rtl8225_write_phy_ofdm(dev, 0x0D, 0x5C);
+ rtl8225_write_phy_ofdm(dev, 0x18, 0xB2);
+ }
+ } else { /* disable antenna diversity */
+ if (def_ant == 1) {
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x00);
+ rtl8225_write_phy_cck(dev, 0x11, 0xBB);
+ rtl8225_write_phy_cck(dev, 0x01, 0x47);
+ rtl8225_write_phy_ofdm(dev, 0x0D, 0x54);
+ rtl8225_write_phy_ofdm(dev, 0x18, 0x32);
+ } else { /* main antenna */
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03);
+ rtl8225_write_phy_cck(dev, 0x11, 0x9B);
+ rtl8225_write_phy_cck(dev, 0x01, 0x47);
+ rtl8225_write_phy_ofdm(dev, 0x0D, 0x5C);
+ rtl8225_write_phy_ofdm(dev, 0x18, 0x32);
+ }
+ }
+ /* priv->curr_ant = def_ant; */
+}
+
+static void rtl8180_int_enable(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+ rtl818x_iowrite32(priv, &priv->map->IMR, IMR_TMGDOK |
+ IMR_TBDER | IMR_THPDER |
+ IMR_THPDER | IMR_THPDOK |
+ IMR_TVODER | IMR_TVODOK |
+ IMR_TVIDER | IMR_TVIDOK |
+ IMR_TBEDER | IMR_TBEDOK |
+ IMR_TBKDER | IMR_TBKDOK |
+ IMR_RDU | IMR_RER |
+ IMR_ROK | IMR_RQOSOK);
+ } else {
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);
+ }
+}
+
+static void rtl8180_int_disable(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+ rtl818x_iowrite32(priv, &priv->map->IMR, 0);
+ } else {
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+ }
+}
+
+static void rtl8180_conf_basic_rates(struct ieee80211_hw *dev,
+ u32 rates_mask)
+{
+ struct rtl8180_priv *priv = dev->priv;
+
+ u8 max, min;
+ u16 reg;
+
+ max = fls(rates_mask) - 1;
+ min = ffs(rates_mask) - 1;
+
+ switch (priv->chip_family) {
+
+ case RTL818X_CHIP_FAMILY_RTL8180:
+ /* in 8180 this is NOT a BITMAP */
+ reg = rtl818x_ioread16(priv, &priv->map->BRSR);
+ reg &= ~3;
+ reg |= max;
+ rtl818x_iowrite16(priv, &priv->map->BRSR, reg);
+ break;
+
+ case RTL818X_CHIP_FAMILY_RTL8185:
+ /* in 8185 this is a BITMAP */
+ rtl818x_iowrite16(priv, &priv->map->BRSR, rates_mask);
+ rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (max << 4) | min);
+ break;
+
+ case RTL818X_CHIP_FAMILY_RTL8187SE:
+ /* in 8187se this is a BITMAP */
+ rtl818x_iowrite16(priv, &priv->map->BRSR_8187SE, rates_mask);
+ break;
+ }
+}
+
+static void rtl8180_config_cardbus(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u16 reg16;
+ u8 reg8;
+
+ reg8 = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ reg8 |= 1 << 1;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg8);
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+ rtl818x_iowrite16(priv, FEMR_SE, 0xffff);
+ } else {
+ reg16 = rtl818x_ioread16(priv, &priv->map->FEMR);
+ reg16 |= (1 << 15) | (1 << 14) | (1 << 4);
+ rtl818x_iowrite16(priv, &priv->map->FEMR, reg16);
+ }
+
+}
+
static int rtl8180_init_hw(struct ieee80211_hw *dev)
{
struct rtl8180_priv *priv = dev->priv;
u16 reg;
+ u32 reg32;
rtl818x_iowrite8(priv, &priv->map->CMD, 0);
rtl818x_ioread8(priv, &priv->map->CMD);
msleep(10);
/* reset */
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+ rtl8180_int_disable(dev);
rtl818x_ioread8(priv, &priv->map->CMD);
reg = rtl818x_ioread8(priv, &priv->map->CMD);
@@ -390,31 +794,45 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev)
msleep(200);
if (rtl818x_ioread8(priv, &priv->map->CONFIG3) & (1 << 3)) {
- /* For cardbus */
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- reg |= 1 << 1;
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
- reg = rtl818x_ioread16(priv, &priv->map->FEMR);
- reg |= (1 << 15) | (1 << 14) | (1 << 4);
- rtl818x_iowrite16(priv, &priv->map->FEMR, reg);
+ rtl8180_config_cardbus(dev);
}
- rtl818x_iowrite8(priv, &priv->map->MSR, 0);
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE)
+ rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_ENEDCA);
+ else
+ rtl818x_iowrite8(priv, &priv->map->MSR, 0);
- if (!priv->r8185)
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180)
rtl8180_set_anaparam(priv, priv->anaparam);
rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma);
- rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma);
- rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma);
- rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma);
- rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma);
+ /* mac80211 queue have higher prio for lower index. The last queue
+ * (that mac80211 is not aware of) is reserved for beacons (and have
+ * the highest priority on the NIC)
+ */
+ if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8187SE) {
+ rtl818x_iowrite32(priv, &priv->map->TBDA,
+ priv->tx_ring[1].dma);
+ rtl818x_iowrite32(priv, &priv->map->TLPDA,
+ priv->tx_ring[0].dma);
+ } else {
+ rtl818x_iowrite32(priv, &priv->map->TBDA,
+ priv->tx_ring[4].dma);
+ rtl818x_iowrite32(priv, &priv->map->TVODA,
+ priv->tx_ring[0].dma);
+ rtl818x_iowrite32(priv, &priv->map->TVIDA,
+ priv->tx_ring[1].dma);
+ rtl818x_iowrite32(priv, &priv->map->TBEDA,
+ priv->tx_ring[2].dma);
+ rtl818x_iowrite32(priv, &priv->map->TBKDA,
+ priv->tx_ring[3].dma);
+ }
/* TODO: necessary? specs indicate not */
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
reg = rtl818x_ioread8(priv, &priv->map->CONFIG2);
rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg & ~(1 << 3));
- if (priv->r8185) {
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) {
reg = rtl818x_ioread8(priv, &priv->map->CONFIG2);
rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg | (1 << 4));
}
@@ -426,13 +844,17 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev)
rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0);
- if (priv->r8185) {
+ if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) {
rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81);
- rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0);
+ } else {
+ rtl818x_iowrite8(priv, &priv->map->SECURITY, 0);
- rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
+ rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6);
+ rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C);
+ }
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) {
/* TODO: set ClkRun enable? necessary? */
reg = rtl818x_ioread8(priv, &priv->map->GP_ENABLE);
rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, reg & ~(1 << 6));
@@ -440,28 +862,90 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev)
reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2));
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
- } else {
- rtl818x_iowrite16(priv, &priv->map->BRSR, 0x1);
- rtl818x_iowrite8(priv, &priv->map->SECURITY, 0);
+ }
- rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6);
- rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C);
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+
+ /* the set auto rate fallback bitmask from 1M to 54 Mb/s */
+ rtl818x_iowrite16(priv, ARFR, 0xFFF);
+ rtl818x_ioread16(priv, ARFR);
+
+ /* stop unused queus (no dma alloc) */
+ rtl818x_iowrite8(priv, &priv->map->TPPOLL_STOP,
+ RTL818x_TPPOLL_STOP_MG | RTL818x_TPPOLL_STOP_HI);
+
+ rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0x00);
+ rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50);
+
+ rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0);
+
+ /* some black magic here.. */
+ rtl8187se_mac_config(dev);
+
+ rtl818x_iowrite16(priv, RFSW_CTRL, 0x569A);
+ rtl818x_ioread16(priv, RFSW_CTRL);
+
+ rtl8180_set_anaparam(priv, RTL8225SE_ANAPARAM_ON);
+ rtl8180_set_anaparam2(priv, RTL8225SE_ANAPARAM2_ON);
+ rtl8180_set_anaparam3(priv, RTL8225SE_ANAPARAM3);
+
+
+ rtl818x_iowrite8(priv, &priv->map->CONFIG5,
+ rtl818x_ioread8(priv, &priv->map->CONFIG5) & 0x7F);
+
+ /*probably this switch led on */
+ rtl818x_iowrite8(priv, &priv->map->PGSELECT,
+ rtl818x_ioread8(priv, &priv->map->PGSELECT) | 0x08);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1BFF);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488);
+
+ rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x4003);
+
+ /* the reference code mac hardcode table write
+ * this reg by doing byte-wide accesses.
+ * It does it just for lowest and highest byte..
+ */
+ reg32 = rtl818x_ioread32(priv, &priv->map->RF_PARA);
+ reg32 &= 0x00ffff00;
+ reg32 |= 0xb8000054;
+ rtl818x_iowrite32(priv, &priv->map->RF_PARA, reg32);
}
priv->rf->init(dev);
- if (priv->r8185)
- rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
+
+ /* default basic rates are 1,2 Mbps for rtl8180. 1,2,6,9,12,18,24 Mbps
+ * otherwise. bitmask 0x3 and 0x01f3 respectively.
+ * NOTE: currenty rtl8225 RF code changes basic rates, so we need to do
+ * this after rf init.
+ * TODO: try to find out whether RF code really needs to do this..
+ */
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180)
+ rtl8180_conf_basic_rates(dev, 0x3);
+ else
+ rtl8180_conf_basic_rates(dev, 0x1f3);
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE)
+ rtl8187se_set_antenna_config(dev,
+ priv->antenna_diversity_default,
+ priv->antenna_diversity_en);
return 0;
}
static int rtl8180_init_rx_ring(struct ieee80211_hw *dev)
{
struct rtl8180_priv *priv = dev->priv;
- struct rtl8180_rx_desc *entry;
+ struct rtl818x_rx_cmd_desc *entry;
int i;
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE)
+ priv->rx_ring_sz = sizeof(struct rtl8187se_rx_desc);
+ else
+ priv->rx_ring_sz = sizeof(struct rtl8180_rx_desc);
+
priv->rx_ring = pci_alloc_consistent(priv->pdev,
- sizeof(*priv->rx_ring) * 32,
+ priv->rx_ring_sz * 32,
&priv->rx_ring_dma);
if (!priv->rx_ring || (unsigned long)priv->rx_ring & 0xFF) {
@@ -469,20 +953,28 @@ static int rtl8180_init_rx_ring(struct ieee80211_hw *dev)
return -ENOMEM;
}
- memset(priv->rx_ring, 0, sizeof(*priv->rx_ring) * 32);
+ memset(priv->rx_ring, 0, priv->rx_ring_sz * 32);
priv->rx_idx = 0;
for (i = 0; i < 32; i++) {
struct sk_buff *skb = dev_alloc_skb(MAX_RX_SIZE);
dma_addr_t *mapping;
- entry = &priv->rx_ring[i];
- if (!skb)
- return 0;
-
+ entry = priv->rx_ring + priv->rx_ring_sz*i;
+ if (!skb) {
+ wiphy_err(dev->wiphy, "Cannot allocate RX skb\n");
+ return -ENOMEM;
+ }
priv->rx_buf[i] = skb;
mapping = (dma_addr_t *)skb->cb;
*mapping = pci_map_single(priv->pdev, skb_tail_pointer(skb),
MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
+
+ if (pci_dma_mapping_error(priv->pdev, *mapping)) {
+ kfree_skb(skb);
+ wiphy_err(dev->wiphy, "Cannot map DMA for RX skb\n");
+ return -ENOMEM;
+ }
+
entry->rx_buf = cpu_to_le32(*mapping);
entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN |
MAX_RX_SIZE);
@@ -507,7 +999,7 @@ static void rtl8180_free_rx_ring(struct ieee80211_hw *dev)
kfree_skb(skb);
}
- pci_free_consistent(priv->pdev, sizeof(*priv->rx_ring) * 32,
+ pci_free_consistent(priv->pdev, priv->rx_ring_sz * 32,
priv->rx_ring, priv->rx_ring_dma);
priv->rx_ring = NULL;
}
@@ -571,7 +1063,7 @@ static int rtl8180_start(struct ieee80211_hw *dev)
if (ret)
return ret;
- for (i = 0; i < 4; i++)
+ for (i = 0; i < (dev->queues + 1); i++)
if ((ret = rtl8180_init_tx_ring(dev, i, 16)))
goto err_free_rings;
@@ -579,23 +1071,28 @@ static int rtl8180_start(struct ieee80211_hw *dev)
if (ret)
goto err_free_rings;
- rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma);
- rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma);
- rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma);
- rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma);
- rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma);
-
- ret = request_irq(priv->pdev->irq, rtl8180_interrupt,
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+ ret = request_irq(priv->pdev->irq, rtl8187se_interrupt,
IRQF_SHARED, KBUILD_MODNAME, dev);
+ } else {
+ ret = request_irq(priv->pdev->irq, rtl8180_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, dev);
+ }
+
if (ret) {
wiphy_err(dev->wiphy, "failed to register IRQ handler\n");
goto err_free_rings;
}
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);
+ rtl8180_int_enable(dev);
- rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0);
- rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0);
+ /* in rtl8187se at MAR regs offset there is the management
+ * TX descriptor DMA addres..
+ */
+ if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8187SE) {
+ rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0);
+ rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0);
+ }
reg = RTL818X_RX_CONF_ONLYERLPKT |
RTL818X_RX_CONF_RX_AUTORESETPHY |
@@ -605,27 +1102,42 @@ static int rtl8180_start(struct ieee80211_hw *dev)
RTL818X_RX_CONF_BROADCAST |
RTL818X_RX_CONF_NICMAC;
- if (priv->r8185)
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185)
reg |= RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2;
- else {
+ else if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) {
reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE1)
? RTL818X_RX_CONF_CSDM1 : 0;
reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE2)
? RTL818X_RX_CONF_CSDM2 : 0;
+ } else {
+ reg &= ~(RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2);
}
priv->rx_conf = reg;
rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
- if (priv->r8185) {
+ if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) {
reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
- reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT;
- reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+
+ /* CW is not on per-packet basis.
+ * in rtl8185 the CW_VALUE reg is used.
+ * in rtl8187se the AC param regs are used.
+ */
+ reg &= ~RTL818X_CW_CONF_PERPACKET_CW;
+ /* retry limit IS on per-packet basis.
+ * the short and long retry limit in TX_CONF
+ * reg are ignored
+ */
+ reg |= RTL818X_CW_CONF_PERPACKET_RETRY;
rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
- reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
- reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+ /* TX antenna and TX gain are not on per-packet basis.
+ * TX Antenna is selected by ANTSEL reg (RX in BB regs).
+ * TX gain is selected with CCK_TX_AGC and OFDM_TX_AGC regs
+ */
+ reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN;
+ reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL;
reg |= RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
@@ -637,11 +1149,16 @@ static int rtl8180_start(struct ieee80211_hw *dev)
reg |= (6 << 21 /* MAX TX DMA */) |
RTL818X_TX_CONF_NO_ICV;
- if (priv->r8185)
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE)
+ reg |= 1<<30; /* "duration procedure mode" */
+
+ if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180)
reg &= ~RTL818X_TX_CONF_PROBE_DTS;
else
reg &= ~RTL818X_TX_CONF_HW_SEQNUM;
+ reg &= ~RTL818X_TX_CONF_DISCW;
+
/* different meaning, same value on both rtl8185 and rtl8180 */
reg &= ~RTL818X_TX_CONF_SAT_HWPLCP;
@@ -656,7 +1173,7 @@ static int rtl8180_start(struct ieee80211_hw *dev)
err_free_rings:
rtl8180_free_rx_ring(dev);
- for (i = 0; i < 4; i++)
+ for (i = 0; i < (dev->queues + 1); i++)
if (priv->tx_ring[i].desc)
rtl8180_free_tx_ring(dev, i);
@@ -669,7 +1186,7 @@ static void rtl8180_stop(struct ieee80211_hw *dev)
u8 reg;
int i;
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+ rtl8180_int_disable(dev);
reg = rtl818x_ioread8(priv, &priv->map->CMD);
reg &= ~RTL818X_CMD_TX_ENABLE;
@@ -686,7 +1203,7 @@ static void rtl8180_stop(struct ieee80211_hw *dev)
free_irq(priv->pdev->irq, dev);
rtl8180_free_rx_ring(dev);
- for (i = 0; i < 4; i++)
+ for (i = 0; i < (dev->queues + 1); i++)
rtl8180_free_tx_ring(dev, i);
}
@@ -794,6 +1311,123 @@ static int rtl8180_config(struct ieee80211_hw *dev, u32 changed)
return 0;
}
+static void rtl8187se_conf_ac_parm(struct ieee80211_hw *dev, u8 queue)
+{
+ const struct ieee80211_tx_queue_params *params;
+ struct rtl8180_priv *priv = dev->priv;
+
+ /* hw value */
+ u32 ac_param;
+
+ u8 aifs;
+ u8 txop;
+ u8 cw_min, cw_max;
+
+ params = &priv->queue_param[queue];
+
+ cw_min = fls(params->cw_min);
+ cw_max = fls(params->cw_max);
+
+ aifs = 10 + params->aifs * priv->slot_time;
+
+ /* TODO: check if txop HW is in us (mult by 32) */
+ txop = params->txop;
+
+ ac_param = txop << AC_PARAM_TXOP_LIMIT_SHIFT |
+ cw_max << AC_PARAM_ECW_MAX_SHIFT |
+ cw_min << AC_PARAM_ECW_MIN_SHIFT |
+ aifs << AC_PARAM_AIFS_SHIFT;
+
+ switch (queue) {
+ case IEEE80211_AC_BK:
+ rtl818x_iowrite32(priv, &priv->map->AC_BK_PARAM, ac_param);
+ break;
+ case IEEE80211_AC_BE:
+ rtl818x_iowrite32(priv, &priv->map->AC_BE_PARAM, ac_param);
+ break;
+ case IEEE80211_AC_VI:
+ rtl818x_iowrite32(priv, &priv->map->AC_VI_PARAM, ac_param);
+ break;
+ case IEEE80211_AC_VO:
+ rtl818x_iowrite32(priv, &priv->map->AC_VO_PARAM, ac_param);
+ break;
+ }
+}
+
+static int rtl8180_conf_tx(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u8 cw_min, cw_max;
+
+ /* nothing to do ? */
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180)
+ return 0;
+
+ cw_min = fls(params->cw_min);
+ cw_max = fls(params->cw_max);
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+ priv->queue_param[queue] = *params;
+ rtl8187se_conf_ac_parm(dev, queue);
+ } else
+ rtl818x_iowrite8(priv, &priv->map->CW_VAL,
+ (cw_max << 4) | cw_min);
+ return 0;
+}
+
+static void rtl8180_conf_erp(struct ieee80211_hw *dev,
+ struct ieee80211_bss_conf *info)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u8 sifs, difs;
+ int eifs;
+ u8 hw_eifs;
+
+ /* TODO: should we do something ? */
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180)
+ return;
+
+ /* I _hope_ this means 10uS for the HW.
+ * In reference code it is 0x22 for
+ * both rtl8187L and rtl8187SE
+ */
+ sifs = 0x22;
+
+ if (info->use_short_slot)
+ priv->slot_time = 9;
+ else
+ priv->slot_time = 20;
+
+ /* 10 is SIFS time in uS */
+ difs = 10 + 2 * priv->slot_time;
+ eifs = 10 + difs + priv->ack_time;
+
+ /* HW should use 4uS units for EIFS (I'm sure for rtl8185)*/
+ hw_eifs = DIV_ROUND_UP(eifs, 4);
+
+
+ rtl818x_iowrite8(priv, &priv->map->SLOT, priv->slot_time);
+ rtl818x_iowrite8(priv, &priv->map->SIFS, sifs);
+ rtl818x_iowrite8(priv, &priv->map->DIFS, difs);
+
+ /* from reference code. set ack timeout reg = eifs reg */
+ rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, hw_eifs);
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE)
+ rtl818x_iowrite8(priv, &priv->map->EIFS_8187SE, hw_eifs);
+ else if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) {
+ /* rtl8187/rtl8185 HW bug. After EIFS is elapsed,
+ * the HW still wait for DIFS.
+ * HW uses 4uS units for EIFS.
+ */
+ hw_eifs = DIV_ROUND_UP(eifs - difs, 4);
+
+ rtl818x_iowrite8(priv, &priv->map->EIFS, hw_eifs);
+ }
+}
+
static void rtl8180_bss_info_changed(struct ieee80211_hw *dev,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info,
@@ -818,11 +1452,40 @@ static void rtl8180_bss_info_changed(struct ieee80211_hw *dev,
reg = RTL818X_MSR_INFRA;
} else
reg = RTL818X_MSR_NO_LINK;
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE)
+ reg |= RTL818X_MSR_ENEDCA;
+
rtl818x_iowrite8(priv, &priv->map->MSR, reg);
}
- if (changed & BSS_CHANGED_ERP_SLOT && priv->rf->conf_erp)
- priv->rf->conf_erp(dev, info);
+ if (changed & BSS_CHANGED_BASIC_RATES)
+ rtl8180_conf_basic_rates(dev, info->basic_rates);
+
+ if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE)) {
+
+ /* when preamble changes, acktime duration changes, and erp must
+ * be recalculated. ACK time is calculated at lowest rate.
+ * Since mac80211 include SIFS time we remove it (-10)
+ */
+ priv->ack_time =
+ le16_to_cpu(ieee80211_generic_frame_duration(dev,
+ priv->vif,
+ IEEE80211_BAND_2GHZ, 10,
+ &priv->rates[0])) - 10;
+
+ rtl8180_conf_erp(dev, info);
+
+ /* mac80211 supplies aifs_n to driver and calls
+ * conf_tx callback whether aifs_n changes, NOT
+ * when aifs changes.
+ * Aifs should be recalculated if slot changes.
+ */
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+ for (i = 0; i < 4; i++)
+ rtl8187se_conf_ac_parm(dev, i);
+ }
+ }
if (changed & BSS_CHANGED_BEACON_ENABLED)
vif_priv->enable_beacon = info->enable_beacon;
@@ -880,6 +1543,7 @@ static const struct ieee80211_ops rtl8180_ops = {
.remove_interface = rtl8180_remove_interface,
.config = rtl8180_config,
.bss_info_changed = rtl8180_bss_info_changed,
+ .conf_tx = rtl8180_conf_tx,
.prepare_multicast = rtl8180_prepare_multicast,
.configure_filter = rtl8180_configure_filter,
.get_tsf = rtl8180_get_tsf,
@@ -887,8 +1551,7 @@ static const struct ieee80211_ops rtl8180_ops = {
static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom)
{
- struct ieee80211_hw *dev = eeprom->data;
- struct rtl8180_priv *priv = dev->priv;
+ struct rtl8180_priv *priv = eeprom->data;
u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE;
@@ -899,8 +1562,7 @@ static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom)
static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom)
{
- struct ieee80211_hw *dev = eeprom->data;
- struct rtl8180_priv *priv = dev->priv;
+ struct rtl8180_priv *priv = eeprom->data;
u8 reg = 2 << 6;
if (eeprom->reg_data_in)
@@ -917,6 +1579,83 @@ static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom)
udelay(10);
}
+static void rtl8180_eeprom_read(struct rtl8180_priv *priv)
+{
+ struct eeprom_93cx6 eeprom;
+ int eeprom_cck_table_adr;
+ u16 eeprom_val;
+ int i;
+
+ eeprom.data = priv;
+ eeprom.register_read = rtl8180_eeprom_register_read;
+ eeprom.register_write = rtl8180_eeprom_register_write;
+ if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6))
+ eeprom.width = PCI_EEPROM_WIDTH_93C66;
+ else
+ eeprom.width = PCI_EEPROM_WIDTH_93C46;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_PROGRAM);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(10);
+
+ eeprom_93cx6_read(&eeprom, 0x06, &eeprom_val);
+ eeprom_val &= 0xFF;
+ priv->rf_type = eeprom_val;
+
+ eeprom_93cx6_read(&eeprom, 0x17, &eeprom_val);
+ priv->csthreshold = eeprom_val >> 8;
+
+ eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)priv->mac_addr, 3);
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE)
+ eeprom_cck_table_adr = 0x30;
+ else
+ eeprom_cck_table_adr = 0x10;
+
+ /* CCK TX power */
+ for (i = 0; i < 14; i += 2) {
+ u16 txpwr;
+ eeprom_93cx6_read(&eeprom, eeprom_cck_table_adr + (i >> 1),
+ &txpwr);
+ priv->channels[i].hw_value = txpwr & 0xFF;
+ priv->channels[i + 1].hw_value = txpwr >> 8;
+ }
+
+ /* OFDM TX power */
+ if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) {
+ for (i = 0; i < 14; i += 2) {
+ u16 txpwr;
+ eeprom_93cx6_read(&eeprom, 0x20 + (i >> 1), &txpwr);
+ priv->channels[i].hw_value |= (txpwr & 0xFF) << 8;
+ priv->channels[i + 1].hw_value |= txpwr & 0xFF00;
+ }
+ }
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) {
+ __le32 anaparam;
+ eeprom_93cx6_multiread(&eeprom, 0xD, (__le16 *)&anaparam, 2);
+ priv->anaparam = le32_to_cpu(anaparam);
+ eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam);
+ }
+
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
+ eeprom_93cx6_read(&eeprom, 0x3F, &eeprom_val);
+ priv->antenna_diversity_en = !!(eeprom_val & 0x100);
+ priv->antenna_diversity_default = (eeprom_val & 0xC00) == 0x400;
+
+ eeprom_93cx6_read(&eeprom, 0x7C, &eeprom_val);
+ priv->xtal_out = eeprom_val & 0xF;
+ priv->xtal_in = (eeprom_val & 0xF0) >> 4;
+ priv->xtal_cal = !!(eeprom_val & 0x1000);
+ priv->thermal_meter_val = (eeprom_val & 0xF00) >> 8;
+ priv->thermal_meter_en = !!(eeprom_val & 0x2000);
+ }
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_NORMAL);
+}
+
static int rtl8180_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -924,12 +1663,9 @@ static int rtl8180_probe(struct pci_dev *pdev,
struct rtl8180_priv *priv;
unsigned long mem_addr, mem_len;
unsigned int io_addr, io_len;
- int err, i;
- struct eeprom_93cx6 eeprom;
+ int err;
const char *chip_name, *rf_name = NULL;
u32 reg;
- u16 eeprom_val;
- u8 mac_addr[ETH_ALEN];
err = pci_enable_device(pdev);
if (err) {
@@ -1011,7 +1747,6 @@ static int rtl8180_probe(struct pci_dev *pdev,
dev->vif_data_size = sizeof(struct rtl8180_vif);
dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC);
- dev->queues = 1;
dev->max_signal = 65;
reg = rtl818x_ioread32(priv, &priv->map->TX_CONF);
@@ -1019,43 +1754,55 @@ static int rtl8180_probe(struct pci_dev *pdev,
switch (reg) {
case RTL818X_TX_CONF_R8180_ABCD:
chip_name = "RTL8180";
+ priv->chip_family = RTL818X_CHIP_FAMILY_RTL8180;
break;
+
case RTL818X_TX_CONF_R8180_F:
chip_name = "RTL8180vF";
+ priv->chip_family = RTL818X_CHIP_FAMILY_RTL8180;
break;
+
case RTL818X_TX_CONF_R8185_ABC:
chip_name = "RTL8185";
+ priv->chip_family = RTL818X_CHIP_FAMILY_RTL8185;
break;
+
case RTL818X_TX_CONF_R8185_D:
chip_name = "RTL8185vD";
+ priv->chip_family = RTL818X_CHIP_FAMILY_RTL8185;
+ break;
+
+ case RTL818X_TX_CONF_RTL8187SE:
+ chip_name = "RTL8187SE";
+ priv->chip_family = RTL818X_CHIP_FAMILY_RTL8187SE;
break;
+
default:
printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n",
pci_name(pdev), reg >> 25);
goto err_iounmap;
}
- priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC;
- if (priv->r8185) {
+ /* we declare to MAC80211 all the queues except for beacon queue
+ * that will be eventually handled by DRV.
+ * TX rings are arranged in such a way that lower is the IDX,
+ * higher is the priority, in order to achieve direct mapping
+ * with mac80211, however the beacon queue is an exception and it
+ * is mapped on the highst tx ring IDX.
+ */
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE)
+ dev->queues = RTL8187SE_NR_TX_QUEUES - 1;
+ else
+ dev->queues = RTL8180_NR_TX_QUEUES - 1;
+
+ if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) {
priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates);
pci_try_set_mwi(pdev);
}
- eeprom.data = dev;
- eeprom.register_read = rtl8180_eeprom_register_read;
- eeprom.register_write = rtl8180_eeprom_register_write;
- if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6))
- eeprom.width = PCI_EEPROM_WIDTH_93C66;
- else
- eeprom.width = PCI_EEPROM_WIDTH_93C46;
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_PROGRAM);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(10);
+ rtl8180_eeprom_read(priv);
- eeprom_93cx6_read(&eeprom, 0x06, &eeprom_val);
- eeprom_val &= 0xFF;
- switch (eeprom_val) {
+ switch (priv->rf_type) {
case 1: rf_name = "Intersil";
break;
case 2: rf_name = "RFMD";
@@ -1066,14 +1813,18 @@ static int rtl8180_probe(struct pci_dev *pdev,
break;
case 5: priv->rf = &grf5101_rf_ops;
break;
- case 9: priv->rf = rtl8180_detect_rf(dev);
+ case 9:
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE)
+ priv->rf = rtl8187se_detect_rf(dev);
+ else
+ priv->rf = rtl8180_detect_rf(dev);
break;
case 10:
rf_name = "RTL8255";
break;
default:
printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n",
- pci_name(pdev), eeprom_val);
+ pci_name(pdev), priv->rf_type);
goto err_iounmap;
}
@@ -1083,42 +1834,12 @@ static int rtl8180_probe(struct pci_dev *pdev,
goto err_iounmap;
}
- eeprom_93cx6_read(&eeprom, 0x17, &eeprom_val);
- priv->csthreshold = eeprom_val >> 8;
- if (!priv->r8185) {
- __le32 anaparam;
- eeprom_93cx6_multiread(&eeprom, 0xD, (__le16 *)&anaparam, 2);
- priv->anaparam = le32_to_cpu(anaparam);
- eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam);
- }
-
- eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)mac_addr, 3);
- if (!is_valid_ether_addr(mac_addr)) {
+ if (!is_valid_ether_addr(priv->mac_addr)) {
printk(KERN_WARNING "%s (rtl8180): Invalid hwaddr! Using"
" randomly generated MAC addr\n", pci_name(pdev));
- eth_random_addr(mac_addr);
- }
- SET_IEEE80211_PERM_ADDR(dev, mac_addr);
-
- /* CCK TX power */
- for (i = 0; i < 14; i += 2) {
- u16 txpwr;
- eeprom_93cx6_read(&eeprom, 0x10 + (i >> 1), &txpwr);
- priv->channels[i].hw_value = txpwr & 0xFF;
- priv->channels[i + 1].hw_value = txpwr >> 8;
- }
-
- /* OFDM TX power */
- if (priv->r8185) {
- for (i = 0; i < 14; i += 2) {
- u16 txpwr;
- eeprom_93cx6_read(&eeprom, 0x20 + (i >> 1), &txpwr);
- priv->channels[i].hw_value |= (txpwr & 0xFF) << 8;
- priv->channels[i + 1].hw_value |= txpwr & 0xFF00;
- }
+ eth_random_addr(priv->mac_addr);
}
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+ SET_IEEE80211_PERM_ADDR(dev, priv->mac_addr);
spin_lock_init(&priv->lock);
@@ -1130,12 +1851,12 @@ static int rtl8180_probe(struct pci_dev *pdev,
}
wiphy_info(dev->wiphy, "hwaddr %pm, %s + %s\n",
- mac_addr, chip_name, priv->rf->name);
+ priv->mac_addr, chip_name, priv->rf->name);
return 0;
err_iounmap:
- iounmap(priv->map);
+ pci_iounmap(pdev, priv->map);
err_free_dev:
ieee80211_free_hw(dev);
diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h
index 30523314da43..291a55970d1a 100644
--- a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h
+++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h
@@ -24,27 +24,64 @@
#define ANAPARAM_PWR1_SHIFT 20
#define ANAPARAM_PWR1_MASK (0x7F << ANAPARAM_PWR1_SHIFT)
+/* rtl8180/rtl8185 have 3 queue + beacon queue.
+ * mac80211 can use just one, + beacon = 2 tot.
+ */
+#define RTL8180_NR_TX_QUEUES 2
+
+/* rtl8187SE have 6 queues + beacon queues
+ * mac80211 can use 4 QoS data queue, + beacon = 5 tot
+ */
+#define RTL8187SE_NR_TX_QUEUES 5
+
+/* for array static allocation, it is the max of above */
+#define RTL818X_NR_TX_QUEUES 5
+
struct rtl8180_tx_desc {
__le32 flags;
__le16 rts_duration;
__le16 plcp_len;
__le32 tx_buf;
- __le32 frame_len;
+ union{
+ __le32 frame_len;
+ struct {
+ __le16 frame_len_se;
+ __le16 frame_duration;
+ } __packed;
+ } __packed;
__le32 next_tx_desc;
u8 cw;
u8 retry_limit;
u8 agc;
u8 flags2;
- u32 reserved[2];
+ /* rsvd for 8180/8185.
+ * valid for 8187se but we dont use it
+ */
+ u32 reserved;
+ /* all rsvd for 8180/8185 */
+ __le16 flags3;
+ __le16 frag_qsize;
+} __packed;
+
+struct rtl818x_rx_cmd_desc {
+ __le32 flags;
+ u32 reserved;
+ __le32 rx_buf;
} __packed;
struct rtl8180_rx_desc {
__le32 flags;
__le32 flags2;
- union {
- __le32 rx_buf;
- __le64 tsft;
- };
+ __le64 tsft;
+
+} __packed;
+
+struct rtl8187se_rx_desc {
+ __le32 flags;
+ __le64 tsft;
+ __le32 flags2;
+ __le32 flags3;
+ u32 reserved[3];
} __packed;
struct rtl8180_tx_ring {
@@ -71,28 +108,45 @@ struct rtl8180_priv {
/* rtl8180 driver specific */
spinlock_t lock;
- struct rtl8180_rx_desc *rx_ring;
+ void *rx_ring;
+ u8 rx_ring_sz;
dma_addr_t rx_ring_dma;
unsigned int rx_idx;
struct sk_buff *rx_buf[32];
- struct rtl8180_tx_ring tx_ring[4];
+ struct rtl8180_tx_ring tx_ring[RTL818X_NR_TX_QUEUES];
struct ieee80211_channel channels[14];
struct ieee80211_rate rates[12];
struct ieee80211_supported_band band;
+ struct ieee80211_tx_queue_params queue_param[4];
struct pci_dev *pdev;
u32 rx_conf;
-
- int r8185;
+ u8 slot_time;
+ u16 ack_time;
+
+ enum {
+ RTL818X_CHIP_FAMILY_RTL8180,
+ RTL818X_CHIP_FAMILY_RTL8185,
+ RTL818X_CHIP_FAMILY_RTL8187SE,
+ } chip_family;
u32 anaparam;
u16 rfparam;
u8 csthreshold;
-
+ u8 mac_addr[ETH_ALEN];
+ u8 rf_type;
+ u8 xtal_out;
+ u8 xtal_in;
+ u8 xtal_cal;
+ u8 thermal_meter_val;
+ u8 thermal_meter_en;
+ u8 antenna_diversity_en;
+ u8 antenna_diversity_default;
/* sequence # */
u16 seqno;
};
void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data);
void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam);
+void rtl8180_set_anaparam2(struct rtl8180_priv *priv, u32 anaparam2);
static inline u8 rtl818x_ioread8(struct rtl8180_priv *priv, u8 __iomem *addr)
{
diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c
index d60a5f399022..9bda5bc78eda 100644
--- a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c
+++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c
@@ -282,6 +282,7 @@ static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
msleep(1); /* FIXME: optional? */
+ /* TODO: use set_anaparam2 dev.c_func*/
/* anaparam2 on */
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
@@ -730,32 +731,11 @@ static void rtl8225_rf_set_channel(struct ieee80211_hw *dev,
msleep(10);
}
-static void rtl8225_rf_conf_erp(struct ieee80211_hw *dev,
- struct ieee80211_bss_conf *info)
-{
- struct rtl8180_priv *priv = dev->priv;
-
- if (info->use_short_slot) {
- rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9);
- rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
- rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14);
- rtl818x_iowrite8(priv, &priv->map->EIFS, 81);
- rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73);
- } else {
- rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14);
- rtl818x_iowrite8(priv, &priv->map->SIFS, 0x44);
- rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24);
- rtl818x_iowrite8(priv, &priv->map->EIFS, 81);
- rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5);
- }
-}
-
static const struct rtl818x_rf_ops rtl8225_ops = {
.name = "rtl8225",
.init = rtl8225_rf_init,
.stop = rtl8225_rf_stop,
.set_chan = rtl8225_rf_set_channel,
- .conf_erp = rtl8225_rf_conf_erp,
};
static const struct rtl818x_rf_ops rtl8225z2_ops = {
@@ -763,7 +743,6 @@ static const struct rtl818x_rf_ops rtl8225z2_ops = {
.init = rtl8225z2_rf_init,
.stop = rtl8225_rf_stop,
.set_chan = rtl8225_rf_set_channel,
- .conf_erp = rtl8225_rf_conf_erp,
};
const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *dev)
diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8225se.c b/drivers/net/wireless/rtl818x/rtl8180/rtl8225se.c
new file mode 100644
index 000000000000..fde89866fa8d
--- /dev/null
+++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225se.c
@@ -0,0 +1,475 @@
+
+/* Radio tuning for RTL8225 on RTL8187SE
+ *
+ * Copyright 2009 Larry Finger <Larry.Finger@lwfinger.net>
+ * Copyright 2014 Andrea Merello <andrea.merello@gmail.com>
+ *
+ * Based on the r8180 and Realtek r8187se drivers, which are:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ *
+ * Also based on the rtl8187 driver, which is:
+ * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007 Andrea Merello <andrea.merello@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 <net/mac80211.h>
+
+#include "rtl8180.h"
+#include "rtl8225se.h"
+
+#define PFX "rtl8225 (se) "
+
+static const u32 RF_GAIN_TABLE[] = {
+ 0x0096, 0x0076, 0x0056, 0x0036, 0x0016, 0x01f6, 0x01d6, 0x01b6,
+ 0x0196, 0x0176, 0x00F7, 0x00D7, 0x00B7, 0x0097, 0x0077, 0x0057,
+ 0x0037, 0x00FB, 0x00DB, 0x00BB, 0x00FF, 0x00E3, 0x00C3, 0x00A3,
+ 0x0083, 0x0063, 0x0043, 0x0023, 0x0003, 0x01E3, 0x01C3, 0x01A3,
+ 0x0183, 0x0163, 0x0143, 0x0123, 0x0103
+};
+
+static const u8 cck_ofdm_gain_settings[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+};
+
+static const u8 rtl8225se_tx_gain_cck_ofdm[] = {
+ 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e
+};
+
+static const u8 rtl8225se_tx_power_cck[] = {
+ 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02,
+ 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02,
+ 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02,
+ 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02,
+ 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03,
+ 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03
+};
+
+static const u8 rtl8225se_tx_power_cck_ch14[] = {
+ 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00,
+ 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00
+};
+
+static const u8 rtl8225se_tx_power_ofdm[] = {
+ 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4
+};
+
+static const u32 rtl8225se_chan[] = {
+ 0x0080, 0x0100, 0x0180, 0x0200, 0x0280, 0x0300, 0x0380,
+ 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x074A,
+};
+
+static const u8 rtl8225sez2_tx_power_cck_ch14[] = {
+ 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00
+};
+
+static const u8 rtl8225sez2_tx_power_cck_B[] = {
+ 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x04
+};
+
+static const u8 rtl8225sez2_tx_power_cck_A[] = {
+ 0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04
+};
+
+static const u8 rtl8225sez2_tx_power_cck[] = {
+ 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04
+};
+
+static const u8 ZEBRA_AGC[] = {
+ 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7C, 0x7B, 0x7A,
+ 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72,
+ 0x71, 0x70, 0x6F, 0x6E, 0x6D, 0x6C, 0x6B, 0x6A,
+ 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62,
+ 0x48, 0x47, 0x46, 0x45, 0x44, 0x29, 0x28, 0x27,
+ 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x08, 0x07,
+ 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x15, 0x16,
+ 0x17, 0x17, 0x18, 0x18, 0x19, 0x1a, 0x1a, 0x1b,
+ 0x1b, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e,
+ 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x21,
+ 0x21, 0x21, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24,
+ 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
+ 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F
+};
+
+static const u8 OFDM_CONFIG[] = {
+ 0x10, 0x0F, 0x0A, 0x0C, 0x14, 0xFA, 0xFF, 0x50,
+ 0x00, 0x50, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00,
+ 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xA8, 0x26,
+ 0x32, 0x33, 0x06, 0xA5, 0x6F, 0x55, 0xC8, 0xBB,
+ 0x0A, 0xE1, 0x2C, 0x4A, 0x86, 0x83, 0x34, 0x00,
+ 0x4F, 0x24, 0x6F, 0xC2, 0x03, 0x40, 0x80, 0x00,
+ 0xC0, 0xC1, 0x58, 0xF1, 0x00, 0xC4, 0x90, 0x3e,
+ 0xD8, 0x3C, 0x7B, 0x10, 0x10
+};
+
+static void rtl8187se_three_wire_io(struct ieee80211_hw *dev, u8 *data,
+ u8 len, bool write)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ int i;
+ u8 tmp;
+
+ do {
+ for (i = 0; i < 5; i++) {
+ tmp = rtl818x_ioread8(priv, SW_3W_CMD1);
+ if (!(tmp & 0x3))
+ break;
+ udelay(10);
+ }
+ if (i == 5)
+ wiphy_err(dev->wiphy, PFX
+ "CmdReg: 0x%x RE/WE bits aren't clear\n", tmp);
+
+ tmp = rtl818x_ioread8(priv, &priv->map->rf_sw_config) | 0x02;
+ rtl818x_iowrite8(priv, &priv->map->rf_sw_config, tmp);
+
+ tmp = rtl818x_ioread8(priv, REG_ADDR1(0x84)) & 0xF7;
+ rtl818x_iowrite8(priv, REG_ADDR1(0x84), tmp);
+ if (write) {
+ if (len == 16) {
+ rtl818x_iowrite16(priv, SW_3W_DB0,
+ *(u16 *)data);
+ } else if (len == 64) {
+ rtl818x_iowrite32(priv, SW_3W_DB0_4,
+ *((u32 *)data));
+ rtl818x_iowrite32(priv, SW_3W_DB1_4,
+ *((u32 *)(data + 4)));
+ } else
+ wiphy_err(dev->wiphy, PFX
+ "Unimplemented length\n");
+ } else {
+ rtl818x_iowrite16(priv, SW_3W_DB0, *(u16 *)data);
+ }
+ if (write)
+ tmp = 2;
+ else
+ tmp = 1;
+ rtl818x_iowrite8(priv, SW_3W_CMD1, tmp);
+ for (i = 0; i < 5; i++) {
+ tmp = rtl818x_ioread8(priv, SW_3W_CMD1);
+ if (!(tmp & 0x3))
+ break;
+ udelay(10);
+ }
+ rtl818x_iowrite8(priv, SW_3W_CMD1, 0);
+ if (!write) {
+ *((u16 *)data) = rtl818x_ioread16(priv, SI_DATA_REG);
+ *((u16 *)data) &= 0x0FFF;
+ }
+ } while (0);
+}
+
+static u32 rtl8187se_rf_readreg(struct ieee80211_hw *dev, u8 addr)
+{
+ u32 dataread = addr & 0x0F;
+ rtl8187se_three_wire_io(dev, (u8 *)&dataread, 16, 0);
+ return dataread;
+}
+
+static void rtl8187se_rf_writereg(struct ieee80211_hw *dev, u8 addr, u32 data)
+{
+ u32 outdata = (data << 4) | (u32)(addr & 0x0F);
+ rtl8187se_three_wire_io(dev, (u8 *)&outdata, 16, 1);
+}
+
+
+static void rtl8225se_write_zebra_agc(struct ieee80211_hw *dev)
+{
+ int i;
+
+ for (i = 0; i < 128; i++) {
+ rtl8225se_write_phy_ofdm(dev, 0xF, ZEBRA_AGC[i]);
+ rtl8225se_write_phy_ofdm(dev, 0xE, i+0x80);
+ rtl8225se_write_phy_ofdm(dev, 0xE, 0);
+ }
+}
+
+static void rtl8187se_write_ofdm_config(struct ieee80211_hw *dev)
+{
+ /* write OFDM_CONFIG table */
+ int i;
+
+ for (i = 0; i < 60; i++)
+ rtl8225se_write_phy_ofdm(dev, i, OFDM_CONFIG[i]);
+
+}
+
+static void rtl8225sez2_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u8 cck_power, ofdm_power;
+
+ cck_power = priv->channels[channel - 1].hw_value & 0xFF;
+ if (cck_power > 35)
+ cck_power = 35;
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
+ cck_ofdm_gain_settings[cck_power]);
+
+ usleep_range(1000, 5000);
+ ofdm_power = priv->channels[channel - 1].hw_value >> 8;
+ if (ofdm_power > 35)
+ ofdm_power = 35;
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
+ cck_ofdm_gain_settings[ofdm_power]);
+ if (ofdm_power < 12) {
+ rtl8225se_write_phy_ofdm(dev, 7, 0x5C);
+ rtl8225se_write_phy_ofdm(dev, 9, 0x5C);
+ }
+ if (ofdm_power < 18) {
+ rtl8225se_write_phy_ofdm(dev, 7, 0x54);
+ rtl8225se_write_phy_ofdm(dev, 9, 0x54);
+ } else {
+ rtl8225se_write_phy_ofdm(dev, 7, 0x50);
+ rtl8225se_write_phy_ofdm(dev, 9, 0x50);
+ }
+
+ usleep_range(1000, 5000);
+}
+
+static void rtl8187se_write_rf_gain(struct ieee80211_hw *dev)
+{
+ int i;
+
+ for (i = 0; i <= 36; i++) {
+ rtl8187se_rf_writereg(dev, 0x01, i); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x02, RF_GAIN_TABLE[i]); mdelay(1);
+ }
+}
+
+static void rtl8187se_write_initial_gain(struct ieee80211_hw *dev,
+ int init_gain)
+{
+ switch (init_gain) {
+ default:
+ rtl8225se_write_phy_ofdm(dev, 0x17, 0x26); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x24, 0x86); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x05, 0xFA); mdelay(1);
+ break;
+ case 2:
+ rtl8225se_write_phy_ofdm(dev, 0x17, 0x36); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x24, 0x86); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x05, 0xFA); mdelay(1);
+ break;
+ case 3:
+ rtl8225se_write_phy_ofdm(dev, 0x17, 0x36); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x24, 0x86); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x05, 0xFB); mdelay(1);
+ break;
+ case 4:
+ rtl8225se_write_phy_ofdm(dev, 0x17, 0x46); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x24, 0x86); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x05, 0xFB); mdelay(1);
+ break;
+ case 5:
+ rtl8225se_write_phy_ofdm(dev, 0x17, 0x46); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x24, 0x96); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x05, 0xFB); mdelay(1);
+ break;
+ case 6:
+ rtl8225se_write_phy_ofdm(dev, 0x17, 0x56); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x24, 0x96); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x05, 0xFC); mdelay(1);
+ break;
+ case 7:
+ rtl8225se_write_phy_ofdm(dev, 0x17, 0x56); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x24, 0xA6); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x05, 0xFC); mdelay(1);
+ break;
+ case 8:
+ rtl8225se_write_phy_ofdm(dev, 0x17, 0x66); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x24, 0xB6); mdelay(1);
+ rtl8225se_write_phy_ofdm(dev, 0x05, 0xFC); mdelay(1);
+ break;
+ }
+}
+
+void rtl8225se_rf_init(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u32 rf23, rf24;
+ u8 d_cut = 0;
+ u8 tmp;
+
+ /* Page 1 */
+ rtl8187se_rf_writereg(dev, 0x00, 0x013F); mdelay(1);
+ rf23 = rtl8187se_rf_readreg(dev, 0x08); mdelay(1);
+ rf24 = rtl8187se_rf_readreg(dev, 0x09); mdelay(1);
+ if (rf23 == 0x0818 && rf24 == 0x070C)
+ d_cut = 1;
+
+ wiphy_info(dev->wiphy, "RTL8225-SE version %s\n",
+ d_cut ? "D" : "not-D");
+
+ /* Page 0: reg 0 - 15 */
+ rtl8187se_rf_writereg(dev, 0x00, 0x009F); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x01, 0x06E0); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x02, 0x004D); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x03, 0x07F1); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x04, 0x0975); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x05, 0x0C72); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x06, 0x0AE6); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x07, 0x00CA); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x08, 0x0E1C); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x09, 0x02F0); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0A, 0x09D0); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0B, 0x01BA); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0C, 0x0640); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0D, 0x08DF); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0E, 0x0020); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0F, 0x0990); mdelay(1);
+ /* page 1: reg 16-30 */
+ rtl8187se_rf_writereg(dev, 0x00, 0x013F); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x03, 0x0806); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x04, 0x03A7); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x05, 0x059B); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x06, 0x0081); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x07, 0x01A0); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0A, 0x0001); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0B, 0x0418); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0C, 0x0FBE); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0D, 0x0008); mdelay(1);
+ if (d_cut)
+ rtl8187se_rf_writereg(dev, 0x0E, 0x0807);
+ else
+ rtl8187se_rf_writereg(dev, 0x0E, 0x0806);
+ mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0F, 0x0ACC); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x00, 0x01D7); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x03, 0x0E00); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x04, 0x0E50); mdelay(1);
+
+ rtl8187se_write_rf_gain(dev);
+
+ rtl8187se_rf_writereg(dev, 0x05, 0x0203); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x06, 0x0200); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x00, 0x0137); mdelay(11);
+ rtl8187se_rf_writereg(dev, 0x0D, 0x0008); mdelay(11);
+ rtl8187se_rf_writereg(dev, 0x00, 0x0037); mdelay(11);
+ rtl8187se_rf_writereg(dev, 0x04, 0x0160); mdelay(11);
+ rtl8187se_rf_writereg(dev, 0x07, 0x0080); mdelay(11);
+ rtl8187se_rf_writereg(dev, 0x02, 0x088D); mdelay(221);
+ rtl8187se_rf_writereg(dev, 0x00, 0x0137); mdelay(11);
+ rtl8187se_rf_writereg(dev, 0x07, 0x0000); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x07, 0x0180); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x07, 0x0220); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x07, 0x03E0); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x06, 0x00C1); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0A, 0x0001); mdelay(1);
+ if (priv->xtal_cal) {
+ tmp = (priv->xtal_in << 4) | (priv->xtal_out << 1) |
+ (1 << 11) | (1 << 9);
+ rtl8187se_rf_writereg(dev, 0x0F, tmp);
+ wiphy_info(dev->wiphy, "Xtal cal\n");
+ mdelay(1);
+ } else {
+ wiphy_info(dev->wiphy, "NO Xtal cal\n");
+ rtl8187se_rf_writereg(dev, 0x0F, 0x0ACC);
+ mdelay(1);
+ }
+ /* page 0 */
+ rtl8187se_rf_writereg(dev, 0x00, 0x00BF); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x0D, 0x08DF); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x02, 0x004D); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x04, 0x0975); mdelay(31);
+ rtl8187se_rf_writereg(dev, 0x00, 0x0197); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x05, 0x05AB); mdelay(1);
+
+ rtl8187se_rf_writereg(dev, 0x00, 0x009F); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x01, 0x0000); mdelay(1);
+ rtl8187se_rf_writereg(dev, 0x02, 0x0000); mdelay(1);
+ /* power save parameters */
+ /* TODO: move to dev.c */
+ rtl818x_iowrite8(priv, REG_ADDR1(0x024E),
+ rtl818x_ioread8(priv, REG_ADDR1(0x24E)) & 0x9F);
+ rtl8225se_write_phy_cck(dev, 0x00, 0xC8);
+ rtl8225se_write_phy_cck(dev, 0x06, 0x1C);
+ rtl8225se_write_phy_cck(dev, 0x10, 0x78);
+ rtl8225se_write_phy_cck(dev, 0x2E, 0xD0);
+ rtl8225se_write_phy_cck(dev, 0x2F, 0x06);
+ rtl8225se_write_phy_cck(dev, 0x01, 0x46);
+
+ /* power control */
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, 0x10);
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, 0x1B);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03);
+ rtl8225se_write_phy_ofdm(dev, 0x00, 0x12);
+
+ rtl8225se_write_zebra_agc(dev);
+
+ rtl8225se_write_phy_ofdm(dev, 0x10, 0x00);
+
+ rtl8187se_write_ofdm_config(dev);
+
+ /* turn on RF */
+ rtl8187se_rf_writereg(dev, 0x00, 0x009F); udelay(500);
+ rtl8187se_rf_writereg(dev, 0x04, 0x0972); udelay(500);
+ /* turn on RF again */
+ rtl8187se_rf_writereg(dev, 0x00, 0x009F); udelay(500);
+ rtl8187se_rf_writereg(dev, 0x04, 0x0972); udelay(500);
+ /* turn on BB */
+ rtl8225se_write_phy_ofdm(dev, 0x10, 0x40);
+ rtl8225se_write_phy_ofdm(dev, 0x12, 0x40);
+
+ rtl8187se_write_initial_gain(dev, 4);
+}
+
+void rtl8225se_rf_stop(struct ieee80211_hw *dev)
+{
+ /* checked for 8187se */
+ struct rtl8180_priv *priv = dev->priv;
+
+ /* turn off BB RXIQ matrix to cut off rx signal */
+ rtl8225se_write_phy_ofdm(dev, 0x10, 0x00);
+ rtl8225se_write_phy_ofdm(dev, 0x12, 0x00);
+ /* turn off RF */
+ rtl8187se_rf_writereg(dev, 0x04, 0x0000);
+ rtl8187se_rf_writereg(dev, 0x00, 0x0000);
+
+ usleep_range(1000, 5000);
+ /* turn off A/D and D/A */
+ rtl8180_set_anaparam(priv, RTL8225SE_ANAPARAM_OFF);
+ rtl8180_set_anaparam2(priv, RTL8225SE_ANAPARAM2_OFF);
+}
+
+void rtl8225se_rf_set_channel(struct ieee80211_hw *dev,
+ struct ieee80211_conf *conf)
+{
+ int chan =
+ ieee80211_frequency_to_channel(conf->chandef.chan->center_freq);
+
+ rtl8225sez2_rf_set_tx_power(dev, chan);
+ rtl8187se_rf_writereg(dev, 0x7, rtl8225se_chan[chan - 1]);
+ if ((rtl8187se_rf_readreg(dev, 0x7) & 0x0F80) !=
+ rtl8225se_chan[chan - 1])
+ rtl8187se_rf_writereg(dev, 0x7, rtl8225se_chan[chan - 1]);
+ usleep_range(10000, 20000);
+}
+
+static const struct rtl818x_rf_ops rtl8225se_ops = {
+ .name = "rtl8225-se",
+ .init = rtl8225se_rf_init,
+ .stop = rtl8225se_rf_stop,
+ .set_chan = rtl8225se_rf_set_channel,
+};
+
+const struct rtl818x_rf_ops *rtl8187se_detect_rf(struct ieee80211_hw *dev)
+{
+ return &rtl8225se_ops;
+}
diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8225se.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8225se.h
new file mode 100644
index 000000000000..229400264088
--- /dev/null
+++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225se.h
@@ -0,0 +1,61 @@
+
+/* Definitions for RTL8187SE hardware
+ *
+ * Copyright 2009 Larry Finger <Larry.Finger@lwfinger.net>
+ * Copyright 2014 Andrea Merello <andrea.merello@gmail.com>
+ *
+ * Based on the r8180 and Realtek r8187se drivers, which are:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ *
+ * Also based on the rtl8187 driver, which is:
+ * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007 Andrea Merello <andrea.merello@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 RTL8187SE_RTL8225_H
+#define RTL8187SE_RTL8225_H
+
+#define RTL8225SE_ANAPARAM_ON 0xb0054d00
+#define RTL8225SE_ANAPARAM2_ON 0x000004c6
+
+/* all off except PLL */
+#define RTL8225SE_ANAPARAM_OFF 0xb0054dec
+/* all on including PLL */
+#define RTL8225SE_ANAPARAM_OFF2 0xb0054dfc
+
+#define RTL8225SE_ANAPARAM2_OFF 0x00ff04c6
+
+#define RTL8225SE_ANAPARAM3 0x10
+
+enum rtl8187se_power_state {
+ RTL8187SE_POWER_ON,
+ RTL8187SE_POWER_OFF,
+ RTL8187SE_POWER_SLEEP
+};
+
+static inline void rtl8225se_write_phy_ofdm(struct ieee80211_hw *dev,
+ u8 addr, u8 data)
+{
+ rtl8180_write_phy(dev, addr, data);
+}
+
+static inline void rtl8225se_write_phy_cck(struct ieee80211_hw *dev,
+ u8 addr, u8 data)
+{
+ rtl8180_write_phy(dev, addr, data | 0x10000);
+}
+
+
+const struct rtl818x_rf_ops *rtl8187se_detect_rf(struct ieee80211_hw *);
+void rtl8225se_rf_stop(struct ieee80211_hw *dev);
+void rtl8225se_rf_set_channel(struct ieee80211_hw *dev,
+ struct ieee80211_conf *conf);
+void rtl8225se_rf_conf_erp(struct ieee80211_hw *dev,
+ struct ieee80211_bss_conf *info);
+void rtl8225se_rf_init(struct ieee80211_hw *dev);
+
+#endif /* RTL8187SE_RTL8225_H */
diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c
index fd78df813a85..0ca17cda48fa 100644
--- a/drivers/net/wireless/rtl818x/rtl8187/dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c
@@ -592,7 +592,7 @@ static void rtl8187_set_anaparam(struct rtl8187_priv *priv, bool rfon)
rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam);
rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, anaparam2);
if (priv->is_rtl8187b)
- rtl818x_iowrite8(priv, &priv->map->ANAPARAM3, anaparam3);
+ rtl818x_iowrite8(priv, &priv->map->ANAPARAM3A, anaparam3);
reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE;
rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
@@ -785,7 +785,7 @@ static int rtl8187b_init_hw(struct ieee80211_hw *dev)
rtl818x_iowrite16(priv, (__le16 *)0xFF34, 0x0FFF);
reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
- reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+ reg |= RTL818X_CW_CONF_PERPACKET_RETRY;
rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
/* Auto Rate Fallback Register (ARFR): 1M-54M setting */
@@ -943,8 +943,8 @@ static int rtl8187_start(struct ieee80211_hw *dev)
rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
- reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
- reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+ reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN;
+ reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL;
reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
@@ -986,13 +986,13 @@ static int rtl8187_start(struct ieee80211_hw *dev)
rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
- reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT;
- reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+ reg &= ~RTL818X_CW_CONF_PERPACKET_CW;
+ reg |= RTL818X_CW_CONF_PERPACKET_RETRY;
rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
- reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
- reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+ reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN;
+ reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL;
reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
@@ -1636,10 +1636,10 @@ static int rtl8187_probe(struct usb_interface *intf,
err_free_dmabuf:
kfree(priv->io_dmabuf);
- err_free_dev:
- ieee80211_free_hw(dev);
usb_set_intfdata(intf, NULL);
usb_put_dev(udev);
+ err_free_dev:
+ ieee80211_free_hw(dev);
return err;
}
diff --git a/drivers/net/wireless/rtl818x/rtl818x.h b/drivers/net/wireless/rtl818x/rtl818x.h
index ce23dfd42381..45ea4e1c4abe 100644
--- a/drivers/net/wireless/rtl818x/rtl818x.h
+++ b/drivers/net/wireless/rtl818x/rtl818x.h
@@ -16,30 +16,82 @@
#define RTL818X_H
struct rtl818x_csr {
+
u8 MAC[6];
u8 reserved_0[2];
- __le32 MAR[2];
- u8 RX_FIFO_COUNT;
- u8 reserved_1;
- u8 TX_FIFO_COUNT;
- u8 BQREQ;
- u8 reserved_2[4];
+
+ union {
+ __le32 MAR[2]; /* 0x8 */
+
+ struct{ /* rtl8187se */
+ u8 rf_sw_config; /* 0x8 */
+ u8 reserved_01[3];
+ __le32 TMGDA; /* 0xc */
+ } __packed;
+ } __packed;
+
+ union { /* 0x10 */
+ struct {
+ u8 RX_FIFO_COUNT;
+ u8 reserved_1;
+ u8 TX_FIFO_COUNT;
+ u8 BQREQ;
+ } __packed;
+
+ __le32 TBKDA; /* for 8187se */
+ } __packed;
+
+ __le32 TBEDA; /* 0x14 - for rtl8187se */
+
__le32 TSFT[2];
- __le32 TLPDA;
- __le32 TNPDA;
- __le32 THPDA;
- __le16 BRSR;
- u8 BSSID[6];
- u8 RESP_RATE;
- u8 EIFS;
- u8 reserved_3[1];
- u8 CMD;
+
+ union { /* 0x20 */
+ __le32 TLPDA;
+ __le32 TVIDA; /* for 8187se */
+ } __packed;
+
+ union { /* 0x24 */
+ __le32 TNPDA;
+ __le32 TVODA; /* for 8187se */
+ } __packed;
+
+ /* hi pri ring for all cards */
+ __le32 THPDA; /* 0x28 */
+
+ union { /* 0x2c */
+ struct {
+ u8 reserved_2a;
+ u8 EIFS_8187SE;
+ } __packed;
+
+ __le16 BRSR;
+ } __packed;
+
+ u8 BSSID[6]; /* 0x2e */
+
+ union { /* 0x34 */
+ struct {
+ u8 RESP_RATE;
+ u8 EIFS;
+ } __packed;
+ __le16 BRSR_8187SE;
+ } __packed;
+
+ u8 reserved_3[1]; /* 0x36 */
+ u8 CMD; /* 0x37 */
#define RTL818X_CMD_TX_ENABLE (1 << 2)
#define RTL818X_CMD_RX_ENABLE (1 << 3)
#define RTL818X_CMD_RESET (1 << 4)
- u8 reserved_4[4];
- __le16 INT_MASK;
- __le16 INT_STATUS;
+ u8 reserved_4[4]; /* 0x38 */
+ union {
+ struct {
+ __le16 INT_MASK;
+ __le16 INT_STATUS;
+ } __packed;
+
+ __le32 INT_STATUS_SE; /* 0x3c */
+ } __packed;
+/* status bits for rtl8187 and rtl8180/8185 */
#define RTL818X_INT_RX_OK (1 << 0)
#define RTL818X_INT_RX_ERR (1 << 1)
#define RTL818X_INT_TXL_OK (1 << 2)
@@ -56,7 +108,34 @@ struct rtl818x_csr {
#define RTL818X_INT_BEACON (1 << 13)
#define RTL818X_INT_TIME_OUT (1 << 14)
#define RTL818X_INT_TX_FO (1 << 15)
- __le32 TX_CONF;
+/* status bits for rtl8187se */
+#define RTL818X_INT_SE_TIMER3 (1 << 0)
+#define RTL818X_INT_SE_TIMER2 (1 << 1)
+#define RTL818X_INT_SE_RQ0SOR (1 << 2)
+#define RTL818X_INT_SE_TXBED_OK (1 << 3)
+#define RTL818X_INT_SE_TXBED_ERR (1 << 4)
+#define RTL818X_INT_SE_TXBE_OK (1 << 5)
+#define RTL818X_INT_SE_TXBE_ERR (1 << 6)
+#define RTL818X_INT_SE_RX_OK (1 << 7)
+#define RTL818X_INT_SE_RX_ERR (1 << 8)
+#define RTL818X_INT_SE_TXL_OK (1 << 9)
+#define RTL818X_INT_SE_TXL_ERR (1 << 10)
+#define RTL818X_INT_SE_RX_DU (1 << 11)
+#define RTL818X_INT_SE_RX_FIFO (1 << 12)
+#define RTL818X_INT_SE_TXN_OK (1 << 13)
+#define RTL818X_INT_SE_TXN_ERR (1 << 14)
+#define RTL818X_INT_SE_TXH_OK (1 << 15)
+#define RTL818X_INT_SE_TXH_ERR (1 << 16)
+#define RTL818X_INT_SE_TXB_OK (1 << 17)
+#define RTL818X_INT_SE_TXB_ERR (1 << 18)
+#define RTL818X_INT_SE_ATIM_TO (1 << 19)
+#define RTL818X_INT_SE_BK_TO (1 << 20)
+#define RTL818X_INT_SE_TIMER1 (1 << 21)
+#define RTL818X_INT_SE_TX_FIFO (1 << 22)
+#define RTL818X_INT_SE_WAKEUP (1 << 23)
+#define RTL818X_INT_SE_BK_DMA (1 << 24)
+#define RTL818X_INT_SE_TMGD_OK (1 << 30)
+ __le32 TX_CONF; /* 0x40 */
#define RTL818X_TX_CONF_LOOPBACK_MAC (1 << 17)
#define RTL818X_TX_CONF_LOOPBACK_CONT (3 << 17)
#define RTL818X_TX_CONF_NO_ICV (1 << 19)
@@ -68,6 +147,7 @@ struct rtl818x_csr {
#define RTL818X_TX_CONF_R8185_D (5 << 25)
#define RTL818X_TX_CONF_R8187vD (5 << 25)
#define RTL818X_TX_CONF_R8187vD_B (6 << 25)
+#define RTL818X_TX_CONF_RTL8187SE (6 << 25)
#define RTL818X_TX_CONF_HWVER_MASK (7 << 25)
#define RTL818X_TX_CONF_DISREQQSIZE (1 << 28)
#define RTL818X_TX_CONF_PROBE_DTS (1 << 29)
@@ -122,31 +202,67 @@ struct rtl818x_csr {
u8 PGSELECT;
u8 SECURITY;
__le32 ANAPARAM2;
- u8 reserved_10[12];
- __le16 BEACON_INTERVAL;
- __le16 ATIM_WND;
- __le16 BEACON_INTERVAL_TIME;
- __le16 ATIMTR_INTERVAL;
- u8 PHY_DELAY;
- u8 CARRIER_SENSE_COUNTER;
- u8 reserved_11[2];
- u8 PHY[4];
- __le16 RFPinsOutput;
- __le16 RFPinsEnable;
- __le16 RFPinsSelect;
- __le16 RFPinsInput;
- __le32 RF_PARA;
- __le32 RF_TIMING;
- u8 GP_ENABLE;
- u8 GPIO0;
- u8 GPIO1;
- u8 reserved_12;
- __le32 HSSI_PARA;
- u8 reserved_13[4];
- u8 TX_AGC_CTL;
-#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT (1 << 0)
-#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT (1 << 1)
-#define RTL818X_TX_AGC_CTL_FEEDBACK_ANT (1 << 2)
+ u8 reserved_10[8];
+ __le32 IMR; /* 0x6c - Interrupt mask reg for 8187se */
+#define IMR_TMGDOK ((1 << 30))
+#define IMR_DOT11HINT ((1 << 25)) /* 802.11h Measurement Interrupt */
+#define IMR_BCNDMAINT ((1 << 24)) /* Beacon DMA Interrupt */
+#define IMR_WAKEINT ((1 << 23)) /* Wake Up Interrupt */
+#define IMR_TXFOVW ((1 << 22)) /* Tx FIFO Overflow */
+#define IMR_TIMEOUT1 ((1 << 21)) /* Time Out Interrupt 1 */
+#define IMR_BCNINT ((1 << 20)) /* Beacon Time out */
+#define IMR_ATIMINT ((1 << 19)) /* ATIM Time Out */
+#define IMR_TBDER ((1 << 18)) /* Tx Beacon Descriptor Error */
+#define IMR_TBDOK ((1 << 17)) /* Tx Beacon Descriptor OK */
+#define IMR_THPDER ((1 << 16)) /* Tx High Priority Descriptor Error */
+#define IMR_THPDOK ((1 << 15)) /* Tx High Priority Descriptor OK */
+#define IMR_TVODER ((1 << 14)) /* Tx AC_VO Descriptor Error Int */
+#define IMR_TVODOK ((1 << 13)) /* Tx AC_VO Descriptor OK Interrupt */
+#define IMR_FOVW ((1 << 12)) /* Rx FIFO Overflow Interrupt */
+#define IMR_RDU ((1 << 11)) /* Rx Descriptor Unavailable */
+#define IMR_TVIDER ((1 << 10)) /* Tx AC_VI Descriptor Error */
+#define IMR_TVIDOK ((1 << 9)) /* Tx AC_VI Descriptor OK Interrupt */
+#define IMR_RER ((1 << 8)) /* Rx Error Interrupt */
+#define IMR_ROK ((1 << 7)) /* Receive OK Interrupt */
+#define IMR_TBEDER ((1 << 6)) /* Tx AC_BE Descriptor Error */
+#define IMR_TBEDOK ((1 << 5)) /* Tx AC_BE Descriptor OK */
+#define IMR_TBKDER ((1 << 4)) /* Tx AC_BK Descriptor Error */
+#define IMR_TBKDOK ((1 << 3)) /* Tx AC_BK Descriptor OK */
+#define IMR_RQOSOK ((1 << 2)) /* Rx QoS OK Interrupt */
+#define IMR_TIMEOUT2 ((1 << 1)) /* Time Out Interrupt 2 */
+#define IMR_TIMEOUT3 ((1 << 0)) /* Time Out Interrupt 3 */
+ __le16 BEACON_INTERVAL; /* 0x70 */
+ __le16 ATIM_WND; /* 0x72 */
+ __le16 BEACON_INTERVAL_TIME; /* 0x74 */
+ __le16 ATIMTR_INTERVAL; /* 0x76 */
+ u8 PHY_DELAY; /* 0x78 */
+ u8 CARRIER_SENSE_COUNTER; /* 0x79 */
+ u8 reserved_11[2]; /* 0x7a */
+ u8 PHY[4]; /* 0x7c */
+ __le16 RFPinsOutput; /* 0x80 */
+ __le16 RFPinsEnable; /* 0x82 */
+ __le16 RFPinsSelect; /* 0x84 */
+ __le16 RFPinsInput; /* 0x86 */
+ __le32 RF_PARA; /* 0x88 */
+ __le32 RF_TIMING; /* 0x8c */
+ u8 GP_ENABLE; /* 0x90 */
+ u8 GPIO0; /* 0x91 */
+ u8 GPIO1; /* 0x92 */
+ u8 TPPOLL_STOP; /* 0x93 - rtl8187se only */
+#define RTL818x_TPPOLL_STOP_BQ (1 << 7)
+#define RTL818x_TPPOLL_STOP_VI (1 << 4)
+#define RTL818x_TPPOLL_STOP_VO (1 << 5)
+#define RTL818x_TPPOLL_STOP_BE (1 << 3)
+#define RTL818x_TPPOLL_STOP_BK (1 << 2)
+#define RTL818x_TPPOLL_STOP_MG (1 << 1)
+#define RTL818x_TPPOLL_STOP_HI (1 << 6)
+
+ __le32 HSSI_PARA; /* 0x94 */
+ u8 reserved_13[4]; /* 0x98 */
+ u8 TX_AGC_CTL; /* 0x9c */
+#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN (1 << 0)
+#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL (1 << 1)
+#define RTL818X_TX_AGC_CTL_FEEDBACK_ANT (1 << 2)
u8 TX_GAIN_CCK;
u8 TX_GAIN_OFDM;
u8 TX_ANTENNA;
@@ -158,8 +274,8 @@ struct rtl818x_csr {
u8 SLOT;
u8 reserved_16[5];
u8 CW_CONF;
-#define RTL818X_CW_CONF_PERPACKET_CW_SHIFT (1 << 0)
-#define RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT (1 << 1)
+#define RTL818X_CW_CONF_PERPACKET_CW (1 << 0)
+#define RTL818X_CW_CONF_PERPACKET_RETRY (1 << 1)
u8 CW_VAL;
u8 RATE_FALLBACK;
#define RTL818X_RATE_FALLBACK_ENABLE (1 << 7)
@@ -167,7 +283,8 @@ struct rtl818x_csr {
u8 reserved_17[24];
u8 CONFIG5;
u8 TX_DMA_POLLING;
- u8 reserved_18[2];
+ u8 PHY_PR;
+ u8 reserved_18;
__le16 CWR;
u8 RETRY_CTR;
u8 reserved_19[3];
@@ -179,20 +296,64 @@ struct rtl818x_csr {
__le32 RDSAR;
__le16 TID_AC_MAP;
u8 reserved_20[4];
- u8 ANAPARAM3;
- u8 reserved_21[5];
- __le16 FEMR;
- u8 reserved_22[4];
- __le16 TALLY_CNT;
- u8 TALLY_SEL;
+ union {
+ __le16 ANAPARAM3; /* 0xee */
+ u8 ANAPARAM3A; /* for rtl8187 */
+ };
+
+#define AC_PARAM_TXOP_LIMIT_SHIFT 16
+#define AC_PARAM_ECW_MAX_SHIFT 12
+#define AC_PARAM_ECW_MIN_SHIFT 8
+#define AC_PARAM_AIFS_SHIFT 0
+
+ __le32 AC_VO_PARAM; /* 0xf0 */
+
+ union { /* 0xf4 */
+ __le32 AC_VI_PARAM;
+ __le16 FEMR;
+ } __packed;
+
+ union{ /* 0xf8 */
+ __le32 AC_BE_PARAM; /* rtl8187se */
+ struct{
+ u8 reserved_21[2];
+ __le16 TALLY_CNT; /* 0xfa */
+ } __packed;
+ } __packed;
+
+ union {
+ u8 TALLY_SEL; /* 0xfc */
+ __le32 AC_BK_PARAM;
+
+ } __packed;
+
} __packed;
+/* These are addresses with NON-standard usage.
+ * They have offsets very far from this struct.
+ * I don't like to introduce a ton of "reserved"..
+ * They are for RTL8187SE
+ */
+#define REG_ADDR1(addr) ((u8 __iomem *)priv->map + addr)
+#define REG_ADDR2(addr) ((__le16 __iomem *)priv->map + (addr >> 1))
+#define REG_ADDR4(addr) ((__le32 __iomem *)priv->map + (addr >> 2))
+
+#define FEMR_SE REG_ADDR2(0x1D4)
+#define ARFR REG_ADDR2(0x1E0)
+#define RFSW_CTRL REG_ADDR2(0x272)
+#define SW_3W_DB0 REG_ADDR2(0x274)
+#define SW_3W_DB0_4 REG_ADDR4(0x274)
+#define SW_3W_DB1 REG_ADDR2(0x278)
+#define SW_3W_DB1_4 REG_ADDR4(0x278)
+#define SW_3W_CMD1 REG_ADDR1(0x27D)
+#define PI_DATA_REG REG_ADDR2(0x360)
+#define SI_DATA_REG REG_ADDR2(0x362)
+
struct rtl818x_rf_ops {
char *name;
void (*init)(struct ieee80211_hw *);
void (*stop)(struct ieee80211_hw *);
void (*set_chan)(struct ieee80211_hw *, struct ieee80211_conf *);
- void (*conf_erp)(struct ieee80211_hw *, struct ieee80211_bss_conf *);
u8 (*calc_rssi)(u8 agc, u8 sq);
};
diff --git a/drivers/net/wireless/rtlwifi/Kconfig b/drivers/net/wireless/rtlwifi/Kconfig
index c2ffce7a907c..bf3cf124e4ea 100644
--- a/drivers/net/wireless/rtlwifi/Kconfig
+++ b/drivers/net/wireless/rtlwifi/Kconfig
@@ -5,7 +5,7 @@ menuconfig RTL_CARDS
---help---
This option will enable support for the Realtek mac80211-based
wireless drivers. Drivers rtl8192ce, rtl8192cu, rtl8192se, rtl8192de,
- rtl8723eu, and rtl8188eu share some common code.
+ rtl8723ae, rtl8723be, and rtl8188ae share some common code.
if RTL_CARDS
@@ -48,12 +48,27 @@ config RTL8723AE
depends on PCI
select RTLWIFI
select RTLWIFI_PCI
+ select RTL8723_COMMON
+ select RTLBTCOEXIST
---help---
This is the driver for Realtek RTL8723AE 802.11n PCIe
wireless network adapters.
If you choose to build it as a module, it will be called rtl8723ae
+config RTL8723BE
+ tristate "Realtek RTL8723BE PCIe Wireless Network Adapter"
+ depends on PCI
+ select RTLWIFI
+ select RTLWIFI_PCI
+ select RTL8723_COMMON
+ select RTLBTCOEXIST
+ ---help---
+ This is the driver for Realtek RTL8723BE 802.11n PCIe
+ wireless network adapters.
+
+ If you choose to build it as a module, it will be called rtl8723be
+
config RTL8188EE
tristate "Realtek RTL8188EE Wireless Network Adapter"
depends on PCI
@@ -101,4 +116,14 @@ config RTL8192C_COMMON
depends on RTL8192CE || RTL8192CU
default y
+config RTL8723_COMMON
+ tristate
+ depends on RTL8723AE || RTL8723BE
+ default y
+
+config RTLBTCOEXIST
+ tristate
+ depends on RTL8723AE || RTL8723BE
+ default y
+
endif
diff --git a/drivers/net/wireless/rtlwifi/Makefile b/drivers/net/wireless/rtlwifi/Makefile
index d56f023a4b90..bba36a06abcc 100644
--- a/drivers/net/wireless/rtlwifi/Makefile
+++ b/drivers/net/wireless/rtlwifi/Makefile
@@ -24,6 +24,9 @@ obj-$(CONFIG_RTL8192CU) += rtl8192cu/
obj-$(CONFIG_RTL8192SE) += rtl8192se/
obj-$(CONFIG_RTL8192DE) += rtl8192de/
obj-$(CONFIG_RTL8723AE) += rtl8723ae/
+obj-$(CONFIG_RTL8723BE) += rtl8723be/
obj-$(CONFIG_RTL8188EE) += rtl8188ee/
+obj-$(CONFIG_RTLBTCOEXIST) += btcoexist/
+obj-$(CONFIG_RTL8723_COMMON) += rtl8723com/
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/rtlwifi/btcoexist/Makefile b/drivers/net/wireless/rtlwifi/btcoexist/Makefile
new file mode 100644
index 000000000000..47ceecfcb7dc
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/btcoexist/Makefile
@@ -0,0 +1,7 @@
+btcoexist-objs := halbtc8723b2ant.o \
+ halbtcoutsrc.o \
+ rtl_btc.o
+
+obj-$(CONFIG_RTLBTCOEXIST) += btcoexist.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbt_precomp.h b/drivers/net/wireless/rtlwifi/btcoexist/halbt_precomp.h
new file mode 100644
index 000000000000..d76684eb24d0
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/btcoexist/halbt_precomp.h
@@ -0,0 +1,75 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#ifndef __HALBT_PRECOMP_H__
+#define __HALBT_PRECOMP_H__
+/*************************************************************
+ * include files
+ *************************************************************/
+#include "../wifi.h"
+#include "../efuse.h"
+#include "../base.h"
+#include "../regd.h"
+#include "../cam.h"
+#include "../ps.h"
+#include "../pci.h"
+
+#include "halbtcoutsrc.h"
+
+#include "halbtc8723b2ant.h"
+
+#define BIT0 0x00000001
+#define BIT1 0x00000002
+#define BIT2 0x00000004
+#define BIT3 0x00000008
+#define BIT4 0x00000010
+#define BIT5 0x00000020
+#define BIT6 0x00000040
+#define BIT7 0x00000080
+#define BIT8 0x00000100
+#define BIT9 0x00000200
+#define BIT10 0x00000400
+#define BIT11 0x00000800
+#define BIT12 0x00001000
+#define BIT13 0x00002000
+#define BIT14 0x00004000
+#define BIT15 0x00008000
+#define BIT16 0x00010000
+#define BIT17 0x00020000
+#define BIT18 0x00040000
+#define BIT19 0x00080000
+#define BIT20 0x00100000
+#define BIT21 0x00200000
+#define BIT22 0x00400000
+#define BIT23 0x00800000
+#define BIT24 0x01000000
+#define BIT25 0x02000000
+#define BIT26 0x04000000
+#define BIT27 0x08000000
+#define BIT28 0x10000000
+#define BIT29 0x20000000
+#define BIT30 0x40000000
+#define BIT31 0x80000000
+
+#endif /* __HALBT_PRECOMP_H__ */
diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.c
new file mode 100644
index 000000000000..d916ab9f3c38
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.c
@@ -0,0 +1,3698 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2012 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+/***************************************************************
+ * Description:
+ *
+ * This file is for RTL8723B Co-exist mechanism
+ *
+ * History
+ * 2012/11/15 Cosa first check in.
+ *
+ **************************************************************/
+/**************************************************************
+ * include files
+ **************************************************************/
+#include "halbt_precomp.h"
+/**************************************************************
+ * Global variables, these are static variables
+ **************************************************************/
+static struct coex_dm_8723b_2ant glcoex_dm_8723b_2ant;
+static struct coex_dm_8723b_2ant *coex_dm = &glcoex_dm_8723b_2ant;
+static struct coex_sta_8723b_2ant glcoex_sta_8723b_2ant;
+static struct coex_sta_8723b_2ant *coex_sta = &glcoex_sta_8723b_2ant;
+
+static const char *const glbt_info_src_8723b_2ant[] = {
+ "BT Info[wifi fw]",
+ "BT Info[bt rsp]",
+ "BT Info[bt auto report]",
+};
+
+static u32 glcoex_ver_date_8723b_2ant = 20130731;
+static u32 glcoex_ver_8723b_2ant = 0x3b;
+
+/**************************************************************
+ * local function proto type if needed
+ **************************************************************/
+/**************************************************************
+ * local function start with btc8723b2ant_
+ **************************************************************/
+static u8 btc8723b2ant_bt_rssi_state(u8 level_num, u8 rssi_thresh,
+ u8 rssi_thresh1)
+{
+ s32 bt_rssi = 0;
+ u8 bt_rssi_state = coex_sta->pre_bt_rssi_state;
+
+ bt_rssi = coex_sta->bt_rssi;
+
+ if (level_num == 2) {
+ if ((coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_LOW) ||
+ (coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) {
+ if (bt_rssi >= rssi_thresh +
+ BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) {
+ bt_rssi_state = BTC_RSSI_STATE_HIGH;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi state "
+ "switch to High\n");
+ } else {
+ bt_rssi_state = BTC_RSSI_STATE_STAY_LOW;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi state "
+ "stay at Low\n");
+ }
+ } else {
+ if (bt_rssi < rssi_thresh) {
+ bt_rssi_state = BTC_RSSI_STATE_LOW;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi state "
+ "switch to Low\n");
+ } else {
+ bt_rssi_state = BTC_RSSI_STATE_STAY_HIGH;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi state "
+ "stay at High\n");
+ }
+ }
+ } else if (level_num == 3) {
+ if (rssi_thresh > rssi_thresh1) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi thresh error!!\n");
+ return coex_sta->pre_bt_rssi_state;
+ }
+
+ if ((coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_LOW) ||
+ (coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) {
+ if (bt_rssi >= rssi_thresh +
+ BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) {
+ bt_rssi_state = BTC_RSSI_STATE_MEDIUM;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi state "
+ "switch to Medium\n");
+ } else {
+ bt_rssi_state = BTC_RSSI_STATE_STAY_LOW;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi state "
+ "stay at Low\n");
+ }
+ } else if ((coex_sta->pre_bt_rssi_state ==
+ BTC_RSSI_STATE_MEDIUM) ||
+ (coex_sta->pre_bt_rssi_state ==
+ BTC_RSSI_STATE_STAY_MEDIUM)) {
+ if (bt_rssi >= rssi_thresh1 +
+ BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) {
+ bt_rssi_state = BTC_RSSI_STATE_HIGH;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi state "
+ "switch to High\n");
+ } else if (bt_rssi < rssi_thresh) {
+ bt_rssi_state = BTC_RSSI_STATE_LOW;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi state "
+ "switch to Low\n");
+ } else {
+ bt_rssi_state = BTC_RSSI_STATE_STAY_MEDIUM;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi state "
+ "stay at Medium\n");
+ }
+ } else {
+ if (bt_rssi < rssi_thresh1) {
+ bt_rssi_state = BTC_RSSI_STATE_MEDIUM;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi state "
+ "switch to Medium\n");
+ } else {
+ bt_rssi_state = BTC_RSSI_STATE_STAY_HIGH;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE,
+ "[BTCoex], BT Rssi state "
+ "stay at High\n");
+ }
+ }
+ }
+
+ coex_sta->pre_bt_rssi_state = bt_rssi_state;
+
+ return bt_rssi_state;
+}
+
+static u8 btc8723b2ant_wifi_rssi_state(struct btc_coexist *btcoexist,
+ u8 index, u8 level_num,
+ u8 rssi_thresh, u8 rssi_thresh1)
+{
+ s32 wifi_rssi = 0;
+ u8 wifi_rssi_state = coex_sta->pre_wifi_rssi_state[index];
+
+ btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi);
+
+ if (level_num == 2) {
+ if ((coex_sta->pre_wifi_rssi_state[index] ==
+ BTC_RSSI_STATE_LOW) ||
+ (coex_sta->pre_wifi_rssi_state[index] ==
+ BTC_RSSI_STATE_STAY_LOW)) {
+ if (wifi_rssi >= rssi_thresh +
+ BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) {
+ wifi_rssi_state = BTC_RSSI_STATE_HIGH;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI state "
+ "switch to High\n");
+ } else {
+ wifi_rssi_state = BTC_RSSI_STATE_STAY_LOW;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI state "
+ "stay at Low\n");
+ }
+ } else {
+ if (wifi_rssi < rssi_thresh) {
+ wifi_rssi_state = BTC_RSSI_STATE_LOW;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI state "
+ "switch to Low\n");
+ } else {
+ wifi_rssi_state = BTC_RSSI_STATE_STAY_HIGH;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI state "
+ "stay at High\n");
+ }
+ }
+ } else if (level_num == 3) {
+ if (rssi_thresh > rssi_thresh1) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI thresh error!!\n");
+ return coex_sta->pre_wifi_rssi_state[index];
+ }
+
+ if ((coex_sta->pre_wifi_rssi_state[index] ==
+ BTC_RSSI_STATE_LOW) ||
+ (coex_sta->pre_wifi_rssi_state[index] ==
+ BTC_RSSI_STATE_STAY_LOW)) {
+ if (wifi_rssi >= rssi_thresh +
+ BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) {
+ wifi_rssi_state = BTC_RSSI_STATE_MEDIUM;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI state "
+ "switch to Medium\n");
+ } else {
+ wifi_rssi_state = BTC_RSSI_STATE_STAY_LOW;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI state "
+ "stay at Low\n");
+ }
+ } else if ((coex_sta->pre_wifi_rssi_state[index] ==
+ BTC_RSSI_STATE_MEDIUM) ||
+ (coex_sta->pre_wifi_rssi_state[index] ==
+ BTC_RSSI_STATE_STAY_MEDIUM)) {
+ if (wifi_rssi >= rssi_thresh1 +
+ BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) {
+ wifi_rssi_state = BTC_RSSI_STATE_HIGH;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI state "
+ "switch to High\n");
+ } else if (wifi_rssi < rssi_thresh) {
+ wifi_rssi_state = BTC_RSSI_STATE_LOW;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI state "
+ "switch to Low\n");
+ } else {
+ wifi_rssi_state = BTC_RSSI_STATE_STAY_MEDIUM;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI state "
+ "stay at Medium\n");
+ }
+ } else {
+ if (wifi_rssi < rssi_thresh1) {
+ wifi_rssi_state = BTC_RSSI_STATE_MEDIUM;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI state "
+ "switch to Medium\n");
+ } else {
+ wifi_rssi_state = BTC_RSSI_STATE_STAY_HIGH;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_WIFI_RSSI_STATE,
+ "[BTCoex], wifi RSSI state "
+ "stay at High\n");
+ }
+ }
+ }
+
+ coex_sta->pre_wifi_rssi_state[index] = wifi_rssi_state;
+
+ return wifi_rssi_state;
+}
+
+static void btc8723b2ant_monitor_bt_ctr(struct btc_coexist *btcoexist)
+{
+ u32 reg_hp_txrx, reg_lp_txrx, u32tmp;
+ u32 reg_hp_tx = 0, reg_hp_rx = 0;
+ u32 reg_lp_tx = 0, reg_lp_rx = 0;
+
+ reg_hp_txrx = 0x770;
+ reg_lp_txrx = 0x774;
+
+ u32tmp = btcoexist->btc_read_4byte(btcoexist, reg_hp_txrx);
+ reg_hp_tx = u32tmp & MASKLWORD;
+ reg_hp_rx = (u32tmp & MASKHWORD) >> 16;
+
+ u32tmp = btcoexist->btc_read_4byte(btcoexist, reg_lp_txrx);
+ reg_lp_tx = u32tmp & MASKLWORD;
+ reg_lp_rx = (u32tmp & MASKHWORD) >> 16;
+
+ coex_sta->high_priority_tx = reg_hp_tx;
+ coex_sta->high_priority_rx = reg_hp_rx;
+ coex_sta->low_priority_tx = reg_lp_tx;
+ coex_sta->low_priority_rx = reg_lp_rx;
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_MONITOR,
+ "[BTCoex], High Priority Tx/Rx(reg 0x%x)=0x%x(%d)/0x%x(%d)\n",
+ reg_hp_txrx, reg_hp_tx, reg_hp_tx, reg_hp_rx, reg_hp_rx);
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_MONITOR,
+ "[BTCoex], Low Priority Tx/Rx(reg 0x%x)=0x%x(%d)/0x%x(%d)\n",
+ reg_lp_txrx, reg_lp_tx, reg_lp_tx, reg_lp_rx, reg_lp_rx);
+
+ /* reset counter */
+ btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc);
+}
+
+static bool btc8723b2ant_is_wifi_status_changed(struct btc_coexist *btcoexist)
+{
+ static bool pre_wifi_busy;
+ static bool pre_under_4way;
+ static bool pre_bt_hs_on;
+ bool wifi_busy = false, under_4way = false, bt_hs_on = false;
+ bool wifi_connected = false;
+
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED,
+ &wifi_connected);
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy);
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on);
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_4_WAY_PROGRESS,
+ &under_4way);
+
+ if (wifi_connected) {
+ if (wifi_busy != pre_wifi_busy) {
+ pre_wifi_busy = wifi_busy;
+ return true;
+ }
+
+ if (under_4way != pre_under_4way) {
+ pre_under_4way = under_4way;
+ return true;
+ }
+
+ if (bt_hs_on != pre_bt_hs_on) {
+ pre_bt_hs_on = bt_hs_on;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void btc8723b2ant_update_bt_link_info(struct btc_coexist *btcoexist)
+{
+ /*struct btc_stack_info *stack_info = &btcoexist->stack_info;*/
+ struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info;
+ bool bt_hs_on = false;
+
+#if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 1) /* profile from bt patch */
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on);
+
+ bt_link_info->bt_link_exist = coex_sta->bt_link_exist;
+ bt_link_info->sco_exist = coex_sta->sco_exist;
+ bt_link_info->a2dp_exist = coex_sta->a2dp_exist;
+ bt_link_info->pan_exist = coex_sta->pan_exist;
+ bt_link_info->hid_exist = coex_sta->hid_exist;
+
+ /* work around for HS mode. */
+ if (bt_hs_on) {
+ bt_link_info->pan_exist = true;
+ bt_link_info->bt_link_exist = true;
+ }
+#else /* profile from bt stack */
+ bt_link_info->bt_link_exist = stack_info->bt_link_exist;
+ bt_link_info->sco_exist = stack_info->sco_exist;
+ bt_link_info->a2dp_exist = stack_info->a2dp_exist;
+ bt_link_info->pan_exist = stack_info->pan_exist;
+ bt_link_info->hid_exist = stack_info->hid_exist;
+
+ /*for win-8 stack HID report error*/
+ if (!stack_info->hid_exist)
+ stack_info->hid_exist = coex_sta->hid_exist;
+ /*sync BTInfo with BT firmware and stack*/
+ /* when stack HID report error, here we use the info from bt fw.*/
+ if (!stack_info->bt_link_exist)
+ stack_info->bt_link_exist = coex_sta->bt_link_exist;
+#endif
+ /* check if Sco only */
+ if (bt_link_info->sco_exist && !bt_link_info->a2dp_exist &&
+ !bt_link_info->pan_exist && !bt_link_info->hid_exist)
+ bt_link_info->sco_only = true;
+ else
+ bt_link_info->sco_only = false;
+
+ /* check if A2dp only */
+ if (!bt_link_info->sco_exist && bt_link_info->a2dp_exist &&
+ !bt_link_info->pan_exist && !bt_link_info->hid_exist)
+ bt_link_info->a2dp_only = true;
+ else
+ bt_link_info->a2dp_only = false;
+
+ /* check if Pan only */
+ if (!bt_link_info->sco_exist && !bt_link_info->a2dp_exist &&
+ bt_link_info->pan_exist && !bt_link_info->hid_exist)
+ bt_link_info->pan_only = true;
+ else
+ bt_link_info->pan_only = false;
+
+ /* check if Hid only */
+ if (!bt_link_info->sco_exist && !bt_link_info->a2dp_exist &&
+ !bt_link_info->pan_exist && bt_link_info->hid_exist)
+ bt_link_info->hid_only = true;
+ else
+ bt_link_info->hid_only = false;
+}
+
+static u8 btc8723b2ant_action_algorithm(struct btc_coexist *btcoexist)
+{
+ struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info;
+ bool bt_hs_on = false;
+ u8 algorithm = BT_8723B_2ANT_COEX_ALGO_UNDEFINED;
+ u8 num_of_diff_profile = 0;
+
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on);
+
+ if (!bt_link_info->bt_link_exist) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], No BT link exists!!!\n");
+ return algorithm;
+ }
+
+ if (bt_link_info->sco_exist)
+ num_of_diff_profile++;
+ if (bt_link_info->hid_exist)
+ num_of_diff_profile++;
+ if (bt_link_info->pan_exist)
+ num_of_diff_profile++;
+ if (bt_link_info->a2dp_exist)
+ num_of_diff_profile++;
+
+ if (num_of_diff_profile == 1) {
+ if (bt_link_info->sco_exist) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], SCO only\n");
+ algorithm = BT_8723B_2ANT_COEX_ALGO_SCO;
+ } else {
+ if (bt_link_info->hid_exist) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], HID only\n");
+ algorithm = BT_8723B_2ANT_COEX_ALGO_HID;
+ } else if (bt_link_info->a2dp_exist) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], A2DP only\n");
+ algorithm = BT_8723B_2ANT_COEX_ALGO_A2DP;
+ } else if (bt_link_info->pan_exist) {
+ if (bt_hs_on) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], PAN(HS) only\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_PANHS;
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], PAN(EDR) only\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_PANEDR;
+ }
+ }
+ }
+ } else if (num_of_diff_profile == 2) {
+ if (bt_link_info->sco_exist) {
+ if (bt_link_info->hid_exist) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], SCO + HID\n");
+ algorithm = BT_8723B_2ANT_COEX_ALGO_PANEDR_HID;
+ } else if (bt_link_info->a2dp_exist) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], SCO + A2DP ==> SCO\n");
+ algorithm = BT_8723B_2ANT_COEX_ALGO_PANEDR_HID;
+ } else if (bt_link_info->pan_exist) {
+ if (bt_hs_on) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], SCO + PAN(HS)\n");
+ algorithm = BT_8723B_2ANT_COEX_ALGO_SCO;
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], SCO + PAN(EDR)\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_PANEDR_HID;
+ }
+ }
+ } else {
+ if (bt_link_info->hid_exist &&
+ bt_link_info->a2dp_exist) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], HID + A2DP\n");
+ algorithm = BT_8723B_2ANT_COEX_ALGO_HID_A2DP;
+ } else if (bt_link_info->hid_exist &&
+ bt_link_info->pan_exist) {
+ if (bt_hs_on) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], HID + PAN(HS)\n");
+ algorithm = BT_8723B_2ANT_COEX_ALGO_HID;
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], HID + PAN(EDR)\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_PANEDR_HID;
+ }
+ } else if (bt_link_info->pan_exist &&
+ bt_link_info->a2dp_exist) {
+ if (bt_hs_on) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], A2DP + PAN(HS)\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_A2DP_PANHS;
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex],A2DP + PAN(EDR)\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_PANEDR_A2DP;
+ }
+ }
+ }
+ } else if (num_of_diff_profile == 3) {
+ if (bt_link_info->sco_exist) {
+ if (bt_link_info->hid_exist &&
+ bt_link_info->a2dp_exist) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], SCO + HID + A2DP"
+ " ==> HID\n");
+ algorithm = BT_8723B_2ANT_COEX_ALGO_PANEDR_HID;
+ } else if (bt_link_info->hid_exist &&
+ bt_link_info->pan_exist) {
+ if (bt_hs_on) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], SCO + HID + "
+ "PAN(HS)\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_PANEDR_HID;
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], SCO + HID + "
+ "PAN(EDR)\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_PANEDR_HID;
+ }
+ } else if (bt_link_info->pan_exist &&
+ bt_link_info->a2dp_exist) {
+ if (bt_hs_on) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], SCO + A2DP + "
+ "PAN(HS)\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_PANEDR_HID;
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], SCO + A2DP + "
+ "PAN(EDR) ==> HID\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_PANEDR_HID;
+ }
+ }
+ } else {
+ if (bt_link_info->hid_exist &&
+ bt_link_info->pan_exist &&
+ bt_link_info->a2dp_exist) {
+ if (bt_hs_on) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], HID + A2DP + "
+ "PAN(HS)\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_HID_A2DP;
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], HID + A2DP + "
+ "PAN(EDR)\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_HID_A2DP_PANEDR;
+ }
+ }
+ }
+ } else if (num_of_diff_profile >= 3) {
+ if (bt_link_info->sco_exist) {
+ if (bt_link_info->hid_exist &&
+ bt_link_info->pan_exist &&
+ bt_link_info->a2dp_exist) {
+ if (bt_hs_on) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Error!!! SCO + HID"
+ " + A2DP + PAN(HS)\n");
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], SCO + HID + A2DP +"
+ " PAN(EDR)==>PAN(EDR)+HID\n");
+ algorithm =
+ BT_8723B_2ANT_COEX_ALGO_PANEDR_HID;
+ }
+ }
+ }
+ }
+ return algorithm;
+}
+
+static bool btc8723b_need_dec_pwr(struct btc_coexist *btcoexist)
+{
+ bool ret = false;
+ bool bt_hs_on = false, wifi_connected = false;
+ s32 bt_hs_rssi = 0;
+ u8 bt_rssi_state;
+
+ if (!btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on))
+ return false;
+ if (!btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED,
+ &wifi_connected))
+ return false;
+ if (!btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi))
+ return false;
+
+ bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0);
+
+ if (wifi_connected) {
+ if (bt_hs_on) {
+ if (bt_hs_rssi > 37) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW,
+ "[BTCoex], Need to decrease bt "
+ "power for HS mode!!\n");
+ ret = true;
+ }
+ } else {
+ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW,
+ "[BTCoex], Need to decrease bt "
+ "power for Wifi is connected!!\n");
+ ret = true;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void btc8723b2ant_set_fw_dac_swing_level(struct btc_coexist *btcoexist,
+ u8 dac_swing_lvl)
+{
+ u8 h2c_parameter[1] = {0};
+
+ /* There are several type of dacswing
+ * 0x18/ 0x10/ 0xc/ 0x8/ 0x4/ 0x6
+ */
+ h2c_parameter[0] = dac_swing_lvl;
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC,
+ "[BTCoex], Set Dac Swing Level=0x%x\n", dac_swing_lvl);
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC,
+ "[BTCoex], FW write 0x64=0x%x\n", h2c_parameter[0]);
+
+ btcoexist->btc_fill_h2c(btcoexist, 0x64, 1, h2c_parameter);
+}
+
+static void btc8723b2ant_set_fw_dec_bt_pwr(struct btc_coexist *btcoexist,
+ bool dec_bt_pwr)
+{
+ u8 h2c_parameter[1] = {0};
+
+ h2c_parameter[0] = 0;
+
+ if (dec_bt_pwr)
+ h2c_parameter[0] |= BIT1;
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC,
+ "[BTCoex], decrease Bt Power : %s, FW write 0x62=0x%x\n",
+ (dec_bt_pwr ? "Yes!!" : "No!!"), h2c_parameter[0]);
+
+ btcoexist->btc_fill_h2c(btcoexist, 0x62, 1, h2c_parameter);
+}
+
+static void btc8723b2ant_dec_bt_pwr(struct btc_coexist *btcoexist,
+ bool force_exec, bool dec_bt_pwr)
+{
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW,
+ "[BTCoex], %s Dec BT power = %s\n",
+ (force_exec ? "force to" : ""), (dec_bt_pwr ? "ON" : "OFF"));
+ coex_dm->cur_dec_bt_pwr = dec_bt_pwr;
+
+ if (!force_exec) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], bPreDecBtPwr=%d, bCurDecBtPwr=%d\n",
+ coex_dm->pre_dec_bt_pwr, coex_dm->cur_dec_bt_pwr);
+
+ if (coex_dm->pre_dec_bt_pwr == coex_dm->cur_dec_bt_pwr)
+ return;
+ }
+ btc8723b2ant_set_fw_dec_bt_pwr(btcoexist, coex_dm->cur_dec_bt_pwr);
+
+ coex_dm->pre_dec_bt_pwr = coex_dm->cur_dec_bt_pwr;
+}
+
+static void btc8723b2ant_fw_dac_swing_lvl(struct btc_coexist *btcoexist,
+ bool force_exec, u8 fw_dac_swing_lvl)
+{
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW,
+ "[BTCoex], %s set FW Dac Swing level = %d\n",
+ (force_exec ? "force to" : ""), fw_dac_swing_lvl);
+ coex_dm->cur_fw_dac_swing_lvl = fw_dac_swing_lvl;
+
+ if (!force_exec) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], preFwDacSwingLvl=%d, "
+ "curFwDacSwingLvl=%d\n",
+ coex_dm->pre_fw_dac_swing_lvl,
+ coex_dm->cur_fw_dac_swing_lvl);
+
+ if (coex_dm->pre_fw_dac_swing_lvl ==
+ coex_dm->cur_fw_dac_swing_lvl)
+ return;
+ }
+
+ btc8723b2ant_set_fw_dac_swing_level(btcoexist,
+ coex_dm->cur_fw_dac_swing_lvl);
+ coex_dm->pre_fw_dac_swing_lvl = coex_dm->cur_fw_dac_swing_lvl;
+}
+
+static void btc8723b2ant_set_sw_rf_rx_lpf_corner(struct btc_coexist *btcoexist,
+ bool rx_rf_shrink_on)
+{
+ if (rx_rf_shrink_on) {
+ /* Shrink RF Rx LPF corner */
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], Shrink RF Rx LPF corner!!\n");
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1e,
+ 0xfffff, 0xffffc);
+ } else {
+ /* Resume RF Rx LPF corner */
+ /* After initialized, we can use coex_dm->btRf0x1eBackup */
+ if (btcoexist->initilized) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], Resume RF Rx LPF corner!!\n");
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1e,
+ 0xfffff,
+ coex_dm->bt_rf0x1e_backup);
+ }
+ }
+}
+
+static void btc8723b2ant_rf_shrink(struct btc_coexist *btcoexist,
+ bool force_exec, bool rx_rf_shrink_on)
+{
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW,
+ "[BTCoex], %s turn Rx RF Shrink = %s\n",
+ (force_exec ? "force to" : ""), (rx_rf_shrink_on ?
+ "ON" : "OFF"));
+ coex_dm->cur_rf_rx_lpf_shrink = rx_rf_shrink_on;
+
+ if (!force_exec) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL,
+ "[BTCoex], bPreRfRxLpfShrink=%d, "
+ "bCurRfRxLpfShrink=%d\n",
+ coex_dm->pre_rf_rx_lpf_shrink,
+ coex_dm->cur_rf_rx_lpf_shrink);
+
+ if (coex_dm->pre_rf_rx_lpf_shrink ==
+ coex_dm->cur_rf_rx_lpf_shrink)
+ return;
+ }
+ btc8723b2ant_set_sw_rf_rx_lpf_corner(btcoexist,
+ coex_dm->cur_rf_rx_lpf_shrink);
+
+ coex_dm->pre_rf_rx_lpf_shrink = coex_dm->cur_rf_rx_lpf_shrink;
+}
+
+static void btc8723b_set_penalty_txrate(struct btc_coexist *btcoexist,
+ bool low_penalty_ra)
+{
+ u8 h2c_parameter[6] = {0};
+
+ h2c_parameter[0] = 0x6; /* opCode, 0x6= Retry_Penalty*/
+
+ if (low_penalty_ra) {
+ h2c_parameter[1] |= BIT0;
+ /*normal rate except MCS7/6/5, OFDM54/48/36*/
+ h2c_parameter[2] = 0x00;
+ h2c_parameter[3] = 0xf7; /*MCS7 or OFDM54*/
+ h2c_parameter[4] = 0xf8; /*MCS6 or OFDM48*/
+ h2c_parameter[5] = 0xf9; /*MCS5 or OFDM36*/
+ }
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC,
+ "[BTCoex], set WiFi Low-Penalty Retry: %s",
+ (low_penalty_ra ? "ON!!" : "OFF!!"));
+
+ btcoexist->btc_fill_h2c(btcoexist, 0x69, 6, h2c_parameter);
+}
+
+static void btc8723b2ant_low_penalty_ra(struct btc_coexist *btcoexist,
+ bool force_exec, bool low_penalty_ra)
+{
+ /*return; */
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW,
+ "[BTCoex], %s turn LowPenaltyRA = %s\n",
+ (force_exec ? "force to" : ""), (low_penalty_ra ?
+ "ON" : "OFF"));
+ coex_dm->cur_low_penalty_ra = low_penalty_ra;
+
+ if (!force_exec) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL,
+ "[BTCoex], bPreLowPenaltyRa=%d, "
+ "bCurLowPenaltyRa=%d\n",
+ coex_dm->pre_low_penalty_ra,
+ coex_dm->cur_low_penalty_ra);
+
+ if (coex_dm->pre_low_penalty_ra == coex_dm->cur_low_penalty_ra)
+ return;
+ }
+ btc8723b_set_penalty_txrate(btcoexist, coex_dm->cur_low_penalty_ra);
+
+ coex_dm->pre_low_penalty_ra = coex_dm->cur_low_penalty_ra;
+}
+
+static void btc8723b2ant_set_dac_swing_reg(struct btc_coexist *btcoexist,
+ u32 level)
+{
+ u8 val = (u8) level;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], Write SwDacSwing = 0x%x\n", level);
+ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x883, 0x3e, val);
+}
+
+static void btc8723b2ant_set_sw_fulltime_dac_swing(struct btc_coexist *btcoex,
+ bool sw_dac_swing_on,
+ u32 sw_dac_swing_lvl)
+{
+ if (sw_dac_swing_on)
+ btc8723b2ant_set_dac_swing_reg(btcoex, sw_dac_swing_lvl);
+ else
+ btc8723b2ant_set_dac_swing_reg(btcoex, 0x18);
+}
+
+
+static void btc8723b2ant_dac_swing(struct btc_coexist *btcoexist,
+ bool force_exec, bool dac_swing_on,
+ u32 dac_swing_lvl)
+{
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW,
+ "[BTCoex], %s turn DacSwing=%s, dac_swing_lvl=0x%x\n",
+ (force_exec ? "force to" : ""),
+ (dac_swing_on ? "ON" : "OFF"), dac_swing_lvl);
+ coex_dm->cur_dac_swing_on = dac_swing_on;
+ coex_dm->cur_dac_swing_lvl = dac_swing_lvl;
+
+ if (!force_exec) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL,
+ "[BTCoex], bPreDacSwingOn=%d, preDacSwingLvl=0x%x,"
+ " bCurDacSwingOn=%d, curDacSwingLvl=0x%x\n",
+ coex_dm->pre_dac_swing_on, coex_dm->pre_dac_swing_lvl,
+ coex_dm->cur_dac_swing_on,
+ coex_dm->cur_dac_swing_lvl);
+
+ if ((coex_dm->pre_dac_swing_on == coex_dm->cur_dac_swing_on) &&
+ (coex_dm->pre_dac_swing_lvl == coex_dm->cur_dac_swing_lvl))
+ return;
+ }
+ mdelay(30);
+ btc8723b2ant_set_sw_fulltime_dac_swing(btcoexist, dac_swing_on,
+ dac_swing_lvl);
+
+ coex_dm->pre_dac_swing_on = coex_dm->cur_dac_swing_on;
+ coex_dm->pre_dac_swing_lvl = coex_dm->cur_dac_swing_lvl;
+}
+
+static void btc8723b2ant_set_agc_table(struct btc_coexist *btcoexist,
+ bool agc_table_en)
+{
+ u8 rssi_adjust_val = 0;
+
+ /* BB AGC Gain Table */
+ if (agc_table_en) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], BB Agc Table On!\n");
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6e1A0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6d1B0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6c1C0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6b1D0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6a1E0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x691F0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x68200001);
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], BB Agc Table Off!\n");
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xaa1A0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa91B0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa81C0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa71D0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa61E0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa51F0001);
+ btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa4200001);
+ }
+
+
+ /* RF Gain */
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xef, 0xfffff, 0x02000);
+ if (agc_table_en) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], Agc Table On!\n");
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b,
+ 0xfffff, 0x38fff);
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b,
+ 0xfffff, 0x38ffe);
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], Agc Table Off!\n");
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b,
+ 0xfffff, 0x380c3);
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b,
+ 0xfffff, 0x28ce6);
+ }
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xef, 0xfffff, 0x0);
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xed, 0xfffff, 0x1);
+
+ if (agc_table_en) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], Agc Table On!\n");
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40,
+ 0xfffff, 0x38fff);
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40,
+ 0xfffff, 0x38ffe);
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], Agc Table Off!\n");
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40,
+ 0xfffff, 0x380c3);
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40,
+ 0xfffff, 0x28ce6);
+ }
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xed, 0xfffff, 0x0);
+
+ /* set rssiAdjustVal for wifi module. */
+ if (agc_table_en)
+ rssi_adjust_val = 8;
+ btcoexist->btc_set(btcoexist, BTC_SET_U1_RSSI_ADJ_VAL_FOR_AGC_TABLE_ON,
+ &rssi_adjust_val);
+}
+
+static void btc8723b2ant_agc_table(struct btc_coexist *btcoexist,
+ bool force_exec, bool agc_table_en)
+{
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW,
+ "[BTCoex], %s %s Agc Table\n",
+ (force_exec ? "force to" : ""),
+ (agc_table_en ? "Enable" : "Disable"));
+ coex_dm->cur_agc_table_en = agc_table_en;
+
+ if (!force_exec) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL,
+ "[BTCoex], bPreAgcTableEn=%d, bCurAgcTableEn=%d\n",
+ coex_dm->pre_agc_table_en, coex_dm->cur_agc_table_en);
+
+ if (coex_dm->pre_agc_table_en == coex_dm->cur_agc_table_en)
+ return;
+ }
+ btc8723b2ant_set_agc_table(btcoexist, agc_table_en);
+
+ coex_dm->pre_agc_table_en = coex_dm->cur_agc_table_en;
+}
+
+static void btc8723b2ant_set_coex_table(struct btc_coexist *btcoexist,
+ u32 val0x6c0, u32 val0x6c4,
+ u32 val0x6c8, u8 val0x6cc)
+{
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], set coex table, set 0x6c0=0x%x\n", val0x6c0);
+ btcoexist->btc_write_4byte(btcoexist, 0x6c0, val0x6c0);
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], set coex table, set 0x6c4=0x%x\n", val0x6c4);
+ btcoexist->btc_write_4byte(btcoexist, 0x6c4, val0x6c4);
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], set coex table, set 0x6c8=0x%x\n", val0x6c8);
+ btcoexist->btc_write_4byte(btcoexist, 0x6c8, val0x6c8);
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC,
+ "[BTCoex], set coex table, set 0x6cc=0x%x\n", val0x6cc);
+ btcoexist->btc_write_1byte(btcoexist, 0x6cc, val0x6cc);
+}
+
+static void btc8723b2ant_coex_table(struct btc_coexist *btcoexist,
+ bool force_exec, u32 val0x6c0,
+ u32 val0x6c4, u32 val0x6c8,
+ u8 val0x6cc)
+{
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW,
+ "[BTCoex], %s write Coex Table 0x6c0=0x%x,"
+ " 0x6c4=0x%x, 0x6c8=0x%x, 0x6cc=0x%x\n",
+ (force_exec ? "force to" : ""), val0x6c0,
+ val0x6c4, val0x6c8, val0x6cc);
+ coex_dm->cur_val0x6c0 = val0x6c0;
+ coex_dm->cur_val0x6c4 = val0x6c4;
+ coex_dm->cur_val0x6c8 = val0x6c8;
+ coex_dm->cur_val0x6cc = val0x6cc;
+
+ if (!force_exec) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL,
+ "[BTCoex], preVal0x6c0=0x%x, "
+ "preVal0x6c4=0x%x, preVal0x6c8=0x%x, "
+ "preVal0x6cc=0x%x !!\n",
+ coex_dm->pre_val0x6c0, coex_dm->pre_val0x6c4,
+ coex_dm->pre_val0x6c8, coex_dm->pre_val0x6cc);
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL,
+ "[BTCoex], curVal0x6c0=0x%x, "
+ "curVal0x6c4=0x%x, curVal0x6c8=0x%x, "
+ "curVal0x6cc=0x%x !!\n",
+ coex_dm->cur_val0x6c0, coex_dm->cur_val0x6c4,
+ coex_dm->cur_val0x6c8, coex_dm->cur_val0x6cc);
+
+ if ((coex_dm->pre_val0x6c0 == coex_dm->cur_val0x6c0) &&
+ (coex_dm->pre_val0x6c4 == coex_dm->cur_val0x6c4) &&
+ (coex_dm->pre_val0x6c8 == coex_dm->cur_val0x6c8) &&
+ (coex_dm->pre_val0x6cc == coex_dm->cur_val0x6cc))
+ return;
+ }
+ btc8723b2ant_set_coex_table(btcoexist, val0x6c0, val0x6c4,
+ val0x6c8, val0x6cc);
+
+ coex_dm->pre_val0x6c0 = coex_dm->cur_val0x6c0;
+ coex_dm->pre_val0x6c4 = coex_dm->cur_val0x6c4;
+ coex_dm->pre_val0x6c8 = coex_dm->cur_val0x6c8;
+ coex_dm->pre_val0x6cc = coex_dm->cur_val0x6cc;
+}
+
+static void btc8723b_coex_tbl_type(struct btc_coexist *btcoexist,
+ bool force_exec, u8 type)
+{
+ switch (type) {
+ case 0:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0x55555555,
+ 0x55555555, 0xffff, 0x3);
+ break;
+ case 1:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0x55555555,
+ 0x5afa5afa, 0xffff, 0x3);
+ break;
+ case 2:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0x5a5a5a5a,
+ 0x5a5a5a5a, 0xffff, 0x3);
+ break;
+ case 3:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0xaaaaaaaa,
+ 0xaaaaaaaa, 0xffff, 0x3);
+ break;
+ case 4:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0xffffffff,
+ 0xffffffff, 0xffff, 0x3);
+ break;
+ case 5:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0x5fff5fff,
+ 0x5fff5fff, 0xffff, 0x3);
+ break;
+ case 6:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff,
+ 0x5a5a5a5a, 0xffff, 0x3);
+ break;
+ case 7:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff,
+ 0x5afa5afa, 0xffff, 0x3);
+ break;
+ case 8:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0x5aea5aea,
+ 0x5aea5aea, 0xffff, 0x3);
+ break;
+ case 9:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff,
+ 0x5aea5aea, 0xffff, 0x3);
+ break;
+ case 10:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff,
+ 0x5aff5aff, 0xffff, 0x3);
+ break;
+ case 11:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff,
+ 0x5a5f5a5f, 0xffff, 0x3);
+ break;
+ case 12:
+ btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff,
+ 0x5f5f5f5f, 0xffff, 0x3);
+ break;
+ default:
+ break;
+ }
+}
+
+static void btc8723b2ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoexist,
+ bool enable)
+{
+ u8 h2c_parameter[1] = {0};
+
+ if (enable)
+ h2c_parameter[0] |= BIT0;/* function enable*/
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC,
+ "[BTCoex], set FW for BT Ignore Wlan_Act, "
+ "FW write 0x63=0x%x\n", h2c_parameter[0]);
+
+ btcoexist->btc_fill_h2c(btcoexist, 0x63, 1, h2c_parameter);
+}
+
+static void btc8723b2ant_ignore_wlan_act(struct btc_coexist *btcoexist,
+ bool force_exec, bool enable)
+{
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW,
+ "[BTCoex], %s turn Ignore WlanAct %s\n",
+ (force_exec ? "force to" : ""), (enable ? "ON" : "OFF"));
+ coex_dm->cur_ignore_wlan_act = enable;
+
+ if (!force_exec) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], bPreIgnoreWlanAct = %d, "
+ "bCurIgnoreWlanAct = %d!!\n",
+ coex_dm->pre_ignore_wlan_act,
+ coex_dm->cur_ignore_wlan_act);
+
+ if (coex_dm->pre_ignore_wlan_act ==
+ coex_dm->cur_ignore_wlan_act)
+ return;
+ }
+ btc8723b2ant_set_fw_ignore_wlan_act(btcoexist, enable);
+
+ coex_dm->pre_ignore_wlan_act = coex_dm->cur_ignore_wlan_act;
+}
+
+static void btc8723b2ant_set_fw_ps_tdma(struct btc_coexist *btcoexist, u8 byte1,
+ u8 byte2, u8 byte3, u8 byte4, u8 byte5)
+{
+ u8 h2c_parameter[5];
+
+ h2c_parameter[0] = byte1;
+ h2c_parameter[1] = byte2;
+ h2c_parameter[2] = byte3;
+ h2c_parameter[3] = byte4;
+ h2c_parameter[4] = byte5;
+
+ coex_dm->ps_tdma_para[0] = byte1;
+ coex_dm->ps_tdma_para[1] = byte2;
+ coex_dm->ps_tdma_para[2] = byte3;
+ coex_dm->ps_tdma_para[3] = byte4;
+ coex_dm->ps_tdma_para[4] = byte5;
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC,
+ "[BTCoex], FW write 0x60(5bytes)=0x%x%08x\n",
+ h2c_parameter[0],
+ h2c_parameter[1] << 24 | h2c_parameter[2] << 16 |
+ h2c_parameter[3] << 8 | h2c_parameter[4]);
+
+ btcoexist->btc_fill_h2c(btcoexist, 0x60, 5, h2c_parameter);
+}
+
+static void btc8723b2ant_sw_mechanism1(struct btc_coexist *btcoexist,
+ bool shrink_rx_lpf, bool low_penalty_ra,
+ bool limited_dig, bool bt_lna_constrain)
+{
+ btc8723b2ant_rf_shrink(btcoexist, NORMAL_EXEC, shrink_rx_lpf);
+ btc8723b2ant_low_penalty_ra(btcoexist, NORMAL_EXEC, low_penalty_ra);
+}
+
+static void btc8723b2ant_sw_mechanism2(struct btc_coexist *btcoexist,
+ bool agc_table_shift, bool adc_backoff,
+ bool sw_dac_swing, u32 dac_swing_lvl)
+{
+ btc8723b2ant_agc_table(btcoexist, NORMAL_EXEC, agc_table_shift);
+ btc8723b2ant_dac_swing(btcoexist, NORMAL_EXEC, sw_dac_swing,
+ dac_swing_lvl);
+}
+
+static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec,
+ bool turn_on, u8 type)
+{
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW,
+ "[BTCoex], %s turn %s PS TDMA, type=%d\n",
+ (force_exec ? "force to" : ""),
+ (turn_on ? "ON" : "OFF"), type);
+ coex_dm->cur_ps_tdma_on = turn_on;
+ coex_dm->cur_ps_tdma = type;
+
+ if (!force_exec) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], bPrePsTdmaOn = %d, bCurPsTdmaOn = %d!!\n",
+ coex_dm->pre_ps_tdma_on, coex_dm->cur_ps_tdma_on);
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], prePsTdma = %d, curPsTdma = %d!!\n",
+ coex_dm->pre_ps_tdma, coex_dm->cur_ps_tdma);
+
+ if ((coex_dm->pre_ps_tdma_on == coex_dm->cur_ps_tdma_on) &&
+ (coex_dm->pre_ps_tdma == coex_dm->cur_ps_tdma))
+ return;
+ }
+ if (turn_on) {
+ switch (type) {
+ case 1:
+ default:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a,
+ 0x1a, 0xe1, 0x90);
+ break;
+ case 2:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12,
+ 0x12, 0xe1, 0x90);
+ break;
+ case 3:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c,
+ 0x3, 0xf1, 0x90);
+ break;
+ case 4:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x10,
+ 0x03, 0xf1, 0x90);
+ break;
+ case 5:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a,
+ 0x1a, 0x60, 0x90);
+ break;
+ case 6:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12,
+ 0x12, 0x60, 0x90);
+ break;
+ case 7:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c,
+ 0x3, 0x70, 0x90);
+ break;
+ case 8:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xa3, 0x10,
+ 0x3, 0x70, 0x90);
+ break;
+ case 9:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a,
+ 0x1a, 0xe1, 0x90);
+ break;
+ case 10:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12,
+ 0x12, 0xe1, 0x90);
+ break;
+ case 11:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa,
+ 0xa, 0xe1, 0x90);
+ break;
+ case 12:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5,
+ 0x5, 0xe1, 0x90);
+ break;
+ case 13:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a,
+ 0x1a, 0x60, 0x90);
+ break;
+ case 14:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12,
+ 0x12, 0x60, 0x90);
+ break;
+ case 15:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa,
+ 0xa, 0x60, 0x90);
+ break;
+ case 16:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5,
+ 0x5, 0x60, 0x90);
+ break;
+ case 17:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xa3, 0x2f,
+ 0x2f, 0x60, 0x90);
+ break;
+ case 18:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5,
+ 0x5, 0xe1, 0x90);
+ break;
+ case 19:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25,
+ 0x25, 0xe1, 0x90);
+ break;
+ case 20:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25,
+ 0x25, 0x60, 0x90);
+ break;
+ case 21:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x15,
+ 0x03, 0x70, 0x90);
+ break;
+ case 71:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a,
+ 0x1a, 0xe1, 0x90);
+ break;
+ }
+ } else {
+ /* disable PS tdma */
+ switch (type) {
+ case 0:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0,
+ 0x40, 0x0);
+ break;
+ case 1:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0,
+ 0x48, 0x0);
+ break;
+ default:
+ btc8723b2ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0,
+ 0x40, 0x0);
+ break;
+ }
+ }
+
+ /* update pre state */
+ coex_dm->pre_ps_tdma_on = coex_dm->cur_ps_tdma_on;
+ coex_dm->pre_ps_tdma = coex_dm->cur_ps_tdma;
+}
+
+static void btc8723b2ant_coex_alloff(struct btc_coexist *btcoexist)
+{
+ /* fw all off */
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1);
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6);
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ /* sw all off */
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false, false, 0x18);
+
+ /* hw all off */
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0);
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0);
+}
+
+static void btc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist)
+{
+ /* force to reset coex mechanism*/
+
+ btc8723b2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1);
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6);
+ btc8723b2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, false);
+
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false, false, 0x18);
+}
+
+static void btc8723b2ant_action_bt_inquiry(struct btc_coexist *btcoexist)
+{
+ bool wifi_connected = false;
+ bool low_pwr_disable = true;
+
+ btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER,
+ &low_pwr_disable);
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED,
+ &wifi_connected);
+
+ if (wifi_connected) {
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7);
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3);
+ } else {
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0);
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1);
+ }
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6);
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false, false, 0x18);
+
+ coex_dm->need_recover_0x948 = true;
+ coex_dm->backup_0x948 = btcoexist->btc_read_2byte(btcoexist, 0x948);
+
+ btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280);
+}
+
+static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist)
+{
+ bool common = false, wifi_connected = false;
+ bool wifi_busy = false;
+ bool bt_hs_on = false, low_pwr_disable = false;
+
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on);
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED,
+ &wifi_connected);
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy);
+
+ if (!wifi_connected) {
+ low_pwr_disable = false;
+ btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER,
+ &low_pwr_disable);
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Wifi non-connected idle!!\n");
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff,
+ 0x0);
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0);
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1);
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6);
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false, false,
+ false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false, false,
+ 0x18);
+
+ common = true;
+ } else {
+ if (BT_8723B_2ANT_BT_STATUS_NON_CONNECTED_IDLE ==
+ coex_dm->bt_status) {
+ low_pwr_disable = false;
+ btcoexist->btc_set(btcoexist,
+ BTC_SET_ACT_DISABLE_LOW_POWER,
+ &low_pwr_disable);
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Wifi connected + "
+ "BT non connected-idle!!\n");
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1,
+ 0xfffff, 0x0);
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0);
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1);
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC,
+ 0xb);
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC,
+ false);
+
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+
+ common = true;
+ } else if (BT_8723B_2ANT_BT_STATUS_CONNECTED_IDLE ==
+ coex_dm->bt_status) {
+ low_pwr_disable = true;
+ btcoexist->btc_set(btcoexist,
+ BTC_SET_ACT_DISABLE_LOW_POWER,
+ &low_pwr_disable);
+
+ if (bt_hs_on)
+ return false;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Wifi connected + "
+ "BT connected-idle!!\n");
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1,
+ 0xfffff, 0x0);
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0);
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1);
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC,
+ 0xb);
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC,
+ false);
+
+ btc8723b2ant_sw_mechanism1(btcoexist, true, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+
+ common = true;
+ } else {
+ low_pwr_disable = true;
+ btcoexist->btc_set(btcoexist,
+ BTC_SET_ACT_DISABLE_LOW_POWER,
+ &low_pwr_disable);
+
+ if (wifi_busy) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Wifi Connected-Busy + "
+ "BT Busy!!\n");
+ common = false;
+ } else {
+ if (bt_hs_on)
+ return false;
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Wifi Connected-Idle + "
+ "BT Busy!!\n");
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A,
+ 0x1, 0xfffff, 0x0);
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC,
+ 7);
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 21);
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist,
+ NORMAL_EXEC,
+ 0xb);
+ if (btc8723b_need_dec_pwr(btcoexist))
+ btc8723b2ant_dec_bt_pwr(btcoexist,
+ NORMAL_EXEC,
+ true);
+ else
+ btc8723b2ant_dec_bt_pwr(btcoexist,
+ NORMAL_EXEC,
+ false);
+ btc8723b2ant_sw_mechanism1(btcoexist, false,
+ false, false,
+ false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false,
+ false, false,
+ 0x18);
+ common = true;
+ }
+ }
+ }
+
+ return common;
+}
+
+static void set_tdma_int1(struct btc_coexist *btcoexist, bool tx_pause,
+ s32 result)
+{
+ /* Set PS TDMA for max interval == 1 */
+ if (tx_pause) {
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], TxPause = 1\n");
+
+ if (coex_dm->cur_ps_tdma == 71) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 5);
+ coex_dm->tdma_adj_type = 5;
+ } else if (coex_dm->cur_ps_tdma == 1) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 5);
+ coex_dm->tdma_adj_type = 5;
+ } else if (coex_dm->cur_ps_tdma == 2) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 6);
+ coex_dm->tdma_adj_type = 6;
+ } else if (coex_dm->cur_ps_tdma == 3) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 4) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 8);
+ coex_dm->tdma_adj_type = 8;
+ } else if (coex_dm->cur_ps_tdma == 9) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 13);
+ coex_dm->tdma_adj_type = 13;
+ } else if (coex_dm->cur_ps_tdma == 10) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 14);
+ coex_dm->tdma_adj_type = 14;
+ } else if (coex_dm->cur_ps_tdma == 11) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 12) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 16);
+ coex_dm->tdma_adj_type = 16;
+ }
+
+ if (result == -1) {
+ if (coex_dm->cur_ps_tdma == 5) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 6);
+ coex_dm->tdma_adj_type = 6;
+ } else if (coex_dm->cur_ps_tdma == 6) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 7) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 8);
+ coex_dm->tdma_adj_type = 8;
+ } else if (coex_dm->cur_ps_tdma == 13) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 14);
+ coex_dm->tdma_adj_type = 14;
+ } else if (coex_dm->cur_ps_tdma == 14) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 15) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 16);
+ coex_dm->tdma_adj_type = 16;
+ }
+ } else if (result == 1) {
+ if (coex_dm->cur_ps_tdma == 8) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 7) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 6);
+ coex_dm->tdma_adj_type = 6;
+ } else if (coex_dm->cur_ps_tdma == 6) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 5);
+ coex_dm->tdma_adj_type = 5;
+ } else if (coex_dm->cur_ps_tdma == 16) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 15) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 14);
+ coex_dm->tdma_adj_type = 14;
+ } else if (coex_dm->cur_ps_tdma == 14) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 13);
+ coex_dm->tdma_adj_type = 13;
+ }
+ }
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], TxPause = 0\n");
+ if (coex_dm->cur_ps_tdma == 5) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 71);
+ coex_dm->tdma_adj_type = 71;
+ } else if (coex_dm->cur_ps_tdma == 6) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2);
+ coex_dm->tdma_adj_type = 2;
+ } else if (coex_dm->cur_ps_tdma == 7) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3);
+ coex_dm->tdma_adj_type = 3;
+ } else if (coex_dm->cur_ps_tdma == 8) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 4);
+ coex_dm->tdma_adj_type = 4;
+ } else if (coex_dm->cur_ps_tdma == 13) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9);
+ coex_dm->tdma_adj_type = 9;
+ } else if (coex_dm->cur_ps_tdma == 14) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10);
+ coex_dm->tdma_adj_type = 10;
+ } else if (coex_dm->cur_ps_tdma == 15) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11);
+ coex_dm->tdma_adj_type = 11;
+ } else if (coex_dm->cur_ps_tdma == 16) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 12);
+ coex_dm->tdma_adj_type = 12;
+ }
+
+ if (result == -1) {
+ if (coex_dm->cur_ps_tdma == 71) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 1);
+ coex_dm->tdma_adj_type = 1;
+ } else if (coex_dm->cur_ps_tdma == 1) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 2);
+ coex_dm->tdma_adj_type = 2;
+ } else if (coex_dm->cur_ps_tdma == 2) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 3);
+ coex_dm->tdma_adj_type = 3;
+ } else if (coex_dm->cur_ps_tdma == 3) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 4);
+ coex_dm->tdma_adj_type = 4;
+ } else if (coex_dm->cur_ps_tdma == 9) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 10);
+ coex_dm->tdma_adj_type = 10;
+ } else if (coex_dm->cur_ps_tdma == 10) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 11);
+ coex_dm->tdma_adj_type = 11;
+ } else if (coex_dm->cur_ps_tdma == 11) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 12);
+ coex_dm->tdma_adj_type = 12;
+ }
+ } else if (result == 1) {
+ int tmp = coex_dm->cur_ps_tdma;
+ switch (tmp) {
+ case 4:
+ case 3:
+ case 2:
+ case 12:
+ case 11:
+ case 10:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, tmp - 1);
+ coex_dm->tdma_adj_type = tmp - 1;
+ break;
+ case 1:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 71);
+ coex_dm->tdma_adj_type = 71;
+ break;
+ }
+ }
+ }
+}
+
+static void set_tdma_int2(struct btc_coexist *btcoexist, bool tx_pause,
+ s32 result)
+{
+ /* Set PS TDMA for max interval == 2 */
+ if (tx_pause) {
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], TxPause = 1\n");
+ if (coex_dm->cur_ps_tdma == 1) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 6);
+ coex_dm->tdma_adj_type = 6;
+ } else if (coex_dm->cur_ps_tdma == 2) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 6);
+ coex_dm->tdma_adj_type = 6;
+ } else if (coex_dm->cur_ps_tdma == 3) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 4) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 8);
+ coex_dm->tdma_adj_type = 8;
+ } else if (coex_dm->cur_ps_tdma == 9) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14);
+ coex_dm->tdma_adj_type = 14;
+ } else if (coex_dm->cur_ps_tdma == 10) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14);
+ coex_dm->tdma_adj_type = 14;
+ } else if (coex_dm->cur_ps_tdma == 11) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 12) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 16);
+ coex_dm->tdma_adj_type = 16;
+ }
+ if (result == -1) {
+ if (coex_dm->cur_ps_tdma == 5) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 6);
+ coex_dm->tdma_adj_type = 6;
+ } else if (coex_dm->cur_ps_tdma == 6) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 7) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 8);
+ coex_dm->tdma_adj_type = 8;
+ } else if (coex_dm->cur_ps_tdma == 13) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 14);
+ coex_dm->tdma_adj_type = 14;
+ } else if (coex_dm->cur_ps_tdma == 14) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 15) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 16);
+ coex_dm->tdma_adj_type = 16;
+ }
+ } else if (result == 1) {
+ if (coex_dm->cur_ps_tdma == 8) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 7) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 6);
+ coex_dm->tdma_adj_type = 6;
+ } else if (coex_dm->cur_ps_tdma == 6) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 6);
+ coex_dm->tdma_adj_type = 6;
+ } else if (coex_dm->cur_ps_tdma == 16) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 15) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 14);
+ coex_dm->tdma_adj_type = 14;
+ } else if (coex_dm->cur_ps_tdma == 14) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 14);
+ coex_dm->tdma_adj_type = 14;
+ }
+ }
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], TxPause = 0\n");
+ if (coex_dm->cur_ps_tdma == 5) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2);
+ coex_dm->tdma_adj_type = 2;
+ } else if (coex_dm->cur_ps_tdma == 6) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2);
+ coex_dm->tdma_adj_type = 2;
+ } else if (coex_dm->cur_ps_tdma == 7) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3);
+ coex_dm->tdma_adj_type = 3;
+ } else if (coex_dm->cur_ps_tdma == 8) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 4);
+ coex_dm->tdma_adj_type = 4;
+ } else if (coex_dm->cur_ps_tdma == 13) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10);
+ coex_dm->tdma_adj_type = 10;
+ } else if (coex_dm->cur_ps_tdma == 14) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10);
+ coex_dm->tdma_adj_type = 10;
+ } else if (coex_dm->cur_ps_tdma == 15) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11);
+ coex_dm->tdma_adj_type = 11;
+ } else if (coex_dm->cur_ps_tdma == 16) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 12);
+ coex_dm->tdma_adj_type = 12;
+ }
+ if (result == -1) {
+ if (coex_dm->cur_ps_tdma == 1) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 2);
+ coex_dm->tdma_adj_type = 2;
+ } else if (coex_dm->cur_ps_tdma == 2) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 3);
+ coex_dm->tdma_adj_type = 3;
+ } else if (coex_dm->cur_ps_tdma == 3) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 4);
+ coex_dm->tdma_adj_type = 4;
+ } else if (coex_dm->cur_ps_tdma == 9) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 10);
+ coex_dm->tdma_adj_type = 10;
+ } else if (coex_dm->cur_ps_tdma == 10) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 11);
+ coex_dm->tdma_adj_type = 11;
+ } else if (coex_dm->cur_ps_tdma == 11) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 12);
+ coex_dm->tdma_adj_type = 12;
+ }
+ } else if (result == 1) {
+ if (coex_dm->cur_ps_tdma == 4) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 3);
+ coex_dm->tdma_adj_type = 3;
+ } else if (coex_dm->cur_ps_tdma == 3) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 2);
+ coex_dm->tdma_adj_type = 2;
+ } else if (coex_dm->cur_ps_tdma == 2) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 2);
+ coex_dm->tdma_adj_type = 2;
+ } else if (coex_dm->cur_ps_tdma == 12) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 11);
+ coex_dm->tdma_adj_type = 11;
+ } else if (coex_dm->cur_ps_tdma == 11) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 10);
+ coex_dm->tdma_adj_type = 10;
+ } else if (coex_dm->cur_ps_tdma == 10) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 10);
+ coex_dm->tdma_adj_type = 10;
+ }
+ }
+ }
+}
+
+static void set_tdma_int3(struct btc_coexist *btcoexist, bool tx_pause,
+ s32 result)
+{
+ /* Set PS TDMA for max interval == 3 */
+ if (tx_pause) {
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], TxPause = 1\n");
+ if (coex_dm->cur_ps_tdma == 1) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 2) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 3) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 4) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 8);
+ coex_dm->tdma_adj_type = 8;
+ } else if (coex_dm->cur_ps_tdma == 9) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 10) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 11) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 12) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 16);
+ coex_dm->tdma_adj_type = 16;
+ }
+ if (result == -1) {
+ if (coex_dm->cur_ps_tdma == 5) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 6) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 7) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 8);
+ coex_dm->tdma_adj_type = 8;
+ } else if (coex_dm->cur_ps_tdma == 13) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 14) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 15) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 16);
+ coex_dm->tdma_adj_type = 16;
+ }
+ } else if (result == 1) {
+ if (coex_dm->cur_ps_tdma == 8) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 7) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 6) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else if (coex_dm->cur_ps_tdma == 16) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 15) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else if (coex_dm->cur_ps_tdma == 14) {
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ }
+ }
+ } else {
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], TxPause = 0\n");
+ switch (coex_dm->cur_ps_tdma) {
+ case 5:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3);
+ coex_dm->tdma_adj_type = 3;
+ break;
+ case 6:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3);
+ coex_dm->tdma_adj_type = 3;
+ break;
+ case 7:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3);
+ coex_dm->tdma_adj_type = 3;
+ break;
+ case 8:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 4);
+ coex_dm->tdma_adj_type = 4;
+ break;
+ case 13:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11);
+ coex_dm->tdma_adj_type = 11;
+ break;
+ case 14:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11);
+ coex_dm->tdma_adj_type = 11;
+ break;
+ case 15:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11);
+ coex_dm->tdma_adj_type = 11;
+ break;
+ case 16:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 12);
+ coex_dm->tdma_adj_type = 12;
+ break;
+ }
+ if (result == -1) {
+ switch (coex_dm->cur_ps_tdma) {
+ case 1:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 3);
+ coex_dm->tdma_adj_type = 3;
+ break;
+ case 2:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 3);
+ coex_dm->tdma_adj_type = 3;
+ break;
+ case 3:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 4);
+ coex_dm->tdma_adj_type = 4;
+ break;
+ case 9:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 11);
+ coex_dm->tdma_adj_type = 11;
+ break;
+ case 10:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 11);
+ coex_dm->tdma_adj_type = 11;
+ break;
+ case 11:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 12);
+ coex_dm->tdma_adj_type = 12;
+ break;
+ }
+ } else if (result == 1) {
+ switch (coex_dm->cur_ps_tdma) {
+ case 4:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 3);
+ coex_dm->tdma_adj_type = 3;
+ break;
+ case 3:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 3);
+ coex_dm->tdma_adj_type = 3;
+ break;
+ case 2:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 3);
+ coex_dm->tdma_adj_type = 3;
+ break;
+ case 12:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 11);
+ coex_dm->tdma_adj_type = 11;
+ break;
+ case 11:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 11);
+ coex_dm->tdma_adj_type = 11;
+ break;
+ case 10:
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC,
+ true, 11);
+ coex_dm->tdma_adj_type = 11;
+ }
+ }
+ }
+}
+
+static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist,
+ bool sco_hid, bool tx_pause,
+ u8 max_interval)
+{
+ static s32 up, dn, m, n, wait_count;
+ /*0: no change, +1: increase WiFi duration, -1: decrease WiFi duration*/
+ s32 result;
+ u8 retry_count = 0;
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW,
+ "[BTCoex], TdmaDurationAdjust()\n");
+
+ if (!coex_dm->auto_tdma_adjust) {
+ coex_dm->auto_tdma_adjust = true;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], first run TdmaDurationAdjust()!!\n");
+ if (sco_hid) {
+ if (tx_pause) {
+ if (max_interval == 1) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 13);
+ coex_dm->tdma_adj_type = 13;
+ } else if (max_interval == 2) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 14);
+ coex_dm->tdma_adj_type = 14;
+ } else if (max_interval == 3) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ } else {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 15);
+ coex_dm->tdma_adj_type = 15;
+ }
+ } else {
+ if (max_interval == 1) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 9);
+ coex_dm->tdma_adj_type = 9;
+ } else if (max_interval == 2) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 10);
+ coex_dm->tdma_adj_type = 10;
+ } else if (max_interval == 3) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 11);
+ coex_dm->tdma_adj_type = 11;
+ } else {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 11);
+ coex_dm->tdma_adj_type = 11;
+ }
+ }
+ } else {
+ if (tx_pause) {
+ if (max_interval == 1) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 5);
+ coex_dm->tdma_adj_type = 5;
+ } else if (max_interval == 2) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 6);
+ coex_dm->tdma_adj_type = 6;
+ } else if (max_interval == 3) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ } else {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 7);
+ coex_dm->tdma_adj_type = 7;
+ }
+ } else {
+ if (max_interval == 1) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 1);
+ coex_dm->tdma_adj_type = 1;
+ } else if (max_interval == 2) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 2);
+ coex_dm->tdma_adj_type = 2;
+ } else if (max_interval == 3) {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 3);
+ coex_dm->tdma_adj_type = 3;
+ } else {
+ btc8723b2ant_ps_tdma(btcoexist,
+ NORMAL_EXEC,
+ true, 3);
+ coex_dm->tdma_adj_type = 3;
+ }
+ }
+ }
+
+ up = 0;
+ dn = 0;
+ m = 1;
+ n = 3;
+ result = 0;
+ wait_count = 0;
+ } else {
+ /*accquire the BT TRx retry count from BT_Info byte2*/
+ retry_count = coex_sta->bt_retry_cnt;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], retry_count = %d\n", retry_count);
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], up=%d, dn=%d, m=%d, n=%d, wait_count=%d\n",
+ up, dn, m, n, wait_count);
+ result = 0;
+ wait_count++;
+ /* no retry in the last 2-second duration*/
+ if (retry_count == 0) {
+ up++;
+ dn--;
+
+ if (dn <= 0)
+ dn = 0;
+
+ if (up >= n) {
+ wait_count = 0;
+ n = 3;
+ up = 0;
+ dn = 0;
+ result = 1;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], Increase wifi "
+ "duration!!\n");
+ } /* <=3 retry in the last 2-second duration*/
+ } else if (retry_count <= 3) {
+ up--;
+ dn++;
+
+ if (up <= 0)
+ up = 0;
+
+ if (dn == 2) {
+ if (wait_count <= 2)
+ m++;
+ else
+ m = 1;
+
+ if (m >= 20)
+ m = 20;
+
+ n = 3 * m;
+ up = 0;
+ dn = 0;
+ wait_count = 0;
+ result = -1;
+ BTC_PRINT(BTC_MSG_ALGORITHM,
+ ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], Decrease wifi duration "
+ "for retry_counter<3!!\n");
+ }
+ } else {
+ if (wait_count == 1)
+ m++;
+ else
+ m = 1;
+
+ if (m >= 20)
+ m = 20;
+
+ n = 3 * m;
+ up = 0;
+ dn = 0;
+ wait_count = 0;
+ result = -1;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], Decrease wifi duration "
+ "for retry_counter>3!!\n");
+ }
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], max Interval = %d\n", max_interval);
+ if (max_interval == 1)
+ set_tdma_int1(btcoexist, tx_pause, result);
+ else if (max_interval == 2)
+ set_tdma_int2(btcoexist, tx_pause, result);
+ else if (max_interval == 3)
+ set_tdma_int3(btcoexist, tx_pause, result);
+ }
+
+ /*if current PsTdma not match with the recorded one (when scan, dhcp..),
+ *then we have to adjust it back to the previous recorded one.
+ */
+ if (coex_dm->cur_ps_tdma != coex_dm->tdma_adj_type) {
+ bool scan = false, link = false, roam = false;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], PsTdma type dismatch!!!, "
+ "curPsTdma=%d, recordPsTdma=%d\n",
+ coex_dm->cur_ps_tdma, coex_dm->tdma_adj_type);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan);
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link);
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam);
+
+ if (!scan && !link && !roam)
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true,
+ coex_dm->tdma_adj_type);
+ else
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL,
+ "[BTCoex], roaming/link/scan is under"
+ " progress, will adjust next time!!!\n");
+ }
+}
+
+/* SCO only or SCO+PAN(HS) */
+static void btc8723b2ant_action_sco(struct btc_coexist *btcoexist)
+{
+ u8 wifi_rssi_state;
+ u32 wifi_bw;
+
+ wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist,
+ 0, 2, 15, 0);
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0);
+
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 4);
+
+ if (btc8723b_need_dec_pwr(btcoexist))
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true);
+ else
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw);
+
+ /*for SCO quality at 11b/g mode*/
+ if (BTC_WIFI_BW_LEGACY == wifi_bw)
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 2);
+ else /*for SCO quality & wifi performance balance at 11n mode*/
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 8);
+
+ /*for voice quality */
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0);
+
+ /* sw mechanism */
+ if (BTC_WIFI_BW_HT40 == wifi_bw) {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ true, 0x4);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ true, 0x4);
+ }
+ } else {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ true, 0x4);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ true, 0x4);
+ }
+ }
+}
+
+static void btc8723b2ant_action_hid(struct btc_coexist *btcoexist)
+{
+ u8 wifi_rssi_state, bt_rssi_state;
+ u32 wifi_bw;
+
+ wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist,
+ 0, 2, 15, 0);
+ bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0);
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0);
+
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6);
+
+ if (btc8723b_need_dec_pwr(btcoexist))
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true);
+ else
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw);
+
+ if (BTC_WIFI_BW_LEGACY == wifi_bw) /*/for HID at 11b/g mode*/
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7);
+ else /*for HID quality & wifi performance balance at 11n mode*/
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 9);
+
+ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH))
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9);
+ else
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 13);
+
+ /* sw mechanism */
+ if (BTC_WIFI_BW_HT40 == wifi_bw) {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ } else {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ }
+}
+
+/*A2DP only / PAN(EDR) only/ A2DP+PAN(HS)*/
+static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist)
+{
+ u8 wifi_rssi_state, bt_rssi_state;
+ u32 wifi_bw;
+
+ wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist,
+ 0, 2, 15, 0);
+ bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0);
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0);
+
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6);
+
+ if (btc8723b_need_dec_pwr(btcoexist))
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true);
+ else
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7);
+
+ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH))
+ btc8723b2ant_tdma_duration_adjust(btcoexist, false,
+ false, 1);
+ else
+ btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 1);
+
+ /* sw mechanism */
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw);
+ if (BTC_WIFI_BW_HT40 == wifi_bw) {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ } else {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ }
+}
+
+static void btc8723b2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist)
+{
+ u8 wifi_rssi_state;
+ u32 wifi_bw;
+
+ wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist,
+ 0, 2, 15, 0);
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0);
+
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6);
+
+ if (btc8723b_need_dec_pwr(btcoexist))
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true);
+ else
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7);
+
+ btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 2);
+
+ /* sw mechanism */
+ btcoexist->btc_get(btcoexist,
+ BTC_GET_U4_WIFI_BW, &wifi_bw);
+ if (BTC_WIFI_BW_HT40 == wifi_bw) {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ } else {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ }
+}
+
+static void btc8723b2ant_action_pan_edr(struct btc_coexist *btcoexist)
+{
+ u8 wifi_rssi_state, bt_rssi_state;
+ u32 wifi_bw;
+
+ wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist,
+ 0, 2, 15, 0);
+ bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0);
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0);
+
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6);
+
+ if (btc8723b_need_dec_pwr(btcoexist))
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true);
+ else
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 10);
+
+ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH))
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 1);
+ else
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5);
+
+ /* sw mechanism */
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw);
+ if (BTC_WIFI_BW_HT40 == wifi_bw) {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ } else {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ }
+}
+
+/*PAN(HS) only*/
+static void btc8723b2ant_action_pan_hs(struct btc_coexist *btcoexist)
+{
+ u8 wifi_rssi_state;
+ u32 wifi_bw;
+
+ wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist,
+ 0, 2, 15, 0);
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0);
+
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6);
+
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH))
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true);
+ else
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7);
+
+ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw);
+ if (BTC_WIFI_BW_HT40 == wifi_bw) {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ } else {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ }
+}
+
+/*PAN(EDR)+A2DP*/
+static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist)
+{
+ u8 wifi_rssi_state, bt_rssi_state;
+ u32 wifi_bw;
+
+ wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist,
+ 0, 2, 15, 0);
+ bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0);
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0);
+
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6);
+
+ if (btc8723b_need_dec_pwr(btcoexist))
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true);
+ else
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw);
+
+ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 12);
+ if (BTC_WIFI_BW_HT40 == wifi_bw)
+ btc8723b2ant_tdma_duration_adjust(btcoexist, false,
+ true, 3);
+ else
+ btc8723b2ant_tdma_duration_adjust(btcoexist, false,
+ false, 3);
+ } else {
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7);
+ btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 3);
+ }
+
+ /* sw mechanism */
+ if (BTC_WIFI_BW_HT40 == wifi_bw) {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ } else {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, false,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ }
+}
+
+static void btc8723b2ant_action_pan_edr_hid(struct btc_coexist *btcoexist)
+{
+ u8 wifi_rssi_state, bt_rssi_state;
+ u32 wifi_bw;
+
+ wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist,
+ 0, 2, 15, 0);
+ bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0);
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw);
+
+ if (btc8723b_need_dec_pwr(btcoexist))
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true);
+ else
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ if (BTC_WIFI_BW_HT40 == wifi_bw) {
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC,
+ 3);
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 11);
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1,
+ 0xfffff, 0x780);
+ } else {
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC,
+ 6);
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7);
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1,
+ 0xfffff, 0x0);
+ }
+ btc8723b2ant_tdma_duration_adjust(btcoexist, true, false, 2);
+ } else {
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6);
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 11);
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff,
+ 0x0);
+ btc8723b2ant_tdma_duration_adjust(btcoexist, true, true, 2);
+ }
+
+ /* sw mechanism */
+ if (BTC_WIFI_BW_HT40 == wifi_bw) {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ } else {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ }
+}
+
+/* HID+A2DP+PAN(EDR) */
+static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist)
+{
+ u8 wifi_rssi_state, bt_rssi_state;
+ u32 wifi_bw;
+
+ wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist,
+ 0, 2, 15, 0);
+ bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0);
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0);
+
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6);
+
+ if (btc8723b_need_dec_pwr(btcoexist))
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true);
+ else
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw);
+
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7);
+
+ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ if (BTC_WIFI_BW_HT40 == wifi_bw)
+ btc8723b2ant_tdma_duration_adjust(btcoexist, true,
+ true, 2);
+ else
+ btc8723b2ant_tdma_duration_adjust(btcoexist, true,
+ false, 3);
+ } else {
+ btc8723b2ant_tdma_duration_adjust(btcoexist, true, true, 3);
+ }
+
+ /* sw mechanism */
+ if (BTC_WIFI_BW_HT40 == wifi_bw) {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ } else {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ }
+}
+
+static void btc8723b2ant_action_hid_a2dp(struct btc_coexist *btcoexist)
+{
+ u8 wifi_rssi_state, bt_rssi_state;
+ u32 wifi_bw;
+
+ wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist,
+ 0, 2, 15, 0);
+ bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0);
+
+ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0);
+
+ btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6);
+
+ if (btc8723b_need_dec_pwr(btcoexist))
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true);
+ else
+ btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw);
+
+ btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7);
+
+ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH))
+ btc8723b2ant_tdma_duration_adjust(btcoexist, true, false, 2);
+ else
+ btc8723b2ant_tdma_duration_adjust(btcoexist, true, true, 2);
+
+ /* sw mechanism */
+ if (BTC_WIFI_BW_HT40 == wifi_bw) {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, true, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ } else {
+ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
+ (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, true, false,
+ false, 0x18);
+ } else {
+ btc8723b2ant_sw_mechanism1(btcoexist, false, true,
+ false, false);
+ btc8723b2ant_sw_mechanism2(btcoexist, false, false,
+ false, 0x18);
+ }
+ }
+}
+
+static void btc8723b2ant_run_coexist_mechanism(struct btc_coexist *btcoexist)
+{
+ u8 algorithm = 0;
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], RunCoexistMechanism()===>\n");
+
+ if (btcoexist->manual_control) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], RunCoexistMechanism(), "
+ "return for Manual CTRL <===\n");
+ return;
+ }
+
+ if (coex_sta->under_ips) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], wifi is under IPS !!!\n");
+ return;
+ }
+
+ algorithm = btc8723b2ant_action_algorithm(btcoexist);
+ if (coex_sta->c2h_bt_inquiry_page &&
+ (BT_8723B_2ANT_COEX_ALGO_PANHS != algorithm)) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], BT is under inquiry/page scan !!\n");
+ btc8723b2ant_action_bt_inquiry(btcoexist);
+ return;
+ } else {
+ if (coex_dm->need_recover_0x948) {
+ coex_dm->need_recover_0x948 = false;
+ btcoexist->btc_write_2byte(btcoexist, 0x948,
+ coex_dm->backup_0x948);
+ }
+ }
+
+ coex_dm->cur_algorithm = algorithm;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, "[BTCoex], Algorithm = %d\n",
+ coex_dm->cur_algorithm);
+
+ if (btc8723b2ant_is_common_action(btcoexist)) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant common.\n");
+ coex_dm->auto_tdma_adjust = false;
+ } else {
+ if (coex_dm->cur_algorithm != coex_dm->pre_algorithm) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], preAlgorithm=%d, "
+ "curAlgorithm=%d\n", coex_dm->pre_algorithm,
+ coex_dm->cur_algorithm);
+ coex_dm->auto_tdma_adjust = false;
+ }
+ switch (coex_dm->cur_algorithm) {
+ case BT_8723B_2ANT_COEX_ALGO_SCO:
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant, algorithm = SCO.\n");
+ btc8723b2ant_action_sco(btcoexist);
+ break;
+ case BT_8723B_2ANT_COEX_ALGO_HID:
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant, algorithm = HID.\n");
+ btc8723b2ant_action_hid(btcoexist);
+ break;
+ case BT_8723B_2ANT_COEX_ALGO_A2DP:
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant, "
+ "algorithm = A2DP.\n");
+ btc8723b2ant_action_a2dp(btcoexist);
+ break;
+ case BT_8723B_2ANT_COEX_ALGO_A2DP_PANHS:
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant, "
+ "algorithm = A2DP+PAN(HS).\n");
+ btc8723b2ant_action_a2dp_pan_hs(btcoexist);
+ break;
+ case BT_8723B_2ANT_COEX_ALGO_PANEDR:
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant, "
+ "algorithm = PAN(EDR).\n");
+ btc8723b2ant_action_pan_edr(btcoexist);
+ break;
+ case BT_8723B_2ANT_COEX_ALGO_PANHS:
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant, "
+ "algorithm = HS mode.\n");
+ btc8723b2ant_action_pan_hs(btcoexist);
+ break;
+ case BT_8723B_2ANT_COEX_ALGO_PANEDR_A2DP:
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant, "
+ "algorithm = PAN+A2DP.\n");
+ btc8723b2ant_action_pan_edr_a2dp(btcoexist);
+ break;
+ case BT_8723B_2ANT_COEX_ALGO_PANEDR_HID:
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant, "
+ "algorithm = PAN(EDR)+HID.\n");
+ btc8723b2ant_action_pan_edr_hid(btcoexist);
+ break;
+ case BT_8723B_2ANT_COEX_ALGO_HID_A2DP_PANEDR:
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant, "
+ "algorithm = HID+A2DP+PAN.\n");
+ btc8723b2ant_action_hid_a2dp_pan_edr(btcoexist);
+ break;
+ case BT_8723B_2ANT_COEX_ALGO_HID_A2DP:
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant, "
+ "algorithm = HID+A2DP.\n");
+ btc8723b2ant_action_hid_a2dp(btcoexist);
+ break;
+ default:
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], Action 2-Ant, "
+ "algorithm = coexist All Off!!\n");
+ btc8723b2ant_coex_alloff(btcoexist);
+ break;
+ }
+ coex_dm->pre_algorithm = coex_dm->cur_algorithm;
+ }
+}
+
+
+
+/*********************************************************************
+ * work around function start with wa_btc8723b2ant_
+ *********************************************************************/
+/*********************************************************************
+ * extern function start with EXbtc8723b2ant_
+ *********************************************************************/
+void ex_halbtc8723b2ant_init_hwconfig(struct btc_coexist *btcoexist)
+{
+ struct btc_board_info *board_info = &btcoexist->board_info;
+ u32 u32tmp = 0, fw_ver;
+ u8 u8tmp = 0;
+ u8 h2c_parameter[2] = {0};
+
+
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT,
+ "[BTCoex], 2Ant Init HW Config!!\n");
+
+ /* backup rf 0x1e value */
+ coex_dm->bt_rf0x1e_backup = btcoexist->btc_get_rf_reg(btcoexist,
+ BTC_RF_A, 0x1e,
+ 0xfffff);
+
+ /* 0x4c[23]=0, 0x4c[24]=1 Antenna control by WL/BT */
+ u32tmp = btcoexist->btc_read_4byte(btcoexist, 0x4c);
+ u32tmp &= ~BIT23;
+ u32tmp |= BIT24;
+ btcoexist->btc_write_4byte(btcoexist, 0x4c, u32tmp);
+
+ btcoexist->btc_write_1byte(btcoexist, 0x974, 0xff);
+ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x944, 0x3, 0x3);
+ btcoexist->btc_write_1byte(btcoexist, 0x930, 0x77);
+ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x67, 0x20, 0x1);
+
+ /* Antenna switch control parameter */
+ /* btcoexist->btc_write_4byte(btcoexist, 0x858, 0x55555555);*/
+
+ /*Force GNT_BT to low*/
+ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x765, 0x18, 0x0);
+ btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0);
+
+ /* 0x790[5:0]=0x5 */
+ u8tmp = btcoexist->btc_read_1byte(btcoexist, 0x790);
+ u8tmp &= 0xc0;
+ u8tmp |= 0x5;
+ btcoexist->btc_write_1byte(btcoexist, 0x790, u8tmp);
+
+
+ /*Antenna config */
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_FW_VER, &fw_ver);
+
+ /*ext switch for fw ver < 0xc */
+ if (fw_ver < 0xc00) {
+ if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) {
+ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x92c,
+ 0x3, 0x1);
+ /*Main Ant to BT for IPS case 0x4c[23]=1*/
+ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1,
+ 0x1);
+
+ /*tell firmware "no antenna inverse"*/
+ h2c_parameter[0] = 0;
+ h2c_parameter[1] = 1; /* ext switch type */
+ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2,
+ h2c_parameter);
+ } else {
+ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x92c,
+ 0x3, 0x2);
+ /*Aux Ant to BT for IPS case 0x4c[23]=1*/
+ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1,
+ 0x0);
+
+ /*tell firmware "antenna inverse"*/
+ h2c_parameter[0] = 1;
+ h2c_parameter[1] = 1; /*ext switch type*/
+ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2,
+ h2c_parameter);
+ }
+ } else {
+ /*ext switch always at s1 (if exist) */
+ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x92c, 0x3, 0x1);
+ /*Main Ant to BT for IPS case 0x4c[23]=1*/
+ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1, 0x1);
+
+ if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) {
+ /*tell firmware "no antenna inverse"*/
+ h2c_parameter[0] = 0;
+ h2c_parameter[1] = 0; /*ext switch type*/
+ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2,
+ h2c_parameter);
+ } else {
+ /*tell firmware "antenna inverse"*/
+ h2c_parameter[0] = 1;
+ h2c_parameter[1] = 0; /*ext switch type*/
+ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2,
+ h2c_parameter);
+ }
+ }
+
+ /* PTA parameter */
+ btc8723b_coex_tbl_type(btcoexist, FORCE_EXEC, 0);
+
+ /* Enable counter statistics */
+ /*0x76e[3] =1, WLAN_Act control by PTA*/
+ btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc);
+ btcoexist->btc_write_1byte(btcoexist, 0x778, 0x3);
+ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x40, 0x20, 0x1);
+}
+
+void ex_halbtc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist)
+{
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT,
+ "[BTCoex], Coex Mechanism Init!!\n");
+ btc8723b2ant_init_coex_dm(btcoexist);
+}
+
+void ex_halbtc8723b2ant_display_coex_info(struct btc_coexist *btcoexist)
+{
+ struct btc_board_info *board_info = &btcoexist->board_info;
+ struct btc_stack_info *stack_info = &btcoexist->stack_info;
+ struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info;
+ u8 *cli_buf = btcoexist->cli_buf;
+ u8 u8tmp[4], i, bt_info_ext, ps_tdma_case = 0;
+ u32 u32tmp[4];
+ bool roam = false, scan = false;
+ bool link = false, wifi_under_5g = false;
+ bool bt_hs_on = false, wifi_busy = false;
+ s32 wifi_rssi = 0, bt_hs_rssi = 0;
+ u32 wifi_bw, wifi_traffic_dir, fa_ofdm, fa_cck;
+ u8 wifi_dot11_chnl, wifi_hs_chnl;
+ u32 fw_ver = 0, bt_patch_ver = 0;
+
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE,
+ "\r\n ============[BT Coexist info]============");
+ CL_PRINTF(cli_buf);
+
+ if (btcoexist->manual_control) {
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE,
+ "\r\n ==========[Under Manual Control]============");
+ CL_PRINTF(cli_buf);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE,
+ "\r\n ==========================================");
+ CL_PRINTF(cli_buf);
+ }
+
+ if (!board_info->bt_exist) {
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n BT not exists !!!");
+ CL_PRINTF(cli_buf);
+ return;
+ }
+
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d ",
+ "Ant PG number/ Ant mechanism:",
+ board_info->pg_ant_num, board_info->btdm_ant_num);
+ CL_PRINTF(cli_buf);
+
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s / %d",
+ "BT stack/ hci ext ver",
+ ((stack_info->profile_notified) ? "Yes" : "No"),
+ stack_info->hci_version);
+ CL_PRINTF(cli_buf);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_BT_PATCH_VER, &bt_patch_ver);
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_FW_VER, &fw_ver);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE,
+ "\r\n %-35s = %d_%x/ 0x%x/ 0x%x(%d)",
+ "CoexVer/ FwVer/ PatchVer",
+ glcoex_ver_date_8723b_2ant, glcoex_ver_8723b_2ant,
+ fw_ver, bt_patch_ver, bt_patch_ver);
+ CL_PRINTF(cli_buf);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on);
+ btcoexist->btc_get(btcoexist, BTC_GET_U1_WIFI_DOT11_CHNL,
+ &wifi_dot11_chnl);
+ btcoexist->btc_get(btcoexist, BTC_GET_U1_WIFI_HS_CHNL, &wifi_hs_chnl);
+
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d / %d(%d)",
+ "Dot11 channel / HsChnl(HsMode)",
+ wifi_dot11_chnl, wifi_hs_chnl, bt_hs_on);
+ CL_PRINTF(cli_buf);
+
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\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]);
+ CL_PRINTF(cli_buf);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi);
+ btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d",
+ "Wifi rssi/ HS rssi", wifi_rssi, bt_hs_rssi);
+ CL_PRINTF(cli_buf);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan);
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link);
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d/ %d ",
+ "Wifi link/ roam/ scan", link, roam, scan);
+ CL_PRINTF(cli_buf);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g);
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw);
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy);
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_TRAFFIC_DIRECTION,
+ &wifi_traffic_dir);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s / %s/ %s ",
+ "Wifi status", (wifi_under_5g ? "5G" : "2.4G"),
+ ((BTC_WIFI_BW_LEGACY == wifi_bw) ? "Legacy" :
+ (((BTC_WIFI_BW_HT40 == wifi_bw) ? "HT40" : "HT20"))),
+ ((!wifi_busy) ? "idle" :
+ ((BTC_WIFI_TRAFFIC_TX == wifi_traffic_dir) ?
+ "uplink" : "downlink")));
+ CL_PRINTF(cli_buf);
+
+ CL_PRINTF(cli_buf);
+
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d / %d / %d / %d",
+ "SCO/HID/PAN/A2DP",
+ bt_link_info->sco_exist, bt_link_info->hid_exist,
+ bt_link_info->pan_exist, bt_link_info->a2dp_exist);
+ CL_PRINTF(cli_buf);
+ btcoexist->btc_disp_dbg_msg(btcoexist, BTC_DBG_DISP_BT_LINK_INFO);
+
+ bt_info_ext = coex_sta->bt_info_ext;
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s",
+ "BT Info A2DP rate",
+ (bt_info_ext&BIT0) ? "Basic rate" : "EDR rate");
+ CL_PRINTF(cli_buf);
+
+ for (i = 0; i < BT_INFO_SRC_8723B_2ANT_MAX; i++) {
+ if (coex_sta->bt_info_c2h_cnt[i]) {
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE,
+ "\r\n %-35s = %02x %02x %02x "
+ "%02x %02x %02x %02x(%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_cnt[i]);
+ CL_PRINTF(cli_buf);
+ }
+ }
+
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s/%s",
+ "PS state, IPS/LPS",
+ ((coex_sta->under_ips ? "IPS ON" : "IPS OFF")),
+ ((coex_sta->under_lps ? "LPS ON" : "LPS OFF")));
+ CL_PRINTF(cli_buf);
+ btcoexist->btc_disp_dbg_msg(btcoexist, BTC_DBG_DISP_FW_PWR_MODE_CMD);
+
+ /* Sw mechanism */
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE,
+ "\r\n %-35s", "============[Sw mechanism]============");
+ CL_PRINTF(cli_buf);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d/ %d ",
+ "SM1[ShRf/ LpRA/ LimDig]", coex_dm->cur_rf_rx_lpf_shrink,
+ coex_dm->cur_low_penalty_ra, coex_dm->limited_dig);
+ CL_PRINTF(cli_buf);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d/ %d(0x%x) ",
+ "SM2[AgcT/ AdcB/ SwDacSwing(lvl)]",
+ coex_dm->cur_agc_table_en, coex_dm->cur_adc_back_off,
+ coex_dm->cur_dac_swing_on, coex_dm->cur_dac_swing_lvl);
+ CL_PRINTF(cli_buf);
+
+ /* Fw mechanism */
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s",
+ "============[Fw mechanism]============");
+ CL_PRINTF(cli_buf);
+
+ ps_tdma_case = coex_dm->cur_ps_tdma;
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE,
+ "\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],
+ ps_tdma_case, coex_dm->auto_tdma_adjust);
+ CL_PRINTF(cli_buf);
+
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d ",
+ "DecBtPwr/ IgnWlanAct", coex_dm->cur_dec_bt_pwr,
+ coex_dm->cur_ignore_wlan_act);
+ CL_PRINTF(cli_buf);
+
+ /* Hw setting */
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s",
+ "============[Hw setting]============");
+ CL_PRINTF(cli_buf);
+
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x",
+ "RF-A, 0x1e initVal", coex_dm->bt_rf0x1e_backup);
+ CL_PRINTF(cli_buf);
+
+ u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x778);
+ u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x880);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x",
+ "0x778/0x880[29:25]", u8tmp[0],
+ (u32tmp[0]&0x3e000000) >> 25);
+ CL_PRINTF(cli_buf);
+
+ u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x948);
+ u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x67);
+ u8tmp[1] = btcoexist->btc_read_1byte(btcoexist, 0x765);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x/ 0x%x",
+ "0x948/ 0x67[5] / 0x765",
+ u32tmp[0], ((u8tmp[0]&0x20) >> 5), u8tmp[1]);
+ CL_PRINTF(cli_buf);
+
+ u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x92c);
+ u32tmp[1] = btcoexist->btc_read_4byte(btcoexist, 0x930);
+ u32tmp[2] = btcoexist->btc_read_4byte(btcoexist, 0x944);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x/ 0x%x",
+ "0x92c[1:0]/ 0x930[7:0]/0x944[1:0]",
+ u32tmp[0]&0x3, u32tmp[1]&0xff, u32tmp[2]&0x3);
+ CL_PRINTF(cli_buf);
+
+
+ u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x39);
+ u8tmp[1] = btcoexist->btc_read_1byte(btcoexist, 0x40);
+ u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x4c);
+ u8tmp[2] = btcoexist->btc_read_1byte(btcoexist, 0x64);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE,
+ "\r\n %-35s = 0x%x/ 0x%x/ 0x%x/ 0x%x",
+ "0x38[11]/0x40/0x4c[24:23]/0x64[0]",
+ ((u8tmp[0] & 0x8)>>3), u8tmp[1],
+ ((u32tmp[0]&0x01800000)>>23), u8tmp[2]&0x1);
+ CL_PRINTF(cli_buf);
+
+ u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x550);
+ u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x522);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x",
+ "0x550(bcn ctrl)/0x522", u32tmp[0], u8tmp[0]);
+ CL_PRINTF(cli_buf);
+
+ u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0xc50);
+ u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x49c);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x",
+ "0xc50(dig)/0x49c(null-drop)", u32tmp[0]&0xff, u8tmp[0]);
+ CL_PRINTF(cli_buf);
+
+ u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0xda0);
+ u32tmp[1] = btcoexist->btc_read_4byte(btcoexist, 0xda4);
+ u32tmp[2] = btcoexist->btc_read_4byte(btcoexist, 0xda8);
+ u32tmp[3] = btcoexist->btc_read_4byte(btcoexist, 0xcf0);
+
+ u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0xa5b);
+ u8tmp[1] = btcoexist->btc_read_1byte(btcoexist, 0xa5c);
+
+ fa_ofdm = ((u32tmp[0]&0xffff0000) >> 16) +
+ ((u32tmp[1]&0xffff0000) >> 16) +
+ (u32tmp[1] & 0xffff) +
+ (u32tmp[2] & 0xffff) +
+ ((u32tmp[3]&0xffff0000) >> 16) +
+ (u32tmp[3] & 0xffff);
+ fa_cck = (u8tmp[0] << 8) + u8tmp[1];
+
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x/ 0x%x",
+ "OFDM-CCA/OFDM-FA/CCK-FA",
+ u32tmp[0]&0xffff, fa_ofdm, fa_cck);
+ CL_PRINTF(cli_buf);
+
+ u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x6c0);
+ u32tmp[1] = btcoexist->btc_read_4byte(btcoexist, 0x6c4);
+ u32tmp[2] = btcoexist->btc_read_4byte(btcoexist, 0x6c8);
+ u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x6cc);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE,
+ "\r\n %-35s = 0x%x/ 0x%x/ 0x%x/ 0x%x",
+ "0x6c0/0x6c4/0x6c8/0x6cc(coexTable)",
+ u32tmp[0], u32tmp[1], u32tmp[2], u8tmp[0]);
+ CL_PRINTF(cli_buf);
+
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d",
+ "0x770(high-pri rx/tx)",
+ coex_sta->high_priority_rx, coex_sta->high_priority_tx);
+ CL_PRINTF(cli_buf);
+ CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d",
+ "0x774(low-pri rx/tx)", coex_sta->low_priority_rx,
+ coex_sta->low_priority_tx);
+ CL_PRINTF(cli_buf);
+#if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 1)
+ btc8723b2ant_monitor_bt_ctr(btcoexist);
+#endif
+ btcoexist->btc_disp_dbg_msg(btcoexist,
+ BTC_DBG_DISP_COEX_STATISTICS);
+}
+
+
+void ex_halbtc8723b2ant_ips_notify(struct btc_coexist *btcoexist, u8 type)
+{
+ if (BTC_IPS_ENTER == type) {
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], IPS ENTER notify\n");
+ coex_sta->under_ips = true;
+ btc8723b2ant_coex_alloff(btcoexist);
+ } else if (BTC_IPS_LEAVE == type) {
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], IPS LEAVE notify\n");
+ coex_sta->under_ips = false;
+ }
+}
+
+void ex_halbtc8723b2ant_lps_notify(struct btc_coexist *btcoexist, u8 type)
+{
+ if (BTC_LPS_ENABLE == type) {
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], LPS ENABLE notify\n");
+ coex_sta->under_lps = true;
+ } else if (BTC_LPS_DISABLE == type) {
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], LPS DISABLE notify\n");
+ coex_sta->under_lps = false;
+ }
+}
+
+void ex_halbtc8723b2ant_scan_notify(struct btc_coexist *btcoexist, u8 type)
+{
+ if (BTC_SCAN_START == type)
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], SCAN START notify\n");
+ else if (BTC_SCAN_FINISH == type)
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], SCAN FINISH notify\n");
+}
+
+void ex_halbtc8723b2ant_connect_notify(struct btc_coexist *btcoexist, u8 type)
+{
+ if (BTC_ASSOCIATE_START == type)
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], CONNECT START notify\n");
+ else if (BTC_ASSOCIATE_FINISH == type)
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], CONNECT FINISH notify\n");
+}
+
+void btc8723b_med_stat_notify(struct btc_coexist *btcoexist,
+ u8 type)
+{
+ u8 h2c_parameter[3] = {0};
+ u32 wifi_bw;
+ u8 wifi_central_chnl;
+
+ if (BTC_MEDIA_CONNECT == type)
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], MEDIA connect notify\n");
+ else
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], MEDIA disconnect notify\n");
+
+ /* only 2.4G we need to inform bt the chnl mask */
+ btcoexist->btc_get(btcoexist,
+ BTC_GET_U1_WIFI_CENTRAL_CHNL, &wifi_central_chnl);
+ if ((BTC_MEDIA_CONNECT == type) &&
+ (wifi_central_chnl <= 14)) {
+ h2c_parameter[0] = 0x1;
+ h2c_parameter[1] = wifi_central_chnl;
+ btcoexist->btc_get(btcoexist,
+ BTC_GET_U4_WIFI_BW, &wifi_bw);
+ if (BTC_WIFI_BW_HT40 == wifi_bw)
+ h2c_parameter[2] = 0x30;
+ else
+ h2c_parameter[2] = 0x20;
+ }
+
+ coex_dm->wifi_chnl_info[0] = h2c_parameter[0];
+ coex_dm->wifi_chnl_info[1] = h2c_parameter[1];
+ coex_dm->wifi_chnl_info[2] = h2c_parameter[2];
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC,
+ "[BTCoex], FW write 0x66=0x%x\n",
+ h2c_parameter[0] << 16 | h2c_parameter[1] << 8 |
+ h2c_parameter[2]);
+
+ btcoexist->btc_fill_h2c(btcoexist, 0x66, 3, h2c_parameter);
+}
+
+void ex_halbtc8723b2ant_special_packet_notify(struct btc_coexist *btcoexist,
+ u8 type)
+{
+ if (type == BTC_PACKET_DHCP)
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], DHCP Packet notify\n");
+}
+
+void ex_halbtc8723b2ant_bt_info_notify(struct btc_coexist *btcoexist,
+ u8 *tmpbuf, u8 length)
+{
+ u8 bt_info = 0;
+ u8 i, rsp_source = 0;
+ bool bt_busy = false, limited_dig = false;
+ bool wifi_connected = false;
+
+ coex_sta->c2h_bt_info_req_sent = false;
+
+ rsp_source = tmpbuf[0]&0xf;
+ if (rsp_source >= BT_INFO_SRC_8723B_2ANT_MAX)
+ rsp_source = BT_INFO_SRC_8723B_2ANT_WIFI_FW;
+ coex_sta->bt_info_c2h_cnt[rsp_source]++;
+
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex], Bt info[%d], length=%d, hex data=[",
+ rsp_source, length);
+ for (i = 0; i < length; i++) {
+ coex_sta->bt_info_c2h[rsp_source][i] = tmpbuf[i];
+ if (i == 1)
+ bt_info = tmpbuf[i];
+ if (i == length-1)
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "0x%02x]\n", tmpbuf[i]);
+ else
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "0x%02x, ", tmpbuf[i]);
+ }
+
+ if (btcoexist->manual_control) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], BtInfoNotify(), "
+ "return for Manual CTRL<===\n");
+ return;
+ }
+
+ if (BT_INFO_SRC_8723B_2ANT_WIFI_FW != rsp_source) {
+ coex_sta->bt_retry_cnt = /* [3:0]*/
+ coex_sta->bt_info_c2h[rsp_source][2] & 0xf;
+
+ coex_sta->bt_rssi =
+ coex_sta->bt_info_c2h[rsp_source][3] * 2 + 10;
+
+ coex_sta->bt_info_ext =
+ coex_sta->bt_info_c2h[rsp_source][4];
+
+ /* Here we need to resend some wifi info to BT
+ * because bt is reset and loss of the info.
+ */
+ if ((coex_sta->bt_info_ext & BIT1)) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], BT ext info bit1 check,"
+ " send wifi BW&Chnl to BT!!\n");
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED,
+ &wifi_connected);
+ if (wifi_connected)
+ btc8723b_med_stat_notify(btcoexist,
+ BTC_MEDIA_CONNECT);
+ else
+ btc8723b_med_stat_notify(btcoexist,
+ BTC_MEDIA_DISCONNECT);
+ }
+
+ if ((coex_sta->bt_info_ext & BIT3)) {
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], BT ext info bit3 check, "
+ "set BT NOT to ignore Wlan active!!\n");
+ btc8723b2ant_ignore_wlan_act(btcoexist, FORCE_EXEC,
+ false);
+ } else {
+ /* BT already NOT ignore Wlan active, do nothing here.*/
+ }
+#if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 0)
+ if ((coex_sta->bt_info_ext & BIT4)) {
+ /* BT auto report already enabled, do nothing*/
+ } else {
+ btc8723b2ant_bt_auto_report(btcoexist, FORCE_EXEC,
+ true);
+ }
+#endif
+ }
+
+ /* check BIT2 first ==> check if bt is under inquiry or page scan*/
+ if (bt_info & BT_INFO_8723B_2ANT_B_INQ_PAGE)
+ coex_sta->c2h_bt_inquiry_page = true;
+ else
+ coex_sta->c2h_bt_inquiry_page = false;
+
+ /* set link exist status*/
+ if (!(bt_info & BT_INFO_8723B_2ANT_B_CONNECTION)) {
+ coex_sta->bt_link_exist = false;
+ coex_sta->pan_exist = false;
+ coex_sta->a2dp_exist = false;
+ coex_sta->hid_exist = false;
+ coex_sta->sco_exist = false;
+ } else { /* connection exists */
+ coex_sta->bt_link_exist = true;
+ if (bt_info & BT_INFO_8723B_2ANT_B_FTP)
+ coex_sta->pan_exist = true;
+ else
+ coex_sta->pan_exist = false;
+ if (bt_info & BT_INFO_8723B_2ANT_B_A2DP)
+ coex_sta->a2dp_exist = true;
+ else
+ coex_sta->a2dp_exist = false;
+ if (bt_info & BT_INFO_8723B_2ANT_B_HID)
+ coex_sta->hid_exist = true;
+ else
+ coex_sta->hid_exist = false;
+ if (bt_info & BT_INFO_8723B_2ANT_B_SCO_ESCO)
+ coex_sta->sco_exist = true;
+ else
+ coex_sta->sco_exist = false;
+ }
+
+ btc8723b2ant_update_bt_link_info(btcoexist);
+
+ if (!(bt_info & BT_INFO_8723B_2ANT_B_CONNECTION)) {
+ coex_dm->bt_status = BT_8723B_2ANT_BT_STATUS_NON_CONNECTED_IDLE;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], BtInfoNotify(), "
+ "BT Non-Connected idle!!!\n");
+ /* connection exists but no busy */
+ } else if (bt_info == BT_INFO_8723B_2ANT_B_CONNECTION) {
+ coex_dm->bt_status = BT_8723B_2ANT_BT_STATUS_CONNECTED_IDLE;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], BtInfoNotify(), BT Connected-idle!!!\n");
+ } else if ((bt_info & BT_INFO_8723B_2ANT_B_SCO_ESCO) ||
+ (bt_info & BT_INFO_8723B_2ANT_B_SCO_BUSY)) {
+ coex_dm->bt_status = BT_8723B_2ANT_BT_STATUS_SCO_BUSY;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], BtInfoNotify(), BT SCO busy!!!\n");
+ } else if (bt_info & BT_INFO_8723B_2ANT_B_ACL_BUSY) {
+ coex_dm->bt_status = BT_8723B_2ANT_BT_STATUS_ACL_BUSY;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], BtInfoNotify(), BT ACL busy!!!\n");
+ } else {
+ coex_dm->bt_status = BT_8723B_2ANT_BT_STATUS_MAX;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], BtInfoNotify(), "
+ "BT Non-Defined state!!!\n");
+ }
+
+ if ((BT_8723B_2ANT_BT_STATUS_ACL_BUSY == coex_dm->bt_status) ||
+ (BT_8723B_2ANT_BT_STATUS_SCO_BUSY == coex_dm->bt_status) ||
+ (BT_8723B_2ANT_BT_STATUS_ACL_SCO_BUSY == coex_dm->bt_status)) {
+ bt_busy = true;
+ limited_dig = true;
+ } else {
+ bt_busy = false;
+ limited_dig = false;
+ }
+
+ btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_TRAFFIC_BUSY, &bt_busy);
+
+ coex_dm->limited_dig = limited_dig;
+ btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_LIMITED_DIG, &limited_dig);
+
+ btc8723b2ant_run_coexist_mechanism(btcoexist);
+}
+
+void ex_halbtc8723b2ant_stack_operation_notify(struct btc_coexist *btcoexist,
+ u8 type)
+{
+ if (BTC_STACK_OP_INQ_PAGE_PAIR_START == type)
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex],StackOP Inquiry/page/pair start notify\n");
+ else if (BTC_STACK_OP_INQ_PAGE_PAIR_FINISH == type)
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY,
+ "[BTCoex],StackOP Inquiry/page/pair finish notify\n");
+}
+
+void ex_halbtc8723b2ant_halt_notify(struct btc_coexist *btcoexist)
+{
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, "[BTCoex], Halt notify\n");
+
+ btc8723b2ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true);
+ btc8723b_med_stat_notify(btcoexist, BTC_MEDIA_DISCONNECT);
+}
+
+void ex_halbtc8723b2ant_periodical(struct btc_coexist *btcoexist)
+{
+ struct btc_board_info *board_info = &btcoexist->board_info;
+ struct btc_stack_info *stack_info = &btcoexist->stack_info;
+ static u8 dis_ver_info_cnt;
+ u32 fw_ver = 0, bt_patch_ver = 0;
+
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "[BTCoex], =========================="
+ "Periodical===========================\n");
+
+ if (dis_ver_info_cnt <= 5) {
+ dis_ver_info_cnt += 1;
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT,
+ "[BTCoex], ****************************"
+ "************************************\n");
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT,
+ "[BTCoex], Ant PG Num/ Ant Mech/ "
+ "Ant Pos = %d/ %d/ %d\n", board_info->pg_ant_num,
+ board_info->btdm_ant_num, board_info->btdm_ant_pos);
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT,
+ "[BTCoex], BT stack/ hci ext ver = %s / %d\n",
+ ((stack_info->profile_notified) ? "Yes" : "No"),
+ stack_info->hci_version);
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_BT_PATCH_VER,
+ &bt_patch_ver);
+ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_FW_VER, &fw_ver);
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT,
+ "[BTCoex], CoexVer/ FwVer/ PatchVer = "
+ "%d_%x/ 0x%x/ 0x%x(%d)\n",
+ glcoex_ver_date_8723b_2ant, glcoex_ver_8723b_2ant,
+ fw_ver, bt_patch_ver, bt_patch_ver);
+ BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT,
+ "[BTCoex], *****************************"
+ "***********************************\n");
+ }
+
+#if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 0)
+ btc8723b2ant_query_bt_info(btcoexist);
+ btc8723b2ant_monitor_bt_ctr(btcoexist);
+ btc8723b2ant_monitor_bt_enable_disable(btcoexist);
+#else
+ if (btc8723b2ant_is_wifi_status_changed(btcoexist) ||
+ coex_dm->auto_tdma_adjust)
+ btc8723b2ant_run_coexist_mechanism(btcoexist);
+#endif
+}
diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.h
new file mode 100644
index 000000000000..e0ad8e545f82
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.h
@@ -0,0 +1,173 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2012 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+#ifndef _HAL8723B_2_ANT
+#define _HAL8723B_2_ANT
+
+/************************************************************************
+ * The following is for 8723B 2Ant BT Co-exist definition
+ ************************************************************************/
+#define BT_AUTO_REPORT_ONLY_8723B_2ANT 1
+
+#define BT_INFO_8723B_2ANT_B_FTP BIT7
+#define BT_INFO_8723B_2ANT_B_A2DP BIT6
+#define BT_INFO_8723B_2ANT_B_HID BIT5
+#define BT_INFO_8723B_2ANT_B_SCO_BUSY BIT4
+#define BT_INFO_8723B_2ANT_B_ACL_BUSY BIT3
+#define BT_INFO_8723B_2ANT_B_INQ_PAGE BIT2
+#define BT_INFO_8723B_2ANT_B_SCO_ESCO BIT1
+#define BT_INFO_8723B_2ANT_B_CONNECTION BIT0
+
+#define BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT 2
+
+enum BT_INFO_SRC_8723B_2ANT {
+ BT_INFO_SRC_8723B_2ANT_WIFI_FW = 0x0,
+ BT_INFO_SRC_8723B_2ANT_BT_RSP = 0x1,
+ BT_INFO_SRC_8723B_2ANT_BT_ACTIVE_SEND = 0x2,
+ BT_INFO_SRC_8723B_2ANT_MAX
+};
+
+enum BT_8723B_2ANT_BT_STATUS {
+ BT_8723B_2ANT_BT_STATUS_NON_CONNECTED_IDLE = 0x0,
+ BT_8723B_2ANT_BT_STATUS_CONNECTED_IDLE = 0x1,
+ BT_8723B_2ANT_BT_STATUS_INQ_PAGE = 0x2,
+ BT_8723B_2ANT_BT_STATUS_ACL_BUSY = 0x3,
+ BT_8723B_2ANT_BT_STATUS_SCO_BUSY = 0x4,
+ BT_8723B_2ANT_BT_STATUS_ACL_SCO_BUSY = 0x5,
+ BT_8723B_2ANT_BT_STATUS_MAX
+};
+
+enum BT_8723B_2ANT_COEX_ALGO {
+ BT_8723B_2ANT_COEX_ALGO_UNDEFINED = 0x0,
+ BT_8723B_2ANT_COEX_ALGO_SCO = 0x1,
+ BT_8723B_2ANT_COEX_ALGO_HID = 0x2,
+ BT_8723B_2ANT_COEX_ALGO_A2DP = 0x3,
+ BT_8723B_2ANT_COEX_ALGO_A2DP_PANHS = 0x4,
+ BT_8723B_2ANT_COEX_ALGO_PANEDR = 0x5,
+ BT_8723B_2ANT_COEX_ALGO_PANHS = 0x6,
+ BT_8723B_2ANT_COEX_ALGO_PANEDR_A2DP = 0x7,
+ BT_8723B_2ANT_COEX_ALGO_PANEDR_HID = 0x8,
+ BT_8723B_2ANT_COEX_ALGO_HID_A2DP_PANEDR = 0x9,
+ BT_8723B_2ANT_COEX_ALGO_HID_A2DP = 0xa,
+ BT_8723B_2ANT_COEX_ALGO_MAX = 0xb,
+};
+
+struct coex_dm_8723b_2ant {
+ /* fw mechanism */
+ bool pre_dec_bt_pwr;
+ bool cur_dec_bt_pwr;
+ u8 pre_fw_dac_swing_lvl;
+ u8 cur_fw_dac_swing_lvl;
+ bool cur_ignore_wlan_act;
+ bool pre_ignore_wlan_act;
+ u8 pre_ps_tdma;
+ u8 cur_ps_tdma;
+ u8 ps_tdma_para[5];
+ u8 tdma_adj_type;
+ bool reset_tdma_adjust;
+ bool auto_tdma_adjust;
+ bool pre_ps_tdma_on;
+ bool cur_ps_tdma_on;
+ bool pre_bt_auto_report;
+ bool cur_bt_auto_report;
+
+ /* sw mechanism */
+ bool pre_rf_rx_lpf_shrink;
+ bool cur_rf_rx_lpf_shrink;
+ u32 bt_rf0x1e_backup;
+ bool pre_low_penalty_ra;
+ bool cur_low_penalty_ra;
+ bool pre_dac_swing_on;
+ u32 pre_dac_swing_lvl;
+ bool cur_dac_swing_on;
+ u32 cur_dac_swing_lvl;
+ bool pre_adc_back_off;
+ bool cur_adc_back_off;
+ bool pre_agc_table_en;
+ bool cur_agc_table_en;
+ u32 pre_val0x6c0;
+ u32 cur_val0x6c0;
+ u32 pre_val0x6c4;
+ u32 cur_val0x6c4;
+ u32 pre_val0x6c8;
+ u32 cur_val0x6c8;
+ u8 pre_val0x6cc;
+ u8 cur_val0x6cc;
+ bool limited_dig;
+
+ /* algorithm related */
+ u8 pre_algorithm;
+ u8 cur_algorithm;
+ u8 bt_status;
+ u8 wifi_chnl_info[3];
+
+ bool need_recover_0x948;
+ u16 backup_0x948;
+};
+
+struct coex_sta_8723b_2ant {
+ bool bt_link_exist;
+ bool sco_exist;
+ bool a2dp_exist;
+ bool hid_exist;
+ bool pan_exist;
+
+ bool under_lps;
+ bool under_ips;
+ u32 high_priority_tx;
+ u32 high_priority_rx;
+ u32 low_priority_tx;
+ u32 low_priority_rx;
+ u8 bt_rssi;
+ u8 pre_bt_rssi_state;
+ u8 pre_wifi_rssi_state[4];
+ bool c2h_bt_info_req_sent;
+ u8 bt_info_c2h[BT_INFO_SRC_8723B_2ANT_MAX][10];
+ u32 bt_info_c2h_cnt[BT_INFO_SRC_8723B_2ANT_MAX];
+ bool c2h_bt_inquiry_page;
+ u8 bt_retry_cnt;
+ u8 bt_info_ext;
+};
+
+/*********************************************************************
+ * The following is interface which will notify coex module.
+ *********************************************************************/
+void ex_halbtc8723b2ant_init_hwconfig(struct btc_coexist *btcoexist);
+void ex_halbtc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist);
+void ex_halbtc8723b2ant_ips_notify(struct btc_coexist *btcoexist, u8 type);
+void ex_halbtc8723b2ant_lps_notify(struct btc_coexist *btcoexist, u8 type);
+void ex_halbtc8723b2ant_scan_notify(struct btc_coexist *btcoexist, u8 type);
+void ex_halbtc8723b2ant_connect_notify(struct btc_coexist *btcoexist, u8 type);
+void btc8723b_med_stat_notify(struct btc_coexist *btcoexist, u8 type);
+void ex_halbtc8723b2ant_special_packet_notify(struct btc_coexist *btcoexist,
+ u8 type);
+void ex_halbtc8723b2ant_bt_info_notify(struct btc_coexist *btcoexist,
+ u8 *tmpbuf, u8 length);
+void ex_halbtc8723b2ant_stack_operation_notify(struct btc_coexist *btcoexist,
+ u8 type);
+void ex_halbtc8723b2ant_halt_notify(struct btc_coexist *btcoexist);
+void ex_halbtc8723b2ant_periodical(struct btc_coexist *btcoexist);
+void ex_halbtc8723b2ant_display_coex_info(struct btc_coexist *btcoexist);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.c
new file mode 100644
index 000000000000..b6722de64a31
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.c
@@ -0,0 +1,1011 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2013 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#include "halbt_precomp.h"
+
+/***********************************************
+ * Global variables
+ ***********************************************/
+
+struct btc_coexist gl_bt_coexist;
+
+u32 btc_dbg_type[BTC_MSG_MAX];
+static u8 btc_dbg_buf[100];
+
+/***************************************************
+ * Debug related function
+ ***************************************************/
+static bool halbtc_is_bt_coexist_available(struct btc_coexist *btcoexist)
+{
+ if (!btcoexist->binded || NULL == btcoexist->adapter)
+ return false;
+
+ return true;
+}
+
+static bool halbtc_is_wifi_busy(struct rtl_priv *rtlpriv)
+{
+ if (rtlpriv->link_info.busytraffic)
+ return true;
+ else
+ return false;
+}
+
+static void halbtc_dbg_init(void)
+{
+ u8 i;
+
+ for (i = 0; i < BTC_MSG_MAX; i++)
+ btc_dbg_type[i] = 0;
+
+ btc_dbg_type[BTC_MSG_INTERFACE] =
+/* INTF_INIT | */
+/* INTF_NOTIFY | */
+ 0;
+
+ btc_dbg_type[BTC_MSG_ALGORITHM] =
+/* ALGO_BT_RSSI_STATE | */
+/* ALGO_WIFI_RSSI_STATE | */
+/* ALGO_BT_MONITOR | */
+/* ALGO_TRACE | */
+/* ALGO_TRACE_FW | */
+/* ALGO_TRACE_FW_DETAIL | */
+/* ALGO_TRACE_FW_EXEC | */
+/* ALGO_TRACE_SW | */
+/* ALGO_TRACE_SW_DETAIL | */
+/* ALGO_TRACE_SW_EXEC | */
+ 0;
+}
+
+static bool halbtc_is_bt40(struct rtl_priv *adapter)
+{
+ struct rtl_priv *rtlpriv = adapter;
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ bool is_ht40 = true;
+ enum ht_channel_width bw = rtlphy->current_chan_bw;
+
+ if (bw == HT_CHANNEL_WIDTH_20)
+ is_ht40 = false;
+ else if (bw == HT_CHANNEL_WIDTH_20_40)
+ is_ht40 = true;
+
+ return is_ht40;
+}
+
+static bool halbtc_legacy(struct rtl_priv *adapter)
+{
+ struct rtl_priv *rtlpriv = adapter;
+ struct rtl_mac *mac = rtl_mac(rtlpriv);
+
+ bool is_legacy = false;
+
+ if ((mac->mode == WIRELESS_MODE_B) || (mac->mode == WIRELESS_MODE_B))
+ is_legacy = true;
+
+ return is_legacy;
+}
+
+bool halbtc_is_wifi_uplink(struct rtl_priv *adapter)
+{
+ struct rtl_priv *rtlpriv = adapter;
+
+ if (rtlpriv->link_info.tx_busy_traffic)
+ return true;
+ else
+ return false;
+}
+
+static u32 halbtc_get_wifi_bw(struct btc_coexist *btcoexist)
+{
+ struct rtl_priv *rtlpriv =
+ (struct rtl_priv *)btcoexist->adapter;
+ u32 wifi_bw = BTC_WIFI_BW_HT20;
+
+ if (halbtc_is_bt40(rtlpriv)) {
+ wifi_bw = BTC_WIFI_BW_HT40;
+ } else {
+ if (halbtc_legacy(rtlpriv))
+ wifi_bw = BTC_WIFI_BW_LEGACY;
+ else
+ wifi_bw = BTC_WIFI_BW_HT20;
+ }
+ return wifi_bw;
+}
+
+static u8 halbtc_get_wifi_central_chnl(struct btc_coexist *btcoexist)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u8 chnl = 1;
+
+ if (rtlphy->current_channel != 0)
+ chnl = rtlphy->current_channel;
+ BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE,
+ "static halbtc_get_wifi_central_chnl:%d\n", chnl);
+ return chnl;
+}
+
+static void halbtc_leave_lps(struct btc_coexist *btcoexist)
+{
+ struct rtl_priv *rtlpriv;
+ struct rtl_ps_ctl *ppsc;
+ bool ap_enable = false;
+
+ rtlpriv = btcoexist->adapter;
+ ppsc = rtl_psc(rtlpriv);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_AP_MODE_ENABLE,
+ &ap_enable);
+
+ if (ap_enable) {
+ pr_info("halbtc_leave_lps()<--dont leave lps under AP mode\n");
+ return;
+ }
+
+ btcoexist->bt_info.bt_ctrl_lps = true;
+ btcoexist->bt_info.bt_lps_on = false;
+}
+
+static void halbtc_enter_lps(struct btc_coexist *btcoexist)
+{
+ struct rtl_priv *rtlpriv;
+ struct rtl_ps_ctl *ppsc;
+ bool ap_enable = false;
+
+ rtlpriv = btcoexist->adapter;
+ ppsc = rtl_psc(rtlpriv);
+
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_AP_MODE_ENABLE,
+ &ap_enable);
+
+ if (ap_enable) {
+ pr_info("halbtc_enter_lps()<--dont enter lps under AP mode\n");
+ return;
+ }
+
+ btcoexist->bt_info.bt_ctrl_lps = true;
+ btcoexist->bt_info.bt_lps_on = false;
+}
+
+static void halbtc_normal_lps(struct btc_coexist *btcoexist)
+{
+ if (btcoexist->bt_info.bt_ctrl_lps) {
+ btcoexist->bt_info.bt_lps_on = false;
+ btcoexist->bt_info.bt_ctrl_lps = false;
+ }
+}
+
+static void halbtc_leave_low_power(void)
+{
+}
+
+static void halbtc_nomal_low_power(void)
+{
+}
+
+static void halbtc_disable_low_power(void)
+{
+}
+
+static void halbtc_aggregation_check(void)
+{
+}
+
+static u32 halbtc_get_bt_patch_version(struct btc_coexist *btcoexist)
+{
+ return 0;
+}
+
+static s32 halbtc_get_wifi_rssi(struct rtl_priv *adapter)
+{
+ struct rtl_priv *rtlpriv = adapter;
+ s32 undec_sm_pwdb = 0;
+
+ if (rtlpriv->mac80211.link_state >= MAC80211_LINKED)
+ undec_sm_pwdb = rtlpriv->dm.undec_sm_pwdb;
+ else /* associated entry pwdb */
+ undec_sm_pwdb = rtlpriv->dm.undec_sm_pwdb;
+ return undec_sm_pwdb;
+}
+
+static bool halbtc_get(void *void_btcoexist, u8 get_type, void *out_buf)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)void_btcoexist;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_mac *mac = rtl_mac(rtlpriv);
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ bool *bool_tmp = (bool *)out_buf;
+ int *s32_tmp = (int *)out_buf;
+ u32 *u32_tmp = (u32 *)out_buf;
+ u8 *u8_tmp = (u8 *)out_buf;
+ bool tmp = false;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return false;
+
+ switch (get_type) {
+ case BTC_GET_BL_HS_OPERATION:
+ *bool_tmp = false;
+ break;
+ case BTC_GET_BL_HS_CONNECTING:
+ *bool_tmp = false;
+ break;
+ case BTC_GET_BL_WIFI_CONNECTED:
+ if (rtlpriv->mac80211.link_state >= MAC80211_LINKED)
+ tmp = true;
+ *bool_tmp = tmp;
+ break;
+ case BTC_GET_BL_WIFI_BUSY:
+ if (halbtc_is_wifi_busy(rtlpriv))
+ *bool_tmp = true;
+ else
+ *bool_tmp = false;
+ break;
+ case BTC_GET_BL_WIFI_SCAN:
+ if (mac->act_scanning)
+ *bool_tmp = true;
+ else
+ *bool_tmp = false;
+ break;
+ case BTC_GET_BL_WIFI_LINK:
+ if (mac->link_state == MAC80211_LINKING)
+ *bool_tmp = true;
+ else
+ *bool_tmp = false;
+ break;
+ case BTC_GET_BL_WIFI_ROAM: /*TODO*/
+ if (mac->link_state == MAC80211_LINKING)
+ *bool_tmp = true;
+ else
+ *bool_tmp = false;
+ break;
+ case BTC_GET_BL_WIFI_4_WAY_PROGRESS: /*TODO*/
+ *bool_tmp = false;
+
+ break;
+ case BTC_GET_BL_WIFI_UNDER_5G:
+ *bool_tmp = false; /*TODO*/
+
+ case BTC_GET_BL_WIFI_DHCP: /*TODO*/
+ break;
+ case BTC_GET_BL_WIFI_SOFTAP_IDLE:
+ *bool_tmp = true;
+ break;
+ case BTC_GET_BL_WIFI_SOFTAP_LINKING:
+ *bool_tmp = false;
+ break;
+ case BTC_GET_BL_WIFI_IN_EARLY_SUSPEND:
+ *bool_tmp = false;
+ break;
+ case BTC_GET_BL_WIFI_AP_MODE_ENABLE:
+ *bool_tmp = false;
+ break;
+ case BTC_GET_BL_WIFI_ENABLE_ENCRYPTION:
+ if (NO_ENCRYPTION == rtlpriv->sec.pairwise_enc_algorithm)
+ *bool_tmp = false;
+ else
+ *bool_tmp = true;
+ break;
+ case BTC_GET_BL_WIFI_UNDER_B_MODE:
+ *bool_tmp = false; /*TODO*/
+ break;
+ case BTC_GET_BL_EXT_SWITCH:
+ *bool_tmp = false;
+ break;
+ case BTC_GET_S4_WIFI_RSSI:
+ *s32_tmp = halbtc_get_wifi_rssi(rtlpriv);
+ break;
+ case BTC_GET_S4_HS_RSSI: /*TODO*/
+ *s32_tmp = halbtc_get_wifi_rssi(rtlpriv);
+ break;
+ case BTC_GET_U4_WIFI_BW:
+ *u32_tmp = halbtc_get_wifi_bw(btcoexist);
+ break;
+ case BTC_GET_U4_WIFI_TRAFFIC_DIRECTION:
+ if (halbtc_is_wifi_uplink(rtlpriv))
+ *u32_tmp = BTC_WIFI_TRAFFIC_TX;
+ else
+ *u32_tmp = BTC_WIFI_TRAFFIC_RX;
+ break;
+ case BTC_GET_U4_WIFI_FW_VER:
+ *u32_tmp = rtlhal->fw_version;
+ break;
+ case BTC_GET_U4_BT_PATCH_VER:
+ *u32_tmp = halbtc_get_bt_patch_version(btcoexist);
+ break;
+ case BTC_GET_U1_WIFI_DOT11_CHNL:
+ *u8_tmp = rtlphy->current_channel;
+ break;
+ case BTC_GET_U1_WIFI_CENTRAL_CHNL:
+ *u8_tmp = halbtc_get_wifi_central_chnl(btcoexist);
+ break;
+ case BTC_GET_U1_WIFI_HS_CHNL:
+ *u8_tmp = 1;/*BT_OperateChnl(rtlpriv);*/
+ break;
+ case BTC_GET_U1_MAC_PHY_MODE:
+ *u8_tmp = BTC_MP_UNKNOWN;
+ break;
+
+ /************* 1Ant **************/
+ case BTC_GET_U1_LPS_MODE:
+ *u8_tmp = btcoexist->pwr_mode_val[0];
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static bool halbtc_set(void *void_btcoexist, u8 set_type, void *in_buf)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)void_btcoexist;
+ bool *bool_tmp = (bool *)in_buf;
+ u8 *u8_tmp = (u8 *)in_buf;
+ u32 *u32_tmp = (u32 *)in_buf;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return false;
+
+ switch (set_type) {
+ /* set some bool type variables. */
+ case BTC_SET_BL_BT_DISABLE:
+ btcoexist->bt_info.bt_disabled = *bool_tmp;
+ break;
+ case BTC_SET_BL_BT_TRAFFIC_BUSY:
+ btcoexist->bt_info.bt_busy = *bool_tmp;
+ break;
+ case BTC_SET_BL_BT_LIMITED_DIG:
+ btcoexist->bt_info.limited_dig = *bool_tmp;
+ break;
+ case BTC_SET_BL_FORCE_TO_ROAM:
+ btcoexist->bt_info.force_to_roam = *bool_tmp;
+ break;
+ case BTC_SET_BL_TO_REJ_AP_AGG_PKT:
+ btcoexist->bt_info.reject_agg_pkt = *bool_tmp;
+ break;
+ case BTC_SET_BL_BT_CTRL_AGG_SIZE:
+ btcoexist->bt_info.b_bt_ctrl_buf_size = *bool_tmp;
+ break;
+ case BTC_SET_BL_INC_SCAN_DEV_NUM:
+ btcoexist->bt_info.increase_scan_dev_num = *bool_tmp;
+ break;
+ /* set some u1Byte type variables. */
+ case BTC_SET_U1_RSSI_ADJ_VAL_FOR_AGC_TABLE_ON:
+ btcoexist->bt_info.rssi_adjust_for_agc_table_on = *u8_tmp;
+ break;
+ case BTC_SET_U1_AGG_BUF_SIZE:
+ btcoexist->bt_info.agg_buf_size = *u8_tmp;
+ break;
+ /* the following are some action which will be triggered */
+ case BTC_SET_ACT_GET_BT_RSSI:
+ /*BTHCI_SendGetBtRssiEvent(rtlpriv);*/
+ break;
+ case BTC_SET_ACT_AGGREGATE_CTRL:
+ halbtc_aggregation_check();
+ break;
+
+ /* 1Ant */
+ case BTC_SET_U1_RSSI_ADJ_VAL_FOR_1ANT_COEX_TYPE:
+ btcoexist->bt_info.rssi_adjust_for_1ant_coex_type = *u8_tmp;
+ break;
+ case BTC_SET_UI_SCAN_SIG_COMPENSATION:
+ /* rtlpriv->mlmepriv.scan_compensation = *u8_tmp; */
+ break;
+ case BTC_SET_U1_1ANT_LPS:
+ btcoexist->bt_info.lps_1ant = *u8_tmp;
+ break;
+ case BTC_SET_U1_1ANT_RPWM:
+ btcoexist->bt_info.rpwm_1ant = *u8_tmp;
+ break;
+ /* the following are some action which will be triggered */
+ case BTC_SET_ACT_LEAVE_LPS:
+ halbtc_leave_lps(btcoexist);
+ break;
+ case BTC_SET_ACT_ENTER_LPS:
+ halbtc_enter_lps(btcoexist);
+ break;
+ case BTC_SET_ACT_NORMAL_LPS:
+ halbtc_normal_lps(btcoexist);
+ break;
+ case BTC_SET_ACT_DISABLE_LOW_POWER:
+ halbtc_disable_low_power();
+ break;
+ case BTC_SET_ACT_UPDATE_ra_mask:
+ btcoexist->bt_info.ra_mask = *u32_tmp;
+ break;
+ case BTC_SET_ACT_SEND_MIMO_PS:
+ break;
+ case BTC_SET_ACT_INC_FORCE_EXEC_PWR_CMD_CNT:
+ btcoexist->bt_info.force_exec_pwr_cmd_cnt++;
+ break;
+ case BTC_SET_ACT_CTRL_BT_INFO: /*wait for 8812/8821*/
+ break;
+ case BTC_SET_ACT_CTRL_BT_COEX:
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static void halbtc_display_coex_statistics(struct btc_coexist *btcoexist)
+{
+}
+
+static void halbtc_display_bt_link_info(struct btc_coexist *btcoexist)
+{
+}
+
+static void halbtc_display_bt_fw_info(struct btc_coexist *btcoexist)
+{
+}
+
+static void halbtc_display_fw_pwr_mode_cmd(struct btc_coexist *btcoexist)
+{
+}
+
+/************************************************************
+ * IO related function
+ ************************************************************/
+static u8 halbtc_read_1byte(void *bt_context, u32 reg_addr)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ return rtl_read_byte(rtlpriv, reg_addr);
+}
+
+static u16 halbtc_read_2byte(void *bt_context, u32 reg_addr)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ return rtl_read_word(rtlpriv, reg_addr);
+}
+
+static u32 halbtc_read_4byte(void *bt_context, u32 reg_addr)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ return rtl_read_dword(rtlpriv, reg_addr);
+}
+
+static void halbtc_write_1byte(void *bt_context, u32 reg_addr, u8 data)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ rtl_write_byte(rtlpriv, reg_addr, data);
+}
+
+static void halbtc_bitmask_write_1byte(void *bt_context, u32 reg_addr,
+ u32 bit_mask, u8 data)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ u8 original_value, bit_shift = 0;
+ u8 i;
+
+ if (bit_mask != MASKDWORD) {/*if not "double word" write*/
+ original_value = rtl_read_byte(rtlpriv, reg_addr);
+ for (i = 0; i <= 7; i++) {
+ if ((bit_mask>>i) & 0x1)
+ break;
+ }
+ bit_shift = i;
+ data = (original_value & (~bit_mask)) |
+ ((data << bit_shift) & bit_mask);
+ }
+ rtl_write_byte(rtlpriv, reg_addr, data);
+}
+
+static void halbtc_write_2byte(void *bt_context, u32 reg_addr, u16 data)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ rtl_write_word(rtlpriv, reg_addr, data);
+}
+
+static void halbtc_write_4byte(void *bt_context, u32 reg_addr, u32 data)
+{
+ struct btc_coexist *btcoexist =
+ (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ rtl_write_dword(rtlpriv, reg_addr, data);
+}
+
+static void halbtc_set_bbreg(void *bt_context, u32 reg_addr, u32 bit_mask,
+ u32 data)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ rtl_set_bbreg(rtlpriv->mac80211.hw, reg_addr, bit_mask, data);
+}
+
+static u32 halbtc_get_bbreg(void *bt_context, u32 reg_addr, u32 bit_mask)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ return rtl_get_bbreg(rtlpriv->mac80211.hw, reg_addr, bit_mask);
+}
+
+static void halbtc_set_rfreg(void *bt_context, u8 rf_path, u32 reg_addr,
+ u32 bit_mask, u32 data)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ rtl_set_rfreg(rtlpriv->mac80211.hw, rf_path, reg_addr, bit_mask, data);
+}
+
+static u32 halbtc_get_rfreg(void *bt_context, u8 rf_path, u32 reg_addr,
+ u32 bit_mask)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ return rtl_get_rfreg(rtlpriv->mac80211.hw, rf_path, reg_addr, bit_mask);
+}
+
+static void halbtc_fill_h2c_cmd(void *bt_context, u8 element_id,
+ u32 cmd_len, u8 *cmd_buf)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ rtlpriv->cfg->ops->fill_h2c_cmd(rtlpriv->mac80211.hw, element_id,
+ cmd_len, cmd_buf);
+}
+
+static void halbtc_display_dbg_msg(void *bt_context, u8 disp_type)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ switch (disp_type) {
+ case BTC_DBG_DISP_COEX_STATISTICS:
+ halbtc_display_coex_statistics(btcoexist);
+ break;
+ case BTC_DBG_DISP_BT_LINK_INFO:
+ halbtc_display_bt_link_info(btcoexist);
+ break;
+ case BTC_DBG_DISP_BT_FW_VER:
+ halbtc_display_bt_fw_info(btcoexist);
+ break;
+ case BTC_DBG_DISP_FW_PWR_MODE_CMD:
+ halbtc_display_fw_pwr_mode_cmd(btcoexist);
+ break;
+ default:
+ break;
+ }
+}
+
+/*****************************************************************
+ * Extern functions called by other module
+ *****************************************************************/
+bool exhalbtc_initlize_variables(struct rtl_priv *adapter)
+{
+ struct btc_coexist *btcoexist = &gl_bt_coexist;
+
+ btcoexist->statistics.cnt_bind++;
+
+ halbtc_dbg_init();
+
+ if (btcoexist->binded)
+ return false;
+ else
+ btcoexist->binded = true;
+
+#if (defined(CONFIG_PCI_HCI))
+ btcoexist->chip_interface = BTC_INTF_PCI;
+#elif (defined(CONFIG_USB_HCI))
+ btcoexist->chip_interface = BTC_INTF_USB;
+#elif (defined(CONFIG_SDIO_HCI))
+ btcoexist->chip_interface = BTC_INTF_SDIO;
+#elif (defined(CONFIG_GSPI_HCI))
+ btcoexist->chip_interface = BTC_INTF_GSPI;
+#else
+ btcoexist->chip_interface = BTC_INTF_UNKNOWN;
+#endif
+
+ if (NULL == btcoexist->adapter)
+ btcoexist->adapter = adapter;
+
+ btcoexist->stack_info.profile_notified = false;
+
+ btcoexist->btc_read_1byte = halbtc_read_1byte;
+ btcoexist->btc_write_1byte = halbtc_write_1byte;
+ btcoexist->btc_write_1byte_bitmask = halbtc_bitmask_write_1byte;
+ btcoexist->btc_read_2byte = halbtc_read_2byte;
+ btcoexist->btc_write_2byte = halbtc_write_2byte;
+ btcoexist->btc_read_4byte = halbtc_read_4byte;
+ btcoexist->btc_write_4byte = halbtc_write_4byte;
+
+ btcoexist->btc_set_bb_reg = halbtc_set_bbreg;
+ btcoexist->btc_get_bb_reg = halbtc_get_bbreg;
+
+ btcoexist->btc_set_rf_reg = halbtc_set_rfreg;
+ btcoexist->btc_get_rf_reg = halbtc_get_rfreg;
+
+ btcoexist->btc_fill_h2c = halbtc_fill_h2c_cmd;
+ btcoexist->btc_disp_dbg_msg = halbtc_display_dbg_msg;
+
+ btcoexist->btc_get = halbtc_get;
+ btcoexist->btc_set = halbtc_set;
+
+ btcoexist->cli_buf = &btc_dbg_buf[0];
+
+ btcoexist->bt_info.b_bt_ctrl_buf_size = false;
+ btcoexist->bt_info.agg_buf_size = 5;
+
+ btcoexist->bt_info.increase_scan_dev_num = false;
+ return true;
+}
+
+void exhalbtc_init_hw_config(struct btc_coexist *btcoexist)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+
+ btcoexist->statistics.cnt_init_hw_config++;
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_init_hwconfig(btcoexist);
+}
+
+void exhalbtc_init_coex_dm(struct btc_coexist *btcoexist)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+
+ btcoexist->statistics.cnt_init_coex_dm++;
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_init_coex_dm(btcoexist);
+
+ btcoexist->initilized = true;
+}
+
+void exhalbtc_ips_notify(struct btc_coexist *btcoexist, u8 type)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ u8 ips_type;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+ btcoexist->statistics.cnt_ips_notify++;
+ if (btcoexist->manual_control)
+ return;
+
+ if (ERFOFF == type)
+ ips_type = BTC_IPS_ENTER;
+ else
+ ips_type = BTC_IPS_LEAVE;
+
+ halbtc_leave_low_power();
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_ips_notify(btcoexist, ips_type);
+
+ halbtc_nomal_low_power();
+}
+
+void exhalbtc_lps_notify(struct btc_coexist *btcoexist, u8 type)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ u8 lps_type;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+ btcoexist->statistics.cnt_lps_notify++;
+ if (btcoexist->manual_control)
+ return;
+
+ if (EACTIVE == type)
+ lps_type = BTC_LPS_DISABLE;
+ else
+ lps_type = BTC_LPS_ENABLE;
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_lps_notify(btcoexist, lps_type);
+}
+
+void exhalbtc_scan_notify(struct btc_coexist *btcoexist, u8 type)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ u8 scan_type;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+ btcoexist->statistics.cnt_scan_notify++;
+ if (btcoexist->manual_control)
+ return;
+
+ if (type)
+ scan_type = BTC_SCAN_START;
+ else
+ scan_type = BTC_SCAN_FINISH;
+
+ halbtc_leave_low_power();
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_scan_notify(btcoexist, scan_type);
+
+ halbtc_nomal_low_power();
+}
+
+void exhalbtc_connect_notify(struct btc_coexist *btcoexist, u8 action)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ u8 asso_type;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+ btcoexist->statistics.cnt_connect_notify++;
+ if (btcoexist->manual_control)
+ return;
+
+ if (action)
+ asso_type = BTC_ASSOCIATE_START;
+ else
+ asso_type = BTC_ASSOCIATE_FINISH;
+
+ halbtc_leave_low_power();
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_connect_notify(btcoexist, asso_type);
+}
+
+void exhalbtc_mediastatus_notify(struct btc_coexist *btcoexist,
+ enum _RT_MEDIA_STATUS media_status)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ u8 status;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+ btcoexist->statistics.cnt_media_status_notify++;
+ if (btcoexist->manual_control)
+ return;
+
+ if (RT_MEDIA_CONNECT == media_status)
+ status = BTC_MEDIA_CONNECT;
+ else
+ status = BTC_MEDIA_DISCONNECT;
+
+ halbtc_leave_low_power();
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ btc8723b_med_stat_notify(btcoexist, status);
+
+ halbtc_nomal_low_power();
+}
+
+void exhalbtc_special_packet_notify(struct btc_coexist *btcoexist, u8 pkt_type)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ u8 packet_type;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+ btcoexist->statistics.cnt_special_packet_notify++;
+ if (btcoexist->manual_control)
+ return;
+
+ packet_type = BTC_PACKET_DHCP;
+
+ halbtc_leave_low_power();
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_special_packet_notify(btcoexist,
+ packet_type);
+
+ halbtc_nomal_low_power();
+}
+
+void exhalbtc_bt_info_notify(struct btc_coexist *btcoexist,
+ u8 *tmp_buf, u8 length)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+ btcoexist->statistics.cnt_bt_info_notify++;
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_bt_info_notify(btcoexist, tmp_buf, length);
+}
+
+void exhalbtc_stack_operation_notify(struct btc_coexist *btcoexist, u8 type)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ u8 stack_op_type;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+ btcoexist->statistics.cnt_stack_operation_notify++;
+ if (btcoexist->manual_control)
+ return;
+
+ stack_op_type = BTC_STACK_OP_NONE;
+
+ halbtc_leave_low_power();
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_stack_operation_notify(btcoexist,
+ stack_op_type);
+
+ halbtc_nomal_low_power();
+}
+
+void exhalbtc_halt_notify(struct btc_coexist *btcoexist)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_halt_notify(btcoexist);
+}
+
+void exhalbtc_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state)
+{
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+}
+
+void exhalbtc_periodical(struct btc_coexist *btcoexist)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+ btcoexist->statistics.cnt_periodical++;
+
+ halbtc_leave_low_power();
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_periodical(btcoexist);
+
+ halbtc_nomal_low_power();
+}
+
+void exhalbtc_dbg_control(struct btc_coexist *btcoexist,
+ u8 code, u8 len, u8 *data)
+{
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+ btcoexist->statistics.cnt_dbg_ctrl++;
+}
+
+void exhalbtc_stack_update_profile_info(void)
+{
+}
+
+void exhalbtc_update_min_bt_rssi(char bt_rssi)
+{
+ struct btc_coexist *btcoexist = &gl_bt_coexist;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+
+ btcoexist->stack_info.min_bt_rssi = bt_rssi;
+}
+
+void exhalbtc_set_hci_version(u16 hci_version)
+{
+ struct btc_coexist *btcoexist = &gl_bt_coexist;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+
+ btcoexist->stack_info.hci_version = hci_version;
+}
+
+void exhalbtc_set_bt_patch_version(u16 bt_hci_version, u16 bt_patch_version)
+{
+ struct btc_coexist *btcoexist = &gl_bt_coexist;
+
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+
+ btcoexist->bt_info.bt_real_fw_ver = bt_patch_version;
+ btcoexist->bt_info.bt_hci_ver = bt_hci_version;
+}
+
+void exhalbtc_set_bt_exist(bool bt_exist)
+{
+ gl_bt_coexist.board_info.bt_exist = bt_exist;
+}
+
+void exhalbtc_set_chip_type(u8 chip_type)
+{
+ switch (chip_type) {
+ default:
+ case BT_2WIRE:
+ case BT_ISSC_3WIRE:
+ case BT_ACCEL:
+ case BT_RTL8756:
+ gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_UNDEF;
+ break;
+ case BT_CSR_BC4:
+ gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_CSR_BC4;
+ break;
+ case BT_CSR_BC8:
+ gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_CSR_BC8;
+ break;
+ case BT_RTL8723A:
+ gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_RTL8723A;
+ break;
+ case BT_RTL8821A:
+ gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_RTL8821;
+ break;
+ case BT_RTL8723B:
+ gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_RTL8723B;
+ break;
+ }
+}
+
+void exhalbtc_set_ant_num(u8 type, u8 ant_num)
+{
+ if (BT_COEX_ANT_TYPE_PG == type) {
+ gl_bt_coexist.board_info.pg_ant_num = ant_num;
+ gl_bt_coexist.board_info.btdm_ant_num = ant_num;
+ } else if (BT_COEX_ANT_TYPE_ANTDIV == type) {
+ gl_bt_coexist.board_info.btdm_ant_num = ant_num;
+ }
+}
+
+void exhalbtc_display_bt_coex_info(struct btc_coexist *btcoexist)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ if (!halbtc_is_bt_coexist_available(btcoexist))
+ return;
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE)
+ ex_halbtc8723b2ant_display_coex_info(btcoexist);
+}
diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h
new file mode 100644
index 000000000000..871fc3c6d559
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h
@@ -0,0 +1,559 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2012 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+#ifndef __HALBTC_OUT_SRC_H__
+#define __HALBTC_OUT_SRC_H__
+
+#include "../wifi.h"
+
+#define NORMAL_EXEC false
+#define FORCE_EXEC true
+
+#define BTC_RF_A RF90_PATH_A
+#define BTC_RF_B RF90_PATH_B
+#define BTC_RF_C RF90_PATH_C
+#define BTC_RF_D RF90_PATH_D
+
+#define BTC_SMSP SINGLEMAC_SINGLEPHY
+#define BTC_DMDP DUALMAC_DUALPHY
+#define BTC_DMSP DUALMAC_SINGLEPHY
+#define BTC_MP_UNKNOWN 0xff
+
+#define IN
+#define OUT
+
+#define BT_TMP_BUF_SIZE 100
+
+#define BT_COEX_ANT_TYPE_PG 0
+#define BT_COEX_ANT_TYPE_ANTDIV 1
+#define BT_COEX_ANT_TYPE_DETECTED 2
+
+#define BTC_MIMO_PS_STATIC 0
+#define BTC_MIMO_PS_DYNAMIC 1
+
+#define BTC_RATE_DISABLE 0
+#define BTC_RATE_ENABLE 1
+
+#define BTC_ANT_PATH_WIFI 0
+#define BTC_ANT_PATH_BT 1
+#define BTC_ANT_PATH_PTA 2
+
+enum btc_chip_interface {
+ BTC_INTF_UNKNOWN = 0,
+ BTC_INTF_PCI = 1,
+ BTC_INTF_USB = 2,
+ BTC_INTF_SDIO = 3,
+ BTC_INTF_GSPI = 4,
+ BTC_INTF_MAX
+};
+
+enum BTC_CHIP_TYPE {
+ BTC_CHIP_UNDEF = 0,
+ BTC_CHIP_CSR_BC4 = 1,
+ BTC_CHIP_CSR_BC8 = 2,
+ BTC_CHIP_RTL8723A = 3,
+ BTC_CHIP_RTL8821 = 4,
+ BTC_CHIP_RTL8723B = 5,
+ BTC_CHIP_MAX
+};
+
+enum BTC_MSG_TYPE {
+ BTC_MSG_INTERFACE = 0x0,
+ BTC_MSG_ALGORITHM = 0x1,
+ BTC_MSG_MAX
+};
+extern u32 btc_dbg_type[];
+
+/* following is for BTC_MSG_INTERFACE */
+#define INTF_INIT BIT0
+#define INTF_NOTIFY BIT2
+
+/* following is for BTC_ALGORITHM */
+#define ALGO_BT_RSSI_STATE BIT0
+#define ALGO_WIFI_RSSI_STATE BIT1
+#define ALGO_BT_MONITOR BIT2
+#define ALGO_TRACE BIT3
+#define ALGO_TRACE_FW BIT4
+#define ALGO_TRACE_FW_DETAIL BIT5
+#define ALGO_TRACE_FW_EXEC BIT6
+#define ALGO_TRACE_SW BIT7
+#define ALGO_TRACE_SW_DETAIL BIT8
+#define ALGO_TRACE_SW_EXEC BIT9
+
+#define BT_COEX_ANT_TYPE_PG 0
+#define BT_COEX_ANT_TYPE_ANTDIV 1
+#define BT_COEX_ANT_TYPE_DETECTED 2
+#define BTC_MIMO_PS_STATIC 0
+#define BTC_MIMO_PS_DYNAMIC 1
+#define BTC_RATE_DISABLE 0
+#define BTC_RATE_ENABLE 1
+#define BTC_ANT_PATH_WIFI 0
+#define BTC_ANT_PATH_BT 1
+#define BTC_ANT_PATH_PTA 2
+
+
+#define CL_SPRINTF snprintf
+#define CL_PRINTF printk
+
+#define BTC_PRINT(dbgtype, dbgflag, printstr, ...) \
+ do { \
+ if (unlikely(btc_dbg_type[dbgtype] & dbgflag)) {\
+ printk(printstr, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define BTC_PRINT_F(dbgtype, dbgflag, printstr, ...) \
+ do { \
+ if (unlikely(btc_dbg_type[dbgtype] & dbgflag)) {\
+ pr_info("%s: ", __func__); \
+ printk(printstr, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define BTC_PRINT_ADDR(dbgtype, dbgflag, printstr, _ptr) \
+ do { \
+ if (unlikely(btc_dbg_type[dbgtype] & dbgflag)) { \
+ int __i; \
+ u8 *__ptr = (u8 *)_ptr; \
+ printk printstr; \
+ for (__i = 0; __i < 6; __i++) \
+ printk("%02X%s", __ptr[__i], (__i == 5) ? \
+ "" : "-"); \
+ pr_info("\n"); \
+ } \
+ } while (0)
+
+#define BTC_PRINT_DATA(dbgtype, dbgflag, _titlestring, _hexdata, _hexdatalen) \
+ do { \
+ if (unlikely(btc_dbg_type[dbgtype] & dbgflag)) { \
+ int __i; \
+ u8 *__ptr = (u8 *)_hexdata; \
+ printk(_titlestring); \
+ for (__i = 0; __i < (int)_hexdatalen; __i++) { \
+ printk("%02X%s", __ptr[__i], (((__i + 1) % 4) \
+ == 0) ? " " : " ");\
+ if (((__i + 1) % 16) == 0) \
+ printk("\n"); \
+ } \
+ pr_debug("\n"); \
+ } \
+ } while (0)
+
+#define BTC_ANT_PATH_WIFI 0
+#define BTC_ANT_PATH_BT 1
+#define BTC_ANT_PATH_PTA 2
+
+enum btc_power_save_type {
+ BTC_PS_WIFI_NATIVE = 0,
+ BTC_PS_LPS_ON = 1,
+ BTC_PS_LPS_OFF = 2,
+ BTC_PS_LPS_MAX
+};
+
+struct btc_board_info {
+ /* The following is some board information */
+ u8 bt_chip_type;
+ u8 pg_ant_num; /* pg ant number */
+ u8 btdm_ant_num; /* ant number for btdm */
+ u8 btdm_ant_pos;
+ bool bt_exist;
+};
+
+enum btc_dbg_opcode {
+ BTC_DBG_SET_COEX_NORMAL = 0x0,
+ BTC_DBG_SET_COEX_WIFI_ONLY = 0x1,
+ BTC_DBG_SET_COEX_BT_ONLY = 0x2,
+ BTC_DBG_MAX
+};
+
+enum btc_rssi_state {
+ BTC_RSSI_STATE_HIGH = 0x0,
+ BTC_RSSI_STATE_MEDIUM = 0x1,
+ BTC_RSSI_STATE_LOW = 0x2,
+ BTC_RSSI_STATE_STAY_HIGH = 0x3,
+ BTC_RSSI_STATE_STAY_MEDIUM = 0x4,
+ BTC_RSSI_STATE_STAY_LOW = 0x5,
+ BTC_RSSI_MAX
+};
+
+enum btc_wifi_role {
+ BTC_ROLE_STATION = 0x0,
+ BTC_ROLE_AP = 0x1,
+ BTC_ROLE_IBSS = 0x2,
+ BTC_ROLE_HS_MODE = 0x3,
+ BTC_ROLE_MAX
+};
+
+enum btc_wifi_bw_mode {
+ BTC_WIFI_BW_LEGACY = 0x0,
+ BTC_WIFI_BW_HT20 = 0x1,
+ BTC_WIFI_BW_HT40 = 0x2,
+ BTC_WIFI_BW_MAX
+};
+
+enum btc_wifi_traffic_dir {
+ BTC_WIFI_TRAFFIC_TX = 0x0,
+ BTC_WIFI_TRAFFIC_RX = 0x1,
+ BTC_WIFI_TRAFFIC_MAX
+};
+
+enum btc_wifi_pnp {
+ BTC_WIFI_PNP_WAKE_UP = 0x0,
+ BTC_WIFI_PNP_SLEEP = 0x1,
+ BTC_WIFI_PNP_MAX
+};
+
+
+enum btc_get_type {
+ /* type bool */
+ BTC_GET_BL_HS_OPERATION,
+ BTC_GET_BL_HS_CONNECTING,
+ BTC_GET_BL_WIFI_CONNECTED,
+ BTC_GET_BL_WIFI_BUSY,
+ BTC_GET_BL_WIFI_SCAN,
+ BTC_GET_BL_WIFI_LINK,
+ BTC_GET_BL_WIFI_DHCP,
+ BTC_GET_BL_WIFI_SOFTAP_IDLE,
+ BTC_GET_BL_WIFI_SOFTAP_LINKING,
+ BTC_GET_BL_WIFI_IN_EARLY_SUSPEND,
+ BTC_GET_BL_WIFI_ROAM,
+ BTC_GET_BL_WIFI_4_WAY_PROGRESS,
+ BTC_GET_BL_WIFI_UNDER_5G,
+ BTC_GET_BL_WIFI_AP_MODE_ENABLE,
+ BTC_GET_BL_WIFI_ENABLE_ENCRYPTION,
+ BTC_GET_BL_WIFI_UNDER_B_MODE,
+ BTC_GET_BL_EXT_SWITCH,
+
+ /* type s4Byte */
+ BTC_GET_S4_WIFI_RSSI,
+ BTC_GET_S4_HS_RSSI,
+
+ /* type u32 */
+ BTC_GET_U4_WIFI_BW,
+ BTC_GET_U4_WIFI_TRAFFIC_DIRECTION,
+ BTC_GET_U4_WIFI_FW_VER,
+ BTC_GET_U4_BT_PATCH_VER,
+
+ /* type u1Byte */
+ BTC_GET_U1_WIFI_DOT11_CHNL,
+ BTC_GET_U1_WIFI_CENTRAL_CHNL,
+ BTC_GET_U1_WIFI_HS_CHNL,
+ BTC_GET_U1_MAC_PHY_MODE,
+
+ /* for 1Ant */
+ BTC_GET_U1_LPS_MODE,
+ BTC_GET_BL_BT_SCO_BUSY,
+
+ /* for test mode */
+ BTC_GET_DRIVER_TEST_CFG,
+ BTC_GET_MAX
+};
+
+
+enum btc_set_type {
+ /* type bool */
+ BTC_SET_BL_BT_DISABLE,
+ BTC_SET_BL_BT_TRAFFIC_BUSY,
+ BTC_SET_BL_BT_LIMITED_DIG,
+ BTC_SET_BL_FORCE_TO_ROAM,
+ BTC_SET_BL_TO_REJ_AP_AGG_PKT,
+ BTC_SET_BL_BT_CTRL_AGG_SIZE,
+ BTC_SET_BL_INC_SCAN_DEV_NUM,
+
+ /* type u1Byte */
+ BTC_SET_U1_RSSI_ADJ_VAL_FOR_AGC_TABLE_ON,
+ BTC_SET_U1_RSSI_ADJ_VAL_FOR_1ANT_COEX_TYPE,
+ BTC_SET_UI_SCAN_SIG_COMPENSATION,
+ BTC_SET_U1_AGG_BUF_SIZE,
+
+ /* type trigger some action */
+ BTC_SET_ACT_GET_BT_RSSI,
+ BTC_SET_ACT_AGGREGATE_CTRL,
+
+ /********* for 1Ant **********/
+ /* type bool */
+ BTC_SET_BL_BT_SCO_BUSY,
+ /* type u1Byte */
+ BTC_SET_U1_1ANT_LPS,
+ BTC_SET_U1_1ANT_RPWM,
+ /* type trigger some action */
+ BTC_SET_ACT_LEAVE_LPS,
+ BTC_SET_ACT_ENTER_LPS,
+ BTC_SET_ACT_NORMAL_LPS,
+ BTC_SET_ACT_INC_FORCE_EXEC_PWR_CMD_CNT,
+ BTC_SET_ACT_DISABLE_LOW_POWER,
+ BTC_SET_ACT_UPDATE_ra_mask,
+ BTC_SET_ACT_SEND_MIMO_PS,
+ /* BT Coex related */
+ BTC_SET_ACT_CTRL_BT_INFO,
+ BTC_SET_ACT_CTRL_BT_COEX,
+ /***************************/
+ BTC_SET_MAX
+};
+
+enum btc_dbg_disp_type {
+ BTC_DBG_DISP_COEX_STATISTICS = 0x0,
+ BTC_DBG_DISP_BT_LINK_INFO = 0x1,
+ BTC_DBG_DISP_BT_FW_VER = 0x2,
+ BTC_DBG_DISP_FW_PWR_MODE_CMD = 0x3,
+ BTC_DBG_DISP_MAX
+};
+
+enum btc_notify_type_ips {
+ BTC_IPS_LEAVE = 0x0,
+ BTC_IPS_ENTER = 0x1,
+ BTC_IPS_MAX
+};
+
+enum btc_notify_type_lps {
+ BTC_LPS_DISABLE = 0x0,
+ BTC_LPS_ENABLE = 0x1,
+ BTC_LPS_MAX
+};
+
+enum btc_notify_type_scan {
+ BTC_SCAN_FINISH = 0x0,
+ BTC_SCAN_START = 0x1,
+ BTC_SCAN_MAX
+};
+
+enum btc_notify_type_associate {
+ BTC_ASSOCIATE_FINISH = 0x0,
+ BTC_ASSOCIATE_START = 0x1,
+ BTC_ASSOCIATE_MAX
+};
+
+enum btc_notify_type_media_status {
+ BTC_MEDIA_DISCONNECT = 0x0,
+ BTC_MEDIA_CONNECT = 0x1,
+ BTC_MEDIA_MAX
+};
+
+enum btc_notify_type_special_packet {
+ BTC_PACKET_UNKNOWN = 0x0,
+ BTC_PACKET_DHCP = 0x1,
+ BTC_PACKET_ARP = 0x2,
+ BTC_PACKET_EAPOL = 0x3,
+ BTC_PACKET_MAX
+};
+
+enum btc_notify_type_stack_operation {
+ BTC_STACK_OP_NONE = 0x0,
+ BTC_STACK_OP_INQ_PAGE_PAIR_START = 0x1,
+ BTC_STACK_OP_INQ_PAGE_PAIR_FINISH = 0x2,
+ BTC_STACK_OP_MAX
+};
+
+
+typedef u8 (*bfp_btc_r1)(void *btc_context, u32 reg_addr);
+
+typedef u16 (*bfp_btc_r2)(void *btc_context, u32 reg_addr);
+
+typedef u32 (*bfp_btc_r4)(void *btc_context, u32 reg_addr);
+
+typedef void (*bfp_btc_w1)(void *btc_context, u32 reg_addr, u8 data);
+
+typedef void (*bfp_btc_w1_bit_mak)(void *btc_context, u32 reg_addr,
+ u32 bit_mask, u8 data1b);
+
+typedef void (*bfp_btc_w2)(void *btc_context, u32 reg_addr, u16 data);
+
+typedef void (*bfp_btc_w4)(void *btc_context, u32 reg_addr, u32 data);
+
+typedef void (*bfp_btc_wr_1byte_bit_mask)(void *btc_context, u32 reg_addr,
+ u8 bit_mask, u8 data);
+
+typedef void (*bfp_btc_set_bb_reg)(void *btc_context, u32 reg_addr,
+ u32 bit_mask, u32 data);
+
+typedef u32 (*bfp_btc_get_bb_reg)(void *btc_context, u32 reg_addr,
+ u32 bit_mask);
+
+typedef void (*bfp_btc_set_rf_reg)(void *btc_context, u8 rf_path, u32 reg_addr,
+ u32 bit_mask, u32 data);
+
+typedef u32 (*bfp_btc_get_rf_reg)(void *btc_context, u8 rf_path,
+ u32 reg_addr, u32 bit_mask);
+
+typedef void (*bfp_btc_fill_h2c)(void *btc_context, u8 element_id,
+ u32 cmd_len, u8 *cmd_buffer);
+
+typedef bool (*bfp_btc_get)(void *btcoexist, u8 get_type, void *out_buf);
+
+typedef bool (*bfp_btc_set)(void *btcoexist, u8 set_type, void *in_buf);
+
+typedef void (*bfp_btc_disp_dbg_msg)(void *btcoexist, u8 disp_type);
+
+struct btc_bt_info {
+ bool bt_disabled;
+ u8 rssi_adjust_for_agc_table_on;
+ u8 rssi_adjust_for_1ant_coex_type;
+ bool bt_busy;
+ u8 agg_buf_size;
+ bool limited_dig;
+ bool reject_agg_pkt;
+ bool b_bt_ctrl_buf_size;
+ bool increase_scan_dev_num;
+ u16 bt_hci_ver;
+ u16 bt_real_fw_ver;
+ u8 bt_fw_ver;
+
+ /* the following is for 1Ant solution */
+ bool bt_ctrl_lps;
+ bool bt_pwr_save_mode;
+ bool bt_lps_on;
+ bool force_to_roam;
+ u8 force_exec_pwr_cmd_cnt;
+ u8 lps_1ant;
+ u8 rpwm_1ant;
+ u32 ra_mask;
+};
+
+struct btc_stack_info {
+ bool profile_notified;
+ u16 hci_version; /* stack hci version */
+ u8 num_of_link;
+ bool bt_link_exist;
+ bool sco_exist;
+ bool acl_exist;
+ bool a2dp_exist;
+ bool hid_exist;
+ u8 num_of_hid;
+ bool pan_exist;
+ bool unknown_acl_exist;
+ char min_bt_rssi;
+};
+
+struct btc_statistics {
+ u32 cnt_bind;
+ u32 cnt_init_hw_config;
+ u32 cnt_init_coex_dm;
+ u32 cnt_ips_notify;
+ u32 cnt_lps_notify;
+ u32 cnt_scan_notify;
+ u32 cnt_connect_notify;
+ u32 cnt_media_status_notify;
+ u32 cnt_special_packet_notify;
+ u32 cnt_bt_info_notify;
+ u32 cnt_periodical;
+ u32 cnt_stack_operation_notify;
+ u32 cnt_dbg_ctrl;
+};
+
+struct btc_bt_link_info {
+ bool bt_link_exist;
+ bool sco_exist;
+ bool sco_only;
+ bool a2dp_exist;
+ bool a2dp_only;
+ bool hid_exist;
+ bool hid_only;
+ bool pan_exist;
+ bool pan_only;
+};
+
+enum btc_antenna_pos {
+ BTC_ANTENNA_AT_MAIN_PORT = 0x1,
+ BTC_ANTENNA_AT_AUX_PORT = 0x2,
+};
+
+struct btc_coexist {
+ /* make sure only one adapter can bind the data context */
+ bool binded;
+ /* default adapter */
+ void *adapter;
+ struct btc_board_info board_info;
+ /* some bt info referenced by non-bt module */
+ struct btc_bt_info bt_info;
+ struct btc_stack_info stack_info;
+ enum btc_chip_interface chip_interface;
+ struct btc_bt_link_info bt_link_info;
+
+ bool initilized;
+ bool stop_coex_dm;
+ bool manual_control;
+ u8 *cli_buf;
+ struct btc_statistics statistics;
+ u8 pwr_mode_val[10];
+
+ /* function pointers - io related */
+ bfp_btc_r1 btc_read_1byte;
+ bfp_btc_w1 btc_write_1byte;
+ bfp_btc_w1_bit_mak btc_write_1byte_bitmask;
+ bfp_btc_r2 btc_read_2byte;
+ bfp_btc_w2 btc_write_2byte;
+ bfp_btc_r4 btc_read_4byte;
+ bfp_btc_w4 btc_write_4byte;
+
+ bfp_btc_set_bb_reg btc_set_bb_reg;
+ bfp_btc_get_bb_reg btc_get_bb_reg;
+
+
+ bfp_btc_set_rf_reg btc_set_rf_reg;
+ bfp_btc_get_rf_reg btc_get_rf_reg;
+
+ bfp_btc_fill_h2c btc_fill_h2c;
+
+ bfp_btc_disp_dbg_msg btc_disp_dbg_msg;
+
+ bfp_btc_get btc_get;
+ bfp_btc_set btc_set;
+};
+
+bool halbtc_is_wifi_uplink(struct rtl_priv *adapter);
+
+extern struct btc_coexist gl_bt_coexist;
+
+bool exhalbtc_initlize_variables(struct rtl_priv *adapter);
+void exhalbtc_init_hw_config(struct btc_coexist *btcoexist);
+void exhalbtc_init_coex_dm(struct btc_coexist *btcoexist);
+void exhalbtc_ips_notify(struct btc_coexist *btcoexist, u8 type);
+void exhalbtc_lps_notify(struct btc_coexist *btcoexist, u8 type);
+void exhalbtc_scan_notify(struct btc_coexist *btcoexist, u8 type);
+void exhalbtc_connect_notify(struct btc_coexist *btcoexist, u8 action);
+void exhalbtc_mediastatus_notify(struct btc_coexist *btcoexist,
+ enum _RT_MEDIA_STATUS media_status);
+void exhalbtc_special_packet_notify(struct btc_coexist *btcoexist, u8 pkt_type);
+void exhalbtc_bt_info_notify(struct btc_coexist *btcoexist, u8 *tmp_buf,
+ u8 length);
+void exhalbtc_stack_operation_notify(struct btc_coexist *btcoexist, u8 type);
+void exhalbtc_halt_notify(struct btc_coexist *btcoexist);
+void exhalbtc_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state);
+void exhalbtc_periodical(struct btc_coexist *btcoexist);
+void exhalbtc_dbg_control(struct btc_coexist *btcoexist, u8 code, u8 len,
+ u8 *data);
+void exhalbtc_stack_update_profile_info(void);
+void exhalbtc_set_hci_version(u16 hci_version);
+void exhalbtc_set_bt_patch_version(u16 bt_hci_version, u16 bt_patch_version);
+void exhalbtc_update_min_bt_rssi(char bt_rssi);
+void exhalbtc_set_bt_exist(bool bt_exist);
+void exhalbtc_set_chip_type(u8 chip_type);
+void exhalbtc_set_ant_num(u8 type, u8 ant_num);
+void exhalbtc_display_bt_coex_info(struct btc_coexist *btcoexist);
+void exhalbtc_signal_compensation(struct btc_coexist *btcoexist,
+ u8 *rssi_wifi, u8 *rssi_bt);
+void exhalbtc_lps_leave(struct btc_coexist *btcoexist);
+void exhalbtc_low_wifi_traffic_notify(struct btc_coexist *btcoexist);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.c b/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.c
new file mode 100644
index 000000000000..0ab94fe4cbbe
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.c
@@ -0,0 +1,218 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "rtl_btc.h"
+#include "halbt_precomp.h"
+
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+
+static struct rtl_btc_ops rtl_btc_operation = {
+ .btc_init_variables = rtl_btc_init_variables,
+ .btc_init_hal_vars = rtl_btc_init_hal_vars,
+ .btc_init_hw_config = rtl_btc_init_hw_config,
+ .btc_ips_notify = rtl_btc_ips_notify,
+ .btc_scan_notify = rtl_btc_scan_notify,
+ .btc_connect_notify = rtl_btc_connect_notify,
+ .btc_mediastatus_notify = rtl_btc_mediastatus_notify,
+ .btc_periodical = rtl_btc_periodical,
+ .btc_halt_notify = rtl_btc_halt_notify,
+ .btc_btinfo_notify = rtl_btc_btinfo_notify,
+ .btc_is_limited_dig = rtl_btc_is_limited_dig,
+ .btc_is_disable_edca_turbo = rtl_btc_is_disable_edca_turbo,
+ .btc_is_bt_disabled = rtl_btc_is_bt_disabled,
+};
+
+void rtl_btc_init_variables(struct rtl_priv *rtlpriv)
+{
+ exhalbtc_initlize_variables(rtlpriv);
+}
+
+void rtl_btc_init_hal_vars(struct rtl_priv *rtlpriv)
+{
+ u8 ant_num;
+ u8 bt_exist;
+ u8 bt_type;
+
+ ant_num = rtl_get_hwpg_ant_num(rtlpriv);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+ "%s, antNum is %d\n", __func__, ant_num);
+
+ bt_exist = rtl_get_hwpg_bt_exist(rtlpriv);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+ "%s, bt_exist is %d\n", __func__, bt_exist);
+ exhalbtc_set_bt_exist(bt_exist);
+
+ bt_type = rtl_get_hwpg_bt_type(rtlpriv);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "%s, bt_type is %d\n",
+ __func__, bt_type);
+ exhalbtc_set_chip_type(bt_type);
+
+ exhalbtc_set_ant_num(BT_COEX_ANT_TYPE_PG, ant_num);
+}
+
+void rtl_btc_init_hw_config(struct rtl_priv *rtlpriv)
+{
+ exhalbtc_init_hw_config(&gl_bt_coexist);
+ exhalbtc_init_coex_dm(&gl_bt_coexist);
+}
+
+void rtl_btc_ips_notify(struct rtl_priv *rtlpriv, u8 type)
+{
+ exhalbtc_ips_notify(&gl_bt_coexist, type);
+}
+
+void rtl_btc_scan_notify(struct rtl_priv *rtlpriv, u8 scantype)
+{
+ exhalbtc_scan_notify(&gl_bt_coexist, scantype);
+}
+
+void rtl_btc_connect_notify(struct rtl_priv *rtlpriv, u8 action)
+{
+ exhalbtc_connect_notify(&gl_bt_coexist, action);
+}
+
+void rtl_btc_mediastatus_notify(struct rtl_priv *rtlpriv,
+ enum _RT_MEDIA_STATUS mstatus)
+{
+ exhalbtc_mediastatus_notify(&gl_bt_coexist, mstatus);
+}
+
+void rtl_btc_periodical(struct rtl_priv *rtlpriv)
+{
+ exhalbtc_periodical(&gl_bt_coexist);
+}
+
+void rtl_btc_halt_notify(void)
+{
+ exhalbtc_halt_notify(&gl_bt_coexist);
+}
+
+void rtl_btc_btinfo_notify(struct rtl_priv *rtlpriv, u8 *tmp_buf, u8 length)
+{
+ exhalbtc_bt_info_notify(&gl_bt_coexist, tmp_buf, length);
+}
+
+bool rtl_btc_is_limited_dig(struct rtl_priv *rtlpriv)
+{
+ return gl_bt_coexist.bt_info.limited_dig;
+}
+
+bool rtl_btc_is_disable_edca_turbo(struct rtl_priv *rtlpriv)
+{
+ bool bt_change_edca = false;
+ u32 cur_edca_val;
+ u32 edca_bt_hs_uplink = 0x5ea42b, edca_bt_hs_downlink = 0x5ea42b;
+ u32 edca_hs;
+ u32 edca_addr = 0x504;
+
+ cur_edca_val = rtl_read_dword(rtlpriv, edca_addr);
+ if (halbtc_is_wifi_uplink(rtlpriv)) {
+ if (cur_edca_val != edca_bt_hs_uplink) {
+ edca_hs = edca_bt_hs_uplink;
+ bt_change_edca = true;
+ }
+ } else {
+ if (cur_edca_val != edca_bt_hs_downlink) {
+ edca_hs = edca_bt_hs_downlink;
+ bt_change_edca = true;
+ }
+ }
+
+ if (bt_change_edca)
+ rtl_write_dword(rtlpriv, edca_addr, edca_hs);
+
+ return true;
+}
+
+bool rtl_btc_is_bt_disabled(struct rtl_priv *rtlpriv)
+{
+ if (gl_bt_coexist.bt_info.bt_disabled)
+ return true;
+ else
+ return false;
+}
+
+struct rtl_btc_ops *rtl_btc_get_ops_pointer(void)
+{
+ return &rtl_btc_operation;
+}
+EXPORT_SYMBOL(rtl_btc_get_ops_pointer);
+
+u8 rtl_get_hwpg_ant_num(struct rtl_priv *rtlpriv)
+{
+ u8 num;
+
+ if (rtlpriv->btcoexist.btc_info.ant_num == ANT_X2)
+ num = 2;
+ else
+ num = 1;
+
+ return num;
+}
+
+enum _RT_MEDIA_STATUS mgnt_link_status_query(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ enum _RT_MEDIA_STATUS m_status = RT_MEDIA_DISCONNECT;
+
+ u8 bibss = (mac->opmode == NL80211_IFTYPE_ADHOC) ? 1 : 0;
+
+ if (bibss || rtlpriv->mac80211.link_state >= MAC80211_LINKED)
+ m_status = RT_MEDIA_CONNECT;
+
+ return m_status;
+}
+
+u8 rtl_get_hwpg_bt_exist(struct rtl_priv *rtlpriv)
+{
+ return rtlpriv->btcoexist.btc_info.btcoexist;
+}
+
+u8 rtl_get_hwpg_bt_type(struct rtl_priv *rtlpriv)
+{
+ return rtlpriv->btcoexist.btc_info.bt_type;
+}
+
+MODULE_AUTHOR("Page He <page_he@realsil.com.cn>");
+MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>");
+MODULE_AUTHOR("Larry Finger <Larry.FInger@lwfinger.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek 802.11n PCI wireless core");
+
+static int __init rtl_btcoexist_module_init(void)
+{
+ return 0;
+}
+
+static void __exit rtl_btcoexist_module_exit(void)
+{
+ return;
+}
+
+module_init(rtl_btcoexist_module_init);
+module_exit(rtl_btcoexist_module_exit);
diff --git a/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.h b/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.h
new file mode 100644
index 000000000000..805b22cc8fc8
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.h
@@ -0,0 +1,52 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2010 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL_BTC_H__
+#define __RTL_BTC_H__
+
+#include "halbt_precomp.h"
+
+void rtl_btc_init_variables(struct rtl_priv *rtlpriv);
+void rtl_btc_init_hal_vars(struct rtl_priv *rtlpriv);
+void rtl_btc_init_hw_config(struct rtl_priv *rtlpriv);
+void rtl_btc_ips_notify(struct rtl_priv *rtlpriv, u8 type);
+void rtl_btc_scan_notify(struct rtl_priv *rtlpriv, u8 scantype);
+void rtl_btc_connect_notify(struct rtl_priv *rtlpriv, u8 action);
+void rtl_btc_mediastatus_notify(struct rtl_priv *rtlpriv,
+ enum _RT_MEDIA_STATUS mstatus);
+void rtl_btc_periodical(struct rtl_priv *rtlpriv);
+void rtl_btc_halt_notify(void);
+void rtl_btc_btinfo_notify(struct rtl_priv *rtlpriv, u8 *tmpbuf, u8 length);
+bool rtl_btc_is_limited_dig(struct rtl_priv *rtlpriv);
+bool rtl_btc_is_disable_edca_turbo(struct rtl_priv *rtlpriv);
+bool rtl_btc_is_bt_disabled(struct rtl_priv *rtlpriv);
+
+struct rtl_btc_ops *rtl_btc_get_ops_pointer(void);
+
+u8 rtl_get_hwpg_ant_num(struct rtl_priv *rtlpriv);
+u8 rtl_get_hwpg_bt_exist(struct rtl_priv *rtlpriv);
+u8 rtl_get_hwpg_bt_type(struct rtl_priv *rtlpriv);
+enum _RT_MEDIA_STATUS mgnt_link_status_query(struct ieee80211_hw *hw);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c
index 2d337a0c3df0..4ec424f26672 100644
--- a/drivers/net/wireless/rtlwifi/core.c
+++ b/drivers/net/wireless/rtlwifi/core.c
@@ -36,6 +36,66 @@
#include <linux/export.h>
+void rtl_addr_delay(u32 addr)
+{
+ if (addr == 0xfe)
+ mdelay(50);
+ else if (addr == 0xfd)
+ mdelay(5);
+ else if (addr == 0xfc)
+ mdelay(1);
+ else if (addr == 0xfb)
+ udelay(50);
+ else if (addr == 0xfa)
+ udelay(5);
+ else if (addr == 0xf9)
+ udelay(1);
+}
+EXPORT_SYMBOL(rtl_addr_delay);
+
+void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr,
+ u32 mask, u32 data)
+{
+ if (addr == 0xfe) {
+ mdelay(50);
+ } else if (addr == 0xfd) {
+ mdelay(5);
+ } else if (addr == 0xfc) {
+ mdelay(1);
+ } else if (addr == 0xfb) {
+ udelay(50);
+ } else if (addr == 0xfa) {
+ udelay(5);
+ } else if (addr == 0xf9) {
+ udelay(1);
+ } else {
+ rtl_set_rfreg(hw, rfpath, addr, mask, data);
+ udelay(1);
+ }
+}
+EXPORT_SYMBOL(rtl_rfreg_delay);
+
+void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data)
+{
+ if (addr == 0xfe) {
+ mdelay(50);
+ } else if (addr == 0xfd) {
+ mdelay(5);
+ } else if (addr == 0xfc) {
+ mdelay(1);
+ } else if (addr == 0xfb) {
+ udelay(50);
+ } else if (addr == 0xfa) {
+ udelay(5);
+ } else if (addr == 0xf9) {
+ udelay(1);
+ } else {
+ rtl_set_bbreg(hw, addr, MASKDWORD, data);
+ udelay(1);
+ }
+}
+EXPORT_SYMBOL(rtl_bb_delay);
+
void rtl_fw_cb(const struct firmware *firmware, void *context)
{
struct ieee80211_hw *hw = context;
@@ -475,20 +535,40 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw,
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ u32 rx_conf;
*new_flags &= RTL_SUPPORTED_FILTERS;
if (!changed_flags)
return;
+ /* if ssid not set to hw don't check bssid
+ * here just used for linked scanning, & linked
+ * and nolink check bssid is set in set network_type */
+ if ((changed_flags & FIF_BCN_PRBRESP_PROMISC) &&
+ (mac->link_state >= MAC80211_LINKED)) {
+ if (mac->opmode != NL80211_IFTYPE_AP &&
+ mac->opmode != NL80211_IFTYPE_MESH_POINT) {
+ if (*new_flags & FIF_BCN_PRBRESP_PROMISC) {
+ rtlpriv->cfg->ops->set_chk_bssid(hw, false);
+ } else {
+ rtlpriv->cfg->ops->set_chk_bssid(hw, true);
+ }
+ }
+ }
+
+ /* must be called after set_chk_bssid since that function modifies the
+ * RCR register too. */
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(&rx_conf));
+
/*TODO: we disable broadcase now, so enable here */
if (changed_flags & FIF_ALLMULTI) {
if (*new_flags & FIF_ALLMULTI) {
- mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AM] |
+ rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AM] |
rtlpriv->cfg->maps[MAC_RCR_AB];
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Enable receive multicast frame\n");
} else {
- mac->rx_conf &= ~(rtlpriv->cfg->maps[MAC_RCR_AM] |
+ rx_conf &= ~(rtlpriv->cfg->maps[MAC_RCR_AM] |
rtlpriv->cfg->maps[MAC_RCR_AB]);
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Disable receive multicast frame\n");
@@ -497,39 +577,25 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw,
if (changed_flags & FIF_FCSFAIL) {
if (*new_flags & FIF_FCSFAIL) {
- mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACRC32];
+ rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACRC32];
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Enable receive FCS error frame\n");
} else {
- mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACRC32];
+ rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACRC32];
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Disable receive FCS error frame\n");
}
}
- /* if ssid not set to hw don't check bssid
- * here just used for linked scanning, & linked
- * and nolink check bssid is set in set network_type */
- if ((changed_flags & FIF_BCN_PRBRESP_PROMISC) &&
- (mac->link_state >= MAC80211_LINKED)) {
- if (mac->opmode != NL80211_IFTYPE_AP &&
- mac->opmode != NL80211_IFTYPE_MESH_POINT) {
- if (*new_flags & FIF_BCN_PRBRESP_PROMISC) {
- rtlpriv->cfg->ops->set_chk_bssid(hw, false);
- } else {
- rtlpriv->cfg->ops->set_chk_bssid(hw, true);
- }
- }
- }
if (changed_flags & FIF_CONTROL) {
if (*new_flags & FIF_CONTROL) {
- mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACF];
+ rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACF];
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Enable receive control frame\n");
} else {
- mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACF];
+ rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACF];
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Disable receive control frame\n");
}
@@ -537,15 +603,17 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw,
if (changed_flags & FIF_OTHER_BSS) {
if (*new_flags & FIF_OTHER_BSS) {
- mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AAP];
+ rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AAP];
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Enable receive other BSS's frame\n");
} else {
- mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_AAP];
+ rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_AAP];
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Disable receive other BSS's frame\n");
}
}
+
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(&rx_conf));
}
static int rtl_op_sta_add(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -738,6 +806,11 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
rtlpriv->cfg->ops->linked_set_reg(hw);
rcu_read_lock();
sta = ieee80211_find_sta(vif, (u8 *)bss_conf->bssid);
+ if (!sta) {
+ pr_err("ieee80211_find_sta returned NULL\n");
+ rcu_read_unlock();
+ goto out;
+ }
if (vif->type == NL80211_IFTYPE_STATION && sta)
rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0);
@@ -892,7 +965,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
mac->basic_rates = basic_rates;
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE,
- (u8 *) (&basic_rates));
+ (u8 *)(&basic_rates));
}
rcu_read_unlock();
}
@@ -906,6 +979,11 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
if (bss_conf->assoc) {
if (ppsc->fwctrl_lps) {
u8 mstatus = RT_MEDIA_CONNECT;
+ u8 keep_alive = 10;
+ rtlpriv->cfg->ops->set_hw_reg(hw,
+ HW_VAR_KEEP_ALIVE,
+ &keep_alive);
+
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_H2C_FW_JOINBSSRPT,
&mstatus);
diff --git a/drivers/net/wireless/rtlwifi/core.h b/drivers/net/wireless/rtlwifi/core.h
index 2fe46a1b4f1f..027e75374dcc 100644
--- a/drivers/net/wireless/rtlwifi/core.h
+++ b/drivers/net/wireless/rtlwifi/core.h
@@ -41,5 +41,9 @@
extern const struct ieee80211_ops rtl_ops;
void rtl_fw_cb(const struct firmware *firmware, void *context);
+void rtl_addr_delay(u32 addr);
+void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr,
+ u32 mask, u32 data);
+void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data);
#endif
diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c
index d7aa165fe677..dae55257f0e8 100644
--- a/drivers/net/wireless/rtlwifi/pci.c
+++ b/drivers/net/wireless/rtlwifi/pci.c
@@ -811,19 +811,19 @@ done:
if (pci_dma_mapping_error(rtlpci->pdev, bufferaddress))
return;
tmp_one = 1;
- rtlpriv->cfg->ops->set_desc((u8 *) pdesc, false,
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, false,
HW_DESC_RXBUFF_ADDR,
(u8 *)&bufferaddress);
- rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false,
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, false,
HW_DESC_RXPKT_LEN,
(u8 *)&rtlpci->rxbuffersize);
if (index == rtlpci->rxringcount - 1)
- rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false,
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, false,
HW_DESC_RXERO,
&tmp_one);
- rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false, HW_DESC_RXOWN,
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, false, HW_DESC_RXOWN,
&tmp_one);
index = (index + 1) % rtlpci->rxringcount;
@@ -983,6 +983,8 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw)
struct sk_buff *pskb = NULL;
struct rtl_tx_desc *pdesc = NULL;
struct rtl_tcb_desc tcb_desc;
+ /*This is for new trx flow*/
+ struct rtl_tx_buffer_desc *pbuffer_desc = NULL;
u8 temp_one = 1;
memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc));
@@ -1004,11 +1006,12 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw)
info = IEEE80211_SKB_CB(pskb);
pdesc = &ring->desc[0];
rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *) pdesc,
- info, NULL, pskb, BEACON_QUEUE, &tcb_desc);
+ (u8 *)pbuffer_desc, info, NULL, pskb,
+ BEACON_QUEUE, &tcb_desc);
__skb_queue_tail(&ring->queue, pskb);
- rtlpriv->cfg->ops->set_desc((u8 *) pdesc, true, HW_DESC_OWN,
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN,
&temp_one);
return;
@@ -1066,7 +1069,7 @@ static void _rtl_pci_init_struct(struct ieee80211_hw *hw,
mac->current_ampdu_factor = 3;
/*QOS*/
- rtlpci->acm_method = eAcmWay2_SW;
+ rtlpci->acm_method = EACMWAY2_SW;
/*task */
tasklet_init(&rtlpriv->works.irq_tasklet,
@@ -1113,7 +1116,7 @@ static int _rtl_pci_init_tx_ring(struct ieee80211_hw *hw,
((i + 1) % entries) *
sizeof(*ring);
- rtlpriv->cfg->ops->set_desc((u8 *)&(ring[i]),
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)&(ring[i]),
true, HW_DESC_TX_NEXTDESC_ADDR,
(u8 *)&nextdescaddress);
}
@@ -1188,19 +1191,19 @@ static int _rtl_pci_init_rx_ring(struct ieee80211_hw *hw)
dev_kfree_skb_any(skb);
return 1;
}
- rtlpriv->cfg->ops->set_desc((u8 *)entry, false,
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false,
HW_DESC_RXBUFF_ADDR,
(u8 *)&bufferaddress);
- rtlpriv->cfg->ops->set_desc((u8 *)entry, false,
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false,
HW_DESC_RXPKT_LEN,
(u8 *)&rtlpci->
rxbuffersize);
- rtlpriv->cfg->ops->set_desc((u8 *) entry, false,
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false,
HW_DESC_RXOWN,
&tmp_one);
}
- rtlpriv->cfg->ops->set_desc((u8 *) entry, false,
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false,
HW_DESC_RXERO, &tmp_one);
}
return 0;
@@ -1331,7 +1334,7 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw)
for (i = 0; i < rtlpci->rxringcount; i++) {
entry = &rtlpci->rx_ring[rx_queue_idx].desc[i];
- rtlpriv->cfg->ops->set_desc((u8 *) entry,
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry,
false,
HW_DESC_RXOWN,
&tmp_one);
@@ -1424,6 +1427,7 @@ static int rtl_pci_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct rtl8192_tx_ring *ring;
struct rtl_tx_desc *pdesc;
+ struct rtl_tx_buffer_desc *ptx_bd_desc = NULL;
u8 idx;
u8 hw_queue = _rtl_mac_to_hwqueue(hw, skb);
unsigned long flags;
@@ -1464,17 +1468,22 @@ static int rtl_pci_tx(struct ieee80211_hw *hw,
idx = 0;
pdesc = &ring->desc[idx];
- own = (u8) rtlpriv->cfg->ops->get_desc((u8 *) pdesc,
- true, HW_DESC_OWN);
+ if (rtlpriv->use_new_trx_flow) {
+ ptx_bd_desc = &ring->buffer_desc[idx];
+ } else {
+ own = (u8) rtlpriv->cfg->ops->get_desc((u8 *)pdesc,
+ true, HW_DESC_OWN);
- if ((own == 1) && (hw_queue != BEACON_QUEUE)) {
- RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
- "No more TX desc@%d, ring->idx = %d, idx = %d, skb_queue_len = 0x%d\n",
- hw_queue, ring->idx, idx,
- skb_queue_len(&ring->queue));
+ if ((own == 1) && (hw_queue != BEACON_QUEUE)) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ "No more TX desc@%d, ring->idx = %d, idx = %d, skb_queue_len = 0x%d\n",
+ hw_queue, ring->idx, idx,
+ skb_queue_len(&ring->queue));
- spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags);
- return skb->len;
+ spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock,
+ flags);
+ return skb->len;
+ }
}
if (ieee80211_is_data_qos(fc)) {
@@ -1494,17 +1503,20 @@ static int rtl_pci_tx(struct ieee80211_hw *hw,
rtlpriv->cfg->ops->led_control(hw, LED_CTL_TX);
rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc,
- info, sta, skb, hw_queue, ptcb_desc);
+ (u8 *)ptx_bd_desc, info, sta, skb, hw_queue, ptcb_desc);
__skb_queue_tail(&ring->queue, skb);
- rtlpriv->cfg->ops->set_desc((u8 *)pdesc, true,
- HW_DESC_OWN, &temp_one);
-
+ if (rtlpriv->use_new_trx_flow) {
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true,
+ HW_DESC_OWN, &hw_queue);
+ } else {
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true,
+ HW_DESC_OWN, &temp_one);
+ }
if ((ring->entries - skb_queue_len(&ring->queue)) < 2 &&
hw_queue != BEACON_QUEUE) {
-
RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
"less desc left, stop skb_queue@%d, ring->idx = %d, idx = %d, skb_queue_len = 0x%d\n",
hw_queue, ring->idx, idx,
@@ -1841,6 +1853,65 @@ static bool _rtl_pci_find_adapter(struct pci_dev *pdev,
return true;
}
+static int rtl_pci_intr_mode_msi(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
+ int ret;
+
+ ret = pci_enable_msi(rtlpci->pdev);
+ if (ret < 0)
+ return ret;
+
+ ret = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, hw);
+ if (ret < 0) {
+ pci_disable_msi(rtlpci->pdev);
+ return ret;
+ }
+
+ rtlpci->using_msi = true;
+
+ RT_TRACE(rtlpriv, COMP_INIT|COMP_INTR, DBG_DMESG,
+ "MSI Interrupt Mode!\n");
+ return 0;
+}
+
+static int rtl_pci_intr_mode_legacy(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
+ int ret;
+
+ ret = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, hw);
+ if (ret < 0)
+ return ret;
+
+ rtlpci->using_msi = false;
+ RT_TRACE(rtlpriv, COMP_INIT|COMP_INTR, DBG_DMESG,
+ "Pin-based Interrupt Mode!\n");
+ return 0;
+}
+
+static int rtl_pci_intr_mode_decide(struct ieee80211_hw *hw)
+{
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
+ int ret;
+
+ if (rtlpci->msi_support) {
+ ret = rtl_pci_intr_mode_msi(hw);
+ if (ret < 0)
+ ret = rtl_pci_intr_mode_legacy(hw);
+ } else {
+ ret = rtl_pci_intr_mode_legacy(hw);
+ }
+ return ret;
+}
+
int rtl_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -1983,8 +2054,7 @@ int rtl_pci_probe(struct pci_dev *pdev,
}
rtlpci = rtl_pcidev(pcipriv);
- err = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt,
- IRQF_SHARED, KBUILD_MODNAME, hw);
+ err = rtl_pci_intr_mode_decide(hw);
if (err) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
"%s: failed to register IRQ handler\n",
@@ -2052,6 +2122,9 @@ void rtl_pci_disconnect(struct pci_dev *pdev)
rtlpci->irq_alloc = 0;
}
+ if (rtlpci->using_msi)
+ pci_disable_msi(rtlpci->pdev);
+
list_del(&rtlpriv->list);
if (rtlpriv->io.pci_mem_start != 0) {
pci_iounmap(pdev, (void __iomem *)rtlpriv->io.pci_mem_start);
diff --git a/drivers/net/wireless/rtlwifi/pci.h b/drivers/net/wireless/rtlwifi/pci.h
index d3262ec45d23..90174a814a6d 100644
--- a/drivers/net/wireless/rtlwifi/pci.h
+++ b/drivers/net/wireless/rtlwifi/pci.h
@@ -137,12 +137,22 @@ struct rtl_tx_cmd_desc {
u32 dword[16];
} __packed;
+/* In new TRX flow, Buffer_desc is new concept
+ * But TX wifi info == TX descriptor in old flow
+ * RX wifi info == RX descriptor in old flow
+ */
+struct rtl_tx_buffer_desc {
+ u32 dword[8]; /*seg = 4*/
+} __packed;
+
struct rtl8192_tx_ring {
struct rtl_tx_desc *desc;
dma_addr_t dma;
unsigned int idx;
unsigned int entries;
struct sk_buff_head queue;
+ /*add for new trx flow*/
+ struct rtl_tx_buffer_desc *buffer_desc; /*tx buffer descriptor*/
};
struct rtl8192_rx_ring {
@@ -199,6 +209,10 @@ struct rtl_pci {
u16 shortretry_limit;
u16 longretry_limit;
+
+ /* MSI support */
+ bool msi_support;
+ bool using_msi;
};
struct mp_adapter {
diff --git a/drivers/net/wireless/rtlwifi/ps.c b/drivers/net/wireless/rtlwifi/ps.c
index d1c0191a195b..50504942ded1 100644
--- a/drivers/net/wireless/rtlwifi/ps.c
+++ b/drivers/net/wireless/rtlwifi/ps.c
@@ -32,6 +32,106 @@
#include "base.h"
#include "ps.h"
+/* Description:
+ * This routine deals with the Power Configuration CMD
+ * parsing for RTL8723/RTL8188E Series IC.
+ * Assumption:
+ * We should follow specific format that was released from HW SD.
+ */
+bool rtl_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version,
+ u8 faversion, u8 interface_type,
+ struct wlan_pwr_cfg pwrcfgcmd[])
+{
+ struct wlan_pwr_cfg cfg_cmd = {0};
+ bool polling_bit = false;
+ u32 ary_idx = 0;
+ u8 value = 0;
+ u32 offset = 0;
+ u32 polling_count = 0;
+ u32 max_polling_cnt = 5000;
+
+ do {
+ cfg_cmd = pwrcfgcmd[ary_idx];
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtl_hal_pwrseqcmdparsing(): offset(%#x),cut_msk(%#x), famsk(%#x),"
+ "interface_msk(%#x), base(%#x), cmd(%#x), msk(%#x), value(%#x)\n",
+ GET_PWR_CFG_OFFSET(cfg_cmd),
+ GET_PWR_CFG_CUT_MASK(cfg_cmd),
+ GET_PWR_CFG_FAB_MASK(cfg_cmd),
+ GET_PWR_CFG_INTF_MASK(cfg_cmd),
+ GET_PWR_CFG_BASE(cfg_cmd), GET_PWR_CFG_CMD(cfg_cmd),
+ GET_PWR_CFG_MASK(cfg_cmd), GET_PWR_CFG_VALUE(cfg_cmd));
+
+ if ((GET_PWR_CFG_FAB_MASK(cfg_cmd)&faversion) &&
+ (GET_PWR_CFG_CUT_MASK(cfg_cmd)&cut_version) &&
+ (GET_PWR_CFG_INTF_MASK(cfg_cmd)&interface_type)) {
+ switch (GET_PWR_CFG_CMD(cfg_cmd)) {
+ case PWR_CMD_READ:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtl_hal_pwrseqcmdparsing(): PWR_CMD_READ\n");
+ break;
+ case PWR_CMD_WRITE:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtl_hal_pwrseqcmdparsing(): PWR_CMD_WRITE\n");
+ offset = GET_PWR_CFG_OFFSET(cfg_cmd);
+
+ /*Read the value from system register*/
+ value = rtl_read_byte(rtlpriv, offset);
+ value &= (~(GET_PWR_CFG_MASK(cfg_cmd)));
+ value |= (GET_PWR_CFG_VALUE(cfg_cmd) &
+ GET_PWR_CFG_MASK(cfg_cmd));
+
+ /*Write the value back to sytem register*/
+ rtl_write_byte(rtlpriv, offset, value);
+ break;
+ case PWR_CMD_POLLING:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtl_hal_pwrseqcmdparsing(): PWR_CMD_POLLING\n");
+ polling_bit = false;
+ offset = GET_PWR_CFG_OFFSET(cfg_cmd);
+
+ do {
+ value = rtl_read_byte(rtlpriv, offset);
+
+ value &= GET_PWR_CFG_MASK(cfg_cmd);
+ if (value ==
+ (GET_PWR_CFG_VALUE(cfg_cmd)
+ & GET_PWR_CFG_MASK(cfg_cmd)))
+ polling_bit = true;
+ else
+ udelay(10);
+
+ if (polling_count++ > max_polling_cnt)
+ return false;
+ } while (!polling_bit);
+ break;
+ case PWR_CMD_DELAY:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtl_hal_pwrseqcmdparsing(): PWR_CMD_DELAY\n");
+ if (GET_PWR_CFG_VALUE(cfg_cmd) ==
+ PWRSEQ_DELAY_US)
+ udelay(GET_PWR_CFG_OFFSET(cfg_cmd));
+ else
+ mdelay(GET_PWR_CFG_OFFSET(cfg_cmd));
+ break;
+ case PWR_CMD_END:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtl_hal_pwrseqcmdparsing(): PWR_CMD_END\n");
+ return true;
+ default:
+ RT_ASSERT(false,
+ "rtl_hal_pwrseqcmdparsing(): Unknown CMD!!\n");
+ break;
+ }
+
+ }
+ ary_idx++;
+ } while (1);
+
+ return true;
+}
+EXPORT_SYMBOL(rtl_hal_pwrseqcmdparsing);
+
bool rtl_ps_enable_nic(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -659,7 +759,7 @@ static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data,
unsigned int len)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct ieee80211_mgmt *mgmt = (void *)data;
+ struct ieee80211_mgmt *mgmt = data;
struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
u8 *pos, *end, *ie;
u16 noa_len;
@@ -758,7 +858,7 @@ static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data,
unsigned int len)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct ieee80211_mgmt *mgmt = (void *)data;
+ struct ieee80211_mgmt *mgmt = data;
struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
u8 noa_num, index, i, noa_index = 0;
u8 *pos, *end, *ie;
@@ -850,9 +950,8 @@ void rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
switch (p2p_ps_state) {
case P2P_PS_DISABLE:
p2pinfo->p2p_ps_state = p2p_ps_state;
- rtlpriv->cfg->ops->set_hw_reg(hw,
- HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
- (u8 *)(&p2p_ps_state));
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
+ &p2p_ps_state);
p2pinfo->noa_index = 0;
p2pinfo->ctwindow = 0;
@@ -864,7 +963,7 @@ void rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
rtlps->smart_ps = 2;
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&rtlps->pwr_mode));
+ &rtlps->pwr_mode);
}
}
break;
@@ -877,12 +976,12 @@ void rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
rtlps->smart_ps = 0;
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&rtlps->pwr_mode));
+ &rtlps->pwr_mode);
}
}
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
- (u8 *)(&p2p_ps_state));
+ &p2p_ps_state);
}
break;
case P2P_PS_SCAN:
@@ -892,7 +991,7 @@ void rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
p2pinfo->p2p_ps_state = p2p_ps_state;
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
- (u8 *)(&p2p_ps_state));
+ &p2p_ps_state);
}
break;
default:
@@ -912,7 +1011,7 @@ void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
- struct ieee80211_hdr *hdr = (void *)data;
+ struct ieee80211_hdr *hdr = data;
if (!mac->p2p)
return;
diff --git a/drivers/net/wireless/rtlwifi/ps.h b/drivers/net/wireless/rtlwifi/ps.h
index 88bd76ea88f7..3bd41f958974 100644
--- a/drivers/net/wireless/rtlwifi/ps.h
+++ b/drivers/net/wireless/rtlwifi/ps.h
@@ -32,6 +32,66 @@
#define MAX_SW_LPS_SLEEP_INTV 5
+/*---------------------------------------------
+ * 3 The value of cmd: 4 bits
+ *---------------------------------------------
+ */
+#define PWR_CMD_READ 0x00
+#define PWR_CMD_WRITE 0x01
+#define PWR_CMD_POLLING 0x02
+#define PWR_CMD_DELAY 0x03
+#define PWR_CMD_END 0x04
+
+/* define the base address of each block */
+#define PWR_BASEADDR_MAC 0x00
+#define PWR_BASEADDR_USB 0x01
+#define PWR_BASEADDR_PCIE 0x02
+#define PWR_BASEADDR_SDIO 0x03
+
+#define PWR_FAB_ALL_MSK (BIT(0)|BIT(1)|BIT(2)|BIT(3))
+#define PWR_CUT_TESTCHIP_MSK BIT(0)
+#define PWR_CUT_A_MSK BIT(1)
+#define PWR_CUT_B_MSK BIT(2)
+#define PWR_CUT_C_MSK BIT(3)
+#define PWR_CUT_D_MSK BIT(4)
+#define PWR_CUT_E_MSK BIT(5)
+#define PWR_CUT_F_MSK BIT(6)
+#define PWR_CUT_G_MSK BIT(7)
+#define PWR_CUT_ALL_MSK 0xFF
+#define PWR_INTF_SDIO_MSK BIT(0)
+#define PWR_INTF_USB_MSK BIT(1)
+#define PWR_INTF_PCI_MSK BIT(2)
+#define PWR_INTF_ALL_MSK (BIT(0)|BIT(1)|BIT(2)|BIT(3))
+
+enum pwrseq_delay_unit {
+ PWRSEQ_DELAY_US,
+ PWRSEQ_DELAY_MS,
+};
+
+struct wlan_pwr_cfg {
+ u16 offset;
+ u8 cut_msk;
+ u8 fab_msk:4;
+ u8 interface_msk:4;
+ u8 base:4;
+ u8 cmd:4;
+ u8 msk;
+ u8 value;
+};
+
+#define GET_PWR_CFG_OFFSET(__PWR_CMD) (__PWR_CMD.offset)
+#define GET_PWR_CFG_CUT_MASK(__PWR_CMD) (__PWR_CMD.cut_msk)
+#define GET_PWR_CFG_FAB_MASK(__PWR_CMD) (__PWR_CMD.fab_msk)
+#define GET_PWR_CFG_INTF_MASK(__PWR_CMD) (__PWR_CMD.interface_msk)
+#define GET_PWR_CFG_BASE(__PWR_CMD) (__PWR_CMD.base)
+#define GET_PWR_CFG_CMD(__PWR_CMD) (__PWR_CMD.cmd)
+#define GET_PWR_CFG_MASK(__PWR_CMD) (__PWR_CMD.msk)
+#define GET_PWR_CFG_VALUE(__PWR_CMD) (__PWR_CMD.value)
+
+bool rtl_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version,
+ u8 fab_version, u8 interface_type,
+ struct wlan_pwr_cfg pwrcfgcmd[]);
+
bool rtl_ps_set_rf_state(struct ieee80211_hw *hw,
enum rf_pwrstate state_toset, u32 changesource);
bool rtl_ps_enable_nic(struct ieee80211_hw *hw);
diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c
index a98acefb8c06..ee28a1a3d010 100644
--- a/drivers/net/wireless/rtlwifi/rc.c
+++ b/drivers/net/wireless/rtlwifi/rc.c
@@ -260,8 +260,7 @@ static void rtl_rate_free_sta(void *rtlpriv,
kfree(rate_priv);
}
-static struct rate_control_ops rtl_rate_ops = {
- .module = NULL,
+static const struct rate_control_ops rtl_rate_ops = {
.name = "rtl_rc",
.alloc = rtl_rate_alloc,
.free = rtl_rate_free,
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/Makefile b/drivers/net/wireless/rtlwifi/rtl8188ee/Makefile
index 5b194e97f4b3..a85419a37651 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/Makefile
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/Makefile
@@ -5,7 +5,6 @@ rtl8188ee-objs := \
led.o \
phy.o \
pwrseq.o \
- pwrseqcmd.o \
rf.o \
sw.o \
table.o \
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c
index a6184b6e1d57..f8daa61cf1c3 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c
@@ -235,7 +235,7 @@ void rtl88e_dm_txpower_track_adjust(struct ieee80211_hw *hw,
u8 pwr_val = 0;
u8 cck_base = rtldm->swing_idx_cck_base;
u8 cck_val = rtldm->swing_idx_cck;
- u8 ofdm_base = rtldm->swing_idx_ofdm_base;
+ u8 ofdm_base = rtldm->swing_idx_ofdm_base[0];
u8 ofdm_val = rtlpriv->dm.swing_idx_ofdm[RF90_PATH_A];
if (type == 0) {
@@ -726,7 +726,7 @@ static void rtl88e_dm_pwdb_monitor(struct ieee80211_hw *hw)
static u64 last_rx;
long tmp_entry_max_pwdb = 0, tmp_entry_min_pwdb = 0xff;
- if (rtlhal->oem_id == RT_CID_819x_HP) {
+ if (rtlhal->oem_id == RT_CID_819X_HP) {
u64 cur_txok_cnt = 0;
u64 cur_rxok_cnt = 0;
cur_txok_cnt = rtlpriv->stats.txbytesunicast - last_txok;
@@ -851,9 +851,8 @@ static void rtl88e_dm_check_edca_turbo(struct ieee80211_hw *hw)
} else {
if (rtlpriv->dm.current_turbo_edca) {
u8 tmp = AC0_BE;
- rtlpriv->cfg->ops->set_hw_reg(hw,
- HW_VAR_AC_PARAM,
- (u8 *)(&tmp));
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM,
+ &tmp);
rtlpriv->dm.current_turbo_edca = false;
}
}
@@ -912,7 +911,7 @@ static void rtl88e_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw
for (i = 0; i < OFDM_TABLE_LENGTH; i++) {
if (ele_d == (ofdmswing_table[i] & MASKOFDM_D)) {
ofdm_old[0] = (u8) i;
- rtldm->swing_idx_ofdm_base = (u8)i;
+ rtldm->swing_idx_ofdm_base[0] = (u8)i;
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
"Initial pathA ele_d reg0x%x = 0x%lx, ofdm_index = 0x%x\n",
ROFDM0_XATXIQIMBAL,
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/fw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/fw.c
index 557bc5b8327e..4f9376ad4739 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/fw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/fw.c
@@ -119,7 +119,7 @@ static void _rtl88e_write_fw(struct ieee80211_hw *hw,
enum version_8188e version, u8 *buffer, u32 size)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- u8 *buf_ptr = (u8 *)buffer;
+ u8 *buf_ptr = buffer;
u32 page_no, remain;
u32 page, offset;
@@ -213,7 +213,7 @@ int rtl88e_download_fw(struct ieee80211_hw *hw, bool buse_wake_on_wlan_fw)
return 1;
pfwheader = (struct rtl92c_firmware_header *)rtlhal->pfirmware;
- pfwdata = (u8 *)rtlhal->pfirmware;
+ pfwdata = rtlhal->pfirmware;
fwsize = rtlhal->fwsize;
RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
"normal Firmware SIZE %d\n", fwsize);
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
index e06971be7df7..94cd9df98381 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
@@ -41,7 +41,6 @@
#include "fw.h"
#include "led.h"
#include "hw.h"
-#include "pwrseqcmd.h"
#include "pwrseq.h"
#define LLT_CONFIG 5
@@ -148,8 +147,7 @@ static void _rtl88ee_set_fw_clock_on(struct ieee80211_hw *hw,
}
if (IS_IN_LOW_POWER_STATE_88E(rtlhal->fw_ps_state)) {
- rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_SET_RPWM,
- (u8 *)(&rpwm_val));
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val);
if (FW_PS_IS_ACK(rpwm_val)) {
isr_regaddr = REG_HISR;
content = rtl_read_dword(rtlpriv, isr_regaddr);
@@ -226,7 +224,7 @@ static void _rtl88ee_set_fw_clock_off(struct ieee80211_hw *hw,
rtlhal->fw_ps_state = FW_PS_STATE(rpwm_val);
rtl_write_word(rtlpriv, REG_HISR, 0x0100);
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
- (u8 *)(&rpwm_val));
+ &rpwm_val);
spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
rtlhal->fw_clk_change_in_progress = false;
spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
@@ -274,15 +272,14 @@ static void _rtl88ee_fwlps_leave(struct ieee80211_hw *hw)
_rtl88ee_set_fw_clock_on(hw, rpwm_val, false);
rtlhal->allow_sw_to_change_hwclc = false;
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&fw_pwrmode));
+ &fw_pwrmode);
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
(u8 *)(&fw_current_inps));
} else {
rpwm_val = FW_PS_STATE_ALL_ON_88E; /* RF on */
- rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
- (u8 *)(&rpwm_val));
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val);
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&fw_pwrmode));
+ &fw_pwrmode);
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
(u8 *)(&fw_current_inps));
}
@@ -301,7 +298,7 @@ static void _rtl88ee_fwlps_enter(struct ieee80211_hw *hw)
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
(u8 *)(&fw_current_inps));
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&ppsc->fwctrl_psmode));
+ &ppsc->fwctrl_psmode);
rtlhal->allow_sw_to_change_hwclc = true;
_rtl88ee_set_fw_clock_off(hw, rpwm_val);
} else {
@@ -309,9 +306,8 @@ static void _rtl88ee_fwlps_enter(struct ieee80211_hw *hw)
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
(u8 *)(&fw_current_inps));
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&ppsc->fwctrl_psmode));
- rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
- (u8 *)(&rpwm_val));
+ &ppsc->fwctrl_psmode);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val);
}
}
@@ -420,12 +416,12 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
for (e_aci = 0; e_aci < AC_MAX; e_aci++) {
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM,
- (u8 *)(&e_aci));
+ &e_aci);
}
break; }
case HW_VAR_ACK_PREAMBLE:{
u8 reg_tmp;
- u8 short_preamble = (bool) (*(u8 *)val);
+ u8 short_preamble = (bool)*val;
reg_tmp = rtl_read_byte(rtlpriv, REG_TRXPTCL_CTL+2);
if (short_preamble) {
reg_tmp |= 0x02;
@@ -436,13 +432,13 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
}
break; }
case HW_VAR_WPA_CONFIG:
- rtl_write_byte(rtlpriv, REG_SECCFG, *((u8 *)val));
+ rtl_write_byte(rtlpriv, REG_SECCFG, *val);
break;
case HW_VAR_AMPDU_MIN_SPACE:{
u8 min_spacing_to_set;
u8 sec_min_space;
- min_spacing_to_set = *((u8 *)val);
+ min_spacing_to_set = *val;
if (min_spacing_to_set <= 7) {
sec_min_space = 0;
@@ -465,7 +461,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
case HW_VAR_SHORTGI_DENSITY:{
u8 density_to_set;
- density_to_set = *((u8 *)val);
+ density_to_set = *val;
mac->min_space_cfg |= (density_to_set << 3);
RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
@@ -483,7 +479,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
reg = regtoset_normal;
- factor = *((u8 *)val);
+ factor = *val;
if (factor <= 3) {
factor = (1 << (factor + 2));
if (factor > 0xf)
@@ -506,15 +502,15 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
}
break; }
case HW_VAR_AC_PARAM:{
- u8 e_aci = *((u8 *)val);
+ u8 e_aci = *val;
rtl88e_dm_init_edca_turbo(hw);
- if (rtlpci->acm_method != eAcmWay2_SW)
+ if (rtlpci->acm_method != EACMWAY2_SW)
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL,
- (u8 *)(&e_aci));
+ &e_aci);
break; }
case HW_VAR_ACM_CTRL:{
- u8 e_aci = *((u8 *)val);
+ u8 e_aci = *val;
union aci_aifsn *p_aci_aifsn =
(union aci_aifsn *)(&(mac->ac[0].aifs));
u8 acm = p_aci_aifsn->f.acm;
@@ -567,7 +563,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
rtlpci->receive_config = ((u32 *)(val))[0];
break;
case HW_VAR_RETRY_LIMIT:{
- u8 retry_limit = ((u8 *)(val))[0];
+ u8 retry_limit = *val;
rtl_write_word(rtlpriv, REG_RL,
retry_limit << RETRY_LIMIT_SHORT_SHIFT |
@@ -580,7 +576,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
rtlefuse->efuse_usedbytes = *((u16 *)val);
break;
case HW_VAR_EFUSE_USAGE:
- rtlefuse->efuse_usedpercentage = *((u8 *)val);
+ rtlefuse->efuse_usedpercentage = *val;
break;
case HW_VAR_IO_CMD:
rtl88e_phy_set_io_cmd(hw, (*(enum io_type *)val));
@@ -592,15 +588,13 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
udelay(1);
if (rpwm_val & BIT(7)) {
- rtl_write_byte(rtlpriv, REG_PCIE_HRPWM,
- (*(u8 *)val));
+ rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val);
} else {
- rtl_write_byte(rtlpriv, REG_PCIE_HRPWM,
- ((*(u8 *)val) | BIT(7)));
+ rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val | BIT(7));
}
break; }
case HW_VAR_H2C_FW_PWRMODE:
- rtl88e_set_fw_pwrmode_cmd(hw, (*(u8 *)val));
+ rtl88e_set_fw_pwrmode_cmd(hw, *val);
break;
case HW_VAR_FW_PSMODE_STATUS:
ppsc->fw_current_inpsmode = *((bool *)val);
@@ -617,7 +611,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
_rtl88ee_fwlps_leave(hw);
break; }
case HW_VAR_H2C_FW_JOINBSSRPT:{
- u8 mstatus = (*(u8 *)val);
+ u8 mstatus = *val;
u8 tmp, tmp_reg422, uval;
u8 count = 0, dlbcn_count = 0;
bool recover = false;
@@ -668,10 +662,10 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
}
rtl_write_byte(rtlpriv, REG_CR + 1, (tmp & ~(BIT(0))));
}
- rtl88e_set_fw_joinbss_report_cmd(hw, (*(u8 *)val));
+ rtl88e_set_fw_joinbss_report_cmd(hw, *val);
break; }
case HW_VAR_H2C_FW_P2P_PS_OFFLOAD:
- rtl88e_set_p2p_ps_offload_cmd(hw, (*(u8 *)val));
+ rtl88e_set_p2p_ps_offload_cmd(hw, *val);
break;
case HW_VAR_AID:{
u16 u2btmp;
@@ -681,7 +675,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
mac->assoc_id));
break; }
case HW_VAR_CORRECT_TSF:{
- u8 btype_ibss = ((u8 *)(val))[0];
+ u8 btype_ibss = *val;
if (btype_ibss == true)
_rtl88ee_stop_tx_beacon(hw);
@@ -815,11 +809,11 @@ static bool _rtl88ee_init_mac(struct ieee80211_hw *hw)
rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x00);
/* HW Power on sequence */
- if (!rtl88_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK,
- PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK,
- Rtl8188E_NIC_ENABLE_FLOW)) {
+ if (!rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK,
+ PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK,
+ Rtl8188E_NIC_ENABLE_FLOW)) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
- "init MAC Fail as rtl88_hal_pwrseqcmdparsing\n");
+ "init MAC Fail as rtl_hal_pwrseqcmdparsing\n");
return false;
}
@@ -1025,9 +1019,20 @@ int rtl88ee_hw_init(struct ieee80211_hw *hw)
bool rtstatus = true;
int err = 0;
u8 tmp_u1b, u1byte;
+ unsigned long flags;
RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Rtl8188EE hw init\n");
rtlpriv->rtlhal.being_init_adapter = true;
+ /* As this function can take a very long time (up to 350 ms)
+ * and can be called with irqs disabled, reenable the irqs
+ * to let the other devices continue being serviced.
+ *
+ * It is safe doing so since our own interrupts will only be enabled
+ * in a subsequent step.
+ */
+ local_save_flags(flags);
+ local_irq_enable();
+
rtlpriv->intf_ops->disable_aspm(hw);
tmp_u1b = rtl_read_byte(rtlpriv, REG_SYS_CLKR+1);
@@ -1043,7 +1048,7 @@ int rtl88ee_hw_init(struct ieee80211_hw *hw)
if (rtstatus != true) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Init MAC failed\n");
err = 1;
- return err;
+ goto exit;
}
err = rtl88e_download_fw(hw, false);
@@ -1051,8 +1056,7 @@ int rtl88ee_hw_init(struct ieee80211_hw *hw)
RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
"Failed to download FW. Init HW without FW now..\n");
err = 1;
- rtlhal->fw_ready = false;
- return err;
+ goto exit;
} else {
rtlhal->fw_ready = true;
}
@@ -1097,7 +1101,7 @@ int rtl88ee_hw_init(struct ieee80211_hw *hw)
if (ppsc->rfpwr_state == ERFON) {
if ((rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV) ||
((rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV) &&
- (rtlhal->oem_id == RT_CID_819x_HP))) {
+ (rtlhal->oem_id == RT_CID_819X_HP))) {
rtl88e_phy_set_rfpath_switch(hw, true);
rtlpriv->dm.fat_table.rx_idle_ant = MAIN_ANT;
} else {
@@ -1135,10 +1139,12 @@ int rtl88ee_hw_init(struct ieee80211_hw *hw)
}
rtl_write_byte(rtlpriv, REG_NAV_CTRL+2, ((30000+127)/128));
rtl88e_dm_init(hw);
+exit:
+ local_irq_restore(flags);
rtlpriv->rtlhal.being_init_adapter = false;
RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "end of Rtl8188EE hw init %x\n",
err);
- return 0;
+ return err;
}
static enum version_8188e _rtl88ee_read_chip_version(struct ieee80211_hw *hw)
@@ -1235,12 +1241,13 @@ static int _rtl88ee_set_media_status(struct ieee80211_hw *hw,
void rtl88ee_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
- u32 reg_rcr = rtlpci->receive_config;
+ u32 reg_rcr;
if (rtlpriv->psc.rfpwr_state != ERFON)
return;
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
+
if (check_bssid == true) {
reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN);
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR,
@@ -1345,9 +1352,9 @@ static void _rtl88ee_poweroff_adapter(struct ieee80211_hw *hw)
}
rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG+1, 0xFF);
- rtl88_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,
- PWR_INTF_PCI_MSK,
- Rtl8188E_NIC_LPS_ENTER_FLOW);
+ rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,
+ PWR_INTF_PCI_MSK,
+ Rtl8188E_NIC_LPS_ENTER_FLOW);
rtl_write_byte(rtlpriv, REG_RF_CTRL, 0x00);
@@ -1361,8 +1368,8 @@ static void _rtl88ee_poweroff_adapter(struct ieee80211_hw *hw)
u1b_tmp = rtl_read_byte(rtlpriv, REG_32K_CTRL);
rtl_write_byte(rtlpriv, REG_32K_CTRL, (u1b_tmp & (~BIT(0))));
- rtl88_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,
- PWR_INTF_PCI_MSK, Rtl8188E_NIC_DISABLE_FLOW);
+ rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,
+ PWR_INTF_PCI_MSK, Rtl8188E_NIC_DISABLE_FLOW);
u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL+1);
rtl_write_byte(rtlpriv, REG_RSV_CTRL+1, (u1b_tmp & (~BIT(3))));
@@ -1816,7 +1823,7 @@ static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw)
RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
"EEPROM SMID = 0x%4x\n", rtlefuse->eeprom_smid);
/*customer ID*/
- rtlefuse->eeprom_oemid = *(u8 *)&hwinfo[EEPROM_CUSTOMER_ID];
+ rtlefuse->eeprom_oemid = hwinfo[EEPROM_CUSTOMER_ID];
if (rtlefuse->eeprom_oemid == 0xFF)
rtlefuse->eeprom_oemid = 0;
@@ -1833,7 +1840,7 @@ static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw)
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
"dev_addr: %pM\n", rtlefuse->dev_addr);
/*channel plan */
- rtlefuse->eeprom_channelplan = *(u8 *)&hwinfo[EEPROM_CHANNELPLAN];
+ rtlefuse->eeprom_channelplan = hwinfo[EEPROM_CHANNELPLAN];
/* set channel paln to world wide 13 */
rtlefuse->channel_plan = COUNTRY_CODE_WORLD_WIDE_13;
/*tx power*/
@@ -1845,7 +1852,7 @@ static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw)
rtlefuse->autoload_failflag,
hwinfo);
/*board type*/
- rtlefuse->board_type = (((*(u8 *)&hwinfo[jj]) & 0xE0) >> 5);
+ rtlefuse->board_type = (hwinfo[jj] & 0xE0) >> 5;
/*Wake on wlan*/
rtlefuse->wowlan_enable = ((hwinfo[kk] & 0x40) >> 6);
/*parse xtal*/
@@ -1872,15 +1879,15 @@ static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw)
case EEPROM_CID_DEFAULT:
if (rtlefuse->eeprom_did == 0x8179) {
if (rtlefuse->eeprom_svid == 0x1025) {
- rtlhal->oem_id = RT_CID_819x_Acer;
+ rtlhal->oem_id = RT_CID_819X_ACER;
} else if ((rtlefuse->eeprom_svid == 0x10EC &&
rtlefuse->eeprom_smid == 0x0179) ||
(rtlefuse->eeprom_svid == 0x17AA &&
rtlefuse->eeprom_smid == 0x0179)) {
- rtlhal->oem_id = RT_CID_819x_Lenovo;
+ rtlhal->oem_id = RT_CID_819X_LENOVO;
} else if (rtlefuse->eeprom_svid == 0x103c &&
rtlefuse->eeprom_smid == 0x197d) {
- rtlhal->oem_id = RT_CID_819x_HP;
+ rtlhal->oem_id = RT_CID_819X_HP;
} else {
rtlhal->oem_id = RT_CID_DEFAULT;
}
@@ -1892,7 +1899,7 @@ static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw)
rtlhal->oem_id = RT_CID_TOSHIBA;
break;
case EEPROM_CID_QMI:
- rtlhal->oem_id = RT_CID_819x_QMI;
+ rtlhal->oem_id = RT_CID_819X_QMI;
break;
case EEPROM_CID_WHQL:
default:
@@ -1911,14 +1918,14 @@ static void _rtl88ee_hal_customized_behavior(struct ieee80211_hw *hw)
pcipriv->ledctl.led_opendrain = true;
switch (rtlhal->oem_id) {
- case RT_CID_819x_HP:
+ case RT_CID_819X_HP:
pcipriv->ledctl.led_opendrain = true;
break;
- case RT_CID_819x_Lenovo:
+ case RT_CID_819X_LENOVO:
case RT_CID_DEFAULT:
case RT_CID_TOSHIBA:
case RT_CID_CCX:
- case RT_CID_819x_Acer:
+ case RT_CID_819X_ACER:
case RT_CID_WHQL:
default:
break;
@@ -2211,8 +2218,7 @@ void rtl88ee_update_channel_access_setting(struct ieee80211_hw *hw)
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
u16 sifs_timer;
- rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME,
- (u8 *)&mac->slot_time);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, &mac->slot_time);
if (!mac->ht_enable)
sifs_timer = 0x0a0a;
else
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c
index d67f9c731cc4..1cd6c16d597e 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c
@@ -29,6 +29,7 @@
#include "../wifi.h"
#include "../pci.h"
+#include "../core.h"
#include "../ps.h"
#include "reg.h"
#include "def.h"
@@ -151,18 +152,7 @@ static bool config_bb_with_pgheader(struct ieee80211_hw *hw,
v2 = table_pg[i + 1];
if (v1 < 0xcdcdcdcd) {
- if (table_pg[i] == 0xfe)
- mdelay(50);
- else if (table_pg[i] == 0xfd)
- mdelay(5);
- else if (table_pg[i] == 0xfc)
- mdelay(1);
- else if (table_pg[i] == 0xfb)
- udelay(50);
- else if (table_pg[i] == 0xfa)
- udelay(5);
- else if (table_pg[i] == 0xf9)
- udelay(1);
+ rtl_addr_delay(table_pg[i]);
store_pwrindex_offset(hw, table_pg[i],
table_pg[i + 1],
@@ -672,24 +662,9 @@ static void _rtl8188e_config_rf_reg(struct ieee80211_hw *hw,
u32 addr, u32 data, enum radio_path rfpath,
u32 regaddr)
{
- if (addr == 0xffe) {
- mdelay(50);
- } else if (addr == 0xfd) {
- mdelay(5);
- } else if (addr == 0xfc) {
- mdelay(1);
- } else if (addr == 0xfb) {
- udelay(50);
- } else if (addr == 0xfa) {
- udelay(5);
- } else if (addr == 0xf9) {
- udelay(1);
- } else {
- rtl_set_rfreg(hw, rfpath, regaddr,
- RFREG_OFFSET_MASK,
- data);
- udelay(1);
- }
+ rtl_rfreg_delay(hw, rfpath, regaddr,
+ RFREG_OFFSET_MASK,
+ data);
}
static void rtl88_config_s(struct ieee80211_hw *hw,
@@ -702,28 +677,6 @@ static void rtl88_config_s(struct ieee80211_hw *hw,
addr | maskforphyset);
}
-static void _rtl8188e_config_bb_reg(struct ieee80211_hw *hw,
- u32 addr, u32 data)
-{
- if (addr == 0xfe) {
- mdelay(50);
- } else if (addr == 0xfd) {
- mdelay(5);
- } else if (addr == 0xfc) {
- mdelay(1);
- } else if (addr == 0xfb) {
- udelay(50);
- } else if (addr == 0xfa) {
- udelay(5);
- } else if (addr == 0xf9) {
- udelay(1);
- } else {
- rtl_set_bbreg(hw, addr, MASKDWORD, data);
- udelay(1);
- }
-}
-
-
#define NEXT_PAIR(v1, v2, i) \
do { \
i += 2; v1 = array_table[i]; \
@@ -795,7 +748,7 @@ static void set_baseband_phy_config(struct ieee80211_hw *hw)
v1 = array_table[i];
v2 = array_table[i + 1];
if (v1 < 0xcdcdcdcd) {
- _rtl8188e_config_bb_reg(hw, v1, v2);
+ rtl_bb_delay(hw, v1, v2);
} else {/*This line is the start line of branch.*/
if (!check_cond(hw, array_table[i])) {
/*Discard the following (offset, data) pairs*/
@@ -811,7 +764,7 @@ static void set_baseband_phy_config(struct ieee80211_hw *hw)
while (v2 != 0xDEAD &&
v2 != 0xCDEF &&
v2 != 0xCDCD && i < arraylen - 2) {
- _rtl8188e_config_bb_reg(hw, v1, v2);
+ rtl_bb_delay(hw, v1, v2);
NEXT_PAIR(v1, v2, i);
}
@@ -1002,7 +955,7 @@ bool rtl88e_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
}
}
- if (rtlhal->oem_id == RT_CID_819x_HP)
+ if (rtlhal->oem_id == RT_CID_819X_HP)
rtl88_config_s(hw, 0x52, 0x7E4BD);
break;
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h b/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h
index 028ec6dd52b4..32e135ab9a63 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h
@@ -30,7 +30,6 @@
#ifndef __RTL8723E_PWRSEQ_H__
#define __RTL8723E_PWRSEQ_H__
-#include "pwrseqcmd.h"
/*
Check document WM-20110607-Paul-RTL8188E_Power_Architecture-R02.vsd
There are 6 HW Power States:
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h b/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h
index d849abf7d94a..7af85cfa8f87 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h
@@ -2215,22 +2215,6 @@
#define BWORD1 0xc
#define BWORD 0xf
-#define MASKBYTE0 0xff
-#define MASKBYTE1 0xff00
-#define MASKBYTE2 0xff0000
-#define MASKBYTE3 0xff000000
-#define MASKHWORD 0xffff0000
-#define MASKLWORD 0x0000ffff
-#define MASKDWORD 0xffffffff
-#define MASK12BITS 0xfff
-#define MASKH4BITS 0xf0000000
-#define MASKOFDM_D 0xffc00000
-#define MASKCCK 0x3f3f3f3f
-
-#define MASK4BITS 0x0f
-#define MASK20BITS 0xfffff
-#define RFREG_OFFSET_MASK 0xfffff
-
#define BENABLE 0x1
#define BDISABLE 0x0
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c
index 347af1e4f438..1b4101bf9974 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c
@@ -93,6 +93,7 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw)
u8 tid;
rtl8188ee_bt_reg_init(hw);
+ rtlpci->msi_support = true;
rtlpriv->dm.dm_initialgain_enable = 1;
rtlpriv->dm.dm_flag = 0;
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c
index aece6c9cccf1..06ef47cd6203 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c
@@ -452,7 +452,7 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw,
/* During testing, hdr was NULL */
return false;
}
- if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+ if ((_ieee80211_is_robust_mgmt_frame(hdr)) &&
(ieee80211_has_protected(hdr->frame_control)))
rx_status->flag &= ~RX_FLAG_DECRYPTED;
else
@@ -489,16 +489,15 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw,
void rtl88ee_tx_fill_desc(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr, u8 *pdesc_tx,
- struct ieee80211_tx_info *info,
- struct ieee80211_sta *sta,
- struct sk_buff *skb,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta, struct sk_buff *skb,
u8 hw_queue, struct rtl_tcb_desc *ptcb_desc)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
- u8 *pdesc = (u8 *)pdesc_tx;
+ u8 *pdesc = pdesc_tx;
u16 seq_number;
__le16 fc = hdr->frame_control;
unsigned int buf_len = 0;
@@ -717,7 +716,7 @@ void rtl88ee_tx_fill_cmddesc(struct ieee80211_hw *hw,
SET_TX_DESC_OWN(pdesc, 1);
- SET_TX_DESC_PKT_SIZE((u8 *)pdesc, (u16)(skb->len));
+ SET_TX_DESC_PKT_SIZE(pdesc, (u16)(skb->len));
SET_TX_DESC_FIRST_SEG(pdesc, 1);
SET_TX_DESC_LAST_SEG(pdesc, 1);
@@ -734,7 +733,8 @@ void rtl88ee_tx_fill_cmddesc(struct ieee80211_hw *hw,
pdesc, TX_DESC_SIZE);
}
-void rtl88ee_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
+void rtl88ee_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val)
{
if (istx == true) {
switch (desc_name) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.h b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.h
index 21ca33a7c770..8c2609412d2c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.h
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.h
@@ -777,15 +777,15 @@ struct rx_desc_88e {
void rtl88ee_tx_fill_desc(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr, u8 *pdesc_tx,
- struct ieee80211_tx_info *info,
- struct ieee80211_sta *sta,
- struct sk_buff *skb,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta, struct sk_buff *skb,
u8 hw_queue, struct rtl_tcb_desc *ptcb_desc);
bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw,
struct rtl_stats *status,
struct ieee80211_rx_status *rx_status,
u8 *pdesc, struct sk_buff *skb);
-void rtl88ee_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val);
+void rtl88ee_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val);
u32 rtl88ee_get_desc(u8 *pdesc, bool istx, u8 desc_name);
void rtl88ee_tx_polling(struct ieee80211_hw *hw, u8 hw_queue);
void rtl88ee_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
index 2eb0b38384dd..55adf043aef7 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
@@ -319,7 +319,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
u8 e_aci = *(val);
rtl92c_dm_init_edca_turbo(hw);
- if (rtlpci->acm_method != eAcmWay2_SW)
+ if (rtlpci->acm_method != EACMWAY2_SW)
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_ACM_CTRL,
(&e_aci));
@@ -476,7 +476,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
break;
}
case HW_VAR_H2C_FW_P2P_PS_OFFLOAD:
- rtl92c_set_p2p_ps_offload_cmd(hw, (*(u8 *)val));
+ rtl92c_set_p2p_ps_offload_cmd(hw, *val);
break;
case HW_VAR_AID:{
u16 u2btmp;
@@ -521,30 +521,32 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
(u8 *)(&fw_current_inps));
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&ppsc->fwctrl_psmode));
+ &ppsc->fwctrl_psmode);
rtlpriv->cfg->ops->set_hw_reg(hw,
- HW_VAR_SET_RPWM,
- (u8 *)(&rpwm_val));
+ HW_VAR_SET_RPWM,
+ &rpwm_val);
} else {
rpwm_val = 0x0C; /* RF on */
fw_pwrmode = FW_PS_ACTIVE_MODE;
fw_current_inps = false;
rtlpriv->cfg->ops->set_hw_reg(hw,
- HW_VAR_SET_RPWM,
- (u8 *)(&rpwm_val));
+ HW_VAR_SET_RPWM,
+ &rpwm_val);
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&fw_pwrmode));
+ &fw_pwrmode);
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_FW_PSMODE_STATUS,
(u8 *)(&fw_current_inps));
}
break; }
+ case HW_VAR_KEEP_ALIVE:
+ break;
default:
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
- "switch case not processed\n");
+ "switch case %d not processed\n", variable);
break;
}
}
@@ -1214,11 +1216,13 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw,
void rtl92ce_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- u32 reg_rcr = rtl_read_dword(rtlpriv, REG_RCR);
+ u32 reg_rcr;
if (rtlpriv->psc.rfpwr_state != ERFON)
return;
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
+
if (check_bssid) {
reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN);
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR,
@@ -1734,7 +1738,7 @@ static void _rtl92ce_read_adapter_info(struct ieee80211_hw *hw)
if (rtlefuse->eeprom_did == 0x8176) {
if ((rtlefuse->eeprom_svid == 0x103C &&
rtlefuse->eeprom_smid == 0x1629))
- rtlhal->oem_id = RT_CID_819x_HP;
+ rtlhal->oem_id = RT_CID_819X_HP;
else
rtlhal->oem_id = RT_CID_DEFAULT;
} else {
@@ -1745,7 +1749,7 @@ static void _rtl92ce_read_adapter_info(struct ieee80211_hw *hw)
rtlhal->oem_id = RT_CID_TOSHIBA;
break;
case EEPROM_CID_QMI:
- rtlhal->oem_id = RT_CID_819x_QMI;
+ rtlhal->oem_id = RT_CID_819X_QMI;
break;
case EEPROM_CID_WHQL:
default:
@@ -1764,14 +1768,14 @@ static void _rtl92ce_hal_customized_behavior(struct ieee80211_hw *hw)
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
switch (rtlhal->oem_id) {
- case RT_CID_819x_HP:
+ case RT_CID_819X_HP:
pcipriv->ledctl.led_opendrain = true;
break;
- case RT_CID_819x_Lenovo:
+ case RT_CID_819X_LENOVO:
case RT_CID_DEFAULT:
case RT_CID_TOSHIBA:
case RT_CID_CCX:
- case RT_CID_819x_Acer:
+ case RT_CID_819X_ACER:
case RT_CID_WHQL:
default:
break;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c
index 73262ca3864b..98b22303c84d 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c
@@ -30,6 +30,7 @@
#include "../wifi.h"
#include "../pci.h"
#include "../ps.h"
+#include "../core.h"
#include "reg.h"
#include "def.h"
#include "hw.h"
@@ -198,18 +199,7 @@ bool _rtl92ce_phy_config_bb_with_headerfile(struct ieee80211_hw *hw,
}
if (configtype == BASEBAND_CONFIG_PHY_REG) {
for (i = 0; i < phy_reg_arraylen; i = i + 2) {
- if (phy_regarray_table[i] == 0xfe)
- mdelay(50);
- else if (phy_regarray_table[i] == 0xfd)
- mdelay(5);
- else if (phy_regarray_table[i] == 0xfc)
- mdelay(1);
- else if (phy_regarray_table[i] == 0xfb)
- udelay(50);
- else if (phy_regarray_table[i] == 0xfa)
- udelay(5);
- else if (phy_regarray_table[i] == 0xf9)
- udelay(1);
+ rtl_addr_delay(phy_regarray_table[i]);
rtl_set_bbreg(hw, phy_regarray_table[i], MASKDWORD,
phy_regarray_table[i + 1]);
udelay(1);
@@ -245,18 +235,7 @@ bool _rtl92ce_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw,
if (configtype == BASEBAND_CONFIG_PHY_REG) {
for (i = 0; i < phy_regarray_pg_len; i = i + 3) {
- if (phy_regarray_table_pg[i] == 0xfe)
- mdelay(50);
- else if (phy_regarray_table_pg[i] == 0xfd)
- mdelay(5);
- else if (phy_regarray_table_pg[i] == 0xfc)
- mdelay(1);
- else if (phy_regarray_table_pg[i] == 0xfb)
- udelay(50);
- else if (phy_regarray_table_pg[i] == 0xfa)
- udelay(5);
- else if (phy_regarray_table_pg[i] == 0xf9)
- udelay(1);
+ rtl_addr_delay(phy_regarray_table_pg[i]);
_rtl92c_store_pwrIndex_diffrate_offset(hw,
phy_regarray_table_pg[i],
@@ -305,46 +284,16 @@ bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
switch (rfpath) {
case RF90_PATH_A:
for (i = 0; i < radioa_arraylen; i = i + 2) {
- if (radioa_array_table[i] == 0xfe)
- mdelay(50);
- else if (radioa_array_table[i] == 0xfd)
- mdelay(5);
- else if (radioa_array_table[i] == 0xfc)
- mdelay(1);
- else if (radioa_array_table[i] == 0xfb)
- udelay(50);
- else if (radioa_array_table[i] == 0xfa)
- udelay(5);
- else if (radioa_array_table[i] == 0xf9)
- udelay(1);
- else {
- rtl_set_rfreg(hw, rfpath, radioa_array_table[i],
- RFREG_OFFSET_MASK,
- radioa_array_table[i + 1]);
- udelay(1);
- }
+ rtl_rfreg_delay(hw, rfpath, radioa_array_table[i],
+ RFREG_OFFSET_MASK,
+ radioa_array_table[i + 1]);
}
break;
case RF90_PATH_B:
for (i = 0; i < radiob_arraylen; i = i + 2) {
- if (radiob_array_table[i] == 0xfe) {
- mdelay(50);
- } else if (radiob_array_table[i] == 0xfd)
- mdelay(5);
- else if (radiob_array_table[i] == 0xfc)
- mdelay(1);
- else if (radiob_array_table[i] == 0xfb)
- udelay(50);
- else if (radiob_array_table[i] == 0xfa)
- udelay(5);
- else if (radiob_array_table[i] == 0xf9)
- udelay(1);
- else {
- rtl_set_rfreg(hw, rfpath, radiob_array_table[i],
- RFREG_OFFSET_MASK,
- radiob_array_table[i + 1]);
- udelay(1);
- }
+ rtl_rfreg_delay(hw, rfpath, radiob_array_table[i],
+ RFREG_OFFSET_MASK,
+ radiob_array_table[i + 1]);
}
break;
case RF90_PATH_C:
@@ -355,6 +304,8 @@ bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
"switch case not processed\n");
break;
+ default:
+ break;
}
return true;
}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
index 8922ecb47ad2..ed703a1b3b7c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
@@ -2044,22 +2044,6 @@
#define BWORD1 0xc
#define BWORD 0xf
-#define MASKBYTE0 0xff
-#define MASKBYTE1 0xff00
-#define MASKBYTE2 0xff0000
-#define MASKBYTE3 0xff000000
-#define MASKHWORD 0xffff0000
-#define MASKLWORD 0x0000ffff
-#define MASKDWORD 0xffffffff
-#define MASK12BITS 0xfff
-#define MASKH4BITS 0xf0000000
-#define MASKOFDM_D 0xffc00000
-#define MASKCCK 0x3f3f3f3f
-
-#define MASK4BITS 0x0f
-#define MASK20BITS 0xfffff
-#define RFREG_OFFSET_MASK 0xfffff
-
#define BENABLE 0x1
#define BDISABLE 0x0
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
index 52abf0a862fa..8f04817cb7ec 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
@@ -393,7 +393,7 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw,
/* In testing, hdr was NULL here */
return false;
}
- if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+ if ((_ieee80211_is_robust_mgmt_frame(hdr)) &&
(ieee80211_has_protected(hdr->frame_control)))
rx_status->flag &= ~RX_FLAG_DECRYPTED;
else
@@ -426,7 +426,7 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw,
void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr, u8 *pdesc_tx,
- struct ieee80211_tx_info *info,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
struct sk_buff *skb,
u8 hw_queue, struct rtl_tcb_desc *tcb_desc)
@@ -666,7 +666,8 @@ void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw,
"H2C Tx Cmd Content", pdesc, TX_DESC_SIZE);
}
-void rtl92ce_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
+void rtl92ce_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val)
{
if (istx) {
switch (desc_name) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
index a7cdd514cb2e..9a39ec4204dd 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
@@ -711,8 +711,8 @@ struct rx_desc_92c {
} __packed;
void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw,
- struct ieee80211_hdr *hdr,
- u8 *pdesc, struct ieee80211_tx_info *info,
+ struct ieee80211_hdr *hdr, u8 *pdesc,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
struct sk_buff *skb, u8 hw_queue,
struct rtl_tcb_desc *ptcb_desc);
@@ -720,7 +720,8 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw,
struct rtl_stats *stats,
struct ieee80211_rx_status *rx_status,
u8 *pdesc, struct sk_buff *skb);
-void rtl92ce_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val);
+void rtl92ce_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val);
u32 rtl92ce_get_desc(u8 *pdesc, bool istx, u8 desc_name);
void rtl92ce_tx_polling(struct ieee80211_hw *hw, u8 hw_queue);
void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
index 468bf73cc883..68b5c7e92cfb 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
@@ -394,7 +394,7 @@ static void _rtl92cu_read_adapter_info(struct ieee80211_hw *hw)
if (rtlefuse->eeprom_did == 0x8176) {
if ((rtlefuse->eeprom_svid == 0x103C &&
rtlefuse->eeprom_smid == 0x1629))
- rtlhal->oem_id = RT_CID_819x_HP;
+ rtlhal->oem_id = RT_CID_819X_HP;
else
rtlhal->oem_id = RT_CID_DEFAULT;
} else {
@@ -405,7 +405,7 @@ static void _rtl92cu_read_adapter_info(struct ieee80211_hw *hw)
rtlhal->oem_id = RT_CID_TOSHIBA;
break;
case EEPROM_CID_QMI:
- rtlhal->oem_id = RT_CID_819x_QMI;
+ rtlhal->oem_id = RT_CID_819X_QMI;
break;
case EEPROM_CID_WHQL:
default:
@@ -423,14 +423,14 @@ static void _rtl92cu_hal_customized_behavior(struct ieee80211_hw *hw)
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
switch (rtlhal->oem_id) {
- case RT_CID_819x_HP:
+ case RT_CID_819X_HP:
usb_priv->ledctl.led_opendrain = true;
break;
- case RT_CID_819x_Lenovo:
+ case RT_CID_819X_LENOVO:
case RT_CID_DEFAULT:
case RT_CID_TOSHIBA:
case RT_CID_CCX:
- case RT_CID_819x_Acer:
+ case RT_CID_819X_ACER:
case RT_CID_WHQL:
default:
break;
@@ -985,6 +985,17 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw)
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
int err = 0;
static bool iqk_initialized;
+ unsigned long flags;
+
+ /* As this function can take a very long time (up to 350 ms)
+ * and can be called with irqs disabled, reenable the irqs
+ * to let the other devices continue being serviced.
+ *
+ * It is safe doing so since our own interrupts will only be enabled
+ * in a subsequent step.
+ */
+ local_save_flags(flags);
+ local_irq_enable();
rtlhal->hw_type = HARDWARE_TYPE_RTL8192CU;
err = _rtl92cu_init_mac(hw);
@@ -997,7 +1008,7 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw)
RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
"Failed to download FW. Init HW without FW now..\n");
err = 1;
- return err;
+ goto exit;
}
rtlhal->last_hmeboxnum = 0; /* h2c */
_rtl92cu_phy_param_tab_init(hw);
@@ -1034,6 +1045,8 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw)
_InitPABias(hw);
_update_mac_setting(hw);
rtl92c_dm_init(hw);
+exit:
+ local_irq_restore(flags);
return err;
}
@@ -1379,11 +1392,13 @@ void rtl92cu_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
- u32 reg_rcr = rtl_read_dword(rtlpriv, REG_RCR);
+ u32 reg_rcr;
if (rtlpriv->psc.rfpwr_state != ERFON)
return;
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
+
if (check_bssid) {
u8 tmp;
if (IS_NORMAL_CHIP(rtlhal->version)) {
@@ -1795,7 +1810,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
e_aci);
break;
}
- if (rtlusb->acm_method != eAcmWay2_SW)
+ if (rtlusb->acm_method != EACMWAY2_SW)
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_ACM_CTRL, &e_aci);
break;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c b/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c
index 0c09240eadcc..9831ff1128ca 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c
@@ -30,6 +30,7 @@
#include "../wifi.h"
#include "../pci.h"
#include "../ps.h"
+#include "../core.h"
#include "reg.h"
#include "def.h"
#include "phy.h"
@@ -188,18 +189,7 @@ bool _rtl92cu_phy_config_bb_with_headerfile(struct ieee80211_hw *hw,
}
if (configtype == BASEBAND_CONFIG_PHY_REG) {
for (i = 0; i < phy_reg_arraylen; i = i + 2) {
- if (phy_regarray_table[i] == 0xfe)
- mdelay(50);
- else if (phy_regarray_table[i] == 0xfd)
- mdelay(5);
- else if (phy_regarray_table[i] == 0xfc)
- mdelay(1);
- else if (phy_regarray_table[i] == 0xfb)
- udelay(50);
- else if (phy_regarray_table[i] == 0xfa)
- udelay(5);
- else if (phy_regarray_table[i] == 0xf9)
- udelay(1);
+ rtl_addr_delay(phy_regarray_table[i]);
rtl_set_bbreg(hw, phy_regarray_table[i], MASKDWORD,
phy_regarray_table[i + 1]);
udelay(1);
@@ -236,18 +226,7 @@ bool _rtl92cu_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw,
phy_regarray_table_pg = rtlphy->hwparam_tables[PHY_REG_PG].pdata;
if (configtype == BASEBAND_CONFIG_PHY_REG) {
for (i = 0; i < phy_regarray_pg_len; i = i + 3) {
- if (phy_regarray_table_pg[i] == 0xfe)
- mdelay(50);
- else if (phy_regarray_table_pg[i] == 0xfd)
- mdelay(5);
- else if (phy_regarray_table_pg[i] == 0xfc)
- mdelay(1);
- else if (phy_regarray_table_pg[i] == 0xfb)
- udelay(50);
- else if (phy_regarray_table_pg[i] == 0xfa)
- udelay(5);
- else if (phy_regarray_table_pg[i] == 0xf9)
- udelay(1);
+ rtl_addr_delay(phy_regarray_table_pg[i]);
_rtl92c_store_pwrIndex_diffrate_offset(hw,
phy_regarray_table_pg[i],
phy_regarray_table_pg[i + 1],
@@ -294,46 +273,16 @@ bool rtl92cu_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
switch (rfpath) {
case RF90_PATH_A:
for (i = 0; i < radioa_arraylen; i = i + 2) {
- if (radioa_array_table[i] == 0xfe)
- mdelay(50);
- else if (radioa_array_table[i] == 0xfd)
- mdelay(5);
- else if (radioa_array_table[i] == 0xfc)
- mdelay(1);
- else if (radioa_array_table[i] == 0xfb)
- udelay(50);
- else if (radioa_array_table[i] == 0xfa)
- udelay(5);
- else if (radioa_array_table[i] == 0xf9)
- udelay(1);
- else {
- rtl_set_rfreg(hw, rfpath, radioa_array_table[i],
- RFREG_OFFSET_MASK,
- radioa_array_table[i + 1]);
- udelay(1);
- }
+ rtl_rfreg_delay(hw, rfpath, radioa_array_table[i],
+ RFREG_OFFSET_MASK,
+ radioa_array_table[i + 1]);
}
break;
case RF90_PATH_B:
for (i = 0; i < radiob_arraylen; i = i + 2) {
- if (radiob_array_table[i] == 0xfe) {
- mdelay(50);
- } else if (radiob_array_table[i] == 0xfd)
- mdelay(5);
- else if (radiob_array_table[i] == 0xfc)
- mdelay(1);
- else if (radiob_array_table[i] == 0xfb)
- udelay(50);
- else if (radiob_array_table[i] == 0xfa)
- udelay(5);
- else if (radiob_array_table[i] == 0xf9)
- udelay(1);
- else {
- rtl_set_rfreg(hw, rfpath, radiob_array_table[i],
- RFREG_OFFSET_MASK,
- radiob_array_table[i + 1]);
- udelay(1);
- }
+ rtl_rfreg_delay(hw, rfpath, radiob_array_table[i],
+ RFREG_OFFSET_MASK,
+ radiob_array_table[i + 1]);
}
break;
case RF90_PATH_C:
@@ -344,6 +293,8 @@ bool rtl92cu_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
"switch case not processed\n");
break;
+ default:
+ break;
}
return true;
}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
index 1bc21ccfa71b..035e0dc3922c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
@@ -495,7 +495,7 @@ static void _rtl_tx_desc_checksum(u8 *txdesc)
void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr, u8 *pdesc_tx,
- struct ieee80211_tx_info *info,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
struct sk_buff *skb,
u8 queue_index,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h
index 725c53accc58..fd8051dcd98a 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h
@@ -420,7 +420,7 @@ struct sk_buff *rtl8192c_tx_aggregate_hdl(struct ieee80211_hw *,
struct sk_buff_head *);
void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr, u8 *pdesc_tx,
- struct ieee80211_tx_info *info,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
struct sk_buff *skb,
u8 queue_index,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
index 7908e1c85819..304c443b89b2 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
@@ -194,15 +194,15 @@ static void rtl92d_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw)
rtl_set_bbreg(hw, ROFDM0_LSTF, BIT(31), 1); /* hold page C counter */
rtl_set_bbreg(hw, ROFDM1_LSTF, BIT(31), 1); /*hold page D counter */
- ret_value = rtl_get_bbreg(hw, ROFDM0_FRAMESYNC, BMASKDWORD);
+ ret_value = rtl_get_bbreg(hw, ROFDM0_FRAMESYNC, MASKDWORD);
falsealm_cnt->cnt_fast_fsync_fail = (ret_value & 0xffff);
falsealm_cnt->cnt_sb_search_fail = ((ret_value & 0xffff0000) >> 16);
- ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER1, BMASKDWORD);
+ ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER1, MASKDWORD);
falsealm_cnt->cnt_parity_fail = ((ret_value & 0xffff0000) >> 16);
- ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER2, BMASKDWORD);
+ ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER2, MASKDWORD);
falsealm_cnt->cnt_rate_illegal = (ret_value & 0xffff);
falsealm_cnt->cnt_crc8_fail = ((ret_value & 0xffff0000) >> 16);
- ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER3, BMASKDWORD);
+ ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER3, MASKDWORD);
falsealm_cnt->cnt_mcs_fail = (ret_value & 0xffff);
falsealm_cnt->cnt_ofdm_fail = falsealm_cnt->cnt_parity_fail +
falsealm_cnt->cnt_rate_illegal +
@@ -214,9 +214,9 @@ static void rtl92d_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw)
if (rtlpriv->rtlhal.current_bandtype != BAND_ON_5G) {
/* hold cck counter */
rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
- ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERLOWER, BMASKBYTE0);
+ ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERLOWER, MASKBYTE0);
falsealm_cnt->cnt_cck_fail = ret_value;
- ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERUPPER, BMASKBYTE3);
+ ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERUPPER, MASKBYTE3);
falsealm_cnt->cnt_cck_fail += (ret_value & 0xff) << 8;
rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
} else {
@@ -331,11 +331,11 @@ static void rtl92d_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw)
if (de_digtable->pre_cck_pd_state != de_digtable->cur_cck_pd_state) {
if (de_digtable->cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI) {
rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
- rtl_set_bbreg(hw, RCCK0_CCA, BMASKBYTE2, 0x83);
+ rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0x83);
rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
} else {
rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
- rtl_set_bbreg(hw, RCCK0_CCA, BMASKBYTE2, 0xcd);
+ rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0xcd);
rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
}
de_digtable->pre_cck_pd_state = de_digtable->cur_cck_pd_state;
@@ -722,7 +722,7 @@ static void rtl92d_dm_rxgain_tracking_thermalmeter(struct ieee80211_hw *hw)
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
"===> Rx Gain %x\n", u4tmp);
for (i = RF90_PATH_A; i < rtlpriv->phy.num_total_rfpath; i++)
- rtl_set_rfreg(hw, i, 0x3C, BRFREGOFFSETMASK,
+ rtl_set_rfreg(hw, i, 0x3C, RFREG_OFFSET_MASK,
(rtlpriv->phy.reg_rf3c[i] & (~(0xF000))) | u4tmp);
}
@@ -737,7 +737,7 @@ static void rtl92d_bandtype_2_4G(struct ieee80211_hw *hw, long *temp_cckg,
/* Query CCK default setting From 0xa24 */
rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag);
temp_cck = rtl_get_bbreg(hw, RCCK0_TXFILTER2,
- BMASKDWORD) & BMASKCCK;
+ MASKDWORD) & MASKCCK;
rtl92d_release_cckandrw_pagea_ctl(hw, &flag);
for (i = 0; i < CCK_TABLE_LENGTH; i++) {
if (rtlpriv->dm.cck_inch14) {
@@ -896,9 +896,9 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
rf = 1;
if (thermalvalue) {
ele_d = rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE,
- BMASKDWORD) & BMASKOFDM_D;
+ MASKDWORD) & MASKOFDM_D;
for (i = 0; i < OFDM_TABLE_SIZE_92D; i++) {
- if (ele_d == (ofdmswing_table[i] & BMASKOFDM_D)) {
+ if (ele_d == (ofdmswing_table[i] & MASKOFDM_D)) {
ofdm_index_old[0] = (u8) i;
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
@@ -910,10 +910,10 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
}
if (is2t) {
ele_d = rtl_get_bbreg(hw, ROFDM0_XBTxIQIMBALANCE,
- BMASKDWORD) & BMASKOFDM_D;
+ MASKDWORD) & MASKOFDM_D;
for (i = 0; i < OFDM_TABLE_SIZE_92D; i++) {
if (ele_d ==
- (ofdmswing_table[i] & BMASKOFDM_D)) {
+ (ofdmswing_table[i] & MASKOFDM_D)) {
ofdm_index_old[1] = (u8) i;
RT_TRACE(rtlpriv, COMP_POWER_TRACKING,
DBG_LOUD,
@@ -1091,10 +1091,10 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
value32 = (ele_d << 22) | ((ele_c & 0x3F) <<
16) | ele_a;
rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE,
- BMASKDWORD, value32);
+ MASKDWORD, value32);
value32 = (ele_c & 0x000003C0) >> 6;
- rtl_set_bbreg(hw, ROFDM0_XCTxAFE, BMASKH4BITS,
+ rtl_set_bbreg(hw, ROFDM0_XCTxAFE, MASKH4BITS,
value32);
value32 = ((val_x * ele_d) >> 7) & 0x01;
@@ -1103,10 +1103,10 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
} else {
rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE,
- BMASKDWORD,
+ MASKDWORD,
ofdmswing_table
[(u8)ofdm_index[0]]);
- rtl_set_bbreg(hw, ROFDM0_XCTxAFE, BMASKH4BITS,
+ rtl_set_bbreg(hw, ROFDM0_XCTxAFE, MASKH4BITS,
0x00);
rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD,
BIT(24), 0x00);
@@ -1204,21 +1204,21 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
ele_a;
rtl_set_bbreg(hw,
ROFDM0_XBTxIQIMBALANCE,
- BMASKDWORD, value32);
+ MASKDWORD, value32);
value32 = (ele_c & 0x000003C0) >> 6;
rtl_set_bbreg(hw, ROFDM0_XDTxAFE,
- BMASKH4BITS, value32);
+ MASKH4BITS, value32);
value32 = ((val_x * ele_d) >> 7) & 0x01;
rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD,
BIT(28), value32);
} else {
rtl_set_bbreg(hw,
ROFDM0_XBTxIQIMBALANCE,
- BMASKDWORD,
+ MASKDWORD,
ofdmswing_table
[(u8) ofdm_index[1]]);
rtl_set_bbreg(hw, ROFDM0_XDTxAFE,
- BMASKH4BITS, 0x00);
+ MASKH4BITS, 0x00);
rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD,
BIT(28), 0x00);
}
@@ -1229,10 +1229,10 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
}
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
"TxPwrTracking 0xc80 = 0x%x, 0xc94 = 0x%x RF 0x24 = 0x%x\n",
- rtl_get_bbreg(hw, 0xc80, BMASKDWORD),
- rtl_get_bbreg(hw, 0xc94, BMASKDWORD),
+ rtl_get_bbreg(hw, 0xc80, MASKDWORD),
+ rtl_get_bbreg(hw, 0xc94, MASKDWORD),
rtl_get_rfreg(hw, RF90_PATH_A, 0x24,
- BRFREGOFFSETMASK));
+ RFREG_OFFSET_MASK));
}
if ((delta_iqk > rtlefuse->delta_iqk) &&
(rtlefuse->delta_iqk != 0)) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
index c4a7db9135d6..2b08671004a0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
@@ -318,7 +318,7 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
case HW_VAR_AC_PARAM: {
u8 e_aci = *val;
rtl92d_dm_init_edca_turbo(hw);
- if (rtlpci->acm_method != eAcmWay2_SW)
+ if (rtlpci->acm_method != EACMWAY2_SW)
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL,
&e_aci);
break;
@@ -985,9 +985,9 @@ int rtl92de_hw_init(struct ieee80211_hw *hw)
/* set default value after initialize RF, */
rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0);
rtlphy->rfreg_chnlval[0] = rtl_get_rfreg(hw, (enum radio_path)0,
- RF_CHNLBW, BRFREGOFFSETMASK);
+ RF_CHNLBW, RFREG_OFFSET_MASK);
rtlphy->rfreg_chnlval[1] = rtl_get_rfreg(hw, (enum radio_path)1,
- RF_CHNLBW, BRFREGOFFSETMASK);
+ RF_CHNLBW, RFREG_OFFSET_MASK);
/*---- Set CCK and OFDM Block "ON"----*/
if (rtlhal->current_bandtype == BAND_ON_2_4G)
@@ -1035,7 +1035,7 @@ int rtl92de_hw_init(struct ieee80211_hw *hw)
tmp_rega = rtl_get_rfreg(hw,
(enum radio_path)RF90_PATH_A,
- 0x2a, BMASKDWORD);
+ 0x2a, MASKDWORD);
if (((tmp_rega & BIT(11)) == BIT(11)))
break;
@@ -1138,11 +1138,13 @@ static int _rtl92de_set_media_status(struct ieee80211_hw *hw,
void rtl92de_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
- u32 reg_rcr = rtlpci->receive_config;
+ u32 reg_rcr;
if (rtlpriv->psc.rfpwr_state != ERFON)
return;
+
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
+
if (check_bssid) {
reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN);
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
@@ -1332,13 +1334,13 @@ void rtl92de_card_disable(struct ieee80211_hw *hw)
/* c. ========RF OFF sequence========== */
/* 0x88c[23:20] = 0xf. */
rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0xf);
- rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00);
+ rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00);
/* APSD_CTRL 0x600[7:0] = 0x40 */
rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40);
/* Close antenna 0,0xc04,0xd04 */
- rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0);
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0);
rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0);
/* SYS_FUNC_EN 0x02[7:0] = 0xE2 reset BB state machine */
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c
index 13196cc4b1d3..3d1f0dd4e52d 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c
@@ -30,6 +30,7 @@
#include "../wifi.h"
#include "../pci.h"
#include "../ps.h"
+#include "../core.h"
#include "reg.h"
#include "def.h"
#include "phy.h"
@@ -242,7 +243,7 @@ void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw,
else if (rtlhal->during_mac0init_radiob)
/* mac0 use phy1 write radio_b. */
dbi_direct = BIT(3) | BIT(2);
- if (bitmask != BMASKDWORD) {
+ if (bitmask != MASKDWORD) {
if (rtlhal->during_mac1init_radioa ||
rtlhal->during_mac0init_radiob)
originalvalue = rtl92de_read_dword_dbi(hw,
@@ -275,20 +276,20 @@ static u32 _rtl92d_phy_rf_serial_read(struct ieee80211_hw *hw,
u32 retvalue;
newoffset = offset;
- tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD);
+ tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD);
if (rfpath == RF90_PATH_A)
tmplong2 = tmplong;
else
- tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, BMASKDWORD);
+ tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD);
tmplong2 = (tmplong2 & (~BLSSIREADADDRESS)) |
(newoffset << 23) | BLSSIREADEDGE;
- rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD,
+ rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD,
tmplong & (~BLSSIREADEDGE));
udelay(10);
- rtl_set_bbreg(hw, pphyreg->rfhssi_para2, BMASKDWORD, tmplong2);
+ rtl_set_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD, tmplong2);
udelay(50);
udelay(50);
- rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD,
+ rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD,
tmplong | BLSSIREADEDGE);
udelay(10);
if (rfpath == RF90_PATH_A)
@@ -321,7 +322,7 @@ static void _rtl92d_phy_rf_serial_write(struct ieee80211_hw *hw,
newoffset = offset;
/* T65 RF */
data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff;
- rtl_set_bbreg(hw, pphyreg->rf3wire_offset, BMASKDWORD, data_and_addr);
+ rtl_set_bbreg(hw, pphyreg->rf3wire_offset, MASKDWORD, data_and_addr);
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFW-%d Addr[0x%x]=0x%x\n",
rfpath, pphyreg->rf3wire_offset, data_and_addr);
}
@@ -362,7 +363,7 @@ void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath,
return;
spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
if (rtlphy->rf_mode != RF_OP_BY_FW) {
- if (bitmask != BRFREGOFFSETMASK) {
+ if (bitmask != RFREG_OFFSET_MASK) {
original_value = _rtl92d_phy_rf_serial_read(hw,
rfpath, regaddr);
bitshift = _rtl92d_phy_calculate_bit_shift(bitmask);
@@ -567,19 +568,8 @@ static bool _rtl92d_phy_config_bb_with_headerfile(struct ieee80211_hw *hw,
" ===> phy:Rtl819XPHY_REG_Array_PG\n");
if (configtype == BASEBAND_CONFIG_PHY_REG) {
for (i = 0; i < phy_reg_arraylen; i = i + 2) {
- if (phy_regarray_table[i] == 0xfe)
- mdelay(50);
- else if (phy_regarray_table[i] == 0xfd)
- mdelay(5);
- else if (phy_regarray_table[i] == 0xfc)
- mdelay(1);
- else if (phy_regarray_table[i] == 0xfb)
- udelay(50);
- else if (phy_regarray_table[i] == 0xfa)
- udelay(5);
- else if (phy_regarray_table[i] == 0xf9)
- udelay(1);
- rtl_set_bbreg(hw, phy_regarray_table[i], BMASKDWORD,
+ rtl_addr_delay(phy_regarray_table[i]);
+ rtl_set_bbreg(hw, phy_regarray_table[i], MASKDWORD,
phy_regarray_table[i + 1]);
udelay(1);
RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
@@ -591,7 +581,7 @@ static bool _rtl92d_phy_config_bb_with_headerfile(struct ieee80211_hw *hw,
if (rtlhal->interfaceindex == 0) {
for (i = 0; i < agctab_arraylen; i = i + 2) {
rtl_set_bbreg(hw, agctab_array_table[i],
- BMASKDWORD,
+ MASKDWORD,
agctab_array_table[i + 1]);
/* Add 1us delay between BB/RF register
* setting. */
@@ -607,7 +597,7 @@ static bool _rtl92d_phy_config_bb_with_headerfile(struct ieee80211_hw *hw,
if (rtlhal->current_bandtype == BAND_ON_2_4G) {
for (i = 0; i < agctab_arraylen; i = i + 2) {
rtl_set_bbreg(hw, agctab_array_table[i],
- BMASKDWORD,
+ MASKDWORD,
agctab_array_table[i + 1]);
/* Add 1us delay between BB/RF register
* setting. */
@@ -623,7 +613,7 @@ static bool _rtl92d_phy_config_bb_with_headerfile(struct ieee80211_hw *hw,
for (i = 0; i < agctab_5garraylen; i = i + 2) {
rtl_set_bbreg(hw,
agctab_5garray_table[i],
- BMASKDWORD,
+ MASKDWORD,
agctab_5garray_table[i + 1]);
/* Add 1us delay between BB/RF registeri
* setting. */
@@ -705,18 +695,7 @@ static bool _rtl92d_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw,
phy_regarray_table_pg = rtl8192de_phy_reg_array_pg;
if (configtype == BASEBAND_CONFIG_PHY_REG) {
for (i = 0; i < phy_regarray_pg_len; i = i + 3) {
- if (phy_regarray_table_pg[i] == 0xfe)
- mdelay(50);
- else if (phy_regarray_table_pg[i] == 0xfd)
- mdelay(5);
- else if (phy_regarray_table_pg[i] == 0xfc)
- mdelay(1);
- else if (phy_regarray_table_pg[i] == 0xfb)
- udelay(50);
- else if (phy_regarray_table_pg[i] == 0xfa)
- udelay(5);
- else if (phy_regarray_table_pg[i] == 0xf9)
- udelay(1);
+ rtl_addr_delay(phy_regarray_table_pg[i]);
_rtl92d_store_pwrindex_diffrate_offset(hw,
phy_regarray_table_pg[i],
phy_regarray_table_pg[i + 1],
@@ -843,54 +822,16 @@ bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
switch (rfpath) {
case RF90_PATH_A:
for (i = 0; i < radioa_arraylen; i = i + 2) {
- if (radioa_array_table[i] == 0xfe) {
- mdelay(50);
- } else if (radioa_array_table[i] == 0xfd) {
- /* delay_ms(5); */
- mdelay(5);
- } else if (radioa_array_table[i] == 0xfc) {
- /* delay_ms(1); */
- mdelay(1);
- } else if (radioa_array_table[i] == 0xfb) {
- udelay(50);
- } else if (radioa_array_table[i] == 0xfa) {
- udelay(5);
- } else if (radioa_array_table[i] == 0xf9) {
- udelay(1);
- } else {
- rtl_set_rfreg(hw, rfpath, radioa_array_table[i],
- BRFREGOFFSETMASK,
- radioa_array_table[i + 1]);
- /* Add 1us delay between BB/RF register set. */
- udelay(1);
- }
+ rtl_rfreg_delay(hw, rfpath, radioa_array_table[i],
+ RFREG_OFFSET_MASK,
+ radioa_array_table[i + 1]);
}
break;
case RF90_PATH_B:
for (i = 0; i < radiob_arraylen; i = i + 2) {
- if (radiob_array_table[i] == 0xfe) {
- /* Delay specific ms. Only RF configuration
- * requires delay. */
- mdelay(50);
- } else if (radiob_array_table[i] == 0xfd) {
- /* delay_ms(5); */
- mdelay(5);
- } else if (radiob_array_table[i] == 0xfc) {
- /* delay_ms(1); */
- mdelay(1);
- } else if (radiob_array_table[i] == 0xfb) {
- udelay(50);
- } else if (radiob_array_table[i] == 0xfa) {
- udelay(5);
- } else if (radiob_array_table[i] == 0xf9) {
- udelay(1);
- } else {
- rtl_set_rfreg(hw, rfpath, radiob_array_table[i],
- BRFREGOFFSETMASK,
- radiob_array_table[i + 1]);
- /* Add 1us delay between BB/RF register set. */
- udelay(1);
- }
+ rtl_rfreg_delay(hw, rfpath, radiob_array_table[i],
+ RFREG_OFFSET_MASK,
+ radiob_array_table[i + 1]);
}
break;
case RF90_PATH_C:
@@ -911,13 +852,13 @@ void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw)
struct rtl_phy *rtlphy = &(rtlpriv->phy);
rtlphy->default_initialgain[0] =
- (u8) rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, BMASKBYTE0);
+ (u8) rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0);
rtlphy->default_initialgain[1] =
- (u8) rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, BMASKBYTE0);
+ (u8) rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0);
rtlphy->default_initialgain[2] =
- (u8) rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, BMASKBYTE0);
+ (u8) rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, MASKBYTE0);
rtlphy->default_initialgain[3] =
- (u8) rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, BMASKBYTE0);
+ (u8) rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, MASKBYTE0);
RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
"Default initial gain (c50=0x%x, c58=0x%x, c60=0x%x, c68=0x%x\n",
rtlphy->default_initialgain[0],
@@ -925,9 +866,9 @@ void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw)
rtlphy->default_initialgain[2],
rtlphy->default_initialgain[3]);
rtlphy->framesync = (u8)rtl_get_bbreg(hw, ROFDM0_RXDETECTOR3,
- BMASKBYTE0);
+ MASKBYTE0);
rtlphy->framesync_c34 = rtl_get_bbreg(hw, ROFDM0_RXDETECTOR2,
- BMASKDWORD);
+ MASKDWORD);
RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
"Default framesync (0x%x) = 0x%x\n",
ROFDM0_RXDETECTOR3, rtlphy->framesync);
@@ -1106,7 +1047,7 @@ static void _rtl92d_phy_stop_trx_before_changeband(struct ieee80211_hw *hw)
{
rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0);
rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0);
- rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x00);
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0x00);
rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x0);
}
@@ -1168,7 +1109,7 @@ static void _rtl92d_phy_reload_imr_setting(struct ieee80211_hw *hw,
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
u32 imr_num = MAX_RF_IMR_INDEX;
- u32 rfmask = BRFREGOFFSETMASK;
+ u32 rfmask = RFREG_OFFSET_MASK;
u8 group, i;
unsigned long flag = 0;
@@ -1211,7 +1152,7 @@ static void _rtl92d_phy_reload_imr_setting(struct ieee80211_hw *hw,
for (i = 0; i < imr_num; i++) {
rtl_set_rfreg(hw, (enum radio_path)rfpath,
rf_reg_for_5g_swchnl_normal[i],
- BRFREGOFFSETMASK,
+ RFREG_OFFSET_MASK,
rf_imr_param_normal[0][0][i]);
}
rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4,
@@ -1329,7 +1270,7 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel)
if (i == 0 && (rtlhal->macphymode == DUALMAC_DUALPHY)) {
rtl_set_rfreg(hw, (enum radio_path)path,
rf_reg_for_c_cut_5g[i],
- BRFREGOFFSETMASK, 0xE439D);
+ RFREG_OFFSET_MASK, 0xE439D);
} else if (rf_reg_for_c_cut_5g[i] == RF_SYN_G4) {
u4tmp2 = (rf_reg_pram_c_5g[index][i] &
0x7FF) | (u4tmp << 11);
@@ -1337,11 +1278,11 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel)
u4tmp2 &= ~(BIT(7) | BIT(6));
rtl_set_rfreg(hw, (enum radio_path)path,
rf_reg_for_c_cut_5g[i],
- BRFREGOFFSETMASK, u4tmp2);
+ RFREG_OFFSET_MASK, u4tmp2);
} else {
rtl_set_rfreg(hw, (enum radio_path)path,
rf_reg_for_c_cut_5g[i],
- BRFREGOFFSETMASK,
+ RFREG_OFFSET_MASK,
rf_reg_pram_c_5g[index][i]);
}
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
@@ -1351,7 +1292,7 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel)
path, index,
rtl_get_rfreg(hw, (enum radio_path)path,
rf_reg_for_c_cut_5g[i],
- BRFREGOFFSETMASK));
+ RFREG_OFFSET_MASK));
}
if (need_pwr_down)
_rtl92d_phy_restore_rf_env(hw, path, &u4regvalue);
@@ -1381,7 +1322,7 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel)
i++) {
rtl_set_rfreg(hw, rfpath,
rf_for_c_cut_5g_internal_pa[i],
- BRFREGOFFSETMASK,
+ RFREG_OFFSET_MASK,
rf_pram_c_5g_int_pa[index][i]);
RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD,
"offset 0x%x value 0x%x path %d index %d\n",
@@ -1422,13 +1363,13 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel)
if (rf_reg_for_c_cut_2g[i] == RF_SYN_G7)
rtl_set_rfreg(hw, (enum radio_path)path,
rf_reg_for_c_cut_2g[i],
- BRFREGOFFSETMASK,
+ RFREG_OFFSET_MASK,
(rf_reg_param_for_c_cut_2g[index][i] |
BIT(17)));
else
rtl_set_rfreg(hw, (enum radio_path)path,
rf_reg_for_c_cut_2g[i],
- BRFREGOFFSETMASK,
+ RFREG_OFFSET_MASK,
rf_reg_param_for_c_cut_2g
[index][i]);
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
@@ -1438,14 +1379,14 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel)
rf_reg_mask_for_c_cut_2g[i], path, index,
rtl_get_rfreg(hw, (enum radio_path)path,
rf_reg_for_c_cut_2g[i],
- BRFREGOFFSETMASK));
+ RFREG_OFFSET_MASK));
}
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"cosa ver 3 set RF-B, 2G, 0x28 = 0x%x !!\n",
rf_syn_g4_for_c_cut_2g | (u4tmp << 11));
rtl_set_rfreg(hw, (enum radio_path)path, RF_SYN_G4,
- BRFREGOFFSETMASK,
+ RFREG_OFFSET_MASK,
rf_syn_g4_for_c_cut_2g | (u4tmp << 11));
if (need_pwr_down)
_rtl92d_phy_restore_rf_env(hw, path, &u4regvalue);
@@ -1493,41 +1434,41 @@ static u8 _rtl92d_phy_patha_iqk(struct ieee80211_hw *hw, bool configpathb)
/* path-A IQK setting */
RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A IQK setting!\n");
if (rtlhal->interfaceindex == 0) {
- rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x10008c1f);
- rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x10008c1f);
+ rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x10008c1f);
+ rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x10008c1f);
} else {
- rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x10008c22);
- rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x10008c22);
+ rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x10008c22);
+ rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x10008c22);
}
- rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82140102);
- rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x28160206);
+ rtl_set_bbreg(hw, 0xe38, MASKDWORD, 0x82140102);
+ rtl_set_bbreg(hw, 0xe3c, MASKDWORD, 0x28160206);
/* path-B IQK setting */
if (configpathb) {
- rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x10008c22);
- rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x10008c22);
- rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82140102);
- rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x28160206);
+ rtl_set_bbreg(hw, 0xe50, MASKDWORD, 0x10008c22);
+ rtl_set_bbreg(hw, 0xe54, MASKDWORD, 0x10008c22);
+ rtl_set_bbreg(hw, 0xe58, MASKDWORD, 0x82140102);
+ rtl_set_bbreg(hw, 0xe5c, MASKDWORD, 0x28160206);
}
/* LO calibration setting */
RTPRINT(rtlpriv, FINIT, INIT_IQK, "LO calibration setting!\n");
- rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911);
+ rtl_set_bbreg(hw, 0xe4c, MASKDWORD, 0x00462911);
/* One shot, path A LOK & IQK */
RTPRINT(rtlpriv, FINIT, INIT_IQK, "One shot, path A LOK & IQK!\n");
- rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf9000000);
- rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000);
+ rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf9000000);
+ rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf8000000);
/* delay x ms */
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"Delay %d ms for One shot, path A LOK & IQK\n",
IQK_DELAY_TIME);
mdelay(IQK_DELAY_TIME);
/* Check failed */
- regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
+ regeac = rtl_get_bbreg(hw, 0xeac, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac);
- rege94 = rtl_get_bbreg(hw, 0xe94, BMASKDWORD);
+ rege94 = rtl_get_bbreg(hw, 0xe94, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe94 = 0x%x\n", rege94);
- rege9c = rtl_get_bbreg(hw, 0xe9c, BMASKDWORD);
+ rege9c = rtl_get_bbreg(hw, 0xe9c, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe9c = 0x%x\n", rege9c);
- regea4 = rtl_get_bbreg(hw, 0xea4, BMASKDWORD);
+ regea4 = rtl_get_bbreg(hw, 0xea4, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xea4 = 0x%x\n", regea4);
if (!(regeac & BIT(28)) && (((rege94 & 0x03FF0000) >> 16) != 0x142) &&
(((rege9c & 0x03FF0000) >> 16) != 0x42))
@@ -1563,42 +1504,42 @@ static u8 _rtl92d_phy_patha_iqk_5g_normal(struct ieee80211_hw *hw,
RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK!\n");
/* path-A IQK setting */
RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A IQK setting!\n");
- rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x18008c1f);
- rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x18008c1f);
- rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82140307);
- rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x68160960);
+ rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x18008c1f);
+ rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x18008c1f);
+ rtl_set_bbreg(hw, 0xe38, MASKDWORD, 0x82140307);
+ rtl_set_bbreg(hw, 0xe3c, MASKDWORD, 0x68160960);
/* path-B IQK setting */
if (configpathb) {
- rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x18008c2f);
- rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x18008c2f);
- rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82110000);
- rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x68110000);
+ rtl_set_bbreg(hw, 0xe50, MASKDWORD, 0x18008c2f);
+ rtl_set_bbreg(hw, 0xe54, MASKDWORD, 0x18008c2f);
+ rtl_set_bbreg(hw, 0xe58, MASKDWORD, 0x82110000);
+ rtl_set_bbreg(hw, 0xe5c, MASKDWORD, 0x68110000);
}
/* LO calibration setting */
RTPRINT(rtlpriv, FINIT, INIT_IQK, "LO calibration setting!\n");
- rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911);
+ rtl_set_bbreg(hw, 0xe4c, MASKDWORD, 0x00462911);
/* path-A PA on */
- rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, 0x07000f60);
- rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BMASKDWORD, 0x66e60e30);
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, MASKDWORD, 0x07000f60);
+ rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, MASKDWORD, 0x66e60e30);
for (i = 0; i < retrycount; i++) {
/* One shot, path A LOK & IQK */
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"One shot, path A LOK & IQK!\n");
- rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf9000000);
- rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000);
+ rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf9000000);
+ rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf8000000);
/* delay x ms */
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"Delay %d ms for One shot, path A LOK & IQK.\n",
IQK_DELAY_TIME);
mdelay(IQK_DELAY_TIME * 10);
/* Check failed */
- regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
+ regeac = rtl_get_bbreg(hw, 0xeac, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac);
- rege94 = rtl_get_bbreg(hw, 0xe94, BMASKDWORD);
+ rege94 = rtl_get_bbreg(hw, 0xe94, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe94 = 0x%x\n", rege94);
- rege9c = rtl_get_bbreg(hw, 0xe9c, BMASKDWORD);
+ rege9c = rtl_get_bbreg(hw, 0xe9c, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe9c = 0x%x\n", rege9c);
- regea4 = rtl_get_bbreg(hw, 0xea4, BMASKDWORD);
+ regea4 = rtl_get_bbreg(hw, 0xea4, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xea4 = 0x%x\n", regea4);
if (!(regeac & TxOKBit) &&
(((rege94 & 0x03FF0000) >> 16) != 0x142)) {
@@ -1620,9 +1561,9 @@ static u8 _rtl92d_phy_patha_iqk_5g_normal(struct ieee80211_hw *hw,
}
}
/* path A PA off */
- rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD,
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, MASKDWORD,
rtlphy->iqk_bb_backup[0]);
- rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BMASKDWORD,
+ rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, MASKDWORD,
rtlphy->iqk_bb_backup[1]);
return result;
}
@@ -1637,22 +1578,22 @@ static u8 _rtl92d_phy_pathb_iqk(struct ieee80211_hw *hw)
RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK!\n");
/* One shot, path B LOK & IQK */
RTPRINT(rtlpriv, FINIT, INIT_IQK, "One shot, path A LOK & IQK!\n");
- rtl_set_bbreg(hw, 0xe60, BMASKDWORD, 0x00000002);
- rtl_set_bbreg(hw, 0xe60, BMASKDWORD, 0x00000000);
+ rtl_set_bbreg(hw, 0xe60, MASKDWORD, 0x00000002);
+ rtl_set_bbreg(hw, 0xe60, MASKDWORD, 0x00000000);
/* delay x ms */
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"Delay %d ms for One shot, path B LOK & IQK\n", IQK_DELAY_TIME);
mdelay(IQK_DELAY_TIME);
/* Check failed */
- regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
+ regeac = rtl_get_bbreg(hw, 0xeac, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac);
- regeb4 = rtl_get_bbreg(hw, 0xeb4, BMASKDWORD);
+ regeb4 = rtl_get_bbreg(hw, 0xeb4, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeb4 = 0x%x\n", regeb4);
- regebc = rtl_get_bbreg(hw, 0xebc, BMASKDWORD);
+ regebc = rtl_get_bbreg(hw, 0xebc, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xebc = 0x%x\n", regebc);
- regec4 = rtl_get_bbreg(hw, 0xec4, BMASKDWORD);
+ regec4 = rtl_get_bbreg(hw, 0xec4, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xec4 = 0x%x\n", regec4);
- regecc = rtl_get_bbreg(hw, 0xecc, BMASKDWORD);
+ regecc = rtl_get_bbreg(hw, 0xecc, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xecc = 0x%x\n", regecc);
if (!(regeac & BIT(31)) && (((regeb4 & 0x03FF0000) >> 16) != 0x142) &&
(((regebc & 0x03FF0000) >> 16) != 0x42))
@@ -1680,31 +1621,31 @@ static u8 _rtl92d_phy_pathb_iqk_5g_normal(struct ieee80211_hw *hw)
RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK!\n");
/* path-A IQK setting */
RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A IQK setting!\n");
- rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x18008c1f);
- rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x18008c1f);
- rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82110000);
- rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x68110000);
+ rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x18008c1f);
+ rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x18008c1f);
+ rtl_set_bbreg(hw, 0xe38, MASKDWORD, 0x82110000);
+ rtl_set_bbreg(hw, 0xe3c, MASKDWORD, 0x68110000);
/* path-B IQK setting */
- rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x18008c2f);
- rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x18008c2f);
- rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82140307);
- rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x68160960);
+ rtl_set_bbreg(hw, 0xe50, MASKDWORD, 0x18008c2f);
+ rtl_set_bbreg(hw, 0xe54, MASKDWORD, 0x18008c2f);
+ rtl_set_bbreg(hw, 0xe58, MASKDWORD, 0x82140307);
+ rtl_set_bbreg(hw, 0xe5c, MASKDWORD, 0x68160960);
/* LO calibration setting */
RTPRINT(rtlpriv, FINIT, INIT_IQK, "LO calibration setting!\n");
- rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911);
+ rtl_set_bbreg(hw, 0xe4c, MASKDWORD, 0x00462911);
/* path-B PA on */
- rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, 0x0f600700);
- rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BMASKDWORD, 0x061f0d30);
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, MASKDWORD, 0x0f600700);
+ rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, MASKDWORD, 0x061f0d30);
for (i = 0; i < retrycount; i++) {
/* One shot, path B LOK & IQK */
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"One shot, path A LOK & IQK!\n");
- rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xfa000000);
- rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000);
+ rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xfa000000);
+ rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf8000000);
/* delay x ms */
RTPRINT(rtlpriv, FINIT, INIT_IQK,
@@ -1712,15 +1653,15 @@ static u8 _rtl92d_phy_pathb_iqk_5g_normal(struct ieee80211_hw *hw)
mdelay(IQK_DELAY_TIME * 10);
/* Check failed */
- regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD);
+ regeac = rtl_get_bbreg(hw, 0xeac, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac);
- regeb4 = rtl_get_bbreg(hw, 0xeb4, BMASKDWORD);
+ regeb4 = rtl_get_bbreg(hw, 0xeb4, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeb4 = 0x%x\n", regeb4);
- regebc = rtl_get_bbreg(hw, 0xebc, BMASKDWORD);
+ regebc = rtl_get_bbreg(hw, 0xebc, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xebc = 0x%x\n", regebc);
- regec4 = rtl_get_bbreg(hw, 0xec4, BMASKDWORD);
+ regec4 = rtl_get_bbreg(hw, 0xec4, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xec4 = 0x%x\n", regec4);
- regecc = rtl_get_bbreg(hw, 0xecc, BMASKDWORD);
+ regecc = rtl_get_bbreg(hw, 0xecc, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xecc = 0x%x\n", regecc);
if (!(regeac & BIT(31)) &&
(((regeb4 & 0x03FF0000) >> 16) != 0x142))
@@ -1738,9 +1679,9 @@ static u8 _rtl92d_phy_pathb_iqk_5g_normal(struct ieee80211_hw *hw)
}
/* path B PA off */
- rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD,
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, MASKDWORD,
rtlphy->iqk_bb_backup[0]);
- rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BMASKDWORD,
+ rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, MASKDWORD,
rtlphy->iqk_bb_backup[2]);
return result;
}
@@ -1754,7 +1695,7 @@ static void _rtl92d_phy_save_adda_registers(struct ieee80211_hw *hw,
RTPRINT(rtlpriv, FINIT, INIT_IQK, "Save ADDA parameters.\n");
for (i = 0; i < regnum; i++)
- adda_backup[i] = rtl_get_bbreg(hw, adda_reg[i], BMASKDWORD);
+ adda_backup[i] = rtl_get_bbreg(hw, adda_reg[i], MASKDWORD);
}
static void _rtl92d_phy_save_mac_registers(struct ieee80211_hw *hw,
@@ -1779,7 +1720,7 @@ static void _rtl92d_phy_reload_adda_registers(struct ieee80211_hw *hw,
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"Reload ADDA power saving parameters !\n");
for (i = 0; i < regnum; i++)
- rtl_set_bbreg(hw, adda_reg[i], BMASKDWORD, adda_backup[i]);
+ rtl_set_bbreg(hw, adda_reg[i], MASKDWORD, adda_backup[i]);
}
static void _rtl92d_phy_reload_mac_registers(struct ieee80211_hw *hw,
@@ -1807,7 +1748,7 @@ static void _rtl92d_phy_path_adda_on(struct ieee80211_hw *hw,
pathon = rtlpriv->rtlhal.interfaceindex == 0 ?
0x04db25a4 : 0x0b1b25a4;
for (i = 0; i < IQK_ADDA_REG_NUM; i++)
- rtl_set_bbreg(hw, adda_reg[i], BMASKDWORD, pathon);
+ rtl_set_bbreg(hw, adda_reg[i], MASKDWORD, pathon);
}
static void _rtl92d_phy_mac_setting_calibration(struct ieee80211_hw *hw,
@@ -1830,9 +1771,9 @@ static void _rtl92d_phy_patha_standby(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A standby mode!\n");
- rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x0);
- rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, BMASKDWORD, 0x00010000);
- rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000);
+ rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x0);
+ rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, MASKDWORD, 0x00010000);
+ rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000);
}
static void _rtl92d_phy_pimode_switch(struct ieee80211_hw *hw, bool pi_mode)
@@ -1843,8 +1784,8 @@ static void _rtl92d_phy_pimode_switch(struct ieee80211_hw *hw, bool pi_mode)
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"BB Switch to %s mode!\n", pi_mode ? "PI" : "SI");
mode = pi_mode ? 0x01000100 : 0x01000000;
- rtl_set_bbreg(hw, 0x820, BMASKDWORD, mode);
- rtl_set_bbreg(hw, 0x828, BMASKDWORD, mode);
+ rtl_set_bbreg(hw, 0x820, MASKDWORD, mode);
+ rtl_set_bbreg(hw, 0x828, MASKDWORD, mode);
}
static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8],
@@ -1875,7 +1816,7 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8],
RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK for 2.4G :Start!!!\n");
if (t == 0) {
- bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, BMASKDWORD);
+ bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "==>0x%08x\n", bbvalue);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQ Calibration for %s\n",
is2t ? "2T2R" : "1T1R");
@@ -1898,40 +1839,40 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8],
_rtl92d_phy_pimode_switch(hw, true);
rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(24), 0x00);
- rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKDWORD, 0x03a05600);
- rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, BMASKDWORD, 0x000800e4);
- rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, BMASKDWORD, 0x22204000);
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKDWORD, 0x03a05600);
+ rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, MASKDWORD, 0x000800e4);
+ rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, MASKDWORD, 0x22204000);
rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xf00000, 0x0f);
if (is2t) {
- rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, BMASKDWORD,
+ rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, MASKDWORD,
0x00010000);
- rtl_set_bbreg(hw, RFPGA0_XB_LSSIPARAMETER, BMASKDWORD,
+ rtl_set_bbreg(hw, RFPGA0_XB_LSSIPARAMETER, MASKDWORD,
0x00010000);
}
/* MAC settings */
_rtl92d_phy_mac_setting_calibration(hw, iqk_mac_reg,
rtlphy->iqk_mac_backup);
/* Page B init */
- rtl_set_bbreg(hw, 0xb68, BMASKDWORD, 0x0f600000);
+ rtl_set_bbreg(hw, 0xb68, MASKDWORD, 0x0f600000);
if (is2t)
- rtl_set_bbreg(hw, 0xb6c, BMASKDWORD, 0x0f600000);
+ rtl_set_bbreg(hw, 0xb6c, MASKDWORD, 0x0f600000);
/* IQ calibration setting */
RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK setting!\n");
- rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000);
- rtl_set_bbreg(hw, 0xe40, BMASKDWORD, 0x01007c00);
- rtl_set_bbreg(hw, 0xe44, BMASKDWORD, 0x01004800);
+ rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000);
+ rtl_set_bbreg(hw, 0xe40, MASKDWORD, 0x01007c00);
+ rtl_set_bbreg(hw, 0xe44, MASKDWORD, 0x01004800);
for (i = 0; i < retrycount; i++) {
patha_ok = _rtl92d_phy_patha_iqk(hw, is2t);
if (patha_ok == 0x03) {
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"Path A IQK Success!!\n");
- result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) &
+ result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) &
+ result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][2] = (rtl_get_bbreg(hw, 0xea4, BMASKDWORD) &
+ result[t][2] = (rtl_get_bbreg(hw, 0xea4, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][3] = (rtl_get_bbreg(hw, 0xeac, BMASKDWORD) &
+ result[t][3] = (rtl_get_bbreg(hw, 0xeac, MASKDWORD) &
0x3FF0000) >> 16;
break;
} else if (i == (retrycount - 1) && patha_ok == 0x01) {
@@ -1939,9 +1880,9 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8],
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"Path A IQK Only Tx Success!!\n");
- result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) &
+ result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) &
+ result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) &
0x3FF0000) >> 16;
}
}
@@ -1957,22 +1898,22 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8],
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"Path B IQK Success!!\n");
result[t][4] = (rtl_get_bbreg(hw, 0xeb4,
- BMASKDWORD) & 0x3FF0000) >> 16;
+ MASKDWORD) & 0x3FF0000) >> 16;
result[t][5] = (rtl_get_bbreg(hw, 0xebc,
- BMASKDWORD) & 0x3FF0000) >> 16;
+ MASKDWORD) & 0x3FF0000) >> 16;
result[t][6] = (rtl_get_bbreg(hw, 0xec4,
- BMASKDWORD) & 0x3FF0000) >> 16;
+ MASKDWORD) & 0x3FF0000) >> 16;
result[t][7] = (rtl_get_bbreg(hw, 0xecc,
- BMASKDWORD) & 0x3FF0000) >> 16;
+ MASKDWORD) & 0x3FF0000) >> 16;
break;
} else if (i == (retrycount - 1) && pathb_ok == 0x01) {
/* Tx IQK OK */
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"Path B Only Tx IQK Success!!\n");
result[t][4] = (rtl_get_bbreg(hw, 0xeb4,
- BMASKDWORD) & 0x3FF0000) >> 16;
+ MASKDWORD) & 0x3FF0000) >> 16;
result[t][5] = (rtl_get_bbreg(hw, 0xebc,
- BMASKDWORD) & 0x3FF0000) >> 16;
+ MASKDWORD) & 0x3FF0000) >> 16;
}
}
if (0x00 == pathb_ok)
@@ -1984,7 +1925,7 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8],
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"IQK:Back to BB mode, load original value!\n");
- rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0);
+ rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0);
if (t != 0) {
/* Switch back BB to SI mode after finish IQ Calibration. */
if (!rtlphy->rfpi_enable)
@@ -2004,8 +1945,8 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8],
rtlphy->iqk_bb_backup,
IQK_BB_REG_NUM - 1);
/* load 0xe30 IQC default value */
- rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x01008c00);
- rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x01008c00);
+ rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x01008c00);
+ rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x01008c00);
}
RTPRINT(rtlpriv, FINIT, INIT_IQK, "<==\n");
}
@@ -2042,7 +1983,7 @@ static void _rtl92d_phy_iq_calibrate_5g_normal(struct ieee80211_hw *hw,
RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK for 5G NORMAL:Start!!!\n");
mdelay(IQK_DELAY_TIME * 20);
if (t == 0) {
- bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, BMASKDWORD);
+ bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, MASKDWORD);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "==>0x%08x\n", bbvalue);
RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQ Calibration for %s\n",
is2t ? "2T2R" : "1T1R");
@@ -2072,38 +2013,38 @@ static void _rtl92d_phy_iq_calibrate_5g_normal(struct ieee80211_hw *hw,
if (!rtlphy->rfpi_enable)
_rtl92d_phy_pimode_switch(hw, true);
rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(24), 0x00);
- rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKDWORD, 0x03a05600);
- rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, BMASKDWORD, 0x000800e4);
- rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, BMASKDWORD, 0x22208000);
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKDWORD, 0x03a05600);
+ rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, MASKDWORD, 0x000800e4);
+ rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, MASKDWORD, 0x22208000);
rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xf00000, 0x0f);
/* Page B init */
- rtl_set_bbreg(hw, 0xb68, BMASKDWORD, 0x0f600000);
+ rtl_set_bbreg(hw, 0xb68, MASKDWORD, 0x0f600000);
if (is2t)
- rtl_set_bbreg(hw, 0xb6c, BMASKDWORD, 0x0f600000);
+ rtl_set_bbreg(hw, 0xb6c, MASKDWORD, 0x0f600000);
/* IQ calibration setting */
RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK setting!\n");
- rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000);
- rtl_set_bbreg(hw, 0xe40, BMASKDWORD, 0x10007c00);
- rtl_set_bbreg(hw, 0xe44, BMASKDWORD, 0x01004800);
+ rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000);
+ rtl_set_bbreg(hw, 0xe40, MASKDWORD, 0x10007c00);
+ rtl_set_bbreg(hw, 0xe44, MASKDWORD, 0x01004800);
patha_ok = _rtl92d_phy_patha_iqk_5g_normal(hw, is2t);
if (patha_ok == 0x03) {
RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Success!!\n");
- result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) &
+ result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) &
+ result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][2] = (rtl_get_bbreg(hw, 0xea4, BMASKDWORD) &
+ result[t][2] = (rtl_get_bbreg(hw, 0xea4, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][3] = (rtl_get_bbreg(hw, 0xeac, BMASKDWORD) &
+ result[t][3] = (rtl_get_bbreg(hw, 0xeac, MASKDWORD) &
0x3FF0000) >> 16;
} else if (patha_ok == 0x01) { /* Tx IQK OK */
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"Path A IQK Only Tx Success!!\n");
- result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) &
+ result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) &
+ result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) &
0x3FF0000) >> 16;
} else {
RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Fail!!\n");
@@ -2116,20 +2057,20 @@ static void _rtl92d_phy_iq_calibrate_5g_normal(struct ieee80211_hw *hw,
if (pathb_ok == 0x03) {
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"Path B IQK Success!!\n");
- result[t][4] = (rtl_get_bbreg(hw, 0xeb4, BMASKDWORD) &
+ result[t][4] = (rtl_get_bbreg(hw, 0xeb4, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][5] = (rtl_get_bbreg(hw, 0xebc, BMASKDWORD) &
+ result[t][5] = (rtl_get_bbreg(hw, 0xebc, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][6] = (rtl_get_bbreg(hw, 0xec4, BMASKDWORD) &
+ result[t][6] = (rtl_get_bbreg(hw, 0xec4, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][7] = (rtl_get_bbreg(hw, 0xecc, BMASKDWORD) &
+ result[t][7] = (rtl_get_bbreg(hw, 0xecc, MASKDWORD) &
0x3FF0000) >> 16;
} else if (pathb_ok == 0x01) { /* Tx IQK OK */
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"Path B Only Tx IQK Success!!\n");
- result[t][4] = (rtl_get_bbreg(hw, 0xeb4, BMASKDWORD) &
+ result[t][4] = (rtl_get_bbreg(hw, 0xeb4, MASKDWORD) &
0x3FF0000) >> 16;
- result[t][5] = (rtl_get_bbreg(hw, 0xebc, BMASKDWORD) &
+ result[t][5] = (rtl_get_bbreg(hw, 0xebc, MASKDWORD) &
0x3FF0000) >> 16;
} else {
RTPRINT(rtlpriv, FINIT, INIT_IQK,
@@ -2140,7 +2081,7 @@ static void _rtl92d_phy_iq_calibrate_5g_normal(struct ieee80211_hw *hw,
/* Back to BB mode, load original value */
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"IQK:Back to BB mode, load original value!\n");
- rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0);
+ rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0);
if (t != 0) {
if (is2t)
_rtl92d_phy_reload_adda_registers(hw, iqk_bb_reg,
@@ -2240,7 +2181,7 @@ static void _rtl92d_phy_patha_fill_iqk_matrix(struct ieee80211_hw *hw,
return;
} else if (iqk_ok) {
oldval_0 = (rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE,
- BMASKDWORD) >> 22) & 0x3FF; /* OFDM0_D */
+ MASKDWORD) >> 22) & 0x3FF; /* OFDM0_D */
val_x = result[final_candidate][0];
if ((val_x & 0x00000200) != 0)
val_x = val_x | 0xFFFFFC00;
@@ -2271,7 +2212,7 @@ static void _rtl92d_phy_patha_fill_iqk_matrix(struct ieee80211_hw *hw,
((val_y * oldval_0 >> 7) & 0x1));
RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xC80 = 0x%x\n",
rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE,
- BMASKDWORD));
+ MASKDWORD));
if (txonly) {
RTPRINT(rtlpriv, FINIT, INIT_IQK, "only Tx OK\n");
return;
@@ -2299,7 +2240,7 @@ static void _rtl92d_phy_pathb_fill_iqk_matrix(struct ieee80211_hw *hw,
return;
} else if (iqk_ok) {
oldval_1 = (rtl_get_bbreg(hw, ROFDM0_XBTxIQIMBALANCE,
- BMASKDWORD) >> 22) & 0x3FF;
+ MASKDWORD) >> 22) & 0x3FF;
val_x = result[final_candidate][4];
if ((val_x & 0x00000200) != 0)
val_x = val_x | 0xFFFFFC00;
@@ -2657,7 +2598,7 @@ static void _rtl92d_phy_lc_calibrate_sw(struct ieee80211_hw *hw, bool is2t)
rf_mode[index] = rtl_read_byte(rtlpriv, offset);
/* 2. Set RF mode = standby mode */
rtl_set_rfreg(hw, (enum radio_path)index, RF_AC,
- BRFREGOFFSETMASK, 0x010000);
+ RFREG_OFFSET_MASK, 0x010000);
if (rtlpci->init_ready) {
/* switch CV-curve control by LC-calibration */
rtl_set_rfreg(hw, (enum radio_path)index, RF_SYN_G7,
@@ -2667,16 +2608,16 @@ static void _rtl92d_phy_lc_calibrate_sw(struct ieee80211_hw *hw, bool is2t)
0x08000, 0x01);
}
u4tmp = rtl_get_rfreg(hw, (enum radio_path)index, RF_SYN_G6,
- BRFREGOFFSETMASK);
+ RFREG_OFFSET_MASK);
while ((!(u4tmp & BIT(11))) && timecount <= timeout) {
mdelay(50);
timecount += 50;
u4tmp = rtl_get_rfreg(hw, (enum radio_path)index,
- RF_SYN_G6, BRFREGOFFSETMASK);
+ RF_SYN_G6, RFREG_OFFSET_MASK);
}
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"PHY_LCK finish delay for %d ms=2\n", timecount);
- u4tmp = rtl_get_rfreg(hw, index, RF_SYN_G4, BRFREGOFFSETMASK);
+ u4tmp = rtl_get_rfreg(hw, index, RF_SYN_G4, RFREG_OFFSET_MASK);
if (index == 0 && rtlhal->interfaceindex == 0) {
RTPRINT(rtlpriv, FINIT, INIT_IQK,
"path-A / 5G LCK\n");
@@ -2696,9 +2637,9 @@ static void _rtl92d_phy_lc_calibrate_sw(struct ieee80211_hw *hw, bool is2t)
0x7f, i);
rtl_set_rfreg(hw, (enum radio_path)index, 0x4D,
- BRFREGOFFSETMASK, 0x0);
+ RFREG_OFFSET_MASK, 0x0);
readval = rtl_get_rfreg(hw, (enum radio_path)index,
- 0x4F, BRFREGOFFSETMASK);
+ 0x4F, RFREG_OFFSET_MASK);
curvecount_val[2 * i + 1] = (readval & 0xfffe0) >> 5;
/* reg 0x4f [4:0] */
/* reg 0x50 [19:10] */
@@ -2912,7 +2853,7 @@ static bool _rtl92d_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw,
}
rtl_set_rfreg(hw, (enum radio_path)rfpath,
currentcmd->para1,
- BRFREGOFFSETMASK,
+ RFREG_OFFSET_MASK,
rtlphy->rfreg_chnlval[rfpath]);
_rtl92d_phy_reload_imr_setting(hw, channel,
rfpath);
@@ -2960,7 +2901,7 @@ u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw)
if (rtlhal->macphymode == SINGLEMAC_SINGLEPHY &&
rtlhal->bandset == BAND_ON_BOTH) {
ret_value = rtl_get_bbreg(hw, RFPGA0_XAB_RFPARAMETER,
- BMASKDWORD);
+ MASKDWORD);
if (rtlphy->current_channel > 14 && !(ret_value & BIT(0)))
rtl92d_phy_switch_wirelessband(hw, BAND_ON_5G);
else if (rtlphy->current_channel <= 14 && (ret_value & BIT(0)))
@@ -3112,7 +3053,7 @@ static void _rtl92d_phy_set_rfsleep(struct ieee80211_hw *hw)
/* a. TXPAUSE 0x522[7:0] = 0xFF Pause MAC TX queue */
rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF);
/* b. RF path 0 offset 0x00 = 0x00 disable RF */
- rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00);
+ rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00);
/* c. APSD_CTRL 0x600[7:0] = 0x40 */
rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40);
/* d. APSD_CTRL 0x600[7:0] = 0x00
@@ -3120,12 +3061,12 @@ static void _rtl92d_phy_set_rfsleep(struct ieee80211_hw *hw)
* RF path 0 offset 0x00 = 0x00
* APSD_CTRL 0x600[7:0] = 0x40
* */
- u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, BRFREGOFFSETMASK);
+ u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK);
while (u4btmp != 0 && delay > 0) {
rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x0);
- rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00);
+ rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00);
rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40);
- u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, BRFREGOFFSETMASK);
+ u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK);
delay--;
}
if (delay == 0) {
@@ -3468,9 +3409,9 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw)
/* 5G LAN ON */
rtl_set_bbreg(hw, 0xB30, 0x00F00000, 0xa);
/* TX BB gain shift*1,Just for testchip,0xc80,0xc88 */
- rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD,
+ rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, MASKDWORD,
0x40000100);
- rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD,
+ rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, MASKDWORD,
0x40000100);
if (rtlhal->macphymode == DUALMAC_DUALPHY) {
rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW,
@@ -3524,16 +3465,16 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw)
rtl_set_bbreg(hw, 0xB30, 0x00F00000, 0x0);
/* TX BB gain shift,Just for testchip,0xc80,0xc88 */
if (rtlefuse->internal_pa_5g[0])
- rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD,
+ rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, MASKDWORD,
0x2d4000b5);
else
- rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD,
+ rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, MASKDWORD,
0x20000080);
if (rtlefuse->internal_pa_5g[1])
- rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD,
+ rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, MASKDWORD,
0x2d4000b5);
else
- rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD,
+ rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, MASKDWORD,
0x20000080);
if (rtlhal->macphymode == DUALMAC_DUALPHY) {
rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW,
@@ -3560,8 +3501,8 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw)
}
}
/* update IQK related settings */
- rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, BMASKDWORD, 0x40000100);
- rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, BMASKDWORD, 0x40000100);
+ rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, MASKDWORD, 0x40000100);
+ rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, MASKDWORD, 0x40000100);
rtl_set_bbreg(hw, ROFDM0_XCTxAFE, 0xF0000000, 0x00);
rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(30) | BIT(28) |
BIT(26) | BIT(24), 0x00);
@@ -3590,7 +3531,7 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw)
/* DMDP */
if (rtlphy->rf_type == RF_1T1R) {
/* Use antenna 0,0xc04,0xd04 */
- rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x11);
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0x11);
rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x1);
/* enable ad/da clock1 for dual-phy reg0x888 */
@@ -3612,7 +3553,7 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw)
} else {
/* Single PHY */
/* Use antenna 0 & 1,0xc04,0xd04 */
- rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x33);
+ rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0x33);
rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x3);
/* disable ad/da clock1,0x888 */
rtl_set_bbreg(hw, RFPGA0_ADDALLOCKEN, BIT(12) | BIT(13), 0);
@@ -3620,9 +3561,9 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw)
for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath;
rfpath++) {
rtlphy->rfreg_chnlval[rfpath] = rtl_get_rfreg(hw, rfpath,
- RF_CHNLBW, BRFREGOFFSETMASK);
+ RF_CHNLBW, RFREG_OFFSET_MASK);
rtlphy->reg_rf3c[rfpath] = rtl_get_rfreg(hw, rfpath, 0x3C,
- BRFREGOFFSETMASK);
+ RFREG_OFFSET_MASK);
}
for (i = 0; i < 2; i++)
RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "RF 0x18 = 0x%x\n",
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h
index b7498c5bafc5..7f29b8d765b3 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h
@@ -1295,18 +1295,4 @@
#define BWORD1 0xc
#define BDWORD 0xf
-#define BMASKBYTE0 0xff
-#define BMASKBYTE1 0xff00
-#define BMASKBYTE2 0xff0000
-#define BMASKBYTE3 0xff000000
-#define BMASKHWORD 0xffff0000
-#define BMASKLWORD 0x0000ffff
-#define BMASKDWORD 0xffffffff
-#define BMASK12BITS 0xfff
-#define BMASKH4BITS 0xf0000000
-#define BMASKOFDM_D 0xffc00000
-#define BMASKCCK 0x3f3f3f3f
-
-#define BRFREGOFFSETMASK 0xfffff
-
#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/rf.c b/drivers/net/wireless/rtlwifi/rtl8192de/rf.c
index 20144e0b4142..6a6ac540d5b5 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/rf.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/rf.c
@@ -125,7 +125,7 @@ void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
}
tmpval = tx_agc[RF90_PATH_A] & 0xff;
- rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, BMASKBYTE1, tmpval);
+ rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, MASKBYTE1, tmpval);
RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
"CCK PWR 1M (rf-A) = 0x%x (reg 0x%x)\n",
tmpval, RTXAGC_A_CCK1_MCS32);
@@ -135,7 +135,7 @@ void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
"CCK PWR 2~11M (rf-A) = 0x%x (reg 0x%x)\n",
tmpval, RTXAGC_B_CCK11_A_CCK2_11);
tmpval = tx_agc[RF90_PATH_B] >> 24;
- rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, BMASKBYTE0, tmpval);
+ rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, MASKBYTE0, tmpval);
RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
"CCK PWR 11M (rf-B) = 0x%x (reg 0x%x)\n",
tmpval, RTXAGC_B_CCK11_A_CCK2_11);
@@ -360,7 +360,7 @@ static void _rtl92d_write_ofdm_power_reg(struct ieee80211_hw *hw,
regoffset = regoffset_a[index];
else
regoffset = regoffset_b[index];
- rtl_set_bbreg(hw, regoffset, BMASKDWORD, writeval);
+ rtl_set_bbreg(hw, regoffset, MASKDWORD, writeval);
RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
"Set 0x%x = %08x\n", regoffset, writeval);
if (((get_rf_type(rtlphy) == RF_2T2R) &&
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c
index 0eb0f4ae5920..99c2ab5dfceb 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c
@@ -545,7 +545,7 @@ static void _rtl92de_insert_emcontent(struct rtl_tcb_desc *ptcb_desc,
void rtl92de_tx_fill_desc(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr, u8 *pdesc_tx,
- struct ieee80211_tx_info *info,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
struct sk_buff *skb,
u8 hw_queue, struct rtl_tcb_desc *ptcb_desc)
@@ -786,7 +786,8 @@ void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw,
SET_TX_DESC_OWN(pdesc, 1);
}
-void rtl92de_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
+void rtl92de_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val)
{
if (istx) {
switch (desc_name) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h
index c1b5dfb79d53..fb5cf0634e8d 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h
@@ -728,8 +728,8 @@ struct rx_desc_92d {
} __packed;
void rtl92de_tx_fill_desc(struct ieee80211_hw *hw,
- struct ieee80211_hdr *hdr,
- u8 *pdesc, struct ieee80211_tx_info *info,
+ struct ieee80211_hdr *hdr, u8 *pdesc,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
struct sk_buff *skb, u8 hw_queue,
struct rtl_tcb_desc *ptcb_desc);
@@ -737,7 +737,8 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw,
struct rtl_stats *stats,
struct ieee80211_rx_status *rx_status,
u8 *pdesc, struct sk_buff *skb);
-void rtl92de_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val);
+void rtl92de_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val);
u32 rtl92de_get_desc(u8 *pdesc, bool istx, u8 desc_name);
void rtl92de_tx_polling(struct ieee80211_hw *hw, u8 hw_queue);
void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
index 4f461786a7eb..9098558d916d 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
@@ -251,7 +251,7 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
u8 e_aci = *val;
rtl92s_dm_init_edca_turbo(hw);
- if (rtlpci->acm_method != eAcmWay2_SW)
+ if (rtlpci->acm_method != EACMWAY2_SW)
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_ACM_CTRL,
&e_aci);
@@ -413,20 +413,18 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
(u8 *)(&fw_current_inps));
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&ppsc->fwctrl_psmode));
+ &ppsc->fwctrl_psmode);
- rtlpriv->cfg->ops->set_hw_reg(hw,
- HW_VAR_SET_RPWM,
- (u8 *)(&rpwm_val));
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
+ &rpwm_val);
} else {
rpwm_val = 0x0C; /* RF on */
fw_pwrmode = FW_PS_ACTIVE_MODE;
fw_current_inps = false;
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
- (u8 *)(&rpwm_val));
- rtlpriv->cfg->ops->set_hw_reg(hw,
- HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&fw_pwrmode));
+ &rpwm_val);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
+ &fw_pwrmode);
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_FW_PSMODE_STATUS,
@@ -955,7 +953,7 @@ int rtl92se_hw_init(struct ieee80211_hw *hw)
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
u8 tmp_byte = 0;
-
+ unsigned long flags;
bool rtstatus = true;
u8 tmp_u1b;
int err = false;
@@ -967,6 +965,16 @@ int rtl92se_hw_init(struct ieee80211_hw *hw)
rtlpci->being_init_adapter = true;
+ /* As this function can take a very long time (up to 350 ms)
+ * and can be called with irqs disabled, reenable the irqs
+ * to let the other devices continue being serviced.
+ *
+ * It is safe doing so since our own interrupts will only be enabled
+ * in a subsequent step.
+ */
+ local_save_flags(flags);
+ local_irq_enable();
+
rtlpriv->intf_ops->disable_aspm(hw);
/* 1. MAC Initialize */
@@ -984,7 +992,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw)
RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
"Failed to download FW. Init HW without FW now... "
"Please copy FW into /lib/firmware/rtlwifi\n");
- return 1;
+ err = 1;
+ goto exit;
}
/* After FW download, we have to reset MAC register */
@@ -997,7 +1006,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw)
/* 3. Initialize MAC/PHY Config by MACPHY_reg.txt */
if (!rtl92s_phy_mac_config(hw)) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "MAC Config failed\n");
- return rtstatus;
+ err = rtstatus;
+ goto exit;
}
/* because last function modify RCR, so we update
@@ -1016,7 +1026,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw)
/* 4. Initialize BB After MAC Config PHY_reg.txt, AGC_Tab.txt */
if (!rtl92s_phy_bb_config(hw)) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "BB Config failed\n");
- return rtstatus;
+ err = rtstatus;
+ goto exit;
}
/* 5. Initiailze RF RAIO_A.txt RF RAIO_B.txt */
@@ -1033,7 +1044,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw)
if (!rtl92s_phy_rf_config(hw)) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "RF Config failed\n");
- return rtstatus;
+ err = rtstatus;
+ goto exit;
}
/* After read predefined TXT, we must set BB/MAC/RF
@@ -1122,8 +1134,9 @@ int rtl92se_hw_init(struct ieee80211_hw *hw)
rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_ON);
rtl92s_dm_init(hw);
+exit:
+ local_irq_restore(flags);
rtlpci->being_init_adapter = false;
-
return err;
}
@@ -1135,12 +1148,13 @@ void rtl92se_set_mac_addr(struct rtl_io *io, const u8 *addr)
void rtl92se_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
- u32 reg_rcr = rtlpci->receive_config;
+ u32 reg_rcr;
if (rtlpriv->psc.rfpwr_state != ERFON)
return;
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
+
if (check_bssid) {
reg_rcr |= (RCR_CBSSID);
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
index 9c092e6eb3fe..77c5b5f35244 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
@@ -30,6 +30,7 @@
#include "../wifi.h"
#include "../pci.h"
#include "../ps.h"
+#include "../core.h"
#include "reg.h"
#include "def.h"
#include "phy.h"
@@ -833,18 +834,7 @@ static bool _rtl92s_phy_config_bb(struct ieee80211_hw *hw, u8 configtype)
if (configtype == BASEBAND_CONFIG_PHY_REG) {
for (i = 0; i < phy_reg_len; i = i + 2) {
- if (phy_reg_table[i] == 0xfe)
- mdelay(50);
- else if (phy_reg_table[i] == 0xfd)
- mdelay(5);
- else if (phy_reg_table[i] == 0xfc)
- mdelay(1);
- else if (phy_reg_table[i] == 0xfb)
- udelay(50);
- else if (phy_reg_table[i] == 0xfa)
- udelay(5);
- else if (phy_reg_table[i] == 0xf9)
- udelay(1);
+ rtl_addr_delay(phy_reg_table[i]);
/* Add delay for ECS T20 & LG malow platform, */
udelay(1);
@@ -886,18 +876,7 @@ static bool _rtl92s_phy_set_bb_to_diff_rf(struct ieee80211_hw *hw,
if (configtype == BASEBAND_CONFIG_PHY_REG) {
for (i = 0; i < phy_regarray2xtxr_len; i = i + 3) {
- if (phy_regarray2xtxr_table[i] == 0xfe)
- mdelay(50);
- else if (phy_regarray2xtxr_table[i] == 0xfd)
- mdelay(5);
- else if (phy_regarray2xtxr_table[i] == 0xfc)
- mdelay(1);
- else if (phy_regarray2xtxr_table[i] == 0xfb)
- udelay(50);
- else if (phy_regarray2xtxr_table[i] == 0xfa)
- udelay(5);
- else if (phy_regarray2xtxr_table[i] == 0xf9)
- udelay(1);
+ rtl_addr_delay(phy_regarray2xtxr_table[i]);
rtl92s_phy_set_bb_reg(hw, phy_regarray2xtxr_table[i],
phy_regarray2xtxr_table[i + 1],
@@ -920,18 +899,7 @@ static bool _rtl92s_phy_config_bb_with_pg(struct ieee80211_hw *hw,
if (configtype == BASEBAND_CONFIG_PHY_REG) {
for (i = 0; i < phy_pg_len; i = i + 3) {
- if (phy_table_pg[i] == 0xfe)
- mdelay(50);
- else if (phy_table_pg[i] == 0xfd)
- mdelay(5);
- else if (phy_table_pg[i] == 0xfc)
- mdelay(1);
- else if (phy_table_pg[i] == 0xfb)
- udelay(50);
- else if (phy_table_pg[i] == 0xfa)
- udelay(5);
- else if (phy_table_pg[i] == 0xf9)
- udelay(1);
+ rtl_addr_delay(phy_table_pg[i]);
_rtl92s_store_pwrindex_diffrate_offset(hw,
phy_table_pg[i],
@@ -1034,28 +1002,9 @@ u8 rtl92s_phy_config_rf(struct ieee80211_hw *hw, enum radio_path rfpath)
switch (rfpath) {
case RF90_PATH_A:
for (i = 0; i < radio_a_tblen; i = i + 2) {
- if (radio_a_table[i] == 0xfe)
- /* Delay specific ms. Only RF configuration
- * requires delay. */
- mdelay(50);
- else if (radio_a_table[i] == 0xfd)
- mdelay(5);
- else if (radio_a_table[i] == 0xfc)
- mdelay(1);
- else if (radio_a_table[i] == 0xfb)
- udelay(50);
- else if (radio_a_table[i] == 0xfa)
- udelay(5);
- else if (radio_a_table[i] == 0xf9)
- udelay(1);
- else
- rtl92s_phy_set_rf_reg(hw, rfpath,
- radio_a_table[i],
- MASK20BITS,
- radio_a_table[i + 1]);
+ rtl_rfreg_delay(hw, rfpath, radio_a_table[i],
+ MASK20BITS, radio_a_table[i + 1]);
- /* Add delay for ECS T20 & LG malow platform */
- udelay(1);
}
/* PA Bias current for inferiority IC */
@@ -1063,28 +1012,8 @@ u8 rtl92s_phy_config_rf(struct ieee80211_hw *hw, enum radio_path rfpath)
break;
case RF90_PATH_B:
for (i = 0; i < radio_b_tblen; i = i + 2) {
- if (radio_b_table[i] == 0xfe)
- /* Delay specific ms. Only RF configuration
- * requires delay.*/
- mdelay(50);
- else if (radio_b_table[i] == 0xfd)
- mdelay(5);
- else if (radio_b_table[i] == 0xfc)
- mdelay(1);
- else if (radio_b_table[i] == 0xfb)
- udelay(50);
- else if (radio_b_table[i] == 0xfa)
- udelay(5);
- else if (radio_b_table[i] == 0xf9)
- udelay(1);
- else
- rtl92s_phy_set_rf_reg(hw, rfpath,
- radio_b_table[i],
- MASK20BITS,
- radio_b_table[i + 1]);
-
- /* Add delay for ECS T20 & LG malow platform */
- udelay(1);
+ rtl_rfreg_delay(hw, rfpath, radio_b_table[i],
+ MASK20BITS, radio_b_table[i + 1]);
}
break;
case RF90_PATH_C:
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/reg.h b/drivers/net/wireless/rtlwifi/rtl8192se/reg.h
index c81c83591940..e13043479b71 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/reg.h
@@ -1165,16 +1165,4 @@
#define BTX_AGCRATECCK 0x7f00
-#define MASKBYTE0 0xff
-#define MASKBYTE1 0xff00
-#define MASKBYTE2 0xff0000
-#define MASKBYTE3 0xff000000
-#define MASKHWORD 0xffff0000
-#define MASKLWORD 0x0000ffff
-#define MASKDWORD 0xffffffff
-
-#define MAKS12BITS 0xfffff
-#define MASK20BITS 0xfffff
-#define RFREG_OFFSET_MASK 0xfffff
-
#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/rf.c b/drivers/net/wireless/rtlwifi/rtl8192se/rf.c
index 92d38ab3c60e..78a81c1e390b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/rf.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/rf.c
@@ -52,7 +52,7 @@ static void _rtl92s_get_powerbase(struct ieee80211_hw *hw, u8 *p_pwrlevel,
/* We only care about the path A for legacy. */
if (rtlefuse->eeprom_version < 2) {
pwrbase0 = pwrlevel[0] + (rtlefuse->legacy_httxpowerdiff & 0xf);
- } else if (rtlefuse->eeprom_version >= 2) {
+ } else {
legacy_pwrdiff = rtlefuse->txpwr_legacyhtdiff
[RF90_PATH_A][chnl - 1];
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
index 27efbcdac6a9..36b48be8329c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
@@ -310,7 +310,7 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
/* during testing, hdr was NULL here */
return false;
}
- if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+ if ((_ieee80211_is_robust_mgmt_frame(hdr)) &&
(ieee80211_has_protected(hdr->frame_control)))
rx_status->flag &= ~RX_FLAG_DECRYPTED;
else
@@ -336,7 +336,7 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
void rtl92se_tx_fill_desc(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr, u8 *pdesc_tx,
- struct ieee80211_tx_info *info,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
struct sk_buff *skb,
u8 hw_queue, struct rtl_tcb_desc *ptcb_desc)
@@ -573,7 +573,8 @@ void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
}
}
-void rtl92se_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
+void rtl92se_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val)
{
if (istx) {
switch (desc_name) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.h b/drivers/net/wireless/rtlwifi/rtl8192se/trx.h
index 64dd66f287c1..5a13f17e3b41 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.h
@@ -29,8 +29,9 @@
#ifndef __REALTEK_PCI92SE_TRX_H__
#define __REALTEK_PCI92SE_TRX_H__
-void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
- u8 *pdesc, struct ieee80211_tx_info *info,
+void rtl92se_tx_fill_desc(struct ieee80211_hw *hw,
+ struct ieee80211_hdr *hdr, u8 *pdesc,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
struct sk_buff *skb, u8 hw_queue,
struct rtl_tcb_desc *ptcb_desc);
@@ -39,7 +40,8 @@ void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg,
bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
struct ieee80211_rx_status *rx_status, u8 *pdesc,
struct sk_buff *skb);
-void rtl92se_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val);
+void rtl92se_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val);
u32 rtl92se_get_desc(u8 *pdesc, bool istx, u8 desc_name);
void rtl92se_tx_polling(struct ieee80211_hw *hw, u8 hw_queue);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile b/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile
index 4ed731f09b1f..9c34a85fdb89 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile
@@ -10,7 +10,6 @@ rtl8723ae-objs := \
led.o \
phy.o \
pwrseq.o \
- pwrseqcmd.o \
rf.o \
sw.o \
table.o \
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/def.h b/drivers/net/wireless/rtlwifi/rtl8723ae/def.h
index 8c110356dff9..debe261a7eeb 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/def.h
@@ -46,11 +46,6 @@
#define E_CUT_VERSION BIT(14)
#define RF_RL_ID (BIT(31)|BIT(30)|BIT(29)|BIT(28))
-enum version_8723e {
- VERSION_TEST_UMC_CHIP_8723 = 0x0081,
- VERSION_NORMAL_UMC_CHIP_8723_1T1R_A_CUT = 0x0089,
- VERSION_NORMAL_UMC_CHIP_8723_1T1R_B_CUT = 0x1089,
-};
/* MASK */
#define IC_TYPE_MASK (BIT(0)|BIT(1)|BIT(2))
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c
index a36eee28f9e7..25cc83058b01 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c
@@ -35,6 +35,7 @@
#include "def.h"
#include "phy.h"
#include "dm.h"
+#include "../rtl8723com/dm_common.h"
#include "fw.h"
#include "hal_btc.h"
@@ -483,16 +484,6 @@ static void rtl8723ae_dm_dig(struct ieee80211_hw *hw)
rtl8723ae_dm_ctrl_initgain_by_twoport(hw);
}
-static void rtl8723ae_dm_init_dynamic_txpower(struct ieee80211_hw *hw)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
-
- rtlpriv->dm.dynamic_txpower_enable = false;
-
- rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL;
- rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL;
-}
-
static void rtl8723ae_dm_dynamic_txpower(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -585,19 +576,6 @@ void rtl8723ae_dm_write_dig(struct ieee80211_hw *hw)
}
}
-static void rtl8723ae_dm_pwdmonitor(struct ieee80211_hw *hw)
-{
-}
-
-void rtl8723ae_dm_init_edca_turbo(struct ieee80211_hw *hw)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
-
- rtlpriv->dm.current_turbo_edca = false;
- rtlpriv->dm.is_any_nonbepkts = false;
- rtlpriv->dm.is_cur_rdlstate = false;
-}
-
static void rtl8723ae_dm_check_edca_turbo(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -669,9 +647,8 @@ static void rtl8723ae_dm_check_edca_turbo(struct ieee80211_hw *hw)
} else {
if (rtlpriv->dm.current_turbo_edca) {
u8 tmp = AC0_BE;
- rtlpriv->cfg->ops->set_hw_reg(hw,
- HW_VAR_AC_PARAM,
- (u8 *) (&tmp));
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM,
+ &tmp);
rtlpriv->dm.current_turbo_edca = false;
}
}
@@ -778,17 +755,6 @@ static void rtl8723ae_dm_refresh_rate_adaptive_mask(struct ieee80211_hw *hw)
}
}
-static void rtl8723ae_dm_init_dynamic_bpowersaving(struct ieee80211_hw *hw)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
-
- rtlpriv->dm_pstable.pre_ccastate = CCA_MAX;
- rtlpriv->dm_pstable.cur_ccasate = CCA_MAX;
- rtlpriv->dm_pstable.pre_rfstate = RF_MAX;
- rtlpriv->dm_pstable.cur_rfstate = RF_MAX;
- rtlpriv->dm_pstable.rssi_val_min = 0;
-}
-
void rtl8723ae_dm_rf_saving(struct ieee80211_hw *hw, u8 force_in_normal)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -905,11 +871,11 @@ void rtl8723ae_dm_init(struct ieee80211_hw *hw)
rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER;
rtl8723ae_dm_diginit(hw);
- rtl8723ae_dm_init_dynamic_txpower(hw);
- rtl8723ae_dm_init_edca_turbo(hw);
+ rtl8723_dm_init_dynamic_txpower(hw);
+ rtl8723_dm_init_edca_turbo(hw);
rtl8723ae_dm_init_rate_adaptive_mask(hw);
rtl8723ae_dm_initialize_txpower_tracking(hw);
- rtl8723ae_dm_init_dynamic_bpowersaving(hw);
+ rtl8723_dm_init_dynamic_bb_powersaving(hw);
}
void rtl8723ae_dm_watchdog(struct ieee80211_hw *hw)
@@ -930,7 +896,6 @@ void rtl8723ae_dm_watchdog(struct ieee80211_hw *hw)
if ((ppsc->rfpwr_state == ERFON) &&
((!fw_current_inpsmode) && fw_ps_awake) &&
(!ppsc->rfchange_inprogress)) {
- rtl8723ae_dm_pwdmonitor(hw);
rtl8723ae_dm_dig(hw);
rtl8723ae_dm_false_alarm_counter_statistics(hw);
rtl8723ae_dm_dynamic_bpowersaving(hw);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h
index a372b0204456..d253bb53d03e 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h
@@ -147,7 +147,6 @@ enum dm_dig_connect_e {
void rtl8723ae_dm_init(struct ieee80211_hw *hw);
void rtl8723ae_dm_watchdog(struct ieee80211_hw *hw);
void rtl8723ae_dm_write_dig(struct ieee80211_hw *hw);
-void rtl8723ae_dm_init_edca_turbo(struct ieee80211_hw *hw);
void rtl8723ae_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw);
void rtl8723ae_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal);
void rtl8723ae_dm_bt_coexist(struct ieee80211_hw *hw);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c
index ba1502b172a6..728b7563ad36 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c
@@ -34,199 +34,7 @@
#include "reg.h"
#include "def.h"
#include "fw.h"
-
-static void _rtl8723ae_enable_fw_download(struct ieee80211_hw *hw, bool enable)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- u8 tmp;
- if (enable) {
- tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
- rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp | 0x04);
-
- tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
- rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01);
-
- tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2);
- rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7);
- } else {
- tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
- rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe);
-
- rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00);
- }
-}
-
-static void _rtl8723ae_fw_block_write(struct ieee80211_hw *hw,
- const u8 *buffer, u32 size)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- u32 blockSize = sizeof(u32);
- u8 *bufferPtr = (u8 *) buffer;
- u32 *pu4BytePtr = (u32 *) buffer;
- u32 i, offset, blockCount, remainSize;
-
- blockCount = size / blockSize;
- remainSize = size % blockSize;
-
- for (i = 0; i < blockCount; i++) {
- offset = i * blockSize;
- rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset),
- *(pu4BytePtr + i));
- }
-
- if (remainSize) {
- offset = blockCount * blockSize;
- bufferPtr += offset;
- for (i = 0; i < remainSize; i++) {
- rtl_write_byte(rtlpriv, (FW_8192C_START_ADDRESS +
- offset + i), *(bufferPtr + i));
- }
- }
-}
-
-static void _rtl8723ae_fw_page_write(struct ieee80211_hw *hw,
- u32 page, const u8 *buffer, u32 size)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- u8 value8;
- u8 u8page = (u8) (page & 0x07);
-
- value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page;
-
- rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8);
- _rtl8723ae_fw_block_write(hw, buffer, size);
-}
-
-static void _rtl8723ae_write_fw(struct ieee80211_hw *hw,
- enum version_8723e version, u8 *buffer,
- u32 size)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- u8 *bufferPtr = (u8 *) buffer;
- u32 page_nums, remain_size;
- u32 page, offset;
-
- RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW size is %d bytes,\n", size);
-
- page_nums = size / FW_8192C_PAGE_SIZE;
- remain_size = size % FW_8192C_PAGE_SIZE;
-
- if (page_nums > 6) {
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
- "Page numbers should not be greater then 6\n");
- }
-
- for (page = 0; page < page_nums; page++) {
- offset = page * FW_8192C_PAGE_SIZE;
- _rtl8723ae_fw_page_write(hw, page, (bufferPtr + offset),
- FW_8192C_PAGE_SIZE);
- }
-
- if (remain_size) {
- offset = page_nums * FW_8192C_PAGE_SIZE;
- page = page_nums;
- _rtl8723ae_fw_page_write(hw, page, (bufferPtr + offset),
- remain_size);
- }
-
- RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW write done.\n");
-}
-
-static int _rtl8723ae_fw_free_to_go(struct ieee80211_hw *hw)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- int err = -EIO;
- u32 counter = 0;
- u32 value32;
-
- do {
- value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
- } while ((counter++ < FW_8192C_POLLING_TIMEOUT_COUNT) &&
- (!(value32 & FWDL_ChkSum_rpt)));
-
- if (counter >= FW_8192C_POLLING_TIMEOUT_COUNT) {
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
- "chksum report faill ! REG_MCUFWDL:0x%08x .\n",
- value32);
- goto exit;
- }
-
- RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
- "Checksum report OK ! REG_MCUFWDL:0x%08x .\n", value32);
-
- value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
- value32 |= MCUFWDL_RDY;
- value32 &= ~WINTINI_RDY;
- rtl_write_dword(rtlpriv, REG_MCUFWDL, value32);
-
- counter = 0;
-
- do {
- value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
- if (value32 & WINTINI_RDY) {
- RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
- "Polling FW ready success!! REG_MCUFWDL:0x%08x .\n",
- value32);
- err = 0;
- goto exit;
- }
-
- mdelay(FW_8192C_POLLING_DELAY);
-
- } while (counter++ < FW_8192C_POLLING_TIMEOUT_COUNT);
-
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
- "Polling FW ready fail!! REG_MCUFWDL:0x%08x .\n", value32);
-
-exit:
- return err;
-}
-
-int rtl8723ae_download_fw(struct ieee80211_hw *hw)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
- struct rtl8723ae_firmware_header *pfwheader;
- u8 *pfwdata;
- u32 fwsize;
- int err;
- enum version_8723e version = rtlhal->version;
-
- if (!rtlhal->pfirmware)
- return 1;
-
- pfwheader = (struct rtl8723ae_firmware_header *)rtlhal->pfirmware;
- pfwdata = (u8 *) rtlhal->pfirmware;
- fwsize = rtlhal->fwsize;
-
- if (IS_FW_HEADER_EXIST(pfwheader)) {
- RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
- "Firmware Version(%d), Signature(%#x),Size(%d)\n",
- pfwheader->version, pfwheader->signature,
- (int)sizeof(struct rtl8723ae_firmware_header));
-
- pfwdata = pfwdata + sizeof(struct rtl8723ae_firmware_header);
- fwsize = fwsize - sizeof(struct rtl8723ae_firmware_header);
- }
-
- if (rtl_read_byte(rtlpriv, REG_MCUFWDL)&BIT(7)) {
- rtl8723ae_firmware_selfreset(hw);
- rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00);
- }
- _rtl8723ae_enable_fw_download(hw, true);
- _rtl8723ae_write_fw(hw, version, pfwdata, fwsize);
- _rtl8723ae_enable_fw_download(hw, false);
-
- err = _rtl8723ae_fw_free_to_go(hw);
- if (err) {
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
- "Firmware is not ready to run!\n");
- } else {
- RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
- "Firmware is ready to run!\n");
- }
- return 0;
-}
+#include "../rtl8723com/fw_common.h"
static bool rtl8723ae_check_fw_read_last_h2c(struct ieee80211_hw *hw, u8 boxnum)
{
@@ -463,50 +271,6 @@ void rtl8723ae_fill_h2c_cmd(struct ieee80211_hw *hw,
return;
}
-void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw)
-{
- u8 u1tmp;
- u8 delay = 100;
- struct rtl_priv *rtlpriv = rtl_priv(hw);
-
- rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20);
- u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
-
- while (u1tmp & BIT(2)) {
- delay--;
- if (delay == 0)
- break;
- udelay(50);
- u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
- }
- if (delay == 0) {
- u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
- rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, u1tmp&(~BIT(2)));
- }
-}
-
-void rtl8723ae_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- u8 u1_h2c_set_pwrmode[3] = { 0 };
- struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
-
- RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode);
-
- SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode);
- SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode,
- (rtlpriv->mac80211.p2p) ?
- ppsc->smart_ps : 1);
- SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode,
- ppsc->reg_max_lps_awakeintvl);
-
- RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
- "rtl8723ae_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode\n",
- u1_h2c_set_pwrmode, 3);
- rtl8723ae_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode);
-
-}
-
static bool _rtl8723ae_cmd_send_packet(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
@@ -812,7 +576,6 @@ void rtl8723ae_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, BIT(4));
p2p_ps_offload->offload_en = 1;
-
if (P2P_ROLE_GO == rtlpriv->mac80211.p2p) {
p2p_ps_offload->role = 1;
p2p_ps_offload->allstasleep = 0;
@@ -836,3 +599,24 @@ void rtl8723ae_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
}
rtl8723ae_fill_h2c_cmd(hw, H2C_P2P_PS_OFFLOAD, 1, (u8 *)p2p_ps_offload);
}
+
+void rtl8723ae_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 u1_h2c_set_pwrmode[3] = { 0 };
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode);
+
+ SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode);
+ SET_H2CCMD_PWRMODE_PARM_SMART_PS_23A(u1_h2c_set_pwrmode,
+ (rtlpriv->mac80211.p2p) ?
+ ppsc->smart_ps : 1);
+ SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode,
+ ppsc->reg_max_lps_awakeintvl);
+
+ RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
+ "rtl8723ae_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode\n",
+ u1_h2c_set_pwrmode, 3);
+ rtl8723ae_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode);
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.h b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.h
index ed3b795e6980..d355b85dd9fe 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.h
@@ -34,7 +34,7 @@
#define FW_8192C_END_ADDRESS 0x3FFF
#define FW_8192C_PAGE_SIZE 4096
#define FW_8192C_POLLING_DELAY 5
-#define FW_8192C_POLLING_TIMEOUT_COUNT 1000
+#define FW_8192C_POLLING_TIMEOUT_COUNT 6000
#define BEACON_PG 0
#define PSPOLL_PG 2
@@ -65,21 +65,9 @@ struct rtl8723ae_firmware_header {
u32 rsvd5;
};
-enum rtl8192c_h2c_cmd {
- H2C_AP_OFFLOAD = 0,
- H2C_SETPWRMODE = 1,
- H2C_JOINBSSRPT = 2,
- H2C_RSVDPAGE = 3,
- H2C_RSSI_REPORT = 4,
- H2C_P2P_PS_CTW_CMD = 5,
- H2C_P2P_PS_OFFLOAD = 6,
- H2C_RA_MASK = 7,
- MAX_H2CCMD
-};
-
#define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \
SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
-#define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \
+#define SET_H2CCMD_PWRMODE_PARM_SMART_PS_23A(__ph2ccmd, __val) \
SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val)
#define SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(__ph2ccmd, __val) \
SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val)
@@ -92,10 +80,8 @@ enum rtl8192c_h2c_cmd {
#define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \
SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val)
-int rtl8723ae_download_fw(struct ieee80211_hw *hw);
void rtl8723ae_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id,
u32 cmd_len, u8 *p_cmdbuffer);
-void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw);
void rtl8723ae_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
void rtl8723ae_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished);
void rtl8723ae_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c
index 3d092e4b0b7f..48fee1be78c2 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c
@@ -31,6 +31,8 @@
#include "../pci.h"
#include "dm.h"
#include "fw.h"
+#include "../rtl8723com/fw_common.h"
+#include "../rtl8723com/fw_common.h"
#include "phy.h"
#include "reg.h"
#include "hal_btc.h"
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c
index 68c28340f791..5d534df8d90c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c
@@ -30,7 +30,9 @@
#include "hal_btc.h"
#include "../pci.h"
#include "phy.h"
+#include "../rtl8723com/phy_common.h"
#include "fw.h"
+#include "../rtl8723com/fw_common.h"
#include "reg.h"
#include "def.h"
@@ -391,13 +393,13 @@ static void rtl8723ae_dm_bt_set_sw_full_time_dac_swing(struct ieee80211_hw *hw,
if (sw_dac_swing_on) {
RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE,
"[BTCoex], SwDacSwing = 0x%x\n", sw_dac_swing_lvl);
- rtl8723ae_phy_set_bb_reg(hw, 0x880, 0xff000000,
- sw_dac_swing_lvl);
+ rtl8723_phy_set_bb_reg(hw, 0x880, 0xff000000,
+ sw_dac_swing_lvl);
rtlpcipriv->bt_coexist.sw_coexist_all_off = false;
} else {
RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE,
"[BTCoex], SwDacSwing Off!\n");
- rtl8723ae_phy_set_bb_reg(hw, 0x880, 0xff000000, 0xc0);
+ rtl8723_phy_set_bb_reg(hw, 0x880, 0xff000000, 0xc0);
}
}
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
index c333dfd116b8..65c9e80e1f78 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
@@ -38,10 +38,11 @@
#include "def.h"
#include "phy.h"
#include "dm.h"
+#include "../rtl8723com/dm_common.h"
#include "fw.h"
+#include "../rtl8723com/fw_common.h"
#include "led.h"
#include "hw.h"
-#include "pwrseqcmd.h"
#include "pwrseq.h"
#include "btc.h"
@@ -206,14 +207,13 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
rtl_write_byte(rtlpriv, REG_SLOT, val[0]);
for (e_aci = 0; e_aci < AC_MAX; e_aci++) {
- rtlpriv->cfg->ops->set_hw_reg(hw,
- HW_VAR_AC_PARAM,
- (u8 *) (&e_aci));
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM,
+ &e_aci);
}
break; }
case HW_VAR_ACK_PREAMBLE:{
u8 reg_tmp;
- u8 short_preamble = (bool) (*(u8 *) val);
+ u8 short_preamble = (bool)*val;
reg_tmp = (mac->cur_40_prime_sc) << 5;
if (short_preamble)
reg_tmp |= 0x80;
@@ -224,7 +224,7 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
u8 min_spacing_to_set;
u8 sec_min_space;
- min_spacing_to_set = *((u8 *) val);
+ min_spacing_to_set = *val;
if (min_spacing_to_set <= 7) {
sec_min_space = 0;
@@ -248,7 +248,7 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
case HW_VAR_SHORTGI_DENSITY:{
u8 density_to_set;
- density_to_set = *((u8 *) val);
+ density_to_set = *val;
mac->min_space_cfg |= (density_to_set << 3);
RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
@@ -272,7 +272,7 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
else
p_regtoset = regtoset_normal;
- factor_toset = *((u8 *) val);
+ factor_toset = *val;
if (factor_toset <= 3) {
factor_toset = (1 << (factor_toset + 2));
if (factor_toset > 0xf)
@@ -303,16 +303,15 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
}
break; }
case HW_VAR_AC_PARAM:{
- u8 e_aci = *((u8 *) val);
- rtl8723ae_dm_init_edca_turbo(hw);
+ u8 e_aci = *val;
+ rtl8723_dm_init_edca_turbo(hw);
- if (rtlpci->acm_method != eAcmWay2_SW)
- rtlpriv->cfg->ops->set_hw_reg(hw,
- HW_VAR_ACM_CTRL,
- (u8 *) (&e_aci));
+ if (rtlpci->acm_method != EACMWAY2_SW)
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL,
+ &e_aci);
break; }
case HW_VAR_ACM_CTRL:{
- u8 e_aci = *((u8 *) val);
+ u8 e_aci = *val;
union aci_aifsn *p_aci_aifsn =
(union aci_aifsn *)(&(mac->ac[0].aifs));
u8 acm = p_aci_aifsn->f.acm;
@@ -365,7 +364,7 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
rtlpci->receive_config = ((u32 *) (val))[0];
break;
case HW_VAR_RETRY_LIMIT:{
- u8 retry_limit = ((u8 *) (val))[0];
+ u8 retry_limit = *val;
rtl_write_word(rtlpriv, REG_RL,
retry_limit << RETRY_LIMIT_SHORT_SHIFT |
@@ -378,13 +377,13 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
rtlefuse->efuse_usedbytes = *((u16 *) val);
break;
case HW_VAR_EFUSE_USAGE:
- rtlefuse->efuse_usedpercentage = *((u8 *) val);
+ rtlefuse->efuse_usedpercentage = *val;
break;
case HW_VAR_IO_CMD:
rtl8723ae_phy_set_io_cmd(hw, (*(enum io_type *)val));
break;
case HW_VAR_WPA_CONFIG:
- rtl_write_byte(rtlpriv, REG_SECCFG, *((u8 *) val));
+ rtl_write_byte(rtlpriv, REG_SECCFG, *val);
break;
case HW_VAR_SET_RPWM:{
u8 rpwm_val;
@@ -393,27 +392,25 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
udelay(1);
if (rpwm_val & BIT(7)) {
- rtl_write_byte(rtlpriv, REG_PCIE_HRPWM,
- (*(u8 *) val));
+ rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val);
} else {
- rtl_write_byte(rtlpriv, REG_PCIE_HRPWM,
- ((*(u8 *) val) | BIT(7)));
+ rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val | BIT(7));
}
break; }
case HW_VAR_H2C_FW_PWRMODE:{
- u8 psmode = (*(u8 *) val);
+ u8 psmode = *val;
if (psmode != FW_PS_ACTIVE_MODE)
rtl8723ae_dm_rf_saving(hw, true);
- rtl8723ae_set_fw_pwrmode_cmd(hw, (*(u8 *) val));
+ rtl8723ae_set_fw_pwrmode_cmd(hw, *val);
break; }
case HW_VAR_FW_PSMODE_STATUS:
ppsc->fw_current_inpsmode = *((bool *) val);
break;
case HW_VAR_H2C_FW_JOINBSSRPT:{
- u8 mstatus = (*(u8 *) val);
+ u8 mstatus = *val;
u8 tmp_regcr, tmp_reg422;
bool recover = false;
@@ -446,11 +443,11 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
rtl_write_byte(rtlpriv, REG_CR + 1,
(tmp_regcr & ~(BIT(0))));
}
- rtl8723ae_set_fw_joinbss_report_cmd(hw, (*(u8 *) val));
+ rtl8723ae_set_fw_joinbss_report_cmd(hw, *val);
break; }
case HW_VAR_H2C_FW_P2P_PS_OFFLOAD:
- rtl8723ae_set_p2p_ps_offload_cmd(hw, (*(u8 *)val));
+ rtl8723ae_set_p2p_ps_offload_cmd(hw, *val);
break;
case HW_VAR_AID:{
u16 u2btmp;
@@ -460,7 +457,7 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
mac->assoc_id));
break; }
case HW_VAR_CORRECT_TSF:{
- u8 btype_ibss = ((u8 *) (val))[0];
+ u8 btype_ibss = *val;
if (btype_ibss == true)
_rtl8723ae_stop_tx_beacon(hw);
@@ -490,20 +487,18 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
(u8 *)(&fw_current_inps));
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&ppsc->fwctrl_psmode));
+ &ppsc->fwctrl_psmode);
- rtlpriv->cfg->ops->set_hw_reg(hw,
- HW_VAR_SET_RPWM,
- (u8 *)(&rpwm_val));
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
+ &rpwm_val);
} else {
rpwm_val = 0x0C; /* RF on */
fw_pwrmode = FW_PS_ACTIVE_MODE;
fw_current_inps = false;
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
- (u8 *)(&rpwm_val));
- rtlpriv->cfg->ops->set_hw_reg(hw,
- HW_VAR_H2C_FW_PWRMODE,
- (u8 *)(&fw_pwrmode));
+ &rpwm_val);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
+ &fw_pwrmode);
rtlpriv->cfg->ops->set_hw_reg(hw,
HW_VAR_FW_PSMODE_STATUS,
@@ -880,23 +875,33 @@ int rtl8723ae_hw_init(struct ieee80211_hw *hw)
bool rtstatus = true;
int err;
u8 tmp_u1b;
+ unsigned long flags;
rtlpriv->rtlhal.being_init_adapter = true;
+ /* As this function can take a very long time (up to 350 ms)
+ * and can be called with irqs disabled, reenable the irqs
+ * to let the other devices continue being serviced.
+ *
+ * It is safe doing so since our own interrupts will only be enabled
+ * in a subsequent step.
+ */
+ local_save_flags(flags);
+ local_irq_enable();
+
rtlpriv->intf_ops->disable_aspm(hw);
rtstatus = _rtl8712e_init_mac(hw);
if (rtstatus != true) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Init MAC failed\n");
err = 1;
- return err;
+ goto exit;
}
- err = rtl8723ae_download_fw(hw);
+ err = rtl8723_download_fw(hw, false);
if (err) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
"Failed to download FW. Init HW without FW now..\n");
err = 1;
- rtlhal->fw_ready = false;
- return err;
+ goto exit;
} else {
rtlhal->fw_ready = true;
}
@@ -971,6 +976,8 @@ int rtl8723ae_hw_init(struct ieee80211_hw *hw)
RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "under 1.5V\n");
}
rtl8723ae_dm_init(hw);
+exit:
+ local_irq_restore(flags);
rtlpriv->rtlhal.being_init_adapter = false;
return err;
}
@@ -1112,12 +1119,13 @@ static int _rtl8723ae_set_media_status(struct ieee80211_hw *hw,
void rtl8723ae_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
- u32 reg_rcr = rtlpci->receive_config;
+ u32 reg_rcr;
if (rtlpriv->psc.rfpwr_state != ERFON)
return;
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(&reg_rcr));
+
if (check_bssid == true) {
reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN);
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR,
@@ -1153,7 +1161,7 @@ void rtl8723ae_set_qos(struct ieee80211_hw *hw, int aci)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- rtl8723ae_dm_init_edca_turbo(hw);
+ rtl8723_dm_init_edca_turbo(hw);
switch (aci) {
case AC1_BK:
rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f);
@@ -1614,10 +1622,10 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw,
rtl8723ae_read_bt_coexist_info_from_hwpg(hw,
rtlefuse->autoload_failflag, hwinfo);
- rtlefuse->eeprom_channelplan = *(u8 *)&hwinfo[EEPROM_CHANNELPLAN];
+ rtlefuse->eeprom_channelplan = hwinfo[EEPROM_CHANNELPLAN];
rtlefuse->eeprom_version = *(u16 *)&hwinfo[EEPROM_VERSION];
rtlefuse->txpwr_fromeprom = true;
- rtlefuse->eeprom_oemid = *(u8 *)&hwinfo[EEPROM_CUSTOMER_ID];
+ rtlefuse->eeprom_oemid = hwinfo[EEPROM_CUSTOMER_ID];
RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
"EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid);
@@ -1655,7 +1663,7 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw,
CHK_SVID_SMID(0x10EC, 0x9185))
rtlhal->oem_id = RT_CID_TOSHIBA;
else if (rtlefuse->eeprom_svid == 0x1025)
- rtlhal->oem_id = RT_CID_819x_Acer;
+ rtlhal->oem_id = RT_CID_819X_ACER;
else if (CHK_SVID_SMID(0x10EC, 0x6191) ||
CHK_SVID_SMID(0x10EC, 0x6192) ||
CHK_SVID_SMID(0x10EC, 0x6193) ||
@@ -1665,7 +1673,7 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw,
CHK_SVID_SMID(0x10EC, 0x8191) ||
CHK_SVID_SMID(0x10EC, 0x8192) ||
CHK_SVID_SMID(0x10EC, 0x8193))
- rtlhal->oem_id = RT_CID_819x_SAMSUNG;
+ rtlhal->oem_id = RT_CID_819X_SAMSUNG;
else if (CHK_SVID_SMID(0x10EC, 0x8195) ||
CHK_SVID_SMID(0x10EC, 0x9195) ||
CHK_SVID_SMID(0x10EC, 0x7194) ||
@@ -1673,24 +1681,24 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw,
CHK_SVID_SMID(0x10EC, 0x8201) ||
CHK_SVID_SMID(0x10EC, 0x8202) ||
CHK_SVID_SMID(0x10EC, 0x9200))
- rtlhal->oem_id = RT_CID_819x_Lenovo;
+ rtlhal->oem_id = RT_CID_819X_LENOVO;
else if (CHK_SVID_SMID(0x10EC, 0x8197) ||
CHK_SVID_SMID(0x10EC, 0x9196))
- rtlhal->oem_id = RT_CID_819x_CLEVO;
+ rtlhal->oem_id = RT_CID_819X_CLEVO;
else if (CHK_SVID_SMID(0x1028, 0x8194) ||
CHK_SVID_SMID(0x1028, 0x8198) ||
CHK_SVID_SMID(0x1028, 0x9197) ||
CHK_SVID_SMID(0x1028, 0x9198))
- rtlhal->oem_id = RT_CID_819x_DELL;
+ rtlhal->oem_id = RT_CID_819X_DELL;
else if (CHK_SVID_SMID(0x103C, 0x1629))
- rtlhal->oem_id = RT_CID_819x_HP;
+ rtlhal->oem_id = RT_CID_819X_HP;
else if (CHK_SVID_SMID(0x1A32, 0x2315))
- rtlhal->oem_id = RT_CID_819x_QMI;
+ rtlhal->oem_id = RT_CID_819X_QMI;
else if (CHK_SVID_SMID(0x10EC, 0x8203))
- rtlhal->oem_id = RT_CID_819x_PRONETS;
+ rtlhal->oem_id = RT_CID_819X_PRONETS;
else if (CHK_SVID_SMID(0x1043, 0x84B5))
rtlhal->oem_id =
- RT_CID_819x_Edimax_ASUS;
+ RT_CID_819X_EDIMAX_ASUS;
else
rtlhal->oem_id = RT_CID_DEFAULT;
} else if (rtlefuse->eeprom_did == 0x8178) {
@@ -1712,12 +1720,12 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw,
CHK_SVID_SMID(0x10EC, 0x9185))
rtlhal->oem_id = RT_CID_TOSHIBA;
else if (rtlefuse->eeprom_svid == 0x1025)
- rtlhal->oem_id = RT_CID_819x_Acer;
+ rtlhal->oem_id = RT_CID_819X_ACER;
else if (CHK_SVID_SMID(0x10EC, 0x8186))
- rtlhal->oem_id = RT_CID_819x_PRONETS;
+ rtlhal->oem_id = RT_CID_819X_PRONETS;
else if (CHK_SVID_SMID(0x1043, 0x8486))
rtlhal->oem_id =
- RT_CID_819x_Edimax_ASUS;
+ RT_CID_819X_EDIMAX_ASUS;
else
rtlhal->oem_id = RT_CID_DEFAULT;
} else {
@@ -1731,7 +1739,7 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw,
rtlhal->oem_id = RT_CID_CCX;
break;
case EEPROM_CID_QMI:
- rtlhal->oem_id = RT_CID_819x_QMI;
+ rtlhal->oem_id = RT_CID_819X_QMI;
break;
case EEPROM_CID_WHQL:
break;
@@ -2037,8 +2045,7 @@ void rtl8723ae_update_channel_access_setting(struct ieee80211_hw *hw)
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
u16 sifs_timer;
- rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME,
- (u8 *)&mac->slot_time);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, &mac->slot_time);
if (!mac->ht_enable)
sifs_timer = 0x0a0a;
else
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c
index 5d318a85eda4..3ea78afdec73 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c
@@ -30,12 +30,14 @@
#include "../wifi.h"
#include "../pci.h"
#include "../ps.h"
+#include "../core.h"
#include "reg.h"
#include "def.h"
#include "phy.h"
#include "rf.h"
#include "dm.h"
#include "table.h"
+#include "../rtl8723com/phy_common.h"
/* static forward definitions */
static u32 _phy_fw_rf_serial_read(struct ieee80211_hw *hw,
@@ -43,72 +45,17 @@ static u32 _phy_fw_rf_serial_read(struct ieee80211_hw *hw,
static void _phy_fw_rf_serial_write(struct ieee80211_hw *hw,
enum radio_path rfpath,
u32 offset, u32 data);
-static u32 _phy_rf_serial_read(struct ieee80211_hw *hw,
- enum radio_path rfpath, u32 offset);
-static void _phy_rf_serial_write(struct ieee80211_hw *hw,
- enum radio_path rfpath, u32 offset, u32 data);
-static u32 _phy_calculate_bit_shift(u32 bitmask);
static bool _phy_bb8192c_config_parafile(struct ieee80211_hw *hw);
static bool _phy_cfg_mac_w_header(struct ieee80211_hw *hw);
static bool _phy_cfg_bb_w_header(struct ieee80211_hw *hw, u8 configtype);
static bool _phy_cfg_bb_w_pgheader(struct ieee80211_hw *hw, u8 configtype);
-static void _phy_init_bb_rf_reg_def(struct ieee80211_hw *hw);
-static bool _phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable,
- u32 cmdtableidx, u32 cmdtablesz,
- enum swchnlcmd_id cmdid,
- u32 para1, u32 para2,
- u32 msdelay);
static bool _phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel,
u8 *stage, u8 *step, u32 *delay);
static u8 _phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw,
enum wireless_mode wirelessmode,
long power_indbm);
-static long _phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw,
- enum wireless_mode wirelessmode, u8 txpwridx);
static void rtl8723ae_phy_set_io(struct ieee80211_hw *hw);
-u32 rtl8723ae_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr,
- u32 bitmask)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- u32 returnvalue, originalvalue, bitshift;
-
- RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
- "regaddr(%#x), bitmask(%#x)\n", regaddr, bitmask);
- originalvalue = rtl_read_dword(rtlpriv, regaddr);
- bitshift = _phy_calculate_bit_shift(bitmask);
- returnvalue = (originalvalue & bitmask) >> bitshift;
-
- RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
- "BBR MASK=0x%x Addr[0x%x]=0x%x\n", bitmask, regaddr,
- originalvalue);
-
- return returnvalue;
-}
-
-void rtl8723ae_phy_set_bb_reg(struct ieee80211_hw *hw,
- u32 regaddr, u32 bitmask, u32 data)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- u32 originalvalue, bitshift;
-
- RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
- "regaddr(%#x), bitmask(%#x), data(%#x)\n", regaddr,
- bitmask, data);
-
- if (bitmask != MASKDWORD) {
- originalvalue = rtl_read_dword(rtlpriv, regaddr);
- bitshift = _phy_calculate_bit_shift(bitmask);
- data = ((originalvalue & (~bitmask)) | (data << bitshift));
- }
-
- rtl_write_dword(rtlpriv, regaddr, data);
-
- RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
- "regaddr(%#x), bitmask(%#x), data(%#x)\n",
- regaddr, bitmask, data);
-}
-
u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw,
enum radio_path rfpath, u32 regaddr, u32 bitmask)
{
@@ -124,11 +71,11 @@ u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw,
spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
if (rtlphy->rf_mode != RF_OP_BY_FW)
- original_value = _phy_rf_serial_read(hw, rfpath, regaddr);
+ original_value = rtl8723_phy_rf_serial_read(hw, rfpath, regaddr);
else
original_value = _phy_fw_rf_serial_read(hw, rfpath, regaddr);
- bitshift = _phy_calculate_bit_shift(bitmask);
+ bitshift = rtl8723_phy_calculate_bit_shift(bitmask);
readback_value = (original_value & bitmask) >> bitshift;
spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags);
@@ -157,19 +104,19 @@ void rtl8723ae_phy_set_rf_reg(struct ieee80211_hw *hw,
if (rtlphy->rf_mode != RF_OP_BY_FW) {
if (bitmask != RFREG_OFFSET_MASK) {
- original_value = _phy_rf_serial_read(hw, rfpath,
- regaddr);
- bitshift = _phy_calculate_bit_shift(bitmask);
+ original_value = rtl8723_phy_rf_serial_read(hw, rfpath,
+ regaddr);
+ bitshift = rtl8723_phy_calculate_bit_shift(bitmask);
data = ((original_value & (~bitmask)) |
(data << bitshift));
}
- _phy_rf_serial_write(hw, rfpath, regaddr, data);
+ rtl8723_phy_rf_serial_write(hw, rfpath, regaddr, data);
} else {
if (bitmask != RFREG_OFFSET_MASK) {
original_value = _phy_fw_rf_serial_read(hw, rfpath,
regaddr);
- bitshift = _phy_calculate_bit_shift(bitmask);
+ bitshift = rtl8723_phy_calculate_bit_shift(bitmask);
data = ((original_value & (~bitmask)) |
(data << bitshift));
}
@@ -197,87 +144,6 @@ static void _phy_fw_rf_serial_write(struct ieee80211_hw *hw,
RT_ASSERT(false, "deprecated!\n");
}
-static u32 _phy_rf_serial_read(struct ieee80211_hw *hw,
- enum radio_path rfpath, u32 offset)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_phy *rtlphy = &(rtlpriv->phy);
- struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath];
- u32 newoffset;
- u32 tmplong, tmplong2;
- u8 rfpi_enable = 0;
- u32 retvalue;
-
- offset &= 0x3f;
- newoffset = offset;
- if (RT_CANNOT_IO(hw)) {
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "return all one\n");
- return 0xFFFFFFFF;
- }
- tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD);
- if (rfpath == RF90_PATH_A)
- tmplong2 = tmplong;
- else
- tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD);
- tmplong2 = (tmplong2 & (~BLSSIREADADDRESS)) |
- (newoffset << 23) | BLSSIREADEDGE;
- rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD,
- tmplong & (~BLSSIREADEDGE));
- mdelay(1);
- rtl_set_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD, tmplong2);
- mdelay(1);
- rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD,
- tmplong | BLSSIREADEDGE);
- mdelay(1);
- if (rfpath == RF90_PATH_A)
- rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1,
- BIT(8));
- else if (rfpath == RF90_PATH_B)
- rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XB_HSSIPARAMETER1,
- BIT(8));
- if (rfpi_enable)
- retvalue = rtl_get_bbreg(hw, pphyreg->rf_rbpi,
- BLSSIREADBACKDATA);
- else
- retvalue = rtl_get_bbreg(hw, pphyreg->rf_rb,
- BLSSIREADBACKDATA);
- RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFR-%d Addr[0x%x]=0x%x\n",
- rfpath, pphyreg->rf_rb, retvalue);
- return retvalue;
-}
-
-static void _phy_rf_serial_write(struct ieee80211_hw *hw,
- enum radio_path rfpath, u32 offset, u32 data)
-{
- u32 data_and_addr;
- u32 newoffset;
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_phy *rtlphy = &(rtlpriv->phy);
- struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath];
-
- if (RT_CANNOT_IO(hw)) {
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "stop\n");
- return;
- }
- offset &= 0x3f;
- newoffset = offset;
- data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff;
- rtl_set_bbreg(hw, pphyreg->rf3wire_offset, MASKDWORD, data_and_addr);
- RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFW-%d Addr[0x%x]=0x%x\n",
- rfpath, pphyreg->rf3wire_offset, data_and_addr);
-}
-
-static u32 _phy_calculate_bit_shift(u32 bitmask)
-{
- u32 i;
-
- for (i = 0; i <= 31; i++) {
- if (((bitmask >> i) & 0x1) == 1)
- break;
- }
- return i;
-}
-
static void _rtl8723ae_phy_bb_config_1t(struct ieee80211_hw *hw)
{
rtl_set_bbreg(hw, RFPGA0_TXINFO, 0x3, 0x2);
@@ -307,7 +173,7 @@ bool rtl8723ae_phy_bb_config(struct ieee80211_hw *hw)
u8 tmpu1b;
u8 reg_hwparafile = 1;
- _phy_init_bb_rf_reg_def(hw);
+ rtl8723_phy_init_bb_rf_reg_def(hw);
/* 1. 0x28[1] = 1 */
tmpu1b = rtl_read_byte(rtlpriv, REG_AFE_PLL_CTRL);
@@ -412,18 +278,7 @@ static bool _phy_cfg_bb_w_header(struct ieee80211_hw *hw, u8 configtype)
phy_regarray_table = RTL8723EPHY_REG_1TARRAY;
if (configtype == BASEBAND_CONFIG_PHY_REG) {
for (i = 0; i < phy_reg_arraylen; i = i + 2) {
- if (phy_regarray_table[i] == 0xfe)
- mdelay(50);
- else if (phy_regarray_table[i] == 0xfd)
- mdelay(5);
- else if (phy_regarray_table[i] == 0xfc)
- mdelay(1);
- else if (phy_regarray_table[i] == 0xfb)
- udelay(50);
- else if (phy_regarray_table[i] == 0xfa)
- udelay(5);
- else if (phy_regarray_table[i] == 0xf9)
- udelay(1);
+ rtl_addr_delay(phy_regarray_table[i]);
rtl_set_bbreg(hw, phy_regarray_table[i], MASKDWORD,
phy_regarray_table[i + 1]);
udelay(1);
@@ -585,18 +440,7 @@ static bool _phy_cfg_bb_w_pgheader(struct ieee80211_hw *hw, u8 configtype)
if (configtype == BASEBAND_CONFIG_PHY_REG) {
for (i = 0; i < phy_regarray_pg_len; i = i + 3) {
- if (phy_regarray_table_pg[i] == 0xfe)
- mdelay(50);
- else if (phy_regarray_table_pg[i] == 0xfd)
- mdelay(5);
- else if (phy_regarray_table_pg[i] == 0xfc)
- mdelay(1);
- else if (phy_regarray_table_pg[i] == 0xfb)
- udelay(50);
- else if (phy_regarray_table_pg[i] == 0xfa)
- udelay(5);
- else if (phy_regarray_table_pg[i] == 0xf9)
- udelay(1);
+ rtl_addr_delay(phy_regarray_table_pg[i]);
_st_pwrIdx_dfrate_off(hw, phy_regarray_table_pg[i],
phy_regarray_table_pg[i + 1],
@@ -623,24 +467,9 @@ bool rtl8723ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
switch (rfpath) {
case RF90_PATH_A:
for (i = 0; i < radioa_arraylen; i = i + 2) {
- if (radioa_array_table[i] == 0xfe)
- mdelay(50);
- else if (radioa_array_table[i] == 0xfd)
- mdelay(5);
- else if (radioa_array_table[i] == 0xfc)
- mdelay(1);
- else if (radioa_array_table[i] == 0xfb)
- udelay(50);
- else if (radioa_array_table[i] == 0xfa)
- udelay(5);
- else if (radioa_array_table[i] == 0xf9)
- udelay(1);
- else {
- rtl_set_rfreg(hw, rfpath, radioa_array_table[i],
- RFREG_OFFSET_MASK,
- radioa_array_table[i + 1]);
- udelay(1);
- }
+ rtl_rfreg_delay(hw, rfpath, radioa_array_table[i],
+ RFREG_OFFSET_MASK,
+ radioa_array_table[i + 1]);
}
break;
case RF90_PATH_B:
@@ -690,92 +519,6 @@ void rtl8723ae_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw)
ROFDM0_RXDETECTOR3, rtlphy->framesync);
}
-static void _phy_init_bb_rf_reg_def(struct ieee80211_hw *hw)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_phy *rtlphy = &(rtlpriv->phy);
-
- rtlphy->phyreg_def[RF90_PATH_A].rfintfs = RFPGA0_XAB_RFINTERFACESW;
- rtlphy->phyreg_def[RF90_PATH_B].rfintfs = RFPGA0_XAB_RFINTERFACESW;
- rtlphy->phyreg_def[RF90_PATH_C].rfintfs = RFPGA0_XCD_RFINTERFACESW;
- rtlphy->phyreg_def[RF90_PATH_D].rfintfs = RFPGA0_XCD_RFINTERFACESW;
-
- rtlphy->phyreg_def[RF90_PATH_A].rfintfi = RFPGA0_XAB_RFINTERFACERB;
- rtlphy->phyreg_def[RF90_PATH_B].rfintfi = RFPGA0_XAB_RFINTERFACERB;
- rtlphy->phyreg_def[RF90_PATH_C].rfintfi = RFPGA0_XCD_RFINTERFACERB;
- rtlphy->phyreg_def[RF90_PATH_D].rfintfi = RFPGA0_XCD_RFINTERFACERB;
-
- rtlphy->phyreg_def[RF90_PATH_A].rfintfo = RFPGA0_XA_RFINTERFACEOE;
- rtlphy->phyreg_def[RF90_PATH_B].rfintfo = RFPGA0_XB_RFINTERFACEOE;
-
- rtlphy->phyreg_def[RF90_PATH_A].rfintfe = RFPGA0_XA_RFINTERFACEOE;
- rtlphy->phyreg_def[RF90_PATH_B].rfintfe = RFPGA0_XB_RFINTERFACEOE;
-
- rtlphy->phyreg_def[RF90_PATH_A].rf3wire_offset =
- RFPGA0_XA_LSSIPARAMETER;
- rtlphy->phyreg_def[RF90_PATH_B].rf3wire_offset =
- RFPGA0_XB_LSSIPARAMETER;
-
- rtlphy->phyreg_def[RF90_PATH_A].rflssi_select = rFPGA0_XAB_RFPARAMETER;
- rtlphy->phyreg_def[RF90_PATH_B].rflssi_select = rFPGA0_XAB_RFPARAMETER;
- rtlphy->phyreg_def[RF90_PATH_C].rflssi_select = rFPGA0_XCD_RFPARAMETER;
- rtlphy->phyreg_def[RF90_PATH_D].rflssi_select = rFPGA0_XCD_RFPARAMETER;
-
- rtlphy->phyreg_def[RF90_PATH_A].rftxgain_stage = RFPGA0_TXGAINSTAGE;
- rtlphy->phyreg_def[RF90_PATH_B].rftxgain_stage = RFPGA0_TXGAINSTAGE;
- rtlphy->phyreg_def[RF90_PATH_C].rftxgain_stage = RFPGA0_TXGAINSTAGE;
- rtlphy->phyreg_def[RF90_PATH_D].rftxgain_stage = RFPGA0_TXGAINSTAGE;
-
- rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para1 = RFPGA0_XA_HSSIPARAMETER1;
- rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para1 = RFPGA0_XB_HSSIPARAMETER1;
-
- rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para2 = RFPGA0_XA_HSSIPARAMETER2;
- rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para2 = RFPGA0_XB_HSSIPARAMETER2;
-
- rtlphy->phyreg_def[RF90_PATH_A].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL;
- rtlphy->phyreg_def[RF90_PATH_B].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL;
- rtlphy->phyreg_def[RF90_PATH_C].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL;
- rtlphy->phyreg_def[RF90_PATH_D].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL;
-
- rtlphy->phyreg_def[RF90_PATH_A].rfagc_control1 = ROFDM0_XAAGCCORE1;
- rtlphy->phyreg_def[RF90_PATH_B].rfagc_control1 = ROFDM0_XBAGCCORE1;
- rtlphy->phyreg_def[RF90_PATH_C].rfagc_control1 = ROFDM0_XCAGCCORE1;
- rtlphy->phyreg_def[RF90_PATH_D].rfagc_control1 = ROFDM0_XDAGCCORE1;
-
- rtlphy->phyreg_def[RF90_PATH_A].rfagc_control2 = ROFDM0_XAAGCCORE2;
- rtlphy->phyreg_def[RF90_PATH_B].rfagc_control2 = ROFDM0_XBAGCCORE2;
- rtlphy->phyreg_def[RF90_PATH_C].rfagc_control2 = ROFDM0_XCAGCCORE2;
- rtlphy->phyreg_def[RF90_PATH_D].rfagc_control2 = ROFDM0_XDAGCCORE2;
-
- rtlphy->phyreg_def[RF90_PATH_A].rfrxiq_imbal = ROFDM0_XARXIQIMBALANCE;
- rtlphy->phyreg_def[RF90_PATH_B].rfrxiq_imbal = ROFDM0_XBRXIQIMBALANCE;
- rtlphy->phyreg_def[RF90_PATH_C].rfrxiq_imbal = ROFDM0_XCRXIQIMBANLANCE;
- rtlphy->phyreg_def[RF90_PATH_D].rfrxiq_imbal = ROFDM0_XDRXIQIMBALANCE;
-
- rtlphy->phyreg_def[RF90_PATH_A].rfrx_afe = ROFDM0_XARXAFE;
- rtlphy->phyreg_def[RF90_PATH_B].rfrx_afe = ROFDM0_XBRXAFE;
- rtlphy->phyreg_def[RF90_PATH_C].rfrx_afe = ROFDM0_XCRXAFE;
- rtlphy->phyreg_def[RF90_PATH_D].rfrx_afe = ROFDM0_XDRXAFE;
-
- rtlphy->phyreg_def[RF90_PATH_A].rftxiq_imbal = ROFDM0_XATXIQIMBALANCE;
- rtlphy->phyreg_def[RF90_PATH_B].rftxiq_imbal = ROFDM0_XBTXIQIMBALANCE;
- rtlphy->phyreg_def[RF90_PATH_C].rftxiq_imbal = ROFDM0_XCTXIQIMBALANCE;
- rtlphy->phyreg_def[RF90_PATH_D].rftxiq_imbal = ROFDM0_XDTXIQIMBALANCE;
-
- rtlphy->phyreg_def[RF90_PATH_A].rftx_afe = ROFDM0_XATXAFE;
- rtlphy->phyreg_def[RF90_PATH_B].rftx_afe = ROFDM0_XBTXAFE;
- rtlphy->phyreg_def[RF90_PATH_C].rftx_afe = ROFDM0_XCTXAFE;
- rtlphy->phyreg_def[RF90_PATH_D].rftx_afe = ROFDM0_XDTXAFE;
-
- rtlphy->phyreg_def[RF90_PATH_A].rf_rb = RFPGA0_XA_LSSIREADBACK;
- rtlphy->phyreg_def[RF90_PATH_B].rf_rb = RFPGA0_XB_LSSIREADBACK;
- rtlphy->phyreg_def[RF90_PATH_C].rf_rb = RFPGA0_XC_LSSIREADBACK;
- rtlphy->phyreg_def[RF90_PATH_D].rf_rb = RFPGA0_XD_LSSIREADBACK;
-
- rtlphy->phyreg_def[RF90_PATH_A].rf_rbpi = TRANSCEIVEA_HSPI_READBACK;
- rtlphy->phyreg_def[RF90_PATH_B].rf_rbpi = TRANSCEIVEB_HSPI_READBACK;
-}
-
void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -785,17 +528,17 @@ void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel)
long txpwr_dbm;
txpwr_level = rtlphy->cur_cck_txpwridx;
- txpwr_dbm = _phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_B, txpwr_level);
+ txpwr_dbm = rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_B, txpwr_level);
txpwr_level = rtlphy->cur_ofdm24g_txpwridx +
rtlefuse->legacy_ht_txpowerdiff;
- if (_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level) > txpwr_dbm)
- txpwr_dbm = _phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G,
+ if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level) > txpwr_dbm)
+ txpwr_dbm = rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G,
txpwr_level);
txpwr_level = rtlphy->cur_ofdm24g_txpwridx;
- if (_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, txpwr_level) >
+ if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, txpwr_level) >
txpwr_dbm)
- txpwr_dbm = _phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G,
- txpwr_level);
+ txpwr_dbm = rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G,
+ txpwr_level);
*powerlevel = txpwr_dbm;
}
@@ -912,28 +655,6 @@ static u8 _phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw,
return txpwridx;
}
-static long _phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw,
- enum wireless_mode wirelessmode, u8 txpwridx)
-{
- long offset;
- long pwrout_dbm;
-
- switch (wirelessmode) {
- case WIRELESS_MODE_B:
- offset = -7;
- break;
- case WIRELESS_MODE_G:
- case WIRELESS_MODE_N_24G:
- offset = -8;
- break;
- default:
- offset = -8;
- break;
- }
- pwrout_dbm = txpwridx / 2 + offset;
- return pwrout_dbm;
-}
-
void rtl8723ae_phy_set_bw_mode_callback(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -1117,26 +838,26 @@ static bool _phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel,
u8 num_total_rfpath = rtlphy->num_total_rfpath;
precommoncmdcnt = 0;
- _phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++,
- MAX_PRECMD_CNT, CMDID_SET_TXPOWEROWER_LEVEL,
- 0, 0, 0);
- _phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++,
- MAX_PRECMD_CNT, CMDID_END, 0, 0, 0);
+ rtl8723_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++,
+ MAX_PRECMD_CNT, CMDID_SET_TXPOWEROWER_LEVEL,
+ 0, 0, 0);
+ rtl8723_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++,
+ MAX_PRECMD_CNT, CMDID_END, 0, 0, 0);
postcommoncmdcnt = 0;
- _phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++,
- MAX_POSTCMD_CNT, CMDID_END, 0, 0, 0);
+ rtl8723_phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++,
+ MAX_POSTCMD_CNT, CMDID_END, 0, 0, 0);
rfdependcmdcnt = 0;
RT_ASSERT((channel >= 1 && channel <= 14),
"illegal channel for Zebra: %d\n", channel);
- _phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++,
- MAX_RFDEPENDCMD_CNT, CMDID_RF_WRITEREG,
+ rtl8723_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++,
+ MAX_RFDEPENDCMD_CNT, CMDID_RF_WRITEREG,
RF_CHNLBW, channel, 10);
- _phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++,
- MAX_RFDEPENDCMD_CNT, CMDID_END, 0, 0, 0);
+ rtl8723_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++,
+ MAX_RFDEPENDCMD_CNT, CMDID_END, 0, 0, 0);
do {
switch (*stage) {
@@ -1204,29 +925,6 @@ static bool _phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel,
return false;
}
-static bool _phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable,
- u32 cmdtableidx, u32 cmdtablesz,
- enum swchnlcmd_id cmdid, u32 para1,
- u32 para2, u32 msdelay)
-{
- struct swchnlcmd *pcmd;
-
- if (cmdtable == NULL) {
- RT_ASSERT(false, "cmdtable cannot be NULL.\n");
- return false;
- }
-
- if (cmdtableidx >= cmdtablesz)
- return false;
-
- pcmd = cmdtable + cmdtableidx;
- pcmd->cmdid = cmdid;
- pcmd->para1 = para1;
- pcmd->para2 = para2;
- pcmd->msdelay = msdelay;
- return true;
-}
-
static u8 _rtl8723ae_phy_path_a_iqk(struct ieee80211_hw *hw, bool config_pathb)
{
u32 reg_eac, reg_e94, reg_e9c, reg_ea4;
@@ -1297,136 +995,6 @@ static u8 _rtl8723ae_phy_path_b_iqk(struct ieee80211_hw *hw)
return result;
}
-static void phy_path_a_fill_iqk_matrix(struct ieee80211_hw *hw, bool iqk_ok,
- long result[][8], u8 final_candidate,
- bool btxonly)
-{
- u32 oldval_0, x, tx0_a, reg;
- long y, tx0_c;
-
- if (final_candidate == 0xFF) {
- return;
- } else if (iqk_ok) {
- oldval_0 = (rtl_get_bbreg(hw, ROFDM0_XATXIQIMBALANCE,
- MASKDWORD) >> 22) & 0x3FF;
- x = result[final_candidate][0];
- if ((x & 0x00000200) != 0)
- x = x | 0xFFFFFC00;
- tx0_a = (x * oldval_0) >> 8;
- rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x3FF, tx0_a);
- rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(31),
- ((x * oldval_0 >> 7) & 0x1));
- y = result[final_candidate][1];
- if ((y & 0x00000200) != 0)
- y = y | 0xFFFFFC00;
- tx0_c = (y * oldval_0) >> 8;
- rtl_set_bbreg(hw, ROFDM0_XCTXAFE, 0xF0000000,
- ((tx0_c & 0x3C0) >> 6));
- rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x003F0000,
- (tx0_c & 0x3F));
- rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(29),
- ((y * oldval_0 >> 7) & 0x1));
- if (btxonly)
- return;
- reg = result[final_candidate][2];
- rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0x3FF, reg);
- reg = result[final_candidate][3] & 0x3F;
- rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0xFC00, reg);
- reg = (result[final_candidate][3] >> 6) & 0xF;
- rtl_set_bbreg(hw, 0xca0, 0xF0000000, reg);
- }
-}
-
-static void phy_save_adda_regs(struct ieee80211_hw *hw,
- u32 *addareg, u32 *addabackup,
- u32 registernum)
-{
- u32 i;
-
- for (i = 0; i < registernum; i++)
- addabackup[i] = rtl_get_bbreg(hw, addareg[i], MASKDWORD);
-}
-
-static void phy_save_mac_regs(struct ieee80211_hw *hw, u32 *macreg,
- u32 *macbackup)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- u32 i;
-
- for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++)
- macbackup[i] = rtl_read_byte(rtlpriv, macreg[i]);
- macbackup[i] = rtl_read_dword(rtlpriv, macreg[i]);
-}
-
-static void phy_reload_adda_regs(struct ieee80211_hw *hw, u32 *addareg,
- u32 *addabackup, u32 regiesternum)
-{
- u32 i;
-
- for (i = 0; i < regiesternum; i++)
- rtl_set_bbreg(hw, addareg[i], MASKDWORD, addabackup[i]);
-}
-
-static void phy_reload_mac_regs(struct ieee80211_hw *hw, u32 *macreg,
- u32 *macbackup)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- u32 i;
-
- for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++)
- rtl_write_byte(rtlpriv, macreg[i], (u8) macbackup[i]);
- rtl_write_dword(rtlpriv, macreg[i], macbackup[i]);
-}
-
-static void _rtl8723ae_phy_path_adda_on(struct ieee80211_hw *hw,
- u32 *addareg, bool is_patha_on,
- bool is2t)
-{
- u32 pathOn;
- u32 i;
-
- pathOn = is_patha_on ? 0x04db25a4 : 0x0b1b25a4;
- if (false == is2t) {
- pathOn = 0x0bdb25a0;
- rtl_set_bbreg(hw, addareg[0], MASKDWORD, 0x0b1b25a0);
- } else {
- rtl_set_bbreg(hw, addareg[0], MASKDWORD, pathOn);
- }
-
- for (i = 1; i < IQK_ADDA_REG_NUM; i++)
- rtl_set_bbreg(hw, addareg[i], MASKDWORD, pathOn);
-}
-
-static void _rtl8723ae_phy_mac_setting_calibration(struct ieee80211_hw *hw,
- u32 *macreg, u32 *macbackup)
-{
- struct rtl_priv *rtlpriv = rtl_priv(hw);
- u32 i = 0;
-
- rtl_write_byte(rtlpriv, macreg[i], 0x3F);
-
- for (i = 1; i < (IQK_MAC_REG_NUM - 1); i++)
- rtl_write_byte(rtlpriv, macreg[i],
- (u8) (macbackup[i] & (~BIT(3))));
- rtl_write_byte(rtlpriv, macreg[i], (u8) (macbackup[i] & (~BIT(5))));
-}
-
-static void _rtl8723ae_phy_path_a_standby(struct ieee80211_hw *hw)
-{
- rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x0);
- rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000);
- rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000);
-}
-
-static void _rtl8723ae_phy_pi_mode_switch(struct ieee80211_hw *hw, bool pi_mode)
-{
- u32 mode;
-
- mode = pi_mode ? 0x01000100 : 0x01000000;
- rtl_set_bbreg(hw, 0x820, MASKDWORD, mode);
- rtl_set_bbreg(hw, 0x828, MASKDWORD, mode);
-}
-
static bool phy_simularity_comp(struct ieee80211_hw *hw, long result[][8],
u8 c1, u8 c2)
{
@@ -1498,10 +1066,12 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw,
const u32 retrycount = 2;
if (t == 0) {
- phy_save_adda_regs(hw, adda_reg, rtlphy->adda_backup, 16);
- phy_save_mac_regs(hw, iqk_mac_reg, rtlphy->iqk_mac_backup);
+ rtl8723_save_adda_registers(hw, adda_reg, rtlphy->adda_backup,
+ 16);
+ rtl8723_phy_save_mac_registers(hw, iqk_mac_reg,
+ rtlphy->iqk_mac_backup);
}
- _rtl8723ae_phy_path_adda_on(hw, adda_reg, true, is2t);
+ rtl8723_phy_path_adda_on(hw, adda_reg, true, is2t);
if (t == 0) {
rtlphy->rfpi_enable = (u8) rtl_get_bbreg(hw,
RFPGA0_XA_HSSIPARAMETER1,
@@ -1509,7 +1079,7 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw,
}
if (!rtlphy->rfpi_enable)
- _rtl8723ae_phy_pi_mode_switch(hw, true);
+ rtl8723_phy_pi_mode_switch(hw, true);
if (t == 0) {
rtlphy->reg_c04 = rtl_get_bbreg(hw, 0xc04, MASKDWORD);
rtlphy->reg_c08 = rtl_get_bbreg(hw, 0xc08, MASKDWORD);
@@ -1522,7 +1092,7 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw,
rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000);
rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00010000);
}
- _rtl8723ae_phy_mac_setting_calibration(hw, iqk_mac_reg,
+ rtl8723_phy_mac_setting_calibration(hw, iqk_mac_reg,
rtlphy->iqk_mac_backup);
rtl_set_bbreg(hw, 0xb68, MASKDWORD, 0x00080000);
if (is2t)
@@ -1552,8 +1122,8 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw,
}
if (is2t) {
- _rtl8723ae_phy_path_a_standby(hw);
- _rtl8723ae_phy_path_adda_on(hw, adda_reg, false, is2t);
+ rtl8723_phy_path_a_standby(hw);
+ rtl8723_phy_path_adda_on(hw, adda_reg, false, is2t);
for (i = 0; i < retrycount; i++) {
pathb_ok = _rtl8723ae_phy_path_b_iqk(hw);
if (pathb_ok == 0x03) {
@@ -1588,9 +1158,11 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw,
rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00032ed3);
if (t != 0) {
if (!rtlphy->rfpi_enable)
- _rtl8723ae_phy_pi_mode_switch(hw, false);
- phy_reload_adda_regs(hw, adda_reg, rtlphy->adda_backup, 16);
- phy_reload_mac_regs(hw, iqk_mac_reg, rtlphy->iqk_mac_backup);
+ rtl8723_phy_pi_mode_switch(hw, false);
+ rtl8723_phy_reload_adda_registers(hw, adda_reg,
+ rtlphy->adda_backup, 16);
+ rtl8723_phy_reload_mac_registers(hw, iqk_mac_reg,
+ rtlphy->iqk_mac_backup);
}
}
@@ -1691,7 +1263,8 @@ void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery)
};
if (recovery) {
- phy_reload_adda_regs(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 10);
+ rtl8723_phy_reload_adda_registers(hw, iqk_bb_reg,
+ rtlphy->iqk_bb_backup, 10);
return;
}
if (start_conttx || singletone)
@@ -1756,9 +1329,10 @@ void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery)
rtlphy->reg_e9c = rtlphy->reg_ebc = 0x0;
}
if (reg_e94 != 0) /*&&(reg_ea4 != 0) */
- phy_path_a_fill_iqk_matrix(hw, patha_ok, result,
- final_candidate, (reg_ea4 == 0));
- phy_save_adda_regs(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 10);
+ rtl8723_phy_path_a_fill_iqk_matrix(hw, patha_ok, result,
+ final_candidate,
+ (reg_ea4 == 0));
+ rtl8723_save_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 10);
}
void rtl8723ae_phy_lc_calibrate(struct ieee80211_hw *hw)
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h
index 007ebdbbe108..cd43139ed332 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h
@@ -76,23 +76,6 @@
#define RTL92C_MAX_PATH_NUM 2
-enum swchnlcmd_id {
- CMDID_END,
- CMDID_SET_TXPOWEROWER_LEVEL,
- CMDID_BBREGWRITE10,
- CMDID_WRITEPORT_ULONG,
- CMDID_WRITEPORT_USHORT,
- CMDID_WRITEPORT_UCHAR,
- CMDID_RF_WRITEREG,
-};
-
-struct swchnlcmd {
- enum swchnlcmd_id cmdid;
- u32 para1;
- u32 para2;
- u32 msdelay;
-};
-
enum hw90_block_e {
HW90_BLOCK_MAC = 0,
HW90_BLOCK_PHY0 = 1,
@@ -183,10 +166,6 @@ struct tx_power_struct {
u32 mcs_original_offset[4][16];
};
-u32 rtl8723ae_phy_query_bb_reg(struct ieee80211_hw *hw,
- u32 regaddr, u32 bitmask);
-void rtl8723ae_phy_set_bb_reg(struct ieee80211_hw *hw,
- u32 regaddr, u32 bitmask, u32 data);
u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw,
enum radio_path rfpath, u32 regaddr,
u32 bitmask);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h
index 7a46f9fdf558..a418acb4d0ca 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h
@@ -30,7 +30,6 @@
#ifndef __RTL8723E_PWRSEQ_H__
#define __RTL8723E_PWRSEQ_H__
-#include "pwrseqcmd.h"
/*
Check document WM-20110607-Paul-RTL8723A_Power_Architecture-R02.vsd
There are 6 HW Power States:
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h b/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h
index 199da366c6da..64376b38708b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h
@@ -2059,22 +2059,6 @@
#define BWORD1 0xc
#define BWORD 0xf
-#define MASKBYTE0 0xff
-#define MASKBYTE1 0xff00
-#define MASKBYTE2 0xff0000
-#define MASKBYTE3 0xff000000
-#define MASKHWORD 0xffff0000
-#define MASKLWORD 0x0000ffff
-#define MASKDWORD 0xffffffff
-#define MASK12BITS 0xfff
-#define MASKH4BITS 0xf0000000
-#define MASKOFDM_D 0xffc00000
-#define MASKCCK 0x3f3f3f3f
-
-#define MASK4BITS 0x0f
-#define MASK20BITS 0xfffff
-#define RFREG_OFFSET_MASK 0xfffff
-
#define BENABLE 0x1
#define BDISABLE 0x0
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c
index 62b204faf773..1087a3bd07fa 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c
@@ -37,8 +37,11 @@
#include "reg.h"
#include "def.h"
#include "phy.h"
+#include "../rtl8723com/phy_common.h"
#include "dm.h"
#include "hw.h"
+#include "fw.h"
+#include "../rtl8723com/fw_common.h"
#include "sw.h"
#include "trx.h"
#include "led.h"
@@ -193,6 +196,11 @@ void rtl8723ae_deinit_sw_vars(struct ieee80211_hw *hw)
}
}
+static bool is_fw_header(struct rtl92c_firmware_header *hdr)
+{
+ return (hdr->signature & 0xfff0) == 0x2300;
+}
+
static struct rtl_hal_ops rtl8723ae_hal_ops = {
.init_sw_vars = rtl8723ae_init_sw_vars,
.deinit_sw_vars = rtl8723ae_deinit_sw_vars,
@@ -231,13 +239,14 @@ static struct rtl_hal_ops rtl8723ae_hal_ops = {
.set_key = rtl8723ae_set_key,
.init_sw_leds = rtl8723ae_init_sw_leds,
.allow_all_destaddr = rtl8723ae_allow_all_destaddr,
- .get_bbreg = rtl8723ae_phy_query_bb_reg,
- .set_bbreg = rtl8723ae_phy_set_bb_reg,
+ .get_bbreg = rtl8723_phy_query_bb_reg,
+ .set_bbreg = rtl8723_phy_set_bb_reg,
.get_rfreg = rtl8723ae_phy_query_rf_reg,
.set_rfreg = rtl8723ae_phy_set_rf_reg,
.c2h_command_handle = rtl_8723e_c2h_command_handle,
.bt_wifi_media_status_notify = rtl_8723e_bt_wifi_media_status_notify,
.bt_coex_off_before_lps = rtl8723ae_bt_coex_off_before_lps,
+ .is_fw_header = is_fw_header,
};
static struct rtl_mod_params rtl8723ae_mod_params = {
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
index 50b7be3f3a60..10b7577b6ae5 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
@@ -334,7 +334,7 @@ bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw,
/* during testing, hdr could be NULL here */
return false;
}
- if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+ if ((_ieee80211_is_robust_mgmt_frame(hdr)) &&
(ieee80211_has_protected(hdr->frame_control)))
rx_status->flag &= ~RX_FLAG_DECRYPTED;
else
@@ -365,7 +365,7 @@ bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw,
void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr, u8 *pdesc_tx,
- struct ieee80211_tx_info *info,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
struct sk_buff *skb, u8 hw_queue,
struct rtl_tcb_desc *ptcdesc)
@@ -375,7 +375,7 @@ void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw,
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
bool defaultadapter = true;
- u8 *pdesc = (u8 *) pdesc_tx;
+ u8 *pdesc = pdesc_tx;
u16 seq_number;
__le16 fc = hdr->frame_control;
u8 fw_qsel = _rtl8723ae_map_hwqueue_to_fwqueue(skb, hw_queue);
@@ -577,7 +577,7 @@ void rtl8723ae_tx_fill_cmddesc(struct ieee80211_hw *hw,
SET_TX_DESC_OWN(pdesc, 1);
- SET_TX_DESC_PKT_SIZE((u8 *) pdesc, (u16) (skb->len));
+ SET_TX_DESC_PKT_SIZE(pdesc, (u16) (skb->len));
SET_TX_DESC_FIRST_SEG(pdesc, 1);
SET_TX_DESC_LAST_SEG(pdesc, 1);
@@ -597,7 +597,8 @@ void rtl8723ae_tx_fill_cmddesc(struct ieee80211_hw *hw,
pdesc, TX_DESC_SIZE);
}
-void rtl8723ae_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val)
+void rtl8723ae_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val)
{
if (istx == true) {
switch (desc_name) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.h b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.h
index ad05b54bc0f1..4380b7d3a91a 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.h
@@ -521,12 +521,6 @@ do { \
memset(__pdesc, 0, _size); \
} while (0)
-#define RTL8723E_RX_HAL_IS_CCK_RATE(rxmcs) \
- ((rxmcs) == DESC92_RATE1M || \
- (rxmcs) == DESC92_RATE2M || \
- (rxmcs) == DESC92_RATE5_5M || \
- (rxmcs) == DESC92_RATE11M)
-
struct rx_fwinfo_8723e {
u8 gain_trsw[4];
u8 pwdb_all;
@@ -706,8 +700,8 @@ struct rx_desc_8723e {
} __packed;
void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw,
- struct ieee80211_hdr *hdr, u8 *pdesc_tx,
- struct ieee80211_tx_info *info,
+ struct ieee80211_hdr *hdr, u8 *pdesc,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
struct sk_buff *skb, u8 hw_queue,
struct rtl_tcb_desc *ptcb_desc);
@@ -715,7 +709,8 @@ bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw,
struct rtl_stats *status,
struct ieee80211_rx_status *rx_status,
u8 *pdesc, struct sk_buff *skb);
-void rtl8723ae_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val);
+void rtl8723ae_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val);
u32 rtl8723ae_get_desc(u8 *pdesc, bool istx, u8 desc_name);
void rtl8723ae_tx_polling(struct ieee80211_hw *hw, u8 hw_queue);
void rtl8723ae_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/Makefile b/drivers/net/wireless/rtlwifi/rtl8723be/Makefile
new file mode 100644
index 000000000000..59e416abd93a
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/Makefile
@@ -0,0 +1,19 @@
+obj-m := rtl8723be.o
+
+
+rtl8723be-objs := \
+ dm.o \
+ fw.o \
+ hw.o \
+ led.o \
+ phy.o \
+ pwrseq.o \
+ rf.o \
+ sw.o \
+ table.o \
+ trx.o \
+
+
+obj-$(CONFIG_RTL8723BE) += rtl8723be.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/def.h b/drivers/net/wireless/rtlwifi/rtl8723be/def.h
new file mode 100644
index 000000000000..3c30b74e983d
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/def.h
@@ -0,0 +1,248 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_DEF_H__
+#define __RTL8723BE_DEF_H__
+
+#define HAL_RETRY_LIMIT_INFRA 48
+#define HAL_RETRY_LIMIT_AP_ADHOC 7
+
+#define RESET_DELAY_8185 20
+
+#define RT_IBSS_INT_MASKS (IMR_BCNINT | IMR_TBDOK | IMR_TBDER)
+#define RT_AC_INT_MASKS (IMR_VIDOK | IMR_VODOK | IMR_BEDOK|IMR_BKDOK)
+
+#define NUM_OF_FIRMWARE_QUEUE 10
+#define NUM_OF_PAGES_IN_FW 0x100
+#define NUM_OF_PAGE_IN_FW_QUEUE_BK 0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_BE 0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_VI 0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_VO 0x07
+#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA 0x0
+#define NUM_OF_PAGE_IN_FW_QUEUE_CMD 0x0
+#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT 0x02
+#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH 0x02
+#define NUM_OF_PAGE_IN_FW_QUEUE_BCN 0x2
+#define NUM_OF_PAGE_IN_FW_QUEUE_PUB 0xA1
+
+#define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM 0x026
+#define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM 0x048
+#define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM 0x048
+#define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM 0x026
+#define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM 0x00
+
+#define MAX_LINES_HWCONFIG_TXT 1000
+#define MAX_BYTES_LINE_HWCONFIG_TXT 256
+
+#define SW_THREE_WIRE 0
+#define HW_THREE_WIRE 2
+
+#define BT_DEMO_BOARD 0
+#define BT_QA_BOARD 1
+#define BT_FPGA 2
+
+#define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0
+#define HAL_PRIME_CHNL_OFFSET_LOWER 1
+#define HAL_PRIME_CHNL_OFFSET_UPPER 2
+
+#define MAX_H2C_QUEUE_NUM 10
+
+#define RX_MPDU_QUEUE 0
+#define RX_CMD_QUEUE 1
+#define RX_MAX_QUEUE 2
+#define AC2QUEUEID(_AC) (_AC)
+
+#define C2H_RX_CMD_HDR_LEN 8
+#define GET_C2H_CMD_CMD_LEN(__prxhdr) \
+ LE_BITS_TO_4BYTE((__prxhdr), 0, 16)
+#define GET_C2H_CMD_ELEMENT_ID(__prxhdr) \
+ LE_BITS_TO_4BYTE((__prxhdr), 16, 8)
+#define GET_C2H_CMD_CMD_SEQ(__prxhdr) \
+ LE_BITS_TO_4BYTE((__prxhdr), 24, 7)
+#define GET_C2H_CMD_CONTINUE(__prxhdr) \
+ LE_BITS_TO_4BYTE((__prxhdr), 31, 1)
+#define GET_C2H_CMD_CONTENT(__prxhdr) \
+ ((u8 *)(__prxhdr) + C2H_RX_CMD_HDR_LEN)
+
+#define GET_C2H_CMD_FEEDBACK_ELEMENT_ID(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE((__pcmdfbhdr), 0, 8)
+#define GET_C2H_CMD_FEEDBACK_CCX_LEN(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE((__pcmdfbhdr), 8, 8)
+#define GET_C2H_CMD_FEEDBACK_CCX_CMD_CNT(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE((__pcmdfbhdr), 16, 16)
+#define GET_C2H_CMD_FEEDBACK_CCX_MAC_ID(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 0, 5)
+#define GET_C2H_CMD_FEEDBACK_CCX_VALID(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 7, 1)
+#define GET_C2H_CMD_FEEDBACK_CCX_RETRY_CNT(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 8, 5)
+#define GET_C2H_CMD_FEEDBACK_CCX_TOK(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 15, 1)
+#define GET_C2H_CMD_FEEDBACK_CCX_QSEL(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 16, 4)
+#define GET_C2H_CMD_FEEDBACK_CCX_SEQ(__pcmdfbhdr) \
+ LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 20, 12)
+
+#define CHIP_BONDING_IDENTIFIER(_value) (((_value)>>22)&0x3)
+#define CHIP_BONDING_92C_1T2R 0x1
+
+#define CHIP_8723 BIT(0)
+#define CHIP_8723B (BIT(1) | BIT(2))
+#define NORMAL_CHIP BIT(3)
+#define RF_TYPE_1T1R (~(BIT(4) | BIT(5) | BIT(6)))
+#define RF_TYPE_1T2R BIT(4)
+#define RF_TYPE_2T2R BIT(5)
+#define CHIP_VENDOR_UMC BIT(7)
+#define B_CUT_VERSION BIT(12)
+#define C_CUT_VERSION BIT(13)
+#define D_CUT_VERSION ((BIT(12) | BIT(13)))
+#define E_CUT_VERSION BIT(14)
+#define RF_RL_ID (BIT(31) | BIT(30) | BIT(29) | BIT(28))
+
+/* MASK */
+#define IC_TYPE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define CHIP_TYPE_MASK BIT(3)
+#define RF_TYPE_MASK (BIT(4) | BIT(5) | BIT(6))
+#define MANUFACTUER_MASK BIT(7)
+#define ROM_VERSION_MASK (BIT(11) | BIT(10) | BIT(9) | BIT(8))
+#define CUT_VERSION_MASK (BIT(15) | BIT(14) | BIT(13) | BIT(12))
+
+/* Get element */
+#define GET_CVID_IC_TYPE(version) ((version) & IC_TYPE_MASK)
+#define GET_CVID_CHIP_TYPE(version) ((version) & CHIP_TYPE_MASK)
+#define GET_CVID_RF_TYPE(version) ((version) & RF_TYPE_MASK)
+#define GET_CVID_MANUFACTUER(version) ((version) & MANUFACTUER_MASK)
+#define GET_CVID_ROM_VERSION(version) ((version) & ROM_VERSION_MASK)
+#define GET_CVID_CUT_VERSION(version) ((version) & CUT_VERSION_MASK)
+
+#define IS_92C_SERIAL(version) ((IS_81XXC(version) && IS_2T2R(version)) ?\
+ true : false)
+#define IS_81XXC(version) ((GET_CVID_IC_TYPE(version) == 0) ?\
+ true : false)
+#define IS_8723_SERIES(version) ((GET_CVID_IC_TYPE(version) == CHIP_8723) ?\
+ true : false)
+#define IS_1T1R(version) ((GET_CVID_RF_TYPE(version)) ? false : true)
+#define IS_1T2R(version) ((GET_CVID_RF_TYPE(version) == RF_TYPE_1T2R)\
+ ? true : false)
+#define IS_2T2R(version) ((GET_CVID_RF_TYPE(version) == RF_TYPE_2T2R)\
+ ? true : false)
+enum rf_optype {
+ RF_OP_BY_SW_3WIRE = 0,
+ RF_OP_BY_FW,
+ RF_OP_MAX
+};
+
+enum rf_power_state {
+ RF_ON,
+ RF_OFF,
+ RF_SLEEP,
+ RF_SHUT_DOWN,
+};
+
+enum power_save_mode {
+ POWER_SAVE_MODE_ACTIVE,
+ POWER_SAVE_MODE_SAVE,
+};
+
+enum power_polocy_config {
+ POWERCFG_MAX_POWER_SAVINGS,
+ POWERCFG_GLOBAL_POWER_SAVINGS,
+ POWERCFG_LOCAL_POWER_SAVINGS,
+ POWERCFG_LENOVO,
+};
+
+enum interface_select_pci {
+ INTF_SEL1_MINICARD = 0,
+ INTF_SEL0_PCIE = 1,
+ INTF_SEL2_RSV = 2,
+ INTF_SEL3_RSV = 3,
+};
+
+enum rtl_desc_qsel {
+ QSLT_BK = 0x2,
+ QSLT_BE = 0x0,
+ QSLT_VI = 0x5,
+ QSLT_VO = 0x7,
+ QSLT_BEACON = 0x10,
+ QSLT_HIGH = 0x11,
+ QSLT_MGNT = 0x12,
+ QSLT_CMD = 0x13,
+};
+
+enum rtl_desc8723e_rate {
+ DESC92C_RATE1M = 0x00,
+ DESC92C_RATE2M = 0x01,
+ DESC92C_RATE5_5M = 0x02,
+ DESC92C_RATE11M = 0x03,
+
+ DESC92C_RATE6M = 0x04,
+ DESC92C_RATE9M = 0x05,
+ DESC92C_RATE12M = 0x06,
+ DESC92C_RATE18M = 0x07,
+ DESC92C_RATE24M = 0x08,
+ DESC92C_RATE36M = 0x09,
+ DESC92C_RATE48M = 0x0a,
+ DESC92C_RATE54M = 0x0b,
+
+ DESC92C_RATEMCS0 = 0x0c,
+ DESC92C_RATEMCS1 = 0x0d,
+ DESC92C_RATEMCS2 = 0x0e,
+ DESC92C_RATEMCS3 = 0x0f,
+ DESC92C_RATEMCS4 = 0x10,
+ DESC92C_RATEMCS5 = 0x11,
+ DESC92C_RATEMCS6 = 0x12,
+ DESC92C_RATEMCS7 = 0x13,
+ DESC92C_RATEMCS8 = 0x14,
+ DESC92C_RATEMCS9 = 0x15,
+ DESC92C_RATEMCS10 = 0x16,
+ DESC92C_RATEMCS11 = 0x17,
+ DESC92C_RATEMCS12 = 0x18,
+ DESC92C_RATEMCS13 = 0x19,
+ DESC92C_RATEMCS14 = 0x1a,
+ DESC92C_RATEMCS15 = 0x1b,
+ DESC92C_RATEMCS15_SG = 0x1c,
+ DESC92C_RATEMCS32 = 0x20,
+};
+
+enum rx_packet_type {
+ NORMAL_RX,
+ TX_REPORT1,
+ TX_REPORT2,
+ HIS_REPORT,
+};
+
+struct phy_sts_cck_8723e_t {
+ u8 adc_pwdb_X[4];
+ u8 sq_rpt;
+ u8 cck_agc_rpt;
+};
+
+struct h2c_cmd_8723e {
+ u8 element_id;
+ u32 cmd_len;
+ u8 *p_cmdbuffer;
+};
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c
new file mode 100644
index 000000000000..13d53a1df789
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c
@@ -0,0 +1,1325 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../base.h"
+#include "../pci.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "dm.h"
+#include "../rtl8723com/dm_common.h"
+#include "fw.h"
+#include "../rtl8723com/fw_common.h"
+#include "trx.h"
+#include "../btcoexist/rtl_btc.h"
+
+static const u32 ofdmswing_table[] = {
+ 0x0b40002d, /* 0, -15.0dB */
+ 0x0c000030, /* 1, -14.5dB */
+ 0x0cc00033, /* 2, -14.0dB */
+ 0x0d800036, /* 3, -13.5dB */
+ 0x0e400039, /* 4, -13.0dB */
+ 0x0f00003c, /* 5, -12.5dB */
+ 0x10000040, /* 6, -12.0dB */
+ 0x11000044, /* 7, -11.5dB */
+ 0x12000048, /* 8, -11.0dB */
+ 0x1300004c, /* 9, -10.5dB */
+ 0x14400051, /* 10, -10.0dB */
+ 0x15800056, /* 11, -9.5dB */
+ 0x16c0005b, /* 12, -9.0dB */
+ 0x18000060, /* 13, -8.5dB */
+ 0x19800066, /* 14, -8.0dB */
+ 0x1b00006c, /* 15, -7.5dB */
+ 0x1c800072, /* 16, -7.0dB */
+ 0x1e400079, /* 17, -6.5dB */
+ 0x20000080, /* 18, -6.0dB */
+ 0x22000088, /* 19, -5.5dB */
+ 0x24000090, /* 20, -5.0dB */
+ 0x26000098, /* 21, -4.5dB */
+ 0x288000a2, /* 22, -4.0dB */
+ 0x2ac000ab, /* 23, -3.5dB */
+ 0x2d4000b5, /* 24, -3.0dB */
+ 0x300000c0, /* 25, -2.5dB */
+ 0x32c000cb, /* 26, -2.0dB */
+ 0x35c000d7, /* 27, -1.5dB */
+ 0x390000e4, /* 28, -1.0dB */
+ 0x3c8000f2, /* 29, -0.5dB */
+ 0x40000100, /* 30, +0dB */
+ 0x43c0010f, /* 31, +0.5dB */
+ 0x47c0011f, /* 32, +1.0dB */
+ 0x4c000130, /* 33, +1.5dB */
+ 0x50800142, /* 34, +2.0dB */
+ 0x55400155, /* 35, +2.5dB */
+ 0x5a400169, /* 36, +3.0dB */
+ 0x5fc0017f, /* 37, +3.5dB */
+ 0x65400195, /* 38, +4.0dB */
+ 0x6b8001ae, /* 39, +4.5dB */
+ 0x71c001c7, /* 40, +5.0dB */
+ 0x788001e2, /* 41, +5.5dB */
+ 0x7f8001fe /* 42, +6.0dB */
+};
+
+static const u8 cckswing_table_ch1ch13[CCK_TABLE_SIZE][8] = {
+ {0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01}, /* 0, -16.0dB */
+ {0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01}, /* 1, -15.5dB */
+ {0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 2, -15.0dB */
+ {0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 3, -14.5dB */
+ {0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 4, -14.0dB */
+ {0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 5, -13.5dB */
+ {0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01}, /* 6, -13.0dB */
+ {0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01}, /* 7, -12.5dB */
+ {0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01}, /* 8, -12.0dB */
+ {0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01}, /* 9, -11.5dB */
+ {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 10, -11.0dB */
+ {0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 11, -10.5dB */
+ {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 12, -10.0dB */
+ {0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 13, -9.5dB */
+ {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01}, /* 14, -9.0dB */
+ {0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02}, /* 15, -8.5dB */
+ {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01}, /* 16, -8.0dB */
+ {0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02}, /* 17, -7.5dB */
+ {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02}, /* 18, -7.0dB */
+ {0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02}, /* 19, -6.5dB */
+ {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02}, /* 20, -6.0dB */
+ {0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02}, /* 21, -5.5dB */
+ {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02}, /* 22, -5.0dB */
+ {0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02}, /* 23, -4.5dB */
+ {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02}, /* 24, -4.0dB */
+ {0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03}, /* 25, -3.5dB */
+ {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03}, /* 26, -3.0dB */
+ {0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03}, /* 27, -2.5dB */
+ {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03}, /* 28, -2.0dB */
+ {0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03}, /* 29, -1.5dB */
+ {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03}, /* 30, -1.0dB */
+ {0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04}, /* 31, -0.5dB */
+ {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04} /* 32, +0dB */
+};
+
+static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = {
+ {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00}, /* 0, -16.0dB */
+ {0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 1, -15.5dB */
+ {0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 2, -15.0dB */
+ {0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 3, -14.5dB */
+ {0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 4, -14.0dB */
+ {0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 5, -13.5dB */
+ {0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 6, -13.0dB */
+ {0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 7, -12.5dB */
+ {0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 8, -12.0dB */
+ {0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 9, -11.5dB */
+ {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 10, -11.0dB */
+ {0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 11, -10.5dB */
+ {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 12, -10.0dB */
+ {0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 13, -9.5dB */
+ {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 14, -9.0dB */
+ {0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 15, -8.5dB */
+ {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 16, -8.0dB */
+ {0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 17, -7.5dB */
+ {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00}, /* 18, -7.0dB */
+ {0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00}, /* 19, -6.5dB */
+ {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 20, -6.0dB */
+ {0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 21, -5.5dB */
+ {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00}, /* 22, -5.0dB */
+ {0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00}, /* 23, -4.5dB */
+ {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00}, /* 24, -4.0dB */
+ {0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00}, /* 25, -3.5dB */
+ {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00}, /* 26, -3.0dB */
+ {0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00}, /* 27, -2.5dB */
+ {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00}, /* 28, -2.0dB */
+ {0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00}, /* 29, -1.5dB */
+ {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00}, /* 30, -1.0dB */
+ {0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00}, /* 31, -0.5dB */
+ {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00} /* 32, +0dB */
+};
+
+static const u32 edca_setting_dl[PEER_MAX] = {
+ 0xa44f, /* 0 UNKNOWN */
+ 0x5ea44f, /* 1 REALTEK_90 */
+ 0x5e4322, /* 2 REALTEK_92SE */
+ 0x5ea42b, /* 3 BROAD */
+ 0xa44f, /* 4 RAL */
+ 0xa630, /* 5 ATH */
+ 0x5ea630, /* 6 CISCO */
+ 0x5ea42b, /* 7 MARVELL */
+};
+
+static const u32 edca_setting_ul[PEER_MAX] = {
+ 0x5e4322, /* 0 UNKNOWN */
+ 0xa44f, /* 1 REALTEK_90 */
+ 0x5ea44f, /* 2 REALTEK_92SE */
+ 0x5ea32b, /* 3 BROAD */
+ 0x5ea422, /* 4 RAL */
+ 0x5ea322, /* 5 ATH */
+ 0x3ea430, /* 6 CISCO */
+ 0x5ea44f, /* 7 MARV */
+};
+
+void rtl8723be_dm_txpower_track_adjust(struct ieee80211_hw *hw, u8 type,
+ u8 *pdirection, u32 *poutwrite_val)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+ u8 pwr_val = 0;
+ u8 ofdm_base = rtlpriv->dm.swing_idx_ofdm_base[RF90_PATH_A];
+ u8 ofdm_val = rtlpriv->dm.swing_idx_ofdm[RF90_PATH_A];
+ u8 cck_base = rtldm->swing_idx_cck_base;
+ u8 cck_val = rtldm->swing_idx_cck;
+
+ if (type == 0) {
+ if (ofdm_val <= ofdm_base) {
+ *pdirection = 1;
+ pwr_val = ofdm_base - ofdm_val;
+ } else {
+ *pdirection = 2;
+ pwr_val = ofdm_val - ofdm_base;
+ }
+ } else if (type == 1) {
+ if (cck_val <= cck_base) {
+ *pdirection = 1;
+ pwr_val = cck_base - cck_val;
+ } else {
+ *pdirection = 2;
+ pwr_val = cck_val - cck_base;
+ }
+ }
+
+ if (pwr_val >= TXPWRTRACK_MAX_IDX && (*pdirection == 1))
+ pwr_val = TXPWRTRACK_MAX_IDX;
+
+ *poutwrite_val = pwr_val | (pwr_val << 8) |
+ (pwr_val << 16) | (pwr_val << 24);
+}
+
+static void rtl8723be_dm_diginit(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct dig_t *dm_digtable = &rtlpriv->dm_digtable;
+
+ dm_digtable->dig_enable_flag = true;
+ dm_digtable->cur_igvalue = rtl_get_bbreg(hw,
+ ROFDM0_XAAGCCORE1, 0x7f);
+ dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW;
+ dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH;
+ dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW;
+ dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH;
+ dm_digtable->rx_gain_max = DM_DIG_MAX;
+ dm_digtable->rx_gain_min = DM_DIG_MIN;
+ dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT;
+ dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX;
+ dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN;
+ dm_digtable->pre_cck_cca_thres = 0xff;
+ dm_digtable->cur_cck_cca_thres = 0x83;
+ dm_digtable->forbidden_igi = DM_DIG_MIN;
+ dm_digtable->large_fa_hit = 0;
+ dm_digtable->recover_cnt = 0;
+ dm_digtable->dig_min_0 = DM_DIG_MIN;
+ dm_digtable->dig_min_1 = DM_DIG_MIN;
+ dm_digtable->media_connect_0 = false;
+ dm_digtable->media_connect_1 = false;
+ rtlpriv->dm.dm_initialgain_enable = true;
+ dm_digtable->bt30_cur_igi = 0x32;
+}
+
+void rtl8723be_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rate_adaptive *ra = &(rtlpriv->ra);
+
+ ra->ratr_state = DM_RATR_STA_INIT;
+ ra->pre_ratr_state = DM_RATR_STA_INIT;
+
+ if (rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER)
+ rtlpriv->dm.useramask = true;
+ else
+ rtlpriv->dm.useramask = false;
+
+ ra->high_rssi_thresh_for_ra = 50;
+ ra->low_rssi_thresh_for_ra40m = 20;
+}
+
+static void rtl8723be_dm_init_txpower_tracking(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->dm.txpower_tracking = true;
+ rtlpriv->dm.txpower_track_control = true;
+ rtlpriv->dm.thermalvalue = 0;
+
+ rtlpriv->dm.ofdm_index[0] = 30;
+ rtlpriv->dm.cck_index = 20;
+
+ rtlpriv->dm.swing_idx_cck_base = rtlpriv->dm.cck_index;
+
+ rtlpriv->dm.swing_idx_ofdm_base[0] = rtlpriv->dm.ofdm_index[0];
+ rtlpriv->dm.delta_power_index[RF90_PATH_A] = 0;
+ rtlpriv->dm.delta_power_index_last[RF90_PATH_A] = 0;
+ rtlpriv->dm.power_index_offset[RF90_PATH_A] = 0;
+
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ " rtlpriv->dm.txpower_tracking = %d\n",
+ rtlpriv->dm.txpower_tracking);
+}
+
+static void rtl8723be_dm_init_dynamic_atc_switch(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->dm.crystal_cap = rtlpriv->efuse.crystalcap;
+ rtlpriv->dm.atc_status = rtl_get_bbreg(hw, ROFDM1_CFOTRACKING, 0x800);
+ rtlpriv->dm.cfo_threshold = CFO_THRESHOLD_XTAL;
+}
+
+void rtl8723be_dm_init(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER;
+ rtl8723be_dm_diginit(hw);
+ rtl8723be_dm_init_rate_adaptive_mask(hw);
+ rtl8723_dm_init_edca_turbo(hw);
+ rtl8723_dm_init_dynamic_bb_powersaving(hw);
+ rtl8723_dm_init_dynamic_txpower(hw);
+ rtl8723be_dm_init_txpower_tracking(hw);
+ rtl8723be_dm_init_dynamic_atc_switch(hw);
+}
+
+static void rtl8723be_dm_find_minimum_rssi(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct dig_t *rtl_dm_dig = &(rtlpriv->dm_digtable);
+ struct rtl_mac *mac = rtl_mac(rtlpriv);
+
+ /* Determine the minimum RSSI */
+ if ((mac->link_state < MAC80211_LINKED) &&
+ (rtlpriv->dm.entry_min_undec_sm_pwdb == 0)) {
+ rtl_dm_dig->min_undec_pwdb_for_dm = 0;
+ RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD,
+ "Not connected to any\n");
+ }
+ if (mac->link_state >= MAC80211_LINKED) {
+ if (mac->opmode == NL80211_IFTYPE_AP ||
+ mac->opmode == NL80211_IFTYPE_ADHOC) {
+ rtl_dm_dig->min_undec_pwdb_for_dm =
+ rtlpriv->dm.entry_min_undec_sm_pwdb;
+ RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD,
+ "AP Client PWDB = 0x%lx\n",
+ rtlpriv->dm.entry_min_undec_sm_pwdb);
+ } else {
+ rtl_dm_dig->min_undec_pwdb_for_dm =
+ rtlpriv->dm.undec_sm_pwdb;
+ RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD,
+ "STA Default Port PWDB = 0x%x\n",
+ rtl_dm_dig->min_undec_pwdb_for_dm);
+ }
+ } else {
+ rtl_dm_dig->min_undec_pwdb_for_dm =
+ rtlpriv->dm.entry_min_undec_sm_pwdb;
+ RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD,
+ "AP Ext Port or disconnet PWDB = 0x%x\n",
+ rtl_dm_dig->min_undec_pwdb_for_dm);
+ }
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "MinUndecoratedPWDBForDM =%d\n",
+ rtl_dm_dig->min_undec_pwdb_for_dm);
+}
+
+static void rtl8723be_dm_check_rssi_monitor(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_sta_info *drv_priv;
+ u8 h2c_parameter[3] = { 0 };
+ long tmp_entry_max_pwdb = 0, tmp_entry_min_pwdb = 0xff;
+
+ /* AP & ADHOC & MESH */
+ spin_lock_bh(&rtlpriv->locks.entry_list_lock);
+ list_for_each_entry(drv_priv, &rtlpriv->entry_list, list) {
+ if (drv_priv->rssi_stat.undec_sm_pwdb <
+ tmp_entry_min_pwdb)
+ tmp_entry_min_pwdb =
+ drv_priv->rssi_stat.undec_sm_pwdb;
+ if (drv_priv->rssi_stat.undec_sm_pwdb >
+ tmp_entry_max_pwdb)
+ tmp_entry_max_pwdb =
+ drv_priv->rssi_stat.undec_sm_pwdb;
+ }
+ spin_unlock_bh(&rtlpriv->locks.entry_list_lock);
+
+ /* If associated entry is found */
+ if (tmp_entry_max_pwdb != 0) {
+ rtlpriv->dm.entry_max_undec_sm_pwdb = tmp_entry_max_pwdb;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ "EntryMaxPWDB = 0x%lx(%ld)\n",
+ tmp_entry_max_pwdb, tmp_entry_max_pwdb);
+ } else {
+ rtlpriv->dm.entry_max_undec_sm_pwdb = 0;
+ }
+ /* If associated entry is found */
+ if (tmp_entry_min_pwdb != 0xff) {
+ rtlpriv->dm.entry_min_undec_sm_pwdb = tmp_entry_min_pwdb;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ "EntryMinPWDB = 0x%lx(%ld)\n",
+ tmp_entry_min_pwdb, tmp_entry_min_pwdb);
+ } else {
+ rtlpriv->dm.entry_min_undec_sm_pwdb = 0;
+ }
+ /* Indicate Rx signal strength to FW. */
+ if (rtlpriv->dm.useramask) {
+ h2c_parameter[2] = (u8) (rtlpriv->dm.undec_sm_pwdb & 0xFF);
+ h2c_parameter[1] = 0x20;
+ h2c_parameter[0] = 0;
+ rtl8723be_fill_h2c_cmd(hw, H2C_RSSI_REPORT, 3, h2c_parameter);
+ } else {
+ rtl_write_byte(rtlpriv, 0x4fe, rtlpriv->dm.undec_sm_pwdb);
+ }
+ rtl8723be_dm_find_minimum_rssi(hw);
+ rtlpriv->dm_digtable.rssi_val_min =
+ rtlpriv->dm_digtable.min_undec_pwdb_for_dm;
+}
+
+void rtl8723be_dm_write_dig(struct ieee80211_hw *hw, u8 current_igi)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (rtlpriv->dm_digtable.cur_igvalue != current_igi) {
+ rtl_set_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f, current_igi);
+ if (rtlpriv->phy.rf_type != RF_1T1R)
+ rtl_set_bbreg(hw, ROFDM0_XBAGCCORE1, 0x7f, current_igi);
+ }
+ rtlpriv->dm_digtable.pre_igvalue = rtlpriv->dm_digtable.cur_igvalue;
+ rtlpriv->dm_digtable.cur_igvalue = current_igi;
+}
+
+static void rtl8723be_dm_dig(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct dig_t *dm_digtable = &(rtlpriv->dm_digtable);
+ u8 dig_dynamic_min, dig_maxofmin;
+ bool firstconnect, firstdisconnect;
+ u8 dm_dig_max, dm_dig_min;
+ u8 current_igi = dm_digtable->cur_igvalue;
+ u8 offset;
+
+ /* AP, BT */
+ if (mac->act_scanning)
+ return;
+
+ dig_dynamic_min = dm_digtable->dig_min_0;
+ firstconnect = (mac->link_state >= MAC80211_LINKED) &&
+ !dm_digtable->media_connect_0;
+ firstdisconnect = (mac->link_state < MAC80211_LINKED) &&
+ dm_digtable->media_connect_0;
+
+ dm_dig_max = 0x5a;
+ dm_dig_min = DM_DIG_MIN;
+ dig_maxofmin = DM_DIG_MAX_AP;
+
+ if (mac->link_state >= MAC80211_LINKED) {
+ if ((dm_digtable->rssi_val_min + 10) > dm_dig_max)
+ dm_digtable->rx_gain_max = dm_dig_max;
+ else if ((dm_digtable->rssi_val_min + 10) < dm_dig_min)
+ dm_digtable->rx_gain_max = dm_dig_min;
+ else
+ dm_digtable->rx_gain_max =
+ dm_digtable->rssi_val_min + 10;
+
+ if (rtlpriv->dm.one_entry_only) {
+ offset = 12;
+ if (dm_digtable->rssi_val_min - offset < dm_dig_min)
+ dig_dynamic_min = dm_dig_min;
+ else if (dm_digtable->rssi_val_min - offset >
+ dig_maxofmin)
+ dig_dynamic_min = dig_maxofmin;
+ else
+ dig_dynamic_min =
+ dm_digtable->rssi_val_min - offset;
+ } else {
+ dig_dynamic_min = dm_dig_min;
+ }
+ } else {
+ dm_digtable->rx_gain_max = dm_dig_max;
+ dig_dynamic_min = dm_dig_min;
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "no link\n");
+ }
+
+ if (rtlpriv->falsealm_cnt.cnt_all > 10000) {
+ if (dm_digtable->large_fa_hit != 3)
+ dm_digtable->large_fa_hit++;
+ if (dm_digtable->forbidden_igi < current_igi) {
+ dm_digtable->forbidden_igi = current_igi;
+ dm_digtable->large_fa_hit = 1;
+ }
+
+ if (dm_digtable->large_fa_hit >= 3) {
+ if ((dm_digtable->forbidden_igi + 1) >
+ dm_digtable->rx_gain_max)
+ dm_digtable->rx_gain_min =
+ dm_digtable->rx_gain_max;
+ else
+ dm_digtable->rx_gain_min =
+ dm_digtable->forbidden_igi + 1;
+ dm_digtable->recover_cnt = 3600;
+ }
+ } else {
+ if (dm_digtable->recover_cnt != 0) {
+ dm_digtable->recover_cnt--;
+ } else {
+ if (dm_digtable->large_fa_hit < 3) {
+ if ((dm_digtable->forbidden_igi - 1) <
+ dig_dynamic_min) {
+ dm_digtable->forbidden_igi =
+ dig_dynamic_min;
+ dm_digtable->rx_gain_min =
+ dig_dynamic_min;
+ } else {
+ dm_digtable->forbidden_igi--;
+ dm_digtable->rx_gain_min =
+ dm_digtable->forbidden_igi + 1;
+ }
+ } else {
+ dm_digtable->large_fa_hit = 0;
+ }
+ }
+ }
+ if (dm_digtable->rx_gain_min > dm_digtable->rx_gain_max)
+ dm_digtable->rx_gain_min = dm_digtable->rx_gain_max;
+
+ if (mac->link_state >= MAC80211_LINKED) {
+ if (firstconnect) {
+ if (dm_digtable->rssi_val_min <= dig_maxofmin)
+ current_igi = dm_digtable->rssi_val_min;
+ else
+ current_igi = dig_maxofmin;
+
+ dm_digtable->large_fa_hit = 0;
+ } else {
+ if (rtlpriv->falsealm_cnt.cnt_all > DM_DIG_FA_TH2)
+ current_igi += 4;
+ else if (rtlpriv->falsealm_cnt.cnt_all > DM_DIG_FA_TH1)
+ current_igi += 2;
+ else if (rtlpriv->falsealm_cnt.cnt_all < DM_DIG_FA_TH0)
+ current_igi -= 2;
+ }
+ } else {
+ if (firstdisconnect) {
+ current_igi = dm_digtable->rx_gain_min;
+ } else {
+ if (rtlpriv->falsealm_cnt.cnt_all > 10000)
+ current_igi += 4;
+ else if (rtlpriv->falsealm_cnt.cnt_all > 8000)
+ current_igi += 2;
+ else if (rtlpriv->falsealm_cnt.cnt_all < 500)
+ current_igi -= 2;
+ }
+ }
+
+ if (current_igi > dm_digtable->rx_gain_max)
+ current_igi = dm_digtable->rx_gain_max;
+ else if (current_igi < dm_digtable->rx_gain_min)
+ current_igi = dm_digtable->rx_gain_min;
+
+ rtl8723be_dm_write_dig(hw, current_igi);
+ dm_digtable->media_connect_0 =
+ ((mac->link_state >= MAC80211_LINKED) ? true : false);
+ dm_digtable->dig_min_0 = dig_dynamic_min;
+}
+
+static void rtl8723be_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw)
+{
+ u32 ret_value;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct false_alarm_statistics *falsealm_cnt = &(rtlpriv->falsealm_cnt);
+
+ rtl_set_bbreg(hw, DM_REG_OFDM_FA_HOLDC_11N, BIT(31), 1);
+ rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTD_11N, BIT(31), 1);
+
+ ret_value = rtl_get_bbreg(hw, DM_REG_OFDM_FA_TYPE1_11N, MASKDWORD);
+ falsealm_cnt->cnt_fast_fsync_fail = ret_value & 0xffff;
+ falsealm_cnt->cnt_sb_search_fail = (ret_value & 0xffff0000) >> 16;
+
+ ret_value = rtl_get_bbreg(hw, DM_REG_OFDM_FA_TYPE2_11N, MASKDWORD);
+ falsealm_cnt->cnt_ofdm_cca = ret_value & 0xffff;
+ falsealm_cnt->cnt_parity_fail = (ret_value & 0xffff0000) >> 16;
+
+ ret_value = rtl_get_bbreg(hw, DM_REG_OFDM_FA_TYPE3_11N, MASKDWORD);
+ falsealm_cnt->cnt_rate_illegal = ret_value & 0xffff;
+ falsealm_cnt->cnt_crc8_fail = (ret_value & 0xffff0000) >> 16;
+
+ ret_value = rtl_get_bbreg(hw, DM_REG_OFDM_FA_TYPE4_11N, MASKDWORD);
+ falsealm_cnt->cnt_mcs_fail = ret_value & 0xffff;
+
+ falsealm_cnt->cnt_ofdm_fail = falsealm_cnt->cnt_parity_fail +
+ falsealm_cnt->cnt_rate_illegal +
+ falsealm_cnt->cnt_crc8_fail +
+ falsealm_cnt->cnt_mcs_fail +
+ falsealm_cnt->cnt_fast_fsync_fail +
+ falsealm_cnt->cnt_sb_search_fail;
+
+ rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(12), 1);
+ rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(14), 1);
+
+ ret_value = rtl_get_bbreg(hw, DM_REG_CCK_FA_RST_11N, MASKBYTE0);
+ falsealm_cnt->cnt_cck_fail = ret_value;
+
+ ret_value = rtl_get_bbreg(hw, DM_REG_CCK_FA_MSB_11N, MASKBYTE3);
+ falsealm_cnt->cnt_cck_fail += (ret_value & 0xff) << 8;
+
+ ret_value = rtl_get_bbreg(hw, DM_REG_CCK_CCA_CNT_11N, MASKDWORD);
+ falsealm_cnt->cnt_cck_cca = ((ret_value & 0xff) << 8) |
+ ((ret_value & 0xff00) >> 8);
+
+ falsealm_cnt->cnt_all = falsealm_cnt->cnt_fast_fsync_fail +
+ falsealm_cnt->cnt_sb_search_fail +
+ falsealm_cnt->cnt_parity_fail +
+ falsealm_cnt->cnt_rate_illegal +
+ falsealm_cnt->cnt_crc8_fail +
+ falsealm_cnt->cnt_mcs_fail +
+ falsealm_cnt->cnt_cck_fail;
+
+ falsealm_cnt->cnt_cca_all = falsealm_cnt->cnt_ofdm_cca +
+ falsealm_cnt->cnt_cck_cca;
+
+ rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTC_11N, BIT(31), 1);
+ rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTC_11N, BIT(31), 0);
+ rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTD_11N, BIT(27), 1);
+ rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTD_11N, BIT(27), 0);
+
+ rtl_set_bbreg(hw, DM_REG_OFDM_FA_HOLDC_11N, BIT(31), 0);
+ rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTD_11N, BIT(31), 0);
+
+ rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(13) | BIT(12), 0);
+ rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(13) | BIT(12), 2);
+
+ rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(15) | BIT(14), 0);
+ rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(15) | BIT(14), 2);
+
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE,
+ "cnt_parity_fail = %d, cnt_rate_illegal = %d, "
+ "cnt_crc8_fail = %d, cnt_mcs_fail = %d\n",
+ falsealm_cnt->cnt_parity_fail,
+ falsealm_cnt->cnt_rate_illegal,
+ falsealm_cnt->cnt_crc8_fail,
+ falsealm_cnt->cnt_mcs_fail);
+
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE,
+ "cnt_ofdm_fail = %x, cnt_cck_fail = %x,"
+ " cnt_all = %x\n",
+ falsealm_cnt->cnt_ofdm_fail,
+ falsealm_cnt->cnt_cck_fail,
+ falsealm_cnt->cnt_all);
+}
+
+static void rtl8723be_dm_dynamic_txpower(struct ieee80211_hw *hw)
+{
+ /* 8723BE does not support ODM_BB_DYNAMIC_TXPWR*/
+ return;
+}
+
+static void rtl8723be_set_iqk_matrix(struct ieee80211_hw *hw, u8 ofdm_index,
+ u8 rfpath, long iqk_result_x,
+ long iqk_result_y)
+{
+ long ele_a = 0, ele_d, ele_c = 0, value32;
+
+ if (ofdm_index >= 43)
+ ofdm_index = 43 - 1;
+
+ ele_d = (ofdmswing_table[ofdm_index] & 0xFFC00000) >> 22;
+
+ if (iqk_result_x != 0) {
+ if ((iqk_result_x & 0x00000200) != 0)
+ iqk_result_x = iqk_result_x | 0xFFFFFC00;
+ ele_a = ((iqk_result_x * ele_d) >> 8) & 0x000003FF;
+
+ if ((iqk_result_y & 0x00000200) != 0)
+ iqk_result_y = iqk_result_y | 0xFFFFFC00;
+ ele_c = ((iqk_result_y * ele_d) >> 8) & 0x000003FF;
+
+ switch (rfpath) {
+ case RF90_PATH_A:
+ value32 = (ele_d << 22) |
+ ((ele_c & 0x3F) << 16) | ele_a;
+ rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, MASKDWORD,
+ value32);
+ value32 = (ele_c & 0x000003C0) >> 6;
+ rtl_set_bbreg(hw, ROFDM0_XCTXAFE, MASKH4BITS, value32);
+ value32 = ((iqk_result_x * ele_d) >> 7) & 0x01;
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(24),
+ value32);
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (rfpath) {
+ case RF90_PATH_A:
+ rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, MASKDWORD,
+ ofdmswing_table[ofdm_index]);
+ rtl_set_bbreg(hw, ROFDM0_XCTXAFE, MASKH4BITS, 0x00);
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(24), 0x00);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void rtl8723be_dm_tx_power_track_set_power(struct ieee80211_hw *hw,
+ enum pwr_track_control_method method,
+ u8 rfpath, u8 idx)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+ u8 swing_idx_ofdm_limit = 36;
+
+ if (method == TXAGC) {
+ rtl8723be_phy_set_txpower_level(hw, rtlphy->current_channel);
+ } else if (method == BBSWING) {
+ if (rtldm->swing_idx_cck >= CCK_TABLE_SIZE)
+ rtldm->swing_idx_cck = CCK_TABLE_SIZE - 1;
+
+ if (!rtldm->cck_inch14) {
+ rtl_write_byte(rtlpriv, 0xa22,
+ cckswing_table_ch1ch13[rtldm->swing_idx_cck][0]);
+ rtl_write_byte(rtlpriv, 0xa23,
+ cckswing_table_ch1ch13[rtldm->swing_idx_cck][1]);
+ rtl_write_byte(rtlpriv, 0xa24,
+ cckswing_table_ch1ch13[rtldm->swing_idx_cck][2]);
+ rtl_write_byte(rtlpriv, 0xa25,
+ cckswing_table_ch1ch13[rtldm->swing_idx_cck][3]);
+ rtl_write_byte(rtlpriv, 0xa26,
+ cckswing_table_ch1ch13[rtldm->swing_idx_cck][4]);
+ rtl_write_byte(rtlpriv, 0xa27,
+ cckswing_table_ch1ch13[rtldm->swing_idx_cck][5]);
+ rtl_write_byte(rtlpriv, 0xa28,
+ cckswing_table_ch1ch13[rtldm->swing_idx_cck][6]);
+ rtl_write_byte(rtlpriv, 0xa29,
+ cckswing_table_ch1ch13[rtldm->swing_idx_cck][7]);
+ } else {
+ rtl_write_byte(rtlpriv, 0xa22,
+ cckswing_table_ch14[rtldm->swing_idx_cck][0]);
+ rtl_write_byte(rtlpriv, 0xa23,
+ cckswing_table_ch14[rtldm->swing_idx_cck][1]);
+ rtl_write_byte(rtlpriv, 0xa24,
+ cckswing_table_ch14[rtldm->swing_idx_cck][2]);
+ rtl_write_byte(rtlpriv, 0xa25,
+ cckswing_table_ch14[rtldm->swing_idx_cck][3]);
+ rtl_write_byte(rtlpriv, 0xa26,
+ cckswing_table_ch14[rtldm->swing_idx_cck][4]);
+ rtl_write_byte(rtlpriv, 0xa27,
+ cckswing_table_ch14[rtldm->swing_idx_cck][5]);
+ rtl_write_byte(rtlpriv, 0xa28,
+ cckswing_table_ch14[rtldm->swing_idx_cck][6]);
+ rtl_write_byte(rtlpriv, 0xa29,
+ cckswing_table_ch14[rtldm->swing_idx_cck][7]);
+ }
+
+ if (rfpath == RF90_PATH_A) {
+ if (rtldm->swing_idx_ofdm[RF90_PATH_A] <
+ swing_idx_ofdm_limit)
+ swing_idx_ofdm_limit =
+ rtldm->swing_idx_ofdm[RF90_PATH_A];
+
+ rtl8723be_set_iqk_matrix(hw,
+ rtldm->swing_idx_ofdm[rfpath], rfpath,
+ rtlphy->iqk_matrix[idx].value[0][0],
+ rtlphy->iqk_matrix[idx].value[0][1]);
+ } else if (rfpath == RF90_PATH_B) {
+ if (rtldm->swing_idx_ofdm[RF90_PATH_B] <
+ swing_idx_ofdm_limit)
+ swing_idx_ofdm_limit =
+ rtldm->swing_idx_ofdm[RF90_PATH_B];
+
+ rtl8723be_set_iqk_matrix(hw,
+ rtldm->swing_idx_ofdm[rfpath], rfpath,
+ rtlphy->iqk_matrix[idx].value[0][4],
+ rtlphy->iqk_matrix[idx].value[0][5]);
+ }
+ } else {
+ return;
+ }
+}
+
+static void txpwr_track_cb_therm(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+ u8 thermalvalue = 0, delta, delta_lck, delta_iqk;
+ u8 thermalvalue_avg_count = 0;
+ u32 thermalvalue_avg = 0;
+ int i = 0;
+
+ u8 ofdm_min_index = 6;
+ u8 index = 0;
+
+ char delta_swing_table_idx_tup_a[] = {
+ 0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5,
+ 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 10,
+ 10, 11, 11, 12, 12, 13, 14, 15};
+ char delta_swing_table_idx_tdown_a[] = {
+ 0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5,
+ 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 9,
+ 9, 10, 10, 11, 12, 13, 14, 15};
+
+ /*Initilization ( 7 steps in total)*/
+ rtlpriv->dm.txpower_trackinginit = true;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ "rtl8723be_dm_txpower_tracking"
+ "_callback_thermalmeter\n");
+
+ thermalvalue = (u8)rtl_get_rfreg(hw, RF90_PATH_A, RF_T_METER, 0xfc00);
+ if (!rtlpriv->dm.txpower_track_control || thermalvalue == 0 ||
+ rtlefuse->eeprom_thermalmeter == 0xFF)
+ return;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ "Readback Thermal Meter = 0x%x pre thermal meter 0x%x "
+ "eeprom_thermalmeter 0x%x\n",
+ thermalvalue, rtldm->thermalvalue,
+ rtlefuse->eeprom_thermalmeter);
+ /*3 Initialize ThermalValues of RFCalibrateInfo*/
+ if (!rtldm->thermalvalue) {
+ rtlpriv->dm.thermalvalue_lck = thermalvalue;
+ rtlpriv->dm.thermalvalue_iqk = thermalvalue;
+ }
+
+ /*4 Calculate average thermal meter*/
+ rtldm->thermalvalue_avg[rtldm->thermalvalue_avg_index] = thermalvalue;
+ rtldm->thermalvalue_avg_index++;
+ if (rtldm->thermalvalue_avg_index == AVG_THERMAL_NUM_8723BE)
+ rtldm->thermalvalue_avg_index = 0;
+
+ for (i = 0; i < AVG_THERMAL_NUM_8723BE; i++) {
+ if (rtldm->thermalvalue_avg[i]) {
+ thermalvalue_avg += rtldm->thermalvalue_avg[i];
+ thermalvalue_avg_count++;
+ }
+ }
+
+ if (thermalvalue_avg_count)
+ thermalvalue = (u8)(thermalvalue_avg / thermalvalue_avg_count);
+
+ /* 5 Calculate delta, delta_LCK, delta_IQK.*/
+ delta = (thermalvalue > rtlpriv->dm.thermalvalue) ?
+ (thermalvalue - rtlpriv->dm.thermalvalue) :
+ (rtlpriv->dm.thermalvalue - thermalvalue);
+ delta_lck = (thermalvalue > rtlpriv->dm.thermalvalue_lck) ?
+ (thermalvalue - rtlpriv->dm.thermalvalue_lck) :
+ (rtlpriv->dm.thermalvalue_lck - thermalvalue);
+ delta_iqk = (thermalvalue > rtlpriv->dm.thermalvalue_iqk) ?
+ (thermalvalue - rtlpriv->dm.thermalvalue_iqk) :
+ (rtlpriv->dm.thermalvalue_iqk - thermalvalue);
+
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ "Readback Thermal Meter = 0x%x pre thermal meter 0x%x "
+ "eeprom_thermalmeter 0x%x delta 0x%x "
+ "delta_lck 0x%x delta_iqk 0x%x\n",
+ thermalvalue, rtlpriv->dm.thermalvalue,
+ rtlefuse->eeprom_thermalmeter, delta, delta_lck, delta_iqk);
+ /* 6 If necessary, do LCK.*/
+ if (delta_lck >= IQK_THRESHOLD) {
+ rtlpriv->dm.thermalvalue_lck = thermalvalue;
+ rtl8723be_phy_lc_calibrate(hw);
+ }
+
+ /* 7 If necessary, move the index of
+ * swing table to adjust Tx power.
+ */
+ if (delta > 0 && rtlpriv->dm.txpower_track_control) {
+ delta = (thermalvalue > rtlefuse->eeprom_thermalmeter) ?
+ (thermalvalue - rtlefuse->eeprom_thermalmeter) :
+ (rtlefuse->eeprom_thermalmeter - thermalvalue);
+
+ if (delta >= TXSCALE_TABLE_SIZE)
+ delta = TXSCALE_TABLE_SIZE - 1;
+ /* 7.1 Get the final CCK_index and
+ * OFDM_index for each swing table.
+ */
+ if (thermalvalue > rtlefuse->eeprom_thermalmeter) {
+ rtldm->delta_power_index_last[RF90_PATH_A] =
+ rtldm->delta_power_index[RF90_PATH_A];
+ rtldm->delta_power_index[RF90_PATH_A] =
+ delta_swing_table_idx_tup_a[delta];
+ } else {
+ rtldm->delta_power_index_last[RF90_PATH_A] =
+ rtldm->delta_power_index[RF90_PATH_A];
+ rtldm->delta_power_index[RF90_PATH_A] =
+ -1 * delta_swing_table_idx_tdown_a[delta];
+ }
+
+ /* 7.2 Handle boundary conditions of index.*/
+ if (rtldm->delta_power_index[RF90_PATH_A] ==
+ rtldm->delta_power_index_last[RF90_PATH_A])
+ rtldm->power_index_offset[RF90_PATH_A] = 0;
+ else
+ rtldm->power_index_offset[RF90_PATH_A] =
+ rtldm->delta_power_index[RF90_PATH_A] -
+ rtldm->delta_power_index_last[RF90_PATH_A];
+
+ rtldm->ofdm_index[0] =
+ rtldm->swing_idx_ofdm_base[RF90_PATH_A] +
+ rtldm->power_index_offset[RF90_PATH_A];
+ rtldm->cck_index = rtldm->swing_idx_cck_base +
+ rtldm->power_index_offset[RF90_PATH_A];
+
+ rtldm->swing_idx_cck = rtldm->cck_index;
+ rtldm->swing_idx_ofdm[0] = rtldm->ofdm_index[0];
+
+ if (rtldm->ofdm_index[0] > OFDM_TABLE_SIZE - 1)
+ rtldm->ofdm_index[0] = OFDM_TABLE_SIZE - 1;
+ else if (rtldm->ofdm_index[0] < ofdm_min_index)
+ rtldm->ofdm_index[0] = ofdm_min_index;
+
+ if (rtldm->cck_index > CCK_TABLE_SIZE - 1)
+ rtldm->cck_index = CCK_TABLE_SIZE - 1;
+ else if (rtldm->cck_index < 0)
+ rtldm->cck_index = 0;
+ } else {
+ rtldm->power_index_offset[RF90_PATH_A] = 0;
+ }
+
+ if ((rtldm->power_index_offset[RF90_PATH_A] != 0) &&
+ (rtldm->txpower_track_control)) {
+ rtldm->done_txpower = true;
+ if (thermalvalue > rtlefuse->eeprom_thermalmeter)
+ rtl8723be_dm_tx_power_track_set_power(hw, BBSWING, 0,
+ index);
+ else
+ rtl8723be_dm_tx_power_track_set_power(hw, BBSWING, 0,
+ index);
+
+ rtldm->swing_idx_cck_base = rtldm->swing_idx_cck;
+ rtldm->swing_idx_ofdm_base[RF90_PATH_A] =
+ rtldm->swing_idx_ofdm[0];
+ rtldm->thermalvalue = thermalvalue;
+ }
+
+ if (delta_iqk >= IQK_THRESHOLD) {
+ rtldm->thermalvalue_iqk = thermalvalue;
+ rtl8723be_phy_iq_calibrate(hw, false);
+ }
+
+ rtldm->txpowercount = 0;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "end\n");
+}
+
+void rtl8723be_dm_check_txpower_tracking(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ static u8 tm_trigger;
+
+ if (!rtlpriv->dm.txpower_tracking)
+ return;
+
+ if (!tm_trigger) {
+ rtl_set_rfreg(hw, RF90_PATH_A, RF_T_METER, BIT(17) | BIT(16),
+ 0x03);
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ "Trigger 8723be Thermal Meter!!\n");
+ tm_trigger = 1;
+ return;
+ } else {
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ "Schedule TxPowerTracking !!\n");
+ txpwr_track_cb_therm(hw);
+ tm_trigger = 0;
+ }
+}
+
+static void rtl8723be_dm_refresh_rate_adaptive_mask(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rate_adaptive *ra = &(rtlpriv->ra);
+ struct ieee80211_sta *sta = NULL;
+ u32 low_rssithresh_for_ra = ra->low2high_rssi_thresh_for_ra40m;
+ u32 high_rssithresh_for_ra = ra->high_rssi_thresh_for_ra;
+ u8 go_up_gap = 5;
+
+ if (is_hal_stop(rtlhal)) {
+ RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+ "driver is going to unload\n");
+ return;
+ }
+
+ if (!rtlpriv->dm.useramask) {
+ RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+ "driver does not control rate adaptive mask\n");
+ return;
+ }
+
+ if (mac->link_state == MAC80211_LINKED &&
+ mac->opmode == NL80211_IFTYPE_STATION) {
+ switch (ra->pre_ratr_state) {
+ case DM_RATR_STA_MIDDLE:
+ high_rssithresh_for_ra += go_up_gap;
+ break;
+ case DM_RATR_STA_LOW:
+ high_rssithresh_for_ra += go_up_gap;
+ low_rssithresh_for_ra += go_up_gap;
+ break;
+ default:
+ break;
+ }
+
+ if (rtlpriv->dm.undec_sm_pwdb >
+ (long)high_rssithresh_for_ra)
+ ra->ratr_state = DM_RATR_STA_HIGH;
+ else if (rtlpriv->dm.undec_sm_pwdb >
+ (long)low_rssithresh_for_ra)
+ ra->ratr_state = DM_RATR_STA_MIDDLE;
+ else
+ ra->ratr_state = DM_RATR_STA_LOW;
+
+ if (ra->pre_ratr_state != ra->ratr_state) {
+ RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+ "RSSI = %ld\n",
+ rtlpriv->dm.undec_sm_pwdb);
+ RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+ "RSSI_LEVEL = %d\n", ra->ratr_state);
+ RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+ "PreState = %d, CurState = %d\n",
+ ra->pre_ratr_state, ra->ratr_state);
+
+ rcu_read_lock();
+ sta = rtl_find_sta(hw, mac->bssid);
+ if (sta)
+ rtlpriv->cfg->ops->update_rate_tbl(hw, sta,
+ ra->ratr_state);
+ rcu_read_unlock();
+
+ ra->pre_ratr_state = ra->ratr_state;
+ }
+ }
+}
+
+static bool rtl8723be_dm_is_edca_turbo_disable(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (rtlpriv->cfg->ops->get_btc_status()) {
+ if (rtlpriv->btcoexist.btc_ops->btc_is_disable_edca_turbo(rtlpriv))
+ return true;
+ }
+ if (rtlpriv->mac80211.mode == WIRELESS_MODE_B)
+ return true;
+
+ return false;
+}
+
+static void rtl8723be_dm_check_edca_turbo(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ static u64 last_txok_cnt;
+ static u64 last_rxok_cnt;
+ u64 cur_txok_cnt = 0;
+ u64 cur_rxok_cnt = 0;
+ u32 edca_be_ul = 0x6ea42b;
+ u32 edca_be_dl = 0x6ea42b;/*not sure*/
+ u32 edca_be = 0x5ea42b;
+ u32 iot_peer = 0;
+ bool is_cur_rdlstate;
+ bool last_is_cur_rdlstate = false;
+ bool bias_on_rx = false;
+ bool edca_turbo_on = false;
+
+ last_is_cur_rdlstate = rtlpriv->dm.is_cur_rdlstate;
+
+ cur_txok_cnt = rtlpriv->stats.txbytesunicast - last_txok_cnt;
+ cur_rxok_cnt = rtlpriv->stats.rxbytesunicast - last_rxok_cnt;
+
+ iot_peer = rtlpriv->mac80211.vendor;
+ bias_on_rx = (iot_peer == PEER_RAL || iot_peer == PEER_ATH) ?
+ true : false;
+ edca_turbo_on = ((!rtlpriv->dm.is_any_nonbepkts) &&
+ (!rtlpriv->dm.disable_framebursting)) ?
+ true : false;
+
+ if ((iot_peer == PEER_CISCO) &&
+ (mac->mode == WIRELESS_MODE_N_24G)) {
+ edca_be_dl = edca_setting_dl[iot_peer];
+ edca_be_ul = edca_setting_ul[iot_peer];
+ }
+ if (rtl8723be_dm_is_edca_turbo_disable(hw))
+ goto exit;
+
+ if (edca_turbo_on) {
+ if (bias_on_rx)
+ is_cur_rdlstate = (cur_txok_cnt > cur_rxok_cnt * 4) ?
+ false : true;
+ else
+ is_cur_rdlstate = (cur_rxok_cnt > cur_txok_cnt * 4) ?
+ true : false;
+
+ edca_be = (is_cur_rdlstate) ? edca_be_dl : edca_be_ul;
+ rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM, edca_be);
+ rtlpriv->dm.is_cur_rdlstate = is_cur_rdlstate;
+ rtlpriv->dm.current_turbo_edca = true;
+ } else {
+ if (rtlpriv->dm.current_turbo_edca) {
+ u8 tmp = AC0_BE;
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM,
+ &tmp);
+ }
+ rtlpriv->dm.current_turbo_edca = false;
+ }
+
+exit:
+ rtlpriv->dm.is_any_nonbepkts = false;
+ last_txok_cnt = rtlpriv->stats.txbytesunicast;
+ last_rxok_cnt = rtlpriv->stats.rxbytesunicast;
+}
+
+static void rtl8723be_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 cur_cck_cca_thresh;
+
+ if (rtlpriv->mac80211.link_state >= MAC80211_LINKED) {
+ if (rtlpriv->dm_digtable.rssi_val_min > 25) {
+ cur_cck_cca_thresh = 0xcd;
+ } else if ((rtlpriv->dm_digtable.rssi_val_min <= 25) &&
+ (rtlpriv->dm_digtable.rssi_val_min > 10)) {
+ cur_cck_cca_thresh = 0x83;
+ } else {
+ if (rtlpriv->falsealm_cnt.cnt_cck_fail > 1000)
+ cur_cck_cca_thresh = 0x83;
+ else
+ cur_cck_cca_thresh = 0x40;
+ }
+ } else {
+ if (rtlpriv->falsealm_cnt.cnt_cck_fail > 1000)
+ cur_cck_cca_thresh = 0x83;
+ else
+ cur_cck_cca_thresh = 0x40;
+ }
+
+ if (rtlpriv->dm_digtable.cur_cck_cca_thres != cur_cck_cca_thresh)
+ rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, cur_cck_cca_thresh);
+
+ rtlpriv->dm_digtable.pre_cck_cca_thres = rtlpriv->dm_digtable.cur_cck_cca_thres;
+ rtlpriv->dm_digtable.cur_cck_cca_thres = cur_cck_cca_thresh;
+ RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE,
+ "CCK cca thresh hold =%x\n",
+ rtlpriv->dm_digtable.cur_cck_cca_thres);
+}
+
+static void rtl8723be_dm_dynamic_edcca(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 reg_c50, reg_c58;
+ bool fw_current_in_ps_mode = false;
+
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
+ (u8 *)(&fw_current_in_ps_mode));
+ if (fw_current_in_ps_mode)
+ return;
+
+ reg_c50 = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0);
+ reg_c58 = rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0);
+
+ if (reg_c50 > 0x28 && reg_c58 > 0x28) {
+ if (!rtlpriv->rtlhal.pre_edcca_enable) {
+ rtl_write_byte(rtlpriv, ROFDM0_ECCATHRESHOLD, 0x03);
+ rtl_write_byte(rtlpriv, ROFDM0_ECCATHRESHOLD + 2, 0x00);
+ }
+ } else if (reg_c50 < 0x25 && reg_c58 < 0x25) {
+ if (rtlpriv->rtlhal.pre_edcca_enable) {
+ rtl_write_byte(rtlpriv, ROFDM0_ECCATHRESHOLD, 0x7f);
+ rtl_write_byte(rtlpriv, ROFDM0_ECCATHRESHOLD + 2, 0x7f);
+ }
+ }
+}
+
+static void rtl8723be_dm_dynamic_atc_switch(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+ u8 crystal_cap;
+ u32 packet_count;
+ int cfo_khz_a, cfo_khz_b, cfo_ave = 0, adjust_xtal = 0;
+ int cfo_ave_diff;
+
+ if (rtlpriv->mac80211.link_state < MAC80211_LINKED) {
+ if (rtldm->atc_status == ATC_STATUS_OFF) {
+ rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(11),
+ ATC_STATUS_ON);
+ rtldm->atc_status = ATC_STATUS_ON;
+ }
+ if (rtlpriv->cfg->ops->get_btc_status()) {
+ if (!rtlpriv->btcoexist.btc_ops->btc_is_bt_disabled(rtlpriv)) {
+ RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD,
+ "odm_DynamicATCSwitch(): Disable"
+ " CFO tracking for BT!!\n");
+ return;
+ }
+ }
+
+ if (rtldm->crystal_cap != rtlpriv->efuse.crystalcap) {
+ rtldm->crystal_cap = rtlpriv->efuse.crystalcap;
+ crystal_cap = rtldm->crystal_cap & 0x3f;
+ rtl_set_bbreg(hw, REG_MAC_PHY_CTRL, 0xFFF000,
+ (crystal_cap | (crystal_cap << 6)));
+ }
+ } else {
+ cfo_khz_a = (int)(rtldm->cfo_tail[0] * 3125) / 1280;
+ cfo_khz_b = (int)(rtldm->cfo_tail[1] * 3125) / 1280;
+ packet_count = rtldm->packet_count;
+
+ if (packet_count == rtldm->packet_count_pre)
+ return;
+
+ rtldm->packet_count_pre = packet_count;
+
+ if (rtlpriv->phy.rf_type == RF_1T1R)
+ cfo_ave = cfo_khz_a;
+ else
+ cfo_ave = (int)(cfo_khz_a + cfo_khz_b) >> 1;
+
+ cfo_ave_diff = (rtldm->cfo_ave_pre >= cfo_ave) ?
+ (rtldm->cfo_ave_pre - cfo_ave) :
+ (cfo_ave - rtldm->cfo_ave_pre);
+
+ if (cfo_ave_diff > 20 && rtldm->large_cfo_hit == 0) {
+ rtldm->large_cfo_hit = 1;
+ return;
+ } else {
+ rtldm->large_cfo_hit = 0;
+ }
+
+ rtldm->cfo_ave_pre = cfo_ave;
+
+ if (cfo_ave >= -rtldm->cfo_threshold &&
+ cfo_ave <= rtldm->cfo_threshold && rtldm->is_freeze == 0) {
+ if (rtldm->cfo_threshold == CFO_THRESHOLD_XTAL) {
+ rtldm->cfo_threshold = CFO_THRESHOLD_XTAL + 10;
+ rtldm->is_freeze = 1;
+ } else {
+ rtldm->cfo_threshold = CFO_THRESHOLD_XTAL;
+ }
+ }
+
+ if (cfo_ave > rtldm->cfo_threshold && rtldm->crystal_cap < 0x3f)
+ adjust_xtal = ((cfo_ave - CFO_THRESHOLD_XTAL) >> 1) + 1;
+ else if ((cfo_ave < -rtlpriv->dm.cfo_threshold) &&
+ rtlpriv->dm.crystal_cap > 0)
+ adjust_xtal = ((cfo_ave + CFO_THRESHOLD_XTAL) >> 1) - 1;
+
+ if (adjust_xtal != 0) {
+ rtldm->is_freeze = 0;
+ rtldm->crystal_cap += adjust_xtal;
+
+ if (rtldm->crystal_cap > 0x3f)
+ rtldm->crystal_cap = 0x3f;
+ else if (rtldm->crystal_cap < 0)
+ rtldm->crystal_cap = 0;
+
+ crystal_cap = rtldm->crystal_cap & 0x3f;
+ rtl_set_bbreg(hw, REG_MAC_PHY_CTRL, 0xFFF000,
+ (crystal_cap | (crystal_cap << 6)));
+ }
+
+ if (cfo_ave < CFO_THRESHOLD_ATC &&
+ cfo_ave > -CFO_THRESHOLD_ATC) {
+ if (rtldm->atc_status == ATC_STATUS_ON) {
+ rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(11),
+ ATC_STATUS_OFF);
+ rtldm->atc_status = ATC_STATUS_OFF;
+ }
+ } else {
+ if (rtldm->atc_status == ATC_STATUS_OFF) {
+ rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(11),
+ ATC_STATUS_ON);
+ rtldm->atc_status = ATC_STATUS_ON;
+ }
+ }
+ }
+}
+
+static void rtl8723be_dm_common_info_self_update(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_sta_info *drv_priv;
+ u8 cnt = 0;
+
+ rtlpriv->dm.one_entry_only = false;
+
+ if (rtlpriv->mac80211.opmode == NL80211_IFTYPE_STATION &&
+ rtlpriv->mac80211.link_state >= MAC80211_LINKED) {
+ rtlpriv->dm.one_entry_only = true;
+ return;
+ }
+
+ if (rtlpriv->mac80211.opmode == NL80211_IFTYPE_AP ||
+ rtlpriv->mac80211.opmode == NL80211_IFTYPE_ADHOC ||
+ rtlpriv->mac80211.opmode == NL80211_IFTYPE_MESH_POINT) {
+ spin_lock_bh(&rtlpriv->locks.entry_list_lock);
+ list_for_each_entry(drv_priv, &rtlpriv->entry_list, list) {
+ cnt++;
+ }
+ spin_unlock_bh(&rtlpriv->locks.entry_list_lock);
+
+ if (cnt == 1)
+ rtlpriv->dm.one_entry_only = true;
+ }
+}
+
+void rtl8723be_dm_watchdog(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ bool fw_current_inpsmode = false;
+ bool fw_ps_awake = true;
+
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
+ (u8 *)(&fw_current_inpsmode));
+
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FWLPS_RF_ON,
+ (u8 *)(&fw_ps_awake));
+
+ if (ppsc->p2p_ps_info.p2p_ps_mode)
+ fw_ps_awake = false;
+
+ if ((ppsc->rfpwr_state == ERFON) &&
+ ((!fw_current_inpsmode) && fw_ps_awake) &&
+ (!ppsc->rfchange_inprogress)) {
+ rtl8723be_dm_common_info_self_update(hw);
+ rtl8723be_dm_false_alarm_counter_statistics(hw);
+ rtl8723be_dm_check_rssi_monitor(hw);
+ rtl8723be_dm_dig(hw);
+ rtl8723be_dm_dynamic_edcca(hw);
+ rtl8723be_dm_cck_packet_detection_thresh(hw);
+ rtl8723be_dm_refresh_rate_adaptive_mask(hw);
+ rtl8723be_dm_check_edca_turbo(hw);
+ rtl8723be_dm_dynamic_atc_switch(hw);
+ rtl8723be_dm_check_txpower_tracking(hw);
+ rtl8723be_dm_dynamic_txpower(hw);
+ if (rtlpriv->cfg->ops->get_btc_status())
+ rtlpriv->btcoexist.btc_ops->btc_periodical(rtlpriv);
+ }
+ rtlpriv->dm.dbginfo.num_qry_beacon_pkt = 0;
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h
new file mode 100644
index 000000000000..c6c2f2a78a66
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h
@@ -0,0 +1,310 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_DM_H__
+#define __RTL8723BE_DM_H__
+
+#define MAIN_ANT 0
+#define AUX_ANT 1
+#define MAIN_ANT_CG_TRX 1
+#define AUX_ANT_CG_TRX 0
+#define MAIN_ANT_CGCS_RX 0
+#define AUX_ANT_CGCS_RX 1
+
+#define TXSCALE_TABLE_SIZE 30
+
+/*RF REG LIST*/
+#define DM_REG_RF_MODE_11N 0x00
+#define DM_REG_RF_0B_11N 0x0B
+#define DM_REG_CHNBW_11N 0x18
+#define DM_REG_T_METER_11N 0x24
+#define DM_REG_RF_25_11N 0x25
+#define DM_REG_RF_26_11N 0x26
+#define DM_REG_RF_27_11N 0x27
+#define DM_REG_RF_2B_11N 0x2B
+#define DM_REG_RF_2C_11N 0x2C
+#define DM_REG_RXRF_A3_11N 0x3C
+#define DM_REG_T_METER_92D_11N 0x42
+#define DM_REG_T_METER_88E_11N 0x42
+
+/*BB REG LIST*/
+/*PAGE 8 */
+#define DM_REG_BB_CTRL_11N 0x800
+#define DM_REG_RF_PIN_11N 0x804
+#define DM_REG_PSD_CTRL_11N 0x808
+#define DM_REG_TX_ANT_CTRL_11N 0x80C
+#define DM_REG_BB_PWR_SAV5_11N 0x818
+#define DM_REG_CCK_RPT_FORMAT_11N 0x824
+#define DM_REG_RX_DEFUALT_A_11N 0x858
+#define DM_REG_RX_DEFUALT_B_11N 0x85A
+#define DM_REG_BB_PWR_SAV3_11N 0x85C
+#define DM_REG_ANTSEL_CTRL_11N 0x860
+#define DM_REG_RX_ANT_CTRL_11N 0x864
+#define DM_REG_PIN_CTRL_11N 0x870
+#define DM_REG_BB_PWR_SAV1_11N 0x874
+#define DM_REG_ANTSEL_PATH_11N 0x878
+#define DM_REG_BB_3WIRE_11N 0x88C
+#define DM_REG_SC_CNT_11N 0x8C4
+#define DM_REG_PSD_DATA_11N 0x8B4
+/*PAGE 9*/
+#define DM_REG_ANT_MAPPING1_11N 0x914
+#define DM_REG_ANT_MAPPING2_11N 0x918
+/*PAGE A*/
+#define DM_REG_CCK_ANTDIV_PARA1_11N 0xA00
+#define DM_REG_CCK_CCA_11N 0xA0A
+#define DM_REG_CCK_ANTDIV_PARA2_11N 0xA0C
+#define DM_REG_CCK_ANTDIV_PARA3_11N 0xA10
+#define DM_REG_CCK_ANTDIV_PARA4_11N 0xA14
+#define DM_REG_CCK_FILTER_PARA1_11N 0xA22
+#define DM_REG_CCK_FILTER_PARA2_11N 0xA23
+#define DM_REG_CCK_FILTER_PARA3_11N 0xA24
+#define DM_REG_CCK_FILTER_PARA4_11N 0xA25
+#define DM_REG_CCK_FILTER_PARA5_11N 0xA26
+#define DM_REG_CCK_FILTER_PARA6_11N 0xA27
+#define DM_REG_CCK_FILTER_PARA7_11N 0xA28
+#define DM_REG_CCK_FILTER_PARA8_11N 0xA29
+#define DM_REG_CCK_FA_RST_11N 0xA2C
+#define DM_REG_CCK_FA_MSB_11N 0xA58
+#define DM_REG_CCK_FA_LSB_11N 0xA5C
+#define DM_REG_CCK_CCA_CNT_11N 0xA60
+#define DM_REG_BB_PWR_SAV4_11N 0xA74
+/*PAGE B */
+#define DM_REG_LNA_SWITCH_11N 0xB2C
+#define DM_REG_PATH_SWITCH_11N 0xB30
+#define DM_REG_RSSI_CTRL_11N 0xB38
+#define DM_REG_CONFIG_ANTA_11N 0xB68
+#define DM_REG_RSSI_BT_11N 0xB9C
+/*PAGE C */
+#define DM_REG_OFDM_FA_HOLDC_11N 0xC00
+#define DM_REG_RX_PATH_11N 0xC04
+#define DM_REG_TRMUX_11N 0xC08
+#define DM_REG_OFDM_FA_RSTC_11N 0xC0C
+#define DM_REG_RXIQI_MATRIX_11N 0xC14
+#define DM_REG_TXIQK_MATRIX_LSB1_11N 0xC4C
+#define DM_REG_IGI_A_11N 0xC50
+#define DM_REG_ANTDIV_PARA2_11N 0xC54
+#define DM_REG_IGI_B_11N 0xC58
+#define DM_REG_ANTDIV_PARA3_11N 0xC5C
+#define DM_REG_BB_PWR_SAV2_11N 0xC70
+#define DM_REG_RX_OFF_11N 0xC7C
+#define DM_REG_TXIQK_MATRIXA_11N 0xC80
+#define DM_REG_TXIQK_MATRIXB_11N 0xC88
+#define DM_REG_TXIQK_MATRIXA_LSB2_11N 0xC94
+#define DM_REG_TXIQK_MATRIXB_LSB2_11N 0xC9C
+#define DM_REG_RXIQK_MATRIX_LSB_11N 0xCA0
+#define DM_REG_ANTDIV_PARA1_11N 0xCA4
+#define DM_REG_OFDM_FA_TYPE1_11N 0xCF0
+/*PAGE D */
+#define DM_REG_OFDM_FA_RSTD_11N 0xD00
+#define DM_REG_OFDM_FA_TYPE2_11N 0xDA0
+#define DM_REG_OFDM_FA_TYPE3_11N 0xDA4
+#define DM_REG_OFDM_FA_TYPE4_11N 0xDA8
+/*PAGE E */
+#define DM_REG_TXAGC_A_6_18_11N 0xE00
+#define DM_REG_TXAGC_A_24_54_11N 0xE04
+#define DM_REG_TXAGC_A_1_MCS32_11N 0xE08
+#define DM_REG_TXAGC_A_MCS0_3_11N 0xE10
+#define DM_REG_TXAGC_A_MCS4_7_11N 0xE14
+#define DM_REG_TXAGC_A_MCS8_11_11N 0xE18
+#define DM_REG_TXAGC_A_MCS12_15_11N 0xE1C
+#define DM_REG_FPGA0_IQK_11N 0xE28
+#define DM_REG_TXIQK_TONE_A_11N 0xE30
+#define DM_REG_RXIQK_TONE_A_11N 0xE34
+#define DM_REG_TXIQK_PI_A_11N 0xE38
+#define DM_REG_RXIQK_PI_A_11N 0xE3C
+#define DM_REG_TXIQK_11N 0xE40
+#define DM_REG_RXIQK_11N 0xE44
+#define DM_REG_IQK_AGC_PTS_11N 0xE48
+#define DM_REG_IQK_AGC_RSP_11N 0xE4C
+#define DM_REG_BLUETOOTH_11N 0xE6C
+#define DM_REG_RX_WAIT_CCA_11N 0xE70
+#define DM_REG_TX_CCK_RFON_11N 0xE74
+#define DM_REG_TX_CCK_BBON_11N 0xE78
+#define DM_REG_OFDM_RFON_11N 0xE7C
+#define DM_REG_OFDM_BBON_11N 0xE80
+#define DM_REG_TX2RX_11N 0xE84
+#define DM_REG_TX2TX_11N 0xE88
+#define DM_REG_RX_CCK_11N 0xE8C
+#define DM_REG_RX_OFDM_11N 0xED0
+#define DM_REG_RX_WAIT_RIFS_11N 0xED4
+#define DM_REG_RX2RX_11N 0xED8
+#define DM_REG_STANDBY_11N 0xEDC
+#define DM_REG_SLEEP_11N 0xEE0
+#define DM_REG_PMPD_ANAEN_11N 0xEEC
+
+/*MAC REG LIST*/
+#define DM_REG_BB_RST_11N 0x02
+#define DM_REG_ANTSEL_PIN_11N 0x4C
+#define DM_REG_EARLY_MODE_11N 0x4D0
+#define DM_REG_RSSI_MONITOR_11N 0x4FE
+#define DM_REG_EDCA_VO_11N 0x500
+#define DM_REG_EDCA_VI_11N 0x504
+#define DM_REG_EDCA_BE_11N 0x508
+#define DM_REG_EDCA_BK_11N 0x50C
+#define DM_REG_TXPAUSE_11N 0x522
+#define DM_REG_RESP_TX_11N 0x6D8
+#define DM_REG_ANT_TRAIN_PARA1_11N 0x7b0
+#define DM_REG_ANT_TRAIN_PARA2_11N 0x7b4
+
+/*DIG Related*/
+#define DM_BIT_IGI_11N 0x0000007F
+
+#define HAL_DM_DIG_DISABLE BIT(0)
+#define HAL_DM_HIPWR_DISABLE BIT(1)
+
+#define OFDM_TABLE_LENGTH 43
+#define CCK_TABLE_LENGTH 33
+
+#define OFDM_TABLE_SIZE 37
+#define CCK_TABLE_SIZE 33
+
+#define BW_AUTO_SWITCH_HIGH_LOW 25
+#define BW_AUTO_SWITCH_LOW_HIGH 30
+
+#define DM_DIG_THRESH_HIGH 40
+#define DM_DIG_THRESH_LOW 35
+
+#define DM_FALSEALARM_THRESH_LOW 400
+#define DM_FALSEALARM_THRESH_HIGH 1000
+
+#define DM_DIG_MAX 0x3e
+#define DM_DIG_MIN 0x1e
+
+#define DM_DIG_MAX_AP 0x32
+#define DM_DIG_MIN_AP 0x20
+
+#define DM_DIG_FA_UPPER 0x3e
+#define DM_DIG_FA_LOWER 0x1e
+#define DM_DIG_FA_TH0 0x200
+#define DM_DIG_FA_TH1 0x300
+#define DM_DIG_FA_TH2 0x400
+
+#define DM_DIG_BACKOFF_MAX 12
+#define DM_DIG_BACKOFF_MIN -4
+#define DM_DIG_BACKOFF_DEFAULT 10
+
+#define RXPATHSELECTION_DIFF_TH 18
+
+#define DM_RATR_STA_INIT 0
+#define DM_RATR_STA_HIGH 1
+#define DM_RATR_STA_MIDDLE 2
+#define DM_RATR_STA_LOW 3
+
+#define CTS2SELF_THVAL 30
+#define REGC38_TH 20
+
+#define TXHIGHPWRLEVEL_NORMAL 0
+#define TXHIGHPWRLEVEL_LEVEL1 1
+#define TXHIGHPWRLEVEL_LEVEL2 2
+#define TXHIGHPWRLEVEL_BT1 3
+#define TXHIGHPWRLEVEL_BT2 4
+
+#define DM_TYPE_BYFW 0
+#define DM_TYPE_BYDRIVER 1
+
+#define TX_POWER_NEAR_FIELD_THRESH_LVL2 74
+#define TX_POWER_NEAR_FIELD_THRESH_LVL1 67
+#define TXPWRTRACK_MAX_IDX 6
+
+/* Dynamic ATC switch */
+#define ATC_STATUS_OFF 0x0 /* enable */
+#define ATC_STATUS_ON 0x1 /* disable */
+#define CFO_THRESHOLD_XTAL 10 /* kHz */
+#define CFO_THRESHOLD_ATC 80 /* kHz */
+
+enum FAT_STATE {
+ FAT_NORMAL_STATE = 0,
+ FAT_TRAINING_STATE = 1,
+};
+
+enum tag_dynamic_init_gain_operation_type_definition {
+ DIG_TYPE_THRESH_HIGH = 0,
+ DIG_TYPE_THRESH_LOW = 1,
+ DIG_TYPE_BACKOFF = 2,
+ DIG_TYPE_RX_GAIN_MIN = 3,
+ DIG_TYPE_RX_GAIN_MAX = 4,
+ DIG_TYPE_ENABLE = 5,
+ DIG_TYPE_DISABLE = 6,
+ DIG_OP_TYPE_MAX
+};
+
+enum dm_1r_cca_e {
+ CCA_1R = 0,
+ CCA_2R = 1,
+ CCA_MAX = 2,
+};
+
+enum dm_rf_e {
+ RF_SAVE = 0,
+ RF_NORMAL = 1,
+ RF_MAX = 2,
+};
+
+enum dm_sw_ant_switch_e {
+ ANS_ANTENNA_B = 1,
+ ANS_ANTENNA_A = 2,
+ ANS_ANTENNA_MAX = 3,
+};
+
+enum dm_dig_ext_port_alg_e {
+ DIG_EXT_PORT_STAGE_0 = 0,
+ DIG_EXT_PORT_STAGE_1 = 1,
+ DIG_EXT_PORT_STAGE_2 = 2,
+ DIG_EXT_PORT_STAGE_3 = 3,
+ DIG_EXT_PORT_STAGE_MAX = 4,
+};
+
+enum dm_dig_connect_e {
+ DIG_STA_DISCONNECT = 0,
+ DIG_STA_CONNECT = 1,
+ DIG_STA_BEFORE_CONNECT = 2,
+ DIG_MULTISTA_DISCONNECT = 3,
+ DIG_MULTISTA_CONNECT = 4,
+ DIG_CONNECT_MAX
+};
+
+enum pwr_track_control_method {
+ BBSWING,
+ TXAGC
+};
+
+#define BT_RSSI_STATE_NORMAL_POWER BIT_OFFSET_LEN_MASK_32(0, 1)
+#define BT_RSSI_STATE_AMDPU_OFF BIT_OFFSET_LEN_MASK_32(1, 1)
+#define BT_RSSI_STATE_SPECIAL_LOW BIT_OFFSET_LEN_MASK_32(2, 1)
+#define BT_RSSI_STATE_BG_EDCA_LOW BIT_OFFSET_LEN_MASK_32(3, 1)
+#define BT_RSSI_STATE_TXPOWER_LOW BIT_OFFSET_LEN_MASK_32(4, 1)
+
+void rtl8723be_dm_set_tx_ant_by_tx_info(struct ieee80211_hw *hw, u8 *pdesc,
+ u32 mac_id);
+void rtl8723be_dm_ant_sel_statistics(struct ieee80211_hw *hw, u8 antsel_tr_mux,
+ u32 mac_id, u32 rx_pwdb_all);
+void rtl8723be_dm_fast_antenna_trainning_callback(unsigned long data);
+void rtl8723be_dm_init(struct ieee80211_hw *hw);
+void rtl8723be_dm_watchdog(struct ieee80211_hw *hw);
+void rtl8723be_dm_write_dig(struct ieee80211_hw *hw, u8 current_igi);
+void rtl8723be_dm_check_txpower_tracking(struct ieee80211_hw *hw);
+void rtl8723be_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw);
+void rtl8723be_dm_txpower_track_adjust(struct ieee80211_hw *hw, u8 type,
+ u8 *pdirection, u32 *poutwrite_val);
+void rtl8723be_dm_init_edca_turbo(struct ieee80211_hw *hw);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/fw.c b/drivers/net/wireless/rtlwifi/rtl8723be/fw.c
new file mode 100644
index 000000000000..f856be6fc138
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/fw.c
@@ -0,0 +1,620 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "../base.h"
+#include "reg.h"
+#include "def.h"
+#include "fw.h"
+#include "../rtl8723com/fw_common.h"
+
+static bool _rtl8723be_check_fw_read_last_h2c(struct ieee80211_hw *hw,
+ u8 boxnum)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 val_hmetfr;
+ bool result = false;
+
+ val_hmetfr = rtl_read_byte(rtlpriv, REG_HMETFR);
+ if (((val_hmetfr >> boxnum) & BIT(0)) == 0)
+ result = true;
+ return result;
+}
+
+static void _rtl8723be_fill_h2c_command(struct ieee80211_hw *hw, u8 element_id,
+ u32 cmd_len, u8 *p_cmdbuffer)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 boxnum;
+ u16 box_reg = 0, box_extreg = 0;
+ u8 u1b_tmp;
+ bool isfw_read = false;
+ u8 buf_index = 0;
+ bool bwrite_sucess = false;
+ u8 wait_h2c_limit = 100;
+ u8 wait_writeh2c_limit = 100;
+ u8 boxcontent[4], boxextcontent[4];
+ u32 h2c_waitcounter = 0;
+ unsigned long flag;
+ u8 idx;
+
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "come in\n");
+
+ while (true) {
+ spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag);
+ if (rtlhal->h2c_setinprogress) {
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ "H2C set in progress! Wait to set.."
+ "element_id(%d).\n", element_id);
+
+ while (rtlhal->h2c_setinprogress) {
+ spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock,
+ flag);
+ h2c_waitcounter++;
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ "Wait 100 us (%d times)...\n",
+ h2c_waitcounter);
+ udelay(100);
+
+ if (h2c_waitcounter > 1000)
+ return;
+ spin_lock_irqsave(&rtlpriv->locks.h2c_lock,
+ flag);
+ }
+ spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
+ } else {
+ rtlhal->h2c_setinprogress = true;
+ spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
+ break;
+ }
+ }
+ while (!bwrite_sucess) {
+ wait_writeh2c_limit--;
+ if (wait_writeh2c_limit == 0) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "Write H2C fail because no trigger "
+ "for FW INT!\n");
+ break;
+ }
+ boxnum = rtlhal->last_hmeboxnum;
+ switch (boxnum) {
+ case 0:
+ box_reg = REG_HMEBOX_0;
+ box_extreg = REG_HMEBOX_EXT_0;
+ break;
+ case 1:
+ box_reg = REG_HMEBOX_1;
+ box_extreg = REG_HMEBOX_EXT_1;
+ break;
+ case 2:
+ box_reg = REG_HMEBOX_2;
+ box_extreg = REG_HMEBOX_EXT_2;
+ break;
+ case 3:
+ box_reg = REG_HMEBOX_3;
+ box_extreg = REG_HMEBOX_EXT_3;
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not processed\n");
+ break;
+ }
+ isfw_read = _rtl8723be_check_fw_read_last_h2c(hw, boxnum);
+ while (!isfw_read) {
+ wait_h2c_limit--;
+ if (wait_h2c_limit == 0) {
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ "Wating too long for FW read "
+ "clear HMEBox(%d)!\n", boxnum);
+ break;
+ }
+ udelay(10);
+
+ isfw_read = _rtl8723be_check_fw_read_last_h2c(hw,
+ boxnum);
+ u1b_tmp = rtl_read_byte(rtlpriv, 0x130);
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ "Wating for FW read clear HMEBox(%d)!!! 0x130 = %2x\n",
+ boxnum, u1b_tmp);
+ }
+ if (!isfw_read) {
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ "Write H2C register BOX[%d] fail!!!!! "
+ "Fw do not read.\n", boxnum);
+ break;
+ }
+ memset(boxcontent, 0, sizeof(boxcontent));
+ memset(boxextcontent, 0, sizeof(boxextcontent));
+ boxcontent[0] = element_id;
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ "Write element_id box_reg(%4x) = %2x\n",
+ box_reg, element_id);
+
+ switch (cmd_len) {
+ case 1:
+ case 2:
+ case 3:
+ /*boxcontent[0] &= ~(BIT(7));*/
+ memcpy((u8 *)(boxcontent) + 1,
+ p_cmdbuffer + buf_index, cmd_len);
+
+ for (idx = 0; idx < 4; idx++) {
+ rtl_write_byte(rtlpriv, box_reg + idx,
+ boxcontent[idx]);
+ }
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ /*boxcontent[0] |= (BIT(7));*/
+ memcpy((u8 *)(boxextcontent),
+ p_cmdbuffer + buf_index+3, cmd_len-3);
+ memcpy((u8 *)(boxcontent) + 1,
+ p_cmdbuffer + buf_index, 3);
+
+ for (idx = 0; idx < 4; idx++) {
+ rtl_write_byte(rtlpriv, box_extreg + idx,
+ boxextcontent[idx]);
+ }
+ for (idx = 0; idx < 4; idx++) {
+ rtl_write_byte(rtlpriv, box_reg + idx,
+ boxcontent[idx]);
+ }
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process\n");
+ break;
+ }
+ bwrite_sucess = true;
+
+ rtlhal->last_hmeboxnum = boxnum + 1;
+ if (rtlhal->last_hmeboxnum == 4)
+ rtlhal->last_hmeboxnum = 0;
+
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD,
+ "pHalData->last_hmeboxnum = %d\n",
+ rtlhal->last_hmeboxnum);
+ }
+ spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag);
+ rtlhal->h2c_setinprogress = false;
+ spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag);
+
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "go out\n");
+}
+
+void rtl8723be_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id,
+ u32 cmd_len, u8 *p_cmdbuffer)
+{
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u32 tmp_cmdbuf[2];
+
+ if (!rtlhal->fw_ready) {
+ RT_ASSERT(false,
+ "return H2C cmd because of Fw download fail!!!\n");
+ return;
+ }
+ memset(tmp_cmdbuf, 0, 8);
+ memcpy(tmp_cmdbuf, p_cmdbuffer, cmd_len);
+ _rtl8723be_fill_h2c_command(hw, element_id, cmd_len,
+ (u8 *)&tmp_cmdbuf);
+ return;
+}
+
+void rtl8723be_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 u1_h2c_set_pwrmode[H2C_8723BE_PWEMODE_LENGTH] = { 0 };
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ u8 rlbm, power_state = 0;
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode);
+
+ SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, ((mode) ? 1 : 0));
+ rlbm = 0;/*YJ, temp, 120316. FW now not support RLBM = 2.*/
+ SET_H2CCMD_PWRMODE_PARM_RLBM(u1_h2c_set_pwrmode, rlbm);
+ SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode,
+ (rtlpriv->mac80211.p2p) ?
+ ppsc->smart_ps : 1);
+ SET_H2CCMD_PWRMODE_PARM_AWAKE_INTERVAL(u1_h2c_set_pwrmode,
+ ppsc->reg_max_lps_awakeintvl);
+ SET_H2CCMD_PWRMODE_PARM_ALL_QUEUE_UAPSD(u1_h2c_set_pwrmode, 0);
+ if (mode == FW_PS_ACTIVE_MODE)
+ power_state |= FW_PWR_STATE_ACTIVE;
+ else
+ power_state |= FW_PWR_STATE_RF_OFF;
+ SET_H2CCMD_PWRMODE_PARM_PWR_STATE(u1_h2c_set_pwrmode, power_state);
+
+ RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
+ "rtl92c_set_fw_pwrmode(): u1_h2c_set_pwrmode\n",
+ u1_h2c_set_pwrmode, H2C_8723BE_PWEMODE_LENGTH);
+ rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_SETPWRMODE,
+ H2C_8723BE_PWEMODE_LENGTH,
+ u1_h2c_set_pwrmode);
+}
+
+static bool _rtl8723be_cmd_send_packet(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl8192_tx_ring *ring;
+ struct rtl_tx_desc *pdesc;
+ struct sk_buff *pskb = NULL;
+ u8 own;
+ unsigned long flags;
+
+ ring = &rtlpci->tx_ring[BEACON_QUEUE];
+
+ pskb = __skb_dequeue(&ring->queue);
+ if (pskb)
+ kfree_skb(pskb);
+
+ spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
+
+ pdesc = &ring->desc[0];
+ own = (u8) rtlpriv->cfg->ops->get_desc((u8 *)pdesc, true, HW_DESC_OWN);
+
+ rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *)pdesc, 1, 1, skb);
+
+ __skb_queue_tail(&ring->queue, skb);
+
+ spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags);
+
+ rtlpriv->cfg->ops->tx_polling(hw, BEACON_QUEUE);
+
+ return true;
+}
+#define BEACON_PG 0 /* ->1 */
+#define PSPOLL_PG 2
+#define NULL_PG 3
+#define PROBERSP_PG 4 /* ->5 */
+
+#define TOTAL_RESERVED_PKT_LEN 768
+
+static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = {
+ /* page 0 beacon */
+ 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x00, 0xE0, 0x4C, 0x02, 0xB1, 0x78,
+ 0xEC, 0x1A, 0x59, 0x0B, 0xAD, 0xD4, 0x20, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x64, 0x00, 0x10, 0x04, 0x00, 0x05, 0x54, 0x65,
+ 0x73, 0x74, 0x32, 0x01, 0x08, 0x82, 0x84, 0x0B,
+ 0x16, 0x24, 0x30, 0x48, 0x6C, 0x03, 0x01, 0x06,
+ 0x06, 0x02, 0x00, 0x00, 0x2A, 0x01, 0x02, 0x32,
+ 0x04, 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C,
+ 0x09, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3D, 0x00, 0xDD, 0x07, 0x00, 0xE0, 0x4C,
+ 0x02, 0x02, 0x00, 0x00, 0xDD, 0x18, 0x00, 0x50,
+ 0xF2, 0x01, 0x01, 0x00, 0x00, 0x50, 0xF2, 0x04,
+ 0x01, 0x00, 0x00, 0x50, 0xF2, 0x04, 0x01, 0x00,
+
+ /* page 1 beacon */
+ 0x00, 0x50, 0xF2, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* page 2 ps-poll */
+ 0xA4, 0x10, 0x01, 0xC0, 0xEC, 0x1A, 0x59, 0x0B,
+ 0xAD, 0xD4, 0x00, 0xE0, 0x4C, 0x02, 0xB1, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* page 3 null */
+ 0x48, 0x01, 0x00, 0x00, 0xEC, 0x1A, 0x59, 0x0B,
+ 0xAD, 0xD4, 0x00, 0xE0, 0x4C, 0x02, 0xB1, 0x78,
+ 0xEC, 0x1A, 0x59, 0x0B, 0xAD, 0xD4, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x72, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* page 4 probe_resp */
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10,
+ 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42,
+ 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00,
+ 0x9E, 0x46, 0x15, 0x32, 0x27, 0xF2, 0x2D, 0x00,
+ 0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69,
+ 0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C,
+ 0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96,
+ 0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A,
+ 0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C,
+ 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02,
+ 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* page 5 probe_resp */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+void rtl8723be_set_fw_rsvdpagepkt(struct ieee80211_hw *hw,
+ bool dl_finished)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct sk_buff *skb = NULL;
+
+ u32 totalpacketlen;
+ bool rtstatus;
+ u8 u1rsvdpageloc[5] = { 0 };
+ bool dlok = false;
+
+ u8 *beacon;
+ u8 *p_pspoll;
+ u8 *nullfunc;
+ u8 *p_probersp;
+ /*---------------------------------------------------------
+ * (1) beacon
+ *---------------------------------------------------------
+ */
+ beacon = &reserved_page_packet[BEACON_PG * 128];
+ SET_80211_HDR_ADDRESS2(beacon, mac->mac_addr);
+ SET_80211_HDR_ADDRESS3(beacon, mac->bssid);
+
+ /*-------------------------------------------------------
+ * (2) ps-poll
+ *-------------------------------------------------------
+ */
+ p_pspoll = &reserved_page_packet[PSPOLL_PG * 128];
+ SET_80211_PS_POLL_AID(p_pspoll, (mac->assoc_id | 0xc000));
+ SET_80211_PS_POLL_BSSID(p_pspoll, mac->bssid);
+ SET_80211_PS_POLL_TA(p_pspoll, mac->mac_addr);
+
+ SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(u1rsvdpageloc, PSPOLL_PG);
+
+ /*--------------------------------------------------------
+ * (3) null data
+ *--------------------------------------------------------
+ */
+ nullfunc = &reserved_page_packet[NULL_PG * 128];
+ SET_80211_HDR_ADDRESS1(nullfunc, mac->bssid);
+ SET_80211_HDR_ADDRESS2(nullfunc, mac->mac_addr);
+ SET_80211_HDR_ADDRESS3(nullfunc, mac->bssid);
+
+ SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(u1rsvdpageloc, NULL_PG);
+
+ /*---------------------------------------------------------
+ * (4) probe response
+ *---------------------------------------------------------
+ */
+ p_probersp = &reserved_page_packet[PROBERSP_PG * 128];
+ SET_80211_HDR_ADDRESS1(p_probersp, mac->bssid);
+ SET_80211_HDR_ADDRESS2(p_probersp, mac->mac_addr);
+ SET_80211_HDR_ADDRESS3(p_probersp, mac->bssid);
+
+ SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(u1rsvdpageloc, PROBERSP_PG);
+
+ totalpacketlen = TOTAL_RESERVED_PKT_LEN;
+
+ RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD,
+ "rtl8723be_set_fw_rsvdpagepkt(): "
+ "HW_VAR_SET_TX_CMD: ALL\n",
+ &reserved_page_packet[0], totalpacketlen);
+ RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG,
+ "rtl8723be_set_fw_rsvdpagepkt(): "
+ "HW_VAR_SET_TX_CMD: ALL\n", u1rsvdpageloc, 3);
+
+
+ skb = dev_alloc_skb(totalpacketlen);
+ memcpy((u8 *)skb_put(skb, totalpacketlen),
+ &reserved_page_packet, totalpacketlen);
+
+ rtstatus = _rtl8723be_cmd_send_packet(hw, skb);
+
+ if (rtstatus)
+ dlok = true;
+
+ if (dlok) {
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ "Set RSVD page location to Fw.\n");
+ RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, "H2C_RSVDPAGE:\n",
+ u1rsvdpageloc, 3);
+ rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_RSVDPAGE,
+ sizeof(u1rsvdpageloc), u1rsvdpageloc);
+ } else {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ "Set RSVD page location to Fw FAIL!!!!!!.\n");
+ }
+}
+
+/*Should check FW support p2p or not.*/
+static void rtl8723be_set_p2p_ctw_period_cmd(struct ieee80211_hw *hw,
+ u8 ctwindow)
+{
+ u8 u1_ctwindow_period[1] = {ctwindow};
+
+ rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_P2P_PS_CTW_CMD, 1,
+ u1_ctwindow_period);
+}
+
+void rtl8723be_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw,
+ u8 p2p_ps_state)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_p2p_ps_info *p2pinfo = &(rtlps->p2p_ps_info);
+ struct p2p_ps_offload_t *p2p_ps_offload = &rtlhal->p2p_ps_offload;
+ u8 i;
+ u16 ctwindow;
+ u32 start_time, tsf_low;
+
+ switch (p2p_ps_state) {
+ case P2P_PS_DISABLE:
+ RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_DISABLE\n");
+ memset(p2p_ps_offload, 0, sizeof(struct p2p_ps_offload_t));
+ break;
+ case P2P_PS_ENABLE:
+ RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_ENABLE\n");
+ /* update CTWindow value. */
+ if (p2pinfo->ctwindow > 0) {
+ p2p_ps_offload->ctwindow_en = 1;
+ ctwindow = p2pinfo->ctwindow;
+ rtl8723be_set_p2p_ctw_period_cmd(hw, ctwindow);
+ }
+ /* hw only support 2 set of NoA */
+ for (i = 0; i < p2pinfo->noa_num; i++) {
+ /* To control the register setting
+ * for which NOA
+ */
+ rtl_write_byte(rtlpriv, 0x5cf, (i << 4));
+ if (i == 0)
+ p2p_ps_offload->noa0_en = 1;
+ else
+ p2p_ps_offload->noa1_en = 1;
+
+ /* config P2P NoA Descriptor Register */
+ rtl_write_dword(rtlpriv, 0x5E0,
+ p2pinfo->noa_duration[i]);
+ rtl_write_dword(rtlpriv, 0x5E4,
+ p2pinfo->noa_interval[i]);
+
+ /*Get Current TSF value */
+ tsf_low = rtl_read_dword(rtlpriv, REG_TSFTR);
+
+ start_time = p2pinfo->noa_start_time[i];
+ if (p2pinfo->noa_count_type[i] != 1) {
+ while (start_time <= (tsf_low + (50 * 1024))) {
+ start_time += p2pinfo->noa_interval[i];
+ if (p2pinfo->noa_count_type[i] != 255)
+ p2pinfo->noa_count_type[i]--;
+ }
+ }
+ rtl_write_dword(rtlpriv, 0x5E8, start_time);
+ rtl_write_dword(rtlpriv, 0x5EC,
+ p2pinfo->noa_count_type[i]);
+ }
+ if ((p2pinfo->opp_ps == 1) ||
+ (p2pinfo->noa_num > 0)) {
+ /* rst p2p circuit */
+ rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, BIT(4));
+
+ p2p_ps_offload->offload_en = 1;
+
+ if (P2P_ROLE_GO == rtlpriv->mac80211.p2p) {
+ p2p_ps_offload->role = 1;
+ p2p_ps_offload->allstasleep = 0;
+ } else {
+ p2p_ps_offload->role = 0;
+ }
+ p2p_ps_offload->discovery = 0;
+ }
+ break;
+ case P2P_PS_SCAN:
+ RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN\n");
+ p2p_ps_offload->discovery = 1;
+ break;
+ case P2P_PS_SCAN_DONE:
+ RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN_DONE\n");
+ p2p_ps_offload->discovery = 0;
+ p2pinfo->p2p_ps_state = P2P_PS_ENABLE;
+ break;
+ default:
+ break;
+ }
+ rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_P2P_PS_OFFLOAD, 1,
+ (u8 *)p2p_ps_offload);
+}
+
+void rtl8723be_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus)
+{
+ u8 u1_joinbssrpt_parm[1] = { 0 };
+
+ SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(u1_joinbssrpt_parm, mstatus);
+
+ rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_JOINBSSRPT, 1,
+ u1_joinbssrpt_parm);
+}
+
+void rtl8723be_set_fw_ap_off_load_cmd(struct ieee80211_hw *hw,
+ u8 ap_offload_enable)
+{
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ u8 u1_apoffload_parm[H2C_8723BE_AP_OFFLOAD_LENGTH] = { 0 };
+
+ SET_H2CCMD_AP_OFFLOAD_ON(u1_apoffload_parm, ap_offload_enable);
+ SET_H2CCMD_AP_OFFLOAD_HIDDEN(u1_apoffload_parm, mac->hiddenssid);
+ SET_H2CCMD_AP_OFFLOAD_DENYANY(u1_apoffload_parm, 0);
+
+ rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_AP_OFFLOAD,
+ H2C_8723BE_AP_OFFLOAD_LENGTH, u1_apoffload_parm);
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/fw.h b/drivers/net/wireless/rtlwifi/rtl8723be/fw.h
new file mode 100644
index 000000000000..31eec281e446
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/fw.h
@@ -0,0 +1,248 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE__FW__H__
+#define __RTL8723BE__FW__H__
+
+#define FW_8192C_SIZE 0x8000
+#define FW_8192C_START_ADDRESS 0x1000
+#define FW_8192C_END_ADDRESS 0x5FFF
+#define FW_8192C_PAGE_SIZE 4096
+#define FW_8192C_POLLING_DELAY 5
+#define FW_8192C_POLLING_TIMEOUT_COUNT 6000
+
+#define IS_FW_HEADER_EXIST(_pfwhdr) \
+ ((_pfwhdr->signature&0xFFF0) == 0x5300)
+#define USE_OLD_WOWLAN_DEBUG_FW 0
+
+#define H2C_8723BE_RSVDPAGE_LOC_LEN 5
+#define H2C_8723BE_PWEMODE_LENGTH 5
+#define H2C_8723BE_JOINBSSRPT_LENGTH 1
+#define H2C_8723BE_AP_OFFLOAD_LENGTH 3
+#define H2C_8723BE_WOWLAN_LENGTH 3
+#define H2C_8723BE_KEEP_ALIVE_CTRL_LENGTH 3
+#if (USE_OLD_WOWLAN_DEBUG_FW == 0)
+#define H2C_8723BE_REMOTE_WAKE_CTRL_LEN 1
+#else
+#define H2C_8723BE_REMOTE_WAKE_CTRL_LEN 3
+#endif
+#define H2C_8723BE_AOAC_GLOBAL_INFO_LEN 2
+#define H2C_8723BE_AOAC_RSVDPAGE_LOC_LEN 7
+
+
+/* Fw PS state for RPWM.
+*BIT[2:0] = HW state
+*BIT[3] = Protocol PS state, 1: register active state , 0: register sleep state
+*BIT[4] = sub-state
+*/
+#define FW_PS_GO_ON BIT(0)
+#define FW_PS_TX_NULL BIT(1)
+#define FW_PS_RF_ON BIT(2)
+#define FW_PS_REGISTER_ACTIVE BIT(3)
+
+#define FW_PS_DPS BIT(0)
+#define FW_PS_LCLK (FW_PS_DPS)
+#define FW_PS_RF_OFF BIT(1)
+#define FW_PS_ALL_ON BIT(2)
+#define FW_PS_ST_ACTIVE BIT(3)
+#define FW_PS_ISR_ENABLE BIT(4)
+#define FW_PS_IMR_ENABLE BIT(5)
+
+
+#define FW_PS_ACK BIT(6)
+#define FW_PS_TOGGLE BIT(7)
+
+ /* 88E RPWM value*/
+ /* BIT[0] = 1: 32k, 0: 40M*/
+#define FW_PS_CLOCK_OFF BIT(0) /* 32k*/
+#define FW_PS_CLOCK_ON 0 /*40M*/
+
+#define FW_PS_STATE_MASK (0x0F)
+#define FW_PS_STATE_HW_MASK (0x07)
+/*ISR_ENABLE, IMR_ENABLE, and PS mode should be inherited.*/
+#define FW_PS_STATE_INT_MASK (0x3F)
+
+#define FW_PS_STATE(x) (FW_PS_STATE_MASK & (x))
+#define FW_PS_STATE_HW(x) (FW_PS_STATE_HW_MASK & (x))
+#define FW_PS_STATE_INT(x) (FW_PS_STATE_INT_MASK & (x))
+#define FW_PS_ISR_VAL(x) ((x) & 0x70)
+#define FW_PS_IMR_MASK(x) ((x) & 0xDF)
+#define FW_PS_KEEP_IMR(x) ((x) & 0x20)
+
+
+#define FW_PS_STATE_S0 (FW_PS_DPS)
+#define FW_PS_STATE_S1 (FW_PS_LCLK)
+#define FW_PS_STATE_S2 (FW_PS_RF_OFF)
+#define FW_PS_STATE_S3 (FW_PS_ALL_ON)
+#define FW_PS_STATE_S4 ((FW_PS_ST_ACTIVE) | (FW_PS_ALL_ON))
+
+/* ((FW_PS_RF_ON) | (FW_PS_REGISTER_ACTIVE))*/
+#define FW_PS_STATE_ALL_ON_88E (FW_PS_CLOCK_ON)
+/* (FW_PS_RF_ON)*/
+#define FW_PS_STATE_RF_ON_88E (FW_PS_CLOCK_ON)
+/* 0x0*/
+#define FW_PS_STATE_RF_OFF_88E (FW_PS_CLOCK_ON)
+/* (FW_PS_STATE_RF_OFF)*/
+#define FW_PS_STATE_RF_OFF_LOW_PWR_88E (FW_PS_CLOCK_OFF)
+
+#define FW_PS_STATE_ALL_ON_92C (FW_PS_STATE_S4)
+#define FW_PS_STATE_RF_ON_92C (FW_PS_STATE_S3)
+#define FW_PS_STATE_RF_OFF_92C (FW_PS_STATE_S2)
+#define FW_PS_STATE_RF_OFF_LOW_PWR_92C (FW_PS_STATE_S1)
+
+
+/* For 88E H2C PwrMode Cmd ID 5.*/
+#define FW_PWR_STATE_ACTIVE ((FW_PS_RF_ON) | (FW_PS_REGISTER_ACTIVE))
+#define FW_PWR_STATE_RF_OFF 0
+
+#define FW_PS_IS_ACK(x) ((x) & FW_PS_ACK)
+#define FW_PS_IS_CLK_ON(x) ((x) & (FW_PS_RF_OFF | FW_PS_ALL_ON))
+#define FW_PS_IS_RF_ON(x) ((x) & (FW_PS_ALL_ON))
+#define FW_PS_IS_ACTIVE(x) ((x) & (FW_PS_ST_ACTIVE))
+#define FW_PS_IS_CPWM_INT(x) ((x) & 0x40)
+
+#define FW_CLR_PS_STATE(x) ((x) = ((x) & (0xF0)))
+
+#define IS_IN_LOW_POWER_STATE_88E(fwpsstate) \
+ (FW_PS_STATE(fwpsstate) == FW_PS_CLOCK_OFF)
+
+#define FW_PWR_STATE_ACTIVE ((FW_PS_RF_ON) | (FW_PS_REGISTER_ACTIVE))
+#define FW_PWR_STATE_RF_OFF 0
+
+#define pagenum_128(_len) (u32)(((_len)>>7) + ((_len)&0x7F ? 1 : 0))
+
+#define SET_88E_H2CCMD_WOWLAN_FUNC_ENABLE(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 1, __val)
+#define SET_88E_H2CCMD_WOWLAN_PATTERN_MATCH_ENABLE(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 1, 1, __val)
+#define SET_88E_H2CCMD_WOWLAN_MAGIC_PKT_ENABLE(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 2, 1, __val)
+#define SET_88E_H2CCMD_WOWLAN_UNICAST_PKT_ENABLE(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 3, 1, __val)
+#define SET_88E_H2CCMD_WOWLAN_ALL_PKT_DROP(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 4, 1, __val)
+#define SET_88E_H2CCMD_WOWLAN_GPIO_ACTIVE(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 5, 1, __val)
+#define SET_88E_H2CCMD_WOWLAN_REKEY_WAKE_UP(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 6, 1, __val)
+#define SET_88E_H2CCMD_WOWLAN_DISCONNECT_WAKE_UP(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 7, 1, __val)
+#define SET_88E_H2CCMD_WOWLAN_GPIONUM(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val)
+#define SET_88E_H2CCMD_WOWLAN_GPIO_DURATION(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val)
+
+
+#define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
+#define SET_H2CCMD_PWRMODE_PARM_RLBM(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 4, __val)
+#define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 4, 4, __val)
+#define SET_H2CCMD_PWRMODE_PARM_AWAKE_INTERVAL(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val)
+#define SET_H2CCMD_PWRMODE_PARM_ALL_QUEUE_UAPSD(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+3, 0, 8, __val)
+#define SET_H2CCMD_PWRMODE_PARM_PWR_STATE(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+4, 0, 8, __val)
+#define GET_88E_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd) \
+ LE_BITS_TO_1BYTE(__ph2ccmd, 0, 8)
+
+#define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
+#define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
+#define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val)
+#define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val)
+
+/* AP_OFFLOAD */
+#define SET_H2CCMD_AP_OFFLOAD_ON(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
+#define SET_H2CCMD_AP_OFFLOAD_HIDDEN(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val)
+#define SET_H2CCMD_AP_OFFLOAD_DENYANY(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val)
+#define SET_H2CCMD_AP_OFFLOAD_WAKEUP_EVT_RPT(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+3, 0, 8, __val)
+
+/* Keep Alive Control*/
+#define SET_88E_H2CCMD_KEEP_ALIVE_ENABLE(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 1, __val)
+#define SET_88E_H2CCMD_KEEP_ALIVE_ACCPEPT_USER_DEFINED(__ph2ccmd, __val)\
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 1, 1, __val)
+#define SET_88E_H2CCMD_KEEP_ALIVE_PERIOD(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val)
+
+/*REMOTE_WAKE_CTRL */
+#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_EN(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 1, __val)
+#if (USE_OLD_WOWLAN_DEBUG_FW == 0)
+#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_ARP_OFFLOAD_EN(__ph2ccmd, __val)\
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 1, 1, __val)
+#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_NDP_OFFLOAD_EN(__ph2ccmd, __val)\
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 2, 1, __val)
+#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_GTK_OFFLOAD_EN(__ph2ccmd, __val)\
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 3, 1, __val)
+#else
+#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_PAIRWISE_ENC_ALG(__ph2ccmd, __val)\
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val)
+#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_GROUP_ENC_ALG(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val)
+#endif
+
+/* GTK_OFFLOAD */
+#define SET_88E_H2CCMD_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(__ph2ccmd, __val)\
+ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val)
+#define SET_88E_H2CCMD_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val)
+
+/* AOAC_RSVDPAGE_LOC */
+#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_REM_WAKE_CTRL_INFO(__ph2ccmd, __val)\
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd), 0, 8, __val)
+#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_ARP_RSP(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val)
+#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_NEIGHBOR_ADV(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val)
+#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_RSP(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+3, 0, 8, __val)
+#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_INFO(__ph2ccmd, __val) \
+ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+4, 0, 8, __val)
+
+void rtl8723be_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
+void rtl8723be_set_fw_ap_off_load_cmd(struct ieee80211_hw *hw,
+ u8 ap_offload_enable);
+void rtl8723be_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id,
+ u32 cmd_len, u8 *p_cmdbuffer);
+void rtl8723be_firmware_selfreset(struct ieee80211_hw *hw);
+void rtl8723be_set_fw_rsvdpagepkt(struct ieee80211_hw *hw,
+ bool dl_finished);
+void rtl8723be_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
+int rtl8723be_download_fw(struct ieee80211_hw *hw,
+ bool buse_wake_on_wlan_fw);
+void rtl8723be_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw,
+ u8 p2p_ps_state);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c
new file mode 100644
index 000000000000..0fdf0909321f
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c
@@ -0,0 +1,2523 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../efuse.h"
+#include "../base.h"
+#include "../regd.h"
+#include "../cam.h"
+#include "../ps.h"
+#include "../pci.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "dm.h"
+#include "../rtl8723com/dm_common.h"
+#include "fw.h"
+#include "../rtl8723com/fw_common.h"
+#include "led.h"
+#include "hw.h"
+#include "pwrseq.h"
+#include "../btcoexist/rtl_btc.h"
+
+#define LLT_CONFIG 5
+
+static void _rtl8723be_return_beacon_queue_skb(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[BEACON_QUEUE];
+
+ while (skb_queue_len(&ring->queue)) {
+ struct rtl_tx_desc *entry = &ring->desc[ring->idx];
+ struct sk_buff *skb = __skb_dequeue(&ring->queue);
+
+ pci_unmap_single(rtlpci->pdev,
+ rtlpriv->cfg->ops->get_desc(
+ (u8 *)entry, true, HW_DESC_TXBUFF_ADDR),
+ skb->len, PCI_DMA_TODEVICE);
+ kfree_skb(skb);
+ ring->idx = (ring->idx + 1) % ring->entries;
+ }
+}
+
+static void _rtl8723be_set_bcn_ctrl_reg(struct ieee80211_hw *hw,
+ u8 set_bits, u8 clear_bits)
+{
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpci->reg_bcn_ctrl_val |= set_bits;
+ rtlpci->reg_bcn_ctrl_val &= ~clear_bits;
+
+ rtl_write_byte(rtlpriv, REG_BCN_CTRL, (u8) rtlpci->reg_bcn_ctrl_val);
+}
+
+static void _rtl8723be_stop_tx_beacon(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 tmp1byte;
+
+ tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2);
+ rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte & (~BIT(6)));
+ rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0x64);
+ tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2);
+ tmp1byte &= ~(BIT(0));
+ rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte);
+}
+
+static void _rtl8723be_resume_tx_beacon(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 tmp1byte;
+
+ tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2);
+ rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte | BIT(6));
+ rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff);
+ tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2);
+ tmp1byte |= BIT(1);
+ rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte);
+}
+
+static void _rtl8723be_enable_bcn_sub_func(struct ieee80211_hw *hw)
+{
+ _rtl8723be_set_bcn_ctrl_reg(hw, 0, BIT(1));
+}
+
+static void _rtl8723be_disable_bcn_sub_func(struct ieee80211_hw *hw)
+{
+ _rtl8723be_set_bcn_ctrl_reg(hw, BIT(1), 0);
+}
+
+static void _rtl8723be_set_fw_clock_on(struct ieee80211_hw *hw, u8 rpwm_val,
+ bool need_turn_off_ckk)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ bool support_remote_wake_up;
+ u32 count = 0, isr_regaddr, content;
+ bool schedule_timer = need_turn_off_ckk;
+ rtlpriv->cfg->ops->get_hw_reg(hw, HAL_DEF_WOWLAN,
+ (u8 *)(&support_remote_wake_up));
+
+ if (!rtlhal->fw_ready)
+ return;
+ if (!rtlpriv->psc.fw_current_inpsmode)
+ return;
+
+ while (1) {
+ spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+ if (rtlhal->fw_clk_change_in_progress) {
+ while (rtlhal->fw_clk_change_in_progress) {
+ spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+ count++;
+ udelay(100);
+ if (count > 1000)
+ return;
+ spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+ }
+ spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+ } else {
+ rtlhal->fw_clk_change_in_progress = false;
+ spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+ break;
+ }
+ }
+ if (IS_IN_LOW_POWER_STATE_88E(rtlhal->fw_ps_state)) {
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_SET_RPWM,
+ &rpwm_val);
+ if (FW_PS_IS_ACK(rpwm_val)) {
+ isr_regaddr = REG_HISR;
+ content = rtl_read_dword(rtlpriv, isr_regaddr);
+ while (!(content & IMR_CPWM) && (count < 500)) {
+ udelay(50);
+ count++;
+ content = rtl_read_dword(rtlpriv, isr_regaddr);
+ }
+
+ if (content & IMR_CPWM) {
+ rtl_write_word(rtlpriv, isr_regaddr, 0x0100);
+ rtlhal->fw_ps_state = FW_PS_STATE_RF_ON_88E;
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ "Receive CPWM INT!!! Set "
+ "pHalData->FwPSState = %X\n",
+ rtlhal->fw_ps_state);
+ }
+ }
+ spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+ rtlhal->fw_clk_change_in_progress = false;
+ spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+ if (schedule_timer) {
+ mod_timer(&rtlpriv->works.fw_clockoff_timer,
+ jiffies + MSECS(10));
+ }
+ } else {
+ spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+ rtlhal->fw_clk_change_in_progress = false;
+ spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+ }
+}
+
+static void _rtl8723be_set_fw_clock_off(struct ieee80211_hw *hw, u8 rpwm_val)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl8192_tx_ring *ring;
+ enum rf_pwrstate rtstate;
+ bool schedule_timer = false;
+ u8 queue;
+
+ if (!rtlhal->fw_ready)
+ return;
+ if (!rtlpriv->psc.fw_current_inpsmode)
+ return;
+ if (!rtlhal->allow_sw_to_change_hwclc)
+ return;
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RF_STATE, (u8 *)(&rtstate));
+ if (rtstate == ERFOFF || rtlpriv->psc.inactive_pwrstate == ERFOFF)
+ return;
+
+ for (queue = 0; queue < RTL_PCI_MAX_TX_QUEUE_COUNT; queue++) {
+ ring = &rtlpci->tx_ring[queue];
+ if (skb_queue_len(&ring->queue)) {
+ schedule_timer = true;
+ break;
+ }
+ }
+ if (schedule_timer) {
+ mod_timer(&rtlpriv->works.fw_clockoff_timer,
+ jiffies + MSECS(10));
+ return;
+ }
+ if (FW_PS_STATE(rtlhal->fw_ps_state) !=
+ FW_PS_STATE_RF_OFF_LOW_PWR_88E) {
+ spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+ if (!rtlhal->fw_clk_change_in_progress) {
+ rtlhal->fw_clk_change_in_progress = true;
+ spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+ rtlhal->fw_ps_state = FW_PS_STATE(rpwm_val);
+ rtl_write_word(rtlpriv, REG_HISR, 0x0100);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
+ &rpwm_val);
+ spin_lock_bh(&rtlpriv->locks.fw_ps_lock);
+ rtlhal->fw_clk_change_in_progress = false;
+ spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+ } else {
+ spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+ mod_timer(&rtlpriv->works.fw_clockoff_timer,
+ jiffies + MSECS(10));
+ }
+ }
+}
+
+static void _rtl8723be_set_fw_ps_rf_on(struct ieee80211_hw *hw)
+{
+ u8 rpwm_val = 0;
+ rpwm_val |= (FW_PS_STATE_RF_OFF_88E | FW_PS_ACK);
+ _rtl8723be_set_fw_clock_on(hw, rpwm_val, true);
+}
+
+static void _rtl8723be_fwlps_leave(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ bool fw_current_inps = false;
+ u8 rpwm_val = 0, fw_pwrmode = FW_PS_ACTIVE_MODE;
+
+ if (ppsc->low_power_enable) {
+ rpwm_val = (FW_PS_STATE_ALL_ON_88E | FW_PS_ACK);/* RF on */
+ _rtl8723be_set_fw_clock_on(hw, rpwm_val, false);
+ rtlhal->allow_sw_to_change_hwclc = false;
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
+ &fw_pwrmode);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
+ (u8 *)(&fw_current_inps));
+ } else {
+ rpwm_val = FW_PS_STATE_ALL_ON_88E; /* RF on */
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
+ &fw_pwrmode);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
+ (u8 *)(&fw_current_inps));
+ }
+}
+
+static void _rtl8723be_fwlps_enter(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ bool fw_current_inps = true;
+ u8 rpwm_val;
+
+ if (ppsc->low_power_enable) {
+ rpwm_val = FW_PS_STATE_RF_OFF_LOW_PWR_88E; /* RF off */
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
+ (u8 *)(&fw_current_inps));
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
+ &ppsc->fwctrl_psmode);
+ rtlhal->allow_sw_to_change_hwclc = true;
+ _rtl8723be_set_fw_clock_off(hw, rpwm_val);
+
+ } else {
+ rpwm_val = FW_PS_STATE_RF_OFF_88E; /* RF off */
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS,
+ (u8 *)(&fw_current_inps));
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE,
+ &ppsc->fwctrl_psmode);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val);
+ }
+}
+
+void rtl8723be_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ switch (variable) {
+ case HW_VAR_RCR:
+ *((u32 *)(val)) = rtlpci->receive_config;
+ break;
+ case HW_VAR_RF_STATE:
+ *((enum rf_pwrstate *)(val)) = ppsc->rfpwr_state;
+ break;
+ case HW_VAR_FWLPS_RF_ON: {
+ enum rf_pwrstate rfstate;
+ u32 val_rcr;
+
+ rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RF_STATE,
+ (u8 *)(&rfstate));
+ if (rfstate == ERFOFF) {
+ *((bool *)(val)) = true;
+ } else {
+ val_rcr = rtl_read_dword(rtlpriv, REG_RCR);
+ val_rcr &= 0x00070000;
+ if (val_rcr)
+ *((bool *)(val)) = false;
+ else
+ *((bool *)(val)) = true;
+ }
+ break; }
+ case HW_VAR_FW_PSMODE_STATUS:
+ *((bool *)(val)) = ppsc->fw_current_inpsmode;
+ break;
+ case HW_VAR_CORRECT_TSF: {
+ u64 tsf;
+ u32 *ptsf_low = (u32 *)&tsf;
+ u32 *ptsf_high = ((u32 *)&tsf) + 1;
+
+ *ptsf_high = rtl_read_dword(rtlpriv, (REG_TSFTR + 4));
+ *ptsf_low = rtl_read_dword(rtlpriv, REG_TSFTR);
+
+ *((u64 *)(val)) = tsf;
+
+ break; }
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process %x\n", variable);
+ break;
+ }
+}
+
+void rtl8723be_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ u8 idx;
+
+ switch (variable) {
+ case HW_VAR_ETHER_ADDR:
+ for (idx = 0; idx < ETH_ALEN; idx++)
+ rtl_write_byte(rtlpriv, (REG_MACID + idx), val[idx]);
+ break;
+ case HW_VAR_BASIC_RATE: {
+ u16 rate_cfg = ((u16 *)val)[0];
+ u8 rate_index = 0;
+ rate_cfg = rate_cfg & 0x15f;
+ rate_cfg |= 0x01;
+ rtl_write_byte(rtlpriv, REG_RRSR, rate_cfg & 0xff);
+ rtl_write_byte(rtlpriv, REG_RRSR + 1, (rate_cfg >> 8) & 0xff);
+ while (rate_cfg > 0x1) {
+ rate_cfg = (rate_cfg >> 1);
+ rate_index++;
+ }
+ rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, rate_index);
+ break; }
+ case HW_VAR_BSSID:
+ for (idx = 0; idx < ETH_ALEN; idx++)
+ rtl_write_byte(rtlpriv, (REG_BSSID + idx), val[idx]);
+ break;
+ case HW_VAR_SIFS:
+ rtl_write_byte(rtlpriv, REG_SIFS_CTX + 1, val[0]);
+ rtl_write_byte(rtlpriv, REG_SIFS_TRX + 1, val[1]);
+
+ rtl_write_byte(rtlpriv, REG_SPEC_SIFS + 1, val[0]);
+ rtl_write_byte(rtlpriv, REG_MAC_SPEC_SIFS + 1, val[0]);
+
+ if (!mac->ht_enable)
+ rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM, 0x0e0e);
+ else
+ rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM,
+ *((u16 *)val));
+ break;
+ case HW_VAR_SLOT_TIME: {
+ u8 e_aci;
+
+ RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+ "HW_VAR_SLOT_TIME %x\n", val[0]);
+
+ rtl_write_byte(rtlpriv, REG_SLOT, val[0]);
+
+ for (e_aci = 0; e_aci < AC_MAX; e_aci++) {
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM,
+ &e_aci);
+ }
+ break; }
+ case HW_VAR_ACK_PREAMBLE: {
+ u8 reg_tmp;
+ u8 short_preamble = (bool)*val;
+ reg_tmp = rtl_read_byte(rtlpriv, REG_TRXPTCL_CTL + 2);
+ if (short_preamble) {
+ reg_tmp |= 0x02;
+ rtl_write_byte(rtlpriv, REG_TRXPTCL_CTL + 2, reg_tmp);
+ } else {
+ reg_tmp &= 0xFD;
+ rtl_write_byte(rtlpriv, REG_TRXPTCL_CTL + 2, reg_tmp);
+ }
+ break; }
+ case HW_VAR_WPA_CONFIG:
+ rtl_write_byte(rtlpriv, REG_SECCFG, *val);
+ break;
+ case HW_VAR_AMPDU_MIN_SPACE: {
+ u8 min_spacing_to_set;
+ u8 sec_min_space;
+
+ min_spacing_to_set = *val;
+ if (min_spacing_to_set <= 7) {
+ sec_min_space = 0;
+
+ if (min_spacing_to_set < sec_min_space)
+ min_spacing_to_set = sec_min_space;
+
+ mac->min_space_cfg = ((mac->min_space_cfg & 0xf8) |
+ min_spacing_to_set);
+
+ *val = min_spacing_to_set;
+
+ RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+ "Set HW_VAR_AMPDU_MIN_SPACE: %#x\n",
+ mac->min_space_cfg);
+
+ rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE,
+ mac->min_space_cfg);
+ }
+ break; }
+ case HW_VAR_SHORTGI_DENSITY: {
+ u8 density_to_set;
+
+ density_to_set = *val;
+ mac->min_space_cfg |= (density_to_set << 3);
+
+ RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+ "Set HW_VAR_SHORTGI_DENSITY: %#x\n",
+ mac->min_space_cfg);
+
+ rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE,
+ mac->min_space_cfg);
+ break; }
+ case HW_VAR_AMPDU_FACTOR: {
+ u8 regtoset_normal[4] = {0x41, 0xa8, 0x72, 0xb9};
+ u8 factor_toset;
+ u8 *p_regtoset = NULL;
+ u8 index = 0;
+
+ p_regtoset = regtoset_normal;
+
+ factor_toset = *val;
+ if (factor_toset <= 3) {
+ factor_toset = (1 << (factor_toset + 2));
+ if (factor_toset > 0xf)
+ factor_toset = 0xf;
+
+ for (index = 0; index < 4; index++) {
+ if ((p_regtoset[index] & 0xf0) >
+ (factor_toset << 4))
+ p_regtoset[index] =
+ (p_regtoset[index] & 0x0f) |
+ (factor_toset << 4);
+
+ if ((p_regtoset[index] & 0x0f) > factor_toset)
+ p_regtoset[index] =
+ (p_regtoset[index] & 0xf0) |
+ (factor_toset);
+
+ rtl_write_byte(rtlpriv,
+ (REG_AGGLEN_LMT + index),
+ p_regtoset[index]);
+ }
+ RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD,
+ "Set HW_VAR_AMPDU_FACTOR: %#x\n",
+ factor_toset);
+ }
+ break; }
+ case HW_VAR_AC_PARAM: {
+ u8 e_aci = *val;
+ rtl8723_dm_init_edca_turbo(hw);
+
+ if (rtlpci->acm_method != EACMWAY2_SW)
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL,
+ &e_aci);
+ break; }
+ case HW_VAR_ACM_CTRL: {
+ u8 e_aci = *val;
+ union aci_aifsn *p_aci_aifsn =
+ (union aci_aifsn *)(&(mac->ac[0].aifs));
+ u8 acm = p_aci_aifsn->f.acm;
+ u8 acm_ctrl = rtl_read_byte(rtlpriv, REG_ACMHWCTRL);
+
+ acm_ctrl =
+ acm_ctrl | ((rtlpci->acm_method == 2) ? 0x0 : 0x1);
+
+ if (acm) {
+ switch (e_aci) {
+ case AC0_BE:
+ acm_ctrl |= ACMHW_BEQEN;
+ break;
+ case AC2_VI:
+ acm_ctrl |= ACMHW_VIQEN;
+ break;
+ case AC3_VO:
+ acm_ctrl |= ACMHW_VOQEN;
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ "HW_VAR_ACM_CTRL acm set "
+ "failed: eACI is %d\n", acm);
+ break;
+ }
+ } else {
+ switch (e_aci) {
+ case AC0_BE:
+ acm_ctrl &= (~ACMHW_BEQEN);
+ break;
+ case AC2_VI:
+ acm_ctrl &= (~ACMHW_VIQEN);
+ break;
+ case AC3_VO:
+ acm_ctrl &= (~ACMHW_BEQEN);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process\n");
+ break;
+ }
+ }
+ RT_TRACE(rtlpriv, COMP_QOS, DBG_TRACE,
+ "SetHwReg8190pci(): [HW_VAR_ACM_CTRL] "
+ "Write 0x%X\n", acm_ctrl);
+ rtl_write_byte(rtlpriv, REG_ACMHWCTRL, acm_ctrl);
+ break; }
+ case HW_VAR_RCR:
+ rtl_write_dword(rtlpriv, REG_RCR, ((u32 *)(val))[0]);
+ rtlpci->receive_config = ((u32 *)(val))[0];
+ break;
+ case HW_VAR_RETRY_LIMIT: {
+ u8 retry_limit = *val;
+
+ rtl_write_word(rtlpriv, REG_RL,
+ retry_limit << RETRY_LIMIT_SHORT_SHIFT |
+ retry_limit << RETRY_LIMIT_LONG_SHIFT);
+ break; }
+ case HW_VAR_DUAL_TSF_RST:
+ rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, (BIT(0) | BIT(1)));
+ break;
+ case HW_VAR_EFUSE_BYTES:
+ rtlefuse->efuse_usedbytes = *((u16 *)val);
+ break;
+ case HW_VAR_EFUSE_USAGE:
+ rtlefuse->efuse_usedpercentage = *val;
+ break;
+ case HW_VAR_IO_CMD:
+ rtl8723be_phy_set_io_cmd(hw, (*(enum io_type *)val));
+ break;
+ case HW_VAR_SET_RPWM: {
+ u8 rpwm_val;
+
+ rpwm_val = rtl_read_byte(rtlpriv, REG_PCIE_HRPWM);
+ udelay(1);
+
+ if (rpwm_val & BIT(7)) {
+ rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val);
+ } else {
+ rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val | BIT(7));
+ }
+ break; }
+ case HW_VAR_H2C_FW_PWRMODE:
+ rtl8723be_set_fw_pwrmode_cmd(hw, *val);
+ break;
+ case HW_VAR_FW_PSMODE_STATUS:
+ ppsc->fw_current_inpsmode = *((bool *)val);
+ break;
+ case HW_VAR_RESUME_CLK_ON:
+ _rtl8723be_set_fw_ps_rf_on(hw);
+ break;
+ case HW_VAR_FW_LPS_ACTION: {
+ bool enter_fwlps = *((bool *)val);
+
+ if (enter_fwlps)
+ _rtl8723be_fwlps_enter(hw);
+ else
+ _rtl8723be_fwlps_leave(hw);
+
+ break; }
+ case HW_VAR_H2C_FW_JOINBSSRPT: {
+ u8 mstatus = *val;
+ u8 tmp_regcr, tmp_reg422, bcnvalid_reg;
+ u8 count = 0, dlbcn_count = 0;
+ bool recover = false;
+
+ if (mstatus == RT_MEDIA_CONNECT) {
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AID, NULL);
+
+ tmp_regcr = rtl_read_byte(rtlpriv, REG_CR + 1);
+ rtl_write_byte(rtlpriv, REG_CR + 1,
+ (tmp_regcr | BIT(0)));
+
+ _rtl8723be_set_bcn_ctrl_reg(hw, 0, BIT(3));
+ _rtl8723be_set_bcn_ctrl_reg(hw, BIT(4), 0);
+
+ tmp_reg422 = rtl_read_byte(rtlpriv,
+ REG_FWHW_TXQ_CTRL + 2);
+ rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2,
+ tmp_reg422 & (~BIT(6)));
+ if (tmp_reg422 & BIT(6))
+ recover = true;
+
+ do {
+ bcnvalid_reg = rtl_read_byte(rtlpriv,
+ REG_TDECTRL + 2);
+ rtl_write_byte(rtlpriv, REG_TDECTRL + 2,
+ (bcnvalid_reg | BIT(0)));
+ _rtl8723be_return_beacon_queue_skb(hw);
+
+ rtl8723be_set_fw_rsvdpagepkt(hw, 0);
+ bcnvalid_reg = rtl_read_byte(rtlpriv,
+ REG_TDECTRL + 2);
+ count = 0;
+ while (!(bcnvalid_reg & BIT(0)) && count < 20) {
+ count++;
+ udelay(10);
+ bcnvalid_reg = rtl_read_byte(rtlpriv,
+ REG_TDECTRL + 2);
+ }
+ dlbcn_count++;
+ } while (!(bcnvalid_reg & BIT(0)) && dlbcn_count < 5);
+
+ if (bcnvalid_reg & BIT(0))
+ rtl_write_byte(rtlpriv, REG_TDECTRL+2, BIT(0));
+
+ _rtl8723be_set_bcn_ctrl_reg(hw, BIT(3), 0);
+ _rtl8723be_set_bcn_ctrl_reg(hw, 0, BIT(4));
+
+ if (recover) {
+ rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2,
+ tmp_reg422);
+ }
+ rtl_write_byte(rtlpriv, REG_CR + 1,
+ (tmp_regcr & ~(BIT(0))));
+ }
+ rtl8723be_set_fw_joinbss_report_cmd(hw, *val);
+ break; }
+ case HW_VAR_H2C_FW_P2P_PS_OFFLOAD:
+ rtl8723be_set_p2p_ps_offload_cmd(hw, *val);
+ break;
+ case HW_VAR_AID: {
+ u16 u2btmp;
+ u2btmp = rtl_read_word(rtlpriv, REG_BCN_PSR_RPT);
+ u2btmp &= 0xC000;
+ rtl_write_word(rtlpriv, REG_BCN_PSR_RPT,
+ (u2btmp | mac->assoc_id));
+ break; }
+ case HW_VAR_CORRECT_TSF: {
+ u8 btype_ibss = *val;
+
+ if (btype_ibss)
+ _rtl8723be_stop_tx_beacon(hw);
+
+ _rtl8723be_set_bcn_ctrl_reg(hw, 0, BIT(3));
+
+ rtl_write_dword(rtlpriv, REG_TSFTR,
+ (u32) (mac->tsf & 0xffffffff));
+ rtl_write_dword(rtlpriv, REG_TSFTR + 4,
+ (u32) ((mac->tsf >> 32) & 0xffffffff));
+
+ _rtl8723be_set_bcn_ctrl_reg(hw, BIT(3), 0);
+
+ if (btype_ibss)
+ _rtl8723be_resume_tx_beacon(hw);
+ break; }
+ case HW_VAR_KEEP_ALIVE: {
+ u8 array[2];
+ array[0] = 0xff;
+ array[1] = *val;
+ rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_KEEP_ALIVE_CTRL,
+ 2, array);
+ break; }
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process %x\n",
+ variable);
+ break;
+ }
+}
+
+static bool _rtl8723be_llt_write(struct ieee80211_hw *hw, u32 address, u32 data)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ bool status = true;
+ int count = 0;
+ u32 value = _LLT_INIT_ADDR(address) | _LLT_INIT_DATA(data) |
+ _LLT_OP(_LLT_WRITE_ACCESS);
+
+ rtl_write_dword(rtlpriv, REG_LLT_INIT, value);
+
+ do {
+ value = rtl_read_dword(rtlpriv, REG_LLT_INIT);
+ if (_LLT_NO_ACTIVE == _LLT_OP_VALUE(value))
+ break;
+
+ if (count > POLLING_LLT_THRESHOLD) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "Failed to polling write LLT done at "
+ "address %d!\n", address);
+ status = false;
+ break;
+ }
+ } while (++count);
+
+ return status;
+}
+
+static bool _rtl8723be_llt_table_init(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ unsigned short i;
+ u8 txpktbuf_bndy;
+ u8 maxpage;
+ bool status;
+
+ maxpage = 255;
+ txpktbuf_bndy = 245;
+
+ rtl_write_dword(rtlpriv, REG_TRXFF_BNDY,
+ (0x27FF0000 | txpktbuf_bndy));
+ rtl_write_byte(rtlpriv, REG_TDECTRL + 1, txpktbuf_bndy);
+
+ rtl_write_byte(rtlpriv, REG_TXPKTBUF_BCNQ_BDNY, txpktbuf_bndy);
+ rtl_write_byte(rtlpriv, REG_TXPKTBUF_MGQ_BDNY, txpktbuf_bndy);
+
+ rtl_write_byte(rtlpriv, 0x45D, txpktbuf_bndy);
+ rtl_write_byte(rtlpriv, REG_PBP, 0x31);
+ rtl_write_byte(rtlpriv, REG_RX_DRVINFO_SZ, 0x4);
+
+ for (i = 0; i < (txpktbuf_bndy - 1); i++) {
+ status = _rtl8723be_llt_write(hw, i, i + 1);
+ if (!status)
+ return status;
+ }
+ status = _rtl8723be_llt_write(hw, (txpktbuf_bndy - 1), 0xFF);
+
+ if (!status)
+ return status;
+
+ for (i = txpktbuf_bndy; i < maxpage; i++) {
+ status = _rtl8723be_llt_write(hw, i, (i + 1));
+ if (!status)
+ return status;
+ }
+ status = _rtl8723be_llt_write(hw, maxpage, txpktbuf_bndy);
+ if (!status)
+ return status;
+
+ rtl_write_dword(rtlpriv, REG_RQPN, 0x80e40808);
+ rtl_write_byte(rtlpriv, REG_RQPN_NPQ, 0x00);
+
+ return true;
+}
+
+static void _rtl8723be_gen_refresh_led_state(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_led *pled0 = &(pcipriv->ledctl.sw_led0);
+
+ if (rtlpriv->rtlhal.up_first_time)
+ return;
+
+ if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS)
+ rtl8723be_sw_led_on(hw, pled0);
+ else if (ppsc->rfoff_reason == RF_CHANGE_BY_INIT)
+ rtl8723be_sw_led_on(hw, pled0);
+ else
+ rtl8723be_sw_led_off(hw, pled0);
+}
+
+static bool _rtl8723be_init_mac(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ unsigned char bytetmp;
+ unsigned short wordtmp;
+ u16 retry = 0;
+ bool mac_func_enable;
+
+ rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x00);
+
+ /*Auto Power Down to CHIP-off State*/
+ bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1) & (~BIT(7));
+ rtl_write_byte(rtlpriv, REG_APS_FSMCO + 1, bytetmp);
+
+ bytetmp = rtl_read_byte(rtlpriv, REG_CR);
+ if (bytetmp == 0xFF)
+ mac_func_enable = true;
+ else
+ mac_func_enable = false;
+
+ /* HW Power on sequence */
+ if (!rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK,
+ PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK,
+ RTL8723_NIC_ENABLE_FLOW)) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "init MAC Fail as power on failure\n");
+ return false;
+ }
+ bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO) | BIT(4);
+ rtl_write_byte(rtlpriv, REG_APS_FSMCO, bytetmp);
+
+ bytetmp = rtl_read_byte(rtlpriv, REG_CR);
+ bytetmp = 0xff;
+ rtl_write_byte(rtlpriv, REG_CR, bytetmp);
+ mdelay(2);
+
+ bytetmp = rtl_read_byte(rtlpriv, REG_HWSEQ_CTRL);
+ bytetmp |= 0x7f;
+ rtl_write_byte(rtlpriv, REG_HWSEQ_CTRL, bytetmp);
+ mdelay(2);
+
+ bytetmp = rtl_read_byte(rtlpriv, REG_SYS_CFG + 3);
+ if (bytetmp & BIT(0)) {
+ bytetmp = rtl_read_byte(rtlpriv, 0x7c);
+ bytetmp |= BIT(6);
+ rtl_write_byte(rtlpriv, 0x7c, bytetmp);
+ }
+ bytetmp = rtl_read_byte(rtlpriv, REG_SYS_CLKR);
+ bytetmp |= BIT(3);
+ rtl_write_byte(rtlpriv, REG_SYS_CLKR, bytetmp);
+ bytetmp = rtl_read_byte(rtlpriv, REG_GPIO_MUXCFG + 1);
+ bytetmp &= ~BIT(4);
+ rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG + 1, bytetmp);
+
+ bytetmp = rtl_read_byte(rtlpriv, REG_PCIE_CTRL_REG+3);
+ rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG+3, bytetmp | 0x77);
+
+ rtl_write_word(rtlpriv, REG_CR, 0x2ff);
+
+ if (!mac_func_enable) {
+ if (!_rtl8723be_llt_table_init(hw))
+ return false;
+ }
+ rtl_write_dword(rtlpriv, REG_HISR, 0xffffffff);
+ rtl_write_dword(rtlpriv, REG_HISRE, 0xffffffff);
+
+ /* Enable FW Beamformer Interrupt */
+ bytetmp = rtl_read_byte(rtlpriv, REG_FWIMR + 3);
+ rtl_write_byte(rtlpriv, REG_FWIMR + 3, bytetmp | BIT(6));
+
+ wordtmp = rtl_read_word(rtlpriv, REG_TRXDMA_CTRL);
+ wordtmp &= 0xf;
+ wordtmp |= 0xF5B1;
+ rtl_write_word(rtlpriv, REG_TRXDMA_CTRL, wordtmp);
+
+ rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 1, 0x1F);
+ rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
+ rtl_write_word(rtlpriv, REG_RXFLTMAP2, 0xFFFF);
+ rtl_write_dword(rtlpriv, REG_TCR, rtlpci->transmit_config);
+
+ rtl_write_byte(rtlpriv, 0x4d0, 0x0);
+
+ rtl_write_dword(rtlpriv, REG_BCNQ_DESA,
+ ((u64) rtlpci->tx_ring[BEACON_QUEUE].dma) &
+ DMA_BIT_MASK(32));
+ rtl_write_dword(rtlpriv, REG_MGQ_DESA,
+ (u64) rtlpci->tx_ring[MGNT_QUEUE].dma &
+ DMA_BIT_MASK(32));
+ rtl_write_dword(rtlpriv, REG_VOQ_DESA,
+ (u64) rtlpci->tx_ring[VO_QUEUE].dma & DMA_BIT_MASK(32));
+ rtl_write_dword(rtlpriv, REG_VIQ_DESA,
+ (u64) rtlpci->tx_ring[VI_QUEUE].dma & DMA_BIT_MASK(32));
+ rtl_write_dword(rtlpriv, REG_BEQ_DESA,
+ (u64) rtlpci->tx_ring[BE_QUEUE].dma & DMA_BIT_MASK(32));
+ rtl_write_dword(rtlpriv, REG_BKQ_DESA,
+ (u64) rtlpci->tx_ring[BK_QUEUE].dma & DMA_BIT_MASK(32));
+ rtl_write_dword(rtlpriv, REG_HQ_DESA,
+ (u64) rtlpci->tx_ring[HIGH_QUEUE].dma &
+ DMA_BIT_MASK(32));
+ rtl_write_dword(rtlpriv, REG_RX_DESA,
+ (u64) rtlpci->rx_ring[RX_MPDU_QUEUE].dma &
+ DMA_BIT_MASK(32));
+
+ bytetmp = rtl_read_byte(rtlpriv, REG_PCIE_CTRL_REG + 3);
+ rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 3, bytetmp | 0x77);
+
+ rtl_write_dword(rtlpriv, REG_INT_MIG, 0);
+
+ bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL);
+ rtl_write_byte(rtlpriv, REG_APSD_CTRL, bytetmp & ~BIT(6));
+
+ rtl_write_byte(rtlpriv, REG_SECONDARY_CCA_CTRL, 0x3);
+
+ do {
+ retry++;
+ bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL);
+ } while ((retry < 200) && (bytetmp & BIT(7)));
+
+ _rtl8723be_gen_refresh_led_state(hw);
+
+ rtl_write_dword(rtlpriv, REG_MCUTST_1, 0x0);
+
+ bytetmp = rtl_read_byte(rtlpriv, REG_RXDMA_CONTROL);
+ rtl_write_byte(rtlpriv, REG_RXDMA_CONTROL, bytetmp & ~BIT(2));
+
+ return true;
+}
+
+static void _rtl8723be_hw_configure(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 reg_bw_opmode;
+ u32 reg_ratr, reg_prsr;
+
+ reg_bw_opmode = BW_OPMODE_20MHZ;
+ reg_ratr = RATE_ALL_CCK | RATE_ALL_OFDM_AG |
+ RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS;
+ reg_prsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+
+ rtl_write_dword(rtlpriv, REG_RRSR, reg_prsr);
+ rtl_write_byte(rtlpriv, REG_HWSEQ_CTRL, 0xFF);
+}
+
+static void _rtl8723be_enable_aspm_back_door(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+
+ rtl_write_byte(rtlpriv, 0x34b, 0x93);
+ rtl_write_word(rtlpriv, 0x350, 0x870c);
+ rtl_write_byte(rtlpriv, 0x352, 0x1);
+
+ if (ppsc->support_backdoor)
+ rtl_write_byte(rtlpriv, 0x349, 0x1b);
+ else
+ rtl_write_byte(rtlpriv, 0x349, 0x03);
+
+ rtl_write_word(rtlpriv, 0x350, 0x2718);
+ rtl_write_byte(rtlpriv, 0x352, 0x1);
+}
+
+void rtl8723be_enable_hw_security_config(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 sec_reg_value;
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+ "PairwiseEncAlgorithm = %d GroupEncAlgorithm = %d\n",
+ rtlpriv->sec.pairwise_enc_algorithm,
+ rtlpriv->sec.group_enc_algorithm);
+
+ if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) {
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+ "not open hw encryption\n");
+ return;
+ }
+ sec_reg_value = SCR_TXENCENABLE | SCR_RXDECENABLE;
+
+ if (rtlpriv->sec.use_defaultkey) {
+ sec_reg_value |= SCR_TXUSEDK;
+ sec_reg_value |= SCR_RXUSEDK;
+ }
+ sec_reg_value |= (SCR_RXBCUSEDK | SCR_TXBCUSEDK);
+
+ rtl_write_byte(rtlpriv, REG_CR + 1, 0x02);
+
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "The SECR-value %x\n",
+ sec_reg_value);
+
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value);
+}
+
+int rtl8723be_hw_init(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ bool rtstatus = true;
+ int err;
+ u8 tmp_u1b;
+ unsigned long flags;
+
+ /* reenable interrupts to not interfere with other devices */
+ local_save_flags(flags);
+ local_irq_enable();
+
+ rtlpriv->rtlhal.being_init_adapter = true;
+ rtlpriv->intf_ops->disable_aspm(hw);
+ rtstatus = _rtl8723be_init_mac(hw);
+ if (!rtstatus) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Init MAC failed\n");
+ err = 1;
+ goto exit;
+ }
+ tmp_u1b = rtl_read_byte(rtlpriv, REG_SYS_CFG);
+ tmp_u1b &= 0x7F;
+ rtl_write_byte(rtlpriv, REG_SYS_CFG, tmp_u1b);
+
+ err = rtl8723_download_fw(hw, true);
+ if (err) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ "Failed to download FW. Init HW without FW now..\n");
+ err = 1;
+ rtlhal->fw_ready = false;
+ goto exit;
+ } else {
+ rtlhal->fw_ready = true;
+ }
+ rtlhal->last_hmeboxnum = 0;
+ rtl8723be_phy_mac_config(hw);
+ /* because last function modify RCR, so we update
+ * rcr var here, or TP will unstable for receive_config
+ * is wrong, RX RCR_ACRC32 will cause TP unstabel & Rx
+ * RCR_APP_ICV will cause mac80211 unassoc for cisco 1252
+ */
+ rtlpci->receive_config = rtl_read_dword(rtlpriv, REG_RCR);
+ rtlpci->receive_config &= ~(RCR_ACRC32 | RCR_AICV);
+ rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
+
+ rtl8723be_phy_bb_config(hw);
+ rtlphy->rf_mode = RF_OP_BY_SW_3WIRE;
+ rtl8723be_phy_rf_config(hw);
+
+ rtlphy->rfreg_chnlval[0] = rtl_get_rfreg(hw, (enum radio_path)0,
+ RF_CHNLBW, RFREG_OFFSET_MASK);
+ rtlphy->rfreg_chnlval[1] = rtl_get_rfreg(hw, (enum radio_path)1,
+ RF_CHNLBW, RFREG_OFFSET_MASK);
+ rtlphy->rfreg_chnlval[0] &= 0xFFF03FF;
+ rtlphy->rfreg_chnlval[0] |= (BIT(10) | BIT(11));
+
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0x1);
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0x1);
+ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 1);
+ _rtl8723be_hw_configure(hw);
+ rtl_cam_reset_all_entry(hw);
+ rtl8723be_enable_hw_security_config(hw);
+
+ ppsc->rfpwr_state = ERFON;
+
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR, mac->mac_addr);
+ _rtl8723be_enable_aspm_back_door(hw);
+ rtlpriv->intf_ops->enable_aspm(hw);
+
+ rtl8723be_bt_hw_init(hw);
+
+ rtl_set_bbreg(hw, 0x64, BIT(20), 0);
+ rtl_set_bbreg(hw, 0x64, BIT(24), 0);
+
+ rtl_set_bbreg(hw, 0x40, BIT(4), 0);
+ rtl_set_bbreg(hw, 0x40, BIT(3), 1);
+
+ rtl_set_bbreg(hw, 0x944, BIT(0)|BIT(1), 0x3);
+ rtl_set_bbreg(hw, 0x930, 0xff, 0x77);
+
+ rtl_set_bbreg(hw, 0x38, BIT(11), 0x1);
+
+ rtl_set_bbreg(hw, 0xb2c, 0xffffffff, 0x80000000);
+
+ if (ppsc->rfpwr_state == ERFON) {
+ rtl8723be_dm_check_txpower_tracking(hw);
+ rtl8723be_phy_lc_calibrate(hw);
+ }
+ tmp_u1b = efuse_read_1byte(hw, 0x1FA);
+ if (!(tmp_u1b & BIT(0))) {
+ rtl_set_rfreg(hw, RF90_PATH_A, 0x15, 0x0F, 0x05);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "PA BIAS path A\n");
+ }
+ if (!(tmp_u1b & BIT(4))) {
+ tmp_u1b = rtl_read_byte(rtlpriv, 0x16);
+ tmp_u1b &= 0x0F;
+ rtl_write_byte(rtlpriv, 0x16, tmp_u1b | 0x80);
+ udelay(10);
+ rtl_write_byte(rtlpriv, 0x16, tmp_u1b | 0x90);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "under 1.5V\n");
+ }
+ rtl8723be_dm_init(hw);
+exit:
+ local_irq_restore(flags);
+ rtlpriv->rtlhal.being_init_adapter = false;
+ return err;
+}
+
+static enum version_8723e _rtl8723be_read_chip_version(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ enum version_8723e version = VERSION_UNKNOWN;
+ u8 count = 0;
+ u8 value8;
+ u32 value32;
+
+ rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0);
+
+ value8 = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 2);
+ rtl_write_byte(rtlpriv, REG_APS_FSMCO + 2, value8 | BIT(0));
+
+ value8 = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1);
+ rtl_write_byte(rtlpriv, REG_APS_FSMCO + 1, value8 | BIT(0));
+
+ value8 = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1);
+ while (((value8 & BIT(0))) && (count++ < 100)) {
+ udelay(10);
+ value8 = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1);
+ }
+ count = 0;
+ value8 = rtl_read_byte(rtlpriv, REG_ROM_VERSION);
+ while ((value8 == 0) && (count++ < 50)) {
+ value8 = rtl_read_byte(rtlpriv, REG_ROM_VERSION);
+ mdelay(1);
+ }
+ value32 = rtl_read_dword(rtlpriv, REG_SYS_CFG1);
+ if ((value32 & (CHIP_8723B)) != CHIP_8723B)
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "unkown chip version\n");
+ else
+ version = (enum version_8723e) VERSION_TEST_CHIP_1T1R_8723B;
+
+ rtlphy->rf_type = RF_1T1R;
+
+ value8 = rtl_read_byte(rtlpriv, REG_ROM_VERSION);
+ if (value8 >= 0x02)
+ version |= BIT(3);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Chip RF Type: %s\n", (rtlphy->rf_type == RF_2T2R) ?
+ "RF_2T2R" : "RF_1T1R");
+
+ return version;
+}
+
+static int _rtl8723be_set_media_status(struct ieee80211_hw *hw,
+ enum nl80211_iftype type)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 bt_msr = rtl_read_byte(rtlpriv, MSR) & 0xfc;
+ enum led_ctl_mode ledaction = LED_CTL_NO_LINK;
+
+ rtl_write_dword(rtlpriv, REG_BCN_CTRL, 0);
+ RT_TRACE(rtlpriv, COMP_BEACON, DBG_LOUD,
+ "clear 0x550 when set HW_VAR_MEDIA_STATUS\n");
+
+ if (type == NL80211_IFTYPE_UNSPECIFIED ||
+ type == NL80211_IFTYPE_STATION) {
+ _rtl8723be_stop_tx_beacon(hw);
+ _rtl8723be_enable_bcn_sub_func(hw);
+ } else if (type == NL80211_IFTYPE_ADHOC || type == NL80211_IFTYPE_AP) {
+ _rtl8723be_resume_tx_beacon(hw);
+ _rtl8723be_disable_bcn_sub_func(hw);
+ } else {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ "Set HW_VAR_MEDIA_STATUS: "
+ "No such media status(%x).\n", type);
+ }
+ switch (type) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ bt_msr |= MSR_NOLINK;
+ ledaction = LED_CTL_LINK;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "Set Network type to NO LINK!\n");
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ bt_msr |= MSR_ADHOC;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "Set Network type to Ad Hoc!\n");
+ break;
+ case NL80211_IFTYPE_STATION:
+ bt_msr |= MSR_INFRA;
+ ledaction = LED_CTL_LINK;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "Set Network type to STA!\n");
+ break;
+ case NL80211_IFTYPE_AP:
+ bt_msr |= MSR_AP;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "Set Network type to AP!\n");
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "Network type %d not support!\n", type);
+ return 1;
+ }
+ rtl_write_byte(rtlpriv, (MSR), bt_msr);
+ rtlpriv->cfg->ops->led_control(hw, ledaction);
+ if ((bt_msr & 0x03) == MSR_AP)
+ rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
+ else
+ rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
+ return 0;
+}
+
+void rtl8723be_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ u32 reg_rcr = rtlpci->receive_config;
+
+ if (rtlpriv->psc.rfpwr_state != ERFON)
+ return;
+
+ if (check_bssid) {
+ reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR,
+ (u8 *)(&reg_rcr));
+ _rtl8723be_set_bcn_ctrl_reg(hw, 0, BIT(4));
+ } else if (!check_bssid) {
+ reg_rcr &= (~(RCR_CBSSID_DATA | RCR_CBSSID_BCN));
+ _rtl8723be_set_bcn_ctrl_reg(hw, BIT(4), 0);
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR,
+ (u8 *)(&reg_rcr));
+ }
+}
+
+int rtl8723be_set_network_type(struct ieee80211_hw *hw,
+ enum nl80211_iftype type)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (_rtl8723be_set_media_status(hw, type))
+ return -EOPNOTSUPP;
+
+ if (rtlpriv->mac80211.link_state == MAC80211_LINKED) {
+ if (type != NL80211_IFTYPE_AP)
+ rtl8723be_set_check_bssid(hw, true);
+ } else {
+ rtl8723be_set_check_bssid(hw, false);
+ }
+ return 0;
+}
+
+/* don't set REG_EDCA_BE_PARAM here
+ * because mac80211 will send pkt when scan
+ */
+void rtl8723be_set_qos(struct ieee80211_hw *hw, int aci)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ rtl8723_dm_init_edca_turbo(hw);
+ switch (aci) {
+ case AC1_BK:
+ rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f);
+ break;
+ case AC0_BE:
+ break;
+ case AC2_VI:
+ rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, 0x5e4322);
+ break;
+ case AC3_VO:
+ rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, 0x2f3222);
+ break;
+ default:
+ RT_ASSERT(false, "invalid aci: %d !\n", aci);
+ break;
+ }
+}
+
+void rtl8723be_enable_interrupt(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF);
+ rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF);
+ rtlpci->irq_enabled = true;
+ /* there are some C2H CMDs have been sent
+ * before system interrupt is enabled, e.g., C2H, CPWM.
+ * So we need to clear all C2H events that FW has notified,
+ * otherwise FW won't schedule any commands anymore.
+ */
+ rtl_write_byte(rtlpriv, REG_C2HEVT_CLEAR, 0);
+ /*enable system interrupt*/
+ rtl_write_dword(rtlpriv, REG_HSIMR, rtlpci->sys_irq_mask & 0xFFFFFFFF);
+}
+
+void rtl8723be_disable_interrupt(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ rtl_write_dword(rtlpriv, REG_HIMR, IMR_DISABLED);
+ rtl_write_dword(rtlpriv, REG_HIMRE, IMR_DISABLED);
+ rtlpci->irq_enabled = false;
+ synchronize_irq(rtlpci->pdev->irq);
+}
+
+static void _rtl8723be_poweroff_adapter(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 u1b_tmp;
+
+ /* Combo (PCIe + USB) Card and PCIe-MF Card */
+ /* 1. Run LPS WL RFOFF flow */
+ rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,
+ PWR_INTF_PCI_MSK, RTL8723_NIC_LPS_ENTER_FLOW);
+
+ /* 2. 0x1F[7:0] = 0 */
+ /* turn off RF */
+ rtl_write_byte(rtlpriv, REG_RF_CTRL, 0x00);
+ if ((rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7)) &&
+ rtlhal->fw_ready)
+ rtl8723be_firmware_selfreset(hw);
+
+ /* Reset MCU. Suggested by Filen. */
+ u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, (u1b_tmp & (~BIT(2))));
+
+ /* g. MCUFWDL 0x80[1:0]= 0 */
+ /* reset MCU ready status */
+ rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00);
+
+ /* HW card disable configuration. */
+ rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,
+ PWR_INTF_PCI_MSK, RTL8723_NIC_DISABLE_FLOW);
+
+ /* Reset MCU IO Wrapper */
+ u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1);
+ rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, (u1b_tmp & (~BIT(0))));
+ u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1);
+ rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, u1b_tmp | BIT(0));
+
+ /* 7. RSV_CTRL 0x1C[7:0] = 0x0E */
+ /* lock ISO/CLK/Power control register */
+ rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x0e);
+}
+
+void rtl8723be_card_disable(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ enum nl80211_iftype opmode;
+
+ mac->link_state = MAC80211_NOLINK;
+ opmode = NL80211_IFTYPE_UNSPECIFIED;
+ _rtl8723be_set_media_status(hw, opmode);
+ if (rtlpriv->rtlhal.driver_is_goingto_unload ||
+ ppsc->rfoff_reason > RF_CHANGE_BY_PS)
+ rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF);
+ RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
+ _rtl8723be_poweroff_adapter(hw);
+
+ /* after power off we should do iqk again */
+ rtlpriv->phy.iqk_initialized = false;
+}
+
+void rtl8723be_interrupt_recognized(struct ieee80211_hw *hw,
+ u32 *p_inta, u32 *p_intb)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ *p_inta = rtl_read_dword(rtlpriv, ISR) & rtlpci->irq_mask[0];
+ rtl_write_dword(rtlpriv, ISR, *p_inta);
+
+ *p_intb = rtl_read_dword(rtlpriv, REG_HISRE) &
+ rtlpci->irq_mask[1];
+ rtl_write_dword(rtlpriv, REG_HISRE, *p_intb);
+}
+
+void rtl8723be_set_beacon_related_registers(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ u16 bcn_interval, atim_window;
+
+ bcn_interval = mac->beacon_interval;
+ atim_window = 2; /*FIX MERGE */
+ rtl8723be_disable_interrupt(hw);
+ rtl_write_word(rtlpriv, REG_ATIMWND, atim_window);
+ rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval);
+ rtl_write_word(rtlpriv, REG_BCNTCFG, 0x660f);
+ rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x18);
+ rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x18);
+ rtl_write_byte(rtlpriv, 0x606, 0x30);
+ rtl8723be_enable_interrupt(hw);
+}
+
+void rtl8723be_set_beacon_interval(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ u16 bcn_interval = mac->beacon_interval;
+
+ RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
+ "beacon_interval:%d\n", bcn_interval);
+ rtl8723be_disable_interrupt(hw);
+ rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval);
+ rtl8723be_enable_interrupt(hw);
+}
+
+void rtl8723be_update_interrupt_mask(struct ieee80211_hw *hw,
+ u32 add_msr, u32 rm_msr)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD,
+ "add_msr:%x, rm_msr:%x\n", add_msr, rm_msr);
+
+ if (add_msr)
+ rtlpci->irq_mask[0] |= add_msr;
+ if (rm_msr)
+ rtlpci->irq_mask[0] &= (~rm_msr);
+ rtl8723be_disable_interrupt(hw);
+ rtl8723be_enable_interrupt(hw);
+}
+
+static u8 _rtl8723be_get_chnl_group(u8 chnl)
+{
+ u8 group;
+
+ if (chnl < 3)
+ group = 0;
+ else if (chnl < 9)
+ group = 1;
+ else
+ group = 2;
+ return group;
+}
+
+static void _rtl8723be_read_power_value_fromprom(struct ieee80211_hw *hw,
+ struct txpower_info_2g *pw2g,
+ struct txpower_info_5g *pw5g,
+ bool autoload_fail, u8 *hwinfo)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 path, addr = EEPROM_TX_PWR_INX, group, cnt = 0;
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "hal_ReadPowerValueFromPROM8723BE(): "
+ "PROMContent[0x%x]= 0x%x\n",
+ (addr + 1), hwinfo[addr + 1]);
+ if (0xFF == hwinfo[addr + 1]) /*YJ, add, 120316*/
+ autoload_fail = true;
+
+ if (autoload_fail) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "auto load fail : Use Default value!\n");
+ for (path = 0; path < MAX_RF_PATH; path++) {
+ /* 2.4G default value */
+ for (group = 0; group < MAX_CHNL_GROUP_24G; group++) {
+ pw2g->index_cck_base[path][group] = 0x2D;
+ pw2g->index_bw40_base[path][group] = 0x2D;
+ }
+ for (cnt = 0; cnt < MAX_TX_COUNT; cnt++) {
+ if (cnt == 0) {
+ pw2g->bw20_diff[path][0] = 0x02;
+ pw2g->ofdm_diff[path][0] = 0x04;
+ } else {
+ pw2g->bw20_diff[path][cnt] = 0xFE;
+ pw2g->bw40_diff[path][cnt] = 0xFE;
+ pw2g->cck_diff[path][cnt] = 0xFE;
+ pw2g->ofdm_diff[path][cnt] = 0xFE;
+ }
+ }
+ }
+ return;
+ }
+ for (path = 0; path < MAX_RF_PATH; path++) {
+ /*2.4G default value*/
+ for (group = 0; group < MAX_CHNL_GROUP_24G; group++) {
+ pw2g->index_cck_base[path][group] = hwinfo[addr++];
+ if (pw2g->index_cck_base[path][group] == 0xFF)
+ pw2g->index_cck_base[path][group] = 0x2D;
+ }
+ for (group = 0; group < MAX_CHNL_GROUP_24G - 1; group++) {
+ pw2g->index_bw40_base[path][group] = hwinfo[addr++];
+ if (pw2g->index_bw40_base[path][group] == 0xFF)
+ pw2g->index_bw40_base[path][group] = 0x2D;
+ }
+ for (cnt = 0; cnt < MAX_TX_COUNT; cnt++) {
+ if (cnt == 0) {
+ pw2g->bw40_diff[path][cnt] = 0;
+ if (hwinfo[addr] == 0xFF) {
+ pw2g->bw20_diff[path][cnt] = 0x02;
+ } else {
+ pw2g->bw20_diff[path][cnt] =
+ (hwinfo[addr] & 0xf0) >> 4;
+ /*bit sign number to 8 bit sign number*/
+ if (pw2g->bw20_diff[path][cnt] & BIT(3))
+ pw2g->bw20_diff[path][cnt] |= 0xF0;
+ }
+ if (hwinfo[addr] == 0xFF) {
+ pw2g->ofdm_diff[path][cnt] = 0x04;
+ } else {
+ pw2g->ofdm_diff[path][cnt] =
+ (hwinfo[addr] & 0x0f);
+ /*bit sign number to 8 bit sign number*/
+ if (pw2g->ofdm_diff[path][cnt] & BIT(3))
+ pw2g->ofdm_diff[path][cnt] |=
+ 0xF0;
+ }
+ pw2g->cck_diff[path][cnt] = 0;
+ addr++;
+ } else {
+ if (hwinfo[addr] == 0xFF) {
+ pw2g->bw40_diff[path][cnt] = 0xFE;
+ } else {
+ pw2g->bw40_diff[path][cnt] =
+ (hwinfo[addr] & 0xf0) >> 4;
+ if (pw2g->bw40_diff[path][cnt] & BIT(3))
+ pw2g->bw40_diff[path][cnt] |=
+ 0xF0;
+ }
+ if (hwinfo[addr] == 0xFF) {
+ pw2g->bw20_diff[path][cnt] = 0xFE;
+ } else {
+ pw2g->bw20_diff[path][cnt] =
+ (hwinfo[addr] & 0x0f);
+ if (pw2g->bw20_diff[path][cnt] & BIT(3))
+ pw2g->bw20_diff[path][cnt] |=
+ 0xF0;
+ }
+ addr++;
+
+ if (hwinfo[addr] == 0xFF) {
+ pw2g->ofdm_diff[path][cnt] = 0xFE;
+ } else {
+ pw2g->ofdm_diff[path][cnt] =
+ (hwinfo[addr] & 0xf0) >> 4;
+ if (pw2g->ofdm_diff[path][cnt] & BIT(3))
+ pw2g->ofdm_diff[path][cnt] |=
+ 0xF0;
+ }
+ if (hwinfo[addr] == 0xFF) {
+ pw2g->cck_diff[path][cnt] = 0xFE;
+ } else {
+ pw2g->cck_diff[path][cnt] =
+ (hwinfo[addr] & 0x0f);
+ if (pw2g->cck_diff[path][cnt] & BIT(3))
+ pw2g->cck_diff[path][cnt] |=
+ 0xF0;
+ }
+ addr++;
+ }
+ }
+ /*5G default value*/
+ for (group = 0; group < MAX_CHNL_GROUP_5G; group++) {
+ pw5g->index_bw40_base[path][group] = hwinfo[addr++];
+ if (pw5g->index_bw40_base[path][group] == 0xFF)
+ pw5g->index_bw40_base[path][group] = 0xFE;
+ }
+ for (cnt = 0; cnt < MAX_TX_COUNT; cnt++) {
+ if (cnt == 0) {
+ pw5g->bw40_diff[path][cnt] = 0;
+
+ if (hwinfo[addr] == 0xFF) {
+ pw5g->bw20_diff[path][cnt] = 0;
+ } else {
+ pw5g->bw20_diff[path][0] =
+ (hwinfo[addr] & 0xf0) >> 4;
+ if (pw5g->bw20_diff[path][cnt] & BIT(3))
+ pw5g->bw20_diff[path][cnt] |=
+ 0xF0;
+ }
+ if (hwinfo[addr] == 0xFF) {
+ pw5g->ofdm_diff[path][cnt] = 0x04;
+ } else {
+ pw5g->ofdm_diff[path][0] =
+ (hwinfo[addr] & 0x0f);
+ if (pw5g->ofdm_diff[path][cnt] & BIT(3))
+ pw5g->ofdm_diff[path][cnt] |=
+ 0xF0;
+ }
+ addr++;
+ } else {
+ if (hwinfo[addr] == 0xFF) {
+ pw5g->bw40_diff[path][cnt] = 0xFE;
+ } else {
+ pw5g->bw40_diff[path][cnt] =
+ (hwinfo[addr] & 0xf0) >> 4;
+ if (pw5g->bw40_diff[path][cnt] & BIT(3))
+ pw5g->bw40_diff[path][cnt] |= 0xF0;
+ }
+ if (hwinfo[addr] == 0xFF) {
+ pw5g->bw20_diff[path][cnt] = 0xFE;
+ } else {
+ pw5g->bw20_diff[path][cnt] =
+ (hwinfo[addr] & 0x0f);
+ if (pw5g->bw20_diff[path][cnt] & BIT(3))
+ pw5g->bw20_diff[path][cnt] |= 0xF0;
+ }
+ addr++;
+ }
+ }
+ if (hwinfo[addr] == 0xFF) {
+ pw5g->ofdm_diff[path][1] = 0xFE;
+ pw5g->ofdm_diff[path][2] = 0xFE;
+ } else {
+ pw5g->ofdm_diff[path][1] = (hwinfo[addr] & 0xf0) >> 4;
+ pw5g->ofdm_diff[path][2] = (hwinfo[addr] & 0x0f);
+ }
+ addr++;
+
+ if (hwinfo[addr] == 0xFF)
+ pw5g->ofdm_diff[path][3] = 0xFE;
+ else
+ pw5g->ofdm_diff[path][3] = (hwinfo[addr] & 0x0f);
+ addr++;
+
+ for (cnt = 1; cnt < MAX_TX_COUNT; cnt++) {
+ if (pw5g->ofdm_diff[path][cnt] == 0xFF)
+ pw5g->ofdm_diff[path][cnt] = 0xFE;
+ else if (pw5g->ofdm_diff[path][cnt] & BIT(3))
+ pw5g->ofdm_diff[path][cnt] |= 0xF0;
+ }
+ }
+}
+
+static void _rtl8723be_read_txpower_info_from_hwpg(struct ieee80211_hw *hw,
+ bool autoload_fail,
+ u8 *hwinfo)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct txpower_info_2g pw2g;
+ struct txpower_info_5g pw5g;
+ u8 rf_path, index;
+ u8 i;
+
+ _rtl8723be_read_power_value_fromprom(hw, &pw2g, &pw5g, autoload_fail,
+ hwinfo);
+
+ for (rf_path = 0; rf_path < 2; rf_path++) {
+ for (i = 0; i < 14; i++) {
+ index = _rtl8723be_get_chnl_group(i+1);
+
+ rtlefuse->txpwrlevel_cck[rf_path][i] =
+ pw2g.index_cck_base[rf_path][index];
+ rtlefuse->txpwrlevel_ht40_1s[rf_path][i] =
+ pw2g.index_bw40_base[rf_path][index];
+ }
+ for (i = 0; i < MAX_TX_COUNT; i++) {
+ rtlefuse->txpwr_ht20diff[rf_path][i] =
+ pw2g.bw20_diff[rf_path][i];
+ rtlefuse->txpwr_ht40diff[rf_path][i] =
+ pw2g.bw40_diff[rf_path][i];
+ rtlefuse->txpwr_legacyhtdiff[rf_path][i] =
+ pw2g.ofdm_diff[rf_path][i];
+ }
+ for (i = 0; i < 14; i++) {
+ RTPRINT(rtlpriv, FINIT, INIT_EEPROM,
+ "RF(%d)-Ch(%d) [CCK / HT40_1S ] = "
+ "[0x%x / 0x%x ]\n", rf_path, i,
+ rtlefuse->txpwrlevel_cck[rf_path][i],
+ rtlefuse->txpwrlevel_ht40_1s[rf_path][i]);
+ }
+ }
+ if (!autoload_fail)
+ rtlefuse->eeprom_thermalmeter =
+ hwinfo[EEPROM_THERMAL_METER_88E];
+ else
+ rtlefuse->eeprom_thermalmeter = EEPROM_DEFAULT_THERMALMETER;
+
+ if (rtlefuse->eeprom_thermalmeter == 0xff || autoload_fail) {
+ rtlefuse->apk_thermalmeterignore = true;
+ rtlefuse->eeprom_thermalmeter = EEPROM_DEFAULT_THERMALMETER;
+ }
+ rtlefuse->thermalmeter[0] = rtlefuse->eeprom_thermalmeter;
+ RTPRINT(rtlpriv, FINIT, INIT_EEPROM,
+ "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter);
+
+ if (!autoload_fail) {
+ rtlefuse->eeprom_regulatory =
+ hwinfo[EEPROM_RF_BOARD_OPTION_88E] & 0x07;/*bit0~2*/
+ if (hwinfo[EEPROM_RF_BOARD_OPTION_88E] == 0xFF)
+ rtlefuse->eeprom_regulatory = 0;
+ } else {
+ rtlefuse->eeprom_regulatory = 0;
+ }
+ RTPRINT(rtlpriv, FINIT, INIT_EEPROM,
+ "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory);
+}
+
+static void _rtl8723be_read_adapter_info(struct ieee80211_hw *hw,
+ bool pseudo_test)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u16 i, usvalue;
+ u8 hwinfo[HWSET_MAX_SIZE];
+ u16 eeprom_id;
+ bool is_toshiba_smid1 = false;
+ bool is_toshiba_smid2 = false;
+ bool is_samsung_smid = false;
+ bool is_lenovo_smid = false;
+ u16 toshiba_smid1[] = {
+ 0x6151, 0x6152, 0x6154, 0x6155, 0x6177, 0x6178, 0x6179, 0x6180,
+ 0x7151, 0x7152, 0x7154, 0x7155, 0x7177, 0x7178, 0x7179, 0x7180,
+ 0x8151, 0x8152, 0x8154, 0x8155, 0x8181, 0x8182, 0x8184, 0x8185,
+ 0x9151, 0x9152, 0x9154, 0x9155, 0x9181, 0x9182, 0x9184, 0x9185
+ };
+ u16 toshiba_smid2[] = {
+ 0x6181, 0x6184, 0x6185, 0x7181, 0x7182, 0x7184, 0x7185, 0x8181,
+ 0x8182, 0x8184, 0x8185, 0x9181, 0x9182, 0x9184, 0x9185
+ };
+ u16 samsung_smid[] = {
+ 0x6191, 0x6192, 0x6193, 0x7191, 0x7192, 0x7193, 0x8191, 0x8192,
+ 0x8193, 0x9191, 0x9192, 0x9193
+ };
+ u16 lenovo_smid[] = {
+ 0x8195, 0x9195, 0x7194, 0x8200, 0x8201, 0x8202, 0x9199, 0x9200
+ };
+
+ if (pseudo_test) {
+ /* needs to be added */
+ return;
+ }
+ if (rtlefuse->epromtype == EEPROM_BOOT_EFUSE) {
+ rtl_efuse_shadow_map_update(hw);
+
+ memcpy(hwinfo, &rtlefuse->efuse_map[EFUSE_INIT_MAP][0],
+ HWSET_MAX_SIZE);
+ } else if (rtlefuse->epromtype == EEPROM_93C46) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "RTL819X Not boot from eeprom, check it !!");
+ }
+ RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG, ("MAP\n"),
+ hwinfo, HWSET_MAX_SIZE);
+
+ eeprom_id = *((u16 *)&hwinfo[0]);
+ if (eeprom_id != RTL8723BE_EEPROM_ID) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ "EEPROM ID(%#x) is invalid!!\n", eeprom_id);
+ rtlefuse->autoload_failflag = true;
+ } else {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n");
+ rtlefuse->autoload_failflag = false;
+ }
+ if (rtlefuse->autoload_failflag)
+ return;
+
+ rtlefuse->eeprom_vid = *(u16 *)&hwinfo[EEPROM_VID];
+ rtlefuse->eeprom_did = *(u16 *)&hwinfo[EEPROM_DID];
+ rtlefuse->eeprom_svid = *(u16 *)&hwinfo[EEPROM_SVID];
+ rtlefuse->eeprom_smid = *(u16 *)&hwinfo[EEPROM_SMID];
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "EEPROMId = 0x%4x\n", eeprom_id);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "EEPROM VID = 0x%4x\n", rtlefuse->eeprom_vid);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "EEPROM DID = 0x%4x\n", rtlefuse->eeprom_did);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "EEPROM SVID = 0x%4x\n", rtlefuse->eeprom_svid);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "EEPROM SMID = 0x%4x\n", rtlefuse->eeprom_smid);
+
+ for (i = 0; i < 6; i += 2) {
+ usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR + i];
+ *((u16 *)(&rtlefuse->dev_addr[i])) = usvalue;
+ }
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "dev_addr: %pM\n",
+ rtlefuse->dev_addr);
+
+ /*parse xtal*/
+ rtlefuse->crystalcap = hwinfo[EEPROM_XTAL_8723BE];
+ if (rtlefuse->crystalcap == 0xFF)
+ rtlefuse->crystalcap = 0x20;
+
+ _rtl8723be_read_txpower_info_from_hwpg(hw, rtlefuse->autoload_failflag,
+ hwinfo);
+
+ rtl8723be_read_bt_coexist_info_from_hwpg(hw,
+ rtlefuse->autoload_failflag,
+ hwinfo);
+
+ rtlefuse->eeprom_channelplan = hwinfo[EEPROM_CHANNELPLAN];
+ rtlefuse->eeprom_version = *(u16 *)&hwinfo[EEPROM_VERSION];
+ rtlefuse->txpwr_fromeprom = true;
+ rtlefuse->eeprom_oemid = hwinfo[EEPROM_CUSTOMER_ID];
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid);
+
+ /* set channel plan to world wide 13 */
+ rtlefuse->channel_plan = COUNTRY_CODE_WORLD_WIDE_13;
+
+ if (rtlhal->oem_id == RT_CID_DEFAULT) {
+ /* Does this one have a Toshiba SMID from group 1? */
+ for (i = 0; i < sizeof(toshiba_smid1) / sizeof(u16); i++) {
+ if (rtlefuse->eeprom_smid == toshiba_smid1[i]) {
+ is_toshiba_smid1 = true;
+ break;
+ }
+ }
+ /* Does this one have a Toshiba SMID from group 2? */
+ for (i = 0; i < sizeof(toshiba_smid2) / sizeof(u16); i++) {
+ if (rtlefuse->eeprom_smid == toshiba_smid2[i]) {
+ is_toshiba_smid2 = true;
+ break;
+ }
+ }
+ /* Does this one have a Samsung SMID? */
+ for (i = 0; i < sizeof(samsung_smid) / sizeof(u16); i++) {
+ if (rtlefuse->eeprom_smid == samsung_smid[i]) {
+ is_samsung_smid = true;
+ break;
+ }
+ }
+ /* Does this one have a Lenovo SMID? */
+ for (i = 0; i < sizeof(lenovo_smid) / sizeof(u16); i++) {
+ if (rtlefuse->eeprom_smid == lenovo_smid[i]) {
+ is_lenovo_smid = true;
+ break;
+ }
+ }
+ switch (rtlefuse->eeprom_oemid) {
+ case EEPROM_CID_DEFAULT:
+ if (rtlefuse->eeprom_did == 0x8176) {
+ if (rtlefuse->eeprom_svid == 0x10EC &&
+ is_toshiba_smid1) {
+ rtlhal->oem_id = RT_CID_TOSHIBA;
+ } else if (rtlefuse->eeprom_svid == 0x1025) {
+ rtlhal->oem_id = RT_CID_819X_ACER;
+ } else if (rtlefuse->eeprom_svid == 0x10EC &&
+ is_samsung_smid) {
+ rtlhal->oem_id = RT_CID_819X_SAMSUNG;
+ } else if (rtlefuse->eeprom_svid == 0x10EC &&
+ is_lenovo_smid) {
+ rtlhal->oem_id = RT_CID_819X_LENOVO;
+ } else if ((rtlefuse->eeprom_svid == 0x10EC &&
+ rtlefuse->eeprom_smid == 0x8197) ||
+ (rtlefuse->eeprom_svid == 0x10EC &&
+ rtlefuse->eeprom_smid == 0x9196)) {
+ rtlhal->oem_id = RT_CID_819X_CLEVO;
+ } else if ((rtlefuse->eeprom_svid == 0x1028 &&
+ rtlefuse->eeprom_smid == 0x8194) ||
+ (rtlefuse->eeprom_svid == 0x1028 &&
+ rtlefuse->eeprom_smid == 0x8198) ||
+ (rtlefuse->eeprom_svid == 0x1028 &&
+ rtlefuse->eeprom_smid == 0x9197) ||
+ (rtlefuse->eeprom_svid == 0x1028 &&
+ rtlefuse->eeprom_smid == 0x9198)) {
+ rtlhal->oem_id = RT_CID_819X_DELL;
+ } else if ((rtlefuse->eeprom_svid == 0x103C &&
+ rtlefuse->eeprom_smid == 0x1629)) {
+ rtlhal->oem_id = RT_CID_819X_HP;
+ } else if ((rtlefuse->eeprom_svid == 0x1A32 &&
+ rtlefuse->eeprom_smid == 0x2315)) {
+ rtlhal->oem_id = RT_CID_819X_QMI;
+ } else if ((rtlefuse->eeprom_svid == 0x10EC &&
+ rtlefuse->eeprom_smid == 0x8203)) {
+ rtlhal->oem_id = RT_CID_819X_PRONETS;
+ } else if ((rtlefuse->eeprom_svid == 0x1043 &&
+ rtlefuse->eeprom_smid == 0x84B5)) {
+ rtlhal->oem_id = RT_CID_819X_EDIMAX_ASUS;
+ } else {
+ rtlhal->oem_id = RT_CID_DEFAULT;
+ }
+ } else if (rtlefuse->eeprom_did == 0x8178) {
+ if (rtlefuse->eeprom_svid == 0x10EC &&
+ is_toshiba_smid2)
+ rtlhal->oem_id = RT_CID_TOSHIBA;
+ else if (rtlefuse->eeprom_svid == 0x1025)
+ rtlhal->oem_id = RT_CID_819X_ACER;
+ else if ((rtlefuse->eeprom_svid == 0x10EC &&
+ rtlefuse->eeprom_smid == 0x8186))
+ rtlhal->oem_id = RT_CID_819X_PRONETS;
+ else if ((rtlefuse->eeprom_svid == 0x1043 &&
+ rtlefuse->eeprom_smid == 0x84B6))
+ rtlhal->oem_id =
+ RT_CID_819X_EDIMAX_ASUS;
+ else
+ rtlhal->oem_id = RT_CID_DEFAULT;
+ } else {
+ rtlhal->oem_id = RT_CID_DEFAULT;
+ }
+ break;
+ case EEPROM_CID_TOSHIBA:
+ rtlhal->oem_id = RT_CID_TOSHIBA;
+ break;
+ case EEPROM_CID_CCX:
+ rtlhal->oem_id = RT_CID_CCX;
+ break;
+ case EEPROM_CID_QMI:
+ rtlhal->oem_id = RT_CID_819X_QMI;
+ break;
+ case EEPROM_CID_WHQL:
+ break;
+ default:
+ rtlhal->oem_id = RT_CID_DEFAULT;
+ break;
+ }
+ }
+}
+
+static void _rtl8723be_hal_customized_behavior(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+
+ pcipriv->ledctl.led_opendrain = true;
+ switch (rtlhal->oem_id) {
+ case RT_CID_819X_HP:
+ pcipriv->ledctl.led_opendrain = true;
+ break;
+ case RT_CID_819X_LENOVO:
+ case RT_CID_DEFAULT:
+ case RT_CID_TOSHIBA:
+ case RT_CID_CCX:
+ case RT_CID_819X_ACER:
+ case RT_CID_WHQL:
+ default:
+ break;
+ }
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+ "RT Customized ID: 0x%02X\n", rtlhal->oem_id);
+}
+
+void rtl8723be_read_eeprom_info(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 tmp_u1b;
+
+ rtlhal->version = _rtl8723be_read_chip_version(hw);
+ if (get_rf_type(rtlphy) == RF_1T1R)
+ rtlpriv->dm.rfpath_rxenable[0] = true;
+ else
+ rtlpriv->dm.rfpath_rxenable[0] =
+ rtlpriv->dm.rfpath_rxenable[1] = true;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "VersionID = 0x%4x\n",
+ rtlhal->version);
+ tmp_u1b = rtl_read_byte(rtlpriv, REG_9346CR);
+ if (tmp_u1b & BIT(4)) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EEPROM\n");
+ rtlefuse->epromtype = EEPROM_93C46;
+ } else {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EFUSE\n");
+ rtlefuse->epromtype = EEPROM_BOOT_EFUSE;
+ }
+ if (tmp_u1b & BIT(5)) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n");
+ rtlefuse->autoload_failflag = false;
+ _rtl8723be_read_adapter_info(hw, false);
+ } else {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Autoload ERR!!\n");
+ }
+ _rtl8723be_hal_customized_behavior(hw);
+}
+
+static void rtl8723be_update_hal_rate_table(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u32 ratr_value;
+ u8 ratr_index = 0;
+ u8 nmode = mac->ht_enable;
+ u8 mimo_ps = IEEE80211_SMPS_OFF;
+ u16 shortgi_rate;
+ u32 tmp_ratr_value;
+ u8 curtxbw_40mhz = mac->bw_40;
+ u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ?
+ 1 : 0;
+ u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ?
+ 1 : 0;
+ enum wireless_mode wirelessmode = mac->mode;
+
+ if (rtlhal->current_bandtype == BAND_ON_5G)
+ ratr_value = sta->supp_rates[1] << 4;
+ else
+ ratr_value = sta->supp_rates[0];
+ if (mac->opmode == NL80211_IFTYPE_ADHOC)
+ ratr_value = 0xfff;
+ ratr_value |= (sta->ht_cap.mcs.rx_mask[1] << 20 |
+ sta->ht_cap.mcs.rx_mask[0] << 12);
+ switch (wirelessmode) {
+ case WIRELESS_MODE_B:
+ if (ratr_value & 0x0000000c)
+ ratr_value &= 0x0000000d;
+ else
+ ratr_value &= 0x0000000f;
+ break;
+ case WIRELESS_MODE_G:
+ ratr_value &= 0x00000FF5;
+ break;
+ case WIRELESS_MODE_N_24G:
+ case WIRELESS_MODE_N_5G:
+ nmode = 1;
+ if (mimo_ps == IEEE80211_SMPS_STATIC) {
+ ratr_value &= 0x0007F005;
+ } else {
+ u32 ratr_mask;
+
+ if (get_rf_type(rtlphy) == RF_1T2R ||
+ get_rf_type(rtlphy) == RF_1T1R)
+ ratr_mask = 0x000ff005;
+ else
+ ratr_mask = 0x0f0ff005;
+ ratr_value &= ratr_mask;
+ }
+ break;
+ default:
+ if (rtlphy->rf_type == RF_1T2R)
+ ratr_value &= 0x000ff0ff;
+ else
+ ratr_value &= 0x0f0ff0ff;
+ break;
+ }
+ if ((rtlpriv->btcoexist.bt_coexistence) &&
+ (rtlpriv->btcoexist.bt_coexist_type == BT_CSR_BC4) &&
+ (rtlpriv->btcoexist.bt_cur_state) &&
+ (rtlpriv->btcoexist.bt_ant_isolation) &&
+ ((rtlpriv->btcoexist.bt_service == BT_SCO) ||
+ (rtlpriv->btcoexist.bt_service == BT_BUSY)))
+ ratr_value &= 0x0fffcfc0;
+ else
+ ratr_value &= 0x0FFFFFFF;
+
+ if (nmode && ((curtxbw_40mhz && curshortgi_40mhz) ||
+ (!curtxbw_40mhz && curshortgi_20mhz))) {
+ ratr_value |= 0x10000000;
+ tmp_ratr_value = (ratr_value >> 12);
+
+ for (shortgi_rate = 15; shortgi_rate > 0; shortgi_rate--) {
+ if ((1 << shortgi_rate) & tmp_ratr_value)
+ break;
+ }
+ shortgi_rate = (shortgi_rate << 12) | (shortgi_rate << 8) |
+ (shortgi_rate << 4) | (shortgi_rate);
+ }
+ rtl_write_dword(rtlpriv, REG_ARFR0 + ratr_index * 4, ratr_value);
+
+ RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG,
+ "%x\n", rtl_read_dword(rtlpriv, REG_ARFR0));
+}
+
+static u8 _rtl8723be_mrate_idx_to_arfr_id(struct ieee80211_hw *hw,
+ u8 rate_index)
+{
+ u8 ret = 0;
+
+ switch (rate_index) {
+ case RATR_INX_WIRELESS_NGB:
+ ret = 1;
+ break;
+ case RATR_INX_WIRELESS_N:
+ case RATR_INX_WIRELESS_NG:
+ ret = 5;
+ break;
+ case RATR_INX_WIRELESS_NB:
+ ret = 3;
+ break;
+ case RATR_INX_WIRELESS_GB:
+ ret = 6;
+ break;
+ case RATR_INX_WIRELESS_G:
+ ret = 7;
+ break;
+ case RATR_INX_WIRELESS_B:
+ ret = 8;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static void rtl8723be_update_hal_rate_mask(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ u8 rssi_level)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_sta_info *sta_entry = NULL;
+ u32 ratr_bitmap;
+ u8 ratr_index;
+ u8 curtxbw_40mhz = (sta->ht_cap.cap &
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1 : 0;
+ u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ?
+ 1 : 0;
+ u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ?
+ 1 : 0;
+ enum wireless_mode wirelessmode = 0;
+ bool shortgi = false;
+ u8 rate_mask[7];
+ u8 macid = 0;
+ u8 mimo_ps = IEEE80211_SMPS_OFF;
+
+ sta_entry = (struct rtl_sta_info *)sta->drv_priv;
+ wirelessmode = sta_entry->wireless_mode;
+ if (mac->opmode == NL80211_IFTYPE_STATION ||
+ mac->opmode == NL80211_IFTYPE_MESH_POINT)
+ curtxbw_40mhz = mac->bw_40;
+ else if (mac->opmode == NL80211_IFTYPE_AP ||
+ mac->opmode == NL80211_IFTYPE_ADHOC)
+ macid = sta->aid + 1;
+
+ ratr_bitmap = sta->supp_rates[0];
+
+ if (mac->opmode == NL80211_IFTYPE_ADHOC)
+ ratr_bitmap = 0xfff;
+
+ ratr_bitmap |= (sta->ht_cap.mcs.rx_mask[1] << 20 |
+ sta->ht_cap.mcs.rx_mask[0] << 12);
+ switch (wirelessmode) {
+ case WIRELESS_MODE_B:
+ ratr_index = RATR_INX_WIRELESS_B;
+ if (ratr_bitmap & 0x0000000c)
+ ratr_bitmap &= 0x0000000d;
+ else
+ ratr_bitmap &= 0x0000000f;
+ break;
+ case WIRELESS_MODE_G:
+ ratr_index = RATR_INX_WIRELESS_GB;
+
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x00000f00;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x00000ff0;
+ else
+ ratr_bitmap &= 0x00000ff5;
+ break;
+ case WIRELESS_MODE_A:
+ ratr_index = RATR_INX_WIRELESS_A;
+ ratr_bitmap &= 0x00000ff0;
+ break;
+ case WIRELESS_MODE_N_24G:
+ case WIRELESS_MODE_N_5G:
+ ratr_index = RATR_INX_WIRELESS_NGB;
+
+ if (mimo_ps == IEEE80211_SMPS_STATIC ||
+ mimo_ps == IEEE80211_SMPS_DYNAMIC) {
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x00070000;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x0007f000;
+ else
+ ratr_bitmap &= 0x0007f005;
+ } else {
+ if (rtlphy->rf_type == RF_1T1R) {
+ if (curtxbw_40mhz) {
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x000f0000;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x000ff000;
+ else
+ ratr_bitmap &= 0x000ff015;
+ } else {
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x000f0000;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x000ff000;
+ else
+ ratr_bitmap &= 0x000ff005;
+ }
+ } else {
+ if (curtxbw_40mhz) {
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x0f8f0000;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x0f8ff000;
+ else
+ ratr_bitmap &= 0x0f8ff015;
+ } else {
+ if (rssi_level == 1)
+ ratr_bitmap &= 0x0f8f0000;
+ else if (rssi_level == 2)
+ ratr_bitmap &= 0x0f8ff000;
+ else
+ ratr_bitmap &= 0x0f8ff005;
+ }
+ }
+ }
+ if ((curtxbw_40mhz && curshortgi_40mhz) ||
+ (!curtxbw_40mhz && curshortgi_20mhz)) {
+ if (macid == 0)
+ shortgi = true;
+ else if (macid == 1)
+ shortgi = false;
+ }
+ break;
+ default:
+ ratr_index = RATR_INX_WIRELESS_NGB;
+
+ if (rtlphy->rf_type == RF_1T2R)
+ ratr_bitmap &= 0x000ff0ff;
+ else
+ ratr_bitmap &= 0x0f0ff0ff;
+ break;
+ }
+ sta_entry->ratr_index = ratr_index;
+
+ RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG,
+ "ratr_bitmap :%x\n", ratr_bitmap);
+ *(u32 *)&rate_mask = (ratr_bitmap & 0x0fffffff) | (ratr_index << 28);
+ rate_mask[0] = macid;
+ rate_mask[1] = _rtl8723be_mrate_idx_to_arfr_id(hw, ratr_index) |
+ (shortgi ? 0x80 : 0x00);
+ rate_mask[2] = curtxbw_40mhz;
+ /* if (prox_priv->proxim_modeinfo->power_output > 0)
+ * rate_mask[2] |= BIT(6);
+ */
+
+ rate_mask[3] = (u8)(ratr_bitmap & 0x000000ff);
+ rate_mask[4] = (u8)((ratr_bitmap & 0x0000ff00) >> 8);
+ rate_mask[5] = (u8)((ratr_bitmap & 0x00ff0000) >> 16);
+ rate_mask[6] = (u8)((ratr_bitmap & 0xff000000) >> 24);
+
+ RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG,
+ "Rate_index:%x, ratr_val:%x, %x:%x:%x:%x:%x:%x:%x\n",
+ ratr_index, ratr_bitmap,
+ rate_mask[0], rate_mask[1],
+ rate_mask[2], rate_mask[3],
+ rate_mask[4], rate_mask[5],
+ rate_mask[6]);
+ rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_RA_MASK, 7, rate_mask);
+ _rtl8723be_set_bcn_ctrl_reg(hw, BIT(3), 0);
+}
+
+void rtl8723be_update_hal_rate_tbl(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ u8 rssi_level)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ if (rtlpriv->dm.useramask)
+ rtl8723be_update_hal_rate_mask(hw, sta, rssi_level);
+ else
+ rtl8723be_update_hal_rate_table(hw, sta);
+}
+
+void rtl8723be_update_channel_access_setting(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ u16 sifs_timer;
+
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, &mac->slot_time);
+ if (!mac->ht_enable)
+ sifs_timer = 0x0a0a;
+ else
+ sifs_timer = 0x0e0e;
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SIFS, (u8 *)&sifs_timer);
+}
+
+bool rtl8723be_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ enum rf_pwrstate e_rfpowerstate_toset, cur_rfstate;
+ u8 u1tmp;
+ bool actuallyset = false;
+
+ if (rtlpriv->rtlhal.being_init_adapter)
+ return false;
+
+ if (ppsc->swrf_processing)
+ return false;
+
+ spin_lock(&rtlpriv->locks.rf_ps_lock);
+ if (ppsc->rfchange_inprogress) {
+ spin_unlock(&rtlpriv->locks.rf_ps_lock);
+ return false;
+ } else {
+ ppsc->rfchange_inprogress = true;
+ spin_unlock(&rtlpriv->locks.rf_ps_lock);
+ }
+ cur_rfstate = ppsc->rfpwr_state;
+
+ rtl_write_byte(rtlpriv, REG_GPIO_IO_SEL_2,
+ rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL_2) & ~(BIT(1)));
+
+ u1tmp = rtl_read_byte(rtlpriv, REG_GPIO_PIN_CTRL_2);
+
+ if (rtlphy->polarity_ctl)
+ e_rfpowerstate_toset = (u1tmp & BIT(1)) ? ERFOFF : ERFON;
+ else
+ e_rfpowerstate_toset = (u1tmp & BIT(1)) ? ERFON : ERFOFF;
+
+ if (ppsc->hwradiooff &&
+ (e_rfpowerstate_toset == ERFON)) {
+ RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+ "GPIOChangeRF - HW Radio ON, RF ON\n");
+
+ e_rfpowerstate_toset = ERFON;
+ ppsc->hwradiooff = false;
+ actuallyset = true;
+ } else if (!ppsc->hwradiooff &&
+ (e_rfpowerstate_toset == ERFOFF)) {
+ RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+ "GPIOChangeRF - HW Radio OFF, RF OFF\n");
+
+ e_rfpowerstate_toset = ERFOFF;
+ ppsc->hwradiooff = true;
+ actuallyset = true;
+ }
+ if (actuallyset) {
+ spin_lock(&rtlpriv->locks.rf_ps_lock);
+ ppsc->rfchange_inprogress = false;
+ spin_unlock(&rtlpriv->locks.rf_ps_lock);
+ } else {
+ if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC)
+ RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
+
+ spin_lock(&rtlpriv->locks.rf_ps_lock);
+ ppsc->rfchange_inprogress = false;
+ spin_unlock(&rtlpriv->locks.rf_ps_lock);
+ }
+ *valid = 1;
+ return !ppsc->hwradiooff;
+}
+
+void rtl8723be_set_key(struct ieee80211_hw *hw, u32 key_index,
+ u8 *p_macaddr, bool is_group, u8 enc_algo,
+ bool is_wepkey, bool clear_all)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u8 *macaddr = p_macaddr;
+ u32 entry_id = 0;
+ bool is_pairwise = false;
+
+ static u8 cam_const_addr[4][6] = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}
+ };
+ static u8 cam_const_broad[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+ if (clear_all) {
+ u8 idx = 0;
+ u8 cam_offset = 0;
+ u8 clear_number = 5;
+
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "clear_all\n");
+
+ for (idx = 0; idx < clear_number; idx++) {
+ rtl_cam_mark_invalid(hw, cam_offset + idx);
+ rtl_cam_empty_entry(hw, cam_offset + idx);
+
+ if (idx < 5) {
+ memset(rtlpriv->sec.key_buf[idx], 0,
+ MAX_KEY_LEN);
+ rtlpriv->sec.key_len[idx] = 0;
+ }
+ }
+ } else {
+ switch (enc_algo) {
+ case WEP40_ENCRYPTION:
+ enc_algo = CAM_WEP40;
+ break;
+ case WEP104_ENCRYPTION:
+ enc_algo = CAM_WEP104;
+ break;
+ case TKIP_ENCRYPTION:
+ enc_algo = CAM_TKIP;
+ break;
+ case AESCCMP_ENCRYPTION:
+ enc_algo = CAM_AES;
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process\n");
+ enc_algo = CAM_TKIP;
+ break;
+ }
+
+ if (is_wepkey || rtlpriv->sec.use_defaultkey) {
+ macaddr = cam_const_addr[key_index];
+ entry_id = key_index;
+ } else {
+ if (is_group) {
+ macaddr = cam_const_broad;
+ entry_id = key_index;
+ } else {
+ if (mac->opmode == NL80211_IFTYPE_AP) {
+ entry_id = rtl_cam_get_free_entry(hw,
+ p_macaddr);
+ if (entry_id >= TOTAL_CAM_ENTRY) {
+ RT_TRACE(rtlpriv, COMP_SEC,
+ DBG_EMERG,
+ "Can not find free"
+ " hw security cam "
+ "entry\n");
+ return;
+ }
+ } else {
+ entry_id = CAM_PAIRWISE_KEY_POSITION;
+ }
+ key_index = PAIRWISE_KEYIDX;
+ is_pairwise = true;
+ }
+ }
+ if (rtlpriv->sec.key_len[key_index] == 0) {
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+ "delete one entry, entry_id is %d\n",
+ entry_id);
+ if (mac->opmode == NL80211_IFTYPE_AP)
+ rtl_cam_del_entry(hw, p_macaddr);
+ rtl_cam_delete_one_entry(hw, p_macaddr, entry_id);
+ } else {
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+ "add one entry\n");
+ if (is_pairwise) {
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+ "set Pairwise key\n");
+
+ rtl_cam_add_one_entry(hw, macaddr, key_index,
+ entry_id, enc_algo,
+ CAM_CONFIG_NO_USEDK,
+ rtlpriv->sec.key_buf[key_index]);
+ } else {
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
+ "set group key\n");
+
+ if (mac->opmode == NL80211_IFTYPE_ADHOC) {
+ rtl_cam_add_one_entry(hw,
+ rtlefuse->dev_addr,
+ PAIRWISE_KEYIDX,
+ CAM_PAIRWISE_KEY_POSITION,
+ enc_algo,
+ CAM_CONFIG_NO_USEDK,
+ rtlpriv->sec.key_buf
+ [entry_id]);
+ }
+ rtl_cam_add_one_entry(hw, macaddr, key_index,
+ entry_id, enc_algo,
+ CAM_CONFIG_NO_USEDK,
+ rtlpriv->sec.key_buf[entry_id]);
+ }
+ }
+ }
+}
+
+void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw,
+ bool auto_load_fail, u8 *hwinfo)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 value;
+ u32 tmpu_32;
+
+ if (!auto_load_fail) {
+ tmpu_32 = rtl_read_dword(rtlpriv, REG_MULTI_FUNC_CTRL);
+ if (tmpu_32 & BIT(18))
+ rtlpriv->btcoexist.btc_info.btcoexist = 1;
+ else
+ rtlpriv->btcoexist.btc_info.btcoexist = 0;
+ value = hwinfo[RF_OPTION4];
+ rtlpriv->btcoexist.btc_info.bt_type = BT_RTL8723B;
+ rtlpriv->btcoexist.btc_info.ant_num = (value & 0x1);
+ } else {
+ rtlpriv->btcoexist.btc_info.btcoexist = 0;
+ rtlpriv->btcoexist.btc_info.bt_type = BT_RTL8723B;
+ rtlpriv->btcoexist.btc_info.ant_num = ANT_X2;
+ }
+}
+
+void rtl8723be_bt_reg_init(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ /* 0:Low, 1:High, 2:From Efuse. */
+ rtlpriv->btcoexist.reg_bt_iso = 2;
+ /* 0:Idle, 1:None-SCO, 2:SCO, 3:From Counter. */
+ rtlpriv->btcoexist.reg_bt_sco = 3;
+ /* 0:Disable BT control A-MPDU, 1:Enable BT control A-MPDU. */
+ rtlpriv->btcoexist.reg_bt_sco = 0;
+}
+
+void rtl8723be_bt_hw_init(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (rtlpriv->cfg->ops->get_btc_status())
+ rtlpriv->btcoexist.btc_ops->btc_init_hw_config(rtlpriv);
+}
+
+void rtl8723be_suspend(struct ieee80211_hw *hw)
+{
+}
+
+void rtl8723be_resume(struct ieee80211_hw *hw)
+{
+}
+
+/* Turn on AAP (RCR:bit 0) for promicuous mode. */
+void rtl8723be_allow_all_destaddr(struct ieee80211_hw *hw, bool allow_all_da,
+ bool write_into_reg)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ if (allow_all_da) /* Set BIT0 */
+ rtlpci->receive_config |= RCR_AAP;
+ else /* Clear BIT0 */
+ rtlpci->receive_config &= ~RCR_AAP;
+
+ if (write_into_reg)
+ rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
+
+ RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
+ "receive_config = 0x%08X, write_into_reg =%d\n",
+ rtlpci->receive_config, write_into_reg);
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/hw.h b/drivers/net/wireless/rtlwifi/rtl8723be/hw.h
new file mode 100644
index 000000000000..b7449a9b57e4
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/hw.h
@@ -0,0 +1,64 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_HW_H__
+#define __RTL8723BE_HW_H__
+
+void rtl8723be_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val);
+void rtl8723be_read_eeprom_info(struct ieee80211_hw *hw);
+
+void rtl8723be_interrupt_recognized(struct ieee80211_hw *hw,
+ u32 *p_inta, u32 *p_intb);
+int rtl8723be_hw_init(struct ieee80211_hw *hw);
+void rtl8723be_card_disable(struct ieee80211_hw *hw);
+void rtl8723be_enable_interrupt(struct ieee80211_hw *hw);
+void rtl8723be_disable_interrupt(struct ieee80211_hw *hw);
+int rtl8723be_set_network_type(struct ieee80211_hw *hw,
+ enum nl80211_iftype type);
+void rtl8723be_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid);
+void rtl8723be_set_qos(struct ieee80211_hw *hw, int aci);
+void rtl8723be_set_beacon_related_registers(struct ieee80211_hw *hw);
+void rtl8723be_set_beacon_interval(struct ieee80211_hw *hw);
+void rtl8723be_update_interrupt_mask(struct ieee80211_hw *hw,
+ u32 add_msr, u32 rm_msr);
+void rtl8723be_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val);
+void rtl8723be_update_hal_rate_tbl(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ u8 rssi_level);
+void rtl8723be_update_channel_access_setting(struct ieee80211_hw *hw);
+bool rtl8723be_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid);
+void rtl8723be_enable_hw_security_config(struct ieee80211_hw *hw);
+void rtl8723be_set_key(struct ieee80211_hw *hw, u32 key_index,
+ u8 *p_macaddr, bool is_group, u8 enc_algo,
+ bool is_wepkey, bool clear_all);
+void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw,
+ bool autoload_fail, u8 *hwinfo);
+void rtl8723be_bt_reg_init(struct ieee80211_hw *hw);
+void rtl8723be_bt_hw_init(struct ieee80211_hw *hw);
+void rtl8723be_suspend(struct ieee80211_hw *hw);
+void rtl8723be_resume(struct ieee80211_hw *hw);
+void rtl8723be_allow_all_destaddr(struct ieee80211_hw *hw, bool allow_all_da,
+ bool write_into_reg);
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/led.c b/drivers/net/wireless/rtlwifi/rtl8723be/led.c
new file mode 100644
index 000000000000..cb931a38dc48
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/led.c
@@ -0,0 +1,153 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "reg.h"
+#include "led.h"
+
+static void _rtl8723be_init_led(struct ieee80211_hw *hw, struct rtl_led *pled,
+ enum rtl_led_pin ledpin)
+{
+ pled->hw = hw;
+ pled->ledpin = ledpin;
+ pled->ledon = false;
+}
+
+void rtl8723be_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled)
+{
+ u8 ledcfg;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD,
+ "LedAddr:%X ledpin =%d\n", REG_LEDCFG2, pled->ledpin);
+
+ switch (pled->ledpin) {
+ case LED_PIN_GPIO0:
+ break;
+ case LED_PIN_LED0:
+ ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2);
+ ledcfg &= ~BIT(6);
+ rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0xf0) | BIT(5));
+ break;
+ case LED_PIN_LED1:
+ ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG1);
+ rtl_write_byte(rtlpriv, REG_LEDCFG1, ledcfg & 0x10);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process\n");
+ break;
+ }
+ pled->ledon = true;
+}
+
+void rtl8723be_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ u8 ledcfg;
+
+ RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD,
+ "LedAddr:%X ledpin =%d\n", REG_LEDCFG2, pled->ledpin);
+
+ ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2);
+
+ switch (pled->ledpin) {
+ case LED_PIN_GPIO0:
+ break;
+ case LED_PIN_LED0:
+ ledcfg &= 0xf0;
+ if (pcipriv->ledctl.led_opendrain) {
+ ledcfg &= 0x90; /* Set to software control. */
+ rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg|BIT(3)));
+ ledcfg = rtl_read_byte(rtlpriv, REG_MAC_PINMUX_CFG);
+ ledcfg &= 0xFE;
+ rtl_write_byte(rtlpriv, REG_MAC_PINMUX_CFG, ledcfg);
+ } else {
+ ledcfg &= ~BIT(6);
+ rtl_write_byte(rtlpriv, REG_LEDCFG2,
+ (ledcfg | BIT(3) | BIT(5)));
+ }
+ break;
+ case LED_PIN_LED1:
+ ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG1);
+ ledcfg &= 0x10; /* Set to software control. */
+ rtl_write_byte(rtlpriv, REG_LEDCFG1, ledcfg|BIT(3));
+
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not processed\n");
+ break;
+ }
+ pled->ledon = false;
+}
+
+void rtl8723be_init_sw_leds(struct ieee80211_hw *hw)
+{
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ _rtl8723be_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0);
+ _rtl8723be_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1);
+}
+
+static void _rtl8723be_sw_led_control(struct ieee80211_hw *hw,
+ enum led_ctl_mode ledaction)
+{
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_led *pled0 = &(pcipriv->ledctl.sw_led0);
+ switch (ledaction) {
+ case LED_CTL_POWER_ON:
+ case LED_CTL_LINK:
+ case LED_CTL_NO_LINK:
+ rtl8723be_sw_led_on(hw, pled0);
+ break;
+ case LED_CTL_POWER_OFF:
+ rtl8723be_sw_led_off(hw, pled0);
+ break;
+ default:
+ break;
+ }
+}
+
+void rtl8723be_led_control(struct ieee80211_hw *hw,
+ enum led_ctl_mode ledaction)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+
+ if ((ppsc->rfoff_reason > RF_CHANGE_BY_PS) &&
+ (ledaction == LED_CTL_TX ||
+ ledaction == LED_CTL_RX ||
+ ledaction == LED_CTL_SITE_SURVEY ||
+ ledaction == LED_CTL_LINK ||
+ ledaction == LED_CTL_NO_LINK ||
+ ledaction == LED_CTL_START_TO_LINK ||
+ ledaction == LED_CTL_POWER_ON)) {
+ return;
+ }
+ RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "ledaction %d,\n", ledaction);
+ _rtl8723be_sw_led_control(hw, ledaction);
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/led.h b/drivers/net/wireless/rtlwifi/rtl8723be/led.h
new file mode 100644
index 000000000000..c57de379ee8d
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/led.h
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_LED_H__
+#define __RTL8723BE_LED_H__
+
+void rtl8723be_init_sw_leds(struct ieee80211_hw *hw);
+void rtl8723be_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled);
+void rtl8723be_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled);
+void rtl8723be_led_control(struct ieee80211_hw *hw,
+ enum led_ctl_mode ledaction);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/phy.c b/drivers/net/wireless/rtlwifi/rtl8723be/phy.c
new file mode 100644
index 000000000000..1575ef9ece9f
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/phy.c
@@ -0,0 +1,2156 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "../ps.h"
+#include "../core.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "../rtl8723com/phy_common.h"
+#include "rf.h"
+#include "dm.h"
+#include "table.h"
+#include "trx.h"
+
+static bool _rtl8723be_phy_bb8723b_config_parafile(struct ieee80211_hw *hw);
+static bool _rtl8723be_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw,
+ u8 configtype);
+static bool rtl8723be_phy_sw_chn_step_by_step(struct ieee80211_hw *hw,
+ u8 channel, u8 *stage,
+ u8 *step, u32 *delay);
+static bool _rtl8723be_check_condition(struct ieee80211_hw *hw,
+ const u32 condition)
+{
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u32 _board = rtlefuse->board_type; /*need efuse define*/
+ u32 _interface = rtlhal->interface;
+ u32 _platform = 0x08;/*SupportPlatform */
+ u32 cond = condition;
+
+ if (condition == 0xCDCDCDCD)
+ return true;
+
+ cond = condition & 0xFF;
+ if ((_board & cond) == 0 && cond != 0x1F)
+ return false;
+
+ cond = condition & 0xFF00;
+ cond = cond >> 8;
+ if ((_interface & cond) == 0 && cond != 0x07)
+ return false;
+
+ cond = condition & 0xFF0000;
+ cond = cond >> 16;
+ if ((_platform & cond) == 0 && cond != 0x0F)
+ return false;
+ return true;
+}
+
+static bool _rtl8723be_phy_config_mac_with_headerfile(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 i;
+ u32 arraylength;
+ u32 *ptrarray;
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Read rtl8723beMACPHY_Array\n");
+ arraylength = RTL8723BEMAC_1T_ARRAYLEN;
+ ptrarray = RTL8723BEMAC_1T_ARRAY;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Img:RTL8723bEMAC_1T_ARRAY LEN %d\n", arraylength);
+ for (i = 0; i < arraylength; i = i + 2)
+ rtl_write_byte(rtlpriv, ptrarray[i], (u8) ptrarray[i + 1]);
+ return true;
+}
+
+static bool _rtl8723be_phy_config_bb_with_headerfile(struct ieee80211_hw *hw,
+ u8 configtype)
+{
+ #define READ_NEXT_PAIR(v1, v2, i) \
+ do { \
+ i += 2; \
+ v1 = array_table[i];\
+ v2 = array_table[i+1]; \
+ } while (0)
+
+ int i;
+ u32 *array_table;
+ u16 arraylen;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 v1 = 0, v2 = 0;
+
+ if (configtype == BASEBAND_CONFIG_PHY_REG) {
+ arraylen = RTL8723BEPHY_REG_1TARRAYLEN;
+ array_table = RTL8723BEPHY_REG_1TARRAY;
+
+ for (i = 0; i < arraylen; i = i + 2) {
+ v1 = array_table[i];
+ v2 = array_table[i+1];
+ if (v1 < 0xcdcdcdcd) {
+ rtl_bb_delay(hw, v1, v2);
+ } else {/*This line is the start line of branch.*/
+ if (!_rtl8723be_check_condition(hw, array_table[i])) {
+ /*Discard the following (offset, data) pairs*/
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD &&
+ i < arraylen - 2) {
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ i -= 2; /* prevent from for-loop += 2*/
+ /* Configure matched pairs and
+ * skip to end of if-else.
+ */
+ } else {
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD &&
+ i < arraylen - 2) {
+ rtl_bb_delay(hw,
+ v1, v2);
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+
+ while (v2 != 0xDEAD && i < arraylen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ }
+ }
+ } else if (configtype == BASEBAND_CONFIG_AGC_TAB) {
+ arraylen = RTL8723BEAGCTAB_1TARRAYLEN;
+ array_table = RTL8723BEAGCTAB_1TARRAY;
+
+ for (i = 0; i < arraylen; i = i + 2) {
+ v1 = array_table[i];
+ v2 = array_table[i+1];
+ if (v1 < 0xCDCDCDCD) {
+ rtl_set_bbreg(hw, array_table[i],
+ MASKDWORD,
+ array_table[i + 1]);
+ udelay(1);
+ continue;
+ } else {/*This line is the start line of branch.*/
+ if (!_rtl8723be_check_condition(hw, array_table[i])) {
+ /* Discard the following
+ * (offset, data) pairs
+ */
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD &&
+ i < arraylen - 2) {
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ i -= 2; /* prevent from for-loop += 2*/
+ /*Configure matched pairs and
+ *skip to end of if-else.
+ */
+ } else {
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD &&
+ i < arraylen - 2) {
+ rtl_set_bbreg(hw, array_table[i],
+ MASKDWORD,
+ array_table[i + 1]);
+ udelay(1);
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+
+ while (v2 != 0xDEAD && i < arraylen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ }
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "The agctab_array_table[0] is "
+ "%x Rtl818EEPHY_REGArray[1] is %x\n",
+ array_table[i], array_table[i + 1]);
+ }
+ }
+ return true;
+}
+
+static u8 _rtl8723be_get_rate_section_index(u32 regaddr)
+{
+ u8 index = 0;
+
+ switch (regaddr) {
+ case RTXAGC_A_RATE18_06:
+ case RTXAGC_B_RATE18_06:
+ index = 0;
+ break;
+ case RTXAGC_A_RATE54_24:
+ case RTXAGC_B_RATE54_24:
+ index = 1;
+ break;
+ case RTXAGC_A_CCK1_MCS32:
+ case RTXAGC_B_CCK1_55_MCS32:
+ index = 2;
+ break;
+ case RTXAGC_B_CCK11_A_CCK2_11:
+ index = 3;
+ break;
+ case RTXAGC_A_MCS03_MCS00:
+ case RTXAGC_B_MCS03_MCS00:
+ index = 4;
+ break;
+ case RTXAGC_A_MCS07_MCS04:
+ case RTXAGC_B_MCS07_MCS04:
+ index = 5;
+ break;
+ case RTXAGC_A_MCS11_MCS08:
+ case RTXAGC_B_MCS11_MCS08:
+ index = 6;
+ break;
+ case RTXAGC_A_MCS15_MCS12:
+ case RTXAGC_B_MCS15_MCS12:
+ index = 7;
+ break;
+ default:
+ regaddr &= 0xFFF;
+ if (regaddr >= 0xC20 && regaddr <= 0xC4C)
+ index = (u8) ((regaddr - 0xC20) / 4);
+ else if (regaddr >= 0xE20 && regaddr <= 0xE4C)
+ index = (u8) ((regaddr - 0xE20) / 4);
+ break;
+ };
+ return index;
+}
+
+u32 rtl8723be_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath,
+ u32 regaddr, u32 bitmask)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 original_value, readback_value, bitshift;
+ unsigned long flags;
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ "regaddr(%#x), rfpath(%#x), bitmask(%#x)\n",
+ regaddr, rfpath, bitmask);
+
+ spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
+
+ original_value = rtl8723_phy_rf_serial_read(hw, rfpath, regaddr);
+ bitshift = rtl8723_phy_calculate_bit_shift(bitmask);
+ readback_value = (original_value & bitmask) >> bitshift;
+
+ spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags);
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ "regaddr(%#x), rfpath(%#x), "
+ "bitmask(%#x), original_value(%#x)\n",
+ regaddr, rfpath, bitmask, original_value);
+
+ return readback_value;
+}
+
+void rtl8723be_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path path,
+ u32 regaddr, u32 bitmask, u32 data)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 original_value, bitshift;
+ unsigned long flags;
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n",
+ regaddr, bitmask, data, path);
+
+ spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags);
+
+ if (bitmask != RFREG_OFFSET_MASK) {
+ original_value = rtl8723_phy_rf_serial_read(hw, path,
+ regaddr);
+ bitshift = rtl8723_phy_calculate_bit_shift(bitmask);
+ data = ((original_value & (~bitmask)) |
+ (data << bitshift));
+ }
+
+ rtl8723_phy_rf_serial_write(hw, path, regaddr, data);
+
+ spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags);
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n",
+ regaddr, bitmask, data, path);
+}
+
+bool rtl8723be_phy_mac_config(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ bool rtstatus = _rtl8723be_phy_config_mac_with_headerfile(hw);
+
+ rtl_write_byte(rtlpriv, 0x04CA, 0x0B);
+ return rtstatus;
+}
+
+bool rtl8723be_phy_bb_config(struct ieee80211_hw *hw)
+{
+ bool rtstatus = true;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u16 regval;
+ u8 reg_hwparafile = 1;
+ u32 tmp;
+ u8 crystalcap = rtlpriv->efuse.crystalcap;
+ rtl8723_phy_init_bb_rf_reg_def(hw);
+ regval = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN);
+ rtl_write_word(rtlpriv, REG_SYS_FUNC_EN,
+ regval | BIT(13) | BIT(0) | BIT(1));
+
+ rtl_write_byte(rtlpriv, REG_RF_CTRL, RF_EN | RF_RSTB | RF_SDMRSTB);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN,
+ FEN_PPLL | FEN_PCIEA | FEN_DIO_PCIE |
+ FEN_BB_GLB_RSTN | FEN_BBRSTB);
+ tmp = rtl_read_dword(rtlpriv, 0x4c);
+ rtl_write_dword(rtlpriv, 0x4c, tmp | BIT(23));
+
+ rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL + 1, 0x80);
+
+ if (reg_hwparafile == 1)
+ rtstatus = _rtl8723be_phy_bb8723b_config_parafile(hw);
+
+ crystalcap = crystalcap & 0x3F;
+ rtl_set_bbreg(hw, REG_MAC_PHY_CTRL, 0xFFF000,
+ (crystalcap | crystalcap << 6));
+
+ return rtstatus;
+}
+
+bool rtl8723be_phy_rf_config(struct ieee80211_hw *hw)
+{
+ return rtl8723be_phy_rf6052_config(hw);
+}
+
+static void _rtl8723be_config_rf_reg(struct ieee80211_hw *hw, u32 addr,
+ u32 data, enum radio_path rfpath,
+ u32 regaddr)
+{
+ if (addr == 0xfe || addr == 0xffe) {
+ mdelay(50);
+ } else {
+ rtl_set_rfreg(hw, rfpath, regaddr, RFREG_OFFSET_MASK, data);
+ udelay(1);
+ }
+}
+
+static void _rtl8723be_config_rf_radio_a(struct ieee80211_hw *hw,
+ u32 addr, u32 data)
+{
+ u32 content = 0x1000; /*RF Content: radio_a_txt*/
+ u32 maskforphyset = (u32)(content & 0xE000);
+
+ _rtl8723be_config_rf_reg(hw, addr, data, RF90_PATH_A,
+ addr | maskforphyset);
+}
+
+static void _rtl8723be_phy_init_tx_power_by_rate(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ u8 band, path, txnum, section;
+
+ for (band = BAND_ON_2_4G; band <= BAND_ON_5G; ++band)
+ for (path = 0; path < TX_PWR_BY_RATE_NUM_RF; ++path)
+ for (txnum = 0; txnum < TX_PWR_BY_RATE_NUM_RF; ++txnum)
+ for (section = 0;
+ section < TX_PWR_BY_RATE_NUM_SECTION;
+ ++section)
+ rtlphy->tx_power_by_rate_offset[band]
+ [path][txnum][section] = 0;
+}
+
+static void phy_set_txpwr_by_rate_base(struct ieee80211_hw *hw, u8 band,
+ u8 path, u8 rate_section,
+ u8 txnum, u8 value)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ if (path > RF90_PATH_D) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Invalid Rf Path %d in phy_SetTxPowerByRatBase()\n",
+ path);
+ return;
+ }
+
+ if (band == BAND_ON_2_4G) {
+ switch (rate_section) {
+ case CCK:
+ rtlphy->txpwr_by_rate_base_24g[path][txnum][0] = value;
+ break;
+ case OFDM:
+ rtlphy->txpwr_by_rate_base_24g[path][txnum][1] = value;
+ break;
+ case HT_MCS0_MCS7:
+ rtlphy->txpwr_by_rate_base_24g[path][txnum][2] = value;
+ break;
+ case HT_MCS8_MCS15:
+ rtlphy->txpwr_by_rate_base_24g[path][txnum][3] = value;
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Invalid RateSection %d in Band 2.4G, Rf Path"
+ " %d, %dTx in PHY_SetTxPowerByRateBase()\n",
+ rate_section, path, txnum);
+ break;
+ };
+ } else {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Invalid Band %d in PHY_SetTxPowerByRateBase()\n",
+ band);
+ }
+}
+
+static u8 phy_get_txpwr_by_rate_base(struct ieee80211_hw *hw, u8 band, u8 path,
+ u8 txnum, u8 rate_section)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u8 value = 0;
+ if (path > RF90_PATH_D) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Invalid Rf Path %d in PHY_GetTxPowerByRateBase()\n",
+ path);
+ return 0;
+ }
+
+ if (band == BAND_ON_2_4G) {
+ switch (rate_section) {
+ case CCK:
+ value = rtlphy->txpwr_by_rate_base_24g[path][txnum][0];
+ break;
+ case OFDM:
+ value = rtlphy->txpwr_by_rate_base_24g[path][txnum][1];
+ break;
+ case HT_MCS0_MCS7:
+ value = rtlphy->txpwr_by_rate_base_24g[path][txnum][2];
+ break;
+ case HT_MCS8_MCS15:
+ value = rtlphy->txpwr_by_rate_base_24g[path][txnum][3];
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Invalid RateSection %d in Band 2.4G, Rf Path"
+ " %d, %dTx in PHY_GetTxPowerByRateBase()\n",
+ rate_section, path, txnum);
+ break;
+ };
+ } else {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Invalid Band %d in PHY_GetTxPowerByRateBase()\n",
+ band);
+ }
+
+ return value;
+}
+
+static void _rtl8723be_phy_store_txpower_by_rate_base(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u16 raw_value = 0;
+ u8 base = 0, path = 0;
+
+ for (path = RF90_PATH_A; path <= RF90_PATH_B; ++path) {
+ if (path == RF90_PATH_A) {
+ raw_value = (u16) (rtlphy->tx_power_by_rate_offset
+ [BAND_ON_2_4G][path][RF_1TX][3] >> 24) & 0xFF;
+ base = (raw_value >> 4) * 10 + (raw_value & 0xF);
+ phy_set_txpwr_by_rate_base(hw, BAND_ON_2_4G, path, CCK,
+ RF_1TX, base);
+ } else if (path == RF90_PATH_B) {
+ raw_value = (u16) (rtlphy->tx_power_by_rate_offset
+ [BAND_ON_2_4G][path][RF_1TX][3] >> 0) & 0xFF;
+ base = (raw_value >> 4) * 10 + (raw_value & 0xF);
+ phy_set_txpwr_by_rate_base(hw, BAND_ON_2_4G, path,
+ CCK, RF_1TX, base);
+ }
+ raw_value = (u16) (rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G]
+ [path][RF_1TX][1] >> 24) & 0xFF;
+ base = (raw_value >> 4) * 10 + (raw_value & 0xF);
+ phy_set_txpwr_by_rate_base(hw, BAND_ON_2_4G, path, OFDM, RF_1TX,
+ base);
+
+ raw_value = (u16) (rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G]
+ [path][RF_1TX][5] >> 24) & 0xFF;
+ base = (raw_value >> 4) * 10 + (raw_value & 0xF);
+ phy_set_txpwr_by_rate_base(hw, BAND_ON_2_4G, path, HT_MCS0_MCS7,
+ RF_1TX, base);
+
+ raw_value = (u16) (rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G]
+ [path][RF_2TX][7] >> 24) & 0xFF;
+ base = (raw_value >> 4) * 10 + (raw_value & 0xF);
+ phy_set_txpwr_by_rate_base(hw, BAND_ON_2_4G, path,
+ HT_MCS8_MCS15, RF_2TX, base);
+ }
+}
+
+static void phy_conv_dbm_to_rel(u32 *data, u8 start, u8 end, u8 base_val)
+{
+ char i = 0;
+ u8 temp_value = 0;
+ u32 temp_data = 0;
+
+ for (i = 3; i >= 0; --i) {
+ if (i >= start && i <= end) {
+ /* Get the exact value */
+ temp_value = (u8) (*data >> (i * 8)) & 0xF;
+ temp_value += ((u8) ((*data >> (i*8 + 4)) & 0xF)) * 10;
+
+ /* Change the value to a relative value */
+ temp_value = (temp_value > base_val) ?
+ temp_value - base_val :
+ base_val - temp_value;
+ } else {
+ temp_value = (u8) (*data >> (i * 8)) & 0xFF;
+ }
+ temp_data <<= 8;
+ temp_data |= temp_value;
+ }
+ *data = temp_data;
+}
+
+static void conv_dbm_to_rel(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u8 base = 0, rfpath = RF90_PATH_A;
+
+ base = phy_get_txpwr_by_rate_base(hw, BAND_ON_2_4G, rfpath,
+ RF_1TX, CCK);
+ phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G]
+ [rfpath][RF_1TX][2]), 1, 1, base);
+ phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G]
+ [rfpath][RF_1TX][3]), 1, 3, base);
+
+ base = phy_get_txpwr_by_rate_base(hw, BAND_ON_2_4G, rfpath,
+ RF_1TX, OFDM);
+ phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G]
+ [rfpath][RF_1TX][0]), 0, 3, base);
+ phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G]
+ [rfpath][RF_1TX][1]), 0, 3, base);
+
+ base = phy_get_txpwr_by_rate_base(hw, BAND_ON_2_4G, rfpath,
+ RF_1TX, HT_MCS0_MCS7);
+ phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G]
+ [rfpath][RF_1TX][4]), 0, 3, base);
+ phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G]
+ [rfpath][RF_1TX][5]), 0, 3, base);
+
+ base = phy_get_txpwr_by_rate_base(hw, BAND_ON_2_4G, rfpath,
+ RF_2TX, HT_MCS8_MCS15);
+ phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G]
+ [rfpath][RF_2TX][6]), 0, 3, base);
+
+ phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G]
+ [rfpath][RF_2TX][7]), 0, 3, base);
+
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE,
+ "<=== conv_dbm_to_rel()\n");
+}
+
+static void _rtl8723be_phy_txpower_by_rate_configuration(
+ struct ieee80211_hw *hw)
+{
+ _rtl8723be_phy_store_txpower_by_rate_base(hw);
+ conv_dbm_to_rel(hw);
+}
+
+static bool _rtl8723be_phy_bb8723b_config_parafile(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ bool rtstatus;
+
+ rtstatus = _rtl8723be_phy_config_bb_with_headerfile(hw,
+ BASEBAND_CONFIG_PHY_REG);
+ if (!rtstatus) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Write BB Reg Fail!!");
+ return false;
+ }
+ _rtl8723be_phy_init_tx_power_by_rate(hw);
+ if (!rtlefuse->autoload_failflag) {
+ rtlphy->pwrgroup_cnt = 0;
+ rtstatus = _rtl8723be_phy_config_bb_with_pgheaderfile(hw,
+ BASEBAND_CONFIG_PHY_REG);
+ }
+ _rtl8723be_phy_txpower_by_rate_configuration(hw);
+ if (!rtstatus) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "BB_PG Reg Fail!!");
+ return false;
+ }
+ rtstatus = _rtl8723be_phy_config_bb_with_headerfile(hw,
+ BASEBAND_CONFIG_AGC_TAB);
+ if (!rtstatus) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "AGC Table Fail\n");
+ return false;
+ }
+ rtlphy->cck_high_power = (bool) (rtl_get_bbreg(hw,
+ RFPGA0_XA_HSSIPARAMETER2,
+ 0x200));
+ return true;
+}
+
+static void _rtl8723be_store_tx_power_by_rate(struct ieee80211_hw *hw,
+ u32 band, u32 rfpath,
+ u32 txnum, u32 regaddr,
+ u32 bitmask, u32 data)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u8 rate_section = _rtl8723be_get_rate_section_index(regaddr);
+
+ if (band != BAND_ON_2_4G && band != BAND_ON_5G) {
+ RT_TRACE(rtlpriv, COMP_POWER, PHY_TXPWR,
+ "Invalid Band %d\n", band);
+ return;
+ }
+
+ if (rfpath > TX_PWR_BY_RATE_NUM_RF) {
+ RT_TRACE(rtlpriv, COMP_POWER, PHY_TXPWR,
+ "Invalid RfPath %d\n", rfpath);
+ return;
+ }
+ if (txnum > TX_PWR_BY_RATE_NUM_RF) {
+ RT_TRACE(rtlpriv, COMP_POWER, PHY_TXPWR,
+ "Invalid TxNum %d\n", txnum);
+ return;
+ }
+ rtlphy->tx_power_by_rate_offset[band][rfpath][txnum][rate_section] =
+ data;
+}
+
+static bool _rtl8723be_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw,
+ u8 configtype)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ int i;
+ u32 *phy_regarray_table_pg;
+ u16 phy_regarray_pg_len;
+ u32 v1 = 0, v2 = 0, v3 = 0, v4 = 0, v5 = 0, v6 = 0;
+
+ phy_regarray_pg_len = RTL8723BEPHY_REG_ARRAY_PGLEN;
+ phy_regarray_table_pg = RTL8723BEPHY_REG_ARRAY_PG;
+
+ if (configtype == BASEBAND_CONFIG_PHY_REG) {
+ for (i = 0; i < phy_regarray_pg_len; i = i + 6) {
+ v1 = phy_regarray_table_pg[i];
+ v2 = phy_regarray_table_pg[i+1];
+ v3 = phy_regarray_table_pg[i+2];
+ v4 = phy_regarray_table_pg[i+3];
+ v5 = phy_regarray_table_pg[i+4];
+ v6 = phy_regarray_table_pg[i+5];
+
+ if (v1 < 0xcdcdcdcd) {
+ if (phy_regarray_table_pg[i] == 0xfe ||
+ phy_regarray_table_pg[i] == 0xffe)
+ mdelay(50);
+ else
+ _rtl8723be_store_tx_power_by_rate(hw,
+ v1, v2, v3, v4, v5, v6);
+ continue;
+ } else {
+ /*don't need the hw_body*/
+ if (!_rtl8723be_check_condition(hw,
+ phy_regarray_table_pg[i])) {
+ i += 2; /* skip the pair of expression*/
+ v1 = phy_regarray_table_pg[i];
+ v2 = phy_regarray_table_pg[i+1];
+ v3 = phy_regarray_table_pg[i+2];
+ while (v2 != 0xDEAD) {
+ i += 3;
+ v1 = phy_regarray_table_pg[i];
+ v2 = phy_regarray_table_pg[i+1];
+ v3 = phy_regarray_table_pg[i+2];
+ }
+ }
+ }
+ }
+ } else {
+ RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE,
+ "configtype != BaseBand_Config_PHY_REG\n");
+ }
+ return true;
+}
+
+bool rtl8723be_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
+ enum radio_path rfpath)
+{
+ #define READ_NEXT_RF_PAIR(v1, v2, i) \
+ do { \
+ i += 2; \
+ v1 = radioa_array_table[i]; \
+ v2 = radioa_array_table[i+1]; \
+ } while (0)
+
+ int i;
+ bool rtstatus = true;
+ u32 *radioa_array_table;
+ u16 radioa_arraylen;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u32 v1 = 0, v2 = 0;
+
+ radioa_arraylen = RTL8723BE_RADIOA_1TARRAYLEN;
+ radioa_array_table = RTL8723BE_RADIOA_1TARRAY;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Radio_A:RTL8723BE_RADIOA_1TARRAY %d\n", radioa_arraylen);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Radio No %x\n", rfpath);
+ rtstatus = true;
+ switch (rfpath) {
+ case RF90_PATH_A:
+ for (i = 0; i < radioa_arraylen; i = i + 2) {
+ v1 = radioa_array_table[i];
+ v2 = radioa_array_table[i+1];
+ if (v1 < 0xcdcdcdcd) {
+ _rtl8723be_config_rf_radio_a(hw, v1, v2);
+ } else { /*This line is the start line of branch.*/
+ if (!_rtl8723be_check_condition(hw,
+ radioa_array_table[i])) {
+ /* Discard the following
+ * (offset, data) pairs
+ */
+ READ_NEXT_RF_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD &&
+ i < radioa_arraylen - 2)
+ READ_NEXT_RF_PAIR(v1, v2, i);
+ i -= 2; /* prevent from for-loop += 2*/
+ } else {
+ /* Configure matched pairs
+ * and skip to end of if-else.
+ */
+ READ_NEXT_RF_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD &&
+ i < radioa_arraylen - 2) {
+ _rtl8723be_config_rf_radio_a(hw,
+ v1, v2);
+ READ_NEXT_RF_PAIR(v1, v2, i);
+ }
+
+ while (v2 != 0xDEAD &&
+ i < radioa_arraylen - 2) {
+ READ_NEXT_RF_PAIR(v1, v2, i);
+ }
+ }
+ }
+ }
+
+ if (rtlhal->oem_id == RT_CID_819X_HP)
+ _rtl8723be_config_rf_radio_a(hw, 0x52, 0x7E4BD);
+
+ break;
+ case RF90_PATH_B:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process\n");
+ break;
+ case RF90_PATH_C:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process\n");
+ break;
+ case RF90_PATH_D:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process\n");
+ break;
+ }
+ return true;
+}
+
+void rtl8723be_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ rtlphy->default_initialgain[0] =
+ (u8) rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0);
+ rtlphy->default_initialgain[1] =
+ (u8) rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0);
+ rtlphy->default_initialgain[2] =
+ (u8) rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, MASKBYTE0);
+ rtlphy->default_initialgain[3] =
+ (u8) rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, MASKBYTE0);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "Default initial gain (c50 = 0x%x, "
+ "c58 = 0x%x, c60 = 0x%x, c68 = 0x%x\n",
+ rtlphy->default_initialgain[0],
+ rtlphy->default_initialgain[1],
+ rtlphy->default_initialgain[2],
+ rtlphy->default_initialgain[3]);
+
+ rtlphy->framesync = (u8) rtl_get_bbreg(hw, ROFDM0_RXDETECTOR3,
+ MASKBYTE0);
+ rtlphy->framesync_c34 = rtl_get_bbreg(hw, ROFDM0_RXDETECTOR2,
+ MASKDWORD);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "Default framesync (0x%x) = 0x%x\n",
+ ROFDM0_RXDETECTOR3, rtlphy->framesync);
+}
+
+void rtl8723be_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u8 txpwr_level;
+ long txpwr_dbm;
+
+ txpwr_level = rtlphy->cur_cck_txpwridx;
+ txpwr_dbm = rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_B,
+ txpwr_level);
+ txpwr_level = rtlphy->cur_ofdm24g_txpwridx;
+ if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level) >
+ txpwr_dbm)
+ txpwr_dbm =
+ rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G,
+ txpwr_level);
+ txpwr_level = rtlphy->cur_ofdm24g_txpwridx;
+ if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G,
+ txpwr_level) > txpwr_dbm)
+ txpwr_dbm =
+ rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G,
+ txpwr_level);
+ *powerlevel = txpwr_dbm;
+}
+
+static u8 _rtl8723be_phy_get_ratesection_intxpower_byrate(enum radio_path path,
+ u8 rate)
+{
+ u8 rate_section = 0;
+
+ switch (rate) {
+ case DESC92C_RATE1M:
+ rate_section = 2;
+ break;
+ case DESC92C_RATE2M:
+ case DESC92C_RATE5_5M:
+ if (path == RF90_PATH_A)
+ rate_section = 3;
+ else if (path == RF90_PATH_B)
+ rate_section = 2;
+ break;
+ case DESC92C_RATE11M:
+ rate_section = 3;
+ break;
+ case DESC92C_RATE6M:
+ case DESC92C_RATE9M:
+ case DESC92C_RATE12M:
+ case DESC92C_RATE18M:
+ rate_section = 0;
+ break;
+ case DESC92C_RATE24M:
+ case DESC92C_RATE36M:
+ case DESC92C_RATE48M:
+ case DESC92C_RATE54M:
+ rate_section = 1;
+ break;
+ case DESC92C_RATEMCS0:
+ case DESC92C_RATEMCS1:
+ case DESC92C_RATEMCS2:
+ case DESC92C_RATEMCS3:
+ rate_section = 4;
+ break;
+ case DESC92C_RATEMCS4:
+ case DESC92C_RATEMCS5:
+ case DESC92C_RATEMCS6:
+ case DESC92C_RATEMCS7:
+ rate_section = 5;
+ break;
+ case DESC92C_RATEMCS8:
+ case DESC92C_RATEMCS9:
+ case DESC92C_RATEMCS10:
+ case DESC92C_RATEMCS11:
+ rate_section = 6;
+ break;
+ case DESC92C_RATEMCS12:
+ case DESC92C_RATEMCS13:
+ case DESC92C_RATEMCS14:
+ case DESC92C_RATEMCS15:
+ rate_section = 7;
+ break;
+ default:
+ RT_ASSERT(true, "Rate_Section is Illegal\n");
+ break;
+ }
+ return rate_section;
+}
+
+static u8 _rtl8723be_get_txpower_by_rate(struct ieee80211_hw *hw,
+ enum band_type band,
+ enum radio_path rfpath, u8 rate)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u8 shift = 0, rate_section, tx_num;
+ char tx_pwr_diff = 0;
+
+ rate_section = _rtl8723be_phy_get_ratesection_intxpower_byrate(rfpath,
+ rate);
+ tx_num = RF_TX_NUM_NONIMPLEMENT;
+
+ if (tx_num == RF_TX_NUM_NONIMPLEMENT) {
+ if (rate >= DESC92C_RATEMCS8 && rate <= DESC92C_RATEMCS15)
+ tx_num = RF_2TX;
+ else
+ tx_num = RF_1TX;
+ }
+
+ switch (rate) {
+ case DESC92C_RATE6M:
+ case DESC92C_RATE24M:
+ case DESC92C_RATEMCS0:
+ case DESC92C_RATEMCS4:
+ case DESC92C_RATEMCS8:
+ case DESC92C_RATEMCS12:
+ shift = 0;
+ break;
+ case DESC92C_RATE1M:
+ case DESC92C_RATE2M:
+ case DESC92C_RATE9M:
+ case DESC92C_RATE36M:
+ case DESC92C_RATEMCS1:
+ case DESC92C_RATEMCS5:
+ case DESC92C_RATEMCS9:
+ case DESC92C_RATEMCS13:
+ shift = 8;
+ break;
+ case DESC92C_RATE5_5M:
+ case DESC92C_RATE12M:
+ case DESC92C_RATE48M:
+ case DESC92C_RATEMCS2:
+ case DESC92C_RATEMCS6:
+ case DESC92C_RATEMCS10:
+ case DESC92C_RATEMCS14:
+ shift = 16;
+ break;
+ case DESC92C_RATE11M:
+ case DESC92C_RATE18M:
+ case DESC92C_RATE54M:
+ case DESC92C_RATEMCS3:
+ case DESC92C_RATEMCS7:
+ case DESC92C_RATEMCS11:
+ case DESC92C_RATEMCS15:
+ shift = 24;
+ break;
+ default:
+ RT_ASSERT(true, "Rate_Section is Illegal\n");
+ break;
+ }
+ tx_pwr_diff = (u8)(rtlphy->tx_power_by_rate_offset[band][rfpath][tx_num]
+ [rate_section] >> shift) & 0xff;
+
+ return tx_pwr_diff;
+}
+
+static u8 _rtl8723be_get_txpower_index(struct ieee80211_hw *hw, u8 path,
+ u8 rate, u8 bandwidth, u8 channel)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u8 index = (channel - 1);
+ u8 txpower;
+ u8 power_diff_byrate = 0;
+
+ if (channel > 14 || channel < 1) {
+ index = 0;
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ "Illegal channel!\n");
+ }
+ if (RTL8723E_RX_HAL_IS_CCK_RATE(rate))
+ txpower = rtlefuse->txpwrlevel_cck[path][index];
+ else if (DESC92C_RATE6M <= rate)
+ txpower = rtlefuse->txpwrlevel_ht40_1s[path][index];
+ else
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
+ "invalid rate\n");
+
+ if (DESC92C_RATE6M <= rate && rate <= DESC92C_RATE54M &&
+ !RTL8723E_RX_HAL_IS_CCK_RATE(rate))
+ txpower += rtlefuse->txpwr_legacyhtdiff[0][TX_1S];
+
+ if (bandwidth == HT_CHANNEL_WIDTH_20) {
+ if (DESC92C_RATEMCS0 <= rate && rate <= DESC92C_RATEMCS15)
+ txpower += rtlefuse->txpwr_ht20diff[0][TX_1S];
+ if (DESC92C_RATEMCS8 <= rate && rate <= DESC92C_RATEMCS15)
+ txpower += rtlefuse->txpwr_ht20diff[0][TX_2S];
+ } else if (bandwidth == HT_CHANNEL_WIDTH_20_40) {
+ if (DESC92C_RATEMCS0 <= rate && rate <= DESC92C_RATEMCS15)
+ txpower += rtlefuse->txpwr_ht40diff[0][TX_1S];
+ if (DESC92C_RATEMCS8 <= rate && rate <= DESC92C_RATEMCS15)
+ txpower += rtlefuse->txpwr_ht40diff[0][TX_2S];
+ }
+ if (rtlefuse->eeprom_regulatory != 2)
+ power_diff_byrate = _rtl8723be_get_txpower_by_rate(hw,
+ BAND_ON_2_4G,
+ path, rate);
+
+ txpower += power_diff_byrate;
+
+ if (txpower > MAX_POWER_INDEX)
+ txpower = MAX_POWER_INDEX;
+
+ return txpower;
+}
+
+static void _rtl8723be_phy_set_txpower_index(struct ieee80211_hw *hw,
+ u8 power_index, u8 path, u8 rate)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ if (path == RF90_PATH_A) {
+ switch (rate) {
+ case DESC92C_RATE1M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_CCK1_MCS32,
+ MASKBYTE1, power_index);
+ break;
+ case DESC92C_RATE2M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_B_CCK11_A_CCK2_11,
+ MASKBYTE1, power_index);
+ break;
+ case DESC92C_RATE5_5M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_B_CCK11_A_CCK2_11,
+ MASKBYTE2, power_index);
+ break;
+ case DESC92C_RATE11M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_B_CCK11_A_CCK2_11,
+ MASKBYTE3, power_index);
+ break;
+ case DESC92C_RATE6M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE18_06,
+ MASKBYTE0, power_index);
+ break;
+ case DESC92C_RATE9M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE18_06,
+ MASKBYTE1, power_index);
+ break;
+ case DESC92C_RATE12M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE18_06,
+ MASKBYTE2, power_index);
+ break;
+ case DESC92C_RATE18M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE18_06,
+ MASKBYTE3, power_index);
+ break;
+ case DESC92C_RATE24M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE54_24,
+ MASKBYTE0, power_index);
+ break;
+ case DESC92C_RATE36M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE54_24,
+ MASKBYTE1, power_index);
+ break;
+ case DESC92C_RATE48M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE54_24,
+ MASKBYTE2, power_index);
+ break;
+ case DESC92C_RATE54M:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE54_24,
+ MASKBYTE3, power_index);
+ break;
+ case DESC92C_RATEMCS0:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS03_MCS00,
+ MASKBYTE0, power_index);
+ break;
+ case DESC92C_RATEMCS1:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS03_MCS00,
+ MASKBYTE1, power_index);
+ break;
+ case DESC92C_RATEMCS2:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS03_MCS00,
+ MASKBYTE2, power_index);
+ break;
+ case DESC92C_RATEMCS3:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS03_MCS00,
+ MASKBYTE3, power_index);
+ break;
+ case DESC92C_RATEMCS4:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS07_MCS04,
+ MASKBYTE0, power_index);
+ break;
+ case DESC92C_RATEMCS5:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS07_MCS04,
+ MASKBYTE1, power_index);
+ break;
+ case DESC92C_RATEMCS6:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS07_MCS04,
+ MASKBYTE2, power_index);
+ break;
+ case DESC92C_RATEMCS7:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS07_MCS04,
+ MASKBYTE3, power_index);
+ break;
+ case DESC92C_RATEMCS8:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS11_MCS08,
+ MASKBYTE0, power_index);
+ break;
+ case DESC92C_RATEMCS9:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS11_MCS08,
+ MASKBYTE1, power_index);
+ break;
+ case DESC92C_RATEMCS10:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS11_MCS08,
+ MASKBYTE2, power_index);
+ break;
+ case DESC92C_RATEMCS11:
+ rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS11_MCS08,
+ MASKBYTE3, power_index);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
+ "Invalid Rate!!\n");
+ break;
+ }
+ } else {
+ RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Invalid RFPath!!\n");
+ }
+}
+
+void rtl8723be_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel)
+{
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u8 cck_rates[] = {DESC92C_RATE1M, DESC92C_RATE2M,
+ DESC92C_RATE5_5M, DESC92C_RATE11M};
+ u8 ofdm_rates[] = {DESC92C_RATE6M, DESC92C_RATE9M,
+ DESC92C_RATE12M, DESC92C_RATE18M,
+ DESC92C_RATE24M, DESC92C_RATE36M,
+ DESC92C_RATE48M, DESC92C_RATE54M};
+ u8 ht_rates_1t[] = {DESC92C_RATEMCS0, DESC92C_RATEMCS1,
+ DESC92C_RATEMCS2, DESC92C_RATEMCS3,
+ DESC92C_RATEMCS4, DESC92C_RATEMCS5,
+ DESC92C_RATEMCS6, DESC92C_RATEMCS7};
+ u8 i, size;
+ u8 power_index;
+
+ if (!rtlefuse->txpwr_fromeprom)
+ return;
+
+ size = sizeof(cck_rates) / sizeof(u8);
+ for (i = 0; i < size; i++) {
+ power_index = _rtl8723be_get_txpower_index(hw, RF90_PATH_A,
+ cck_rates[i],
+ rtl_priv(hw)->phy.current_chan_bw,
+ channel);
+ _rtl8723be_phy_set_txpower_index(hw, power_index, RF90_PATH_A,
+ cck_rates[i]);
+ }
+ size = sizeof(ofdm_rates) / sizeof(u8);
+ for (i = 0; i < size; i++) {
+ power_index = _rtl8723be_get_txpower_index(hw, RF90_PATH_A,
+ ofdm_rates[i],
+ rtl_priv(hw)->phy.current_chan_bw,
+ channel);
+ _rtl8723be_phy_set_txpower_index(hw, power_index, RF90_PATH_A,
+ ofdm_rates[i]);
+ }
+ size = sizeof(ht_rates_1t) / sizeof(u8);
+ for (i = 0; i < size; i++) {
+ power_index = _rtl8723be_get_txpower_index(hw, RF90_PATH_A,
+ ht_rates_1t[i],
+ rtl_priv(hw)->phy.current_chan_bw,
+ channel);
+ _rtl8723be_phy_set_txpower_index(hw, power_index, RF90_PATH_A,
+ ht_rates_1t[i]);
+ }
+}
+
+void rtl8723be_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ enum io_type iotype;
+
+ if (!is_hal_stop(rtlhal)) {
+ switch (operation) {
+ case SCAN_OPT_BACKUP:
+ iotype = IO_CMD_PAUSE_DM_BY_SCAN;
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD,
+ (u8 *)&iotype);
+ break;
+ case SCAN_OPT_RESTORE:
+ iotype = IO_CMD_RESUME_DM_BY_SCAN;
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD,
+ (u8 *)&iotype);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "Unknown Scan Backup operation.\n");
+ break;
+ }
+ }
+}
+
+void rtl8723be_phy_set_bw_mode_callback(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ u8 reg_bw_opmode;
+ u8 reg_prsr_rsc;
+
+ RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE,
+ "Switch to %s bandwidth\n",
+ rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20 ?
+ "20MHz" : "40MHz");
+
+ if (is_hal_stop(rtlhal)) {
+ rtlphy->set_bwmode_inprogress = false;
+ return;
+ }
+
+ reg_bw_opmode = rtl_read_byte(rtlpriv, REG_BWOPMODE);
+ reg_prsr_rsc = rtl_read_byte(rtlpriv, REG_RRSR + 2);
+
+ switch (rtlphy->current_chan_bw) {
+ case HT_CHANNEL_WIDTH_20:
+ reg_bw_opmode |= BW_OPMODE_20MHZ;
+ rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode);
+ break;
+ case HT_CHANNEL_WIDTH_20_40:
+ reg_bw_opmode &= ~BW_OPMODE_20MHZ;
+ rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode);
+ reg_prsr_rsc = (reg_prsr_rsc & 0x90) |
+ (mac->cur_40_prime_sc << 5);
+ rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_prsr_rsc);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "unknown bandwidth: %#X\n", rtlphy->current_chan_bw);
+ break;
+ }
+
+ switch (rtlphy->current_chan_bw) {
+ case HT_CHANNEL_WIDTH_20:
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x0);
+ rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x0);
+ break;
+ case HT_CHANNEL_WIDTH_20_40:
+ rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x1);
+ rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x1);
+ rtl_set_bbreg(hw, RCCK0_SYSTEM, BCCK_SIDEBAND,
+ (mac->cur_40_prime_sc >> 1));
+ rtl_set_bbreg(hw, ROFDM1_LSTF, 0xC00, mac->cur_40_prime_sc);
+ rtl_set_bbreg(hw, 0x818, (BIT(26) | BIT(27)),
+ (mac->cur_40_prime_sc ==
+ HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "unknown bandwidth: %#X\n", rtlphy->current_chan_bw);
+ break;
+ }
+ rtl8723be_phy_rf6052_set_bandwidth(hw, rtlphy->current_chan_bw);
+ rtlphy->set_bwmode_inprogress = false;
+ RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD, "\n");
+}
+
+void rtl8723be_phy_set_bw_mode(struct ieee80211_hw *hw,
+ enum nl80211_channel_type ch_type)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 tmp_bw = rtlphy->current_chan_bw;
+
+ if (rtlphy->set_bwmode_inprogress)
+ return;
+ rtlphy->set_bwmode_inprogress = true;
+ if ((!is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) {
+ rtl8723be_phy_set_bw_mode_callback(hw);
+ } else {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ "false driver sleep or unload\n");
+ rtlphy->set_bwmode_inprogress = false;
+ rtlphy->current_chan_bw = tmp_bw;
+ }
+}
+
+void rtl8723be_phy_sw_chnl_callback(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u32 delay;
+
+ RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE,
+ "switch to channel%d\n", rtlphy->current_channel);
+ if (is_hal_stop(rtlhal))
+ return;
+ do {
+ if (!rtlphy->sw_chnl_inprogress)
+ break;
+ if (!rtl8723be_phy_sw_chn_step_by_step(hw,
+ rtlphy->current_channel,
+ &rtlphy->sw_chnl_stage,
+ &rtlphy->sw_chnl_step,
+ &delay)) {
+ if (delay > 0)
+ mdelay(delay);
+ else
+ continue;
+ } else {
+ rtlphy->sw_chnl_inprogress = false;
+ }
+ break;
+ } while (true);
+ RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "\n");
+}
+
+u8 rtl8723be_phy_sw_chnl(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+
+ if (rtlphy->sw_chnl_inprogress)
+ return 0;
+ if (rtlphy->set_bwmode_inprogress)
+ return 0;
+ RT_ASSERT((rtlphy->current_channel <= 14),
+ "WIRELESS_MODE_G but channel>14");
+ rtlphy->sw_chnl_inprogress = true;
+ rtlphy->sw_chnl_stage = 0;
+ rtlphy->sw_chnl_step = 0;
+ if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) {
+ rtl8723be_phy_sw_chnl_callback(hw);
+ RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD,
+ "sw_chnl_inprogress false schdule "
+ "workitem current channel %d\n",
+ rtlphy->current_channel);
+ rtlphy->sw_chnl_inprogress = false;
+ } else {
+ RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD,
+ "sw_chnl_inprogress false driver sleep or"
+ " unload\n");
+ rtlphy->sw_chnl_inprogress = false;
+ }
+ return 1;
+}
+
+static bool rtl8723be_phy_sw_chn_step_by_step(struct ieee80211_hw *hw,
+ u8 channel, u8 *stage,
+ u8 *step, u32 *delay)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct swchnlcmd precommoncmd[MAX_PRECMD_CNT];
+ u32 precommoncmdcnt;
+ struct swchnlcmd postcommoncmd[MAX_POSTCMD_CNT];
+ u32 postcommoncmdcnt;
+ struct swchnlcmd rfdependcmd[MAX_RFDEPENDCMD_CNT];
+ u32 rfdependcmdcnt;
+ struct swchnlcmd *currentcmd = NULL;
+ u8 rfpath;
+ u8 num_total_rfpath = rtlphy->num_total_rfpath;
+
+ precommoncmdcnt = 0;
+ rtl8723_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++,
+ MAX_PRECMD_CNT,
+ CMDID_SET_TXPOWEROWER_LEVEL,
+ 0, 0, 0);
+ rtl8723_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++,
+ MAX_PRECMD_CNT, CMDID_END, 0, 0, 0);
+ postcommoncmdcnt = 0;
+ rtl8723_phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++,
+ MAX_POSTCMD_CNT, CMDID_END,
+ 0, 0, 0);
+ rfdependcmdcnt = 0;
+
+ RT_ASSERT((channel >= 1 && channel <= 14),
+ "illegal channel for Zebra: %d\n", channel);
+
+ rtl8723_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++,
+ MAX_RFDEPENDCMD_CNT,
+ CMDID_RF_WRITEREG,
+ RF_CHNLBW, channel, 10);
+
+ rtl8723_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++,
+ MAX_RFDEPENDCMD_CNT,
+ CMDID_END, 0, 0, 0);
+
+ do {
+ switch (*stage) {
+ case 0:
+ currentcmd = &precommoncmd[*step];
+ break;
+ case 1:
+ currentcmd = &rfdependcmd[*step];
+ break;
+ case 2:
+ currentcmd = &postcommoncmd[*step];
+ break;
+ }
+
+ if (currentcmd->cmdid == CMDID_END) {
+ if ((*stage) == 2) {
+ return true;
+ } else {
+ (*stage)++;
+ (*step) = 0;
+ continue;
+ }
+ }
+
+ switch (currentcmd->cmdid) {
+ case CMDID_SET_TXPOWEROWER_LEVEL:
+ rtl8723be_phy_set_txpower_level(hw, channel);
+ break;
+ case CMDID_WRITEPORT_ULONG:
+ rtl_write_dword(rtlpriv, currentcmd->para1,
+ currentcmd->para2);
+ break;
+ case CMDID_WRITEPORT_USHORT:
+ rtl_write_word(rtlpriv, currentcmd->para1,
+ (u16) currentcmd->para2);
+ break;
+ case CMDID_WRITEPORT_UCHAR:
+ rtl_write_byte(rtlpriv, currentcmd->para1,
+ (u8) currentcmd->para2);
+ break;
+ case CMDID_RF_WRITEREG:
+ for (rfpath = 0; rfpath < num_total_rfpath; rfpath++) {
+ rtlphy->rfreg_chnlval[rfpath] =
+ ((rtlphy->rfreg_chnlval[rfpath] &
+ 0xfffffc00) | currentcmd->para2);
+
+ rtl_set_rfreg(hw, (enum radio_path)rfpath,
+ currentcmd->para1,
+ RFREG_OFFSET_MASK,
+ rtlphy->rfreg_chnlval[rfpath]);
+ }
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process\n");
+ break;
+ }
+
+ break;
+ } while (true);
+
+ (*delay) = currentcmd->msdelay;
+ (*step)++;
+ return false;
+}
+
+static u8 _rtl8723be_phy_path_a_iqk(struct ieee80211_hw *hw, bool config_pathb)
+{
+ u32 reg_eac, reg_e94, reg_e9c, reg_ea4;
+ u8 result = 0x00;
+
+ rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x10008c1c);
+ rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x30008c1c);
+ rtl_set_bbreg(hw, 0xe38, MASKDWORD, 0x8214032a);
+ rtl_set_bbreg(hw, 0xe3c, MASKDWORD, 0x28160000);
+
+ rtl_set_bbreg(hw, 0xe4c, MASKDWORD, 0x00462911);
+ rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf9000000);
+ rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf8000000);
+
+ mdelay(IQK_DELAY_TIME);
+
+ reg_eac = rtl_get_bbreg(hw, 0xeac, MASKDWORD);
+ reg_e94 = rtl_get_bbreg(hw, 0xe94, MASKDWORD);
+ reg_e9c = rtl_get_bbreg(hw, 0xe9c, MASKDWORD);
+ reg_ea4 = rtl_get_bbreg(hw, 0xea4, MASKDWORD);
+
+ if (!(reg_eac & BIT(28)) &&
+ (((reg_e94 & 0x03FF0000) >> 16) != 0x142) &&
+ (((reg_e9c & 0x03FF0000) >> 16) != 0x42))
+ result |= 0x01;
+ return result;
+}
+
+static bool phy_similarity_cmp(struct ieee80211_hw *hw, long result[][8],
+ u8 c1, u8 c2)
+{
+ u32 i, j, diff, simularity_bitmap, bound;
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+
+ u8 final_candidate[2] = { 0xFF, 0xFF };
+ bool bresult = true, is2t = IS_92C_SERIAL(rtlhal->version);
+
+ if (is2t)
+ bound = 8;
+ else
+ bound = 4;
+
+ simularity_bitmap = 0;
+
+ for (i = 0; i < bound; i++) {
+ diff = (result[c1][i] > result[c2][i]) ?
+ (result[c1][i] - result[c2][i]) :
+ (result[c2][i] - result[c1][i]);
+
+ if (diff > MAX_TOLERANCE) {
+ if ((i == 2 || i == 6) && !simularity_bitmap) {
+ if (result[c1][i] + result[c1][i + 1] == 0)
+ final_candidate[(i / 4)] = c2;
+ else if (result[c2][i] + result[c2][i + 1] == 0)
+ final_candidate[(i / 4)] = c1;
+ else
+ simularity_bitmap |= (1 << i);
+ } else {
+ simularity_bitmap |= (1 << i);
+ }
+ }
+ }
+
+ if (simularity_bitmap == 0) {
+ for (i = 0; i < (bound / 4); i++) {
+ if (final_candidate[i] != 0xFF) {
+ for (j = i * 4; j < (i + 1) * 4 - 2; j++)
+ result[3][j] =
+ result[final_candidate[i]][j];
+ bresult = false;
+ }
+ }
+ return bresult;
+ } else if (!(simularity_bitmap & 0x0F)) {
+ for (i = 0; i < 4; i++)
+ result[3][i] = result[c1][i];
+ return false;
+ } else if (!(simularity_bitmap & 0xF0) && is2t) {
+ for (i = 4; i < 8; i++)
+ result[3][i] = result[c1][i];
+ return false;
+ } else {
+ return false;
+ }
+}
+
+static void _rtl8723be_phy_iq_calibrate(struct ieee80211_hw *hw,
+ long result[][8], u8 t, bool is2t)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u32 i;
+ u8 patha_ok;
+ u32 adda_reg[IQK_ADDA_REG_NUM] = {
+ 0x85c, 0xe6c, 0xe70, 0xe74,
+ 0xe78, 0xe7c, 0xe80, 0xe84,
+ 0xe88, 0xe8c, 0xed0, 0xed4,
+ 0xed8, 0xedc, 0xee0, 0xeec
+ };
+
+ u32 iqk_mac_reg[IQK_MAC_REG_NUM] = {
+ 0x522, 0x550, 0x551, 0x040
+ };
+ u32 iqk_bb_reg[IQK_BB_REG_NUM] = {
+ ROFDM0_TRXPATHENABLE, ROFDM0_TRMUXPAR,
+ RFPGA0_XCD_RFINTERFACESW, 0xb68, 0xb6c,
+ 0x870, 0x860,
+ 0x864, 0x800
+ };
+ const u32 retrycount = 2;
+ u32 path_sel_bb, path_sel_rf;
+ u8 tmp_reg_c50, tmp_reg_c58;
+
+ tmp_reg_c50 = rtl_get_bbreg(hw, 0xc50, MASKBYTE0);
+ tmp_reg_c58 = rtl_get_bbreg(hw, 0xc58, MASKBYTE0);
+
+ if (t == 0) {
+ rtl8723_save_adda_registers(hw, adda_reg,
+ rtlphy->adda_backup, 16);
+ rtl8723_phy_save_mac_registers(hw, iqk_mac_reg,
+ rtlphy->iqk_mac_backup);
+ rtl8723_save_adda_registers(hw, iqk_bb_reg,
+ rtlphy->iqk_bb_backup,
+ IQK_BB_REG_NUM);
+ }
+ rtl8723_phy_path_adda_on(hw, adda_reg, true, is2t);
+ if (t == 0) {
+ rtlphy->rfpi_enable = (u8) rtl_get_bbreg(hw,
+ RFPGA0_XA_HSSIPARAMETER1,
+ BIT(8));
+ }
+ if (!rtlphy->rfpi_enable)
+ rtl8723_phy_pi_mode_switch(hw, true);
+
+ path_sel_bb = rtl_get_bbreg(hw, 0x948, MASKDWORD);
+ path_sel_rf = rtl_get_rfreg(hw, RF90_PATH_A, 0xb0, 0xfffff);
+
+ /*BB Setting*/
+ rtl_set_bbreg(hw, 0x800, BIT(24), 0x00);
+ rtl_set_bbreg(hw, 0xc04, MASKDWORD, 0x03a05600);
+ rtl_set_bbreg(hw, 0xc08, MASKDWORD, 0x000800e4);
+ rtl_set_bbreg(hw, 0x874, MASKDWORD, 0x22204000);
+
+ rtl_set_bbreg(hw, 0x870, BIT(10), 0x01);
+ rtl_set_bbreg(hw, 0x870, BIT(26), 0x01);
+ rtl_set_bbreg(hw, 0x860, BIT(10), 0x00);
+ rtl_set_bbreg(hw, 0x864, BIT(10), 0x00);
+
+ if (is2t)
+ rtl_set_rfreg(hw, RF90_PATH_B, 0x00, MASKDWORD, 0x10000);
+ rtl8723_phy_mac_setting_calibration(hw, iqk_mac_reg,
+ rtlphy->iqk_mac_backup);
+ rtl_set_bbreg(hw, 0xb68, MASKDWORD, 0x0f600000);
+
+ rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000);
+ rtl_set_bbreg(hw, 0xe40, MASKDWORD, 0x01007c00);
+ rtl_set_bbreg(hw, 0xe44, MASKDWORD, 0x81004800);
+ for (i = 0; i < retrycount; i++) {
+ patha_ok = _rtl8723be_phy_path_a_iqk(hw, is2t);
+ if (patha_ok == 0x01) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Path A Tx IQK Success!!\n");
+ result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) &
+ 0x3FF0000) >> 16;
+ result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) &
+ 0x3FF0000) >> 16;
+ break;
+ }
+ }
+
+ if (0 == patha_ok)
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Path A IQK Success!!\n");
+ if (is2t) {
+ rtl8723_phy_path_a_standby(hw);
+ rtl8723_phy_path_adda_on(hw, adda_reg, false, is2t);
+ }
+
+ rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0);
+
+ if (t != 0) {
+ if (!rtlphy->rfpi_enable)
+ rtl8723_phy_pi_mode_switch(hw, false);
+ rtl8723_phy_reload_adda_registers(hw, adda_reg,
+ rtlphy->adda_backup, 16);
+ rtl8723_phy_reload_mac_registers(hw, iqk_mac_reg,
+ rtlphy->iqk_mac_backup);
+ rtl8723_phy_reload_adda_registers(hw, iqk_bb_reg,
+ rtlphy->iqk_bb_backup,
+ IQK_BB_REG_NUM);
+
+ rtl_set_bbreg(hw, 0x948, MASKDWORD, path_sel_bb);
+ rtl_set_rfreg(hw, RF90_PATH_B, 0xb0, 0xfffff, path_sel_rf);
+
+ rtl_set_bbreg(hw, 0xc50, MASKBYTE0, 0x50);
+ rtl_set_bbreg(hw, 0xc50, MASKBYTE0, tmp_reg_c50);
+ if (is2t) {
+ rtl_set_bbreg(hw, 0xc58, MASKBYTE0, 0x50);
+ rtl_set_bbreg(hw, 0xc58, MASKBYTE0, tmp_reg_c58);
+ }
+ rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x01008c00);
+ rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x01008c00);
+ }
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "8723be IQK Finish!!\n");
+}
+
+static void _rtl8723be_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 tmpreg;
+ u32 rf_a_mode = 0, rf_b_mode = 0, lc_cal;
+
+ tmpreg = rtl_read_byte(rtlpriv, 0xd03);
+
+ if ((tmpreg & 0x70) != 0)
+ rtl_write_byte(rtlpriv, 0xd03, tmpreg & 0x8F);
+ else
+ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF);
+
+ if ((tmpreg & 0x70) != 0) {
+ rf_a_mode = rtl_get_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS);
+
+ if (is2t)
+ rf_b_mode = rtl_get_rfreg(hw, RF90_PATH_B, 0x00,
+ MASK12BITS);
+
+ rtl_set_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS,
+ (rf_a_mode & 0x8FFFF) | 0x10000);
+
+ if (is2t)
+ rtl_set_rfreg(hw, RF90_PATH_B, 0x00, MASK12BITS,
+ (rf_b_mode & 0x8FFFF) | 0x10000);
+ }
+ lc_cal = rtl_get_rfreg(hw, RF90_PATH_A, 0x18, MASK12BITS);
+
+ rtl_set_rfreg(hw, RF90_PATH_A, 0xb0, RFREG_OFFSET_MASK, 0xdfbe0);
+ rtl_set_rfreg(hw, RF90_PATH_A, 0x18, MASK12BITS, 0x8c0a);
+
+ mdelay(100);
+
+ rtl_set_rfreg(hw, RF90_PATH_A, 0xb0, RFREG_OFFSET_MASK, 0xdffe0);
+
+ if ((tmpreg & 0x70) != 0) {
+ rtl_write_byte(rtlpriv, 0xd03, tmpreg);
+ rtl_set_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS, rf_a_mode);
+
+ if (is2t)
+ rtl_set_rfreg(hw, RF90_PATH_B, 0x00,
+ MASK12BITS, rf_b_mode);
+ } else {
+ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00);
+ }
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "\n");
+}
+
+static void _rtl8723be_phy_set_rfpath_switch(struct ieee80211_hw *hw,
+ bool bmain, bool is2t)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "\n");
+
+ if (is_hal_stop(rtlhal)) {
+ u8 u1btmp;
+ u1btmp = rtl_read_byte(rtlpriv, REG_LEDCFG0);
+ rtl_write_byte(rtlpriv, REG_LEDCFG0, u1btmp | BIT(7));
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(13), 0x01);
+ }
+ if (is2t) {
+ if (bmain)
+ rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE,
+ BIT(5) | BIT(6), 0x1);
+ else
+ rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE,
+ BIT(5) | BIT(6), 0x2);
+ } else {
+ rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BIT(8) | BIT(9), 0);
+ rtl_set_bbreg(hw, 0x914, MASKLWORD, 0x0201);
+
+ /* We use the RF definition of MAIN and AUX,
+ * left antenna and right antenna repectively.
+ * Default output at AUX.
+ */
+ if (bmain) {
+ rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE,
+ BIT(14) | BIT(13) | BIT(12), 0);
+ rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE,
+ BIT(5) | BIT(4) | BIT(3), 0);
+ if (rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV)
+ rtl_set_bbreg(hw, CONFIG_RAM64X16, BIT(31), 0);
+ } else {
+ rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE,
+ BIT(14) | BIT(13) | BIT(12), 1);
+ rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE,
+ BIT(5) | BIT(4) | BIT(3), 1);
+ if (rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV)
+ rtl_set_bbreg(hw, CONFIG_RAM64X16, BIT(31), 1);
+ }
+ }
+}
+
+#undef IQK_ADDA_REG_NUM
+#undef IQK_DELAY_TIME
+
+void rtl8723be_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ long result[4][8];
+ u8 i, final_candidate;
+ bool patha_ok, pathb_ok;
+ long reg_e94, reg_e9c, reg_ea4, reg_eac, reg_eb4, reg_ebc, reg_ec4,
+ reg_ecc, reg_tmp = 0;
+ bool is12simular, is13simular, is23simular;
+ u32 iqk_bb_reg[9] = {
+ ROFDM0_XARXIQIMBALANCE,
+ ROFDM0_XBRXIQIMBALANCE,
+ ROFDM0_ECCATHRESHOLD,
+ ROFDM0_AGCRSSITABLE,
+ ROFDM0_XATXIQIMBALANCE,
+ ROFDM0_XBTXIQIMBALANCE,
+ ROFDM0_XCTXAFE,
+ ROFDM0_XDTXAFE,
+ ROFDM0_RXIQEXTANTA
+ };
+
+ if (recovery) {
+ rtl8723_phy_reload_adda_registers(hw, iqk_bb_reg,
+ rtlphy->iqk_bb_backup, 9);
+ return;
+ }
+
+ for (i = 0; i < 8; i++) {
+ result[0][i] = 0;
+ result[1][i] = 0;
+ result[2][i] = 0;
+ result[3][i] = 0;
+ }
+ final_candidate = 0xff;
+ patha_ok = false;
+ pathb_ok = false;
+ is12simular = false;
+ is23simular = false;
+ is13simular = false;
+ for (i = 0; i < 3; i++) {
+ if (get_rf_type(rtlphy) == RF_2T2R)
+ _rtl8723be_phy_iq_calibrate(hw, result, i, true);
+ else
+ _rtl8723be_phy_iq_calibrate(hw, result, i, false);
+ if (i == 1) {
+ is12simular = phy_similarity_cmp(hw, result, 0, 1);
+ if (is12simular) {
+ final_candidate = 0;
+ break;
+ }
+ }
+ if (i == 2) {
+ is13simular = phy_similarity_cmp(hw, result, 0, 2);
+ if (is13simular) {
+ final_candidate = 0;
+ break;
+ }
+ is23simular = phy_similarity_cmp(hw, result, 1, 2);
+ if (is23simular) {
+ final_candidate = 1;
+ } else {
+ for (i = 0; i < 8; i++)
+ reg_tmp += result[3][i];
+
+ if (reg_tmp != 0)
+ final_candidate = 3;
+ else
+ final_candidate = 0xFF;
+ }
+ }
+ }
+ for (i = 0; i < 4; i++) {
+ reg_e94 = result[i][0];
+ reg_e9c = result[i][1];
+ reg_ea4 = result[i][2];
+ reg_eac = result[i][3];
+ reg_eb4 = result[i][4];
+ reg_ebc = result[i][5];
+ reg_ec4 = result[i][6];
+ reg_ecc = result[i][7];
+ }
+ if (final_candidate != 0xff) {
+ reg_e94 = result[final_candidate][0];
+ rtlphy->reg_e94 = reg_e94;
+ reg_e9c = result[final_candidate][1];
+ rtlphy->reg_e9c = reg_e9c;
+ reg_ea4 = result[final_candidate][2];
+ reg_eac = result[final_candidate][3];
+ reg_eb4 = result[final_candidate][4];
+ rtlphy->reg_eb4 = reg_eb4;
+ reg_ebc = result[final_candidate][5];
+ rtlphy->reg_ebc = reg_ebc;
+ reg_ec4 = result[final_candidate][6];
+ reg_ecc = result[final_candidate][7];
+ patha_ok = true;
+ pathb_ok = true;
+ } else {
+ rtlphy->reg_e94 = 0x100;
+ rtlphy->reg_eb4 = 0x100;
+ rtlphy->reg_e9c = 0x0;
+ rtlphy->reg_ebc = 0x0;
+ }
+ if (reg_e94 != 0) /*&&(reg_ea4 != 0) */
+ rtl8723_phy_path_a_fill_iqk_matrix(hw, patha_ok, result,
+ final_candidate,
+ (reg_ea4 == 0));
+ if (final_candidate != 0xFF) {
+ for (i = 0; i < IQK_MATRIX_REG_NUM; i++)
+ rtlphy->iqk_matrix[0].value[0][i] =
+ result[final_candidate][i];
+ rtlphy->iqk_matrix[0].iqk_done = true;
+ }
+ rtl8723_save_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 9);
+}
+
+void rtl8723be_phy_lc_calibrate(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_hal *rtlhal = &(rtlpriv->rtlhal);
+ u32 timeout = 2000, timecount = 0;
+
+ while (rtlpriv->mac80211.act_scanning && timecount < timeout) {
+ udelay(50);
+ timecount += 50;
+ }
+
+ rtlphy->lck_inprogress = true;
+ RTPRINT(rtlpriv, FINIT, INIT_EEPROM,
+ "LCK:Start!!! currentband %x delay %d ms\n",
+ rtlhal->current_bandtype, timecount);
+
+ _rtl8723be_phy_lc_calibrate(hw, false);
+
+ rtlphy->lck_inprogress = false;
+}
+
+void rtl23b_phy_ap_calibrate(struct ieee80211_hw *hw, char delta)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ if (rtlphy->apk_done)
+ return;
+
+ return;
+}
+
+void rtl8723be_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain)
+{
+ _rtl8723be_phy_set_rfpath_switch(hw, bmain, false);
+}
+
+static void rtl8723be_phy_set_io(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+ "--->Cmd(%#x), set_io_inprogress(%d)\n",
+ rtlphy->current_io_type, rtlphy->set_io_inprogress);
+ switch (rtlphy->current_io_type) {
+ case IO_CMD_RESUME_DM_BY_SCAN:
+ rtlpriv->dm_digtable.cur_igvalue =
+ rtlphy->initgain_backup.xaagccore1;
+ /*rtl92c_dm_write_dig(hw);*/
+ rtl8723be_phy_set_txpower_level(hw, rtlphy->current_channel);
+ rtl_set_bbreg(hw, RCCK0_CCA, 0xff0000, 0x83);
+ break;
+ case IO_CMD_PAUSE_DM_BY_SCAN:
+ rtlphy->initgain_backup.xaagccore1 =
+ rtlpriv->dm_digtable.cur_igvalue;
+ rtlpriv->dm_digtable.cur_igvalue = 0x17;
+ rtl_set_bbreg(hw, RCCK0_CCA, 0xff0000, 0x40);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process\n");
+ break;
+ }
+ rtlphy->set_io_inprogress = false;
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+ "(%#x)\n", rtlphy->current_io_type);
+}
+
+bool rtl8723be_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ bool postprocessing = false;
+
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+ "-->IO Cmd(%#x), set_io_inprogress(%d)\n",
+ iotype, rtlphy->set_io_inprogress);
+ do {
+ switch (iotype) {
+ case IO_CMD_RESUME_DM_BY_SCAN:
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+ "[IO CMD] Resume DM after scan.\n");
+ postprocessing = true;
+ break;
+ case IO_CMD_PAUSE_DM_BY_SCAN:
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE,
+ "[IO CMD] Pause DM before scan.\n");
+ postprocessing = true;
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process\n");
+ break;
+ }
+ } while (false);
+ if (postprocessing && !rtlphy->set_io_inprogress) {
+ rtlphy->set_io_inprogress = true;
+ rtlphy->current_io_type = iotype;
+ } else {
+ return false;
+ }
+ rtl8723be_phy_set_io(hw);
+ RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "IO Type(%#x)\n", iotype);
+ return true;
+}
+
+static void rtl8723be_phy_set_rf_on(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3);
+ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00);
+}
+
+static void _rtl8723be_phy_set_rf_sleep(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF);
+ rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2);
+ rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x22);
+}
+
+static bool _rtl8723be_phy_set_rf_power_state(struct ieee80211_hw *hw,
+ enum rf_pwrstate rfpwr_state)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+ bool bresult = true;
+ u8 i, queue_id;
+ struct rtl8192_tx_ring *ring = NULL;
+
+ switch (rfpwr_state) {
+ case ERFON:
+ if ((ppsc->rfpwr_state == ERFOFF) &&
+ RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC)) {
+ bool rtstatus;
+ u32 initialize_count = 0;
+ do {
+ initialize_count++;
+ RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+ "IPS Set eRf nic enable\n");
+ rtstatus = rtl_ps_enable_nic(hw);
+ } while (!rtstatus && (initialize_count < 10));
+ RT_CLEAR_PS_LEVEL(ppsc,
+ RT_RF_OFF_LEVL_HALT_NIC);
+ } else {
+ RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+ "Set ERFON sleeped:%d ms\n",
+ jiffies_to_msecs(jiffies -
+ ppsc->last_sleep_jiffies));
+ ppsc->last_awake_jiffies = jiffies;
+ rtl8723be_phy_set_rf_on(hw);
+ }
+ if (mac->link_state == MAC80211_LINKED)
+ rtlpriv->cfg->ops->led_control(hw, LED_CTL_LINK);
+ else
+ rtlpriv->cfg->ops->led_control(hw, LED_CTL_NO_LINK);
+ break;
+ case ERFOFF:
+ for (queue_id = 0, i = 0;
+ queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) {
+ ring = &pcipriv->dev.tx_ring[queue_id];
+ if (skb_queue_len(&ring->queue) == 0) {
+ queue_id++;
+ continue;
+ } else {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ "eRf Off/Sleep: %d times "
+ "TcbBusyQueue[%d] =%d before "
+ "doze!\n", (i + 1), queue_id,
+ skb_queue_len(&ring->queue));
+
+ udelay(10);
+ i++;
+ }
+ if (i >= MAX_DOZE_WAITING_TIMES_9x) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ "\n ERFSLEEP: %d times "
+ "TcbBusyQueue[%d] = %d !\n",
+ MAX_DOZE_WAITING_TIMES_9x,
+ queue_id,
+ skb_queue_len(&ring->queue));
+ break;
+ }
+ }
+
+ if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) {
+ RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+ "IPS Set eRf nic disable\n");
+ rtl_ps_disable_nic(hw);
+ RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
+ } else {
+ if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) {
+ rtlpriv->cfg->ops->led_control(hw,
+ LED_CTL_NO_LINK);
+ } else {
+ rtlpriv->cfg->ops->led_control(hw,
+ LED_CTL_POWER_OFF);
+ }
+ }
+ break;
+ case ERFSLEEP:
+ if (ppsc->rfpwr_state == ERFOFF)
+ break;
+ for (queue_id = 0, i = 0;
+ queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) {
+ ring = &pcipriv->dev.tx_ring[queue_id];
+ if (skb_queue_len(&ring->queue) == 0) {
+ queue_id++;
+ continue;
+ } else {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ "eRf Off/Sleep: %d times "
+ "TcbBusyQueue[%d] =%d before "
+ "doze!\n", (i + 1), queue_id,
+ skb_queue_len(&ring->queue));
+
+ udelay(10);
+ i++;
+ }
+ if (i >= MAX_DOZE_WAITING_TIMES_9x) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ "\n ERFSLEEP: %d times "
+ "TcbBusyQueue[%d] = %d !\n",
+ MAX_DOZE_WAITING_TIMES_9x,
+ queue_id,
+ skb_queue_len(&ring->queue));
+ break;
+ }
+ }
+ RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
+ "Set ERFSLEEP awaked:%d ms\n",
+ jiffies_to_msecs(jiffies -
+ ppsc->last_awake_jiffies));
+ ppsc->last_sleep_jiffies = jiffies;
+ _rtl8723be_phy_set_rf_sleep(hw);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "switch case not process\n");
+ bresult = false;
+ break;
+ }
+ if (bresult)
+ ppsc->rfpwr_state = rfpwr_state;
+ return bresult;
+}
+
+bool rtl8723be_phy_set_rf_power_state(struct ieee80211_hw *hw,
+ enum rf_pwrstate rfpwr_state)
+{
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
+
+ bool bresult = false;
+
+ if (rfpwr_state == ppsc->rfpwr_state)
+ return bresult;
+ bresult = _rtl8723be_phy_set_rf_power_state(hw, rfpwr_state);
+ return bresult;
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/phy.h b/drivers/net/wireless/rtlwifi/rtl8723be/phy.h
new file mode 100644
index 000000000000..444ef95bb6af
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/phy.h
@@ -0,0 +1,217 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_PHY_H__
+#define __RTL8723BE_PHY_H__
+
+/*It must always set to 4, otherwise read efuse table secquence will be wrong.*/
+#define MAX_TX_COUNT 4
+#define TX_1S 0
+#define TX_2S 1
+
+#define MAX_POWER_INDEX 0x3F
+
+#define MAX_PRECMD_CNT 16
+#define MAX_RFDEPENDCMD_CNT 16
+#define MAX_POSTCMD_CNT 16
+
+#define MAX_DOZE_WAITING_TIMES_9x 64
+
+#define RT_CANNOT_IO(hw) false
+#define HIGHPOWER_RADIOA_ARRAYLEN 22
+
+#define IQK_ADDA_REG_NUM 16
+#define IQK_BB_REG_NUM 9
+#define MAX_TOLERANCE 5
+#define IQK_DELAY_TIME 10
+#define index_mapping_NUM 15
+
+#define APK_BB_REG_NUM 5
+#define APK_AFE_REG_NUM 16
+#define APK_CURVE_REG_NUM 4
+#define PATH_NUM 1
+
+#define LOOP_LIMIT 5
+#define MAX_STALL_TIME 50
+#define ANTENNADIVERSITYVALUE 0x80
+#define MAX_TXPWR_IDX_NMODE_92S 63
+#define RESET_CNT_LIMIT 3
+
+#define IQK_ADDA_REG_NUM 16
+#define IQK_MAC_REG_NUM 4
+
+#define RF6052_MAX_PATH 2
+
+#define CT_OFFSET_MAC_ADDR 0X16
+
+#define CT_OFFSET_CCK_TX_PWR_IDX 0x5A
+#define CT_OFFSET_HT401S_TX_PWR_IDX 0x60
+#define CT_OFFSET_HT402S_TX_PWR_IDX_DIFF 0x66
+#define CT_OFFSET_HT20_TX_PWR_IDX_DIFF 0x69
+#define CT_OFFSET_OFDM_TX_PWR_IDX_DIFF 0x6C
+
+#define CT_OFFSET_HT40_MAX_PWR_OFFSET 0x6F
+#define CT_OFFSET_HT20_MAX_PWR_OFFSET 0x72
+
+#define CT_OFFSET_CHANNEL_PLAH 0x75
+#define CT_OFFSET_THERMAL_METER 0x78
+#define CT_OFFSET_RF_OPTION 0x79
+#define CT_OFFSET_VERSION 0x7E
+#define CT_OFFSET_CUSTOMER_ID 0x7F
+
+#define RTL92C_MAX_PATH_NUM 2
+
+enum hw90_block_e {
+ HW90_BLOCK_MAC = 0,
+ HW90_BLOCK_PHY0 = 1,
+ HW90_BLOCK_PHY1 = 2,
+ HW90_BLOCK_RF = 3,
+ HW90_BLOCK_MAXIMUM = 4,
+};
+
+enum baseband_config_type {
+ BASEBAND_CONFIG_PHY_REG = 0,
+ BASEBAND_CONFIG_AGC_TAB = 1,
+};
+
+enum ra_offset_area {
+ RA_OFFSET_LEGACY_OFDM1,
+ RA_OFFSET_LEGACY_OFDM2,
+ RA_OFFSET_HT_OFDM1,
+ RA_OFFSET_HT_OFDM2,
+ RA_OFFSET_HT_OFDM3,
+ RA_OFFSET_HT_OFDM4,
+ RA_OFFSET_HT_CCK,
+};
+
+enum antenna_path {
+ ANTENNA_NONE,
+ ANTENNA_D,
+ ANTENNA_C,
+ ANTENNA_CD,
+ ANTENNA_B,
+ ANTENNA_BD,
+ ANTENNA_BC,
+ ANTENNA_BCD,
+ ANTENNA_A,
+ ANTENNA_AD,
+ ANTENNA_AC,
+ ANTENNA_ACD,
+ ANTENNA_AB,
+ ANTENNA_ABD,
+ ANTENNA_ABC,
+ ANTENNA_ABCD
+};
+
+struct r_antenna_select_ofdm {
+ u32 r_tx_antenna:4;
+ u32 r_ant_l:4;
+ u32 r_ant_non_ht:4;
+ u32 r_ant_ht1:4;
+ u32 r_ant_ht2:4;
+ u32 r_ant_ht_s1:4;
+ u32 r_ant_non_ht_s1:4;
+ u32 ofdm_txsc:2;
+ u32 reserved:2;
+};
+
+struct r_antenna_select_cck {
+ u8 r_cckrx_enable_2:2;
+ u8 r_cckrx_enable:2;
+ u8 r_ccktx_enable:4;
+};
+
+
+struct efuse_contents {
+ u8 mac_addr[ETH_ALEN];
+ u8 cck_tx_power_idx[6];
+ u8 ht40_1s_tx_power_idx[6];
+ u8 ht40_2s_tx_power_idx_diff[3];
+ u8 ht20_tx_power_idx_diff[3];
+ u8 ofdm_tx_power_idx_diff[3];
+ u8 ht40_max_power_offset[3];
+ u8 ht20_max_power_offset[3];
+ u8 channel_plan;
+ u8 thermal_meter;
+ u8 rf_option[5];
+ u8 version;
+ u8 oem_id;
+ u8 regulatory;
+};
+
+struct tx_power_struct {
+ u8 cck[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+ u8 ht40_1s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+ u8 ht40_2s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+ u8 ht20_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+ u8 legacy_ht_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+ u8 legacy_ht_txpowerdiff;
+ u8 groupht20[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+ u8 groupht40[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER];
+ u8 pwrgroup_cnt;
+ u32 mcs_original_offset[4][16];
+};
+
+enum _ANT_DIV_TYPE {
+ NO_ANTDIV = 0xFF,
+ CG_TRX_HW_ANTDIV = 0x01,
+ CGCS_RX_HW_ANTDIV = 0x02,
+ FIXED_HW_ANTDIV = 0x03,
+ CG_TRX_SMART_ANTDIV = 0x04,
+ CGCS_RX_SW_ANTDIV = 0x05,
+};
+
+u32 rtl8723be_phy_query_rf_reg(struct ieee80211_hw *hw,
+ enum radio_path rfpath,
+ u32 regaddr, u32 bitmask);
+void rtl8723be_phy_set_rf_reg(struct ieee80211_hw *hw,
+ enum radio_path rfpath,
+ u32 regaddr, u32 bitmask, u32 data);
+bool rtl8723be_phy_mac_config(struct ieee80211_hw *hw);
+bool rtl8723be_phy_bb_config(struct ieee80211_hw *hw);
+bool rtl8723be_phy_rf_config(struct ieee80211_hw *hw);
+void rtl8723be_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw);
+void rtl8723be_phy_get_txpower_level(struct ieee80211_hw *hw,
+ long *powerlevel);
+void rtl8723be_phy_set_txpower_level(struct ieee80211_hw *hw,
+ u8 channel);
+void rtl8723be_phy_scan_operation_backup(struct ieee80211_hw *hw,
+ u8 operation);
+void rtl8723be_phy_set_bw_mode_callback(struct ieee80211_hw *hw);
+void rtl8723be_phy_set_bw_mode(struct ieee80211_hw *hw,
+ enum nl80211_channel_type ch_type);
+void rtl8723be_phy_sw_chnl_callback(struct ieee80211_hw *hw);
+u8 rtl8723be_phy_sw_chnl(struct ieee80211_hw *hw);
+void rtl8723be_phy_iq_calibrate(struct ieee80211_hw *hw,
+ bool b_recovery);
+void rtl23b_phy_ap_calibrate(struct ieee80211_hw *hw, char delta);
+void rtl8723be_phy_lc_calibrate(struct ieee80211_hw *hw);
+void rtl8723be_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain);
+bool rtl8723be_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
+ enum radio_path rfpath);
+bool rtl8723be_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype);
+bool rtl8723be_phy_set_rf_power_state(struct ieee80211_hw *hw,
+ enum rf_pwrstate rfpwr_state);
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.c b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.c
new file mode 100644
index 000000000000..b5167e73fecf
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.c
@@ -0,0 +1,106 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "pwrseqcmd.h"
+#include "pwrseq.h"
+
+
+/* drivers should parse below arrays and do the corresponding actions */
+/*3 Power on Array*/
+struct wlan_pwr_cfg rtl8723B_power_on_flow[RTL8723B_TRANS_CARDEMU_TO_ACT_STEPS +
+ RTL8723B_TRANS_END_STEPS] = {
+ RTL8723B_TRANS_CARDEMU_TO_ACT
+ RTL8723B_TRANS_END
+};
+
+/*3Radio off GPIO Array */
+struct wlan_pwr_cfg rtl8723B_radio_off_flow[RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS
+ + RTL8723B_TRANS_END_STEPS] = {
+ RTL8723B_TRANS_ACT_TO_CARDEMU
+ RTL8723B_TRANS_END
+};
+
+/*3Card Disable Array*/
+struct wlan_pwr_cfg rtl8723B_card_disable_flow
+ [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS +
+ RTL8723B_TRANS_END_STEPS] = {
+ RTL8723B_TRANS_ACT_TO_CARDEMU
+ RTL8723B_TRANS_CARDEMU_TO_CARDDIS
+ RTL8723B_TRANS_END
+};
+
+/*3 Card Enable Array*/
+struct wlan_pwr_cfg rtl8723B_card_enable_flow
+ [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS +
+ RTL8723B_TRANS_END_STEPS] = {
+ RTL8723B_TRANS_CARDDIS_TO_CARDEMU
+ RTL8723B_TRANS_CARDEMU_TO_ACT
+ RTL8723B_TRANS_END
+};
+
+/*3Suspend Array*/
+struct wlan_pwr_cfg rtl8723B_suspend_flow[RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8723B_TRANS_CARDEMU_TO_SUS_STEPS +
+ RTL8723B_TRANS_END_STEPS] = {
+ RTL8723B_TRANS_ACT_TO_CARDEMU
+ RTL8723B_TRANS_CARDEMU_TO_SUS
+ RTL8723B_TRANS_END
+};
+
+/*3 Resume Array*/
+struct wlan_pwr_cfg rtl8723B_resume_flow[RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8723B_TRANS_CARDEMU_TO_SUS_STEPS +
+ RTL8723B_TRANS_END_STEPS] = {
+ RTL8723B_TRANS_SUS_TO_CARDEMU
+ RTL8723B_TRANS_CARDEMU_TO_ACT
+ RTL8723B_TRANS_END
+};
+
+/*3HWPDN Array*/
+struct wlan_pwr_cfg rtl8723B_hwpdn_flow[RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS +
+ RTL8723B_TRANS_END_STEPS] = {
+ RTL8723B_TRANS_ACT_TO_CARDEMU
+ RTL8723B_TRANS_CARDEMU_TO_PDN
+ RTL8723B_TRANS_END
+};
+
+/*3 Enter LPS */
+struct wlan_pwr_cfg rtl8723B_enter_lps_flow[RTL8723B_TRANS_ACT_TO_LPS_STEPS +
+ RTL8723B_TRANS_END_STEPS] = {
+ /*FW behavior*/
+ RTL8723B_TRANS_ACT_TO_LPS
+ RTL8723B_TRANS_END
+};
+
+/*3 Leave LPS */
+struct wlan_pwr_cfg rtl8723B_leave_lps_flow[RTL8723B_TRANS_LPS_TO_ACT_STEPS +
+ RTL8723B_TRANS_END_STEPS] = {
+ /*FW behavior*/
+ RTL8723B_TRANS_LPS_TO_ACT
+ RTL8723B_TRANS_END
+};
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.h b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.h
new file mode 100644
index 000000000000..a62f43ed8d32
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.h
@@ -0,0 +1,304 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_PWRSEQ_H__
+#define __RTL8723BE_PWRSEQ_H__
+
+/* Check document WM-20130425-JackieLau-RTL8723B_Power_Architecture v05.vsd
+ * There are 6 HW Power States:
+ * 0: POFF--Power Off
+ * 1: PDN--Power Down
+ * 2: CARDEMU--Card Emulation
+ * 3: ACT--Active Mode
+ * 4: LPS--Low Power State
+ * 5: SUS--Suspend
+ *
+ * The transition from different states are defined below
+ * TRANS_CARDEMU_TO_ACT
+ * TRANS_ACT_TO_CARDEMU
+ * TRANS_CARDEMU_TO_SUS
+ * TRANS_SUS_TO_CARDEMU
+ * TRANS_CARDEMU_TO_PDN
+ * TRANS_ACT_TO_LPS
+ * TRANS_LPS_TO_ACT
+ *
+ * TRANS_END
+ */
+#define RTL8723B_TRANS_CARDEMU_TO_ACT_STEPS 23
+#define RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS 15
+#define RTL8723B_TRANS_CARDEMU_TO_SUS_STEPS 15
+#define RTL8723B_TRANS_SUS_TO_CARDEMU_STEPS 15
+#define RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS 15
+#define RTL8723B_TRANS_PDN_TO_CARDEMU_STEPS 15
+#define RTL8723B_TRANS_ACT_TO_LPS_STEPS 15
+#define RTL8723B_TRANS_LPS_TO_ACT_STEPS 15
+#define RTL8723B_TRANS_END_STEPS 1
+
+#define RTL8723B_TRANS_CARDEMU_TO_ACT \
+ {0x0020, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, \
+ {0x0067, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, \
+ {0x0001, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_DELAY, 1, PWRSEQ_DELAY_MS}, \
+ {0x0000, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), 0}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT(4)|BIT(3)|BIT(2)), 0}, \
+ {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0) , 0}, \
+ {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), BIT(1)}, \
+ {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0) , BIT(0)}, \
+ {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT(4)|BIT(3)), 0}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(0), 0}, \
+ {0x0010, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6), BIT(6)}, \
+ {0x0049, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \
+ {0x0063, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \
+ {0x0062, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0}, \
+ {0x0058, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, \
+ {0x005A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \
+ {0x0068, PWR_CUT_TESTCHIP_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,\
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3), BIT(3)}, \
+ {0x0069, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6), BIT(6)},
+
+#define RTL8723B_TRANS_ACT_TO_CARDEMU \
+ {0x001F, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0}, \
+ {0x004F, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, \
+ {0x0049, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), 0}, \
+ {0x0010, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6), 0}, \
+ {0x0000, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \
+ PWR_CMD_WRITE, BIT(5), BIT(5)}, \
+ {0x0020, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \
+ PWR_CMD_WRITE, BIT(0), 0},
+
+#define RTL8723B_TRANS_CARDEMU_TO_SUS \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4) | BIT(3), (BIT(4) | BIT(3))}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \
+ PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3)}, \
+ {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)}, \
+ {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x20}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3) | BIT(4)},\
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), BIT(0)}, \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), 0},
+
+#define RTL8723B_TRANS_SUS_TO_CARDEMU \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3) | BIT(7), 0}, \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), 0}, \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), BIT(1)}, \
+ {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0},
+
+#define RTL8723B_TRANS_CARDEMU_TO_CARDDIS \
+ {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x20}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(2), BIT(2)}, \
+ {0x004A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 1}, \
+ {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)}, \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), BIT(0)}, \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), 0},
+
+#define RTL8723B_TRANS_CARDDIS_TO_CARDEMU \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3) | BIT(7), 0}, \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), 0}, \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), BIT(1)}, \
+ {0x004A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0}, \
+ {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, \
+ {0x0301, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0},
+
+#define RTL8723B_TRANS_CARDEMU_TO_PDN \
+ {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)}, \
+ {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_SDIO_MSK | PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, \
+ PWR_CMD_WRITE, 0xFF, 0x20}, \
+ {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), BIT(7)},
+
+#define RTL8723B_TRANS_PDN_TO_CARDEMU \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0},
+
+#define RTL8723B_TRANS_ACT_TO_LPS \
+ {0x0301, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, \
+ {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, \
+ {0x05F8, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \
+ {0x05F9, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \
+ {0x05FA, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \
+ {0x05FB, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_US}, \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0}, \
+ {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x03}, \
+ {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0}, \
+ {0x0093, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x00}, \
+ {0x0553, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), BIT(5)},
+
+#define RTL8723B_TRANS_LPS_TO_ACT \
+ {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_WRITE, 0xFF, 0x84}, \
+ {0xFE58, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, \
+ {0x0361, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_MS}, \
+ {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, \
+ {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(7), 0}, \
+ {0x0029, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6)|BIT(7), 0}, \
+ {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \
+ {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1) | BIT(0), BIT(1) | BIT(0)}, \
+ {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0},
+
+#define RTL8723B_TRANS_END \
+ {0xFFFF, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, 0, \
+ PWR_CMD_END, 0, 0},
+
+extern struct wlan_pwr_cfg rtl8723B_power_on_flow
+ [RTL8723B_TRANS_CARDEMU_TO_ACT_STEPS +
+ RTL8723B_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723B_radio_off_flow
+ [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8723B_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723B_card_disable_flow
+ [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS +
+ RTL8723B_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723B_card_enable_flow
+ [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS +
+ RTL8723B_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723B_suspend_flow
+ [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8723B_TRANS_CARDEMU_TO_SUS_STEPS +
+ RTL8723B_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723B_resume_flow
+ [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8723B_TRANS_CARDEMU_TO_SUS_STEPS +
+ RTL8723B_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723B_hwpdn_flow
+ [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS +
+ RTL8723B_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723B_enter_lps_flow
+ [RTL8723B_TRANS_ACT_TO_LPS_STEPS +
+ RTL8723B_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723B_leave_lps_flow
+ [RTL8723B_TRANS_LPS_TO_ACT_STEPS +
+ RTL8723B_TRANS_END_STEPS];
+
+/* RTL8723 Power Configuration CMDs for PCIe interface */
+#define RTL8723_NIC_PWR_ON_FLOW rtl8723B_power_on_flow
+#define RTL8723_NIC_RF_OFF_FLOW rtl8723B_radio_off_flow
+#define RTL8723_NIC_DISABLE_FLOW rtl8723B_card_disable_flow
+#define RTL8723_NIC_ENABLE_FLOW rtl8723B_card_enable_flow
+#define RTL8723_NIC_SUSPEND_FLOW rtl8723B_suspend_flow
+#define RTL8723_NIC_RESUME_FLOW rtl8723B_resume_flow
+#define RTL8723_NIC_PDN_FLOW rtl8723B_hwpdn_flow
+#define RTL8723_NIC_LPS_ENTER_FLOW rtl8723B_enter_lps_flow
+#define RTL8723_NIC_LPS_LEAVE_FLOW rtl8723B_leave_lps_flow
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.c b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.c
new file mode 100644
index 000000000000..e4a507a756fb
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.c
@@ -0,0 +1,140 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "pwrseq.h"
+
+/* Description:
+ * This routine deal with the Power Configuration CMDs
+ * parsing for RTL8723/RTL8188E Series IC.
+ * Assumption:
+ * We should follow specific format which was released from HW SD.
+ *
+ * 2011.07.07, added by Roger.
+ */
+bool rtlbe_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version,
+ u8 fab_version, u8 interface_type,
+ struct wlan_pwr_cfg pwrcfgcmd[])
+
+{
+ struct wlan_pwr_cfg pwr_cfg_cmd = {0};
+ bool b_polling_bit = false;
+ u32 ary_idx = 0;
+ u8 value = 0;
+ u32 offset = 0;
+ u32 polling_count = 0;
+ u32 max_polling_cnt = 5000;
+
+ do {
+ pwr_cfg_cmd = pwrcfgcmd[ary_idx];
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtlbe_hal_pwrseqcmdparsing(): "
+ "offset(%#x),cut_msk(%#x), fab_msk(%#x),"
+ "interface_msk(%#x), base(%#x), "
+ "cmd(%#x), msk(%#x), value(%#x)\n",
+ GET_PWR_CFG_OFFSET(pwr_cfg_cmd),
+ GET_PWR_CFG_CUT_MASK(pwr_cfg_cmd),
+ GET_PWR_CFG_FAB_MASK(pwr_cfg_cmd),
+ GET_PWR_CFG_INTF_MASK(pwr_cfg_cmd),
+ GET_PWR_CFG_BASE(pwr_cfg_cmd),
+ GET_PWR_CFG_CMD(pwr_cfg_cmd),
+ GET_PWR_CFG_MASK(pwr_cfg_cmd),
+ GET_PWR_CFG_VALUE(pwr_cfg_cmd));
+
+ if ((GET_PWR_CFG_FAB_MASK(pwr_cfg_cmd)&fab_version) &&
+ (GET_PWR_CFG_CUT_MASK(pwr_cfg_cmd)&cut_version) &&
+ (GET_PWR_CFG_INTF_MASK(pwr_cfg_cmd)&interface_type)) {
+ switch (GET_PWR_CFG_CMD(pwr_cfg_cmd)) {
+ case PWR_CMD_READ:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtlbe_hal_pwrseqcmdparsing(): "
+ "PWR_CMD_READ\n");
+ break;
+ case PWR_CMD_WRITE:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtlbe_hal_pwrseqcmdparsing(): "
+ "PWR_CMD_WRITE\n");
+ offset = GET_PWR_CFG_OFFSET(pwr_cfg_cmd);
+
+ /*Read the value from system register*/
+ value = rtl_read_byte(rtlpriv, offset);
+ value &= (~(GET_PWR_CFG_MASK(pwr_cfg_cmd)));
+ value = value | (GET_PWR_CFG_VALUE(pwr_cfg_cmd)
+ & GET_PWR_CFG_MASK(pwr_cfg_cmd));
+
+ /*Write the value back to sytem register*/
+ rtl_write_byte(rtlpriv, offset, value);
+ break;
+ case PWR_CMD_POLLING:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtlbe_hal_pwrseqcmdparsing(): "
+ "PWR_CMD_POLLING\n");
+ b_polling_bit = false;
+ offset = GET_PWR_CFG_OFFSET(pwr_cfg_cmd);
+
+ do {
+ value = rtl_read_byte(rtlpriv, offset);
+
+ value &= GET_PWR_CFG_MASK(pwr_cfg_cmd);
+ if (value ==
+ (GET_PWR_CFG_VALUE(pwr_cfg_cmd) &
+ GET_PWR_CFG_MASK(pwr_cfg_cmd)))
+ b_polling_bit = true;
+ else
+ udelay(10);
+
+ if (polling_count++ > max_polling_cnt)
+ return false;
+
+ } while (!b_polling_bit);
+ break;
+ case PWR_CMD_DELAY:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtlbe_hal_pwrseqcmdparsing(): "
+ "PWR_CMD_DELAY\n");
+ if (GET_PWR_CFG_VALUE(pwr_cfg_cmd) ==
+ PWRSEQ_DELAY_US)
+ udelay(GET_PWR_CFG_OFFSET(pwr_cfg_cmd));
+ else
+ mdelay(GET_PWR_CFG_OFFSET(pwr_cfg_cmd));
+ break;
+ case PWR_CMD_END:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "rtlbe_hal_pwrseqcmdparsing(): "
+ "PWR_CMD_END\n");
+ return true;
+ break;
+ default:
+ RT_ASSERT(false,
+ "rtlbe_hal_pwrseqcmdparsing(): "
+ "Unknown CMD!!\n");
+ break;
+ }
+ }
+
+ ary_idx++;
+ } while (1);
+
+ return true;
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.h b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.h
new file mode 100644
index 000000000000..ce14a3b5cb71
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.h
@@ -0,0 +1,95 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_PWRSEQCMD_H__
+#define __RTL8723BE_PWRSEQCMD_H__
+
+#include "../wifi.h"
+/*---------------------------------------------*/
+/*The value of cmd: 4 bits */
+/*---------------------------------------------*/
+#define PWR_CMD_READ 0x00
+#define PWR_CMD_WRITE 0x01
+#define PWR_CMD_POLLING 0x02
+#define PWR_CMD_DELAY 0x03
+#define PWR_CMD_END 0x04
+
+/* define the base address of each block */
+#define PWR_BASEADDR_MAC 0x00
+#define PWR_BASEADDR_USB 0x01
+#define PWR_BASEADDR_PCIE 0x02
+#define PWR_BASEADDR_SDIO 0x03
+
+#define PWR_INTF_SDIO_MSK BIT(0)
+#define PWR_INTF_USB_MSK BIT(1)
+#define PWR_INTF_PCI_MSK BIT(2)
+#define PWR_INTF_ALL_MSK (BIT(0) | BIT(1) | BIT(2) | BIT(3))
+
+#define PWR_FAB_TSMC_MSK BIT(0)
+#define PWR_FAB_UMC_MSK BIT(1)
+#define PWR_FAB_ALL_MSK (BIT(0) | BIT(1) | BIT(2) | BIT(3))
+
+#define PWR_CUT_TESTCHIP_MSK BIT(0)
+#define PWR_CUT_A_MSK BIT(1)
+#define PWR_CUT_B_MSK BIT(2)
+#define PWR_CUT_C_MSK BIT(3)
+#define PWR_CUT_D_MSK BIT(4)
+#define PWR_CUT_E_MSK BIT(5)
+#define PWR_CUT_F_MSK BIT(6)
+#define PWR_CUT_G_MSK BIT(7)
+#define PWR_CUT_ALL_MSK 0xFF
+
+
+enum pwrseq_delay_unit {
+ PWRSEQ_DELAY_US,
+ PWRSEQ_DELAY_MS,
+};
+
+struct wlan_pwr_cfg {
+ u16 offset;
+ u8 cut_msk;
+ u8 fab_msk:4;
+ u8 interface_msk:4;
+ u8 base:4;
+ u8 cmd:4;
+ u8 msk;
+ u8 value;
+
+};
+
+#define GET_PWR_CFG_OFFSET(__PWR_CMD) __PWR_CMD.offset
+#define GET_PWR_CFG_CUT_MASK(__PWR_CMD) __PWR_CMD.cut_msk
+#define GET_PWR_CFG_FAB_MASK(__PWR_CMD) __PWR_CMD.fab_msk
+#define GET_PWR_CFG_INTF_MASK(__PWR_CMD) __PWR_CMD.interface_msk
+#define GET_PWR_CFG_BASE(__PWR_CMD) __PWR_CMD.base
+#define GET_PWR_CFG_CMD(__PWR_CMD) __PWR_CMD.cmd
+#define GET_PWR_CFG_MASK(__PWR_CMD) __PWR_CMD.msk
+#define GET_PWR_CFG_VALUE(__PWR_CMD) __PWR_CMD.value
+
+bool rtlbe_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version,
+ u8 fab_version, u8 interface_type,
+ struct wlan_pwr_cfg pwrcfgcmd[]);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/reg.h b/drivers/net/wireless/rtlwifi/rtl8723be/reg.h
new file mode 100644
index 000000000000..4c653fab8795
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/reg.h
@@ -0,0 +1,2277 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_REG_H__
+#define __RTL8723BE_REG_H__
+
+#define TXPKT_BUF_SELECT 0x69
+#define RXPKT_BUF_SELECT 0xA5
+#define DISABLE_TRXPKT_BUF_ACCESS 0x0
+
+#define REG_SYS_ISO_CTRL 0x0000
+#define REG_SYS_FUNC_EN 0x0002
+#define REG_APS_FSMCO 0x0004
+#define REG_SYS_CLKR 0x0008
+#define REG_9346CR 0x000A
+#define REG_EE_VPD 0x000C
+#define REG_AFE_MISC 0x0010
+#define REG_SPS0_CTRL 0x0011
+#define REG_SPS_OCP_CFG 0x0018
+#define REG_RSV_CTRL 0x001C
+#define REG_RF_CTRL 0x001F
+#define REG_LDOA15_CTRL 0x0020
+#define REG_LDOV12D_CTRL 0x0021
+#define REG_LDOHCI12_CTRL 0x0022
+#define REG_LPLDO_CTRL 0x0023
+#define REG_AFE_XTAL_CTRL 0x0024
+/* 1.5v for 8188EE test chip, 1.4v for MP chip */
+#define REG_AFE_LDO_CTRL 0x0027
+#define REG_AFE_PLL_CTRL 0x0028
+#define REG_MAC_PHY_CTRL 0x002c
+#define REG_EFUSE_CTRL 0x0030
+#define REG_EFUSE_TEST 0x0034
+#define REG_PWR_DATA 0x0038
+#define REG_CAL_TIMER 0x003C
+#define REG_ACLK_MON 0x003E
+#define REG_GPIO_MUXCFG 0x0040
+#define REG_GPIO_IO_SEL 0x0042
+#define REG_MAC_PINMUX_CFG 0x0043
+#define REG_GPIO_PIN_CTRL 0x0044
+#define REG_GPIO_INTM 0x0048
+#define REG_LEDCFG0 0x004C
+#define REG_LEDCFG1 0x004D
+#define REG_LEDCFG2 0x004E
+#define REG_LEDCFG3 0x004F
+#define REG_FSIMR 0x0050
+#define REG_FSISR 0x0054
+#define REG_HSIMR 0x0058
+#define REG_HSISR 0x005c
+#define REG_GPIO_PIN_CTRL_2 0x0060
+#define REG_GPIO_IO_SEL_2 0x0062
+#define REG_MULTI_FUNC_CTRL 0x0068
+#define REG_GPIO_OUTPUT 0x006c
+#define REG_AFE_XTAL_CTRL_EXT 0x0078
+#define REG_XCK_OUT_CTRL 0x007c
+#define REG_MCUFWDL 0x0080
+#define REG_WOL_EVENT 0x0081
+#define REG_MCUTSTCFG 0x0084
+
+
+#define REG_HIMR 0x00B0
+#define REG_HISR 0x00B4
+#define REG_HIMRE 0x00B8
+#define REG_HISRE 0x00BC
+
+#define REG_EFUSE_ACCESS 0x00CF
+
+#define REG_BIST_SCAN 0x00D0
+#define REG_BIST_RPT 0x00D4
+#define REG_BIST_ROM_RPT 0x00D8
+#define REG_USB_SIE_INTF 0x00E0
+#define REG_PCIE_MIO_INTF 0x00E4
+#define REG_PCIE_MIO_INTD 0x00E8
+#define REG_HPON_FSM 0x00EC
+#define REG_SYS_CFG 0x00F0
+#define REG_GPIO_OUTSTS 0x00F4
+#define REG_SYS_CFG1 0x00F0
+#define REG_ROM_VERSION 0x00FD
+
+#define REG_CR 0x0100
+#define REG_PBP 0x0104
+#define REG_PKT_BUFF_ACCESS_CTRL 0x0106
+#define REG_TRXDMA_CTRL 0x010C
+#define REG_TRXFF_BNDY 0x0114
+#define REG_TRXFF_STATUS 0x0118
+#define REG_RXFF_PTR 0x011C
+
+#define REG_CPWM 0x012F
+#define REG_FWIMR 0x0130
+#define REG_FWISR 0x0134
+#define REG_PKTBUF_DBG_CTRL 0x0140
+#define REG_PKTBUF_DBG_DATA_L 0x0144
+#define REG_PKTBUF_DBG_DATA_H 0x0148
+#define REG_RXPKTBUF_CTRL (REG_PKTBUF_DBG_CTRL + 2)
+
+#define REG_TC0_CTRL 0x0150
+#define REG_TC1_CTRL 0x0154
+#define REG_TC2_CTRL 0x0158
+#define REG_TC3_CTRL 0x015C
+#define REG_TC4_CTRL 0x0160
+#define REG_TCUNIT_BASE 0x0164
+#define REG_MBIST_START 0x0174
+#define REG_MBIST_DONE 0x0178
+#define REG_MBIST_FAIL 0x017C
+#define REG_32K_CTRL 0x0194
+#define REG_C2HEVT_MSG_NORMAL 0x01A0
+#define REG_C2HEVT_CLEAR 0x01AF
+#define REG_C2HEVT_MSG_TEST 0x01B8
+#define REG_MCUTST_1 0x01c0
+#define REG_FMETHR 0x01C8
+#define REG_HMETFR 0x01CC
+#define REG_HMEBOX_0 0x01D0
+#define REG_HMEBOX_1 0x01D4
+#define REG_HMEBOX_2 0x01D8
+#define REG_HMEBOX_3 0x01DC
+
+#define REG_LLT_INIT 0x01E0
+#define REG_BB_ACCEESS_CTRL 0x01E8
+#define REG_BB_ACCESS_DATA 0x01EC
+
+#define REG_HMEBOX_EXT_0 0x01F0
+#define REG_HMEBOX_EXT_1 0x01F4
+#define REG_HMEBOX_EXT_2 0x01F8
+#define REG_HMEBOX_EXT_3 0x01FC
+
+#define REG_RQPN 0x0200
+#define REG_FIFOPAGE 0x0204
+#define REG_TDECTRL 0x0208
+#define REG_TXDMA_OFFSET_CHK 0x020C
+#define REG_TXDMA_STATUS 0x0210
+#define REG_RQPN_NPQ 0x0214
+
+#define REG_RXDMA_AGG_PG_TH 0x0280
+/* FW shall update this register before FW write RXPKT_RELEASE_POLL to 1 */
+#define REG_FW_UPD_RDPTR 0x0284
+/* Control the RX DMA.*/
+#define REG_RXDMA_CONTROL 0x0286
+/* The number of packets in RXPKTBUF. */
+#define REG_RXPKT_NUM 0x0287
+
+#define REG_PCIE_CTRL_REG 0x0300
+#define REG_INT_MIG 0x0304
+#define REG_BCNQ_DESA 0x0308
+#define REG_HQ_DESA 0x0310
+#define REG_MGQ_DESA 0x0318
+#define REG_VOQ_DESA 0x0320
+#define REG_VIQ_DESA 0x0328
+#define REG_BEQ_DESA 0x0330
+#define REG_BKQ_DESA 0x0338
+#define REG_RX_DESA 0x0340
+
+#define REG_DBI 0x0348
+#define REG_MDIO 0x0354
+#define REG_DBG_SEL 0x0360
+#define REG_PCIE_HRPWM 0x0361
+#define REG_PCIE_HCPWM 0x0363
+#define REG_UART_CTRL 0x0364
+#define REG_WATCH_DOG 0x0368
+#define REG_UART_TX_DESA 0x0370
+#define REG_UART_RX_DESA 0x0378
+
+
+#define REG_HDAQ_DESA_NODEF 0x0000
+#define REG_CMDQ_DESA_NODEF 0x0000
+
+#define REG_VOQ_INFORMATION 0x0400
+#define REG_VIQ_INFORMATION 0x0404
+#define REG_BEQ_INFORMATION 0x0408
+#define REG_BKQ_INFORMATION 0x040C
+#define REG_MGQ_INFORMATION 0x0410
+#define REG_HGQ_INFORMATION 0x0414
+#define REG_BCNQ_INFORMATION 0x0418
+#define REG_TXPKT_EMPTY 0x041A
+
+
+#define REG_CPU_MGQ_INFORMATION 0x041C
+#define REG_FWHW_TXQ_CTRL 0x0420
+#define REG_HWSEQ_CTRL 0x0423
+#define REG_TXPKTBUF_BCNQ_BDNY 0x0424
+#define REG_TXPKTBUF_MGQ_BDNY 0x0425
+#define REG_MULTI_BCNQ_EN 0x0426
+#define REG_MULTI_BCNQ_OFFSET 0x0427
+#define REG_SPEC_SIFS 0x0428
+#define REG_RL 0x042A
+#define REG_DARFRC 0x0430
+#define REG_RARFRC 0x0438
+#define REG_RRSR 0x0440
+#define REG_ARFR0 0x0444
+#define REG_ARFR1 0x0448
+#define REG_ARFR2 0x044C
+#define REG_ARFR3 0x0450
+#define REG_AMPDU_MAX_TIME 0x0456
+#define REG_AGGLEN_LMT 0x0458
+#define REG_AMPDU_MIN_SPACE 0x045C
+#define REG_TXPKTBUF_WMAC_LBK_BF_HD 0x045D
+#define REG_FAST_EDCA_CTRL 0x0460
+#define REG_RD_RESP_PKT_TH 0x0463
+#define REG_INIRTS_RATE_SEL 0x0480
+#define REG_INIDATA_RATE_SEL 0x0484
+#define REG_POWER_STATUS 0x04A4
+#define REG_POWER_STAGE1 0x04B4
+#define REG_POWER_STAGE2 0x04B8
+#define REG_PKT_LIFE_TIME 0x04C0
+#define REG_STBC_SETTING 0x04C4
+#define REG_PROT_MODE_CTRL 0x04C8
+#define REG_BAR_MODE_CTRL 0x04CC
+#define REG_RA_TRY_RATE_AGG_LMT 0x04CF
+#define REG_EARLY_MODE_CONTROL 0x04D0
+#define REG_NQOS_SEQ 0x04DC
+#define REG_QOS_SEQ 0x04DE
+#define REG_NEED_CPU_HANDLE 0x04E0
+#define REG_PKT_LOSE_RPT 0x04E1
+#define REG_PTCL_ERR_STATUS 0x04E2
+#define REG_TX_RPT_CTRL 0x04EC
+#define REG_TX_RPT_TIME 0x04F0
+#define REG_DUMMY 0x04FC
+
+#define REG_EDCA_VO_PARAM 0x0500
+#define REG_EDCA_VI_PARAM 0x0504
+#define REG_EDCA_BE_PARAM 0x0508
+#define REG_EDCA_BK_PARAM 0x050C
+#define REG_BCNTCFG 0x0510
+#define REG_PIFS 0x0512
+#define REG_RDG_PIFS 0x0513
+#define REG_SIFS_CTX 0x0514
+#define REG_SIFS_TRX 0x0516
+#define REG_AGGR_BREAK_TIME 0x051A
+#define REG_SLOT 0x051B
+#define REG_TX_PTCL_CTRL 0x0520
+#define REG_TXPAUSE 0x0522
+#define REG_DIS_TXREQ_CLR 0x0523
+#define REG_RD_CTRL 0x0524
+#define REG_TBTT_PROHIBIT 0x0540
+#define REG_RD_NAV_NXT 0x0544
+#define REG_NAV_PROT_LEN 0x0546
+#define REG_BCN_CTRL 0x0550
+#define REG_USTIME_TSF 0x0551
+#define REG_MBID_NUM 0x0552
+#define REG_DUAL_TSF_RST 0x0553
+#define REG_BCN_INTERVAL 0x0554
+#define REG_MBSSID_BCN_SPACE 0x0554
+#define REG_DRVERLYINT 0x0558
+#define REG_BCNDMATIM 0x0559
+#define REG_ATIMWND 0x055A
+#define REG_BCN_MAX_ERR 0x055D
+#define REG_RXTSF_OFFSET_CCK 0x055E
+#define REG_RXTSF_OFFSET_OFDM 0x055F
+#define REG_TSFTR 0x0560
+#define REG_INIT_TSFTR 0x0564
+#define REG_SECONDARY_CCA_CTRL 0x0577
+#define REG_PSTIMER 0x0580
+#define REG_TIMER0 0x0584
+#define REG_TIMER1 0x0588
+#define REG_ACMHWCTRL 0x05C0
+#define REG_ACMRSTCTRL 0x05C1
+#define REG_ACMAVG 0x05C2
+#define REG_VO_ADMTIME 0x05C4
+#define REG_VI_ADMTIME 0x05C6
+#define REG_BE_ADMTIME 0x05C8
+#define REG_EDCA_RANDOM_GEN 0x05CC
+#define REG_SCH_TXCMD 0x05D0
+
+#define REG_APSD_CTRL 0x0600
+#define REG_BWOPMODE 0x0603
+#define REG_TCR 0x0604
+#define REG_RCR 0x0608
+#define REG_RX_PKT_LIMIT 0x060C
+#define REG_RX_DLK_TIME 0x060D
+#define REG_RX_DRVINFO_SZ 0x060F
+
+#define REG_MACID 0x0610
+#define REG_BSSID 0x0618
+#define REG_MAR 0x0620
+#define REG_MBIDCAMCFG 0x0628
+
+#define REG_USTIME_EDCA 0x0638
+#define REG_MAC_SPEC_SIFS 0x063A
+#define REG_RESP_SIFS_CCK 0x063C
+#define REG_RESP_SIFS_OFDM 0x063E
+#define REG_ACKTO 0x0640
+#define REG_CTS2TO 0x0641
+#define REG_EIFS 0x0642
+
+#define REG_NAV_CTRL 0x0650
+#define REG_BACAMCMD 0x0654
+#define REG_BACAMCONTENT 0x0658
+#define REG_LBDLY 0x0660
+#define REG_FWDLY 0x0661
+#define REG_RXERR_RPT 0x0664
+#define REG_TRXPTCL_CTL 0x0668
+
+#define REG_CAMCMD 0x0670
+#define REG_CAMWRITE 0x0674
+#define REG_CAMREAD 0x0678
+#define REG_CAMDBG 0x067C
+#define REG_SECCFG 0x0680
+
+#define REG_WOW_CTRL 0x0690
+#define REG_PSSTATUS 0x0691
+#define REG_PS_RX_INFO 0x0692
+#define REG_UAPSD_TID 0x0693
+#define REG_LPNAV_CTRL 0x0694
+#define REG_WKFMCAM_NUM 0x0698
+#define REG_WKFMCAM_RWD 0x069C
+#define REG_RXFLTMAP0 0x06A0
+#define REG_RXFLTMAP1 0x06A2
+#define REG_RXFLTMAP2 0x06A4
+#define REG_BCN_PSR_RPT 0x06A8
+#define REG_CALB32K_CTRL 0x06AC
+#define REG_PKT_MON_CTRL 0x06B4
+#define REG_BT_COEX_TABLE 0x06C0
+#define REG_WMAC_RESP_TXINFO 0x06D8
+
+#define REG_USB_INFO 0xFE17
+#define REG_USB_SPECIAL_OPTION 0xFE55
+#define REG_USB_DMA_AGG_TO 0xFE5B
+#define REG_USB_AGG_TO 0xFE5C
+#define REG_USB_AGG_TH 0xFE5D
+
+#define REG_TEST_USB_TXQS 0xFE48
+#define REG_TEST_SIE_VID 0xFE60
+#define REG_TEST_SIE_PID 0xFE62
+#define REG_TEST_SIE_OPTIONAL 0xFE64
+#define REG_TEST_SIE_CHIRP_K 0xFE65
+#define REG_TEST_SIE_PHY 0xFE66
+#define REG_TEST_SIE_MAC_ADDR 0xFE70
+#define REG_TEST_SIE_STRING 0xFE80
+
+#define REG_NORMAL_SIE_VID 0xFE60
+#define REG_NORMAL_SIE_PID 0xFE62
+#define REG_NORMAL_SIE_OPTIONAL 0xFE64
+#define REG_NORMAL_SIE_EP 0xFE65
+#define REG_NORMAL_SIE_PHY 0xFE68
+#define REG_NORMAL_SIE_MAC_ADDR 0xFE70
+#define REG_NORMAL_SIE_STRING 0xFE80
+
+#define CR9346 REG_9346CR
+#define MSR (REG_CR + 2)
+#define ISR REG_HISR
+#define TSFR REG_TSFTR
+
+#define MACIDR0 REG_MACID
+#define MACIDR4 (REG_MACID + 4)
+
+#define PBP REG_PBP
+
+#define IDR0 MACIDR0
+#define IDR4 MACIDR4
+
+#define UNUSED_REGISTER 0x1BF
+#define DCAM UNUSED_REGISTER
+#define PSR UNUSED_REGISTER
+#define BBADDR UNUSED_REGISTER
+#define PHYDATAR UNUSED_REGISTER
+
+#define INVALID_BBRF_VALUE 0x12345678
+
+#define MAX_MSS_DENSITY_2T 0x13
+#define MAX_MSS_DENSITY_1T 0x0A
+
+#define CMDEEPROM_EN BIT(5)
+#define CMDEEPROM_SEL BIT(4)
+#define CMD9346CR_9356SEL BIT(4)
+#define AUTOLOAD_EEPROM (CMDEEPROM_EN | CMDEEPROM_SEL)
+#define AUTOLOAD_EFUSE CMDEEPROM_EN
+
+#define GPIOSEL_GPIO 0
+#define GPIOSEL_ENBT BIT(5)
+
+#define GPIO_IN REG_GPIO_PIN_CTRL
+#define GPIO_OUT (REG_GPIO_PIN_CTRL + 1)
+#define GPIO_IO_SEL (REG_GPIO_PIN_CTRL + 2)
+#define GPIO_MOD (REG_GPIO_PIN_CTRL + 3)
+
+/* 8723/8188E Host System Interrupt Mask Register (offset 0x58, 32 byte) */
+#define HSIMR_GPIO12_0_INT_EN BIT(0)
+#define HSIMR_SPS_OCP_INT_EN BIT(5)
+#define HSIMR_RON_INT_EN BIT(6)
+#define HSIMR_PDN_INT_EN BIT(7)
+#define HSIMR_GPIO9_INT_EN BIT(25)
+
+/* 8723/8188E Host System Interrupt Status Register (offset 0x5C, 32 byte) */
+
+#define HSISR_GPIO12_0_INT BIT(0)
+#define HSISR_SPS_OCP_INT BIT(5)
+#define HSISR_RON_INT_EN BIT(6)
+#define HSISR_PDNINT BIT(7)
+#define HSISR_GPIO9_INT BIT(25)
+
+#define MSR_NOLINK 0x00
+#define MSR_ADHOC 0x01
+#define MSR_INFRA 0x02
+#define MSR_AP 0x03
+
+#define RRSR_RSC_OFFSET 21
+#define RRSR_SHORT_OFFSET 23
+#define RRSR_RSC_BW_40M 0x600000
+#define RRSR_RSC_UPSUBCHNL 0x400000
+#define RRSR_RSC_LOWSUBCHNL 0x200000
+#define RRSR_SHORT 0x800000
+#define RRSR_1M BIT(0)
+#define RRSR_2M BIT(1)
+#define RRSR_5_5M BIT(2)
+#define RRSR_11M BIT(3)
+#define RRSR_6M BIT(4)
+#define RRSR_9M BIT(5)
+#define RRSR_12M BIT(6)
+#define RRSR_18M BIT(7)
+#define RRSR_24M BIT(8)
+#define RRSR_36M BIT(9)
+#define RRSR_48M BIT(10)
+#define RRSR_54M BIT(11)
+#define RRSR_MCS0 BIT(12)
+#define RRSR_MCS1 BIT(13)
+#define RRSR_MCS2 BIT(14)
+#define RRSR_MCS3 BIT(15)
+#define RRSR_MCS4 BIT(16)
+#define RRSR_MCS5 BIT(17)
+#define RRSR_MCS6 BIT(18)
+#define RRSR_MCS7 BIT(19)
+#define BRSR_ACKSHORTPMB BIT(23)
+
+#define RATR_1M 0x00000001
+#define RATR_2M 0x00000002
+#define RATR_55M 0x00000004
+#define RATR_11M 0x00000008
+#define RATR_6M 0x00000010
+#define RATR_9M 0x00000020
+#define RATR_12M 0x00000040
+#define RATR_18M 0x00000080
+#define RATR_24M 0x00000100
+#define RATR_36M 0x00000200
+#define RATR_48M 0x00000400
+#define RATR_54M 0x00000800
+#define RATR_MCS0 0x00001000
+#define RATR_MCS1 0x00002000
+#define RATR_MCS2 0x00004000
+#define RATR_MCS3 0x00008000
+#define RATR_MCS4 0x00010000
+#define RATR_MCS5 0x00020000
+#define RATR_MCS6 0x00040000
+#define RATR_MCS7 0x00080000
+#define RATR_MCS8 0x00100000
+#define RATR_MCS9 0x00200000
+#define RATR_MCS10 0x00400000
+#define RATR_MCS11 0x00800000
+#define RATR_MCS12 0x01000000
+#define RATR_MCS13 0x02000000
+#define RATR_MCS14 0x04000000
+#define RATR_MCS15 0x08000000
+
+#define RATE_1M BIT(0)
+#define RATE_2M BIT(1)
+#define RATE_5_5M BIT(2)
+#define RATE_11M BIT(3)
+#define RATE_6M BIT(4)
+#define RATE_9M BIT(5)
+#define RATE_12M BIT(6)
+#define RATE_18M BIT(7)
+#define RATE_24M BIT(8)
+#define RATE_36M BIT(9)
+#define RATE_48M BIT(10)
+#define RATE_54M BIT(11)
+#define RATE_MCS0 BIT(12)
+#define RATE_MCS1 BIT(13)
+#define RATE_MCS2 BIT(14)
+#define RATE_MCS3 BIT(15)
+#define RATE_MCS4 BIT(16)
+#define RATE_MCS5 BIT(17)
+#define RATE_MCS6 BIT(18)
+#define RATE_MCS7 BIT(19)
+#define RATE_MCS8 BIT(20)
+#define RATE_MCS9 BIT(21)
+#define RATE_MCS10 BIT(22)
+#define RATE_MCS11 BIT(23)
+#define RATE_MCS12 BIT(24)
+#define RATE_MCS13 BIT(25)
+#define RATE_MCS14 BIT(26)
+#define RATE_MCS15 BIT(27)
+
+#define RATE_ALL_CCK (RATR_1M | RATR_2M | RATR_55M | RATR_11M)
+#define RATE_ALL_OFDM_AG (RATR_6M | RATR_9M | RATR_12M | RATR_18M |\
+ RATR_24M | RATR_36M | RATR_48M | RATR_54M)
+#define RATE_ALL_OFDM_1SS (RATR_MCS0 | RATR_MCS1 | RATR_MCS2 |\
+ RATR_MCS3 | RATR_MCS4 | RATR_MCS5 |\
+ RATR_MCS6 | RATR_MCS7)
+#define RATE_ALL_OFDM_2SS (RATR_MCS8 | RATR_MCS9 | RATR_MCS10 |\
+ RATR_MCS11 | RATR_MCS12 | RATR_MCS13 |\
+ RATR_MCS14 | RATR_MCS15)
+
+#define BW_OPMODE_20MHZ BIT(2)
+#define BW_OPMODE_5G BIT(1)
+#define BW_OPMODE_11J BIT(0)
+
+#define CAM_VALID BIT(15)
+#define CAM_NOTVALID 0x0000
+#define CAM_USEDK BIT(5)
+
+#define CAM_NONE 0x0
+#define CAM_WEP40 0x01
+#define CAM_TKIP 0x02
+#define CAM_AES 0x04
+#define CAM_WEP104 0x05
+
+#define TOTAL_CAM_ENTRY 32
+#define HALF_CAM_ENTRY 16
+
+#define CAM_WRITE BIT(16)
+#define CAM_READ 0x00000000
+#define CAM_POLLINIG BIT(31)
+
+#define SCR_USEDK 0x01
+#define SCR_TXSEC_ENABLE 0x02
+#define SCR_RXSEC_ENABLE 0x04
+
+#define WOW_PMEN BIT(0)
+#define WOW_WOMEN BIT(1)
+#define WOW_MAGIC BIT(2)
+#define WOW_UWF BIT(3)
+
+/*********************************************
+* 8723BE IMR/ISR bits
+**********************************************/
+#define IMR_DISABLED 0x0
+/* IMR DW0(0x0060-0063) Bit 0-31 */
+#define IMR_TXCCK BIT(30) /* TXRPT interrupt when
+ * CCX bit of the packet is set
+ */
+#define IMR_PSTIMEOUT BIT(29) /* Power Save Time Out Interrupt */
+#define IMR_GTINT4 BIT(28) /* When GTIMER4 expires,
+ * this bit is set to 1
+ */
+#define IMR_GTINT3 BIT(27) /* When GTIMER3 expires,
+ * this bit is set to 1
+ */
+#define IMR_TBDER BIT(26) /* Transmit Beacon0 Error */
+#define IMR_TBDOK BIT(25) /* Transmit Beacon0 OK */
+#define IMR_TSF_BIT32_TOGGLE BIT(24) /* TSF Timer BIT32 toggle
+ * indication interrupt
+ */
+#define IMR_BCNDMAINT0 BIT(20) /* Beacon DMA Interrupt 0 */
+#define IMR_BCNDOK0 BIT(16) /* Beacon Queue DMA OK0 */
+#define IMR_HSISR_IND_ON_INT BIT(15) /* HSISR Indicator (HSIMR & HSISR is
+ * true, this bit is set to 1)
+ */
+#define IMR_BCNDMAINT_E BIT(14) /* Beacon DMA Interrupt
+ * Extension for Win7
+ */
+#define IMR_ATIMEND BIT(12) /* CTWidnow End or ATIM Window End */
+#define IMR_HISR1_IND_INT BIT(11) /* HISR1 Indicator (HISR1 & HIMR1 is
+ * true, this bit is set to 1)
+ */
+#define IMR_C2HCMD BIT(10) /* CPU to Host Command INT Status,
+ * Write 1 clear
+ */
+#define IMR_CPWM2 BIT(9) /* CPU power Mode exchange INT Status,
+ * Write 1 clear
+ */
+#define IMR_CPWM BIT(8) /* CPU power Mode exchange INT Status,
+ * Write 1 clear
+ */
+#define IMR_HIGHDOK BIT(7) /* High Queue DMA OK */
+#define IMR_MGNTDOK BIT(6) /* Management Queue DMA OK */
+#define IMR_BKDOK BIT(5) /* AC_BK DMA OK */
+#define IMR_BEDOK BIT(4) /* AC_BE DMA OK */
+#define IMR_VIDOK BIT(3) /* AC_VI DMA OK */
+#define IMR_VODOK BIT(2) /* AC_VO DMA OK */
+#define IMR_RDU BIT(1) /* Rx Descriptor Unavailable */
+#define IMR_ROK BIT(0) /* Receive DMA OK */
+
+/* IMR DW1(0x00B4-00B7) Bit 0-31 */
+#define IMR_BCNDMAINT7 BIT(27) /* Beacon DMA Interrupt 7 */
+#define IMR_BCNDMAINT6 BIT(26) /* Beacon DMA Interrupt 6 */
+#define IMR_BCNDMAINT5 BIT(25) /* Beacon DMA Interrupt 5 */
+#define IMR_BCNDMAINT4 BIT(24) /* Beacon DMA Interrupt 4 */
+#define IMR_BCNDMAINT3 BIT(23) /* Beacon DMA Interrupt 3 */
+#define IMR_BCNDMAINT2 BIT(22) /* Beacon DMA Interrupt 2 */
+#define IMR_BCNDMAINT1 BIT(21) /* Beacon DMA Interrupt 1 */
+#define IMR_BCNDOK7 BIT(20) /* Beacon Queue DMA OK Interrup 7 */
+#define IMR_BCNDOK6 BIT(19) /* Beacon Queue DMA OK Interrup 6 */
+#define IMR_BCNDOK5 BIT(18) /* Beacon Queue DMA OK Interrup 5 */
+#define IMR_BCNDOK4 BIT(17) /* Beacon Queue DMA OK Interrup 4 */
+#define IMR_BCNDOK3 BIT(16) /* Beacon Queue DMA OK Interrup 3 */
+#define IMR_BCNDOK2 BIT(15) /* Beacon Queue DMA OK Interrup 2 */
+#define IMR_BCNDOK1 BIT(14) /* Beacon Queue DMA OK Interrup 1 */
+#define IMR_ATIMEND_E BIT(13) /* ATIM Window End Extension for Win7 */
+#define IMR_TXERR BIT(11) /* Tx Error Flag Interrupt Status,
+ * write 1 clear.
+ */
+#define IMR_RXERR BIT(10) /* Rx Error Flag INT Status,
+ * Write 1 clear
+ */
+#define IMR_TXFOVW BIT(9) /* Transmit FIFO Overflow */
+#define IMR_RXFOVW BIT(8) /* Receive FIFO Overflow */
+
+#define HWSET_MAX_SIZE 512
+#define EFUSE_MAX_SECTION 64
+#define EFUSE_REAL_CONTENT_LEN 256
+#define EFUSE_OOB_PROTECT_BYTES 18 /* PG data exclude header,
+ * dummy 7 bytes frome CP test
+ * and reserved 1byte.
+ */
+
+#define EEPROM_DEFAULT_TSSI 0x0
+#define EEPROM_DEFAULT_TXPOWERDIFF 0x0
+#define EEPROM_DEFAULT_CRYSTALCAP 0x5
+#define EEPROM_DEFAULT_BOARDTYPE 0x02
+#define EEPROM_DEFAULT_TXPOWER 0x1010
+#define EEPROM_DEFAULT_HT2T_TXPWR 0x10
+
+#define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x3
+#define EEPROM_DEFAULT_THERMALMETER 0x18
+#define EEPROM_DEFAULT_ANTTXPOWERDIFF 0x0
+#define EEPROM_DEFAULT_TXPWDIFF_CRYSTALCAP 0x5
+#define EEPROM_DEFAULT_TXPOWERLEVEL 0x22
+#define EEPROM_DEFAULT_HT40_2SDIFF 0x0
+#define EEPROM_DEFAULT_HT20_DIFF 2
+#define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x3
+#define EEPROM_DEFAULT_HT40_PWRMAXOFFSET 0
+#define EEPROM_DEFAULT_HT20_PWRMAXOFFSET 0
+
+#define RF_OPTION1 0x79
+#define RF_OPTION2 0x7A
+#define RF_OPTION3 0x7B
+#define RF_OPTION4 0xC3
+
+#define EEPROM_DEFAULT_PID 0x1234
+#define EEPROM_DEFAULT_VID 0x5678
+#define EEPROM_DEFAULT_CUSTOMERID 0xAB
+#define EEPROM_DEFAULT_SUBCUSTOMERID 0xCD
+#define EEPROM_DEFAULT_VERSION 0
+
+#define EEPROM_CHANNEL_PLAN_FCC 0x0
+#define EEPROM_CHANNEL_PLAN_IC 0x1
+#define EEPROM_CHANNEL_PLAN_ETSI 0x2
+#define EEPROM_CHANNEL_PLAN_SPAIN 0x3
+#define EEPROM_CHANNEL_PLAN_FRANCE 0x4
+#define EEPROM_CHANNEL_PLAN_MKK 0x5
+#define EEPROM_CHANNEL_PLAN_MKK1 0x6
+#define EEPROM_CHANNEL_PLAN_ISRAEL 0x7
+#define EEPROM_CHANNEL_PLAN_TELEC 0x8
+#define EEPROM_CHANNEL_PLAN_GLOBAL_DOMAIN 0x9
+#define EEPROM_CHANNEL_PLAN_WORLD_WIDE_13 0xA
+#define EEPROM_CHANNEL_PLAN_NCC 0xB
+#define EEPROM_CHANNEL_PLAN_BY_HW_MASK 0x80
+
+#define EEPROM_CID_DEFAULT 0x0
+#define EEPROM_CID_TOSHIBA 0x4
+#define EEPROM_CID_CCX 0x10
+#define EEPROM_CID_QMI 0x0D
+#define EEPROM_CID_WHQL 0xFE
+
+#define RTL8723BE_EEPROM_ID 0x8129
+
+#define EEPROM_HPON 0x02
+#define EEPROM_CLK 0x06
+#define EEPROM_TESTR 0x08
+
+
+#define EEPROM_TXPOWERCCK 0x10
+#define EEPROM_TXPOWERHT40_1S 0x16
+#define EEPROM_TXPOWERHT20DIFF 0x1B
+#define EEPROM_TXPOWER_OFDMDIFF 0x1B
+
+
+
+#define EEPROM_TX_PWR_INX 0x10
+
+#define EEPROM_CHANNELPLAN 0xB8
+#define EEPROM_XTAL_8723BE 0xB9
+#define EEPROM_THERMAL_METER_88E 0xBA
+#define EEPROM_IQK_LCK_88E 0xBB
+
+#define EEPROM_RF_BOARD_OPTION_88E 0xC1
+#define EEPROM_RF_FEATURE_OPTION_88E 0xC2
+#define EEPROM_RF_BT_SETTING_88E 0xC3
+#define EEPROM_VERSION 0xC4
+#define EEPROM_CUSTOMER_ID 0xC5
+#define EEPROM_RF_ANTENNA_OPT_88E 0xC9
+
+#define EEPROM_MAC_ADDR 0xD0
+#define EEPROM_VID 0xD6
+#define EEPROM_DID 0xD8
+#define EEPROM_SVID 0xDA
+#define EEPROM_SMID 0xDC
+
+#define STOPBECON BIT(6)
+#define STOPHIGHT BIT(5)
+#define STOPMGT BIT(4)
+#define STOPVO BIT(3)
+#define STOPVI BIT(2)
+#define STOPBE BIT(1)
+#define STOPBK BIT(0)
+
+#define RCR_APPFCS BIT(31)
+#define RCR_APP_MIC BIT(30)
+#define RCR_APP_ICV BIT(29)
+#define RCR_APP_PHYST_RXFF BIT(28)
+#define RCR_APP_BA_SSN BIT(27)
+#define RCR_ENMBID BIT(24)
+#define RCR_LSIGEN BIT(23)
+#define RCR_MFBEN BIT(22)
+#define RCR_HTC_LOC_CTRL BIT(14)
+#define RCR_AMF BIT(13)
+#define RCR_ACF BIT(12)
+#define RCR_ADF BIT(11)
+#define RCR_AICV BIT(9)
+#define RCR_ACRC32 BIT(8)
+#define RCR_CBSSID_BCN BIT(7)
+#define RCR_CBSSID_DATA BIT(6)
+#define RCR_CBSSID RCR_CBSSID_DATA
+#define RCR_APWRMGT BIT(5)
+#define RCR_ADD3 BIT(4)
+#define RCR_AB BIT(3)
+#define RCR_AM BIT(2)
+#define RCR_APM BIT(1)
+#define RCR_AAP BIT(0)
+#define RCR_MXDMA_OFFSET 8
+#define RCR_FIFO_OFFSET 13
+
+#define RSV_CTRL 0x001C
+#define RD_CTRL 0x0524
+
+#define REG_USB_INFO 0xFE17
+#define REG_USB_SPECIAL_OPTION 0xFE55
+#define REG_USB_DMA_AGG_TO 0xFE5B
+#define REG_USB_AGG_TO 0xFE5C
+#define REG_USB_AGG_TH 0xFE5D
+
+#define REG_USB_VID 0xFE60
+#define REG_USB_PID 0xFE62
+#define REG_USB_OPTIONAL 0xFE64
+#define REG_USB_CHIRP_K 0xFE65
+#define REG_USB_PHY 0xFE66
+#define REG_USB_MAC_ADDR 0xFE70
+#define REG_USB_HRPWM 0xFE58
+#define REG_USB_HCPWM 0xFE57
+
+#define SW18_FPWM BIT(3)
+
+#define ISO_MD2PP BIT(0)
+#define ISO_UA2USB BIT(1)
+#define ISO_UD2CORE BIT(2)
+#define ISO_PA2PCIE BIT(3)
+#define ISO_PD2CORE BIT(4)
+#define ISO_IP2MAC BIT(5)
+#define ISO_DIOP BIT(6)
+#define ISO_DIOE BIT(7)
+#define ISO_EB2CORE BIT(8)
+#define ISO_DIOR BIT(9)
+
+#define PWC_EV25V BIT(14)
+#define PWC_EV12V BIT(15)
+
+#define FEN_BBRSTB BIT(0)
+#define FEN_BB_GLB_RSTN BIT(1)
+#define FEN_USBA BIT(2)
+#define FEN_UPLL BIT(3)
+#define FEN_USBD BIT(4)
+#define FEN_DIO_PCIE BIT(5)
+#define FEN_PCIEA BIT(6)
+#define FEN_PPLL BIT(7)
+#define FEN_PCIED BIT(8)
+#define FEN_DIOE BIT(9)
+#define FEN_CPUEN BIT(10)
+#define FEN_DCORE BIT(11)
+#define FEN_ELDR BIT(12)
+#define FEN_DIO_RF BIT(13)
+#define FEN_HWPDN BIT(14)
+#define FEN_MREGEN BIT(15)
+
+#define PFM_LDALL BIT(0)
+#define PFM_ALDN BIT(1)
+#define PFM_LDKP BIT(2)
+#define PFM_WOWL BIT(3)
+#define ENPDN BIT(4)
+#define PDN_PL BIT(5)
+#define APFM_ONMAC BIT(8)
+#define APFM_OFF BIT(9)
+#define APFM_RSM BIT(10)
+#define AFSM_HSUS BIT(11)
+#define AFSM_PCIE BIT(12)
+#define APDM_MAC BIT(13)
+#define APDM_HOST BIT(14)
+#define APDM_HPDN BIT(15)
+#define RDY_MACON BIT(16)
+#define SUS_HOST BIT(17)
+#define ROP_ALD BIT(20)
+#define ROP_PWR BIT(21)
+#define ROP_SPS BIT(22)
+#define SOP_MRST BIT(25)
+#define SOP_FUSE BIT(26)
+#define SOP_ABG BIT(27)
+#define SOP_AMB BIT(28)
+#define SOP_RCK BIT(29)
+#define SOP_A8M BIT(30)
+#define XOP_BTCK BIT(31)
+
+#define ANAD16V_EN BIT(0)
+#define ANA8M BIT(1)
+#define MACSLP BIT(4)
+#define LOADER_CLK_EN BIT(5)
+#define _80M_SSC_DIS BIT(7)
+#define _80M_SSC_EN_HO BIT(8)
+#define PHY_SSC_RSTB BIT(9)
+#define SEC_CLK_EN BIT(10)
+#define MAC_CLK_EN BIT(11)
+#define SYS_CLK_EN BIT(12)
+#define RING_CLK_EN BIT(13)
+
+#define BOOT_FROM_EEPROM BIT(4)
+#define EEPROM_EN BIT(5)
+
+#define AFE_BGEN BIT(0)
+#define AFE_MBEN BIT(1)
+#define MAC_ID_EN BIT(7)
+
+#define WLOCK_ALL BIT(0)
+#define WLOCK_00 BIT(1)
+#define WLOCK_04 BIT(2)
+#define WLOCK_08 BIT(3)
+#define WLOCK_40 BIT(4)
+#define R_DIS_PRST_0 BIT(5)
+#define R_DIS_PRST_1 BIT(6)
+#define LOCK_ALL_EN BIT(7)
+
+#define RF_EN BIT(0)
+#define RF_RSTB BIT(1)
+#define RF_SDMRSTB BIT(2)
+
+#define LDA15_EN BIT(0)
+#define LDA15_STBY BIT(1)
+#define LDA15_OBUF BIT(2)
+#define LDA15_REG_VOS BIT(3)
+#define _LDA15_VOADJ(x) (((x) & 0x7) << 4)
+
+#define LDV12_EN BIT(0)
+#define LDV12_SDBY BIT(1)
+#define LPLDO_HSM BIT(2)
+#define LPLDO_LSM_DIS BIT(3)
+#define _LDV12_VADJ(x) (((x) & 0xF) << 4)
+
+#define XTAL_EN BIT(0)
+#define XTAL_BSEL BIT(1)
+#define _XTAL_BOSC(x) (((x) & 0x3) << 2)
+#define _XTAL_CADJ(x) (((x) & 0xF) << 4)
+#define XTAL_GATE_USB BIT(8)
+#define _XTAL_USB_DRV(x) (((x) & 0x3) << 9)
+#define XTAL_GATE_AFE BIT(11)
+#define _XTAL_AFE_DRV(x) (((x) & 0x3) << 12)
+#define XTAL_RF_GATE BIT(14)
+#define _XTAL_RF_DRV(x) (((x) & 0x3) << 15)
+#define XTAL_GATE_DIG BIT(17)
+#define _XTAL_DIG_DRV(x) (((x) & 0x3) << 18)
+#define XTAL_BT_GATE BIT(20)
+#define _XTAL_BT_DRV(x) (((x) & 0x3) << 21)
+#define _XTAL_GPIO(x) (((x) & 0x7) << 23)
+
+#define CKDLY_AFE BIT(26)
+#define CKDLY_USB BIT(27)
+#define CKDLY_DIG BIT(28)
+#define CKDLY_BT BIT(29)
+
+#define APLL_EN BIT(0)
+#define APLL_320_EN BIT(1)
+#define APLL_FREF_SEL BIT(2)
+#define APLL_EDGE_SEL BIT(3)
+#define APLL_WDOGB BIT(4)
+#define APLL_LPFEN BIT(5)
+
+#define APLL_REF_CLK_13MHZ 0x1
+#define APLL_REF_CLK_19_2MHZ 0x2
+#define APLL_REF_CLK_20MHZ 0x3
+#define APLL_REF_CLK_25MHZ 0x4
+#define APLL_REF_CLK_26MHZ 0x5
+#define APLL_REF_CLK_38_4MHZ 0x6
+#define APLL_REF_CLK_40MHZ 0x7
+
+#define APLL_320EN BIT(14)
+#define APLL_80EN BIT(15)
+#define APLL_1MEN BIT(24)
+
+#define ALD_EN BIT(18)
+#define EF_PD BIT(19)
+#define EF_FLAG BIT(31)
+
+#define EF_TRPT BIT(7)
+#define LDOE25_EN BIT(31)
+
+#define RSM_EN BIT(0)
+#define TIMER_EN BIT(4)
+
+#define TRSW0EN BIT(2)
+#define TRSW1EN BIT(3)
+#define EROM_EN BIT(4)
+#define ENBT BIT(5)
+#define ENUART BIT(8)
+#define UART_910 BIT(9)
+#define ENPMAC BIT(10)
+#define SIC_SWRST BIT(11)
+#define ENSIC BIT(12)
+#define SIC_23 BIT(13)
+#define ENHDP BIT(14)
+#define SIC_LBK BIT(15)
+
+#define LED0PL BIT(4)
+#define LED1PL BIT(12)
+#define LED0DIS BIT(7)
+
+#define MCUFWDL_EN BIT(0)
+#define MCUFWDL_RDY BIT(1)
+#define FWDL_CHKSUM_RPT BIT(2)
+#define MACINI_RDY BIT(3)
+#define BBINI_RDY BIT(4)
+#define RFINI_RDY BIT(5)
+#define WINTINI_RDY BIT(6)
+#define CPRST BIT(23)
+
+#define XCLK_VLD BIT(0)
+#define ACLK_VLD BIT(1)
+#define UCLK_VLD BIT(2)
+#define PCLK_VLD BIT(3)
+#define PCIRSTB BIT(4)
+#define V15_VLD BIT(5)
+#define TRP_B15V_EN BIT(7)
+#define SIC_IDLE BIT(8)
+#define BD_MAC2 BIT(9)
+#define BD_MAC1 BIT(10)
+#define IC_MACPHY_MODE BIT(11)
+#define VENDOR_ID BIT(19)
+#define PAD_HWPD_IDN BIT(22)
+#define TRP_VAUX_EN BIT(23)
+#define TRP_BT_EN BIT(24)
+#define BD_PKG_SEL BIT(25)
+#define BD_HCI_SEL BIT(26)
+#define TYPE_ID BIT(27)
+
+#define CHIP_VER_RTL_MASK 0xF000
+#define CHIP_VER_RTL_SHIFT 12
+
+#define REG_LBMODE (REG_CR + 3)
+
+#define HCI_TXDMA_EN BIT(0)
+#define HCI_RXDMA_EN BIT(1)
+#define TXDMA_EN BIT(2)
+#define RXDMA_EN BIT(3)
+#define PROTOCOL_EN BIT(4)
+#define SCHEDULE_EN BIT(5)
+#define MACTXEN BIT(6)
+#define MACRXEN BIT(7)
+#define ENSWBCN BIT(8)
+#define ENSEC BIT(9)
+
+#define _NETTYPE(x) (((x) & 0x3) << 16)
+#define MASK_NETTYPE 0x30000
+#define NT_NO_LINK 0x0
+#define NT_LINK_AD_HOC 0x1
+#define NT_LINK_AP 0x2
+#define NT_AS_AP 0x3
+
+#define _LBMODE(x) (((x) & 0xF) << 24)
+#define MASK_LBMODE 0xF000000
+#define LOOPBACK_NORMAL 0x0
+#define LOOPBACK_IMMEDIATELY 0xB
+#define LOOPBACK_MAC_DELAY 0x3
+#define LOOPBACK_PHY 0x1
+#define LOOPBACK_DMA 0x7
+
+#define GET_RX_PAGE_SIZE(value) ((value) & 0xF)
+#define GET_TX_PAGE_SIZE(value) (((value) & 0xF0) >> 4)
+#define _PSRX_MASK 0xF
+#define _PSTX_MASK 0xF0
+#define _PSRX(x) (x)
+#define _PSTX(x) ((x) << 4)
+
+#define PBP_64 0x0
+#define PBP_128 0x1
+#define PBP_256 0x2
+#define PBP_512 0x3
+#define PBP_1024 0x4
+
+#define RXDMA_ARBBW_EN BIT(0)
+#define RXSHFT_EN BIT(1)
+#define RXDMA_AGG_EN BIT(2)
+#define QS_VO_QUEUE BIT(8)
+#define QS_VI_QUEUE BIT(9)
+#define QS_BE_QUEUE BIT(10)
+#define QS_BK_QUEUE BIT(11)
+#define QS_MANAGER_QUEUE BIT(12)
+#define QS_HIGH_QUEUE BIT(13)
+
+#define HQSEL_VOQ BIT(0)
+#define HQSEL_VIQ BIT(1)
+#define HQSEL_BEQ BIT(2)
+#define HQSEL_BKQ BIT(3)
+#define HQSEL_MGTQ BIT(4)
+#define HQSEL_HIQ BIT(5)
+
+#define _TXDMA_HIQ_MAP(x) (((x)&0x3) << 14)
+#define _TXDMA_MGQ_MAP(x) (((x)&0x3) << 12)
+#define _TXDMA_BKQ_MAP(x) (((x)&0x3) << 10)
+#define _TXDMA_BEQ_MAP(x) (((x)&0x3) << 8)
+#define _TXDMA_VIQ_MAP(x) (((x)&0x3) << 6)
+#define _TXDMA_VOQ_MAP(x) (((x)&0x3) << 4)
+
+#define QUEUE_LOW 1
+#define QUEUE_NORMAL 2
+#define QUEUE_HIGH 3
+
+#define _LLT_NO_ACTIVE 0x0
+#define _LLT_WRITE_ACCESS 0x1
+#define _LLT_READ_ACCESS 0x2
+
+#define _LLT_INIT_DATA(x) ((x) & 0xFF)
+#define _LLT_INIT_ADDR(x) (((x) & 0xFF) << 8)
+#define _LLT_OP(x) (((x) & 0x3) << 30)
+#define _LLT_OP_VALUE(x) (((x) >> 30) & 0x3)
+
+#define BB_WRITE_READ_MASK (BIT(31) | BIT(30))
+#define BB_WRITE_EN BIT(30)
+#define BB_READ_EN BIT(31)
+
+#define _HPQ(x) ((x) & 0xFF)
+#define _LPQ(x) (((x) & 0xFF) << 8)
+#define _PUBQ(x) (((x) & 0xFF) << 16)
+#define _NPQ(x) ((x) & 0xFF)
+
+#define HPQ_PUBLIC_DIS BIT(24)
+#define LPQ_PUBLIC_DIS BIT(25)
+#define LD_RQPN BIT(31)
+
+#define BCN_VALID BIT(16)
+#define BCN_HEAD(x) (((x) & 0xFF) << 8)
+#define BCN_HEAD_MASK 0xFF00
+
+#define BLK_DESC_NUM_SHIFT 4
+#define BLK_DESC_NUM_MASK 0xF
+
+#define DROP_DATA_EN BIT(9)
+
+#define EN_AMPDU_RTY_NEW BIT(7)
+
+#define _INIRTSMCS_SEL(x) ((x) & 0x3F)
+
+#define _SPEC_SIFS_CCK(x) ((x) & 0xFF)
+#define _SPEC_SIFS_OFDM(x) (((x) & 0xFF) << 8)
+
+#define RATE_REG_BITMAP_ALL 0xFFFFF
+
+#define _RRSC_BITMAP(x) ((x) & 0xFFFFF)
+
+#define _RRSR_RSC(x) (((x) & 0x3) << 21)
+#define RRSR_RSC_RESERVED 0x0
+#define RRSR_RSC_UPPER_SUBCHANNEL 0x1
+#define RRSR_RSC_LOWER_SUBCHANNEL 0x2
+#define RRSR_RSC_DUPLICATE_MODE 0x3
+
+#define USE_SHORT_G1 BIT(20)
+
+#define _AGGLMT_MCS0(x) ((x) & 0xF)
+#define _AGGLMT_MCS1(x) (((x) & 0xF) << 4)
+#define _AGGLMT_MCS2(x) (((x) & 0xF) << 8)
+#define _AGGLMT_MCS3(x) (((x) & 0xF) << 12)
+#define _AGGLMT_MCS4(x) (((x) & 0xF) << 16)
+#define _AGGLMT_MCS5(x) (((x) & 0xF) << 20)
+#define _AGGLMT_MCS6(x) (((x) & 0xF) << 24)
+#define _AGGLMT_MCS7(x) (((x) & 0xF) << 28)
+
+#define RETRY_LIMIT_SHORT_SHIFT 8
+#define RETRY_LIMIT_LONG_SHIFT 0
+
+#define _DARF_RC1(x) ((x) & 0x1F)
+#define _DARF_RC2(x) (((x) & 0x1F) << 8)
+#define _DARF_RC3(x) (((x) & 0x1F) << 16)
+#define _DARF_RC4(x) (((x) & 0x1F) << 24)
+#define _DARF_RC5(x) ((x) & 0x1F)
+#define _DARF_RC6(x) (((x) & 0x1F) << 8)
+#define _DARF_RC7(x) (((x) & 0x1F) << 16)
+#define _DARF_RC8(x) (((x) & 0x1F) << 24)
+
+#define _RARF_RC1(x) ((x) & 0x1F)
+#define _RARF_RC2(x) (((x) & 0x1F) << 8)
+#define _RARF_RC3(x) (((x) & 0x1F) << 16)
+#define _RARF_RC4(x) (((x) & 0x1F) << 24)
+#define _RARF_RC5(x) ((x) & 0x1F)
+#define _RARF_RC6(x) (((x) & 0x1F) << 8)
+#define _RARF_RC7(x) (((x) & 0x1F) << 16)
+#define _RARF_RC8(x) (((x) & 0x1F) << 24)
+
+#define AC_PARAM_TXOP_LIMIT_OFFSET 16
+#define AC_PARAM_ECW_MAX_OFFSET 12
+#define AC_PARAM_ECW_MIN_OFFSET 8
+#define AC_PARAM_AIFS_OFFSET 0
+
+#define _AIFS(x) (x)
+#define _ECW_MAX_MIN(x) ((x) << 8)
+#define _TXOP_LIMIT(x) ((x) << 16)
+
+#define _BCNIFS(x) ((x) & 0xFF)
+#define _BCNECW(x) ((((x) & 0xF)) << 8)
+
+#define _LRL(x) ((x) & 0x3F)
+#define _SRL(x) (((x) & 0x3F) << 8)
+
+#define _SIFS_CCK_CTX(x) ((x) & 0xFF)
+#define _SIFS_CCK_TRX(x) (((x) & 0xFF) << 8)
+
+#define _SIFS_OFDM_CTX(x) ((x) & 0xFF)
+#define _SIFS_OFDM_TRX(x) (((x) & 0xFF) << 8)
+
+#define _TBTT_PROHIBIT_HOLD(x) (((x) & 0xFF) << 8)
+
+#define DIS_EDCA_CNT_DWN BIT(11)
+
+#define EN_MBSSID BIT(1)
+#define EN_TXBCN_RPT BIT(2)
+#define EN_BCN_FUNCTION BIT(3)
+
+#define TSFTR_RST BIT(0)
+#define TSFTR1_RST BIT(1)
+
+#define STOP_BCNQ BIT(6)
+
+#define DIS_TSF_UDT0_NORMAL_CHIP BIT(4)
+#define DIS_TSF_UDT0_TEST_CHIP BIT(5)
+
+#define ACMHW_HWEN BIT(0)
+#define ACMHW_BEQEN BIT(1)
+#define ACMHW_VIQEN BIT(2)
+#define ACMHW_VOQEN BIT(3)
+#define ACMHW_BEQSTATUS BIT(4)
+#define ACMHW_VIQSTATUS BIT(5)
+#define ACMHW_VOQSTATUS BIT(6)
+
+#define APSDOFF BIT(6)
+#define APSDOFF_STATUS BIT(7)
+
+#define BW_20MHZ BIT(2)
+
+#define RATE_BITMAP_ALL 0xFFFFF
+
+#define RATE_RRSR_CCK_ONLY_1M 0xFFFF1
+
+#define TSFRST BIT(0)
+#define DIS_GCLK BIT(1)
+#define PAD_SEL BIT(2)
+#define PWR_ST BIT(6)
+#define PWRBIT_OW_EN BIT(7)
+#define ACRC BIT(8)
+#define CFENDFORM BIT(9)
+#define ICV BIT(10)
+
+#define AAP BIT(0)
+#define APM BIT(1)
+#define AM BIT(2)
+#define AB BIT(3)
+#define ADD3 BIT(4)
+#define APWRMGT BIT(5)
+#define CBSSID BIT(6)
+#define CBSSID_DATA BIT(6)
+#define CBSSID_BCN BIT(7)
+#define ACRC32 BIT(8)
+#define AICV BIT(9)
+#define ADF BIT(11)
+#define ACF BIT(12)
+#define AMF BIT(13)
+#define HTC_LOC_CTRL BIT(14)
+#define UC_DATA_EN BIT(16)
+#define BM_DATA_EN BIT(17)
+#define MFBEN BIT(22)
+#define LSIGEN BIT(23)
+#define ENMBID BIT(24)
+#define APP_BASSN BIT(27)
+#define APP_PHYSTS BIT(28)
+#define APP_ICV BIT(29)
+#define APP_MIC BIT(30)
+#define APP_FCS BIT(31)
+
+#define _MIN_SPACE(x) ((x) & 0x7)
+#define _SHORT_GI_PADDING(x) (((x) & 0x1F) << 3)
+
+#define RXERR_TYPE_OFDM_PPDU 0
+#define RXERR_TYPE_OFDM_FALSE_ALARM 1
+#define RXERR_TYPE_OFDM_MPDU_OK 2
+#define RXERR_TYPE_OFDM_MPDU_FAIL 3
+#define RXERR_TYPE_CCK_PPDU 4
+#define RXERR_TYPE_CCK_FALSE_ALARM 5
+#define RXERR_TYPE_CCK_MPDU_OK 6
+#define RXERR_TYPE_CCK_MPDU_FAIL 7
+#define RXERR_TYPE_HT_PPDU 8
+#define RXERR_TYPE_HT_FALSE_ALARM 9
+#define RXERR_TYPE_HT_MPDU_TOTAL 10
+#define RXERR_TYPE_HT_MPDU_OK 11
+#define RXERR_TYPE_HT_MPDU_FAIL 12
+#define RXERR_TYPE_RX_FULL_DROP 15
+
+#define RXERR_COUNTER_MASK 0xFFFFF
+#define RXERR_RPT_RST BIT(27)
+#define _RXERR_RPT_SEL(type) ((type) << 28)
+
+#define SCR_TXUSEDK BIT(0)
+#define SCR_RXUSEDK BIT(1)
+#define SCR_TXENCENABLE BIT(2)
+#define SCR_RXDECENABLE BIT(3)
+#define SCR_SKBYA2 BIT(4)
+#define SCR_NOSKMC BIT(5)
+#define SCR_TXBCUSEDK BIT(6)
+#define SCR_RXBCUSEDK BIT(7)
+
+#define XCLK_VLD BIT(0)
+#define ACLK_VLD BIT(1)
+#define UCLK_VLD BIT(2)
+#define PCLK_VLD BIT(3)
+#define PCIRSTB BIT(4)
+#define V15_VLD BIT(5)
+#define TRP_B15V_EN BIT(7)
+#define SIC_IDLE BIT(8)
+#define BD_MAC2 BIT(9)
+#define BD_MAC1 BIT(10)
+#define IC_MACPHY_MODE BIT(11)
+#define BT_FUNC BIT(16)
+#define VENDOR_ID BIT(19)
+#define PAD_HWPD_IDN BIT(22)
+#define TRP_VAUX_EN BIT(23)
+#define TRP_BT_EN BIT(24)
+#define BD_PKG_SEL BIT(25)
+#define BD_HCI_SEL BIT(26)
+#define TYPE_ID BIT(27)
+
+#define USB_IS_HIGH_SPEED 0
+#define USB_IS_FULL_SPEED 1
+#define USB_SPEED_MASK BIT(5)
+
+#define USB_NORMAL_SIE_EP_MASK 0xF
+#define USB_NORMAL_SIE_EP_SHIFT 4
+
+#define USB_TEST_EP_MASK 0x30
+#define USB_TEST_EP_SHIFT 4
+
+#define USB_AGG_EN BIT(3)
+
+#define MAC_ADDR_LEN 6
+#define LAST_ENTRY_OF_TX_PKT_BUFFER 175/*255 88e*/
+
+#define POLLING_LLT_THRESHOLD 20
+#define POLLING_READY_TIMEOUT_COUNT 3000
+
+#define MAX_MSS_DENSITY_2T 0x13
+#define MAX_MSS_DENSITY_1T 0x0A
+
+#define EPROM_CMD_OPERATING_MODE_MASK ((1<<7)|(1<<6))
+#define EPROM_CMD_CONFIG 0x3
+#define EPROM_CMD_LOAD 1
+
+#define HWSET_MAX_SIZE_92S HWSET_MAX_SIZE
+
+#define HAL_8192C_HW_GPIO_WPS_BIT BIT(2)
+
+#define RPMAC_RESET 0x100
+#define RPMAC_TXSTART 0x104
+#define RPMAC_TXLEGACYSIG 0x108
+#define RPMAC_TXHTSIG1 0x10c
+#define RPMAC_TXHTSIG2 0x110
+#define RPMAC_PHYDEBUG 0x114
+#define RPMAC_TXPACKETNUM 0x118
+#define RPMAC_TXIDLE 0x11c
+#define RPMAC_TXMACHEADER0 0x120
+#define RPMAC_TXMACHEADER1 0x124
+#define RPMAC_TXMACHEADER2 0x128
+#define RPMAC_TXMACHEADER3 0x12c
+#define RPMAC_TXMACHEADER4 0x130
+#define RPMAC_TXMACHEADER5 0x134
+#define RPMAC_TXDADATYPE 0x138
+#define RPMAC_TXRANDOMSEED 0x13c
+#define RPMAC_CCKPLCPPREAMBLE 0x140
+#define RPMAC_CCKPLCPHEADER 0x144
+#define RPMAC_CCKCRC16 0x148
+#define RPMAC_OFDMRXCRC32OK 0x170
+#define RPMAC_OFDMRXCRC32ER 0x174
+#define RPMAC_OFDMRXPARITYER 0x178
+#define RPMAC_OFDMRXCRC8ER 0x17c
+#define RPMAC_CCKCRXRC16ER 0x180
+#define RPMAC_CCKCRXRC32ER 0x184
+#define RPMAC_CCKCRXRC32OK 0x188
+#define RPMAC_TXSTATUS 0x18c
+
+#define RFPGA0_RFMOD 0x800
+
+#define RFPGA0_TXINFO 0x804
+#define RFPGA0_PSDFUNCTION 0x808
+
+#define RFPGA0_TXGAINSTAGE 0x80c
+
+#define RFPGA0_RFTIMING1 0x810
+#define RFPGA0_RFTIMING2 0x814
+
+#define RFPGA0_XA_HSSIPARAMETER1 0x820
+#define RFPGA0_XA_HSSIPARAMETER2 0x824
+#define RFPGA0_XB_HSSIPARAMETER1 0x828
+#define RFPGA0_XB_HSSIPARAMETER2 0x82c
+
+#define RFPGA0_XA_LSSIPARAMETER 0x840
+#define RFPGA0_XB_LSSIPARAMETER 0x844
+
+#define RFPGA0_RFWAKEUPPARAMETER 0x850
+#define RFPGA0_RFSLEEPUPPARAMETER 0x854
+
+#define RFPGA0_XAB_SWITCHCONTROL 0x858
+#define RFPGA0_XCD_SWITCHCONTROL 0x85c
+
+#define RFPGA0_XA_RFINTERFACEOE 0x860
+#define RFPGA0_XB_RFINTERFACEOE 0x864
+
+#define RFPGA0_XAB_RFINTERFACESW 0x870
+#define RFPGA0_XCD_RFINTERFACESW 0x874
+
+#define RFPGA0_XAB_RFPARAMETER 0x878
+#define RFPGA0_XCD_RFPARAMETER 0x87c
+
+#define RFPGA0_ANALOGPARAMETER1 0x880
+#define RFPGA0_ANALOGPARAMETER2 0x884
+#define RFPGA0_ANALOGPARAMETER3 0x888
+#define RFPGA0_ANALOGPARAMETER4 0x88c
+
+#define RFPGA0_XA_LSSIREADBACK 0x8a0
+#define RFPGA0_XB_LSSIREADBACK 0x8a4
+#define RFPGA0_XC_LSSIREADBACK 0x8a8
+#define RFPGA0_XD_LSSIREADBACK 0x8ac
+
+#define RFPGA0_PSDREPORT 0x8b4
+#define TRANSCEIVEA_HSPI_READBACK 0x8b8
+#define TRANSCEIVEB_HSPI_READBACK 0x8bc
+#define REG_SC_CNT 0x8c4
+#define RFPGA0_XAB_RFINTERFACERB 0x8e0
+#define RFPGA0_XCD_RFINTERFACERB 0x8e4
+
+#define RFPGA1_RFMOD 0x900
+
+#define RFPGA1_TXBLOCK 0x904
+#define RFPGA1_DEBUGSELECT 0x908
+#define RFPGA1_TXINFO 0x90c
+
+#define RCCK0_SYSTEM 0xa00
+
+#define RCCK0_AFESETTING 0xa04
+#define RCCK0_CCA 0xa08
+
+#define RCCK0_RXAGC1 0xa0c
+#define RCCK0_RXAGC2 0xa10
+
+#define RCCK0_RXHP 0xa14
+
+#define RCCK0_DSPPARAMETER1 0xa18
+#define RCCK0_DSPPARAMETER2 0xa1c
+
+#define RCCK0_TXFILTER1 0xa20
+#define RCCK0_TXFILTER2 0xa24
+#define RCCK0_DEBUGPORT 0xa28
+#define RCCK0_FALSEALARMREPORT 0xa2c
+#define RCCK0_TRSSIREPORT 0xa50
+#define RCCK0_RXREPORT 0xa54
+#define RCCK0_FACOUNTERLOWER 0xa5c
+#define RCCK0_FACOUNTERUPPER 0xa58
+#define RCCK0_CCA_CNT 0xa60
+
+
+/* PageB(0xB00) */
+#define RPDP_ANTA 0xb00
+#define RPDP_ANTA_4 0xb04
+#define RPDP_ANTA_8 0xb08
+#define RPDP_ANTA_C 0xb0c
+#define RPDP_ANTA_10 0xb10
+#define RPDP_ANTA_14 0xb14
+#define RPDP_ANTA_18 0xb18
+#define RPDP_ANTA_1C 0xb1c
+#define RPDP_ANTA_20 0xb20
+#define RPDP_ANTA_24 0xb24
+
+#define RCONFIG_PMPD_ANTA 0xb28
+#define CONFIG_RAM64X16 0xb2c
+
+#define RBNDA 0xb30
+#define RHSSIPAR 0xb34
+
+#define RCONFIG_ANTA 0xb68
+#define RCONFIG_ANTB 0xb6c
+
+#define RPDP_ANTB 0xb70
+#define RPDP_ANTB_4 0xb74
+#define RPDP_ANTB_8 0xb78
+#define RPDP_ANTB_C 0xb7c
+#define RPDP_ANTB_10 0xb80
+#define RPDP_ANTB_14 0xb84
+#define RPDP_ANTB_18 0xb88
+#define RPDP_ANTB_1C 0xb8c
+#define RPDP_ANTB_20 0xb90
+#define RPDP_ANTB_24 0xb94
+
+#define RCONFIG_PMPD_ANTB 0xb98
+
+#define RBNDB 0xba0
+
+#define RAPK 0xbd8
+#define RPM_RX0_ANTA 0xbdc
+#define RPM_RX1_ANTA 0xbe0
+#define RPM_RX2_ANTA 0xbe4
+#define RPM_RX3_ANTA 0xbe8
+#define RPM_RX0_ANTB 0xbec
+#define RPM_RX1_ANTB 0xbf0
+#define RPM_RX2_ANTB 0xbf4
+#define RPM_RX3_ANTB 0xbf8
+
+/*Page C*/
+#define ROFDM0_LSTF 0xc00
+
+#define ROFDM0_TRXPATHENABLE 0xc04
+#define ROFDM0_TRMUXPAR 0xc08
+#define ROFDM0_TRSWISOLATION 0xc0c
+
+#define ROFDM0_XARXAFE 0xc10
+#define ROFDM0_XARXIQIMBALANCE 0xc14
+#define ROFDM0_XBRXAFE 0xc18
+#define ROFDM0_XBRXIQIMBALANCE 0xc1c
+#define ROFDM0_XCRXAFE 0xc20
+#define ROFDM0_XCRXIQIMBANLANCE 0xc24
+#define ROFDM0_XDRXAFE 0xc28
+#define ROFDM0_XDRXIQIMBALANCE 0xc2c
+
+#define ROFDM0_RXDETECTOR1 0xc30
+#define ROFDM0_RXDETECTOR2 0xc34
+#define ROFDM0_RXDETECTOR3 0xc38
+#define ROFDM0_RXDETECTOR4 0xc3c
+
+#define ROFDM0_RXDSP 0xc40
+#define ROFDM0_CFOANDDAGC 0xc44
+#define ROFDM0_CCADROPTHRESHOLD 0xc48
+#define ROFDM0_ECCATHRESHOLD 0xc4c
+
+#define ROFDM0_XAAGCCORE1 0xc50
+#define ROFDM0_XAAGCCORE2 0xc54
+#define ROFDM0_XBAGCCORE1 0xc58
+#define ROFDM0_XBAGCCORE2 0xc5c
+#define ROFDM0_XCAGCCORE1 0xc60
+#define ROFDM0_XCAGCCORE2 0xc64
+#define ROFDM0_XDAGCCORE1 0xc68
+#define ROFDM0_XDAGCCORE2 0xc6c
+
+#define ROFDM0_AGCPARAMETER1 0xc70
+#define ROFDM0_AGCPARAMETER2 0xc74
+#define ROFDM0_AGCRSSITABLE 0xc78
+#define ROFDM0_HTSTFAGC 0xc7c
+
+#define ROFDM0_XATXIQIMBALANCE 0xc80
+#define ROFDM0_XATXAFE 0xc84
+#define ROFDM0_XBTXIQIMBALANCE 0xc88
+#define ROFDM0_XBTXAFE 0xc8c
+#define ROFDM0_XCTXIQIMBALANCE 0xc90
+#define ROFDM0_XCTXAFE 0xc94
+#define ROFDM0_XDTXIQIMBALANCE 0xc98
+#define ROFDM0_XDTXAFE 0xc9c
+
+#define ROFDM0_RXIQEXTANTA 0xca0
+#define ROFDM0_TXCOEFF1 0xca4
+#define ROFDM0_TXCOEFF2 0xca8
+#define ROFDM0_TXCOEFF3 0xcac
+#define ROFDM0_TXCOEFF4 0xcb0
+#define ROFDM0_TXCOEFF5 0xcb4
+#define ROFDM0_TXCOEFF6 0xcb8
+
+#define ROFDM0_RXHPPARAMETER 0xce0
+#define ROFDM0_TXPSEUDONOISEWGT 0xce4
+#define ROFDM0_FRAMESYNC 0xcf0
+#define ROFDM0_DFSREPORT 0xcf4
+
+
+#define ROFDM1_LSTF 0xd00
+#define ROFDM1_TRXPATHENABLE 0xd04
+
+#define ROFDM1_CF0 0xd08
+#define ROFDM1_CSI1 0xd10
+#define ROFDM1_SBD 0xd14
+#define ROFDM1_CSI2 0xd18
+#define ROFDM1_CFOTRACKING 0xd2c
+#define ROFDM1_TRXMESAURE1 0xd34
+#define ROFDM1_INTFDET 0xd3c
+#define ROFDM1_PSEUDONOISESTATEAB 0xd50
+#define ROFDM1_PSEUDONOISESTATECD 0xd54
+#define ROFDM1_RXPSEUDONOISEWGT 0xd58
+
+#define ROFDM_PHYCOUNTER1 0xda0
+#define ROFDM_PHYCOUNTER2 0xda4
+#define ROFDM_PHYCOUNTER3 0xda8
+
+#define ROFDM_SHORTCFOAB 0xdac
+#define ROFDM_SHORTCFOCD 0xdb0
+#define ROFDM_LONGCFOAB 0xdb4
+#define ROFDM_LONGCFOCD 0xdb8
+#define ROFDM_TAILCF0AB 0xdbc
+#define ROFDM_TAILCF0CD 0xdc0
+#define ROFDM_PWMEASURE1 0xdc4
+#define ROFDM_PWMEASURE2 0xdc8
+#define ROFDM_BWREPORT 0xdcc
+#define ROFDM_AGCREPORT 0xdd0
+#define ROFDM_RXSNR 0xdd4
+#define ROFDM_RXEVMCSI 0xdd8
+#define ROFDM_SIGREPORT 0xddc
+
+#define RTXAGC_A_RATE18_06 0xe00
+#define RTXAGC_A_RATE54_24 0xe04
+#define RTXAGC_A_CCK1_MCS32 0xe08
+#define RTXAGC_A_MCS03_MCS00 0xe10
+#define RTXAGC_A_MCS07_MCS04 0xe14
+#define RTXAGC_A_MCS11_MCS08 0xe18
+#define RTXAGC_A_MCS15_MCS12 0xe1c
+
+#define RTXAGC_B_RATE18_06 0x830
+#define RTXAGC_B_RATE54_24 0x834
+#define RTXAGC_B_CCK1_55_MCS32 0x838
+#define RTXAGC_B_MCS03_MCS00 0x83c
+#define RTXAGC_B_MCS07_MCS04 0x848
+#define RTXAGC_B_MCS11_MCS08 0x84c
+#define RTXAGC_B_MCS15_MCS12 0x868
+#define RTXAGC_B_CCK11_A_CCK2_11 0x86c
+
+#define RFPGA0_IQK 0xe28
+#define RTX_IQK_TONE_A 0xe30
+#define RRX_IQK_TONE_A 0xe34
+#define RTX_IQK_PI_A 0xe38
+#define RRX_IQK_PI_A 0xe3c
+
+#define RTX_IQK 0xe40
+#define RRX_IQK 0xe44
+#define RIQK_AGC_PTS 0xe48
+#define RIQK_AGC_RSP 0xe4c
+#define RTX_IQK_TONE_B 0xe50
+#define RRX_IQK_TONE_B 0xe54
+#define RTX_IQK_PI_B 0xe58
+#define RRX_IQK_PI_B 0xe5c
+#define RIQK_AGC_CONT 0xe60
+
+#define RBLUE_TOOTH 0xe6c
+#define RRX_WAIT_CCA 0xe70
+#define RTX_CCK_RFON 0xe74
+#define RTX_CCK_BBON 0xe78
+#define RTX_OFDM_RFON 0xe7c
+#define RTX_OFDM_BBON 0xe80
+#define RTX_TO_RX 0xe84
+#define RTX_TO_TX 0xe88
+#define RRX_CCK 0xe8c
+
+#define RTX_POWER_BEFORE_IQK_A 0xe94
+#define RTX_POWER_AFTER_IQK_A 0xe9c
+
+#define RRX_POWER_BEFORE_IQK_A 0xea0
+#define RRX_POWER_BEFORE_IQK_A_2 0xea4
+#define RRX_POWER_AFTER_IQK_A 0xea8
+#define RRX_POWER_AFTER_IQK_A_2 0xeac
+
+#define RTX_POWER_BEFORE_IQK_B 0xeb4
+#define RTX_POWER_AFTER_IQK_B 0xebc
+
+#define RRX_POWER_BEFORE_IQK_B 0xec0
+#define RRX_POWER_BEFORE_IQK_B_2 0xec4
+#define RRX_POWER_AFTER_IQK_B 0xec8
+#define RRX_POWER_AFTER_IQK_B_2 0xecc
+
+#define RRX_OFDM 0xed0
+#define RRX_WAIT_RIFS 0xed4
+#define RRX_TO_RX 0xed8
+#define RSTANDBY 0xedc
+#define RSLEEP 0xee0
+#define RPMPD_ANAEN 0xeec
+
+#define RZEBRA1_HSSIENABLE 0x0
+#define RZEBRA1_TRXENABLE1 0x1
+#define RZEBRA1_TRXENABLE2 0x2
+#define RZEBRA1_AGC 0x4
+#define RZEBRA1_CHARGEPUMP 0x5
+#define RZEBRA1_CHANNEL 0x7
+
+#define RZEBRA1_TXGAIN 0x8
+#define RZEBRA1_TXLPF 0x9
+#define RZEBRA1_RXLPF 0xb
+#define RZEBRA1_RXHPFCORNER 0xc
+
+#define RGLOBALCTRL 0
+#define RRTL8256_TXLPF 19
+#define RRTL8256_RXLPF 11
+#define RRTL8258_TXLPF 0x11
+#define RRTL8258_RXLPF 0x13
+#define RRTL8258_RSSILPF 0xa
+
+#define RF_AC 0x00
+
+#define RF_IQADJ_G1 0x01
+#define RF_IQADJ_G2 0x02
+#define RF_POW_TRSW 0x05
+
+#define RF_GAIN_RX 0x06
+#define RF_GAIN_TX 0x07
+
+#define RF_TXM_IDAC 0x08
+#define RF_BS_IQGEN 0x0F
+
+#define RF_MODE1 0x10
+#define RF_MODE2 0x11
+
+#define RF_RX_AGC_HP 0x12
+#define RF_TX_AGC 0x13
+#define RF_BIAS 0x14
+#define RF_IPA 0x15
+#define RF_POW_ABILITY 0x17
+#define RF_MODE_AG 0x18
+#define RRFCHANNEL 0x18
+#define RF_CHNLBW 0x18
+#define RF_TOP 0x19
+
+#define RF_RX_G1 0x1A
+#define RF_RX_G2 0x1B
+
+#define RF_RX_BB2 0x1C
+#define RF_RX_BB1 0x1D
+
+#define RF_RCK1 0x1E
+#define RF_RCK2 0x1F
+
+#define RF_TX_G1 0x20
+#define RF_TX_G2 0x21
+#define RF_TX_G3 0x22
+
+#define RF_TX_BB1 0x23
+#define RF_T_METER 0x42
+
+#define RF_SYN_G1 0x25
+#define RF_SYN_G2 0x26
+#define RF_SYN_G3 0x27
+#define RF_SYN_G4 0x28
+#define RF_SYN_G5 0x29
+#define RF_SYN_G6 0x2A
+#define RF_SYN_G7 0x2B
+#define RF_SYN_G8 0x2C
+
+#define RF_RCK_OS 0x30
+#define RF_TXPA_G1 0x31
+#define RF_TXPA_G2 0x32
+#define RF_TXPA_G3 0x33
+
+#define RF_TX_BIAS_A 0x35
+#define RF_TX_BIAS_D 0x36
+#define RF_LOBF_9 0x38
+#define RF_RXRF_A3 0x3C
+#define RF_TRSW 0x3F
+
+#define RF_TXRF_A2 0x41
+#define RF_TXPA_G4 0x46
+#define RF_TXPA_A4 0x4B
+
+#define RF_WE_LUT 0xEF
+
+#define BBBRESETB 0x100
+#define BGLOBALRESETB 0x200
+#define BOFDMTXSTART 0x4
+#define BCCKTXSTART 0x8
+#define BCRC32DEBUG 0x100
+#define BPMACLOOPBACK 0x10
+#define BTXLSIG 0xffffff
+#define BOFDMTXRATE 0xf
+#define BOFDMTXRESERVED 0x10
+#define BOFDMTXLENGTH 0x1ffe0
+#define BOFDMTXPARITY 0x20000
+#define BTXHTSIG1 0xffffff
+#define BTXHTMCSRATE 0x7f
+#define BTXHTBW 0x80
+#define BTXHTLENGTH 0xffff00
+#define BTXHTSIG2 0xffffff
+#define BTXHTSMOOTHING 0x1
+#define BTXHTSOUNDING 0x2
+#define BTXHTRESERVED 0x4
+#define BTXHTAGGREATION 0x8
+#define BTXHTSTBC 0x30
+#define BTXHTADVANCECODING 0x40
+#define BTXHTSHORTGI 0x80
+#define BTXHTNUMBERHT_LTF 0x300
+#define BTXHTCRC8 0x3fc00
+#define BCOUNTERRESET 0x10000
+#define BNUMOFOFDMTX 0xffff
+#define BNUMOFCCKTX 0xffff0000
+#define BTXIDLEINTERVAL 0xffff
+#define BOFDMSERVICE 0xffff0000
+#define BTXMACHEADER 0xffffffff
+#define BTXDATAINIT 0xff
+#define BTXHTMODE 0x100
+#define BTXDATATYPE 0x30000
+#define BTXRANDOMSEED 0xffffffff
+#define BCCKTXPREAMBLE 0x1
+#define BCCKTXSFD 0xffff0000
+#define BCCKTXSIG 0xff
+#define BCCKTXSERVICE 0xff00
+#define BCCKLENGTHEXT 0x8000
+#define BCCKTXLENGHT 0xffff0000
+#define BCCKTXCRC16 0xffff
+#define BCCKTXSTATUS 0x1
+#define BOFDMTXSTATUS 0x2
+#define IS_BB_REG_OFFSET_92S(_offset) \
+ ((_offset >= 0x800) && (_offset <= 0xfff))
+
+#define BRFMOD 0x1
+#define BJAPANMODE 0x2
+#define BCCKTXSC 0x30
+#define BCCKEN 0x1000000
+#define BOFDMEN 0x2000000
+
+#define BOFDMRXADCPHASE 0x10000
+#define BOFDMTXDACPHASE 0x40000
+#define BXATXAGC 0x3f
+
+#define BXBTXAGC 0xf00
+#define BXCTXAGC 0xf000
+#define BXDTXAGC 0xf0000
+
+#define BPASTART 0xf0000000
+#define BTRSTART 0x00f00000
+#define BRFSTART 0x0000f000
+#define BBBSTART 0x000000f0
+#define BBBCCKSTART 0x0000000f
+#define BPAEND 0xf
+#define BTREND 0x0f000000
+#define BRFEND 0x000f0000
+#define BCCAMASK 0x000000f0
+#define BR2RCCAMASK 0x00000f00
+#define BHSSI_R2TDELAY 0xf8000000
+#define BHSSI_T2RDELAY 0xf80000
+#define BCONTXHSSI 0x400
+#define BIGFROMCCK 0x200
+#define BAGCADDRESS 0x3f
+#define BRXHPTX 0x7000
+#define BRXHP2RX 0x38000
+#define BRXHPCCKINI 0xc0000
+#define BAGCTXCODE 0xc00000
+#define BAGCRXCODE 0x300000
+
+#define B3WIREDATALENGTH 0x800
+#define B3WIREADDREAALENGTH 0x400
+
+#define B3WIRERFPOWERDOWN 0x1
+#define B5GPAPEPOLARITY 0x40000000
+#define B2GPAPEPOLARITY 0x80000000
+#define BRFSW_TXDEFAULTANT 0x3
+#define BRFSW_TXOPTIONANT 0x30
+#define BRFSW_RXDEFAULTANT 0x300
+#define BRFSW_RXOPTIONANT 0x3000
+#define BRFSI_3WIREDATA 0x1
+#define BRFSI_3WIRECLOCK 0x2
+#define BRFSI_3WIRELOAD 0x4
+#define BRFSI_3WIRERW 0x8
+#define BRFSI_3WIRE 0xf
+
+#define BRFSI_RFENV 0x10
+
+#define BRFSI_TRSW 0x20
+#define BRFSI_TRSWB 0x40
+#define BRFSI_ANTSW 0x100
+#define BRFSI_ANTSWB 0x200
+#define BRFSI_PAPE 0x400
+#define BRFSI_PAPE5G 0x800
+#define BBANDSELECT 0x1
+#define BHTSIG2_GI 0x80
+#define BHTSIG2_SMOOTHING 0x01
+#define BHTSIG2_SOUNDING 0x02
+#define BHTSIG2_AGGREATON 0x08
+#define BHTSIG2_STBC 0x30
+#define BHTSIG2_ADVCODING 0x40
+#define BHTSIG2_NUMOFHTLTF 0x300
+#define BHTSIG2_CRC8 0x3fc
+#define BHTSIG1_MCS 0x7f
+#define BHTSIG1_BANDWIDTH 0x80
+#define BHTSIG1_HTLENGTH 0xffff
+#define BLSIG_RATE 0xf
+#define BLSIG_RESERVED 0x10
+#define BLSIG_LENGTH 0x1fffe
+#define BLSIG_PARITY 0x20
+#define BCCKRXPHASE 0x4
+
+#define BLSSIREADADDRESS 0x7f800000
+#define BLSSIREADEDGE 0x80000000
+
+#define BLSSIREADBACKDATA 0xfffff
+
+#define BLSSIREADOKFLAG 0x1000
+#define BCCKSAMPLERATE 0x8
+#define BREGULATOR0STANDBY 0x1
+#define BREGULATORPLLSTANDBY 0x2
+#define BREGULATOR1STANDBY 0x4
+#define BPLLPOWERUP 0x8
+#define BDPLLPOWERUP 0x10
+#define BDA10POWERUP 0x20
+#define BAD7POWERUP 0x200
+#define BDA6POWERUP 0x2000
+#define BXTALPOWERUP 0x4000
+#define B40MDCLKPOWERUP 0x8000
+#define BDA6DEBUGMODE 0x20000
+#define BDA6SWING 0x380000
+
+#define BADCLKPHASE 0x4000000
+#define B80MCLKDELAY 0x18000000
+#define BAFEWATCHDOGENABLE 0x20000000
+
+#define BXTALCAP01 0xc0000000
+#define BXTALCAP23 0x3
+#define BXTALCAP92X 0x0f000000
+#define BXTALCAP 0x0f000000
+
+#define BINTDIFCLKENABLE 0x400
+#define BEXTSIGCLKENABLE 0x800
+#define BBANDGAP_MBIAS_POWERUP 0x10000
+#define BAD11SH_GAIN 0xc0000
+#define BAD11NPUT_RANGE 0x700000
+#define BAD110P_CURRENT 0x3800000
+#define BLPATH_LOOPBACK 0x4000000
+#define BQPATH_LOOPBACK 0x8000000
+#define BAFE_LOOPBACK 0x10000000
+#define BDA10_SWING 0x7e0
+#define BDA10_REVERSE 0x800
+#define BDA_CLK_SOURCE 0x1000
+#define BDA7INPUT_RANGE 0x6000
+#define BDA7_GAIN 0x38000
+#define BDA7OUTPUT_CM_MODE 0x40000
+#define BDA7INPUT_CM_MODE 0x380000
+#define BDA7CURRENT 0xc00000
+#define BREGULATOR_ADJUST 0x7000000
+#define BAD11POWERUP_ATTX 0x1
+#define BDA10PS_ATTX 0x10
+#define BAD11POWERUP_ATRX 0x100
+#define BDA10PS_ATRX 0x1000
+#define BCCKRX_AGC_FORMAT 0x200
+#define BPSDFFT_SAMPLE_POINT 0xc000
+#define BPSD_AVERAGE_NUM 0x3000
+#define BIQPATH_CONTROL 0xc00
+#define BPSD_FREQ 0x3ff
+#define BPSD_ANTENNA_PATH 0x30
+#define BPSD_IQ_SWITCH 0x40
+#define BPSD_RX_TRIGGER 0x400000
+#define BPSD_TX_TRIGGER 0x80000000
+#define BPSD_SINE_TONE_SCALE 0x7f000000
+#define BPSD_REPORT 0xffff
+
+#define BOFDM_TXSC 0x30000000
+#define BCCK_TXON 0x1
+#define BOFDM_TXON 0x2
+#define BDEBUG_PAGE 0xfff
+#define BDEBUG_ITEM 0xff
+#define BANTL 0x10
+#define BANT_NONHT 0x100
+#define BANT_HT1 0x1000
+#define BANT_HT2 0x10000
+#define BANT_HT1S1 0x100000
+#define BANT_NONHTS1 0x1000000
+
+#define BCCK_BBMODE 0x3
+#define BCCK_TXPOWERSAVING 0x80
+#define BCCK_RXPOWERSAVING 0x40
+
+#define BCCK_SIDEBAND 0x10
+
+#define BCCK_SCRAMBLE 0x8
+#define BCCK_ANTDIVERSITY 0x8000
+#define BCCK_CARRIER_RECOVERY 0x4000
+#define BCCK_TXRATE 0x3000
+#define BCCK_DCCANCEL 0x0800
+#define BCCK_ISICANCEL 0x0400
+#define BCCK_MATCH_FILTER 0x0200
+#define BCCK_EQUALIZER 0x0100
+#define BCCK_PREAMBLE_DETECT 0x800000
+#define BCCK_FAST_FALSECCA 0x400000
+#define BCCK_CH_ESTSTART 0x300000
+#define BCCK_CCA_COUNT 0x080000
+#define BCCK_CS_LIM 0x070000
+#define BCCK_BIST_MODE 0x80000000
+#define BCCK_CCAMASK 0x40000000
+#define BCCK_TX_DAC_PHASE 0x4
+#define BCCK_RX_ADC_PHASE 0x20000000
+#define BCCKR_CP_MODE 0x0100
+#define BCCK_TXDC_OFFSET 0xf0
+#define BCCK_RXDC_OFFSET 0xf
+#define BCCK_CCA_MODE 0xc000
+#define BCCK_FALSECS_LIM 0x3f00
+#define BCCK_CS_RATIO 0xc00000
+#define BCCK_CORGBIT_SEL 0x300000
+#define BCCK_PD_LIM 0x0f0000
+#define BCCK_NEWCCA 0x80000000
+#define BCCK_RXHP_OF_IG 0x8000
+#define BCCK_RXIG 0x7f00
+#define BCCK_LNA_POLARITY 0x800000
+#define BCCK_RX1ST_BAIN 0x7f0000
+#define BCCK_RF_EXTEND 0x20000000
+#define BCCK_RXAGC_SATLEVEL 0x1f000000
+#define BCCK_RXAGC_SATCOUNT 0xe0
+#define BCCKRXRFSETTLE 0x1f
+#define BCCK_FIXED_RXAGC 0x8000
+#define BCCK_ANTENNA_POLARITY 0x2000
+#define BCCK_TXFILTER_TYPE 0x0c00
+#define BCCK_RXAGC_REPORTTYPE 0x0300
+#define BCCK_RXDAGC_EN 0x80000000
+#define BCCK_RXDAGC_PERIOD 0x20000000
+#define BCCK_RXDAGC_SATLEVEL 0x1f000000
+#define BCCK_TIMING_RECOVERY 0x800000
+#define BCCK_TXC0 0x3f0000
+#define BCCK_TXC1 0x3f000000
+#define BCCK_TXC2 0x3f
+#define BCCK_TXC3 0x3f00
+#define BCCK_TXC4 0x3f0000
+#define BCCK_TXC5 0x3f000000
+#define BCCK_TXC6 0x3f
+#define BCCK_TXC7 0x3f00
+#define BCCK_DEBUGPORT 0xff0000
+#define BCCK_DAC_DEBUG 0x0f000000
+#define BCCK_FALSEALARM_ENABLE 0x8000
+#define BCCK_FALSEALARM_READ 0x4000
+#define BCCK_TRSSI 0x7f
+#define BCCK_RXAGC_REPORT 0xfe
+#define BCCK_RXREPORT_ANTSEL 0x80000000
+#define BCCK_RXREPORT_MFOFF 0x40000000
+#define BCCK_RXREPORT_SQLOSS 0x20000000
+#define BCCK_RXREPORT_PKTLOSS 0x10000000
+#define BCCK_RXREPORT_LOCKEDBIT 0x08000000
+#define BCCK_RXREPORT_RATEERROR 0x04000000
+#define BCCK_RXREPORT_RXRATE 0x03000000
+#define BCCK_RXFA_COUNTER_LOWER 0xff
+#define BCCK_RXFA_COUNTER_UPPER 0xff000000
+#define BCCK_RXHPAGC_START 0xe000
+#define BCCK_RXHPAGC_FINAL 0x1c00
+#define BCCK_RXFALSEALARM_ENABLE 0x8000
+#define BCCK_FACOUNTER_FREEZE 0x4000
+#define BCCK_TXPATH_SEL 0x10000000
+#define BCCK_DEFAULT_RXPATH 0xc000000
+#define BCCK_OPTION_RXPATH 0x3000000
+
+#define BNUM_OFSTF 0x3
+#define BSHIFT_L 0xc0
+#define BGI_TH 0xc
+#define BRXPATH_A 0x1
+#define BRXPATH_B 0x2
+#define BRXPATH_C 0x4
+#define BRXPATH_D 0x8
+#define BTXPATH_A 0x1
+#define BTXPATH_B 0x2
+#define BTXPATH_C 0x4
+#define BTXPATH_D 0x8
+#define BTRSSI_FREQ 0x200
+#define BADC_BACKOFF 0x3000
+#define BDFIR_BACKOFF 0xc000
+#define BTRSSI_LATCH_PHASE 0x10000
+#define BRX_LDC_OFFSET 0xff
+#define BRX_QDC_OFFSET 0xff00
+#define BRX_DFIR_MODE 0x1800000
+#define BRX_DCNF_TYPE 0xe000000
+#define BRXIQIMB_A 0x3ff
+#define BRXIQIMB_B 0xfc00
+#define BRXIQIMB_C 0x3f0000
+#define BRXIQIMB_D 0xffc00000
+#define BDC_DC_NOTCH 0x60000
+#define BRXNB_NOTCH 0x1f000000
+#define BPD_TH 0xf
+#define BPD_TH_OPT2 0xc000
+#define BPWED_TH 0x700
+#define BIFMF_WIN_L 0x800
+#define BPD_OPTION 0x1000
+#define BMF_WIN_L 0xe000
+#define BBW_SEARCH_L 0x30000
+#define BWIN_ENH_L 0xc0000
+#define BBW_TH 0x700000
+#define BED_TH2 0x3800000
+#define BBW_OPTION 0x4000000
+#define BRADIO_TH 0x18000000
+#define BWINDOW_L 0xe0000000
+#define BSBD_OPTION 0x1
+#define BFRAME_TH 0x1c
+#define BFS_OPTION 0x60
+#define BDC_SLOPE_CHECK 0x80
+#define BFGUARD_COUNTER_DC_L 0xe00
+#define BFRAME_WEIGHT_SHORT 0x7000
+#define BSUB_TUNE 0xe00000
+#define BFRAME_DC_LENGTH 0xe000000
+#define BSBD_START_OFFSET 0x30000000
+#define BFRAME_TH_2 0x7
+#define BFRAME_GI2_TH 0x38
+#define BGI2_SYNC_EN 0x40
+#define BSARCH_SHORT_EARLY 0x300
+#define BSARCH_SHORT_LATE 0xc00
+#define BSARCH_GI2_LATE 0x70000
+#define BCFOANTSUM 0x1
+#define BCFOACC 0x2
+#define BCFOSTARTOFFSET 0xc
+#define BCFOLOOPBACK 0x70
+#define BCFOSUMWEIGHT 0x80
+#define BDAGCENABLE 0x10000
+#define BTXIQIMB_A 0x3ff
+#define BTXIQIMB_b 0xfc00
+#define BTXIQIMB_C 0x3f0000
+#define BTXIQIMB_D 0xffc00000
+#define BTXIDCOFFSET 0xff
+#define BTXIQDCOFFSET 0xff00
+#define BTXDFIRMODE 0x10000
+#define BTXPESUDO_NOISEON 0x4000000
+#define BTXPESUDO_NOISE_A 0xff
+#define BTXPESUDO_NOISE_B 0xff00
+#define BTXPESUDO_NOISE_C 0xff0000
+#define BTXPESUDO_NOISE_D 0xff000000
+#define BCCA_DROPOPTION 0x20000
+#define BCCA_DROPTHRES 0xfff00000
+#define BEDCCA_H 0xf
+#define BEDCCA_L 0xf0
+#define BLAMBDA_ED 0x300
+#define BRX_INITIALGAIN 0x7f
+#define BRX_ANTDIV_EN 0x80
+#define BRX_AGC_ADDRESS_FOR_LNA 0x7f00
+#define BRX_HIGHPOWER_FLOW 0x8000
+#define BRX_AGC_FREEZE_THRES 0xc0000
+#define BRX_FREEZESTEP_AGC1 0x300000
+#define BRX_FREEZESTEP_AGC2 0xc00000
+#define BRX_FREEZESTEP_AGC3 0x3000000
+#define BRX_FREEZESTEP_AGC0 0xc000000
+#define BRXRSSI_CMP_EN 0x10000000
+#define BRXQUICK_AGCEN 0x20000000
+#define BRXAGC_FREEZE_THRES_MODE 0x40000000
+#define BRX_OVERFLOW_CHECKTYPE 0x80000000
+#define BRX_AGCSHIFT 0x7f
+#define BTRSW_TRI_ONLY 0x80
+#define BPOWER_THRES 0x300
+#define BRXAGC_EN 0x1
+#define BRXAGC_TOGETHER_EN 0x2
+#define BRXAGC_MIN 0x4
+#define BRXHP_INI 0x7
+#define BRXHP_TRLNA 0x70
+#define BRXHP_RSSI 0x700
+#define BRXHP_BBP1 0x7000
+#define BRXHP_BBP2 0x70000
+#define BRXHP_BBP3 0x700000
+#define BRSSI_H 0x7f0000
+#define BRSSI_GEN 0x7f000000
+#define BRXSETTLE_TRSW 0x7
+#define BRXSETTLE_LNA 0x38
+#define BRXSETTLE_RSSI 0x1c0
+#define BRXSETTLE_BBP 0xe00
+#define BRXSETTLE_RXHP 0x7000
+#define BRXSETTLE_ANTSW_RSSI 0x38000
+#define BRXSETTLE_ANTSW 0xc0000
+#define BRXPROCESS_TIME_DAGC 0x300000
+#define BRXSETTLE_HSSI 0x400000
+#define BRXPROCESS_TIME_BBPPW 0x800000
+#define BRXANTENNA_POWER_SHIFT 0x3000000
+#define BRSSI_TABLE_SELECT 0xc000000
+#define BRXHP_FINAL 0x7000000
+#define BRXHPSETTLE_BBP 0x7
+#define BRXHTSETTLE_HSSI 0x8
+#define BRXHTSETTLE_RXHP 0x70
+#define BRXHTSETTLE_BBPPW 0x80
+#define BRXHTSETTLE_IDLE 0x300
+#define BRXHTSETTLE_RESERVED 0x1c00
+#define BRXHT_RXHP_EN 0x8000
+#define BRXAGC_FREEZE_THRES 0x30000
+#define BRXAGC_TOGETHEREN 0x40000
+#define BRXHTAGC_MIN 0x80000
+#define BRXHTAGC_EN 0x100000
+#define BRXHTDAGC_EN 0x200000
+#define BRXHT_RXHP_BBP 0x1c00000
+#define BRXHT_RXHP_FINAL 0xe0000000
+#define BRXPW_RADIO_TH 0x3
+#define BRXPW_RADIO_EN 0x4
+#define BRXMF_HOLD 0x3800
+#define BRXPD_DELAY_TH1 0x38
+#define BRXPD_DELAY_TH2 0x1c0
+#define BRXPD_DC_COUNT_MAX 0x600
+#define BRXPD_DELAY_TH 0x8000
+#define BRXPROCESS_DELAY 0xf0000
+#define BRXSEARCHRANGE_GI2_EARLY 0x700000
+#define BRXFRAME_FUARD_COUNTER_L 0x3800000
+#define BRXSGI_GUARD_L 0xc000000
+#define BRXSGI_SEARCH_L 0x30000000
+#define BRXSGI_TH 0xc0000000
+#define BDFSCNT0 0xff
+#define BDFSCNT1 0xff00
+#define BDFSFLAG 0xf0000
+#define BMF_WEIGHT_SUM 0x300000
+#define BMINIDX_TH 0x7f000000
+#define BDAFORMAT 0x40000
+#define BTXCH_EMU_ENABLE 0x01000000
+#define BTRSW_ISOLATION_A 0x7f
+#define BTRSW_ISOLATION_B 0x7f00
+#define BTRSW_ISOLATION_C 0x7f0000
+#define BTRSW_ISOLATION_D 0x7f000000
+#define BEXT_LNA_GAIN 0x7c00
+
+#define BSTBC_EN 0x4
+#define BANTENNA_MAPPING 0x10
+#define BNSS 0x20
+#define BCFO_ANTSUM_ID 0x200
+#define BPHY_COUNTER_RESET 0x8000000
+#define BCFO_REPORT_GET 0x4000000
+#define BOFDM_CONTINUE_TX 0x10000000
+#define BOFDM_SINGLE_CARRIER 0x20000000
+#define BOFDM_SINGLE_TONE 0x40000000
+#define BHT_DETECT 0x100
+#define BCFOEN 0x10000
+#define BCFOVALUE 0xfff00000
+#define BSIGTONE_RE 0x3f
+#define BSIGTONE_IM 0x7f00
+#define BCOUNTER_CCA 0xffff
+#define BCOUNTER_PARITYFAIL 0xffff0000
+#define BCOUNTER_RATEILLEGAL 0xffff
+#define BCOUNTER_CRC8FAIL 0xffff0000
+#define BCOUNTER_MCSNOSUPPORT 0xffff
+#define BCOUNTER_FASTSYNC 0xffff
+#define BSHORTCFO 0xfff
+#define BSHORTCFOT_LENGTH 12
+#define BSHORTCFOF_LENGTH 11
+#define BLONGCFO 0x7ff
+#define BLONGCFOT_LENGTH 11
+#define BLONGCFOF_LENGTH 11
+#define BTAILCFO 0x1fff
+#define BTAILCFOT_LENGTH 13
+#define BTAILCFOF_LENGTH 12
+#define BNOISE_EN_PWDB 0xffff
+#define BCC_POWER_DB 0xffff0000
+#define BMOISE_PWDB 0xffff
+#define BPOWERMEAST_LENGTH 10
+#define BPOWERMEASF_LENGTH 3
+#define BRX_HT_BW 0x1
+#define BRXSC 0x6
+#define BRX_HT 0x8
+#define BNB_INTF_DET_ON 0x1
+#define BINTF_WIN_LEN_CFG 0x30
+#define BNB_INTF_TH_CFG 0x1c0
+#define BRFGAIN 0x3f
+#define BTABLESEL 0x40
+#define BTRSW 0x80
+#define BRXSNR_A 0xff
+#define BRXSNR_B 0xff00
+#define BRXSNR_C 0xff0000
+#define BRXSNR_D 0xff000000
+#define BSNR_EVMT_LENGTH 8
+#define BSNR_EVMF_LENGTH 1
+#define BCSI1ST 0xff
+#define BCSI2ND 0xff00
+#define BRXEVM1ST 0xff0000
+#define BRXEVM2ND 0xff000000
+#define BSIGEVM 0xff
+#define BPWDB 0xff00
+#define BSGIEN 0x10000
+
+#define BSFACTOR_QMA1 0xf
+#define BSFACTOR_QMA2 0xf0
+#define BSFACTOR_QMA3 0xf00
+#define BSFACTOR_QMA4 0xf000
+#define BSFACTOR_QMA5 0xf0000
+#define BSFACTOR_QMA6 0xf0000
+#define BSFACTOR_QMA7 0xf00000
+#define BSFACTOR_QMA8 0xf000000
+#define BSFACTOR_QMA9 0xf0000000
+#define BCSI_SCHEME 0x100000
+
+#define BNOISE_LVL_TOP_SET 0x3
+#define BCHSMOOTH 0x4
+#define BCHSMOOTH_CFG1 0x38
+#define BCHSMOOTH_CFG2 0x1c0
+#define BCHSMOOTH_CFG3 0xe00
+#define BCHSMOOTH_CFG4 0x7000
+#define BMRCMODE 0x800000
+#define BTHEVMCFG 0x7000000
+
+#define BLOOP_FIT_TYPE 0x1
+#define BUPD_CFO 0x40
+#define BUPD_CFO_OFFDATA 0x80
+#define BADV_UPD_CFO 0x100
+#define BADV_TIME_CTRL 0x800
+#define BUPD_CLKO 0x1000
+#define BFC 0x6000
+#define BTRACKING_MODE 0x8000
+#define BPHCMP_ENABLE 0x10000
+#define BUPD_CLKO_LTF 0x20000
+#define BCOM_CH_CFO 0x40000
+#define BCSI_ESTI_MODE 0x80000
+#define BADV_UPD_EQZ 0x100000
+#define BUCHCFG 0x7000000
+#define BUPDEQZ 0x8000000
+
+#define BRX_PESUDO_NOISE_ON 0x20000000
+#define BRX_PESUDO_NOISE_A 0xff
+#define BRX_PESUDO_NOISE_B 0xff00
+#define BRX_PESUDO_NOISE_C 0xff0000
+#define BRX_PESUDO_NOISE_D 0xff000000
+#define BRX_PESUDO_NOISESTATE_A 0xffff
+#define BRX_PESUDO_NOISESTATE_B 0xffff0000
+#define BRX_PESUDO_NOISESTATE_C 0xffff
+#define BRX_PESUDO_NOISESTATE_D 0xffff0000
+
+#define BZEBRA1_HSSIENABLE 0x8
+#define BZEBRA1_TRXCONTROL 0xc00
+#define BZEBRA1_TRXGAINSETTING 0x07f
+#define BZEBRA1_RXCOUNTER 0xc00
+#define BZEBRA1_TXCHANGEPUMP 0x38
+#define BZEBRA1_RXCHANGEPUMP 0x7
+#define BZEBRA1_CHANNEL_NUM 0xf80
+#define BZEBRA1_TXLPFBW 0x400
+#define BZEBRA1_RXLPFBW 0x600
+
+#define BRTL8256REG_MODE_CTRL1 0x100
+#define BRTL8256REG_MODE_CTRL0 0x40
+#define BRTL8256REG_TXLPFBW 0x18
+#define BRTL8256REG_RXLPFBW 0x600
+
+#define BRTL8258_TXLPFBW 0xc
+#define BRTL8258_RXLPFBW 0xc00
+#define BRTL8258_RSSILPFBW 0xc0
+
+#define BBYTE0 0x1
+#define BBYTE1 0x2
+#define BBYTE2 0x4
+#define BBYTE3 0x8
+#define BWORD0 0x3
+#define BWORD1 0xc
+#define BWORD 0xf
+
+#define BENABLE 0x1
+#define BDISABLE 0x0
+
+#define LEFT_ANTENNA 0x0
+#define RIGHT_ANTENNA 0x1
+
+#define TCHECK_TXSTATUS 500
+#define TUPDATE_RXCOUNTER 100
+
+#define REG_UN_used_register 0x01bf
+
+/* WOL bit information */
+#define HAL92C_WOL_PTK_UPDATE_EVENT BIT(0)
+#define HAL92C_WOL_GTK_UPDATE_EVENT BIT(1)
+#define HAL92C_WOL_DISASSOC_EVENT BIT(2)
+#define HAL92C_WOL_DEAUTH_EVENT BIT(3)
+#define HAL92C_WOL_FW_DISCONNECT_EVENT BIT(4)
+
+#define WOL_REASON_PTK_UPDATE BIT(0)
+#define WOL_REASON_GTK_UPDATE BIT(1)
+#define WOL_REASON_DISASSOC BIT(2)
+#define WOL_REASON_DEAUTH BIT(3)
+#define WOL_REASON_FW_DISCONNECT BIT(4)
+
+/* 2 EFUSE_TEST (For RTL8723 partially) */
+#define EFUSE_SEL(x) (((x) & 0x3) << 8)
+#define EFUSE_SEL_MASK 0x300
+#define EFUSE_WIFI_SEL_0 0x0
+
+#define WL_HWPDN_EN BIT(0) /* Enable GPIO[9] as WiFi HW PDn source*/
+#define WL_HWPDN_SL BIT(1) /* WiFi HW PDn polarity control*/
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/rf.c b/drivers/net/wireless/rtlwifi/rtl8723be/rf.c
new file mode 100644
index 000000000000..486294930a7b
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/rf.c
@@ -0,0 +1,504 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "rf.h"
+#include "dm.h"
+
+static bool _rtl8723be_phy_rf6052_config_parafile(struct ieee80211_hw *hw);
+
+void rtl8723be_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ switch (bandwidth) {
+ case HT_CHANNEL_WIDTH_20:
+ rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] &
+ 0xfffff3ff) | BIT(10) | BIT(11));
+ rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK,
+ rtlphy->rfreg_chnlval[0]);
+ break;
+ case HT_CHANNEL_WIDTH_20_40:
+ rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] &
+ 0xfffff3ff) | BIT(10));
+ rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK,
+ rtlphy->rfreg_chnlval[0]);
+ break;
+ default:
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG,
+ "unknown bandwidth: %#X\n", bandwidth);
+ break;
+ }
+}
+
+void rtl8723be_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
+ u8 *ppowerlevel)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u32 tx_agc[2] = {0, 0}, tmpval;
+ bool turbo_scanoff = false;
+ u8 idx1, idx2;
+ u8 *ptr;
+ u8 direction;
+ u32 pwrtrac_value;
+
+ if (rtlefuse->eeprom_regulatory != 0)
+ turbo_scanoff = true;
+
+ if (mac->act_scanning) {
+ tx_agc[RF90_PATH_A] = 0x3f3f3f3f;
+ tx_agc[RF90_PATH_B] = 0x3f3f3f3f;
+
+ if (turbo_scanoff) {
+ for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) {
+ tx_agc[idx1] = ppowerlevel[idx1] |
+ (ppowerlevel[idx1] << 8) |
+ (ppowerlevel[idx1] << 16) |
+ (ppowerlevel[idx1] << 24);
+ }
+ }
+ } else {
+ for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) {
+ tx_agc[idx1] = ppowerlevel[idx1] |
+ (ppowerlevel[idx1] << 8) |
+ (ppowerlevel[idx1] << 16) |
+ (ppowerlevel[idx1] << 24);
+ }
+ if (rtlefuse->eeprom_regulatory == 0) {
+ tmpval =
+ (rtlphy->mcs_offset[0][6]) +
+ (rtlphy->mcs_offset[0][7] << 8);
+ tx_agc[RF90_PATH_A] += tmpval;
+
+ tmpval = (rtlphy->mcs_offset[0][14]) +
+ (rtlphy->mcs_offset[0][15] <<
+ 24);
+ tx_agc[RF90_PATH_B] += tmpval;
+ }
+ }
+ for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) {
+ ptr = (u8 *)(&(tx_agc[idx1]));
+ for (idx2 = 0; idx2 < 4; idx2++) {
+ if (*ptr > RF6052_MAX_TX_PWR)
+ *ptr = RF6052_MAX_TX_PWR;
+ ptr++;
+ }
+ }
+ rtl8723be_dm_txpower_track_adjust(hw, 1, &direction, &pwrtrac_value);
+ if (direction == 1) {
+ tx_agc[0] += pwrtrac_value;
+ tx_agc[1] += pwrtrac_value;
+ } else if (direction == 2) {
+ tx_agc[0] -= pwrtrac_value;
+ tx_agc[1] -= pwrtrac_value;
+ }
+ tmpval = tx_agc[RF90_PATH_A] & 0xff;
+ rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, MASKBYTE1, tmpval);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "CCK PWR 1M (rf-A) = 0x%x (reg 0x%x)\n", tmpval,
+ RTXAGC_A_CCK1_MCS32);
+
+ tmpval = tx_agc[RF90_PATH_A] >> 8;
+
+ rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "CCK PWR 2~11M (rf-A) = 0x%x (reg 0x%x)\n", tmpval,
+ RTXAGC_B_CCK11_A_CCK2_11);
+
+ tmpval = tx_agc[RF90_PATH_B] >> 24;
+ rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, MASKBYTE0, tmpval);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "CCK PWR 11M (rf-B) = 0x%x (reg 0x%x)\n", tmpval,
+ RTXAGC_B_CCK11_A_CCK2_11);
+
+ tmpval = tx_agc[RF90_PATH_B] & 0x00ffffff;
+ rtl_set_bbreg(hw, RTXAGC_B_CCK1_55_MCS32, 0xffffff00, tmpval);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "CCK PWR 1~5.5M (rf-B) = 0x%x (reg 0x%x)\n", tmpval,
+ RTXAGC_B_CCK1_55_MCS32);
+}
+
+static void rtl8723be_phy_get_power_base(struct ieee80211_hw *hw,
+ u8 *ppowerlevel_ofdm,
+ u8 *ppowerlevel_bw20,
+ u8 *ppowerlevel_bw40,
+ u8 channel, u32 *ofdmbase,
+ u32 *mcsbase)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ u32 powerbase0, powerbase1;
+ u8 i, powerlevel[2];
+
+ for (i = 0; i < 2; i++) {
+ powerbase0 = ppowerlevel_ofdm[i];
+
+ powerbase0 = (powerbase0 << 24) | (powerbase0 << 16) |
+ (powerbase0 << 8) | powerbase0;
+ *(ofdmbase + i) = powerbase0;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ " [OFDM power base index rf(%c) = 0x%x]\n",
+ ((i == 0) ? 'A' : 'B'), *(ofdmbase + i));
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20)
+ powerlevel[i] = ppowerlevel_bw20[i];
+ else
+ powerlevel[i] = ppowerlevel_bw40[i];
+ powerbase1 = powerlevel[i];
+ powerbase1 = (powerbase1 << 24) | (powerbase1 << 16) |
+ (powerbase1 << 8) | powerbase1;
+
+ *(mcsbase + i) = powerbase1;
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ " [MCS power base index rf(%c) = 0x%x]\n",
+ ((i == 0) ? 'A' : 'B'), *(mcsbase + i));
+ }
+}
+
+static void txpwr_by_regulatory(struct ieee80211_hw *hw, u8 channel, u8 index,
+ u32 *powerbase0, u32 *powerbase1,
+ u32 *p_outwriteval)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ u8 i, chnlgroup = 0, pwr_diff_limit[4];
+ u8 pwr_diff = 0, customer_pwr_diff;
+ u32 writeval, customer_limit, rf;
+
+ for (rf = 0; rf < 2; rf++) {
+ switch (rtlefuse->eeprom_regulatory) {
+ case 0:
+ chnlgroup = 0;
+
+ writeval =
+ rtlphy->mcs_offset[chnlgroup][index + (rf ? 8 : 0)]
+ + ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "RTK better performance, "
+ "writeval(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'), writeval);
+ break;
+ case 1:
+ if (rtlphy->pwrgroup_cnt == 1) {
+ chnlgroup = 0;
+ } else {
+ if (channel < 3)
+ chnlgroup = 0;
+ else if (channel < 6)
+ chnlgroup = 1;
+ else if (channel < 9)
+ chnlgroup = 2;
+ else if (channel < 12)
+ chnlgroup = 3;
+ else if (channel < 14)
+ chnlgroup = 4;
+ else if (channel == 14)
+ chnlgroup = 5;
+ }
+ writeval = rtlphy->mcs_offset[chnlgroup]
+ [index + (rf ? 8 : 0)] + ((index < 2) ?
+ powerbase0[rf] :
+ powerbase1[rf]);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Realtek regulatory, 20MHz, "
+ "writeval(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'), writeval);
+
+ break;
+ case 2:
+ writeval =
+ ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Better regulatory, "
+ "writeval(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'), writeval);
+ break;
+ case 3:
+ chnlgroup = 0;
+
+ if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "customer's limit, 40MHz "
+ "rf(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'),
+ rtlefuse->pwrgroup_ht40[rf]
+ [channel-1]);
+ } else {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "customer's limit, 20MHz "
+ "rf(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'),
+ rtlefuse->pwrgroup_ht20[rf]
+ [channel-1]);
+ }
+
+ if (index < 2)
+ pwr_diff =
+ rtlefuse->txpwr_legacyhtdiff[rf][channel-1];
+ else if (rtlphy->current_chan_bw ==
+ HT_CHANNEL_WIDTH_20)
+ pwr_diff =
+ rtlefuse->txpwr_ht20diff[rf][channel-1];
+
+ if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40)
+ customer_pwr_diff =
+ rtlefuse->pwrgroup_ht40[rf][channel-1];
+ else
+ customer_pwr_diff =
+ rtlefuse->pwrgroup_ht20[rf][channel-1];
+
+ if (pwr_diff > customer_pwr_diff)
+ pwr_diff = 0;
+ else
+ pwr_diff = customer_pwr_diff - pwr_diff;
+
+ for (i = 0; i < 4; i++) {
+ pwr_diff_limit[i] =
+ (u8)((rtlphy->mcs_offset
+ [chnlgroup][index + (rf ? 8 : 0)] &
+ (0x7f << (i * 8))) >> (i * 8));
+
+ if (pwr_diff_limit[i] > pwr_diff)
+ pwr_diff_limit[i] = pwr_diff;
+ }
+
+ customer_limit = (pwr_diff_limit[3] << 24) |
+ (pwr_diff_limit[2] << 16) |
+ (pwr_diff_limit[1] << 8) |
+ (pwr_diff_limit[0]);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Customer's limit rf(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'), customer_limit);
+
+ writeval = customer_limit + ((index < 2) ?
+ powerbase0[rf] :
+ powerbase1[rf]);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Customer, writeval rf(%c)= 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'), writeval);
+ break;
+ default:
+ chnlgroup = 0;
+ writeval =
+ rtlphy->mcs_offset[chnlgroup]
+ [index + (rf ? 8 : 0)]
+ + ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "RTK better performance, writeval "
+ "rf(%c) = 0x%x\n",
+ ((rf == 0) ? 'A' : 'B'), writeval);
+ break;
+ }
+
+ if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT1)
+ writeval = writeval - 0x06060606;
+ else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
+ TXHIGHPWRLEVEL_BT2)
+ writeval = writeval - 0x0c0c0c0c;
+ *(p_outwriteval + rf) = writeval;
+ }
+}
+
+static void _rtl8723be_write_ofdm_power_reg(struct ieee80211_hw *hw,
+ u8 index, u32 *value)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u16 regoffset_a[6] = {
+ RTXAGC_A_RATE18_06, RTXAGC_A_RATE54_24,
+ RTXAGC_A_MCS03_MCS00, RTXAGC_A_MCS07_MCS04,
+ RTXAGC_A_MCS11_MCS08, RTXAGC_A_MCS15_MCS12
+ };
+ u16 regoffset_b[6] = {
+ RTXAGC_B_RATE18_06, RTXAGC_B_RATE54_24,
+ RTXAGC_B_MCS03_MCS00, RTXAGC_B_MCS07_MCS04,
+ RTXAGC_B_MCS11_MCS08, RTXAGC_B_MCS15_MCS12
+ };
+ u8 i, rf, pwr_val[4];
+ u32 writeval;
+ u16 regoffset;
+
+ for (rf = 0; rf < 2; rf++) {
+ writeval = value[rf];
+ for (i = 0; i < 4; i++) {
+ pwr_val[i] = (u8) ((writeval & (0x7f <<
+ (i * 8))) >> (i * 8));
+
+ if (pwr_val[i] > RF6052_MAX_TX_PWR)
+ pwr_val[i] = RF6052_MAX_TX_PWR;
+ }
+ writeval = (pwr_val[3] << 24) | (pwr_val[2] << 16) |
+ (pwr_val[1] << 8) | pwr_val[0];
+
+ if (rf == 0)
+ regoffset = regoffset_a[index];
+ else
+ regoffset = regoffset_b[index];
+ rtl_set_bbreg(hw, regoffset, MASKDWORD, writeval);
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ "Set 0x%x = %08x\n", regoffset, writeval);
+ }
+}
+
+void rtl8723be_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
+ u8 *ppowerlevel_ofdm,
+ u8 *ppowerlevel_bw20,
+ u8 *ppowerlevel_bw40, u8 channel)
+{
+ u32 writeval[2], powerbase0[2], powerbase1[2];
+ u8 index;
+ u8 direction;
+ u32 pwrtrac_value;
+
+ rtl8723be_phy_get_power_base(hw, ppowerlevel_ofdm, ppowerlevel_bw20,
+ ppowerlevel_bw40, channel,
+ &powerbase0[0], &powerbase1[0]);
+
+ rtl8723be_dm_txpower_track_adjust(hw, 1, &direction, &pwrtrac_value);
+
+ for (index = 0; index < 6; index++) {
+ txpwr_by_regulatory(hw, channel, index, &powerbase0[0],
+ &powerbase1[0], &writeval[0]);
+ if (direction == 1) {
+ writeval[0] += pwrtrac_value;
+ writeval[1] += pwrtrac_value;
+ } else if (direction == 2) {
+ writeval[0] -= pwrtrac_value;
+ writeval[1] -= pwrtrac_value;
+ }
+ _rtl8723be_write_ofdm_power_reg(hw, index, &writeval[0]);
+ }
+}
+
+bool rtl8723be_phy_rf6052_config(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ if (rtlphy->rf_type == RF_1T1R)
+ rtlphy->num_total_rfpath = 1;
+ else
+ rtlphy->num_total_rfpath = 2;
+
+ return _rtl8723be_phy_rf6052_config_parafile(hw);
+}
+
+static bool _rtl8723be_phy_rf6052_config_parafile(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct bb_reg_def *pphyreg;
+ u32 u4_regvalue = 0;
+ u8 rfpath;
+ bool rtstatus = true;
+
+ for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) {
+ pphyreg = &rtlphy->phyreg_def[rfpath];
+
+ switch (rfpath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs,
+ BRFSI_RFENV);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs,
+ BRFSI_RFENV << 16);
+ break;
+ }
+
+ rtl_set_bbreg(hw, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1);
+ udelay(1);
+
+ rtl_set_bbreg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1);
+ udelay(1);
+
+ rtl_set_bbreg(hw, pphyreg->rfhssi_para2,
+ B3WIREADDREAALENGTH, 0x0);
+ udelay(1);
+
+ rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREDATALENGTH, 0x0);
+ udelay(1);
+
+ switch (rfpath) {
+ case RF90_PATH_A:
+ rtstatus = rtl8723be_phy_config_rf_with_headerfile(hw,
+ (enum radio_path)rfpath);
+ break;
+ case RF90_PATH_B:
+ rtstatus = rtl8723be_phy_config_rf_with_headerfile(hw,
+ (enum radio_path)rfpath);
+ break;
+ case RF90_PATH_C:
+ break;
+ case RF90_PATH_D:
+ break;
+ }
+
+ switch (rfpath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ rtl_set_bbreg(hw, pphyreg->rfintfs,
+ BRFSI_RFENV, u4_regvalue);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ rtl_set_bbreg(hw, pphyreg->rfintfs,
+ BRFSI_RFENV << 16, u4_regvalue);
+ break;
+ }
+
+ if (!rtstatus) {
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
+ "Radio[%d] Fail!!", rfpath);
+ return false;
+ }
+ }
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "\n");
+ return rtstatus;
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/rf.h b/drivers/net/wireless/rtlwifi/rtl8723be/rf.h
new file mode 100644
index 000000000000..a6fea106ced4
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/rf.h
@@ -0,0 +1,43 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_RF_H__
+#define __RTL8723BE_RF_H__
+
+#define RF6052_MAX_TX_PWR 0x3F
+#define RF6052_MAX_REG 0x3F
+
+void rtl8723be_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
+ u8 bandwidth);
+void rtl8723be_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
+ u8 *ppowerlevel);
+void rtl8723be_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
+ u8 *ppowerlevel_ofdm,
+ u8 *ppowerlevel_bw20,
+ u8 *ppowerlevel_bw40,
+ u8 channel);
+bool rtl8723be_phy_rf6052_config(struct ieee80211_hw *hw);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c
new file mode 100644
index 000000000000..b4577ebc4bb0
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c
@@ -0,0 +1,384 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../core.h"
+#include "../pci.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "../rtl8723com/phy_common.h"
+#include "dm.h"
+#include "hw.h"
+#include "fw.h"
+#include "../rtl8723com/fw_common.h"
+#include "sw.h"
+#include "trx.h"
+#include "led.h"
+#include "table.h"
+#include "../btcoexist/rtl_btc.h"
+
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+
+static void rtl8723be_init_aspm_vars(struct ieee80211_hw *hw)
+{
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+
+ /*close ASPM for AMD defaultly */
+ rtlpci->const_amdpci_aspm = 0;
+
+ /* ASPM PS mode.
+ * 0 - Disable ASPM,
+ * 1 - Enable ASPM without Clock Req,
+ * 2 - Enable ASPM with Clock Req,
+ * 3 - Alwyas Enable ASPM with Clock Req,
+ * 4 - Always Enable ASPM without Clock Req.
+ * set defult to RTL8192CE:3 RTL8192E:2
+ */
+ rtlpci->const_pci_aspm = 3;
+
+ /*Setting for PCI-E device */
+ rtlpci->const_devicepci_aspm_setting = 0x03;
+
+ /*Setting for PCI-E bridge */
+ rtlpci->const_hostpci_aspm_setting = 0x02;
+
+ /* In Hw/Sw Radio Off situation.
+ * 0 - Default,
+ * 1 - From ASPM setting without low Mac Pwr,
+ * 2 - From ASPM setting with low Mac Pwr,
+ * 3 - Bus D3
+ * set default to RTL8192CE:0 RTL8192SE:2
+ */
+ rtlpci->const_hwsw_rfoff_d3 = 0;
+
+ /* This setting works for those device with
+ * backdoor ASPM setting such as EPHY setting.
+ * 0 - Not support ASPM,
+ * 1 - Support ASPM,
+ * 2 - According to chipset.
+ */
+ rtlpci->const_support_pciaspm = 1;
+}
+
+int rtl8723be_init_sw_vars(struct ieee80211_hw *hw)
+{
+ int err = 0;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+
+ rtl8723be_bt_reg_init(hw);
+ rtlpci->msi_support = true;
+ rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer();
+
+ rtlpriv->dm.dm_initialgain_enable = 1;
+ rtlpriv->dm.dm_flag = 0;
+ rtlpriv->dm.disable_framebursting = 0;
+ rtlpriv->dm.thermalvalue = 0;
+ rtlpci->transmit_config = CFENDFORM | BIT(15) | BIT(24) | BIT(25);
+
+ mac->ht_enable = true;
+
+ /* compatible 5G band 88ce just 2.4G band & smsp */
+ rtlpriv->rtlhal.current_bandtype = BAND_ON_2_4G;
+ rtlpriv->rtlhal.bandset = BAND_ON_2_4G;
+ rtlpriv->rtlhal.macphymode = SINGLEMAC_SINGLEPHY;
+
+ rtlpci->receive_config = (RCR_APPFCS |
+ RCR_APP_MIC |
+ RCR_APP_ICV |
+ RCR_APP_PHYST_RXFF |
+ RCR_HTC_LOC_CTRL |
+ RCR_AMF |
+ RCR_ACF |
+ RCR_ADF |
+ RCR_AICV |
+ RCR_AB |
+ RCR_AM |
+ RCR_APM |
+ 0);
+
+ rtlpci->irq_mask[0] = (u32) (IMR_PSTIMEOUT |
+ IMR_HSISR_IND_ON_INT |
+ IMR_C2HCMD |
+ IMR_HIGHDOK |
+ IMR_MGNTDOK |
+ IMR_BKDOK |
+ IMR_BEDOK |
+ IMR_VIDOK |
+ IMR_VODOK |
+ IMR_RDU |
+ IMR_ROK |
+ 0);
+
+ rtlpci->irq_mask[1] = (u32)(IMR_RXFOVW | 0);
+
+ /* for debug level */
+ rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug;
+ /* for LPS & IPS */
+ 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->psc.reg_fwctrl_lps = 3;
+ rtlpriv->psc.reg_max_lps_awakeintvl = 5;
+ /* for ASPM, you can close aspm through
+ * set const_support_pciaspm = 0
+ */
+ rtl8723be_init_aspm_vars(hw);
+
+ if (rtlpriv->psc.reg_fwctrl_lps == 1)
+ rtlpriv->psc.fwctrl_psmode = FW_PS_MIN_MODE;
+ else if (rtlpriv->psc.reg_fwctrl_lps == 2)
+ rtlpriv->psc.fwctrl_psmode = FW_PS_MAX_MODE;
+ else if (rtlpriv->psc.reg_fwctrl_lps == 3)
+ rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE;
+
+ /* for firmware buf */
+ rtlpriv->rtlhal.pfirmware = vzalloc(0x8000);
+ if (!rtlpriv->rtlhal.pfirmware) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "Can't alloc buffer for fw.\n");
+ return 1;
+ }
+
+ rtlpriv->max_fw_size = 0x8000;
+ pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
+ err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
+ rtlpriv->io.dev, GFP_KERNEL, hw,
+ rtl_fw_cb);
+ if (err) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "Failed to request firmware!\n");
+ return 1;
+ }
+ return 0;
+}
+
+void rtl8723be_deinit_sw_vars(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (rtlpriv->cfg->ops->get_btc_status())
+ rtlpriv->btcoexist.btc_ops->btc_halt_notify();
+ if (rtlpriv->rtlhal.pfirmware) {
+ vfree(rtlpriv->rtlhal.pfirmware);
+ rtlpriv->rtlhal.pfirmware = NULL;
+ }
+}
+
+/* get bt coexist status */
+bool rtl8723be_get_btc_status(void)
+{
+ return true;
+}
+
+static bool is_fw_header(struct rtl92c_firmware_header *hdr)
+{
+ return (hdr->signature & 0xfff0) == 0x5300;
+}
+
+static struct rtl_hal_ops rtl8723be_hal_ops = {
+ .init_sw_vars = rtl8723be_init_sw_vars,
+ .deinit_sw_vars = rtl8723be_deinit_sw_vars,
+ .read_eeprom_info = rtl8723be_read_eeprom_info,
+ .interrupt_recognized = rtl8723be_interrupt_recognized,
+ .hw_init = rtl8723be_hw_init,
+ .hw_disable = rtl8723be_card_disable,
+ .hw_suspend = rtl8723be_suspend,
+ .hw_resume = rtl8723be_resume,
+ .enable_interrupt = rtl8723be_enable_interrupt,
+ .disable_interrupt = rtl8723be_disable_interrupt,
+ .set_network_type = rtl8723be_set_network_type,
+ .set_chk_bssid = rtl8723be_set_check_bssid,
+ .set_qos = rtl8723be_set_qos,
+ .set_bcn_reg = rtl8723be_set_beacon_related_registers,
+ .set_bcn_intv = rtl8723be_set_beacon_interval,
+ .update_interrupt_mask = rtl8723be_update_interrupt_mask,
+ .get_hw_reg = rtl8723be_get_hw_reg,
+ .set_hw_reg = rtl8723be_set_hw_reg,
+ .update_rate_tbl = rtl8723be_update_hal_rate_tbl,
+ .fill_tx_desc = rtl8723be_tx_fill_desc,
+ .fill_tx_cmddesc = rtl8723be_tx_fill_cmddesc,
+ .query_rx_desc = rtl8723be_rx_query_desc,
+ .set_channel_access = rtl8723be_update_channel_access_setting,
+ .radio_onoff_checking = rtl8723be_gpio_radio_on_off_checking,
+ .set_bw_mode = rtl8723be_phy_set_bw_mode,
+ .switch_channel = rtl8723be_phy_sw_chnl,
+ .dm_watchdog = rtl8723be_dm_watchdog,
+ .scan_operation_backup = rtl8723be_phy_scan_operation_backup,
+ .set_rf_power_state = rtl8723be_phy_set_rf_power_state,
+ .led_control = rtl8723be_led_control,
+ .set_desc = rtl8723be_set_desc,
+ .get_desc = rtl8723be_get_desc,
+ .is_tx_desc_closed = rtl8723be_is_tx_desc_closed,
+ .tx_polling = rtl8723be_tx_polling,
+ .enable_hw_sec = rtl8723be_enable_hw_security_config,
+ .set_key = rtl8723be_set_key,
+ .init_sw_leds = rtl8723be_init_sw_leds,
+ .get_bbreg = rtl8723_phy_query_bb_reg,
+ .set_bbreg = rtl8723_phy_set_bb_reg,
+ .get_rfreg = rtl8723be_phy_query_rf_reg,
+ .set_rfreg = rtl8723be_phy_set_rf_reg,
+ .fill_h2c_cmd = rtl8723be_fill_h2c_cmd,
+ .get_btc_status = rtl8723be_get_btc_status,
+ .is_fw_header = is_fw_header,
+};
+
+static struct rtl_mod_params rtl8723be_mod_params = {
+ .sw_crypto = false,
+ .inactiveps = true,
+ .swctrl_lps = false,
+ .fwctrl_lps = true,
+ .debug = DBG_EMERG,
+};
+
+static struct rtl_hal_cfg rtl8723be_hal_cfg = {
+ .bar_id = 2,
+ .write_readback = true,
+ .name = "rtl8723be_pci",
+ .fw_name = "rtlwifi/rtl8723befw.bin",
+ .ops = &rtl8723be_hal_ops,
+ .mod_params = &rtl8723be_mod_params,
+ .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL,
+ .maps[SYS_FUNC_EN] = REG_SYS_FUNC_EN,
+ .maps[SYS_CLK] = REG_SYS_CLKR,
+ .maps[MAC_RCR_AM] = AM,
+ .maps[MAC_RCR_AB] = AB,
+ .maps[MAC_RCR_ACRC32] = ACRC32,
+ .maps[MAC_RCR_ACF] = ACF,
+ .maps[MAC_RCR_AAP] = AAP,
+
+ .maps[EFUSE_ACCESS] = REG_EFUSE_ACCESS,
+
+ .maps[EFUSE_TEST] = REG_EFUSE_TEST,
+ .maps[EFUSE_CTRL] = REG_EFUSE_CTRL,
+ .maps[EFUSE_CLK] = 0,
+ .maps[EFUSE_CLK_CTRL] = REG_EFUSE_CTRL,
+ .maps[EFUSE_PWC_EV12V] = PWC_EV12V,
+ .maps[EFUSE_FEN_ELDR] = FEN_ELDR,
+ .maps[EFUSE_LOADER_CLK_EN] = LOADER_CLK_EN,
+ .maps[EFUSE_ANA8M] = ANA8M,
+ .maps[EFUSE_HWSET_MAX_SIZE] = HWSET_MAX_SIZE,
+ .maps[EFUSE_MAX_SECTION_MAP] = EFUSE_MAX_SECTION,
+ .maps[EFUSE_REAL_CONTENT_SIZE] = EFUSE_REAL_CONTENT_LEN,
+ .maps[EFUSE_OOB_PROTECT_BYTES_LEN] = EFUSE_OOB_PROTECT_BYTES,
+
+ .maps[RWCAM] = REG_CAMCMD,
+ .maps[WCAMI] = REG_CAMWRITE,
+ .maps[RCAMO] = REG_CAMREAD,
+ .maps[CAMDBG] = REG_CAMDBG,
+ .maps[SECR] = REG_SECCFG,
+ .maps[SEC_CAM_NONE] = CAM_NONE,
+ .maps[SEC_CAM_WEP40] = CAM_WEP40,
+ .maps[SEC_CAM_TKIP] = CAM_TKIP,
+ .maps[SEC_CAM_AES] = CAM_AES,
+ .maps[SEC_CAM_WEP104] = CAM_WEP104,
+
+ .maps[RTL_IMR_BCNDMAINT6] = IMR_BCNDMAINT6,
+ .maps[RTL_IMR_BCNDMAINT5] = IMR_BCNDMAINT5,
+ .maps[RTL_IMR_BCNDMAINT4] = IMR_BCNDMAINT4,
+ .maps[RTL_IMR_BCNDMAINT3] = IMR_BCNDMAINT3,
+ .maps[RTL_IMR_BCNDMAINT2] = IMR_BCNDMAINT2,
+ .maps[RTL_IMR_BCNDMAINT1] = IMR_BCNDMAINT1,
+ .maps[RTL_IMR_BCNDOK7] = IMR_BCNDOK7,
+ .maps[RTL_IMR_BCNDOK6] = IMR_BCNDOK6,
+ .maps[RTL_IMR_BCNDOK5] = IMR_BCNDOK5,
+ .maps[RTL_IMR_BCNDOK4] = IMR_BCNDOK4,
+ .maps[RTL_IMR_BCNDOK3] = IMR_BCNDOK3,
+ .maps[RTL_IMR_BCNDOK2] = IMR_BCNDOK2,
+ .maps[RTL_IMR_BCNDOK1] = IMR_BCNDOK1,
+
+ .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW,
+ .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT,
+ .maps[RTL_IMR_BCNINT] = IMR_BCNDMAINT0,
+ .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW,
+ .maps[RTL_IMR_RDU] = IMR_RDU,
+ .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND,
+ .maps[RTL_IMR_BDOK] = IMR_BCNDOK0,
+ .maps[RTL_IMR_MGNTDOK] = IMR_MGNTDOK,
+ .maps[RTL_IMR_TBDER] = IMR_TBDER,
+ .maps[RTL_IMR_HIGHDOK] = IMR_HIGHDOK,
+ .maps[RTL_IMR_TBDOK] = IMR_TBDOK,
+ .maps[RTL_IMR_BKDOK] = IMR_BKDOK,
+ .maps[RTL_IMR_BEDOK] = IMR_BEDOK,
+ .maps[RTL_IMR_VIDOK] = IMR_VIDOK,
+ .maps[RTL_IMR_VODOK] = IMR_VODOK,
+ .maps[RTL_IMR_ROK] = IMR_ROK,
+ .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNDMAINT0 | IMR_TBDOK | IMR_TBDER),
+
+ .maps[RTL_RC_CCK_RATE1M] = DESC92C_RATE1M,
+ .maps[RTL_RC_CCK_RATE2M] = DESC92C_RATE2M,
+ .maps[RTL_RC_CCK_RATE5_5M] = DESC92C_RATE5_5M,
+ .maps[RTL_RC_CCK_RATE11M] = DESC92C_RATE11M,
+ .maps[RTL_RC_OFDM_RATE6M] = DESC92C_RATE6M,
+ .maps[RTL_RC_OFDM_RATE9M] = DESC92C_RATE9M,
+ .maps[RTL_RC_OFDM_RATE12M] = DESC92C_RATE12M,
+ .maps[RTL_RC_OFDM_RATE18M] = DESC92C_RATE18M,
+ .maps[RTL_RC_OFDM_RATE24M] = DESC92C_RATE24M,
+ .maps[RTL_RC_OFDM_RATE36M] = DESC92C_RATE36M,
+ .maps[RTL_RC_OFDM_RATE48M] = DESC92C_RATE48M,
+ .maps[RTL_RC_OFDM_RATE54M] = DESC92C_RATE54M,
+
+ .maps[RTL_RC_HT_RATEMCS7] = DESC92C_RATEMCS7,
+ .maps[RTL_RC_HT_RATEMCS15] = DESC92C_RATEMCS15,
+};
+
+static DEFINE_PCI_DEVICE_TABLE(rtl8723be_pci_id) = {
+ {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xb723, rtl8723be_hal_cfg)},
+ {},
+};
+
+MODULE_DEVICE_TABLE(pci, rtl8723be_pci_id);
+
+MODULE_AUTHOR("PageHe <page_he@realsil.com.cn>");
+MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek 8723BE 802.11n PCI wireless");
+MODULE_FIRMWARE("rtlwifi/rtl8723befw.bin");
+
+module_param_named(swenc, rtl8723be_mod_params.sw_crypto, bool, 0444);
+module_param_named(debug, rtl8723be_mod_params.debug, int, 0444);
+module_param_named(ips, rtl8723be_mod_params.inactiveps, bool, 0444);
+module_param_named(swlps, rtl8723be_mod_params.swctrl_lps, bool, 0444);
+module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444);
+MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n");
+MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n");
+MODULE_PARM_DESC(fwlps, "using linked fw control power save (default 1 is open)\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);
+
+static struct pci_driver rtl8723be_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rtl8723be_pci_id,
+ .probe = rtl_pci_probe,
+ .remove = rtl_pci_disconnect,
+
+ .driver.pm = &rtlwifi_pm_ops,
+};
+
+module_pci_driver(rtl8723be_driver);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/sw.h b/drivers/net/wireless/rtlwifi/rtl8723be/sw.h
new file mode 100644
index 000000000000..a7b25e769950
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/sw.h
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_SW_H__
+#define __RTL8723BE_SW_H__
+
+int rtl8723be_init_sw_vars(struct ieee80211_hw *hw);
+void rtl8723be_deinit_sw_vars(struct ieee80211_hw *hw);
+void rtl8723be_init_var_map(struct ieee80211_hw *hw);
+bool rtl8723be_get_btc_status(void);
+
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/table.c b/drivers/net/wireless/rtlwifi/rtl8723be/table.c
new file mode 100644
index 000000000000..4b283cde042e
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/table.c
@@ -0,0 +1,572 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Created on 2010/ 5/18, 1:41
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "table.h"
+u32 RTL8723BEPHY_REG_1TARRAY[] = {
+ 0x800, 0x80040000,
+ 0x804, 0x00000003,
+ 0x808, 0x0000FC00,
+ 0x80C, 0x0000000A,
+ 0x810, 0x10001331,
+ 0x814, 0x020C3D10,
+ 0x818, 0x02200385,
+ 0x81C, 0x00000000,
+ 0x820, 0x01000100,
+ 0x824, 0x00390204,
+ 0x828, 0x00000000,
+ 0x82C, 0x00000000,
+ 0x830, 0x00000000,
+ 0x834, 0x00000000,
+ 0x838, 0x00000000,
+ 0x83C, 0x00000000,
+ 0x840, 0x00010000,
+ 0x844, 0x00000000,
+ 0x848, 0x00000000,
+ 0x84C, 0x00000000,
+ 0x850, 0x00000000,
+ 0x854, 0x00000000,
+ 0x858, 0x569A11A9,
+ 0x85C, 0x01000014,
+ 0x860, 0x66F60110,
+ 0x864, 0x061F0649,
+ 0x868, 0x00000000,
+ 0x86C, 0x27272700,
+ 0x870, 0x07000760,
+ 0x874, 0x25004000,
+ 0x878, 0x00000808,
+ 0x87C, 0x00000000,
+ 0x880, 0xB0000C1C,
+ 0x884, 0x00000001,
+ 0x888, 0x00000000,
+ 0x88C, 0xCCC000C0,
+ 0x890, 0x00000800,
+ 0x894, 0xFFFFFFFE,
+ 0x898, 0x40302010,
+ 0x89C, 0x00706050,
+ 0x900, 0x00000000,
+ 0x904, 0x00000023,
+ 0x908, 0x00000000,
+ 0x90C, 0x81121111,
+ 0x910, 0x00000002,
+ 0x914, 0x00000201,
+ 0x948, 0x00000000,
+ 0xA00, 0x00D047C8,
+ 0xA04, 0x80FF000C,
+ 0xA08, 0x8C838300,
+ 0xA0C, 0x2E7F120F,
+ 0xA10, 0x9500BB78,
+ 0xA14, 0x1114D028,
+ 0xA18, 0x00881117,
+ 0xA1C, 0x89140F00,
+ 0xA20, 0x1A1B0000,
+ 0xA24, 0x090E1317,
+ 0xA28, 0x00000204,
+ 0xA2C, 0x00D30000,
+ 0xA70, 0x101FBF00,
+ 0xA74, 0x00000007,
+ 0xA78, 0x00000900,
+ 0xA7C, 0x225B0606,
+ 0xA80, 0x21806490,
+ 0xB2C, 0x00000000,
+ 0xC00, 0x48071D40,
+ 0xC04, 0x03A05611,
+ 0xC08, 0x000000E4,
+ 0xC0C, 0x6C6C6C6C,
+ 0xC10, 0x08800000,
+ 0xC14, 0x40000100,
+ 0xC18, 0x08800000,
+ 0xC1C, 0x40000100,
+ 0xC20, 0x00000000,
+ 0xC24, 0x00000000,
+ 0xC28, 0x00000000,
+ 0xC2C, 0x00000000,
+ 0xC30, 0x69E9AC44,
+ 0xC34, 0x469652AF,
+ 0xC38, 0x49795994,
+ 0xC3C, 0x0A97971C,
+ 0xC40, 0x1F7C403F,
+ 0xC44, 0x000100B7,
+ 0xC48, 0xEC020107,
+ 0xC4C, 0x007F037F,
+ 0xC50, 0x69553420,
+ 0xC54, 0x43BC0094,
+ 0xC58, 0x00023169,
+ 0xC5C, 0x00250492,
+ 0xC60, 0x00000000,
+ 0xC64, 0x7112848B,
+ 0xC68, 0x47C00BFF,
+ 0xC6C, 0x00000036,
+ 0xC70, 0x2C7F000D,
+ 0xC74, 0x020610DB,
+ 0xC78, 0x0000001F,
+ 0xC7C, 0x00B91612,
+ 0xC80, 0x390000E4,
+ 0xC84, 0x20F60000,
+ 0xC88, 0x40000100,
+ 0xC8C, 0x20200000,
+ 0xC90, 0x00020E1A,
+ 0xC94, 0x00000000,
+ 0xC98, 0x00020E1A,
+ 0xC9C, 0x00007F7F,
+ 0xCA0, 0x00000000,
+ 0xCA4, 0x000300A0,
+ 0xCA8, 0x00000000,
+ 0xCAC, 0x00000000,
+ 0xCB0, 0x00000000,
+ 0xCB4, 0x00000000,
+ 0xCB8, 0x00000000,
+ 0xCBC, 0x28000000,
+ 0xCC0, 0x00000000,
+ 0xCC4, 0x00000000,
+ 0xCC8, 0x00000000,
+ 0xCCC, 0x00000000,
+ 0xCD0, 0x00000000,
+ 0xCD4, 0x00000000,
+ 0xCD8, 0x64B22427,
+ 0xCDC, 0x00766932,
+ 0xCE0, 0x00222222,
+ 0xCE4, 0x00000000,
+ 0xCE8, 0x37644302,
+ 0xCEC, 0x2F97D40C,
+ 0xD00, 0x00000740,
+ 0xD04, 0x40020401,
+ 0xD08, 0x0000907F,
+ 0xD0C, 0x20010201,
+ 0xD10, 0xA0633333,
+ 0xD14, 0x3333BC53,
+ 0xD18, 0x7A8F5B6F,
+ 0xD2C, 0xCC979975,
+ 0xD30, 0x00000000,
+ 0xD34, 0x80608000,
+ 0xD38, 0x00000000,
+ 0xD3C, 0x00127353,
+ 0xD40, 0x00000000,
+ 0xD44, 0x00000000,
+ 0xD48, 0x00000000,
+ 0xD4C, 0x00000000,
+ 0xD50, 0x6437140A,
+ 0xD54, 0x00000000,
+ 0xD58, 0x00000282,
+ 0xD5C, 0x30032064,
+ 0xD60, 0x4653DE68,
+ 0xD64, 0x04518A3C,
+ 0xD68, 0x00002101,
+ 0xD6C, 0x2A201C16,
+ 0xD70, 0x1812362E,
+ 0xD74, 0x322C2220,
+ 0xD78, 0x000E3C24,
+ 0xE00, 0x2D2D2D2D,
+ 0xE04, 0x2D2D2D2D,
+ 0xE08, 0x0390272D,
+ 0xE10, 0x2D2D2D2D,
+ 0xE14, 0x2D2D2D2D,
+ 0xE18, 0x2D2D2D2D,
+ 0xE1C, 0x2D2D2D2D,
+ 0xE28, 0x00000000,
+ 0xE30, 0x1000DC1F,
+ 0xE34, 0x10008C1F,
+ 0xE38, 0x02140102,
+ 0xE3C, 0x681604C2,
+ 0xE40, 0x01007C00,
+ 0xE44, 0x01004800,
+ 0xE48, 0xFB000000,
+ 0xE4C, 0x000028D1,
+ 0xE50, 0x1000DC1F,
+ 0xE54, 0x10008C1F,
+ 0xE58, 0x02140102,
+ 0xE5C, 0x28160D05,
+ 0xE60, 0x00000008,
+ 0xE68, 0x001B2556,
+ 0xE6C, 0x00C00096,
+ 0xE70, 0x00C00096,
+ 0xE74, 0x01000056,
+ 0xE78, 0x01000014,
+ 0xE7C, 0x01000056,
+ 0xE80, 0x01000014,
+ 0xE84, 0x00C00096,
+ 0xE88, 0x01000056,
+ 0xE8C, 0x00C00096,
+ 0xED0, 0x00C00096,
+ 0xED4, 0x00C00096,
+ 0xED8, 0x00C00096,
+ 0xEDC, 0x000000D6,
+ 0xEE0, 0x000000D6,
+ 0xEEC, 0x01C00016,
+ 0xF14, 0x00000003,
+ 0xF4C, 0x00000000,
+ 0xF00, 0x00000300,
+ 0x820, 0x01000100,
+ 0x800, 0x83040000,
+};
+
+u32 RTL8723BEPHY_REG_ARRAY_PG[] = {
+ 0, 0, 0, 0x00000e08, 0x0000ff00, 0x00004000,
+ 0, 0, 0, 0x0000086c, 0xffffff00, 0x34363800,
+ 0, 0, 0, 0x00000e00, 0xffffffff, 0x42444646,
+ 0, 0, 0, 0x00000e04, 0xffffffff, 0x30343840,
+ 0, 0, 0, 0x00000e10, 0xffffffff, 0x38404244,
+ 0, 0, 0, 0x00000e14, 0xffffffff, 0x26303436
+};
+
+u32 RTL8723BE_RADIOA_1TARRAY[] = {
+ 0x000, 0x00010000,
+ 0x0B0, 0x000DFFE0,
+ 0x0FE, 0x00000000,
+ 0x0FE, 0x00000000,
+ 0x0FE, 0x00000000,
+ 0x0B1, 0x00000018,
+ 0x0FE, 0x00000000,
+ 0x0FE, 0x00000000,
+ 0x0FE, 0x00000000,
+ 0x0B2, 0x00084C00,
+ 0x0B5, 0x0000D2CC,
+ 0x0B6, 0x000925AA,
+ 0x0B7, 0x00000010,
+ 0x0B8, 0x0000907F,
+ 0x05C, 0x00000002,
+ 0x07C, 0x00000002,
+ 0x07E, 0x00000005,
+ 0x08B, 0x0006FC00,
+ 0x0B0, 0x000FF9F0,
+ 0x01C, 0x000739D2,
+ 0x01E, 0x00000000,
+ 0x0DF, 0x00000780,
+ 0x050, 0x00067435,
+ 0x051, 0x0006B04E,
+ 0x052, 0x000007D2,
+ 0x053, 0x00000000,
+ 0x054, 0x00050400,
+ 0x055, 0x0004026E,
+ 0x0DD, 0x0000004C,
+ 0x070, 0x00067435,
+ 0x071, 0x0006B04E,
+ 0x072, 0x000007D2,
+ 0x073, 0x00000000,
+ 0x074, 0x00050400,
+ 0x075, 0x0004026E,
+ 0x0EF, 0x00000100,
+ 0x034, 0x0000ADD7,
+ 0x035, 0x00005C00,
+ 0x034, 0x00009DD4,
+ 0x035, 0x00005000,
+ 0x034, 0x00008DD1,
+ 0x035, 0x00004400,
+ 0x034, 0x00007DCE,
+ 0x035, 0x00003800,
+ 0x034, 0x00006CD1,
+ 0x035, 0x00004400,
+ 0x034, 0x00005CCE,
+ 0x035, 0x00003800,
+ 0x034, 0x000048CE,
+ 0x035, 0x00004400,
+ 0x034, 0x000034CE,
+ 0x035, 0x00003800,
+ 0x034, 0x00002451,
+ 0x035, 0x00004400,
+ 0x034, 0x0000144E,
+ 0x035, 0x00003800,
+ 0x034, 0x00000051,
+ 0x035, 0x00004400,
+ 0x0EF, 0x00000000,
+ 0x0EF, 0x00000100,
+ 0x0ED, 0x00000010,
+ 0x044, 0x0000ADD7,
+ 0x044, 0x00009DD4,
+ 0x044, 0x00008DD1,
+ 0x044, 0x00007DCE,
+ 0x044, 0x00006CC1,
+ 0x044, 0x00005CCE,
+ 0x044, 0x000044D1,
+ 0x044, 0x000034CE,
+ 0x044, 0x00002451,
+ 0x044, 0x0000144E,
+ 0x044, 0x00000051,
+ 0x0EF, 0x00000000,
+ 0x0ED, 0x00000000,
+ 0x0EF, 0x00002000,
+ 0x03B, 0x000380EF,
+ 0x03B, 0x000302FE,
+ 0x03B, 0x00028CE6,
+ 0x03B, 0x000200BC,
+ 0x03B, 0x000188A5,
+ 0x03B, 0x00010FBC,
+ 0x03B, 0x00008F71,
+ 0x03B, 0x00000900,
+ 0x0EF, 0x00000000,
+ 0x0ED, 0x00000001,
+ 0x040, 0x000380EF,
+ 0x040, 0x000302FE,
+ 0x040, 0x00028CE6,
+ 0x040, 0x000200BC,
+ 0x040, 0x000188A5,
+ 0x040, 0x00010FBC,
+ 0x040, 0x00008F71,
+ 0x040, 0x00000900,
+ 0x0ED, 0x00000000,
+ 0x082, 0x00080000,
+ 0x083, 0x00008000,
+ 0x084, 0x00048D80,
+ 0x085, 0x00068000,
+ 0x0A2, 0x00080000,
+ 0x0A3, 0x00008000,
+ 0x0A4, 0x00048D80,
+ 0x0A5, 0x00068000,
+ 0x000, 0x00033D80,
+};
+
+u32 RTL8723BEMAC_1T_ARRAY[] = {
+ 0x02F, 0x00000030,
+ 0x035, 0x00000000,
+ 0x428, 0x0000000A,
+ 0x429, 0x00000010,
+ 0x430, 0x00000000,
+ 0x431, 0x00000000,
+ 0x432, 0x00000000,
+ 0x433, 0x00000001,
+ 0x434, 0x00000004,
+ 0x435, 0x00000005,
+ 0x436, 0x00000007,
+ 0x437, 0x00000008,
+ 0x43C, 0x00000004,
+ 0x43D, 0x00000005,
+ 0x43E, 0x00000007,
+ 0x43F, 0x00000008,
+ 0x440, 0x0000005D,
+ 0x441, 0x00000001,
+ 0x442, 0x00000000,
+ 0x444, 0x00000010,
+ 0x445, 0x00000000,
+ 0x446, 0x00000000,
+ 0x447, 0x00000000,
+ 0x448, 0x00000000,
+ 0x449, 0x000000F0,
+ 0x44A, 0x0000000F,
+ 0x44B, 0x0000003E,
+ 0x44C, 0x00000010,
+ 0x44D, 0x00000000,
+ 0x44E, 0x00000000,
+ 0x44F, 0x00000000,
+ 0x450, 0x00000000,
+ 0x451, 0x000000F0,
+ 0x452, 0x0000000F,
+ 0x453, 0x00000000,
+ 0x456, 0x0000005E,
+ 0x460, 0x00000066,
+ 0x461, 0x00000066,
+ 0x4C8, 0x000000FF,
+ 0x4C9, 0x00000008,
+ 0x4CC, 0x000000FF,
+ 0x4CD, 0x000000FF,
+ 0x4CE, 0x00000001,
+ 0x500, 0x00000026,
+ 0x501, 0x000000A2,
+ 0x502, 0x0000002F,
+ 0x503, 0x00000000,
+ 0x504, 0x00000028,
+ 0x505, 0x000000A3,
+ 0x506, 0x0000005E,
+ 0x507, 0x00000000,
+ 0x508, 0x0000002B,
+ 0x509, 0x000000A4,
+ 0x50A, 0x0000005E,
+ 0x50B, 0x00000000,
+ 0x50C, 0x0000004F,
+ 0x50D, 0x000000A4,
+ 0x50E, 0x00000000,
+ 0x50F, 0x00000000,
+ 0x512, 0x0000001C,
+ 0x514, 0x0000000A,
+ 0x516, 0x0000000A,
+ 0x525, 0x0000004F,
+ 0x550, 0x00000010,
+ 0x551, 0x00000010,
+ 0x559, 0x00000002,
+ 0x55C, 0x00000050,
+ 0x55D, 0x000000FF,
+ 0x605, 0x00000030,
+ 0x608, 0x0000000E,
+ 0x609, 0x0000002A,
+ 0x620, 0x000000FF,
+ 0x621, 0x000000FF,
+ 0x622, 0x000000FF,
+ 0x623, 0x000000FF,
+ 0x624, 0x000000FF,
+ 0x625, 0x000000FF,
+ 0x626, 0x000000FF,
+ 0x627, 0x000000FF,
+ 0x638, 0x00000050,
+ 0x63C, 0x0000000A,
+ 0x63D, 0x0000000A,
+ 0x63E, 0x0000000E,
+ 0x63F, 0x0000000E,
+ 0x640, 0x00000040,
+ 0x642, 0x00000040,
+ 0x643, 0x00000000,
+ 0x652, 0x000000C8,
+ 0x66E, 0x00000005,
+ 0x700, 0x00000021,
+ 0x701, 0x00000043,
+ 0x702, 0x00000065,
+ 0x703, 0x00000087,
+ 0x708, 0x00000021,
+ 0x709, 0x00000043,
+ 0x70A, 0x00000065,
+ 0x70B, 0x00000087,
+};
+
+u32 RTL8723BEAGCTAB_1TARRAY[] = {
+ 0xC78, 0xFD000001,
+ 0xC78, 0xFC010001,
+ 0xC78, 0xFB020001,
+ 0xC78, 0xFA030001,
+ 0xC78, 0xF9040001,
+ 0xC78, 0xF8050001,
+ 0xC78, 0xF7060001,
+ 0xC78, 0xF6070001,
+ 0xC78, 0xF5080001,
+ 0xC78, 0xF4090001,
+ 0xC78, 0xF30A0001,
+ 0xC78, 0xF20B0001,
+ 0xC78, 0xF10C0001,
+ 0xC78, 0xF00D0001,
+ 0xC78, 0xEF0E0001,
+ 0xC78, 0xEE0F0001,
+ 0xC78, 0xED100001,
+ 0xC78, 0xEC110001,
+ 0xC78, 0xEB120001,
+ 0xC78, 0xEA130001,
+ 0xC78, 0xE9140001,
+ 0xC78, 0xE8150001,
+ 0xC78, 0xE7160001,
+ 0xC78, 0xAA170001,
+ 0xC78, 0xA9180001,
+ 0xC78, 0xA8190001,
+ 0xC78, 0xA71A0001,
+ 0xC78, 0xA61B0001,
+ 0xC78, 0xA51C0001,
+ 0xC78, 0xA41D0001,
+ 0xC78, 0xA31E0001,
+ 0xC78, 0x671F0001,
+ 0xC78, 0x66200001,
+ 0xC78, 0x65210001,
+ 0xC78, 0x64220001,
+ 0xC78, 0x63230001,
+ 0xC78, 0x62240001,
+ 0xC78, 0x61250001,
+ 0xC78, 0x47260001,
+ 0xC78, 0x46270001,
+ 0xC78, 0x45280001,
+ 0xC78, 0x44290001,
+ 0xC78, 0x432A0001,
+ 0xC78, 0x422B0001,
+ 0xC78, 0x292C0001,
+ 0xC78, 0x282D0001,
+ 0xC78, 0x272E0001,
+ 0xC78, 0x262F0001,
+ 0xC78, 0x25300001,
+ 0xC78, 0x24310001,
+ 0xC78, 0x09320001,
+ 0xC78, 0x08330001,
+ 0xC78, 0x07340001,
+ 0xC78, 0x06350001,
+ 0xC78, 0x05360001,
+ 0xC78, 0x04370001,
+ 0xC78, 0x03380001,
+ 0xC78, 0x02390001,
+ 0xC78, 0x013A0001,
+ 0xC78, 0x003B0001,
+ 0xC78, 0x003C0001,
+ 0xC78, 0x003D0001,
+ 0xC78, 0x003E0001,
+ 0xC78, 0x003F0001,
+ 0xC78, 0xFC400001,
+ 0xC78, 0xFB410001,
+ 0xC78, 0xFA420001,
+ 0xC78, 0xF9430001,
+ 0xC78, 0xF8440001,
+ 0xC78, 0xF7450001,
+ 0xC78, 0xF6460001,
+ 0xC78, 0xF5470001,
+ 0xC78, 0xF4480001,
+ 0xC78, 0xF3490001,
+ 0xC78, 0xF24A0001,
+ 0xC78, 0xF14B0001,
+ 0xC78, 0xF04C0001,
+ 0xC78, 0xEF4D0001,
+ 0xC78, 0xEE4E0001,
+ 0xC78, 0xED4F0001,
+ 0xC78, 0xEC500001,
+ 0xC78, 0xEB510001,
+ 0xC78, 0xEA520001,
+ 0xC78, 0xE9530001,
+ 0xC78, 0xE8540001,
+ 0xC78, 0xE7550001,
+ 0xC78, 0xE6560001,
+ 0xC78, 0xE5570001,
+ 0xC78, 0xAA580001,
+ 0xC78, 0xA9590001,
+ 0xC78, 0xA85A0001,
+ 0xC78, 0xA75B0001,
+ 0xC78, 0xA65C0001,
+ 0xC78, 0xA55D0001,
+ 0xC78, 0xA45E0001,
+ 0xC78, 0x675F0001,
+ 0xC78, 0x66600001,
+ 0xC78, 0x65610001,
+ 0xC78, 0x64620001,
+ 0xC78, 0x63630001,
+ 0xC78, 0x62640001,
+ 0xC78, 0x61650001,
+ 0xC78, 0x47660001,
+ 0xC78, 0x46670001,
+ 0xC78, 0x45680001,
+ 0xC78, 0x44690001,
+ 0xC78, 0x436A0001,
+ 0xC78, 0x426B0001,
+ 0xC78, 0x296C0001,
+ 0xC78, 0x286D0001,
+ 0xC78, 0x276E0001,
+ 0xC78, 0x266F0001,
+ 0xC78, 0x25700001,
+ 0xC78, 0x24710001,
+ 0xC78, 0x09720001,
+ 0xC78, 0x08730001,
+ 0xC78, 0x07740001,
+ 0xC78, 0x06750001,
+ 0xC78, 0x05760001,
+ 0xC78, 0x04770001,
+ 0xC78, 0x03780001,
+ 0xC78, 0x02790001,
+ 0xC78, 0x017A0001,
+ 0xC78, 0x007B0001,
+ 0xC78, 0x007C0001,
+ 0xC78, 0x007D0001,
+ 0xC78, 0x007E0001,
+ 0xC78, 0x007F0001,
+ 0xC50, 0x69553422,
+ 0xC50, 0x69553420,
+};
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/table.h b/drivers/net/wireless/rtlwifi/rtl8723be/table.h
new file mode 100644
index 000000000000..932760a84827
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/table.h
@@ -0,0 +1,43 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Created on 2010/ 5/18, 1:41
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_TABLE__H_
+#define __RTL8723BE_TABLE__H_
+
+#include <linux/types.h>
+#define RTL8723BEPHY_REG_1TARRAYLEN 388
+extern u32 RTL8723BEPHY_REG_1TARRAY[];
+#define RTL8723BEPHY_REG_ARRAY_PGLEN 36
+extern u32 RTL8723BEPHY_REG_ARRAY_PG[];
+#define RTL8723BE_RADIOA_1TARRAYLEN 206
+extern u32 RTL8723BE_RADIOA_1TARRAY[];
+#define RTL8723BEMAC_1T_ARRAYLEN 194
+extern u32 RTL8723BEMAC_1T_ARRAY[];
+#define RTL8723BEAGCTAB_1TARRAYLEN 260
+extern u32 RTL8723BEAGCTAB_1TARRAY[];
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c
new file mode 100644
index 000000000000..e0a0d8c8fed5
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c
@@ -0,0 +1,960 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "../base.h"
+#include "../stats.h"
+#include "reg.h"
+#include "def.h"
+#include "phy.h"
+#include "trx.h"
+#include "led.h"
+#include "dm.h"
+#include "phy.h"
+
+static u8 _rtl8723be_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue)
+{
+ __le16 fc = rtl_get_fc(skb);
+
+ if (unlikely(ieee80211_is_beacon(fc)))
+ return QSLT_BEACON;
+ if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc))
+ return QSLT_MGNT;
+
+ return skb->priority;
+}
+
+/* mac80211's rate_idx is like this:
+ *
+ * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ
+ *
+ * B/G rate:
+ * (rx_status->flag & RX_FLAG_HT) = 0,
+ * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11,
+ *
+ * N rate:
+ * (rx_status->flag & RX_FLAG_HT) = 1,
+ * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15
+ *
+ * 5G band:rx_status->band == IEEE80211_BAND_5GHZ
+ * A rate:
+ * (rx_status->flag & RX_FLAG_HT) = 0,
+ * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7,
+ *
+ * N rate:
+ * (rx_status->flag & RX_FLAG_HT) = 1,
+ * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15
+ */
+static int _rtl8723be_rate_mapping(struct ieee80211_hw *hw,
+ bool isht, u8 desc_rate)
+{
+ int rate_idx;
+
+ if (!isht) {
+ if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) {
+ switch (desc_rate) {
+ case DESC92C_RATE1M:
+ rate_idx = 0;
+ break;
+ case DESC92C_RATE2M:
+ rate_idx = 1;
+ break;
+ case DESC92C_RATE5_5M:
+ rate_idx = 2;
+ break;
+ case DESC92C_RATE11M:
+ rate_idx = 3;
+ break;
+ case DESC92C_RATE6M:
+ rate_idx = 4;
+ break;
+ case DESC92C_RATE9M:
+ rate_idx = 5;
+ break;
+ case DESC92C_RATE12M:
+ rate_idx = 6;
+ break;
+ case DESC92C_RATE18M:
+ rate_idx = 7;
+ break;
+ case DESC92C_RATE24M:
+ rate_idx = 8;
+ break;
+ case DESC92C_RATE36M:
+ rate_idx = 9;
+ break;
+ case DESC92C_RATE48M:
+ rate_idx = 10;
+ break;
+ case DESC92C_RATE54M:
+ rate_idx = 11;
+ break;
+ default:
+ rate_idx = 0;
+ break;
+ }
+ } else {
+ switch (desc_rate) {
+ case DESC92C_RATE6M:
+ rate_idx = 0;
+ break;
+ case DESC92C_RATE9M:
+ rate_idx = 1;
+ break;
+ case DESC92C_RATE12M:
+ rate_idx = 2;
+ break;
+ case DESC92C_RATE18M:
+ rate_idx = 3;
+ break;
+ case DESC92C_RATE24M:
+ rate_idx = 4;
+ break;
+ case DESC92C_RATE36M:
+ rate_idx = 5;
+ break;
+ case DESC92C_RATE48M:
+ rate_idx = 6;
+ break;
+ case DESC92C_RATE54M:
+ rate_idx = 7;
+ break;
+ default:
+ rate_idx = 0;
+ break;
+ }
+ }
+ } else {
+ switch (desc_rate) {
+ case DESC92C_RATEMCS0:
+ rate_idx = 0;
+ break;
+ case DESC92C_RATEMCS1:
+ rate_idx = 1;
+ break;
+ case DESC92C_RATEMCS2:
+ rate_idx = 2;
+ break;
+ case DESC92C_RATEMCS3:
+ rate_idx = 3;
+ break;
+ case DESC92C_RATEMCS4:
+ rate_idx = 4;
+ break;
+ case DESC92C_RATEMCS5:
+ rate_idx = 5;
+ break;
+ case DESC92C_RATEMCS6:
+ rate_idx = 6;
+ break;
+ case DESC92C_RATEMCS7:
+ rate_idx = 7;
+ break;
+ case DESC92C_RATEMCS8:
+ rate_idx = 8;
+ break;
+ case DESC92C_RATEMCS9:
+ rate_idx = 9;
+ break;
+ case DESC92C_RATEMCS10:
+ rate_idx = 10;
+ break;
+ case DESC92C_RATEMCS11:
+ rate_idx = 11;
+ break;
+ case DESC92C_RATEMCS12:
+ rate_idx = 12;
+ break;
+ case DESC92C_RATEMCS13:
+ rate_idx = 13;
+ break;
+ case DESC92C_RATEMCS14:
+ rate_idx = 14;
+ break;
+ case DESC92C_RATEMCS15:
+ rate_idx = 15;
+ break;
+ default:
+ rate_idx = 0;
+ break;
+ }
+ }
+ return rate_idx;
+}
+
+static void _rtl8723be_query_rxphystatus(struct ieee80211_hw *hw,
+ struct rtl_stats *pstatus, u8 *pdesc,
+ struct rx_fwinfo_8723be *p_drvinfo,
+ bool packet_match_bssid,
+ bool packet_toself,
+ bool packet_beacon)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_ps_ctl *ppsc = rtl_psc(rtlpriv);
+ struct phy_sts_cck_8723e_t *cck_buf;
+ struct phy_status_rpt *p_phystrpt = (struct phy_status_rpt *)p_drvinfo;
+ struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw));
+ char rx_pwr_all = 0, rx_pwr[4];
+ u8 rf_rx_num = 0, evm, pwdb_all;
+ u8 i, max_spatial_stream;
+ u32 rssi, total_rssi = 0;
+ bool is_cck = pstatus->is_cck;
+ u8 lan_idx, vga_idx;
+
+ /* Record it for next packet processing */
+ pstatus->packet_matchbssid = packet_match_bssid;
+ pstatus->packet_toself = packet_toself;
+ pstatus->packet_beacon = packet_beacon;
+ pstatus->rx_mimo_sig_qual[0] = -1;
+ pstatus->rx_mimo_sig_qual[1] = -1;
+
+ if (is_cck) {
+ u8 cck_highpwr;
+ u8 cck_agc_rpt;
+ /* CCK Driver info Structure is not the same as OFDM packet. */
+ cck_buf = (struct phy_sts_cck_8723e_t *)p_drvinfo;
+ cck_agc_rpt = cck_buf->cck_agc_rpt;
+
+ /* (1)Hardware does not provide RSSI for CCK
+ * (2)PWDB, Average PWDB cacluated by
+ * hardware (for rate adaptive)
+ */
+ if (ppsc->rfpwr_state == ERFON)
+ cck_highpwr = (u8) rtl_get_bbreg(hw,
+ RFPGA0_XA_HSSIPARAMETER2,
+ BIT(9));
+ else
+ cck_highpwr = false;
+
+ lan_idx = ((cck_agc_rpt & 0xE0) >> 5);
+ vga_idx = (cck_agc_rpt & 0x1f);
+ switch (lan_idx) {
+ case 7:
+ if (vga_idx <= 27)/*VGA_idx = 27~2*/
+ rx_pwr_all = -100 + 2 * (27 - vga_idx);
+ else
+ rx_pwr_all = -100;
+ break;
+ case 6:/*VGA_idx = 2~0*/
+ rx_pwr_all = -48 + 2 * (2 - vga_idx);
+ break;
+ case 5:/*VGA_idx = 7~5*/
+ rx_pwr_all = -42 + 2 * (7 - vga_idx);
+ break;
+ case 4:/*VGA_idx = 7~4*/
+ rx_pwr_all = -36 + 2 * (7 - vga_idx);
+ break;
+ case 3:/*VGA_idx = 7~0*/
+ rx_pwr_all = -24 + 2 * (7 - vga_idx);
+ break;
+ case 2:
+ if (cck_highpwr)/*VGA_idx = 5~0*/
+ rx_pwr_all = -12 + 2 * (5 - vga_idx);
+ else
+ rx_pwr_all = -6 + 2 * (5 - vga_idx);
+ break;
+ case 1:
+ rx_pwr_all = 8 - 2 * vga_idx;
+ break;
+ case 0:
+ rx_pwr_all = 14 - 2 * vga_idx;
+ break;
+ default:
+ break;
+ }
+ rx_pwr_all += 6;
+ pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all);
+ /* CCK gain is smaller than OFDM/MCS gain, */
+ /* so we add gain diff by experiences,
+ * the val is 6
+ */
+ pwdb_all += 6;
+ if (pwdb_all > 100)
+ pwdb_all = 100;
+ /* modify the offset to make the same gain index with OFDM. */
+ if (pwdb_all > 34 && pwdb_all <= 42)
+ pwdb_all -= 2;
+ else if (pwdb_all > 26 && pwdb_all <= 34)
+ pwdb_all -= 6;
+ else if (pwdb_all > 14 && pwdb_all <= 26)
+ pwdb_all -= 8;
+ else if (pwdb_all > 4 && pwdb_all <= 14)
+ pwdb_all -= 4;
+ if (!cck_highpwr) {
+ if (pwdb_all >= 80)
+ pwdb_all = ((pwdb_all - 80) << 1) +
+ ((pwdb_all - 80) >> 1) + 80;
+ else if ((pwdb_all <= 78) && (pwdb_all >= 20))
+ pwdb_all += 3;
+ if (pwdb_all > 100)
+ pwdb_all = 100;
+ }
+
+ pstatus->rx_pwdb_all = pwdb_all;
+ pstatus->recvsignalpower = rx_pwr_all;
+
+ /* (3) Get Signal Quality (EVM) */
+ if (packet_match_bssid) {
+ u8 sq;
+
+ if (pstatus->rx_pwdb_all > 40) {
+ sq = 100;
+ } else {
+ sq = cck_buf->sq_rpt;
+ if (sq > 64)
+ sq = 0;
+ else if (sq < 20)
+ sq = 100;
+ else
+ sq = ((64 - sq) * 100) / 44;
+ }
+
+ pstatus->signalquality = sq;
+ pstatus->rx_mimo_sig_qual[0] = sq;
+ pstatus->rx_mimo_sig_qual[1] = -1;
+ }
+ } else {
+ rtlpriv->dm.rfpath_rxenable[0] = true;
+ rtlpriv->dm.rfpath_rxenable[1] = true;
+
+ /* (1)Get RSSI for HT rate */
+ for (i = RF90_PATH_A; i < RF6052_MAX_PATH; i++) {
+ /* we will judge RF RX path now. */
+ if (rtlpriv->dm.rfpath_rxenable[i])
+ rf_rx_num++;
+
+ rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f)*2) - 110;
+
+ /* Translate DBM to percentage. */
+ rssi = rtl_query_rxpwrpercentage(rx_pwr[i]);
+ total_rssi += rssi;
+
+ /* Get Rx snr value in DB */
+ rtlpriv->stats.rx_snr_db[i] =
+ (long)(p_drvinfo->rxsnr[i] / 2);
+
+ /* Record Signal Strength for next packet */
+ if (packet_match_bssid)
+ pstatus->rx_mimo_signalstrength[i] = (u8) rssi;
+ }
+
+ /* (2)PWDB, Avg cacluated by hardware (for rate adaptive) */
+ rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 110;
+
+ pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all);
+ pstatus->rx_pwdb_all = pwdb_all;
+ pstatus->rxpower = rx_pwr_all;
+ pstatus->recvsignalpower = rx_pwr_all;
+
+ /* (3)EVM of HT rate */
+ if (pstatus->is_ht && pstatus->rate >= DESC92C_RATEMCS8 &&
+ pstatus->rate <= DESC92C_RATEMCS15)
+ max_spatial_stream = 2;
+ else
+ max_spatial_stream = 1;
+
+ for (i = 0; i < max_spatial_stream; i++) {
+ evm = rtl_evm_db_to_percentage(p_drvinfo->rxevm[i]);
+
+ if (packet_match_bssid) {
+ /* Fill value in RFD, Get the first
+ * spatial stream only
+ */
+ if (i == 0)
+ pstatus->signalquality =
+ (u8) (evm & 0xff);
+ pstatus->rx_mimo_sig_qual[i] =
+ (u8) (evm & 0xff);
+ }
+ }
+ if (packet_match_bssid) {
+ for (i = RF90_PATH_A; i <= RF90_PATH_B; i++)
+ rtl_priv(hw)->dm.cfo_tail[i] =
+ (char)p_phystrpt->path_cfotail[i];
+
+ rtl_priv(hw)->dm.packet_count++;
+ if (rtl_priv(hw)->dm.packet_count == 0xffffffff)
+ rtl_priv(hw)->dm.packet_count = 0;
+ }
+ }
+
+ /* UI BSS List signal strength(in percentage),
+ * make it good looking, from 0~100.
+ */
+ if (is_cck)
+ pstatus->signalstrength = (u8)(rtl_signal_scale_mapping(hw,
+ pwdb_all));
+ else if (rf_rx_num != 0)
+ pstatus->signalstrength = (u8)(rtl_signal_scale_mapping(hw,
+ total_rssi /= rf_rx_num));
+ /*HW antenna diversity*/
+ rtldm->fat_table.antsel_rx_keep_0 = p_phystrpt->ant_sel;
+ rtldm->fat_table.antsel_rx_keep_1 = p_phystrpt->ant_sel_b;
+ rtldm->fat_table.antsel_rx_keep_2 = p_phystrpt->antsel_rx_keep_2;
+}
+
+static void _rtl8723be_translate_rx_signal_stuff(struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct rtl_stats *pstatus,
+ u8 *pdesc,
+ struct rx_fwinfo_8723be *p_drvinfo)
+{
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
+ struct ieee80211_hdr *hdr;
+ u8 *tmp_buf;
+ u8 *praddr;
+ u8 *psaddr;
+ u16 fc, type;
+ bool packet_matchbssid, packet_toself, packet_beacon;
+
+ tmp_buf = skb->data + pstatus->rx_drvinfo_size + pstatus->rx_bufshift;
+
+ hdr = (struct ieee80211_hdr *)tmp_buf;
+ fc = le16_to_cpu(hdr->frame_control);
+ type = WLAN_FC_GET_TYPE(hdr->frame_control);
+ praddr = hdr->addr1;
+ psaddr = ieee80211_get_SA(hdr);
+ memcpy(pstatus->psaddr, psaddr, ETH_ALEN);
+
+ packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) &&
+ (!ether_addr_equal(mac->bssid, (fc & IEEE80211_FCTL_TODS) ?
+ hdr->addr1 : (fc & IEEE80211_FCTL_FROMDS) ?
+ hdr->addr2 : hdr->addr3)) &&
+ (!pstatus->hwerror) &&
+ (!pstatus->crc) && (!pstatus->icv));
+
+ packet_toself = packet_matchbssid &&
+ (!ether_addr_equal(praddr, rtlefuse->dev_addr));
+
+ /* YP: packet_beacon is not initialized,
+ * this assignment is neccesary,
+ * otherwise it counld be true in this case
+ * the situation is much worse in Kernel 3.10
+ */
+ if (ieee80211_is_beacon(hdr->frame_control))
+ packet_beacon = true;
+ else
+ packet_beacon = false;
+
+ if (packet_beacon && packet_matchbssid)
+ rtl_priv(hw)->dm.dbginfo.num_qry_beacon_pkt++;
+
+ _rtl8723be_query_rxphystatus(hw, pstatus, pdesc, p_drvinfo,
+ packet_matchbssid,
+ packet_toself,
+ packet_beacon);
+
+ rtl_process_phyinfo(hw, tmp_buf, pstatus);
+}
+
+static void _rtl8723be_insert_emcontent(struct rtl_tcb_desc *ptcb_desc,
+ u8 *virtualaddress)
+{
+ u32 dwtmp = 0;
+ memset(virtualaddress, 0, 8);
+
+ SET_EARLYMODE_PKTNUM(virtualaddress, ptcb_desc->empkt_num);
+ if (ptcb_desc->empkt_num == 1) {
+ dwtmp = ptcb_desc->empkt_len[0];
+ } else {
+ dwtmp = ptcb_desc->empkt_len[0];
+ dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4;
+ dwtmp += ptcb_desc->empkt_len[1];
+ }
+ SET_EARLYMODE_LEN0(virtualaddress, dwtmp);
+
+ if (ptcb_desc->empkt_num <= 3) {
+ dwtmp = ptcb_desc->empkt_len[2];
+ } else {
+ dwtmp = ptcb_desc->empkt_len[2];
+ dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4;
+ dwtmp += ptcb_desc->empkt_len[3];
+ }
+ SET_EARLYMODE_LEN1(virtualaddress, dwtmp);
+ if (ptcb_desc->empkt_num <= 5) {
+ dwtmp = ptcb_desc->empkt_len[4];
+ } else {
+ dwtmp = ptcb_desc->empkt_len[4];
+ dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4;
+ dwtmp += ptcb_desc->empkt_len[5];
+ }
+ SET_EARLYMODE_LEN2_1(virtualaddress, dwtmp & 0xF);
+ SET_EARLYMODE_LEN2_2(virtualaddress, dwtmp >> 4);
+ if (ptcb_desc->empkt_num <= 7) {
+ dwtmp = ptcb_desc->empkt_len[6];
+ } else {
+ dwtmp = ptcb_desc->empkt_len[6];
+ dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4;
+ dwtmp += ptcb_desc->empkt_len[7];
+ }
+ SET_EARLYMODE_LEN3(virtualaddress, dwtmp);
+ if (ptcb_desc->empkt_num <= 9) {
+ dwtmp = ptcb_desc->empkt_len[8];
+ } else {
+ dwtmp = ptcb_desc->empkt_len[8];
+ dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4;
+ dwtmp += ptcb_desc->empkt_len[9];
+ }
+ SET_EARLYMODE_LEN4(virtualaddress, dwtmp);
+}
+
+bool rtl8723be_rx_query_desc(struct ieee80211_hw *hw,
+ struct rtl_stats *status,
+ struct ieee80211_rx_status *rx_status,
+ u8 *pdesc, struct sk_buff *skb)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rx_fwinfo_8723be *p_drvinfo;
+ struct ieee80211_hdr *hdr;
+
+ u32 phystatus = GET_RX_DESC_PHYST(pdesc);
+ status->packet_report_type = (u8)GET_RX_STATUS_DESC_RPT_SEL(pdesc);
+ if (status->packet_report_type == TX_REPORT2)
+ status->length = (u16) GET_RX_RPT2_DESC_PKT_LEN(pdesc);
+ else
+ status->length = (u16) GET_RX_DESC_PKT_LEN(pdesc);
+ status->rx_drvinfo_size = (u8) GET_RX_DESC_DRV_INFO_SIZE(pdesc) *
+ RX_DRV_INFO_SIZE_UNIT;
+ status->rx_bufshift = (u8) (GET_RX_DESC_SHIFT(pdesc) & 0x03);
+ status->icv = (u16) GET_RX_DESC_ICV(pdesc);
+ status->crc = (u16) GET_RX_DESC_CRC32(pdesc);
+ status->hwerror = (status->crc | status->icv);
+ status->decrypted = !GET_RX_DESC_SWDEC(pdesc);
+ status->rate = (u8) GET_RX_DESC_RXMCS(pdesc);
+ status->shortpreamble = (u16) GET_RX_DESC_SPLCP(pdesc);
+ status->isampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1);
+ status->isfirst_ampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1);
+ if (status->packet_report_type == NORMAL_RX)
+ status->timestamp_low = GET_RX_DESC_TSFL(pdesc);
+ status->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc);
+ status->is_ht = (bool)GET_RX_DESC_RXHT(pdesc);
+
+ status->is_cck = RTL8723E_RX_HAL_IS_CCK_RATE(status->rate);
+
+ status->macid = GET_RX_DESC_MACID(pdesc);
+ if (GET_RX_STATUS_DESC_MAGIC_MATCH(pdesc))
+ status->wake_match = BIT(2);
+ else if (GET_RX_STATUS_DESC_MAGIC_MATCH(pdesc))
+ status->wake_match = BIT(1);
+ else if (GET_RX_STATUS_DESC_UNICAST_MATCH(pdesc))
+ status->wake_match = BIT(0);
+ else
+ status->wake_match = 0;
+ if (status->wake_match)
+ RT_TRACE(rtlpriv, COMP_RXDESC, DBG_LOUD,
+ "GGGGGGGGGGGGGet Wakeup Packet!! WakeMatch=%d\n",
+ status->wake_match);
+ rx_status->freq = hw->conf.chandef.chan->center_freq;
+ rx_status->band = hw->conf.chandef.chan->band;
+
+
+ hdr = (struct ieee80211_hdr *)(skb->data + status->rx_drvinfo_size +
+ status->rx_bufshift);
+
+ if (status->crc)
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+ if (status->rx_is40Mhzpacket)
+ rx_status->flag |= RX_FLAG_40MHZ;
+
+ if (status->is_ht)
+ rx_status->flag |= RX_FLAG_HT;
+
+ rx_status->flag |= RX_FLAG_MACTIME_START;
+
+ /* hw will set status->decrypted true, if it finds the
+ * frame is open data frame or mgmt frame.
+ * So hw will not decryption robust managment frame
+ * for IEEE80211w but still set status->decrypted
+ * true, so here we should set it back to undecrypted
+ * for IEEE80211w frame, and mac80211 sw will help
+ * to decrypt it
+ */
+ if (status->decrypted) {
+ if (!hdr) {
+ WARN_ON_ONCE(true);
+ pr_err("decrypted is true but hdr NULL in skb %p\n",
+ rtl_get_hdr(skb));
+ return false;
+ }
+
+ if ((_ieee80211_is_robust_mgmt_frame(hdr)) &&
+ (ieee80211_has_protected(hdr->frame_control)))
+ rx_status->flag &= ~RX_FLAG_DECRYPTED;
+ else
+ rx_status->flag |= RX_FLAG_DECRYPTED;
+ }
+
+ /* rate_idx: index of data rate into band's
+ * supported rates or MCS index if HT rates
+ * are use (RX_FLAG_HT)
+ * Notice: this is diff with windows define
+ */
+ rx_status->rate_idx = _rtl8723be_rate_mapping(hw, status->is_ht,
+ status->rate);
+
+ rx_status->mactime = status->timestamp_low;
+ if (phystatus) {
+ p_drvinfo = (struct rx_fwinfo_8723be *)(skb->data +
+ status->rx_bufshift);
+
+ _rtl8723be_translate_rx_signal_stuff(hw, skb, status,
+ pdesc, p_drvinfo);
+ }
+
+ /*rx_status->qual = status->signal; */
+ rx_status->signal = status->recvsignalpower + 10;
+ if (status->packet_report_type == TX_REPORT2) {
+ status->macid_valid_entry[0] =
+ GET_RX_RPT2_DESC_MACID_VALID_1(pdesc);
+ status->macid_valid_entry[1] =
+ GET_RX_RPT2_DESC_MACID_VALID_2(pdesc);
+ }
+ return true;
+}
+
+void rtl8723be_tx_fill_desc(struct ieee80211_hw *hw,
+ struct ieee80211_hdr *hdr, u8 *pdesc_tx,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta, struct sk_buff *skb,
+ u8 hw_queue, struct rtl_tcb_desc *ptcb_desc)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ u8 *pdesc = pdesc_tx;
+ u16 seq_number;
+ __le16 fc = hdr->frame_control;
+ unsigned int buf_len = 0;
+ unsigned int skb_len = skb->len;
+ u8 fw_qsel = _rtl8723be_map_hwqueue_to_fwqueue(skb, hw_queue);
+ bool firstseg = ((hdr->seq_ctrl &
+ cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0);
+ bool lastseg = ((hdr->frame_control &
+ cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) == 0);
+ dma_addr_t mapping;
+ u8 bw_40 = 0;
+ u8 short_gi = 0;
+
+ if (mac->opmode == NL80211_IFTYPE_STATION) {
+ bw_40 = mac->bw_40;
+ } else if (mac->opmode == NL80211_IFTYPE_AP ||
+ mac->opmode == NL80211_IFTYPE_ADHOC) {
+ if (sta)
+ bw_40 = sta->ht_cap.cap &
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ }
+ seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
+ rtl_get_tcb_desc(hw, info, sta, skb, ptcb_desc);
+ /* reserve 8 byte for AMPDU early mode */
+ if (rtlhal->earlymode_enable) {
+ skb_push(skb, EM_HDR_LEN);
+ memset(skb->data, 0, EM_HDR_LEN);
+ }
+ buf_len = skb->len;
+ mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(rtlpci->pdev, mapping)) {
+ RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "DMA mapping error");
+ return;
+ }
+ CLEAR_PCI_TX_DESC_CONTENT(pdesc, sizeof(struct tx_desc_8723be));
+ if (ieee80211_is_nullfunc(fc) || ieee80211_is_ctl(fc)) {
+ firstseg = true;
+ lastseg = true;
+ }
+ if (firstseg) {
+ if (rtlhal->earlymode_enable) {
+ SET_TX_DESC_PKT_OFFSET(pdesc, 1);
+ SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN +
+ EM_HDR_LEN);
+ if (ptcb_desc->empkt_num) {
+ RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE,
+ "Insert 8 byte.pTcb->EMPktNum:%d\n",
+ ptcb_desc->empkt_num);
+ _rtl8723be_insert_emcontent(ptcb_desc,
+ (u8 *)(skb->data));
+ }
+ } else {
+ SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN);
+ }
+
+ /* ptcb_desc->use_driver_rate = true; */
+ SET_TX_DESC_TX_RATE(pdesc, ptcb_desc->hw_rate);
+ if (ptcb_desc->hw_rate > DESC92C_RATEMCS0)
+ short_gi = (ptcb_desc->use_shortgi) ? 1 : 0;
+ else
+ short_gi = (ptcb_desc->use_shortpreamble) ? 1 : 0;
+
+ SET_TX_DESC_DATA_SHORTGI(pdesc, short_gi);
+
+ if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+ SET_TX_DESC_AGG_ENABLE(pdesc, 1);
+ SET_TX_DESC_MAX_AGG_NUM(pdesc, 0x14);
+ }
+ SET_TX_DESC_SEQ(pdesc, seq_number);
+ SET_TX_DESC_RTS_ENABLE(pdesc, ((ptcb_desc->rts_enable &&
+ !ptcb_desc->cts_enable) ?
+ 1 : 0));
+ SET_TX_DESC_HW_RTS_ENABLE(pdesc, 0);
+ SET_TX_DESC_CTS2SELF(pdesc, ((ptcb_desc->cts_enable) ?
+ 1 : 0));
+
+ SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate);
+
+ SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc);
+ SET_TX_DESC_RTS_SHORT(pdesc,
+ ((ptcb_desc->rts_rate <= DESC92C_RATE54M) ?
+ (ptcb_desc->rts_use_shortpreamble ? 1 : 0) :
+ (ptcb_desc->rts_use_shortgi ? 1 : 0)));
+
+ if (ptcb_desc->btx_enable_sw_calc_duration)
+ SET_TX_DESC_NAV_USE_HDR(pdesc, 1);
+
+ if (bw_40) {
+ if (ptcb_desc->packet_bw) {
+ SET_TX_DESC_DATA_BW(pdesc, 1);
+ SET_TX_DESC_TX_SUB_CARRIER(pdesc, 3);
+ } else {
+ SET_TX_DESC_DATA_BW(pdesc, 0);
+ SET_TX_DESC_TX_SUB_CARRIER(pdesc, mac->cur_40_prime_sc);
+ }
+ } else {
+ SET_TX_DESC_DATA_BW(pdesc, 0);
+ SET_TX_DESC_TX_SUB_CARRIER(pdesc, 0);
+ }
+
+ SET_TX_DESC_LINIP(pdesc, 0);
+ SET_TX_DESC_PKT_SIZE(pdesc, (u16) skb_len);
+ if (sta) {
+ u8 ampdu_density = sta->ht_cap.ampdu_density;
+ SET_TX_DESC_AMPDU_DENSITY(pdesc, ampdu_density);
+ }
+ if (info->control.hw_key) {
+ struct ieee80211_key_conf *keyconf =
+ info->control.hw_key;
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ case WLAN_CIPHER_SUITE_TKIP:
+ SET_TX_DESC_SEC_TYPE(pdesc, 0x1);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ SET_TX_DESC_SEC_TYPE(pdesc, 0x3);
+ break;
+ default:
+ SET_TX_DESC_SEC_TYPE(pdesc, 0x0);
+ break;
+ }
+ }
+
+ SET_TX_DESC_QUEUE_SEL(pdesc, fw_qsel);
+ SET_TX_DESC_DATA_RATE_FB_LIMIT(pdesc, 0x1F);
+ SET_TX_DESC_RTS_RATE_FB_LIMIT(pdesc, 0xF);
+ SET_TX_DESC_DISABLE_FB(pdesc, ptcb_desc->disable_ratefallback ?
+ 1 : 0);
+ SET_TX_DESC_USE_RATE(pdesc, ptcb_desc->use_driver_rate ? 1 : 0);
+
+ if (ieee80211_is_data_qos(fc)) {
+ if (mac->rdg_en) {
+ RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE,
+ "Enable RDG function.\n");
+ SET_TX_DESC_RDG_ENABLE(pdesc, 1);
+ SET_TX_DESC_HTC(pdesc, 1);
+ }
+ }
+ }
+
+ SET_TX_DESC_FIRST_SEG(pdesc, (firstseg ? 1 : 0));
+ SET_TX_DESC_LAST_SEG(pdesc, (lastseg ? 1 : 0));
+ SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) buf_len);
+ SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping);
+ SET_TX_DESC_RATE_ID(pdesc, ptcb_desc->ratr_index);
+ SET_TX_DESC_MACID(pdesc, ptcb_desc->mac_id);
+
+ if (!ieee80211_is_data_qos(fc)) {
+ SET_TX_DESC_HWSEQ_EN(pdesc, 1);
+ SET_TX_DESC_HWSEQ_SEL(pdesc, 0);
+ }
+ SET_TX_DESC_MORE_FRAG(pdesc, (lastseg ? 0 : 1));
+ if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) ||
+ is_broadcast_ether_addr(ieee80211_get_DA(hdr))) {
+ SET_TX_DESC_BMC(pdesc, 1);
+ }
+ RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "\n");
+}
+
+void rtl8723be_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
+ bool b_firstseg, bool b_lastseg,
+ struct sk_buff *skb)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ u8 fw_queue = QSLT_BEACON;
+
+ dma_addr_t mapping = pci_map_single(rtlpci->pdev,
+ skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+
+ if (pci_dma_mapping_error(rtlpci->pdev, mapping)) {
+ RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE,
+ "DMA mapping error");
+ return;
+ }
+ CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE);
+
+ SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN);
+
+ SET_TX_DESC_TX_RATE(pdesc, DESC92C_RATE1M);
+
+ SET_TX_DESC_SEQ(pdesc, 0);
+
+ SET_TX_DESC_LINIP(pdesc, 0);
+
+ SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue);
+
+ SET_TX_DESC_FIRST_SEG(pdesc, 1);
+ SET_TX_DESC_LAST_SEG(pdesc, 1);
+
+ SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16)(skb->len));
+
+ SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping);
+
+ SET_TX_DESC_RATE_ID(pdesc, 0);
+ SET_TX_DESC_MACID(pdesc, 0);
+
+ SET_TX_DESC_OWN(pdesc, 1);
+
+ SET_TX_DESC_PKT_SIZE(pdesc, (u16)(skb->len));
+
+ SET_TX_DESC_FIRST_SEG(pdesc, 1);
+ SET_TX_DESC_LAST_SEG(pdesc, 1);
+
+ SET_TX_DESC_USE_RATE(pdesc, 1);
+}
+
+void rtl8723be_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val)
+{
+ if (istx) {
+ switch (desc_name) {
+ case HW_DESC_OWN:
+ SET_TX_DESC_OWN(pdesc, 1);
+ break;
+ case HW_DESC_TX_NEXTDESC_ADDR:
+ SET_TX_DESC_NEXT_DESC_ADDRESS(pdesc, *(u32 *)val);
+ break;
+ default:
+ RT_ASSERT(false, "ERR txdesc :%d not process\n",
+ desc_name);
+ break;
+ }
+ } else {
+ switch (desc_name) {
+ case HW_DESC_RXOWN:
+ SET_RX_DESC_OWN(pdesc, 1);
+ break;
+ case HW_DESC_RXBUFF_ADDR:
+ SET_RX_DESC_BUFF_ADDR(pdesc, *(u32 *)val);
+ break;
+ case HW_DESC_RXPKT_LEN:
+ SET_RX_DESC_PKT_LEN(pdesc, *(u32 *)val);
+ break;
+ case HW_DESC_RXERO:
+ SET_RX_DESC_EOR(pdesc, 1);
+ break;
+ default:
+ RT_ASSERT(false, "ERR rxdesc :%d not process\n",
+ desc_name);
+ break;
+ }
+ }
+}
+
+u32 rtl8723be_get_desc(u8 *pdesc, bool istx, u8 desc_name)
+{
+ u32 ret = 0;
+
+ if (istx) {
+ switch (desc_name) {
+ case HW_DESC_OWN:
+ ret = GET_TX_DESC_OWN(pdesc);
+ break;
+ case HW_DESC_TXBUFF_ADDR:
+ ret = GET_TX_DESC_TX_BUFFER_ADDRESS(pdesc);
+ break;
+ default:
+ RT_ASSERT(false, "ERR txdesc :%d not process\n",
+ desc_name);
+ break;
+ }
+ } else {
+ switch (desc_name) {
+ case HW_DESC_OWN:
+ ret = GET_RX_DESC_OWN(pdesc);
+ break;
+ case HW_DESC_RXPKT_LEN:
+ ret = GET_RX_DESC_PKT_LEN(pdesc);
+ break;
+ default:
+ RT_ASSERT(false, "ERR rxdesc :%d not process\n",
+ desc_name);
+ break;
+ }
+ }
+ return ret;
+}
+
+bool rtl8723be_is_tx_desc_closed(struct ieee80211_hw *hw,
+ u8 hw_queue, u16 index)
+{
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue];
+ u8 *entry = (u8 *)(&ring->desc[ring->idx]);
+ u8 own = (u8) rtl8723be_get_desc(entry, true, HW_DESC_OWN);
+
+ /*beacon packet will only use the first
+ *descriptor by default, and the own may not
+ *be cleared by the hardware
+ */
+ if (own)
+ return false;
+ else
+ return true;
+}
+
+void rtl8723be_tx_polling(struct ieee80211_hw *hw, u8 hw_queue)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ if (hw_queue == BEACON_QUEUE) {
+ rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG, BIT(4));
+ } else {
+ rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG,
+ BIT(0) << (hw_queue));
+ }
+}
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/trx.h b/drivers/net/wireless/rtlwifi/rtl8723be/trx.h
new file mode 100644
index 000000000000..102f33dcc988
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/trx.h
@@ -0,0 +1,617 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __RTL8723BE_TRX_H__
+#define __RTL8723BE_TRX_H__
+
+#define TX_DESC_SIZE 40
+#define TX_DESC_AGGR_SUBFRAME_SIZE 32
+
+#define RX_DESC_SIZE 32
+#define RX_DRV_INFO_SIZE_UNIT 8
+
+#define TX_DESC_NEXT_DESC_OFFSET 40
+#define USB_HWDESC_HEADER_LEN 40
+#define CRCLENGTH 4
+
+#define SET_TX_DESC_PKT_SIZE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 0, 16, __val)
+#define SET_TX_DESC_OFFSET(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 16, 8, __val)
+#define SET_TX_DESC_BMC(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 24, 1, __val)
+#define SET_TX_DESC_HTC(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 25, 1, __val)
+#define SET_TX_DESC_LAST_SEG(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 26, 1, __val)
+#define SET_TX_DESC_FIRST_SEG(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 27, 1, __val)
+#define SET_TX_DESC_LINIP(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 28, 1, __val)
+#define SET_TX_DESC_NO_ACM(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 29, 1, __val)
+#define SET_TX_DESC_GF(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 30, 1, __val)
+#define SET_TX_DESC_OWN(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 31, 1, __val)
+
+#define GET_TX_DESC_PKT_SIZE(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 0, 16)
+#define GET_TX_DESC_OFFSET(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 16, 8)
+#define GET_TX_DESC_BMC(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 24, 1)
+#define GET_TX_DESC_HTC(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 25, 1)
+#define GET_TX_DESC_LAST_SEG(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 26, 1)
+#define GET_TX_DESC_FIRST_SEG(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 27, 1)
+#define GET_TX_DESC_LINIP(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 28, 1)
+#define GET_TX_DESC_NO_ACM(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 29, 1)
+#define GET_TX_DESC_GF(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 30, 1)
+#define GET_TX_DESC_OWN(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 31, 1)
+
+#define SET_TX_DESC_MACID(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+4, 0, 7, __val)
+#define SET_TX_DESC_QUEUE_SEL(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+4, 8, 5, __val)
+#define SET_TX_DESC_RDG_NAV_EXT(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+4, 13, 1, __val)
+#define SET_TX_DESC_LSIG_TXOP_EN(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+4, 14, 1, __val)
+#define SET_TX_DESC_PIFS(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+4, 15, 1, __val)
+#define SET_TX_DESC_RATE_ID(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+4, 16, 5, __val)
+#define SET_TX_DESC_EN_DESC_ID(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+4, 21, 1, __val)
+#define SET_TX_DESC_SEC_TYPE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+4, 22, 2, __val)
+#define SET_TX_DESC_PKT_OFFSET(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+4, 24, 5, __val)
+
+
+#define SET_TX_DESC_PAID(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 0, 9, __val)
+#define SET_TX_DESC_CCA_RTS(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 10, 2, __val)
+#define SET_TX_DESC_AGG_ENABLE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 12, 1, __val)
+#define SET_TX_DESC_RDG_ENABLE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 13, 1, __val)
+#define SET_TX_DESC_BAR_RTY_TH(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 14, 2, __val)
+#define SET_TX_DESC_AGG_BREAK(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 16, 1, __val)
+#define SET_TX_DESC_MORE_FRAG(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 17, 1, __val)
+#define SET_TX_DESC_RAW(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 18, 1, __val)
+#define SET_TX_DESC_SPE_RPT(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 19, 1, __val)
+#define SET_TX_DESC_AMPDU_DENSITY(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 20, 3, __val)
+#define SET_TX_DESC_BT_INT(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 23, 1, __val)
+#define SET_TX_DESC_GID(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+8, 24, 6, __val)
+
+
+#define SET_TX_DESC_WHEADER_LEN(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 0, 4, __val)
+#define SET_TX_DESC_CHK_EN(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 4, 1, __val)
+#define SET_TX_DESC_EARLY_MODE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 5, 1, __val)
+#define SET_TX_DESC_HWSEQ_SEL(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 6, 2, __val)
+#define SET_TX_DESC_USE_RATE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 8, 1, __val)
+#define SET_TX_DESC_DISABLE_RTS_FB(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 9, 1, __val)
+#define SET_TX_DESC_DISABLE_FB(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 10, 1, __val)
+#define SET_TX_DESC_CTS2SELF(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 11, 1, __val)
+#define SET_TX_DESC_RTS_ENABLE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 12, 1, __val)
+#define SET_TX_DESC_HW_RTS_ENABLE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 13, 1, __val)
+#define SET_TX_DESC_NAV_USE_HDR(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 15, 1, __val)
+#define SET_TX_DESC_USE_MAX_LEN(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 16, 1, __val)
+#define SET_TX_DESC_MAX_AGG_NUM(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 17, 5, __val)
+#define SET_TX_DESC_NDPA(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 22, 2, __val)
+#define SET_TX_DESC_AMPDU_MAX_TIME(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+12, 24, 8, __val)
+
+
+#define SET_TX_DESC_TX_RATE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+16, 0, 7, __val)
+#define SET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+16, 8, 5, __val)
+#define SET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+16, 13, 4, __val)
+#define SET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+16, 17, 1, __val)
+#define SET_TX_DESC_DATA_RETRY_LIMIT(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+16, 18, 6, __val)
+#define SET_TX_DESC_RTS_RATE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+16, 24, 5, __val)
+
+
+#define SET_TX_DESC_TX_SUB_CARRIER(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+20, 0, 4, __val)
+#define SET_TX_DESC_DATA_SHORTGI(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+20, 4, 1, __val)
+#define SET_TX_DESC_DATA_BW(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+20, 5, 2, __val)
+#define SET_TX_DESC_DATA_LDPC(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+20, 7, 1, __val)
+#define SET_TX_DESC_DATA_STBC(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+20, 8, 2, __val)
+#define SET_TX_DESC_CTROL_STBC(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+20, 10, 2, __val)
+#define SET_TX_DESC_RTS_SHORT(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+20, 12, 1, __val)
+#define SET_TX_DESC_RTS_SC(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+20, 13, 4, __val)
+
+
+#define SET_TX_DESC_TX_BUFFER_SIZE(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+28, 0, 16, __val)
+
+#define GET_TX_DESC_TX_BUFFER_SIZE(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+28, 0, 16)
+
+#define SET_TX_DESC_HWSEQ_EN(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+32, 15, 1, __val)
+
+#define SET_TX_DESC_SEQ(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+36, 12, 12, __val)
+
+#define SET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+40, 0, 32, __val)
+
+#define GET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+40, 0, 32)
+
+
+#define SET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+48, 0, 32, __val)
+
+#define GET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+48, 0, 32)
+
+#define GET_RX_DESC_PKT_LEN(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 0, 14)
+#define GET_RX_DESC_CRC32(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 14, 1)
+#define GET_RX_DESC_ICV(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 15, 1)
+#define GET_RX_DESC_DRV_INFO_SIZE(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 16, 4)
+#define GET_RX_DESC_SECURITY(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 20, 3)
+#define GET_RX_DESC_QOS(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 23, 1)
+#define GET_RX_DESC_SHIFT(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 24, 2)
+#define GET_RX_DESC_PHYST(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 26, 1)
+#define GET_RX_DESC_SWDEC(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 27, 1)
+#define GET_RX_DESC_LS(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 28, 1)
+#define GET_RX_DESC_FS(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 29, 1)
+#define GET_RX_DESC_EOR(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 30, 1)
+#define GET_RX_DESC_OWN(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc, 31, 1)
+
+#define SET_RX_DESC_PKT_LEN(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 0, 14, __val)
+#define SET_RX_DESC_EOR(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 30, 1, __val)
+#define SET_RX_DESC_OWN(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc, 31, 1, __val)
+
+#define GET_RX_DESC_MACID(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 0, 7)
+#define GET_RX_DESC_TID(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 8, 4)
+#define GET_RX_DESC_AMSDU(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 13, 1)
+#define GET_RX_STATUS_DESC_RXID_MATCH(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 14, 1)
+#define GET_RX_DESC_PAGGR(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 15, 1)
+#define GET_RX_DESC_A1_FIT(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 16, 4)
+#define GET_RX_DESC_CHKERR(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 20, 1)
+#define GET_RX_DESC_IPVER(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 21, 1)
+#define GET_RX_STATUS_DESC_IS_TCPUDP(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 22, 1)
+#define GET_RX_STATUS_DESC_CHK_VLD(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 23, 1)
+#define GET_RX_DESC_PAM(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 24, 1)
+#define GET_RX_DESC_PWR(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 25, 1)
+#define GET_RX_DESC_MD(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 26, 1)
+#define GET_RX_DESC_MF(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 27, 1)
+#define GET_RX_DESC_TYPE(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 28, 2)
+#define GET_RX_DESC_MC(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 30, 1)
+#define GET_RX_DESC_BC(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+4, 31, 1)
+
+
+#define GET_RX_DESC_SEQ(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+8, 0, 12)
+#define GET_RX_DESC_FRAG(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+8, 12, 4)
+#define GET_RX_STATUS_DESC_RX_IS_QOS(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+8, 16, 1)
+#define GET_RX_STATUS_DESC_WLANHD_IV_LEN(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+8, 18, 6)
+#define GET_RX_STATUS_DESC_RPT_SEL(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+8, 28, 1)
+
+
+#define GET_RX_DESC_RXMCS(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+12, 0, 7)
+#define GET_RX_DESC_RXHT(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+12, 6, 1)
+#define GET_RX_STATUS_DESC_RX_GF(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+12, 7, 1)
+#define GET_RX_DESC_HTC(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+12, 10, 1)
+#define GET_RX_STATUS_DESC_EOSP(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+12, 11, 1)
+#define GET_RX_STATUS_DESC_BSSID_FIT(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+12, 12, 2)
+
+#define GET_RX_STATUS_DESC_PATTERN_MATCH(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+12, 29, 1)
+#define GET_RX_STATUS_DESC_UNICAST_MATCH(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+12, 30, 1)
+#define GET_RX_STATUS_DESC_MAGIC_MATCH(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+12, 31, 1)
+
+#define GET_RX_DESC_SPLCP(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+16, 0, 1)
+#define GET_RX_STATUS_DESC_LDPC(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+16, 1, 1)
+#define GET_RX_STATUS_DESC_STBC(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+16, 2, 1)
+#define GET_RX_DESC_BW(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+16, 4, 2)
+
+#define GET_RX_DESC_TSFL(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+20, 0, 32)
+
+#define GET_RX_DESC_BUFF_ADDR(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+24, 0, 32)
+#define GET_RX_DESC_BUFF_ADDR64(__pdesc) \
+ LE_BITS_TO_4BYTE(__pdesc+28, 0, 32)
+
+#define SET_RX_DESC_BUFF_ADDR(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+24, 0, 32, __val)
+#define SET_RX_DESC_BUFF_ADDR64(__pdesc, __val) \
+ SET_BITS_TO_LE_4BYTE(__pdesc+28, 0, 32, __val)
+
+
+/* TX report 2 format in Rx desc*/
+
+#define GET_RX_RPT2_DESC_PKT_LEN(__rxstatusdesc) \
+ LE_BITS_TO_4BYTE(__rxstatusdesc, 0, 9)
+#define GET_RX_RPT2_DESC_MACID_VALID_1(__rxstatusdesc) \
+ LE_BITS_TO_4BYTE(__rxstatusdesc+16, 0, 32)
+#define GET_RX_RPT2_DESC_MACID_VALID_2(__rxstatusdesc) \
+ LE_BITS_TO_4BYTE(__rxstatusdesc+20, 0, 32)
+
+#define SET_EARLYMODE_PKTNUM(__paddr, __value) \
+ SET_BITS_TO_LE_4BYTE(__paddr, 0, 4, __value)
+#define SET_EARLYMODE_LEN0(__paddr, __value) \
+ SET_BITS_TO_LE_4BYTE(__paddr, 4, 12, __value)
+#define SET_EARLYMODE_LEN1(__paddr, __value) \
+ SET_BITS_TO_LE_4BYTE(__paddr, 16, 12, __value)
+#define SET_EARLYMODE_LEN2_1(__paddr, __value) \
+ SET_BITS_TO_LE_4BYTE(__paddr, 28, 4, __value)
+#define SET_EARLYMODE_LEN2_2(__paddr, __value) \
+ SET_BITS_TO_LE_4BYTE(__paddr+4, 0, 8, __value)
+#define SET_EARLYMODE_LEN3(__paddr, __value) \
+ SET_BITS_TO_LE_4BYTE(__paddr+4, 8, 12, __value)
+#define SET_EARLYMODE_LEN4(__paddr, __value) \
+ SET_BITS_TO_LE_4BYTE(__paddr+4, 20, 12, __value)
+
+#define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \
+do { \
+ if (_size > TX_DESC_NEXT_DESC_OFFSET) \
+ memset(__pdesc, 0, TX_DESC_NEXT_DESC_OFFSET); \
+ else \
+ memset(__pdesc, 0, _size); \
+} while (0)
+
+struct phy_rx_agc_info_t {
+ #ifdef __LITTLE_ENDIAN
+ u8 gain:7, trsw:1;
+ #else
+ u8 trsw:1, gain:7;
+ #endif
+};
+struct phy_status_rpt {
+ struct phy_rx_agc_info_t path_agc[2];
+ u8 ch_corr[2];
+ u8 cck_sig_qual_ofdm_pwdb_all;
+ u8 cck_agc_rpt_ofdm_cfosho_a;
+ u8 cck_rpt_b_ofdm_cfosho_b;
+ u8 rsvd_1;/* ch_corr_msb; */
+ u8 noise_power_db_msb;
+ char path_cfotail[2];
+ u8 pcts_mask[2];
+ char stream_rxevm[2];
+ u8 path_rxsnr[2];
+ u8 noise_power_db_lsb;
+ u8 rsvd_2[3];
+ u8 stream_csi[2];
+ u8 stream_target_csi[2];
+ u8 sig_evm;
+ u8 rsvd_3;
+#ifdef __LITTLE_ENDIAN
+ u8 antsel_rx_keep_2:1; /*ex_intf_flg:1;*/
+ u8 sgi_en:1;
+ u8 rxsc:2;
+ u8 idle_long:1;
+ u8 r_ant_train_en:1;
+ u8 ant_sel_b:1;
+ u8 ant_sel:1;
+#else /* _BIG_ENDIAN_ */
+ u8 ant_sel:1;
+ u8 ant_sel_b:1;
+ u8 r_ant_train_en:1;
+ u8 idle_long:1;
+ u8 rxsc:2;
+ u8 sgi_en:1;
+ u8 antsel_rx_keep_2:1; /*ex_intf_flg:1;*/
+#endif
+} __packed;
+
+struct rx_fwinfo_8723be {
+ u8 gain_trsw[4];
+ u8 pwdb_all;
+ u8 cfosho[4];
+ u8 cfotail[4];
+ char rxevm[2];
+ char rxsnr[4];
+ u8 pdsnr[2];
+ u8 csi_current[2];
+ u8 csi_target[2];
+ u8 sigevm;
+ u8 max_ex_pwr;
+ u8 ex_intf_flag:1;
+ u8 sgi_en:1;
+ u8 rxsc:2;
+ u8 reserve:4;
+} __packed;
+
+struct tx_desc_8723be {
+ u32 pktsize:16;
+ u32 offset:8;
+ u32 bmc:1;
+ u32 htc:1;
+ u32 lastseg:1;
+ u32 firstseg:1;
+ u32 linip:1;
+ u32 noacm:1;
+ u32 gf:1;
+ u32 own:1;
+
+ u32 macid:6;
+ u32 rsvd0:2;
+ u32 queuesel:5;
+ u32 rd_nav_ext:1;
+ u32 lsig_txop_en:1;
+ u32 pifs:1;
+ u32 rateid:4;
+ u32 nav_usehdr:1;
+ u32 en_descid:1;
+ u32 sectype:2;
+ u32 pktoffset:8;
+
+ u32 rts_rc:6;
+ u32 data_rc:6;
+ u32 agg_en:1;
+ u32 rdg_en:1;
+ u32 bar_retryht:2;
+ u32 agg_break:1;
+ u32 morefrag:1;
+ u32 raw:1;
+ u32 ccx:1;
+ u32 ampdudensity:3;
+ u32 bt_int:1;
+ u32 ant_sela:1;
+ u32 ant_selb:1;
+ u32 txant_cck:2;
+ u32 txant_l:2;
+ u32 txant_ht:2;
+
+ u32 nextheadpage:8;
+ u32 tailpage:8;
+ u32 seq:12;
+ u32 cpu_handle:1;
+ u32 tag1:1;
+ u32 trigger_int:1;
+ u32 hwseq_en:1;
+
+ u32 rtsrate:5;
+ u32 apdcfe:1;
+ u32 qos:1;
+ u32 hwseq_ssn:1;
+ u32 userrate:1;
+ u32 dis_rtsfb:1;
+ u32 dis_datafb:1;
+ u32 cts2self:1;
+ u32 rts_en:1;
+ u32 hwrts_en:1;
+ u32 portid:1;
+ u32 pwr_status:3;
+ u32 waitdcts:1;
+ u32 cts2ap_en:1;
+ u32 txsc:2;
+ u32 stbc:2;
+ u32 txshort:1;
+ u32 txbw:1;
+ u32 rtsshort:1;
+ u32 rtsbw:1;
+ u32 rtssc:2;
+ u32 rtsstbc:2;
+
+ u32 txrate:6;
+ u32 shortgi:1;
+ u32 ccxt:1;
+ u32 txrate_fb_lmt:5;
+ u32 rtsrate_fb_lmt:4;
+ u32 retrylmt_en:1;
+ u32 txretrylmt:6;
+ u32 usb_txaggnum:8;
+
+ u32 txagca:5;
+ u32 txagcb:5;
+ u32 usemaxlen:1;
+ u32 maxaggnum:5;
+ u32 mcsg1maxlen:4;
+ u32 mcsg2maxlen:4;
+ u32 mcsg3maxlen:4;
+ u32 mcs7sgimaxlen:4;
+
+ u32 txbuffersize:16;
+ u32 sw_offset30:8;
+ u32 sw_offset31:4;
+ u32 rsvd1:1;
+ u32 antsel_c:1;
+ u32 null_0:1;
+ u32 null_1:1;
+
+ u32 txbuffaddr;
+ u32 txbufferaddr64;
+ u32 nextdescaddress;
+ u32 nextdescaddress64;
+
+ u32 reserve_pass_pcie_mm_limit[4];
+} __packed;
+
+struct rx_desc_8723be {
+ u32 length:14;
+ u32 crc32:1;
+ u32 icverror:1;
+ u32 drv_infosize:4;
+ u32 security:3;
+ u32 qos:1;
+ u32 shift:2;
+ u32 phystatus:1;
+ u32 swdec:1;
+ u32 lastseg:1;
+ u32 firstseg:1;
+ u32 eor:1;
+ u32 own:1;
+
+ u32 macid:6;
+ u32 tid:4;
+ u32 hwrsvd:5;
+ u32 paggr:1;
+ u32 faggr:1;
+ u32 a1_fit:4;
+ u32 a2_fit:4;
+ u32 pam:1;
+ u32 pwr:1;
+ u32 moredata:1;
+ u32 morefrag:1;
+ u32 type:2;
+ u32 mc:1;
+ u32 bc:1;
+
+ u32 seq:12;
+ u32 frag:4;
+ u32 nextpktlen:14;
+ u32 nextind:1;
+ u32 rsvd:1;
+
+ u32 rxmcs:6;
+ u32 rxht:1;
+ u32 amsdu:1;
+ u32 splcp:1;
+ u32 bandwidth:1;
+ u32 htc:1;
+ u32 tcpchk_rpt:1;
+ u32 ipcchk_rpt:1;
+ u32 tcpchk_valid:1;
+ u32 hwpcerr:1;
+ u32 hwpcind:1;
+ u32 iv0:16;
+
+ u32 iv1;
+
+ u32 tsfl;
+
+ u32 bufferaddress;
+ u32 bufferaddress64;
+
+} __packed;
+
+void rtl8723be_tx_fill_desc(struct ieee80211_hw *hw,
+ struct ieee80211_hdr *hdr, u8 *pdesc,
+ u8 *pbd_desc_tx, struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta, struct sk_buff *skb,
+ u8 hw_queue, struct rtl_tcb_desc *ptcb_desc);
+bool rtl8723be_rx_query_desc(struct ieee80211_hw *hw,
+ struct rtl_stats *status,
+ struct ieee80211_rx_status *rx_status,
+ u8 *pdesc, struct sk_buff *skb);
+void rtl8723be_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val);
+u32 rtl8723be_get_desc(u8 *pdesc, bool istx, u8 desc_name);
+bool rtl8723be_is_tx_desc_closed(struct ieee80211_hw *hw,
+ u8 hw_queue, u16 index);
+void rtl8723be_tx_polling(struct ieee80211_hw *hw, u8 hw_queue);
+void rtl8723be_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
+ bool b_firstseg, bool b_lastseg,
+ struct sk_buff *skb);
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/Makefile b/drivers/net/wireless/rtlwifi/rtl8723com/Makefile
new file mode 100644
index 000000000000..345a68adcf38
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723com/Makefile
@@ -0,0 +1,9 @@
+rtl8723-common-objs := \
+ main.o \
+ dm_common.o \
+ fw_common.o \
+ phy_common.o
+
+obj-$(CONFIG_RTL8723_COMMON) += rtl8723-common.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.c
new file mode 100644
index 000000000000..4e254b72bf45
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.c
@@ -0,0 +1,65 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "dm_common.h"
+#include "../rtl8723ae/dm.h"
+#include <linux/module.h>
+
+/* These routines are common to RTL8723AE and RTL8723bE */
+
+void rtl8723_dm_init_dynamic_txpower(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->dm.dynamic_txpower_enable = false;
+
+ rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL;
+ rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL;
+}
+EXPORT_SYMBOL_GPL(rtl8723_dm_init_dynamic_txpower);
+
+void rtl8723_dm_init_edca_turbo(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->dm.current_turbo_edca = false;
+ rtlpriv->dm.is_any_nonbepkts = false;
+ rtlpriv->dm.is_cur_rdlstate = false;
+}
+EXPORT_SYMBOL_GPL(rtl8723_dm_init_edca_turbo);
+
+void rtl8723_dm_init_dynamic_bb_powersaving(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->dm_pstable.pre_ccastate = CCA_MAX;
+ rtlpriv->dm_pstable.cur_ccasate = CCA_MAX;
+ rtlpriv->dm_pstable.pre_rfstate = RF_MAX;
+ rtlpriv->dm_pstable.cur_rfstate = RF_MAX;
+ rtlpriv->dm_pstable.rssi_val_min = 0;
+ rtlpriv->dm_pstable.initialize = 0;
+}
+EXPORT_SYMBOL_GPL(rtl8723_dm_init_dynamic_bb_powersaving);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.h b/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.h
new file mode 100644
index 000000000000..5c1b94ce2f86
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.h
@@ -0,0 +1,33 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __DM_COMMON_H__
+#define __DM_COMMON_H__
+
+void rtl8723_dm_init_dynamic_txpower(struct ieee80211_hw *hw);
+void rtl8723_dm_init_edca_turbo(struct ieee80211_hw *hw);
+void rtl8723_dm_init_dynamic_bb_powersaving(struct ieee80211_hw *hw);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.c b/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.c
new file mode 100644
index 000000000000..540278ff462b
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.c
@@ -0,0 +1,329 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "../pci.h"
+#include "../base.h"
+#include "fw_common.h"
+#include <linux/module.h>
+
+void rtl8723_enable_fw_download(struct ieee80211_hw *hw, bool enable)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 tmp;
+
+ if (enable) {
+ tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp | 0x04);
+
+ tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
+ rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01);
+
+ tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2);
+ rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7);
+ } else {
+ tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL);
+ rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe);
+
+ rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00);
+ }
+}
+EXPORT_SYMBOL_GPL(rtl8723_enable_fw_download);
+
+void rtl8723_fw_block_write(struct ieee80211_hw *hw,
+ const u8 *buffer, u32 size)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 blocksize = sizeof(u32);
+ u8 *bufferptr = (u8 *)buffer;
+ u32 *pu4byteptr = (u32 *)buffer;
+ u32 i, offset, blockcount, remainsize;
+
+ blockcount = size / blocksize;
+ remainsize = size % blocksize;
+
+ for (i = 0; i < blockcount; i++) {
+ offset = i * blocksize;
+ rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset),
+ *(pu4byteptr + i));
+ }
+ if (remainsize) {
+ offset = blockcount * blocksize;
+ bufferptr += offset;
+ for (i = 0; i < remainsize; i++) {
+ rtl_write_byte(rtlpriv,
+ (FW_8192C_START_ADDRESS + offset + i),
+ *(bufferptr + i));
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(rtl8723_fw_block_write);
+
+void rtl8723_fw_page_write(struct ieee80211_hw *hw,
+ u32 page, const u8 *buffer, u32 size)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 value8;
+ u8 u8page = (u8) (page & 0x07);
+
+ value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page;
+
+ rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8);
+ rtl8723_fw_block_write(hw, buffer, size);
+}
+EXPORT_SYMBOL_GPL(rtl8723_fw_page_write);
+
+static void rtl8723_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
+{
+ u32 fwlen = *pfwlen;
+ u8 remain = (u8) (fwlen % 4);
+
+ remain = (remain == 0) ? 0 : (4 - remain);
+
+ while (remain > 0) {
+ pfwbuf[fwlen] = 0;
+ fwlen++;
+ remain--;
+ }
+ *pfwlen = fwlen;
+}
+
+void rtl8723_write_fw(struct ieee80211_hw *hw,
+ enum version_8723e version,
+ u8 *buffer, u32 size)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u8 *bufferptr = buffer;
+ u32 pagenums, remainsize;
+ u32 page, offset;
+
+ RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "FW size is %d bytes,\n", size);
+
+ rtl8723_fill_dummy(bufferptr, &size);
+
+ pagenums = size / FW_8192C_PAGE_SIZE;
+ remainsize = size % FW_8192C_PAGE_SIZE;
+
+ if (pagenums > 8) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "Page numbers should not greater then 8\n");
+ }
+ for (page = 0; page < pagenums; page++) {
+ offset = page * FW_8192C_PAGE_SIZE;
+ rtl8723_fw_page_write(hw, page, (bufferptr + offset),
+ FW_8192C_PAGE_SIZE);
+ }
+ if (remainsize) {
+ offset = pagenums * FW_8192C_PAGE_SIZE;
+ page = pagenums;
+ rtl8723_fw_page_write(hw, page, (bufferptr + offset),
+ remainsize);
+ }
+}
+EXPORT_SYMBOL_GPL(rtl8723_write_fw);
+
+void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw)
+{
+ u8 u1tmp;
+ u8 delay = 100;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20);
+ u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+
+ while (u1tmp & BIT(2)) {
+ delay--;
+ if (delay == 0)
+ break;
+ udelay(50);
+ u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+ }
+ if (delay == 0) {
+ u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, u1tmp&(~BIT(2)));
+ }
+}
+EXPORT_SYMBOL_GPL(rtl8723ae_firmware_selfreset);
+
+void rtl8723be_firmware_selfreset(struct ieee80211_hw *hw)
+{
+ u8 u1b_tmp;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1);
+ rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, (u1b_tmp & (~BIT(0))));
+
+ u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, (u1b_tmp & (~BIT(2))));
+ udelay(50);
+
+ u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1);
+ rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, (u1b_tmp | BIT(0)));
+
+ u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, (u1b_tmp | BIT(2)));
+
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
+ " _8051Reset8723be(): 8051 reset success .\n");
+}
+EXPORT_SYMBOL_GPL(rtl8723be_firmware_selfreset);
+
+int rtl8723_fw_free_to_go(struct ieee80211_hw *hw, bool is_8723be)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ int err = -EIO;
+ u32 counter = 0;
+ u32 value32;
+
+ do {
+ value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
+ } while ((counter++ < FW_8192C_POLLING_TIMEOUT_COUNT) &&
+ (!(value32 & FWDL_CHKSUM_RPT)));
+
+ if (counter >= FW_8192C_POLLING_TIMEOUT_COUNT) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "chksum report fail ! REG_MCUFWDL:0x%08x .\n",
+ value32);
+ goto exit;
+ }
+ RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
+ "Checksum report OK ! REG_MCUFWDL:0x%08x .\n", value32);
+
+ value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL) | MCUFWDL_RDY;
+ value32 &= ~WINTINI_RDY;
+ rtl_write_dword(rtlpriv, REG_MCUFWDL, value32);
+
+ if (is_8723be)
+ rtl8723be_firmware_selfreset(hw);
+ counter = 0;
+
+ do {
+ value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL);
+ if (value32 & WINTINI_RDY) {
+ RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE,
+ "Polling FW ready success!! "
+ "REG_MCUFWDL:0x%08x .\n",
+ value32);
+ err = 0;
+ goto exit;
+ }
+ udelay(FW_8192C_POLLING_DELAY);
+
+ } while (counter++ < FW_8192C_POLLING_TIMEOUT_COUNT);
+
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "Polling FW ready fail!! REG_MCUFWDL:0x%08x .\n",
+ value32);
+
+exit:
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtl8723_fw_free_to_go);
+
+int rtl8723_download_fw(struct ieee80211_hw *hw,
+ bool is_8723be)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ struct rtl92c_firmware_header *pfwheader;
+ u8 *pfwdata;
+ u32 fwsize;
+ int err;
+ enum version_8723e version = rtlhal->version;
+
+ if (!rtlhal->pfirmware)
+ return 1;
+
+ pfwheader = (struct rtl92c_firmware_header *)rtlhal->pfirmware;
+ pfwdata = rtlhal->pfirmware;
+ fwsize = rtlhal->fwsize;
+ RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
+ "normal Firmware SIZE %d\n", fwsize);
+
+ if (rtlpriv->cfg->ops->is_fw_header(pfwheader)) {
+ RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
+ "Firmware Version(%d), Signature(%#x), Size(%d)\n",
+ pfwheader->version, pfwheader->signature,
+ (int)sizeof(struct rtl92c_firmware_header));
+
+ pfwdata = pfwdata + sizeof(struct rtl92c_firmware_header);
+ fwsize = fwsize - sizeof(struct rtl92c_firmware_header);
+ }
+ if (rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7)) {
+ rtl_write_byte(rtlpriv, REG_MCUFWDL, 0);
+ if (is_8723be)
+ rtl8723be_firmware_selfreset(hw);
+ else
+ rtl8723ae_firmware_selfreset(hw);
+ }
+ rtl8723_enable_fw_download(hw, true);
+ rtl8723_write_fw(hw, version, pfwdata, fwsize);
+ rtl8723_enable_fw_download(hw, false);
+
+ err = rtl8723_fw_free_to_go(hw, is_8723be);
+ if (err) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "Firmware is not ready to run!\n");
+ } else {
+ RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
+ "Firmware is ready to run!\n");
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8723_download_fw);
+
+bool rtl8723_cmd_send_packet(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl8192_tx_ring *ring;
+ struct rtl_tx_desc *pdesc;
+ struct sk_buff *pskb = NULL;
+ u8 own;
+ unsigned long flags;
+
+ ring = &rtlpci->tx_ring[BEACON_QUEUE];
+
+ pskb = __skb_dequeue(&ring->queue);
+ if (pskb)
+ kfree_skb(pskb);
+
+ spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
+
+ pdesc = &ring->desc[0];
+ own = (u8) rtlpriv->cfg->ops->get_desc((u8 *)pdesc, true, HW_DESC_OWN);
+
+ rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *)pdesc, 1, 1, skb);
+
+ __skb_queue_tail(&ring->queue, skb);
+
+ spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags);
+
+ rtlpriv->cfg->ops->tx_polling(hw, BEACON_QUEUE);
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(rtl8723_cmd_send_packet);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.h b/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.h
new file mode 100644
index 000000000000..cf1cc5804d06
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.h
@@ -0,0 +1,126 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __FW_COMMON_H__
+#define __FW_COMMON_H__
+
+#define REG_SYS_FUNC_EN 0x0002
+#define REG_MCUFWDL 0x0080
+#define FW_8192C_START_ADDRESS 0x1000
+#define FW_8192C_PAGE_SIZE 4096
+#define FW_8192C_POLLING_TIMEOUT_COUNT 6000
+#define FW_8192C_POLLING_DELAY 5
+
+#define MCUFWDL_RDY BIT(1)
+#define FWDL_CHKSUM_RPT BIT(2)
+#define WINTINI_RDY BIT(6)
+
+#define REG_RSV_CTRL 0x001C
+#define REG_HMETFR 0x01CC
+
+enum version_8723e {
+ VERSION_TEST_UMC_CHIP_8723 = 0x0081,
+ VERSION_NORMAL_UMC_CHIP_8723_1T1R_A_CUT = 0x0089,
+ VERSION_NORMAL_UMC_CHIP_8723_1T1R_B_CUT = 0x1089,
+ VERSION_TEST_CHIP_1T1R_8723B = 0x0106,
+ VERSION_NORMAL_SMIC_CHIP_1T1R_8723B = 0x010E,
+ VERSION_UNKNOWN = 0xFF,
+};
+
+enum rtl8723ae_h2c_cmd {
+ H2C_AP_OFFLOAD = 0,
+ H2C_SETPWRMODE = 1,
+ H2C_JOINBSSRPT = 2,
+ H2C_RSVDPAGE = 3,
+ H2C_RSSI_REPORT = 4,
+ H2C_P2P_PS_CTW_CMD = 5,
+ H2C_P2P_PS_OFFLOAD = 6,
+ H2C_RA_MASK = 7,
+ MAX_H2CCMD
+};
+
+enum rtl8723be_cmd {
+ H2C_8723BE_RSVDPAGE = 0,
+ H2C_8723BE_JOINBSSRPT = 1,
+ H2C_8723BE_SCAN = 2,
+ H2C_8723BE_KEEP_ALIVE_CTRL = 3,
+ H2C_8723BE_DISCONNECT_DECISION = 4,
+ H2C_8723BE_INIT_OFFLOAD = 6,
+ H2C_8723BE_AP_OFFLOAD = 8,
+ H2C_8723BE_BCN_RSVDPAGE = 9,
+ H2C_8723BE_PROBERSP_RSVDPAGE = 10,
+
+ H2C_8723BE_SETPWRMODE = 0x20,
+ H2C_8723BE_PS_TUNING_PARA = 0x21,
+ H2C_8723BE_PS_TUNING_PARA2 = 0x22,
+ H2C_8723BE_PS_LPS_PARA = 0x23,
+ H2C_8723BE_P2P_PS_OFFLOAD = 0x24,
+
+ H2C_8723BE_WO_WLAN = 0x80,
+ H2C_8723BE_REMOTE_WAKE_CTRL = 0x81,
+ H2C_8723BE_AOAC_GLOBAL_INFO = 0x82,
+ H2C_8723BE_AOAC_RSVDPAGE = 0x83,
+ H2C_8723BE_RSSI_REPORT = 0x42,
+ H2C_8723BE_RA_MASK = 0x40,
+ H2C_8723BE_SELECTIVE_SUSPEND_ROF_CMD,
+ H2C_8723BE_P2P_PS_MODE,
+ H2C_8723BE_PSD_RESULT,
+ /*Not defined CTW CMD for P2P yet*/
+ H2C_8723BE_P2P_PS_CTW_CMD,
+ MAX_8723BE_H2CCMD
+};
+
+struct rtl92c_firmware_header {
+ u16 signature;
+ u8 category;
+ u8 function;
+ u16 version;
+ u8 subversion;
+ u8 rsvd1;
+ u8 month;
+ u8 date;
+ u8 hour;
+ u8 minute;
+ u16 ramcodesize;
+ u16 rsvd2;
+ u32 svnindex;
+ u32 rsvd3;
+ u32 rsvd4;
+ u32 rsvd5;
+};
+
+void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw);
+void rtl8723be_firmware_selfreset(struct ieee80211_hw *hw);
+void rtl8723_enable_fw_download(struct ieee80211_hw *hw, bool enable);
+void rtl8723_fw_block_write(struct ieee80211_hw *hw,
+ const u8 *buffer, u32 size);
+void rtl8723_fw_page_write(struct ieee80211_hw *hw,
+ u32 page, const u8 *buffer, u32 size);
+void rtl8723_write_fw(struct ieee80211_hw *hw,
+ enum version_8723e version,
+ u8 *buffer, u32 size);
+int rtl8723_fw_free_to_go(struct ieee80211_hw *hw, bool is_8723be);
+int rtl8723_download_fw(struct ieee80211_hw *hw, bool is_8723be);
+#endif
diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/main.c b/drivers/net/wireless/rtlwifi/rtl8723com/main.c
new file mode 100644
index 000000000000..9014a94fac6a
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723com/main.c
@@ -0,0 +1,33 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include <linux/module.h>
+
+
+MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>");
+MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek RTL8723AE/RTL8723BE 802.11n PCI wireless common routines");
diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.c b/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.c
new file mode 100644
index 000000000000..d73b659bd2b5
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.c
@@ -0,0 +1,434 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "../wifi.h"
+#include "phy_common.h"
+#include "../rtl8723ae/reg.h"
+#include <linux/module.h>
+
+/* These routines are common to RTL8723AE and RTL8723bE */
+
+u32 rtl8723_phy_query_bb_reg(struct ieee80211_hw *hw,
+ u32 regaddr, u32 bitmask)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 returnvalue, originalvalue, bitshift;
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ "regaddr(%#x), bitmask(%#x)\n", regaddr, bitmask);
+ originalvalue = rtl_read_dword(rtlpriv, regaddr);
+ bitshift = rtl8723_phy_calculate_bit_shift(bitmask);
+ returnvalue = (originalvalue & bitmask) >> bitshift;
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ "BBR MASK = 0x%x Addr[0x%x]= 0x%x\n",
+ bitmask, regaddr, originalvalue);
+
+ return returnvalue;
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_query_bb_reg);
+
+void rtl8723_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr,
+ u32 bitmask, u32 data)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 originalvalue, bitshift;
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ "regaddr(%#x), bitmask(%#x), data(%#x)\n",
+ regaddr, bitmask, data);
+
+ if (bitmask != MASKDWORD) {
+ originalvalue = rtl_read_dword(rtlpriv, regaddr);
+ bitshift = rtl8723_phy_calculate_bit_shift(bitmask);
+ data = ((originalvalue & (~bitmask)) | (data << bitshift));
+ }
+
+ rtl_write_dword(rtlpriv, regaddr, data);
+
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ "regaddr(%#x), bitmask(%#x), data(%#x)\n",
+ regaddr, bitmask, data);
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_set_bb_reg);
+
+u32 rtl8723_phy_calculate_bit_shift(u32 bitmask)
+{
+ u32 i;
+
+ for (i = 0; i <= 31; i++) {
+ if (((bitmask >> i) & 0x1) == 1)
+ break;
+ }
+ return i;
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_calculate_bit_shift);
+
+u32 rtl8723_phy_rf_serial_read(struct ieee80211_hw *hw,
+ enum radio_path rfpath, u32 offset)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath];
+ u32 newoffset;
+ u32 tmplong, tmplong2;
+ u8 rfpi_enable = 0;
+ u32 retvalue;
+
+ offset &= 0xff;
+ newoffset = offset;
+ if (RT_CANNOT_IO(hw)) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "return all one\n");
+ return 0xFFFFFFFF;
+ }
+ tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD);
+ if (rfpath == RF90_PATH_A)
+ tmplong2 = tmplong;
+ else
+ tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD);
+ tmplong2 = (tmplong2 & (~BLSSIREADADDRESS)) |
+ (newoffset << 23) | BLSSIREADEDGE;
+ rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD,
+ tmplong & (~BLSSIREADEDGE));
+ mdelay(1);
+ rtl_set_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD, tmplong2);
+ mdelay(2);
+ if (rfpath == RF90_PATH_A)
+ rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1,
+ BIT(8));
+ else if (rfpath == RF90_PATH_B)
+ rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XB_HSSIPARAMETER1,
+ BIT(8));
+ if (rfpi_enable)
+ retvalue = rtl_get_bbreg(hw, pphyreg->rf_rbpi,
+ BLSSIREADBACKDATA);
+ else
+ retvalue = rtl_get_bbreg(hw, pphyreg->rf_rb,
+ BLSSIREADBACKDATA);
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ "RFR-%d Addr[0x%x]= 0x%x\n",
+ rfpath, pphyreg->rf_rb, retvalue);
+ return retvalue;
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_rf_serial_read);
+
+void rtl8723_phy_rf_serial_write(struct ieee80211_hw *hw,
+ enum radio_path rfpath,
+ u32 offset, u32 data)
+{
+ u32 data_and_addr;
+ u32 newoffset;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+ struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath];
+
+ if (RT_CANNOT_IO(hw)) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "stop\n");
+ return;
+ }
+ offset &= 0xff;
+ newoffset = offset;
+ data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff;
+ rtl_set_bbreg(hw, pphyreg->rf3wire_offset, MASKDWORD, data_and_addr);
+ RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
+ "RFW-%d Addr[0x%x]= 0x%x\n", rfpath,
+ pphyreg->rf3wire_offset, data_and_addr);
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_rf_serial_write);
+
+long rtl8723_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw,
+ enum wireless_mode wirelessmode,
+ u8 txpwridx)
+{
+ long offset;
+ long pwrout_dbm;
+
+ switch (wirelessmode) {
+ case WIRELESS_MODE_B:
+ offset = -7;
+ break;
+ case WIRELESS_MODE_G:
+ case WIRELESS_MODE_N_24G:
+ default:
+ offset = -8;
+ break;
+ }
+ pwrout_dbm = txpwridx / 2 + offset;
+ return pwrout_dbm;
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_txpwr_idx_to_dbm);
+
+void rtl8723_phy_init_bb_rf_reg_def(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_phy *rtlphy = &(rtlpriv->phy);
+
+ rtlphy->phyreg_def[RF90_PATH_A].rfintfs = RFPGA0_XAB_RFINTERFACESW;
+ rtlphy->phyreg_def[RF90_PATH_B].rfintfs = RFPGA0_XAB_RFINTERFACESW;
+ rtlphy->phyreg_def[RF90_PATH_C].rfintfs = RFPGA0_XCD_RFINTERFACESW;
+ rtlphy->phyreg_def[RF90_PATH_D].rfintfs = RFPGA0_XCD_RFINTERFACESW;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rfintfi = RFPGA0_XAB_RFINTERFACERB;
+ rtlphy->phyreg_def[RF90_PATH_B].rfintfi = RFPGA0_XAB_RFINTERFACERB;
+ rtlphy->phyreg_def[RF90_PATH_C].rfintfi = RFPGA0_XCD_RFINTERFACERB;
+ rtlphy->phyreg_def[RF90_PATH_D].rfintfi = RFPGA0_XCD_RFINTERFACERB;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rfintfo = RFPGA0_XA_RFINTERFACEOE;
+ rtlphy->phyreg_def[RF90_PATH_B].rfintfo = RFPGA0_XB_RFINTERFACEOE;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rfintfe = RFPGA0_XA_RFINTERFACEOE;
+ rtlphy->phyreg_def[RF90_PATH_B].rfintfe = RFPGA0_XB_RFINTERFACEOE;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rf3wire_offset =
+ RFPGA0_XA_LSSIPARAMETER;
+ rtlphy->phyreg_def[RF90_PATH_B].rf3wire_offset =
+ RFPGA0_XB_LSSIPARAMETER;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rflssi_select = rFPGA0_XAB_RFPARAMETER;
+ rtlphy->phyreg_def[RF90_PATH_B].rflssi_select = rFPGA0_XAB_RFPARAMETER;
+ rtlphy->phyreg_def[RF90_PATH_C].rflssi_select = rFPGA0_XCD_RFPARAMETER;
+ rtlphy->phyreg_def[RF90_PATH_D].rflssi_select = rFPGA0_XCD_RFPARAMETER;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+ rtlphy->phyreg_def[RF90_PATH_B].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+ rtlphy->phyreg_def[RF90_PATH_C].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+ rtlphy->phyreg_def[RF90_PATH_D].rftxgain_stage = RFPGA0_TXGAINSTAGE;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para1 = RFPGA0_XA_HSSIPARAMETER1;
+ rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para1 = RFPGA0_XB_HSSIPARAMETER1;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para2 = RFPGA0_XA_HSSIPARAMETER2;
+ rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para2 = RFPGA0_XB_HSSIPARAMETER2;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL;
+ rtlphy->phyreg_def[RF90_PATH_B].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL;
+ rtlphy->phyreg_def[RF90_PATH_C].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL;
+ rtlphy->phyreg_def[RF90_PATH_D].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rfagc_control1 = ROFDM0_XAAGCCORE1;
+ rtlphy->phyreg_def[RF90_PATH_B].rfagc_control1 = ROFDM0_XBAGCCORE1;
+ rtlphy->phyreg_def[RF90_PATH_C].rfagc_control1 = ROFDM0_XCAGCCORE1;
+ rtlphy->phyreg_def[RF90_PATH_D].rfagc_control1 = ROFDM0_XDAGCCORE1;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rfagc_control2 = ROFDM0_XAAGCCORE2;
+ rtlphy->phyreg_def[RF90_PATH_B].rfagc_control2 = ROFDM0_XBAGCCORE2;
+ rtlphy->phyreg_def[RF90_PATH_C].rfagc_control2 = ROFDM0_XCAGCCORE2;
+ rtlphy->phyreg_def[RF90_PATH_D].rfagc_control2 = ROFDM0_XDAGCCORE2;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rfrxiq_imbal = ROFDM0_XARXIQIMBALANCE;
+ rtlphy->phyreg_def[RF90_PATH_B].rfrxiq_imbal = ROFDM0_XBRXIQIMBALANCE;
+ rtlphy->phyreg_def[RF90_PATH_C].rfrxiq_imbal = ROFDM0_XCRXIQIMBANLANCE;
+ rtlphy->phyreg_def[RF90_PATH_D].rfrxiq_imbal = ROFDM0_XDRXIQIMBALANCE;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rfrx_afe = ROFDM0_XARXAFE;
+ rtlphy->phyreg_def[RF90_PATH_B].rfrx_afe = ROFDM0_XBRXAFE;
+ rtlphy->phyreg_def[RF90_PATH_C].rfrx_afe = ROFDM0_XCRXAFE;
+ rtlphy->phyreg_def[RF90_PATH_D].rfrx_afe = ROFDM0_XDRXAFE;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rftxiq_imbal = ROFDM0_XATXIQIMBALANCE;
+ rtlphy->phyreg_def[RF90_PATH_B].rftxiq_imbal = ROFDM0_XBTXIQIMBALANCE;
+ rtlphy->phyreg_def[RF90_PATH_C].rftxiq_imbal = ROFDM0_XCTXIQIMBALANCE;
+ rtlphy->phyreg_def[RF90_PATH_D].rftxiq_imbal = ROFDM0_XDTXIQIMBALANCE;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rftx_afe = ROFDM0_XATXAFE;
+ rtlphy->phyreg_def[RF90_PATH_B].rftx_afe = ROFDM0_XBTXAFE;
+ rtlphy->phyreg_def[RF90_PATH_C].rftx_afe = ROFDM0_XCTXAFE;
+ rtlphy->phyreg_def[RF90_PATH_D].rftx_afe = ROFDM0_XDTXAFE;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rf_rb = RFPGA0_XA_LSSIREADBACK;
+ rtlphy->phyreg_def[RF90_PATH_B].rf_rb = RFPGA0_XB_LSSIREADBACK;
+ rtlphy->phyreg_def[RF90_PATH_C].rf_rb = RFPGA0_XC_LSSIREADBACK;
+ rtlphy->phyreg_def[RF90_PATH_D].rf_rb = RFPGA0_XD_LSSIREADBACK;
+
+ rtlphy->phyreg_def[RF90_PATH_A].rf_rbpi = TRANSCEIVEA_HSPI_READBACK;
+ rtlphy->phyreg_def[RF90_PATH_B].rf_rbpi = TRANSCEIVEB_HSPI_READBACK;
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_init_bb_rf_reg_def);
+
+bool rtl8723_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable,
+ u32 cmdtableidx,
+ u32 cmdtablesz,
+ enum swchnlcmd_id cmdid,
+ u32 para1, u32 para2,
+ u32 msdelay)
+{
+ struct swchnlcmd *pcmd;
+
+ if (cmdtable == NULL) {
+ RT_ASSERT(false, "cmdtable cannot be NULL.\n");
+ return false;
+ }
+
+ if (cmdtableidx >= cmdtablesz)
+ return false;
+
+ pcmd = cmdtable + cmdtableidx;
+ pcmd->cmdid = cmdid;
+ pcmd->para1 = para1;
+ pcmd->para2 = para2;
+ pcmd->msdelay = msdelay;
+ return true;
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_set_sw_chnl_cmdarray);
+
+void rtl8723_phy_path_a_fill_iqk_matrix(struct ieee80211_hw *hw,
+ bool iqk_ok,
+ long result[][8],
+ u8 final_candidate,
+ bool btxonly)
+{
+ u32 oldval_0, x, tx0_a, reg;
+ long y, tx0_c;
+
+ if (final_candidate == 0xFF) {
+ return;
+ } else if (iqk_ok) {
+ oldval_0 = (rtl_get_bbreg(hw, ROFDM0_XATXIQIMBALANCE,
+ MASKDWORD) >> 22) & 0x3FF;
+ x = result[final_candidate][0];
+ if ((x & 0x00000200) != 0)
+ x = x | 0xFFFFFC00;
+ tx0_a = (x * oldval_0) >> 8;
+ rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x3FF, tx0_a);
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(31),
+ ((x * oldval_0 >> 7) & 0x1));
+ y = result[final_candidate][1];
+ if ((y & 0x00000200) != 0)
+ y = y | 0xFFFFFC00;
+ tx0_c = (y * oldval_0) >> 8;
+ rtl_set_bbreg(hw, ROFDM0_XCTXAFE, 0xF0000000,
+ ((tx0_c & 0x3C0) >> 6));
+ rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x003F0000,
+ (tx0_c & 0x3F));
+ rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(29),
+ ((y * oldval_0 >> 7) & 0x1));
+ if (btxonly)
+ return;
+ reg = result[final_candidate][2];
+ rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0x3FF, reg);
+ reg = result[final_candidate][3] & 0x3F;
+ rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0xFC00, reg);
+ reg = (result[final_candidate][3] >> 6) & 0xF;
+ rtl_set_bbreg(hw, 0xca0, 0xF0000000, reg);
+ }
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_path_a_fill_iqk_matrix);
+
+void rtl8723_save_adda_registers(struct ieee80211_hw *hw, u32 *addareg,
+ u32 *addabackup, u32 registernum)
+{
+ u32 i;
+
+ for (i = 0; i < registernum; i++)
+ addabackup[i] = rtl_get_bbreg(hw, addareg[i], MASKDWORD);
+}
+EXPORT_SYMBOL_GPL(rtl8723_save_adda_registers);
+
+void rtl8723_phy_save_mac_registers(struct ieee80211_hw *hw,
+ u32 *macreg, u32 *macbackup)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 i;
+
+ for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++)
+ macbackup[i] = rtl_read_byte(rtlpriv, macreg[i]);
+ macbackup[i] = rtl_read_dword(rtlpriv, macreg[i]);
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_save_mac_registers);
+
+void rtl8723_phy_reload_adda_registers(struct ieee80211_hw *hw,
+ u32 *addareg, u32 *addabackup,
+ u32 regiesternum)
+{
+ u32 i;
+
+ for (i = 0; i < regiesternum; i++)
+ rtl_set_bbreg(hw, addareg[i], MASKDWORD, addabackup[i]);
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_reload_adda_registers);
+
+void rtl8723_phy_reload_mac_registers(struct ieee80211_hw *hw,
+ u32 *macreg, u32 *macbackup)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 i;
+
+ for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++)
+ rtl_write_byte(rtlpriv, macreg[i], (u8) macbackup[i]);
+ rtl_write_dword(rtlpriv, macreg[i], macbackup[i]);
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_reload_mac_registers);
+
+void rtl8723_phy_path_adda_on(struct ieee80211_hw *hw, u32 *addareg,
+ bool is_patha_on, bool is2t)
+{
+ u32 pathon;
+ u32 i;
+
+ pathon = is_patha_on ? 0x04db25a4 : 0x0b1b25a4;
+ if (!is2t) {
+ pathon = 0x0bdb25a0;
+ rtl_set_bbreg(hw, addareg[0], MASKDWORD, 0x0b1b25a0);
+ } else {
+ rtl_set_bbreg(hw, addareg[0], MASKDWORD, pathon);
+ }
+
+ for (i = 1; i < IQK_ADDA_REG_NUM; i++)
+ rtl_set_bbreg(hw, addareg[i], MASKDWORD, pathon);
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_path_adda_on);
+
+void rtl8723_phy_mac_setting_calibration(struct ieee80211_hw *hw,
+ u32 *macreg, u32 *macbackup)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ u32 i = 0;
+
+ rtl_write_byte(rtlpriv, macreg[i], 0x3F);
+
+ for (i = 1; i < (IQK_MAC_REG_NUM - 1); i++)
+ rtl_write_byte(rtlpriv, macreg[i],
+ (u8) (macbackup[i] & (~BIT(3))));
+ rtl_write_byte(rtlpriv, macreg[i], (u8) (macbackup[i] & (~BIT(5))));
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_mac_setting_calibration);
+
+void rtl8723_phy_path_a_standby(struct ieee80211_hw *hw)
+{
+ rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x0);
+ rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000);
+ rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000);
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_path_a_standby);
+
+void rtl8723_phy_pi_mode_switch(struct ieee80211_hw *hw, bool pi_mode)
+{
+ u32 mode;
+
+ mode = pi_mode ? 0x01000100 : 0x01000000;
+ rtl_set_bbreg(hw, 0x820, MASKDWORD, mode);
+ rtl_set_bbreg(hw, 0x828, MASKDWORD, mode);
+}
+EXPORT_SYMBOL_GPL(rtl8723_phy_pi_mode_switch);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.h b/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.h
new file mode 100644
index 000000000000..83b891a9adb8
--- /dev/null
+++ b/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.h
@@ -0,0 +1,89 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2014 Realtek 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#ifndef __PHY_COMMON__
+#define __PHY_COMMON__
+
+#define RT_CANNOT_IO(hw) false
+
+enum swchnlcmd_id {
+ CMDID_END,
+ CMDID_SET_TXPOWEROWER_LEVEL,
+ CMDID_BBREGWRITE10,
+ CMDID_WRITEPORT_ULONG,
+ CMDID_WRITEPORT_USHORT,
+ CMDID_WRITEPORT_UCHAR,
+ CMDID_RF_WRITEREG,
+};
+
+struct swchnlcmd {
+ enum swchnlcmd_id cmdid;
+ u32 para1;
+ u32 para2;
+ u32 msdelay;
+};
+
+u32 rtl8723_phy_query_bb_reg(struct ieee80211_hw *hw,
+ u32 regaddr, u32 bitmask);
+void rtl8723_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr,
+ u32 bitmask, u32 data);
+u32 rtl8723_phy_calculate_bit_shift(u32 bitmask);
+u32 rtl8723_phy_rf_serial_read(struct ieee80211_hw *hw,
+ enum radio_path rfpath, u32 offset);
+void rtl8723_phy_rf_serial_write(struct ieee80211_hw *hw,
+ enum radio_path rfpath,
+ u32 offset, u32 data);
+long rtl8723_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw,
+ enum wireless_mode wirelessmode,
+ u8 txpwridx);
+void rtl8723_phy_init_bb_rf_reg_def(struct ieee80211_hw *hw);
+bool rtl8723_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable,
+ u32 cmdtableidx,
+ u32 cmdtablesz,
+ enum swchnlcmd_id cmdid,
+ u32 para1, u32 para2,
+ u32 msdelay);
+void rtl8723_phy_path_a_fill_iqk_matrix(struct ieee80211_hw *hw,
+ bool iqk_ok,
+ long result[][8],
+ u8 final_candidate,
+ bool btxonly);
+void rtl8723_save_adda_registers(struct ieee80211_hw *hw, u32 *addareg,
+ u32 *addabackup, u32 registernum);
+void rtl8723_phy_save_mac_registers(struct ieee80211_hw *hw,
+ u32 *macreg, u32 *macbackup);
+void rtl8723_phy_reload_adda_registers(struct ieee80211_hw *hw,
+ u32 *addareg, u32 *addabackup,
+ u32 regiesternum);
+void rtl8723_phy_reload_mac_registers(struct ieee80211_hw *hw,
+ u32 *macreg, u32 *macbackup);
+void rtl8723_phy_path_adda_on(struct ieee80211_hw *hw, u32 *addareg,
+ bool is_patha_on, bool is2t);
+void rtl8723_phy_mac_setting_calibration(struct ieee80211_hw *hw,
+ u32 *macreg, u32 *macbackup);
+void rtl8723_phy_path_a_standby(struct ieee80211_hw *hw);
+void rtl8723_phy_pi_mode_switch(struct ieee80211_hw *hw, bool pi_mode);
+
+#endif
diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c
index 4933f02ce1d5..0398d3ea15b0 100644
--- a/drivers/net/wireless/rtlwifi/usb.c
+++ b/drivers/net/wireless/rtlwifi/usb.c
@@ -410,7 +410,7 @@ static void rtl_usb_init_sw(struct ieee80211_hw *hw)
mac->current_ampdu_factor = 3;
/* QOS */
- rtlusb->acm_method = eAcmWay2_SW;
+ rtlusb->acm_method = EACMWAY2_SW;
/* IRQ */
/* HIMR - turn all on */
@@ -994,7 +994,7 @@ static void _rtl_usb_tx_preprocess(struct ieee80211_hw *hw,
seq_number += 1;
seq_number <<= 4;
}
- rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, info, sta, skb,
+ rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, NULL, info, sta, skb,
hw_queue, &tcb_desc);
if (!ieee80211_has_morefrags(hdr->frame_control)) {
if (qc)
diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h
index 8c647391bedf..6965afdf572a 100644
--- a/drivers/net/wireless/rtlwifi/wifi.h
+++ b/drivers/net/wireless/rtlwifi/wifi.h
@@ -41,6 +41,38 @@
#include <linux/completion.h>
#include "debug.h"
+#define MASKBYTE0 0xff
+#define MASKBYTE1 0xff00
+#define MASKBYTE2 0xff0000
+#define MASKBYTE3 0xff000000
+#define MASKHWORD 0xffff0000
+#define MASKLWORD 0x0000ffff
+#define MASKDWORD 0xffffffff
+#define MASK12BITS 0xfff
+#define MASKH4BITS 0xf0000000
+#define MASKOFDM_D 0xffc00000
+#define MASKCCK 0x3f3f3f3f
+
+#define MASK4BITS 0x0f
+#define MASK20BITS 0xfffff
+#define RFREG_OFFSET_MASK 0xfffff
+
+#define MASKBYTE0 0xff
+#define MASKBYTE1 0xff00
+#define MASKBYTE2 0xff0000
+#define MASKBYTE3 0xff000000
+#define MASKHWORD 0xffff0000
+#define MASKLWORD 0x0000ffff
+#define MASKDWORD 0xffffffff
+#define MASK12BITS 0xfff
+#define MASKH4BITS 0xf0000000
+#define MASKOFDM_D 0xffc00000
+#define MASKCCK 0x3f3f3f3f
+
+#define MASK4BITS 0x0f
+#define MASK20BITS 0xfffff
+#define RFREG_OFFSET_MASK 0xfffff
+
#define RF_CHANGE_BY_INIT 0
#define RF_CHANGE_BY_IPS BIT(28)
#define RF_CHANGE_BY_PS BIT(29)
@@ -49,6 +81,7 @@
#define IQK_ADDA_REG_NUM 16
#define IQK_MAC_REG_NUM 4
+#define IQK_THRESHOLD 8
#define MAX_KEY_LEN 61
#define KEY_BUF_SIZE 5
@@ -86,7 +119,18 @@
#define MAC80211_4ADDR_LEN 30
#define CHANNEL_MAX_NUMBER (14 + 24 + 21) /* 14 is the max channel no */
+#define CHANNEL_MAX_NUMBER_2G 14
+#define CHANNEL_MAX_NUMBER_5G 54 /* Please refer to
+ *"phy_GetChnlGroup8812A" and
+ * "Hal_ReadTxPowerInfo8812A"
+ */
+#define CHANNEL_MAX_NUMBER_5G_80M 7
#define CHANNEL_GROUP_MAX (3 + 9) /* ch1~3, 4~9, 10~14 = three groups */
+#define CHANNEL_MAX_NUMBER_5G 54 /* Please refer to
+ *"phy_GetChnlGroup8812A" and
+ * "Hal_ReadTxPowerInfo8812A"
+ */
+#define CHANNEL_MAX_NUMBER_5G_80M 7
#define MAX_PG_GROUP 13
#define CHANNEL_GROUP_MAX_2G 3
#define CHANNEL_GROUP_IDX_5GL 3
@@ -96,6 +140,7 @@
#define CHANNEL_MAX_NUMBER_2G 14
#define AVG_THERMAL_NUM 8
#define AVG_THERMAL_NUM_88E 4
+#define AVG_THERMAL_NUM_8723BE 4
#define MAX_TID_COUNT 9
/* for early mode */
@@ -107,6 +152,24 @@
#define MAX_CHNL_GROUP_24G 6
#define MAX_CHNL_GROUP_5G 14
+#define TX_PWR_BY_RATE_NUM_BAND 2
+#define TX_PWR_BY_RATE_NUM_RF 4
+#define TX_PWR_BY_RATE_NUM_SECTION 12
+#define MAX_BASE_NUM_IN_PHY_REG_PG_24G 6
+#define MAX_BASE_NUM_IN_PHY_REG_PG_5G 5
+
+#define RTL8192EE_SEG_NUM 1 /* 0:2 seg, 1: 4 seg, 2: 8 seg */
+
+#define DEL_SW_IDX_SZ 30
+#define BAND_NUM 3
+
+enum rf_tx_num {
+ RF_1TX = 0,
+ RF_2TX,
+ RF_MAX_TX_NUM,
+ RF_TX_NUM_NONIMPLEMENT,
+};
+
struct txpower_info_2g {
u8 index_cck_base[MAX_RF_PATH][MAX_CHNL_GROUP_24G];
u8 index_bw40_base[MAX_RF_PATH][MAX_CHNL_GROUP_24G];
@@ -115,6 +178,8 @@ struct txpower_info_2g {
u8 ofdm_diff[MAX_RF_PATH][MAX_TX_COUNT];
u8 bw20_diff[MAX_RF_PATH][MAX_TX_COUNT];
u8 bw40_diff[MAX_RF_PATH][MAX_TX_COUNT];
+ u8 bw80_diff[MAX_RF_PATH][MAX_TX_COUNT];
+ u8 bw160_diff[MAX_RF_PATH][MAX_TX_COUNT];
};
struct txpower_info_5g {
@@ -123,6 +188,17 @@ struct txpower_info_5g {
u8 ofdm_diff[MAX_RF_PATH][MAX_TX_COUNT];
u8 bw20_diff[MAX_RF_PATH][MAX_TX_COUNT];
u8 bw40_diff[MAX_RF_PATH][MAX_TX_COUNT];
+ u8 bw80_diff[MAX_RF_PATH][MAX_TX_COUNT];
+ u8 bw160_diff[MAX_RF_PATH][MAX_TX_COUNT];
+};
+
+enum rate_section {
+ CCK = 0,
+ OFDM,
+ HT_MCS0_MCS7,
+ HT_MCS8_MCS15,
+ VHT_1SSMCS0_1SSMCS9,
+ VHT_2SSMCS0_2SSMCS9,
};
enum intf_type {
@@ -158,7 +234,10 @@ enum hardware_type {
HARDWARE_TYPE_RTL8192DU,
HARDWARE_TYPE_RTL8723AE,
HARDWARE_TYPE_RTL8723U,
+ HARDWARE_TYPE_RTL8723BE,
HARDWARE_TYPE_RTL8188EE,
+ HARDWARE_TYPE_RTL8821AE,
+ HARDWARE_TYPE_RTL8812AE,
/* keep it last */
HARDWARE_TYPE_NUM
@@ -195,8 +274,16 @@ enum hardware_type {
_pdesc->rxmcs == DESC92_RATE5_5M || \
_pdesc->rxmcs == DESC92_RATE11M)
+#define RTL8723E_RX_HAL_IS_CCK_RATE(rxmcs) \
+ ((rxmcs) == DESC92_RATE1M || \
+ (rxmcs) == DESC92_RATE2M || \
+ (rxmcs) == DESC92_RATE5_5M || \
+ (rxmcs) == DESC92_RATE11M)
+
enum scan_operation_backup_opt {
SCAN_OPT_BACKUP = 0,
+ SCAN_OPT_BACKUP_BAND0 = 0,
+ SCAN_OPT_BACKUP_BAND1,
SCAN_OPT_RESTORE,
SCAN_OPT_MAX
};
@@ -231,7 +318,9 @@ struct bb_reg_def {
enum io_type {
IO_CMD_PAUSE_DM_BY_SCAN = 0,
- IO_CMD_RESUME_DM_BY_SCAN = 1,
+ IO_CMD_PAUSE_BAND0_DM_BY_SCAN = 0,
+ IO_CMD_PAUSE_BAND1_DM_BY_SCAN = 1,
+ IO_CMD_RESUME_DM_BY_SCAN = 2,
};
enum hw_variables {
@@ -298,6 +387,7 @@ enum hw_variables {
HW_VAR_SET_RPWM,
HW_VAR_H2C_FW_PWRMODE,
HW_VAR_H2C_FW_JOINBSSRPT,
+ HW_VAR_H2C_FW_MEDIASTATUSRPT,
HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
HW_VAR_FW_PSMODE_STATUS,
HW_VAR_RESUME_CLK_ON,
@@ -330,6 +420,8 @@ enum hw_variables {
HAL_DEF_WOWLAN,
HW_VAR_MRC,
+ HW_VAR_KEEP_ALIVE,
+ HW_VAR_NAV_UPPER,
HW_VAR_MGT_FILTER,
HW_VAR_CTRL_FILTER,
@@ -348,34 +440,34 @@ enum rt_oem_id {
RT_CID_8187_HW_LED = 3,
RT_CID_8187_NETGEAR = 4,
RT_CID_WHQL = 5,
- RT_CID_819x_CAMEO = 6,
- RT_CID_819x_RUNTOP = 7,
- RT_CID_819x_Senao = 8,
+ RT_CID_819X_CAMEO = 6,
+ RT_CID_819X_RUNTOP = 7,
+ RT_CID_819X_SENAO = 8,
RT_CID_TOSHIBA = 9,
- RT_CID_819x_Netcore = 10,
- RT_CID_Nettronix = 11,
+ RT_CID_819X_NETCORE = 10,
+ RT_CID_NETTRONIX = 11,
RT_CID_DLINK = 12,
RT_CID_PRONET = 13,
RT_CID_COREGA = 14,
- RT_CID_819x_ALPHA = 15,
- RT_CID_819x_Sitecom = 16,
+ RT_CID_819X_ALPHA = 15,
+ RT_CID_819X_SITECOM = 16,
RT_CID_CCX = 17,
- RT_CID_819x_Lenovo = 18,
- RT_CID_819x_QMI = 19,
- RT_CID_819x_Edimax_Belkin = 20,
- RT_CID_819x_Sercomm_Belkin = 21,
- RT_CID_819x_CAMEO1 = 22,
- RT_CID_819x_MSI = 23,
- RT_CID_819x_Acer = 24,
- RT_CID_819x_HP = 27,
- RT_CID_819x_CLEVO = 28,
- RT_CID_819x_Arcadyan_Belkin = 29,
- RT_CID_819x_SAMSUNG = 30,
- RT_CID_819x_WNC_COREGA = 31,
- RT_CID_819x_Foxcoon = 32,
- RT_CID_819x_DELL = 33,
- RT_CID_819x_PRONETS = 34,
- RT_CID_819x_Edimax_ASUS = 35,
+ RT_CID_819X_LENOVO = 18,
+ RT_CID_819X_QMI = 19,
+ RT_CID_819X_EDIMAX_BELKIN = 20,
+ RT_CID_819X_SERCOMM_BELKIN = 21,
+ RT_CID_819X_CAMEO1 = 22,
+ RT_CID_819X_MSI = 23,
+ RT_CID_819X_ACER = 24,
+ RT_CID_819X_HP = 27,
+ RT_CID_819X_CLEVO = 28,
+ RT_CID_819X_ARCADYAN_BELKIN = 29,
+ RT_CID_819X_SAMSUNG = 30,
+ RT_CID_819X_WNC_COREGA = 31,
+ RT_CID_819X_FOXCOON = 32,
+ RT_CID_819X_DELL = 33,
+ RT_CID_819X_PRONETS = 34,
+ RT_CID_819X_EDIMAX_ASUS = 35,
RT_CID_NETGEAR = 36,
RT_CID_PLANEX = 37,
RT_CID_CC_C = 38,
@@ -389,6 +481,7 @@ enum hw_descs {
HW_DESC_RXBUFF_ADDR,
HW_DESC_RXPKT_LEN,
HW_DESC_RXERO,
+ HW_DESC_RX_PREPARE,
};
enum prime_sc {
@@ -407,6 +500,7 @@ enum rf_type {
enum ht_channel_width {
HT_CHANNEL_WIDTH_20 = 0,
HT_CHANNEL_WIDTH_20_40 = 1,
+ HT_CHANNEL_WIDTH_80 = 2,
};
/* Ref: 802.11i sepc D10.0 7.3.2.25.1
@@ -471,6 +565,9 @@ enum rtl_var_map {
MAC_RCR_ACRC32,
MAC_RCR_ACF,
MAC_RCR_AAP,
+ MAC_HIMR,
+ MAC_HIMRE,
+ MAC_HSISR,
/*efuse map */
EFUSE_TEST,
@@ -608,7 +705,7 @@ enum rtl_led_pin {
enum acm_method {
eAcmWay0_SwAndHw = 0,
eAcmWay1_HW = 1,
- eAcmWay2_SW = 2,
+ EACMWAY2_SW = 2,
};
enum macphy_mode {
@@ -645,7 +742,9 @@ enum wireless_mode {
WIRELESS_MODE_G = 0x04,
WIRELESS_MODE_AUTO = 0x08,
WIRELESS_MODE_N_24G = 0x10,
- WIRELESS_MODE_N_5G = 0x20
+ WIRELESS_MODE_N_5G = 0x20,
+ WIRELESS_MODE_AC_5G = 0x40,
+ WIRELESS_MODE_AC_24G = 0x80
};
#define IS_WIRELESS_MODE_A(wirelessmode) \
@@ -669,6 +768,8 @@ enum ratr_table_mode {
RATR_INX_WIRELESS_B = 6,
RATR_INX_WIRELESS_MC = 7,
RATR_INX_WIRELESS_A = 8,
+ RATR_INX_WIRELESS_AC_5N = 8,
+ RATR_INX_WIRELESS_AC_24N = 9,
};
enum rtl_link_state {
@@ -803,8 +904,12 @@ struct wireless_stats {
long signal_strength;
u8 rx_rssi_percentage[4];
+ u8 rx_evm_dbm[4];
u8 rx_evm_percentage[2];
+ u16 rx_cfo_short[4];
+ u16 rx_cfo_tail[4];
+
struct rt_smooth_data ui_rssi;
struct rt_smooth_data ui_link_quality;
};
@@ -817,9 +922,9 @@ struct rate_adaptive {
u32 high_rssi_thresh_for_ra;
u32 high2low_rssi_thresh_for_ra;
u8 low2high_rssi_thresh_for_ra40m;
- u32 low_rssi_thresh_for_ra40M;
+ u32 low_rssi_thresh_for_ra40m;
u8 low2high_rssi_thresh_for_ra20m;
- u32 low_rssi_thresh_for_ra20M;
+ u32 low_rssi_thresh_for_ra20m;
u32 upper_rssi_threshold_ratr;
u32 middleupper_rssi_threshold_ratr;
u32 middle_rssi_threshold_ratr;
@@ -833,6 +938,10 @@ struct rate_adaptive {
u32 ping_rssi_thresh_for_ra;
u32 last_ratr;
u8 pre_ratr_state;
+ u8 ldpc_thres;
+ bool use_ldpc;
+ bool lower_rts_rate;
+ bool is_special_data;
};
struct regd_pair_mapping {
@@ -841,6 +950,16 @@ struct regd_pair_mapping {
u16 reg_2ghz_ctl;
};
+struct dynamic_primary_cca {
+ u8 pricca_flag;
+ u8 intf_flag;
+ u8 intf_type;
+ u8 dup_rts_flag;
+ u8 monitor_flag;
+ u8 ch_offset;
+ u8 mf_state;
+};
+
struct rtl_regulatory {
char alpha2[2];
u16 country_code;
@@ -976,16 +1095,29 @@ struct rtl_phy {
u32 iqk_bb_backup[10];
bool iqk_initialized;
+ bool rfpath_rx_enable[MAX_RF_PATH];
+ u8 reg_837;
/* Dual mac */
bool need_iqk;
struct iqk_matrix_regs iqk_matrix[IQK_MATRIX_SETTINGS_NUM];
bool rfpi_enable;
+ bool iqk_in_progress;
u8 pwrgroup_cnt;
u8 cck_high_power;
/* MAX_PG_GROUP groups of pwr diff by rates */
u32 mcs_offset[MAX_PG_GROUP][16];
+ u32 tx_power_by_rate_offset[TX_PWR_BY_RATE_NUM_BAND]
+ [TX_PWR_BY_RATE_NUM_RF]
+ [TX_PWR_BY_RATE_NUM_RF]
+ [TX_PWR_BY_RATE_NUM_SECTION];
+ u8 txpwr_by_rate_base_24g[TX_PWR_BY_RATE_NUM_RF]
+ [TX_PWR_BY_RATE_NUM_RF]
+ [MAX_BASE_NUM_IN_PHY_REG_PG_24G];
+ u8 txpwr_by_rate_base_5g[TX_PWR_BY_RATE_NUM_RF]
+ [TX_PWR_BY_RATE_NUM_RF]
+ [MAX_BASE_NUM_IN_PHY_REG_PG_5G];
u8 default_initialgain[4];
/* the current Tx power level */
@@ -998,6 +1130,7 @@ struct rtl_phy {
bool apk_done;
u32 reg_rf3c[2]; /* pathA / pathB */
+ u32 backup_rf_0x1a;/*92ee*/
/* bfsync */
u8 framesync;
u32 framesync_c34;
@@ -1006,6 +1139,7 @@ struct rtl_phy {
struct phy_parameters hwparam_tables[MAX_TAB];
u16 rf_pathmap;
+ u8 hw_rof_enable; /*Enable GPIO[9] as WL RF HW PDn source*/
enum rt_polarity_ctl polarity_ctl;
};
@@ -1133,6 +1267,7 @@ struct rtl_mac {
u8 use_cts_protect;
u8 cur_40_prime_sc;
u8 cur_40_prime_sc_bk;
+ u8 cur_80_prime_sc;
u64 tsf;
u8 retry_short;
u8 retry_long;
@@ -1213,6 +1348,7 @@ struct rtl_hal {
bool being_init_adapter;
bool bbrf_ready;
bool mac_func_enable;
+ bool pre_edcca_enable;
struct bt_coexist_8723 hal_coex_8723;
enum intf_type interface;
@@ -1234,6 +1370,7 @@ struct rtl_hal {
/*Reserve page start offset except beacon in TxQ. */
u8 fw_rsvdpage_startoffset;
u8 h2c_txcmd_seq;
+ u8 current_ra_rate;
/* FW Cmd IO related */
u16 fwcmd_iomap;
@@ -1273,6 +1410,9 @@ struct rtl_hal {
bool disable_amsdu_8k;
bool master_of_dmsp;
bool slave_of_dmsp;
+
+ u16 rx_tag;/*for 92ee*/
+ u8 rts_en;
};
struct rtl_security {
@@ -1321,6 +1461,16 @@ struct fast_ant_training {
bool becomelinked;
};
+struct dm_phy_dbg_info {
+ char rx_snrdb[4];
+ u64 num_qry_phy_status;
+ u64 num_qry_phy_status_cck;
+ u64 num_qry_phy_status_ofdm;
+ u16 num_qry_beacon_pkt;
+ u16 num_non_be_pkt;
+ s32 rx_evm[4];
+};
+
struct rtl_dm {
/*PHY status for Dynamic Management */
long entry_min_undec_sm_pwdb;
@@ -1360,29 +1510,84 @@ struct rtl_dm {
u8 txpower_track_control;
bool interrupt_migration;
bool disable_tx_int;
- char ofdm_index[2];
+ char ofdm_index[MAX_RF_PATH];
+ u8 default_ofdm_index;
+ u8 default_cck_index;
char cck_index;
- char delta_power_index;
- char delta_power_index_last;
- char power_index_offset;
+ char delta_power_index[MAX_RF_PATH];
+ char delta_power_index_last[MAX_RF_PATH];
+ char power_index_offset[MAX_RF_PATH];
+ char absolute_ofdm_swing_idx[MAX_RF_PATH];
+ char remnant_ofdm_swing_idx[MAX_RF_PATH];
+ char remnant_cck_idx;
+ bool modify_txagc_flag_path_a;
+ bool modify_txagc_flag_path_b;
+
+ bool one_entry_only;
+ struct dm_phy_dbg_info dbginfo;
+
+ /* Dynamic ATC switch */
+ bool atc_status;
+ bool large_cfo_hit;
+ bool is_freeze;
+ int cfo_tail[2];
+ int cfo_ave_pre;
+ int crystal_cap;
+ u8 cfo_threshold;
+ u32 packet_count;
+ u32 packet_count_pre;
+ u8 tx_rate;
/*88e tx power tracking*/
- u8 swing_idx_ofdm[2];
+ u8 swing_idx_ofdm[MAX_RF_PATH];
u8 swing_idx_ofdm_cur;
- u8 swing_idx_ofdm_base;
+ u8 swing_idx_ofdm_base[MAX_RF_PATH];
bool swing_flag_ofdm;
u8 swing_idx_cck;
u8 swing_idx_cck_cur;
u8 swing_idx_cck_base;
bool swing_flag_cck;
+ char swing_diff_2g;
+ char swing_diff_5g;
+
+ u8 delta_swing_table_idx_24gccka_p[DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_24gccka_n[DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_24gcckb_p[DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_24gcckb_n[DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_24ga_p[DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_24ga_n[DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_24gb_p[DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_24gb_n[DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_5ga_p[BAND_NUM][DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_5ga_n[BAND_NUM][DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_5gb_p[BAND_NUM][DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_5gb_n[BAND_NUM][DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_24ga_p_8188e[DEL_SW_IDX_SZ];
+ u8 delta_swing_table_idx_24ga_n_8188e[DEL_SW_IDX_SZ];
+
/* DMSP */
bool supp_phymode_switch;
+ /* DulMac */
struct fast_ant_training fat_table;
+
+ u8 resp_tx_path;
+ u8 path_sel;
+ u32 patha_sum;
+ u32 pathb_sum;
+ u32 patha_cnt;
+ u32 pathb_cnt;
+
+ u8 pre_channel;
+ u8 *p_channel;
+ u8 linked_interval;
+
+ u64 last_tx_ok_cnt;
+ u64 last_rx_ok_cnt;
};
-#define EFUSE_MAX_LOGICAL_SIZE 256
+#define EFUSE_MAX_LOGICAL_SIZE 512
struct rtl_efuse {
bool autoLoad_ok;
@@ -1422,12 +1627,9 @@ struct rtl_efuse {
u8 eeprom_tssi_5g[3][2]; /* for 5GL/5GM/5GH band. */
u8 eeprom_pwrlimit_ht20[CHANNEL_GROUP_MAX];
u8 eeprom_pwrlimit_ht40[CHANNEL_GROUP_MAX];
- u8 eeprom_chnlarea_txpwr_cck[2][CHANNEL_GROUP_MAX_2G];
- u8 eeprom_chnlarea_txpwr_ht40_1s[2][CHANNEL_GROUP_MAX];
- u8 eprom_chnl_txpwr_ht40_2sdf[2][CHANNEL_GROUP_MAX];
- u8 txpwrlevel_cck[2][CHANNEL_MAX_NUMBER_2G];
- u8 txpwrlevel_ht40_1s[2][CHANNEL_MAX_NUMBER]; /*For HT 40MHZ pwr */
- u8 txpwrlevel_ht40_2s[2][CHANNEL_MAX_NUMBER]; /*For HT 40MHZ pwr */
+ u8 eeprom_chnlarea_txpwr_cck[MAX_RF_PATH][CHANNEL_GROUP_MAX_2G];
+ u8 eeprom_chnlarea_txpwr_ht40_1s[MAX_RF_PATH][CHANNEL_GROUP_MAX];
+ u8 eprom_chnl_txpwr_ht40_2sdf[MAX_RF_PATH][CHANNEL_GROUP_MAX];
u8 internal_pa_5g[2]; /* pathA / pathB */
u8 eeprom_c9;
@@ -1438,9 +1640,38 @@ struct rtl_efuse {
u8 pwrgroup_ht20[2][CHANNEL_MAX_NUMBER];
u8 pwrgroup_ht40[2][CHANNEL_MAX_NUMBER];
- char txpwr_ht20diff[2][CHANNEL_MAX_NUMBER]; /*HT 20<->40 Pwr diff */
- /*For HT<->legacy pwr diff*/
- u8 txpwr_legacyhtdiff[2][CHANNEL_MAX_NUMBER];
+ u8 txpwrlevel_cck[MAX_RF_PATH][CHANNEL_MAX_NUMBER_2G];
+ /*For HT 40MHZ pwr */
+ u8 txpwrlevel_ht40_1s[MAX_RF_PATH][CHANNEL_MAX_NUMBER];
+ /*For HT 40MHZ pwr */
+ u8 txpwrlevel_ht40_2s[MAX_RF_PATH][CHANNEL_MAX_NUMBER];
+
+ /*--------------------------------------------------------*
+ * 8192CE\8192SE\8192DE\8723AE use the following 4 arrays,
+ * other ICs (8188EE\8723BE\8192EE\8812AE...)
+ * define new arrays in Windows code.
+ * BUT, in linux code, we use the same array for all ICs.
+ *
+ * The Correspondance relation between two arrays is:
+ * txpwr_cckdiff[][] == CCK_24G_Diff[][]
+ * txpwr_ht20diff[][] == BW20_24G_Diff[][]
+ * txpwr_ht40diff[][] == BW40_24G_Diff[][]
+ * txpwr_legacyhtdiff[][] == OFDM_24G_Diff[][]
+ *
+ * Sizes of these arrays are decided by the larger ones.
+ */
+ char txpwr_cckdiff[MAX_RF_PATH][CHANNEL_MAX_NUMBER];
+ char txpwr_ht20diff[MAX_RF_PATH][CHANNEL_MAX_NUMBER];
+ char txpwr_ht40diff[MAX_RF_PATH][CHANNEL_MAX_NUMBER];
+ char txpwr_legacyhtdiff[MAX_RF_PATH][CHANNEL_MAX_NUMBER];
+
+ u8 txpwr_5g_bw40base[MAX_RF_PATH][CHANNEL_MAX_NUMBER];
+ u8 txpwr_5g_bw80base[MAX_RF_PATH][CHANNEL_MAX_NUMBER_5G_80M];
+ char txpwr_5g_ofdmdiff[MAX_RF_PATH][MAX_TX_COUNT];
+ char txpwr_5g_bw20diff[MAX_RF_PATH][MAX_TX_COUNT];
+ char txpwr_5g_bw40diff[MAX_RF_PATH][MAX_TX_COUNT];
+ char txpwr_5g_bw80diff[MAX_RF_PATH][MAX_TX_COUNT];
+
u8 txpwr_safetyflag; /* Band edge enable flag */
u16 eeprom_txpowerdiff;
u8 legacy_httxpowerdiff; /* Legacy to HT rate power diff */
@@ -1571,7 +1802,9 @@ struct rtl_stats {
bool rx_is40Mhzpacket;
u32 rx_pwdb_all;
u8 rx_mimo_signalstrength[4]; /*in 0~100 index */
- s8 rx_mimo_sig_qual[2];
+ s8 rx_mimo_sig_qual[4];
+ u8 rx_pwr[4]; /* per-path's pwdb */
+ u8 rx_snr[4]; /* per-path's SNR */
bool packet_matchbssid;
bool is_cck;
bool is_ht;
@@ -1644,6 +1877,8 @@ struct rtl_tcb_desc {
bool btx_enable_sw_calc_duration;
};
+struct rtl92c_firmware_header;
+
struct rtl_hal_ops {
int (*init_sw_vars) (struct ieee80211_hw *hw);
void (*deinit_sw_vars) (struct ieee80211_hw *hw);
@@ -1673,9 +1908,17 @@ struct rtl_hal_ops {
void (*set_hw_reg) (struct ieee80211_hw *hw, u8 variable, u8 *val);
void (*update_rate_tbl) (struct ieee80211_hw *hw,
struct ieee80211_sta *sta, u8 rssi_level);
+ void (*pre_fill_tx_bd_desc)(struct ieee80211_hw *hw, u8 *tx_bd_desc,
+ u8 *desc, u8 queue_index,
+ struct sk_buff *skb, dma_addr_t addr);
void (*update_rate_mask) (struct ieee80211_hw *hw, u8 rssi_level);
+ u16 (*rx_desc_buff_remained_cnt)(struct ieee80211_hw *hw,
+ u8 queue_index);
+ void (*rx_check_dma_ok)(struct ieee80211_hw *hw, u8 *header_desc,
+ u8 queue_index);
void (*fill_tx_desc) (struct ieee80211_hw *hw,
struct ieee80211_hdr *hdr, u8 *pdesc_tx,
+ u8 *pbd_desc_tx,
struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
struct sk_buff *skb, u8 hw_queue,
@@ -1698,8 +1941,11 @@ struct rtl_hal_ops {
enum rf_pwrstate rfpwr_state);
void (*led_control) (struct ieee80211_hw *hw,
enum led_ctl_mode ledaction);
- void (*set_desc) (u8 *pdesc, bool istx, u8 desc_name, u8 *val);
+ void (*set_desc)(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
+ u8 desc_name, u8 *val);
u32 (*get_desc) (u8 *pdesc, bool istx, u8 desc_name);
+ bool (*is_tx_desc_closed) (struct ieee80211_hw *hw,
+ u8 hw_queue, u16 index);
void (*tx_polling) (struct ieee80211_hw *hw, u8 hw_queue);
void (*enable_hw_sec) (struct ieee80211_hw *hw);
void (*set_key) (struct ieee80211_hw *hw, u32 key_index,
@@ -1738,6 +1984,10 @@ struct rtl_hal_ops {
void (*bt_coex_off_before_lps) (struct ieee80211_hw *hw);
void (*fill_h2c_cmd) (struct ieee80211_hw *hw, u8 element_id,
u32 cmd_len, u8 *p_cmdbuffer);
+ bool (*get_btc_status) (void);
+ bool (*is_fw_header) (struct rtl92c_firmware_header *hdr);
+ u32 (*rx_command_packet)(struct ieee80211_hw *hw,
+ struct rtl_stats status, struct sk_buff *skb);
};
struct rtl_intf_ops {
@@ -1847,6 +2097,8 @@ struct rtl_locks {
/*Easy concurrent*/
spinlock_t check_sendpkt_lock;
+
+ spinlock_t iqk_lock;
};
struct rtl_works {
@@ -1915,6 +2167,7 @@ struct ps_t {
u8 cur_ccasate;
u8 pre_rfstate;
u8 cur_rfstate;
+ u8 initialize;
long rssi_val_min;
};
@@ -1939,6 +2192,7 @@ struct dig_t {
u8 cursta_cstate;
u8 presta_cstate;
u8 curmultista_cstate;
+ u8 stop_dig;
char back_val;
char back_range_max;
char back_range_min;
@@ -1956,6 +2210,7 @@ struct dig_t {
u8 cur_ccasate;
u8 large_fa_hit;
u8 dig_dynamic_min;
+ u8 dig_dynamic_min_1;
u8 forbidden_igi;
u8 dig_state;
u8 dig_highpwrstate;
@@ -1972,6 +2227,7 @@ struct dig_t {
char backoffval_range_min;
u8 dig_min_0;
u8 dig_min_1;
+ u8 bt30_cur_igi;
bool media_connect_0;
bool media_connect_1;
@@ -1986,6 +2242,96 @@ struct rtl_global_var {
spinlock_t glb_list_lock;
};
+struct rtl_btc_info {
+ u8 bt_type;
+ u8 btcoexist;
+ u8 ant_num;
+};
+
+struct bt_coexist_info {
+ struct rtl_btc_ops *btc_ops;
+ struct rtl_btc_info btc_info;
+ /* EEPROM BT info. */
+ u8 eeprom_bt_coexist;
+ u8 eeprom_bt_type;
+ u8 eeprom_bt_ant_num;
+ u8 eeprom_bt_ant_isol;
+ u8 eeprom_bt_radio_shared;
+
+ u8 bt_coexistence;
+ u8 bt_ant_num;
+ u8 bt_coexist_type;
+ u8 bt_state;
+ u8 bt_cur_state; /* 0:on, 1:off */
+ u8 bt_ant_isolation; /* 0:good, 1:bad */
+ u8 bt_pape_ctrl; /* 0:SW, 1:SW/HW dynamic */
+ u8 bt_service;
+ u8 bt_radio_shared_type;
+ u8 bt_rfreg_origin_1e;
+ u8 bt_rfreg_origin_1f;
+ u8 bt_rssi_state;
+ u32 ratio_tx;
+ u32 ratio_pri;
+ u32 bt_edca_ul;
+ u32 bt_edca_dl;
+
+ bool init_set;
+ bool bt_busy_traffic;
+ bool bt_traffic_mode_set;
+ bool bt_non_traffic_mode_set;
+
+ bool fw_coexist_all_off;
+ bool sw_coexist_all_off;
+ bool hw_coexist_all_off;
+ u32 cstate;
+ u32 previous_state;
+ u32 cstate_h;
+ u32 previous_state_h;
+
+ u8 bt_pre_rssi_state;
+ u8 bt_pre_rssi_state1;
+
+ u8 reg_bt_iso;
+ u8 reg_bt_sco;
+ bool balance_on;
+ u8 bt_active_zero_cnt;
+ bool cur_bt_disabled;
+ bool pre_bt_disabled;
+
+ u8 bt_profile_case;
+ u8 bt_profile_action;
+ bool bt_busy;
+ bool hold_for_bt_operation;
+ u8 lps_counter;
+};
+
+struct rtl_btc_ops {
+ void (*btc_init_variables) (struct rtl_priv *rtlpriv);
+ void (*btc_init_hal_vars) (struct rtl_priv *rtlpriv);
+ void (*btc_init_hw_config) (struct rtl_priv *rtlpriv);
+ void (*btc_ips_notify) (struct rtl_priv *rtlpriv, u8 type);
+ void (*btc_scan_notify) (struct rtl_priv *rtlpriv, u8 scantype);
+ void (*btc_connect_notify) (struct rtl_priv *rtlpriv, u8 action);
+ void (*btc_mediastatus_notify) (struct rtl_priv *rtlpriv,
+ enum _RT_MEDIA_STATUS mstatus);
+ void (*btc_periodical) (struct rtl_priv *rtlpriv);
+ void (*btc_halt_notify) (void);
+ void (*btc_btinfo_notify) (struct rtl_priv *rtlpriv,
+ u8 *tmp_buf, u8 length);
+ bool (*btc_is_limited_dig) (struct rtl_priv *rtlpriv);
+ bool (*btc_is_disable_edca_turbo) (struct rtl_priv *rtlpriv);
+ bool (*btc_is_bt_disabled) (struct rtl_priv *rtlpriv);
+};
+
+struct proxim {
+ bool proxim_on;
+
+ void *proximity_priv;
+ int (*proxim_rx)(struct ieee80211_hw *hw, struct rtl_stats *status,
+ struct sk_buff *skb);
+ u8 (*proxim_get_var)(struct ieee80211_hw *hw, u8 type);
+};
+
struct rtl_priv {
struct ieee80211_hw *hw;
struct completion firmware_loading_complete;
@@ -2008,6 +2354,7 @@ struct rtl_priv {
struct rtl_ps_ctl psc;
struct rate_adaptive ra;
+ struct dynamic_primary_cca primarycca;
struct wireless_stats stats;
struct rt_link_detect link_info;
struct false_alarm_statistics falsealm_cnt;
@@ -2048,6 +2395,20 @@ struct rtl_priv {
bool enter_ps; /* true when entering PS */
u8 rate_mask[5];
+ /* intel Proximity, should be alloc mem
+ * in intel Proximity module and can only
+ * be used in intel Proximity mode
+ */
+ struct proxim proximity;
+
+ /*for bt coexist use*/
+ struct bt_coexist_info btcoexist;
+
+ /* separate 92ee from other ICs,
+ * 92ee use new trx flow.
+ */
+ bool use_new_trx_flow;
+
/*This must be the last item so
that it points to the data allocated
beyond this structure like:
@@ -2079,6 +2440,15 @@ enum bt_co_type {
BT_CSR_BC8 = 4,
BT_RTL8756 = 5,
BT_RTL8723A = 6,
+ BT_RTL8821A = 7,
+ BT_RTL8723B = 8,
+ BT_RTL8192E = 9,
+ BT_RTL8812A = 11,
+};
+
+enum bt_total_ant_num {
+ ANT_TOTAL_X2 = 0,
+ ANT_TOTAL_X1 = 1
};
enum bt_cur_state {
@@ -2104,62 +2474,6 @@ enum bt_radio_shared {
BT_RADIO_INDIVIDUAL = 1,
};
-struct bt_coexist_info {
-
- /* EEPROM BT info. */
- u8 eeprom_bt_coexist;
- u8 eeprom_bt_type;
- u8 eeprom_bt_ant_num;
- u8 eeprom_bt_ant_isol;
- u8 eeprom_bt_radio_shared;
-
- u8 bt_coexistence;
- u8 bt_ant_num;
- u8 bt_coexist_type;
- u8 bt_state;
- u8 bt_cur_state; /* 0:on, 1:off */
- u8 bt_ant_isolation; /* 0:good, 1:bad */
- u8 bt_pape_ctrl; /* 0:SW, 1:SW/HW dynamic */
- u8 bt_service;
- u8 bt_radio_shared_type;
- u8 bt_rfreg_origin_1e;
- u8 bt_rfreg_origin_1f;
- u8 bt_rssi_state;
- u32 ratio_tx;
- u32 ratio_pri;
- u32 bt_edca_ul;
- u32 bt_edca_dl;
-
- bool init_set;
- bool bt_busy_traffic;
- bool bt_traffic_mode_set;
- bool bt_non_traffic_mode_set;
-
- bool fw_coexist_all_off;
- bool sw_coexist_all_off;
- bool hw_coexist_all_off;
- u32 cstate;
- u32 previous_state;
- u32 cstate_h;
- u32 previous_state_h;
-
- u8 bt_pre_rssi_state;
- u8 bt_pre_rssi_state1;
-
- u8 reg_bt_iso;
- u8 reg_bt_sco;
- bool balance_on;
- u8 bt_active_zero_cnt;
- bool cur_bt_disabled;
- bool pre_bt_disabled;
-
- u8 bt_profile_case;
- u8 bt_profile_action;
- bool bt_busy;
- bool hold_for_bt_operation;
- u8 lps_counter;
-};
-
/****************************************
mem access macro define start
diff --git a/drivers/net/wireless/ti/wilink_platform_data.c b/drivers/net/wireless/ti/wilink_platform_data.c
index 998e95895f9d..a92bd3e89796 100644
--- a/drivers/net/wireless/ti/wilink_platform_data.c
+++ b/drivers/net/wireless/ti/wilink_platform_data.c
@@ -23,17 +23,17 @@
#include <linux/err.h>
#include <linux/wl12xx.h>
-static struct wl12xx_platform_data *platform_data;
+static struct wl12xx_platform_data *wl12xx_platform_data;
int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
{
- if (platform_data)
+ if (wl12xx_platform_data)
return -EBUSY;
if (!data)
return -EINVAL;
- platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL);
- if (!platform_data)
+ wl12xx_platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL);
+ if (!wl12xx_platform_data)
return -ENOMEM;
return 0;
@@ -41,9 +41,34 @@ int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
struct wl12xx_platform_data *wl12xx_get_platform_data(void)
{
- if (!platform_data)
+ if (!wl12xx_platform_data)
return ERR_PTR(-ENODEV);
- return platform_data;
+ return wl12xx_platform_data;
}
EXPORT_SYMBOL(wl12xx_get_platform_data);
+
+static struct wl1251_platform_data *wl1251_platform_data;
+
+int __init wl1251_set_platform_data(const struct wl1251_platform_data *data)
+{
+ if (wl1251_platform_data)
+ return -EBUSY;
+ if (!data)
+ return -EINVAL;
+
+ wl1251_platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL);
+ if (!wl1251_platform_data)
+ return -ENOMEM;
+
+ return 0;
+}
+
+struct wl1251_platform_data *wl1251_get_platform_data(void)
+{
+ if (!wl1251_platform_data)
+ return ERR_PTR(-ENODEV);
+
+ return wl1251_platform_data;
+}
+EXPORT_SYMBOL(wl1251_get_platform_data);
diff --git a/drivers/net/wireless/ti/wl1251/cmd.c b/drivers/net/wireless/ti/wl1251/cmd.c
index 223649bcaa5a..bf1fa18b9786 100644
--- a/drivers/net/wireless/ti/wl1251/cmd.c
+++ b/drivers/net/wireless/ti/wl1251/cmd.c
@@ -448,7 +448,7 @@ int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len,
* Note: This bug may be caused by the fw's DTIM handling.
*/
if (is_zero_ether_addr(wl->bssid))
- cmd->params.scan_options |= WL1251_SCAN_OPT_PRIORITY_HIGH;
+ cmd->params.scan_options |= cpu_to_le16(WL1251_SCAN_OPT_PRIORITY_HIGH);
cmd->params.num_channels = n_channels;
cmd->params.num_probe_requests = n_probes;
cmd->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */
diff --git a/drivers/net/wireless/ti/wl1251/sdio.c b/drivers/net/wireless/ti/wl1251/sdio.c
index e2b3d9c541e8..b661f896e9fe 100644
--- a/drivers/net/wireless/ti/wl1251/sdio.c
+++ b/drivers/net/wireless/ti/wl1251/sdio.c
@@ -28,6 +28,7 @@
#include <linux/wl12xx.h>
#include <linux/irq.h>
#include <linux/pm_runtime.h>
+#include <linux/gpio.h>
#include "wl1251.h"
@@ -182,8 +183,9 @@ static int wl1251_sdio_set_power(struct wl1251 *wl, bool enable)
* callback in case it wants to do any additional setup,
* for example enabling clock buffer for the module.
*/
- if (wl->set_power)
- wl->set_power(true);
+ if (gpio_is_valid(wl->power_gpio))
+ gpio_set_value(wl->power_gpio, true);
+
ret = pm_runtime_get_sync(&func->dev);
if (ret < 0) {
@@ -203,8 +205,8 @@ static int wl1251_sdio_set_power(struct wl1251 *wl, bool enable)
if (ret < 0)
goto out;
- if (wl->set_power)
- wl->set_power(false);
+ if (gpio_is_valid(wl->power_gpio))
+ gpio_set_value(wl->power_gpio, false);
}
out:
@@ -227,7 +229,7 @@ static int wl1251_sdio_probe(struct sdio_func *func,
struct wl1251 *wl;
struct ieee80211_hw *hw;
struct wl1251_sdio *wl_sdio;
- const struct wl12xx_platform_data *wl12xx_board_data;
+ const struct wl1251_platform_data *wl1251_board_data;
hw = wl1251_alloc_hw();
if (IS_ERR(hw))
@@ -254,11 +256,20 @@ static int wl1251_sdio_probe(struct sdio_func *func,
wl->if_priv = wl_sdio;
wl->if_ops = &wl1251_sdio_ops;
- wl12xx_board_data = wl12xx_get_platform_data();
- if (!IS_ERR(wl12xx_board_data)) {
- wl->set_power = wl12xx_board_data->set_power;
- wl->irq = wl12xx_board_data->irq;
- wl->use_eeprom = wl12xx_board_data->use_eeprom;
+ wl1251_board_data = wl1251_get_platform_data();
+ if (!IS_ERR(wl1251_board_data)) {
+ wl->power_gpio = wl1251_board_data->power_gpio;
+ wl->irq = wl1251_board_data->irq;
+ wl->use_eeprom = wl1251_board_data->use_eeprom;
+ }
+
+ if (gpio_is_valid(wl->power_gpio)) {
+ ret = devm_gpio_request(&func->dev, wl->power_gpio,
+ "wl1251 power");
+ if (ret) {
+ wl1251_error("Failed to request gpio: %d\n", ret);
+ goto disable;
+ }
}
if (wl->irq) {
diff --git a/drivers/net/wireless/ti/wl1251/spi.c b/drivers/net/wireless/ti/wl1251/spi.c
index 1342f81e683d..b06d36d99362 100644
--- a/drivers/net/wireless/ti/wl1251/spi.c
+++ b/drivers/net/wireless/ti/wl1251/spi.c
@@ -26,6 +26,10 @@
#include <linux/crc7.h>
#include <linux/spi/spi.h>
#include <linux/wl12xx.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
#include "wl1251.h"
#include "reg.h"
@@ -221,8 +225,8 @@ static void wl1251_spi_disable_irq(struct wl1251 *wl)
static int wl1251_spi_set_power(struct wl1251 *wl, bool enable)
{
- if (wl->set_power)
- wl->set_power(enable);
+ if (gpio_is_valid(wl->power_gpio))
+ gpio_set_value(wl->power_gpio, enable);
return 0;
}
@@ -238,13 +242,13 @@ static const struct wl1251_if_operations wl1251_spi_ops = {
static int wl1251_spi_probe(struct spi_device *spi)
{
- struct wl12xx_platform_data *pdata;
+ struct wl1251_platform_data *pdata = dev_get_platdata(&spi->dev);
+ struct device_node *np = spi->dev.of_node;
struct ieee80211_hw *hw;
struct wl1251 *wl;
int ret;
- pdata = dev_get_platdata(&spi->dev);
- if (!pdata) {
+ if (!np && !pdata) {
wl1251_error("no platform data");
return -ENODEV;
}
@@ -271,22 +275,42 @@ static int wl1251_spi_probe(struct spi_device *spi)
goto out_free;
}
- wl->set_power = pdata->set_power;
- if (!wl->set_power) {
- wl1251_error("set power function missing in platform data");
- return -ENODEV;
+ if (np) {
+ wl->use_eeprom = of_property_read_bool(np, "ti,wl1251-has-eeprom");
+ wl->power_gpio = of_get_named_gpio(np, "ti,power-gpio", 0);
+ } else if (pdata) {
+ wl->power_gpio = pdata->power_gpio;
+ wl->use_eeprom = pdata->use_eeprom;
+ }
+
+ if (wl->power_gpio == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto out_free;
+ }
+
+ if (gpio_is_valid(wl->power_gpio)) {
+ ret = devm_gpio_request_one(&spi->dev, wl->power_gpio,
+ GPIOF_OUT_INIT_LOW, "wl1251 power");
+ if (ret) {
+ wl1251_error("Failed to request gpio: %d\n", ret);
+ goto out_free;
+ }
+ } else {
+ wl1251_error("set power gpio missing in platform data");
+ ret = -ENODEV;
+ goto out_free;
}
wl->irq = spi->irq;
if (wl->irq < 0) {
wl1251_error("irq missing in platform data");
- return -ENODEV;
+ ret = -ENODEV;
+ goto out_free;
}
- wl->use_eeprom = pdata->use_eeprom;
-
irq_set_status_flags(wl->irq, IRQ_NOAUTOEN);
- ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl);
+ ret = devm_request_irq(&spi->dev, wl->irq, wl1251_irq, 0,
+ DRIVER_NAME, wl);
if (ret < 0) {
wl1251_error("request_irq() failed: %d", ret);
goto out_free;
@@ -294,16 +318,26 @@ static int wl1251_spi_probe(struct spi_device *spi)
irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
+ wl->vio = devm_regulator_get(&spi->dev, "vio");
+ if (IS_ERR(wl->vio)) {
+ ret = PTR_ERR(wl->vio);
+ wl1251_error("vio regulator missing: %d", ret);
+ goto out_free;
+ }
+
+ ret = regulator_enable(wl->vio);
+ if (ret)
+ goto out_free;
+
ret = wl1251_init_ieee80211(wl);
if (ret)
- goto out_irq;
+ goto disable_regulator;
return 0;
- out_irq:
- free_irq(wl->irq, wl);
-
- out_free:
+disable_regulator:
+ regulator_disable(wl->vio);
+out_free:
ieee80211_free_hw(hw);
return ret;
@@ -315,6 +349,7 @@ static int wl1251_spi_remove(struct spi_device *spi)
free_irq(wl->irq, wl);
wl1251_free_hw(wl);
+ regulator_disable(wl->vio);
return 0;
}
diff --git a/drivers/net/wireless/ti/wl1251/wl1251.h b/drivers/net/wireless/ti/wl1251/wl1251.h
index 235617a7716d..16dae5269175 100644
--- a/drivers/net/wireless/ti/wl1251/wl1251.h
+++ b/drivers/net/wireless/ti/wl1251/wl1251.h
@@ -276,10 +276,12 @@ struct wl1251 {
void *if_priv;
const struct wl1251_if_operations *if_ops;
- void (*set_power)(bool enable);
+ int power_gpio;
int irq;
bool use_eeprom;
+ struct regulator *vio;
+
spinlock_t wl_lock;
enum wl1251_state state;
diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
index be7129ba16ad..d50dfac91631 100644
--- a/drivers/net/wireless/ti/wl12xx/main.c
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -1378,7 +1378,7 @@ static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
static int wl12xx_tx_delayed_compl(struct wl1271 *wl)
{
- if (wl->fw_status_1->tx_results_counter ==
+ if (wl->fw_status->tx_results_counter ==
(wl->tx_results_count & 0xff))
return 0;
@@ -1438,6 +1438,37 @@ out:
return ret;
}
+static void wl12xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
+ struct wl_fw_status *fw_status)
+{
+ struct wl12xx_fw_status *int_fw_status = raw_fw_status;
+
+ fw_status->intr = le32_to_cpu(int_fw_status->intr);
+ fw_status->fw_rx_counter = int_fw_status->fw_rx_counter;
+ fw_status->drv_rx_counter = int_fw_status->drv_rx_counter;
+ fw_status->tx_results_counter = int_fw_status->tx_results_counter;
+ fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs;
+
+ fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime);
+ fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap);
+ fw_status->link_fast_bitmap =
+ le32_to_cpu(int_fw_status->link_fast_bitmap);
+ fw_status->total_released_blks =
+ le32_to_cpu(int_fw_status->total_released_blks);
+ fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total);
+
+ fw_status->counters.tx_released_pkts =
+ int_fw_status->counters.tx_released_pkts;
+ fw_status->counters.tx_lnk_free_pkts =
+ int_fw_status->counters.tx_lnk_free_pkts;
+ fw_status->counters.tx_voice_released_blks =
+ int_fw_status->counters.tx_voice_released_blks;
+ fw_status->counters.tx_last_rate =
+ int_fw_status->counters.tx_last_rate;
+
+ fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr);
+}
+
static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
@@ -1677,6 +1708,7 @@ static struct wlcore_ops wl12xx_ops = {
.tx_delayed_compl = wl12xx_tx_delayed_compl,
.hw_init = wl12xx_hw_init,
.init_vif = NULL,
+ .convert_fw_status = wl12xx_convert_fw_status,
.sta_get_ap_rate_mask = wl12xx_sta_get_ap_rate_mask,
.get_pg_ver = wl12xx_get_pg_ver,
.get_mac = wl12xx_get_mac,
@@ -1711,22 +1743,53 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
},
};
+static const struct ieee80211_iface_limit wl12xx_iface_limits[] = {
+ {
+ .max = 3,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT),
+ },
+};
+
+static const struct ieee80211_iface_combination
+wl12xx_iface_combinations[] = {
+ {
+ .max_interfaces = 3,
+ .limits = wl12xx_iface_limits,
+ .n_limits = ARRAY_SIZE(wl12xx_iface_limits),
+ .num_different_channels = 1,
+ },
+};
+
static int wl12xx_setup(struct wl1271 *wl)
{
struct wl12xx_priv *priv = wl->priv;
struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev);
struct wl12xx_platform_data *pdata = pdev_data->pdata;
+ BUILD_BUG_ON(WL12XX_MAX_LINKS > WLCORE_MAX_LINKS);
+ BUILD_BUG_ON(WL12XX_MAX_AP_STATIONS > WL12XX_MAX_LINKS);
+
wl->rtable = wl12xx_rtable;
wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
- wl->num_channels = 1;
+ wl->num_links = WL12XX_MAX_LINKS;
+ wl->max_ap_stations = WL12XX_MAX_AP_STATIONS;
+ wl->iface_combinations = wl12xx_iface_combinations;
+ wl->n_iface_combinations = ARRAY_SIZE(wl12xx_iface_combinations);
wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;
wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
+ wl->fw_status_len = sizeof(struct wl12xx_fw_status);
wl->fw_status_priv_len = 0;
wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics);
+ wl->ofdm_only_ap = true;
wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl12xx_ht_cap);
wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap);
wl12xx_conf_init(wl);
diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h
index 9e5484a73667..75c92658bfea 100644
--- a/drivers/net/wireless/ti/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h
@@ -65,6 +65,9 @@
#define WL12XX_RX_BA_MAX_SESSIONS 3
+#define WL12XX_MAX_AP_STATIONS 8
+#define WL12XX_MAX_LINKS 12
+
struct wl127x_rx_mem_pool_addr {
u32 addr;
u32 addr_extra;
@@ -79,4 +82,54 @@ struct wl12xx_priv {
struct wl127x_rx_mem_pool_addr *rx_mem_addr;
};
+struct wl12xx_fw_packet_counters {
+ /* Cumulative counter of released packets per AC */
+ u8 tx_released_pkts[NUM_TX_QUEUES];
+
+ /* Cumulative counter of freed packets per HLID */
+ u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS];
+
+ /* Cumulative counter of released Voice memory blocks */
+ u8 tx_voice_released_blks;
+
+ /* Tx rate of the last transmitted packet */
+ u8 tx_last_rate;
+
+ u8 padding[2];
+} __packed;
+
+/* FW status registers */
+struct wl12xx_fw_status {
+ __le32 intr;
+ u8 fw_rx_counter;
+ u8 drv_rx_counter;
+ u8 reserved;
+ u8 tx_results_counter;
+ __le32 rx_pkt_descs[WL12XX_NUM_RX_DESCRIPTORS];
+
+ __le32 fw_localtime;
+
+ /*
+ * A bitmap (where each bit represents a single HLID)
+ * to indicate if the station is in PS mode.
+ */
+ __le32 link_ps_bitmap;
+
+ /*
+ * A bitmap (where each bit represents a single HLID) to indicate
+ * if the station is in Fast mode
+ */
+ __le32 link_fast_bitmap;
+
+ /* Cumulative counter of total released mem blocks since FW-reset */
+ __le32 total_released_blks;
+
+ /* Size (in Memory Blocks) of TX pool */
+ __le32 tx_total;
+
+ struct wl12xx_fw_packet_counters counters;
+
+ __le32 log_start_addr;
+} __packed;
+
#endif /* __WL12XX_PRIV_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index ec37b16585df..de5b4fa5d166 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -648,7 +648,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
};
/* TODO: maybe move to a new header file? */
-#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-2.bin"
+#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-3.bin"
static int wl18xx_identify_chip(struct wl1271 *wl)
{
@@ -1133,6 +1133,39 @@ static int wl18xx_hw_init(struct wl1271 *wl)
return ret;
}
+static void wl18xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
+ struct wl_fw_status *fw_status)
+{
+ struct wl18xx_fw_status *int_fw_status = raw_fw_status;
+
+ fw_status->intr = le32_to_cpu(int_fw_status->intr);
+ fw_status->fw_rx_counter = int_fw_status->fw_rx_counter;
+ fw_status->drv_rx_counter = int_fw_status->drv_rx_counter;
+ fw_status->tx_results_counter = int_fw_status->tx_results_counter;
+ fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs;
+
+ fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime);
+ fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap);
+ fw_status->link_fast_bitmap =
+ le32_to_cpu(int_fw_status->link_fast_bitmap);
+ fw_status->total_released_blks =
+ le32_to_cpu(int_fw_status->total_released_blks);
+ fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total);
+
+ fw_status->counters.tx_released_pkts =
+ int_fw_status->counters.tx_released_pkts;
+ fw_status->counters.tx_lnk_free_pkts =
+ int_fw_status->counters.tx_lnk_free_pkts;
+ fw_status->counters.tx_voice_released_blks =
+ int_fw_status->counters.tx_voice_released_blks;
+ fw_status->counters.tx_last_rate =
+ int_fw_status->counters.tx_last_rate;
+
+ fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr);
+
+ fw_status->priv = &int_fw_status->priv;
+}
+
static void wl18xx_set_tx_desc_csum(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb)
@@ -1572,7 +1605,7 @@ static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
{
u8 thold;
struct wl18xx_fw_status_priv *status_priv =
- (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+ (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
/* suspended links are never high priority */
@@ -1594,7 +1627,7 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
{
u8 thold;
struct wl18xx_fw_status_priv *status_priv =
- (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+ (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
@@ -1632,6 +1665,7 @@ static struct wlcore_ops wl18xx_ops = {
.tx_immediate_compl = wl18xx_tx_immediate_completion,
.tx_delayed_compl = NULL,
.hw_init = wl18xx_hw_init,
+ .convert_fw_status = wl18xx_convert_fw_status,
.set_tx_desc_csum = wl18xx_set_tx_desc_csum,
.get_pg_ver = wl18xx_get_pg_ver,
.set_rx_csum = wl18xx_set_rx_csum,
@@ -1713,19 +1747,62 @@ static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
},
};
+static const struct ieee80211_iface_limit wl18xx_iface_limits[] = {
+ {
+ .max = 3,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT),
+ },
+};
+
+static const struct ieee80211_iface_limit wl18xx_iface_ap_limits[] = {
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_AP),
+ },
+};
+
+static const struct ieee80211_iface_combination
+wl18xx_iface_combinations[] = {
+ {
+ .max_interfaces = 3,
+ .limits = wl18xx_iface_limits,
+ .n_limits = ARRAY_SIZE(wl18xx_iface_limits),
+ .num_different_channels = 2,
+ },
+ {
+ .max_interfaces = 2,
+ .limits = wl18xx_iface_ap_limits,
+ .n_limits = ARRAY_SIZE(wl18xx_iface_ap_limits),
+ .num_different_channels = 1,
+ }
+};
+
static int wl18xx_setup(struct wl1271 *wl)
{
struct wl18xx_priv *priv = wl->priv;
int ret;
+ BUILD_BUG_ON(WL18XX_MAX_LINKS > WLCORE_MAX_LINKS);
+ BUILD_BUG_ON(WL18XX_MAX_AP_STATIONS > WL18XX_MAX_LINKS);
+
wl->rtable = wl18xx_rtable;
wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS;
wl->num_rx_desc = WL18XX_NUM_RX_DESCRIPTORS;
- wl->num_channels = 2;
+ wl->num_links = WL18XX_MAX_LINKS;
+ wl->max_ap_stations = WL18XX_MAX_AP_STATIONS;
+ wl->iface_combinations = wl18xx_iface_combinations;
+ wl->n_iface_combinations = ARRAY_SIZE(wl18xx_iface_combinations);
wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES;
wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0;
+ wl->fw_status_len = sizeof(struct wl18xx_fw_status);
wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv);
wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics);
wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv);
diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c
index 57c694396647..be1ebd55ac88 100644
--- a/drivers/net/wireless/ti/wl18xx/tx.c
+++ b/drivers/net/wireless/ti/wl18xx/tx.c
@@ -32,7 +32,7 @@ static
void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
struct ieee80211_tx_rate *rate)
{
- u8 fw_rate = wl->fw_status_2->counters.tx_last_rate;
+ u8 fw_rate = wl->fw_status->counters.tx_last_rate;
if (fw_rate > CONF_HW_RATE_INDEX_MAX) {
wl1271_error("last Tx rate invalid: %d", fw_rate);
@@ -139,7 +139,7 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
void wl18xx_tx_immediate_complete(struct wl1271 *wl)
{
struct wl18xx_fw_status_priv *status_priv =
- (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+ (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
struct wl18xx_priv *priv = wl->priv;
u8 i;
diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h
index 9204e07ee432..eb7cfe817010 100644
--- a/drivers/net/wireless/ti/wl18xx/wl18xx.h
+++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h
@@ -26,10 +26,10 @@
/* minimum FW required for driver */
#define WL18XX_CHIP_VER 8
-#define WL18XX_IFTYPE_VER 5
+#define WL18XX_IFTYPE_VER 8
#define WL18XX_MAJOR_VER WLCORE_FW_VER_IGNORE
#define WL18XX_SUBTYPE_VER WLCORE_FW_VER_IGNORE
-#define WL18XX_MINOR_VER 39
+#define WL18XX_MINOR_VER 13
#define WL18XX_CMD_MAX_SIZE 740
@@ -40,7 +40,10 @@
#define WL18XX_NUM_MAC_ADDRESSES 3
-#define WL18XX_RX_BA_MAX_SESSIONS 5
+#define WL18XX_RX_BA_MAX_SESSIONS 13
+
+#define WL18XX_MAX_AP_STATIONS 10
+#define WL18XX_MAX_LINKS 16
struct wl18xx_priv {
/* buffer for sending commands to FW */
@@ -109,6 +112,59 @@ struct wl18xx_fw_status_priv {
u8 padding[3];
};
+struct wl18xx_fw_packet_counters {
+ /* Cumulative counter of released packets per AC */
+ u8 tx_released_pkts[NUM_TX_QUEUES];
+
+ /* Cumulative counter of freed packets per HLID */
+ u8 tx_lnk_free_pkts[WL18XX_MAX_LINKS];
+
+ /* Cumulative counter of released Voice memory blocks */
+ u8 tx_voice_released_blks;
+
+ /* Tx rate of the last transmitted packet */
+ u8 tx_last_rate;
+
+ u8 padding[2];
+} __packed;
+
+/* FW status registers */
+struct wl18xx_fw_status {
+ __le32 intr;
+ u8 fw_rx_counter;
+ u8 drv_rx_counter;
+ u8 reserved;
+ u8 tx_results_counter;
+ __le32 rx_pkt_descs[WL18XX_NUM_RX_DESCRIPTORS];
+
+ __le32 fw_localtime;
+
+ /*
+ * A bitmap (where each bit represents a single HLID)
+ * to indicate if the station is in PS mode.
+ */
+ __le32 link_ps_bitmap;
+
+ /*
+ * A bitmap (where each bit represents a single HLID) to indicate
+ * if the station is in Fast mode
+ */
+ __le32 link_fast_bitmap;
+
+ /* Cumulative counter of total released mem blocks since FW-reset */
+ __le32 total_released_blks;
+
+ /* Size (in Memory Blocks) of TX pool */
+ __le32 tx_total;
+
+ struct wl18xx_fw_packet_counters counters;
+
+ __le32 log_start_addr;
+
+ /* Private status to be used by the lower drivers */
+ struct wl18xx_fw_status_priv priv;
+} __packed;
+
#define WL18XX_PHY_VERSION_MAX_LEN 20
struct wl18xx_static_data_priv {
diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c
index ec83675a2446..b924ceadc02c 100644
--- a/drivers/net/wireless/ti/wlcore/acx.c
+++ b/drivers/net/wireless/ti/wlcore/acx.c
@@ -358,7 +358,8 @@ int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct acx_beacon_filter_option *beacon_filter = NULL;
int ret = 0;
- wl1271_debug(DEBUG_ACX, "acx beacon filter opt");
+ wl1271_debug(DEBUG_ACX, "acx beacon filter opt enable=%d",
+ enable_filter);
if (enable_filter &&
wl->conf.conn.bcn_filt_mode == CONF_BCN_FILT_MODE_DISABLED)
@@ -1591,7 +1592,8 @@ out:
return ret;
}
-int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr)
+int wl1271_acx_set_inconnection_sta(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif, u8 *addr)
{
struct wl1271_acx_inconnection_sta *acx = NULL;
int ret;
@@ -1603,6 +1605,7 @@ int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr)
return -ENOMEM;
memcpy(acx->addr, addr, ETH_ALEN);
+ acx->role_id = wlvif->role_id;
ret = wl1271_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST,
acx, sizeof(*acx));
diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h
index 6dcfad9b0472..954d57ec98f4 100644
--- a/drivers/net/wireless/ti/wlcore/acx.h
+++ b/drivers/net/wireless/ti/wlcore/acx.h
@@ -824,7 +824,8 @@ struct wl1271_acx_inconnection_sta {
struct acx_header header;
u8 addr[ETH_ALEN];
- u8 padding1[2];
+ u8 role_id;
+ u8 padding;
} __packed;
/*
@@ -1118,7 +1119,8 @@ int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable);
int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_acx_config_ps(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
+int wl1271_acx_set_inconnection_sta(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif, u8 *addr);
int wl1271_acx_fm_coex(struct wl1271 *wl);
int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl);
int wl12xx_acx_config_hangover(struct wl1271 *wl);
diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index 9b2ecf52449f..40dc30f4faaa 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -60,8 +60,8 @@ static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf,
u16 status;
u16 poll_count = 0;
- if (WARN_ON(wl->state == WLCORE_STATE_RESTARTING &&
- id != CMD_STOP_FWLOGGER))
+ if (unlikely(wl->state == WLCORE_STATE_RESTARTING &&
+ id != CMD_STOP_FWLOGGER))
return -EIO;
cmd = buf;
@@ -312,8 +312,8 @@ static int wlcore_get_new_session_id(struct wl1271 *wl, u8 hlid)
int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
{
unsigned long flags;
- u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS);
- if (link >= WL12XX_MAX_LINKS)
+ u8 link = find_first_zero_bit(wl->links_map, wl->num_links);
+ if (link >= wl->num_links)
return -EBUSY;
wl->session_ids[link] = wlcore_get_new_session_id(wl, link);
@@ -324,9 +324,14 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
__set_bit(link, wlvif->links_map);
spin_unlock_irqrestore(&wl->wl_lock, flags);
- /* take the last "freed packets" value from the current FW status */
- wl->links[link].prev_freed_pkts =
- wl->fw_status_2->counters.tx_lnk_free_pkts[link];
+ /*
+ * take the last "freed packets" value from the current FW status.
+ * on recovery, we might not have fw_status yet, and
+ * tx_lnk_free_pkts will be NULL. check for it.
+ */
+ if (wl->fw_status->counters.tx_lnk_free_pkts)
+ wl->links[link].prev_freed_pkts =
+ wl->fw_status->counters.tx_lnk_free_pkts[link];
wl->links[link].wlvif = wlvif;
/*
@@ -1527,6 +1532,7 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
cmd->sp_len = sta->max_sp;
cmd->wmm = sta->wme ? 1 : 0;
cmd->session_id = wl->session_ids[hlid];
+ cmd->role_id = wlvif->role_id;
for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
if (sta->wme && (sta->uapsd_queues & BIT(i)))
@@ -1563,7 +1569,8 @@ out:
return ret;
}
-int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid)
+int wl12xx_cmd_remove_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ u8 hlid)
{
struct wl12xx_cmd_remove_peer *cmd;
int ret;
@@ -1581,6 +1588,7 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid)
/* We never send a deauth, mac80211 is in charge of this */
cmd->reason_opcode = 0;
cmd->send_deauth_flag = 0;
+ cmd->role_id = wlvif->role_id;
ret = wl1271_cmd_send(wl, CMD_REMOVE_PEER, cmd, sizeof(*cmd), 0);
if (ret < 0) {
diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h
index 323d4a856e4b..b084830a61cf 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.h
+++ b/drivers/net/wireless/ti/wlcore/cmd.h
@@ -88,7 +88,8 @@ int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
int wl12xx_croc(struct wl1271 *wl, u8 role_id);
int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_sta *sta, u8 hlid);
-int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid);
+int wl12xx_cmd_remove_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ u8 hlid);
void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
enum ieee80211_band band);
int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl);
@@ -206,7 +207,7 @@ enum cmd_templ {
#define WL1271_COMMAND_TIMEOUT 2000
#define WL1271_CMD_TEMPL_DFLT_SIZE 252
#define WL1271_CMD_TEMPL_MAX_SIZE 512
-#define WL1271_EVENT_TIMEOUT 1500
+#define WL1271_EVENT_TIMEOUT 5000
struct wl1271_cmd_header {
__le16 id;
@@ -594,6 +595,8 @@ struct wl12xx_cmd_add_peer {
u8 sp_len;
u8 wmm;
u8 session_id;
+ u8 role_id;
+ u8 padding[3];
} __packed;
struct wl12xx_cmd_remove_peer {
@@ -602,7 +605,7 @@ struct wl12xx_cmd_remove_peer {
u8 hlid;
u8 reason_opcode;
u8 send_deauth_flag;
- u8 padding1;
+ u8 role_id;
} __packed;
/*
diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c
index 8d3b34965db3..1f9a36031b06 100644
--- a/drivers/net/wireless/ti/wlcore/event.c
+++ b/drivers/net/wireless/ti/wlcore/event.c
@@ -67,7 +67,7 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
u8 hlid;
struct wl1271_link *lnk;
for_each_set_bit(hlid, wlvif->ap.sta_hlid_map,
- WL12XX_MAX_LINKS) {
+ wl->num_links) {
lnk = &wl->links[hlid];
if (!lnk->ba_bitmap)
continue;
@@ -172,7 +172,7 @@ static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap)
const u8 *addr;
int h;
- for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
+ for_each_set_bit(h, &sta_bitmap, wl->num_links) {
bool found = false;
/* find the ap vif connected to this sta */
wl12xx_for_each_wlvif_ap(wl, wlvif) {
diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h
index 51f8d634d32f..1555ff970050 100644
--- a/drivers/net/wireless/ti/wlcore/hw_ops.h
+++ b/drivers/net/wireless/ti/wlcore/hw_ops.h
@@ -106,6 +106,15 @@ wlcore_hw_init_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
return 0;
}
+static inline void
+wlcore_hw_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
+ struct wl_fw_status *fw_status)
+{
+ BUG_ON(!wl->ops->convert_fw_status);
+
+ wl->ops->convert_fw_status(wl, raw_fw_status, fw_status);
+}
+
static inline u32
wlcore_hw_sta_get_ap_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c
index 7699f9d07e26..199e94120864 100644
--- a/drivers/net/wireless/ti/wlcore/init.c
+++ b/drivers/net/wireless/ti/wlcore/init.c
@@ -287,8 +287,8 @@ static int wl1271_init_sta_beacon_filter(struct wl1271 *wl,
if (ret < 0)
return ret;
- /* enable beacon filtering */
- ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
+ /* disable beacon filtering until we get the first beacon */
+ ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
if (ret < 0)
return ret;
@@ -462,7 +462,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
* If the basic rates contain OFDM rates, use OFDM only
* rates for unicast TX as well. Else use all supported rates.
*/
- if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
+ if (wl->ofdm_only_ap && (wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
supported_rates = CONF_TX_OFDM_RATES;
else
supported_rates = CONF_TX_ENABLED_RATES;
diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h
index 07e3d6a049ad..0305729d0986 100644
--- a/drivers/net/wireless/ti/wlcore/io.h
+++ b/drivers/net/wireless/ti/wlcore/io.h
@@ -60,7 +60,9 @@ static inline int __must_check wlcore_raw_write(struct wl1271 *wl, int addr,
{
int ret;
- if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags))
+ if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags) ||
+ WARN_ON((test_bit(WL1271_FLAG_IN_ELP, &wl->flags) &&
+ addr != HW_ACCESS_ELP_CTRL_REG)))
return -EIO;
ret = wl->if_ops->write(wl->dev, addr, buf, len, fixed);
@@ -76,7 +78,9 @@ static inline int __must_check wlcore_raw_read(struct wl1271 *wl, int addr,
{
int ret;
- if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags))
+ if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags) ||
+ WARN_ON((test_bit(WL1271_FLAG_IN_ELP, &wl->flags) &&
+ addr != HW_ACCESS_ELP_CTRL_REG)))
return -EIO;
ret = wl->if_ops->read(wl->dev, addr, buf, len, fixed);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index b46b3116cc55..ed88d3913483 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -345,24 +345,24 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
* Start high-level PS if the STA is asleep with enough blocks in FW.
* Make an exception if this is the only connected link. In this
* case FW-memory congestion is less of a problem.
- * Note that a single connected STA means 3 active links, since we must
- * account for the global and broadcast AP links. The "fw_ps" check
- * assures us the third link is a STA connected to the AP. Otherwise
- * the FW would not set the PSM bit.
+ * Note that a single connected STA means 2*ap_count + 1 active links,
+ * since we must account for the global and broadcast AP links
+ * for each AP. The "fw_ps" check assures us the other link is a STA
+ * connected to the AP. Otherwise the FW would not set the PSM bit.
*/
- else if (wl->active_link_count > 3 && fw_ps &&
+ else if (wl->active_link_count > (wl->ap_count*2 + 1) && fw_ps &&
tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
wl12xx_ps_link_start(wl, wlvif, hlid, true);
}
static void wl12xx_irq_update_links_status(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
- struct wl_fw_status_2 *status)
+ struct wl_fw_status *status)
{
u32 cur_fw_ps_map;
u8 hlid;
- cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
+ cur_fw_ps_map = status->link_ps_bitmap;
if (wl->ap_fw_ps_map != cur_fw_ps_map) {
wl1271_debug(DEBUG_PSM,
"link ps prev 0x%x cur 0x%x changed 0x%x",
@@ -372,77 +372,73 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
wl->ap_fw_ps_map = cur_fw_ps_map;
}
- for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS)
+ for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, wl->num_links)
wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
wl->links[hlid].allocated_pkts);
}
-static int wlcore_fw_status(struct wl1271 *wl,
- struct wl_fw_status_1 *status_1,
- struct wl_fw_status_2 *status_2)
+static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status)
{
struct wl12xx_vif *wlvif;
struct timespec ts;
u32 old_tx_blk_count = wl->tx_blocks_available;
int avail, freed_blocks;
int i;
- size_t status_len;
int ret;
struct wl1271_link *lnk;
- status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
- sizeof(*status_2) + wl->fw_status_priv_len;
-
- ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
- status_len, false);
+ ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR,
+ wl->raw_fw_status,
+ wl->fw_status_len, false);
if (ret < 0)
return ret;
+ wlcore_hw_convert_fw_status(wl, wl->raw_fw_status, wl->fw_status);
+
wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
"drv_rx_counter = %d, tx_results_counter = %d)",
- status_1->intr,
- status_1->fw_rx_counter,
- status_1->drv_rx_counter,
- status_1->tx_results_counter);
+ status->intr,
+ status->fw_rx_counter,
+ status->drv_rx_counter,
+ status->tx_results_counter);
for (i = 0; i < NUM_TX_QUEUES; i++) {
/* prevent wrap-around in freed-packets counter */
wl->tx_allocated_pkts[i] -=
- (status_2->counters.tx_released_pkts[i] -
+ (status->counters.tx_released_pkts[i] -
wl->tx_pkts_freed[i]) & 0xff;
- wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
+ wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i];
}
- for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) {
+ for_each_set_bit(i, wl->links_map, wl->num_links) {
u8 diff;
lnk = &wl->links[i];
/* prevent wrap-around in freed-packets counter */
- diff = (status_2->counters.tx_lnk_free_pkts[i] -
+ diff = (status->counters.tx_lnk_free_pkts[i] -
lnk->prev_freed_pkts) & 0xff;
if (diff == 0)
continue;
lnk->allocated_pkts -= diff;
- lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i];
+ lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[i];
/* accumulate the prev_freed_pkts counter */
lnk->total_freed_pkts += diff;
}
/* prevent wrap-around in total blocks counter */
- if (likely(wl->tx_blocks_freed <=
- le32_to_cpu(status_2->total_released_blks)))
- freed_blocks = le32_to_cpu(status_2->total_released_blks) -
+ if (likely(wl->tx_blocks_freed <= status->total_released_blks))
+ freed_blocks = status->total_released_blks -
wl->tx_blocks_freed;
else
freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
- le32_to_cpu(status_2->total_released_blks);
+ status->total_released_blks;
- wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks);
+ wl->tx_blocks_freed = status->total_released_blks;
wl->tx_allocated_blocks -= freed_blocks;
@@ -458,7 +454,7 @@ static int wlcore_fw_status(struct wl1271 *wl,
cancel_delayed_work(&wl->tx_watchdog_work);
}
- avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks;
+ avail = status->tx_total - wl->tx_allocated_blocks;
/*
* The FW might change the total number of TX memblocks before
@@ -477,15 +473,15 @@ static int wlcore_fw_status(struct wl1271 *wl,
/* for AP update num of allocated TX blocks per link and ps status */
wl12xx_for_each_wlvif_ap(wl, wlvif) {
- wl12xx_irq_update_links_status(wl, wlvif, status_2);
+ wl12xx_irq_update_links_status(wl, wlvif, status);
}
/* update the host-chipset time offset */
getnstimeofday(&ts);
wl->time_offset = (timespec_to_ns(&ts) >> 10) -
- (s64)le32_to_cpu(status_2->fw_localtime);
+ (s64)(status->fw_localtime);
- wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap);
+ wl->fw_fast_lnk_map = status->link_fast_bitmap;
return 0;
}
@@ -549,13 +545,13 @@ static int wlcore_irq_locked(struct wl1271 *wl)
clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
smp_mb__after_clear_bit();
- ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+ ret = wlcore_fw_status(wl, wl->fw_status);
if (ret < 0)
goto out;
wlcore_hw_tx_immediate_compl(wl);
- intr = le32_to_cpu(wl->fw_status_1->intr);
+ intr = wl->fw_status->intr;
intr &= WLCORE_ALL_INTR_MASK;
if (!intr) {
done = true;
@@ -584,7 +580,7 @@ static int wlcore_irq_locked(struct wl1271 *wl)
if (likely(intr & WL1271_ACX_INTR_DATA)) {
wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
- ret = wlcore_rx(wl, wl->fw_status_1);
+ ret = wlcore_rx(wl, wl->fw_status);
if (ret < 0)
goto out;
@@ -786,10 +782,11 @@ out:
void wl12xx_queue_recovery_work(struct wl1271 *wl)
{
- WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
-
/* Avoid a recursive recovery */
if (wl->state == WLCORE_STATE_ON) {
+ WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY,
+ &wl->flags));
+
wl->state = WLCORE_STATE_RESTARTING;
set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
wl1271_ps_elp_wakeup(wl);
@@ -803,7 +800,7 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
size_t len;
/* Make sure we have enough room */
- len = min(maxlen, (size_t)(PAGE_SIZE - wl->fwlog_size));
+ len = min_t(size_t, maxlen, PAGE_SIZE - wl->fwlog_size);
/* Fill the FW log file, consumed by the sysfs fwlog entry */
memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
@@ -843,11 +840,11 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
wl12xx_cmd_stop_fwlog(wl);
/* Read the first memory block address */
- ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+ ret = wlcore_fw_status(wl, wl->fw_status);
if (ret < 0)
goto out;
- addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
+ addr = wl->fw_status->log_start_addr;
if (!addr)
goto out;
@@ -990,23 +987,23 @@ static int wlcore_fw_wakeup(struct wl1271 *wl)
static int wl1271_setup(struct wl1271 *wl)
{
- wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
- sizeof(*wl->fw_status_2) +
- wl->fw_status_priv_len, GFP_KERNEL);
- if (!wl->fw_status_1)
- return -ENOMEM;
+ wl->raw_fw_status = kzalloc(wl->fw_status_len, GFP_KERNEL);
+ if (!wl->raw_fw_status)
+ goto err;
- wl->fw_status_2 = (struct wl_fw_status_2 *)
- (((u8 *) wl->fw_status_1) +
- WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
+ wl->fw_status = kzalloc(sizeof(*wl->fw_status), GFP_KERNEL);
+ if (!wl->fw_status)
+ goto err;
wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
- if (!wl->tx_res_if) {
- kfree(wl->fw_status_1);
- return -ENOMEM;
- }
+ if (!wl->tx_res_if)
+ goto err;
return 0;
+err:
+ kfree(wl->fw_status);
+ kfree(wl->raw_fw_status);
+ return -ENOMEM;
}
static int wl12xx_set_power_on(struct wl1271 *wl)
@@ -1767,6 +1764,12 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
flush_work(&wl->tx_work);
flush_delayed_work(&wl->elp_work);
+ /*
+ * Cancel the watchdog even if above tx_flush failed. We will detect
+ * it on resume anyway.
+ */
+ cancel_delayed_work(&wl->tx_watchdog_work);
+
return 0;
}
@@ -1824,6 +1827,13 @@ static int wl1271_op_resume(struct ieee80211_hw *hw)
out:
wl->wow_enabled = false;
+
+ /*
+ * Set a flag to re-init the watchdog on the first Tx after resume.
+ * That way we avoid possible conditions where Tx-complete interrupts
+ * fail to arrive and we perform a spurious recovery.
+ */
+ set_bit(WL1271_FLAG_REINIT_TX_WDOG, &wl->flags);
mutex_unlock(&wl->mutex);
return 0;
@@ -1914,6 +1924,7 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
memset(wl->links_map, 0, sizeof(wl->links_map));
memset(wl->roc_map, 0, sizeof(wl->roc_map));
memset(wl->session_ids, 0, sizeof(wl->session_ids));
+ memset(wl->rx_filter_enabled, 0, sizeof(wl->rx_filter_enabled));
wl->active_sta_count = 0;
wl->active_link_count = 0;
@@ -1938,9 +1949,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
wl1271_debugfs_reset(wl);
- kfree(wl->fw_status_1);
- wl->fw_status_1 = NULL;
- wl->fw_status_2 = NULL;
+ kfree(wl->raw_fw_status);
+ wl->raw_fw_status = NULL;
+ kfree(wl->fw_status);
+ wl->fw_status = NULL;
kfree(wl->tx_res_if);
wl->tx_res_if = NULL;
kfree(wl->target_mem_map);
@@ -2571,10 +2583,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
ieee80211_scan_completed(wl->hw, true);
}
- if (wl->sched_vif == wlvif) {
- ieee80211_sched_scan_stopped(wl->hw);
+ if (wl->sched_vif == wlvif)
wl->sched_vif = NULL;
- }
if (wl->roc_vif == vif) {
wl->roc_vif = NULL;
@@ -2931,6 +2941,11 @@ static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
if (ret < 0)
return ret;
+
+ /* disable beacon filtering */
+ ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
+ if (ret < 0)
+ return ret;
}
if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
@@ -3463,6 +3478,10 @@ static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw,
wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d",
key_idx);
+ /* we don't handle unsetting of default key */
+ if (key_idx == -1)
+ return;
+
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON)) {
@@ -3649,8 +3668,8 @@ out:
return ret;
}
-static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+static int wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
@@ -3672,6 +3691,8 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
+
+ return 0;
}
static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
@@ -4298,6 +4319,13 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
}
}
+ if ((changed & BSS_CHANGED_BEACON_INFO) && bss_conf->dtim_period) {
+ /* enable beacon filtering */
+ ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
+ if (ret < 0)
+ goto out;
+ }
+
ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
if (ret < 0)
goto out;
@@ -4651,7 +4679,7 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
int ret;
- if (wl->active_sta_count >= AP_MAX_STATIONS) {
+ if (wl->active_sta_count >= wl->max_ap_stations) {
wl1271_warning("could not allocate HLID - too much stations");
return -EBUSY;
}
@@ -4754,7 +4782,7 @@ static int wl12xx_sta_remove(struct wl1271 *wl,
if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
return -EINVAL;
- ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
+ ret = wl12xx_cmd_remove_peer(wl, wlvif, wl_sta->hlid);
if (ret < 0)
return ret;
@@ -5679,28 +5707,6 @@ static void wl1271_unregister_hw(struct wl1271 *wl)
}
-static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
- {
- .max = 3,
- .types = BIT(NL80211_IFTYPE_STATION),
- },
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_P2P_CLIENT),
- },
-};
-
-static struct ieee80211_iface_combination
-wlcore_iface_combinations[] = {
- {
- .max_interfaces = 3,
- .limits = wlcore_iface_limits,
- .n_limits = ARRAY_SIZE(wlcore_iface_limits),
- },
-};
-
static int wl1271_init_ieee80211(struct wl1271 *wl)
{
int i;
@@ -5733,7 +5739,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
IEEE80211_HW_AP_LINK_PS |
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
- IEEE80211_HW_QUEUE_CONTROL;
+ IEEE80211_HW_QUEUE_CONTROL |
+ IEEE80211_HW_CHANCTX_STA_CSA;
wl->hw->wiphy->cipher_suites = cipher_suites;
wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
@@ -5821,10 +5828,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
/* allowed interface combinations */
- wlcore_iface_combinations[0].num_different_channels = wl->num_channels;
- wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
- wl->hw->wiphy->n_iface_combinations =
- ARRAY_SIZE(wlcore_iface_combinations);
+ wl->hw->wiphy->iface_combinations = wl->iface_combinations;
+ wl->hw->wiphy->n_iface_combinations = wl->n_iface_combinations;
SET_IEEE80211_DEV(wl->hw, wl->dev);
@@ -5844,8 +5849,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
int i, j, ret;
unsigned int order;
- BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS);
-
hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
if (!hw) {
wl1271_error("could not alloc ieee80211_hw");
@@ -5867,8 +5870,12 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
wl->hw = hw;
+ /*
+ * wl->num_links is not configured yet, so just use WLCORE_MAX_LINKS.
+ * we don't allocate any additional resource here, so that's fine.
+ */
for (i = 0; i < NUM_TX_QUEUES; i++)
- for (j = 0; j < WL12XX_MAX_LINKS; j++)
+ for (j = 0; j < WLCORE_MAX_LINKS; j++)
skb_queue_head_init(&wl->links[j].tx_queue[i]);
skb_queue_head_init(&wl->deferred_rx_queue);
@@ -6011,7 +6018,8 @@ int wlcore_free_hw(struct wl1271 *wl)
kfree(wl->nvs);
wl->nvs = NULL;
- kfree(wl->fw_status_1);
+ kfree(wl->raw_fw_status);
+ kfree(wl->fw_status);
kfree(wl->tx_res_if);
destroy_workqueue(wl->freezable_wq);
diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c
index 26bfc365ba70..b52516eed7b2 100644
--- a/drivers/net/wireless/ti/wlcore/ps.c
+++ b/drivers/net/wireless/ti/wlcore/ps.c
@@ -280,7 +280,11 @@ void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_sta *sta;
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
- if (test_bit(hlid, &wl->ap_ps_map))
+ if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS))
+ return;
+
+ if (!test_bit(hlid, wlvif->ap.sta_hlid_map) ||
+ test_bit(hlid, &wl->ap_ps_map))
return;
wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d pkts %d "
diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c
index 6791a1a6afba..e125974285cc 100644
--- a/drivers/net/wireless/ti/wlcore/rx.c
+++ b/drivers/net/wireless/ti/wlcore/rx.c
@@ -203,9 +203,9 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
return is_data;
}
-int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status)
+int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status)
{
- unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
+ unsigned long active_hlids[BITS_TO_LONGS(WLCORE_MAX_LINKS)] = {0};
u32 buf_size;
u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc;
u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc;
@@ -263,12 +263,12 @@ int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status)
wl->aggr_buf + pkt_offset,
pkt_len, rx_align,
&hlid) == 1) {
- if (hlid < WL12XX_MAX_LINKS)
+ if (hlid < wl->num_links)
__set_bit(hlid, active_hlids);
else
WARN(1,
- "hlid exceeded WL12XX_MAX_LINKS "
- "(%d)\n", hlid);
+ "hlid (%d) exceeded MAX_LINKS\n",
+ hlid);
}
wl->rx_counter++;
@@ -302,7 +302,7 @@ int wl1271_rx_filter_enable(struct wl1271 *wl,
{
int ret;
- if (wl->rx_filter_enabled[index] == enable) {
+ if (!!test_bit(index, wl->rx_filter_enabled) == enable) {
wl1271_warning("Request to enable an already "
"enabled rx filter %d", index);
return 0;
@@ -316,7 +316,10 @@ int wl1271_rx_filter_enable(struct wl1271 *wl,
return ret;
}
- wl->rx_filter_enabled[index] = enable;
+ if (enable)
+ __set_bit(index, wl->rx_filter_enabled);
+ else
+ __clear_bit(index, wl->rx_filter_enabled);
return 0;
}
@@ -326,7 +329,7 @@ int wl1271_rx_filter_clear_all(struct wl1271 *wl)
int i, ret = 0;
for (i = 0; i < WL1271_MAX_RX_FILTERS; i++) {
- if (!wl->rx_filter_enabled[i])
+ if (!test_bit(i, wl->rx_filter_enabled))
continue;
ret = wl1271_rx_filter_enable(wl, i, 0, NULL);
if (ret)
diff --git a/drivers/net/wireless/ti/wlcore/rx.h b/drivers/net/wireless/ti/wlcore/rx.h
index 3363f60fb7da..a3b1618db27c 100644
--- a/drivers/net/wireless/ti/wlcore/rx.h
+++ b/drivers/net/wireless/ti/wlcore/rx.h
@@ -142,7 +142,7 @@ struct wl1271_rx_descriptor {
u8 reserved;
} __packed;
-int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status);
+int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status);
u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
int wl1271_rx_filter_enable(struct wl1271 *wl,
int index, bool enable,
diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
index b2c018dccf18..dbe826dd7c23 100644
--- a/drivers/net/wireless/ti/wlcore/spi.c
+++ b/drivers/net/wireless/ti/wlcore/spi.c
@@ -211,7 +211,7 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
u32 chunk_len;
while (len > 0) {
- chunk_len = min((size_t)WSPI_MAX_CHUNK_SIZE, len);
+ chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
cmd = &wl->buffer_cmd;
busy_buf = wl->buffer_busyword;
@@ -285,7 +285,7 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
cmd = &commands[0];
i = 0;
while (len > 0) {
- chunk_len = min((size_t)WSPI_MAX_CHUNK_SIZE, len);
+ chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
*cmd = 0;
*cmd |= WSPI_CMD_WRITE;
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c
index 8e583497940d..24dd288d6809 100644
--- a/drivers/net/wireless/ti/wlcore/sysfs.c
+++ b/drivers/net/wireless/ti/wlcore/sysfs.c
@@ -152,7 +152,7 @@ static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
}
/* Seeking is not supported - old logs are not kept. Disregard pos. */
- len = min(count, (size_t)wl->fwlog_size);
+ len = min_t(size_t, count, wl->fwlog_size);
wl->fwlog_size -= len;
memcpy(buffer, wl->fwlog, len);
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index 87cd707affa2..40b43115f835 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -101,7 +101,7 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
* authentication response. this way it won't get de-authed by FW
* when transmitting too soon.
*/
- wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
+ wl1271_acx_set_inconnection_sta(wl, wlvif, hdr->addr1);
/*
* ROC for 1 second on the AP channel for completing the connection.
@@ -134,12 +134,12 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
* into high-level PS and clean out its TX queues.
* Make an exception if this is the only connected link. In this
* case FW-memory congestion is less of a problem.
- * Note that a single connected STA means 3 active links, since we must
- * account for the global and broadcast AP links. The "fw_ps" check
- * assures us the third link is a STA connected to the AP. Otherwise
- * the FW would not set the PSM bit.
+ * Note that a single connected STA means 2*ap_count + 1 active links,
+ * since we must account for the global and broadcast AP links
+ * for each AP. The "fw_ps" check assures us the other link is a STA
+ * connected to the AP. Otherwise the FW would not set the PSM bit.
*/
- if (wl->active_link_count > 3 && fw_ps &&
+ if (wl->active_link_count > (wl->ap_count*2 + 1) && fw_ps &&
tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
wl12xx_ps_link_start(wl, wlvif, hlid, true);
}
@@ -234,8 +234,13 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
wl->tx_blocks_available -= total_blocks;
wl->tx_allocated_blocks += total_blocks;
- /* If the FW was empty before, arm the Tx watchdog */
- if (wl->tx_allocated_blocks == total_blocks)
+ /*
+ * If the FW was empty before, arm the Tx watchdog. Also do
+ * this on the first Tx after resume, as we always cancel the
+ * watchdog on suspend.
+ */
+ if (wl->tx_allocated_blocks == total_blocks ||
+ test_and_clear_bit(WL1271_FLAG_REINIT_TX_WDOG, &wl->flags))
wl12xx_rearm_tx_watchdog_locked(wl);
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
@@ -357,6 +362,10 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
ieee80211_has_protected(frame_control))
tx_attr |= TX_HW_ATTR_HOST_ENCRYPT;
+ /* send EAPOL frames as voice */
+ if (control->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)
+ tx_attr |= TX_HW_ATTR_EAPOL_FRAME;
+
desc->tx_attr = cpu_to_le16(tx_attr);
wlcore_hw_set_tx_desc_csum(wl, desc, skb);
@@ -560,11 +569,11 @@ static struct sk_buff *wlcore_vif_dequeue_high_prio(struct wl1271 *wl,
int i, h, start_hlid;
/* start from the link after the last one */
- start_hlid = (wlvif->last_tx_hlid + 1) % WL12XX_MAX_LINKS;
+ start_hlid = (wlvif->last_tx_hlid + 1) % wl->num_links;
/* dequeue according to AC, round robin on each link */
- for (i = 0; i < WL12XX_MAX_LINKS; i++) {
- h = (start_hlid + i) % WL12XX_MAX_LINKS;
+ for (i = 0; i < wl->num_links; i++) {
+ h = (start_hlid + i) % wl->num_links;
/* only consider connected stations */
if (!test_bit(h, wlvif->links_map))
@@ -688,8 +697,8 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
skb_queue_head(&wl->links[hlid].tx_queue[q], skb);
/* make sure we dequeue the same packet next time */
- wlvif->last_tx_hlid = (hlid + WL12XX_MAX_LINKS - 1) %
- WL12XX_MAX_LINKS;
+ wlvif->last_tx_hlid = (hlid + wl->num_links - 1) %
+ wl->num_links;
}
spin_lock_irqsave(&wl->wl_lock, flags);
@@ -722,7 +731,7 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids)
timeout = wl->conf.rx_streaming.duration;
wl12xx_for_each_wlvif_sta(wl, wlvif) {
bool found = false;
- for_each_set_bit(hlid, active_hlids, WL12XX_MAX_LINKS) {
+ for_each_set_bit(hlid, active_hlids, wl->num_links) {
if (test_bit(hlid, wlvif->links_map)) {
found = true;
break;
@@ -759,7 +768,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
struct wl1271_tx_hw_descr *desc;
u32 buf_offset = 0, last_len = 0;
bool sent_packets = false;
- unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
+ unsigned long active_hlids[BITS_TO_LONGS(WLCORE_MAX_LINKS)] = {0};
int ret = 0;
int bus_ret = 0;
u8 hlid;
@@ -1061,7 +1070,7 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
int i;
/* TX failure */
- for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) {
+ for_each_set_bit(i, wlvif->links_map, wl->num_links) {
if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
i != wlvif->ap.bcast_hlid && i != wlvif->ap.global_hlid) {
/* this calls wl12xx_free_link */
@@ -1085,7 +1094,7 @@ void wl12xx_tx_reset(struct wl1271 *wl)
/* only reset the queues if something bad happened */
if (wl1271_tx_total_queue_count(wl) != 0) {
- for (i = 0; i < WL12XX_MAX_LINKS; i++)
+ for (i = 0; i < wl->num_links; i++)
wl1271_tx_reset_link_queues(wl, i);
for (i = 0; i < NUM_TX_QUEUES; i++)
@@ -1178,7 +1187,7 @@ void wl1271_tx_flush(struct wl1271 *wl)
WL1271_TX_FLUSH_TIMEOUT / 1000);
/* forcibly flush all Tx buffers on our queues */
- for (i = 0; i < WL12XX_MAX_LINKS; i++)
+ for (i = 0; i < wl->num_links; i++)
wl1271_tx_reset_link_queues(wl, i);
out_wake:
diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h
index 35489c300da1..79cb3ff8b71f 100644
--- a/drivers/net/wireless/ti/wlcore/tx.h
+++ b/drivers/net/wireless/ti/wlcore/tx.h
@@ -37,6 +37,7 @@
#define TX_HW_ATTR_TX_CMPLT_REQ BIT(12)
#define TX_HW_ATTR_TX_DUMMY_REQ BIT(13)
#define TX_HW_ATTR_HOST_ENCRYPT BIT(14)
+#define TX_HW_ATTR_EAPOL_FRAME BIT(15)
#define TX_HW_ATTR_OFST_SAVE_RETRIES 0
#define TX_HW_ATTR_OFST_HEADER_PAD 1
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 06efc12a39e5..95a54504f0cc 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -73,6 +73,8 @@ struct wlcore_ops {
void (*tx_immediate_compl)(struct wl1271 *wl);
int (*hw_init)(struct wl1271 *wl);
int (*init_vif)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+ void (*convert_fw_status)(struct wl1271 *wl, void *raw_fw_status,
+ struct wl_fw_status *fw_status);
u32 (*sta_get_ap_rate_mask)(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int (*get_pg_ver)(struct wl1271 *wl, s8 *ver);
@@ -220,7 +222,7 @@ struct wl1271 {
int channel;
u8 system_hlid;
- unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
+ unsigned long links_map[BITS_TO_LONGS(WLCORE_MAX_LINKS)];
unsigned long roles_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
unsigned long roc_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
unsigned long rate_policies_map[
@@ -228,7 +230,7 @@ struct wl1271 {
unsigned long klv_templates_map[
BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)];
- u8 session_ids[WL12XX_MAX_LINKS];
+ u8 session_ids[WLCORE_MAX_LINKS];
struct list_head wlvif_list;
@@ -346,8 +348,8 @@ struct wl1271 {
u32 buffer_cmd;
u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
- struct wl_fw_status_1 *fw_status_1;
- struct wl_fw_status_2 *fw_status_2;
+ void *raw_fw_status;
+ struct wl_fw_status *fw_status;
struct wl1271_tx_hw_res_if *tx_res_if;
/* Current chipset configuration */
@@ -376,7 +378,7 @@ struct wl1271 {
* AP-mode - links indexed by HLID. The global and broadcast links
* are always active.
*/
- struct wl1271_link links[WL12XX_MAX_LINKS];
+ struct wl1271_link links[WLCORE_MAX_LINKS];
/* number of currently active links */
int active_link_count;
@@ -405,6 +407,9 @@ struct wl1271 {
/* AP-mode - number of currently connected stations */
int active_sta_count;
+ /* Flag determining whether AP should broadcast OFDM-only rates */
+ bool ofdm_only_ap;
+
/* last wlvif we transmitted from */
struct wl12xx_vif *last_wlvif;
@@ -434,6 +439,10 @@ struct wl1271 {
u32 num_tx_desc;
/* number of RX descriptors the HW supports. */
u32 num_rx_desc;
+ /* number of links the HW supports */
+ u8 num_links;
+ /* max stations a single AP can support */
+ u8 max_ap_stations;
/* translate HW Tx rates to standard rate-indices */
const u8 **band_rate_to_idx;
@@ -448,10 +457,11 @@ struct wl1271 {
struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS];
/* size of the private FW status data */
+ size_t fw_status_len;
size_t fw_status_priv_len;
/* RX Data filter rule state - enabled/disabled */
- bool rx_filter_enabled[WL1271_MAX_RX_FILTERS];
+ unsigned long rx_filter_enabled[BITS_TO_LONGS(WL1271_MAX_RX_FILTERS)];
/* size of the private static data */
size_t static_data_priv_len;
@@ -476,8 +486,9 @@ struct wl1271 {
struct completion nvs_loading_complete;
- /* number of concurrent channels the HW supports */
- u32 num_channels;
+ /* interface combinations supported by the hw */
+ const struct ieee80211_iface_combination *iface_combinations;
+ u8 n_iface_combinations;
};
int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h
index ce7261ce8b59..756e890bc5ee 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h
@@ -58,10 +58,15 @@
#define WL1271_DEFAULT_DTIM_PERIOD 1
#define WL12XX_MAX_ROLES 4
-#define WL12XX_MAX_LINKS 12
#define WL12XX_INVALID_ROLE_ID 0xff
#define WL12XX_INVALID_LINK_ID 0xff
+/*
+ * max number of links allowed by all HWs.
+ * this is NOT the actual max links supported by the current hw.
+ */
+#define WLCORE_MAX_LINKS 16
+
/* the driver supports the 2.4Ghz and 5Ghz bands */
#define WLCORE_NUM_BANDS 2
@@ -118,72 +123,58 @@ struct wl1271_chip {
#define NUM_TX_QUEUES 4
-#define AP_MAX_STATIONS 8
-
-struct wl_fw_packet_counters {
- /* Cumulative counter of released packets per AC */
- u8 tx_released_pkts[NUM_TX_QUEUES];
-
- /* Cumulative counter of freed packets per HLID */
- u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS];
-
- /* Cumulative counter of released Voice memory blocks */
- u8 tx_voice_released_blks;
-
- /* Tx rate of the last transmitted packet */
- u8 tx_last_rate;
-
- u8 padding[2];
-} __packed;
-
-/* FW status registers */
-struct wl_fw_status_1 {
- __le32 intr;
+struct wl_fw_status {
+ u32 intr;
u8 fw_rx_counter;
u8 drv_rx_counter;
- u8 reserved;
u8 tx_results_counter;
- __le32 rx_pkt_descs[0];
-} __packed;
-
-/*
- * Each HW arch has a different number of Rx descriptors.
- * The length of the status depends on it, since it holds an array
- * of descriptors.
- */
-#define WLCORE_FW_STATUS_1_LEN(num_rx_desc) \
- (sizeof(struct wl_fw_status_1) + \
- (sizeof(((struct wl_fw_status_1 *)0)->rx_pkt_descs[0])) * \
- num_rx_desc)
+ __le32 *rx_pkt_descs;
-struct wl_fw_status_2 {
- __le32 fw_localtime;
+ u32 fw_localtime;
/*
* A bitmap (where each bit represents a single HLID)
* to indicate if the station is in PS mode.
*/
- __le32 link_ps_bitmap;
+ u32 link_ps_bitmap;
/*
* A bitmap (where each bit represents a single HLID) to indicate
* if the station is in Fast mode
*/
- __le32 link_fast_bitmap;
+ u32 link_fast_bitmap;
/* Cumulative counter of total released mem blocks since FW-reset */
- __le32 total_released_blks;
+ u32 total_released_blks;
/* Size (in Memory Blocks) of TX pool */
- __le32 tx_total;
+ u32 tx_total;
+
+ struct {
+ /*
+ * Cumulative counter of released packets per AC
+ * (length of the array is NUM_TX_QUEUES)
+ */
+ u8 *tx_released_pkts;
- struct wl_fw_packet_counters counters;
+ /*
+ * Cumulative counter of freed packets per HLID
+ * (length of the array is wl->num_links)
+ */
+ u8 *tx_lnk_free_pkts;
+
+ /* Cumulative counter of released Voice memory blocks */
+ u8 tx_voice_released_blks;
- __le32 log_start_addr;
+ /* Tx rate of the last transmitted packet */
+ u8 tx_last_rate;
+ } counters;
+
+ u32 log_start_addr;
/* Private status to be used by the lower drivers */
- u8 priv[0];
-} __packed;
+ void *priv;
+};
#define WL1271_MAX_CHANNELS 64
struct wl1271_scan {
@@ -240,6 +231,7 @@ enum wl12xx_flags {
WL1271_FLAG_VIF_CHANGE_IN_PROGRESS,
WL1271_FLAG_INTENDED_FW_RECOVERY,
WL1271_FLAG_IO_FAILED,
+ WL1271_FLAG_REINIT_TX_WDOG,
};
enum wl12xx_vif_flags {
@@ -368,7 +360,7 @@ struct wl12xx_vif {
/* HLIDs bitmap of associated stations */
unsigned long sta_hlid_map[BITS_TO_LONGS(
- WL12XX_MAX_LINKS)];
+ WLCORE_MAX_LINKS)];
/* recoreded keys - set here before AP startup */
struct wl1271_ap_key *recorded_keys[MAX_NUM_KEYS];
@@ -385,7 +377,7 @@ struct wl12xx_vif {
/* counters of packets per AC, across all links in the vif */
int tx_queue_count[NUM_TX_QUEUES];
- unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
+ unsigned long links_map[BITS_TO_LONGS(WLCORE_MAX_LINKS)];
u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
u8 ssid_len;
diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c
index d24d4a958c67..d5c371d77ddf 100644
--- a/drivers/net/wireless/wl3501_cs.c
+++ b/drivers/net/wireless/wl3501_cs.c
@@ -42,8 +42,7 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/wireless.h>
-#include <linux/ieee80211.h>
-#include <linux/etherdevice.h>
+#include <net/cfg80211.h>
#include <net/iw_handler.h>
@@ -1454,7 +1453,8 @@ static int wl3501_get_freq(struct net_device *dev, struct iw_request_info *info,
{
struct wl3501_card *this = netdev_priv(dev);
- wrqu->freq.m = ieee80211_dsss_chan_to_freq(this->chan) * 100000;
+ wrqu->freq.m = 100000 *
+ ieee80211_channel_to_frequency(this->chan, IEEE80211_BAND_2GHZ);
wrqu->freq.e = 1;
return 0;
}
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c
index d39c4178c33a..6f5c793a7855 100644
--- a/drivers/net/wireless/zd1201.c
+++ b/drivers/net/wireless/zd1201.c
@@ -18,7 +18,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/wireless.h>
-#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
#include <net/iw_handler.h>
#include <linux/string.h>
#include <linux/if_arp.h>
@@ -914,11 +914,8 @@ static int zd1201_set_freq(struct net_device *dev,
if (freq->e == 0)
channel = freq->m;
- else {
- channel = ieee80211_freq_to_dsss_chan(freq->m);
- if (channel < 0)
- channel = 0;
- }
+ else
+ channel = ieee80211_frequency_to_channel(freq->m);
err = zd1201_setconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, channel);
if (err)
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
index ae413a2cbee7..89d1d0556b6e 100644
--- a/drivers/net/xen-netback/common.h
+++ b/drivers/net/xen-netback/common.h
@@ -48,37 +48,19 @@
typedef unsigned int pending_ring_idx_t;
#define INVALID_PENDING_RING_IDX (~0U)
-/* For the head field in pending_tx_info: it is used to indicate
- * whether this tx info is the head of one or more coalesced requests.
- *
- * When head != INVALID_PENDING_RING_IDX, it means the start of a new
- * tx requests queue and the end of previous queue.
- *
- * An example sequence of head fields (I = INVALID_PENDING_RING_IDX):
- *
- * ...|0 I I I|5 I|9 I I I|...
- * -->|<-INUSE----------------
- *
- * After consuming the first slot(s) we have:
- *
- * ...|V V V V|5 I|9 I I I|...
- * -----FREE->|<-INUSE--------
- *
- * where V stands for "valid pending ring index". Any number other
- * than INVALID_PENDING_RING_IDX is OK. These entries are considered
- * free and can contain any number other than
- * INVALID_PENDING_RING_IDX. In practice we use 0.
- *
- * The in use non-INVALID_PENDING_RING_IDX (say 0, 5 and 9 in the
- * above example) number is the index into pending_tx_info and
- * mmap_pages arrays.
- */
struct pending_tx_info {
- struct xen_netif_tx_request req; /* coalesced tx request */
- pending_ring_idx_t head; /* head != INVALID_PENDING_RING_IDX
- * if it is head of one or more tx
- * reqs
- */
+ struct xen_netif_tx_request req; /* tx request */
+ /* Callback data for released SKBs. The callback is always
+ * xenvif_zerocopy_callback, desc contains the pending_idx, which is
+ * also an index in pending_tx_info array. It is initialized in
+ * xenvif_alloc and it never changes.
+ * skb_shinfo(skb)->destructor_arg points to the first mapped slot's
+ * callback_struct in this array of struct pending_tx_info's, then ctx
+ * to the next, or NULL if there is no more slot for this skb.
+ * ubuf_to_vif is a helper which finds the struct xenvif from a pointer
+ * to this field.
+ */
+ struct ubuf_info callback_struct;
};
#define XEN_NETIF_TX_RING_SIZE __CONST_RING_SIZE(xen_netif_tx, PAGE_SIZE)
@@ -99,7 +81,7 @@ struct xenvif_rx_meta {
#define MAX_BUFFER_OFFSET PAGE_SIZE
-#define MAX_PENDING_REQS 256
+#define MAX_PENDING_REQS XEN_NETIF_TX_RING_SIZE
/* It's possible for an skb to have a maximal number of frags
* but still be less than MAX_BUFFER_OFFSET in size. Thus the
@@ -108,11 +90,25 @@ struct xenvif_rx_meta {
*/
#define MAX_GRANT_COPY_OPS (MAX_SKB_FRAGS * XEN_NETIF_RX_RING_SIZE)
+#define NETBACK_INVALID_HANDLE -1
+
+/* To avoid confusion, we define XEN_NETBK_LEGACY_SLOTS_MAX indicating
+ * the maximum slots a valid packet can use. Now this value is defined
+ * to be XEN_NETIF_NR_SLOTS_MIN, which is supposed to be supported by
+ * all backend.
+ */
+#define XEN_NETBK_LEGACY_SLOTS_MAX XEN_NETIF_NR_SLOTS_MIN
+
struct xenvif {
/* Unique identifier for this interface. */
domid_t domid;
unsigned int handle;
+ /* Is this interface disabled? True when backend discovers
+ * frontend is rogue.
+ */
+ bool disabled;
+
/* Use NAPI for guest TX */
struct napi_struct napi;
/* When feature-split-event-channels = 0, tx_irq = rx_irq. */
@@ -126,13 +122,26 @@ struct xenvif {
pending_ring_idx_t pending_cons;
u16 pending_ring[MAX_PENDING_REQS];
struct pending_tx_info pending_tx_info[MAX_PENDING_REQS];
-
- /* Coalescing tx requests before copying makes number of grant
- * copy ops greater or equal to number of slots required. In
- * worst case a tx request consumes 2 gnttab_copy.
+ grant_handle_t grant_tx_handle[MAX_PENDING_REQS];
+
+ struct gnttab_map_grant_ref tx_map_ops[MAX_PENDING_REQS];
+ struct gnttab_unmap_grant_ref tx_unmap_ops[MAX_PENDING_REQS];
+ /* passed to gnttab_[un]map_refs with pages under (un)mapping */
+ struct page *pages_to_map[MAX_PENDING_REQS];
+ struct page *pages_to_unmap[MAX_PENDING_REQS];
+
+ /* This prevents zerocopy callbacks to race over dealloc_ring */
+ spinlock_t callback_lock;
+ /* This prevents dealloc thread and NAPI instance to race over response
+ * creation and pending_ring in xenvif_idx_release. In xenvif_tx_err
+ * it only protect response creation
*/
- struct gnttab_copy tx_copy_ops[2*MAX_PENDING_REQS];
-
+ spinlock_t response_lock;
+ pending_ring_idx_t dealloc_prod;
+ pending_ring_idx_t dealloc_cons;
+ u16 dealloc_ring[MAX_PENDING_REQS];
+ struct task_struct *dealloc_task;
+ wait_queue_head_t dealloc_wq;
/* Use kthread for guest RX */
struct task_struct *task;
@@ -144,6 +153,9 @@ struct xenvif {
struct xen_netif_rx_back_ring rx;
struct sk_buff_head rx_queue;
RING_IDX rx_last_skb_slots;
+ bool rx_queue_purge;
+
+ struct timer_list wake_queue;
/* This array is allocated seperately as it is large */
struct gnttab_copy *grant_copy_op;
@@ -175,6 +187,10 @@ struct xenvif {
/* Statistics */
unsigned long rx_gso_checksum_fixup;
+ unsigned long tx_zerocopy_sent;
+ unsigned long tx_zerocopy_success;
+ unsigned long tx_zerocopy_fail;
+ unsigned long tx_frag_overflow;
/* Miscellaneous private stuff. */
struct net_device *dev;
@@ -216,9 +232,11 @@ void xenvif_carrier_off(struct xenvif *vif);
int xenvif_tx_action(struct xenvif *vif, int budget);
-int xenvif_kthread(void *data);
+int xenvif_kthread_guest_rx(void *data);
void xenvif_kick_thread(struct xenvif *vif);
+int xenvif_dealloc_kthread(void *data);
+
/* Determine whether the needed number of slots (req) are available,
* and set req_event if not.
*/
@@ -226,6 +244,24 @@ bool xenvif_rx_ring_slots_available(struct xenvif *vif, int needed);
void xenvif_stop_queue(struct xenvif *vif);
+/* Callback from stack when TX packet can be released */
+void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success);
+
+/* Unmap a pending page and release it back to the guest */
+void xenvif_idx_unmap(struct xenvif *vif, u16 pending_idx);
+
+static inline pending_ring_idx_t nr_pending_reqs(struct xenvif *vif)
+{
+ return MAX_PENDING_REQS -
+ vif->pending_prod + vif->pending_cons;
+}
+
+/* Callback from stack when TX packet can be released */
+void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success);
+
extern bool separate_tx_rx_irq;
+extern unsigned int rx_drain_timeout_msecs;
+extern unsigned int rx_drain_timeout_jiffies;
+
#endif /* __XEN_NETBACK__COMMON_H__ */
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
index 301cc037fda8..ef05c5c49d41 100644
--- a/drivers/net/xen-netback/interface.c
+++ b/drivers/net/xen-netback/interface.c
@@ -38,6 +38,7 @@
#include <xen/events.h>
#include <asm/xen/hypercall.h>
+#include <xen/balloon.h>
#define XENVIF_QUEUE_LENGTH 32
#define XENVIF_NAPI_WEIGHT 64
@@ -62,6 +63,15 @@ static int xenvif_poll(struct napi_struct *napi, int budget)
struct xenvif *vif = container_of(napi, struct xenvif, napi);
int work_done;
+ /* This vif is rogue, we pretend we've there is nothing to do
+ * for this vif to deschedule it from NAPI. But this interface
+ * will be turned off in thread context later.
+ */
+ if (unlikely(vif->disabled)) {
+ napi_complete(napi);
+ return 0;
+ }
+
work_done = xenvif_tx_action(vif, budget);
if (work_done < budget) {
@@ -113,6 +123,18 @@ static irqreturn_t xenvif_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static void xenvif_wake_queue(unsigned long data)
+{
+ struct xenvif *vif = (struct xenvif *)data;
+
+ if (netif_queue_stopped(vif->dev)) {
+ netdev_err(vif->dev, "draining TX queue\n");
+ vif->rx_queue_purge = true;
+ xenvif_kick_thread(vif);
+ netif_wake_queue(vif->dev);
+ }
+}
+
static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct xenvif *vif = netdev_priv(dev);
@@ -121,7 +143,9 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
BUG_ON(skb->dev != dev);
/* Drop the packet if vif is not ready */
- if (vif->task == NULL || !xenvif_schedulable(vif))
+ if (vif->task == NULL ||
+ vif->dealloc_task == NULL ||
+ !xenvif_schedulable(vif))
goto drop;
/* At best we'll need one slot for the header and one for each
@@ -139,8 +163,13 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
* then turn off the queue to give the ring a chance to
* drain.
*/
- if (!xenvif_rx_ring_slots_available(vif, min_slots_needed))
+ if (!xenvif_rx_ring_slots_available(vif, min_slots_needed)) {
+ vif->wake_queue.function = xenvif_wake_queue;
+ vif->wake_queue.data = (unsigned long)vif;
xenvif_stop_queue(vif);
+ mod_timer(&vif->wake_queue,
+ jiffies + rx_drain_timeout_jiffies);
+ }
skb_queue_tail(&vif->rx_queue, skb);
xenvif_kick_thread(vif);
@@ -233,6 +262,28 @@ static const struct xenvif_stat {
"rx_gso_checksum_fixup",
offsetof(struct xenvif, rx_gso_checksum_fixup)
},
+ /* If (sent != success + fail), there are probably packets never
+ * freed up properly!
+ */
+ {
+ "tx_zerocopy_sent",
+ offsetof(struct xenvif, tx_zerocopy_sent),
+ },
+ {
+ "tx_zerocopy_success",
+ offsetof(struct xenvif, tx_zerocopy_success),
+ },
+ {
+ "tx_zerocopy_fail",
+ offsetof(struct xenvif, tx_zerocopy_fail)
+ },
+ /* Number of packets exceeding MAX_SKB_FRAG slots. You should use
+ * a guest with the same MAX_SKB_FRAG
+ */
+ {
+ "tx_frag_overflow",
+ offsetof(struct xenvif, tx_frag_overflow)
+ },
};
static int xenvif_get_sset_count(struct net_device *dev, int string_set)
@@ -321,11 +372,15 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
vif->ip_csum = 1;
vif->dev = dev;
+ vif->disabled = false;
+
vif->credit_bytes = vif->remaining_credit = ~0UL;
vif->credit_usec = 0UL;
init_timer(&vif->credit_timeout);
vif->credit_window_start = get_jiffies_64();
+ init_timer(&vif->wake_queue);
+
dev->netdev_ops = &xenvif_netdev_ops;
dev->hw_features = NETIF_F_SG |
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
@@ -342,8 +397,26 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
vif->pending_prod = MAX_PENDING_REQS;
for (i = 0; i < MAX_PENDING_REQS; i++)
vif->pending_ring[i] = i;
- for (i = 0; i < MAX_PENDING_REQS; i++)
- vif->mmap_pages[i] = NULL;
+ spin_lock_init(&vif->callback_lock);
+ spin_lock_init(&vif->response_lock);
+ /* If ballooning is disabled, this will consume real memory, so you
+ * better enable it. The long term solution would be to use just a
+ * bunch of valid page descriptors, without dependency on ballooning
+ */
+ err = alloc_xenballooned_pages(MAX_PENDING_REQS,
+ vif->mmap_pages,
+ false);
+ if (err) {
+ netdev_err(dev, "Could not reserve mmap_pages\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ for (i = 0; i < MAX_PENDING_REQS; i++) {
+ vif->pending_tx_info[i].callback_struct = (struct ubuf_info)
+ { .callback = xenvif_zerocopy_callback,
+ .ctx = NULL,
+ .desc = i };
+ vif->grant_tx_handle[i] = NETBACK_INVALID_HANDLE;
+ }
/*
* Initialise a dummy MAC address. We choose the numerically
@@ -381,12 +454,14 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
BUG_ON(vif->tx_irq);
BUG_ON(vif->task);
+ BUG_ON(vif->dealloc_task);
err = xenvif_map_frontend_rings(vif, tx_ring_ref, rx_ring_ref);
if (err < 0)
goto err;
init_waitqueue_head(&vif->wq);
+ init_waitqueue_head(&vif->dealloc_wq);
if (tx_evtchn == rx_evtchn) {
/* feature-split-event-channels == 0 */
@@ -420,8 +495,8 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
disable_irq(vif->rx_irq);
}
- task = kthread_create(xenvif_kthread,
- (void *)vif, "%s", vif->dev->name);
+ task = kthread_create(xenvif_kthread_guest_rx,
+ (void *)vif, "%s-guest-rx", vif->dev->name);
if (IS_ERR(task)) {
pr_warn("Could not allocate kthread for %s\n", vif->dev->name);
err = PTR_ERR(task);
@@ -430,6 +505,16 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
vif->task = task;
+ task = kthread_create(xenvif_dealloc_kthread,
+ (void *)vif, "%s-dealloc", vif->dev->name);
+ if (IS_ERR(task)) {
+ pr_warn("Could not allocate kthread for %s\n", vif->dev->name);
+ err = PTR_ERR(task);
+ goto err_rx_unbind;
+ }
+
+ vif->dealloc_task = task;
+
rtnl_lock();
if (!vif->can_sg && vif->dev->mtu > ETH_DATA_LEN)
dev_set_mtu(vif->dev, ETH_DATA_LEN);
@@ -440,6 +525,7 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
rtnl_unlock();
wake_up_process(vif->task);
+ wake_up_process(vif->dealloc_task);
return 0;
@@ -473,10 +559,16 @@ void xenvif_disconnect(struct xenvif *vif)
xenvif_carrier_off(vif);
if (vif->task) {
+ del_timer_sync(&vif->wake_queue);
kthread_stop(vif->task);
vif->task = NULL;
}
+ if (vif->dealloc_task) {
+ kthread_stop(vif->dealloc_task);
+ vif->dealloc_task = NULL;
+ }
+
if (vif->tx_irq) {
if (vif->tx_irq == vif->rx_irq)
unbind_from_irqhandler(vif->tx_irq, vif);
@@ -492,6 +584,43 @@ void xenvif_disconnect(struct xenvif *vif)
void xenvif_free(struct xenvif *vif)
{
+ int i, unmap_timeout = 0;
+ /* Here we want to avoid timeout messages if an skb can be legitimately
+ * stuck somewhere else. Realistically this could be an another vif's
+ * internal or QDisc queue. That another vif also has this
+ * rx_drain_timeout_msecs timeout, but the timer only ditches the
+ * internal queue. After that, the QDisc queue can put in worst case
+ * XEN_NETIF_RX_RING_SIZE / MAX_SKB_FRAGS skbs into that another vif's
+ * internal queue, so we need several rounds of such timeouts until we
+ * can be sure that no another vif should have skb's from us. We are
+ * not sending more skb's, so newly stuck packets are not interesting
+ * for us here.
+ */
+ unsigned int worst_case_skb_lifetime = (rx_drain_timeout_msecs/1000) *
+ DIV_ROUND_UP(XENVIF_QUEUE_LENGTH, (XEN_NETIF_RX_RING_SIZE / MAX_SKB_FRAGS));
+
+ for (i = 0; i < MAX_PENDING_REQS; ++i) {
+ if (vif->grant_tx_handle[i] != NETBACK_INVALID_HANDLE) {
+ unmap_timeout++;
+ schedule_timeout(msecs_to_jiffies(1000));
+ if (unmap_timeout > worst_case_skb_lifetime &&
+ net_ratelimit())
+ netdev_err(vif->dev,
+ "Page still granted! Index: %x\n",
+ i);
+ /* If there are still unmapped pages, reset the loop to
+ * start checking again. We shouldn't exit here until
+ * dealloc thread and NAPI instance release all the
+ * pages. If a kernel bug causes the skbs to stall
+ * somewhere, the interface cannot be brought down
+ * properly.
+ */
+ i = -1;
+ }
+ }
+
+ free_xenballooned_pages(MAX_PENDING_REQS, vif->mmap_pages);
+
netif_napi_del(&vif->napi);
unregister_netdev(vif->dev);
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 438d0c09b7e6..3f021e054ba1 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -37,6 +37,7 @@
#include <linux/kthread.h>
#include <linux/if_vlan.h>
#include <linux/udp.h>
+#include <linux/highmem.h>
#include <net/tcp.h>
@@ -54,6 +55,13 @@
bool separate_tx_rx_irq = 1;
module_param(separate_tx_rx_irq, bool, 0644);
+/* When guest ring is filled up, qdisc queues the packets for us, but we have
+ * to timeout them, otherwise other guests' packets can get stuck there
+ */
+unsigned int rx_drain_timeout_msecs = 10000;
+module_param(rx_drain_timeout_msecs, uint, 0444);
+unsigned int rx_drain_timeout_jiffies;
+
/*
* This is the maximum slots a skb can have. If a guest sends a skb
* which exceeds this limit it is considered malicious.
@@ -62,24 +70,6 @@ module_param(separate_tx_rx_irq, bool, 0644);
static unsigned int fatal_skb_slots = FATAL_SKB_SLOTS_DEFAULT;
module_param(fatal_skb_slots, uint, 0444);
-/*
- * To avoid confusion, we define XEN_NETBK_LEGACY_SLOTS_MAX indicating
- * the maximum slots a valid packet can use. Now this value is defined
- * to be XEN_NETIF_NR_SLOTS_MIN, which is supposed to be supported by
- * all backend.
- */
-#define XEN_NETBK_LEGACY_SLOTS_MAX XEN_NETIF_NR_SLOTS_MIN
-
-/*
- * If head != INVALID_PENDING_RING_IDX, it means this tx request is head of
- * one or more merged tx requests, otherwise it is the continuation of
- * previous tx request.
- */
-static inline int pending_tx_is_head(struct xenvif *vif, RING_IDX idx)
-{
- return vif->pending_tx_info[idx].head != INVALID_PENDING_RING_IDX;
-}
-
static void xenvif_idx_release(struct xenvif *vif, u16 pending_idx,
u8 status);
@@ -109,6 +99,21 @@ static inline unsigned long idx_to_kaddr(struct xenvif *vif,
return (unsigned long)pfn_to_kaddr(idx_to_pfn(vif, idx));
}
+#define callback_param(vif, pending_idx) \
+ (vif->pending_tx_info[pending_idx].callback_struct)
+
+/* Find the containing VIF's structure from a pointer in pending_tx_info array
+ */
+static inline struct xenvif* ubuf_to_vif(struct ubuf_info *ubuf)
+{
+ u16 pending_idx = ubuf->desc;
+ struct pending_tx_info *temp =
+ container_of(ubuf, struct pending_tx_info, callback_struct);
+ return container_of(temp - pending_idx,
+ struct xenvif,
+ pending_tx_info[0]);
+}
+
/* This is a miniumum size for the linear area to avoid lots of
* calls to __pskb_pull_tail() as we set up checksum offsets. The
* value 128 was chosen as it covers all IPv4 and most likely
@@ -131,12 +136,6 @@ static inline pending_ring_idx_t pending_index(unsigned i)
return i & (MAX_PENDING_REQS-1);
}
-static inline pending_ring_idx_t nr_pending_reqs(struct xenvif *vif)
-{
- return MAX_PENDING_REQS -
- vif->pending_prod + vif->pending_cons;
-}
-
bool xenvif_rx_ring_slots_available(struct xenvif *vif, int needed)
{
RING_IDX prod, cons;
@@ -192,8 +191,8 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
* into multiple copies tend to give large frags their
* own buffers as before.
*/
- if ((offset + size > MAX_BUFFER_OFFSET) &&
- (size <= MAX_BUFFER_OFFSET) && offset && !head)
+ BUG_ON(size > MAX_BUFFER_OFFSET);
+ if ((offset + size > MAX_BUFFER_OFFSET) && offset && !head)
return true;
return false;
@@ -235,7 +234,9 @@ static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif *vif,
static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
struct netrx_pending_operations *npo,
struct page *page, unsigned long size,
- unsigned long offset, int *head)
+ unsigned long offset, int *head,
+ struct xenvif *foreign_vif,
+ grant_ref_t foreign_gref)
{
struct gnttab_copy *copy_gop;
struct xenvif_rx_meta *meta;
@@ -277,8 +278,15 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
copy_gop->flags = GNTCOPY_dest_gref;
copy_gop->len = bytes;
- copy_gop->source.domid = DOMID_SELF;
- copy_gop->source.u.gmfn = virt_to_mfn(page_address(page));
+ if (foreign_vif) {
+ copy_gop->source.domid = foreign_vif->domid;
+ copy_gop->source.u.ref = foreign_gref;
+ copy_gop->flags |= GNTCOPY_source_gref;
+ } else {
+ copy_gop->source.domid = DOMID_SELF;
+ copy_gop->source.u.gmfn =
+ virt_to_mfn(page_address(page));
+ }
copy_gop->source.offset = offset;
copy_gop->dest.domid = vif->domid;
@@ -338,6 +346,9 @@ static int xenvif_gop_skb(struct sk_buff *skb,
int head = 1;
int old_meta_prod;
int gso_type;
+ struct ubuf_info *ubuf = skb_shinfo(skb)->destructor_arg;
+ grant_ref_t foreign_grefs[MAX_SKB_FRAGS];
+ struct xenvif *foreign_vif = NULL;
old_meta_prod = npo->meta_prod;
@@ -375,6 +386,19 @@ static int xenvif_gop_skb(struct sk_buff *skb,
npo->copy_off = 0;
npo->copy_gref = req->gref;
+ if ((skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) &&
+ (ubuf->callback == &xenvif_zerocopy_callback)) {
+ int i = 0;
+ foreign_vif = ubuf_to_vif(ubuf);
+
+ do {
+ u16 pending_idx = ubuf->desc;
+ foreign_grefs[i++] =
+ foreign_vif->pending_tx_info[pending_idx].req.gref;
+ ubuf = (struct ubuf_info *) ubuf->ctx;
+ } while (ubuf);
+ }
+
data = skb->data;
while (data < skb_tail_pointer(skb)) {
unsigned int offset = offset_in_page(data);
@@ -384,7 +408,9 @@ static int xenvif_gop_skb(struct sk_buff *skb,
len = skb_tail_pointer(skb) - data;
xenvif_gop_frag_copy(vif, skb, npo,
- virt_to_page(data), len, offset, &head);
+ virt_to_page(data), len, offset, &head,
+ NULL,
+ 0);
data += len;
}
@@ -393,7 +419,9 @@ static int xenvif_gop_skb(struct sk_buff *skb,
skb_frag_page(&skb_shinfo(skb)->frags[i]),
skb_frag_size(&skb_shinfo(skb)->frags[i]),
skb_shinfo(skb)->frags[i].page_offset,
- &head);
+ &head,
+ foreign_vif,
+ foreign_grefs[i]);
}
return npo->meta_prod - old_meta_prod;
@@ -451,10 +479,12 @@ static void xenvif_add_frag_responses(struct xenvif *vif, int status,
}
}
-struct skb_cb_overlay {
+struct xenvif_rx_cb {
int meta_slots_used;
};
+#define XENVIF_RX_CB(skb) ((struct xenvif_rx_cb *)(skb)->cb)
+
void xenvif_kick_thread(struct xenvif *vif)
{
wake_up(&vif->wq);
@@ -470,7 +500,6 @@ static void xenvif_rx_action(struct xenvif *vif)
LIST_HEAD(notify);
int ret;
unsigned long offset;
- struct skb_cb_overlay *sco;
bool need_to_notify = false;
struct netrx_pending_operations npo = {
@@ -482,6 +511,8 @@ static void xenvif_rx_action(struct xenvif *vif)
while ((skb = skb_dequeue(&vif->rx_queue)) != NULL) {
RING_IDX max_slots_needed;
+ RING_IDX old_req_cons;
+ RING_IDX ring_slots_used;
int i;
/* We need a cheap worse case estimate for the number of
@@ -493,9 +524,28 @@ static void xenvif_rx_action(struct xenvif *vif)
PAGE_SIZE);
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
unsigned int size;
+ unsigned int offset;
+
size = skb_frag_size(&skb_shinfo(skb)->frags[i]);
- max_slots_needed += DIV_ROUND_UP(size, PAGE_SIZE);
+ offset = skb_shinfo(skb)->frags[i].page_offset;
+
+ /* For a worse-case estimate we need to factor in
+ * the fragment page offset as this will affect the
+ * number of times xenvif_gop_frag_copy() will
+ * call start_new_rx_buffer().
+ */
+ max_slots_needed += DIV_ROUND_UP(offset + size,
+ PAGE_SIZE);
}
+
+ /* To avoid the estimate becoming too pessimal for some
+ * frontends that limit posted rx requests, cap the estimate
+ * at MAX_SKB_FRAGS.
+ */
+ if (max_slots_needed > MAX_SKB_FRAGS)
+ max_slots_needed = MAX_SKB_FRAGS;
+
+ /* We may need one more slot for GSO metadata */
if (skb_is_gso(skb) &&
(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4 ||
skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6))
@@ -510,9 +560,11 @@ static void xenvif_rx_action(struct xenvif *vif)
} else
vif->rx_last_skb_slots = 0;
- sco = (struct skb_cb_overlay *)skb->cb;
- sco->meta_slots_used = xenvif_gop_skb(skb, &npo);
- BUG_ON(sco->meta_slots_used > max_slots_needed);
+ old_req_cons = vif->rx.req_cons;
+ XENVIF_RX_CB(skb)->meta_slots_used = xenvif_gop_skb(skb, &npo);
+ ring_slots_used = vif->rx.req_cons - old_req_cons;
+
+ BUG_ON(ring_slots_used > max_slots_needed);
__skb_queue_tail(&rxq, skb);
}
@@ -526,7 +578,6 @@ static void xenvif_rx_action(struct xenvif *vif)
gnttab_batch_copy(vif->grant_copy_op, npo.copy_prod);
while ((skb = __skb_dequeue(&rxq)) != NULL) {
- sco = (struct skb_cb_overlay *)skb->cb;
if ((1 << vif->meta[npo.meta_cons].gso_type) &
vif->gso_prefix_mask) {
@@ -537,19 +588,21 @@ static void xenvif_rx_action(struct xenvif *vif)
resp->offset = vif->meta[npo.meta_cons].gso_size;
resp->id = vif->meta[npo.meta_cons].id;
- resp->status = sco->meta_slots_used;
+ resp->status = XENVIF_RX_CB(skb)->meta_slots_used;
npo.meta_cons++;
- sco->meta_slots_used--;
+ XENVIF_RX_CB(skb)->meta_slots_used--;
}
vif->dev->stats.tx_bytes += skb->len;
vif->dev->stats.tx_packets++;
- status = xenvif_check_gop(vif, sco->meta_slots_used, &npo);
+ status = xenvif_check_gop(vif,
+ XENVIF_RX_CB(skb)->meta_slots_used,
+ &npo);
- if (sco->meta_slots_used == 1)
+ if (XENVIF_RX_CB(skb)->meta_slots_used == 1)
flags = 0;
else
flags = XEN_NETRXF_more_data;
@@ -586,13 +639,13 @@ static void xenvif_rx_action(struct xenvif *vif)
xenvif_add_frag_responses(vif, status,
vif->meta + npo.meta_cons + 1,
- sco->meta_slots_used);
+ XENVIF_RX_CB(skb)->meta_slots_used);
RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->rx, ret);
need_to_notify |= !!ret;
- npo.meta_cons += sco->meta_slots_used;
+ npo.meta_cons += XENVIF_RX_CB(skb)->meta_slots_used;
dev_kfree_skb(skb);
}
@@ -642,9 +695,12 @@ static void xenvif_tx_err(struct xenvif *vif,
struct xen_netif_tx_request *txp, RING_IDX end)
{
RING_IDX cons = vif->tx.req_cons;
+ unsigned long flags;
do {
+ spin_lock_irqsave(&vif->response_lock, flags);
make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR);
+ spin_unlock_irqrestore(&vif->response_lock, flags);
if (cons == end)
break;
txp = RING_GET_REQUEST(&vif->tx, cons++);
@@ -655,7 +711,8 @@ static void xenvif_tx_err(struct xenvif *vif,
static void xenvif_fatal_tx_err(struct xenvif *vif)
{
netdev_err(vif->dev, "fatal error; disabling device\n");
- xenvif_carrier_off(vif);
+ vif->disabled = true;
+ xenvif_kick_thread(vif);
}
static int xenvif_count_requests(struct xenvif *vif,
@@ -756,180 +813,168 @@ static int xenvif_count_requests(struct xenvif *vif,
return slots;
}
-static struct page *xenvif_alloc_page(struct xenvif *vif,
- u16 pending_idx)
+
+struct xenvif_tx_cb {
+ u16 pending_idx;
+};
+
+#define XENVIF_TX_CB(skb) ((struct xenvif_tx_cb *)(skb)->cb)
+
+static inline void xenvif_tx_create_gop(struct xenvif *vif,
+ u16 pending_idx,
+ struct xen_netif_tx_request *txp,
+ struct gnttab_map_grant_ref *gop)
{
- struct page *page;
+ vif->pages_to_map[gop-vif->tx_map_ops] = vif->mmap_pages[pending_idx];
+ gnttab_set_map_op(gop, idx_to_kaddr(vif, pending_idx),
+ GNTMAP_host_map | GNTMAP_readonly,
+ txp->gref, vif->domid);
+
+ memcpy(&vif->pending_tx_info[pending_idx].req, txp,
+ sizeof(*txp));
+}
- page = alloc_page(GFP_ATOMIC|__GFP_COLD);
- if (!page)
+static inline struct sk_buff *xenvif_alloc_skb(unsigned int size)
+{
+ struct sk_buff *skb =
+ alloc_skb(size + NET_SKB_PAD + NET_IP_ALIGN,
+ GFP_ATOMIC | __GFP_NOWARN);
+ if (unlikely(skb == NULL))
return NULL;
- vif->mmap_pages[pending_idx] = page;
- return page;
+ /* Packets passed to netif_rx() must have some headroom. */
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+
+ /* Initialize it here to avoid later surprises */
+ skb_shinfo(skb)->destructor_arg = NULL;
+
+ return skb;
}
-static struct gnttab_copy *xenvif_get_requests(struct xenvif *vif,
- struct sk_buff *skb,
- struct xen_netif_tx_request *txp,
- struct gnttab_copy *gop)
+static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif *vif,
+ struct sk_buff *skb,
+ struct xen_netif_tx_request *txp,
+ struct gnttab_map_grant_ref *gop)
{
struct skb_shared_info *shinfo = skb_shinfo(skb);
skb_frag_t *frags = shinfo->frags;
- u16 pending_idx = *((u16 *)skb->data);
- u16 head_idx = 0;
- int slot, start;
- struct page *page;
- pending_ring_idx_t index, start_idx = 0;
- uint16_t dst_offset;
- unsigned int nr_slots;
- struct pending_tx_info *first = NULL;
+ u16 pending_idx = XENVIF_TX_CB(skb)->pending_idx;
+ int start;
+ pending_ring_idx_t index;
+ unsigned int nr_slots, frag_overflow = 0;
/* At this point shinfo->nr_frags is in fact the number of
* slots, which can be as large as XEN_NETBK_LEGACY_SLOTS_MAX.
*/
+ if (shinfo->nr_frags > MAX_SKB_FRAGS) {
+ frag_overflow = shinfo->nr_frags - MAX_SKB_FRAGS;
+ BUG_ON(frag_overflow > MAX_SKB_FRAGS);
+ shinfo->nr_frags = MAX_SKB_FRAGS;
+ }
nr_slots = shinfo->nr_frags;
/* Skip first skb fragment if it is on same page as header fragment. */
start = (frag_get_pending_idx(&shinfo->frags[0]) == pending_idx);
- /* Coalesce tx requests, at this point the packet passed in
- * should be <= 64K. Any packets larger than 64K have been
- * handled in xenvif_count_requests().
- */
- for (shinfo->nr_frags = slot = start; slot < nr_slots;
- shinfo->nr_frags++) {
- struct pending_tx_info *pending_tx_info =
- vif->pending_tx_info;
+ for (shinfo->nr_frags = start; shinfo->nr_frags < nr_slots;
+ shinfo->nr_frags++, txp++, gop++) {
+ index = pending_index(vif->pending_cons++);
+ pending_idx = vif->pending_ring[index];
+ xenvif_tx_create_gop(vif, pending_idx, txp, gop);
+ frag_set_pending_idx(&frags[shinfo->nr_frags], pending_idx);
+ }
- page = alloc_page(GFP_ATOMIC|__GFP_COLD);
- if (!page)
- goto err;
-
- dst_offset = 0;
- first = NULL;
- while (dst_offset < PAGE_SIZE && slot < nr_slots) {
- gop->flags = GNTCOPY_source_gref;
-
- gop->source.u.ref = txp->gref;
- gop->source.domid = vif->domid;
- gop->source.offset = txp->offset;
-
- gop->dest.domid = DOMID_SELF;
-
- gop->dest.offset = dst_offset;
- gop->dest.u.gmfn = virt_to_mfn(page_address(page));
-
- if (dst_offset + txp->size > PAGE_SIZE) {
- /* This page can only merge a portion
- * of tx request. Do not increment any
- * pointer / counter here. The txp
- * will be dealt with in future
- * rounds, eventually hitting the
- * `else` branch.
- */
- gop->len = PAGE_SIZE - dst_offset;
- txp->offset += gop->len;
- txp->size -= gop->len;
- dst_offset += gop->len; /* quit loop */
- } else {
- /* This tx request can be merged in the page */
- gop->len = txp->size;
- dst_offset += gop->len;
-
- index = pending_index(vif->pending_cons++);
-
- pending_idx = vif->pending_ring[index];
-
- memcpy(&pending_tx_info[pending_idx].req, txp,
- sizeof(*txp));
-
- /* Poison these fields, corresponding
- * fields for head tx req will be set
- * to correct values after the loop.
- */
- vif->mmap_pages[pending_idx] = (void *)(~0UL);
- pending_tx_info[pending_idx].head =
- INVALID_PENDING_RING_IDX;
-
- if (!first) {
- first = &pending_tx_info[pending_idx];
- start_idx = index;
- head_idx = pending_idx;
- }
-
- txp++;
- slot++;
- }
+ if (frag_overflow) {
+ struct sk_buff *nskb = xenvif_alloc_skb(0);
+ if (unlikely(nskb == NULL)) {
+ if (net_ratelimit())
+ netdev_err(vif->dev,
+ "Can't allocate the frag_list skb.\n");
+ return NULL;
+ }
+
+ shinfo = skb_shinfo(nskb);
+ frags = shinfo->frags;
- gop++;
+ for (shinfo->nr_frags = 0; shinfo->nr_frags < frag_overflow;
+ shinfo->nr_frags++, txp++, gop++) {
+ index = pending_index(vif->pending_cons++);
+ pending_idx = vif->pending_ring[index];
+ xenvif_tx_create_gop(vif, pending_idx, txp, gop);
+ frag_set_pending_idx(&frags[shinfo->nr_frags],
+ pending_idx);
}
- first->req.offset = 0;
- first->req.size = dst_offset;
- first->head = start_idx;
- vif->mmap_pages[head_idx] = page;
- frag_set_pending_idx(&frags[shinfo->nr_frags], head_idx);
+ skb_shinfo(skb)->frag_list = nskb;
}
- BUG_ON(shinfo->nr_frags > MAX_SKB_FRAGS);
-
return gop;
-err:
- /* Unwind, freeing all pages and sending error responses. */
- while (shinfo->nr_frags-- > start) {
- xenvif_idx_release(vif,
- frag_get_pending_idx(&frags[shinfo->nr_frags]),
- XEN_NETIF_RSP_ERROR);
+}
+
+static inline void xenvif_grant_handle_set(struct xenvif *vif,
+ u16 pending_idx,
+ grant_handle_t handle)
+{
+ if (unlikely(vif->grant_tx_handle[pending_idx] !=
+ NETBACK_INVALID_HANDLE)) {
+ netdev_err(vif->dev,
+ "Trying to overwrite active handle! pending_idx: %x\n",
+ pending_idx);
+ BUG();
}
- /* The head too, if necessary. */
- if (start)
- xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
+ vif->grant_tx_handle[pending_idx] = handle;
+}
- return NULL;
+static inline void xenvif_grant_handle_reset(struct xenvif *vif,
+ u16 pending_idx)
+{
+ if (unlikely(vif->grant_tx_handle[pending_idx] ==
+ NETBACK_INVALID_HANDLE)) {
+ netdev_err(vif->dev,
+ "Trying to unmap invalid handle! pending_idx: %x\n",
+ pending_idx);
+ BUG();
+ }
+ vif->grant_tx_handle[pending_idx] = NETBACK_INVALID_HANDLE;
}
static int xenvif_tx_check_gop(struct xenvif *vif,
struct sk_buff *skb,
- struct gnttab_copy **gopp)
+ struct gnttab_map_grant_ref **gopp)
{
- struct gnttab_copy *gop = *gopp;
- u16 pending_idx = *((u16 *)skb->data);
+ struct gnttab_map_grant_ref *gop = *gopp;
+ u16 pending_idx = XENVIF_TX_CB(skb)->pending_idx;
struct skb_shared_info *shinfo = skb_shinfo(skb);
struct pending_tx_info *tx_info;
int nr_frags = shinfo->nr_frags;
int i, err, start;
- u16 peek; /* peek into next tx request */
+ struct sk_buff *first_skb = NULL;
/* Check status of header. */
err = gop->status;
if (unlikely(err))
xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
+ else
+ xenvif_grant_handle_set(vif, pending_idx , gop->handle);
/* Skip first skb fragment if it is on same page as header fragment. */
start = (frag_get_pending_idx(&shinfo->frags[0]) == pending_idx);
+check_frags:
for (i = start; i < nr_frags; i++) {
int j, newerr;
- pending_ring_idx_t head;
pending_idx = frag_get_pending_idx(&shinfo->frags[i]);
tx_info = &vif->pending_tx_info[pending_idx];
- head = tx_info->head;
/* Check error status: if okay then remember grant handle. */
- do {
- newerr = (++gop)->status;
- if (newerr)
- break;
- peek = vif->pending_ring[pending_index(++head)];
- } while (!pending_tx_is_head(vif, peek));
+ newerr = (++gop)->status;
if (likely(!newerr)) {
+ xenvif_grant_handle_set(vif, pending_idx , gop->handle);
/* Had a previous error? Invalidate this fragment. */
if (unlikely(err))
- xenvif_idx_release(vif, pending_idx,
- XEN_NETIF_RSP_OKAY);
+ xenvif_idx_unmap(vif, pending_idx);
continue;
}
@@ -939,20 +984,45 @@ static int xenvif_tx_check_gop(struct xenvif *vif,
/* Not the first error? Preceding frags already invalidated. */
if (err)
continue;
-
/* First error: invalidate header and preceding fragments. */
- pending_idx = *((u16 *)skb->data);
- xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY);
+ if (!first_skb)
+ pending_idx = XENVIF_TX_CB(skb)->pending_idx;
+ else
+ pending_idx = XENVIF_TX_CB(skb)->pending_idx;
+ xenvif_idx_unmap(vif, pending_idx);
for (j = start; j < i; j++) {
pending_idx = frag_get_pending_idx(&shinfo->frags[j]);
- xenvif_idx_release(vif, pending_idx,
- XEN_NETIF_RSP_OKAY);
+ xenvif_idx_unmap(vif, pending_idx);
}
/* Remember the error: invalidate all subsequent fragments. */
err = newerr;
}
+ if (skb_has_frag_list(skb)) {
+ first_skb = skb;
+ skb = shinfo->frag_list;
+ shinfo = skb_shinfo(skb);
+ nr_frags = shinfo->nr_frags;
+ start = 0;
+
+ goto check_frags;
+ }
+
+ /* There was a mapping error in the frag_list skb. We have to unmap
+ * the first skb's frags
+ */
+ if (first_skb && err) {
+ int j;
+ shinfo = skb_shinfo(first_skb);
+ pending_idx = XENVIF_TX_CB(skb)->pending_idx;
+ start = (frag_get_pending_idx(&shinfo->frags[0]) == pending_idx);
+ for (j = start; j < shinfo->nr_frags; j++) {
+ pending_idx = frag_get_pending_idx(&shinfo->frags[j]);
+ xenvif_idx_unmap(vif, pending_idx);
+ }
+ }
+
*gopp = gop + 1;
return err;
}
@@ -962,6 +1032,10 @@ static void xenvif_fill_frags(struct xenvif *vif, struct sk_buff *skb)
struct skb_shared_info *shinfo = skb_shinfo(skb);
int nr_frags = shinfo->nr_frags;
int i;
+ u16 prev_pending_idx = INVALID_PENDING_IDX;
+
+ if (skb_shinfo(skb)->destructor_arg)
+ prev_pending_idx = XENVIF_TX_CB(skb)->pending_idx;
for (i = 0; i < nr_frags; i++) {
skb_frag_t *frag = shinfo->frags + i;
@@ -971,6 +1045,17 @@ static void xenvif_fill_frags(struct xenvif *vif, struct sk_buff *skb)
pending_idx = frag_get_pending_idx(frag);
+ /* If this is not the first frag, chain it to the previous*/
+ if (unlikely(prev_pending_idx == INVALID_PENDING_IDX))
+ skb_shinfo(skb)->destructor_arg =
+ &callback_param(vif, pending_idx);
+ else if (likely(pending_idx != prev_pending_idx))
+ callback_param(vif, prev_pending_idx).ctx =
+ &callback_param(vif, pending_idx);
+
+ callback_param(vif, pending_idx).ctx = NULL;
+ prev_pending_idx = pending_idx;
+
txp = &vif->pending_tx_info[pending_idx].req;
page = virt_to_page(idx_to_kaddr(vif, pending_idx));
__skb_fill_page_desc(skb, i, page, txp->offset, txp->size);
@@ -978,10 +1063,15 @@ static void xenvif_fill_frags(struct xenvif *vif, struct sk_buff *skb)
skb->data_len += txp->size;
skb->truesize += txp->size;
- /* Take an extra reference to offset xenvif_idx_release */
+ /* Take an extra reference to offset network stack's put_page */
get_page(vif->mmap_pages[pending_idx]);
- xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY);
}
+ /* FIXME: __skb_fill_page_desc set this to true because page->pfmemalloc
+ * overlaps with "index", and "mapping" is not set. I think mapping
+ * should be set. If delivered to local stack, it would drop this
+ * skb in sk_filter unless the socket has the right to use it.
+ */
+ skb->pfmemalloc = false;
}
static int xenvif_get_extras(struct xenvif *vif,
@@ -1101,16 +1191,13 @@ static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
static unsigned xenvif_tx_build_gops(struct xenvif *vif, int budget)
{
- struct gnttab_copy *gop = vif->tx_copy_ops, *request_gop;
+ struct gnttab_map_grant_ref *gop = vif->tx_map_ops, *request_gop;
struct sk_buff *skb;
int ret;
- while ((nr_pending_reqs(vif) + XEN_NETBK_LEGACY_SLOTS_MAX
- < MAX_PENDING_REQS) &&
- (skb_queue_len(&vif->tx_queue) < budget)) {
+ while (skb_queue_len(&vif->tx_queue) < budget) {
struct xen_netif_tx_request txreq;
struct xen_netif_tx_request txfrags[XEN_NETBK_LEGACY_SLOTS_MAX];
- struct page *page;
struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX-1];
u16 pending_idx;
RING_IDX idx;
@@ -1126,7 +1213,7 @@ static unsigned xenvif_tx_build_gops(struct xenvif *vif, int budget)
vif->tx.sring->req_prod, vif->tx.req_cons,
XEN_NETIF_TX_RING_SIZE);
xenvif_fatal_tx_err(vif);
- continue;
+ break;
}
work_to_do = RING_HAS_UNCONSUMED_REQUESTS(&vif->tx);
@@ -1186,8 +1273,7 @@ static unsigned xenvif_tx_build_gops(struct xenvif *vif, int budget)
ret < XEN_NETBK_LEGACY_SLOTS_MAX) ?
PKT_PROT_LEN : txreq.size;
- skb = alloc_skb(data_len + NET_SKB_PAD + NET_IP_ALIGN,
- GFP_ATOMIC | __GFP_NOWARN);
+ skb = xenvif_alloc_skb(data_len);
if (unlikely(skb == NULL)) {
netdev_dbg(vif->dev,
"Can't allocate a skb in start_xmit.\n");
@@ -1195,9 +1281,6 @@ static unsigned xenvif_tx_build_gops(struct xenvif *vif, int budget)
break;
}
- /* Packets passed to netif_rx() must have some headroom. */
- skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
-
if (extras[XEN_NETIF_EXTRA_TYPE_GSO - 1].type) {
struct xen_netif_extra_info *gso;
gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1];
@@ -1209,31 +1292,11 @@ static unsigned xenvif_tx_build_gops(struct xenvif *vif, int budget)
}
}
- /* XXX could copy straight to head */
- page = xenvif_alloc_page(vif, pending_idx);
- if (!page) {
- kfree_skb(skb);
- xenvif_tx_err(vif, &txreq, idx);
- break;
- }
-
- gop->source.u.ref = txreq.gref;
- gop->source.domid = vif->domid;
- gop->source.offset = txreq.offset;
-
- gop->dest.u.gmfn = virt_to_mfn(page_address(page));
- gop->dest.domid = DOMID_SELF;
- gop->dest.offset = txreq.offset;
-
- gop->len = txreq.size;
- gop->flags = GNTCOPY_source_gref;
+ xenvif_tx_create_gop(vif, pending_idx, &txreq, gop);
gop++;
- memcpy(&vif->pending_tx_info[pending_idx].req,
- &txreq, sizeof(txreq));
- vif->pending_tx_info[pending_idx].head = index;
- *((u16 *)skb->data) = pending_idx;
+ XENVIF_TX_CB(skb)->pending_idx = pending_idx;
__skb_put(skb, data_len);
@@ -1261,17 +1324,82 @@ static unsigned xenvif_tx_build_gops(struct xenvif *vif, int budget)
vif->tx.req_cons = idx;
- if ((gop-vif->tx_copy_ops) >= ARRAY_SIZE(vif->tx_copy_ops))
+ if ((gop-vif->tx_map_ops) >= ARRAY_SIZE(vif->tx_map_ops))
break;
}
- return gop - vif->tx_copy_ops;
+ return gop - vif->tx_map_ops;
}
+/* Consolidate skb with a frag_list into a brand new one with local pages on
+ * frags. Returns 0 or -ENOMEM if can't allocate new pages.
+ */
+static int xenvif_handle_frag_list(struct xenvif *vif, struct sk_buff *skb)
+{
+ unsigned int offset = skb_headlen(skb);
+ skb_frag_t frags[MAX_SKB_FRAGS];
+ int i;
+ struct ubuf_info *uarg;
+ struct sk_buff *nskb = skb_shinfo(skb)->frag_list;
+
+ vif->tx_zerocopy_sent += 2;
+ vif->tx_frag_overflow++;
+
+ xenvif_fill_frags(vif, nskb);
+ /* Subtract frags size, we will correct it later */
+ skb->truesize -= skb->data_len;
+ skb->len += nskb->len;
+ skb->data_len += nskb->len;
+
+ /* create a brand new frags array and coalesce there */
+ for (i = 0; offset < skb->len; i++) {
+ struct page *page;
+ unsigned int len;
+
+ BUG_ON(i >= MAX_SKB_FRAGS);
+ page = alloc_page(GFP_ATOMIC|__GFP_COLD);
+ if (!page) {
+ int j;
+ skb->truesize += skb->data_len;
+ for (j = 0; j < i; j++)
+ put_page(frags[j].page.p);
+ return -ENOMEM;
+ }
+
+ if (offset + PAGE_SIZE < skb->len)
+ len = PAGE_SIZE;
+ else
+ len = skb->len - offset;
+ if (skb_copy_bits(skb, offset, page_address(page), len))
+ BUG();
+
+ offset += len;
+ frags[i].page.p = page;
+ frags[i].page_offset = 0;
+ skb_frag_size_set(&frags[i], len);
+ }
+ /* swap out with old one */
+ memcpy(skb_shinfo(skb)->frags,
+ frags,
+ i * sizeof(skb_frag_t));
+ skb_shinfo(skb)->nr_frags = i;
+ skb->truesize += i * PAGE_SIZE;
+
+ /* remove traces of mapped pages and frag_list */
+ skb_frag_list_init(skb);
+ uarg = skb_shinfo(skb)->destructor_arg;
+ uarg->callback(uarg, true);
+ skb_shinfo(skb)->destructor_arg = NULL;
+
+ skb_shinfo(nskb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
+ kfree_skb(nskb);
+
+ return 0;
+}
static int xenvif_tx_submit(struct xenvif *vif)
{
- struct gnttab_copy *gop = vif->tx_copy_ops;
+ struct gnttab_map_grant_ref *gop = vif->tx_map_ops;
struct sk_buff *skb;
int work_done = 0;
@@ -1280,7 +1408,7 @@ static int xenvif_tx_submit(struct xenvif *vif)
u16 pending_idx;
unsigned data_len;
- pending_idx = *((u16 *)skb->data);
+ pending_idx = XENVIF_TX_CB(skb)->pending_idx;
txp = &vif->pending_tx_info[pending_idx].req;
/* Check the remap error code. */
@@ -1295,14 +1423,16 @@ static int xenvif_tx_submit(struct xenvif *vif)
memcpy(skb->data,
(void *)(idx_to_kaddr(vif, pending_idx)|txp->offset),
data_len);
+ callback_param(vif, pending_idx).ctx = NULL;
if (data_len < txp->size) {
/* Append the packet payload as a fragment. */
txp->offset += data_len;
txp->size -= data_len;
+ skb_shinfo(skb)->destructor_arg =
+ &callback_param(vif, pending_idx);
} else {
/* Schedule a response immediately. */
- xenvif_idx_release(vif, pending_idx,
- XEN_NETIF_RSP_OKAY);
+ xenvif_idx_unmap(vif, pending_idx);
}
if (txp->flags & XEN_NETTXF_csum_blank)
@@ -1312,6 +1442,17 @@ static int xenvif_tx_submit(struct xenvif *vif)
xenvif_fill_frags(vif, skb);
+ if (unlikely(skb_has_frag_list(skb))) {
+ if (xenvif_handle_frag_list(vif, skb)) {
+ if (net_ratelimit())
+ netdev_err(vif->dev,
+ "Not enough memory to consolidate frag_list!\n");
+ skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
+ kfree_skb(skb);
+ continue;
+ }
+ }
+
if (skb_is_nonlinear(skb) && skb_headlen(skb) < PKT_PROT_LEN) {
int target = min_t(int, skb->len, PKT_PROT_LEN);
__pskb_pull_tail(skb, target - skb_headlen(skb));
@@ -1324,6 +1465,9 @@ static int xenvif_tx_submit(struct xenvif *vif)
if (checksum_setup(vif, skb)) {
netdev_dbg(vif->dev,
"Can't setup checksum in net_tx_action\n");
+ /* We have to set this flag to trigger the callback */
+ if (skb_shinfo(skb)->destructor_arg)
+ skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
kfree_skb(skb);
continue;
}
@@ -1349,17 +1493,126 @@ static int xenvif_tx_submit(struct xenvif *vif)
work_done++;
+ /* Set this flag right before netif_receive_skb, otherwise
+ * someone might think this packet already left netback, and
+ * do a skb_copy_ubufs while we are still in control of the
+ * skb. E.g. the __pskb_pull_tail earlier can do such thing.
+ */
+ if (skb_shinfo(skb)->destructor_arg) {
+ skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
+ vif->tx_zerocopy_sent++;
+ }
+
netif_receive_skb(skb);
}
return work_done;
}
+void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success)
+{
+ unsigned long flags;
+ pending_ring_idx_t index;
+ struct xenvif *vif = ubuf_to_vif(ubuf);
+
+ /* This is the only place where we grab this lock, to protect callbacks
+ * from each other.
+ */
+ spin_lock_irqsave(&vif->callback_lock, flags);
+ do {
+ u16 pending_idx = ubuf->desc;
+ ubuf = (struct ubuf_info *) ubuf->ctx;
+ BUG_ON(vif->dealloc_prod - vif->dealloc_cons >=
+ MAX_PENDING_REQS);
+ index = pending_index(vif->dealloc_prod);
+ vif->dealloc_ring[index] = pending_idx;
+ /* Sync with xenvif_tx_dealloc_action:
+ * insert idx then incr producer.
+ */
+ smp_wmb();
+ vif->dealloc_prod++;
+ } while (ubuf);
+ wake_up(&vif->dealloc_wq);
+ spin_unlock_irqrestore(&vif->callback_lock, flags);
+
+ if (likely(zerocopy_success))
+ vif->tx_zerocopy_success++;
+ else
+ vif->tx_zerocopy_fail++;
+}
+
+static inline void xenvif_tx_dealloc_action(struct xenvif *vif)
+{
+ struct gnttab_unmap_grant_ref *gop;
+ pending_ring_idx_t dc, dp;
+ u16 pending_idx, pending_idx_release[MAX_PENDING_REQS];
+ unsigned int i = 0;
+
+ dc = vif->dealloc_cons;
+ gop = vif->tx_unmap_ops;
+
+ /* Free up any grants we have finished using */
+ do {
+ dp = vif->dealloc_prod;
+
+ /* Ensure we see all indices enqueued by all
+ * xenvif_zerocopy_callback().
+ */
+ smp_rmb();
+
+ while (dc != dp) {
+ BUG_ON(gop - vif->tx_unmap_ops > MAX_PENDING_REQS);
+ pending_idx =
+ vif->dealloc_ring[pending_index(dc++)];
+
+ pending_idx_release[gop-vif->tx_unmap_ops] =
+ pending_idx;
+ vif->pages_to_unmap[gop-vif->tx_unmap_ops] =
+ vif->mmap_pages[pending_idx];
+ gnttab_set_unmap_op(gop,
+ idx_to_kaddr(vif, pending_idx),
+ GNTMAP_host_map,
+ vif->grant_tx_handle[pending_idx]);
+ xenvif_grant_handle_reset(vif, pending_idx);
+ ++gop;
+ }
+
+ } while (dp != vif->dealloc_prod);
+
+ vif->dealloc_cons = dc;
+
+ if (gop - vif->tx_unmap_ops > 0) {
+ int ret;
+ ret = gnttab_unmap_refs(vif->tx_unmap_ops,
+ NULL,
+ vif->pages_to_unmap,
+ gop - vif->tx_unmap_ops);
+ if (ret) {
+ netdev_err(vif->dev, "Unmap fail: nr_ops %tx ret %d\n",
+ gop - vif->tx_unmap_ops, ret);
+ for (i = 0; i < gop - vif->tx_unmap_ops; ++i) {
+ if (gop[i].status != GNTST_okay)
+ netdev_err(vif->dev,
+ " host_addr: %llx handle: %x status: %d\n",
+ gop[i].host_addr,
+ gop[i].handle,
+ gop[i].status);
+ }
+ BUG();
+ }
+ }
+
+ for (i = 0; i < gop - vif->tx_unmap_ops; ++i)
+ xenvif_idx_release(vif, pending_idx_release[i],
+ XEN_NETIF_RSP_OKAY);
+}
+
+
/* Called after netfront has transmitted */
int xenvif_tx_action(struct xenvif *vif, int budget)
{
unsigned nr_gops;
- int work_done;
+ int work_done, ret;
if (unlikely(!tx_work_todo(vif)))
return 0;
@@ -1369,7 +1622,11 @@ int xenvif_tx_action(struct xenvif *vif, int budget)
if (nr_gops == 0)
return 0;
- gnttab_batch_copy(vif->tx_copy_ops, nr_gops);
+ ret = gnttab_map_refs(vif->tx_map_ops,
+ NULL,
+ vif->pages_to_map,
+ nr_gops);
+ BUG_ON(ret);
work_done = xenvif_tx_submit(vif);
@@ -1380,45 +1637,18 @@ static void xenvif_idx_release(struct xenvif *vif, u16 pending_idx,
u8 status)
{
struct pending_tx_info *pending_tx_info;
- pending_ring_idx_t head;
- u16 peek; /* peek into next tx request */
-
- BUG_ON(vif->mmap_pages[pending_idx] == (void *)(~0UL));
-
- /* Already complete? */
- if (vif->mmap_pages[pending_idx] == NULL)
- return;
+ pending_ring_idx_t index;
+ unsigned long flags;
pending_tx_info = &vif->pending_tx_info[pending_idx];
-
- head = pending_tx_info->head;
-
- BUG_ON(!pending_tx_is_head(vif, head));
- BUG_ON(vif->pending_ring[pending_index(head)] != pending_idx);
-
- do {
- pending_ring_idx_t index;
- pending_ring_idx_t idx = pending_index(head);
- u16 info_idx = vif->pending_ring[idx];
-
- pending_tx_info = &vif->pending_tx_info[info_idx];
- make_tx_response(vif, &pending_tx_info->req, status);
-
- /* Setting any number other than
- * INVALID_PENDING_RING_IDX indicates this slot is
- * starting a new packet / ending a previous packet.
- */
- pending_tx_info->head = 0;
-
- index = pending_index(vif->pending_prod++);
- vif->pending_ring[index] = vif->pending_ring[info_idx];
-
- peek = vif->pending_ring[pending_index(++head)];
-
- } while (!pending_tx_is_head(vif, peek));
-
- put_page(vif->mmap_pages[pending_idx]);
- vif->mmap_pages[pending_idx] = NULL;
+ spin_lock_irqsave(&vif->response_lock, flags);
+ make_tx_response(vif, &pending_tx_info->req, status);
+ index = pending_index(vif->pending_prod);
+ vif->pending_ring[index] = pending_idx;
+ /* TX shouldn't use the index before we give it back here */
+ mb();
+ vif->pending_prod++;
+ spin_unlock_irqrestore(&vif->response_lock, flags);
}
@@ -1466,23 +1696,54 @@ static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
return resp;
}
+void xenvif_idx_unmap(struct xenvif *vif, u16 pending_idx)
+{
+ int ret;
+ struct gnttab_unmap_grant_ref tx_unmap_op;
+
+ gnttab_set_unmap_op(&tx_unmap_op,
+ idx_to_kaddr(vif, pending_idx),
+ GNTMAP_host_map,
+ vif->grant_tx_handle[pending_idx]);
+ xenvif_grant_handle_reset(vif, pending_idx);
+
+ ret = gnttab_unmap_refs(&tx_unmap_op, NULL,
+ &vif->mmap_pages[pending_idx], 1);
+ if (ret) {
+ netdev_err(vif->dev,
+ "Unmap fail: ret: %d pending_idx: %d host_addr: %llx handle: %x status: %d\n",
+ ret,
+ pending_idx,
+ tx_unmap_op.host_addr,
+ tx_unmap_op.handle,
+ tx_unmap_op.status);
+ BUG();
+ }
+
+ xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY);
+}
+
static inline int rx_work_todo(struct xenvif *vif)
{
- return !skb_queue_empty(&vif->rx_queue) &&
- xenvif_rx_ring_slots_available(vif, vif->rx_last_skb_slots);
+ return (!skb_queue_empty(&vif->rx_queue) &&
+ xenvif_rx_ring_slots_available(vif, vif->rx_last_skb_slots)) ||
+ vif->rx_queue_purge;
}
static inline int tx_work_todo(struct xenvif *vif)
{
- if (likely(RING_HAS_UNCONSUMED_REQUESTS(&vif->tx)) &&
- (nr_pending_reqs(vif) + XEN_NETBK_LEGACY_SLOTS_MAX
- < MAX_PENDING_REQS))
+ if (likely(RING_HAS_UNCONSUMED_REQUESTS(&vif->tx)))
return 1;
return 0;
}
+static inline bool tx_dealloc_work_todo(struct xenvif *vif)
+{
+ return vif->dealloc_cons != vif->dealloc_prod;
+}
+
void xenvif_unmap_frontend_rings(struct xenvif *vif)
{
if (vif->tx.sring)
@@ -1540,7 +1801,7 @@ static void xenvif_start_queue(struct xenvif *vif)
netif_wake_queue(vif->dev);
}
-int xenvif_kthread(void *data)
+int xenvif_kthread_guest_rx(void *data)
{
struct xenvif *vif = data;
struct sk_buff *skb;
@@ -1548,16 +1809,34 @@ int xenvif_kthread(void *data)
while (!kthread_should_stop()) {
wait_event_interruptible(vif->wq,
rx_work_todo(vif) ||
+ vif->disabled ||
kthread_should_stop());
+
+ /* This frontend is found to be rogue, disable it in
+ * kthread context. Currently this is only set when
+ * netback finds out frontend sends malformed packet,
+ * but we cannot disable the interface in softirq
+ * context so we defer it here.
+ */
+ if (unlikely(vif->disabled && netif_carrier_ok(vif->dev)))
+ xenvif_carrier_off(vif);
+
if (kthread_should_stop())
break;
+ if (vif->rx_queue_purge) {
+ skb_queue_purge(&vif->rx_queue);
+ vif->rx_queue_purge = false;
+ }
+
if (!skb_queue_empty(&vif->rx_queue))
xenvif_rx_action(vif);
if (skb_queue_empty(&vif->rx_queue) &&
- netif_queue_stopped(vif->dev))
+ netif_queue_stopped(vif->dev)) {
+ del_timer_sync(&vif->wake_queue);
xenvif_start_queue(vif);
+ }
cond_resched();
}
@@ -1569,6 +1848,28 @@ int xenvif_kthread(void *data)
return 0;
}
+int xenvif_dealloc_kthread(void *data)
+{
+ struct xenvif *vif = data;
+
+ while (!kthread_should_stop()) {
+ wait_event_interruptible(vif->dealloc_wq,
+ tx_dealloc_work_todo(vif) ||
+ kthread_should_stop());
+ if (kthread_should_stop())
+ break;
+
+ xenvif_tx_dealloc_action(vif);
+ cond_resched();
+ }
+
+ /* Unmap anything remaining*/
+ if (tx_dealloc_work_todo(vif))
+ xenvif_tx_dealloc_action(vif);
+
+ return 0;
+}
+
static int __init netback_init(void)
{
int rc = 0;
@@ -1586,6 +1887,8 @@ static int __init netback_init(void)
if (rc)
goto failed_init;
+ rx_drain_timeout_jiffies = msecs_to_jiffies(rx_drain_timeout_msecs);
+
return 0;
failed_init:
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index e30d80033cbc..057b05700f8b 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -658,7 +658,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
drop:
dev->stats.tx_dropped++;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
@@ -1060,13 +1060,13 @@ static struct rtnl_link_stats64 *xennet_get_stats64(struct net_device *dev,
unsigned int start;
do {
- start = u64_stats_fetch_begin_bh(&stats->syncp);
+ start = u64_stats_fetch_begin_irq(&stats->syncp);
rx_packets = stats->rx_packets;
tx_packets = stats->tx_packets;
rx_bytes = stats->rx_bytes;
tx_bytes = stats->tx_bytes;
- } while (u64_stats_fetch_retry_bh(&stats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&stats->syncp, start));
tot->rx_packets += rx_packets;
tot->tx_packets += tx_packets;
@@ -1282,16 +1282,10 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev)
np->rx_refill_timer.function = rx_refill_timeout;
err = -ENOMEM;
- np->stats = alloc_percpu(struct netfront_stats);
+ np->stats = netdev_alloc_pcpu_stats(struct netfront_stats);
if (np->stats == NULL)
goto exit;
- for_each_possible_cpu(i) {
- struct netfront_stats *xen_nf_stats;
- xen_nf_stats = per_cpu_ptr(np->stats, i);
- u64_stats_init(&xen_nf_stats->syncp);
- }
-
/* Initialise tx_skbs as a free chain containing every entry. */
np->tx_skb_freelist = 0;
for (i = 0; i < NET_TX_RING_SIZE; i++) {
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index fe20e1cc0545..65d4ca19d132 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -26,6 +26,18 @@ config NFC_WILINK
Say Y here to compile support for Texas Instrument's NFC WiLink driver
into the kernel or say M to compile it as module.
+config NFC_TRF7970A
+ tristate "Texas Instruments TRF7970a NFC driver"
+ depends on SPI && NFC_DIGITAL
+ help
+ This option enables the NFC driver for Texas Instruments' TRF7970a
+ device. Such device supports 5 different protocols: ISO14443A,
+ ISO14443B, FeLiCa, ISO15693 and ISO18000-3.
+
+ Say Y here to compile support for TRF7970a into the kernel or
+ say M to compile it as a module. The module will be called
+ trf7970a.ko.
+
config NFC_MEI_PHY
tristate "MEI bus NFC device support"
depends on INTEL_MEI && NFC_HCI
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index 56ab822ba03d..ae42a3fa60c9 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -10,5 +10,6 @@ obj-$(CONFIG_NFC_MEI_PHY) += mei_phy.o
obj-$(CONFIG_NFC_SIM) += nfcsim.o
obj-$(CONFIG_NFC_PORT100) += port100.o
obj-$(CONFIG_NFC_MRVL) += nfcmrvl/
+obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o
ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c
index cf1a87bb74f8..d46a700a9637 100644
--- a/drivers/nfc/pn533.c
+++ b/drivers/nfc/pn533.c
@@ -55,26 +55,14 @@
NFC_PROTO_NFC_DEP_MASK)
static const struct usb_device_id pn533_table[] = {
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
- .idVendor = PN533_VENDOR_ID,
- .idProduct = PN533_PRODUCT_ID,
- .driver_info = PN533_DEVICE_STD,
- },
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
- .idVendor = SCM_VENDOR_ID,
- .idProduct = SCL3711_PRODUCT_ID,
- .driver_info = PN533_DEVICE_STD,
- },
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
- .idVendor = SONY_VENDOR_ID,
- .idProduct = PASORI_PRODUCT_ID,
- .driver_info = PN533_DEVICE_PASORI,
- },
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
- .idVendor = ACS_VENDOR_ID,
- .idProduct = ACR122U_PRODUCT_ID,
- .driver_info = PN533_DEVICE_ACR122U,
- },
+ { USB_DEVICE(PN533_VENDOR_ID, PN533_PRODUCT_ID),
+ .driver_info = PN533_DEVICE_STD },
+ { USB_DEVICE(SCM_VENDOR_ID, SCL3711_PRODUCT_ID),
+ .driver_info = PN533_DEVICE_STD },
+ { USB_DEVICE(SONY_VENDOR_ID, PASORI_PRODUCT_ID),
+ .driver_info = PN533_DEVICE_PASORI },
+ { USB_DEVICE(ACS_VENDOR_ID, ACR122U_PRODUCT_ID),
+ .driver_info = PN533_DEVICE_ACR122U },
{ }
};
MODULE_DEVICE_TABLE(usb, pn533_table);
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c
index d6185ff2f87b..f2acd85be86e 100644
--- a/drivers/nfc/pn544/i2c.c
+++ b/drivers/nfc/pn544/i2c.c
@@ -58,8 +58,19 @@ MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table);
#define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c"
+/*
+ * Exposed through the 4 most significant bytes
+ * from the HCI SW_VERSION first byte, a.k.a.
+ * SW RomLib.
+ */
+#define PN544_HW_VARIANT_C2 0xa
+#define PN544_HW_VARIANT_C3 0xb
+
+#define PN544_FW_CMD_RESET 0x01
#define PN544_FW_CMD_WRITE 0x08
#define PN544_FW_CMD_CHECK 0x06
+#define PN544_FW_CMD_SECURE_WRITE 0x0C
+#define PN544_FW_CMD_SECURE_CHUNK_WRITE 0x0D
struct pn544_i2c_fw_frame_write {
u8 cmd;
@@ -88,13 +99,31 @@ struct pn544_i2c_fw_blob {
u8 data[];
};
+struct pn544_i2c_fw_secure_frame {
+ u8 cmd;
+ u16 be_datalen;
+ u8 data[];
+} __packed;
+
+struct pn544_i2c_fw_secure_blob {
+ u64 header;
+ u8 data[];
+};
+
#define PN544_FW_CMD_RESULT_TIMEOUT 0x01
#define PN544_FW_CMD_RESULT_BAD_CRC 0x02
#define PN544_FW_CMD_RESULT_ACCESS_DENIED 0x08
#define PN544_FW_CMD_RESULT_PROTOCOL_ERROR 0x0B
#define PN544_FW_CMD_RESULT_INVALID_PARAMETER 0x11
+#define PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND 0x13
#define PN544_FW_CMD_RESULT_INVALID_LENGTH 0x18
+#define PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR 0x19
+#define PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR 0x1D
+#define PN544_FW_CMD_RESULT_MEMORY_ERROR 0x20
+#define PN544_FW_CMD_RESULT_CHUNK_OK 0x21
#define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74
+#define PN544_FW_CMD_RESULT_COMMAND_REJECTED 0xE0
+#define PN544_FW_CMD_RESULT_CHUNK_ERROR 0xE6
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
@@ -104,11 +133,17 @@ struct pn544_i2c_fw_blob {
#define PN544_FW_I2C_WRITE_DATA_MAX_LEN MIN((PN544_FW_I2C_MAX_PAYLOAD -\
PN544_FW_I2C_WRITE_FRAME_HEADER_LEN),\
PN544_FW_WRITE_BUFFER_MAX_LEN)
+#define PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN 3
+#define PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN (PN544_FW_I2C_MAX_PAYLOAD -\
+ PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN)
+#define PN544_FW_SECURE_FRAME_HEADER_LEN 3
+#define PN544_FW_SECURE_BLOB_HEADER_LEN 8
#define FW_WORK_STATE_IDLE 1
#define FW_WORK_STATE_START 2
#define FW_WORK_STATE_WAIT_WRITE_ANSWER 3
#define FW_WORK_STATE_WAIT_CHECK_ANSWER 4
+#define FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER 5
struct pn544_i2c_phy {
struct i2c_client *i2c_dev;
@@ -119,6 +154,8 @@ struct pn544_i2c_phy {
unsigned int gpio_fw;
unsigned int en_polarity;
+ u8 hw_variant;
+
struct work_struct fw_work;
int fw_work_state;
char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
@@ -127,6 +164,8 @@ struct pn544_i2c_phy {
size_t fw_blob_size;
const u8 *fw_blob_data;
size_t fw_written;
+ size_t fw_size;
+
int fw_cmd_result;
int powered;
@@ -390,6 +429,8 @@ static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy)
switch (response.status) {
case 0:
return 0;
+ case PN544_FW_CMD_RESULT_CHUNK_OK:
+ return response.status;
case PN544_FW_CMD_RESULT_TIMEOUT:
return -ETIMEDOUT;
case PN544_FW_CMD_RESULT_BAD_CRC:
@@ -400,9 +441,20 @@ static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy)
return -EPROTO;
case PN544_FW_CMD_RESULT_INVALID_PARAMETER:
return -EINVAL;
+ case PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND:
+ return -ENOTSUPP;
case PN544_FW_CMD_RESULT_INVALID_LENGTH:
return -EBADMSG;
+ case PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR:
+ return -ENOKEY;
+ case PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR:
+ return -EINVAL;
+ case PN544_FW_CMD_RESULT_MEMORY_ERROR:
+ return -ENOMEM;
+ case PN544_FW_CMD_RESULT_COMMAND_REJECTED:
+ return -EACCES;
case PN544_FW_CMD_RESULT_WRITE_FAILED:
+ case PN544_FW_CMD_RESULT_CHUNK_ERROR:
return -EIO;
default:
return -EIO;
@@ -469,7 +521,8 @@ static struct nfc_phy_ops i2c_phy_ops = {
.disable = pn544_hci_i2c_disable,
};
-static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name)
+static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name,
+ u8 hw_variant)
{
struct pn544_i2c_phy *phy = phy_id;
@@ -477,6 +530,7 @@ static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name)
strcpy(phy->firmware_name, firmware_name);
+ phy->hw_variant = hw_variant;
phy->fw_work_state = FW_WORK_STATE_START;
schedule_work(&phy->fw_work);
@@ -598,12 +652,93 @@ static int pn544_hci_i2c_fw_write_chunk(struct pn544_i2c_phy *phy)
return 0;
}
+static int pn544_hci_i2c_fw_secure_write_frame_cmd(struct pn544_i2c_phy *phy,
+ const u8 *data, u16 datalen)
+{
+ u8 buf[PN544_FW_I2C_MAX_PAYLOAD];
+ struct pn544_i2c_fw_secure_frame *chunk;
+ int chunklen;
+ int r;
+
+ if (datalen > PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN)
+ datalen = PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN;
+
+ chunk = (struct pn544_i2c_fw_secure_frame *) buf;
+
+ chunk->cmd = PN544_FW_CMD_SECURE_CHUNK_WRITE;
+
+ put_unaligned_be16(datalen, &chunk->be_datalen);
+
+ memcpy(chunk->data, data, datalen);
+
+ chunklen = sizeof(chunk->cmd) + sizeof(chunk->be_datalen) + datalen;
+
+ r = i2c_master_send(phy->i2c_dev, buf, chunklen);
+
+ if (r == chunklen)
+ return datalen;
+ else if (r < 0)
+ return r;
+ else
+ return -EIO;
+
+}
+
+static int pn544_hci_i2c_fw_secure_write_frame(struct pn544_i2c_phy *phy)
+{
+ struct pn544_i2c_fw_secure_frame *framep;
+ int r;
+
+ framep = (struct pn544_i2c_fw_secure_frame *) phy->fw_blob_data;
+ if (phy->fw_written == 0)
+ phy->fw_blob_size = get_unaligned_be16(&framep->be_datalen)
+ + PN544_FW_SECURE_FRAME_HEADER_LEN;
+
+ /* Only secure write command can be chunked*/
+ if (phy->fw_blob_size > PN544_FW_I2C_MAX_PAYLOAD &&
+ framep->cmd != PN544_FW_CMD_SECURE_WRITE)
+ return -EINVAL;
+
+ /* The firmware also have other commands, we just send them directly */
+ if (phy->fw_blob_size < PN544_FW_I2C_MAX_PAYLOAD) {
+ r = i2c_master_send(phy->i2c_dev,
+ (const char *) phy->fw_blob_data, phy->fw_blob_size);
+
+ if (r == phy->fw_blob_size)
+ goto exit;
+ else if (r < 0)
+ return r;
+ else
+ return -EIO;
+ }
+
+ r = pn544_hci_i2c_fw_secure_write_frame_cmd(phy,
+ phy->fw_blob_data + phy->fw_written,
+ phy->fw_blob_size - phy->fw_written);
+ if (r < 0)
+ return r;
+
+exit:
+ phy->fw_written += r;
+ phy->fw_work_state = FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER;
+
+ /* SW reset command will not trig any response from PN544 */
+ if (framep->cmd == PN544_FW_CMD_RESET) {
+ pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE);
+ phy->fw_cmd_result = 0;
+ schedule_work(&phy->fw_work);
+ }
+
+ return 0;
+}
+
static void pn544_hci_i2c_fw_work(struct work_struct *work)
{
struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy,
fw_work);
int r;
struct pn544_i2c_fw_blob *blob;
+ struct pn544_i2c_fw_secure_blob *secure_blob;
switch (phy->fw_work_state) {
case FW_WORK_STATE_START:
@@ -614,13 +749,29 @@ static void pn544_hci_i2c_fw_work(struct work_struct *work)
if (r < 0)
goto exit_state_start;
- blob = (struct pn544_i2c_fw_blob *) phy->fw->data;
- phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
- phy->fw_blob_dest_addr = get_unaligned_be32(&blob->be_destaddr);
- phy->fw_blob_data = blob->data;
-
phy->fw_written = 0;
- r = pn544_hci_i2c_fw_write_chunk(phy);
+
+ switch (phy->hw_variant) {
+ case PN544_HW_VARIANT_C2:
+ blob = (struct pn544_i2c_fw_blob *) phy->fw->data;
+ phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
+ phy->fw_blob_dest_addr = get_unaligned_be32(
+ &blob->be_destaddr);
+ phy->fw_blob_data = blob->data;
+
+ r = pn544_hci_i2c_fw_write_chunk(phy);
+ break;
+ case PN544_HW_VARIANT_C3:
+ secure_blob = (struct pn544_i2c_fw_secure_blob *)
+ phy->fw->data;
+ phy->fw_blob_data = secure_blob->data;
+ phy->fw_size = phy->fw->size;
+ r = pn544_hci_i2c_fw_secure_write_frame(phy);
+ break;
+ default:
+ r = -ENOTSUPP;
+ break;
+ }
exit_state_start:
if (r < 0)
@@ -672,6 +823,35 @@ exit_state_wait_check_answer:
pn544_hci_i2c_fw_work_complete(phy, r);
break;
+ case FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER:
+ r = phy->fw_cmd_result;
+ if (r < 0)
+ goto exit_state_wait_secure_write_answer;
+
+ if (r == PN544_FW_CMD_RESULT_CHUNK_OK) {
+ r = pn544_hci_i2c_fw_secure_write_frame(phy);
+ goto exit_state_wait_secure_write_answer;
+ }
+
+ if (phy->fw_written == phy->fw_blob_size) {
+ secure_blob = (struct pn544_i2c_fw_secure_blob *)
+ (phy->fw_blob_data + phy->fw_blob_size);
+ phy->fw_size -= phy->fw_blob_size +
+ PN544_FW_SECURE_BLOB_HEADER_LEN;
+ if (phy->fw_size >= PN544_FW_SECURE_BLOB_HEADER_LEN
+ + PN544_FW_SECURE_FRAME_HEADER_LEN) {
+ phy->fw_blob_data = secure_blob->data;
+
+ phy->fw_written = 0;
+ r = pn544_hci_i2c_fw_secure_write_frame(phy);
+ }
+ }
+
+exit_state_wait_secure_write_answer:
+ if (r < 0 || phy->fw_size == 0)
+ pn544_hci_i2c_fw_work_complete(phy, r);
+ break;
+
default:
break;
}
diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c
index 3df4a109cfad..9c8051d20cea 100644
--- a/drivers/nfc/pn544/pn544.c
+++ b/drivers/nfc/pn544/pn544.c
@@ -786,7 +786,7 @@ static int pn544_hci_fw_download(struct nfc_hci_dev *hdev,
if (info->fw_download == NULL)
return -ENOTSUPP;
- return info->fw_download(info->phy_id, firmware_name);
+ return info->fw_download(info->phy_id, firmware_name, hdev->sw_romlib);
}
static int pn544_hci_discover_se(struct nfc_hci_dev *hdev)
diff --git a/drivers/nfc/pn544/pn544.h b/drivers/nfc/pn544/pn544.h
index 491bf45da358..2aa9233e8086 100644
--- a/drivers/nfc/pn544/pn544.h
+++ b/drivers/nfc/pn544/pn544.h
@@ -25,7 +25,8 @@
#define PN544_HCI_MODE 0
#define PN544_FW_MODE 1
-typedef int (*fw_download_t)(void *context, const char *firmware_name);
+typedef int (*fw_download_t)(void *context, const char *firmware_name,
+ u8 hw_variant);
int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
int phy_headroom, int phy_tailroom, int phy_payload,
diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c
index a8555f81cbba..b7a372af5eb7 100644
--- a/drivers/nfc/port100.c
+++ b/drivers/nfc/port100.c
@@ -27,7 +27,8 @@
#define PORT100_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
NFC_PROTO_MIFARE_MASK | \
NFC_PROTO_FELICA_MASK | \
- NFC_PROTO_NFC_DEP_MASK)
+ NFC_PROTO_NFC_DEP_MASK | \
+ NFC_PROTO_ISO14443_MASK)
#define PORT100_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \
NFC_DIGITAL_DRV_CAPS_TG_CRC)
@@ -139,6 +140,8 @@ static const struct port100_in_rf_setting in_rf_settings[] = {
.in_recv_set_number = 15,
.in_recv_comm_type = PORT100_COMM_TYPE_IN_106A,
},
+ /* Ensures the array has NFC_DIGITAL_RF_TECH_LAST elements */
+ [NFC_DIGITAL_RF_TECH_LAST] = { 0 },
};
/**
@@ -174,6 +177,9 @@ static const struct port100_tg_rf_setting tg_rf_settings[] = {
.tg_set_number = 8,
.tg_comm_type = PORT100_COMM_TYPE_TG_424F,
},
+ /* Ensures the array has NFC_DIGITAL_RF_TECH_LAST elements */
+ [NFC_DIGITAL_RF_TECH_LAST] = { 0 },
+
};
#define PORT100_IN_PROT_INITIAL_GUARD_TIME 0x00
@@ -293,6 +299,10 @@ in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = {
{ PORT100_IN_PROT_CHECK_CRC, 0 },
{ PORT100_IN_PROT_END, 0 },
},
+ [NFC_DIGITAL_FRAMING_NFCA_T4T] = {
+ /* nfc_digital_framing_nfca_standard_with_crc_a */
+ { PORT100_IN_PROT_END, 0 },
+ },
[NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = {
/* nfc_digital_framing_nfca_standard */
{ PORT100_IN_PROT_END, 0 },
@@ -330,6 +340,10 @@ in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = {
[NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = {
{ PORT100_IN_PROT_END, 0 },
},
+ /* Ensures the array has NFC_DIGITAL_FRAMING_LAST elements */
+ [NFC_DIGITAL_FRAMING_LAST] = {
+ { PORT100_IN_PROT_END, 0 },
+ },
};
static struct port100_protocol
@@ -371,6 +385,10 @@ tg_protocols[][PORT100_TG_MAX_NUM_PROTOCOLS + 1] = {
{ PORT100_TG_PROT_RF_OFF, 1 },
{ PORT100_TG_PROT_END, 0 },
},
+ /* Ensures the array has NFC_DIGITAL_FRAMING_LAST elements */
+ [NFC_DIGITAL_FRAMING_LAST] = {
+ { PORT100_TG_PROT_END, 0 },
+ },
};
struct port100 {
@@ -1356,10 +1374,7 @@ static struct nfc_digital_ops port100_digital_ops = {
};
static const struct usb_device_id port100_table[] = {
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
- .idVendor = SONY_VENDOR_ID,
- .idProduct = RCS380_PRODUCT_ID,
- },
+ { USB_DEVICE(SONY_VENDOR_ID, RCS380_PRODUCT_ID), },
{ }
};
MODULE_DEVICE_TABLE(usb, port100_table);
diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c
new file mode 100644
index 000000000000..d9babe986473
--- /dev/null
+++ b/drivers/nfc/trf7970a.c
@@ -0,0 +1,1370 @@
+/*
+ * TI TRF7970a RFID/NFC Transceiver Driver
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Erick Macias <emacias@ti.com>
+ * Author: Felipe Balbi <balbi@ti.com>
+ * Author: Mark A. Greer <mgreer@animalcreek.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.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/nfc.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+
+#include <net/nfc/nfc.h>
+#include <net/nfc/digital.h>
+
+/* There are 3 ways the host can communicate with the trf7970a:
+ * parallel mode, SPI with Slave Select (SS) mode, and SPI without
+ * SS mode. The driver only supports the two SPI modes.
+ *
+ * The trf7970a is very timing sensitive and the VIN, EN2, and EN
+ * pins must asserted in that order and with specific delays in between.
+ * The delays used in the driver were provided by TI and have been
+ * confirmed to work with this driver.
+ *
+ * Timeouts are implemented using the delayed workqueue kernel facility.
+ * Timeouts are required so things don't hang when there is no response
+ * from the trf7970a (or tag). Using this mechanism creates a race with
+ * interrupts, however. That is, an interrupt and a timeout could occur
+ * closely enough together that one is blocked by the mutex while the other
+ * executes. When the timeout handler executes first and blocks the
+ * interrupt handler, it will eventually set the state to IDLE so the
+ * interrupt handler will check the state and exit with no harm done.
+ * When the interrupt handler executes first and blocks the timeout handler,
+ * the cancel_delayed_work() call will know that it didn't cancel the
+ * work item (i.e., timeout) and will return zero. That return code is
+ * used by the timer handler to indicate that it should ignore the timeout
+ * once its unblocked.
+ *
+ * Aborting an active command isn't as simple as it seems because the only
+ * way to abort a command that's already been sent to the tag is so turn
+ * off power to the tag. If we do that, though, we'd have to go through
+ * the entire anticollision procedure again but the digital layer doesn't
+ * support that. So, if an abort is received before trf7970a_in_send_cmd()
+ * has sent the command to the tag, it simply returns -ECANCELED. If the
+ * command has already been sent to the tag, then the driver continues
+ * normally and recieves the response data (or error) but just before
+ * sending the data upstream, it frees the rx_skb and sends -ECANCELED
+ * upstream instead. If the command failed, that error will be sent
+ * upstream.
+ *
+ * When recieving data from a tag and the interrupt status register has
+ * only the SRX bit set, it means that all of the data has been received
+ * (once what's in the fifo has been read). However, depending on timing
+ * an interrupt status with only the SRX bit set may not be recived. In
+ * those cases, the timeout mechanism is used to wait 5 ms in case more
+ * data arrives. After 5 ms, it is assumed that all of the data has been
+ * received and the accumulated rx data is sent upstream. The
+ * 'TRF7970A_ST_WAIT_FOR_RX_DATA_CONT' state is used for this purpose
+ * (i.e., it indicates that some data has been received but we're not sure
+ * if there is more coming so a timeout in this state means all data has
+ * been received and there isn't an error). The delay is 5 ms since delays
+ * over 2 ms have been observed during testing (a little extra just in case).
+ *
+ * Type 2 write and sector select commands respond with a 4-bit ACK or NACK.
+ * Having only 4 bits in the FIFO won't normally generate an interrupt so
+ * driver enables the '4_bit_RX' bit of the Special Functions register 1
+ * to cause an interrupt in that case. Leaving that bit for a read command
+ * messes up the data returned so it is only enabled when the framing is
+ * 'NFC_DIGITAL_FRAMING_NFCA_T2T' and the command is not a read command.
+ * Unfortunately, that means that the driver has to peek into tx frames
+ * when the framing is 'NFC_DIGITAL_FRAMING_NFCA_T2T'. This is done by
+ * the trf7970a_per_cmd_config() routine.
+ *
+ * ISO/IEC 15693 frames specify whether to use single or double sub-carrier
+ * frequencies and whether to use low or high data rates in the flags byte
+ * of the frame. This means that the driver has to peek at all 15693 frames
+ * to determine what speed to set the communication to. In addition, write
+ * and lock commands use the OPTION flag to indicate that an EOF must be
+ * sent to the tag before it will send its response. So the driver has to
+ * examine all frames for that reason too.
+ *
+ * It is unclear how long to wait before sending the EOF. According to the
+ * Note under Table 1-1 in section 1.6 of
+ * http://www.ti.com/lit/ug/scbu011/scbu011.pdf, that wait should be at least
+ * 10 ms for TI Tag-it HF-I tags; however testing has shown that is not long
+ * enough. For this reason, the driver waits 20 ms which seems to work
+ * reliably.
+ */
+
+#define TRF7970A_SUPPORTED_PROTOCOLS \
+ (NFC_PROTO_MIFARE_MASK | NFC_PROTO_ISO14443_MASK | \
+ NFC_PROTO_ISO15693_MASK)
+
+/* TX data must be prefixed with a FIFO reset cmd, a cmd that depends
+ * on what the current framing is, the address of the TX length byte 1
+ * register (0x1d), and the 2 byte length of the data to be transmitted.
+ * That totals 5 bytes.
+ */
+#define TRF7970A_TX_SKB_HEADROOM 5
+
+#define TRF7970A_RX_SKB_ALLOC_SIZE 256
+
+#define TRF7970A_FIFO_SIZE 128
+
+/* TX length is 3 nibbles long ==> 4KB - 1 bytes max */
+#define TRF7970A_TX_MAX (4096 - 1)
+
+#define TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT 5
+#define TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT 3
+#define TRF7970A_WAIT_TO_ISSUE_ISO15693_EOF 20
+
+/* Quirks */
+/* Erratum: When reading IRQ Status register on trf7970a, we must issue a
+ * read continuous command for IRQ Status and Collision Position registers.
+ */
+#define TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA BIT(0)
+
+/* Direct commands */
+#define TRF7970A_CMD_IDLE 0x00
+#define TRF7970A_CMD_SOFT_INIT 0x03
+#define TRF7970A_CMD_RF_COLLISION 0x04
+#define TRF7970A_CMD_RF_COLLISION_RESPONSE_N 0x05
+#define TRF7970A_CMD_RF_COLLISION_RESPONSE_0 0x06
+#define TRF7970A_CMD_FIFO_RESET 0x0f
+#define TRF7970A_CMD_TRANSMIT_NO_CRC 0x10
+#define TRF7970A_CMD_TRANSMIT 0x11
+#define TRF7970A_CMD_DELAY_TRANSMIT_NO_CRC 0x12
+#define TRF7970A_CMD_DELAY_TRANSMIT 0x13
+#define TRF7970A_CMD_EOF 0x14
+#define TRF7970A_CMD_CLOSE_SLOT 0x15
+#define TRF7970A_CMD_BLOCK_RX 0x16
+#define TRF7970A_CMD_ENABLE_RX 0x17
+#define TRF7970A_CMD_TEST_EXT_RF 0x18
+#define TRF7970A_CMD_TEST_INT_RF 0x19
+#define TRF7970A_CMD_RX_GAIN_ADJUST 0x1a
+
+/* Bits determining whether its a direct command or register R/W,
+ * whether to use a continuous SPI transaction or not, and the actual
+ * direct cmd opcode or regster address.
+ */
+#define TRF7970A_CMD_BIT_CTRL BIT(7)
+#define TRF7970A_CMD_BIT_RW BIT(6)
+#define TRF7970A_CMD_BIT_CONTINUOUS BIT(5)
+#define TRF7970A_CMD_BIT_OPCODE(opcode) ((opcode) & 0x1f)
+
+/* Registers addresses */
+#define TRF7970A_CHIP_STATUS_CTRL 0x00
+#define TRF7970A_ISO_CTRL 0x01
+#define TRF7970A_ISO14443B_TX_OPTIONS 0x02
+#define TRF7970A_ISO14443A_HIGH_BITRATE_OPTIONS 0x03
+#define TRF7970A_TX_TIMER_SETTING_H_BYTE 0x04
+#define TRF7970A_TX_TIMER_SETTING_L_BYTE 0x05
+#define TRF7970A_TX_PULSE_LENGTH_CTRL 0x06
+#define TRF7970A_RX_NO_RESPONSE_WAIT 0x07
+#define TRF7970A_RX_WAIT_TIME 0x08
+#define TRF7970A_MODULATOR_SYS_CLK_CTRL 0x09
+#define TRF7970A_RX_SPECIAL_SETTINGS 0x0a
+#define TRF7970A_REG_IO_CTRL 0x0b
+#define TRF7970A_IRQ_STATUS 0x0c
+#define TRF7970A_COLLISION_IRQ_MASK 0x0d
+#define TRF7970A_COLLISION_POSITION 0x0e
+#define TRF7970A_RSSI_OSC_STATUS 0x0f
+#define TRF7970A_SPECIAL_FCN_REG1 0x10
+#define TRF7970A_SPECIAL_FCN_REG2 0x11
+#define TRF7970A_RAM1 0x12
+#define TRF7970A_RAM2 0x13
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS 0x14
+#define TRF7970A_NFC_LOW_FIELD_LEVEL 0x16
+#define TRF7970A_NFCID1 0x17
+#define TRF7970A_NFC_TARGET_LEVEL 0x18
+#define TRF79070A_NFC_TARGET_PROTOCOL 0x19
+#define TRF7970A_TEST_REGISTER1 0x1a
+#define TRF7970A_TEST_REGISTER2 0x1b
+#define TRF7970A_FIFO_STATUS 0x1c
+#define TRF7970A_TX_LENGTH_BYTE1 0x1d
+#define TRF7970A_TX_LENGTH_BYTE2 0x1e
+#define TRF7970A_FIFO_IO_REGISTER 0x1f
+
+/* Chip Status Control Register Bits */
+#define TRF7970A_CHIP_STATUS_VRS5_3 BIT(0)
+#define TRF7970A_CHIP_STATUS_REC_ON BIT(1)
+#define TRF7970A_CHIP_STATUS_AGC_ON BIT(2)
+#define TRF7970A_CHIP_STATUS_PM_ON BIT(3)
+#define TRF7970A_CHIP_STATUS_RF_PWR BIT(4)
+#define TRF7970A_CHIP_STATUS_RF_ON BIT(5)
+#define TRF7970A_CHIP_STATUS_DIRECT BIT(6)
+#define TRF7970A_CHIP_STATUS_STBY BIT(7)
+
+/* ISO Control Register Bits */
+#define TRF7970A_ISO_CTRL_15693_SGL_1OF4_662 0x00
+#define TRF7970A_ISO_CTRL_15693_SGL_1OF256_662 0x01
+#define TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648 0x02
+#define TRF7970A_ISO_CTRL_15693_SGL_1OF256_2648 0x03
+#define TRF7970A_ISO_CTRL_15693_DBL_1OF4_667a 0x04
+#define TRF7970A_ISO_CTRL_15693_DBL_1OF256_667 0x05
+#define TRF7970A_ISO_CTRL_15693_DBL_1OF4_2669 0x06
+#define TRF7970A_ISO_CTRL_15693_DBL_1OF256_2669 0x07
+#define TRF7970A_ISO_CTRL_14443A_106 0x08
+#define TRF7970A_ISO_CTRL_14443A_212 0x09
+#define TRF7970A_ISO_CTRL_14443A_424 0x0a
+#define TRF7970A_ISO_CTRL_14443A_848 0x0b
+#define TRF7970A_ISO_CTRL_14443B_106 0x0c
+#define TRF7970A_ISO_CTRL_14443B_212 0x0d
+#define TRF7970A_ISO_CTRL_14443B_424 0x0e
+#define TRF7970A_ISO_CTRL_14443B_848 0x0f
+#define TRF7970A_ISO_CTRL_FELICA_212 0x1a
+#define TRF7970A_ISO_CTRL_FELICA_424 0x1b
+#define TRF7970A_ISO_CTRL_RFID BIT(5)
+#define TRF7970A_ISO_CTRL_DIR_MODE BIT(6)
+#define TRF7970A_ISO_CTRL_RX_CRC_N BIT(7) /* true == No CRC */
+
+#define TRF7970A_ISO_CTRL_RFID_SPEED_MASK 0x1f
+
+/* Modulator and SYS_CLK Control Register Bits */
+#define TRF7970A_MODULATOR_DEPTH(n) ((n) & 0x7)
+#define TRF7970A_MODULATOR_DEPTH_ASK10 (TRF7970A_MODULATOR_DEPTH(0))
+#define TRF7970A_MODULATOR_DEPTH_OOK (TRF7970A_MODULATOR_DEPTH(1))
+#define TRF7970A_MODULATOR_DEPTH_ASK7 (TRF7970A_MODULATOR_DEPTH(2))
+#define TRF7970A_MODULATOR_DEPTH_ASK8_5 (TRF7970A_MODULATOR_DEPTH(3))
+#define TRF7970A_MODULATOR_DEPTH_ASK13 (TRF7970A_MODULATOR_DEPTH(4))
+#define TRF7970A_MODULATOR_DEPTH_ASK16 (TRF7970A_MODULATOR_DEPTH(5))
+#define TRF7970A_MODULATOR_DEPTH_ASK22 (TRF7970A_MODULATOR_DEPTH(6))
+#define TRF7970A_MODULATOR_DEPTH_ASK30 (TRF7970A_MODULATOR_DEPTH(7))
+#define TRF7970A_MODULATOR_EN_ANA BIT(3)
+#define TRF7970A_MODULATOR_CLK(n) (((n) & 0x3) << 4)
+#define TRF7970A_MODULATOR_CLK_DISABLED (TRF7970A_MODULATOR_CLK(0))
+#define TRF7970A_MODULATOR_CLK_3_6 (TRF7970A_MODULATOR_CLK(1))
+#define TRF7970A_MODULATOR_CLK_6_13 (TRF7970A_MODULATOR_CLK(2))
+#define TRF7970A_MODULATOR_CLK_13_27 (TRF7970A_MODULATOR_CLK(3))
+#define TRF7970A_MODULATOR_EN_OOK BIT(6)
+#define TRF7970A_MODULATOR_27MHZ BIT(7)
+
+/* IRQ Status Register Bits */
+#define TRF7970A_IRQ_STATUS_NORESP BIT(0) /* ISO15693 only */
+#define TRF7970A_IRQ_STATUS_COL BIT(1)
+#define TRF7970A_IRQ_STATUS_FRAMING_EOF_ERROR BIT(2)
+#define TRF7970A_IRQ_STATUS_PARITY_ERROR BIT(3)
+#define TRF7970A_IRQ_STATUS_CRC_ERROR BIT(4)
+#define TRF7970A_IRQ_STATUS_FIFO BIT(5)
+#define TRF7970A_IRQ_STATUS_SRX BIT(6)
+#define TRF7970A_IRQ_STATUS_TX BIT(7)
+
+#define TRF7970A_IRQ_STATUS_ERROR \
+ (TRF7970A_IRQ_STATUS_COL | \
+ TRF7970A_IRQ_STATUS_FRAMING_EOF_ERROR | \
+ TRF7970A_IRQ_STATUS_PARITY_ERROR | \
+ TRF7970A_IRQ_STATUS_CRC_ERROR)
+
+#define TRF7970A_SPECIAL_FCN_REG1_COL_7_6 BIT(0)
+#define TRF7970A_SPECIAL_FCN_REG1_14_ANTICOLL BIT(1)
+#define TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX BIT(2)
+#define TRF7970A_SPECIAL_FCN_REG1_SP_DIR_MODE BIT(3)
+#define TRF7970A_SPECIAL_FCN_REG1_NEXT_SLOT_37US BIT(4)
+#define TRF7970A_SPECIAL_FCN_REG1_PAR43 BIT(5)
+
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_124 (0x0 << 2)
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_120 (0x1 << 2)
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_112 (0x2 << 2)
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_96 (0x3 << 2)
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_4 0x0
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_8 0x1
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_16 0x2
+#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_32 0x3
+
+#define TRF7970A_FIFO_STATUS_OVERFLOW BIT(7)
+
+/* NFC (ISO/IEC 14443A) Type 2 Tag commands */
+#define NFC_T2T_CMD_READ 0x30
+
+/* ISO 15693 commands codes */
+#define ISO15693_CMD_INVENTORY 0x01
+#define ISO15693_CMD_READ_SINGLE_BLOCK 0x20
+#define ISO15693_CMD_WRITE_SINGLE_BLOCK 0x21
+#define ISO15693_CMD_LOCK_BLOCK 0x22
+#define ISO15693_CMD_READ_MULTIPLE_BLOCK 0x23
+#define ISO15693_CMD_WRITE_MULTIPLE_BLOCK 0x24
+#define ISO15693_CMD_SELECT 0x25
+#define ISO15693_CMD_RESET_TO_READY 0x26
+#define ISO15693_CMD_WRITE_AFI 0x27
+#define ISO15693_CMD_LOCK_AFI 0x28
+#define ISO15693_CMD_WRITE_DSFID 0x29
+#define ISO15693_CMD_LOCK_DSFID 0x2a
+#define ISO15693_CMD_GET_SYSTEM_INFO 0x2b
+#define ISO15693_CMD_GET_MULTIPLE_BLOCK_SECURITY_STATUS 0x2c
+
+/* ISO 15693 request and response flags */
+#define ISO15693_REQ_FLAG_SUB_CARRIER BIT(0)
+#define ISO15693_REQ_FLAG_DATA_RATE BIT(1)
+#define ISO15693_REQ_FLAG_INVENTORY BIT(2)
+#define ISO15693_REQ_FLAG_PROTOCOL_EXT BIT(3)
+#define ISO15693_REQ_FLAG_SELECT BIT(4)
+#define ISO15693_REQ_FLAG_AFI BIT(4)
+#define ISO15693_REQ_FLAG_ADDRESS BIT(5)
+#define ISO15693_REQ_FLAG_NB_SLOTS BIT(5)
+#define ISO15693_REQ_FLAG_OPTION BIT(6)
+
+#define ISO15693_REQ_FLAG_SPEED_MASK \
+ (ISO15693_REQ_FLAG_SUB_CARRIER | ISO15693_REQ_FLAG_DATA_RATE)
+
+enum trf7970a_state {
+ TRF7970A_ST_OFF,
+ TRF7970A_ST_IDLE,
+ TRF7970A_ST_IDLE_RX_BLOCKED,
+ TRF7970A_ST_WAIT_FOR_TX_FIFO,
+ TRF7970A_ST_WAIT_FOR_RX_DATA,
+ TRF7970A_ST_WAIT_FOR_RX_DATA_CONT,
+ TRF7970A_ST_WAIT_TO_ISSUE_EOF,
+ TRF7970A_ST_MAX
+};
+
+struct trf7970a {
+ enum trf7970a_state state;
+ struct device *dev;
+ struct spi_device *spi;
+ struct regulator *regulator;
+ struct nfc_digital_dev *ddev;
+ u32 quirks;
+ bool powering_up;
+ bool aborting;
+ struct sk_buff *tx_skb;
+ struct sk_buff *rx_skb;
+ nfc_digital_cmd_complete_t cb;
+ void *cb_arg;
+ u8 iso_ctrl;
+ u8 special_fcn_reg1;
+ int technology;
+ int framing;
+ u8 tx_cmd;
+ bool issue_eof;
+ int en2_gpio;
+ int en_gpio;
+ struct mutex lock;
+ unsigned int timeout;
+ bool ignore_timeout;
+ struct delayed_work timeout_work;
+};
+
+
+static int trf7970a_cmd(struct trf7970a *trf, u8 opcode)
+{
+ u8 cmd = TRF7970A_CMD_BIT_CTRL | TRF7970A_CMD_BIT_OPCODE(opcode);
+ int ret;
+
+ dev_dbg(trf->dev, "cmd: 0x%x\n", cmd);
+
+ ret = spi_write(trf->spi, &cmd, 1);
+ if (ret)
+ dev_err(trf->dev, "%s - cmd: 0x%x, ret: %d\n", __func__, cmd,
+ ret);
+ return ret;
+}
+
+static int trf7970a_read(struct trf7970a *trf, u8 reg, u8 *val)
+{
+ u8 addr = TRF7970A_CMD_BIT_RW | reg;
+ int ret;
+
+ ret = spi_write_then_read(trf->spi, &addr, 1, val, 1);
+ if (ret)
+ dev_err(trf->dev, "%s - addr: 0x%x, ret: %d\n", __func__, addr,
+ ret);
+
+ dev_dbg(trf->dev, "read(0x%x): 0x%x\n", addr, *val);
+
+ return ret;
+}
+
+static int trf7970a_read_cont(struct trf7970a *trf, u8 reg,
+ u8 *buf, size_t len)
+{
+ u8 addr = reg | TRF7970A_CMD_BIT_RW | TRF7970A_CMD_BIT_CONTINUOUS;
+ int ret;
+
+ dev_dbg(trf->dev, "read_cont(0x%x, %zd)\n", addr, len);
+
+ ret = spi_write_then_read(trf->spi, &addr, 1, buf, len);
+ if (ret)
+ dev_err(trf->dev, "%s - addr: 0x%x, ret: %d\n", __func__, addr,
+ ret);
+ return ret;
+}
+
+static int trf7970a_write(struct trf7970a *trf, u8 reg, u8 val)
+{
+ u8 buf[2] = { reg, val };
+ int ret;
+
+ dev_dbg(trf->dev, "write(0x%x): 0x%x\n", reg, val);
+
+ ret = spi_write(trf->spi, buf, 2);
+ if (ret)
+ dev_err(trf->dev, "%s - write: 0x%x 0x%x, ret: %d\n", __func__,
+ buf[0], buf[1], ret);
+
+ return ret;
+}
+
+static int trf7970a_read_irqstatus(struct trf7970a *trf, u8 *status)
+{
+ int ret;
+ u8 buf[2];
+ u8 addr;
+
+ addr = TRF7970A_IRQ_STATUS | TRF7970A_CMD_BIT_RW;
+
+ if (trf->quirks & TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA) {
+ addr |= TRF7970A_CMD_BIT_CONTINUOUS;
+ ret = spi_write_then_read(trf->spi, &addr, 1, buf, 2);
+ } else {
+ ret = spi_write_then_read(trf->spi, &addr, 1, buf, 1);
+ }
+
+ if (ret)
+ dev_err(trf->dev, "%s - irqstatus: Status read failed: %d\n",
+ __func__, ret);
+ else
+ *status = buf[0];
+
+ return ret;
+}
+
+static void trf7970a_send_upstream(struct trf7970a *trf)
+{
+ u8 rssi;
+
+ dev_kfree_skb_any(trf->tx_skb);
+ trf->tx_skb = NULL;
+
+ if (trf->rx_skb && !IS_ERR(trf->rx_skb) && !trf->aborting)
+ print_hex_dump_debug("trf7970a rx data: ", DUMP_PREFIX_NONE,
+ 16, 1, trf->rx_skb->data, trf->rx_skb->len,
+ false);
+
+ /* According to the manual it is "good form" to reset the fifo and
+ * read the RSSI levels & oscillator status register here. It doesn't
+ * explain why.
+ */
+ trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET);
+ trf7970a_read(trf, TRF7970A_RSSI_OSC_STATUS, &rssi);
+
+ trf->state = TRF7970A_ST_IDLE;
+
+ if (trf->aborting) {
+ dev_dbg(trf->dev, "Abort process complete\n");
+
+ if (!IS_ERR(trf->rx_skb)) {
+ kfree_skb(trf->rx_skb);
+ trf->rx_skb = ERR_PTR(-ECANCELED);
+ }
+
+ trf->aborting = false;
+ }
+
+ trf->cb(trf->ddev, trf->cb_arg, trf->rx_skb);
+
+ trf->rx_skb = NULL;
+}
+
+static void trf7970a_send_err_upstream(struct trf7970a *trf, int errno)
+{
+ dev_dbg(trf->dev, "Error - state: %d, errno: %d\n", trf->state, errno);
+
+ kfree_skb(trf->rx_skb);
+ trf->rx_skb = ERR_PTR(errno);
+
+ trf7970a_send_upstream(trf);
+}
+
+static int trf7970a_transmit(struct trf7970a *trf, struct sk_buff *skb,
+ unsigned int len)
+{
+ unsigned int timeout;
+ int ret;
+
+ print_hex_dump_debug("trf7970a tx data: ", DUMP_PREFIX_NONE,
+ 16, 1, skb->data, len, false);
+
+ ret = spi_write(trf->spi, skb->data, len);
+ if (ret) {
+ dev_err(trf->dev, "%s - Can't send tx data: %d\n", __func__,
+ ret);
+ return ret;
+ }
+
+ skb_pull(skb, len);
+
+ if (skb->len > 0) {
+ trf->state = TRF7970A_ST_WAIT_FOR_TX_FIFO;
+ timeout = TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT;
+ } else {
+ if (trf->issue_eof) {
+ trf->state = TRF7970A_ST_WAIT_TO_ISSUE_EOF;
+ timeout = TRF7970A_WAIT_TO_ISSUE_ISO15693_EOF;
+ } else {
+ trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA;
+ timeout = trf->timeout;
+ }
+ }
+
+ dev_dbg(trf->dev, "Setting timeout for %d ms, state: %d\n", timeout,
+ trf->state);
+
+ schedule_delayed_work(&trf->timeout_work, msecs_to_jiffies(timeout));
+
+ return 0;
+}
+
+static void trf7970a_fill_fifo(struct trf7970a *trf)
+{
+ struct sk_buff *skb = trf->tx_skb;
+ unsigned int len;
+ int ret;
+ u8 fifo_bytes;
+
+ ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, &fifo_bytes);
+ if (ret) {
+ trf7970a_send_err_upstream(trf, ret);
+ return;
+ }
+
+ dev_dbg(trf->dev, "Filling FIFO - fifo_bytes: 0x%x\n", fifo_bytes);
+
+ if (fifo_bytes & TRF7970A_FIFO_STATUS_OVERFLOW) {
+ dev_err(trf->dev, "%s - fifo overflow: 0x%x\n", __func__,
+ fifo_bytes);
+ trf7970a_send_err_upstream(trf, -EIO);
+ return;
+ }
+
+ /* Calculate how much more data can be written to the fifo */
+ len = TRF7970A_FIFO_SIZE - fifo_bytes;
+ len = min(skb->len, len);
+
+ ret = trf7970a_transmit(trf, skb, len);
+ if (ret)
+ trf7970a_send_err_upstream(trf, ret);
+}
+
+static void trf7970a_drain_fifo(struct trf7970a *trf, u8 status)
+{
+ struct sk_buff *skb = trf->rx_skb;
+ int ret;
+ u8 fifo_bytes;
+
+ if (status & TRF7970A_IRQ_STATUS_ERROR) {
+ trf7970a_send_err_upstream(trf, -EIO);
+ return;
+ }
+
+ ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, &fifo_bytes);
+ if (ret) {
+ trf7970a_send_err_upstream(trf, ret);
+ return;
+ }
+
+ dev_dbg(trf->dev, "Draining FIFO - fifo_bytes: 0x%x\n", fifo_bytes);
+
+ if (!fifo_bytes)
+ goto no_rx_data;
+
+ if (fifo_bytes & TRF7970A_FIFO_STATUS_OVERFLOW) {
+ dev_err(trf->dev, "%s - fifo overflow: 0x%x\n", __func__,
+ fifo_bytes);
+ trf7970a_send_err_upstream(trf, -EIO);
+ return;
+ }
+
+ if (fifo_bytes > skb_tailroom(skb)) {
+ skb = skb_copy_expand(skb, skb_headroom(skb),
+ max_t(int, fifo_bytes,
+ TRF7970A_RX_SKB_ALLOC_SIZE),
+ GFP_KERNEL);
+ if (!skb) {
+ trf7970a_send_err_upstream(trf, -ENOMEM);
+ return;
+ }
+
+ kfree_skb(trf->rx_skb);
+ trf->rx_skb = skb;
+ }
+
+ ret = trf7970a_read_cont(trf, TRF7970A_FIFO_IO_REGISTER,
+ skb_put(skb, fifo_bytes), fifo_bytes);
+ if (ret) {
+ trf7970a_send_err_upstream(trf, ret);
+ return;
+ }
+
+ /* If received Type 2 ACK/NACK, shift right 4 bits and pass up */
+ if ((trf->framing == NFC_DIGITAL_FRAMING_NFCA_T2T) && (skb->len == 1) &&
+ (trf->special_fcn_reg1 ==
+ TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX)) {
+ skb->data[0] >>= 4;
+ status = TRF7970A_IRQ_STATUS_SRX;
+ } else {
+ trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA_CONT;
+ }
+
+no_rx_data:
+ if (status == TRF7970A_IRQ_STATUS_SRX) { /* Receive complete */
+ trf7970a_send_upstream(trf);
+ return;
+ }
+
+ dev_dbg(trf->dev, "Setting timeout for %d ms\n",
+ TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT);
+
+ schedule_delayed_work(&trf->timeout_work,
+ msecs_to_jiffies(TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT));
+}
+
+static irqreturn_t trf7970a_irq(int irq, void *dev_id)
+{
+ struct trf7970a *trf = dev_id;
+ int ret;
+ u8 status;
+
+ mutex_lock(&trf->lock);
+
+ if (trf->state == TRF7970A_ST_OFF) {
+ mutex_unlock(&trf->lock);
+ return IRQ_NONE;
+ }
+
+ ret = trf7970a_read_irqstatus(trf, &status);
+ if (ret) {
+ mutex_unlock(&trf->lock);
+ return IRQ_NONE;
+ }
+
+ dev_dbg(trf->dev, "IRQ - state: %d, status: 0x%x\n", trf->state,
+ status);
+
+ if (!status) {
+ mutex_unlock(&trf->lock);
+ return IRQ_NONE;
+ }
+
+ switch (trf->state) {
+ case TRF7970A_ST_IDLE:
+ case TRF7970A_ST_IDLE_RX_BLOCKED:
+ /* If getting interrupts caused by RF noise, turn off the
+ * receiver to avoid unnecessary interrupts. It will be
+ * turned back on in trf7970a_in_send_cmd() when the next
+ * command is issued.
+ */
+ if (status & TRF7970A_IRQ_STATUS_ERROR) {
+ trf7970a_cmd(trf, TRF7970A_CMD_BLOCK_RX);
+ trf->state = TRF7970A_ST_IDLE_RX_BLOCKED;
+ }
+
+ trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET);
+ break;
+ case TRF7970A_ST_WAIT_FOR_TX_FIFO:
+ if (status & TRF7970A_IRQ_STATUS_TX) {
+ trf->ignore_timeout =
+ !cancel_delayed_work(&trf->timeout_work);
+ trf7970a_fill_fifo(trf);
+ } else {
+ trf7970a_send_err_upstream(trf, -EIO);
+ }
+ break;
+ case TRF7970A_ST_WAIT_FOR_RX_DATA:
+ case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
+ if (status & TRF7970A_IRQ_STATUS_SRX) {
+ trf->ignore_timeout =
+ !cancel_delayed_work(&trf->timeout_work);
+ trf7970a_drain_fifo(trf, status);
+ } else if (!(status & TRF7970A_IRQ_STATUS_TX)) {
+ trf7970a_send_err_upstream(trf, -EIO);
+ }
+ break;
+ case TRF7970A_ST_WAIT_TO_ISSUE_EOF:
+ if (status != TRF7970A_IRQ_STATUS_TX)
+ trf7970a_send_err_upstream(trf, -EIO);
+ break;
+ default:
+ dev_err(trf->dev, "%s - Driver in invalid state: %d\n",
+ __func__, trf->state);
+ }
+
+ mutex_unlock(&trf->lock);
+ return IRQ_HANDLED;
+}
+
+static void trf7970a_issue_eof(struct trf7970a *trf)
+{
+ int ret;
+
+ dev_dbg(trf->dev, "Issuing EOF\n");
+
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET);
+ if (ret)
+ trf7970a_send_err_upstream(trf, ret);
+
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_EOF);
+ if (ret)
+ trf7970a_send_err_upstream(trf, ret);
+
+ trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA;
+
+ dev_dbg(trf->dev, "Setting timeout for %d ms, state: %d\n",
+ trf->timeout, trf->state);
+
+ schedule_delayed_work(&trf->timeout_work,
+ msecs_to_jiffies(trf->timeout));
+}
+
+static void trf7970a_timeout_work_handler(struct work_struct *work)
+{
+ struct trf7970a *trf = container_of(work, struct trf7970a,
+ timeout_work.work);
+
+ dev_dbg(trf->dev, "Timeout - state: %d, ignore_timeout: %d\n",
+ trf->state, trf->ignore_timeout);
+
+ mutex_lock(&trf->lock);
+
+ if (trf->ignore_timeout)
+ trf->ignore_timeout = false;
+ else if (trf->state == TRF7970A_ST_WAIT_FOR_RX_DATA_CONT)
+ trf7970a_send_upstream(trf); /* No more rx data so send up */
+ else if (trf->state == TRF7970A_ST_WAIT_TO_ISSUE_EOF)
+ trf7970a_issue_eof(trf);
+ else
+ trf7970a_send_err_upstream(trf, -ETIMEDOUT);
+
+ mutex_unlock(&trf->lock);
+}
+
+static int trf7970a_init(struct trf7970a *trf)
+{
+ int ret;
+
+ dev_dbg(trf->dev, "Initializing device - state: %d\n", trf->state);
+
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_SOFT_INIT);
+ if (ret)
+ goto err_out;
+
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_IDLE);
+ if (ret)
+ goto err_out;
+
+ ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL,
+ TRF7970A_MODULATOR_DEPTH_OOK);
+ if (ret)
+ goto err_out;
+
+ ret = trf7970a_write(trf, TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS,
+ TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_96 |
+ TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_32);
+ if (ret)
+ goto err_out;
+
+ ret = trf7970a_write(trf, TRF7970A_SPECIAL_FCN_REG1, 0);
+ if (ret)
+ goto err_out;
+
+ trf->special_fcn_reg1 = 0;
+
+ ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL,
+ TRF7970A_CHIP_STATUS_RF_ON |
+ TRF7970A_CHIP_STATUS_VRS5_3);
+ if (ret)
+ goto err_out;
+
+ return 0;
+
+err_out:
+ dev_dbg(trf->dev, "Couldn't init device: %d\n", ret);
+ return ret;
+}
+
+static void trf7970a_switch_rf_off(struct trf7970a *trf)
+{
+ dev_dbg(trf->dev, "Switching rf off\n");
+
+ gpio_set_value(trf->en_gpio, 0);
+ gpio_set_value(trf->en2_gpio, 0);
+
+ trf->aborting = false;
+ trf->state = TRF7970A_ST_OFF;
+}
+
+static int trf7970a_switch_rf_on(struct trf7970a *trf)
+{
+ unsigned long delay;
+ int ret;
+
+ dev_dbg(trf->dev, "Switching rf on\n");
+
+ if (trf->powering_up)
+ usleep_range(5000, 6000);
+
+ gpio_set_value(trf->en2_gpio, 1);
+ usleep_range(1000, 2000);
+ gpio_set_value(trf->en_gpio, 1);
+
+ /* The delay between enabling the trf7970a and issuing the first
+ * command is significantly longer the very first time after powering
+ * up. Make sure the longer delay is only done the first time.
+ */
+ if (trf->powering_up) {
+ delay = 20000;
+ trf->powering_up = false;
+ } else {
+ delay = 5000;
+ }
+
+ usleep_range(delay, delay + 1000);
+
+ ret = trf7970a_init(trf);
+ if (ret)
+ trf7970a_switch_rf_off(trf);
+ else
+ trf->state = TRF7970A_ST_IDLE;
+
+ return ret;
+}
+
+static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+ int ret = 0;
+
+ dev_dbg(trf->dev, "Switching RF - state: %d, on: %d\n", trf->state, on);
+
+ mutex_lock(&trf->lock);
+
+ if (on) {
+ switch (trf->state) {
+ case TRF7970A_ST_OFF:
+ ret = trf7970a_switch_rf_on(trf);
+ break;
+ case TRF7970A_ST_IDLE:
+ case TRF7970A_ST_IDLE_RX_BLOCKED:
+ break;
+ default:
+ dev_err(trf->dev, "%s - Invalid request: %d %d\n",
+ __func__, trf->state, on);
+ trf7970a_switch_rf_off(trf);
+ }
+ } else {
+ switch (trf->state) {
+ case TRF7970A_ST_OFF:
+ break;
+ default:
+ dev_err(trf->dev, "%s - Invalid request: %d %d\n",
+ __func__, trf->state, on);
+ /* FALLTHROUGH */
+ case TRF7970A_ST_IDLE:
+ case TRF7970A_ST_IDLE_RX_BLOCKED:
+ trf7970a_switch_rf_off(trf);
+ }
+ }
+
+ mutex_unlock(&trf->lock);
+ return ret;
+}
+
+static int trf7970a_config_rf_tech(struct trf7970a *trf, int tech)
+{
+ int ret = 0;
+
+ dev_dbg(trf->dev, "rf technology: %d\n", tech);
+
+ switch (tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ trf->iso_ctrl = TRF7970A_ISO_CTRL_14443A_106;
+ break;
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ trf->iso_ctrl = TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648;
+ break;
+ default:
+ dev_dbg(trf->dev, "Unsupported rf technology: %d\n", tech);
+ return -EINVAL;
+ }
+
+ trf->technology = tech;
+
+ return ret;
+}
+
+static int trf7970a_config_framing(struct trf7970a *trf, int framing)
+{
+ dev_dbg(trf->dev, "framing: %d\n", framing);
+
+ switch (framing) {
+ case NFC_DIGITAL_FRAMING_NFCA_SHORT:
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD:
+ trf->tx_cmd = TRF7970A_CMD_TRANSMIT_NO_CRC;
+ trf->iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A:
+ case NFC_DIGITAL_FRAMING_NFCA_T4T:
+ case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY:
+ case NFC_DIGITAL_FRAMING_ISO15693_T5T:
+ trf->tx_cmd = TRF7970A_CMD_TRANSMIT;
+ trf->iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_T2T:
+ trf->tx_cmd = TRF7970A_CMD_TRANSMIT;
+ trf->iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
+ break;
+ default:
+ dev_dbg(trf->dev, "Unsupported Framing: %d\n", framing);
+ return -EINVAL;
+ }
+
+ trf->framing = framing;
+
+ return trf7970a_write(trf, TRF7970A_ISO_CTRL, trf->iso_ctrl);
+}
+
+static int trf7970a_in_configure_hw(struct nfc_digital_dev *ddev, int type,
+ int param)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+ int ret = 0;
+
+ dev_dbg(trf->dev, "Configure hw - type: %d, param: %d\n", type, param);
+
+ mutex_lock(&trf->lock);
+
+ if (trf->state == TRF7970A_ST_OFF) {
+ ret = trf7970a_switch_rf_on(trf);
+ if (ret)
+ goto err_out;
+ }
+
+ switch (type) {
+ case NFC_DIGITAL_CONFIG_RF_TECH:
+ ret = trf7970a_config_rf_tech(trf, param);
+ break;
+ case NFC_DIGITAL_CONFIG_FRAMING:
+ ret = trf7970a_config_framing(trf, param);
+ break;
+ default:
+ dev_dbg(trf->dev, "Unknown type: %d\n", type);
+ ret = -EINVAL;
+ }
+
+err_out:
+ mutex_unlock(&trf->lock);
+ return ret;
+}
+
+static int trf7970a_is_iso15693_write_or_lock(u8 cmd)
+{
+ switch (cmd) {
+ case ISO15693_CMD_WRITE_SINGLE_BLOCK:
+ case ISO15693_CMD_LOCK_BLOCK:
+ case ISO15693_CMD_WRITE_MULTIPLE_BLOCK:
+ case ISO15693_CMD_WRITE_AFI:
+ case ISO15693_CMD_LOCK_AFI:
+ case ISO15693_CMD_WRITE_DSFID:
+ case ISO15693_CMD_LOCK_DSFID:
+ return 1;
+ break;
+ default:
+ return 0;
+ }
+}
+
+static int trf7970a_per_cmd_config(struct trf7970a *trf, struct sk_buff *skb)
+{
+ u8 *req = skb->data;
+ u8 special_fcn_reg1, iso_ctrl;
+ int ret;
+
+ trf->issue_eof = false;
+
+ /* When issuing Type 2 read command, make sure the '4_bit_RX' bit in
+ * special functions register 1 is cleared; otherwise, its a write or
+ * sector select command and '4_bit_RX' must be set.
+ *
+ * When issuing an ISO 15693 command, inspect the flags byte to see
+ * what speed to use. Also, remember if the OPTION flag is set on
+ * a Type 5 write or lock command so the driver will know that it
+ * has to send an EOF in order to get a response.
+ */
+ if ((trf->technology == NFC_DIGITAL_RF_TECH_106A) &&
+ (trf->framing == NFC_DIGITAL_FRAMING_NFCA_T2T)) {
+ if (req[0] == NFC_T2T_CMD_READ)
+ special_fcn_reg1 = 0;
+ else
+ special_fcn_reg1 = TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX;
+
+ if (special_fcn_reg1 != trf->special_fcn_reg1) {
+ ret = trf7970a_write(trf, TRF7970A_SPECIAL_FCN_REG1,
+ special_fcn_reg1);
+ if (ret)
+ return ret;
+
+ trf->special_fcn_reg1 = special_fcn_reg1;
+ }
+ } else if (trf->technology == NFC_DIGITAL_RF_TECH_ISO15693) {
+ iso_ctrl = trf->iso_ctrl & ~TRF7970A_ISO_CTRL_RFID_SPEED_MASK;
+
+ switch (req[0] & ISO15693_REQ_FLAG_SPEED_MASK) {
+ case 0x00:
+ iso_ctrl |= TRF7970A_ISO_CTRL_15693_SGL_1OF4_662;
+ break;
+ case ISO15693_REQ_FLAG_SUB_CARRIER:
+ iso_ctrl |= TRF7970A_ISO_CTRL_15693_DBL_1OF4_667a;
+ break;
+ case ISO15693_REQ_FLAG_DATA_RATE:
+ iso_ctrl |= TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648;
+ break;
+ case (ISO15693_REQ_FLAG_SUB_CARRIER |
+ ISO15693_REQ_FLAG_DATA_RATE):
+ iso_ctrl |= TRF7970A_ISO_CTRL_15693_DBL_1OF4_2669;
+ break;
+ }
+
+ if (iso_ctrl != trf->iso_ctrl) {
+ ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, iso_ctrl);
+ if (ret)
+ return ret;
+
+ trf->iso_ctrl = iso_ctrl;
+ }
+
+ if ((trf->framing == NFC_DIGITAL_FRAMING_ISO15693_T5T) &&
+ trf7970a_is_iso15693_write_or_lock(req[1]) &&
+ (req[0] & ISO15693_REQ_FLAG_OPTION))
+ trf->issue_eof = true;
+ }
+
+ return 0;
+}
+
+static int trf7970a_in_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb, u16 timeout,
+ nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+ char *prefix;
+ unsigned int len;
+ int ret;
+
+ dev_dbg(trf->dev, "New request - state: %d, timeout: %d ms, len: %d\n",
+ trf->state, timeout, skb->len);
+
+ if (skb->len > TRF7970A_TX_MAX)
+ return -EINVAL;
+
+ mutex_lock(&trf->lock);
+
+ if ((trf->state != TRF7970A_ST_IDLE) &&
+ (trf->state != TRF7970A_ST_IDLE_RX_BLOCKED)) {
+ dev_err(trf->dev, "%s - Bogus state: %d\n", __func__,
+ trf->state);
+ ret = -EIO;
+ goto out_err;
+ }
+
+ if (trf->aborting) {
+ dev_dbg(trf->dev, "Abort process complete\n");
+ trf->aborting = false;
+ ret = -ECANCELED;
+ goto out_err;
+ }
+
+ trf->rx_skb = nfc_alloc_recv_skb(TRF7970A_RX_SKB_ALLOC_SIZE,
+ GFP_KERNEL);
+ if (!trf->rx_skb) {
+ dev_dbg(trf->dev, "Can't alloc rx_skb\n");
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ if (trf->state == TRF7970A_ST_IDLE_RX_BLOCKED) {
+ ret = trf7970a_cmd(trf, TRF7970A_CMD_ENABLE_RX);
+ if (ret)
+ goto out_err;
+
+ trf->state = TRF7970A_ST_IDLE;
+ }
+
+ ret = trf7970a_per_cmd_config(trf, skb);
+ if (ret)
+ goto out_err;
+
+ trf->ddev = ddev;
+ trf->tx_skb = skb;
+ trf->cb = cb;
+ trf->cb_arg = arg;
+ trf->timeout = timeout;
+ trf->ignore_timeout = false;
+
+ len = skb->len;
+ prefix = skb_push(skb, TRF7970A_TX_SKB_HEADROOM);
+
+ /* TX data must be prefixed with a FIFO reset cmd, a cmd that depends
+ * on what the current framing is, the address of the TX length byte 1
+ * register (0x1d), and the 2 byte length of the data to be transmitted.
+ */
+ prefix[0] = TRF7970A_CMD_BIT_CTRL |
+ TRF7970A_CMD_BIT_OPCODE(TRF7970A_CMD_FIFO_RESET);
+ prefix[1] = TRF7970A_CMD_BIT_CTRL |
+ TRF7970A_CMD_BIT_OPCODE(trf->tx_cmd);
+ prefix[2] = TRF7970A_CMD_BIT_CONTINUOUS | TRF7970A_TX_LENGTH_BYTE1;
+
+ if (trf->framing == NFC_DIGITAL_FRAMING_NFCA_SHORT) {
+ prefix[3] = 0x00;
+ prefix[4] = 0x0f; /* 7 bits */
+ } else {
+ prefix[3] = (len & 0xf00) >> 4;
+ prefix[3] |= ((len & 0xf0) >> 4);
+ prefix[4] = ((len & 0x0f) << 4);
+ }
+
+ len = min_t(int, skb->len, TRF7970A_FIFO_SIZE);
+
+ usleep_range(1000, 2000);
+
+ ret = trf7970a_transmit(trf, skb, len);
+ if (ret) {
+ kfree_skb(trf->rx_skb);
+ trf->rx_skb = NULL;
+ }
+
+out_err:
+ mutex_unlock(&trf->lock);
+ return ret;
+}
+
+static int trf7970a_tg_configure_hw(struct nfc_digital_dev *ddev,
+ int type, int param)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+
+ dev_dbg(trf->dev, "Unsupported interface\n");
+
+ return -EINVAL;
+}
+
+static int trf7970a_tg_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb, u16 timeout,
+ nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+
+ dev_dbg(trf->dev, "Unsupported interface\n");
+
+ return -EINVAL;
+}
+
+static int trf7970a_tg_listen(struct nfc_digital_dev *ddev,
+ u16 timeout, nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+
+ dev_dbg(trf->dev, "Unsupported interface\n");
+
+ return -EINVAL;
+}
+
+static int trf7970a_tg_listen_mdaa(struct nfc_digital_dev *ddev,
+ struct digital_tg_mdaa_params *mdaa_params,
+ u16 timeout, nfc_digital_cmd_complete_t cb, void *arg)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+
+ dev_dbg(trf->dev, "Unsupported interface\n");
+
+ return -EINVAL;
+}
+
+static void trf7970a_abort_cmd(struct nfc_digital_dev *ddev)
+{
+ struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
+
+ dev_dbg(trf->dev, "Abort process initiated\n");
+
+ mutex_lock(&trf->lock);
+ trf->aborting = true;
+ mutex_unlock(&trf->lock);
+}
+
+static struct nfc_digital_ops trf7970a_nfc_ops = {
+ .in_configure_hw = trf7970a_in_configure_hw,
+ .in_send_cmd = trf7970a_in_send_cmd,
+ .tg_configure_hw = trf7970a_tg_configure_hw,
+ .tg_send_cmd = trf7970a_tg_send_cmd,
+ .tg_listen = trf7970a_tg_listen,
+ .tg_listen_mdaa = trf7970a_tg_listen_mdaa,
+ .switch_rf = trf7970a_switch_rf,
+ .abort_cmd = trf7970a_abort_cmd,
+};
+
+static int trf7970a_probe(struct spi_device *spi)
+{
+ struct device_node *np = spi->dev.of_node;
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct trf7970a *trf;
+ int ret;
+
+ if (!np) {
+ dev_err(&spi->dev, "No Device Tree entry\n");
+ return -EINVAL;
+ }
+
+ trf = devm_kzalloc(&spi->dev, sizeof(*trf), GFP_KERNEL);
+ if (!trf)
+ return -ENOMEM;
+
+ trf->state = TRF7970A_ST_OFF;
+ trf->dev = &spi->dev;
+ trf->spi = spi;
+ trf->quirks = id->driver_data;
+
+ spi->mode = SPI_MODE_1;
+ spi->bits_per_word = 8;
+
+ /* There are two enable pins - both must be present */
+ trf->en_gpio = of_get_named_gpio(np, "ti,enable-gpios", 0);
+ if (!gpio_is_valid(trf->en_gpio)) {
+ dev_err(trf->dev, "No EN GPIO property\n");
+ return trf->en_gpio;
+ }
+
+ ret = devm_gpio_request_one(trf->dev, trf->en_gpio,
+ GPIOF_DIR_OUT | GPIOF_INIT_LOW, "EN");
+ if (ret) {
+ dev_err(trf->dev, "Can't request EN GPIO: %d\n", ret);
+ return ret;
+ }
+
+ trf->en2_gpio = of_get_named_gpio(np, "ti,enable-gpios", 1);
+ if (!gpio_is_valid(trf->en2_gpio)) {
+ dev_err(trf->dev, "No EN2 GPIO property\n");
+ return trf->en2_gpio;
+ }
+
+ ret = devm_gpio_request_one(trf->dev, trf->en2_gpio,
+ GPIOF_DIR_OUT | GPIOF_INIT_LOW, "EN2");
+ if (ret) {
+ dev_err(trf->dev, "Can't request EN2 GPIO: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(trf->dev, spi->irq, NULL,
+ trf7970a_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "trf7970a", trf);
+ if (ret) {
+ dev_err(trf->dev, "Can't request IRQ#%d: %d\n", spi->irq, ret);
+ return ret;
+ }
+
+ mutex_init(&trf->lock);
+ INIT_DELAYED_WORK(&trf->timeout_work, trf7970a_timeout_work_handler);
+
+ trf->regulator = devm_regulator_get(&spi->dev, "vin");
+ if (IS_ERR(trf->regulator)) {
+ ret = PTR_ERR(trf->regulator);
+ dev_err(trf->dev, "Can't get VIN regulator: %d\n", ret);
+ goto err_destroy_lock;
+ }
+
+ ret = regulator_enable(trf->regulator);
+ if (ret) {
+ dev_err(trf->dev, "Can't enable VIN: %d\n", ret);
+ goto err_destroy_lock;
+ }
+
+ trf->powering_up = true;
+
+ trf->ddev = nfc_digital_allocate_device(&trf7970a_nfc_ops,
+ TRF7970A_SUPPORTED_PROTOCOLS,
+ NFC_DIGITAL_DRV_CAPS_IN_CRC, TRF7970A_TX_SKB_HEADROOM,
+ 0);
+ if (!trf->ddev) {
+ dev_err(trf->dev, "Can't allocate NFC digital device\n");
+ ret = -ENOMEM;
+ goto err_disable_regulator;
+ }
+
+ nfc_digital_set_parent_dev(trf->ddev, trf->dev);
+ nfc_digital_set_drvdata(trf->ddev, trf);
+ spi_set_drvdata(spi, trf);
+
+ ret = nfc_digital_register_device(trf->ddev);
+ if (ret) {
+ dev_err(trf->dev, "Can't register NFC digital device: %d\n",
+ ret);
+ goto err_free_ddev;
+ }
+
+ return 0;
+
+err_free_ddev:
+ nfc_digital_free_device(trf->ddev);
+err_disable_regulator:
+ regulator_disable(trf->regulator);
+err_destroy_lock:
+ mutex_destroy(&trf->lock);
+ return ret;
+}
+
+static int trf7970a_remove(struct spi_device *spi)
+{
+ struct trf7970a *trf = spi_get_drvdata(spi);
+
+ mutex_lock(&trf->lock);
+
+ trf7970a_switch_rf_off(trf);
+ trf7970a_init(trf);
+
+ switch (trf->state) {
+ case TRF7970A_ST_WAIT_FOR_TX_FIFO:
+ case TRF7970A_ST_WAIT_FOR_RX_DATA:
+ case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
+ case TRF7970A_ST_WAIT_TO_ISSUE_EOF:
+ trf7970a_send_err_upstream(trf, -ECANCELED);
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&trf->lock);
+
+ nfc_digital_unregister_device(trf->ddev);
+ nfc_digital_free_device(trf->ddev);
+
+ regulator_disable(trf->regulator);
+
+ mutex_destroy(&trf->lock);
+
+ return 0;
+}
+
+static const struct spi_device_id trf7970a_id_table[] = {
+ { "trf7970a", TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, trf7970a_id_table);
+
+static struct spi_driver trf7970a_spi_driver = {
+ .probe = trf7970a_probe,
+ .remove = trf7970a_remove,
+ .id_table = trf7970a_id_table,
+ .driver = {
+ .name = "trf7970a",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_spi_driver(trf7970a_spi_driver);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TI trf7970a RFID/NFC Transceiver Driver");
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index c6973f101a3e..889005fa4d04 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -7,14 +7,6 @@ config OF
menu "Device Tree and Open Firmware support"
depends on OF
-config PROC_DEVICETREE
- bool "Support for device tree in /proc"
- depends on PROC_FS && !SPARC
- help
- This option adds a device-tree directory under /proc which contains
- an image of the device tree that the kernel copies from Open
- Firmware or other boot firmware. If unsure, say Y here.
-
config OF_SELFTEST
bool "Device Tree Runtime self tests"
depends on OF_IRQ
@@ -44,6 +36,10 @@ config OF_DYNAMIC
config OF_ADDRESS
def_bool y
depends on !SPARC
+ select OF_ADDRESS_PCI if PCI
+
+config OF_ADDRESS_PCI
+ bool
config OF_IRQ
def_bool y
@@ -75,4 +71,10 @@ config OF_MTD
depends on MTD
def_bool y
+config OF_RESERVED_MEM
+ depends on OF_EARLY_FLATTREE
+ bool
+ help
+ Helpers to allow for reservation of memory regions
+
endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index efd05102c405..ed9660adad77 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
+obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 1a54f1ffaadb..cb4242a69cd5 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -91,7 +91,7 @@ static unsigned int of_bus_default_get_flags(const __be32 *addr)
return IORESOURCE_MEM;
}
-#ifdef CONFIG_PCI
+#ifdef CONFIG_OF_ADDRESS_PCI
/*
* PCI bus specific translator
*/
@@ -166,7 +166,9 @@ static int of_bus_pci_translate(__be32 *addr, u64 offset, int na)
{
return of_bus_default_translate(addr + 1, offset, na - 1);
}
+#endif /* CONFIG_OF_ADDRESS_PCI */
+#ifdef CONFIG_PCI
const __be32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size,
unsigned int *flags)
{
@@ -356,7 +358,7 @@ static unsigned int of_bus_isa_get_flags(const __be32 *addr)
*/
static struct of_bus of_busses[] = {
-#ifdef CONFIG_PCI
+#ifdef CONFIG_OF_ADDRESS_PCI
/* PCI */
{
.name = "pci",
@@ -367,7 +369,7 @@ static struct of_bus of_busses[] = {
.translate = of_bus_pci_translate,
.get_flags = of_bus_pci_get_flags,
},
-#endif /* CONFIG_PCI */
+#endif /* CONFIG_OF_ADDRESS_PCI */
/* ISA */
{
.name = "isa",
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 5f81bfea5246..f72d19b7e5d2 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -24,6 +24,7 @@
#include <linux/of_graph.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/proc_fs.h>
#include "of_private.h"
@@ -36,6 +37,12 @@ struct device_node *of_chosen;
struct device_node *of_aliases;
static struct device_node *of_stdout;
+static struct kset *of_kset;
+
+/*
+ * Used to protect the of_aliases; but also overloaded to hold off addition of
+ * nodes to sysfs
+ */
DEFINE_MUTEX(of_aliases_mutex);
/* use when traversing tree through the allnext, child, sibling,
@@ -93,14 +100,14 @@ int __weak of_node_to_nid(struct device_node *np)
struct device_node *of_node_get(struct device_node *node)
{
if (node)
- kref_get(&node->kref);
+ kobject_get(&node->kobj);
return node;
}
EXPORT_SYMBOL(of_node_get);
-static inline struct device_node *kref_to_device_node(struct kref *kref)
+static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
{
- return container_of(kref, struct device_node, kref);
+ return container_of(kobj, struct device_node, kobj);
}
/**
@@ -110,16 +117,15 @@ static inline struct device_node *kref_to_device_node(struct kref *kref)
* In of_node_put() this function is passed to kref_put()
* as the destructor.
*/
-static void of_node_release(struct kref *kref)
+static void of_node_release(struct kobject *kobj)
{
- struct device_node *node = kref_to_device_node(kref);
+ struct device_node *node = kobj_to_device_node(kobj);
struct property *prop = node->properties;
/* We should never be releasing nodes that haven't been detached. */
if (!of_node_check_flag(node, OF_DETACHED)) {
pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name);
dump_stack();
- kref_init(&node->kref);
return;
}
@@ -152,11 +158,154 @@ static void of_node_release(struct kref *kref)
void of_node_put(struct device_node *node)
{
if (node)
- kref_put(&node->kref, of_node_release);
+ kobject_put(&node->kobj);
}
EXPORT_SYMBOL(of_node_put);
+#else
+static void of_node_release(struct kobject *kobj)
+{
+ /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
+}
#endif /* CONFIG_OF_DYNAMIC */
+struct kobj_type of_node_ktype = {
+ .release = of_node_release,
+};
+
+static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t offset, size_t count)
+{
+ struct property *pp = container_of(bin_attr, struct property, attr);
+ return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length);
+}
+
+static const char *safe_name(struct kobject *kobj, const char *orig_name)
+{
+ const char *name = orig_name;
+ struct kernfs_node *kn;
+ int i = 0;
+
+ /* don't be a hero. After 16 tries give up */
+ while (i < 16 && (kn = sysfs_get_dirent(kobj->sd, name))) {
+ sysfs_put(kn);
+ if (name != orig_name)
+ kfree(name);
+ name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i);
+ }
+
+ if (name != orig_name)
+ pr_warn("device-tree: Duplicate name in %s, renamed to \"%s\"\n",
+ kobject_name(kobj), name);
+ return name;
+}
+
+static int __of_add_property_sysfs(struct device_node *np, struct property *pp)
+{
+ int rc;
+
+ /* Important: Don't leak passwords */
+ bool secure = strncmp(pp->name, "security-", 9) == 0;
+
+ sysfs_bin_attr_init(&pp->attr);
+ pp->attr.attr.name = safe_name(&np->kobj, pp->name);
+ pp->attr.attr.mode = secure ? S_IRUSR : S_IRUGO;
+ pp->attr.size = secure ? 0 : pp->length;
+ pp->attr.read = of_node_property_read;
+
+ rc = sysfs_create_bin_file(&np->kobj, &pp->attr);
+ WARN(rc, "error adding attribute %s to node %s\n", pp->name, np->full_name);
+ return rc;
+}
+
+static int __of_node_add(struct device_node *np)
+{
+ const char *name;
+ struct property *pp;
+ int rc;
+
+ np->kobj.kset = of_kset;
+ if (!np->parent) {
+ /* Nodes without parents are new top level trees */
+ rc = kobject_add(&np->kobj, NULL, safe_name(&of_kset->kobj, "base"));
+ } else {
+ name = safe_name(&np->parent->kobj, kbasename(np->full_name));
+ if (!name || !name[0])
+ return -EINVAL;
+
+ rc = kobject_add(&np->kobj, &np->parent->kobj, "%s", name);
+ }
+ if (rc)
+ return rc;
+
+ for_each_property_of_node(np, pp)
+ __of_add_property_sysfs(np, pp);
+
+ return 0;
+}
+
+int of_node_add(struct device_node *np)
+{
+ int rc = 0;
+
+ BUG_ON(!of_node_is_initialized(np));
+
+ /*
+ * Grab the mutex here so that in a race condition between of_init() and
+ * of_node_add(), node addition will still be consistent.
+ */
+ mutex_lock(&of_aliases_mutex);
+ if (of_kset)
+ rc = __of_node_add(np);
+ else
+ /* This scenario may be perfectly valid, but report it anyway */
+ pr_info("of_node_add(%s) before of_init()\n", np->full_name);
+ mutex_unlock(&of_aliases_mutex);
+ return rc;
+}
+
+#if defined(CONFIG_OF_DYNAMIC)
+static void of_node_remove(struct device_node *np)
+{
+ struct property *pp;
+
+ BUG_ON(!of_node_is_initialized(np));
+
+ /* only remove properties if on sysfs */
+ if (of_node_is_attached(np)) {
+ for_each_property_of_node(np, pp)
+ sysfs_remove_bin_file(&np->kobj, &pp->attr);
+ kobject_del(&np->kobj);
+ }
+
+ /* finally remove the kobj_init ref */
+ of_node_put(np);
+}
+#endif
+
+static int __init of_init(void)
+{
+ struct device_node *np;
+
+ /* Create the kset, and register existing nodes */
+ mutex_lock(&of_aliases_mutex);
+ of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
+ if (!of_kset) {
+ mutex_unlock(&of_aliases_mutex);
+ return -ENOMEM;
+ }
+ for_each_of_allnodes(np)
+ __of_node_add(np);
+ mutex_unlock(&of_aliases_mutex);
+
+ /* Symlink in /proc as required by userspace ABI */
+ if (of_allnodes)
+ proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
+
+ return 0;
+}
+core_initcall(of_init);
+
static struct property *__of_find_property(const struct device_node *np,
const char *name, int *lenp)
{
@@ -1531,6 +1680,10 @@ static int of_property_notify(int action, struct device_node *np,
{
struct of_prop_reconfig pr;
+ /* only call notifiers if the node is attached */
+ if (!of_node_is_attached(np))
+ return 0;
+
pr.dn = np;
pr.prop = prop;
return of_reconfig_notify(action, &pr);
@@ -1544,11 +1697,31 @@ static int of_property_notify(int action, struct device_node *np,
#endif
/**
+ * __of_add_property - Add a property to a node without lock operations
+ */
+static int __of_add_property(struct device_node *np, struct property *prop)
+{
+ struct property **next;
+
+ prop->next = NULL;
+ next = &np->properties;
+ while (*next) {
+ if (strcmp(prop->name, (*next)->name) == 0)
+ /* duplicate ! don't insert it */
+ return -EEXIST;
+
+ next = &(*next)->next;
+ }
+ *next = prop;
+
+ return 0;
+}
+
+/**
* of_add_property - Add a property to a node
*/
int of_add_property(struct device_node *np, struct property *prop)
{
- struct property **next;
unsigned long flags;
int rc;
@@ -1556,27 +1729,16 @@ int of_add_property(struct device_node *np, struct property *prop)
if (rc)
return rc;
- prop->next = NULL;
raw_spin_lock_irqsave(&devtree_lock, flags);
- next = &np->properties;
- while (*next) {
- if (strcmp(prop->name, (*next)->name) == 0) {
- /* duplicate ! don't insert it */
- raw_spin_unlock_irqrestore(&devtree_lock, flags);
- return -1;
- }
- next = &(*next)->next;
- }
- *next = prop;
+ rc = __of_add_property(np, prop);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ if (rc)
+ return rc;
-#ifdef CONFIG_PROC_DEVICETREE
- /* try to add to proc as well if it was initialized */
- if (np->pde)
- proc_device_tree_add_prop(np->pde, prop);
-#endif /* CONFIG_PROC_DEVICETREE */
+ if (of_node_is_attached(np))
+ __of_add_property_sysfs(np, prop);
- return 0;
+ return rc;
}
/**
@@ -1616,11 +1778,11 @@ int of_remove_property(struct device_node *np, struct property *prop)
if (!found)
return -ENODEV;
-#ifdef CONFIG_PROC_DEVICETREE
- /* try to remove the proc node as well */
- if (np->pde)
- proc_device_tree_remove_prop(np->pde, prop);
-#endif /* CONFIG_PROC_DEVICETREE */
+ /* at early boot, bail hear and defer setup to of_init() */
+ if (!of_kset)
+ return 0;
+
+ sysfs_remove_bin_file(&np->kobj, &prop->attr);
return 0;
}
@@ -1666,16 +1828,17 @@ int of_update_property(struct device_node *np, struct property *newprop)
next = &(*next)->next;
}
raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ if (rc)
+ return rc;
+
+ /* Update the sysfs attribute */
+ if (oldprop)
+ sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
+ __of_add_property_sysfs(np, newprop);
if (!found)
return -ENODEV;
-#ifdef CONFIG_PROC_DEVICETREE
- /* try to add to proc as well if it was initialized */
- if (np->pde)
- proc_device_tree_update_prop(np->pde, newprop, oldprop);
-#endif /* CONFIG_PROC_DEVICETREE */
-
return 0;
}
@@ -1710,22 +1873,6 @@ int of_reconfig_notify(unsigned long action, void *p)
return notifier_to_errno(rc);
}
-#ifdef CONFIG_PROC_DEVICETREE
-static void of_add_proc_dt_entry(struct device_node *dn)
-{
- struct proc_dir_entry *ent;
-
- ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde);
- if (ent)
- proc_device_tree_add_node(dn, ent);
-}
-#else
-static void of_add_proc_dt_entry(struct device_node *dn)
-{
- return;
-}
-#endif
-
/**
* of_attach_node - Plug a device node into the tree and global list.
*/
@@ -1743,24 +1890,13 @@ int of_attach_node(struct device_node *np)
np->allnext = of_allnodes;
np->parent->child = np;
of_allnodes = np;
+ of_node_clear_flag(np, OF_DETACHED);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
- of_add_proc_dt_entry(np);
+ of_node_add(np);
return 0;
}
-#ifdef CONFIG_PROC_DEVICETREE
-static void of_remove_proc_dt_entry(struct device_node *dn)
-{
- proc_remove(dn->pde);
-}
-#else
-static void of_remove_proc_dt_entry(struct device_node *dn)
-{
- return;
-}
-#endif
-
/**
* of_detach_node - "Unplug" a node from the device tree.
*
@@ -1816,7 +1952,7 @@ int of_detach_node(struct device_node *np)
of_node_set_flag(np, OF_DETACHED);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
- of_remove_proc_dt_entry(np);
+ of_node_remove(np);
return rc;
}
#endif /* defined(CONFIG_OF_DYNAMIC) */
@@ -1852,9 +1988,9 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
of_chosen = of_find_node_by_path("/chosen@0");
if (of_chosen) {
- const char *name;
-
- name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+ const char *name = of_get_property(of_chosen, "stdout-path", NULL);
+ if (!name)
+ name = of_get_property(of_chosen, "linux,stdout-path", NULL);
if (name)
of_stdout = of_find_node_by_path(name);
}
@@ -2060,64 +2196,65 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
struct device_node *prev)
{
struct device_node *endpoint;
- struct device_node *port = NULL;
+ struct device_node *port;
if (!parent)
return NULL;
+ /*
+ * Start by locating the port node. If no previous endpoint is specified
+ * search for the first port node, otherwise get the previous endpoint
+ * parent port node.
+ */
if (!prev) {
struct device_node *node;
- /*
- * It's the first call, we have to find a port subnode
- * within this node or within an optional 'ports' node.
- */
+
node = of_get_child_by_name(parent, "ports");
if (node)
parent = node;
port = of_get_child_by_name(parent, "port");
-
- if (port) {
- /* Found a port, get an endpoint. */
- endpoint = of_get_next_child(port, NULL);
- of_node_put(port);
- } else {
- endpoint = NULL;
- }
-
- if (!endpoint)
- pr_err("%s(): no endpoint nodes specified for %s\n",
- __func__, parent->full_name);
of_node_put(node);
- return endpoint;
- }
-
- port = of_get_parent(prev);
- if (WARN_ONCE(!port, "%s(): endpoint %s has no parent node\n",
- __func__, prev->full_name))
- return NULL;
+ if (!port) {
+ pr_err("%s(): no port node found in %s\n",
+ __func__, parent->full_name);
+ return NULL;
+ }
+ } else {
+ port = of_get_parent(prev);
+ if (WARN_ONCE(!port, "%s(): endpoint %s has no parent node\n",
+ __func__, prev->full_name))
+ return NULL;
- /* Avoid dropping prev node refcount to 0. */
- of_node_get(prev);
- endpoint = of_get_next_child(port, prev);
- if (endpoint) {
- of_node_put(port);
- return endpoint;
+ /*
+ * Avoid dropping prev node refcount to 0 when getting the next
+ * child below.
+ */
+ of_node_get(prev);
}
- /* No more endpoints under this port, try the next one. */
- do {
- port = of_get_next_child(parent, port);
- if (!port)
- return NULL;
- } while (of_node_cmp(port->name, "port"));
+ while (1) {
+ /*
+ * Now that we have a port node, get the next endpoint by
+ * getting the next child. If the previous endpoint is NULL this
+ * will return the first child.
+ */
+ endpoint = of_get_next_child(port, prev);
+ if (endpoint) {
+ of_node_put(port);
+ return endpoint;
+ }
- /* Pick up the first endpoint in this port. */
- endpoint = of_get_next_child(port, NULL);
- of_node_put(port);
+ /* No more endpoints under this port, try the next one. */
+ prev = NULL;
- return endpoint;
+ do {
+ port = of_get_next_child(parent, port);
+ if (!port)
+ return NULL;
+ } while (of_node_cmp(port->name, "port"));
+ }
}
EXPORT_SYMBOL(of_graph_get_next_endpoint);
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 758b4f8b30b7..fa16a912a927 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -15,6 +15,8 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/sizes.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/slab.h>
@@ -202,6 +204,7 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
__alignof__(struct device_node));
if (allnextpp) {
char *fn;
+ of_node_init(np);
np->full_name = fn = ((char *)np) + sizeof(*np);
if (new_format) {
/* rebuild full path for new format */
@@ -232,7 +235,6 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
dad->next->sibling = np;
dad->next = np;
}
- kref_init(&np->kref);
}
/* process properties */
while (1) {
@@ -440,6 +442,129 @@ struct boot_param_header *initial_boot_params;
#ifdef CONFIG_OF_EARLY_FLATTREE
/**
+ * res_mem_reserve_reg() - reserve all memory described in 'reg' property
+ */
+static int __init __reserved_mem_reserve_reg(unsigned long node,
+ const char *uname)
+{
+ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
+ phys_addr_t base, size;
+ unsigned long len;
+ __be32 *prop;
+ int nomap, first = 1;
+
+ prop = of_get_flat_dt_prop(node, "reg", &len);
+ if (!prop)
+ return -ENOENT;
+
+ if (len && len % t_len != 0) {
+ pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
+ uname);
+ return -EINVAL;
+ }
+
+ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+
+ while (len >= t_len) {
+ base = dt_mem_next_cell(dt_root_addr_cells, &prop);
+ size = dt_mem_next_cell(dt_root_size_cells, &prop);
+
+ if (base && size &&
+ early_init_dt_reserve_memory_arch(base, size, nomap) == 0)
+ pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n",
+ uname, &base, (unsigned long)size / SZ_1M);
+ else
+ pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n",
+ uname, &base, (unsigned long)size / SZ_1M);
+
+ len -= t_len;
+ if (first) {
+ fdt_reserved_mem_save_node(node, uname, base, size);
+ first = 0;
+ }
+ }
+ return 0;
+}
+
+/**
+ * __reserved_mem_check_root() - check if #size-cells, #address-cells provided
+ * in /reserved-memory matches the values supported by the current implementation,
+ * also check if ranges property has been provided
+ */
+static int __reserved_mem_check_root(unsigned long node)
+{
+ __be32 *prop;
+
+ prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
+ if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
+ return -EINVAL;
+
+ prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
+ if (!prop || be32_to_cpup(prop) != dt_root_addr_cells)
+ return -EINVAL;
+
+ prop = of_get_flat_dt_prop(node, "ranges", NULL);
+ if (!prop)
+ return -EINVAL;
+ return 0;
+}
+
+/**
+ * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
+ */
+static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ static int found;
+ const char *status;
+ int err;
+
+ if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) {
+ if (__reserved_mem_check_root(node) != 0) {
+ pr_err("Reserved memory: unsupported node format, ignoring\n");
+ /* break scan */
+ return 1;
+ }
+ found = 1;
+ /* scan next node */
+ return 0;
+ } else if (!found) {
+ /* scan next node */
+ return 0;
+ } else if (found && depth < 2) {
+ /* scanning of /reserved-memory has been finished */
+ return 1;
+ }
+
+ status = of_get_flat_dt_prop(node, "status", NULL);
+ if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0)
+ return 0;
+
+ err = __reserved_mem_reserve_reg(node, uname);
+ if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL))
+ fdt_reserved_mem_save_node(node, uname, 0, 0);
+
+ /* scan next node */
+ return 0;
+}
+
+/**
+ * early_init_fdt_scan_reserved_mem() - create reserved memory regions
+ *
+ * This function grabs memory from early allocator for device exclusive use
+ * defined in device tree structures. It should be called by arch specific code
+ * once the early allocator (i.e. memblock) has been fully activated.
+ */
+void __init early_init_fdt_scan_reserved_mem(void)
+{
+ if (!initial_boot_params)
+ return;
+
+ of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
+ fdt_init_reserved_mem();
+}
+
+/**
* of_scan_flat_dt - scan flattened tree blob and call callback on each.
* @it: callback function
* @data: context data pointer
@@ -856,6 +981,16 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
memblock_add(base, size);
}
+int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
+ phys_addr_t size, bool nomap)
+{
+ if (memblock_is_region_reserved(base, size))
+ return -EBUSY;
+ if (nomap)
+ return memblock_remove(base, size);
+ return memblock_reserve(base, size);
+}
+
/*
* called from unflatten_device_tree() to bootstrap devicetree itself
* Architectures can override this definition if memblock isn't used
@@ -864,6 +999,14 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align)
{
return __va(memblock_alloc(size, align));
}
+#else
+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%llx - 0x%llx%s\n",
+ base, size, nomap ? " (nomap)" : "");
+ return -ENOSYS;
+}
#endif
bool __init early_init_dt_scan(void *params)
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index 5b3c24f3cde5..9a95831bd065 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -43,6 +43,23 @@ static void of_set_phy_supported(struct phy_device *phydev, u32 max_speed)
}
}
+/* Extract the clause 22 phy ID from the compatible string of the form
+ * ethernet-phy-idAAAA.BBBB */
+static int of_get_phy_id(struct device_node *device, u32 *phy_id)
+{
+ struct property *prop;
+ const char *cp;
+ unsigned int upper, lower;
+
+ of_property_for_each_string(device, "compatible", prop, cp) {
+ if (sscanf(cp, "ethernet-phy-id%4x.%4x", &upper, &lower) == 2) {
+ *phy_id = ((upper & 0xFFFF) << 16) | (lower & 0xFFFF);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *child,
u32 addr)
{
@@ -50,11 +67,15 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
bool is_c45;
int rc;
u32 max_speed = 0;
+ u32 phy_id;
is_c45 = of_device_is_compatible(child,
"ethernet-phy-ieee802.3-c45");
- phy = get_phy_device(mdio, addr, is_c45);
+ if (!is_c45 && !of_get_phy_id(child, &phy_id))
+ phy = phy_device_create(mdio, addr, phy_id, 0, NULL);
+ else
+ phy = get_phy_device(mdio, addr, is_c45);
if (!phy || IS_ERR(phy))
return 1;
diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c
index a27ec94877e4..b7361ed70537 100644
--- a/drivers/of/of_mtd.c
+++ b/drivers/of/of_mtd.c
@@ -50,6 +50,40 @@ int of_get_nand_ecc_mode(struct device_node *np)
EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode);
/**
+ * of_get_nand_ecc_step_size - Get ECC step size associated to
+ * the required ECC strength (see below).
+ * @np: Pointer to the given device_node
+ *
+ * return the ECC step size, or errno in error case.
+ */
+int of_get_nand_ecc_step_size(struct device_node *np)
+{
+ int ret;
+ u32 val;
+
+ ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
+ return ret ? ret : val;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_ecc_step_size);
+
+/**
+ * of_get_nand_ecc_strength - Get required ECC strength over the
+ * correspnding step size as defined by 'nand-ecc-size'
+ * @np: Pointer to the given device_node
+ *
+ * return the ECC strength, or errno in error case.
+ */
+int of_get_nand_ecc_strength(struct device_node *np)
+{
+ int ret;
+ u32 val;
+
+ ret = of_property_read_u32(np, "nand-ecc-strength", &val);
+ return ret ? ret : val;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength);
+
+/**
* of_get_nand_bus_width - Get nand bus witdh for given device_node
* @np: Pointer to the given device_node
*
diff --git a/drivers/of/of_net.c b/drivers/of/of_net.c
index a208a457558c..73e14184aafe 100644
--- a/drivers/of/of_net.c
+++ b/drivers/of/of_net.c
@@ -12,33 +12,12 @@
#include <linux/export.h>
/**
- * It maps 'enum phy_interface_t' found in include/linux/phy.h
- * into the device tree binding of 'phy-mode', so that Ethernet
- * device driver can get phy interface from device tree.
- */
-static const char *phy_modes[] = {
- [PHY_INTERFACE_MODE_NA] = "",
- [PHY_INTERFACE_MODE_MII] = "mii",
- [PHY_INTERFACE_MODE_GMII] = "gmii",
- [PHY_INTERFACE_MODE_SGMII] = "sgmii",
- [PHY_INTERFACE_MODE_TBI] = "tbi",
- [PHY_INTERFACE_MODE_REVMII] = "rev-mii",
- [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_SMII] = "smii",
- [PHY_INTERFACE_MODE_XGMII] = "xgmii",
-};
-
-/**
* of_get_phy_mode - Get phy mode for given device_node
* @np: Pointer to the given device_node
*
- * The function gets phy interface string from property 'phy-mode',
- * and return its index in phy_modes table, or errno in error case.
+ * The function gets phy interface string from property 'phy-mode' or
+ * 'phy-connection-type', and return its index in phy_modes table, or errno in
+ * error case.
*/
int of_get_phy_mode(struct device_node *np)
{
@@ -47,10 +26,12 @@ int of_get_phy_mode(struct device_node *np)
err = of_property_read_string(np, "phy-mode", &pm);
if (err < 0)
+ err = of_property_read_string(np, "phy-connection-type", &pm);
+ if (err < 0)
return err;
- for (i = 0; i < ARRAY_SIZE(phy_modes); i++)
- if (!strcasecmp(pm, phy_modes[i]))
+ for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++)
+ if (!strcasecmp(pm, phy_modes(i)))
return i;
return -ENODEV;
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
new file mode 100644
index 000000000000..daaaf935911d
--- /dev/null
+++ b/drivers/of/of_reserved_mem.c
@@ -0,0 +1,217 @@
+/*
+ * Device tree based initialization code for reserved memory.
+ *
+ * Copyright (c) 2013, The Linux Foundation. All Rights Reserved.
+ * Copyright (c) 2013,2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Author: Marek Szyprowski <m.szyprowski@samsung.com>
+ * Author: Josh Cartwright <joshc@codeaurora.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 optional) any later version of the license.
+ */
+
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_platform.h>
+#include <linux/mm.h>
+#include <linux/sizes.h>
+#include <linux/of_reserved_mem.h>
+
+#define MAX_RESERVED_REGIONS 16
+static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
+static int reserved_mem_count;
+
+#if defined(CONFIG_HAVE_MEMBLOCK)
+#include <linux/memblock.h>
+int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
+ phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
+ phys_addr_t *res_base)
+{
+ /*
+ * We use __memblock_alloc_base() because memblock_alloc_base()
+ * panic()s on allocation failure.
+ */
+ phys_addr_t base = __memblock_alloc_base(size, align, end);
+ if (!base)
+ return -ENOMEM;
+
+ /*
+ * Check if the allocated region fits in to start..end window
+ */
+ if (base < start) {
+ memblock_free(base, size);
+ return -ENOMEM;
+ }
+
+ *res_base = base;
+ if (nomap)
+ return memblock_remove(base, size);
+ return 0;
+}
+#else
+int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
+ phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
+ phys_addr_t *res_base)
+{
+ pr_err("Reserved memory not supported, ignoring region 0x%llx%s\n",
+ size, nomap ? " (nomap)" : "");
+ return -ENOSYS;
+}
+#endif
+
+/**
+ * res_mem_save_node() - save fdt node for second pass initialization
+ */
+void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
+ phys_addr_t base, phys_addr_t size)
+{
+ struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
+
+ if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {
+ pr_err("Reserved memory: not enough space all defined regions.\n");
+ return;
+ }
+
+ rmem->fdt_node = node;
+ rmem->name = uname;
+ rmem->base = base;
+ rmem->size = size;
+
+ reserved_mem_count++;
+ return;
+}
+
+/**
+ * res_mem_alloc_size() - allocate reserved memory described by 'size', 'align'
+ * and 'alloc-ranges' properties
+ */
+static int __init __reserved_mem_alloc_size(unsigned long node,
+ const char *uname, phys_addr_t *res_base, phys_addr_t *res_size)
+{
+ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
+ phys_addr_t start = 0, end = 0;
+ phys_addr_t base = 0, align = 0, size;
+ unsigned long len;
+ __be32 *prop;
+ int nomap;
+ int ret;
+
+ prop = of_get_flat_dt_prop(node, "size", &len);
+ if (!prop)
+ return -EINVAL;
+
+ if (len != dt_root_size_cells * sizeof(__be32)) {
+ pr_err("Reserved memory: invalid size property in '%s' node.\n",
+ uname);
+ return -EINVAL;
+ }
+ size = dt_mem_next_cell(dt_root_size_cells, &prop);
+
+ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+
+ prop = of_get_flat_dt_prop(node, "alignment", &len);
+ if (prop) {
+ if (len != dt_root_addr_cells * sizeof(__be32)) {
+ pr_err("Reserved memory: invalid alignment property in '%s' node.\n",
+ uname);
+ return -EINVAL;
+ }
+ align = dt_mem_next_cell(dt_root_addr_cells, &prop);
+ }
+
+ prop = of_get_flat_dt_prop(node, "alloc-ranges", &len);
+ if (prop) {
+
+ if (len % t_len != 0) {
+ pr_err("Reserved memory: invalid alloc-ranges property in '%s', skipping node.\n",
+ uname);
+ return -EINVAL;
+ }
+
+ base = 0;
+
+ while (len > 0) {
+ start = dt_mem_next_cell(dt_root_addr_cells, &prop);
+ end = start + dt_mem_next_cell(dt_root_size_cells,
+ &prop);
+
+ ret = early_init_dt_alloc_reserved_memory_arch(size,
+ align, start, end, nomap, &base);
+ if (ret == 0) {
+ pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n",
+ uname, &base,
+ (unsigned long)size / SZ_1M);
+ break;
+ }
+ len -= t_len;
+ }
+
+ } else {
+ ret = early_init_dt_alloc_reserved_memory_arch(size, align,
+ 0, 0, nomap, &base);
+ if (ret == 0)
+ pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n",
+ uname, &base, (unsigned long)size / SZ_1M);
+ }
+
+ if (base == 0) {
+ pr_info("Reserved memory: failed to allocate memory for node '%s'\n",
+ uname);
+ return -ENOMEM;
+ }
+
+ *res_base = base;
+ *res_size = size;
+
+ return 0;
+}
+
+static const struct of_device_id __rmem_of_table_sentinel
+ __used __section(__reservedmem_of_table_end);
+
+/**
+ * res_mem_init_node() - call region specific reserved memory init code
+ */
+static int __init __reserved_mem_init_node(struct reserved_mem *rmem)
+{
+ extern const struct of_device_id __reservedmem_of_table[];
+ const struct of_device_id *i;
+
+ for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) {
+ reservedmem_of_init_fn initfn = i->data;
+ const char *compat = i->compatible;
+
+ if (!of_flat_dt_is_compatible(rmem->fdt_node, compat))
+ continue;
+
+ if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) {
+ pr_info("Reserved memory: initialized node %s, compatible id %s\n",
+ rmem->name, compat);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+/**
+ * fdt_init_reserved_mem - allocate and init all saved reserved memory regions
+ */
+void __init fdt_init_reserved_mem(void)
+{
+ int i;
+ for (i = 0; i < reserved_mem_count; i++) {
+ struct reserved_mem *rmem = &reserved_mem[i];
+ unsigned long node = rmem->fdt_node;
+ int err = 0;
+
+ if (rmem->size == 0)
+ err = __reserved_mem_alloc_size(node, rmem->name,
+ &rmem->base, &rmem->size);
+ if (err == 0)
+ __reserved_mem_init_node(rmem);
+ }
+}
diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c
index 7b666736c168..36b4035881b0 100644
--- a/drivers/of/pdt.c
+++ b/drivers/of/pdt.c
@@ -176,11 +176,10 @@ static struct device_node * __init of_pdt_create_node(phandle node,
return NULL;
dp = prom_early_alloc(sizeof(*dp));
+ of_node_init(dp);
of_pdt_incr_unique_id(dp);
dp->parent = parent;
- kref_init(&dp->kref);
-
dp->name = of_pdt_get_one_property(node, "name");
dp->type = of_pdt_get_one_property(node, "device_type");
dp->phandle = node;
diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c
index 6643d1920985..ae4450070503 100644
--- a/drivers/of/selftest.c
+++ b/drivers/of/selftest.c
@@ -30,6 +30,67 @@ static struct selftest_results {
} \
}
+static void __init of_selftest_dynamic(void)
+{
+ struct device_node *np;
+ struct property *prop;
+
+ np = of_find_node_by_path("/testcase-data");
+ if (!np) {
+ pr_err("missing testcase data\n");
+ return;
+ }
+
+ /* Array of 4 properties for the purpose of testing */
+ prop = kzalloc(sizeof(*prop) * 4, GFP_KERNEL);
+ if (!prop) {
+ selftest(0, "kzalloc() failed\n");
+ return;
+ }
+
+ /* Add a new property - should pass*/
+ prop->name = "new-property";
+ prop->value = "new-property-data";
+ prop->length = strlen(prop->value);
+ selftest(of_add_property(np, prop) == 0, "Adding a new property failed\n");
+
+ /* Try to add an existing property - should fail */
+ prop++;
+ prop->name = "new-property";
+ prop->value = "new-property-data-should-fail";
+ prop->length = strlen(prop->value);
+ selftest(of_add_property(np, prop) != 0,
+ "Adding an existing property should have failed\n");
+
+ /* Try to modify an existing property - should pass */
+ prop->value = "modify-property-data-should-pass";
+ prop->length = strlen(prop->value);
+ selftest(of_update_property(np, prop) == 0,
+ "Updating an existing property should have passed\n");
+
+ /* Try to modify non-existent property - should pass*/
+ prop++;
+ prop->name = "modify-property";
+ prop->value = "modify-missing-property-data-should-pass";
+ prop->length = strlen(prop->value);
+ selftest(of_update_property(np, prop) == 0,
+ "Updating a missing property should have passed\n");
+
+ /* Remove property - should pass */
+ selftest(of_remove_property(np, prop) == 0,
+ "Removing a property should have passed\n");
+
+ /* Adding very large property - should pass */
+ prop++;
+ prop->name = "large-property-PAGE_SIZEx8";
+ prop->length = PAGE_SIZE * 8;
+ prop->value = kzalloc(prop->length, GFP_KERNEL);
+ selftest(prop->value != NULL, "Unable to allocate large buffer\n");
+ if (prop->value)
+ selftest(of_add_property(np, prop) == 0,
+ "Adding a large property should have passed\n");
+}
+
static void __init of_selftest_parse_phandle_with_args(void)
{
struct device_node *np;
@@ -378,6 +439,7 @@ static int __init of_selftest(void)
of_node_put(np);
pr_info("start of selftest - you will see error messages\n");
+ of_selftest_dynamic();
of_selftest_parse_phandle_with_args();
of_selftest_property_match_string();
of_selftest_parse_interrupts();
diff --git a/drivers/of/testcase-data/tests-phandle.dtsi b/drivers/of/testcase-data/tests-phandle.dtsi
index 0007d3cd7dc2..788a4c24b8f5 100644
--- a/drivers/of/testcase-data/tests-phandle.dtsi
+++ b/drivers/of/testcase-data/tests-phandle.dtsi
@@ -1,6 +1,9 @@
/ {
testcase-data {
+ security-password = "password";
+ duplicate-name = "duplicate";
+ duplicate-name { };
phandle-tests {
provider0: provider0 {
#phandle-cells = <0>;
diff --git a/drivers/oprofile/nmi_timer_int.c b/drivers/oprofile/nmi_timer_int.c
index 76f1c9357f39..9559829fb234 100644
--- a/drivers/oprofile/nmi_timer_int.c
+++ b/drivers/oprofile/nmi_timer_int.c
@@ -108,8 +108,8 @@ static void nmi_timer_shutdown(void)
struct perf_event *event;
int cpu;
- get_online_cpus();
- unregister_cpu_notifier(&nmi_timer_cpu_nb);
+ cpu_notifier_register_begin();
+ __unregister_cpu_notifier(&nmi_timer_cpu_nb);
for_each_possible_cpu(cpu) {
event = per_cpu(nmi_timer_events, cpu);
if (!event)
@@ -119,7 +119,7 @@ static void nmi_timer_shutdown(void)
perf_event_release_kernel(event);
}
- put_online_cpus();
+ cpu_notifier_register_done();
}
static int nmi_timer_setup(void)
@@ -132,20 +132,23 @@ static int nmi_timer_setup(void)
do_div(period, HZ);
nmi_timer_attr.sample_period = period;
- get_online_cpus();
- err = register_cpu_notifier(&nmi_timer_cpu_nb);
+ cpu_notifier_register_begin();
+ err = __register_cpu_notifier(&nmi_timer_cpu_nb);
if (err)
goto out;
+
/* can't attach events to offline cpus: */
for_each_online_cpu(cpu) {
err = nmi_timer_start_cpu(cpu);
- if (err)
- break;
+ if (err) {
+ cpu_notifier_register_done();
+ nmi_timer_shutdown();
+ return err;
+ }
}
- if (err)
- nmi_timer_shutdown();
+
out:
- put_online_cpus();
+ cpu_notifier_register_done();
return err;
}
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index 7dd62fa9d0bd..396c200b9ddb 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -116,11 +116,11 @@ static void pci_slot_release(struct kobject *kobj)
}
static struct pci_slot_attribute pci_slot_attr_address =
- __ATTR(address, (S_IFREG | S_IRUGO), address_read_file, NULL);
+ __ATTR(address, S_IRUGO, address_read_file, NULL);
static struct pci_slot_attribute pci_slot_attr_max_speed =
- __ATTR(max_bus_speed, (S_IFREG | S_IRUGO), max_speed_read_file, NULL);
+ __ATTR(max_bus_speed, S_IRUGO, max_speed_read_file, NULL);
static struct pci_slot_attribute pci_slot_attr_cur_speed =
- __ATTR(cur_bus_speed, (S_IFREG | S_IRUGO), cur_speed_read_file, NULL);
+ __ATTR(cur_bus_speed, S_IRUGO, cur_speed_read_file, NULL);
static struct attribute *pci_slot_default_attrs[] = {
&pci_slot_attr_address.attr,
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 8d3c49cc500f..3bb05f17b9b4 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -27,7 +27,7 @@ config PHY_EXYNOS_MIPI_VIDEO
config PHY_MVEBU_SATA
def_bool y
- depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE
+ depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
depends on OF
select GENERIC_PHY
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 06cee0189f3e..e49324032611 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -235,6 +235,9 @@ config PINCTRL_NOMADIK
depends on ARCH_U8500 || ARCH_NOMADIK
select PINMUX
select PINCONF
+ select GPIOLIB
+ select OF_GPIO
+ select GPIOLIB_IRQCHIP
config PINCTRL_STN8815
bool "STN8815 pin controller driver"
@@ -321,6 +324,7 @@ config PINCTRL_U300
config PINCTRL_COH901
bool "ST-Ericsson U300 COH 901 335/571 GPIO"
depends on GPIOLIB && ARCH_U300 && PINCTRL_U300
+ select GPIOLIB_IRQCHIP
help
Say yes here to support GPIO interface on ST-Ericsson U300.
The names of the two IP block variants supported are
diff --git a/drivers/pinctrl/pinctrl-adi2.c b/drivers/pinctrl/pinctrl-adi2.c
index 200ea1e72d40..0cc0eec83396 100644
--- a/drivers/pinctrl/pinctrl-adi2.c
+++ b/drivers/pinctrl/pinctrl-adi2.c
@@ -337,6 +337,7 @@ static unsigned int adi_gpio_irq_startup(struct irq_data *d)
if (!port) {
pr_err("GPIO IRQ %d :Not exist\n", d->irq);
+ /* FIXME: negative return code will be ignored */
return -ENODEV;
}
diff --git a/drivers/pinctrl/pinctrl-baytrail.c b/drivers/pinctrl/pinctrl-baytrail.c
index bf2b3f655469..6e8301f77187 100644
--- a/drivers/pinctrl/pinctrl-baytrail.c
+++ b/drivers/pinctrl/pinctrl-baytrail.c
@@ -407,23 +407,23 @@ static void byt_irq_mask(struct irq_data *d)
{
}
-static unsigned int byt_irq_startup(struct irq_data *d)
+static int byt_irq_reqres(struct irq_data *d)
{
struct byt_gpio *vg = irq_data_get_irq_chip_data(d);
- if (gpio_lock_as_irq(&vg->chip, irqd_to_hwirq(d)))
+ if (gpio_lock_as_irq(&vg->chip, irqd_to_hwirq(d))) {
dev_err(vg->chip.dev,
"unable to lock HW IRQ %lu for IRQ\n",
irqd_to_hwirq(d));
- byt_irq_unmask(d);
+ return -EINVAL;
+ }
return 0;
}
-static void byt_irq_shutdown(struct irq_data *d)
+static void byt_irq_relres(struct irq_data *d)
{
struct byt_gpio *vg = irq_data_get_irq_chip_data(d);
- byt_irq_mask(d);
gpio_unlock_as_irq(&vg->chip, irqd_to_hwirq(d));
}
@@ -432,8 +432,8 @@ static struct irq_chip byt_irqchip = {
.irq_mask = byt_irq_mask,
.irq_unmask = byt_irq_unmask,
.irq_set_type = byt_irq_type,
- .irq_startup = byt_irq_startup,
- .irq_shutdown = byt_irq_shutdown,
+ .irq_request_resources = byt_irq_reqres,
+ .irq_release_resources = byt_irq_relres,
};
static void byt_gpio_irq_init_hw(struct byt_gpio *vg)
diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c
index 162ac0d73739..d182fdd2e715 100644
--- a/drivers/pinctrl/pinctrl-coh901.c
+++ b/drivers/pinctrl/pinctrl-coh901.c
@@ -8,17 +8,14 @@
* Author: Jonas Aaberg <jonas.aberg@stericsson.com>
*/
#include <linux/module.h>
-#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/io.h>
-#include <linux/irqdomain.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
-#include <linux/list.h>
#include <linux/slab.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinconf-generic.h>
@@ -61,9 +58,17 @@
#define U300_GPIO_PINS_PER_PORT 8
#define U300_GPIO_MAX (U300_GPIO_PINS_PER_PORT * U300_GPIO_NUM_PORTS)
+struct u300_gpio_port {
+ struct u300_gpio *gpio;
+ char name[8];
+ int irq;
+ int number;
+ u8 toggle_edge_mode;
+};
+
struct u300_gpio {
struct gpio_chip chip;
- struct list_head port_list;
+ struct u300_gpio_port ports[U300_GPIO_NUM_PORTS];
struct clk *clk;
void __iomem *base;
struct device *dev;
@@ -78,16 +83,6 @@ struct u300_gpio {
u32 iev;
};
-struct u300_gpio_port {
- struct list_head node;
- struct u300_gpio *gpio;
- char name[8];
- struct irq_domain *domain;
- int irq;
- int number;
- u8 toggle_edge_mode;
-};
-
/*
* Macro to expand to read a specific register found in the "gpio"
* struct. It requires the struct u300_gpio *gpio variable to exist in
@@ -308,39 +303,6 @@ static int u300_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
return 0;
}
-static int u300_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
- struct u300_gpio *gpio = to_u300_gpio(chip);
- int portno = offset >> 3;
- struct u300_gpio_port *port = NULL;
- struct list_head *p;
- int retirq;
- bool found = false;
-
- list_for_each(p, &gpio->port_list) {
- port = list_entry(p, struct u300_gpio_port, node);
- if (port->number == portno) {
- found = true;
- break;
- }
- }
- if (!found) {
- dev_err(gpio->dev, "could not locate port for GPIO %d IRQ\n",
- offset);
- return -EINVAL;
- }
-
- /*
- * The local hwirqs on the port are the lower three bits, there
- * are exactly 8 IRQs per port since they are 8-bit
- */
- retirq = irq_find_mapping(port->domain, (offset & 0x7));
-
- dev_dbg(gpio->dev, "request IRQ for GPIO %d, return %d from port %d\n",
- offset, retirq, port->number);
- return retirq;
-}
-
/* Returning -EINVAL means "supported but not available" */
int u300_gpio_config_get(struct gpio_chip *chip,
unsigned offset,
@@ -461,7 +423,6 @@ static struct gpio_chip u300_gpio_chip = {
.set = u300_gpio_set,
.direction_input = u300_gpio_direction_input,
.direction_output = u300_gpio_direction_output,
- .to_irq = u300_gpio_to_irq,
};
static void u300_toggle_trigger(struct u300_gpio *gpio, unsigned offset)
@@ -485,9 +446,10 @@ static void u300_toggle_trigger(struct u300_gpio *gpio, unsigned offset)
static int u300_gpio_irq_type(struct irq_data *d, unsigned trigger)
{
- struct u300_gpio_port *port = irq_data_get_irq_chip_data(d);
- struct u300_gpio *gpio = port->gpio;
- int offset = (port->number << 3) + d->hwirq;
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct u300_gpio *gpio = to_u300_gpio(chip);
+ struct u300_gpio_port *port = &gpio->ports[d->hwirq >> 3];
+ int offset = d->hwirq;
u32 val;
if ((trigger & IRQF_TRIGGER_RISING) &&
@@ -521,18 +483,15 @@ static int u300_gpio_irq_type(struct irq_data *d, unsigned trigger)
static void u300_gpio_irq_enable(struct irq_data *d)
{
- struct u300_gpio_port *port = irq_data_get_irq_chip_data(d);
- struct u300_gpio *gpio = port->gpio;
- int offset = (port->number << 3) + d->hwirq;
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct u300_gpio *gpio = to_u300_gpio(chip);
+ struct u300_gpio_port *port = &gpio->ports[d->hwirq >> 3];
+ int offset = d->hwirq;
u32 val;
unsigned long flags;
dev_dbg(gpio->dev, "enable IRQ for hwirq %lu on port %s, offset %d\n",
d->hwirq, port->name, offset);
- if (gpio_lock_as_irq(&gpio->chip, d->hwirq))
- dev_err(gpio->dev,
- "unable to lock HW IRQ %lu for IRQ\n",
- d->hwirq);
local_irq_save(flags);
val = readl(U300_PIN_REG(offset, ien));
writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, ien));
@@ -541,9 +500,9 @@ static void u300_gpio_irq_enable(struct irq_data *d)
static void u300_gpio_irq_disable(struct irq_data *d)
{
- struct u300_gpio_port *port = irq_data_get_irq_chip_data(d);
- struct u300_gpio *gpio = port->gpio;
- int offset = (port->number << 3) + d->hwirq;
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct u300_gpio *gpio = to_u300_gpio(chip);
+ int offset = d->hwirq;
u32 val;
unsigned long flags;
@@ -551,7 +510,6 @@ static void u300_gpio_irq_disable(struct irq_data *d)
val = readl(U300_PIN_REG(offset, ien));
writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, ien));
local_irq_restore(flags);
- gpio_unlock_as_irq(&gpio->chip, d->hwirq);
}
static struct irq_chip u300_gpio_irqchip = {
@@ -559,17 +517,19 @@ static struct irq_chip u300_gpio_irqchip = {
.irq_enable = u300_gpio_irq_enable,
.irq_disable = u300_gpio_irq_disable,
.irq_set_type = u300_gpio_irq_type,
-
};
static void u300_gpio_irq_handler(unsigned irq, struct irq_desc *desc)
{
- struct u300_gpio_port *port = irq_get_handler_data(irq);
- struct u300_gpio *gpio = port->gpio;
+ struct irq_chip *parent_chip = irq_get_chip(irq);
+ struct gpio_chip *chip = irq_get_handler_data(irq);
+ struct u300_gpio *gpio = to_u300_gpio(chip);
+ struct u300_gpio_port *port = &gpio->ports[irq - chip->base];
int pinoffset = port->number << 3; /* get the right stride */
unsigned long val;
- desc->irq_data.chip->irq_ack(&desc->irq_data);
+ chained_irq_enter(parent_chip, desc);
+
/* Read event register */
val = readl(U300_PIN_REG(pinoffset, iev));
/* Mask relevant bits */
@@ -582,8 +542,8 @@ static void u300_gpio_irq_handler(unsigned irq, struct irq_desc *desc)
int irqoffset;
for_each_set_bit(irqoffset, &val, U300_GPIO_PINS_PER_PORT) {
- int pin_irq = irq_find_mapping(port->domain, irqoffset);
int offset = pinoffset + irqoffset;
+ int pin_irq = irq_find_mapping(chip->irqdomain, offset);
dev_dbg(gpio->dev, "GPIO IRQ %d on pin %d\n",
pin_irq, offset);
@@ -597,7 +557,7 @@ static void u300_gpio_irq_handler(unsigned irq, struct irq_desc *desc)
}
}
- desc->irq_data.chip->irq_unmask(&desc->irq_data);
+ chained_irq_exit(parent_chip, desc);
}
static void __init u300_gpio_init_pin(struct u300_gpio *gpio,
@@ -648,20 +608,6 @@ static void __init u300_gpio_init_coh901571(struct u300_gpio *gpio)
}
}
-static inline void u300_gpio_free_ports(struct u300_gpio *gpio)
-{
- struct u300_gpio_port *port;
- struct list_head *p, *n;
-
- list_for_each_safe(p, n, &gpio->port_list) {
- port = list_entry(p, struct u300_gpio_port, node);
- list_del(&port->node);
- if (port->domain)
- irq_domain_remove(port->domain);
- kfree(port);
- }
-}
-
/*
* Here we map a GPIO in the local gpio_chip pin space to a pin in
* the local pinctrl pin space. The pin controller used is
@@ -752,17 +698,28 @@ static int __init u300_gpio_probe(struct platform_device *pdev)
gpio->base + U300_GPIO_CR);
u300_gpio_init_coh901571(gpio);
+#ifdef CONFIG_OF_GPIO
+ gpio->chip.of_node = pdev->dev.of_node;
+#endif
+ err = gpiochip_add(&gpio->chip);
+ if (err) {
+ dev_err(gpio->dev, "unable to add gpiochip: %d\n", err);
+ goto err_no_chip;
+ }
+
+ err = gpiochip_irqchip_add(&gpio->chip,
+ &u300_gpio_irqchip,
+ 0,
+ handle_simple_irq,
+ IRQ_TYPE_EDGE_FALLING);
+ if (err) {
+ dev_err(gpio->dev, "no GPIO irqchip\n");
+ goto err_no_irqchip;
+ }
+
/* Add each port with its IRQ separately */
- INIT_LIST_HEAD(&gpio->port_list);
for (portno = 0 ; portno < U300_GPIO_NUM_PORTS; portno++) {
- struct u300_gpio_port *port =
- kmalloc(sizeof(struct u300_gpio_port), GFP_KERNEL);
-
- if (!port) {
- dev_err(gpio->dev, "out of memory\n");
- err = -ENOMEM;
- goto err_no_port;
- }
+ struct u300_gpio_port *port = &gpio->ports[portno];
snprintf(port->name, 8, "gpio%d", portno);
port->number = portno;
@@ -770,50 +727,16 @@ static int __init u300_gpio_probe(struct platform_device *pdev)
port->irq = platform_get_irq(pdev, portno);
- dev_dbg(gpio->dev, "register IRQ %d for port %s\n", port->irq,
- port->name);
-
- port->domain = irq_domain_add_linear(pdev->dev.of_node,
- U300_GPIO_PINS_PER_PORT,
- &irq_domain_simple_ops,
- port);
- if (!port->domain) {
- err = -ENOMEM;
- goto err_no_domain;
- }
-
- irq_set_chained_handler(port->irq, u300_gpio_irq_handler);
- irq_set_handler_data(port->irq, port);
-
- /* For each GPIO pin set the unique IRQ handler */
- for (i = 0; i < U300_GPIO_PINS_PER_PORT; i++) {
- int irqno = irq_create_mapping(port->domain, i);
-
- dev_dbg(gpio->dev, "GPIO%d on port %s gets IRQ %d\n",
- gpio->chip.base + (port->number << 3) + i,
- port->name, irqno);
- irq_set_chip_and_handler(irqno, &u300_gpio_irqchip,
- handle_simple_irq);
- set_irq_flags(irqno, IRQF_VALID);
- irq_set_chip_data(irqno, port);
- }
+ gpiochip_set_chained_irqchip(&gpio->chip,
+ &u300_gpio_irqchip,
+ port->irq,
+ u300_gpio_irq_handler);
/* Turns off irq force (test register) for this port */
writel(0x0, gpio->base + portno * gpio->stride + ifr);
-
- list_add_tail(&port->node, &gpio->port_list);
}
dev_dbg(gpio->dev, "initialized %d GPIO ports\n", portno);
-#ifdef CONFIG_OF_GPIO
- gpio->chip.of_node = pdev->dev.of_node;
-#endif
- err = gpiochip_add(&gpio->chip);
- if (err) {
- dev_err(gpio->dev, "unable to add gpiochip: %d\n", err);
- goto err_no_chip;
- }
-
/*
* Add pinctrl pin ranges, the pin controller must be registered
* at this point
@@ -832,12 +755,10 @@ static int __init u300_gpio_probe(struct platform_device *pdev)
return 0;
err_no_range:
+err_no_irqchip:
if (gpiochip_remove(&gpio->chip))
dev_err(&pdev->dev, "failed to remove gpio chip\n");
err_no_chip:
-err_no_domain:
-err_no_port:
- u300_gpio_free_ports(gpio);
clk_disable_unprepare(gpio->clk);
dev_err(&pdev->dev, "module ERROR:%d\n", err);
return err;
@@ -856,7 +777,6 @@ static int __exit u300_gpio_remove(struct platform_device *pdev)
dev_err(gpio->dev, "unable to remove gpiochip: %d\n", err);
return err;
}
- u300_gpio_free_ports(gpio);
clk_disable_unprepare(gpio->clk);
return 0;
}
diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
index 343f421c7696..38d579b47f31 100644
--- a/drivers/pinctrl/pinctrl-msm.c
+++ b/drivers/pinctrl/pinctrl-msm.c
@@ -785,23 +785,22 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
return 0;
}
-static unsigned int msm_gpio_irq_startup(struct irq_data *d)
+static int msm_gpio_irq_reqres(struct irq_data *d)
{
struct msm_pinctrl *pctrl = irq_data_get_irq_chip_data(d);
if (gpio_lock_as_irq(&pctrl->chip, d->hwirq)) {
dev_err(pctrl->dev, "unable to lock HW IRQ %lu for IRQ\n",
d->hwirq);
+ return -EINVAL;
}
- msm_gpio_irq_unmask(d);
return 0;
}
-static void msm_gpio_irq_shutdown(struct irq_data *d)
+static void msm_gpio_irq_relres(struct irq_data *d)
{
struct msm_pinctrl *pctrl = irq_data_get_irq_chip_data(d);
- msm_gpio_irq_mask(d);
gpio_unlock_as_irq(&pctrl->chip, d->hwirq);
}
@@ -812,8 +811,8 @@ static struct irq_chip msm_gpio_irq_chip = {
.irq_ack = msm_gpio_irq_ack,
.irq_set_type = msm_gpio_irq_set_type,
.irq_set_wake = msm_gpio_irq_set_wake,
- .irq_startup = msm_gpio_irq_startup,
- .irq_shutdown = msm_gpio_irq_shutdown,
+ .irq_request_resources = msm_gpio_irq_reqres,
+ .irq_release_resources = msm_gpio_irq_relres,
};
static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c
index cec7762cf335..208341fd57d2 100644
--- a/drivers/pinctrl/pinctrl-nomadik.c
+++ b/drivers/pinctrl/pinctrl-nomadik.c
@@ -21,9 +21,6 @@
#include <linux/gpio.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/irqdomain.h>
-#include <linux/irqchip/chained_irq.h>
#include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
@@ -246,28 +243,14 @@ enum nmk_gpio_slpm {
NMK_GPIO_SLPM_WAKEUP_DISABLE = NMK_GPIO_SLPM_NOCHANGE,
};
-/*
- * Platform data to register a block: only the initial gpio/irq number.
- */
-struct nmk_gpio_platform_data {
- char *name;
- int first_gpio;
- int first_irq;
- int num_gpio;
- u32 (*get_secondary_status)(unsigned int bank);
- void (*set_ioforce)(bool enable);
- bool supports_sleepmode;
-};
-
struct nmk_gpio_chip {
struct gpio_chip chip;
- struct irq_domain *domain;
void __iomem *addr;
struct clk *clk;
unsigned int bank;
unsigned int parent_irq;
- int secondary_parent_irq;
- u32 (*get_secondary_status)(unsigned int bank);
+ int latent_parent_irq;
+ u32 (*get_latent_status)(unsigned int bank);
void (*set_ioforce)(bool enable);
spinlock_t lock;
bool sleepmode;
@@ -432,7 +415,7 @@ nmk_gpio_disable_lazy_irq(struct nmk_gpio_chip *nmk_chip, unsigned offset)
u32 falling = nmk_chip->fimsc & BIT(offset);
u32 rising = nmk_chip->rimsc & BIT(offset);
int gpio = nmk_chip->chip.base + offset;
- int irq = irq_find_mapping(nmk_chip->domain, offset);
+ int irq = irq_find_mapping(nmk_chip->chip.irqdomain, offset);
struct irq_data *d = irq_get_irq_data(irq);
if (!rising && !falling)
@@ -660,11 +643,8 @@ static inline int nmk_gpio_get_bitmask(int gpio)
static void nmk_gpio_irq_ack(struct irq_data *d)
{
- struct nmk_gpio_chip *nmk_chip;
-
- nmk_chip = irq_data_get_irq_chip_data(d);
- if (!nmk_chip)
- return;
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip);
clk_enable(nmk_chip->clk);
writel(nmk_gpio_get_bitmask(d->hwirq), nmk_chip->addr + NMK_GPIO_IC);
@@ -848,10 +828,6 @@ static unsigned int nmk_gpio_irq_startup(struct irq_data *d)
{
struct nmk_gpio_chip *nmk_chip = irq_data_get_irq_chip_data(d);
- if (gpio_lock_as_irq(&nmk_chip->chip, d->hwirq))
- dev_err(nmk_chip->chip.dev,
- "unable to lock HW IRQ %lu for IRQ\n",
- d->hwirq);
clk_enable(nmk_chip->clk);
nmk_gpio_irq_unmask(d);
return 0;
@@ -863,7 +839,6 @@ static void nmk_gpio_irq_shutdown(struct irq_data *d)
nmk_gpio_irq_mask(d);
clk_disable(nmk_chip->clk);
- gpio_unlock_as_irq(&nmk_chip->chip, d->hwirq);
}
static struct irq_chip nmk_gpio_irq_chip = {
@@ -881,16 +856,15 @@ static struct irq_chip nmk_gpio_irq_chip = {
static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc,
u32 status)
{
- struct nmk_gpio_chip *nmk_chip;
struct irq_chip *host_chip = irq_get_chip(irq);
+ struct gpio_chip *chip = irq_desc_get_handler_data(desc);
chained_irq_enter(host_chip, desc);
- nmk_chip = irq_get_handler_data(irq);
while (status) {
int bit = __ffs(status);
- generic_handle_irq(irq_find_mapping(nmk_chip->domain, bit));
+ generic_handle_irq(irq_find_mapping(chip->irqdomain, bit));
status &= ~BIT(bit);
}
@@ -899,9 +873,11 @@ static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc,
static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
- struct nmk_gpio_chip *nmk_chip = irq_get_handler_data(irq);
+ struct gpio_chip *chip = irq_desc_get_handler_data(desc);
+ struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip);
u32 status;
+ pr_err("PLONK IRQ %d\n", irq);
clk_enable(nmk_chip->clk);
status = readl(nmk_chip->addr + NMK_GPIO_IS);
clk_disable(nmk_chip->clk);
@@ -909,29 +885,16 @@ static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
__nmk_gpio_irq_handler(irq, desc, status);
}
-static void nmk_gpio_secondary_irq_handler(unsigned int irq,
+static void nmk_gpio_latent_irq_handler(unsigned int irq,
struct irq_desc *desc)
{
- struct nmk_gpio_chip *nmk_chip = irq_get_handler_data(irq);
- u32 status = nmk_chip->get_secondary_status(nmk_chip->bank);
+ struct gpio_chip *chip = irq_desc_get_handler_data(desc);
+ struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip);
+ u32 status = nmk_chip->get_latent_status(nmk_chip->bank);
__nmk_gpio_irq_handler(irq, desc, status);
}
-static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip)
-{
- irq_set_chained_handler(nmk_chip->parent_irq, nmk_gpio_irq_handler);
- irq_set_handler_data(nmk_chip->parent_irq, nmk_chip);
-
- if (nmk_chip->secondary_parent_irq >= 0) {
- irq_set_chained_handler(nmk_chip->secondary_parent_irq,
- nmk_gpio_secondary_irq_handler);
- irq_set_handler_data(nmk_chip->secondary_parent_irq, nmk_chip);
- }
-
- return 0;
-}
-
/* I/O Functions */
static int nmk_gpio_request(struct gpio_chip *chip, unsigned offset)
@@ -1010,14 +973,6 @@ static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset,
return 0;
}
-static int nmk_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
- struct nmk_gpio_chip *nmk_chip =
- container_of(chip, struct nmk_gpio_chip, chip);
-
- return irq_create_mapping(nmk_chip->domain, offset);
-}
-
#ifdef CONFIG_DEBUG_FS
#include <linux/seq_file.h>
@@ -1116,7 +1071,6 @@ static struct gpio_chip nmk_gpio_template = {
.get = nmk_gpio_get_input,
.direction_output = nmk_gpio_make_output,
.set = nmk_gpio_set_output,
- .to_irq = nmk_gpio_to_irq,
.dbg_show = nmk_gpio_dbg_show,
.can_sleep = false,
};
@@ -1217,62 +1171,35 @@ void nmk_gpio_read_pull(int gpio_bank, u32 *pull_up)
}
}
-static int nmk_gpio_irq_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hwirq)
-{
- struct nmk_gpio_chip *nmk_chip = d->host_data;
-
- if (!nmk_chip)
- return -EINVAL;
-
- irq_set_chip_and_handler(irq, &nmk_gpio_irq_chip, handle_edge_irq);
- set_irq_flags(irq, IRQF_VALID);
- irq_set_chip_data(irq, nmk_chip);
- irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
-
- return 0;
-}
-
-static const struct irq_domain_ops nmk_gpio_irq_simple_ops = {
- .map = nmk_gpio_irq_map,
- .xlate = irq_domain_xlate_twocell,
-};
-
static int nmk_gpio_probe(struct platform_device *dev)
{
- struct nmk_gpio_platform_data *pdata;
struct device_node *np = dev->dev.of_node;
struct nmk_gpio_chip *nmk_chip;
struct gpio_chip *chip;
struct resource *res;
struct clk *clk;
- int secondary_irq;
+ int latent_irq;
+ bool supports_sleepmode;
void __iomem *base;
int irq;
int ret;
- pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
if (of_get_property(np, "st,supports-sleepmode", NULL))
- pdata->supports_sleepmode = true;
+ supports_sleepmode = true;
+ else
+ supports_sleepmode = false;
if (of_property_read_u32(np, "gpio-bank", &dev->id)) {
dev_err(&dev->dev, "gpio-bank property not found\n");
return -EINVAL;
}
- pdata->first_gpio = dev->id * NMK_GPIO_PER_CHIP;
- pdata->num_gpio = NMK_GPIO_PER_CHIP;
-
irq = platform_get_irq(dev, 0);
if (irq < 0)
return irq;
- secondary_irq = platform_get_irq(dev, 1);
- if (secondary_irq >= 0 && !pdata->get_secondary_status)
- return -EINVAL;
+ /* It's OK for this IRQ not to be present */
+ latent_irq = platform_get_irq(dev, 1);
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&dev->dev, res);
@@ -1297,16 +1224,14 @@ static int nmk_gpio_probe(struct platform_device *dev)
nmk_chip->addr = base;
nmk_chip->chip = nmk_gpio_template;
nmk_chip->parent_irq = irq;
- nmk_chip->secondary_parent_irq = secondary_irq;
- nmk_chip->get_secondary_status = pdata->get_secondary_status;
- nmk_chip->set_ioforce = pdata->set_ioforce;
- nmk_chip->sleepmode = pdata->supports_sleepmode;
+ nmk_chip->latent_parent_irq = latent_irq;
+ nmk_chip->sleepmode = supports_sleepmode;
spin_lock_init(&nmk_chip->lock);
chip = &nmk_chip->chip;
- chip->base = pdata->first_gpio;
- chip->ngpio = pdata->num_gpio;
- chip->label = pdata->name ?: dev_name(&dev->dev);
+ chip->base = dev->id * NMK_GPIO_PER_CHIP;
+ chip->ngpio = NMK_GPIO_PER_CHIP;
+ chip->label = dev_name(&dev->dev);
chip->dev = &dev->dev;
chip->owner = THIS_MODULE;
@@ -1325,17 +1250,31 @@ static int nmk_gpio_probe(struct platform_device *dev)
platform_set_drvdata(dev, nmk_chip);
- nmk_chip->domain = irq_domain_add_simple(np,
- NMK_GPIO_PER_CHIP, 0,
- &nmk_gpio_irq_simple_ops, nmk_chip);
- if (!nmk_chip->domain) {
- dev_err(&dev->dev, "failed to create irqdomain\n");
- /* Just do this, no matter if it fails */
+ /*
+ * Let the generic code handle this edge IRQ, the the chained
+ * handler will perform the actual work of handling the parent
+ * interrupt.
+ */
+ ret = gpiochip_irqchip_add(&nmk_chip->chip,
+ &nmk_gpio_irq_chip,
+ 0,
+ handle_edge_irq,
+ IRQ_TYPE_EDGE_FALLING);
+ if (ret) {
+ dev_err(&dev->dev, "could not add irqchip\n");
ret = gpiochip_remove(&nmk_chip->chip);
- return -ENOSYS;
+ return -ENODEV;
}
-
- nmk_gpio_init_irq(nmk_chip);
+ /* Then register the chain on the parent IRQ */
+ gpiochip_set_chained_irqchip(&nmk_chip->chip,
+ &nmk_gpio_irq_chip,
+ nmk_chip->parent_irq,
+ nmk_gpio_irq_handler);
+ if (nmk_chip->latent_parent_irq > 0)
+ gpiochip_set_chained_irqchip(&nmk_chip->chip,
+ &nmk_gpio_irq_chip,
+ nmk_chip->latent_parent_irq,
+ nmk_gpio_latent_irq_handler);
dev_info(&dev->dev, "at address %p\n", nmk_chip->addr);
diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c
index 5f3adb87c1ef..76502aab2cb1 100644
--- a/drivers/pinctrl/sirf/pinctrl-sirf.c
+++ b/drivers/pinctrl/sirf/pinctrl-sirf.c
@@ -595,23 +595,23 @@ static int sirfsoc_gpio_irq_type(struct irq_data *d, unsigned type)
return 0;
}
-static unsigned int sirfsoc_gpio_irq_startup(struct irq_data *d)
+static int sirfsoc_gpio_irq_reqres(struct irq_data *d)
{
struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d);
- if (gpio_lock_as_irq(&bank->chip.gc, d->hwirq % SIRFSOC_GPIO_BANK_SIZE))
+ if (gpio_lock_as_irq(&bank->chip.gc, d->hwirq % SIRFSOC_GPIO_BANK_SIZE)) {
dev_err(bank->chip.gc.dev,
"unable to lock HW IRQ %lu for IRQ\n",
d->hwirq);
- sirfsoc_gpio_irq_unmask(d);
+ return -EINVAL;
+ }
return 0;
}
-static void sirfsoc_gpio_irq_shutdown(struct irq_data *d)
+static void sirfsoc_gpio_irq_relres(struct irq_data *d)
{
struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d);
- sirfsoc_gpio_irq_mask(d);
gpio_unlock_as_irq(&bank->chip.gc, d->hwirq % SIRFSOC_GPIO_BANK_SIZE);
}
@@ -621,8 +621,8 @@ static struct irq_chip sirfsoc_irq_chip = {
.irq_mask = sirfsoc_gpio_irq_mask,
.irq_unmask = sirfsoc_gpio_irq_unmask,
.irq_set_type = sirfsoc_gpio_irq_type,
- .irq_startup = sirfsoc_gpio_irq_startup,
- .irq_shutdown = sirfsoc_gpio_irq_shutdown,
+ .irq_request_resources = sirfsoc_gpio_irq_reqres,
+ .irq_release_resources = sirfsoc_gpio_irq_relres,
};
static void sirfsoc_gpio_handle_irq(unsigned int irq, struct irq_desc *desc)
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 6d452a78b19c..fa0e4e057b99 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -22,7 +22,7 @@ config POWER_RESET_GPIO
config POWER_RESET_MSM
bool "Qualcomm MSM power-off driver"
- depends on POWER_RESET && ARCH_MSM
+ depends on POWER_RESET && ARCH_QCOM
help
Power off and restart support for Qualcomm boards.
diff --git a/drivers/power/reset/qnap-poweroff.c b/drivers/power/reset/qnap-poweroff.c
index 37f56f7ee926..a75db7f8a92f 100644
--- a/drivers/power/reset/qnap-poweroff.c
+++ b/drivers/power/reset/qnap-poweroff.c
@@ -1,5 +1,5 @@
/*
- * QNAP Turbo NAS Board power off
+ * QNAP Turbo NAS Board power off. Can also be used on Synology devices.
*
* Copyright (C) 2012 Andrew Lunn <andrew@lunn.ch>
*
@@ -25,17 +25,43 @@
#define UART1_REG(x) (base + ((UART_##x) << 2))
+struct power_off_cfg {
+ u32 baud;
+ char cmd;
+};
+
+static const struct power_off_cfg qnap_power_off_cfg = {
+ .baud = 19200,
+ .cmd = 'A',
+};
+
+static const struct power_off_cfg synology_power_off_cfg = {
+ .baud = 9600,
+ .cmd = '1',
+};
+
+static const struct of_device_id qnap_power_off_of_match_table[] = {
+ { .compatible = "qnap,power-off",
+ .data = &qnap_power_off_cfg,
+ },
+ { .compatible = "synology,power-off",
+ .data = &synology_power_off_cfg,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, qnap_power_off_of_match_table);
+
static void __iomem *base;
static unsigned long tclk;
+static const struct power_off_cfg *cfg;
static void qnap_power_off(void)
{
- /* 19200 baud divisor */
- const unsigned divisor = ((tclk + (8 * 19200)) / (16 * 19200));
+ const unsigned divisor = ((tclk + (8 * cfg->baud)) / (16 * cfg->baud));
pr_err("%s: triggering power-off...\n", __func__);
- /* hijack UART1 and reset into sane state (19200,8n1) */
+ /* hijack UART1 and reset into sane state */
writel(0x83, UART1_REG(LCR));
writel(divisor & 0xff, UART1_REG(DLL));
writel((divisor >> 8) & 0xff, UART1_REG(DLM));
@@ -44,16 +70,21 @@ static void qnap_power_off(void)
writel(0x00, UART1_REG(FCR));
writel(0x00, UART1_REG(MCR));
- /* send the power-off command 'A' to PIC */
- writel('A', UART1_REG(TX));
+ /* send the power-off command to PIC */
+ writel(cfg->cmd, UART1_REG(TX));
}
static int qnap_power_off_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct resource *res;
struct clk *clk;
char symname[KSYM_NAME_LEN];
+ const struct of_device_id *match =
+ of_match_node(qnap_power_off_of_match_table, np);
+ cfg = match->data;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Missing resource");
@@ -94,12 +125,6 @@ static int qnap_power_off_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id qnap_power_off_of_match_table[] = {
- { .compatible = "qnap,power-off", },
- {}
-};
-MODULE_DEVICE_TABLE(of, qnap_power_off_of_match_table);
-
static struct platform_driver qnap_power_off_driver = {
.probe = qnap_power_off_probe,
.remove = qnap_power_off_remove,
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index 61b51e17d932..d9a0770b6c73 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -1374,6 +1374,9 @@ static int __init rapl_init(void)
return -ENODEV;
}
+
+ cpu_notifier_register_begin();
+
/* prevent CPU hotplug during detection */
get_online_cpus();
ret = rapl_detect_topology();
@@ -1385,20 +1388,23 @@ static int __init rapl_init(void)
ret = -ENODEV;
goto done;
}
- register_hotcpu_notifier(&rapl_cpu_notifier);
+ __register_hotcpu_notifier(&rapl_cpu_notifier);
done:
put_online_cpus();
+ cpu_notifier_register_done();
return ret;
}
static void __exit rapl_exit(void)
{
+ cpu_notifier_register_begin();
get_online_cpus();
- unregister_hotcpu_notifier(&rapl_cpu_notifier);
+ __unregister_hotcpu_notifier(&rapl_cpu_notifier);
rapl_unregister_powercap();
rapl_cleanup_data();
put_online_cpus();
+ cpu_notifier_register_done();
}
module_init(rapl_init);
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 5a7910e61e17..6963bdf54175 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -7,6 +7,7 @@ menu "PTP clock support"
config PTP_1588_CLOCK
tristate "PTP clock support"
select PPS
+ select NET_PTP_CLASSIFY
help
The IEEE 1588 standard defines a method to precisely
synchronize distributed clocks over Ethernet networks. The
diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
index 34a0c607318e..419056d7887e 100644
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -25,6 +25,96 @@
#include "ptp_private.h"
+static int ptp_disable_pinfunc(struct ptp_clock_info *ops,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ struct ptp_clock_request rq;
+ int err = 0;
+
+ memset(&rq, 0, sizeof(rq));
+
+ switch (func) {
+ case PTP_PF_NONE:
+ break;
+ case PTP_PF_EXTTS:
+ rq.type = PTP_CLK_REQ_EXTTS;
+ rq.extts.index = chan;
+ err = ops->enable(ops, &rq, 0);
+ break;
+ case PTP_PF_PEROUT:
+ rq.type = PTP_CLK_REQ_PEROUT;
+ rq.perout.index = chan;
+ err = ops->enable(ops, &rq, 0);
+ break;
+ case PTP_PF_PHYSYNC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ struct ptp_clock_info *info = ptp->info;
+ struct ptp_pin_desc *pin1 = NULL, *pin2 = &info->pin_config[pin];
+ unsigned int i;
+
+ /* Check to see if any other pin previously had this function. */
+ for (i = 0; i < info->n_pins; i++) {
+ if (info->pin_config[i].func == func &&
+ info->pin_config[i].chan == chan) {
+ pin1 = &info->pin_config[i];
+ break;
+ }
+ }
+ if (pin1 && i == pin)
+ return 0;
+
+ /* Check the desired function and channel. */
+ switch (func) {
+ case PTP_PF_NONE:
+ break;
+ case PTP_PF_EXTTS:
+ if (chan >= info->n_ext_ts)
+ return -EINVAL;
+ break;
+ case PTP_PF_PEROUT:
+ if (chan >= info->n_per_out)
+ return -EINVAL;
+ break;
+ case PTP_PF_PHYSYNC:
+ pr_err("sorry, cannot reassign the calibration pin\n");
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+
+ if (pin2->func == PTP_PF_PHYSYNC) {
+ pr_err("sorry, cannot reprogram the calibration pin\n");
+ return -EINVAL;
+ }
+
+ if (info->verify(info, pin, func, chan)) {
+ pr_err("driver cannot use function %u on pin %u\n", func, chan);
+ return -EOPNOTSUPP;
+ }
+
+ /* Disable whatever function was previously assigned. */
+ if (pin1) {
+ ptp_disable_pinfunc(info, func, chan);
+ pin1->func = PTP_PF_NONE;
+ pin1->chan = 0;
+ }
+ ptp_disable_pinfunc(info, pin2->func, pin2->chan);
+ pin2->func = func;
+ pin2->chan = chan;
+
+ return 0;
+}
+
int ptp_open(struct posix_clock *pc, fmode_t fmode)
{
return 0;
@@ -35,12 +125,13 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
struct ptp_clock_caps caps;
struct ptp_clock_request req;
struct ptp_sys_offset *sysoff = NULL;
+ struct ptp_pin_desc pd;
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
struct ptp_clock_info *ops = ptp->info;
struct ptp_clock_time *pct;
struct timespec ts;
int enable, err = 0;
- unsigned int i;
+ unsigned int i, pin_index;
switch (cmd) {
@@ -51,6 +142,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
caps.n_ext_ts = ptp->info->n_ext_ts;
caps.n_per_out = ptp->info->n_per_out;
caps.pps = ptp->info->pps;
+ caps.n_pins = ptp->info->n_pins;
if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
err = -EFAULT;
break;
@@ -126,6 +218,40 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
err = -EFAULT;
break;
+ case PTP_PIN_GETFUNC:
+ if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) {
+ err = -EFAULT;
+ break;
+ }
+ pin_index = pd.index;
+ if (pin_index >= ops->n_pins) {
+ err = -EINVAL;
+ break;
+ }
+ if (mutex_lock_interruptible(&ptp->pincfg_mux))
+ return -ERESTARTSYS;
+ pd = ops->pin_config[pin_index];
+ mutex_unlock(&ptp->pincfg_mux);
+ if (!err && copy_to_user((void __user *)arg, &pd, sizeof(pd)))
+ err = -EFAULT;
+ break;
+
+ case PTP_PIN_SETFUNC:
+ if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) {
+ err = -EFAULT;
+ break;
+ }
+ pin_index = pd.index;
+ if (pin_index >= ops->n_pins) {
+ err = -EINVAL;
+ break;
+ }
+ if (mutex_lock_interruptible(&ptp->pincfg_mux))
+ return -ERESTARTSYS;
+ err = ptp_set_pinfunc(ptp, pin_index, pd.func, pd.chan);
+ mutex_unlock(&ptp->pincfg_mux);
+ break;
+
default:
err = -ENOTTY;
break;
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index a8319b266643..e25d2bc898e5 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -169,6 +169,7 @@ static void delete_ptp_clock(struct posix_clock *pc)
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
mutex_destroy(&ptp->tsevq_mux);
+ mutex_destroy(&ptp->pincfg_mux);
ida_simple_remove(&ptp_clocks_map, ptp->index);
kfree(ptp);
}
@@ -203,6 +204,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
ptp->index = index;
spin_lock_init(&ptp->tsevq.lock);
mutex_init(&ptp->tsevq_mux);
+ mutex_init(&ptp->pincfg_mux);
init_waitqueue_head(&ptp->tsev_wq);
/* Create a new device in our class. */
@@ -249,6 +251,7 @@ no_sysfs:
device_destroy(ptp_class, ptp->devid);
no_device:
mutex_destroy(&ptp->tsevq_mux);
+ mutex_destroy(&ptp->pincfg_mux);
no_slot:
kfree(ptp);
no_memory:
@@ -305,6 +308,26 @@ int ptp_clock_index(struct ptp_clock *ptp)
}
EXPORT_SYMBOL(ptp_clock_index);
+int ptp_find_pin(struct ptp_clock *ptp,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ struct ptp_pin_desc *pin = NULL;
+ int i;
+
+ mutex_lock(&ptp->pincfg_mux);
+ for (i = 0; i < ptp->info->n_pins; i++) {
+ if (ptp->info->pin_config[i].func == func &&
+ ptp->info->pin_config[i].chan == chan) {
+ pin = &ptp->info->pin_config[i];
+ break;
+ }
+ }
+ mutex_unlock(&ptp->pincfg_mux);
+
+ return pin ? i : -1;
+}
+EXPORT_SYMBOL(ptp_find_pin);
+
/* module operations */
static void __exit ptp_exit(void)
diff --git a/drivers/ptp/ptp_ixp46x.c b/drivers/ptp/ptp_ixp46x.c
index 4a08727fcaf3..604d340f2095 100644
--- a/drivers/ptp/ptp_ixp46x.c
+++ b/drivers/ptp/ptp_ixp46x.c
@@ -244,6 +244,7 @@ static struct ptp_clock_info ptp_ixp_caps = {
.name = "IXP46X timer",
.max_adj = 66666655,
.n_ext_ts = N_EXT_TS,
+ .n_pins = 0,
.pps = 0,
.adjfreq = ptp_ixp_adjfreq,
.adjtime = ptp_ixp_adjtime,
diff --git a/drivers/ptp/ptp_pch.c b/drivers/ptp/ptp_pch.c
index 71a2559278d7..90a106308c4f 100644
--- a/drivers/ptp/ptp_pch.c
+++ b/drivers/ptp/ptp_pch.c
@@ -514,6 +514,7 @@ static struct ptp_clock_info ptp_pch_caps = {
.name = "PCH timer",
.max_adj = 50000000,
.n_ext_ts = N_EXT_TS,
+ .n_pins = 0,
.pps = 0,
.adjfreq = ptp_pch_adjfreq,
.adjtime = ptp_pch_adjtime,
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index df03f2e30ad9..9c5d41421b65 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h
@@ -48,8 +48,12 @@ struct ptp_clock {
long dialed_frequency; /* remembers the frequency adjustment */
struct timestamp_event_queue tsevq; /* simple fifo for time stamps */
struct mutex tsevq_mux; /* one process at a time reading the fifo */
+ struct mutex pincfg_mux; /* protect concurrent info->pin_config access */
wait_queue_head_t tsev_wq;
int defunct; /* tells readers to go away when clock is being removed */
+ struct device_attribute *pin_dev_attr;
+ struct attribute **pin_attr;
+ struct attribute_group pin_attr_group;
};
/*
@@ -69,6 +73,10 @@ static inline int queue_cnt(struct timestamp_event_queue *q)
* see ptp_chardev.c
*/
+/* caller must hold pincfg_mux */
+int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan);
+
long ptp_ioctl(struct posix_clock *pc,
unsigned int cmd, unsigned long arg);
diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c
index 13ec5311746a..302e626fe6b0 100644
--- a/drivers/ptp/ptp_sysfs.c
+++ b/drivers/ptp/ptp_sysfs.c
@@ -18,6 +18,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/capability.h>
+#include <linux/slab.h>
#include "ptp_private.h"
@@ -42,6 +43,7 @@ PTP_SHOW_INT(max_adjustment, max_adj);
PTP_SHOW_INT(n_alarms, n_alarm);
PTP_SHOW_INT(n_external_timestamps, n_ext_ts);
PTP_SHOW_INT(n_periodic_outputs, n_per_out);
+PTP_SHOW_INT(n_programmable_pins, n_pins);
PTP_SHOW_INT(pps_available, pps);
static struct attribute *ptp_attrs[] = {
@@ -50,6 +52,7 @@ static struct attribute *ptp_attrs[] = {
&dev_attr_n_alarms.attr,
&dev_attr_n_external_timestamps.attr,
&dev_attr_n_periodic_outputs.attr,
+ &dev_attr_n_programmable_pins.attr,
&dev_attr_pps_available.attr,
NULL,
};
@@ -175,6 +178,63 @@ out:
return err;
}
+static int ptp_pin_name2index(struct ptp_clock *ptp, const char *name)
+{
+ int i;
+ for (i = 0; i < ptp->info->n_pins; i++) {
+ if (!strcmp(ptp->info->pin_config[i].name, name))
+ return i;
+ }
+ return -1;
+}
+
+static ssize_t ptp_pin_show(struct device *dev, struct device_attribute *attr,
+ char *page)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ unsigned int func, chan;
+ int index;
+
+ index = ptp_pin_name2index(ptp, attr->attr.name);
+ if (index < 0)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&ptp->pincfg_mux))
+ return -ERESTARTSYS;
+
+ func = ptp->info->pin_config[index].func;
+ chan = ptp->info->pin_config[index].chan;
+
+ mutex_unlock(&ptp->pincfg_mux);
+
+ return snprintf(page, PAGE_SIZE, "%u %u\n", func, chan);
+}
+
+static ssize_t ptp_pin_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ unsigned int func, chan;
+ int cnt, err, index;
+
+ cnt = sscanf(buf, "%u %u", &func, &chan);
+ if (cnt != 2)
+ return -EINVAL;
+
+ index = ptp_pin_name2index(ptp, attr->attr.name);
+ if (index < 0)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&ptp->pincfg_mux))
+ return -ERESTARTSYS;
+ err = ptp_set_pinfunc(ptp, index, func, chan);
+ mutex_unlock(&ptp->pincfg_mux);
+ if (err)
+ return err;
+
+ return count;
+}
+
static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store);
static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL);
static DEVICE_ATTR(period, 0220, NULL, period_store);
@@ -195,9 +255,56 @@ int ptp_cleanup_sysfs(struct ptp_clock *ptp)
if (info->pps)
device_remove_file(dev, &dev_attr_pps_enable);
+ if (info->n_pins) {
+ sysfs_remove_group(&dev->kobj, &ptp->pin_attr_group);
+ kfree(ptp->pin_attr);
+ kfree(ptp->pin_dev_attr);
+ }
return 0;
}
+static int ptp_populate_pins(struct ptp_clock *ptp)
+{
+ struct device *dev = ptp->dev;
+ struct ptp_clock_info *info = ptp->info;
+ int err = -ENOMEM, i, n_pins = info->n_pins;
+
+ ptp->pin_dev_attr = kzalloc(n_pins * sizeof(*ptp->pin_dev_attr),
+ GFP_KERNEL);
+ if (!ptp->pin_dev_attr)
+ goto no_dev_attr;
+
+ ptp->pin_attr = kzalloc((1 + n_pins) * sizeof(struct attribute *),
+ GFP_KERNEL);
+ if (!ptp->pin_attr)
+ goto no_pin_attr;
+
+ for (i = 0; i < n_pins; i++) {
+ struct device_attribute *da = &ptp->pin_dev_attr[i];
+ sysfs_attr_init(&da->attr);
+ da->attr.name = info->pin_config[i].name;
+ da->attr.mode = 0644;
+ da->show = ptp_pin_show;
+ da->store = ptp_pin_store;
+ ptp->pin_attr[i] = &da->attr;
+ }
+
+ ptp->pin_attr_group.name = "pins";
+ ptp->pin_attr_group.attrs = ptp->pin_attr;
+
+ err = sysfs_create_group(&dev->kobj, &ptp->pin_attr_group);
+ if (err)
+ goto no_group;
+ return 0;
+
+no_group:
+ kfree(ptp->pin_attr);
+no_pin_attr:
+ kfree(ptp->pin_dev_attr);
+no_dev_attr:
+ return err;
+}
+
int ptp_populate_sysfs(struct ptp_clock *ptp)
{
struct device *dev = ptp->dev;
@@ -222,7 +329,15 @@ int ptp_populate_sysfs(struct ptp_clock *ptp)
if (err)
goto out4;
}
+ if (info->n_pins) {
+ err = ptp_populate_pins(ptp);
+ if (err)
+ goto out5;
+ }
return 0;
+out5:
+ if (info->pps)
+ device_remove_file(dev, &dev_attr_pps_enable);
out4:
if (info->n_per_out)
device_remove_file(dev, &dev_attr_period);
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 22f2f2857b82..5b34ff29ea38 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -71,6 +71,15 @@ config PWM_BFIN
To compile this driver as a module, choose M here: the module
will be called pwm-bfin.
+config PWM_CLPS711X
+ tristate "CLPS711X PWM support"
+ depends on ARCH_CLPS711X || COMPILE_TEST
+ help
+ Generic PWM framework driver for Cirrus Logic CLPS711X.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-clps711x.
+
config PWM_EP93XX
tristate "Cirrus Logic EP93xx PWM support"
depends on ARCH_EP93XX
@@ -80,6 +89,16 @@ config PWM_EP93XX
To compile this driver as a module, choose M here: the module
will be called pwm-ep93xx.
+config PWM_FSL_FTM
+ tristate "Freescale FlexTimer Module (FTM) PWM support"
+ depends on OF
+ help
+ Generic FTM PWM framework driver for Freescale VF610 and
+ Layerscape LS-1 SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-fsl-ftm.
+
config PWM_IMX
tristate "i.MX PWM support"
depends on ARCH_MXC
@@ -119,6 +138,16 @@ config PWM_LPC32XX
To compile this driver as a module, choose M here: the module
will be called pwm-lpc32xx.
+config PWM_LPSS
+ tristate "Intel LPSS PWM support"
+ depends on ACPI
+ help
+ Generic PWM framework driver for Intel Low Power Subsystem PWM
+ controller.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-lpss.
+
config PWM_MXS
tristate "Freescale MXS PWM support"
depends on ARCH_MXS && OF
@@ -160,6 +189,7 @@ config PWM_PXA
config PWM_RENESAS_TPU
tristate "Renesas TPU PWM support"
depends on ARCH_SHMOBILE || COMPILE_TEST
+ depends on HAS_IOMEM
help
This driver exposes the Timer Pulse Unit (TPU) PWM controller found
in Renesas chips through the PWM API.
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index d8906ec69976..e57d2c38a794 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -4,11 +4,14 @@ obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o
obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
+obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
+obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o
obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
+obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o
obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index bf4144a14661..0adc952cc4ef 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -32,6 +32,7 @@
/* Bit field in CMR */
#define PWM_CMR_CPOL (1 << 9)
#define PWM_CMR_UPD_CDTY (1 << 10)
+#define PWM_CMR_CPRE_MSK 0xF
/* The following registers for PWM v1 */
#define PWMV1_CDTY 0x04
@@ -104,6 +105,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
unsigned long clk_rate, prd, dty;
unsigned long long div;
unsigned int pres = 0;
+ u32 val;
int ret;
if (test_bit(PWMF_ENABLED, &pwm->flags) && (period_ns != pwm->period)) {
@@ -131,7 +133,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
prd = div;
div *= duty_ns;
do_div(div, period_ns);
- dty = div;
+ dty = prd - div;
ret = clk_enable(atmel_pwm->clk);
if (ret) {
@@ -139,7 +141,10 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
return ret;
}
- atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, pres);
+ /* It is necessary to preserve CPOL, inside CMR */
+ val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
+ val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK);
+ atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
atmel_pwm->config(chip, pwm, dty, prd);
clk_disable(atmel_pwm->clk);
diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c
new file mode 100644
index 000000000000..fafb6a0111b0
--- /dev/null
+++ b/drivers/pwm/pwm-clps711x.c
@@ -0,0 +1,176 @@
+/*
+ * Cirrus Logic CLPS711X PWM driver
+ *
+ * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+struct clps711x_chip {
+ struct pwm_chip chip;
+ void __iomem *pmpcon;
+ struct clk *clk;
+ spinlock_t lock;
+};
+
+static inline struct clps711x_chip *to_clps711x_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct clps711x_chip, chip);
+}
+
+static void clps711x_pwm_update_val(struct clps711x_chip *priv, u32 n, u32 v)
+{
+ /* PWM0 - bits 4..7, PWM1 - bits 8..11 */
+ u32 shift = (n + 1) * 4;
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ tmp = readl(priv->pmpcon);
+ tmp &= ~(0xf << shift);
+ tmp |= v << shift;
+ writel(tmp, priv->pmpcon);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static unsigned int clps711x_get_duty(struct pwm_device *pwm, unsigned int v)
+{
+ /* Duty cycle 0..15 max */
+ return DIV_ROUND_CLOSEST(v * 0xf, pwm_get_period(pwm));
+}
+
+static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct clps711x_chip *priv = to_clps711x_chip(chip);
+ unsigned int freq = clk_get_rate(priv->clk);
+
+ if (!freq)
+ return -EINVAL;
+
+ /* Store constant period value */
+ pwm_set_period(pwm, DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq));
+
+ return 0;
+}
+
+static int clps711x_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct clps711x_chip *priv = to_clps711x_chip(chip);
+ unsigned int duty;
+
+ if (period_ns != pwm_get_period(pwm))
+ return -EINVAL;
+
+ duty = clps711x_get_duty(pwm, duty_ns);
+ clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
+
+ return 0;
+}
+
+static int clps711x_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct clps711x_chip *priv = to_clps711x_chip(chip);
+ unsigned int duty;
+
+ duty = clps711x_get_duty(pwm, pwm_get_duty_cycle(pwm));
+ clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
+
+ return 0;
+}
+
+static void clps711x_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct clps711x_chip *priv = to_clps711x_chip(chip);
+
+ clps711x_pwm_update_val(priv, pwm->hwpwm, 0);
+}
+
+static const struct pwm_ops clps711x_pwm_ops = {
+ .request = clps711x_pwm_request,
+ .config = clps711x_pwm_config,
+ .enable = clps711x_pwm_enable,
+ .disable = clps711x_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static struct pwm_device *clps711x_pwm_xlate(struct pwm_chip *chip,
+ const struct of_phandle_args *args)
+{
+ if (args->args[0] >= chip->npwm)
+ return ERR_PTR(-EINVAL);
+
+ return pwm_request_from_chip(chip, args->args[0], NULL);
+}
+
+static int clps711x_pwm_probe(struct platform_device *pdev)
+{
+ struct clps711x_chip *priv;
+ struct resource *res;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->pmpcon = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->pmpcon))
+ return PTR_ERR(priv->pmpcon);
+
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ priv->chip.ops = &clps711x_pwm_ops;
+ priv->chip.dev = &pdev->dev;
+ priv->chip.base = -1;
+ priv->chip.npwm = 2;
+ priv->chip.of_xlate = clps711x_pwm_xlate;
+ priv->chip.of_pwm_n_cells = 1;
+
+ spin_lock_init(&priv->lock);
+
+ platform_set_drvdata(pdev, priv);
+
+ return pwmchip_add(&priv->chip);
+}
+
+static int clps711x_pwm_remove(struct platform_device *pdev)
+{
+ struct clps711x_chip *priv = platform_get_drvdata(pdev);
+
+ return pwmchip_remove(&priv->chip);
+}
+
+static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = {
+ { .compatible = "cirrus,clps711x-pwm", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids);
+
+static struct platform_driver clps711x_pwm_driver = {
+ .driver = {
+ .name = "clps711x-pwm",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(clps711x_pwm_dt_ids),
+ },
+ .probe = clps711x_pwm_probe,
+ .remove = clps711x_pwm_remove,
+};
+module_platform_driver(clps711x_pwm_driver);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CLPS711X PWM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c
new file mode 100644
index 000000000000..420169e96b5f
--- /dev/null
+++ b/drivers/pwm/pwm-fsl-ftm.c
@@ -0,0 +1,495 @@
+/*
+ * Freescale FlexTimer Module (FTM) PWM Driver
+ *
+ * Copyright 2012-2013 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/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define FTM_SC 0x00
+#define FTM_SC_CLK_MASK 0x3
+#define FTM_SC_CLK_SHIFT 3
+#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_SHIFT)
+#define FTM_SC_PS_MASK 0x7
+#define FTM_SC_PS_SHIFT 0
+
+#define FTM_CNT 0x04
+#define FTM_MOD 0x08
+
+#define FTM_CSC_BASE 0x0C
+#define FTM_CSC_MSB BIT(5)
+#define FTM_CSC_MSA BIT(4)
+#define FTM_CSC_ELSB BIT(3)
+#define FTM_CSC_ELSA BIT(2)
+#define FTM_CSC(_channel) (FTM_CSC_BASE + ((_channel) * 8))
+
+#define FTM_CV_BASE 0x10
+#define FTM_CV(_channel) (FTM_CV_BASE + ((_channel) * 8))
+
+#define FTM_CNTIN 0x4C
+#define FTM_STATUS 0x50
+
+#define FTM_MODE 0x54
+#define FTM_MODE_FTMEN BIT(0)
+#define FTM_MODE_INIT BIT(2)
+#define FTM_MODE_PWMSYNC BIT(3)
+
+#define FTM_SYNC 0x58
+#define FTM_OUTINIT 0x5C
+#define FTM_OUTMASK 0x60
+#define FTM_COMBINE 0x64
+#define FTM_DEADTIME 0x68
+#define FTM_EXTTRIG 0x6C
+#define FTM_POL 0x70
+#define FTM_FMS 0x74
+#define FTM_FILTER 0x78
+#define FTM_FLTCTRL 0x7C
+#define FTM_QDCTRL 0x80
+#define FTM_CONF 0x84
+#define FTM_FLTPOL 0x88
+#define FTM_SYNCONF 0x8C
+#define FTM_INVCTRL 0x90
+#define FTM_SWOCTRL 0x94
+#define FTM_PWMLOAD 0x98
+
+enum fsl_pwm_clk {
+ FSL_PWM_CLK_SYS,
+ FSL_PWM_CLK_FIX,
+ FSL_PWM_CLK_EXT,
+ FSL_PWM_CLK_CNTEN,
+ FSL_PWM_CLK_MAX
+};
+
+struct fsl_pwm_chip {
+ struct pwm_chip chip;
+
+ struct mutex lock;
+
+ unsigned int use_count;
+ unsigned int cnt_select;
+ unsigned int clk_ps;
+
+ void __iomem *base;
+
+ int period_ns;
+
+ struct clk *clk[FSL_PWM_CLK_MAX];
+};
+
+static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct fsl_pwm_chip, chip);
+}
+
+static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+
+ return clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]);
+}
+
+static void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+
+ clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
+}
+
+static int fsl_pwm_calculate_default_ps(struct fsl_pwm_chip *fpc,
+ enum fsl_pwm_clk index)
+{
+ unsigned long sys_rate, cnt_rate;
+ unsigned long long ratio;
+
+ sys_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_SYS]);
+ if (!sys_rate)
+ return -EINVAL;
+
+ cnt_rate = clk_get_rate(fpc->clk[fpc->cnt_select]);
+ if (!cnt_rate)
+ return -EINVAL;
+
+ switch (index) {
+ case FSL_PWM_CLK_SYS:
+ fpc->clk_ps = 1;
+ break;
+ case FSL_PWM_CLK_FIX:
+ ratio = 2 * cnt_rate - 1;
+ do_div(ratio, sys_rate);
+ fpc->clk_ps = ratio;
+ break;
+ case FSL_PWM_CLK_EXT:
+ ratio = 4 * cnt_rate - 1;
+ do_div(ratio, sys_rate);
+ fpc->clk_ps = ratio;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned long fsl_pwm_calculate_cycles(struct fsl_pwm_chip *fpc,
+ unsigned long period_ns)
+{
+ unsigned long long c, c0;
+
+ c = clk_get_rate(fpc->clk[fpc->cnt_select]);
+ c = c * period_ns;
+ do_div(c, 1000000000UL);
+
+ do {
+ c0 = c;
+ do_div(c0, (1 << fpc->clk_ps));
+ if (c0 <= 0xFFFF)
+ return (unsigned long)c0;
+ } while (++fpc->clk_ps < 8);
+
+ return 0;
+}
+
+static unsigned long fsl_pwm_calculate_period_cycles(struct fsl_pwm_chip *fpc,
+ unsigned long period_ns,
+ enum fsl_pwm_clk index)
+{
+ int ret;
+
+ ret = fsl_pwm_calculate_default_ps(fpc, index);
+ if (ret) {
+ dev_err(fpc->chip.dev,
+ "failed to calculate default prescaler: %d\n",
+ ret);
+ return 0;
+ }
+
+ return fsl_pwm_calculate_cycles(fpc, period_ns);
+}
+
+static unsigned long fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc,
+ unsigned long period_ns)
+{
+ enum fsl_pwm_clk m0, m1;
+ unsigned long fix_rate, ext_rate, cycles;
+
+ cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns,
+ FSL_PWM_CLK_SYS);
+ if (cycles) {
+ fpc->cnt_select = FSL_PWM_CLK_SYS;
+ return cycles;
+ }
+
+ fix_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_FIX]);
+ ext_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_EXT]);
+
+ if (fix_rate > ext_rate) {
+ m0 = FSL_PWM_CLK_FIX;
+ m1 = FSL_PWM_CLK_EXT;
+ } else {
+ m0 = FSL_PWM_CLK_EXT;
+ m1 = FSL_PWM_CLK_FIX;
+ }
+
+ cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, m0);
+ if (cycles) {
+ fpc->cnt_select = m0;
+ return cycles;
+ }
+
+ fpc->cnt_select = m1;
+
+ return fsl_pwm_calculate_period_cycles(fpc, period_ns, m1);
+}
+
+static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc,
+ unsigned long period_ns,
+ unsigned long duty_ns)
+{
+ unsigned long long val, duty;
+
+ val = readl(fpc->base + FTM_MOD);
+ duty = duty_ns * (val + 1);
+ do_div(duty, period_ns);
+
+ return (unsigned long)duty;
+}
+
+static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ u32 val, period, duty;
+
+ mutex_lock(&fpc->lock);
+
+ /*
+ * The Freescale FTM controller supports only a single period for
+ * all PWM channels, therefore incompatible changes need to be
+ * refused.
+ */
+ if (fpc->period_ns && fpc->period_ns != period_ns) {
+ dev_err(fpc->chip.dev,
+ "conflicting period requested for PWM %u\n",
+ pwm->hwpwm);
+ mutex_unlock(&fpc->lock);
+ return -EBUSY;
+ }
+
+ if (!fpc->period_ns && duty_ns) {
+ period = fsl_pwm_calculate_period(fpc, period_ns);
+ if (!period) {
+ dev_err(fpc->chip.dev, "failed to calculate period\n");
+ mutex_unlock(&fpc->lock);
+ return -EINVAL;
+ }
+
+ val = readl(fpc->base + FTM_SC);
+ val &= ~(FTM_SC_PS_MASK << FTM_SC_PS_SHIFT);
+ val |= fpc->clk_ps;
+ writel(val, fpc->base + FTM_SC);
+ writel(period - 1, fpc->base + FTM_MOD);
+
+ fpc->period_ns = period_ns;
+ }
+
+ mutex_unlock(&fpc->lock);
+
+ duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns);
+
+ writel(FTM_CSC_MSB | FTM_CSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm));
+ writel(duty, fpc->base + FTM_CV(pwm->hwpwm));
+
+ return 0;
+}
+
+static int fsl_pwm_set_polarity(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ u32 val;
+
+ val = readl(fpc->base + FTM_POL);
+
+ if (polarity == PWM_POLARITY_INVERSED)
+ val |= BIT(pwm->hwpwm);
+ else
+ val &= ~BIT(pwm->hwpwm);
+
+ writel(val, fpc->base + FTM_POL);
+
+ return 0;
+}
+
+static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc)
+{
+ u32 val;
+ int ret;
+
+ if (fpc->use_count != 0)
+ return 0;
+
+ /* select counter clock source */
+ val = readl(fpc->base + FTM_SC);
+ val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT);
+ val |= FTM_SC_CLK(fpc->cnt_select);
+ writel(val, fpc->base + FTM_SC);
+
+ ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]);
+ if (ret) {
+ clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
+ return ret;
+ }
+
+ fpc->use_count++;
+
+ return 0;
+}
+
+static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ u32 val;
+ int ret;
+
+ mutex_lock(&fpc->lock);
+ val = readl(fpc->base + FTM_OUTMASK);
+ val &= ~BIT(pwm->hwpwm);
+ writel(val, fpc->base + FTM_OUTMASK);
+
+ ret = fsl_counter_clock_enable(fpc);
+ mutex_unlock(&fpc->lock);
+
+ return ret;
+}
+
+static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
+{
+ u32 val;
+
+ /*
+ * already disabled, do nothing
+ */
+ if (fpc->use_count == 0)
+ return;
+
+ /* there are still users, so can't disable yet */
+ if (--fpc->use_count > 0)
+ return;
+
+ /* no users left, disable PWM counter clock */
+ val = readl(fpc->base + FTM_SC);
+ val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT);
+ writel(val, fpc->base + FTM_SC);
+
+ clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
+ clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
+}
+
+static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ u32 val;
+
+ mutex_lock(&fpc->lock);
+ val = readl(fpc->base + FTM_OUTMASK);
+ val |= BIT(pwm->hwpwm);
+ writel(val, fpc->base + FTM_OUTMASK);
+
+ fsl_counter_clock_disable(fpc);
+
+ val = readl(fpc->base + FTM_OUTMASK);
+
+ if ((val & 0xFF) == 0xFF)
+ fpc->period_ns = 0;
+
+ mutex_unlock(&fpc->lock);
+}
+
+static const struct pwm_ops fsl_pwm_ops = {
+ .request = fsl_pwm_request,
+ .free = fsl_pwm_free,
+ .config = fsl_pwm_config,
+ .set_polarity = fsl_pwm_set_polarity,
+ .enable = fsl_pwm_enable,
+ .disable = fsl_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int fsl_pwm_init(struct fsl_pwm_chip *fpc)
+{
+ int ret;
+
+ ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]);
+ if (ret)
+ return ret;
+
+ writel(0x00, fpc->base + FTM_CNTIN);
+ writel(0x00, fpc->base + FTM_OUTINIT);
+ writel(0xFF, fpc->base + FTM_OUTMASK);
+
+ clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
+
+ return 0;
+}
+
+static int fsl_pwm_probe(struct platform_device *pdev)
+{
+ struct fsl_pwm_chip *fpc;
+ struct resource *res;
+ int ret;
+
+ fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL);
+ if (!fpc)
+ return -ENOMEM;
+
+ mutex_init(&fpc->lock);
+
+ fpc->chip.dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ fpc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fpc->base))
+ return PTR_ERR(fpc->base);
+
+ fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) {
+ dev_err(&pdev->dev, "failed to get \"ftm_sys\" clock\n");
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_SYS]);
+ }
+
+ fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(fpc->chip.dev, "ftm_fix");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_FIX]))
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_FIX]);
+
+ fpc->clk[FSL_PWM_CLK_EXT] = devm_clk_get(fpc->chip.dev, "ftm_ext");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_EXT]))
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_EXT]);
+
+ fpc->clk[FSL_PWM_CLK_CNTEN] =
+ devm_clk_get(fpc->chip.dev, "ftm_cnt_clk_en");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]))
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]);
+
+ fpc->chip.ops = &fsl_pwm_ops;
+ fpc->chip.of_xlate = of_pwm_xlate_with_flags;
+ fpc->chip.of_pwm_n_cells = 3;
+ fpc->chip.base = -1;
+ fpc->chip.npwm = 8;
+
+ ret = pwmchip_add(&fpc->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, fpc);
+
+ return fsl_pwm_init(fpc);
+}
+
+static int fsl_pwm_remove(struct platform_device *pdev)
+{
+ struct fsl_pwm_chip *fpc = platform_get_drvdata(pdev);
+
+ return pwmchip_remove(&fpc->chip);
+}
+
+static const struct of_device_id fsl_pwm_dt_ids[] = {
+ { .compatible = "fsl,vf610-ftm-pwm", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_pwm_dt_ids);
+
+static struct platform_driver fsl_pwm_driver = {
+ .driver = {
+ .name = "fsl-ftm-pwm",
+ .of_match_table = fsl_pwm_dt_ids,
+ },
+ .probe = fsl_pwm_probe,
+ .remove = fsl_pwm_remove,
+};
+module_platform_driver(fsl_pwm_driver);
+
+MODULE_DESCRIPTION("Freescale FlexTimer Module PWM Driver");
+MODULE_AUTHOR("Xiubo Li <Li.Xiubo@freescale.com>");
+MODULE_ALIAS("platform:fsl-ftm-pwm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c
new file mode 100644
index 000000000000..449e372050a0
--- /dev/null
+++ b/drivers/pwm/pwm-lpss.c
@@ -0,0 +1,183 @@
+/*
+ * Intel Low Power Subsystem PWM controller driver
+ *
+ * Copyright (C) 2014, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ * Author: Chew Kean Ho <kean.ho.chew@intel.com>
+ * Author: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com>
+ * Author: Chew Chiau Ee <chiau.ee.chew@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include <linux/platform_device.h>
+
+#define PWM 0x00000000
+#define PWM_ENABLE BIT(31)
+#define PWM_SW_UPDATE BIT(30)
+#define PWM_BASE_UNIT_SHIFT 8
+#define PWM_BASE_UNIT_MASK 0x00ffff00
+#define PWM_ON_TIME_DIV_MASK 0x000000ff
+#define PWM_DIVISION_CORRECTION 0x2
+#define PWM_LIMIT (0x8000 + PWM_DIVISION_CORRECTION)
+#define NSECS_PER_SEC 1000000000UL
+
+struct pwm_lpss_chip {
+ struct pwm_chip chip;
+ void __iomem *regs;
+ struct clk *clk;
+};
+
+static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
+{
+ return container_of(chip, struct pwm_lpss_chip, chip);
+}
+
+static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct pwm_lpss_chip *lpwm = to_lpwm(chip);
+ u8 on_time_div;
+ unsigned long c;
+ unsigned long long base_unit, freq = NSECS_PER_SEC;
+ u32 ctrl;
+
+ do_div(freq, period_ns);
+
+ /* The equation is: base_unit = ((freq / c) * 65536) + correction */
+ base_unit = freq * 65536;
+
+ c = clk_get_rate(lpwm->clk);
+ if (!c)
+ return -EINVAL;
+
+ do_div(base_unit, c);
+ base_unit += PWM_DIVISION_CORRECTION;
+ if (base_unit > PWM_LIMIT)
+ return -EINVAL;
+
+ if (duty_ns <= 0)
+ duty_ns = 1;
+ on_time_div = 255 - (255 * duty_ns / period_ns);
+
+ ctrl = readl(lpwm->regs + PWM);
+ ctrl &= ~(PWM_BASE_UNIT_MASK | PWM_ON_TIME_DIV_MASK);
+ ctrl |= (u16) base_unit << PWM_BASE_UNIT_SHIFT;
+ ctrl |= on_time_div;
+ /* request PWM to update on next cycle */
+ ctrl |= PWM_SW_UPDATE;
+ writel(ctrl, lpwm->regs + PWM);
+
+ return 0;
+}
+
+static int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pwm_lpss_chip *lpwm = to_lpwm(chip);
+ u32 ctrl;
+ int ret;
+
+ ret = clk_prepare_enable(lpwm->clk);
+ if (ret)
+ return ret;
+
+ ctrl = readl(lpwm->regs + PWM);
+ writel(ctrl | PWM_ENABLE, lpwm->regs + PWM);
+
+ return 0;
+}
+
+static void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pwm_lpss_chip *lpwm = to_lpwm(chip);
+ u32 ctrl;
+
+ ctrl = readl(lpwm->regs + PWM);
+ writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM);
+
+ clk_disable_unprepare(lpwm->clk);
+}
+
+static const struct pwm_ops pwm_lpss_ops = {
+ .config = pwm_lpss_config,
+ .enable = pwm_lpss_enable,
+ .disable = pwm_lpss_disable,
+ .owner = THIS_MODULE,
+};
+
+static const struct acpi_device_id pwm_lpss_acpi_match[] = {
+ { "80860F09", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
+
+static int pwm_lpss_probe(struct platform_device *pdev)
+{
+ struct pwm_lpss_chip *lpwm;
+ struct resource *r;
+ int ret;
+
+ lpwm = devm_kzalloc(&pdev->dev, sizeof(*lpwm), GFP_KERNEL);
+ if (!lpwm)
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ lpwm->regs = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(lpwm->regs))
+ return PTR_ERR(lpwm->regs);
+
+ lpwm->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(lpwm->clk)) {
+ dev_err(&pdev->dev, "failed to get PWM clock\n");
+ return PTR_ERR(lpwm->clk);
+ }
+
+ lpwm->chip.dev = &pdev->dev;
+ lpwm->chip.ops = &pwm_lpss_ops;
+ lpwm->chip.base = -1;
+ lpwm->chip.npwm = 1;
+
+ ret = pwmchip_add(&lpwm->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, lpwm);
+ return 0;
+}
+
+static int pwm_lpss_remove(struct platform_device *pdev)
+{
+ struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev);
+ u32 ctrl;
+
+ ctrl = readl(lpwm->regs + PWM);
+ writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM);
+
+ return pwmchip_remove(&lpwm->chip);
+}
+
+static struct platform_driver pwm_lpss_driver = {
+ .driver = {
+ .name = "pwm-lpss",
+ .acpi_match_table = pwm_lpss_acpi_match,
+ },
+ .probe = pwm_lpss_probe,
+ .remove = pwm_lpss_remove,
+};
+module_platform_driver(pwm_lpss_driver);
+
+MODULE_DESCRIPTION("PWM driver for Intel LPSS");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pwm-lpss");
diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
index 8d995731cef8..cd356d870244 100644
--- a/drivers/pwm/pwm-pxa.c
+++ b/drivers/pwm/pwm-pxa.c
@@ -127,12 +127,12 @@ static struct pwm_ops pxa_pwm_ops = {
#ifdef CONFIG_OF
/*
- * Device tree users must create one device instance for each pwm channel.
+ * Device tree users must create one device instance for each PWM channel.
* Hence we dispense with the HAS_SECONDARY_PWM and "tell" the original driver
* code that this is a single channel pxa25x-pwm. Currently all devices are
* supported identically.
*/
-static struct of_device_id pwm_of_match[] = {
+static const struct of_device_id pwm_of_match[] = {
{ .compatible = "marvell,pxa250-pwm", .data = &pwm_id_table[0]},
{ .compatible = "marvell,pxa270-pwm", .data = &pwm_id_table[0]},
{ .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0]},
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c
index b59639e0c029..d66529a995a1 100644
--- a/drivers/pwm/pwm-samsung.c
+++ b/drivers/pwm/pwm-samsung.c
@@ -598,9 +598,8 @@ static int pwm_samsung_resume(struct device *dev)
}
#endif
-static const struct dev_pm_ops pwm_samsung_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pwm_samsung_suspend, pwm_samsung_resume)
-};
+static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, pwm_samsung_suspend,
+ pwm_samsung_resume);
static struct platform_driver pwm_samsung_driver = {
.driver = {
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index bac485acc7f3..9a09f3cdbabb 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2138,7 +2138,7 @@ EXPORT_SYMBOL_GPL(regulator_is_enabled);
* @regulator: regulator source
*
* Returns positive if the regulator driver backing the source/client
- * can change its voltage, false otherwise. Usefull for detecting fixed
+ * can change its voltage, false otherwise. Useful for detecting fixed
* or dummy regulators and disabling voltage change logic in the client
* driver.
*/
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index c9d04f797862..0615f50a14cd 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -11,3 +11,5 @@ menuconfig RESET_CONTROLLER
via GPIOs or SoC-internal reset controller modules.
If unsure, say no.
+
+source "drivers/reset/sti/Kconfig"
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index cc29832c9638..4f60caf750ce 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_RESET_CONTROLLER) += core.o
obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
+obj-$(CONFIG_ARCH_STI) += sti/
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index d1b6089a0ef8..baeaf82d40d9 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -43,7 +43,7 @@ struct reset_control {
* This simple translation function should be used for reset controllers
* with 1:1 mapping, where reset lines can be indexed by number without gaps.
*/
-int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
+static int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec)
{
if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells))
@@ -54,7 +54,6 @@ int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
return reset_spec->args[0];
}
-EXPORT_SYMBOL_GPL(of_reset_simple_xlate);
/**
* reset_controller_register - register a reset controller device
@@ -127,15 +126,16 @@ int reset_control_deassert(struct reset_control *rstc)
EXPORT_SYMBOL_GPL(reset_control_deassert);
/**
- * reset_control_get - Lookup and obtain a reference to a reset controller.
- * @dev: device to be reset by the controller
+ * of_reset_control_get - Lookup and obtain a reference to a reset controller.
+ * @node: device to be reset by the controller
* @id: reset line name
*
* Returns a struct reset_control or IS_ERR() condition containing errno.
*
* Use of id names is optional.
*/
-struct reset_control *reset_control_get(struct device *dev, const char *id)
+struct reset_control *of_reset_control_get(struct device_node *node,
+ const char *id)
{
struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER);
struct reset_controller_dev *r, *rcdev;
@@ -144,13 +144,10 @@ struct reset_control *reset_control_get(struct device *dev, const char *id)
int rstc_id;
int ret;
- if (!dev)
- return ERR_PTR(-EINVAL);
-
if (id)
- index = of_property_match_string(dev->of_node,
+ index = of_property_match_string(node,
"reset-names", id);
- ret = of_parse_phandle_with_args(dev->of_node, "resets", "#reset-cells",
+ ret = of_parse_phandle_with_args(node, "resets", "#reset-cells",
index, &args);
if (ret)
return ERR_PTR(ret);
@@ -167,7 +164,7 @@ struct reset_control *reset_control_get(struct device *dev, const char *id)
if (!rcdev) {
mutex_unlock(&reset_controller_list_mutex);
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(-EPROBE_DEFER);
}
rstc_id = rcdev->of_xlate(rcdev, &args);
@@ -185,12 +182,35 @@ struct reset_control *reset_control_get(struct device *dev, const char *id)
return ERR_PTR(-ENOMEM);
}
- rstc->dev = dev;
rstc->rcdev = rcdev;
rstc->id = rstc_id;
return rstc;
}
+EXPORT_SYMBOL_GPL(of_reset_control_get);
+
+/**
+ * reset_control_get - Lookup and obtain a reference to a reset controller.
+ * @dev: device to be reset by the controller
+ * @id: reset line name
+ *
+ * Returns a struct reset_control or IS_ERR() condition containing errno.
+ *
+ * Use of id names is optional.
+ */
+struct reset_control *reset_control_get(struct device *dev, const char *id)
+{
+ struct reset_control *rstc;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ rstc = of_reset_control_get(dev->of_node, id);
+ if (!IS_ERR(rstc))
+ rstc->dev = dev;
+
+ return rstc;
+}
EXPORT_SYMBOL_GPL(reset_control_get);
/**
@@ -243,33 +263,6 @@ struct reset_control *devm_reset_control_get(struct device *dev, const char *id)
}
EXPORT_SYMBOL_GPL(devm_reset_control_get);
-static int devm_reset_control_match(struct device *dev, void *res, void *data)
-{
- struct reset_control **rstc = res;
- if (WARN_ON(!rstc || !*rstc))
- return 0;
- return *rstc == data;
-}
-
-/**
- * devm_reset_control_put - resource managed reset_control_put()
- * @rstc: reset controller to free
- *
- * Deallocate a reset control allocated withd devm_reset_control_get().
- * This function will not need to be called normally, as devres will take
- * care of freeing the resource.
- */
-void devm_reset_control_put(struct reset_control *rstc)
-{
- int ret;
-
- ret = devres_release(rstc->dev, devm_reset_control_release,
- devm_reset_control_match, rstc);
- if (ret)
- WARN_ON(ret);
-}
-EXPORT_SYMBOL_GPL(devm_reset_control_put);
-
/**
* device_reset - find reset controller associated with the device
* and perform reset
diff --git a/drivers/reset/sti/Kconfig b/drivers/reset/sti/Kconfig
new file mode 100644
index 000000000000..88d2d0316613
--- /dev/null
+++ b/drivers/reset/sti/Kconfig
@@ -0,0 +1,15 @@
+if ARCH_STI
+
+config STI_RESET_SYSCFG
+ bool
+ select RESET_CONTROLLER
+
+config STIH415_RESET
+ bool
+ select STI_RESET_SYSCFG
+
+config STIH416_RESET
+ bool
+ select STI_RESET_SYSCFG
+
+endif
diff --git a/drivers/reset/sti/Makefile b/drivers/reset/sti/Makefile
new file mode 100644
index 000000000000..be1c97647871
--- /dev/null
+++ b/drivers/reset/sti/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_STI_RESET_SYSCFG) += reset-syscfg.o
+
+obj-$(CONFIG_STIH415_RESET) += reset-stih415.o
+obj-$(CONFIG_STIH416_RESET) += reset-stih416.o
diff --git a/drivers/reset/sti/reset-stih415.c b/drivers/reset/sti/reset-stih415.c
new file mode 100644
index 000000000000..e6f6c41abe12
--- /dev/null
+++ b/drivers/reset/sti/reset-stih415.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited
+ * Author: Stephen Gallimore <stephen.gallimore@st.com>
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@st.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/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/reset-controller/stih415-resets.h>
+
+#include "reset-syscfg.h"
+
+/*
+ * STiH415 Peripheral powerdown definitions.
+ */
+static const char stih415_front[] = "st,stih415-front-syscfg";
+static const char stih415_rear[] = "st,stih415-rear-syscfg";
+static const char stih415_sbc[] = "st,stih415-sbc-syscfg";
+static const char stih415_lpm[] = "st,stih415-lpm-syscfg";
+
+#define STIH415_PDN_FRONT(_bit) \
+ _SYSCFG_RST_CH(stih415_front, SYSCFG_114, _bit, SYSSTAT_187, _bit)
+
+#define STIH415_PDN_REAR(_cntl, _stat) \
+ _SYSCFG_RST_CH(stih415_rear, SYSCFG_336, _cntl, SYSSTAT_384, _stat)
+
+#define STIH415_SRST_REAR(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih415_rear, _reg, _bit)
+
+#define STIH415_SRST_SBC(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih415_sbc, _reg, _bit)
+
+#define STIH415_SRST_FRONT(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih415_front, _reg, _bit)
+
+#define STIH415_SRST_LPM(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih415_lpm, _reg, _bit)
+
+#define SYSCFG_114 0x38 /* Powerdown request EMI/NAND/Keyscan */
+#define SYSSTAT_187 0x15c /* Powerdown status EMI/NAND/Keyscan */
+
+#define SYSCFG_336 0x90 /* Powerdown request USB/SATA/PCIe */
+#define SYSSTAT_384 0x150 /* Powerdown status USB/SATA/PCIe */
+
+#define SYSCFG_376 0x130 /* Reset generator 0 control 0 */
+#define SYSCFG_166 0x108 /* Softreset Ethernet 0 */
+#define SYSCFG_31 0x7c /* Softreset Ethernet 1 */
+#define LPM_SYSCFG_1 0x4 /* Softreset IRB */
+
+static const struct syscfg_reset_channel_data stih415_powerdowns[] = {
+ [STIH415_EMISS_POWERDOWN] = STIH415_PDN_FRONT(0),
+ [STIH415_NAND_POWERDOWN] = STIH415_PDN_FRONT(1),
+ [STIH415_KEYSCAN_POWERDOWN] = STIH415_PDN_FRONT(2),
+ [STIH415_USB0_POWERDOWN] = STIH415_PDN_REAR(0, 0),
+ [STIH415_USB1_POWERDOWN] = STIH415_PDN_REAR(1, 1),
+ [STIH415_USB2_POWERDOWN] = STIH415_PDN_REAR(2, 2),
+ [STIH415_SATA0_POWERDOWN] = STIH415_PDN_REAR(3, 3),
+ [STIH415_SATA1_POWERDOWN] = STIH415_PDN_REAR(4, 4),
+ [STIH415_PCIE_POWERDOWN] = STIH415_PDN_REAR(5, 8),
+};
+
+static const struct syscfg_reset_channel_data stih415_softresets[] = {
+ [STIH415_ETH0_SOFTRESET] = STIH415_SRST_FRONT(SYSCFG_166, 0),
+ [STIH415_ETH1_SOFTRESET] = STIH415_SRST_SBC(SYSCFG_31, 0),
+ [STIH415_IRB_SOFTRESET] = STIH415_SRST_LPM(LPM_SYSCFG_1, 6),
+ [STIH415_USB0_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 9),
+ [STIH415_USB1_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 10),
+ [STIH415_USB2_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 11),
+};
+
+static struct syscfg_reset_controller_data stih415_powerdown_controller = {
+ .wait_for_ack = true,
+ .nr_channels = ARRAY_SIZE(stih415_powerdowns),
+ .channels = stih415_powerdowns,
+};
+
+static struct syscfg_reset_controller_data stih415_softreset_controller = {
+ .wait_for_ack = false,
+ .active_low = true,
+ .nr_channels = ARRAY_SIZE(stih415_softresets),
+ .channels = stih415_softresets,
+};
+
+static struct of_device_id stih415_reset_match[] = {
+ { .compatible = "st,stih415-powerdown",
+ .data = &stih415_powerdown_controller, },
+ { .compatible = "st,stih415-softreset",
+ .data = &stih415_softreset_controller, },
+ {},
+};
+
+static struct platform_driver stih415_reset_driver = {
+ .probe = syscfg_reset_probe,
+ .driver = {
+ .name = "reset-stih415",
+ .owner = THIS_MODULE,
+ .of_match_table = stih415_reset_match,
+ },
+};
+
+static int __init stih415_reset_init(void)
+{
+ return platform_driver_register(&stih415_reset_driver);
+}
+arch_initcall(stih415_reset_init);
diff --git a/drivers/reset/sti/reset-stih416.c b/drivers/reset/sti/reset-stih416.c
new file mode 100644
index 000000000000..fe3bf02bdc8c
--- /dev/null
+++ b/drivers/reset/sti/reset-stih416.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited
+ * Author: Stephen Gallimore <stephen.gallimore@st.com>
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@st.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/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/reset-controller/stih416-resets.h>
+
+#include "reset-syscfg.h"
+
+/*
+ * STiH416 Peripheral powerdown definitions.
+ */
+static const char stih416_front[] = "st,stih416-front-syscfg";
+static const char stih416_rear[] = "st,stih416-rear-syscfg";
+static const char stih416_sbc[] = "st,stih416-sbc-syscfg";
+static const char stih416_lpm[] = "st,stih416-lpm-syscfg";
+static const char stih416_cpu[] = "st,stih416-cpu-syscfg";
+
+#define STIH416_PDN_FRONT(_bit) \
+ _SYSCFG_RST_CH(stih416_front, SYSCFG_1500, _bit, SYSSTAT_1578, _bit)
+
+#define STIH416_PDN_REAR(_cntl, _stat) \
+ _SYSCFG_RST_CH(stih416_rear, SYSCFG_2525, _cntl, SYSSTAT_2583, _stat)
+
+#define SYSCFG_1500 0x7d0 /* Powerdown request EMI/NAND/Keyscan */
+#define SYSSTAT_1578 0x908 /* Powerdown status EMI/NAND/Keyscan */
+
+#define SYSCFG_2525 0x834 /* Powerdown request USB/SATA/PCIe */
+#define SYSSTAT_2583 0x91c /* Powerdown status USB/SATA/PCIe */
+
+#define SYSCFG_2552 0x8A0 /* Reset Generator control 0 */
+#define SYSCFG_1539 0x86c /* Softreset Ethernet 0 */
+#define SYSCFG_510 0x7f8 /* Softreset Ethernet 1 */
+#define LPM_SYSCFG_1 0x4 /* Softreset IRB */
+#define SYSCFG_2553 0x8a4 /* Softreset SATA0/1, PCIE0/1 */
+#define SYSCFG_7563 0x8cc /* MPE softresets 0 */
+#define SYSCFG_7564 0x8d0 /* MPE softresets 1 */
+
+#define STIH416_SRST_CPU(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih416_cpu, _reg, _bit)
+
+#define STIH416_SRST_FRONT(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih416_front, _reg, _bit)
+
+#define STIH416_SRST_REAR(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih416_rear, _reg, _bit)
+
+#define STIH416_SRST_LPM(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih416_lpm, _reg, _bit)
+
+#define STIH416_SRST_SBC(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih416_sbc, _reg, _bit)
+
+static const struct syscfg_reset_channel_data stih416_powerdowns[] = {
+ [STIH416_EMISS_POWERDOWN] = STIH416_PDN_FRONT(0),
+ [STIH416_NAND_POWERDOWN] = STIH416_PDN_FRONT(1),
+ [STIH416_KEYSCAN_POWERDOWN] = STIH416_PDN_FRONT(2),
+ [STIH416_USB0_POWERDOWN] = STIH416_PDN_REAR(0, 0),
+ [STIH416_USB1_POWERDOWN] = STIH416_PDN_REAR(1, 1),
+ [STIH416_USB2_POWERDOWN] = STIH416_PDN_REAR(2, 2),
+ [STIH416_USB3_POWERDOWN] = STIH416_PDN_REAR(6, 5),
+ [STIH416_SATA0_POWERDOWN] = STIH416_PDN_REAR(3, 3),
+ [STIH416_SATA1_POWERDOWN] = STIH416_PDN_REAR(4, 4),
+ [STIH416_PCIE0_POWERDOWN] = STIH416_PDN_REAR(7, 9),
+ [STIH416_PCIE1_POWERDOWN] = STIH416_PDN_REAR(5, 8),
+};
+
+static const struct syscfg_reset_channel_data stih416_softresets[] = {
+ [STIH416_ETH0_SOFTRESET] = STIH416_SRST_FRONT(SYSCFG_1539, 0),
+ [STIH416_ETH1_SOFTRESET] = STIH416_SRST_SBC(SYSCFG_510, 0),
+ [STIH416_IRB_SOFTRESET] = STIH416_SRST_LPM(LPM_SYSCFG_1, 6),
+ [STIH416_USB0_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 9),
+ [STIH416_USB1_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 10),
+ [STIH416_USB2_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 11),
+ [STIH416_USB3_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 28),
+ [STIH416_SATA0_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 7),
+ [STIH416_SATA1_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 3),
+ [STIH416_PCIE0_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 15),
+ [STIH416_PCIE1_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 2),
+ [STIH416_AUD_DAC_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 14),
+ [STIH416_HDTVOUT_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 5),
+ [STIH416_VTAC_M_RX_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 25),
+ [STIH416_VTAC_A_RX_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 26),
+ [STIH416_SYNC_HD_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 5),
+ [STIH416_SYNC_SD_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 6),
+ [STIH416_BLITTER_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 10),
+ [STIH416_GPU_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 11),
+ [STIH416_VTAC_M_TX_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 18),
+ [STIH416_VTAC_A_TX_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 19),
+ [STIH416_VTG_AUX_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 21),
+ [STIH416_JPEG_DEC_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 23),
+ [STIH416_HVA_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 2),
+ [STIH416_COMPO_M_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 3),
+ [STIH416_COMPO_A_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 4),
+ [STIH416_VP8_DEC_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 10),
+ [STIH416_VTG_MAIN_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 16),
+};
+
+static struct syscfg_reset_controller_data stih416_powerdown_controller = {
+ .wait_for_ack = true,
+ .nr_channels = ARRAY_SIZE(stih416_powerdowns),
+ .channels = stih416_powerdowns,
+};
+
+static struct syscfg_reset_controller_data stih416_softreset_controller = {
+ .wait_for_ack = false,
+ .active_low = true,
+ .nr_channels = ARRAY_SIZE(stih416_softresets),
+ .channels = stih416_softresets,
+};
+
+static struct of_device_id stih416_reset_match[] = {
+ { .compatible = "st,stih416-powerdown",
+ .data = &stih416_powerdown_controller, },
+ { .compatible = "st,stih416-softreset",
+ .data = &stih416_softreset_controller, },
+ {},
+};
+
+static struct platform_driver stih416_reset_driver = {
+ .probe = syscfg_reset_probe,
+ .driver = {
+ .name = "reset-stih416",
+ .owner = THIS_MODULE,
+ .of_match_table = stih416_reset_match,
+ },
+};
+
+static int __init stih416_reset_init(void)
+{
+ return platform_driver_register(&stih416_reset_driver);
+}
+arch_initcall(stih416_reset_init);
diff --git a/drivers/reset/sti/reset-syscfg.c b/drivers/reset/sti/reset-syscfg.c
new file mode 100644
index 000000000000..a145cc066d4a
--- /dev/null
+++ b/drivers/reset/sti/reset-syscfg.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics Limited
+ * Author: Stephen Gallimore <stephen.gallimore@st.com>
+ *
+ * Inspired by mach-imx/src.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "reset-syscfg.h"
+
+/**
+ * Reset channel regmap configuration
+ *
+ * @reset: regmap field for the channel's reset bit.
+ * @ack: regmap field for the channel's ack bit (optional).
+ */
+struct syscfg_reset_channel {
+ struct regmap_field *reset;
+ struct regmap_field *ack;
+};
+
+/**
+ * A reset controller which groups together a set of related reset bits, which
+ * may be located in different system configuration registers.
+ *
+ * @rst: base reset controller structure.
+ * @active_low: are the resets in this controller active low, i.e. clearing
+ * the reset bit puts the hardware into reset.
+ * @channels: An array of reset channels for this controller.
+ */
+struct syscfg_reset_controller {
+ struct reset_controller_dev rst;
+ bool active_low;
+ struct syscfg_reset_channel *channels;
+};
+
+#define to_syscfg_reset_controller(_rst) \
+ container_of(_rst, struct syscfg_reset_controller, rst)
+
+static int syscfg_reset_program_hw(struct reset_controller_dev *rcdev,
+ unsigned long idx, int assert)
+{
+ struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev);
+ const struct syscfg_reset_channel *ch;
+ u32 ctrl_val = rst->active_low ? !assert : !!assert;
+ int err;
+
+ if (idx >= rcdev->nr_resets)
+ return -EINVAL;
+
+ ch = &rst->channels[idx];
+
+ err = regmap_field_write(ch->reset, ctrl_val);
+ if (err)
+ return err;
+
+ if (ch->ack) {
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ u32 ack_val;
+
+ while (true) {
+ err = regmap_field_read(ch->ack, &ack_val);
+ if (err)
+ return err;
+
+ if (ack_val == ctrl_val)
+ break;
+
+ if (time_after(jiffies, timeout))
+ return -ETIME;
+
+ cpu_relax();
+ }
+ }
+
+ return 0;
+}
+
+static int syscfg_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ return syscfg_reset_program_hw(rcdev, idx, true);
+}
+
+static int syscfg_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ return syscfg_reset_program_hw(rcdev, idx, false);
+}
+
+static int syscfg_reset_dev(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ int err = syscfg_reset_assert(rcdev, idx);
+ if (err)
+ return err;
+
+ return syscfg_reset_deassert(rcdev, idx);
+}
+
+static struct reset_control_ops syscfg_reset_ops = {
+ .reset = syscfg_reset_dev,
+ .assert = syscfg_reset_assert,
+ .deassert = syscfg_reset_deassert,
+};
+
+static int syscfg_reset_controller_register(struct device *dev,
+ const struct syscfg_reset_controller_data *data)
+{
+ struct syscfg_reset_controller *rc;
+ size_t size;
+ int i, err;
+
+ rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL);
+ if (!rc)
+ return -ENOMEM;
+
+ size = sizeof(struct syscfg_reset_channel) * data->nr_channels;
+
+ rc->channels = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!rc->channels)
+ return -ENOMEM;
+
+ rc->rst.ops = &syscfg_reset_ops,
+ rc->rst.of_node = dev->of_node;
+ rc->rst.nr_resets = data->nr_channels;
+ rc->active_low = data->active_low;
+
+ for (i = 0; i < data->nr_channels; i++) {
+ struct regmap *map;
+ struct regmap_field *f;
+ const char *compatible = data->channels[i].compatible;
+
+ map = syscon_regmap_lookup_by_compatible(compatible);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ f = devm_regmap_field_alloc(dev, map, data->channels[i].reset);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ rc->channels[i].reset = f;
+
+ if (!data->wait_for_ack)
+ continue;
+
+ f = devm_regmap_field_alloc(dev, map, data->channels[i].ack);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ rc->channels[i].ack = f;
+ }
+
+ err = reset_controller_register(&rc->rst);
+ if (!err)
+ dev_info(dev, "registered\n");
+
+ return err;
+}
+
+int syscfg_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = pdev ? &pdev->dev : NULL;
+ const struct of_device_id *match;
+
+ if (!dev || !dev->driver)
+ return -ENODEV;
+
+ match = of_match_device(dev->driver->of_match_table, dev);
+ if (!match || !match->data)
+ return -EINVAL;
+
+ return syscfg_reset_controller_register(dev, match->data);
+}
diff --git a/drivers/reset/sti/reset-syscfg.h b/drivers/reset/sti/reset-syscfg.h
new file mode 100644
index 000000000000..2cc2283bac40
--- /dev/null
+++ b/drivers/reset/sti/reset-syscfg.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited
+ * Author: Stephen Gallimore <stephen.gallimore@st.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 __STI_RESET_SYSCFG_H
+#define __STI_RESET_SYSCFG_H
+
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+/**
+ * Reset channel description for a system configuration register based
+ * reset controller.
+ *
+ * @compatible: Compatible string of the syscon regmap containing this
+ * channel's control and ack (status) bits.
+ * @reset: Regmap field description of the channel's reset bit.
+ * @ack: Regmap field description of the channel's acknowledge bit.
+ */
+struct syscfg_reset_channel_data {
+ const char *compatible;
+ struct reg_field reset;
+ struct reg_field ack;
+};
+
+#define _SYSCFG_RST_CH(_c, _rr, _rb, _ar, _ab) \
+ { .compatible = _c, \
+ .reset = REG_FIELD(_rr, _rb, _rb), \
+ .ack = REG_FIELD(_ar, _ab, _ab), }
+
+#define _SYSCFG_RST_CH_NO_ACK(_c, _rr, _rb) \
+ { .compatible = _c, \
+ .reset = REG_FIELD(_rr, _rb, _rb), }
+
+/**
+ * Description of a system configuration register based reset controller.
+ *
+ * @wait_for_ack: The controller will wait for reset assert and de-assert to
+ * be "ack'd" in a channel's ack field.
+ * @active_low: Are the resets in this controller active low, i.e. clearing
+ * the reset bit puts the hardware into reset.
+ * @nr_channels: The number of reset channels in this controller.
+ * @channels: An array of reset channel descriptions.
+ */
+struct syscfg_reset_controller_data {
+ bool wait_for_ack;
+ bool active_low;
+ int nr_channels;
+ const struct syscfg_reset_channel_data *channels;
+};
+
+/**
+ * syscfg_reset_probe(): platform device probe function used by syscfg
+ * reset controller drivers. This registers a reset
+ * controller configured by the OF match data for
+ * the compatible device which should be of type
+ * "struct syscfg_reset_controller_data".
+ *
+ * @pdev: platform device
+ */
+int syscfg_reset_probe(struct platform_device *pdev);
+
+#endif /* __STI_RESET_SYSCFG_H */
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index db933decc39c..2e565f8e5165 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -573,6 +573,18 @@ config RTC_DRV_DS1305
This driver can also be built as a module. If so, the module
will be called rtc-ds1305.
+config RTC_DRV_DS1347
+ tristate "Dallas/Maxim DS1347"
+ help
+ If you say yes here you get support for the
+ Dallas/Maxim DS1347 chips.
+
+ This driver only supports the RTC feature, and not other chip
+ features such as alarms.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1347.
+
config RTC_DRV_DS1390
tristate "Dallas/Maxim DS1390/93/94"
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index b427bf7dd20d..40a09915c8f6 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o
obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o
obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o
obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o
+obj-$(CONFIG_RTC_DRV_DS1347) += rtc-ds1347.o
obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o
obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds1390.o
obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 544be722937c..c2eff6082363 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -584,6 +584,9 @@ enum hrtimer_restart rtc_pie_update_irq(struct hrtimer *timer)
void rtc_update_irq(struct rtc_device *rtc,
unsigned long num, unsigned long events)
{
+ if (unlikely(IS_ERR_OR_NULL(rtc)))
+ return;
+
pm_stay_awake(rtc->dev.parent);
schedule_work(&rtc->irqwork);
}
diff --git a/drivers/rtc/rtc-as3722.c b/drivers/rtc/rtc-as3722.c
index 4af016985890..9f38eda69154 100644
--- a/drivers/rtc/rtc-as3722.c
+++ b/drivers/rtc/rtc-as3722.c
@@ -242,9 +242,8 @@ static int as3722_rtc_resume(struct device *dev)
}
#endif
-static const struct dev_pm_ops as3722_rtc_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(as3722_rtc_suspend, as3722_rtc_resume)
-};
+static SIMPLE_DEV_PM_OPS(as3722_rtc_pm_ops, as3722_rtc_suspend,
+ as3722_rtc_resume);
static struct platform_driver as3722_rtc_driver = {
.probe = as3722_rtc_probe,
diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c
index 3161ab5263ed..aee3387fb099 100644
--- a/drivers/rtc/rtc-at32ap700x.c
+++ b/drivers/rtc/rtc-at32ap700x.c
@@ -204,10 +204,8 @@ static int __init at32_rtc_probe(struct platform_device *pdev)
rtc = devm_kzalloc(&pdev->dev, sizeof(struct rtc_at32ap700x),
GFP_KERNEL);
- if (!rtc) {
- dev_dbg(&pdev->dev, "out of memory\n");
+ if (!rtc)
return -ENOMEM;
- }
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) {
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c
index 309b8b342d9c..596374304532 100644
--- a/drivers/rtc/rtc-at91sam9.c
+++ b/drivers/rtc/rtc-at91sam9.c
@@ -24,7 +24,7 @@
#include <mach/at91_rtt.h>
#include <mach/cpu.h>
-
+#include <mach/hardware.h>
/*
* This driver uses two configurable hardware resources that live in the
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index cae212f30d65..0963c9309c74 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -837,7 +837,7 @@ static void __exit cmos_do_remove(struct device *dev)
cmos->dev = NULL;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int cmos_suspend(struct device *dev)
{
@@ -935,8 +935,6 @@ static int cmos_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume);
-
#else
static inline int cmos_poweroff(struct device *dev)
@@ -946,6 +944,8 @@ static inline int cmos_poweroff(struct device *dev)
#endif
+static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume);
+
/*----------------------------------------------------------------*/
/* On non-x86 systems, a "CMOS" RTC lives most naturally on platform_bus.
@@ -1088,11 +1088,9 @@ static struct pnp_driver cmos_pnp_driver = {
/* flag ensures resume() gets called, and stops syslog spam */
.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
-#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &cmos_pm_ops,
},
-#endif
};
#endif /* CONFIG_PNP */
diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c
index 73f157519dff..869cae273799 100644
--- a/drivers/rtc/rtc-coh901331.c
+++ b/drivers/rtc/rtc-coh901331.c
@@ -43,8 +43,6 @@
struct coh901331_port {
struct rtc_device *rtc;
struct clk *clk;
- u32 phybase;
- u32 physize;
void __iomem *virtbase;
int irq;
#ifdef CONFIG_PM_SLEEP
@@ -173,19 +171,9 @@ static int __init coh901331_probe(struct platform_device *pdev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOENT;
-
- rtap->phybase = res->start;
- rtap->physize = resource_size(res);
-
- if (devm_request_mem_region(&pdev->dev, rtap->phybase, rtap->physize,
- "rtc-coh901331") == NULL)
- return -EBUSY;
-
- rtap->virtbase = devm_ioremap(&pdev->dev, rtap->phybase, rtap->physize);
- if (!rtap->virtbase)
- return -ENOMEM;
+ rtap->virtbase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rtap->virtbase))
+ return PTR_ERR(rtap->virtbase);
rtap->irq = platform_get_irq(pdev, 0);
if (devm_request_irq(&pdev->dev, rtap->irq, coh901331_interrupt, 0,
diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c
index 4385ca4503da..a1cbf64242a5 100644
--- a/drivers/rtc/rtc-da9052.c
+++ b/drivers/rtc/rtc-da9052.c
@@ -26,7 +26,6 @@
struct da9052_rtc {
struct rtc_device *rtc;
struct da9052 *da9052;
- int irq;
};
static int da9052_rtc_enable_alarm(struct da9052 *da9052, bool enable)
@@ -240,8 +239,7 @@ static int da9052_rtc_probe(struct platform_device *pdev)
rtc->da9052 = dev_get_drvdata(pdev->dev.parent);
platform_set_drvdata(pdev, rtc);
- rtc->irq = DA9052_IRQ_ALARM;
- ret = da9052_request_irq(rtc->da9052, rtc->irq, "ALM",
+ ret = da9052_request_irq(rtc->da9052, DA9052_IRQ_ALARM, "ALM",
da9052_rtc_irq, rtc);
if (ret != 0) {
rtc_err(rtc->da9052, "irq registration failed: %d\n", ret);
diff --git a/drivers/rtc/rtc-da9055.c b/drivers/rtc/rtc-da9055.c
index 48cb2ac3bd3e..a825491331c8 100644
--- a/drivers/rtc/rtc-da9055.c
+++ b/drivers/rtc/rtc-da9055.c
@@ -302,7 +302,9 @@ static int da9055_rtc_probe(struct platform_device *pdev)
}
alm_irq = platform_get_irq_byname(pdev, "ALM");
- alm_irq = regmap_irq_get_virq(rtc->da9055->irq_data, alm_irq);
+ if (alm_irq < 0)
+ return alm_irq;
+
ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL,
da9055_rtc_alm_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c
index 24677ef8c39a..c0a3b59f65a2 100644
--- a/drivers/rtc/rtc-davinci.c
+++ b/drivers/rtc/rtc-davinci.c
@@ -119,8 +119,6 @@ static DEFINE_SPINLOCK(davinci_rtc_lock);
struct davinci_rtc {
struct rtc_device *rtc;
void __iomem *base;
- resource_size_t pbase;
- size_t base_size;
int irq;
};
@@ -482,14 +480,12 @@ static int __init davinci_rtc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct davinci_rtc *davinci_rtc;
- struct resource *res, *mem;
+ struct resource *res;
int ret = 0;
davinci_rtc = devm_kzalloc(&pdev->dev, sizeof(struct davinci_rtc), GFP_KERNEL);
- if (!davinci_rtc) {
- dev_dbg(dev, "could not allocate memory for private data\n");
+ if (!davinci_rtc)
return -ENOMEM;
- }
davinci_rtc->irq = platform_get_irq(pdev, 0);
if (davinci_rtc->irq < 0) {
@@ -498,28 +494,9 @@ static int __init davinci_rtc_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "no mem resource\n");
- return -EINVAL;
- }
-
- davinci_rtc->pbase = res->start;
- davinci_rtc->base_size = resource_size(res);
-
- mem = devm_request_mem_region(dev, davinci_rtc->pbase,
- davinci_rtc->base_size, pdev->name);
- if (!mem) {
- dev_err(dev, "RTC registers at %08x are not free\n",
- davinci_rtc->pbase);
- return -EBUSY;
- }
-
- davinci_rtc->base = devm_ioremap(dev, davinci_rtc->pbase,
- davinci_rtc->base_size);
- if (!davinci_rtc->base) {
- dev_err(dev, "unable to ioremap MEM resource\n");
- return -ENOMEM;
- }
+ davinci_rtc->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(davinci_rtc->base))
+ return PTR_ERR(davinci_rtc->base);
platform_set_drvdata(pdev, davinci_rtc);
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c
index 2dd586a19b59..129add77065d 100644
--- a/drivers/rtc/rtc-ds1305.c
+++ b/drivers/rtc/rtc-ds1305.c
@@ -756,19 +756,17 @@ static int ds1305_probe(struct spi_device *spi)
status = devm_request_irq(&spi->dev, spi->irq, ds1305_irq,
0, dev_name(&ds1305->rtc->dev), ds1305);
if (status < 0) {
- dev_dbg(&spi->dev, "request_irq %d --> %d\n",
+ dev_err(&spi->dev, "request_irq %d --> %d\n",
spi->irq, status);
- return status;
+ } else {
+ device_set_wakeup_capable(&spi->dev, 1);
}
-
- device_set_wakeup_capable(&spi->dev, 1);
}
/* export NVRAM */
status = sysfs_create_bin_file(&spi->dev.kobj, &nvram);
if (status < 0) {
- dev_dbg(&spi->dev, "register nvram --> %d\n", status);
- return status;
+ dev_err(&spi->dev, "register nvram --> %d\n", status);
}
return 0;
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 4e75345a559a..f03d5ba96db1 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -154,6 +154,7 @@ static const struct chip_desc chips[last_ds_type] = {
.alarm = 1,
},
[mcp7941x] = {
+ .alarm = 1,
/* this is battery backed SRAM */
.nvram_offset = 0x20,
.nvram_size = 0x40,
@@ -606,6 +607,178 @@ static const struct rtc_class_ops ds13xx_rtc_ops = {
/*----------------------------------------------------------------------*/
+/*
+ * Alarm support for mcp7941x devices.
+ */
+
+#define MCP7941X_REG_CONTROL 0x07
+# define MCP7941X_BIT_ALM0_EN 0x10
+# define MCP7941X_BIT_ALM1_EN 0x20
+#define MCP7941X_REG_ALARM0_BASE 0x0a
+#define MCP7941X_REG_ALARM0_CTRL 0x0d
+#define MCP7941X_REG_ALARM1_BASE 0x11
+#define MCP7941X_REG_ALARM1_CTRL 0x14
+# define MCP7941X_BIT_ALMX_IF (1 << 3)
+# define MCP7941X_BIT_ALMX_C0 (1 << 4)
+# define MCP7941X_BIT_ALMX_C1 (1 << 5)
+# define MCP7941X_BIT_ALMX_C2 (1 << 6)
+# define MCP7941X_BIT_ALMX_POL (1 << 7)
+# define MCP7941X_MSK_ALMX_MATCH (MCP7941X_BIT_ALMX_C0 | \
+ MCP7941X_BIT_ALMX_C1 | \
+ MCP7941X_BIT_ALMX_C2)
+
+static void mcp7941x_work(struct work_struct *work)
+{
+ struct ds1307 *ds1307 = container_of(work, struct ds1307, work);
+ struct i2c_client *client = ds1307->client;
+ int reg, ret;
+
+ mutex_lock(&ds1307->rtc->ops_lock);
+
+ /* Check and clear alarm 0 interrupt flag. */
+ reg = i2c_smbus_read_byte_data(client, MCP7941X_REG_ALARM0_CTRL);
+ if (reg < 0)
+ goto out;
+ if (!(reg & MCP7941X_BIT_ALMX_IF))
+ goto out;
+ reg &= ~MCP7941X_BIT_ALMX_IF;
+ ret = i2c_smbus_write_byte_data(client, MCP7941X_REG_ALARM0_CTRL, reg);
+ if (ret < 0)
+ goto out;
+
+ /* Disable alarm 0. */
+ reg = i2c_smbus_read_byte_data(client, MCP7941X_REG_CONTROL);
+ if (reg < 0)
+ goto out;
+ reg &= ~MCP7941X_BIT_ALM0_EN;
+ ret = i2c_smbus_write_byte_data(client, MCP7941X_REG_CONTROL, reg);
+ if (ret < 0)
+ goto out;
+
+ rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
+
+out:
+ if (test_bit(HAS_ALARM, &ds1307->flags))
+ enable_irq(client->irq);
+ mutex_unlock(&ds1307->rtc->ops_lock);
+}
+
+static int mcp7941x_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ u8 *regs = ds1307->regs;
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ /* Read control and alarm 0 registers. */
+ ret = ds1307->read_block_data(client, MCP7941X_REG_CONTROL, 10, regs);
+ if (ret < 0)
+ return ret;
+
+ t->enabled = !!(regs[0] & MCP7941X_BIT_ALM0_EN);
+
+ /* Report alarm 0 time assuming 24-hour and day-of-month modes. */
+ t->time.tm_sec = bcd2bin(ds1307->regs[3] & 0x7f);
+ t->time.tm_min = bcd2bin(ds1307->regs[4] & 0x7f);
+ t->time.tm_hour = bcd2bin(ds1307->regs[5] & 0x3f);
+ t->time.tm_wday = bcd2bin(ds1307->regs[6] & 0x7) - 1;
+ t->time.tm_mday = bcd2bin(ds1307->regs[7] & 0x3f);
+ t->time.tm_mon = bcd2bin(ds1307->regs[8] & 0x1f) - 1;
+ t->time.tm_year = -1;
+ t->time.tm_yday = -1;
+ t->time.tm_isdst = -1;
+
+ dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
+ "enabled=%d polarity=%d irq=%d match=%d\n", __func__,
+ t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
+ t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled,
+ !!(ds1307->regs[6] & MCP7941X_BIT_ALMX_POL),
+ !!(ds1307->regs[6] & MCP7941X_BIT_ALMX_IF),
+ (ds1307->regs[6] & MCP7941X_MSK_ALMX_MATCH) >> 4);
+
+ return 0;
+}
+
+static int mcp7941x_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ unsigned char *regs = ds1307->regs;
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
+ "enabled=%d pending=%d\n", __func__,
+ t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
+ t->time.tm_wday, t->time.tm_mday, t->time.tm_mon,
+ t->enabled, t->pending);
+
+ /* Read control and alarm 0 registers. */
+ ret = ds1307->read_block_data(client, MCP7941X_REG_CONTROL, 10, regs);
+ if (ret < 0)
+ return ret;
+
+ /* Set alarm 0, using 24-hour and day-of-month modes. */
+ regs[3] = bin2bcd(t->time.tm_sec);
+ regs[4] = bin2bcd(t->time.tm_min);
+ regs[5] = bin2bcd(t->time.tm_hour);
+ regs[6] = bin2bcd(t->time.tm_wday) + 1;
+ regs[7] = bin2bcd(t->time.tm_mday);
+ regs[8] = bin2bcd(t->time.tm_mon) + 1;
+
+ /* Clear the alarm 0 interrupt flag. */
+ regs[6] &= ~MCP7941X_BIT_ALMX_IF;
+ /* Set alarm match: second, minute, hour, day, date, month. */
+ regs[6] |= MCP7941X_MSK_ALMX_MATCH;
+
+ if (t->enabled)
+ regs[0] |= MCP7941X_BIT_ALM0_EN;
+ else
+ regs[0] &= ~MCP7941X_BIT_ALM0_EN;
+
+ ret = ds1307->write_block_data(client, MCP7941X_REG_CONTROL, 10, regs);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int mcp7941x_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ int reg;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ reg = i2c_smbus_read_byte_data(client, MCP7941X_REG_CONTROL);
+ if (reg < 0)
+ return reg;
+
+ if (enabled)
+ reg |= MCP7941X_BIT_ALM0_EN;
+ else
+ reg &= ~MCP7941X_BIT_ALM0_EN;
+
+ return i2c_smbus_write_byte_data(client, MCP7941X_REG_CONTROL, reg);
+}
+
+static const struct rtc_class_ops mcp7941x_rtc_ops = {
+ .read_time = ds1307_get_time,
+ .set_time = ds1307_set_time,
+ .read_alarm = mcp7941x_read_alarm,
+ .set_alarm = mcp7941x_set_alarm,
+ .alarm_irq_enable = mcp7941x_alarm_irq_enable,
+};
+
+/*----------------------------------------------------------------------*/
+
static ssize_t
ds1307_nvram_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
@@ -678,6 +851,7 @@ static int ds1307_probe(struct i2c_client *client,
[ds_1339] = DS1339_BIT_BBSQI,
[ds_3231] = DS3231_BIT_BBSQW,
};
+ const struct rtc_class_ops *rtc_ops = &ds13xx_rtc_ops;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)
&& !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
@@ -816,6 +990,13 @@ static int ds1307_probe(struct i2c_client *client,
case ds_1388:
ds1307->offset = 1; /* Seconds starts at 1 */
break;
+ case mcp7941x:
+ rtc_ops = &mcp7941x_rtc_ops;
+ if (ds1307->client->irq > 0 && chip->alarm) {
+ INIT_WORK(&ds1307->work, mcp7941x_work);
+ want_irq = true;
+ }
+ break;
default:
break;
}
@@ -927,55 +1108,61 @@ read_rtc:
bin2bcd(tmp));
}
+ device_set_wakeup_capable(&client->dev, want_irq);
ds1307->rtc = devm_rtc_device_register(&client->dev, client->name,
- &ds13xx_rtc_ops, THIS_MODULE);
+ rtc_ops, THIS_MODULE);
if (IS_ERR(ds1307->rtc)) {
- err = PTR_ERR(ds1307->rtc);
- dev_err(&client->dev,
- "unable to register the class device\n");
- goto exit;
+ return PTR_ERR(ds1307->rtc);
}
if (want_irq) {
err = request_irq(client->irq, ds1307_irq, IRQF_SHARED,
ds1307->rtc->name, client);
if (err) {
- dev_err(&client->dev,
- "unable to request IRQ!\n");
- goto exit;
- }
+ client->irq = 0;
+ dev_err(&client->dev, "unable to request IRQ!\n");
+ } else {
- device_set_wakeup_capable(&client->dev, 1);
- set_bit(HAS_ALARM, &ds1307->flags);
- dev_dbg(&client->dev, "got IRQ %d\n", client->irq);
+ set_bit(HAS_ALARM, &ds1307->flags);
+ dev_dbg(&client->dev, "got IRQ %d\n", client->irq);
+ }
}
if (chip->nvram_size) {
+
ds1307->nvram = devm_kzalloc(&client->dev,
sizeof(struct bin_attribute),
GFP_KERNEL);
if (!ds1307->nvram) {
- err = -ENOMEM;
- goto err_irq;
+ dev_err(&client->dev, "cannot allocate memory for nvram sysfs\n");
+ } else {
+
+ ds1307->nvram->attr.name = "nvram";
+ ds1307->nvram->attr.mode = S_IRUGO | S_IWUSR;
+
+ sysfs_bin_attr_init(ds1307->nvram);
+
+ ds1307->nvram->read = ds1307_nvram_read;
+ ds1307->nvram->write = ds1307_nvram_write;
+ ds1307->nvram->size = chip->nvram_size;
+ ds1307->nvram_offset = chip->nvram_offset;
+
+ err = sysfs_create_bin_file(&client->dev.kobj,
+ ds1307->nvram);
+ if (err) {
+ dev_err(&client->dev,
+ "unable to create sysfs file: %s\n",
+ ds1307->nvram->attr.name);
+ } else {
+ set_bit(HAS_NVRAM, &ds1307->flags);
+ dev_info(&client->dev, "%zu bytes nvram\n",
+ ds1307->nvram->size);
+ }
}
- ds1307->nvram->attr.name = "nvram";
- ds1307->nvram->attr.mode = S_IRUGO | S_IWUSR;
- sysfs_bin_attr_init(ds1307->nvram);
- ds1307->nvram->read = ds1307_nvram_read;
- ds1307->nvram->write = ds1307_nvram_write;
- ds1307->nvram->size = chip->nvram_size;
- ds1307->nvram_offset = chip->nvram_offset;
- err = sysfs_create_bin_file(&client->dev.kobj, ds1307->nvram);
- if (err)
- goto err_irq;
- set_bit(HAS_NVRAM, &ds1307->flags);
- dev_info(&client->dev, "%zu bytes nvram\n", ds1307->nvram->size);
}
return 0;
-err_irq:
- free_irq(client->irq, client);
exit:
return err;
}
diff --git a/drivers/rtc/rtc-ds1347.c b/drivers/rtc/rtc-ds1347.c
new file mode 100644
index 000000000000..c82b4c050326
--- /dev/null
+++ b/drivers/rtc/rtc-ds1347.c
@@ -0,0 +1,166 @@
+/* rtc-ds1347.c
+ *
+ * Driver for Dallas Semiconductor DS1347 Low Current, SPI Compatible
+ * Real Time Clock
+ *
+ * Author : Raghavendra Chandra Ganiga <ravi23ganiga@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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+#include <linux/bcd.h>
+
+/* Registers in ds1347 rtc */
+
+#define DS1347_SECONDS_REG 0x01
+#define DS1347_MINUTES_REG 0x03
+#define DS1347_HOURS_REG 0x05
+#define DS1347_DATE_REG 0x07
+#define DS1347_MONTH_REG 0x09
+#define DS1347_DAY_REG 0x0B
+#define DS1347_YEAR_REG 0x0D
+#define DS1347_CONTROL_REG 0x0F
+#define DS1347_STATUS_REG 0x17
+#define DS1347_CLOCK_BURST 0x3F
+
+static int ds1347_read_reg(struct device *dev, unsigned char address,
+ unsigned char *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ *data = address | 0x80;
+
+ return spi_write_then_read(spi, data, 1, data, 1);
+}
+
+static int ds1347_write_reg(struct device *dev, unsigned char address,
+ unsigned char data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[2];
+
+ buf[0] = address & 0x7F;
+ buf[1] = data;
+
+ return spi_write_then_read(spi, buf, 2, NULL, 0);
+}
+
+static int ds1347_read_time(struct device *dev, struct rtc_time *dt)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int err;
+ unsigned char buf[8];
+
+ buf[0] = DS1347_CLOCK_BURST | 0x80;
+
+ err = spi_write_then_read(spi, buf, 1, buf, 8);
+ if (err)
+ return err;
+
+ dt->tm_sec = bcd2bin(buf[0]);
+ dt->tm_min = bcd2bin(buf[1]);
+ dt->tm_hour = bcd2bin(buf[2] & 0x3F);
+ dt->tm_mday = bcd2bin(buf[3]);
+ dt->tm_mon = bcd2bin(buf[4]) - 1;
+ dt->tm_wday = bcd2bin(buf[5]) - 1;
+ dt->tm_year = bcd2bin(buf[6]) + 100;
+
+ return rtc_valid_tm(dt);
+}
+
+static int ds1347_set_time(struct device *dev, struct rtc_time *dt)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[9];
+
+ buf[0] = DS1347_CLOCK_BURST & 0x7F;
+ buf[1] = bin2bcd(dt->tm_sec);
+ buf[2] = bin2bcd(dt->tm_min);
+ buf[3] = (bin2bcd(dt->tm_hour) & 0x3F);
+ buf[4] = bin2bcd(dt->tm_mday);
+ buf[5] = bin2bcd(dt->tm_mon + 1);
+ buf[6] = bin2bcd(dt->tm_wday + 1);
+
+ /* year in linux is from 1900 i.e in range of 100
+ in rtc it is from 00 to 99 */
+ dt->tm_year = dt->tm_year % 100;
+
+ buf[7] = bin2bcd(dt->tm_year);
+ buf[8] = bin2bcd(0x00);
+
+ /* write the rtc settings */
+ return spi_write_then_read(spi, buf, 9, NULL, 0);
+}
+
+static const struct rtc_class_ops ds1347_rtc_ops = {
+ .read_time = ds1347_read_time,
+ .set_time = ds1347_set_time,
+};
+
+static int ds1347_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ unsigned char data;
+ int res;
+
+ /* spi setup with ds1347 in mode 3 and bits per word as 8 */
+ spi->mode = SPI_MODE_3;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ /* RTC Settings */
+ res = ds1347_read_reg(&spi->dev, DS1347_SECONDS_REG, &data);
+ if (res)
+ return res;
+
+ /* Disable the write protect of rtc */
+ ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data);
+ data = data & ~(1<<7);
+ ds1347_write_reg(&spi->dev, DS1347_CONTROL_REG, data);
+
+ /* Enable the oscillator , disable the oscillator stop flag,
+ and glitch filter to reduce current consumption */
+ ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data);
+ data = data & 0x1B;
+ ds1347_write_reg(&spi->dev, DS1347_STATUS_REG, data);
+
+ /* display the settings */
+ ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data);
+ dev_info(&spi->dev, "DS1347 RTC CTRL Reg = 0x%02x\n", data);
+
+ ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data);
+ dev_info(&spi->dev, "DS1347 RTC Status Reg = 0x%02x\n", data);
+
+ rtc = devm_rtc_device_register(&spi->dev, "ds1347",
+ &ds1347_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ spi_set_drvdata(spi, rtc);
+
+ return 0;
+}
+
+static struct spi_driver ds1347_driver = {
+ .driver = {
+ .name = "ds1347",
+ .owner = THIS_MODULE,
+ },
+ .probe = ds1347_probe,
+};
+
+module_spi_driver(ds1347_driver);
+
+MODULE_DESCRIPTION("DS1347 SPI RTC DRIVER");
+MODULE_AUTHOR("Raghavendra C Ganiga <ravi23ganiga@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c
index be9d8c0a7e3a..e67bfcb3a1aa 100644
--- a/drivers/rtc/rtc-ds1390.c
+++ b/drivers/rtc/rtc-ds1390.c
@@ -132,10 +132,9 @@ static int ds1390_probe(struct spi_device *spi)
spi_setup(spi);
chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL);
- if (!chip) {
- dev_err(&spi->dev, "unable to allocate device memory\n");
+ if (!chip)
return -ENOMEM;
- }
+
spi_set_drvdata(spi, chip);
res = ds1390_get_reg(&spi->dev, DS1390_REG_SECONDS, &tmp);
diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c
index bc7b4fcf603c..b13d1399b81a 100644
--- a/drivers/rtc/rtc-ds1511.c
+++ b/drivers/rtc/rtc-ds1511.c
@@ -371,8 +371,7 @@ ds1511_interrupt(int irq, void *dev_id)
events |= RTC_UF;
else
events |= RTC_AF;
- if (likely(pdata->rtc))
- rtc_update_irq(pdata->rtc, 1, events);
+ rtc_update_irq(pdata->rtc, 1, events);
}
spin_unlock(&pdata->lock);
return events ? IRQ_HANDLED : IRQ_NONE;
@@ -473,7 +472,6 @@ static struct bin_attribute ds1511_nvram_attr = {
static int ds1511_rtc_probe(struct platform_device *pdev)
{
- struct rtc_device *rtc;
struct resource *res;
struct rtc_plat_data *pdata;
int ret = 0;
@@ -512,6 +510,12 @@ static int ds1511_rtc_probe(struct platform_device *pdev)
spin_lock_init(&pdata->lock);
platform_set_drvdata(pdev, pdata);
+
+ pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &ds1511_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pdata->rtc))
+ return PTR_ERR(pdata->rtc);
+
/*
* if the platform has an interrupt in mind for this device,
* then by all means, set it
@@ -526,15 +530,12 @@ static int ds1511_rtc_probe(struct platform_device *pdev)
}
}
- rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &ds1511_rtc_ops,
- THIS_MODULE);
- if (IS_ERR(rtc))
- return PTR_ERR(rtc);
- pdata->rtc = rtc;
-
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr);
+ if (ret)
+ dev_err(&pdev->dev, "Unable to create sysfs entry: %s\n",
+ ds1511_nvram_attr.attr.name);
- return ret;
+ return 0;
}
static int ds1511_rtc_remove(struct platform_device *pdev)
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index fd31571941f5..ab56893aac73 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -206,8 +206,7 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id)
events |= RTC_UF;
else
events |= RTC_AF;
- if (likely(pdata->rtc))
- rtc_update_irq(pdata->rtc, 1, events);
+ rtc_update_irq(pdata->rtc, 1, events);
}
spin_unlock(&pdata->lock);
return events ? IRQ_HANDLED : IRQ_NONE;
@@ -278,7 +277,6 @@ static struct bin_attribute ds1553_nvram_attr = {
static int ds1553_rtc_probe(struct platform_device *pdev)
{
- struct rtc_device *rtc;
struct resource *res;
unsigned int cen, sec;
struct rtc_plat_data *pdata;
@@ -311,6 +309,12 @@ static int ds1553_rtc_probe(struct platform_device *pdev)
spin_lock_init(&pdata->lock);
pdata->last_jiffies = jiffies;
platform_set_drvdata(pdev, pdata);
+
+ pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &ds1553_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pdata->rtc))
+ return PTR_ERR(pdata->rtc);
+
if (pdata->irq > 0) {
writeb(0, ioaddr + RTC_INTERRUPTS);
if (devm_request_irq(&pdev->dev, pdata->irq,
@@ -321,15 +325,12 @@ static int ds1553_rtc_probe(struct platform_device *pdev)
}
}
- rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &ds1553_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc))
- return PTR_ERR(rtc);
- pdata->rtc = rtc;
-
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
+ if (ret)
+ dev_err(&pdev->dev, "unable to create sysfs file: %s\n",
+ ds1553_nvram_attr.attr.name);
- return ret;
+ return 0;
}
static int ds1553_rtc_remove(struct platform_device *pdev)
diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c
index 18e2d8471472..a4888dbca2e1 100644
--- a/drivers/rtc/rtc-ds1672.c
+++ b/drivers/rtc/rtc-ds1672.c
@@ -177,8 +177,9 @@ static int ds1672_probe(struct i2c_client *client,
/* read control register */
err = ds1672_get_control(client, &control);
- if (err)
- goto exit_devreg;
+ if (err) {
+ dev_warn(&client->dev, "Unable to read the control register\n");
+ }
if (control & DS1672_REG_CONTROL_EOSC)
dev_warn(&client->dev, "Oscillator not enabled. "
@@ -187,12 +188,10 @@ static int ds1672_probe(struct i2c_client *client,
/* Register sysfs hooks */
err = device_create_file(&client->dev, &dev_attr_control);
if (err)
- goto exit_devreg;
+ dev_err(&client->dev, "Unable to create sysfs entry: %s\n",
+ dev_attr_control.attr.name);
return 0;
-
- exit_devreg:
- return err;
}
static struct i2c_device_id ds1672_id[] = {
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
index 5a1f3b2a8f1e..942103dac30f 100644
--- a/drivers/rtc/rtc-ds1742.c
+++ b/drivers/rtc/rtc-ds1742.c
@@ -204,8 +204,11 @@ static int ds1742_rtc_probe(struct platform_device *pdev)
return PTR_ERR(rtc);
ret = sysfs_create_bin_file(&pdev->dev.kobj, &pdata->nvram_attr);
+ if (ret)
+ dev_err(&pdev->dev, "Unable to create sysfs entry: %s\n",
+ pdata->nvram_attr.attr.name);
- return ret;
+ return 0;
}
static int ds1742_rtc_remove(struct platform_device *pdev)
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
index b83bb5a527f8..adaf06c41479 100644
--- a/drivers/rtc/rtc-ds3232.c
+++ b/drivers/rtc/rtc-ds3232.c
@@ -57,6 +57,7 @@ struct ds3232 {
* in the remove function.
*/
struct mutex mutex;
+ bool suspended;
int exiting;
};
@@ -345,7 +346,15 @@ static irqreturn_t ds3232_irq(int irq, void *dev_id)
struct ds3232 *ds3232 = i2c_get_clientdata(client);
disable_irq_nosync(irq);
- schedule_work(&ds3232->work);
+
+ /*
+ * If rtc as a wakeup source, can't schedule the work
+ * at system resume flow, because at this time the i2c bus
+ * has not been resumed.
+ */
+ if (!ds3232->suspended)
+ schedule_work(&ds3232->work);
+
return IRQ_HANDLED;
}
@@ -363,22 +372,26 @@ static void ds3232_work(struct work_struct *work)
if (stat & DS3232_REG_SR_A1F) {
control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
- if (control < 0)
- goto out;
- /* disable alarm1 interrupt */
- control &= ~(DS3232_REG_CR_A1IE);
- i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
-
- /* clear the alarm pend flag */
- stat &= ~DS3232_REG_SR_A1F;
- i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
-
- rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
+ if (control < 0) {
+ pr_warn("Read DS3232 Control Register error."
+ "Disable IRQ%d.\n", client->irq);
+ } else {
+ /* disable alarm1 interrupt */
+ control &= ~(DS3232_REG_CR_A1IE);
+ i2c_smbus_write_byte_data(client, DS3232_REG_CR,
+ control);
+
+ /* clear the alarm pend flag */
+ stat &= ~DS3232_REG_SR_A1F;
+ i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+
+ rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
+
+ if (!ds3232->exiting)
+ enable_irq(client->irq);
+ }
}
-out:
- if (!ds3232->exiting)
- enable_irq(client->irq);
unlock:
mutex_unlock(&ds3232->mutex);
}
@@ -411,23 +424,17 @@ static int ds3232_probe(struct i2c_client *client,
if (ret)
return ret;
- ds3232->rtc = devm_rtc_device_register(&client->dev, client->name,
- &ds3232_rtc_ops, THIS_MODULE);
- if (IS_ERR(ds3232->rtc)) {
- dev_err(&client->dev, "unable to register the class device\n");
- return PTR_ERR(ds3232->rtc);
- }
-
- if (client->irq >= 0) {
- ret = devm_request_irq(&client->dev, client->irq, ds3232_irq, 0,
- "ds3232", client);
+ if (client->irq > 0) {
+ ret = devm_request_irq(&client->dev, client->irq, ds3232_irq,
+ IRQF_SHARED, "ds3232", client);
if (ret) {
dev_err(&client->dev, "unable to request IRQ\n");
- return ret;
}
+ device_init_wakeup(&client->dev, 1);
}
-
- return 0;
+ ds3232->rtc = devm_rtc_device_register(&client->dev, client->name,
+ &ds3232_rtc_ops, THIS_MODULE);
+ return PTR_ERR_OR_ZERO(ds3232->rtc);
}
static int ds3232_remove(struct i2c_client *client)
@@ -446,6 +453,42 @@ static int ds3232_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int ds3232_suspend(struct device *dev)
+{
+ struct ds3232 *ds3232 = dev_get_drvdata(dev);
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_can_wakeup(dev)) {
+ ds3232->suspended = true;
+ irq_set_irq_wake(client->irq, 1);
+ }
+
+ return 0;
+}
+
+static int ds3232_resume(struct device *dev)
+{
+ struct ds3232 *ds3232 = dev_get_drvdata(dev);
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (ds3232->suspended) {
+ ds3232->suspended = false;
+
+ /* Clear the hardware alarm pend flag */
+ schedule_work(&ds3232->work);
+
+ irq_set_irq_wake(client->irq, 0);
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops ds3232_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ds3232_suspend, ds3232_resume)
+};
+
static const struct i2c_device_id ds3232_id[] = {
{ "ds3232", 0 },
{ }
@@ -456,6 +499,7 @@ static struct i2c_driver ds3232_driver = {
.driver = {
.name = "rtc-ds3232",
.owner = THIS_MODULE,
+ .pm = &ds3232_pm_ops,
},
.probe = ds3232_probe,
.remove = ds3232_remove,
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
index abd7f9091f34..cd741c77e085 100644
--- a/drivers/rtc/rtc-imxdi.c
+++ b/drivers/rtc/rtc-imxdi.c
@@ -401,7 +401,9 @@ static int __init dryice_rtc_probe(struct platform_device *pdev)
imxdi->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(imxdi->clk))
return PTR_ERR(imxdi->clk);
- clk_prepare_enable(imxdi->clk);
+ rc = clk_prepare_enable(imxdi->clk);
+ if (rc)
+ return rc;
/*
* Initialize dryice hardware
diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c
index 7854a656628f..41bd76aaff76 100644
--- a/drivers/rtc/rtc-isl12057.c
+++ b/drivers/rtc/rtc-isl12057.c
@@ -26,7 +26,6 @@
#include <linux/rtc.h>
#include <linux/i2c.h>
#include <linux/bcd.h>
-#include <linux/rtc.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
@@ -275,10 +274,7 @@ static int isl12057_probe(struct i2c_client *client,
dev_set_drvdata(dev, data);
rtc = devm_rtc_device_register(dev, DRV_NAME, &rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc))
- return PTR_ERR(rtc);
-
- return 0;
+ return PTR_ERR_OR_ZERO(rtc);
}
#ifdef CONFIG_OF
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index 1b126d2513de..08f5160fb6d4 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -38,7 +38,6 @@
#define JZ_RTC_CTRL_ENABLE BIT(0)
struct jz4740_rtc {
- struct resource *mem;
void __iomem *base;
struct rtc_device *rtc;
@@ -216,6 +215,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
int ret;
struct jz4740_rtc *rtc;
uint32_t scratchpad;
+ struct resource *mem;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
@@ -227,25 +227,10 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
return -ENOENT;
}
- rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!rtc->mem) {
- dev_err(&pdev->dev, "Failed to get platform mmio memory\n");
- return -ENOENT;
- }
-
- rtc->mem = devm_request_mem_region(&pdev->dev, rtc->mem->start,
- resource_size(rtc->mem), pdev->name);
- if (!rtc->mem) {
- dev_err(&pdev->dev, "Failed to request mmio memory region\n");
- return -EBUSY;
- }
-
- rtc->base = devm_ioremap_nocache(&pdev->dev, rtc->mem->start,
- resource_size(rtc->mem));
- if (!rtc->base) {
- dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
- return -EBUSY;
- }
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rtc->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(rtc->base))
+ return PTR_ERR(rtc->base);
spin_lock_init(&rtc->lock);
diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c
index bfdbcb82d069..f130c08c98f8 100644
--- a/drivers/rtc/rtc-lpc32xx.c
+++ b/drivers/rtc/rtc-lpc32xx.c
@@ -211,10 +211,9 @@ static int lpc32xx_rtc_probe(struct platform_device *pdev)
}
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
- if (unlikely(!rtc)) {
- dev_err(&pdev->dev, "Can't allocate memory\n");
+ if (unlikely(!rtc))
return -ENOMEM;
- }
+
rtc->irq = rtcirq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c
index 77ea9896b5ba..0765606a2d14 100644
--- a/drivers/rtc/rtc-mc13xxx.c
+++ b/drivers/rtc/rtc-mc13xxx.c
@@ -23,6 +23,8 @@
#define MC13XXX_RTCDAY 22
#define MC13XXX_RTCDAYA 23
+#define SEC_PER_DAY (24 * 60 * 60)
+
struct mc13xxx_rtc {
struct rtc_device *rtc;
struct mc13xxx *mc13xxx;
@@ -42,15 +44,15 @@ static int mc13xxx_rtc_irq_enable_unlocked(struct device *dev,
return func(priv->mc13xxx, irq);
}
-static int mc13xxx_rtc_irq_enable(struct device *dev,
- unsigned int enabled, int irq)
+static int mc13xxx_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
{
struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
int ret;
mc13xxx_lock(priv->mc13xxx);
- ret = mc13xxx_rtc_irq_enable_unlocked(dev, enabled, irq);
+ ret = mc13xxx_rtc_irq_enable_unlocked(dev, enabled, MC13XXX_IRQ_TODA);
mc13xxx_unlock(priv->mc13xxx);
@@ -61,44 +63,27 @@ static int mc13xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
unsigned int seconds, days1, days2;
- unsigned long s1970;
- int ret;
-
- mc13xxx_lock(priv->mc13xxx);
-
- if (!priv->valid) {
- ret = -ENODATA;
- goto out;
- }
- ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days1);
- if (unlikely(ret))
- goto out;
-
- ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTOD, &seconds);
- if (unlikely(ret))
- goto out;
-
- ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days2);
-out:
- mc13xxx_unlock(priv->mc13xxx);
+ if (!priv->valid)
+ return -ENODATA;
- if (ret)
- return ret;
+ do {
+ int ret;
- if (days2 == days1 + 1) {
- if (seconds >= 86400 / 2)
- days2 = days1;
- else
- days1 = days2;
- }
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days1);
+ if (ret)
+ return ret;
- if (days1 != days2)
- return -EIO;
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTOD, &seconds);
+ if (ret)
+ return ret;
- s1970 = days1 * 86400 + seconds;
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days2);
+ if (ret)
+ return ret;
+ } while (days1 != days2);
- rtc_time_to_tm(s1970, tm);
+ rtc_time_to_tm(days1 * SEC_PER_DAY + seconds, tm);
return rtc_valid_tm(tm);
}
@@ -110,8 +95,8 @@ static int mc13xxx_rtc_set_mmss(struct device *dev, unsigned long secs)
unsigned int alarmseconds;
int ret;
- seconds = secs % 86400;
- days = secs / 86400;
+ seconds = secs % SEC_PER_DAY;
+ days = secs / SEC_PER_DAY;
mc13xxx_lock(priv->mc13xxx);
@@ -123,7 +108,7 @@ static int mc13xxx_rtc_set_mmss(struct device *dev, unsigned long secs)
if (unlikely(ret))
goto out;
- if (alarmseconds < 86400) {
+ if (alarmseconds < SEC_PER_DAY) {
ret = mc13xxx_reg_write(priv->mc13xxx,
MC13XXX_RTCTODA, 0x1ffff);
if (unlikely(ret))
@@ -147,18 +132,21 @@ static int mc13xxx_rtc_set_mmss(struct device *dev, unsigned long secs)
goto out;
/* restore alarm */
- if (alarmseconds < 86400) {
+ if (alarmseconds < SEC_PER_DAY) {
ret = mc13xxx_reg_write(priv->mc13xxx,
MC13XXX_RTCTODA, alarmseconds);
if (unlikely(ret))
goto out;
}
- ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
- if (unlikely(ret))
- goto out;
+ if (!priv->valid) {
+ ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_irq_unmask(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
+ }
- ret = mc13xxx_irq_unmask(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
out:
priv->valid = !ret;
@@ -180,7 +168,7 @@ static int mc13xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &seconds);
if (unlikely(ret))
goto out;
- if (seconds >= 86400) {
+ if (seconds >= SEC_PER_DAY) {
ret = -ENODATA;
goto out;
}
@@ -201,7 +189,7 @@ out:
alarm->enabled = enabled;
alarm->pending = pending;
- s1970 = days * 86400 + seconds;
+ s1970 = days * SEC_PER_DAY + seconds;
rtc_time_to_tm(s1970, &alarm->time);
dev_dbg(dev, "%s: %lu\n", __func__, s1970);
@@ -239,8 +227,8 @@ static int mc13xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
if (unlikely(ret))
goto out;
- seconds = s1970 % 86400;
- days = s1970 / 86400;
+ seconds = s1970 % SEC_PER_DAY;
+ days = s1970 / SEC_PER_DAY;
ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAYA, days);
if (unlikely(ret))
@@ -259,8 +247,6 @@ static irqreturn_t mc13xxx_rtc_alarm_handler(int irq, void *dev)
struct mc13xxx_rtc *priv = dev;
struct mc13xxx *mc13xxx = priv->mc13xxx;
- dev_dbg(&priv->rtc->dev, "Alarm\n");
-
rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF);
mc13xxx_irq_ack(mc13xxx, irq);
@@ -273,8 +259,6 @@ static irqreturn_t mc13xxx_rtc_update_handler(int irq, void *dev)
struct mc13xxx_rtc *priv = dev;
struct mc13xxx *mc13xxx = priv->mc13xxx;
- dev_dbg(&priv->rtc->dev, "1HZ\n");
-
rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
mc13xxx_irq_ack(mc13xxx, irq);
@@ -282,12 +266,6 @@ static irqreturn_t mc13xxx_rtc_update_handler(int irq, void *dev)
return IRQ_HANDLED;
}
-static int mc13xxx_rtc_alarm_irq_enable(struct device *dev,
- unsigned int enabled)
-{
- return mc13xxx_rtc_irq_enable(dev, enabled, MC13XXX_IRQ_TODA);
-}
-
static const struct rtc_class_ops mc13xxx_rtc_ops = {
.read_time = mc13xxx_rtc_read_time,
.set_mmss = mc13xxx_rtc_set_mmss,
@@ -301,7 +279,6 @@ static irqreturn_t mc13xxx_rtc_reset_handler(int irq, void *dev)
struct mc13xxx_rtc *priv = dev;
struct mc13xxx *mc13xxx = priv->mc13xxx;
- dev_dbg(&priv->rtc->dev, "RTCRST\n");
priv->valid = 0;
mc13xxx_irq_mask(mc13xxx, irq);
@@ -314,7 +291,6 @@ static int __init mc13xxx_rtc_probe(struct platform_device *pdev)
int ret;
struct mc13xxx_rtc *priv;
struct mc13xxx *mc13xxx;
- int rtcrst_pending;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -322,60 +298,47 @@ static int __init mc13xxx_rtc_probe(struct platform_device *pdev)
mc13xxx = dev_get_drvdata(pdev->dev.parent);
priv->mc13xxx = mc13xxx;
+ priv->valid = 1;
platform_set_drvdata(pdev, priv);
mc13xxx_lock(mc13xxx);
+ mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_RTCRST);
+
ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_RTCRST,
mc13xxx_rtc_reset_handler, DRIVER_NAME, priv);
if (ret)
- goto err_reset_irq_request;
-
- ret = mc13xxx_irq_status(mc13xxx, MC13XXX_IRQ_RTCRST,
- NULL, &rtcrst_pending);
- if (ret)
- goto err_reset_irq_status;
-
- priv->valid = !rtcrst_pending;
+ goto err_irq_request;
- ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_1HZ,
+ ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_1HZ,
mc13xxx_rtc_update_handler, DRIVER_NAME, priv);
if (ret)
- goto err_update_irq_request;
+ goto err_irq_request;
ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_TODA,
mc13xxx_rtc_alarm_handler, DRIVER_NAME, priv);
if (ret)
- goto err_alarm_irq_request;
+ goto err_irq_request;
mc13xxx_unlock(mc13xxx);
priv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &mc13xxx_rtc_ops, THIS_MODULE);
- if (IS_ERR(priv->rtc)) {
- ret = PTR_ERR(priv->rtc);
+ &mc13xxx_rtc_ops, THIS_MODULE);
- mc13xxx_lock(mc13xxx);
-
- mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_TODA, priv);
-err_alarm_irq_request:
-
- mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_1HZ, priv);
-err_update_irq_request:
-
-err_reset_irq_status:
+ return 0;
- mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_RTCRST, priv);
-err_reset_irq_request:
+err_irq_request:
+ mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_TODA, priv);
+ mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_1HZ, priv);
+ mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_RTCRST, priv);
- mc13xxx_unlock(mc13xxx);
- }
+ mc13xxx_unlock(mc13xxx);
return ret;
}
-static int __exit mc13xxx_rtc_remove(struct platform_device *pdev)
+static int mc13xxx_rtc_remove(struct platform_device *pdev)
{
struct mc13xxx_rtc *priv = platform_get_drvdata(pdev);
@@ -404,7 +367,7 @@ MODULE_DEVICE_TABLE(platform, mc13xxx_rtc_idtable);
static struct platform_driver mc13xxx_rtc_driver = {
.id_table = mc13xxx_rtc_idtable,
- .remove = __exit_p(mc13xxx_rtc_remove),
+ .remove = mc13xxx_rtc_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/rtc/rtc-moxart.c b/drivers/rtc/rtc-moxart.c
index c29dee0946e6..c31846238871 100644
--- a/drivers/rtc/rtc-moxart.c
+++ b/drivers/rtc/rtc-moxart.c
@@ -247,10 +247,8 @@ static int moxart_rtc_probe(struct platform_device *pdev)
int ret = 0;
moxart_rtc = devm_kzalloc(&pdev->dev, sizeof(*moxart_rtc), GFP_KERNEL);
- if (!moxart_rtc) {
- dev_err(&pdev->dev, "devm_kzalloc failed\n");
+ if (!moxart_rtc)
return -ENOMEM;
- }
moxart_rtc->gpio_data = of_get_named_gpio(pdev->dev.of_node,
"gpio-rtc-data", 0);
diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c
index d536c5962c99..d15a999363fc 100644
--- a/drivers/rtc/rtc-mv.c
+++ b/drivers/rtc/rtc-mv.c
@@ -222,6 +222,7 @@ static int __init mv_rtc_probe(struct platform_device *pdev)
struct resource *res;
struct rtc_plat_data *pdata;
u32 rtc_time;
+ u32 rtc_date;
int ret = 0;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
@@ -257,6 +258,17 @@ static int __init mv_rtc_probe(struct platform_device *pdev)
}
}
+ /*
+ * A date after January 19th, 2038 does not fit on 32 bits and
+ * will confuse the kernel and userspace. Reset to a sane date
+ * (January 1st, 2013) if we're after 2038.
+ */
+ rtc_date = readl(pdata->ioaddr + RTC_DATE_REG_OFFS);
+ if (bcd2bin((rtc_date >> RTC_YEAR_OFFS) & 0xff) >= 38) {
+ dev_info(&pdev->dev, "invalid RTC date, resetting to January 1st, 2013\n");
+ writel(0x130101, pdata->ioaddr + RTC_DATE_REG_OFFS);
+ }
+
pdata->irq = platform_get_irq(pdev, 0);
platform_set_drvdata(pdev, pdata);
diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c
index 248653c74b80..a53da0958e95 100644
--- a/drivers/rtc/rtc-nuc900.c
+++ b/drivers/rtc/rtc-nuc900.c
@@ -229,10 +229,9 @@ static int __init nuc900_rtc_probe(struct platform_device *pdev)
nuc900_rtc = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_rtc),
GFP_KERNEL);
- if (!nuc900_rtc) {
- dev_err(&pdev->dev, "kzalloc nuc900_rtc failed\n");
+ if (!nuc900_rtc)
return -ENOMEM;
- }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nuc900_rtc->rtc_reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(nuc900_rtc->rtc_reg))
diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c
index fffb7d3449d7..c360d62fb3f6 100644
--- a/drivers/rtc/rtc-palmas.c
+++ b/drivers/rtc/rtc-palmas.c
@@ -348,9 +348,8 @@ static int palmas_rtc_resume(struct device *dev)
}
#endif
-static const struct dev_pm_ops palmas_rtc_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(palmas_rtc_suspend, palmas_rtc_resume)
-};
+static SIMPLE_DEV_PM_OPS(palmas_rtc_pm_ops, palmas_rtc_suspend,
+ palmas_rtc_resume);
#ifdef CONFIG_OF
static struct of_device_id of_palmas_rtc_match[] = {
diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c
index 03f8f75d5af2..197699f358c7 100644
--- a/drivers/rtc/rtc-pm8xxx.c
+++ b/drivers/rtc/rtc-pm8xxx.c
@@ -9,18 +9,16 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-
+#include <linux/of.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rtc.h>
+#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <linux/mfd/pm8xxx/core.h>
-#include <linux/mfd/pm8xxx/rtc.h>
-
-
/* RTC Register offsets from RTC CTRL REG */
#define PM8XXX_ALARM_CTRL_OFFSET 0x01
#define PM8XXX_RTC_WRITE_OFFSET 0x02
@@ -37,6 +35,8 @@
/**
* struct pm8xxx_rtc - rtc driver internal structure
* @rtc: rtc device for this driver.
+ * @regmap: regmap used to access RTC registers
+ * @allow_set_time: indicates whether writing to the RTC is allowed
* @rtc_alarm_irq: rtc alarm irq number.
* @rtc_base: address of rtc control register.
* @rtc_read_base: base address of read registers.
@@ -48,55 +48,19 @@
*/
struct pm8xxx_rtc {
struct rtc_device *rtc;
+ struct regmap *regmap;
+ bool allow_set_time;
int rtc_alarm_irq;
int rtc_base;
int rtc_read_base;
int rtc_write_base;
int alarm_rw_base;
- u8 ctrl_reg;
+ u8 ctrl_reg;
struct device *rtc_dev;
spinlock_t ctrl_reg_lock;
};
/*
- * The RTC registers need to be read/written one byte at a time. This is a
- * hardware limitation.
- */
-static int pm8xxx_read_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val,
- int base, int count)
-{
- int i, rc;
- struct device *parent = rtc_dd->rtc_dev->parent;
-
- for (i = 0; i < count; i++) {
- rc = pm8xxx_readb(parent, base + i, &rtc_val[i]);
- if (rc < 0) {
- dev_err(rtc_dd->rtc_dev, "PMIC read failed\n");
- return rc;
- }
- }
-
- return 0;
-}
-
-static int pm8xxx_write_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val,
- int base, int count)
-{
- int i, rc;
- struct device *parent = rtc_dd->rtc_dev->parent;
-
- for (i = 0; i < count; i++) {
- rc = pm8xxx_writeb(parent, base + i, rtc_val[i]);
- if (rc < 0) {
- dev_err(rtc_dd->rtc_dev, "PMIC write failed\n");
- return rc;
- }
- }
-
- return 0;
-}
-
-/*
* Steps to write the RTC registers.
* 1. Disable alarm if enabled.
* 2. Write 0x00 to LSB.
@@ -107,9 +71,12 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
int rc, i;
unsigned long secs, irq_flags;
- u8 value[NUM_8_BIT_RTC_REGS], reg = 0, alarm_enabled = 0, ctrl_reg;
+ u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0, ctrl_reg;
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+ if (!rtc_dd->allow_set_time)
+ return -EACCES;
+
rtc_tm_to_time(tm, &secs);
for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
@@ -125,47 +92,43 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
if (ctrl_reg & PM8xxx_RTC_ALARM_ENABLE) {
alarm_enabled = 1;
ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
- rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
- 1);
- if (rc < 0) {
- dev_err(dev, "Write to RTC control register "
- "failed\n");
+ rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
+ if (rc) {
+ dev_err(dev, "Write to RTC control register failed\n");
goto rtc_rw_fail;
}
rtc_dd->ctrl_reg = ctrl_reg;
- } else
+ } else {
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+ }
/* Write 0 to Byte[0] */
- reg = 0;
- rc = pm8xxx_write_wrapper(rtc_dd, &reg, rtc_dd->rtc_write_base, 1);
- if (rc < 0) {
+ rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_write_base, 0);
+ if (rc) {
dev_err(dev, "Write to RTC write data register failed\n");
goto rtc_rw_fail;
}
/* Write Byte[1], Byte[2], Byte[3] */
- rc = pm8xxx_write_wrapper(rtc_dd, value + 1,
- rtc_dd->rtc_write_base + 1, 3);
- if (rc < 0) {
+ rc = regmap_bulk_write(rtc_dd->regmap, rtc_dd->rtc_write_base + 1,
+ &value[1], sizeof(value) - 1);
+ if (rc) {
dev_err(dev, "Write to RTC write data register failed\n");
goto rtc_rw_fail;
}
/* Write Byte[0] */
- rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->rtc_write_base, 1);
- if (rc < 0) {
+ rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_write_base, value[0]);
+ if (rc) {
dev_err(dev, "Write to RTC write data register failed\n");
goto rtc_rw_fail;
}
if (alarm_enabled) {
ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE;
- rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
- 1);
- if (rc < 0) {
- dev_err(dev, "Write to RTC control register "
- "failed\n");
+ rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
+ if (rc) {
+ dev_err(dev, "Write to RTC control register failed\n");
goto rtc_rw_fail;
}
rtc_dd->ctrl_reg = ctrl_reg;
@@ -181,13 +144,14 @@ rtc_rw_fail:
static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
int rc;
- u8 value[NUM_8_BIT_RTC_REGS], reg;
+ u8 value[NUM_8_BIT_RTC_REGS];
unsigned long secs;
+ unsigned int reg;
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
- rc = pm8xxx_read_wrapper(rtc_dd, value, rtc_dd->rtc_read_base,
- NUM_8_BIT_RTC_REGS);
- if (rc < 0) {
+ rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->rtc_read_base,
+ value, sizeof(value));
+ if (rc) {
dev_err(dev, "RTC read data register failed\n");
return rc;
}
@@ -196,16 +160,16 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
* Read the LSB again and check if there has been a carry over.
* If there is, redo the read operation.
*/
- rc = pm8xxx_read_wrapper(rtc_dd, &reg, rtc_dd->rtc_read_base, 1);
+ rc = regmap_read(rtc_dd->regmap, rtc_dd->rtc_read_base, &reg);
if (rc < 0) {
dev_err(dev, "RTC read data register failed\n");
return rc;
}
if (unlikely(reg < value[0])) {
- rc = pm8xxx_read_wrapper(rtc_dd, value,
- rtc_dd->rtc_read_base, NUM_8_BIT_RTC_REGS);
- if (rc < 0) {
+ rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->rtc_read_base,
+ value, sizeof(value));
+ if (rc) {
dev_err(dev, "RTC read data register failed\n");
return rc;
}
@@ -222,8 +186,8 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
}
dev_dbg(dev, "secs = %lu, h:m:s == %d:%d:%d, d/m/y = %d/%d/%d\n",
- secs, tm->tm_hour, tm->tm_min, tm->tm_sec,
- tm->tm_mday, tm->tm_mon, tm->tm_year);
+ secs, tm->tm_hour, tm->tm_min, tm->tm_sec,
+ tm->tm_mday, tm->tm_mon, tm->tm_year);
return 0;
}
@@ -244,19 +208,22 @@ static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
- rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->alarm_rw_base,
- NUM_8_BIT_RTC_REGS);
- if (rc < 0) {
+ rc = regmap_bulk_write(rtc_dd->regmap, rtc_dd->alarm_rw_base, value,
+ sizeof(value));
+ if (rc) {
dev_err(dev, "Write to RTC ALARM register failed\n");
goto rtc_rw_fail;
}
ctrl_reg = rtc_dd->ctrl_reg;
- ctrl_reg = alarm->enabled ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) :
- (ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE);
- rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
- if (rc < 0) {
+ if (alarm->enabled)
+ ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE;
+ else
+ ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
+
+ rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
+ if (rc) {
dev_err(dev, "Write to RTC control register failed\n");
goto rtc_rw_fail;
}
@@ -264,9 +231,9 @@ static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
rtc_dd->ctrl_reg = ctrl_reg;
dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
- alarm->time.tm_hour, alarm->time.tm_min,
- alarm->time.tm_sec, alarm->time.tm_mday,
- alarm->time.tm_mon, alarm->time.tm_year);
+ alarm->time.tm_hour, alarm->time.tm_min,
+ alarm->time.tm_sec, alarm->time.tm_mday,
+ alarm->time.tm_mon, alarm->time.tm_year);
rtc_rw_fail:
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
return rc;
@@ -279,9 +246,9 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
unsigned long secs;
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
- rc = pm8xxx_read_wrapper(rtc_dd, value, rtc_dd->alarm_rw_base,
- NUM_8_BIT_RTC_REGS);
- if (rc < 0) {
+ rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->alarm_rw_base, value,
+ sizeof(value));
+ if (rc) {
dev_err(dev, "RTC alarm time read failed\n");
return rc;
}
@@ -297,9 +264,9 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
}
dev_dbg(dev, "Alarm set for - h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
- alarm->time.tm_hour, alarm->time.tm_min,
- alarm->time.tm_sec, alarm->time.tm_mday,
- alarm->time.tm_mon, alarm->time.tm_year);
+ alarm->time.tm_hour, alarm->time.tm_min,
+ alarm->time.tm_sec, alarm->time.tm_mday,
+ alarm->time.tm_mon, alarm->time.tm_year);
return 0;
}
@@ -312,12 +279,16 @@ static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
u8 ctrl_reg;
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
+
ctrl_reg = rtc_dd->ctrl_reg;
- ctrl_reg = (enable) ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) :
- (ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE);
- rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
- if (rc < 0) {
+ if (enable)
+ ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE;
+ else
+ ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
+
+ rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
+ if (rc) {
dev_err(dev, "Write to RTC control register failed\n");
goto rtc_rw_fail;
}
@@ -329,8 +300,9 @@ rtc_rw_fail:
return rc;
}
-static struct rtc_class_ops pm8xxx_rtc_ops = {
+static const struct rtc_class_ops pm8xxx_rtc_ops = {
.read_time = pm8xxx_rtc_read_time,
+ .set_time = pm8xxx_rtc_set_time,
.set_alarm = pm8xxx_rtc_set_alarm,
.read_alarm = pm8xxx_rtc_read_alarm,
.alarm_irq_enable = pm8xxx_rtc_alarm_irq_enable,
@@ -339,7 +311,7 @@ static struct rtc_class_ops pm8xxx_rtc_ops = {
static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
{
struct pm8xxx_rtc *rtc_dd = dev_id;
- u8 ctrl_reg;
+ unsigned int ctrl_reg;
int rc;
unsigned long irq_flags;
@@ -351,11 +323,11 @@ static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
ctrl_reg = rtc_dd->ctrl_reg;
ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
- rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
- if (rc < 0) {
+ rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
+ if (rc) {
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
- dev_err(rtc_dd->rtc_dev, "Write to RTC control register "
- "failed\n");
+ dev_err(rtc_dd->rtc_dev,
+ "Write to RTC control register failed\n");
goto rtc_alarm_handled;
}
@@ -363,61 +335,71 @@ static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
/* Clear RTC alarm register */
- rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base +
- PM8XXX_ALARM_CTRL_OFFSET, 1);
- if (rc < 0) {
- dev_err(rtc_dd->rtc_dev, "RTC Alarm control register read "
- "failed\n");
+ rc = regmap_read(rtc_dd->regmap,
+ rtc_dd->rtc_base + PM8XXX_ALARM_CTRL_OFFSET,
+ &ctrl_reg);
+ if (rc) {
+ dev_err(rtc_dd->rtc_dev,
+ "RTC Alarm control register read failed\n");
goto rtc_alarm_handled;
}
ctrl_reg &= ~PM8xxx_RTC_ALARM_CLEAR;
- rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base +
- PM8XXX_ALARM_CTRL_OFFSET, 1);
- if (rc < 0)
- dev_err(rtc_dd->rtc_dev, "Write to RTC Alarm control register"
- " failed\n");
+ rc = regmap_write(rtc_dd->regmap,
+ rtc_dd->rtc_base + PM8XXX_ALARM_CTRL_OFFSET,
+ ctrl_reg);
+ if (rc)
+ dev_err(rtc_dd->rtc_dev,
+ "Write to RTC Alarm control register failed\n");
rtc_alarm_handled:
return IRQ_HANDLED;
}
+/*
+ * Hardcoded RTC bases until IORESOURCE_REG mapping is figured out
+ */
+static const struct of_device_id pm8xxx_id_table[] = {
+ { .compatible = "qcom,pm8921-rtc", .data = (void *) 0x11D },
+ { .compatible = "qcom,pm8058-rtc", .data = (void *) 0x1E8 },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
+
static int pm8xxx_rtc_probe(struct platform_device *pdev)
{
int rc;
- u8 ctrl_reg;
- bool rtc_write_enable = false;
+ unsigned int ctrl_reg;
struct pm8xxx_rtc *rtc_dd;
- struct resource *rtc_resource;
- const struct pm8xxx_rtc_platform_data *pdata =
- dev_get_platdata(&pdev->dev);
+ const struct of_device_id *match;
- if (pdata != NULL)
- rtc_write_enable = pdata->rtc_write_enable;
+ match = of_match_node(pm8xxx_id_table, pdev->dev.of_node);
+ if (!match)
+ return -ENXIO;
rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL);
- if (rtc_dd == NULL) {
- dev_err(&pdev->dev, "Unable to allocate memory!\n");
+ if (rtc_dd == NULL)
return -ENOMEM;
- }
/* Initialise spinlock to protect RTC control register */
spin_lock_init(&rtc_dd->ctrl_reg_lock);
+ rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!rtc_dd->regmap) {
+ dev_err(&pdev->dev, "Parent regmap unavailable.\n");
+ return -ENXIO;
+ }
+
rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0);
if (rtc_dd->rtc_alarm_irq < 0) {
dev_err(&pdev->dev, "Alarm IRQ resource absent!\n");
return -ENXIO;
}
- rtc_resource = platform_get_resource_byname(pdev, IORESOURCE_IO,
- "pmic_rtc_base");
- if (!(rtc_resource && rtc_resource->start)) {
- dev_err(&pdev->dev, "RTC IO resource absent!\n");
- return -ENXIO;
- }
+ rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node,
+ "allow-set-time");
- rtc_dd->rtc_base = rtc_resource->start;
+ rtc_dd->rtc_base = (long) match->data;
/* Setup RTC register addresses */
rtc_dd->rtc_write_base = rtc_dd->rtc_base + PM8XXX_RTC_WRITE_OFFSET;
@@ -427,64 +409,52 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
rtc_dd->rtc_dev = &pdev->dev;
/* Check if the RTC is on, else turn it on */
- rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
- if (rc < 0) {
+ rc = regmap_read(rtc_dd->regmap, rtc_dd->rtc_base, &ctrl_reg);
+ if (rc) {
dev_err(&pdev->dev, "RTC control register read failed!\n");
return rc;
}
if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) {
ctrl_reg |= PM8xxx_RTC_ENABLE;
- rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
- 1);
- if (rc < 0) {
- dev_err(&pdev->dev, "Write to RTC control register "
- "failed\n");
+ rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "Write to RTC control register failed\n");
return rc;
}
}
rtc_dd->ctrl_reg = ctrl_reg;
- if (rtc_write_enable == true)
- pm8xxx_rtc_ops.set_time = pm8xxx_rtc_set_time;
platform_set_drvdata(pdev, rtc_dd);
+ device_init_wakeup(&pdev->dev, 1);
+
/* Register the RTC device */
rtc_dd->rtc = devm_rtc_device_register(&pdev->dev, "pm8xxx_rtc",
- &pm8xxx_rtc_ops, THIS_MODULE);
+ &pm8xxx_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc_dd->rtc)) {
dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n",
- __func__, PTR_ERR(rtc_dd->rtc));
+ __func__, PTR_ERR(rtc_dd->rtc));
return PTR_ERR(rtc_dd->rtc);
}
/* Request the alarm IRQ */
- rc = request_any_context_irq(rtc_dd->rtc_alarm_irq,
- pm8xxx_alarm_trigger, IRQF_TRIGGER_RISING,
- "pm8xxx_rtc_alarm", rtc_dd);
+ rc = devm_request_any_context_irq(&pdev->dev, rtc_dd->rtc_alarm_irq,
+ pm8xxx_alarm_trigger,
+ IRQF_TRIGGER_RISING,
+ "pm8xxx_rtc_alarm", rtc_dd);
if (rc < 0) {
dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc);
return rc;
}
- device_init_wakeup(&pdev->dev, 1);
-
dev_dbg(&pdev->dev, "Probe success !!\n");
return 0;
}
-static int pm8xxx_rtc_remove(struct platform_device *pdev)
-{
- struct pm8xxx_rtc *rtc_dd = platform_get_drvdata(pdev);
-
- device_init_wakeup(&pdev->dev, 0);
- free_irq(rtc_dd->rtc_alarm_irq, rtc_dd);
-
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int pm8xxx_rtc_resume(struct device *dev)
{
@@ -507,15 +477,17 @@ static int pm8xxx_rtc_suspend(struct device *dev)
}
#endif
-static SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, pm8xxx_rtc_suspend, pm8xxx_rtc_resume);
+static SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops,
+ pm8xxx_rtc_suspend,
+ pm8xxx_rtc_resume);
static struct platform_driver pm8xxx_rtc_driver = {
.probe = pm8xxx_rtc_probe,
- .remove = pm8xxx_rtc_remove,
.driver = {
- .name = PM8XXX_RTC_DEV_NAME,
- .owner = THIS_MODULE,
- .pm = &pm8xxx_rtc_pm_ops,
+ .name = "rtc-pm8xxx",
+ .owner = THIS_MODULE,
+ .pm = &pm8xxx_rtc_pm_ops,
+ .of_match_table = pm8xxx_id_table,
},
};
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
index a355f2b82bb8..cccbf9d89729 100644
--- a/drivers/rtc/rtc-pxa.c
+++ b/drivers/rtc/rtc-pxa.c
@@ -32,7 +32,6 @@
#include <mach/hardware.h>
-#define TIMER_FREQ CLOCK_TICK_RATE
#define RTC_DEF_DIVIDER (32768 - 1)
#define RTC_DEF_TRIM 0
#define MAXFREQ_PERIODIC 1000
diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c
index 1a779a67ff66..e9ac5a43be1a 100644
--- a/drivers/rtc/rtc-rv3029c2.c
+++ b/drivers/rtc/rtc-rv3029c2.c
@@ -395,6 +395,12 @@ static int rv3029c2_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_EMUL))
return -ENODEV;
+ rc = rv3029c2_i2c_get_sr(client, buf);
+ if (rc < 0) {
+ dev_err(&client->dev, "reading status failed\n");
+ return rc;
+ }
+
rtc = devm_rtc_device_register(&client->dev, client->name,
&rv3029c2_rtc_ops, THIS_MODULE);
@@ -403,12 +409,6 @@ static int rv3029c2_probe(struct i2c_client *client,
i2c_set_clientdata(client, rtc);
- rc = rv3029c2_i2c_get_sr(client, buf);
- if (rc < 0) {
- dev_err(&client->dev, "reading status failed\n");
- return rc;
- }
-
return 0;
}
diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c
index 8fa23eabcb68..e6298e02b400 100644
--- a/drivers/rtc/rtc-rx8025.c
+++ b/drivers/rtc/rtc-rx8025.c
@@ -551,7 +551,6 @@ static int rx8025_probe(struct i2c_client *client,
rx8025 = devm_kzalloc(&client->dev, sizeof(*rx8025), GFP_KERNEL);
if (!rx8025) {
- dev_err(&adapter->dev, "failed to alloc memory\n");
err = -ENOMEM;
goto errout;
}
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index c4cde9c08f1f..4958a363b2c7 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -48,8 +48,8 @@ struct s3c_rtc_drv_data {
static struct clk *rtc_clk;
static void __iomem *s3c_rtc_base;
-static int s3c_rtc_alarmno = NO_IRQ;
-static int s3c_rtc_tickno = NO_IRQ;
+static int s3c_rtc_alarmno;
+static int s3c_rtc_tickno;
static enum s3c_cpu_type s3c_rtc_cpu_type;
static DEFINE_SPINLOCK(s3c_rtc_pie_lock);
diff --git a/drivers/rtc/rtc-sirfsoc.c b/drivers/rtc/rtc-sirfsoc.c
index 3eb3642ae299..76e38007ba90 100644
--- a/drivers/rtc/rtc-sirfsoc.c
+++ b/drivers/rtc/rtc-sirfsoc.c
@@ -264,12 +264,8 @@ static int sirfsoc_rtc_probe(struct platform_device *pdev)
rtcdrv = devm_kzalloc(&pdev->dev,
sizeof(struct sirfsoc_rtc_drv), GFP_KERNEL);
- if (rtcdrv == NULL) {
- dev_err(&pdev->dev,
- "%s: can't alloc mem for drv struct\n",
- pdev->name);
+ if (rtcdrv == NULL)
return -ENOMEM;
- }
err = of_property_read_u32(np, "reg", &rtcdrv->rtc_base);
if (err) {
@@ -335,39 +331,29 @@ static int sirfsoc_rtc_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
-
+#ifdef CONFIG_PM_SLEEP
static int sirfsoc_rtc_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct sirfsoc_rtc_drv *rtcdrv = platform_get_drvdata(pdev);
+ struct sirfsoc_rtc_drv *rtcdrv = dev_get_drvdata(dev);
rtcdrv->overflow_rtc =
sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_SW_VALUE);
rtcdrv->saved_counter =
sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN);
rtcdrv->saved_overflow_rtc = rtcdrv->overflow_rtc;
- if (device_may_wakeup(&pdev->dev) && !enable_irq_wake(rtcdrv->irq))
+ if (device_may_wakeup(dev) && !enable_irq_wake(rtcdrv->irq))
rtcdrv->irq_wake = 1;
return 0;
}
-static int sirfsoc_rtc_freeze(struct device *dev)
-{
- sirfsoc_rtc_suspend(dev);
-
- return 0;
-}
-
-static int sirfsoc_rtc_thaw(struct device *dev)
+static int sirfsoc_rtc_resume(struct device *dev)
{
u32 tmp;
- struct sirfsoc_rtc_drv *rtcdrv;
- rtcdrv = dev_get_drvdata(dev);
+ struct sirfsoc_rtc_drv *rtcdrv = dev_get_drvdata(dev);
/*
- * if resume from snapshot and the rtc power is losed,
+ * if resume from snapshot and the rtc power is lost,
* restroe the rtc settings
*/
if (SIRFSOC_RTC_CLK != sirfsoc_rtc_iobrg_readl(
@@ -407,57 +393,23 @@ static int sirfsoc_rtc_thaw(struct device *dev)
sirfsoc_rtc_iobrg_writel(rtcdrv->overflow_rtc,
rtcdrv->rtc_base + RTC_SW_VALUE);
- return 0;
-}
-
-static int sirfsoc_rtc_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct sirfsoc_rtc_drv *rtcdrv = platform_get_drvdata(pdev);
- sirfsoc_rtc_thaw(dev);
- if (device_may_wakeup(&pdev->dev) && rtcdrv->irq_wake) {
+ if (device_may_wakeup(dev) && rtcdrv->irq_wake) {
disable_irq_wake(rtcdrv->irq);
rtcdrv->irq_wake = 0;
}
return 0;
}
-
-static int sirfsoc_rtc_restore(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct sirfsoc_rtc_drv *rtcdrv = platform_get_drvdata(pdev);
-
- if (device_may_wakeup(&pdev->dev) && rtcdrv->irq_wake) {
- disable_irq_wake(rtcdrv->irq);
- rtcdrv->irq_wake = 0;
- }
- return 0;
-}
-
-#else
-#define sirfsoc_rtc_suspend NULL
-#define sirfsoc_rtc_resume NULL
-#define sirfsoc_rtc_freeze NULL
-#define sirfsoc_rtc_thaw NULL
-#define sirfsoc_rtc_restore NULL
#endif
-static const struct dev_pm_ops sirfsoc_rtc_pm_ops = {
- .suspend = sirfsoc_rtc_suspend,
- .resume = sirfsoc_rtc_resume,
- .freeze = sirfsoc_rtc_freeze,
- .thaw = sirfsoc_rtc_thaw,
- .restore = sirfsoc_rtc_restore,
-};
+static SIMPLE_DEV_PM_OPS(sirfsoc_rtc_pm_ops,
+ sirfsoc_rtc_suspend, sirfsoc_rtc_resume);
static struct platform_driver sirfsoc_rtc_driver = {
.driver = {
.name = "sirfsoc-rtc",
.owner = THIS_MODULE,
-#ifdef CONFIG_PM
.pm = &sirfsoc_rtc_pm_ops,
-#endif
.of_match_table = sirfsoc_rtc_of_match,
},
.probe = sirfsoc_rtc_probe,
diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c
index c492cf0ab8cd..d2cdb9823a15 100644
--- a/drivers/rtc/rtc-spear.c
+++ b/drivers/rtc/rtc-spear.c
@@ -365,10 +365,8 @@ static int spear_rtc_probe(struct platform_device *pdev)
}
config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
- if (!config) {
- dev_err(&pdev->dev, "out of memory\n");
+ if (!config)
return -ENOMEM;
- }
/* alarm irqs */
irq = platform_get_irq(pdev, 0);
diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c
index a176ba614683..35ed49ea1f81 100644
--- a/drivers/rtc/rtc-stk17ta8.c
+++ b/drivers/rtc/rtc-stk17ta8.c
@@ -214,8 +214,7 @@ static irqreturn_t stk17ta8_rtc_interrupt(int irq, void *dev_id)
events |= RTC_UF;
else
events |= RTC_AF;
- if (likely(pdata->rtc))
- rtc_update_irq(pdata->rtc, 1, events);
+ rtc_update_irq(pdata->rtc, 1, events);
}
spin_unlock(&pdata->lock);
return events ? IRQ_HANDLED : IRQ_NONE;
diff --git a/drivers/rtc/rtc-sunxi.c b/drivers/rtc/rtc-sunxi.c
index 68a35284e5ad..b6f21f73d508 100644
--- a/drivers/rtc/rtc-sunxi.c
+++ b/drivers/rtc/rtc-sunxi.c
@@ -428,7 +428,7 @@ static const struct rtc_class_ops sunxi_rtc_ops = {
};
static const struct of_device_id sunxi_rtc_dt_ids[] = {
- { .compatible = "allwinner,sun4i-rtc", .data = &data_year_param[0] },
+ { .compatible = "allwinner,sun4i-a10-rtc", .data = &data_year_param[0] },
{ .compatible = "allwinner,sun7i-a20-rtc", .data = &data_year_param[1] },
{ /* sentinel */ },
};
diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c
index 7746e65b93f2..6599c20bc454 100644
--- a/drivers/rtc/rtc-test.c
+++ b/drivers/rtc/rtc-test.c
@@ -104,20 +104,17 @@ static int test_probe(struct platform_device *plat_dev)
rtc = devm_rtc_device_register(&plat_dev->dev, "test",
&test_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
- err = PTR_ERR(rtc);
- return err;
+ return PTR_ERR(rtc);
}
err = device_create_file(&plat_dev->dev, &dev_attr_irq);
if (err)
- goto err;
+ dev_err(&plat_dev->dev, "Unable to create sysfs entry: %s\n",
+ dev_attr_irq.attr.name);
platform_set_drvdata(plat_dev, rtc);
return 0;
-
-err:
- return err;
}
static int test_remove(struct platform_device *plat_dev)
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
index 4f87234e0dee..2e678c681b13 100644
--- a/drivers/rtc/rtc-tx4939.c
+++ b/drivers/rtc/rtc-tx4939.c
@@ -176,8 +176,8 @@ static irqreturn_t tx4939_rtc_interrupt(int irq, void *dev_id)
tx4939_rtc_cmd(rtcreg, TX4939_RTCCTL_COMMAND_NOP);
}
spin_unlock(&pdata->lock);
- if (likely(pdata->rtc))
- rtc_update_irq(pdata->rtc, 1, events);
+ rtc_update_irq(pdata->rtc, 1, events);
+
return IRQ_HANDLED;
}
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
index df2ef3eba7cd..051da968da6d 100644
--- a/drivers/rtc/rtc-vt8500.c
+++ b/drivers/rtc/rtc-vt8500.c
@@ -79,7 +79,6 @@
struct vt8500_rtc {
void __iomem *regbase;
- struct resource *res;
int irq_alarm;
struct rtc_device *rtc;
spinlock_t lock; /* Protects this structure */
@@ -209,6 +208,7 @@ static const struct rtc_class_ops vt8500_rtc_ops = {
static int vt8500_rtc_probe(struct platform_device *pdev)
{
struct vt8500_rtc *vt8500_rtc;
+ struct resource *res;
int ret;
vt8500_rtc = devm_kzalloc(&pdev->dev,
@@ -219,34 +219,16 @@ static int vt8500_rtc_probe(struct platform_device *pdev)
spin_lock_init(&vt8500_rtc->lock);
platform_set_drvdata(pdev, vt8500_rtc);
- vt8500_rtc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!vt8500_rtc->res) {
- dev_err(&pdev->dev, "No I/O memory resource defined\n");
- return -ENXIO;
- }
-
vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
if (vt8500_rtc->irq_alarm < 0) {
dev_err(&pdev->dev, "No alarm IRQ resource defined\n");
return vt8500_rtc->irq_alarm;
}
- vt8500_rtc->res = devm_request_mem_region(&pdev->dev,
- vt8500_rtc->res->start,
- resource_size(vt8500_rtc->res),
- "vt8500-rtc");
- if (vt8500_rtc->res == NULL) {
- dev_err(&pdev->dev, "failed to request I/O memory\n");
- return -EBUSY;
- }
-
- vt8500_rtc->regbase = devm_ioremap(&pdev->dev, vt8500_rtc->res->start,
- resource_size(vt8500_rtc->res));
- if (!vt8500_rtc->regbase) {
- dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
- ret = -EBUSY;
- goto err_return;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ vt8500_rtc->regbase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(vt8500_rtc->regbase))
+ return PTR_ERR(vt8500_rtc->regbase);
/* Enable RTC and set it to 24-hour mode */
writel(VT8500_RTC_CR_ENABLE,
diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c
index 365dc6505148..b1de58e0b3d0 100644
--- a/drivers/rtc/rtc-x1205.c
+++ b/drivers/rtc/rtc-x1205.c
@@ -660,7 +660,7 @@ static int x1205_probe(struct i2c_client *client,
err = x1205_sysfs_register(&client->dev);
if (err)
- return err;
+ dev_err(&client->dev, "Unable to create sysfs entries\n");
return 0;
}
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c
index 0fc584832001..1e1fc671f89a 100644
--- a/drivers/s390/kvm/virtio_ccw.c
+++ b/drivers/s390/kvm/virtio_ccw.c
@@ -1,7 +1,7 @@
/*
* ccw based virtio transport
*
- * Copyright IBM Corp. 2012
+ * Copyright IBM Corp. 2012, 2014
*
* 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)
@@ -32,6 +32,8 @@
#include <asm/cio.h>
#include <asm/ccwdev.h>
#include <asm/virtio-ccw.h>
+#include <asm/isc.h>
+#include <asm/airq.h>
/*
* virtio related functions
@@ -58,6 +60,9 @@ struct virtio_ccw_device {
unsigned long indicators;
unsigned long indicators2;
struct vq_config_block *config_block;
+ bool is_thinint;
+ bool going_away;
+ void *airq_info;
};
struct vq_info_block {
@@ -72,15 +77,38 @@ struct virtio_feature_desc {
__u8 index;
} __packed;
+struct virtio_thinint_area {
+ unsigned long summary_indicator;
+ unsigned long indicator;
+ u64 bit_nr;
+ u8 isc;
+} __packed;
+
struct virtio_ccw_vq_info {
struct virtqueue *vq;
int num;
void *queue;
struct vq_info_block *info_block;
+ int bit_nr;
struct list_head node;
long cookie;
};
+#define VIRTIO_AIRQ_ISC IO_SCH_ISC /* inherit from subchannel */
+
+#define VIRTIO_IV_BITS (L1_CACHE_BYTES * 8)
+#define MAX_AIRQ_AREAS 20
+
+static int virtio_ccw_use_airq = 1;
+
+struct airq_info {
+ rwlock_t lock;
+ u8 summary_indicator;
+ struct airq_struct airq;
+ struct airq_iv *aiv;
+};
+static struct airq_info *airq_areas[MAX_AIRQ_AREAS];
+
#define CCW_CMD_SET_VQ 0x13
#define CCW_CMD_VDEV_RESET 0x33
#define CCW_CMD_SET_IND 0x43
@@ -91,6 +119,7 @@ struct virtio_ccw_vq_info {
#define CCW_CMD_WRITE_CONF 0x21
#define CCW_CMD_WRITE_STATUS 0x31
#define CCW_CMD_READ_VQ_CONF 0x32
+#define CCW_CMD_SET_IND_ADAPTER 0x73
#define VIRTIO_CCW_DOING_SET_VQ 0x00010000
#define VIRTIO_CCW_DOING_RESET 0x00040000
@@ -102,6 +131,7 @@ struct virtio_ccw_vq_info {
#define VIRTIO_CCW_DOING_SET_IND 0x01000000
#define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000
#define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000
+#define VIRTIO_CCW_DOING_SET_IND_ADAPTER 0x08000000
#define VIRTIO_CCW_INTPARM_MASK 0xffff0000
static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev)
@@ -109,6 +139,125 @@ static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev)
return container_of(vdev, struct virtio_ccw_device, vdev);
}
+static void drop_airq_indicator(struct virtqueue *vq, struct airq_info *info)
+{
+ unsigned long i, flags;
+
+ write_lock_irqsave(&info->lock, flags);
+ for (i = 0; i < airq_iv_end(info->aiv); i++) {
+ if (vq == (void *)airq_iv_get_ptr(info->aiv, i)) {
+ airq_iv_free_bit(info->aiv, i);
+ airq_iv_set_ptr(info->aiv, i, 0);
+ break;
+ }
+ }
+ write_unlock_irqrestore(&info->lock, flags);
+}
+
+static void virtio_airq_handler(struct airq_struct *airq)
+{
+ struct airq_info *info = container_of(airq, struct airq_info, airq);
+ unsigned long ai;
+
+ inc_irq_stat(IRQIO_VAI);
+ read_lock(&info->lock);
+ /* Walk through indicators field, summary indicator active. */
+ for (ai = 0;;) {
+ ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv));
+ if (ai == -1UL)
+ break;
+ vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai));
+ }
+ info->summary_indicator = 0;
+ smp_wmb();
+ /* Walk through indicators field, summary indicator not active. */
+ for (ai = 0;;) {
+ ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv));
+ if (ai == -1UL)
+ break;
+ vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai));
+ }
+ read_unlock(&info->lock);
+}
+
+static struct airq_info *new_airq_info(void)
+{
+ struct airq_info *info;
+ int rc;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return NULL;
+ rwlock_init(&info->lock);
+ info->aiv = airq_iv_create(VIRTIO_IV_BITS, AIRQ_IV_ALLOC | AIRQ_IV_PTR);
+ if (!info->aiv) {
+ kfree(info);
+ return NULL;
+ }
+ info->airq.handler = virtio_airq_handler;
+ info->airq.lsi_ptr = &info->summary_indicator;
+ info->airq.lsi_mask = 0xff;
+ info->airq.isc = VIRTIO_AIRQ_ISC;
+ rc = register_adapter_interrupt(&info->airq);
+ if (rc) {
+ airq_iv_release(info->aiv);
+ kfree(info);
+ return NULL;
+ }
+ return info;
+}
+
+static void destroy_airq_info(struct airq_info *info)
+{
+ if (!info)
+ return;
+
+ unregister_adapter_interrupt(&info->airq);
+ airq_iv_release(info->aiv);
+ kfree(info);
+}
+
+static unsigned long get_airq_indicator(struct virtqueue *vqs[], int nvqs,
+ u64 *first, void **airq_info)
+{
+ int i, j;
+ struct airq_info *info;
+ unsigned long indicator_addr = 0;
+ unsigned long bit, flags;
+
+ for (i = 0; i < MAX_AIRQ_AREAS && !indicator_addr; i++) {
+ if (!airq_areas[i])
+ airq_areas[i] = new_airq_info();
+ info = airq_areas[i];
+ if (!info)
+ return 0;
+ write_lock_irqsave(&info->lock, flags);
+ bit = airq_iv_alloc(info->aiv, nvqs);
+ if (bit == -1UL) {
+ /* Not enough vacancies. */
+ write_unlock_irqrestore(&info->lock, flags);
+ continue;
+ }
+ *first = bit;
+ *airq_info = info;
+ indicator_addr = (unsigned long)info->aiv->vector;
+ for (j = 0; j < nvqs; j++) {
+ airq_iv_set_ptr(info->aiv, bit + j,
+ (unsigned long)vqs[j]);
+ }
+ write_unlock_irqrestore(&info->lock, flags);
+ }
+ return indicator_addr;
+}
+
+static void virtio_ccw_drop_indicators(struct virtio_ccw_device *vcdev)
+{
+ struct virtio_ccw_vq_info *info;
+
+ list_for_each_entry(info, &vcdev->virtqueues, node)
+ drop_airq_indicator(info->vq, vcdev->airq_info);
+}
+
static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag)
{
unsigned long flags;
@@ -145,6 +294,51 @@ static int ccw_io_helper(struct virtio_ccw_device *vcdev,
return ret ? ret : vcdev->err;
}
+static void virtio_ccw_drop_indicator(struct virtio_ccw_device *vcdev,
+ struct ccw1 *ccw)
+{
+ int ret;
+ unsigned long *indicatorp = NULL;
+ struct virtio_thinint_area *thinint_area = NULL;
+ struct airq_info *airq_info = vcdev->airq_info;
+
+ if (vcdev->is_thinint) {
+ thinint_area = kzalloc(sizeof(*thinint_area),
+ GFP_DMA | GFP_KERNEL);
+ if (!thinint_area)
+ return;
+ thinint_area->summary_indicator =
+ (unsigned long) &airq_info->summary_indicator;
+ thinint_area->isc = VIRTIO_AIRQ_ISC;
+ ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER;
+ ccw->count = sizeof(*thinint_area);
+ ccw->cda = (__u32)(unsigned long) thinint_area;
+ } else {
+ indicatorp = kmalloc(sizeof(&vcdev->indicators),
+ GFP_DMA | GFP_KERNEL);
+ if (!indicatorp)
+ return;
+ *indicatorp = 0;
+ ccw->cmd_code = CCW_CMD_SET_IND;
+ ccw->count = sizeof(vcdev->indicators);
+ ccw->cda = (__u32)(unsigned long) indicatorp;
+ }
+ /* Deregister indicators from host. */
+ vcdev->indicators = 0;
+ ccw->flags = 0;
+ ret = ccw_io_helper(vcdev, ccw,
+ vcdev->is_thinint ?
+ VIRTIO_CCW_DOING_SET_IND_ADAPTER :
+ VIRTIO_CCW_DOING_SET_IND);
+ if (ret && (ret != -ENODEV))
+ dev_info(&vcdev->cdev->dev,
+ "Failed to deregister indicators (%d)\n", ret);
+ else if (vcdev->is_thinint)
+ virtio_ccw_drop_indicators(vcdev);
+ kfree(indicatorp);
+ kfree(thinint_area);
+}
+
static inline long do_kvm_notify(struct subchannel_id schid,
unsigned long queue_index,
long cookie)
@@ -232,11 +426,13 @@ static void virtio_ccw_del_vqs(struct virtio_device *vdev)
{
struct virtqueue *vq, *n;
struct ccw1 *ccw;
+ struct virtio_ccw_device *vcdev = to_vc_device(vdev);
ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
if (!ccw)
return;
+ virtio_ccw_drop_indicator(vcdev, ccw);
list_for_each_entry_safe(vq, n, &vdev->vqs, list)
virtio_ccw_del_vq(vq, ccw);
@@ -326,6 +522,54 @@ out_err:
return ERR_PTR(err);
}
+static int virtio_ccw_register_adapter_ind(struct virtio_ccw_device *vcdev,
+ struct virtqueue *vqs[], int nvqs,
+ struct ccw1 *ccw)
+{
+ int ret;
+ struct virtio_thinint_area *thinint_area = NULL;
+ struct airq_info *info;
+
+ thinint_area = kzalloc(sizeof(*thinint_area), GFP_DMA | GFP_KERNEL);
+ if (!thinint_area) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ /* Try to get an indicator. */
+ thinint_area->indicator = get_airq_indicator(vqs, nvqs,
+ &thinint_area->bit_nr,
+ &vcdev->airq_info);
+ if (!thinint_area->indicator) {
+ ret = -ENOSPC;
+ goto out;
+ }
+ info = vcdev->airq_info;
+ thinint_area->summary_indicator =
+ (unsigned long) &info->summary_indicator;
+ thinint_area->isc = VIRTIO_AIRQ_ISC;
+ ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER;
+ ccw->flags = CCW_FLAG_SLI;
+ ccw->count = sizeof(*thinint_area);
+ ccw->cda = (__u32)(unsigned long)thinint_area;
+ ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND_ADAPTER);
+ if (ret) {
+ if (ret == -EOPNOTSUPP) {
+ /*
+ * The host does not support adapter interrupts
+ * for virtio-ccw, stop trying.
+ */
+ virtio_ccw_use_airq = 0;
+ pr_info("Adapter interrupts unsupported on host\n");
+ } else
+ dev_warn(&vcdev->cdev->dev,
+ "enabling adapter interrupts = %d\n", ret);
+ virtio_ccw_drop_indicators(vcdev);
+ }
+out:
+ kfree(thinint_area);
+ return ret;
+}
+
static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs,
struct virtqueue *vqs[],
vq_callback_t *callbacks[],
@@ -355,15 +599,23 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs,
if (!indicatorp)
goto out;
*indicatorp = (unsigned long) &vcdev->indicators;
- /* Register queue indicators with host. */
- vcdev->indicators = 0;
- ccw->cmd_code = CCW_CMD_SET_IND;
- ccw->flags = 0;
- ccw->count = sizeof(vcdev->indicators);
- ccw->cda = (__u32)(unsigned long) indicatorp;
- ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND);
- if (ret)
- goto out;
+ if (vcdev->is_thinint) {
+ ret = virtio_ccw_register_adapter_ind(vcdev, vqs, nvqs, ccw);
+ if (ret)
+ /* no error, just fall back to legacy interrupts */
+ vcdev->is_thinint = 0;
+ }
+ if (!vcdev->is_thinint) {
+ /* Register queue indicators with host. */
+ vcdev->indicators = 0;
+ ccw->cmd_code = CCW_CMD_SET_IND;
+ ccw->flags = 0;
+ ccw->count = sizeof(vcdev->indicators);
+ ccw->cda = (__u32)(unsigned long) indicatorp;
+ ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND);
+ if (ret)
+ goto out;
+ }
/* Register indicators2 with host for config changes */
*indicatorp = (unsigned long) &vcdev->indicators2;
vcdev->indicators2 = 0;
@@ -636,6 +888,8 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
struct virtqueue *vq;
struct virtio_driver *drv;
+ if (!vcdev)
+ return;
/* Check if it's a notification from the host. */
if ((intparm == 0) &&
(scsw_stctl(&irb->scsw) ==
@@ -663,6 +917,7 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
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:
vcdev->curr_io &= ~activity;
wake_up(&vcdev->wait_q);
break;
@@ -734,23 +989,46 @@ static int virtio_ccw_probe(struct ccw_device *cdev)
return 0;
}
+static struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev)
+{
+ unsigned long flags;
+ struct virtio_ccw_device *vcdev;
+
+ spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+ vcdev = dev_get_drvdata(&cdev->dev);
+ if (!vcdev || vcdev->going_away) {
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+ return NULL;
+ }
+ vcdev->going_away = true;
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+ return vcdev;
+}
+
static void virtio_ccw_remove(struct ccw_device *cdev)
{
- struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev);
+ unsigned long flags;
+ struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
- if (cdev->online) {
+ if (vcdev && cdev->online)
unregister_virtio_device(&vcdev->vdev);
- dev_set_drvdata(&cdev->dev, NULL);
- }
+ spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+ dev_set_drvdata(&cdev->dev, NULL);
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
cdev->handler = NULL;
}
static int virtio_ccw_offline(struct ccw_device *cdev)
{
- struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev);
+ unsigned long flags;
+ struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
- unregister_virtio_device(&vcdev->vdev);
- dev_set_drvdata(&cdev->dev, NULL);
+ if (vcdev) {
+ unregister_virtio_device(&vcdev->vdev);
+ spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+ dev_set_drvdata(&cdev->dev, NULL);
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+ }
return 0;
}
@@ -759,6 +1037,7 @@ static int virtio_ccw_online(struct ccw_device *cdev)
{
int ret;
struct virtio_ccw_device *vcdev;
+ unsigned long flags;
vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL);
if (!vcdev) {
@@ -778,6 +1057,8 @@ static int virtio_ccw_online(struct ccw_device *cdev)
goto out_free;
}
+ vcdev->is_thinint = virtio_ccw_use_airq; /* at least try */
+
vcdev->vdev.dev.parent = &cdev->dev;
vcdev->vdev.dev.release = virtio_ccw_release_dev;
vcdev->vdev.config = &virtio_ccw_config_ops;
@@ -786,7 +1067,9 @@ static int virtio_ccw_online(struct ccw_device *cdev)
INIT_LIST_HEAD(&vcdev->virtqueues);
spin_lock_init(&vcdev->lock);
+ spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
dev_set_drvdata(&cdev->dev, vcdev);
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
vcdev->vdev.id.vendor = cdev->id.cu_type;
vcdev->vdev.id.device = cdev->id.cu_model;
ret = register_virtio_device(&vcdev->vdev);
@@ -797,7 +1080,9 @@ static int virtio_ccw_online(struct ccw_device *cdev)
}
return 0;
out_put:
+ spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
dev_set_drvdata(&cdev->dev, NULL);
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
put_device(&vcdev->vdev.dev);
return ret;
out_free:
@@ -935,6 +1220,10 @@ module_init(virtio_ccw_init);
static void __exit virtio_ccw_exit(void)
{
+ int i;
+
ccw_driver_unregister(&virtio_ccw_driver);
+ for (i = 0; i < MAX_AIRQ_AREAS; i++)
+ destroy_airq_info(airq_areas[i]);
}
module_exit(virtio_ccw_exit);
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index a0de045eb227..5333b2c018e7 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -854,8 +854,11 @@ static inline int qeth_get_micros(void)
static inline int qeth_get_ip_version(struct sk_buff *skb)
{
- struct ethhdr *ehdr = (struct ethhdr *)skb->data;
- switch (ehdr->h_proto) {
+ __be16 *p = &((struct ethhdr *)skb->data)->h_proto;
+
+ if (*p == ETH_P_8021Q)
+ p += 2;
+ switch (*p) {
case ETH_P_IPV6:
return 6;
case ETH_P_IP:
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index a0aff2eb247c..22470a3b182f 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -4610,8 +4610,8 @@ out:
}
EXPORT_SYMBOL_GPL(qeth_query_oat_command);
-int qeth_query_card_info_cb(struct qeth_card *card,
- struct qeth_reply *reply, unsigned long data)
+static int qeth_query_card_info_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
{
struct qeth_ipa_cmd *cmd;
struct qeth_query_card_info *card_info;
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 908d82529ee9..8dea3f12ccc1 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -241,7 +241,7 @@ static inline int qeth_l2_get_cast_type(struct qeth_card *card,
}
static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
- struct sk_buff *skb, int ipv, int cast_type)
+ struct sk_buff *skb, int cast_type)
{
struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
@@ -762,7 +762,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
goto tx_drop;
elements_needed++;
skb_reset_mac_header(new_skb);
- qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
+ qeth_l2_fill_header(card, hdr, new_skb, cast_type);
hdr->hdr.l2.pkt_length = new_skb->len;
memcpy(((char *)hdr) + sizeof(struct qeth_hdr),
skb_mac_header(new_skb), ETH_HLEN);
@@ -775,7 +775,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
hdr = (struct qeth_hdr *)skb_push(new_skb,
sizeof(struct qeth_hdr));
skb_set_mac_header(new_skb, sizeof(struct qeth_hdr));
- qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
+ qeth_l2_fill_header(card, hdr, new_skb, cast_type);
}
}
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index 6287f6a8b79d..1d41f4b9114f 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -2592,12 +2592,16 @@ static int __init bnx2fc_mod_init(void)
spin_lock_init(&p->fp_work_lock);
}
+ cpu_notifier_register_begin();
+
for_each_online_cpu(cpu) {
bnx2fc_percpu_thread_create(cpu);
}
/* Initialize per CPU interrupt thread */
- register_hotcpu_notifier(&bnx2fc_cpu_notifier);
+ __register_hotcpu_notifier(&bnx2fc_cpu_notifier);
+
+ cpu_notifier_register_done();
cnic_register_driver(CNIC_ULP_FCOE, &bnx2fc_cnic_cb);
@@ -2662,13 +2666,17 @@ static void __exit bnx2fc_mod_exit(void)
if (l2_thread)
kthread_stop(l2_thread);
- unregister_hotcpu_notifier(&bnx2fc_cpu_notifier);
+ cpu_notifier_register_begin();
/* Destroy per cpu threads */
for_each_online_cpu(cpu) {
bnx2fc_percpu_thread_destroy(cpu);
}
+ __unregister_hotcpu_notifier(&bnx2fc_cpu_notifier);
+
+ cpu_notifier_register_done();
+
destroy_workqueue(bnx2fc_wq);
/*
* detach from scsi transport
diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c
index 34c294b42c84..80c03b452d61 100644
--- a/drivers/scsi/bnx2i/bnx2i_init.c
+++ b/drivers/scsi/bnx2i/bnx2i_init.c
@@ -537,11 +537,15 @@ static int __init bnx2i_mod_init(void)
p->iothread = NULL;
}
+ cpu_notifier_register_begin();
+
for_each_online_cpu(cpu)
bnx2i_percpu_thread_create(cpu);
/* Initialize per CPU interrupt thread */
- register_hotcpu_notifier(&bnx2i_cpu_notifier);
+ __register_hotcpu_notifier(&bnx2i_cpu_notifier);
+
+ cpu_notifier_register_done();
return 0;
@@ -581,11 +585,15 @@ static void __exit bnx2i_mod_exit(void)
}
mutex_unlock(&bnx2i_dev_lock);
- unregister_hotcpu_notifier(&bnx2i_cpu_notifier);
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
bnx2i_percpu_thread_destroy(cpu);
+ __unregister_hotcpu_notifier(&bnx2i_cpu_notifier);
+
+ cpu_notifier_register_done();
+
iscsi_unregister_transport(&bnx2i_iscsi_transport);
cnic_unregister_driver(CNIC_ULP_ISCSI);
}
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index f3170008ae71..d5e105b173f0 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -2633,14 +2633,18 @@ static int __init fcoe_init(void)
skb_queue_head_init(&p->fcoe_rx_list);
}
+ cpu_notifier_register_begin();
+
for_each_online_cpu(cpu)
fcoe_percpu_thread_create(cpu);
/* Initialize per CPU interrupt thread */
- rc = register_hotcpu_notifier(&fcoe_cpu_notifier);
+ rc = __register_hotcpu_notifier(&fcoe_cpu_notifier);
if (rc)
goto out_free;
+ cpu_notifier_register_done();
+
/* Setup link change notification */
fcoe_dev_setup();
@@ -2655,6 +2659,9 @@ out_free:
for_each_online_cpu(cpu) {
fcoe_percpu_thread_destroy(cpu);
}
+
+ cpu_notifier_register_done();
+
mutex_unlock(&fcoe_config_mutex);
destroy_workqueue(fcoe_wq);
return rc;
@@ -2687,11 +2694,15 @@ static void __exit fcoe_exit(void)
}
rtnl_unlock();
- unregister_hotcpu_notifier(&fcoe_cpu_notifier);
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
fcoe_percpu_thread_destroy(cpu);
+ __unregister_hotcpu_notifier(&fcoe_cpu_notifier);
+
+ cpu_notifier_register_done();
+
mutex_unlock(&fcoe_config_mutex);
/*
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 5b8605ca42fa..26dc005bb0f0 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -387,6 +387,10 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
if (rc)
return rc;
}
+
+ if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL)
+ task->protected = true;
+
if (sc->sc_data_direction == DMA_TO_DEVICE) {
unsigned out_len = scsi_out(sc)->length;
struct iscsi_r2t_info *r2t = &task->unsol_r2t;
@@ -822,6 +826,33 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
sc->result = (DID_OK << 16) | rhdr->cmd_status;
+ if (task->protected) {
+ sector_t sector;
+ u8 ascq;
+
+ /**
+ * Transports that didn't implement check_protection
+ * callback but still published T10-PI support to scsi-mid
+ * deserve this BUG_ON.
+ **/
+ BUG_ON(!session->tt->check_protection);
+
+ ascq = session->tt->check_protection(task, &sector);
+ if (ascq) {
+ sc->result = DRIVER_SENSE << 24 |
+ SAM_STAT_CHECK_CONDITION;
+ scsi_build_sense_buffer(1, sc->sense_buffer,
+ ILLEGAL_REQUEST, 0x10, ascq);
+ sc->sense_buffer[7] = 0xc; /* Additional sense length */
+ sc->sense_buffer[8] = 0; /* Information desc type */
+ sc->sense_buffer[9] = 0xa; /* Additional desc length */
+ sc->sense_buffer[10] = 0x80; /* Validity bit */
+
+ put_unaligned_be64(sector, &sc->sense_buffer[12]);
+ goto out;
+ }
+ }
+
if (rhdr->response != ISCSI_STATUS_CMD_COMPLETED) {
sc->result = DID_ERROR << 16;
goto out;
@@ -1582,6 +1613,7 @@ static inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn,
task->have_checked_conn = false;
task->last_timeout = jiffies;
task->last_xfer = jiffies;
+ task->protected = false;
INIT_LIST_HEAD(&task->running);
return task;
}
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index fd8ffe6bcfdd..0102a2d70dd8 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -1225,7 +1225,7 @@ struct bus_type iscsi_flashnode_bus = {
* Adds a sysfs entry for the flashnode session attributes
*
* Returns:
- * pointer to allocated flashnode sess on sucess
+ * pointer to allocated flashnode sess on success
* %NULL on failure
*/
struct iscsi_bus_flash_session *
@@ -1423,7 +1423,7 @@ static int iscsi_iter_destroy_flashnode_conn_fn(struct device *dev, void *data)
}
/**
- * iscsi_destroy_flashnode_sess - destory flashnode session entry
+ * iscsi_destroy_flashnode_sess - destroy flashnode session entry
* @fnode_sess: pointer to flashnode session entry to be destroyed
*
* Deletes the flashnode session entry and all children flashnode connection
@@ -1453,7 +1453,7 @@ static int iscsi_iter_destroy_flashnode_fn(struct device *dev, void *data)
}
/**
- * iscsi_destroy_all_flashnode - destory all flashnode session entries
+ * iscsi_destroy_all_flashnode - destroy all flashnode session entries
* @shost: pointer to host data
*
* Destroys all the flashnode session entries and all corresponding children
diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c
index d47ffc8d3e43..13e898332e45 100644
--- a/drivers/scsi/scsi_transport_srp.c
+++ b/drivers/scsi/scsi_transport_srp.c
@@ -810,6 +810,7 @@ EXPORT_SYMBOL_GPL(srp_remove_host);
/**
* srp_stop_rport_timers - stop the transport layer recovery timers
+ * @rport: SRP remote port for which to stop the timers.
*
* Must be called after srp_remove_host() and scsi_remove_host(). The caller
* must hold a reference on the rport (rport->dev) and on the SCSI host
diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c
index 1ebe67cd1833..7442bc130055 100644
--- a/drivers/sh/clk/cpg.c
+++ b/drivers/sh/clk/cpg.c
@@ -36,9 +36,47 @@ static void sh_clk_write(int value, struct clk *clk)
iowrite32(value, clk->mapped_reg);
}
+static unsigned int r8(const void __iomem *addr)
+{
+ return ioread8(addr);
+}
+
+static unsigned int r16(const void __iomem *addr)
+{
+ return ioread16(addr);
+}
+
+static unsigned int r32(const void __iomem *addr)
+{
+ return ioread32(addr);
+}
+
static int sh_clk_mstp_enable(struct clk *clk)
{
sh_clk_write(sh_clk_read(clk) & ~(1 << clk->enable_bit), clk);
+ if (clk->status_reg) {
+ unsigned int (*read)(const void __iomem *addr);
+ int i;
+ void __iomem *mapped_status = (phys_addr_t)clk->status_reg -
+ (phys_addr_t)clk->enable_reg + clk->mapped_reg;
+
+ if (clk->flags & CLK_ENABLE_REG_8BIT)
+ read = r8;
+ else if (clk->flags & CLK_ENABLE_REG_16BIT)
+ read = r16;
+ else
+ read = r32;
+
+ for (i = 1000;
+ (read(mapped_status) & (1 << clk->enable_bit)) && i;
+ i--)
+ cpu_relax();
+ if (!i) {
+ pr_err("cpg: failed to enable %p[%d]\n",
+ clk->enable_reg, clk->enable_bit);
+ return -ETIMEDOUT;
+ }
+ }
return 0;
}
diff --git a/drivers/sh/intc/Kconfig b/drivers/sh/intc/Kconfig
index a305731742a9..f7d90617c9d9 100644
--- a/drivers/sh/intc/Kconfig
+++ b/drivers/sh/intc/Kconfig
@@ -6,7 +6,7 @@ comment "Interrupt controller options"
config INTC_USERIMASK
bool "Userspace interrupt masking support"
- depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A)
+ depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A) || COMPILE_TEST
help
This enables support for hardware-assisted userspace hardirq
masking.
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index ab28d2b5e308..99e484f845f2 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -2,7 +2,6 @@ menu "Android"
config ANDROID
bool "Android Drivers"
- default N
---help---
Enable support for various drivers needed on the Android platform
@@ -73,7 +72,6 @@ config ANDROID_TIMED_GPIO
config ANDROID_LOW_MEMORY_KILLER
bool "Android Low Memory Killer"
- default N
---help---
Registers processes to be killed when memory is low
diff --git a/drivers/staging/bcm/Kconfig b/drivers/staging/bcm/Kconfig
index 83c9752504d4..8acf4b24a7c9 100644
--- a/drivers/staging/bcm/Kconfig
+++ b/drivers/staging/bcm/Kconfig
@@ -1,7 +1,6 @@
config BCM_WIMAX
tristate "Beceem BCS200/BCS220-3 and BCSM250 wimax support"
depends on USB && NET
- default N
help
This is an experimental driver for the Beceem WIMAX chipset used
by Sprint 4G.
diff --git a/drivers/staging/frontier/Kconfig b/drivers/staging/frontier/Kconfig
index 7121853bd397..4da290b2f5bd 100644
--- a/drivers/staging/frontier/Kconfig
+++ b/drivers/staging/frontier/Kconfig
@@ -1,6 +1,5 @@
config TRANZPORT
tristate "Frontier Tranzport and Alphatrack support"
depends on USB
- default N
---help---
Enable support for the Frontier Tranzport and Alphatrack devices.
diff --git a/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h b/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h
index eefdb8d061b1..81cc7a0134bb 100644
--- a/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h
+++ b/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h
@@ -105,8 +105,8 @@ static inline void ll_set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt,
#define ll_vfs_unlink(inode,entry,mnt) vfs_unlink(inode,entry)
#define ll_vfs_mknod(dir,entry,mnt,mode,dev) vfs_mknod(dir,entry,mode,dev)
#define ll_security_inode_unlink(dir,entry,mnt) security_inode_unlink(dir,entry)
-#define ll_vfs_rename(old,old_dir,mnt,new,new_dir,mnt1,delegated_inode) \
- vfs_rename(old,old_dir,new,new_dir,delegated_inode)
+#define ll_vfs_rename(old, old_dir, mnt, new, new_dir, mnt1) \
+ vfs_rename(old, old_dir, new, new_dir, NULL, 0)
#define cfs_bio_io_error(a,b) bio_io_error((a))
#define cfs_bio_endio(a,b,c) bio_endio((a),(c))
diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c
index 26003d3c1be7..7c4fd97a7fa0 100644
--- a/drivers/staging/lustre/lustre/llite/llite_lib.c
+++ b/drivers/staging/lustre/lustre/llite/llite_lib.c
@@ -1877,7 +1877,7 @@ void ll_delete_inode(struct inode *inode)
cl_sync_file_range(inode, 0, OBD_OBJECT_EOF,
CL_FSYNC_DISCARD, 1);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
/* Workaround for LU-118 */
if (inode->i_data.nrpages) {
diff --git a/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c b/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c
index e44b7a532de7..374a9b78e1d2 100644
--- a/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c
+++ b/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c
@@ -223,7 +223,7 @@ int lustre_rename(struct dentry *dir, struct vfsmount *mnt,
GOTO(put_old, err = PTR_ERR(dchild_new));
err = ll_vfs_rename(dir->d_inode, dchild_old, mnt,
- dir->d_inode, dchild_new, mnt, NULL);
+ dir->d_inode, dchild_new, mnt);
dput(dchild_new);
put_old:
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index 22b0c9d6f046..a9f2e63a7c9c 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -41,6 +41,8 @@ source "drivers/staging/media/solo6x10/Kconfig"
source "drivers/staging/media/omap4iss/Kconfig"
+source "drivers/staging/media/rtl2832u_sdr/Kconfig"
+
# Keep LIRC at the end, as it has sub-menus
source "drivers/staging/media/lirc/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index bedc62aaede6..8e2c5d272162 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -11,3 +11,5 @@ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
obj-$(CONFIG_USB_SN9C102) += sn9c102/
obj-$(CONFIG_VIDEO_OMAP2) += omap24xx/
obj-$(CONFIG_VIDEO_TCM825X) += omap24xx/
+obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832u_sdr/
+
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
index 2d36b60bdbf1..b2daf5e63f88 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
@@ -267,7 +267,7 @@ int config_ipipe_hw(struct vpfe_ipipe_device *ipipe)
}
ipipe_mode = get_ipipe_mode(ipipe);
- if (ipipe < 0) {
+ if (ipipe_mode < 0) {
pr_err("Failed to get ipipe mode");
return -EINVAL;
}
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
index d8ce20d2fbda..cda8388cbb89 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
@@ -298,7 +298,7 @@ static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
{
int ret = 0;
- ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED,
+ ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, 0,
"vpfe_capture0", vpfe_dev);
if (ret < 0) {
v4l2_err(&vpfe_dev->v4l2_dev,
@@ -306,7 +306,7 @@ static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
return ret;
}
- ret = request_irq(vpfe_dev->ccdc_irq1, vpfe_vdint1_isr, IRQF_DISABLED,
+ ret = request_irq(vpfe_dev->ccdc_irq1, vpfe_vdint1_isr, 0,
"vpfe_capture1", vpfe_dev);
if (ret < 0) {
v4l2_err(&vpfe_dev->v4l2_dev,
@@ -316,7 +316,7 @@ static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
}
ret = request_irq(vpfe_dev->imp_dma_irq, vpfe_imp_dma_isr,
- IRQF_DISABLED, "Imp_Sdram_Irq", vpfe_dev);
+ 0, "Imp_Sdram_Irq", vpfe_dev);
if (ret < 0) {
v4l2_err(&vpfe_dev->v4l2_dev,
"Error: requesting IMP IRQ interrupt\n");
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
index 1f3b0f9a8d10..8c101cbbee97 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -1201,8 +1201,6 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
unsigned long addr;
int ret;
- if (count == 0)
- return -ENOBUFS;
ret = mutex_lock_interruptible(&video->lock);
if (ret)
goto streamoff;
@@ -1327,6 +1325,7 @@ static int vpfe_reqbufs(struct file *file, void *priv,
q->type = req_buf->type;
q->io_modes = VB2_MMAP | VB2_USERPTR;
q->drv_priv = fh;
+ q->min_buffers_needed = 1;
q->ops = &video_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct vpfe_cap_buffer);
diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c
index 97e7a9b48ac2..afbc2e519606 100644
--- a/drivers/staging/media/dt3155v4l/dt3155v4l.c
+++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c
@@ -31,7 +31,6 @@
#include "dt3155v4l.h"
-#define DT3155_VENDOR_ID 0x8086
#define DT3155_DEVICE_ID 0x1223
/* DT3155_CHUNK_SIZE is 4M (2^22) 8 full size buffers */
@@ -391,7 +390,7 @@ dt3155_open(struct file *filp)
goto err_alloc_queue;
}
pd->q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- pd->q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ pd->q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
pd->q->io_modes = VB2_READ | VB2_MMAP;
pd->q->ops = &q_ops;
pd->q->mem_ops = &vb2_dma_contig_memops;
@@ -975,7 +974,7 @@ dt3155_remove(struct pci_dev *pdev)
}
static const struct pci_device_id pci_ids[] = {
- { PCI_DEVICE(DT3155_VENDOR_ID, DT3155_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, DT3155_DEVICE_ID) },
{ 0, /* zero marks the end */ },
};
MODULE_DEVICE_TABLE(pci, pci_ids);
diff --git a/drivers/staging/media/go7007/Kconfig b/drivers/staging/media/go7007/Kconfig
index 04bd0fba7b1f..95a3af644a92 100644
--- a/drivers/staging/media/go7007/Kconfig
+++ b/drivers/staging/media/go7007/Kconfig
@@ -13,7 +13,6 @@ config VIDEO_GO7007
select VIDEO_TW9906 if MEDIA_SUBDRV_AUTOSELECT
select VIDEO_OV7640 if MEDIA_SUBDRV_AUTOSELECT
select VIDEO_UDA1342 if MEDIA_SUBDRV_AUTOSELECT
- default N
---help---
This is a video4linux driver for the WIS GO7007 MPEG
encoder chip.
@@ -24,7 +23,6 @@ config VIDEO_GO7007
config VIDEO_GO7007_USB
tristate "WIS GO7007 USB support"
depends on VIDEO_GO7007 && USB
- default N
---help---
This is a video4linux driver for the WIS GO7007 MPEG
encoder chip over USB.
@@ -46,7 +44,6 @@ config VIDEO_GO7007_LOADER
config VIDEO_GO7007_USB_S2250_BOARD
tristate "Sensoray 2250/2251 support"
depends on VIDEO_GO7007_USB && USB
- default N
---help---
This is a video4linux driver for the Sensoray 2250/2251 device.
diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c
index bdf414e19c8f..b397aa3c0f44 100644
--- a/drivers/staging/media/go7007/go7007-v4l2.c
+++ b/drivers/staging/media/go7007/go7007-v4l2.c
@@ -471,7 +471,7 @@ static int go7007_buf_prepare(struct vb2_buffer *vb)
return 0;
}
-static int go7007_buf_finish(struct vb2_buffer *vb)
+static void go7007_buf_finish(struct vb2_buffer *vb)
{
struct vb2_queue *vq = vb->vb2_queue;
struct go7007 *go = vb2_get_drv_priv(vq);
@@ -484,7 +484,6 @@ static int go7007_buf_finish(struct vb2_buffer *vb)
V4L2_BUF_FLAG_PFRAME);
buf->flags |= frame_type_flag;
buf->field = V4L2_FIELD_NONE;
- return 0;
}
static int go7007_start_streaming(struct vb2_queue *q, unsigned int count)
@@ -995,7 +994,7 @@ int go7007_v4l2_init(struct go7007 *go)
go->vidq.mem_ops = &vb2_vmalloc_memops;
go->vidq.drv_priv = go;
go->vidq.buf_struct_size = sizeof(struct go7007_buffer);
- go->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ go->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
go->vidq.lock = &go->queue_lock;
rv = vb2_queue_init(&go->vidq);
if (rv)
diff --git a/drivers/staging/media/msi3101/Kconfig b/drivers/staging/media/msi3101/Kconfig
index 0c349c8595e4..de0b3bba3873 100644
--- a/drivers/staging/media/msi3101/Kconfig
+++ b/drivers/staging/media/msi3101/Kconfig
@@ -1,5 +1,10 @@
config USB_MSI3101
tristate "Mirics MSi3101 SDR Dongle"
- depends on USB && VIDEO_DEV && VIDEO_V4L2
+ depends on USB && VIDEO_DEV && VIDEO_V4L2 && SPI
select VIDEOBUF2_CORE
select VIDEOBUF2_VMALLOC
+ select MEDIA_TUNER_MSI001
+
+config MEDIA_TUNER_MSI001
+ tristate "Mirics MSi001"
+ depends on VIDEO_V4L2 && SPI
diff --git a/drivers/staging/media/msi3101/Makefile b/drivers/staging/media/msi3101/Makefile
index 3730654b0eb9..daf4f58d9a56 100644
--- a/drivers/staging/media/msi3101/Makefile
+++ b/drivers/staging/media/msi3101/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_USB_MSI3101) += sdr-msi3101.o
+obj-$(CONFIG_MEDIA_TUNER_MSI001) += msi001.o
diff --git a/drivers/staging/media/msi3101/msi001.c b/drivers/staging/media/msi3101/msi001.c
new file mode 100644
index 000000000000..ac43bae10102
--- /dev/null
+++ b/drivers/staging/media/msi3101/msi001.c
@@ -0,0 +1,500 @@
+/*
+ * Mirics MSi001 silicon tuner driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/gcd.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+static const struct v4l2_frequency_band bands[] = {
+ {
+ .type = V4L2_TUNER_RF,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 49000000,
+ .rangehigh = 263000000,
+ }, {
+ .type = V4L2_TUNER_RF,
+ .index = 1,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 390000000,
+ .rangehigh = 960000000,
+ },
+};
+
+struct msi001 {
+ struct spi_device *spi;
+ struct v4l2_subdev sd;
+
+ /* Controls */
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *bandwidth_auto;
+ struct v4l2_ctrl *bandwidth;
+ struct v4l2_ctrl *lna_gain;
+ struct v4l2_ctrl *mixer_gain;
+ struct v4l2_ctrl *if_gain;
+
+ unsigned int f_tuner;
+};
+
+static inline struct msi001 *sd_to_msi001(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct msi001, sd);
+}
+
+static int msi001_wreg(struct msi001 *s, u32 data)
+{
+ /* Register format: 4 bits addr + 20 bits value */
+ return spi_write(s->spi, &data, 3);
+};
+
+static int msi001_set_gain(struct msi001 *s, int lna_gain, int mixer_gain,
+ int if_gain)
+{
+ int ret;
+ u32 reg;
+ dev_dbg(&s->spi->dev, "%s: lna=%d mixer=%d if=%d\n", __func__,
+ lna_gain, mixer_gain, if_gain);
+
+ reg = 1 << 0;
+ reg |= (59 - if_gain) << 4;
+ reg |= 0 << 10;
+ reg |= (1 - mixer_gain) << 12;
+ reg |= (1 - lna_gain) << 13;
+ reg |= 4 << 14;
+ reg |= 0 << 17;
+ ret = msi001_wreg(s, reg);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret);
+ return ret;
+};
+
+static int msi001_set_tuner(struct msi001 *s)
+{
+ int ret, i;
+ unsigned int n, m, thresh, frac, vco_step, tmp, f_if1;
+ u32 reg;
+ u64 f_vco, tmp64;
+ u8 mode, filter_mode, lo_div;
+ static const struct {
+ u32 rf;
+ u8 mode;
+ u8 lo_div;
+ } band_lut[] = {
+ { 50000000, 0xe1, 16}, /* AM_MODE2, antenna 2 */
+ {108000000, 0x42, 32}, /* VHF_MODE */
+ {330000000, 0x44, 16}, /* B3_MODE */
+ {960000000, 0x48, 4}, /* B45_MODE */
+ { ~0U, 0x50, 2}, /* BL_MODE */
+ };
+ static const struct {
+ u32 freq;
+ u8 filter_mode;
+ } if_freq_lut[] = {
+ { 0, 0x03}, /* Zero IF */
+ { 450000, 0x02}, /* 450 kHz IF */
+ {1620000, 0x01}, /* 1.62 MHz IF */
+ {2048000, 0x00}, /* 2.048 MHz IF */
+ };
+ static const struct {
+ u32 freq;
+ u8 val;
+ } bandwidth_lut[] = {
+ { 200000, 0x00}, /* 200 kHz */
+ { 300000, 0x01}, /* 300 kHz */
+ { 600000, 0x02}, /* 600 kHz */
+ {1536000, 0x03}, /* 1.536 MHz */
+ {5000000, 0x04}, /* 5 MHz */
+ {6000000, 0x05}, /* 6 MHz */
+ {7000000, 0x06}, /* 7 MHz */
+ {8000000, 0x07}, /* 8 MHz */
+ };
+
+ unsigned int f_rf = s->f_tuner;
+
+ /*
+ * bandwidth (Hz)
+ * 200000, 300000, 600000, 1536000, 5000000, 6000000, 7000000, 8000000
+ */
+ unsigned int bandwidth;
+
+ /*
+ * intermediate frequency (Hz)
+ * 0, 450000, 1620000, 2048000
+ */
+ unsigned int f_if = 0;
+ #define F_REF 24000000
+ #define R_REF 4
+ #define F_OUT_STEP 1
+
+ dev_dbg(&s->spi->dev,
+ "%s: f_rf=%d f_if=%d\n",
+ __func__, f_rf, f_if);
+
+ for (i = 0; i < ARRAY_SIZE(band_lut); i++) {
+ if (f_rf <= band_lut[i].rf) {
+ mode = band_lut[i].mode;
+ lo_div = band_lut[i].lo_div;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(band_lut)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* AM_MODE is upconverted */
+ if ((mode >> 0) & 0x1)
+ f_if1 = 5 * F_REF;
+ else
+ f_if1 = 0;
+
+ for (i = 0; i < ARRAY_SIZE(if_freq_lut); i++) {
+ if (f_if == if_freq_lut[i].freq) {
+ filter_mode = if_freq_lut[i].filter_mode;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(if_freq_lut)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* filters */
+ bandwidth = s->bandwidth->val;
+ bandwidth = clamp(bandwidth, 200000U, 8000000U);
+
+ for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) {
+ if (bandwidth <= bandwidth_lut[i].freq) {
+ bandwidth = bandwidth_lut[i].val;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(bandwidth_lut)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ s->bandwidth->val = bandwidth_lut[i].freq;
+
+ dev_dbg(&s->spi->dev, "%s: bandwidth selected=%d\n",
+ __func__, bandwidth_lut[i].freq);
+
+ f_vco = (f_rf + f_if + f_if1) * lo_div;
+ tmp64 = f_vco;
+ m = do_div(tmp64, F_REF * R_REF);
+ n = (unsigned int) tmp64;
+
+ vco_step = F_OUT_STEP * lo_div;
+ thresh = (F_REF * R_REF) / vco_step;
+ frac = 1ul * thresh * m / (F_REF * R_REF);
+
+ /* Find out greatest common divisor and divide to smaller. */
+ tmp = gcd(thresh, frac);
+ thresh /= tmp;
+ frac /= tmp;
+
+ /* Force divide to reg max. Resolution will be reduced. */
+ tmp = DIV_ROUND_UP(thresh, 4095);
+ thresh = DIV_ROUND_CLOSEST(thresh, tmp);
+ frac = DIV_ROUND_CLOSEST(frac, tmp);
+
+ /* calc real RF set */
+ tmp = 1ul * F_REF * R_REF * n;
+ tmp += 1ul * F_REF * R_REF * frac / thresh;
+ tmp /= lo_div;
+
+ dev_dbg(&s->spi->dev,
+ "%s: rf=%u:%u n=%d thresh=%d frac=%d\n",
+ __func__, f_rf, tmp, n, thresh, frac);
+
+ ret = msi001_wreg(s, 0x00000e);
+ if (ret)
+ goto err;
+
+ ret = msi001_wreg(s, 0x000003);
+ if (ret)
+ goto err;
+
+ reg = 0 << 0;
+ reg |= mode << 4;
+ reg |= filter_mode << 12;
+ reg |= bandwidth << 14;
+ reg |= 0x02 << 17;
+ reg |= 0x00 << 20;
+ ret = msi001_wreg(s, reg);
+ if (ret)
+ goto err;
+
+ reg = 5 << 0;
+ reg |= thresh << 4;
+ reg |= 1 << 19;
+ reg |= 1 << 21;
+ ret = msi001_wreg(s, reg);
+ if (ret)
+ goto err;
+
+ reg = 2 << 0;
+ reg |= frac << 4;
+ reg |= n << 16;
+ ret = msi001_wreg(s, reg);
+ if (ret)
+ goto err;
+
+ ret = msi001_set_gain(s, s->lna_gain->cur.val, s->mixer_gain->cur.val,
+ s->if_gain->cur.val);
+ if (ret)
+ goto err;
+
+ reg = 6 << 0;
+ reg |= 63 << 4;
+ reg |= 4095 << 10;
+ ret = msi001_wreg(s, reg);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret);
+ return ret;
+};
+
+static int msi001_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct msi001 *s = sd_to_msi001(sd);
+ int ret;
+ dev_dbg(&s->spi->dev, "%s: on=%d\n", __func__, on);
+
+ if (on)
+ ret = 0;
+ else
+ ret = msi001_wreg(s, 0x000000);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_core_ops msi001_core_ops = {
+ .s_power = msi001_s_power,
+};
+
+static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
+{
+ struct msi001 *s = sd_to_msi001(sd);
+ dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index);
+
+ strlcpy(v->name, "Mirics MSi001", sizeof(v->name));
+ v->type = V4L2_TUNER_RF;
+ v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+ v->rangelow = 49000000;
+ v->rangehigh = 960000000;
+
+ return 0;
+}
+
+static int msi001_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v)
+{
+ struct msi001 *s = sd_to_msi001(sd);
+ dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index);
+ return 0;
+}
+
+static int msi001_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+ struct msi001 *s = sd_to_msi001(sd);
+ dev_dbg(&s->spi->dev, "%s: tuner=%d\n", __func__, f->tuner);
+ f->frequency = s->f_tuner;
+ return 0;
+}
+
+static int msi001_s_frequency(struct v4l2_subdev *sd,
+ const struct v4l2_frequency *f)
+{
+ struct msi001 *s = sd_to_msi001(sd);
+ unsigned int band;
+ dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d frequency=%u\n",
+ __func__, f->tuner, f->type, f->frequency);
+
+ if (f->frequency < ((bands[0].rangehigh + bands[1].rangelow) / 2))
+ band = 0;
+ else
+ band = 1;
+ s->f_tuner = clamp_t(unsigned int, f->frequency,
+ bands[band].rangelow, bands[band].rangehigh);
+
+ return msi001_set_tuner(s);
+}
+
+static int msi001_enum_freq_bands(struct v4l2_subdev *sd,
+ struct v4l2_frequency_band *band)
+{
+ struct msi001 *s = sd_to_msi001(sd);
+ dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d index=%d\n",
+ __func__, band->tuner, band->type, band->index);
+
+ if (band->index >= ARRAY_SIZE(bands))
+ return -EINVAL;
+
+ band->capability = bands[band->index].capability;
+ band->rangelow = bands[band->index].rangelow;
+ band->rangehigh = bands[band->index].rangehigh;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_tuner_ops msi001_tuner_ops = {
+ .g_tuner = msi001_g_tuner,
+ .s_tuner = msi001_s_tuner,
+ .g_frequency = msi001_g_frequency,
+ .s_frequency = msi001_s_frequency,
+ .enum_freq_bands = msi001_enum_freq_bands,
+};
+
+static const struct v4l2_subdev_ops msi001_ops = {
+ .core = &msi001_core_ops,
+ .tuner = &msi001_tuner_ops,
+};
+
+static int msi001_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct msi001 *s = container_of(ctrl->handler, struct msi001, hdl);
+
+ int ret;
+ dev_dbg(&s->spi->dev,
+ "%s: id=%d name=%s val=%d min=%d max=%d step=%d\n",
+ __func__, ctrl->id, ctrl->name, ctrl->val,
+ ctrl->minimum, ctrl->maximum, ctrl->step);
+
+ switch (ctrl->id) {
+ case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
+ case V4L2_CID_RF_TUNER_BANDWIDTH:
+ ret = msi001_set_tuner(s);
+ break;
+ case V4L2_CID_RF_TUNER_LNA_GAIN:
+ ret = msi001_set_gain(s, s->lna_gain->val,
+ s->mixer_gain->cur.val, s->if_gain->cur.val);
+ break;
+ case V4L2_CID_RF_TUNER_MIXER_GAIN:
+ ret = msi001_set_gain(s, s->lna_gain->cur.val,
+ s->mixer_gain->val, s->if_gain->cur.val);
+ break;
+ case V4L2_CID_RF_TUNER_IF_GAIN:
+ ret = msi001_set_gain(s, s->lna_gain->cur.val,
+ s->mixer_gain->cur.val, s->if_gain->val);
+ break;
+ default:
+ dev_dbg(&s->spi->dev, "%s: unkown control %d\n",
+ __func__, ctrl->id);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops msi001_ctrl_ops = {
+ .s_ctrl = msi001_s_ctrl,
+};
+
+static int msi001_probe(struct spi_device *spi)
+{
+ struct msi001 *s;
+ int ret;
+ dev_dbg(&spi->dev, "%s:\n", __func__);
+
+ s = kzalloc(sizeof(struct msi001), GFP_KERNEL);
+ if (s == NULL) {
+ ret = -ENOMEM;
+ dev_dbg(&spi->dev, "Could not allocate memory for msi001\n");
+ goto err_kfree;
+ }
+
+ s->spi = spi;
+ s->f_tuner = bands[0].rangelow;
+ v4l2_spi_subdev_init(&s->sd, spi, &msi001_ops);
+
+ /* Register controls */
+ v4l2_ctrl_handler_init(&s->hdl, 5);
+ s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops,
+ V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
+ s->bandwidth = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops,
+ V4L2_CID_RF_TUNER_BANDWIDTH, 200000, 8000000, 1, 200000);
+ v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+ s->lna_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops,
+ V4L2_CID_RF_TUNER_LNA_GAIN, 0, 1, 1, 1);
+ s->mixer_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops,
+ V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1);
+ s->if_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops,
+ V4L2_CID_RF_TUNER_IF_GAIN, 0, 59, 1, 0);
+ if (s->hdl.error) {
+ ret = s->hdl.error;
+ dev_err(&s->spi->dev, "Could not initialize controls\n");
+ /* control init failed, free handler */
+ goto err_ctrl_handler_free;
+ }
+
+ s->sd.ctrl_handler = &s->hdl;
+ return 0;
+
+err_ctrl_handler_free:
+ v4l2_ctrl_handler_free(&s->hdl);
+err_kfree:
+ kfree(s);
+ return ret;
+}
+
+static int msi001_remove(struct spi_device *spi)
+{
+ struct v4l2_subdev *sd = spi_get_drvdata(spi);
+ struct msi001 *s = sd_to_msi001(sd);
+ dev_dbg(&spi->dev, "%s:\n", __func__);
+
+ /*
+ * Registered by v4l2_spi_new_subdev() from master driver, but we must
+ * unregister it from here. Weird.
+ */
+ v4l2_device_unregister_subdev(&s->sd);
+ v4l2_ctrl_handler_free(&s->hdl);
+ kfree(s);
+ return 0;
+}
+
+static const struct spi_device_id msi001_id[] = {
+ {"msi001", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, msi001_id);
+
+static struct spi_driver msi001_driver = {
+ .driver = {
+ .name = "msi001",
+ .owner = THIS_MODULE,
+ },
+ .probe = msi001_probe,
+ .remove = msi001_remove,
+ .id_table = msi001_id,
+};
+module_spi_driver(msi001_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Mirics MSi001");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c
index 5a0400fdb98c..260d1b736721 100644
--- a/drivers/staging/media/msi3101/sdr-msi3101.c
+++ b/drivers/staging/media/msi3101/sdr-msi3101.c
@@ -21,25 +21,10 @@
* (C) 1999-2004 Nemosoft Unv.
* (C) 2004-2006 Luc Saillard (luc@saillard.org)
* (C) 2011 Hans de Goede <hdegoede@redhat.com>
- *
- * Development tree of that driver will be on:
- * http://git.linuxtv.org/anttip/media_tree.git/shortlog/refs/heads/mirics
- *
- * GNU Radio plugin "gr-kernel" for device usage will be on:
- * http://git.linuxtv.org/anttip/gr-kernel.git
- *
- * TODO:
- * Help is very highly welcome for these + all the others you could imagine:
- * - split USB ADC interface and RF tuner to own drivers (msi2500 and msi001)
- * - move controls to V4L2 API
- * - use libv4l2 for stream format conversions
- * - gr-kernel: switch to v4l2_mmap (current read eats a lot of cpu)
- * - SDRSharp support
*/
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/gcd.h>
#include <asm/div64.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -47,317 +32,7 @@
#include <media/v4l2-event.h>
#include <linux/usb.h>
#include <media/videobuf2-vmalloc.h>
-
-struct msi3101_gain {
- u8 tot:7;
- u8 baseband:6;
- bool lna:1;
- bool mixer:1;
-};
-
-/* 60 – 120 MHz band, lna 24dB, mixer 19dB */
-static const struct msi3101_gain msi3101_gain_lut_120[] = {
- { 0, 0, 0, 0},
- { 1, 1, 0, 0},
- { 2, 2, 0, 0},
- { 3, 3, 0, 0},
- { 4, 4, 0, 0},
- { 5, 5, 0, 0},
- { 6, 6, 0, 0},
- { 7, 7, 0, 0},
- { 8, 8, 0, 0},
- { 9, 9, 0, 0},
- { 10, 10, 0, 0},
- { 11, 11, 0, 0},
- { 12, 12, 0, 0},
- { 13, 13, 0, 0},
- { 14, 14, 0, 0},
- { 15, 15, 0, 0},
- { 16, 16, 0, 0},
- { 17, 17, 0, 0},
- { 18, 18, 0, 0},
- { 19, 19, 0, 0},
- { 20, 20, 0, 0},
- { 21, 21, 0, 0},
- { 22, 22, 0, 0},
- { 23, 23, 0, 0},
- { 24, 24, 0, 0},
- { 25, 25, 0, 0},
- { 26, 26, 0, 0},
- { 27, 27, 0, 0},
- { 28, 28, 0, 0},
- { 29, 5, 1, 0},
- { 30, 6, 1, 0},
- { 31, 7, 1, 0},
- { 32, 8, 1, 0},
- { 33, 9, 1, 0},
- { 34, 10, 1, 0},
- { 35, 11, 1, 0},
- { 36, 12, 1, 0},
- { 37, 13, 1, 0},
- { 38, 14, 1, 0},
- { 39, 15, 1, 0},
- { 40, 16, 1, 0},
- { 41, 17, 1, 0},
- { 42, 18, 1, 0},
- { 43, 19, 1, 0},
- { 44, 20, 1, 0},
- { 45, 21, 1, 0},
- { 46, 22, 1, 0},
- { 47, 23, 1, 0},
- { 48, 24, 1, 0},
- { 49, 25, 1, 0},
- { 50, 26, 1, 0},
- { 51, 27, 1, 0},
- { 52, 28, 1, 0},
- { 53, 29, 1, 0},
- { 54, 30, 1, 0},
- { 55, 31, 1, 0},
- { 56, 32, 1, 0},
- { 57, 33, 1, 0},
- { 58, 34, 1, 0},
- { 59, 35, 1, 0},
- { 60, 36, 1, 0},
- { 61, 37, 1, 0},
- { 62, 38, 1, 0},
- { 63, 39, 1, 0},
- { 64, 40, 1, 0},
- { 65, 41, 1, 0},
- { 66, 42, 1, 0},
- { 67, 43, 1, 0},
- { 68, 44, 1, 0},
- { 69, 45, 1, 0},
- { 70, 46, 1, 0},
- { 71, 47, 1, 0},
- { 72, 48, 1, 0},
- { 73, 49, 1, 0},
- { 74, 50, 1, 0},
- { 75, 51, 1, 0},
- { 76, 52, 1, 0},
- { 77, 53, 1, 0},
- { 78, 54, 1, 0},
- { 79, 55, 1, 0},
- { 80, 56, 1, 0},
- { 81, 57, 1, 0},
- { 82, 58, 1, 0},
- { 83, 40, 1, 1},
- { 84, 41, 1, 1},
- { 85, 42, 1, 1},
- { 86, 43, 1, 1},
- { 87, 44, 1, 1},
- { 88, 45, 1, 1},
- { 89, 46, 1, 1},
- { 90, 47, 1, 1},
- { 91, 48, 1, 1},
- { 92, 49, 1, 1},
- { 93, 50, 1, 1},
- { 94, 51, 1, 1},
- { 95, 52, 1, 1},
- { 96, 53, 1, 1},
- { 97, 54, 1, 1},
- { 98, 55, 1, 1},
- { 99, 56, 1, 1},
- {100, 57, 1, 1},
- {101, 58, 1, 1},
- {102, 59, 1, 1},
-};
-
-/* 120 – 245 MHz band, lna 24dB, mixer 19dB */
-static const struct msi3101_gain msi3101_gain_lut_245[] = {
- { 0, 0, 0, 0},
- { 1, 1, 0, 0},
- { 2, 2, 0, 0},
- { 3, 3, 0, 0},
- { 4, 4, 0, 0},
- { 5, 5, 0, 0},
- { 6, 6, 0, 0},
- { 7, 7, 0, 0},
- { 8, 8, 0, 0},
- { 9, 9, 0, 0},
- { 10, 10, 0, 0},
- { 11, 11, 0, 0},
- { 12, 12, 0, 0},
- { 13, 13, 0, 0},
- { 14, 14, 0, 0},
- { 15, 15, 0, 0},
- { 16, 16, 0, 0},
- { 17, 17, 0, 0},
- { 18, 18, 0, 0},
- { 19, 19, 0, 0},
- { 20, 20, 0, 0},
- { 21, 21, 0, 0},
- { 22, 22, 0, 0},
- { 23, 23, 0, 0},
- { 24, 24, 0, 0},
- { 25, 25, 0, 0},
- { 26, 26, 0, 0},
- { 27, 27, 0, 0},
- { 28, 28, 0, 0},
- { 29, 5, 1, 0},
- { 30, 6, 1, 0},
- { 31, 7, 1, 0},
- { 32, 8, 1, 0},
- { 33, 9, 1, 0},
- { 34, 10, 1, 0},
- { 35, 11, 1, 0},
- { 36, 12, 1, 0},
- { 37, 13, 1, 0},
- { 38, 14, 1, 0},
- { 39, 15, 1, 0},
- { 40, 16, 1, 0},
- { 41, 17, 1, 0},
- { 42, 18, 1, 0},
- { 43, 19, 1, 0},
- { 44, 20, 1, 0},
- { 45, 21, 1, 0},
- { 46, 22, 1, 0},
- { 47, 23, 1, 0},
- { 48, 24, 1, 0},
- { 49, 25, 1, 0},
- { 50, 26, 1, 0},
- { 51, 27, 1, 0},
- { 52, 28, 1, 0},
- { 53, 29, 1, 0},
- { 54, 30, 1, 0},
- { 55, 31, 1, 0},
- { 56, 32, 1, 0},
- { 57, 33, 1, 0},
- { 58, 34, 1, 0},
- { 59, 35, 1, 0},
- { 60, 36, 1, 0},
- { 61, 37, 1, 0},
- { 62, 38, 1, 0},
- { 63, 39, 1, 0},
- { 64, 40, 1, 0},
- { 65, 41, 1, 0},
- { 66, 42, 1, 0},
- { 67, 43, 1, 0},
- { 68, 44, 1, 0},
- { 69, 45, 1, 0},
- { 70, 46, 1, 0},
- { 71, 47, 1, 0},
- { 72, 48, 1, 0},
- { 73, 49, 1, 0},
- { 74, 50, 1, 0},
- { 75, 51, 1, 0},
- { 76, 52, 1, 0},
- { 77, 53, 1, 0},
- { 78, 54, 1, 0},
- { 79, 55, 1, 0},
- { 80, 56, 1, 0},
- { 81, 57, 1, 0},
- { 82, 58, 1, 0},
- { 83, 40, 1, 1},
- { 84, 41, 1, 1},
- { 85, 42, 1, 1},
- { 86, 43, 1, 1},
- { 87, 44, 1, 1},
- { 88, 45, 1, 1},
- { 89, 46, 1, 1},
- { 90, 47, 1, 1},
- { 91, 48, 1, 1},
- { 92, 49, 1, 1},
- { 93, 50, 1, 1},
- { 94, 51, 1, 1},
- { 95, 52, 1, 1},
- { 96, 53, 1, 1},
- { 97, 54, 1, 1},
- { 98, 55, 1, 1},
- { 99, 56, 1, 1},
- {100, 57, 1, 1},
- {101, 58, 1, 1},
- {102, 59, 1, 1},
-};
-
-/* 420 – 1000 MHz band, lna 7dB, mixer 19dB */
-static const struct msi3101_gain msi3101_gain_lut_1000[] = {
- { 0, 0, 0, 0},
- { 1, 1, 0, 0},
- { 2, 2, 0, 0},
- { 3, 3, 0, 0},
- { 4, 4, 0, 0},
- { 5, 5, 0, 0},
- { 6, 6, 0, 0},
- { 7, 7, 0, 0},
- { 8, 8, 0, 0},
- { 9, 9, 0, 0},
- { 10, 10, 0, 0},
- { 11, 11, 0, 0},
- { 12, 5, 1, 0},
- { 13, 6, 1, 0},
- { 14, 7, 1, 0},
- { 15, 8, 1, 0},
- { 16, 9, 1, 0},
- { 17, 10, 1, 0},
- { 18, 11, 1, 0},
- { 19, 12, 1, 0},
- { 20, 13, 1, 0},
- { 21, 14, 1, 0},
- { 22, 15, 1, 0},
- { 23, 16, 1, 0},
- { 24, 17, 1, 0},
- { 25, 18, 1, 0},
- { 26, 19, 1, 0},
- { 27, 20, 1, 0},
- { 28, 21, 1, 0},
- { 29, 22, 1, 0},
- { 30, 23, 1, 0},
- { 31, 24, 1, 0},
- { 32, 25, 1, 0},
- { 33, 26, 1, 0},
- { 34, 27, 1, 0},
- { 35, 28, 1, 0},
- { 36, 29, 1, 0},
- { 37, 30, 1, 0},
- { 38, 31, 1, 0},
- { 39, 32, 1, 0},
- { 40, 33, 1, 0},
- { 41, 34, 1, 0},
- { 42, 35, 1, 0},
- { 43, 36, 1, 0},
- { 44, 37, 1, 0},
- { 45, 38, 1, 0},
- { 46, 39, 1, 0},
- { 47, 40, 1, 0},
- { 48, 41, 1, 0},
- { 49, 42, 1, 0},
- { 50, 43, 1, 0},
- { 51, 44, 1, 0},
- { 52, 45, 1, 0},
- { 53, 46, 1, 0},
- { 54, 47, 1, 0},
- { 55, 48, 1, 0},
- { 56, 49, 1, 0},
- { 57, 50, 1, 0},
- { 58, 51, 1, 0},
- { 59, 52, 1, 0},
- { 60, 53, 1, 0},
- { 61, 54, 1, 0},
- { 62, 55, 1, 0},
- { 63, 56, 1, 0},
- { 64, 57, 1, 0},
- { 65, 58, 1, 0},
- { 66, 40, 1, 1},
- { 67, 41, 1, 1},
- { 68, 42, 1, 1},
- { 69, 43, 1, 1},
- { 70, 44, 1, 1},
- { 71, 45, 1, 1},
- { 72, 46, 1, 1},
- { 73, 47, 1, 1},
- { 74, 48, 1, 1},
- { 75, 49, 1, 1},
- { 76, 50, 1, 1},
- { 77, 51, 1, 1},
- { 78, 52, 1, 1},
- { 79, 53, 1, 1},
- { 80, 54, 1, 1},
- { 81, 55, 1, 1},
- { 82, 56, 1, 1},
- { 83, 57, 1, 1},
- { 84, 58, 1, 1},
- { 85, 59, 1, 1},
-};
+#include <linux/spi/spi.h>
/*
* iConfiguration 0
@@ -377,13 +52,54 @@ static const struct msi3101_gain msi3101_gain_lut_1000[] = {
#define MAX_ISOC_ERRORS 20
/* TODO: These should be moved to V4L2 API */
-#define MSI3101_CID_SAMPLING_MODE ((V4L2_CID_USER_BASE | 0xf000) + 0)
-#define MSI3101_CID_SAMPLING_RATE ((V4L2_CID_USER_BASE | 0xf000) + 1)
-#define MSI3101_CID_SAMPLING_RESOLUTION ((V4L2_CID_USER_BASE | 0xf000) + 2)
-#define MSI3101_CID_TUNER_RF ((V4L2_CID_USER_BASE | 0xf000) + 10)
-#define MSI3101_CID_TUNER_BW ((V4L2_CID_USER_BASE | 0xf000) + 11)
-#define MSI3101_CID_TUNER_IF ((V4L2_CID_USER_BASE | 0xf000) + 12)
-#define MSI3101_CID_TUNER_GAIN ((V4L2_CID_USER_BASE | 0xf000) + 13)
+#define V4L2_PIX_FMT_SDR_S8 v4l2_fourcc('D', 'S', '0', '8') /* signed 8-bit */
+#define V4L2_PIX_FMT_SDR_S12 v4l2_fourcc('D', 'S', '1', '2') /* signed 12-bit */
+#define V4L2_PIX_FMT_SDR_S14 v4l2_fourcc('D', 'S', '1', '4') /* signed 14-bit */
+#define V4L2_PIX_FMT_SDR_MSI2500_384 v4l2_fourcc('M', '3', '8', '4') /* Mirics MSi2500 format 384 */
+
+static const struct v4l2_frequency_band bands[] = {
+ {
+ .tuner = 0,
+ .type = V4L2_TUNER_ADC,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 1200000,
+ .rangehigh = 15000000,
+ },
+};
+
+/* stream formats */
+struct msi3101_format {
+ char *name;
+ u32 pixelformat;
+};
+
+/* format descriptions for capture and preview */
+static struct msi3101_format formats[] = {
+ {
+ .name = "IQ U8",
+ .pixelformat = V4L2_SDR_FMT_CU8,
+ }, {
+ .name = "IQ U16LE",
+ .pixelformat = V4L2_SDR_FMT_CU16LE,
+#if 0
+ }, {
+ .name = "8-bit signed",
+ .pixelformat = V4L2_PIX_FMT_SDR_S8,
+ }, {
+ .name = "10+2-bit signed",
+ .pixelformat = V4L2_PIX_FMT_SDR_MSI2500_384,
+ }, {
+ .name = "12-bit signed",
+ .pixelformat = V4L2_PIX_FMT_SDR_S12,
+ }, {
+ .name = "14-bit signed",
+ .pixelformat = V4L2_PIX_FMT_SDR_S14,
+#endif
+ },
+};
+
+static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
/* intermediate buffers with raw data from the USB device */
struct msi3101_frame_buf {
@@ -394,6 +110,8 @@ struct msi3101_frame_buf {
struct msi3101_state {
struct video_device vdev;
struct v4l2_device v4l2_dev;
+ struct v4l2_subdev *v4l2_subdev;
+ struct spi_master *master;
/* videobuf2 queue and queued buffers list */
struct vb2_queue vb_queue;
@@ -407,24 +125,22 @@ struct msi3101_state {
/* Pointer to our usb_device, will be NULL after unplug */
struct usb_device *udev; /* Both mutexes most be hold when setting! */
+ unsigned int f_adc;
+ u32 pixelformat;
+
unsigned int isoc_errors; /* number of contiguous ISOC errors */
unsigned int vb_full; /* vb is full and packets dropped */
struct urb *urbs[MAX_ISO_BUFS];
- int (*convert_stream)(struct msi3101_state *s, u32 *dst, u8 *src,
+ int (*convert_stream)(struct msi3101_state *s, u8 *dst, u8 *src,
unsigned int src_len);
/* Controls */
- struct v4l2_ctrl_handler ctrl_handler;
- struct v4l2_ctrl *ctrl_sampling_rate;
- struct v4l2_ctrl *ctrl_tuner_rf;
- struct v4l2_ctrl *ctrl_tuner_bw;
- struct v4l2_ctrl *ctrl_tuner_if;
- struct v4l2_ctrl *ctrl_tuner_gain;
+ struct v4l2_ctrl_handler hdl;
u32 next_sample; /* for track lost packets */
u32 sample; /* for sample rate calc */
- unsigned long jiffies;
+ unsigned long jiffies_next;
unsigned int sample_ctrl_bit[4];
};
@@ -448,98 +164,79 @@ leave:
/*
* +===========================================================================
- * | 00-1023 | USB packet type '384'
+ * | 00-1023 | USB packet type '504'
* +===========================================================================
* | 00- 03 | sequence number of first sample in that USB packet
* +---------------------------------------------------------------------------
* | 04- 15 | garbage
* +---------------------------------------------------------------------------
- * | 16- 175 | samples
- * +---------------------------------------------------------------------------
- * | 176- 179 | control bits for previous samples
- * +---------------------------------------------------------------------------
- * | 180- 339 | samples
- * +---------------------------------------------------------------------------
- * | 340- 343 | control bits for previous samples
- * +---------------------------------------------------------------------------
- * | 344- 503 | samples
- * +---------------------------------------------------------------------------
- * | 504- 507 | control bits for previous samples
- * +---------------------------------------------------------------------------
- * | 508- 667 | samples
- * +---------------------------------------------------------------------------
- * | 668- 671 | control bits for previous samples
- * +---------------------------------------------------------------------------
- * | 672- 831 | samples
- * +---------------------------------------------------------------------------
- * | 832- 835 | control bits for previous samples
- * +---------------------------------------------------------------------------
- * | 836- 995 | samples
- * +---------------------------------------------------------------------------
- * | 996- 999 | control bits for previous samples
- * +---------------------------------------------------------------------------
- * | 1000-1023 | garbage
+ * | 16-1023 | samples
* +---------------------------------------------------------------------------
- *
- * Bytes 4 - 7 could have some meaning?
- *
- * Control bits for previous samples is 32-bit field, containing 16 x 2-bit
- * numbers. This results one 2-bit number for 8 samples. It is likely used for
- * for bit shifting sample by given bits, increasing actual sampling resolution.
- * Number 2 (0b10) was never seen.
- *
- * 6 * 16 * 2 * 4 = 768 samples. 768 * 4 = 3072 bytes
+ * signed 8-bit sample
+ * 504 * 2 = 1008 samples
*/
+static int msi3101_convert_stream_504(struct msi3101_state *s, u8 *dst,
+ u8 *src, unsigned int src_len)
+{
+ int i, i_max, dst_len = 0;
+ u32 sample_num[3];
-/*
- * Integer to 32-bit IEEE floating point representation routine is taken
- * from Radeon R600 driver (drivers/gpu/drm/radeon/r600_blit_kms.c).
- *
- * TODO: Currently we do conversion here in Kernel, but in future that will
- * be moved to the libv4l2 library as video format conversions are.
- */
-#define I2F_FRAC_BITS 23
-#define I2F_MASK ((1 << I2F_FRAC_BITS) - 1)
+ /* There could be 1-3 1024 bytes URB frames */
+ i_max = src_len / 1024;
-/*
- * Converts signed 8-bit integer into 32-bit IEEE floating point
- * representation.
- */
-static u32 msi3101_convert_sample_504(struct msi3101_state *s, u16 x)
-{
- u32 msb, exponent, fraction, sign;
+ for (i = 0; i < i_max; i++) {
+ sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0;
+ if (i == 0 && s->next_sample != sample_num[0]) {
+ dev_dbg_ratelimited(&s->udev->dev,
+ "%d samples lost, %d %08x:%08x\n",
+ sample_num[0] - s->next_sample,
+ src_len, s->next_sample, sample_num[0]);
+ }
- /* Zero is special */
- if (!x)
- return 0;
+ /*
+ * Dump all unknown 'garbage' data - maybe we will discover
+ * someday if there is something rational...
+ */
+ dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]);
- /* Negative / positive value */
- if (x & (1 << 7)) {
- x = -x;
- x &= 0x7f; /* result is 7 bit ... + sign */
- sign = 1 << 31;
- } else {
- sign = 0 << 31;
+ /* 504 x I+Q samples */
+ src += 16;
+ memcpy(dst, src, 1008);
+ src += 1008;
+ dst += 1008;
+ dst_len += 1008;
}
- /* Get location of the most significant bit */
- msb = __fls(x);
+ /* calculate samping rate and output it in 10 seconds intervals */
+ if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) {
+ unsigned long jiffies_now = jiffies;
+ unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next);
+ unsigned int samples = sample_num[i_max - 1] - s->sample;
+ s->jiffies_next = jiffies_now;
+ s->sample = sample_num[i_max - 1];
+ dev_dbg(&s->udev->dev,
+ "slen=%d samples=%u msecs=%lu sampling rate=%lu\n",
+ src_len, samples, msecs,
+ samples * 1000UL / msecs);
+ }
- fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK;
- exponent = (127 + msb) << I2F_FRAC_BITS;
+ /* next sample (sample = sample + i * 504) */
+ s->next_sample = sample_num[i_max - 1] + 504;
- return (fraction + exponent) | sign;
+ return dst_len;
}
-static int msi3101_convert_stream_504(struct msi3101_state *s, u32 *dst,
+static int msi3101_convert_stream_504_u8(struct msi3101_state *s, u8 *dst,
u8 *src, unsigned int src_len)
{
int i, j, i_max, dst_len = 0;
- u16 sample[2];
u32 sample_num[3];
+ s8 *s8src;
+ u8 *u8dst;
/* There could be 1-3 1024 bytes URB frames */
i_max = src_len / 1024;
+ u8dst = (u8 *) dst;
for (i = 0; i < i_max; i++) {
sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0;
@@ -556,30 +253,28 @@ static int msi3101_convert_stream_504(struct msi3101_state *s, u32 *dst,
*/
dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]);
+ /* 504 x I+Q samples */
src += 16;
- for (j = 0; j < 1008; j += 2) {
- sample[0] = src[j + 0];
- sample[1] = src[j + 1];
- *dst++ = msi3101_convert_sample_504(s, sample[0]);
- *dst++ = msi3101_convert_sample_504(s, sample[1]);
- }
- /* 504 x I+Q 32bit float samples */
- dst_len += 504 * 2 * 4;
+ s8src = (s8 *) src;
+ for (j = 0; j < 1008; j++)
+ *u8dst++ = *s8src++ + 128;
+
src += 1008;
+ dst += 1008;
+ dst_len += 1008;
}
/* calculate samping rate and output it in 10 seconds intervals */
- if ((s->jiffies + msecs_to_jiffies(10000)) <= jiffies) {
- unsigned long jiffies_now = jiffies;
- unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies);
+ if (unlikely(time_is_before_jiffies(s->jiffies_next))) {
+#define MSECS 10000UL
unsigned int samples = sample_num[i_max - 1] - s->sample;
- s->jiffies = jiffies_now;
+ s->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
s->sample = sample_num[i_max - 1];
dev_dbg(&s->udev->dev,
"slen=%d samples=%u msecs=%lu sampling rate=%lu\n",
- src_len, samples, msecs,
- samples * 1000UL / msecs);
+ src_len, samples, MSECS,
+ samples * 1000UL / MSECS);
}
/* next sample (sample = sample + i * 504) */
@@ -589,48 +284,53 @@ static int msi3101_convert_stream_504(struct msi3101_state *s, u32 *dst,
}
/*
- * Converts signed ~10+2-bit integer into 32-bit IEEE floating point
- * representation.
+ * +===========================================================================
+ * | 00-1023 | USB packet type '384'
+ * +===========================================================================
+ * | 00- 03 | sequence number of first sample in that USB packet
+ * +---------------------------------------------------------------------------
+ * | 04- 15 | garbage
+ * +---------------------------------------------------------------------------
+ * | 16- 175 | samples
+ * +---------------------------------------------------------------------------
+ * | 176- 179 | control bits for previous samples
+ * +---------------------------------------------------------------------------
+ * | 180- 339 | samples
+ * +---------------------------------------------------------------------------
+ * | 340- 343 | control bits for previous samples
+ * +---------------------------------------------------------------------------
+ * | 344- 503 | samples
+ * +---------------------------------------------------------------------------
+ * | 504- 507 | control bits for previous samples
+ * +---------------------------------------------------------------------------
+ * | 508- 667 | samples
+ * +---------------------------------------------------------------------------
+ * | 668- 671 | control bits for previous samples
+ * +---------------------------------------------------------------------------
+ * | 672- 831 | samples
+ * +---------------------------------------------------------------------------
+ * | 832- 835 | control bits for previous samples
+ * +---------------------------------------------------------------------------
+ * | 836- 995 | samples
+ * +---------------------------------------------------------------------------
+ * | 996- 999 | control bits for previous samples
+ * +---------------------------------------------------------------------------
+ * | 1000-1023 | garbage
+ * +---------------------------------------------------------------------------
+ *
+ * Bytes 4 - 7 could have some meaning?
+ *
+ * Control bits for previous samples is 32-bit field, containing 16 x 2-bit
+ * numbers. This results one 2-bit number for 8 samples. It is likely used for
+ * for bit shifting sample by given bits, increasing actual sampling resolution.
+ * Number 2 (0b10) was never seen.
+ *
+ * 6 * 16 * 2 * 4 = 768 samples. 768 * 4 = 3072 bytes
*/
-static u32 msi3101_convert_sample_384(struct msi3101_state *s, u16 x, int shift)
-{
- u32 msb, exponent, fraction, sign;
- s->sample_ctrl_bit[shift]++;
-
- /* Zero is special */
- if (!x)
- return 0;
-
- if (shift == 3)
- shift = 2;
-
- /* Convert 10-bit two's complement to 12-bit */
- if (x & (1 << 9)) {
- x |= ~0U << 10; /* set all the rest bits to one */
- x <<= shift;
- x = -x;
- x &= 0x7ff; /* result is 11 bit ... + sign */
- sign = 1 << 31;
- } else {
- x <<= shift;
- sign = 0 << 31;
- }
-
- /* Get location of the most significant bit */
- msb = __fls(x);
-
- fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK;
- exponent = (127 + msb) << I2F_FRAC_BITS;
-
- return (fraction + exponent) | sign;
-}
-
-static int msi3101_convert_stream_384(struct msi3101_state *s, u32 *dst,
+static int msi3101_convert_stream_384(struct msi3101_state *s, u8 *dst,
u8 *src, unsigned int src_len)
{
- int i, j, k, l, i_max, dst_len = 0;
- u16 sample[4];
- u32 bits;
+ int i, i_max, dst_len = 0;
u32 sample_num[3];
/* There could be 1-3 1024 bytes URB frames */
@@ -651,38 +351,20 @@ static int msi3101_convert_stream_384(struct msi3101_state *s, u32 *dst,
dev_dbg_ratelimited(&s->udev->dev,
"%*ph %*ph\n", 12, &src[4], 24, &src[1000]);
+ /* 384 x I+Q samples */
src += 16;
- for (j = 0; j < 6; j++) {
- bits = src[160 + 3] << 24 | src[160 + 2] << 16 | src[160 + 1] << 8 | src[160 + 0] << 0;
- for (k = 0; k < 16; k++) {
- for (l = 0; l < 10; l += 5) {
- sample[0] = (src[l + 0] & 0xff) >> 0 | (src[l + 1] & 0x03) << 8;
- sample[1] = (src[l + 1] & 0xfc) >> 2 | (src[l + 2] & 0x0f) << 6;
- sample[2] = (src[l + 2] & 0xf0) >> 4 | (src[l + 3] & 0x3f) << 4;
- sample[3] = (src[l + 3] & 0xc0) >> 6 | (src[l + 4] & 0xff) << 2;
-
- *dst++ = msi3101_convert_sample_384(s, sample[0], (bits >> (2 * k)) & 0x3);
- *dst++ = msi3101_convert_sample_384(s, sample[1], (bits >> (2 * k)) & 0x3);
- *dst++ = msi3101_convert_sample_384(s, sample[2], (bits >> (2 * k)) & 0x3);
- *dst++ = msi3101_convert_sample_384(s, sample[3], (bits >> (2 * k)) & 0x3);
- }
- src += 10;
- }
- dev_dbg_ratelimited(&s->udev->dev,
- "sample control bits %08x\n", bits);
- src += 4;
- }
- /* 384 x I+Q 32bit float samples */
- dst_len += 384 * 2 * 4;
- src += 24;
+ memcpy(dst, src, 984);
+ src += 984 + 24;
+ dst += 984;
+ dst_len += 984;
}
/* calculate samping rate and output it in 10 seconds intervals */
- if ((s->jiffies + msecs_to_jiffies(10000)) <= jiffies) {
+ if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) {
unsigned long jiffies_now = jiffies;
- unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies);
+ unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next);
unsigned int samples = sample_num[i_max - 1] - s->sample;
- s->jiffies = jiffies_now;
+ s->jiffies_next = jiffies_now;
s->sample = sample_num[i_max - 1];
dev_dbg(&s->udev->dev,
"slen=%d samples=%u msecs=%lu sampling rate=%lu bits=%d.%d.%d.%d\n",
@@ -699,40 +381,21 @@ static int msi3101_convert_stream_384(struct msi3101_state *s, u32 *dst,
}
/*
- * Converts signed 12-bit integer into 32-bit IEEE floating point
- * representation.
+ * +===========================================================================
+ * | 00-1023 | USB packet type '336'
+ * +===========================================================================
+ * | 00- 03 | sequence number of first sample in that USB packet
+ * +---------------------------------------------------------------------------
+ * | 04- 15 | garbage
+ * +---------------------------------------------------------------------------
+ * | 16-1023 | samples
+ * +---------------------------------------------------------------------------
+ * signed 12-bit sample
*/
-static u32 msi3101_convert_sample_336(struct msi3101_state *s, u16 x)
-{
- u32 msb, exponent, fraction, sign;
-
- /* Zero is special */
- if (!x)
- return 0;
-
- /* Negative / positive value */
- if (x & (1 << 11)) {
- x = -x;
- x &= 0x7ff; /* result is 11 bit ... + sign */
- sign = 1 << 31;
- } else {
- sign = 0 << 31;
- }
-
- /* Get location of the most significant bit */
- msb = __fls(x);
-
- fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK;
- exponent = (127 + msb) << I2F_FRAC_BITS;
-
- return (fraction + exponent) | sign;
-}
-
-static int msi3101_convert_stream_336(struct msi3101_state *s, u32 *dst,
+static int msi3101_convert_stream_336(struct msi3101_state *s, u8 *dst,
u8 *src, unsigned int src_len)
{
- int i, j, i_max, dst_len = 0;
- u16 sample[2];
+ int i, i_max, dst_len = 0;
u32 sample_num[3];
/* There could be 1-3 1024 bytes URB frames */
@@ -753,25 +416,20 @@ static int msi3101_convert_stream_336(struct msi3101_state *s, u32 *dst,
*/
dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]);
+ /* 336 x I+Q samples */
src += 16;
- for (j = 0; j < 1008; j += 3) {
- sample[0] = (src[j + 0] & 0xff) >> 0 | (src[j + 1] & 0x0f) << 8;
- sample[1] = (src[j + 1] & 0xf0) >> 4 | (src[j + 2] & 0xff) << 4;
-
- *dst++ = msi3101_convert_sample_336(s, sample[0]);
- *dst++ = msi3101_convert_sample_336(s, sample[1]);
- }
- /* 336 x I+Q 32bit float samples */
- dst_len += 336 * 2 * 4;
+ memcpy(dst, src, 1008);
src += 1008;
+ dst += 1008;
+ dst_len += 1008;
}
/* calculate samping rate and output it in 10 seconds intervals */
- if ((s->jiffies + msecs_to_jiffies(10000)) <= jiffies) {
+ if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) {
unsigned long jiffies_now = jiffies;
- unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies);
+ unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next);
unsigned int samples = sample_num[i_max - 1] - s->sample;
- s->jiffies = jiffies_now;
+ s->jiffies_next = jiffies_now;
s->sample = sample_num[i_max - 1];
dev_dbg(&s->udev->dev,
"slen=%d samples=%u msecs=%lu sampling rate=%lu\n",
@@ -786,41 +444,75 @@ static int msi3101_convert_stream_336(struct msi3101_state *s, u32 *dst,
}
/*
- * Converts signed 14-bit integer into 32-bit IEEE floating point
- * representation.
+ * +===========================================================================
+ * | 00-1023 | USB packet type '252'
+ * +===========================================================================
+ * | 00- 03 | sequence number of first sample in that USB packet
+ * +---------------------------------------------------------------------------
+ * | 04- 15 | garbage
+ * +---------------------------------------------------------------------------
+ * | 16-1023 | samples
+ * +---------------------------------------------------------------------------
+ * signed 14-bit sample
*/
-static u32 msi3101_convert_sample_252(struct msi3101_state *s, u16 x)
+static int msi3101_convert_stream_252(struct msi3101_state *s, u8 *dst,
+ u8 *src, unsigned int src_len)
{
- u32 msb, exponent, fraction, sign;
+ int i, i_max, dst_len = 0;
+ u32 sample_num[3];
- /* Zero is special */
- if (!x)
- return 0;
+ /* There could be 1-3 1024 bytes URB frames */
+ i_max = src_len / 1024;
- /* Negative / positive value */
- if (x & (1 << 13)) {
- x = -x;
- x &= 0x1fff; /* result is 13 bit ... + sign */
- sign = 1 << 31;
- } else {
- sign = 0 << 31;
+ for (i = 0; i < i_max; i++) {
+ sample_num[i] = src[3] << 24 | src[2] << 16 | src[1] << 8 | src[0] << 0;
+ if (i == 0 && s->next_sample != sample_num[0]) {
+ dev_dbg_ratelimited(&s->udev->dev,
+ "%d samples lost, %d %08x:%08x\n",
+ sample_num[0] - s->next_sample,
+ src_len, s->next_sample, sample_num[0]);
+ }
+
+ /*
+ * Dump all unknown 'garbage' data - maybe we will discover
+ * someday if there is something rational...
+ */
+ dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]);
+
+ /* 252 x I+Q samples */
+ src += 16;
+ memcpy(dst, src, 1008);
+ src += 1008;
+ dst += 1008;
+ dst_len += 1008;
}
- /* Get location of the most significant bit */
- msb = __fls(x);
+ /* calculate samping rate and output it in 10 seconds intervals */
+ if ((s->jiffies_next + msecs_to_jiffies(10000)) <= jiffies) {
+ unsigned long jiffies_now = jiffies;
+ unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies_next);
+ unsigned int samples = sample_num[i_max - 1] - s->sample;
+ s->jiffies_next = jiffies_now;
+ s->sample = sample_num[i_max - 1];
+ dev_dbg(&s->udev->dev,
+ "slen=%d samples=%u msecs=%lu sampling rate=%lu\n",
+ src_len, samples, msecs,
+ samples * 1000UL / msecs);
+ }
- fraction = ror32(x, (msb - I2F_FRAC_BITS) & 0x1f) & I2F_MASK;
- exponent = (127 + msb) << I2F_FRAC_BITS;
+ /* next sample (sample = sample + i * 252) */
+ s->next_sample = sample_num[i_max - 1] + 252;
- return (fraction + exponent) | sign;
+ return dst_len;
}
-static int msi3101_convert_stream_252(struct msi3101_state *s, u32 *dst,
+static int msi3101_convert_stream_252_u16(struct msi3101_state *s, u8 *dst,
u8 *src, unsigned int src_len)
{
int i, j, i_max, dst_len = 0;
- u16 sample[2];
u32 sample_num[3];
+ u16 *u16dst = (u16 *) dst;
+ struct {signed int x:14;} se;
/* There could be 1-3 1024 bytes URB frames */
i_max = src_len / 1024;
@@ -840,30 +532,44 @@ static int msi3101_convert_stream_252(struct msi3101_state *s, u32 *dst,
*/
dev_dbg_ratelimited(&s->udev->dev, "%*ph\n", 12, &src[4]);
+ /* 252 x I+Q samples */
src += 16;
+
for (j = 0; j < 1008; j += 4) {
- sample[0] = src[j + 0] >> 0 | src[j + 1] << 8;
- sample[1] = src[j + 2] >> 0 | src[j + 3] << 8;
+ unsigned int usample[2];
+ int ssample[2];
+
+ usample[0] = src[j + 0] >> 0 | src[j + 1] << 8;
+ usample[1] = src[j + 2] >> 0 | src[j + 3] << 8;
- *dst++ = msi3101_convert_sample_252(s, sample[0]);
- *dst++ = msi3101_convert_sample_252(s, sample[1]);
+ /* sign extension from 14-bit to signed int */
+ ssample[0] = se.x = usample[0];
+ ssample[1] = se.x = usample[1];
+
+ /* from signed to unsigned */
+ usample[0] = ssample[0] + 8192;
+ usample[1] = ssample[1] + 8192;
+
+ /* from 14-bit to 16-bit */
+ *u16dst++ = (usample[0] << 2) | (usample[0] >> 12);
+ *u16dst++ = (usample[1] << 2) | (usample[1] >> 12);
}
- /* 252 x I+Q 32bit float samples */
- dst_len += 252 * 2 * 4;
+
src += 1008;
+ dst += 1008;
+ dst_len += 1008;
}
/* calculate samping rate and output it in 10 seconds intervals */
- if ((s->jiffies + msecs_to_jiffies(10000)) <= jiffies) {
- unsigned long jiffies_now = jiffies;
- unsigned long msecs = jiffies_to_msecs(jiffies_now) - jiffies_to_msecs(s->jiffies);
+ if (unlikely(time_is_before_jiffies(s->jiffies_next))) {
+#define MSECS 10000UL
unsigned int samples = sample_num[i_max - 1] - s->sample;
- s->jiffies = jiffies_now;
+ s->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
s->sample = sample_num[i_max - 1];
dev_dbg(&s->udev->dev,
"slen=%d samples=%u msecs=%lu sampling rate=%lu\n",
- src_len, samples, msecs,
- samples * 1000UL / msecs);
+ src_len, samples, MSECS,
+ samples * 1000UL / MSECS);
}
/* next sample (sample = sample + i * 252) */
@@ -883,14 +589,14 @@ static void msi3101_isoc_handler(struct urb *urb)
unsigned char *iso_buf = NULL;
struct msi3101_frame_buf *fbuf;
- if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
- urb->status == -ESHUTDOWN) {
+ if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN)) {
dev_dbg(&s->udev->dev, "URB (%p) unlinked %ssynchronuously\n",
urb, urb->status == -ENOENT ? "" : "a");
return;
}
- if (urb->status != 0) {
+ if (unlikely(urb->status != 0)) {
dev_dbg(&s->udev->dev,
"msi3101_isoc_handler() called with status %d\n",
urb->status);
@@ -910,28 +616,28 @@ static void msi3101_isoc_handler(struct urb *urb)
/* Check frame error */
fstatus = urb->iso_frame_desc[i].status;
- if (fstatus) {
+ if (unlikely(fstatus)) {
dev_dbg_ratelimited(&s->udev->dev,
"frame=%d/%d has error %d skipping\n",
i, urb->number_of_packets, fstatus);
- goto skip;
+ continue;
}
/* Check if that frame contains data */
flen = urb->iso_frame_desc[i].actual_length;
- if (flen == 0)
- goto skip;
+ if (unlikely(flen == 0))
+ continue;
iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
/* Get free framebuffer */
fbuf = msi3101_get_next_fill_buf(s);
- if (fbuf == NULL) {
+ if (unlikely(fbuf == NULL)) {
s->vb_full++;
dev_dbg_ratelimited(&s->udev->dev,
"videobuf is full, %d packets dropped\n",
s->vb_full);
- goto skip;
+ continue;
}
/* fill framebuffer */
@@ -939,13 +645,11 @@ static void msi3101_isoc_handler(struct urb *urb)
flen = s->convert_stream(s, ptr, iso_buf, flen);
vb2_set_plane_payload(&fbuf->vb, 0, flen);
vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
-skip:
- ;
}
handler_end:
i = usb_submit_urb(urb, GFP_ATOMIC);
- if (i != 0)
+ if (unlikely(i != 0))
dev_dbg(&s->udev->dev,
"Error (%d) re-submitting urb in msi3101_isoc_handler\n",
i);
@@ -1008,7 +712,7 @@ static int msi3101_isoc_init(struct msi3101_state *s)
udev = s->udev;
ret = usb_set_interface(s->udev, 0, 1);
- if (ret < 0)
+ if (ret)
return ret;
/* Allocate and init Isochronuous urbs */
@@ -1094,9 +798,9 @@ static void msi3101_disconnect(struct usb_interface *intf)
mutex_lock(&s->v4l2_lock);
/* No need to keep the urbs around after disconnection */
s->udev = NULL;
-
v4l2_device_disconnect(&s->v4l2_dev);
video_unregister_device(&s->vdev);
+ spi_unregister_master(s->master);
mutex_unlock(&s->v4l2_lock);
mutex_unlock(&s->vb_queue_lock);
@@ -1112,14 +816,12 @@ static int msi3101_querycap(struct file *file, void *fh,
strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
strlcpy(cap->card, s->vdev.name, sizeof(cap->card));
usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
- cap->device_caps = V4L2_CAP_TUNER;
+ cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE | V4L2_CAP_TUNER;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
-
/* Videobuf2 operations */
static int msi3101_queue_setup(struct vb2_queue *vq,
const struct v4l2_format *fmt, unsigned int *nbuffers,
@@ -1129,31 +831,20 @@ static int msi3101_queue_setup(struct vb2_queue *vq,
dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers);
/* Absolute min and max number of buffers available for mmap() */
- *nbuffers = 32;
+ *nbuffers = clamp_t(unsigned int, *nbuffers, 8, 32);
*nplanes = 1;
/*
* 3, wMaxPacketSize 3x 1024 bytes
* 504, max IQ sample pairs per 1024 frame
* 2, two samples, I and Q
- * 4, 32-bit float
+ * 2, 16-bit is enough for single sample
*/
- sizes[0] = PAGE_ALIGN(3 * 504 * 2 * 4); /* = 12096 */
+ sizes[0] = PAGE_ALIGN(3 * 504 * 2 * 2);
dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n",
__func__, *nbuffers, sizes[0]);
return 0;
}
-static int msi3101_buf_prepare(struct vb2_buffer *vb)
-{
- struct msi3101_state *s = vb2_get_drv_priv(vb->vb2_queue);
-
- /* Don't allow queing new buffers after device disconnection */
- if (!s->udev)
- return -ENODEV;
-
- return 0;
-}
-
static void msi3101_buf_queue(struct vb2_buffer *vb)
{
struct msi3101_state *s = vb2_get_drv_priv(vb->vb2_queue);
@@ -1162,7 +853,7 @@ static void msi3101_buf_queue(struct vb2_buffer *vb)
unsigned long flags = 0;
/* Check the device has not disconnected between prep and queuing */
- if (!s->udev) {
+ if (unlikely(!s->udev)) {
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
return;
}
@@ -1209,41 +900,63 @@ static int msi3101_ctrl_msg(struct msi3101_state *s, u8 cmd, u32 data)
return ret;
};
-static int msi3101_tuner_write(struct msi3101_state *s, u32 data)
-{
- return msi3101_ctrl_msg(s, CMD_WREG, data << 8 | 0x09);
-};
-
#define F_REF 24000000
#define DIV_R_IN 2
static int msi3101_set_usb_adc(struct msi3101_state *s)
{
int ret, div_n, div_m, div_r_out, f_sr, f_vco, fract;
u32 reg3, reg4, reg7;
+ struct v4l2_ctrl *bandwidth_auto;
+ struct v4l2_ctrl *bandwidth;
- f_sr = s->ctrl_sampling_rate->val64;
+ f_sr = s->f_adc;
+
+ /* set tuner, subdev, filters according to sampling rate */
+ bandwidth_auto = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO);
+ bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH);
+ if (v4l2_ctrl_g_ctrl(bandwidth_auto)) {
+ bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH);
+ v4l2_ctrl_s_ctrl(bandwidth, s->f_adc);
+ }
/* select stream format */
- if (f_sr < 6000000) {
- s->convert_stream = msi3101_convert_stream_252;
+ switch (s->pixelformat) {
+ case V4L2_SDR_FMT_CU8:
+ s->convert_stream = msi3101_convert_stream_504_u8;
+ reg7 = 0x000c9407;
+ break;
+ case V4L2_SDR_FMT_CU16LE:
+ s->convert_stream = msi3101_convert_stream_252_u16;
reg7 = 0x00009407;
- } else if (f_sr < 8000000) {
- s->convert_stream = msi3101_convert_stream_336;
- reg7 = 0x00008507;
- } else if (f_sr < 9000000) {
+ break;
+ case V4L2_PIX_FMT_SDR_S8:
+ s->convert_stream = msi3101_convert_stream_504;
+ reg7 = 0x000c9407;
+ break;
+ case V4L2_PIX_FMT_SDR_MSI2500_384:
s->convert_stream = msi3101_convert_stream_384;
reg7 = 0x0000a507;
- } else {
- s->convert_stream = msi3101_convert_stream_504;
+ break;
+ case V4L2_PIX_FMT_SDR_S12:
+ s->convert_stream = msi3101_convert_stream_336;
+ reg7 = 0x00008507;
+ break;
+ case V4L2_PIX_FMT_SDR_S14:
+ s->convert_stream = msi3101_convert_stream_252;
+ reg7 = 0x00009407;
+ break;
+ default:
+ s->convert_stream = msi3101_convert_stream_504_u8;
reg7 = 0x000c9407;
+ break;
}
/*
* Synthesizer config is just a educated guess...
*
* [7:0] 0x03, register address
- * [8] 1, always
- * [9] ?
+ * [8] 1, power control
+ * [9] ?, power control
* [12:10] output divider
* [13] 0 ?
* [14] 0 ?
@@ -1334,224 +1047,6 @@ err:
return ret;
};
-static int msi3101_set_tuner(struct msi3101_state *s)
-{
- int ret, i, len;
- unsigned int n, m, thresh, frac, vco_step, tmp, f_if1;
- u32 reg;
- u64 f_vco, tmp64;
- u8 mode, filter_mode, lo_div;
- const struct msi3101_gain *gain_lut;
- static const struct {
- u32 rf;
- u8 mode;
- u8 lo_div;
- } band_lut[] = {
- { 50000000, 0xe1, 16}, /* AM_MODE2, antenna 2 */
- {108000000, 0x42, 32}, /* VHF_MODE */
- {330000000, 0x44, 16}, /* B3_MODE */
- {960000000, 0x48, 4}, /* B45_MODE */
- { ~0U, 0x50, 2}, /* BL_MODE */
- };
- static const struct {
- u32 freq;
- u8 filter_mode;
- } if_freq_lut[] = {
- { 0, 0x03}, /* Zero IF */
- { 450000, 0x02}, /* 450 kHz IF */
- {1620000, 0x01}, /* 1.62 MHz IF */
- {2048000, 0x00}, /* 2.048 MHz IF */
- };
- static const struct {
- u32 freq;
- u8 val;
- } bandwidth_lut[] = {
- { 200000, 0x00}, /* 200 kHz */
- { 300000, 0x01}, /* 300 kHz */
- { 600000, 0x02}, /* 600 kHz */
- {1536000, 0x03}, /* 1.536 MHz */
- {5000000, 0x04}, /* 5 MHz */
- {6000000, 0x05}, /* 6 MHz */
- {7000000, 0x06}, /* 7 MHz */
- {8000000, 0x07}, /* 8 MHz */
- };
-
- unsigned int f_rf = s->ctrl_tuner_rf->val64;
-
- /*
- * bandwidth (Hz)
- * 200000, 300000, 600000, 1536000, 5000000, 6000000, 7000000, 8000000
- */
- unsigned int bandwidth = s->ctrl_tuner_bw->val;
-
- /*
- * intermediate frequency (Hz)
- * 0, 450000, 1620000, 2048000
- */
- unsigned int f_if = s->ctrl_tuner_if->val;
-
- /*
- * gain reduction (dB)
- * 0 - 102 below 420 MHz
- * 0 - 85 above 420 MHz
- */
- int gain = s->ctrl_tuner_gain->val;
-
- dev_dbg(&s->udev->dev,
- "%s: f_rf=%d bandwidth=%d f_if=%d gain=%d\n",
- __func__, f_rf, bandwidth, f_if, gain);
-
- ret = -EINVAL;
-
- for (i = 0; i < ARRAY_SIZE(band_lut); i++) {
- if (f_rf <= band_lut[i].rf) {
- mode = band_lut[i].mode;
- lo_div = band_lut[i].lo_div;
- break;
- }
- }
-
- if (i == ARRAY_SIZE(band_lut))
- goto err;
-
- /* AM_MODE is upconverted */
- if ((mode >> 0) & 0x1)
- f_if1 = 5 * F_REF;
- else
- f_if1 = 0;
-
- for (i = 0; i < ARRAY_SIZE(if_freq_lut); i++) {
- if (f_if == if_freq_lut[i].freq) {
- filter_mode = if_freq_lut[i].filter_mode;
- break;
- }
- }
-
- if (i == ARRAY_SIZE(if_freq_lut))
- goto err;
-
- for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) {
- if (bandwidth == bandwidth_lut[i].freq) {
- bandwidth = bandwidth_lut[i].val;
- break;
- }
- }
-
- if (i == ARRAY_SIZE(bandwidth_lut))
- goto err;
-
-#define F_OUT_STEP 1
-#define R_REF 4
- f_vco = (f_rf + f_if + f_if1) * lo_div;
-
- tmp64 = f_vco;
- m = do_div(tmp64, F_REF * R_REF);
- n = (unsigned int) tmp64;
-
- vco_step = F_OUT_STEP * lo_div;
- thresh = (F_REF * R_REF) / vco_step;
- frac = 1ul * thresh * m / (F_REF * R_REF);
-
- /* Find out greatest common divisor and divide to smaller. */
- tmp = gcd(thresh, frac);
- thresh /= tmp;
- frac /= tmp;
-
- /* Force divide to reg max. Resolution will be reduced. */
- tmp = DIV_ROUND_UP(thresh, 4095);
- thresh = DIV_ROUND_CLOSEST(thresh, tmp);
- frac = DIV_ROUND_CLOSEST(frac, tmp);
-
- /* calc real RF set */
- tmp = 1ul * F_REF * R_REF * n;
- tmp += 1ul * F_REF * R_REF * frac / thresh;
- tmp /= lo_div;
-
- dev_dbg(&s->udev->dev,
- "%s: rf=%u:%u n=%d thresh=%d frac=%d\n",
- __func__, f_rf, tmp, n, thresh, frac);
-
- ret = msi3101_tuner_write(s, 0x00000e);
- if (ret)
- goto err;
-
- ret = msi3101_tuner_write(s, 0x000003);
- if (ret)
- goto err;
-
- reg = 0 << 0;
- reg |= mode << 4;
- reg |= filter_mode << 12;
- reg |= bandwidth << 14;
- reg |= 0x02 << 17;
- reg |= 0x00 << 20;
- ret = msi3101_tuner_write(s, reg);
- if (ret)
- goto err;
-
- reg = 5 << 0;
- reg |= thresh << 4;
- reg |= 1 << 19;
- reg |= 1 << 21;
- ret = msi3101_tuner_write(s, reg);
- if (ret)
- goto err;
-
- reg = 2 << 0;
- reg |= frac << 4;
- reg |= n << 16;
- ret = msi3101_tuner_write(s, reg);
- if (ret)
- goto err;
-
- if (f_rf < 120000000) {
- gain_lut = msi3101_gain_lut_120;
- len = ARRAY_SIZE(msi3101_gain_lut_120);
- } else if (f_rf < 245000000) {
- gain_lut = msi3101_gain_lut_245;
- len = ARRAY_SIZE(msi3101_gain_lut_120);
- } else {
- gain_lut = msi3101_gain_lut_1000;
- len = ARRAY_SIZE(msi3101_gain_lut_1000);
- }
-
- for (i = 0; i < len; i++) {
- if (gain_lut[i].tot >= gain)
- break;
- }
-
- if (i == len)
- goto err;
-
- dev_dbg(&s->udev->dev,
- "%s: gain tot=%d baseband=%d lna=%d mixer=%d\n",
- __func__, gain_lut[i].tot, gain_lut[i].baseband,
- gain_lut[i].lna, gain_lut[i].mixer);
-
- reg = 1 << 0;
- reg |= gain_lut[i].baseband << 4;
- reg |= 0 << 10;
- reg |= gain_lut[i].mixer << 12;
- reg |= gain_lut[i].lna << 13;
- reg |= 4 << 14;
- reg |= 0 << 17;
- ret = msi3101_tuner_write(s, reg);
- if (ret)
- goto err;
-
- reg = 6 << 0;
- reg |= 63 << 4;
- reg |= 4095 << 10;
- ret = msi3101_tuner_write(s, reg);
- if (ret)
- goto err;
-
- return 0;
-err:
- dev_dbg(&s->udev->dev, "%s: failed %d\n", __func__, ret);
- return ret;
-};
-
static int msi3101_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct msi3101_state *s = vb2_get_drv_priv(vq);
@@ -1564,6 +1059,9 @@ static int msi3101_start_streaming(struct vb2_queue *vq, unsigned int count)
if (mutex_lock_interruptible(&s->v4l2_lock))
return -ERESTARTSYS;
+ /* wake-up tuner */
+ v4l2_subdev_call(s->v4l2_subdev, core, s_power, 1);
+
ret = msi3101_set_usb_adc(s);
ret = msi3101_isoc_init(s);
@@ -1594,6 +1092,12 @@ static int msi3101_stop_streaming(struct vb2_queue *vq)
msleep(20);
msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0);
+ /* sleep USB IF / ADC */
+ msi3101_ctrl_msg(s, CMD_WREG, 0x01000003);
+
+ /* sleep tuner */
+ v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0);
+
mutex_unlock(&s->v4l2_lock);
return 0;
@@ -1601,7 +1105,6 @@ static int msi3101_stop_streaming(struct vb2_queue *vq)
static struct vb2_ops msi3101_vb2_ops = {
.queue_setup = msi3101_queue_setup,
- .buf_prepare = msi3101_buf_prepare,
.buf_queue = msi3101_buf_queue,
.start_streaming = msi3101_start_streaming,
.stop_streaming = msi3101_stop_streaming,
@@ -1609,66 +1112,195 @@ static struct vb2_ops msi3101_vb2_ops = {
.wait_finish = vb2_ops_wait_finish,
};
-static int msi3101_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+static int msi3101_enum_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
{
- if (i->index != 0)
+ struct msi3101_state *s = video_drvdata(file);
+ dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, f->index);
+
+ if (f->index >= NUM_FORMATS)
return -EINVAL;
- strlcpy(i->name, "SDR data", sizeof(i->name));
- i->type = V4L2_INPUT_TYPE_CAMERA;
+ strlcpy(f->description, formats[f->index].name, sizeof(f->description));
+ f->pixelformat = formats[f->index].pixelformat;
+
+ return 0;
+}
+
+static int msi3101_g_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct msi3101_state *s = video_drvdata(file);
+ dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+ (char *)&s->pixelformat);
+
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+ f->fmt.sdr.pixelformat = s->pixelformat;
return 0;
}
-static int msi3101_g_input(struct file *file, void *fh, unsigned int *i)
+static int msi3101_s_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- *i = 0;
+ struct msi3101_state *s = video_drvdata(file);
+ struct vb2_queue *q = &s->vb_queue;
+ int i;
+ dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+ (char *)&f->fmt.sdr.pixelformat);
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+ s->pixelformat = f->fmt.sdr.pixelformat;
+ return 0;
+ }
+ }
+
+ f->fmt.sdr.pixelformat = formats[0].pixelformat;
+ s->pixelformat = formats[0].pixelformat;
return 0;
}
-static int msi3101_s_input(struct file *file, void *fh, unsigned int i)
+static int msi3101_try_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- return i ? -EINVAL : 0;
+ struct msi3101_state *s = video_drvdata(file);
+ int i;
+ dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+ (char *)&f->fmt.sdr.pixelformat);
+
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].pixelformat == f->fmt.sdr.pixelformat)
+ return 0;
+ }
+
+ f->fmt.sdr.pixelformat = formats[0].pixelformat;
+
+ return 0;
}
-static int vidioc_s_tuner(struct file *file, void *priv,
+static int msi3101_s_tuner(struct file *file, void *priv,
const struct v4l2_tuner *v)
{
struct msi3101_state *s = video_drvdata(file);
- dev_dbg(&s->udev->dev, "%s:\n", __func__);
+ int ret;
+ dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index);
- return 0;
+ if (v->index == 0)
+ ret = 0;
+ else if (v->index == 1)
+ ret = v4l2_subdev_call(s->v4l2_subdev, tuner, s_tuner, v);
+ else
+ ret = -EINVAL;
+
+ return ret;
}
-static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
+static int msi3101_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
{
struct msi3101_state *s = video_drvdata(file);
- dev_dbg(&s->udev->dev, "%s:\n", __func__);
+ int ret;
+ dev_dbg(&s->udev->dev, "%s: index=%d\n", __func__, v->index);
+
+ if (v->index == 0) {
+ strlcpy(v->name, "Mirics MSi2500", sizeof(v->name));
+ v->type = V4L2_TUNER_ADC;
+ v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+ v->rangelow = 1200000;
+ v->rangehigh = 15000000;
+ ret = 0;
+ } else if (v->index == 1) {
+ ret = v4l2_subdev_call(s->v4l2_subdev, tuner, g_tuner, v);
+ } else {
+ ret = -EINVAL;
+ }
- strcpy(v->name, "SDR RX");
- v->capability = V4L2_TUNER_CAP_LOW;
+ return ret;
+}
- return 0;
+static int msi3101_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct msi3101_state *s = video_drvdata(file);
+ int ret = 0;
+ dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n",
+ __func__, f->tuner, f->type);
+
+ if (f->tuner == 0) {
+ f->frequency = s->f_adc;
+ ret = 0;
+ } else if (f->tuner == 1) {
+ f->type = V4L2_TUNER_RF;
+ ret = v4l2_subdev_call(s->v4l2_subdev, tuner, g_frequency, f);
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
}
-static int vidioc_s_frequency(struct file *file, void *priv,
+static int msi3101_s_frequency(struct file *file, void *priv,
const struct v4l2_frequency *f)
{
struct msi3101_state *s = video_drvdata(file);
- dev_dbg(&s->udev->dev, "%s: frequency=%lu Hz (%u)\n",
- __func__, f->frequency * 625UL / 10UL, f->frequency);
+ int ret;
+ dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n",
+ __func__, f->tuner, f->type, f->frequency);
+
+ if (f->tuner == 0) {
+ s->f_adc = clamp_t(unsigned int, f->frequency,
+ bands[0].rangelow,
+ bands[0].rangehigh);
+ dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n",
+ __func__, s->f_adc);
+ ret = msi3101_set_usb_adc(s);
+ } else if (f->tuner == 1) {
+ ret = v4l2_subdev_call(s->v4l2_subdev, tuner, s_frequency, f);
+ } else {
+ ret = -EINVAL;
+ }
- return v4l2_ctrl_s_ctrl_int64(s->ctrl_tuner_rf,
- f->frequency * 625UL / 10UL);
+ return ret;
+}
+
+static int msi3101_enum_freq_bands(struct file *file, void *priv,
+ struct v4l2_frequency_band *band)
+{
+ struct msi3101_state *s = video_drvdata(file);
+ int ret;
+ dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n",
+ __func__, band->tuner, band->type, band->index);
+
+ if (band->tuner == 0) {
+ if (band->index >= ARRAY_SIZE(bands)) {
+ ret = -EINVAL;
+ } else {
+ *band = bands[band->index];
+ ret = 0;
+ }
+ } else if (band->tuner == 1) {
+ ret = v4l2_subdev_call(s->v4l2_subdev, tuner,
+ enum_freq_bands, band);
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
}
static const struct v4l2_ioctl_ops msi3101_ioctl_ops = {
.vidioc_querycap = msi3101_querycap,
- .vidioc_enum_input = msi3101_enum_input,
- .vidioc_g_input = msi3101_g_input,
- .vidioc_s_input = msi3101_s_input,
+ .vidioc_enum_fmt_sdr_cap = msi3101_enum_fmt_sdr_cap,
+ .vidioc_g_fmt_sdr_cap = msi3101_g_fmt_sdr_cap,
+ .vidioc_s_fmt_sdr_cap = msi3101_s_fmt_sdr_cap,
+ .vidioc_try_fmt_sdr_cap = msi3101_try_fmt_sdr_cap,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
@@ -1680,9 +1312,12 @@ static const struct v4l2_ioctl_ops msi3101_ioctl_ops = {
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_g_tuner = msi3101_g_tuner,
+ .vidioc_s_tuner = msi3101_s_tuner,
+
+ .vidioc_g_frequency = msi3101_g_frequency,
+ .vidioc_s_frequency = msi3101_s_frequency,
+ .vidioc_enum_freq_bands = msi3101_enum_freq_bands,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
@@ -1706,129 +1341,52 @@ static struct video_device msi3101_template = {
.ioctl_ops = &msi3101_ioctl_ops,
};
-static int msi3101_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct msi3101_state *s =
- container_of(ctrl->handler, struct msi3101_state,
- ctrl_handler);
- int ret;
- dev_dbg(&s->udev->dev,
- "%s: id=%d name=%s val=%d min=%d max=%d step=%d\n",
- __func__, ctrl->id, ctrl->name, ctrl->val,
- ctrl->minimum, ctrl->maximum, ctrl->step);
-
- switch (ctrl->id) {
- case MSI3101_CID_SAMPLING_MODE:
- case MSI3101_CID_SAMPLING_RATE:
- case MSI3101_CID_SAMPLING_RESOLUTION:
- ret = 0;
- break;
- case MSI3101_CID_TUNER_RF:
- case MSI3101_CID_TUNER_BW:
- case MSI3101_CID_TUNER_IF:
- case MSI3101_CID_TUNER_GAIN:
- ret = msi3101_set_tuner(s);
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static const struct v4l2_ctrl_ops msi3101_ctrl_ops = {
- .s_ctrl = msi3101_s_ctrl,
-};
-
static void msi3101_video_release(struct v4l2_device *v)
{
struct msi3101_state *s =
container_of(v, struct msi3101_state, v4l2_dev);
- v4l2_ctrl_handler_free(&s->ctrl_handler);
+ v4l2_ctrl_handler_free(&s->hdl);
v4l2_device_unregister(&s->v4l2_dev);
kfree(s);
}
+static int msi3101_transfer_one_message(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct msi3101_state *s = spi_master_get_devdata(master);
+ struct spi_transfer *t;
+ int ret = 0;
+ u32 data;
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ dev_dbg(&s->udev->dev, "%s: msg=%*ph\n",
+ __func__, t->len, t->tx_buf);
+ data = 0x09; /* reg 9 is SPI adapter */
+ data |= ((u8 *)t->tx_buf)[0] << 8;
+ data |= ((u8 *)t->tx_buf)[1] << 16;
+ data |= ((u8 *)t->tx_buf)[2] << 24;
+ ret = msi3101_ctrl_msg(s, CMD_WREG, data);
+ }
+
+ m->status = ret;
+ spi_finalize_current_message(master);
+ return ret;
+}
+
static int msi3101_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct msi3101_state *s = NULL;
+ struct v4l2_subdev *sd;
+ struct spi_master *master;
int ret;
- static const char * const ctrl_sampling_mode_qmenu_strings[] = {
- "Quadrature Sampling",
- NULL,
- };
- static const struct v4l2_ctrl_config ctrl_sampling_mode = {
- .ops = &msi3101_ctrl_ops,
- .id = MSI3101_CID_SAMPLING_MODE,
- .type = V4L2_CTRL_TYPE_MENU,
- .flags = V4L2_CTRL_FLAG_INACTIVE,
- .name = "Sampling Mode",
- .qmenu = ctrl_sampling_mode_qmenu_strings,
- };
- static const struct v4l2_ctrl_config ctrl_sampling_rate = {
- .ops = &msi3101_ctrl_ops,
- .id = MSI3101_CID_SAMPLING_RATE,
- .type = V4L2_CTRL_TYPE_INTEGER64,
- .name = "Sampling Rate",
- .min = 500000,
- .max = 12000000,
- .def = 2048000,
- .step = 1,
- };
- static const struct v4l2_ctrl_config ctrl_sampling_resolution = {
- .ops = &msi3101_ctrl_ops,
- .id = MSI3101_CID_SAMPLING_RESOLUTION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .flags = V4L2_CTRL_FLAG_INACTIVE,
- .name = "Sampling Resolution",
- .min = 10,
- .max = 10,
- .def = 10,
- .step = 1,
- };
- static const struct v4l2_ctrl_config ctrl_tuner_rf = {
- .ops = &msi3101_ctrl_ops,
- .id = MSI3101_CID_TUNER_RF,
- .type = V4L2_CTRL_TYPE_INTEGER64,
- .name = "Tuner RF",
- .min = 40000000,
- .max = 2000000000,
- .def = 100000000,
- .step = 1,
- };
- static const struct v4l2_ctrl_config ctrl_tuner_bw = {
- .ops = &msi3101_ctrl_ops,
- .id = MSI3101_CID_TUNER_BW,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Tuner BW",
- .min = 200000,
- .max = 8000000,
- .def = 600000,
- .step = 1,
- };
- static const struct v4l2_ctrl_config ctrl_tuner_if = {
- .ops = &msi3101_ctrl_ops,
- .id = MSI3101_CID_TUNER_IF,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .flags = V4L2_CTRL_FLAG_INACTIVE,
- .name = "Tuner IF",
- .min = 0,
- .max = 2048000,
- .def = 0,
- .step = 1,
- };
- static const struct v4l2_ctrl_config ctrl_tuner_gain = {
- .ops = &msi3101_ctrl_ops,
- .id = MSI3101_CID_TUNER_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Tuner Gain",
- .min = 0,
- .max = 102,
- .def = 0,
- .step = 1,
+ static struct spi_board_info board_info = {
+ .modalias = "msi001",
+ .bus_num = 0,
+ .chip_select = 0,
+ .max_speed_hz = 12000000,
};
s = kzalloc(sizeof(struct msi3101_state), GFP_KERNEL);
@@ -1841,19 +1399,20 @@ static int msi3101_probe(struct usb_interface *intf,
mutex_init(&s->vb_queue_lock);
spin_lock_init(&s->queued_bufs_lock);
INIT_LIST_HEAD(&s->queued_bufs);
-
s->udev = udev;
+ s->f_adc = bands[0].rangelow;
+ s->pixelformat = V4L2_SDR_FMT_CU8;
/* Init videobuf2 queue structure */
- s->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
s->vb_queue.drv_priv = s;
s->vb_queue.buf_struct_size = sizeof(struct msi3101_frame_buf);
s->vb_queue.ops = &msi3101_vb2_ops;
s->vb_queue.mem_ops = &vb2_vmalloc_memops;
- s->vb_queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(&s->vb_queue);
- if (ret < 0) {
+ if (ret) {
dev_err(&s->udev->dev, "Could not initialize vb2 queue\n");
goto err_free_mem;
}
@@ -1865,36 +1424,59 @@ static int msi3101_probe(struct usb_interface *intf,
set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev.flags);
video_set_drvdata(&s->vdev, s);
- /* Register controls */
- v4l2_ctrl_handler_init(&s->ctrl_handler, 7);
- v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_sampling_mode, NULL);
- s->ctrl_sampling_rate = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_sampling_rate, NULL);
- v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_sampling_resolution, NULL);
- s->ctrl_tuner_rf = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_tuner_rf, NULL);
- s->ctrl_tuner_bw = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_tuner_bw, NULL);
- s->ctrl_tuner_if = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_tuner_if, NULL);
- s->ctrl_tuner_gain = v4l2_ctrl_new_custom(&s->ctrl_handler, &ctrl_tuner_gain, NULL);
- if (s->ctrl_handler.error) {
- ret = s->ctrl_handler.error;
- dev_err(&s->udev->dev, "Could not initialize controls\n");
- goto err_free_controls;
- }
-
/* Register the v4l2_device structure */
s->v4l2_dev.release = msi3101_video_release;
ret = v4l2_device_register(&intf->dev, &s->v4l2_dev);
if (ret) {
dev_err(&s->udev->dev,
"Failed to register v4l2-device (%d)\n", ret);
+ goto err_free_mem;
+ }
+
+ /* SPI master adapter */
+ master = spi_alloc_master(&s->udev->dev, 0);
+ if (master == NULL) {
+ ret = -ENOMEM;
+ goto err_unregister_v4l2_dev;
+ }
+
+ s->master = master;
+ master->bus_num = 0;
+ master->num_chipselect = 1;
+ master->transfer_one_message = msi3101_transfer_one_message;
+ spi_master_set_devdata(master, s);
+ ret = spi_register_master(master);
+ if (ret) {
+ spi_master_put(master);
+ goto err_unregister_v4l2_dev;
+ }
+
+ /* load v4l2 subdevice */
+ sd = v4l2_spi_new_subdev(&s->v4l2_dev, master, &board_info);
+ s->v4l2_subdev = sd;
+ if (sd == NULL) {
+ dev_err(&s->udev->dev, "cannot get v4l2 subdevice\n");
+ ret = -ENODEV;
+ goto err_unregister_master;
+ }
+
+ /* Register controls */
+ v4l2_ctrl_handler_init(&s->hdl, 0);
+ if (s->hdl.error) {
+ ret = s->hdl.error;
+ dev_err(&s->udev->dev, "Could not initialize controls\n");
goto err_free_controls;
}
- s->v4l2_dev.ctrl_handler = &s->ctrl_handler;
+ /* currently all controls are from subdev */
+ v4l2_ctrl_add_handler(&s->hdl, sd->ctrl_handler, NULL);
+
+ s->v4l2_dev.ctrl_handler = &s->hdl;
s->vdev.v4l2_dev = &s->v4l2_dev;
s->vdev.lock = &s->v4l2_lock;
- ret = video_register_device(&s->vdev, VFL_TYPE_GRABBER, -1);
- if (ret < 0) {
+ ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1);
+ if (ret) {
dev_err(&s->udev->dev,
"Failed to register as video device (%d)\n",
ret);
@@ -1905,10 +1487,12 @@ static int msi3101_probe(struct usb_interface *intf,
return 0;
+err_free_controls:
+ v4l2_ctrl_handler_free(&s->hdl);
+err_unregister_master:
+ spi_unregister_master(s->master);
err_unregister_v4l2_dev:
v4l2_device_unregister(&s->v4l2_dev);
-err_free_controls:
- v4l2_ctrl_handler_free(&s->ctrl_handler);
err_free_mem:
kfree(s);
return ret;
diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c
index 8c7f35029cd5..ded31ea6bd39 100644
--- a/drivers/staging/media/omap4iss/iss_video.c
+++ b/drivers/staging/media/omap4iss/iss_video.c
@@ -1074,7 +1074,7 @@ static int iss_video_open(struct file *file)
q->ops = &iss_video_vb2ops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct iss_buffer);
- q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(q);
if (ret) {
diff --git a/drivers/staging/media/rtl2832u_sdr/Kconfig b/drivers/staging/media/rtl2832u_sdr/Kconfig
new file mode 100644
index 000000000000..3ede5fe8f0a5
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/Kconfig
@@ -0,0 +1,7 @@
+config DVB_RTL2832_SDR
+ tristate "Realtek RTL2832 SDR"
+ depends on USB && DVB_CORE && I2C && VIDEO_V4L2 && DVB_USB_RTL28XXU
+ select DVB_RTL2832
+ select VIDEOBUF2_VMALLOC
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+
diff --git a/drivers/staging/media/rtl2832u_sdr/Makefile b/drivers/staging/media/rtl2832u_sdr/Makefile
new file mode 100644
index 000000000000..7e00a0df4631
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/usb/dvb-usb-v2
diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
new file mode 100644
index 000000000000..104ee8af79af
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
@@ -0,0 +1,1500 @@
+/*
+ * Realtek RTL2832U SDR driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * GNU Radio plugin "gr-kernel" for device usage will be on:
+ * http://git.linuxtv.org/anttip/gr-kernel.git
+ *
+ */
+
+#include "dvb_frontend.h"
+#include "rtl2832_sdr.h"
+#include "dvb_usb.h"
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include <linux/jiffies.h>
+#include <linux/math64.h>
+
+#define MAX_BULK_BUFS (10)
+#define BULK_BUFFER_SIZE (128 * 512)
+
+static const struct v4l2_frequency_band bands_adc[] = {
+ {
+ .tuner = 0,
+ .type = V4L2_TUNER_ADC,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 300000,
+ .rangehigh = 300000,
+ },
+ {
+ .tuner = 0,
+ .type = V4L2_TUNER_ADC,
+ .index = 1,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 900001,
+ .rangehigh = 2800000,
+ },
+ {
+ .tuner = 0,
+ .type = V4L2_TUNER_ADC,
+ .index = 2,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 3200000,
+ .rangehigh = 3200000,
+ },
+};
+
+static const struct v4l2_frequency_band bands_fm[] = {
+ {
+ .tuner = 1,
+ .type = V4L2_TUNER_RF,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 50000000,
+ .rangehigh = 2000000000,
+ },
+};
+
+/* stream formats */
+struct rtl2832_sdr_format {
+ char *name;
+ u32 pixelformat;
+};
+
+static struct rtl2832_sdr_format formats[] = {
+ {
+ .name = "IQ U8",
+ .pixelformat = V4L2_SDR_FMT_CU8,
+ }, {
+ .name = "IQ U16LE (emulated)",
+ .pixelformat = V4L2_SDR_FMT_CU16LE,
+ },
+};
+
+static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
+
+/* intermediate buffers with raw data from the USB device */
+struct rtl2832_sdr_frame_buf {
+ struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */
+ struct list_head list;
+};
+
+struct rtl2832_sdr_state {
+#define POWER_ON (1 << 1)
+#define URB_BUF (1 << 2)
+ unsigned long flags;
+
+ const struct rtl2832_config *cfg;
+ struct dvb_frontend *fe;
+ struct dvb_usb_device *d;
+ struct i2c_adapter *i2c;
+ u8 bank;
+
+ struct video_device vdev;
+ struct v4l2_device v4l2_dev;
+
+ /* videobuf2 queue and queued buffers list */
+ struct vb2_queue vb_queue;
+ struct list_head queued_bufs;
+ spinlock_t queued_bufs_lock; /* Protects queued_bufs */
+ unsigned sequence; /* buffer sequence counter */
+
+ /* Note if taking both locks v4l2_lock must always be locked first! */
+ struct mutex v4l2_lock; /* Protects everything else */
+ struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */
+
+ /* Pointer to our usb_device, will be NULL after unplug */
+ struct usb_device *udev; /* Both mutexes most be hold when setting! */
+
+ unsigned int vb_full; /* vb is full and packets dropped */
+
+ struct urb *urb_list[MAX_BULK_BUFS];
+ int buf_num;
+ unsigned long buf_size;
+ u8 *buf_list[MAX_BULK_BUFS];
+ dma_addr_t dma_addr[MAX_BULK_BUFS];
+ int urbs_initialized;
+ int urbs_submitted;
+
+ unsigned int f_adc, f_tuner;
+ u32 pixelformat;
+
+ /* Controls */
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *bandwidth_auto;
+ struct v4l2_ctrl *bandwidth;
+
+ /* for sample rate calc */
+ unsigned int sample;
+ unsigned int sample_measured;
+ unsigned long jiffies_next;
+};
+
+/* write multiple hardware registers */
+static int rtl2832_sdr_wr(struct rtl2832_sdr_state *s, u8 reg, const u8 *val,
+ int len)
+{
+ int ret;
+#define MAX_WR_LEN 24
+#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1)
+ u8 buf[MAX_WR_XFER_LEN];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = s->cfg->i2c_addr,
+ .flags = 0,
+ .len = 1 + len,
+ .buf = buf,
+ }
+ };
+
+ if (WARN_ON(len > MAX_WR_LEN))
+ return -EINVAL;
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(s->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_err(&s->i2c->dev,
+ "%s: I2C wr failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple hardware registers */
+static int rtl2832_sdr_rd(struct rtl2832_sdr_state *s, u8 reg, u8 *val, int len)
+{
+ int ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = s->cfg->i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = s->cfg->i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ ret = i2c_transfer(s->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ dev_err(&s->i2c->dev,
+ "%s: I2C rd failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* write multiple registers */
+static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_state *s, u16 reg,
+ const u8 *val, int len)
+{
+ int ret;
+ u8 reg2 = (reg >> 0) & 0xff;
+ u8 bank = (reg >> 8) & 0xff;
+
+ /* switch bank if needed */
+ if (bank != s->bank) {
+ ret = rtl2832_sdr_wr(s, 0x00, &bank, 1);
+ if (ret)
+ return ret;
+
+ s->bank = bank;
+ }
+
+ return rtl2832_sdr_wr(s, reg2, val, len);
+}
+
+/* read multiple registers */
+static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_state *s, u16 reg, u8 *val,
+ int len)
+{
+ int ret;
+ u8 reg2 = (reg >> 0) & 0xff;
+ u8 bank = (reg >> 8) & 0xff;
+
+ /* switch bank if needed */
+ if (bank != s->bank) {
+ ret = rtl2832_sdr_wr(s, 0x00, &bank, 1);
+ if (ret)
+ return ret;
+
+ s->bank = bank;
+ }
+
+ return rtl2832_sdr_rd(s, reg2, val, len);
+}
+
+/* write single register */
+static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_state *s, u16 reg, u8 val)
+{
+ return rtl2832_sdr_wr_regs(s, reg, &val, 1);
+}
+
+#if 0
+/* read single register */
+static int rtl2832_sdr_rd_reg(struct rtl2832_sdr_state *s, u16 reg, u8 *val)
+{
+ return rtl2832_sdr_rd_regs(s, reg, val, 1);
+}
+#endif
+
+/* write single register with mask */
+static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_state *s, u16 reg,
+ u8 val, u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return rtl2832_sdr_wr_regs(s, reg, &val, 1);
+}
+
+#if 0
+/* read single register with mask */
+static int rtl2832_sdr_rd_reg_mask(struct rtl2832_sdr_state *s, u16 reg,
+ u8 *val, u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+#endif
+
+/* Private functions */
+static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf(
+ struct rtl2832_sdr_state *s)
+{
+ unsigned long flags = 0;
+ struct rtl2832_sdr_frame_buf *buf = NULL;
+
+ spin_lock_irqsave(&s->queued_bufs_lock, flags);
+ if (list_empty(&s->queued_bufs))
+ goto leave;
+
+ buf = list_entry(s->queued_bufs.next,
+ struct rtl2832_sdr_frame_buf, list);
+ list_del(&buf->list);
+leave:
+ spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
+ return buf;
+}
+
+static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s,
+ void *dst, const u8 *src, unsigned int src_len)
+{
+ unsigned int dst_len;
+
+ if (s->pixelformat == V4L2_SDR_FMT_CU8) {
+ /* native stream, no need to convert */
+ memcpy(dst, src, src_len);
+ dst_len = src_len;
+ } else if (s->pixelformat == V4L2_SDR_FMT_CU16LE) {
+ /* convert u8 to u16 */
+ unsigned int i;
+ u16 *u16dst = dst;
+ for (i = 0; i < src_len; i++)
+ *u16dst++ = (src[i] << 8) | (src[i] >> 0);
+ dst_len = 2 * src_len;
+ } else {
+ dst_len = 0;
+ }
+
+ /* calculate samping rate and output it in 10 seconds intervals */
+ if (unlikely(time_is_before_jiffies(s->jiffies_next))) {
+#define MSECS 10000UL
+ unsigned int samples = s->sample - s->sample_measured;
+ s->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
+ s->sample_measured = s->sample;
+ dev_dbg(&s->udev->dev,
+ "slen=%d samples=%u msecs=%lu sampling rate=%lu\n",
+ src_len, samples, MSECS,
+ samples * 1000UL / MSECS);
+ }
+
+ /* total number of I+Q pairs */
+ s->sample += src_len / 2;
+
+ return dst_len;
+}
+
+/*
+ * This gets called for the bulk stream pipe. This is done in interrupt
+ * time, so it has to be fast, not crash, and not stall. Neat.
+ */
+static void rtl2832_sdr_urb_complete(struct urb *urb)
+{
+ struct rtl2832_sdr_state *s = urb->context;
+ struct rtl2832_sdr_frame_buf *fbuf;
+
+ dev_dbg_ratelimited(&s->udev->dev,
+ "%s: status=%d length=%d/%d errors=%d\n",
+ __func__, urb->status, urb->actual_length,
+ urb->transfer_buffer_length, urb->error_count);
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ dev_err_ratelimited(&s->udev->dev, "urb failed=%d\n",
+ urb->status);
+ break;
+ }
+
+ if (likely(urb->actual_length > 0)) {
+ void *ptr;
+ unsigned int len;
+ /* get free framebuffer */
+ fbuf = rtl2832_sdr_get_next_fill_buf(s);
+ if (unlikely(fbuf == NULL)) {
+ s->vb_full++;
+ dev_notice_ratelimited(&s->udev->dev,
+ "videobuf is full, %d packets dropped\n",
+ s->vb_full);
+ goto skip;
+ }
+
+ /* fill framebuffer */
+ ptr = vb2_plane_vaddr(&fbuf->vb, 0);
+ len = rtl2832_sdr_convert_stream(s, ptr, urb->transfer_buffer,
+ urb->actual_length);
+ vb2_set_plane_payload(&fbuf->vb, 0, len);
+ v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp);
+ fbuf->vb.v4l2_buf.sequence = s->sequence++;
+ vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
+ }
+skip:
+ usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_state *s)
+{
+ int i;
+
+ for (i = s->urbs_submitted - 1; i >= 0; i--) {
+ dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i);
+ /* stop the URB */
+ usb_kill_urb(s->urb_list[i]);
+ }
+ s->urbs_submitted = 0;
+
+ return 0;
+}
+
+static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_state *s)
+{
+ int i, ret;
+
+ for (i = 0; i < s->urbs_initialized; i++) {
+ dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i);
+ ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC);
+ if (ret) {
+ dev_err(&s->udev->dev,
+ "Could not submit urb no. %d - get them all back\n",
+ i);
+ rtl2832_sdr_kill_urbs(s);
+ return ret;
+ }
+ s->urbs_submitted++;
+ }
+
+ return 0;
+}
+
+static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_state *s)
+{
+ if (s->flags & USB_STATE_URB_BUF) {
+ while (s->buf_num) {
+ s->buf_num--;
+ dev_dbg(&s->udev->dev, "%s: free buf=%d\n",
+ __func__, s->buf_num);
+ usb_free_coherent(s->udev, s->buf_size,
+ s->buf_list[s->buf_num],
+ s->dma_addr[s->buf_num]);
+ }
+ }
+ s->flags &= ~USB_STATE_URB_BUF;
+
+ return 0;
+}
+
+static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_state *s)
+{
+ s->buf_num = 0;
+ s->buf_size = BULK_BUFFER_SIZE;
+
+ dev_dbg(&s->udev->dev,
+ "%s: all in all I will use %u bytes for streaming\n",
+ __func__, MAX_BULK_BUFS * BULK_BUFFER_SIZE);
+
+ for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) {
+ s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev,
+ BULK_BUFFER_SIZE, GFP_ATOMIC,
+ &s->dma_addr[s->buf_num]);
+ if (!s->buf_list[s->buf_num]) {
+ dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n",
+ __func__, s->buf_num);
+ rtl2832_sdr_free_stream_bufs(s);
+ return -ENOMEM;
+ }
+
+ dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n",
+ __func__, s->buf_num,
+ s->buf_list[s->buf_num],
+ (long long)s->dma_addr[s->buf_num]);
+ s->flags |= USB_STATE_URB_BUF;
+ }
+
+ return 0;
+}
+
+static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_state *s)
+{
+ int i;
+
+ rtl2832_sdr_kill_urbs(s);
+
+ for (i = s->urbs_initialized - 1; i >= 0; i--) {
+ if (s->urb_list[i]) {
+ dev_dbg(&s->udev->dev, "%s: free urb=%d\n",
+ __func__, i);
+ /* free the URBs */
+ usb_free_urb(s->urb_list[i]);
+ }
+ }
+ s->urbs_initialized = 0;
+
+ return 0;
+}
+
+static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s)
+{
+ int i, j;
+
+ /* allocate the URBs */
+ for (i = 0; i < MAX_BULK_BUFS; i++) {
+ dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i);
+ s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!s->urb_list[i]) {
+ dev_dbg(&s->udev->dev, "%s: failed\n", __func__);
+ for (j = 0; j < i; j++)
+ usb_free_urb(s->urb_list[j]);
+ return -ENOMEM;
+ }
+ usb_fill_bulk_urb(s->urb_list[i],
+ s->udev,
+ usb_rcvbulkpipe(s->udev, 0x81),
+ s->buf_list[i],
+ BULK_BUFFER_SIZE,
+ rtl2832_sdr_urb_complete, s);
+
+ s->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ s->urb_list[i]->transfer_dma = s->dma_addr[i];
+ s->urbs_initialized++;
+ }
+
+ return 0;
+}
+
+/* Must be called with vb_queue_lock hold */
+static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s)
+{
+ unsigned long flags = 0;
+ dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+ spin_lock_irqsave(&s->queued_bufs_lock, flags);
+ while (!list_empty(&s->queued_bufs)) {
+ struct rtl2832_sdr_frame_buf *buf;
+ buf = list_entry(s->queued_bufs.next,
+ struct rtl2832_sdr_frame_buf, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
+}
+
+/* The user yanked out the cable... */
+static void rtl2832_sdr_release_sec(struct dvb_frontend *fe)
+{
+ struct rtl2832_sdr_state *s = fe->sec_priv;
+ dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+ mutex_lock(&s->vb_queue_lock);
+ mutex_lock(&s->v4l2_lock);
+ /* No need to keep the urbs around after disconnection */
+ s->udev = NULL;
+
+ v4l2_device_disconnect(&s->v4l2_dev);
+ video_unregister_device(&s->vdev);
+ mutex_unlock(&s->v4l2_lock);
+ mutex_unlock(&s->vb_queue_lock);
+
+ v4l2_device_put(&s->v4l2_dev);
+
+ fe->sec_priv = NULL;
+}
+
+static int rtl2832_sdr_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct rtl2832_sdr_state *s = video_drvdata(file);
+ dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+ strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strlcpy(cap->card, s->vdev.name, sizeof(cap->card));
+ usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE | V4L2_CAP_TUNER;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+/* Videobuf2 operations */
+static int rtl2832_sdr_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+ dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers);
+
+ /* Need at least 8 buffers */
+ if (vq->num_buffers + *nbuffers < 8)
+ *nbuffers = 8 - vq->num_buffers;
+ *nplanes = 1;
+ /* 2 = max 16-bit sample returned */
+ sizes[0] = PAGE_ALIGN(BULK_BUFFER_SIZE * 2);
+ dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n",
+ __func__, *nbuffers, sizes[0]);
+ return 0;
+}
+
+static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb)
+{
+ struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue);
+
+ /* Don't allow queing new buffers after device disconnection */
+ if (!s->udev)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb)
+{
+ struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue);
+ struct rtl2832_sdr_frame_buf *buf =
+ container_of(vb, struct rtl2832_sdr_frame_buf, vb);
+ unsigned long flags = 0;
+
+ /* Check the device has not disconnected between prep and queuing */
+ if (!s->udev) {
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ return;
+ }
+
+ spin_lock_irqsave(&s->queued_bufs_lock, flags);
+ list_add_tail(&buf->list, &s->queued_bufs);
+ spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
+}
+
+static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s)
+{
+ struct dvb_frontend *fe = s->fe;
+ int ret;
+ unsigned int f_sr, f_if;
+ u8 buf[4], u8tmp1, u8tmp2;
+ u64 u64tmp;
+ u32 u32tmp;
+ dev_dbg(&s->udev->dev, "%s: f_adc=%u\n", __func__, s->f_adc);
+
+ if (!test_bit(POWER_ON, &s->flags))
+ return 0;
+
+ if (s->f_adc == 0)
+ return 0;
+
+ f_sr = s->f_adc;
+
+ ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x00\x00", 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_wr_regs(s, 0x115, "\x00\x00\x00\x00", 4);
+ if (ret)
+ goto err;
+
+ /* get IF from tuner */
+ if (fe->ops.tuner_ops.get_if_frequency)
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &f_if);
+ else
+ ret = -EINVAL;
+
+ if (ret)
+ goto err;
+
+ /* program IF */
+ u64tmp = f_if % s->cfg->xtal;
+ u64tmp *= 0x400000;
+ u64tmp = div_u64(u64tmp, s->cfg->xtal);
+ u64tmp = -u64tmp;
+ u32tmp = u64tmp & 0x3fffff;
+
+ dev_dbg(&s->udev->dev, "%s: f_if=%u if_ctl=%08x\n",
+ __func__, f_if, u32tmp);
+
+ buf[0] = (u32tmp >> 16) & 0xff;
+ buf[1] = (u32tmp >> 8) & 0xff;
+ buf[2] = (u32tmp >> 0) & 0xff;
+
+ ret = rtl2832_sdr_wr_regs(s, 0x119, buf, 3);
+ if (ret)
+ goto err;
+
+ /* BB / IF mode */
+ /* POR: 0x1b1=0x1f, 0x008=0x0d, 0x006=0x80 */
+ if (f_if) {
+ u8tmp1 = 0x1a; /* disable Zero-IF */
+ u8tmp2 = 0x8d; /* enable ADC I */
+ } else {
+ u8tmp1 = 0x1b; /* enable Zero-IF, DC, IQ */
+ u8tmp2 = 0xcd; /* enable ADC I, ADC Q */
+ }
+
+ ret = rtl2832_sdr_wr_reg(s, 0x1b1, u8tmp1);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_wr_reg(s, 0x008, u8tmp2);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_wr_reg(s, 0x006, 0x80);
+ if (ret)
+ goto err;
+
+ /* program sampling rate (resampling down) */
+ u32tmp = div_u64(s->cfg->xtal * 0x400000ULL, f_sr * 4U);
+ u32tmp <<= 2;
+ buf[0] = (u32tmp >> 24) & 0xff;
+ buf[1] = (u32tmp >> 16) & 0xff;
+ buf[2] = (u32tmp >> 8) & 0xff;
+ buf[3] = (u32tmp >> 0) & 0xff;
+ ret = rtl2832_sdr_wr_regs(s, 0x19f, buf, 4);
+ if (ret)
+ goto err;
+
+ /* low-pass filter */
+ ret = rtl2832_sdr_wr_regs(s, 0x11c,
+ "\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5",
+ 20);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2);
+ if (ret)
+ goto err;
+
+ /* mode */
+ ret = rtl2832_sdr_wr_regs(s, 0x019, "\x05", 1);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_wr_regs(s, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6);
+ if (ret)
+ goto err;
+
+ /* FSM */
+ ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\xf0\x0f", 3);
+ if (ret)
+ goto err;
+
+ /* PID filter */
+ ret = rtl2832_sdr_wr_regs(s, 0x061, "\x60", 1);
+ if (ret)
+ goto err;
+
+ /* used RF tuner based settings */
+ switch (s->cfg->tuner) {
+ case RTL2832_TUNER_E4000:
+ ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x30", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x104, "\xd0", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x18", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x011, "\xd4", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x14", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xec", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x83", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x010, "\x49", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x87", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x85", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x013, "\x02", 1);
+ break;
+ case RTL2832_TUNER_FC0012:
+ case RTL2832_TUNER_FC0013:
+ ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x2c", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x16", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x011, "\xe9\xbf", 2);
+ ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x11", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xef", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1);
+ break;
+ case RTL2832_TUNER_R820T:
+ ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x115, "\x01", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x103, "\x80", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x24", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x14", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+ ret = rtl2832_sdr_wr_regs(s, 0x011, "\xf4", 1);
+ break;
+ default:
+ dev_notice(&s->udev->dev, "Unsupported tuner\n");
+ }
+
+ /* software reset */
+ ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x04, 0x04);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x00, 0x04);
+ if (ret)
+ goto err;
+err:
+ return ret;
+};
+
+static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_state *s)
+{
+ int ret;
+
+ dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+ /* PID filter */
+ ret = rtl2832_sdr_wr_regs(s, 0x061, "\xe0", 1);
+ if (ret)
+ goto err;
+
+ /* mode */
+ ret = rtl2832_sdr_wr_regs(s, 0x019, "\x20", 1);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2);
+ if (ret)
+ goto err;
+
+ /* FSM */
+ ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\x0f\xff", 3);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x40\x00", 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_wr_regs(s, 0x115, "\x06\x3f\xce\xcc", 4);
+ if (ret)
+ goto err;
+err:
+ return;
+};
+
+static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s)
+{
+ struct dvb_frontend *fe = s->fe;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct v4l2_ctrl *bandwidth_auto;
+ struct v4l2_ctrl *bandwidth;
+
+ /*
+ * tuner RF (Hz)
+ */
+ if (s->f_tuner == 0)
+ return 0;
+
+ /*
+ * bandwidth (Hz)
+ */
+ bandwidth_auto = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO);
+ bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH);
+ if (v4l2_ctrl_g_ctrl(bandwidth_auto)) {
+ c->bandwidth_hz = s->f_adc;
+ v4l2_ctrl_s_ctrl(bandwidth, s->f_adc);
+ } else {
+ c->bandwidth_hz = v4l2_ctrl_g_ctrl(bandwidth);
+ }
+
+ c->frequency = s->f_tuner;
+ c->delivery_system = SYS_DVBT;
+
+ dev_dbg(&s->udev->dev, "%s: frequency=%u bandwidth=%d\n",
+ __func__, c->frequency, c->bandwidth_hz);
+
+ if (!test_bit(POWER_ON, &s->flags))
+ return 0;
+
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ return 0;
+};
+
+static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_state *s)
+{
+ struct dvb_frontend *fe = s->fe;
+
+ dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+ if (fe->ops.tuner_ops.init)
+ fe->ops.tuner_ops.init(fe);
+
+ return 0;
+};
+
+static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_state *s)
+{
+ struct dvb_frontend *fe = s->fe;
+
+ dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+ if (fe->ops.tuner_ops.sleep)
+ fe->ops.tuner_ops.sleep(fe);
+
+ return;
+};
+
+static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+ int ret;
+ dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+ if (!s->udev)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&s->v4l2_lock))
+ return -ERESTARTSYS;
+
+ if (s->d->props->power_ctrl)
+ s->d->props->power_ctrl(s->d, 1);
+
+ set_bit(POWER_ON, &s->flags);
+
+ ret = rtl2832_sdr_set_tuner(s);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_set_tuner_freq(s);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_set_adc(s);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_alloc_stream_bufs(s);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_alloc_urbs(s);
+ if (ret)
+ goto err;
+
+ s->sequence = 0;
+
+ ret = rtl2832_sdr_submit_urbs(s);
+ if (ret)
+ goto err;
+
+err:
+ mutex_unlock(&s->v4l2_lock);
+
+ return ret;
+}
+
+static int rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
+{
+ struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+ dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+ if (mutex_lock_interruptible(&s->v4l2_lock))
+ return -ERESTARTSYS;
+
+ rtl2832_sdr_kill_urbs(s);
+ rtl2832_sdr_free_urbs(s);
+ rtl2832_sdr_free_stream_bufs(s);
+ rtl2832_sdr_cleanup_queued_bufs(s);
+ rtl2832_sdr_unset_adc(s);
+ rtl2832_sdr_unset_tuner(s);
+
+ clear_bit(POWER_ON, &s->flags);
+
+ if (s->d->props->power_ctrl)
+ s->d->props->power_ctrl(s->d, 0);
+
+ mutex_unlock(&s->v4l2_lock);
+
+ return 0;
+}
+
+static struct vb2_ops rtl2832_sdr_vb2_ops = {
+ .queue_setup = rtl2832_sdr_queue_setup,
+ .buf_prepare = rtl2832_sdr_buf_prepare,
+ .buf_queue = rtl2832_sdr_buf_queue,
+ .start_streaming = rtl2832_sdr_start_streaming,
+ .stop_streaming = rtl2832_sdr_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int rtl2832_sdr_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct rtl2832_sdr_state *s = video_drvdata(file);
+ dev_dbg(&s->udev->dev, "%s: index=%d type=%d\n",
+ __func__, v->index, v->type);
+
+ if (v->index == 0) {
+ strlcpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name));
+ v->type = V4L2_TUNER_ADC;
+ v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+ v->rangelow = 300000;
+ v->rangehigh = 3200000;
+ } else if (v->index == 1) {
+ strlcpy(v->name, "RF: <unknown>", sizeof(v->name));
+ v->type = V4L2_TUNER_RF;
+ v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+ v->rangelow = 50000000;
+ v->rangehigh = 2000000000;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rtl2832_sdr_s_tuner(struct file *file, void *priv,
+ const struct v4l2_tuner *v)
+{
+ struct rtl2832_sdr_state *s = video_drvdata(file);
+ dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+ if (v->index > 1)
+ return -EINVAL;
+ return 0;
+}
+
+static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv,
+ struct v4l2_frequency_band *band)
+{
+ struct rtl2832_sdr_state *s = video_drvdata(file);
+ dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n",
+ __func__, band->tuner, band->type, band->index);
+
+ if (band->tuner == 0) {
+ if (band->index >= ARRAY_SIZE(bands_adc))
+ return -EINVAL;
+
+ *band = bands_adc[band->index];
+ } else if (band->tuner == 1) {
+ if (band->index >= ARRAY_SIZE(bands_fm))
+ return -EINVAL;
+
+ *band = bands_fm[band->index];
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rtl2832_sdr_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct rtl2832_sdr_state *s = video_drvdata(file);
+ int ret = 0;
+ dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n",
+ __func__, f->tuner, f->type);
+
+ if (f->tuner == 0) {
+ f->frequency = s->f_adc;
+ f->type = V4L2_TUNER_ADC;
+ } else if (f->tuner == 1) {
+ f->frequency = s->f_tuner;
+ f->type = V4L2_TUNER_RF;
+ } else {
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int rtl2832_sdr_s_frequency(struct file *file, void *priv,
+ const struct v4l2_frequency *f)
+{
+ struct rtl2832_sdr_state *s = video_drvdata(file);
+ int ret, band;
+
+ dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n",
+ __func__, f->tuner, f->type, f->frequency);
+
+ /* ADC band midpoints */
+ #define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2)
+ #define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2)
+
+ if (f->tuner == 0 && f->type == V4L2_TUNER_ADC) {
+ if (f->frequency < BAND_ADC_0)
+ band = 0;
+ else if (f->frequency < BAND_ADC_1)
+ band = 1;
+ else
+ band = 2;
+
+ s->f_adc = clamp_t(unsigned int, f->frequency,
+ bands_adc[band].rangelow,
+ bands_adc[band].rangehigh);
+
+ dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n",
+ __func__, s->f_adc);
+ ret = rtl2832_sdr_set_adc(s);
+ } else if (f->tuner == 1) {
+ s->f_tuner = clamp_t(unsigned int, f->frequency,
+ bands_fm[0].rangelow,
+ bands_fm[0].rangehigh);
+ dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n",
+ __func__, f->frequency);
+
+ ret = rtl2832_sdr_set_tuner_freq(s);
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct rtl2832_sdr_state *s = video_drvdata(file);
+ dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+ if (f->index >= NUM_FORMATS)
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name, sizeof(f->description));
+ f->pixelformat = formats[f->index].pixelformat;
+
+ return 0;
+}
+
+static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rtl2832_sdr_state *s = video_drvdata(file);
+ dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+ f->fmt.sdr.pixelformat = s->pixelformat;
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+
+ return 0;
+}
+
+static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rtl2832_sdr_state *s = video_drvdata(file);
+ struct vb2_queue *q = &s->vb_queue;
+ int i;
+ dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+ (char *)&f->fmt.sdr.pixelformat);
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+ s->pixelformat = f->fmt.sdr.pixelformat;
+ return 0;
+ }
+ }
+
+ f->fmt.sdr.pixelformat = formats[0].pixelformat;
+ s->pixelformat = formats[0].pixelformat;
+
+ return 0;
+}
+
+static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rtl2832_sdr_state *s = video_drvdata(file);
+ int i;
+ dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+ (char *)&f->fmt.sdr.pixelformat);
+
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].pixelformat == f->fmt.sdr.pixelformat)
+ return 0;
+ }
+
+ f->fmt.sdr.pixelformat = formats[0].pixelformat;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops rtl2832_sdr_ioctl_ops = {
+ .vidioc_querycap = rtl2832_sdr_querycap,
+
+ .vidioc_enum_fmt_sdr_cap = rtl2832_sdr_enum_fmt_sdr_cap,
+ .vidioc_g_fmt_sdr_cap = rtl2832_sdr_g_fmt_sdr_cap,
+ .vidioc_s_fmt_sdr_cap = rtl2832_sdr_s_fmt_sdr_cap,
+ .vidioc_try_fmt_sdr_cap = rtl2832_sdr_try_fmt_sdr_cap,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_g_tuner = rtl2832_sdr_g_tuner,
+ .vidioc_s_tuner = rtl2832_sdr_s_tuner,
+
+ .vidioc_enum_freq_bands = rtl2832_sdr_enum_freq_bands,
+ .vidioc_g_frequency = rtl2832_sdr_g_frequency,
+ .vidioc_s_frequency = rtl2832_sdr_s_frequency,
+
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+};
+
+static const struct v4l2_file_operations rtl2832_sdr_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static struct video_device rtl2832_sdr_template = {
+ .name = "Realtek RTL2832 SDR",
+ .release = video_device_release_empty,
+ .fops = &rtl2832_sdr_fops,
+ .ioctl_ops = &rtl2832_sdr_ioctl_ops,
+};
+
+static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct rtl2832_sdr_state *s =
+ container_of(ctrl->handler, struct rtl2832_sdr_state,
+ hdl);
+ struct dvb_frontend *fe = s->fe;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ dev_dbg(&s->udev->dev,
+ "%s: id=%d name=%s val=%d min=%d max=%d step=%d\n",
+ __func__, ctrl->id, ctrl->name, ctrl->val,
+ ctrl->minimum, ctrl->maximum, ctrl->step);
+
+ switch (ctrl->id) {
+ case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
+ case V4L2_CID_RF_TUNER_BANDWIDTH:
+ /* TODO: these controls should be moved to tuner drivers */
+ if (s->bandwidth_auto->val) {
+ /* Round towards the closest legal value */
+ s32 val = s->f_adc + s->bandwidth->step / 2;
+ u32 offset;
+ val = clamp(val, s->bandwidth->minimum, s->bandwidth->maximum);
+ offset = val - s->bandwidth->minimum;
+ offset = s->bandwidth->step * (offset / s->bandwidth->step);
+ s->bandwidth->val = s->bandwidth->minimum + offset;
+ }
+
+ c->bandwidth_hz = s->bandwidth->val;
+
+ if (!test_bit(POWER_ON, &s->flags))
+ return 0;
+
+ if (fe->ops.tuner_ops.set_params)
+ ret = fe->ops.tuner_ops.set_params(fe);
+ else
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops rtl2832_sdr_ctrl_ops = {
+ .s_ctrl = rtl2832_sdr_s_ctrl,
+};
+
+static void rtl2832_sdr_video_release(struct v4l2_device *v)
+{
+ struct rtl2832_sdr_state *s =
+ container_of(v, struct rtl2832_sdr_state, v4l2_dev);
+
+ v4l2_ctrl_handler_free(&s->hdl);
+ v4l2_device_unregister(&s->v4l2_dev);
+ kfree(s);
+}
+
+struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
+ struct v4l2_subdev *sd)
+{
+ int ret;
+ struct rtl2832_sdr_state *s;
+ const struct v4l2_ctrl_ops *ops = &rtl2832_sdr_ctrl_ops;
+ struct dvb_usb_device *d = i2c_get_adapdata(i2c);
+
+ s = kzalloc(sizeof(struct rtl2832_sdr_state), GFP_KERNEL);
+ if (s == NULL) {
+ dev_err(&d->udev->dev,
+ "Could not allocate memory for rtl2832_sdr_state\n");
+ return NULL;
+ }
+
+ /* setup the state */
+ s->fe = fe;
+ s->d = d;
+ s->udev = d->udev;
+ s->i2c = i2c;
+ s->cfg = cfg;
+ s->f_adc = bands_adc[0].rangelow;
+ s->f_tuner = bands_fm[0].rangelow;
+ s->pixelformat = V4L2_SDR_FMT_CU8;
+
+ mutex_init(&s->v4l2_lock);
+ mutex_init(&s->vb_queue_lock);
+ spin_lock_init(&s->queued_bufs_lock);
+ INIT_LIST_HEAD(&s->queued_bufs);
+
+ /* Init videobuf2 queue structure */
+ s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
+ s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+ s->vb_queue.drv_priv = s;
+ s->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf);
+ s->vb_queue.ops = &rtl2832_sdr_vb2_ops;
+ s->vb_queue.mem_ops = &vb2_vmalloc_memops;
+ s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ ret = vb2_queue_init(&s->vb_queue);
+ if (ret) {
+ dev_err(&s->udev->dev, "Could not initialize vb2 queue\n");
+ goto err_free_mem;
+ }
+
+ /* Register controls */
+ switch (s->cfg->tuner) {
+ case RTL2832_TUNER_E4000:
+ v4l2_ctrl_handler_init(&s->hdl, 9);
+ if (sd)
+ v4l2_ctrl_add_handler(&s->hdl, sd->ctrl_handler, NULL);
+ break;
+ case RTL2832_TUNER_R820T:
+ v4l2_ctrl_handler_init(&s->hdl, 2);
+ s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
+ s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH, 0, 8000000, 100000, 0);
+ v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+ break;
+ case RTL2832_TUNER_FC0012:
+ case RTL2832_TUNER_FC0013:
+ v4l2_ctrl_handler_init(&s->hdl, 2);
+ s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
+ s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH, 6000000, 8000000, 1000000, 6000000);
+ v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+ break;
+ default:
+ v4l2_ctrl_handler_init(&s->hdl, 0);
+ dev_notice(&s->udev->dev, "%s: Unsupported tuner\n",
+ KBUILD_MODNAME);
+ goto err_free_controls;
+ }
+
+ if (s->hdl.error) {
+ ret = s->hdl.error;
+ dev_err(&s->udev->dev, "Could not initialize controls\n");
+ goto err_free_controls;
+ }
+
+ /* Init video_device structure */
+ s->vdev = rtl2832_sdr_template;
+ s->vdev.queue = &s->vb_queue;
+ s->vdev.queue->lock = &s->vb_queue_lock;
+ set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev.flags);
+ video_set_drvdata(&s->vdev, s);
+
+ /* Register the v4l2_device structure */
+ s->v4l2_dev.release = rtl2832_sdr_video_release;
+ ret = v4l2_device_register(&s->udev->dev, &s->v4l2_dev);
+ if (ret) {
+ dev_err(&s->udev->dev,
+ "Failed to register v4l2-device (%d)\n", ret);
+ goto err_free_controls;
+ }
+
+ s->v4l2_dev.ctrl_handler = &s->hdl;
+ s->vdev.v4l2_dev = &s->v4l2_dev;
+ s->vdev.lock = &s->v4l2_lock;
+ s->vdev.vfl_dir = VFL_DIR_RX;
+
+ ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1);
+ if (ret) {
+ dev_err(&s->udev->dev,
+ "Failed to register as video device (%d)\n",
+ ret);
+ goto err_unregister_v4l2_dev;
+ }
+ dev_info(&s->udev->dev, "Registered as %s\n",
+ video_device_node_name(&s->vdev));
+
+ fe->sec_priv = s;
+ fe->ops.release_sec = rtl2832_sdr_release_sec;
+
+ dev_info(&s->i2c->dev, "%s: Realtek RTL2832 SDR attached\n",
+ KBUILD_MODNAME);
+ return fe;
+
+err_unregister_v4l2_dev:
+ v4l2_device_unregister(&s->v4l2_dev);
+err_free_controls:
+ v4l2_ctrl_handler_free(&s->hdl);
+err_free_mem:
+ kfree(s);
+ return NULL;
+}
+EXPORT_SYMBOL(rtl2832_sdr_attach);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Realtek RTL2832 SDR driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h
new file mode 100644
index 000000000000..b865fadf184f
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h
@@ -0,0 +1,54 @@
+/*
+ * Realtek RTL2832U SDR driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * GNU Radio plugin "gr-kernel" for device usage will be on:
+ * http://git.linuxtv.org/anttip/gr-kernel.git
+ *
+ * TODO:
+ * Help is very highly welcome for these + all the others you could imagine:
+ * - move controls to V4L2 API
+ * - use libv4l2 for stream format conversions
+ * - gr-kernel: switch to v4l2_mmap (current read eats a lot of cpu)
+ * - SDRSharp support
+ */
+
+#ifndef RTL2832_SDR_H
+#define RTL2832_SDR_H
+
+#include <linux/kconfig.h>
+#include <media/v4l2-subdev.h>
+
+/* for config struct */
+#include "rtl2832.h"
+
+#if IS_ENABLED(CONFIG_DVB_RTL2832_SDR)
+extern struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
+ struct v4l2_subdev *sd);
+#else
+static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
+ struct v4l2_subdev *sd)
+{
+ dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* RTL2832_SDR_H */
diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c
index 5aeb9c0c2781..2cbe088f1697 100644
--- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c
@@ -1295,7 +1295,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
solo_enc->vidq.mem_ops = &vb2_dma_sg_memops;
solo_enc->vidq.drv_priv = solo_enc;
solo_enc->vidq.gfp_flags = __GFP_DMA32;
- solo_enc->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ solo_enc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
solo_enc->vidq.buf_struct_size = sizeof(struct solo_vb2_buf);
solo_enc->vidq.lock = &solo_enc->lock;
ret = vb2_queue_init(&solo_enc->vidq);
diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2.c b/drivers/staging/media/solo6x10/solo6x10-v4l2.c
index 47e72dac9b13..1815f765d033 100644
--- a/drivers/staging/media/solo6x10/solo6x10-v4l2.c
+++ b/drivers/staging/media/solo6x10/solo6x10-v4l2.c
@@ -676,7 +676,7 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
solo_dev->vidq.ops = &solo_video_qops;
solo_dev->vidq.mem_ops = &vb2_dma_contig_memops;
solo_dev->vidq.drv_priv = solo_dev;
- solo_dev->vidq.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ solo_dev->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
solo_dev->vidq.gfp_flags = __GFP_DMA32;
solo_dev->vidq.buf_struct_size = sizeof(struct solo_vb2_buf);
solo_dev->vidq.lock = &solo_dev->lock;
diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c
index 8ca55c4e9db2..4e54d8540219 100644
--- a/drivers/staging/octeon/ethernet-tx.c
+++ b/drivers/staging/octeon/ethernet-tx.c
@@ -554,7 +554,7 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
printk_ratelimited("%s: Failed to allocate a work queue entry\n",
dev->name);
priv->stats.tx_dropped++;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return 0;
}
@@ -565,7 +565,7 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
dev->name);
cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
priv->stats.tx_dropped++;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return 0;
}
@@ -682,7 +682,7 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
work->grp);
priv->stats.tx_packets++;
priv->stats.tx_bytes += skb->len;
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
return 0;
}
diff --git a/drivers/staging/rtl8187se/Kconfig b/drivers/staging/rtl8187se/Kconfig
index 3162aabbeb07..ff8d41ebca36 100644
--- a/drivers/staging/rtl8187se/Kconfig
+++ b/drivers/staging/rtl8187se/Kconfig
@@ -6,6 +6,5 @@ config R8187SE
select WEXT_PRIV
select EEPROM_93CX6
select CRYPTO
- default N
---help---
If built as a module, it will be called r8187se.ko.
diff --git a/drivers/staging/rtl8188eu/Kconfig b/drivers/staging/rtl8188eu/Kconfig
index 9a57d31ed248..e45c106c2162 100644
--- a/drivers/staging/rtl8188eu/Kconfig
+++ b/drivers/staging/rtl8188eu/Kconfig
@@ -3,7 +3,6 @@ config R8188EU
depends on WLAN && USB
select WIRELESS_EXT
select WEXT_PRIV
- default N
---help---
This option adds the Realtek RTL8188EU USB device such as TP-Link TL-WN725N.
If built as a module, it will be called r8188eu.
diff --git a/drivers/staging/rtl8192e/rtl8192e/Kconfig b/drivers/staging/rtl8192e/rtl8192e/Kconfig
index 50e0d91a409a..ad82bc348a75 100644
--- a/drivers/staging/rtl8192e/rtl8192e/Kconfig
+++ b/drivers/staging/rtl8192e/rtl8192e/Kconfig
@@ -5,5 +5,4 @@ config RTL8192E
select WIRELESS_EXT
select WEXT_PRIV
select CRYPTO
- default N
---help---
diff --git a/drivers/staging/rtl8192u/Kconfig b/drivers/staging/rtl8192u/Kconfig
index 3f055091b35f..3ee9d0d00fb6 100644
--- a/drivers/staging/rtl8192u/Kconfig
+++ b/drivers/staging/rtl8192u/Kconfig
@@ -5,5 +5,4 @@ config RTL8192U
select WIRELESS_EXT
select WEXT_PRIV
select CRYPTO
- default N
---help---
diff --git a/drivers/staging/rtl8712/Kconfig b/drivers/staging/rtl8712/Kconfig
index 6a43312380e0..f160eee52f09 100644
--- a/drivers/staging/rtl8712/Kconfig
+++ b/drivers/staging/rtl8712/Kconfig
@@ -4,7 +4,6 @@ config R8712U
select WIRELESS_EXT
select WEXT_PRIV
select FW_LOADER
- default N
---help---
This option adds the Realtek RTL8712 USB device such as the D-Link DWA-130.
If built as a module, it will be called r8712u.
@@ -12,7 +11,6 @@ config R8712U
config R8712_TX_AGGR
bool "Realtek RTL8712U Transmit Aggregation code"
depends on R8712U && BROKEN
- default N
---help---
This option provides transmit aggregation for the Realtek RTL8712 USB device.
diff --git a/drivers/staging/rtl8821ae/rc.c b/drivers/staging/rtl8821ae/rc.c
index d387f13ea7dc..0cc32c60ddee 100644
--- a/drivers/staging/rtl8821ae/rc.c
+++ b/drivers/staging/rtl8821ae/rc.c
@@ -286,7 +286,6 @@ static void rtl_rate_free_sta(void *rtlpriv,
}
static struct rate_control_ops rtl_rate_ops = {
- .module = NULL,
.name = "rtl_rc",
.alloc = rtl_rate_alloc,
.free = rtl_rate_free,
diff --git a/drivers/staging/rtl8821ae/rtl8821ae/trx.c b/drivers/staging/rtl8821ae/rtl8821ae/trx.c
index dd0f6dcb3085..f40a93c0d2bb 100644
--- a/drivers/staging/rtl8821ae/rtl8821ae/trx.c
+++ b/drivers/staging/rtl8821ae/rtl8821ae/trx.c
@@ -616,7 +616,7 @@ bool rtl8821ae_rx_query_desc(struct ieee80211_hw *hw,
return false;
}
- if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+ if ((_ieee80211_is_robust_mgmt_frame(hdr)) &&
(ieee80211_has_protected(hdr->frame_control)))
rx_status->flag &= ~RX_FLAG_DECRYPTED;
else
diff --git a/drivers/staging/speakup/kobjects.c b/drivers/staging/speakup/kobjects.c
index e2f597ee6261..1ca91f7092b1 100644
--- a/drivers/staging/speakup/kobjects.c
+++ b/drivers/staging/speakup/kobjects.c
@@ -851,75 +851,75 @@ static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr,
* Declare the attributes.
*/
static struct kobj_attribute keymap_attribute =
- __ATTR(keymap, ROOT_W, keymap_show, keymap_store);
+ __ATTR(keymap, S_IWUSR|S_IRUGO, keymap_show, keymap_store);
static struct kobj_attribute silent_attribute =
- __ATTR(silent, USER_W, NULL, silent_store);
+ __ATTR(silent, S_IWUGO, NULL, silent_store);
static struct kobj_attribute synth_attribute =
- __ATTR(synth, USER_RW, synth_show, synth_store);
+ __ATTR(synth, S_IWUGO|S_IRUGO, synth_show, synth_store);
static struct kobj_attribute synth_direct_attribute =
- __ATTR(synth_direct, USER_W, NULL, synth_direct_store);
+ __ATTR(synth_direct, S_IWUGO, NULL, synth_direct_store);
static struct kobj_attribute version_attribute =
__ATTR_RO(version);
static struct kobj_attribute delimiters_attribute =
- __ATTR(delimiters, USER_RW, punc_show, punc_store);
+ __ATTR(delimiters, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute ex_num_attribute =
- __ATTR(ex_num, USER_RW, punc_show, punc_store);
+ __ATTR(ex_num, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute punc_all_attribute =
- __ATTR(punc_all, USER_RW, punc_show, punc_store);
+ __ATTR(punc_all, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute punc_most_attribute =
- __ATTR(punc_most, USER_RW, punc_show, punc_store);
+ __ATTR(punc_most, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute punc_some_attribute =
- __ATTR(punc_some, USER_RW, punc_show, punc_store);
+ __ATTR(punc_some, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute repeats_attribute =
- __ATTR(repeats, USER_RW, punc_show, punc_store);
+ __ATTR(repeats, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute attrib_bleep_attribute =
- __ATTR(attrib_bleep, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(attrib_bleep, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute bell_pos_attribute =
- __ATTR(bell_pos, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(bell_pos, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute bleep_time_attribute =
- __ATTR(bleep_time, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(bleep_time, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute bleeps_attribute =
- __ATTR(bleeps, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(bleeps, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute cursor_time_attribute =
- __ATTR(cursor_time, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(cursor_time, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute key_echo_attribute =
- __ATTR(key_echo, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(key_echo, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute no_interrupt_attribute =
- __ATTR(no_interrupt, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(no_interrupt, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punc_level_attribute =
- __ATTR(punc_level, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punc_level, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute reading_punc_attribute =
- __ATTR(reading_punc, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(reading_punc, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute say_control_attribute =
- __ATTR(say_control, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(say_control, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute say_word_ctl_attribute =
- __ATTR(say_word_ctl, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(say_word_ctl, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute spell_delay_attribute =
- __ATTR(spell_delay, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(spell_delay, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
/*
* These attributes are i18n related.
*/
static struct kobj_attribute announcements_attribute =
- __ATTR(announcements, USER_RW, message_show, message_store);
+ __ATTR(announcements, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute characters_attribute =
- __ATTR(characters, USER_RW, chars_chartab_show, chars_chartab_store);
+ __ATTR(characters, S_IWUGO|S_IRUGO, chars_chartab_show, chars_chartab_store);
static struct kobj_attribute chartab_attribute =
- __ATTR(chartab, USER_RW, chars_chartab_show, chars_chartab_store);
+ __ATTR(chartab, S_IWUGO|S_IRUGO, chars_chartab_show, chars_chartab_store);
static struct kobj_attribute ctl_keys_attribute =
- __ATTR(ctl_keys, USER_RW, message_show, message_store);
+ __ATTR(ctl_keys, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute colors_attribute =
- __ATTR(colors, USER_RW, message_show, message_store);
+ __ATTR(colors, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute formatted_attribute =
- __ATTR(formatted, USER_RW, message_show, message_store);
+ __ATTR(formatted, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute function_names_attribute =
- __ATTR(function_names, USER_RW, message_show, message_store);
+ __ATTR(function_names, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute key_names_attribute =
- __ATTR(key_names, USER_RW, message_show, message_store);
+ __ATTR(key_names, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute states_attribute =
- __ATTR(states, USER_RW, message_show, message_store);
+ __ATTR(states, S_IWUGO|S_IRUGO, message_show, message_store);
/*
* Create groups of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup.h b/drivers/staging/speakup/speakup.h
index 0126f714821a..a7bcceec436a 100644
--- a/drivers/staging/speakup/speakup.h
+++ b/drivers/staging/speakup/speakup.h
@@ -12,8 +12,6 @@
/* proc permissions */
#define USER_R (S_IFREG|S_IRUGO)
#define USER_W (S_IFREG|S_IWUGO)
-#define USER_RW (S_IFREG|S_IRUGO|S_IWUGO)
-#define ROOT_W (S_IFREG|S_IRUGO|S_IWUSR)
#define TOGGLE_0 .u.n = {NULL, 0, 0, 1, 0, 0, NULL }
#define TOGGLE_1 .u.n = {NULL, 1, 0, 1, 0, 0, NULL }
diff --git a/drivers/staging/speakup/speakup_acntpc.c b/drivers/staging/speakup/speakup_acntpc.c
index 1c8a7f4a0ef5..e7dfa434bd96 100644
--- a/drivers/staging/speakup/speakup_acntpc.c
+++ b/drivers/staging/speakup/speakup_acntpc.c
@@ -62,28 +62,28 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/acntpc.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_acntsa.c b/drivers/staging/speakup/speakup_acntsa.c
index 22a8b7291098..c7f014ed9628 100644
--- a/drivers/staging/speakup/speakup_acntsa.c
+++ b/drivers/staging/speakup/speakup_acntsa.c
@@ -47,28 +47,28 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/acntsa.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IRUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IRUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IRUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IRUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_apollo.c b/drivers/staging/speakup/speakup_apollo.c
index 70cf1591676a..38c8c2221e4e 100644
--- a/drivers/staging/speakup/speakup_apollo.c
+++ b/drivers/staging/speakup/speakup_apollo.c
@@ -53,30 +53,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/apollo.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute lang_attribute =
- __ATTR(lang, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(lang, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_audptr.c b/drivers/staging/speakup/speakup_audptr.c
index 61a3ceeb0d3a..de5b4a5f43b6 100644
--- a/drivers/staging/speakup/speakup_audptr.c
+++ b/drivers/staging/speakup/speakup_audptr.c
@@ -49,30 +49,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/audptr.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_bns.c b/drivers/staging/speakup/speakup_bns.c
index 4bfe3d458dc0..4939e8c7272e 100644
--- a/drivers/staging/speakup/speakup_bns.c
+++ b/drivers/staging/speakup/speakup_bns.c
@@ -44,28 +44,28 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/bns.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_decext.c b/drivers/staging/speakup/speakup_decext.c
index d306e010d3ea..b17af9803929 100644
--- a/drivers/staging/speakup/speakup_decext.c
+++ b/drivers/staging/speakup/speakup_decext.c
@@ -70,30 +70,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/decext.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_decpc.c b/drivers/staging/speakup/speakup_decpc.c
index ea6b72d40b31..cfa4bc032358 100644
--- a/drivers/staging/speakup/speakup_decpc.c
+++ b/drivers/staging/speakup/speakup_decpc.c
@@ -164,30 +164,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/decpc.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_dectlk.c b/drivers/staging/speakup/speakup_dectlk.c
index 756d01535d3e..1fcae55dabba 100644
--- a/drivers/staging/speakup/speakup_dectlk.c
+++ b/drivers/staging/speakup/speakup_dectlk.c
@@ -70,30 +70,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/dectlk.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_dtlk.c b/drivers/staging/speakup/speakup_dtlk.c
index 1feb0fba1b43..5c6c34191e8d 100644
--- a/drivers/staging/speakup/speakup_dtlk.c
+++ b/drivers/staging/speakup/speakup_dtlk.c
@@ -67,34 +67,34 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/dtlk.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute freq_attribute =
- __ATTR(freq, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(freq, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_dummy.c b/drivers/staging/speakup/speakup_dummy.c
index 4a24b9c1e8e3..e19e9994bbb5 100644
--- a/drivers/staging/speakup/speakup_dummy.c
+++ b/drivers/staging/speakup/speakup_dummy.c
@@ -46,28 +46,28 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/dummy.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_keypc.c b/drivers/staging/speakup/speakup_keypc.c
index 2f2fe5eeff63..9c246d701a95 100644
--- a/drivers/staging/speakup/speakup_keypc.c
+++ b/drivers/staging/speakup/speakup_keypc.c
@@ -59,24 +59,24 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/keypc.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_ltlk.c b/drivers/staging/speakup/speakup_ltlk.c
index 326f94d6b079..c9be6f52c254 100644
--- a/drivers/staging/speakup/speakup_ltlk.c
+++ b/drivers/staging/speakup/speakup_ltlk.c
@@ -50,34 +50,34 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/ltlk.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute freq_attribute =
- __ATTR(freq, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(freq, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_soft.c b/drivers/staging/speakup/speakup_soft.c
index 243c3d52fe5e..ee6089502a96 100644
--- a/drivers/staging/speakup/speakup_soft.c
+++ b/drivers/staging/speakup/speakup_soft.c
@@ -61,41 +61,41 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/soft.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute freq_attribute =
- __ATTR(freq, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(freq, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
/*
* We should uncomment the following definition, when we agree on a
* method of passing a language designation to the software synthesizer.
* static struct kobj_attribute lang_attribute =
- * __ATTR(lang, USER_RW, spk_var_show, spk_var_store);
+ * __ATTR(lang, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
*/
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_spkout.c b/drivers/staging/speakup/speakup_spkout.c
index e74f85620c68..711cf114df83 100644
--- a/drivers/staging/speakup/speakup_spkout.c
+++ b/drivers/staging/speakup/speakup_spkout.c
@@ -48,30 +48,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/spkout.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_txprt.c b/drivers/staging/speakup/speakup_txprt.c
index 5a29b9fcc930..3f0be04df071 100644
--- a/drivers/staging/speakup/speakup_txprt.c
+++ b/drivers/staging/speakup/speakup_txprt.c
@@ -44,28 +44,28 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/txprt.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/usbip/Kconfig b/drivers/staging/usbip/Kconfig
index 886000980474..bd99e9e47e50 100644
--- a/drivers/staging/usbip/Kconfig
+++ b/drivers/staging/usbip/Kconfig
@@ -1,7 +1,6 @@
config USBIP_CORE
tristate "USB/IP support"
depends on USB && NET
- default N
---help---
This enables pushing USB packets over IP to allow remote
machines direct access to USB devices. It provides the
@@ -18,7 +17,6 @@ config USBIP_CORE
config USBIP_VHCI_HCD
tristate "VHCI hcd"
depends on USBIP_CORE
- default N
---help---
This enables the USB/IP virtual host controller driver,
which is run on the remote machine.
@@ -29,7 +27,6 @@ config USBIP_VHCI_HCD
config USBIP_HOST
tristate "Host driver"
depends on USBIP_CORE
- default N
---help---
This enables the USB/IP host driver, which is run on the
machine that is sharing the USB devices.
@@ -40,6 +37,5 @@ config USBIP_HOST
config USBIP_DEBUG
bool "Debug messages for USB/IP"
depends on USBIP_CORE
- default N
---help---
This enables the debug messages from the USB/IP drivers.
diff --git a/drivers/staging/wlags49_h2/wl_netdev.c b/drivers/staging/wlags49_h2/wl_netdev.c
index 77e4be21e44b..a10d014365f2 100644
--- a/drivers/staging/wlags49_h2/wl_netdev.c
+++ b/drivers/staging/wlags49_h2/wl_netdev.c
@@ -695,7 +695,7 @@ int wl_send(struct wl_private *lp)
/* Free the skb and perform queue cleanup, as the buffer was
transmitted successfully */
- dev_kfree_skb(lp->txF.skb);
+ dev_consume_skb_any( lp->txF.skb );
lp->txF.skb = NULL;
lp->txF.port = 0;
@@ -1750,7 +1750,7 @@ int wl_send_dma(struct wl_private *lp, struct sk_buff *skb, int port)
WL_WDS_NETIF_STOP_QUEUE(lp);
lp->netif_queue_on = FALSE;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any( skb );
return 0;
}
}
@@ -1775,7 +1775,7 @@ int wl_send_dma(struct wl_private *lp, struct sk_buff *skb, int port)
/* Free the skb and perform queue cleanup, as the buffer was
transmitted successfully */
- dev_kfree_skb(skb);
+ dev_consume_skb_any( skb );
return TRUE;
} /* wl_send_dma */
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 5f88d767671e..2d51912a6e40 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -143,7 +143,7 @@ config RCAR_THERMAL
config KIRKWOOD_THERMAL
tristate "Temperature sensor on Marvell Kirkwood SoCs"
- depends on ARCH_KIRKWOOD
+ depends on ARCH_KIRKWOOD || MACH_KIRKWOOD
depends on OF
help
Support for the Kirkwood thermal sensor driver into the Linux thermal
diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c
index 081fd7e6a9f0..9ea3d9d49ffc 100644
--- a/drivers/thermal/x86_pkg_temp_thermal.c
+++ b/drivers/thermal/x86_pkg_temp_thermal.c
@@ -590,12 +590,12 @@ static int __init pkg_temp_thermal_init(void)
platform_thermal_package_rate_control =
pkg_temp_thermal_platform_thermal_rate_control;
- get_online_cpus();
+ cpu_notifier_register_begin();
for_each_online_cpu(i)
if (get_core_online(i))
goto err_ret;
- register_hotcpu_notifier(&pkg_temp_thermal_notifier);
- put_online_cpus();
+ __register_hotcpu_notifier(&pkg_temp_thermal_notifier);
+ cpu_notifier_register_done();
pkg_temp_debugfs_init(); /* Don't care if fails */
@@ -604,7 +604,7 @@ static int __init pkg_temp_thermal_init(void)
err_ret:
for_each_online_cpu(i)
put_core_offline(i);
- put_online_cpus();
+ cpu_notifier_register_done();
kfree(pkg_work_scheduled);
platform_thermal_package_notify = NULL;
platform_thermal_package_rate_control = NULL;
@@ -617,8 +617,8 @@ static void __exit pkg_temp_thermal_exit(void)
struct phy_dev_entry *phdev, *n;
int i;
- get_online_cpus();
- unregister_hotcpu_notifier(&pkg_temp_thermal_notifier);
+ cpu_notifier_register_begin();
+ __unregister_hotcpu_notifier(&pkg_temp_thermal_notifier);
mutex_lock(&phy_dev_list_mutex);
list_for_each_entry_safe(phdev, n, &phy_dev_list, list) {
/* Retore old MSR value for package thermal interrupt */
@@ -636,7 +636,7 @@ static void __exit pkg_temp_thermal_exit(void)
for_each_online_cpu(i)
cancel_delayed_work_sync(
&per_cpu(pkg_temp_thermal_threshold_work, i));
- put_online_cpus();
+ cpu_notifier_register_done();
kfree(pkg_work_scheduled);
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 2577d67bacb2..2e6d8ddc4425 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1024,7 +1024,7 @@ config SERIAL_SGI_IOC3
config SERIAL_MSM
bool "MSM on-chip serial port support"
- depends on ARCH_MSM
+ depends on ARCH_MSM || ARCH_QCOM
select SERIAL_CORE
config SERIAL_MSM_CONSOLE
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index b0603e1f7d82..53eeea13ff16 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -35,21 +35,18 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
#include <linux/atmel_serial.h>
#include <linux/uaccess.h>
#include <linux/platform_data/atmel.h>
#include <linux/timer.h>
+#include <linux/gpio.h>
#include <asm/io.h>
#include <asm/ioctls.h>
-#ifdef CONFIG_ARM
-#include <mach/cpu.h>
-#include <asm/gpio.h>
-#endif
-
#define PDC_BUFFER_SIZE 512
/* Revisit: We should calculate this based on the actual port settings */
#define PDC_RX_TIMEOUT (3 * 10) /* 3 bytes */
@@ -165,6 +162,7 @@ struct atmel_uart_port {
struct circ_buf rx_ring;
struct serial_rs485 rs485; /* rs485 settings */
+ int rts_gpio; /* optional RTS GPIO */
unsigned int tx_done_mask;
bool is_usart; /* usart or uart */
struct timer_list uart_timer; /* uart timer */
@@ -298,20 +296,16 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
unsigned int mode;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
-#ifdef CONFIG_ARCH_AT91RM9200
- if (cpu_is_at91rm9200()) {
- /*
- * AT91RM9200 Errata #39: RTS0 is not internally connected
- * to PA21. We need to drive the pin manually.
- */
- if (port->mapbase == AT91RM9200_BASE_US0) {
- if (mctrl & TIOCM_RTS)
- at91_set_gpio_value(AT91_PIN_PA21, 0);
- else
- at91_set_gpio_value(AT91_PIN_PA21, 1);
- }
+ /*
+ * AT91RM9200 Errata #39: RTS0 is not internally connected
+ * to PA21. We need to drive the pin as a GPIO.
+ */
+ if (gpio_is_valid(atmel_port->rts_gpio)) {
+ if (mctrl & TIOCM_RTS)
+ gpio_set_value(atmel_port->rts_gpio, 0);
+ else
+ gpio_set_value(atmel_port->rts_gpio, 1);
}
-#endif
if (mctrl & TIOCM_RTS)
control |= ATMEL_US_RTSEN;
@@ -2365,6 +2359,25 @@ static int atmel_serial_probe(struct platform_device *pdev)
port = &atmel_ports[ret];
port->backup_imr = 0;
port->uart.line = ret;
+ port->rts_gpio = -EINVAL; /* Invalid, zero could be valid */
+ if (pdata)
+ port->rts_gpio = pdata->rts_gpio;
+ else if (np)
+ port->rts_gpio = of_get_named_gpio(np, "rts-gpios", 0);
+
+ if (gpio_is_valid(port->rts_gpio)) {
+ ret = devm_gpio_request(&pdev->dev, port->rts_gpio, "RTS");
+ if (ret) {
+ dev_err(&pdev->dev, "error requesting RTS GPIO\n");
+ goto err;
+ }
+ /* Default to 1 as RTS is active low */
+ ret = gpio_direction_output(port->rts_gpio, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "error setting up RTS GPIO\n");
+ goto err;
+ }
+ }
ret = atmel_init_port(port, pdev);
if (ret)
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index fed7f68d025d..cb8e99156f5a 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -10,7 +10,6 @@ config USB_DEBUG
config USB_ANNOUNCE_NEW_DEVICES
bool "USB announce new devices"
- default N
help
Say Y here if you want the USB core to always announce the
idVendor, idProduct, Manufacturer, Product, and SerialNumber
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 3cdcd0a2c0b7..0c8a7fc4dad8 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -178,7 +178,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg);
*
* Return:
* If successful, 0. Otherwise a negative error number. The number of actual
- * bytes transferred will be stored in the @actual_length paramater.
+ * bytes transferred will be stored in the @actual_length parameter.
*/
int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout)
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 9ff665f1322f..991386ceb4ec 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -831,7 +831,7 @@ EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
*
* this allows all outstanding URBs to be unlinked starting
* from the back of the queue. This function is asynchronous.
- * The unlinking is just tiggered. It may happen after this
+ * The unlinking is just triggered. It may happen after this
* function has returned.
*
* This routine should not be called by a driver after its disconnect
diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c
index a139894c600f..e471580a2a3b 100644
--- a/drivers/usb/gadget/lpc32xx_udc.c
+++ b/drivers/usb/gadget/lpc32xx_udc.c
@@ -55,7 +55,6 @@
#include <mach/hardware.h>
#include <linux/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <mach/platform.h>
#include <mach/irqs.h>
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index e22b82660831..3d9e54062d62 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -314,7 +314,6 @@ config USB_ISP1760_HCD
config USB_ISP1362_HCD
tristate "ISP1362 HCD support"
- default N
---help---
Supports the Philips ISP1362 chip as a host controller
@@ -326,7 +325,6 @@ config USB_ISP1362_HCD
config USB_FUSBH200_HCD
tristate "FUSBH200 HCD support"
depends on USB
- default N
---help---
Faraday FUSBH200 is designed to meet USB2.0 EHCI specification
with minor modification.
@@ -337,7 +335,6 @@ config USB_FUSBH200_HCD
config USB_FOTG210_HCD
tristate "FOTG210 HCD support"
depends on USB
- default N
---help---
Faraday FOTG210 is an OTG controller which can be configured as
an USB2.0 host. It is designed to meet USB2.0 EHCI specification
diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
index 26b3d9d1409f..af7b204b9215 100644
--- a/drivers/vfio/Kconfig
+++ b/drivers/vfio/Kconfig
@@ -13,6 +13,7 @@ menuconfig VFIO
depends on IOMMU_API
select VFIO_IOMMU_TYPE1 if X86
select VFIO_IOMMU_SPAPR_TCE if (PPC_POWERNV || PPC_PSERIES)
+ select ANON_INODES
help
VFIO provides a framework for secure userspace device drivers.
See Documentation/vfio.txt for more details.
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 21271d8df023..512f479d8a50 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -1413,6 +1413,12 @@ int vfio_external_user_iommu_id(struct vfio_group *group)
}
EXPORT_SYMBOL_GPL(vfio_external_user_iommu_id);
+long vfio_external_check_extension(struct vfio_group *group, unsigned long arg)
+{
+ return vfio_ioctl_check_extension(group->container, arg);
+}
+EXPORT_SYMBOL_GPL(vfio_external_check_extension);
+
/**
* Module/class support
*/
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 54af4e933695..6673e7be507f 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -30,7 +30,6 @@
#include <linux/iommu.h>
#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/pci.h> /* pci_bus_type */
#include <linux/rbtree.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -55,11 +54,17 @@ MODULE_PARM_DESC(disable_hugepages,
"Disable VFIO IOMMU support for IOMMU hugepages.");
struct vfio_iommu {
- struct iommu_domain *domain;
+ struct list_head domain_list;
struct mutex lock;
struct rb_root dma_list;
+ bool v2;
+};
+
+struct vfio_domain {
+ struct iommu_domain *domain;
+ struct list_head next;
struct list_head group_list;
- bool cache;
+ int prot; /* IOMMU_CACHE */
};
struct vfio_dma {
@@ -99,7 +104,7 @@ static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu,
return NULL;
}
-static void vfio_insert_dma(struct vfio_iommu *iommu, struct vfio_dma *new)
+static void vfio_link_dma(struct vfio_iommu *iommu, struct vfio_dma *new)
{
struct rb_node **link = &iommu->dma_list.rb_node, *parent = NULL;
struct vfio_dma *dma;
@@ -118,7 +123,7 @@ static void vfio_insert_dma(struct vfio_iommu *iommu, struct vfio_dma *new)
rb_insert_color(&new->node, &iommu->dma_list);
}
-static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *old)
+static void vfio_unlink_dma(struct vfio_iommu *iommu, struct vfio_dma *old)
{
rb_erase(&old->node, &iommu->dma_list);
}
@@ -322,32 +327,39 @@ static long vfio_unpin_pages(unsigned long pfn, long npage,
return unlocked;
}
-static int vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
- dma_addr_t iova, size_t *size)
+static void vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma)
{
- dma_addr_t start = iova, end = iova + *size;
+ dma_addr_t iova = dma->iova, end = dma->iova + dma->size;
+ struct vfio_domain *domain, *d;
long unlocked = 0;
+ if (!dma->size)
+ return;
+ /*
+ * We use the IOMMU to track the physical addresses, otherwise we'd
+ * need a much more complicated tracking system. Unfortunately that
+ * means we need to use one of the iommu domains to figure out the
+ * pfns to unpin. The rest need to be unmapped in advance so we have
+ * no iommu translations remaining when the pages are unpinned.
+ */
+ domain = d = list_first_entry(&iommu->domain_list,
+ struct vfio_domain, next);
+
+ list_for_each_entry_continue(d, &iommu->domain_list, next)
+ iommu_unmap(d->domain, dma->iova, dma->size);
+
while (iova < end) {
size_t unmapped;
phys_addr_t phys;
- /*
- * We use the IOMMU to track the physical address. This
- * saves us from having a lot more entries in our mapping
- * tree. The downside is that we don't track the size
- * used to do the mapping. We request unmap of a single
- * page, but expect IOMMUs that support large pages to
- * unmap a larger chunk.
- */
- phys = iommu_iova_to_phys(iommu->domain, iova);
+ phys = iommu_iova_to_phys(domain->domain, iova);
if (WARN_ON(!phys)) {
iova += PAGE_SIZE;
continue;
}
- unmapped = iommu_unmap(iommu->domain, iova, PAGE_SIZE);
- if (!unmapped)
+ unmapped = iommu_unmap(domain->domain, iova, PAGE_SIZE);
+ if (WARN_ON(!unmapped))
break;
unlocked += vfio_unpin_pages(phys >> PAGE_SHIFT,
@@ -357,119 +369,26 @@ static int vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
}
vfio_lock_acct(-unlocked);
-
- *size = iova - start;
-
- return 0;
}
-static int vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start,
- size_t *size, struct vfio_dma *dma)
+static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *dma)
{
- size_t offset, overlap, tmp;
- struct vfio_dma *split;
- int ret;
-
- if (!*size)
- return 0;
-
- /*
- * Existing dma region is completely covered, unmap all. This is
- * the likely case since userspace tends to map and unmap buffers
- * in one shot rather than multiple mappings within a buffer.
- */
- if (likely(start <= dma->iova &&
- start + *size >= dma->iova + dma->size)) {
- *size = dma->size;
- ret = vfio_unmap_unpin(iommu, dma, dma->iova, size);
- if (ret)
- return ret;
-
- /*
- * Did we remove more than we have? Should never happen
- * since a vfio_dma is contiguous in iova and vaddr.
- */
- WARN_ON(*size != dma->size);
-
- vfio_remove_dma(iommu, dma);
- kfree(dma);
- return 0;
- }
-
- /* Overlap low address of existing range */
- if (start <= dma->iova) {
- overlap = start + *size - dma->iova;
- ret = vfio_unmap_unpin(iommu, dma, dma->iova, &overlap);
- if (ret)
- return ret;
-
- vfio_remove_dma(iommu, dma);
-
- /*
- * Check, we may have removed to whole vfio_dma. If not
- * fixup and re-insert.
- */
- if (overlap < dma->size) {
- dma->iova += overlap;
- dma->vaddr += overlap;
- dma->size -= overlap;
- vfio_insert_dma(iommu, dma);
- } else
- kfree(dma);
-
- *size = overlap;
- return 0;
- }
-
- /* Overlap high address of existing range */
- if (start + *size >= dma->iova + dma->size) {
- offset = start - dma->iova;
- overlap = dma->size - offset;
-
- ret = vfio_unmap_unpin(iommu, dma, start, &overlap);
- if (ret)
- return ret;
-
- dma->size -= overlap;
- *size = overlap;
- return 0;
- }
-
- /* Split existing */
-
- /*
- * Allocate our tracking structure early even though it may not
- * be used. An Allocation failure later loses track of pages and
- * is more difficult to unwind.
- */
- split = kzalloc(sizeof(*split), GFP_KERNEL);
- if (!split)
- return -ENOMEM;
-
- offset = start - dma->iova;
-
- ret = vfio_unmap_unpin(iommu, dma, start, size);
- if (ret || !*size) {
- kfree(split);
- return ret;
- }
-
- tmp = dma->size;
+ vfio_unmap_unpin(iommu, dma);
+ vfio_unlink_dma(iommu, dma);
+ kfree(dma);
+}
- /* Resize the lower vfio_dma in place, before the below insert */
- dma->size = offset;
+static unsigned long vfio_pgsize_bitmap(struct vfio_iommu *iommu)
+{
+ struct vfio_domain *domain;
+ unsigned long bitmap = PAGE_MASK;
- /* Insert new for remainder, assuming it didn't all get unmapped */
- if (likely(offset + *size < tmp)) {
- split->size = tmp - offset - *size;
- split->iova = dma->iova + offset + *size;
- split->vaddr = dma->vaddr + offset + *size;
- split->prot = dma->prot;
- vfio_insert_dma(iommu, split);
- } else
- kfree(split);
+ mutex_lock(&iommu->lock);
+ list_for_each_entry(domain, &iommu->domain_list, next)
+ bitmap &= domain->domain->ops->pgsize_bitmap;
+ mutex_unlock(&iommu->lock);
- return 0;
+ return bitmap;
}
static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
@@ -477,10 +396,10 @@ static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
{
uint64_t mask;
struct vfio_dma *dma;
- size_t unmapped = 0, size;
+ size_t unmapped = 0;
int ret = 0;
- mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1;
+ mask = ((uint64_t)1 << __ffs(vfio_pgsize_bitmap(iommu))) - 1;
if (unmap->iova & mask)
return -EINVAL;
@@ -491,20 +410,61 @@ static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
mutex_lock(&iommu->lock);
+ /*
+ * vfio-iommu-type1 (v1) - User mappings were coalesced together to
+ * avoid tracking individual mappings. This means that the granularity
+ * of the original mapping was lost and the user was allowed to attempt
+ * to unmap any range. Depending on the contiguousness of physical
+ * memory and page sizes supported by the IOMMU, arbitrary unmaps may
+ * or may not have worked. We only guaranteed unmap granularity
+ * matching the original mapping; even though it was untracked here,
+ * the original mappings are reflected in IOMMU mappings. This
+ * resulted in a couple unusual behaviors. First, if a range is not
+ * able to be unmapped, ex. a set of 4k pages that was mapped as a
+ * 2M hugepage into the IOMMU, the unmap ioctl returns success but with
+ * a zero sized unmap. Also, if an unmap request overlaps the first
+ * address of a hugepage, the IOMMU will unmap the entire hugepage.
+ * This also returns success and the returned unmap size reflects the
+ * actual size unmapped.
+ *
+ * We attempt to maintain compatibility with this "v1" interface, but
+ * we take control out of the hands of the IOMMU. Therefore, an unmap
+ * request offset from the beginning of the original mapping will
+ * return success with zero sized unmap. And an unmap request covering
+ * the first iova of mapping will unmap the entire range.
+ *
+ * The v2 version of this interface intends to be more deterministic.
+ * Unmap requests must fully cover previous mappings. Multiple
+ * mappings may still be unmaped by specifying large ranges, but there
+ * must not be any previous mappings bisected by the range. An error
+ * will be returned if these conditions are not met. The v2 interface
+ * will only return success and a size of zero if there were no
+ * mappings within the range.
+ */
+ if (iommu->v2) {
+ dma = vfio_find_dma(iommu, unmap->iova, 0);
+ if (dma && dma->iova != unmap->iova) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ dma = vfio_find_dma(iommu, unmap->iova + unmap->size - 1, 0);
+ if (dma && dma->iova + dma->size != unmap->iova + unmap->size) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ }
+
while ((dma = vfio_find_dma(iommu, unmap->iova, unmap->size))) {
- size = unmap->size;
- ret = vfio_remove_dma_overlap(iommu, unmap->iova, &size, dma);
- if (ret || !size)
+ if (!iommu->v2 && unmap->iova > dma->iova)
break;
- unmapped += size;
+ unmapped += dma->size;
+ vfio_remove_dma(iommu, dma);
}
+unlock:
mutex_unlock(&iommu->lock);
- /*
- * We may unmap more than requested, update the unmap struct so
- * userspace can know.
- */
+ /* Report how much was unmapped */
unmap->size = unmapped;
return ret;
@@ -516,22 +476,47 @@ static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
* soon, so this is just a temporary workaround to break mappings down into
* PAGE_SIZE. Better to map smaller pages than nothing.
*/
-static int map_try_harder(struct vfio_iommu *iommu, dma_addr_t iova,
+static int map_try_harder(struct vfio_domain *domain, dma_addr_t iova,
unsigned long pfn, long npage, int prot)
{
long i;
int ret;
for (i = 0; i < npage; i++, pfn++, iova += PAGE_SIZE) {
- ret = iommu_map(iommu->domain, iova,
+ ret = iommu_map(domain->domain, iova,
(phys_addr_t)pfn << PAGE_SHIFT,
- PAGE_SIZE, prot);
+ PAGE_SIZE, prot | domain->prot);
if (ret)
break;
}
for (; i < npage && i > 0; i--, iova -= PAGE_SIZE)
- iommu_unmap(iommu->domain, iova, PAGE_SIZE);
+ iommu_unmap(domain->domain, iova, PAGE_SIZE);
+
+ return ret;
+}
+
+static int vfio_iommu_map(struct vfio_iommu *iommu, dma_addr_t iova,
+ unsigned long pfn, long npage, int prot)
+{
+ struct vfio_domain *d;
+ int ret;
+
+ list_for_each_entry(d, &iommu->domain_list, next) {
+ ret = iommu_map(d->domain, iova, (phys_addr_t)pfn << PAGE_SHIFT,
+ npage << PAGE_SHIFT, prot | d->prot);
+ if (ret) {
+ if (ret != -EBUSY ||
+ map_try_harder(d, iova, pfn, npage, prot))
+ goto unwind;
+ }
+ }
+
+ return 0;
+
+unwind:
+ list_for_each_entry_continue_reverse(d, &iommu->domain_list, next)
+ iommu_unmap(d->domain, iova, npage << PAGE_SHIFT);
return ret;
}
@@ -545,12 +530,12 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
long npage;
int ret = 0, prot = 0;
uint64_t mask;
- struct vfio_dma *dma = NULL;
+ struct vfio_dma *dma;
unsigned long pfn;
end = map->iova + map->size;
- mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1;
+ mask = ((uint64_t)1 << __ffs(vfio_pgsize_bitmap(iommu))) - 1;
/* READ/WRITE from device perspective */
if (map->flags & VFIO_DMA_MAP_FLAG_WRITE)
@@ -561,9 +546,6 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
if (!prot)
return -EINVAL; /* No READ/WRITE? */
- if (iommu->cache)
- prot |= IOMMU_CACHE;
-
if (vaddr & mask)
return -EINVAL;
if (map->iova & mask)
@@ -588,180 +570,257 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
return -EEXIST;
}
- for (iova = map->iova; iova < end; iova += size, vaddr += size) {
- long i;
+ dma = kzalloc(sizeof(*dma), GFP_KERNEL);
+ if (!dma) {
+ mutex_unlock(&iommu->lock);
+ return -ENOMEM;
+ }
+
+ dma->iova = map->iova;
+ dma->vaddr = map->vaddr;
+ dma->prot = prot;
+
+ /* Insert zero-sized and grow as we map chunks of it */
+ vfio_link_dma(iommu, dma);
+ for (iova = map->iova; iova < end; iova += size, vaddr += size) {
/* Pin a contiguous chunk of memory */
npage = vfio_pin_pages(vaddr, (end - iova) >> PAGE_SHIFT,
prot, &pfn);
if (npage <= 0) {
WARN_ON(!npage);
ret = (int)npage;
- goto out;
- }
-
- /* Verify pages are not already mapped */
- for (i = 0; i < npage; i++) {
- if (iommu_iova_to_phys(iommu->domain,
- iova + (i << PAGE_SHIFT))) {
- ret = -EBUSY;
- goto out_unpin;
- }
+ break;
}
- ret = iommu_map(iommu->domain, iova,
- (phys_addr_t)pfn << PAGE_SHIFT,
- npage << PAGE_SHIFT, prot);
+ /* Map it! */
+ ret = vfio_iommu_map(iommu, iova, pfn, npage, prot);
if (ret) {
- if (ret != -EBUSY ||
- map_try_harder(iommu, iova, pfn, npage, prot)) {
- goto out_unpin;
- }
+ vfio_unpin_pages(pfn, npage, prot, true);
+ break;
}
size = npage << PAGE_SHIFT;
+ dma->size += size;
+ }
- /*
- * Check if we abut a region below - nothing below 0.
- * This is the most likely case when mapping chunks of
- * physically contiguous regions within a virtual address
- * range. Update the abutting entry in place since iova
- * doesn't change.
- */
- if (likely(iova)) {
- struct vfio_dma *tmp;
- tmp = vfio_find_dma(iommu, iova - 1, 1);
- if (tmp && tmp->prot == prot &&
- tmp->vaddr + tmp->size == vaddr) {
- tmp->size += size;
- iova = tmp->iova;
- size = tmp->size;
- vaddr = tmp->vaddr;
- dma = tmp;
- }
- }
+ if (ret)
+ vfio_remove_dma(iommu, dma);
- /*
- * Check if we abut a region above - nothing above ~0 + 1.
- * If we abut above and below, remove and free. If only
- * abut above, remove, modify, reinsert.
- */
- if (likely(iova + size)) {
- struct vfio_dma *tmp;
- tmp = vfio_find_dma(iommu, iova + size, 1);
- if (tmp && tmp->prot == prot &&
- tmp->vaddr == vaddr + size) {
- vfio_remove_dma(iommu, tmp);
- if (dma) {
- dma->size += tmp->size;
- kfree(tmp);
- } else {
- size += tmp->size;
- tmp->size = size;
- tmp->iova = iova;
- tmp->vaddr = vaddr;
- vfio_insert_dma(iommu, tmp);
- dma = tmp;
- }
- }
- }
+ mutex_unlock(&iommu->lock);
+ return ret;
+}
+
+static int vfio_bus_type(struct device *dev, void *data)
+{
+ struct bus_type **bus = data;
+
+ if (*bus && *bus != dev->bus)
+ return -EINVAL;
+
+ *bus = dev->bus;
+
+ return 0;
+}
+
+static int vfio_iommu_replay(struct vfio_iommu *iommu,
+ struct vfio_domain *domain)
+{
+ struct vfio_domain *d;
+ struct rb_node *n;
+ int ret;
+
+ /* Arbitrarily pick the first domain in the list for lookups */
+ d = list_first_entry(&iommu->domain_list, struct vfio_domain, next);
+ n = rb_first(&iommu->dma_list);
+
+ /* If there's not a domain, there better not be any mappings */
+ if (WARN_ON(n && !d))
+ return -EINVAL;
+
+ for (; n; n = rb_next(n)) {
+ struct vfio_dma *dma;
+ dma_addr_t iova;
+
+ dma = rb_entry(n, struct vfio_dma, node);
+ iova = dma->iova;
+
+ while (iova < dma->iova + dma->size) {
+ phys_addr_t phys = iommu_iova_to_phys(d->domain, iova);
+ size_t size;
- if (!dma) {
- dma = kzalloc(sizeof(*dma), GFP_KERNEL);
- if (!dma) {
- iommu_unmap(iommu->domain, iova, size);
- ret = -ENOMEM;
- goto out_unpin;
+ if (WARN_ON(!phys)) {
+ iova += PAGE_SIZE;
+ continue;
}
- dma->size = size;
- dma->iova = iova;
- dma->vaddr = vaddr;
- dma->prot = prot;
- vfio_insert_dma(iommu, dma);
- }
- }
+ size = PAGE_SIZE;
- WARN_ON(ret);
- mutex_unlock(&iommu->lock);
- return ret;
+ while (iova + size < dma->iova + dma->size &&
+ phys + size == iommu_iova_to_phys(d->domain,
+ iova + size))
+ size += PAGE_SIZE;
-out_unpin:
- vfio_unpin_pages(pfn, npage, prot, true);
+ ret = iommu_map(domain->domain, iova, phys,
+ size, dma->prot | domain->prot);
+ if (ret)
+ return ret;
-out:
- iova = map->iova;
- size = map->size;
- while ((dma = vfio_find_dma(iommu, iova, size))) {
- int r = vfio_remove_dma_overlap(iommu, iova,
- &size, dma);
- if (WARN_ON(r || !size))
- break;
+ iova += size;
+ }
}
- mutex_unlock(&iommu->lock);
- return ret;
+ return 0;
}
static int vfio_iommu_type1_attach_group(void *iommu_data,
struct iommu_group *iommu_group)
{
struct vfio_iommu *iommu = iommu_data;
- struct vfio_group *group, *tmp;
+ struct vfio_group *group, *g;
+ struct vfio_domain *domain, *d;
+ struct bus_type *bus = NULL;
int ret;
- group = kzalloc(sizeof(*group), GFP_KERNEL);
- if (!group)
- return -ENOMEM;
-
mutex_lock(&iommu->lock);
- list_for_each_entry(tmp, &iommu->group_list, next) {
- if (tmp->iommu_group == iommu_group) {
+ list_for_each_entry(d, &iommu->domain_list, next) {
+ list_for_each_entry(g, &d->group_list, next) {
+ if (g->iommu_group != iommu_group)
+ continue;
+
mutex_unlock(&iommu->lock);
- kfree(group);
return -EINVAL;
}
}
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+ if (!group || !domain) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ group->iommu_group = iommu_group;
+
+ /* Determine bus_type in order to allocate a domain */
+ ret = iommu_group_for_each_dev(iommu_group, &bus, vfio_bus_type);
+ if (ret)
+ goto out_free;
+
+ domain->domain = iommu_domain_alloc(bus);
+ if (!domain->domain) {
+ ret = -EIO;
+ goto out_free;
+ }
+
+ ret = iommu_attach_group(domain->domain, iommu_group);
+ if (ret)
+ goto out_domain;
+
+ INIT_LIST_HEAD(&domain->group_list);
+ list_add(&group->next, &domain->group_list);
+
+ if (!allow_unsafe_interrupts &&
+ !iommu_domain_has_cap(domain->domain, IOMMU_CAP_INTR_REMAP)) {
+ pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n",
+ __func__);
+ ret = -EPERM;
+ goto out_detach;
+ }
+
+ if (iommu_domain_has_cap(domain->domain, IOMMU_CAP_CACHE_COHERENCY))
+ domain->prot |= IOMMU_CACHE;
+
/*
- * TODO: Domain have capabilities that might change as we add
- * groups (see iommu->cache, currently never set). Check for
- * them and potentially disallow groups to be attached when it
- * would change capabilities (ugh).
+ * Try to match an existing compatible domain. We don't want to
+ * preclude an IOMMU driver supporting multiple bus_types and being
+ * able to include different bus_types in the same IOMMU domain, so
+ * we test whether the domains use the same iommu_ops rather than
+ * testing if they're on the same bus_type.
*/
- ret = iommu_attach_group(iommu->domain, iommu_group);
- if (ret) {
- mutex_unlock(&iommu->lock);
- kfree(group);
- return ret;
+ list_for_each_entry(d, &iommu->domain_list, next) {
+ if (d->domain->ops == domain->domain->ops &&
+ d->prot == domain->prot) {
+ iommu_detach_group(domain->domain, iommu_group);
+ if (!iommu_attach_group(d->domain, iommu_group)) {
+ list_add(&group->next, &d->group_list);
+ iommu_domain_free(domain->domain);
+ kfree(domain);
+ mutex_unlock(&iommu->lock);
+ return 0;
+ }
+
+ ret = iommu_attach_group(domain->domain, iommu_group);
+ if (ret)
+ goto out_domain;
+ }
}
- group->iommu_group = iommu_group;
- list_add(&group->next, &iommu->group_list);
+ /* replay mappings on new domains */
+ ret = vfio_iommu_replay(iommu, domain);
+ if (ret)
+ goto out_detach;
+
+ list_add(&domain->next, &iommu->domain_list);
mutex_unlock(&iommu->lock);
return 0;
+
+out_detach:
+ iommu_detach_group(domain->domain, iommu_group);
+out_domain:
+ iommu_domain_free(domain->domain);
+out_free:
+ kfree(domain);
+ kfree(group);
+ mutex_unlock(&iommu->lock);
+ return ret;
+}
+
+static void vfio_iommu_unmap_unpin_all(struct vfio_iommu *iommu)
+{
+ struct rb_node *node;
+
+ while ((node = rb_first(&iommu->dma_list)))
+ vfio_remove_dma(iommu, rb_entry(node, struct vfio_dma, node));
}
static void vfio_iommu_type1_detach_group(void *iommu_data,
struct iommu_group *iommu_group)
{
struct vfio_iommu *iommu = iommu_data;
+ struct vfio_domain *domain;
struct vfio_group *group;
mutex_lock(&iommu->lock);
- list_for_each_entry(group, &iommu->group_list, next) {
- if (group->iommu_group == iommu_group) {
- iommu_detach_group(iommu->domain, iommu_group);
+ list_for_each_entry(domain, &iommu->domain_list, next) {
+ list_for_each_entry(group, &domain->group_list, next) {
+ if (group->iommu_group != iommu_group)
+ continue;
+
+ iommu_detach_group(domain->domain, iommu_group);
list_del(&group->next);
kfree(group);
- break;
+ /*
+ * Group ownership provides privilege, if the group
+ * list is empty, the domain goes away. If it's the
+ * last domain, then all the mappings go away too.
+ */
+ if (list_empty(&domain->group_list)) {
+ if (list_is_singular(&iommu->domain_list))
+ vfio_iommu_unmap_unpin_all(iommu);
+ iommu_domain_free(domain->domain);
+ list_del(&domain->next);
+ kfree(domain);
+ }
+ goto done;
}
}
+done:
mutex_unlock(&iommu->lock);
}
@@ -769,40 +828,17 @@ static void *vfio_iommu_type1_open(unsigned long arg)
{
struct vfio_iommu *iommu;
- if (arg != VFIO_TYPE1_IOMMU)
+ if (arg != VFIO_TYPE1_IOMMU && arg != VFIO_TYPE1v2_IOMMU)
return ERR_PTR(-EINVAL);
iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
if (!iommu)
return ERR_PTR(-ENOMEM);
- INIT_LIST_HEAD(&iommu->group_list);
+ INIT_LIST_HEAD(&iommu->domain_list);
iommu->dma_list = RB_ROOT;
mutex_init(&iommu->lock);
-
- /*
- * Wish we didn't have to know about bus_type here.
- */
- iommu->domain = iommu_domain_alloc(&pci_bus_type);
- if (!iommu->domain) {
- kfree(iommu);
- return ERR_PTR(-EIO);
- }
-
- /*
- * Wish we could specify required capabilities rather than create
- * a domain, see what comes out and hope it doesn't change along
- * the way. Fortunately we know interrupt remapping is global for
- * our iommus.
- */
- if (!allow_unsafe_interrupts &&
- !iommu_domain_has_cap(iommu->domain, IOMMU_CAP_INTR_REMAP)) {
- pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n",
- __func__);
- iommu_domain_free(iommu->domain);
- kfree(iommu);
- return ERR_PTR(-EPERM);
- }
+ iommu->v2 = (arg == VFIO_TYPE1v2_IOMMU);
return iommu;
}
@@ -810,26 +846,42 @@ static void *vfio_iommu_type1_open(unsigned long arg)
static void vfio_iommu_type1_release(void *iommu_data)
{
struct vfio_iommu *iommu = iommu_data;
+ struct vfio_domain *domain, *domain_tmp;
struct vfio_group *group, *group_tmp;
- struct rb_node *node;
- list_for_each_entry_safe(group, group_tmp, &iommu->group_list, next) {
- iommu_detach_group(iommu->domain, group->iommu_group);
- list_del(&group->next);
- kfree(group);
+ vfio_iommu_unmap_unpin_all(iommu);
+
+ list_for_each_entry_safe(domain, domain_tmp,
+ &iommu->domain_list, next) {
+ list_for_each_entry_safe(group, group_tmp,
+ &domain->group_list, next) {
+ iommu_detach_group(domain->domain, group->iommu_group);
+ list_del(&group->next);
+ kfree(group);
+ }
+ iommu_domain_free(domain->domain);
+ list_del(&domain->next);
+ kfree(domain);
}
- while ((node = rb_first(&iommu->dma_list))) {
- struct vfio_dma *dma = rb_entry(node, struct vfio_dma, node);
- size_t size = dma->size;
- vfio_remove_dma_overlap(iommu, dma->iova, &size, dma);
- if (WARN_ON(!size))
+ kfree(iommu);
+}
+
+static int vfio_domains_have_iommu_cache(struct vfio_iommu *iommu)
+{
+ struct vfio_domain *domain;
+ int ret = 1;
+
+ mutex_lock(&iommu->lock);
+ list_for_each_entry(domain, &iommu->domain_list, next) {
+ if (!(domain->prot & IOMMU_CACHE)) {
+ ret = 0;
break;
+ }
}
+ mutex_unlock(&iommu->lock);
- iommu_domain_free(iommu->domain);
- iommu->domain = NULL;
- kfree(iommu);
+ return ret;
}
static long vfio_iommu_type1_ioctl(void *iommu_data,
@@ -841,7 +893,12 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
if (cmd == VFIO_CHECK_EXTENSION) {
switch (arg) {
case VFIO_TYPE1_IOMMU:
+ case VFIO_TYPE1v2_IOMMU:
return 1;
+ case VFIO_DMA_CC_IOMMU:
+ if (!iommu)
+ return 0;
+ return vfio_domains_have_iommu_cache(iommu);
default:
return 0;
}
@@ -858,7 +915,7 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
info.flags = 0;
- info.iova_pgsizes = iommu->domain->ops->pgsize_bitmap;
+ info.iova_pgsizes = vfio_pgsize_bitmap(iommu);
return copy_to_user((void __user *)arg, &info, minsz);
@@ -911,9 +968,6 @@ static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
static int __init vfio_iommu_type1_init(void)
{
- if (!iommu_present(&pci_bus_type))
- return -ENODEV;
-
return vfio_register_iommu_driver(&vfio_iommu_driver_ops_type1);
}
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index b4b209ce8029..6c793bc683d9 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -967,7 +967,7 @@ config FB_PVR2
config FB_OPENCORES
tristate "OpenCores VGA/LCD core 2.0 framebuffer support"
- depends on FB
+ depends on FB && HAS_DMA
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index cd961622f9c1..e683b6ef9594 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -1190,12 +1190,12 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
if (!sinfo->config)
goto free_info;
- strcpy(info->fix.id, sinfo->pdev->name);
info->flags = ATMEL_LCDFB_FBINFO_DEFAULT;
info->pseudo_palette = sinfo->pseudo_palette;
info->fbops = &atmel_lcdfb_ops;
info->fix = atmel_lcdfb_fix;
+ strcpy(info->fix.id, sinfo->pdev->name);
/* Enable LCDC Clocks */
sinfo->bus_clk = clk_get(dev, "hclk");
@@ -1298,6 +1298,12 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
goto unregister_irqs;
}
+ ret = atmel_lcdfb_set_par(info);
+ if (ret < 0) {
+ dev_err(dev, "set par failed: %d\n", ret);
+ goto unregister_irqs;
+ }
+
dev_set_drvdata(dev, info);
/*
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index 28fafbf864a5..c3d0074a32db 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -862,8 +862,8 @@ static int aty_var_to_crtc(const struct fb_info *info,
h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
- if ((xres > 1600) || (yres > 1200)) {
- FAIL("MACH64 chips are designed for max 1600x1200\n"
+ if ((xres > 1920) || (yres > 1200)) {
+ FAIL("MACH64 chips are designed for max 1920x1200\n"
"select another resolution.");
}
h_sync_strt = h_disp + var->right_margin;
@@ -2653,7 +2653,8 @@ static int aty_init(struct fb_info *info)
FBINFO_HWACCEL_IMAGEBLIT |
FBINFO_HWACCEL_FILLRECT |
FBINFO_HWACCEL_COPYAREA |
- FBINFO_HWACCEL_YPAN;
+ FBINFO_HWACCEL_YPAN |
+ FBINFO_READS_FAST;
#ifdef CONFIG_PMAC_BACKLIGHT
if (M64_HAS(G3_PB_1_1) && of_machine_is_compatible("PowerBook1,1")) {
diff --git a/drivers/video/aty/mach64_accel.c b/drivers/video/aty/mach64_accel.c
index e45833ce975b..182bd680141f 100644
--- a/drivers/video/aty/mach64_accel.c
+++ b/drivers/video/aty/mach64_accel.c
@@ -4,6 +4,7 @@
*/
#include <linux/delay.h>
+#include <asm/unaligned.h>
#include <linux/fb.h>
#include <video/mach64.h>
#include "atyfb.h"
@@ -419,7 +420,7 @@ void atyfb_imageblit(struct fb_info *info, const struct fb_image *image)
u32 *pbitmap, dwords = (src_bytes + 3) / 4;
for (pbitmap = (u32*)(image->data); dwords; dwords--, pbitmap++) {
wait_for_fifo(1, par);
- aty_st_le32(HOST_DATA0, le32_to_cpup(pbitmap), par);
+ aty_st_le32(HOST_DATA0, get_unaligned_le32(pbitmap), par);
}
}
diff --git a/drivers/video/aty/mach64_cursor.c b/drivers/video/aty/mach64_cursor.c
index 95ec042ddbf8..0fe02e22d9a4 100644
--- a/drivers/video/aty/mach64_cursor.c
+++ b/drivers/video/aty/mach64_cursor.c
@@ -5,6 +5,7 @@
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/string.h>
+#include "../fb_draw.h"
#include <asm/io.h>
@@ -157,24 +158,33 @@ static int atyfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
+ u16 l = 0xaaaa;
b = *src++;
m = *msk++;
switch (cursor->rop) {
case ROP_XOR:
// Upper 4 bits of mask data
- fb_writeb(cursor_bits_lookup[(b ^ m) >> 4], dst++);
+ l = cursor_bits_lookup[(b ^ m) >> 4] |
// Lower 4 bits of mask
- fb_writeb(cursor_bits_lookup[(b ^ m) & 0x0f],
- dst++);
+ (cursor_bits_lookup[(b ^ m) & 0x0f] << 8);
break;
case ROP_COPY:
// Upper 4 bits of mask data
- fb_writeb(cursor_bits_lookup[(b & m) >> 4], dst++);
+ l = cursor_bits_lookup[(b & m) >> 4] |
// Lower 4 bits of mask
- fb_writeb(cursor_bits_lookup[(b & m) & 0x0f],
- dst++);
+ (cursor_bits_lookup[(b & m) & 0x0f] << 8);
break;
}
+ /*
+ * If cursor size is not a multiple of 8 characters
+ * we must pad it with transparent pattern (0xaaaa).
+ */
+ if ((j + 1) * 8 > cursor->image.width) {
+ l = comp(l, 0xaaaa,
+ (1 << ((cursor->image.width & 7) * 2)) - 1);
+ }
+ fb_writeb(l & 0xff, dst++);
+ fb_writeb(l >> 8, dst++);
}
dst += offset;
}
diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c
index ee0c0a982e4e..ec5350f2c28a 100644
--- a/drivers/video/backlight/aat2870_bl.c
+++ b/drivers/video/backlight/aat2870_bl.c
@@ -149,8 +149,6 @@ static int aat2870_bl_probe(struct platform_device *pdev)
sizeof(struct aat2870_bl_driver_data),
GFP_KERNEL);
if (!aat2870_bl) {
- dev_err(&pdev->dev,
- "Failed to allocate memory for aat2870 backlight\n");
ret = -ENOMEM;
goto out;
}
diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c
index 9d656717d0f7..be8d83deca7d 100644
--- a/drivers/video/backlight/adp8860_bl.c
+++ b/drivers/video/backlight/adp8860_bl.c
@@ -224,10 +224,8 @@ static int adp8860_led_probe(struct i2c_client *client)
led = devm_kzalloc(&client->dev, sizeof(*led) * pdata->num_leds,
GFP_KERNEL);
- if (led == NULL) {
- dev_err(&client->dev, "failed to alloc memory\n");
+ if (led == NULL)
return -ENOMEM;
- }
ret = adp8860_write(client, ADP8860_ISCFR, pdata->led_fade_law);
ret = adp8860_write(client, ADP8860_ISCT1,
diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c
index 63707205326b..251af4d38d86 100644
--- a/drivers/video/backlight/adp8870_bl.c
+++ b/drivers/video/backlight/adp8870_bl.c
@@ -246,10 +246,8 @@ static int adp8870_led_probe(struct i2c_client *client)
led = devm_kzalloc(&client->dev, pdata->num_leds * sizeof(*led),
GFP_KERNEL);
- if (led == NULL) {
- dev_err(&client->dev, "failed to alloc memory\n");
+ if (led == NULL)
return -ENOMEM;
- }
ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law);
if (ret)
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
index 5d05555fe841..27d3cf255e78 100644
--- a/drivers/video/backlight/backlight.c
+++ b/drivers/video/backlight/backlight.c
@@ -34,13 +34,15 @@ static const char *const backlight_types[] = {
defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE))
/* This callback gets called when something important happens inside a
* framebuffer driver. We're looking if that important event is blanking,
- * and if it is, we're switching backlight power as well ...
+ * and if it is and necessary, we're switching backlight power as well ...
*/
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct backlight_device *bd;
struct fb_event *evdata = data;
+ int node = evdata->info->node;
+ int fb_blank = 0;
/* If we aren't interested in this event, skip it immediately ... */
if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK)
@@ -51,12 +53,24 @@ static int fb_notifier_callback(struct notifier_block *self,
if (bd->ops)
if (!bd->ops->check_fb ||
bd->ops->check_fb(bd, evdata->info)) {
- bd->props.fb_blank = *(int *)evdata->data;
- if (bd->props.fb_blank == FB_BLANK_UNBLANK)
- bd->props.state &= ~BL_CORE_FBBLANK;
- else
- bd->props.state |= BL_CORE_FBBLANK;
- backlight_update_status(bd);
+ fb_blank = *(int *)evdata->data;
+ if (fb_blank == FB_BLANK_UNBLANK &&
+ !bd->fb_bl_on[node]) {
+ bd->fb_bl_on[node] = true;
+ if (!bd->use_count++) {
+ bd->props.state &= ~BL_CORE_FBBLANK;
+ bd->props.fb_blank = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
+ }
+ } else if (fb_blank != FB_BLANK_UNBLANK &&
+ bd->fb_bl_on[node]) {
+ bd->fb_bl_on[node] = false;
+ if (!(--bd->use_count)) {
+ bd->props.state |= BL_CORE_FBBLANK;
+ bd->props.fb_blank = fb_blank;
+ backlight_update_status(bd);
+ }
+ }
}
mutex_unlock(&bd->ops_lock);
return 0;
diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c
index db8db5fa6583..51d18d637e2b 100644
--- a/drivers/video/backlight/corgi_lcd.c
+++ b/drivers/video/backlight/corgi_lcd.c
@@ -543,10 +543,8 @@ static int corgi_lcd_probe(struct spi_device *spi)
}
lcd = devm_kzalloc(&spi->dev, sizeof(struct corgi_lcd), GFP_KERNEL);
- if (!lcd) {
- dev_err(&spi->dev, "failed to allocate memory\n");
+ if (!lcd)
return -ENOMEM;
- }
lcd->spi_dev = spi;
diff --git a/drivers/video/backlight/hx8357.c b/drivers/video/backlight/hx8357.c
index 985e854e244b..23f50b92a930 100644
--- a/drivers/video/backlight/hx8357.c
+++ b/drivers/video/backlight/hx8357.c
@@ -587,10 +587,8 @@ static int hx8357_probe(struct spi_device *spi)
int i, ret;
lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
- if (!lcd) {
- dev_err(&spi->dev, "Couldn't allocate lcd internal structure!\n");
+ if (!lcd)
return -ENOMEM;
- }
ret = spi_setup(spi);
if (ret < 0) {
diff --git a/drivers/video/backlight/ili922x.c b/drivers/video/backlight/ili922x.c
index 73464e4b4c74..ea67fe199e34 100644
--- a/drivers/video/backlight/ili922x.c
+++ b/drivers/video/backlight/ili922x.c
@@ -482,10 +482,8 @@ static int ili922x_probe(struct spi_device *spi)
u16 reg = 0;
ili = devm_kzalloc(&spi->dev, sizeof(*ili), GFP_KERNEL);
- if (!ili) {
- dev_err(&spi->dev, "cannot alloc priv data\n");
+ if (!ili)
return -ENOMEM;
- }
ili->spi = spi;
spi_set_drvdata(spi, ili);
diff --git a/drivers/video/backlight/ili9320.c b/drivers/video/backlight/ili9320.c
index e2b8b40a9bd9..2cf39e6d519d 100644
--- a/drivers/video/backlight/ili9320.c
+++ b/drivers/video/backlight/ili9320.c
@@ -219,10 +219,8 @@ int ili9320_probe_spi(struct spi_device *spi,
/* allocate and initialse our state */
ili = devm_kzalloc(&spi->dev, sizeof(struct ili9320), GFP_KERNEL);
- if (ili == NULL) {
- dev_err(dev, "no memory for device\n");
+ if (ili == NULL)
return -ENOMEM;
- }
ili->access.spi.id = ILI9320_SPI_IDCODE | ILI9320_SPI_ID(1);
diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c
index 63e763828e0e..5fa2649c9631 100644
--- a/drivers/video/backlight/l4f00242t03.c
+++ b/drivers/video/backlight/l4f00242t03.c
@@ -181,11 +181,8 @@ static int l4f00242t03_probe(struct spi_device *spi)
priv = devm_kzalloc(&spi->dev, sizeof(struct l4f00242t03_priv),
GFP_KERNEL);
-
- if (priv == NULL) {
- dev_err(&spi->dev, "No memory for this device.\n");
+ if (priv == NULL)
return -ENOMEM;
- }
spi_set_drvdata(spi, priv);
spi->bits_per_word = 9;
diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c
index 187d1c283c1d..cff1fbe89a1b 100644
--- a/drivers/video/backlight/lm3533_bl.c
+++ b/drivers/video/backlight/lm3533_bl.c
@@ -296,11 +296,8 @@ static int lm3533_bl_probe(struct platform_device *pdev)
}
bl = devm_kzalloc(&pdev->dev, sizeof(*bl), GFP_KERNEL);
- if (!bl) {
- dev_err(&pdev->dev,
- "failed to allocate memory for backlight\n");
+ if (!bl)
return -ENOMEM;
- }
bl->lm3533 = lm3533;
bl->id = pdev->id;
diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c
index de8832504f68..14590c54aedf 100644
--- a/drivers/video/backlight/lms283gf05.c
+++ b/drivers/video/backlight/lms283gf05.c
@@ -168,10 +168,8 @@ static int lms283gf05_probe(struct spi_device *spi)
st = devm_kzalloc(&spi->dev, sizeof(struct lms283gf05_state),
GFP_KERNEL);
- if (st == NULL) {
- dev_err(&spi->dev, "No memory for device state\n");
+ if (st == NULL)
return -ENOMEM;
- }
ld = devm_lcd_device_register(&spi->dev, "lms283gf05", &spi->dev, st,
&lms_ops);
diff --git a/drivers/video/backlight/platform_lcd.c b/drivers/video/backlight/platform_lcd.c
index d01884d4f1bf..c3d2e209fc8f 100644
--- a/drivers/video/backlight/platform_lcd.c
+++ b/drivers/video/backlight/platform_lcd.c
@@ -94,10 +94,8 @@ static int platform_lcd_probe(struct platform_device *pdev)
plcd = devm_kzalloc(&pdev->dev, sizeof(struct platform_lcd),
GFP_KERNEL);
- if (!plcd) {
- dev_err(dev, "no memory for state\n");
+ if (!plcd)
return -ENOMEM;
- }
plcd->us = dev;
plcd->pdata = pdata;
diff --git a/drivers/video/backlight/tps65217_bl.c b/drivers/video/backlight/tps65217_bl.c
index cbba37e6836e..595dcf561020 100644
--- a/drivers/video/backlight/tps65217_bl.c
+++ b/drivers/video/backlight/tps65217_bl.c
@@ -200,7 +200,6 @@ tps65217_bl_parse_dt(struct platform_device *pdev)
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
- dev_err(&pdev->dev, "failed to allocate platform data\n");
err = ERR_PTR(-ENOMEM);
goto err;
}
@@ -296,10 +295,8 @@ static int tps65217_bl_probe(struct platform_device *pdev)
tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl),
GFP_KERNEL);
- if (tps65217_bl == NULL) {
- dev_err(&pdev->dev, "allocation of struct tps65217_bl failed\n");
+ if (tps65217_bl == NULL)
return -ENOMEM;
- }
tps65217_bl->tps = tps;
tps65217_bl->dev = &pdev->dev;
diff --git a/drivers/video/cfbcopyarea.c b/drivers/video/cfbcopyarea.c
index bb5a96b1645d..bcb57235fcc7 100644
--- a/drivers/video/cfbcopyarea.c
+++ b/drivers/video/cfbcopyarea.c
@@ -43,13 +43,22 @@
*/
static void
-bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
- const unsigned long __iomem *src, int src_idx, int bits,
+bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
+ const unsigned long __iomem *src, unsigned src_idx, int bits,
unsigned n, u32 bswapmask)
{
unsigned long first, last;
int const shift = dst_idx-src_idx;
- int left, right;
+
+#if 0
+ /*
+ * If you suspect bug in this function, compare it with this simple
+ * memmove implementation.
+ */
+ fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
+ (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
+ return;
+#endif
first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
@@ -98,9 +107,8 @@ bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
unsigned long d0, d1;
int m;
- right = shift & (bits - 1);
- left = -shift & (bits - 1);
- bswapmask &= shift;
+ int const left = shift & (bits - 1);
+ int const right = -shift & (bits - 1);
if (dst_idx+n <= bits) {
// Single destination word
@@ -110,15 +118,15 @@ bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
d0 = fb_rev_pixels_in_long(d0, bswapmask);
if (shift > 0) {
// Single source word
- d0 >>= right;
+ d0 <<= left;
} else if (src_idx+n <= bits) {
// Single source word
- d0 <<= left;
+ d0 >>= right;
} else {
// 2 source words
d1 = FB_READL(src + 1);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0<<left | d1>>right;
+ d0 = d0 >> right | d1 << left;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
@@ -135,60 +143,59 @@ bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
if (shift > 0) {
// Single source word
d1 = d0;
- d0 >>= right;
- dst++;
+ d0 <<= left;
n -= bits - dst_idx;
} else {
// 2 source words
d1 = FB_READL(src++);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0<<left | d1>>right;
- dst++;
+ d0 = d0 >> right | d1 << left;
n -= bits - dst_idx;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
d0 = d1;
+ dst++;
// Main chunk
m = n % bits;
n /= bits;
while ((n >= 4) && !bswapmask) {
d1 = FB_READL(src++);
- FB_WRITEL(d0 << left | d1 >> right, dst++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
d0 = d1;
d1 = FB_READL(src++);
- FB_WRITEL(d0 << left | d1 >> right, dst++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
d0 = d1;
d1 = FB_READL(src++);
- FB_WRITEL(d0 << left | d1 >> right, dst++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
d0 = d1;
d1 = FB_READL(src++);
- FB_WRITEL(d0 << left | d1 >> right, dst++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
d0 = d1;
n -= 4;
}
while (n--) {
d1 = FB_READL(src++);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0 << left | d1 >> right;
+ d0 = d0 >> right | d1 << left;
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(d0, dst++);
d0 = d1;
}
// Trailing bits
- if (last) {
- if (m <= right) {
+ if (m) {
+ if (m <= bits - right) {
// Single source word
- d0 <<= left;
+ d0 >>= right;
} else {
// 2 source words
d1 = FB_READL(src);
d1 = fb_rev_pixels_in_long(d1,
bswapmask);
- d0 = d0<<left | d1>>right;
+ d0 = d0 >> right | d1 << left;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
@@ -202,43 +209,46 @@ bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
*/
static void
-bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
- const unsigned long __iomem *src, int src_idx, int bits,
+bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
+ const unsigned long __iomem *src, unsigned src_idx, int bits,
unsigned n, u32 bswapmask)
{
unsigned long first, last;
int shift;
- dst += (n-1)/bits;
- src += (n-1)/bits;
- if ((n-1) % bits) {
- dst_idx += (n-1) % bits;
- dst += dst_idx >> (ffs(bits) - 1);
- dst_idx &= bits - 1;
- src_idx += (n-1) % bits;
- src += src_idx >> (ffs(bits) - 1);
- src_idx &= bits - 1;
- }
+#if 0
+ /*
+ * If you suspect bug in this function, compare it with this simple
+ * memmove implementation.
+ */
+ fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
+ (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
+ return;
+#endif
+
+ dst += (dst_idx + n - 1) / bits;
+ src += (src_idx + n - 1) / bits;
+ dst_idx = (dst_idx + n - 1) % bits;
+ src_idx = (src_idx + n - 1) % bits;
shift = dst_idx-src_idx;
- first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
- last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
- bswapmask);
+ first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
+ last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
if (!shift) {
// Same alignment for source and dest
if ((unsigned long)dst_idx+1 >= n) {
// Single word
- if (last)
- first &= last;
- FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
+ if (first)
+ last &= first;
+ FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
} else {
// Multiple destination words
// Leading bits
- if (first != ~0UL) {
+ if (first) {
FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
dst--;
src--;
@@ -262,7 +272,7 @@ bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
FB_WRITEL(FB_READL(src--), dst--);
// Trailing bits
- if (last)
+ if (last != -1UL)
FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
}
} else {
@@ -270,29 +280,28 @@ bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
unsigned long d0, d1;
int m;
- int const left = -shift & (bits-1);
- int const right = shift & (bits-1);
- bswapmask &= shift;
+ int const left = shift & (bits-1);
+ int const right = -shift & (bits-1);
if ((unsigned long)dst_idx+1 >= n) {
// Single destination word
- if (last)
- first &= last;
+ if (first)
+ last &= first;
d0 = FB_READL(src);
if (shift < 0) {
// Single source word
- d0 <<= left;
+ d0 >>= right;
} else if (1+(unsigned long)src_idx >= n) {
// Single source word
- d0 >>= right;
+ d0 <<= left;
} else {
// 2 source words
d1 = FB_READL(src - 1);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0>>right | d1<<left;
+ d0 = d0 << left | d1 >> right;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
- FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
+ FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
} else {
// Multiple destination words
/** We must always remember the last value read, because in case
@@ -307,12 +316,12 @@ bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
if (shift < 0) {
// Single source word
d1 = d0;
- d0 <<= left;
+ d0 >>= right;
} else {
// 2 source words
d1 = FB_READL(src--);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0>>right | d1<<left;
+ d0 = d0 << left | d1 >> right;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
@@ -325,39 +334,39 @@ bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
n /= bits;
while ((n >= 4) && !bswapmask) {
d1 = FB_READL(src--);
- FB_WRITEL(d0 >> right | d1 << left, dst--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
d0 = d1;
d1 = FB_READL(src--);
- FB_WRITEL(d0 >> right | d1 << left, dst--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
d0 = d1;
d1 = FB_READL(src--);
- FB_WRITEL(d0 >> right | d1 << left, dst--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
d0 = d1;
d1 = FB_READL(src--);
- FB_WRITEL(d0 >> right | d1 << left, dst--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
d0 = d1;
n -= 4;
}
while (n--) {
d1 = FB_READL(src--);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0 >> right | d1 << left;
+ d0 = d0 << left | d1 >> right;
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(d0, dst--);
d0 = d1;
}
// Trailing bits
- if (last) {
- if (m <= left) {
+ if (m) {
+ if (m <= bits - left) {
// Single source word
- d0 >>= right;
+ d0 <<= left;
} else {
// 2 source words
d1 = FB_READL(src);
d1 = fb_rev_pixels_in_long(d1,
bswapmask);
- d0 = d0>>right | d1<<left;
+ d0 = d0 << left | d1 >> right;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
@@ -371,9 +380,9 @@ void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
u32 height = area->height, width = area->width;
unsigned long const bits_per_line = p->fix.line_length*8u;
- unsigned long __iomem *dst = NULL, *src = NULL;
+ unsigned long __iomem *base = NULL;
int bits = BITS_PER_LONG, bytes = bits >> 3;
- int dst_idx = 0, src_idx = 0, rev_copy = 0;
+ unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
u32 bswapmask = fb_compute_bswapmask(p);
if (p->state != FBINFO_STATE_RUNNING)
@@ -389,7 +398,7 @@ void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
// split the base of the framebuffer into a long-aligned address and the
// index of the first bit
- dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
+ base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
// add offset of source and target area
dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
@@ -402,20 +411,14 @@ void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
while (height--) {
dst_idx -= bits_per_line;
src_idx -= bits_per_line;
- dst += dst_idx >> (ffs(bits) - 1);
- dst_idx &= (bytes - 1);
- src += src_idx >> (ffs(bits) - 1);
- src_idx &= (bytes - 1);
- bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
+ bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
+ base + (src_idx / bits), src_idx % bits, bits,
width*p->var.bits_per_pixel, bswapmask);
}
} else {
while (height--) {
- dst += dst_idx >> (ffs(bits) - 1);
- dst_idx &= (bytes - 1);
- src += src_idx >> (ffs(bits) - 1);
- src_idx &= (bytes - 1);
- bitcpy(p, dst, dst_idx, src, src_idx, bits,
+ bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
+ base + (src_idx / bits), src_idx % bits, bits,
width*p->var.bits_per_pixel, bswapmask);
dst_idx += bits_per_line;
src_idx += bits_per_line;
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 4e39291ac8b4..f447734b09b4 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -759,7 +759,7 @@ static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
newinfo in an undefined state. Thus, a call to
fb_set_par() may be needed for the newinfo.
*/
- if (newinfo->fbops->fb_set_par) {
+ if (newinfo && newinfo->fbops->fb_set_par) {
ret = newinfo->fbops->fb_set_par(newinfo);
if (ret)
@@ -3028,8 +3028,31 @@ static int fbcon_fb_unbind(int idx)
if (con2fb_map[i] == idx)
set_con2fb_map(i, new_idx, 0);
}
- } else
+ } else {
+ struct fb_info *info = registered_fb[idx];
+
+ /* This is sort of like set_con2fb_map, except it maps
+ * the consoles to no device and then releases the
+ * oldinfo to free memory and cancel the cursor blink
+ * timer. I can imagine this just becoming part of
+ * set_con2fb_map where new_idx is -1
+ */
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
+ if (con2fb_map[i] == idx) {
+ con2fb_map[i] = -1;
+ if (!search_fb_in_map(idx)) {
+ ret = con2fb_release_oldinfo(vc_cons[i].d,
+ info, NULL, i,
+ idx, 0);
+ if (ret) {
+ con2fb_map[i] = idx;
+ return ret;
+ }
+ }
+ }
+ }
ret = fbcon_unbind();
+ }
return ret;
}
diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c
index a1d74dd11988..0c0ba920ea48 100644
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
@@ -1546,7 +1546,7 @@ err_pm_runtime_disable:
return ret;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static struct lcdc_context {
u32 clk_enable;
u32 ctrl;
@@ -1610,9 +1610,9 @@ static void lcd_context_restore(void)
return;
}
-static int fb_suspend(struct platform_device *dev, pm_message_t state)
+static int fb_suspend(struct device *dev)
{
- struct fb_info *info = platform_get_drvdata(dev);
+ struct fb_info *info = dev_get_drvdata(dev);
struct da8xx_fb_par *par = info->par;
console_lock();
@@ -1622,18 +1622,18 @@ static int fb_suspend(struct platform_device *dev, pm_message_t state)
fb_set_suspend(info, 1);
lcd_disable_raster(DA8XX_FRAME_WAIT);
lcd_context_save();
- pm_runtime_put_sync(&dev->dev);
+ pm_runtime_put_sync(dev);
console_unlock();
return 0;
}
-static int fb_resume(struct platform_device *dev)
+static int fb_resume(struct device *dev)
{
- struct fb_info *info = platform_get_drvdata(dev);
+ struct fb_info *info = dev_get_drvdata(dev);
struct da8xx_fb_par *par = info->par;
console_lock();
- pm_runtime_get_sync(&dev->dev);
+ pm_runtime_get_sync(dev);
lcd_context_restore();
if (par->blank == FB_BLANK_UNBLANK) {
lcd_enable_raster();
@@ -1647,19 +1647,17 @@ static int fb_resume(struct platform_device *dev)
return 0;
}
-#else
-#define fb_suspend NULL
-#define fb_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(fb_pm_ops, fb_suspend, fb_resume);
+
static struct platform_driver da8xx_fb_driver = {
.probe = fb_probe,
.remove = fb_remove,
- .suspend = fb_suspend,
- .resume = fb_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .pm = &fb_pm_ops,
},
};
module_platform_driver(da8xx_fb_driver);
diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c
index cd7c0df9f24b..ae9618ff6735 100644
--- a/drivers/video/efifb.c
+++ b/drivers/video/efifb.c
@@ -73,7 +73,6 @@ static void efifb_destroy(struct fb_info *info)
release_mem_region(info->apertures->ranges[0].base,
info->apertures->ranges[0].size);
fb_dealloc_cmap(&info->cmap);
- framebuffer_release(info);
}
static struct fb_ops efifb_ops = {
@@ -244,6 +243,7 @@ static int efifb_probe(struct platform_device *dev)
err = -ENOMEM;
goto err_release_mem;
}
+ platform_set_drvdata(dev, info);
info->pseudo_palette = info->par;
info->par = NULL;
@@ -337,12 +337,23 @@ err_release_mem:
return err;
}
+static int efifb_remove(struct platform_device *pdev)
+{
+ struct fb_info *info = platform_get_drvdata(pdev);
+
+ unregister_framebuffer(info);
+ framebuffer_release(info);
+
+ return 0;
+}
+
static struct platform_driver efifb_driver = {
.driver = {
.name = "efi-framebuffer",
.owner = THIS_MODULE,
},
.probe = efifb_probe,
+ .remove = efifb_remove,
};
module_platform_driver(efifb_driver);
diff --git a/drivers/video/exynos/Kconfig b/drivers/video/exynos/Kconfig
index 75c8a8e7efc0..eb6f2b059821 100644
--- a/drivers/video/exynos/Kconfig
+++ b/drivers/video/exynos/Kconfig
@@ -31,7 +31,7 @@ config EXYNOS_LCD_S6E8AX0
config EXYNOS_DP
bool "EXYNOS DP driver support"
- depends on OF && ARCH_EXYNOS
+ depends on ARCH_EXYNOS
default n
help
This enables support for DP device.
diff --git a/drivers/video/exynos/s6e8ax0.c b/drivers/video/exynos/s6e8ax0.c
index ca2602413aa4..29e70ed3f154 100644
--- a/drivers/video/exynos/s6e8ax0.c
+++ b/drivers/video/exynos/s6e8ax0.c
@@ -794,19 +794,18 @@ static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
return ret;
}
- lcd->ld = lcd_device_register("s6e8ax0", lcd->dev, lcd,
+ lcd->ld = devm_lcd_device_register(lcd->dev, "s6e8ax0", lcd->dev, lcd,
&s6e8ax0_lcd_ops);
if (IS_ERR(lcd->ld)) {
dev_err(lcd->dev, "failed to register lcd ops.\n");
return PTR_ERR(lcd->ld);
}
- lcd->bd = backlight_device_register("s6e8ax0-bl", lcd->dev, lcd,
- &s6e8ax0_backlight_ops, NULL);
+ lcd->bd = devm_backlight_device_register(lcd->dev, "s6e8ax0-bl",
+ lcd->dev, lcd, &s6e8ax0_backlight_ops, NULL);
if (IS_ERR(lcd->bd)) {
dev_err(lcd->dev, "failed to register backlight ops.\n");
- ret = PTR_ERR(lcd->bd);
- goto err_backlight_register;
+ return PTR_ERR(lcd->bd);
}
lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
@@ -834,10 +833,6 @@ static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n");
return 0;
-
-err_backlight_register:
- lcd_device_unregister(lcd->ld);
- return ret;
}
#ifdef CONFIG_PM
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 7309ac704e26..b6d5008f361f 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1596,8 +1596,7 @@ static int do_remove_conflicting_framebuffers(struct apertures_struct *a,
(primary && gen_aper && gen_aper->count &&
gen_aper->ranges[0].base == VGA_FB_PHYS)) {
- printk(KERN_INFO "fb: conflicting fb hw usage "
- "%s vs %s - removing generic driver\n",
+ printk(KERN_INFO "fb: switching to %s from %s\n",
name, registered_fb[i]->fix.id);
ret = do_unregister_framebuffer(registered_fb[i]);
if (ret)
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
index 44ee678481d5..f6e621684953 100644
--- a/drivers/video/imxfb.c
+++ b/drivers/video/imxfb.c
@@ -30,10 +30,13 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
+#include <linux/lcd.h>
#include <linux/math64.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
#include <video/videomode.h>
@@ -45,12 +48,6 @@
*/
#define DEBUG_VAR 1
-#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
- (defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) && \
- defined(CONFIG_FB_IMX_MODULE))
-#define PWMR_BACKLIGHT_AVAILABLE
-#endif
-
#define DRIVER_NAME "imx-fb"
#define LCDC_SSA 0x00
@@ -153,11 +150,8 @@ struct imxfb_info {
* the framebuffer memory region to.
*/
dma_addr_t map_dma;
- u_char *map_cpu;
u_int map_size;
- u_char *screen_cpu;
- dma_addr_t screen_dma;
u_int palette_size;
dma_addr_t dbar1;
@@ -167,18 +161,13 @@ struct imxfb_info {
u_int pwmr;
u_int lscr1;
u_int dmacr;
- u_int cmap_inverse:1,
- cmap_static:1,
- unused:30;
+ bool cmap_inverse;
+ bool cmap_static;
struct imx_fb_videomode *mode;
int num_modes;
-#ifdef PWMR_BACKLIGHT_AVAILABLE
- struct backlight_device *bl;
-#endif
- void (*lcd_power)(int);
- void (*backlight_power)(int);
+ struct regulator *lcd_pwr;
};
static struct platform_device_id imxfb_devtype[] = {
@@ -484,83 +473,6 @@ static int imxfb_set_par(struct fb_info *info)
return 0;
}
-#ifdef PWMR_BACKLIGHT_AVAILABLE
-static int imxfb_bl_get_brightness(struct backlight_device *bl)
-{
- struct imxfb_info *fbi = bl_get_data(bl);
-
- return readl(fbi->regs + LCDC_PWMR) & 0xFF;
-}
-
-static int imxfb_bl_update_status(struct backlight_device *bl)
-{
- struct imxfb_info *fbi = bl_get_data(bl);
- int brightness = bl->props.brightness;
-
- if (!fbi->pwmr)
- return 0;
-
- if (bl->props.power != FB_BLANK_UNBLANK)
- brightness = 0;
- if (bl->props.fb_blank != FB_BLANK_UNBLANK)
- brightness = 0;
-
- fbi->pwmr = (fbi->pwmr & ~0xFF) | brightness;
-
- if (bl->props.fb_blank != FB_BLANK_UNBLANK) {
- clk_prepare_enable(fbi->clk_ipg);
- clk_prepare_enable(fbi->clk_ahb);
- clk_prepare_enable(fbi->clk_per);
- }
- writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
- if (bl->props.fb_blank != FB_BLANK_UNBLANK) {
- clk_disable_unprepare(fbi->clk_per);
- clk_disable_unprepare(fbi->clk_ahb);
- clk_disable_unprepare(fbi->clk_ipg);
- }
-
- return 0;
-}
-
-static const struct backlight_ops imxfb_lcdc_bl_ops = {
- .update_status = imxfb_bl_update_status,
- .get_brightness = imxfb_bl_get_brightness,
-};
-
-static void imxfb_init_backlight(struct imxfb_info *fbi)
-{
- struct backlight_properties props;
- struct backlight_device *bl;
-
- if (fbi->bl)
- return;
-
- memset(&props, 0, sizeof(struct backlight_properties));
- props.max_brightness = 0xff;
- props.type = BACKLIGHT_RAW;
- writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
-
- bl = backlight_device_register("imxfb-bl", &fbi->pdev->dev, fbi,
- &imxfb_lcdc_bl_ops, &props);
- if (IS_ERR(bl)) {
- dev_err(&fbi->pdev->dev, "error %ld on backlight register\n",
- PTR_ERR(bl));
- return;
- }
-
- fbi->bl = bl;
- bl->props.power = FB_BLANK_UNBLANK;
- bl->props.fb_blank = FB_BLANK_UNBLANK;
- bl->props.brightness = imxfb_bl_get_brightness(bl);
-}
-
-static void imxfb_exit_backlight(struct imxfb_info *fbi)
-{
- if (fbi->bl)
- backlight_device_unregister(fbi->bl);
-}
-#endif
-
static void imxfb_enable_controller(struct imxfb_info *fbi)
{
@@ -569,7 +481,7 @@ static void imxfb_enable_controller(struct imxfb_info *fbi)
pr_debug("Enabling LCD controller\n");
- writel(fbi->screen_dma, fbi->regs + LCDC_SSA);
+ writel(fbi->map_dma, fbi->regs + LCDC_SSA);
/* panning offset 0 (0 pixel offset) */
writel(0x00000000, fbi->regs + LCDC_POS);
@@ -588,11 +500,6 @@ static void imxfb_enable_controller(struct imxfb_info *fbi)
clk_prepare_enable(fbi->clk_ahb);
clk_prepare_enable(fbi->clk_per);
fbi->enabled = true;
-
- if (fbi->backlight_power)
- fbi->backlight_power(1);
- if (fbi->lcd_power)
- fbi->lcd_power(1);
}
static void imxfb_disable_controller(struct imxfb_info *fbi)
@@ -602,11 +509,6 @@ static void imxfb_disable_controller(struct imxfb_info *fbi)
pr_debug("Disabling LCD controller\n");
- if (fbi->backlight_power)
- fbi->backlight_power(0);
- if (fbi->lcd_power)
- fbi->lcd_power(0);
-
clk_disable_unprepare(fbi->clk_per);
clk_disable_unprepare(fbi->clk_ipg);
clk_disable_unprepare(fbi->clk_ahb);
@@ -709,10 +611,8 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
fbi->regs + LCDC_SIZE);
writel(fbi->pcr, fbi->regs + LCDC_PCR);
-#ifndef PWMR_BACKLIGHT_AVAILABLE
if (fbi->pwmr)
writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
-#endif
writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
/* dmacr = 0 is no valid value, as we need DMA control marks. */
@@ -722,37 +622,6 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
return 0;
}
-#ifdef CONFIG_PM
-/*
- * Power management hooks. Note that we won't be called from IRQ context,
- * unlike the blank functions above, so we may sleep.
- */
-static int imxfb_suspend(struct platform_device *dev, pm_message_t state)
-{
- struct fb_info *info = platform_get_drvdata(dev);
- struct imxfb_info *fbi = info->par;
-
- pr_debug("%s\n", __func__);
-
- imxfb_disable_controller(fbi);
- return 0;
-}
-
-static int imxfb_resume(struct platform_device *dev)
-{
- struct fb_info *info = platform_get_drvdata(dev);
- struct imxfb_info *fbi = info->par;
-
- pr_debug("%s\n", __func__);
-
- imxfb_enable_controller(fbi);
- return 0;
-}
-#else
-#define imxfb_suspend NULL
-#define imxfb_resume NULL
-#endif
-
static int imxfb_init_fbinfo(struct platform_device *pdev)
{
struct imx_fb_platform_data *pdata = dev_get_platdata(&pdev->dev);
@@ -790,14 +659,9 @@ static int imxfb_init_fbinfo(struct platform_device *pdev)
info->flags = FBINFO_FLAG_DEFAULT |
FBINFO_READS_FAST;
if (pdata) {
- info->var.grayscale = pdata->cmap_greyscale;
- fbi->cmap_inverse = pdata->cmap_inverse;
- fbi->cmap_static = pdata->cmap_static;
fbi->lscr1 = pdata->lscr1;
fbi->dmacr = pdata->dmacr;
fbi->pwmr = pdata->pwmr;
- fbi->lcd_power = pdata->lcd_power;
- fbi->backlight_power = pdata->backlight_power;
} else {
np = pdev->dev.of_node;
info->var.grayscale = of_property_read_bool(np,
@@ -806,14 +670,12 @@ static int imxfb_init_fbinfo(struct platform_device *pdev)
fbi->cmap_static = of_property_read_bool(np, "cmap-static");
fbi->lscr1 = IMXFB_LSCR1_DEFAULT;
+
+ of_property_read_u32(np, "fsl,lpccr", &fbi->pwmr);
+
of_property_read_u32(np, "fsl,lscr1", &fbi->lscr1);
of_property_read_u32(np, "fsl,dmacr", &fbi->dmacr);
-
- /* These two function pointers could be used by some specific
- * platforms. */
- fbi->lcd_power = NULL;
- fbi->backlight_power = NULL;
}
return 0;
@@ -856,9 +718,98 @@ static int imxfb_of_read_mode(struct device *dev, struct device_node *np,
return 0;
}
+static int imxfb_lcd_check_fb(struct lcd_device *lcddev, struct fb_info *fi)
+{
+ struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev);
+
+ if (!fi || fi->par == fbi)
+ return 1;
+
+ return 0;
+}
+
+static int imxfb_lcd_get_contrast(struct lcd_device *lcddev)
+{
+ struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev);
+
+ return fbi->pwmr & 0xff;
+}
+
+static int imxfb_lcd_set_contrast(struct lcd_device *lcddev, int contrast)
+{
+ struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev);
+
+ if (fbi->pwmr && fbi->enabled) {
+ if (contrast > 255)
+ contrast = 255;
+ else if (contrast < 0)
+ contrast = 0;
+
+ fbi->pwmr &= ~0xff;
+ fbi->pwmr |= contrast;
+
+ writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+ }
+
+ return 0;
+}
+
+static int imxfb_lcd_get_power(struct lcd_device *lcddev)
+{
+ struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev);
+
+ if (!IS_ERR(fbi->lcd_pwr))
+ return regulator_is_enabled(fbi->lcd_pwr);
+
+ return 1;
+}
+
+static int imxfb_lcd_set_power(struct lcd_device *lcddev, int power)
+{
+ struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev);
+
+ if (!IS_ERR(fbi->lcd_pwr)) {
+ if (power)
+ return regulator_enable(fbi->lcd_pwr);
+ else
+ return regulator_disable(fbi->lcd_pwr);
+ }
+
+ return 0;
+}
+
+static struct lcd_ops imxfb_lcd_ops = {
+ .check_fb = imxfb_lcd_check_fb,
+ .get_contrast = imxfb_lcd_get_contrast,
+ .set_contrast = imxfb_lcd_set_contrast,
+ .get_power = imxfb_lcd_get_power,
+ .set_power = imxfb_lcd_set_power,
+};
+
+static int imxfb_setup(void)
+{
+ char *opt, *options = NULL;
+
+ if (fb_get_options("imxfb", &options))
+ return -ENODEV;
+
+ if (!options || !*options)
+ return 0;
+
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+ else
+ fb_mode = opt;
+ }
+
+ return 0;
+}
+
static int imxfb_probe(struct platform_device *pdev)
{
struct imxfb_info *fbi;
+ struct lcd_device *lcd;
struct fb_info *info;
struct imx_fb_platform_data *pdata;
struct resource *res;
@@ -869,6 +820,10 @@ static int imxfb_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "i.MX Framebuffer driver\n");
+ ret = imxfb_setup();
+ if (ret < 0)
+ return ret;
+
of_id = of_match_device(imxfb_of_dev_id, &pdev->dev);
if (of_id)
pdev->id_entry = of_id->data;
@@ -966,32 +921,18 @@ static int imxfb_probe(struct platform_device *pdev)
goto failed_ioremap;
}
- /* Seems not being used by anyone, so no support for oftree */
- if (!pdata || !pdata->fixed_screen_cpu) {
- fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
- fbi->map_cpu = dma_alloc_writecombine(&pdev->dev,
- fbi->map_size, &fbi->map_dma, GFP_KERNEL);
+ fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
+ info->screen_base = dma_alloc_writecombine(&pdev->dev, fbi->map_size,
+ &fbi->map_dma, GFP_KERNEL);
- if (!fbi->map_cpu) {
- dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret);
- ret = -ENOMEM;
- goto failed_map;
- }
-
- info->screen_base = fbi->map_cpu;
- fbi->screen_cpu = fbi->map_cpu;
- fbi->screen_dma = fbi->map_dma;
- info->fix.smem_start = fbi->screen_dma;
- } else {
- /* Fixed framebuffer mapping enables location of the screen in eSRAM */
- fbi->map_cpu = pdata->fixed_screen_cpu;
- fbi->map_dma = pdata->fixed_screen_dma;
- info->screen_base = fbi->map_cpu;
- fbi->screen_cpu = fbi->map_cpu;
- fbi->screen_dma = fbi->map_dma;
- info->fix.smem_start = fbi->screen_dma;
+ if (!info->screen_base) {
+ dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret);
+ ret = -ENOMEM;
+ goto failed_map;
}
+ info->fix.smem_start = fbi->map_dma;
+
if (pdata && pdata->init) {
ret = pdata->init(fbi->pdev);
if (ret)
@@ -1020,23 +961,37 @@ static int imxfb_probe(struct platform_device *pdev)
goto failed_register;
}
+ fbi->lcd_pwr = devm_regulator_get(&pdev->dev, "lcd");
+ if (IS_ERR(fbi->lcd_pwr) && (PTR_ERR(fbi->lcd_pwr) == -EPROBE_DEFER)) {
+ ret = -EPROBE_DEFER;
+ goto failed_lcd;
+ }
+
+ lcd = devm_lcd_device_register(&pdev->dev, "imxfb-lcd", &pdev->dev, fbi,
+ &imxfb_lcd_ops);
+ if (IS_ERR(lcd)) {
+ ret = PTR_ERR(lcd);
+ goto failed_lcd;
+ }
+
+ lcd->props.max_contrast = 0xff;
+
imxfb_enable_controller(fbi);
fbi->pdev = pdev;
-#ifdef PWMR_BACKLIGHT_AVAILABLE
- imxfb_init_backlight(fbi);
-#endif
return 0;
+failed_lcd:
+ unregister_framebuffer(info);
+
failed_register:
fb_dealloc_cmap(&info->cmap);
failed_cmap:
if (pdata && pdata->exit)
pdata->exit(fbi->pdev);
failed_platform_init:
- if (pdata && !pdata->fixed_screen_cpu)
- dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu,
- fbi->map_dma);
+ dma_free_writecombine(&pdev->dev, fbi->map_size, info->screen_base,
+ fbi->map_dma);
failed_map:
iounmap(fbi->regs);
failed_ioremap:
@@ -1061,9 +1016,6 @@ static int imxfb_remove(struct platform_device *pdev)
imxfb_disable_controller(fbi);
-#ifdef PWMR_BACKLIGHT_AVAILABLE
- imxfb_exit_backlight(fbi);
-#endif
unregister_framebuffer(info);
pdata = dev_get_platdata(&pdev->dev);
@@ -1074,69 +1026,49 @@ static int imxfb_remove(struct platform_device *pdev)
kfree(info->pseudo_palette);
framebuffer_release(info);
+ dma_free_writecombine(&pdev->dev, fbi->map_size, info->screen_base,
+ fbi->map_dma);
+
iounmap(fbi->regs);
release_mem_region(res->start, resource_size(res));
return 0;
}
-static void imxfb_shutdown(struct platform_device *dev)
+static int __maybe_unused imxfb_suspend(struct device *dev)
{
- struct fb_info *info = platform_get_drvdata(dev);
+ struct fb_info *info = dev_get_drvdata(dev);
struct imxfb_info *fbi = info->par;
- imxfb_disable_controller(fbi);
-}
-
-static struct platform_driver imxfb_driver = {
- .suspend = imxfb_suspend,
- .resume = imxfb_resume,
- .remove = imxfb_remove,
- .shutdown = imxfb_shutdown,
- .driver = {
- .name = DRIVER_NAME,
- .of_match_table = imxfb_of_dev_id,
- },
- .id_table = imxfb_devtype,
-};
-
-static int imxfb_setup(void)
-{
-#ifndef MODULE
- char *opt, *options = NULL;
- if (fb_get_options("imxfb", &options))
- return -ENODEV;
-
- if (!options || !*options)
- return 0;
+ imxfb_disable_controller(fbi);
- while ((opt = strsep(&options, ",")) != NULL) {
- if (!*opt)
- continue;
- else
- fb_mode = opt;
- }
-#endif
return 0;
}
-static int __init imxfb_init(void)
+static int __maybe_unused imxfb_resume(struct device *dev)
{
- int ret = imxfb_setup();
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct imxfb_info *fbi = info->par;
- if (ret < 0)
- return ret;
+ imxfb_enable_controller(fbi);
- return platform_driver_probe(&imxfb_driver, imxfb_probe);
+ return 0;
}
-static void __exit imxfb_cleanup(void)
-{
- platform_driver_unregister(&imxfb_driver);
-}
+static SIMPLE_DEV_PM_OPS(imxfb_pm_ops, imxfb_suspend, imxfb_resume);
-module_init(imxfb_init);
-module_exit(imxfb_cleanup);
+static struct platform_driver imxfb_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = imxfb_of_dev_id,
+ .owner = THIS_MODULE,
+ .pm = &imxfb_pm_ops,
+ },
+ .probe = imxfb_probe,
+ .remove = imxfb_remove,
+ .id_table = imxfb_devtype,
+};
+module_platform_driver(imxfb_driver);
MODULE_DESCRIPTION("Freescale i.MX framebuffer driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
diff --git a/drivers/video/matrox/matroxfb_accel.c b/drivers/video/matrox/matroxfb_accel.c
index 8335a6fe303e..0d5cb85d071a 100644
--- a/drivers/video/matrox/matroxfb_accel.c
+++ b/drivers/video/matrox/matroxfb_accel.c
@@ -192,10 +192,18 @@ void matrox_cfbX_init(struct matrox_fb_info *minfo)
minfo->accel.m_dwg_rect = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO;
if (isMilleniumII(minfo)) minfo->accel.m_dwg_rect |= M_DWG_TRANSC;
minfo->accel.m_opmode = mopmode;
+ minfo->accel.m_access = maccess;
+ minfo->accel.m_pitch = mpitch;
}
EXPORT_SYMBOL(matrox_cfbX_init);
+static void matrox_accel_restore_maccess(struct matrox_fb_info *minfo)
+{
+ mga_outl(M_MACCESS, minfo->accel.m_access);
+ mga_outl(M_PITCH, minfo->accel.m_pitch);
+}
+
static void matrox_accel_bmove(struct matrox_fb_info *minfo, int vxres, int sy,
int sx, int dy, int dx, int height, int width)
{
@@ -207,7 +215,8 @@ static void matrox_accel_bmove(struct matrox_fb_info *minfo, int vxres, int sy,
CRITBEGIN
if ((dy < sy) || ((dy == sy) && (dx <= sx))) {
- mga_fifo(2);
+ mga_fifo(4);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO |
M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_AR5, vxres);
@@ -215,7 +224,8 @@ static void matrox_accel_bmove(struct matrox_fb_info *minfo, int vxres, int sy,
start = sy*vxres+sx+curr_ydstorg(minfo);
end = start+width;
} else {
- mga_fifo(3);
+ mga_fifo(5);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_SGN, 5);
mga_outl(M_AR5, -vxres);
@@ -224,7 +234,8 @@ static void matrox_accel_bmove(struct matrox_fb_info *minfo, int vxres, int sy,
start = end+width;
dy += height-1;
}
- mga_fifo(4);
+ mga_fifo(6);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_AR0, end);
mga_outl(M_AR3, start);
mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx);
@@ -246,7 +257,8 @@ static void matrox_accel_bmove_lin(struct matrox_fb_info *minfo, int vxres,
CRITBEGIN
if ((dy < sy) || ((dy == sy) && (dx <= sx))) {
- mga_fifo(2);
+ mga_fifo(4);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO |
M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_AR5, vxres);
@@ -254,7 +266,8 @@ static void matrox_accel_bmove_lin(struct matrox_fb_info *minfo, int vxres,
start = sy*vxres+sx+curr_ydstorg(minfo);
end = start+width;
} else {
- mga_fifo(3);
+ mga_fifo(5);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_SGN, 5);
mga_outl(M_AR5, -vxres);
@@ -263,7 +276,8 @@ static void matrox_accel_bmove_lin(struct matrox_fb_info *minfo, int vxres,
start = end+width;
dy += height-1;
}
- mga_fifo(5);
+ mga_fifo(7);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_AR0, end);
mga_outl(M_AR3, start);
mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx);
@@ -298,7 +312,8 @@ static void matroxfb_accel_clear(struct matrox_fb_info *minfo, u_int32_t color,
CRITBEGIN
- mga_fifo(5);
+ mga_fifo(7);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE);
mga_outl(M_FCOL, color);
mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx);
@@ -341,7 +356,8 @@ static void matroxfb_cfb4_clear(struct matrox_fb_info *minfo, u_int32_t bgx,
width >>= 1;
sx >>= 1;
if (width) {
- mga_fifo(5);
+ mga_fifo(7);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE2);
mga_outl(M_FCOL, bgx);
mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx);
@@ -415,7 +431,8 @@ static void matroxfb_1bpp_imageblit(struct matrox_fb_info *minfo, u_int32_t fgx,
CRITBEGIN
- mga_fifo(3);
+ mga_fifo(5);
+ matrox_accel_restore_maccess(minfo);
if (easy)
mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE);
else
@@ -425,7 +442,8 @@ static void matroxfb_1bpp_imageblit(struct matrox_fb_info *minfo, u_int32_t fgx,
fxbndry = ((xx + width - 1) << 16) | xx;
mmio = minfo->mmio.vbase;
- mga_fifo(6);
+ mga_fifo(8);
+ matrox_accel_restore_maccess(minfo);
mga_writel(mmio, M_FXBNDRY, fxbndry);
mga_writel(mmio, M_AR0, ar0);
mga_writel(mmio, M_AR3, 0);
diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c
index 87c64ff4546c..7116c5309c7d 100644
--- a/drivers/video/matrox/matroxfb_base.c
+++ b/drivers/video/matrox/matroxfb_base.c
@@ -1773,7 +1773,8 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b)
FBINFO_HWACCEL_FILLRECT | /* And fillrect */
FBINFO_HWACCEL_IMAGEBLIT | /* And imageblit */
FBINFO_HWACCEL_XPAN | /* And we support both horizontal */
- FBINFO_HWACCEL_YPAN; /* And vertical panning */
+ FBINFO_HWACCEL_YPAN | /* And vertical panning */
+ FBINFO_READS_FAST;
minfo->video.len_usable &= PAGE_MASK;
fb_alloc_cmap(&minfo->fbcon.cmap, 256, 1);
diff --git a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h
index 11ed57bb704e..556d96ce40bf 100644
--- a/drivers/video/matrox/matroxfb_base.h
+++ b/drivers/video/matrox/matroxfb_base.h
@@ -307,6 +307,8 @@ struct matrox_accel_data {
#endif
u_int32_t m_dwg_rect;
u_int32_t m_opmode;
+ u_int32_t m_access;
+ u_int32_t m_pitch;
};
struct v4l2_queryctrl;
diff --git a/drivers/video/omap2/displays-new/connector-analog-tv.c b/drivers/video/omap2/displays-new/connector-analog-tv.c
index ccd9073f706f..5ee3b5505f7f 100644
--- a/drivers/video/omap2/displays-new/connector-analog-tv.c
+++ b/drivers/video/omap2/displays-new/connector-analog-tv.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
@@ -31,7 +32,7 @@ struct panel_drv_data {
static const struct omap_video_timings tvc_pal_timings = {
.x_res = 720,
.y_res = 574,
- .pixel_clock = 13500,
+ .pixelclock = 13500000,
.hsw = 64,
.hfp = 12,
.hbp = 68,
@@ -42,6 +43,12 @@ static const struct omap_video_timings tvc_pal_timings = {
.interlace = true,
};
+static const struct of_device_id tvc_of_match[];
+
+struct tvc_of_data {
+ enum omap_dss_venc_type connector_type;
+};
+
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int tvc_connect(struct omap_dss_device *dssdev)
@@ -91,8 +98,12 @@ static int tvc_enable(struct omap_dss_device *dssdev)
in->ops.atv->set_timings(in, &ddata->timings);
- in->ops.atv->set_type(in, ddata->connector_type);
- in->ops.atv->invert_vid_out_polarity(in, ddata->invert_polarity);
+ if (!ddata->dev->of_node) {
+ in->ops.atv->set_type(in, ddata->connector_type);
+
+ in->ops.atv->invert_vid_out_polarity(in,
+ ddata->invert_polarity);
+ }
r = in->ops.atv->enable(in);
if (r)
@@ -205,6 +216,23 @@ static int tvc_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int tvc_probe_of(struct platform_device *pdev)
+{
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+ struct omap_dss_device *in;
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ return 0;
+}
+
static int tvc_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
@@ -222,6 +250,10 @@ static int tvc_probe(struct platform_device *pdev)
r = tvc_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = tvc_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
@@ -263,12 +295,19 @@ static int __exit tvc_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id tvc_of_match[] = {
+ { .compatible = "omapdss,svideo-connector", },
+ { .compatible = "omapdss,composite-video-connector", },
+ {},
+};
+
static struct platform_driver tvc_connector_driver = {
.probe = tvc_probe,
.remove = __exit_p(tvc_remove),
.driver = {
.name = "connector-analog-tv",
.owner = THIS_MODULE,
+ .of_match_table = tvc_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/connector-dvi.c b/drivers/video/omap2/displays-new/connector-dvi.c
index b6c50904038e..74de2bc50c4f 100644
--- a/drivers/video/omap2/displays-new/connector-dvi.c
+++ b/drivers/video/omap2/displays-new/connector-dvi.c
@@ -23,7 +23,7 @@ static const struct omap_video_timings dvic_default_timings = {
.x_res = 640,
.y_res = 480,
- .pixel_clock = 23500,
+ .pixelclock = 23500000,
.hfp = 48,
.hsw = 32,
@@ -277,6 +277,37 @@ static int dvic_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int dvic_probe_of(struct platform_device *pdev)
+{
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+ struct omap_dss_device *in;
+ struct device_node *adapter_node;
+ struct i2c_adapter *adapter;
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0);
+ if (adapter_node) {
+ adapter = of_find_i2c_adapter_by_node(adapter_node);
+ if (adapter == NULL) {
+ dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n");
+ omap_dss_put_device(ddata->in);
+ return -EPROBE_DEFER;
+ }
+
+ ddata->i2c_adapter = adapter;
+ }
+
+ return 0;
+}
+
static int dvic_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
@@ -293,6 +324,10 @@ static int dvic_probe(struct platform_device *pdev)
r = dvic_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = dvic_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
@@ -342,12 +377,20 @@ static int __exit dvic_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id dvic_of_match[] = {
+ { .compatible = "omapdss,dvi-connector", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dvic_of_match);
+
static struct platform_driver dvi_connector_driver = {
.probe = dvic_probe,
.remove = __exit_p(dvic_remove),
.driver = {
.name = "connector-dvi",
.owner = THIS_MODULE,
+ .of_match_table = dvic_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/connector-hdmi.c b/drivers/video/omap2/displays-new/connector-hdmi.c
index 9abe2c039ae9..29ed21b9dce5 100644
--- a/drivers/video/omap2/displays-new/connector-hdmi.c
+++ b/drivers/video/omap2/displays-new/connector-hdmi.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <drm/drm_edid.h>
@@ -21,7 +22,7 @@
static const struct omap_video_timings hdmic_default_timings = {
.x_res = 640,
.y_res = 480,
- .pixel_clock = 25175,
+ .pixelclock = 25175000,
.hsw = 96,
.hfp = 16,
.hbp = 48,
@@ -301,6 +302,23 @@ static int hdmic_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int hdmic_probe_of(struct platform_device *pdev)
+{
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+ struct omap_dss_device *in;
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ return 0;
+}
+
static int hdmic_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
@@ -318,6 +336,10 @@ static int hdmic_probe(struct platform_device *pdev)
r = hdmic_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = hdmic_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
@@ -359,12 +381,20 @@ static int __exit hdmic_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id hdmic_of_match[] = {
+ { .compatible = "omapdss,hdmi-connector", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, hdmic_of_match);
+
static struct platform_driver hdmi_connector_driver = {
.probe = hdmic_probe,
.remove = __exit_p(hdmic_remove),
.driver = {
.name = "connector-hdmi",
.owner = THIS_MODULE,
+ .of_match_table = hdmic_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/encoder-tfp410.c b/drivers/video/omap2/displays-new/encoder-tfp410.c
index 4a291e756be9..b4e9a42a79e6 100644
--- a/drivers/video/omap2/displays-new/encoder-tfp410.c
+++ b/drivers/video/omap2/displays-new/encoder-tfp410.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
@@ -82,7 +83,8 @@ static int tfp410_enable(struct omap_dss_device *dssdev)
return 0;
in->ops.dpi->set_timings(in, &ddata->timings);
- in->ops.dpi->set_data_lines(in, ddata->data_lines);
+ if (ddata->data_lines)
+ in->ops.dpi->set_data_lines(in, ddata->data_lines);
r = in->ops.dpi->enable(in);
if (r)
@@ -179,6 +181,33 @@ static int tfp410_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int tfp410_probe_of(struct platform_device *pdev)
+{
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+ struct omap_dss_device *in;
+ int gpio;
+
+ gpio = of_get_named_gpio(node, "powerdown-gpios", 0);
+
+ if (gpio_is_valid(gpio) || gpio == -ENOENT) {
+ ddata->pd_gpio = gpio;
+ } else {
+ dev_err(&pdev->dev, "failed to parse PD gpio\n");
+ return gpio;
+ }
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ return 0;
+}
+
static int tfp410_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
@@ -195,6 +224,10 @@ static int tfp410_probe(struct platform_device *pdev)
r = tfp410_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = tfp410_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
@@ -251,12 +284,20 @@ static int __exit tfp410_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id tfp410_of_match[] = {
+ { .compatible = "omapdss,ti,tfp410", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, tfp410_of_match);
+
static struct platform_driver tfp410_driver = {
.probe = tfp410_probe,
.remove = __exit_p(tfp410_remove),
.driver = {
.name = "tfp410",
.owner = THIS_MODULE,
+ .of_match_table = tfp410_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/encoder-tpd12s015.c b/drivers/video/omap2/displays-new/encoder-tpd12s015.c
index d5c936cb217f..7e33686171e3 100644
--- a/drivers/video/omap2/displays-new/encoder-tpd12s015.c
+++ b/drivers/video/omap2/displays-new/encoder-tpd12s015.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
@@ -289,6 +290,49 @@ static int tpd_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int tpd_probe_of(struct platform_device *pdev)
+{
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+ struct omap_dss_device *in;
+ int gpio;
+
+ /* CT CP HPD GPIO */
+ gpio = of_get_gpio(node, 0);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(&pdev->dev, "failed to parse CT CP HPD gpio\n");
+ return gpio;
+ }
+ ddata->ct_cp_hpd_gpio = gpio;
+
+ /* LS OE GPIO */
+ gpio = of_get_gpio(node, 1);
+ if (gpio_is_valid(gpio) || gpio == -ENOENT) {
+ ddata->ls_oe_gpio = gpio;
+ } else {
+ dev_err(&pdev->dev, "failed to parse LS OE gpio\n");
+ return gpio;
+ }
+
+ /* HPD GPIO */
+ gpio = of_get_gpio(node, 2);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(&pdev->dev, "failed to parse HPD gpio\n");
+ return gpio;
+ }
+ ddata->hpd_gpio = gpio;
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ return 0;
+}
+
static int tpd_probe(struct platform_device *pdev)
{
struct omap_dss_device *in, *dssdev;
@@ -307,6 +351,10 @@ static int tpd_probe(struct platform_device *pdev)
r = tpd_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = tpd_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
@@ -379,12 +427,20 @@ static int __exit tpd_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id tpd_of_match[] = {
+ { .compatible = "omapdss,ti,tpd12s015", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, tpd_of_match);
+
static struct platform_driver tpd_driver = {
.probe = tpd_probe,
.remove = __exit_p(tpd_remove),
.driver = {
.name = "tpd12s015",
.owner = THIS_MODULE,
+ .of_match_table = tpd_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/panel-dsi-cm.c b/drivers/video/omap2/displays-new/panel-dsi-cm.c
index b7baafe83aa3..d6f14e8717e8 100644
--- a/drivers/video/omap2/displays-new/panel-dsi-cm.c
+++ b/drivers/video/omap2/displays-new/panel-dsi-cm.c
@@ -22,6 +22,8 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
@@ -595,10 +597,13 @@ static int dsicm_power_on(struct panel_drv_data *ddata)
.lp_clk_max = 10000000,
};
- r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
- if (r) {
- dev_err(&ddata->pdev->dev, "failed to configure DSI pins\n");
- goto err0;
+ if (ddata->pin_config.num_pins > 0) {
+ r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
+ if (r) {
+ dev_err(&ddata->pdev->dev,
+ "failed to configure DSI pins\n");
+ goto err0;
+ }
}
r = in->ops.dsi->set_config(in, &dsi_config);
@@ -1156,6 +1161,41 @@ static int dsicm_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int dsicm_probe_of(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct omap_dss_device *in;
+ int gpio;
+
+ gpio = of_get_named_gpio(node, "reset-gpios", 0);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(&pdev->dev, "failed to parse reset gpio\n");
+ return gpio;
+ }
+ ddata->reset_gpio = gpio;
+
+ gpio = of_get_named_gpio(node, "te-gpios", 0);
+ if (gpio_is_valid(gpio) || gpio == -ENOENT) {
+ ddata->ext_te_gpio = gpio;
+ } else {
+ dev_err(&pdev->dev, "failed to parse TE gpio\n");
+ return gpio;
+ }
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ /* TODO: ulps, backlight */
+
+ return 0;
+}
+
static int dsicm_probe(struct platform_device *pdev)
{
struct backlight_properties props;
@@ -1178,13 +1218,17 @@ static int dsicm_probe(struct platform_device *pdev)
r = dsicm_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = dsicm_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
ddata->timings.x_res = 864;
ddata->timings.y_res = 480;
- ddata->timings.pixel_clock = DIV_ROUND_UP(864 * 480 * 60, 1000);
+ ddata->timings.pixelclock = 864 * 480 * 60;
dssdev = &ddata->dssdev;
dssdev->dev = dev;
@@ -1320,12 +1364,20 @@ static int __exit dsicm_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id dsicm_of_match[] = {
+ { .compatible = "omapdss,panel-dsi-cm", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dsicm_of_match);
+
static struct platform_driver dsicm_driver = {
.probe = dsicm_probe,
.remove = __exit_p(dsicm_remove),
.driver = {
.name = "panel-dsi-cm",
.owner = THIS_MODULE,
+ .of_match_table = dsicm_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c
index 6e8977b18950..2e6b513222d9 100644
--- a/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c
+++ b/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c
@@ -23,7 +23,7 @@ static struct omap_video_timings lb035q02_timings = {
.x_res = 320,
.y_res = 240,
- .pixel_clock = 6500,
+ .pixelclock = 6500000,
.hsw = 2,
.hfp = 20,
diff --git a/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c b/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c
index bb217da65c5f..996fa004b48c 100644
--- a/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c
+++ b/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c
@@ -40,7 +40,7 @@ struct panel_drv_data {
* NEC PIX Clock Ratings
* MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz
*/
-#define LCD_PIXEL_CLOCK 23800
+#define LCD_PIXEL_CLOCK 23800000
static const struct {
unsigned char addr;
@@ -69,7 +69,7 @@ static const struct {
static const struct omap_video_timings nec_8048_panel_timings = {
.x_res = LCD_XRES,
.y_res = LCD_YRES,
- .pixel_clock = LCD_PIXEL_CLOCK,
+ .pixelclock = LCD_PIXEL_CLOCK,
.hfp = 6,
.hsw = 1,
.hbp = 4,
diff --git a/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c
index 72a4fb5aa6b1..b2f710be565d 100644
--- a/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c
+++ b/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c
@@ -37,7 +37,7 @@ static const struct omap_video_timings sharp_ls_timings = {
.x_res = 480,
.y_res = 640,
- .pixel_clock = 19200,
+ .pixelclock = 19200000,
.hsw = 2,
.hfp = 1,
diff --git a/drivers/video/omap2/displays-new/panel-sony-acx565akm.c b/drivers/video/omap2/displays-new/panel-sony-acx565akm.c
index 8e97d06921ff..c7ba4d8b928a 100644
--- a/drivers/video/omap2/displays-new/panel-sony-acx565akm.c
+++ b/drivers/video/omap2/displays-new/panel-sony-acx565akm.c
@@ -30,6 +30,8 @@
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
@@ -93,7 +95,7 @@ struct panel_drv_data {
static const struct omap_video_timings acx565akm_panel_timings = {
.x_res = 800,
.y_res = 480,
- .pixel_clock = 24000,
+ .pixelclock = 24000000,
.hfp = 28,
.hsw = 4,
.hbp = 24,
@@ -547,7 +549,9 @@ static int acx565akm_panel_power_on(struct omap_dss_device *dssdev)
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
in->ops.sdi->set_timings(in, &ddata->videomode);
- in->ops.sdi->set_datapairs(in, ddata->datapairs);
+
+ if (ddata->datapairs > 0)
+ in->ops.sdi->set_datapairs(in, ddata->datapairs);
r = in->ops.sdi->enable(in);
if (r) {
@@ -726,6 +730,22 @@ static int acx565akm_probe_pdata(struct spi_device *spi)
return 0;
}
+static int acx565akm_probe_of(struct spi_device *spi)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+ struct device_node *np = spi->dev.of_node;
+
+ ddata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
+
+ ddata->in = omapdss_of_find_source_for_first_ep(np);
+ if (IS_ERR(ddata->in)) {
+ dev_err(&spi->dev, "failed to find video source\n");
+ return PTR_ERR(ddata->in);
+ }
+
+ return 0;
+}
+
static int acx565akm_probe(struct spi_device *spi)
{
struct panel_drv_data *ddata;
@@ -753,7 +773,12 @@ static int acx565akm_probe(struct spi_device *spi)
r = acx565akm_probe_pdata(spi);
if (r)
return r;
+ } else if (spi->dev.of_node) {
+ r = acx565akm_probe_of(spi);
+ if (r)
+ return r;
} else {
+ dev_err(&spi->dev, "platform data missing!\n");
return -ENODEV;
}
@@ -864,10 +889,16 @@ static int acx565akm_remove(struct spi_device *spi)
return 0;
}
+static const struct of_device_id acx565akm_of_match[] = {
+ { .compatible = "omapdss,sony,acx565akm", },
+ {},
+};
+
static struct spi_driver acx565akm_driver = {
.driver = {
.name = "acx565akm",
.owner = THIS_MODULE,
+ .of_match_table = acx565akm_of_match,
},
.probe = acx565akm_probe,
.remove = acx565akm_remove,
diff --git a/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c b/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c
index 9a08908fe998..fae6adc005a7 100644
--- a/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c
+++ b/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c
@@ -45,7 +45,7 @@ struct panel_drv_data {
static struct omap_video_timings td028ttec1_panel_timings = {
.x_res = 480,
.y_res = 640,
- .pixel_clock = 22153,
+ .pixelclock = 22153000,
.hfp = 24,
.hsw = 8,
.hbp = 8,
diff --git a/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c
index eadc6529fa3d..875b40263b33 100644
--- a/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c
+++ b/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c
@@ -76,7 +76,7 @@ static const struct omap_video_timings tpo_td043_timings = {
.x_res = 800,
.y_res = 480,
- .pixel_clock = 36000,
+ .pixelclock = 36000000,
.hsw = 1,
.hfp = 68,
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index d3aa91bdd6a8..8aec8bda27cc 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -1,7 +1,7 @@
obj-$(CONFIG_OMAP2_DSS) += omapdss.o
# Core DSS files
omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
- output.o
+ output.o dss-of.o
# DSS compat layer files
omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \
dispc-compat.o display-sysfs.o
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 77d6221618f4..2bbdb7ff7daf 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -100,8 +100,6 @@ static struct {
struct platform_device *pdev;
void __iomem *base;
- int ctx_loss_cnt;
-
int irq;
unsigned long core_clk_rate;
@@ -357,29 +355,20 @@ static void dispc_save_context(void)
if (dss_has_feature(FEAT_CORE_CLK_DIV))
SR(DIVISOR);
- dispc.ctx_loss_cnt = dss_get_ctx_loss_count();
dispc.ctx_valid = true;
- DSSDBG("context saved, ctx_loss_count %d\n", dispc.ctx_loss_cnt);
+ DSSDBG("context saved\n");
}
static void dispc_restore_context(void)
{
- int i, j, ctx;
+ int i, j;
DSSDBG("dispc_restore_context\n");
if (!dispc.ctx_valid)
return;
- ctx = dss_get_ctx_loss_count();
-
- if (ctx >= 0 && ctx == dispc.ctx_loss_cnt)
- return;
-
- DSSDBG("ctx_loss_count: saved %d, current %d\n",
- dispc.ctx_loss_cnt, ctx);
-
/*RR(IRQENABLE);*/
/*RR(CONTROL);*/
RR(CONFIG);
@@ -2884,7 +2873,7 @@ bool dispc_mgr_timings_ok(enum omap_channel channel,
timings_ok = _dispc_mgr_size_ok(timings->x_res, timings->y_res);
- timings_ok &= _dispc_mgr_pclk_ok(channel, timings->pixel_clock * 1000);
+ timings_ok &= _dispc_mgr_pclk_ok(channel, timings->pixelclock);
if (dss_mgr_is_lcd(channel)) {
timings_ok &= _dispc_lcd_timings_ok(timings->hsw, timings->hfp,
@@ -2979,10 +2968,10 @@ void dispc_mgr_set_timings(enum omap_channel channel,
xtot = t.x_res + t.hfp + t.hsw + t.hbp;
ytot = t.y_res + t.vfp + t.vsw + t.vbp;
- ht = (timings->pixel_clock * 1000) / xtot;
- vt = (timings->pixel_clock * 1000) / xtot / ytot;
+ ht = timings->pixelclock / xtot;
+ vt = timings->pixelclock / xtot / ytot;
- DSSDBG("pck %u\n", timings->pixel_clock);
+ DSSDBG("pck %u\n", timings->pixelclock);
DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n",
t.hsw, t.hfp, t.hbp, t.vsw, t.vfp, t.vbp);
DSSDBG("vsync_level %d hsync_level %d data_pclk_edge %d de_level %d sync_pclk_edge %d\n",
@@ -3768,6 +3757,15 @@ static int dispc_runtime_suspend(struct device *dev)
static int dispc_runtime_resume(struct device *dev)
{
+ /*
+ * The reset value for load mode is 0 (OMAP_DSS_LOAD_CLUT_AND_FRAME)
+ * but we always initialize it to 2 (OMAP_DSS_LOAD_FRAME_ONLY) in
+ * _omap_dispc_initial_config(). We can thus use it to detect if
+ * we have lost register context.
+ */
+ if (REG_GET(DISPC_CONFIG, 2, 1) == OMAP_DSS_LOAD_FRAME_ONLY)
+ return 0;
+
_omap_dispc_initial_config();
dispc_restore_context();
@@ -3780,12 +3778,20 @@ static const struct dev_pm_ops dispc_pm_ops = {
.runtime_resume = dispc_runtime_resume,
};
+static const struct of_device_id dispc_of_match[] = {
+ { .compatible = "ti,omap2-dispc", },
+ { .compatible = "ti,omap3-dispc", },
+ { .compatible = "ti,omap4-dispc", },
+ {},
+};
+
static struct platform_driver omap_dispchw_driver = {
.remove = __exit_p(omap_dispchw_remove),
.driver = {
.name = "omapdss_dispc",
.owner = THIS_MODULE,
.pm = &dispc_pm_ops,
+ .of_match_table = dispc_of_match,
},
};
diff --git a/drivers/video/omap2/dss/display-sysfs.c b/drivers/video/omap2/dss/display-sysfs.c
index f7b5f9561041..5a2095a98ed8 100644
--- a/drivers/video/omap2/dss/display-sysfs.c
+++ b/drivers/video/omap2/dss/display-sysfs.c
@@ -132,7 +132,7 @@ static ssize_t display_timings_show(struct device *dev,
dssdev->driver->get_timings(dssdev, &t);
return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n",
- t.pixel_clock,
+ t.pixelclock,
t.x_res, t.hfp, t.hbp, t.hsw,
t.y_res, t.vfp, t.vbp, t.vsw);
}
@@ -158,7 +158,7 @@ static ssize_t display_timings_store(struct device *dev,
}
#endif
if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu",
- &t.pixel_clock,
+ &t.pixelclock,
&t.x_res, &t.hfp, &t.hbp, &t.hsw,
&t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9)
return -EINVAL;
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
index 669a81fdf58e..2412a0dd0c13 100644
--- a/drivers/video/omap2/dss/display.c
+++ b/drivers/video/omap2/dss/display.c
@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <video/omapdss.h>
#include "dss.h"
@@ -133,9 +134,32 @@ static int disp_num_counter;
int omapdss_register_display(struct omap_dss_device *dssdev)
{
struct omap_dss_driver *drv = dssdev->driver;
+ int id;
- snprintf(dssdev->alias, sizeof(dssdev->alias),
- "display%d", disp_num_counter++);
+ /*
+ * Note: this presumes all the displays are either using DT or non-DT,
+ * which normally should be the case. This also presumes that all
+ * displays either have an DT alias, or none has.
+ */
+
+ if (dssdev->dev->of_node) {
+ id = of_alias_get_id(dssdev->dev->of_node, "display");
+
+ if (id < 0)
+ id = disp_num_counter++;
+ } else {
+ id = disp_num_counter++;
+ }
+
+ snprintf(dssdev->alias, sizeof(dssdev->alias), "display%d", id);
+
+ /* Use 'label' property for name, if it exists */
+ if (dssdev->dev->of_node)
+ of_property_read_string(dssdev->dev->of_node, "label",
+ &dssdev->name);
+
+ if (dssdev->name == NULL)
+ dssdev->name = dssdev->alias;
if (drv && drv->get_resolution == NULL)
drv->get_resolution = omapdss_default_get_resolution;
@@ -248,7 +272,7 @@ void videomode_to_omap_video_timings(const struct videomode *vm,
{
memset(ovt, 0, sizeof(*ovt));
- ovt->pixel_clock = vm->pixelclock / 1000;
+ ovt->pixelclock = vm->pixelclock;
ovt->x_res = vm->hactive;
ovt->hbp = vm->hback_porch;
ovt->hfp = vm->hfront_porch;
@@ -280,7 +304,7 @@ void omap_video_timings_to_videomode(const struct omap_video_timings *ovt,
{
memset(vm, 0, sizeof(*vm));
- vm->pixelclock = ovt->pixel_clock * 1000;
+ vm->pixelclock = ovt->pixelclock;
vm->hactive = ovt->x_res;
vm->hback_porch = ovt->hbp;
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
index 23ef21ffc2c4..157921db447a 100644
--- a/drivers/video/omap2/dss/dpi.c
+++ b/drivers/video/omap2/dss/dpi.c
@@ -30,6 +30,7 @@
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/string.h>
+#include <linux/of.h>
#include <video/omapdss.h>
@@ -49,6 +50,8 @@ static struct {
int data_lines;
struct omap_dss_device output;
+
+ bool port_initialized;
} dpi;
static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
@@ -307,22 +310,21 @@ static int dpi_set_mode(struct omap_overlay_manager *mgr)
int r = 0;
if (dpi.dsidev)
- r = dpi_set_dsi_clk(mgr->id, t->pixel_clock * 1000, &fck,
+ r = dpi_set_dsi_clk(mgr->id, t->pixelclock, &fck,
&lck_div, &pck_div);
else
- r = dpi_set_dispc_clk(t->pixel_clock * 1000, &fck,
+ r = dpi_set_dispc_clk(t->pixelclock, &fck,
&lck_div, &pck_div);
if (r)
return r;
- pck = fck / lck_div / pck_div / 1000;
+ pck = fck / lck_div / pck_div;
- if (pck != t->pixel_clock) {
- DSSWARN("Could not find exact pixel clock. "
- "Requested %d kHz, got %lu kHz\n",
- t->pixel_clock, pck);
+ if (pck != t->pixelclock) {
+ DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n",
+ t->pixelclock, pck);
- t->pixel_clock = pck;
+ t->pixelclock = pck;
}
dss_mgr_set_timings(mgr, t);
@@ -480,17 +482,17 @@ static int dpi_check_timings(struct omap_dss_device *dssdev,
if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
return -EINVAL;
- if (timings->pixel_clock == 0)
+ if (timings->pixelclock == 0)
return -EINVAL;
if (dpi.dsidev) {
- ok = dpi_dsi_clk_calc(timings->pixel_clock * 1000, &ctx);
+ ok = dpi_dsi_clk_calc(timings->pixelclock, &ctx);
if (!ok)
return -EINVAL;
fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
} else {
- ok = dpi_dss_clk_calc(timings->pixel_clock * 1000, &ctx);
+ ok = dpi_dss_clk_calc(timings->pixelclock, &ctx);
if (!ok)
return -EINVAL;
@@ -500,9 +502,9 @@ static int dpi_check_timings(struct omap_dss_device *dssdev,
lck_div = ctx.dispc_cinfo.lck_div;
pck_div = ctx.dispc_cinfo.pck_div;
- pck = fck / lck_div / pck_div / 1000;
+ pck = fck / lck_div / pck_div;
- timings->pixel_clock = pck;
+ timings->pixelclock = pck;
return 0;
}
@@ -726,3 +728,47 @@ void __exit dpi_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_dpi_driver);
}
+
+int __init dpi_init_port(struct platform_device *pdev, struct device_node *port)
+{
+ struct device_node *ep;
+ u32 datalines;
+ int r;
+
+ ep = omapdss_of_get_next_endpoint(port, NULL);
+ if (!ep)
+ return 0;
+
+ r = of_property_read_u32(ep, "data-lines", &datalines);
+ if (r) {
+ DSSERR("failed to parse datalines\n");
+ goto err_datalines;
+ }
+
+ dpi.data_lines = datalines;
+
+ of_node_put(ep);
+
+ dpi.pdev = pdev;
+
+ mutex_init(&dpi.lock);
+
+ dpi_init_output(pdev);
+
+ dpi.port_initialized = true;
+
+ return 0;
+
+err_datalines:
+ of_node_put(ep);
+
+ return r;
+}
+
+void __exit dpi_uninit_port(void)
+{
+ if (!dpi.port_initialized)
+ return;
+
+ dpi_uninit_output(dpi.pdev);
+}
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index a820c37e323e..121d1049d0bc 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -38,6 +38,8 @@
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <video/omapdss.h>
#include <video/mipi_display.h>
@@ -386,6 +388,13 @@ struct dsi_packet_sent_handler_data {
struct completion *completion;
};
+struct dsi_module_id_data {
+ u32 address;
+ int id;
+};
+
+static const struct of_device_id dsi_of_match[];
+
#ifdef DSI_PERF_MEASURE
static bool dsi_perf;
module_param(dsi_perf, bool, 0644);
@@ -1151,15 +1160,11 @@ static int dsi_regulator_init(struct platform_device *dsidev)
if (dsi->vdds_dsi_reg != NULL)
return 0;
- vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "vdds_dsi");
-
- /* DT HACK: try VCXIO to make omapdss work for o4 sdp/panda */
- if (IS_ERR(vdds_dsi))
- vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "VCXIO");
+ vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "vdd");
if (IS_ERR(vdds_dsi)) {
if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER)
- DSSERR("can't get VDDS_DSI regulator\n");
+ DSSERR("can't get DSI VDD regulator\n");
return PTR_ERR(vdds_dsi);
}
@@ -4616,7 +4621,7 @@ static void print_dsi_vm(const char *str,
static void print_dispc_vm(const char *str, const struct omap_video_timings *t)
{
- unsigned long pck = t->pixel_clock * 1000;
+ unsigned long pck = t->pixelclock;
int hact, bl, tot;
hact = t->x_res;
@@ -4656,7 +4661,7 @@ static void print_dsi_dispc_vm(const char *str,
dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(t->hact * t->bitspp, 8) + 6, t->ndl);
dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfp;
- vm.pixel_clock = pck / 1000;
+ vm.pixelclock = pck;
vm.hsw = div64_u64((u64)(t->hsa + t->hse) * pck, byteclk);
vm.hbp = div64_u64((u64)t->hbp * pck, byteclk);
vm.hfp = div64_u64((u64)t->hfp * pck, byteclk);
@@ -4678,7 +4683,7 @@ static bool dsi_cm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
ctx->dispc_cinfo.pck = pck;
*t = *ctx->config->timings;
- t->pixel_clock = pck / 1000;
+ t->pixelclock = pck;
t->x_res = ctx->config->timings->x_res;
t->y_res = ctx->config->timings->y_res;
t->hsw = t->hfp = t->hbp = t->vsw = 1;
@@ -4732,7 +4737,7 @@ static bool dsi_cm_calc(struct dsi_data *dsi,
* especially as we go to LP between each pixel packet due to HW
* "feature". So let's just estimate very roughly and multiply by 1.5.
*/
- pck = cfg->timings->pixel_clock * 1000;
+ pck = cfg->timings->pixelclock;
pck = pck * 3 / 2;
txbyteclk = pck * bitspp / 8 / ndl;
@@ -4909,7 +4914,7 @@ static bool dsi_vm_calc_blanking(struct dsi_clk_calc_ctx *ctx)
dispc_vm = &ctx->dispc_vm;
*dispc_vm = *req_vm;
- dispc_vm->pixel_clock = dispc_pck / 1000;
+ dispc_vm->pixelclock = dispc_pck;
if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
hsa = div64_u64((u64)req_vm->hsw * dispc_pck,
@@ -5031,9 +5036,9 @@ static bool dsi_vm_calc(struct dsi_data *dsi,
ctx->dsi_cinfo.clkin = clkin;
/* these limits should come from the panel driver */
- ctx->req_pck_min = t->pixel_clock * 1000 - 1000;
- ctx->req_pck_nom = t->pixel_clock * 1000;
- ctx->req_pck_max = t->pixel_clock * 1000 + 1000;
+ ctx->req_pck_min = t->pixelclock - 1000;
+ ctx->req_pck_nom = t->pixelclock;
+ ctx->req_pck_max = t->pixelclock + 1000;
byteclk_min = div64_u64((u64)ctx->req_pck_min * bitspp, ndl * 8);
pll_min = max(cfg->hs_clk_min * 4, byteclk_min * 4 * 4);
@@ -5370,12 +5375,69 @@ static void dsi_uninit_output(struct platform_device *dsidev)
omapdss_unregister_output(out);
}
+static int dsi_probe_of(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
+ struct property *prop;
+ u32 lane_arr[10];
+ int len, num_pins;
+ int r, i;
+ struct device_node *ep;
+ struct omap_dsi_pin_config pin_cfg;
+
+ ep = omapdss_of_get_first_endpoint(node);
+ if (!ep)
+ return 0;
+
+ prop = of_find_property(ep, "lanes", &len);
+ if (prop == NULL) {
+ dev_err(&pdev->dev, "failed to find lane data\n");
+ r = -EINVAL;
+ goto err;
+ }
+
+ num_pins = len / sizeof(u32);
+
+ if (num_pins < 4 || num_pins % 2 != 0 ||
+ num_pins > dsi->num_lanes_supported * 2) {
+ dev_err(&pdev->dev, "bad number of lanes\n");
+ r = -EINVAL;
+ goto err;
+ }
+
+ r = of_property_read_u32_array(ep, "lanes", lane_arr, num_pins);
+ if (r) {
+ dev_err(&pdev->dev, "failed to read lane data\n");
+ goto err;
+ }
+
+ pin_cfg.num_pins = num_pins;
+ for (i = 0; i < num_pins; ++i)
+ pin_cfg.pins[i] = (int)lane_arr[i];
+
+ r = dsi_configure_pins(&dsi->output, &pin_cfg);
+ if (r) {
+ dev_err(&pdev->dev, "failed to configure pins");
+ goto err;
+ }
+
+ of_node_put(ep);
+
+ return 0;
+
+err:
+ of_node_put(ep);
+ return r;
+}
+
/* DSI1 HW IP initialisation */
static int omap_dsihw_probe(struct platform_device *dsidev)
{
u32 rev;
int r, i;
struct dsi_data *dsi;
+ struct resource *dsi_mem;
struct resource *res;
struct resource temp_res;
@@ -5383,7 +5445,6 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
if (!dsi)
return -ENOMEM;
- dsi->module_id = dsidev->id;
dsi->pdev = dsidev;
dev_set_drvdata(&dsidev->dev, dsi);
@@ -5421,6 +5482,8 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
res = &temp_res;
}
+ dsi_mem = res;
+
dsi->proto_base = devm_ioremap(&dsidev->dev, res->start,
resource_size(res));
if (!dsi->proto_base) {
@@ -5481,6 +5544,31 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
return r;
}
+ if (dsidev->dev.of_node) {
+ const struct of_device_id *match;
+ const struct dsi_module_id_data *d;
+
+ match = of_match_node(dsi_of_match, dsidev->dev.of_node);
+ if (!match) {
+ DSSERR("unsupported DSI module\n");
+ return -ENODEV;
+ }
+
+ d = match->data;
+
+ while (d->address != 0 && d->address != dsi_mem->start)
+ d++;
+
+ if (d->address == 0) {
+ DSSERR("unsupported DSI module\n");
+ return -ENODEV;
+ }
+
+ dsi->module_id = d->id;
+ } else {
+ dsi->module_id = dsidev->id;
+ }
+
/* DSI VCs initialization */
for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
dsi->vc[i].source = DSI_VC_SOURCE_L4;
@@ -5516,6 +5604,19 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
dsi_init_output(dsidev);
+ if (dsidev->dev.of_node) {
+ r = dsi_probe_of(dsidev);
+ if (r) {
+ DSSERR("Invalid DSI DT data\n");
+ goto err_probe_of;
+ }
+
+ r = of_platform_populate(dsidev->dev.of_node, NULL, NULL,
+ &dsidev->dev);
+ if (r)
+ DSSERR("Failed to populate DSI child devices: %d\n", r);
+ }
+
dsi_runtime_put(dsidev);
if (dsi->module_id == 0)
@@ -5529,17 +5630,31 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
else if (dsi->module_id == 1)
dss_debugfs_create_file("dsi2_irqs", dsi2_dump_irqs);
#endif
+
return 0;
+err_probe_of:
+ dsi_uninit_output(dsidev);
+ dsi_runtime_put(dsidev);
+
err_runtime_get:
pm_runtime_disable(&dsidev->dev);
return r;
}
+static int dsi_unregister_child(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ platform_device_unregister(pdev);
+ return 0;
+}
+
static int __exit omap_dsihw_remove(struct platform_device *dsidev)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ device_for_each_child(&dsidev->dev, NULL, dsi_unregister_child);
+
WARN_ON(dsi->scp_clk_refcount > 0);
dsi_uninit_output(dsidev);
@@ -5577,6 +5692,23 @@ static const struct dev_pm_ops dsi_pm_ops = {
.runtime_resume = dsi_runtime_resume,
};
+static const struct dsi_module_id_data dsi_of_data_omap3[] = {
+ { .address = 0x4804fc00, .id = 0, },
+ { },
+};
+
+static const struct dsi_module_id_data dsi_of_data_omap4[] = {
+ { .address = 0x58004000, .id = 0, },
+ { .address = 0x58005000, .id = 1, },
+ { },
+};
+
+static const struct of_device_id dsi_of_match[] = {
+ { .compatible = "ti,omap3-dsi", .data = dsi_of_data_omap3, },
+ { .compatible = "ti,omap4-dsi", .data = dsi_of_data_omap4, },
+ {},
+};
+
static struct platform_driver omap_dsihw_driver = {
.probe = omap_dsihw_probe,
.remove = __exit_p(omap_dsihw_remove),
@@ -5584,6 +5716,7 @@ static struct platform_driver omap_dsihw_driver = {
.name = "omapdss_dsi",
.owner = THIS_MODULE,
.pm = &dsi_pm_ops,
+ .of_match_table = dsi_of_match,
},
};
diff --git a/drivers/video/omap2/dss/dss-of.c b/drivers/video/omap2/dss/dss-of.c
new file mode 100644
index 000000000000..a4b20aaf6142
--- /dev/null
+++ b/drivers/video/omap2/dss/dss-of.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@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 in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/seq_file.h>
+
+#include <video/omapdss.h>
+
+struct device_node *
+omapdss_of_get_next_port(const struct device_node *parent,
+ struct device_node *prev)
+{
+ struct device_node *port = NULL;
+
+ if (!parent)
+ return NULL;
+
+ if (!prev) {
+ struct device_node *ports;
+ /*
+ * It's the first call, we have to find a port subnode
+ * within this node or within an optional 'ports' node.
+ */
+ ports = of_get_child_by_name(parent, "ports");
+ if (ports)
+ parent = ports;
+
+ port = of_get_child_by_name(parent, "port");
+
+ /* release the 'ports' node */
+ of_node_put(ports);
+ } else {
+ struct device_node *ports;
+
+ ports = of_get_parent(prev);
+ if (!ports)
+ return NULL;
+
+ do {
+ port = of_get_next_child(ports, prev);
+ if (!port) {
+ of_node_put(ports);
+ return NULL;
+ }
+ prev = port;
+ } while (of_node_cmp(port->name, "port") != 0);
+ }
+
+ return port;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_get_next_port);
+
+struct device_node *
+omapdss_of_get_next_endpoint(const struct device_node *parent,
+ struct device_node *prev)
+{
+ struct device_node *ep = NULL;
+
+ if (!parent)
+ return NULL;
+
+ do {
+ ep = of_get_next_child(parent, prev);
+ if (!ep)
+ return NULL;
+ prev = ep;
+ } while (of_node_cmp(ep->name, "endpoint") != 0);
+
+ return ep;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_get_next_endpoint);
+
+static struct device_node *
+omapdss_of_get_remote_device_node(const struct device_node *node)
+{
+ struct device_node *np;
+ int i;
+
+ np = of_parse_phandle(node, "remote-endpoint", 0);
+
+ if (!np)
+ return NULL;
+
+ np = of_get_next_parent(np);
+
+ for (i = 0; i < 3 && np; ++i) {
+ struct property *prop;
+
+ prop = of_find_property(np, "compatible", NULL);
+
+ if (prop)
+ return np;
+
+ np = of_get_next_parent(np);
+ }
+
+ return NULL;
+}
+
+struct device_node *
+omapdss_of_get_first_endpoint(const struct device_node *parent)
+{
+ struct device_node *port, *ep;
+
+ port = omapdss_of_get_next_port(parent, NULL);
+
+ if (!port)
+ return NULL;
+
+ ep = omapdss_of_get_next_endpoint(port, NULL);
+
+ of_node_put(port);
+
+ return ep;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_get_first_endpoint);
+
+struct omap_dss_device *
+omapdss_of_find_source_for_first_ep(struct device_node *node)
+{
+ struct device_node *ep;
+ struct device_node *src_node;
+ struct omap_dss_device *src;
+
+ ep = omapdss_of_get_first_endpoint(node);
+ if (!ep)
+ return ERR_PTR(-EINVAL);
+
+ src_node = omapdss_of_get_remote_device_node(ep);
+
+ of_node_put(ep);
+
+ if (!src_node)
+ return ERR_PTR(-EINVAL);
+
+ src = omap_dss_find_output_by_node(src_node);
+
+ of_node_put(src_node);
+
+ if (!src)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return src;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_find_source_for_first_ep);
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
index 9a145da35ad3..825c019ddee7 100644
--- a/drivers/video/omap2/dss/dss.c
+++ b/drivers/video/omap2/dss/dss.c
@@ -23,6 +23,7 @@
#define DSS_SUBSYS_NAME "DSS"
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/io.h>
#include <linux/export.h>
#include <linux/err.h>
@@ -33,6 +34,7 @@
#include <linux/pm_runtime.h>
#include <linux/gfp.h>
#include <linux/sizes.h>
+#include <linux/of.h>
#include <video/omapdss.h>
@@ -154,22 +156,6 @@ static void dss_restore_context(void)
#undef SR
#undef RR
-int dss_get_ctx_loss_count(void)
-{
- struct platform_device *core_pdev = dss_get_core_pdev();
- struct omap_dss_board_info *board_data = core_pdev->dev.platform_data;
- int cnt;
-
- if (!board_data->get_context_loss_count)
- return -ENOENT;
-
- cnt = board_data->get_context_loss_count(&dss.pdev->dev);
-
- WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt);
-
- return cnt;
-}
-
void dss_sdi_init(int datapairs)
{
u32 l;
@@ -788,6 +774,56 @@ static int __init dss_init_features(struct platform_device *pdev)
return 0;
}
+static int __init dss_init_ports(struct platform_device *pdev)
+{
+ struct device_node *parent = pdev->dev.of_node;
+ struct device_node *port;
+ int r;
+
+ if (parent == NULL)
+ return 0;
+
+ port = omapdss_of_get_next_port(parent, NULL);
+ if (!port) {
+#ifdef CONFIG_OMAP2_DSS_DPI
+ dpi_init_port(pdev, parent);
+#endif
+ return 0;
+ }
+
+ do {
+ u32 reg;
+
+ r = of_property_read_u32(port, "reg", &reg);
+ if (r)
+ reg = 0;
+
+#ifdef CONFIG_OMAP2_DSS_DPI
+ if (reg == 0)
+ dpi_init_port(pdev, port);
+#endif
+
+#ifdef CONFIG_OMAP2_DSS_SDI
+ if (reg == 1)
+ sdi_init_port(pdev, port);
+#endif
+
+ } while ((port = omapdss_of_get_next_port(parent, port)) != NULL);
+
+ return 0;
+}
+
+static void dss_uninit_ports(void)
+{
+#ifdef CONFIG_OMAP2_DSS_DPI
+ dpi_uninit_port();
+#endif
+
+#ifdef CONFIG_OMAP2_DSS_SDI
+ sdi_uninit_port();
+#endif
+}
+
/* DSS HW IP initialisation */
static int __init omap_dsshw_probe(struct platform_device *pdev)
{
@@ -846,6 +882,8 @@ static int __init omap_dsshw_probe(struct platform_device *pdev)
dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
+ dss_init_ports(pdev);
+
rev = dss_read_reg(DSS_REVISION);
printk(KERN_INFO "OMAP DSS rev %d.%d\n",
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
@@ -865,6 +903,8 @@ err_setup_clocks:
static int __exit omap_dsshw_remove(struct platform_device *pdev)
{
+ dss_uninit_ports();
+
pm_runtime_disable(&pdev->dev);
dss_put_clocks();
@@ -902,12 +942,22 @@ static const struct dev_pm_ops dss_pm_ops = {
.runtime_resume = dss_runtime_resume,
};
+static const struct of_device_id dss_of_match[] = {
+ { .compatible = "ti,omap2-dss", },
+ { .compatible = "ti,omap3-dss", },
+ { .compatible = "ti,omap4-dss", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dss_of_match);
+
static struct platform_driver omap_dsshw_driver = {
.remove = __exit_p(omap_dsshw_remove),
.driver = {
.name = "omapdss_dss",
.owner = THIS_MODULE,
.pm = &dss_pm_ops,
+ .of_match_table = dss_of_match,
},
};
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 057f24c8a332..918fec182424 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -225,8 +225,6 @@ void dss_dump_clocks(struct seq_file *s);
void dss_debug_dump_clocks(struct seq_file *s);
#endif
-int dss_get_ctx_loss_count(void);
-
void dss_sdi_init(int datapairs);
int dss_sdi_enable(void);
void dss_sdi_disable(void);
@@ -252,6 +250,9 @@ bool dss_div_calc(unsigned long pck, unsigned long fck_min,
int sdi_init_platform_driver(void) __init;
void sdi_uninit_platform_driver(void) __exit;
+int sdi_init_port(struct platform_device *pdev, struct device_node *port) __init;
+void sdi_uninit_port(void) __exit;
+
/* DSI */
typedef bool (*dsi_pll_calc_func)(int regn, int regm, unsigned long fint,
@@ -363,6 +364,9 @@ static inline bool dsi_pll_calc(struct platform_device *dsidev,
int dpi_init_platform_driver(void) __init;
void dpi_uninit_platform_driver(void) __exit;
+int dpi_init_port(struct platform_device *pdev, struct device_node *port) __init;
+void dpi_uninit_port(void) __exit;
+
/* DISPC */
int dispc_init_platform_driver(void) __init;
void dispc_uninit_platform_driver(void) __exit;
diff --git a/drivers/video/omap2/dss/hdmi4.c b/drivers/video/omap2/dss/hdmi4.c
index 4a74538f9ea5..f5f7944a1fd1 100644
--- a/drivers/video/omap2/dss/hdmi4.c
+++ b/drivers/video/omap2/dss/hdmi4.c
@@ -88,15 +88,11 @@ static int hdmi_init_regulator(void)
if (hdmi.vdda_hdmi_dac_reg != NULL)
return 0;
- reg = devm_regulator_get(&hdmi.pdev->dev, "vdda_hdmi_dac");
-
- /* DT HACK: try VDAC to make omapdss work for o4 sdp/panda */
- if (IS_ERR(reg))
- reg = devm_regulator_get(&hdmi.pdev->dev, "VDAC");
+ reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
if (IS_ERR(reg)) {
if (PTR_ERR(reg) != -EPROBE_DEFER)
- DSSERR("can't get VDDA_HDMI_DAC regulator\n");
+ DSSERR("can't get VDDA regulator\n");
return PTR_ERR(reg);
}
@@ -153,7 +149,8 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
- phy = p->pixel_clock;
+ /* the functions below use kHz pixel clock. TODO: change to Hz */
+ phy = p->pixelclock / 1000;
hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy);
@@ -238,13 +235,13 @@ static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
if (t != NULL) {
hdmi.cfg = *t;
- dispc_set_tv_pclk(t->timings.pixel_clock * 1000);
+ dispc_set_tv_pclk(t->timings.pixelclock);
} else {
hdmi.cfg.timings = *timings;
hdmi.cfg.cm.code = 0;
hdmi.cfg.cm.mode = HDMI_DVI;
- dispc_set_tv_pclk(timings->pixel_clock * 1000);
+ dispc_set_tv_pclk(timings->pixelclock);
}
DSSDBG("using mode: %s, code %d\n", hdmi.cfg.cm.mode == HDMI_DVI ?
@@ -509,7 +506,7 @@ static int hdmi_audio_config(struct omap_dss_device *dssdev,
struct omap_dss_audio *audio)
{
int r;
- u32 pclk = hdmi.cfg.timings.pixel_clock;
+ u32 pclk = hdmi.cfg.timings.pixelclock;
mutex_lock(&hdmi.lock);
@@ -679,6 +676,11 @@ static const struct dev_pm_ops hdmi_pm_ops = {
.runtime_resume = hdmi_runtime_resume,
};
+static const struct of_device_id hdmi_of_match[] = {
+ { .compatible = "ti,omap4-hdmi", },
+ {},
+};
+
static struct platform_driver omapdss_hdmihw_driver = {
.probe = omapdss_hdmihw_probe,
.remove = __exit_p(omapdss_hdmihw_remove),
@@ -686,6 +688,7 @@ static struct platform_driver omapdss_hdmihw_driver = {
.name = "omapdss_hdmi",
.owner = THIS_MODULE,
.pm = &hdmi_pm_ops,
+ .of_match_table = hdmi_of_match,
},
};
diff --git a/drivers/video/omap2/dss/hdmi_common.c b/drivers/video/omap2/dss/hdmi_common.c
index 0614922902dd..b11afac8e068 100644
--- a/drivers/video/omap2/dss/hdmi_common.c
+++ b/drivers/video/omap2/dss/hdmi_common.c
@@ -23,91 +23,91 @@
static const struct hdmi_config cea_timings[] = {
{
- { 640, 480, 25200, 96, 16, 48, 2, 10, 33,
+ { 640, 480, 25200000, 96, 16, 48, 2, 10, 33,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 1, HDMI_HDMI },
},
{
- { 720, 480, 27027, 62, 16, 60, 6, 9, 30,
+ { 720, 480, 27027000, 62, 16, 60, 6, 9, 30,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 2, HDMI_HDMI },
},
{
- { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
+ { 1280, 720, 74250000, 40, 110, 220, 5, 5, 20,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 4, HDMI_HDMI },
},
{
- { 1920, 540, 74250, 44, 88, 148, 5, 2, 15,
+ { 1920, 540, 74250000, 44, 88, 148, 5, 2, 15,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
true, },
{ 5, HDMI_HDMI },
},
{
- { 1440, 240, 27027, 124, 38, 114, 3, 4, 15,
+ { 1440, 240, 27027000, 124, 38, 114, 3, 4, 15,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
true, },
{ 6, HDMI_HDMI },
},
{
- { 1920, 1080, 148500, 44, 88, 148, 5, 4, 36,
+ { 1920, 1080, 148500000, 44, 88, 148, 5, 4, 36,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 16, HDMI_HDMI },
},
{
- { 720, 576, 27000, 64, 12, 68, 5, 5, 39,
+ { 720, 576, 27000000, 64, 12, 68, 5, 5, 39,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 17, HDMI_HDMI },
},
{
- { 1280, 720, 74250, 40, 440, 220, 5, 5, 20,
+ { 1280, 720, 74250000, 40, 440, 220, 5, 5, 20,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 19, HDMI_HDMI },
},
{
- { 1920, 540, 74250, 44, 528, 148, 5, 2, 15,
+ { 1920, 540, 74250000, 44, 528, 148, 5, 2, 15,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
true, },
{ 20, HDMI_HDMI },
},
{
- { 1440, 288, 27000, 126, 24, 138, 3, 2, 19,
+ { 1440, 288, 27000000, 126, 24, 138, 3, 2, 19,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
true, },
{ 21, HDMI_HDMI },
},
{
- { 1440, 576, 54000, 128, 24, 136, 5, 5, 39,
+ { 1440, 576, 54000000, 128, 24, 136, 5, 5, 39,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 29, HDMI_HDMI },
},
{
- { 1920, 1080, 148500, 44, 528, 148, 5, 4, 36,
+ { 1920, 1080, 148500000, 44, 528, 148, 5, 4, 36,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 31, HDMI_HDMI },
},
{
- { 1920, 1080, 74250, 44, 638, 148, 5, 4, 36,
+ { 1920, 1080, 74250000, 44, 638, 148, 5, 4, 36,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 32, HDMI_HDMI },
},
{
- { 2880, 480, 108108, 248, 64, 240, 6, 9, 30,
+ { 2880, 480, 108108000, 248, 64, 240, 6, 9, 30,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 35, HDMI_HDMI },
},
{
- { 2880, 576, 108000, 256, 48, 272, 5, 5, 39,
+ { 2880, 576, 108000000, 256, 48, 272, 5, 5, 39,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 37, HDMI_HDMI },
@@ -117,121 +117,121 @@ static const struct hdmi_config cea_timings[] = {
static const struct hdmi_config vesa_timings[] = {
/* VESA From Here */
{
- { 640, 480, 25175, 96, 16, 48, 2, 11, 31,
+ { 640, 480, 25175000, 96, 16, 48, 2, 11, 31,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 4, HDMI_DVI },
},
{
- { 800, 600, 40000, 128, 40, 88, 4, 1, 23,
+ { 800, 600, 40000000, 128, 40, 88, 4, 1, 23,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 9, HDMI_DVI },
},
{
- { 848, 480, 33750, 112, 16, 112, 8, 6, 23,
+ { 848, 480, 33750000, 112, 16, 112, 8, 6, 23,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0xE, HDMI_DVI },
},
{
- { 1280, 768, 79500, 128, 64, 192, 7, 3, 20,
+ { 1280, 768, 79500000, 128, 64, 192, 7, 3, 20,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 0x17, HDMI_DVI },
},
{
- { 1280, 800, 83500, 128, 72, 200, 6, 3, 22,
+ { 1280, 800, 83500000, 128, 72, 200, 6, 3, 22,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 0x1C, HDMI_DVI },
},
{
- { 1360, 768, 85500, 112, 64, 256, 6, 3, 18,
+ { 1360, 768, 85500000, 112, 64, 256, 6, 3, 18,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0x27, HDMI_DVI },
},
{
- { 1280, 960, 108000, 112, 96, 312, 3, 1, 36,
+ { 1280, 960, 108000000, 112, 96, 312, 3, 1, 36,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0x20, HDMI_DVI },
},
{
- { 1280, 1024, 108000, 112, 48, 248, 3, 1, 38,
+ { 1280, 1024, 108000000, 112, 48, 248, 3, 1, 38,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0x23, HDMI_DVI },
},
{
- { 1024, 768, 65000, 136, 24, 160, 6, 3, 29,
+ { 1024, 768, 65000000, 136, 24, 160, 6, 3, 29,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 0x10, HDMI_DVI },
},
{
- { 1400, 1050, 121750, 144, 88, 232, 4, 3, 32,
+ { 1400, 1050, 121750000, 144, 88, 232, 4, 3, 32,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 0x2A, HDMI_DVI },
},
{
- { 1440, 900, 106500, 152, 80, 232, 6, 3, 25,
+ { 1440, 900, 106500000, 152, 80, 232, 6, 3, 25,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 0x2F, HDMI_DVI },
},
{
- { 1680, 1050, 146250, 176 , 104, 280, 6, 3, 30,
+ { 1680, 1050, 146250000, 176 , 104, 280, 6, 3, 30,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
false, },
{ 0x3A, HDMI_DVI },
},
{
- { 1366, 768, 85500, 143, 70, 213, 3, 3, 24,
+ { 1366, 768, 85500000, 143, 70, 213, 3, 3, 24,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0x51, HDMI_DVI },
},
{
- { 1920, 1080, 148500, 44, 148, 80, 5, 4, 36,
+ { 1920, 1080, 148500000, 44, 148, 80, 5, 4, 36,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0x52, HDMI_DVI },
},
{
- { 1280, 768, 68250, 32, 48, 80, 7, 3, 12,
+ { 1280, 768, 68250000, 32, 48, 80, 7, 3, 12,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0x16, HDMI_DVI },
},
{
- { 1400, 1050, 101000, 32, 48, 80, 4, 3, 23,
+ { 1400, 1050, 101000000, 32, 48, 80, 4, 3, 23,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0x29, HDMI_DVI },
},
{
- { 1680, 1050, 119000, 32, 48, 80, 6, 3, 21,
+ { 1680, 1050, 119000000, 32, 48, 80, 6, 3, 21,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0x39, HDMI_DVI },
},
{
- { 1280, 800, 79500, 32, 48, 80, 6, 3, 14,
+ { 1280, 800, 79500000, 32, 48, 80, 6, 3, 14,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0x1B, HDMI_DVI },
},
{
- { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
+ { 1280, 720, 74250000, 40, 110, 220, 5, 5, 20,
OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0x55, HDMI_DVI },
},
{
- { 1920, 1200, 154000, 32, 48, 80, 6, 3, 26,
+ { 1920, 1200, 154000000, 32, 48, 80, 6, 3, 26,
OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
false, },
{ 0x44, HDMI_DVI },
@@ -277,8 +277,8 @@ static bool hdmi_timings_compare(struct omap_video_timings *timing1,
{
int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync;
- if ((DIV_ROUND_CLOSEST(timing2->pixel_clock, 1000) ==
- DIV_ROUND_CLOSEST(timing1->pixel_clock, 1000)) &&
+ if ((DIV_ROUND_CLOSEST(timing2->pixelclock, 1000000) ==
+ DIV_ROUND_CLOSEST(timing1->pixelclock, 1000000)) &&
(timing2->x_res == timing1->x_res) &&
(timing2->y_res == timing1->y_res)) {
diff --git a/drivers/video/omap2/dss/hdmi_wp.c b/drivers/video/omap2/dss/hdmi_wp.c
index cd620c6e43a0..f5f4ccf50d90 100644
--- a/drivers/video/omap2/dss/hdmi_wp.c
+++ b/drivers/video/omap2/dss/hdmi_wp.c
@@ -171,6 +171,8 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444;
video_fmt->y_res = param->timings.y_res;
video_fmt->x_res = param->timings.x_res;
+ if (param->timings.interlace)
+ video_fmt->y_res /= 2;
timings->hbp = param->timings.hbp;
timings->hfp = param->timings.hfp;
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c
index ba806c9e7f54..911dcc9173a6 100644
--- a/drivers/video/omap2/dss/sdi.c
+++ b/drivers/video/omap2/dss/sdi.c
@@ -26,6 +26,7 @@
#include <linux/export.h>
#include <linux/platform_device.h>
#include <linux/string.h>
+#include <linux/of.h>
#include <video/omapdss.h>
#include "dss.h"
@@ -41,6 +42,8 @@ static struct {
int datapairs;
struct omap_dss_device output;
+
+ bool port_initialized;
} sdi;
struct sdi_clk_calc_ctx {
@@ -149,20 +152,19 @@ static int sdi_display_enable(struct omap_dss_device *dssdev)
t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
- r = sdi_calc_clock_div(t->pixel_clock * 1000, &fck, &dispc_cinfo);
+ r = sdi_calc_clock_div(t->pixelclock, &fck, &dispc_cinfo);
if (r)
goto err_calc_clock_div;
sdi.mgr_config.clock_info = dispc_cinfo;
- pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div / 1000;
+ pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;
- if (pck != t->pixel_clock) {
- DSSWARN("Could not find exact pixel clock. Requested %d kHz, "
- "got %lu kHz\n",
- t->pixel_clock, pck);
+ if (pck != t->pixelclock) {
+ DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n",
+ t->pixelclock, pck);
- t->pixel_clock = pck;
+ t->pixelclock = pck;
}
@@ -244,7 +246,7 @@ static int sdi_check_timings(struct omap_dss_device *dssdev,
if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
return -EINVAL;
- if (timings->pixel_clock == 0)
+ if (timings->pixelclock == 0)
return -EINVAL;
return 0;
@@ -387,3 +389,45 @@ void __exit sdi_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_sdi_driver);
}
+
+int __init sdi_init_port(struct platform_device *pdev, struct device_node *port)
+{
+ struct device_node *ep;
+ u32 datapairs;
+ int r;
+
+ ep = omapdss_of_get_next_endpoint(port, NULL);
+ if (!ep)
+ return 0;
+
+ r = of_property_read_u32(ep, "datapairs", &datapairs);
+ if (r) {
+ DSSERR("failed to parse datapairs\n");
+ goto err_datapairs;
+ }
+
+ sdi.datapairs = datapairs;
+
+ of_node_put(ep);
+
+ sdi.pdev = pdev;
+
+ sdi_init_output(pdev);
+
+ sdi.port_initialized = true;
+
+ return 0;
+
+err_datapairs:
+ of_node_put(ep);
+
+ return r;
+}
+
+void __exit sdi_uninit_port(void)
+{
+ if (!sdi.port_initialized)
+ return;
+
+ sdi_uninit_output(sdi.pdev);
+}
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
index 2cd7f7e42105..21d81113962b 100644
--- a/drivers/video/omap2/dss/venc.c
+++ b/drivers/video/omap2/dss/venc.c
@@ -34,6 +34,7 @@
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
#include <video/omapdss.h>
@@ -264,7 +265,7 @@ static const struct venc_config venc_config_pal_bdghi = {
const struct omap_video_timings omap_dss_pal_timings = {
.x_res = 720,
.y_res = 574,
- .pixel_clock = 13500,
+ .pixelclock = 13500000,
.hsw = 64,
.hfp = 12,
.hbp = 68,
@@ -279,7 +280,7 @@ EXPORT_SYMBOL(omap_dss_pal_timings);
const struct omap_video_timings omap_dss_ntsc_timings = {
.x_res = 720,
.y_res = 482,
- .pixel_clock = 13500,
+ .pixelclock = 13500000,
.hsw = 64,
.hfp = 16,
.hbp = 58,
@@ -636,7 +637,10 @@ static int venc_init_regulator(void)
if (venc.vdda_dac_reg != NULL)
return 0;
- vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda_dac");
+ if (venc.pdev->dev.of_node)
+ vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda");
+ else
+ vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda_dac");
if (IS_ERR(vdda_dac)) {
if (PTR_ERR(vdda_dac) != -EPROBE_DEFER)
@@ -805,6 +809,48 @@ static void __exit venc_uninit_output(struct platform_device *pdev)
omapdss_unregister_output(out);
}
+static int venc_probe_of(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device_node *ep;
+ u32 channels;
+ int r;
+
+ ep = omapdss_of_get_first_endpoint(node);
+ if (!ep)
+ return 0;
+
+ venc.invert_polarity = of_property_read_bool(ep, "ti,invert-polarity");
+
+ r = of_property_read_u32(ep, "ti,channels", &channels);
+ if (r) {
+ dev_err(&pdev->dev,
+ "failed to read property 'ti,channels': %d\n", r);
+ goto err;
+ }
+
+ switch (channels) {
+ case 1:
+ venc.type = OMAP_DSS_VENC_TYPE_COMPOSITE;
+ break;
+ case 2:
+ venc.type = OMAP_DSS_VENC_TYPE_SVIDEO;
+ break;
+ default:
+ dev_err(&pdev->dev, "bad channel propert '%d'\n", channels);
+ r = -EINVAL;
+ goto err;
+ }
+
+ of_node_put(ep);
+
+ return 0;
+err:
+ of_node_put(ep);
+
+ return 0;
+}
+
/* VENC HW IP initialisation */
static int omap_venchw_probe(struct platform_device *pdev)
{
@@ -846,12 +892,21 @@ static int omap_venchw_probe(struct platform_device *pdev)
venc_runtime_put();
+ if (pdev->dev.of_node) {
+ r = venc_probe_of(pdev);
+ if (r) {
+ DSSERR("Invalid DT data\n");
+ goto err_probe_of;
+ }
+ }
+
dss_debugfs_create_file("venc", venc_dump_regs);
venc_init_output(pdev);
return 0;
+err_probe_of:
err_runtime_get:
pm_runtime_disable(&pdev->dev);
return r;
@@ -895,6 +950,14 @@ static const struct dev_pm_ops venc_pm_ops = {
.runtime_resume = venc_runtime_resume,
};
+
+static const struct of_device_id venc_of_match[] = {
+ { .compatible = "ti,omap2-venc", },
+ { .compatible = "ti,omap3-venc", },
+ { .compatible = "ti,omap4-venc", },
+ {},
+};
+
static struct platform_driver omap_venchw_driver = {
.probe = omap_venchw_probe,
.remove = __exit_p(omap_venchw_remove),
@@ -902,6 +965,7 @@ static struct platform_driver omap_venchw_driver = {
.name = "omapdss_venc",
.owner = THIS_MODULE,
.pm = &venc_pm_ops,
+ .of_match_table = venc_of_match,
},
};
diff --git a/drivers/video/omap2/dss/venc_panel.c b/drivers/video/omap2/dss/venc_panel.c
index f7d92c57bd73..af68cd444d7e 100644
--- a/drivers/video/omap2/dss/venc_panel.c
+++ b/drivers/video/omap2/dss/venc_panel.c
@@ -89,7 +89,7 @@ static int venc_panel_probe(struct omap_dss_device *dssdev)
const struct omap_video_timings default_timings = {
.x_res = 720,
.y_res = 574,
- .pixel_clock = 13500,
+ .pixelclock = 13500000,
.hsw = 64,
.hfp = 12,
.hbp = 68,
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index fcb9e932d00c..ec2d132c782d 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -723,8 +723,8 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
display->driver->get_timings(display, &timings);
/* pixclock in ps, the rest in pixclock */
- var->pixclock = timings.pixel_clock != 0 ?
- KHZ2PICOS(timings.pixel_clock) :
+ var->pixclock = timings.pixelclock != 0 ?
+ KHZ2PICOS(timings.pixelclock / 1000) :
0;
var->left_margin = timings.hbp;
var->right_margin = timings.hfp;
@@ -2077,7 +2077,7 @@ static int omapfb_mode_to_timings(const char *mode_str,
timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
}
- timings->pixel_clock = PICOS2KHZ(var->pixclock);
+ timings->pixelclock = PICOS2KHZ(var->pixclock) * 1000;
timings->hbp = var->left_margin;
timings->hfp = var->right_margin;
timings->vbp = var->upper_margin;
@@ -2229,7 +2229,7 @@ static void fb_videomode_to_omap_timings(struct fb_videomode *m,
t->x_res = m->xres;
t->y_res = m->yres;
- t->pixel_clock = PICOS2KHZ(m->pixclock);
+ t->pixelclock = PICOS2KHZ(m->pixclock) * 1000;
t->hsw = m->hsync_len;
t->hfp = m->right_margin;
t->hbp = m->left_margin;
@@ -2417,6 +2417,55 @@ static int omapfb_init_connections(struct omapfb2_device *fbdev,
return 0;
}
+static struct omap_dss_device *
+omapfb_find_default_display(struct omapfb2_device *fbdev)
+{
+ const char *def_name;
+ int i;
+
+ /*
+ * Search with the display name from the user or the board file,
+ * comparing to display names and aliases
+ */
+
+ def_name = omapdss_get_default_display_name();
+
+ if (def_name) {
+ for (i = 0; i < fbdev->num_displays; ++i) {
+ struct omap_dss_device *dssdev;
+
+ dssdev = fbdev->displays[i].dssdev;
+
+ if (dssdev->name && strcmp(def_name, dssdev->name) == 0)
+ return dssdev;
+
+ if (strcmp(def_name, dssdev->alias) == 0)
+ return dssdev;
+ }
+
+ /* def_name given but not found */
+ return NULL;
+ }
+
+ /* then look for DT alias display0 */
+ for (i = 0; i < fbdev->num_displays; ++i) {
+ struct omap_dss_device *dssdev;
+ int id;
+
+ dssdev = fbdev->displays[i].dssdev;
+
+ if (dssdev->dev->of_node == NULL)
+ continue;
+
+ id = of_alias_get_id(dssdev->dev->of_node, "display");
+ if (id == 0)
+ return dssdev;
+ }
+
+ /* return the first display we have in the list */
+ return fbdev->displays[0].dssdev;
+}
+
static int omapfb_probe(struct platform_device *pdev)
{
struct omapfb2_device *fbdev = NULL;
@@ -2494,23 +2543,7 @@ static int omapfb_probe(struct platform_device *pdev)
for (i = 0; i < fbdev->num_managers; i++)
fbdev->managers[i] = omap_dss_get_overlay_manager(i);
- def_display = NULL;
-
- for (i = 0; i < fbdev->num_displays; ++i) {
- struct omap_dss_device *dssdev;
- const char *def_name;
-
- def_name = omapdss_get_default_display_name();
-
- dssdev = fbdev->displays[i].dssdev;
-
- if (def_name == NULL ||
- (dssdev->name && strcmp(def_name, dssdev->name) == 0)) {
- def_display = dssdev;
- break;
- }
- }
-
+ def_display = omapfb_find_default_display(fbdev);
if (def_display == NULL) {
dev_err(fbdev->dev, "failed to find default display\n");
r = -EPROBE_DEFER;
diff --git a/drivers/video/pxa3xx-gcu.c b/drivers/video/pxa3xx-gcu.c
index ad382b3396cd..417f9a27eb7d 100644
--- a/drivers/video/pxa3xx-gcu.c
+++ b/drivers/video/pxa3xx-gcu.c
@@ -107,7 +107,6 @@ struct pxa3xx_gcu_priv {
struct timeval base_time;
struct pxa3xx_gcu_batch *free;
-
struct pxa3xx_gcu_batch *ready;
struct pxa3xx_gcu_batch *ready_last;
struct pxa3xx_gcu_batch *running;
@@ -368,27 +367,35 @@ pxa3xx_gcu_wait_free(struct pxa3xx_gcu_priv *priv)
/* Misc device layer */
-static inline struct pxa3xx_gcu_priv *file_dev(struct file *file)
+static inline struct pxa3xx_gcu_priv *to_pxa3xx_gcu_priv(struct file *file)
{
struct miscdevice *dev = file->private_data;
return container_of(dev, struct pxa3xx_gcu_priv, misc_dev);
}
+/*
+ * provide an empty .open callback, so the core sets file->private_data
+ * for us.
+ */
+static int pxa3xx_gcu_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
static ssize_t
-pxa3xx_gcu_misc_write(struct file *file, const char *buff,
- size_t count, loff_t *offp)
+pxa3xx_gcu_write(struct file *file, const char *buff,
+ size_t count, loff_t *offp)
{
int ret;
unsigned long flags;
struct pxa3xx_gcu_batch *buffer;
- struct pxa3xx_gcu_priv *priv = file_dev(file);
+ struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
int words = count / 4;
/* Does not need to be atomic. There's a lock in user space,
* but anyhow, this is just for statistics. */
priv->shared->num_writes++;
-
priv->shared->num_words += words;
/* Last word reserved for batch buffer end command */
@@ -406,10 +413,8 @@ pxa3xx_gcu_misc_write(struct file *file, const char *buff,
* Get buffer from free list
*/
spin_lock_irqsave(&priv->spinlock, flags);
-
buffer = priv->free;
priv->free = buffer->next;
-
spin_unlock_irqrestore(&priv->spinlock, flags);
@@ -454,10 +459,10 @@ pxa3xx_gcu_misc_write(struct file *file, const char *buff,
static long
-pxa3xx_gcu_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+pxa3xx_gcu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned long flags;
- struct pxa3xx_gcu_priv *priv = file_dev(file);
+ struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
switch (cmd) {
case PXA3XX_GCU_IOCTL_RESET:
@@ -474,10 +479,10 @@ pxa3xx_gcu_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
static int
-pxa3xx_gcu_misc_mmap(struct file *file, struct vm_area_struct *vma)
+pxa3xx_gcu_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned int size = vma->vm_end - vma->vm_start;
- struct pxa3xx_gcu_priv *priv = file_dev(file);
+ struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
switch (vma->vm_pgoff) {
case 0:
@@ -532,8 +537,8 @@ static inline void pxa3xx_gcu_init_debug_timer(void) {}
#endif
static int
-add_buffer(struct platform_device *dev,
- struct pxa3xx_gcu_priv *priv)
+pxa3xx_gcu_add_buffer(struct device *dev,
+ struct pxa3xx_gcu_priv *priv)
{
struct pxa3xx_gcu_batch *buffer;
@@ -541,7 +546,7 @@ add_buffer(struct platform_device *dev,
if (!buffer)
return -ENOMEM;
- buffer->ptr = dma_alloc_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4,
+ buffer->ptr = dma_alloc_coherent(dev, PXA3XX_GCU_BATCH_WORDS * 4,
&buffer->phys, GFP_KERNEL);
if (!buffer->ptr) {
kfree(buffer);
@@ -549,57 +554,49 @@ add_buffer(struct platform_device *dev,
}
buffer->next = priv->free;
-
priv->free = buffer;
return 0;
}
static void
-free_buffers(struct platform_device *dev,
- struct pxa3xx_gcu_priv *priv)
+pxa3xx_gcu_free_buffers(struct device *dev,
+ struct pxa3xx_gcu_priv *priv)
{
struct pxa3xx_gcu_batch *next, *buffer = priv->free;
while (buffer) {
next = buffer->next;
- dma_free_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4,
+ dma_free_coherent(dev, PXA3XX_GCU_BATCH_WORDS * 4,
buffer->ptr, buffer->phys);
kfree(buffer);
-
buffer = next;
}
priv->free = NULL;
}
-static const struct file_operations misc_fops = {
- .owner = THIS_MODULE,
- .write = pxa3xx_gcu_misc_write,
- .unlocked_ioctl = pxa3xx_gcu_misc_ioctl,
- .mmap = pxa3xx_gcu_misc_mmap
+static const struct file_operations pxa3xx_gcu_miscdev_fops = {
+ .owner = THIS_MODULE,
+ .open = pxa3xx_gcu_open,
+ .write = pxa3xx_gcu_write,
+ .unlocked_ioctl = pxa3xx_gcu_ioctl,
+ .mmap = pxa3xx_gcu_mmap,
};
-static int pxa3xx_gcu_probe(struct platform_device *dev)
+static int pxa3xx_gcu_probe(struct platform_device *pdev)
{
int i, ret, irq;
struct resource *r;
struct pxa3xx_gcu_priv *priv;
+ struct device *dev = &pdev->dev;
- priv = kzalloc(sizeof(struct pxa3xx_gcu_priv), GFP_KERNEL);
+ priv = devm_kzalloc(dev, sizeof(struct pxa3xx_gcu_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- for (i = 0; i < 8; i++) {
- ret = add_buffer(dev, priv);
- if (ret) {
- dev_err(&dev->dev, "failed to allocate DMA memory\n");
- goto err_free_priv;
- }
- }
-
init_waitqueue_head(&priv->wait_idle);
init_waitqueue_head(&priv->wait_free);
spin_lock_init(&priv->spinlock);
@@ -611,125 +608,99 @@ static int pxa3xx_gcu_probe(struct platform_device *dev)
priv->misc_dev.minor = MISCDEV_MINOR,
priv->misc_dev.name = DRV_NAME,
- priv->misc_dev.fops = &misc_fops,
+ priv->misc_dev.fops = &pxa3xx_gcu_miscdev_fops;
- /* register misc device */
- ret = misc_register(&priv->misc_dev);
- if (ret < 0) {
- dev_err(&dev->dev, "misc_register() for minor %d failed\n",
- MISCDEV_MINOR);
- goto err_free_priv;
+ /* handle IO resources */
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->mmio_base = devm_request_and_ioremap(dev, r);
+ if (IS_ERR(priv->mmio_base)) {
+ dev_err(dev, "failed to map I/O memory\n");
+ return PTR_ERR(priv->mmio_base);
}
- /* handle IO resources */
- r = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (r == NULL) {
- dev_err(&dev->dev, "no I/O memory resource defined\n");
- ret = -ENODEV;
- goto err_misc_deregister;
+ /* enable the clock */
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "failed to get clock\n");
+ return PTR_ERR(priv->clk);
}
- if (!request_mem_region(r->start, resource_size(r), dev->name)) {
- dev_err(&dev->dev, "failed to request I/O memory\n");
- ret = -EBUSY;
- goto err_misc_deregister;
+ /* request the IRQ */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "no IRQ defined\n");
+ return -ENODEV;
}
- priv->mmio_base = ioremap_nocache(r->start, resource_size(r));
- if (!priv->mmio_base) {
- dev_err(&dev->dev, "failed to map I/O memory\n");
- ret = -EBUSY;
- goto err_free_mem_region;
+ ret = devm_request_irq(dev, irq, pxa3xx_gcu_handle_irq,
+ 0, DRV_NAME, priv);
+ if (ret < 0) {
+ dev_err(dev, "request_irq failed\n");
+ return ret;
}
/* allocate dma memory */
- priv->shared = dma_alloc_coherent(&dev->dev, SHARED_SIZE,
+ priv->shared = dma_alloc_coherent(dev, SHARED_SIZE,
&priv->shared_phys, GFP_KERNEL);
-
if (!priv->shared) {
- dev_err(&dev->dev, "failed to allocate DMA memory\n");
- ret = -ENOMEM;
- goto err_free_io;
+ dev_err(dev, "failed to allocate DMA memory\n");
+ return -ENOMEM;
}
- /* enable the clock */
- priv->clk = clk_get(&dev->dev, NULL);
- if (IS_ERR(priv->clk)) {
- dev_err(&dev->dev, "failed to get clock\n");
- ret = -ENODEV;
+ /* register misc device */
+ ret = misc_register(&priv->misc_dev);
+ if (ret < 0) {
+ dev_err(dev, "misc_register() for minor %d failed\n",
+ MISCDEV_MINOR);
goto err_free_dma;
}
ret = clk_enable(priv->clk);
if (ret < 0) {
- dev_err(&dev->dev, "failed to enable clock\n");
- goto err_put_clk;
- }
-
- /* request the IRQ */
- irq = platform_get_irq(dev, 0);
- if (irq < 0) {
- dev_err(&dev->dev, "no IRQ defined\n");
- ret = -ENODEV;
- goto err_put_clk;
+ dev_err(dev, "failed to enable clock\n");
+ goto err_misc_deregister;
}
- ret = request_irq(irq, pxa3xx_gcu_handle_irq,
- 0, DRV_NAME, priv);
- if (ret) {
- dev_err(&dev->dev, "request_irq failed\n");
- ret = -EBUSY;
- goto err_put_clk;
+ for (i = 0; i < 8; i++) {
+ ret = pxa3xx_gcu_add_buffer(dev, priv);
+ if (ret) {
+ dev_err(dev, "failed to allocate DMA memory\n");
+ goto err_disable_clk;
+ }
}
- platform_set_drvdata(dev, priv);
+ platform_set_drvdata(pdev, priv);
priv->resource_mem = r;
pxa3xx_gcu_reset(priv);
pxa3xx_gcu_init_debug_timer();
- dev_info(&dev->dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n",
+ dev_info(dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n",
(void *) r->start, (void *) priv->shared_phys,
SHARED_SIZE, irq);
return 0;
-err_put_clk:
- clk_disable(priv->clk);
- clk_put(priv->clk);
-
err_free_dma:
- dma_free_coherent(&dev->dev, SHARED_SIZE,
+ dma_free_coherent(dev, SHARED_SIZE,
priv->shared, priv->shared_phys);
-err_free_io:
- iounmap(priv->mmio_base);
-
-err_free_mem_region:
- release_mem_region(r->start, resource_size(r));
-
err_misc_deregister:
misc_deregister(&priv->misc_dev);
-err_free_priv:
- free_buffers(dev, priv);
- kfree(priv);
+err_disable_clk:
+ clk_disable(priv->clk);
+
return ret;
}
-static int pxa3xx_gcu_remove(struct platform_device *dev)
+static int pxa3xx_gcu_remove(struct platform_device *pdev)
{
- struct pxa3xx_gcu_priv *priv = platform_get_drvdata(dev);
- struct resource *r = priv->resource_mem;
+ struct pxa3xx_gcu_priv *priv = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
pxa3xx_gcu_wait_idle(priv);
-
misc_deregister(&priv->misc_dev);
- dma_free_coherent(&dev->dev, SHARED_SIZE,
- priv->shared, priv->shared_phys);
- iounmap(priv->mmio_base);
- release_mem_region(r->start, resource_size(r));
- clk_disable(priv->clk);
- free_buffers(dev, priv);
- kfree(priv);
+ dma_free_coherent(dev, SHARED_SIZE, priv->shared, priv->shared_phys);
+ pxa3xx_gcu_free_buffers(dev, priv);
return 0;
}
diff --git a/drivers/video/sis/init.c b/drivers/video/sis/init.c
index 4f26bc28e60b..bd40f5ecd901 100644
--- a/drivers/video/sis/init.c
+++ b/drivers/video/sis/init.c
@@ -651,6 +651,7 @@ SiS_GetModeID_LCD(int VGAEngine, unsigned int VBFlags, int HDisplay, int VDispla
switch(VDisplay) {
case 720:
ModeIndex = ModeIndex_1280x720[Depth];
+ break;
case 768:
if(VGAEngine == SIS_300_VGA) {
ModeIndex = ModeIndex_300_1280x768[Depth];
diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c
index 07c7df9ee77b..65ba9921506e 100644
--- a/drivers/video/tgafb.c
+++ b/drivers/video/tgafb.c
@@ -182,6 +182,8 @@ tgafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
if (var->xres_virtual != var->xres || var->yres_virtual != var->yres)
return -EINVAL;
+ if (var->xres * var->yres * (var->bits_per_pixel >> 3) > info->fix.smem_len)
+ return -EINVAL;
if (var->nonstd)
return -EINVAL;
if (1000000000 / var->pixclock > TGA_PLL_MAX_FREQ)
@@ -190,8 +192,8 @@ tgafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
return -EINVAL;
/* Some of the acceleration routines assume the line width is
- a multiple of 64 bytes. */
- if (var->xres * (par->tga_type == TGA_TYPE_8PLANE ? 1 : 4) % 64)
+ a multiple of 8 bytes. */
+ if (var->xres * (par->tga_type == TGA_TYPE_8PLANE ? 1 : 4) % 8)
return -EINVAL;
return 0;
@@ -262,6 +264,7 @@ tgafb_set_par(struct fb_info *info)
par->yres = info->var.yres;
par->pll_freq = pll_freq = 1000000000 / info->var.pixclock;
par->bits_per_pixel = info->var.bits_per_pixel;
+ info->fix.line_length = par->xres * (par->bits_per_pixel >> 3);
tga_type = par->tga_type;
@@ -1136,222 +1139,57 @@ copyarea_line_32bpp(struct fb_info *info, u32 dy, u32 sy,
__raw_writel(TGA_MODE_SBM_24BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
}
-/* The general case of forward copy in 8bpp mode. */
+/* The (almost) general case of backward copy in 8bpp mode. */
static inline void
-copyarea_foreward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
- u32 height, u32 width, u32 line_length)
+copyarea_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
+ u32 height, u32 width, u32 line_length,
+ const struct fb_copyarea *area)
{
struct tga_par *par = (struct tga_par *) info->par;
- unsigned long i, copied, left;
- unsigned long dpos, spos, dalign, salign, yincr;
- u32 smask_first, dmask_first, dmask_last;
- int pixel_shift, need_prime, need_second;
- unsigned long n64, n32, xincr_first;
+ unsigned i, yincr;
+ int depos, sepos, backward, last_step, step;
+ u32 mask_last;
+ unsigned n32;
void __iomem *tga_regs;
void __iomem *tga_fb;
- yincr = line_length;
- if (dy > sy) {
- dy += height - 1;
- sy += height - 1;
- yincr = -yincr;
- }
-
- /* Compute the offsets and alignments in the frame buffer.
- More than anything else, these control how we do copies. */
- dpos = dy * line_length + dx;
- spos = sy * line_length + sx;
- dalign = dpos & 7;
- salign = spos & 7;
- dpos &= -8;
- spos &= -8;
-
- /* Compute the value for the PIXELSHIFT register. This controls
- both non-co-aligned source and destination and copy direction. */
- if (dalign >= salign)
- pixel_shift = dalign - salign;
- else
- pixel_shift = 8 - (salign - dalign);
-
- /* Figure out if we need an additional priming step for the
- residue register. */
- need_prime = (salign > dalign);
- if (need_prime)
- dpos -= 8;
-
- /* Begin by copying the leading unaligned destination. Copy enough
- to make the next destination address 32-byte aligned. */
- copied = 32 - (dalign + (dpos & 31));
- if (copied == 32)
- copied = 0;
- xincr_first = (copied + 7) & -8;
- smask_first = dmask_first = (1ul << copied) - 1;
- smask_first <<= salign;
- dmask_first <<= dalign + need_prime*8;
- if (need_prime && copied > 24)
- copied -= 8;
- left = width - copied;
-
- /* Care for small copies. */
- if (copied > width) {
- u32 t;
- t = (1ul << width) - 1;
- t <<= dalign + need_prime*8;
- dmask_first &= t;
- left = 0;
- }
-
- /* Attempt to use 64-byte copies. This is only possible if the
- source and destination are co-aligned at 64 bytes. */
- n64 = need_second = 0;
- if ((dpos & 63) == (spos & 63)
- && (height == 1 || line_length % 64 == 0)) {
- /* We may need a 32-byte copy to ensure 64 byte alignment. */
- need_second = (dpos + xincr_first) & 63;
- if ((need_second & 32) != need_second)
- printk(KERN_ERR "tgafb: need_second wrong\n");
- if (left >= need_second + 64) {
- left -= need_second;
- n64 = left / 64;
- left %= 64;
- } else
- need_second = 0;
- }
-
- /* Copy trailing full 32-byte sections. This will be the main
- loop if the 64 byte loop can't be used. */
- n32 = left / 32;
- left %= 32;
-
- /* Copy the trailing unaligned destination. */
- dmask_last = (1ul << left) - 1;
-
- tga_regs = par->tga_regs_base;
- tga_fb = par->tga_fb_base;
-
- /* Set up the MODE and PIXELSHIFT registers. */
- __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
- __raw_writel(pixel_shift, tga_regs+TGA_PIXELSHIFT_REG);
- wmb();
-
- for (i = 0; i < height; ++i) {
- unsigned long j;
- void __iomem *sfb;
- void __iomem *dfb;
-
- sfb = tga_fb + spos;
- dfb = tga_fb + dpos;
- if (dmask_first) {
- __raw_writel(smask_first, sfb);
- wmb();
- __raw_writel(dmask_first, dfb);
- wmb();
- sfb += xincr_first;
- dfb += xincr_first;
- }
-
- if (need_second) {
- __raw_writel(0xffffffff, sfb);
- wmb();
- __raw_writel(0xffffffff, dfb);
- wmb();
- sfb += 32;
- dfb += 32;
- }
-
- if (n64 && (((unsigned long)sfb | (unsigned long)dfb) & 63))
- printk(KERN_ERR
- "tgafb: misaligned copy64 (s:%p, d:%p)\n",
- sfb, dfb);
-
- for (j = 0; j < n64; ++j) {
- __raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC);
- wmb();
- __raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST);
- wmb();
- sfb += 64;
- dfb += 64;
- }
-
- for (j = 0; j < n32; ++j) {
- __raw_writel(0xffffffff, sfb);
- wmb();
- __raw_writel(0xffffffff, dfb);
- wmb();
- sfb += 32;
- dfb += 32;
- }
-
- if (dmask_last) {
- __raw_writel(0xffffffff, sfb);
- wmb();
- __raw_writel(dmask_last, dfb);
- wmb();
- }
-
- spos += yincr;
- dpos += yincr;
+ /* Do acceleration only if we are aligned on 8 pixels */
+ if ((dx | sx | width) & 7) {
+ cfb_copyarea(info, area);
+ return;
}
- /* Reset the MODE register to normal. */
- __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
-}
-
-/* The (almost) general case of backward copy in 8bpp mode. */
-static inline void
-copyarea_backward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
- u32 height, u32 width, u32 line_length,
- const struct fb_copyarea *area)
-{
- struct tga_par *par = (struct tga_par *) info->par;
- unsigned long i, left, yincr;
- unsigned long depos, sepos, dealign, sealign;
- u32 mask_first, mask_last;
- unsigned long n32;
- void __iomem *tga_regs;
- void __iomem *tga_fb;
-
yincr = line_length;
if (dy > sy) {
dy += height - 1;
sy += height - 1;
yincr = -yincr;
}
+ backward = dy == sy && dx > sx && dx < sx + width;
/* Compute the offsets and alignments in the frame buffer.
More than anything else, these control how we do copies. */
- depos = dy * line_length + dx + width;
- sepos = sy * line_length + sx + width;
- dealign = depos & 7;
- sealign = sepos & 7;
-
- /* ??? The documentation appears to be incorrect (or very
- misleading) wrt how pixel shifting works in backward copy
- mode, i.e. when PIXELSHIFT is negative. I give up for now.
- Do handle the common case of co-aligned backward copies,
- but frob everything else back on generic code. */
- if (dealign != sealign) {
- cfb_copyarea(info, area);
- return;
- }
-
- /* We begin the copy with the trailing pixels of the
- unaligned destination. */
- mask_first = (1ul << dealign) - 1;
- left = width - dealign;
-
- /* Care for small copies. */
- if (dealign > width) {
- mask_first ^= (1ul << (dealign - width)) - 1;
- left = 0;
- }
+ depos = dy * line_length + dx;
+ sepos = sy * line_length + sx;
+ if (backward)
+ depos += width, sepos += width;
/* Next copy full words at a time. */
- n32 = left / 32;
- left %= 32;
+ n32 = width / 32;
+ last_step = width % 32;
/* Finally copy the unaligned head of the span. */
- mask_last = -1 << (32 - left);
+ mask_last = (1ul << last_step) - 1;
+
+ if (!backward) {
+ step = 32;
+ last_step = 32;
+ } else {
+ step = -32;
+ last_step = -last_step;
+ sepos -= 32;
+ depos -= 32;
+ }
tga_regs = par->tga_regs_base;
tga_fb = par->tga_fb_base;
@@ -1368,25 +1206,33 @@ copyarea_backward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
sfb = tga_fb + sepos;
dfb = tga_fb + depos;
- if (mask_first) {
- __raw_writel(mask_first, sfb);
- wmb();
- __raw_writel(mask_first, dfb);
- wmb();
- }
- for (j = 0; j < n32; ++j) {
- sfb -= 32;
- dfb -= 32;
+ for (j = 0; j < n32; j++) {
+ if (j < 2 && j + 1 < n32 && !backward &&
+ !(((unsigned long)sfb | (unsigned long)dfb) & 63)) {
+ do {
+ __raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC);
+ wmb();
+ __raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST);
+ wmb();
+ sfb += 64;
+ dfb += 64;
+ j += 2;
+ } while (j + 1 < n32);
+ j--;
+ continue;
+ }
__raw_writel(0xffffffff, sfb);
wmb();
__raw_writel(0xffffffff, dfb);
wmb();
+ sfb += step;
+ dfb += step;
}
if (mask_last) {
- sfb -= 32;
- dfb -= 32;
+ sfb += last_step - step;
+ dfb += last_step - step;
__raw_writel(mask_last, sfb);
wmb();
__raw_writel(mask_last, dfb);
@@ -1434,7 +1280,7 @@ tgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
bpp = info->var.bits_per_pixel;
/* Detect copies of the entire line. */
- if (width * (bpp >> 3) == line_length) {
+ if (!(line_length & 63) && width * (bpp >> 3) == line_length) {
if (bpp == 8)
copyarea_line_8bpp(info, dy, sy, height, width);
else
@@ -1447,14 +1293,9 @@ tgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
else if (bpp == 32)
cfb_copyarea(info, area);
- /* Detect overlapping source and destination that requires
- a backward copy. */
- else if (dy == sy && dx > sx && dx < sx + width)
- copyarea_backward_8bpp(info, dx, dy, sx, sy, height,
- width, line_length, area);
else
- copyarea_foreward_8bpp(info, dx, dy, sx, sy, height,
- width, line_length);
+ copyarea_8bpp(info, dx, dy, sx, sy, height,
+ width, line_length, area);
}
@@ -1470,6 +1311,7 @@ tgafb_init_fix(struct fb_info *info)
int tga_bus_tc = TGA_BUS_TC(par->dev);
u8 tga_type = par->tga_type;
const char *tga_type_name = NULL;
+ unsigned memory_size;
switch (tga_type) {
case TGA_TYPE_8PLANE:
@@ -1477,22 +1319,27 @@ tgafb_init_fix(struct fb_info *info)
tga_type_name = "Digital ZLXp-E1";
if (tga_bus_tc)
tga_type_name = "Digital ZLX-E1";
+ memory_size = 2097152;
break;
case TGA_TYPE_24PLANE:
if (tga_bus_pci)
tga_type_name = "Digital ZLXp-E2";
if (tga_bus_tc)
tga_type_name = "Digital ZLX-E2";
+ memory_size = 8388608;
break;
case TGA_TYPE_24PLUSZ:
if (tga_bus_pci)
tga_type_name = "Digital ZLXp-E3";
if (tga_bus_tc)
tga_type_name = "Digital ZLX-E3";
+ memory_size = 16777216;
break;
}
- if (!tga_type_name)
+ if (!tga_type_name) {
tga_type_name = "Unknown";
+ memory_size = 16777216;
+ }
strlcpy(info->fix.id, tga_type_name, sizeof(info->fix.id));
@@ -1502,9 +1349,8 @@ tgafb_init_fix(struct fb_info *info)
? FB_VISUAL_PSEUDOCOLOR
: FB_VISUAL_DIRECTCOLOR);
- info->fix.line_length = par->xres * (par->bits_per_pixel >> 3);
info->fix.smem_start = (size_t) par->tga_fb_base;
- info->fix.smem_len = info->fix.line_length * par->yres;
+ info->fix.smem_len = memory_size;
info->fix.mmio_start = (size_t) par->tga_regs_base;
info->fix.mmio_len = 512;
@@ -1628,6 +1474,9 @@ static int tgafb_register(struct device *dev)
modedb_tga = &modedb_tc;
modedbsize_tga = 1;
}
+
+ tgafb_init_fix(info);
+
ret = fb_find_mode(&info->var, info,
mode_option ? mode_option : mode_option_tga,
modedb_tga, modedbsize_tga, NULL,
@@ -1645,7 +1494,6 @@ static int tgafb_register(struct device *dev)
}
tgafb_set_par(info);
- tgafb_init_fix(info);
if (register_framebuffer(info) < 0) {
printk(KERN_ERR "tgafb: Could not register framebuffer\n");
diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c
index 1f38445014c1..509d452e8f91 100644
--- a/drivers/video/uvesafb.c
+++ b/drivers/video/uvesafb.c
@@ -1474,12 +1474,7 @@ static void uvesafb_init_info(struct fb_info *info, struct vbe_mode_ib *mode)
* used video mode, i.e. the minimum amount of
* memory we need.
*/
- if (mode != NULL) {
- size_vmode = info->var.yres * mode->bytes_per_scan_line;
- } else {
- size_vmode = info->var.yres * info->var.xres *
- ((info->var.bits_per_pixel + 7) >> 3);
- }
+ size_vmode = info->var.yres * mode->bytes_per_scan_line;
/*
* size_total -- all video memory we have. Used for mtrr
@@ -1812,11 +1807,9 @@ static int uvesafb_remove(struct platform_device *dev)
fb_destroy_modedb(info->monspecs.modedb);
fb_dealloc_cmap(&info->cmap);
- if (par) {
- kfree(par->vbe_modes);
- kfree(par->vbe_state_orig);
- kfree(par->vbe_state_saved);
- }
+ kfree(par->vbe_modes);
+ kfree(par->vbe_state_orig);
+ kfree(par->vbe_state_saved);
framebuffer_release(info);
}
diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c
index 1c7da3b098d6..6170e7f58640 100644
--- a/drivers/video/vesafb.c
+++ b/drivers/video/vesafb.c
@@ -179,7 +179,6 @@ static void vesafb_destroy(struct fb_info *info)
if (info->screen_base)
iounmap(info->screen_base);
release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size);
- framebuffer_release(info);
}
static struct fb_ops vesafb_ops = {
@@ -297,6 +296,7 @@ static int vesafb_probe(struct platform_device *dev)
release_mem_region(vesafb_fix.smem_start, size_total);
return -ENOMEM;
}
+ platform_set_drvdata(dev, info);
info->pseudo_palette = info->par;
info->par = NULL;
@@ -499,12 +499,23 @@ err:
return err;
}
+static int vesafb_remove(struct platform_device *pdev)
+{
+ struct fb_info *info = platform_get_drvdata(pdev);
+
+ unregister_framebuffer(info);
+ framebuffer_release(info);
+
+ return 0;
+}
+
static struct platform_driver vesafb_driver = {
.driver = {
.name = "vesa-framebuffer",
.owner = THIS_MODULE,
},
.probe = vesafb_probe,
+ .remove = vesafb_remove,
};
module_platform_driver(vesafb_driver);
diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c
index 6ff1a91e9dfd..553cff2f3f4c 100644
--- a/drivers/video/xilinxfb.c
+++ b/drivers/video/xilinxfb.c
@@ -33,7 +33,6 @@
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/io.h>
-#include <linux/xilinxfb.h>
#include <linux/slab.h>
#ifdef CONFIG_PPC_DCR
@@ -84,6 +83,20 @@
#define PALETTE_ENTRIES_NO 16 /* passed to fb_alloc_cmap() */
+/* ML300/403 reference design framebuffer driver platform data struct */
+struct xilinxfb_platform_data {
+ u32 rotate_screen; /* Flag to rotate display 180 degrees */
+ u32 screen_height_mm; /* Physical dimensions of screen in mm */
+ u32 screen_width_mm;
+ u32 xres, yres; /* resolution of screen in pixels */
+ u32 xvirt, yvirt; /* resolution of memory buffer */
+
+ /* Physical address of framebuffer memory; If non-zero, driver
+ * will use provided memory address instead of allocating one from
+ * the consistent pool. */
+ u32 fb_phys;
+};
+
/*
* Default xilinxfb configuration
*/
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 34bdabaecbd6..25ebe8eecdb7 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -108,8 +108,7 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq)
sg_init_one(&sg, vb->pfns, sizeof(vb->pfns[0]) * vb->num_pfns);
/* We should always be able to add one buffer to an empty queue. */
- if (virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL) < 0)
- BUG();
+ virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL);
virtqueue_kick(vq);
/* When host has read buffer, this completes via balloon_ack */
@@ -258,8 +257,7 @@ static void stats_handle_request(struct virtio_balloon *vb)
if (!virtqueue_get_buf(vq, &len))
return;
sg_init_one(&sg, vb->stats, sizeof(vb->stats));
- if (virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL) < 0)
- BUG();
+ virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL);
virtqueue_kick(vq);
}
@@ -310,6 +308,12 @@ static int balloon(void *_vballoon)
else if (diff < 0)
leak_balloon(vb, -diff);
update_balloon_size(vb);
+
+ /*
+ * For large balloon changes, we could spend a lot of time
+ * and always have work to do. Be nice if preempt disabled.
+ */
+ cond_resched();
}
return 0;
}
@@ -338,7 +342,7 @@ static int init_vqs(struct virtio_balloon *vb)
/*
* Prime this virtqueue with one buffer so the hypervisor can
- * use it to signal us later.
+ * use it to signal us later (it can't be broken yet!).
*/
sg_init_one(&sg, vb->stats, sizeof vb->stats);
if (virtqueue_add_outbuf(vb->stats_vq, &sg, 1, vb, GFP_KERNEL)
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index a416f9b2a7f6..101db3faf5d4 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -333,10 +333,8 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
for (i = 0; i < nvectors; ++i)
vp_dev->msix_entries[i].entry = i;
- /* pci_enable_msix returns positive if we can't get this many. */
- err = pci_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries, nvectors);
- if (err > 0)
- err = -ENOSPC;
+ err = pci_enable_msix_exact(vp_dev->pci_dev,
+ vp_dev->msix_entries, nvectors);
if (err)
goto error;
vp_dev->msix_enabled = 1;
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 28b5338fff71..1e443629f76d 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/hrtimer.h>
+#include <linux/kmemleak.h>
#ifdef DEBUG
/* For development, we want to crash whenever the ring is screwed. */
@@ -203,6 +204,11 @@ static inline int virtqueue_add(struct virtqueue *_vq,
BUG_ON(data == NULL);
+ if (unlikely(vq->broken)) {
+ END_USE(vq);
+ return -EIO;
+ }
+
#ifdef DEBUG
{
ktime_t now = ktime_get();
@@ -309,7 +315,7 @@ add_head:
* Caller must ensure we don't call this with other virtqueue operations
* at the same time (except where noted).
*
- * Returns zero or a negative error (ie. ENOSPC, ENOMEM).
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
*/
int virtqueue_add_sgs(struct virtqueue *_vq,
struct scatterlist *sgs[],
@@ -347,7 +353,7 @@ EXPORT_SYMBOL_GPL(virtqueue_add_sgs);
* Caller must ensure we don't call this with other virtqueue operations
* at the same time (except where noted).
*
- * Returns zero or a negative error (ie. ENOSPC, ENOMEM).
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
*/
int virtqueue_add_outbuf(struct virtqueue *vq,
struct scatterlist sg[], unsigned int num,
@@ -369,7 +375,7 @@ EXPORT_SYMBOL_GPL(virtqueue_add_outbuf);
* Caller must ensure we don't call this with other virtqueue operations
* at the same time (except where noted).
*
- * Returns zero or a negative error (ie. ENOSPC, ENOMEM).
+ * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
*/
int virtqueue_add_inbuf(struct virtqueue *vq,
struct scatterlist sg[], unsigned int num,
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 0c6048d5c9a3..74ec8fc5cc03 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -301,7 +301,7 @@ config DAVINCI_WATCHDOG
config ORION_WATCHDOG
tristate "Orion watchdog"
- depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE
+ depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU
select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 0e6c0333f775..0ba1b7c99760 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -48,7 +48,7 @@
/* Module and version information */
#define DRV_NAME "iTCO_wdt"
-#define DRV_VERSION "1.10"
+#define DRV_VERSION "1.11"
/* Includes */
#include <linux/module.h> /* For module specific items */
@@ -92,9 +92,12 @@ static struct { /* this is private data for the iTCO_wdt device */
unsigned int iTCO_version;
struct resource *tco_res;
struct resource *smi_res;
- struct resource *gcs_res;
- /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/
- unsigned long __iomem *gcs;
+ /*
+ * NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2),
+ * or memory-mapped PMC register bit 4 (TCO version 3).
+ */
+ struct resource *gcs_pmc_res;
+ unsigned long __iomem *gcs_pmc;
/* the lock for io operations */
spinlock_t io_lock;
struct platform_device *dev;
@@ -125,11 +128,19 @@ MODULE_PARM_DESC(turn_SMI_watchdog_clear_off,
* Some TCO specific functions
*/
-static inline unsigned int seconds_to_ticks(int seconds)
+/*
+ * The iTCO v1 and v2's internal timer is stored as ticks which decrement
+ * every 0.6 seconds. v3's internal timer is stored as seconds (some
+ * datasheets incorrectly state 0.6 seconds).
+ */
+static inline unsigned int seconds_to_ticks(int secs)
{
- /* the internal timer is stored as ticks which decrement
- * every 0.6 seconds */
- return (seconds * 10) / 6;
+ return iTCO_wdt_private.iTCO_version == 3 ? secs : (secs * 10) / 6;
+}
+
+static inline unsigned int ticks_to_seconds(int ticks)
+{
+ return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10;
}
static void iTCO_wdt_set_NO_REBOOT_bit(void)
@@ -137,10 +148,14 @@ static void iTCO_wdt_set_NO_REBOOT_bit(void)
u32 val32;
/* Set the NO_REBOOT bit: this disables reboots */
- if (iTCO_wdt_private.iTCO_version == 2) {
- val32 = readl(iTCO_wdt_private.gcs);
+ if (iTCO_wdt_private.iTCO_version == 3) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
+ val32 |= 0x00000010;
+ writel(val32, iTCO_wdt_private.gcs_pmc);
+ } else if (iTCO_wdt_private.iTCO_version == 2) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
val32 |= 0x00000020;
- writel(val32, iTCO_wdt_private.gcs);
+ writel(val32, iTCO_wdt_private.gcs_pmc);
} else if (iTCO_wdt_private.iTCO_version == 1) {
pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
val32 |= 0x00000002;
@@ -154,12 +169,20 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void)
u32 val32;
/* Unset the NO_REBOOT bit: this enables reboots */
- if (iTCO_wdt_private.iTCO_version == 2) {
- val32 = readl(iTCO_wdt_private.gcs);
+ if (iTCO_wdt_private.iTCO_version == 3) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
+ val32 &= 0xffffffef;
+ writel(val32, iTCO_wdt_private.gcs_pmc);
+
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
+ if (val32 & 0x00000010)
+ ret = -EIO;
+ } else if (iTCO_wdt_private.iTCO_version == 2) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
val32 &= 0xffffffdf;
- writel(val32, iTCO_wdt_private.gcs);
+ writel(val32, iTCO_wdt_private.gcs_pmc);
- val32 = readl(iTCO_wdt_private.gcs);
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
if (val32 & 0x00000020)
ret = -EIO;
} else if (iTCO_wdt_private.iTCO_version == 1) {
@@ -192,7 +215,7 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev)
/* Force the timer to its reload value by writing to the TCO_RLD
register */
- if (iTCO_wdt_private.iTCO_version == 2)
+ if (iTCO_wdt_private.iTCO_version >= 2)
outw(0x01, TCO_RLD);
else if (iTCO_wdt_private.iTCO_version == 1)
outb(0x01, TCO_RLD);
@@ -240,9 +263,9 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout);
/* Reload the timer by writing to the TCO Timer Counter register */
- if (iTCO_wdt_private.iTCO_version == 2)
+ if (iTCO_wdt_private.iTCO_version >= 2) {
outw(0x01, TCO_RLD);
- else if (iTCO_wdt_private.iTCO_version == 1) {
+ } else if (iTCO_wdt_private.iTCO_version == 1) {
/* Reset the timeout status bit so that the timer
* needs to count down twice again before rebooting */
outw(0x0008, TCO1_STS); /* write 1 to clear bit */
@@ -270,14 +293,14 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
/* "Values of 0h-3h are ignored and should not be attempted" */
if (tmrval < 0x04)
return -EINVAL;
- if (((iTCO_wdt_private.iTCO_version == 2) && (tmrval > 0x3ff)) ||
+ if (((iTCO_wdt_private.iTCO_version >= 2) && (tmrval > 0x3ff)) ||
((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f)))
return -EINVAL;
iTCO_vendor_pre_set_heartbeat(tmrval);
/* Write new heartbeat to watchdog */
- if (iTCO_wdt_private.iTCO_version == 2) {
+ if (iTCO_wdt_private.iTCO_version >= 2) {
spin_lock(&iTCO_wdt_private.io_lock);
val16 = inw(TCOv2_TMR);
val16 &= 0xfc00;
@@ -312,13 +335,13 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
unsigned int time_left = 0;
/* read the TCO Timer */
- if (iTCO_wdt_private.iTCO_version == 2) {
+ if (iTCO_wdt_private.iTCO_version >= 2) {
spin_lock(&iTCO_wdt_private.io_lock);
val16 = inw(TCO_RLD);
val16 &= 0x3ff;
spin_unlock(&iTCO_wdt_private.io_lock);
- time_left = (val16 * 6) / 10;
+ time_left = ticks_to_seconds(val16);
} else if (iTCO_wdt_private.iTCO_version == 1) {
spin_lock(&iTCO_wdt_private.io_lock);
val8 = inb(TCO_RLD);
@@ -327,7 +350,7 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
val8 += (inb(TCOv1_TMR) & 0x3f);
spin_unlock(&iTCO_wdt_private.io_lock);
- time_left = (val8 * 6) / 10;
+ time_left = ticks_to_seconds(val8);
}
return time_left;
}
@@ -376,16 +399,16 @@ static void iTCO_wdt_cleanup(void)
resource_size(iTCO_wdt_private.tco_res));
release_region(iTCO_wdt_private.smi_res->start,
resource_size(iTCO_wdt_private.smi_res));
- if (iTCO_wdt_private.iTCO_version == 2) {
- iounmap(iTCO_wdt_private.gcs);
- release_mem_region(iTCO_wdt_private.gcs_res->start,
- resource_size(iTCO_wdt_private.gcs_res));
+ if (iTCO_wdt_private.iTCO_version >= 2) {
+ iounmap(iTCO_wdt_private.gcs_pmc);
+ release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
+ resource_size(iTCO_wdt_private.gcs_pmc_res));
}
iTCO_wdt_private.tco_res = NULL;
iTCO_wdt_private.smi_res = NULL;
- iTCO_wdt_private.gcs_res = NULL;
- iTCO_wdt_private.gcs = NULL;
+ iTCO_wdt_private.gcs_pmc_res = NULL;
+ iTCO_wdt_private.gcs_pmc = NULL;
}
static int iTCO_wdt_probe(struct platform_device *dev)
@@ -414,27 +437,27 @@ static int iTCO_wdt_probe(struct platform_device *dev)
iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent);
/*
- * Get the Memory-Mapped GCS register, we need it for the
- * NO_REBOOT flag (TCO v2).
+ * Get the Memory-Mapped GCS or PMC register, we need it for the
+ * NO_REBOOT flag (TCO v2 and v3).
*/
- if (iTCO_wdt_private.iTCO_version == 2) {
- iTCO_wdt_private.gcs_res = platform_get_resource(dev,
+ if (iTCO_wdt_private.iTCO_version >= 2) {
+ iTCO_wdt_private.gcs_pmc_res = platform_get_resource(dev,
IORESOURCE_MEM,
- ICH_RES_MEM_GCS);
+ ICH_RES_MEM_GCS_PMC);
- if (!iTCO_wdt_private.gcs_res)
+ if (!iTCO_wdt_private.gcs_pmc_res)
goto out;
- if (!request_mem_region(iTCO_wdt_private.gcs_res->start,
- resource_size(iTCO_wdt_private.gcs_res), dev->name)) {
+ if (!request_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
+ resource_size(iTCO_wdt_private.gcs_pmc_res), dev->name)) {
ret = -EBUSY;
goto out;
}
- iTCO_wdt_private.gcs = ioremap(iTCO_wdt_private.gcs_res->start,
- resource_size(iTCO_wdt_private.gcs_res));
- if (!iTCO_wdt_private.gcs) {
+ iTCO_wdt_private.gcs_pmc = ioremap(iTCO_wdt_private.gcs_pmc_res->start,
+ resource_size(iTCO_wdt_private.gcs_pmc_res));
+ if (!iTCO_wdt_private.gcs_pmc) {
ret = -EIO;
- goto unreg_gcs;
+ goto unreg_gcs_pmc;
}
}
@@ -442,7 +465,7 @@ static int iTCO_wdt_probe(struct platform_device *dev)
if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
ret = -ENODEV; /* Cannot reset NO_REBOOT bit */
- goto unmap_gcs;
+ goto unmap_gcs_pmc;
}
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
@@ -454,7 +477,7 @@ static int iTCO_wdt_probe(struct platform_device *dev)
pr_err("I/O address 0x%04llx already in use, device disabled\n",
(u64)SMI_EN);
ret = -EBUSY;
- goto unmap_gcs;
+ goto unmap_gcs_pmc;
}
if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) {
/*
@@ -478,9 +501,13 @@ static int iTCO_wdt_probe(struct platform_device *dev)
ich_info->name, ich_info->iTCO_version, (u64)TCOBASE);
/* Clear out the (probably old) status */
- outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
- outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
- outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */
+ if (iTCO_wdt_private.iTCO_version == 3) {
+ outl(0x20008, TCO1_STS);
+ } else {
+ outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
+ outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
+ outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */
+ }
iTCO_wdt_watchdog_dev.bootstatus = 0;
iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT;
@@ -515,18 +542,18 @@ unreg_tco:
unreg_smi:
release_region(iTCO_wdt_private.smi_res->start,
resource_size(iTCO_wdt_private.smi_res));
-unmap_gcs:
- if (iTCO_wdt_private.iTCO_version == 2)
- iounmap(iTCO_wdt_private.gcs);
-unreg_gcs:
- if (iTCO_wdt_private.iTCO_version == 2)
- release_mem_region(iTCO_wdt_private.gcs_res->start,
- resource_size(iTCO_wdt_private.gcs_res));
+unmap_gcs_pmc:
+ if (iTCO_wdt_private.iTCO_version >= 2)
+ iounmap(iTCO_wdt_private.gcs_pmc);
+unreg_gcs_pmc:
+ if (iTCO_wdt_private.iTCO_version >= 2)
+ release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
+ resource_size(iTCO_wdt_private.gcs_pmc_res));
out:
iTCO_wdt_private.tco_res = NULL;
iTCO_wdt_private.smi_res = NULL;
- iTCO_wdt_private.gcs_res = NULL;
- iTCO_wdt_private.gcs = NULL;
+ iTCO_wdt_private.gcs_pmc_res = NULL;
+ iTCO_wdt_private.gcs_pmc = NULL;
return ret;
}
diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c
index 461208831428..4baf2d788920 100644
--- a/drivers/watchdog/octeon-wdt-main.c
+++ b/drivers/watchdog/octeon-wdt-main.c
@@ -708,10 +708,13 @@ static int __init octeon_wdt_init(void)
cpumask_clear(&irq_enabled_cpus);
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
octeon_wdt_setup_interrupt(cpu);
- register_hotcpu_notifier(&octeon_wdt_cpu_notifier);
+ __register_hotcpu_notifier(&octeon_wdt_cpu_notifier);
+ cpu_notifier_register_done();
+
out:
return ret;
}
@@ -725,7 +728,8 @@ static void __exit octeon_wdt_cleanup(void)
misc_deregister(&octeon_wdt_miscdev);
- unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier);
+ cpu_notifier_register_begin();
+ __unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier);
for_each_online_cpu(cpu) {
int core = cpu2core(cpu);
@@ -734,6 +738,9 @@ static void __exit octeon_wdt_cleanup(void)
/* Free the interrupt handler */
free_irq(OCTEON_IRQ_WDOG0 + core, octeon_wdt_poke_irq);
}
+
+ cpu_notifier_register_done();
+
/*
* Disable the boot-bus memory, the code it points to is soon
* to go missing.
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index 498163497c1c..9b3c41d18703 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -18,101 +18,204 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/of.h>
-#include <mach/bridge-regs.h>
+#include <linux/of_device.h>
+
+/* RSTOUT mask register physical address for Orion5x, Kirkwood and Dove */
+#define ORION_RSTOUT_MASK_OFFSET 0x20108
+
+/* Internal registers can be configured at any 1 MiB aligned address */
+#define INTERNAL_REGS_MASK ~(SZ_1M - 1)
/*
* Watchdog timer block registers.
*/
#define TIMER_CTRL 0x0000
-#define WDT_EN 0x0010
-#define WDT_VAL 0x0024
+#define TIMER_A370_STATUS 0x04
#define WDT_MAX_CYCLE_COUNT 0xffffffff
-#define WDT_IN_USE 0
-#define WDT_OK_TO_CLOSE 1
-#define WDT_RESET_OUT_EN BIT(1)
-#define WDT_INT_REQ BIT(3)
+#define WDT_A370_RATIO_MASK(v) ((v) << 16)
+#define WDT_A370_RATIO_SHIFT 5
+#define WDT_A370_RATIO (1 << WDT_A370_RATIO_SHIFT)
+
+#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
+#define WDT_A370_EXPIRED BIT(31)
static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = -1; /* module parameter (seconds) */
-static unsigned int wdt_max_duration; /* (seconds) */
-static struct clk *clk;
-static unsigned int wdt_tclk;
-static void __iomem *wdt_reg;
-static DEFINE_SPINLOCK(wdt_lock);
-static int orion_wdt_ping(struct watchdog_device *wdt_dev)
+struct orion_watchdog;
+
+struct orion_watchdog_data {
+ int wdt_counter_offset;
+ int wdt_enable_bit;
+ int rstout_enable_bit;
+ int (*clock_init)(struct platform_device *,
+ struct orion_watchdog *);
+ int (*start)(struct watchdog_device *);
+};
+
+struct orion_watchdog {
+ struct watchdog_device wdt;
+ void __iomem *reg;
+ void __iomem *rstout;
+ unsigned long clk_rate;
+ struct clk *clk;
+ const struct orion_watchdog_data *data;
+};
+
+static int orion_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
{
- spin_lock(&wdt_lock);
+ int ret;
- /* Reload watchdog duration */
- writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL);
+ dev->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+ ret = clk_prepare_enable(dev->clk);
+ if (ret) {
+ clk_put(dev->clk);
+ return ret;
+ }
- spin_unlock(&wdt_lock);
+ dev->clk_rate = clk_get_rate(dev->clk);
return 0;
}
-static int orion_wdt_start(struct watchdog_device *wdt_dev)
+static int armada370_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
{
- u32 reg;
+ int ret;
- spin_lock(&wdt_lock);
+ dev->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+ ret = clk_prepare_enable(dev->clk);
+ if (ret) {
+ clk_put(dev->clk);
+ return ret;
+ }
+
+ /* Setup watchdog input clock */
+ atomic_io_modify(dev->reg + TIMER_CTRL,
+ WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT),
+ WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT));
+
+ dev->clk_rate = clk_get_rate(dev->clk) / WDT_A370_RATIO;
+ return 0;
+}
+
+static int armadaxp_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
+{
+ int ret;
+
+ dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed");
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+ ret = clk_prepare_enable(dev->clk);
+ if (ret) {
+ clk_put(dev->clk);
+ return ret;
+ }
+
+ /* Enable the fixed watchdog clock input */
+ atomic_io_modify(dev->reg + TIMER_CTRL,
+ WDT_AXP_FIXED_ENABLE_BIT,
+ WDT_AXP_FIXED_ENABLE_BIT);
+
+ dev->clk_rate = clk_get_rate(dev->clk);
+ return 0;
+}
+
+static int orion_wdt_ping(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+ /* Reload watchdog duration */
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
+ return 0;
+}
+
+static int armada370_start(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
/* Set watchdog duration */
- writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL);
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
- /* Clear watchdog timer interrupt */
- writel(~WDT_INT_REQ, BRIDGE_CAUSE);
+ /* Clear the watchdog expiration bit */
+ atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
/* Enable watchdog timer */
- reg = readl(wdt_reg + TIMER_CTRL);
- reg |= WDT_EN;
- writel(reg, wdt_reg + TIMER_CTRL);
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
+ dev->data->wdt_enable_bit);
+
+ atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
+ dev->data->rstout_enable_bit);
+ return 0;
+}
+
+static int orion_start(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ /* Set watchdog duration */
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
+
+ /* Enable watchdog timer */
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
+ dev->data->wdt_enable_bit);
/* Enable reset on watchdog */
- reg = readl(RSTOUTn_MASK);
- reg |= WDT_RESET_OUT_EN;
- writel(reg, RSTOUTn_MASK);
+ atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
+ dev->data->rstout_enable_bit);
- spin_unlock(&wdt_lock);
return 0;
}
-static int orion_wdt_stop(struct watchdog_device *wdt_dev)
+static int orion_wdt_start(struct watchdog_device *wdt_dev)
{
- u32 reg;
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
- spin_lock(&wdt_lock);
+ /* There are some per-SoC quirks to handle */
+ return dev->data->start(wdt_dev);
+}
+
+static int orion_wdt_stop(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
/* Disable reset on watchdog */
- reg = readl(RSTOUTn_MASK);
- reg &= ~WDT_RESET_OUT_EN;
- writel(reg, RSTOUTn_MASK);
+ atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, 0);
/* Disable watchdog timer */
- reg = readl(wdt_reg + TIMER_CTRL);
- reg &= ~WDT_EN;
- writel(reg, wdt_reg + TIMER_CTRL);
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
- spin_unlock(&wdt_lock);
return 0;
}
-static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
+static int orion_wdt_enabled(struct orion_watchdog *dev)
{
- unsigned int time_left;
+ bool enabled, running;
+
+ enabled = readl(dev->rstout) & dev->data->rstout_enable_bit;
+ running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit;
- spin_lock(&wdt_lock);
- time_left = readl(wdt_reg + WDT_VAL) / wdt_tclk;
- spin_unlock(&wdt_lock);
+ return enabled && running;
+}
- return time_left;
+static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+ return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate;
}
static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev,
@@ -136,68 +239,188 @@ static const struct watchdog_ops orion_wdt_ops = {
.get_timeleft = orion_wdt_get_timeleft,
};
-static struct watchdog_device orion_wdt = {
- .info = &orion_wdt_info,
- .ops = &orion_wdt_ops,
- .min_timeout = 1,
+static irqreturn_t orion_wdt_irq(int irq, void *devid)
+{
+ panic("Watchdog Timeout");
+ return IRQ_HANDLED;
+}
+
+/*
+ * The original devicetree binding for this driver specified only
+ * one memory resource, so in order to keep DT backwards compatibility
+ * we try to fallback to a hardcoded register address, if the resource
+ * is missing from the devicetree.
+ */
+static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev,
+ phys_addr_t internal_regs)
+{
+ struct resource *res;
+ phys_addr_t rstout;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res)
+ return devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+
+ /* This workaround works only for "orion-wdt", DT-enabled */
+ if (!of_device_is_compatible(pdev->dev.of_node, "marvell,orion-wdt"))
+ return NULL;
+
+ rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET;
+
+ WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout);
+ return devm_ioremap(&pdev->dev, rstout, 0x4);
+}
+
+static const struct orion_watchdog_data orion_data = {
+ .rstout_enable_bit = BIT(1),
+ .wdt_enable_bit = BIT(4),
+ .wdt_counter_offset = 0x24,
+ .clock_init = orion_wdt_clock_init,
+ .start = orion_start,
+};
+
+static const struct orion_watchdog_data armada370_data = {
+ .rstout_enable_bit = BIT(8),
+ .wdt_enable_bit = BIT(8),
+ .wdt_counter_offset = 0x34,
+ .clock_init = armada370_wdt_clock_init,
+ .start = armada370_start,
};
+static const struct orion_watchdog_data armadaxp_data = {
+ .rstout_enable_bit = BIT(8),
+ .wdt_enable_bit = BIT(8),
+ .wdt_counter_offset = 0x34,
+ .clock_init = armadaxp_wdt_clock_init,
+ .start = armada370_start,
+};
+
+static const struct of_device_id orion_wdt_of_match_table[] = {
+ {
+ .compatible = "marvell,orion-wdt",
+ .data = &orion_data,
+ },
+ {
+ .compatible = "marvell,armada-370-wdt",
+ .data = &armada370_data,
+ },
+ {
+ .compatible = "marvell,armada-xp-wdt",
+ .data = &armadaxp_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
+
static int orion_wdt_probe(struct platform_device *pdev)
{
+ struct orion_watchdog *dev;
+ const struct of_device_id *match;
+ unsigned int wdt_max_duration; /* (seconds) */
struct resource *res;
- int ret;
+ int ret, irq;
- clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "Orion Watchdog missing clock\n");
- return -ENODEV;
- }
- clk_prepare_enable(clk);
- wdt_tclk = clk_get_rate(clk);
+ dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog),
+ GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ match = of_match_device(orion_wdt_of_match_table, &pdev->dev);
+ if (!match)
+ /* Default legacy match */
+ match = &orion_wdt_of_match_table[0];
+
+ dev->wdt.info = &orion_wdt_info;
+ dev->wdt.ops = &orion_wdt_ops;
+ dev->wdt.min_timeout = 1;
+ dev->data = match->data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
- wdt_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!wdt_reg)
- return -ENOMEM;
- wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
+ dev->reg = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!dev->reg)
+ return -ENOMEM;
- orion_wdt.timeout = wdt_max_duration;
- orion_wdt.max_timeout = wdt_max_duration;
- watchdog_init_timeout(&orion_wdt, heartbeat, &pdev->dev);
+ dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start &
+ INTERNAL_REGS_MASK);
+ if (!dev->rstout)
+ return -ENODEV;
- watchdog_set_nowayout(&orion_wdt, nowayout);
- ret = watchdog_register_device(&orion_wdt);
+ ret = dev->data->clock_init(pdev, dev);
if (ret) {
- clk_disable_unprepare(clk);
+ dev_err(&pdev->dev, "cannot initialize clock\n");
return ret;
}
+ wdt_max_duration = WDT_MAX_CYCLE_COUNT / dev->clk_rate;
+
+ dev->wdt.timeout = wdt_max_duration;
+ dev->wdt.max_timeout = wdt_max_duration;
+ watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
+
+ platform_set_drvdata(pdev, &dev->wdt);
+ watchdog_set_drvdata(&dev->wdt, dev);
+
+ /*
+ * Let's make sure the watchdog is fully stopped, unless it's
+ * explicitly enabled. This may be the case if the module was
+ * removed and re-insterted, or if the bootloader explicitly
+ * set a running watchdog before booting the kernel.
+ */
+ if (!orion_wdt_enabled(dev))
+ orion_wdt_stop(&dev->wdt);
+
+ /* Request the IRQ only after the watchdog is disabled */
+ irq = platform_get_irq(pdev, 0);
+ if (irq > 0) {
+ /*
+ * Not all supported platforms specify an interrupt for the
+ * watchdog, so let's make it optional.
+ */
+ ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0,
+ pdev->name, dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ goto disable_clk;
+ }
+ }
+
+ watchdog_set_nowayout(&dev->wdt, nowayout);
+ ret = watchdog_register_device(&dev->wdt);
+ if (ret)
+ goto disable_clk;
+
pr_info("Initial timeout %d sec%s\n",
- orion_wdt.timeout, nowayout ? ", nowayout" : "");
+ dev->wdt.timeout, nowayout ? ", nowayout" : "");
return 0;
+
+disable_clk:
+ clk_disable_unprepare(dev->clk);
+ clk_put(dev->clk);
+ return ret;
}
static int orion_wdt_remove(struct platform_device *pdev)
{
- watchdog_unregister_device(&orion_wdt);
- clk_disable_unprepare(clk);
+ struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ watchdog_unregister_device(wdt_dev);
+ clk_disable_unprepare(dev->clk);
+ clk_put(dev->clk);
return 0;
}
static void orion_wdt_shutdown(struct platform_device *pdev)
{
- orion_wdt_stop(&orion_wdt);
+ struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
+ orion_wdt_stop(wdt_dev);
}
-static const struct of_device_id orion_wdt_of_match_table[] = {
- { .compatible = "marvell,orion-wdt", },
- {},
-};
-MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
-
static struct platform_driver orion_wdt_driver = {
.probe = orion_wdt_probe,
.remove = orion_wdt_remove,
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index 61a6ac8fa8fc..b7a506f2bb14 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -604,19 +604,29 @@ static void __init balloon_add_region(unsigned long start_pfn,
}
}
+static int alloc_balloon_scratch_page(int cpu)
+{
+ if (per_cpu(balloon_scratch_page, cpu) != NULL)
+ return 0;
+
+ per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL);
+ if (per_cpu(balloon_scratch_page, cpu) == NULL) {
+ pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+
static int balloon_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
int cpu = (long)hcpu;
switch (action) {
case CPU_UP_PREPARE:
- if (per_cpu(balloon_scratch_page, cpu) != NULL)
- break;
- per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL);
- if (per_cpu(balloon_scratch_page, cpu) == NULL) {
- pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu);
+ if (alloc_balloon_scratch_page(cpu))
return NOTIFY_BAD;
- }
break;
default:
break;
@@ -636,15 +646,17 @@ static int __init balloon_init(void)
return -ENODEV;
if (!xen_feature(XENFEAT_auto_translated_physmap)) {
- for_each_online_cpu(cpu)
- {
- per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL);
- if (per_cpu(balloon_scratch_page, cpu) == NULL) {
- pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu);
+ register_cpu_notifier(&balloon_cpu_notifier);
+
+ get_online_cpus();
+ for_each_online_cpu(cpu) {
+ if (alloc_balloon_scratch_page(cpu)) {
+ put_online_cpus();
+ unregister_cpu_notifier(&balloon_cpu_notifier);
return -ENOMEM;
}
}
- register_cpu_notifier(&balloon_cpu_notifier);
+ put_online_cpus();
}
pr_info("Initialising balloon driver\n");
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index c3458f58de90..d5a3de88ac59 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -388,10 +388,10 @@ static void xen_irq_init(unsigned irq)
list_add_tail(&info->list, &xen_irq_list_head);
}
-static int __must_check xen_allocate_irq_dynamic(void)
+static int __must_check xen_allocate_irqs_dynamic(int nvec)
{
int first = 0;
- int irq;
+ int i, irq;
#ifdef CONFIG_X86_IO_APIC
/*
@@ -405,14 +405,22 @@ static int __must_check xen_allocate_irq_dynamic(void)
first = get_nr_irqs_gsi();
#endif
- irq = irq_alloc_desc_from(first, -1);
+ irq = irq_alloc_descs_from(first, nvec, -1);
- if (irq >= 0)
- xen_irq_init(irq);
+ if (irq >= 0) {
+ for (i = 0; i < nvec; i++)
+ xen_irq_init(irq + i);
+ }
return irq;
}
+static inline int __must_check xen_allocate_irq_dynamic(void)
+{
+
+ return xen_allocate_irqs_dynamic(1);
+}
+
static int __must_check xen_allocate_irq_gsi(unsigned gsi)
{
int irq;
@@ -466,9 +474,6 @@ static void xen_evtchn_close(unsigned int port)
close.port = port;
if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0)
BUG();
-
- /* Closed ports are implicitly re-bound to VCPU0. */
- bind_evtchn_to_cpu(port, 0);
}
static void pirq_query_unmask(int irq)
@@ -730,22 +735,25 @@ int xen_allocate_pirq_msi(struct pci_dev *dev, struct msi_desc *msidesc)
}
int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc,
- int pirq, const char *name, domid_t domid)
+ int pirq, int nvec, const char *name, domid_t domid)
{
- int irq, ret;
+ int i, irq, ret;
mutex_lock(&irq_mapping_update_lock);
- irq = xen_allocate_irq_dynamic();
+ irq = xen_allocate_irqs_dynamic(nvec);
if (irq < 0)
goto out;
- irq_set_chip_and_handler_name(irq, &xen_pirq_chip, handle_edge_irq,
- name);
+ for (i = 0; i < nvec; i++) {
+ irq_set_chip_and_handler_name(irq + i, &xen_pirq_chip, handle_edge_irq, name);
+
+ ret = xen_irq_info_pirq_setup(irq + i, 0, pirq + i, 0, domid,
+ i == 0 ? 0 : PIRQ_MSI_GROUP);
+ if (ret < 0)
+ goto error_irq;
+ }
- ret = xen_irq_info_pirq_setup(irq, 0, pirq, 0, domid, 0);
- if (ret < 0)
- goto error_irq;
ret = irq_set_msi_desc(irq, msidesc);
if (ret < 0)
goto error_irq;
@@ -753,7 +761,8 @@ out:
mutex_unlock(&irq_mapping_update_lock);
return irq;
error_irq:
- __unbind_from_irq(irq);
+ for (; i >= 0; i--)
+ __unbind_from_irq(irq + i);
mutex_unlock(&irq_mapping_update_lock);
return ret;
}
@@ -767,7 +776,12 @@ int xen_destroy_irq(int irq)
mutex_lock(&irq_mapping_update_lock);
- if (xen_initial_domain()) {
+ /*
+ * If trying to remove a vector in a MSI group different
+ * than the first one skip the PIRQ unmap unless this vector
+ * is the first one in the group.
+ */
+ if (xen_initial_domain() && !(info->u.pirq.flags & PIRQ_MSI_GROUP)) {
unmap_irq.pirq = info->u.pirq.pirq;
unmap_irq.domid = info->u.pirq.domid;
rc = HYPERVISOR_physdev_op(PHYSDEVOP_unmap_pirq, &unmap_irq);
@@ -1329,26 +1343,6 @@ static int set_affinity_irq(struct irq_data *data, const struct cpumask *dest,
return rebind_irq_to_cpu(data->irq, tcpu);
}
-static int retrigger_evtchn(int evtchn)
-{
- int masked;
-
- if (!VALID_EVTCHN(evtchn))
- return 0;
-
- masked = test_and_set_mask(evtchn);
- set_evtchn(evtchn);
- if (!masked)
- unmask_evtchn(evtchn);
-
- return 1;
-}
-
-int resend_irq_on_evtchn(unsigned int irq)
-{
- return retrigger_evtchn(evtchn_from_irq(irq));
-}
-
static void enable_dynirq(struct irq_data *data)
{
int evtchn = evtchn_from_irq(data->irq);
@@ -1383,7 +1377,18 @@ static void mask_ack_dynirq(struct irq_data *data)
static int retrigger_dynirq(struct irq_data *data)
{
- return retrigger_evtchn(evtchn_from_irq(data->irq));
+ unsigned int evtchn = evtchn_from_irq(data->irq);
+ int masked;
+
+ if (!VALID_EVTCHN(evtchn))
+ return 0;
+
+ masked = test_and_set_mask(evtchn);
+ set_evtchn(evtchn);
+ if (!masked)
+ unmask_evtchn(evtchn);
+
+ return 1;
}
static void restore_pirqs(void)
diff --git a/drivers/xen/events/events_internal.h b/drivers/xen/events/events_internal.h
index 677f41a0fff9..50c2050a1e32 100644
--- a/drivers/xen/events/events_internal.h
+++ b/drivers/xen/events/events_internal.h
@@ -53,6 +53,7 @@ struct irq_info {
#define PIRQ_NEEDS_EOI (1 << 0)
#define PIRQ_SHAREABLE (1 << 1)
+#define PIRQ_MSI_GROUP (1 << 2)
struct evtchn_ops {
unsigned (*max_channels)(void);
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index b84e3ab839aa..6d325bda76da 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -933,9 +933,6 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
struct page **pages, unsigned int count)
{
int i, ret;
- bool lazy = false;
- pte_t *pte;
- unsigned long mfn;
ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map_ops, count);
if (ret)
@@ -947,45 +944,7 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
gnttab_retry_eagain_gop(GNTTABOP_map_grant_ref, map_ops + i,
&map_ops[i].status, __func__);
- /* this is basically a nop on x86 */
- if (xen_feature(XENFEAT_auto_translated_physmap)) {
- for (i = 0; i < count; i++) {
- if (map_ops[i].status)
- continue;
- set_phys_to_machine(map_ops[i].host_addr >> PAGE_SHIFT,
- map_ops[i].dev_bus_addr >> PAGE_SHIFT);
- }
- return ret;
- }
-
- if (!in_interrupt() && paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) {
- arch_enter_lazy_mmu_mode();
- lazy = true;
- }
-
- for (i = 0; i < count; i++) {
- /* Do not add to override if the map failed. */
- if (map_ops[i].status)
- continue;
-
- if (map_ops[i].flags & GNTMAP_contains_pte) {
- pte = (pte_t *) (mfn_to_virt(PFN_DOWN(map_ops[i].host_addr)) +
- (map_ops[i].host_addr & ~PAGE_MASK));
- mfn = pte_mfn(*pte);
- } else {
- mfn = PFN_DOWN(map_ops[i].dev_bus_addr);
- }
- ret = m2p_add_override(mfn, pages[i], kmap_ops ?
- &kmap_ops[i] : NULL);
- if (ret)
- goto out;
- }
-
- out:
- if (lazy)
- arch_leave_lazy_mmu_mode();
-
- return ret;
+ return set_foreign_p2m_mapping(map_ops, kmap_ops, pages, count);
}
EXPORT_SYMBOL_GPL(gnttab_map_refs);
@@ -993,39 +952,13 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
struct gnttab_map_grant_ref *kmap_ops,
struct page **pages, unsigned int count)
{
- int i, ret;
- bool lazy = false;
+ int ret;
ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap_ops, count);
if (ret)
return ret;
- /* this is basically a nop on x86 */
- if (xen_feature(XENFEAT_auto_translated_physmap)) {
- for (i = 0; i < count; i++) {
- set_phys_to_machine(unmap_ops[i].host_addr >> PAGE_SHIFT,
- INVALID_P2M_ENTRY);
- }
- return ret;
- }
-
- if (!in_interrupt() && paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) {
- arch_enter_lazy_mmu_mode();
- lazy = true;
- }
-
- for (i = 0; i < count; i++) {
- ret = m2p_remove_override(pages[i], kmap_ops ?
- &kmap_ops[i] : NULL);
- if (ret)
- goto out;
- }
-
- out:
- if (lazy)
- arch_leave_lazy_mmu_mode();
-
- return ret;
+ return clear_foreign_p2m_mapping(unmap_ops, kmap_ops, pages, count);
}
EXPORT_SYMBOL_GPL(gnttab_unmap_refs);
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c
index 624e8dc24532..fc6c94c0b436 100644
--- a/drivers/xen/manage.c
+++ b/drivers/xen/manage.c
@@ -46,6 +46,20 @@ struct suspend_info {
void (*post)(int cancelled);
};
+static RAW_NOTIFIER_HEAD(xen_resume_notifier);
+
+void xen_resume_notifier_register(struct notifier_block *nb)
+{
+ raw_notifier_chain_register(&xen_resume_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(xen_resume_notifier_register);
+
+void xen_resume_notifier_unregister(struct notifier_block *nb)
+{
+ raw_notifier_chain_unregister(&xen_resume_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(xen_resume_notifier_unregister);
+
#ifdef CONFIG_HIBERNATE_CALLBACKS
static void xen_hvm_post_suspend(int cancelled)
{
@@ -152,6 +166,8 @@ static void do_suspend(void)
err = stop_machine(xen_suspend, &si, cpumask_of(0));
+ raw_notifier_call_chain(&xen_resume_notifier, 0, NULL);
+
dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
if (err) {
diff --git a/drivers/xen/pcpu.c b/drivers/xen/pcpu.c
index 79e1dff7ed4f..0aac403d53fd 100644
--- a/drivers/xen/pcpu.c
+++ b/drivers/xen/pcpu.c
@@ -40,6 +40,7 @@
#include <linux/capability.h>
#include <xen/xen.h>
+#include <xen/acpi.h>
#include <xen/xenbus.h>
#include <xen/events.h>
#include <xen/interface/platform.h>
diff --git a/drivers/xen/platform-pci.c b/drivers/xen/platform-pci.c
index a1361c312c06..3454973dc3bb 100644
--- a/drivers/xen/platform-pci.c
+++ b/drivers/xen/platform-pci.c
@@ -45,7 +45,7 @@ static unsigned long platform_mmio_alloc;
static unsigned long platform_mmiolen;
static uint64_t callback_via;
-unsigned long alloc_xen_mmio(unsigned long len)
+static unsigned long alloc_xen_mmio(unsigned long len)
{
unsigned long addr;
diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c
index 7231859119f1..82358d14ecf1 100644
--- a/drivers/xen/xen-acpi-processor.c
+++ b/drivers/xen/xen-acpi-processor.c
@@ -27,10 +27,10 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/syscore_ops.h>
#include <linux/acpi.h>
#include <acpi/processor.h>
#include <xen/xen.h>
+#include <xen/xen-ops.h>
#include <xen/interface/platform.h>
#include <asm/xen/hypercall.h>
@@ -495,14 +495,15 @@ static int xen_upload_processor_pm_data(void)
return rc;
}
-static void xen_acpi_processor_resume(void)
+static int xen_acpi_processor_resume(struct notifier_block *nb,
+ unsigned long action, void *data)
{
bitmap_zero(acpi_ids_done, nr_acpi_bits);
- xen_upload_processor_pm_data();
+ return xen_upload_processor_pm_data();
}
-static struct syscore_ops xap_syscore_ops = {
- .resume = xen_acpi_processor_resume,
+struct notifier_block xen_acpi_processor_resume_nb = {
+ .notifier_call = xen_acpi_processor_resume,
};
static int __init xen_acpi_processor_init(void)
@@ -555,7 +556,7 @@ static int __init xen_acpi_processor_init(void)
if (rc)
goto err_unregister;
- register_syscore_ops(&xap_syscore_ops);
+ xen_resume_notifier_register(&xen_acpi_processor_resume_nb);
return 0;
err_unregister:
@@ -574,7 +575,7 @@ static void __exit xen_acpi_processor_exit(void)
{
int i;
- unregister_syscore_ops(&xap_syscore_ops);
+ xen_resume_notifier_unregister(&xen_acpi_processor_resume_nb);
kfree(acpi_ids_done);
kfree(acpi_id_present);
kfree(acpi_id_cst_present);
diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c
index 64eb0cd8b8af..929dd46bb40c 100644
--- a/drivers/xen/xen-pciback/pciback_ops.c
+++ b/drivers/xen/xen-pciback/pciback_ops.c
@@ -213,8 +213,7 @@ int xen_pcibk_enable_msix(struct xen_pcibk_device *pdev,
entries[i].vector = op->msix_entries[i].vector;
}
- result = pci_enable_msix(dev, entries, op->value);
-
+ result = pci_enable_msix_exact(dev, entries, op->value);
if (result == 0) {
for (i = 0; i < op->value; i++) {
op->msix_entries[i].entry = entries[i].entry;
diff --git a/drivers/xen/xen-selfballoon.c b/drivers/xen/xen-selfballoon.c
index 745ad79c1d8e..3b2bffde534f 100644
--- a/drivers/xen/xen-selfballoon.c
+++ b/drivers/xen/xen-selfballoon.c
@@ -170,6 +170,7 @@ static void frontswap_selfshrink(void)
tgt_frontswap_pages = cur_frontswap_pages -
(cur_frontswap_pages / frontswap_hysteresis);
frontswap_shrink(tgt_frontswap_pages);
+ frontswap_inertia_counter = frontswap_inertia;
}
#endif /* CONFIG_FRONTSWAP */
diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c
index 01d59e66565d..439c9dca9eee 100644
--- a/drivers/xen/xenbus/xenbus_client.c
+++ b/drivers/xen/xenbus/xenbus_client.c
@@ -401,33 +401,6 @@ EXPORT_SYMBOL_GPL(xenbus_alloc_evtchn);
/**
- * Bind to an existing interdomain event channel in another domain. Returns 0
- * on success and stores the local port in *port. On error, returns -errno,
- * switches the device to XenbusStateClosing, and saves the error in XenStore.
- */
-int xenbus_bind_evtchn(struct xenbus_device *dev, int remote_port, int *port)
-{
- struct evtchn_bind_interdomain bind_interdomain;
- int err;
-
- bind_interdomain.remote_dom = dev->otherend_id;
- bind_interdomain.remote_port = remote_port;
-
- err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
- &bind_interdomain);
- if (err)
- xenbus_dev_fatal(dev, err,
- "binding to event channel %d from domain %d",
- remote_port, dev->otherend_id);
- else
- *port = bind_interdomain.local_port;
-
- return err;
-}
-EXPORT_SYMBOL_GPL(xenbus_bind_evtchn);
-
-
-/**
* Free an existing event channel. Returns 0 on success or -errno on error.
*/
int xenbus_free_evtchn(struct xenbus_device *dev, int port)
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index bb7991c7e5c7..53161ec058a7 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -451,7 +451,7 @@ void v9fs_evict_inode(struct inode *inode)
{
struct v9fs_inode *v9inode = V9FS_I(inode);
- truncate_inode_pages(inode->i_mapping, 0);
+ truncate_inode_pages_final(inode->i_mapping);
clear_inode(inode);
filemap_fdatawrite(inode->i_mapping);
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
index 7b3003cb6f1b..952aeb048349 100644
--- a/fs/adfs/super.c
+++ b/fs/adfs/super.c
@@ -212,6 +212,7 @@ static int parse_options(struct super_block *sb, char *options)
static int adfs_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
*flags |= MS_NODIRATIME;
return parse_options(sb, data);
}
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
index 0e092d08680e..96df91e8c334 100644
--- a/fs/affs/inode.c
+++ b/fs/affs/inode.c
@@ -259,7 +259,7 @@ affs_evict_inode(struct inode *inode)
{
unsigned long cache_page;
pr_debug("AFFS: evict_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
if (!inode->i_nlink) {
inode->i_size = 0;
diff --git a/fs/affs/super.c b/fs/affs/super.c
index d098731b82ff..307453086c3f 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -530,6 +530,7 @@ affs_remount(struct super_block *sb, int *flags, char *data)
pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data);
+ sync_filesystem(sb);
*flags |= MS_NODIRATIME;
memcpy(volume, sbi->s_volume, 32);
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index ce25d755b7aa..294671288449 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -422,7 +422,7 @@ void afs_evict_inode(struct inode *inode)
ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
afs_give_up_callback(vnode);
diff --git a/fs/befs/Makefile b/fs/befs/Makefile
index 2f370bd7a50d..8b9f66642a83 100644
--- a/fs/befs/Makefile
+++ b/fs/befs/Makefile
@@ -3,5 +3,5 @@
#
obj-$(CONFIG_BEFS_FS) += befs.o
-
+ccflags-$(CONFIG_BEFS_DEBUG) += -DDEBUG
befs-objs := datastream.o btree.o super.o inode.o debug.o io.o linuxvfs.o
diff --git a/fs/befs/befs.h b/fs/befs/befs.h
index b26642839156..3a7813ab8c95 100644
--- a/fs/befs/befs.h
+++ b/fs/befs/befs.h
@@ -88,8 +88,11 @@ enum befs_err {
/****************************/
/* debug.c */
+__printf(2, 3)
void befs_error(const struct super_block *sb, const char *fmt, ...);
+__printf(2, 3)
void befs_warning(const struct super_block *sb, const char *fmt, ...);
+__printf(2, 3)
void befs_debug(const struct super_block *sb, const char *fmt, ...);
void befs_dump_super_block(const struct super_block *sb, befs_super_block *);
diff --git a/fs/befs/btree.c b/fs/befs/btree.c
index 74e397db0b8b..a2cd305a993a 100644
--- a/fs/befs/btree.c
+++ b/fs/befs/btree.c
@@ -137,7 +137,7 @@ befs_bt_read_super(struct super_block *sb, befs_data_stream * ds,
struct buffer_head *bh = NULL;
befs_disk_btree_super *od_sup = NULL;
- befs_debug(sb, "---> befs_btree_read_super()");
+ befs_debug(sb, "---> %s", __func__);
bh = befs_read_datastream(sb, ds, 0, NULL);
@@ -162,11 +162,11 @@ befs_bt_read_super(struct super_block *sb, befs_data_stream * ds,
goto error;
}
- befs_debug(sb, "<--- befs_btree_read_super()");
+ befs_debug(sb, "<--- %s", __func__);
return BEFS_OK;
error:
- befs_debug(sb, "<--- befs_btree_read_super() ERROR");
+ befs_debug(sb, "<--- %s ERROR", __func__);
return BEFS_ERR;
}
@@ -195,16 +195,16 @@ befs_bt_read_node(struct super_block *sb, befs_data_stream * ds,
{
uint off = 0;
- befs_debug(sb, "---> befs_bt_read_node()");
+ befs_debug(sb, "---> %s", __func__);
if (node->bh)
brelse(node->bh);
node->bh = befs_read_datastream(sb, ds, node_off, &off);
if (!node->bh) {
- befs_error(sb, "befs_bt_read_node() failed to read "
- "node at %Lu", node_off);
- befs_debug(sb, "<--- befs_bt_read_node() ERROR");
+ befs_error(sb, "%s failed to read "
+ "node at %llu", __func__, node_off);
+ befs_debug(sb, "<--- %s ERROR", __func__);
return BEFS_ERR;
}
@@ -221,7 +221,7 @@ befs_bt_read_node(struct super_block *sb, befs_data_stream * ds,
node->head.all_key_length =
fs16_to_cpu(sb, node->od_node->all_key_length);
- befs_debug(sb, "<--- befs_btree_read_node()");
+ befs_debug(sb, "<--- %s", __func__);
return BEFS_OK;
}
@@ -252,7 +252,7 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds,
befs_off_t node_off;
int res;
- befs_debug(sb, "---> befs_btree_find() Key: %s", key);
+ befs_debug(sb, "---> %s Key: %s", __func__, key);
if (befs_bt_read_super(sb, ds, &bt_super) != BEFS_OK) {
befs_error(sb,
@@ -263,7 +263,7 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds,
this_node = kmalloc(sizeof (befs_btree_node),
GFP_NOFS);
if (!this_node) {
- befs_error(sb, "befs_btree_find() failed to allocate %u "
+ befs_error(sb, "befs_btree_find() failed to allocate %zu "
"bytes of memory", sizeof (befs_btree_node));
goto error;
}
@@ -274,7 +274,7 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds,
node_off = bt_super.root_node_ptr;
if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) {
befs_error(sb, "befs_btree_find() failed to read "
- "node at %Lu", node_off);
+ "node at %llu", node_off);
goto error_alloc;
}
@@ -285,7 +285,7 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds,
/* if no match, go to overflow node */
if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) {
befs_error(sb, "befs_btree_find() failed to read "
- "node at %Lu", node_off);
+ "node at %llu", node_off);
goto error_alloc;
}
}
@@ -298,11 +298,11 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds,
kfree(this_node);
if (res != BEFS_BT_MATCH) {
- befs_debug(sb, "<--- befs_btree_find() Key %s not found", key);
+ befs_debug(sb, "<--- %s Key %s not found", __func__, key);
*value = 0;
return BEFS_BT_NOT_FOUND;
}
- befs_debug(sb, "<--- befs_btree_find() Found key %s, value %Lu",
+ befs_debug(sb, "<--- %s Found key %s, value %llu", __func__,
key, *value);
return BEFS_OK;
@@ -310,7 +310,7 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds,
kfree(this_node);
error:
*value = 0;
- befs_debug(sb, "<--- befs_btree_find() ERROR");
+ befs_debug(sb, "<--- %s ERROR", __func__);
return BEFS_ERR;
}
@@ -343,7 +343,7 @@ befs_find_key(struct super_block *sb, befs_btree_node * node,
char *thiskey;
fs64 *valarray;
- befs_debug(sb, "---> befs_find_key() %s", findkey);
+ befs_debug(sb, "---> %s %s", __func__, findkey);
*value = 0;
@@ -355,7 +355,7 @@ befs_find_key(struct super_block *sb, befs_btree_node * node,
eq = befs_compare_strings(thiskey, keylen, findkey, findkey_len);
if (eq < 0) {
- befs_debug(sb, "<--- befs_find_key() %s not found", findkey);
+ befs_debug(sb, "<--- %s %s not found", __func__, findkey);
return BEFS_BT_NOT_FOUND;
}
@@ -373,8 +373,8 @@ befs_find_key(struct super_block *sb, befs_btree_node * node,
findkey_len);
if (eq == 0) {
- befs_debug(sb, "<--- befs_find_key() found %s at %d",
- thiskey, mid);
+ befs_debug(sb, "<--- %s found %s at %d",
+ __func__, thiskey, mid);
*value = fs64_to_cpu(sb, valarray[mid]);
return BEFS_BT_MATCH;
@@ -388,7 +388,7 @@ befs_find_key(struct super_block *sb, befs_btree_node * node,
*value = fs64_to_cpu(sb, valarray[mid + 1]);
else
*value = fs64_to_cpu(sb, valarray[mid]);
- befs_debug(sb, "<--- befs_find_key() found %s at %d", thiskey, mid);
+ befs_debug(sb, "<--- %s found %s at %d", __func__, thiskey, mid);
return BEFS_BT_PARMATCH;
}
@@ -428,7 +428,7 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds,
uint key_sum = 0;
- befs_debug(sb, "---> befs_btree_read()");
+ befs_debug(sb, "---> %s", __func__);
if (befs_bt_read_super(sb, ds, &bt_super) != BEFS_OK) {
befs_error(sb,
@@ -437,7 +437,7 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds,
}
if ((this_node = kmalloc(sizeof (befs_btree_node), GFP_NOFS)) == NULL) {
- befs_error(sb, "befs_btree_read() failed to allocate %u "
+ befs_error(sb, "befs_btree_read() failed to allocate %zu "
"bytes of memory", sizeof (befs_btree_node));
goto error;
}
@@ -452,7 +452,7 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds,
kfree(this_node);
*value = 0;
*keysize = 0;
- befs_debug(sb, "<--- befs_btree_read() Tree is EMPTY");
+ befs_debug(sb, "<--- %s Tree is EMPTY", __func__);
return BEFS_BT_EMPTY;
} else if (res == BEFS_ERR) {
goto error_alloc;
@@ -467,7 +467,8 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds,
*keysize = 0;
*value = 0;
befs_debug(sb,
- "<--- befs_btree_read() END of keys at %Lu",
+ "<--- %s END of keys at %llu", __func__,
+ (unsigned long long)
key_sum + this_node->head.all_key_count);
brelse(this_node->bh);
kfree(this_node);
@@ -478,8 +479,8 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds,
node_off = this_node->head.right;
if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) {
- befs_error(sb, "befs_btree_read() failed to read "
- "node at %Lu", node_off);
+ befs_error(sb, "%s failed to read node at %llu",
+ __func__, (unsigned long long)node_off);
goto error_alloc;
}
}
@@ -492,11 +493,13 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds,
keystart = befs_bt_get_key(sb, this_node, cur_key, &keylen);
- befs_debug(sb, "Read [%Lu,%d]: keysize %d", node_off, cur_key, keylen);
+ befs_debug(sb, "Read [%llu,%d]: keysize %d",
+ (long long unsigned int)node_off, (int)cur_key,
+ (int)keylen);
if (bufsize < keylen + 1) {
- befs_error(sb, "befs_btree_read() keybuf too small (%u) "
- "for key of size %d", bufsize, keylen);
+ befs_error(sb, "%s keybuf too small (%zu) "
+ "for key of size %d", __func__, bufsize, keylen);
brelse(this_node->bh);
goto error_alloc;
};
@@ -506,13 +509,13 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds,
*keysize = keylen;
keybuf[keylen] = '\0';
- befs_debug(sb, "Read [%Lu,%d]: Key \"%.*s\", Value %Lu", node_off,
+ befs_debug(sb, "Read [%llu,%d]: Key \"%.*s\", Value %llu", node_off,
cur_key, keylen, keybuf, *value);
brelse(this_node->bh);
kfree(this_node);
- befs_debug(sb, "<--- befs_btree_read()");
+ befs_debug(sb, "<--- %s", __func__);
return BEFS_OK;
@@ -522,7 +525,7 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds,
error:
*keysize = 0;
*value = 0;
- befs_debug(sb, "<--- befs_btree_read() ERROR");
+ befs_debug(sb, "<--- %s ERROR", __func__);
return BEFS_ERR;
}
@@ -547,26 +550,26 @@ befs_btree_seekleaf(struct super_block *sb, befs_data_stream * ds,
befs_off_t * node_off)
{
- befs_debug(sb, "---> befs_btree_seekleaf()");
+ befs_debug(sb, "---> %s", __func__);
if (befs_bt_read_node(sb, ds, this_node, *node_off) != BEFS_OK) {
- befs_error(sb, "befs_btree_seekleaf() failed to read "
- "node at %Lu", *node_off);
+ befs_error(sb, "%s failed to read "
+ "node at %llu", __func__, *node_off);
goto error;
}
- befs_debug(sb, "Seekleaf to root node %Lu", *node_off);
+ befs_debug(sb, "Seekleaf to root node %llu", *node_off);
if (this_node->head.all_key_count == 0 && befs_leafnode(this_node)) {
- befs_debug(sb, "<--- befs_btree_seekleaf() Tree is EMPTY");
+ befs_debug(sb, "<--- %s Tree is EMPTY", __func__);
return BEFS_BT_EMPTY;
}
while (!befs_leafnode(this_node)) {
if (this_node->head.all_key_count == 0) {
- befs_debug(sb, "befs_btree_seekleaf() encountered "
- "an empty interior node: %Lu. Using Overflow "
- "node: %Lu", *node_off,
+ befs_debug(sb, "%s encountered "
+ "an empty interior node: %llu. Using Overflow "
+ "node: %llu", __func__, *node_off,
this_node->head.overflow);
*node_off = this_node->head.overflow;
} else {
@@ -574,19 +577,19 @@ befs_btree_seekleaf(struct super_block *sb, befs_data_stream * ds,
*node_off = fs64_to_cpu(sb, valarray[0]);
}
if (befs_bt_read_node(sb, ds, this_node, *node_off) != BEFS_OK) {
- befs_error(sb, "befs_btree_seekleaf() failed to read "
- "node at %Lu", *node_off);
+ befs_error(sb, "%s failed to read "
+ "node at %llu", __func__, *node_off);
goto error;
}
- befs_debug(sb, "Seekleaf to child node %Lu", *node_off);
+ befs_debug(sb, "Seekleaf to child node %llu", *node_off);
}
- befs_debug(sb, "Node %Lu is a leaf node", *node_off);
+ befs_debug(sb, "Node %llu is a leaf node", *node_off);
return BEFS_OK;
error:
- befs_debug(sb, "<--- befs_btree_seekleaf() ERROR");
+ befs_debug(sb, "<--- %s ERROR", __func__);
return BEFS_ERR;
}
diff --git a/fs/befs/datastream.c b/fs/befs/datastream.c
index 59096b5e0fc7..c467bebd50af 100644
--- a/fs/befs/datastream.c
+++ b/fs/befs/datastream.c
@@ -52,26 +52,25 @@ befs_read_datastream(struct super_block *sb, befs_data_stream * ds,
befs_block_run run;
befs_blocknr_t block; /* block coresponding to pos */
- befs_debug(sb, "---> befs_read_datastream() %Lu", pos);
+ befs_debug(sb, "---> %s %llu", __func__, pos);
block = pos >> BEFS_SB(sb)->block_shift;
if (off)
*off = pos - (block << BEFS_SB(sb)->block_shift);
if (befs_fblock2brun(sb, ds, block, &run) != BEFS_OK) {
befs_error(sb, "BeFS: Error finding disk addr of block %lu",
- block);
- befs_debug(sb, "<--- befs_read_datastream() ERROR");
+ (unsigned long)block);
+ befs_debug(sb, "<--- %s ERROR", __func__);
return NULL;
}
bh = befs_bread_iaddr(sb, run);
if (!bh) {
befs_error(sb, "BeFS: Error reading block %lu from datastream",
- block);
+ (unsigned long)block);
return NULL;
}
- befs_debug(sb, "<--- befs_read_datastream() read data, starting at %Lu",
- pos);
+ befs_debug(sb, "<--- %s read data, starting at %llu", __func__, pos);
return bh;
}
@@ -106,7 +105,8 @@ befs_fblock2brun(struct super_block *sb, befs_data_stream * data,
} else {
befs_error(sb,
"befs_fblock2brun() was asked to find block %lu, "
- "which is not mapped by the datastream\n", fblock);
+ "which is not mapped by the datastream\n",
+ (unsigned long)fblock);
err = BEFS_ERR;
}
return err;
@@ -128,14 +128,14 @@ befs_read_lsymlink(struct super_block * sb, befs_data_stream * ds, void *buff,
befs_off_t bytes_read = 0; /* bytes readed */
u16 plen;
struct buffer_head *bh = NULL;
- befs_debug(sb, "---> befs_read_lsymlink() length: %Lu", len);
+ befs_debug(sb, "---> %s length: %llu", __func__, len);
while (bytes_read < len) {
bh = befs_read_datastream(sb, ds, bytes_read, NULL);
if (!bh) {
befs_error(sb, "BeFS: Error reading datastream block "
- "starting from %Lu", bytes_read);
- befs_debug(sb, "<--- befs_read_lsymlink() ERROR");
+ "starting from %llu", bytes_read);
+ befs_debug(sb, "<--- %s ERROR", __func__);
return bytes_read;
}
@@ -146,7 +146,8 @@ befs_read_lsymlink(struct super_block * sb, befs_data_stream * ds, void *buff,
bytes_read += plen;
}
- befs_debug(sb, "<--- befs_read_lsymlink() read %u bytes", bytes_read);
+ befs_debug(sb, "<--- %s read %u bytes", __func__, (unsigned int)
+ bytes_read);
return bytes_read;
}
@@ -169,7 +170,7 @@ befs_count_blocks(struct super_block * sb, befs_data_stream * ds)
befs_blocknr_t metablocks; /* FS metadata blocks */
befs_sb_info *befs_sb = BEFS_SB(sb);
- befs_debug(sb, "---> befs_count_blocks()");
+ befs_debug(sb, "---> %s", __func__);
datablocks = ds->size >> befs_sb->block_shift;
if (ds->size & (befs_sb->block_size - 1))
@@ -206,7 +207,7 @@ befs_count_blocks(struct super_block * sb, befs_data_stream * ds)
}
blocks = datablocks + metablocks;
- befs_debug(sb, "<--- befs_count_blocks() %u blocks", blocks);
+ befs_debug(sb, "<--- %s %u blocks", __func__, (unsigned int)blocks);
return blocks;
}
@@ -251,11 +252,11 @@ befs_find_brun_direct(struct super_block *sb, befs_data_stream * data,
befs_blocknr_t max_block =
data->max_direct_range >> BEFS_SB(sb)->block_shift;
- befs_debug(sb, "---> befs_find_brun_direct(), find %lu", blockno);
+ befs_debug(sb, "---> %s, find %lu", __func__, (unsigned long)blockno);
if (blockno > max_block) {
- befs_error(sb, "befs_find_brun_direct() passed block outside of"
- "direct region");
+ befs_error(sb, "%s passed block outside of direct region",
+ __func__);
return BEFS_ERR;
}
@@ -267,13 +268,14 @@ befs_find_brun_direct(struct super_block *sb, befs_data_stream * data,
run->start = array[i].start + offset;
run->len = array[i].len - offset;
- befs_debug(sb, "---> befs_find_brun_direct(), "
- "found %lu at direct[%d]", blockno, i);
+ befs_debug(sb, "---> %s, "
+ "found %lu at direct[%d]", __func__,
+ (unsigned long)blockno, i);
return BEFS_OK;
}
}
- befs_debug(sb, "---> befs_find_brun_direct() ERROR");
+ befs_debug(sb, "---> %s ERROR", __func__);
return BEFS_ERR;
}
@@ -316,7 +318,7 @@ befs_find_brun_indirect(struct super_block *sb,
befs_blocknr_t indirblockno = iaddr2blockno(sb, &indirect);
int arraylen = befs_iaddrs_per_block(sb);
- befs_debug(sb, "---> befs_find_brun_indirect(), find %lu", blockno);
+ befs_debug(sb, "---> %s, find %lu", __func__, (unsigned long)blockno);
indir_start_blk = data->max_direct_range >> BEFS_SB(sb)->block_shift;
search_blk = blockno - indir_start_blk;
@@ -325,10 +327,9 @@ befs_find_brun_indirect(struct super_block *sb,
for (i = 0; i < indirect.len; i++) {
indirblock = befs_bread(sb, indirblockno + i);
if (indirblock == NULL) {
- befs_debug(sb,
- "---> befs_find_brun_indirect() failed to "
- "read disk block %lu from the indirect brun",
- indirblockno + i);
+ befs_debug(sb, "---> %s failed to read "
+ "disk block %lu from the indirect brun",
+ __func__, (unsigned long)indirblockno + i);
return BEFS_ERR;
}
@@ -348,9 +349,10 @@ befs_find_brun_indirect(struct super_block *sb,
brelse(indirblock);
befs_debug(sb,
- "<--- befs_find_brun_indirect() found "
- "file block %lu at indirect[%d]",
- blockno, j + (i * arraylen));
+ "<--- %s found file block "
+ "%lu at indirect[%d]", __func__,
+ (unsigned long)blockno,
+ j + (i * arraylen));
return BEFS_OK;
}
sum += len;
@@ -360,10 +362,10 @@ befs_find_brun_indirect(struct super_block *sb,
}
/* Only fallthrough is an error */
- befs_error(sb, "BeFS: befs_find_brun_indirect() failed to find "
- "file block %lu", blockno);
+ befs_error(sb, "BeFS: %s failed to find "
+ "file block %lu", __func__, (unsigned long)blockno);
- befs_debug(sb, "<--- befs_find_brun_indirect() ERROR");
+ befs_debug(sb, "<--- %s ERROR", __func__);
return BEFS_ERR;
}
@@ -444,7 +446,7 @@ befs_find_brun_dblindirect(struct super_block *sb,
size_t diblklen = iblklen * befs_iaddrs_per_block(sb)
* BEFS_DBLINDIR_BRUN_LEN;
- befs_debug(sb, "---> befs_find_brun_dblindirect() find %lu", blockno);
+ befs_debug(sb, "---> %s find %lu", __func__, (unsigned long)blockno);
/* First, discover which of the double_indir->indir blocks
* contains pos. Then figure out how much of pos that
@@ -460,8 +462,9 @@ befs_find_brun_dblindirect(struct super_block *sb,
dbl_which_block = dblindir_indx / befs_iaddrs_per_block(sb);
if (dbl_which_block > data->double_indirect.len) {
befs_error(sb, "The double-indirect index calculated by "
- "befs_read_brun_dblindirect(), %d, is outside the range "
- "of the double-indirect block", dblindir_indx);
+ "%s, %d, is outside the range "
+ "of the double-indirect block", __func__,
+ dblindir_indx);
return BEFS_ERR;
}
@@ -469,10 +472,10 @@ befs_find_brun_dblindirect(struct super_block *sb,
befs_bread(sb, iaddr2blockno(sb, &data->double_indirect) +
dbl_which_block);
if (dbl_indir_block == NULL) {
- befs_error(sb, "befs_read_brun_dblindirect() couldn't read the "
- "double-indirect block at blockno %lu",
- iaddr2blockno(sb,
- &data->double_indirect) +
+ befs_error(sb, "%s couldn't read the "
+ "double-indirect block at blockno %lu", __func__,
+ (unsigned long)
+ iaddr2blockno(sb, &data->double_indirect) +
dbl_which_block);
brelse(dbl_indir_block);
return BEFS_ERR;
@@ -489,16 +492,16 @@ befs_find_brun_dblindirect(struct super_block *sb,
which_block = indir_indx / befs_iaddrs_per_block(sb);
if (which_block > indir_run.len) {
befs_error(sb, "The indirect index calculated by "
- "befs_read_brun_dblindirect(), %d, is outside the range "
- "of the indirect block", indir_indx);
+ "%s, %d, is outside the range "
+ "of the indirect block", __func__, indir_indx);
return BEFS_ERR;
}
indir_block =
befs_bread(sb, iaddr2blockno(sb, &indir_run) + which_block);
if (indir_block == NULL) {
- befs_error(sb, "befs_read_brun_dblindirect() couldn't read the "
- "indirect block at blockno %lu",
+ befs_error(sb, "%s couldn't read the indirect block "
+ "at blockno %lu", __func__, (unsigned long)
iaddr2blockno(sb, &indir_run) + which_block);
brelse(indir_block);
return BEFS_ERR;
@@ -519,7 +522,7 @@ befs_find_brun_dblindirect(struct super_block *sb,
run->len -= offset;
befs_debug(sb, "Found file block %lu in double_indirect[%d][%d],"
- " double_indirect_leftover = %lu",
+ " double_indirect_leftover = %lu", (unsigned long)
blockno, dblindir_indx, indir_indx, dblindir_leftover);
return BEFS_OK;
diff --git a/fs/befs/debug.c b/fs/befs/debug.c
index 622e73775c83..4de7cffcd662 100644
--- a/fs/befs/debug.c
+++ b/fs/befs/debug.c
@@ -10,6 +10,7 @@
* debug functions
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#ifdef __KERNEL__
#include <stdarg.h>
@@ -23,43 +24,30 @@
#include "befs.h"
-#define ERRBUFSIZE 1024
-
void
befs_error(const struct super_block *sb, const char *fmt, ...)
{
+ struct va_format vaf;
va_list args;
- char *err_buf = kmalloc(ERRBUFSIZE, GFP_KERNEL);
- if (err_buf == NULL) {
- printk(KERN_ERR "could not allocate %d bytes\n", ERRBUFSIZE);
- return;
- }
va_start(args, fmt);
- vsnprintf(err_buf, ERRBUFSIZE, fmt, args);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ pr_err("(%s): %pV\n", sb->s_id, &vaf);
va_end(args);
-
- printk(KERN_ERR "BeFS(%s): %s\n", sb->s_id, err_buf);
- kfree(err_buf);
}
void
befs_warning(const struct super_block *sb, const char *fmt, ...)
{
+ struct va_format vaf;
va_list args;
- char *err_buf = kmalloc(ERRBUFSIZE, GFP_KERNEL);
- if (err_buf == NULL) {
- printk(KERN_ERR "could not allocate %d bytes\n", ERRBUFSIZE);
- return;
- }
va_start(args, fmt);
- vsnprintf(err_buf, ERRBUFSIZE, fmt, args);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ pr_warn("(%s): %pV\n", sb->s_id, &vaf);
va_end(args);
-
- printk(KERN_WARNING "BeFS(%s): %s\n", sb->s_id, err_buf);
-
- kfree(err_buf);
}
void
@@ -67,25 +55,13 @@ befs_debug(const struct super_block *sb, const char *fmt, ...)
{
#ifdef CONFIG_BEFS_DEBUG
+ struct va_format vaf;
va_list args;
- char *err_buf = NULL;
-
- if (BEFS_SB(sb)->mount_opts.debug) {
- err_buf = kmalloc(ERRBUFSIZE, GFP_KERNEL);
- if (err_buf == NULL) {
- printk(KERN_ERR "could not allocate %d bytes\n",
- ERRBUFSIZE);
- return;
- }
-
- va_start(args, fmt);
- vsnprintf(err_buf, ERRBUFSIZE, fmt, args);
- va_end(args);
-
- printk(KERN_DEBUG "BeFS(%s): %s\n", sb->s_id, err_buf);
-
- kfree(err_buf);
- }
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ pr_debug("(%s): %pV\n", sb->s_id, &vaf);
+ va_end(args);
#endif //CONFIG_BEFS_DEBUG
}
@@ -109,9 +85,9 @@ befs_dump_inode(const struct super_block *sb, befs_inode * inode)
befs_debug(sb, " gid %u", fs32_to_cpu(sb, inode->gid));
befs_debug(sb, " mode %08x", fs32_to_cpu(sb, inode->mode));
befs_debug(sb, " flags %08x", fs32_to_cpu(sb, inode->flags));
- befs_debug(sb, " create_time %Lu",
+ befs_debug(sb, " create_time %llu",
fs64_to_cpu(sb, inode->create_time));
- befs_debug(sb, " last_modified_time %Lu",
+ befs_debug(sb, " last_modified_time %llu",
fs64_to_cpu(sb, inode->last_modified_time));
tmp_run = fsrun_to_cpu(sb, inode->parent);
@@ -137,7 +113,7 @@ befs_dump_inode(const struct super_block *sb, befs_inode * inode)
tmp_run.allocation_group, tmp_run.start,
tmp_run.len);
}
- befs_debug(sb, " max_direct_range %Lu",
+ befs_debug(sb, " max_direct_range %llu",
fs64_to_cpu(sb,
inode->data.datastream.
max_direct_range));
@@ -147,7 +123,7 @@ befs_dump_inode(const struct super_block *sb, befs_inode * inode)
tmp_run.allocation_group,
tmp_run.start, tmp_run.len);
- befs_debug(sb, " max_indirect_range %Lu",
+ befs_debug(sb, " max_indirect_range %llu",
fs64_to_cpu(sb,
inode->data.datastream.
max_indirect_range));
@@ -158,12 +134,12 @@ befs_dump_inode(const struct super_block *sb, befs_inode * inode)
tmp_run.allocation_group, tmp_run.start,
tmp_run.len);
- befs_debug(sb, " max_double_indirect_range %Lu",
+ befs_debug(sb, " max_double_indirect_range %llu",
fs64_to_cpu(sb,
inode->data.datastream.
max_double_indirect_range));
- befs_debug(sb, " size %Lu",
+ befs_debug(sb, " size %llu",
fs64_to_cpu(sb, inode->data.datastream.size));
}
@@ -191,8 +167,8 @@ befs_dump_super_block(const struct super_block *sb, befs_super_block * sup)
befs_debug(sb, " block_size %u", fs32_to_cpu(sb, sup->block_size));
befs_debug(sb, " block_shift %u", fs32_to_cpu(sb, sup->block_shift));
- befs_debug(sb, " num_blocks %Lu", fs64_to_cpu(sb, sup->num_blocks));
- befs_debug(sb, " used_blocks %Lu", fs64_to_cpu(sb, sup->used_blocks));
+ befs_debug(sb, " num_blocks %llu", fs64_to_cpu(sb, sup->num_blocks));
+ befs_debug(sb, " used_blocks %llu", fs64_to_cpu(sb, sup->used_blocks));
befs_debug(sb, " magic2 %08x", fs32_to_cpu(sb, sup->magic2));
befs_debug(sb, " blocks_per_ag %u",
@@ -206,8 +182,8 @@ befs_dump_super_block(const struct super_block *sb, befs_super_block * sup)
befs_debug(sb, " log_blocks %u, %hu, %hu",
tmp_run.allocation_group, tmp_run.start, tmp_run.len);
- befs_debug(sb, " log_start %Ld", fs64_to_cpu(sb, sup->log_start));
- befs_debug(sb, " log_end %Ld", fs64_to_cpu(sb, sup->log_end));
+ befs_debug(sb, " log_start %lld", fs64_to_cpu(sb, sup->log_start));
+ befs_debug(sb, " log_end %lld", fs64_to_cpu(sb, sup->log_end));
befs_debug(sb, " magic3 %08x", fs32_to_cpu(sb, sup->magic3));
diff --git a/fs/befs/inode.c b/fs/befs/inode.c
index 94c17f9a9576..fa4b718de597 100644
--- a/fs/befs/inode.c
+++ b/fs/befs/inode.c
@@ -25,7 +25,8 @@ befs_check_inode(struct super_block *sb, befs_inode * raw_inode,
/* check magic header. */
if (magic1 != BEFS_INODE_MAGIC1) {
befs_error(sb,
- "Inode has a bad magic header - inode = %lu", inode);
+ "Inode has a bad magic header - inode = %lu",
+ (unsigned long)inode);
return BEFS_BAD_INODE;
}
@@ -34,8 +35,8 @@ befs_check_inode(struct super_block *sb, befs_inode * raw_inode,
*/
if (inode != iaddr2blockno(sb, &ino_num)) {
befs_error(sb, "inode blocknr field disagrees with vfs "
- "VFS: %lu, Inode %lu",
- inode, iaddr2blockno(sb, &ino_num));
+ "VFS: %lu, Inode %lu", (unsigned long)
+ inode, (unsigned long)iaddr2blockno(sb, &ino_num));
return BEFS_BAD_INODE;
}
@@ -44,7 +45,8 @@ befs_check_inode(struct super_block *sb, befs_inode * raw_inode,
*/
if (!(flags & BEFS_INODE_IN_USE)) {
- befs_error(sb, "inode is not used - inode = %lu", inode);
+ befs_error(sb, "inode is not used - inode = %lu",
+ (unsigned long)inode);
return BEFS_BAD_INODE;
}
diff --git a/fs/befs/io.c b/fs/befs/io.c
index ddef98aa255d..0408a3d601d0 100644
--- a/fs/befs/io.c
+++ b/fs/befs/io.c
@@ -30,9 +30,9 @@ befs_bread_iaddr(struct super_block *sb, befs_inode_addr iaddr)
befs_blocknr_t block = 0;
befs_sb_info *befs_sb = BEFS_SB(sb);
- befs_debug(sb, "---> Enter befs_read_iaddr() "
- "[%u, %hu, %hu]",
- iaddr.allocation_group, iaddr.start, iaddr.len);
+ befs_debug(sb, "---> Enter %s "
+ "[%u, %hu, %hu]", __func__, iaddr.allocation_group,
+ iaddr.start, iaddr.len);
if (iaddr.allocation_group > befs_sb->num_ags) {
befs_error(sb, "BEFS: Invalid allocation group %u, max is %u",
@@ -42,20 +42,21 @@ befs_bread_iaddr(struct super_block *sb, befs_inode_addr iaddr)
block = iaddr2blockno(sb, &iaddr);
- befs_debug(sb, "befs_read_iaddr: offset = %lu", block);
+ befs_debug(sb, "%s: offset = %lu", __func__, (unsigned long)block);
bh = sb_bread(sb, block);
if (bh == NULL) {
- befs_error(sb, "Failed to read block %lu", block);
+ befs_error(sb, "Failed to read block %lu",
+ (unsigned long)block);
goto error;
}
- befs_debug(sb, "<--- befs_read_iaddr()");
+ befs_debug(sb, "<--- %s", __func__);
return bh;
error:
- befs_debug(sb, "<--- befs_read_iaddr() ERROR");
+ befs_debug(sb, "<--- %s ERROR", __func__);
return NULL;
}
@@ -64,20 +65,21 @@ befs_bread(struct super_block *sb, befs_blocknr_t block)
{
struct buffer_head *bh = NULL;
- befs_debug(sb, "---> Enter befs_read() %Lu", block);
+ befs_debug(sb, "---> Enter %s %lu", __func__, (unsigned long)block);
bh = sb_bread(sb, block);
if (bh == NULL) {
- befs_error(sb, "Failed to read block %lu", block);
+ befs_error(sb, "Failed to read block %lu",
+ (unsigned long)block);
goto error;
}
- befs_debug(sb, "<--- befs_read()");
+ befs_debug(sb, "<--- %s", __func__);
return bh;
error:
- befs_debug(sb, "<--- befs_read() ERROR");
+ befs_debug(sb, "<--- %s ERROR", __func__);
return NULL;
}
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index 845d2d690ce2..d626756ff721 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -5,6 +5,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
@@ -39,7 +41,6 @@ static struct dentry *befs_lookup(struct inode *, struct dentry *, unsigned int)
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 int befs_init_inodecache(void);
static void befs_destroy_inodecache(void);
static void *befs_follow_link(struct dentry *, struct nameidata *);
static void *befs_fast_follow_link(struct dentry *, struct nameidata *);
@@ -131,26 +132,28 @@ befs_get_block(struct inode *inode, sector_t block,
ulong disk_off;
befs_debug(sb, "---> befs_get_block() for inode %lu, block %ld",
- inode->i_ino, block);
+ (unsigned long)inode->i_ino, (long)block);
if (block < 0) {
befs_error(sb, "befs_get_block() was asked for a block "
"number less than zero: block %ld in inode %lu",
- block, inode->i_ino);
+ (long)block, (unsigned long)inode->i_ino);
return -EIO;
}
if (create) {
befs_error(sb, "befs_get_block() was asked to write to "
- "block %ld in inode %lu", block, inode->i_ino);
+ "block %ld in inode %lu", (long)block,
+ (unsigned long)inode->i_ino);
return -EPERM;
}
res = befs_fblock2brun(sb, ds, block, &run);
if (res != BEFS_OK) {
befs_error(sb,
- "<--- befs_get_block() for inode %lu, block "
- "%ld ERROR", inode->i_ino, block);
+ "<--- %s for inode %lu, block %ld ERROR",
+ __func__, (unsigned long)inode->i_ino,
+ (long)block);
return -EFBIG;
}
@@ -158,8 +161,9 @@ befs_get_block(struct inode *inode, sector_t block,
map_bh(bh_result, inode->i_sb, disk_off);
- befs_debug(sb, "<--- befs_get_block() for inode %lu, block %ld, "
- "disk address %lu", inode->i_ino, block, disk_off);
+ befs_debug(sb, "<--- %s for inode %lu, block %ld, disk address %lu",
+ __func__, (unsigned long)inode->i_ino, (long)block,
+ (unsigned long)disk_off);
return 0;
}
@@ -176,15 +180,15 @@ befs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
char *utfname;
const char *name = dentry->d_name.name;
- befs_debug(sb, "---> befs_lookup() "
- "name %s inode %ld", dentry->d_name.name, dir->i_ino);
+ befs_debug(sb, "---> %s name %s inode %ld", __func__,
+ dentry->d_name.name, dir->i_ino);
/* Convert to UTF-8 */
if (BEFS_SB(sb)->nls) {
ret =
befs_nls2utf(sb, name, strlen(name), &utfname, &utfnamelen);
if (ret < 0) {
- befs_debug(sb, "<--- befs_lookup() ERROR");
+ befs_debug(sb, "<--- %s ERROR", __func__);
return ERR_PTR(ret);
}
ret = befs_btree_find(sb, ds, utfname, &offset);
@@ -195,12 +199,12 @@ befs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
}
if (ret == BEFS_BT_NOT_FOUND) {
- befs_debug(sb, "<--- befs_lookup() %s not found",
+ befs_debug(sb, "<--- %s %s not found", __func__,
dentry->d_name.name);
return ERR_PTR(-ENOENT);
} else if (ret != BEFS_OK || offset == 0) {
- befs_warning(sb, "<--- befs_lookup() Error");
+ befs_warning(sb, "<--- %s Error", __func__);
return ERR_PTR(-ENODATA);
}
@@ -210,7 +214,7 @@ befs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
d_add(dentry, inode);
- befs_debug(sb, "<--- befs_lookup()");
+ befs_debug(sb, "<--- %s", __func__);
return NULL;
}
@@ -228,26 +232,25 @@ befs_readdir(struct file *file, struct dir_context *ctx)
char keybuf[BEFS_NAME_LEN + 1];
const char *dirname = file->f_path.dentry->d_name.name;
- befs_debug(sb, "---> befs_readdir() "
- "name %s, inode %ld, ctx->pos %Ld",
- dirname, inode->i_ino, ctx->pos);
+ befs_debug(sb, "---> %s name %s, inode %ld, ctx->pos %lld",
+ __func__, dirname, inode->i_ino, ctx->pos);
more:
result = befs_btree_read(sb, ds, ctx->pos, BEFS_NAME_LEN + 1,
keybuf, &keysize, &value);
if (result == BEFS_ERR) {
- befs_debug(sb, "<--- befs_readdir() ERROR");
+ befs_debug(sb, "<--- %s ERROR", __func__);
befs_error(sb, "IO error reading %s (inode %lu)",
dirname, inode->i_ino);
return -EIO;
} else if (result == BEFS_BT_END) {
- befs_debug(sb, "<--- befs_readdir() END");
+ befs_debug(sb, "<--- %s END", __func__);
return 0;
} else if (result == BEFS_BT_EMPTY) {
- befs_debug(sb, "<--- befs_readdir() Empty directory");
+ befs_debug(sb, "<--- %s Empty directory", __func__);
return 0;
}
@@ -260,7 +263,7 @@ more:
result =
befs_utf2nls(sb, keybuf, keysize, &nlsname, &nlsnamelen);
if (result < 0) {
- befs_debug(sb, "<--- befs_readdir() ERROR");
+ befs_debug(sb, "<--- %s ERROR", __func__);
return result;
}
if (!dir_emit(ctx, nlsname, nlsnamelen,
@@ -277,7 +280,7 @@ more:
ctx->pos++;
goto more;
- befs_debug(sb, "<--- befs_readdir() pos %Ld", ctx->pos);
+ befs_debug(sb, "<--- %s pos %lld", __func__, ctx->pos);
return 0;
}
@@ -321,7 +324,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
struct inode *inode;
long ret = -EIO;
- befs_debug(sb, "---> befs_read_inode() " "inode = %lu", ino);
+ befs_debug(sb, "---> %s inode = %lu", __func__, ino);
inode = iget_locked(sb, ino);
if (!inode)
@@ -428,7 +431,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
}
brelse(bh);
- befs_debug(sb, "<--- befs_read_inode()");
+ befs_debug(sb, "<--- %s", __func__);
unlock_new_inode(inode);
return inode;
@@ -437,7 +440,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
unacquire_none:
iget_failed(inode);
- befs_debug(sb, "<--- befs_read_inode() - Bad inode");
+ befs_debug(sb, "<--- %s - Bad inode", __func__);
return ERR_PTR(ret);
}
@@ -445,7 +448,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
*
* Taken from NFS implementation by Al Viro.
*/
-static int
+static int __init
befs_init_inodecache(void)
{
befs_inode_cachep = kmem_cache_create("befs_inode_cache",
@@ -454,11 +457,9 @@ befs_init_inodecache(void)
SLAB_MEM_SPREAD),
init_once);
if (befs_inode_cachep == NULL) {
- printk(KERN_ERR "befs_init_inodecache: "
- "Couldn't initialize inode slabcache\n");
+ pr_err("%s: Couldn't initialize inode slabcache\n", __func__);
return -ENOMEM;
}
-
return 0;
}
@@ -544,16 +545,16 @@ befs_utf2nls(struct super_block *sb, const char *in,
*/
int maxlen = in_len + 1;
- befs_debug(sb, "---> utf2nls()");
+ befs_debug(sb, "---> %s", __func__);
if (!nls) {
- befs_error(sb, "befs_utf2nls called with no NLS table loaded");
+ befs_error(sb, "%s called with no NLS table loaded", __func__);
return -EINVAL;
}
*out = result = kmalloc(maxlen, GFP_NOFS);
if (!*out) {
- befs_error(sb, "befs_utf2nls() cannot allocate memory");
+ befs_error(sb, "%s cannot allocate memory", __func__);
*out_len = 0;
return -ENOMEM;
}
@@ -575,14 +576,14 @@ befs_utf2nls(struct super_block *sb, const char *in,
result[o] = '\0';
*out_len = o;
- befs_debug(sb, "<--- utf2nls()");
+ befs_debug(sb, "<--- %s", __func__);
return o;
conv_err:
befs_error(sb, "Name using character set %s contains a character that "
"cannot be converted to unicode.", nls->charset);
- befs_debug(sb, "<--- utf2nls()");
+ befs_debug(sb, "<--- %s", __func__);
kfree(result);
return -EILSEQ;
}
@@ -623,16 +624,17 @@ befs_nls2utf(struct super_block *sb, const char *in,
* in special cases */
int maxlen = (3 * in_len) + 1;
- befs_debug(sb, "---> nls2utf()\n");
+ befs_debug(sb, "---> %s\n", __func__);
if (!nls) {
- befs_error(sb, "befs_nls2utf called with no NLS table loaded.");
+ befs_error(sb, "%s called with no NLS table loaded.",
+ __func__);
return -EINVAL;
}
*out = result = kmalloc(maxlen, GFP_NOFS);
if (!*out) {
- befs_error(sb, "befs_nls2utf() cannot allocate memory");
+ befs_error(sb, "%s cannot allocate memory", __func__);
*out_len = 0;
return -ENOMEM;
}
@@ -653,14 +655,14 @@ befs_nls2utf(struct super_block *sb, const char *in,
result[o] = '\0';
*out_len = o;
- befs_debug(sb, "<--- nls2utf()");
+ befs_debug(sb, "<--- %s", __func__);
return i;
conv_err:
befs_error(sb, "Name using charecter set %s contains a charecter that "
"cannot be converted to unicode.", nls->charset);
- befs_debug(sb, "<--- nls2utf()");
+ befs_debug(sb, "<--- %s", __func__);
kfree(result);
return -EILSEQ;
}
@@ -715,8 +717,8 @@ parse_options(char *options, befs_mount_options * opts)
if (option >= 0)
uid = make_kuid(current_user_ns(), option);
if (!uid_valid(uid)) {
- printk(KERN_ERR "BeFS: Invalid uid %d, "
- "using default\n", option);
+ pr_err("Invalid uid %d, "
+ "using default\n", option);
break;
}
opts->uid = uid;
@@ -729,8 +731,8 @@ parse_options(char *options, befs_mount_options * opts)
if (option >= 0)
gid = make_kgid(current_user_ns(), option);
if (!gid_valid(gid)) {
- printk(KERN_ERR "BeFS: Invalid gid %d, "
- "using default\n", option);
+ pr_err("Invalid gid %d, "
+ "using default\n", option);
break;
}
opts->gid = gid;
@@ -740,8 +742,8 @@ parse_options(char *options, befs_mount_options * opts)
kfree(opts->iocharset);
opts->iocharset = match_strdup(&args[0]);
if (!opts->iocharset) {
- printk(KERN_ERR "BeFS: allocation failure for "
- "iocharset string\n");
+ pr_err("allocation failure for "
+ "iocharset string\n");
return 0;
}
break;
@@ -749,8 +751,8 @@ parse_options(char *options, befs_mount_options * opts)
opts->debug = 1;
break;
default:
- printk(KERN_ERR "BeFS: Unrecognized mount option \"%s\" "
- "or missing value\n", p);
+ pr_err("Unrecognized mount option \"%s\" "
+ "or missing value\n", p);
return 0;
}
}
@@ -791,22 +793,20 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
save_mount_options(sb, data);
- sb->s_fs_info = kmalloc(sizeof (*befs_sb), GFP_KERNEL);
+ sb->s_fs_info = kzalloc(sizeof(*befs_sb), GFP_KERNEL);
if (sb->s_fs_info == NULL) {
- printk(KERN_ERR
- "BeFS(%s): Unable to allocate memory for private "
+ pr_err("(%s): Unable to allocate memory for private "
"portion of superblock. Bailing.\n", sb->s_id);
goto unacquire_none;
}
befs_sb = BEFS_SB(sb);
- memset(befs_sb, 0, sizeof(befs_sb_info));
if (!parse_options((char *) data, &befs_sb->mount_opts)) {
befs_error(sb, "cannot parse mount options");
goto unacquire_priv_sbp;
}
- befs_debug(sb, "---> befs_fill_super()");
+ befs_debug(sb, "---> %s", __func__);
#ifndef CONFIG_BEFS_RW
if (!(sb->s_flags & MS_RDONLY)) {
@@ -854,7 +854,7 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
goto unacquire_priv_sbp;
if( befs_sb->num_blocks > ~((sector_t)0) ) {
- befs_error(sb, "blocks count: %Lu "
+ befs_error(sb, "blocks count: %llu "
"is larger than the host can use",
befs_sb->num_blocks);
goto unacquire_priv_sbp;
@@ -913,6 +913,7 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
static int
befs_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
if (!(*flags & MS_RDONLY))
return -EINVAL;
return 0;
@@ -924,7 +925,7 @@ befs_statfs(struct dentry *dentry, struct kstatfs *buf)
struct super_block *sb = dentry->d_sb;
u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
- befs_debug(sb, "---> befs_statfs()");
+ befs_debug(sb, "---> %s", __func__);
buf->f_type = BEFS_SUPER_MAGIC;
buf->f_bsize = sb->s_blocksize;
@@ -937,7 +938,7 @@ befs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_fsid.val[1] = (u32)(id >> 32);
buf->f_namelen = BEFS_NAME_LEN;
- befs_debug(sb, "<--- befs_statfs()");
+ befs_debug(sb, "<--- %s", __func__);
return 0;
}
@@ -963,7 +964,7 @@ init_befs_fs(void)
{
int err;
- printk(KERN_INFO "BeFS version: %s\n", BEFS_VERSION);
+ pr_info("version: %s\n", BEFS_VERSION);
err = befs_init_inodecache();
if (err)
diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c
index 8defc6b3f9a2..29aa5cf6639b 100644
--- a/fs/bfs/inode.c
+++ b/fs/bfs/inode.c
@@ -172,7 +172,7 @@ static void bfs_evict_inode(struct inode *inode)
dprintf("ino=%08lx\n", ino);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
invalidate_inode_buffers(inode);
clear_inode(inode);
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 67be2951b98a..0f59799fa105 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -46,10 +46,15 @@
#endif
static int load_elf_binary(struct linux_binprm *bprm);
-static int load_elf_library(struct file *);
static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *,
int, int, unsigned long);
+#ifdef CONFIG_USELIB
+static int load_elf_library(struct file *);
+#else
+#define load_elf_library NULL
+#endif
+
/*
* If we don't support core dumping, then supply a NULL so we
* don't even try.
@@ -1005,6 +1010,7 @@ out_free_ph:
goto out;
}
+#ifdef CONFIG_USELIB
/* This is really simpleminded and specialized - we are loading an
a.out library that is given an ELF header. */
static int load_elf_library(struct file *file)
@@ -1083,6 +1089,7 @@ out_free_ph:
out:
return error;
}
+#endif /* #ifdef CONFIG_USELIB */
#ifdef CONFIG_ELF_CORE
/*
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 1c740e152f38..b60500300dd7 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -656,6 +656,7 @@ static ssize_t bm_status_write(struct file * file, const char __user * buffer,
mutex_unlock(&root->d_inode->i_mutex);
dput(root);
+ break;
default: return res;
}
return count;
diff --git a/fs/bio.c b/fs/bio.c
index b2dd42ed9edd..b1bc722b89aa 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -1969,7 +1969,7 @@ int bio_associate_current(struct bio *bio)
/* associate blkcg if exists */
rcu_read_lock();
- css = task_css(current, blkio_subsys_id);
+ css = task_css(current, blkio_cgrp_id);
if (css && css_tryget(css))
bio->bi_css = css;
rcu_read_unlock();
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 1e86823a9cbd..ba0d2b05bb78 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -83,7 +83,7 @@ void kill_bdev(struct block_device *bdev)
{
struct address_space *mapping = bdev->bd_inode->i_mapping;
- if (mapping->nrpages == 0)
+ if (mapping->nrpages == 0 && mapping->nrshadows == 0)
return;
invalidate_bh_lrus();
@@ -419,7 +419,7 @@ static void bdev_evict_inode(struct inode *inode)
{
struct block_device *bdev = &BDEV_I(inode)->bdev;
struct list_head *p;
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
invalidate_inode_buffers(inode); /* is it needed here? */
clear_inode(inode);
spin_lock(&bdev_lock);
@@ -1523,7 +1523,7 @@ ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov,
ssize_t err;
err = generic_write_sync(file, pos, ret);
- if (err < 0 && ret > 0)
+ if (err < 0)
ret = err;
}
blk_finish_plug(&plug);
diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c
index c1e0b0caf9cc..ecb5832c0967 100644
--- a/fs/btrfs/async-thread.c
+++ b/fs/btrfs/async-thread.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2014 Fujitsu. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
@@ -21,708 +22,313 @@
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/freezer.h>
+#include <linux/workqueue.h>
#include "async-thread.h"
+#include "ctree.h"
+
+#define WORK_DONE_BIT 0
+#define WORK_ORDER_DONE_BIT 1
+#define WORK_HIGH_PRIO_BIT 2
+
+#define NO_THRESHOLD (-1)
+#define DFT_THRESHOLD (32)
+
+struct __btrfs_workqueue {
+ struct workqueue_struct *normal_wq;
+ /* List head pointing to ordered work list */
+ struct list_head ordered_list;
+
+ /* Spinlock for ordered_list */
+ spinlock_t list_lock;
+
+ /* Thresholding related variants */
+ atomic_t pending;
+ int max_active;
+ int current_max;
+ int thresh;
+ unsigned int count;
+ spinlock_t thres_lock;
+};
-#define WORK_QUEUED_BIT 0
-#define WORK_DONE_BIT 1
-#define WORK_ORDER_DONE_BIT 2
-#define WORK_HIGH_PRIO_BIT 3
-
-/*
- * container for the kthread task pointer and the list of pending work
- * One of these is allocated per thread.
- */
-struct btrfs_worker_thread {
- /* pool we belong to */
- struct btrfs_workers *workers;
-
- /* list of struct btrfs_work that are waiting for service */
- struct list_head pending;
- struct list_head prio_pending;
-
- /* list of worker threads from struct btrfs_workers */
- struct list_head worker_list;
-
- /* kthread */
- struct task_struct *task;
+struct btrfs_workqueue {
+ struct __btrfs_workqueue *normal;
+ struct __btrfs_workqueue *high;
+};
- /* number of things on the pending list */
- atomic_t num_pending;
+static inline struct __btrfs_workqueue
+*__btrfs_alloc_workqueue(const char *name, int flags, int max_active,
+ int thresh)
+{
+ struct __btrfs_workqueue *ret = kzalloc(sizeof(*ret), GFP_NOFS);
- /* reference counter for this struct */
- atomic_t refs;
+ if (unlikely(!ret))
+ return NULL;
- unsigned long sequence;
+ ret->max_active = max_active;
+ atomic_set(&ret->pending, 0);
+ if (thresh == 0)
+ thresh = DFT_THRESHOLD;
+ /* For low threshold, disabling threshold is a better choice */
+ if (thresh < DFT_THRESHOLD) {
+ ret->current_max = max_active;
+ ret->thresh = NO_THRESHOLD;
+ } else {
+ ret->current_max = 1;
+ ret->thresh = thresh;
+ }
- /* protects the pending list. */
- spinlock_t lock;
+ if (flags & WQ_HIGHPRI)
+ ret->normal_wq = alloc_workqueue("%s-%s-high", flags,
+ ret->max_active,
+ "btrfs", name);
+ else
+ ret->normal_wq = alloc_workqueue("%s-%s", flags,
+ ret->max_active, "btrfs",
+ name);
+ if (unlikely(!ret->normal_wq)) {
+ kfree(ret);
+ return NULL;
+ }
- /* set to non-zero when this thread is already awake and kicking */
- int working;
+ INIT_LIST_HEAD(&ret->ordered_list);
+ spin_lock_init(&ret->list_lock);
+ spin_lock_init(&ret->thres_lock);
+ trace_btrfs_workqueue_alloc(ret, name, flags & WQ_HIGHPRI);
+ return ret;
+}
- /* are we currently idle */
- int idle;
-};
+static inline void
+__btrfs_destroy_workqueue(struct __btrfs_workqueue *wq);
-static int __btrfs_start_workers(struct btrfs_workers *workers);
+struct btrfs_workqueue *btrfs_alloc_workqueue(const char *name,
+ int flags,
+ int max_active,
+ int thresh)
+{
+ struct btrfs_workqueue *ret = kzalloc(sizeof(*ret), GFP_NOFS);
-/*
- * btrfs_start_workers uses kthread_run, which can block waiting for memory
- * for a very long time. It will actually throttle on page writeback,
- * and so it may not make progress until after our btrfs worker threads
- * process all of the pending work structs in their queue
- *
- * This means we can't use btrfs_start_workers from inside a btrfs worker
- * thread that is used as part of cleaning dirty memory, which pretty much
- * involves all of the worker threads.
- *
- * Instead we have a helper queue who never has more than one thread
- * where we scheduler thread start operations. This worker_start struct
- * is used to contain the work and hold a pointer to the queue that needs
- * another worker.
- */
-struct worker_start {
- struct btrfs_work work;
- struct btrfs_workers *queue;
-};
+ if (unlikely(!ret))
+ return NULL;
-static void start_new_worker_func(struct btrfs_work *work)
-{
- struct worker_start *start;
- start = container_of(work, struct worker_start, work);
- __btrfs_start_workers(start->queue);
- kfree(start);
-}
+ ret->normal = __btrfs_alloc_workqueue(name, flags & ~WQ_HIGHPRI,
+ max_active, thresh);
+ if (unlikely(!ret->normal)) {
+ kfree(ret);
+ return NULL;
+ }
-/*
- * helper function to move a thread onto the idle list after it
- * has finished some requests.
- */
-static void check_idle_worker(struct btrfs_worker_thread *worker)
-{
- if (!worker->idle && atomic_read(&worker->num_pending) <
- worker->workers->idle_thresh / 2) {
- unsigned long flags;
- spin_lock_irqsave(&worker->workers->lock, flags);
- worker->idle = 1;
-
- /* the list may be empty if the worker is just starting */
- if (!list_empty(&worker->worker_list) &&
- !worker->workers->stopping) {
- list_move(&worker->worker_list,
- &worker->workers->idle_list);
+ if (flags & WQ_HIGHPRI) {
+ ret->high = __btrfs_alloc_workqueue(name, flags, max_active,
+ thresh);
+ if (unlikely(!ret->high)) {
+ __btrfs_destroy_workqueue(ret->normal);
+ kfree(ret);
+ return NULL;
}
- spin_unlock_irqrestore(&worker->workers->lock, flags);
}
+ return ret;
}
/*
- * helper function to move a thread off the idle list after new
- * pending work is added.
+ * Hook for threshold which will be called in btrfs_queue_work.
+ * This hook WILL be called in IRQ handler context,
+ * so workqueue_set_max_active MUST NOT be called in this hook
*/
-static void check_busy_worker(struct btrfs_worker_thread *worker)
+static inline void thresh_queue_hook(struct __btrfs_workqueue *wq)
{
- if (worker->idle && atomic_read(&worker->num_pending) >=
- worker->workers->idle_thresh) {
- unsigned long flags;
- spin_lock_irqsave(&worker->workers->lock, flags);
- worker->idle = 0;
-
- if (!list_empty(&worker->worker_list) &&
- !worker->workers->stopping) {
- list_move_tail(&worker->worker_list,
- &worker->workers->worker_list);
- }
- spin_unlock_irqrestore(&worker->workers->lock, flags);
- }
+ if (wq->thresh == NO_THRESHOLD)
+ return;
+ atomic_inc(&wq->pending);
}
-static void check_pending_worker_creates(struct btrfs_worker_thread *worker)
+/*
+ * Hook for threshold which will be called before executing the work,
+ * This hook is called in kthread content.
+ * So workqueue_set_max_active is called here.
+ */
+static inline void thresh_exec_hook(struct __btrfs_workqueue *wq)
{
- struct btrfs_workers *workers = worker->workers;
- struct worker_start *start;
- unsigned long flags;
+ int new_max_active;
+ long pending;
+ int need_change = 0;
- rmb();
- if (!workers->atomic_start_pending)
+ if (wq->thresh == NO_THRESHOLD)
return;
- start = kzalloc(sizeof(*start), GFP_NOFS);
- if (!start)
- return;
-
- start->work.func = start_new_worker_func;
- start->queue = workers;
-
- spin_lock_irqsave(&workers->lock, flags);
- if (!workers->atomic_start_pending)
- goto out;
-
- workers->atomic_start_pending = 0;
- if (workers->num_workers + workers->num_workers_starting >=
- workers->max_workers)
- goto out;
-
- workers->num_workers_starting += 1;
- spin_unlock_irqrestore(&workers->lock, flags);
- btrfs_queue_worker(workers->atomic_worker_start, &start->work);
- return;
+ atomic_dec(&wq->pending);
+ spin_lock(&wq->thres_lock);
+ /*
+ * Use wq->count to limit the calling frequency of
+ * workqueue_set_max_active.
+ */
+ wq->count++;
+ wq->count %= (wq->thresh / 4);
+ if (!wq->count)
+ goto out;
+ new_max_active = wq->current_max;
+ /*
+ * pending may be changed later, but it's OK since we really
+ * don't need it so accurate to calculate new_max_active.
+ */
+ pending = atomic_read(&wq->pending);
+ if (pending > wq->thresh)
+ new_max_active++;
+ if (pending < wq->thresh / 2)
+ new_max_active--;
+ new_max_active = clamp_val(new_max_active, 1, wq->max_active);
+ if (new_max_active != wq->current_max) {
+ need_change = 1;
+ wq->current_max = new_max_active;
+ }
out:
- kfree(start);
- spin_unlock_irqrestore(&workers->lock, flags);
+ spin_unlock(&wq->thres_lock);
+
+ if (need_change) {
+ workqueue_set_max_active(wq->normal_wq, wq->current_max);
+ }
}
-static noinline void run_ordered_completions(struct btrfs_workers *workers,
- struct btrfs_work *work)
+static void run_ordered_work(struct __btrfs_workqueue *wq)
{
- if (!workers->ordered)
- return;
-
- set_bit(WORK_DONE_BIT, &work->flags);
-
- spin_lock(&workers->order_lock);
+ struct list_head *list = &wq->ordered_list;
+ struct btrfs_work *work;
+ spinlock_t *lock = &wq->list_lock;
+ unsigned long flags;
while (1) {
- if (!list_empty(&workers->prio_order_list)) {
- work = list_entry(workers->prio_order_list.next,
- struct btrfs_work, order_list);
- } else if (!list_empty(&workers->order_list)) {
- work = list_entry(workers->order_list.next,
- struct btrfs_work, order_list);
- } else {
+ spin_lock_irqsave(lock, flags);
+ if (list_empty(list))
break;
- }
+ work = list_entry(list->next, struct btrfs_work,
+ ordered_list);
if (!test_bit(WORK_DONE_BIT, &work->flags))
break;
- /* we are going to call the ordered done function, but
+ /*
+ * we are going to call the ordered done function, but
* we leave the work item on the list as a barrier so
* that later work items that are done don't have their
* functions called before this one returns
*/
if (test_and_set_bit(WORK_ORDER_DONE_BIT, &work->flags))
break;
-
- spin_unlock(&workers->order_lock);
-
+ trace_btrfs_ordered_sched(work);
+ spin_unlock_irqrestore(lock, flags);
work->ordered_func(work);
/* now take the lock again and drop our item from the list */
- spin_lock(&workers->order_lock);
- list_del(&work->order_list);
- spin_unlock(&workers->order_lock);
+ spin_lock_irqsave(lock, flags);
+ list_del(&work->ordered_list);
+ spin_unlock_irqrestore(lock, flags);
/*
* we don't want to call the ordered free functions
* with the lock held though
*/
work->ordered_free(work);
- spin_lock(&workers->order_lock);
- }
-
- spin_unlock(&workers->order_lock);
-}
-
-static void put_worker(struct btrfs_worker_thread *worker)
-{
- if (atomic_dec_and_test(&worker->refs))
- kfree(worker);
-}
-
-static int try_worker_shutdown(struct btrfs_worker_thread *worker)
-{
- int freeit = 0;
-
- spin_lock_irq(&worker->lock);
- spin_lock(&worker->workers->lock);
- if (worker->workers->num_workers > 1 &&
- worker->idle &&
- !worker->working &&
- !list_empty(&worker->worker_list) &&
- list_empty(&worker->prio_pending) &&
- list_empty(&worker->pending) &&
- atomic_read(&worker->num_pending) == 0) {
- freeit = 1;
- list_del_init(&worker->worker_list);
- worker->workers->num_workers--;
+ trace_btrfs_all_work_done(work);
}
- spin_unlock(&worker->workers->lock);
- spin_unlock_irq(&worker->lock);
-
- if (freeit)
- put_worker(worker);
- return freeit;
+ spin_unlock_irqrestore(lock, flags);
}
-static struct btrfs_work *get_next_work(struct btrfs_worker_thread *worker,
- struct list_head *prio_head,
- struct list_head *head)
-{
- struct btrfs_work *work = NULL;
- struct list_head *cur = NULL;
-
- if (!list_empty(prio_head))
- cur = prio_head->next;
-
- smp_mb();
- if (!list_empty(&worker->prio_pending))
- goto refill;
-
- if (!list_empty(head))
- cur = head->next;
-
- if (cur)
- goto out;
-
-refill:
- spin_lock_irq(&worker->lock);
- list_splice_tail_init(&worker->prio_pending, prio_head);
- list_splice_tail_init(&worker->pending, head);
-
- if (!list_empty(prio_head))
- cur = prio_head->next;
- else if (!list_empty(head))
- cur = head->next;
- spin_unlock_irq(&worker->lock);
-
- if (!cur)
- goto out_fail;
-
-out:
- work = list_entry(cur, struct btrfs_work, list);
-
-out_fail:
- return work;
-}
-
-/*
- * main loop for servicing work items
- */
-static int worker_loop(void *arg)
+static void normal_work_helper(struct work_struct *arg)
{
- struct btrfs_worker_thread *worker = arg;
- struct list_head head;
- struct list_head prio_head;
struct btrfs_work *work;
+ struct __btrfs_workqueue *wq;
+ int need_order = 0;
- INIT_LIST_HEAD(&head);
- INIT_LIST_HEAD(&prio_head);
-
- do {
-again:
- while (1) {
-
-
- work = get_next_work(worker, &prio_head, &head);
- if (!work)
- break;
-
- list_del(&work->list);
- clear_bit(WORK_QUEUED_BIT, &work->flags);
-
- work->worker = worker;
-
- work->func(work);
-
- atomic_dec(&worker->num_pending);
- /*
- * unless this is an ordered work queue,
- * 'work' was probably freed by func above.
- */
- run_ordered_completions(worker->workers, work);
-
- check_pending_worker_creates(worker);
- cond_resched();
- }
-
- spin_lock_irq(&worker->lock);
- check_idle_worker(worker);
-
- if (freezing(current)) {
- worker->working = 0;
- spin_unlock_irq(&worker->lock);
- try_to_freeze();
- } else {
- spin_unlock_irq(&worker->lock);
- if (!kthread_should_stop()) {
- cpu_relax();
- /*
- * we've dropped the lock, did someone else
- * jump_in?
- */
- smp_mb();
- if (!list_empty(&worker->pending) ||
- !list_empty(&worker->prio_pending))
- continue;
-
- /*
- * this short schedule allows more work to
- * come in without the queue functions
- * needing to go through wake_up_process()
- *
- * worker->working is still 1, so nobody
- * is going to try and wake us up
- */
- schedule_timeout(1);
- smp_mb();
- if (!list_empty(&worker->pending) ||
- !list_empty(&worker->prio_pending))
- continue;
-
- if (kthread_should_stop())
- break;
-
- /* still no more work?, sleep for real */
- spin_lock_irq(&worker->lock);
- set_current_state(TASK_INTERRUPTIBLE);
- if (!list_empty(&worker->pending) ||
- !list_empty(&worker->prio_pending)) {
- spin_unlock_irq(&worker->lock);
- set_current_state(TASK_RUNNING);
- goto again;
- }
-
- /*
- * this makes sure we get a wakeup when someone
- * adds something new to the queue
- */
- worker->working = 0;
- spin_unlock_irq(&worker->lock);
-
- if (!kthread_should_stop()) {
- schedule_timeout(HZ * 120);
- if (!worker->working &&
- try_worker_shutdown(worker)) {
- return 0;
- }
- }
- }
- __set_current_state(TASK_RUNNING);
- }
- } while (!kthread_should_stop());
- return 0;
-}
-
-/*
- * this will wait for all the worker threads to shutdown
- */
-void btrfs_stop_workers(struct btrfs_workers *workers)
-{
- struct list_head *cur;
- struct btrfs_worker_thread *worker;
- int can_stop;
-
- spin_lock_irq(&workers->lock);
- workers->stopping = 1;
- list_splice_init(&workers->idle_list, &workers->worker_list);
- while (!list_empty(&workers->worker_list)) {
- cur = workers->worker_list.next;
- worker = list_entry(cur, struct btrfs_worker_thread,
- worker_list);
-
- atomic_inc(&worker->refs);
- workers->num_workers -= 1;
- if (!list_empty(&worker->worker_list)) {
- list_del_init(&worker->worker_list);
- put_worker(worker);
- can_stop = 1;
- } else
- can_stop = 0;
- spin_unlock_irq(&workers->lock);
- if (can_stop)
- kthread_stop(worker->task);
- spin_lock_irq(&workers->lock);
- put_worker(worker);
+ work = container_of(arg, struct btrfs_work, normal_work);
+ /*
+ * We should not touch things inside work in the following cases:
+ * 1) after work->func() if it has no ordered_free
+ * Since the struct is freed in work->func().
+ * 2) after setting WORK_DONE_BIT
+ * The work may be freed in other threads almost instantly.
+ * So we save the needed things here.
+ */
+ if (work->ordered_func)
+ need_order = 1;
+ wq = work->wq;
+
+ trace_btrfs_work_sched(work);
+ thresh_exec_hook(wq);
+ work->func(work);
+ if (need_order) {
+ set_bit(WORK_DONE_BIT, &work->flags);
+ run_ordered_work(wq);
}
- spin_unlock_irq(&workers->lock);
+ if (!need_order)
+ trace_btrfs_all_work_done(work);
}
-/*
- * simple init on struct btrfs_workers
- */
-void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max,
- struct btrfs_workers *async_helper)
+void btrfs_init_work(struct btrfs_work *work,
+ btrfs_func_t func,
+ btrfs_func_t ordered_func,
+ btrfs_func_t ordered_free)
{
- workers->num_workers = 0;
- workers->num_workers_starting = 0;
- INIT_LIST_HEAD(&workers->worker_list);
- INIT_LIST_HEAD(&workers->idle_list);
- INIT_LIST_HEAD(&workers->order_list);
- INIT_LIST_HEAD(&workers->prio_order_list);
- spin_lock_init(&workers->lock);
- spin_lock_init(&workers->order_lock);
- workers->max_workers = max;
- workers->idle_thresh = 32;
- workers->name = name;
- workers->ordered = 0;
- workers->atomic_start_pending = 0;
- workers->atomic_worker_start = async_helper;
- workers->stopping = 0;
+ work->func = func;
+ work->ordered_func = ordered_func;
+ work->ordered_free = ordered_free;
+ INIT_WORK(&work->normal_work, normal_work_helper);
+ INIT_LIST_HEAD(&work->ordered_list);
+ work->flags = 0;
}
-/*
- * starts new worker threads. This does not enforce the max worker
- * count in case you need to temporarily go past it.
- */
-static int __btrfs_start_workers(struct btrfs_workers *workers)
+static inline void __btrfs_queue_work(struct __btrfs_workqueue *wq,
+ struct btrfs_work *work)
{
- struct btrfs_worker_thread *worker;
- int ret = 0;
-
- worker = kzalloc(sizeof(*worker), GFP_NOFS);
- if (!worker) {
- ret = -ENOMEM;
- goto fail;
- }
-
- INIT_LIST_HEAD(&worker->pending);
- INIT_LIST_HEAD(&worker->prio_pending);
- INIT_LIST_HEAD(&worker->worker_list);
- spin_lock_init(&worker->lock);
-
- atomic_set(&worker->num_pending, 0);
- atomic_set(&worker->refs, 1);
- worker->workers = workers;
- worker->task = kthread_create(worker_loop, worker,
- "btrfs-%s-%d", workers->name,
- workers->num_workers + 1);
- if (IS_ERR(worker->task)) {
- ret = PTR_ERR(worker->task);
- goto fail;
- }
+ unsigned long flags;
- spin_lock_irq(&workers->lock);
- if (workers->stopping) {
- spin_unlock_irq(&workers->lock);
- ret = -EINVAL;
- goto fail_kthread;
+ work->wq = wq;
+ thresh_queue_hook(wq);
+ if (work->ordered_func) {
+ spin_lock_irqsave(&wq->list_lock, flags);
+ list_add_tail(&work->ordered_list, &wq->ordered_list);
+ spin_unlock_irqrestore(&wq->list_lock, flags);
}
- list_add_tail(&worker->worker_list, &workers->idle_list);
- worker->idle = 1;
- workers->num_workers++;
- workers->num_workers_starting--;
- WARN_ON(workers->num_workers_starting < 0);
- spin_unlock_irq(&workers->lock);
-
- wake_up_process(worker->task);
- return 0;
-
-fail_kthread:
- kthread_stop(worker->task);
-fail:
- kfree(worker);
- spin_lock_irq(&workers->lock);
- workers->num_workers_starting--;
- spin_unlock_irq(&workers->lock);
- return ret;
+ queue_work(wq->normal_wq, &work->normal_work);
+ trace_btrfs_work_queued(work);
}
-int btrfs_start_workers(struct btrfs_workers *workers)
+void btrfs_queue_work(struct btrfs_workqueue *wq,
+ struct btrfs_work *work)
{
- spin_lock_irq(&workers->lock);
- workers->num_workers_starting++;
- spin_unlock_irq(&workers->lock);
- return __btrfs_start_workers(workers);
-}
-
-/*
- * run through the list and find a worker thread that doesn't have a lot
- * to do right now. This can return null if we aren't yet at the thread
- * count limit and all of the threads are busy.
- */
-static struct btrfs_worker_thread *next_worker(struct btrfs_workers *workers)
-{
- struct btrfs_worker_thread *worker;
- struct list_head *next;
- int enforce_min;
-
- enforce_min = (workers->num_workers + workers->num_workers_starting) <
- workers->max_workers;
-
- /*
- * if we find an idle thread, don't move it to the end of the
- * idle list. This improves the chance that the next submission
- * will reuse the same thread, and maybe catch it while it is still
- * working
- */
- if (!list_empty(&workers->idle_list)) {
- next = workers->idle_list.next;
- worker = list_entry(next, struct btrfs_worker_thread,
- worker_list);
- return worker;
- }
- if (enforce_min || list_empty(&workers->worker_list))
- return NULL;
-
- /*
- * if we pick a busy task, move the task to the end of the list.
- * hopefully this will keep things somewhat evenly balanced.
- * Do the move in batches based on the sequence number. This groups
- * requests submitted at roughly the same time onto the same worker.
- */
- next = workers->worker_list.next;
- worker = list_entry(next, struct btrfs_worker_thread, worker_list);
- worker->sequence++;
+ struct __btrfs_workqueue *dest_wq;
- if (worker->sequence % workers->idle_thresh == 0)
- list_move_tail(next, &workers->worker_list);
- return worker;
+ if (test_bit(WORK_HIGH_PRIO_BIT, &work->flags) && wq->high)
+ dest_wq = wq->high;
+ else
+ dest_wq = wq->normal;
+ __btrfs_queue_work(dest_wq, work);
}
-/*
- * selects a worker thread to take the next job. This will either find
- * an idle worker, start a new worker up to the max count, or just return
- * one of the existing busy workers.
- */
-static struct btrfs_worker_thread *find_worker(struct btrfs_workers *workers)
+static inline void
+__btrfs_destroy_workqueue(struct __btrfs_workqueue *wq)
{
- struct btrfs_worker_thread *worker;
- unsigned long flags;
- struct list_head *fallback;
- int ret;
-
- spin_lock_irqsave(&workers->lock, flags);
-again:
- worker = next_worker(workers);
-
- if (!worker) {
- if (workers->num_workers + workers->num_workers_starting >=
- workers->max_workers) {
- goto fallback;
- } else if (workers->atomic_worker_start) {
- workers->atomic_start_pending = 1;
- goto fallback;
- } else {
- workers->num_workers_starting++;
- spin_unlock_irqrestore(&workers->lock, flags);
- /* we're below the limit, start another worker */
- ret = __btrfs_start_workers(workers);
- spin_lock_irqsave(&workers->lock, flags);
- if (ret)
- goto fallback;
- goto again;
- }
- }
- goto found;
-
-fallback:
- fallback = NULL;
- /*
- * we have failed to find any workers, just
- * return the first one we can find.
- */
- if (!list_empty(&workers->worker_list))
- fallback = workers->worker_list.next;
- if (!list_empty(&workers->idle_list))
- fallback = workers->idle_list.next;
- BUG_ON(!fallback);
- worker = list_entry(fallback,
- struct btrfs_worker_thread, worker_list);
-found:
- /*
- * this makes sure the worker doesn't exit before it is placed
- * onto a busy/idle list
- */
- atomic_inc(&worker->num_pending);
- spin_unlock_irqrestore(&workers->lock, flags);
- return worker;
+ destroy_workqueue(wq->normal_wq);
+ trace_btrfs_workqueue_destroy(wq);
+ kfree(wq);
}
-/*
- * btrfs_requeue_work just puts the work item back on the tail of the list
- * it was taken from. It is intended for use with long running work functions
- * that make some progress and want to give the cpu up for others.
- */
-void btrfs_requeue_work(struct btrfs_work *work)
+void btrfs_destroy_workqueue(struct btrfs_workqueue *wq)
{
- struct btrfs_worker_thread *worker = work->worker;
- unsigned long flags;
- int wake = 0;
-
- if (test_and_set_bit(WORK_QUEUED_BIT, &work->flags))
+ if (!wq)
return;
-
- spin_lock_irqsave(&worker->lock, flags);
- if (test_bit(WORK_HIGH_PRIO_BIT, &work->flags))
- list_add_tail(&work->list, &worker->prio_pending);
- else
- list_add_tail(&work->list, &worker->pending);
- atomic_inc(&worker->num_pending);
-
- /* by definition we're busy, take ourselves off the idle
- * list
- */
- if (worker->idle) {
- spin_lock(&worker->workers->lock);
- worker->idle = 0;
- list_move_tail(&worker->worker_list,
- &worker->workers->worker_list);
- spin_unlock(&worker->workers->lock);
- }
- if (!worker->working) {
- wake = 1;
- worker->working = 1;
- }
-
- if (wake)
- wake_up_process(worker->task);
- spin_unlock_irqrestore(&worker->lock, flags);
+ if (wq->high)
+ __btrfs_destroy_workqueue(wq->high);
+ __btrfs_destroy_workqueue(wq->normal);
+ kfree(wq);
}
-void btrfs_set_work_high_prio(struct btrfs_work *work)
+void btrfs_workqueue_set_max(struct btrfs_workqueue *wq, int max)
{
- set_bit(WORK_HIGH_PRIO_BIT, &work->flags);
+ wq->normal->max_active = max;
+ if (wq->high)
+ wq->high->max_active = max;
}
-/*
- * places a struct btrfs_work into the pending queue of one of the kthreads
- */
-void btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work)
+void btrfs_set_work_high_priority(struct btrfs_work *work)
{
- struct btrfs_worker_thread *worker;
- unsigned long flags;
- int wake = 0;
-
- /* don't requeue something already on a list */
- if (test_and_set_bit(WORK_QUEUED_BIT, &work->flags))
- return;
-
- worker = find_worker(workers);
- if (workers->ordered) {
- /*
- * you're not allowed to do ordered queues from an
- * interrupt handler
- */
- spin_lock(&workers->order_lock);
- if (test_bit(WORK_HIGH_PRIO_BIT, &work->flags)) {
- list_add_tail(&work->order_list,
- &workers->prio_order_list);
- } else {
- list_add_tail(&work->order_list, &workers->order_list);
- }
- spin_unlock(&workers->order_lock);
- } else {
- INIT_LIST_HEAD(&work->order_list);
- }
-
- spin_lock_irqsave(&worker->lock, flags);
-
- if (test_bit(WORK_HIGH_PRIO_BIT, &work->flags))
- list_add_tail(&work->list, &worker->prio_pending);
- else
- list_add_tail(&work->list, &worker->pending);
- check_busy_worker(worker);
-
- /*
- * avoid calling into wake_up_process if this thread has already
- * been kicked
- */
- if (!worker->working)
- wake = 1;
- worker->working = 1;
-
- if (wake)
- wake_up_process(worker->task);
- spin_unlock_irqrestore(&worker->lock, flags);
+ set_bit(WORK_HIGH_PRIO_BIT, &work->flags);
}
diff --git a/fs/btrfs/async-thread.h b/fs/btrfs/async-thread.h
index 1f26792683ed..9c6b66d15fb0 100644
--- a/fs/btrfs/async-thread.h
+++ b/fs/btrfs/async-thread.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2014 Fujitsu. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
@@ -19,103 +20,35 @@
#ifndef __BTRFS_ASYNC_THREAD_
#define __BTRFS_ASYNC_THREAD_
-struct btrfs_worker_thread;
+struct btrfs_workqueue;
+/* Internal use only */
+struct __btrfs_workqueue;
+struct btrfs_work;
+typedef void (*btrfs_func_t)(struct btrfs_work *arg);
-/*
- * This is similar to a workqueue, but it is meant to spread the operations
- * across all available cpus instead of just the CPU that was used to
- * queue the work. There is also some batching introduced to try and
- * cut down on context switches.
- *
- * By default threads are added on demand up to 2 * the number of cpus.
- * Changing struct btrfs_workers->max_workers is one way to prevent
- * demand creation of kthreads.
- *
- * the basic model of these worker threads is to embed a btrfs_work
- * structure in your own data struct, and use container_of in a
- * work function to get back to your data struct.
- */
struct btrfs_work {
- /*
- * func should be set to the function you want called
- * your work struct is passed as the only arg
- *
- * ordered_func must be set for work sent to an ordered work queue,
- * and it is called to complete a given work item in the same
- * order they were sent to the queue.
- */
- void (*func)(struct btrfs_work *work);
- void (*ordered_func)(struct btrfs_work *work);
- void (*ordered_free)(struct btrfs_work *work);
-
- /*
- * flags should be set to zero. It is used to make sure the
- * struct is only inserted once into the list.
- */
+ btrfs_func_t func;
+ btrfs_func_t ordered_func;
+ btrfs_func_t ordered_free;
+
+ /* Don't touch things below */
+ struct work_struct normal_work;
+ struct list_head ordered_list;
+ struct __btrfs_workqueue *wq;
unsigned long flags;
-
- /* don't touch these */
- struct btrfs_worker_thread *worker;
- struct list_head list;
- struct list_head order_list;
-};
-
-struct btrfs_workers {
- /* current number of running workers */
- int num_workers;
-
- int num_workers_starting;
-
- /* max number of workers allowed. changed by btrfs_start_workers */
- int max_workers;
-
- /* once a worker has this many requests or fewer, it is idle */
- int idle_thresh;
-
- /* force completions in the order they were queued */
- int ordered;
-
- /* more workers required, but in an interrupt handler */
- int atomic_start_pending;
-
- /*
- * are we allowed to sleep while starting workers or are we required
- * to start them at a later time? If we can't sleep, this indicates
- * which queue we need to use to schedule thread creation.
- */
- struct btrfs_workers *atomic_worker_start;
-
- /* list with all the work threads. The workers on the idle thread
- * may be actively servicing jobs, but they haven't yet hit the
- * idle thresh limit above.
- */
- struct list_head worker_list;
- struct list_head idle_list;
-
- /*
- * when operating in ordered mode, this maintains the list
- * of work items waiting for completion
- */
- struct list_head order_list;
- struct list_head prio_order_list;
-
- /* lock for finding the next worker thread to queue on */
- spinlock_t lock;
-
- /* lock for the ordered lists */
- spinlock_t order_lock;
-
- /* extra name for this worker, used for current->name */
- char *name;
-
- int stopping;
};
-void btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work);
-int btrfs_start_workers(struct btrfs_workers *workers);
-void btrfs_stop_workers(struct btrfs_workers *workers);
-void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max,
- struct btrfs_workers *async_starter);
-void btrfs_requeue_work(struct btrfs_work *work);
-void btrfs_set_work_high_prio(struct btrfs_work *work);
+struct btrfs_workqueue *btrfs_alloc_workqueue(const char *name,
+ int flags,
+ int max_active,
+ int thresh);
+void btrfs_init_work(struct btrfs_work *work,
+ btrfs_func_t func,
+ btrfs_func_t ordered_func,
+ btrfs_func_t ordered_free);
+void btrfs_queue_work(struct btrfs_workqueue *wq,
+ struct btrfs_work *work);
+void btrfs_destroy_workqueue(struct btrfs_workqueue *wq);
+void btrfs_workqueue_set_max(struct btrfs_workqueue *wq, int max);
+void btrfs_set_work_high_priority(struct btrfs_work *work);
#endif
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index aded3ef3d3d4..aad7201ad11b 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -220,7 +220,8 @@ static int __add_prelim_ref(struct list_head *head, u64 root_id,
static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
struct ulist *parents, struct __prelim_ref *ref,
- int level, u64 time_seq, const u64 *extent_item_pos)
+ int level, u64 time_seq, const u64 *extent_item_pos,
+ u64 total_refs)
{
int ret = 0;
int slot;
@@ -249,7 +250,7 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
if (path->slots[0] >= btrfs_header_nritems(path->nodes[0]))
ret = btrfs_next_old_leaf(root, path, time_seq);
- while (!ret && count < ref->count) {
+ while (!ret && count < total_refs) {
eb = path->nodes[0];
slot = path->slots[0];
@@ -306,7 +307,7 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
struct btrfs_path *path, u64 time_seq,
struct __prelim_ref *ref,
struct ulist *parents,
- const u64 *extent_item_pos)
+ const u64 *extent_item_pos, u64 total_refs)
{
struct btrfs_root *root;
struct btrfs_key root_key;
@@ -361,7 +362,7 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
}
ret = add_all_parents(root, path, parents, ref, level, time_seq,
- extent_item_pos);
+ extent_item_pos, total_refs);
out:
path->lowest_level = 0;
btrfs_release_path(path);
@@ -374,7 +375,7 @@ out:
static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
struct btrfs_path *path, u64 time_seq,
struct list_head *head,
- const u64 *extent_item_pos)
+ const u64 *extent_item_pos, u64 total_refs)
{
int err;
int ret = 0;
@@ -400,7 +401,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
if (ref->count == 0)
continue;
err = __resolve_indirect_ref(fs_info, path, time_seq, ref,
- parents, extent_item_pos);
+ parents, extent_item_pos,
+ total_refs);
/*
* we can only tolerate ENOENT,otherwise,we should catch error
* and return directly.
@@ -557,7 +559,7 @@ static void __merge_refs(struct list_head *head, int mode)
* smaller or equal that seq to the list
*/
static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
- struct list_head *prefs)
+ struct list_head *prefs, u64 *total_refs)
{
struct btrfs_delayed_extent_op *extent_op = head->extent_op;
struct rb_node *n = &head->node.rb_node;
@@ -593,6 +595,7 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
default:
BUG_ON(1);
}
+ *total_refs += (node->ref_mod * sgn);
switch (node->type) {
case BTRFS_TREE_BLOCK_REF_KEY: {
struct btrfs_delayed_tree_ref *ref;
@@ -653,7 +656,8 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
*/
static int __add_inline_refs(struct btrfs_fs_info *fs_info,
struct btrfs_path *path, u64 bytenr,
- int *info_level, struct list_head *prefs)
+ int *info_level, struct list_head *prefs,
+ u64 *total_refs)
{
int ret = 0;
int slot;
@@ -677,6 +681,7 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
flags = btrfs_extent_flags(leaf, ei);
+ *total_refs += btrfs_extent_refs(leaf, ei);
btrfs_item_key_to_cpu(leaf, &found_key, slot);
ptr = (unsigned long)(ei + 1);
@@ -859,6 +864,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
struct list_head prefs;
struct __prelim_ref *ref;
struct extent_inode_elem *eie = NULL;
+ u64 total_refs = 0;
INIT_LIST_HEAD(&prefs);
INIT_LIST_HEAD(&prefs_delayed);
@@ -873,8 +879,10 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
- if (!trans)
+ if (!trans) {
path->search_commit_root = 1;
+ path->skip_locking = 1;
+ }
/*
* grab both a lock on the path and a lock on the delayed ref head.
@@ -915,7 +923,7 @@ again:
}
spin_unlock(&delayed_refs->lock);
ret = __add_delayed_refs(head, time_seq,
- &prefs_delayed);
+ &prefs_delayed, &total_refs);
mutex_unlock(&head->mutex);
if (ret)
goto out;
@@ -936,7 +944,8 @@ again:
(key.type == BTRFS_EXTENT_ITEM_KEY ||
key.type == BTRFS_METADATA_ITEM_KEY)) {
ret = __add_inline_refs(fs_info, path, bytenr,
- &info_level, &prefs);
+ &info_level, &prefs,
+ &total_refs);
if (ret)
goto out;
ret = __add_keyed_refs(fs_info, path, bytenr,
@@ -956,7 +965,7 @@ again:
__merge_refs(&prefs, 1);
ret = __resolve_indirect_refs(fs_info, path, time_seq, &prefs,
- extent_item_pos);
+ extent_item_pos, total_refs);
if (ret)
goto out;
@@ -965,7 +974,7 @@ again:
while (!list_empty(&prefs)) {
ref = list_first_entry(&prefs, struct __prelim_ref, list);
WARN_ON(ref->count < 0);
- if (ref->count && ref->root_id && ref->parent == 0) {
+ if (roots && ref->count && ref->root_id && ref->parent == 0) {
/* no parent == root of tree */
ret = ulist_add(roots, ref->root_id, 0, GFP_NOFS);
if (ret < 0)
@@ -1061,22 +1070,14 @@ static int btrfs_find_all_leafs(struct btrfs_trans_handle *trans,
u64 time_seq, struct ulist **leafs,
const u64 *extent_item_pos)
{
- struct ulist *tmp;
int ret;
- tmp = ulist_alloc(GFP_NOFS);
- if (!tmp)
- return -ENOMEM;
*leafs = ulist_alloc(GFP_NOFS);
- if (!*leafs) {
- ulist_free(tmp);
+ if (!*leafs)
return -ENOMEM;
- }
ret = find_parent_nodes(trans, fs_info, bytenr,
- time_seq, *leafs, tmp, extent_item_pos);
- ulist_free(tmp);
-
+ time_seq, *leafs, NULL, extent_item_pos);
if (ret < 0 && ret != -ENOENT) {
free_leaf_list(*leafs);
return ret;
@@ -1333,38 +1334,13 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
if (ret < 0)
return ret;
- while (1) {
- u32 nritems;
- if (path->slots[0] == 0) {
- btrfs_set_path_blocking(path);
- ret = btrfs_prev_leaf(fs_info->extent_root, path);
- if (ret != 0) {
- if (ret > 0) {
- pr_debug("logical %llu is not within "
- "any extent\n", logical);
- ret = -ENOENT;
- }
- return ret;
- }
- } else {
- path->slots[0]--;
- }
- nritems = btrfs_header_nritems(path->nodes[0]);
- if (nritems == 0) {
- pr_debug("logical %llu is not within any extent\n",
- logical);
- return -ENOENT;
- }
- if (path->slots[0] == nritems)
- path->slots[0]--;
-
- btrfs_item_key_to_cpu(path->nodes[0], found_key,
- path->slots[0]);
- if (found_key->type == BTRFS_EXTENT_ITEM_KEY ||
- found_key->type == BTRFS_METADATA_ITEM_KEY)
- break;
+ ret = btrfs_previous_extent_item(fs_info->extent_root, path, 0);
+ if (ret) {
+ if (ret > 0)
+ ret = -ENOENT;
+ return ret;
}
-
+ btrfs_item_key_to_cpu(path->nodes[0], found_key, path->slots[0]);
if (found_key->type == BTRFS_METADATA_ITEM_KEY)
size = fs_info->extent_root->leafsize;
else if (found_key->type == BTRFS_EXTENT_ITEM_KEY)
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 8fed2125689e..c9a24444ec9a 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -109,14 +109,17 @@ struct btrfs_inode {
u64 last_trans;
/*
- * log transid when this inode was last modified
+ * transid that last logged this inode
*/
- u64 last_sub_trans;
+ u64 logged_trans;
/*
- * transid that last logged this inode
+ * log transid when this inode was last modified
*/
- u64 logged_trans;
+ int last_sub_trans;
+
+ /* a local copy of root's last_log_commit */
+ int last_log_commit;
/* total number of bytes pending delalloc, used by stat to calc the
* real block usage of the file
@@ -155,9 +158,6 @@ struct btrfs_inode {
/* flags field from the on disk inode */
u32 flags;
- /* a local copy of root's last_log_commit */
- unsigned long last_log_commit;
-
/*
* Counters to keep track of the number of extent item's we may use due
* to delalloc and such. outstanding_extents is the number of extent
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index b01fb6c527e3..d43c544d3b68 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -472,7 +472,7 @@ static noinline int add_ra_bio_pages(struct inode *inode,
rcu_read_lock();
page = radix_tree_lookup(&mapping->page_tree, pg_index);
rcu_read_unlock();
- if (page) {
+ if (page && !radix_tree_exceptional_entry(page)) {
misses++;
if (misses > 4)
break;
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index cbd3a7d6fa68..88d1b1eedc9c 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -5376,6 +5376,8 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
int advance_right;
u64 left_blockptr;
u64 right_blockptr;
+ u64 left_gen;
+ u64 right_gen;
u64 left_start_ctransid;
u64 right_start_ctransid;
u64 ctransid;
@@ -5640,7 +5642,14 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
right_blockptr = btrfs_node_blockptr(
right_path->nodes[right_level],
right_path->slots[right_level]);
- if (left_blockptr == right_blockptr) {
+ left_gen = btrfs_node_ptr_generation(
+ left_path->nodes[left_level],
+ left_path->slots[left_level]);
+ right_gen = btrfs_node_ptr_generation(
+ right_path->nodes[right_level],
+ right_path->slots[right_level]);
+ if (left_blockptr == right_blockptr &&
+ left_gen == right_gen) {
/*
* As we're on a shared block, don't
* allow to go deeper.
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 2c1a42ca519f..bc96c03dd259 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -351,6 +351,7 @@ static inline unsigned long btrfs_chunk_item_size(int num_stripes)
#define BTRFS_FS_STATE_ERROR 0
#define BTRFS_FS_STATE_REMOUNTING 1
#define BTRFS_FS_STATE_TRANS_ABORTED 2
+#define BTRFS_FS_STATE_DEV_REPLACING 3
/* Super block flags */
/* Errors detected */
@@ -1489,6 +1490,7 @@ struct btrfs_fs_info {
*/
struct list_head ordered_roots;
+ struct mutex delalloc_root_mutex;
spinlock_t delalloc_root_lock;
/* all fs/file tree roots that have delalloc inodes. */
struct list_head delalloc_roots;
@@ -1503,28 +1505,27 @@ struct btrfs_fs_info {
* A third pool does submit_bio to avoid deadlocking with the other
* two
*/
- struct btrfs_workers generic_worker;
- struct btrfs_workers workers;
- struct btrfs_workers delalloc_workers;
- struct btrfs_workers flush_workers;
- struct btrfs_workers endio_workers;
- struct btrfs_workers endio_meta_workers;
- struct btrfs_workers endio_raid56_workers;
- struct btrfs_workers rmw_workers;
- struct btrfs_workers endio_meta_write_workers;
- struct btrfs_workers endio_write_workers;
- struct btrfs_workers endio_freespace_worker;
- struct btrfs_workers submit_workers;
- struct btrfs_workers caching_workers;
- struct btrfs_workers readahead_workers;
+ struct btrfs_workqueue *workers;
+ struct btrfs_workqueue *delalloc_workers;
+ struct btrfs_workqueue *flush_workers;
+ struct btrfs_workqueue *endio_workers;
+ struct btrfs_workqueue *endio_meta_workers;
+ struct btrfs_workqueue *endio_raid56_workers;
+ struct btrfs_workqueue *rmw_workers;
+ struct btrfs_workqueue *endio_meta_write_workers;
+ struct btrfs_workqueue *endio_write_workers;
+ struct btrfs_workqueue *endio_freespace_worker;
+ struct btrfs_workqueue *submit_workers;
+ struct btrfs_workqueue *caching_workers;
+ struct btrfs_workqueue *readahead_workers;
/*
* fixup workers take dirty pages that didn't properly go through
* the cow mechanism and make them safe to write. It happens
* for the sys_munmap function call path
*/
- struct btrfs_workers fixup_workers;
- struct btrfs_workers delayed_workers;
+ struct btrfs_workqueue *fixup_workers;
+ struct btrfs_workqueue *delayed_workers;
struct task_struct *transaction_kthread;
struct task_struct *cleaner_kthread;
int thread_pool_size;
@@ -1604,9 +1605,9 @@ struct btrfs_fs_info {
atomic_t scrub_cancel_req;
wait_queue_head_t scrub_pause_wait;
int scrub_workers_refcnt;
- struct btrfs_workers scrub_workers;
- struct btrfs_workers scrub_wr_completion_workers;
- struct btrfs_workers scrub_nocow_workers;
+ struct btrfs_workqueue *scrub_workers;
+ struct btrfs_workqueue *scrub_wr_completion_workers;
+ struct btrfs_workqueue *scrub_nocow_workers;
#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
u32 check_integrity_print_mask;
@@ -1647,7 +1648,7 @@ struct btrfs_fs_info {
/* qgroup rescan items */
struct mutex qgroup_rescan_lock; /* protects the progress item */
struct btrfs_key qgroup_rescan_progress;
- struct btrfs_workers qgroup_rescan_workers;
+ struct btrfs_workqueue *qgroup_rescan_workers;
struct completion qgroup_rescan_completion;
struct btrfs_work qgroup_rescan_work;
@@ -1674,10 +1675,18 @@ struct btrfs_fs_info {
atomic_t mutually_exclusive_operation_running;
+ struct percpu_counter bio_counter;
+ wait_queue_head_t replace_wait;
+
struct semaphore uuid_tree_rescan_sem;
unsigned int update_uuid_tree_gen:1;
};
+struct btrfs_subvolume_writers {
+ struct percpu_counter counter;
+ wait_queue_head_t wait;
+};
+
/*
* in ram representation of the tree. extent_root is used for all allocations
* and for the extent tree extent_root root.
@@ -1714,11 +1723,15 @@ struct btrfs_root {
struct mutex log_mutex;
wait_queue_head_t log_writer_wait;
wait_queue_head_t log_commit_wait[2];
+ struct list_head log_ctxs[2];
atomic_t log_writers;
atomic_t log_commit[2];
atomic_t log_batch;
- unsigned long log_transid;
- unsigned long last_log_commit;
+ int log_transid;
+ /* No matter the commit succeeds or not*/
+ int log_transid_committed;
+ /* Just be updated when the commit succeeds. */
+ int last_log_commit;
pid_t log_start_pid;
bool log_multiple_pids;
@@ -1793,6 +1806,7 @@ struct btrfs_root {
spinlock_t root_item_lock;
atomic_t refs;
+ struct mutex delalloc_mutex;
spinlock_t delalloc_lock;
/*
* all of the inodes that have delalloc bytes. It is possible for
@@ -1802,6 +1816,8 @@ struct btrfs_root {
struct list_head delalloc_inodes;
struct list_head delalloc_root;
u64 nr_delalloc_inodes;
+
+ struct mutex ordered_extent_mutex;
/*
* this is used by the balancing code to wait for all the pending
* ordered extents
@@ -1822,6 +1838,8 @@ struct btrfs_root {
* manipulation with the read-only status via SUBVOL_SETFLAGS
*/
int send_in_progress;
+ struct btrfs_subvolume_writers *subv_writers;
+ atomic_t will_be_snapshoted;
};
struct btrfs_ioctl_defrag_range_args {
@@ -3346,6 +3364,9 @@ int btrfs_init_space_info(struct btrfs_fs_info *fs_info);
int btrfs_delayed_refs_qgroup_accounting(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info);
int __get_raid_index(u64 flags);
+
+int btrfs_start_nocow_write(struct btrfs_root *root);
+void btrfs_end_nocow_write(struct btrfs_root *root);
/* ctree.c */
int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key,
int level, int *slot);
@@ -3723,7 +3744,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
u32 min_type);
int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput);
-int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput);
+int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput,
+ int nr);
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end,
struct extent_state **cached_state);
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
@@ -4005,6 +4027,11 @@ int btrfs_scrub_cancel_dev(struct btrfs_fs_info *info,
int btrfs_scrub_progress(struct btrfs_root *root, u64 devid,
struct btrfs_scrub_progress *progress);
+/* dev-replace.c */
+void btrfs_bio_counter_inc_blocked(struct btrfs_fs_info *fs_info);
+void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info);
+void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info);
+
/* reada.c */
struct reada_control {
struct btrfs_root *root; /* tree to prefetch */
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index 451b00c86f6c..33e561a84013 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1392,11 +1392,11 @@ static int btrfs_wq_run_delayed_node(struct btrfs_delayed_root *delayed_root,
return -ENOMEM;
async_work->delayed_root = delayed_root;
- async_work->work.func = btrfs_async_run_delayed_root;
- async_work->work.flags = 0;
+ btrfs_init_work(&async_work->work, btrfs_async_run_delayed_root,
+ NULL, NULL);
async_work->nr = nr;
- btrfs_queue_worker(&root->fs_info->delayed_workers, &async_work->work);
+ btrfs_queue_work(root->fs_info->delayed_workers, &async_work->work);
return 0;
}
diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c
index f3bff89eecf0..31299646024d 100644
--- a/fs/btrfs/delayed-ref.c
+++ b/fs/btrfs/delayed-ref.c
@@ -199,44 +199,31 @@ static struct btrfs_delayed_ref_head *htree_insert(struct rb_root *root,
*/
static struct btrfs_delayed_ref_head *
find_ref_head(struct rb_root *root, u64 bytenr,
- struct btrfs_delayed_ref_head **last, int return_bigger)
+ int return_bigger)
{
struct rb_node *n;
struct btrfs_delayed_ref_head *entry;
- int cmp = 0;
-again:
n = root->rb_node;
entry = NULL;
while (n) {
entry = rb_entry(n, struct btrfs_delayed_ref_head, href_node);
- if (last)
- *last = entry;
if (bytenr < entry->node.bytenr)
- cmp = -1;
- else if (bytenr > entry->node.bytenr)
- cmp = 1;
- else
- cmp = 0;
-
- if (cmp < 0)
n = n->rb_left;
- else if (cmp > 0)
+ else if (bytenr > entry->node.bytenr)
n = n->rb_right;
else
return entry;
}
if (entry && return_bigger) {
- if (cmp > 0) {
+ if (bytenr > entry->node.bytenr) {
n = rb_next(&entry->href_node);
if (!n)
n = rb_first(root);
entry = rb_entry(n, struct btrfs_delayed_ref_head,
href_node);
- bytenr = entry->node.bytenr;
- return_bigger = 0;
- goto again;
+ return entry;
}
return entry;
}
@@ -415,12 +402,12 @@ btrfs_select_ref_head(struct btrfs_trans_handle *trans)
again:
start = delayed_refs->run_delayed_start;
- head = find_ref_head(&delayed_refs->href_root, start, NULL, 1);
+ head = find_ref_head(&delayed_refs->href_root, start, 1);
if (!head && !loop) {
delayed_refs->run_delayed_start = 0;
start = 0;
loop = true;
- head = find_ref_head(&delayed_refs->href_root, start, NULL, 1);
+ head = find_ref_head(&delayed_refs->href_root, start, 1);
if (!head)
return NULL;
} else if (!head && loop) {
@@ -508,6 +495,7 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing,
ref = btrfs_delayed_node_to_head(update);
BUG_ON(existing_ref->is_data != ref->is_data);
+ spin_lock(&existing_ref->lock);
if (ref->must_insert_reserved) {
/* if the extent was freed and then
* reallocated before the delayed ref
@@ -549,7 +537,6 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing,
* only need the lock for this case cause we could be processing it
* currently, for refs we just added we know we're a-ok.
*/
- spin_lock(&existing_ref->lock);
existing->ref_mod += update->ref_mod;
spin_unlock(&existing_ref->lock);
}
@@ -898,7 +885,7 @@ btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr)
struct btrfs_delayed_ref_root *delayed_refs;
delayed_refs = &trans->transaction->delayed_refs;
- return find_ref_head(&delayed_refs->href_root, bytenr, NULL, 0);
+ return find_ref_head(&delayed_refs->href_root, bytenr, 0);
}
void btrfs_delayed_ref_exit(void)
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c
index 564c92638b20..9f2290509aca 100644
--- a/fs/btrfs/dev-replace.c
+++ b/fs/btrfs/dev-replace.c
@@ -431,6 +431,35 @@ leave_no_lock:
return ret;
}
+/*
+ * blocked until all flighting bios are finished.
+ */
+static void btrfs_rm_dev_replace_blocked(struct btrfs_fs_info *fs_info)
+{
+ s64 writers;
+ DEFINE_WAIT(wait);
+
+ set_bit(BTRFS_FS_STATE_DEV_REPLACING, &fs_info->fs_state);
+ do {
+ prepare_to_wait(&fs_info->replace_wait, &wait,
+ TASK_UNINTERRUPTIBLE);
+ writers = percpu_counter_sum(&fs_info->bio_counter);
+ if (writers)
+ schedule();
+ finish_wait(&fs_info->replace_wait, &wait);
+ } while (writers);
+}
+
+/*
+ * we have removed target device, it is safe to allow new bios request.
+ */
+static void btrfs_rm_dev_replace_unblocked(struct btrfs_fs_info *fs_info)
+{
+ clear_bit(BTRFS_FS_STATE_DEV_REPLACING, &fs_info->fs_state);
+ if (waitqueue_active(&fs_info->replace_wait))
+ wake_up(&fs_info->replace_wait);
+}
+
static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
int scrub_ret)
{
@@ -458,17 +487,11 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
src_device = dev_replace->srcdev;
btrfs_dev_replace_unlock(dev_replace);
- /* replace old device with new one in mapping tree */
- if (!scrub_ret)
- btrfs_dev_replace_update_device_in_mapping_tree(fs_info,
- src_device,
- tgt_device);
-
/*
* flush all outstanding I/O and inode extent mappings before the
* copy operation is declared as being finished
*/
- ret = btrfs_start_delalloc_roots(root->fs_info, 0);
+ ret = btrfs_start_delalloc_roots(root->fs_info, 0, -1);
if (ret) {
mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
return ret;
@@ -484,6 +507,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
WARN_ON(ret);
/* keep away write_all_supers() during the finishing procedure */
+ mutex_lock(&root->fs_info->chunk_mutex);
mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
btrfs_dev_replace_lock(dev_replace);
dev_replace->replace_state =
@@ -494,7 +518,12 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
dev_replace->time_stopped = get_seconds();
dev_replace->item_needs_writeback = 1;
- if (scrub_ret) {
+ /* replace old device with new one in mapping tree */
+ if (!scrub_ret) {
+ btrfs_dev_replace_update_device_in_mapping_tree(fs_info,
+ src_device,
+ tgt_device);
+ } else {
printk_in_rcu(KERN_ERR
"BTRFS: btrfs_scrub_dev(%s, %llu, %s) failed %d\n",
src_device->missing ? "<missing disk>" :
@@ -503,6 +532,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
rcu_str_deref(tgt_device->name), scrub_ret);
btrfs_dev_replace_unlock(dev_replace);
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
+ mutex_unlock(&root->fs_info->chunk_mutex);
if (tgt_device)
btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device);
mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
@@ -532,8 +562,12 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
fs_info->fs_devices->latest_bdev = tgt_device->bdev;
list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list);
+ btrfs_rm_dev_replace_blocked(fs_info);
+
btrfs_rm_dev_replace_srcdev(fs_info, src_device);
+ btrfs_rm_dev_replace_unblocked(fs_info);
+
/*
* this is again a consistent state where no dev_replace procedure
* is running, the target device is part of the filesystem, the
@@ -543,6 +577,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
*/
btrfs_dev_replace_unlock(dev_replace);
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
+ mutex_unlock(&root->fs_info->chunk_mutex);
/* write back the superblocks */
trans = btrfs_start_transaction(root, 0);
@@ -862,3 +897,31 @@ void btrfs_dev_replace_unlock(struct btrfs_dev_replace *dev_replace)
mutex_unlock(&dev_replace->lock_management_lock);
}
}
+
+void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info)
+{
+ percpu_counter_inc(&fs_info->bio_counter);
+}
+
+void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info)
+{
+ percpu_counter_dec(&fs_info->bio_counter);
+
+ if (waitqueue_active(&fs_info->replace_wait))
+ wake_up(&fs_info->replace_wait);
+}
+
+void btrfs_bio_counter_inc_blocked(struct btrfs_fs_info *fs_info)
+{
+ DEFINE_WAIT(wait);
+again:
+ percpu_counter_inc(&fs_info->bio_counter);
+ if (test_bit(BTRFS_FS_STATE_DEV_REPLACING, &fs_info->fs_state)) {
+ btrfs_bio_counter_dec(fs_info);
+ wait_event(fs_info->replace_wait,
+ !test_bit(BTRFS_FS_STATE_DEV_REPLACING,
+ &fs_info->fs_state));
+ goto again;
+ }
+
+}
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 81ea55314b1f..bd0f752b797b 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -678,32 +678,31 @@ static void end_workqueue_bio(struct bio *bio, int err)
fs_info = end_io_wq->info;
end_io_wq->error = err;
- end_io_wq->work.func = end_workqueue_fn;
- end_io_wq->work.flags = 0;
+ btrfs_init_work(&end_io_wq->work, end_workqueue_fn, NULL, NULL);
if (bio->bi_rw & REQ_WRITE) {
if (end_io_wq->metadata == BTRFS_WQ_ENDIO_METADATA)
- btrfs_queue_worker(&fs_info->endio_meta_write_workers,
- &end_io_wq->work);
+ btrfs_queue_work(fs_info->endio_meta_write_workers,
+ &end_io_wq->work);
else if (end_io_wq->metadata == BTRFS_WQ_ENDIO_FREE_SPACE)
- btrfs_queue_worker(&fs_info->endio_freespace_worker,
- &end_io_wq->work);
+ btrfs_queue_work(fs_info->endio_freespace_worker,
+ &end_io_wq->work);
else if (end_io_wq->metadata == BTRFS_WQ_ENDIO_RAID56)
- btrfs_queue_worker(&fs_info->endio_raid56_workers,
- &end_io_wq->work);
+ btrfs_queue_work(fs_info->endio_raid56_workers,
+ &end_io_wq->work);
else
- btrfs_queue_worker(&fs_info->endio_write_workers,
- &end_io_wq->work);
+ btrfs_queue_work(fs_info->endio_write_workers,
+ &end_io_wq->work);
} else {
if (end_io_wq->metadata == BTRFS_WQ_ENDIO_RAID56)
- btrfs_queue_worker(&fs_info->endio_raid56_workers,
- &end_io_wq->work);
+ btrfs_queue_work(fs_info->endio_raid56_workers,
+ &end_io_wq->work);
else if (end_io_wq->metadata)
- btrfs_queue_worker(&fs_info->endio_meta_workers,
- &end_io_wq->work);
+ btrfs_queue_work(fs_info->endio_meta_workers,
+ &end_io_wq->work);
else
- btrfs_queue_worker(&fs_info->endio_workers,
- &end_io_wq->work);
+ btrfs_queue_work(fs_info->endio_workers,
+ &end_io_wq->work);
}
}
@@ -738,7 +737,7 @@ int btrfs_bio_wq_end_io(struct btrfs_fs_info *info, struct bio *bio,
unsigned long btrfs_async_submit_limit(struct btrfs_fs_info *info)
{
unsigned long limit = min_t(unsigned long,
- info->workers.max_workers,
+ info->thread_pool_size,
info->fs_devices->open_devices);
return 256 * limit;
}
@@ -811,11 +810,9 @@ int btrfs_wq_submit_bio(struct btrfs_fs_info *fs_info, struct inode *inode,
async->submit_bio_start = submit_bio_start;
async->submit_bio_done = submit_bio_done;
- async->work.func = run_one_async_start;
- async->work.ordered_func = run_one_async_done;
- async->work.ordered_free = run_one_async_free;
+ btrfs_init_work(&async->work, run_one_async_start,
+ run_one_async_done, run_one_async_free);
- async->work.flags = 0;
async->bio_flags = bio_flags;
async->bio_offset = bio_offset;
@@ -824,9 +821,9 @@ int btrfs_wq_submit_bio(struct btrfs_fs_info *fs_info, struct inode *inode,
atomic_inc(&fs_info->nr_async_submits);
if (rw & REQ_SYNC)
- btrfs_set_work_high_prio(&async->work);
+ btrfs_set_work_high_priority(&async->work);
- btrfs_queue_worker(&fs_info->workers, &async->work);
+ btrfs_queue_work(fs_info->workers, &async->work);
while (atomic_read(&fs_info->async_submit_draining) &&
atomic_read(&fs_info->nr_async_submits)) {
@@ -1149,6 +1146,32 @@ void clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
}
}
+static struct btrfs_subvolume_writers *btrfs_alloc_subvolume_writers(void)
+{
+ struct btrfs_subvolume_writers *writers;
+ int ret;
+
+ writers = kmalloc(sizeof(*writers), GFP_NOFS);
+ if (!writers)
+ return ERR_PTR(-ENOMEM);
+
+ ret = percpu_counter_init(&writers->counter, 0);
+ if (ret < 0) {
+ kfree(writers);
+ return ERR_PTR(ret);
+ }
+
+ init_waitqueue_head(&writers->wait);
+ return writers;
+}
+
+static void
+btrfs_free_subvolume_writers(struct btrfs_subvolume_writers *writers)
+{
+ percpu_counter_destroy(&writers->counter);
+ kfree(writers);
+}
+
static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
u32 stripesize, struct btrfs_root *root,
struct btrfs_fs_info *fs_info,
@@ -1194,16 +1217,22 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
spin_lock_init(&root->log_extents_lock[1]);
mutex_init(&root->objectid_mutex);
mutex_init(&root->log_mutex);
+ mutex_init(&root->ordered_extent_mutex);
+ mutex_init(&root->delalloc_mutex);
init_waitqueue_head(&root->log_writer_wait);
init_waitqueue_head(&root->log_commit_wait[0]);
init_waitqueue_head(&root->log_commit_wait[1]);
+ INIT_LIST_HEAD(&root->log_ctxs[0]);
+ INIT_LIST_HEAD(&root->log_ctxs[1]);
atomic_set(&root->log_commit[0], 0);
atomic_set(&root->log_commit[1], 0);
atomic_set(&root->log_writers, 0);
atomic_set(&root->log_batch, 0);
atomic_set(&root->orphan_inodes, 0);
atomic_set(&root->refs, 1);
+ atomic_set(&root->will_be_snapshoted, 0);
root->log_transid = 0;
+ root->log_transid_committed = -1;
root->last_log_commit = 0;
if (fs_info)
extent_io_tree_init(&root->dirty_log_pages,
@@ -1417,6 +1446,7 @@ int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
WARN_ON(root->log_root);
root->log_root = log_root;
root->log_transid = 0;
+ root->log_transid_committed = -1;
root->last_log_commit = 0;
return 0;
}
@@ -1498,6 +1528,7 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_root *tree_root,
int btrfs_init_fs_root(struct btrfs_root *root)
{
int ret;
+ struct btrfs_subvolume_writers *writers;
root->free_ino_ctl = kzalloc(sizeof(*root->free_ino_ctl), GFP_NOFS);
root->free_ino_pinned = kzalloc(sizeof(*root->free_ino_pinned),
@@ -1507,6 +1538,13 @@ int btrfs_init_fs_root(struct btrfs_root *root)
goto fail;
}
+ writers = btrfs_alloc_subvolume_writers();
+ if (IS_ERR(writers)) {
+ ret = PTR_ERR(writers);
+ goto fail;
+ }
+ root->subv_writers = writers;
+
btrfs_init_free_ino_ctl(root);
mutex_init(&root->fs_commit_mutex);
spin_lock_init(&root->cache_lock);
@@ -1514,8 +1552,11 @@ int btrfs_init_fs_root(struct btrfs_root *root)
ret = get_anon_bdev(&root->anon_dev);
if (ret)
- goto fail;
+ goto free_writers;
return 0;
+
+free_writers:
+ btrfs_free_subvolume_writers(root->subv_writers);
fail:
kfree(root->free_ino_ctl);
kfree(root->free_ino_pinned);
@@ -1990,23 +2031,22 @@ static noinline int next_root_backup(struct btrfs_fs_info *info,
/* helper to cleanup workers */
static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info)
{
- btrfs_stop_workers(&fs_info->generic_worker);
- btrfs_stop_workers(&fs_info->fixup_workers);
- btrfs_stop_workers(&fs_info->delalloc_workers);
- btrfs_stop_workers(&fs_info->workers);
- btrfs_stop_workers(&fs_info->endio_workers);
- btrfs_stop_workers(&fs_info->endio_meta_workers);
- btrfs_stop_workers(&fs_info->endio_raid56_workers);
- btrfs_stop_workers(&fs_info->rmw_workers);
- btrfs_stop_workers(&fs_info->endio_meta_write_workers);
- btrfs_stop_workers(&fs_info->endio_write_workers);
- btrfs_stop_workers(&fs_info->endio_freespace_worker);
- btrfs_stop_workers(&fs_info->submit_workers);
- btrfs_stop_workers(&fs_info->delayed_workers);
- btrfs_stop_workers(&fs_info->caching_workers);
- btrfs_stop_workers(&fs_info->readahead_workers);
- btrfs_stop_workers(&fs_info->flush_workers);
- btrfs_stop_workers(&fs_info->qgroup_rescan_workers);
+ btrfs_destroy_workqueue(fs_info->fixup_workers);
+ btrfs_destroy_workqueue(fs_info->delalloc_workers);
+ btrfs_destroy_workqueue(fs_info->workers);
+ btrfs_destroy_workqueue(fs_info->endio_workers);
+ btrfs_destroy_workqueue(fs_info->endio_meta_workers);
+ btrfs_destroy_workqueue(fs_info->endio_raid56_workers);
+ btrfs_destroy_workqueue(fs_info->rmw_workers);
+ btrfs_destroy_workqueue(fs_info->endio_meta_write_workers);
+ btrfs_destroy_workqueue(fs_info->endio_write_workers);
+ btrfs_destroy_workqueue(fs_info->endio_freespace_worker);
+ btrfs_destroy_workqueue(fs_info->submit_workers);
+ btrfs_destroy_workqueue(fs_info->delayed_workers);
+ btrfs_destroy_workqueue(fs_info->caching_workers);
+ btrfs_destroy_workqueue(fs_info->readahead_workers);
+ btrfs_destroy_workqueue(fs_info->flush_workers);
+ btrfs_destroy_workqueue(fs_info->qgroup_rescan_workers);
}
static void free_root_extent_buffers(struct btrfs_root *root)
@@ -2097,6 +2137,8 @@ int open_ctree(struct super_block *sb,
int err = -EINVAL;
int num_backups_tried = 0;
int backup_index = 0;
+ int max_active;
+ int flags = WQ_MEM_RECLAIM | WQ_FREEZABLE | WQ_UNBOUND;
bool create_uuid_tree;
bool check_uuid_tree;
@@ -2133,10 +2175,16 @@ int open_ctree(struct super_block *sb,
goto fail_dirty_metadata_bytes;
}
+ ret = percpu_counter_init(&fs_info->bio_counter, 0);
+ if (ret) {
+ err = ret;
+ goto fail_delalloc_bytes;
+ }
+
fs_info->btree_inode = new_inode(sb);
if (!fs_info->btree_inode) {
err = -ENOMEM;
- goto fail_delalloc_bytes;
+ goto fail_bio_counter;
}
mapping_set_gfp_mask(fs_info->btree_inode->i_mapping, GFP_NOFS);
@@ -2159,6 +2207,7 @@ int open_ctree(struct super_block *sb,
spin_lock_init(&fs_info->buffer_lock);
rwlock_init(&fs_info->tree_mod_log_lock);
mutex_init(&fs_info->reloc_mutex);
+ mutex_init(&fs_info->delalloc_root_mutex);
seqlock_init(&fs_info->profiles_lock);
init_completion(&fs_info->kobj_unregister);
@@ -2211,6 +2260,7 @@ int open_ctree(struct super_block *sb,
atomic_set(&fs_info->scrub_pause_req, 0);
atomic_set(&fs_info->scrubs_paused, 0);
atomic_set(&fs_info->scrub_cancel_req, 0);
+ init_waitqueue_head(&fs_info->replace_wait);
init_waitqueue_head(&fs_info->scrub_pause_wait);
fs_info->scrub_workers_refcnt = 0;
#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
@@ -2458,104 +2508,68 @@ int open_ctree(struct super_block *sb,
goto fail_alloc;
}
- btrfs_init_workers(&fs_info->generic_worker,
- "genwork", 1, NULL);
-
- btrfs_init_workers(&fs_info->workers, "worker",
- fs_info->thread_pool_size,
- &fs_info->generic_worker);
+ max_active = fs_info->thread_pool_size;
- btrfs_init_workers(&fs_info->delalloc_workers, "delalloc",
- fs_info->thread_pool_size, NULL);
+ fs_info->workers =
+ btrfs_alloc_workqueue("worker", flags | WQ_HIGHPRI,
+ max_active, 16);
- btrfs_init_workers(&fs_info->flush_workers, "flush_delalloc",
- fs_info->thread_pool_size, NULL);
+ fs_info->delalloc_workers =
+ btrfs_alloc_workqueue("delalloc", flags, max_active, 2);
- btrfs_init_workers(&fs_info->submit_workers, "submit",
- min_t(u64, fs_devices->num_devices,
- fs_info->thread_pool_size), NULL);
+ fs_info->flush_workers =
+ btrfs_alloc_workqueue("flush_delalloc", flags, max_active, 0);
- btrfs_init_workers(&fs_info->caching_workers, "cache",
- fs_info->thread_pool_size, NULL);
+ fs_info->caching_workers =
+ btrfs_alloc_workqueue("cache", flags, max_active, 0);
- /* a higher idle thresh on the submit workers makes it much more
+ /*
+ * a higher idle thresh on the submit workers makes it much more
* likely that bios will be send down in a sane order to the
* devices
*/
- fs_info->submit_workers.idle_thresh = 64;
-
- fs_info->workers.idle_thresh = 16;
- fs_info->workers.ordered = 1;
-
- fs_info->delalloc_workers.idle_thresh = 2;
- fs_info->delalloc_workers.ordered = 1;
-
- btrfs_init_workers(&fs_info->fixup_workers, "fixup", 1,
- &fs_info->generic_worker);
- btrfs_init_workers(&fs_info->endio_workers, "endio",
- fs_info->thread_pool_size,
- &fs_info->generic_worker);
- btrfs_init_workers(&fs_info->endio_meta_workers, "endio-meta",
- fs_info->thread_pool_size,
- &fs_info->generic_worker);
- btrfs_init_workers(&fs_info->endio_meta_write_workers,
- "endio-meta-write", fs_info->thread_pool_size,
- &fs_info->generic_worker);
- btrfs_init_workers(&fs_info->endio_raid56_workers,
- "endio-raid56", fs_info->thread_pool_size,
- &fs_info->generic_worker);
- btrfs_init_workers(&fs_info->rmw_workers,
- "rmw", fs_info->thread_pool_size,
- &fs_info->generic_worker);
- btrfs_init_workers(&fs_info->endio_write_workers, "endio-write",
- fs_info->thread_pool_size,
- &fs_info->generic_worker);
- btrfs_init_workers(&fs_info->endio_freespace_worker, "freespace-write",
- 1, &fs_info->generic_worker);
- btrfs_init_workers(&fs_info->delayed_workers, "delayed-meta",
- fs_info->thread_pool_size,
- &fs_info->generic_worker);
- btrfs_init_workers(&fs_info->readahead_workers, "readahead",
- fs_info->thread_pool_size,
- &fs_info->generic_worker);
- btrfs_init_workers(&fs_info->qgroup_rescan_workers, "qgroup-rescan", 1,
- &fs_info->generic_worker);
+ fs_info->submit_workers =
+ btrfs_alloc_workqueue("submit", flags,
+ min_t(u64, fs_devices->num_devices,
+ max_active), 64);
+
+ fs_info->fixup_workers =
+ btrfs_alloc_workqueue("fixup", flags, 1, 0);
/*
* endios are largely parallel and should have a very
* low idle thresh
*/
- fs_info->endio_workers.idle_thresh = 4;
- fs_info->endio_meta_workers.idle_thresh = 4;
- fs_info->endio_raid56_workers.idle_thresh = 4;
- fs_info->rmw_workers.idle_thresh = 2;
-
- fs_info->endio_write_workers.idle_thresh = 2;
- fs_info->endio_meta_write_workers.idle_thresh = 2;
- fs_info->readahead_workers.idle_thresh = 2;
-
- /*
- * btrfs_start_workers can really only fail because of ENOMEM so just
- * return -ENOMEM if any of these fail.
- */
- ret = btrfs_start_workers(&fs_info->workers);
- ret |= btrfs_start_workers(&fs_info->generic_worker);
- ret |= btrfs_start_workers(&fs_info->submit_workers);
- ret |= btrfs_start_workers(&fs_info->delalloc_workers);
- ret |= btrfs_start_workers(&fs_info->fixup_workers);
- ret |= btrfs_start_workers(&fs_info->endio_workers);
- ret |= btrfs_start_workers(&fs_info->endio_meta_workers);
- ret |= btrfs_start_workers(&fs_info->rmw_workers);
- ret |= btrfs_start_workers(&fs_info->endio_raid56_workers);
- ret |= btrfs_start_workers(&fs_info->endio_meta_write_workers);
- ret |= btrfs_start_workers(&fs_info->endio_write_workers);
- ret |= btrfs_start_workers(&fs_info->endio_freespace_worker);
- ret |= btrfs_start_workers(&fs_info->delayed_workers);
- ret |= btrfs_start_workers(&fs_info->caching_workers);
- ret |= btrfs_start_workers(&fs_info->readahead_workers);
- ret |= btrfs_start_workers(&fs_info->flush_workers);
- ret |= btrfs_start_workers(&fs_info->qgroup_rescan_workers);
- if (ret) {
+ fs_info->endio_workers =
+ btrfs_alloc_workqueue("endio", flags, max_active, 4);
+ fs_info->endio_meta_workers =
+ btrfs_alloc_workqueue("endio-meta", flags, max_active, 4);
+ fs_info->endio_meta_write_workers =
+ btrfs_alloc_workqueue("endio-meta-write", flags, max_active, 2);
+ fs_info->endio_raid56_workers =
+ btrfs_alloc_workqueue("endio-raid56", flags, max_active, 4);
+ fs_info->rmw_workers =
+ btrfs_alloc_workqueue("rmw", flags, max_active, 2);
+ fs_info->endio_write_workers =
+ btrfs_alloc_workqueue("endio-write", flags, max_active, 2);
+ fs_info->endio_freespace_worker =
+ btrfs_alloc_workqueue("freespace-write", flags, max_active, 0);
+ fs_info->delayed_workers =
+ btrfs_alloc_workqueue("delayed-meta", flags, max_active, 0);
+ fs_info->readahead_workers =
+ btrfs_alloc_workqueue("readahead", flags, max_active, 2);
+ fs_info->qgroup_rescan_workers =
+ btrfs_alloc_workqueue("qgroup-rescan", flags, 1, 0);
+
+ if (!(fs_info->workers && fs_info->delalloc_workers &&
+ fs_info->submit_workers && fs_info->flush_workers &&
+ fs_info->endio_workers && fs_info->endio_meta_workers &&
+ fs_info->endio_meta_write_workers &&
+ fs_info->endio_write_workers && fs_info->endio_raid56_workers &&
+ fs_info->endio_freespace_worker && fs_info->rmw_workers &&
+ fs_info->caching_workers && fs_info->readahead_workers &&
+ fs_info->fixup_workers && fs_info->delayed_workers &&
+ fs_info->qgroup_rescan_workers)) {
err = -ENOMEM;
goto fail_sb_buffer;
}
@@ -2963,6 +2977,8 @@ fail_iput:
btrfs_mapping_tree_free(&fs_info->mapping_tree);
iput(fs_info->btree_inode);
+fail_bio_counter:
+ percpu_counter_destroy(&fs_info->bio_counter);
fail_delalloc_bytes:
percpu_counter_destroy(&fs_info->delalloc_bytes);
fail_dirty_metadata_bytes:
@@ -3244,6 +3260,8 @@ static int barrier_all_devices(struct btrfs_fs_info *info)
/* send down all the barriers */
head = &info->fs_devices->devices;
list_for_each_entry_rcu(dev, head, dev_list) {
+ if (dev->missing)
+ continue;
if (!dev->bdev) {
errors_send++;
continue;
@@ -3258,6 +3276,8 @@ static int barrier_all_devices(struct btrfs_fs_info *info)
/* wait for all the barriers */
list_for_each_entry_rcu(dev, head, dev_list) {
+ if (dev->missing)
+ continue;
if (!dev->bdev) {
errors_wait++;
continue;
@@ -3477,6 +3497,8 @@ static void free_fs_root(struct btrfs_root *root)
root->orphan_block_rsv = NULL;
if (root->anon_dev)
free_anon_bdev(root->anon_dev);
+ if (root->subv_writers)
+ btrfs_free_subvolume_writers(root->subv_writers);
free_extent_buffer(root->node);
free_extent_buffer(root->commit_root);
kfree(root->free_ino_ctl);
@@ -3610,6 +3632,7 @@ int close_ctree(struct btrfs_root *root)
percpu_counter_destroy(&fs_info->dirty_metadata_bytes);
percpu_counter_destroy(&fs_info->delalloc_bytes);
+ percpu_counter_destroy(&fs_info->bio_counter);
bdi_destroy(&fs_info->bdi);
cleanup_srcu_struct(&fs_info->subvol_srcu);
@@ -3791,9 +3814,11 @@ static void btrfs_destroy_all_ordered_extents(struct btrfs_fs_info *fs_info)
list_move_tail(&root->ordered_root,
&fs_info->ordered_roots);
+ spin_unlock(&fs_info->ordered_root_lock);
btrfs_destroy_ordered_extents(root);
- cond_resched_lock(&fs_info->ordered_root_lock);
+ cond_resched();
+ spin_lock(&fs_info->ordered_root_lock);
}
spin_unlock(&fs_info->ordered_root_lock);
}
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 32312e09f0f5..c6b6a6e3e735 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -549,7 +549,7 @@ static int cache_block_group(struct btrfs_block_group_cache *cache,
caching_ctl->block_group = cache;
caching_ctl->progress = cache->key.objectid;
atomic_set(&caching_ctl->count, 1);
- caching_ctl->work.func = caching_thread;
+ btrfs_init_work(&caching_ctl->work, caching_thread, NULL, NULL);
spin_lock(&cache->lock);
/*
@@ -640,7 +640,7 @@ static int cache_block_group(struct btrfs_block_group_cache *cache,
btrfs_get_block_group(cache);
- btrfs_queue_worker(&fs_info->caching_workers, &caching_ctl->work);
+ btrfs_queue_work(fs_info->caching_workers, &caching_ctl->work);
return ret;
}
@@ -3971,7 +3971,7 @@ static int can_overcommit(struct btrfs_root *root,
}
static void btrfs_writeback_inodes_sb_nr(struct btrfs_root *root,
- unsigned long nr_pages)
+ unsigned long nr_pages, int nr_items)
{
struct super_block *sb = root->fs_info->sb;
@@ -3986,9 +3986,9 @@ static void btrfs_writeback_inodes_sb_nr(struct btrfs_root *root,
* the filesystem is readonly(all dirty pages are written to
* the disk).
*/
- btrfs_start_delalloc_roots(root->fs_info, 0);
+ btrfs_start_delalloc_roots(root->fs_info, 0, nr_items);
if (!current->journal_info)
- btrfs_wait_ordered_roots(root->fs_info, -1);
+ btrfs_wait_ordered_roots(root->fs_info, nr_items);
}
}
@@ -4045,7 +4045,7 @@ static void shrink_delalloc(struct btrfs_root *root, u64 to_reclaim, u64 orig,
while (delalloc_bytes && loops < 3) {
max_reclaim = min(delalloc_bytes, to_reclaim);
nr_pages = max_reclaim >> PAGE_CACHE_SHIFT;
- btrfs_writeback_inodes_sb_nr(root, nr_pages);
+ btrfs_writeback_inodes_sb_nr(root, nr_pages, items);
/*
* We need to wait for the async pages to actually start before
* we do anything.
@@ -4112,13 +4112,9 @@ static int may_commit_transaction(struct btrfs_root *root,
goto commit;
/* See if there is enough pinned space to make this reservation */
- spin_lock(&space_info->lock);
if (percpu_counter_compare(&space_info->total_bytes_pinned,
- bytes) >= 0) {
- spin_unlock(&space_info->lock);
+ bytes) >= 0)
goto commit;
- }
- spin_unlock(&space_info->lock);
/*
* See if there is some space in the delayed insertion reservation for
@@ -4127,16 +4123,13 @@ static int may_commit_transaction(struct btrfs_root *root,
if (space_info != delayed_rsv->space_info)
return -ENOSPC;
- spin_lock(&space_info->lock);
spin_lock(&delayed_rsv->lock);
if (percpu_counter_compare(&space_info->total_bytes_pinned,
bytes - delayed_rsv->size) >= 0) {
spin_unlock(&delayed_rsv->lock);
- spin_unlock(&space_info->lock);
return -ENOSPC;
}
spin_unlock(&delayed_rsv->lock);
- spin_unlock(&space_info->lock);
commit:
trans = btrfs_join_transaction(root);
@@ -4181,7 +4174,7 @@ static int flush_space(struct btrfs_root *root,
break;
case FLUSH_DELALLOC:
case FLUSH_DELALLOC_WAIT:
- shrink_delalloc(root, num_bytes, orig_bytes,
+ shrink_delalloc(root, num_bytes * 2, orig_bytes,
state == FLUSH_DELALLOC_WAIT);
break;
case ALLOC_CHUNK:
@@ -8938,3 +8931,38 @@ int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range)
range->len = trimmed;
return ret;
}
+
+/*
+ * btrfs_{start,end}_write() is similar to mnt_{want, drop}_write(),
+ * they are used to prevent the some tasks writing data into the page cache
+ * by nocow before the subvolume is snapshoted, but flush the data into
+ * the disk after the snapshot creation.
+ */
+void btrfs_end_nocow_write(struct btrfs_root *root)
+{
+ percpu_counter_dec(&root->subv_writers->counter);
+ /*
+ * Make sure counter is updated before we wake up
+ * waiters.
+ */
+ smp_mb();
+ if (waitqueue_active(&root->subv_writers->wait))
+ wake_up(&root->subv_writers->wait);
+}
+
+int btrfs_start_nocow_write(struct btrfs_root *root)
+{
+ if (unlikely(atomic_read(&root->will_be_snapshoted)))
+ return 0;
+
+ percpu_counter_inc(&root->subv_writers->counter);
+ /*
+ * Make sure counter is updated before we check for snapshot creation.
+ */
+ smp_mb();
+ if (unlikely(atomic_read(&root->will_be_snapshoted))) {
+ btrfs_end_nocow_write(root);
+ return 0;
+ }
+ return 1;
+}
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 85bbd01f1271..ae69a00387e7 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -229,12 +229,14 @@ void free_extent_state(struct extent_state *state)
}
}
-static struct rb_node *tree_insert(struct rb_root *root, u64 offset,
+static struct rb_node *tree_insert(struct rb_root *root,
+ struct rb_node *search_start,
+ u64 offset,
struct rb_node *node,
struct rb_node ***p_in,
struct rb_node **parent_in)
{
- struct rb_node **p = &root->rb_node;
+ struct rb_node **p;
struct rb_node *parent = NULL;
struct tree_entry *entry;
@@ -244,6 +246,7 @@ static struct rb_node *tree_insert(struct rb_root *root, u64 offset,
goto do_insert;
}
+ p = search_start ? &search_start : &root->rb_node;
while (*p) {
parent = *p;
entry = rb_entry(parent, struct tree_entry, rb_node);
@@ -430,7 +433,7 @@ static int insert_state(struct extent_io_tree *tree,
set_state_bits(tree, state, bits);
- node = tree_insert(&tree->state, end, &state->rb_node, p, parent);
+ node = tree_insert(&tree->state, NULL, end, &state->rb_node, p, parent);
if (node) {
struct extent_state *found;
found = rb_entry(node, struct extent_state, rb_node);
@@ -477,8 +480,8 @@ static int split_state(struct extent_io_tree *tree, struct extent_state *orig,
prealloc->state = orig->state;
orig->start = split;
- node = tree_insert(&tree->state, prealloc->end, &prealloc->rb_node,
- NULL, NULL);
+ node = tree_insert(&tree->state, &orig->rb_node, prealloc->end,
+ &prealloc->rb_node, NULL, NULL);
if (node) {
free_extent_state(prealloc);
return -EEXIST;
@@ -2757,7 +2760,7 @@ __get_extent_map(struct inode *inode, struct page *page, size_t pg_offset,
if (em_cached && *em_cached) {
em = *em_cached;
- if (em->in_tree && start >= em->start &&
+ if (extent_map_in_tree(em) && start >= em->start &&
start < extent_map_end(em)) {
atomic_inc(&em->refs);
return em;
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 996ad56b57db..1874aee69c86 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -51,7 +51,7 @@ struct extent_map *alloc_extent_map(void)
em = kmem_cache_zalloc(extent_map_cache, GFP_NOFS);
if (!em)
return NULL;
- em->in_tree = 0;
+ RB_CLEAR_NODE(&em->rb_node);
em->flags = 0;
em->compress_type = BTRFS_COMPRESS_NONE;
em->generation = 0;
@@ -73,7 +73,7 @@ void free_extent_map(struct extent_map *em)
return;
WARN_ON(atomic_read(&em->refs) == 0);
if (atomic_dec_and_test(&em->refs)) {
- WARN_ON(em->in_tree);
+ WARN_ON(extent_map_in_tree(em));
WARN_ON(!list_empty(&em->list));
kmem_cache_free(extent_map_cache, em);
}
@@ -99,8 +99,6 @@ static int tree_insert(struct rb_root *root, struct extent_map *em)
parent = *p;
entry = rb_entry(parent, struct extent_map, rb_node);
- WARN_ON(!entry->in_tree);
-
if (em->start < entry->start)
p = &(*p)->rb_left;
else if (em->start >= extent_map_end(entry))
@@ -128,7 +126,6 @@ static int tree_insert(struct rb_root *root, struct extent_map *em)
if (end > entry->start && em->start < extent_map_end(entry))
return -EEXIST;
- em->in_tree = 1;
rb_link_node(&em->rb_node, orig_parent, p);
rb_insert_color(&em->rb_node, root);
return 0;
@@ -153,8 +150,6 @@ static struct rb_node *__tree_search(struct rb_root *root, u64 offset,
prev = n;
prev_entry = entry;
- WARN_ON(!entry->in_tree);
-
if (offset < entry->start)
n = n->rb_left;
else if (offset >= extent_map_end(entry))
@@ -240,12 +235,12 @@ static void try_merge_map(struct extent_map_tree *tree, struct extent_map *em)
em->len += merge->len;
em->block_len += merge->block_len;
em->block_start = merge->block_start;
- merge->in_tree = 0;
em->mod_len = (em->mod_len + em->mod_start) - merge->mod_start;
em->mod_start = merge->mod_start;
em->generation = max(em->generation, merge->generation);
rb_erase(&merge->rb_node, &tree->map);
+ RB_CLEAR_NODE(&merge->rb_node);
free_extent_map(merge);
}
}
@@ -257,7 +252,7 @@ static void try_merge_map(struct extent_map_tree *tree, struct extent_map *em)
em->len += merge->len;
em->block_len += merge->block_len;
rb_erase(&merge->rb_node, &tree->map);
- merge->in_tree = 0;
+ RB_CLEAR_NODE(&merge->rb_node);
em->mod_len = (merge->mod_start + merge->mod_len) - em->mod_start;
em->generation = max(em->generation, merge->generation);
free_extent_map(merge);
@@ -319,7 +314,21 @@ out:
void clear_em_logging(struct extent_map_tree *tree, struct extent_map *em)
{
clear_bit(EXTENT_FLAG_LOGGING, &em->flags);
- if (em->in_tree)
+ if (extent_map_in_tree(em))
+ try_merge_map(tree, em);
+}
+
+static inline void setup_extent_mapping(struct extent_map_tree *tree,
+ struct extent_map *em,
+ int modified)
+{
+ atomic_inc(&em->refs);
+ em->mod_start = em->start;
+ em->mod_len = em->len;
+
+ if (modified)
+ list_move(&em->list, &tree->modified_extents);
+ else
try_merge_map(tree, em);
}
@@ -342,15 +351,7 @@ int add_extent_mapping(struct extent_map_tree *tree,
if (ret)
goto out;
- atomic_inc(&em->refs);
-
- em->mod_start = em->start;
- em->mod_len = em->len;
-
- if (modified)
- list_move(&em->list, &tree->modified_extents);
- else
- try_merge_map(tree, em);
+ setup_extent_mapping(tree, em, modified);
out:
return ret;
}
@@ -434,6 +435,21 @@ int remove_extent_mapping(struct extent_map_tree *tree, struct extent_map *em)
rb_erase(&em->rb_node, &tree->map);
if (!test_bit(EXTENT_FLAG_LOGGING, &em->flags))
list_del_init(&em->list);
- em->in_tree = 0;
+ RB_CLEAR_NODE(&em->rb_node);
return ret;
}
+
+void replace_extent_mapping(struct extent_map_tree *tree,
+ struct extent_map *cur,
+ struct extent_map *new,
+ int modified)
+{
+ WARN_ON(test_bit(EXTENT_FLAG_PINNED, &cur->flags));
+ ASSERT(extent_map_in_tree(cur));
+ if (!test_bit(EXTENT_FLAG_LOGGING, &cur->flags))
+ list_del_init(&cur->list);
+ rb_replace_node(&cur->rb_node, &new->rb_node, &tree->map);
+ RB_CLEAR_NODE(&cur->rb_node);
+
+ setup_extent_mapping(tree, new, modified);
+}
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index 93fba716d7f8..e7fd8a56a140 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -33,7 +33,6 @@ struct extent_map {
unsigned long flags;
struct block_device *bdev;
atomic_t refs;
- unsigned int in_tree;
unsigned int compress_type;
struct list_head list;
};
@@ -44,6 +43,11 @@ struct extent_map_tree {
rwlock_t lock;
};
+static inline int extent_map_in_tree(const struct extent_map *em)
+{
+ return !RB_EMPTY_NODE(&em->rb_node);
+}
+
static inline u64 extent_map_end(struct extent_map *em)
{
if (em->start + em->len < em->start)
@@ -64,6 +68,10 @@ struct extent_map *lookup_extent_mapping(struct extent_map_tree *tree,
int add_extent_mapping(struct extent_map_tree *tree,
struct extent_map *em, int modified);
int remove_extent_mapping(struct extent_map_tree *tree, struct extent_map *em);
+void replace_extent_mapping(struct extent_map_tree *tree,
+ struct extent_map *cur,
+ struct extent_map *new,
+ int modified);
struct extent_map *alloc_extent_map(void);
void free_extent_map(struct extent_map *em);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 0165b8672f09..e1ffb1e22898 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -591,7 +591,6 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
clear_bit(EXTENT_FLAG_PINNED, &em->flags);
clear_bit(EXTENT_FLAG_LOGGING, &flags);
modified = !list_empty(&em->list);
- remove_extent_mapping(em_tree, em);
if (no_splits)
goto next;
@@ -622,8 +621,7 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
split->bdev = em->bdev;
split->flags = flags;
split->compress_type = em->compress_type;
- ret = add_extent_mapping(em_tree, split, modified);
- BUG_ON(ret); /* Logic error */
+ replace_extent_mapping(em_tree, em, split, modified);
free_extent_map(split);
split = split2;
split2 = NULL;
@@ -661,12 +659,20 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
split->orig_block_len = 0;
}
- ret = add_extent_mapping(em_tree, split, modified);
- BUG_ON(ret); /* Logic error */
+ if (extent_map_in_tree(em)) {
+ replace_extent_mapping(em_tree, em, split,
+ modified);
+ } else {
+ ret = add_extent_mapping(em_tree, split,
+ modified);
+ ASSERT(ret == 0); /* Logic error */
+ }
free_extent_map(split);
split = NULL;
}
next:
+ if (extent_map_in_tree(em))
+ remove_extent_mapping(em_tree, em);
write_unlock(&em_tree->lock);
/* once for us */
@@ -720,7 +726,7 @@ int __btrfs_drop_extents(struct btrfs_trans_handle *trans,
if (drop_cache)
btrfs_drop_extent_cache(inode, start, end - 1, 0);
- if (start >= BTRFS_I(inode)->disk_i_size)
+ if (start >= BTRFS_I(inode)->disk_i_size && !replace_extent)
modify_tree = 0;
while (1) {
@@ -798,7 +804,10 @@ next_slot:
*/
if (start > key.offset && end < extent_end) {
BUG_ON(del_nr > 0);
- BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE);
+ if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+ ret = -EINVAL;
+ break;
+ }
memcpy(&new_key, &key, sizeof(new_key));
new_key.offset = start;
@@ -841,7 +850,10 @@ next_slot:
* | -------- extent -------- |
*/
if (start <= key.offset && end < extent_end) {
- BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE);
+ if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+ ret = -EINVAL;
+ break;
+ }
memcpy(&new_key, &key, sizeof(new_key));
new_key.offset = end;
@@ -864,7 +876,10 @@ next_slot:
*/
if (start > key.offset && end >= extent_end) {
BUG_ON(del_nr > 0);
- BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE);
+ if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+ ret = -EINVAL;
+ break;
+ }
btrfs_set_file_extent_num_bytes(leaf, fi,
start - key.offset);
@@ -938,34 +953,42 @@ next_slot:
* Set path->slots[0] to first slot, so that after the delete
* if items are move off from our leaf to its immediate left or
* right neighbor leafs, we end up with a correct and adjusted
- * path->slots[0] for our insertion.
+ * path->slots[0] for our insertion (if replace_extent != 0).
*/
path->slots[0] = del_slot;
ret = btrfs_del_items(trans, root, path, del_slot, del_nr);
if (ret)
btrfs_abort_transaction(trans, root, ret);
+ }
- leaf = path->nodes[0];
- /*
- * leaf eb has flag EXTENT_BUFFER_STALE if it was deleted (that
- * is, its contents got pushed to its neighbors), in which case
- * it means path->locks[0] == 0
- */
- if (!ret && replace_extent && leafs_visited == 1 &&
- path->locks[0] &&
- btrfs_leaf_free_space(root, leaf) >=
- sizeof(struct btrfs_item) + extent_item_size) {
-
- key.objectid = ino;
- key.type = BTRFS_EXTENT_DATA_KEY;
- key.offset = start;
- setup_items_for_insert(root, path, &key,
- &extent_item_size,
- extent_item_size,
- sizeof(struct btrfs_item) +
- extent_item_size, 1);
- *key_inserted = 1;
+ leaf = path->nodes[0];
+ /*
+ * If btrfs_del_items() was called, it might have deleted a leaf, in
+ * which case it unlocked our path, so check path->locks[0] matches a
+ * write lock.
+ */
+ if (!ret && replace_extent && leafs_visited == 1 &&
+ (path->locks[0] == BTRFS_WRITE_LOCK_BLOCKING ||
+ path->locks[0] == BTRFS_WRITE_LOCK) &&
+ btrfs_leaf_free_space(root, leaf) >=
+ sizeof(struct btrfs_item) + extent_item_size) {
+
+ key.objectid = ino;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = start;
+ if (!del_nr && path->slots[0] < btrfs_header_nritems(leaf)) {
+ struct btrfs_key slot_key;
+
+ btrfs_item_key_to_cpu(leaf, &slot_key, path->slots[0]);
+ if (btrfs_comp_cpu_keys(&key, &slot_key) > 0)
+ path->slots[0]++;
}
+ setup_items_for_insert(root, path, &key,
+ &extent_item_size,
+ extent_item_size,
+ sizeof(struct btrfs_item) +
+ extent_item_size, 1);
+ *key_inserted = 1;
}
if (!replace_extent || !(*key_inserted))
@@ -1346,11 +1369,11 @@ lock_and_cleanup_extent_if_need(struct inode *inode, struct page **pages,
struct btrfs_ordered_extent *ordered;
lock_extent_bits(&BTRFS_I(inode)->io_tree,
start_pos, last_pos, 0, cached_state);
- ordered = btrfs_lookup_first_ordered_extent(inode, last_pos);
+ ordered = btrfs_lookup_ordered_range(inode, start_pos,
+ last_pos - start_pos + 1);
if (ordered &&
ordered->file_offset + ordered->len > start_pos &&
ordered->file_offset <= last_pos) {
- btrfs_put_ordered_extent(ordered);
unlock_extent_cached(&BTRFS_I(inode)->io_tree,
start_pos, last_pos,
cached_state, GFP_NOFS);
@@ -1358,12 +1381,9 @@ lock_and_cleanup_extent_if_need(struct inode *inode, struct page **pages,
unlock_page(pages[i]);
page_cache_release(pages[i]);
}
- ret = btrfs_wait_ordered_range(inode, start_pos,
- last_pos - start_pos + 1);
- if (ret)
- return ret;
- else
- return -EAGAIN;
+ btrfs_start_ordered_extent(inode, ordered, 1);
+ btrfs_put_ordered_extent(ordered);
+ return -EAGAIN;
}
if (ordered)
btrfs_put_ordered_extent(ordered);
@@ -1396,8 +1416,12 @@ static noinline int check_can_nocow(struct inode *inode, loff_t pos,
u64 num_bytes;
int ret;
+ ret = btrfs_start_nocow_write(root);
+ if (!ret)
+ return -ENOSPC;
+
lockstart = round_down(pos, root->sectorsize);
- lockend = lockstart + round_up(*write_bytes, root->sectorsize) - 1;
+ lockend = round_up(pos + *write_bytes, root->sectorsize) - 1;
while (1) {
lock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend);
@@ -1415,12 +1439,10 @@ static noinline int check_can_nocow(struct inode *inode, loff_t pos,
ret = can_nocow_extent(inode, lockstart, &num_bytes, NULL, NULL, NULL);
if (ret <= 0) {
ret = 0;
+ btrfs_end_nocow_write(root);
} else {
- clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend,
- EXTENT_DIRTY | EXTENT_DELALLOC |
- EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, 0, 0,
- NULL, GFP_NOFS);
- *write_bytes = min_t(size_t, *write_bytes, num_bytes);
+ *write_bytes = min_t(size_t, *write_bytes ,
+ num_bytes - pos + lockstart);
}
unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend);
@@ -1510,6 +1532,8 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
if (!only_release_metadata)
btrfs_free_reserved_data_space(inode,
reserve_bytes);
+ else
+ btrfs_end_nocow_write(root);
break;
}
@@ -1598,6 +1622,9 @@ again:
}
release_bytes = 0;
+ if (only_release_metadata)
+ btrfs_end_nocow_write(root);
+
if (only_release_metadata && copied > 0) {
u64 lockstart = round_down(pos, root->sectorsize);
u64 lockend = lockstart +
@@ -1624,10 +1651,12 @@ again:
kfree(pages);
if (release_bytes) {
- if (only_release_metadata)
+ if (only_release_metadata) {
+ btrfs_end_nocow_write(root);
btrfs_delalloc_release_metadata(inode, release_bytes);
- else
+ } else {
btrfs_delalloc_release_space(inode, release_bytes);
+ }
}
return num_written ? num_written : ret;
@@ -1797,7 +1826,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
BTRFS_I(inode)->last_sub_trans = root->log_transid;
if (num_written > 0) {
err = generic_write_sync(file, pos, num_written);
- if (err < 0 && num_written > 0)
+ if (err < 0)
num_written = err;
}
@@ -1856,8 +1885,9 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
struct dentry *dentry = file->f_path.dentry;
struct inode *inode = dentry->d_inode;
struct btrfs_root *root = BTRFS_I(inode)->root;
- int ret = 0;
struct btrfs_trans_handle *trans;
+ struct btrfs_log_ctx ctx;
+ int ret = 0;
bool full_sync = 0;
trace_btrfs_sync_file(file, datasync);
@@ -1951,7 +1981,9 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
}
trans->sync = true;
- ret = btrfs_log_dentry_safe(trans, root, dentry);
+ btrfs_init_log_ctx(&ctx);
+
+ ret = btrfs_log_dentry_safe(trans, root, dentry, &ctx);
if (ret < 0) {
/* Fallthrough and commit/free transaction. */
ret = 1;
@@ -1971,7 +2003,7 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
if (ret != BTRFS_NO_LOG_SYNC) {
if (!ret) {
- ret = btrfs_sync_log(trans, root);
+ ret = btrfs_sync_log(trans, root, &ctx);
if (!ret) {
ret = btrfs_end_transaction(trans, root);
goto out;
@@ -2157,6 +2189,7 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
bool same_page = ((offset >> PAGE_CACHE_SHIFT) ==
((offset + len - 1) >> PAGE_CACHE_SHIFT));
bool no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
+ u64 ino_size = round_up(inode->i_size, PAGE_CACHE_SIZE);
ret = btrfs_wait_ordered_range(inode, offset, len);
if (ret)
@@ -2172,14 +2205,14 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
* entire page.
*/
if (same_page && len < PAGE_CACHE_SIZE) {
- if (offset < round_up(inode->i_size, PAGE_CACHE_SIZE))
+ if (offset < ino_size)
ret = btrfs_truncate_page(inode, offset, len, 0);
mutex_unlock(&inode->i_mutex);
return ret;
}
/* zero back part of the first page */
- if (offset < round_up(inode->i_size, PAGE_CACHE_SIZE)) {
+ if (offset < ino_size) {
ret = btrfs_truncate_page(inode, offset, 0, 0);
if (ret) {
mutex_unlock(&inode->i_mutex);
@@ -2188,7 +2221,7 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
}
/* zero the front end of the last page */
- if (offset + len < round_up(inode->i_size, PAGE_CACHE_SIZE)) {
+ if (offset + len < ino_size) {
ret = btrfs_truncate_page(inode, offset + len, 0, 1);
if (ret) {
mutex_unlock(&inode->i_mutex);
@@ -2277,10 +2310,13 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
trans->block_rsv = &root->fs_info->trans_block_rsv;
- ret = fill_holes(trans, inode, path, cur_offset, drop_end);
- if (ret) {
- err = ret;
- break;
+ if (cur_offset < ino_size) {
+ ret = fill_holes(trans, inode, path, cur_offset,
+ drop_end);
+ if (ret) {
+ err = ret;
+ break;
+ }
}
cur_offset = drop_end;
@@ -2313,10 +2349,12 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
}
trans->block_rsv = &root->fs_info->trans_block_rsv;
- ret = fill_holes(trans, inode, path, cur_offset, drop_end);
- if (ret) {
- err = ret;
- goto out_trans;
+ if (cur_offset < ino_size) {
+ ret = fill_holes(trans, inode, path, cur_offset, drop_end);
+ if (ret) {
+ err = ret;
+ goto out_trans;
+ }
}
out_trans:
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index d3d44486290b..06e9a4152b14 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -864,7 +864,8 @@ static noinline int cow_file_range(struct inode *inode,
if (btrfs_is_free_space_inode(inode)) {
WARN_ON_ONCE(1);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_unlock;
}
num_bytes = ALIGN(end - start + 1, blocksize);
@@ -1075,17 +1076,15 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
async_cow->end = cur_end;
INIT_LIST_HEAD(&async_cow->extents);
- async_cow->work.func = async_cow_start;
- async_cow->work.ordered_func = async_cow_submit;
- async_cow->work.ordered_free = async_cow_free;
- async_cow->work.flags = 0;
+ btrfs_init_work(&async_cow->work, async_cow_start,
+ async_cow_submit, async_cow_free);
nr_pages = (cur_end - start + PAGE_CACHE_SIZE) >>
PAGE_CACHE_SHIFT;
atomic_add(nr_pages, &root->fs_info->async_delalloc_pages);
- btrfs_queue_worker(&root->fs_info->delalloc_workers,
- &async_cow->work);
+ btrfs_queue_work(root->fs_info->delalloc_workers,
+ &async_cow->work);
if (atomic_read(&root->fs_info->async_delalloc_pages) > limit) {
wait_event(root->fs_info->async_submit_wait,
@@ -1843,9 +1842,9 @@ static int btrfs_writepage_start_hook(struct page *page, u64 start, u64 end)
SetPageChecked(page);
page_cache_get(page);
- fixup->work.func = btrfs_writepage_fixup_worker;
+ btrfs_init_work(&fixup->work, btrfs_writepage_fixup_worker, NULL, NULL);
fixup->page = page;
- btrfs_queue_worker(&root->fs_info->fixup_workers, &fixup->work);
+ btrfs_queue_work(root->fs_info->fixup_workers, &fixup->work);
return -EBUSY;
}
@@ -2239,6 +2238,11 @@ static noinline int relink_extent_backref(struct btrfs_path *path,
return PTR_ERR(root);
}
+ if (btrfs_root_readonly(root)) {
+ srcu_read_unlock(&fs_info->subvol_srcu, index);
+ return 0;
+ }
+
/* step 2: get inode */
key.objectid = backref->inum;
key.type = BTRFS_INODE_ITEM_KEY;
@@ -2759,7 +2763,7 @@ static int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
struct inode *inode = page->mapping->host;
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_ordered_extent *ordered_extent = NULL;
- struct btrfs_workers *workers;
+ struct btrfs_workqueue *workers;
trace_btrfs_writepage_end_io_hook(page, start, end, uptodate);
@@ -2768,14 +2772,13 @@ static int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
end - start + 1, uptodate))
return 0;
- ordered_extent->work.func = finish_ordered_fn;
- ordered_extent->work.flags = 0;
+ btrfs_init_work(&ordered_extent->work, finish_ordered_fn, NULL, NULL);
if (btrfs_is_free_space_inode(inode))
- workers = &root->fs_info->endio_freespace_worker;
+ workers = root->fs_info->endio_freespace_worker;
else
- workers = &root->fs_info->endio_write_workers;
- btrfs_queue_worker(workers, &ordered_extent->work);
+ workers = root->fs_info->endio_write_workers;
+ btrfs_queue_work(workers, &ordered_extent->work);
return 0;
}
@@ -4593,7 +4596,7 @@ static void evict_inode_truncate_pages(struct inode *inode)
struct rb_node *node;
ASSERT(inode->i_state & I_FREEING);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
write_lock(&map_tree->lock);
while (!RB_EMPTY_ROOT(&map_tree->map)) {
@@ -4924,7 +4927,8 @@ void btrfs_invalidate_inodes(struct btrfs_root *root)
struct inode *inode;
u64 objectid = 0;
- WARN_ON(btrfs_root_refs(&root->root_item) != 0);
+ if (!test_bit(BTRFS_FS_STATE_ERROR, &root->fs_info->fs_state))
+ WARN_ON(btrfs_root_refs(&root->root_item) != 0);
spin_lock(&root->inode_lock);
again:
@@ -5799,6 +5803,7 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry,
}
out_unlock:
btrfs_end_transaction(trans, root);
+ btrfs_balance_delayed_items(root);
btrfs_btree_balance_dirty(root);
if (drop_inode) {
inode_dec_link_count(inode);
@@ -5872,6 +5877,7 @@ out_unlock:
inode_dec_link_count(inode);
iput(inode);
}
+ btrfs_balance_delayed_items(root);
btrfs_btree_balance_dirty(root);
return err;
}
@@ -5930,6 +5936,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
}
btrfs_end_transaction(trans, root);
+ btrfs_balance_delayed_items(root);
fail:
if (drop_inode) {
inode_dec_link_count(inode);
@@ -5996,6 +6003,7 @@ out_fail:
btrfs_end_transaction(trans, root);
if (drop_on_err)
iput(inode);
+ btrfs_balance_delayed_items(root);
btrfs_btree_balance_dirty(root);
return err;
}
@@ -6550,6 +6558,7 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
int ret;
struct extent_buffer *leaf;
struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
struct btrfs_file_extent_item *fi;
struct btrfs_key key;
u64 disk_bytenr;
@@ -6626,6 +6635,20 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
if (btrfs_extent_readonly(root, disk_bytenr))
goto out;
+
+ num_bytes = min(offset + *len, extent_end) - offset;
+ if (!nocow && found_type == BTRFS_FILE_EXTENT_PREALLOC) {
+ u64 range_end;
+
+ range_end = round_up(offset + num_bytes, root->sectorsize) - 1;
+ ret = test_range_bit(io_tree, offset, range_end,
+ EXTENT_DELALLOC, 0, NULL);
+ if (ret) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ }
+
btrfs_release_path(path);
/*
@@ -6654,7 +6677,6 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
*/
disk_bytenr += backref_offset;
disk_bytenr += offset - key.offset;
- num_bytes = min(offset + *len, extent_end) - offset;
if (csum_exist_in_range(root, disk_bytenr, num_bytes))
goto out;
/*
@@ -7024,10 +7046,9 @@ again:
if (!ret)
goto out_test;
- ordered->work.func = finish_ordered_fn;
- ordered->work.flags = 0;
- btrfs_queue_worker(&root->fs_info->endio_write_workers,
- &ordered->work);
+ btrfs_init_work(&ordered->work, finish_ordered_fn, NULL, NULL);
+ btrfs_queue_work(root->fs_info->endio_write_workers,
+ &ordered->work);
out_test:
/*
* our bio might span multiple ordered extents. If we haven't
@@ -7404,15 +7425,15 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
smp_mb__after_atomic_inc();
/*
- * The generic stuff only does filemap_write_and_wait_range, which isn't
- * enough if we've written compressed pages to this area, so we need to
- * call btrfs_wait_ordered_range to make absolutely sure that any
- * outstanding dirty pages are on disk.
+ * The generic stuff only does filemap_write_and_wait_range, which
+ * isn't enough if we've written compressed pages to this area, so
+ * we need to flush the dirty pages again to make absolutely sure
+ * that any outstanding dirty pages are on disk.
*/
count = iov_length(iov, nr_segs);
- ret = btrfs_wait_ordered_range(inode, offset, count);
- if (ret)
- return ret;
+ if (test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
+ &BTRFS_I(inode)->runtime_flags))
+ filemap_fdatawrite_range(inode->i_mapping, offset, count);
if (rw & WRITE) {
/*
@@ -8404,7 +8425,7 @@ struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode,
work->inode = inode;
work->wait = wait;
work->delay_iput = delay_iput;
- work->work.func = btrfs_run_delalloc_work;
+ btrfs_init_work(&work->work, btrfs_run_delalloc_work, NULL, NULL);
return work;
}
@@ -8419,7 +8440,8 @@ void btrfs_wait_and_free_delalloc_work(struct btrfs_delalloc_work *work)
* some fairly slow code that needs optimization. This walks the list
* of all the inodes with pending delalloc and forces them to disk.
*/
-static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
+static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput,
+ int nr)
{
struct btrfs_inode *binode;
struct inode *inode;
@@ -8431,6 +8453,7 @@ static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
INIT_LIST_HEAD(&works);
INIT_LIST_HEAD(&splice);
+ mutex_lock(&root->delalloc_mutex);
spin_lock(&root->delalloc_lock);
list_splice_init(&root->delalloc_inodes, &splice);
while (!list_empty(&splice)) {
@@ -8453,12 +8476,14 @@ static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
else
iput(inode);
ret = -ENOMEM;
- goto out;
+ break;
}
list_add_tail(&work->list, &works);
- btrfs_queue_worker(&root->fs_info->flush_workers,
- &work->work);
-
+ btrfs_queue_work(root->fs_info->flush_workers,
+ &work->work);
+ ret++;
+ if (nr != -1 && ret >= nr)
+ break;
cond_resched();
spin_lock(&root->delalloc_lock);
}
@@ -8468,18 +8493,13 @@ static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
list_del_init(&work->list);
btrfs_wait_and_free_delalloc_work(work);
}
- return 0;
-out:
- list_for_each_entry_safe(work, next, &works, list) {
- list_del_init(&work->list);
- btrfs_wait_and_free_delalloc_work(work);
- }
if (!list_empty_careful(&splice)) {
spin_lock(&root->delalloc_lock);
list_splice_tail(&splice, &root->delalloc_inodes);
spin_unlock(&root->delalloc_lock);
}
+ mutex_unlock(&root->delalloc_mutex);
return ret;
}
@@ -8490,7 +8510,9 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
if (test_bit(BTRFS_FS_STATE_ERROR, &root->fs_info->fs_state))
return -EROFS;
- ret = __start_delalloc_inodes(root, delay_iput);
+ ret = __start_delalloc_inodes(root, delay_iput, -1);
+ if (ret > 0)
+ ret = 0;
/*
* the filemap_flush will queue IO into the worker threads, but
* we have to make sure the IO is actually started and that
@@ -8507,7 +8529,8 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
return ret;
}
-int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput)
+int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput,
+ int nr)
{
struct btrfs_root *root;
struct list_head splice;
@@ -8518,9 +8541,10 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput)
INIT_LIST_HEAD(&splice);
+ mutex_lock(&fs_info->delalloc_root_mutex);
spin_lock(&fs_info->delalloc_root_lock);
list_splice_init(&fs_info->delalloc_roots, &splice);
- while (!list_empty(&splice)) {
+ while (!list_empty(&splice) && nr) {
root = list_first_entry(&splice, struct btrfs_root,
delalloc_root);
root = btrfs_grab_fs_root(root);
@@ -8529,15 +8553,20 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput)
&fs_info->delalloc_roots);
spin_unlock(&fs_info->delalloc_root_lock);
- ret = __start_delalloc_inodes(root, delay_iput);
+ ret = __start_delalloc_inodes(root, delay_iput, nr);
btrfs_put_fs_root(root);
- if (ret)
+ if (ret < 0)
goto out;
+ if (nr != -1) {
+ nr -= ret;
+ WARN_ON(nr < 0);
+ }
spin_lock(&fs_info->delalloc_root_lock);
}
spin_unlock(&fs_info->delalloc_root_lock);
+ ret = 0;
atomic_inc(&fs_info->async_submit_draining);
while (atomic_read(&fs_info->nr_async_submits) ||
atomic_read(&fs_info->async_delalloc_pages)) {
@@ -8546,13 +8575,13 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput)
atomic_read(&fs_info->async_delalloc_pages) == 0));
}
atomic_dec(&fs_info->async_submit_draining);
- return 0;
out:
if (!list_empty_careful(&splice)) {
spin_lock(&fs_info->delalloc_root_lock);
list_splice_tail(&splice, &fs_info->delalloc_roots);
spin_unlock(&fs_info->delalloc_root_lock);
}
+ mutex_unlock(&fs_info->delalloc_root_mutex);
return ret;
}
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index a6d8efa46bfe..0401397b5c92 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -59,6 +59,32 @@
#include "props.h"
#include "sysfs.h"
+#ifdef CONFIG_64BIT
+/* If we have a 32-bit userspace and 64-bit kernel, then the UAPI
+ * structures are incorrect, as the timespec structure from userspace
+ * is 4 bytes too small. We define these alternatives here to teach
+ * the kernel about the 32-bit struct packing.
+ */
+struct btrfs_ioctl_timespec_32 {
+ __u64 sec;
+ __u32 nsec;
+} __attribute__ ((__packed__));
+
+struct btrfs_ioctl_received_subvol_args_32 {
+ char uuid[BTRFS_UUID_SIZE]; /* in */
+ __u64 stransid; /* in */
+ __u64 rtransid; /* out */
+ struct btrfs_ioctl_timespec_32 stime; /* in */
+ struct btrfs_ioctl_timespec_32 rtime; /* out */
+ __u64 flags; /* in */
+ __u64 reserved[16]; /* in */
+} __attribute__ ((__packed__));
+
+#define BTRFS_IOC_SET_RECEIVED_SUBVOL_32 _IOWR(BTRFS_IOCTL_MAGIC, 37, \
+ struct btrfs_ioctl_received_subvol_args_32)
+#endif
+
+
static int btrfs_clone(struct inode *src, struct inode *inode,
u64 off, u64 olen, u64 olen_aligned, u64 destoff);
@@ -585,6 +611,23 @@ fail:
return ret;
}
+static void btrfs_wait_nocow_write(struct btrfs_root *root)
+{
+ s64 writers;
+ DEFINE_WAIT(wait);
+
+ do {
+ prepare_to_wait(&root->subv_writers->wait, &wait,
+ TASK_UNINTERRUPTIBLE);
+
+ writers = percpu_counter_sum(&root->subv_writers->counter);
+ if (writers)
+ schedule();
+
+ finish_wait(&root->subv_writers->wait, &wait);
+ } while (writers);
+}
+
static int create_snapshot(struct btrfs_root *root, struct inode *dir,
struct dentry *dentry, char *name, int namelen,
u64 *async_transid, bool readonly,
@@ -598,15 +641,21 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
if (!root->ref_cows)
return -EINVAL;
+ atomic_inc(&root->will_be_snapshoted);
+ smp_mb__after_atomic_inc();
+ btrfs_wait_nocow_write(root);
+
ret = btrfs_start_delalloc_inodes(root, 0);
if (ret)
- return ret;
+ goto out;
btrfs_wait_ordered_extents(root, -1);
pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_NOFS);
- if (!pending_snapshot)
- return -ENOMEM;
+ if (!pending_snapshot) {
+ ret = -ENOMEM;
+ goto out;
+ }
btrfs_init_block_rsv(&pending_snapshot->block_rsv,
BTRFS_BLOCK_RSV_TEMP);
@@ -623,7 +672,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
&pending_snapshot->qgroup_reserved,
false);
if (ret)
- goto out;
+ goto free;
pending_snapshot->dentry = dentry;
pending_snapshot->root = root;
@@ -674,8 +723,10 @@ fail:
btrfs_subvolume_release_metadata(BTRFS_I(dir)->root,
&pending_snapshot->block_rsv,
pending_snapshot->qgroup_reserved);
-out:
+free:
kfree(pending_snapshot);
+out:
+ atomic_dec(&root->will_be_snapshoted);
return ret;
}
@@ -884,12 +935,14 @@ static int find_new_extents(struct btrfs_root *root,
min_key.type = BTRFS_EXTENT_DATA_KEY;
min_key.offset = *off;
- path->keep_locks = 1;
-
while (1) {
+ path->keep_locks = 1;
ret = btrfs_search_forward(root, &min_key, path, newer_than);
if (ret != 0)
goto none;
+ path->keep_locks = 0;
+ btrfs_unlock_up_safe(path, 1);
+process_slot:
if (min_key.objectid != ino)
goto none;
if (min_key.type != BTRFS_EXTENT_DATA_KEY)
@@ -908,6 +961,12 @@ static int find_new_extents(struct btrfs_root *root,
return 0;
}
+ path->slots[0]++;
+ if (path->slots[0] < btrfs_header_nritems(leaf)) {
+ btrfs_item_key_to_cpu(leaf, &min_key, path->slots[0]);
+ goto process_slot;
+ }
+
if (min_key.offset == (u64)-1)
goto none;
@@ -935,10 +994,13 @@ static struct extent_map *defrag_lookup_extent(struct inode *inode, u64 start)
read_unlock(&em_tree->lock);
if (!em) {
+ struct extent_state *cached = NULL;
+ u64 end = start + len - 1;
+
/* get the big lock and read metadata off disk */
- lock_extent(io_tree, start, start + len - 1);
+ lock_extent_bits(io_tree, start, end, 0, &cached);
em = btrfs_get_extent(inode, NULL, 0, start, len, 0);
- unlock_extent(io_tree, start, start + len - 1);
+ unlock_extent_cached(io_tree, start, end, &cached, GFP_NOFS);
if (IS_ERR(em))
return NULL;
@@ -957,7 +1019,8 @@ static bool defrag_check_next_extent(struct inode *inode, struct extent_map *em)
return false;
next = defrag_lookup_extent(inode, em->start + em->len);
- if (!next || next->block_start >= EXTENT_MAP_LAST_BYTE)
+ if (!next || next->block_start >= EXTENT_MAP_LAST_BYTE ||
+ (em->block_start + em->block_len == next->block_start))
ret = false;
free_extent_map(next);
@@ -1076,10 +1139,12 @@ again:
page_start = page_offset(page);
page_end = page_start + PAGE_CACHE_SIZE - 1;
while (1) {
- lock_extent(tree, page_start, page_end);
+ lock_extent_bits(tree, page_start, page_end,
+ 0, &cached_state);
ordered = btrfs_lookup_ordered_extent(inode,
page_start);
- unlock_extent(tree, page_start, page_end);
+ unlock_extent_cached(tree, page_start, page_end,
+ &cached_state, GFP_NOFS);
if (!ordered)
break;
@@ -1356,8 +1421,12 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
}
}
- if ((range->flags & BTRFS_DEFRAG_RANGE_START_IO))
+ if ((range->flags & BTRFS_DEFRAG_RANGE_START_IO)) {
filemap_flush(inode->i_mapping);
+ if (test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
+ &BTRFS_I(inode)->runtime_flags))
+ filemap_flush(inode->i_mapping);
+ }
if ((range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)) {
/* the filemap_flush will queue IO into the worker threads, but
@@ -1573,7 +1642,7 @@ static noinline int btrfs_ioctl_snap_create_transid(struct file *file,
if (src_inode->i_sb != file_inode(file)->i_sb) {
btrfs_info(BTRFS_I(src_inode)->root->fs_info,
"Snapshot src from another FS");
- ret = -EINVAL;
+ ret = -EXDEV;
} else if (!inode_owner_or_capable(src_inode)) {
/*
* Subvolume creation is not restricted, but snapshots
@@ -1797,7 +1866,9 @@ static noinline int may_destroy_subvol(struct btrfs_root *root)
if (di && !IS_ERR(di)) {
btrfs_dir_item_key_to_cpu(path->nodes[0], di, &key);
if (key.objectid == root->root_key.objectid) {
- ret = -ENOTEMPTY;
+ ret = -EPERM;
+ btrfs_err(root->fs_info, "deleting default subvolume "
+ "%llu is not allowed", key.objectid);
goto out;
}
btrfs_release_path(path);
@@ -2994,8 +3065,9 @@ process_slot:
new_key.offset + datal,
1);
if (ret) {
- btrfs_abort_transaction(trans, root,
- ret);
+ if (ret != -EINVAL)
+ btrfs_abort_transaction(trans,
+ root, ret);
btrfs_end_transaction(trans, root);
goto out;
}
@@ -3153,8 +3225,9 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
* decompress into destination's address_space (the file offset
* may change, so source mapping won't do), then recompress (or
* otherwise reinsert) a subrange.
- * - allow ranges within the same file to be cloned (provided
- * they don't overlap)?
+ *
+ * - split destination inode's inline extents. The inline extents can
+ * be either compressed or non-compressed.
*/
/* the destination must be opened for writing */
@@ -4353,10 +4426,9 @@ static long btrfs_ioctl_quota_rescan_wait(struct file *file, void __user *arg)
return btrfs_qgroup_wait_for_completion(root->fs_info);
}
-static long btrfs_ioctl_set_received_subvol(struct file *file,
- void __user *arg)
+static long _btrfs_ioctl_set_received_subvol(struct file *file,
+ struct btrfs_ioctl_received_subvol_args *sa)
{
- struct btrfs_ioctl_received_subvol_args *sa = NULL;
struct inode *inode = file_inode(file);
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_root_item *root_item = &root->root_item;
@@ -4384,13 +4456,6 @@ static long btrfs_ioctl_set_received_subvol(struct file *file,
goto out;
}
- sa = memdup_user(arg, sizeof(*sa));
- if (IS_ERR(sa)) {
- ret = PTR_ERR(sa);
- sa = NULL;
- goto out;
- }
-
/*
* 1 - root item
* 2 - uuid items (received uuid + subvol uuid)
@@ -4444,14 +4509,91 @@ static long btrfs_ioctl_set_received_subvol(struct file *file,
goto out;
}
+out:
+ up_write(&root->fs_info->subvol_sem);
+ mnt_drop_write_file(file);
+ return ret;
+}
+
+#ifdef CONFIG_64BIT
+static long btrfs_ioctl_set_received_subvol_32(struct file *file,
+ void __user *arg)
+{
+ struct btrfs_ioctl_received_subvol_args_32 *args32 = NULL;
+ struct btrfs_ioctl_received_subvol_args *args64 = NULL;
+ int ret = 0;
+
+ args32 = memdup_user(arg, sizeof(*args32));
+ if (IS_ERR(args32)) {
+ ret = PTR_ERR(args32);
+ args32 = NULL;
+ goto out;
+ }
+
+ args64 = kmalloc(sizeof(*args64), GFP_NOFS);
+ if (IS_ERR(args64)) {
+ ret = PTR_ERR(args64);
+ args64 = NULL;
+ goto out;
+ }
+
+ memcpy(args64->uuid, args32->uuid, BTRFS_UUID_SIZE);
+ args64->stransid = args32->stransid;
+ args64->rtransid = args32->rtransid;
+ args64->stime.sec = args32->stime.sec;
+ args64->stime.nsec = args32->stime.nsec;
+ args64->rtime.sec = args32->rtime.sec;
+ args64->rtime.nsec = args32->rtime.nsec;
+ args64->flags = args32->flags;
+
+ ret = _btrfs_ioctl_set_received_subvol(file, args64);
+ if (ret)
+ goto out;
+
+ memcpy(args32->uuid, args64->uuid, BTRFS_UUID_SIZE);
+ args32->stransid = args64->stransid;
+ args32->rtransid = args64->rtransid;
+ args32->stime.sec = args64->stime.sec;
+ args32->stime.nsec = args64->stime.nsec;
+ args32->rtime.sec = args64->rtime.sec;
+ args32->rtime.nsec = args64->rtime.nsec;
+ args32->flags = args64->flags;
+
+ ret = copy_to_user(arg, args32, sizeof(*args32));
+ if (ret)
+ ret = -EFAULT;
+
+out:
+ kfree(args32);
+ kfree(args64);
+ return ret;
+}
+#endif
+
+static long btrfs_ioctl_set_received_subvol(struct file *file,
+ void __user *arg)
+{
+ struct btrfs_ioctl_received_subvol_args *sa = NULL;
+ int ret = 0;
+
+ sa = memdup_user(arg, sizeof(*sa));
+ if (IS_ERR(sa)) {
+ ret = PTR_ERR(sa);
+ sa = NULL;
+ goto out;
+ }
+
+ ret = _btrfs_ioctl_set_received_subvol(file, sa);
+
+ if (ret)
+ goto out;
+
ret = copy_to_user(arg, sa, sizeof(*sa));
if (ret)
ret = -EFAULT;
out:
kfree(sa);
- up_write(&root->fs_info->subvol_sem);
- mnt_drop_write_file(file);
return ret;
}
@@ -4746,7 +4888,7 @@ long btrfs_ioctl(struct file *file, unsigned int
case BTRFS_IOC_SYNC: {
int ret;
- ret = btrfs_start_delalloc_roots(root->fs_info, 0);
+ ret = btrfs_start_delalloc_roots(root->fs_info, 0, -1);
if (ret)
return ret;
ret = btrfs_sync_fs(file->f_dentry->d_sb, 1);
@@ -4770,6 +4912,10 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_balance_progress(root, argp);
case BTRFS_IOC_SET_RECEIVED_SUBVOL:
return btrfs_ioctl_set_received_subvol(file, argp);
+#ifdef CONFIG_64BIT
+ case BTRFS_IOC_SET_RECEIVED_SUBVOL_32:
+ return btrfs_ioctl_set_received_subvol_32(file, argp);
+#endif
case BTRFS_IOC_SEND:
return btrfs_ioctl_send(file, argp);
case BTRFS_IOC_GET_DEV_STATS:
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index b16450b840e7..a94b05f72869 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -349,10 +349,13 @@ int btrfs_dec_test_first_ordered_pending(struct inode *inode,
if (!uptodate)
set_bit(BTRFS_ORDERED_IOERR, &entry->flags);
- if (entry->bytes_left == 0)
+ if (entry->bytes_left == 0) {
ret = test_and_set_bit(BTRFS_ORDERED_IO_DONE, &entry->flags);
- else
+ if (waitqueue_active(&entry->wait))
+ wake_up(&entry->wait);
+ } else {
ret = 1;
+ }
out:
if (!ret && cached && entry) {
*cached = entry;
@@ -410,10 +413,13 @@ have_entry:
if (!uptodate)
set_bit(BTRFS_ORDERED_IOERR, &entry->flags);
- if (entry->bytes_left == 0)
+ if (entry->bytes_left == 0) {
ret = test_and_set_bit(BTRFS_ORDERED_IO_DONE, &entry->flags);
- else
+ if (waitqueue_active(&entry->wait))
+ wake_up(&entry->wait);
+ } else {
ret = 1;
+ }
out:
if (!ret && cached && entry) {
*cached = entry;
@@ -424,27 +430,48 @@ out:
}
/* Needs to either be called under a log transaction or the log_mutex */
-void btrfs_get_logged_extents(struct btrfs_root *log, struct inode *inode)
+void btrfs_get_logged_extents(struct inode *inode,
+ struct list_head *logged_list)
{
struct btrfs_ordered_inode_tree *tree;
struct btrfs_ordered_extent *ordered;
struct rb_node *n;
- int index = log->log_transid % 2;
tree = &BTRFS_I(inode)->ordered_tree;
spin_lock_irq(&tree->lock);
for (n = rb_first(&tree->tree); n; n = rb_next(n)) {
ordered = rb_entry(n, struct btrfs_ordered_extent, rb_node);
- spin_lock(&log->log_extents_lock[index]);
- if (list_empty(&ordered->log_list)) {
- list_add_tail(&ordered->log_list, &log->logged_list[index]);
- atomic_inc(&ordered->refs);
- }
- spin_unlock(&log->log_extents_lock[index]);
+ if (!list_empty(&ordered->log_list))
+ continue;
+ list_add_tail(&ordered->log_list, logged_list);
+ atomic_inc(&ordered->refs);
}
spin_unlock_irq(&tree->lock);
}
+void btrfs_put_logged_extents(struct list_head *logged_list)
+{
+ struct btrfs_ordered_extent *ordered;
+
+ while (!list_empty(logged_list)) {
+ ordered = list_first_entry(logged_list,
+ struct btrfs_ordered_extent,
+ log_list);
+ list_del_init(&ordered->log_list);
+ btrfs_put_ordered_extent(ordered);
+ }
+}
+
+void btrfs_submit_logged_extents(struct list_head *logged_list,
+ struct btrfs_root *log)
+{
+ int index = log->log_transid % 2;
+
+ spin_lock_irq(&log->log_extents_lock[index]);
+ list_splice_tail(logged_list, &log->logged_list[index]);
+ spin_unlock_irq(&log->log_extents_lock[index]);
+}
+
void btrfs_wait_logged_extents(struct btrfs_root *log, u64 transid)
{
struct btrfs_ordered_extent *ordered;
@@ -577,7 +604,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr)
INIT_LIST_HEAD(&splice);
INIT_LIST_HEAD(&works);
- mutex_lock(&root->fs_info->ordered_operations_mutex);
+ mutex_lock(&root->ordered_extent_mutex);
spin_lock(&root->ordered_extent_lock);
list_splice_init(&root->ordered_extents, &splice);
while (!list_empty(&splice) && nr) {
@@ -588,10 +615,11 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr)
atomic_inc(&ordered->refs);
spin_unlock(&root->ordered_extent_lock);
- ordered->flush_work.func = btrfs_run_ordered_extent_work;
+ btrfs_init_work(&ordered->flush_work,
+ btrfs_run_ordered_extent_work, NULL, NULL);
list_add_tail(&ordered->work_list, &works);
- btrfs_queue_worker(&root->fs_info->flush_workers,
- &ordered->flush_work);
+ btrfs_queue_work(root->fs_info->flush_workers,
+ &ordered->flush_work);
cond_resched();
spin_lock(&root->ordered_extent_lock);
@@ -608,7 +636,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr)
btrfs_put_ordered_extent(ordered);
cond_resched();
}
- mutex_unlock(&root->fs_info->ordered_operations_mutex);
+ mutex_unlock(&root->ordered_extent_mutex);
return count;
}
@@ -621,6 +649,7 @@ void btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr)
INIT_LIST_HEAD(&splice);
+ mutex_lock(&fs_info->ordered_operations_mutex);
spin_lock(&fs_info->ordered_root_lock);
list_splice_init(&fs_info->ordered_roots, &splice);
while (!list_empty(&splice) && nr) {
@@ -643,6 +672,7 @@ void btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr)
}
list_splice_tail(&splice, &fs_info->ordered_roots);
spin_unlock(&fs_info->ordered_root_lock);
+ mutex_unlock(&fs_info->ordered_operations_mutex);
}
/*
@@ -704,8 +734,8 @@ int btrfs_run_ordered_operations(struct btrfs_trans_handle *trans,
goto out;
}
list_add_tail(&work->list, &works);
- btrfs_queue_worker(&root->fs_info->flush_workers,
- &work->work);
+ btrfs_queue_work(root->fs_info->flush_workers,
+ &work->work);
cond_resched();
spin_lock(&root->fs_info->ordered_root_lock);
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
index 9b0450f7ac20..246897058efb 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -197,7 +197,11 @@ void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
struct inode *inode);
int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr);
void btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr);
-void btrfs_get_logged_extents(struct btrfs_root *log, struct inode *inode);
+void btrfs_get_logged_extents(struct inode *inode,
+ struct list_head *logged_list);
+void btrfs_put_logged_extents(struct list_head *logged_list);
+void btrfs_submit_logged_extents(struct list_head *logged_list,
+ struct btrfs_root *log);
void btrfs_wait_logged_extents(struct btrfs_root *log, u64 transid);
void btrfs_free_logged_extents(struct btrfs_root *log, u64 transid);
int __init ordered_data_init(void);
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 472302a2d745..2cf905877aaf 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -1509,8 +1509,8 @@ int btrfs_run_qgroups(struct btrfs_trans_handle *trans,
ret = qgroup_rescan_init(fs_info, 0, 1);
if (!ret) {
qgroup_rescan_zero_tracking(fs_info);
- btrfs_queue_worker(&fs_info->qgroup_rescan_workers,
- &fs_info->qgroup_rescan_work);
+ btrfs_queue_work(fs_info->qgroup_rescan_workers,
+ &fs_info->qgroup_rescan_work);
}
ret = 0;
}
@@ -2095,7 +2095,8 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
memset(&fs_info->qgroup_rescan_work, 0,
sizeof(fs_info->qgroup_rescan_work));
- fs_info->qgroup_rescan_work.func = btrfs_qgroup_rescan_worker;
+ btrfs_init_work(&fs_info->qgroup_rescan_work,
+ btrfs_qgroup_rescan_worker, NULL, NULL);
if (ret) {
err:
@@ -2158,8 +2159,8 @@ btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info)
qgroup_rescan_zero_tracking(fs_info);
- btrfs_queue_worker(&fs_info->qgroup_rescan_workers,
- &fs_info->qgroup_rescan_work);
+ btrfs_queue_work(fs_info->qgroup_rescan_workers,
+ &fs_info->qgroup_rescan_work);
return 0;
}
@@ -2190,6 +2191,6 @@ void
btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info)
{
if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN)
- btrfs_queue_worker(&fs_info->qgroup_rescan_workers,
- &fs_info->qgroup_rescan_work);
+ btrfs_queue_work(fs_info->qgroup_rescan_workers,
+ &fs_info->qgroup_rescan_work);
}
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 9af0b25d991a..4055291a523e 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1416,20 +1416,18 @@ cleanup:
static void async_rmw_stripe(struct btrfs_raid_bio *rbio)
{
- rbio->work.flags = 0;
- rbio->work.func = rmw_work;
+ btrfs_init_work(&rbio->work, rmw_work, NULL, NULL);
- btrfs_queue_worker(&rbio->fs_info->rmw_workers,
- &rbio->work);
+ btrfs_queue_work(rbio->fs_info->rmw_workers,
+ &rbio->work);
}
static void async_read_rebuild(struct btrfs_raid_bio *rbio)
{
- rbio->work.flags = 0;
- rbio->work.func = read_rebuild_work;
+ btrfs_init_work(&rbio->work, read_rebuild_work, NULL, NULL);
- btrfs_queue_worker(&rbio->fs_info->rmw_workers,
- &rbio->work);
+ btrfs_queue_work(rbio->fs_info->rmw_workers,
+ &rbio->work);
}
/*
@@ -1667,10 +1665,9 @@ static void btrfs_raid_unplug(struct blk_plug_cb *cb, bool from_schedule)
plug = container_of(cb, struct btrfs_plug_cb, cb);
if (from_schedule) {
- plug->work.flags = 0;
- plug->work.func = unplug_work;
- btrfs_queue_worker(&plug->info->rmw_workers,
- &plug->work);
+ btrfs_init_work(&plug->work, unplug_work, NULL, NULL);
+ btrfs_queue_work(plug->info->rmw_workers,
+ &plug->work);
return;
}
run_plug(plug);
diff --git a/fs/btrfs/reada.c b/fs/btrfs/reada.c
index 31c797c48c3e..30947f923620 100644
--- a/fs/btrfs/reada.c
+++ b/fs/btrfs/reada.c
@@ -793,10 +793,10 @@ static void reada_start_machine(struct btrfs_fs_info *fs_info)
/* FIXME we cannot handle this properly right now */
BUG();
}
- rmw->work.func = reada_start_machine_worker;
+ btrfs_init_work(&rmw->work, reada_start_machine_worker, NULL, NULL);
rmw->fs_info = fs_info;
- btrfs_queue_worker(&fs_info->readahead_workers, &rmw->work);
+ btrfs_queue_work(fs_info->readahead_workers, &rmw->work);
}
#ifdef DEBUG
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 07b3b36f40ee..def428a25b2a 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -4248,7 +4248,7 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
btrfs_info(extent_root->fs_info, "relocating block group %llu flags %llu",
rc->block_group->key.objectid, rc->block_group->flags);
- ret = btrfs_start_delalloc_roots(fs_info, 0);
+ ret = btrfs_start_delalloc_roots(fs_info, 0, -1);
if (ret < 0) {
err = ret;
goto out;
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
index 1389b69059de..38bb47e7d6b1 100644
--- a/fs/btrfs/root-tree.c
+++ b/fs/btrfs/root-tree.c
@@ -16,6 +16,7 @@
* Boston, MA 021110-1307, USA.
*/
+#include <linux/err.h>
#include <linux/uuid.h>
#include "ctree.h"
#include "transaction.h"
@@ -271,7 +272,7 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root)
key.offset++;
root = btrfs_read_fs_root(tree_root, &root_key);
- err = PTR_RET(root);
+ err = PTR_ERR_OR_ZERO(root);
if (err && err != -ENOENT) {
break;
} else if (err == -ENOENT) {
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index efba5d1282ee..93e6d7172844 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -315,6 +315,16 @@ static void scrub_pending_trans_workers_inc(struct scrub_ctx *sctx)
atomic_inc(&fs_info->scrubs_running);
atomic_inc(&fs_info->scrubs_paused);
mutex_unlock(&fs_info->scrub_lock);
+
+ /*
+ * check if @scrubs_running=@scrubs_paused condition
+ * inside wait_event() is not an atomic operation.
+ * which means we may inc/dec @scrub_running/paused
+ * at any time. Let's wake up @scrub_pause_wait as
+ * much as we can to let commit transaction blocked less.
+ */
+ wake_up(&fs_info->scrub_pause_wait);
+
atomic_inc(&sctx->workers_pending);
}
@@ -418,7 +428,8 @@ struct scrub_ctx *scrub_setup_ctx(struct btrfs_device *dev, int is_dev_replace)
sbio->index = i;
sbio->sctx = sctx;
sbio->page_count = 0;
- sbio->work.func = scrub_bio_end_io_worker;
+ btrfs_init_work(&sbio->work, scrub_bio_end_io_worker,
+ NULL, NULL);
if (i != SCRUB_BIOS_PER_SCTX - 1)
sctx->bios[i]->next_free = i + 1;
@@ -987,9 +998,10 @@ nodatasum_case:
fixup_nodatasum->root = fs_info->extent_root;
fixup_nodatasum->mirror_num = failed_mirror_index + 1;
scrub_pending_trans_workers_inc(sctx);
- fixup_nodatasum->work.func = scrub_fixup_nodatasum;
- btrfs_queue_worker(&fs_info->scrub_workers,
- &fixup_nodatasum->work);
+ btrfs_init_work(&fixup_nodatasum->work, scrub_fixup_nodatasum,
+ NULL, NULL);
+ btrfs_queue_work(fs_info->scrub_workers,
+ &fixup_nodatasum->work);
goto out;
}
@@ -1603,8 +1615,8 @@ static void scrub_wr_bio_end_io(struct bio *bio, int err)
sbio->err = err;
sbio->bio = bio;
- sbio->work.func = scrub_wr_bio_end_io_worker;
- btrfs_queue_worker(&fs_info->scrub_wr_completion_workers, &sbio->work);
+ btrfs_init_work(&sbio->work, scrub_wr_bio_end_io_worker, NULL, NULL);
+ btrfs_queue_work(fs_info->scrub_wr_completion_workers, &sbio->work);
}
static void scrub_wr_bio_end_io_worker(struct btrfs_work *work)
@@ -2072,7 +2084,7 @@ static void scrub_bio_end_io(struct bio *bio, int err)
sbio->err = err;
sbio->bio = bio;
- btrfs_queue_worker(&fs_info->scrub_workers, &sbio->work);
+ btrfs_queue_work(fs_info->scrub_workers, &sbio->work);
}
static void scrub_bio_end_io_worker(struct btrfs_work *work)
@@ -2686,10 +2698,23 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,
wait_event(sctx->list_wait,
atomic_read(&sctx->bios_in_flight) == 0);
- atomic_set(&sctx->wr_ctx.flush_all_writes, 0);
+ atomic_inc(&fs_info->scrubs_paused);
+ wake_up(&fs_info->scrub_pause_wait);
+
+ /*
+ * must be called before we decrease @scrub_paused.
+ * make sure we don't block transaction commit while
+ * we are waiting pending workers finished.
+ */
wait_event(sctx->list_wait,
atomic_read(&sctx->workers_pending) == 0);
- scrub_blocked_if_needed(fs_info);
+ atomic_set(&sctx->wr_ctx.flush_all_writes, 0);
+
+ mutex_lock(&fs_info->scrub_lock);
+ __scrub_blocked_if_needed(fs_info);
+ atomic_dec(&fs_info->scrubs_paused);
+ mutex_unlock(&fs_info->scrub_lock);
+ wake_up(&fs_info->scrub_pause_wait);
btrfs_put_block_group(cache);
if (ret)
@@ -2757,33 +2782,35 @@ static noinline_for_stack int scrub_workers_get(struct btrfs_fs_info *fs_info,
int is_dev_replace)
{
int ret = 0;
+ int flags = WQ_FREEZABLE | WQ_UNBOUND;
+ int max_active = fs_info->thread_pool_size;
if (fs_info->scrub_workers_refcnt == 0) {
if (is_dev_replace)
- btrfs_init_workers(&fs_info->scrub_workers, "scrub", 1,
- &fs_info->generic_worker);
+ fs_info->scrub_workers =
+ btrfs_alloc_workqueue("btrfs-scrub", flags,
+ 1, 4);
else
- btrfs_init_workers(&fs_info->scrub_workers, "scrub",
- fs_info->thread_pool_size,
- &fs_info->generic_worker);
- fs_info->scrub_workers.idle_thresh = 4;
- ret = btrfs_start_workers(&fs_info->scrub_workers);
- if (ret)
+ fs_info->scrub_workers =
+ btrfs_alloc_workqueue("btrfs-scrub", flags,
+ max_active, 4);
+ if (!fs_info->scrub_workers) {
+ ret = -ENOMEM;
goto out;
- btrfs_init_workers(&fs_info->scrub_wr_completion_workers,
- "scrubwrc",
- fs_info->thread_pool_size,
- &fs_info->generic_worker);
- fs_info->scrub_wr_completion_workers.idle_thresh = 2;
- ret = btrfs_start_workers(
- &fs_info->scrub_wr_completion_workers);
- if (ret)
+ }
+ fs_info->scrub_wr_completion_workers =
+ btrfs_alloc_workqueue("btrfs-scrubwrc", flags,
+ max_active, 2);
+ if (!fs_info->scrub_wr_completion_workers) {
+ ret = -ENOMEM;
goto out;
- btrfs_init_workers(&fs_info->scrub_nocow_workers, "scrubnc", 1,
- &fs_info->generic_worker);
- ret = btrfs_start_workers(&fs_info->scrub_nocow_workers);
- if (ret)
+ }
+ fs_info->scrub_nocow_workers =
+ btrfs_alloc_workqueue("btrfs-scrubnc", flags, 1, 0);
+ if (!fs_info->scrub_nocow_workers) {
+ ret = -ENOMEM;
goto out;
+ }
}
++fs_info->scrub_workers_refcnt;
out:
@@ -2793,9 +2820,9 @@ out:
static noinline_for_stack void scrub_workers_put(struct btrfs_fs_info *fs_info)
{
if (--fs_info->scrub_workers_refcnt == 0) {
- btrfs_stop_workers(&fs_info->scrub_workers);
- btrfs_stop_workers(&fs_info->scrub_wr_completion_workers);
- btrfs_stop_workers(&fs_info->scrub_nocow_workers);
+ btrfs_destroy_workqueue(fs_info->scrub_workers);
+ btrfs_destroy_workqueue(fs_info->scrub_wr_completion_workers);
+ btrfs_destroy_workqueue(fs_info->scrub_nocow_workers);
}
WARN_ON(fs_info->scrub_workers_refcnt < 0);
}
@@ -3106,10 +3133,10 @@ static int copy_nocow_pages(struct scrub_ctx *sctx, u64 logical, u64 len,
nocow_ctx->len = len;
nocow_ctx->mirror_num = mirror_num;
nocow_ctx->physical_for_dev_replace = physical_for_dev_replace;
- nocow_ctx->work.func = copy_nocow_pages_worker;
+ btrfs_init_work(&nocow_ctx->work, copy_nocow_pages_worker, NULL, NULL);
INIT_LIST_HEAD(&nocow_ctx->inodes);
- btrfs_queue_worker(&fs_info->scrub_nocow_workers,
- &nocow_ctx->work);
+ btrfs_queue_work(fs_info->scrub_nocow_workers,
+ &nocow_ctx->work);
return 0;
}
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 9dde9717c1b9..9b6da9d55f9a 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -51,15 +51,18 @@ struct fs_path {
struct {
char *start;
char *end;
- char *prepared;
char *buf;
- int buf_len;
- unsigned int reversed:1;
- unsigned int virtual_mem:1;
+ unsigned short buf_len:15;
+ unsigned short reversed:1;
char inline_buf[];
};
- char pad[PAGE_SIZE];
+ /*
+ * Average path length does not exceed 200 bytes, we'll have
+ * better packing in the slab and higher chance to satisfy
+ * a allocation later during send.
+ */
+ char pad[256];
};
};
#define FS_PATH_INLINE_SIZE \
@@ -109,6 +112,7 @@ struct send_ctx {
int cur_inode_deleted;
u64 cur_inode_size;
u64 cur_inode_mode;
+ u64 cur_inode_rdev;
u64 cur_inode_last_extent;
u64 send_progress;
@@ -120,6 +124,8 @@ struct send_ctx {
struct list_head name_cache_list;
int name_cache_size;
+ struct file_ra_state ra;
+
char *read_buf;
/*
@@ -175,6 +181,47 @@ struct send_ctx {
* own move/rename can be performed.
*/
struct rb_root waiting_dir_moves;
+
+ /*
+ * A directory that is going to be rm'ed might have a child directory
+ * which is in the pending directory moves index above. In this case,
+ * the directory can only be removed after the move/rename of its child
+ * is performed. Example:
+ *
+ * Parent snapshot:
+ *
+ * . (ino 256)
+ * |-- a/ (ino 257)
+ * |-- b/ (ino 258)
+ * |-- c/ (ino 259)
+ * | |-- x/ (ino 260)
+ * |
+ * |-- y/ (ino 261)
+ *
+ * Send snapshot:
+ *
+ * . (ino 256)
+ * |-- a/ (ino 257)
+ * |-- b/ (ino 258)
+ * |-- YY/ (ino 261)
+ * |-- x/ (ino 260)
+ *
+ * Sequence of steps that lead to the send snapshot:
+ * rm -f /a/b/c/foo.txt
+ * mv /a/b/y /a/b/YY
+ * mv /a/b/c/x /a/b/YY
+ * rmdir /a/b/c
+ *
+ * When the child is processed, its move/rename is delayed until its
+ * parent is processed (as explained above), but all other operations
+ * like update utimes, chown, chgrp, etc, are performed and the paths
+ * that it uses for those operations must use the orphanized name of
+ * its parent (the directory we're going to rm later), so we need to
+ * memorize that name.
+ *
+ * Indexed by the inode number of the directory to be deleted.
+ */
+ struct rb_root orphan_dirs;
};
struct pending_dir_move {
@@ -189,6 +236,18 @@ struct pending_dir_move {
struct waiting_dir_move {
struct rb_node node;
u64 ino;
+ /*
+ * There might be some directory that could not be removed because it
+ * was waiting for this directory inode to be moved first. Therefore
+ * after this directory is moved, we can try to rmdir the ino rmdir_ino.
+ */
+ u64 rmdir_ino;
+};
+
+struct orphan_dir_info {
+ struct rb_node node;
+ u64 ino;
+ u64 gen;
};
struct name_cache_entry {
@@ -214,6 +273,11 @@ struct name_cache_entry {
static int is_waiting_for_move(struct send_ctx *sctx, u64 ino);
+static struct waiting_dir_move *
+get_waiting_dir_move(struct send_ctx *sctx, u64 ino);
+
+static int is_waiting_for_rm(struct send_ctx *sctx, u64 dir_ino);
+
static int need_send_hole(struct send_ctx *sctx)
{
return (sctx->parent_root && !sctx->cur_inode_new &&
@@ -242,7 +306,6 @@ static struct fs_path *fs_path_alloc(void)
if (!p)
return NULL;
p->reversed = 0;
- p->virtual_mem = 0;
p->buf = p->inline_buf;
p->buf_len = FS_PATH_INLINE_SIZE;
fs_path_reset(p);
@@ -265,12 +328,8 @@ static void fs_path_free(struct fs_path *p)
{
if (!p)
return;
- if (p->buf != p->inline_buf) {
- if (p->virtual_mem)
- vfree(p->buf);
- else
- kfree(p->buf);
- }
+ if (p->buf != p->inline_buf)
+ kfree(p->buf);
kfree(p);
}
@@ -292,40 +351,23 @@ static int fs_path_ensure_buf(struct fs_path *p, int len)
path_len = p->end - p->start;
old_buf_len = p->buf_len;
- len = PAGE_ALIGN(len);
-
- if (p->buf == p->inline_buf) {
- tmp_buf = kmalloc(len, GFP_NOFS | __GFP_NOWARN);
- if (!tmp_buf) {
- tmp_buf = vmalloc(len);
- if (!tmp_buf)
- return -ENOMEM;
- p->virtual_mem = 1;
- }
- memcpy(tmp_buf, p->buf, p->buf_len);
- p->buf = tmp_buf;
- p->buf_len = len;
- } else {
- if (p->virtual_mem) {
- tmp_buf = vmalloc(len);
- if (!tmp_buf)
- return -ENOMEM;
- memcpy(tmp_buf, p->buf, p->buf_len);
- vfree(p->buf);
- } else {
- tmp_buf = krealloc(p->buf, len, GFP_NOFS);
- if (!tmp_buf) {
- tmp_buf = vmalloc(len);
- if (!tmp_buf)
- return -ENOMEM;
- memcpy(tmp_buf, p->buf, p->buf_len);
- kfree(p->buf);
- p->virtual_mem = 1;
- }
- }
- p->buf = tmp_buf;
- p->buf_len = len;
- }
+
+ /*
+ * First time the inline_buf does not suffice
+ */
+ if (p->buf == p->inline_buf)
+ tmp_buf = kmalloc(len, GFP_NOFS);
+ else
+ tmp_buf = krealloc(p->buf, len, GFP_NOFS);
+ if (!tmp_buf)
+ return -ENOMEM;
+ p->buf = tmp_buf;
+ /*
+ * The real size of the buffer is bigger, this will let the fast path
+ * happen most of the time
+ */
+ p->buf_len = ksize(p->buf);
+
if (p->reversed) {
tmp_buf = p->buf + old_buf_len - path_len - 1;
p->end = p->buf + p->buf_len - 1;
@@ -338,7 +380,8 @@ static int fs_path_ensure_buf(struct fs_path *p, int len)
return 0;
}
-static int fs_path_prepare_for_add(struct fs_path *p, int name_len)
+static int fs_path_prepare_for_add(struct fs_path *p, int name_len,
+ char **prepared)
{
int ret;
int new_len;
@@ -354,11 +397,11 @@ static int fs_path_prepare_for_add(struct fs_path *p, int name_len)
if (p->start != p->end)
*--p->start = '/';
p->start -= name_len;
- p->prepared = p->start;
+ *prepared = p->start;
} else {
if (p->start != p->end)
*p->end++ = '/';
- p->prepared = p->end;
+ *prepared = p->end;
p->end += name_len;
*p->end = 0;
}
@@ -370,12 +413,12 @@ out:
static int fs_path_add(struct fs_path *p, const char *name, int name_len)
{
int ret;
+ char *prepared;
- ret = fs_path_prepare_for_add(p, name_len);
+ ret = fs_path_prepare_for_add(p, name_len, &prepared);
if (ret < 0)
goto out;
- memcpy(p->prepared, name, name_len);
- p->prepared = NULL;
+ memcpy(prepared, name, name_len);
out:
return ret;
@@ -384,12 +427,12 @@ out:
static int fs_path_add_path(struct fs_path *p, struct fs_path *p2)
{
int ret;
+ char *prepared;
- ret = fs_path_prepare_for_add(p, p2->end - p2->start);
+ ret = fs_path_prepare_for_add(p, p2->end - p2->start, &prepared);
if (ret < 0)
goto out;
- memcpy(p->prepared, p2->start, p2->end - p2->start);
- p->prepared = NULL;
+ memcpy(prepared, p2->start, p2->end - p2->start);
out:
return ret;
@@ -400,13 +443,13 @@ static int fs_path_add_from_extent_buffer(struct fs_path *p,
unsigned long off, int len)
{
int ret;
+ char *prepared;
- ret = fs_path_prepare_for_add(p, len);
+ ret = fs_path_prepare_for_add(p, len, &prepared);
if (ret < 0)
goto out;
- read_extent_buffer(eb, p->prepared, off, len);
- p->prepared = NULL;
+ read_extent_buffer(eb, prepared, off, len);
out:
return ret;
@@ -915,9 +958,7 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
struct btrfs_dir_item *di;
struct btrfs_key di_key;
char *buf = NULL;
- char *buf2 = NULL;
- int buf_len;
- int buf_virtual = 0;
+ const int buf_len = PATH_MAX;
u32 name_len;
u32 data_len;
u32 cur;
@@ -927,7 +968,6 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
int num;
u8 type;
- buf_len = PAGE_SIZE;
buf = kmalloc(buf_len, GFP_NOFS);
if (!buf) {
ret = -ENOMEM;
@@ -949,30 +989,12 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
type = btrfs_dir_type(eb, di);
btrfs_dir_item_key_to_cpu(eb, di, &di_key);
+ /*
+ * Path too long
+ */
if (name_len + data_len > buf_len) {
- buf_len = PAGE_ALIGN(name_len + data_len);
- if (buf_virtual) {
- buf2 = vmalloc(buf_len);
- if (!buf2) {
- ret = -ENOMEM;
- goto out;
- }
- vfree(buf);
- } else {
- buf2 = krealloc(buf, buf_len, GFP_NOFS);
- if (!buf2) {
- buf2 = vmalloc(buf_len);
- if (!buf2) {
- ret = -ENOMEM;
- goto out;
- }
- kfree(buf);
- buf_virtual = 1;
- }
- }
-
- buf = buf2;
- buf2 = NULL;
+ ret = -ENAMETOOLONG;
+ goto out;
}
read_extent_buffer(eb, buf, (unsigned long)(di + 1),
@@ -995,10 +1017,7 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
}
out:
- if (buf_virtual)
- vfree(buf);
- else
- kfree(buf);
+ kfree(buf);
return ret;
}
@@ -1292,8 +1311,6 @@ static int find_extent_clone(struct send_ctx *sctx,
extent_item_pos = logical - found_key.objectid;
else
extent_item_pos = 0;
-
- extent_item_pos = logical - found_key.objectid;
ret = iterate_extent_inodes(sctx->send_root->fs_info,
found_key.objectid, extent_item_pos, 1,
__iterate_backrefs, backref_ctx);
@@ -1418,11 +1435,7 @@ static int gen_unique_name(struct send_ctx *sctx,
while (1) {
len = snprintf(tmp, sizeof(tmp), "o%llu-%llu-%llu",
ino, gen, idx);
- if (len >= sizeof(tmp)) {
- /* should really not happen */
- ret = -EOVERFLOW;
- goto out;
- }
+ ASSERT(len < sizeof(tmp));
di = btrfs_lookup_dir_item(NULL, sctx->send_root,
path, BTRFS_FIRST_FREE_OBJECTID,
@@ -1898,13 +1911,20 @@ static void name_cache_delete(struct send_ctx *sctx,
nce_head = radix_tree_lookup(&sctx->name_cache,
(unsigned long)nce->ino);
- BUG_ON(!nce_head);
+ if (!nce_head) {
+ btrfs_err(sctx->send_root->fs_info,
+ "name_cache_delete lookup failed ino %llu cache size %d, leaking memory",
+ nce->ino, sctx->name_cache_size);
+ }
list_del(&nce->radix_list);
list_del(&nce->list);
sctx->name_cache_size--;
- if (list_empty(nce_head)) {
+ /*
+ * We may not get to the final release of nce_head if the lookup fails
+ */
+ if (nce_head && list_empty(nce_head)) {
radix_tree_delete(&sctx->name_cache, (unsigned long)nce->ino);
kfree(nce_head);
}
@@ -1977,7 +1997,6 @@ static void name_cache_free(struct send_ctx *sctx)
*/
static int __get_cur_name_and_parent(struct send_ctx *sctx,
u64 ino, u64 gen,
- int skip_name_cache,
u64 *parent_ino,
u64 *parent_gen,
struct fs_path *dest)
@@ -1987,8 +2006,6 @@ static int __get_cur_name_and_parent(struct send_ctx *sctx,
struct btrfs_path *path = NULL;
struct name_cache_entry *nce = NULL;
- if (skip_name_cache)
- goto get_ref;
/*
* First check if we already did a call to this function with the same
* ino/gen. If yes, check if the cache entry is still up-to-date. If yes
@@ -2033,12 +2050,11 @@ static int __get_cur_name_and_parent(struct send_ctx *sctx,
goto out_cache;
}
-get_ref:
/*
* Depending on whether the inode was already processed or not, use
* send_root or parent_root for ref lookup.
*/
- if (ino < sctx->send_progress && !skip_name_cache)
+ if (ino < sctx->send_progress)
ret = get_first_ref(sctx->send_root, ino,
parent_ino, parent_gen, dest);
else
@@ -2062,8 +2078,6 @@ get_ref:
goto out;
ret = 1;
}
- if (skip_name_cache)
- goto out;
out_cache:
/*
@@ -2131,9 +2145,6 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen,
u64 parent_inode = 0;
u64 parent_gen = 0;
int stop = 0;
- u64 start_ino = ino;
- u64 start_gen = gen;
- int skip_name_cache = 0;
name = fs_path_alloc();
if (!name) {
@@ -2141,31 +2152,33 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen,
goto out;
}
- if (is_waiting_for_move(sctx, ino))
- skip_name_cache = 1;
-
-again:
dest->reversed = 1;
fs_path_reset(dest);
while (!stop && ino != BTRFS_FIRST_FREE_OBJECTID) {
fs_path_reset(name);
- ret = __get_cur_name_and_parent(sctx, ino, gen, skip_name_cache,
- &parent_inode, &parent_gen, name);
+ if (is_waiting_for_rm(sctx, ino)) {
+ ret = gen_unique_name(sctx, ino, gen, name);
+ if (ret < 0)
+ goto out;
+ ret = fs_path_add_path(dest, name);
+ break;
+ }
+
+ if (is_waiting_for_move(sctx, ino)) {
+ ret = get_first_ref(sctx->parent_root, ino,
+ &parent_inode, &parent_gen, name);
+ } else {
+ ret = __get_cur_name_and_parent(sctx, ino, gen,
+ &parent_inode,
+ &parent_gen, name);
+ if (ret)
+ stop = 1;
+ }
+
if (ret < 0)
goto out;
- if (ret)
- stop = 1;
-
- if (!skip_name_cache &&
- is_waiting_for_move(sctx, parent_inode)) {
- ino = start_ino;
- gen = start_gen;
- stop = 0;
- skip_name_cache = 1;
- goto again;
- }
ret = fs_path_add_path(dest, name);
if (ret < 0)
@@ -2429,10 +2442,16 @@ verbose_printk("btrfs: send_create_inode %llu\n", ino);
if (!p)
return -ENOMEM;
- ret = get_inode_info(sctx->send_root, ino, NULL, &gen, &mode, NULL,
- NULL, &rdev);
- if (ret < 0)
- goto out;
+ if (ino != sctx->cur_ino) {
+ ret = get_inode_info(sctx->send_root, ino, NULL, &gen, &mode,
+ NULL, NULL, &rdev);
+ if (ret < 0)
+ goto out;
+ } else {
+ gen = sctx->cur_inode_gen;
+ mode = sctx->cur_inode_mode;
+ rdev = sctx->cur_inode_rdev;
+ }
if (S_ISREG(mode)) {
cmd = BTRFS_SEND_C_MKFILE;
@@ -2512,17 +2531,26 @@ static int did_create_dir(struct send_ctx *sctx, u64 dir)
key.objectid = dir;
key.type = BTRFS_DIR_INDEX_KEY;
key.offset = 0;
+ ret = btrfs_search_slot(NULL, sctx->send_root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+
while (1) {
- ret = btrfs_search_slot_for_read(sctx->send_root, &key, path,
- 1, 0);
- if (ret < 0)
- goto out;
- if (!ret) {
- eb = path->nodes[0];
- slot = path->slots[0];
- btrfs_item_key_to_cpu(eb, &found_key, slot);
+ eb = path->nodes[0];
+ slot = path->slots[0];
+ if (slot >= btrfs_header_nritems(eb)) {
+ ret = btrfs_next_leaf(sctx->send_root, path);
+ if (ret < 0) {
+ goto out;
+ } else if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ continue;
}
- if (ret || found_key.objectid != key.objectid ||
+
+ btrfs_item_key_to_cpu(eb, &found_key, slot);
+ if (found_key.objectid != key.objectid ||
found_key.type != key.type) {
ret = 0;
goto out;
@@ -2537,8 +2565,7 @@ static int did_create_dir(struct send_ctx *sctx, u64 dir)
goto out;
}
- key.offset = found_key.offset + 1;
- btrfs_release_path(path);
+ path->slots[0]++;
}
out:
@@ -2590,7 +2617,7 @@ struct recorded_ref {
* everything mixed. So we first record all refs and later process them.
* This function is a helper to record one ref.
*/
-static int record_ref(struct list_head *head, u64 dir,
+static int __record_ref(struct list_head *head, u64 dir,
u64 dir_gen, struct fs_path *path)
{
struct recorded_ref *ref;
@@ -2676,12 +2703,78 @@ out:
return ret;
}
+static struct orphan_dir_info *
+add_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
+{
+ struct rb_node **p = &sctx->orphan_dirs.rb_node;
+ struct rb_node *parent = NULL;
+ struct orphan_dir_info *entry, *odi;
+
+ odi = kmalloc(sizeof(*odi), GFP_NOFS);
+ if (!odi)
+ return ERR_PTR(-ENOMEM);
+ odi->ino = dir_ino;
+ odi->gen = 0;
+
+ while (*p) {
+ parent = *p;
+ entry = rb_entry(parent, struct orphan_dir_info, node);
+ if (dir_ino < entry->ino) {
+ p = &(*p)->rb_left;
+ } else if (dir_ino > entry->ino) {
+ p = &(*p)->rb_right;
+ } else {
+ kfree(odi);
+ return entry;
+ }
+ }
+
+ rb_link_node(&odi->node, parent, p);
+ rb_insert_color(&odi->node, &sctx->orphan_dirs);
+ return odi;
+}
+
+static struct orphan_dir_info *
+get_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
+{
+ struct rb_node *n = sctx->orphan_dirs.rb_node;
+ struct orphan_dir_info *entry;
+
+ while (n) {
+ entry = rb_entry(n, struct orphan_dir_info, node);
+ if (dir_ino < entry->ino)
+ n = n->rb_left;
+ else if (dir_ino > entry->ino)
+ n = n->rb_right;
+ else
+ return entry;
+ }
+ return NULL;
+}
+
+static int is_waiting_for_rm(struct send_ctx *sctx, u64 dir_ino)
+{
+ struct orphan_dir_info *odi = get_orphan_dir_info(sctx, dir_ino);
+
+ return odi != NULL;
+}
+
+static void free_orphan_dir_info(struct send_ctx *sctx,
+ struct orphan_dir_info *odi)
+{
+ if (!odi)
+ return;
+ rb_erase(&odi->node, &sctx->orphan_dirs);
+ kfree(odi);
+}
+
/*
* Returns 1 if a directory can be removed at this point in time.
* We check this by iterating all dir items and checking if the inode behind
* the dir item was already processed.
*/
-static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 send_progress)
+static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen,
+ u64 send_progress)
{
int ret = 0;
struct btrfs_root *root = sctx->parent_root;
@@ -2704,31 +2797,52 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 send_progress)
key.objectid = dir;
key.type = BTRFS_DIR_INDEX_KEY;
key.offset = 0;
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
while (1) {
- ret = btrfs_search_slot_for_read(root, &key, path, 1, 0);
- if (ret < 0)
- goto out;
- if (!ret) {
- btrfs_item_key_to_cpu(path->nodes[0], &found_key,
- path->slots[0]);
+ struct waiting_dir_move *dm;
+
+ if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0)
+ goto out;
+ else if (ret > 0)
+ break;
+ continue;
}
- if (ret || found_key.objectid != key.objectid ||
- found_key.type != key.type) {
+ btrfs_item_key_to_cpu(path->nodes[0], &found_key,
+ path->slots[0]);
+ if (found_key.objectid != key.objectid ||
+ found_key.type != key.type)
break;
- }
di = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_dir_item);
btrfs_dir_item_key_to_cpu(path->nodes[0], di, &loc);
+ dm = get_waiting_dir_move(sctx, loc.objectid);
+ if (dm) {
+ struct orphan_dir_info *odi;
+
+ odi = add_orphan_dir_info(sctx, dir);
+ if (IS_ERR(odi)) {
+ ret = PTR_ERR(odi);
+ goto out;
+ }
+ odi->gen = dir_gen;
+ dm->rmdir_ino = dir;
+ ret = 0;
+ goto out;
+ }
+
if (loc.objectid > send_progress) {
ret = 0;
goto out;
}
- btrfs_release_path(path);
- key.offset = found_key.offset + 1;
+ path->slots[0]++;
}
ret = 1;
@@ -2740,19 +2854,9 @@ out:
static int is_waiting_for_move(struct send_ctx *sctx, u64 ino)
{
- struct rb_node *n = sctx->waiting_dir_moves.rb_node;
- struct waiting_dir_move *entry;
+ struct waiting_dir_move *entry = get_waiting_dir_move(sctx, ino);
- while (n) {
- entry = rb_entry(n, struct waiting_dir_move, node);
- if (ino < entry->ino)
- n = n->rb_left;
- else if (ino > entry->ino)
- n = n->rb_right;
- else
- return 1;
- }
- return 0;
+ return entry != NULL;
}
static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino)
@@ -2765,6 +2869,7 @@ static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino)
if (!dm)
return -ENOMEM;
dm->ino = ino;
+ dm->rmdir_ino = 0;
while (*p) {
parent = *p;
@@ -2784,31 +2889,41 @@ static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino)
return 0;
}
-static int del_waiting_dir_move(struct send_ctx *sctx, u64 ino)
+static struct waiting_dir_move *
+get_waiting_dir_move(struct send_ctx *sctx, u64 ino)
{
struct rb_node *n = sctx->waiting_dir_moves.rb_node;
struct waiting_dir_move *entry;
while (n) {
entry = rb_entry(n, struct waiting_dir_move, node);
- if (ino < entry->ino) {
+ if (ino < entry->ino)
n = n->rb_left;
- } else if (ino > entry->ino) {
+ else if (ino > entry->ino)
n = n->rb_right;
- } else {
- rb_erase(&entry->node, &sctx->waiting_dir_moves);
- kfree(entry);
- return 0;
- }
+ else
+ return entry;
}
- return -ENOENT;
+ return NULL;
+}
+
+static void free_waiting_dir_move(struct send_ctx *sctx,
+ struct waiting_dir_move *dm)
+{
+ if (!dm)
+ return;
+ rb_erase(&dm->node, &sctx->waiting_dir_moves);
+ kfree(dm);
}
-static int add_pending_dir_move(struct send_ctx *sctx, u64 parent_ino)
+static int add_pending_dir_move(struct send_ctx *sctx,
+ u64 ino,
+ u64 ino_gen,
+ u64 parent_ino)
{
struct rb_node **p = &sctx->pending_dir_moves.rb_node;
struct rb_node *parent = NULL;
- struct pending_dir_move *entry, *pm;
+ struct pending_dir_move *entry = NULL, *pm;
struct recorded_ref *cur;
int exists = 0;
int ret;
@@ -2817,8 +2932,8 @@ static int add_pending_dir_move(struct send_ctx *sctx, u64 parent_ino)
if (!pm)
return -ENOMEM;
pm->parent_ino = parent_ino;
- pm->ino = sctx->cur_ino;
- pm->gen = sctx->cur_inode_gen;
+ pm->ino = ino;
+ pm->gen = ino_gen;
INIT_LIST_HEAD(&pm->list);
INIT_LIST_HEAD(&pm->update_refs);
RB_CLEAR_NODE(&pm->node);
@@ -2888,19 +3003,52 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
{
struct fs_path *from_path = NULL;
struct fs_path *to_path = NULL;
+ struct fs_path *name = NULL;
u64 orig_progress = sctx->send_progress;
struct recorded_ref *cur;
+ u64 parent_ino, parent_gen;
+ struct waiting_dir_move *dm = NULL;
+ u64 rmdir_ino = 0;
int ret;
+ name = fs_path_alloc();
from_path = fs_path_alloc();
- if (!from_path)
- return -ENOMEM;
+ if (!name || !from_path) {
+ ret = -ENOMEM;
+ goto out;
+ }
- sctx->send_progress = pm->ino;
- ret = get_cur_path(sctx, pm->ino, pm->gen, from_path);
+ dm = get_waiting_dir_move(sctx, pm->ino);
+ ASSERT(dm);
+ rmdir_ino = dm->rmdir_ino;
+ free_waiting_dir_move(sctx, dm);
+
+ ret = get_first_ref(sctx->parent_root, pm->ino,
+ &parent_ino, &parent_gen, name);
if (ret < 0)
goto out;
+ if (parent_ino == sctx->cur_ino) {
+ /* child only renamed, not moved */
+ ASSERT(parent_gen == sctx->cur_inode_gen);
+ ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen,
+ from_path);
+ if (ret < 0)
+ goto out;
+ ret = fs_path_add_path(from_path, name);
+ if (ret < 0)
+ goto out;
+ } else {
+ /* child moved and maybe renamed too */
+ sctx->send_progress = pm->ino;
+ ret = get_cur_path(sctx, pm->ino, pm->gen, from_path);
+ if (ret < 0)
+ goto out;
+ }
+
+ fs_path_free(name);
+ name = NULL;
+
to_path = fs_path_alloc();
if (!to_path) {
ret = -ENOMEM;
@@ -2908,9 +3056,6 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
}
sctx->send_progress = sctx->cur_ino + 1;
- ret = del_waiting_dir_move(sctx, pm->ino);
- ASSERT(ret == 0);
-
ret = get_cur_path(sctx, pm->ino, pm->gen, to_path);
if (ret < 0)
goto out;
@@ -2919,6 +3064,35 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
if (ret < 0)
goto out;
+ if (rmdir_ino) {
+ struct orphan_dir_info *odi;
+
+ odi = get_orphan_dir_info(sctx, rmdir_ino);
+ if (!odi) {
+ /* already deleted */
+ goto finish;
+ }
+ ret = can_rmdir(sctx, rmdir_ino, odi->gen, sctx->cur_ino + 1);
+ if (ret < 0)
+ goto out;
+ if (!ret)
+ goto finish;
+
+ name = fs_path_alloc();
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = get_cur_path(sctx, rmdir_ino, odi->gen, name);
+ if (ret < 0)
+ goto out;
+ ret = send_rmdir(sctx, name);
+ if (ret < 0)
+ goto out;
+ free_orphan_dir_info(sctx, odi);
+ }
+
+finish:
ret = send_utimes(sctx, pm->ino, pm->gen);
if (ret < 0)
goto out;
@@ -2928,12 +3102,15 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
* and old parent(s).
*/
list_for_each_entry(cur, &pm->update_refs, list) {
+ if (cur->dir == rmdir_ino)
+ continue;
ret = send_utimes(sctx, cur->dir, cur->dir_gen);
if (ret < 0)
goto out;
}
out:
+ fs_path_free(name);
fs_path_free(from_path);
fs_path_free(to_path);
sctx->send_progress = orig_progress;
@@ -3005,17 +3182,19 @@ static int wait_for_parent_move(struct send_ctx *sctx,
int ret;
u64 ino = parent_ref->dir;
u64 parent_ino_before, parent_ino_after;
- u64 new_gen, old_gen;
+ u64 old_gen;
struct fs_path *path_before = NULL;
struct fs_path *path_after = NULL;
int len1, len2;
-
- if (parent_ref->dir <= sctx->cur_ino)
- return 0;
+ int register_upper_dirs;
+ u64 gen;
if (is_waiting_for_move(sctx, ino))
return 1;
+ if (parent_ref->dir <= sctx->cur_ino)
+ return 0;
+
ret = get_inode_info(sctx->parent_root, ino, NULL, &old_gen,
NULL, NULL, NULL, NULL);
if (ret == -ENOENT)
@@ -3023,12 +3202,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
else if (ret < 0)
return ret;
- ret = get_inode_info(sctx->send_root, ino, NULL, &new_gen,
- NULL, NULL, NULL, NULL);
- if (ret < 0)
- return ret;
-
- if (new_gen != old_gen)
+ if (parent_ref->dir_gen != old_gen)
return 0;
path_before = fs_path_alloc();
@@ -3051,7 +3225,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
}
ret = get_first_ref(sctx->send_root, ino, &parent_ino_after,
- NULL, path_after);
+ &gen, path_after);
if (ret == -ENOENT) {
ret = 0;
goto out;
@@ -3061,13 +3235,67 @@ static int wait_for_parent_move(struct send_ctx *sctx,
len1 = fs_path_len(path_before);
len2 = fs_path_len(path_after);
- if ((parent_ino_before != parent_ino_after) && (len1 != len2 ||
- memcmp(path_before->start, path_after->start, len1))) {
+ if (parent_ino_before != parent_ino_after || len1 != len2 ||
+ memcmp(path_before->start, path_after->start, len1)) {
ret = 1;
goto out;
}
ret = 0;
+ /*
+ * Ok, our new most direct ancestor has a higher inode number but
+ * wasn't moved/renamed. So maybe some of the new ancestors higher in
+ * the hierarchy have an higher inode number too *and* were renamed
+ * or moved - in this case we need to wait for the ancestor's rename
+ * or move operation before we can do the move/rename for the current
+ * inode.
+ */
+ register_upper_dirs = 0;
+ ino = parent_ino_after;
+again:
+ while ((ret == 0 || register_upper_dirs) && ino > sctx->cur_ino) {
+ u64 parent_gen;
+
+ fs_path_reset(path_before);
+ fs_path_reset(path_after);
+
+ ret = get_first_ref(sctx->send_root, ino, &parent_ino_after,
+ &parent_gen, path_after);
+ if (ret < 0)
+ goto out;
+ ret = get_first_ref(sctx->parent_root, ino, &parent_ino_before,
+ NULL, path_before);
+ if (ret == -ENOENT) {
+ ret = 0;
+ break;
+ } else if (ret < 0) {
+ goto out;
+ }
+
+ len1 = fs_path_len(path_before);
+ len2 = fs_path_len(path_after);
+ if (parent_ino_before != parent_ino_after || len1 != len2 ||
+ memcmp(path_before->start, path_after->start, len1)) {
+ ret = 1;
+ if (register_upper_dirs) {
+ break;
+ } else {
+ register_upper_dirs = 1;
+ ino = parent_ref->dir;
+ gen = parent_ref->dir_gen;
+ goto again;
+ }
+ } else if (register_upper_dirs) {
+ ret = add_pending_dir_move(sctx, ino, gen,
+ parent_ino_after);
+ if (ret < 0 && ret != -EEXIST)
+ goto out;
+ }
+
+ ino = parent_ino_after;
+ gen = parent_gen;
+ }
+
out:
fs_path_free(path_before);
fs_path_free(path_after);
@@ -3089,6 +3317,7 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
u64 ow_gen;
int did_overwrite = 0;
int is_orphan = 0;
+ u64 last_dir_ino_rm = 0;
verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
@@ -3227,9 +3456,14 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
* dirs, we always have one new and one deleted
* ref. The deleted ref is ignored later.
*/
- if (wait_for_parent_move(sctx, cur)) {
+ ret = wait_for_parent_move(sctx, cur);
+ if (ret < 0)
+ goto out;
+ if (ret) {
ret = add_pending_dir_move(sctx,
- cur->dir);
+ sctx->cur_ino,
+ sctx->cur_inode_gen,
+ cur->dir);
*pending_move = 1;
} else {
ret = send_rename(sctx, valid_path,
@@ -3259,7 +3493,8 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
* later, we do this check again and rmdir it then if possible.
* See the use of check_dirs for more details.
*/
- ret = can_rmdir(sctx, sctx->cur_ino, sctx->cur_ino);
+ ret = can_rmdir(sctx, sctx->cur_ino, sctx->cur_inode_gen,
+ sctx->cur_ino);
if (ret < 0)
goto out;
if (ret) {
@@ -3350,8 +3585,10 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
ret = send_utimes(sctx, cur->dir, cur->dir_gen);
if (ret < 0)
goto out;
- } else if (ret == inode_state_did_delete) {
- ret = can_rmdir(sctx, cur->dir, sctx->cur_ino);
+ } else if (ret == inode_state_did_delete &&
+ cur->dir != last_dir_ino_rm) {
+ ret = can_rmdir(sctx, cur->dir, cur->dir_gen,
+ sctx->cur_ino);
if (ret < 0)
goto out;
if (ret) {
@@ -3362,6 +3599,7 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
ret = send_rmdir(sctx, valid_path);
if (ret < 0)
goto out;
+ last_dir_ino_rm = cur->dir;
}
}
}
@@ -3375,9 +3613,8 @@ out:
return ret;
}
-static int __record_new_ref(int num, u64 dir, int index,
- struct fs_path *name,
- void *ctx)
+static int record_ref(struct btrfs_root *root, int num, u64 dir, int index,
+ struct fs_path *name, void *ctx, struct list_head *refs)
{
int ret = 0;
struct send_ctx *sctx = ctx;
@@ -3388,7 +3625,7 @@ static int __record_new_ref(int num, u64 dir, int index,
if (!p)
return -ENOMEM;
- ret = get_inode_info(sctx->send_root, dir, NULL, &gen, NULL, NULL,
+ ret = get_inode_info(root, dir, NULL, &gen, NULL, NULL,
NULL, NULL);
if (ret < 0)
goto out;
@@ -3400,7 +3637,7 @@ static int __record_new_ref(int num, u64 dir, int index,
if (ret < 0)
goto out;
- ret = record_ref(&sctx->new_refs, dir, gen, p);
+ ret = __record_ref(refs, dir, gen, p);
out:
if (ret)
@@ -3408,37 +3645,23 @@ out:
return ret;
}
+static int __record_new_ref(int num, u64 dir, int index,
+ struct fs_path *name,
+ void *ctx)
+{
+ struct send_ctx *sctx = ctx;
+ return record_ref(sctx->send_root, num, dir, index, name,
+ ctx, &sctx->new_refs);
+}
+
+
static int __record_deleted_ref(int num, u64 dir, int index,
struct fs_path *name,
void *ctx)
{
- int ret = 0;
struct send_ctx *sctx = ctx;
- struct fs_path *p;
- u64 gen;
-
- p = fs_path_alloc();
- if (!p)
- return -ENOMEM;
-
- ret = get_inode_info(sctx->parent_root, dir, NULL, &gen, NULL, NULL,
- NULL, NULL);
- if (ret < 0)
- goto out;
-
- ret = get_cur_path(sctx, dir, gen, p);
- if (ret < 0)
- goto out;
- ret = fs_path_add_path(p, name);
- if (ret < 0)
- goto out;
-
- ret = record_ref(&sctx->deleted_refs, dir, gen, p);
-
-out:
- if (ret)
- fs_path_free(p);
- return ret;
+ return record_ref(sctx->parent_root, num, dir, index, name,
+ ctx, &sctx->deleted_refs);
}
static int record_new_ref(struct send_ctx *sctx)
@@ -3619,21 +3842,31 @@ static int process_all_refs(struct send_ctx *sctx,
root = sctx->parent_root;
cb = __record_deleted_ref;
} else {
- BUG();
+ btrfs_err(sctx->send_root->fs_info,
+ "Wrong command %d in process_all_refs", cmd);
+ ret = -EINVAL;
+ goto out;
}
key.objectid = sctx->cmp_key->objectid;
key.type = BTRFS_INODE_REF_KEY;
key.offset = 0;
- while (1) {
- ret = btrfs_search_slot_for_read(root, &key, path, 1, 0);
- if (ret < 0)
- goto out;
- if (ret)
- break;
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+ while (1) {
eb = path->nodes[0];
slot = path->slots[0];
+ if (slot >= btrfs_header_nritems(eb)) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0)
+ goto out;
+ else if (ret > 0)
+ break;
+ continue;
+ }
+
btrfs_item_key_to_cpu(eb, &found_key, slot);
if (found_key.objectid != key.objectid ||
@@ -3642,11 +3875,10 @@ static int process_all_refs(struct send_ctx *sctx,
break;
ret = iterate_inode_ref(root, path, &found_key, 0, cb, sctx);
- btrfs_release_path(path);
if (ret < 0)
goto out;
- key.offset = found_key.offset + 1;
+ path->slots[0]++;
}
btrfs_release_path(path);
@@ -3927,19 +4159,25 @@ static int process_all_new_xattrs(struct send_ctx *sctx)
key.objectid = sctx->cmp_key->objectid;
key.type = BTRFS_XATTR_ITEM_KEY;
key.offset = 0;
- while (1) {
- ret = btrfs_search_slot_for_read(root, &key, path, 1, 0);
- if (ret < 0)
- goto out;
- if (ret) {
- ret = 0;
- goto out;
- }
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+ while (1) {
eb = path->nodes[0];
slot = path->slots[0];
- btrfs_item_key_to_cpu(eb, &found_key, slot);
+ if (slot >= btrfs_header_nritems(eb)) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0) {
+ goto out;
+ } else if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ continue;
+ }
+ btrfs_item_key_to_cpu(eb, &found_key, slot);
if (found_key.objectid != key.objectid ||
found_key.type != key.type) {
ret = 0;
@@ -3951,8 +4189,7 @@ static int process_all_new_xattrs(struct send_ctx *sctx)
if (ret < 0)
goto out;
- btrfs_release_path(path);
- key.offset = found_key.offset + 1;
+ path->slots[0]++;
}
out:
@@ -3991,6 +4228,13 @@ static ssize_t fill_read_buf(struct send_ctx *sctx, u64 offset, u32 len)
goto out;
last_index = (offset + len - 1) >> PAGE_CACHE_SHIFT;
+
+ /* initial readahead */
+ memset(&sctx->ra, 0, sizeof(struct file_ra_state));
+ file_ra_state_init(&sctx->ra, inode->i_mapping);
+ btrfs_force_ra(inode->i_mapping, &sctx->ra, NULL, index,
+ last_index - index + 1);
+
while (index <= last_index) {
unsigned cur_len = min_t(unsigned, len,
PAGE_CACHE_SIZE - pg_offset);
@@ -4763,18 +5007,19 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
ret = apply_children_dir_moves(sctx);
if (ret)
goto out;
+ /*
+ * Need to send that every time, no matter if it actually
+ * changed between the two trees as we have done changes to
+ * the inode before. If our inode is a directory and it's
+ * waiting to be moved/renamed, we will send its utimes when
+ * it's moved/renamed, therefore we don't need to do it here.
+ */
+ sctx->send_progress = sctx->cur_ino + 1;
+ ret = send_utimes(sctx, sctx->cur_ino, sctx->cur_inode_gen);
+ if (ret < 0)
+ goto out;
}
- /*
- * Need to send that every time, no matter if it actually
- * changed between the two trees as we have done changes to
- * the inode before.
- */
- sctx->send_progress = sctx->cur_ino + 1;
- ret = send_utimes(sctx, sctx->cur_ino, sctx->cur_inode_gen);
- if (ret < 0)
- goto out;
-
out:
return ret;
}
@@ -4840,6 +5085,8 @@ static int changed_inode(struct send_ctx *sctx,
sctx->left_path->nodes[0], left_ii);
sctx->cur_inode_mode = btrfs_inode_mode(
sctx->left_path->nodes[0], left_ii);
+ sctx->cur_inode_rdev = btrfs_inode_rdev(
+ sctx->left_path->nodes[0], left_ii);
if (sctx->cur_ino != BTRFS_FIRST_FREE_OBJECTID)
ret = send_create_inode_if_needed(sctx);
} else if (result == BTRFS_COMPARE_TREE_DELETED) {
@@ -4884,6 +5131,8 @@ static int changed_inode(struct send_ctx *sctx,
sctx->left_path->nodes[0], left_ii);
sctx->cur_inode_mode = btrfs_inode_mode(
sctx->left_path->nodes[0], left_ii);
+ sctx->cur_inode_rdev = btrfs_inode_rdev(
+ sctx->left_path->nodes[0], left_ii);
ret = send_create_inode_if_needed(sctx);
if (ret < 0)
goto out;
@@ -5118,6 +5367,7 @@ out:
static int full_send_tree(struct send_ctx *sctx)
{
int ret;
+ struct btrfs_trans_handle *trans = NULL;
struct btrfs_root *send_root = sctx->send_root;
struct btrfs_key key;
struct btrfs_key found_key;
@@ -5139,6 +5389,19 @@ static int full_send_tree(struct send_ctx *sctx)
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
+join_trans:
+ /*
+ * We need to make sure the transaction does not get committed
+ * while we do anything on commit roots. Join a transaction to prevent
+ * this.
+ */
+ trans = btrfs_join_transaction(send_root);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ trans = NULL;
+ goto out;
+ }
+
/*
* Make sure the tree has not changed after re-joining. We detect this
* by comparing start_ctransid and ctransid. They should always match.
@@ -5162,6 +5425,19 @@ static int full_send_tree(struct send_ctx *sctx)
goto out_finish;
while (1) {
+ /*
+ * When someone want to commit while we iterate, end the
+ * joined transaction and rejoin.
+ */
+ if (btrfs_should_end_transaction(trans, send_root)) {
+ ret = btrfs_end_transaction(trans, send_root);
+ trans = NULL;
+ if (ret < 0)
+ goto out;
+ btrfs_release_path(path);
+ goto join_trans;
+ }
+
eb = path->nodes[0];
slot = path->slots[0];
btrfs_item_key_to_cpu(eb, &found_key, slot);
@@ -5189,6 +5465,12 @@ out_finish:
out:
btrfs_free_path(path);
+ if (trans) {
+ if (!ret)
+ ret = btrfs_end_transaction(trans, send_root);
+ else
+ btrfs_end_transaction(trans, send_root);
+ }
return ret;
}
@@ -5340,6 +5622,7 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
sctx->pending_dir_moves = RB_ROOT;
sctx->waiting_dir_moves = RB_ROOT;
+ sctx->orphan_dirs = RB_ROOT;
sctx->clone_roots = vzalloc(sizeof(struct clone_root) *
(arg->clone_sources_count + 1));
@@ -5477,6 +5760,16 @@ out:
kfree(dm);
}
+ WARN_ON(sctx && !ret && !RB_EMPTY_ROOT(&sctx->orphan_dirs));
+ while (sctx && !RB_EMPTY_ROOT(&sctx->orphan_dirs)) {
+ struct rb_node *n;
+ struct orphan_dir_info *odi;
+
+ n = rb_first(&sctx->orphan_dirs);
+ odi = rb_entry(n, struct orphan_dir_info, node);
+ free_orphan_dir_info(sctx, odi);
+ }
+
if (sort_clone_roots) {
for (i = 0; i < sctx->clone_roots_cnt; i++)
btrfs_root_dec_send_in_progress(
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index d04db817be5c..9dbf42395153 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1305,13 +1305,6 @@ error_fs_info:
return ERR_PTR(error);
}
-static void btrfs_set_max_workers(struct btrfs_workers *workers, int new_limit)
-{
- spin_lock_irq(&workers->lock);
- workers->max_workers = new_limit;
- spin_unlock_irq(&workers->lock);
-}
-
static void btrfs_resize_thread_pool(struct btrfs_fs_info *fs_info,
int new_pool_size, int old_pool_size)
{
@@ -1323,21 +1316,20 @@ static void btrfs_resize_thread_pool(struct btrfs_fs_info *fs_info,
btrfs_info(fs_info, "resize thread pool %d -> %d",
old_pool_size, new_pool_size);
- btrfs_set_max_workers(&fs_info->generic_worker, new_pool_size);
- btrfs_set_max_workers(&fs_info->workers, new_pool_size);
- btrfs_set_max_workers(&fs_info->delalloc_workers, new_pool_size);
- btrfs_set_max_workers(&fs_info->submit_workers, new_pool_size);
- btrfs_set_max_workers(&fs_info->caching_workers, new_pool_size);
- btrfs_set_max_workers(&fs_info->fixup_workers, new_pool_size);
- btrfs_set_max_workers(&fs_info->endio_workers, new_pool_size);
- btrfs_set_max_workers(&fs_info->endio_meta_workers, new_pool_size);
- btrfs_set_max_workers(&fs_info->endio_meta_write_workers, new_pool_size);
- btrfs_set_max_workers(&fs_info->endio_write_workers, new_pool_size);
- btrfs_set_max_workers(&fs_info->endio_freespace_worker, new_pool_size);
- btrfs_set_max_workers(&fs_info->delayed_workers, new_pool_size);
- btrfs_set_max_workers(&fs_info->readahead_workers, new_pool_size);
- btrfs_set_max_workers(&fs_info->scrub_wr_completion_workers,
- new_pool_size);
+ btrfs_workqueue_set_max(fs_info->workers, new_pool_size);
+ btrfs_workqueue_set_max(fs_info->delalloc_workers, new_pool_size);
+ btrfs_workqueue_set_max(fs_info->submit_workers, new_pool_size);
+ btrfs_workqueue_set_max(fs_info->caching_workers, new_pool_size);
+ btrfs_workqueue_set_max(fs_info->endio_workers, new_pool_size);
+ btrfs_workqueue_set_max(fs_info->endio_meta_workers, new_pool_size);
+ btrfs_workqueue_set_max(fs_info->endio_meta_write_workers,
+ new_pool_size);
+ btrfs_workqueue_set_max(fs_info->endio_write_workers, new_pool_size);
+ btrfs_workqueue_set_max(fs_info->endio_freespace_worker, new_pool_size);
+ btrfs_workqueue_set_max(fs_info->delayed_workers, new_pool_size);
+ btrfs_workqueue_set_max(fs_info->readahead_workers, new_pool_size);
+ btrfs_workqueue_set_max(fs_info->scrub_wr_completion_workers,
+ new_pool_size);
}
static inline void btrfs_remount_prepare(struct btrfs_fs_info *fs_info)
@@ -1388,6 +1380,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
unsigned int old_metadata_ratio = fs_info->metadata_ratio;
int ret;
+ sync_filesystem(sb);
btrfs_remount_prepare(fs_info);
ret = btrfs_parse_options(root, data);
@@ -1479,6 +1472,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
sb->s_flags &= ~MS_RDONLY;
}
out:
+ wake_up_process(fs_info->transaction_kthread);
btrfs_remount_cleanup(fs_info, old_opts);
return 0;
diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c
index 865f4cf9a769..c5eb2143dc66 100644
--- a/fs/btrfs/sysfs.c
+++ b/fs/btrfs/sysfs.c
@@ -24,6 +24,7 @@
#include <linux/kobject.h>
#include <linux/bug.h>
#include <linux/genhd.h>
+#include <linux/debugfs.h>
#include "ctree.h"
#include "disk-io.h"
@@ -599,6 +600,12 @@ static int add_device_membership(struct btrfs_fs_info *fs_info)
/* /sys/fs/btrfs/ entry */
static struct kset *btrfs_kset;
+/* /sys/kernel/debug/btrfs */
+static struct dentry *btrfs_debugfs_root_dentry;
+
+/* Debugging tunables and exported data */
+u64 btrfs_debugfs_test;
+
int btrfs_sysfs_add_one(struct btrfs_fs_info *fs_info)
{
int error;
@@ -642,27 +649,41 @@ failure:
return error;
}
+static int btrfs_init_debugfs(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ btrfs_debugfs_root_dentry = debugfs_create_dir("btrfs", NULL);
+ if (!btrfs_debugfs_root_dentry)
+ return -ENOMEM;
+
+ debugfs_create_u64("test", S_IRUGO | S_IWUGO, btrfs_debugfs_root_dentry,
+ &btrfs_debugfs_test);
+#endif
+ return 0;
+}
+
int btrfs_init_sysfs(void)
{
int ret;
+
btrfs_kset = kset_create_and_add("btrfs", NULL, fs_kobj);
if (!btrfs_kset)
return -ENOMEM;
- init_feature_attrs();
+ ret = btrfs_init_debugfs();
+ if (ret)
+ return ret;
+ init_feature_attrs();
ret = sysfs_create_group(&btrfs_kset->kobj, &btrfs_feature_attr_group);
- if (ret) {
- kset_unregister(btrfs_kset);
- return ret;
- }
- return 0;
+ return ret;
}
void btrfs_exit_sysfs(void)
{
sysfs_remove_group(&btrfs_kset->kobj, &btrfs_feature_attr_group);
kset_unregister(btrfs_kset);
+ debugfs_remove_recursive(btrfs_debugfs_root_dentry);
}
diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h
index f3cea3710d44..9ab576318a84 100644
--- a/fs/btrfs/sysfs.h
+++ b/fs/btrfs/sysfs.h
@@ -1,6 +1,11 @@
#ifndef _BTRFS_SYSFS_H_
#define _BTRFS_SYSFS_H_
+/*
+ * Data exported through sysfs
+ */
+extern u64 btrfs_debugfs_test;
+
enum btrfs_feature_set {
FEAT_COMPAT,
FEAT_COMPAT_RO,
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 34cd83184c4a..a04707f740d6 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -683,7 +683,8 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
int lock = (trans->type != TRANS_JOIN_NOLOCK);
int err = 0;
- if (--trans->use_count) {
+ if (trans->use_count > 1) {
+ trans->use_count--;
trans->block_rsv = trans->orig_rsv;
return 0;
}
@@ -731,17 +732,10 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
}
if (lock && ACCESS_ONCE(cur_trans->state) == TRANS_STATE_BLOCKED) {
- if (throttle) {
- /*
- * We may race with somebody else here so end up having
- * to call end_transaction on ourselves again, so inc
- * our use_count.
- */
- trans->use_count++;
+ if (throttle)
return btrfs_commit_transaction(trans, root);
- } else {
+ else
wake_up_process(info->transaction_kthread);
- }
}
if (trans->type & __TRANS_FREEZABLE)
@@ -1578,10 +1572,9 @@ static void cleanup_transaction(struct btrfs_trans_handle *trans,
trace_btrfs_transaction_commit(root);
- btrfs_scrub_continue(root);
-
if (current->journal_info == trans)
current->journal_info = NULL;
+ btrfs_scrub_cancel(root->fs_info);
kmem_cache_free(btrfs_trans_handle_cachep, trans);
}
@@ -1621,7 +1614,7 @@ static int btrfs_flush_all_pending_stuffs(struct btrfs_trans_handle *trans,
static inline int btrfs_start_delalloc_flush(struct btrfs_fs_info *fs_info)
{
if (btrfs_test_opt(fs_info->tree_root, FLUSHONCOMMIT))
- return btrfs_start_delalloc_roots(fs_info, 1);
+ return btrfs_start_delalloc_roots(fs_info, 1, -1);
return 0;
}
@@ -1754,7 +1747,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
/* ->aborted might be set after the previous check, so check it */
if (unlikely(ACCESS_ONCE(cur_trans->aborted))) {
ret = cur_trans->aborted;
- goto cleanup_transaction;
+ goto scrub_continue;
}
/*
* the reloc mutex makes sure that we stop
@@ -1771,7 +1764,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
ret = create_pending_snapshots(trans, root->fs_info);
if (ret) {
mutex_unlock(&root->fs_info->reloc_mutex);
- goto cleanup_transaction;
+ goto scrub_continue;
}
/*
@@ -1787,13 +1780,13 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
ret = btrfs_run_delayed_items(trans, root);
if (ret) {
mutex_unlock(&root->fs_info->reloc_mutex);
- goto cleanup_transaction;
+ goto scrub_continue;
}
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
if (ret) {
mutex_unlock(&root->fs_info->reloc_mutex);
- goto cleanup_transaction;
+ goto scrub_continue;
}
/*
@@ -1823,7 +1816,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
if (ret) {
mutex_unlock(&root->fs_info->tree_log_mutex);
mutex_unlock(&root->fs_info->reloc_mutex);
- goto cleanup_transaction;
+ goto scrub_continue;
}
/*
@@ -1844,7 +1837,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
if (ret) {
mutex_unlock(&root->fs_info->tree_log_mutex);
mutex_unlock(&root->fs_info->reloc_mutex);
- goto cleanup_transaction;
+ goto scrub_continue;
}
/*
@@ -1855,7 +1848,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
ret = cur_trans->aborted;
mutex_unlock(&root->fs_info->tree_log_mutex);
mutex_unlock(&root->fs_info->reloc_mutex);
- goto cleanup_transaction;
+ goto scrub_continue;
}
btrfs_prepare_extent_commit(trans, root);
@@ -1891,13 +1884,13 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
btrfs_error(root->fs_info, ret,
"Error while writing out transaction");
mutex_unlock(&root->fs_info->tree_log_mutex);
- goto cleanup_transaction;
+ goto scrub_continue;
}
ret = write_ctree_super(trans, root, 0);
if (ret) {
mutex_unlock(&root->fs_info->tree_log_mutex);
- goto cleanup_transaction;
+ goto scrub_continue;
}
/*
@@ -1940,6 +1933,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
return ret;
+scrub_continue:
+ btrfs_scrub_continue(root);
cleanup_transaction:
btrfs_trans_release_metadata(trans, root);
trans->block_rsv = NULL;
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 39d83da03e03..e2f45fc02610 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -136,13 +136,20 @@ static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans,
* syncing the tree wait for us to finish
*/
static int start_log_trans(struct btrfs_trans_handle *trans,
- struct btrfs_root *root)
+ struct btrfs_root *root,
+ struct btrfs_log_ctx *ctx)
{
+ int index;
int ret;
- int err = 0;
mutex_lock(&root->log_mutex);
if (root->log_root) {
+ if (ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) ==
+ trans->transid) {
+ ret = -EAGAIN;
+ goto out;
+ }
+
if (!root->log_start_pid) {
root->log_start_pid = current->pid;
root->log_multiple_pids = false;
@@ -152,27 +159,40 @@ static int start_log_trans(struct btrfs_trans_handle *trans,
atomic_inc(&root->log_batch);
atomic_inc(&root->log_writers);
+ if (ctx) {
+ index = root->log_transid % 2;
+ list_add_tail(&ctx->list, &root->log_ctxs[index]);
+ ctx->log_transid = root->log_transid;
+ }
mutex_unlock(&root->log_mutex);
return 0;
}
- root->log_multiple_pids = false;
- root->log_start_pid = current->pid;
+
+ ret = 0;
mutex_lock(&root->fs_info->tree_log_mutex);
- if (!root->fs_info->log_root_tree) {
+ if (!root->fs_info->log_root_tree)
ret = btrfs_init_log_root_tree(trans, root->fs_info);
- if (ret)
- err = ret;
- }
- if (err == 0 && !root->log_root) {
+ mutex_unlock(&root->fs_info->tree_log_mutex);
+ if (ret)
+ goto out;
+
+ if (!root->log_root) {
ret = btrfs_add_log_tree(trans, root);
if (ret)
- err = ret;
+ goto out;
}
- mutex_unlock(&root->fs_info->tree_log_mutex);
+ root->log_multiple_pids = false;
+ root->log_start_pid = current->pid;
atomic_inc(&root->log_batch);
atomic_inc(&root->log_writers);
+ if (ctx) {
+ index = root->log_transid % 2;
+ list_add_tail(&ctx->list, &root->log_ctxs[index]);
+ ctx->log_transid = root->log_transid;
+ }
+out:
mutex_unlock(&root->log_mutex);
- return err;
+ return ret;
}
/*
@@ -2359,8 +2379,8 @@ static int update_log_root(struct btrfs_trans_handle *trans,
return ret;
}
-static int wait_log_commit(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, unsigned long transid)
+static void wait_log_commit(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, int transid)
{
DEFINE_WAIT(wait);
int index = transid % 2;
@@ -2375,36 +2395,63 @@ static int wait_log_commit(struct btrfs_trans_handle *trans,
&wait, TASK_UNINTERRUPTIBLE);
mutex_unlock(&root->log_mutex);
- if (root->fs_info->last_trans_log_full_commit !=
- trans->transid && root->log_transid < transid + 2 &&
+ if (root->log_transid_committed < transid &&
atomic_read(&root->log_commit[index]))
schedule();
finish_wait(&root->log_commit_wait[index], &wait);
mutex_lock(&root->log_mutex);
- } while (root->fs_info->last_trans_log_full_commit !=
- trans->transid && root->log_transid < transid + 2 &&
+ } while (root->log_transid_committed < transid &&
atomic_read(&root->log_commit[index]));
- return 0;
}
static void wait_for_writer(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
DEFINE_WAIT(wait);
- while (root->fs_info->last_trans_log_full_commit !=
- trans->transid && atomic_read(&root->log_writers)) {
+
+ while (atomic_read(&root->log_writers)) {
prepare_to_wait(&root->log_writer_wait,
&wait, TASK_UNINTERRUPTIBLE);
mutex_unlock(&root->log_mutex);
- if (root->fs_info->last_trans_log_full_commit !=
- trans->transid && atomic_read(&root->log_writers))
+ if (atomic_read(&root->log_writers))
schedule();
mutex_lock(&root->log_mutex);
finish_wait(&root->log_writer_wait, &wait);
}
}
+static inline void btrfs_remove_log_ctx(struct btrfs_root *root,
+ struct btrfs_log_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+ mutex_lock(&root->log_mutex);
+ list_del_init(&ctx->list);
+ mutex_unlock(&root->log_mutex);
+}
+
+/*
+ * Invoked in log mutex context, or be sure there is no other task which
+ * can access the list.
+ */
+static inline void btrfs_remove_all_log_ctxs(struct btrfs_root *root,
+ int index, int error)
+{
+ struct btrfs_log_ctx *ctx;
+
+ if (!error) {
+ INIT_LIST_HEAD(&root->log_ctxs[index]);
+ return;
+ }
+
+ list_for_each_entry(ctx, &root->log_ctxs[index], list)
+ ctx->log_ret = error;
+
+ INIT_LIST_HEAD(&root->log_ctxs[index]);
+}
+
/*
* btrfs_sync_log does sends a given tree log down to the disk and
* updates the super blocks to record it. When this call is done,
@@ -2418,7 +2465,7 @@ static void wait_for_writer(struct btrfs_trans_handle *trans,
* that has happened.
*/
int btrfs_sync_log(struct btrfs_trans_handle *trans,
- struct btrfs_root *root)
+ struct btrfs_root *root, struct btrfs_log_ctx *ctx)
{
int index1;
int index2;
@@ -2426,22 +2473,30 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
int ret;
struct btrfs_root *log = root->log_root;
struct btrfs_root *log_root_tree = root->fs_info->log_root_tree;
- unsigned long log_transid = 0;
+ int log_transid = 0;
+ struct btrfs_log_ctx root_log_ctx;
struct blk_plug plug;
mutex_lock(&root->log_mutex);
- log_transid = root->log_transid;
- index1 = root->log_transid % 2;
+ log_transid = ctx->log_transid;
+ if (root->log_transid_committed >= log_transid) {
+ mutex_unlock(&root->log_mutex);
+ return ctx->log_ret;
+ }
+
+ index1 = log_transid % 2;
if (atomic_read(&root->log_commit[index1])) {
- wait_log_commit(trans, root, root->log_transid);
+ wait_log_commit(trans, root, log_transid);
mutex_unlock(&root->log_mutex);
- return 0;
+ return ctx->log_ret;
}
+ ASSERT(log_transid == root->log_transid);
atomic_set(&root->log_commit[index1], 1);
/* wait for previous tree log sync to complete */
if (atomic_read(&root->log_commit[(index1 + 1) % 2]))
- wait_log_commit(trans, root, root->log_transid - 1);
+ wait_log_commit(trans, root, log_transid - 1);
+
while (1) {
int batch = atomic_read(&root->log_batch);
/* when we're on an ssd, just kick the log commit out */
@@ -2456,7 +2511,8 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
}
/* bail out if we need to do a full commit */
- if (root->fs_info->last_trans_log_full_commit == trans->transid) {
+ if (ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) ==
+ trans->transid) {
ret = -EAGAIN;
btrfs_free_logged_extents(log, log_transid);
mutex_unlock(&root->log_mutex);
@@ -2477,6 +2533,8 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
blk_finish_plug(&plug);
btrfs_abort_transaction(trans, root, ret);
btrfs_free_logged_extents(log, log_transid);
+ ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) =
+ trans->transid;
mutex_unlock(&root->log_mutex);
goto out;
}
@@ -2486,7 +2544,6 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
root->log_transid++;
log->log_transid = root->log_transid;
root->log_start_pid = 0;
- smp_mb();
/*
* IO has been started, blocks of the log tree have WRITTEN flag set
* in their headers. new modifications of the log will be written to
@@ -2494,9 +2551,16 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
*/
mutex_unlock(&root->log_mutex);
+ btrfs_init_log_ctx(&root_log_ctx);
+
mutex_lock(&log_root_tree->log_mutex);
atomic_inc(&log_root_tree->log_batch);
atomic_inc(&log_root_tree->log_writers);
+
+ index2 = log_root_tree->log_transid % 2;
+ list_add_tail(&root_log_ctx.list, &log_root_tree->log_ctxs[index2]);
+ root_log_ctx.log_transid = log_root_tree->log_transid;
+
mutex_unlock(&log_root_tree->log_mutex);
ret = update_log_root(trans, log);
@@ -2509,13 +2573,17 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
}
if (ret) {
+ if (!list_empty(&root_log_ctx.list))
+ list_del_init(&root_log_ctx.list);
+
blk_finish_plug(&plug);
+ ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) =
+ trans->transid;
if (ret != -ENOSPC) {
btrfs_abort_transaction(trans, root, ret);
mutex_unlock(&log_root_tree->log_mutex);
goto out;
}
- root->fs_info->last_trans_log_full_commit = trans->transid;
btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
btrfs_free_logged_extents(log, log_transid);
mutex_unlock(&log_root_tree->log_mutex);
@@ -2523,22 +2591,29 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
goto out;
}
- index2 = log_root_tree->log_transid % 2;
+ if (log_root_tree->log_transid_committed >= root_log_ctx.log_transid) {
+ mutex_unlock(&log_root_tree->log_mutex);
+ ret = root_log_ctx.log_ret;
+ goto out;
+ }
+
+ index2 = root_log_ctx.log_transid % 2;
if (atomic_read(&log_root_tree->log_commit[index2])) {
blk_finish_plug(&plug);
btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
wait_log_commit(trans, log_root_tree,
- log_root_tree->log_transid);
+ root_log_ctx.log_transid);
btrfs_free_logged_extents(log, log_transid);
mutex_unlock(&log_root_tree->log_mutex);
- ret = 0;
+ ret = root_log_ctx.log_ret;
goto out;
}
+ ASSERT(root_log_ctx.log_transid == log_root_tree->log_transid);
atomic_set(&log_root_tree->log_commit[index2], 1);
if (atomic_read(&log_root_tree->log_commit[(index2 + 1) % 2])) {
wait_log_commit(trans, log_root_tree,
- log_root_tree->log_transid - 1);
+ root_log_ctx.log_transid - 1);
}
wait_for_writer(trans, log_root_tree);
@@ -2547,7 +2622,8 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
* now that we've moved on to the tree of log tree roots,
* check the full commit flag again
*/
- if (root->fs_info->last_trans_log_full_commit == trans->transid) {
+ if (ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) ==
+ trans->transid) {
blk_finish_plug(&plug);
btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
btrfs_free_logged_extents(log, log_transid);
@@ -2561,6 +2637,8 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
EXTENT_DIRTY | EXTENT_NEW);
blk_finish_plug(&plug);
if (ret) {
+ ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) =
+ trans->transid;
btrfs_abort_transaction(trans, root, ret);
btrfs_free_logged_extents(log, log_transid);
mutex_unlock(&log_root_tree->log_mutex);
@@ -2578,8 +2656,6 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
btrfs_header_level(log_root_tree->node));
log_root_tree->log_transid++;
- smp_mb();
-
mutex_unlock(&log_root_tree->log_mutex);
/*
@@ -2591,6 +2667,8 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
*/
ret = write_ctree_super(trans, root->fs_info->tree_root, 1);
if (ret) {
+ ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) =
+ trans->transid;
btrfs_abort_transaction(trans, root, ret);
goto out_wake_log_root;
}
@@ -2601,13 +2679,28 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
mutex_unlock(&root->log_mutex);
out_wake_log_root:
+ /*
+ * We needn't get log_mutex here because we are sure all
+ * the other tasks are blocked.
+ */
+ btrfs_remove_all_log_ctxs(log_root_tree, index2, ret);
+
+ mutex_lock(&log_root_tree->log_mutex);
+ log_root_tree->log_transid_committed++;
atomic_set(&log_root_tree->log_commit[index2], 0);
- smp_mb();
+ mutex_unlock(&log_root_tree->log_mutex);
+
if (waitqueue_active(&log_root_tree->log_commit_wait[index2]))
wake_up(&log_root_tree->log_commit_wait[index2]);
out:
+ /* See above. */
+ btrfs_remove_all_log_ctxs(root, index1, ret);
+
+ mutex_lock(&root->log_mutex);
+ root->log_transid_committed++;
atomic_set(&root->log_commit[index1], 0);
- smp_mb();
+ mutex_unlock(&root->log_mutex);
+
if (waitqueue_active(&root->log_commit_wait[index1]))
wake_up(&root->log_commit_wait[index1]);
return ret;
@@ -3479,7 +3572,8 @@ static int extent_cmp(void *priv, struct list_head *a, struct list_head *b)
static int log_one_extent(struct btrfs_trans_handle *trans,
struct inode *inode, struct btrfs_root *root,
- struct extent_map *em, struct btrfs_path *path)
+ struct extent_map *em, struct btrfs_path *path,
+ struct list_head *logged_list)
{
struct btrfs_root *log = root->log_root;
struct btrfs_file_extent_item *fi;
@@ -3495,7 +3589,6 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
u64 extent_offset = em->start - em->orig_start;
u64 block_len;
int ret;
- int index = log->log_transid % 2;
bool skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
int extent_inserted = 0;
@@ -3579,17 +3672,12 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
* First check and see if our csums are on our outstanding ordered
* extents.
*/
-again:
- spin_lock_irq(&log->log_extents_lock[index]);
- list_for_each_entry(ordered, &log->logged_list[index], log_list) {
+ list_for_each_entry(ordered, logged_list, log_list) {
struct btrfs_ordered_sum *sum;
if (!mod_len)
break;
- if (ordered->inode != inode)
- continue;
-
if (ordered->file_offset + ordered->len <= mod_start ||
mod_start + mod_len <= ordered->file_offset)
continue;
@@ -3632,12 +3720,6 @@ again:
if (test_and_set_bit(BTRFS_ORDERED_LOGGED_CSUM,
&ordered->flags))
continue;
- atomic_inc(&ordered->refs);
- spin_unlock_irq(&log->log_extents_lock[index]);
- /*
- * we've dropped the lock, we must either break or
- * start over after this.
- */
if (ordered->csum_bytes_left) {
btrfs_start_ordered_extent(inode, ordered, 0);
@@ -3647,16 +3729,11 @@ again:
list_for_each_entry(sum, &ordered->list, list) {
ret = btrfs_csum_file_blocks(trans, log, sum);
- if (ret) {
- btrfs_put_ordered_extent(ordered);
+ if (ret)
goto unlocked;
- }
}
- btrfs_put_ordered_extent(ordered);
- goto again;
}
- spin_unlock_irq(&log->log_extents_lock[index]);
unlocked:
if (!mod_len || ret)
@@ -3694,7 +3771,8 @@ unlocked:
static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct inode *inode,
- struct btrfs_path *path)
+ struct btrfs_path *path,
+ struct list_head *logged_list)
{
struct extent_map *em, *n;
struct list_head extents;
@@ -3752,7 +3830,7 @@ process:
write_unlock(&tree->lock);
- ret = log_one_extent(trans, inode, root, em, path);
+ ret = log_one_extent(trans, inode, root, em, path, logged_list);
write_lock(&tree->lock);
clear_em_logging(tree, em);
free_extent_map(em);
@@ -3788,6 +3866,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
struct btrfs_key max_key;
struct btrfs_root *log = root->log_root;
struct extent_buffer *src = NULL;
+ LIST_HEAD(logged_list);
u64 last_extent = 0;
int err = 0;
int ret;
@@ -3836,7 +3915,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
mutex_lock(&BTRFS_I(inode)->log_mutex);
- btrfs_get_logged_extents(log, inode);
+ btrfs_get_logged_extents(inode, &logged_list);
/*
* a brute force approach to making sure we get the most uptodate
@@ -3962,7 +4041,8 @@ log_extents:
btrfs_release_path(path);
btrfs_release_path(dst_path);
if (fast_search) {
- ret = btrfs_log_changed_extents(trans, root, inode, dst_path);
+ ret = btrfs_log_changed_extents(trans, root, inode, dst_path,
+ &logged_list);
if (ret) {
err = ret;
goto out_unlock;
@@ -3987,8 +4067,10 @@ log_extents:
BTRFS_I(inode)->logged_trans = trans->transid;
BTRFS_I(inode)->last_log_commit = BTRFS_I(inode)->last_sub_trans;
out_unlock:
- if (err)
- btrfs_free_logged_extents(log, log->log_transid);
+ if (unlikely(err))
+ btrfs_put_logged_extents(&logged_list);
+ else
+ btrfs_submit_logged_extents(&logged_list, log);
mutex_unlock(&BTRFS_I(inode)->log_mutex);
btrfs_free_path(path);
@@ -4079,7 +4161,8 @@ out:
*/
static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct inode *inode,
- struct dentry *parent, int exists_only)
+ struct dentry *parent, int exists_only,
+ struct btrfs_log_ctx *ctx)
{
int inode_only = exists_only ? LOG_INODE_EXISTS : LOG_INODE_ALL;
struct super_block *sb;
@@ -4116,9 +4199,9 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
goto end_no_trans;
}
- ret = start_log_trans(trans, root);
+ ret = start_log_trans(trans, root, ctx);
if (ret)
- goto end_trans;
+ goto end_no_trans;
ret = btrfs_log_inode(trans, root, inode, inode_only);
if (ret)
@@ -4166,6 +4249,9 @@ end_trans:
root->fs_info->last_trans_log_full_commit = trans->transid;
ret = 1;
}
+
+ if (ret)
+ btrfs_remove_log_ctx(root, ctx);
btrfs_end_log_trans(root);
end_no_trans:
return ret;
@@ -4178,12 +4264,14 @@ end_no_trans:
* data on disk.
*/
int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct dentry *dentry)
+ struct btrfs_root *root, struct dentry *dentry,
+ struct btrfs_log_ctx *ctx)
{
struct dentry *parent = dget_parent(dentry);
int ret;
- ret = btrfs_log_inode_parent(trans, root, dentry->d_inode, parent, 0);
+ ret = btrfs_log_inode_parent(trans, root, dentry->d_inode, parent,
+ 0, ctx);
dput(parent);
return ret;
@@ -4420,6 +4508,6 @@ int btrfs_log_new_name(struct btrfs_trans_handle *trans,
root->fs_info->last_trans_committed))
return 0;
- return btrfs_log_inode_parent(trans, root, inode, parent, 1);
+ return btrfs_log_inode_parent(trans, root, inode, parent, 1, NULL);
}
diff --git a/fs/btrfs/tree-log.h b/fs/btrfs/tree-log.h
index 1d4ae0d15a70..91b145fce333 100644
--- a/fs/btrfs/tree-log.h
+++ b/fs/btrfs/tree-log.h
@@ -22,14 +22,28 @@
/* return value for btrfs_log_dentry_safe that means we don't need to log it at all */
#define BTRFS_NO_LOG_SYNC 256
+struct btrfs_log_ctx {
+ int log_ret;
+ int log_transid;
+ struct list_head list;
+};
+
+static inline void btrfs_init_log_ctx(struct btrfs_log_ctx *ctx)
+{
+ ctx->log_ret = 0;
+ ctx->log_transid = 0;
+ INIT_LIST_HEAD(&ctx->list);
+}
+
int btrfs_sync_log(struct btrfs_trans_handle *trans,
- struct btrfs_root *root);
+ struct btrfs_root *root, struct btrfs_log_ctx *ctx);
int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root);
int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info);
int btrfs_recover_log_trees(struct btrfs_root *tree_root);
int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct dentry *dentry);
+ struct btrfs_root *root, struct dentry *dentry,
+ struct btrfs_log_ctx *ctx);
int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
const char *name, int name_len,
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index bab0b84d8f80..d241130a32fd 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -415,7 +415,8 @@ loop_lock:
device->running_pending = 1;
spin_unlock(&device->io_lock);
- btrfs_requeue_work(&device->work);
+ btrfs_queue_work(fs_info->submit_workers,
+ &device->work);
goto done;
}
/* unplug every 64 requests just for good measure */
@@ -5263,6 +5264,7 @@ int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
static void btrfs_end_bio(struct bio *bio, int err)
{
struct btrfs_bio *bbio = bio->bi_private;
+ struct btrfs_device *dev = bbio->stripes[0].dev;
int is_orig_bio = 0;
if (err) {
@@ -5270,7 +5272,6 @@ static void btrfs_end_bio(struct bio *bio, int err)
if (err == -EIO || err == -EREMOTEIO) {
unsigned int stripe_index =
btrfs_io_bio(bio)->stripe_index;
- struct btrfs_device *dev;
BUG_ON(stripe_index >= bbio->num_stripes);
dev = bbio->stripes[stripe_index].dev;
@@ -5292,6 +5293,8 @@ static void btrfs_end_bio(struct bio *bio, int err)
if (bio == bbio->orig_bio)
is_orig_bio = 1;
+ btrfs_bio_counter_dec(bbio->fs_info);
+
if (atomic_dec_and_test(&bbio->stripes_pending)) {
if (!is_orig_bio) {
bio_put(bio);
@@ -5328,13 +5331,6 @@ static void btrfs_end_bio(struct bio *bio, int err)
}
}
-struct async_sched {
- struct bio *bio;
- int rw;
- struct btrfs_fs_info *info;
- struct btrfs_work work;
-};
-
/*
* see run_scheduled_bios for a description of why bios are collected for
* async submit.
@@ -5391,8 +5387,8 @@ static noinline void btrfs_schedule_bio(struct btrfs_root *root,
spin_unlock(&device->io_lock);
if (should_queue)
- btrfs_queue_worker(&root->fs_info->submit_workers,
- &device->work);
+ btrfs_queue_work(root->fs_info->submit_workers,
+ &device->work);
}
static int bio_size_ok(struct block_device *bdev, struct bio *bio,
@@ -5447,6 +5443,9 @@ static void submit_stripe_bio(struct btrfs_root *root, struct btrfs_bio *bbio,
}
#endif
bio->bi_bdev = dev->bdev;
+
+ btrfs_bio_counter_inc_noblocked(root->fs_info);
+
if (async)
btrfs_schedule_bio(root, dev, rw, bio);
else
@@ -5515,28 +5514,38 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
length = bio->bi_iter.bi_size;
map_length = length;
+ btrfs_bio_counter_inc_blocked(root->fs_info);
ret = __btrfs_map_block(root->fs_info, rw, logical, &map_length, &bbio,
mirror_num, &raid_map);
- if (ret) /* -ENOMEM */
+ if (ret) {
+ btrfs_bio_counter_dec(root->fs_info);
return ret;
+ }
total_devs = bbio->num_stripes;
bbio->orig_bio = first_bio;
bbio->private = first_bio->bi_private;
bbio->end_io = first_bio->bi_end_io;
+ bbio->fs_info = root->fs_info;
atomic_set(&bbio->stripes_pending, bbio->num_stripes);
if (raid_map) {
/* In this case, map_length has been set to the length of
a single stripe; not the whole write */
if (rw & WRITE) {
- return raid56_parity_write(root, bio, bbio,
- raid_map, map_length);
+ ret = raid56_parity_write(root, bio, bbio,
+ raid_map, map_length);
} else {
- return raid56_parity_recover(root, bio, bbio,
- raid_map, map_length,
- mirror_num);
+ ret = raid56_parity_recover(root, bio, bbio,
+ raid_map, map_length,
+ mirror_num);
}
+ /*
+ * FIXME, replace dosen't support raid56 yet, please fix
+ * it in the future.
+ */
+ btrfs_bio_counter_dec(root->fs_info);
+ return ret;
}
if (map_length < length) {
@@ -5578,6 +5587,7 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
async_submit);
dev_nr++;
}
+ btrfs_bio_counter_dec(root->fs_info);
return 0;
}
@@ -5666,7 +5676,7 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info,
else
generate_random_uuid(dev->uuid);
- dev->work.func = pending_bios_fn;
+ btrfs_init_work(&dev->work, pending_bios_fn, NULL, NULL);
return dev;
}
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 8b3cd142b373..80754f9dd3df 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -192,6 +192,7 @@ typedef void (btrfs_bio_end_io_t) (struct btrfs_bio *bio, int err);
struct btrfs_bio {
atomic_t stripes_pending;
+ struct btrfs_fs_info *fs_info;
bio_end_io_t *end_io;
struct bio *orig_bio;
void *private;
diff --git a/fs/buffer.c b/fs/buffer.c
index 27265a8b43c1..8c53a2b15ecb 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -3088,7 +3088,7 @@ EXPORT_SYMBOL(submit_bh);
* until the buffer gets unlocked).
*
* ll_rw_block sets b_end_io to simple completion handler that marks
- * the buffer up-to-date (if approriate), unlocks the buffer and wakes
+ * the buffer up-to-date (if appropriate), unlocks the buffer and wakes
* any waiters.
*
* All of the buffers must be for the same device, and must also be a
diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
index ca65f39dc8dc..6494d9f673aa 100644
--- a/fs/cachefiles/namei.c
+++ b/fs/cachefiles/namei.c
@@ -391,12 +391,12 @@ try_again:
path.dentry = dir;
path_to_graveyard.mnt = cache->mnt;
path_to_graveyard.dentry = cache->graveyard;
- ret = security_path_rename(&path, rep, &path_to_graveyard, grave);
+ ret = security_path_rename(&path, rep, &path_to_graveyard, grave, 0);
if (ret < 0) {
cachefiles_io_error(cache, "Rename security error %d", ret);
} else {
ret = vfs_rename(dir->d_inode, rep,
- cache->graveyard->d_inode, grave, NULL);
+ cache->graveyard->d_inode, grave, NULL, 0);
if (ret != 0 && ret != -ENOMEM)
cachefiles_io_error(cache,
"Rename failed with error %d", ret);
diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c
index ebaff368120d..4b1fb5ca65b8 100644
--- a/fs/cachefiles/rdwr.c
+++ b/fs/cachefiles/rdwr.c
@@ -265,24 +265,22 @@ static int cachefiles_read_backing_file_one(struct cachefiles_object *object,
goto nomem_monitor;
}
- ret = add_to_page_cache(newpage, bmapping,
- netpage->index, cachefiles_gfp);
+ ret = add_to_page_cache_lru(newpage, bmapping,
+ netpage->index, cachefiles_gfp);
if (ret == 0)
goto installed_new_backing_page;
if (ret != -EEXIST)
goto nomem_page;
}
- /* we've installed a new backing page, so now we need to add it
- * to the LRU list and start it reading */
+ /* we've installed a new backing page, so now we need to start
+ * it reading */
installed_new_backing_page:
_debug("- new %p", newpage);
backpage = newpage;
newpage = NULL;
- lru_cache_add_file(backpage);
-
read_backing_page:
ret = bmapping->a_ops->readpage(NULL, backpage);
if (ret < 0)
@@ -510,24 +508,23 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object,
goto nomem;
}
- ret = add_to_page_cache(newpage, bmapping,
- netpage->index, cachefiles_gfp);
+ ret = add_to_page_cache_lru(newpage, bmapping,
+ netpage->index,
+ cachefiles_gfp);
if (ret == 0)
goto installed_new_backing_page;
if (ret != -EEXIST)
goto nomem;
}
- /* we've installed a new backing page, so now we need to add it
- * to the LRU list and start it reading */
+ /* we've installed a new backing page, so now we need
+ * to start it reading */
installed_new_backing_page:
_debug("- new %p", newpage);
backpage = newpage;
newpage = NULL;
- lru_cache_add_file(backpage);
-
reread_backing_page:
ret = bmapping->a_ops->readpage(NULL, backpage);
if (ret < 0)
@@ -538,8 +535,8 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object,
monitor_backing_page:
_debug("- monitor add");
- ret = add_to_page_cache(netpage, op->mapping, netpage->index,
- cachefiles_gfp);
+ ret = add_to_page_cache_lru(netpage, op->mapping,
+ netpage->index, cachefiles_gfp);
if (ret < 0) {
if (ret == -EEXIST) {
page_cache_release(netpage);
@@ -549,8 +546,6 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object,
goto nomem;
}
- lru_cache_add_file(netpage);
-
/* install a monitor */
page_cache_get(netpage);
monitor->netfs_page = netpage;
@@ -613,8 +608,8 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object,
backing_page_already_uptodate:
_debug("- uptodate");
- ret = add_to_page_cache(netpage, op->mapping, netpage->index,
- cachefiles_gfp);
+ ret = add_to_page_cache_lru(netpage, op->mapping,
+ netpage->index, cachefiles_gfp);
if (ret < 0) {
if (ret == -EEXIST) {
page_cache_release(netpage);
@@ -631,8 +626,6 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object,
fscache_mark_page_cached(op, netpage);
- lru_cache_add_file(netpage);
-
/* the netpage is unlocked and marked up to date here */
fscache_end_io(op, netpage, 0);
page_cache_release(netpage);
diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c
index 8c44fdd4e1c3..834f9f3723fb 100644
--- a/fs/ceph/cache.c
+++ b/fs/ceph/cache.c
@@ -205,6 +205,7 @@ void ceph_fscache_register_inode_cookie(struct ceph_fs_client* fsc,
ci->fscache = fscache_acquire_cookie(fsc->fscache,
&ceph_fscache_inode_object_def,
ci, true);
+ fscache_check_consistency(ci->fscache);
done:
mutex_unlock(&inode->i_mutex);
diff --git a/fs/ceph/cache.h b/fs/ceph/cache.h
index da95f61b7a09..5ac591bd012b 100644
--- a/fs/ceph/cache.h
+++ b/fs/ceph/cache.h
@@ -48,6 +48,12 @@ void ceph_readpage_to_fscache(struct inode *inode, struct page *page);
void ceph_invalidate_fscache_page(struct inode* inode, struct page *page);
void ceph_queue_revalidate(struct inode *inode);
+static inline void ceph_fscache_update_objectsize(struct inode *inode)
+{
+ struct ceph_inode_info *ci = ceph_inode(inode);
+ fscache_attr_changed(ci->fscache);
+}
+
static inline void ceph_fscache_invalidate(struct inode *inode)
{
fscache_invalidate(ceph_inode(inode)->fscache);
@@ -135,6 +141,10 @@ static inline void ceph_readpage_to_fscache(struct inode *inode,
{
}
+static inline void ceph_fscache_update_objectsize(struct inode *inode)
+{
+}
+
static inline void ceph_fscache_invalidate(struct inode *inode)
{
}
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index 17543383545c..2e5e648eb5c3 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -622,8 +622,10 @@ retry:
if (flags & CEPH_CAP_FLAG_AUTH) {
if (ci->i_auth_cap == NULL ||
- ceph_seq_cmp(ci->i_auth_cap->mseq, mseq) < 0)
+ ceph_seq_cmp(ci->i_auth_cap->mseq, mseq) < 0) {
ci->i_auth_cap = cap;
+ cap->mds_wanted = wanted;
+ }
ci->i_cap_exporting_issued = 0;
} else {
WARN_ON(ci->i_auth_cap == cap);
@@ -885,7 +887,10 @@ int __ceph_caps_mds_wanted(struct ceph_inode_info *ci)
cap = rb_entry(p, struct ceph_cap, ci_node);
if (!__cap_is_valid(cap))
continue;
- mds_wanted |= cap->mds_wanted;
+ if (cap == ci->i_auth_cap)
+ mds_wanted |= cap->mds_wanted;
+ else
+ mds_wanted |= (cap->mds_wanted & ~CEPH_CAP_ANY_FILE_WR);
}
return mds_wanted;
}
diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c
index 6d59006bfa27..16b54aa31f08 100644
--- a/fs/ceph/debugfs.c
+++ b/fs/ceph/debugfs.c
@@ -93,6 +93,8 @@ static int mdsc_show(struct seq_file *s, void *p)
} else if (req->r_path1) {
seq_printf(s, " #%llx/%s", req->r_ino1.ino,
req->r_path1);
+ } else {
+ seq_printf(s, " #%llx", req->r_ino1.ino);
}
if (req->r_old_dentry) {
@@ -102,7 +104,8 @@ static int mdsc_show(struct seq_file *s, void *p)
path = NULL;
spin_lock(&req->r_old_dentry->d_lock);
seq_printf(s, " #%llx/%.*s (%s)",
- ceph_ino(req->r_old_dentry_dir),
+ req->r_old_dentry_dir ?
+ ceph_ino(req->r_old_dentry_dir) : 0,
req->r_old_dentry->d_name.len,
req->r_old_dentry->d_name.name,
path ? path : "");
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 45eda6d7a40c..766410a12c2c 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -119,7 +119,8 @@ static int fpos_cmp(loff_t l, loff_t r)
* defined IFF we hold CEPH_CAP_FILE_SHARED (which will be revoked by
* the MDS if/when the directory is modified).
*/
-static int __dcache_readdir(struct file *file, struct dir_context *ctx)
+static int __dcache_readdir(struct file *file, struct dir_context *ctx,
+ u32 shared_gen)
{
struct ceph_file_info *fi = file->private_data;
struct dentry *parent = file->f_dentry;
@@ -133,8 +134,8 @@ static int __dcache_readdir(struct file *file, struct dir_context *ctx)
last = fi->dentry;
fi->dentry = NULL;
- dout("__dcache_readdir %p at %llu (last %p)\n", dir, ctx->pos,
- last);
+ dout("__dcache_readdir %p v%u at %llu (last %p)\n",
+ dir, shared_gen, ctx->pos, last);
spin_lock(&parent->d_lock);
@@ -161,7 +162,8 @@ more:
goto out_unlock;
}
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
- if (!d_unhashed(dentry) && dentry->d_inode &&
+ if (di->lease_shared_gen == shared_gen &&
+ !d_unhashed(dentry) && dentry->d_inode &&
ceph_snap(dentry->d_inode) != CEPH_SNAPDIR &&
ceph_ino(dentry->d_inode) != CEPH_INO_CEPH &&
fpos_cmp(ctx->pos, di->offset) <= 0)
@@ -190,7 +192,7 @@ more:
if (last) {
/* remember our position */
fi->dentry = last;
- fi->next_offset = di->offset;
+ fi->next_offset = fpos_off(di->offset);
}
dput(dentry);
return 0;
@@ -252,8 +254,6 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
int err;
u32 ftype;
struct ceph_mds_reply_info_parsed *rinfo;
- const int max_entries = fsc->mount_options->max_readdir;
- const int max_bytes = fsc->mount_options->max_readdir_bytes;
dout("readdir %p file %p frag %u off %u\n", inode, file, frag, off);
if (fi->flags & CEPH_F_ATEND)
@@ -291,8 +291,9 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
ceph_snap(inode) != CEPH_SNAPDIR &&
__ceph_dir_is_complete(ci) &&
__ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1)) {
+ u32 shared_gen = ci->i_shared_gen;
spin_unlock(&ci->i_ceph_lock);
- err = __dcache_readdir(file, ctx);
+ err = __dcache_readdir(file, ctx, shared_gen);
if (err != -EAGAIN)
return err;
} else {
@@ -322,14 +323,16 @@ more:
fi->last_readdir = NULL;
}
- /* requery frag tree, as the frag topology may have changed */
- frag = ceph_choose_frag(ceph_inode(inode), frag, NULL, NULL);
-
dout("readdir fetching %llx.%llx frag %x offset '%s'\n",
ceph_vinop(inode), frag, fi->last_name);
req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
if (IS_ERR(req))
return PTR_ERR(req);
+ err = ceph_alloc_readdir_reply_buffer(req, inode);
+ if (err) {
+ ceph_mdsc_put_request(req);
+ return err;
+ }
req->r_inode = inode;
ihold(inode);
req->r_dentry = dget(file->f_dentry);
@@ -340,9 +343,6 @@ more:
req->r_path2 = kstrdup(fi->last_name, GFP_NOFS);
req->r_readdir_offset = fi->next_offset;
req->r_args.readdir.frag = cpu_to_le32(frag);
- req->r_args.readdir.max_entries = cpu_to_le32(max_entries);
- req->r_args.readdir.max_bytes = cpu_to_le32(max_bytes);
- req->r_num_caps = max_entries + 1;
err = ceph_mdsc_do_request(mdsc, NULL, req);
if (err < 0) {
ceph_mdsc_put_request(req);
@@ -369,9 +369,9 @@ more:
fi->next_offset = 0;
off = fi->next_offset;
}
+ fi->frag = frag;
fi->offset = fi->next_offset;
fi->last_readdir = req;
- fi->frag = frag;
if (req->r_reply_info.dir_end) {
kfree(fi->last_name);
@@ -454,7 +454,7 @@ more:
return 0;
}
-static void reset_readdir(struct ceph_file_info *fi)
+static void reset_readdir(struct ceph_file_info *fi, unsigned frag)
{
if (fi->last_readdir) {
ceph_mdsc_put_request(fi->last_readdir);
@@ -462,7 +462,10 @@ static void reset_readdir(struct ceph_file_info *fi)
}
kfree(fi->last_name);
fi->last_name = NULL;
- fi->next_offset = 2; /* compensate for . and .. */
+ if (ceph_frag_is_leftmost(frag))
+ fi->next_offset = 2; /* compensate for . and .. */
+ else
+ fi->next_offset = 0;
if (fi->dentry) {
dput(fi->dentry);
fi->dentry = NULL;
@@ -474,7 +477,7 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence)
{
struct ceph_file_info *fi = file->private_data;
struct inode *inode = file->f_mapping->host;
- loff_t old_offset = offset;
+ loff_t old_offset = ceph_make_fpos(fi->frag, fi->next_offset);
loff_t retval;
mutex_lock(&inode->i_mutex);
@@ -491,7 +494,7 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence)
goto out;
}
- if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) {
+ if (offset >= 0) {
if (offset != file->f_pos) {
file->f_pos = offset;
file->f_version = 0;
@@ -504,14 +507,14 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence)
* seek to new frag, or seek prior to current chunk.
*/
if (offset == 0 ||
- fpos_frag(offset) != fpos_frag(old_offset) ||
+ fpos_frag(offset) != fi->frag ||
fpos_off(offset) < fi->offset) {
dout("dir_llseek dropping %p content\n", file);
- reset_readdir(fi);
+ reset_readdir(fi, fpos_frag(offset));
}
/* bump dir_release_count if we did a forward seek */
- if (offset > old_offset)
+ if (fpos_cmp(offset, old_offset) > 0)
fi->dir_release_count--;
}
out:
@@ -812,8 +815,7 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir,
}
req->r_dentry = dget(dentry);
req->r_num_caps = 2;
- req->r_old_dentry = dget(old_dentry); /* or inode? hrm. */
- req->r_old_dentry_dir = ceph_get_dentry_parent_inode(old_dentry);
+ req->r_old_dentry = dget(old_dentry);
req->r_locked_dir = dir;
req->r_dentry_drop = CEPH_CAP_FILE_SHARED;
req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
@@ -911,10 +913,11 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_RENAME, USE_AUTH_MDS);
if (IS_ERR(req))
return PTR_ERR(req);
+ ihold(old_dir);
req->r_dentry = dget(new_dentry);
req->r_num_caps = 2;
req->r_old_dentry = dget(old_dentry);
- req->r_old_dentry_dir = ceph_get_dentry_parent_inode(old_dentry);
+ req->r_old_dentry_dir = old_dir;
req->r_locked_dir = new_dir;
req->r_old_dentry_drop = CEPH_CAP_FILE_SHARED;
req->r_old_dentry_unless = CEPH_CAP_FILE_EXCL;
diff --git a/fs/ceph/export.c b/fs/ceph/export.c
index 16796be53ca5..00d6af6a32ec 100644
--- a/fs/ceph/export.c
+++ b/fs/ceph/export.c
@@ -8,23 +8,6 @@
#include "mds_client.h"
/*
- * NFS export support
- *
- * NFS re-export of a ceph mount is, at present, only semireliable.
- * The basic issue is that the Ceph architectures doesn't lend itself
- * well to generating filehandles that will remain valid forever.
- *
- * So, we do our best. If you're lucky, your inode will be in the
- * client's cache. If it's not, and you have a connectable fh, then
- * the MDS server may be able to find it for you. Otherwise, you get
- * ESTALE.
- *
- * There are ways to this more reliable, but in the non-connectable fh
- * case, we won't every work perfectly, and in the connectable case,
- * some changes are needed on the MDS side to work better.
- */
-
-/*
* Basic fh
*/
struct ceph_nfs_fh {
@@ -32,22 +15,12 @@ struct ceph_nfs_fh {
} __attribute__ ((packed));
/*
- * Larger 'connectable' fh that includes parent ino and name hash.
- * Use this whenever possible, as it works more reliably.
+ * Larger fh that includes parent ino.
*/
struct ceph_nfs_confh {
u64 ino, parent_ino;
- u32 parent_name_hash;
} __attribute__ ((packed));
-/*
- * The presence of @parent_inode here tells us whether NFS wants a
- * connectable file handle. However, we want to make a connectionable
- * file handle unconditionally so that the MDS gets as much of a hint
- * as possible. That means we only use @parent_dentry to indicate
- * whether nfsd wants a connectable fh, and whether we should indicate
- * failure from a too-small @max_len.
- */
static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
struct inode *parent_inode)
{
@@ -56,54 +29,36 @@ static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
struct ceph_nfs_confh *cfh = (void *)rawfh;
int connected_handle_length = sizeof(*cfh)/4;
int handle_length = sizeof(*fh)/4;
- struct dentry *dentry;
- struct dentry *parent;
/* don't re-export snaps */
if (ceph_snap(inode) != CEPH_NOSNAP)
return -EINVAL;
- dentry = d_find_alias(inode);
+ if (parent_inode && (*max_len < connected_handle_length)) {
+ *max_len = connected_handle_length;
+ return FILEID_INVALID;
+ } else if (*max_len < handle_length) {
+ *max_len = handle_length;
+ return FILEID_INVALID;
+ }
- /* if we found an alias, generate a connectable fh */
- if (*max_len >= connected_handle_length && dentry) {
- dout("encode_fh %p connectable\n", dentry);
- spin_lock(&dentry->d_lock);
- parent = dentry->d_parent;
+ if (parent_inode) {
+ dout("encode_fh %llx with parent %llx\n",
+ ceph_ino(inode), ceph_ino(parent_inode));
cfh->ino = ceph_ino(inode);
- cfh->parent_ino = ceph_ino(parent->d_inode);
- cfh->parent_name_hash = ceph_dentry_hash(parent->d_inode,
- dentry);
+ cfh->parent_ino = ceph_ino(parent_inode);
*max_len = connected_handle_length;
- type = 2;
- spin_unlock(&dentry->d_lock);
- } else if (*max_len >= handle_length) {
- if (parent_inode) {
- /* nfsd wants connectable */
- *max_len = connected_handle_length;
- type = FILEID_INVALID;
- } else {
- dout("encode_fh %p\n", dentry);
- fh->ino = ceph_ino(inode);
- *max_len = handle_length;
- type = 1;
- }
+ type = FILEID_INO32_GEN_PARENT;
} else {
+ dout("encode_fh %llx\n", ceph_ino(inode));
+ fh->ino = ceph_ino(inode);
*max_len = handle_length;
- type = FILEID_INVALID;
+ type = FILEID_INO32_GEN;
}
- if (dentry)
- dput(dentry);
return type;
}
-/*
- * convert regular fh to dentry
- *
- * FIXME: we should try harder by querying the mds for the ino.
- */
-static struct dentry *__fh_to_dentry(struct super_block *sb,
- struct ceph_nfs_fh *fh, int fh_len)
+static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
{
struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
struct inode *inode;
@@ -111,11 +66,7 @@ static struct dentry *__fh_to_dentry(struct super_block *sb,
struct ceph_vino vino;
int err;
- if (fh_len < sizeof(*fh) / 4)
- return ERR_PTR(-ESTALE);
-
- dout("__fh_to_dentry %llx\n", fh->ino);
- vino.ino = fh->ino;
+ vino.ino = ino;
vino.snap = CEPH_NOSNAP;
inode = ceph_find_inode(sb, vino);
if (!inode) {
@@ -139,139 +90,161 @@ static struct dentry *__fh_to_dentry(struct super_block *sb,
dentry = d_obtain_alias(inode);
if (IS_ERR(dentry)) {
- pr_err("fh_to_dentry %llx -- inode %p but ENOMEM\n",
- fh->ino, inode);
iput(inode);
return dentry;
}
err = ceph_init_dentry(dentry);
if (err < 0) {
- iput(inode);
+ dput(dentry);
return ERR_PTR(err);
}
- dout("__fh_to_dentry %llx %p dentry %p\n", fh->ino, inode, dentry);
+ dout("__fh_to_dentry %llx %p dentry %p\n", ino, inode, dentry);
return dentry;
}
/*
- * convert connectable fh to dentry
+ * convert regular fh to dentry
*/
-static struct dentry *__cfh_to_dentry(struct super_block *sb,
- struct ceph_nfs_confh *cfh, int fh_len)
+static struct dentry *ceph_fh_to_dentry(struct super_block *sb,
+ struct fid *fid,
+ int fh_len, int fh_type)
+{
+ struct ceph_nfs_fh *fh = (void *)fid->raw;
+
+ if (fh_type != FILEID_INO32_GEN &&
+ fh_type != FILEID_INO32_GEN_PARENT)
+ return NULL;
+ if (fh_len < sizeof(*fh) / 4)
+ return NULL;
+
+ dout("fh_to_dentry %llx\n", fh->ino);
+ return __fh_to_dentry(sb, fh->ino);
+}
+
+static struct dentry *__get_parent(struct super_block *sb,
+ struct dentry *child, u64 ino)
{
struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
+ struct ceph_mds_request *req;
struct inode *inode;
struct dentry *dentry;
- struct ceph_vino vino;
int err;
- if (fh_len < sizeof(*cfh) / 4)
- return ERR_PTR(-ESTALE);
-
- dout("__cfh_to_dentry %llx (%llx/%x)\n",
- cfh->ino, cfh->parent_ino, cfh->parent_name_hash);
-
- vino.ino = cfh->ino;
- vino.snap = CEPH_NOSNAP;
- inode = ceph_find_inode(sb, vino);
- if (!inode) {
- struct ceph_mds_request *req;
-
- req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPHASH,
- USE_ANY_MDS);
- if (IS_ERR(req))
- return ERR_CAST(req);
+ req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPPARENT,
+ USE_ANY_MDS);
+ if (IS_ERR(req))
+ return ERR_CAST(req);
- req->r_ino1 = vino;
- req->r_ino2.ino = cfh->parent_ino;
- req->r_ino2.snap = CEPH_NOSNAP;
- req->r_path2 = kmalloc(16, GFP_NOFS);
- snprintf(req->r_path2, 16, "%d", cfh->parent_name_hash);
- req->r_num_caps = 1;
- err = ceph_mdsc_do_request(mdsc, NULL, req);
- inode = req->r_target_inode;
- if (inode)
- ihold(inode);
- ceph_mdsc_put_request(req);
- if (!inode)
- return ERR_PTR(err ? err : -ESTALE);
+ if (child) {
+ req->r_inode = child->d_inode;
+ ihold(child->d_inode);
+ } else {
+ req->r_ino1 = (struct ceph_vino) {
+ .ino = ino,
+ .snap = CEPH_NOSNAP,
+ };
}
+ req->r_num_caps = 1;
+ err = ceph_mdsc_do_request(mdsc, NULL, req);
+ inode = req->r_target_inode;
+ if (inode)
+ ihold(inode);
+ ceph_mdsc_put_request(req);
+ if (!inode)
+ return ERR_PTR(-ENOENT);
dentry = d_obtain_alias(inode);
if (IS_ERR(dentry)) {
- pr_err("cfh_to_dentry %llx -- inode %p but ENOMEM\n",
- cfh->ino, inode);
iput(inode);
return dentry;
}
err = ceph_init_dentry(dentry);
if (err < 0) {
- iput(inode);
+ dput(dentry);
return ERR_PTR(err);
}
- dout("__cfh_to_dentry %llx %p dentry %p\n", cfh->ino, inode, dentry);
+ dout("__get_parent ino %llx parent %p ino %llx.%llx\n",
+ child ? ceph_ino(child->d_inode) : ino,
+ dentry, ceph_vinop(inode));
return dentry;
}
-static struct dentry *ceph_fh_to_dentry(struct super_block *sb, struct fid *fid,
- int fh_len, int fh_type)
+struct dentry *ceph_get_parent(struct dentry *child)
{
- if (fh_type == 1)
- return __fh_to_dentry(sb, (struct ceph_nfs_fh *)fid->raw,
- fh_len);
- else
- return __cfh_to_dentry(sb, (struct ceph_nfs_confh *)fid->raw,
- fh_len);
+ /* don't re-export snaps */
+ if (ceph_snap(child->d_inode) != CEPH_NOSNAP)
+ return ERR_PTR(-EINVAL);
+
+ dout("get_parent %p ino %llx.%llx\n",
+ child, ceph_vinop(child->d_inode));
+ return __get_parent(child->d_sb, child, 0);
}
/*
- * get parent, if possible.
- *
- * FIXME: we could do better by querying the mds to discover the
- * parent.
+ * convert regular fh to parent
*/
static struct dentry *ceph_fh_to_parent(struct super_block *sb,
- struct fid *fid,
+ struct fid *fid,
int fh_len, int fh_type)
{
struct ceph_nfs_confh *cfh = (void *)fid->raw;
- struct ceph_vino vino;
- struct inode *inode;
struct dentry *dentry;
- int err;
- if (fh_type == 1)
- return ERR_PTR(-ESTALE);
+ if (fh_type != FILEID_INO32_GEN_PARENT)
+ return NULL;
if (fh_len < sizeof(*cfh) / 4)
- return ERR_PTR(-ESTALE);
+ return NULL;
- pr_debug("fh_to_parent %llx/%d\n", cfh->parent_ino,
- cfh->parent_name_hash);
+ dout("fh_to_parent %llx\n", cfh->parent_ino);
+ dentry = __get_parent(sb, NULL, cfh->ino);
+ if (IS_ERR(dentry) && PTR_ERR(dentry) == -ENOENT)
+ dentry = __fh_to_dentry(sb, cfh->parent_ino);
+ return dentry;
+}
- vino.ino = cfh->ino;
- vino.snap = CEPH_NOSNAP;
- inode = ceph_find_inode(sb, vino);
- if (!inode)
- return ERR_PTR(-ESTALE);
+static int ceph_get_name(struct dentry *parent, char *name,
+ struct dentry *child)
+{
+ struct ceph_mds_client *mdsc;
+ struct ceph_mds_request *req;
+ int err;
- dentry = d_obtain_alias(inode);
- if (IS_ERR(dentry)) {
- pr_err("fh_to_parent %llx -- inode %p but ENOMEM\n",
- cfh->ino, inode);
- iput(inode);
- return dentry;
- }
- err = ceph_init_dentry(dentry);
- if (err < 0) {
- iput(inode);
- return ERR_PTR(err);
+ mdsc = ceph_inode_to_client(child->d_inode)->mdsc;
+ req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPNAME,
+ USE_ANY_MDS);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ mutex_lock(&parent->d_inode->i_mutex);
+
+ req->r_inode = child->d_inode;
+ ihold(child->d_inode);
+ req->r_ino2 = ceph_vino(parent->d_inode);
+ req->r_locked_dir = parent->d_inode;
+ req->r_num_caps = 2;
+ err = ceph_mdsc_do_request(mdsc, NULL, req);
+
+ mutex_unlock(&parent->d_inode->i_mutex);
+
+ if (!err) {
+ struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
+ memcpy(name, rinfo->dname, rinfo->dname_len);
+ name[rinfo->dname_len] = 0;
+ dout("get_name %p ino %llx.%llx name %s\n",
+ child, ceph_vinop(child->d_inode), name);
+ } else {
+ dout("get_name %p ino %llx.%llx err %d\n",
+ child, ceph_vinop(child->d_inode), err);
}
- dout("fh_to_parent %llx %p dentry %p\n", cfh->ino, inode, dentry);
- return dentry;
+
+ ceph_mdsc_put_request(req);
+ return err;
}
const struct export_operations ceph_export_ops = {
.encode_fh = ceph_encode_fh,
.fh_to_dentry = ceph_fh_to_dentry,
.fh_to_parent = ceph_fh_to_parent,
+ .get_parent = ceph_get_parent,
+ .get_name = ceph_get_name,
};
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 09c7afe32e49..66075a4ad979 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -210,7 +210,7 @@ int ceph_open(struct inode *inode, struct file *file)
ihold(inode);
req->r_num_caps = 1;
- if (flags & (O_CREAT|O_TRUNC))
+ if (flags & O_CREAT)
parent_inode = ceph_get_dentry_parent_inode(file->f_dentry);
err = ceph_mdsc_do_request(mdsc, parent_inode, req);
iput(parent_inode);
@@ -291,8 +291,9 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
}
err = finish_open(file, dentry, ceph_open, opened);
}
-
out_err:
+ if (!req->r_err && req->r_target_inode)
+ ceph_put_fmode(ceph_inode(req->r_target_inode), req->r_fmode);
ceph_mdsc_put_request(req);
dout("atomic_open result=%d\n", err);
return err;
@@ -970,6 +971,7 @@ retry_snap:
goto retry_snap;
}
} else {
+ loff_t old_size = inode->i_size;
/*
* No need to acquire the i_truncate_mutex. Because
* the MDS revokes Fwb caps before sending truncate
@@ -980,6 +982,8 @@ retry_snap:
written = generic_file_buffered_write(iocb, iov, nr_segs,
pos, &iocb->ki_pos,
count, 0);
+ if (inode->i_size > old_size)
+ ceph_fscache_update_objectsize(inode);
mutex_unlock(&inode->i_mutex);
}
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 32d519d8a2e2..0b0728e5be2d 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -659,14 +659,6 @@ static int fill_inode(struct inode *inode,
le32_to_cpu(info->time_warp_seq),
&ctime, &mtime, &atime);
- /* only update max_size on auth cap */
- if ((info->cap.flags & CEPH_CAP_FLAG_AUTH) &&
- ci->i_max_size != le64_to_cpu(info->max_size)) {
- dout("max_size %lld -> %llu\n", ci->i_max_size,
- le64_to_cpu(info->max_size));
- ci->i_max_size = le64_to_cpu(info->max_size);
- }
-
ci->i_layout = info->layout;
inode->i_blkbits = fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1;
@@ -755,6 +747,14 @@ static int fill_inode(struct inode *inode,
ci->i_max_offset = 2;
}
no_change:
+ /* only update max_size on auth cap */
+ if ((info->cap.flags & CEPH_CAP_FLAG_AUTH) &&
+ ci->i_max_size != le64_to_cpu(info->max_size)) {
+ dout("max_size %lld -> %llu\n", ci->i_max_size,
+ le64_to_cpu(info->max_size));
+ ci->i_max_size = le64_to_cpu(info->max_size);
+ }
+
spin_unlock(&ci->i_ceph_lock);
/* queue truncate if we saw i_size decrease */
@@ -1044,10 +1044,59 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req,
session, req->r_request_started, -1,
&req->r_caps_reservation);
if (err < 0)
- return err;
+ goto done;
} else {
WARN_ON_ONCE(1);
}
+
+ if (dir && req->r_op == CEPH_MDS_OP_LOOKUPNAME) {
+ struct qstr dname;
+ struct dentry *dn, *parent;
+
+ BUG_ON(!rinfo->head->is_target);
+ BUG_ON(req->r_dentry);
+
+ parent = d_find_any_alias(dir);
+ BUG_ON(!parent);
+
+ dname.name = rinfo->dname;
+ dname.len = rinfo->dname_len;
+ dname.hash = full_name_hash(dname.name, dname.len);
+ vino.ino = le64_to_cpu(rinfo->targeti.in->ino);
+ vino.snap = le64_to_cpu(rinfo->targeti.in->snapid);
+retry_lookup:
+ dn = d_lookup(parent, &dname);
+ dout("d_lookup on parent=%p name=%.*s got %p\n",
+ parent, dname.len, dname.name, dn);
+
+ if (!dn) {
+ dn = d_alloc(parent, &dname);
+ dout("d_alloc %p '%.*s' = %p\n", parent,
+ dname.len, dname.name, dn);
+ if (dn == NULL) {
+ dput(parent);
+ err = -ENOMEM;
+ goto done;
+ }
+ err = ceph_init_dentry(dn);
+ if (err < 0) {
+ dput(dn);
+ dput(parent);
+ goto done;
+ }
+ } else if (dn->d_inode &&
+ (ceph_ino(dn->d_inode) != vino.ino ||
+ ceph_snap(dn->d_inode) != vino.snap)) {
+ dout(" dn %p points to wrong inode %p\n",
+ dn, dn->d_inode);
+ d_delete(dn);
+ dput(dn);
+ goto retry_lookup;
+ }
+
+ req->r_dentry = dn;
+ dput(parent);
+ }
}
if (rinfo->head->is_target) {
@@ -1063,7 +1112,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req,
err = fill_inode(in, &rinfo->targeti, NULL,
session, req->r_request_started,
- (le32_to_cpu(rinfo->head->result) == 0) ?
+ (!req->r_aborted && rinfo->head->result == 0) ?
req->r_fmode : -1,
&req->r_caps_reservation);
if (err < 0) {
@@ -1616,8 +1665,6 @@ static const struct inode_operations ceph_symlink_iops = {
.getxattr = ceph_getxattr,
.listxattr = ceph_listxattr,
.removexattr = ceph_removexattr,
- .get_acl = ceph_get_acl,
- .set_acl = ceph_set_acl,
};
/*
@@ -1627,7 +1674,6 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
struct ceph_inode_info *ci = ceph_inode(inode);
- struct inode *parent_inode;
const unsigned int ia_valid = attr->ia_valid;
struct ceph_mds_request *req;
struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc;
@@ -1819,9 +1865,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
req->r_inode_drop = release;
req->r_args.setattr.mask = cpu_to_le32(mask);
req->r_num_caps = 1;
- parent_inode = ceph_get_dentry_parent_inode(dentry);
- err = ceph_mdsc_do_request(mdsc, parent_inode, req);
- iput(parent_inode);
+ err = ceph_mdsc_do_request(mdsc, NULL, req);
}
dout("setattr %p result=%d (%s locally, %d remote)\n", inode, err,
ceph_cap_string(dirtied), mask);
diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
index dc66c9e023e4..efbe08289292 100644
--- a/fs/ceph/ioctl.c
+++ b/fs/ceph/ioctl.c
@@ -64,7 +64,6 @@ static long __validate_layout(struct ceph_mds_client *mdsc,
static long ceph_ioctl_set_layout(struct file *file, void __user *arg)
{
struct inode *inode = file_inode(file);
- struct inode *parent_inode;
struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
struct ceph_mds_request *req;
struct ceph_ioctl_layout l;
@@ -121,9 +120,7 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg)
cpu_to_le32(l.object_size);
req->r_args.setlayout.layout.fl_pg_pool = cpu_to_le32(l.data_pool);
- parent_inode = ceph_get_dentry_parent_inode(file->f_dentry);
- err = ceph_mdsc_do_request(mdsc, parent_inode, req);
- iput(parent_inode);
+ err = ceph_mdsc_do_request(mdsc, NULL, req);
ceph_mdsc_put_request(req);
return err;
}
diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c
index ae6d14e82b0f..d94ba0df9f4d 100644
--- a/fs/ceph/locks.c
+++ b/fs/ceph/locks.c
@@ -2,11 +2,31 @@
#include <linux/file.h>
#include <linux/namei.h>
+#include <linux/random.h>
#include "super.h"
#include "mds_client.h"
#include <linux/ceph/pagelist.h>
+static u64 lock_secret;
+
+static inline u64 secure_addr(void *addr)
+{
+ u64 v = lock_secret ^ (u64)(unsigned long)addr;
+ /*
+ * Set the most significant bit, so that MDS knows the 'owner'
+ * is sufficient to identify the owner of lock. (old code uses
+ * both 'owner' and 'pid')
+ */
+ v |= (1ULL << 63);
+ return v;
+}
+
+void __init ceph_flock_init(void)
+{
+ get_random_bytes(&lock_secret, sizeof(lock_secret));
+}
+
/**
* Implement fcntl and flock locking functions.
*/
@@ -14,11 +34,11 @@ static int ceph_lock_message(u8 lock_type, u16 operation, struct file *file,
int cmd, u8 wait, struct file_lock *fl)
{
struct inode *inode = file_inode(file);
- struct ceph_mds_client *mdsc =
- ceph_sb_to_client(inode->i_sb)->mdsc;
+ struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
struct ceph_mds_request *req;
int err;
u64 length = 0;
+ u64 owner;
req = ceph_mdsc_create_request(mdsc, operation, USE_AUTH_MDS);
if (IS_ERR(req))
@@ -32,25 +52,27 @@ static int ceph_lock_message(u8 lock_type, u16 operation, struct file *file,
else
length = fl->fl_end - fl->fl_start + 1;
- dout("ceph_lock_message: rule: %d, op: %d, pid: %llu, start: %llu, "
- "length: %llu, wait: %d, type: %d", (int)lock_type,
- (int)operation, (u64)fl->fl_pid, fl->fl_start,
- length, wait, fl->fl_type);
+ if (lock_type == CEPH_LOCK_FCNTL)
+ owner = secure_addr(fl->fl_owner);
+ else
+ owner = secure_addr(fl->fl_file);
+
+ dout("ceph_lock_message: rule: %d, op: %d, owner: %llx, pid: %llu, "
+ "start: %llu, length: %llu, wait: %d, type: %d", (int)lock_type,
+ (int)operation, owner, (u64)fl->fl_pid, fl->fl_start, length,
+ wait, fl->fl_type);
req->r_args.filelock_change.rule = lock_type;
req->r_args.filelock_change.type = cmd;
+ req->r_args.filelock_change.owner = cpu_to_le64(owner);
req->r_args.filelock_change.pid = cpu_to_le64((u64)fl->fl_pid);
- /* This should be adjusted, but I'm not sure if
- namespaces actually get id numbers*/
- req->r_args.filelock_change.pid_namespace =
- cpu_to_le64((u64)(unsigned long)fl->fl_nspid);
req->r_args.filelock_change.start = cpu_to_le64(fl->fl_start);
req->r_args.filelock_change.length = cpu_to_le64(length);
req->r_args.filelock_change.wait = wait;
err = ceph_mdsc_do_request(mdsc, inode, req);
- if ( operation == CEPH_MDS_OP_GETFILELOCK){
+ if (operation == CEPH_MDS_OP_GETFILELOCK) {
fl->fl_pid = le64_to_cpu(req->r_reply_info.filelock_reply->pid);
if (CEPH_LOCK_SHARED == req->r_reply_info.filelock_reply->type)
fl->fl_type = F_RDLCK;
@@ -87,14 +109,19 @@ int ceph_lock(struct file *file, int cmd, struct file_lock *fl)
u8 wait = 0;
u16 op = CEPH_MDS_OP_SETFILELOCK;
- fl->fl_nspid = get_pid(task_tgid(current));
- dout("ceph_lock, fl_pid:%d", fl->fl_pid);
+ if (!(fl->fl_flags & FL_POSIX))
+ return -ENOLCK;
+ /* No mandatory locks */
+ if (__mandatory_lock(file->f_mapping->host) && fl->fl_type != F_UNLCK)
+ return -ENOLCK;
+
+ dout("ceph_lock, fl_owner: %p", fl->fl_owner);
/* set wait bit as appropriate, then make command as Ceph expects it*/
- if (F_SETLKW == cmd)
- wait = 1;
- if (F_GETLK == cmd)
+ if (IS_GETLK(cmd))
op = CEPH_MDS_OP_GETFILELOCK;
+ else if (IS_SETLKW(cmd))
+ wait = 1;
if (F_RDLCK == fl->fl_type)
lock_cmd = CEPH_LOCK_SHARED;
@@ -105,7 +132,7 @@ int ceph_lock(struct file *file, int cmd, struct file_lock *fl)
err = ceph_lock_message(CEPH_LOCK_FCNTL, op, file, lock_cmd, wait, fl);
if (!err) {
- if ( op != CEPH_MDS_OP_GETFILELOCK ){
+ if (op != CEPH_MDS_OP_GETFILELOCK) {
dout("mds locked, locking locally");
err = posix_lock_file(file, fl, NULL);
if (err && (CEPH_MDS_OP_SETFILELOCK == op)) {
@@ -131,20 +158,22 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl)
{
u8 lock_cmd;
int err;
- u8 wait = 1;
-
- fl->fl_nspid = get_pid(task_tgid(current));
- dout("ceph_flock, fl_pid:%d", fl->fl_pid);
-
- /* set wait bit, then clear it out of cmd*/
- if (cmd & LOCK_NB)
- wait = 0;
- cmd = cmd & (LOCK_SH | LOCK_EX | LOCK_UN);
- /* set command sequence that Ceph wants to see:
- shared lock, exclusive lock, or unlock */
- if (LOCK_SH == cmd)
+ u8 wait = 0;
+
+ if (!(fl->fl_flags & FL_FLOCK))
+ return -ENOLCK;
+ /* No mandatory locks */
+ if (__mandatory_lock(file->f_mapping->host) && fl->fl_type != F_UNLCK)
+ return -ENOLCK;
+
+ dout("ceph_flock, fl_file: %p", fl->fl_file);
+
+ if (IS_SETLKW(cmd))
+ wait = 1;
+
+ if (F_RDLCK == fl->fl_type)
lock_cmd = CEPH_LOCK_SHARED;
- else if (LOCK_EX == cmd)
+ else if (F_WRLCK == fl->fl_type)
lock_cmd = CEPH_LOCK_EXCL;
else
lock_cmd = CEPH_LOCK_UNLOCK;
@@ -280,13 +309,14 @@ int lock_to_ceph_filelock(struct file_lock *lock,
struct ceph_filelock *cephlock)
{
int err = 0;
-
cephlock->start = cpu_to_le64(lock->fl_start);
cephlock->length = cpu_to_le64(lock->fl_end - lock->fl_start + 1);
cephlock->client = cpu_to_le64(0);
- cephlock->pid = cpu_to_le64(lock->fl_pid);
- cephlock->pid_namespace =
- cpu_to_le64((u64)(unsigned long)lock->fl_nspid);
+ cephlock->pid = cpu_to_le64((u64)lock->fl_pid);
+ if (lock->fl_flags & FL_POSIX)
+ cephlock->owner = cpu_to_le64(secure_addr(lock->fl_owner));
+ else
+ cephlock->owner = cpu_to_le64(secure_addr(lock->fl_file));
switch (lock->fl_type) {
case F_RDLCK:
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index f4f050a69a48..2b4d093d0563 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -3,6 +3,7 @@
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/slab.h>
+#include <linux/gfp.h>
#include <linux/sched.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
@@ -165,21 +166,18 @@ static int parse_reply_info_dir(void **p, void *end,
if (num == 0)
goto done;
- /* alloc large array */
- info->dir_nr = num;
- info->dir_in = kcalloc(num, sizeof(*info->dir_in) +
- sizeof(*info->dir_dname) +
- sizeof(*info->dir_dname_len) +
- sizeof(*info->dir_dlease),
- GFP_NOFS);
- if (info->dir_in == NULL) {
- err = -ENOMEM;
- goto out_bad;
- }
+ BUG_ON(!info->dir_in);
info->dir_dname = (void *)(info->dir_in + num);
info->dir_dname_len = (void *)(info->dir_dname + num);
info->dir_dlease = (void *)(info->dir_dname_len + num);
+ if ((unsigned long)(info->dir_dlease + num) >
+ (unsigned long)info->dir_in + info->dir_buf_size) {
+ pr_err("dir contents are larger than expected\n");
+ WARN_ON(1);
+ goto bad;
+ }
+ info->dir_nr = num;
while (num) {
/* dentry */
ceph_decode_need(p, end, sizeof(u32)*2, bad);
@@ -327,7 +325,9 @@ out_bad:
static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
{
- kfree(info->dir_in);
+ if (!info->dir_in)
+ return;
+ free_pages((unsigned long)info->dir_in, get_order(info->dir_buf_size));
}
@@ -512,12 +512,11 @@ void ceph_mdsc_release_request(struct kref *kref)
struct ceph_mds_request *req = container_of(kref,
struct ceph_mds_request,
r_kref);
+ destroy_reply_info(&req->r_reply_info);
if (req->r_request)
ceph_msg_put(req->r_request);
- if (req->r_reply) {
+ if (req->r_reply)
ceph_msg_put(req->r_reply);
- destroy_reply_info(&req->r_reply_info);
- }
if (req->r_inode) {
ceph_put_cap_refs(ceph_inode(req->r_inode), CEPH_CAP_PIN);
iput(req->r_inode);
@@ -528,7 +527,9 @@ void ceph_mdsc_release_request(struct kref *kref)
iput(req->r_target_inode);
if (req->r_dentry)
dput(req->r_dentry);
- if (req->r_old_dentry) {
+ if (req->r_old_dentry)
+ dput(req->r_old_dentry);
+ if (req->r_old_dentry_dir) {
/*
* track (and drop pins for) r_old_dentry_dir
* separately, since r_old_dentry's d_parent may have
@@ -537,7 +538,6 @@ void ceph_mdsc_release_request(struct kref *kref)
*/
ceph_put_cap_refs(ceph_inode(req->r_old_dentry_dir),
CEPH_CAP_PIN);
- dput(req->r_old_dentry);
iput(req->r_old_dentry_dir);
}
kfree(req->r_path1);
@@ -1311,6 +1311,9 @@ static int trim_caps(struct ceph_mds_client *mdsc,
trim_caps - session->s_trim_caps);
session->s_trim_caps = 0;
}
+
+ ceph_add_cap_releases(mdsc, session);
+ ceph_send_cap_releases(mdsc, session);
return 0;
}
@@ -1461,15 +1464,18 @@ static void discard_cap_releases(struct ceph_mds_client *mdsc,
dout("discard_cap_releases mds%d\n", session->s_mds);
- /* zero out the in-progress message */
- msg = list_first_entry(&session->s_cap_releases,
- struct ceph_msg, list_head);
- head = msg->front.iov_base;
- num = le32_to_cpu(head->num);
- dout("discard_cap_releases mds%d %p %u\n", session->s_mds, msg, num);
- head->num = cpu_to_le32(0);
- msg->front.iov_len = sizeof(*head);
- session->s_num_cap_releases += num;
+ if (!list_empty(&session->s_cap_releases)) {
+ /* zero out the in-progress message */
+ msg = list_first_entry(&session->s_cap_releases,
+ struct ceph_msg, list_head);
+ head = msg->front.iov_base;
+ num = le32_to_cpu(head->num);
+ dout("discard_cap_releases mds%d %p %u\n",
+ session->s_mds, msg, num);
+ head->num = cpu_to_le32(0);
+ msg->front.iov_len = sizeof(*head);
+ session->s_num_cap_releases += num;
+ }
/* requeue completed messages */
while (!list_empty(&session->s_cap_releases_done)) {
@@ -1492,6 +1498,43 @@ static void discard_cap_releases(struct ceph_mds_client *mdsc,
* requests
*/
+int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req,
+ struct inode *dir)
+{
+ struct ceph_inode_info *ci = ceph_inode(dir);
+ struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
+ struct ceph_mount_options *opt = req->r_mdsc->fsc->mount_options;
+ size_t size = sizeof(*rinfo->dir_in) + sizeof(*rinfo->dir_dname_len) +
+ sizeof(*rinfo->dir_dname) + sizeof(*rinfo->dir_dlease);
+ int order, num_entries;
+
+ spin_lock(&ci->i_ceph_lock);
+ num_entries = ci->i_files + ci->i_subdirs;
+ spin_unlock(&ci->i_ceph_lock);
+ num_entries = max(num_entries, 1);
+ num_entries = min(num_entries, opt->max_readdir);
+
+ order = get_order(size * num_entries);
+ while (order >= 0) {
+ rinfo->dir_in = (void*)__get_free_pages(GFP_NOFS | __GFP_NOWARN,
+ order);
+ if (rinfo->dir_in)
+ break;
+ order--;
+ }
+ if (!rinfo->dir_in)
+ return -ENOMEM;
+
+ num_entries = (PAGE_SIZE << order) / size;
+ num_entries = min(num_entries, opt->max_readdir);
+
+ rinfo->dir_buf_size = PAGE_SIZE << order;
+ req->r_num_caps = num_entries + 1;
+ req->r_args.readdir.max_entries = cpu_to_le32(num_entries);
+ req->r_args.readdir.max_bytes = cpu_to_le32(opt->max_readdir_bytes);
+ return 0;
+}
+
/*
* Create an mds request.
*/
@@ -2053,7 +2096,7 @@ int ceph_mdsc_do_request(struct ceph_mds_client *mdsc,
ceph_get_cap_refs(ceph_inode(req->r_inode), CEPH_CAP_PIN);
if (req->r_locked_dir)
ceph_get_cap_refs(ceph_inode(req->r_locked_dir), CEPH_CAP_PIN);
- if (req->r_old_dentry)
+ if (req->r_old_dentry_dir)
ceph_get_cap_refs(ceph_inode(req->r_old_dentry_dir),
CEPH_CAP_PIN);
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index 68288917c737..e90cfccf93bd 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -67,6 +67,7 @@ struct ceph_mds_reply_info_parsed {
/* for readdir results */
struct {
struct ceph_mds_reply_dirfrag *dir_dir;
+ size_t dir_buf_size;
int dir_nr;
char **dir_dname;
u32 *dir_dname_len;
@@ -346,7 +347,8 @@ extern void ceph_mdsc_lease_release(struct ceph_mds_client *mdsc,
struct dentry *dn);
extern void ceph_invalidate_dir_request(struct ceph_mds_request *req);
-
+extern int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req,
+ struct inode *dir);
extern struct ceph_mds_request *
ceph_mdsc_create_request(struct ceph_mds_client *mdsc, int op, int mode);
extern void ceph_mdsc_submit_request(struct ceph_mds_client *mdsc,
diff --git a/fs/ceph/strings.c b/fs/ceph/strings.c
index 4440f447fd3f..51cc23e48111 100644
--- a/fs/ceph/strings.c
+++ b/fs/ceph/strings.c
@@ -54,6 +54,7 @@ const char *ceph_mds_op_name(int op)
case CEPH_MDS_OP_LOOKUPHASH: return "lookuphash";
case CEPH_MDS_OP_LOOKUPPARENT: return "lookupparent";
case CEPH_MDS_OP_LOOKUPINO: return "lookupino";
+ case CEPH_MDS_OP_LOOKUPNAME: return "lookupname";
case CEPH_MDS_OP_GETATTR: return "getattr";
case CEPH_MDS_OP_SETXATTR: return "setxattr";
case CEPH_MDS_OP_SETATTR: return "setattr";
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index 10a4ccbf38da..06150fd745ac 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -1026,6 +1026,7 @@ static int __init init_ceph(void)
if (ret)
goto out;
+ ceph_flock_init();
ceph_xattr_init();
ret = register_filesystem(&ceph_fs_type);
if (ret)
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index d8801a95b685..7866cd05a6bb 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -577,7 +577,7 @@ struct ceph_file_info {
/* readdir: position within a frag */
unsigned offset; /* offset of last chunk, adjusted for . and .. */
- u64 next_offset; /* offset of next chunk (last_name's + 1) */
+ unsigned next_offset; /* offset of next chunk (last_name's + 1) */
char *last_name; /* last entry in previous chunk */
struct dentry *dentry; /* next dentry (for dcache readdir) */
int dir_release_count;
@@ -871,6 +871,7 @@ extern long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
extern const struct export_operations ceph_export_ops;
/* locks.c */
+extern __init void ceph_flock_init(void);
extern int ceph_lock(struct file *file, int cmd, struct file_lock *fl);
extern int ceph_flock(struct file *file, int cmd, struct file_lock *fl);
extern void ceph_count_locks(struct inode *inode, int *p_num, int *f_num);
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index a55ec37378c6..c9c2b887381e 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -64,32 +64,48 @@ static bool ceph_vxattrcb_layout_exists(struct ceph_inode_info *ci)
}
static size_t ceph_vxattrcb_layout(struct ceph_inode_info *ci, char *val,
- size_t size)
+ size_t size)
{
int ret;
struct ceph_fs_client *fsc = ceph_sb_to_client(ci->vfs_inode.i_sb);
struct ceph_osd_client *osdc = &fsc->client->osdc;
s64 pool = ceph_file_layout_pg_pool(ci->i_layout);
const char *pool_name;
+ char buf[128];
dout("ceph_vxattrcb_layout %p\n", &ci->vfs_inode);
down_read(&osdc->map_sem);
pool_name = ceph_pg_pool_name_by_id(osdc->osdmap, pool);
- if (pool_name)
- ret = snprintf(val, size,
- "stripe_unit=%lld stripe_count=%lld object_size=%lld pool=%s",
+ if (pool_name) {
+ size_t len = strlen(pool_name);
+ ret = snprintf(buf, sizeof(buf),
+ "stripe_unit=%lld stripe_count=%lld object_size=%lld pool=",
(unsigned long long)ceph_file_layout_su(ci->i_layout),
(unsigned long long)ceph_file_layout_stripe_count(ci->i_layout),
- (unsigned long long)ceph_file_layout_object_size(ci->i_layout),
- pool_name);
- else
- ret = snprintf(val, size,
+ (unsigned long long)ceph_file_layout_object_size(ci->i_layout));
+ if (!size) {
+ ret += len;
+ } else if (ret + len > size) {
+ ret = -ERANGE;
+ } else {
+ memcpy(val, buf, ret);
+ memcpy(val + ret, pool_name, len);
+ ret += len;
+ }
+ } else {
+ ret = snprintf(buf, sizeof(buf),
"stripe_unit=%lld stripe_count=%lld object_size=%lld pool=%lld",
(unsigned long long)ceph_file_layout_su(ci->i_layout),
(unsigned long long)ceph_file_layout_stripe_count(ci->i_layout),
(unsigned long long)ceph_file_layout_object_size(ci->i_layout),
(unsigned long long)pool);
-
+ if (size) {
+ if (ret <= size)
+ memcpy(val, buf, ret);
+ else
+ ret = -ERANGE;
+ }
+ }
up_read(&osdc->map_sem);
return ret;
}
@@ -215,7 +231,7 @@ static struct ceph_vxattr ceph_dir_vxattrs[] = {
.name_size = sizeof("ceph.dir.layout"),
.getxattr_cb = ceph_vxattrcb_layout,
.readonly = false,
- .hidden = false,
+ .hidden = true,
.exists_cb = ceph_vxattrcb_layout_exists,
},
XATTR_LAYOUT_FIELD(dir, layout, stripe_unit),
@@ -242,7 +258,7 @@ static struct ceph_vxattr ceph_file_vxattrs[] = {
.name_size = sizeof("ceph.file.layout"),
.getxattr_cb = ceph_vxattrcb_layout,
.readonly = false,
- .hidden = false,
+ .hidden = true,
.exists_cb = ceph_vxattrcb_layout_exists,
},
XATTR_LAYOUT_FIELD(file, layout, stripe_unit),
@@ -842,7 +858,6 @@ static int ceph_sync_setxattr(struct dentry *dentry, const char *name,
struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb);
struct inode *inode = dentry->d_inode;
struct ceph_inode_info *ci = ceph_inode(inode);
- struct inode *parent_inode;
struct ceph_mds_request *req;
struct ceph_mds_client *mdsc = fsc->mdsc;
int err;
@@ -893,9 +908,7 @@ static int ceph_sync_setxattr(struct dentry *dentry, const char *name,
req->r_data_len = size;
dout("xattr.ver (before): %lld\n", ci->i_xattrs.version);
- parent_inode = ceph_get_dentry_parent_inode(dentry);
- err = ceph_mdsc_do_request(mdsc, parent_inode, req);
- iput(parent_inode);
+ err = ceph_mdsc_do_request(mdsc, NULL, req);
ceph_mdsc_put_request(req);
dout("xattr.ver (after): %lld\n", ci->i_xattrs.version);
@@ -1019,7 +1032,6 @@ static int ceph_send_removexattr(struct dentry *dentry, const char *name)
struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb);
struct ceph_mds_client *mdsc = fsc->mdsc;
struct inode *inode = dentry->d_inode;
- struct inode *parent_inode;
struct ceph_mds_request *req;
int err;
@@ -1033,9 +1045,7 @@ static int ceph_send_removexattr(struct dentry *dentry, const char *name)
req->r_num_caps = 1;
req->r_path2 = kstrdup(name, GFP_NOFS);
- parent_inode = ceph_get_dentry_parent_inode(dentry);
- err = ceph_mdsc_do_request(mdsc, parent_inode, req);
- iput(parent_inode);
+ err = ceph_mdsc_do_request(mdsc, NULL, req);
ceph_mdsc_put_request(req);
return err;
}
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 849f6132b327..2c70cbe35d39 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -286,7 +286,7 @@ cifs_destroy_inode(struct inode *inode)
static void
cifs_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
cifs_fscache_release_inode_cookie(inode);
}
@@ -541,6 +541,7 @@ static int cifs_show_stats(struct seq_file *s, struct dentry *root)
static int cifs_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
*flags |= MS_NODIRATIME;
return 0;
}
@@ -1005,7 +1006,7 @@ cifs_init_once(void *inode)
init_rwsem(&cifsi->lock_sem);
}
-static int
+static int __init
cifs_init_inodecache(void)
{
cifs_inode_cachep = kmem_cache_create("cifs_inode_cache",
diff --git a/fs/coda/coda_int.h b/fs/coda/coda_int.h
index b7143cf783ac..381c993b1427 100644
--- a/fs/coda/coda_int.h
+++ b/fs/coda/coda_int.h
@@ -10,7 +10,7 @@ extern int coda_hard;
extern int coda_fake_statfs;
void coda_destroy_inodecache(void);
-int coda_init_inodecache(void);
+int __init coda_init_inodecache(void);
int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync);
void coda_sysctl_init(void);
void coda_sysctl_clean(void);
diff --git a/fs/coda/inode.c b/fs/coda/inode.c
index 506de34a4ef3..d9c7751f10ac 100644
--- a/fs/coda/inode.c
+++ b/fs/coda/inode.c
@@ -73,7 +73,7 @@ static void init_once(void *foo)
inode_init_once(&ei->vfs_inode);
}
-int coda_init_inodecache(void)
+int __init coda_init_inodecache(void)
{
coda_inode_cachep = kmem_cache_create("coda_inode_cache",
sizeof(struct coda_inode_info),
@@ -96,6 +96,7 @@ void coda_destroy_inodecache(void)
static int coda_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
*flags |= MS_NOATIME;
return 0;
}
@@ -250,7 +251,7 @@ static void coda_put_super(struct super_block *sb)
static void coda_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
coda_cache_clear_inode(inode);
}
diff --git a/fs/compat.c b/fs/compat.c
index f86df85dff61..ca926ad0430c 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -399,12 +399,28 @@ static int put_compat_flock64(struct flock *kfl, struct compat_flock64 __user *u
}
#endif
+static unsigned int
+convert_fcntl_cmd(unsigned int cmd)
+{
+ switch (cmd) {
+ case F_GETLK64:
+ return F_GETLK;
+ case F_SETLK64:
+ return F_SETLK;
+ case F_SETLKW64:
+ return F_SETLKW;
+ }
+
+ return cmd;
+}
+
COMPAT_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
compat_ulong_t, arg)
{
mm_segment_t old_fs;
struct flock f;
long ret;
+ unsigned int conv_cmd;
switch (cmd) {
case F_GETLK:
@@ -441,16 +457,18 @@ COMPAT_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
case F_GETLK64:
case F_SETLK64:
case F_SETLKW64:
+ case F_GETLKP:
+ case F_SETLKP:
+ case F_SETLKPW:
ret = get_compat_flock64(&f, compat_ptr(arg));
if (ret != 0)
break;
old_fs = get_fs();
set_fs(KERNEL_DS);
- ret = sys_fcntl(fd, (cmd == F_GETLK64) ? F_GETLK :
- ((cmd == F_SETLK64) ? F_SETLK : F_SETLKW),
- (unsigned long)&f);
+ conv_cmd = convert_fcntl_cmd(cmd);
+ ret = sys_fcntl(fd, conv_cmd, (unsigned long)&f);
set_fs(old_fs);
- if (cmd == F_GETLK64 && ret == 0) {
+ if ((conv_cmd == F_GETLK || conv_cmd == F_GETLKP) && ret == 0) {
/* need to return lock information - see above for commentary */
if (f.l_start > COMPAT_LOFF_T_MAX)
ret = -EOVERFLOW;
@@ -471,8 +489,15 @@ COMPAT_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
COMPAT_SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd,
compat_ulong_t, arg)
{
- if ((cmd == F_GETLK64) || (cmd == F_SETLK64) || (cmd == F_SETLKW64))
+ switch (cmd) {
+ case F_GETLK64:
+ case F_SETLK64:
+ case F_SETLKW64:
+ case F_GETLKP:
+ case F_SETLKP:
+ case F_SETLKPW:
return -EINVAL;
+ }
return compat_sys_fcntl64(fd, cmd, arg);
}
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 06610cf94d57..ddcfe590b8a8 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -195,8 +195,7 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i
struct page *page = NULL;
if (blocknr + i < devsize) {
- page = read_mapping_page_async(mapping, blocknr + i,
- NULL);
+ page = read_mapping_page(mapping, blocknr + i, NULL);
/* synchronous error? */
if (IS_ERR(page))
page = NULL;
@@ -244,6 +243,7 @@ static void cramfs_kill_sb(struct super_block *sb)
static int cramfs_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
*flags |= MS_RDONLY;
return 0;
}
diff --git a/fs/dcache.c b/fs/dcache.c
index ca02c13a84aa..66cba5a8a346 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2483,12 +2483,14 @@ static void switch_names(struct dentry *dentry, struct dentry *target)
dentry->d_name.name = dentry->d_iname;
} else {
/*
- * Both are internal. Just copy target to dentry
+ * Both are internal.
*/
- memcpy(dentry->d_iname, target->d_name.name,
- target->d_name.len + 1);
- dentry->d_name.len = target->d_name.len;
- return;
+ unsigned int i;
+ BUILD_BUG_ON(!IS_ALIGNED(DNAME_INLINE_LEN, sizeof(long)));
+ for (i = 0; i < DNAME_INLINE_LEN / sizeof(long); i++) {
+ swap(((long *) &dentry->d_iname)[i],
+ ((long *) &target->d_iname)[i]);
+ }
}
}
swap(dentry->d_name.len, target->d_name.len);
@@ -2545,13 +2547,15 @@ static void dentry_unlock_parents_for_move(struct dentry *dentry,
* __d_move - move a dentry
* @dentry: entry to move
* @target: new dentry
+ * @exchange: exchange the two dentries
*
* Update the dcache to reflect the move of a file name. Negative
* dcache entries should not be moved in this way. Caller must hold
* rename_lock, the i_mutex of the source and target directories,
* and the sb->s_vfs_rename_mutex if they differ. See lock_rename().
*/
-static void __d_move(struct dentry * dentry, struct dentry * target)
+static void __d_move(struct dentry *dentry, struct dentry *target,
+ bool exchange)
{
if (!dentry->d_inode)
printk(KERN_WARNING "VFS: moving negative dcache entry\n");
@@ -2573,8 +2577,15 @@ static void __d_move(struct dentry * dentry, struct dentry * target)
__d_drop(dentry);
__d_rehash(dentry, d_hash(target->d_parent, target->d_name.hash));
- /* Unhash the target: dput() will then get rid of it */
+ /*
+ * Unhash the target (d_delete() is not usable here). If exchanging
+ * the two dentries, then rehash onto the other's hash queue.
+ */
__d_drop(target);
+ if (exchange) {
+ __d_rehash(target,
+ d_hash(dentry->d_parent, dentry->d_name.hash));
+ }
list_del(&dentry->d_u.d_child);
list_del(&target->d_u.d_child);
@@ -2601,6 +2612,8 @@ static void __d_move(struct dentry * dentry, struct dentry * target)
write_seqcount_end(&dentry->d_seq);
dentry_unlock_parents_for_move(dentry, target);
+ if (exchange)
+ fsnotify_d_move(target);
spin_unlock(&target->d_lock);
fsnotify_d_move(dentry);
spin_unlock(&dentry->d_lock);
@@ -2618,11 +2631,30 @@ static void __d_move(struct dentry * dentry, struct dentry * target)
void d_move(struct dentry *dentry, struct dentry *target)
{
write_seqlock(&rename_lock);
- __d_move(dentry, target);
+ __d_move(dentry, target, false);
write_sequnlock(&rename_lock);
}
EXPORT_SYMBOL(d_move);
+/*
+ * d_exchange - exchange two dentries
+ * @dentry1: first dentry
+ * @dentry2: second dentry
+ */
+void d_exchange(struct dentry *dentry1, struct dentry *dentry2)
+{
+ write_seqlock(&rename_lock);
+
+ WARN_ON(!dentry1->d_inode);
+ WARN_ON(!dentry2->d_inode);
+ WARN_ON(IS_ROOT(dentry1));
+ WARN_ON(IS_ROOT(dentry2));
+
+ __d_move(dentry1, dentry2, true);
+
+ write_sequnlock(&rename_lock);
+}
+
/**
* d_ancestor - search for an ancestor
* @p1: ancestor dentry
@@ -2670,7 +2702,7 @@ static struct dentry *__d_unalias(struct inode *inode,
m2 = &alias->d_parent->d_inode->i_mutex;
out_unalias:
if (likely(!d_mountpoint(alias))) {
- __d_move(alias, dentry);
+ __d_move(alias, dentry, false);
ret = alias;
}
out_err:
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 9c0444cccbe1..8c41b52da358 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -218,6 +218,7 @@ static int debugfs_remount(struct super_block *sb, int *flags, char *data)
int err;
struct debugfs_fs_info *fsi = sb->s_fs_info;
+ sync_filesystem(sb);
err = debugfs_parse_options(data, &fsi->mount_opts);
if (err)
goto fail;
@@ -358,7 +359,7 @@ exit:
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have.
* @parent: a pointer to the parent dentry for this file. This should be a
- * directory dentry if set. If this paramater is NULL, then the
+ * directory dentry if set. If this parameter is NULL, then the
* file will be created in the root of the debugfs filesystem.
* @data: a pointer to something that the caller will want to get to later
* on. The inode.i_private pointer will point to this value on
@@ -400,7 +401,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_file);
* @name: a pointer to a string containing the name of the directory to
* create.
* @parent: a pointer to the parent dentry for this file. This should be a
- * directory dentry if set. If this paramater is NULL, then the
+ * directory dentry if set. If this parameter is NULL, then the
* directory will be created in the root of the debugfs filesystem.
*
* This function creates a directory in debugfs with the given name.
@@ -425,7 +426,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_dir);
* @name: a pointer to a string containing the name of the symbolic link to
* create.
* @parent: a pointer to the parent dentry for this symbolic link. This
- * should be a directory dentry if set. If this paramater is NULL,
+ * should be a directory dentry if set. If this parameter is NULL,
* then the symbolic link will be created in the root of the debugfs
* filesystem.
* @target: a pointer to a string containing the path to the target of the
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index a726b9f29cb7..c71038079b47 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -313,6 +313,7 @@ static int devpts_remount(struct super_block *sb, int *flags, char *data)
struct pts_fs_info *fsi = DEVPTS_SB(sb);
struct pts_mount_opts *opts = &fsi->mount_opts;
+ sync_filesystem(sb);
err = parse_mount_options(data, PARSE_REMOUNT, opts);
/*
diff --git a/fs/direct-io.c b/fs/direct-io.c
index 160a5489a939..31ba0935e32e 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -664,7 +664,6 @@ static inline int dio_new_bio(struct dio *dio, struct dio_submit *sdio,
goto out;
sector = start_sector << (sdio->blkbits - 9);
nr_pages = min(sdio->pages_in_io, bio_get_nr_vecs(map_bh->b_bdev));
- nr_pages = min(nr_pages, BIO_MAX_PAGES);
BUG_ON(nr_pages <= 0);
dio_bio_alloc(dio, sdio, map_bh->b_bdev, sector, nr_pages);
sdio->boundary = 0;
@@ -1194,13 +1193,19 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
}
/*
- * For file extending writes updating i_size before data
- * writeouts complete can expose uninitialized blocks. So
- * even for AIO, we need to wait for i/o to complete before
- * returning in this case.
+ * For file extending writes updating i_size before data writeouts
+ * complete can expose uninitialized blocks in dumb filesystems.
+ * In that case we need to wait for I/O completion even if asked
+ * for an asynchronous write.
*/
- dio->is_async = !is_sync_kiocb(iocb) && !((rw & WRITE) &&
- (end > i_size_read(inode)));
+ if (is_sync_kiocb(iocb))
+ dio->is_async = false;
+ else if (!(dio->flags & DIO_ASYNC_EXTEND) &&
+ (rw & WRITE) && end > i_size_read(inode))
+ dio->is_async = false;
+ else
+ dio->is_async = true;
+
dio->inode = inode;
dio->rw = rw;
diff --git a/fs/dlm/ast.c b/fs/dlm/ast.c
index 0e90f0c91b93..dcea1e37a1b7 100644
--- a/fs/dlm/ast.c
+++ b/fs/dlm/ast.c
@@ -14,6 +14,7 @@
#include "dlm_internal.h"
#include "lock.h"
#include "user.h"
+#include "ast.h"
static uint64_t dlm_cb_seq;
static DEFINE_SPINLOCK(dlm_cb_seq_spin);
@@ -308,6 +309,6 @@ void dlm_callback_resume(struct dlm_ls *ls)
mutex_unlock(&ls->ls_cb_mutex);
if (count)
- log_debug(ls, "dlm_callback_resume %d", count);
+ log_rinfo(ls, "dlm_callback_resume %d", count);
}
diff --git a/fs/dlm/dir.c b/fs/dlm/dir.c
index 278a75cda446..d975851a7e1e 100644
--- a/fs/dlm/dir.c
+++ b/fs/dlm/dir.c
@@ -68,7 +68,7 @@ int dlm_recover_directory(struct dlm_ls *ls)
uint16_t namelen;
unsigned int count = 0, count_match = 0, count_bad = 0, count_add = 0;
- log_debug(ls, "dlm_recover_directory");
+ log_rinfo(ls, "dlm_recover_directory");
if (dlm_no_directory(ls))
goto out_status;
@@ -189,7 +189,7 @@ int dlm_recover_directory(struct dlm_ls *ls)
error = 0;
dlm_set_recover_status(ls, DLM_RS_DIR);
- log_debug(ls, "dlm_recover_directory %u in %u new",
+ log_rinfo(ls, "dlm_recover_directory %u in %u new",
count, count_add);
out_free:
kfree(last_name);
diff --git a/fs/dlm/dlm_internal.h b/fs/dlm/dlm_internal.h
index e7665c31f7b1..5eff6ea3e27f 100644
--- a/fs/dlm/dlm_internal.h
+++ b/fs/dlm/dlm_internal.h
@@ -65,6 +65,8 @@ struct dlm_mhandle;
printk(KERN_ERR "dlm: "fmt"\n" , ##args)
#define log_error(ls, fmt, args...) \
printk(KERN_ERR "dlm: %s: " fmt "\n", (ls)->ls_name , ##args)
+#define log_rinfo(ls, fmt, args...) \
+ printk(KERN_INFO "dlm: %s: " fmt "\n", (ls)->ls_name , ##args);
#define log_debug(ls, fmt, args...) \
do { \
diff --git a/fs/dlm/lock.c b/fs/dlm/lock.c
index e223a911a834..83f3d5520307 100644
--- a/fs/dlm/lock.c
+++ b/fs/dlm/lock.c
@@ -687,6 +687,7 @@ static int find_rsb_dir(struct dlm_ls *ls, char *name, int len,
log_error(ls, "find_rsb new from_other %d dir %d our %d %s",
from_nodeid, dir_nodeid, our_nodeid, r->res_name);
dlm_free_rsb(r);
+ r = NULL;
error = -ENOTBLK;
goto out_unlock;
}
@@ -5462,7 +5463,7 @@ void dlm_recover_purge(struct dlm_ls *ls)
up_write(&ls->ls_root_sem);
if (lkb_count)
- log_debug(ls, "dlm_recover_purge %u locks for %u nodes",
+ log_rinfo(ls, "dlm_recover_purge %u locks for %u nodes",
lkb_count, nodes_count);
}
@@ -5536,7 +5537,7 @@ void dlm_recover_grant(struct dlm_ls *ls)
}
if (lkb_count)
- log_debug(ls, "dlm_recover_grant %u locks on %u resources",
+ log_rinfo(ls, "dlm_recover_grant %u locks on %u resources",
lkb_count, rsb_count);
}
@@ -5695,7 +5696,7 @@ int dlm_recover_master_copy(struct dlm_ls *ls, struct dlm_rcom *rc)
put_rsb(r);
out:
if (error && error != -EEXIST)
- log_debug(ls, "dlm_recover_master_copy remote %d %x error %d",
+ log_rinfo(ls, "dlm_recover_master_copy remote %d %x error %d",
from_nodeid, remid, error);
rl->rl_result = cpu_to_le32(error);
return error;
diff --git a/fs/dlm/lockspace.c b/fs/dlm/lockspace.c
index d5abafd56a6d..04d6398c1f1c 100644
--- a/fs/dlm/lockspace.c
+++ b/fs/dlm/lockspace.c
@@ -190,7 +190,7 @@ static int do_uevent(struct dlm_ls *ls, int in)
else
kobject_uevent(&ls->ls_kobj, KOBJ_OFFLINE);
- log_debug(ls, "%s the lockspace group...", in ? "joining" : "leaving");
+ log_rinfo(ls, "%s the lockspace group...", in ? "joining" : "leaving");
/* dlm_controld will see the uevent, do the necessary group management
and then write to sysfs to wake us */
@@ -198,7 +198,7 @@ static int do_uevent(struct dlm_ls *ls, int in)
error = wait_event_interruptible(ls->ls_uevent_wait,
test_and_clear_bit(LSFL_UEVENT_WAIT, &ls->ls_flags));
- log_debug(ls, "group event done %d %d", error, ls->ls_uevent_result);
+ log_rinfo(ls, "group event done %d %d", error, ls->ls_uevent_result);
if (error)
goto out;
@@ -640,7 +640,7 @@ static int new_lockspace(const char *name, const char *cluster,
dlm_create_debug_file(ls);
- log_debug(ls, "join complete");
+ log_rinfo(ls, "join complete");
*lockspace = ls;
return 0;
@@ -835,7 +835,7 @@ static int release_lockspace(struct dlm_ls *ls, int force)
dlm_clear_members(ls);
dlm_clear_members_gone(ls);
kfree(ls->ls_node_array);
- log_debug(ls, "release_lockspace final free");
+ log_rinfo(ls, "release_lockspace final free");
kobject_put(&ls->ls_kobj);
/* The ls structure will be freed when the kobject is done with */
diff --git a/fs/dlm/member.c b/fs/dlm/member.c
index 476557b54921..9c47f1c14a8b 100644
--- a/fs/dlm/member.c
+++ b/fs/dlm/member.c
@@ -60,18 +60,15 @@ void dlm_slots_copy_out(struct dlm_ls *ls, struct dlm_rcom *rc)
#define SLOT_DEBUG_LINE 128
-static void log_debug_slots(struct dlm_ls *ls, uint32_t gen, int num_slots,
- struct rcom_slot *ro0, struct dlm_slot *array,
- int array_size)
+static void log_slots(struct dlm_ls *ls, uint32_t gen, int num_slots,
+ struct rcom_slot *ro0, struct dlm_slot *array,
+ int array_size)
{
char line[SLOT_DEBUG_LINE];
int len = SLOT_DEBUG_LINE - 1;
int pos = 0;
int ret, i;
- if (!dlm_config.ci_log_debug)
- return;
-
memset(line, 0, sizeof(line));
if (array) {
@@ -95,7 +92,7 @@ static void log_debug_slots(struct dlm_ls *ls, uint32_t gen, int num_slots,
}
}
- log_debug(ls, "generation %u slots %d%s", gen, num_slots, line);
+ log_rinfo(ls, "generation %u slots %d%s", gen, num_slots, line);
}
int dlm_slots_copy_in(struct dlm_ls *ls)
@@ -129,7 +126,7 @@ int dlm_slots_copy_in(struct dlm_ls *ls)
ro->ro_slot = le16_to_cpu(ro->ro_slot);
}
- log_debug_slots(ls, gen, num_slots, ro0, NULL, 0);
+ log_slots(ls, gen, num_slots, ro0, NULL, 0);
list_for_each_entry(memb, &ls->ls_nodes, list) {
for (i = 0, ro = ro0; i < num_slots; i++, ro++) {
@@ -274,7 +271,7 @@ int dlm_slots_assign(struct dlm_ls *ls, int *num_slots, int *slots_size,
gen++;
- log_debug_slots(ls, gen, num, NULL, array, array_size);
+ log_slots(ls, gen, num, NULL, array, array_size);
max_slots = (dlm_config.ci_buffer_size - sizeof(struct dlm_rcom) -
sizeof(struct rcom_config)) / sizeof(struct rcom_slot);
@@ -447,7 +444,7 @@ static int ping_members(struct dlm_ls *ls)
break;
}
if (error)
- log_debug(ls, "ping_members aborted %d last nodeid %d",
+ log_rinfo(ls, "ping_members aborted %d last nodeid %d",
error, ls->ls_recover_nodeid);
return error;
}
@@ -539,7 +536,7 @@ int dlm_recover_members(struct dlm_ls *ls, struct dlm_recover *rv, int *neg_out)
count as a negative change so the "neg" recovery steps will happen */
list_for_each_entry(memb, &ls->ls_nodes_gone, list) {
- log_debug(ls, "prev removed member %d", memb->nodeid);
+ log_rinfo(ls, "prev removed member %d", memb->nodeid);
neg++;
}
@@ -551,10 +548,10 @@ int dlm_recover_members(struct dlm_ls *ls, struct dlm_recover *rv, int *neg_out)
continue;
if (!node) {
- log_debug(ls, "remove member %d", memb->nodeid);
+ log_rinfo(ls, "remove member %d", memb->nodeid);
} else {
/* removed and re-added */
- log_debug(ls, "remove member %d comm_seq %u %u",
+ log_rinfo(ls, "remove member %d comm_seq %u %u",
memb->nodeid, memb->comm_seq, node->comm_seq);
}
@@ -571,7 +568,7 @@ int dlm_recover_members(struct dlm_ls *ls, struct dlm_recover *rv, int *neg_out)
if (dlm_is_member(ls, node->nodeid))
continue;
dlm_add_member(ls, node);
- log_debug(ls, "add member %d", node->nodeid);
+ log_rinfo(ls, "add member %d", node->nodeid);
}
list_for_each_entry(memb, &ls->ls_nodes, list) {
@@ -591,7 +588,7 @@ int dlm_recover_members(struct dlm_ls *ls, struct dlm_recover *rv, int *neg_out)
complete(&ls->ls_members_done);
}
- log_debug(ls, "dlm_recover_members %d nodes", ls->ls_num_nodes);
+ log_rinfo(ls, "dlm_recover_members %d nodes", ls->ls_num_nodes);
return error;
}
diff --git a/fs/dlm/recover.c b/fs/dlm/recover.c
index a6bc63f6e31b..eaea789bf97d 100644
--- a/fs/dlm/recover.c
+++ b/fs/dlm/recover.c
@@ -526,7 +526,7 @@ int dlm_recover_masters(struct dlm_ls *ls)
int nodir = dlm_no_directory(ls);
int error;
- log_debug(ls, "dlm_recover_masters");
+ log_rinfo(ls, "dlm_recover_masters");
down_read(&ls->ls_root_sem);
list_for_each_entry(r, &ls->ls_root_list, res_root_list) {
@@ -552,7 +552,7 @@ int dlm_recover_masters(struct dlm_ls *ls)
}
up_read(&ls->ls_root_sem);
- log_debug(ls, "dlm_recover_masters %u of %u", count, total);
+ log_rinfo(ls, "dlm_recover_masters %u of %u", count, total);
error = dlm_wait_function(ls, &recover_idr_empty);
out:
@@ -685,7 +685,7 @@ int dlm_recover_locks(struct dlm_ls *ls)
}
up_read(&ls->ls_root_sem);
- log_debug(ls, "dlm_recover_locks %d out", count);
+ log_rinfo(ls, "dlm_recover_locks %d out", count);
error = dlm_wait_function(ls, &recover_list_empty);
out:
@@ -883,7 +883,7 @@ void dlm_recover_rsbs(struct dlm_ls *ls)
up_read(&ls->ls_root_sem);
if (count)
- log_debug(ls, "dlm_recover_rsbs %d done", count);
+ log_rinfo(ls, "dlm_recover_rsbs %d done", count);
}
/* Create a single list of all root rsb's to be used during recovery */
@@ -950,6 +950,6 @@ void dlm_clear_toss(struct dlm_ls *ls)
}
if (count)
- log_debug(ls, "dlm_clear_toss %u done", count);
+ log_rinfo(ls, "dlm_clear_toss %u done", count);
}
diff --git a/fs/dlm/recoverd.c b/fs/dlm/recoverd.c
index 32f9f8926ec3..6859b4bf971e 100644
--- a/fs/dlm/recoverd.c
+++ b/fs/dlm/recoverd.c
@@ -55,7 +55,7 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
unsigned long start;
int error, neg = 0;
- log_debug(ls, "dlm_recover %llu", (unsigned long long)rv->seq);
+ log_rinfo(ls, "dlm_recover %llu", (unsigned long long)rv->seq);
mutex_lock(&ls->ls_recoverd_active);
@@ -76,7 +76,7 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
error = dlm_recover_members(ls, rv, &neg);
if (error) {
- log_debug(ls, "dlm_recover_members error %d", error);
+ log_rinfo(ls, "dlm_recover_members error %d", error);
goto fail;
}
@@ -90,7 +90,7 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
error = dlm_recover_members_wait(ls);
if (error) {
- log_debug(ls, "dlm_recover_members_wait error %d", error);
+ log_rinfo(ls, "dlm_recover_members_wait error %d", error);
goto fail;
}
@@ -103,7 +103,7 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
error = dlm_recover_directory(ls);
if (error) {
- log_debug(ls, "dlm_recover_directory error %d", error);
+ log_rinfo(ls, "dlm_recover_directory error %d", error);
goto fail;
}
@@ -111,11 +111,11 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
error = dlm_recover_directory_wait(ls);
if (error) {
- log_debug(ls, "dlm_recover_directory_wait error %d", error);
+ log_rinfo(ls, "dlm_recover_directory_wait error %d", error);
goto fail;
}
- log_debug(ls, "dlm_recover_directory %u out %u messages",
+ log_rinfo(ls, "dlm_recover_directory %u out %u messages",
ls->ls_recover_dir_sent_res, ls->ls_recover_dir_sent_msg);
/*
@@ -144,7 +144,7 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
error = dlm_recover_masters(ls);
if (error) {
- log_debug(ls, "dlm_recover_masters error %d", error);
+ log_rinfo(ls, "dlm_recover_masters error %d", error);
goto fail;
}
@@ -154,7 +154,7 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
error = dlm_recover_locks(ls);
if (error) {
- log_debug(ls, "dlm_recover_locks error %d", error);
+ log_rinfo(ls, "dlm_recover_locks error %d", error);
goto fail;
}
@@ -162,11 +162,11 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
error = dlm_recover_locks_wait(ls);
if (error) {
- log_debug(ls, "dlm_recover_locks_wait error %d", error);
+ log_rinfo(ls, "dlm_recover_locks_wait error %d", error);
goto fail;
}
- log_debug(ls, "dlm_recover_locks %u in",
+ log_rinfo(ls, "dlm_recover_locks %u in",
ls->ls_recover_locks_in);
/*
@@ -186,7 +186,7 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
error = dlm_recover_locks_wait(ls);
if (error) {
- log_debug(ls, "dlm_recover_locks_wait error %d", error);
+ log_rinfo(ls, "dlm_recover_locks_wait error %d", error);
goto fail;
}
}
@@ -205,7 +205,7 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
error = dlm_recover_done_wait(ls);
if (error) {
- log_debug(ls, "dlm_recover_done_wait error %d", error);
+ log_rinfo(ls, "dlm_recover_done_wait error %d", error);
goto fail;
}
@@ -217,25 +217,25 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
error = enable_locking(ls, rv->seq);
if (error) {
- log_debug(ls, "enable_locking error %d", error);
+ log_rinfo(ls, "enable_locking error %d", error);
goto fail;
}
error = dlm_process_requestqueue(ls);
if (error) {
- log_debug(ls, "dlm_process_requestqueue error %d", error);
+ log_rinfo(ls, "dlm_process_requestqueue error %d", error);
goto fail;
}
error = dlm_recover_waiters_post(ls);
if (error) {
- log_debug(ls, "dlm_recover_waiters_post error %d", error);
+ log_rinfo(ls, "dlm_recover_waiters_post error %d", error);
goto fail;
}
dlm_recover_grant(ls);
- log_debug(ls, "dlm_recover %llu generation %u done: %u ms",
+ log_rinfo(ls, "dlm_recover %llu generation %u done: %u ms",
(unsigned long long)rv->seq, ls->ls_generation,
jiffies_to_msecs(jiffies - start));
mutex_unlock(&ls->ls_recoverd_active);
@@ -245,7 +245,7 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
fail:
dlm_release_root_list(ls);
- log_debug(ls, "dlm_recover %llu error %d",
+ log_rinfo(ls, "dlm_recover %llu error %d",
(unsigned long long)rv->seq, error);
mutex_unlock(&ls->ls_recoverd_active);
return error;
diff --git a/fs/drop_caches.c b/fs/drop_caches.c
index 9fd702f5bfb2..9280202e488c 100644
--- a/fs/drop_caches.c
+++ b/fs/drop_caches.c
@@ -59,10 +59,22 @@ int drop_caches_sysctl_handler(ctl_table *table, int write,
if (ret)
return ret;
if (write) {
- if (sysctl_drop_caches & 1)
+ static int stfu;
+
+ if (sysctl_drop_caches & 1) {
iterate_supers(drop_pagecache_sb, NULL);
- if (sysctl_drop_caches & 2)
+ count_vm_event(DROP_PAGECACHE);
+ }
+ if (sysctl_drop_caches & 2) {
drop_slab();
+ count_vm_event(DROP_SLAB);
+ }
+ if (!stfu) {
+ pr_info("%s (%d): drop_caches: %d\n",
+ current->comm, task_pid_nr(current),
+ sysctl_drop_caches);
+ }
+ stfu |= sysctl_drop_caches & 4;
}
return 0;
}
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index b167ca48b8ee..d4a9431ec73c 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -641,7 +641,7 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
}
rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
lower_new_dir_dentry->d_inode, lower_new_dentry,
- NULL);
+ NULL, 0);
if (rc)
goto out_lock;
if (target_inode)
diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c
index e879cf8ff0b1..afa1b81c3418 100644
--- a/fs/ecryptfs/super.c
+++ b/fs/ecryptfs/super.c
@@ -132,7 +132,7 @@ static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf)
*/
static void ecryptfs_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
iput(ecryptfs_inode_to_lower(inode));
}
diff --git a/fs/efs/super.c b/fs/efs/super.c
index 50215bbd6463..3befcc9f5d63 100644
--- a/fs/efs/super.c
+++ b/fs/efs/super.c
@@ -91,7 +91,7 @@ static void init_once(void *foo)
inode_init_once(&ei->vfs_inode);
}
-static int init_inodecache(void)
+static int __init init_inodecache(void)
{
efs_inode_cachep = kmem_cache_create("efs_inode_cache",
sizeof(struct efs_inode_info),
@@ -114,6 +114,7 @@ static void destroy_inodecache(void)
static int efs_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
*flags |= MS_RDONLY;
return 0;
}
diff --git a/fs/exec.c b/fs/exec.c
index 4f59402fdda5..25dfeba6d55f 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -97,6 +97,7 @@ static inline void put_binfmt(struct linux_binfmt * fmt)
module_put(fmt->module);
}
+#ifdef CONFIG_USELIB
/*
* Note that a shared library must be both readable and executable due to
* security reasons.
@@ -156,6 +157,7 @@ exit:
out:
return error;
}
+#endif /* #ifdef CONFIG_USELIB */
#ifdef CONFIG_MMU
/*
diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
index ee4317faccb1..d1c244d67667 100644
--- a/fs/exofs/inode.c
+++ b/fs/exofs/inode.c
@@ -1486,7 +1486,7 @@ void exofs_evict_inode(struct inode *inode)
struct ore_io_state *ios;
int ret;
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
/* TODO: should do better here */
if (inode->i_nlink || is_bad_inode(inode))
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 94ed36849b71..b1d2a4675d42 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -78,7 +78,7 @@ void ext2_evict_inode(struct inode * inode)
dquot_drop(inode);
}
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
if (want_delete) {
sb_start_intwrite(inode->i_sb);
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 20d6697bd638..d260115c0350 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -1254,6 +1254,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
unsigned long old_sb_flags;
int err;
+ sync_filesystem(sb);
spin_lock(&sbi->s_lock);
/* Store the old options */
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 384b6ebb655f..efce2bbfb5e5 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -228,7 +228,7 @@ void ext3_evict_inode (struct inode *inode)
log_wait_commit(journal, commit_tid);
filemap_write_and_wait(&inode->i_data);
}
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
ext3_discard_reservation(inode);
rsv = ei->i_block_alloc_info;
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index 37fd31ed16e7..95c6c5a6d0c5 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -2649,6 +2649,8 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
int i;
#endif
+ sync_filesystem(sb);
+
/* Store the original options */
old_sb_flags = sb->s_flags;
old_opts.s_mount_opt = sbi->s_mount_opt;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index d3a534fdc5ff..f1c65dc7cc0a 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -31,6 +31,7 @@
#include <linux/percpu_counter.h>
#include <linux/ratelimit.h>
#include <crypto/hash.h>
+#include <linux/falloc.h>
#ifdef __KERNEL__
#include <linux/compat.h>
#endif
@@ -567,6 +568,8 @@ enum {
#define EXT4_GET_BLOCKS_NO_LOCK 0x0100
/* Do not put hole in extent cache */
#define EXT4_GET_BLOCKS_NO_PUT_HOLE 0x0200
+ /* Convert written extents to unwritten */
+#define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN 0x0400
/*
* The bit position of these flags must not overlap with any of the
@@ -998,6 +1001,8 @@ struct ext4_inode_info {
#define EXT4_MOUNT2_STD_GROUP_SIZE 0x00000002 /* We have standard group
size of blocksize * 8
blocks */
+#define EXT4_MOUNT2_HURD_COMPAT 0x00000004 /* Support HURD-castrated
+ file systems */
#define clear_opt(sb, opt) EXT4_SB(sb)->s_mount_opt &= \
~EXT4_MOUNT_##opt
@@ -1326,6 +1331,7 @@ struct ext4_sb_info {
struct list_head s_es_lru;
unsigned long s_es_last_sorted;
struct percpu_counter s_extent_cache_cnt;
+ struct mb_cache *s_mb_cache;
spinlock_t s_es_lru_lock ____cacheline_aligned_in_smp;
/* Ratelimit ext4 messages. */
@@ -2133,8 +2139,6 @@ extern int ext4_writepage_trans_blocks(struct inode *);
extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks);
extern int ext4_block_truncate_page(handle_t *handle,
struct address_space *mapping, loff_t from);
-extern int ext4_block_zero_page_range(handle_t *handle,
- struct address_space *mapping, loff_t from, loff_t length);
extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
loff_t lstart, loff_t lend);
extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
@@ -2757,6 +2761,7 @@ extern int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk);
extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len);
extern int ext4_ext_precache(struct inode *inode);
+extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len);
/* move_extent.c */
extern void ext4_double_down_write_data_sem(struct inode *first,
@@ -2766,6 +2771,8 @@ extern void ext4_double_up_write_data_sem(struct inode *orig_inode,
extern int ext4_move_extents(struct file *o_filp, struct file *d_filp,
__u64 start_orig, __u64 start_donor,
__u64 len, __u64 *moved_len);
+extern int mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
+ struct ext4_extent **extent);
/* page-io.c */
extern int __init ext4_init_pageio(void);
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index 3fe29de832c8..c3fb607413ed 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -259,6 +259,16 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
if (WARN_ON_ONCE(err)) {
ext4_journal_abort_handle(where, line, __func__, bh,
handle, err);
+ if (inode == NULL) {
+ pr_err("EXT4: jbd2_journal_dirty_metadata "
+ "failed: handle type %u started at "
+ "line %u, credits %u/%u, errcode %d",
+ handle->h_type,
+ handle->h_line_no,
+ handle->h_requested_credits,
+ handle->h_buffer_credits, err);
+ return err;
+ }
ext4_error_inode(inode, where, line,
bh->b_blocknr,
"journal_dirty_metadata failed: "
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 74bc2d549c58..82df3ce9874a 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -37,7 +37,6 @@
#include <linux/quotaops.h>
#include <linux/string.h>
#include <linux/slab.h>
-#include <linux/falloc.h>
#include <asm/uaccess.h>
#include <linux/fiemap.h>
#include "ext4_jbd2.h"
@@ -1691,7 +1690,7 @@ ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1,
* the extent that was written properly split out and conversion to
* initialized is trivial.
*/
- if (ext4_ext_is_uninitialized(ex1) || ext4_ext_is_uninitialized(ex2))
+ if (ext4_ext_is_uninitialized(ex1) != ext4_ext_is_uninitialized(ex2))
return 0;
ext1_ee_len = ext4_ext_get_actual_len(ex1);
@@ -1708,6 +1707,11 @@ ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1,
*/
if (ext1_ee_len + ext2_ee_len > EXT_INIT_MAX_LEN)
return 0;
+ if (ext4_ext_is_uninitialized(ex1) &&
+ (ext4_test_inode_state(inode, EXT4_STATE_DIO_UNWRITTEN) ||
+ atomic_read(&EXT4_I(inode)->i_unwritten) ||
+ (ext1_ee_len + ext2_ee_len > EXT_UNINIT_MAX_LEN)))
+ return 0;
#ifdef AGGRESSIVE_TEST
if (ext1_ee_len >= 4)
return 0;
@@ -1731,7 +1735,7 @@ static int ext4_ext_try_to_merge_right(struct inode *inode,
{
struct ext4_extent_header *eh;
unsigned int depth, len;
- int merge_done = 0;
+ int merge_done = 0, uninit;
depth = ext_depth(inode);
BUG_ON(path[depth].p_hdr == NULL);
@@ -1741,8 +1745,11 @@ static int ext4_ext_try_to_merge_right(struct inode *inode,
if (!ext4_can_extents_be_merged(inode, ex, ex + 1))
break;
/* merge with next extent! */
+ uninit = ext4_ext_is_uninitialized(ex);
ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex)
+ ext4_ext_get_actual_len(ex + 1));
+ if (uninit)
+ ext4_ext_mark_uninitialized(ex);
if (ex + 1 < EXT_LAST_EXTENT(eh)) {
len = (EXT_LAST_EXTENT(eh) - ex - 1)
@@ -1896,7 +1903,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
struct ext4_ext_path *npath = NULL;
int depth, len, err;
ext4_lblk_t next;
- int mb_flags = 0;
+ int mb_flags = 0, uninit;
if (unlikely(ext4_ext_get_actual_len(newext) == 0)) {
EXT4_ERROR_INODE(inode, "ext4_ext_get_actual_len(newext) == 0");
@@ -1946,9 +1953,11 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
path + depth);
if (err)
return err;
-
+ uninit = ext4_ext_is_uninitialized(ex);
ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex)
+ ext4_ext_get_actual_len(newext));
+ if (uninit)
+ ext4_ext_mark_uninitialized(ex);
eh = path[depth].p_hdr;
nearex = ex;
goto merge;
@@ -1971,10 +1980,13 @@ prepend:
if (err)
return err;
+ uninit = ext4_ext_is_uninitialized(ex);
ex->ee_block = newext->ee_block;
ext4_ext_store_pblock(ex, ext4_ext_pblock(newext));
ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex)
+ ext4_ext_get_actual_len(newext));
+ if (uninit)
+ ext4_ext_mark_uninitialized(ex);
eh = path[depth].p_hdr;
nearex = ex;
goto merge;
@@ -2585,6 +2597,27 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
ex_ee_block = le32_to_cpu(ex->ee_block);
ex_ee_len = ext4_ext_get_actual_len(ex);
+ /*
+ * If we're starting with an extent other than the last one in the
+ * node, we need to see if it shares a cluster with the extent to
+ * the right (towards the end of the file). If its leftmost cluster
+ * is this extent's rightmost cluster and it is not cluster aligned,
+ * we'll mark it as a partial that is not to be deallocated.
+ */
+
+ if (ex != EXT_LAST_EXTENT(eh)) {
+ ext4_fsblk_t current_pblk, right_pblk;
+ long long current_cluster, right_cluster;
+
+ current_pblk = ext4_ext_pblock(ex) + ex_ee_len - 1;
+ current_cluster = (long long)EXT4_B2C(sbi, current_pblk);
+ right_pblk = ext4_ext_pblock(ex + 1);
+ right_cluster = (long long)EXT4_B2C(sbi, right_pblk);
+ if (current_cluster == right_cluster &&
+ EXT4_PBLK_COFF(sbi, right_pblk))
+ *partial_cluster = -right_cluster;
+ }
+
trace_ext4_ext_rm_leaf(inode, start, ex, *partial_cluster);
while (ex >= EXT_FIRST_EXTENT(eh) &&
@@ -2710,10 +2743,15 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
err = ext4_ext_correct_indexes(handle, inode, path);
/*
- * Free the partial cluster only if the current extent does not
- * reference it. Otherwise we might free used cluster.
+ * If there's a partial cluster and at least one extent remains in
+ * the leaf, free the partial cluster if it isn't shared with the
+ * current extent. If there's a partial cluster and no extents
+ * remain in the leaf, it can't be freed here. It can only be
+ * freed when it's possible to determine if it's not shared with
+ * any other extent - when the next leaf is processed or when space
+ * removal is complete.
*/
- if (*partial_cluster > 0 &&
+ if (*partial_cluster > 0 && eh->eh_entries &&
(EXT4_B2C(sbi, ext4_ext_pblock(ex) + ex_ee_len - 1) !=
*partial_cluster)) {
int flags = get_default_free_blocks_flags(inode);
@@ -3569,6 +3607,8 @@ out:
* b> Splits in two extents: Write is happening at either end of the extent
* c> Splits in three extents: Somone is writing in middle of the extent
*
+ * This works the same way in the case of initialized -> unwritten conversion.
+ *
* One of more index blocks maybe needed if the extent tree grow after
* the uninitialized extent split. To prevent ENOSPC occur at the IO
* complete, we need to split the uninitialized extent before DIO submit
@@ -3579,7 +3619,7 @@ out:
*
* Returns the size of uninitialized extent to be written on success.
*/
-static int ext4_split_unwritten_extents(handle_t *handle,
+static int ext4_split_convert_extents(handle_t *handle,
struct inode *inode,
struct ext4_map_blocks *map,
struct ext4_ext_path *path,
@@ -3591,9 +3631,9 @@ static int ext4_split_unwritten_extents(handle_t *handle,
unsigned int ee_len;
int split_flag = 0, depth;
- ext_debug("ext4_split_unwritten_extents: inode %lu, logical"
- "block %llu, max_blocks %u\n", inode->i_ino,
- (unsigned long long)map->m_lblk, map->m_len);
+ ext_debug("%s: inode %lu, logical block %llu, max_blocks %u\n",
+ __func__, inode->i_ino,
+ (unsigned long long)map->m_lblk, map->m_len);
eof_block = (inode->i_size + inode->i_sb->s_blocksize - 1) >>
inode->i_sb->s_blocksize_bits;
@@ -3608,14 +3648,73 @@ static int ext4_split_unwritten_extents(handle_t *handle,
ee_block = le32_to_cpu(ex->ee_block);
ee_len = ext4_ext_get_actual_len(ex);
- split_flag |= ee_block + ee_len <= eof_block ? EXT4_EXT_MAY_ZEROOUT : 0;
- split_flag |= EXT4_EXT_MARK_UNINIT2;
- if (flags & EXT4_GET_BLOCKS_CONVERT)
- split_flag |= EXT4_EXT_DATA_VALID2;
+ /* Convert to unwritten */
+ if (flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN) {
+ split_flag |= EXT4_EXT_DATA_VALID1;
+ /* Convert to initialized */
+ } else if (flags & EXT4_GET_BLOCKS_CONVERT) {
+ split_flag |= ee_block + ee_len <= eof_block ?
+ EXT4_EXT_MAY_ZEROOUT : 0;
+ split_flag |= (EXT4_EXT_MARK_UNINIT2 | EXT4_EXT_DATA_VALID2);
+ }
flags |= EXT4_GET_BLOCKS_PRE_IO;
return ext4_split_extent(handle, inode, path, map, split_flag, flags);
}
+static int ext4_convert_initialized_extents(handle_t *handle,
+ struct inode *inode,
+ struct ext4_map_blocks *map,
+ struct ext4_ext_path *path)
+{
+ struct ext4_extent *ex;
+ ext4_lblk_t ee_block;
+ unsigned int ee_len;
+ int depth;
+ int err = 0;
+
+ depth = ext_depth(inode);
+ ex = path[depth].p_ext;
+ ee_block = le32_to_cpu(ex->ee_block);
+ ee_len = ext4_ext_get_actual_len(ex);
+
+ ext_debug("%s: inode %lu, logical"
+ "block %llu, max_blocks %u\n", __func__, inode->i_ino,
+ (unsigned long long)ee_block, ee_len);
+
+ if (ee_block != map->m_lblk || ee_len > map->m_len) {
+ err = ext4_split_convert_extents(handle, inode, map, path,
+ EXT4_GET_BLOCKS_CONVERT_UNWRITTEN);
+ if (err < 0)
+ goto out;
+ ext4_ext_drop_refs(path);
+ path = ext4_ext_find_extent(inode, map->m_lblk, path, 0);
+ if (IS_ERR(path)) {
+ err = PTR_ERR(path);
+ goto out;
+ }
+ depth = ext_depth(inode);
+ ex = path[depth].p_ext;
+ }
+
+ err = ext4_ext_get_access(handle, inode, path + depth);
+ if (err)
+ goto out;
+ /* first mark the extent as uninitialized */
+ ext4_ext_mark_uninitialized(ex);
+
+ /* note: ext4_ext_correct_indexes() isn't needed here because
+ * borders are not changed
+ */
+ ext4_ext_try_to_merge(handle, inode, path, ex);
+
+ /* Mark modified extent as dirty */
+ err = ext4_ext_dirty(handle, inode, path + path->p_depth);
+out:
+ ext4_ext_show_leaf(inode, path);
+ return err;
+}
+
+
static int ext4_convert_unwritten_extents_endio(handle_t *handle,
struct inode *inode,
struct ext4_map_blocks *map,
@@ -3649,8 +3748,8 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
inode->i_ino, (unsigned long long)ee_block, ee_len,
(unsigned long long)map->m_lblk, map->m_len);
#endif
- err = ext4_split_unwritten_extents(handle, inode, map, path,
- EXT4_GET_BLOCKS_CONVERT);
+ err = ext4_split_convert_extents(handle, inode, map, path,
+ EXT4_GET_BLOCKS_CONVERT);
if (err < 0)
goto out;
ext4_ext_drop_refs(path);
@@ -3851,6 +3950,38 @@ get_reserved_cluster_alloc(struct inode *inode, ext4_lblk_t lblk_start,
}
static int
+ext4_ext_convert_initialized_extent(handle_t *handle, struct inode *inode,
+ struct ext4_map_blocks *map,
+ struct ext4_ext_path *path, int flags,
+ unsigned int allocated, ext4_fsblk_t newblock)
+{
+ int ret = 0;
+ int err = 0;
+
+ /*
+ * Make sure that the extent is no bigger than we support with
+ * uninitialized extent
+ */
+ if (map->m_len > EXT_UNINIT_MAX_LEN)
+ map->m_len = EXT_UNINIT_MAX_LEN / 2;
+
+ ret = ext4_convert_initialized_extents(handle, inode, map,
+ path);
+ if (ret >= 0) {
+ ext4_update_inode_fsync_trans(handle, inode, 1);
+ err = check_eofblocks_fl(handle, inode, map->m_lblk,
+ path, map->m_len);
+ } else
+ err = ret;
+ map->m_flags |= EXT4_MAP_UNWRITTEN;
+ if (allocated > map->m_len)
+ allocated = map->m_len;
+ map->m_len = allocated;
+
+ return err ? err : allocated;
+}
+
+static int
ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
struct ext4_map_blocks *map,
struct ext4_ext_path *path, int flags,
@@ -3877,8 +4008,8 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
/* get_block() before submit the IO, split the extent */
if ((flags & EXT4_GET_BLOCKS_PRE_IO)) {
- ret = ext4_split_unwritten_extents(handle, inode, map,
- path, flags);
+ ret = ext4_split_convert_extents(handle, inode, map,
+ path, flags | EXT4_GET_BLOCKS_CONVERT);
if (ret <= 0)
goto out;
/*
@@ -3993,10 +4124,6 @@ out1:
map->m_pblk = newblock;
map->m_len = allocated;
out2:
- if (path) {
- ext4_ext_drop_refs(path);
- kfree(path);
- }
return err ? err : allocated;
}
@@ -4128,7 +4255,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
struct ext4_extent newex, *ex, *ex2;
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
ext4_fsblk_t newblock = 0;
- int free_on_err = 0, err = 0, depth;
+ int free_on_err = 0, err = 0, depth, ret;
unsigned int allocated = 0, offset = 0;
unsigned int allocated_clusters = 0;
struct ext4_allocation_request ar;
@@ -4170,6 +4297,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
ext4_fsblk_t ee_start = ext4_ext_pblock(ex);
unsigned short ee_len;
+
/*
* Uninitialized extents are treated as holes, except that
* we split out initialized portions during a write.
@@ -4186,13 +4314,27 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
ext_debug("%u fit into %u:%d -> %llu\n", map->m_lblk,
ee_block, ee_len, newblock);
- if (!ext4_ext_is_uninitialized(ex))
+ /*
+ * If the extent is initialized check whether the
+ * caller wants to convert it to unwritten.
+ */
+ if ((!ext4_ext_is_uninitialized(ex)) &&
+ (flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN)) {
+ allocated = ext4_ext_convert_initialized_extent(
+ handle, inode, map, path, flags,
+ allocated, newblock);
+ goto out2;
+ } else if (!ext4_ext_is_uninitialized(ex))
goto out;
- allocated = ext4_ext_handle_uninitialized_extents(
+ ret = ext4_ext_handle_uninitialized_extents(
handle, inode, map, path, flags,
allocated, newblock);
- goto out3;
+ if (ret < 0)
+ err = ret;
+ else
+ allocated = ret;
+ goto out2;
}
}
@@ -4473,7 +4615,6 @@ out2:
kfree(path);
}
-out3:
trace_ext4_ext_map_blocks_exit(inode, flags, map,
err ? err : allocated);
ext4_es_lru_add(inode);
@@ -4514,34 +4655,200 @@ retry:
ext4_std_error(inode->i_sb, err);
}
-static void ext4_falloc_update_inode(struct inode *inode,
- int mode, loff_t new_size, int update_ctime)
+static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
+ ext4_lblk_t len, int flags, int mode)
{
- struct timespec now;
+ struct inode *inode = file_inode(file);
+ handle_t *handle;
+ int ret = 0;
+ int ret2 = 0;
+ int retries = 0;
+ struct ext4_map_blocks map;
+ unsigned int credits;
- if (update_ctime) {
- now = current_fs_time(inode->i_sb);
- if (!timespec_equal(&inode->i_ctime, &now))
- inode->i_ctime = now;
+ map.m_lblk = offset;
+ /*
+ * Don't normalize the request if it can fit in one extent so
+ * that it doesn't get unnecessarily split into multiple
+ * extents.
+ */
+ if (len <= EXT_UNINIT_MAX_LEN)
+ flags |= EXT4_GET_BLOCKS_NO_NORMALIZE;
+
+ /*
+ * credits to insert 1 extent into extent tree
+ */
+ credits = ext4_chunk_trans_blocks(inode, len);
+
+retry:
+ while (ret >= 0 && ret < len) {
+ map.m_lblk = map.m_lblk + ret;
+ map.m_len = len = len - ret;
+ handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS,
+ credits);
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ break;
+ }
+ ret = ext4_map_blocks(handle, inode, &map, flags);
+ if (ret <= 0) {
+ ext4_debug("inode #%lu: block %u: len %u: "
+ "ext4_ext_map_blocks returned %d",
+ inode->i_ino, map.m_lblk,
+ map.m_len, ret);
+ ext4_mark_inode_dirty(handle, inode);
+ ret2 = ext4_journal_stop(handle);
+ break;
+ }
+ ret2 = ext4_journal_stop(handle);
+ if (ret2)
+ break;
+ }
+ if (ret == -ENOSPC &&
+ ext4_should_retry_alloc(inode->i_sb, &retries)) {
+ ret = 0;
+ goto retry;
}
+
+ return ret > 0 ? ret2 : ret;
+}
+
+static long ext4_zero_range(struct file *file, loff_t offset,
+ loff_t len, int mode)
+{
+ struct inode *inode = file_inode(file);
+ handle_t *handle = NULL;
+ unsigned int max_blocks;
+ loff_t new_size = 0;
+ int ret = 0;
+ int flags;
+ int partial;
+ loff_t start, end;
+ ext4_lblk_t lblk;
+ struct address_space *mapping = inode->i_mapping;
+ unsigned int blkbits = inode->i_blkbits;
+
+ trace_ext4_zero_range(inode, offset, len, mode);
+
+ /*
+ * Write out all dirty pages to avoid race conditions
+ * Then release them.
+ */
+ if (mapping->nrpages && mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) {
+ ret = filemap_write_and_wait_range(mapping, offset,
+ offset + len - 1);
+ if (ret)
+ return ret;
+ }
+
/*
- * Update only when preallocation was requested beyond
- * the file size.
+ * Round up offset. This is not fallocate, we neet to zero out
+ * blocks, so convert interior block aligned part of the range to
+ * unwritten and possibly manually zero out unaligned parts of the
+ * range.
*/
- if (!(mode & FALLOC_FL_KEEP_SIZE)) {
+ start = round_up(offset, 1 << blkbits);
+ end = round_down((offset + len), 1 << blkbits);
+
+ if (start < offset || end > offset + len)
+ return -EINVAL;
+ partial = (offset + len) & ((1 << blkbits) - 1);
+
+ lblk = start >> blkbits;
+ max_blocks = (end >> blkbits);
+ if (max_blocks < lblk)
+ max_blocks = 0;
+ else
+ max_blocks -= lblk;
+
+ flags = EXT4_GET_BLOCKS_CREATE_UNINIT_EXT |
+ EXT4_GET_BLOCKS_CONVERT_UNWRITTEN;
+ if (mode & FALLOC_FL_KEEP_SIZE)
+ flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
+
+ mutex_lock(&inode->i_mutex);
+
+ /*
+ * Indirect files do not support unwritten extnets
+ */
+ if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
+ ret = -EOPNOTSUPP;
+ goto out_mutex;
+ }
+
+ if (!(mode & FALLOC_FL_KEEP_SIZE) &&
+ offset + len > i_size_read(inode)) {
+ new_size = offset + len;
+ ret = inode_newsize_ok(inode, new_size);
+ if (ret)
+ goto out_mutex;
+ /*
+ * If we have a partial block after EOF we have to allocate
+ * the entire block.
+ */
+ if (partial)
+ max_blocks += 1;
+ }
+
+ if (max_blocks > 0) {
+
+ /* Now release the pages and zero block aligned part of pages*/
+ truncate_pagecache_range(inode, start, end - 1);
+
+ /* Wait all existing dio workers, newcomers will block on i_mutex */
+ ext4_inode_block_unlocked_dio(inode);
+ inode_dio_wait(inode);
+
+ /*
+ * Remove entire range from the extent status tree.
+ */
+ ret = ext4_es_remove_extent(inode, lblk, max_blocks);
+ if (ret)
+ goto out_dio;
+
+ ret = ext4_alloc_file_blocks(file, lblk, max_blocks, flags,
+ mode);
+ if (ret)
+ goto out_dio;
+ }
+
+ handle = ext4_journal_start(inode, EXT4_HT_MISC, 4);
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ ext4_std_error(inode->i_sb, ret);
+ goto out_dio;
+ }
+
+ inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
+
+ if (new_size) {
if (new_size > i_size_read(inode))
i_size_write(inode, new_size);
if (new_size > EXT4_I(inode)->i_disksize)
ext4_update_i_disksize(inode, new_size);
} else {
/*
- * Mark that we allocate beyond EOF so the subsequent truncate
- * can proceed even if the new size is the same as i_size.
- */
- if (new_size > i_size_read(inode))
+ * Mark that we allocate beyond EOF so the subsequent truncate
+ * can proceed even if the new size is the same as i_size.
+ */
+ if ((offset + len) > i_size_read(inode))
ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS);
}
+ ext4_mark_inode_dirty(handle, inode);
+
+ /* Zero out partial block at the edges of the range */
+ ret = ext4_zero_partial_blocks(handle, inode, offset, len);
+
+ if (file->f_flags & O_SYNC)
+ ext4_handle_sync(handle);
+
+ ext4_journal_stop(handle);
+out_dio:
+ ext4_inode_resume_unlocked_dio(inode);
+out_mutex:
+ mutex_unlock(&inode->i_mutex);
+ return ret;
}
/*
@@ -4555,22 +4862,25 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
{
struct inode *inode = file_inode(file);
handle_t *handle;
- loff_t new_size;
+ loff_t new_size = 0;
unsigned int max_blocks;
int ret = 0;
- int ret2 = 0;
- int retries = 0;
int flags;
- struct ext4_map_blocks map;
- unsigned int credits, blkbits = inode->i_blkbits;
+ ext4_lblk_t lblk;
+ struct timespec tv;
+ unsigned int blkbits = inode->i_blkbits;
/* Return error if mode is not supported */
- if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+ if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
+ FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE))
return -EOPNOTSUPP;
if (mode & FALLOC_FL_PUNCH_HOLE)
return ext4_punch_hole(inode, offset, len);
+ if (mode & FALLOC_FL_COLLAPSE_RANGE)
+ return ext4_collapse_range(inode, offset, len);
+
ret = ext4_convert_inline_data(inode);
if (ret)
return ret;
@@ -4582,83 +4892,66 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
return -EOPNOTSUPP;
+ if (mode & FALLOC_FL_ZERO_RANGE)
+ return ext4_zero_range(file, offset, len, mode);
+
trace_ext4_fallocate_enter(inode, offset, len, mode);
- map.m_lblk = offset >> blkbits;
+ lblk = offset >> blkbits;
/*
* We can't just convert len to max_blocks because
* If blocksize = 4096 offset = 3072 and len = 2048
*/
max_blocks = (EXT4_BLOCK_ALIGN(len + offset, blkbits) >> blkbits)
- - map.m_lblk;
- /*
- * credits to insert 1 extent into extent tree
- */
- credits = ext4_chunk_trans_blocks(inode, max_blocks);
- mutex_lock(&inode->i_mutex);
- ret = inode_newsize_ok(inode, (len + offset));
- if (ret) {
- mutex_unlock(&inode->i_mutex);
- trace_ext4_fallocate_exit(inode, offset, max_blocks, ret);
- return ret;
- }
+ - lblk;
+
flags = EXT4_GET_BLOCKS_CREATE_UNINIT_EXT;
if (mode & FALLOC_FL_KEEP_SIZE)
flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
- /*
- * Don't normalize the request if it can fit in one extent so
- * that it doesn't get unnecessarily split into multiple
- * extents.
- */
- if (len <= EXT_UNINIT_MAX_LEN << blkbits)
- flags |= EXT4_GET_BLOCKS_NO_NORMALIZE;
-retry:
- while (ret >= 0 && ret < max_blocks) {
- map.m_lblk = map.m_lblk + ret;
- map.m_len = max_blocks = max_blocks - ret;
- handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS,
- credits);
- if (IS_ERR(handle)) {
- ret = PTR_ERR(handle);
- break;
- }
- ret = ext4_map_blocks(handle, inode, &map, flags);
- if (ret <= 0) {
-#ifdef EXT4FS_DEBUG
- ext4_warning(inode->i_sb,
- "inode #%lu: block %u: len %u: "
- "ext4_ext_map_blocks returned %d",
- inode->i_ino, map.m_lblk,
- map.m_len, ret);
-#endif
- ext4_mark_inode_dirty(handle, inode);
- ret2 = ext4_journal_stop(handle);
- break;
- }
- if ((map.m_lblk + ret) >= (EXT4_BLOCK_ALIGN(offset + len,
- blkbits) >> blkbits))
- new_size = offset + len;
- else
- new_size = ((loff_t) map.m_lblk + ret) << blkbits;
+ mutex_lock(&inode->i_mutex);
- ext4_falloc_update_inode(inode, mode, new_size,
- (map.m_flags & EXT4_MAP_NEW));
- ext4_mark_inode_dirty(handle, inode);
- if ((file->f_flags & O_SYNC) && ret >= max_blocks)
- ext4_handle_sync(handle);
- ret2 = ext4_journal_stop(handle);
- if (ret2)
- break;
+ if (!(mode & FALLOC_FL_KEEP_SIZE) &&
+ offset + len > i_size_read(inode)) {
+ new_size = offset + len;
+ ret = inode_newsize_ok(inode, new_size);
+ if (ret)
+ goto out;
}
- if (ret == -ENOSPC &&
- ext4_should_retry_alloc(inode->i_sb, &retries)) {
- ret = 0;
- goto retry;
+
+ ret = ext4_alloc_file_blocks(file, lblk, max_blocks, flags, mode);
+ if (ret)
+ goto out;
+
+ handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
+ if (IS_ERR(handle))
+ goto out;
+
+ tv = inode->i_ctime = ext4_current_time(inode);
+
+ if (new_size) {
+ if (new_size > i_size_read(inode)) {
+ i_size_write(inode, new_size);
+ inode->i_mtime = tv;
+ }
+ if (new_size > EXT4_I(inode)->i_disksize)
+ ext4_update_i_disksize(inode, new_size);
+ } else {
+ /*
+ * Mark that we allocate beyond EOF so the subsequent truncate
+ * can proceed even if the new size is the same as i_size.
+ */
+ if ((offset + len) > i_size_read(inode))
+ ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS);
}
+ ext4_mark_inode_dirty(handle, inode);
+ if (file->f_flags & O_SYNC)
+ ext4_handle_sync(handle);
+
+ ext4_journal_stop(handle);
+out:
mutex_unlock(&inode->i_mutex);
- trace_ext4_fallocate_exit(inode, offset, max_blocks,
- ret > 0 ? ret2 : ret);
- return ret > 0 ? ret2 : ret;
+ trace_ext4_fallocate_exit(inode, offset, max_blocks, ret);
+ return ret;
}
/*
@@ -4869,3 +5162,304 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
ext4_es_lru_add(inode);
return error;
}
+
+/*
+ * ext4_access_path:
+ * Function to access the path buffer for marking it dirty.
+ * It also checks if there are sufficient credits left in the journal handle
+ * to update path.
+ */
+static int
+ext4_access_path(handle_t *handle, struct inode *inode,
+ struct ext4_ext_path *path)
+{
+ int credits, err;
+
+ if (!ext4_handle_valid(handle))
+ return 0;
+
+ /*
+ * Check if need to extend journal credits
+ * 3 for leaf, sb, and inode plus 2 (bmap and group
+ * descriptor) for each block group; assume two block
+ * groups
+ */
+ if (handle->h_buffer_credits < 7) {
+ credits = ext4_writepage_trans_blocks(inode);
+ err = ext4_ext_truncate_extend_restart(handle, inode, credits);
+ /* EAGAIN is success */
+ if (err && err != -EAGAIN)
+ return err;
+ }
+
+ err = ext4_ext_get_access(handle, inode, path);
+ return err;
+}
+
+/*
+ * ext4_ext_shift_path_extents:
+ * Shift the extents of a path structure lying between path[depth].p_ext
+ * and EXT_LAST_EXTENT(path[depth].p_hdr) downwards, by subtracting shift
+ * from starting block for each extent.
+ */
+static int
+ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift,
+ struct inode *inode, handle_t *handle,
+ ext4_lblk_t *start)
+{
+ int depth, err = 0;
+ struct ext4_extent *ex_start, *ex_last;
+ bool update = 0;
+ depth = path->p_depth;
+
+ while (depth >= 0) {
+ if (depth == path->p_depth) {
+ ex_start = path[depth].p_ext;
+ if (!ex_start)
+ return -EIO;
+
+ ex_last = EXT_LAST_EXTENT(path[depth].p_hdr);
+ if (!ex_last)
+ return -EIO;
+
+ err = ext4_access_path(handle, inode, path + depth);
+ if (err)
+ goto out;
+
+ if (ex_start == EXT_FIRST_EXTENT(path[depth].p_hdr))
+ update = 1;
+
+ *start = ex_last->ee_block +
+ ext4_ext_get_actual_len(ex_last);
+
+ while (ex_start <= ex_last) {
+ ex_start->ee_block -= shift;
+ if (ex_start >
+ EXT_FIRST_EXTENT(path[depth].p_hdr)) {
+ if (ext4_ext_try_to_merge_right(inode,
+ path, ex_start - 1))
+ ex_last--;
+ }
+ ex_start++;
+ }
+ err = ext4_ext_dirty(handle, inode, path + depth);
+ if (err)
+ goto out;
+
+ if (--depth < 0 || !update)
+ break;
+ }
+
+ /* Update index too */
+ err = ext4_access_path(handle, inode, path + depth);
+ if (err)
+ goto out;
+
+ path[depth].p_idx->ei_block -= shift;
+ err = ext4_ext_dirty(handle, inode, path + depth);
+ if (err)
+ goto out;
+
+ /* we are done if current index is not a starting index */
+ if (path[depth].p_idx != EXT_FIRST_INDEX(path[depth].p_hdr))
+ break;
+
+ depth--;
+ }
+
+out:
+ return err;
+}
+
+/*
+ * ext4_ext_shift_extents:
+ * All the extents which lies in the range from start to the last allocated
+ * block for the file are shifted downwards by shift blocks.
+ * On success, 0 is returned, error otherwise.
+ */
+static int
+ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
+ ext4_lblk_t start, ext4_lblk_t shift)
+{
+ struct ext4_ext_path *path;
+ int ret = 0, depth;
+ struct ext4_extent *extent;
+ ext4_lblk_t stop_block, current_block;
+ ext4_lblk_t ex_start, ex_end;
+
+ /* Let path point to the last extent */
+ path = ext4_ext_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL, 0);
+ if (IS_ERR(path))
+ return PTR_ERR(path);
+
+ depth = path->p_depth;
+ extent = path[depth].p_ext;
+ if (!extent) {
+ ext4_ext_drop_refs(path);
+ kfree(path);
+ return ret;
+ }
+
+ stop_block = extent->ee_block + ext4_ext_get_actual_len(extent);
+ ext4_ext_drop_refs(path);
+ kfree(path);
+
+ /* Nothing to shift, if hole is at the end of file */
+ if (start >= stop_block)
+ return ret;
+
+ /*
+ * Don't start shifting extents until we make sure the hole is big
+ * enough to accomodate the shift.
+ */
+ path = ext4_ext_find_extent(inode, start - 1, NULL, 0);
+ depth = path->p_depth;
+ extent = path[depth].p_ext;
+ ex_start = extent->ee_block;
+ ex_end = extent->ee_block + ext4_ext_get_actual_len(extent);
+ ext4_ext_drop_refs(path);
+ kfree(path);
+
+ if ((start == ex_start && shift > ex_start) ||
+ (shift > start - ex_end))
+ return -EINVAL;
+
+ /* Its safe to start updating extents */
+ while (start < stop_block) {
+ path = ext4_ext_find_extent(inode, start, NULL, 0);
+ if (IS_ERR(path))
+ return PTR_ERR(path);
+ depth = path->p_depth;
+ extent = path[depth].p_ext;
+ current_block = extent->ee_block;
+ if (start > current_block) {
+ /* Hole, move to the next extent */
+ ret = mext_next_extent(inode, path, &extent);
+ if (ret != 0) {
+ ext4_ext_drop_refs(path);
+ kfree(path);
+ if (ret == 1)
+ ret = 0;
+ break;
+ }
+ }
+ ret = ext4_ext_shift_path_extents(path, shift, inode,
+ handle, &start);
+ ext4_ext_drop_refs(path);
+ kfree(path);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * ext4_collapse_range:
+ * This implements the fallocate's collapse range functionality for ext4
+ * Returns: 0 and non-zero on error.
+ */
+int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
+{
+ struct super_block *sb = inode->i_sb;
+ ext4_lblk_t punch_start, punch_stop;
+ handle_t *handle;
+ unsigned int credits;
+ loff_t new_size;
+ int ret;
+
+ BUG_ON(offset + len > i_size_read(inode));
+
+ /* Collapse range works only on fs block size aligned offsets. */
+ if (offset & (EXT4_BLOCK_SIZE(sb) - 1) ||
+ len & (EXT4_BLOCK_SIZE(sb) - 1))
+ return -EINVAL;
+
+ if (!S_ISREG(inode->i_mode))
+ return -EOPNOTSUPP;
+
+ trace_ext4_collapse_range(inode, offset, len);
+
+ punch_start = offset >> EXT4_BLOCK_SIZE_BITS(sb);
+ punch_stop = (offset + len) >> EXT4_BLOCK_SIZE_BITS(sb);
+
+ /* Write out all dirty pages */
+ ret = filemap_write_and_wait_range(inode->i_mapping, offset, -1);
+ if (ret)
+ return ret;
+
+ /* Take mutex lock */
+ mutex_lock(&inode->i_mutex);
+
+ /* It's not possible punch hole on append only file */
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) {
+ ret = -EPERM;
+ goto out_mutex;
+ }
+
+ if (IS_SWAPFILE(inode)) {
+ ret = -ETXTBSY;
+ goto out_mutex;
+ }
+
+ /* Currently just for extent based files */
+ if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
+ ret = -EOPNOTSUPP;
+ goto out_mutex;
+ }
+
+ truncate_pagecache_range(inode, offset, -1);
+
+ /* Wait for existing dio to complete */
+ ext4_inode_block_unlocked_dio(inode);
+ inode_dio_wait(inode);
+
+ credits = ext4_writepage_trans_blocks(inode);
+ handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits);
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ goto out_dio;
+ }
+
+ down_write(&EXT4_I(inode)->i_data_sem);
+ ext4_discard_preallocations(inode);
+
+ ret = ext4_es_remove_extent(inode, punch_start,
+ EXT_MAX_BLOCKS - punch_start - 1);
+ if (ret) {
+ up_write(&EXT4_I(inode)->i_data_sem);
+ goto out_stop;
+ }
+
+ ret = ext4_ext_remove_space(inode, punch_start, punch_stop - 1);
+ if (ret) {
+ up_write(&EXT4_I(inode)->i_data_sem);
+ goto out_stop;
+ }
+
+ ret = ext4_ext_shift_extents(inode, handle, punch_stop,
+ punch_stop - punch_start);
+ if (ret) {
+ up_write(&EXT4_I(inode)->i_data_sem);
+ goto out_stop;
+ }
+
+ new_size = i_size_read(inode) - len;
+ truncate_setsize(inode, new_size);
+ EXT4_I(inode)->i_disksize = new_size;
+
+ ext4_discard_preallocations(inode);
+ up_write(&EXT4_I(inode)->i_data_sem);
+ if (IS_SYNC(inode))
+ ext4_handle_sync(handle);
+ inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
+ ext4_mark_inode_dirty(handle, inode);
+
+out_stop:
+ ext4_journal_stop(handle);
+out_dio:
+ ext4_inode_resume_unlocked_dio(inode);
+out_mutex:
+ mutex_unlock(&inode->i_mutex);
+ return ret;
+}
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
index 3981ff783950..0a014a7194b2 100644
--- a/fs/ext4/extents_status.c
+++ b/fs/ext4/extents_status.c
@@ -184,7 +184,7 @@ static void ext4_es_print_tree(struct inode *inode)
while (node) {
struct extent_status *es;
es = rb_entry(node, struct extent_status, rb_node);
- printk(KERN_DEBUG " [%u/%u) %llu %llx",
+ printk(KERN_DEBUG " [%u/%u) %llu %x",
es->es_lblk, es->es_len,
ext4_es_pblock(es), ext4_es_status(es));
node = rb_next(node);
@@ -445,8 +445,8 @@ static void ext4_es_insert_extent_ext_check(struct inode *inode,
pr_warn("ES insert assertion failed for "
"inode: %lu we can find an extent "
"at block [%d/%d/%llu/%c], but we "
- "want to add an delayed/hole extent "
- "[%d/%d/%llu/%llx]\n",
+ "want to add a delayed/hole extent "
+ "[%d/%d/%llu/%x]\n",
inode->i_ino, ee_block, ee_len,
ee_start, ee_status ? 'u' : 'w',
es->es_lblk, es->es_len,
@@ -486,8 +486,8 @@ static void ext4_es_insert_extent_ext_check(struct inode *inode,
if (!ext4_es_is_delayed(es) && !ext4_es_is_hole(es)) {
pr_warn("ES insert assertion failed for inode: %lu "
"can't find an extent at block %d but we want "
- "to add an written/unwritten extent "
- "[%d/%d/%llu/%llx]\n", inode->i_ino,
+ "to add a written/unwritten extent "
+ "[%d/%d/%llu/%x]\n", inode->i_ino,
es->es_lblk, es->es_lblk, es->es_len,
ext4_es_pblock(es), ext4_es_status(es));
}
@@ -524,7 +524,7 @@ static void ext4_es_insert_extent_ind_check(struct inode *inode,
*/
pr_warn("ES insert assertion failed for inode: %lu "
"We can find blocks but we want to add a "
- "delayed/hole extent [%d/%d/%llu/%llx]\n",
+ "delayed/hole extent [%d/%d/%llu/%x]\n",
inode->i_ino, es->es_lblk, es->es_len,
ext4_es_pblock(es), ext4_es_status(es));
return;
@@ -554,7 +554,7 @@ static void ext4_es_insert_extent_ind_check(struct inode *inode,
if (ext4_es_is_written(es)) {
pr_warn("ES insert assertion failed for inode: %lu "
"We can't find the block but we want to add "
- "an written extent [%d/%d/%llu/%llx]\n",
+ "a written extent [%d/%d/%llu/%x]\n",
inode->i_ino, es->es_lblk, es->es_len,
ext4_es_pblock(es), ext4_es_status(es));
return;
@@ -658,8 +658,7 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
newes.es_lblk = lblk;
newes.es_len = len;
- ext4_es_store_pblock(&newes, pblk);
- ext4_es_store_status(&newes, status);
+ ext4_es_store_pblock_status(&newes, pblk, status);
trace_ext4_es_insert_extent(inode, &newes);
ext4_es_insert_extent_check(inode, &newes);
@@ -699,8 +698,7 @@ void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk,
newes.es_lblk = lblk;
newes.es_len = len;
- ext4_es_store_pblock(&newes, pblk);
- ext4_es_store_status(&newes, status);
+ ext4_es_store_pblock_status(&newes, pblk, status);
trace_ext4_es_cache_extent(inode, &newes);
if (!len)
@@ -812,13 +810,13 @@ retry:
newes.es_lblk = end + 1;
newes.es_len = len2;
+ block = 0x7FDEADBEEF;
if (ext4_es_is_written(&orig_es) ||
- ext4_es_is_unwritten(&orig_es)) {
+ ext4_es_is_unwritten(&orig_es))
block = ext4_es_pblock(&orig_es) +
orig_es.es_len - len2;
- ext4_es_store_pblock(&newes, block);
- }
- ext4_es_store_status(&newes, ext4_es_status(&orig_es));
+ ext4_es_store_pblock_status(&newes, block,
+ ext4_es_status(&orig_es));
err = __es_insert_extent(inode, &newes);
if (err) {
es->es_lblk = orig_es.es_lblk;
diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h
index 167f4ab8ecc3..f1b62a419920 100644
--- a/fs/ext4/extents_status.h
+++ b/fs/ext4/extents_status.h
@@ -129,6 +129,15 @@ static inline void ext4_es_store_status(struct extent_status *es,
(es->es_pblk & ~ES_MASK));
}
+static inline void ext4_es_store_pblock_status(struct extent_status *es,
+ ext4_fsblk_t pb,
+ unsigned int status)
+{
+ es->es_pblk = (((ext4_fsblk_t)
+ (status & EXTENT_STATUS_FLAGS) << ES_SHIFT) |
+ (pb & ~ES_MASK));
+}
+
extern void ext4_es_register_shrinker(struct ext4_sb_info *sbi);
extern void ext4_es_unregister_shrinker(struct ext4_sb_info *sbi);
extern void ext4_es_lru_add(struct inode *inode);
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 1a5073959f32..6db7f7db7777 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -153,7 +153,7 @@ ext4_file_dio_write(struct kiocb *iocb, const struct iovec *iov,
ssize_t err;
err = generic_write_sync(file, iocb->ki_pos - ret, ret);
- if (err < 0 && ret > 0)
+ if (err < 0)
ret = err;
}
blk_finish_plug(&plug);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 24bfd7ff3049..5b0d2c7d5408 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -215,7 +215,7 @@ void ext4_evict_inode(struct inode *inode)
jbd2_complete_transaction(journal, commit_tid);
filemap_write_and_wait(&inode->i_data);
}
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
WARN_ON(atomic_read(&EXT4_I(inode)->i_ioend_count));
goto no_delete;
@@ -226,7 +226,7 @@ void ext4_evict_inode(struct inode *inode)
if (ext4_should_order_data(inode))
ext4_begin_ordered_truncate(inode, 0);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
WARN_ON(atomic_read(&EXT4_I(inode)->i_ioend_count));
if (is_bad_inode(inode))
@@ -504,6 +504,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
{
struct extent_status es;
int retval;
+ int ret = 0;
#ifdef ES_AGGRESSIVE_TEST
struct ext4_map_blocks orig_map;
@@ -515,6 +516,12 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
"logical block %lu\n", inode->i_ino, flags, map->m_len,
(unsigned long) map->m_lblk);
+ /*
+ * ext4_map_blocks returns an int, and m_len is an unsigned int
+ */
+ if (unlikely(map->m_len > INT_MAX))
+ map->m_len = INT_MAX;
+
/* Lookup extent status tree firstly */
if (ext4_es_lookup_extent(inode, map->m_lblk, &es)) {
ext4_es_lru_add(inode);
@@ -553,7 +560,6 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
EXT4_GET_BLOCKS_KEEP_SIZE);
}
if (retval > 0) {
- int ret;
unsigned int status;
if (unlikely(retval != map->m_len)) {
@@ -580,7 +586,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
found:
if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
- int ret = check_block_validity(inode, map);
+ ret = check_block_validity(inode, map);
if (ret != 0)
return ret;
}
@@ -597,7 +603,13 @@ found:
* with buffer head unmapped.
*/
if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED)
- return retval;
+ /*
+ * If we need to convert extent to unwritten
+ * we continue and do the actual work in
+ * ext4_ext_map_blocks()
+ */
+ if (!(flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN))
+ return retval;
/*
* Here we clear m_flags because after allocating an new extent,
@@ -653,7 +665,6 @@ found:
ext4_clear_inode_state(inode, EXT4_STATE_DELALLOC_RESERVED);
if (retval > 0) {
- int ret;
unsigned int status;
if (unlikely(retval != map->m_len)) {
@@ -688,7 +699,7 @@ found:
has_zeroout:
up_write((&EXT4_I(inode)->i_data_sem));
if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
- int ret = check_block_validity(inode, map);
+ ret = check_block_validity(inode, map);
if (ret != 0)
return ret;
}
@@ -3313,33 +3324,13 @@ void ext4_set_aops(struct inode *inode)
}
/*
- * ext4_block_truncate_page() zeroes out a mapping from file offset `from'
- * up to the end of the block which corresponds to `from'.
- * This required during truncate. We need to physically zero the tail end
- * of that block so it doesn't yield old data if the file is later grown.
- */
-int ext4_block_truncate_page(handle_t *handle,
- struct address_space *mapping, loff_t from)
-{
- unsigned offset = from & (PAGE_CACHE_SIZE-1);
- unsigned length;
- unsigned blocksize;
- struct inode *inode = mapping->host;
-
- blocksize = inode->i_sb->s_blocksize;
- length = blocksize - (offset & (blocksize - 1));
-
- return ext4_block_zero_page_range(handle, mapping, from, length);
-}
-
-/*
* ext4_block_zero_page_range() zeros out a mapping of length 'length'
* starting from file offset 'from'. The range to be zero'd must
* be contained with in one block. If the specified range exceeds
* the end of the block it will be shortened to end of the block
* that cooresponds to 'from'
*/
-int ext4_block_zero_page_range(handle_t *handle,
+static int ext4_block_zero_page_range(handle_t *handle,
struct address_space *mapping, loff_t from, loff_t length)
{
ext4_fsblk_t index = from >> PAGE_CACHE_SHIFT;
@@ -3429,6 +3420,26 @@ unlock:
return err;
}
+/*
+ * ext4_block_truncate_page() zeroes out a mapping from file offset `from'
+ * up to the end of the block which corresponds to `from'.
+ * This required during truncate. We need to physically zero the tail end
+ * of that block so it doesn't yield old data if the file is later grown.
+ */
+int ext4_block_truncate_page(handle_t *handle,
+ struct address_space *mapping, loff_t from)
+{
+ unsigned offset = from & (PAGE_CACHE_SIZE-1);
+ unsigned length;
+ unsigned blocksize;
+ struct inode *inode = mapping->host;
+
+ blocksize = inode->i_sb->s_blocksize;
+ length = blocksize - (offset & (blocksize - 1));
+
+ return ext4_block_zero_page_range(handle, mapping, from, length);
+}
+
int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
loff_t lstart, loff_t length)
{
@@ -3502,7 +3513,7 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
if (!S_ISREG(inode->i_mode))
return -EOPNOTSUPP;
- trace_ext4_punch_hole(inode, offset, length);
+ trace_ext4_punch_hole(inode, offset, length, 0);
/*
* Write out all dirty pages to avoid race conditions
@@ -3609,6 +3620,12 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
up_write(&EXT4_I(inode)->i_data_sem);
if (IS_SYNC(inode))
ext4_handle_sync(handle);
+
+ /* Now release the pages again to reduce race window */
+ if (last_block_offset > first_block_offset)
+ truncate_pagecache_range(inode, first_block_offset,
+ last_block_offset);
+
inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
ext4_mark_inode_dirty(handle, inode);
out_stop:
@@ -3682,7 +3699,7 @@ void ext4_truncate(struct inode *inode)
/*
* There is a possibility that we're either freeing the inode
- * or it completely new indode. In those cases we might not
+ * or it's a completely new inode. In those cases we might not
* have i_mutex locked because it's not necessary.
*/
if (!(inode->i_state & (I_NEW|I_FREEING)))
@@ -3934,8 +3951,8 @@ void ext4_set_inode_flags(struct inode *inode)
new_fl |= S_NOATIME;
if (flags & EXT4_DIRSYNC_FL)
new_fl |= S_DIRSYNC;
- set_mask_bits(&inode->i_flags,
- S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC, new_fl);
+ inode_set_flags(inode, new_fl,
+ S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
}
/* Propagate flags from i_flags to EXT4_I(inode)->i_flags */
@@ -4154,11 +4171,13 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
EXT4_INODE_GET_XTIME(i_atime, inode, raw_inode);
EXT4_EINODE_GET_XTIME(i_crtime, ei, raw_inode);
- inode->i_version = le32_to_cpu(raw_inode->i_disk_version);
- if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
- if (EXT4_FITS_IN_INODE(raw_inode, ei, i_version_hi))
- inode->i_version |=
- (__u64)(le32_to_cpu(raw_inode->i_version_hi)) << 32;
+ if (likely(!test_opt2(inode->i_sb, HURD_COMPAT))) {
+ inode->i_version = le32_to_cpu(raw_inode->i_disk_version);
+ if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
+ if (EXT4_FITS_IN_INODE(raw_inode, ei, i_version_hi))
+ inode->i_version |=
+ (__u64)(le32_to_cpu(raw_inode->i_version_hi)) << 32;
+ }
}
ret = 0;
@@ -4328,8 +4347,7 @@ static int ext4_do_update_inode(handle_t *handle,
goto out_brelse;
raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
raw_inode->i_flags = cpu_to_le32(ei->i_flags & 0xFFFFFFFF);
- if (EXT4_SB(inode->i_sb)->s_es->s_creator_os !=
- cpu_to_le32(EXT4_OS_HURD))
+ if (likely(!test_opt2(inode->i_sb, HURD_COMPAT)))
raw_inode->i_file_acl_high =
cpu_to_le16(ei->i_file_acl >> 32);
raw_inode->i_file_acl_lo = cpu_to_le32(ei->i_file_acl);
@@ -4374,12 +4392,15 @@ static int ext4_do_update_inode(handle_t *handle,
raw_inode->i_block[block] = ei->i_data[block];
}
- raw_inode->i_disk_version = cpu_to_le32(inode->i_version);
- if (ei->i_extra_isize) {
- if (EXT4_FITS_IN_INODE(raw_inode, ei, i_version_hi))
- raw_inode->i_version_hi =
- cpu_to_le32(inode->i_version >> 32);
- raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize);
+ if (likely(!test_opt2(inode->i_sb, HURD_COMPAT))) {
+ raw_inode->i_disk_version = cpu_to_le32(inode->i_version);
+ if (ei->i_extra_isize) {
+ if (EXT4_FITS_IN_INODE(raw_inode, ei, i_version_hi))
+ raw_inode->i_version_hi =
+ cpu_to_le32(inode->i_version >> 32);
+ raw_inode->i_extra_isize =
+ cpu_to_le16(ei->i_extra_isize);
+ }
}
ext4_inode_csum_set(inode, raw_inode, ei);
@@ -4446,7 +4467,12 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc)
return -EIO;
}
- if (wbc->sync_mode != WB_SYNC_ALL)
+ /*
+ * No need to force transaction in WB_SYNC_NONE mode. Also
+ * ext4_sync_fs() will force the commit after everything is
+ * written.
+ */
+ if (wbc->sync_mode != WB_SYNC_ALL || wbc->for_sync)
return 0;
err = ext4_force_commit(inode->i_sb);
@@ -4456,7 +4482,11 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc)
err = __ext4_get_inode_loc(inode, &iloc, 0);
if (err)
return err;
- if (wbc->sync_mode == WB_SYNC_ALL)
+ /*
+ * sync(2) will flush the whole buffer cache. No need to do
+ * it here separately for each inode.
+ */
+ if (wbc->sync_mode == WB_SYNC_ALL && !wbc->for_sync)
sync_dirty_buffer(iloc.bh);
if (buffer_req(iloc.bh) && !buffer_uptodate(iloc.bh)) {
EXT4_ERROR_INODE_BLOCK(inode, iloc.bh->b_blocknr,
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index a2a837f00407..0f2252ec274d 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -104,21 +104,15 @@ static long swap_inode_boot_loader(struct super_block *sb,
struct ext4_inode_info *ei_bl;
struct ext4_sb_info *sbi = EXT4_SB(sb);
- if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode)) {
- err = -EINVAL;
- goto swap_boot_out;
- }
+ if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode))
+ return -EINVAL;
- if (!inode_owner_or_capable(inode) || !capable(CAP_SYS_ADMIN)) {
- err = -EPERM;
- goto swap_boot_out;
- }
+ if (!inode_owner_or_capable(inode) || !capable(CAP_SYS_ADMIN))
+ return -EPERM;
inode_bl = ext4_iget(sb, EXT4_BOOT_LOADER_INO);
- if (IS_ERR(inode_bl)) {
- err = PTR_ERR(inode_bl);
- goto swap_boot_out;
- }
+ if (IS_ERR(inode_bl))
+ return PTR_ERR(inode_bl);
ei_bl = EXT4_I(inode_bl);
filemap_flush(inode->i_mapping);
@@ -193,20 +187,14 @@ static long swap_inode_boot_loader(struct super_block *sb,
ext4_mark_inode_dirty(handle, inode);
}
}
-
ext4_journal_stop(handle);
-
ext4_double_up_write_data_sem(inode, inode_bl);
journal_err_out:
ext4_inode_resume_unlocked_dio(inode);
ext4_inode_resume_unlocked_dio(inode_bl);
-
unlock_two_nondirectories(inode, inode_bl);
-
iput(inode_bl);
-
-swap_boot_out:
return err;
}
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 04a5c7504be9..a888cac76e9c 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -1808,6 +1808,7 @@ int ext4_mb_find_by_goal(struct ext4_allocation_context *ac,
ext4_lock_group(ac->ac_sb, group);
max = mb_find_extent(e4b, ac->ac_g_ex.fe_start,
ac->ac_g_ex.fe_len, &ex);
+ ex.fe_logical = 0xDEADFA11; /* debug value */
if (max >= ac->ac_g_ex.fe_len && ac->ac_g_ex.fe_len == sbi->s_stripe) {
ext4_fsblk_t start;
@@ -1936,7 +1937,7 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
*/
break;
}
-
+ ex.fe_logical = 0xDEADC0DE; /* debug value */
ext4_mb_measure_extent(ac, &ex, e4b);
i += ex.fe_len;
@@ -1977,6 +1978,7 @@ void ext4_mb_scan_aligned(struct ext4_allocation_context *ac,
max = mb_find_extent(e4b, i, sbi->s_stripe, &ex);
if (max >= sbi->s_stripe) {
ac->ac_found++;
+ ex.fe_logical = 0xDEADF00D; /* debug value */
ac->ac_b_ex = ex;
ext4_mb_use_best_found(ac, e4b);
break;
@@ -4006,8 +4008,7 @@ static void ext4_mb_show_ac(struct ext4_allocation_context *ac)
(unsigned long)ac->ac_b_ex.fe_len,
(unsigned long)ac->ac_b_ex.fe_logical,
(int)ac->ac_criteria);
- ext4_msg(ac->ac_sb, KERN_ERR, "%lu scanned, %d found",
- ac->ac_ex_scanned, ac->ac_found);
+ ext4_msg(ac->ac_sb, KERN_ERR, "%d found", ac->ac_found);
ext4_msg(ac->ac_sb, KERN_ERR, "groups: ");
ngroups = ext4_get_groups_count(sb);
for (i = 0; i < ngroups; i++) {
diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h
index 08481ee84cd5..d634e183b4d4 100644
--- a/fs/ext4/mballoc.h
+++ b/fs/ext4/mballoc.h
@@ -48,7 +48,7 @@ extern ushort ext4_mballoc_debug;
} \
} while (0)
#else
-#define mb_debug(n, fmt, a...)
+#define mb_debug(n, fmt, a...) no_printk(fmt, ## a)
#endif
#define EXT4_MB_HISTORY_ALLOC 1 /* allocation */
@@ -175,8 +175,6 @@ struct ext4_allocation_context {
/* copy of the best found extent taken before preallocation efforts */
struct ext4_free_extent ac_f_ex;
- /* number of iterations done. we have to track to limit searching */
- unsigned long ac_ex_scanned;
__u16 ac_groups_scanned;
__u16 ac_found;
__u16 ac_tail;
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index 773b503bd18c..58ee7dc87669 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -76,7 +76,7 @@ copy_extent_status(struct ext4_extent *src, struct ext4_extent *dest)
* ext4_ext_path structure refers to the last extent, or a negative error
* value on failure.
*/
-static int
+int
mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
struct ext4_extent **extent)
{
@@ -861,8 +861,7 @@ mext_page_mkuptodate(struct page *page, unsigned from, unsigned to)
}
if (!buffer_mapped(bh)) {
zero_user(page, block_start, blocksize);
- if (!err)
- set_buffer_uptodate(bh);
+ set_buffer_uptodate(bh);
continue;
}
}
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index d050e043e884..1cb84f78909e 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3000,6 +3000,154 @@ static struct buffer_head *ext4_get_first_dir_block(handle_t *handle,
return ext4_get_first_inline_block(inode, parent_de, retval);
}
+struct ext4_renament {
+ struct inode *dir;
+ struct dentry *dentry;
+ struct inode *inode;
+ bool is_dir;
+ int dir_nlink_delta;
+
+ /* entry for "dentry" */
+ struct buffer_head *bh;
+ struct ext4_dir_entry_2 *de;
+ int inlined;
+
+ /* entry for ".." in inode if it's a directory */
+ struct buffer_head *dir_bh;
+ struct ext4_dir_entry_2 *parent_de;
+ int dir_inlined;
+};
+
+static int ext4_rename_dir_prepare(handle_t *handle, struct ext4_renament *ent)
+{
+ int retval;
+
+ ent->dir_bh = ext4_get_first_dir_block(handle, ent->inode,
+ &retval, &ent->parent_de,
+ &ent->dir_inlined);
+ if (!ent->dir_bh)
+ return retval;
+ if (le32_to_cpu(ent->parent_de->inode) != ent->dir->i_ino)
+ return -EIO;
+ BUFFER_TRACE(ent->dir_bh, "get_write_access");
+ return ext4_journal_get_write_access(handle, ent->dir_bh);
+}
+
+static int ext4_rename_dir_finish(handle_t *handle, struct ext4_renament *ent,
+ unsigned dir_ino)
+{
+ int retval;
+
+ ent->parent_de->inode = cpu_to_le32(dir_ino);
+ BUFFER_TRACE(ent->dir_bh, "call ext4_handle_dirty_metadata");
+ if (!ent->dir_inlined) {
+ if (is_dx(ent->inode)) {
+ retval = ext4_handle_dirty_dx_node(handle,
+ ent->inode,
+ ent->dir_bh);
+ } else {
+ retval = ext4_handle_dirty_dirent_node(handle,
+ ent->inode,
+ ent->dir_bh);
+ }
+ } else {
+ retval = ext4_mark_inode_dirty(handle, ent->inode);
+ }
+ if (retval) {
+ ext4_std_error(ent->dir->i_sb, retval);
+ return retval;
+ }
+ return 0;
+}
+
+static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
+ unsigned ino, unsigned file_type)
+{
+ int retval;
+
+ BUFFER_TRACE(ent->bh, "get write access");
+ retval = ext4_journal_get_write_access(handle, ent->bh);
+ if (retval)
+ return retval;
+ ent->de->inode = cpu_to_le32(ino);
+ if (EXT4_HAS_INCOMPAT_FEATURE(ent->dir->i_sb,
+ EXT4_FEATURE_INCOMPAT_FILETYPE))
+ ent->de->file_type = file_type;
+ ent->dir->i_version++;
+ ent->dir->i_ctime = ent->dir->i_mtime =
+ ext4_current_time(ent->dir);
+ ext4_mark_inode_dirty(handle, ent->dir);
+ BUFFER_TRACE(ent->bh, "call ext4_handle_dirty_metadata");
+ if (!ent->inlined) {
+ retval = ext4_handle_dirty_dirent_node(handle,
+ ent->dir, ent->bh);
+ if (unlikely(retval)) {
+ ext4_std_error(ent->dir->i_sb, retval);
+ return retval;
+ }
+ }
+ brelse(ent->bh);
+ ent->bh = NULL;
+
+ return 0;
+}
+
+static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
+ const struct qstr *d_name)
+{
+ int retval = -ENOENT;
+ struct buffer_head *bh;
+ struct ext4_dir_entry_2 *de;
+
+ bh = ext4_find_entry(dir, d_name, &de, NULL);
+ if (bh) {
+ retval = ext4_delete_entry(handle, dir, de, bh);
+ brelse(bh);
+ }
+ return retval;
+}
+
+static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent)
+{
+ int retval;
+ /*
+ * ent->de could have moved from under us during htree split, so make
+ * sure that we are deleting the right entry. We might also be pointing
+ * to a stale entry in the unused part of ent->bh so just checking inum
+ * and the name isn't enough.
+ */
+ if (le32_to_cpu(ent->de->inode) != ent->inode->i_ino ||
+ ent->de->name_len != ent->dentry->d_name.len ||
+ strncmp(ent->de->name, ent->dentry->d_name.name,
+ ent->de->name_len)) {
+ retval = ext4_find_delete_entry(handle, ent->dir,
+ &ent->dentry->d_name);
+ } else {
+ retval = ext4_delete_entry(handle, ent->dir, ent->de, ent->bh);
+ if (retval == -ENOENT) {
+ retval = ext4_find_delete_entry(handle, ent->dir,
+ &ent->dentry->d_name);
+ }
+ }
+
+ if (retval) {
+ ext4_warning(ent->dir->i_sb,
+ "Deleting old file (%lu), %d, error=%d",
+ ent->dir->i_ino, ent->dir->i_nlink, retval);
+ }
+}
+
+static void ext4_update_dir_count(handle_t *handle, struct ext4_renament *ent)
+{
+ if (ent->dir_nlink_delta) {
+ if (ent->dir_nlink_delta == -1)
+ ext4_dec_count(handle, ent->dir);
+ else
+ ext4_inc_count(handle, ent->dir);
+ ext4_mark_inode_dirty(handle, ent->dir);
+ }
+}
+
/*
* Anybody can rename anything with this: the permission checks are left to the
* higher-level routines.
@@ -3012,198 +3160,267 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
handle_t *handle = NULL;
- struct inode *old_inode, *new_inode;
- struct buffer_head *old_bh, *new_bh, *dir_bh;
- struct ext4_dir_entry_2 *old_de, *new_de;
+ struct ext4_renament old = {
+ .dir = old_dir,
+ .dentry = old_dentry,
+ .inode = old_dentry->d_inode,
+ };
+ struct ext4_renament new = {
+ .dir = new_dir,
+ .dentry = new_dentry,
+ .inode = new_dentry->d_inode,
+ };
int retval;
- int inlined = 0, new_inlined = 0;
- struct ext4_dir_entry_2 *parent_de;
- dquot_initialize(old_dir);
- dquot_initialize(new_dir);
-
- old_bh = new_bh = dir_bh = NULL;
+ dquot_initialize(old.dir);
+ dquot_initialize(new.dir);
/* Initialize quotas before so that eventual writes go
* in separate transaction */
- if (new_dentry->d_inode)
- dquot_initialize(new_dentry->d_inode);
+ if (new.inode)
+ dquot_initialize(new.inode);
- old_bh = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de, NULL);
+ old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
/*
* Check for inode number is _not_ due to possible IO errors.
* We might rmdir the source, keep it as pwd of some process
* and merrily kill the link to whatever was created under the
* same name. Goodbye sticky bit ;-<
*/
- old_inode = old_dentry->d_inode;
retval = -ENOENT;
- if (!old_bh || le32_to_cpu(old_de->inode) != old_inode->i_ino)
+ if (!old.bh || le32_to_cpu(old.de->inode) != old.inode->i_ino)
goto end_rename;
- new_inode = new_dentry->d_inode;
- new_bh = ext4_find_entry(new_dir, &new_dentry->d_name,
- &new_de, &new_inlined);
- if (new_bh) {
- if (!new_inode) {
- brelse(new_bh);
- new_bh = NULL;
+ new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
+ &new.de, &new.inlined);
+ if (new.bh) {
+ if (!new.inode) {
+ brelse(new.bh);
+ new.bh = NULL;
}
}
- if (new_inode && !test_opt(new_dir->i_sb, NO_AUTO_DA_ALLOC))
- ext4_alloc_da_blocks(old_inode);
+ if (new.inode && !test_opt(new.dir->i_sb, NO_AUTO_DA_ALLOC))
+ ext4_alloc_da_blocks(old.inode);
- handle = ext4_journal_start(old_dir, EXT4_HT_DIR,
- (2 * EXT4_DATA_TRANS_BLOCKS(old_dir->i_sb) +
+ handle = ext4_journal_start(old.dir, EXT4_HT_DIR,
+ (2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2));
if (IS_ERR(handle))
return PTR_ERR(handle);
- if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
+ if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir))
ext4_handle_sync(handle);
- if (S_ISDIR(old_inode->i_mode)) {
- if (new_inode) {
+ if (S_ISDIR(old.inode->i_mode)) {
+ if (new.inode) {
retval = -ENOTEMPTY;
- if (!empty_dir(new_inode))
+ if (!empty_dir(new.inode))
+ goto end_rename;
+ } else {
+ retval = -EMLINK;
+ if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir))
goto end_rename;
}
- retval = -EIO;
- dir_bh = ext4_get_first_dir_block(handle, old_inode,
- &retval, &parent_de,
- &inlined);
- if (!dir_bh)
- goto end_rename;
- if (le32_to_cpu(parent_de->inode) != old_dir->i_ino)
- goto end_rename;
- retval = -EMLINK;
- if (!new_inode && new_dir != old_dir &&
- EXT4_DIR_LINK_MAX(new_dir))
- goto end_rename;
- BUFFER_TRACE(dir_bh, "get_write_access");
- retval = ext4_journal_get_write_access(handle, dir_bh);
+ retval = ext4_rename_dir_prepare(handle, &old);
if (retval)
goto end_rename;
}
- if (!new_bh) {
- retval = ext4_add_entry(handle, new_dentry, old_inode);
+ if (!new.bh) {
+ retval = ext4_add_entry(handle, new.dentry, old.inode);
if (retval)
goto end_rename;
} else {
- BUFFER_TRACE(new_bh, "get write access");
- retval = ext4_journal_get_write_access(handle, new_bh);
+ retval = ext4_setent(handle, &new,
+ old.inode->i_ino, old.de->file_type);
if (retval)
goto end_rename;
- new_de->inode = cpu_to_le32(old_inode->i_ino);
- if (EXT4_HAS_INCOMPAT_FEATURE(new_dir->i_sb,
- EXT4_FEATURE_INCOMPAT_FILETYPE))
- new_de->file_type = old_de->file_type;
- new_dir->i_version++;
- new_dir->i_ctime = new_dir->i_mtime =
- ext4_current_time(new_dir);
- ext4_mark_inode_dirty(handle, new_dir);
- BUFFER_TRACE(new_bh, "call ext4_handle_dirty_metadata");
- if (!new_inlined) {
- retval = ext4_handle_dirty_dirent_node(handle,
- new_dir, new_bh);
- if (unlikely(retval)) {
- ext4_std_error(new_dir->i_sb, retval);
- goto end_rename;
- }
- }
- brelse(new_bh);
- new_bh = NULL;
}
/*
* Like most other Unix systems, set the ctime for inodes on a
* rename.
*/
- old_inode->i_ctime = ext4_current_time(old_inode);
- ext4_mark_inode_dirty(handle, old_inode);
+ old.inode->i_ctime = ext4_current_time(old.inode);
+ ext4_mark_inode_dirty(handle, old.inode);
/*
* ok, that's it
*/
- if (le32_to_cpu(old_de->inode) != old_inode->i_ino ||
- old_de->name_len != old_dentry->d_name.len ||
- strncmp(old_de->name, old_dentry->d_name.name, old_de->name_len) ||
- (retval = ext4_delete_entry(handle, old_dir,
- old_de, old_bh)) == -ENOENT) {
- /* old_de could have moved from under us during htree split, so
- * make sure that we are deleting the right entry. We might
- * also be pointing to a stale entry in the unused part of
- * old_bh so just checking inum and the name isn't enough. */
- struct buffer_head *old_bh2;
- struct ext4_dir_entry_2 *old_de2;
-
- old_bh2 = ext4_find_entry(old_dir, &old_dentry->d_name,
- &old_de2, NULL);
- if (old_bh2) {
- retval = ext4_delete_entry(handle, old_dir,
- old_de2, old_bh2);
- brelse(old_bh2);
- }
+ ext4_rename_delete(handle, &old);
+
+ if (new.inode) {
+ ext4_dec_count(handle, new.inode);
+ new.inode->i_ctime = ext4_current_time(new.inode);
}
- if (retval) {
- ext4_warning(old_dir->i_sb,
- "Deleting old file (%lu), %d, error=%d",
- old_dir->i_ino, old_dir->i_nlink, retval);
- }
-
- if (new_inode) {
- ext4_dec_count(handle, new_inode);
- new_inode->i_ctime = ext4_current_time(new_inode);
- }
- old_dir->i_ctime = old_dir->i_mtime = ext4_current_time(old_dir);
- ext4_update_dx_flag(old_dir);
- if (dir_bh) {
- parent_de->inode = cpu_to_le32(new_dir->i_ino);
- BUFFER_TRACE(dir_bh, "call ext4_handle_dirty_metadata");
- if (!inlined) {
- if (is_dx(old_inode)) {
- retval = ext4_handle_dirty_dx_node(handle,
- old_inode,
- dir_bh);
- } else {
- retval = ext4_handle_dirty_dirent_node(handle,
- old_inode, dir_bh);
- }
- } else {
- retval = ext4_mark_inode_dirty(handle, old_inode);
- }
- if (retval) {
- ext4_std_error(old_dir->i_sb, retval);
+ old.dir->i_ctime = old.dir->i_mtime = ext4_current_time(old.dir);
+ ext4_update_dx_flag(old.dir);
+ if (old.dir_bh) {
+ retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);
+ if (retval)
goto end_rename;
- }
- ext4_dec_count(handle, old_dir);
- if (new_inode) {
+
+ ext4_dec_count(handle, old.dir);
+ if (new.inode) {
/* checked empty_dir above, can't have another parent,
* ext4_dec_count() won't work for many-linked dirs */
- clear_nlink(new_inode);
+ clear_nlink(new.inode);
} else {
- ext4_inc_count(handle, new_dir);
- ext4_update_dx_flag(new_dir);
- ext4_mark_inode_dirty(handle, new_dir);
+ ext4_inc_count(handle, new.dir);
+ ext4_update_dx_flag(new.dir);
+ ext4_mark_inode_dirty(handle, new.dir);
}
}
- ext4_mark_inode_dirty(handle, old_dir);
- if (new_inode) {
- ext4_mark_inode_dirty(handle, new_inode);
- if (!new_inode->i_nlink)
- ext4_orphan_add(handle, new_inode);
+ ext4_mark_inode_dirty(handle, old.dir);
+ if (new.inode) {
+ ext4_mark_inode_dirty(handle, new.inode);
+ if (!new.inode->i_nlink)
+ ext4_orphan_add(handle, new.inode);
}
retval = 0;
end_rename:
- brelse(dir_bh);
- brelse(old_bh);
- brelse(new_bh);
+ brelse(old.dir_bh);
+ brelse(old.bh);
+ brelse(new.bh);
if (handle)
ext4_journal_stop(handle);
return retval;
}
+static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ handle_t *handle = NULL;
+ struct ext4_renament old = {
+ .dir = old_dir,
+ .dentry = old_dentry,
+ .inode = old_dentry->d_inode,
+ };
+ struct ext4_renament new = {
+ .dir = new_dir,
+ .dentry = new_dentry,
+ .inode = new_dentry->d_inode,
+ };
+ u8 new_file_type;
+ int retval;
+
+ dquot_initialize(old.dir);
+ dquot_initialize(new.dir);
+
+ old.bh = ext4_find_entry(old.dir, &old.dentry->d_name,
+ &old.de, &old.inlined);
+ /*
+ * Check for inode number is _not_ due to possible IO errors.
+ * We might rmdir the source, keep it as pwd of some process
+ * and merrily kill the link to whatever was created under the
+ * same name. Goodbye sticky bit ;-<
+ */
+ retval = -ENOENT;
+ if (!old.bh || le32_to_cpu(old.de->inode) != old.inode->i_ino)
+ goto end_rename;
+
+ new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
+ &new.de, &new.inlined);
+
+ /* RENAME_EXCHANGE case: old *and* new must both exist */
+ if (!new.bh || le32_to_cpu(new.de->inode) != new.inode->i_ino)
+ goto end_rename;
+
+ handle = ext4_journal_start(old.dir, EXT4_HT_DIR,
+ (2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) +
+ 2 * EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2));
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir))
+ ext4_handle_sync(handle);
+
+ if (S_ISDIR(old.inode->i_mode)) {
+ old.is_dir = true;
+ retval = ext4_rename_dir_prepare(handle, &old);
+ if (retval)
+ goto end_rename;
+ }
+ if (S_ISDIR(new.inode->i_mode)) {
+ new.is_dir = true;
+ retval = ext4_rename_dir_prepare(handle, &new);
+ if (retval)
+ goto end_rename;
+ }
+
+ /*
+ * Other than the special case of overwriting a directory, parents'
+ * nlink only needs to be modified if this is a cross directory rename.
+ */
+ if (old.dir != new.dir && old.is_dir != new.is_dir) {
+ old.dir_nlink_delta = old.is_dir ? -1 : 1;
+ new.dir_nlink_delta = -old.dir_nlink_delta;
+ retval = -EMLINK;
+ if ((old.dir_nlink_delta > 0 && EXT4_DIR_LINK_MAX(old.dir)) ||
+ (new.dir_nlink_delta > 0 && EXT4_DIR_LINK_MAX(new.dir)))
+ goto end_rename;
+ }
+
+ new_file_type = new.de->file_type;
+ retval = ext4_setent(handle, &new, old.inode->i_ino, old.de->file_type);
+ if (retval)
+ goto end_rename;
+
+ retval = ext4_setent(handle, &old, new.inode->i_ino, new_file_type);
+ if (retval)
+ goto end_rename;
+
+ /*
+ * Like most other Unix systems, set the ctime for inodes on a
+ * rename.
+ */
+ old.inode->i_ctime = ext4_current_time(old.inode);
+ new.inode->i_ctime = ext4_current_time(new.inode);
+ ext4_mark_inode_dirty(handle, old.inode);
+ ext4_mark_inode_dirty(handle, new.inode);
+
+ if (old.dir_bh) {
+ retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);
+ if (retval)
+ goto end_rename;
+ }
+ if (new.dir_bh) {
+ retval = ext4_rename_dir_finish(handle, &new, old.dir->i_ino);
+ if (retval)
+ goto end_rename;
+ }
+ ext4_update_dir_count(handle, &old);
+ ext4_update_dir_count(handle, &new);
+ retval = 0;
+
+end_rename:
+ brelse(old.dir_bh);
+ brelse(new.dir_bh);
+ brelse(old.bh);
+ brelse(new.bh);
+ if (handle)
+ ext4_journal_stop(handle);
+ return retval;
+}
+
+static int ext4_rename2(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ unsigned int flags)
+{
+ if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
+ return -EINVAL;
+
+ if (flags & RENAME_EXCHANGE) {
+ return ext4_cross_rename(old_dir, old_dentry,
+ new_dir, new_dentry);
+ }
+ /*
+ * Existence checking was done by the VFS, otherwise "RENAME_NOREPLACE"
+ * is equivalent to regular rename.
+ */
+ return ext4_rename(old_dir, old_dentry, new_dir, new_dentry);
+}
+
/*
* directories can handle most operations...
*/
@@ -3218,6 +3435,7 @@ const struct inode_operations ext4_dir_inode_operations = {
.mknod = ext4_mknod,
.tmpfile = ext4_tmpfile,
.rename = ext4_rename,
+ .rename2 = ext4_rename2,
.setattr = ext4_setattr,
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 710fed2377d4..f3c667091618 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -59,6 +59,7 @@ static struct kset *ext4_kset;
static struct ext4_lazy_init *ext4_li_info;
static struct mutex ext4_li_mtx;
static struct ext4_features *ext4_feat;
+static int ext4_mballoc_ready;
static int ext4_load_journal(struct super_block *, struct ext4_super_block *,
unsigned long journal_devnum);
@@ -845,6 +846,10 @@ static void ext4_put_super(struct super_block *sb)
invalidate_bdev(sbi->journal_bdev);
ext4_blkdev_remove(sbi);
}
+ if (sbi->s_mb_cache) {
+ ext4_xattr_destroy_cache(sbi->s_mb_cache);
+ sbi->s_mb_cache = NULL;
+ }
if (sbi->s_mmp_tsk)
kthread_stop(sbi->s_mmp_tsk);
sb->s_fs_info = NULL;
@@ -940,7 +945,7 @@ static void init_once(void *foo)
inode_init_once(&ei->vfs_inode);
}
-static int init_inodecache(void)
+static int __init init_inodecache(void)
{
ext4_inode_cachep = kmem_cache_create("ext4_inode_cache",
sizeof(struct ext4_inode_info),
@@ -3575,6 +3580,16 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
"feature flags set on rev 0 fs, "
"running e2fsck is recommended");
+ if (es->s_creator_os == cpu_to_le32(EXT4_OS_HURD)) {
+ set_opt2(sb, HURD_COMPAT);
+ if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+ EXT4_FEATURE_INCOMPAT_64BIT)) {
+ ext4_msg(sb, KERN_ERR,
+ "The Hurd can't support 64-bit file systems");
+ goto failed_mount;
+ }
+ }
+
if (IS_EXT2_SB(sb)) {
if (ext2_feature_set_ok(sb))
ext4_msg(sb, KERN_INFO, "mounting ext2 file system "
@@ -4010,6 +4025,14 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
percpu_counter_set(&sbi->s_dirtyclusters_counter, 0);
no_journal:
+ if (ext4_mballoc_ready) {
+ sbi->s_mb_cache = ext4_xattr_create_cache(sb->s_id);
+ if (!sbi->s_mb_cache) {
+ ext4_msg(sb, KERN_ERR, "Failed to create an mb_cache");
+ goto failed_mount_wq;
+ }
+ }
+
/*
* Get the # of file system overhead blocks from the
* superblock if present.
@@ -4835,6 +4858,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
}
if (*flags & MS_RDONLY) {
+ err = sync_filesystem(sb);
+ if (err < 0)
+ goto restore_opts;
err = dquot_suspend(sb, -1);
if (err < 0)
goto restore_opts;
@@ -5516,11 +5542,9 @@ static int __init ext4_init_fs(void)
err = ext4_init_mballoc();
if (err)
- goto out3;
-
- err = ext4_init_xattr();
- if (err)
goto out2;
+ else
+ ext4_mballoc_ready = 1;
err = init_inodecache();
if (err)
goto out1;
@@ -5536,10 +5560,9 @@ out:
unregister_as_ext3();
destroy_inodecache();
out1:
- ext4_exit_xattr();
-out2:
+ ext4_mballoc_ready = 0;
ext4_exit_mballoc();
-out3:
+out2:
ext4_exit_feat_adverts();
out4:
if (ext4_proc_root)
@@ -5562,7 +5585,6 @@ static void __exit ext4_exit_fs(void)
unregister_as_ext3();
unregister_filesystem(&ext4_fs_type);
destroy_inodecache();
- ext4_exit_xattr();
ext4_exit_mballoc();
ext4_exit_feat_adverts();
remove_proc_entry("fs/ext4", NULL);
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index e175e94116ac..1f5cf5880718 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -81,7 +81,7 @@
# define ea_bdebug(bh, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
#endif
-static void ext4_xattr_cache_insert(struct buffer_head *);
+static void ext4_xattr_cache_insert(struct mb_cache *, struct buffer_head *);
static struct buffer_head *ext4_xattr_cache_find(struct inode *,
struct ext4_xattr_header *,
struct mb_cache_entry **);
@@ -90,8 +90,6 @@ static void ext4_xattr_rehash(struct ext4_xattr_header *,
static int ext4_xattr_list(struct dentry *dentry, char *buffer,
size_t buffer_size);
-static struct mb_cache *ext4_xattr_cache;
-
static const struct xattr_handler *ext4_xattr_handler_map[] = {
[EXT4_XATTR_INDEX_USER] = &ext4_xattr_user_handler,
#ifdef CONFIG_EXT4_FS_POSIX_ACL
@@ -117,6 +115,9 @@ const struct xattr_handler *ext4_xattr_handlers[] = {
NULL
};
+#define EXT4_GET_MB_CACHE(inode) (((struct ext4_sb_info *) \
+ inode->i_sb->s_fs_info)->s_mb_cache)
+
static __le32 ext4_xattr_block_csum(struct inode *inode,
sector_t block_nr,
struct ext4_xattr_header *hdr)
@@ -265,6 +266,7 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name,
struct ext4_xattr_entry *entry;
size_t size;
int error;
+ struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",
name_index, name, buffer, (long)buffer_size);
@@ -286,7 +288,7 @@ bad_block:
error = -EIO;
goto cleanup;
}
- ext4_xattr_cache_insert(bh);
+ ext4_xattr_cache_insert(ext4_mb_cache, bh);
entry = BFIRST(bh);
error = ext4_xattr_find_entry(&entry, name_index, name, bh->b_size, 1);
if (error == -EIO)
@@ -409,6 +411,7 @@ ext4_xattr_block_list(struct dentry *dentry, char *buffer, size_t buffer_size)
struct inode *inode = dentry->d_inode;
struct buffer_head *bh = NULL;
int error;
+ struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
ea_idebug(inode, "buffer=%p, buffer_size=%ld",
buffer, (long)buffer_size);
@@ -430,7 +433,7 @@ ext4_xattr_block_list(struct dentry *dentry, char *buffer, size_t buffer_size)
error = -EIO;
goto cleanup;
}
- ext4_xattr_cache_insert(bh);
+ ext4_xattr_cache_insert(ext4_mb_cache, bh);
error = ext4_xattr_list_entries(dentry, BFIRST(bh), buffer, buffer_size);
cleanup:
@@ -526,8 +529,9 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
{
struct mb_cache_entry *ce = NULL;
int error = 0;
+ struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
- ce = mb_cache_entry_get(ext4_xattr_cache, bh->b_bdev, bh->b_blocknr);
+ ce = mb_cache_entry_get(ext4_mb_cache, bh->b_bdev, bh->b_blocknr);
error = ext4_journal_get_write_access(handle, bh);
if (error)
goto out;
@@ -567,12 +571,13 @@ static size_t ext4_xattr_free_space(struct ext4_xattr_entry *last,
size_t *min_offs, void *base, int *total)
{
for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {
- *total += EXT4_XATTR_LEN(last->e_name_len);
if (!last->e_value_block && last->e_value_size) {
size_t offs = le16_to_cpu(last->e_value_offs);
if (offs < *min_offs)
*min_offs = offs;
}
+ if (total)
+ *total += EXT4_XATTR_LEN(last->e_name_len);
}
return (*min_offs - ((void *)last - base) - sizeof(__u32));
}
@@ -745,13 +750,14 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
struct ext4_xattr_search *s = &bs->s;
struct mb_cache_entry *ce = NULL;
int error = 0;
+ struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
#define header(x) ((struct ext4_xattr_header *)(x))
if (i->value && i->value_len > sb->s_blocksize)
return -ENOSPC;
if (s->base) {
- ce = mb_cache_entry_get(ext4_xattr_cache, bs->bh->b_bdev,
+ ce = mb_cache_entry_get(ext4_mb_cache, bs->bh->b_bdev,
bs->bh->b_blocknr);
error = ext4_journal_get_write_access(handle, bs->bh);
if (error)
@@ -769,7 +775,8 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
if (!IS_LAST_ENTRY(s->first))
ext4_xattr_rehash(header(s->base),
s->here);
- ext4_xattr_cache_insert(bs->bh);
+ ext4_xattr_cache_insert(ext4_mb_cache,
+ bs->bh);
}
unlock_buffer(bs->bh);
if (error == -EIO)
@@ -905,7 +912,7 @@ getblk_failed:
memcpy(new_bh->b_data, s->base, new_bh->b_size);
set_buffer_uptodate(new_bh);
unlock_buffer(new_bh);
- ext4_xattr_cache_insert(new_bh);
+ ext4_xattr_cache_insert(ext4_mb_cache, new_bh);
error = ext4_handle_dirty_xattr_block(handle,
inode, new_bh);
if (error)
@@ -1228,7 +1235,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
struct ext4_xattr_block_find *bs = NULL;
char *buffer = NULL, *b_entry_name = NULL;
size_t min_offs, free;
- int total_ino, total_blk;
+ int total_ino;
void *base, *start, *end;
int extra_isize = 0, error = 0, tried_min_extra_isize = 0;
int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize);
@@ -1286,8 +1293,7 @@ retry:
first = BFIRST(bh);
end = bh->b_data + bh->b_size;
min_offs = end - base;
- free = ext4_xattr_free_space(first, &min_offs, base,
- &total_blk);
+ free = ext4_xattr_free_space(first, &min_offs, base, NULL);
if (free < new_extra_isize) {
if (!tried_min_extra_isize && s_min_extra_isize) {
tried_min_extra_isize++;
@@ -1495,13 +1501,13 @@ ext4_xattr_put_super(struct super_block *sb)
* Returns 0, or a negative error number on failure.
*/
static void
-ext4_xattr_cache_insert(struct buffer_head *bh)
+ext4_xattr_cache_insert(struct mb_cache *ext4_mb_cache, struct buffer_head *bh)
{
__u32 hash = le32_to_cpu(BHDR(bh)->h_hash);
struct mb_cache_entry *ce;
int error;
- ce = mb_cache_entry_alloc(ext4_xattr_cache, GFP_NOFS);
+ ce = mb_cache_entry_alloc(ext4_mb_cache, GFP_NOFS);
if (!ce) {
ea_bdebug(bh, "out of memory");
return;
@@ -1573,12 +1579,13 @@ ext4_xattr_cache_find(struct inode *inode, struct ext4_xattr_header *header,
{
__u32 hash = le32_to_cpu(header->h_hash);
struct mb_cache_entry *ce;
+ struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
if (!header->h_hash)
return NULL; /* never share */
ea_idebug(inode, "looking for cached blocks [%x]", (int)hash);
again:
- ce = mb_cache_entry_find_first(ext4_xattr_cache, inode->i_sb->s_bdev,
+ ce = mb_cache_entry_find_first(ext4_mb_cache, inode->i_sb->s_bdev,
hash);
while (ce) {
struct buffer_head *bh;
@@ -1676,19 +1683,17 @@ static void ext4_xattr_rehash(struct ext4_xattr_header *header,
#undef BLOCK_HASH_SHIFT
-int __init
-ext4_init_xattr(void)
+#define HASH_BUCKET_BITS 10
+
+struct mb_cache *
+ext4_xattr_create_cache(char *name)
{
- ext4_xattr_cache = mb_cache_create("ext4_xattr", 6);
- if (!ext4_xattr_cache)
- return -ENOMEM;
- return 0;
+ return mb_cache_create(name, HASH_BUCKET_BITS);
}
-void
-ext4_exit_xattr(void)
+void ext4_xattr_destroy_cache(struct mb_cache *cache)
{
- if (ext4_xattr_cache)
- mb_cache_destroy(ext4_xattr_cache);
- ext4_xattr_cache = NULL;
+ if (cache)
+ mb_cache_destroy(cache);
}
+
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 819d6398833f..29bedf5589f6 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -110,9 +110,6 @@ extern void ext4_xattr_put_super(struct super_block *);
extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
struct ext4_inode *raw_inode, handle_t *handle);
-extern int __init ext4_init_xattr(void);
-extern void ext4_exit_xattr(void);
-
extern const struct xattr_handler *ext4_xattr_handlers[];
extern int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
@@ -124,6 +121,9 @@ extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
struct ext4_xattr_info *i,
struct ext4_xattr_ibody_find *is);
+extern struct mb_cache *ext4_xattr_create_cache(char *name);
+extern void ext4_xattr_destroy_cache(struct mb_cache *);
+
#ifdef CONFIG_EXT4_FS_SECURITY
extern int ext4_init_security(handle_t *handle, struct inode *inode,
struct inode *dir, const struct qstr *qstr);
diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
index fa8da4cb8c4b..e93e4ec7d165 100644
--- a/fs/f2fs/acl.c
+++ b/fs/f2fs/acl.c
@@ -174,7 +174,7 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
retval = f2fs_getxattr(inode, name_index, "", NULL, 0);
if (retval > 0) {
- value = kmalloc(retval, GFP_KERNEL);
+ value = kmalloc(retval, GFP_F2FS_ZERO);
if (!value)
return ERR_PTR(-ENOMEM);
retval = f2fs_getxattr(inode, name_index, "", value, retval);
@@ -203,6 +203,12 @@ static int __f2fs_set_acl(struct inode *inode, int type,
size_t size = 0;
int error;
+ if (acl) {
+ error = posix_acl_valid(acl);
+ if (error < 0)
+ return error;
+ }
+
switch (type) {
case ACL_TYPE_ACCESS:
name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 293d0486a40f..4aa521aa9bc3 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -33,14 +33,12 @@ struct page *grab_meta_page(struct f2fs_sb_info *sbi, pgoff_t index)
struct address_space *mapping = META_MAPPING(sbi);
struct page *page = NULL;
repeat:
- page = grab_cache_page(mapping, index);
+ page = grab_cache_page_write_begin(mapping, index, AOP_FLAG_NOFS);
if (!page) {
cond_resched();
goto repeat;
}
- /* We wait writeback only inside grab_meta_page() */
- wait_on_page_writeback(page);
SetPageUptodate(page);
return page;
}
@@ -75,23 +73,102 @@ out:
return page;
}
+inline int get_max_meta_blks(struct f2fs_sb_info *sbi, int type)
+{
+ switch (type) {
+ case META_NAT:
+ return NM_I(sbi)->max_nid / NAT_ENTRY_PER_BLOCK;
+ case META_SIT:
+ return SIT_BLK_CNT(sbi);
+ case META_SSA:
+ case META_CP:
+ return 0;
+ default:
+ BUG();
+ }
+}
+
+/*
+ * Readahead CP/NAT/SIT/SSA pages
+ */
+int ra_meta_pages(struct f2fs_sb_info *sbi, int start, int nrpages, int type)
+{
+ block_t prev_blk_addr = 0;
+ struct page *page;
+ int blkno = start;
+ int max_blks = get_max_meta_blks(sbi, type);
+
+ struct f2fs_io_info fio = {
+ .type = META,
+ .rw = READ_SYNC | REQ_META | REQ_PRIO
+ };
+
+ for (; nrpages-- > 0; blkno++) {
+ block_t blk_addr;
+
+ switch (type) {
+ case META_NAT:
+ /* get nat block addr */
+ if (unlikely(blkno >= max_blks))
+ blkno = 0;
+ blk_addr = current_nat_addr(sbi,
+ blkno * NAT_ENTRY_PER_BLOCK);
+ break;
+ case META_SIT:
+ /* get sit block addr */
+ if (unlikely(blkno >= max_blks))
+ goto out;
+ blk_addr = current_sit_addr(sbi,
+ blkno * SIT_ENTRY_PER_BLOCK);
+ if (blkno != start && prev_blk_addr + 1 != blk_addr)
+ goto out;
+ prev_blk_addr = blk_addr;
+ break;
+ case META_SSA:
+ case META_CP:
+ /* get ssa/cp block addr */
+ blk_addr = blkno;
+ break;
+ default:
+ BUG();
+ }
+
+ page = grab_cache_page(META_MAPPING(sbi), blk_addr);
+ if (!page)
+ continue;
+ if (PageUptodate(page)) {
+ mark_page_accessed(page);
+ f2fs_put_page(page, 1);
+ continue;
+ }
+
+ f2fs_submit_page_mbio(sbi, page, blk_addr, &fio);
+ mark_page_accessed(page);
+ f2fs_put_page(page, 0);
+ }
+out:
+ f2fs_submit_merged_bio(sbi, META, READ);
+ return blkno - start;
+}
+
static int f2fs_write_meta_page(struct page *page,
struct writeback_control *wbc)
{
struct inode *inode = page->mapping->host;
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
- /* Should not write any meta pages, if any IO error was occurred */
- if (unlikely(sbi->por_doing ||
- is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ERROR_FLAG)))
+ if (unlikely(sbi->por_doing))
goto redirty_out;
-
if (wbc->for_reclaim)
goto redirty_out;
- wait_on_page_writeback(page);
+ /* Should not write any meta pages, if any IO error was occurred */
+ if (unlikely(is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ERROR_FLAG)))
+ goto no_write;
+ f2fs_wait_on_page_writeback(page, META);
write_meta_page(sbi, page);
+no_write:
dec_page_count(sbi, F2FS_DIRTY_META);
unlock_page(page);
return 0;
@@ -99,6 +176,7 @@ static int f2fs_write_meta_page(struct page *page,
redirty_out:
dec_page_count(sbi, F2FS_DIRTY_META);
wbc->pages_skipped++;
+ account_page_redirty(page);
set_page_dirty(page);
return AOP_WRITEPAGE_ACTIVATE;
}
@@ -107,21 +185,23 @@ static int f2fs_write_meta_pages(struct address_space *mapping,
struct writeback_control *wbc)
{
struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb);
- int nrpages = MAX_BIO_BLOCKS(max_hw_blocks(sbi));
- long written;
-
- if (wbc->for_kupdate)
- return 0;
+ long diff, written;
/* collect a number of dirty meta pages and write together */
- if (get_pages(sbi, F2FS_DIRTY_META) < nrpages)
- return 0;
+ if (wbc->for_kupdate ||
+ get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META))
+ goto skip_write;
/* if mounting is failed, skip writing node pages */
mutex_lock(&sbi->cp_mutex);
- written = sync_meta_pages(sbi, META, nrpages);
+ diff = nr_pages_to_write(sbi, META, wbc);
+ written = sync_meta_pages(sbi, META, wbc->nr_to_write);
mutex_unlock(&sbi->cp_mutex);
- wbc->nr_to_write -= written;
+ wbc->nr_to_write = max((long)0, wbc->nr_to_write - written - diff);
+ return 0;
+
+skip_write:
+ wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_META);
return 0;
}
@@ -148,10 +228,22 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type,
for (i = 0; i < nr_pages; i++) {
struct page *page = pvec.pages[i];
+
lock_page(page);
- f2fs_bug_on(page->mapping != mapping);
- f2fs_bug_on(!PageDirty(page));
- clear_page_dirty_for_io(page);
+
+ if (unlikely(page->mapping != mapping)) {
+continue_unlock:
+ unlock_page(page);
+ continue;
+ }
+ if (!PageDirty(page)) {
+ /* someone wrote it for us */
+ goto continue_unlock;
+ }
+
+ if (!clear_page_dirty_for_io(page))
+ goto continue_unlock;
+
if (f2fs_write_meta_page(page, &wbc)) {
unlock_page(page);
break;
@@ -216,16 +308,15 @@ void release_orphan_inode(struct f2fs_sb_info *sbi)
void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
{
- struct list_head *head, *this;
- struct orphan_inode_entry *new = NULL, *orphan = NULL;
+ struct list_head *head;
+ struct orphan_inode_entry *new, *orphan;
new = f2fs_kmem_cache_alloc(orphan_entry_slab, GFP_ATOMIC);
new->ino = ino;
spin_lock(&sbi->orphan_inode_lock);
head = &sbi->orphan_inode_list;
- list_for_each(this, head) {
- orphan = list_entry(this, struct orphan_inode_entry, list);
+ list_for_each_entry(orphan, head, list) {
if (orphan->ino == ino) {
spin_unlock(&sbi->orphan_inode_lock);
kmem_cache_free(orphan_entry_slab, new);
@@ -234,14 +325,10 @@ void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
if (orphan->ino > ino)
break;
- orphan = NULL;
}
- /* add new_oentry into list which is sorted by inode number */
- if (orphan)
- list_add(&new->list, this->prev);
- else
- list_add_tail(&new->list, head);
+ /* add new orphan entry into list which is sorted by inode number */
+ list_add_tail(&new->list, &orphan->list);
spin_unlock(&sbi->orphan_inode_lock);
}
@@ -255,10 +342,11 @@ void remove_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
list_for_each_entry(orphan, head, list) {
if (orphan->ino == ino) {
list_del(&orphan->list);
- kmem_cache_free(orphan_entry_slab, orphan);
f2fs_bug_on(sbi->n_orphans == 0);
sbi->n_orphans--;
- break;
+ spin_unlock(&sbi->orphan_inode_lock);
+ kmem_cache_free(orphan_entry_slab, orphan);
+ return;
}
}
spin_unlock(&sbi->orphan_inode_lock);
@@ -285,6 +373,8 @@ void recover_orphan_inodes(struct f2fs_sb_info *sbi)
start_blk = __start_cp_addr(sbi) + 1;
orphan_blkaddr = __start_sum_addr(sbi) - 1;
+ ra_meta_pages(sbi, start_blk, orphan_blkaddr, META_CP);
+
for (i = 0; i < orphan_blkaddr; i++) {
struct page *page = get_meta_page(sbi, start_blk + i);
struct f2fs_orphan_block *orphan_blk;
@@ -466,14 +556,12 @@ static int __add_dirty_inode(struct inode *inode, struct dir_inode_entry *new)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct list_head *head = &sbi->dir_inode_list;
- struct list_head *this;
+ struct dir_inode_entry *entry;
- list_for_each(this, head) {
- struct dir_inode_entry *entry;
- entry = list_entry(this, struct dir_inode_entry, list);
+ list_for_each_entry(entry, head, list)
if (unlikely(entry->inode == inode))
return -EEXIST;
- }
+
list_add_tail(&new->list, head);
stat_inc_dirty_dir(sbi);
return 0;
@@ -483,6 +571,7 @@ void set_dirty_dir_page(struct inode *inode, struct page *page)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct dir_inode_entry *new;
+ int ret = 0;
if (!S_ISDIR(inode->i_mode))
return;
@@ -492,13 +581,13 @@ void set_dirty_dir_page(struct inode *inode, struct page *page)
INIT_LIST_HEAD(&new->list);
spin_lock(&sbi->dir_inode_lock);
- if (__add_dirty_inode(inode, new))
- kmem_cache_free(inode_entry_slab, new);
-
- inc_page_count(sbi, F2FS_DIRTY_DENTS);
+ ret = __add_dirty_inode(inode, new);
inode_inc_dirty_dents(inode);
SetPagePrivate(page);
spin_unlock(&sbi->dir_inode_lock);
+
+ if (ret)
+ kmem_cache_free(inode_entry_slab, new);
}
void add_dirty_dir_inode(struct inode *inode)
@@ -506,44 +595,47 @@ void add_dirty_dir_inode(struct inode *inode)
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct dir_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);
- if (__add_dirty_inode(inode, new))
- kmem_cache_free(inode_entry_slab, new);
+ ret = __add_dirty_inode(inode, new);
spin_unlock(&sbi->dir_inode_lock);
+
+ if (ret)
+ kmem_cache_free(inode_entry_slab, new);
}
void remove_dirty_dir_inode(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
-
- struct list_head *this, *head;
+ struct list_head *head;
+ struct dir_inode_entry *entry;
if (!S_ISDIR(inode->i_mode))
return;
spin_lock(&sbi->dir_inode_lock);
- if (atomic_read(&F2FS_I(inode)->dirty_dents)) {
+ if (get_dirty_dents(inode)) {
spin_unlock(&sbi->dir_inode_lock);
return;
}
head = &sbi->dir_inode_list;
- list_for_each(this, head) {
- struct dir_inode_entry *entry;
- entry = list_entry(this, struct dir_inode_entry, list);
+ list_for_each_entry(entry, head, list) {
if (entry->inode == inode) {
list_del(&entry->list);
- kmem_cache_free(inode_entry_slab, entry);
stat_dec_dirty_dir(sbi);
- break;
+ spin_unlock(&sbi->dir_inode_lock);
+ kmem_cache_free(inode_entry_slab, entry);
+ goto done;
}
}
spin_unlock(&sbi->dir_inode_lock);
+done:
/* 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);
@@ -554,15 +646,14 @@ void remove_dirty_dir_inode(struct inode *inode)
struct inode *check_dirty_dir_inode(struct f2fs_sb_info *sbi, nid_t ino)
{
- struct list_head *this, *head;
+ struct list_head *head;
struct inode *inode = NULL;
+ struct dir_inode_entry *entry;
spin_lock(&sbi->dir_inode_lock);
head = &sbi->dir_inode_list;
- list_for_each(this, head) {
- struct dir_inode_entry *entry;
- entry = list_entry(this, struct dir_inode_entry, list);
+ list_for_each_entry(entry, head, list) {
if (entry->inode->i_ino == ino) {
inode = entry->inode;
break;
@@ -589,7 +680,7 @@ retry:
inode = igrab(entry->inode);
spin_unlock(&sbi->dir_inode_lock);
if (inode) {
- filemap_flush(inode->i_mapping);
+ filemap_fdatawrite(inode->i_mapping);
iput(inode);
} else {
/*
@@ -824,6 +915,7 @@ void write_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
unblock_operations(sbi);
mutex_unlock(&sbi->cp_mutex);
+ stat_inc_cp_count(sbi->stat_info);
trace_f2fs_write_checkpoint(sbi->sb, is_umount, "finish checkpoint");
}
@@ -845,11 +937,11 @@ void init_orphan_info(struct f2fs_sb_info *sbi)
int __init create_checkpoint_caches(void)
{
orphan_entry_slab = f2fs_kmem_cache_create("f2fs_orphan_entry",
- sizeof(struct orphan_inode_entry), NULL);
+ sizeof(struct orphan_inode_entry));
if (!orphan_entry_slab)
return -ENOMEM;
inode_entry_slab = f2fs_kmem_cache_create("f2fs_dirty_dir_entry",
- sizeof(struct dir_inode_entry), NULL);
+ sizeof(struct dir_inode_entry));
if (!inode_entry_slab) {
kmem_cache_destroy(orphan_entry_slab);
return -ENOMEM;
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 2261ccdd0b5f..45abd60e2bff 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -45,7 +45,7 @@ static void f2fs_read_end_io(struct bio *bio, int err)
static void f2fs_write_end_io(struct bio *bio, int err)
{
- struct f2fs_sb_info *sbi = F2FS_SB(bio->bi_io_vec->bv_page->mapping->host->i_sb);
+ struct f2fs_sb_info *sbi = bio->bi_private;
struct bio_vec *bvec;
int i;
@@ -55,15 +55,16 @@ static void f2fs_write_end_io(struct bio *bio, int err)
if (unlikely(err)) {
SetPageError(page);
set_bit(AS_EIO, &page->mapping->flags);
- set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG);
- sbi->sb->s_flags |= MS_RDONLY;
+ f2fs_stop_checkpoint(sbi);
}
end_page_writeback(page);
dec_page_count(sbi, F2FS_WRITEBACK);
}
- if (bio->bi_private)
- complete(bio->bi_private);
+ if (sbi->wait_io) {
+ complete(sbi->wait_io);
+ sbi->wait_io = NULL;
+ }
if (!get_pages(sbi, F2FS_WRITEBACK) &&
!list_empty(&sbi->cp_wait.task_list))
@@ -86,6 +87,7 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr,
bio->bi_bdev = sbi->sb->s_bdev;
bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr);
bio->bi_end_io = is_read ? f2fs_read_end_io : f2fs_write_end_io;
+ bio->bi_private = sbi;
return bio;
}
@@ -113,7 +115,7 @@ static void __submit_merged_bio(struct f2fs_bio_info *io)
*/
if (fio->type == META_FLUSH) {
DECLARE_COMPLETION_ONSTACK(wait);
- io->bio->bi_private = &wait;
+ io->sbi->wait_io = &wait;
submit_bio(rw, io->bio);
wait_for_completion(&wait);
} else {
@@ -132,7 +134,7 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
io = is_read_io(rw) ? &sbi->read_io : &sbi->write_io[btype];
- mutex_lock(&io->io_mutex);
+ down_write(&io->io_rwsem);
/* change META to META_FLUSH in the checkpoint procedure */
if (type >= META_FLUSH) {
@@ -140,7 +142,7 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
io->fio.rw = WRITE_FLUSH_FUA | REQ_META | REQ_PRIO;
}
__submit_merged_bio(io);
- mutex_unlock(&io->io_mutex);
+ up_write(&io->io_rwsem);
}
/*
@@ -178,7 +180,7 @@ void f2fs_submit_page_mbio(struct f2fs_sb_info *sbi, struct page *page,
verify_block_addr(sbi, blk_addr);
- mutex_lock(&io->io_mutex);
+ down_write(&io->io_rwsem);
if (!is_read)
inc_page_count(sbi, F2FS_WRITEBACK);
@@ -202,7 +204,7 @@ alloc_new:
io->last_block_in_bio = blk_addr;
- mutex_unlock(&io->io_mutex);
+ up_write(&io->io_rwsem);
trace_f2fs_submit_page_mbio(page, fio->rw, fio->type, blk_addr);
}
@@ -797,48 +799,36 @@ static int f2fs_write_data_page(struct page *page,
*/
offset = i_size & (PAGE_CACHE_SIZE - 1);
if ((page->index >= end_index + 1) || !offset) {
- if (S_ISDIR(inode->i_mode)) {
- dec_page_count(sbi, F2FS_DIRTY_DENTS);
- inode_dec_dirty_dents(inode);
- }
+ inode_dec_dirty_dents(inode);
goto out;
}
zero_user_segment(page, offset, PAGE_CACHE_SIZE);
write:
- if (unlikely(sbi->por_doing)) {
- err = AOP_WRITEPAGE_ACTIVATE;
+ if (unlikely(sbi->por_doing))
goto redirty_out;
- }
/* Dentry blocks are controlled by checkpoint */
if (S_ISDIR(inode->i_mode)) {
- dec_page_count(sbi, F2FS_DIRTY_DENTS);
inode_dec_dirty_dents(inode);
err = do_write_data_page(page, &fio);
- } else {
- f2fs_lock_op(sbi);
-
- if (f2fs_has_inline_data(inode) || f2fs_may_inline(inode)) {
- err = f2fs_write_inline_data(inode, page, offset);
- f2fs_unlock_op(sbi);
- goto out;
- } else {
- err = do_write_data_page(page, &fio);
- }
+ goto done;
+ }
- f2fs_unlock_op(sbi);
+ if (!wbc->for_reclaim)
need_balance_fs = true;
- }
- if (err == -ENOENT)
- goto out;
- else if (err)
+ else if (has_not_enough_free_secs(sbi, 0))
goto redirty_out;
- if (wbc->for_reclaim) {
- f2fs_submit_merged_bio(sbi, DATA, WRITE);
- need_balance_fs = false;
- }
+ f2fs_lock_op(sbi);
+ if (f2fs_has_inline_data(inode) || f2fs_may_inline(inode))
+ err = f2fs_write_inline_data(inode, page, offset);
+ else
+ err = do_write_data_page(page, &fio);
+ f2fs_unlock_op(sbi);
+done:
+ if (err && err != -ENOENT)
+ goto redirty_out;
clear_cold_data(page);
out:
@@ -849,12 +839,11 @@ out:
redirty_out:
wbc->pages_skipped++;
+ account_page_redirty(page);
set_page_dirty(page);
- return err;
+ return AOP_WRITEPAGE_ACTIVATE;
}
-#define MAX_DESIRED_PAGES_WP 4096
-
static int __f2fs_writepage(struct page *page, struct writeback_control *wbc,
void *data)
{
@@ -871,17 +860,17 @@ static int f2fs_write_data_pages(struct address_space *mapping,
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
bool locked = false;
int ret;
- long excess_nrtw = 0, desired_nrtw;
+ long diff;
/* deal with chardevs and other special file */
if (!mapping->a_ops->writepage)
return 0;
- if (wbc->nr_to_write < MAX_DESIRED_PAGES_WP) {
- desired_nrtw = MAX_DESIRED_PAGES_WP;
- excess_nrtw = desired_nrtw - wbc->nr_to_write;
- wbc->nr_to_write = desired_nrtw;
- }
+ if (S_ISDIR(inode->i_mode) && wbc->sync_mode == WB_SYNC_NONE &&
+ get_dirty_dents(inode) < nr_pages_to_skip(sbi, DATA))
+ goto skip_write;
+
+ diff = nr_pages_to_write(sbi, DATA, wbc);
if (!S_ISDIR(inode->i_mode)) {
mutex_lock(&sbi->writepages);
@@ -895,8 +884,12 @@ static int f2fs_write_data_pages(struct address_space *mapping,
remove_dirty_dir_inode(inode);
- wbc->nr_to_write -= excess_nrtw;
+ wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff);
return ret;
+
+skip_write:
+ wbc->pages_skipped += get_dirty_dents(inode);
+ return 0;
}
static int f2fs_write_begin(struct file *file, struct address_space *mapping,
@@ -949,13 +942,19 @@ inline_data:
if (dn.data_blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
} else {
- if (f2fs_has_inline_data(inode))
+ if (f2fs_has_inline_data(inode)) {
err = f2fs_read_inline_data(inode, page);
- else
+ if (err) {
+ page_cache_release(page);
+ return err;
+ }
+ } else {
err = f2fs_submit_page_bio(sbi, page, dn.data_blkaddr,
READ_SYNC);
- if (err)
- return err;
+ if (err)
+ return err;
+ }
+
lock_page(page);
if (unlikely(!PageUptodate(page))) {
f2fs_put_page(page, 1);
@@ -1031,11 +1030,8 @@ static void f2fs_invalidate_data_page(struct page *page, unsigned int offset,
unsigned int length)
{
struct inode *inode = page->mapping->host;
- struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
- if (S_ISDIR(inode->i_mode) && PageDirty(page)) {
- dec_page_count(sbi, F2FS_DIRTY_DENTS);
+ if (PageDirty(page))
inode_dec_dirty_dents(inode);
- }
ClearPagePrivate(page);
}
diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c
index 3de9d20d0c14..b52c12cf5873 100644
--- a/fs/f2fs/debug.c
+++ b/fs/f2fs/debug.c
@@ -86,7 +86,6 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
{
struct f2fs_stat_info *si = F2FS_STAT(sbi);
unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist;
- struct sit_info *sit_i = SIT_I(sbi);
unsigned int segno, vblocks;
int ndirty = 0;
@@ -94,7 +93,6 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
total_vblocks = 0;
blks_per_sec = sbi->segs_per_sec * (1 << sbi->log_blocks_per_seg);
hblks_per_sec = blks_per_sec / 2;
- mutex_lock(&sit_i->sentry_lock);
for (segno = 0; segno < TOTAL_SEGS(sbi); segno += sbi->segs_per_sec) {
vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec);
dist = abs(vblocks - hblks_per_sec);
@@ -105,7 +103,6 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
ndirty++;
}
}
- mutex_unlock(&sit_i->sentry_lock);
dist = TOTAL_SECS(sbi) * hblks_per_sec * hblks_per_sec / 100;
si->bimodal = bimodal / dist;
if (si->dirty_count)
@@ -236,6 +233,7 @@ 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, "GC calls: %d (BG: %d)\n",
si->call_count, si->bg_gc);
seq_printf(s, " - data segments : %d\n", si->data_segs);
@@ -252,10 +250,10 @@ static int stat_show(struct seq_file *s, void *v)
si->ndirty_dent, si->ndirty_dirs);
seq_printf(s, " - meta: %4d in %4d\n",
si->ndirty_meta, si->meta_pages);
- seq_printf(s, " - NATs: %5d > %lu\n",
- si->nats, NM_WOUT_THRESHOLD);
- seq_printf(s, " - SITs: %5d\n - free_nids: %5d\n",
- si->sits, si->fnids);
+ seq_printf(s, " - NATs: %9d\n - SITs: %9d\n",
+ si->nats, si->sits);
+ seq_printf(s, " - free_nids: %9d\n",
+ si->fnids);
seq_puts(s, "\nDistribution of User Blocks:");
seq_puts(s, " [ valid | invalid | free ]\n");
seq_puts(s, " [");
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 2b7c255bcbdf..972fd0ef230f 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -21,12 +21,12 @@ static unsigned long dir_blocks(struct inode *inode)
>> PAGE_CACHE_SHIFT;
}
-static unsigned int dir_buckets(unsigned int level)
+static unsigned int dir_buckets(unsigned int level, int dir_level)
{
if (level < MAX_DIR_HASH_DEPTH / 2)
- return 1 << level;
+ return 1 << (level + dir_level);
else
- return 1 << ((MAX_DIR_HASH_DEPTH / 2) - 1);
+ return 1 << ((MAX_DIR_HASH_DEPTH / 2 + dir_level) - 1);
}
static unsigned int bucket_blocks(unsigned int level)
@@ -65,13 +65,14 @@ static void set_de_type(struct f2fs_dir_entry *de, struct inode *inode)
de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
}
-static unsigned long dir_block_index(unsigned int level, unsigned int idx)
+static unsigned long dir_block_index(unsigned int level,
+ int dir_level, unsigned int idx)
{
unsigned long i;
unsigned long bidx = 0;
for (i = 0; i < level; i++)
- bidx += dir_buckets(i) * bucket_blocks(i);
+ bidx += dir_buckets(i, dir_level) * bucket_blocks(i);
bidx += idx * bucket_blocks(level);
return bidx;
}
@@ -93,16 +94,21 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
f2fs_hash_t namehash, struct page **res_page)
{
struct f2fs_dir_entry *de;
- unsigned long bit_pos, end_pos, next_pos;
+ unsigned long bit_pos = 0;
struct f2fs_dentry_block *dentry_blk = kmap(dentry_page);
- int slots;
+ const void *dentry_bits = &dentry_blk->dentry_bitmap;
+ int max_len = 0;
- bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
- NR_DENTRY_IN_BLOCK, 0);
while (bit_pos < NR_DENTRY_IN_BLOCK) {
+ if (!test_bit_le(bit_pos, dentry_bits)) {
+ if (bit_pos == 0)
+ max_len = 1;
+ else if (!test_bit_le(bit_pos - 1, dentry_bits))
+ max_len++;
+ bit_pos++;
+ continue;
+ }
de = &dentry_blk->dentry[bit_pos];
- slots = GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
-
if (early_match_name(name, namelen, namehash, de)) {
if (!memcmp(dentry_blk->filename[bit_pos],
name, namelen)) {
@@ -110,20 +116,18 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
goto found;
}
}
- next_pos = bit_pos + slots;
- bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
- NR_DENTRY_IN_BLOCK, next_pos);
- if (bit_pos >= NR_DENTRY_IN_BLOCK)
- end_pos = NR_DENTRY_IN_BLOCK;
- else
- end_pos = bit_pos;
- if (*max_slots < end_pos - next_pos)
- *max_slots = end_pos - next_pos;
+ if (max_len > *max_slots) {
+ *max_slots = max_len;
+ max_len = 0;
+ }
+ bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
}
de = NULL;
kunmap(dentry_page);
found:
+ if (max_len > *max_slots)
+ *max_slots = max_len;
return de;
}
@@ -141,10 +145,11 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
f2fs_bug_on(level > MAX_DIR_HASH_DEPTH);
- nbucket = dir_buckets(level);
+ nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
nblock = bucket_blocks(level);
- bidx = dir_block_index(level, le32_to_cpu(namehash) % nbucket);
+ bidx = dir_block_index(level, F2FS_I(dir)->i_dir_level,
+ le32_to_cpu(namehash) % nbucket);
end_block = bidx + nblock;
for (; bidx < end_block; bidx++) {
@@ -248,7 +253,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
struct page *page, struct inode *inode)
{
lock_page(page);
- wait_on_page_writeback(page);
+ f2fs_wait_on_page_writeback(page, DATA);
de->ino = cpu_to_le32(inode->i_ino);
set_de_type(de, inode);
kunmap(page);
@@ -347,14 +352,11 @@ static struct page *init_inode_metadata(struct inode *inode,
err = f2fs_init_security(inode, dir, name, page);
if (err)
goto put_error;
-
- wait_on_page_writeback(page);
} else {
page = get_node_page(F2FS_SB(dir->i_sb), inode->i_ino);
if (IS_ERR(page))
return page;
- wait_on_page_writeback(page);
set_cold_node(inode, page);
}
@@ -372,6 +374,10 @@ static struct page *init_inode_metadata(struct inode *inode,
put_error:
f2fs_put_page(page, 1);
+ /* once the failed inode becomes a bad inode, i_mode is S_IFREG */
+ truncate_inode_pages(&inode->i_data, 0);
+ truncate_blocks(inode, 0);
+ remove_dirty_dir_inode(inode);
error:
remove_inode_page(inode);
return ERR_PTR(err);
@@ -395,9 +401,6 @@ static void update_parent_metadata(struct inode *dir, struct inode *inode,
set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
}
- if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR))
- update_inode_page(dir);
-
if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK))
clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
}
@@ -464,10 +467,11 @@ start:
if (level == current_depth)
++current_depth;
- nbucket = dir_buckets(level);
+ nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
nblock = bucket_blocks(level);
- bidx = dir_block_index(level, (le32_to_cpu(dentry_hash) % nbucket));
+ bidx = dir_block_index(level, F2FS_I(dir)->i_dir_level,
+ (le32_to_cpu(dentry_hash) % nbucket));
for (block = bidx; block <= (bidx + nblock - 1); block++) {
dentry_page = get_new_data_page(dir, NULL, block, true);
@@ -487,8 +491,9 @@ start:
++level;
goto start;
add_dentry:
- wait_on_page_writeback(dentry_page);
+ f2fs_wait_on_page_writeback(dentry_page, DATA);
+ down_write(&F2FS_I(inode)->i_sem);
page = init_inode_metadata(inode, dir, name);
if (IS_ERR(page)) {
err = PTR_ERR(page);
@@ -511,7 +516,12 @@ add_dentry:
update_parent_metadata(dir, inode, current_depth);
fail:
- clear_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
+ up_write(&F2FS_I(inode)->i_sem);
+
+ if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) {
+ update_inode_page(dir);
+ clear_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
+ }
kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
return err;
@@ -528,13 +538,12 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
unsigned int bit_pos;
struct address_space *mapping = page->mapping;
struct inode *dir = mapping->host;
- struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
void *kaddr = page_address(page);
int i;
lock_page(page);
- wait_on_page_writeback(page);
+ f2fs_wait_on_page_writeback(page, DATA);
dentry_blk = (struct f2fs_dentry_block *)kaddr;
bit_pos = dentry - (struct f2fs_dir_entry *)dentry_blk->dentry;
@@ -551,6 +560,10 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
if (inode) {
+ struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
+
+ down_write(&F2FS_I(inode)->i_sem);
+
if (S_ISDIR(inode->i_mode)) {
drop_nlink(dir);
update_inode_page(dir);
@@ -561,6 +574,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
drop_nlink(inode);
i_size_write(inode, 0);
}
+ up_write(&F2FS_I(inode)->i_sem);
update_inode_page(inode);
if (inode->i_nlink == 0)
@@ -573,7 +587,6 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
truncate_hole(dir, page->index, page->index + 1);
clear_page_dirty_for_io(page);
ClearPageUptodate(page);
- dec_page_count(sbi, F2FS_DIRTY_DENTS);
inode_dec_dirty_dents(dir);
}
f2fs_put_page(page, 1);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index fc3c558cb4f3..2ecac8312359 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -40,6 +40,7 @@
#define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040
#define F2FS_MOUNT_INLINE_XATTR 0x00000080
#define F2FS_MOUNT_INLINE_DATA 0x00000100
+#define F2FS_MOUNT_FLUSH_MERGE 0x00000200
#define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option)
#define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option)
@@ -88,6 +89,16 @@ enum {
SIT_BITMAP
};
+/*
+ * For CP/NAT/SIT/SSA readahead
+ */
+enum {
+ META_CP,
+ META_NAT,
+ META_SIT,
+ META_SSA
+};
+
/* for the list of orphan inodes */
struct orphan_inode_entry {
struct list_head list; /* list head */
@@ -187,16 +198,20 @@ struct extent_info {
#define FADVISE_COLD_BIT 0x01
#define FADVISE_LOST_PINO_BIT 0x02
+#define DEF_DIR_LEVEL 0
+
struct f2fs_inode_info {
struct inode vfs_inode; /* serve a vfs inode */
unsigned long i_flags; /* keep an inode flags for ioctl */
unsigned char i_advise; /* use to give file attribute hints */
+ unsigned char i_dir_level; /* use for dentry level for large dir */
unsigned int i_current_depth; /* use only in directory structure */
unsigned int i_pino; /* parent inode number */
umode_t i_acl_mode; /* keep file acl mode temporarily */
/* Use below internally in f2fs*/
unsigned long flags; /* use to pass per-file flags */
+ struct rw_semaphore i_sem; /* protect fi info */
atomic_t dirty_dents; /* # of dirty dentry pages */
f2fs_hash_t chash; /* hash value of given file name */
unsigned int clevel; /* maximum level of given file name */
@@ -229,6 +244,7 @@ struct f2fs_nm_info {
block_t nat_blkaddr; /* base disk address of NAT */
nid_t max_nid; /* maximum possible node ids */
nid_t next_scan_nid; /* the next nid to be scanned */
+ unsigned int ram_thresh; /* control the memory footprint */
/* NAT cache management */
struct radix_tree_root nat_root;/* root of the nat entry cache */
@@ -238,6 +254,7 @@ struct f2fs_nm_info {
struct list_head dirty_nat_entries; /* cached nat entry list (dirty) */
/* free node ids management */
+ struct radix_tree_root free_nid_root;/* root of the free_nid cache */
struct list_head free_nid_list; /* a list for free nids */
spinlock_t free_nid_list_lock; /* protect free nid list */
unsigned int fcnt; /* the number of free node id */
@@ -300,6 +317,12 @@ enum {
NO_CHECK_TYPE
};
+struct flush_cmd {
+ struct flush_cmd *next;
+ struct completion wait;
+ int ret;
+};
+
struct f2fs_sm_info {
struct sit_info *sit_info; /* whole segment information */
struct free_segmap_info *free_info; /* free segment information */
@@ -328,6 +351,14 @@ struct f2fs_sm_info {
unsigned int ipu_policy; /* in-place-update policy */
unsigned int min_ipu_util; /* in-place-update threshold */
+
+ /* for flush command control */
+ struct task_struct *f2fs_issue_flush; /* flush thread */
+ wait_queue_head_t flush_wait_queue; /* waiting queue for wake-up */
+ struct flush_cmd *issue_list; /* list for command issue */
+ struct flush_cmd *dispatch_list; /* list for command dispatch */
+ spinlock_t issue_lock; /* for issue list lock */
+ struct flush_cmd *issue_tail; /* list tail of issue list */
};
/*
@@ -378,7 +409,7 @@ struct f2fs_bio_info {
struct bio *bio; /* bios to merge */
sector_t last_block_in_bio; /* last block number */
struct f2fs_io_info fio; /* store buffered io info. */
- struct mutex io_mutex; /* mutex for bio */
+ struct rw_semaphore io_rwsem; /* blocking op for bio */
};
struct f2fs_sb_info {
@@ -398,6 +429,7 @@ struct f2fs_sb_info {
/* for bio operations */
struct f2fs_bio_info read_io; /* for read bios */
struct f2fs_bio_info write_io[NR_PAGE_TYPE]; /* for write bios */
+ struct completion *wait_io; /* for completion bios */
/* for checkpoint */
struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */
@@ -407,7 +439,6 @@ struct f2fs_sb_info {
struct mutex node_write; /* locking node writes */
struct mutex writepages; /* mutex for writepages() */
bool por_doing; /* recovery is doing or not */
- bool on_build_free_nids; /* build_free_nids is doing */
wait_queue_head_t cp_wait;
/* for orphan inode management */
@@ -436,6 +467,7 @@ struct f2fs_sb_info {
unsigned int total_valid_node_count; /* valid node block count */
unsigned int total_valid_inode_count; /* valid inode count */
int active_logs; /* # of active logs */
+ int dir_level; /* directory level */
block_t user_block_count; /* # of user blocks */
block_t total_valid_block_count; /* # of valid blocks */
@@ -622,6 +654,11 @@ static inline int F2FS_HAS_BLOCKS(struct inode *inode)
return inode->i_blocks > F2FS_DEFAULT_ALLOCATED_BLOCKS;
}
+static inline bool f2fs_has_xattr_block(unsigned int ofs)
+{
+ return ofs == XATTR_NODE_OFFSET;
+}
+
static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi,
struct inode *inode, blkcnt_t count)
{
@@ -661,6 +698,7 @@ static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type)
static inline void inode_inc_dirty_dents(struct inode *inode)
{
+ inc_page_count(F2FS_SB(inode->i_sb), F2FS_DIRTY_DENTS);
atomic_inc(&F2FS_I(inode)->dirty_dents);
}
@@ -671,6 +709,10 @@ static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type)
static inline void inode_dec_dirty_dents(struct inode *inode)
{
+ if (!S_ISDIR(inode->i_mode))
+ return;
+
+ dec_page_count(F2FS_SB(inode->i_sb), F2FS_DIRTY_DENTS);
atomic_dec(&F2FS_I(inode)->dirty_dents);
}
@@ -679,6 +721,11 @@ static inline int get_pages(struct f2fs_sb_info *sbi, int count_type)
return atomic_read(&sbi->nr_pages[count_type]);
}
+static inline int get_dirty_dents(struct inode *inode)
+{
+ return atomic_read(&F2FS_I(inode)->dirty_dents);
+}
+
static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type)
{
unsigned int pages_per_sec = sbi->segs_per_sec *
@@ -689,11 +736,7 @@ static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type)
static inline block_t valid_user_blocks(struct f2fs_sb_info *sbi)
{
- block_t ret;
- spin_lock(&sbi->stat_lock);
- ret = sbi->total_valid_block_count;
- spin_unlock(&sbi->stat_lock);
- return ret;
+ return sbi->total_valid_block_count;
}
static inline unsigned long __bitmap_size(struct f2fs_sb_info *sbi, int flag)
@@ -789,11 +832,7 @@ static inline void dec_valid_node_count(struct f2fs_sb_info *sbi,
static inline unsigned int valid_node_count(struct f2fs_sb_info *sbi)
{
- unsigned int ret;
- spin_lock(&sbi->stat_lock);
- ret = sbi->total_valid_node_count;
- spin_unlock(&sbi->stat_lock);
- return ret;
+ return sbi->total_valid_node_count;
}
static inline void inc_valid_inode_count(struct f2fs_sb_info *sbi)
@@ -814,11 +853,7 @@ static inline void dec_valid_inode_count(struct f2fs_sb_info *sbi)
static inline unsigned int valid_inode_count(struct f2fs_sb_info *sbi)
{
- unsigned int ret;
- spin_lock(&sbi->stat_lock);
- ret = sbi->total_valid_inode_count;
- spin_unlock(&sbi->stat_lock);
- return ret;
+ return sbi->total_valid_inode_count;
}
static inline void f2fs_put_page(struct page *page, int unlock)
@@ -844,9 +879,9 @@ static inline void f2fs_put_dnode(struct dnode_of_data *dn)
}
static inline struct kmem_cache *f2fs_kmem_cache_create(const char *name,
- size_t size, void (*ctor)(void *))
+ size_t size)
{
- return kmem_cache_create(name, size, 0, SLAB_RECLAIM_ACCOUNT, ctor);
+ return kmem_cache_create(name, size, 0, SLAB_RECLAIM_ACCOUNT, NULL);
}
static inline void *f2fs_kmem_cache_alloc(struct kmem_cache *cachep,
@@ -983,24 +1018,28 @@ static inline void set_raw_inline(struct f2fs_inode_info *fi,
ri->i_inline |= F2FS_INLINE_DATA;
}
+static inline int f2fs_has_inline_xattr(struct inode *inode)
+{
+ return is_inode_flag_set(F2FS_I(inode), FI_INLINE_XATTR);
+}
+
static inline unsigned int addrs_per_inode(struct f2fs_inode_info *fi)
{
- if (is_inode_flag_set(fi, FI_INLINE_XATTR))
+ if (f2fs_has_inline_xattr(&fi->vfs_inode))
return DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS;
return DEF_ADDRS_PER_INODE;
}
static inline void *inline_xattr_addr(struct page *page)
{
- struct f2fs_inode *ri;
- ri = (struct f2fs_inode *)page_address(page);
+ struct f2fs_inode *ri = F2FS_INODE(page);
return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE -
F2FS_INLINE_XATTR_ADDRS]);
}
static inline int inline_xattr_size(struct inode *inode)
{
- if (is_inode_flag_set(F2FS_I(inode), FI_INLINE_XATTR))
+ if (f2fs_has_inline_xattr(inode))
return F2FS_INLINE_XATTR_ADDRS << 2;
else
return 0;
@@ -1013,8 +1052,7 @@ static inline int f2fs_has_inline_data(struct inode *inode)
static inline void *inline_data_addr(struct page *page)
{
- struct f2fs_inode *ri;
- ri = (struct f2fs_inode *)page_address(page);
+ struct f2fs_inode *ri = F2FS_INODE(page);
return (void *)&(ri->i_addr[1]);
}
@@ -1023,6 +1061,12 @@ static inline int f2fs_readonly(struct super_block *sb)
return sb->s_flags & MS_RDONLY;
}
+static inline void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi)
+{
+ set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG);
+ sbi->sb->s_flags |= MS_RDONLY;
+}
+
#define get_inode_mode(i) \
((is_inode_flag_set(F2FS_I(i), FI_ACL_MODE)) ? \
(F2FS_I(i)->i_acl_mode) : ((i)->i_mode))
@@ -1048,7 +1092,7 @@ 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 *);
-int update_inode_page(struct inode *);
+void update_inode_page(struct inode *);
int f2fs_write_inode(struct inode *, struct writeback_control *);
void f2fs_evict_inode(struct inode *);
@@ -1097,6 +1141,7 @@ struct dnode_of_data;
struct node_info;
int is_checkpointed_node(struct f2fs_sb_info *, nid_t);
+bool fsync_mark_done(struct f2fs_sb_info *, nid_t);
void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *);
int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int);
int truncate_inode_blocks(struct inode *, pgoff_t);
@@ -1115,6 +1160,7 @@ void alloc_nid_done(struct f2fs_sb_info *, nid_t);
void alloc_nid_failed(struct f2fs_sb_info *, nid_t);
void recover_node_page(struct f2fs_sb_info *, struct page *,
struct f2fs_summary *, struct node_info *, block_t);
+bool recover_xattr_data(struct inode *, struct page *, block_t);
int recover_inode_page(struct f2fs_sb_info *, struct page *);
int restore_node_summary(struct f2fs_sb_info *, unsigned int,
struct f2fs_summary_block *);
@@ -1129,7 +1175,9 @@ void destroy_node_manager_caches(void);
*/
void f2fs_balance_fs(struct f2fs_sb_info *);
void f2fs_balance_fs_bg(struct f2fs_sb_info *);
+int f2fs_issue_flush(struct f2fs_sb_info *);
void invalidate_blocks(struct f2fs_sb_info *, block_t);
+void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t);
void clear_prefree_segments(struct f2fs_sb_info *);
int npages_for_summary_flush(struct f2fs_sb_info *);
void allocate_new_segments(struct f2fs_sb_info *);
@@ -1162,6 +1210,7 @@ void destroy_segment_manager_caches(void);
*/
struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t);
struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t);
+int ra_meta_pages(struct f2fs_sb_info *, int, int, int);
long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long);
int acquire_orphan_inode(struct f2fs_sb_info *);
void release_orphan_inode(struct f2fs_sb_info *);
@@ -1231,7 +1280,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;
+ int prefree_count, call_count, cp_count;
int tot_segs, node_segs, data_segs, free_segs, free_secs;
int tot_blks, data_blks, node_blks;
int curseg[NR_CURSEG_TYPE];
@@ -1248,6 +1297,7 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi)
return (struct f2fs_stat_info *)sbi->stat_info;
}
+#define stat_inc_cp_count(si) ((si)->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++)
@@ -1302,6 +1352,7 @@ void f2fs_destroy_stats(struct f2fs_sb_info *);
void __init f2fs_create_root_stats(void);
void f2fs_destroy_root_stats(void);
#else
+#define stat_inc_cp_count(si)
#define stat_inc_call_count(si)
#define stat_inc_bggc_count(si)
#define stat_inc_dirty_dir(sbi)
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 0dfcef53a6ed..302d552afea5 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -76,7 +76,7 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
trace_f2fs_vm_page_mkwrite(page, DATA);
mapped:
/* fill the page */
- wait_on_page_writeback(page);
+ f2fs_wait_on_page_writeback(page, DATA);
out:
sb_end_pagefault(inode->i_sb);
return block_page_mkwrite_return(err);
@@ -111,11 +111,12 @@ static int get_parent_ino(struct inode *inode, nid_t *pino)
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{
struct inode *inode = file->f_mapping->host;
+ struct f2fs_inode_info *fi = F2FS_I(inode);
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
int ret = 0;
bool need_cp = false;
struct writeback_control wbc = {
- .sync_mode = WB_SYNC_NONE,
+ .sync_mode = WB_SYNC_ALL,
.nr_to_write = LONG_MAX,
.for_reclaim = 0,
};
@@ -133,7 +134,7 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
/* guarantee free sections for fsync */
f2fs_balance_fs(sbi);
- mutex_lock(&inode->i_mutex);
+ down_read(&fi->i_sem);
/*
* Both of fdatasync() and fsync() are able to be recovered from
@@ -150,25 +151,33 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
else if (F2FS_I(inode)->xattr_ver == cur_cp_version(F2FS_CKPT(sbi)))
need_cp = true;
+ up_read(&fi->i_sem);
+
if (need_cp) {
nid_t pino;
- F2FS_I(inode)->xattr_ver = 0;
-
/* all the dirty node pages should be flushed for POR */
ret = f2fs_sync_fs(inode->i_sb, 1);
+
+ down_write(&fi->i_sem);
+ F2FS_I(inode)->xattr_ver = 0;
if (file_wrong_pino(inode) && inode->i_nlink == 1 &&
get_parent_ino(inode, &pino)) {
F2FS_I(inode)->i_pino = pino;
file_got_pino(inode);
+ up_write(&fi->i_sem);
mark_inode_dirty_sync(inode);
ret = f2fs_write_inode(inode, NULL);
if (ret)
goto out;
+ } else {
+ up_write(&fi->i_sem);
}
} else {
/* if there is no written node page, write its inode page */
while (!sync_node_pages(sbi, inode->i_ino, &wbc)) {
+ if (fsync_mark_done(sbi, inode->i_ino))
+ goto out;
mark_inode_dirty_sync(inode);
ret = f2fs_write_inode(inode, NULL);
if (ret)
@@ -177,10 +186,9 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
ret = wait_on_node_pages_writeback(sbi, inode->i_ino);
if (ret)
goto out;
- ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
+ ret = f2fs_issue_flush(F2FS_SB(inode->i_sb));
}
out:
- mutex_unlock(&inode->i_mutex);
trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret);
return ret;
}
@@ -245,7 +253,7 @@ static void truncate_partial_data_page(struct inode *inode, u64 from)
f2fs_put_page(page, 1);
return;
}
- wait_on_page_writeback(page);
+ f2fs_wait_on_page_writeback(page, DATA);
zero_user(page, offset, PAGE_CACHE_SIZE - offset);
set_page_dirty(page);
f2fs_put_page(page, 1);
@@ -422,7 +430,7 @@ static void fill_zero(struct inode *inode, pgoff_t index,
f2fs_unlock_op(sbi);
if (!IS_ERR(page)) {
- wait_on_page_writeback(page);
+ f2fs_wait_on_page_writeback(page, DATA);
zero_user(page, start, len);
set_page_dirty(page);
f2fs_put_page(page, 1);
@@ -560,6 +568,8 @@ static long f2fs_fallocate(struct file *file, int mode,
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
return -EOPNOTSUPP;
+ mutex_lock(&inode->i_mutex);
+
if (mode & FALLOC_FL_PUNCH_HOLE)
ret = punch_hole(inode, offset, len);
else
@@ -569,6 +579,9 @@ static long f2fs_fallocate(struct file *file, int mode,
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
}
+
+ mutex_unlock(&inode->i_mutex);
+
trace_f2fs_fallocate(inode, mode, offset, len, ret);
return ret;
}
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index ea0371e854b4..b90dbe55403a 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -531,15 +531,10 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type)
set_page_dirty(page);
set_cold_data(page);
} else {
- struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
-
f2fs_wait_on_page_writeback(page, DATA);
- if (clear_page_dirty_for_io(page) &&
- S_ISDIR(inode->i_mode)) {
- dec_page_count(sbi, F2FS_DIRTY_DENTS);
+ if (clear_page_dirty_for_io(page))
inode_dec_dirty_dents(inode);
- }
set_cold_data(page);
do_write_data_page(page, &fio);
clear_cold_data(page);
@@ -701,6 +696,8 @@ int f2fs_gc(struct f2fs_sb_info *sbi)
gc_more:
if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE)))
goto stop;
+ if (unlikely(is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ERROR_FLAG)))
+ goto stop;
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) {
gc_type = FG_GC;
@@ -711,6 +708,11 @@ gc_more:
goto stop;
ret = 0;
+ /* readahead multi ssa blocks those have contiguous address */
+ if (sbi->segs_per_sec > 1)
+ ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), sbi->segs_per_sec,
+ META_SSA);
+
for (i = 0; i < sbi->segs_per_sec; i++)
do_garbage_collect(sbi, segno + i, &ilist, gc_type);
@@ -740,7 +742,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
int __init create_gc_caches(void)
{
winode_slab = f2fs_kmem_cache_create("f2fs_gc_inodes",
- sizeof(struct inode_entry), NULL);
+ sizeof(struct inode_entry));
if (!winode_slab)
return -ENOMEM;
return 0;
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index 31ee5b164ff9..383db1fabcf4 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -45,8 +45,10 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page)
}
ipage = get_node_page(sbi, inode->i_ino);
- if (IS_ERR(ipage))
+ if (IS_ERR(ipage)) {
+ unlock_page(page);
return PTR_ERR(ipage);
+ }
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 4d67ed736dca..ee829d360468 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -107,6 +107,7 @@ static int do_read_inode(struct inode *inode)
fi->flags = 0;
fi->i_advise = ri->i_advise;
fi->i_pino = le32_to_cpu(ri->i_pino);
+ fi->i_dir_level = ri->i_dir_level;
get_extent_info(&fi->ext, ri->i_ext);
get_inline_info(fi, ri);
@@ -204,6 +205,7 @@ void update_inode(struct inode *inode, struct page *node_page)
ri->i_flags = cpu_to_le32(F2FS_I(inode)->i_flags);
ri->i_pino = cpu_to_le32(F2FS_I(inode)->i_pino);
ri->i_generation = cpu_to_le32(inode->i_generation);
+ ri->i_dir_level = F2FS_I(inode)->i_dir_level;
__set_inode_rdev(inode, ri);
set_cold_node(inode, node_page);
@@ -212,24 +214,29 @@ void update_inode(struct inode *inode, struct page *node_page)
clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
}
-int update_inode_page(struct inode *inode)
+void update_inode_page(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct page *node_page;
-
+retry:
node_page = get_node_page(sbi, inode->i_ino);
- if (IS_ERR(node_page))
- return PTR_ERR(node_page);
-
+ if (IS_ERR(node_page)) {
+ int err = PTR_ERR(node_page);
+ if (err == -ENOMEM) {
+ cond_resched();
+ goto retry;
+ } else if (err != -ENOENT) {
+ f2fs_stop_checkpoint(sbi);
+ }
+ return;
+ }
update_inode(inode, node_page);
f2fs_put_page(node_page, 1);
- return 0;
}
int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
- int ret;
if (inode->i_ino == F2FS_NODE_INO(sbi) ||
inode->i_ino == F2FS_META_INO(sbi))
@@ -243,13 +250,13 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
* during the urgent cleaning time when runing out of free sections.
*/
f2fs_lock_op(sbi);
- ret = update_inode_page(inode);
+ update_inode_page(inode);
f2fs_unlock_op(sbi);
if (wbc)
f2fs_balance_fs(sbi);
- return ret;
+ return 0;
}
/*
@@ -260,13 +267,13 @@ void f2fs_evict_inode(struct inode *inode)
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
trace_f2fs_evict_inode(inode);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
if (inode->i_ino == F2FS_NODE_INO(sbi) ||
inode->i_ino == F2FS_META_INO(sbi))
goto no_delete;
- f2fs_bug_on(atomic_read(&F2FS_I(inode)->dirty_dents));
+ f2fs_bug_on(get_dirty_dents(inode));
remove_dirty_dir_inode(inode);
if (inode->i_nlink || is_bad_inode(inode))
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 397d459e97bf..a9409d19dfd4 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -207,6 +207,8 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
inode = f2fs_iget(dir->i_sb, ino);
if (IS_ERR(inode))
return ERR_CAST(inode);
+
+ stat_inc_inline_inode(inode);
}
return d_splice_alias(inode, dentry);
@@ -424,12 +426,17 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
}
f2fs_set_link(new_dir, new_entry, new_page, old_inode);
+ down_write(&F2FS_I(old_inode)->i_sem);
F2FS_I(old_inode)->i_pino = new_dir->i_ino;
+ up_write(&F2FS_I(old_inode)->i_sem);
new_inode->i_ctime = CURRENT_TIME;
+ down_write(&F2FS_I(new_inode)->i_sem);
if (old_dir_entry)
drop_nlink(new_inode);
drop_nlink(new_inode);
+ up_write(&F2FS_I(new_inode)->i_sem);
+
mark_inode_dirty(new_inode);
if (!new_inode->i_nlink)
@@ -459,7 +466,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (old_dir != new_dir) {
f2fs_set_link(old_inode, old_dir_entry,
old_dir_page, new_dir);
+ down_write(&F2FS_I(old_inode)->i_sem);
F2FS_I(old_inode)->i_pino = new_dir->i_ino;
+ up_write(&F2FS_I(old_inode)->i_sem);
update_inode_page(old_inode);
} else {
kunmap(old_dir_page);
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index b0649b76eb4f..a161e955c4c8 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -21,9 +21,27 @@
#include "segment.h"
#include <trace/events/f2fs.h>
+#define on_build_free_nids(nmi) mutex_is_locked(&nm_i->build_lock)
+
static struct kmem_cache *nat_entry_slab;
static struct kmem_cache *free_nid_slab;
+static inline bool available_free_memory(struct f2fs_nm_info *nm_i, int type)
+{
+ struct sysinfo val;
+ unsigned long mem_size = 0;
+
+ si_meminfo(&val);
+ if (type == FREE_NIDS)
+ mem_size = nm_i->fcnt * sizeof(struct free_nid);
+ else if (type == NAT_ENTRIES)
+ mem_size += nm_i->nat_cnt * sizeof(struct nat_entry);
+ mem_size >>= 12;
+
+ /* give 50:50 memory for free nids and nat caches respectively */
+ return (mem_size < ((val.totalram * nm_i->ram_thresh) >> 11));
+}
+
static void clear_node_page_dirty(struct page *page)
{
struct address_space *mapping = page->mapping;
@@ -82,42 +100,6 @@ static struct page *get_next_nat_page(struct f2fs_sb_info *sbi, nid_t nid)
return dst_page;
}
-/*
- * Readahead NAT pages
- */
-static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid)
-{
- struct address_space *mapping = META_MAPPING(sbi);
- struct f2fs_nm_info *nm_i = NM_I(sbi);
- struct page *page;
- pgoff_t index;
- int i;
- struct f2fs_io_info fio = {
- .type = META,
- .rw = READ_SYNC | REQ_META | REQ_PRIO
- };
-
-
- for (i = 0; i < FREE_NID_PAGES; i++, nid += NAT_ENTRY_PER_BLOCK) {
- if (unlikely(nid >= nm_i->max_nid))
- nid = 0;
- index = current_nat_addr(sbi, nid);
-
- page = grab_cache_page(mapping, index);
- if (!page)
- continue;
- if (PageUptodate(page)) {
- mark_page_accessed(page);
- f2fs_put_page(page, 1);
- continue;
- }
- f2fs_submit_page_mbio(sbi, page, index, &fio);
- mark_page_accessed(page);
- f2fs_put_page(page, 0);
- }
- f2fs_submit_merged_bio(sbi, META, READ);
-}
-
static struct nat_entry *__lookup_nat_cache(struct f2fs_nm_info *nm_i, nid_t n)
{
return radix_tree_lookup(&nm_i->nat_root, n);
@@ -151,6 +133,20 @@ int is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid)
return is_cp;
}
+bool fsync_mark_done(struct f2fs_sb_info *sbi, nid_t nid)
+{
+ struct f2fs_nm_info *nm_i = NM_I(sbi);
+ struct nat_entry *e;
+ bool fsync_done = false;
+
+ read_lock(&nm_i->nat_tree_lock);
+ e = __lookup_nat_cache(nm_i, nid);
+ if (e)
+ fsync_done = e->fsync_done;
+ read_unlock(&nm_i->nat_tree_lock);
+ return fsync_done;
+}
+
static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid)
{
struct nat_entry *new;
@@ -164,6 +160,7 @@ static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid)
}
memset(new, 0, sizeof(struct nat_entry));
nat_set_nid(new, nid);
+ new->checkpointed = true;
list_add_tail(&new->list, &nm_i->nat_entries);
nm_i->nat_cnt++;
return new;
@@ -185,13 +182,12 @@ retry:
nat_set_blkaddr(e, le32_to_cpu(ne->block_addr));
nat_set_ino(e, le32_to_cpu(ne->ino));
nat_set_version(e, ne->version);
- e->checkpointed = true;
}
write_unlock(&nm_i->nat_tree_lock);
}
static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
- block_t new_blkaddr)
+ block_t new_blkaddr, bool fsync_done)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct nat_entry *e;
@@ -205,7 +201,6 @@ retry:
goto retry;
}
e->ni = *ni;
- e->checkpointed = true;
f2fs_bug_on(ni->blk_addr == NEW_ADDR);
} else if (new_blkaddr == NEW_ADDR) {
/*
@@ -217,9 +212,6 @@ retry:
f2fs_bug_on(ni->blk_addr != NULL_ADDR);
}
- if (new_blkaddr == NEW_ADDR)
- e->checkpointed = false;
-
/* sanity check */
f2fs_bug_on(nat_get_blkaddr(e) != ni->blk_addr);
f2fs_bug_on(nat_get_blkaddr(e) == NULL_ADDR &&
@@ -239,6 +231,11 @@ retry:
/* change address */
nat_set_blkaddr(e, new_blkaddr);
__set_nat_cache_dirty(nm_i, e);
+
+ /* update fsync_mark if its inode nat entry is still alive */
+ e = __lookup_nat_cache(nm_i, ni->ino);
+ if (e)
+ e->fsync_done = fsync_done;
write_unlock(&nm_i->nat_tree_lock);
}
@@ -246,7 +243,7 @@ int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
- if (nm_i->nat_cnt <= NM_WOUT_THRESHOLD)
+ if (available_free_memory(nm_i, NAT_ENTRIES))
return 0;
write_lock(&nm_i->nat_tree_lock);
@@ -505,7 +502,7 @@ static void truncate_node(struct dnode_of_data *dn)
/* Deallocate node address */
invalidate_blocks(sbi, ni.blk_addr);
dec_valid_node_count(sbi, dn->inode);
- set_node_addr(sbi, &ni, NULL_ADDR);
+ set_node_addr(sbi, &ni, NULL_ADDR, false);
if (dn->nid == dn->inode->i_ino) {
remove_orphan_inode(sbi, dn->nid);
@@ -763,7 +760,7 @@ skip_partial:
f2fs_put_page(page, 1);
goto restart;
}
- wait_on_page_writeback(page);
+ f2fs_wait_on_page_writeback(page, NODE);
ri->i_nid[offset[0] - NODE_DIR1_BLOCK] = 0;
set_page_dirty(page);
unlock_page(page);
@@ -852,7 +849,8 @@ struct page *new_node_page(struct dnode_of_data *dn,
if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC)))
return ERR_PTR(-EPERM);
- page = grab_cache_page(NODE_MAPPING(sbi), dn->nid);
+ page = grab_cache_page_write_begin(NODE_MAPPING(sbi),
+ dn->nid, AOP_FLAG_NOFS);
if (!page)
return ERR_PTR(-ENOMEM);
@@ -867,14 +865,14 @@ struct page *new_node_page(struct dnode_of_data *dn,
f2fs_bug_on(old_ni.blk_addr != NULL_ADDR);
new_ni = old_ni;
new_ni.ino = dn->inode->i_ino;
- set_node_addr(sbi, &new_ni, NEW_ADDR);
+ set_node_addr(sbi, &new_ni, NEW_ADDR, false);
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 (ofs == XATTR_NODE_OFFSET)
+ if (f2fs_has_xattr_block(ofs))
F2FS_I(dn->inode)->i_xattr_nid = dn->nid;
dn->node_page = page;
@@ -948,7 +946,8 @@ struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid)
struct page *page;
int err;
repeat:
- page = grab_cache_page(NODE_MAPPING(sbi), nid);
+ page = grab_cache_page_write_begin(NODE_MAPPING(sbi),
+ nid, AOP_FLAG_NOFS);
if (!page)
return ERR_PTR(-ENOMEM);
@@ -959,7 +958,7 @@ repeat:
goto got_it;
lock_page(page);
- if (unlikely(!PageUptodate(page))) {
+ if (unlikely(!PageUptodate(page) || nid != nid_of_node(page))) {
f2fs_put_page(page, 1);
return ERR_PTR(-EIO);
}
@@ -968,7 +967,6 @@ repeat:
goto repeat;
}
got_it:
- f2fs_bug_on(nid != nid_of_node(page));
mark_page_accessed(page);
return page;
}
@@ -1168,7 +1166,7 @@ int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino)
continue;
if (ino && ino_of_node(page) == ino) {
- wait_on_page_writeback(page);
+ f2fs_wait_on_page_writeback(page, NODE);
if (TestClearPageError(page))
ret = -EIO;
}
@@ -1201,7 +1199,7 @@ static int f2fs_write_node_page(struct page *page,
if (unlikely(sbi->por_doing))
goto redirty_out;
- wait_on_page_writeback(page);
+ f2fs_wait_on_page_writeback(page, NODE);
/* get old block addr of this node page */
nid = nid_of_node(page);
@@ -1222,7 +1220,7 @@ static int f2fs_write_node_page(struct page *page,
mutex_lock(&sbi->node_write);
set_page_writeback(page);
write_node_page(sbi, page, &fio, nid, ni.blk_addr, &new_addr);
- set_node_addr(sbi, &ni, new_addr);
+ set_node_addr(sbi, &ni, new_addr, is_fsync_dnode(page));
dec_page_count(sbi, F2FS_DIRTY_NODES);
mutex_unlock(&sbi->node_write);
unlock_page(page);
@@ -1231,35 +1229,32 @@ static int f2fs_write_node_page(struct page *page,
redirty_out:
dec_page_count(sbi, F2FS_DIRTY_NODES);
wbc->pages_skipped++;
+ account_page_redirty(page);
set_page_dirty(page);
return AOP_WRITEPAGE_ACTIVATE;
}
-/*
- * It is very important to gather dirty pages and write at once, so that we can
- * submit a big bio without interfering other data writes.
- * Be default, 512 pages (2MB) * 3 node types, is more reasonable.
- */
-#define COLLECT_DIRTY_NODES 1536
static int f2fs_write_node_pages(struct address_space *mapping,
struct writeback_control *wbc)
{
struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb);
- long nr_to_write = wbc->nr_to_write;
+ long diff;
/* balancing f2fs's metadata in background */
f2fs_balance_fs_bg(sbi);
/* collect a number of dirty node pages and write together */
- if (get_pages(sbi, F2FS_DIRTY_NODES) < COLLECT_DIRTY_NODES)
- return 0;
+ if (get_pages(sbi, F2FS_DIRTY_NODES) < nr_pages_to_skip(sbi, NODE))
+ goto skip_write;
- /* if mounting is failed, skip writing node pages */
- wbc->nr_to_write = 3 * max_hw_blocks(sbi);
+ diff = nr_pages_to_write(sbi, NODE, wbc);
wbc->sync_mode = WB_SYNC_NONE;
sync_node_pages(sbi, 0, wbc);
- wbc->nr_to_write = nr_to_write - (3 * max_hw_blocks(sbi) -
- wbc->nr_to_write);
+ wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff);
+ return 0;
+
+skip_write:
+ wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_NODES);
return 0;
}
@@ -1307,22 +1302,17 @@ const struct address_space_operations f2fs_node_aops = {
.releasepage = f2fs_release_node_page,
};
-static struct free_nid *__lookup_free_nid_list(nid_t n, struct list_head *head)
+static struct free_nid *__lookup_free_nid_list(struct f2fs_nm_info *nm_i,
+ nid_t n)
{
- struct list_head *this;
- struct free_nid *i;
- list_for_each(this, head) {
- i = list_entry(this, struct free_nid, list);
- if (i->nid == n)
- return i;
- }
- return NULL;
+ return radix_tree_lookup(&nm_i->free_nid_root, n);
}
-static void __del_from_free_nid_list(struct free_nid *i)
+static void __del_from_free_nid_list(struct f2fs_nm_info *nm_i,
+ struct free_nid *i)
{
list_del(&i->list);
- kmem_cache_free(free_nid_slab, i);
+ radix_tree_delete(&nm_i->free_nid_root, i->nid);
}
static int add_free_nid(struct f2fs_nm_info *nm_i, nid_t nid, bool build)
@@ -1331,7 +1321,7 @@ static int add_free_nid(struct f2fs_nm_info *nm_i, nid_t nid, bool build)
struct nat_entry *ne;
bool allocated = false;
- if (nm_i->fcnt > 2 * MAX_FREE_NIDS)
+ if (!available_free_memory(nm_i, FREE_NIDS))
return -1;
/* 0 nid should not be used */
@@ -1342,7 +1332,8 @@ static int add_free_nid(struct f2fs_nm_info *nm_i, nid_t nid, bool build)
/* do not add allocated nids */
read_lock(&nm_i->nat_tree_lock);
ne = __lookup_nat_cache(nm_i, nid);
- if (ne && nat_get_blkaddr(ne) != NULL_ADDR)
+ if (ne &&
+ (!ne->checkpointed || nat_get_blkaddr(ne) != NULL_ADDR))
allocated = true;
read_unlock(&nm_i->nat_tree_lock);
if (allocated)
@@ -1354,7 +1345,7 @@ static int add_free_nid(struct f2fs_nm_info *nm_i, nid_t nid, bool build)
i->state = NID_NEW;
spin_lock(&nm_i->free_nid_list_lock);
- if (__lookup_free_nid_list(nid, &nm_i->free_nid_list)) {
+ if (radix_tree_insert(&nm_i->free_nid_root, i->nid, i)) {
spin_unlock(&nm_i->free_nid_list_lock);
kmem_cache_free(free_nid_slab, i);
return 0;
@@ -1368,13 +1359,19 @@ static int add_free_nid(struct f2fs_nm_info *nm_i, nid_t nid, bool build)
static void remove_free_nid(struct f2fs_nm_info *nm_i, nid_t nid)
{
struct free_nid *i;
+ bool need_free = false;
+
spin_lock(&nm_i->free_nid_list_lock);
- i = __lookup_free_nid_list(nid, &nm_i->free_nid_list);
+ i = __lookup_free_nid_list(nm_i, nid);
if (i && i->state == NID_NEW) {
- __del_from_free_nid_list(i);
+ __del_from_free_nid_list(nm_i, i);
nm_i->fcnt--;
+ need_free = true;
}
spin_unlock(&nm_i->free_nid_list_lock);
+
+ if (need_free)
+ kmem_cache_free(free_nid_slab, i);
}
static void scan_nat_page(struct f2fs_nm_info *nm_i,
@@ -1413,7 +1410,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi)
return;
/* readahead nat pages to be scanned */
- ra_nat_pages(sbi, nid);
+ ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES, META_NAT);
while (1) {
struct page *page = get_current_nat_page(sbi, nid);
@@ -1454,7 +1451,6 @@ bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct free_nid *i = NULL;
- struct list_head *this;
retry:
if (unlikely(sbi->total_valid_node_count + 1 >= nm_i->max_nid))
return false;
@@ -1462,13 +1458,11 @@ retry:
spin_lock(&nm_i->free_nid_list_lock);
/* We should not use stale free nids created by build_free_nids */
- if (nm_i->fcnt && !sbi->on_build_free_nids) {
+ if (nm_i->fcnt && !on_build_free_nids(nm_i)) {
f2fs_bug_on(list_empty(&nm_i->free_nid_list));
- list_for_each(this, &nm_i->free_nid_list) {
- i = list_entry(this, struct free_nid, list);
+ list_for_each_entry(i, &nm_i->free_nid_list, list)
if (i->state == NID_NEW)
break;
- }
f2fs_bug_on(i->state != NID_NEW);
*nid = i->nid;
@@ -1481,9 +1475,7 @@ retry:
/* Let's scan nat pages and its caches to get free nids */
mutex_lock(&nm_i->build_lock);
- sbi->on_build_free_nids = true;
build_free_nids(sbi);
- sbi->on_build_free_nids = false;
mutex_unlock(&nm_i->build_lock);
goto retry;
}
@@ -1497,10 +1489,12 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid)
struct free_nid *i;
spin_lock(&nm_i->free_nid_list_lock);
- i = __lookup_free_nid_list(nid, &nm_i->free_nid_list);
+ i = __lookup_free_nid_list(nm_i, nid);
f2fs_bug_on(!i || i->state != NID_ALLOC);
- __del_from_free_nid_list(i);
+ __del_from_free_nid_list(nm_i, i);
spin_unlock(&nm_i->free_nid_list_lock);
+
+ kmem_cache_free(free_nid_slab, i);
}
/*
@@ -1510,20 +1504,25 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct free_nid *i;
+ bool need_free = false;
if (!nid)
return;
spin_lock(&nm_i->free_nid_list_lock);
- i = __lookup_free_nid_list(nid, &nm_i->free_nid_list);
+ i = __lookup_free_nid_list(nm_i, nid);
f2fs_bug_on(!i || i->state != NID_ALLOC);
- if (nm_i->fcnt > 2 * MAX_FREE_NIDS) {
- __del_from_free_nid_list(i);
+ if (!available_free_memory(nm_i, FREE_NIDS)) {
+ __del_from_free_nid_list(nm_i, i);
+ need_free = true;
} else {
i->state = NID_NEW;
nm_i->fcnt++;
}
spin_unlock(&nm_i->free_nid_list_lock);
+
+ if (need_free)
+ kmem_cache_free(free_nid_slab, i);
}
void recover_node_page(struct f2fs_sb_info *sbi, struct page *page,
@@ -1531,10 +1530,83 @@ void recover_node_page(struct f2fs_sb_info *sbi, struct page *page,
block_t new_blkaddr)
{
rewrite_node_page(sbi, page, sum, ni->blk_addr, new_blkaddr);
- set_node_addr(sbi, ni, new_blkaddr);
+ set_node_addr(sbi, ni, new_blkaddr, false);
clear_node_page_dirty(page);
}
+void recover_inline_xattr(struct inode *inode, struct page *page)
+{
+ struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
+ void *src_addr, *dst_addr;
+ size_t inline_size;
+ struct page *ipage;
+ struct f2fs_inode *ri;
+
+ if (!f2fs_has_inline_xattr(inode))
+ return;
+
+ if (!IS_INODE(page))
+ return;
+
+ ri = F2FS_INODE(page);
+ if (!(ri->i_inline & F2FS_INLINE_XATTR))
+ return;
+
+ ipage = get_node_page(sbi, inode->i_ino);
+ f2fs_bug_on(IS_ERR(ipage));
+
+ dst_addr = inline_xattr_addr(ipage);
+ src_addr = inline_xattr_addr(page);
+ inline_size = inline_xattr_size(inode);
+
+ memcpy(dst_addr, src_addr, inline_size);
+
+ update_inode(inode, ipage);
+ f2fs_put_page(ipage, 1);
+}
+
+bool recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr)
+{
+ struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
+ nid_t prev_xnid = F2FS_I(inode)->i_xattr_nid;
+ nid_t new_xnid = nid_of_node(page);
+ struct node_info ni;
+
+ recover_inline_xattr(inode, page);
+
+ if (!f2fs_has_xattr_block(ofs_of_node(page)))
+ return false;
+
+ /* 1: invalidate the previous xattr nid */
+ if (!prev_xnid)
+ goto recover_xnid;
+
+ /* Deallocate node address */
+ get_node_info(sbi, prev_xnid, &ni);
+ f2fs_bug_on(ni.blk_addr == NULL_ADDR);
+ invalidate_blocks(sbi, ni.blk_addr);
+ dec_valid_node_count(sbi, inode);
+ set_node_addr(sbi, &ni, NULL_ADDR, false);
+
+recover_xnid:
+ /* 2: allocate new xattr nid */
+ if (unlikely(!inc_valid_node_count(sbi, inode)))
+ f2fs_bug_on(1);
+
+ remove_free_nid(NM_I(sbi), new_xnid);
+ get_node_info(sbi, new_xnid, &ni);
+ ni.ino = inode->i_ino;
+ set_node_addr(sbi, &ni, NEW_ADDR, false);
+ F2FS_I(inode)->i_xattr_nid = new_xnid;
+
+ /* 3: update xattr blkaddr */
+ refresh_sit_entry(sbi, NEW_ADDR, blkaddr);
+ set_node_addr(sbi, &ni, blkaddr, false);
+
+ update_inode_page(inode);
+ return true;
+}
+
int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page)
{
struct f2fs_inode *src, *dst;
@@ -1567,7 +1639,7 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page)
if (unlikely(!inc_valid_node_count(sbi, NULL)))
WARN_ON(1);
- set_node_addr(sbi, &new_ni, NEW_ADDR);
+ set_node_addr(sbi, &new_ni, NEW_ADDR, false);
inc_valid_inode_count(sbi);
f2fs_put_page(ipage, 1);
return 0;
@@ -1590,15 +1662,8 @@ static int ra_sum_pages(struct f2fs_sb_info *sbi, struct list_head *pages,
for (; page_idx < start + nrpages; page_idx++) {
/* alloc temporal page for read node summary info*/
page = alloc_page(GFP_F2FS_ZERO);
- if (!page) {
- struct page *tmp;
- list_for_each_entry_safe(page, tmp, pages, lru) {
- list_del(&page->lru);
- unlock_page(page);
- __free_pages(page, 0);
- }
- return -ENOMEM;
- }
+ if (!page)
+ break;
lock_page(page);
page->index = page_idx;
@@ -1609,7 +1674,8 @@ static int ra_sum_pages(struct f2fs_sb_info *sbi, struct list_head *pages,
f2fs_submit_page_mbio(sbi, page, page->index, &fio);
f2fs_submit_merged_bio(sbi, META, READ);
- return 0;
+
+ return page_idx - start;
}
int restore_node_summary(struct f2fs_sb_info *sbi,
@@ -1628,15 +1694,17 @@ int restore_node_summary(struct f2fs_sb_info *sbi,
addr = START_BLOCK(sbi, segno);
sum_entry = &sum->entries[0];
- for (i = 0; i < last_offset; i += nrpages, addr += nrpages) {
+ for (i = 0; !err && i < last_offset; i += nrpages, addr += nrpages) {
nrpages = min(last_offset - i, bio_blocks);
/* read ahead node pages */
- err = ra_sum_pages(sbi, &page_list, addr, nrpages);
- if (err)
- return err;
+ nrpages = ra_sum_pages(sbi, &page_list, addr, nrpages);
+ if (!nrpages)
+ return -ENOMEM;
list_for_each_entry_safe(page, tmp, &page_list, lru) {
+ if (err)
+ goto skip;
lock_page(page);
if (unlikely(!PageUptodate(page))) {
@@ -1648,9 +1716,9 @@ int restore_node_summary(struct f2fs_sb_info *sbi,
sum_entry->ofs_in_node = 0;
sum_entry++;
}
-
- list_del(&page->lru);
unlock_page(page);
+skip:
+ list_del(&page->lru);
__free_pages(page, 0);
}
}
@@ -1709,7 +1777,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk;
- struct list_head *cur, *n;
+ struct nat_entry *ne, *cur;
struct page *page = NULL;
struct f2fs_nat_block *nat_blk = NULL;
nid_t start_nid = 0, end_nid = 0;
@@ -1721,18 +1789,17 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
mutex_lock(&curseg->curseg_mutex);
/* 1) flush dirty nat caches */
- list_for_each_safe(cur, n, &nm_i->dirty_nat_entries) {
- struct nat_entry *ne;
+ list_for_each_entry_safe(ne, cur, &nm_i->dirty_nat_entries, list) {
nid_t nid;
struct f2fs_nat_entry raw_ne;
int offset = -1;
block_t new_blkaddr;
- ne = list_entry(cur, struct nat_entry, list);
- nid = nat_get_nid(ne);
-
if (nat_get_blkaddr(ne) == NEW_ADDR)
continue;
+
+ nid = nat_get_nid(ne);
+
if (flushed)
goto to_nat_page;
@@ -1783,16 +1850,12 @@ flush_now:
} else {
write_lock(&nm_i->nat_tree_lock);
__clear_nat_cache_dirty(nm_i, ne);
- ne->checkpointed = true;
write_unlock(&nm_i->nat_tree_lock);
}
}
if (!flushed)
mutex_unlock(&curseg->curseg_mutex);
f2fs_put_page(page, 1);
-
- /* 2) shrink nat caches if necessary */
- try_to_free_nats(sbi, nm_i->nat_cnt - NM_WOUT_THRESHOLD);
}
static int init_node_manager(struct f2fs_sb_info *sbi)
@@ -1807,10 +1870,14 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
/* segment_count_nat includes pair segment so divide to 2. */
nat_segs = le32_to_cpu(sb_raw->segment_count_nat) >> 1;
nat_blocks = nat_segs << le32_to_cpu(sb_raw->log_blocks_per_seg);
- nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
+
+ /* not used nids: 0, node, meta, (and root counted as valid node) */
+ nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks - 3;
nm_i->fcnt = 0;
nm_i->nat_cnt = 0;
+ nm_i->ram_thresh = DEF_RAM_THRESHOLD;
+ INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC);
INIT_LIST_HEAD(&nm_i->free_nid_list);
INIT_RADIX_TREE(&nm_i->nat_root, GFP_ATOMIC);
INIT_LIST_HEAD(&nm_i->nat_entries);
@@ -1864,8 +1931,11 @@ void destroy_node_manager(struct f2fs_sb_info *sbi)
spin_lock(&nm_i->free_nid_list_lock);
list_for_each_entry_safe(i, next_i, &nm_i->free_nid_list, list) {
f2fs_bug_on(i->state == NID_ALLOC);
- __del_from_free_nid_list(i);
+ __del_from_free_nid_list(nm_i, i);
nm_i->fcnt--;
+ spin_unlock(&nm_i->free_nid_list_lock);
+ kmem_cache_free(free_nid_slab, i);
+ spin_lock(&nm_i->free_nid_list_lock);
}
f2fs_bug_on(nm_i->fcnt);
spin_unlock(&nm_i->free_nid_list_lock);
@@ -1875,11 +1945,9 @@ void destroy_node_manager(struct f2fs_sb_info *sbi)
while ((found = __gang_lookup_nat_cache(nm_i,
nid, NATVEC_SIZE, natvec))) {
unsigned idx;
- for (idx = 0; idx < found; idx++) {
- struct nat_entry *e = natvec[idx];
- nid = nat_get_nid(e) + 1;
- __del_from_nat_cache(nm_i, e);
- }
+ nid = nat_get_nid(natvec[found - 1]) + 1;
+ for (idx = 0; idx < found; idx++)
+ __del_from_nat_cache(nm_i, natvec[idx]);
}
f2fs_bug_on(nm_i->nat_cnt);
write_unlock(&nm_i->nat_tree_lock);
@@ -1892,12 +1960,12 @@ void destroy_node_manager(struct f2fs_sb_info *sbi)
int __init create_node_manager_caches(void)
{
nat_entry_slab = f2fs_kmem_cache_create("nat_entry",
- sizeof(struct nat_entry), NULL);
+ sizeof(struct nat_entry));
if (!nat_entry_slab)
return -ENOMEM;
free_nid_slab = f2fs_kmem_cache_create("free_nid",
- sizeof(struct free_nid), NULL);
+ sizeof(struct free_nid));
if (!free_nid_slab) {
kmem_cache_destroy(nat_entry_slab);
return -ENOMEM;
diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h
index c4c79885c993..5decc1a375f0 100644
--- a/fs/f2fs/node.h
+++ b/fs/f2fs/node.h
@@ -17,14 +17,11 @@
/* # of pages to perform readahead before building free nids */
#define FREE_NID_PAGES 4
-/* maximum # of free node ids to produce during build_free_nids */
-#define MAX_FREE_NIDS (NAT_ENTRY_PER_BLOCK * FREE_NID_PAGES)
-
/* maximum readahead size for node during getting data blocks */
#define MAX_RA_NODE 128
-/* maximum cached nat entries to manage memory footprint */
-#define NM_WOUT_THRESHOLD (64 * NAT_ENTRY_PER_BLOCK)
+/* control the memory footprint threshold (10MB per 1GB ram) */
+#define DEF_RAM_THRESHOLD 10
/* vector size for gang look-up from nat cache that consists of radix tree */
#define NATVEC_SIZE 64
@@ -45,6 +42,7 @@ struct node_info {
struct nat_entry {
struct list_head list; /* for clean or dirty nat list */
bool checkpointed; /* whether it is checkpointed or not */
+ bool fsync_done; /* whether the latest node has fsync mark */
struct node_info ni; /* in-memory node information */
};
@@ -58,9 +56,15 @@ struct nat_entry {
#define nat_set_version(nat, v) (nat->ni.version = v)
#define __set_nat_cache_dirty(nm_i, ne) \
- list_move_tail(&ne->list, &nm_i->dirty_nat_entries);
+ do { \
+ ne->checkpointed = false; \
+ list_move_tail(&ne->list, &nm_i->dirty_nat_entries); \
+ } while (0);
#define __clear_nat_cache_dirty(nm_i, ne) \
- list_move_tail(&ne->list, &nm_i->nat_entries);
+ do { \
+ ne->checkpointed = true; \
+ list_move_tail(&ne->list, &nm_i->nat_entries); \
+ } while (0);
#define inc_node_version(version) (++version)
static inline void node_info_from_raw_nat(struct node_info *ni,
@@ -71,6 +75,11 @@ static inline void node_info_from_raw_nat(struct node_info *ni,
ni->version = raw_ne->version;
}
+enum nid_type {
+ FREE_NIDS, /* indicates the free nid list */
+ NAT_ENTRIES /* indicates the cached nat entry */
+};
+
/*
* For free nid mangement
*/
@@ -236,7 +245,7 @@ static inline bool IS_DNODE(struct page *node_page)
{
unsigned int ofs = ofs_of_node(node_page);
- if (ofs == XATTR_NODE_OFFSET)
+ if (f2fs_has_xattr_block(ofs))
return false;
if (ofs == 3 || ofs == 4 + NIDS_PER_BLOCK ||
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index 976a7a934db5..b1ae89f0f44e 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -27,14 +27,12 @@ bool space_for_roll_forward(struct f2fs_sb_info *sbi)
static struct fsync_inode_entry *get_fsync_inode(struct list_head *head,
nid_t ino)
{
- struct list_head *this;
struct fsync_inode_entry *entry;
- list_for_each(this, head) {
- entry = list_entry(this, struct fsync_inode_entry, list);
+ list_for_each_entry(entry, head, list)
if (entry->inode->i_ino == ino)
return entry;
- }
+
return NULL;
}
@@ -136,7 +134,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
/* get node pages in the current segment */
curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
- blkaddr = START_BLOCK(sbi, curseg->segno) + curseg->next_blkoff;
+ blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
/* read node page */
page = alloc_page(GFP_F2FS_ZERO);
@@ -218,13 +216,12 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
{
struct seg_entry *sentry;
unsigned int segno = GET_SEGNO(sbi, blkaddr);
- unsigned short blkoff = GET_SEGOFF_FROM_SEG0(sbi, blkaddr) &
- (sbi->blocks_per_seg - 1);
+ unsigned short blkoff = GET_BLKOFF_FROM_SEG0(sbi, blkaddr);
+ struct f2fs_summary_block *sum_node;
struct f2fs_summary sum;
+ struct page *sum_page, *node_page;
nid_t ino, nid;
- void *kaddr;
struct inode *inode;
- struct page *node_page;
unsigned int offset;
block_t bidx;
int i;
@@ -238,18 +235,15 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
struct curseg_info *curseg = CURSEG_I(sbi, i);
if (curseg->segno == segno) {
sum = curseg->sum_blk->entries[blkoff];
- break;
+ goto got_it;
}
}
- if (i > CURSEG_COLD_DATA) {
- struct page *sum_page = get_sum_page(sbi, segno);
- struct f2fs_summary_block *sum_node;
- kaddr = page_address(sum_page);
- sum_node = (struct f2fs_summary_block *)kaddr;
- sum = sum_node->entries[blkoff];
- f2fs_put_page(sum_page, 1);
- }
+ sum_page = get_sum_page(sbi, segno);
+ sum_node = (struct f2fs_summary_block *)page_address(sum_page);
+ sum = sum_node->entries[blkoff];
+ f2fs_put_page(sum_page, 1);
+got_it:
/* Use the locked dnode page and inode */
nid = le32_to_cpu(sum.nid);
if (dn->inode->i_ino == nid) {
@@ -301,6 +295,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
if (recover_inline_data(inode, page))
goto out;
+ if (recover_xattr_data(inode, page, blkaddr))
+ goto out;
+
start = start_bidx_of_node(ofs_of_node(page), fi);
if (IS_INODE(page))
end = start + ADDRS_PER_INODE(fi);
@@ -317,7 +314,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
goto out;
}
- wait_on_page_writeback(dn.node_page);
+ f2fs_wait_on_page_writeback(dn.node_page, NODE);
get_node_info(sbi, dn.nid, &ni);
f2fs_bug_on(ni.ino != ino_of_node(page));
@@ -437,7 +434,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi)
bool need_writecp = false;
fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
- sizeof(struct fsync_inode_entry), NULL);
+ sizeof(struct fsync_inode_entry));
if (!fsync_entry_slab)
return -ENOMEM;
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 7caac5f2ca9e..085f548be7a3 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -13,6 +13,7 @@
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/prefetch.h>
+#include <linux/kthread.h>
#include <linux/vmalloc.h>
#include <linux/swap.h>
@@ -24,6 +25,7 @@
#define __reverse_ffz(x) __reverse_ffs(~(x))
static struct kmem_cache *discard_entry_slab;
+static struct kmem_cache *flush_cmd_slab;
/*
* __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since
@@ -195,6 +197,73 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
f2fs_sync_fs(sbi->sb, true);
}
+static int issue_flush_thread(void *data)
+{
+ struct f2fs_sb_info *sbi = data;
+ struct f2fs_sm_info *sm_i = SM_I(sbi);
+ wait_queue_head_t *q = &sm_i->flush_wait_queue;
+repeat:
+ if (kthread_should_stop())
+ return 0;
+
+ spin_lock(&sm_i->issue_lock);
+ if (sm_i->issue_list) {
+ sm_i->dispatch_list = sm_i->issue_list;
+ sm_i->issue_list = sm_i->issue_tail = NULL;
+ }
+ spin_unlock(&sm_i->issue_lock);
+
+ if (sm_i->dispatch_list) {
+ struct bio *bio = bio_alloc(GFP_NOIO, 0);
+ struct flush_cmd *cmd, *next;
+ int ret;
+
+ bio->bi_bdev = sbi->sb->s_bdev;
+ ret = submit_bio_wait(WRITE_FLUSH, bio);
+
+ for (cmd = sm_i->dispatch_list; cmd; cmd = next) {
+ cmd->ret = ret;
+ next = cmd->next;
+ complete(&cmd->wait);
+ }
+ sm_i->dispatch_list = NULL;
+ }
+
+ wait_event_interruptible(*q, kthread_should_stop() || sm_i->issue_list);
+ goto repeat;
+}
+
+int f2fs_issue_flush(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_sm_info *sm_i = SM_I(sbi);
+ struct flush_cmd *cmd;
+ int ret;
+
+ if (!test_opt(sbi, FLUSH_MERGE))
+ return blkdev_issue_flush(sbi->sb->s_bdev, GFP_KERNEL, NULL);
+
+ cmd = f2fs_kmem_cache_alloc(flush_cmd_slab, GFP_ATOMIC);
+ cmd->next = NULL;
+ cmd->ret = 0;
+ init_completion(&cmd->wait);
+
+ spin_lock(&sm_i->issue_lock);
+ if (sm_i->issue_list)
+ sm_i->issue_tail->next = cmd;
+ else
+ sm_i->issue_list = cmd;
+ sm_i->issue_tail = cmd;
+ spin_unlock(&sm_i->issue_lock);
+
+ if (!sm_i->dispatch_list)
+ wake_up(&sm_i->flush_wait_queue);
+
+ wait_for_completion(&cmd->wait);
+ ret = cmd->ret;
+ kmem_cache_free(flush_cmd_slab, cmd);
+ return ret;
+}
+
static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
enum dirty_type dirty_type)
{
@@ -340,8 +409,7 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi)
void clear_prefree_segments(struct f2fs_sb_info *sbi)
{
struct list_head *head = &(SM_I(sbi)->discard_list);
- struct list_head *this, *next;
- struct discard_entry *entry;
+ struct discard_entry *entry, *this;
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
unsigned long *prefree_map = dirty_i->dirty_segmap[PRE];
unsigned int total_segs = TOTAL_SEGS(sbi);
@@ -370,8 +438,7 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi)
mutex_unlock(&dirty_i->seglist_lock);
/* send small discards */
- list_for_each_safe(this, next, head) {
- entry = list_entry(this, struct discard_entry, list);
+ list_for_each_entry_safe(entry, this, head, list) {
f2fs_issue_discard(sbi, entry->blkaddr, entry->len);
list_del(&entry->list);
SM_I(sbi)->nr_discards -= entry->len;
@@ -405,7 +472,7 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
se = get_seg_entry(sbi, segno);
new_vblocks = se->valid_blocks + del;
- offset = GET_SEGOFF_FROM_SEG0(sbi, blkaddr) & (sbi->blocks_per_seg - 1);
+ offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr);
f2fs_bug_on((new_vblocks >> (sizeof(unsigned short) << 3) ||
(new_vblocks > sbi->blocks_per_seg)));
@@ -434,12 +501,14 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
get_sec_entry(sbi, segno)->valid_blocks += del;
}
-static void refresh_sit_entry(struct f2fs_sb_info *sbi,
- block_t old_blkaddr, block_t new_blkaddr)
+void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new)
{
- update_sit_entry(sbi, new_blkaddr, 1);
- if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
- update_sit_entry(sbi, old_blkaddr, -1);
+ update_sit_entry(sbi, new, 1);
+ if (GET_SEGNO(sbi, old) != NULL_SEGNO)
+ update_sit_entry(sbi, old, -1);
+
+ locate_dirty_segment(sbi, GET_SEGNO(sbi, old));
+ locate_dirty_segment(sbi, GET_SEGNO(sbi, new));
}
void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
@@ -881,17 +950,15 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
stat_inc_block_count(sbi, curseg);
+ if (!__has_curseg_space(sbi, type))
+ sit_i->s_ops->allocate_segment(sbi, type, false);
/*
* SIT information should be updated before segment allocation,
* since SSR needs latest valid block information.
*/
refresh_sit_entry(sbi, old_blkaddr, *new_blkaddr);
-
- if (!__has_curseg_space(sbi, type))
- sit_i->s_ops->allocate_segment(sbi, type, false);
-
locate_dirty_segment(sbi, old_cursegno);
- locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr));
+
mutex_unlock(&sit_i->sentry_lock);
if (page && IS_NODESEG(type))
@@ -987,14 +1054,11 @@ void recover_data_page(struct f2fs_sb_info *sbi,
change_curseg(sbi, type, true);
}
- curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, new_blkaddr) &
- (sbi->blocks_per_seg - 1);
+ curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr);
__add_sum_entry(sbi, type, sum);
refresh_sit_entry(sbi, old_blkaddr, new_blkaddr);
-
locate_dirty_segment(sbi, old_cursegno);
- locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr));
mutex_unlock(&sit_i->sentry_lock);
mutex_unlock(&curseg->curseg_mutex);
@@ -1028,8 +1092,7 @@ void rewrite_node_page(struct f2fs_sb_info *sbi,
curseg->next_segno = segno;
change_curseg(sbi, type, true);
}
- curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, new_blkaddr) &
- (sbi->blocks_per_seg - 1);
+ curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr);
__add_sum_entry(sbi, type, sum);
/* change the current log to the next block addr in advance */
@@ -1037,28 +1100,50 @@ void rewrite_node_page(struct f2fs_sb_info *sbi,
curseg->next_segno = next_segno;
change_curseg(sbi, type, true);
}
- curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, next_blkaddr) &
- (sbi->blocks_per_seg - 1);
+ curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, next_blkaddr);
/* rewrite node page */
set_page_writeback(page);
f2fs_submit_page_mbio(sbi, page, new_blkaddr, &fio);
f2fs_submit_merged_bio(sbi, NODE, WRITE);
refresh_sit_entry(sbi, old_blkaddr, new_blkaddr);
-
locate_dirty_segment(sbi, old_cursegno);
- locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr));
mutex_unlock(&sit_i->sentry_lock);
mutex_unlock(&curseg->curseg_mutex);
}
+static inline bool is_merged_page(struct f2fs_sb_info *sbi,
+ struct page *page, enum page_type type)
+{
+ enum page_type btype = PAGE_TYPE_OF_BIO(type);
+ struct f2fs_bio_info *io = &sbi->write_io[btype];
+ struct bio_vec *bvec;
+ int i;
+
+ down_read(&io->io_rwsem);
+ if (!io->bio)
+ goto out;
+
+ bio_for_each_segment_all(bvec, io->bio, i) {
+ if (page == bvec->bv_page) {
+ up_read(&io->io_rwsem);
+ return true;
+ }
+ }
+
+out:
+ up_read(&io->io_rwsem);
+ return false;
+}
+
void f2fs_wait_on_page_writeback(struct page *page,
enum page_type type)
{
struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb);
if (PageWriteback(page)) {
- f2fs_submit_merged_bio(sbi, type, WRITE);
+ if (is_merged_page(sbi, page, type))
+ f2fs_submit_merged_bio(sbi, type, WRITE);
wait_on_page_writeback(page);
}
}
@@ -1167,9 +1252,12 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
ns->ofs_in_node = 0;
}
} else {
- if (restore_node_summary(sbi, segno, sum)) {
+ int err;
+
+ err = restore_node_summary(sbi, segno, sum);
+ if (err) {
f2fs_put_page(new, 1);
- return -EINVAL;
+ return err;
}
}
}
@@ -1190,6 +1278,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
{
int type = CURSEG_HOT_DATA;
+ int err;
if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) {
/* restore for compacted data summary */
@@ -1198,9 +1287,12 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
type = CURSEG_HOT_NODE;
}
- for (; type <= CURSEG_COLD_NODE; type++)
- if (read_normal_summaries(sbi, type))
- return -EINVAL;
+ for (; type <= CURSEG_COLD_NODE; type++) {
+ err = read_normal_summaries(sbi, type);
+ if (err)
+ return err;
+ }
+
return 0;
}
@@ -1583,47 +1675,6 @@ static int build_curseg(struct f2fs_sb_info *sbi)
return restore_curseg_summaries(sbi);
}
-static int ra_sit_pages(struct f2fs_sb_info *sbi, int start, int nrpages)
-{
- struct address_space *mapping = META_MAPPING(sbi);
- struct page *page;
- block_t blk_addr, prev_blk_addr = 0;
- int sit_blk_cnt = SIT_BLK_CNT(sbi);
- int blkno = start;
- struct f2fs_io_info fio = {
- .type = META,
- .rw = READ_SYNC | REQ_META | REQ_PRIO
- };
-
- for (; blkno < start + nrpages && blkno < sit_blk_cnt; blkno++) {
-
- blk_addr = current_sit_addr(sbi, blkno * SIT_ENTRY_PER_BLOCK);
-
- if (blkno != start && prev_blk_addr + 1 != blk_addr)
- break;
- prev_blk_addr = blk_addr;
-repeat:
- page = grab_cache_page(mapping, blk_addr);
- if (!page) {
- cond_resched();
- goto repeat;
- }
- if (PageUptodate(page)) {
- mark_page_accessed(page);
- f2fs_put_page(page, 1);
- continue;
- }
-
- f2fs_submit_page_mbio(sbi, page, blk_addr, &fio);
-
- mark_page_accessed(page);
- f2fs_put_page(page, 0);
- }
-
- f2fs_submit_merged_bio(sbi, META, READ);
- return blkno - start;
-}
-
static void build_sit_entries(struct f2fs_sb_info *sbi)
{
struct sit_info *sit_i = SIT_I(sbi);
@@ -1635,7 +1686,7 @@ static void build_sit_entries(struct f2fs_sb_info *sbi)
int nrpages = MAX_BIO_BLOCKS(max_hw_blocks(sbi));
do {
- readed = ra_sit_pages(sbi, start_blk, nrpages);
+ readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT);
start = start_blk * sit_i->sents_per_block;
end = (start_blk + readed) * sit_i->sents_per_block;
@@ -1781,6 +1832,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
{
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+ dev_t dev = sbi->sb->s_bdev->bd_dev;
struct f2fs_sm_info *sm_info;
int err;
@@ -1799,7 +1851,8 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
sm_info->ovp_segments = le32_to_cpu(ckpt->overprov_segment_count);
sm_info->main_segments = le32_to_cpu(raw_super->segment_count_main);
sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr);
- sm_info->rec_prefree_segments = DEF_RECLAIM_PREFREE_SEGMENTS;
+ sm_info->rec_prefree_segments = sm_info->main_segments *
+ DEF_RECLAIM_PREFREE_SEGMENTS / 100;
sm_info->ipu_policy = F2FS_IPU_DISABLE;
sm_info->min_ipu_util = DEF_MIN_IPU_UTIL;
@@ -1807,6 +1860,16 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
sm_info->nr_discards = 0;
sm_info->max_discards = 0;
+ if (test_opt(sbi, FLUSH_MERGE)) {
+ spin_lock_init(&sm_info->issue_lock);
+ init_waitqueue_head(&sm_info->flush_wait_queue);
+
+ sm_info->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi,
+ "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev));
+ if (IS_ERR(sm_info->f2fs_issue_flush))
+ return PTR_ERR(sm_info->f2fs_issue_flush);
+ }
+
err = build_sit_info(sbi);
if (err)
return err;
@@ -1915,6 +1978,8 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi)
struct f2fs_sm_info *sm_info = SM_I(sbi);
if (!sm_info)
return;
+ if (sm_info->f2fs_issue_flush)
+ kthread_stop(sm_info->f2fs_issue_flush);
destroy_dirty_segmap(sbi);
destroy_curseg(sbi);
destroy_free_segmap(sbi);
@@ -1926,13 +1991,20 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi)
int __init create_segment_manager_caches(void)
{
discard_entry_slab = f2fs_kmem_cache_create("discard_entry",
- sizeof(struct discard_entry), NULL);
+ sizeof(struct discard_entry));
if (!discard_entry_slab)
return -ENOMEM;
+ flush_cmd_slab = f2fs_kmem_cache_create("flush_command",
+ sizeof(struct flush_cmd));
+ if (!flush_cmd_slab) {
+ kmem_cache_destroy(discard_entry_slab);
+ return -ENOMEM;
+ }
return 0;
}
void destroy_segment_manager_caches(void)
{
kmem_cache_destroy(discard_entry_slab);
+ kmem_cache_destroy(flush_cmd_slab);
}
diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h
index 5731682d7516..7091204680f4 100644
--- a/fs/f2fs/segment.h
+++ b/fs/f2fs/segment.h
@@ -14,7 +14,7 @@
#define NULL_SEGNO ((unsigned int)(~0))
#define NULL_SECNO ((unsigned int)(~0))
-#define DEF_RECLAIM_PREFREE_SEGMENTS 100 /* 200MB of prefree segments */
+#define DEF_RECLAIM_PREFREE_SEGMENTS 5 /* 5% over total segments */
/* L: Logical segment # in volume, R: Relative segment # in main area */
#define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno)
@@ -57,6 +57,9 @@
((blk_addr) - SM_I(sbi)->seg0_blkaddr)
#define GET_SEGNO_FROM_SEG0(sbi, blk_addr) \
(GET_SEGOFF_FROM_SEG0(sbi, blk_addr) >> sbi->log_blocks_per_seg)
+#define GET_BLKOFF_FROM_SEG0(sbi, blk_addr) \
+ (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & (sbi->blocks_per_seg - 1))
+
#define GET_SEGNO(sbi, blk_addr) \
(((blk_addr == NULL_ADDR) || (blk_addr == NEW_ADDR)) ? \
NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi), \
@@ -377,26 +380,12 @@ static inline void get_sit_bitmap(struct f2fs_sb_info *sbi,
static inline block_t written_block_count(struct f2fs_sb_info *sbi)
{
- struct sit_info *sit_i = SIT_I(sbi);
- block_t vblocks;
-
- mutex_lock(&sit_i->sentry_lock);
- vblocks = sit_i->written_valid_blocks;
- mutex_unlock(&sit_i->sentry_lock);
-
- return vblocks;
+ return SIT_I(sbi)->written_valid_blocks;
}
static inline unsigned int free_segments(struct f2fs_sb_info *sbi)
{
- struct free_segmap_info *free_i = FREE_I(sbi);
- unsigned int free_segs;
-
- read_lock(&free_i->segmap_lock);
- free_segs = free_i->free_segments;
- read_unlock(&free_i->segmap_lock);
-
- return free_segs;
+ return FREE_I(sbi)->free_segments;
}
static inline int reserved_segments(struct f2fs_sb_info *sbi)
@@ -406,14 +395,7 @@ static inline int reserved_segments(struct f2fs_sb_info *sbi)
static inline unsigned int free_sections(struct f2fs_sb_info *sbi)
{
- struct free_segmap_info *free_i = FREE_I(sbi);
- unsigned int free_secs;
-
- read_lock(&free_i->segmap_lock);
- free_secs = free_i->free_sections;
- read_unlock(&free_i->segmap_lock);
-
- return free_secs;
+ return FREE_I(sbi)->free_sections;
}
static inline unsigned int prefree_segments(struct f2fs_sb_info *sbi)
@@ -682,3 +664,46 @@ static inline unsigned int max_hw_blocks(struct f2fs_sb_info *sbi)
struct request_queue *q = bdev_get_queue(bdev);
return SECTOR_TO_BLOCK(sbi, queue_max_sectors(q));
}
+
+/*
+ * It is very important to gather dirty pages and write at once, so that we can
+ * submit a big bio without interfering other data writes.
+ * By default, 512 pages for directory data,
+ * 512 pages (2MB) * 3 for three types of nodes, and
+ * max_bio_blocks for meta are set.
+ */
+static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type)
+{
+ if (type == DATA)
+ return sbi->blocks_per_seg;
+ else if (type == NODE)
+ return 3 * sbi->blocks_per_seg;
+ else if (type == META)
+ return MAX_BIO_BLOCKS(max_hw_blocks(sbi));
+ else
+ return 0;
+}
+
+/*
+ * When writing pages, it'd better align nr_to_write for segment size.
+ */
+static inline long nr_pages_to_write(struct f2fs_sb_info *sbi, int type,
+ struct writeback_control *wbc)
+{
+ long nr_to_write, desired;
+
+ if (wbc->sync_mode != WB_SYNC_NONE)
+ return 0;
+
+ nr_to_write = wbc->nr_to_write;
+
+ if (type == DATA)
+ desired = 4096;
+ else if (type == NODE)
+ desired = 3 * max_hw_blocks(sbi);
+ else
+ desired = MAX_BIO_BLOCKS(max_hw_blocks(sbi));
+
+ wbc->nr_to_write = desired;
+ return desired - nr_to_write;
+}
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 1a85f83abd53..c756923a7302 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -51,6 +51,7 @@ enum {
Opt_disable_ext_identify,
Opt_inline_xattr,
Opt_inline_data,
+ Opt_flush_merge,
Opt_err,
};
@@ -67,6 +68,7 @@ static match_table_t f2fs_tokens = {
{Opt_disable_ext_identify, "disable_ext_identify"},
{Opt_inline_xattr, "inline_xattr"},
{Opt_inline_data, "inline_data"},
+ {Opt_flush_merge, "flush_merge"},
{Opt_err, NULL},
};
@@ -74,6 +76,7 @@ static match_table_t f2fs_tokens = {
enum {
GC_THREAD, /* struct f2fs_gc_thread */
SM_INFO, /* struct f2fs_sm_info */
+ NM_INFO, /* struct f2fs_nm_info */
F2FS_SBI, /* struct f2fs_sb_info */
};
@@ -92,6 +95,8 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type)
return (unsigned char *)sbi->gc_thread;
else if (struct_type == SM_INFO)
return (unsigned char *)SM_I(sbi);
+ else if (struct_type == NM_INFO)
+ return (unsigned char *)NM_I(sbi);
else if (struct_type == F2FS_SBI)
return (unsigned char *)sbi;
return NULL;
@@ -183,7 +188,9 @@ F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments);
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, max_small_discards, max_discards);
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy);
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util);
+F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh);
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);
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
static struct attribute *f2fs_attrs[] = {
@@ -196,6 +203,8 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(ipu_policy),
ATTR_LIST(min_ipu_util),
ATTR_LIST(max_victim_search),
+ ATTR_LIST(dir_level),
+ ATTR_LIST(ram_thresh),
NULL,
};
@@ -256,9 +265,9 @@ static int parse_options(struct super_block *sb, char *options)
if (!name)
return -ENOMEM;
- if (!strncmp(name, "on", 2))
+ if (strlen(name) == 2 && !strncmp(name, "on", 2))
set_opt(sbi, BG_GC);
- else if (!strncmp(name, "off", 3))
+ else if (strlen(name) == 3 && !strncmp(name, "off", 3))
clear_opt(sbi, BG_GC);
else {
kfree(name);
@@ -327,6 +336,9 @@ static int parse_options(struct super_block *sb, char *options)
case Opt_inline_data:
set_opt(sbi, INLINE_DATA);
break;
+ case Opt_flush_merge:
+ set_opt(sbi, FLUSH_MERGE);
+ break;
default:
f2fs_msg(sb, KERN_ERR,
"Unrecognized mount option \"%s\" or missing value",
@@ -353,12 +365,16 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
fi->i_current_depth = 1;
fi->i_advise = 0;
rwlock_init(&fi->ext.ext_lock);
+ init_rwsem(&fi->i_sem);
set_inode_flag(fi, FI_NEW_INODE);
if (test_opt(F2FS_SB(sb), INLINE_XATTR))
set_inode_flag(fi, FI_INLINE_XATTR);
+ /* Will be used by directory only */
+ fi->i_dir_level = F2FS_SB(sb)->dir_level;
+
return &fi->vfs_inode;
}
@@ -526,6 +542,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
seq_puts(seq, ",disable_ext_identify");
if (test_opt(sbi, INLINE_DATA))
seq_puts(seq, ",inline_data");
+ if (test_opt(sbi, FLUSH_MERGE))
+ seq_puts(seq, ",flush_merge");
seq_printf(seq, ",active_logs=%u", sbi->active_logs);
return 0;
@@ -539,13 +557,22 @@ static int segment_info_seq_show(struct seq_file *seq, void *offset)
le32_to_cpu(sbi->raw_super->segment_count_main);
int i;
+ seq_puts(seq, "format: segment_type|valid_blocks\n"
+ "segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n");
+
for (i = 0; i < total_segs; i++) {
- seq_printf(seq, "%u", get_valid_blocks(sbi, i, 1));
- if (i != 0 && (i % 10) == 0)
- seq_puts(seq, "\n");
+ struct seg_entry *se = get_seg_entry(sbi, i);
+
+ if ((i % 10) == 0)
+ seq_printf(seq, "%-5d", i);
+ seq_printf(seq, "%d|%-3u", se->type,
+ get_valid_blocks(sbi, i, 1));
+ if ((i % 10) == 9 || i == (total_segs - 1))
+ seq_putc(seq, '\n');
else
- seq_puts(seq, " ");
+ seq_putc(seq, ' ');
}
+
return 0;
}
@@ -568,6 +595,8 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
struct f2fs_mount_info org_mount_opt;
int err, active_logs;
+ sync_filesystem(sb);
+
/*
* Save the old mount options in case we
* need to restore them.
@@ -638,6 +667,8 @@ static struct inode *f2fs_nfs_get_inode(struct super_block *sb,
if (unlikely(ino < F2FS_ROOT_INO(sbi)))
return ERR_PTR(-ESTALE);
+ if (unlikely(ino >= NM_I(sbi)->max_nid))
+ return ERR_PTR(-ESTALE);
/*
* f2fs_iget isn't quite right if the inode is currently unallocated!
@@ -785,6 +816,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
for (i = 0; i < NR_COUNT_TYPE; i++)
atomic_set(&sbi->nr_pages[i], 0);
+
+ sbi->dir_level = DEF_DIR_LEVEL;
}
/*
@@ -896,11 +929,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
sbi->por_doing = false;
spin_lock_init(&sbi->stat_lock);
- mutex_init(&sbi->read_io.io_mutex);
+ init_rwsem(&sbi->read_io.io_rwsem);
sbi->read_io.sbi = sbi;
sbi->read_io.bio = NULL;
for (i = 0; i < NR_PAGE_TYPE; i++) {
- mutex_init(&sbi->write_io[i].io_mutex);
+ init_rwsem(&sbi->write_io[i].io_rwsem);
sbi->write_io[i].sbi = sbi;
sbi->write_io[i].bio = NULL;
}
@@ -989,28 +1022,9 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
goto free_root_inode;
}
- /* recover fsynced data */
- if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
- err = recover_fsync_data(sbi);
- if (err)
- f2fs_msg(sb, KERN_ERR,
- "Cannot recover all fsync data errno=%ld", err);
- }
-
- /*
- * If filesystem is not mounted as read-only then
- * do start the gc_thread.
- */
- if (!(sb->s_flags & MS_RDONLY)) {
- /* After POR, we can run background GC thread.*/
- err = start_gc_thread(sbi);
- if (err)
- goto free_gc;
- }
-
err = f2fs_build_stats(sbi);
if (err)
- goto free_gc;
+ goto free_root_inode;
if (f2fs_proc_root)
sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root);
@@ -1032,17 +1046,36 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL,
"%s", sb->s_id);
if (err)
- goto fail;
+ goto free_proc;
+
+ /* recover fsynced data */
+ if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
+ err = recover_fsync_data(sbi);
+ if (err)
+ f2fs_msg(sb, KERN_ERR,
+ "Cannot recover all fsync data errno=%ld", err);
+ }
+ /*
+ * If filesystem is not mounted as read-only then
+ * do start the gc_thread.
+ */
+ if (!(sb->s_flags & MS_RDONLY)) {
+ /* After POR, we can run background GC thread.*/
+ err = start_gc_thread(sbi);
+ if (err)
+ goto free_kobj;
+ }
return 0;
-fail:
+
+free_kobj:
+ kobject_del(&sbi->s_kobj);
+free_proc:
if (sbi->s_proc) {
remove_proc_entry("segment_info", sbi->s_proc);
remove_proc_entry(sb->s_id, f2fs_proc_root);
}
f2fs_destroy_stats(sbi);
-free_gc:
- stop_gc_thread(sbi);
free_root_inode:
dput(sb->s_root);
sb->s_root = NULL;
@@ -1082,7 +1115,7 @@ 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), NULL);
+ sizeof(struct f2fs_inode_info));
if (!f2fs_inode_cachep)
return -ENOMEM;
return 0;
diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
index 89d0422a91a8..503c2451131e 100644
--- a/fs/f2fs/xattr.c
+++ b/fs/f2fs/xattr.c
@@ -275,7 +275,7 @@ static void *read_all_xattrs(struct inode *inode, struct page *ipage)
inline_size = inline_xattr_size(inode);
- txattr_addr = kzalloc(inline_size + size, GFP_KERNEL);
+ txattr_addr = kzalloc(inline_size + size, GFP_F2FS_ZERO);
if (!txattr_addr)
return NULL;
@@ -407,6 +407,8 @@ int f2fs_getxattr(struct inode *inode, int name_index, const char *name,
if (name == NULL)
return -EINVAL;
name_len = strlen(name);
+ if (name_len > F2FS_NAME_LEN)
+ return -ERANGE;
base_addr = read_all_xattrs(inode, NULL);
if (!base_addr)
@@ -590,7 +592,10 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
f2fs_balance_fs(sbi);
f2fs_lock_op(sbi);
+ /* protect xattr_ver */
+ down_write(&F2FS_I(inode)->i_sem);
err = __f2fs_setxattr(inode, name_index, name, value, value_len, ipage);
+ up_write(&F2FS_I(inode)->i_sem);
f2fs_unlock_op(sbi);
return err;
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 854b578f6695..b3361fe2bcb5 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -490,7 +490,7 @@ EXPORT_SYMBOL_GPL(fat_build_inode);
static void fat_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
if (!inode->i_nlink) {
inode->i_size = 0;
fat_truncate_blocks(inode, 0);
@@ -635,6 +635,8 @@ static int fat_remount(struct super_block *sb, int *flags, char *data)
struct msdos_sb_info *sbi = MSDOS_SB(sb);
*flags |= MS_NODIRATIME | (sbi->options.isvfat ? 0 : MS_NOATIME);
+ sync_filesystem(sb);
+
/* make sure we update state on remount. */
new_rdonly = *flags & MS_RDONLY;
if (new_rdonly != (sb->s_flags & MS_RDONLY)) {
diff --git a/fs/fcntl.c b/fs/fcntl.c
index ef6866592a0f..9ead1596399a 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -272,9 +272,19 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
case F_SETFL:
err = setfl(fd, filp, arg);
break;
+#if BITS_PER_LONG != 32
+ /* 32-bit arches must use fcntl64() */
+ case F_GETLKP:
+#endif
case F_GETLK:
- err = fcntl_getlk(filp, (struct flock __user *) arg);
+ err = fcntl_getlk(filp, cmd, (struct flock __user *) arg);
break;
+#if BITS_PER_LONG != 32
+ /* 32-bit arches must use fcntl64() */
+ case F_SETLKP:
+ case F_SETLKPW:
+#endif
+ /* Fallthrough */
case F_SETLK:
case F_SETLKW:
err = fcntl_setlk(fd, filp, cmd, (struct flock __user *) arg);
@@ -388,17 +398,20 @@ SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
goto out1;
switch (cmd) {
- case F_GETLK64:
- err = fcntl_getlk64(f.file, (struct flock64 __user *) arg);
- break;
- case F_SETLK64:
- case F_SETLKW64:
- err = fcntl_setlk64(fd, f.file, cmd,
- (struct flock64 __user *) arg);
- break;
- default:
- err = do_fcntl(fd, cmd, arg, f.file);
- break;
+ case F_GETLK64:
+ case F_GETLKP:
+ err = fcntl_getlk64(f.file, cmd, (struct flock64 __user *) arg);
+ break;
+ case F_SETLK64:
+ case F_SETLKW64:
+ case F_SETLKP:
+ case F_SETLKPW:
+ err = fcntl_setlk64(fd, f.file, cmd,
+ (struct flock64 __user *) arg);
+ break;
+ default:
+ err = do_fcntl(fd, cmd, arg, f.file);
+ break;
}
out1:
fdput(f);
diff --git a/fs/file_table.c b/fs/file_table.c
index 5b24008ea4f6..01071c4d752e 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -235,7 +235,7 @@ static void __fput(struct file *file)
* in the file cleanup chain.
*/
eventpoll_release(file);
- locks_remove_flock(file);
+ locks_remove_file(file);
if (unlikely(file->f_flags & FASYNC)) {
if (file->f_op->fasync)
diff --git a/fs/filesystems.c b/fs/filesystems.c
index 92567d95ba6a..5797d45a78cb 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -121,6 +121,7 @@ int unregister_filesystem(struct file_system_type * fs)
EXPORT_SYMBOL(unregister_filesystem);
+#ifdef CONFIG_SYSFS_SYSCALL
static int fs_index(const char __user * __name)
{
struct file_system_type * tmp;
@@ -199,6 +200,7 @@ SYSCALL_DEFINE3(sysfs, int, option, unsigned long, arg1, unsigned long, arg2)
}
return retval;
}
+#endif
int __init get_filesystem_list(char *buf)
{
diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c
index f47df72cef17..363e3ae25f6b 100644
--- a/fs/freevxfs/vxfs_inode.c
+++ b/fs/freevxfs/vxfs_inode.c
@@ -354,7 +354,7 @@ static void vxfs_i_callback(struct rcu_head *head)
void
vxfs_evict_inode(struct inode *ip)
{
- truncate_inode_pages(&ip->i_data, 0);
+ truncate_inode_pages_final(&ip->i_data);
clear_inode(ip);
call_rcu(&ip->i_rcu, vxfs_i_callback);
}
diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c
index 25d4099a4aea..99c7f0a37af4 100644
--- a/fs/freevxfs/vxfs_lookup.c
+++ b/fs/freevxfs/vxfs_lookup.c
@@ -192,7 +192,7 @@ vxfs_inode_by_name(struct inode *dip, struct dentry *dp)
* vxfs_lookup - lookup pathname component
* @dip: dir in which we lookup
* @dp: dentry we lookup
- * @nd: lookup nameidata
+ * @flags: lookup flags
*
* Description:
* vxfs_lookup tries to lookup the pathname component described
diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c
index e37eb274e492..7ca8c75d50d3 100644
--- a/fs/freevxfs/vxfs_super.c
+++ b/fs/freevxfs/vxfs_super.c
@@ -124,6 +124,7 @@ vxfs_statfs(struct dentry *dentry, struct kstatfs *bufp)
static int vxfs_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
*flags |= MS_RDONLY;
return 0;
}
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index d754e3cf99a8..be568b7311d6 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -89,16 +89,31 @@ static inline struct inode *wb_inode(struct list_head *head)
#define CREATE_TRACE_POINTS
#include <trace/events/writeback.h>
+EXPORT_TRACEPOINT_SYMBOL_GPL(wbc_writepage);
+
+static void bdi_wakeup_thread(struct backing_dev_info *bdi)
+{
+ spin_lock_bh(&bdi->wb_lock);
+ if (test_bit(BDI_registered, &bdi->state))
+ mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
+ spin_unlock_bh(&bdi->wb_lock);
+}
+
static void bdi_queue_work(struct backing_dev_info *bdi,
struct wb_writeback_work *work)
{
trace_writeback_queue(bdi, work);
spin_lock_bh(&bdi->wb_lock);
+ if (!test_bit(BDI_registered, &bdi->state)) {
+ if (work->done)
+ complete(work->done);
+ goto out_unlock;
+ }
list_add_tail(&work->list, &bdi->work_list);
- spin_unlock_bh(&bdi->wb_lock);
-
mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
+out_unlock:
+ spin_unlock_bh(&bdi->wb_lock);
}
static void
@@ -114,7 +129,7 @@ __bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work) {
trace_writeback_nowork(bdi);
- mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
+ bdi_wakeup_thread(bdi);
return;
}
@@ -161,7 +176,7 @@ void bdi_start_background_writeback(struct backing_dev_info *bdi)
* writeback as soon as there is no other work to do.
*/
trace_writeback_wake_background(bdi);
- mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0);
+ bdi_wakeup_thread(bdi);
}
/*
@@ -1017,7 +1032,7 @@ void bdi_writeback_workfn(struct work_struct *work)
current->flags |= PF_SWAPWRITE;
if (likely(!current_is_workqueue_rescuer() ||
- list_empty(&bdi->bdi_list))) {
+ !test_bit(BDI_registered, &bdi->state))) {
/*
* The normal path. Keep writing back @bdi until its
* work_list is empty. Note that this path is also taken
@@ -1039,10 +1054,10 @@ void bdi_writeback_workfn(struct work_struct *work)
trace_writeback_pages_written(pages_written);
}
- if (!list_empty(&bdi->work_list) ||
- (wb_has_dirty_io(wb) && dirty_writeback_interval))
- queue_delayed_work(bdi_wq, &wb->dwork,
- msecs_to_jiffies(dirty_writeback_interval * 10));
+ if (!list_empty(&bdi->work_list))
+ mod_delayed_work(bdi_wq, &wb->dwork, 0);
+ else if (wb_has_dirty_io(wb) && dirty_writeback_interval)
+ bdi_wakeup_thread_delayed(bdi);
current->flags &= ~PF_SWAPWRITE;
}
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c
index b96a49b37d66..13b691a8a7d2 100644
--- a/fs/fuse/cuse.c
+++ b/fs/fuse/cuse.c
@@ -95,7 +95,7 @@ static ssize_t cuse_read(struct file *file, char __user *buf, size_t count,
struct iovec iov = { .iov_base = buf, .iov_len = count };
struct fuse_io_priv io = { .async = 0, .file = file };
- return fuse_direct_io(&io, &iov, 1, count, &pos, 0);
+ return fuse_direct_io(&io, &iov, 1, count, &pos, FUSE_DIO_CUSE);
}
static ssize_t cuse_write(struct file *file, const char __user *buf,
@@ -109,7 +109,8 @@ static ssize_t cuse_write(struct file *file, const char __user *buf,
* No locking or generic_write_checks(), the server is
* responsible for locking and sanity checks.
*/
- return fuse_direct_io(&io, &iov, 1, count, &pos, 1);
+ return fuse_direct_io(&io, &iov, 1, count, &pos,
+ FUSE_DIO_WRITE | FUSE_DIO_CUSE);
}
static int cuse_open(struct inode *inode, struct file *file)
@@ -568,7 +569,7 @@ static ssize_t cuse_class_waiting_show(struct device *dev,
return sprintf(buf, "%d\n", atomic_read(&cc->fc.num_waiting));
}
-static DEVICE_ATTR(waiting, S_IFREG | 0400, cuse_class_waiting_show, NULL);
+static DEVICE_ATTR(waiting, 0400, cuse_class_waiting_show, NULL);
static ssize_t cuse_class_abort_store(struct device *dev,
struct device_attribute *attr,
@@ -579,7 +580,7 @@ static ssize_t cuse_class_abort_store(struct device *dev,
fuse_abort_conn(&cc->fc);
return count;
}
-static DEVICE_ATTR(abort, S_IFREG | 0200, NULL, cuse_class_abort_store);
+static DEVICE_ATTR(abort, 0200, NULL, cuse_class_abort_store);
static struct attribute *cuse_class_dev_attrs[] = {
&dev_attr_waiting.attr,
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 1d1292c581c3..5b4e035b364c 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -839,6 +839,14 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
struct kstat *stat)
{
unsigned int blkbits;
+ struct fuse_conn *fc = get_fuse_conn(inode);
+
+ /* see the comment in fuse_change_attributes() */
+ if (fc->writeback_cache && S_ISREG(inode->i_mode)) {
+ attr->size = i_size_read(inode);
+ attr->mtime = inode->i_mtime.tv_sec;
+ attr->mtimensec = inode->i_mtime.tv_nsec;
+ }
stat->dev = inode->i_sb->s_dev;
stat->ino = attr->ino;
@@ -1477,12 +1485,16 @@ static long fuse_dir_compat_ioctl(struct file *file, unsigned int cmd,
FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR);
}
-static bool update_mtime(unsigned ivalid)
+static bool update_mtime(unsigned ivalid, bool trust_local_mtime)
{
/* Always update if mtime is explicitly set */
if (ivalid & ATTR_MTIME_SET)
return true;
+ /* Or if kernel i_mtime is the official one */
+ if (trust_local_mtime)
+ return true;
+
/* If it's an open(O_TRUNC) or an ftruncate(), don't update */
if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE)))
return false;
@@ -1491,7 +1503,8 @@ static bool update_mtime(unsigned ivalid)
return true;
}
-static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
+static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg,
+ bool trust_local_mtime)
{
unsigned ivalid = iattr->ia_valid;
@@ -1510,11 +1523,11 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
if (!(ivalid & ATTR_ATIME_SET))
arg->valid |= FATTR_ATIME_NOW;
}
- if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) {
+ if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_mtime)) {
arg->valid |= FATTR_MTIME;
arg->mtime = iattr->ia_mtime.tv_sec;
arg->mtimensec = iattr->ia_mtime.tv_nsec;
- if (!(ivalid & ATTR_MTIME_SET))
+ if (!(ivalid & ATTR_MTIME_SET) && !trust_local_mtime)
arg->valid |= FATTR_MTIME_NOW;
}
}
@@ -1563,6 +1576,63 @@ void fuse_release_nowrite(struct inode *inode)
spin_unlock(&fc->lock);
}
+static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_req *req,
+ struct inode *inode,
+ struct fuse_setattr_in *inarg_p,
+ struct fuse_attr_out *outarg_p)
+{
+ req->in.h.opcode = FUSE_SETATTR;
+ req->in.h.nodeid = get_node_id(inode);
+ req->in.numargs = 1;
+ req->in.args[0].size = sizeof(*inarg_p);
+ req->in.args[0].value = inarg_p;
+ req->out.numargs = 1;
+ if (fc->minor < 9)
+ req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE;
+ else
+ req->out.args[0].size = sizeof(*outarg_p);
+ req->out.args[0].value = outarg_p;
+}
+
+/*
+ * Flush inode->i_mtime to the server
+ */
+int fuse_flush_mtime(struct file *file, bool nofail)
+{
+ struct inode *inode = file->f_mapping->host;
+ struct fuse_inode *fi = get_fuse_inode(inode);
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_req *req = NULL;
+ struct fuse_setattr_in inarg;
+ struct fuse_attr_out outarg;
+ int err;
+
+ if (nofail) {
+ req = fuse_get_req_nofail_nopages(fc, file);
+ } else {
+ req = fuse_get_req_nopages(fc);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+ }
+
+ memset(&inarg, 0, sizeof(inarg));
+ memset(&outarg, 0, sizeof(outarg));
+
+ inarg.valid |= FATTR_MTIME;
+ inarg.mtime = inode->i_mtime.tv_sec;
+ inarg.mtimensec = inode->i_mtime.tv_nsec;
+
+ fuse_setattr_fill(fc, req, inode, &inarg, &outarg);
+ fuse_request_send(fc, req);
+ err = req->out.h.error;
+ fuse_put_request(fc, req);
+
+ if (!err)
+ clear_bit(FUSE_I_MTIME_DIRTY, &fi->state);
+
+ return err;
+}
+
/*
* Set attributes, and at the same time refresh them.
*
@@ -1580,8 +1650,10 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
struct fuse_setattr_in inarg;
struct fuse_attr_out outarg;
bool is_truncate = false;
+ bool is_wb = fc->writeback_cache;
loff_t oldsize;
int err;
+ bool trust_local_mtime = is_wb && S_ISREG(inode->i_mode);
if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
attr->ia_valid |= ATTR_FORCE;
@@ -1610,7 +1682,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
memset(&inarg, 0, sizeof(inarg));
memset(&outarg, 0, sizeof(outarg));
- iattr_to_fattr(attr, &inarg);
+ iattr_to_fattr(attr, &inarg, trust_local_mtime);
if (file) {
struct fuse_file *ff = file->private_data;
inarg.valid |= FATTR_FH;
@@ -1621,17 +1693,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
inarg.valid |= FATTR_LOCKOWNER;
inarg.lock_owner = fuse_lock_owner_id(fc, current->files);
}
- req->in.h.opcode = FUSE_SETATTR;
- req->in.h.nodeid = get_node_id(inode);
- req->in.numargs = 1;
- req->in.args[0].size = sizeof(inarg);
- req->in.args[0].value = &inarg;
- req->out.numargs = 1;
- if (fc->minor < 9)
- req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE;
- else
- req->out.args[0].size = sizeof(outarg);
- req->out.args[0].value = &outarg;
+ fuse_setattr_fill(fc, req, inode, &inarg, &outarg);
fuse_request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
@@ -1648,10 +1710,18 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
}
spin_lock(&fc->lock);
+ /* the kernel maintains i_mtime locally */
+ if (trust_local_mtime && (attr->ia_valid & ATTR_MTIME)) {
+ inode->i_mtime = attr->ia_mtime;
+ clear_bit(FUSE_I_MTIME_DIRTY, &fi->state);
+ }
+
fuse_change_attributes_common(inode, &outarg.attr,
attr_timeout(&outarg));
oldsize = inode->i_size;
- i_size_write(inode, outarg.attr.size);
+ /* see the comment in fuse_change_attributes() */
+ if (!is_wb || is_truncate || !S_ISREG(inode->i_mode))
+ i_size_write(inode, outarg.attr.size);
if (is_truncate) {
/* NOTE: this may release/reacquire fc->lock */
@@ -1663,7 +1733,8 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
* Only call invalidate_inode_pages2() after removing
* FUSE_NOWRITE, otherwise fuse_launder_page() would deadlock.
*/
- if (S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) {
+ if ((is_truncate || !is_wb) &&
+ S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) {
truncate_pagecache(inode, outarg.attr.size);
invalidate_inode_pages2(inode->i_mapping);
}
@@ -1875,6 +1946,17 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
return err;
}
+static int fuse_update_time(struct inode *inode, struct timespec *now,
+ int flags)
+{
+ if (flags & S_MTIME) {
+ inode->i_mtime = *now;
+ set_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state);
+ BUG_ON(!S_ISREG(inode->i_mode));
+ }
+ return 0;
+}
+
static const struct inode_operations fuse_dir_inode_operations = {
.lookup = fuse_lookup,
.mkdir = fuse_mkdir,
@@ -1914,6 +1996,7 @@ static const struct inode_operations fuse_common_inode_operations = {
.getxattr = fuse_getxattr,
.listxattr = fuse_listxattr,
.removexattr = fuse_removexattr,
+ .update_time = fuse_update_time,
};
static const struct inode_operations fuse_symlink_inode_operations = {
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 77bcc303c3ae..65df7d8be4f5 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -188,6 +188,22 @@ int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
}
EXPORT_SYMBOL_GPL(fuse_do_open);
+static void fuse_link_write_file(struct file *file)
+{
+ struct inode *inode = file_inode(file);
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_inode *fi = get_fuse_inode(inode);
+ struct fuse_file *ff = file->private_data;
+ /*
+ * file may be written through mmap, so chain it onto the
+ * inodes's write_file list
+ */
+ spin_lock(&fc->lock);
+ if (list_empty(&ff->write_entry))
+ list_add(&ff->write_entry, &fi->write_files);
+ spin_unlock(&fc->lock);
+}
+
void fuse_finish_open(struct inode *inode, struct file *file)
{
struct fuse_file *ff = file->private_data;
@@ -208,6 +224,8 @@ void fuse_finish_open(struct inode *inode, struct file *file)
spin_unlock(&fc->lock);
fuse_invalidate_attr(inode);
}
+ if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)
+ fuse_link_write_file(file);
}
int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
@@ -292,6 +310,15 @@ static int fuse_open(struct inode *inode, struct file *file)
static int fuse_release(struct inode *inode, struct file *file)
{
+ struct fuse_conn *fc = get_fuse_conn(inode);
+
+ /* see fuse_vma_close() for !writeback_cache case */
+ if (fc->writeback_cache)
+ filemap_write_and_wait(file->f_mapping);
+
+ if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state))
+ fuse_flush_mtime(file, true);
+
fuse_release_common(file, FUSE_RELEASE);
/* return value is ignored by VFS */
@@ -333,12 +360,13 @@ u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id)
}
/*
- * Check if page is under writeback
+ * Check if any page in a range is under writeback
*
* This is currently done by walking the list of writepage requests
* for the inode, which can be pretty inefficient.
*/
-static bool fuse_page_is_writeback(struct inode *inode, pgoff_t index)
+static bool fuse_range_is_writeback(struct inode *inode, pgoff_t idx_from,
+ pgoff_t idx_to)
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
@@ -351,8 +379,8 @@ static bool fuse_page_is_writeback(struct inode *inode, pgoff_t index)
BUG_ON(req->inode != inode);
curr_index = req->misc.write.in.offset >> PAGE_CACHE_SHIFT;
- if (curr_index <= index &&
- index < curr_index + req->num_pages) {
+ if (idx_from < curr_index + req->num_pages &&
+ curr_index <= idx_to) {
found = true;
break;
}
@@ -362,6 +390,11 @@ static bool fuse_page_is_writeback(struct inode *inode, pgoff_t index)
return found;
}
+static inline bool fuse_page_is_writeback(struct inode *inode, pgoff_t index)
+{
+ return fuse_range_is_writeback(inode, index, index);
+}
+
/*
* Wait for page writeback to be completed.
*
@@ -376,6 +409,21 @@ static int fuse_wait_on_page_writeback(struct inode *inode, pgoff_t index)
return 0;
}
+/*
+ * Wait for all pending writepages on the inode to finish.
+ *
+ * This is currently done by blocking further writes with FUSE_NOWRITE
+ * and waiting for all sent writes to complete.
+ *
+ * This must be called under i_mutex, otherwise the FUSE_NOWRITE usage
+ * could conflict with truncation.
+ */
+static void fuse_sync_writes(struct inode *inode)
+{
+ fuse_set_nowrite(inode);
+ fuse_release_nowrite(inode);
+}
+
static int fuse_flush(struct file *file, fl_owner_t id)
{
struct inode *inode = file_inode(file);
@@ -391,6 +439,14 @@ static int fuse_flush(struct file *file, fl_owner_t id)
if (fc->no_flush)
return 0;
+ err = filemap_write_and_wait(file->f_mapping);
+ if (err)
+ return err;
+
+ mutex_lock(&inode->i_mutex);
+ fuse_sync_writes(inode);
+ mutex_unlock(&inode->i_mutex);
+
req = fuse_get_req_nofail_nopages(fc, file);
memset(&inarg, 0, sizeof(inarg));
inarg.fh = ff->fh;
@@ -411,21 +467,6 @@ static int fuse_flush(struct file *file, fl_owner_t id)
return err;
}
-/*
- * Wait for all pending writepages on the inode to finish.
- *
- * This is currently done by blocking further writes with FUSE_NOWRITE
- * and waiting for all sent writes to complete.
- *
- * This must be called under i_mutex, otherwise the FUSE_NOWRITE usage
- * could conflict with truncation.
- */
-static void fuse_sync_writes(struct inode *inode)
-{
- fuse_set_nowrite(inode);
- fuse_release_nowrite(inode);
-}
-
int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
int datasync, int isdir)
{
@@ -459,6 +500,12 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
fuse_sync_writes(inode);
+ if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) {
+ int err = fuse_flush_mtime(file, false);
+ if (err)
+ goto out;
+ }
+
req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -655,7 +702,33 @@ static void fuse_read_update_size(struct inode *inode, loff_t size,
spin_unlock(&fc->lock);
}
-static int fuse_readpage(struct file *file, struct page *page)
+static void fuse_short_read(struct fuse_req *req, struct inode *inode,
+ u64 attr_ver)
+{
+ size_t num_read = req->out.args[0].size;
+ struct fuse_conn *fc = get_fuse_conn(inode);
+
+ if (fc->writeback_cache) {
+ /*
+ * A hole in a file. Some data after the hole are in page cache,
+ * but have not reached the client fs yet. So, the hole is not
+ * present there.
+ */
+ int i;
+ int start_idx = num_read >> PAGE_CACHE_SHIFT;
+ size_t off = num_read & (PAGE_CACHE_SIZE - 1);
+
+ for (i = start_idx; i < req->num_pages; i++) {
+ zero_user_segment(req->pages[i], off, PAGE_CACHE_SIZE);
+ off = 0;
+ }
+ } else {
+ loff_t pos = page_offset(req->pages[0]) + num_read;
+ fuse_read_update_size(inode, pos, attr_ver);
+ }
+}
+
+static int fuse_do_readpage(struct file *file, struct page *page)
{
struct fuse_io_priv io = { .async = 0, .file = file };
struct inode *inode = page->mapping->host;
@@ -667,10 +740,6 @@ static int fuse_readpage(struct file *file, struct page *page)
u64 attr_ver;
int err;
- err = -EIO;
- if (is_bad_inode(inode))
- goto out;
-
/*
* Page writeback can extend beyond the lifetime of the
* page-cache page, so make sure we read a properly synced
@@ -679,9 +748,8 @@ static int fuse_readpage(struct file *file, struct page *page)
fuse_wait_on_page_writeback(inode, page->index);
req = fuse_get_req(fc, 1);
- err = PTR_ERR(req);
if (IS_ERR(req))
- goto out;
+ return PTR_ERR(req);
attr_ver = fuse_get_attr_version(fc);
@@ -692,18 +760,32 @@ static int fuse_readpage(struct file *file, struct page *page)
req->page_descs[0].length = count;
num_read = fuse_send_read(req, &io, pos, count, NULL);
err = req->out.h.error;
- fuse_put_request(fc, req);
if (!err) {
/*
* Short read means EOF. If file size is larger, truncate it
*/
if (num_read < count)
- fuse_read_update_size(inode, pos + num_read, attr_ver);
+ fuse_short_read(req, inode, attr_ver);
SetPageUptodate(page);
}
+ fuse_put_request(fc, req);
+
+ return err;
+}
+
+static int fuse_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ int err;
+
+ err = -EIO;
+ if (is_bad_inode(inode))
+ goto out;
+
+ err = fuse_do_readpage(file, page);
fuse_invalidate_atime(inode);
out:
unlock_page(page);
@@ -726,13 +808,9 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
/*
* Short read means EOF. If file size is larger, truncate it
*/
- if (!req->out.h.error && num_read < count) {
- loff_t pos;
+ if (!req->out.h.error && num_read < count)
+ fuse_short_read(req, inode, req->misc.read.attr_ver);
- pos = page_offset(req->pages[0]) + num_read;
- fuse_read_update_size(inode, pos,
- req->misc.read.attr_ver);
- }
fuse_invalidate_atime(inode);
}
@@ -922,16 +1000,21 @@ static size_t fuse_send_write(struct fuse_req *req, struct fuse_io_priv *io,
return req->misc.write.out.size;
}
-void fuse_write_update_size(struct inode *inode, loff_t pos)
+bool fuse_write_update_size(struct inode *inode, loff_t pos)
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
+ bool ret = false;
spin_lock(&fc->lock);
fi->attr_version = ++fc->attr_version;
- if (pos > inode->i_size)
+ if (pos > inode->i_size) {
i_size_write(inode, pos);
+ ret = true;
+ }
spin_unlock(&fc->lock);
+
+ return ret;
}
static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file,
@@ -1116,6 +1199,15 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
struct iov_iter i;
loff_t endbyte = 0;
+ if (get_fuse_conn(inode)->writeback_cache) {
+ /* Update size (EOF optimization) and mode (SUID clearing) */
+ err = fuse_update_attributes(mapping->host, NULL, file, NULL);
+ if (err)
+ return err;
+
+ return generic_file_aio_write(iocb, iov, nr_segs, pos);
+ }
+
WARN_ON(iocb->ki_pos != pos);
ocount = 0;
@@ -1289,13 +1381,18 @@ static inline int fuse_iter_npages(const struct iov_iter *ii_p)
ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov,
unsigned long nr_segs, size_t count, loff_t *ppos,
- int write)
+ int flags)
{
+ int write = flags & FUSE_DIO_WRITE;
+ int cuse = flags & FUSE_DIO_CUSE;
struct file *file = io->file;
+ struct inode *inode = file->f_mapping->host;
struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = ff->fc;
size_t nmax = write ? fc->max_write : fc->max_read;
loff_t pos = *ppos;
+ pgoff_t idx_from = pos >> PAGE_CACHE_SHIFT;
+ pgoff_t idx_to = (pos + count - 1) >> PAGE_CACHE_SHIFT;
ssize_t res = 0;
struct fuse_req *req;
struct iov_iter ii;
@@ -1309,6 +1406,14 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov,
if (IS_ERR(req))
return PTR_ERR(req);
+ if (!cuse && fuse_range_is_writeback(inode, idx_from, idx_to)) {
+ if (!write)
+ mutex_lock(&inode->i_mutex);
+ fuse_sync_writes(inode);
+ if (!write)
+ mutex_unlock(&inode->i_mutex);
+ }
+
while (count) {
size_t nres;
fl_owner_t owner = current->files;
@@ -1397,7 +1502,8 @@ static ssize_t __fuse_direct_write(struct fuse_io_priv *io,
res = generic_write_checks(file, ppos, &count, 0);
if (!res)
- res = fuse_direct_io(io, iov, nr_segs, count, ppos, 1);
+ res = fuse_direct_io(io, iov, nr_segs, count, ppos,
+ FUSE_DIO_WRITE);
fuse_invalidate_attr(inode);
@@ -1885,6 +1991,77 @@ out:
return err;
}
+/*
+ * It's worthy to make sure that space is reserved on disk for the write,
+ * but how to implement it without killing performance need more thinking.
+ */
+static int fuse_write_begin(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned flags,
+ struct page **pagep, void **fsdata)
+{
+ pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+ struct fuse_conn *fc = get_fuse_conn(file->f_dentry->d_inode);
+ struct page *page;
+ loff_t fsize;
+ int err = -ENOMEM;
+
+ WARN_ON(!fc->writeback_cache);
+
+ page = grab_cache_page_write_begin(mapping, index, flags);
+ if (!page)
+ goto error;
+
+ fuse_wait_on_page_writeback(mapping->host, page->index);
+
+ if (PageUptodate(page) || len == PAGE_CACHE_SIZE)
+ goto success;
+ /*
+ * Check if the start this page comes after the end of file, in which
+ * case the readpage can be optimized away.
+ */
+ fsize = i_size_read(mapping->host);
+ if (fsize <= (pos & PAGE_CACHE_MASK)) {
+ size_t off = pos & ~PAGE_CACHE_MASK;
+ if (off)
+ zero_user_segment(page, 0, off);
+ goto success;
+ }
+ err = fuse_do_readpage(file, page);
+ if (err)
+ goto cleanup;
+success:
+ *pagep = page;
+ return 0;
+
+cleanup:
+ unlock_page(page);
+ page_cache_release(page);
+error:
+ return err;
+}
+
+static int fuse_write_end(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned copied,
+ struct page *page, void *fsdata)
+{
+ struct inode *inode = page->mapping->host;
+
+ if (!PageUptodate(page)) {
+ /* Zero any unwritten bytes at the end of the page */
+ size_t endoff = (pos + copied) & ~PAGE_CACHE_MASK;
+ if (endoff)
+ zero_user_segment(page, endoff, PAGE_CACHE_SIZE);
+ SetPageUptodate(page);
+ }
+
+ fuse_write_update_size(inode, pos + copied);
+ set_page_dirty(page);
+ unlock_page(page);
+ page_cache_release(page);
+
+ return copied;
+}
+
static int fuse_launder_page(struct page *page)
{
int err = 0;
@@ -1946,20 +2123,9 @@ static const struct vm_operations_struct fuse_file_vm_ops = {
static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
{
- if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {
- struct inode *inode = file_inode(file);
- struct fuse_conn *fc = get_fuse_conn(inode);
- struct fuse_inode *fi = get_fuse_inode(inode);
- struct fuse_file *ff = file->private_data;
- /*
- * file may be written through mmap, so chain it onto the
- * inodes's write_file list
- */
- spin_lock(&fc->lock);
- if (list_empty(&ff->write_entry))
- list_add(&ff->write_entry, &fi->write_files);
- spin_unlock(&fc->lock);
- }
+ if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
+ fuse_link_write_file(file);
+
file_accessed(file);
vma->vm_ops = &fuse_file_vm_ops;
return 0;
@@ -2606,7 +2772,7 @@ static void fuse_register_polled_file(struct fuse_conn *fc,
{
spin_lock(&fc->lock);
if (RB_EMPTY_NODE(&ff->polled_node)) {
- struct rb_node **link, *parent;
+ struct rb_node **link, *uninitialized_var(parent);
link = fuse_find_polled_node(fc, ff->kh, &parent);
BUG_ON(*link);
@@ -2850,8 +3016,16 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
goto out;
/* we could have extended the file */
- if (!(mode & FALLOC_FL_KEEP_SIZE))
- fuse_write_update_size(inode, offset + length);
+ if (!(mode & FALLOC_FL_KEEP_SIZE)) {
+ bool changed = fuse_write_update_size(inode, offset + length);
+
+ if (changed && fc->writeback_cache) {
+ struct fuse_inode *fi = get_fuse_inode(inode);
+
+ inode->i_mtime = current_fs_time(inode->i_sb);
+ set_bit(FUSE_I_MTIME_DIRTY, &fi->state);
+ }
+ }
if (mode & FALLOC_FL_PUNCH_HOLE)
truncate_pagecache_range(inode, offset, offset + length - 1);
@@ -2915,6 +3089,8 @@ static const struct address_space_operations fuse_file_aops = {
.set_page_dirty = __set_page_dirty_nobuffers,
.bmap = fuse_bmap,
.direct_IO = fuse_direct_IO,
+ .write_begin = fuse_write_begin,
+ .write_end = fuse_write_end,
};
void fuse_init_file_inode(struct inode *inode)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 2da5db2c8bdb..a257ed8ebee6 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -119,6 +119,8 @@ enum {
FUSE_I_INIT_RDPLUS,
/** An operation changing file size is in progress */
FUSE_I_SIZE_UNSTABLE,
+ /** i_mtime has been updated locally; a flush to userspace needed */
+ FUSE_I_MTIME_DIRTY,
};
struct fuse_conn;
@@ -480,6 +482,9 @@ struct fuse_conn {
/** Set if bdi is valid */
unsigned bdi_initialized:1;
+ /** write-back cache policy (default is write-through) */
+ unsigned writeback_cache:1;
+
/*
* The following bitfields are only for optimization purposes
* and hence races in setting them will not cause malfunction
@@ -863,9 +868,20 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
bool isdir);
+
+/**
+ * fuse_direct_io() flags
+ */
+
+/** If set, it is WRITE; otherwise - READ */
+#define FUSE_DIO_WRITE (1 << 0)
+
+/** CUSE pass fuse_direct_io() a file which f_mapping->host is not from FUSE */
+#define FUSE_DIO_CUSE (1 << 1)
+
ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov,
unsigned long nr_segs, size_t count, loff_t *ppos,
- int write);
+ int flags);
long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
unsigned int flags);
long fuse_ioctl_common(struct file *file, unsigned int cmd,
@@ -873,7 +889,9 @@ long fuse_ioctl_common(struct file *file, unsigned int cmd,
unsigned fuse_file_poll(struct file *file, poll_table *wait);
int fuse_dev_release(struct inode *inode, struct file *file);
-void fuse_write_update_size(struct inode *inode, loff_t pos);
+bool fuse_write_update_size(struct inode *inode, loff_t pos);
+
+int fuse_flush_mtime(struct file *file, bool nofail);
int fuse_do_setattr(struct inode *inode, struct iattr *attr,
struct file *file);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index d468643a68b2..8d611696fcad 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -123,7 +123,7 @@ static void fuse_destroy_inode(struct inode *inode)
static void fuse_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (inode->i_sb->s_flags & MS_ACTIVE) {
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -135,6 +135,7 @@ static void fuse_evict_inode(struct inode *inode)
static int fuse_remount_fs(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
if (*flags & MS_MANDLOCK)
return -EINVAL;
@@ -170,8 +171,11 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
inode->i_blocks = attr->blocks;
inode->i_atime.tv_sec = attr->atime;
inode->i_atime.tv_nsec = attr->atimensec;
- inode->i_mtime.tv_sec = attr->mtime;
- inode->i_mtime.tv_nsec = attr->mtimensec;
+ /* mtime from server may be stale due to local buffered write */
+ if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) {
+ inode->i_mtime.tv_sec = attr->mtime;
+ inode->i_mtime.tv_nsec = attr->mtimensec;
+ }
inode->i_ctime.tv_sec = attr->ctime;
inode->i_ctime.tv_nsec = attr->ctimensec;
@@ -197,6 +201,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
+ bool is_wb = fc->writeback_cache;
loff_t oldsize;
struct timespec old_mtime;
@@ -211,10 +216,16 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
fuse_change_attributes_common(inode, attr, attr_valid);
oldsize = inode->i_size;
- i_size_write(inode, attr->size);
+ /*
+ * In case of writeback_cache enabled, the cached writes beyond EOF
+ * extend local i_size without keeping userspace server in sync. So,
+ * attr->size coming from server can be stale. We cannot trust it.
+ */
+ if (!is_wb || !S_ISREG(inode->i_mode))
+ i_size_write(inode, attr->size);
spin_unlock(&fc->lock);
- if (S_ISREG(inode->i_mode)) {
+ if (!is_wb && S_ISREG(inode->i_mode)) {
bool inval = false;
if (oldsize != attr->size) {
@@ -243,6 +254,8 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
{
inode->i_mode = attr->mode & S_IFMT;
inode->i_size = attr->size;
+ inode->i_mtime.tv_sec = attr->mtime;
+ inode->i_mtime.tv_nsec = attr->mtimensec;
if (S_ISREG(inode->i_mode)) {
fuse_init_common(inode);
fuse_init_file_inode(inode);
@@ -289,7 +302,9 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
return NULL;
if ((inode->i_state & I_NEW)) {
- inode->i_flags |= S_NOATIME|S_NOCMTIME;
+ inode->i_flags |= S_NOATIME;
+ if (!fc->writeback_cache || !S_ISREG(inode->i_mode))
+ inode->i_flags |= S_NOCMTIME;
inode->i_generation = generation;
inode->i_data.backing_dev_info = &fc->bdi;
fuse_init_inode(inode, attr);
@@ -873,6 +888,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
}
if (arg->flags & FUSE_ASYNC_DIO)
fc->async_dio = 1;
+ if (arg->flags & FUSE_WRITEBACK_CACHE)
+ fc->writeback_cache = 1;
} else {
ra_pages = fc->max_read / PAGE_CACHE_SIZE;
fc->no_lock = 1;
@@ -900,7 +917,8 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK |
FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |
FUSE_FLOCK_LOCKS | FUSE_IOCTL_DIR | FUSE_AUTO_INVAL_DATA |
- FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO;
+ FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
+ FUSE_WRITEBACK_CACHE;
req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg);
diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c
index ba9456685f47..3088e2a38e30 100644
--- a/fs/gfs2/acl.c
+++ b/fs/gfs2/acl.c
@@ -64,18 +64,6 @@ struct posix_acl *gfs2_get_acl(struct inode *inode, int type)
return acl;
}
-static int gfs2_set_mode(struct inode *inode, umode_t mode)
-{
- int error = 0;
-
- if (mode != inode->i_mode) {
- inode->i_mode = mode;
- mark_inode_dirty(inode);
- }
-
- return error;
-}
-
int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
int error;
@@ -85,8 +73,8 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
BUG_ON(name == NULL);
- if (acl->a_count > GFS2_ACL_MAX_ENTRIES)
- return -EINVAL;
+ if (acl->a_count > GFS2_ACL_MAX_ENTRIES(GFS2_SB(inode)))
+ return -E2BIG;
if (type == ACL_TYPE_ACCESS) {
umode_t mode = inode->i_mode;
@@ -98,9 +86,10 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (error == 0)
acl = NULL;
- error = gfs2_set_mode(inode, mode);
- if (error)
- return error;
+ if (mode != inode->i_mode) {
+ inode->i_mode = mode;
+ mark_inode_dirty(inode);
+ }
}
if (acl) {
diff --git a/fs/gfs2/acl.h b/fs/gfs2/acl.h
index 301260c999ba..2d65ec4cd4be 100644
--- a/fs/gfs2/acl.h
+++ b/fs/gfs2/acl.h
@@ -14,7 +14,7 @@
#define GFS2_POSIX_ACL_ACCESS "posix_acl_access"
#define GFS2_POSIX_ACL_DEFAULT "posix_acl_default"
-#define GFS2_ACL_MAX_ENTRIES 25
+#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);
extern int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type);
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 49436fa7cd4f..ce62dcac90b6 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -21,6 +21,7 @@
#include <linux/gfs2_ondisk.h>
#include <linux/backing-dev.h>
#include <linux/aio.h>
+#include <trace/events/writeback.h>
#include "gfs2.h"
#include "incore.h"
@@ -230,13 +231,11 @@ static int gfs2_writepages(struct address_space *mapping,
static int gfs2_write_jdata_pagevec(struct address_space *mapping,
struct writeback_control *wbc,
struct pagevec *pvec,
- int nr_pages, pgoff_t end)
+ int nr_pages, pgoff_t end,
+ pgoff_t *done_index)
{
struct inode *inode = mapping->host;
struct gfs2_sbd *sdp = GFS2_SB(inode);
- loff_t i_size = i_size_read(inode);
- pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT;
- unsigned offset = i_size & (PAGE_CACHE_SIZE-1);
unsigned nrblocks = nr_pages * (PAGE_CACHE_SIZE/inode->i_sb->s_blocksize);
int i;
int ret;
@@ -248,40 +247,83 @@ static int gfs2_write_jdata_pagevec(struct address_space *mapping,
for(i = 0; i < nr_pages; i++) {
struct page *page = pvec->pages[i];
+ /*
+ * At this point, the page may be truncated or
+ * invalidated (changing page->mapping to NULL), or
+ * even swizzled back from swapper_space to tmpfs file
+ * mapping. However, page->index will not change
+ * because we have a reference on the page.
+ */
+ if (page->index > end) {
+ /*
+ * can't be range_cyclic (1st pass) because
+ * end == -1 in that case.
+ */
+ ret = 1;
+ break;
+ }
+
+ *done_index = page->index;
+
lock_page(page);
if (unlikely(page->mapping != mapping)) {
+continue_unlock:
unlock_page(page);
continue;
}
- if (!wbc->range_cyclic && page->index > end) {
- ret = 1;
- unlock_page(page);
- continue;
+ if (!PageDirty(page)) {
+ /* someone wrote it for us */
+ goto continue_unlock;
}
- if (wbc->sync_mode != WB_SYNC_NONE)
- wait_on_page_writeback(page);
-
- if (PageWriteback(page) ||
- !clear_page_dirty_for_io(page)) {
- unlock_page(page);
- continue;
+ if (PageWriteback(page)) {
+ if (wbc->sync_mode != WB_SYNC_NONE)
+ wait_on_page_writeback(page);
+ else
+ goto continue_unlock;
}
- /* Is the page fully outside i_size? (truncate in progress) */
- if (page->index > end_index || (page->index == end_index && !offset)) {
- page->mapping->a_ops->invalidatepage(page, 0,
- PAGE_CACHE_SIZE);
- unlock_page(page);
- continue;
- }
+ BUG_ON(PageWriteback(page));
+ if (!clear_page_dirty_for_io(page))
+ goto continue_unlock;
+
+ trace_wbc_writepage(wbc, mapping->backing_dev_info);
ret = __gfs2_jdata_writepage(page, wbc);
+ if (unlikely(ret)) {
+ if (ret == AOP_WRITEPAGE_ACTIVATE) {
+ unlock_page(page);
+ ret = 0;
+ } else {
+
+ /*
+ * done_index is set past this page,
+ * so media errors will not choke
+ * background writeout for the entire
+ * file. This has consequences for
+ * range_cyclic semantics (ie. it may
+ * not be suitable for data integrity
+ * writeout).
+ */
+ *done_index = page->index + 1;
+ ret = 1;
+ break;
+ }
+ }
- if (ret || (--(wbc->nr_to_write) <= 0))
+ /*
+ * We stop writing back only if we are not doing
+ * integrity sync. In case of integrity sync we have to
+ * keep going until we have written all the pages
+ * we tagged for writeback prior to entering this loop.
+ */
+ if (--wbc->nr_to_write <= 0 && wbc->sync_mode == WB_SYNC_NONE) {
ret = 1;
+ break;
+ }
+
}
gfs2_trans_end(sdp);
return ret;
@@ -306,51 +348,69 @@ static int gfs2_write_cache_jdata(struct address_space *mapping,
int done = 0;
struct pagevec pvec;
int nr_pages;
+ pgoff_t uninitialized_var(writeback_index);
pgoff_t index;
pgoff_t end;
- int scanned = 0;
+ pgoff_t done_index;
+ int cycled;
int range_whole = 0;
+ int tag;
pagevec_init(&pvec, 0);
if (wbc->range_cyclic) {
- index = mapping->writeback_index; /* Start from prev offset */
+ writeback_index = mapping->writeback_index; /* prev offset */
+ index = writeback_index;
+ if (index == 0)
+ cycled = 1;
+ else
+ cycled = 0;
end = -1;
} else {
index = wbc->range_start >> PAGE_CACHE_SHIFT;
end = wbc->range_end >> PAGE_CACHE_SHIFT;
if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX)
range_whole = 1;
- scanned = 1;
+ cycled = 1; /* ignore range_cyclic tests */
}
+ if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages)
+ tag = PAGECACHE_TAG_TOWRITE;
+ else
+ tag = PAGECACHE_TAG_DIRTY;
retry:
- while (!done && (index <= end) &&
- (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
- PAGECACHE_TAG_DIRTY,
- min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1))) {
- scanned = 1;
- ret = gfs2_write_jdata_pagevec(mapping, wbc, &pvec, nr_pages, end);
+ if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages)
+ tag_pages_for_writeback(mapping, index, end);
+ done_index = index;
+ while (!done && (index <= end)) {
+ nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, tag,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1);
+ if (nr_pages == 0)
+ break;
+
+ ret = gfs2_write_jdata_pagevec(mapping, wbc, &pvec, nr_pages, end, &done_index);
if (ret)
done = 1;
if (ret > 0)
ret = 0;
-
pagevec_release(&pvec);
cond_resched();
}
- if (!scanned && !done) {
+ if (!cycled && !done) {
/*
+ * range_cyclic:
* We hit the last page and there is more work to be done: wrap
* back to the start of the file
*/
- scanned = 1;
+ cycled = 1;
index = 0;
+ end = writeback_index - 1;
goto retry;
}
if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0))
- mapping->writeback_index = index;
+ mapping->writeback_index = done_index;
+
return ret;
}
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index fe0500c0af7a..c62d4b9f51dc 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -1328,6 +1328,121 @@ int gfs2_file_dealloc(struct gfs2_inode *ip)
}
/**
+ * gfs2_free_journal_extents - Free cached journal bmap info
+ * @jd: The journal
+ *
+ */
+
+void gfs2_free_journal_extents(struct gfs2_jdesc *jd)
+{
+ struct gfs2_journal_extent *jext;
+
+ while(!list_empty(&jd->extent_list)) {
+ jext = list_entry(jd->extent_list.next, struct gfs2_journal_extent, list);
+ list_del(&jext->list);
+ kfree(jext);
+ }
+}
+
+/**
+ * gfs2_add_jextent - Add or merge a new extent to extent cache
+ * @jd: The journal descriptor
+ * @lblock: The logical block at start of new extent
+ * @pblock: The physical block at start of new extent
+ * @blocks: Size of extent in fs blocks
+ *
+ * Returns: 0 on success or -ENOMEM
+ */
+
+static int gfs2_add_jextent(struct gfs2_jdesc *jd, u64 lblock, u64 dblock, u64 blocks)
+{
+ struct gfs2_journal_extent *jext;
+
+ if (!list_empty(&jd->extent_list)) {
+ jext = list_entry(jd->extent_list.prev, struct gfs2_journal_extent, list);
+ if ((jext->dblock + jext->blocks) == dblock) {
+ jext->blocks += blocks;
+ return 0;
+ }
+ }
+
+ jext = kzalloc(sizeof(struct gfs2_journal_extent), GFP_NOFS);
+ if (jext == NULL)
+ return -ENOMEM;
+ jext->dblock = dblock;
+ jext->lblock = lblock;
+ jext->blocks = blocks;
+ list_add_tail(&jext->list, &jd->extent_list);
+ jd->nr_extents++;
+ return 0;
+}
+
+/**
+ * gfs2_map_journal_extents - Cache journal bmap info
+ * @sdp: The super block
+ * @jd: The journal to map
+ *
+ * Create a reusable "extent" mapping from all logical
+ * blocks to all physical blocks for the given journal. This will save
+ * us time when writing journal blocks. Most journals will have only one
+ * extent that maps all their logical blocks. That's because gfs2.mkfs
+ * arranges the journal blocks sequentially to maximize performance.
+ * So the extent would map the first block for the entire file length.
+ * However, gfs2_jadd can happen while file activity is happening, so
+ * those journals may not be sequential. Less likely is the case where
+ * the users created their own journals by mounting the metafs and
+ * laying it out. But it's still possible. These journals might have
+ * several extents.
+ *
+ * Returns: 0 on success, or error on failure
+ */
+
+int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd)
+{
+ u64 lblock = 0;
+ u64 lblock_stop;
+ struct gfs2_inode *ip = GFS2_I(jd->jd_inode);
+ struct buffer_head bh;
+ unsigned int shift = sdp->sd_sb.sb_bsize_shift;
+ u64 size;
+ int rc;
+
+ lblock_stop = i_size_read(jd->jd_inode) >> shift;
+ size = (lblock_stop - lblock) << shift;
+ jd->nr_extents = 0;
+ WARN_ON(!list_empty(&jd->extent_list));
+
+ do {
+ bh.b_state = 0;
+ bh.b_blocknr = 0;
+ bh.b_size = size;
+ rc = gfs2_block_map(jd->jd_inode, lblock, &bh, 0);
+ if (rc || !buffer_mapped(&bh))
+ goto fail;
+ rc = gfs2_add_jextent(jd, lblock, bh.b_blocknr, bh.b_size >> shift);
+ if (rc)
+ goto fail;
+ size -= bh.b_size;
+ lblock += (bh.b_size >> ip->i_inode.i_blkbits);
+ } while(size > 0);
+
+ fs_info(sdp, "journal %d mapped with %u extents\n", jd->jd_jid,
+ jd->nr_extents);
+ return 0;
+
+fail:
+ fs_warn(sdp, "error %d mapping journal %u at offset %llu (extent %u)\n",
+ rc, jd->jd_jid,
+ (unsigned long long)(i_size_read(jd->jd_inode) - size),
+ jd->nr_extents);
+ fs_warn(sdp, "bmap=%d lblock=%llu block=%llu, state=0x%08lx, size=%llu\n",
+ rc, (unsigned long long)lblock, (unsigned long long)bh.b_blocknr,
+ bh.b_state, (unsigned long long)bh.b_size);
+ gfs2_free_journal_extents(jd);
+ return rc;
+}
+
+/**
* gfs2_write_alloc_required - figure out if a write will require an allocation
* @ip: the file being written to
* @offset: the offset to write to
diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h
index 42fea03e2bd9..81ded5e2aaa2 100644
--- a/fs/gfs2/bmap.h
+++ b/fs/gfs2/bmap.h
@@ -55,5 +55,7 @@ extern int gfs2_truncatei_resume(struct gfs2_inode *ip);
extern int gfs2_file_dealloc(struct gfs2_inode *ip);
extern int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset,
unsigned int len);
+extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd);
+extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd);
#endif /* __BMAP_DOT_H__ */
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index fa32655449c8..1a349f9a9685 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -53,6 +53,8 @@
* but never before the maximum hash table size has been reached.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/buffer_head.h>
@@ -507,8 +509,8 @@ static int gfs2_check_dirent(struct gfs2_dirent *dent, unsigned int offset,
goto error;
return 0;
error:
- printk(KERN_WARNING "gfs2_check_dirent: %s (%s)\n", msg,
- first ? "first in block" : "not first in block");
+ pr_warn("%s: %s (%s)\n",
+ __func__, msg, first ? "first in block" : "not first in block");
return -EIO;
}
@@ -531,8 +533,7 @@ static int gfs2_dirent_offset(const void *buf)
}
return offset;
wrong_type:
- printk(KERN_WARNING "gfs2_scan_dirent: wrong block type %u\n",
- be32_to_cpu(h->mh_type));
+ pr_warn("%s: wrong block type %u\n", __func__, be32_to_cpu(h->mh_type));
return -1;
}
@@ -728,7 +729,7 @@ static int get_leaf(struct gfs2_inode *dip, u64 leaf_no,
error = gfs2_meta_read(dip->i_gl, leaf_no, DIO_WAIT, bhp);
if (!error && gfs2_metatype_check(GFS2_SB(&dip->i_inode), *bhp, GFS2_METATYPE_LF)) {
- /* printk(KERN_INFO "block num=%llu\n", leaf_no); */
+ /* pr_info("block num=%llu\n", leaf_no); */
error = -EIO;
}
@@ -1006,7 +1007,8 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name)
len = 1 << (dip->i_depth - be16_to_cpu(oleaf->lf_depth));
half_len = len >> 1;
if (!half_len) {
- printk(KERN_WARNING "i_depth %u lf_depth %u index %u\n", dip->i_depth, be16_to_cpu(oleaf->lf_depth), index);
+ pr_warn("i_depth %u lf_depth %u index %u\n",
+ dip->i_depth, be16_to_cpu(oleaf->lf_depth), index);
gfs2_consist_inode(dip);
error = -EIO;
goto fail_brelse;
@@ -1684,6 +1686,14 @@ static int dir_new_leaf(struct inode *inode, const struct qstr *name)
return 0;
}
+static u16 gfs2_inode_ra_len(const struct gfs2_inode *ip)
+{
+ u64 where = ip->i_no_addr + 1;
+ if (ip->i_eattr == where)
+ return 1;
+ return 0;
+}
+
/**
* gfs2_dir_add - Add new filename into directory
* @inode: The directory inode
@@ -1721,6 +1731,7 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name,
dent = gfs2_init_dirent(inode, dent, name, bh);
gfs2_inum_out(nip, dent);
dent->de_type = cpu_to_be16(IF2DT(nip->i_inode.i_mode));
+ dent->de_rahead = cpu_to_be16(gfs2_inode_ra_len(nip));
tv = CURRENT_TIME;
if (ip->i_diskflags & GFS2_DIF_EXHASH) {
leaf = (struct gfs2_leaf *)bh->b_data;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index efc078f0ee4e..6c794085abac 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -811,6 +811,8 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset,
loff_t bsize_mask = ~((loff_t)sdp->sd_sb.sb_bsize - 1);
loff_t next = (offset + len - 1) >> sdp->sd_sb.sb_bsize_shift;
loff_t max_chunk_size = UINT_MAX & bsize_mask;
+ struct gfs2_holder gh;
+
next = (next + 1) << sdp->sd_sb.sb_bsize_shift;
/* We only support the FALLOC_FL_KEEP_SIZE mode */
@@ -831,8 +833,10 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset,
if (error)
return error;
- gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ip->i_gh);
- error = gfs2_glock_nq(&ip->i_gh);
+ mutex_lock(&inode->i_mutex);
+
+ gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
+ error = gfs2_glock_nq(&gh);
if (unlikely(error))
goto out_uninit;
@@ -900,9 +904,10 @@ out_trans_fail:
out_qunlock:
gfs2_quota_unlock(ip);
out_unlock:
- gfs2_glock_dq(&ip->i_gh);
+ gfs2_glock_dq(&gh);
out_uninit:
- gfs2_holder_uninit(&ip->i_gh);
+ gfs2_holder_uninit(&gh);
+ mutex_unlock(&inode->i_mutex);
return error;
}
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index ca0be6c69a26..aec7f73832f0 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -7,6 +7,8 @@
* of the GNU General Public License version 2.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -468,7 +470,7 @@ retry:
do_xmote(gl, gh, LM_ST_UNLOCKED);
break;
default: /* Everything else */
- printk(KERN_ERR "GFS2: wanted %u got %u\n", gl->gl_target, state);
+ pr_err("wanted %u got %u\n", gl->gl_target, state);
GLOCK_BUG_ON(gl, 1);
}
spin_unlock(&gl->gl_spin);
@@ -542,7 +544,7 @@ __acquires(&gl->gl_spin)
/* lock_dlm */
ret = sdp->sd_lockstruct.ls_ops->lm_lock(gl, target, lck_flags);
if (ret) {
- printk(KERN_ERR "GFS2: lm_lock ret %d\n", ret);
+ pr_err("lm_lock ret %d\n", ret);
GLOCK_BUG_ON(gl, 1);
}
} else { /* lock_nolock */
@@ -935,7 +937,7 @@ void gfs2_print_dbg(struct seq_file *seq, const char *fmt, ...)
vaf.fmt = fmt;
vaf.va = &args;
- printk(KERN_ERR " %pV", &vaf);
+ pr_err("%pV", &vaf);
}
va_end(args);
@@ -1010,13 +1012,13 @@ do_cancel:
return;
trap_recursive:
- printk(KERN_ERR "original: %pSR\n", (void *)gh2->gh_ip);
- printk(KERN_ERR "pid: %d\n", pid_nr(gh2->gh_owner_pid));
- printk(KERN_ERR "lock type: %d req lock state : %d\n",
+ pr_err("original: %pSR\n", (void *)gh2->gh_ip);
+ pr_err("pid: %d\n", pid_nr(gh2->gh_owner_pid));
+ pr_err("lock type: %d req lock state : %d\n",
gh2->gh_gl->gl_name.ln_type, gh2->gh_state);
- printk(KERN_ERR "new: %pSR\n", (void *)gh->gh_ip);
- printk(KERN_ERR "pid: %d\n", pid_nr(gh->gh_owner_pid));
- printk(KERN_ERR "lock type: %d req lock state : %d\n",
+ pr_err("new: %pSR\n", (void *)gh->gh_ip);
+ pr_err("pid: %d\n", pid_nr(gh->gh_owner_pid));
+ pr_err("lock type: %d req lock state : %d\n",
gh->gh_gl->gl_name.ln_type, gh->gh_state);
gfs2_dump_glock(NULL, gl);
BUG();
@@ -1045,9 +1047,13 @@ int gfs2_glock_nq(struct gfs2_holder *gh)
spin_lock(&gl->gl_spin);
add_to_queue(gh);
- if ((LM_FLAG_NOEXP & gh->gh_flags) &&
- test_and_clear_bit(GLF_FROZEN, &gl->gl_flags))
+ if (unlikely((LM_FLAG_NOEXP & gh->gh_flags) &&
+ test_and_clear_bit(GLF_FROZEN, &gl->gl_flags))) {
set_bit(GLF_REPLY_PENDING, &gl->gl_flags);
+ gl->gl_lockref.count++;
+ if (queue_delayed_work(glock_workqueue, &gl->gl_work, 0) == 0)
+ gl->gl_lockref.count--;
+ }
run_queue(gl, 1);
spin_unlock(&gl->gl_spin);
diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c
index 3bf0631b5d56..54b66809e818 100644
--- a/fs/gfs2/glops.c
+++ b/fs/gfs2/glops.c
@@ -82,6 +82,8 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
struct gfs2_trans tr;
memset(&tr, 0, sizeof(tr));
+ INIT_LIST_HEAD(&tr.tr_buf);
+ INIT_LIST_HEAD(&tr.tr_databuf);
tr.tr_revokes = atomic_read(&gl->gl_ail_count);
if (!tr.tr_revokes)
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index cf0e34400f71..bdf70c18610c 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -52,7 +52,7 @@ struct gfs2_log_header_host {
*/
struct gfs2_log_operations {
- void (*lo_before_commit) (struct gfs2_sbd *sdp);
+ void (*lo_before_commit) (struct gfs2_sbd *sdp, struct gfs2_trans *tr);
void (*lo_after_commit) (struct gfs2_sbd *sdp, struct gfs2_trans *tr);
void (*lo_before_scan) (struct gfs2_jdesc *jd,
struct gfs2_log_header_host *head, int pass);
@@ -371,6 +371,7 @@ enum {
GIF_ALLOC_FAILED = 2,
GIF_SW_PAGED = 3,
GIF_ORDERED = 4,
+ GIF_FREE_VFS_INODE = 5,
};
struct gfs2_inode {
@@ -462,11 +463,11 @@ struct gfs2_trans {
unsigned int tr_blocks;
unsigned int tr_revokes;
unsigned int tr_reserved;
+ unsigned int tr_touched:1;
+ unsigned int tr_attached:1;
struct gfs2_holder tr_t_gh;
- int tr_touched;
- int tr_attached;
unsigned int tr_num_buf_new;
unsigned int tr_num_databuf_new;
@@ -476,6 +477,8 @@ struct gfs2_trans {
unsigned int tr_num_revoke_rm;
struct list_head tr_list;
+ struct list_head tr_databuf;
+ struct list_head tr_buf;
unsigned int tr_first;
struct list_head tr_ail1_list;
@@ -483,7 +486,7 @@ struct gfs2_trans {
};
struct gfs2_journal_extent {
- struct list_head extent_list;
+ struct list_head list;
unsigned int lblock; /* First logical block */
u64 dblock; /* First disk block */
@@ -493,6 +496,7 @@ struct gfs2_journal_extent {
struct gfs2_jdesc {
struct list_head jd_list;
struct list_head extent_list;
+ unsigned int nr_extents;
struct work_struct jd_work;
struct inode *jd_inode;
unsigned long jd_flags;
@@ -500,6 +504,15 @@ struct gfs2_jdesc {
unsigned int jd_jid;
unsigned int jd_blocks;
int jd_recover_error;
+ /* Replay stuff */
+
+ unsigned int jd_found_blocks;
+ unsigned int jd_found_revokes;
+ unsigned int jd_replayed_blocks;
+
+ struct list_head jd_revoke_list;
+ unsigned int jd_replay_tail;
+
};
struct gfs2_statfs_change_host {
@@ -746,19 +759,12 @@ struct gfs2_sbd {
struct gfs2_trans *sd_log_tr;
unsigned int sd_log_blks_reserved;
- unsigned int sd_log_commited_buf;
- unsigned int sd_log_commited_databuf;
int sd_log_commited_revoke;
atomic_t sd_log_pinned;
- unsigned int sd_log_num_buf;
unsigned int sd_log_num_revoke;
- unsigned int sd_log_num_rg;
- unsigned int sd_log_num_databuf;
- struct list_head sd_log_le_buf;
struct list_head sd_log_le_revoke;
- struct list_head sd_log_le_databuf;
struct list_head sd_log_le_ordered;
spinlock_t sd_ordered_lock;
@@ -786,15 +792,6 @@ struct gfs2_sbd {
struct list_head sd_ail1_list;
struct list_head sd_ail2_list;
- /* Replay stuff */
-
- struct list_head sd_revoke_list;
- unsigned int sd_replay_tail;
-
- unsigned int sd_found_blocks;
- unsigned int sd_found_revokes;
- unsigned int sd_replayed_blocks;
-
/* For quiescing the filesystem */
struct gfs2_holder sd_freeze_gh;
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 5c524180c98e..28cc7bf6575a 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -376,12 +376,11 @@ static void munge_mode_uid_gid(const struct gfs2_inode *dip,
inode->i_gid = current_fsgid();
}
-static int alloc_dinode(struct gfs2_inode *ip, u32 flags)
+static int alloc_dinode(struct gfs2_inode *ip, u32 flags, unsigned *dblocks)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
- struct gfs2_alloc_parms ap = { .target = RES_DINODE, .aflags = flags, };
+ struct gfs2_alloc_parms ap = { .target = *dblocks, .aflags = flags, };
int error;
- int dblocks = 1;
error = gfs2_quota_lock_check(ip);
if (error)
@@ -391,11 +390,11 @@ static int alloc_dinode(struct gfs2_inode *ip, u32 flags)
if (error)
goto out_quota;
- error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS + RES_QUOTA, 0);
+ error = gfs2_trans_begin(sdp, (*dblocks * RES_RG_BIT) + RES_STATFS + RES_QUOTA, 0);
if (error)
goto out_ipreserv;
- error = gfs2_alloc_blocks(ip, &ip->i_no_addr, &dblocks, 1, &ip->i_generation);
+ error = gfs2_alloc_blocks(ip, &ip->i_no_addr, dblocks, 1, &ip->i_generation);
ip->i_no_formal_ino = ip->i_generation;
ip->i_inode.i_ino = ip->i_no_addr;
ip->i_goal = ip->i_no_addr;
@@ -428,6 +427,33 @@ static void gfs2_init_dir(struct buffer_head *dibh,
}
/**
+ * gfs2_init_xattr - Initialise an xattr block for a new inode
+ * @ip: The inode in question
+ *
+ * This sets up an empty xattr block for a new inode, ready to
+ * take any ACLs, LSM xattrs, etc.
+ */
+
+static void gfs2_init_xattr(struct gfs2_inode *ip)
+{
+ struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
+ struct buffer_head *bh;
+ struct gfs2_ea_header *ea;
+
+ bh = gfs2_meta_new(ip->i_gl, ip->i_eattr);
+ gfs2_trans_add_meta(ip->i_gl, bh);
+ gfs2_metatype_set(bh, GFS2_METATYPE_EA, GFS2_FORMAT_EA);
+ gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header));
+
+ ea = GFS2_EA_BH2FIRST(bh);
+ ea->ea_rec_len = cpu_to_be32(sdp->sd_jbsize);
+ ea->ea_type = GFS2_EATYPE_UNUSED;
+ ea->ea_flags = GFS2_EAFLAG_LAST;
+
+ brelse(bh);
+}
+
+/**
* init_dinode - Fill in a new dinode structure
* @dip: The directory this inode is being created in
* @ip: The inode
@@ -545,13 +571,6 @@ static int gfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
return err;
}
-static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
- const struct qstr *qstr)
-{
- return security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr,
- &gfs2_initxattrs, NULL);
-}
-
/**
* gfs2_create_inode - Create a new inode
* @dir: The parent directory
@@ -578,8 +597,9 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
struct gfs2_glock *io_gl;
struct dentry *d;
- int error;
+ int error, free_vfs_inode = 0;
u32 aflags = 0;
+ unsigned blocks = 1;
struct gfs2_diradd da = { .bh = NULL, };
if (!name->len || name->len > GFS2_FNAMESIZE)
@@ -676,10 +696,15 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
(dip->i_diskflags & GFS2_DIF_TOPDIR))
aflags |= GFS2_AF_ORLOV;
- error = alloc_dinode(ip, aflags);
+ if (default_acl || acl)
+ blocks++;
+
+ error = alloc_dinode(ip, aflags, &blocks);
if (error)
goto fail_free_inode;
+ gfs2_set_inode_blocks(inode, blocks);
+
error = gfs2_glock_get(sdp, ip->i_no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl);
if (error)
goto fail_free_inode;
@@ -689,10 +714,14 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
if (error)
goto fail_free_inode;
- error = gfs2_trans_begin(sdp, RES_DINODE, 0);
+ error = gfs2_trans_begin(sdp, blocks, 0);
if (error)
goto fail_gunlock2;
+ if (blocks > 1) {
+ ip->i_eattr = ip->i_no_addr + 1;
+ gfs2_init_xattr(ip);
+ }
init_dinode(dip, ip, symname);
gfs2_trans_end(sdp);
@@ -722,7 +751,8 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
if (error)
goto fail_gunlock3;
- error = gfs2_security_init(dip, ip, name);
+ error = security_inode_init_security(&ip->i_inode, &dip->i_inode, name,
+ &gfs2_initxattrs, NULL);
if (error)
goto fail_gunlock3;
@@ -758,15 +788,16 @@ fail_free_acls:
if (acl)
posix_acl_release(acl);
fail_free_vfs_inode:
- free_inode_nonrcu(inode);
- inode = NULL;
+ free_vfs_inode = 1;
fail_gunlock:
gfs2_dir_no_add(&da);
gfs2_glock_dq_uninit(ghs);
if (inode && !IS_ERR(inode)) {
clear_nlink(inode);
- mark_inode_dirty(inode);
- set_bit(GIF_ALLOC_FAILED, &GFS2_I(inode)->i_flags);
+ if (!free_vfs_inode)
+ mark_inode_dirty(inode);
+ set_bit(free_vfs_inode ? GIF_FREE_VFS_INODE : GIF_ALLOC_FAILED,
+ &GFS2_I(inode)->i_flags);
iput(inode);
}
fail:
@@ -1263,6 +1294,10 @@ static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to)
}
tmp = gfs2_lookupi(dir, &gfs2_qdotdot, 1);
+ if (!tmp) {
+ error = -ENOENT;
+ break;
+ }
if (IS_ERR(tmp)) {
error = PTR_ERR(tmp);
break;
diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c
index 2a6ba06bee6f..c1eb555dc588 100644
--- a/fs/gfs2/lock_dlm.c
+++ b/fs/gfs2/lock_dlm.c
@@ -7,6 +7,8 @@
* of the GNU General Public License version 2.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/fs.h>
#include <linux/dlm.h>
#include <linux/slab.h>
@@ -176,7 +178,7 @@ static void gdlm_bast(void *arg, int mode)
gfs2_glock_cb(gl, LM_ST_SHARED);
break;
default:
- printk(KERN_ERR "unknown bast mode %d", mode);
+ pr_err("unknown bast mode %d\n", mode);
BUG();
}
}
@@ -195,7 +197,7 @@ static int make_mode(const unsigned int lmstate)
case LM_ST_SHARED:
return DLM_LOCK_PR;
}
- printk(KERN_ERR "unknown LM state %d", lmstate);
+ pr_err("unknown LM state %d\n", lmstate);
BUG();
return -1;
}
@@ -308,7 +310,7 @@ static void gdlm_put_lock(struct gfs2_glock *gl)
error = dlm_unlock(ls->ls_dlm, gl->gl_lksb.sb_lkid, DLM_LKF_VALBLK,
NULL, gl);
if (error) {
- printk(KERN_ERR "gdlm_unlock %x,%llx err=%d\n",
+ pr_err("gdlm_unlock %x,%llx err=%d\n",
gl->gl_name.ln_type,
(unsigned long long)gl->gl_name.ln_number, error);
return;
@@ -1102,7 +1104,7 @@ static void gdlm_recover_slot(void *arg, struct dlm_slot *slot)
}
if (ls->ls_recover_submit[jid]) {
- fs_info(sdp, "recover_slot jid %d gen %u prev %u",
+ fs_info(sdp, "recover_slot jid %d gen %u prev %u\n",
jid, ls->ls_recover_block, ls->ls_recover_submit[jid]);
}
ls->ls_recover_submit[jid] = ls->ls_recover_block;
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index 9dcb9777a5f8..4a14d504ef83 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -18,6 +18,7 @@
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/bio.h>
+#include <linux/blkdev.h>
#include <linux/writeback.h>
#include <linux/list_sort.h>
@@ -145,8 +146,10 @@ void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc)
{
struct list_head *head = &sdp->sd_ail1_list;
struct gfs2_trans *tr;
+ struct blk_plug plug;
trace_gfs2_ail_flush(sdp, wbc, 1);
+ blk_start_plug(&plug);
spin_lock(&sdp->sd_ail_lock);
restart:
list_for_each_entry_reverse(tr, head, tr_list) {
@@ -156,6 +159,7 @@ restart:
goto restart;
}
spin_unlock(&sdp->sd_ail_lock);
+ blk_finish_plug(&plug);
trace_gfs2_ail_flush(sdp, wbc, 0);
}
@@ -410,24 +414,22 @@ static inline unsigned int log_distance(struct gfs2_sbd *sdp, unsigned int newer
static unsigned int calc_reserved(struct gfs2_sbd *sdp)
{
unsigned int reserved = 0;
- unsigned int mbuf_limit, metabufhdrs_needed;
- unsigned int dbuf_limit, databufhdrs_needed;
- unsigned int revokes = 0;
+ unsigned int mbuf;
+ unsigned int dbuf;
+ struct gfs2_trans *tr = sdp->sd_log_tr;
- mbuf_limit = buf_limit(sdp);
- metabufhdrs_needed = (sdp->sd_log_commited_buf +
- (mbuf_limit - 1)) / mbuf_limit;
- dbuf_limit = databuf_limit(sdp);
- databufhdrs_needed = (sdp->sd_log_commited_databuf +
- (dbuf_limit - 1)) / dbuf_limit;
+ if (tr) {
+ mbuf = tr->tr_num_buf_new - tr->tr_num_buf_rm;
+ dbuf = tr->tr_num_databuf_new - tr->tr_num_databuf_rm;
+ reserved = mbuf + dbuf;
+ /* Account for header blocks */
+ reserved += DIV_ROUND_UP(mbuf, buf_limit(sdp));
+ reserved += DIV_ROUND_UP(dbuf, databuf_limit(sdp));
+ }
if (sdp->sd_log_commited_revoke > 0)
- revokes = gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke,
+ reserved += gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke,
sizeof(u64));
-
- reserved = sdp->sd_log_commited_buf + metabufhdrs_needed +
- sdp->sd_log_commited_databuf + databufhdrs_needed +
- revokes;
/* One for the overall header */
if (reserved)
reserved++;
@@ -682,36 +684,25 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
}
trace_gfs2_log_flush(sdp, 1);
+ sdp->sd_log_flush_head = sdp->sd_log_head;
+ sdp->sd_log_flush_wrapped = 0;
tr = sdp->sd_log_tr;
if (tr) {
sdp->sd_log_tr = NULL;
INIT_LIST_HEAD(&tr->tr_ail1_list);
INIT_LIST_HEAD(&tr->tr_ail2_list);
+ tr->tr_first = sdp->sd_log_flush_head;
}
- if (sdp->sd_log_num_buf != sdp->sd_log_commited_buf) {
- printk(KERN_INFO "GFS2: log buf %u %u\n", sdp->sd_log_num_buf,
- sdp->sd_log_commited_buf);
- gfs2_assert_withdraw(sdp, 0);
- }
- if (sdp->sd_log_num_databuf != sdp->sd_log_commited_databuf) {
- printk(KERN_INFO "GFS2: log databuf %u %u\n",
- sdp->sd_log_num_databuf, sdp->sd_log_commited_databuf);
- gfs2_assert_withdraw(sdp, 0);
- }
gfs2_assert_withdraw(sdp,
sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke);
- sdp->sd_log_flush_head = sdp->sd_log_head;
- sdp->sd_log_flush_wrapped = 0;
- if (tr)
- tr->tr_first = sdp->sd_log_flush_head;
-
gfs2_ordered_write(sdp);
- lops_before_commit(sdp);
+ lops_before_commit(sdp, tr);
gfs2_log_flush_bio(sdp, WRITE);
if (sdp->sd_log_head != sdp->sd_log_flush_head) {
+ log_flush_wait(sdp);
log_write_header(sdp, 0);
} else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){
atomic_dec(&sdp->sd_log_blks_free); /* Adjust for unreserved buffer */
@@ -723,8 +714,6 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
gfs2_log_lock(sdp);
sdp->sd_log_head = sdp->sd_log_flush_head;
sdp->sd_log_blks_reserved = 0;
- sdp->sd_log_commited_buf = 0;
- sdp->sd_log_commited_databuf = 0;
sdp->sd_log_commited_revoke = 0;
spin_lock(&sdp->sd_ail_lock);
@@ -740,34 +729,54 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
kfree(tr);
}
+/**
+ * gfs2_merge_trans - Merge a new transaction into a cached transaction
+ * @old: Original transaction to be expanded
+ * @new: New transaction to be merged
+ */
+
+static void gfs2_merge_trans(struct gfs2_trans *old, struct gfs2_trans *new)
+{
+ WARN_ON_ONCE(old->tr_attached != 1);
+
+ old->tr_num_buf_new += new->tr_num_buf_new;
+ old->tr_num_databuf_new += new->tr_num_databuf_new;
+ old->tr_num_buf_rm += new->tr_num_buf_rm;
+ old->tr_num_databuf_rm += new->tr_num_databuf_rm;
+ old->tr_num_revoke += new->tr_num_revoke;
+ old->tr_num_revoke_rm += new->tr_num_revoke_rm;
+
+ list_splice_tail_init(&new->tr_databuf, &old->tr_databuf);
+ list_splice_tail_init(&new->tr_buf, &old->tr_buf);
+}
+
static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
unsigned int reserved;
unsigned int unused;
+ unsigned int maxres;
gfs2_log_lock(sdp);
- sdp->sd_log_commited_buf += tr->tr_num_buf_new - tr->tr_num_buf_rm;
- sdp->sd_log_commited_databuf += tr->tr_num_databuf_new -
- tr->tr_num_databuf_rm;
- gfs2_assert_withdraw(sdp, (((int)sdp->sd_log_commited_buf) >= 0) ||
- (((int)sdp->sd_log_commited_databuf) >= 0));
+ if (sdp->sd_log_tr) {
+ gfs2_merge_trans(sdp->sd_log_tr, tr);
+ } else if (tr->tr_num_buf_new || tr->tr_num_databuf_new) {
+ gfs2_assert_withdraw(sdp, tr->tr_t_gh.gh_gl);
+ sdp->sd_log_tr = tr;
+ tr->tr_attached = 1;
+ }
+
sdp->sd_log_commited_revoke += tr->tr_num_revoke - tr->tr_num_revoke_rm;
reserved = calc_reserved(sdp);
- gfs2_assert_withdraw(sdp, sdp->sd_log_blks_reserved + tr->tr_reserved >= reserved);
- unused = sdp->sd_log_blks_reserved - reserved + tr->tr_reserved;
+ maxres = sdp->sd_log_blks_reserved + tr->tr_reserved;
+ gfs2_assert_withdraw(sdp, maxres >= reserved);
+ unused = maxres - reserved;
atomic_add(unused, &sdp->sd_log_blks_free);
trace_gfs2_log_blocks(sdp, unused);
gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <=
sdp->sd_jdesc->jd_blocks);
sdp->sd_log_blks_reserved = reserved;
- if (sdp->sd_log_tr == NULL &&
- (tr->tr_num_buf_new || tr->tr_num_databuf_new)) {
- gfs2_assert_withdraw(sdp, tr->tr_t_gh.gh_gl);
- sdp->sd_log_tr = tr;
- tr->tr_attached = 1;
- }
gfs2_log_unlock(sdp);
}
@@ -807,10 +816,7 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp)
down_write(&sdp->sd_log_flush_lock);
gfs2_assert_withdraw(sdp, !sdp->sd_log_blks_reserved);
- gfs2_assert_withdraw(sdp, !sdp->sd_log_num_buf);
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke);
- gfs2_assert_withdraw(sdp, !sdp->sd_log_num_rg);
- gfs2_assert_withdraw(sdp, !sdp->sd_log_num_databuf);
gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail1_list));
sdp->sd_log_flush_head = sdp->sd_log_head;
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 76693793cedd..a294d8d8bcd4 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -146,8 +146,8 @@ static u64 gfs2_log_bmap(struct gfs2_sbd *sdp)
struct gfs2_journal_extent *je;
u64 block;
- list_for_each_entry(je, &sdp->sd_jdesc->extent_list, extent_list) {
- if (lbn >= je->lblock && lbn < je->lblock + je->blocks) {
+ list_for_each_entry(je, &sdp->sd_jdesc->extent_list, list) {
+ if ((lbn >= je->lblock) && (lbn < (je->lblock + je->blocks))) {
block = je->dblock + lbn - je->lblock;
gfs2_log_incr_head(sdp);
return block;
@@ -491,44 +491,40 @@ static void gfs2_before_commit(struct gfs2_sbd *sdp, unsigned int limit,
gfs2_log_unlock(sdp);
}
-static void buf_lo_before_commit(struct gfs2_sbd *sdp)
+static void buf_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
unsigned int limit = buf_limit(sdp); /* 503 for 4k blocks */
-
- gfs2_before_commit(sdp, limit, sdp->sd_log_num_buf,
- &sdp->sd_log_le_buf, 0);
+ unsigned int nbuf;
+ if (tr == NULL)
+ return;
+ nbuf = tr->tr_num_buf_new - tr->tr_num_buf_rm;
+ gfs2_before_commit(sdp, limit, nbuf, &tr->tr_buf, 0);
}
static void buf_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
- struct list_head *head = &sdp->sd_log_le_buf;
+ struct list_head *head;
struct gfs2_bufdata *bd;
- if (tr == NULL) {
- gfs2_assert(sdp, list_empty(head));
+ if (tr == NULL)
return;
- }
+ head = &tr->tr_buf;
while (!list_empty(head)) {
bd = list_entry(head->next, struct gfs2_bufdata, bd_list);
list_del_init(&bd->bd_list);
- sdp->sd_log_num_buf--;
-
gfs2_unpin(sdp, bd->bd_bh, tr);
}
- gfs2_assert_warn(sdp, !sdp->sd_log_num_buf);
}
static void buf_lo_before_scan(struct gfs2_jdesc *jd,
struct gfs2_log_header_host *head, int pass)
{
- struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
-
if (pass != 0)
return;
- sdp->sd_found_blocks = 0;
- sdp->sd_replayed_blocks = 0;
+ jd->jd_found_blocks = 0;
+ jd->jd_replayed_blocks = 0;
}
static int buf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
@@ -551,9 +547,9 @@ static int buf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
for (; blks; gfs2_replay_incr_blk(sdp, &start), blks--) {
blkno = be64_to_cpu(*ptr++);
- sdp->sd_found_blocks++;
+ jd->jd_found_blocks++;
- if (gfs2_revoke_check(sdp, blkno, start))
+ if (gfs2_revoke_check(jd, blkno, start))
continue;
error = gfs2_replay_read_block(jd, start, &bh_log);
@@ -574,7 +570,7 @@ static int buf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
if (error)
break;
- sdp->sd_replayed_blocks++;
+ jd->jd_replayed_blocks++;
}
return error;
@@ -617,10 +613,10 @@ static void buf_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass)
gfs2_meta_sync(ip->i_gl);
fs_info(sdp, "jid=%u: Replayed %u of %u blocks\n",
- jd->jd_jid, sdp->sd_replayed_blocks, sdp->sd_found_blocks);
+ jd->jd_jid, jd->jd_replayed_blocks, jd->jd_found_blocks);
}
-static void revoke_lo_before_commit(struct gfs2_sbd *sdp)
+static void revoke_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
struct gfs2_meta_header *mh;
unsigned int offset;
@@ -679,13 +675,11 @@ static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
static void revoke_lo_before_scan(struct gfs2_jdesc *jd,
struct gfs2_log_header_host *head, int pass)
{
- struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
-
if (pass != 0)
return;
- sdp->sd_found_revokes = 0;
- sdp->sd_replay_tail = head->lh_tail;
+ jd->jd_found_revokes = 0;
+ jd->jd_replay_tail = head->lh_tail;
}
static int revoke_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
@@ -717,13 +711,13 @@ static int revoke_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
while (offset + sizeof(u64) <= sdp->sd_sb.sb_bsize) {
blkno = be64_to_cpu(*(__be64 *)(bh->b_data + offset));
- error = gfs2_revoke_add(sdp, blkno, start);
+ error = gfs2_revoke_add(jd, blkno, start);
if (error < 0) {
brelse(bh);
return error;
}
else if (error)
- sdp->sd_found_revokes++;
+ jd->jd_found_revokes++;
if (!--revokes)
break;
@@ -743,16 +737,16 @@ static void revoke_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass)
struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
if (error) {
- gfs2_revoke_clean(sdp);
+ gfs2_revoke_clean(jd);
return;
}
if (pass != 1)
return;
fs_info(sdp, "jid=%u: Found %u revoke tags\n",
- jd->jd_jid, sdp->sd_found_revokes);
+ jd->jd_jid, jd->jd_found_revokes);
- gfs2_revoke_clean(sdp);
+ gfs2_revoke_clean(jd);
}
/**
@@ -760,12 +754,14 @@ static void revoke_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass)
*
*/
-static void databuf_lo_before_commit(struct gfs2_sbd *sdp)
+static void databuf_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
- unsigned int limit = buf_limit(sdp) / 2;
-
- gfs2_before_commit(sdp, limit, sdp->sd_log_num_databuf,
- &sdp->sd_log_le_databuf, 1);
+ unsigned int limit = databuf_limit(sdp);
+ unsigned int nbuf;
+ if (tr == NULL)
+ return;
+ nbuf = tr->tr_num_databuf_new - tr->tr_num_databuf_rm;
+ gfs2_before_commit(sdp, limit, nbuf, &tr->tr_databuf, 1);
}
static int databuf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
@@ -789,9 +785,9 @@ static int databuf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
blkno = be64_to_cpu(*ptr++);
esc = be64_to_cpu(*ptr++);
- sdp->sd_found_blocks++;
+ jd->jd_found_blocks++;
- if (gfs2_revoke_check(sdp, blkno, start))
+ if (gfs2_revoke_check(jd, blkno, start))
continue;
error = gfs2_replay_read_block(jd, start, &bh_log);
@@ -811,7 +807,7 @@ static int databuf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
brelse(bh_log);
brelse(bh_ip);
- sdp->sd_replayed_blocks++;
+ jd->jd_replayed_blocks++;
}
return error;
@@ -835,26 +831,23 @@ static void databuf_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass)
gfs2_meta_sync(ip->i_gl);
fs_info(sdp, "jid=%u: Replayed %u of %u data blocks\n",
- jd->jd_jid, sdp->sd_replayed_blocks, sdp->sd_found_blocks);
+ jd->jd_jid, jd->jd_replayed_blocks, jd->jd_found_blocks);
}
static void databuf_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
- struct list_head *head = &sdp->sd_log_le_databuf;
+ struct list_head *head;
struct gfs2_bufdata *bd;
- if (tr == NULL) {
- gfs2_assert(sdp, list_empty(head));
+ if (tr == NULL)
return;
- }
+ head = &tr->tr_databuf;
while (!list_empty(head)) {
bd = list_entry(head->next, struct gfs2_bufdata, bd_list);
list_del_init(&bd->bd_list);
- sdp->sd_log_num_databuf--;
gfs2_unpin(sdp, bd->bd_bh, tr);
}
- gfs2_assert_warn(sdp, !sdp->sd_log_num_databuf);
}
diff --git a/fs/gfs2/lops.h b/fs/gfs2/lops.h
index 9ca2e6438419..a65a7ba32ffd 100644
--- a/fs/gfs2/lops.h
+++ b/fs/gfs2/lops.h
@@ -46,12 +46,13 @@ static inline unsigned int databuf_limit(struct gfs2_sbd *sdp)
return limit;
}
-static inline void lops_before_commit(struct gfs2_sbd *sdp)
+static inline void lops_before_commit(struct gfs2_sbd *sdp,
+ struct gfs2_trans *tr)
{
int x;
for (x = 0; gfs2_log_ops[x]; x++)
if (gfs2_log_ops[x]->lo_before_commit)
- gfs2_log_ops[x]->lo_before_commit(sdp);
+ gfs2_log_ops[x]->lo_before_commit(sdp, tr);
}
static inline void lops_after_commit(struct gfs2_sbd *sdp,
diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c
index c272e73063de..82b6ac829656 100644
--- a/fs/gfs2/main.c
+++ b/fs/gfs2/main.c
@@ -7,6 +7,8 @@
* of the GNU General Public License version 2.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
@@ -165,7 +167,7 @@ static int __init init_gfs2_fs(void)
gfs2_register_debugfs();
- printk("GFS2 installed\n");
+ pr_info("GFS2 installed\n");
return 0;
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index c7f24690ed05..2cf09b63a6b4 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -97,6 +97,11 @@ const struct address_space_operations gfs2_meta_aops = {
.releasepage = gfs2_releasepage,
};
+const struct address_space_operations gfs2_rgrp_aops = {
+ .writepage = gfs2_aspace_writepage,
+ .releasepage = gfs2_releasepage,
+};
+
/**
* gfs2_getbuf - Get a buffer with a given address space
* @gl: the glock
@@ -267,15 +272,10 @@ void gfs2_remove_from_journal(struct buffer_head *bh, struct gfs2_trans *tr, int
trace_gfs2_pin(bd, 0);
atomic_dec(&sdp->sd_log_pinned);
list_del_init(&bd->bd_list);
- if (meta) {
- gfs2_assert_warn(sdp, sdp->sd_log_num_buf);
- sdp->sd_log_num_buf--;
+ if (meta)
tr->tr_num_buf_rm++;
- } else {
- gfs2_assert_warn(sdp, sdp->sd_log_num_databuf);
- sdp->sd_log_num_databuf--;
+ else
tr->tr_num_databuf_rm++;
- }
tr->tr_touched = 1;
was_pinned = 1;
brelse(bh);
diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h
index 4823b934208a..ac5d8027d335 100644
--- a/fs/gfs2/meta_io.h
+++ b/fs/gfs2/meta_io.h
@@ -38,12 +38,15 @@ static inline void gfs2_buffer_copy_tail(struct buffer_head *to_bh,
}
extern const struct address_space_operations gfs2_meta_aops;
+extern const struct address_space_operations gfs2_rgrp_aops;
static inline struct gfs2_sbd *gfs2_mapping2sbd(struct address_space *mapping)
{
struct inode *inode = mapping->host;
if (mapping->a_ops == &gfs2_meta_aops)
return (((struct gfs2_glock *)mapping) - 1)->gl_sbd;
+ else if (mapping->a_ops == &gfs2_rgrp_aops)
+ return container_of(mapping, struct gfs2_sbd, sd_aspace);
else
return inode->i_sb->s_fs_info;
}
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index c6872d09561a..22f954051bb8 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -7,6 +7,8 @@
* of the GNU General Public License version 2.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -104,7 +106,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
mapping = &sdp->sd_aspace;
address_space_init_once(mapping);
- mapping->a_ops = &gfs2_meta_aops;
+ mapping->a_ops = &gfs2_rgrp_aops;
mapping->host = sb->s_bdev->bd_inode;
mapping->flags = 0;
mapping_set_gfp_mask(mapping, GFP_NOFS);
@@ -114,9 +116,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
spin_lock_init(&sdp->sd_log_lock);
atomic_set(&sdp->sd_log_pinned, 0);
- INIT_LIST_HEAD(&sdp->sd_log_le_buf);
INIT_LIST_HEAD(&sdp->sd_log_le_revoke);
- INIT_LIST_HEAD(&sdp->sd_log_le_databuf);
INIT_LIST_HEAD(&sdp->sd_log_le_ordered);
spin_lock_init(&sdp->sd_ordered_lock);
@@ -130,8 +130,6 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
atomic_set(&sdp->sd_log_in_flight, 0);
init_waitqueue_head(&sdp->sd_log_flush_wait);
- INIT_LIST_HEAD(&sdp->sd_revoke_list);
-
return sdp;
}
@@ -154,7 +152,7 @@ static int gfs2_check_sb(struct gfs2_sbd *sdp, int silent)
if (sb->sb_magic != GFS2_MAGIC ||
sb->sb_type != GFS2_METATYPE_SB) {
if (!silent)
- printk(KERN_WARNING "GFS2: not a GFS2 filesystem\n");
+ pr_warn("not a GFS2 filesystem\n");
return -EINVAL;
}
@@ -176,7 +174,7 @@ static void end_bio_io_page(struct bio *bio, int error)
if (!error)
SetPageUptodate(page);
else
- printk(KERN_WARNING "gfs2: error %d reading superblock\n", error);
+ pr_warn("error %d reading superblock\n", error);
unlock_page(page);
}
@@ -519,67 +517,6 @@ out:
return ret;
}
-/**
- * map_journal_extents - create a reusable "extent" mapping from all logical
- * blocks to all physical blocks for the given journal. This will save
- * us time when writing journal blocks. Most journals will have only one
- * extent that maps all their logical blocks. That's because gfs2.mkfs
- * arranges the journal blocks sequentially to maximize performance.
- * So the extent would map the first block for the entire file length.
- * However, gfs2_jadd can happen while file activity is happening, so
- * those journals may not be sequential. Less likely is the case where
- * the users created their own journals by mounting the metafs and
- * laying it out. But it's still possible. These journals might have
- * several extents.
- *
- * TODO: This should be done in bigger chunks rather than one block at a time,
- * but since it's only done at mount time, I'm not worried about the
- * time it takes.
- */
-static int map_journal_extents(struct gfs2_sbd *sdp)
-{
- struct gfs2_jdesc *jd = sdp->sd_jdesc;
- unsigned int lb;
- u64 db, prev_db; /* logical block, disk block, prev disk block */
- struct gfs2_inode *ip = GFS2_I(jd->jd_inode);
- struct gfs2_journal_extent *jext = NULL;
- struct buffer_head bh;
- int rc = 0;
-
- prev_db = 0;
-
- for (lb = 0; lb < i_size_read(jd->jd_inode) >> sdp->sd_sb.sb_bsize_shift; lb++) {
- bh.b_state = 0;
- bh.b_blocknr = 0;
- bh.b_size = 1 << ip->i_inode.i_blkbits;
- rc = gfs2_block_map(jd->jd_inode, lb, &bh, 0);
- db = bh.b_blocknr;
- if (rc || !db) {
- printk(KERN_INFO "GFS2 journal mapping error %d: lb="
- "%u db=%llu\n", rc, lb, (unsigned long long)db);
- break;
- }
- if (!prev_db || db != prev_db + 1) {
- jext = kzalloc(sizeof(struct gfs2_journal_extent),
- GFP_KERNEL);
- if (!jext) {
- printk(KERN_INFO "GFS2 error: out of memory "
- "mapping journal extents.\n");
- rc = -ENOMEM;
- break;
- }
- jext->dblock = db;
- jext->lblock = lb;
- jext->blocks = 1;
- list_add_tail(&jext->extent_list, &jd->extent_list);
- } else {
- jext->blocks++;
- }
- prev_db = db;
- }
- return rc;
-}
-
static void gfs2_others_may_mount(struct gfs2_sbd *sdp)
{
char *message = "FIRSTMOUNT=Done";
@@ -638,6 +575,8 @@ static int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh)
break;
INIT_LIST_HEAD(&jd->extent_list);
+ INIT_LIST_HEAD(&jd->jd_revoke_list);
+
INIT_WORK(&jd->jd_work, gfs2_recover_func);
jd->jd_inode = gfs2_lookupi(sdp->sd_jindex, &name, 1);
if (!jd->jd_inode || IS_ERR(jd->jd_inode)) {
@@ -781,7 +720,7 @@ static int init_journal(struct gfs2_sbd *sdp, int undo)
atomic_set(&sdp->sd_log_thresh2, 4*sdp->sd_jdesc->jd_blocks/5);
/* Map the extents for this journal's blocks */
- map_journal_extents(sdp);
+ gfs2_map_journal_extents(sdp, sdp->sd_jdesc);
}
trace_gfs2_log_blocks(sdp, atomic_read(&sdp->sd_log_blks_free));
@@ -1008,7 +947,7 @@ static int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent)
lm = &gfs2_dlm_ops;
#endif
} else {
- printk(KERN_INFO "GFS2: can't find protocol %s\n", proto);
+ pr_info("can't find protocol %s\n", proto);
return -ENOENT;
}
@@ -1115,7 +1054,7 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent
sdp = init_sbd(sb);
if (!sdp) {
- printk(KERN_WARNING "GFS2: can't alloc struct gfs2_sbd\n");
+ pr_warn("can't alloc struct gfs2_sbd\n");
return -ENOMEM;
}
sdp->sd_args = *args;
@@ -1363,7 +1302,7 @@ static struct dentry *gfs2_mount(struct file_system_type *fs_type, int flags,
error = gfs2_mount_args(&args, data);
if (error) {
- printk(KERN_WARNING "GFS2: can't parse mount arguments\n");
+ pr_warn("can't parse mount arguments\n");
goto error_super;
}
@@ -1413,15 +1352,15 @@ static struct dentry *gfs2_mount_meta(struct file_system_type *fs_type,
error = kern_path(dev_name, LOOKUP_FOLLOW, &path);
if (error) {
- printk(KERN_WARNING "GFS2: path_lookup on %s returned error %d\n",
- dev_name, error);
+ pr_warn("path_lookup on %s returned error %d\n",
+ dev_name, error);
return ERR_PTR(error);
}
s = sget(&gfs2_fs_type, test_gfs2_super, set_meta_super, flags,
path.dentry->d_inode->i_sb->s_bdev);
path_put(&path);
if (IS_ERR(s)) {
- printk(KERN_WARNING "GFS2: gfs2 mount does not exist\n");
+ pr_warn("gfs2 mount does not exist\n");
return ERR_CAST(s);
}
if ((flags ^ s->s_flags) & MS_RDONLY) {
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index 8bec0e3192dd..c4effff7cf55 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -36,6 +36,8 @@
* the quota file, so it is not being constantly read.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mm.h>
@@ -330,6 +332,7 @@ static int slot_get(struct gfs2_quota_data *qd)
if (bit < sdp->sd_quota_slots) {
set_bit(bit, sdp->sd_quota_bitmap);
qd->qd_slot = bit;
+ error = 0;
out:
qd->qd_slot_count++;
}
@@ -1081,10 +1084,10 @@ static int print_message(struct gfs2_quota_data *qd, char *type)
{
struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd;
- printk(KERN_INFO "GFS2: fsid=%s: quota %s for %s %u\n",
- sdp->sd_fsname, type,
- (qd->qd_id.type == USRQUOTA) ? "user" : "group",
- from_kqid(&init_user_ns, qd->qd_id));
+ fs_info(sdp, "quota %s for %s %u\n",
+ type,
+ (qd->qd_id.type == USRQUOTA) ? "user" : "group",
+ from_kqid(&init_user_ns, qd->qd_id));
return 0;
}
@@ -1242,14 +1245,13 @@ int gfs2_quota_init(struct gfs2_sbd *sdp)
bm_size = DIV_ROUND_UP(sdp->sd_quota_slots, 8 * sizeof(unsigned long));
bm_size *= sizeof(unsigned long);
error = -ENOMEM;
- sdp->sd_quota_bitmap = kmalloc(bm_size, GFP_NOFS|__GFP_NOWARN);
+ sdp->sd_quota_bitmap = kzalloc(bm_size, GFP_NOFS | __GFP_NOWARN);
if (sdp->sd_quota_bitmap == NULL)
- sdp->sd_quota_bitmap = __vmalloc(bm_size, GFP_NOFS, PAGE_KERNEL);
+ sdp->sd_quota_bitmap = __vmalloc(bm_size, GFP_NOFS |
+ __GFP_ZERO, PAGE_KERNEL);
if (!sdp->sd_quota_bitmap)
return error;
- memset(sdp->sd_quota_bitmap, 0, bm_size);
-
for (x = 0; x < blocks; x++) {
struct buffer_head *bh;
const struct gfs2_quota_change *qc;
diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c
index 963b2d75200c..7ad4094d68c0 100644
--- a/fs/gfs2/recovery.c
+++ b/fs/gfs2/recovery.c
@@ -52,9 +52,9 @@ int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk,
return error;
}
-int gfs2_revoke_add(struct gfs2_sbd *sdp, u64 blkno, unsigned int where)
+int gfs2_revoke_add(struct gfs2_jdesc *jd, u64 blkno, unsigned int where)
{
- struct list_head *head = &sdp->sd_revoke_list;
+ struct list_head *head = &jd->jd_revoke_list;
struct gfs2_revoke_replay *rr;
int found = 0;
@@ -81,13 +81,13 @@ int gfs2_revoke_add(struct gfs2_sbd *sdp, u64 blkno, unsigned int where)
return 1;
}
-int gfs2_revoke_check(struct gfs2_sbd *sdp, u64 blkno, unsigned int where)
+int gfs2_revoke_check(struct gfs2_jdesc *jd, u64 blkno, unsigned int where)
{
struct gfs2_revoke_replay *rr;
int wrap, a, b, revoke;
int found = 0;
- list_for_each_entry(rr, &sdp->sd_revoke_list, rr_list) {
+ list_for_each_entry(rr, &jd->jd_revoke_list, rr_list) {
if (rr->rr_blkno == blkno) {
found = 1;
break;
@@ -97,17 +97,17 @@ int gfs2_revoke_check(struct gfs2_sbd *sdp, u64 blkno, unsigned int where)
if (!found)
return 0;
- wrap = (rr->rr_where < sdp->sd_replay_tail);
- a = (sdp->sd_replay_tail < where);
+ wrap = (rr->rr_where < jd->jd_replay_tail);
+ a = (jd->jd_replay_tail < where);
b = (where < rr->rr_where);
revoke = (wrap) ? (a || b) : (a && b);
return revoke;
}
-void gfs2_revoke_clean(struct gfs2_sbd *sdp)
+void gfs2_revoke_clean(struct gfs2_jdesc *jd)
{
- struct list_head *head = &sdp->sd_revoke_list;
+ struct list_head *head = &jd->jd_revoke_list;
struct gfs2_revoke_replay *rr;
while (!list_empty(head)) {
diff --git a/fs/gfs2/recovery.h b/fs/gfs2/recovery.h
index 2226136c7647..6142836cce96 100644
--- a/fs/gfs2/recovery.h
+++ b/fs/gfs2/recovery.h
@@ -23,9 +23,9 @@ static inline void gfs2_replay_incr_blk(struct gfs2_sbd *sdp, unsigned int *blk)
extern int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk,
struct buffer_head **bh);
-extern int gfs2_revoke_add(struct gfs2_sbd *sdp, u64 blkno, unsigned int where);
-extern int gfs2_revoke_check(struct gfs2_sbd *sdp, u64 blkno, unsigned int where);
-extern void gfs2_revoke_clean(struct gfs2_sbd *sdp);
+extern int gfs2_revoke_add(struct gfs2_jdesc *jd, u64 blkno, unsigned int where);
+extern int gfs2_revoke_check(struct gfs2_jdesc *jd, u64 blkno, unsigned int where);
+extern void gfs2_revoke_clean(struct gfs2_jdesc *jd);
extern int gfs2_find_jhead(struct gfs2_jdesc *jd,
struct gfs2_log_header_host *head);
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index a1da21349235..281a7716e3f3 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -7,6 +7,8 @@
* of the GNU General Public License version 2.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
@@ -99,12 +101,12 @@ static inline void gfs2_setbit(const struct gfs2_rbm *rbm, bool do_clone,
cur_state = (*byte1 >> bit) & GFS2_BIT_MASK;
if (unlikely(!valid_change[new_state * 4 + cur_state])) {
- printk(KERN_WARNING "GFS2: buf_blk = 0x%x old_state=%d, "
- "new_state=%d\n", rbm->offset, cur_state, new_state);
- printk(KERN_WARNING "GFS2: rgrp=0x%llx bi_start=0x%x\n",
- (unsigned long long)rbm->rgd->rd_addr, bi->bi_start);
- printk(KERN_WARNING "GFS2: bi_offset=0x%x bi_len=0x%x\n",
- bi->bi_offset, bi->bi_len);
+ pr_warn("buf_blk = 0x%x old_state=%d, new_state=%d\n",
+ rbm->offset, cur_state, new_state);
+ pr_warn("rgrp=0x%llx bi_start=0x%x\n",
+ (unsigned long long)rbm->rgd->rd_addr, bi->bi_start);
+ pr_warn("bi_offset=0x%x bi_len=0x%x\n",
+ bi->bi_offset, bi->bi_len);
dump_stack();
gfs2_consist_rgrpd(rbm->rgd);
return;
@@ -736,11 +738,11 @@ void gfs2_clear_rgrpd(struct gfs2_sbd *sdp)
static void gfs2_rindex_print(const struct gfs2_rgrpd *rgd)
{
- printk(KERN_INFO " ri_addr = %llu\n", (unsigned long long)rgd->rd_addr);
- printk(KERN_INFO " ri_length = %u\n", rgd->rd_length);
- printk(KERN_INFO " ri_data0 = %llu\n", (unsigned long long)rgd->rd_data0);
- printk(KERN_INFO " ri_data = %u\n", rgd->rd_data);
- printk(KERN_INFO " ri_bitbytes = %u\n", rgd->rd_bitbytes);
+ pr_info("ri_addr = %llu\n", (unsigned long long)rgd->rd_addr);
+ pr_info("ri_length = %u\n", rgd->rd_length);
+ pr_info("ri_data0 = %llu\n", (unsigned long long)rgd->rd_data0);
+ pr_info("ri_data = %u\n", rgd->rd_data);
+ pr_info("ri_bitbytes = %u\n", rgd->rd_bitbytes);
}
/**
@@ -1102,7 +1104,7 @@ static u32 count_unlinked(struct gfs2_rgrpd *rgd)
* Returns: errno
*/
-int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd)
+static int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd)
{
struct gfs2_sbd *sdp = rgd->rd_sbd;
struct gfs2_glock *gl = rgd->rd_gl;
@@ -1169,7 +1171,7 @@ fail:
return error;
}
-int update_rgrp_lvb(struct gfs2_rgrpd *rgd)
+static int update_rgrp_lvb(struct gfs2_rgrpd *rgd)
{
u32 rl_flags;
@@ -2278,7 +2280,7 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
}
}
if (rbm.rgd->rd_free < *nblocks) {
- printk(KERN_WARNING "nblocks=%u\n", *nblocks);
+ pr_warn("nblocks=%u\n", *nblocks);
goto rgrp_error;
}
@@ -2296,7 +2298,7 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
gfs2_statfs_change(sdp, 0, -(s64)*nblocks, dinode ? 1 : 0);
if (dinode)
- gfs2_trans_add_unrevoke(sdp, block, 1);
+ gfs2_trans_add_unrevoke(sdp, block, *nblocks);
gfs2_quota_change(ip, *nblocks, ip->i_inode.i_uid, ip->i_inode.i_gid);
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index 60f60f6181f3..de8afad89e51 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -7,6 +7,8 @@
* of the GNU General Public License version 2.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/bio.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -175,8 +177,7 @@ int gfs2_mount_args(struct gfs2_args *args, char *options)
break;
case Opt_debug:
if (args->ar_errors == GFS2_ERRORS_PANIC) {
- printk(KERN_WARNING "GFS2: -o debug and -o errors=panic "
- "are mutually exclusive.\n");
+ pr_warn("-o debug and -o errors=panic are mutually exclusive\n");
return -EINVAL;
}
args->ar_debug = 1;
@@ -228,21 +229,21 @@ int gfs2_mount_args(struct gfs2_args *args, char *options)
case Opt_commit:
rv = match_int(&tmp[0], &args->ar_commit);
if (rv || args->ar_commit <= 0) {
- printk(KERN_WARNING "GFS2: commit mount option requires a positive numeric argument\n");
+ pr_warn("commit mount option requires a positive numeric argument\n");
return rv ? rv : -EINVAL;
}
break;
case Opt_statfs_quantum:
rv = match_int(&tmp[0], &args->ar_statfs_quantum);
if (rv || args->ar_statfs_quantum < 0) {
- printk(KERN_WARNING "GFS2: statfs_quantum mount option requires a non-negative numeric argument\n");
+ pr_warn("statfs_quantum mount option requires a non-negative numeric argument\n");
return rv ? rv : -EINVAL;
}
break;
case Opt_quota_quantum:
rv = match_int(&tmp[0], &args->ar_quota_quantum);
if (rv || args->ar_quota_quantum <= 0) {
- printk(KERN_WARNING "GFS2: quota_quantum mount option requires a positive numeric argument\n");
+ pr_warn("quota_quantum mount option requires a positive numeric argument\n");
return rv ? rv : -EINVAL;
}
break;
@@ -250,7 +251,7 @@ int gfs2_mount_args(struct gfs2_args *args, char *options)
rv = match_int(&tmp[0], &args->ar_statfs_percent);
if (rv || args->ar_statfs_percent < 0 ||
args->ar_statfs_percent > 100) {
- printk(KERN_WARNING "statfs_percent mount option requires a numeric argument between 0 and 100\n");
+ pr_warn("statfs_percent mount option requires a numeric argument between 0 and 100\n");
return rv ? rv : -EINVAL;
}
break;
@@ -259,8 +260,7 @@ int gfs2_mount_args(struct gfs2_args *args, char *options)
break;
case Opt_err_panic:
if (args->ar_debug) {
- printk(KERN_WARNING "GFS2: -o debug and -o errors=panic "
- "are mutually exclusive.\n");
+ pr_warn("-o debug and -o errors=panic are mutually exclusive\n");
return -EINVAL;
}
args->ar_errors = GFS2_ERRORS_PANIC;
@@ -279,7 +279,7 @@ int gfs2_mount_args(struct gfs2_args *args, char *options)
break;
case Opt_error:
default:
- printk(KERN_WARNING "GFS2: invalid mount option: %s\n", o);
+ pr_warn("invalid mount option: %s\n", o);
return -EINVAL;
}
}
@@ -295,9 +295,8 @@ int gfs2_mount_args(struct gfs2_args *args, char *options)
void gfs2_jindex_free(struct gfs2_sbd *sdp)
{
- struct list_head list, *head;
+ struct list_head list;
struct gfs2_jdesc *jd;
- struct gfs2_journal_extent *jext;
spin_lock(&sdp->sd_jindex_spin);
list_add(&list, &sdp->sd_jindex_list);
@@ -307,14 +306,7 @@ void gfs2_jindex_free(struct gfs2_sbd *sdp)
while (!list_empty(&list)) {
jd = list_entry(list.next, struct gfs2_jdesc, jd_list);
- head = &jd->extent_list;
- while (!list_empty(head)) {
- jext = list_entry(head->next,
- struct gfs2_journal_extent,
- extent_list);
- list_del(&jext->extent_list);
- kfree(jext);
- }
+ gfs2_free_journal_extents(jd);
list_del(&jd->jd_list);
iput(jd->jd_inode);
kfree(jd);
@@ -1175,6 +1167,8 @@ static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data)
struct gfs2_tune *gt = &sdp->sd_tune;
int error;
+ sync_filesystem(sb);
+
spin_lock(&gt->gt_spin);
args.ar_commit = gt->gt_logd_secs;
args.ar_quota_quantum = gt->gt_quota_quantum;
@@ -1256,7 +1250,7 @@ static int gfs2_drop_inode(struct inode *inode)
{
struct gfs2_inode *ip = GFS2_I(inode);
- if (inode->i_nlink) {
+ if (!test_bit(GIF_FREE_VFS_INODE, &ip->i_flags) && inode->i_nlink) {
struct gfs2_glock *gl = ip->i_iopen_gh.gh_gl;
if (gl && test_bit(GLF_DEMOTE, &gl->gl_flags))
clear_nlink(inode);
@@ -1471,6 +1465,11 @@ static void gfs2_evict_inode(struct inode *inode)
struct gfs2_holder gh;
int error;
+ if (test_bit(GIF_FREE_VFS_INODE, &ip->i_flags)) {
+ clear_inode(inode);
+ return;
+ }
+
if (inode->i_nlink || (sb->s_flags & MS_RDONLY))
goto out;
@@ -1558,7 +1557,7 @@ out_unlock:
fs_warn(sdp, "gfs2_evict_inode: %d\n", error);
out:
/* Case 3 starts here */
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
gfs2_rs_delete(ip, NULL);
gfs2_ordered_del_inode(ip);
clear_inode(inode);
diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c
index d09f6edda0ff..de25d5577e5d 100644
--- a/fs/gfs2/sys.c
+++ b/fs/gfs2/sys.c
@@ -7,6 +7,8 @@
* of the GNU General Public License version 2.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
@@ -138,9 +140,8 @@ static ssize_t withdraw_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
if (simple_strtol(buf, NULL, 0) != 1)
return -EINVAL;
- gfs2_lm_withdraw(sdp,
- "GFS2: fsid=%s: withdrawing from cluster at user's request\n",
- sdp->sd_fsname);
+ gfs2_lm_withdraw(sdp, "withdrawing from cluster at user's request\n");
+
return len;
}
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c
index 2b20d7046bf3..bead90d27bad 100644
--- a/fs/gfs2/trans.c
+++ b/fs/gfs2/trans.c
@@ -7,6 +7,8 @@
* of the GNU General Public License version 2.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -51,6 +53,9 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
if (revokes)
tr->tr_reserved += gfs2_struct2blk(sdp, revokes,
sizeof(u64));
+ INIT_LIST_HEAD(&tr->tr_databuf);
+ INIT_LIST_HEAD(&tr->tr_buf);
+
sb_start_intwrite(sdp->sd_vfs);
gfs2_holder_init(sdp->sd_trans_gl, LM_ST_SHARED, 0, &tr->tr_t_gh);
@@ -96,14 +101,13 @@ static void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks)
static void gfs2_print_trans(const struct gfs2_trans *tr)
{
- printk(KERN_WARNING "GFS2: Transaction created at: %pSR\n",
- (void *)tr->tr_ip);
- printk(KERN_WARNING "GFS2: blocks=%u revokes=%u reserved=%u touched=%d\n",
- tr->tr_blocks, tr->tr_revokes, tr->tr_reserved, tr->tr_touched);
- printk(KERN_WARNING "GFS2: Buf %u/%u Databuf %u/%u Revoke %u/%u\n",
- tr->tr_num_buf_new, tr->tr_num_buf_rm,
- tr->tr_num_databuf_new, tr->tr_num_databuf_rm,
- tr->tr_num_revoke, tr->tr_num_revoke_rm);
+ pr_warn("Transaction created at: %pSR\n", (void *)tr->tr_ip);
+ pr_warn("blocks=%u revokes=%u reserved=%u touched=%u\n",
+ tr->tr_blocks, tr->tr_revokes, tr->tr_reserved, tr->tr_touched);
+ pr_warn("Buf %u/%u Databuf %u/%u Revoke %u/%u\n",
+ tr->tr_num_buf_new, tr->tr_num_buf_rm,
+ tr->tr_num_databuf_new, tr->tr_num_databuf_rm,
+ tr->tr_num_revoke, tr->tr_num_revoke_rm);
}
void gfs2_trans_end(struct gfs2_sbd *sdp)
@@ -210,8 +214,7 @@ void gfs2_trans_add_data(struct gfs2_glock *gl, struct buffer_head *bh)
set_bit(GLF_DIRTY, &bd->bd_gl->gl_flags);
gfs2_pin(sdp, bd->bd_bh);
tr->tr_num_databuf_new++;
- sdp->sd_log_num_databuf++;
- list_add_tail(&bd->bd_list, &sdp->sd_log_le_databuf);
+ list_add_tail(&bd->bd_list, &tr->tr_databuf);
}
gfs2_log_unlock(sdp);
unlock_buffer(bh);
@@ -230,16 +233,14 @@ static void meta_lo_add(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd)
set_bit(GLF_DIRTY, &bd->bd_gl->gl_flags);
mh = (struct gfs2_meta_header *)bd->bd_bh->b_data;
if (unlikely(mh->mh_magic != cpu_to_be32(GFS2_MAGIC))) {
- printk(KERN_ERR
- "Attempting to add uninitialised block to journal (inplace block=%lld)\n",
+ pr_err("Attempting to add uninitialised block to journal (inplace block=%lld)\n",
(unsigned long long)bd->bd_bh->b_blocknr);
BUG();
}
gfs2_pin(sdp, bd->bd_bh);
mh->__pad0 = cpu_to_be64(0);
mh->mh_jid = cpu_to_be32(sdp->sd_jdesc->jd_jid);
- sdp->sd_log_num_buf++;
- list_add(&bd->bd_list, &sdp->sd_log_le_buf);
+ list_add(&bd->bd_list, &tr->tr_buf);
tr->tr_num_buf_new++;
}
diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c
index f7109f689e61..86d2035ac669 100644
--- a/fs/gfs2/util.c
+++ b/fs/gfs2/util.c
@@ -7,6 +7,8 @@
* of the GNU General Public License version 2.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
@@ -30,22 +32,27 @@ mempool_t *gfs2_page_pool __read_mostly;
void gfs2_assert_i(struct gfs2_sbd *sdp)
{
- printk(KERN_EMERG "GFS2: fsid=%s: fatal assertion failed\n",
- sdp->sd_fsname);
+ fs_emerg(sdp, "fatal assertion failed\n");
}
-int gfs2_lm_withdraw(struct gfs2_sbd *sdp, char *fmt, ...)
+int gfs2_lm_withdraw(struct gfs2_sbd *sdp, const char *fmt, ...)
{
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
const struct lm_lockops *lm = ls->ls_ops;
va_list args;
+ struct va_format vaf;
if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW &&
test_and_set_bit(SDF_SHUTDOWN, &sdp->sd_flags))
return 0;
va_start(args, fmt);
- vprintk(fmt, args);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ fs_err(sdp, "%pV", &vaf);
+
va_end(args);
if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW) {
@@ -66,7 +73,7 @@ int gfs2_lm_withdraw(struct gfs2_sbd *sdp, char *fmt, ...)
}
if (sdp->sd_args.ar_errors == GFS2_ERRORS_PANIC)
- panic("GFS2: fsid=%s: panic requested.\n", sdp->sd_fsname);
+ panic("GFS2: fsid=%s: panic requested\n", sdp->sd_fsname);
return -1;
}
@@ -82,10 +89,9 @@ int gfs2_assert_withdraw_i(struct gfs2_sbd *sdp, char *assertion,
{
int me;
me = gfs2_lm_withdraw(sdp,
- "GFS2: fsid=%s: fatal: assertion \"%s\" failed\n"
- "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
- sdp->sd_fsname, assertion,
- sdp->sd_fsname, function, file, line);
+ "fatal: assertion \"%s\" failed\n"
+ " function = %s, file = %s, line = %u\n",
+ assertion, function, file, line);
dump_stack();
return (me) ? -1 : -2;
}
@@ -105,11 +111,8 @@ int gfs2_assert_warn_i(struct gfs2_sbd *sdp, char *assertion,
return -2;
if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW)
- printk(KERN_WARNING
- "GFS2: fsid=%s: warning: assertion \"%s\" failed\n"
- "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
- sdp->sd_fsname, assertion,
- sdp->sd_fsname, function, file, line);
+ fs_warn(sdp, "warning: assertion \"%s\" failed at function = %s, file = %s, line = %u\n",
+ assertion, function, file, line);
if (sdp->sd_args.ar_debug)
BUG();
@@ -138,10 +141,8 @@ int gfs2_consist_i(struct gfs2_sbd *sdp, int cluster_wide, const char *function,
{
int rv;
rv = gfs2_lm_withdraw(sdp,
- "GFS2: fsid=%s: fatal: filesystem consistency error\n"
- "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
- sdp->sd_fsname,
- sdp->sd_fsname, function, file, line);
+ "fatal: filesystem consistency error - function = %s, file = %s, line = %u\n",
+ function, file, line);
return rv;
}
@@ -157,13 +158,12 @@ int gfs2_consist_inode_i(struct gfs2_inode *ip, int cluster_wide,
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
int rv;
rv = gfs2_lm_withdraw(sdp,
- "GFS2: fsid=%s: fatal: filesystem consistency error\n"
- "GFS2: fsid=%s: inode = %llu %llu\n"
- "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
- sdp->sd_fsname,
- sdp->sd_fsname, (unsigned long long)ip->i_no_formal_ino,
- (unsigned long long)ip->i_no_addr,
- sdp->sd_fsname, function, file, line);
+ "fatal: filesystem consistency error\n"
+ " inode = %llu %llu\n"
+ " function = %s, file = %s, line = %u\n",
+ (unsigned long long)ip->i_no_formal_ino,
+ (unsigned long long)ip->i_no_addr,
+ function, file, line);
return rv;
}
@@ -179,12 +179,11 @@ int gfs2_consist_rgrpd_i(struct gfs2_rgrpd *rgd, int cluster_wide,
struct gfs2_sbd *sdp = rgd->rd_sbd;
int rv;
rv = gfs2_lm_withdraw(sdp,
- "GFS2: fsid=%s: fatal: filesystem consistency error\n"
- "GFS2: fsid=%s: RG = %llu\n"
- "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
- sdp->sd_fsname,
- sdp->sd_fsname, (unsigned long long)rgd->rd_addr,
- sdp->sd_fsname, function, file, line);
+ "fatal: filesystem consistency error\n"
+ " RG = %llu\n"
+ " function = %s, file = %s, line = %u\n",
+ (unsigned long long)rgd->rd_addr,
+ function, file, line);
return rv;
}
@@ -200,12 +199,11 @@ int gfs2_meta_check_ii(struct gfs2_sbd *sdp, struct buffer_head *bh,
{
int me;
me = gfs2_lm_withdraw(sdp,
- "GFS2: fsid=%s: fatal: invalid metadata block\n"
- "GFS2: fsid=%s: bh = %llu (%s)\n"
- "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
- sdp->sd_fsname,
- sdp->sd_fsname, (unsigned long long)bh->b_blocknr, type,
- sdp->sd_fsname, function, file, line);
+ "fatal: invalid metadata block\n"
+ " bh = %llu (%s)\n"
+ " function = %s, file = %s, line = %u\n",
+ (unsigned long long)bh->b_blocknr, type,
+ function, file, line);
return (me) ? -1 : -2;
}
@@ -221,12 +219,11 @@ int gfs2_metatype_check_ii(struct gfs2_sbd *sdp, struct buffer_head *bh,
{
int me;
me = gfs2_lm_withdraw(sdp,
- "GFS2: fsid=%s: fatal: invalid metadata block\n"
- "GFS2: fsid=%s: bh = %llu (type: exp=%u, found=%u)\n"
- "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
- sdp->sd_fsname,
- sdp->sd_fsname, (unsigned long long)bh->b_blocknr, type, t,
- sdp->sd_fsname, function, file, line);
+ "fatal: invalid metadata block\n"
+ " bh = %llu (type: exp=%u, found=%u)\n"
+ " function = %s, file = %s, line = %u\n",
+ (unsigned long long)bh->b_blocknr, type, t,
+ function, file, line);
return (me) ? -1 : -2;
}
@@ -241,10 +238,9 @@ int gfs2_io_error_i(struct gfs2_sbd *sdp, const char *function, char *file,
{
int rv;
rv = gfs2_lm_withdraw(sdp,
- "GFS2: fsid=%s: fatal: I/O error\n"
- "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
- sdp->sd_fsname,
- sdp->sd_fsname, function, file, line);
+ "fatal: I/O error\n"
+ " function = %s, file = %s, line = %u\n",
+ function, file, line);
return rv;
}
@@ -259,12 +255,11 @@ int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh,
{
int rv;
rv = gfs2_lm_withdraw(sdp,
- "GFS2: fsid=%s: fatal: I/O error\n"
- "GFS2: fsid=%s: block = %llu\n"
- "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
- sdp->sd_fsname,
- sdp->sd_fsname, (unsigned long long)bh->b_blocknr,
- sdp->sd_fsname, function, file, line);
+ "fatal: I/O error\n"
+ " block = %llu\n"
+ " function = %s, file = %s, line = %u\n",
+ (unsigned long long)bh->b_blocknr,
+ function, file, line);
return rv;
}
diff --git a/fs/gfs2/util.h b/fs/gfs2/util.h
index b7ffb09b99ea..cbdcbdf39614 100644
--- a/fs/gfs2/util.h
+++ b/fs/gfs2/util.h
@@ -10,22 +10,23 @@
#ifndef __UTIL_DOT_H__
#define __UTIL_DOT_H__
+#ifdef pr_fmt
+#undef pr_fmt
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#endif
+
#include <linux/mempool.h>
#include "incore.h"
-#define fs_printk(level, fs, fmt, arg...) \
- printk(level "GFS2: fsid=%s: " fmt , (fs)->sd_fsname , ## arg)
-
-#define fs_info(fs, fmt, arg...) \
- fs_printk(KERN_INFO , fs , fmt , ## arg)
-
-#define fs_warn(fs, fmt, arg...) \
- fs_printk(KERN_WARNING , fs , fmt , ## arg)
-
-#define fs_err(fs, fmt, arg...) \
- fs_printk(KERN_ERR, fs , fmt , ## arg)
-
+#define fs_emerg(fs, fmt, ...) \
+ pr_emerg("fsid=%s: " fmt, (fs)->sd_fsname, ##__VA_ARGS__)
+#define fs_warn(fs, fmt, ...) \
+ pr_warn("fsid=%s: " fmt, (fs)->sd_fsname, ##__VA_ARGS__)
+#define fs_err(fs, fmt, ...) \
+ pr_err("fsid=%s: " fmt, (fs)->sd_fsname, ##__VA_ARGS__)
+#define fs_info(fs, fmt, ...) \
+ pr_info("fsid=%s: " fmt, (fs)->sd_fsname, ##__VA_ARGS__)
void gfs2_assert_i(struct gfs2_sbd *sdp);
@@ -85,7 +86,7 @@ static inline int gfs2_meta_check(struct gfs2_sbd *sdp,
struct gfs2_meta_header *mh = (struct gfs2_meta_header *)bh->b_data;
u32 magic = be32_to_cpu(mh->mh_magic);
if (unlikely(magic != GFS2_MAGIC)) {
- printk(KERN_ERR "GFS2: Magic number missing at %llu\n",
+ pr_err("Magic number missing at %llu\n",
(unsigned long long)bh->b_blocknr);
return -EIO;
}
@@ -164,7 +165,7 @@ static inline unsigned int gfs2_tune_get_i(struct gfs2_tune *gt,
#define gfs2_tune_get(sdp, field) \
gfs2_tune_get_i(&(sdp)->sd_tune, &(sdp)->sd_tune.field)
-int gfs2_lm_withdraw(struct gfs2_sbd *sdp, char *fmt, ...);
+__printf(2, 3)
+int gfs2_lm_withdraw(struct gfs2_sbd *sdp, const char *fmt, ...);
#endif /* __UTIL_DOT_H__ */
-
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 380ab31b5e0f..9e2fecd62f62 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -547,7 +547,7 @@ out:
void hfs_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (HFS_IS_RSRC(inode) && HFS_I(inode)->rsrc_inode) {
HFS_I(HFS_I(inode)->rsrc_inode)->rsrc_inode = NULL;
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 2d2039e754cd..eee7206c38d1 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -112,6 +112,7 @@ static int hfs_statfs(struct dentry *dentry, struct kstatfs *buf)
static int hfs_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
*flags |= MS_NODIRATIME;
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
return 0;
diff --git a/fs/hfsplus/attributes.c b/fs/hfsplus/attributes.c
index 0f47890299c4..caf89a7be0a1 100644
--- a/fs/hfsplus/attributes.c
+++ b/fs/hfsplus/attributes.c
@@ -11,7 +11,7 @@
static struct kmem_cache *hfsplus_attr_tree_cachep;
-int hfsplus_create_attr_tree_cache(void)
+int __init hfsplus_create_attr_tree_cache(void)
{
if (hfsplus_attr_tree_cachep)
return -EEXIST;
diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c
index fbb212fbb1ef..a7aafb35b624 100644
--- a/fs/hfsplus/extents.c
+++ b/fs/hfsplus/extents.c
@@ -227,10 +227,8 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,
u32 ablock, dblock, mask;
sector_t sector;
int was_dirty = 0;
- int shift;
/* Convert inode block to disk allocation block */
- shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits;
ablock = iblock >> sbi->fs_shift;
if (iblock >= hip->fs_blocks) {
@@ -498,11 +496,13 @@ int hfsplus_file_extend(struct inode *inode)
goto insert_extent;
}
out:
- mutex_unlock(&hip->extents_lock);
if (!res) {
hip->alloc_blocks += len;
+ mutex_unlock(&hip->extents_lock);
hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
+ return 0;
}
+ mutex_unlock(&hip->extents_lock);
return res;
insert_extent:
@@ -556,11 +556,13 @@ void hfsplus_file_truncate(struct inode *inode)
blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >>
HFSPLUS_SB(sb)->alloc_blksz_shift;
+
+ mutex_lock(&hip->extents_lock);
+
alloc_cnt = hip->alloc_blocks;
if (blk_cnt == alloc_cnt)
- goto out;
+ goto out_unlock;
- mutex_lock(&hip->extents_lock);
res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
if (res) {
mutex_unlock(&hip->extents_lock);
@@ -592,10 +594,10 @@ void hfsplus_file_truncate(struct inode *inode)
hfs_brec_remove(&fd);
}
hfs_find_exit(&fd);
- mutex_unlock(&hip->extents_lock);
hip->alloc_blocks = blk_cnt;
-out:
+out_unlock:
+ mutex_unlock(&hip->extents_lock);
hip->phys_size = inode->i_size;
hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >>
sb->s_blocksize_bits;
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 62d571eb69ba..83dc29286b10 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -367,7 +367,7 @@ typedef int (*search_strategy_t)(struct hfs_bnode *,
*/
/* attributes.c */
-int hfsplus_create_attr_tree_cache(void);
+int __init hfsplus_create_attr_tree_cache(void);
void hfsplus_destroy_attr_tree_cache(void);
hfsplus_attr_entry *hfsplus_alloc_attr_entry(void);
void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry_p);
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 80875aa640ef..a513d2d36be9 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -161,7 +161,7 @@ static int hfsplus_write_inode(struct inode *inode,
static void hfsplus_evict_inode(struct inode *inode)
{
hfs_dbg(INODE, "hfsplus_evict_inode: %lu\n", inode->i_ino);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (HFSPLUS_IS_RSRC(inode)) {
HFSPLUS_I(HFSPLUS_I(inode)->rsrc_inode)->rsrc_inode = NULL;
@@ -323,6 +323,7 @@ static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
return 0;
if (!(*flags & MS_RDONLY)) {
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index fe649d325b1f..9c470fde9878 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -230,7 +230,7 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb)
static void hostfs_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (HOSTFS_I(inode)->fd != -1) {
close_file(&HOSTFS_I(inode)->fd);
diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c
index 9edeeb0ea97e..50a427313835 100644
--- a/fs/hpfs/inode.c
+++ b/fs/hpfs/inode.c
@@ -304,7 +304,7 @@ void hpfs_write_if_changed(struct inode *inode)
void hpfs_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (!inode->i_nlink) {
hpfs_lock(inode->i_sb);
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
index 4534ff688b76..fe3463a43236 100644
--- a/fs/hpfs/super.c
+++ b/fs/hpfs/super.c
@@ -421,6 +421,8 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
struct hpfs_sb_info *sbi = hpfs_sb(s);
char *new_opts = kstrdup(data, GFP_KERNEL);
+ sync_filesystem(s);
+
*flags |= MS_NOATIME;
hpfs_lock(s);
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index d19b30ababf1..204027520937 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -366,7 +366,13 @@ static void truncate_hugepages(struct inode *inode, loff_t lstart)
static void hugetlbfs_evict_inode(struct inode *inode)
{
+ struct resv_map *resv_map;
+
truncate_hugepages(inode, 0);
+ resv_map = (struct resv_map *)inode->i_mapping->private_data;
+ /* root inode doesn't have the resv_map, so we should check it */
+ if (resv_map)
+ resv_map_release(&resv_map->refs);
clear_inode(inode);
}
@@ -476,6 +482,11 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb,
umode_t mode, dev_t dev)
{
struct inode *inode;
+ struct resv_map *resv_map;
+
+ resv_map = resv_map_alloc();
+ if (!resv_map)
+ return NULL;
inode = new_inode(sb);
if (inode) {
@@ -487,7 +498,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb,
inode->i_mapping->a_ops = &hugetlbfs_aops;
inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- INIT_LIST_HEAD(&inode->i_mapping->private_list);
+ inode->i_mapping->private_data = resv_map;
info = HUGETLBFS_I(inode);
/*
* The policy is initialized here even if we are creating a
@@ -517,7 +528,9 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb,
break;
}
lockdep_annotate_inode_mutex_key(inode);
- }
+ } else
+ kref_put(&resv_map->refs, resv_map_release);
+
return inode;
}
diff --git a/fs/inode.c b/fs/inode.c
index 4bcdad3c9361..f96d2a6f88cc 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -503,6 +503,7 @@ void clear_inode(struct inode *inode)
*/
spin_lock_irq(&inode->i_data.tree_lock);
BUG_ON(inode->i_data.nrpages);
+ BUG_ON(inode->i_data.nrshadows);
spin_unlock_irq(&inode->i_data.tree_lock);
BUG_ON(!list_empty(&inode->i_data.private_list));
BUG_ON(!(inode->i_state & I_FREEING));
@@ -548,8 +549,7 @@ static void evict(struct inode *inode)
if (op->evict_inode) {
op->evict_inode(inode);
} else {
- if (inode->i_data.nrpages)
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
}
if (S_ISBLK(inode->i_mode) && inode->i_bdev)
@@ -944,24 +944,22 @@ EXPORT_SYMBOL(unlock_new_inode);
/**
* lock_two_nondirectories - take two i_mutexes on non-directory objects
+ *
+ * Lock any non-NULL argument that is not a directory.
+ * Zero, one or two objects may be locked by this function.
+ *
* @inode1: first inode to lock
* @inode2: second inode to lock
*/
void lock_two_nondirectories(struct inode *inode1, struct inode *inode2)
{
- WARN_ON_ONCE(S_ISDIR(inode1->i_mode));
- if (inode1 == inode2 || !inode2) {
- mutex_lock(&inode1->i_mutex);
- return;
- }
- WARN_ON_ONCE(S_ISDIR(inode2->i_mode));
- if (inode1 < inode2) {
+ if (inode1 > inode2)
+ swap(inode1, inode2);
+
+ if (inode1 && !S_ISDIR(inode1->i_mode))
mutex_lock(&inode1->i_mutex);
+ if (inode2 && !S_ISDIR(inode2->i_mode) && inode2 != inode1)
mutex_lock_nested(&inode2->i_mutex, I_MUTEX_NONDIR2);
- } else {
- mutex_lock(&inode2->i_mutex);
- mutex_lock_nested(&inode1->i_mutex, I_MUTEX_NONDIR2);
- }
}
EXPORT_SYMBOL(lock_two_nondirectories);
@@ -972,8 +970,9 @@ EXPORT_SYMBOL(lock_two_nondirectories);
*/
void unlock_two_nondirectories(struct inode *inode1, struct inode *inode2)
{
- mutex_unlock(&inode1->i_mutex);
- if (inode2 && inode2 != inode1)
+ if (inode1 && !S_ISDIR(inode1->i_mode))
+ mutex_unlock(&inode1->i_mutex);
+ if (inode2 && !S_ISDIR(inode2->i_mode) && inode2 != inode1)
mutex_unlock(&inode2->i_mutex);
}
EXPORT_SYMBOL(unlock_two_nondirectories);
@@ -1899,3 +1898,34 @@ void inode_dio_done(struct inode *inode)
wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
}
EXPORT_SYMBOL(inode_dio_done);
+
+/*
+ * inode_set_flags - atomically set some inode flags
+ *
+ * Note: the caller should be holding i_mutex, or else be sure that
+ * they have exclusive access to the inode structure (i.e., while the
+ * inode is being instantiated). The reason for the cmpxchg() loop
+ * --- which wouldn't be necessary if all code paths which modify
+ * i_flags actually followed this rule, is that there is at least one
+ * code path which doesn't today --- for example,
+ * __generic_file_aio_write() calls file_remove_suid() without holding
+ * i_mutex --- so we use cmpxchg() out of an abundance of caution.
+ *
+ * In the long run, i_mutex is overkill, and we should probably look
+ * at using the i_lock spinlock to protect i_flags, and then make sure
+ * it is so documented in include/linux/fs.h and that all code follows
+ * the locking convention!!
+ */
+void inode_set_flags(struct inode *inode, unsigned int flags,
+ unsigned int mask)
+{
+ unsigned int old_flags, new_flags;
+
+ WARN_ON_ONCE(flags & ~mask);
+ do {
+ old_flags = ACCESS_ONCE(inode->i_flags);
+ new_flags = (old_flags & ~mask) | flags;
+ } while (unlikely(cmpxchg(&inode->i_flags, old_flags,
+ new_flags) != old_flags));
+}
+EXPORT_SYMBOL(inode_set_flags);
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 4a9e10ea13f2..6af66ee56390 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -117,6 +117,7 @@ static void destroy_inodecache(void)
static int isofs_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
if (!(*flags & MS_RDONLY))
return -EROFS;
return 0;
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
index cf2fc0594063..5f26139a165a 100644
--- a/fs/jbd2/commit.c
+++ b/fs/jbd2/commit.c
@@ -555,7 +555,6 @@ void jbd2_journal_commit_transaction(journal_t *journal)
blk_start_plug(&plug);
jbd2_journal_write_revoke_records(journal, commit_transaction,
&log_bufs, WRITE_SYNC);
- blk_finish_plug(&plug);
jbd_debug(3, "JBD2: commit phase 2b\n");
@@ -582,7 +581,6 @@ void jbd2_journal_commit_transaction(journal_t *journal)
err = 0;
bufs = 0;
descriptor = NULL;
- blk_start_plug(&plug);
while (commit_transaction->t_buffers) {
/* Find the next buffer to be journaled... */
@@ -1067,6 +1065,25 @@ restart_loop:
goto restart_loop;
}
+ /* Add the transaction to the checkpoint list
+ * __journal_remove_checkpoint() can not destroy transaction
+ * under us because it is not marked as T_FINISHED yet */
+ if (journal->j_checkpoint_transactions == NULL) {
+ journal->j_checkpoint_transactions = commit_transaction;
+ commit_transaction->t_cpnext = commit_transaction;
+ commit_transaction->t_cpprev = commit_transaction;
+ } else {
+ commit_transaction->t_cpnext =
+ journal->j_checkpoint_transactions;
+ commit_transaction->t_cpprev =
+ commit_transaction->t_cpnext->t_cpprev;
+ commit_transaction->t_cpnext->t_cpprev =
+ commit_transaction;
+ commit_transaction->t_cpprev->t_cpnext =
+ commit_transaction;
+ }
+ spin_unlock(&journal->j_list_lock);
+
/* Done with this transaction! */
jbd_debug(3, "JBD2: commit phase 7\n");
@@ -1085,24 +1102,7 @@ restart_loop:
atomic_read(&commit_transaction->t_handle_count);
trace_jbd2_run_stats(journal->j_fs_dev->bd_dev,
commit_transaction->t_tid, &stats.run);
-
- /*
- * Calculate overall stats
- */
- spin_lock(&journal->j_history_lock);
- journal->j_stats.ts_tid++;
- if (commit_transaction->t_requested)
- journal->j_stats.ts_requested++;
- journal->j_stats.run.rs_wait += stats.run.rs_wait;
- journal->j_stats.run.rs_request_delay += stats.run.rs_request_delay;
- journal->j_stats.run.rs_running += stats.run.rs_running;
- journal->j_stats.run.rs_locked += stats.run.rs_locked;
- journal->j_stats.run.rs_flushing += stats.run.rs_flushing;
- journal->j_stats.run.rs_logging += stats.run.rs_logging;
- journal->j_stats.run.rs_handle_count += stats.run.rs_handle_count;
- journal->j_stats.run.rs_blocks += stats.run.rs_blocks;
- journal->j_stats.run.rs_blocks_logged += stats.run.rs_blocks_logged;
- spin_unlock(&journal->j_history_lock);
+ stats.ts_requested = (commit_transaction->t_requested) ? 1 : 0;
commit_transaction->t_state = T_COMMIT_CALLBACK;
J_ASSERT(commit_transaction == journal->j_committing_transaction);
@@ -1122,24 +1122,6 @@ restart_loop:
write_unlock(&journal->j_state_lock);
- if (journal->j_checkpoint_transactions == NULL) {
- journal->j_checkpoint_transactions = commit_transaction;
- commit_transaction->t_cpnext = commit_transaction;
- commit_transaction->t_cpprev = commit_transaction;
- } else {
- commit_transaction->t_cpnext =
- journal->j_checkpoint_transactions;
- commit_transaction->t_cpprev =
- commit_transaction->t_cpnext->t_cpprev;
- commit_transaction->t_cpnext->t_cpprev =
- commit_transaction;
- commit_transaction->t_cpprev->t_cpnext =
- commit_transaction;
- }
- spin_unlock(&journal->j_list_lock);
- /* Drop all spin_locks because commit_callback may be block.
- * __journal_remove_checkpoint() can not destroy transaction
- * under us because it is not marked as T_FINISHED yet */
if (journal->j_commit_callback)
journal->j_commit_callback(journal, commit_transaction);
@@ -1150,7 +1132,7 @@ restart_loop:
write_lock(&journal->j_state_lock);
spin_lock(&journal->j_list_lock);
commit_transaction->t_state = T_FINISHED;
- /* Recheck checkpoint lists after j_list_lock was dropped */
+ /* Check if the transaction can be dropped now that we are finished */
if (commit_transaction->t_checkpoint_list == NULL &&
commit_transaction->t_checkpoint_io_list == NULL) {
__jbd2_journal_drop_transaction(journal, commit_transaction);
@@ -1159,4 +1141,21 @@ restart_loop:
spin_unlock(&journal->j_list_lock);
write_unlock(&journal->j_state_lock);
wake_up(&journal->j_wait_done_commit);
+
+ /*
+ * Calculate overall stats
+ */
+ spin_lock(&journal->j_history_lock);
+ journal->j_stats.ts_tid++;
+ journal->j_stats.ts_requested += stats.ts_requested;
+ journal->j_stats.run.rs_wait += stats.run.rs_wait;
+ journal->j_stats.run.rs_request_delay += stats.run.rs_request_delay;
+ journal->j_stats.run.rs_running += stats.run.rs_running;
+ journal->j_stats.run.rs_locked += stats.run.rs_locked;
+ journal->j_stats.run.rs_flushing += stats.run.rs_flushing;
+ journal->j_stats.run.rs_logging += stats.run.rs_logging;
+ journal->j_stats.run.rs_handle_count += stats.run.rs_handle_count;
+ journal->j_stats.run.rs_blocks += stats.run.rs_blocks;
+ journal->j_stats.run.rs_blocks_logged += stats.run.rs_blocks_logged;
+ spin_unlock(&journal->j_history_lock);
}
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 5fa344afb49a..67b8e303946c 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -122,7 +122,7 @@ EXPORT_SYMBOL(__jbd2_debug);
#endif
/* Checksumming functions */
-int jbd2_verify_csum_type(journal_t *j, journal_superblock_t *sb)
+static int jbd2_verify_csum_type(journal_t *j, journal_superblock_t *sb)
{
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return 1;
@@ -143,7 +143,7 @@ static __be32 jbd2_superblock_csum(journal_t *j, journal_superblock_t *sb)
return cpu_to_be32(csum);
}
-int jbd2_superblock_csum_verify(journal_t *j, journal_superblock_t *sb)
+static int jbd2_superblock_csum_verify(journal_t *j, journal_superblock_t *sb)
{
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return 1;
@@ -151,7 +151,7 @@ int jbd2_superblock_csum_verify(journal_t *j, journal_superblock_t *sb)
return sb->s_checksum == jbd2_superblock_csum(j, sb);
}
-void jbd2_superblock_csum_set(journal_t *j, journal_superblock_t *sb)
+static void jbd2_superblock_csum_set(journal_t *j, journal_superblock_t *sb)
{
if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
return;
@@ -302,8 +302,8 @@ static void journal_kill_thread(journal_t *journal)
journal->j_flags |= JBD2_UNMOUNT;
while (journal->j_task) {
- wake_up(&journal->j_wait_commit);
write_unlock(&journal->j_state_lock);
+ wake_up(&journal->j_wait_commit);
wait_event(journal->j_wait_done_commit, journal->j_task == NULL);
write_lock(&journal->j_state_lock);
}
@@ -710,8 +710,8 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid)
while (tid_gt(tid, journal->j_commit_sequence)) {
jbd_debug(1, "JBD2: want %d, j_commit_sequence=%d\n",
tid, journal->j_commit_sequence);
- wake_up(&journal->j_wait_commit);
read_unlock(&journal->j_state_lock);
+ wake_up(&journal->j_wait_commit);
wait_event(journal->j_wait_done_commit,
!tid_gt(tid, journal->j_commit_sequence));
read_lock(&journal->j_state_lock);
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index 60bb365f54a5..38cfcf5f6fce 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -1073,7 +1073,6 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
* reused here.
*/
jbd_lock_bh_state(bh);
- spin_lock(&journal->j_list_lock);
J_ASSERT_JH(jh, (jh->b_transaction == transaction ||
jh->b_transaction == NULL ||
(jh->b_transaction == journal->j_committing_transaction &&
@@ -1096,12 +1095,14 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
jh->b_modified = 0;
JBUFFER_TRACE(jh, "file as BJ_Reserved");
+ spin_lock(&journal->j_list_lock);
__jbd2_journal_file_buffer(jh, transaction, BJ_Reserved);
} else if (jh->b_transaction == journal->j_committing_transaction) {
/* first access by this transaction */
jh->b_modified = 0;
JBUFFER_TRACE(jh, "set next transaction");
+ spin_lock(&journal->j_list_lock);
jh->b_next_transaction = transaction;
}
spin_unlock(&journal->j_list_lock);
@@ -1312,7 +1313,7 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
journal->j_running_transaction)) {
printk(KERN_ERR "JBD2: %s: "
"jh->b_transaction (%llu, %p, %u) != "
- "journal->j_running_transaction (%p, %u)",
+ "journal->j_running_transaction (%p, %u)\n",
journal->j_devname,
(unsigned long long) bh->b_blocknr,
jh->b_transaction,
@@ -1335,30 +1336,25 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
*/
if (jh->b_transaction != transaction) {
JBUFFER_TRACE(jh, "already on other transaction");
- if (unlikely(jh->b_transaction !=
- journal->j_committing_transaction)) {
- printk(KERN_ERR "JBD2: %s: "
- "jh->b_transaction (%llu, %p, %u) != "
- "journal->j_committing_transaction (%p, %u)",
+ if (unlikely(((jh->b_transaction !=
+ journal->j_committing_transaction)) ||
+ (jh->b_next_transaction != transaction))) {
+ printk(KERN_ERR "jbd2_journal_dirty_metadata: %s: "
+ "bad jh for block %llu: "
+ "transaction (%p, %u), "
+ "jh->b_transaction (%p, %u), "
+ "jh->b_next_transaction (%p, %u), jlist %u\n",
journal->j_devname,
(unsigned long long) bh->b_blocknr,
+ transaction, transaction->t_tid,
jh->b_transaction,
- jh->b_transaction ? jh->b_transaction->t_tid : 0,
- journal->j_committing_transaction,
- journal->j_committing_transaction ?
- journal->j_committing_transaction->t_tid : 0);
- ret = -EINVAL;
- }
- if (unlikely(jh->b_next_transaction != transaction)) {
- printk(KERN_ERR "JBD2: %s: "
- "jh->b_next_transaction (%llu, %p, %u) != "
- "transaction (%p, %u)",
- journal->j_devname,
- (unsigned long long) bh->b_blocknr,
+ jh->b_transaction ?
+ jh->b_transaction->t_tid : 0,
jh->b_next_transaction,
jh->b_next_transaction ?
jh->b_next_transaction->t_tid : 0,
- transaction, transaction->t_tid);
+ jh->b_jlist);
+ WARN_ON(1);
ret = -EINVAL;
}
/* And this case is illegal: we can't reuse another
@@ -1415,7 +1411,6 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
BUFFER_TRACE(bh, "entry");
jbd_lock_bh_state(bh);
- spin_lock(&journal->j_list_lock);
if (!buffer_jbd(bh))
goto not_jbd;
@@ -1468,6 +1463,7 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
* we know to remove the checkpoint after we commit.
*/
+ spin_lock(&journal->j_list_lock);
if (jh->b_cp_transaction) {
__jbd2_journal_temp_unlink_buffer(jh);
__jbd2_journal_file_buffer(jh, transaction, BJ_Forget);
@@ -1480,6 +1476,7 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
goto drop;
}
}
+ spin_unlock(&journal->j_list_lock);
} else if (jh->b_transaction) {
J_ASSERT_JH(jh, (jh->b_transaction ==
journal->j_committing_transaction));
@@ -1491,7 +1488,9 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
if (jh->b_next_transaction) {
J_ASSERT(jh->b_next_transaction == transaction);
+ spin_lock(&journal->j_list_lock);
jh->b_next_transaction = NULL;
+ spin_unlock(&journal->j_list_lock);
/*
* only drop a reference if this transaction modified
@@ -1503,7 +1502,6 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
}
not_jbd:
- spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh);
__brelse(bh);
drop:
@@ -1821,11 +1819,11 @@ __journal_try_to_free_buffer(journal_t *journal, struct buffer_head *bh)
if (buffer_locked(bh) || buffer_dirty(bh))
goto out;
- if (jh->b_next_transaction != NULL)
+ if (jh->b_next_transaction != NULL || jh->b_transaction != NULL)
goto out;
spin_lock(&journal->j_list_lock);
- if (jh->b_cp_transaction != NULL && jh->b_transaction == NULL) {
+ if (jh->b_cp_transaction != NULL) {
/* written-back checkpointed metadata buffer */
JBUFFER_TRACE(jh, "remove from checkpoint list");
__jbd2_journal_remove_checkpoint(jh);
diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c
index 16a5047903a6..406d9cc84ba8 100644
--- a/fs/jffs2/compr_rtime.c
+++ b/fs/jffs2/compr_rtime.c
@@ -33,7 +33,7 @@ static int jffs2_rtime_compress(unsigned char *data_in,
unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
{
- short positions[256];
+ unsigned short positions[256];
int outpos = 0;
int pos=0;
@@ -74,7 +74,7 @@ static int jffs2_rtime_decompress(unsigned char *data_in,
unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen)
{
- short positions[256];
+ unsigned short positions[256];
int outpos = 0;
int pos=0;
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index a69e426435dd..601afd1afddf 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -242,7 +242,7 @@ void jffs2_evict_inode (struct inode *inode)
jffs2_dbg(1, "%s(): ino #%lu mode %o\n",
__func__, inode->i_ino, inode->i_mode);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
jffs2_do_clear_inode(c, f);
}
@@ -457,12 +457,14 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r
The umask is only applied if there's no default ACL */
ret = jffs2_init_acl_pre(dir_i, inode, &mode);
if (ret) {
- make_bad_inode(inode);
- iput(inode);
- return ERR_PTR(ret);
+ mutex_unlock(&f->sem);
+ make_bad_inode(inode);
+ iput(inode);
+ return ERR_PTR(ret);
}
ret = jffs2_do_new_inode (c, f, mode, ri);
if (ret) {
+ mutex_unlock(&f->sem);
make_bad_inode(inode);
iput(inode);
return ERR_PTR(ret);
@@ -479,6 +481,7 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r
inode->i_size = 0;
if (insert_inode_locked(inode) < 0) {
+ mutex_unlock(&f->sem);
make_bad_inode(inode);
iput(inode);
return ERR_PTR(-EINVAL);
@@ -687,7 +690,7 @@ unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
struct inode *inode = OFNI_EDONI_2SFFJ(f);
struct page *pg;
- pg = read_cache_page_async(inode->i_mapping, offset >> PAGE_CACHE_SHIFT,
+ pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT,
(void *)jffs2_do_readpage_unlock, inode);
if (IS_ERR(pg))
return (void *)pg;
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
index e4619b00f7c5..fa35ff79ab35 100644
--- a/fs/jffs2/nodelist.h
+++ b/fs/jffs2/nodelist.h
@@ -231,7 +231,7 @@ struct jffs2_tmp_dnode_info
uint32_t version;
uint32_t data_crc;
uint32_t partial_crc;
- uint16_t csize;
+ uint32_t csize;
uint16_t overlapped;
};
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index 03310721712f..b6bd4affd9ad 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -179,6 +179,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
spin_unlock(&c->erase_completion_lock);
schedule();
+ remove_wait_queue(&c->erase_wait, &wait);
} else
spin_unlock(&c->erase_completion_lock);
} else if (ret)
@@ -211,20 +212,25 @@ out:
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
uint32_t *len, uint32_t sumsize)
{
- int ret = -EAGAIN;
+ int ret;
minsize = PAD(minsize);
jffs2_dbg(1, "%s(): Requested 0x%x bytes\n", __func__, minsize);
- spin_lock(&c->erase_completion_lock);
- while(ret == -EAGAIN) {
+ while (true) {
+ spin_lock(&c->erase_completion_lock);
ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
if (ret) {
jffs2_dbg(1, "%s(): looping, ret is %d\n",
__func__, ret);
}
+ spin_unlock(&c->erase_completion_lock);
+
+ if (ret == -EAGAIN)
+ cond_resched();
+ else
+ break;
}
- spin_unlock(&c->erase_completion_lock);
if (!ret)
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index 0defb1cc2a35..0918f0e2e266 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -243,6 +243,7 @@ static int jffs2_remount_fs(struct super_block *sb, int *flags, char *data)
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
int err;
+ sync_filesystem(sb);
err = jffs2_parse_options(c, data);
if (err)
return -EINVAL;
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index f4aab719add5..6f8fe72c2a7a 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -154,7 +154,7 @@ void jfs_evict_inode(struct inode *inode)
dquot_initialize(inode);
if (JFS_IP(inode)->fileset == FILESYSTEM_I) {
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
if (test_cflag(COMMIT_Freewmap, inode))
jfs_free_zero_link(inode);
@@ -168,7 +168,7 @@ void jfs_evict_inode(struct inode *inode)
dquot_free_inode(inode);
}
} else {
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
}
clear_inode(inode);
dquot_drop(inode);
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index e2b7483444fd..97f7fda51890 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -418,6 +418,7 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data)
int flag = JFS_SBI(sb)->flag;
int ret;
+ sync_filesystem(sb);
if (!parse_options(data, sb, &newLVSize, &flag)) {
return -EINVAL;
}
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 0bd05ab26003..78f3403300af 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -112,6 +112,7 @@ char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen)
spin_unlock_irqrestore(&kernfs_rename_lock, flags);
return p;
}
+EXPORT_SYMBOL_GPL(kernfs_path);
/**
* pr_cont_kernfs_name - pr_cont name of a kernfs_node
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index e55126f85bd2..abb0f1f53d93 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -355,7 +355,7 @@ void kernfs_evict_inode(struct inode *inode)
{
struct kernfs_node *kn = inode->i_private;
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
kernfs_put(kn);
}
diff --git a/fs/locks.c b/fs/locks.c
index 92a0f0a52b06..13fc7a6d380a 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -135,6 +135,7 @@
#define IS_POSIX(fl) (fl->fl_flags & FL_POSIX)
#define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK)
#define IS_LEASE(fl) (fl->fl_flags & (FL_LEASE|FL_DELEG))
+#define IS_FILE_PVT(fl) (fl->fl_flags & FL_FILE_PVT)
static bool lease_breaking(struct file_lock *fl)
{
@@ -344,48 +345,43 @@ static int assign_type(struct file_lock *fl, long type)
return 0;
}
-/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
- * style lock.
- */
-static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
- struct flock *l)
+static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
+ struct flock64 *l)
{
- off_t start, end;
-
switch (l->l_whence) {
case SEEK_SET:
- start = 0;
+ fl->fl_start = 0;
break;
case SEEK_CUR:
- start = filp->f_pos;
+ fl->fl_start = filp->f_pos;
break;
case SEEK_END:
- start = i_size_read(file_inode(filp));
+ fl->fl_start = i_size_read(file_inode(filp));
break;
default:
return -EINVAL;
}
+ if (l->l_start > OFFSET_MAX - fl->fl_start)
+ return -EOVERFLOW;
+ fl->fl_start += l->l_start;
+ if (fl->fl_start < 0)
+ return -EINVAL;
/* POSIX-1996 leaves the case l->l_len < 0 undefined;
POSIX-2001 defines it. */
- start += l->l_start;
- if (start < 0)
- return -EINVAL;
- fl->fl_end = OFFSET_MAX;
if (l->l_len > 0) {
- end = start + l->l_len - 1;
- fl->fl_end = end;
+ if (l->l_len - 1 > OFFSET_MAX - fl->fl_start)
+ return -EOVERFLOW;
+ fl->fl_end = fl->fl_start + l->l_len - 1;
+
} else if (l->l_len < 0) {
- end = start - 1;
- fl->fl_end = end;
- start += l->l_len;
- if (start < 0)
+ if (fl->fl_start + l->l_len < 0)
return -EINVAL;
- }
- fl->fl_start = start; /* we record the absolute position */
- if (fl->fl_end < fl->fl_start)
- return -EOVERFLOW;
-
+ fl->fl_end = fl->fl_start - 1;
+ fl->fl_start += l->l_len;
+ } else
+ fl->fl_end = OFFSET_MAX;
+
fl->fl_owner = current->files;
fl->fl_pid = current->tgid;
fl->fl_file = filp;
@@ -393,55 +389,36 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
fl->fl_ops = NULL;
fl->fl_lmops = NULL;
- return assign_type(fl, l->l_type);
-}
-
-#if BITS_PER_LONG == 32
-static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
- struct flock64 *l)
-{
- loff_t start;
-
- switch (l->l_whence) {
- case SEEK_SET:
- start = 0;
- break;
- case SEEK_CUR:
- start = filp->f_pos;
+ /* Ensure that fl->fl_filp has compatible f_mode */
+ switch (l->l_type) {
+ case F_RDLCK:
+ if (!(filp->f_mode & FMODE_READ))
+ return -EBADF;
break;
- case SEEK_END:
- start = i_size_read(file_inode(filp));
+ case F_WRLCK:
+ if (!(filp->f_mode & FMODE_WRITE))
+ return -EBADF;
break;
- default:
- return -EINVAL;
}
- start += l->l_start;
- if (start < 0)
- return -EINVAL;
- fl->fl_end = OFFSET_MAX;
- if (l->l_len > 0) {
- fl->fl_end = start + l->l_len - 1;
- } else if (l->l_len < 0) {
- fl->fl_end = start - 1;
- start += l->l_len;
- if (start < 0)
- return -EINVAL;
- }
- fl->fl_start = start; /* we record the absolute position */
- if (fl->fl_end < fl->fl_start)
- return -EOVERFLOW;
-
- fl->fl_owner = current->files;
- fl->fl_pid = current->tgid;
- fl->fl_file = filp;
- fl->fl_flags = FL_POSIX;
- fl->fl_ops = NULL;
- fl->fl_lmops = NULL;
-
return assign_type(fl, l->l_type);
}
-#endif
+
+/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
+ * style lock.
+ */
+static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
+ struct flock *l)
+{
+ struct flock64 ll = {
+ .l_type = l->l_type,
+ .l_whence = l->l_whence,
+ .l_start = l->l_start,
+ .l_len = l->l_len,
+ };
+
+ return flock64_to_posix_lock(filp, fl, &ll);
+}
/* default lease lock manager operations */
static void lease_break_callback(struct file_lock *fl)
@@ -511,8 +488,7 @@ static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2)
}
/* Must be called with the i_lock held! */
-static inline void
-locks_insert_global_locks(struct file_lock *fl)
+static void locks_insert_global_locks(struct file_lock *fl)
{
lg_local_lock(&file_lock_lglock);
fl->fl_link_cpu = smp_processor_id();
@@ -521,8 +497,7 @@ locks_insert_global_locks(struct file_lock *fl)
}
/* Must be called with the i_lock held! */
-static inline void
-locks_delete_global_locks(struct file_lock *fl)
+static void locks_delete_global_locks(struct file_lock *fl)
{
/*
* Avoid taking lock if already unhashed. This is safe since this check
@@ -544,14 +519,12 @@ posix_owner_key(struct file_lock *fl)
return (unsigned long)fl->fl_owner;
}
-static inline void
-locks_insert_global_blocked(struct file_lock *waiter)
+static void locks_insert_global_blocked(struct file_lock *waiter)
{
hash_add(blocked_hash, &waiter->fl_link, posix_owner_key(waiter));
}
-static inline void
-locks_delete_global_blocked(struct file_lock *waiter)
+static void locks_delete_global_blocked(struct file_lock *waiter)
{
hash_del(&waiter->fl_link);
}
@@ -581,7 +554,7 @@ static void locks_delete_block(struct file_lock *waiter)
* it seems like the reasonable thing to do.
*
* Must be called with both the i_lock and blocked_lock_lock held. The fl_block
- * list itself is protected by the file_lock_list, but by ensuring that the
+ * list itself is protected by the blocked_lock_lock, but by ensuring that the
* i_lock is also held on insertions we can avoid taking the blocked_lock_lock
* in some cases when we see that the fl_block list is empty.
*/
@@ -591,7 +564,7 @@ static void __locks_insert_block(struct file_lock *blocker,
BUG_ON(!list_empty(&waiter->fl_block));
waiter->fl_next = blocker;
list_add_tail(&waiter->fl_block, &blocker->fl_block);
- if (IS_POSIX(blocker))
+ if (IS_POSIX(blocker) && !IS_FILE_PVT(blocker))
locks_insert_global_blocked(waiter);
}
@@ -652,15 +625,18 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
locks_insert_global_locks(fl);
}
-/*
- * Delete a lock and then free it.
- * Wake up processes that are blocked waiting for this lock,
- * notify the FS that the lock has been cleared and
- * finally free the lock.
+/**
+ * locks_delete_lock - Delete a lock and then free it.
+ * @thisfl_p: pointer that points to the fl_next field of the previous
+ * inode->i_flock list entry
+ *
+ * Unlink a lock from all lists and free the namespace reference, but don't
+ * free it yet. Wake up processes that are blocked waiting for this lock and
+ * notify the FS that the lock has been cleared.
*
* Must be called with the i_lock held!
*/
-static void locks_delete_lock(struct file_lock **thisfl_p)
+static void locks_unlink_lock(struct file_lock **thisfl_p)
{
struct file_lock *fl = *thisfl_p;
@@ -675,6 +651,18 @@ static void locks_delete_lock(struct file_lock **thisfl_p)
}
locks_wake_up_blocks(fl);
+}
+
+/*
+ * Unlink a lock from all lists and free it.
+ *
+ * Must be called with i_lock held!
+ */
+static void locks_delete_lock(struct file_lock **thisfl_p)
+{
+ struct file_lock *fl = *thisfl_p;
+
+ locks_unlink_lock(thisfl_p);
locks_free_lock(fl);
}
@@ -769,8 +757,16 @@ EXPORT_SYMBOL(posix_test_lock);
* Note: the above assumption may not be true when handling lock
* requests from a broken NFS client. It may also fail in the presence
* of tasks (such as posix threads) sharing the same open file table.
- *
* To handle those cases, we just bail out after a few iterations.
+ *
+ * For FL_FILE_PVT locks, the owner is the filp, not the files_struct.
+ * Because the owner is not even nominally tied to a thread of
+ * execution, the deadlock detection below can't reasonably work well. Just
+ * skip it for those.
+ *
+ * In principle, we could do a more limited deadlock detection on FL_FILE_PVT
+ * locks that just checks for the case where two tasks are attempting to
+ * upgrade from read to write locks on the same inode.
*/
#define MAX_DEADLK_ITERATIONS 10
@@ -793,6 +789,13 @@ static int posix_locks_deadlock(struct file_lock *caller_fl,
{
int i = 0;
+ /*
+ * This deadlock detector can't reasonably detect deadlocks with
+ * FL_FILE_PVT locks, since they aren't owned by a process, per-se.
+ */
+ if (IS_FILE_PVT(caller_fl))
+ return 0;
+
while ((block_fl = what_owner_is_waiting_for(block_fl))) {
if (i++ > MAX_DEADLK_ITERATIONS)
return 0;
@@ -1152,13 +1155,14 @@ EXPORT_SYMBOL(posix_lock_file_wait);
/**
* locks_mandatory_locked - Check for an active lock
- * @inode: the file to check
+ * @file: the file to check
*
* Searches the inode's list of locks to find any POSIX locks which conflict.
* This function is called from locks_verify_locked() only.
*/
-int locks_mandatory_locked(struct inode *inode)
+int locks_mandatory_locked(struct file *file)
{
+ struct inode *inode = file_inode(file);
fl_owner_t owner = current->files;
struct file_lock *fl;
@@ -1169,7 +1173,7 @@ int locks_mandatory_locked(struct inode *inode)
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!IS_POSIX(fl))
continue;
- if (fl->fl_owner != owner)
+ if (fl->fl_owner != owner && fl->fl_owner != (fl_owner_t)file)
break;
}
spin_unlock(&inode->i_lock);
@@ -1195,19 +1199,30 @@ int locks_mandatory_area(int read_write, struct inode *inode,
{
struct file_lock fl;
int error;
+ bool sleep = false;
locks_init_lock(&fl);
- fl.fl_owner = current->files;
fl.fl_pid = current->tgid;
fl.fl_file = filp;
fl.fl_flags = FL_POSIX | FL_ACCESS;
if (filp && !(filp->f_flags & O_NONBLOCK))
- fl.fl_flags |= FL_SLEEP;
+ sleep = true;
fl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
fl.fl_start = offset;
fl.fl_end = offset + count - 1;
for (;;) {
+ if (filp) {
+ fl.fl_owner = (fl_owner_t)filp;
+ fl.fl_flags &= ~FL_SLEEP;
+ error = __posix_lock_file(inode, &fl, NULL);
+ if (!error)
+ break;
+ }
+
+ if (sleep)
+ fl.fl_flags |= FL_SLEEP;
+ fl.fl_owner = current->files;
error = __posix_lock_file(inode, &fl, NULL);
if (error != FILE_LOCK_DEFERRED)
break;
@@ -1472,6 +1487,32 @@ int fcntl_getlease(struct file *filp)
return type;
}
+/**
+ * check_conflicting_open - see if the given dentry points to a file that has
+ * an existing open that would conflict with the
+ * desired lease.
+ * @dentry: dentry to check
+ * @arg: type of lease that we're trying to acquire
+ *
+ * Check to see if there's an existing open fd on this file that would
+ * conflict with the lease we're trying to set.
+ */
+static int
+check_conflicting_open(const struct dentry *dentry, const long arg)
+{
+ int ret = 0;
+ struct inode *inode = dentry->d_inode;
+
+ if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
+ return -EAGAIN;
+
+ if ((arg == F_WRLCK) && ((d_count(dentry) > 1) ||
+ (atomic_read(&inode->i_count) > 1)))
+ ret = -EAGAIN;
+
+ return ret;
+}
+
static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
{
struct file_lock *fl, **before, **my_before = NULL, *lease;
@@ -1499,12 +1540,8 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
return -EINVAL;
}
- error = -EAGAIN;
- if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
- goto out;
- if ((arg == F_WRLCK)
- && ((d_count(dentry) > 1)
- || (atomic_read(&inode->i_count) > 1)))
+ error = check_conflicting_open(dentry, arg);
+ if (error)
goto out;
/*
@@ -1549,7 +1586,19 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
goto out;
locks_insert_lock(before, lease);
- error = 0;
+ /*
+ * The check in break_lease() is lockless. It's possible for another
+ * open to race in after we did the earlier check for a conflicting
+ * open but before the lease was inserted. Check again for a
+ * conflicting open and cancel the lease if there is one.
+ *
+ * We also add a barrier here to ensure that the insertion of the lock
+ * precedes these checks.
+ */
+ smp_mb();
+ error = check_conflicting_open(dentry, arg);
+ if (error)
+ locks_unlink_lock(flp);
out:
if (is_deleg)
mutex_unlock(&inode->i_mutex);
@@ -1842,7 +1891,7 @@ EXPORT_SYMBOL_GPL(vfs_test_lock);
static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl)
{
- flock->l_pid = fl->fl_pid;
+ flock->l_pid = IS_FILE_PVT(fl) ? -1 : fl->fl_pid;
#if BITS_PER_LONG == 32
/*
* Make sure we can represent the posix lock via
@@ -1864,7 +1913,7 @@ static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl)
#if BITS_PER_LONG == 32
static void posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl)
{
- flock->l_pid = fl->fl_pid;
+ flock->l_pid = IS_FILE_PVT(fl) ? -1 : fl->fl_pid;
flock->l_start = fl->fl_start;
flock->l_len = fl->fl_end == OFFSET_MAX ? 0 :
fl->fl_end - fl->fl_start + 1;
@@ -1876,7 +1925,7 @@ static void posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl)
/* Report the first existing lock that would conflict with l.
* This implements the F_GETLK command of fcntl().
*/
-int fcntl_getlk(struct file *filp, struct flock __user *l)
+int fcntl_getlk(struct file *filp, unsigned int cmd, struct flock __user *l)
{
struct file_lock file_lock;
struct flock flock;
@@ -1893,6 +1942,16 @@ int fcntl_getlk(struct file *filp, struct flock __user *l)
if (error)
goto out;
+ if (cmd == F_GETLKP) {
+ error = -EINVAL;
+ if (flock.l_pid != 0)
+ goto out;
+
+ cmd = F_GETLK;
+ file_lock.fl_flags |= FL_FILE_PVT;
+ file_lock.fl_owner = (fl_owner_t)filp;
+ }
+
error = vfs_test_lock(filp, &file_lock);
if (error)
goto out;
@@ -2012,25 +2071,32 @@ again:
error = flock_to_posix_lock(filp, file_lock, &flock);
if (error)
goto out;
- if (cmd == F_SETLKW) {
- file_lock->fl_flags |= FL_SLEEP;
- }
-
- error = -EBADF;
- switch (flock.l_type) {
- case F_RDLCK:
- if (!(filp->f_mode & FMODE_READ))
- goto out;
- break;
- case F_WRLCK:
- if (!(filp->f_mode & FMODE_WRITE))
+
+ /*
+ * If the cmd is requesting file-private locks, then set the
+ * FL_FILE_PVT flag and override the owner.
+ */
+ switch (cmd) {
+ case F_SETLKP:
+ error = -EINVAL;
+ if (flock.l_pid != 0)
goto out;
+
+ cmd = F_SETLK;
+ file_lock->fl_flags |= FL_FILE_PVT;
+ file_lock->fl_owner = (fl_owner_t)filp;
break;
- case F_UNLCK:
- break;
- default:
+ case F_SETLKPW:
error = -EINVAL;
- goto out;
+ if (flock.l_pid != 0)
+ goto out;
+
+ cmd = F_SETLKW;
+ file_lock->fl_flags |= FL_FILE_PVT;
+ file_lock->fl_owner = (fl_owner_t)filp;
+ /* Fallthrough */
+ case F_SETLKW:
+ file_lock->fl_flags |= FL_SLEEP;
}
error = do_lock_file_wait(filp, cmd, file_lock);
@@ -2061,7 +2127,7 @@ out:
/* Report the first existing lock that would conflict with l.
* This implements the F_GETLK command of fcntl().
*/
-int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
+int fcntl_getlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l)
{
struct file_lock file_lock;
struct flock64 flock;
@@ -2078,6 +2144,16 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
if (error)
goto out;
+ if (cmd == F_GETLKP) {
+ error = -EINVAL;
+ if (flock.l_pid != 0)
+ goto out;
+
+ cmd = F_GETLK64;
+ file_lock.fl_flags |= FL_FILE_PVT;
+ file_lock.fl_owner = (fl_owner_t)filp;
+ }
+
error = vfs_test_lock(filp, &file_lock);
if (error)
goto out;
@@ -2130,25 +2206,32 @@ again:
error = flock64_to_posix_lock(filp, file_lock, &flock);
if (error)
goto out;
- if (cmd == F_SETLKW64) {
- file_lock->fl_flags |= FL_SLEEP;
- }
-
- error = -EBADF;
- switch (flock.l_type) {
- case F_RDLCK:
- if (!(filp->f_mode & FMODE_READ))
- goto out;
- break;
- case F_WRLCK:
- if (!(filp->f_mode & FMODE_WRITE))
+
+ /*
+ * If the cmd is requesting file-private locks, then set the
+ * FL_FILE_PVT flag and override the owner.
+ */
+ switch (cmd) {
+ case F_SETLKP:
+ error = -EINVAL;
+ if (flock.l_pid != 0)
goto out;
+
+ cmd = F_SETLK64;
+ file_lock->fl_flags |= FL_FILE_PVT;
+ file_lock->fl_owner = (fl_owner_t)filp;
break;
- case F_UNLCK:
- break;
- default:
+ case F_SETLKPW:
error = -EINVAL;
- goto out;
+ if (flock.l_pid != 0)
+ goto out;
+
+ cmd = F_SETLKW64;
+ file_lock->fl_flags |= FL_FILE_PVT;
+ file_lock->fl_owner = (fl_owner_t)filp;
+ /* Fallthrough */
+ case F_SETLKW64:
+ file_lock->fl_flags |= FL_SLEEP;
}
error = do_lock_file_wait(filp, cmd, file_lock);
@@ -2209,7 +2292,7 @@ EXPORT_SYMBOL(locks_remove_posix);
/*
* This function is called on the last close of an open file.
*/
-void locks_remove_flock(struct file *filp)
+void locks_remove_file(struct file *filp)
{
struct inode * inode = file_inode(filp);
struct file_lock *fl;
@@ -2218,6 +2301,8 @@ void locks_remove_flock(struct file *filp)
if (!inode->i_flock)
return;
+ locks_remove_posix(filp, (fl_owner_t)filp);
+
if (filp->f_op->flock) {
struct file_lock fl = {
.fl_pid = current->tgid,
@@ -2236,16 +2321,28 @@ void locks_remove_flock(struct file *filp)
while ((fl = *before) != NULL) {
if (fl->fl_file == filp) {
- if (IS_FLOCK(fl)) {
- locks_delete_lock(before);
- continue;
- }
if (IS_LEASE(fl)) {
lease_modify(before, F_UNLCK);
continue;
}
- /* What? */
- BUG();
+
+ /*
+ * There's a leftover lock on the list of a type that
+ * we didn't expect to see. Most likely a classic
+ * POSIX lock that ended up not getting released
+ * properly, or that raced onto the list somehow. Log
+ * some info about it and then just remove it from
+ * the list.
+ */
+ WARN(!IS_FLOCK(fl),
+ "leftover lock: dev=%u:%u ino=%lu type=%hhd flags=0x%x start=%lld end=%lld\n",
+ MAJOR(inode->i_sb->s_dev),
+ MINOR(inode->i_sb->s_dev), inode->i_ino,
+ fl->fl_type, fl->fl_flags,
+ fl->fl_start, fl->fl_end);
+
+ locks_delete_lock(before);
+ continue;
}
before = &fl->fl_next;
}
@@ -2314,8 +2411,14 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
seq_printf(f, "%lld:%s ", id, pfx);
if (IS_POSIX(fl)) {
- seq_printf(f, "%6s %s ",
- (fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
+ if (fl->fl_flags & FL_ACCESS)
+ seq_printf(f, "ACCESS");
+ else if (IS_FILE_PVT(fl))
+ seq_printf(f, "FLPVT ");
+ else
+ seq_printf(f, "POSIX ");
+
+ seq_printf(f, " %s ",
(inode == NULL) ? "*NOINODE*" :
mandatory_lock(inode) ? "MANDATORY" : "ADVISORY ");
} else if (IS_FLOCK(fl)) {
@@ -2385,6 +2488,7 @@ static int locks_show(struct seq_file *f, void *v)
}
static void *locks_start(struct seq_file *f, loff_t *pos)
+ __acquires(&blocked_lock_lock)
{
struct locks_iterator *iter = f->private;
@@ -2403,6 +2507,7 @@ static void *locks_next(struct seq_file *f, void *v, loff_t *pos)
}
static void locks_stop(struct seq_file *f, void *v)
+ __releases(&blocked_lock_lock)
{
spin_unlock(&blocked_lock_lock);
lg_global_unlock(&file_lock_lglock);
diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c
index 9a59cbade2fb..48140315f627 100644
--- a/fs/logfs/readwrite.c
+++ b/fs/logfs/readwrite.c
@@ -2180,7 +2180,7 @@ void logfs_evict_inode(struct inode *inode)
do_delete_inode(inode);
}
}
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
/* Cheaper version of write_inode. All changes are concealed in
diff --git a/fs/mbcache.c b/fs/mbcache.c
index e519e45bf673..bf166e388f0d 100644
--- a/fs/mbcache.c
+++ b/fs/mbcache.c
@@ -26,6 +26,41 @@
* back on the lru list.
*/
+/*
+ * Lock descriptions and usage:
+ *
+ * Each hash chain of both the block and index hash tables now contains
+ * a built-in lock used to serialize accesses to the hash chain.
+ *
+ * Accesses to global data structures mb_cache_list and mb_cache_lru_list
+ * are serialized via the global spinlock mb_cache_spinlock.
+ *
+ * Each mb_cache_entry contains a spinlock, e_entry_lock, to serialize
+ * accesses to its local data, such as e_used and e_queued.
+ *
+ * Lock ordering:
+ *
+ * Each block hash chain's lock has the highest lock order, followed by an
+ * index hash chain's lock, mb_cache_bg_lock (used to implement mb_cache_entry's
+ * lock), and mb_cach_spinlock, with the lowest order. While holding
+ * either a block or index hash chain lock, a thread can acquire an
+ * mc_cache_bg_lock, which in turn can also acquire mb_cache_spinlock.
+ *
+ * Synchronization:
+ *
+ * Since both mb_cache_entry_get and mb_cache_entry_find scan the block and
+ * index hash chian, it needs to lock the corresponding hash chain. For each
+ * mb_cache_entry within the chain, it needs to lock the mb_cache_entry to
+ * prevent either any simultaneous release or free on the entry and also
+ * to serialize accesses to either the e_used or e_queued member of the entry.
+ *
+ * To avoid having a dangling reference to an already freed
+ * mb_cache_entry, an mb_cache_entry is only freed when it is not on a
+ * block hash chain and also no longer being referenced, both e_used,
+ * and e_queued are 0's. When an mb_cache_entry is explicitly freed it is
+ * first removed from a block hash chain.
+ */
+
#include <linux/kernel.h>
#include <linux/module.h>
@@ -34,9 +69,10 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/sched.h>
-#include <linux/init.h>
+#include <linux/list_bl.h>
#include <linux/mbcache.h>
-
+#include <linux/init.h>
+#include <linux/blockgroup_lock.h>
#ifdef MB_CACHE_DEBUG
# define mb_debug(f...) do { \
@@ -57,8 +93,14 @@
#define MB_CACHE_WRITER ((unsigned short)~0U >> 1)
+#define MB_CACHE_ENTRY_LOCK_BITS __builtin_log2(NR_BG_LOCKS)
+#define MB_CACHE_ENTRY_LOCK_INDEX(ce) \
+ (hash_long((unsigned long)ce, MB_CACHE_ENTRY_LOCK_BITS))
+
static DECLARE_WAIT_QUEUE_HEAD(mb_cache_queue);
-
+static struct blockgroup_lock *mb_cache_bg_lock;
+static struct kmem_cache *mb_cache_kmem_cache;
+
MODULE_AUTHOR("Andreas Gruenbacher <a.gruenbacher@computer.org>");
MODULE_DESCRIPTION("Meta block cache (for extended attributes)");
MODULE_LICENSE("GPL");
@@ -86,58 +128,110 @@ static LIST_HEAD(mb_cache_list);
static LIST_HEAD(mb_cache_lru_list);
static DEFINE_SPINLOCK(mb_cache_spinlock);
+static inline void
+__spin_lock_mb_cache_entry(struct mb_cache_entry *ce)
+{
+ spin_lock(bgl_lock_ptr(mb_cache_bg_lock,
+ MB_CACHE_ENTRY_LOCK_INDEX(ce)));
+}
+
+static inline void
+__spin_unlock_mb_cache_entry(struct mb_cache_entry *ce)
+{
+ spin_unlock(bgl_lock_ptr(mb_cache_bg_lock,
+ MB_CACHE_ENTRY_LOCK_INDEX(ce)));
+}
+
static inline int
-__mb_cache_entry_is_hashed(struct mb_cache_entry *ce)
+__mb_cache_entry_is_block_hashed(struct mb_cache_entry *ce)
{
- return !list_empty(&ce->e_block_list);
+ return !hlist_bl_unhashed(&ce->e_block_list);
}
-static void
-__mb_cache_entry_unhash(struct mb_cache_entry *ce)
+static inline void
+__mb_cache_entry_unhash_block(struct mb_cache_entry *ce)
{
- if (__mb_cache_entry_is_hashed(ce)) {
- list_del_init(&ce->e_block_list);
- list_del(&ce->e_index.o_list);
- }
+ if (__mb_cache_entry_is_block_hashed(ce))
+ hlist_bl_del_init(&ce->e_block_list);
}
+static inline int
+__mb_cache_entry_is_index_hashed(struct mb_cache_entry *ce)
+{
+ return !hlist_bl_unhashed(&ce->e_index.o_list);
+}
+
+static inline void
+__mb_cache_entry_unhash_index(struct mb_cache_entry *ce)
+{
+ if (__mb_cache_entry_is_index_hashed(ce))
+ hlist_bl_del_init(&ce->e_index.o_list);
+}
+
+/*
+ * __mb_cache_entry_unhash_unlock()
+ *
+ * This function is called to unhash both the block and index hash
+ * chain.
+ * It assumes both the block and index hash chain is locked upon entry.
+ * It also unlock both hash chains both exit
+ */
+static inline void
+__mb_cache_entry_unhash_unlock(struct mb_cache_entry *ce)
+{
+ __mb_cache_entry_unhash_index(ce);
+ hlist_bl_unlock(ce->e_index_hash_p);
+ __mb_cache_entry_unhash_block(ce);
+ hlist_bl_unlock(ce->e_block_hash_p);
+}
static void
__mb_cache_entry_forget(struct mb_cache_entry *ce, gfp_t gfp_mask)
{
struct mb_cache *cache = ce->e_cache;
- mb_assert(!(ce->e_used || ce->e_queued));
+ mb_assert(!(ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt)));
kmem_cache_free(cache->c_entry_cache, ce);
atomic_dec(&cache->c_entry_count);
}
-
static void
-__mb_cache_entry_release_unlock(struct mb_cache_entry *ce)
- __releases(mb_cache_spinlock)
+__mb_cache_entry_release(struct mb_cache_entry *ce)
{
+ /* First lock the entry to serialize access to its local data. */
+ __spin_lock_mb_cache_entry(ce);
/* Wake up all processes queuing for this cache entry. */
if (ce->e_queued)
wake_up_all(&mb_cache_queue);
if (ce->e_used >= MB_CACHE_WRITER)
ce->e_used -= MB_CACHE_WRITER;
+ /*
+ * Make sure that all cache entries on lru_list have
+ * both e_used and e_qued of 0s.
+ */
ce->e_used--;
- if (!(ce->e_used || ce->e_queued)) {
- if (!__mb_cache_entry_is_hashed(ce))
+ if (!(ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt))) {
+ if (!__mb_cache_entry_is_block_hashed(ce)) {
+ __spin_unlock_mb_cache_entry(ce);
goto forget;
- mb_assert(list_empty(&ce->e_lru_list));
- list_add_tail(&ce->e_lru_list, &mb_cache_lru_list);
+ }
+ /*
+ * Need access to lru list, first drop entry lock,
+ * then reacquire the lock in the proper order.
+ */
+ spin_lock(&mb_cache_spinlock);
+ if (list_empty(&ce->e_lru_list))
+ list_add_tail(&ce->e_lru_list, &mb_cache_lru_list);
+ spin_unlock(&mb_cache_spinlock);
}
- spin_unlock(&mb_cache_spinlock);
+ __spin_unlock_mb_cache_entry(ce);
return;
forget:
- spin_unlock(&mb_cache_spinlock);
+ mb_assert(list_empty(&ce->e_lru_list));
__mb_cache_entry_forget(ce, GFP_KERNEL);
}
-
/*
* mb_cache_shrink_scan() memory pressure callback
*
@@ -160,17 +254,34 @@ mb_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
mb_debug("trying to free %d entries", nr_to_scan);
spin_lock(&mb_cache_spinlock);
- while (nr_to_scan-- && !list_empty(&mb_cache_lru_list)) {
+ while ((nr_to_scan-- > 0) && !list_empty(&mb_cache_lru_list)) {
struct mb_cache_entry *ce =
list_entry(mb_cache_lru_list.next,
- struct mb_cache_entry, e_lru_list);
- list_move_tail(&ce->e_lru_list, &free_list);
- __mb_cache_entry_unhash(ce);
- freed++;
+ struct mb_cache_entry, e_lru_list);
+ list_del_init(&ce->e_lru_list);
+ if (ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt))
+ continue;
+ spin_unlock(&mb_cache_spinlock);
+ /* Prevent any find or get operation on the entry */
+ hlist_bl_lock(ce->e_block_hash_p);
+ hlist_bl_lock(ce->e_index_hash_p);
+ /* Ignore if it is touched by a find/get */
+ if (ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt) ||
+ !list_empty(&ce->e_lru_list)) {
+ hlist_bl_unlock(ce->e_index_hash_p);
+ hlist_bl_unlock(ce->e_block_hash_p);
+ spin_lock(&mb_cache_spinlock);
+ continue;
+ }
+ __mb_cache_entry_unhash_unlock(ce);
+ list_add_tail(&ce->e_lru_list, &free_list);
+ spin_lock(&mb_cache_spinlock);
}
spin_unlock(&mb_cache_spinlock);
+
list_for_each_entry_safe(entry, tmp, &free_list, e_lru_list) {
__mb_cache_entry_forget(entry, gfp_mask);
+ freed++;
}
return freed;
}
@@ -215,29 +326,40 @@ mb_cache_create(const char *name, int bucket_bits)
int n, bucket_count = 1 << bucket_bits;
struct mb_cache *cache = NULL;
+ if (!mb_cache_bg_lock) {
+ mb_cache_bg_lock = kmalloc(sizeof(struct blockgroup_lock),
+ GFP_KERNEL);
+ if (!mb_cache_bg_lock)
+ return NULL;
+ bgl_lock_init(mb_cache_bg_lock);
+ }
+
cache = kmalloc(sizeof(struct mb_cache), GFP_KERNEL);
if (!cache)
return NULL;
cache->c_name = name;
atomic_set(&cache->c_entry_count, 0);
cache->c_bucket_bits = bucket_bits;
- cache->c_block_hash = kmalloc(bucket_count * sizeof(struct list_head),
- GFP_KERNEL);
+ cache->c_block_hash = kmalloc(bucket_count *
+ sizeof(struct hlist_bl_head), GFP_KERNEL);
if (!cache->c_block_hash)
goto fail;
for (n=0; n<bucket_count; n++)
- INIT_LIST_HEAD(&cache->c_block_hash[n]);
- cache->c_index_hash = kmalloc(bucket_count * sizeof(struct list_head),
- GFP_KERNEL);
+ INIT_HLIST_BL_HEAD(&cache->c_block_hash[n]);
+ cache->c_index_hash = kmalloc(bucket_count *
+ sizeof(struct hlist_bl_head), GFP_KERNEL);
if (!cache->c_index_hash)
goto fail;
for (n=0; n<bucket_count; n++)
- INIT_LIST_HEAD(&cache->c_index_hash[n]);
- cache->c_entry_cache = kmem_cache_create(name,
- sizeof(struct mb_cache_entry), 0,
- SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL);
- if (!cache->c_entry_cache)
- goto fail2;
+ INIT_HLIST_BL_HEAD(&cache->c_index_hash[n]);
+ if (!mb_cache_kmem_cache) {
+ mb_cache_kmem_cache = kmem_cache_create(name,
+ sizeof(struct mb_cache_entry), 0,
+ SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL);
+ if (!mb_cache_kmem_cache)
+ goto fail2;
+ }
+ cache->c_entry_cache = mb_cache_kmem_cache;
/*
* Set an upper limit on the number of cache entries so that the hash
@@ -273,21 +395,47 @@ void
mb_cache_shrink(struct block_device *bdev)
{
LIST_HEAD(free_list);
- struct list_head *l, *ltmp;
+ struct list_head *l;
+ struct mb_cache_entry *ce, *tmp;
+ l = &mb_cache_lru_list;
spin_lock(&mb_cache_spinlock);
- list_for_each_safe(l, ltmp, &mb_cache_lru_list) {
- struct mb_cache_entry *ce =
- list_entry(l, struct mb_cache_entry, e_lru_list);
+ while (!list_is_last(l, &mb_cache_lru_list)) {
+ l = l->next;
+ ce = list_entry(l, struct mb_cache_entry, e_lru_list);
if (ce->e_bdev == bdev) {
- list_move_tail(&ce->e_lru_list, &free_list);
- __mb_cache_entry_unhash(ce);
+ list_del_init(&ce->e_lru_list);
+ if (ce->e_used || ce->e_queued ||
+ atomic_read(&ce->e_refcnt))
+ continue;
+ spin_unlock(&mb_cache_spinlock);
+ /*
+ * Prevent any find or get operation on the entry.
+ */
+ hlist_bl_lock(ce->e_block_hash_p);
+ hlist_bl_lock(ce->e_index_hash_p);
+ /* Ignore if it is touched by a find/get */
+ if (ce->e_used || ce->e_queued ||
+ atomic_read(&ce->e_refcnt) ||
+ !list_empty(&ce->e_lru_list)) {
+ hlist_bl_unlock(ce->e_index_hash_p);
+ hlist_bl_unlock(ce->e_block_hash_p);
+ l = &mb_cache_lru_list;
+ spin_lock(&mb_cache_spinlock);
+ continue;
+ }
+ __mb_cache_entry_unhash_unlock(ce);
+ mb_assert(!(ce->e_used || ce->e_queued ||
+ atomic_read(&ce->e_refcnt)));
+ list_add_tail(&ce->e_lru_list, &free_list);
+ l = &mb_cache_lru_list;
+ spin_lock(&mb_cache_spinlock);
}
}
spin_unlock(&mb_cache_spinlock);
- list_for_each_safe(l, ltmp, &free_list) {
- __mb_cache_entry_forget(list_entry(l, struct mb_cache_entry,
- e_lru_list), GFP_KERNEL);
+
+ list_for_each_entry_safe(ce, tmp, &free_list, e_lru_list) {
+ __mb_cache_entry_forget(ce, GFP_KERNEL);
}
}
@@ -303,23 +451,27 @@ void
mb_cache_destroy(struct mb_cache *cache)
{
LIST_HEAD(free_list);
- struct list_head *l, *ltmp;
+ struct mb_cache_entry *ce, *tmp;
spin_lock(&mb_cache_spinlock);
- list_for_each_safe(l, ltmp, &mb_cache_lru_list) {
- struct mb_cache_entry *ce =
- list_entry(l, struct mb_cache_entry, e_lru_list);
- if (ce->e_cache == cache) {
+ list_for_each_entry_safe(ce, tmp, &mb_cache_lru_list, e_lru_list) {
+ if (ce->e_cache == cache)
list_move_tail(&ce->e_lru_list, &free_list);
- __mb_cache_entry_unhash(ce);
- }
}
list_del(&cache->c_cache_list);
spin_unlock(&mb_cache_spinlock);
- list_for_each_safe(l, ltmp, &free_list) {
- __mb_cache_entry_forget(list_entry(l, struct mb_cache_entry,
- e_lru_list), GFP_KERNEL);
+ list_for_each_entry_safe(ce, tmp, &free_list, e_lru_list) {
+ list_del_init(&ce->e_lru_list);
+ /*
+ * Prevent any find or get operation on the entry.
+ */
+ hlist_bl_lock(ce->e_block_hash_p);
+ hlist_bl_lock(ce->e_index_hash_p);
+ mb_assert(!(ce->e_used || ce->e_queued ||
+ atomic_read(&ce->e_refcnt)));
+ __mb_cache_entry_unhash_unlock(ce);
+ __mb_cache_entry_forget(ce, GFP_KERNEL);
}
if (atomic_read(&cache->c_entry_count) > 0) {
@@ -328,8 +480,10 @@ mb_cache_destroy(struct mb_cache *cache)
atomic_read(&cache->c_entry_count));
}
- kmem_cache_destroy(cache->c_entry_cache);
-
+ if (list_empty(&mb_cache_list)) {
+ kmem_cache_destroy(mb_cache_kmem_cache);
+ mb_cache_kmem_cache = NULL;
+ }
kfree(cache->c_index_hash);
kfree(cache->c_block_hash);
kfree(cache);
@@ -346,28 +500,61 @@ mb_cache_destroy(struct mb_cache *cache)
struct mb_cache_entry *
mb_cache_entry_alloc(struct mb_cache *cache, gfp_t gfp_flags)
{
- struct mb_cache_entry *ce = NULL;
+ struct mb_cache_entry *ce;
if (atomic_read(&cache->c_entry_count) >= cache->c_max_entries) {
+ struct list_head *l;
+
+ l = &mb_cache_lru_list;
spin_lock(&mb_cache_spinlock);
- if (!list_empty(&mb_cache_lru_list)) {
- ce = list_entry(mb_cache_lru_list.next,
- struct mb_cache_entry, e_lru_list);
- list_del_init(&ce->e_lru_list);
- __mb_cache_entry_unhash(ce);
+ while (!list_is_last(l, &mb_cache_lru_list)) {
+ l = l->next;
+ ce = list_entry(l, struct mb_cache_entry, e_lru_list);
+ if (ce->e_cache == cache) {
+ list_del_init(&ce->e_lru_list);
+ if (ce->e_used || ce->e_queued ||
+ atomic_read(&ce->e_refcnt))
+ continue;
+ spin_unlock(&mb_cache_spinlock);
+ /*
+ * Prevent any find or get operation on the
+ * entry.
+ */
+ hlist_bl_lock(ce->e_block_hash_p);
+ hlist_bl_lock(ce->e_index_hash_p);
+ /* Ignore if it is touched by a find/get */
+ if (ce->e_used || ce->e_queued ||
+ atomic_read(&ce->e_refcnt) ||
+ !list_empty(&ce->e_lru_list)) {
+ hlist_bl_unlock(ce->e_index_hash_p);
+ hlist_bl_unlock(ce->e_block_hash_p);
+ l = &mb_cache_lru_list;
+ spin_lock(&mb_cache_spinlock);
+ continue;
+ }
+ mb_assert(list_empty(&ce->e_lru_list));
+ mb_assert(!(ce->e_used || ce->e_queued ||
+ atomic_read(&ce->e_refcnt)));
+ __mb_cache_entry_unhash_unlock(ce);
+ goto found;
+ }
}
spin_unlock(&mb_cache_spinlock);
}
- if (!ce) {
- ce = kmem_cache_alloc(cache->c_entry_cache, gfp_flags);
- if (!ce)
- return NULL;
- atomic_inc(&cache->c_entry_count);
- INIT_LIST_HEAD(&ce->e_lru_list);
- INIT_LIST_HEAD(&ce->e_block_list);
- ce->e_cache = cache;
- ce->e_queued = 0;
- }
+
+ ce = kmem_cache_alloc(cache->c_entry_cache, gfp_flags);
+ if (!ce)
+ return NULL;
+ atomic_inc(&cache->c_entry_count);
+ INIT_LIST_HEAD(&ce->e_lru_list);
+ INIT_HLIST_BL_NODE(&ce->e_block_list);
+ INIT_HLIST_BL_NODE(&ce->e_index.o_list);
+ ce->e_cache = cache;
+ ce->e_queued = 0;
+ atomic_set(&ce->e_refcnt, 0);
+found:
+ ce->e_block_hash_p = &cache->c_block_hash[0];
+ ce->e_index_hash_p = &cache->c_index_hash[0];
ce->e_used = 1 + MB_CACHE_WRITER;
return ce;
}
@@ -393,29 +580,38 @@ mb_cache_entry_insert(struct mb_cache_entry *ce, struct block_device *bdev,
{
struct mb_cache *cache = ce->e_cache;
unsigned int bucket;
- struct list_head *l;
- int error = -EBUSY;
+ struct hlist_bl_node *l;
+ struct hlist_bl_head *block_hash_p;
+ struct hlist_bl_head *index_hash_p;
+ struct mb_cache_entry *lce;
+ mb_assert(ce);
bucket = hash_long((unsigned long)bdev + (block & 0xffffffff),
cache->c_bucket_bits);
- spin_lock(&mb_cache_spinlock);
- list_for_each_prev(l, &cache->c_block_hash[bucket]) {
- struct mb_cache_entry *ce =
- list_entry(l, struct mb_cache_entry, e_block_list);
- if (ce->e_bdev == bdev && ce->e_block == block)
- goto out;
+ block_hash_p = &cache->c_block_hash[bucket];
+ hlist_bl_lock(block_hash_p);
+ hlist_bl_for_each_entry(lce, l, block_hash_p, e_block_list) {
+ if (lce->e_bdev == bdev && lce->e_block == block) {
+ hlist_bl_unlock(block_hash_p);
+ return -EBUSY;
+ }
}
- __mb_cache_entry_unhash(ce);
+ mb_assert(!__mb_cache_entry_is_block_hashed(ce));
+ __mb_cache_entry_unhash_block(ce);
+ __mb_cache_entry_unhash_index(ce);
ce->e_bdev = bdev;
ce->e_block = block;
- list_add(&ce->e_block_list, &cache->c_block_hash[bucket]);
+ ce->e_block_hash_p = block_hash_p;
ce->e_index.o_key = key;
+ hlist_bl_add_head(&ce->e_block_list, block_hash_p);
+ hlist_bl_unlock(block_hash_p);
bucket = hash_long(key, cache->c_bucket_bits);
- list_add(&ce->e_index.o_list, &cache->c_index_hash[bucket]);
- error = 0;
-out:
- spin_unlock(&mb_cache_spinlock);
- return error;
+ index_hash_p = &cache->c_index_hash[bucket];
+ hlist_bl_lock(index_hash_p);
+ ce->e_index_hash_p = index_hash_p;
+ hlist_bl_add_head(&ce->e_index.o_list, index_hash_p);
+ hlist_bl_unlock(index_hash_p);
+ return 0;
}
@@ -429,24 +625,26 @@ out:
void
mb_cache_entry_release(struct mb_cache_entry *ce)
{
- spin_lock(&mb_cache_spinlock);
- __mb_cache_entry_release_unlock(ce);
+ __mb_cache_entry_release(ce);
}
/*
* mb_cache_entry_free()
*
- * This is equivalent to the sequence mb_cache_entry_takeout() --
- * mb_cache_entry_release().
*/
void
mb_cache_entry_free(struct mb_cache_entry *ce)
{
- spin_lock(&mb_cache_spinlock);
+ mb_assert(ce);
mb_assert(list_empty(&ce->e_lru_list));
- __mb_cache_entry_unhash(ce);
- __mb_cache_entry_release_unlock(ce);
+ hlist_bl_lock(ce->e_index_hash_p);
+ __mb_cache_entry_unhash_index(ce);
+ hlist_bl_unlock(ce->e_index_hash_p);
+ hlist_bl_lock(ce->e_block_hash_p);
+ __mb_cache_entry_unhash_block(ce);
+ hlist_bl_unlock(ce->e_block_hash_p);
+ __mb_cache_entry_release(ce);
}
@@ -463,84 +661,110 @@ mb_cache_entry_get(struct mb_cache *cache, struct block_device *bdev,
sector_t block)
{
unsigned int bucket;
- struct list_head *l;
+ struct hlist_bl_node *l;
struct mb_cache_entry *ce;
+ struct hlist_bl_head *block_hash_p;
bucket = hash_long((unsigned long)bdev + (block & 0xffffffff),
cache->c_bucket_bits);
- spin_lock(&mb_cache_spinlock);
- list_for_each(l, &cache->c_block_hash[bucket]) {
- ce = list_entry(l, struct mb_cache_entry, e_block_list);
+ block_hash_p = &cache->c_block_hash[bucket];
+ /* First serialize access to the block corresponding hash chain. */
+ hlist_bl_lock(block_hash_p);
+ hlist_bl_for_each_entry(ce, l, block_hash_p, e_block_list) {
+ mb_assert(ce->e_block_hash_p == block_hash_p);
if (ce->e_bdev == bdev && ce->e_block == block) {
- DEFINE_WAIT(wait);
+ /*
+ * Prevent a free from removing the entry.
+ */
+ atomic_inc(&ce->e_refcnt);
+ hlist_bl_unlock(block_hash_p);
+ __spin_lock_mb_cache_entry(ce);
+ atomic_dec(&ce->e_refcnt);
+ if (ce->e_used > 0) {
+ DEFINE_WAIT(wait);
+ while (ce->e_used > 0) {
+ ce->e_queued++;
+ prepare_to_wait(&mb_cache_queue, &wait,
+ TASK_UNINTERRUPTIBLE);
+ __spin_unlock_mb_cache_entry(ce);
+ schedule();
+ __spin_lock_mb_cache_entry(ce);
+ ce->e_queued--;
+ }
+ finish_wait(&mb_cache_queue, &wait);
+ }
+ ce->e_used += 1 + MB_CACHE_WRITER;
+ __spin_unlock_mb_cache_entry(ce);
- if (!list_empty(&ce->e_lru_list))
+ if (!list_empty(&ce->e_lru_list)) {
+ spin_lock(&mb_cache_spinlock);
list_del_init(&ce->e_lru_list);
-
- while (ce->e_used > 0) {
- ce->e_queued++;
- prepare_to_wait(&mb_cache_queue, &wait,
- TASK_UNINTERRUPTIBLE);
spin_unlock(&mb_cache_spinlock);
- schedule();
- spin_lock(&mb_cache_spinlock);
- ce->e_queued--;
}
- finish_wait(&mb_cache_queue, &wait);
- ce->e_used += 1 + MB_CACHE_WRITER;
-
- if (!__mb_cache_entry_is_hashed(ce)) {
- __mb_cache_entry_release_unlock(ce);
+ if (!__mb_cache_entry_is_block_hashed(ce)) {
+ __mb_cache_entry_release(ce);
return NULL;
}
- goto cleanup;
+ return ce;
}
}
- ce = NULL;
-
-cleanup:
- spin_unlock(&mb_cache_spinlock);
- return ce;
+ hlist_bl_unlock(block_hash_p);
+ return NULL;
}
#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0)
static struct mb_cache_entry *
-__mb_cache_entry_find(struct list_head *l, struct list_head *head,
+__mb_cache_entry_find(struct hlist_bl_node *l, struct hlist_bl_head *head,
struct block_device *bdev, unsigned int key)
{
- while (l != head) {
+
+ /* The index hash chain is alredy acquire by caller. */
+ while (l != NULL) {
struct mb_cache_entry *ce =
- list_entry(l, struct mb_cache_entry, e_index.o_list);
+ hlist_bl_entry(l, struct mb_cache_entry,
+ e_index.o_list);
+ mb_assert(ce->e_index_hash_p == head);
if (ce->e_bdev == bdev && ce->e_index.o_key == key) {
- DEFINE_WAIT(wait);
-
- if (!list_empty(&ce->e_lru_list))
- list_del_init(&ce->e_lru_list);
-
+ /*
+ * Prevent a free from removing the entry.
+ */
+ atomic_inc(&ce->e_refcnt);
+ hlist_bl_unlock(head);
+ __spin_lock_mb_cache_entry(ce);
+ atomic_dec(&ce->e_refcnt);
+ ce->e_used++;
/* Incrementing before holding the lock gives readers
priority over writers. */
- ce->e_used++;
- while (ce->e_used >= MB_CACHE_WRITER) {
- ce->e_queued++;
- prepare_to_wait(&mb_cache_queue, &wait,
- TASK_UNINTERRUPTIBLE);
- spin_unlock(&mb_cache_spinlock);
- schedule();
- spin_lock(&mb_cache_spinlock);
- ce->e_queued--;
+ if (ce->e_used >= MB_CACHE_WRITER) {
+ DEFINE_WAIT(wait);
+
+ while (ce->e_used >= MB_CACHE_WRITER) {
+ ce->e_queued++;
+ prepare_to_wait(&mb_cache_queue, &wait,
+ TASK_UNINTERRUPTIBLE);
+ __spin_unlock_mb_cache_entry(ce);
+ schedule();
+ __spin_lock_mb_cache_entry(ce);
+ ce->e_queued--;
+ }
+ finish_wait(&mb_cache_queue, &wait);
}
- finish_wait(&mb_cache_queue, &wait);
-
- if (!__mb_cache_entry_is_hashed(ce)) {
- __mb_cache_entry_release_unlock(ce);
+ __spin_unlock_mb_cache_entry(ce);
+ if (!list_empty(&ce->e_lru_list)) {
spin_lock(&mb_cache_spinlock);
+ list_del_init(&ce->e_lru_list);
+ spin_unlock(&mb_cache_spinlock);
+ }
+ if (!__mb_cache_entry_is_block_hashed(ce)) {
+ __mb_cache_entry_release(ce);
return ERR_PTR(-EAGAIN);
}
return ce;
}
l = l->next;
}
+ hlist_bl_unlock(head);
return NULL;
}
@@ -562,13 +786,17 @@ mb_cache_entry_find_first(struct mb_cache *cache, struct block_device *bdev,
unsigned int key)
{
unsigned int bucket = hash_long(key, cache->c_bucket_bits);
- struct list_head *l;
- struct mb_cache_entry *ce;
-
- spin_lock(&mb_cache_spinlock);
- l = cache->c_index_hash[bucket].next;
- ce = __mb_cache_entry_find(l, &cache->c_index_hash[bucket], bdev, key);
- spin_unlock(&mb_cache_spinlock);
+ struct hlist_bl_node *l;
+ struct mb_cache_entry *ce = NULL;
+ struct hlist_bl_head *index_hash_p;
+
+ index_hash_p = &cache->c_index_hash[bucket];
+ hlist_bl_lock(index_hash_p);
+ if (!hlist_bl_empty(index_hash_p)) {
+ l = hlist_bl_first(index_hash_p);
+ ce = __mb_cache_entry_find(l, index_hash_p, bdev, key);
+ } else
+ hlist_bl_unlock(index_hash_p);
return ce;
}
@@ -597,13 +825,17 @@ mb_cache_entry_find_next(struct mb_cache_entry *prev,
{
struct mb_cache *cache = prev->e_cache;
unsigned int bucket = hash_long(key, cache->c_bucket_bits);
- struct list_head *l;
+ struct hlist_bl_node *l;
struct mb_cache_entry *ce;
+ struct hlist_bl_head *index_hash_p;
- spin_lock(&mb_cache_spinlock);
+ index_hash_p = &cache->c_index_hash[bucket];
+ mb_assert(prev->e_index_hash_p == index_hash_p);
+ hlist_bl_lock(index_hash_p);
+ mb_assert(!hlist_bl_empty(index_hash_p));
l = prev->e_index.o_list.next;
- ce = __mb_cache_entry_find(l, &cache->c_index_hash[bucket], bdev, key);
- __mb_cache_entry_release_unlock(prev);
+ ce = __mb_cache_entry_find(l, index_hash_p, bdev, key);
+ __mb_cache_entry_release(prev);
return ce;
}
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 0332109162a5..f007a3355570 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -26,7 +26,7 @@ static int minix_remount (struct super_block * sb, int * flags, char * data);
static void minix_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
if (!inode->i_nlink) {
inode->i_size = 0;
minix_truncate(inode);
@@ -86,7 +86,7 @@ static void init_once(void *foo)
inode_init_once(&ei->vfs_inode);
}
-static int init_inodecache(void)
+static int __init init_inodecache(void)
{
minix_inode_cachep = kmem_cache_create("minix_inode_cache",
sizeof(struct minix_inode_info),
@@ -123,6 +123,7 @@ static int minix_remount (struct super_block * sb, int * flags, char * data)
struct minix_sb_info * sbi = minix_sb(sb);
struct minix_super_block * ms;
+ sync_filesystem(sb);
ms = sbi->s_ms;
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
return 0;
diff --git a/fs/namei.c b/fs/namei.c
index 4b491b431990..88339f59efb5 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1796,7 +1796,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
if (err)
return err;
}
- if (!d_is_directory(nd->path.dentry)) {
+ if (!d_can_lookup(nd->path.dentry)) {
err = -ENOTDIR;
break;
}
@@ -1817,7 +1817,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
struct dentry *root = nd->root.dentry;
struct inode *inode = root->d_inode;
if (*name) {
- if (!d_is_directory(root))
+ if (!d_can_lookup(root))
return -ENOTDIR;
retval = inode_permission(inode, MAY_EXEC);
if (retval)
@@ -1873,7 +1873,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
dentry = f.file->f_path.dentry;
if (*name) {
- if (!d_is_directory(dentry)) {
+ if (!d_can_lookup(dentry)) {
fdput(f);
return -ENOTDIR;
}
@@ -1955,7 +1955,7 @@ static int path_lookupat(int dfd, const char *name,
err = complete_walk(nd);
if (!err && nd->flags & LOOKUP_DIRECTORY) {
- if (!d_is_directory(nd->path.dentry)) {
+ if (!d_can_lookup(nd->path.dentry)) {
path_put(&nd->path);
err = -ENOTDIR;
}
@@ -2414,11 +2414,11 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
IS_IMMUTABLE(inode) || IS_SWAPFILE(inode))
return -EPERM;
if (isdir) {
- if (!d_is_directory(victim) && !d_is_autodir(victim))
+ if (!d_is_dir(victim))
return -ENOTDIR;
if (IS_ROOT(victim))
return -EBUSY;
- } else if (d_is_directory(victim) || d_is_autodir(victim))
+ } else if (d_is_dir(victim))
return -EISDIR;
if (IS_DEADDIR(dir))
return -ENOENT;
@@ -2569,7 +2569,7 @@ static int handle_truncate(struct file *filp)
/*
* Refuse to truncate files with mandatory locks held on them.
*/
- error = locks_verify_locked(inode);
+ error = locks_verify_locked(filp);
if (!error)
error = security_path_truncate(path);
if (!error) {
@@ -3016,11 +3016,10 @@ finish_open:
}
audit_inode(name, nd->path.dentry, 0);
error = -EISDIR;
- if ((open_flag & O_CREAT) &&
- (d_is_directory(nd->path.dentry) || d_is_autodir(nd->path.dentry)))
+ if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry))
goto out;
error = -ENOTDIR;
- if ((nd->flags & LOOKUP_DIRECTORY) && !d_is_directory(nd->path.dentry))
+ if ((nd->flags & LOOKUP_DIRECTORY) && !d_can_lookup(nd->path.dentry))
goto out;
if (!S_ISREG(nd->inode->i_mode))
will_truncate = false;
@@ -3744,7 +3743,7 @@ exit1:
slashes:
if (d_is_negative(dentry))
error = -ENOENT;
- else if (d_is_directory(dentry) || d_is_autodir(dentry))
+ else if (d_is_dir(dentry))
error = -EISDIR;
else
error = -ENOTDIR;
@@ -3974,7 +3973,28 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
}
-/*
+/**
+ * vfs_rename - rename a filesystem object
+ * @old_dir: parent of source
+ * @old_dentry: source
+ * @new_dir: parent of destination
+ * @new_dentry: destination
+ * @delegated_inode: returns an inode needing a delegation break
+ * @flags: rename flags
+ *
+ * The caller must hold multiple mutexes--see lock_rename()).
+ *
+ * If vfs_rename discovers a delegation in need of breaking at either
+ * the source or destination, it will return -EWOULDBLOCK and return a
+ * reference to the inode in delegated_inode. The caller should then
+ * break the delegation and retry. Because breaking a delegation may
+ * take a long time, the caller should drop all locks before doing
+ * so.
+ *
+ * Alternatively, a caller may pass NULL for delegated_inode. This may
+ * be appropriate for callers that expect the underlying filesystem not
+ * to be NFS exported.
+ *
* The worst of all namespace operations - renaming directory. "Perverted"
* doesn't even start to describe it. Somebody in UCB had a heck of a trip...
* Problems:
@@ -4002,163 +4022,139 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
* ->i_mutex on parents, which works but leads to some truly excessive
* locking].
*/
-static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
+int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ struct inode **delegated_inode, unsigned int flags)
{
- int error = 0;
+ int error;
+ bool is_dir = d_is_dir(old_dentry);
+ const unsigned char *old_name;
+ struct inode *source = old_dentry->d_inode;
struct inode *target = new_dentry->d_inode;
+ bool new_is_dir = false;
unsigned max_links = new_dir->i_sb->s_max_links;
+ if (source == target)
+ return 0;
+
+ error = may_delete(old_dir, old_dentry, is_dir);
+ if (error)
+ return error;
+
+ if (!target) {
+ error = may_create(new_dir, new_dentry);
+ } else {
+ new_is_dir = d_is_dir(new_dentry);
+
+ if (!(flags & RENAME_EXCHANGE))
+ error = may_delete(new_dir, new_dentry, is_dir);
+ else
+ error = may_delete(new_dir, new_dentry, new_is_dir);
+ }
+ if (error)
+ return error;
+
+ if (!old_dir->i_op->rename)
+ return -EPERM;
+
+ if (flags && !old_dir->i_op->rename2)
+ return -EINVAL;
+
/*
* If we are going to change the parent - check write permissions,
* we'll need to flip '..'.
*/
if (new_dir != old_dir) {
- error = inode_permission(old_dentry->d_inode, MAY_WRITE);
- if (error)
- return error;
+ if (is_dir) {
+ error = inode_permission(source, MAY_WRITE);
+ if (error)
+ return error;
+ }
+ if ((flags & RENAME_EXCHANGE) && new_is_dir) {
+ error = inode_permission(target, MAY_WRITE);
+ if (error)
+ return error;
+ }
}
- error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
+ error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry,
+ flags);
if (error)
return error;
+ old_name = fsnotify_oldname_init(old_dentry->d_name.name);
dget(new_dentry);
- if (target)
+ if (!is_dir || (flags & RENAME_EXCHANGE))
+ lock_two_nondirectories(source, target);
+ else if (target)
mutex_lock(&target->i_mutex);
error = -EBUSY;
if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry))
goto out;
- error = -EMLINK;
- if (max_links && !target && new_dir != old_dir &&
- new_dir->i_nlink >= max_links)
- goto out;
-
- if (target)
+ if (max_links && new_dir != old_dir) {
+ error = -EMLINK;
+ if (is_dir && !new_is_dir && new_dir->i_nlink >= max_links)
+ goto out;
+ if ((flags & RENAME_EXCHANGE) && !is_dir && new_is_dir &&
+ old_dir->i_nlink >= max_links)
+ goto out;
+ }
+ if (is_dir && !(flags & RENAME_EXCHANGE) && target)
shrink_dcache_parent(new_dentry);
- error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
- if (error)
- goto out;
-
- if (target) {
- target->i_flags |= S_DEAD;
- dont_mount(new_dentry);
+ if (!is_dir) {
+ error = try_break_deleg(source, delegated_inode);
+ if (error)
+ goto out;
}
-out:
- if (target)
- mutex_unlock(&target->i_mutex);
- dput(new_dentry);
- if (!error)
- if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
- d_move(old_dentry,new_dentry);
- return error;
-}
-
-static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry,
- struct inode **delegated_inode)
-{
- struct inode *target = new_dentry->d_inode;
- struct inode *source = old_dentry->d_inode;
- int error;
-
- error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
- if (error)
- return error;
-
- dget(new_dentry);
- lock_two_nondirectories(source, target);
-
- error = -EBUSY;
- if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
- goto out;
-
- error = try_break_deleg(source, delegated_inode);
- if (error)
- goto out;
- if (target) {
+ if (target && !new_is_dir) {
error = try_break_deleg(target, delegated_inode);
if (error)
goto out;
}
- error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+ if (!flags) {
+ error = old_dir->i_op->rename(old_dir, old_dentry,
+ new_dir, new_dentry);
+ } else {
+ error = old_dir->i_op->rename2(old_dir, old_dentry,
+ new_dir, new_dentry, flags);
+ }
if (error)
goto out;
- if (target)
+ if (!(flags & RENAME_EXCHANGE) && target) {
+ if (is_dir)
+ target->i_flags |= S_DEAD;
dont_mount(new_dentry);
- if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
- d_move(old_dentry, new_dentry);
+ }
+ if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) {
+ if (!(flags & RENAME_EXCHANGE))
+ d_move(old_dentry, new_dentry);
+ else
+ d_exchange(old_dentry, new_dentry);
+ }
out:
- unlock_two_nondirectories(source, target);
+ if (!is_dir || (flags & RENAME_EXCHANGE))
+ unlock_two_nondirectories(source, target);
+ else if (target)
+ mutex_unlock(&target->i_mutex);
dput(new_dentry);
- return error;
-}
-
-/**
- * vfs_rename - rename a filesystem object
- * @old_dir: parent of source
- * @old_dentry: source
- * @new_dir: parent of destination
- * @new_dentry: destination
- * @delegated_inode: returns an inode needing a delegation break
- *
- * The caller must hold multiple mutexes--see lock_rename()).
- *
- * If vfs_rename discovers a delegation in need of breaking at either
- * the source or destination, it will return -EWOULDBLOCK and return a
- * reference to the inode in delegated_inode. The caller should then
- * break the delegation and retry. Because breaking a delegation may
- * take a long time, the caller should drop all locks before doing
- * so.
- *
- * Alternatively, a caller may pass NULL for delegated_inode. This may
- * be appropriate for callers that expect the underlying filesystem not
- * to be NFS exported.
- */
-int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry,
- struct inode **delegated_inode)
-{
- int error;
- int is_dir = d_is_directory(old_dentry) || d_is_autodir(old_dentry);
- const unsigned char *old_name;
-
- if (old_dentry->d_inode == new_dentry->d_inode)
- return 0;
-
- error = may_delete(old_dir, old_dentry, is_dir);
- if (error)
- return error;
-
- if (!new_dentry->d_inode)
- error = may_create(new_dir, new_dentry);
- else
- error = may_delete(new_dir, new_dentry, is_dir);
- if (error)
- return error;
-
- if (!old_dir->i_op->rename)
- return -EPERM;
-
- old_name = fsnotify_oldname_init(old_dentry->d_name.name);
-
- if (is_dir)
- error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
- else
- error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry,delegated_inode);
- if (!error)
+ if (!error) {
fsnotify_move(old_dir, new_dir, old_name, is_dir,
- new_dentry->d_inode, old_dentry);
+ !(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
+ if (flags & RENAME_EXCHANGE) {
+ fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
+ new_is_dir, NULL, new_dentry);
+ }
+ }
fsnotify_oldname_free(old_name);
return error;
}
-SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
- int, newdfd, const char __user *, newname)
+SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
+ int, newdfd, const char __user *, newname, unsigned int, flags)
{
struct dentry *old_dir, *new_dir;
struct dentry *old_dentry, *new_dentry;
@@ -4170,6 +4166,13 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
unsigned int lookup_flags = 0;
bool should_retry = false;
int error;
+
+ if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
+ return -EINVAL;
+
+ if ((flags & RENAME_NOREPLACE) && (flags & RENAME_EXCHANGE))
+ return -EINVAL;
+
retry:
from = user_path_parent(olddfd, oldname, &oldnd, lookup_flags);
if (IS_ERR(from)) {
@@ -4193,6 +4196,8 @@ retry:
goto exit2;
new_dir = newnd.path.dentry;
+ if (flags & RENAME_NOREPLACE)
+ error = -EEXIST;
if (newnd.last_type != LAST_NORM)
goto exit2;
@@ -4202,7 +4207,8 @@ retry:
oldnd.flags &= ~LOOKUP_PARENT;
newnd.flags &= ~LOOKUP_PARENT;
- newnd.flags |= LOOKUP_RENAME_TARGET;
+ if (!(flags & RENAME_EXCHANGE))
+ newnd.flags |= LOOKUP_RENAME_TARGET;
retry_deleg:
trap = lock_rename(new_dir, old_dir);
@@ -4215,34 +4221,49 @@ retry_deleg:
error = -ENOENT;
if (d_is_negative(old_dentry))
goto exit4;
+ new_dentry = lookup_hash(&newnd);
+ error = PTR_ERR(new_dentry);
+ if (IS_ERR(new_dentry))
+ goto exit4;
+ error = -EEXIST;
+ if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry))
+ goto exit5;
+ if (flags & RENAME_EXCHANGE) {
+ error = -ENOENT;
+ if (d_is_negative(new_dentry))
+ goto exit5;
+
+ if (!d_is_dir(new_dentry)) {
+ error = -ENOTDIR;
+ if (newnd.last.name[newnd.last.len])
+ goto exit5;
+ }
+ }
/* unless the source is a directory trailing slashes give -ENOTDIR */
- if (!d_is_directory(old_dentry) && !d_is_autodir(old_dentry)) {
+ if (!d_is_dir(old_dentry)) {
error = -ENOTDIR;
if (oldnd.last.name[oldnd.last.len])
- goto exit4;
- if (newnd.last.name[newnd.last.len])
- goto exit4;
+ goto exit5;
+ if (!(flags & RENAME_EXCHANGE) && newnd.last.name[newnd.last.len])
+ goto exit5;
}
/* source should not be ancestor of target */
error = -EINVAL;
if (old_dentry == trap)
- goto exit4;
- new_dentry = lookup_hash(&newnd);
- error = PTR_ERR(new_dentry);
- if (IS_ERR(new_dentry))
- goto exit4;
+ goto exit5;
/* target should not be an ancestor of source */
- error = -ENOTEMPTY;
+ if (!(flags & RENAME_EXCHANGE))
+ error = -ENOTEMPTY;
if (new_dentry == trap)
goto exit5;
error = security_path_rename(&oldnd.path, old_dentry,
- &newnd.path, new_dentry);
+ &newnd.path, new_dentry, flags);
if (error)
goto exit5;
error = vfs_rename(old_dir->d_inode, old_dentry,
- new_dir->d_inode, new_dentry,
- &delegated_inode);
+ new_dir->d_inode, new_dentry,
+ &delegated_inode, flags);
exit5:
dput(new_dentry);
exit4:
@@ -4272,9 +4293,15 @@ exit:
return error;
}
+SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
+ int, newdfd, const char __user *, newname)
+{
+ return sys_renameat2(olddfd, oldname, newdfd, newname, 0);
+}
+
SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname)
{
- return sys_renameat(AT_FDCWD, oldname, AT_FDCWD, newname);
+ return sys_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
}
int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
index 2cf2ebecb55f..647d86d2db39 100644
--- a/fs/ncpfs/inode.c
+++ b/fs/ncpfs/inode.c
@@ -99,6 +99,7 @@ static void destroy_inodecache(void)
static int ncp_remount(struct super_block *sb, int *flags, char* data)
{
+ sync_filesystem(sb);
*flags |= MS_NODIRATIME;
return 0;
}
@@ -296,7 +297,7 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
static void
ncp_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (S_ISDIR(inode->i_mode)) {
diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c
index 56ff823ca82e..65d849bdf77a 100644
--- a/fs/nfs/blocklayout/blocklayout.c
+++ b/fs/nfs/blocklayout/blocklayout.c
@@ -1213,7 +1213,7 @@ static u64 pnfs_num_cont_bytes(struct inode *inode, pgoff_t idx)
end = DIV_ROUND_UP(i_size_read(inode), PAGE_CACHE_SIZE);
if (end != NFS_I(inode)->npages) {
rcu_read_lock();
- end = radix_tree_next_hole(&mapping->page_tree, idx + 1, ULONG_MAX);
+ end = page_cache_next_hole(mapping, idx + 1, ULONG_MAX);
rcu_read_unlock();
}
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index ae2e87b95453..41db5258e7a7 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -112,7 +112,8 @@ out:
* TODO: keep track of all layouts (and delegations) in a hash table
* hashed by filehandle.
*/
-static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp, struct nfs_fh *fh)
+static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
+ struct nfs_fh *fh, nfs4_stateid *stateid)
{
struct nfs_server *server;
struct inode *ino;
@@ -120,17 +121,19 @@ static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
list_for_each_entry(lo, &server->layouts, plh_layouts) {
+ if (!nfs4_stateid_match_other(&lo->plh_stateid, stateid))
+ continue;
if (nfs_compare_fh(fh, &NFS_I(lo->plh_inode)->fh))
continue;
ino = igrab(lo->plh_inode);
if (!ino)
- continue;
+ break;
spin_lock(&ino->i_lock);
/* Is this layout in the process of being freed? */
if (NFS_I(ino)->layout != lo) {
spin_unlock(&ino->i_lock);
iput(ino);
- continue;
+ break;
}
pnfs_get_layout_hdr(lo);
spin_unlock(&ino->i_lock);
@@ -141,13 +144,14 @@ static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
return NULL;
}
-static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp, struct nfs_fh *fh)
+static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp,
+ struct nfs_fh *fh, nfs4_stateid *stateid)
{
struct pnfs_layout_hdr *lo;
spin_lock(&clp->cl_lock);
rcu_read_lock();
- lo = get_layout_by_fh_locked(clp, fh);
+ lo = get_layout_by_fh_locked(clp, fh, stateid);
rcu_read_unlock();
spin_unlock(&clp->cl_lock);
@@ -162,9 +166,9 @@ static u32 initiate_file_draining(struct nfs_client *clp,
u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
LIST_HEAD(free_me_list);
- lo = get_layout_by_fh(clp, &args->cbl_fh);
+ lo = get_layout_by_fh(clp, &args->cbl_fh, &args->cbl_stateid);
if (!lo)
- return NFS4ERR_NOMATCHING_LAYOUT;
+ goto out;
ino = lo->plh_inode;
spin_lock(&ino->i_lock);
@@ -179,6 +183,7 @@ static u32 initiate_file_draining(struct nfs_client *clp,
pnfs_free_lseg_list(&free_me_list);
pnfs_put_layout_hdr(lo);
iput(ino);
+out:
return rv;
}
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 4a48fe4b84b6..d9f3d067cd15 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -69,21 +69,28 @@ const struct address_space_operations nfs_dir_aops = {
static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred)
{
+ struct nfs_inode *nfsi = NFS_I(dir);
struct nfs_open_dir_context *ctx;
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (ctx != NULL) {
ctx->duped = 0;
- ctx->attr_gencount = NFS_I(dir)->attr_gencount;
+ ctx->attr_gencount = nfsi->attr_gencount;
ctx->dir_cookie = 0;
ctx->dup_cookie = 0;
ctx->cred = get_rpccred(cred);
+ spin_lock(&dir->i_lock);
+ list_add(&ctx->list, &nfsi->open_files);
+ spin_unlock(&dir->i_lock);
return ctx;
}
return ERR_PTR(-ENOMEM);
}
-static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx)
+static void put_nfs_open_dir_context(struct inode *dir, struct nfs_open_dir_context *ctx)
{
+ spin_lock(&dir->i_lock);
+ list_del(&ctx->list);
+ spin_unlock(&dir->i_lock);
put_rpccred(ctx->cred);
kfree(ctx);
}
@@ -126,7 +133,7 @@ out:
static int
nfs_closedir(struct inode *inode, struct file *filp)
{
- put_nfs_open_dir_context(filp->private_data);
+ put_nfs_open_dir_context(filp->f_path.dentry->d_inode, filp->private_data);
return 0;
}
@@ -306,10 +313,9 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des
if (printk_ratelimit()) {
pr_notice("NFS: directory %pD2 contains a readdir loop."
"Please contact your server vendor. "
- "The file: %s has duplicate cookie %llu\n",
- desc->file,
- array->array[i].string.name,
- *desc->dir_cookie);
+ "The file: %.*s has duplicate cookie %llu\n",
+ desc->file, array->array[i].string.len,
+ array->array[i].string.name, *desc->dir_cookie);
}
status = -ELOOP;
goto out;
@@ -437,6 +443,22 @@ void nfs_advise_use_readdirplus(struct inode *dir)
set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags);
}
+/*
+ * This function is mainly for use by nfs_getattr().
+ *
+ * If this is an 'ls -l', we want to force use of readdirplus.
+ * Do this by checking if there is an active file descriptor
+ * and calling nfs_advise_use_readdirplus, then forcing a
+ * cache flush.
+ */
+void nfs_force_use_readdirplus(struct inode *dir)
+{
+ if (!list_empty(&NFS_I(dir)->open_files)) {
+ nfs_advise_use_readdirplus(dir);
+ nfs_zap_mapping(dir, dir->i_mapping);
+ }
+}
+
static
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
{
@@ -815,6 +837,17 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc)
goto out;
}
+static bool nfs_dir_mapping_need_revalidate(struct inode *dir)
+{
+ struct nfs_inode *nfsi = NFS_I(dir);
+
+ if (nfs_attribute_cache_expired(dir))
+ return true;
+ if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
+ return true;
+ return false;
+}
+
/* The file offset position represents the dirent entry number. A
last cookie cache takes care of the common case of reading the
whole directory.
@@ -847,7 +880,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0;
nfs_block_sillyrename(dentry);
- if (ctx->pos == 0 || nfs_attribute_cache_expired(inode))
+ if (ctx->pos == 0 || nfs_dir_mapping_need_revalidate(inode))
res = nfs_revalidate_mapping(inode, file->f_mapping);
if (res < 0)
goto out;
@@ -1911,6 +1944,7 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *old_inode = old_dentry->d_inode;
struct inode *new_inode = new_dentry->d_inode;
struct dentry *dentry = NULL, *rehash = NULL;
+ struct rpc_task *task;
int error = -EBUSY;
dfprintk(VFS, "NFS: rename(%pd2 -> %pd2, ct=%d)\n",
@@ -1958,8 +1992,16 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (new_inode != NULL)
NFS_PROTO(new_inode)->return_delegation(new_inode);
- error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
- new_dir, &new_dentry->d_name);
+ task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, NULL);
+ if (IS_ERR(task)) {
+ error = PTR_ERR(task);
+ goto out;
+ }
+
+ error = rpc_wait_for_completion_task(task);
+ if (error == 0)
+ error = task->tk_status;
+ rpc_put_task(task);
nfs_mark_for_revalidate(old_inode);
out:
if (rehash)
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 360114ae8b82..0c438973f3c8 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -128,7 +128,7 @@ EXPORT_SYMBOL_GPL(nfs_clear_inode);
void nfs_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
nfs_clear_inode(inode);
}
@@ -588,6 +588,25 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
}
EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
+static void nfs_request_parent_use_readdirplus(struct dentry *dentry)
+{
+ struct dentry *parent;
+
+ parent = dget_parent(dentry);
+ nfs_force_use_readdirplus(parent->d_inode);
+ dput(parent);
+}
+
+static bool nfs_need_revalidate_inode(struct inode *inode)
+{
+ if (NFS_I(inode)->cache_validity &
+ (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
+ return true;
+ if (nfs_attribute_cache_expired(inode))
+ return true;
+ return false;
+}
+
int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{
struct inode *inode = dentry->d_inode;
@@ -616,10 +635,13 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)))
need_atime = 0;
- if (need_atime)
- err = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
- else
- err = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ if (need_atime || nfs_need_revalidate_inode(inode)) {
+ struct nfs_server *server = NFS_SERVER(inode);
+
+ if (server->caps & NFS_CAP_READDIRPLUS)
+ nfs_request_parent_use_readdirplus(dentry);
+ err = __nfs_revalidate_inode(server, inode);
+ }
if (!err) {
generic_fillattr(inode, stat);
stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
@@ -961,9 +983,7 @@ int nfs_attribute_cache_expired(struct inode *inode)
*/
int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{
- if (!(NFS_I(inode)->cache_validity &
- (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
- && !nfs_attribute_cache_expired(inode))
+ if (!nfs_need_revalidate_inode(inode))
return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode);
}
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index b46cf5a67329..dd8bfc2e2464 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -301,6 +301,7 @@ extern struct nfs_client *nfs_init_client(struct nfs_client *clp,
const char *ip_addr);
/* dir.c */
+extern void nfs_force_use_readdirplus(struct inode *dir);
extern unsigned long nfs_access_cache_count(struct shrinker *shrink,
struct shrink_control *sc);
extern unsigned long nfs_access_cache_scan(struct shrinker *shrink,
@@ -474,6 +475,13 @@ extern int nfs_migrate_page(struct address_space *,
#define nfs_migrate_page NULL
#endif
+/* unlink.c */
+extern struct rpc_task *
+nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
+ struct dentry *old_dentry, struct dentry *new_dentry,
+ void (*complete)(struct rpc_task *, struct nfs_renamedata *));
+extern int nfs_sillyrename(struct inode *dir, struct dentry *dentry);
+
/* direct.c */
void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo,
struct nfs_direct_req *dreq);
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index a462ef0fb5d6..db60149c4579 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -479,41 +479,6 @@ nfs3_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
}
static int
-nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name,
- struct inode *new_dir, struct qstr *new_name)
-{
- struct nfs_renameargs arg = {
- .old_dir = NFS_FH(old_dir),
- .old_name = old_name,
- .new_dir = NFS_FH(new_dir),
- .new_name = new_name,
- };
- struct nfs_renameres res;
- struct rpc_message msg = {
- .rpc_proc = &nfs3_procedures[NFS3PROC_RENAME],
- .rpc_argp = &arg,
- .rpc_resp = &res,
- };
- int status = -ENOMEM;
-
- dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name);
-
- res.old_fattr = nfs_alloc_fattr();
- res.new_fattr = nfs_alloc_fattr();
- if (res.old_fattr == NULL || res.new_fattr == NULL)
- goto out;
-
- status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0);
- nfs_post_op_update_inode(old_dir, res.old_fattr);
- nfs_post_op_update_inode(new_dir, res.new_fattr);
-out:
- nfs_free_fattr(res.old_fattr);
- nfs_free_fattr(res.new_fattr);
- dprintk("NFS reply rename: %d\n", status);
- return status;
-}
-
-static int
nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
{
struct nfs3_linkargs arg = {
@@ -968,7 +933,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
.unlink_setup = nfs3_proc_unlink_setup,
.unlink_rpc_prepare = nfs3_proc_unlink_rpc_prepare,
.unlink_done = nfs3_proc_unlink_done,
- .rename = nfs3_proc_rename,
.rename_setup = nfs3_proc_rename_setup,
.rename_rpc_prepare = nfs3_proc_rename_rpc_prepare,
.rename_done = nfs3_proc_rename_done,
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index a5b27c2d9689..e1d1badbe53c 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -427,6 +427,7 @@ extern void nfs4_close_sync(struct nfs4_state *, fmode_t);
extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t);
extern void nfs_inode_find_state_and_recover(struct inode *inode,
const nfs4_stateid *stateid);
+extern int nfs4_state_mark_reclaim_nograce(struct nfs_client *, struct nfs4_state *);
extern void nfs4_schedule_lease_recovery(struct nfs_client *);
extern int nfs4_wait_clnt_recover(struct nfs_client *clp);
extern int nfs4_client_recover_expired_lease(struct nfs_client *clp);
@@ -500,6 +501,16 @@ static inline bool nfs4_stateid_match(const nfs4_stateid *dst, const nfs4_statei
return memcmp(dst, src, sizeof(*dst)) == 0;
}
+static inline bool nfs4_stateid_match_other(const nfs4_stateid *dst, const nfs4_stateid *src)
+{
+ return memcmp(dst->other, src->other, NFS4_STATEID_OTHER_SIZE) == 0;
+}
+
+static inline bool nfs4_stateid_is_newer(const nfs4_stateid *s1, const nfs4_stateid *s2)
+{
+ return (s32)(be32_to_cpu(s1->seqid) - be32_to_cpu(s2->seqid)) > 0;
+}
+
static inline bool nfs4_valid_open_stateid(const struct nfs4_state *state)
{
return test_bit(NFS_STATE_RECOVERY_FAILED, &state->flags) == 0;
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 0e46d3d1b6cc..aa9ef4876046 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -531,6 +531,13 @@ int nfs40_walk_client_list(struct nfs_client *new,
*result = pos;
dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n",
__func__, pos, atomic_read(&pos->cl_count));
+ goto out;
+ case -ERESTARTSYS:
+ case -ETIMEDOUT:
+ /* The callback path may have been inadvertently
+ * changed. Schedule recovery!
+ */
+ nfs4_schedule_path_down_recovery(pos);
default:
goto out;
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 450bfedbe2f4..397be39c6dc8 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1068,6 +1068,7 @@ static void nfs4_opendata_free(struct kref *kref)
dput(p->dentry);
nfs_sb_deactive(sb);
nfs_fattr_free_names(&p->f_attr);
+ kfree(p->f_attr.mdsthreshold);
kfree(p);
}
@@ -1137,12 +1138,71 @@ static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)
nfs4_state_set_mode_locked(state, state->state | fmode);
}
-static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
+static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
+{
+ struct nfs_client *clp = state->owner->so_server->nfs_client;
+ bool need_recover = false;
+
+ if (test_and_clear_bit(NFS_O_RDONLY_STATE, &state->flags) && state->n_rdonly)
+ need_recover = true;
+ if (test_and_clear_bit(NFS_O_WRONLY_STATE, &state->flags) && state->n_wronly)
+ need_recover = true;
+ if (test_and_clear_bit(NFS_O_RDWR_STATE, &state->flags) && state->n_rdwr)
+ need_recover = true;
+ if (need_recover)
+ nfs4_state_mark_reclaim_nograce(clp, state);
+}
+
+static bool nfs_need_update_open_stateid(struct nfs4_state *state,
+ nfs4_stateid *stateid)
+{
+ if (test_and_set_bit(NFS_OPEN_STATE, &state->flags) == 0)
+ return true;
+ if (!nfs4_stateid_match_other(stateid, &state->open_stateid)) {
+ nfs_test_and_clear_all_open_stateid(state);
+ return true;
+ }
+ if (nfs4_stateid_is_newer(stateid, &state->open_stateid))
+ return true;
+ return false;
+}
+
+static void nfs_clear_open_stateid_locked(struct nfs4_state *state,
+ nfs4_stateid *stateid, fmode_t fmode)
{
+ clear_bit(NFS_O_RDWR_STATE, &state->flags);
+ switch (fmode & (FMODE_READ|FMODE_WRITE)) {
+ case FMODE_WRITE:
+ clear_bit(NFS_O_RDONLY_STATE, &state->flags);
+ break;
+ case FMODE_READ:
+ clear_bit(NFS_O_WRONLY_STATE, &state->flags);
+ break;
+ case 0:
+ clear_bit(NFS_O_RDONLY_STATE, &state->flags);
+ clear_bit(NFS_O_WRONLY_STATE, &state->flags);
+ clear_bit(NFS_OPEN_STATE, &state->flags);
+ }
+ if (stateid == NULL)
+ return;
+ if (!nfs_need_update_open_stateid(state, stateid))
+ return;
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
nfs4_stateid_copy(&state->stateid, stateid);
nfs4_stateid_copy(&state->open_stateid, stateid);
- set_bit(NFS_OPEN_STATE, &state->flags);
+}
+
+static void nfs_clear_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
+{
+ write_seqlock(&state->seqlock);
+ nfs_clear_open_stateid_locked(state, stateid, fmode);
+ write_sequnlock(&state->seqlock);
+ if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags))
+ nfs4_schedule_state_manager(state->owner->so_server->nfs_client);
+}
+
+static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
+{
switch (fmode) {
case FMODE_READ:
set_bit(NFS_O_RDONLY_STATE, &state->flags);
@@ -1153,13 +1213,11 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *
case FMODE_READ|FMODE_WRITE:
set_bit(NFS_O_RDWR_STATE, &state->flags);
}
-}
-
-static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
-{
- write_seqlock(&state->seqlock);
- nfs_set_open_stateid_locked(state, stateid, fmode);
- write_sequnlock(&state->seqlock);
+ if (!nfs_need_update_open_stateid(state, stateid))
+ return;
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
+ nfs4_stateid_copy(&state->stateid, stateid);
+ nfs4_stateid_copy(&state->open_stateid, stateid);
}
static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
@@ -1217,6 +1275,8 @@ no_delegation:
__update_open_stateid(state, open_stateid, NULL, fmode);
ret = 1;
}
+ if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags))
+ nfs4_schedule_state_manager(state->owner->so_server->nfs_client);
return ret;
}
@@ -1450,12 +1510,15 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
struct nfs4_state *newstate;
int ret;
+ /* Don't trigger recovery in nfs_test_and_clear_all_open_stateid */
+ clear_bit(NFS_O_RDWR_STATE, &state->flags);
+ clear_bit(NFS_O_WRONLY_STATE, &state->flags);
+ clear_bit(NFS_O_RDONLY_STATE, &state->flags);
/* memory barrier prior to reading state->n_* */
clear_bit(NFS_DELEGATED_STATE, &state->flags);
clear_bit(NFS_OPEN_STATE, &state->flags);
smp_rmb();
if (state->n_rdwr != 0) {
- clear_bit(NFS_O_RDWR_STATE, &state->flags);
ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate);
if (ret != 0)
return ret;
@@ -1463,7 +1526,6 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
return -ESTALE;
}
if (state->n_wronly != 0) {
- clear_bit(NFS_O_WRONLY_STATE, &state->flags);
ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate);
if (ret != 0)
return ret;
@@ -1471,7 +1533,6 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
return -ESTALE;
}
if (state->n_rdonly != 0) {
- clear_bit(NFS_O_RDONLY_STATE, &state->flags);
ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate);
if (ret != 0)
return ret;
@@ -2244,10 +2305,12 @@ static int _nfs4_do_open(struct inode *dir,
}
}
- if (ctx_th && server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) {
- opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc();
- if (!opendata->f_attr.mdsthreshold)
- goto err_free_label;
+ if (server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) {
+ if (!opendata->f_attr.mdsthreshold) {
+ opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc();
+ if (!opendata->f_attr.mdsthreshold)
+ goto err_free_label;
+ }
opendata->o_arg.open_bitmap = &nfs4_pnfs_open_bitmap[0];
}
if (dentry->d_inode != NULL)
@@ -2275,11 +2338,10 @@ static int _nfs4_do_open(struct inode *dir,
if (opendata->file_created)
*opened |= FILE_CREATED;
- if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server))
+ if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server)) {
*ctx_th = opendata->f_attr.mdsthreshold;
- else
- kfree(opendata->f_attr.mdsthreshold);
- opendata->f_attr.mdsthreshold = NULL;
+ opendata->f_attr.mdsthreshold = NULL;
+ }
nfs4_label_free(olabel);
@@ -2289,7 +2351,6 @@ static int _nfs4_do_open(struct inode *dir,
err_free_label:
nfs4_label_free(olabel);
err_opendata_put:
- kfree(opendata->f_attr.mdsthreshold);
nfs4_opendata_put(opendata);
err_put_state_owner:
nfs4_put_state_owner(sp);
@@ -2479,26 +2540,6 @@ static void nfs4_free_closedata(void *data)
kfree(calldata);
}
-static void nfs4_close_clear_stateid_flags(struct nfs4_state *state,
- fmode_t fmode)
-{
- spin_lock(&state->owner->so_lock);
- clear_bit(NFS_O_RDWR_STATE, &state->flags);
- switch (fmode & (FMODE_READ|FMODE_WRITE)) {
- case FMODE_WRITE:
- clear_bit(NFS_O_RDONLY_STATE, &state->flags);
- break;
- case FMODE_READ:
- clear_bit(NFS_O_WRONLY_STATE, &state->flags);
- break;
- case 0:
- clear_bit(NFS_O_RDONLY_STATE, &state->flags);
- clear_bit(NFS_O_WRONLY_STATE, &state->flags);
- clear_bit(NFS_OPEN_STATE, &state->flags);
- }
- spin_unlock(&state->owner->so_lock);
-}
-
static void nfs4_close_done(struct rpc_task *task, void *data)
{
struct nfs4_closedata *calldata = data;
@@ -2517,9 +2558,9 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
if (calldata->roc)
pnfs_roc_set_barrier(state->inode,
calldata->roc_barrier);
- nfs_set_open_stateid(state, &calldata->res.stateid, 0);
+ nfs_clear_open_stateid(state, &calldata->res.stateid, 0);
renew_lease(server, calldata->timestamp);
- break;
+ goto out_release;
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_OLD_STATEID:
@@ -2533,7 +2574,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
goto out_release;
}
}
- nfs4_close_clear_stateid_flags(state, calldata->arg.fmode);
+ nfs_clear_open_stateid(state, NULL, calldata->arg.fmode);
out_release:
nfs_release_seqid(calldata->arg.seqid);
nfs_refresh_inode(calldata->inode, calldata->res.fattr);
@@ -3507,49 +3548,6 @@ static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
return 1;
}
-static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
- struct inode *new_dir, struct qstr *new_name)
-{
- struct nfs_server *server = NFS_SERVER(old_dir);
- struct nfs_renameargs arg = {
- .old_dir = NFS_FH(old_dir),
- .new_dir = NFS_FH(new_dir),
- .old_name = old_name,
- .new_name = new_name,
- };
- struct nfs_renameres res = {
- .server = server,
- };
- struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME],
- .rpc_argp = &arg,
- .rpc_resp = &res,
- };
- int status = -ENOMEM;
-
- status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
- if (!status) {
- update_changeattr(old_dir, &res.old_cinfo);
- update_changeattr(new_dir, &res.new_cinfo);
- }
- return status;
-}
-
-static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
- struct inode *new_dir, struct qstr *new_name)
-{
- struct nfs4_exception exception = { };
- int err;
- do {
- err = _nfs4_proc_rename(old_dir, old_name,
- new_dir, new_name);
- trace_nfs4_rename(old_dir, old_name, new_dir, new_name, err);
- err = nfs4_handle_exception(NFS_SERVER(old_dir), err,
- &exception);
- } while (exception.retry);
- return err;
-}
-
static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
{
struct nfs_server *server = NFS_SERVER(inode);
@@ -4884,6 +4882,20 @@ nfs4_init_uniform_client_string(const struct nfs_client *clp,
nodename);
}
+/*
+ * nfs4_callback_up_net() starts only "tcp" and "tcp6" callback
+ * services. Advertise one based on the address family of the
+ * clientaddr.
+ */
+static unsigned int
+nfs4_init_callback_netid(const struct nfs_client *clp, char *buf, size_t len)
+{
+ if (strchr(clp->cl_ipaddr, ':') != NULL)
+ return scnprintf(buf, len, "tcp6");
+ else
+ return scnprintf(buf, len, "tcp");
+}
+
/**
* nfs4_proc_setclientid - Negotiate client ID
* @clp: state data structure
@@ -4925,12 +4937,10 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
setclientid.sc_name,
sizeof(setclientid.sc_name));
/* cb_client4 */
- rcu_read_lock();
- setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
- sizeof(setclientid.sc_netid), "%s",
- rpc_peeraddr2str(clp->cl_rpcclient,
- RPC_DISPLAY_NETID));
- rcu_read_unlock();
+ setclientid.sc_netid_len =
+ nfs4_init_callback_netid(clp,
+ setclientid.sc_netid,
+ sizeof(setclientid.sc_netid));
setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr,
sizeof(setclientid.sc_uaddr), "%s.%u.%u",
clp->cl_ipaddr, port >> 8, port & 255);
@@ -8408,7 +8418,6 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.unlink_setup = nfs4_proc_unlink_setup,
.unlink_rpc_prepare = nfs4_proc_unlink_rpc_prepare,
.unlink_done = nfs4_proc_unlink_done,
- .rename = nfs4_proc_rename,
.rename_setup = nfs4_proc_rename_setup,
.rename_rpc_prepare = nfs4_proc_rename_rpc_prepare,
.rename_done = nfs4_proc_rename_done,
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 0deb32105ccf..2349518eef2c 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -1316,7 +1316,7 @@ static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_st
return 1;
}
-static int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
+int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
{
set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
@@ -2075,8 +2075,10 @@ again:
switch (status) {
case 0:
break;
- case -NFS4ERR_DELAY:
case -ETIMEDOUT:
+ if (clnt->cl_softrtry)
+ break;
+ case -NFS4ERR_DELAY:
case -EAGAIN:
ssleep(1);
case -NFS4ERR_STALE_CLIENTID:
diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c
index 808f29574412..6f340f02f2ba 100644
--- a/fs/nfs/nfs4super.c
+++ b/fs/nfs/nfs4super.c
@@ -90,7 +90,7 @@ static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
*/
static void nfs4_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
pnfs_return_layout(inode);
pnfs_destroy_layout(NFS_I(inode));
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 72f3bf1754ef..73ce8d4fe2c8 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -203,8 +203,7 @@ static int nfs4_stat_to_errno(int);
2 + encode_verifier_maxsz + 5 + \
nfs4_label_maxsz)
#define decode_readdir_maxsz (op_decode_hdr_maxsz + \
- decode_verifier_maxsz + \
- nfs4_label_maxsz + nfs4_fattr_maxsz)
+ decode_verifier_maxsz)
#define encode_readlink_maxsz (op_encode_hdr_maxsz)
#define decode_readlink_maxsz (op_decode_hdr_maxsz + 1)
#define encode_write_maxsz (op_encode_hdr_maxsz + \
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index 4755858e37a0..cb53d450ae32 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -662,7 +662,18 @@ pnfs_destroy_all_layouts(struct nfs_client *clp)
*/
static bool pnfs_seqid_is_newer(u32 s1, u32 s2)
{
- return (s32)s1 - (s32)s2 > 0;
+ return (s32)(s1 - s2) > 0;
+}
+
+static void
+pnfs_verify_layout_stateid(struct pnfs_layout_hdr *lo,
+ const nfs4_stateid *new,
+ struct list_head *free_me_list)
+{
+ if (nfs4_stateid_match_other(&lo->plh_stateid, new))
+ return;
+ /* Layout is new! Kill existing layout segments */
+ pnfs_mark_matching_lsegs_invalid(lo, free_me_list, NULL);
}
/* update lo->plh_stateid with new if is more recent */
@@ -1315,6 +1326,7 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
struct nfs4_layoutget_res *res = &lgp->res;
struct pnfs_layout_segment *lseg;
struct inode *ino = lo->plh_inode;
+ LIST_HEAD(free_me);
int status = 0;
/* Inject layout blob into I/O device driver */
@@ -1341,6 +1353,8 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
goto out_forget_reply;
}
+ /* Check that the new stateid matches the old stateid */
+ pnfs_verify_layout_stateid(lo, &res->stateid, &free_me);
/* Done processing layoutget. Set the layout stateid */
pnfs_set_layout_stateid(lo, &res->stateid, false);
@@ -1355,6 +1369,7 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
}
spin_unlock(&ino->i_lock);
+ pnfs_free_lseg_list(&free_me);
return lseg;
out:
return ERR_PTR(status);
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index fddbba2d9eff..e55ce9e8b034 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -357,30 +357,6 @@ nfs_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
}
static int
-nfs_proc_rename(struct inode *old_dir, struct qstr *old_name,
- struct inode *new_dir, struct qstr *new_name)
-{
- struct nfs_renameargs arg = {
- .old_dir = NFS_FH(old_dir),
- .old_name = old_name,
- .new_dir = NFS_FH(new_dir),
- .new_name = new_name,
- };
- struct rpc_message msg = {
- .rpc_proc = &nfs_procedures[NFSPROC_RENAME],
- .rpc_argp = &arg,
- };
- int status;
-
- dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name);
- status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0);
- nfs_mark_for_revalidate(old_dir);
- nfs_mark_for_revalidate(new_dir);
- dprintk("NFS reply rename: %d\n", status);
- return status;
-}
-
-static int
nfs_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
{
struct nfs_linkargs arg = {
@@ -745,7 +721,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
.unlink_setup = nfs_proc_unlink_setup,
.unlink_rpc_prepare = nfs_proc_unlink_rpc_prepare,
.unlink_done = nfs_proc_unlink_done,
- .rename = nfs_proc_rename,
.rename_setup = nfs_proc_rename_setup,
.rename_rpc_prepare = nfs_proc_rename_rpc_prepare,
.rename_done = nfs_proc_rename_done,
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 910ed906eb82..2cb56943e232 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2215,6 +2215,8 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
struct nfs4_mount_data *options4 = (struct nfs4_mount_data *)raw_data;
u32 nfsvers = nfss->nfs_client->rpc_ops->version;
+ sync_filesystem(sb);
+
/*
* Userspace mount programs that send binary options generally send
* them populated with default values. We have no way to know which
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c
index 11d78944de79..de54129336c6 100644
--- a/fs/nfs/unlink.c
+++ b/fs/nfs/unlink.c
@@ -14,6 +14,7 @@
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/namei.h>
+#include <linux/fsnotify.h>
#include "internal.h"
#include "nfs4_fs.h"
@@ -353,8 +354,8 @@ static void nfs_async_rename_done(struct rpc_task *task, void *calldata)
return;
}
- if (task->tk_status != 0)
- nfs_cancel_async_unlink(old_dentry);
+ if (data->complete)
+ data->complete(task, data);
}
/**
@@ -399,9 +400,10 @@ static const struct rpc_call_ops nfs_rename_ops = {
*
* It's expected that valid references to the dentries and inodes are held
*/
-static struct rpc_task *
+struct rpc_task *
nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
- struct dentry *old_dentry, struct dentry *new_dentry)
+ struct dentry *old_dentry, struct dentry *new_dentry,
+ void (*complete)(struct rpc_task *, struct nfs_renamedata *))
{
struct nfs_renamedata *data;
struct rpc_message msg = { };
@@ -438,6 +440,7 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
data->new_dentry = dget(new_dentry);
nfs_fattr_init(&data->old_fattr);
nfs_fattr_init(&data->new_fattr);
+ data->complete = complete;
/* set up nfs_renameargs */
data->args.old_dir = NFS_FH(old_dir);
@@ -456,6 +459,27 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
return rpc_run_task(&task_setup_data);
}
+/*
+ * Perform tasks needed when a sillyrename is done such as cancelling the
+ * queued async unlink if it failed.
+ */
+static void
+nfs_complete_sillyrename(struct rpc_task *task, struct nfs_renamedata *data)
+{
+ struct dentry *dentry = data->old_dentry;
+
+ if (task->tk_status != 0) {
+ nfs_cancel_async_unlink(dentry);
+ return;
+ }
+
+ /*
+ * vfs_unlink and the like do not issue this when a file is
+ * sillyrenamed, so do it here.
+ */
+ fsnotify_nameremove(dentry, 0);
+}
+
#define SILLYNAME_PREFIX ".nfs"
#define SILLYNAME_PREFIX_LEN ((unsigned)sizeof(SILLYNAME_PREFIX) - 1)
#define SILLYNAME_FILEID_LEN ((unsigned)sizeof(u64) << 1)
@@ -548,7 +572,8 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
}
/* run the rename task, undo unlink if it fails */
- task = nfs_async_rename(dir, dir, dentry, sdentry);
+ task = nfs_async_rename(dir, dir, dentry, sdentry,
+ nfs_complete_sillyrename);
if (IS_ERR(task)) {
error = -EBUSY;
nfs_cancel_async_unlink(dentry);
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
index 06cddd572264..2645be435e75 100644
--- a/fs/nfsd/auth.c
+++ b/fs/nfsd/auth.c
@@ -71,10 +71,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
if (gid_eq(new->fsgid, INVALID_GID))
new->fsgid = exp->ex_anon_gid;
- ret = set_groups(new, gi);
+ set_groups(new, gi);
put_group_info(gi);
- if (ret < 0)
- goto error;
if (!uid_eq(new->fsuid, GLOBAL_ROOT_UID))
new->cap_effective = cap_drop_nfsd_set(new->cap_effective);
@@ -89,7 +87,6 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
oom:
ret = -ENOMEM;
-error:
abort_creds(new);
return ret;
}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 6d7be3f80356..915808b36df7 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1694,7 +1694,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
if (ffhp->fh_export->ex_path.dentry != tfhp->fh_export->ex_path.dentry)
goto out_dput_new;
- host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL);
+ host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL, 0);
if (!host_err) {
host_err = commit_metadata(tfhp);
if (!host_err)
diff --git a/fs/nilfs2/cpfile.c b/fs/nilfs2/cpfile.c
index deaa3d33a0aa..0d58075f34e2 100644
--- a/fs/nilfs2/cpfile.c
+++ b/fs/nilfs2/cpfile.c
@@ -942,6 +942,18 @@ int nilfs_cpfile_read(struct super_block *sb, size_t cpsize,
struct inode *cpfile;
int err;
+ if (cpsize > sb->s_blocksize) {
+ printk(KERN_ERR
+ "NILFS: too large checkpoint size: %zu bytes.\n",
+ cpsize);
+ return -EINVAL;
+ } else if (cpsize < NILFS_MIN_CHECKPOINT_SIZE) {
+ printk(KERN_ERR
+ "NILFS: too small checkpoint size: %zu bytes.\n",
+ cpsize);
+ return -EINVAL;
+ }
+
cpfile = nilfs_iget_locked(sb, NULL, NILFS_CPFILE_INO);
if (unlikely(!cpfile))
return -ENOMEM;
diff --git a/fs/nilfs2/dat.c b/fs/nilfs2/dat.c
index fa0f80308c2d..0d5fada91191 100644
--- a/fs/nilfs2/dat.c
+++ b/fs/nilfs2/dat.c
@@ -484,6 +484,18 @@ int nilfs_dat_read(struct super_block *sb, size_t entry_size,
struct nilfs_dat_info *di;
int err;
+ if (entry_size > sb->s_blocksize) {
+ printk(KERN_ERR
+ "NILFS: too large DAT entry size: %zu bytes.\n",
+ entry_size);
+ return -EINVAL;
+ } else if (entry_size < NILFS_MIN_DAT_ENTRY_SIZE) {
+ printk(KERN_ERR
+ "NILFS: too small DAT entry size: %zu bytes.\n",
+ entry_size);
+ return -EINVAL;
+ }
+
dat = nilfs_iget_locked(sb, NULL, NILFS_DAT_INO);
if (unlikely(!dat))
return -ENOMEM;
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index 7e350c562e0e..b9c5726120e3 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -783,16 +783,14 @@ void nilfs_evict_inode(struct inode *inode)
int ret;
if (inode->i_nlink || !ii->i_root || unlikely(is_bad_inode(inode))) {
- if (inode->i_data.nrpages)
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
nilfs_clear_inode(inode);
return;
}
nilfs_transaction_begin(sb, &ti, 0); /* never fails */
- if (inode->i_data.nrpages)
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
/* TODO: some of the following operations may fail. */
nilfs_truncate_bmap(ii, 0);
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 2b34021948e4..422fb54b7377 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -1072,6 +1072,48 @@ out:
}
/**
+ * nilfs_ioctl_trim_fs() - trim ioctl handle function
+ * @inode: inode object
+ * @argp: pointer on argument from userspace
+ *
+ * Decription: nilfs_ioctl_trim_fs is the FITRIM ioctl handle function. It
+ * checks the arguments from userspace and calls nilfs_sufile_trim_fs, which
+ * performs the actual trim operation.
+ *
+ * Return Value: On success, 0 is returned or negative error code, otherwise.
+ */
+static int nilfs_ioctl_trim_fs(struct inode *inode, void __user *argp)
+{
+ struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
+ struct request_queue *q = bdev_get_queue(nilfs->ns_bdev);
+ struct fstrim_range range;
+ int ret;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!blk_queue_discard(q))
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&range, argp, sizeof(range)))
+ return -EFAULT;
+
+ range.minlen = max_t(u64, range.minlen, q->limits.discard_granularity);
+
+ down_read(&nilfs->ns_segctor_sem);
+ ret = nilfs_sufile_trim_fs(nilfs->ns_sufile, &range);
+ up_read(&nilfs->ns_segctor_sem);
+
+ if (ret < 0)
+ return ret;
+
+ if (copy_to_user(argp, &range, sizeof(range)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
* nilfs_ioctl_set_alloc_range - limit range of segments to be allocated
* @inode: inode object
* @argp: pointer on argument from userspace
@@ -1163,6 +1205,95 @@ static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp,
return ret;
}
+/**
+ * nilfs_ioctl_set_suinfo - set segment usage info
+ * @inode: inode object
+ * @filp: file object
+ * @cmd: ioctl's request code
+ * @argp: pointer on argument from userspace
+ *
+ * Description: Expects an array of nilfs_suinfo_update structures
+ * encapsulated in nilfs_argv and updates the segment usage info
+ * according to the flags in nilfs_suinfo_update.
+ *
+ * Return Value: On success, 0 is returned. On error, one of the
+ * following negative error codes is returned.
+ *
+ * %-EPERM - Not enough permissions
+ *
+ * %-EFAULT - Error copying input data
+ *
+ * %-EIO - I/O error.
+ *
+ * %-ENOMEM - Insufficient amount of memory available.
+ *
+ * %-EINVAL - Invalid values in input (segment number, flags or nblocks)
+ */
+static int nilfs_ioctl_set_suinfo(struct inode *inode, struct file *filp,
+ unsigned int cmd, void __user *argp)
+{
+ struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
+ struct nilfs_transaction_info ti;
+ struct nilfs_argv argv;
+ size_t len;
+ void __user *base;
+ void *kbuf;
+ int ret;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ ret = mnt_want_write_file(filp);
+ if (ret)
+ return ret;
+
+ ret = -EFAULT;
+ if (copy_from_user(&argv, argp, sizeof(argv)))
+ goto out;
+
+ ret = -EINVAL;
+ if (argv.v_size < sizeof(struct nilfs_suinfo_update))
+ goto out;
+
+ if (argv.v_nmembs > nilfs->ns_nsegments)
+ goto out;
+
+ if (argv.v_nmembs >= UINT_MAX / argv.v_size)
+ goto out;
+
+ len = argv.v_size * argv.v_nmembs;
+ if (!len) {
+ ret = 0;
+ goto out;
+ }
+
+ base = (void __user *)(unsigned long)argv.v_base;
+ kbuf = vmalloc(len);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(kbuf, base, len)) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ nilfs_transaction_begin(inode->i_sb, &ti, 0);
+ ret = nilfs_sufile_set_suinfo(nilfs->ns_sufile, kbuf, argv.v_size,
+ argv.v_nmembs);
+ if (unlikely(ret < 0))
+ nilfs_transaction_abort(inode->i_sb);
+ else
+ nilfs_transaction_commit(inode->i_sb); /* never fails */
+
+out_free:
+ vfree(kbuf);
+out:
+ mnt_drop_write_file(filp);
+ return ret;
+}
+
long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file_inode(filp);
@@ -1189,6 +1320,8 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return nilfs_ioctl_get_info(inode, filp, cmd, argp,
sizeof(struct nilfs_suinfo),
nilfs_ioctl_do_get_suinfo);
+ case NILFS_IOCTL_SET_SUINFO:
+ return nilfs_ioctl_set_suinfo(inode, filp, cmd, argp);
case NILFS_IOCTL_GET_SUSTAT:
return nilfs_ioctl_get_sustat(inode, filp, cmd, argp);
case NILFS_IOCTL_GET_VINFO:
@@ -1205,6 +1338,8 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return nilfs_ioctl_resize(inode, filp, argp);
case NILFS_IOCTL_SET_ALLOC_RANGE:
return nilfs_ioctl_set_alloc_range(inode, argp);
+ case FITRIM:
+ return nilfs_ioctl_trim_fs(inode, argp);
default:
return -ENOTTY;
}
@@ -1228,6 +1363,7 @@ long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case NILFS_IOCTL_GET_CPINFO:
case NILFS_IOCTL_GET_CPSTAT:
case NILFS_IOCTL_GET_SUINFO:
+ case NILFS_IOCTL_SET_SUINFO:
case NILFS_IOCTL_GET_SUSTAT:
case NILFS_IOCTL_GET_VINFO:
case NILFS_IOCTL_GET_BDESCS:
@@ -1235,6 +1371,7 @@ long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case NILFS_IOCTL_SYNC:
case NILFS_IOCTL_RESIZE:
case NILFS_IOCTL_SET_ALLOC_RANGE:
+ case FITRIM:
break;
default:
return -ENOIOCTLCMD;
diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c
index 3127e9f438a7..2a869c35c362 100644
--- a/fs/nilfs2/sufile.c
+++ b/fs/nilfs2/sufile.c
@@ -870,6 +870,289 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf,
}
/**
+ * nilfs_sufile_set_suinfo - sets segment usage info
+ * @sufile: inode of segment usage file
+ * @buf: array of suinfo_update
+ * @supsz: byte size of suinfo_update
+ * @nsup: size of suinfo_update array
+ *
+ * Description: Takes an array of nilfs_suinfo_update structs and updates
+ * segment usage accordingly. Only the fields indicated by the sup_flags
+ * are updated.
+ *
+ * Return Value: On success, 0 is returned. On error, one of the
+ * following negative error codes is returned.
+ *
+ * %-EIO - I/O error.
+ *
+ * %-ENOMEM - Insufficient amount of memory available.
+ *
+ * %-EINVAL - Invalid values in input (segment number, flags or nblocks)
+ */
+ssize_t nilfs_sufile_set_suinfo(struct inode *sufile, void *buf,
+ unsigned supsz, size_t nsup)
+{
+ struct the_nilfs *nilfs = sufile->i_sb->s_fs_info;
+ struct buffer_head *header_bh, *bh;
+ struct nilfs_suinfo_update *sup, *supend = buf + supsz * nsup;
+ struct nilfs_segment_usage *su;
+ void *kaddr;
+ unsigned long blkoff, prev_blkoff;
+ int cleansi, cleansu, dirtysi, dirtysu;
+ long ncleaned = 0, ndirtied = 0;
+ int ret = 0;
+
+ if (unlikely(nsup == 0))
+ return ret;
+
+ for (sup = buf; sup < supend; sup = (void *)sup + supsz) {
+ if (sup->sup_segnum >= nilfs->ns_nsegments
+ || (sup->sup_flags &
+ (~0UL << __NR_NILFS_SUINFO_UPDATE_FIELDS))
+ || (nilfs_suinfo_update_nblocks(sup) &&
+ sup->sup_sui.sui_nblocks >
+ nilfs->ns_blocks_per_segment))
+ return -EINVAL;
+ }
+
+ down_write(&NILFS_MDT(sufile)->mi_sem);
+
+ ret = nilfs_sufile_get_header_block(sufile, &header_bh);
+ if (ret < 0)
+ goto out_sem;
+
+ sup = buf;
+ blkoff = nilfs_sufile_get_blkoff(sufile, sup->sup_segnum);
+ ret = nilfs_mdt_get_block(sufile, blkoff, 1, NULL, &bh);
+ if (ret < 0)
+ goto out_header;
+
+ for (;;) {
+ kaddr = kmap_atomic(bh->b_page);
+ su = nilfs_sufile_block_get_segment_usage(
+ sufile, sup->sup_segnum, bh, kaddr);
+
+ if (nilfs_suinfo_update_lastmod(sup))
+ su->su_lastmod = cpu_to_le64(sup->sup_sui.sui_lastmod);
+
+ if (nilfs_suinfo_update_nblocks(sup))
+ su->su_nblocks = cpu_to_le32(sup->sup_sui.sui_nblocks);
+
+ if (nilfs_suinfo_update_flags(sup)) {
+ /*
+ * Active flag is a virtual flag projected by running
+ * nilfs kernel code - drop it not to write it to
+ * disk.
+ */
+ sup->sup_sui.sui_flags &=
+ ~(1UL << NILFS_SEGMENT_USAGE_ACTIVE);
+
+ cleansi = nilfs_suinfo_clean(&sup->sup_sui);
+ cleansu = nilfs_segment_usage_clean(su);
+ dirtysi = nilfs_suinfo_dirty(&sup->sup_sui);
+ dirtysu = nilfs_segment_usage_dirty(su);
+
+ if (cleansi && !cleansu)
+ ++ncleaned;
+ else if (!cleansi && cleansu)
+ --ncleaned;
+
+ if (dirtysi && !dirtysu)
+ ++ndirtied;
+ else if (!dirtysi && dirtysu)
+ --ndirtied;
+
+ su->su_flags = cpu_to_le32(sup->sup_sui.sui_flags);
+ }
+
+ kunmap_atomic(kaddr);
+
+ sup = (void *)sup + supsz;
+ if (sup >= supend)
+ break;
+
+ prev_blkoff = blkoff;
+ blkoff = nilfs_sufile_get_blkoff(sufile, sup->sup_segnum);
+ if (blkoff == prev_blkoff)
+ continue;
+
+ /* get different block */
+ mark_buffer_dirty(bh);
+ put_bh(bh);
+ ret = nilfs_mdt_get_block(sufile, blkoff, 1, NULL, &bh);
+ if (unlikely(ret < 0))
+ goto out_mark;
+ }
+ mark_buffer_dirty(bh);
+ put_bh(bh);
+
+ out_mark:
+ if (ncleaned || ndirtied) {
+ nilfs_sufile_mod_counter(header_bh, (u64)ncleaned,
+ (u64)ndirtied);
+ NILFS_SUI(sufile)->ncleansegs += ncleaned;
+ }
+ nilfs_mdt_mark_dirty(sufile);
+ out_header:
+ put_bh(header_bh);
+ out_sem:
+ up_write(&NILFS_MDT(sufile)->mi_sem);
+ return ret;
+}
+
+/**
+ * nilfs_sufile_trim_fs() - trim ioctl handle function
+ * @sufile: inode of segment usage file
+ * @range: fstrim_range structure
+ *
+ * start: First Byte to trim
+ * len: number of Bytes to trim from start
+ * minlen: minimum extent length in Bytes
+ *
+ * Decription: nilfs_sufile_trim_fs goes through all segments containing bytes
+ * from start to start+len. start is rounded up to the next block boundary
+ * and start+len is rounded down. For each clean segment blkdev_issue_discard
+ * function is invoked.
+ *
+ * Return Value: On success, 0 is returned or negative error code, otherwise.
+ */
+int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range)
+{
+ struct the_nilfs *nilfs = sufile->i_sb->s_fs_info;
+ struct buffer_head *su_bh;
+ struct nilfs_segment_usage *su;
+ void *kaddr;
+ size_t n, i, susz = NILFS_MDT(sufile)->mi_entry_size;
+ sector_t seg_start, seg_end, start_block, end_block;
+ sector_t start = 0, nblocks = 0;
+ u64 segnum, segnum_end, minlen, len, max_blocks, ndiscarded = 0;
+ int ret = 0;
+ unsigned int sects_per_block;
+
+ sects_per_block = (1 << nilfs->ns_blocksize_bits) /
+ bdev_logical_block_size(nilfs->ns_bdev);
+ len = range->len >> nilfs->ns_blocksize_bits;
+ minlen = range->minlen >> nilfs->ns_blocksize_bits;
+ max_blocks = ((u64)nilfs->ns_nsegments * nilfs->ns_blocks_per_segment);
+
+ if (!len || range->start >= max_blocks << nilfs->ns_blocksize_bits)
+ return -EINVAL;
+
+ start_block = (range->start + nilfs->ns_blocksize - 1) >>
+ nilfs->ns_blocksize_bits;
+
+ /*
+ * range->len can be very large (actually, it is set to
+ * ULLONG_MAX by default) - truncate upper end of the range
+ * carefully so as not to overflow.
+ */
+ if (max_blocks - start_block < len)
+ end_block = max_blocks - 1;
+ else
+ end_block = start_block + len - 1;
+
+ segnum = nilfs_get_segnum_of_block(nilfs, start_block);
+ segnum_end = nilfs_get_segnum_of_block(nilfs, end_block);
+
+ down_read(&NILFS_MDT(sufile)->mi_sem);
+
+ while (segnum <= segnum_end) {
+ n = nilfs_sufile_segment_usages_in_block(sufile, segnum,
+ segnum_end);
+
+ ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0,
+ &su_bh);
+ if (ret < 0) {
+ if (ret != -ENOENT)
+ goto out_sem;
+ /* hole */
+ segnum += n;
+ continue;
+ }
+
+ kaddr = kmap_atomic(su_bh->b_page);
+ su = nilfs_sufile_block_get_segment_usage(sufile, segnum,
+ su_bh, kaddr);
+ for (i = 0; i < n; ++i, ++segnum, su = (void *)su + susz) {
+ if (!nilfs_segment_usage_clean(su))
+ continue;
+
+ nilfs_get_segment_range(nilfs, segnum, &seg_start,
+ &seg_end);
+
+ if (!nblocks) {
+ /* start new extent */
+ start = seg_start;
+ nblocks = seg_end - seg_start + 1;
+ continue;
+ }
+
+ if (start + nblocks == seg_start) {
+ /* add to previous extent */
+ nblocks += seg_end - seg_start + 1;
+ continue;
+ }
+
+ /* discard previous extent */
+ if (start < start_block) {
+ nblocks -= start_block - start;
+ start = start_block;
+ }
+
+ if (nblocks >= minlen) {
+ kunmap_atomic(kaddr);
+
+ ret = blkdev_issue_discard(nilfs->ns_bdev,
+ start * sects_per_block,
+ nblocks * sects_per_block,
+ GFP_NOFS, 0);
+ if (ret < 0) {
+ put_bh(su_bh);
+ goto out_sem;
+ }
+
+ ndiscarded += nblocks;
+ kaddr = kmap_atomic(su_bh->b_page);
+ su = nilfs_sufile_block_get_segment_usage(
+ sufile, segnum, su_bh, kaddr);
+ }
+
+ /* start new extent */
+ start = seg_start;
+ nblocks = seg_end - seg_start + 1;
+ }
+ kunmap_atomic(kaddr);
+ put_bh(su_bh);
+ }
+
+
+ if (nblocks) {
+ /* discard last extent */
+ if (start < start_block) {
+ nblocks -= start_block - start;
+ start = start_block;
+ }
+ if (start + nblocks > end_block + 1)
+ nblocks = end_block - start + 1;
+
+ if (nblocks >= minlen) {
+ ret = blkdev_issue_discard(nilfs->ns_bdev,
+ start * sects_per_block,
+ nblocks * sects_per_block,
+ GFP_NOFS, 0);
+ if (!ret)
+ ndiscarded += nblocks;
+ }
+ }
+
+out_sem:
+ up_read(&NILFS_MDT(sufile)->mi_sem);
+
+ range->len = ndiscarded << nilfs->ns_blocksize_bits;
+ return ret;
+}
+
+/**
* nilfs_sufile_read - read or get sufile inode
* @sb: super block instance
* @susize: size of a segment usage entry
@@ -886,6 +1169,18 @@ int nilfs_sufile_read(struct super_block *sb, size_t susize,
void *kaddr;
int err;
+ if (susize > sb->s_blocksize) {
+ printk(KERN_ERR
+ "NILFS: too large segment usage size: %zu bytes.\n",
+ susize);
+ return -EINVAL;
+ } else if (susize < NILFS_MIN_SEGMENT_USAGE_SIZE) {
+ printk(KERN_ERR
+ "NILFS: too small segment usage size: %zu bytes.\n",
+ susize);
+ return -EINVAL;
+ }
+
sufile = nilfs_iget_locked(sb, NULL, NILFS_SUFILE_INO);
if (unlikely(!sufile))
return -ENOMEM;
diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h
index e84bc5b51fc1..b8afd72f2379 100644
--- a/fs/nilfs2/sufile.h
+++ b/fs/nilfs2/sufile.h
@@ -44,6 +44,7 @@ int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum,
int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *);
ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, void *, unsigned,
size_t);
+ssize_t nilfs_sufile_set_suinfo(struct inode *, void *, unsigned , size_t);
int nilfs_sufile_updatev(struct inode *, __u64 *, size_t, int, size_t *,
void (*dofunc)(struct inode *, __u64,
@@ -65,6 +66,7 @@ void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *,
int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs);
int nilfs_sufile_read(struct super_block *sb, size_t susize,
struct nilfs_inode *raw_inode, struct inode **inodep);
+int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range);
/**
* nilfs_sufile_scrap - make a segment garbage
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 7ac2a122ca1d..8c532b2ca3ab 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -1129,6 +1129,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
unsigned long old_mount_opt;
int err;
+ sync_filesystem(sb);
old_sb_flags = sb->s_flags;
old_mount_opt = nilfs->ns_mount_opt;
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
index 94c451ce6d24..8ba8229ba076 100644
--- a/fs/nilfs2/the_nilfs.c
+++ b/fs/nilfs2/the_nilfs.c
@@ -399,6 +399,16 @@ static int nilfs_store_disk_layout(struct the_nilfs *nilfs,
return -EINVAL;
nilfs->ns_inode_size = le16_to_cpu(sbp->s_inode_size);
+ if (nilfs->ns_inode_size > nilfs->ns_blocksize) {
+ printk(KERN_ERR "NILFS: too large inode size: %d bytes.\n",
+ nilfs->ns_inode_size);
+ return -EINVAL;
+ } else if (nilfs->ns_inode_size < NILFS_MIN_INODE_SIZE) {
+ printk(KERN_ERR "NILFS: too small inode size: %d bytes.\n",
+ nilfs->ns_inode_size);
+ return -EINVAL;
+ }
+
nilfs->ns_first_ino = le32_to_cpu(sbp->s_first_ino);
nilfs->ns_blocks_per_segment = le32_to_cpu(sbp->s_blocks_per_segment);
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index dc638f786d5c..ee9cb3795c2b 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -60,8 +60,8 @@ static int fanotify_merge(struct list_head *list, struct fsnotify_event *event)
}
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
-static int fanotify_get_response_from_access(struct fsnotify_group *group,
- struct fanotify_event_info *event)
+static int fanotify_get_response(struct fsnotify_group *group,
+ struct fanotify_perm_event_info *event)
{
int ret;
@@ -142,6 +142,40 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark,
return false;
}
+struct fanotify_event_info *fanotify_alloc_event(struct inode *inode, u32 mask,
+ struct path *path)
+{
+ struct fanotify_event_info *event;
+
+#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
+ if (mask & FAN_ALL_PERM_EVENTS) {
+ struct fanotify_perm_event_info *pevent;
+
+ pevent = kmem_cache_alloc(fanotify_perm_event_cachep,
+ GFP_KERNEL);
+ if (!pevent)
+ return NULL;
+ event = &pevent->fae;
+ pevent->response = 0;
+ goto init;
+ }
+#endif
+ event = kmem_cache_alloc(fanotify_event_cachep, GFP_KERNEL);
+ if (!event)
+ return NULL;
+init: __maybe_unused
+ fsnotify_init_event(&event->fse, inode, mask);
+ event->tgid = get_pid(task_tgid(current));
+ if (path) {
+ event->path = *path;
+ path_get(&event->path);
+ } else {
+ event->path.mnt = NULL;
+ event->path.dentry = NULL;
+ }
+ return event;
+}
+
static int fanotify_handle_event(struct fsnotify_group *group,
struct inode *inode,
struct fsnotify_mark *inode_mark,
@@ -171,25 +205,11 @@ static int fanotify_handle_event(struct fsnotify_group *group,
pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode,
mask);
- event = kmem_cache_alloc(fanotify_event_cachep, GFP_KERNEL);
+ event = fanotify_alloc_event(inode, mask, data);
if (unlikely(!event))
return -ENOMEM;
fsn_event = &event->fse;
- fsnotify_init_event(fsn_event, inode, mask);
- event->tgid = get_pid(task_tgid(current));
- if (data_type == FSNOTIFY_EVENT_PATH) {
- struct path *path = data;
- event->path = *path;
- path_get(&event->path);
- } else {
- event->path.mnt = NULL;
- event->path.dentry = NULL;
- }
-#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
- event->response = 0;
-#endif
-
ret = fsnotify_add_notify_event(group, fsn_event, fanotify_merge);
if (ret) {
/* Permission events shouldn't be merged */
@@ -202,7 +222,7 @@ static int fanotify_handle_event(struct fsnotify_group *group,
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
if (mask & FAN_ALL_PERM_EVENTS) {
- ret = fanotify_get_response_from_access(group, event);
+ ret = fanotify_get_response(group, FANOTIFY_PE(fsn_event));
fsnotify_destroy_event(group, fsn_event);
}
#endif
@@ -225,6 +245,13 @@ static void fanotify_free_event(struct fsnotify_event *fsn_event)
event = FANOTIFY_E(fsn_event);
path_put(&event->path);
put_pid(event->tgid);
+#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
+ if (fsn_event->mask & FAN_ALL_PERM_EVENTS) {
+ kmem_cache_free(fanotify_perm_event_cachep,
+ FANOTIFY_PE(fsn_event));
+ return;
+ }
+#endif
kmem_cache_free(fanotify_event_cachep, event);
}
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 32a2f034fb94..2a5fb14115df 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -3,13 +3,12 @@
#include <linux/slab.h>
extern struct kmem_cache *fanotify_event_cachep;
+extern struct kmem_cache *fanotify_perm_event_cachep;
/*
- * Lifetime of the structure differs for normal and permission events. In both
- * cases the structure is allocated in fanotify_handle_event(). For normal
- * events the structure is freed immediately after reporting it to userspace.
- * For permission events we free it only after we receive response from
- * userspace.
+ * Structure for normal fanotify events. It gets allocated in
+ * fanotify_handle_event() and freed when the information is retrieved by
+ * userspace
*/
struct fanotify_event_info {
struct fsnotify_event fse;
@@ -19,12 +18,33 @@ struct fanotify_event_info {
*/
struct path path;
struct pid *tgid;
+};
+
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
- u32 response; /* userspace answer to question */
-#endif
+/*
+ * Structure for permission fanotify events. It gets allocated and freed in
+ * fanotify_handle_event() since we wait there for user response. When the
+ * information is retrieved by userspace the structure is moved from
+ * group->notification_list to group->fanotify_data.access_list to wait for
+ * user response.
+ */
+struct fanotify_perm_event_info {
+ struct fanotify_event_info fae;
+ int response; /* userspace answer to question */
+ int fd; /* fd we passed to userspace for this event */
};
+static inline struct fanotify_perm_event_info *
+FANOTIFY_PE(struct fsnotify_event *fse)
+{
+ return container_of(fse, struct fanotify_perm_event_info, fae.fse);
+}
+#endif
+
static inline struct fanotify_event_info *FANOTIFY_E(struct fsnotify_event *fse)
{
return container_of(fse, struct fanotify_event_info, fse);
}
+
+struct fanotify_event_info *fanotify_alloc_event(struct inode *inode, u32 mask,
+ struct path *path);
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 287a22c04149..4e565c814309 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -28,14 +28,8 @@
extern const struct fsnotify_ops fanotify_fsnotify_ops;
static struct kmem_cache *fanotify_mark_cache __read_mostly;
-static struct kmem_cache *fanotify_response_event_cache __read_mostly;
struct kmem_cache *fanotify_event_cachep __read_mostly;
-
-struct fanotify_response_event {
- struct list_head list;
- __s32 fd;
- struct fanotify_event_info *event;
-};
+struct kmem_cache *fanotify_perm_event_cachep __read_mostly;
/*
* Get an fsnotify notification event if one exists and is small
@@ -135,33 +129,34 @@ static int fill_event_metadata(struct fsnotify_group *group,
}
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
-static struct fanotify_response_event *dequeue_re(struct fsnotify_group *group,
- __s32 fd)
+static struct fanotify_perm_event_info *dequeue_event(
+ struct fsnotify_group *group, int fd)
{
- struct fanotify_response_event *re, *return_re = NULL;
+ struct fanotify_perm_event_info *event, *return_e = NULL;
- mutex_lock(&group->fanotify_data.access_mutex);
- list_for_each_entry(re, &group->fanotify_data.access_list, list) {
- if (re->fd != fd)
+ spin_lock(&group->fanotify_data.access_lock);
+ list_for_each_entry(event, &group->fanotify_data.access_list,
+ fae.fse.list) {
+ if (event->fd != fd)
continue;
- list_del_init(&re->list);
- return_re = re;
+ list_del_init(&event->fae.fse.list);
+ return_e = event;
break;
}
- mutex_unlock(&group->fanotify_data.access_mutex);
+ spin_unlock(&group->fanotify_data.access_lock);
- pr_debug("%s: found return_re=%p\n", __func__, return_re);
+ pr_debug("%s: found return_re=%p\n", __func__, return_e);
- return return_re;
+ return return_e;
}
static int process_access_response(struct fsnotify_group *group,
struct fanotify_response *response_struct)
{
- struct fanotify_response_event *re;
- __s32 fd = response_struct->fd;
- __u32 response = response_struct->response;
+ struct fanotify_perm_event_info *event;
+ int fd = response_struct->fd;
+ int response = response_struct->response;
pr_debug("%s: group=%p fd=%d response=%d\n", __func__, group,
fd, response);
@@ -181,58 +176,15 @@ static int process_access_response(struct fsnotify_group *group,
if (fd < 0)
return -EINVAL;
- re = dequeue_re(group, fd);
- if (!re)
+ event = dequeue_event(group, fd);
+ if (!event)
return -ENOENT;
- re->event->response = response;
-
+ event->response = response;
wake_up(&group->fanotify_data.access_waitq);
- kmem_cache_free(fanotify_response_event_cache, re);
-
- return 0;
-}
-
-static int prepare_for_access_response(struct fsnotify_group *group,
- struct fsnotify_event *event,
- __s32 fd)
-{
- struct fanotify_response_event *re;
-
- if (!(event->mask & FAN_ALL_PERM_EVENTS))
- return 0;
-
- re = kmem_cache_alloc(fanotify_response_event_cache, GFP_KERNEL);
- if (!re)
- return -ENOMEM;
-
- re->event = FANOTIFY_E(event);
- re->fd = fd;
-
- mutex_lock(&group->fanotify_data.access_mutex);
-
- if (atomic_read(&group->fanotify_data.bypass_perm)) {
- mutex_unlock(&group->fanotify_data.access_mutex);
- kmem_cache_free(fanotify_response_event_cache, re);
- FANOTIFY_E(event)->response = FAN_ALLOW;
- return 0;
- }
-
- list_add_tail(&re->list, &group->fanotify_data.access_list);
- mutex_unlock(&group->fanotify_data.access_mutex);
-
- return 0;
-}
-
-#else
-static int prepare_for_access_response(struct fsnotify_group *group,
- struct fsnotify_event *event,
- __s32 fd)
-{
return 0;
}
-
#endif
static ssize_t copy_event_to_user(struct fsnotify_group *group,
@@ -247,7 +199,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
ret = fill_event_metadata(group, &fanotify_event_metadata, event, &f);
if (ret < 0)
- goto out;
+ return ret;
fd = fanotify_event_metadata.fd;
ret = -EFAULT;
@@ -255,9 +207,10 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
fanotify_event_metadata.event_len))
goto out_close_fd;
- ret = prepare_for_access_response(group, event, fd);
- if (ret)
- goto out_close_fd;
+#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
+ if (event->mask & FAN_ALL_PERM_EVENTS)
+ FANOTIFY_PE(event)->fd = fd;
+#endif
if (fd != FAN_NOFD)
fd_install(fd, f);
@@ -268,13 +221,6 @@ out_close_fd:
put_unused_fd(fd);
fput(f);
}
-out:
-#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
- if (event->mask & FAN_ALL_PERM_EVENTS) {
- FANOTIFY_E(event)->response = FAN_DENY;
- wake_up(&group->fanotify_data.access_waitq);
- }
-#endif
return ret;
}
@@ -314,35 +260,50 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
kevent = get_one_event(group, count);
mutex_unlock(&group->notification_mutex);
- if (kevent) {
+ if (IS_ERR(kevent)) {
ret = PTR_ERR(kevent);
- if (IS_ERR(kevent))
+ break;
+ }
+
+ if (!kevent) {
+ ret = -EAGAIN;
+ if (file->f_flags & O_NONBLOCK)
break;
- ret = copy_event_to_user(group, kevent, buf);
- /*
- * Permission events get destroyed after we
- * receive response
- */
- if (!(kevent->mask & FAN_ALL_PERM_EVENTS))
- fsnotify_destroy_event(group, kevent);
- if (ret < 0)
+
+ ret = -ERESTARTSYS;
+ if (signal_pending(current))
+ break;
+
+ if (start != buf)
break;
- buf += ret;
- count -= ret;
+ schedule();
continue;
}
- ret = -EAGAIN;
- if (file->f_flags & O_NONBLOCK)
- break;
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- break;
-
- if (start != buf)
- break;
-
- schedule();
+ ret = copy_event_to_user(group, kevent, buf);
+ /*
+ * Permission events get queued to wait for response. Other
+ * events can be destroyed now.
+ */
+ if (!(kevent->mask & FAN_ALL_PERM_EVENTS)) {
+ fsnotify_destroy_event(group, kevent);
+ if (ret < 0)
+ break;
+ } else {
+#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
+ if (ret < 0) {
+ FANOTIFY_PE(kevent)->response = FAN_DENY;
+ wake_up(&group->fanotify_data.access_waitq);
+ break;
+ }
+ spin_lock(&group->fanotify_data.access_lock);
+ list_add_tail(&kevent->list,
+ &group->fanotify_data.access_list);
+ spin_unlock(&group->fanotify_data.access_lock);
+#endif
+ }
+ buf += ret;
+ count -= ret;
}
finish_wait(&group->notification_waitq, &wait);
@@ -383,22 +344,21 @@ static int fanotify_release(struct inode *ignored, struct file *file)
struct fsnotify_group *group = file->private_data;
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
- struct fanotify_response_event *re, *lre;
+ struct fanotify_perm_event_info *event, *next;
- mutex_lock(&group->fanotify_data.access_mutex);
+ spin_lock(&group->fanotify_data.access_lock);
atomic_inc(&group->fanotify_data.bypass_perm);
- list_for_each_entry_safe(re, lre, &group->fanotify_data.access_list, list) {
- pr_debug("%s: found group=%p re=%p event=%p\n", __func__, group,
- re, re->event);
+ list_for_each_entry_safe(event, next, &group->fanotify_data.access_list,
+ fae.fse.list) {
+ pr_debug("%s: found group=%p event=%p\n", __func__, group,
+ event);
- list_del_init(&re->list);
- re->event->response = FAN_ALLOW;
-
- kmem_cache_free(fanotify_response_event_cache, re);
+ list_del_init(&event->fae.fse.list);
+ event->response = FAN_ALLOW;
}
- mutex_unlock(&group->fanotify_data.access_mutex);
+ spin_unlock(&group->fanotify_data.access_lock);
wake_up(&group->fanotify_data.access_waitq);
#endif
@@ -731,21 +691,16 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
group->fanotify_data.user = user;
atomic_inc(&user->fanotify_listeners);
- oevent = kmem_cache_alloc(fanotify_event_cachep, GFP_KERNEL);
+ oevent = fanotify_alloc_event(NULL, FS_Q_OVERFLOW, NULL);
if (unlikely(!oevent)) {
fd = -ENOMEM;
goto out_destroy_group;
}
group->overflow_event = &oevent->fse;
- fsnotify_init_event(group->overflow_event, NULL, FS_Q_OVERFLOW);
- oevent->tgid = get_pid(task_tgid(current));
- oevent->path.mnt = NULL;
- oevent->path.dentry = NULL;
group->fanotify_data.f_flags = event_f_flags;
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
- oevent->response = 0;
- mutex_init(&group->fanotify_data.access_mutex);
+ spin_lock_init(&group->fanotify_data.access_lock);
init_waitqueue_head(&group->fanotify_data.access_waitq);
INIT_LIST_HEAD(&group->fanotify_data.access_list);
atomic_set(&group->fanotify_data.bypass_perm, 0);
@@ -920,9 +875,11 @@ COMPAT_SYSCALL_DEFINE6(fanotify_mark,
static int __init fanotify_user_setup(void)
{
fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC);
- fanotify_response_event_cache = KMEM_CACHE(fanotify_response_event,
- SLAB_PANIC);
fanotify_event_cachep = KMEM_CACHE(fanotify_event_info, SLAB_PANIC);
+#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
+ fanotify_perm_event_cachep = KMEM_CACHE(fanotify_perm_event_info,
+ SLAB_PANIC);
+#endif
return 0;
}
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index ffb9b3675736..9d8153ebacfb 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -2259,7 +2259,7 @@ void ntfs_evict_big_inode(struct inode *vi)
{
ntfs_inode *ni = NTFS_I(vi);
- truncate_inode_pages(&vi->i_data, 0);
+ truncate_inode_pages_final(&vi->i_data);
clear_inode(vi);
#ifdef NTFS_RW
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 82650d52d916..bd5610d48242 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -468,6 +468,8 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt)
ntfs_debug("Entering with remount options string: %s", opt);
+ sync_filesystem(sb);
+
#ifndef NTFS_RW
/* For read-only compiled driver, enforce read-only flag. */
*flags |= MS_RDONLY;
diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c
index 555f4cddefe3..7e8282dcea2a 100644
--- a/fs/ocfs2/acl.c
+++ b/fs/ocfs2/acl.c
@@ -205,6 +205,7 @@ static int ocfs2_acl_set_mode(struct inode *inode, struct buffer_head *di_bh,
di->i_mode = cpu_to_le16(inode->i_mode);
di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
+ ocfs2_update_inode_fsync_trans(handle, inode, 0);
ocfs2_journal_dirty(handle, di_bh);
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index e2edff38be52..b4deb5f750d9 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -5728,6 +5728,7 @@ int ocfs2_remove_btree_range(struct inode *inode,
}
ocfs2_et_update_clusters(et, -len);
+ ocfs2_update_inode_fsync_trans(handle, inode, 1);
ocfs2_journal_dirty(handle, et->et_root_bh);
@@ -6932,6 +6933,7 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,
di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features);
spin_unlock(&oi->ip_lock);
+ ocfs2_update_inode_fsync_trans(handle, inode, 1);
ocfs2_dinode_new_extent_list(inode, di);
ocfs2_journal_dirty(handle, di_bh);
@@ -7208,6 +7210,7 @@ int ocfs2_truncate_inline(struct inode *inode, struct buffer_head *di_bh,
di->i_ctime = di->i_mtime = cpu_to_le64(inode->i_ctime.tv_sec);
di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
+ ocfs2_update_inode_fsync_trans(handle, inode, 1);
ocfs2_journal_dirty(handle, di_bh);
out_commit:
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index aeb44e879c51..d310d12a9adc 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -571,7 +571,6 @@ static void ocfs2_dio_end_io(struct kiocb *iocb,
{
struct inode *inode = file_inode(iocb->ki_filp);
int level;
- wait_queue_head_t *wq = ocfs2_ioend_wq(inode);
/* this io's submitter should not have unlocked this before we could */
BUG_ON(!ocfs2_iocb_is_rw_locked(iocb));
@@ -582,10 +581,7 @@ static void ocfs2_dio_end_io(struct kiocb *iocb,
if (ocfs2_iocb_is_unaligned_aio(iocb)) {
ocfs2_iocb_clear_unaligned_aio(iocb);
- if (atomic_dec_and_test(&OCFS2_I(inode)->ip_unaligned_aio) &&
- waitqueue_active(wq)) {
- wake_up_all(wq);
- }
+ mutex_unlock(&OCFS2_I(inode)->ip_unaligned_aio);
}
ocfs2_iocb_clear_rw_locked(iocb);
@@ -2043,6 +2039,7 @@ out_write_size:
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec);
di->i_mtime_nsec = di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
+ ocfs2_update_inode_fsync_trans(handle, inode, 1);
ocfs2_journal_dirty(handle, wc->w_di_bh);
ocfs2_commit_trans(osb, handle);
diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h
index f671e49beb34..6cae155d54df 100644
--- a/fs/ocfs2/aops.h
+++ b/fs/ocfs2/aops.h
@@ -102,9 +102,4 @@ enum ocfs2_iocb_lock_bits {
#define ocfs2_iocb_is_unaligned_aio(iocb) \
test_bit(OCFS2_IOCB_UNALIGNED_IO, (unsigned long *)&iocb->private)
-#define OCFS2_IOEND_WQ_HASH_SZ 37
-#define ocfs2_ioend_wq(v) (&ocfs2__ioend_wq[((unsigned long)(v)) %\
- OCFS2_IOEND_WQ_HASH_SZ])
-extern wait_queue_head_t ocfs2__ioend_wq[OCFS2_IOEND_WQ_HASH_SZ];
-
#endif /* OCFS2_FILE_H */
diff --git a/fs/ocfs2/buffer_head_io.c b/fs/ocfs2/buffer_head_io.c
index 5b704c63a103..1edcb141f639 100644
--- a/fs/ocfs2/buffer_head_io.c
+++ b/fs/ocfs2/buffer_head_io.c
@@ -90,7 +90,6 @@ int ocfs2_write_block(struct ocfs2_super *osb, struct buffer_head *bh,
* information for this bh as it's not marked locally
* uptodate. */
ret = -EIO;
- put_bh(bh);
mlog_errno(ret);
}
@@ -420,7 +419,6 @@ int ocfs2_write_super_or_backup(struct ocfs2_super *osb,
if (!buffer_uptodate(bh)) {
ret = -EIO;
- put_bh(bh);
mlog_errno(ret);
}
diff --git a/fs/ocfs2/cluster/sys.c b/fs/ocfs2/cluster/sys.c
index a4b07730b2e1..b7f57271d49c 100644
--- a/fs/ocfs2/cluster/sys.c
+++ b/fs/ocfs2/cluster/sys.c
@@ -41,7 +41,7 @@ static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr,
return snprintf(buf, PAGE_SIZE, "%u\n", O2NM_API_VERSION);
}
static struct kobj_attribute attr_version =
- __ATTR(interface_revision, S_IFREG | S_IRUGO, version_show, NULL);
+ __ATTR(interface_revision, S_IRUGO, version_show, NULL);
static struct attribute *o2cb_attrs[] = {
&attr_version.attr,
diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c
index 2cd2406b4140..eb649d23a4de 100644
--- a/fs/ocfs2/cluster/tcp.c
+++ b/fs/ocfs2/cluster/tcp.c
@@ -262,17 +262,17 @@ static void o2net_update_recv_stats(struct o2net_sock_container *sc)
#endif /* CONFIG_OCFS2_FS_STATS */
-static inline int o2net_reconnect_delay(void)
+static inline unsigned int o2net_reconnect_delay(void)
{
return o2nm_single_cluster->cl_reconnect_delay_ms;
}
-static inline int o2net_keepalive_delay(void)
+static inline unsigned int o2net_keepalive_delay(void)
{
return o2nm_single_cluster->cl_keepalive_delay_ms;
}
-static inline int o2net_idle_timeout(void)
+static inline unsigned int o2net_idle_timeout(void)
{
return o2nm_single_cluster->cl_idle_timeout_ms;
}
@@ -1964,18 +1964,30 @@ static void o2net_listen_data_ready(struct sock *sk, int bytes)
goto out;
}
- /* ->sk_data_ready is also called for a newly established child socket
- * before it has been accepted and the acceptor has set up their
- * data_ready.. we only want to queue listen work for our listening
- * socket */
+ /* This callback may called twice when a new connection
+ * is being established as a child socket inherits everything
+ * from a parent LISTEN socket, including the data_ready cb of
+ * the parent. This leads to a hazard. In o2net_accept_one()
+ * we are still initializing the child socket but have not
+ * changed the inherited data_ready callback yet when
+ * data starts arriving.
+ * We avoid this hazard by checking the state.
+ * For the listening socket, the state will be TCP_LISTEN; for the new
+ * socket, will be TCP_ESTABLISHED. Also, in this case,
+ * sk->sk_user_data is not a valid function pointer.
+ */
+
if (sk->sk_state == TCP_LISTEN) {
mlog(ML_TCP, "bytes: %d\n", bytes);
queue_work(o2net_wq, &o2net_listen_work);
+ } else {
+ ready = NULL;
}
out:
read_unlock(&sk->sk_callback_lock);
- ready(sk, bytes);
+ if (ready != NULL)
+ ready(sk, bytes);
}
static int o2net_open_listening_sock(__be32 addr, __be16 port)
diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c
index 0d3a97d2d5f6..e2e05a106beb 100644
--- a/fs/ocfs2/dcache.c
+++ b/fs/ocfs2/dcache.c
@@ -37,7 +37,6 @@
#include "dlmglue.h"
#include "file.h"
#include "inode.h"
-#include "super.h"
#include "ocfs2_trace.h"
void ocfs2_dentry_attach_gen(struct dentry *dentry)
@@ -346,52 +345,6 @@ out_attach:
return ret;
}
-DEFINE_SPINLOCK(dentry_list_lock);
-
-/* We limit the number of dentry locks to drop in one go. We have
- * this limit so that we don't starve other users of ocfs2_wq. */
-#define DL_INODE_DROP_COUNT 64
-
-/* Drop inode references from dentry locks */
-static void __ocfs2_drop_dl_inodes(struct ocfs2_super *osb, int drop_count)
-{
- struct ocfs2_dentry_lock *dl;
-
- spin_lock(&dentry_list_lock);
- while (osb->dentry_lock_list && (drop_count < 0 || drop_count--)) {
- dl = osb->dentry_lock_list;
- osb->dentry_lock_list = dl->dl_next;
- spin_unlock(&dentry_list_lock);
- iput(dl->dl_inode);
- kfree(dl);
- spin_lock(&dentry_list_lock);
- }
- spin_unlock(&dentry_list_lock);
-}
-
-void ocfs2_drop_dl_inodes(struct work_struct *work)
-{
- struct ocfs2_super *osb = container_of(work, struct ocfs2_super,
- dentry_lock_work);
-
- __ocfs2_drop_dl_inodes(osb, DL_INODE_DROP_COUNT);
- /*
- * Don't queue dropping if umount is in progress. We flush the
- * list in ocfs2_dismount_volume
- */
- spin_lock(&dentry_list_lock);
- if (osb->dentry_lock_list &&
- !ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED))
- queue_work(ocfs2_wq, &osb->dentry_lock_work);
- spin_unlock(&dentry_list_lock);
-}
-
-/* Flush the whole work queue */
-void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb)
-{
- __ocfs2_drop_dl_inodes(osb, -1);
-}
-
/*
* ocfs2_dentry_iput() and friends.
*
@@ -416,24 +369,16 @@ void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb)
static void ocfs2_drop_dentry_lock(struct ocfs2_super *osb,
struct ocfs2_dentry_lock *dl)
{
+ iput(dl->dl_inode);
ocfs2_simple_drop_lockres(osb, &dl->dl_lockres);
ocfs2_lock_res_free(&dl->dl_lockres);
-
- /* We leave dropping of inode reference to ocfs2_wq as that can
- * possibly lead to inode deletion which gets tricky */
- spin_lock(&dentry_list_lock);
- if (!osb->dentry_lock_list &&
- !ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED))
- queue_work(ocfs2_wq, &osb->dentry_lock_work);
- dl->dl_next = osb->dentry_lock_list;
- osb->dentry_lock_list = dl;
- spin_unlock(&dentry_list_lock);
+ kfree(dl);
}
void ocfs2_dentry_lock_put(struct ocfs2_super *osb,
struct ocfs2_dentry_lock *dl)
{
- int unlock;
+ int unlock = 0;
BUG_ON(dl->dl_count == 0);
diff --git a/fs/ocfs2/dcache.h b/fs/ocfs2/dcache.h
index b79eff709958..55f58892b153 100644
--- a/fs/ocfs2/dcache.h
+++ b/fs/ocfs2/dcache.h
@@ -29,13 +29,8 @@
extern const struct dentry_operations ocfs2_dentry_ops;
struct ocfs2_dentry_lock {
- /* Use count of dentry lock */
unsigned int dl_count;
- union {
- /* Linked list of dentry locks to release */
- struct ocfs2_dentry_lock *dl_next;
- u64 dl_parent_blkno;
- };
+ u64 dl_parent_blkno;
/*
* The ocfs2_dentry_lock keeps an inode reference until
@@ -49,14 +44,9 @@ struct ocfs2_dentry_lock {
int ocfs2_dentry_attach_lock(struct dentry *dentry, struct inode *inode,
u64 parent_blkno);
-extern spinlock_t dentry_list_lock;
-
void ocfs2_dentry_lock_put(struct ocfs2_super *osb,
struct ocfs2_dentry_lock *dl);
-void ocfs2_drop_dl_inodes(struct work_struct *work);
-void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb);
-
struct dentry *ocfs2_find_local_alias(struct inode *inode, u64 parent_blkno,
int skip_unhashed);
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index 91a7e85ac8fd..0717662b4aef 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -2957,6 +2957,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,
ocfs2_init_dir_trailer(dir, dirdata_bh, i);
}
+ ocfs2_update_inode_fsync_trans(handle, dir, 1);
ocfs2_journal_dirty(handle, dirdata_bh);
if (ocfs2_supports_indexed_dirs(osb) && !dx_inline) {
@@ -3005,6 +3006,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,
di->i_size = cpu_to_le64(sb->s_blocksize);
di->i_ctime = di->i_mtime = cpu_to_le64(dir->i_ctime.tv_sec);
di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(dir->i_ctime.tv_nsec);
+ ocfs2_update_inode_fsync_trans(handle, dir, 1);
/*
* This should never fail as our extent list is empty and all
@@ -3338,6 +3340,7 @@ do_extend:
} else {
de->rec_len = cpu_to_le16(sb->s_blocksize);
}
+ ocfs2_update_inode_fsync_trans(handle, dir, 1);
ocfs2_journal_dirty(handle, new_bh);
dir_i_size += dir->i_sb->s_blocksize;
@@ -3896,6 +3899,7 @@ out_commit:
dquot_free_space_nodirty(dir,
ocfs2_clusters_to_bytes(dir->i_sb, 1));
+ ocfs2_update_inode_fsync_trans(handle, dir, 1);
ocfs2_commit_trans(osb, handle);
out:
@@ -4134,6 +4138,7 @@ static int ocfs2_expand_inline_dx_root(struct inode *dir,
mlog_errno(ret);
did_quota = 0;
+ ocfs2_update_inode_fsync_trans(handle, dir, 1);
ocfs2_journal_dirty(handle, dx_root_bh);
out_commit:
@@ -4401,6 +4406,7 @@ static int ocfs2_dx_dir_remove_index(struct inode *dir,
di->i_dyn_features = cpu_to_le16(OCFS2_I(dir)->ip_dyn_features);
spin_unlock(&OCFS2_I(dir)->ip_lock);
di->i_dx_root = cpu_to_le64(0ULL);
+ ocfs2_update_inode_fsync_trans(handle, dir, 1);
ocfs2_journal_dirty(handle, di_bh);
diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c
index 33660a4a52fa..c973690dc0bc 100644
--- a/fs/ocfs2/dlm/dlmdomain.c
+++ b/fs/ocfs2/dlm/dlmdomain.c
@@ -1123,7 +1123,6 @@ static int dlm_query_region_handler(struct o2net_msg *msg, u32 len,
struct dlm_ctxt *dlm = NULL;
char *local = NULL;
int status = 0;
- int locked = 0;
qr = (struct dlm_query_region *) msg->buf;
@@ -1132,10 +1131,8 @@ static int dlm_query_region_handler(struct o2net_msg *msg, u32 len,
/* buffer used in dlm_mast_regions() */
local = kmalloc(sizeof(qr->qr_regions), GFP_KERNEL);
- if (!local) {
- status = -ENOMEM;
- goto bail;
- }
+ if (!local)
+ return -ENOMEM;
status = -EINVAL;
@@ -1144,16 +1141,15 @@ static int dlm_query_region_handler(struct o2net_msg *msg, u32 len,
if (!dlm) {
mlog(ML_ERROR, "Node %d queried hb regions on domain %s "
"before join domain\n", qr->qr_node, qr->qr_domain);
- goto bail;
+ goto out_domain_lock;
}
spin_lock(&dlm->spinlock);
- locked = 1;
if (dlm->joining_node != qr->qr_node) {
mlog(ML_ERROR, "Node %d queried hb regions on domain %s "
"but joining node is %d\n", qr->qr_node, qr->qr_domain,
dlm->joining_node);
- goto bail;
+ goto out_dlm_lock;
}
/* Support for global heartbeat was added in 1.1 */
@@ -1163,14 +1159,15 @@ static int dlm_query_region_handler(struct o2net_msg *msg, u32 len,
"but active dlm protocol is %d.%d\n", qr->qr_node,
qr->qr_domain, dlm->dlm_locking_proto.pv_major,
dlm->dlm_locking_proto.pv_minor);
- goto bail;
+ goto out_dlm_lock;
}
status = dlm_match_regions(dlm, qr, local, sizeof(qr->qr_regions));
-bail:
- if (locked)
- spin_unlock(&dlm->spinlock);
+out_dlm_lock:
+ spin_unlock(&dlm->spinlock);
+
+out_domain_lock:
spin_unlock(&dlm_domain_lock);
kfree(local);
@@ -1877,19 +1874,19 @@ static int dlm_join_domain(struct dlm_ctxt *dlm)
goto bail;
}
- status = dlm_debug_init(dlm);
+ status = dlm_launch_thread(dlm);
if (status < 0) {
mlog_errno(status);
goto bail;
}
- status = dlm_launch_thread(dlm);
+ status = dlm_launch_recovery_thread(dlm);
if (status < 0) {
mlog_errno(status);
goto bail;
}
- status = dlm_launch_recovery_thread(dlm);
+ status = dlm_debug_init(dlm);
if (status < 0) {
mlog_errno(status);
goto bail;
diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c
index 7035af09cc03..fe29f7978f81 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -537,7 +537,10 @@ master_here:
/* success! see if any other nodes need recovery */
mlog(0, "DONE mastering recovery of %s:%u here(this=%u)!\n",
dlm->name, dlm->reco.dead_node, dlm->node_num);
- dlm_reset_recovery(dlm);
+ spin_lock(&dlm->spinlock);
+ __dlm_reset_recovery(dlm);
+ dlm->reco.state &= ~DLM_RECO_STATE_FINALIZE;
+ spin_unlock(&dlm->spinlock);
}
dlm_end_recovery(dlm);
@@ -695,6 +698,14 @@ static int dlm_remaster_locks(struct dlm_ctxt *dlm, u8 dead_node)
if (all_nodes_done) {
int ret;
+ /* Set this flag on recovery master to avoid
+ * a new recovery for another dead node start
+ * before the recovery is not done. That may
+ * cause recovery hung.*/
+ spin_lock(&dlm->spinlock);
+ dlm->reco.state |= DLM_RECO_STATE_FINALIZE;
+ spin_unlock(&dlm->spinlock);
+
/* all nodes are now in DLM_RECO_NODE_DATA_DONE state
* just send a finalize message to everyone and
* clean up */
@@ -1750,13 +1761,13 @@ static int dlm_process_recovery_data(struct dlm_ctxt *dlm,
struct dlm_migratable_lockres *mres)
{
struct dlm_migratable_lock *ml;
- struct list_head *queue;
+ struct list_head *queue, *iter;
struct list_head *tmpq = NULL;
struct dlm_lock *newlock = NULL;
struct dlm_lockstatus *lksb = NULL;
int ret = 0;
int i, j, bad;
- struct dlm_lock *lock = NULL;
+ struct dlm_lock *lock;
u8 from = O2NM_MAX_NODES;
unsigned int added = 0;
__be64 c;
@@ -1791,14 +1802,16 @@ static int dlm_process_recovery_data(struct dlm_ctxt *dlm,
/* MIGRATION ONLY! */
BUG_ON(!(mres->flags & DLM_MRES_MIGRATION));
+ lock = NULL;
spin_lock(&res->spinlock);
for (j = DLM_GRANTED_LIST; j <= DLM_BLOCKED_LIST; j++) {
tmpq = dlm_list_idx_to_ptr(res, j);
- list_for_each_entry(lock, tmpq, list) {
- if (lock->ml.cookie != ml->cookie)
- lock = NULL;
- else
+ list_for_each(iter, tmpq) {
+ lock = list_entry(iter,
+ struct dlm_lock, list);
+ if (lock->ml.cookie == ml->cookie)
break;
+ lock = NULL;
}
if (lock)
break;
@@ -2882,8 +2895,8 @@ int dlm_finalize_reco_handler(struct o2net_msg *msg, u32 len, void *data,
BUG();
}
dlm->reco.state &= ~DLM_RECO_STATE_FINALIZE;
+ __dlm_reset_recovery(dlm);
spin_unlock(&dlm->spinlock);
- dlm_reset_recovery(dlm);
dlm_kick_recovery_thread(dlm);
break;
default:
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 19986959d149..6bd690b5a061 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -3144,22 +3144,60 @@ out:
return 0;
}
+static void ocfs2_process_blocked_lock(struct ocfs2_super *osb,
+ struct ocfs2_lock_res *lockres);
+
/* Mark the lockres as being dropped. It will no longer be
* queued if blocking, but we still may have to wait on it
* being dequeued from the downconvert thread before we can consider
* it safe to drop.
*
* You can *not* attempt to call cluster_lock on this lockres anymore. */
-void ocfs2_mark_lockres_freeing(struct ocfs2_lock_res *lockres)
+void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb,
+ struct ocfs2_lock_res *lockres)
{
int status;
struct ocfs2_mask_waiter mw;
- unsigned long flags;
+ unsigned long flags, flags2;
ocfs2_init_mask_waiter(&mw);
spin_lock_irqsave(&lockres->l_lock, flags);
lockres->l_flags |= OCFS2_LOCK_FREEING;
+ if (lockres->l_flags & OCFS2_LOCK_QUEUED && current == osb->dc_task) {
+ /*
+ * We know the downconvert is queued but not in progress
+ * because we are the downconvert thread and processing
+ * different lock. So we can just remove the lock from the
+ * queue. This is not only an optimization but also a way
+ * to avoid the following deadlock:
+ * ocfs2_dentry_post_unlock()
+ * ocfs2_dentry_lock_put()
+ * ocfs2_drop_dentry_lock()
+ * iput()
+ * ocfs2_evict_inode()
+ * ocfs2_clear_inode()
+ * ocfs2_mark_lockres_freeing()
+ * ... blocks waiting for OCFS2_LOCK_QUEUED
+ * since we are the downconvert thread which
+ * should clear the flag.
+ */
+ spin_unlock_irqrestore(&lockres->l_lock, flags);
+ spin_lock_irqsave(&osb->dc_task_lock, flags2);
+ list_del_init(&lockres->l_blocked_list);
+ osb->blocked_lock_count--;
+ spin_unlock_irqrestore(&osb->dc_task_lock, flags2);
+ /*
+ * Warn if we recurse into another post_unlock call. Strictly
+ * speaking it isn't a problem but we need to be careful if
+ * that happens (stack overflow, deadlocks, ...) so warn if
+ * ocfs2 grows a path for which this can happen.
+ */
+ WARN_ON_ONCE(lockres->l_ops->post_unlock);
+ /* Since the lock is freeing we don't do much in the fn below */
+ ocfs2_process_blocked_lock(osb, lockres);
+ return;
+ }
while (lockres->l_flags & OCFS2_LOCK_QUEUED) {
lockres_add_mask_waiter(lockres, &mw, OCFS2_LOCK_QUEUED, 0);
spin_unlock_irqrestore(&lockres->l_lock, flags);
@@ -3180,7 +3218,7 @@ void ocfs2_simple_drop_lockres(struct ocfs2_super *osb,
{
int ret;
- ocfs2_mark_lockres_freeing(lockres);
+ ocfs2_mark_lockres_freeing(osb, lockres);
ret = ocfs2_drop_lock(osb, lockres);
if (ret)
mlog_errno(ret);
diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h
index 1d596d8c4a4a..d293a22c32c5 100644
--- a/fs/ocfs2/dlmglue.h
+++ b/fs/ocfs2/dlmglue.h
@@ -157,7 +157,8 @@ int ocfs2_refcount_lock(struct ocfs2_refcount_tree *ref_tree, int ex);
void ocfs2_refcount_unlock(struct ocfs2_refcount_tree *ref_tree, int ex);
-void ocfs2_mark_lockres_freeing(struct ocfs2_lock_res *lockres);
+void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb,
+ struct ocfs2_lock_res *lockres);
void ocfs2_simple_drop_lockres(struct ocfs2_super *osb,
struct ocfs2_lock_res *lockres);
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 51632c40e896..ff33c5ef87f2 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -175,9 +175,13 @@ static int ocfs2_sync_file(struct file *file, loff_t start, loff_t end,
int datasync)
{
int err = 0;
- journal_t *journal;
struct inode *inode = file->f_mapping->host;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ struct ocfs2_inode_info *oi = OCFS2_I(inode);
+ journal_t *journal = osb->journal->j_journal;
+ int ret;
+ tid_t commit_tid;
+ bool needs_barrier = false;
trace_ocfs2_sync_file(inode, file, file->f_path.dentry,
OCFS2_I(inode)->ip_blkno,
@@ -192,29 +196,19 @@ static int ocfs2_sync_file(struct file *file, loff_t start, loff_t end,
if (err)
return err;
- /*
- * Probably don't need the i_mutex at all in here, just putting it here
- * to be consistent with how fsync used to be called, someone more
- * familiar with the fs could possibly remove it.
- */
- mutex_lock(&inode->i_mutex);
- if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) {
- /*
- * We still have to flush drive's caches to get data to the
- * platter
- */
- if (osb->s_mount_opt & OCFS2_MOUNT_BARRIER)
- blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
- goto bail;
+ commit_tid = datasync ? oi->i_datasync_tid : oi->i_sync_tid;
+ if (journal->j_flags & JBD2_BARRIER &&
+ !jbd2_trans_will_send_data_barrier(journal, commit_tid))
+ needs_barrier = true;
+ err = jbd2_complete_transaction(journal, commit_tid);
+ if (needs_barrier) {
+ ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
+ if (!err)
+ err = ret;
}
- journal = osb->journal->j_journal;
- err = jbd2_journal_force_commit(journal);
-
-bail:
if (err)
mlog_errno(err);
- mutex_unlock(&inode->i_mutex);
return (err < 0) ? -EIO : 0;
}
@@ -292,6 +286,7 @@ int ocfs2_update_inode_atime(struct inode *inode,
inode->i_atime = CURRENT_TIME;
di->i_atime = cpu_to_le64(inode->i_atime.tv_sec);
di->i_atime_nsec = cpu_to_le32(inode->i_atime.tv_nsec);
+ ocfs2_update_inode_fsync_trans(handle, inode, 0);
ocfs2_journal_dirty(handle, bh);
out_commit:
@@ -341,6 +336,7 @@ int ocfs2_simple_size_update(struct inode *inode,
if (ret < 0)
mlog_errno(ret);
+ ocfs2_update_inode_fsync_trans(handle, inode, 0);
ocfs2_commit_trans(osb, handle);
out:
return ret;
@@ -435,6 +431,7 @@ static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb,
di->i_size = cpu_to_le64(new_i_size);
di->i_ctime = di->i_mtime = cpu_to_le64(inode->i_ctime.tv_sec);
di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
+ ocfs2_update_inode_fsync_trans(handle, inode, 0);
ocfs2_journal_dirty(handle, fe_bh);
@@ -650,7 +647,7 @@ restarted_transaction:
mlog_errno(status);
goto leave;
}
-
+ ocfs2_update_inode_fsync_trans(handle, inode, 1);
ocfs2_journal_dirty(handle, bh);
spin_lock(&OCFS2_I(inode)->ip_lock);
@@ -743,6 +740,7 @@ static handle_t *ocfs2_zero_start_ordered_transaction(struct inode *inode,
OCFS2_JOURNAL_ACCESS_WRITE);
if (ret)
mlog_errno(ret);
+ ocfs2_update_inode_fsync_trans(handle, inode, 1);
out:
if (ret) {
@@ -840,6 +838,7 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from,
di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
di->i_mtime_nsec = di->i_ctime_nsec;
ocfs2_journal_dirty(handle, di_bh);
+ ocfs2_update_inode_fsync_trans(handle, inode, 1);
ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
}
@@ -1344,6 +1343,7 @@ static int __ocfs2_write_remove_suid(struct inode *inode,
di = (struct ocfs2_dinode *) bh->b_data;
di->i_mode = cpu_to_le16(inode->i_mode);
+ ocfs2_update_inode_fsync_trans(handle, inode, 0);
ocfs2_journal_dirty(handle, bh);
@@ -1576,6 +1576,7 @@ static int ocfs2_zero_partial_clusters(struct inode *inode,
if (ret)
mlog_errno(ret);
}
+ ocfs2_update_inode_fsync_trans(handle, inode, 1);
ocfs2_commit_trans(osb, handle);
out:
@@ -2061,13 +2062,6 @@ out:
return ret;
}
-static void ocfs2_aiodio_wait(struct inode *inode)
-{
- wait_queue_head_t *wq = ocfs2_ioend_wq(inode);
-
- wait_event(*wq, (atomic_read(&OCFS2_I(inode)->ip_unaligned_aio) == 0));
-}
-
static int ocfs2_is_io_unaligned(struct inode *inode, size_t count, loff_t pos)
{
int blockmask = inode->i_sb->s_blocksize - 1;
@@ -2345,10 +2339,8 @@ relock:
* Wait on previous unaligned aio to complete before
* proceeding.
*/
- ocfs2_aiodio_wait(inode);
-
- /* Mark the iocb as needing a decrement in ocfs2_dio_end_io */
- atomic_inc(&OCFS2_I(inode)->ip_unaligned_aio);
+ mutex_lock(&OCFS2_I(inode)->ip_unaligned_aio);
+ /* Mark the iocb as needing an unlock in ocfs2_dio_end_io */
ocfs2_iocb_set_unaligned_aio(iocb);
}
@@ -2428,7 +2420,7 @@ out_dio:
if (unaligned_dio) {
ocfs2_iocb_clear_unaligned_aio(iocb);
- atomic_dec(&OCFS2_I(inode)->ip_unaligned_aio);
+ mutex_unlock(&OCFS2_I(inode)->ip_unaligned_aio);
}
out:
@@ -2645,7 +2637,16 @@ static loff_t ocfs2_file_llseek(struct file *file, loff_t offset, int whence)
case SEEK_SET:
break;
case SEEK_END:
- offset += inode->i_size;
+ /* SEEK_END requires the OCFS2 inode lock for the file
+ * because it references the file's size.
+ */
+ ret = ocfs2_inode_lock(inode, NULL, 0);
+ if (ret < 0) {
+ mlog_errno(ret);
+ goto out;
+ }
+ offset += i_size_read(inode);
+ ocfs2_inode_unlock(inode, 0);
break;
case SEEK_CUR:
if (offset == 0) {
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index f29a90fde619..437de7f768c6 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -130,6 +130,7 @@ struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 blkno, unsigned flags,
struct inode *inode = NULL;
struct super_block *sb = osb->sb;
struct ocfs2_find_inode_args args;
+ journal_t *journal = OCFS2_SB(sb)->journal->j_journal;
trace_ocfs2_iget_begin((unsigned long long)blkno, flags,
sysfile_type);
@@ -169,6 +170,32 @@ struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 blkno, unsigned flags,
goto bail;
}
+ /*
+ * Set transaction id's of transactions that have to be committed
+ * to finish f[data]sync. We set them to currently running transaction
+ * as we cannot be sure that the inode or some of its metadata isn't
+ * part of the transaction - the inode could have been reclaimed and
+ * now it is reread from disk.
+ */
+ if (journal) {
+ transaction_t *transaction;
+ tid_t tid;
+ struct ocfs2_inode_info *oi = OCFS2_I(inode);
+
+ read_lock(&journal->j_state_lock);
+ if (journal->j_running_transaction)
+ transaction = journal->j_running_transaction;
+ else
+ transaction = journal->j_committing_transaction;
+ if (transaction)
+ tid = transaction->t_tid;
+ else
+ tid = journal->j_commit_sequence;
+ read_unlock(&journal->j_state_lock);
+ oi->i_sync_tid = tid;
+ oi->i_datasync_tid = tid;
+ }
+
bail:
if (!IS_ERR(inode)) {
trace_ocfs2_iget_end(inode,
@@ -804,11 +831,13 @@ static int ocfs2_inode_is_valid_to_delete(struct inode *inode)
goto bail;
}
- /* If we're coming from downconvert_thread we can't go into our own
- * voting [hello, deadlock city!], so unforuntately we just
- * have to skip deleting this guy. That's OK though because
- * the node who's doing the actual deleting should handle it
- * anyway. */
+ /*
+ * If we're coming from downconvert_thread we can't go into our own
+ * voting [hello, deadlock city!] so we cannot delete the inode. But
+ * since we dropped last inode ref when downconverting dentry lock,
+ * we cannot have the file open and thus the node doing unlink will
+ * take care of deleting the inode.
+ */
if (current == osb->dc_task)
goto bail;
@@ -822,12 +851,6 @@ static int ocfs2_inode_is_valid_to_delete(struct inode *inode)
goto bail_unlock;
}
- /* If we have allowd wipe of this inode for another node, it
- * will be marked here so we can safely skip it. Recovery will
- * cleanup any inodes we might inadvertently skip here. */
- if (oi->ip_flags & OCFS2_INODE_SKIP_DELETE)
- goto bail_unlock;
-
ret = 1;
bail_unlock:
spin_unlock(&oi->ip_lock);
@@ -941,7 +964,7 @@ static void ocfs2_cleanup_delete_inode(struct inode *inode,
(unsigned long long)OCFS2_I(inode)->ip_blkno, sync_data);
if (sync_data)
filemap_write_and_wait(inode->i_mapping);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
}
static void ocfs2_delete_inode(struct inode *inode)
@@ -960,8 +983,6 @@ static void ocfs2_delete_inode(struct inode *inode)
if (is_bad_inode(inode) || !OCFS2_I(inode)->ip_blkno)
goto bail;
- dquot_initialize(inode);
-
if (!ocfs2_inode_is_valid_to_delete(inode)) {
/* It's probably not necessary to truncate_inode_pages
* here but we do it for safety anyway (it will most
@@ -970,6 +991,8 @@ static void ocfs2_delete_inode(struct inode *inode)
goto bail;
}
+ dquot_initialize(inode);
+
/* We want to block signals in delete_inode as the lock and
* messaging paths may return us -ERESTARTSYS. Which would
* cause us to exit early, resulting in inodes being orphaned
@@ -1057,6 +1080,7 @@ static void ocfs2_clear_inode(struct inode *inode)
{
int status;
struct ocfs2_inode_info *oi = OCFS2_I(inode);
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
clear_inode(inode);
trace_ocfs2_clear_inode((unsigned long long)oi->ip_blkno,
@@ -1073,9 +1097,9 @@ static void ocfs2_clear_inode(struct inode *inode)
/* Do these before all the other work so that we don't bounce
* the downconvert thread while waiting to destroy the locks. */
- ocfs2_mark_lockres_freeing(&oi->ip_rw_lockres);
- ocfs2_mark_lockres_freeing(&oi->ip_inode_lockres);
- ocfs2_mark_lockres_freeing(&oi->ip_open_lockres);
+ ocfs2_mark_lockres_freeing(osb, &oi->ip_rw_lockres);
+ ocfs2_mark_lockres_freeing(osb, &oi->ip_inode_lockres);
+ ocfs2_mark_lockres_freeing(osb, &oi->ip_open_lockres);
ocfs2_resv_discard(&OCFS2_SB(inode->i_sb)->osb_la_resmap,
&oi->ip_la_data_resv);
@@ -1157,7 +1181,7 @@ void ocfs2_evict_inode(struct inode *inode)
(OCFS2_I(inode)->ip_flags & OCFS2_INODE_MAYBE_ORPHANED)) {
ocfs2_delete_inode(inode);
} else {
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
}
ocfs2_clear_inode(inode);
}
@@ -1260,6 +1284,7 @@ int ocfs2_mark_inode_dirty(handle_t *handle,
fe->i_mtime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
ocfs2_journal_dirty(handle, bh);
+ ocfs2_update_inode_fsync_trans(handle, inode, 1);
leave:
return status;
}
diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h
index 621fc73bf23d..a6c991c0fc98 100644
--- a/fs/ocfs2/inode.h
+++ b/fs/ocfs2/inode.h
@@ -44,7 +44,7 @@ struct ocfs2_inode_info
struct rw_semaphore ip_xattr_sem;
/* Number of outstanding AIO's which are not page aligned */
- atomic_t ip_unaligned_aio;
+ struct mutex ip_unaligned_aio;
/* These fields are protected by ip_lock */
spinlock_t ip_lock;
@@ -73,6 +73,13 @@ struct ocfs2_inode_info
u32 ip_dir_lock_gen;
struct ocfs2_alloc_reservation ip_la_data_resv;
+
+ /*
+ * Transactions that contain inode's metadata needed to complete
+ * fsync and fdatasync, respectively.
+ */
+ tid_t i_sync_tid;
+ tid_t i_datasync_tid;
};
/*
@@ -84,8 +91,6 @@ struct ocfs2_inode_info
#define OCFS2_INODE_BITMAP 0x00000004
/* This inode has been wiped from disk */
#define OCFS2_INODE_DELETED 0x00000008
-/* Another node is deleting, so our delete is a nop */
-#define OCFS2_INODE_SKIP_DELETE 0x00000010
/* Has the inode been orphaned on another node?
*
* This hints to ocfs2_drop_inode that it should clear i_nlink before
@@ -100,11 +105,11 @@ struct ocfs2_inode_info
* rely on ocfs2_delete_inode to sort things out under the proper
* cluster locks.
*/
-#define OCFS2_INODE_MAYBE_ORPHANED 0x00000020
+#define OCFS2_INODE_MAYBE_ORPHANED 0x00000010
/* Does someone have the file open O_DIRECT */
-#define OCFS2_INODE_OPEN_DIRECT 0x00000040
+#define OCFS2_INODE_OPEN_DIRECT 0x00000020
/* Tell the inode wipe code it's not in orphan dir */
-#define OCFS2_INODE_SKIP_ORPHAN_DIR 0x00000080
+#define OCFS2_INODE_SKIP_ORPHAN_DIR 0x00000040
static inline struct ocfs2_inode_info *OCFS2_I(struct inode *inode)
{
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 8ca3c29accbf..490229f43731 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -413,11 +413,12 @@ int ocfs2_info_handle_freeinode(struct inode *inode,
}
status = ocfs2_info_scan_inode_alloc(osb, inode_alloc, blkno, oifi, i);
- if (status < 0)
- goto bail;
iput(inode_alloc);
inode_alloc = NULL;
+
+ if (status < 0)
+ goto bail;
}
o2info_set_request_filled(&oifi->ifi_req);
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index 44fc3e530c3d..03ea9314fecd 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -2132,12 +2132,6 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb,
iter = oi->ip_next_orphan;
spin_lock(&oi->ip_lock);
- /* The remote delete code may have set these on the
- * assumption that the other node would wipe them
- * successfully. If they are still in the node's
- * orphan dir, we need to reset that state. */
- oi->ip_flags &= ~(OCFS2_INODE_DELETED|OCFS2_INODE_SKIP_DELETE);
-
/* Set the proper information to get us going into
* ocfs2_delete_inode. */
oi->ip_flags |= OCFS2_INODE_MAYBE_ORPHANED;
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h
index 9ff4e8cf9d97..7f8cde94abfe 100644
--- a/fs/ocfs2/journal.h
+++ b/fs/ocfs2/journal.h
@@ -626,4 +626,15 @@ static inline int ocfs2_begin_ordered_truncate(struct inode *inode,
new_size);
}
+static inline void ocfs2_update_inode_fsync_trans(handle_t *handle,
+ struct inode *inode,
+ int datasync)
+{
+ struct ocfs2_inode_info *oi = OCFS2_I(inode);
+
+ oi->i_sync_tid = handle->h_transaction->t_tid;
+ if (datasync)
+ oi->i_datasync_tid = handle->h_transaction->t_tid;
+}
+
#endif /* OCFS2_JOURNAL_H */
diff --git a/fs/ocfs2/locks.c b/fs/ocfs2/locks.c
index e57c804069ea..6b6d092b0998 100644
--- a/fs/ocfs2/locks.c
+++ b/fs/ocfs2/locks.c
@@ -82,6 +82,8 @@ static int ocfs2_do_flock(struct file *file, struct inode *inode,
}
ret = flock_lock_file_wait(file, fl);
+ if (ret)
+ ocfs2_file_unlock(file);
out:
mutex_unlock(&fp->fp_mutex);
diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c
index 64c304d668f0..599eb4c4c8be 100644
--- a/fs/ocfs2/move_extents.c
+++ b/fs/ocfs2/move_extents.c
@@ -151,6 +151,7 @@ static int __ocfs2_move_extent(handle_t *handle,
old_blkno, len);
}
+ ocfs2_update_inode_fsync_trans(handle, inode, 0);
out:
ocfs2_free_path(path);
return ret;
@@ -690,8 +691,11 @@ static int ocfs2_move_extent(struct ocfs2_move_extents_context *context,
ret = ocfs2_block_group_set_bits(handle, gb_inode, gd, gd_bh,
goal_bit, len);
- if (ret)
+ if (ret) {
+ ocfs2_rollback_alloc_dinode_counts(gb_inode, gb_bh, len,
+ le16_to_cpu(gd->bg_chain));
mlog_errno(ret);
+ }
/*
* Here we should write the new page out first if we are
@@ -957,6 +961,7 @@ static int ocfs2_move_extents(struct ocfs2_move_extents_context *context)
inode->i_ctime = CURRENT_TIME;
di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
+ ocfs2_update_inode_fsync_trans(handle, inode, 0);
ocfs2_journal_dirty(handle, di_bh);
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index 3683643f3f0e..2060fc398445 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -450,7 +450,6 @@ leave:
brelse(new_fe_bh);
brelse(parent_fe_bh);
- kfree(si.name);
kfree(si.value);
ocfs2_free_dir_lookup_result(&lookup);
@@ -495,6 +494,7 @@ static int __ocfs2_mknod_locked(struct inode *dir,
struct ocfs2_dinode *fe = NULL;
struct ocfs2_extent_list *fel;
u16 feat;
+ struct ocfs2_inode_info *oi = OCFS2_I(inode);
*new_fe_bh = NULL;
@@ -576,8 +576,8 @@ static int __ocfs2_mknod_locked(struct inode *dir,
mlog_errno(status);
}
- status = 0; /* error in ocfs2_create_new_inode_locks is not
- * critical */
+ oi->i_sync_tid = handle->h_transaction->t_tid;
+ oi->i_datasync_tid = handle->h_transaction->t_tid;
leave:
if (status < 0) {
@@ -1855,7 +1855,6 @@ bail:
brelse(new_fe_bh);
brelse(parent_fe_bh);
- kfree(si.name);
kfree(si.value);
ocfs2_free_dir_lookup_result(&lookup);
if (inode_ac)
@@ -2481,6 +2480,7 @@ int ocfs2_mv_orphaned_inode_to_new(struct inode *dir,
di->i_orphaned_slot = 0;
set_nlink(inode, 1);
ocfs2_set_links_count(di, inode->i_nlink);
+ ocfs2_update_inode_fsync_trans(handle, inode, 1);
ocfs2_journal_dirty(handle, di_bh);
status = ocfs2_add_entry(handle, dentry, inode,
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index 553f53cc73ae..8d64a97a9d5e 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -30,6 +30,7 @@
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/list.h>
+#include <linux/llist.h>
#include <linux/rbtree.h>
#include <linux/workqueue.h>
#include <linux/kref.h>
@@ -274,19 +275,16 @@ enum ocfs2_mount_options
OCFS2_MOUNT_HB_GLOBAL = 1 << 14, /* Global heartbeat */
};
-#define OCFS2_OSB_SOFT_RO 0x0001
-#define OCFS2_OSB_HARD_RO 0x0002
-#define OCFS2_OSB_ERROR_FS 0x0004
-#define OCFS2_OSB_DROP_DENTRY_LOCK_IMMED 0x0008
-
-#define OCFS2_DEFAULT_ATIME_QUANTUM 60
+#define OCFS2_OSB_SOFT_RO 0x0001
+#define OCFS2_OSB_HARD_RO 0x0002
+#define OCFS2_OSB_ERROR_FS 0x0004
+#define OCFS2_DEFAULT_ATIME_QUANTUM 60
struct ocfs2_journal;
struct ocfs2_slot_info;
struct ocfs2_recovery_map;
struct ocfs2_replay_map;
struct ocfs2_quota_recovery;
-struct ocfs2_dentry_lock;
struct ocfs2_super
{
struct task_struct *commit_task;
@@ -414,10 +412,9 @@ struct ocfs2_super
struct list_head blocked_lock_list;
unsigned long blocked_lock_count;
- /* List of dentry locks to release. Anyone can add locks to
- * the list, ocfs2_wq processes the list */
- struct ocfs2_dentry_lock *dentry_lock_list;
- struct work_struct dentry_lock_work;
+ /* List of dquot structures to drop last reference to */
+ struct llist_head dquot_drop_list;
+ struct work_struct dquot_drop_work;
wait_queue_head_t osb_mount_event;
@@ -449,6 +446,8 @@ struct ocfs2_super
/* rb tree root for refcount lock. */
struct rb_root osb_rf_lock_tree;
struct ocfs2_refcount_tree *osb_ref_tree_lru;
+
+ struct mutex system_file_mutex;
};
#define OCFS2_SB(sb) ((struct ocfs2_super *)(sb)->s_fs_info)
@@ -579,18 +578,6 @@ static inline void ocfs2_set_osb_flag(struct ocfs2_super *osb,
spin_unlock(&osb->osb_lock);
}
-
-static inline unsigned long ocfs2_test_osb_flag(struct ocfs2_super *osb,
- unsigned long flag)
-{
- unsigned long ret;
-
- spin_lock(&osb->osb_lock);
- ret = osb->osb_flags & flag;
- spin_unlock(&osb->osb_lock);
- return ret;
-}
-
static inline void ocfs2_set_ro_flag(struct ocfs2_super *osb,
int hard)
{
diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h
index d5ab56cbe5c5..f266d67df3c6 100644
--- a/fs/ocfs2/quota.h
+++ b/fs/ocfs2/quota.h
@@ -28,6 +28,7 @@ struct ocfs2_dquot {
unsigned int dq_use_count; /* Number of nodes having reference to this entry in global quota file */
s64 dq_origspace; /* Last globally synced space usage */
s64 dq_originodes; /* Last globally synced inode usage */
+ struct llist_node list; /* Member of list of dquots to drop */
};
/* Description of one chunk to recover in memory */
@@ -110,6 +111,7 @@ int ocfs2_read_quota_phys_block(struct inode *inode, u64 p_block,
int ocfs2_create_local_dquot(struct dquot *dquot);
int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot);
int ocfs2_local_write_dquot(struct dquot *dquot);
+void ocfs2_drop_dquot_refs(struct work_struct *work);
extern const struct dquot_operations ocfs2_quota_operations;
extern struct quota_format_type ocfs2_quota_format;
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index d7b5108789e2..b990a62cff50 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -10,6 +10,7 @@
#include <linux/jiffies.h>
#include <linux/writeback.h>
#include <linux/workqueue.h>
+#include <linux/llist.h>
#include <cluster/masklog.h>
@@ -679,6 +680,27 @@ static int ocfs2_calc_qdel_credits(struct super_block *sb, int type)
OCFS2_INODE_UPDATE_CREDITS;
}
+void ocfs2_drop_dquot_refs(struct work_struct *work)
+{
+ struct ocfs2_super *osb = container_of(work, struct ocfs2_super,
+ dquot_drop_work);
+ struct llist_node *list;
+ struct ocfs2_dquot *odquot, *next_odquot;
+
+ list = llist_del_all(&osb->dquot_drop_list);
+ llist_for_each_entry_safe(odquot, next_odquot, list, list) {
+ /* Drop the reference we acquired in ocfs2_dquot_release() */
+ dqput(&odquot->dq_dquot);
+ }
+}
+
+/*
+ * Called when the last reference to dquot is dropped. If we are called from
+ * downconvert thread, we cannot do all the handling here because grabbing
+ * quota lock could deadlock (the node holding the quota lock could need some
+ * other cluster lock to proceed but with blocked downconvert thread we cannot
+ * release any lock).
+ */
static int ocfs2_release_dquot(struct dquot *dquot)
{
handle_t *handle;
@@ -694,6 +716,19 @@ static int ocfs2_release_dquot(struct dquot *dquot)
/* Check whether we are not racing with some other dqget() */
if (atomic_read(&dquot->dq_count) > 1)
goto out;
+ /* Running from downconvert thread? Postpone quota processing to wq */
+ if (current == osb->dc_task) {
+ /*
+ * Grab our own reference to dquot and queue it for delayed
+ * dropping. Quota code rechecks after calling
+ * ->release_dquot() and won't free dquot structure.
+ */
+ dqgrab(dquot);
+ /* First entry on list -> queue work */
+ if (llist_add(&OCFS2_DQUOT(dquot)->list, &osb->dquot_drop_list))
+ queue_work(ocfs2_wq, &osb->dquot_drop_work);
+ goto out;
+ }
status = ocfs2_lock_global_qf(oinfo, 1);
if (status < 0)
goto out;
diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c
index ca5ce14cbddc..83f1a665ae97 100644
--- a/fs/ocfs2/stackglue.c
+++ b/fs/ocfs2/stackglue.c
@@ -496,7 +496,7 @@ static ssize_t ocfs2_max_locking_protocol_show(struct kobject *kobj,
}
static struct kobj_attribute ocfs2_attr_max_locking_protocol =
- __ATTR(max_locking_protocol, S_IFREG | S_IRUGO,
+ __ATTR(max_locking_protocol, S_IRUGO,
ocfs2_max_locking_protocol_show, NULL);
static ssize_t ocfs2_loaded_cluster_plugins_show(struct kobject *kobj,
@@ -528,7 +528,7 @@ static ssize_t ocfs2_loaded_cluster_plugins_show(struct kobject *kobj,
}
static struct kobj_attribute ocfs2_attr_loaded_cluster_plugins =
- __ATTR(loaded_cluster_plugins, S_IFREG | S_IRUGO,
+ __ATTR(loaded_cluster_plugins, S_IRUGO,
ocfs2_loaded_cluster_plugins_show, NULL);
static ssize_t ocfs2_active_cluster_plugin_show(struct kobject *kobj,
@@ -550,7 +550,7 @@ static ssize_t ocfs2_active_cluster_plugin_show(struct kobject *kobj,
}
static struct kobj_attribute ocfs2_attr_active_cluster_plugin =
- __ATTR(active_cluster_plugin, S_IFREG | S_IRUGO,
+ __ATTR(active_cluster_plugin, S_IRUGO,
ocfs2_active_cluster_plugin_show, NULL);
static ssize_t ocfs2_cluster_stack_show(struct kobject *kobj,
@@ -599,15 +599,29 @@ static ssize_t ocfs2_cluster_stack_store(struct kobject *kobj,
static struct kobj_attribute ocfs2_attr_cluster_stack =
- __ATTR(cluster_stack, S_IFREG | S_IRUGO | S_IWUSR,
+ __ATTR(cluster_stack, S_IRUGO | S_IWUSR,
ocfs2_cluster_stack_show,
ocfs2_cluster_stack_store);
+
+
+static ssize_t ocfs2_dlm_recover_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "1\n");
+}
+
+static struct kobj_attribute ocfs2_attr_dlm_recover_support =
+ __ATTR(dlm_recover_callback_support, S_IRUGO,
+ ocfs2_dlm_recover_show, NULL);
+
static struct attribute *ocfs2_attrs[] = {
&ocfs2_attr_max_locking_protocol.attr,
&ocfs2_attr_loaded_cluster_plugins.attr,
&ocfs2_attr_active_cluster_plugin.attr,
&ocfs2_attr_cluster_stack.attr,
+ &ocfs2_attr_dlm_recover_support.attr,
NULL,
};
diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c
index 47ae2663a6f5..0cb889a17ae1 100644
--- a/fs/ocfs2/suballoc.c
+++ b/fs/ocfs2/suballoc.c
@@ -771,6 +771,7 @@ static int ocfs2_block_group_alloc(struct ocfs2_super *osb,
spin_unlock(&OCFS2_I(alloc_inode)->ip_lock);
i_size_write(alloc_inode, le64_to_cpu(fe->i_size));
alloc_inode->i_blocks = ocfs2_inode_sector_count(alloc_inode);
+ ocfs2_update_inode_fsync_trans(handle, alloc_inode, 0);
status = 0;
@@ -1607,6 +1608,21 @@ out:
return ret;
}
+void ocfs2_rollback_alloc_dinode_counts(struct inode *inode,
+ struct buffer_head *di_bh,
+ u32 num_bits,
+ u16 chain)
+{
+ u32 tmp_used;
+ struct ocfs2_dinode *di = (struct ocfs2_dinode *) di_bh->b_data;
+ struct ocfs2_chain_list *cl;
+
+ cl = (struct ocfs2_chain_list *)&di->id2.i_chain;
+ tmp_used = le32_to_cpu(di->id1.bitmap1.i_used);
+ di->id1.bitmap1.i_used = cpu_to_le32(tmp_used - num_bits);
+ le32_add_cpu(&cl->cl_recs[chain].c_free, num_bits);
+}
+
static int ocfs2_bg_discontig_fix_by_rec(struct ocfs2_suballoc_result *res,
struct ocfs2_extent_rec *rec,
struct ocfs2_chain_list *cl)
@@ -1707,8 +1723,12 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac,
ret = ocfs2_block_group_set_bits(handle, alloc_inode, gd, group_bh,
res->sr_bit_offset, res->sr_bits);
- if (ret < 0)
+ if (ret < 0) {
+ ocfs2_rollback_alloc_dinode_counts(alloc_inode, ac->ac_bh,
+ res->sr_bits,
+ le16_to_cpu(gd->bg_chain));
mlog_errno(ret);
+ }
out_loc_only:
*bits_left = le16_to_cpu(gd->bg_free_bits_count);
@@ -1838,6 +1858,8 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
res->sr_bit_offset,
res->sr_bits);
if (status < 0) {
+ ocfs2_rollback_alloc_dinode_counts(alloc_inode,
+ ac->ac_bh, res->sr_bits, chain);
mlog_errno(status);
goto bail;
}
@@ -2091,7 +2113,7 @@ int ocfs2_find_new_inode_loc(struct inode *dir,
ac->ac_find_loc_priv = res;
*fe_blkno = res->sr_blkno;
-
+ ocfs2_update_inode_fsync_trans(handle, dir, 0);
out:
if (handle)
ocfs2_commit_trans(OCFS2_SB(dir->i_sb), handle);
@@ -2149,6 +2171,8 @@ int ocfs2_claim_new_inode_at_loc(handle_t *handle,
res->sr_bit_offset,
res->sr_bits);
if (ret < 0) {
+ ocfs2_rollback_alloc_dinode_counts(ac->ac_inode,
+ ac->ac_bh, res->sr_bits, chain);
mlog_errno(ret);
goto out;
}
@@ -2870,6 +2894,7 @@ int ocfs2_test_inode_bit(struct ocfs2_super *osb, u64 blkno, int *res)
status = ocfs2_inode_lock(inode_alloc_inode, &alloc_bh, 0);
if (status < 0) {
mutex_unlock(&inode_alloc_inode->i_mutex);
+ iput(inode_alloc_inode);
mlog(ML_ERROR, "lock on alloc inode on slot %u failed %d\n",
(u32)suballoc_slot, status);
goto bail;
diff --git a/fs/ocfs2/suballoc.h b/fs/ocfs2/suballoc.h
index 218d8036b3e7..2d2501767c0c 100644
--- a/fs/ocfs2/suballoc.h
+++ b/fs/ocfs2/suballoc.h
@@ -91,6 +91,10 @@ int ocfs2_alloc_dinode_update_counts(struct inode *inode,
struct buffer_head *di_bh,
u32 num_bits,
u16 chain);
+void ocfs2_rollback_alloc_dinode_counts(struct inode *inode,
+ struct buffer_head *di_bh,
+ u32 num_bits,
+ u16 chain);
int ocfs2_block_group_set_bits(handle_t *handle,
struct inode *alloc_inode,
struct ocfs2_group_desc *bg,
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 49d84f80f36c..a7cdd56f4c79 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -561,6 +561,9 @@ static struct inode *ocfs2_alloc_inode(struct super_block *sb)
if (!oi)
return NULL;
+ oi->i_sync_tid = 0;
+ oi->i_datasync_tid = 0;
+
jbd2_journal_init_jbd_inode(&oi->ip_jinode, &oi->vfs_inode);
return &oi->vfs_inode;
}
@@ -631,6 +634,8 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
struct ocfs2_super *osb = OCFS2_SB(sb);
u32 tmp;
+ sync_filesystem(sb);
+
if (!ocfs2_parse_options(sb, data, &parsed_options, 1) ||
!ocfs2_check_set_options(sb, &parsed_options)) {
ret = -EINVAL;
@@ -1238,30 +1243,11 @@ static struct dentry *ocfs2_mount(struct file_system_type *fs_type,
return mount_bdev(fs_type, flags, dev_name, data, ocfs2_fill_super);
}
-static void ocfs2_kill_sb(struct super_block *sb)
-{
- struct ocfs2_super *osb = OCFS2_SB(sb);
-
- /* Failed mount? */
- if (!osb || atomic_read(&osb->vol_state) == VOLUME_DISABLED)
- goto out;
-
- /* Prevent further queueing of inode drop events */
- spin_lock(&dentry_list_lock);
- ocfs2_set_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED);
- spin_unlock(&dentry_list_lock);
- /* Wait for work to finish and/or remove it */
- cancel_work_sync(&osb->dentry_lock_work);
-out:
- kill_block_super(sb);
-}
-
static struct file_system_type ocfs2_fs_type = {
.owner = THIS_MODULE,
.name = "ocfs2",
.mount = ocfs2_mount,
- .kill_sb = ocfs2_kill_sb,
-
+ .kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV|FS_RENAME_DOES_D_MOVE,
.next = NULL
};
@@ -1612,14 +1598,9 @@ static int ocfs2_show_options(struct seq_file *s, struct dentry *root)
return 0;
}
-wait_queue_head_t ocfs2__ioend_wq[OCFS2_IOEND_WQ_HASH_SZ];
-
static int __init ocfs2_init(void)
{
- int status, i;
-
- for (i = 0; i < OCFS2_IOEND_WQ_HASH_SZ; i++)
- init_waitqueue_head(&ocfs2__ioend_wq[i]);
+ int status;
status = init_ocfs2_uptodate_cache();
if (status < 0)
@@ -1761,7 +1742,7 @@ static void ocfs2_inode_init_once(void *data)
ocfs2_extent_map_init(&oi->vfs_inode);
INIT_LIST_HEAD(&oi->ip_io_markers);
oi->ip_dir_start_lookup = 0;
- atomic_set(&oi->ip_unaligned_aio, 0);
+ mutex_init(&oi->ip_unaligned_aio);
init_rwsem(&oi->ip_alloc_sem);
init_rwsem(&oi->ip_xattr_sem);
mutex_init(&oi->ip_io_mutex);
@@ -1932,17 +1913,16 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err)
debugfs_remove(osb->osb_ctxt);
- /*
- * Flush inode dropping work queue so that deletes are
- * performed while the filesystem is still working
- */
- ocfs2_drop_all_dl_inodes(osb);
-
/* Orphan scan should be stopped as early as possible */
ocfs2_orphan_scan_stop(osb);
ocfs2_disable_quotas(osb);
+ /* All dquots should be freed by now */
+ WARN_ON(!llist_empty(&osb->dquot_drop_list));
+ /* Wait for worker to be done with the work structure in osb */
+ cancel_work_sync(&osb->dquot_drop_work);
+
ocfs2_shutdown_local_alloc(osb);
/* This will disable recovery and flush any recovery work. */
@@ -2077,7 +2057,6 @@ static int ocfs2_initialize_super(struct super_block *sb,
struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data;
struct inode *inode = NULL;
struct ocfs2_journal *journal;
- __le32 uuid_net_key;
struct ocfs2_super *osb;
u64 total_blocks;
@@ -2123,6 +2102,8 @@ static int ocfs2_initialize_super(struct super_block *sb,
spin_lock_init(&osb->osb_xattr_lock);
ocfs2_init_steal_slots(osb);
+ mutex_init(&osb->system_file_mutex);
+
atomic_set(&osb->alloc_stats.moves, 0);
atomic_set(&osb->alloc_stats.local_data, 0);
atomic_set(&osb->alloc_stats.bitmap_data, 0);
@@ -2276,8 +2257,8 @@ static int ocfs2_initialize_super(struct super_block *sb,
INIT_WORK(&journal->j_recovery_work, ocfs2_complete_recovery);
journal->j_state = OCFS2_JOURNAL_FREE;
- INIT_WORK(&osb->dentry_lock_work, ocfs2_drop_dl_inodes);
- osb->dentry_lock_list = NULL;
+ INIT_WORK(&osb->dquot_drop_work, ocfs2_drop_dquot_refs);
+ init_llist_head(&osb->dquot_drop_list);
/* get some pseudo constants for clustersize bits */
osb->s_clustersize_bits =
@@ -2311,8 +2292,6 @@ static int ocfs2_initialize_super(struct super_block *sb,
goto bail;
}
- memcpy(&uuid_net_key, di->id2.i_super.s_uuid, sizeof(uuid_net_key));
-
strncpy(osb->vol_label, di->id2.i_super.s_label, 63);
osb->vol_label[63] = '\0';
osb->root_blkno = le64_to_cpu(di->id2.i_super.s_root_blkno);
diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c
index f053688d22a3..af155c183123 100644
--- a/fs/ocfs2/sysfile.c
+++ b/fs/ocfs2/sysfile.c
@@ -113,9 +113,11 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb,
} else
arr = get_local_system_inode(osb, type, slot);
+ mutex_lock(&osb->system_file_mutex);
if (arr && ((inode = *arr) != NULL)) {
/* get a ref in addition to the array ref */
inode = igrab(inode);
+ mutex_unlock(&osb->system_file_mutex);
BUG_ON(!inode);
return inode;
@@ -129,6 +131,7 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb,
*arr = igrab(inode);
BUG_ON(!*arr);
}
+ mutex_unlock(&osb->system_file_mutex);
return inode;
}
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 185fa3b7f962..016f01df3825 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -369,7 +369,7 @@ static void ocfs2_xattr_bucket_free(struct ocfs2_xattr_bucket *bucket)
* them fully.
*/
static int ocfs2_init_xattr_bucket(struct ocfs2_xattr_bucket *bucket,
- u64 xb_blkno)
+ u64 xb_blkno, int new)
{
int i, rc = 0;
@@ -383,9 +383,16 @@ static int ocfs2_init_xattr_bucket(struct ocfs2_xattr_bucket *bucket,
}
if (!ocfs2_buffer_uptodate(INODE_CACHE(bucket->bu_inode),
- bucket->bu_bhs[i]))
- ocfs2_set_new_buffer_uptodate(INODE_CACHE(bucket->bu_inode),
- bucket->bu_bhs[i]);
+ bucket->bu_bhs[i])) {
+ if (new)
+ ocfs2_set_new_buffer_uptodate(INODE_CACHE(bucket->bu_inode),
+ bucket->bu_bhs[i]);
+ else {
+ set_buffer_uptodate(bucket->bu_bhs[i]);
+ ocfs2_set_buffer_uptodate(INODE_CACHE(bucket->bu_inode),
+ bucket->bu_bhs[i]);
+ }
+ }
}
if (rc)
@@ -2602,6 +2609,7 @@ int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh)
oi->ip_dyn_features &= ~(OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL);
di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features);
spin_unlock(&oi->ip_lock);
+ ocfs2_update_inode_fsync_trans(handle, inode, 0);
ocfs2_journal_dirty(handle, di_bh);
out_commit:
@@ -3200,8 +3208,15 @@ meta_guess:
clusters_add += 1;
}
} else {
- meta_add += 1;
credits += OCFS2_XATTR_BLOCK_CREATE_CREDITS;
+ if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) {
+ struct ocfs2_extent_list *el = &def_xv.xv.xr_list;
+ meta_add += ocfs2_extend_meta_needed(el);
+ credits += ocfs2_calc_extend_credits(inode->i_sb,
+ el);
+ } else {
+ meta_add += 1;
+ }
}
out:
if (clusters_need)
@@ -3614,6 +3629,7 @@ int ocfs2_xattr_set(struct inode *inode,
}
ret = __ocfs2_xattr_set_handle(inode, di, &xi, &xis, &xbs, &ctxt);
+ ocfs2_update_inode_fsync_trans(ctxt.handle, inode, 0);
ocfs2_commit_trans(osb, ctxt.handle);
@@ -4294,7 +4310,7 @@ static int ocfs2_xattr_create_index_block(struct inode *inode,
trace_ocfs2_xattr_create_index_block((unsigned long long)blkno);
- ret = ocfs2_init_xattr_bucket(xs->bucket, blkno);
+ ret = ocfs2_init_xattr_bucket(xs->bucket, blkno, 1);
if (ret) {
mlog_errno(ret);
goto out;
@@ -4638,7 +4654,7 @@ static int ocfs2_divide_xattr_bucket(struct inode *inode,
* Even if !new_bucket_head, we're overwriting t_bucket. Thus,
* there's no need to read it.
*/
- ret = ocfs2_init_xattr_bucket(t_bucket, new_blk);
+ ret = ocfs2_init_xattr_bucket(t_bucket, new_blk, new_bucket_head);
if (ret) {
mlog_errno(ret);
goto out;
@@ -4804,7 +4820,7 @@ static int ocfs2_cp_xattr_bucket(struct inode *inode,
* Even if !t_is_new, we're overwriting t_bucket. Thus,
* there's no need to read it.
*/
- ret = ocfs2_init_xattr_bucket(t_bucket, t_blkno);
+ ret = ocfs2_init_xattr_bucket(t_bucket, t_blkno, t_is_new);
if (ret)
goto out;
@@ -5476,6 +5492,7 @@ static int ocfs2_rm_xattr_cluster(struct inode *inode,
ret = ocfs2_truncate_log_append(osb, handle, blkno, len);
if (ret)
mlog_errno(ret);
+ ocfs2_update_inode_fsync_trans(handle, inode, 0);
out_commit:
ocfs2_commit_trans(osb, handle);
@@ -6830,7 +6847,7 @@ static int ocfs2_reflink_xattr_bucket(handle_t *handle,
break;
}
- ret = ocfs2_init_xattr_bucket(args->new_bucket, new_blkno);
+ ret = ocfs2_init_xattr_bucket(args->new_bucket, new_blkno, 1);
if (ret) {
mlog_errno(ret);
break;
diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c
index d8b0afde2179..ec58c7659183 100644
--- a/fs/omfs/inode.c
+++ b/fs/omfs/inode.c
@@ -183,7 +183,7 @@ int omfs_sync_inode(struct inode *inode)
*/
static void omfs_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (inode->i_nlink)
diff --git a/fs/open.c b/fs/open.c
index b9ed8b25c108..631aea815def 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -231,7 +231,13 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
return -EINVAL;
/* Return error if mode is not supported */
- if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+ if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
+ FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE))
+ return -EOPNOTSUPP;
+
+ /* Punch hole and zero range are mutually exclusive */
+ if ((mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE)) ==
+ (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
return -EOPNOTSUPP;
/* Punch hole must have keep size set */
@@ -239,11 +245,20 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
!(mode & FALLOC_FL_KEEP_SIZE))
return -EOPNOTSUPP;
+ /* Collapse range should only be used exclusively. */
+ if ((mode & FALLOC_FL_COLLAPSE_RANGE) &&
+ (mode & ~FALLOC_FL_COLLAPSE_RANGE))
+ return -EINVAL;
+
if (!(file->f_mode & FMODE_WRITE))
return -EBADF;
- /* It's not possible punch hole on append only file */
- if (mode & FALLOC_FL_PUNCH_HOLE && IS_APPEND(inode))
+ /*
+ * It's not possible to punch hole or perform collapse range
+ * on append only file
+ */
+ if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE)
+ && IS_APPEND(inode))
return -EPERM;
if (IS_IMMUTABLE(inode))
@@ -271,6 +286,14 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0))
return -EFBIG;
+ /*
+ * There is no need to overlap collapse range with EOF, in which case
+ * it is effectively a truncate operation
+ */
+ if ((mode & FALLOC_FL_COLLAPSE_RANGE) &&
+ (offset + len >= i_size_read(inode)))
+ return -EINVAL;
+
if (!file->f_op->fallocate)
return -EOPNOTSUPP;
diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c
index 8c0ceb8dd1f7..15e4500cda3e 100644
--- a/fs/openpromfs/inode.c
+++ b/fs/openpromfs/inode.c
@@ -368,6 +368,7 @@ static struct inode *openprom_iget(struct super_block *sb, ino_t ino)
static int openprom_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
*flags |= MS_NOATIME;
return 0;
}
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 11c54fd51e16..9e363e41dacc 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -723,7 +723,7 @@ posix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl,
void *buffer, size_t size)
{
posix_acl_xattr_header *ext_acl = (posix_acl_xattr_header *)buffer;
- posix_acl_xattr_entry *ext_entry = ext_acl->a_entries;
+ posix_acl_xattr_entry *ext_entry;
int real_size, n;
real_size = posix_acl_xattr_size(acl->a_count);
@@ -731,7 +731,8 @@ posix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl,
return real_size;
if (real_size > size)
return -ERANGE;
-
+
+ ext_entry = ext_acl->a_entries;
ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
for (n=0; n < acl->a_count; n++, ext_entry++) {
diff --git a/fs/proc/Makefile b/fs/proc/Makefile
index ab30716584f5..239493ec718e 100644
--- a/fs/proc/Makefile
+++ b/fs/proc/Makefile
@@ -27,6 +27,5 @@ proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o
proc-$(CONFIG_NET) += proc_net.o
proc-$(CONFIG_PROC_KCORE) += kcore.o
proc-$(CONFIG_PROC_VMCORE) += vmcore.o
-proc-$(CONFIG_PROC_DEVICETREE) += proc_devtree.o
proc-$(CONFIG_PRINTK) += kmsg.o
proc-$(CONFIG_PROC_PAGE_MONITOR) += page.o
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 124fc43c7090..8f20e3404fd2 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -35,7 +35,7 @@ static void proc_evict_inode(struct inode *inode)
const struct proc_ns_operations *ns_ops;
void *ns;
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
/* Stop tracking associated processes */
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 651d09a11dde..3ab6d14e71c5 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -211,13 +211,6 @@ extern int proc_fill_super(struct super_block *);
extern void proc_entry_rundown(struct proc_dir_entry *);
/*
- * proc_devtree.c
- */
-#ifdef CONFIG_PROC_DEVICETREE
-extern void proc_device_tree_init(void);
-#endif
-
-/*
* proc_namespaces.c
*/
extern const struct inode_operations proc_ns_dir_inode_operations;
diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c
deleted file mode 100644
index c82dd5147845..000000000000
--- a/fs/proc/proc_devtree.c
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * proc_devtree.c - handles /proc/device-tree
- *
- * Copyright 1997 Paul Mackerras
- */
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/time.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/printk.h>
-#include <linux/stat.h>
-#include <linux/string.h>
-#include <linux/of.h>
-#include <linux/export.h>
-#include <linux/slab.h>
-#include <asm/uaccess.h>
-#include "internal.h"
-
-static inline void set_node_proc_entry(struct device_node *np,
- struct proc_dir_entry *de)
-{
- np->pde = de;
-}
-
-static struct proc_dir_entry *proc_device_tree;
-
-/*
- * Supply data on a read from /proc/device-tree/node/property.
- */
-static int property_proc_show(struct seq_file *m, void *v)
-{
- struct property *pp = m->private;
-
- seq_write(m, pp->value, pp->length);
- return 0;
-}
-
-static int property_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, property_proc_show, __PDE_DATA(inode));
-}
-
-static const struct file_operations property_proc_fops = {
- .owner = THIS_MODULE,
- .open = property_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-/*
- * For a node with a name like "gc@10", we make symlinks called "gc"
- * and "@10" to it.
- */
-
-/*
- * Add a property to a node
- */
-static struct proc_dir_entry *
-__proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp,
- const char *name)
-{
- struct proc_dir_entry *ent;
-
- /*
- * Unfortunately proc_register puts each new entry
- * at the beginning of the list. So we rearrange them.
- */
- ent = proc_create_data(name,
- strncmp(name, "security-", 9) ? S_IRUGO : S_IRUSR,
- de, &property_proc_fops, pp);
- if (ent == NULL)
- return NULL;
-
- if (!strncmp(name, "security-", 9))
- proc_set_size(ent, 0); /* don't leak number of password chars */
- else
- proc_set_size(ent, pp->length);
-
- return ent;
-}
-
-
-void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop)
-{
- __proc_device_tree_add_prop(pde, prop, prop->name);
-}
-
-void proc_device_tree_remove_prop(struct proc_dir_entry *pde,
- struct property *prop)
-{
- remove_proc_entry(prop->name, pde);
-}
-
-void proc_device_tree_update_prop(struct proc_dir_entry *pde,
- struct property *newprop,
- struct property *oldprop)
-{
- struct proc_dir_entry *ent;
-
- if (!oldprop) {
- proc_device_tree_add_prop(pde, newprop);
- return;
- }
-
- for (ent = pde->subdir; ent != NULL; ent = ent->next)
- if (ent->data == oldprop)
- break;
- if (ent == NULL) {
- pr_warn("device-tree: property \"%s\" does not exist\n",
- oldprop->name);
- } else {
- ent->data = newprop;
- ent->size = newprop->length;
- }
-}
-
-/*
- * Various dodgy firmware might give us nodes and/or properties with
- * conflicting names. That's generally ok, except for exporting via /proc,
- * so munge names here to ensure they're unique.
- */
-
-static int duplicate_name(struct proc_dir_entry *de, const char *name)
-{
- struct proc_dir_entry *ent;
- int found = 0;
-
- spin_lock(&proc_subdir_lock);
-
- for (ent = de->subdir; ent != NULL; ent = ent->next) {
- if (strcmp(ent->name, name) == 0) {
- found = 1;
- break;
- }
- }
-
- spin_unlock(&proc_subdir_lock);
-
- return found;
-}
-
-static const char *fixup_name(struct device_node *np, struct proc_dir_entry *de,
- const char *name)
-{
- char *fixed_name;
- int fixup_len = strlen(name) + 2 + 1; /* name + #x + \0 */
- int i = 1, size;
-
-realloc:
- fixed_name = kmalloc(fixup_len, GFP_KERNEL);
- if (fixed_name == NULL) {
- pr_err("device-tree: Out of memory trying to fixup "
- "name \"%s\"\n", name);
- return name;
- }
-
-retry:
- size = snprintf(fixed_name, fixup_len, "%s#%d", name, i);
- size++; /* account for NULL */
-
- if (size > fixup_len) {
- /* We ran out of space, free and reallocate. */
- kfree(fixed_name);
- fixup_len = size;
- goto realloc;
- }
-
- if (duplicate_name(de, fixed_name)) {
- /* Multiple duplicates. Retry with a different offset. */
- i++;
- goto retry;
- }
-
- pr_warn("device-tree: Duplicate name in %s, renamed to \"%s\"\n",
- np->full_name, fixed_name);
-
- return fixed_name;
-}
-
-/*
- * Process a node, adding entries for its children and its properties.
- */
-void proc_device_tree_add_node(struct device_node *np,
- struct proc_dir_entry *de)
-{
- struct property *pp;
- struct proc_dir_entry *ent;
- struct device_node *child;
- const char *p;
-
- set_node_proc_entry(np, de);
- for (child = NULL; (child = of_get_next_child(np, child));) {
- /* Use everything after the last slash, or the full name */
- p = kbasename(child->full_name);
-
- if (duplicate_name(de, p))
- p = fixup_name(np, de, p);
-
- ent = proc_mkdir(p, de);
- if (ent == NULL)
- break;
- proc_device_tree_add_node(child, ent);
- }
- of_node_put(child);
-
- for (pp = np->properties; pp != NULL; pp = pp->next) {
- p = pp->name;
-
- if (strchr(p, '/'))
- continue;
-
- if (duplicate_name(de, p))
- p = fixup_name(np, de, p);
-
- ent = __proc_device_tree_add_prop(de, pp, p);
- if (ent == NULL)
- break;
- }
-}
-
-/*
- * Called on initialization to set up the /proc/device-tree subtree
- */
-void __init proc_device_tree_init(void)
-{
- struct device_node *root;
-
- proc_device_tree = proc_mkdir("device-tree", NULL);
- if (proc_device_tree == NULL)
- return;
- root = of_find_node_by_path("/");
- if (root == NULL) {
- remove_proc_entry("device-tree", NULL);
- pr_debug("/proc/device-tree: can't find root\n");
- return;
- }
- proc_device_tree_add_node(root, proc_device_tree);
- of_node_put(root);
-}
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 87dbcbef7fe4..5dbadecb234d 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -92,6 +92,8 @@ static int proc_parse_options(char *options, struct pid_namespace *pid)
int proc_remount(struct super_block *sb, int *flags, char *data)
{
struct pid_namespace *pid = sb->s_fs_info;
+
+ sync_filesystem(sb);
return !proc_parse_options(data, pid);
}
@@ -183,9 +185,6 @@ void __init proc_root_init(void)
proc_mkdir("openprom", NULL);
#endif
proc_tty_init();
-#ifdef CONFIG_PROC_DEVICETREE
- proc_device_tree_init();
-#endif
proc_mkdir("bus", NULL);
proc_sys_init();
}
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index 12823845d324..192297b0090d 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -249,6 +249,7 @@ static void parse_options(char *options)
static int pstore_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
parse_options(data);
return 0;
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 78c3c2097787..46d269e38706 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -497,6 +497,7 @@ void pstore_get_records(int quiet)
big_oops_buf_sz);
if (unzipped_len > 0) {
+ kfree(buf);
buf = big_oops_buf;
size = unzipped_len;
compressed = false;
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c
index fa8cef2cca3a..3b5744306ed8 100644
--- a/fs/pstore/ram.c
+++ b/fs/pstore/ram.c
@@ -86,6 +86,7 @@ struct ramoops_context {
struct persistent_ram_ecc_info ecc_info;
unsigned int max_dump_cnt;
unsigned int dump_write_cnt;
+ /* _read_cnt need clear on ramoops_pstore_open */
unsigned int dump_read_cnt;
unsigned int console_read_cnt;
unsigned int ftrace_read_cnt;
@@ -101,6 +102,7 @@ static int ramoops_pstore_open(struct pstore_info *psi)
cxt->dump_read_cnt = 0;
cxt->console_read_cnt = 0;
+ cxt->ftrace_read_cnt = 0;
return 0;
}
@@ -117,13 +119,15 @@ ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max,
return NULL;
prz = przs[i];
+ if (!prz)
+ return NULL;
- if (update) {
- /* Update old/shadowed buffer. */
+ /* Update old/shadowed buffer. */
+ if (update)
persistent_ram_save_old(prz);
- if (!persistent_ram_old_size(prz))
- return NULL;
- }
+
+ if (!persistent_ram_old_size(prz))
+ return NULL;
*typep = type;
*id = i;
@@ -316,6 +320,7 @@ static void ramoops_free_przs(struct ramoops_context *cxt)
{
int i;
+ cxt->max_dump_cnt = 0;
if (!cxt->przs)
return;
@@ -346,7 +351,7 @@ static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt,
GFP_KERNEL);
if (!cxt->przs) {
dev_err(dev, "failed to initialize a prz array for dumps\n");
- return -ENOMEM;
+ goto fail_prz;
}
for (i = 0; i < cxt->max_dump_cnt; i++) {
@@ -428,7 +433,6 @@ static int ramoops_probe(struct platform_device *pdev)
if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size))
pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size);
- cxt->dump_read_cnt = 0;
cxt->size = pdata->mem_size;
cxt->phys_addr = pdata->mem_address;
cxt->record_size = pdata->record_size;
@@ -505,7 +509,6 @@ fail_buf:
kfree(cxt->pstore.buf);
fail_clear:
cxt->pstore.bufsize = 0;
- cxt->max_dump_cnt = 0;
fail_cnt:
kfree(cxt->fprz);
fail_init_fprz:
diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c
index de272d426763..ff7e3d4df5a1 100644
--- a/fs/pstore/ram_core.c
+++ b/fs/pstore/ram_core.c
@@ -54,7 +54,7 @@ static size_t buffer_start_add_atomic(struct persistent_ram_zone *prz, size_t a)
do {
old = atomic_read(&prz->buffer->start);
new = old + a;
- while (unlikely(new > prz->buffer_size))
+ while (unlikely(new >= prz->buffer_size))
new -= prz->buffer_size;
} while (atomic_cmpxchg(&prz->buffer->start, old, new) != old);
@@ -91,7 +91,7 @@ static size_t buffer_start_add_locked(struct persistent_ram_zone *prz, size_t a)
old = atomic_read(&prz->buffer->start);
new = old + a;
- while (unlikely(new > prz->buffer_size))
+ while (unlikely(new >= prz->buffer_size))
new -= prz->buffer_size;
atomic_set(&prz->buffer->start, new);
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
index 89558810381c..c4bcb778886e 100644
--- a/fs/qnx4/inode.c
+++ b/fs/qnx4/inode.c
@@ -44,6 +44,7 @@ static int qnx4_remount(struct super_block *sb, int *flags, char *data)
{
struct qnx4_sb_info *qs;
+ sync_filesystem(sb);
qs = qnx4_sb(sb);
qs->Version = QNX4_VERSION;
*flags |= MS_RDONLY;
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index 8d941edfefa1..65cdaab3ed49 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -55,6 +55,7 @@ static int qnx6_show_options(struct seq_file *seq, struct dentry *root)
static int qnx6_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
*flags |= MS_RDONLY;
return 0;
}
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index cfc8dcc16043..9cd5f63715c0 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -528,7 +528,7 @@ restart:
if (atomic_read(&dquot->dq_count)) {
DEFINE_WAIT(wait);
- atomic_inc(&dquot->dq_count);
+ dqgrab(dquot);
prepare_to_wait(&dquot->dq_wait_unused, &wait,
TASK_UNINTERRUPTIBLE);
spin_unlock(&dq_list_lock);
@@ -632,7 +632,7 @@ int dquot_writeback_dquots(struct super_block *sb, int type)
/* Now we have active dquot from which someone is
* holding reference so we can safely just increase
* use count */
- atomic_inc(&dquot->dq_count);
+ dqgrab(dquot);
spin_unlock(&dq_list_lock);
dqstats_inc(DQST_LOOKUPS);
err = sb->dq_op->write_dquot(dquot);
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index ad62bdbb451e..bc8b8009897d 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -35,7 +35,7 @@ void reiserfs_evict_inode(struct inode *inode)
if (!inode->i_nlink && !is_bad_inode(inode))
dquot_initialize(inode);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
if (inode->i_nlink)
goto no_delete;
diff --git a/fs/reiserfs/reiserfs.h b/fs/reiserfs/reiserfs.h
index 8d06adf89948..83d4eac8059a 100644
--- a/fs/reiserfs/reiserfs.h
+++ b/fs/reiserfs/reiserfs.h
@@ -2831,6 +2831,7 @@ void reiserfs_init_alloc_options(struct super_block *s);
*/
__le32 reiserfs_choose_packing(struct inode *dir);
+void show_alloc_options(struct seq_file *seq, struct super_block *s);
int reiserfs_init_bitmap_cache(struct super_block *sb);
void reiserfs_free_bitmap_cache(struct super_block *sb);
void reiserfs_cache_bitmap_metadata(struct super_block *sb, struct buffer_head *bh, struct reiserfs_bitmap_info *info);
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index 2c803353f8ac..9fb20426005e 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -62,7 +62,6 @@ static int is_any_reiserfs_magic_string(struct reiserfs_super_block *rs)
static int reiserfs_remount(struct super_block *s, int *flags, char *data);
static int reiserfs_statfs(struct dentry *dentry, struct kstatfs *buf);
-void show_alloc_options(struct seq_file *seq, struct super_block *s);
static int reiserfs_sync_fs(struct super_block *s, int wait)
{
@@ -597,7 +596,7 @@ static void init_once(void *foo)
inode_init_once(&ei->vfs_inode);
}
-static int init_inodecache(void)
+static int __init init_inodecache(void)
{
reiserfs_inode_cachep = kmem_cache_create("reiser_inode_cache",
sizeof(struct
@@ -1319,6 +1318,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)
int i;
#endif
+ sync_filesystem(s);
reiserfs_write_lock(s);
#ifdef CONFIG_QUOTA
diff --git a/fs/romfs/super.c b/fs/romfs/super.c
index d8418782862b..ef90e8bca95a 100644
--- a/fs/romfs/super.c
+++ b/fs/romfs/super.c
@@ -432,6 +432,7 @@ static int romfs_statfs(struct dentry *dentry, struct kstatfs *buf)
*/
static int romfs_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
*flags |= MS_RDONLY;
return 0;
}
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 202df6312d4e..031c8d67fd51 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -371,6 +371,7 @@ static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
static int squashfs_remount(struct super_block *sb, int *flags, char *data)
{
+ sync_filesystem(sb);
*flags |= MS_RDONLY;
return 0;
}
diff --git a/fs/super.c b/fs/super.c
index 80d5cf2ca765..e9dc3c3fe159 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -719,8 +719,6 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
}
}
- sync_filesystem(sb);
-
if (sb->s_op->remount_fs) {
retval = sb->s_op->remount_fs(sb, &flags, data);
if (retval) {
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index c327d4ee1235..88956309cc86 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -60,6 +60,7 @@ static int sysv_remount(struct super_block *sb, int *flags, char *data)
{
struct sysv_sb_info *sbi = SYSV_SB(sb);
+ sync_filesystem(sb);
if (sbi->s_forced_ro)
*flags |= MS_RDONLY;
return 0;
@@ -295,7 +296,7 @@ int sysv_sync_inode(struct inode *inode)
static void sysv_evict_inode(struct inode *inode)
{
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
if (!inode->i_nlink) {
inode->i_size = 0;
sysv_truncate(inode);
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 5ded8490c0c6..a1266089eca1 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -351,7 +351,7 @@ static void ubifs_evict_inode(struct inode *inode)
dbg_gen("inode %lu, mode %#x", inode->i_ino, (int)inode->i_mode);
ubifs_assert(!atomic_read(&inode->i_count));
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
if (inode->i_nlink)
goto done;
@@ -1827,6 +1827,7 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
int err;
struct ubifs_info *c = sb->s_fs_info;
+ sync_filesystem(sb);
dbg_gen("old flags %#lx, new flags %#x", sb->s_flags, *flags);
err = ubifs_parse_options(c, data, 1);
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 982ce05c87ed..5d643706212f 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -146,8 +146,8 @@ void udf_evict_inode(struct inode *inode)
want_delete = 1;
udf_setsize(inode, 0);
udf_update_inode(inode, IS_SYNC(inode));
- } else
- truncate_inode_pages(&inode->i_data, 0);
+ }
+ truncate_inode_pages_final(&inode->i_data);
invalidate_inode_buffers(inode);
clear_inode(inode);
if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB &&
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 3306b9f69bed..64f2b7334d08 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -646,6 +646,7 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
int error = 0;
struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb);
+ sync_filesystem(sb);
if (lvidiu) {
int write_rev = le16_to_cpu(lvidiu->minUDFWriteRev);
if (write_rev > UDF_MAX_WRITE_VERSION && !(*flags & MS_RDONLY))
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index c8ca96086784..61e8a9b021dd 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -885,7 +885,7 @@ void ufs_evict_inode(struct inode * inode)
if (!inode->i_nlink && !is_bad_inode(inode))
want_delete = 1;
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
if (want_delete) {
loff_t old_i_size;
/*UFS_I(inode)->i_dtime = CURRENT_TIME;*/
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index 329f2f53b7ed..b8c6791f046f 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -1280,6 +1280,7 @@ static int ufs_remount (struct super_block *sb, int *mount_flags, char *data)
unsigned new_mount_opt, ufstype;
unsigned flags;
+ sync_filesystem(sb);
lock_ufs(sb);
mutex_lock(&UFS_SB(sb)->s_lock);
uspi = UFS_SB(sb)->s_uspi;
diff --git a/fs/xfs/kmem.c b/fs/xfs/kmem.c
index 66a36befc5c0..844e288b9576 100644
--- a/fs/xfs/kmem.c
+++ b/fs/xfs/kmem.c
@@ -65,12 +65,31 @@ kmem_alloc(size_t size, xfs_km_flags_t flags)
void *
kmem_zalloc_large(size_t size, xfs_km_flags_t flags)
{
+ unsigned noio_flag = 0;
void *ptr;
+ gfp_t lflags;
ptr = kmem_zalloc(size, flags | KM_MAYFAIL);
if (ptr)
return ptr;
- return vzalloc(size);
+
+ /*
+ * __vmalloc() will allocate data pages and auxillary structures (e.g.
+ * pagetables) with GFP_KERNEL, yet we may be under GFP_NOFS context
+ * here. Hence we need to tell memory reclaim that we are in such a
+ * context via PF_MEMALLOC_NOIO to prevent memory reclaim re-entering
+ * the filesystem here and potentially deadlocking.
+ */
+ if ((current->flags & PF_FSTRANS) || (flags & KM_NOFS))
+ noio_flag = memalloc_noio_save();
+
+ lflags = kmem_flags_convert(flags);
+ ptr = __vmalloc(size, lflags | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL);
+
+ if ((current->flags & PF_FSTRANS) || (flags & KM_NOFS))
+ memalloc_noio_restore(noio_flag);
+
+ return ptr;
}
void
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
index 0ecec1896f25..6888ad886ff6 100644
--- a/fs/xfs/xfs_acl.c
+++ b/fs/xfs/xfs_acl.c
@@ -281,7 +281,7 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (!acl)
goto set_acl;
- error = -EINVAL;
+ error = -E2BIG;
if (acl->a_count > XFS_ACL_MAX_ENTRIES(XFS_M(inode->i_sb)))
return error;
diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h
index 3fc109819c34..0fdd4109c624 100644
--- a/fs/xfs/xfs_ag.h
+++ b/fs/xfs/xfs_ag.h
@@ -89,6 +89,8 @@ typedef struct xfs_agf {
/* structure must be padded to 64 bit alignment */
} xfs_agf_t;
+#define XFS_AGF_CRC_OFF offsetof(struct xfs_agf, agf_crc)
+
#define XFS_AGF_MAGICNUM 0x00000001
#define XFS_AGF_VERSIONNUM 0x00000002
#define XFS_AGF_SEQNO 0x00000004
@@ -167,6 +169,8 @@ typedef struct xfs_agi {
/* structure must be padded to 64 bit alignment */
} xfs_agi_t;
+#define XFS_AGI_CRC_OFF offsetof(struct xfs_agi, agi_crc)
+
#define XFS_AGI_MAGICNUM 0x00000001
#define XFS_AGI_VERSIONNUM 0x00000002
#define XFS_AGI_SEQNO 0x00000004
@@ -222,6 +226,8 @@ typedef struct xfs_agfl {
__be32 agfl_bno[]; /* actually XFS_AGFL_SIZE(mp) */
} xfs_agfl_t;
+#define XFS_AGFL_CRC_OFF offsetof(struct xfs_agfl, agfl_crc)
+
/*
* tags for inode radix tree
*/
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c
index 9eab2dfdcbb5..c1cf6a336a72 100644
--- a/fs/xfs/xfs_alloc.c
+++ b/fs/xfs/xfs_alloc.c
@@ -474,7 +474,6 @@ xfs_agfl_read_verify(
struct xfs_buf *bp)
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- int agfl_ok = 1;
/*
* There is no verification of non-crc AGFLs because mkfs does not
@@ -485,15 +484,13 @@ xfs_agfl_read_verify(
if (!xfs_sb_version_hascrc(&mp->m_sb))
return;
- agfl_ok = xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- offsetof(struct xfs_agfl, agfl_crc));
-
- agfl_ok = agfl_ok && xfs_agfl_verify(bp);
-
- if (!agfl_ok) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+ if (!xfs_buf_verify_cksum(bp, XFS_AGFL_CRC_OFF))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (!xfs_agfl_verify(bp))
xfs_buf_ioerror(bp, EFSCORRUPTED);
- }
+
+ if (bp->b_error)
+ xfs_verifier_error(bp);
}
static void
@@ -508,16 +505,15 @@ xfs_agfl_write_verify(
return;
if (!xfs_agfl_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
if (bip)
XFS_BUF_TO_AGFL(bp)->agfl_lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
- offsetof(struct xfs_agfl, agfl_crc));
+ xfs_buf_update_cksum(bp, XFS_AGFL_CRC_OFF);
}
const struct xfs_buf_ops xfs_agfl_buf_ops = {
@@ -2238,19 +2234,17 @@ xfs_agf_read_verify(
struct xfs_buf *bp)
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- int agf_ok = 1;
-
- if (xfs_sb_version_hascrc(&mp->m_sb))
- agf_ok = xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- offsetof(struct xfs_agf, agf_crc));
- agf_ok = agf_ok && xfs_agf_verify(mp, bp);
-
- if (unlikely(XFS_TEST_ERROR(!agf_ok, mp, XFS_ERRTAG_ALLOC_READ_AGF,
- XFS_RANDOM_ALLOC_READ_AGF))) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+ if (xfs_sb_version_hascrc(&mp->m_sb) &&
+ !xfs_buf_verify_cksum(bp, XFS_AGF_CRC_OFF))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (XFS_TEST_ERROR(!xfs_agf_verify(mp, bp), mp,
+ XFS_ERRTAG_ALLOC_READ_AGF,
+ XFS_RANDOM_ALLOC_READ_AGF))
xfs_buf_ioerror(bp, EFSCORRUPTED);
- }
+
+ if (bp->b_error)
+ xfs_verifier_error(bp);
}
static void
@@ -2261,8 +2255,8 @@ xfs_agf_write_verify(
struct xfs_buf_log_item *bip = bp->b_fspriv;
if (!xfs_agf_verify(mp, bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
@@ -2272,8 +2266,7 @@ xfs_agf_write_verify(
if (bip)
XFS_BUF_TO_AGF(bp)->agf_lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
- offsetof(struct xfs_agf, agf_crc));
+ xfs_buf_update_cksum(bp, XFS_AGF_CRC_OFF);
}
const struct xfs_buf_ops xfs_agf_buf_ops = {
diff --git a/fs/xfs/xfs_alloc_btree.c b/fs/xfs/xfs_alloc_btree.c
index 13085429e523..cc1eadcbb049 100644
--- a/fs/xfs/xfs_alloc_btree.c
+++ b/fs/xfs/xfs_alloc_btree.c
@@ -355,12 +355,14 @@ static void
xfs_allocbt_read_verify(
struct xfs_buf *bp)
{
- if (!(xfs_btree_sblock_verify_crc(bp) &&
- xfs_allocbt_verify(bp))) {
- trace_xfs_btree_corrupt(bp, _RET_IP_);
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
- bp->b_target->bt_mount, bp->b_addr);
+ if (!xfs_btree_sblock_verify_crc(bp))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (!xfs_allocbt_verify(bp))
xfs_buf_ioerror(bp, EFSCORRUPTED);
+
+ if (bp->b_error) {
+ trace_xfs_btree_corrupt(bp, _RET_IP_);
+ xfs_verifier_error(bp);
}
}
@@ -370,9 +372,9 @@ xfs_allocbt_write_verify(
{
if (!xfs_allocbt_verify(bp)) {
trace_xfs_btree_corrupt(bp, _RET_IP_);
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
- bp->b_target->bt_mount, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
+ return;
}
xfs_btree_sblock_calc_crc(bp);
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index db2cfb067d0b..75df77d09f75 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -632,38 +632,46 @@ xfs_map_at_offset(
}
/*
- * Test if a given page is suitable for writing as part of an unwritten
- * or delayed allocate extent.
+ * Test if a given page contains at least one buffer of a given @type.
+ * If @check_all_buffers is true, then we walk all the buffers in the page to
+ * try to find one of the type passed in. If it is not set, then the caller only
+ * needs to check the first buffer on the page for a match.
*/
-STATIC int
+STATIC bool
xfs_check_page_type(
struct page *page,
- unsigned int type)
+ unsigned int type,
+ bool check_all_buffers)
{
- if (PageWriteback(page))
- return 0;
+ struct buffer_head *bh;
+ struct buffer_head *head;
- if (page->mapping && page_has_buffers(page)) {
- struct buffer_head *bh, *head;
- int acceptable = 0;
+ if (PageWriteback(page))
+ return false;
+ if (!page->mapping)
+ return false;
+ if (!page_has_buffers(page))
+ return false;
- bh = head = page_buffers(page);
- do {
- if (buffer_unwritten(bh))
- acceptable += (type == XFS_IO_UNWRITTEN);
- else if (buffer_delay(bh))
- acceptable += (type == XFS_IO_DELALLOC);
- else if (buffer_dirty(bh) && buffer_mapped(bh))
- acceptable += (type == XFS_IO_OVERWRITE);
- else
- break;
- } while ((bh = bh->b_this_page) != head);
+ bh = head = page_buffers(page);
+ do {
+ if (buffer_unwritten(bh)) {
+ if (type == XFS_IO_UNWRITTEN)
+ return true;
+ } else if (buffer_delay(bh)) {
+ if (type == XFS_IO_DELALLOC)
+ return true;
+ } else if (buffer_dirty(bh) && buffer_mapped(bh)) {
+ if (type == XFS_IO_OVERWRITE)
+ return true;
+ }
- if (acceptable)
- return 1;
- }
+ /* If we are only checking the first buffer, we are done now. */
+ if (!check_all_buffers)
+ break;
+ } while ((bh = bh->b_this_page) != head);
- return 0;
+ return false;
}
/*
@@ -697,7 +705,7 @@ xfs_convert_page(
goto fail_unlock_page;
if (page->mapping != inode->i_mapping)
goto fail_unlock_page;
- if (!xfs_check_page_type(page, (*ioendp)->io_type))
+ if (!xfs_check_page_type(page, (*ioendp)->io_type, false))
goto fail_unlock_page;
/*
@@ -742,6 +750,15 @@ xfs_convert_page(
p_offset = p_offset ? roundup(p_offset, len) : PAGE_CACHE_SIZE;
page_dirty = p_offset / len;
+ /*
+ * The moment we find a buffer that doesn't match our current type
+ * specification or can't be written, abort the loop and start
+ * writeback. As per the above xfs_imap_valid() check, only
+ * xfs_vm_writepage() can handle partial page writeback fully - we are
+ * limited here to the buffers that are contiguous with the current
+ * ioend, and hence a buffer we can't write breaks that contiguity and
+ * we have to defer the rest of the IO to xfs_vm_writepage().
+ */
bh = head = page_buffers(page);
do {
if (offset >= end_offset)
@@ -750,7 +767,7 @@ xfs_convert_page(
uptodate = 0;
if (!(PageUptodate(page) || buffer_uptodate(bh))) {
done = 1;
- continue;
+ break;
}
if (buffer_unwritten(bh) || buffer_delay(bh) ||
@@ -762,10 +779,11 @@ xfs_convert_page(
else
type = XFS_IO_OVERWRITE;
- if (!xfs_imap_valid(inode, imap, offset)) {
- done = 1;
- continue;
- }
+ /*
+ * imap should always be valid because of the above
+ * partial page end_offset check on the imap.
+ */
+ ASSERT(xfs_imap_valid(inode, imap, offset));
lock_buffer(bh);
if (type != XFS_IO_OVERWRITE)
@@ -777,6 +795,7 @@ xfs_convert_page(
count++;
} else {
done = 1;
+ break;
}
} while (offset += len, (bh = bh->b_this_page) != head);
@@ -868,7 +887,7 @@ xfs_aops_discard_page(
struct buffer_head *bh, *head;
loff_t offset = page_offset(page);
- if (!xfs_check_page_type(page, XFS_IO_DELALLOC))
+ if (!xfs_check_page_type(page, XFS_IO_DELALLOC, true))
goto out_invalidate;
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
@@ -1441,7 +1460,8 @@ xfs_vm_direct_IO(
ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iov,
offset, nr_segs,
xfs_get_blocks_direct,
- xfs_end_io_direct_write, NULL, 0);
+ xfs_end_io_direct_write, NULL,
+ DIO_ASYNC_EXTEND);
if (ret != -EIOCBQUEUED && iocb->private)
goto out_destroy_ioend;
} else {
diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c
index 7b126f46a2f9..fe9587fab17a 100644
--- a/fs/xfs/xfs_attr_leaf.c
+++ b/fs/xfs/xfs_attr_leaf.c
@@ -213,8 +213,8 @@ xfs_attr3_leaf_write_verify(
struct xfs_attr3_leaf_hdr *hdr3 = bp->b_addr;
if (!xfs_attr3_leaf_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
@@ -224,7 +224,7 @@ xfs_attr3_leaf_write_verify(
if (bip)
hdr3->info.lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), XFS_ATTR3_LEAF_CRC_OFF);
+ xfs_buf_update_cksum(bp, XFS_ATTR3_LEAF_CRC_OFF);
}
/*
@@ -239,13 +239,14 @@ xfs_attr3_leaf_read_verify(
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- if ((xfs_sb_version_hascrc(&mp->m_sb) &&
- !xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- XFS_ATTR3_LEAF_CRC_OFF)) ||
- !xfs_attr3_leaf_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+ if (xfs_sb_version_hascrc(&mp->m_sb) &&
+ !xfs_buf_verify_cksum(bp, XFS_ATTR3_LEAF_CRC_OFF))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (!xfs_attr3_leaf_verify(bp))
xfs_buf_ioerror(bp, EFSCORRUPTED);
- }
+
+ if (bp->b_error)
+ xfs_verifier_error(bp);
}
const struct xfs_buf_ops xfs_attr3_leaf_buf_ops = {
diff --git a/fs/xfs/xfs_attr_remote.c b/fs/xfs/xfs_attr_remote.c
index 5549d69ddb45..6e37823e2932 100644
--- a/fs/xfs/xfs_attr_remote.c
+++ b/fs/xfs/xfs_attr_remote.c
@@ -125,7 +125,6 @@ xfs_attr3_rmt_read_verify(
struct xfs_mount *mp = bp->b_target->bt_mount;
char *ptr;
int len;
- bool corrupt = false;
xfs_daddr_t bno;
/* no verification of non-crc buffers */
@@ -140,11 +139,11 @@ xfs_attr3_rmt_read_verify(
while (len > 0) {
if (!xfs_verify_cksum(ptr, XFS_LBSIZE(mp),
XFS_ATTR3_RMT_CRC_OFF)) {
- corrupt = true;
+ xfs_buf_ioerror(bp, EFSBADCRC);
break;
}
if (!xfs_attr3_rmt_verify(mp, ptr, XFS_LBSIZE(mp), bno)) {
- corrupt = true;
+ xfs_buf_ioerror(bp, EFSCORRUPTED);
break;
}
len -= XFS_LBSIZE(mp);
@@ -152,10 +151,9 @@ xfs_attr3_rmt_read_verify(
bno += mp->m_bsize;
}
- if (corrupt) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
- xfs_buf_ioerror(bp, EFSCORRUPTED);
- } else
+ if (bp->b_error)
+ xfs_verifier_error(bp);
+ else
ASSERT(len == 0);
}
@@ -180,9 +178,8 @@ xfs_attr3_rmt_write_verify(
while (len > 0) {
if (!xfs_attr3_rmt_verify(mp, ptr, XFS_LBSIZE(mp), bno)) {
- XFS_CORRUPTION_ERROR(__func__,
- XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
if (bip) {
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index 152543c4ca70..5b6092ef51ef 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -5378,3 +5378,196 @@ error0:
}
return error;
}
+
+/*
+ * Shift extent records to the left to cover a hole.
+ *
+ * The maximum number of extents to be shifted in a single operation
+ * is @num_exts, and @current_ext keeps track of the current extent
+ * index we have shifted. @offset_shift_fsb is the length by which each
+ * extent is shifted. If there is no hole to shift the extents
+ * into, this will be considered invalid operation and we abort immediately.
+ */
+int
+xfs_bmap_shift_extents(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ int *done,
+ xfs_fileoff_t start_fsb,
+ xfs_fileoff_t offset_shift_fsb,
+ xfs_extnum_t *current_ext,
+ xfs_fsblock_t *firstblock,
+ struct xfs_bmap_free *flist,
+ int num_exts)
+{
+ struct xfs_btree_cur *cur;
+ struct xfs_bmbt_rec_host *gotp;
+ struct xfs_bmbt_irec got;
+ struct xfs_bmbt_irec left;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_ifork *ifp;
+ xfs_extnum_t nexts = 0;
+ xfs_fileoff_t startoff;
+ int error = 0;
+ int i;
+ int whichfork = XFS_DATA_FORK;
+ int logflags;
+ xfs_filblks_t blockcount = 0;
+
+ if (unlikely(XFS_TEST_ERROR(
+ (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE),
+ mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) {
+ XFS_ERROR_REPORT("xfs_bmap_shift_extents",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ ASSERT(current_ext != NULL);
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+
+ if (!(ifp->if_flags & XFS_IFEXTENTS)) {
+ /* Read in all the extents */
+ error = xfs_iread_extents(tp, ip, whichfork);
+ if (error)
+ return error;
+ }
+
+ /*
+ * If *current_ext is 0, we would need to lookup the extent
+ * from where we would start shifting and store it in gotp.
+ */
+ if (!*current_ext) {
+ gotp = xfs_iext_bno_to_ext(ifp, start_fsb, current_ext);
+ /*
+ * gotp can be null in 2 cases: 1) if there are no extents
+ * or 2) start_fsb lies in a hole beyond which there are
+ * no extents. Either way, we are done.
+ */
+ if (!gotp) {
+ *done = 1;
+ return 0;
+ }
+ }
+
+ /* We are going to change core inode */
+ logflags = XFS_ILOG_CORE;
+
+ if (ifp->if_flags & XFS_IFBROOT) {
+ cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork);
+ cur->bc_private.b.firstblock = *firstblock;
+ cur->bc_private.b.flist = flist;
+ cur->bc_private.b.flags = 0;
+ } else {
+ cur = NULL;
+ logflags |= XFS_ILOG_DEXT;
+ }
+
+ while (nexts++ < num_exts &&
+ *current_ext < XFS_IFORK_NEXTENTS(ip, whichfork)) {
+
+ gotp = xfs_iext_get_ext(ifp, *current_ext);
+ xfs_bmbt_get_all(gotp, &got);
+ startoff = got.br_startoff - offset_shift_fsb;
+
+ /*
+ * Before shifting extent into hole, make sure that the hole
+ * is large enough to accomodate the shift.
+ */
+ if (*current_ext) {
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp,
+ *current_ext - 1), &left);
+
+ if (startoff < left.br_startoff + left.br_blockcount)
+ error = XFS_ERROR(EINVAL);
+ } else if (offset_shift_fsb > got.br_startoff) {
+ /*
+ * When first extent is shifted, offset_shift_fsb
+ * should be less than the stating offset of
+ * the first extent.
+ */
+ error = XFS_ERROR(EINVAL);
+ }
+
+ if (error)
+ goto del_cursor;
+
+ if (cur) {
+ error = xfs_bmbt_lookup_eq(cur, got.br_startoff,
+ got.br_startblock,
+ got.br_blockcount,
+ &i);
+ if (error)
+ goto del_cursor;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor);
+ }
+
+ /* Check if we can merge 2 adjacent extents */
+ if (*current_ext &&
+ left.br_startoff + left.br_blockcount == startoff &&
+ left.br_startblock + left.br_blockcount ==
+ got.br_startblock &&
+ left.br_state == got.br_state &&
+ left.br_blockcount + got.br_blockcount <= MAXEXTLEN) {
+ blockcount = left.br_blockcount +
+ got.br_blockcount;
+ xfs_iext_remove(ip, *current_ext, 1, 0);
+ if (cur) {
+ error = xfs_btree_delete(cur, &i);
+ if (error)
+ goto del_cursor;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor);
+ }
+ XFS_IFORK_NEXT_SET(ip, whichfork,
+ XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
+ gotp = xfs_iext_get_ext(ifp, --*current_ext);
+ xfs_bmbt_get_all(gotp, &got);
+
+ /* Make cursor point to the extent we will update */
+ if (cur) {
+ error = xfs_bmbt_lookup_eq(cur, got.br_startoff,
+ got.br_startblock,
+ got.br_blockcount,
+ &i);
+ if (error)
+ goto del_cursor;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor);
+ }
+
+ xfs_bmbt_set_blockcount(gotp, blockcount);
+ got.br_blockcount = blockcount;
+ } else {
+ /* We have to update the startoff */
+ xfs_bmbt_set_startoff(gotp, startoff);
+ got.br_startoff = startoff;
+ }
+
+ if (cur) {
+ error = xfs_bmbt_update(cur, got.br_startoff,
+ got.br_startblock,
+ got.br_blockcount,
+ got.br_state);
+ if (error)
+ goto del_cursor;
+ }
+
+ (*current_ext)++;
+ }
+
+ /* Check if we are done */
+ if (*current_ext == XFS_IFORK_NEXTENTS(ip, whichfork))
+ *done = 1;
+
+del_cursor:
+ if (cur)
+ xfs_btree_del_cursor(cur,
+ error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
+ xfs_trans_log_inode(tp, ip, logflags);
+
+ return error;
+}
diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h
index 33b41f351225..f84bd7af43be 100644
--- a/fs/xfs/xfs_bmap.h
+++ b/fs/xfs/xfs_bmap.h
@@ -127,6 +127,16 @@ static inline void xfs_bmap_init(xfs_bmap_free_t *flp, xfs_fsblock_t *fbp)
{ BMAP_RIGHT_FILLING, "RF" }, \
{ BMAP_ATTRFORK, "ATTR" }
+
+/*
+ * This macro is used to determine how many extents will be shifted
+ * in one write transaction. We could require two splits,
+ * an extent move on the first and an extent merge on the second,
+ * So it is proper that one extent is shifted inside write transaction
+ * at a time.
+ */
+#define XFS_BMAP_MAX_SHIFT_EXTENTS 1
+
#ifdef DEBUG
void xfs_bmap_trace_exlist(struct xfs_inode *ip, xfs_extnum_t cnt,
int whichfork, unsigned long caller_ip);
@@ -169,5 +179,10 @@ int xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
int xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx,
xfs_extnum_t num);
uint xfs_default_attroffset(struct xfs_inode *ip);
+int xfs_bmap_shift_extents(struct xfs_trans *tp, struct xfs_inode *ip,
+ int *done, xfs_fileoff_t start_fsb,
+ xfs_fileoff_t offset_shift_fsb, xfs_extnum_t *current_ext,
+ xfs_fsblock_t *firstblock, struct xfs_bmap_free *flist,
+ int num_exts);
#endif /* __XFS_BMAP_H__ */
diff --git a/fs/xfs/xfs_bmap_btree.c b/fs/xfs/xfs_bmap_btree.c
index 706bc3f777cb..818d546664e7 100644
--- a/fs/xfs/xfs_bmap_btree.c
+++ b/fs/xfs/xfs_bmap_btree.c
@@ -780,12 +780,14 @@ static void
xfs_bmbt_read_verify(
struct xfs_buf *bp)
{
- if (!(xfs_btree_lblock_verify_crc(bp) &&
- xfs_bmbt_verify(bp))) {
- trace_xfs_btree_corrupt(bp, _RET_IP_);
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
- bp->b_target->bt_mount, bp->b_addr);
+ if (!xfs_btree_lblock_verify_crc(bp))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (!xfs_bmbt_verify(bp))
xfs_buf_ioerror(bp, EFSCORRUPTED);
+
+ if (bp->b_error) {
+ trace_xfs_btree_corrupt(bp, _RET_IP_);
+ xfs_verifier_error(bp);
}
}
@@ -794,11 +796,9 @@ xfs_bmbt_write_verify(
struct xfs_buf *bp)
{
if (!xfs_bmbt_verify(bp)) {
- xfs_warn(bp->b_target->bt_mount, "bmbt daddr 0x%llx failed", bp->b_bn);
trace_xfs_btree_corrupt(bp, _RET_IP_);
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
- bp->b_target->bt_mount, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
xfs_btree_lblock_calc_crc(bp);
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index f264616080ca..01f6a646caa1 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1349,7 +1349,6 @@ xfs_free_file_space(
* the freeing of the space succeeds at ENOSPC.
*/
tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT);
- tp->t_flags |= XFS_TRANS_RESERVE;
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write, resblks, 0);
/*
@@ -1468,6 +1467,102 @@ out:
}
/*
+ * xfs_collapse_file_space()
+ * This routine frees disk space and shift extent for the given file.
+ * The first thing we do is to free data blocks in the specified range
+ * by calling xfs_free_file_space(). It would also sync dirty data
+ * and invalidate page cache over the region on which collapse range
+ * is working. And Shift extent records to the left to cover a hole.
+ * RETURNS:
+ * 0 on success
+ * errno on error
+ *
+ */
+int
+xfs_collapse_file_space(
+ struct xfs_inode *ip,
+ xfs_off_t offset,
+ xfs_off_t len)
+{
+ int done = 0;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_trans *tp;
+ int error;
+ xfs_extnum_t current_ext = 0;
+ struct xfs_bmap_free free_list;
+ xfs_fsblock_t first_block;
+ int committed;
+ xfs_fileoff_t start_fsb;
+ xfs_fileoff_t shift_fsb;
+
+ ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
+
+ trace_xfs_collapse_file_space(ip);
+
+ start_fsb = XFS_B_TO_FSB(mp, offset + len);
+ shift_fsb = XFS_B_TO_FSB(mp, len);
+
+ error = xfs_free_file_space(ip, offset, len);
+ if (error)
+ return error;
+
+ while (!error && !done) {
+ tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT);
+ tp->t_flags |= XFS_TRANS_RESERVE;
+ /*
+ * We would need to reserve permanent block for transaction.
+ * This will come into picture when after shifting extent into
+ * hole we found that adjacent extents can be merged which
+ * may lead to freeing of a block during record update.
+ */
+ error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write,
+ XFS_DIOSTRAT_SPACE_RES(mp, 0), 0);
+ if (error) {
+ ASSERT(error == ENOSPC || XFS_FORCED_SHUTDOWN(mp));
+ xfs_trans_cancel(tp, 0);
+ break;
+ }
+
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ error = xfs_trans_reserve_quota(tp, mp, ip->i_udquot,
+ ip->i_gdquot, ip->i_pdquot,
+ XFS_DIOSTRAT_SPACE_RES(mp, 0), 0,
+ XFS_QMOPT_RES_REGBLKS);
+ if (error)
+ goto out;
+
+ xfs_trans_ijoin(tp, ip, 0);
+
+ xfs_bmap_init(&free_list, &first_block);
+
+ /*
+ * We are using the write transaction in which max 2 bmbt
+ * updates are allowed
+ */
+ error = xfs_bmap_shift_extents(tp, ip, &done, start_fsb,
+ shift_fsb, &current_ext,
+ &first_block, &free_list,
+ XFS_BMAP_MAX_SHIFT_EXTENTS);
+ if (error)
+ goto out;
+
+ error = xfs_bmap_finish(&tp, &free_list, &committed);
+ if (error)
+ goto out;
+
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
+
+ return error;
+
+out:
+ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ return error;
+}
+
+/*
* We need to check that the format of the data fork in the temporary inode is
* valid for the target inode before doing the swap. This is not a problem with
* attr1 because of the fixed fork offset, but attr2 has a dynamically sized
diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
index 900747b25772..935ed2b24edf 100644
--- a/fs/xfs/xfs_bmap_util.h
+++ b/fs/xfs/xfs_bmap_util.h
@@ -99,6 +99,8 @@ int xfs_free_file_space(struct xfs_inode *ip, xfs_off_t offset,
xfs_off_t len);
int xfs_zero_file_space(struct xfs_inode *ip, xfs_off_t offset,
xfs_off_t len);
+int xfs_collapse_file_space(struct xfs_inode *, xfs_off_t offset,
+ xfs_off_t len);
/* EOF block manipulation functions */
bool xfs_can_free_eofblocks(struct xfs_inode *ip, bool force);
diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c
index 9adaae4f3e2f..e80d59fdf89a 100644
--- a/fs/xfs/xfs_btree.c
+++ b/fs/xfs/xfs_btree.c
@@ -234,8 +234,7 @@ xfs_btree_lblock_calc_crc(
return;
if (bip)
block->bb_u.l.bb_lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
- XFS_BTREE_LBLOCK_CRC_OFF);
+ xfs_buf_update_cksum(bp, XFS_BTREE_LBLOCK_CRC_OFF);
}
bool
@@ -243,8 +242,8 @@ xfs_btree_lblock_verify_crc(
struct xfs_buf *bp)
{
if (xfs_sb_version_hascrc(&bp->b_target->bt_mount->m_sb))
- return xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- XFS_BTREE_LBLOCK_CRC_OFF);
+ return xfs_buf_verify_cksum(bp, XFS_BTREE_LBLOCK_CRC_OFF);
+
return true;
}
@@ -267,8 +266,7 @@ xfs_btree_sblock_calc_crc(
return;
if (bip)
block->bb_u.s.bb_lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
- XFS_BTREE_SBLOCK_CRC_OFF);
+ xfs_buf_update_cksum(bp, XFS_BTREE_SBLOCK_CRC_OFF);
}
bool
@@ -276,8 +274,8 @@ xfs_btree_sblock_verify_crc(
struct xfs_buf *bp)
{
if (xfs_sb_version_hascrc(&bp->b_target->bt_mount->m_sb))
- return xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- XFS_BTREE_SBLOCK_CRC_OFF);
+ return xfs_buf_verify_cksum(bp, XFS_BTREE_SBLOCK_CRC_OFF);
+
return true;
}
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index 9c061ef2b0d9..107f2fdfe41f 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -396,7 +396,17 @@ _xfs_buf_map_pages(
bp->b_addr = NULL;
} else {
int retried = 0;
+ unsigned noio_flag;
+ /*
+ * vm_map_ram() will allocate auxillary structures (e.g.
+ * pagetables) with GFP_KERNEL, yet we are likely to be under
+ * GFP_NOFS context here. Hence we need to tell memory reclaim
+ * that we are in such a context via PF_MEMALLOC_NOIO to prevent
+ * memory reclaim re-entering the filesystem here and
+ * potentially deadlocking.
+ */
+ noio_flag = memalloc_noio_save();
do {
bp->b_addr = vm_map_ram(bp->b_pages, bp->b_page_count,
-1, PAGE_KERNEL);
@@ -404,6 +414,7 @@ _xfs_buf_map_pages(
break;
vm_unmap_aliases();
} while (retried++ <= 1);
+ memalloc_noio_restore(noio_flag);
if (!bp->b_addr)
return -ENOMEM;
diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h
index 995339534db6..b8a3abf6cf47 100644
--- a/fs/xfs/xfs_buf.h
+++ b/fs/xfs/xfs_buf.h
@@ -369,6 +369,20 @@ static inline void xfs_buf_relse(xfs_buf_t *bp)
xfs_buf_rele(bp);
}
+static inline int
+xfs_buf_verify_cksum(struct xfs_buf *bp, unsigned long cksum_offset)
+{
+ return xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
+ cksum_offset);
+}
+
+static inline void
+xfs_buf_update_cksum(struct xfs_buf *bp, unsigned long cksum_offset)
+{
+ xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
+ cksum_offset);
+}
+
/*
* Handling of buftargs.
*/
diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c
index 33149113e333..8752821443be 100644
--- a/fs/xfs/xfs_buf_item.c
+++ b/fs/xfs/xfs_buf_item.c
@@ -796,20 +796,6 @@ xfs_buf_item_init(
bip->bli_formats[i].blf_map_size = map_size;
}
-#ifdef XFS_TRANS_DEBUG
- /*
- * Allocate the arrays for tracking what needs to be logged
- * and what our callers request to be logged. bli_orig
- * holds a copy of the original, clean buffer for comparison
- * against, and bli_logged keeps a 1 bit flag per byte in
- * the buffer to indicate which bytes the callers have asked
- * to have logged.
- */
- bip->bli_orig = kmem_alloc(BBTOB(bp->b_length), KM_SLEEP);
- memcpy(bip->bli_orig, bp->b_addr, BBTOB(bp->b_length));
- bip->bli_logged = kmem_zalloc(BBTOB(bp->b_length) / NBBY, KM_SLEEP);
-#endif
-
/*
* Put the buf item into the list of items attached to the
* buffer at the front.
@@ -957,11 +943,6 @@ STATIC void
xfs_buf_item_free(
xfs_buf_log_item_t *bip)
{
-#ifdef XFS_TRANS_DEBUG
- kmem_free(bip->bli_orig);
- kmem_free(bip->bli_logged);
-#endif /* XFS_TRANS_DEBUG */
-
xfs_buf_item_free_format(bip);
kmem_zone_free(xfs_buf_item_zone, bip);
}
diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c
index 796272a2e129..6cc5f6785a77 100644
--- a/fs/xfs/xfs_da_btree.c
+++ b/fs/xfs/xfs_da_btree.c
@@ -185,8 +185,8 @@ xfs_da3_node_write_verify(
struct xfs_da3_node_hdr *hdr3 = bp->b_addr;
if (!xfs_da3_node_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
@@ -196,7 +196,7 @@ xfs_da3_node_write_verify(
if (bip)
hdr3->info.lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), XFS_DA3_NODE_CRC_OFF);
+ xfs_buf_update_cksum(bp, XFS_DA3_NODE_CRC_OFF);
}
/*
@@ -209,18 +209,20 @@ static void
xfs_da3_node_read_verify(
struct xfs_buf *bp)
{
- struct xfs_mount *mp = bp->b_target->bt_mount;
struct xfs_da_blkinfo *info = bp->b_addr;
switch (be16_to_cpu(info->magic)) {
case XFS_DA3_NODE_MAGIC:
- if (!xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- XFS_DA3_NODE_CRC_OFF))
+ if (!xfs_buf_verify_cksum(bp, XFS_DA3_NODE_CRC_OFF)) {
+ xfs_buf_ioerror(bp, EFSBADCRC);
break;
+ }
/* fall through */
case XFS_DA_NODE_MAGIC:
- if (!xfs_da3_node_verify(bp))
+ if (!xfs_da3_node_verify(bp)) {
+ xfs_buf_ioerror(bp, EFSCORRUPTED);
break;
+ }
return;
case XFS_ATTR_LEAF_MAGIC:
case XFS_ATTR3_LEAF_MAGIC:
@@ -237,8 +239,7 @@ xfs_da3_node_read_verify(
}
/* corrupt block */
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
- xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
}
const struct xfs_buf_ops xfs_da3_node_buf_ops = {
@@ -1295,7 +1296,7 @@ xfs_da3_fixhashpath(
node = blk->bp->b_addr;
dp->d_ops->node_hdr_from_disk(&nodehdr, node);
btree = dp->d_ops->node_tree_p(node);
- if (be32_to_cpu(btree->hashval) == lasthash)
+ if (be32_to_cpu(btree[blk->index].hashval) == lasthash)
break;
blk->hashval = lasthash;
btree[blk->index].hashval = cpu_to_be32(lasthash);
diff --git a/fs/xfs/xfs_dinode.h b/fs/xfs/xfs_dinode.h
index e5869b50dc41..623bbe8fd921 100644
--- a/fs/xfs/xfs_dinode.h
+++ b/fs/xfs/xfs_dinode.h
@@ -89,6 +89,8 @@ typedef struct xfs_dinode {
/* structure must be padded to 64 bit alignment */
} xfs_dinode_t;
+#define XFS_DINODE_CRC_OFF offsetof(struct xfs_dinode, di_crc)
+
#define DI_MAX_FLUSH 0xffff
/*
diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c
index ce16ef02997a..fda46253966a 100644
--- a/fs/xfs/xfs_dir2.c
+++ b/fs/xfs/xfs_dir2.c
@@ -180,16 +180,23 @@ xfs_dir_init(
xfs_inode_t *dp,
xfs_inode_t *pdp)
{
- xfs_da_args_t args;
+ struct xfs_da_args *args;
int error;
- memset((char *)&args, 0, sizeof(args));
- args.dp = dp;
- args.trans = tp;
ASSERT(S_ISDIR(dp->i_d.di_mode));
- if ((error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino)))
+ error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino);
+ if (error)
return error;
- return xfs_dir2_sf_create(&args, pdp->i_ino);
+
+ args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
+ if (!args)
+ return ENOMEM;
+
+ args->dp = dp;
+ args->trans = tp;
+ error = xfs_dir2_sf_create(args, pdp->i_ino);
+ kmem_free(args);
+ return error;
}
/*
@@ -205,41 +212,56 @@ xfs_dir_createname(
xfs_bmap_free_t *flist, /* bmap's freeblock list */
xfs_extlen_t total) /* bmap's total block count */
{
- xfs_da_args_t args;
+ struct xfs_da_args *args;
int rval;
int v; /* type-checking value */
ASSERT(S_ISDIR(dp->i_d.di_mode));
- if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
+ rval = xfs_dir_ino_validate(tp->t_mountp, inum);
+ if (rval)
return rval;
XFS_STATS_INC(xs_dir_create);
- memset(&args, 0, sizeof(xfs_da_args_t));
- args.name = name->name;
- args.namelen = name->len;
- args.filetype = name->type;
- args.hashval = dp->i_mount->m_dirnameops->hashname(name);
- args.inumber = inum;
- args.dp = dp;
- args.firstblock = first;
- args.flist = flist;
- args.total = total;
- args.whichfork = XFS_DATA_FORK;
- args.trans = tp;
- args.op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
-
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
- rval = xfs_dir2_sf_addname(&args);
- else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
- return rval;
- else if (v)
- rval = xfs_dir2_block_addname(&args);
- else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
- return rval;
- else if (v)
- rval = xfs_dir2_leaf_addname(&args);
+ args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
+ if (!args)
+ return ENOMEM;
+
+ args->name = name->name;
+ args->namelen = name->len;
+ args->filetype = name->type;
+ args->hashval = dp->i_mount->m_dirnameops->hashname(name);
+ args->inumber = inum;
+ args->dp = dp;
+ args->firstblock = first;
+ args->flist = flist;
+ args->total = total;
+ args->whichfork = XFS_DATA_FORK;
+ args->trans = tp;
+ args->op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
+
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ rval = xfs_dir2_sf_addname(args);
+ goto out_free;
+ }
+
+ rval = xfs_dir2_isblock(tp, dp, &v);
+ if (rval)
+ goto out_free;
+ if (v) {
+ rval = xfs_dir2_block_addname(args);
+ goto out_free;
+ }
+
+ rval = xfs_dir2_isleaf(tp, dp, &v);
+ if (rval)
+ goto out_free;
+ if (v)
+ rval = xfs_dir2_leaf_addname(args);
else
- rval = xfs_dir2_node_addname(&args);
+ rval = xfs_dir2_node_addname(args);
+
+out_free:
+ kmem_free(args);
return rval;
}
@@ -282,46 +304,66 @@ xfs_dir_lookup(
xfs_ino_t *inum, /* out: inode number */
struct xfs_name *ci_name) /* out: actual name if CI match */
{
- xfs_da_args_t args;
+ struct xfs_da_args *args;
int rval;
int v; /* type-checking value */
ASSERT(S_ISDIR(dp->i_d.di_mode));
XFS_STATS_INC(xs_dir_lookup);
- memset(&args, 0, sizeof(xfs_da_args_t));
- args.name = name->name;
- args.namelen = name->len;
- args.filetype = name->type;
- args.hashval = dp->i_mount->m_dirnameops->hashname(name);
- args.dp = dp;
- args.whichfork = XFS_DATA_FORK;
- args.trans = tp;
- args.op_flags = XFS_DA_OP_OKNOENT;
+ /*
+ * We need to use KM_NOFS here so that lockdep will not throw false
+ * positive deadlock warnings on a non-transactional lookup path. It is
+ * safe to recurse into inode recalim in that case, but lockdep can't
+ * easily be taught about it. Hence KM_NOFS avoids having to add more
+ * lockdep Doing this avoids having to add a bunch of lockdep class
+ * annotations into the reclaim path for the ilock.
+ */
+ args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
+ args->name = name->name;
+ args->namelen = name->len;
+ args->filetype = name->type;
+ args->hashval = dp->i_mount->m_dirnameops->hashname(name);
+ args->dp = dp;
+ args->whichfork = XFS_DATA_FORK;
+ args->trans = tp;
+ args->op_flags = XFS_DA_OP_OKNOENT;
if (ci_name)
- args.op_flags |= XFS_DA_OP_CILOOKUP;
+ args->op_flags |= XFS_DA_OP_CILOOKUP;
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
- rval = xfs_dir2_sf_lookup(&args);
- else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
- return rval;
- else if (v)
- rval = xfs_dir2_block_lookup(&args);
- else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
- return rval;
- else if (v)
- rval = xfs_dir2_leaf_lookup(&args);
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ rval = xfs_dir2_sf_lookup(args);
+ goto out_check_rval;
+ }
+
+ rval = xfs_dir2_isblock(tp, dp, &v);
+ if (rval)
+ goto out_free;
+ if (v) {
+ rval = xfs_dir2_block_lookup(args);
+ goto out_check_rval;
+ }
+
+ rval = xfs_dir2_isleaf(tp, dp, &v);
+ if (rval)
+ goto out_free;
+ if (v)
+ rval = xfs_dir2_leaf_lookup(args);
else
- rval = xfs_dir2_node_lookup(&args);
+ rval = xfs_dir2_node_lookup(args);
+
+out_check_rval:
if (rval == EEXIST)
rval = 0;
if (!rval) {
- *inum = args.inumber;
+ *inum = args->inumber;
if (ci_name) {
- ci_name->name = args.value;
- ci_name->len = args.valuelen;
+ ci_name->name = args->value;
+ ci_name->len = args->valuelen;
}
}
+out_free:
+ kmem_free(args);
return rval;
}
@@ -338,38 +380,51 @@ xfs_dir_removename(
xfs_bmap_free_t *flist, /* bmap's freeblock list */
xfs_extlen_t total) /* bmap's total block count */
{
- xfs_da_args_t args;
+ struct xfs_da_args *args;
int rval;
int v; /* type-checking value */
ASSERT(S_ISDIR(dp->i_d.di_mode));
XFS_STATS_INC(xs_dir_remove);
- memset(&args, 0, sizeof(xfs_da_args_t));
- args.name = name->name;
- args.namelen = name->len;
- args.filetype = name->type;
- args.hashval = dp->i_mount->m_dirnameops->hashname(name);
- args.inumber = ino;
- args.dp = dp;
- args.firstblock = first;
- args.flist = flist;
- args.total = total;
- args.whichfork = XFS_DATA_FORK;
- args.trans = tp;
-
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
- rval = xfs_dir2_sf_removename(&args);
- else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
- return rval;
- else if (v)
- rval = xfs_dir2_block_removename(&args);
- else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
- return rval;
- else if (v)
- rval = xfs_dir2_leaf_removename(&args);
+ args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
+ if (!args)
+ return ENOMEM;
+
+ args->name = name->name;
+ args->namelen = name->len;
+ args->filetype = name->type;
+ args->hashval = dp->i_mount->m_dirnameops->hashname(name);
+ args->inumber = ino;
+ args->dp = dp;
+ args->firstblock = first;
+ args->flist = flist;
+ args->total = total;
+ args->whichfork = XFS_DATA_FORK;
+ args->trans = tp;
+
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ rval = xfs_dir2_sf_removename(args);
+ goto out_free;
+ }
+
+ rval = xfs_dir2_isblock(tp, dp, &v);
+ if (rval)
+ goto out_free;
+ if (v) {
+ rval = xfs_dir2_block_removename(args);
+ goto out_free;
+ }
+
+ rval = xfs_dir2_isleaf(tp, dp, &v);
+ if (rval)
+ goto out_free;
+ if (v)
+ rval = xfs_dir2_leaf_removename(args);
else
- rval = xfs_dir2_node_removename(&args);
+ rval = xfs_dir2_node_removename(args);
+out_free:
+ kmem_free(args);
return rval;
}
@@ -386,40 +441,54 @@ xfs_dir_replace(
xfs_bmap_free_t *flist, /* bmap's freeblock list */
xfs_extlen_t total) /* bmap's total block count */
{
- xfs_da_args_t args;
+ struct xfs_da_args *args;
int rval;
int v; /* type-checking value */
ASSERT(S_ISDIR(dp->i_d.di_mode));
- if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
+ rval = xfs_dir_ino_validate(tp->t_mountp, inum);
+ if (rval)
return rval;
- memset(&args, 0, sizeof(xfs_da_args_t));
- args.name = name->name;
- args.namelen = name->len;
- args.filetype = name->type;
- args.hashval = dp->i_mount->m_dirnameops->hashname(name);
- args.inumber = inum;
- args.dp = dp;
- args.firstblock = first;
- args.flist = flist;
- args.total = total;
- args.whichfork = XFS_DATA_FORK;
- args.trans = tp;
-
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
- rval = xfs_dir2_sf_replace(&args);
- else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
- return rval;
- else if (v)
- rval = xfs_dir2_block_replace(&args);
- else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
- return rval;
- else if (v)
- rval = xfs_dir2_leaf_replace(&args);
+ args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
+ if (!args)
+ return ENOMEM;
+
+ args->name = name->name;
+ args->namelen = name->len;
+ args->filetype = name->type;
+ args->hashval = dp->i_mount->m_dirnameops->hashname(name);
+ args->inumber = inum;
+ args->dp = dp;
+ args->firstblock = first;
+ args->flist = flist;
+ args->total = total;
+ args->whichfork = XFS_DATA_FORK;
+ args->trans = tp;
+
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ rval = xfs_dir2_sf_replace(args);
+ goto out_free;
+ }
+
+ rval = xfs_dir2_isblock(tp, dp, &v);
+ if (rval)
+ goto out_free;
+ if (v) {
+ rval = xfs_dir2_block_replace(args);
+ goto out_free;
+ }
+
+ rval = xfs_dir2_isleaf(tp, dp, &v);
+ if (rval)
+ goto out_free;
+ if (v)
+ rval = xfs_dir2_leaf_replace(args);
else
- rval = xfs_dir2_node_replace(&args);
+ rval = xfs_dir2_node_replace(args);
+out_free:
+ kmem_free(args);
return rval;
}
@@ -434,7 +503,7 @@ xfs_dir_canenter(
struct xfs_name *name, /* name of entry to add */
uint resblks)
{
- xfs_da_args_t args;
+ struct xfs_da_args *args;
int rval;
int v; /* type-checking value */
@@ -443,29 +512,42 @@ xfs_dir_canenter(
ASSERT(S_ISDIR(dp->i_d.di_mode));
- memset(&args, 0, sizeof(xfs_da_args_t));
- args.name = name->name;
- args.namelen = name->len;
- args.filetype = name->type;
- args.hashval = dp->i_mount->m_dirnameops->hashname(name);
- args.dp = dp;
- args.whichfork = XFS_DATA_FORK;
- args.trans = tp;
- args.op_flags = XFS_DA_OP_JUSTCHECK | XFS_DA_OP_ADDNAME |
+ args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
+ if (!args)
+ return ENOMEM;
+
+ args->name = name->name;
+ args->namelen = name->len;
+ args->filetype = name->type;
+ args->hashval = dp->i_mount->m_dirnameops->hashname(name);
+ args->dp = dp;
+ args->whichfork = XFS_DATA_FORK;
+ args->trans = tp;
+ args->op_flags = XFS_DA_OP_JUSTCHECK | XFS_DA_OP_ADDNAME |
XFS_DA_OP_OKNOENT;
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
- rval = xfs_dir2_sf_addname(&args);
- else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
- return rval;
- else if (v)
- rval = xfs_dir2_block_addname(&args);
- else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
- return rval;
- else if (v)
- rval = xfs_dir2_leaf_addname(&args);
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ rval = xfs_dir2_sf_addname(args);
+ goto out_free;
+ }
+
+ rval = xfs_dir2_isblock(tp, dp, &v);
+ if (rval)
+ goto out_free;
+ if (v) {
+ rval = xfs_dir2_block_addname(args);
+ goto out_free;
+ }
+
+ rval = xfs_dir2_isleaf(tp, dp, &v);
+ if (rval)
+ goto out_free;
+ if (v)
+ rval = xfs_dir2_leaf_addname(args);
else
- rval = xfs_dir2_node_addname(&args);
+ rval = xfs_dir2_node_addname(args);
+out_free:
+ kmem_free(args);
return rval;
}
diff --git a/fs/xfs/xfs_dir2_block.c b/fs/xfs/xfs_dir2_block.c
index 90cdbf4b5f19..4f6a38cb83a4 100644
--- a/fs/xfs/xfs_dir2_block.c
+++ b/fs/xfs/xfs_dir2_block.c
@@ -89,13 +89,14 @@ xfs_dir3_block_read_verify(
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- if ((xfs_sb_version_hascrc(&mp->m_sb) &&
- !xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- XFS_DIR3_DATA_CRC_OFF)) ||
- !xfs_dir3_block_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+ if (xfs_sb_version_hascrc(&mp->m_sb) &&
+ !xfs_buf_verify_cksum(bp, XFS_DIR3_DATA_CRC_OFF))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (!xfs_dir3_block_verify(bp))
xfs_buf_ioerror(bp, EFSCORRUPTED);
- }
+
+ if (bp->b_error)
+ xfs_verifier_error(bp);
}
static void
@@ -107,8 +108,8 @@ xfs_dir3_block_write_verify(
struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
if (!xfs_dir3_block_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
@@ -118,7 +119,7 @@ xfs_dir3_block_write_verify(
if (bip)
hdr3->lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), XFS_DIR3_DATA_CRC_OFF);
+ xfs_buf_update_cksum(bp, XFS_DIR3_DATA_CRC_OFF);
}
const struct xfs_buf_ops xfs_dir3_block_buf_ops = {
diff --git a/fs/xfs/xfs_dir2_data.c b/fs/xfs/xfs_dir2_data.c
index 70acff4ee173..afa4ad523f3f 100644
--- a/fs/xfs/xfs_dir2_data.c
+++ b/fs/xfs/xfs_dir2_data.c
@@ -241,7 +241,6 @@ static void
xfs_dir3_data_reada_verify(
struct xfs_buf *bp)
{
- struct xfs_mount *mp = bp->b_target->bt_mount;
struct xfs_dir2_data_hdr *hdr = bp->b_addr;
switch (hdr->magic) {
@@ -255,8 +254,8 @@ xfs_dir3_data_reada_verify(
xfs_dir3_data_verify(bp);
return;
default:
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, hdr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
break;
}
}
@@ -267,13 +266,14 @@ xfs_dir3_data_read_verify(
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- if ((xfs_sb_version_hascrc(&mp->m_sb) &&
- !xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- XFS_DIR3_DATA_CRC_OFF)) ||
- !xfs_dir3_data_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+ if (xfs_sb_version_hascrc(&mp->m_sb) &&
+ !xfs_buf_verify_cksum(bp, XFS_DIR3_DATA_CRC_OFF))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (!xfs_dir3_data_verify(bp))
xfs_buf_ioerror(bp, EFSCORRUPTED);
- }
+
+ if (bp->b_error)
+ xfs_verifier_error(bp);
}
static void
@@ -285,8 +285,8 @@ xfs_dir3_data_write_verify(
struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
if (!xfs_dir3_data_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
@@ -296,7 +296,7 @@ xfs_dir3_data_write_verify(
if (bip)
hdr3->lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), XFS_DIR3_DATA_CRC_OFF);
+ xfs_buf_update_cksum(bp, XFS_DIR3_DATA_CRC_OFF);
}
const struct xfs_buf_ops xfs_dir3_data_buf_ops = {
diff --git a/fs/xfs/xfs_dir2_leaf.c b/fs/xfs/xfs_dir2_leaf.c
index ae47ec6e16c4..d36e97df1187 100644
--- a/fs/xfs/xfs_dir2_leaf.c
+++ b/fs/xfs/xfs_dir2_leaf.c
@@ -179,13 +179,14 @@ __read_verify(
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- if ((xfs_sb_version_hascrc(&mp->m_sb) &&
- !xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- XFS_DIR3_LEAF_CRC_OFF)) ||
- !xfs_dir3_leaf_verify(bp, magic)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+ if (xfs_sb_version_hascrc(&mp->m_sb) &&
+ !xfs_buf_verify_cksum(bp, XFS_DIR3_LEAF_CRC_OFF))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (!xfs_dir3_leaf_verify(bp, magic))
xfs_buf_ioerror(bp, EFSCORRUPTED);
- }
+
+ if (bp->b_error)
+ xfs_verifier_error(bp);
}
static void
@@ -198,8 +199,8 @@ __write_verify(
struct xfs_dir3_leaf_hdr *hdr3 = bp->b_addr;
if (!xfs_dir3_leaf_verify(bp, magic)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
@@ -209,7 +210,7 @@ __write_verify(
if (bip)
hdr3->info.lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), XFS_DIR3_LEAF_CRC_OFF);
+ xfs_buf_update_cksum(bp, XFS_DIR3_LEAF_CRC_OFF);
}
static void
diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c
index 48c7d18f68c3..cb434d732681 100644
--- a/fs/xfs/xfs_dir2_node.c
+++ b/fs/xfs/xfs_dir2_node.c
@@ -115,13 +115,14 @@ xfs_dir3_free_read_verify(
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- if ((xfs_sb_version_hascrc(&mp->m_sb) &&
- !xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- XFS_DIR3_FREE_CRC_OFF)) ||
- !xfs_dir3_free_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+ if (xfs_sb_version_hascrc(&mp->m_sb) &&
+ !xfs_buf_verify_cksum(bp, XFS_DIR3_FREE_CRC_OFF))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (!xfs_dir3_free_verify(bp))
xfs_buf_ioerror(bp, EFSCORRUPTED);
- }
+
+ if (bp->b_error)
+ xfs_verifier_error(bp);
}
static void
@@ -133,8 +134,8 @@ xfs_dir3_free_write_verify(
struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
if (!xfs_dir3_free_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
@@ -144,7 +145,7 @@ xfs_dir3_free_write_verify(
if (bip)
hdr3->lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), XFS_DIR3_FREE_CRC_OFF);
+ xfs_buf_update_cksum(bp, XFS_DIR3_FREE_CRC_OFF);
}
const struct xfs_buf_ops xfs_dir3_free_buf_ops = {
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index 7aeb4c895b32..868b19f096bf 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -615,7 +615,7 @@ xfs_qm_dqread(
if (flags & XFS_QMOPT_DQALLOC) {
tp = xfs_trans_alloc(mp, XFS_TRANS_QM_DQALLOC);
- error = xfs_trans_reserve(tp, &M_RES(mp)->tr_attrsetm,
+ error = xfs_trans_reserve(tp, &M_RES(mp)->tr_qm_dqalloc,
XFS_QM_DQALLOC_SPACE_RES(mp), 0);
if (error)
goto error1;
diff --git a/fs/xfs/xfs_dquot_buf.c b/fs/xfs/xfs_dquot_buf.c
index d401457d2f25..610da8177737 100644
--- a/fs/xfs/xfs_dquot_buf.c
+++ b/fs/xfs/xfs_dquot_buf.c
@@ -257,10 +257,13 @@ xfs_dquot_buf_read_verify(
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- if (!xfs_dquot_buf_verify_crc(mp, bp) || !xfs_dquot_buf_verify(mp, bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+ if (!xfs_dquot_buf_verify_crc(mp, bp))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (!xfs_dquot_buf_verify(mp, bp))
xfs_buf_ioerror(bp, EFSCORRUPTED);
- }
+
+ if (bp->b_error)
+ xfs_verifier_error(bp);
}
/*
@@ -275,8 +278,8 @@ xfs_dquot_buf_write_verify(
struct xfs_mount *mp = bp->b_target->bt_mount;
if (!xfs_dquot_buf_verify(mp, bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
}
diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c
index 9995b807d627..edac5b057d28 100644
--- a/fs/xfs/xfs_error.c
+++ b/fs/xfs/xfs_error.c
@@ -156,7 +156,7 @@ xfs_error_report(
{
if (level <= xfs_error_level) {
xfs_alert_tag(mp, XFS_PTAG_ERROR_REPORT,
- "Internal error %s at line %d of file %s. Caller 0x%p",
+ "Internal error %s at line %d of file %s. Caller %pF",
tag, linenum, filename, ra);
xfs_stack_trace();
@@ -178,3 +178,28 @@ xfs_corruption_error(
xfs_error_report(tag, level, mp, filename, linenum, ra);
xfs_alert(mp, "Corruption detected. Unmount and run xfs_repair");
}
+
+/*
+ * Warnings specifically for verifier errors. Differentiate CRC vs. invalid
+ * values, and omit the stack trace unless the error level is tuned high.
+ */
+void
+xfs_verifier_error(
+ struct xfs_buf *bp)
+{
+ struct xfs_mount *mp = bp->b_target->bt_mount;
+
+ xfs_alert(mp, "Metadata %s detected at %pF, block 0x%llx",
+ bp->b_error == EFSBADCRC ? "CRC error" : "corruption",
+ __return_address, bp->b_bn);
+
+ xfs_alert(mp, "Unmount and run xfs_repair");
+
+ if (xfs_error_level >= XFS_ERRLEVEL_LOW) {
+ xfs_alert(mp, "First 64 bytes of corrupted metadata buffer:");
+ xfs_hex_dump(xfs_buf_offset(bp, 0), 64);
+ }
+
+ if (xfs_error_level >= XFS_ERRLEVEL_HIGH)
+ xfs_stack_trace();
+}
diff --git a/fs/xfs/xfs_error.h b/fs/xfs/xfs_error.h
index 079a367f44ee..c1c57d4a4b5d 100644
--- a/fs/xfs/xfs_error.h
+++ b/fs/xfs/xfs_error.h
@@ -34,6 +34,7 @@ extern void xfs_error_report(const char *tag, int level, struct xfs_mount *mp,
extern void xfs_corruption_error(const char *tag, int level,
struct xfs_mount *mp, void *p, const char *filename,
int linenum, inst_t *ra);
+extern void xfs_verifier_error(struct xfs_buf *bp);
#define XFS_ERROR_REPORT(e, lvl, mp) \
xfs_error_report(e, lvl, mp, __FILE__, __LINE__, __return_address)
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 64b48eade91d..f7abff8c16ca 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -823,7 +823,8 @@ xfs_file_fallocate(
if (!S_ISREG(inode->i_mode))
return -EINVAL;
- if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+ if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
+ FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE))
return -EOPNOTSUPP;
xfs_ilock(ip, XFS_IOLOCK_EXCL);
@@ -831,6 +832,20 @@ xfs_file_fallocate(
error = xfs_free_file_space(ip, offset, len);
if (error)
goto out_unlock;
+ } else if (mode & FALLOC_FL_COLLAPSE_RANGE) {
+ unsigned blksize_mask = (1 << inode->i_blkbits) - 1;
+
+ if (offset & blksize_mask || len & blksize_mask) {
+ error = -EINVAL;
+ goto out_unlock;
+ }
+
+ ASSERT(offset + len < i_size_read(inode));
+ new_size = i_size_read(inode) - len;
+
+ error = xfs_collapse_file_space(ip, offset, len);
+ if (error)
+ goto out_unlock;
} else {
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
offset + len > i_size_read(inode)) {
@@ -840,8 +855,11 @@ xfs_file_fallocate(
goto out_unlock;
}
- error = xfs_alloc_file_space(ip, offset, len,
- XFS_BMAPI_PREALLOC);
+ if (mode & FALLOC_FL_ZERO_RANGE)
+ error = xfs_zero_file_space(ip, offset, len);
+ else
+ error = xfs_alloc_file_space(ip, offset, len,
+ XFS_BMAPI_PREALLOC);
if (error)
goto out_unlock;
}
@@ -859,7 +877,7 @@ xfs_file_fallocate(
if (ip->i_d.di_mode & S_IXGRP)
ip->i_d.di_mode &= ~S_ISGID;
- if (!(mode & FALLOC_FL_PUNCH_HOLE))
+ if (!(mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE)))
ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
diff --git a/fs/xfs/xfs_format.h b/fs/xfs/xfs_format.h
index b6ab5a3cfa12..9898f31d05d8 100644
--- a/fs/xfs/xfs_format.h
+++ b/fs/xfs/xfs_format.h
@@ -145,6 +145,8 @@ struct xfs_dsymlink_hdr {
__be64 sl_lsn;
};
+#define XFS_SYMLINK_CRC_OFF offsetof(struct xfs_dsymlink_hdr, sl_crc)
+
/*
* The maximum pathlen is 1024 bytes. Since the minimum file system
* blocksize is 512 bytes, we can get a max of 3 extents back from
diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c
index 5d7f105a1c82..8f711db61a0c 100644
--- a/fs/xfs/xfs_ialloc.c
+++ b/fs/xfs/xfs_ialloc.c
@@ -363,6 +363,18 @@ xfs_ialloc_ag_alloc(
args.minleft = args.mp->m_in_maxlevels - 1;
if ((error = xfs_alloc_vextent(&args)))
return error;
+
+ /*
+ * This request might have dirtied the transaction if the AG can
+ * satisfy the request, but the exact block was not available.
+ * If the allocation did fail, subsequent requests will relax
+ * the exact agbno requirement and increase the alignment
+ * instead. It is critical that the total size of the request
+ * (len + alignment + slop) does not increase from this point
+ * on, so reset minalignslop to ensure it is not included in
+ * subsequent requests.
+ */
+ args.minalignslop = 0;
} else
args.fsbno = NULLFSBLOCK;
@@ -1568,18 +1580,17 @@ xfs_agi_read_verify(
struct xfs_buf *bp)
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- int agi_ok = 1;
-
- if (xfs_sb_version_hascrc(&mp->m_sb))
- agi_ok = xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- offsetof(struct xfs_agi, agi_crc));
- agi_ok = agi_ok && xfs_agi_verify(bp);
- if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IALLOC_READ_AGI,
- XFS_RANDOM_IALLOC_READ_AGI))) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+ if (xfs_sb_version_hascrc(&mp->m_sb) &&
+ !xfs_buf_verify_cksum(bp, XFS_AGI_CRC_OFF))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (XFS_TEST_ERROR(!xfs_agi_verify(bp), mp,
+ XFS_ERRTAG_IALLOC_READ_AGI,
+ XFS_RANDOM_IALLOC_READ_AGI))
xfs_buf_ioerror(bp, EFSCORRUPTED);
- }
+
+ if (bp->b_error)
+ xfs_verifier_error(bp);
}
static void
@@ -1590,8 +1601,8 @@ xfs_agi_write_verify(
struct xfs_buf_log_item *bip = bp->b_fspriv;
if (!xfs_agi_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
@@ -1600,8 +1611,7 @@ xfs_agi_write_verify(
if (bip)
XFS_BUF_TO_AGI(bp)->agi_lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
- offsetof(struct xfs_agi, agi_crc));
+ xfs_buf_update_cksum(bp, XFS_AGI_CRC_OFF);
}
const struct xfs_buf_ops xfs_agi_buf_ops = {
diff --git a/fs/xfs/xfs_ialloc_btree.c b/fs/xfs/xfs_ialloc_btree.c
index c8fa5bbb36de..7e309b11e87d 100644
--- a/fs/xfs/xfs_ialloc_btree.c
+++ b/fs/xfs/xfs_ialloc_btree.c
@@ -243,12 +243,14 @@ static void
xfs_inobt_read_verify(
struct xfs_buf *bp)
{
- if (!(xfs_btree_sblock_verify_crc(bp) &&
- xfs_inobt_verify(bp))) {
- trace_xfs_btree_corrupt(bp, _RET_IP_);
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
- bp->b_target->bt_mount, bp->b_addr);
+ if (!xfs_btree_sblock_verify_crc(bp))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (!xfs_inobt_verify(bp))
xfs_buf_ioerror(bp, EFSCORRUPTED);
+
+ if (bp->b_error) {
+ trace_xfs_btree_corrupt(bp, _RET_IP_);
+ xfs_verifier_error(bp);
}
}
@@ -258,9 +260,9 @@ xfs_inobt_write_verify(
{
if (!xfs_inobt_verify(bp)) {
trace_xfs_btree_corrupt(bp, _RET_IP_);
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
- bp->b_target->bt_mount, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
+ return;
}
xfs_btree_sblock_calc_crc(bp);
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 3a137e9f9a7d..5e7a38fa6ee6 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -42,7 +42,6 @@
#include "xfs_bmap_util.h"
#include "xfs_error.h"
#include "xfs_quota.h"
-#include "xfs_dinode.h"
#include "xfs_filestream.h"
#include "xfs_cksum.h"
#include "xfs_trace.h"
@@ -62,6 +61,8 @@ kmem_zone_t *xfs_inode_zone;
STATIC int xfs_iflush_int(xfs_inode_t *, xfs_buf_t *);
+STATIC int xfs_iunlink_remove(xfs_trans_t *, xfs_inode_t *);
+
/*
* helper function to extract extent size hint from inode
*/
@@ -1115,7 +1116,7 @@ xfs_bumplink(
{
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
- ASSERT(ip->i_d.di_nlink > 0);
+ ASSERT(ip->i_d.di_nlink > 0 || (VFS_I(ip)->i_state & I_LINKABLE));
ip->i_d.di_nlink++;
inc_nlink(VFS_I(ip));
if ((ip->i_d.di_version == 1) &&
@@ -1165,10 +1166,7 @@ xfs_create(
if (XFS_FORCED_SHUTDOWN(mp))
return XFS_ERROR(EIO);
- if (dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)
- prid = xfs_get_projid(dp);
- else
- prid = XFS_PROJID_DEFAULT;
+ prid = xfs_get_initial_prid(dp);
/*
* Make sure that we have allocated dquot(s) on disk.
@@ -1333,6 +1331,113 @@ xfs_create(
}
int
+xfs_create_tmpfile(
+ struct xfs_inode *dp,
+ struct dentry *dentry,
+ umode_t mode)
+{
+ struct xfs_mount *mp = dp->i_mount;
+ struct xfs_inode *ip = NULL;
+ struct xfs_trans *tp = NULL;
+ int error;
+ uint cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
+ prid_t prid;
+ struct xfs_dquot *udqp = NULL;
+ struct xfs_dquot *gdqp = NULL;
+ struct xfs_dquot *pdqp = NULL;
+ struct xfs_trans_res *tres;
+ uint resblks;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ prid = xfs_get_initial_prid(dp);
+
+ /*
+ * Make sure that we have allocated dquot(s) on disk.
+ */
+ error = xfs_qm_vop_dqalloc(dp, xfs_kuid_to_uid(current_fsuid()),
+ xfs_kgid_to_gid(current_fsgid()), prid,
+ XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT,
+ &udqp, &gdqp, &pdqp);
+ if (error)
+ return error;
+
+ resblks = XFS_IALLOC_SPACE_RES(mp);
+ tp = xfs_trans_alloc(mp, XFS_TRANS_CREATE_TMPFILE);
+
+ tres = &M_RES(mp)->tr_create_tmpfile;
+ error = xfs_trans_reserve(tp, tres, resblks, 0);
+ if (error == ENOSPC) {
+ /* No space at all so try a "no-allocation" reservation */
+ resblks = 0;
+ error = xfs_trans_reserve(tp, tres, 0, 0);
+ }
+ if (error) {
+ cancel_flags = 0;
+ goto out_trans_cancel;
+ }
+
+ error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp,
+ pdqp, resblks, 1, 0);
+ if (error)
+ goto out_trans_cancel;
+
+ error = xfs_dir_ialloc(&tp, dp, mode, 1, 0,
+ prid, resblks > 0, &ip, NULL);
+ if (error) {
+ if (error == ENOSPC)
+ goto out_trans_cancel;
+ goto out_trans_abort;
+ }
+
+ if (mp->m_flags & XFS_MOUNT_WSYNC)
+ xfs_trans_set_sync(tp);
+
+ /*
+ * Attach the dquot(s) to the inodes and modify them incore.
+ * These ids of the inode couldn't have changed since the new
+ * inode has been locked ever since it was created.
+ */
+ xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);
+
+ ip->i_d.di_nlink--;
+ d_tmpfile(dentry, VFS_I(ip));
+ error = xfs_iunlink(tp, ip);
+ if (error)
+ goto out_trans_abort;
+
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
+ if (error)
+ goto out_release_inode;
+
+ xfs_qm_dqrele(udqp);
+ xfs_qm_dqrele(gdqp);
+ xfs_qm_dqrele(pdqp);
+
+ return 0;
+
+ out_trans_abort:
+ cancel_flags |= XFS_TRANS_ABORT;
+ out_trans_cancel:
+ xfs_trans_cancel(tp, cancel_flags);
+ out_release_inode:
+ /*
+ * Wait until after the current transaction is aborted to
+ * release the inode. This prevents recursive transactions
+ * and deadlocks from xfs_inactive.
+ */
+ if (ip)
+ IRELE(ip);
+
+ xfs_qm_dqrele(udqp);
+ xfs_qm_dqrele(gdqp);
+ xfs_qm_dqrele(pdqp);
+
+ return error;
+}
+
+int
xfs_link(
xfs_inode_t *tdp,
xfs_inode_t *sip,
@@ -1397,6 +1502,12 @@ xfs_link(
xfs_bmap_init(&free_list, &first_block);
+ if (sip->i_d.di_nlink == 0) {
+ error = xfs_iunlink_remove(tp, sip);
+ if (error)
+ goto abort_return;
+ }
+
error = xfs_dir_createname(tp, tdp, target_name, sip->i_ino,
&first_block, &free_list, resblks);
if (error)
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index 65e2350f449c..396cc1fafd0d 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -20,6 +20,7 @@
#include "xfs_inode_buf.h"
#include "xfs_inode_fork.h"
+#include "xfs_dinode.h"
/*
* Kernel only inode definitions
@@ -192,6 +193,15 @@ xfs_set_projid(struct xfs_inode *ip,
ip->i_d.di_projid_lo = (__uint16_t) (projid & 0xffff);
}
+static inline prid_t
+xfs_get_initial_prid(struct xfs_inode *dp)
+{
+ if (dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)
+ return xfs_get_projid(dp);
+
+ return XFS_PROJID_DEFAULT;
+}
+
/*
* In-core inode flags.
*/
@@ -323,6 +333,8 @@ int xfs_lookup(struct xfs_inode *dp, struct xfs_name *name,
struct xfs_inode **ipp, struct xfs_name *ci_name);
int xfs_create(struct xfs_inode *dp, struct xfs_name *name,
umode_t mode, xfs_dev_t rdev, struct xfs_inode **ipp);
+int xfs_create_tmpfile(struct xfs_inode *dp, struct dentry *dentry,
+ umode_t mode);
int xfs_remove(struct xfs_inode *dp, struct xfs_name *name,
struct xfs_inode *ip);
int xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip,
diff --git a/fs/xfs/xfs_inode_buf.c b/fs/xfs/xfs_inode_buf.c
index 4fc9f39dd89e..24e993996bdc 100644
--- a/fs/xfs/xfs_inode_buf.c
+++ b/fs/xfs/xfs_inode_buf.c
@@ -102,8 +102,7 @@ xfs_inode_buf_verify(
}
xfs_buf_ioerror(bp, EFSCORRUPTED);
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_HIGH,
- mp, dip);
+ xfs_verifier_error(bp);
#ifdef DEBUG
xfs_alert(mp,
"bad inode magic/vsn daddr %lld #%d (magic=%x)",
@@ -306,7 +305,7 @@ xfs_dinode_verify(
if (!xfs_sb_version_hascrc(&mp->m_sb))
return false;
if (!xfs_verify_cksum((char *)dip, mp->m_sb.sb_inodesize,
- offsetof(struct xfs_dinode, di_crc)))
+ XFS_DINODE_CRC_OFF))
return false;
if (be64_to_cpu(dip->di_ino) != ip->i_ino)
return false;
@@ -327,7 +326,7 @@ xfs_dinode_calc_crc(
ASSERT(xfs_sb_version_hascrc(&mp->m_sb));
crc = xfs_start_cksum((char *)dip, mp->m_sb.sb_inodesize,
- offsetof(struct xfs_dinode, di_crc));
+ XFS_DINODE_CRC_OFF);
dip->di_crc = xfs_end_cksum(crc);
}
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 22d1cbea283d..3b80ebae05f5 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -128,7 +128,6 @@ xfs_iomap_write_direct(
xfs_fsblock_t firstfsb;
xfs_extlen_t extsz, temp;
int nimaps;
- int bmapi_flag;
int quota_flag;
int rt;
xfs_trans_t *tp;
@@ -200,18 +199,15 @@ xfs_iomap_write_direct(
xfs_trans_ijoin(tp, ip, 0);
- bmapi_flag = 0;
- if (offset < XFS_ISIZE(ip) || extsz)
- bmapi_flag |= XFS_BMAPI_PREALLOC;
-
/*
* From this point onwards we overwrite the imap pointer that the
* caller gave to us.
*/
xfs_bmap_init(&free_list, &firstfsb);
nimaps = 1;
- error = xfs_bmapi_write(tp, ip, offset_fsb, count_fsb, bmapi_flag,
- &firstfsb, 0, imap, &nimaps, &free_list);
+ error = xfs_bmapi_write(tp, ip, offset_fsb, count_fsb,
+ XFS_BMAPI_PREALLOC, &firstfsb, 0,
+ imap, &nimaps, &free_list);
if (error)
goto out_bmap_cancel;
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 9ddfb8190ca1..89b07e43ca28 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -39,6 +39,7 @@
#include "xfs_da_btree.h"
#include "xfs_dir2_priv.h"
#include "xfs_dinode.h"
+#include "xfs_trans_space.h"
#include <linux/capability.h>
#include <linux/xattr.h>
@@ -48,6 +49,18 @@
#include <linux/fiemap.h>
#include <linux/slab.h>
+/*
+ * Directories have different lock order w.r.t. mmap_sem compared to regular
+ * files. This is due to readdir potentially triggering page faults on a user
+ * buffer inside filldir(), and this happens with the ilock on the directory
+ * held. For regular files, the lock order is the other way around - the
+ * mmap_sem is taken during the page fault, and then we lock the ilock to do
+ * block mapping. Hence we need a different class for the directory ilock so
+ * that lockdep can tell them apart.
+ */
+static struct lock_class_key xfs_nondir_ilock_class;
+static struct lock_class_key xfs_dir_ilock_class;
+
static int
xfs_initxattrs(
struct inode *inode,
@@ -1034,6 +1047,19 @@ xfs_vn_fiemap(
return 0;
}
+STATIC int
+xfs_vn_tmpfile(
+ struct inode *dir,
+ struct dentry *dentry,
+ umode_t mode)
+{
+ int error;
+
+ error = xfs_create_tmpfile(XFS_I(dir), dentry, mode);
+
+ return -error;
+}
+
static const struct inode_operations xfs_inode_operations = {
.get_acl = xfs_get_acl,
.set_acl = xfs_set_acl,
@@ -1072,6 +1098,7 @@ static const struct inode_operations xfs_dir_inode_operations = {
.removexattr = generic_removexattr,
.listxattr = xfs_vn_listxattr,
.update_time = xfs_vn_update_time,
+ .tmpfile = xfs_vn_tmpfile,
};
static const struct inode_operations xfs_dir_ci_inode_operations = {
@@ -1099,6 +1126,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = {
.removexattr = generic_removexattr,
.listxattr = xfs_vn_listxattr,
.update_time = xfs_vn_update_time,
+ .tmpfile = xfs_vn_tmpfile,
};
static const struct inode_operations xfs_symlink_inode_operations = {
@@ -1191,6 +1219,7 @@ xfs_setup_inode(
xfs_diflags_to_iflags(inode, ip);
ip->d_ops = ip->i_mount->m_nondir_inode_ops;
+ lockdep_set_class(&ip->i_lock.mr_lock, &xfs_nondir_ilock_class);
switch (inode->i_mode & S_IFMT) {
case S_IFREG:
inode->i_op = &xfs_inode_operations;
@@ -1198,6 +1227,7 @@ xfs_setup_inode(
inode->i_mapping->a_ops = &xfs_address_space_operations;
break;
case S_IFDIR:
+ lockdep_set_class(&ip->i_lock.mr_lock, &xfs_dir_ilock_class);
if (xfs_sb_version_hasasciici(&XFS_M(inode->i_sb)->m_sb))
inode->i_op = &xfs_dir_ci_inode_operations;
else
diff --git a/fs/xfs/xfs_linux.h b/fs/xfs/xfs_linux.h
index f9bb590acc0e..825249d2dfc1 100644
--- a/fs/xfs/xfs_linux.h
+++ b/fs/xfs/xfs_linux.h
@@ -119,6 +119,7 @@ typedef __uint64_t __psunsigned_t;
#include "xfs_iops.h"
#include "xfs_aops.h"
#include "xfs_super.h"
+#include "xfs_cksum.h"
#include "xfs_buf.h"
#include "xfs_message.h"
@@ -178,6 +179,7 @@ typedef __uint64_t __psunsigned_t;
#define ENOATTR ENODATA /* Attribute not found */
#define EWRONGFS EINVAL /* Mount with wrong filesystem type */
#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
+#define EFSBADCRC EBADMSG /* Bad CRC detected */
#define SYNCHRONIZE() barrier()
#define __return_address __builtin_return_address(0)
diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h
index b0f4ef77fa70..2c4004475e71 100644
--- a/fs/xfs/xfs_log.h
+++ b/fs/xfs/xfs_log.h
@@ -175,7 +175,7 @@ void xlog_iodone(struct xfs_buf *);
struct xlog_ticket *xfs_log_ticket_get(struct xlog_ticket *ticket);
void xfs_log_ticket_put(struct xlog_ticket *ticket);
-int xfs_log_commit_cil(struct xfs_mount *mp, struct xfs_trans *tp,
+void xfs_log_commit_cil(struct xfs_mount *mp, struct xfs_trans *tp,
xfs_lsn_t *commit_lsn, int flags);
bool xfs_log_item_in_current_chkpt(struct xfs_log_item *lip);
diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c
index 4ef6fdbced78..7e5455391176 100644
--- a/fs/xfs/xfs_log_cil.c
+++ b/fs/xfs/xfs_log_cil.c
@@ -499,13 +499,6 @@ xlog_cil_push(
cil->xc_ctx = new_ctx;
/*
- * mirror the new sequence into the cil structure so that we can do
- * unlocked checks against the current sequence in log forces without
- * risking deferencing a freed context pointer.
- */
- cil->xc_current_sequence = new_ctx->sequence;
-
- /*
* The switch is now done, so we can drop the context lock and move out
* of a shared context. We can't just go straight to the commit record,
* though - we need to synchronise with previous and future commits so
@@ -523,8 +516,15 @@ xlog_cil_push(
* Hence we need to add this context to the committing context list so
* that higher sequences will wait for us to write out a commit record
* before they do.
+ *
+ * xfs_log_force_lsn requires us to mirror the new sequence into the cil
+ * structure atomically with the addition of this sequence to the
+ * committing list. This also ensures that we can do unlocked checks
+ * against the current sequence in log forces without risking
+ * deferencing a freed context pointer.
*/
spin_lock(&cil->xc_push_lock);
+ cil->xc_current_sequence = new_ctx->sequence;
list_add(&ctx->committing, &cil->xc_committing);
spin_unlock(&cil->xc_push_lock);
up_write(&cil->xc_ctx_lock);
@@ -662,8 +662,14 @@ xlog_cil_push_background(
}
+/*
+ * xlog_cil_push_now() is used to trigger an immediate CIL push to the sequence
+ * number that is passed. When it returns, the work will be queued for
+ * @push_seq, but it won't be completed. The caller is expected to do any
+ * waiting for push_seq to complete if it is required.
+ */
static void
-xlog_cil_push_foreground(
+xlog_cil_push_now(
struct xlog *log,
xfs_lsn_t push_seq)
{
@@ -688,10 +694,8 @@ xlog_cil_push_foreground(
}
cil->xc_push_seq = push_seq;
+ queue_work(log->l_mp->m_cil_workqueue, &cil->xc_push_work);
spin_unlock(&cil->xc_push_lock);
-
- /* do the push now */
- xlog_cil_push(log);
}
bool
@@ -721,7 +725,7 @@ xlog_cil_empty(
* background commit, returns without it held once background commits are
* allowed again.
*/
-int
+void
xfs_log_commit_cil(
struct xfs_mount *mp,
struct xfs_trans *tp,
@@ -767,7 +771,6 @@ xfs_log_commit_cil(
xlog_cil_push_background(log);
up_read(&cil->xc_ctx_lock);
- return 0;
}
/*
@@ -796,7 +799,8 @@ xlog_cil_force_lsn(
* xlog_cil_push() handles racing pushes for the same sequence,
* so no need to deal with it here.
*/
- xlog_cil_push_foreground(log, sequence);
+restart:
+ xlog_cil_push_now(log, sequence);
/*
* See if we can find a previous sequence still committing.
@@ -804,7 +808,6 @@ xlog_cil_force_lsn(
* before allowing the force of push_seq to go ahead. Hence block
* on commits for those as well.
*/
-restart:
spin_lock(&cil->xc_push_lock);
list_for_each_entry(ctx, &cil->xc_committing, committing) {
if (ctx->sequence > sequence)
@@ -822,6 +825,28 @@ restart:
/* found it! */
commit_lsn = ctx->commit_lsn;
}
+
+ /*
+ * The call to xlog_cil_push_now() executes the push in the background.
+ * Hence by the time we have got here it our sequence may not have been
+ * pushed yet. This is true if the current sequence still matches the
+ * push sequence after the above wait loop and the CIL still contains
+ * dirty objects.
+ *
+ * When the push occurs, it will empty the CIL and
+ * atomically increment the currect sequence past the push sequence and
+ * move it into the committing list. Of course, if the CIL is clean at
+ * the time of the push, it won't have pushed the CIL at all, so in that
+ * case we should try the push for this sequence again from the start
+ * just in case.
+ */
+
+ if (sequence == cil->xc_current_sequence &&
+ !list_empty(&cil->xc_cil)) {
+ spin_unlock(&cil->xc_push_lock);
+ goto restart;
+ }
+
spin_unlock(&cil->xc_push_lock);
return commit_lsn;
}
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index f96c05669a9e..993cb19e7d39 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -314,6 +314,9 @@ reread:
error = bp->b_error;
if (loud)
xfs_warn(mp, "SB validate failed with error %d.", error);
+ /* bad CRC means corrupted metadata */
+ if (error == EFSBADCRC)
+ error = EFSCORRUPTED;
goto release_buf;
}
diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c
index a6a76b2b6a85..ec5ca65c6211 100644
--- a/fs/xfs/xfs_rtalloc.c
+++ b/fs/xfs/xfs_rtalloc.c
@@ -842,7 +842,7 @@ xfs_growfs_rt_alloc(
/*
* Reserve space & log for one extent added to the file.
*/
- error = xfs_trans_reserve(tp, &M_RES(mp)->tr_growdata,
+ error = xfs_trans_reserve(tp, &M_RES(mp)->tr_growrtalloc,
resblks, 0);
if (error)
goto error_cancel;
diff --git a/fs/xfs/xfs_sb.c b/fs/xfs/xfs_sb.c
index 1e116794bb66..0c0e41bbe4e3 100644
--- a/fs/xfs/xfs_sb.c
+++ b/fs/xfs/xfs_sb.c
@@ -288,6 +288,7 @@ xfs_mount_validate_sb(
sbp->sb_inodelog < XFS_DINODE_MIN_LOG ||
sbp->sb_inodelog > XFS_DINODE_MAX_LOG ||
sbp->sb_inodesize != (1 << sbp->sb_inodelog) ||
+ sbp->sb_inopblock != howmany(sbp->sb_blocksize,sbp->sb_inodesize) ||
(sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog) ||
(sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE) ||
(sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE) ||
@@ -610,12 +611,11 @@ xfs_sb_read_verify(
XFS_SB_VERSION_5) ||
dsb->sb_crc != 0)) {
- if (!xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- offsetof(struct xfs_sb, sb_crc))) {
+ if (!xfs_buf_verify_cksum(bp, XFS_SB_CRC_OFF)) {
/* Only fail bad secondaries on a known V5 filesystem */
if (bp->b_bn == XFS_SB_DADDR ||
xfs_sb_version_hascrc(&mp->m_sb)) {
- error = EFSCORRUPTED;
+ error = EFSBADCRC;
goto out_error;
}
}
@@ -624,10 +624,9 @@ xfs_sb_read_verify(
out_error:
if (error) {
- if (error == EFSCORRUPTED)
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
- mp, bp->b_addr);
xfs_buf_ioerror(bp, error);
+ if (error == EFSCORRUPTED || error == EFSBADCRC)
+ xfs_verifier_error(bp);
}
}
@@ -662,9 +661,8 @@ xfs_sb_write_verify(
error = xfs_sb_verify(bp, false);
if (error) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
- mp, bp->b_addr);
xfs_buf_ioerror(bp, error);
+ xfs_verifier_error(bp);
return;
}
@@ -674,8 +672,7 @@ xfs_sb_write_verify(
if (bip)
XFS_BUF_TO_SBP(bp)->sb_lsn = cpu_to_be64(bip->bli_item.li_lsn);
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
- offsetof(struct xfs_sb, sb_crc));
+ xfs_buf_update_cksum(bp, XFS_SB_CRC_OFF);
}
const struct xfs_buf_ops xfs_sb_buf_ops = {
diff --git a/fs/xfs/xfs_sb.h b/fs/xfs/xfs_sb.h
index 35061d4b614c..f7b2fe77c5a5 100644
--- a/fs/xfs/xfs_sb.h
+++ b/fs/xfs/xfs_sb.h
@@ -182,6 +182,8 @@ typedef struct xfs_sb {
/* must be padded to 64 bit alignment */
} xfs_sb_t;
+#define XFS_SB_CRC_OFF offsetof(struct xfs_sb, sb_crc)
+
/*
* Superblock - on disk version. Must match the in core version above.
* Must be padded to 64 bit alignment.
diff --git a/fs/xfs/xfs_shared.h b/fs/xfs/xfs_shared.h
index 8c5035a13df1..4484e5151395 100644
--- a/fs/xfs/xfs_shared.h
+++ b/fs/xfs/xfs_shared.h
@@ -104,7 +104,8 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
#define XFS_TRANS_SB_COUNT 41
#define XFS_TRANS_CHECKPOINT 42
#define XFS_TRANS_ICREATE 43
-#define XFS_TRANS_TYPE_MAX 43
+#define XFS_TRANS_CREATE_TMPFILE 44
+#define XFS_TRANS_TYPE_MAX 44
/* new transaction types need to be reflected in xfs_logprint(8) */
#define XFS_TRANS_TYPES \
@@ -112,6 +113,7 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
{ XFS_TRANS_SETATTR_SIZE, "SETATTR_SIZE" }, \
{ XFS_TRANS_INACTIVE, "INACTIVE" }, \
{ XFS_TRANS_CREATE, "CREATE" }, \
+ { XFS_TRANS_CREATE_TMPFILE, "CREATE_TMPFILE" }, \
{ XFS_TRANS_CREATE_TRUNC, "CREATE_TRUNC" }, \
{ XFS_TRANS_TRUNCATE_FILE, "TRUNCATE_FILE" }, \
{ XFS_TRANS_REMOVE, "REMOVE" }, \
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index d971f4932b5d..205376776377 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -996,7 +996,7 @@ xfs_fs_evict_inode(
trace_xfs_evict_inode(ip);
- truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
XFS_STATS_INC(vn_rele);
XFS_STATS_INC(vn_remove);
@@ -1197,6 +1197,7 @@ xfs_fs_remount(
char *p;
int error;
+ sync_filesystem(sb);
while ((p = strsep(&options, ",")) != NULL) {
int token;
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
index 14e58f2c96bd..52979aa90986 100644
--- a/fs/xfs/xfs_symlink.c
+++ b/fs/xfs/xfs_symlink.c
@@ -80,6 +80,10 @@ xfs_readlink_bmap(
if (error) {
xfs_buf_ioerror_alert(bp, __func__);
xfs_buf_relse(bp);
+
+ /* bad CRC means corrupted metadata */
+ if (error == EFSBADCRC)
+ error = EFSCORRUPTED;
goto out;
}
byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt);
@@ -208,10 +212,7 @@ xfs_symlink(
return XFS_ERROR(ENAMETOOLONG);
udqp = gdqp = NULL;
- if (dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)
- prid = xfs_get_projid(dp);
- else
- prid = XFS_PROJID_DEFAULT;
+ prid = xfs_get_initial_prid(dp);
/*
* Make sure that we have allocated dquot(s) on disk.
diff --git a/fs/xfs/xfs_symlink_remote.c b/fs/xfs/xfs_symlink_remote.c
index bf59a2b45f8c..9b32052ff65e 100644
--- a/fs/xfs/xfs_symlink_remote.c
+++ b/fs/xfs/xfs_symlink_remote.c
@@ -133,12 +133,13 @@ xfs_symlink_read_verify(
if (!xfs_sb_version_hascrc(&mp->m_sb))
return;
- if (!xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
- offsetof(struct xfs_dsymlink_hdr, sl_crc)) ||
- !xfs_symlink_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+ if (!xfs_buf_verify_cksum(bp, XFS_SYMLINK_CRC_OFF))
+ xfs_buf_ioerror(bp, EFSBADCRC);
+ else if (!xfs_symlink_verify(bp))
xfs_buf_ioerror(bp, EFSCORRUPTED);
- }
+
+ if (bp->b_error)
+ xfs_verifier_error(bp);
}
static void
@@ -153,8 +154,8 @@ xfs_symlink_write_verify(
return;
if (!xfs_symlink_verify(bp)) {
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
+ xfs_verifier_error(bp);
return;
}
@@ -162,8 +163,7 @@ xfs_symlink_write_verify(
struct xfs_dsymlink_hdr *dsl = bp->b_addr;
dsl->sl_lsn = cpu_to_be64(bip->bli_item.li_lsn);
}
- xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
- offsetof(struct xfs_dsymlink_hdr, sl_crc));
+ xfs_buf_update_cksum(bp, XFS_SYMLINK_CRC_OFF);
}
const struct xfs_buf_ops xfs_symlink_buf_ops = {
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 425dfa45b9a0..a4ae41c179a8 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -603,6 +603,7 @@ DEFINE_INODE_EVENT(xfs_readlink);
DEFINE_INODE_EVENT(xfs_inactive_symlink);
DEFINE_INODE_EVENT(xfs_alloc_file_space);
DEFINE_INODE_EVENT(xfs_free_file_space);
+DEFINE_INODE_EVENT(xfs_collapse_file_space);
DEFINE_INODE_EVENT(xfs_readdir);
#ifdef CONFIG_XFS_POSIX_ACL
DEFINE_INODE_EVENT(xfs_get_acl);
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index c812c5c060de..54a57326d85b 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -887,12 +887,7 @@ xfs_trans_commit(
xfs_trans_apply_sb_deltas(tp);
xfs_trans_apply_dquot_deltas(tp);
- error = xfs_log_commit_cil(mp, tp, &commit_lsn, flags);
- if (error == ENOMEM) {
- xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
- error = XFS_ERROR(EIO);
- goto out_unreserve;
- }
+ xfs_log_commit_cil(mp, tp, &commit_lsn, flags);
current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
xfs_trans_free(tp);
@@ -902,10 +897,7 @@ xfs_trans_commit(
* log out now and wait for it.
*/
if (sync) {
- if (!error) {
- error = _xfs_log_force_lsn(mp, commit_lsn,
- XFS_LOG_SYNC, NULL);
- }
+ error = _xfs_log_force_lsn(mp, commit_lsn, XFS_LOG_SYNC, NULL);
XFS_STATS_INC(xs_trans_sync);
} else {
XFS_STATS_INC(xs_trans_async);
diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c
index 647b6f1d8923..b8eef0549f3f 100644
--- a/fs/xfs/xfs_trans_buf.c
+++ b/fs/xfs/xfs_trans_buf.c
@@ -275,6 +275,10 @@ xfs_trans_read_buf_map(
XFS_BUF_UNDONE(bp);
xfs_buf_stale(bp);
xfs_buf_relse(bp);
+
+ /* bad CRC means corrupted metadata */
+ if (error == EFSBADCRC)
+ error = EFSCORRUPTED;
return error;
}
#ifdef DEBUG
@@ -338,6 +342,9 @@ xfs_trans_read_buf_map(
if (tp->t_flags & XFS_TRANS_DIRTY)
xfs_force_shutdown(tp->t_mountp,
SHUTDOWN_META_IO_ERROR);
+ /* bad CRC means corrupted metadata */
+ if (error == EFSBADCRC)
+ error = EFSCORRUPTED;
return error;
}
}
@@ -375,6 +382,10 @@ xfs_trans_read_buf_map(
if (tp->t_flags & XFS_TRANS_DIRTY)
xfs_force_shutdown(tp->t_mountp, SHUTDOWN_META_IO_ERROR);
xfs_buf_relse(bp);
+
+ /* bad CRC means corrupted metadata */
+ if (error == EFSBADCRC)
+ error = EFSCORRUPTED;
return error;
}
#ifdef DEBUG
diff --git a/fs/xfs/xfs_trans_resv.c b/fs/xfs/xfs_trans_resv.c
index 2ffd3e331b49..ae368165244d 100644
--- a/fs/xfs/xfs_trans_resv.c
+++ b/fs/xfs/xfs_trans_resv.c
@@ -81,20 +81,28 @@ xfs_calc_buf_res(
* on disk. Hence we need an inode reservation function that calculates all this
* correctly. So, we log:
*
- * - log op headers for object
+ * - 4 log op headers for object
+ * - for the ilf, the inode core and 2 forks
* - inode log format object
- * - the entire inode contents (core + 2 forks)
- * - two bmap btree block headers
+ * - the inode core
+ * - two inode forks containing bmap btree root blocks.
+ * - the btree data contained by both forks will fit into the inode size,
+ * hence when combined with the inode core above, we have a total of the
+ * actual inode size.
+ * - the BMBT headers need to be accounted separately, as they are
+ * additional to the records and pointers that fit inside the inode
+ * forks.
*/
STATIC uint
xfs_calc_inode_res(
struct xfs_mount *mp,
uint ninodes)
{
- return ninodes * (sizeof(struct xlog_op_header) +
- sizeof(struct xfs_inode_log_format) +
- mp->m_sb.sb_inodesize +
- 2 * XFS_BMBT_BLOCK_LEN(mp));
+ return ninodes *
+ (4 * sizeof(struct xlog_op_header) +
+ sizeof(struct xfs_inode_log_format) +
+ mp->m_sb.sb_inodesize +
+ 2 * XFS_BMBT_BLOCK_LEN(mp));
}
/*
@@ -204,6 +212,19 @@ xfs_calc_rename_reservation(
}
/*
+ * For removing an inode from unlinked list at first, we can modify:
+ * the agi hash list and counters: sector size
+ * the on disk inode before ours in the agi hash list: inode cluster size
+ */
+STATIC uint
+xfs_calc_iunlink_remove_reservation(
+ struct xfs_mount *mp)
+{
+ return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
+ max_t(uint, XFS_FSB_TO_B(mp, 1), mp->m_inode_cluster_size);
+}
+
+/*
* For creating a link to an inode:
* the parent directory inode: inode size
* the linked inode: inode size
@@ -220,6 +241,7 @@ xfs_calc_link_reservation(
struct xfs_mount *mp)
{
return XFS_DQUOT_LOGRES(mp) +
+ xfs_calc_iunlink_remove_reservation(mp) +
MAX((xfs_calc_inode_res(mp, 2) +
xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
XFS_FSB_TO_B(mp, 1))),
@@ -229,6 +251,18 @@ xfs_calc_link_reservation(
}
/*
+ * For adding an inode to unlinked list we can modify:
+ * the agi hash list: sector size
+ * the unlinked inode: inode size
+ */
+STATIC uint
+xfs_calc_iunlink_add_reservation(xfs_mount_t *mp)
+{
+ return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
+ xfs_calc_inode_res(mp, 1);
+}
+
+/*
* For removing a directory entry we can modify:
* the parent directory inode: inode size
* the removed inode: inode size
@@ -245,10 +279,11 @@ xfs_calc_remove_reservation(
struct xfs_mount *mp)
{
return XFS_DQUOT_LOGRES(mp) +
- MAX((xfs_calc_inode_res(mp, 2) +
+ xfs_calc_iunlink_add_reservation(mp) +
+ MAX((xfs_calc_inode_res(mp, 1) +
xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
XFS_FSB_TO_B(mp, 1))),
- (xfs_calc_buf_res(5, mp->m_sb.sb_sectsize) +
+ (xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) +
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 2),
XFS_FSB_TO_B(mp, 1))));
}
@@ -343,6 +378,20 @@ xfs_calc_create_reservation(
}
+STATIC uint
+xfs_calc_create_tmpfile_reservation(
+ struct xfs_mount *mp)
+{
+ uint res = XFS_DQUOT_LOGRES(mp);
+
+ if (xfs_sb_version_hascrc(&mp->m_sb))
+ res += xfs_calc_icreate_resv_alloc(mp);
+ else
+ res += xfs_calc_create_resv_alloc(mp);
+
+ return res + xfs_calc_iunlink_add_reservation(mp);
+}
+
/*
* Making a new directory is the same as creating a new file.
*/
@@ -383,9 +432,9 @@ xfs_calc_ifree_reservation(
{
return XFS_DQUOT_LOGRES(mp) +
xfs_calc_inode_res(mp, 1) +
- xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +
+ xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
xfs_calc_buf_res(1, XFS_FSB_TO_B(mp, 1)) +
- max_t(uint, XFS_FSB_TO_B(mp, 1), mp->m_inode_cluster_size) +
+ xfs_calc_iunlink_remove_reservation(mp) +
xfs_calc_buf_res(1, 0) +
xfs_calc_buf_res(2 + mp->m_ialloc_blks +
mp->m_in_maxlevels, 0) +
@@ -644,15 +693,14 @@ xfs_calc_qm_setqlim_reservation(
/*
* Allocating quota on disk if needed.
- * the write transaction log space: M_RES(mp)->tr_write.tr_logres
+ * the write transaction log space for quota file extent allocation
* the unit of quota allocation: one system block size
*/
STATIC uint
xfs_calc_qm_dqalloc_reservation(
struct xfs_mount *mp)
{
- ASSERT(M_RES(mp)->tr_write.tr_logres);
- return M_RES(mp)->tr_write.tr_logres +
+ return xfs_calc_write_reservation(mp) +
xfs_calc_buf_res(1,
XFS_FSB_TO_B(mp, XFS_DQUOT_CLUSTER_SIZE_FSB) - 1);
}
@@ -729,6 +777,11 @@ xfs_trans_resv_calc(
resp->tr_create.tr_logcount = XFS_CREATE_LOG_COUNT;
resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+ resp->tr_create_tmpfile.tr_logres =
+ xfs_calc_create_tmpfile_reservation(mp);
+ resp->tr_create_tmpfile.tr_logcount = XFS_CREATE_TMPFILE_LOG_COUNT;
+ resp->tr_create_tmpfile.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
@@ -784,7 +837,6 @@ xfs_trans_resv_calc(
/* The following transaction are logged in logical format */
resp->tr_ichange.tr_logres = xfs_calc_ichange_reservation(mp);
resp->tr_growdata.tr_logres = xfs_calc_growdata_reservation(mp);
- resp->tr_swrite.tr_logres = xfs_calc_swrite_reservation(mp);
resp->tr_fsyncts.tr_logres = xfs_calc_swrite_reservation(mp);
resp->tr_writeid.tr_logres = xfs_calc_writeid_reservation(mp);
resp->tr_attrsetrt.tr_logres = xfs_calc_attrsetrt_reservation(mp);
diff --git a/fs/xfs/xfs_trans_resv.h b/fs/xfs/xfs_trans_resv.h
index de7de9aaad8a..1097d14cd583 100644
--- a/fs/xfs/xfs_trans_resv.h
+++ b/fs/xfs/xfs_trans_resv.h
@@ -38,11 +38,11 @@ struct xfs_trans_resv {
struct xfs_trans_res tr_remove; /* unlink trans */
struct xfs_trans_res tr_symlink; /* symlink trans */
struct xfs_trans_res tr_create; /* create trans */
+ struct xfs_trans_res tr_create_tmpfile; /* create O_TMPFILE trans */
struct xfs_trans_res tr_mkdir; /* mkdir trans */
struct xfs_trans_res tr_ifree; /* inode free trans */
struct xfs_trans_res tr_ichange; /* inode update trans */
struct xfs_trans_res tr_growdata; /* fs data section grow trans */
- struct xfs_trans_res tr_swrite; /* sync write inode trans */
struct xfs_trans_res tr_addafork; /* add inode attr fork trans */
struct xfs_trans_res tr_writeid; /* write setuid/setgid file */
struct xfs_trans_res tr_attrinval; /* attr fork buffer
@@ -100,6 +100,7 @@ struct xfs_trans_resv {
#define XFS_ITRUNCATE_LOG_COUNT 2
#define XFS_INACTIVE_LOG_COUNT 2
#define XFS_CREATE_LOG_COUNT 2
+#define XFS_CREATE_TMPFILE_LOG_COUNT 2
#define XFS_MKDIR_LOG_COUNT 3
#define XFS_SYMLINK_LOG_COUNT 3
#define XFS_REMOVE_LOG_COUNT 2
diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h
index f3372441e3a5..c8adad9c6b6a 100644
--- a/include/acpi/actbl2.h
+++ b/include/acpi/actbl2.h
@@ -424,7 +424,8 @@ enum acpi_dmar_type {
ACPI_DMAR_TYPE_RESERVED_MEMORY = 1,
ACPI_DMAR_TYPE_ATSR = 2,
ACPI_DMAR_HARDWARE_AFFINITY = 3,
- ACPI_DMAR_TYPE_RESERVED = 4 /* 4 and greater are reserved */
+ ACPI_DMAR_TYPE_ANDD = 4,
+ ACPI_DMAR_TYPE_RESERVED = 5 /* 5 and greater are reserved */
};
/* DMAR Device Scope structure */
@@ -445,7 +446,8 @@ enum acpi_dmar_scope_type {
ACPI_DMAR_SCOPE_TYPE_BRIDGE = 2,
ACPI_DMAR_SCOPE_TYPE_IOAPIC = 3,
ACPI_DMAR_SCOPE_TYPE_HPET = 4,
- ACPI_DMAR_SCOPE_TYPE_RESERVED = 5 /* 5 and greater are reserved */
+ ACPI_DMAR_SCOPE_TYPE_ACPI = 5,
+ ACPI_DMAR_SCOPE_TYPE_RESERVED = 6 /* 6 and greater are reserved */
};
struct acpi_dmar_pci_path {
@@ -507,6 +509,15 @@ struct acpi_dmar_rhsa {
u32 proximity_domain;
};
+/* 4: ACPI Namespace Device Declaration Structure */
+
+struct acpi_dmar_andd {
+ struct acpi_dmar_header header;
+ u8 reserved[3];
+ u8 device_number;
+ u8 object_name[];
+};
+
/*******************************************************************************
*
* HPET - High Precision Event Timer table
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index a5f56a0213a7..23e364538ab5 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -69,7 +69,7 @@ static inline int gpio_direction_input(unsigned gpio)
}
static inline int gpio_direction_output(unsigned gpio, int value)
{
- return gpiod_direction_output(gpio_to_desc(gpio), value);
+ return gpiod_direction_output_raw(gpio_to_desc(gpio), value);
}
static inline int gpio_set_debounce(unsigned gpio, unsigned debounce)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index bc2121fa9132..146e4fffd710 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -167,6 +167,25 @@
#define CLK_OF_TABLES()
#endif
+#ifdef CONFIG_OF_RESERVED_MEM
+#define RESERVEDMEM_OF_TABLES() \
+ . = ALIGN(8); \
+ VMLINUX_SYMBOL(__reservedmem_of_table) = .; \
+ *(__reservedmem_of_table) \
+ *(__reservedmem_of_table_end)
+#else
+#define RESERVEDMEM_OF_TABLES()
+#endif
+
+#ifdef CONFIG_SMP
+#define CPU_METHOD_OF_TABLES() . = ALIGN(8); \
+ VMLINUX_SYMBOL(__cpu_method_of_table_begin) = .; \
+ *(__cpu_method_of_table) \
+ VMLINUX_SYMBOL(__cpu_method_of_table_end) = .;
+#else
+#define CPU_METHOD_OF_TABLES()
+#endif
+
#define KERNEL_DTB() \
STRUCT_ALIGN(); \
VMLINUX_SYMBOL(__dtb_start) = .; \
@@ -490,7 +509,9 @@
TRACE_SYSCALLS() \
MEM_DISCARD(init.rodata) \
CLK_OF_TABLES() \
+ RESERVEDMEM_OF_TABLES() \
CLKSRC_OF_TABLES() \
+ CPU_METHOD_OF_TABLES() \
KERNEL_DTB() \
IRQCHIP_OF_MATCH_TABLE()
diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h
index e73c19e90e38..016c2f110f63 100644
--- a/include/crypto/algapi.h
+++ b/include/crypto/algapi.h
@@ -100,9 +100,12 @@ struct blkcipher_walk {
void *page;
u8 *buffer;
u8 *iv;
+ unsigned int ivsize;
int flags;
- unsigned int blocksize;
+ unsigned int walk_blocksize;
+ unsigned int cipher_blocksize;
+ unsigned int alignmask;
};
struct ablkcipher_walk {
@@ -192,6 +195,10 @@ int blkcipher_walk_phys(struct blkcipher_desc *desc,
int blkcipher_walk_virt_block(struct blkcipher_desc *desc,
struct blkcipher_walk *walk,
unsigned int blocksize);
+int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc,
+ struct blkcipher_walk *walk,
+ struct crypto_aead *tfm,
+ unsigned int blocksize);
int ablkcipher_walk_done(struct ablkcipher_request *req,
struct ablkcipher_walk *walk, int err);
diff --git a/include/crypto/null.h b/include/crypto/null.h
new file mode 100644
index 000000000000..b7c864cc70df
--- /dev/null
+++ b/include/crypto/null.h
@@ -0,0 +1,11 @@
+/* Values for NULL algorithms */
+
+#ifndef _CRYPTO_NULL_H
+#define _CRYPTO_NULL_H
+
+#define NULL_KEY_SIZE 0
+#define NULL_BLOCK_SIZE 1
+#define NULL_DIGEST_SIZE 0
+#define NULL_IV_SIZE 0
+
+#endif
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 471f276ce8f7..0145b948b147 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -55,7 +55,7 @@ struct drm_fb_helper_surface_size {
* save the current lut when force-restoring the fbdev for e.g.
* kdbg.
* @fb_probe: Driver callback to allocate and initialize the fbdev info
- * structure. Futhermore it also needs to allocate the drm
+ * structure. Furthermore it also needs to allocate the drm
* framebuffer used to back the fbdev.
* @initial_config: Setup an initial fbdev display configuration
*
diff --git a/include/dt-bindings/clock/bcm281xx.h b/include/dt-bindings/clock/bcm281xx.h
new file mode 100644
index 000000000000..e0096940886d
--- /dev/null
+++ b/include/dt-bindings/clock/bcm281xx.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ * Copyright 2013 Linaro 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CLOCK_BCM281XX_H
+#define _CLOCK_BCM281XX_H
+
+/*
+ * This file defines the values used to specify clocks provided by
+ * the clock control units (CCUs) on Broadcom BCM281XX family SoCs.
+ */
+
+/* root CCU clock ids */
+
+#define BCM281XX_ROOT_CCU_FRAC_1M 0
+#define BCM281XX_ROOT_CCU_CLOCK_COUNT 1
+
+/* aon CCU clock ids */
+
+#define BCM281XX_AON_CCU_HUB_TIMER 0
+#define BCM281XX_AON_CCU_PMU_BSC 1
+#define BCM281XX_AON_CCU_PMU_BSC_VAR 2
+#define BCM281XX_AON_CCU_CLOCK_COUNT 3
+
+/* hub CCU clock ids */
+
+#define BCM281XX_HUB_CCU_TMON_1M 0
+#define BCM281XX_HUB_CCU_CLOCK_COUNT 1
+
+/* master CCU clock ids */
+
+#define BCM281XX_MASTER_CCU_SDIO1 0
+#define BCM281XX_MASTER_CCU_SDIO2 1
+#define BCM281XX_MASTER_CCU_SDIO3 2
+#define BCM281XX_MASTER_CCU_SDIO4 3
+#define BCM281XX_MASTER_CCU_USB_IC 4
+#define BCM281XX_MASTER_CCU_HSIC2_48M 5
+#define BCM281XX_MASTER_CCU_HSIC2_12M 6
+#define BCM281XX_MASTER_CCU_CLOCK_COUNT 7
+
+/* slave CCU clock ids */
+
+#define BCM281XX_SLAVE_CCU_UARTB 0
+#define BCM281XX_SLAVE_CCU_UARTB2 1
+#define BCM281XX_SLAVE_CCU_UARTB3 2
+#define BCM281XX_SLAVE_CCU_UARTB4 3
+#define BCM281XX_SLAVE_CCU_SSP0 4
+#define BCM281XX_SLAVE_CCU_SSP2 5
+#define BCM281XX_SLAVE_CCU_BSC1 6
+#define BCM281XX_SLAVE_CCU_BSC2 7
+#define BCM281XX_SLAVE_CCU_BSC3 8
+#define BCM281XX_SLAVE_CCU_PWM 9
+#define BCM281XX_SLAVE_CCU_CLOCK_COUNT 10
+
+#endif /* _CLOCK_BCM281XX_H */
diff --git a/include/dt-bindings/clk/exynos-audss-clk.h b/include/dt-bindings/clock/exynos-audss-clk.h
index 0ae6f5a75d2a..0ae6f5a75d2a 100644
--- a/include/dt-bindings/clk/exynos-audss-clk.h
+++ b/include/dt-bindings/clock/exynos-audss-clk.h
diff --git a/include/dt-bindings/clock/hi3620-clock.h b/include/dt-bindings/clock/hi3620-clock.h
index 6eaa6a45e110..21b9d0e2eb0c 100644
--- a/include/dt-bindings/clock/hi3620-clock.h
+++ b/include/dt-bindings/clock/hi3620-clock.h
@@ -147,6 +147,11 @@
#define HI3620_MMC_CLK3 217
#define HI3620_MCU_CLK 218
+#define HI3620_SD_CIUCLK 0
+#define HI3620_MMC_CIUCLK1 1
+#define HI3620_MMC_CIUCLK2 2
+#define HI3620_MMC_CIUCLK3 3
+
#define HI3620_NR_CLKS 219
#endif /* __DTS_HI3620_CLOCK_H */
diff --git a/arch/arm/mach-at91/include/mach/timex.h b/include/dt-bindings/clock/hip04-clock.h
index 5e917a66edd7..695e61cd1523 100644
--- a/arch/arm/mach-at91/include/mach/timex.h
+++ b/include/dt-bindings/clock/hip04-clock.h
@@ -1,7 +1,8 @@
/*
- * arch/arm/mach-at91/include/mach/timex.h
+ * Copyright (c) 2013-2014 Hisilicon Limited.
+ * Copyright (c) 2013-2014 Linaro Limited.
*
- * Copyright (C) 2003 SAN People
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -13,25 +14,22 @@
* 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
+ * 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.
+ *
*/
-#ifndef __ASM_ARCH_TIMEX_H
-#define __ASM_ARCH_TIMEX_H
-
-#include <mach/hardware.h>
-
-#ifdef CONFIG_ARCH_AT91X40
-
-#define AT91X40_MASTER_CLOCK 40000000
-#define CLOCK_TICK_RATE (AT91X40_MASTER_CLOCK)
+#ifndef __DTS_HIP04_CLOCK_H
+#define __DTS_HIP04_CLOCK_H
-#else
+#define HIP04_NONE_CLOCK 0
-#define CLOCK_TICK_RATE 12345678
+/* fixed rate & fixed factor clocks */
+#define HIP04_OSC50M 1
+#define HIP04_CLK_50M 2
+#define HIP04_CLK_168M 3
-#endif
+#define HIP04_NR_CLKS 64
-#endif /* __ASM_ARCH_TIMEX_H */
+#endif /* __DTS_HIP04_CLOCK_H */
diff --git a/include/dt-bindings/clock/r8a7790-clock.h b/include/dt-bindings/clock/r8a7790-clock.h
index 859e9be511d9..6548a5fbcf4a 100644
--- a/include/dt-bindings/clock/r8a7790-clock.h
+++ b/include/dt-bindings/clock/r8a7790-clock.h
@@ -46,8 +46,8 @@
#define R8A7790_CLK_MSIOF1 8
#define R8A7790_CLK_MSIOF3 15
#define R8A7790_CLK_SCIFB2 16
-#define R8A7790_CLK_SYS_DMAC0 18
-#define R8A7790_CLK_SYS_DMAC1 19
+#define R8A7790_CLK_SYS_DMAC1 18
+#define R8A7790_CLK_SYS_DMAC0 19
/* MSTP3 */
#define R8A7790_CLK_TPU0 4
diff --git a/include/dt-bindings/pinctrl/am43xx.h b/include/dt-bindings/pinctrl/am43xx.h
index eb6c366adfba..9c2e4f82381e 100644
--- a/include/dt-bindings/pinctrl/am43xx.h
+++ b/include/dt-bindings/pinctrl/am43xx.h
@@ -13,6 +13,7 @@
#define MUX_MODE5 5
#define MUX_MODE6 6
#define MUX_MODE7 7
+#define MUX_MODE8 8
#define PULL_DISABLE (1 << 16)
#define PULL_UP (1 << 17)
diff --git a/include/dt-bindings/reset-controller/stih415-resets.h b/include/dt-bindings/reset-controller/stih415-resets.h
new file mode 100644
index 000000000000..c2f8a66913c5
--- /dev/null
+++ b/include/dt-bindings/reset-controller/stih415-resets.h
@@ -0,0 +1,26 @@
+/*
+ * This header provides constants for the reset controller
+ * based peripheral powerdown requests on the STMicroelectronics
+ * STiH415 SoC.
+ */
+#ifndef _DT_BINDINGS_RESET_CONTROLLER_STIH415
+#define _DT_BINDINGS_RESET_CONTROLLER_STIH415
+
+#define STIH415_EMISS_POWERDOWN 0
+#define STIH415_NAND_POWERDOWN 1
+#define STIH415_KEYSCAN_POWERDOWN 2
+#define STIH415_USB0_POWERDOWN 3
+#define STIH415_USB1_POWERDOWN 4
+#define STIH415_USB2_POWERDOWN 5
+#define STIH415_SATA0_POWERDOWN 6
+#define STIH415_SATA1_POWERDOWN 7
+#define STIH415_PCIE_POWERDOWN 8
+
+#define STIH415_ETH0_SOFTRESET 0
+#define STIH415_ETH1_SOFTRESET 1
+#define STIH415_IRB_SOFTRESET 2
+#define STIH415_USB0_SOFTRESET 3
+#define STIH415_USB1_SOFTRESET 4
+#define STIH415_USB2_SOFTRESET 5
+
+#endif /* _DT_BINDINGS_RESET_CONTROLLER_STIH415 */
diff --git a/include/dt-bindings/reset-controller/stih416-resets.h b/include/dt-bindings/reset-controller/stih416-resets.h
new file mode 100644
index 000000000000..2127743f23e3
--- /dev/null
+++ b/include/dt-bindings/reset-controller/stih416-resets.h
@@ -0,0 +1,50 @@
+/*
+ * This header provides constants for the reset controller
+ * based peripheral powerdown requests on the STMicroelectronics
+ * STiH416 SoC.
+ */
+#ifndef _DT_BINDINGS_RESET_CONTROLLER_STIH416
+#define _DT_BINDINGS_RESET_CONTROLLER_STIH416
+
+#define STIH416_EMISS_POWERDOWN 0
+#define STIH416_NAND_POWERDOWN 1
+#define STIH416_KEYSCAN_POWERDOWN 2
+#define STIH416_USB0_POWERDOWN 3
+#define STIH416_USB1_POWERDOWN 4
+#define STIH416_USB2_POWERDOWN 5
+#define STIH416_USB3_POWERDOWN 6
+#define STIH416_SATA0_POWERDOWN 7
+#define STIH416_SATA1_POWERDOWN 8
+#define STIH416_PCIE0_POWERDOWN 9
+#define STIH416_PCIE1_POWERDOWN 10
+
+#define STIH416_ETH0_SOFTRESET 0
+#define STIH416_ETH1_SOFTRESET 1
+#define STIH416_IRB_SOFTRESET 2
+#define STIH416_USB0_SOFTRESET 3
+#define STIH416_USB1_SOFTRESET 4
+#define STIH416_USB2_SOFTRESET 5
+#define STIH416_USB3_SOFTRESET 6
+#define STIH416_SATA0_SOFTRESET 7
+#define STIH416_SATA1_SOFTRESET 8
+#define STIH416_PCIE0_SOFTRESET 9
+#define STIH416_PCIE1_SOFTRESET 10
+#define STIH416_AUD_DAC_SOFTRESET 11
+#define STIH416_HDTVOUT_SOFTRESET 12
+#define STIH416_VTAC_M_RX_SOFTRESET 13
+#define STIH416_VTAC_A_RX_SOFTRESET 14
+#define STIH416_SYNC_HD_SOFTRESET 15
+#define STIH416_SYNC_SD_SOFTRESET 16
+#define STIH416_BLITTER_SOFTRESET 17
+#define STIH416_GPU_SOFTRESET 18
+#define STIH416_VTAC_M_TX_SOFTRESET 19
+#define STIH416_VTAC_A_TX_SOFTRESET 20
+#define STIH416_VTG_AUX_SOFTRESET 21
+#define STIH416_JPEG_DEC_SOFTRESET 22
+#define STIH416_HVA_SOFTRESET 23
+#define STIH416_COMPO_M_SOFTRESET 24
+#define STIH416_COMPO_A_SOFTRESET 25
+#define STIH416_VP8_DEC_SOFTRESET 26
+#define STIH416_VTG_MAIN_SOFTRESET 27
+
+#endif /* _DT_BINDINGS_RESET_CONTROLLER_STIH416 */
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index 24819001f5c8..e488e9459a93 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -95,7 +95,7 @@ struct backing_dev_info {
unsigned int max_ratio, max_prop_frac;
struct bdi_writeback wb; /* default writeback info for this bdi */
- spinlock_t wb_lock; /* protects work_list */
+ spinlock_t wb_lock; /* protects work_list & wb.dwork scheduling */
struct list_head work_list;
diff --git a/include/linux/backlight.h b/include/linux/backlight.h
index 5f9cd963213d..72647429adf6 100644
--- a/include/linux/backlight.h
+++ b/include/linux/backlight.h
@@ -9,6 +9,7 @@
#define _LINUX_BACKLIGHT_H
#include <linux/device.h>
+#include <linux/fb.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
@@ -104,6 +105,11 @@ struct backlight_device {
struct list_head entry;
struct device dev;
+
+ /* Multiple framebuffers may share one backlight device */
+ bool fb_bl_on[FB_MAX];
+
+ int use_count;
};
static inline void backlight_update_status(struct backlight_device *bd)
diff --git a/include/linux/basic_mmio_gpio.h b/include/linux/basic_mmio_gpio.h
index d8a97ec0e2b8..0e97856b2cff 100644
--- a/include/linux/basic_mmio_gpio.h
+++ b/include/linux/basic_mmio_gpio.h
@@ -19,6 +19,7 @@
#include <linux/spinlock_types.h>
struct bgpio_pdata {
+ const char *label;
int base;
int ngpio;
};
diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h
index 677b4f01b2d0..6f76277baf39 100644
--- a/include/linux/brcmphy.h
+++ b/include/linux/brcmphy.h
@@ -13,10 +13,17 @@
#define PHY_ID_BCM5461 0x002060c0
#define PHY_ID_BCM57780 0x03625d90
+#define PHY_ID_BCM7366 0x600d8490
+#define PHY_ID_BCM7439 0x600d8480
+#define PHY_ID_BCM7445 0x600d8510
+#define PHY_ID_BCM7XXX_28 0x600d8400
+
#define PHY_BCM_OUI_MASK 0xfffffc00
#define PHY_BCM_OUI_1 0x00206000
#define PHY_BCM_OUI_2 0x0143bc00
#define PHY_BCM_OUI_3 0x03625c00
+#define PHY_BCM_OUI_4 0x600d0000
+#define PHY_BCM_OUI_5 0x03625e00
#define PHY_BCM_FLAGS_MODE_COPPER 0x00000001
@@ -31,6 +38,59 @@
#define PHY_BRCM_EXT_IBND_TX_ENABLE 0x00002000
#define PHY_BRCM_CLEAR_RGMII_MODE 0x00004000
#define PHY_BRCM_DIS_TXCRXC_NOENRGY 0x00008000
+/* Broadcom BCM7xxx specific workarounds */
+#define PHY_BRCM_100MBPS_WAR 0x00010000
#define PHY_BCM_FLAGS_VALID 0x80000000
+/* Broadcom BCM54XX register definitions, common to most Broadcom PHYs */
+#define MII_BCM54XX_ECR 0x10 /* BCM54xx extended control register */
+#define MII_BCM54XX_ECR_IM 0x1000 /* Interrupt mask */
+#define MII_BCM54XX_ECR_IF 0x0800 /* Interrupt force */
+
+#define MII_BCM54XX_ESR 0x11 /* BCM54xx extended status register */
+#define MII_BCM54XX_ESR_IS 0x1000 /* Interrupt status */
+
+#define MII_BCM54XX_EXP_DATA 0x15 /* Expansion register data */
+#define MII_BCM54XX_EXP_SEL 0x17 /* Expansion register select */
+#define MII_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */
+#define MII_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */
+
+#define MII_BCM54XX_AUX_CTL 0x18 /* Auxiliary control register */
+#define MII_BCM54XX_ISR 0x1a /* BCM54xx interrupt status register */
+#define MII_BCM54XX_IMR 0x1b /* BCM54xx interrupt mask register */
+#define MII_BCM54XX_INT_CRCERR 0x0001 /* CRC error */
+#define MII_BCM54XX_INT_LINK 0x0002 /* Link status changed */
+#define MII_BCM54XX_INT_SPEED 0x0004 /* Link speed change */
+#define MII_BCM54XX_INT_DUPLEX 0x0008 /* Duplex mode changed */
+#define MII_BCM54XX_INT_LRS 0x0010 /* Local receiver status changed */
+#define MII_BCM54XX_INT_RRS 0x0020 /* Remote receiver status changed */
+#define MII_BCM54XX_INT_SSERR 0x0040 /* Scrambler synchronization error */
+#define MII_BCM54XX_INT_UHCD 0x0080 /* Unsupported HCD negotiated */
+#define MII_BCM54XX_INT_NHCD 0x0100 /* No HCD */
+#define MII_BCM54XX_INT_NHCDL 0x0200 /* No HCD link */
+#define MII_BCM54XX_INT_ANPR 0x0400 /* Auto-negotiation page received */
+#define MII_BCM54XX_INT_LC 0x0800 /* All counters below 128 */
+#define MII_BCM54XX_INT_HC 0x1000 /* Counter above 32768 */
+#define MII_BCM54XX_INT_MDIX 0x2000 /* MDIX status change */
+#define MII_BCM54XX_INT_PSERR 0x4000 /* Pair swap error */
+
+#define MII_BCM54XX_SHD 0x1c /* 0x1c shadow registers */
+#define MII_BCM54XX_SHD_WRITE 0x8000
+#define MII_BCM54XX_SHD_VAL(x) ((x & 0x1f) << 10)
+#define MII_BCM54XX_SHD_DATA(x) ((x & 0x3ff) << 0)
+
+/*
+ * AUXILIARY CONTROL SHADOW ACCESS REGISTERS. (PHY REG 0x18)
+ */
+#define MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL 0x0000
+#define MII_BCM54XX_AUXCTL_ACTL_TX_6DB 0x0400
+#define MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA 0x0800
+
+#define MII_BCM54XX_AUXCTL_MISC_WREN 0x8000
+#define MII_BCM54XX_AUXCTL_MISC_FORCE_AMDIX 0x0200
+#define MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC 0x7000
+#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007
+
+#define MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL 0x0000
+
#endif /* _LINUX_BRCMPHY_H */
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index fb0ab651a041..3ce5e526525f 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -33,8 +33,9 @@ enum can_mode {
struct can_priv {
struct can_device_stats can_stats;
- struct can_bittiming bittiming;
- const struct can_bittiming_const *bittiming_const;
+ struct can_bittiming bittiming, data_bittiming;
+ const struct can_bittiming_const *bittiming_const,
+ *data_bittiming_const;
struct can_clock clock;
enum can_state state;
@@ -45,6 +46,7 @@ struct can_priv {
struct timer_list restart_timer;
int (*do_set_bittiming)(struct net_device *dev);
+ int (*do_set_data_bittiming)(struct net_device *dev);
int (*do_set_mode)(struct net_device *dev, enum can_mode mode);
int (*do_get_state)(const struct net_device *dev,
enum can_state *state);
@@ -111,6 +113,7 @@ struct can_priv *safe_candev_priv(struct net_device *dev);
int open_candev(struct net_device *dev);
void close_candev(struct net_device *dev);
+int can_change_mtu(struct net_device *dev, int new_mtu);
int register_candev(struct net_device *dev);
void unregister_candev(struct net_device *dev);
@@ -124,6 +127,8 @@ unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx);
void can_free_echo_skb(struct net_device *dev, unsigned int idx);
struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf);
+struct sk_buff *alloc_canfd_skb(struct net_device *dev,
+ struct canfd_frame **cfd);
struct sk_buff *alloc_can_err_skb(struct net_device *dev,
struct can_frame **cf);
diff --git a/include/linux/ccp.h b/include/linux/ccp.h
index b941ab9f762b..ebcc9d146219 100644
--- a/include/linux/ccp.h
+++ b/include/linux/ccp.h
@@ -232,6 +232,9 @@ enum ccp_sha_type {
* @ctx_len: length in bytes of hash value
* @src: data to be used for this operation
* @src_len: length in bytes of data used for this operation
+ * @opad: data to be used for final HMAC operation
+ * @opad_len: length in bytes of data used for final HMAC operation
+ * @first: indicates first SHA operation
* @final: indicates final SHA operation
* @msg_bits: total length of the message in bits used in final SHA operation
*
@@ -251,6 +254,10 @@ struct ccp_sha_engine {
struct scatterlist *src;
u64 src_len; /* In bytes */
+ struct scatterlist *opad;
+ u32 opad_len; /* In bytes */
+
+ u32 first; /* Indicates first sha cmd */
u32 final; /* Indicates final sha cmd */
u64 msg_bits; /* Message length in bits required for
* final sha cmd */
diff --git a/include/linux/ceph/ceph_features.h b/include/linux/ceph/ceph_features.h
index 138448f766b4..d12659ce550d 100644
--- a/include/linux/ceph/ceph_features.h
+++ b/include/linux/ceph/ceph_features.h
@@ -43,6 +43,13 @@
#define CEPH_FEATURE_CRUSH_V2 (1ULL<<36) /* new indep; SET_* steps */
#define CEPH_FEATURE_EXPORT_PEER (1ULL<<37)
#define CEPH_FEATURE_OSD_ERASURE_CODES (1ULL<<38)
+#define CEPH_FEATURE_OSD_TMAP2OMAP (1ULL<<38) /* overlap with EC */
+/* The process supports new-style OSDMap encoding. Monitors also use
+ this bit to determine if peers support NAK messages. */
+#define CEPH_FEATURE_OSDMAP_ENC (1ULL<<39)
+#define CEPH_FEATURE_MDS_INLINE_DATA (1ULL<<40)
+#define CEPH_FEATURE_CRUSH_TUNABLES3 (1ULL<<41)
+#define CEPH_FEATURE_OSD_PRIMARY_AFFINITY (1ULL<<41) /* overlap w/ tunables3 */
/*
* The introduction of CEPH_FEATURE_OSD_SNAPMAPPER caused the feature
@@ -82,7 +89,10 @@ static inline u64 ceph_sanitize_features(u64 features)
CEPH_FEATURE_OSDHASHPSPOOL | \
CEPH_FEATURE_OSD_CACHEPOOL | \
CEPH_FEATURE_CRUSH_V2 | \
- CEPH_FEATURE_EXPORT_PEER)
+ CEPH_FEATURE_EXPORT_PEER | \
+ CEPH_FEATURE_OSDMAP_ENC | \
+ CEPH_FEATURE_CRUSH_TUNABLES3 | \
+ CEPH_FEATURE_OSD_PRIMARY_AFFINITY)
#define CEPH_FEATURES_REQUIRED_DEFAULT \
(CEPH_FEATURE_NOSRCADDR | \
diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h
index 25bfb0eff772..5f6db18d72e8 100644
--- a/include/linux/ceph/ceph_fs.h
+++ b/include/linux/ceph/ceph_fs.h
@@ -332,6 +332,7 @@ enum {
CEPH_MDS_OP_LOOKUPHASH = 0x00102,
CEPH_MDS_OP_LOOKUPPARENT = 0x00103,
CEPH_MDS_OP_LOOKUPINO = 0x00104,
+ CEPH_MDS_OP_LOOKUPNAME = 0x00105,
CEPH_MDS_OP_SETXATTR = 0x01105,
CEPH_MDS_OP_RMXATTR = 0x01106,
@@ -420,8 +421,8 @@ union ceph_mds_request_args {
struct {
__u8 rule; /* currently fcntl or flock */
__u8 type; /* shared, exclusive, remove*/
+ __le64 owner; /* owner of the lock */
__le64 pid; /* process id requesting the lock */
- __le64 pid_namespace;
__le64 start; /* initial location to lock */
__le64 length; /* num bytes to lock from start */
__u8 wait; /* will caller wait for lock to become available? */
@@ -532,8 +533,8 @@ struct ceph_filelock {
__le64 start;/* file offset to start lock at */
__le64 length; /* num bytes to lock; 0 for all following start */
__le64 client; /* which client holds the lock */
+ __le64 owner; /* owner the lock */
__le64 pid; /* process id holding the lock on the client */
- __le64 pid_namespace;
__u8 type; /* shared lock, exclusive lock, or unlock */
} __attribute__ ((packed));
diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h
index fd47e872ebcc..94ec69672164 100644
--- a/include/linux/ceph/osd_client.h
+++ b/include/linux/ceph/osd_client.h
@@ -43,7 +43,7 @@ struct ceph_osd {
};
-#define CEPH_OSD_MAX_OP 2
+#define CEPH_OSD_MAX_OP 3
enum ceph_osd_data_type {
CEPH_OSD_DATA_TYPE_NONE = 0,
@@ -76,6 +76,7 @@ struct ceph_osd_data {
struct ceph_osd_req_op {
u16 op; /* CEPH_OSD_OP_* */
+ u32 flags; /* CEPH_OSD_OP_FLAG_* */
u32 payload_len;
union {
struct ceph_osd_data raw_data_in;
@@ -102,6 +103,10 @@ struct ceph_osd_req_op {
u32 timeout;
__u8 flag;
} watch;
+ struct {
+ u64 expected_object_size;
+ u64 expected_write_size;
+ } alloc_hint;
};
};
@@ -293,6 +298,10 @@ extern void osd_req_op_cls_init(struct ceph_osd_request *osd_req,
extern void osd_req_op_watch_init(struct ceph_osd_request *osd_req,
unsigned int which, u16 opcode,
u64 cookie, u64 version, int flag);
+extern void osd_req_op_alloc_hint_init(struct ceph_osd_request *osd_req,
+ unsigned int which,
+ u64 expected_object_size,
+ u64 expected_write_size);
extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc,
struct ceph_snap_context *snapc,
diff --git a/include/linux/ceph/osdmap.h b/include/linux/ceph/osdmap.h
index 49ff69f0746b..561ea896c657 100644
--- a/include/linux/ceph/osdmap.h
+++ b/include/linux/ceph/osdmap.h
@@ -41,6 +41,18 @@ struct ceph_pg_pool_info {
char *name;
};
+static inline bool ceph_can_shift_osds(struct ceph_pg_pool_info *pool)
+{
+ switch (pool->type) {
+ case CEPH_POOL_TYPE_REP:
+ return true;
+ case CEPH_POOL_TYPE_EC:
+ return false;
+ default:
+ BUG_ON(1);
+ }
+}
+
struct ceph_object_locator {
s64 pool;
};
@@ -60,8 +72,16 @@ struct ceph_object_id {
struct ceph_pg_mapping {
struct rb_node node;
struct ceph_pg pgid;
- int len;
- int osds[];
+
+ union {
+ struct {
+ int len;
+ int osds[];
+ } pg_temp;
+ struct {
+ int osd;
+ } primary_temp;
+ };
};
struct ceph_osdmap {
@@ -78,12 +98,19 @@ struct ceph_osdmap {
struct ceph_entity_addr *osd_addr;
struct rb_root pg_temp;
+ struct rb_root primary_temp;
+
+ u32 *osd_primary_affinity;
+
struct rb_root pg_pools;
u32 pool_max;
/* the CRUSH map specifies the mapping of placement groups to
* the list of osds that store+replicate them. */
struct crush_map *crush;
+
+ struct mutex crush_scratch_mutex;
+ int crush_scratch_ary[CEPH_PG_MAX_SIZE * 3];
};
static inline void ceph_oid_set_name(struct ceph_object_id *oid,
@@ -110,9 +137,21 @@ static inline void ceph_oid_copy(struct ceph_object_id *dest,
dest->name_len = src->name_len;
}
+static inline int ceph_osd_exists(struct ceph_osdmap *map, int osd)
+{
+ return osd >= 0 && osd < map->max_osd &&
+ (map->osd_state[osd] & CEPH_OSD_EXISTS);
+}
+
static inline int ceph_osd_is_up(struct ceph_osdmap *map, int osd)
{
- return (osd < map->max_osd) && (map->osd_state[osd] & CEPH_OSD_UP);
+ return ceph_osd_exists(map, osd) &&
+ (map->osd_state[osd] & CEPH_OSD_UP);
+}
+
+static inline int ceph_osd_is_down(struct ceph_osdmap *map, int osd)
+{
+ return !ceph_osd_is_up(map, osd);
}
static inline bool ceph_osdmap_flag(struct ceph_osdmap *map, int flag)
@@ -121,6 +160,7 @@ static inline bool ceph_osdmap_flag(struct ceph_osdmap *map, int flag)
}
extern char *ceph_osdmap_state_str(char *str, int len, int state);
+extern u32 ceph_get_primary_affinity(struct ceph_osdmap *map, int osd);
static inline struct ceph_entity_addr *ceph_osd_addr(struct ceph_osdmap *map,
int osd)
@@ -153,7 +193,7 @@ static inline int ceph_decode_pgid(void **p, void *end, struct ceph_pg *pgid)
return 0;
}
-extern struct ceph_osdmap *osdmap_decode(void **p, void *end);
+extern struct ceph_osdmap *ceph_osdmap_decode(void **p, void *end);
extern struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
struct ceph_osdmap *map,
struct ceph_messenger *msgr);
@@ -172,7 +212,7 @@ extern int ceph_oloc_oid_to_pg(struct ceph_osdmap *osdmap,
extern int ceph_calc_pg_acting(struct ceph_osdmap *osdmap,
struct ceph_pg pgid,
- int *acting);
+ int *osds, int *primary);
extern int ceph_calc_pg_primary(struct ceph_osdmap *osdmap,
struct ceph_pg pgid);
diff --git a/include/linux/ceph/rados.h b/include/linux/ceph/rados.h
index 96292df4041b..f20e0d8a2155 100644
--- a/include/linux/ceph/rados.h
+++ b/include/linux/ceph/rados.h
@@ -81,8 +81,9 @@ struct ceph_pg_v1 {
*/
#define CEPH_NOPOOL ((__u64) (-1)) /* pool id not defined */
-#define CEPH_PG_TYPE_REP 1
-#define CEPH_PG_TYPE_RAID4 2
+#define CEPH_POOL_TYPE_REP 1
+#define CEPH_POOL_TYPE_RAID4 2 /* never implemented */
+#define CEPH_POOL_TYPE_EC 3
/*
* stable_mod func is used to control number of placement groups.
@@ -133,6 +134,10 @@ extern const char *ceph_osd_state_name(int s);
#define CEPH_OSD_IN 0x10000
#define CEPH_OSD_OUT 0
+/* osd primary-affinity. fixed point value: 0x10000 == baseline */
+#define CEPH_OSD_MAX_PRIMARY_AFFINITY 0x10000
+#define CEPH_OSD_DEFAULT_PRIMARY_AFFINITY 0x10000
+
/*
* osd map flag bits
@@ -227,6 +232,9 @@ enum {
CEPH_OSD_OP_OMAPRMKEYS = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 24,
CEPH_OSD_OP_OMAP_CMP = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 25,
+ /* hints */
+ CEPH_OSD_OP_SETALLOCHINT = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 35,
+
/** multi **/
CEPH_OSD_OP_CLONERANGE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_MULTI | 1,
CEPH_OSD_OP_ASSERT_SRC_VERSION = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_MULTI | 2,
@@ -382,7 +390,7 @@ enum {
*/
struct ceph_osd_op {
__le16 op; /* CEPH_OSD_OP_* */
- __le32 flags; /* CEPH_OSD_FLAG_* */
+ __le32 flags; /* CEPH_OSD_OP_FLAG_* */
union {
struct {
__le64 offset, length;
@@ -416,6 +424,10 @@ struct ceph_osd_op {
__le64 offset, length;
__le64 src_offset;
} __attribute__ ((packed)) clonerange;
+ struct {
+ __le64 expected_object_size;
+ __le64 expected_write_size;
+ } __attribute__ ((packed)) alloc_hint;
};
__le32 payload_len;
} __attribute__ ((packed));
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index 9450f025fe0c..c2515851c1aa 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -14,18 +14,17 @@
#include <linux/rcupdate.h>
#include <linux/rculist.h>
#include <linux/cgroupstats.h>
-#include <linux/prio_heap.h>
#include <linux/rwsem.h>
#include <linux/idr.h>
#include <linux/workqueue.h>
-#include <linux/xattr.h>
#include <linux/fs.h>
#include <linux/percpu-refcount.h>
#include <linux/seq_file.h>
+#include <linux/kernfs.h>
#ifdef CONFIG_CGROUPS
-struct cgroupfs_root;
+struct cgroup_root;
struct cgroup_subsys;
struct inode;
struct cgroup;
@@ -34,31 +33,16 @@ extern int cgroup_init_early(void);
extern int cgroup_init(void);
extern void cgroup_fork(struct task_struct *p);
extern void cgroup_post_fork(struct task_struct *p);
-extern void cgroup_exit(struct task_struct *p, int run_callbacks);
+extern void cgroup_exit(struct task_struct *p);
extern int cgroupstats_build(struct cgroupstats *stats,
struct dentry *dentry);
-extern int cgroup_load_subsys(struct cgroup_subsys *ss);
-extern void cgroup_unload_subsys(struct cgroup_subsys *ss);
extern int proc_cgroup_show(struct seq_file *, void *);
-/*
- * Define the enumeration of all cgroup subsystems.
- *
- * We define ids for builtin subsystems and then modular ones.
- */
-#define SUBSYS(_x) _x ## _subsys_id,
+/* define the enumeration of all cgroup subsystems */
+#define SUBSYS(_x) _x ## _cgrp_id,
enum cgroup_subsys_id {
-#define IS_SUBSYS_ENABLED(option) IS_BUILTIN(option)
-#include <linux/cgroup_subsys.h>
-#undef IS_SUBSYS_ENABLED
- CGROUP_BUILTIN_SUBSYS_COUNT,
-
- __CGROUP_SUBSYS_TEMP_PLACEHOLDER = CGROUP_BUILTIN_SUBSYS_COUNT - 1,
-
-#define IS_SUBSYS_ENABLED(option) IS_MODULE(option)
#include <linux/cgroup_subsys.h>
-#undef IS_SUBSYS_ENABLED
CGROUP_SUBSYS_COUNT,
};
#undef SUBSYS
@@ -153,11 +137,6 @@ enum {
CGRP_SANE_BEHAVIOR,
};
-struct cgroup_name {
- struct rcu_head rcu_head;
- char name[];
-};
-
struct cgroup {
unsigned long flags; /* "unsigned long" so bitops work */
@@ -174,16 +153,17 @@ struct cgroup {
/* the number of attached css's */
int nr_css;
+ atomic_t refcnt;
+
/*
* We link our 'sibling' struct into our parent's 'children'.
* Our children link their 'sibling' into our 'children'.
*/
struct list_head sibling; /* my parent's children */
struct list_head children; /* my children */
- struct list_head files; /* my files */
struct cgroup *parent; /* my parent */
- struct dentry *dentry; /* cgroup fs entry, RCU protected */
+ struct kernfs_node *kn; /* cgroup kernfs entry */
/*
* Monotonically increasing unique serial number which defines a
@@ -193,23 +173,13 @@ struct cgroup {
*/
u64 serial_nr;
- /*
- * This is a copy of dentry->d_name, and it's needed because
- * we can't use dentry->d_name in cgroup_path().
- *
- * You must acquire rcu_read_lock() to access cgrp->name, and
- * the only place that can change it is rename(), which is
- * protected by parent dir's i_mutex.
- *
- * Normally you should use cgroup_name() wrapper rather than
- * access it directly.
- */
- struct cgroup_name __rcu *name;
+ /* The bitmask of subsystems attached to this cgroup */
+ unsigned long subsys_mask;
/* Private pointers for each registered subsystem */
struct cgroup_subsys_state __rcu *subsys[CGROUP_SUBSYS_COUNT];
- struct cgroupfs_root *root;
+ struct cgroup_root *root;
/*
* List of cgrp_cset_links pointing at css_sets with tasks in this
@@ -237,14 +207,11 @@ struct cgroup {
/* For css percpu_ref killing and RCU-protected deletion */
struct rcu_head rcu_head;
struct work_struct destroy_work;
-
- /* directory xattrs */
- struct simple_xattrs xattrs;
};
#define MAX_CGROUP_ROOT_NAMELEN 64
-/* cgroupfs_root->flags */
+/* cgroup_root->flags */
enum {
/*
* Unfortunately, cgroup core and various controllers are riddled
@@ -262,8 +229,8 @@ enum {
*
* The followings are the behaviors currently affected this flag.
*
- * - Mount options "noprefix" and "clone_children" are disallowed.
- * Also, cgroupfs file cgroup.clone_children is not created.
+ * - Mount options "noprefix", "xattr", "clone_children",
+ * "release_agent" and "name" are disallowed.
*
* - When mounting an existing superblock, mount options should
* match.
@@ -281,6 +248,11 @@ enum {
* - "release_agent" and "notify_on_release" are removed.
* Replacement notification mechanism will be implemented.
*
+ * - "cgroup.clone_children" is removed.
+ *
+ * - If mount is requested with sane_behavior but without any
+ * subsystem, the default unified hierarchy is mounted.
+ *
* - cpuset: tasks will be kept in empty cpusets when hotplug happens
* and take masks of ancestors with non-empty cpus/mems, instead of
* being moved to an ancestor.
@@ -300,29 +272,24 @@ enum {
/* mount options live below bit 16 */
CGRP_ROOT_OPTION_MASK = (1 << 16) - 1,
-
- CGRP_ROOT_SUBSYS_BOUND = (1 << 16), /* subsystems finished binding */
};
/*
- * A cgroupfs_root represents the root of a cgroup hierarchy, and may be
- * associated with a superblock to form an active hierarchy. This is
+ * A cgroup_root represents the root of a cgroup hierarchy, and may be
+ * associated with a kernfs_root to form an active hierarchy. This is
* internal to cgroup core. Don't access directly from controllers.
*/
-struct cgroupfs_root {
- struct super_block *sb;
-
- /* The bitmask of subsystems attached to this hierarchy */
- unsigned long subsys_mask;
+struct cgroup_root {
+ struct kernfs_root *kf_root;
/* Unique id for this hierarchy. */
int hierarchy_id;
- /* The root cgroup for this hierarchy */
- struct cgroup top_cgroup;
+ /* The root cgroup. Root is destroyed on its release. */
+ struct cgroup cgrp;
- /* Tracks how many cgroups are currently defined in hierarchy.*/
- int number_of_cgroups;
+ /* Number of cgroups in the hierarchy, used only for /proc/cgroups */
+ atomic_t nr_cgrps;
/* A list running through the active hierarchies */
struct list_head root_list;
@@ -360,10 +327,14 @@ struct css_set {
struct hlist_node hlist;
/*
- * List running through all tasks using this cgroup
- * group. Protected by css_set_lock
+ * Lists running through all tasks using this cgroup group.
+ * mg_tasks lists tasks which belong to this cset but are in the
+ * process of being migrated out or in. Protected by
+ * css_set_rwsem, but, during migration, once tasks are moved to
+ * mg_tasks, it can be read safely while holding cgroup_mutex.
*/
struct list_head tasks;
+ struct list_head mg_tasks;
/*
* List of cgrp_cset_links pointing at cgroups referenced from this
@@ -372,13 +343,29 @@ struct css_set {
struct list_head cgrp_links;
/*
- * Set of subsystem states, one for each subsystem. This array
- * is immutable after creation apart from the init_css_set
- * during subsystem registration (at boot time) and modular subsystem
- * loading/unloading.
+ * Set of subsystem states, one for each subsystem. This array is
+ * immutable after creation apart from the init_css_set during
+ * subsystem registration (at boot time).
*/
struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];
+ /*
+ * List of csets participating in the on-going migration either as
+ * source or destination. Protected by cgroup_mutex.
+ */
+ struct list_head mg_preload_node;
+ struct list_head mg_node;
+
+ /*
+ * If this cset is acting as the source of migration the following
+ * two fields are set. mg_src_cgrp is the source cgroup of the
+ * on-going migration and mg_dst_cset is the destination cset the
+ * target tasks on this cset should be migrated to. Protected by
+ * cgroup_mutex.
+ */
+ struct cgroup *mg_src_cgrp;
+ struct css_set *mg_dst_cset;
+
/* For RCU-protected deletion */
struct rcu_head rcu_head;
};
@@ -397,6 +384,7 @@ enum {
CFTYPE_NOT_ON_ROOT = (1 << 1), /* don't create on root cgrp */
CFTYPE_INSANE = (1 << 2), /* don't create if sane_behavior */
CFTYPE_NO_PREFIX = (1 << 3), /* (DON'T USE FOR NEW FILES) no subsys prefix */
+ CFTYPE_ONLY_ON_DFL = (1 << 4), /* only on default hierarchy */
};
#define MAX_CFTYPE_NAME 64
@@ -416,8 +404,9 @@ struct cftype {
umode_t mode;
/*
- * If non-zero, defines the maximum length of string that can
- * be passed to write_string; defaults to 64
+ * The maximum length of string, excluding trailing nul, that can
+ * be passed to write_string. If < PAGE_SIZE-1, PAGE_SIZE-1 is
+ * assumed.
*/
size_t max_write_len;
@@ -425,10 +414,12 @@ struct cftype {
unsigned int flags;
/*
- * The subsys this file belongs to. Initialized automatically
- * during registration. NULL for cgroup core files.
+ * Fields used for internal bookkeeping. Initialized automatically
+ * during registration.
*/
- struct cgroup_subsys *ss;
+ struct cgroup_subsys *ss; /* NULL for cgroup core files */
+ struct list_head node; /* anchored at ss->cfts */
+ struct kernfs_ops *kf_ops;
/*
* read_u64() is a shortcut for the common case of returning a
@@ -467,7 +458,7 @@ struct cftype {
* Returns 0 or -ve error code.
*/
int (*write_string)(struct cgroup_subsys_state *css, struct cftype *cft,
- const char *buffer);
+ char *buffer);
/*
* trigger() callback can be used to get some kick from the
* userspace, when the actual string written is not important
@@ -475,37 +466,18 @@ struct cftype {
* kick type for multiplexing.
*/
int (*trigger)(struct cgroup_subsys_state *css, unsigned int event);
-};
-/*
- * cftype_sets describe cftypes belonging to a subsystem and are chained at
- * cgroup_subsys->cftsets. Each cftset points to an array of cftypes
- * terminated by zero length name.
- */
-struct cftype_set {
- struct list_head node; /* chained at subsys->cftsets */
- struct cftype *cfts;
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ struct lock_class_key lockdep_key;
+#endif
};
-/*
- * cgroupfs file entry, pointed to from leaf dentry->d_fsdata. Don't
- * access directly.
- */
-struct cfent {
- struct list_head node;
- struct dentry *dentry;
- struct cftype *type;
- struct cgroup_subsys_state *css;
-
- /* file xattrs */
- struct simple_xattrs xattrs;
-};
+extern struct cgroup_root cgrp_dfl_root;
-/* seq_file->private points to the following, only ->priv is public */
-struct cgroup_open_file {
- struct cfent *cfe;
- void *priv;
-};
+static inline bool cgroup_on_dfl(const struct cgroup *cgrp)
+{
+ return cgrp->root == &cgrp_dfl_root;
+}
/*
* See the comment above CGRP_ROOT_SANE_BEHAVIOR for details. This
@@ -516,34 +488,63 @@ static inline bool cgroup_sane_behavior(const struct cgroup *cgrp)
return cgrp->root->flags & CGRP_ROOT_SANE_BEHAVIOR;
}
-/* Caller should hold rcu_read_lock() */
-static inline const char *cgroup_name(const struct cgroup *cgrp)
+/* no synchronization, the result can only be used as a hint */
+static inline bool cgroup_has_tasks(struct cgroup *cgrp)
{
- return rcu_dereference(cgrp->name)->name;
+ return !list_empty(&cgrp->cset_links);
}
-static inline struct cgroup_subsys_state *seq_css(struct seq_file *seq)
+/* returns ino associated with a cgroup, 0 indicates unmounted root */
+static inline ino_t cgroup_ino(struct cgroup *cgrp)
{
- struct cgroup_open_file *of = seq->private;
- return of->cfe->css;
+ if (cgrp->kn)
+ return cgrp->kn->ino;
+ else
+ return 0;
}
static inline struct cftype *seq_cft(struct seq_file *seq)
{
- struct cgroup_open_file *of = seq->private;
- return of->cfe->type;
+ struct kernfs_open_file *of = seq->private;
+
+ return of->kn->priv;
+}
+
+struct cgroup_subsys_state *seq_css(struct seq_file *seq);
+
+/*
+ * Name / path handling functions. All are thin wrappers around the kernfs
+ * counterparts and can be called under any context.
+ */
+
+static inline int cgroup_name(struct cgroup *cgrp, char *buf, size_t buflen)
+{
+ return kernfs_name(cgrp->kn, buf, buflen);
}
+static inline char * __must_check cgroup_path(struct cgroup *cgrp, char *buf,
+ size_t buflen)
+{
+ return kernfs_path(cgrp->kn, buf, buflen);
+}
+
+static inline void pr_cont_cgroup_name(struct cgroup *cgrp)
+{
+ pr_cont_kernfs_name(cgrp->kn);
+}
+
+static inline void pr_cont_cgroup_path(struct cgroup *cgrp)
+{
+ pr_cont_kernfs_path(cgrp->kn);
+}
+
+char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen);
+
int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts);
int cgroup_rm_cftypes(struct cftype *cfts);
bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor);
-int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen);
-int task_cgroup_path(struct task_struct *task, char *buf, size_t buflen);
-
-int cgroup_task_count(const struct cgroup *cgrp);
-
/*
* Control Group taskset, used to pass around set of tasks to cgroup_subsys
* methods.
@@ -551,22 +552,15 @@ int cgroup_task_count(const struct cgroup *cgrp);
struct cgroup_taskset;
struct task_struct *cgroup_taskset_first(struct cgroup_taskset *tset);
struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset);
-struct cgroup_subsys_state *cgroup_taskset_cur_css(struct cgroup_taskset *tset,
- int subsys_id);
-int cgroup_taskset_size(struct cgroup_taskset *tset);
/**
* cgroup_taskset_for_each - iterate cgroup_taskset
* @task: the loop cursor
- * @skip_css: skip if task's css matches this, %NULL to iterate through all
* @tset: taskset to iterate
*/
-#define cgroup_taskset_for_each(task, skip_css, tset) \
+#define cgroup_taskset_for_each(task, tset) \
for ((task) = cgroup_taskset_first((tset)); (task); \
- (task) = cgroup_taskset_next((tset))) \
- if (!(skip_css) || \
- cgroup_taskset_cur_css((tset), \
- (skip_css)->ss->subsys_id) != (skip_css))
+ (task) = cgroup_taskset_next((tset)))
/*
* Control Group subsystem type.
@@ -591,7 +585,6 @@ struct cgroup_subsys {
struct task_struct *task);
void (*bind)(struct cgroup_subsys_state *root_css);
- int subsys_id;
int disabled;
int early_init;
@@ -610,27 +603,26 @@ struct cgroup_subsys {
bool broken_hierarchy;
bool warned_broken_hierarchy;
+ /* the following two fields are initialized automtically during boot */
+ int id;
#define MAX_CGROUP_TYPE_NAMELEN 32
const char *name;
/* link to parent, protected by cgroup_lock() */
- struct cgroupfs_root *root;
+ struct cgroup_root *root;
- /* list of cftype_sets */
- struct list_head cftsets;
+ /*
+ * List of cftypes. Each entry is the first entry of an array
+ * terminated by zero length name.
+ */
+ struct list_head cfts;
- /* base cftypes, automatically [de]registered with subsys itself */
+ /* base cftypes, automatically registered with subsys itself */
struct cftype *base_cftypes;
- struct cftype_set base_cftset;
-
- /* should be defined only by modular subsystems */
- struct module *module;
};
-#define SUBSYS(_x) extern struct cgroup_subsys _x ## _subsys;
-#define IS_SUBSYS_ENABLED(option) IS_BUILTIN(option)
+#define SUBSYS(_x) extern struct cgroup_subsys _x ## _cgrp_subsys;
#include <linux/cgroup_subsys.h>
-#undef IS_SUBSYS_ENABLED
#undef SUBSYS
/**
@@ -661,10 +653,12 @@ struct cgroup_subsys_state *css_parent(struct cgroup_subsys_state *css)
*/
#ifdef CONFIG_PROVE_RCU
extern struct mutex cgroup_mutex;
+extern struct rw_semaphore css_set_rwsem;
#define task_css_set_check(task, __c) \
rcu_dereference_check((task)->cgroups, \
- lockdep_is_held(&(task)->alloc_lock) || \
- lockdep_is_held(&cgroup_mutex) || (__c))
+ lockdep_is_held(&cgroup_mutex) || \
+ lockdep_is_held(&css_set_rwsem) || \
+ ((task)->flags & PF_EXITING) || (__c))
#else
#define task_css_set_check(task, __c) \
rcu_dereference((task)->cgroups)
@@ -837,16 +831,11 @@ void css_task_iter_start(struct cgroup_subsys_state *css,
struct task_struct *css_task_iter_next(struct css_task_iter *it);
void css_task_iter_end(struct css_task_iter *it);
-int css_scan_tasks(struct cgroup_subsys_state *css,
- bool (*test)(struct task_struct *, void *),
- void (*process)(struct task_struct *, void *),
- void *data, struct ptr_heap *heap);
-
int cgroup_attach_task_all(struct task_struct *from, struct task_struct *);
int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from);
-struct cgroup_subsys_state *css_from_dir(struct dentry *dentry,
- struct cgroup_subsys *ss);
+struct cgroup_subsys_state *css_tryget_from_dir(struct dentry *dentry,
+ struct cgroup_subsys *ss);
#else /* !CONFIG_CGROUPS */
@@ -854,7 +843,7 @@ static inline int cgroup_init_early(void) { return 0; }
static inline int cgroup_init(void) { return 0; }
static inline void cgroup_fork(struct task_struct *p) {}
static inline void cgroup_post_fork(struct task_struct *p) {}
-static inline void cgroup_exit(struct task_struct *p, int callbacks) {}
+static inline void cgroup_exit(struct task_struct *p) {}
static inline int cgroupstats_build(struct cgroupstats *stats,
struct dentry *dentry)
diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h
index 7b99d717411d..768fe44e19f0 100644
--- a/include/linux/cgroup_subsys.h
+++ b/include/linux/cgroup_subsys.h
@@ -3,51 +3,51 @@
*
* DO NOT ADD ANY SUBSYSTEM WITHOUT EXPLICIT ACKS FROM CGROUP MAINTAINERS.
*/
-#if IS_SUBSYS_ENABLED(CONFIG_CPUSETS)
+#if IS_ENABLED(CONFIG_CPUSETS)
SUBSYS(cpuset)
#endif
-#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_DEBUG)
+#if IS_ENABLED(CONFIG_CGROUP_DEBUG)
SUBSYS(debug)
#endif
-#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_SCHED)
-SUBSYS(cpu_cgroup)
+#if IS_ENABLED(CONFIG_CGROUP_SCHED)
+SUBSYS(cpu)
#endif
-#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_CPUACCT)
+#if IS_ENABLED(CONFIG_CGROUP_CPUACCT)
SUBSYS(cpuacct)
#endif
-#if IS_SUBSYS_ENABLED(CONFIG_MEMCG)
-SUBSYS(mem_cgroup)
+#if IS_ENABLED(CONFIG_MEMCG)
+SUBSYS(memory)
#endif
-#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_DEVICE)
+#if IS_ENABLED(CONFIG_CGROUP_DEVICE)
SUBSYS(devices)
#endif
-#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_FREEZER)
+#if IS_ENABLED(CONFIG_CGROUP_FREEZER)
SUBSYS(freezer)
#endif
-#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_NET_CLASSID)
+#if IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)
SUBSYS(net_cls)
#endif
-#if IS_SUBSYS_ENABLED(CONFIG_BLK_CGROUP)
+#if IS_ENABLED(CONFIG_BLK_CGROUP)
SUBSYS(blkio)
#endif
-#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_PERF)
-SUBSYS(perf)
+#if IS_ENABLED(CONFIG_CGROUP_PERF)
+SUBSYS(perf_event)
#endif
-#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_NET_PRIO)
+#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
SUBSYS(net_prio)
#endif
-#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_HUGETLB)
+#if IS_ENABLED(CONFIG_CGROUP_HUGETLB)
SUBSYS(hugetlb)
#endif
/*
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 939533da93a7..511917416fb0 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -32,6 +32,7 @@
#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */
struct clk_hw;
+struct dentry;
/**
* struct clk_ops - Callback operations for hardware clocks; these are to
@@ -127,6 +128,12 @@ struct clk_hw;
* separately via calls to .set_parent and .set_rate.
* Returns 0 on success, -EERROR otherwise.
*
+ * @debug_init: Set up type-specific debugfs entries for this clock. This
+ * is called once, after the debugfs directory entry for this
+ * clock has been created. The dentry pointer representing that
+ * directory is provided as an argument. Called with
+ * prepare_lock held. Returns 0 on success, -EERROR otherwise.
+ *
*
* The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow
* implementations to split any work between atomic (enable) and sleepable
@@ -165,6 +172,7 @@ struct clk_ops {
unsigned long (*recalc_accuracy)(struct clk_hw *hw,
unsigned long parent_accuracy);
void (*init)(struct clk_hw *hw);
+ int (*debug_init)(struct clk_hw *hw, struct dentry *dentry);
};
/**
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 0dd91148165e..fb5e097d8f72 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -78,8 +78,22 @@ struct clk_notifier_data {
unsigned long new_rate;
};
+/**
+ * clk_notifier_register: register a clock rate-change notifier callback
+ * @clk: clock whose rate we are interested in
+ * @nb: notifier block with callback function pointer
+ *
+ * ProTip: debugging across notifier chains can be frustrating. Make sure that
+ * your notifier callback function prints a nice big warning in case of
+ * failure.
+ */
int clk_notifier_register(struct clk *clk, struct notifier_block *nb);
+/**
+ * clk_notifier_unregister: unregister a clock rate-change notifier callback
+ * @clk: clock whose rate we are no longer interested in
+ * @nb: notifier block which will be unregistered
+ */
int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);
/**
diff --git a/include/linux/clk/zynq.h b/include/linux/clk/zynq.h
index e062d317ccce..7a5633b71533 100644
--- a/include/linux/clk/zynq.h
+++ b/include/linux/clk/zynq.h
@@ -22,7 +22,7 @@
#include <linux/spinlock.h>
-void zynq_clock_init(void __iomem *slcr);
+void zynq_clock_init(void);
struct clk *clk_register_zynq_pll(const char *name, const char *parent,
void __iomem *pll_ctrl, void __iomem *pll_status, u8 lock_index,
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 03e962e23eaf..81887120395c 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -115,26 +115,46 @@ enum {
{ .notifier_call = fn, .priority = pri }; \
register_cpu_notifier(&fn##_nb); \
}
+
+#define __cpu_notifier(fn, pri) { \
+ static struct notifier_block fn##_nb = \
+ { .notifier_call = fn, .priority = pri }; \
+ __register_cpu_notifier(&fn##_nb); \
+}
#else /* #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */
#define cpu_notifier(fn, pri) do { (void)(fn); } while (0)
+#define __cpu_notifier(fn, pri) do { (void)(fn); } while (0)
#endif /* #else #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */
+
#ifdef CONFIG_HOTPLUG_CPU
extern int register_cpu_notifier(struct notifier_block *nb);
+extern int __register_cpu_notifier(struct notifier_block *nb);
extern void unregister_cpu_notifier(struct notifier_block *nb);
+extern void __unregister_cpu_notifier(struct notifier_block *nb);
#else
#ifndef MODULE
extern int register_cpu_notifier(struct notifier_block *nb);
+extern int __register_cpu_notifier(struct notifier_block *nb);
#else
static inline int register_cpu_notifier(struct notifier_block *nb)
{
return 0;
}
+
+static inline int __register_cpu_notifier(struct notifier_block *nb)
+{
+ return 0;
+}
#endif
static inline void unregister_cpu_notifier(struct notifier_block *nb)
{
}
+
+static inline void __unregister_cpu_notifier(struct notifier_block *nb)
+{
+}
#endif
int cpu_up(unsigned int cpu);
@@ -142,19 +162,32 @@ void notify_cpu_starting(unsigned int cpu);
extern void cpu_maps_update_begin(void);
extern void cpu_maps_update_done(void);
+#define cpu_notifier_register_begin cpu_maps_update_begin
+#define cpu_notifier_register_done cpu_maps_update_done
+
#else /* CONFIG_SMP */
#define cpu_notifier(fn, pri) do { (void)(fn); } while (0)
+#define __cpu_notifier(fn, pri) do { (void)(fn); } while (0)
static inline int register_cpu_notifier(struct notifier_block *nb)
{
return 0;
}
+static inline int __register_cpu_notifier(struct notifier_block *nb)
+{
+ return 0;
+}
+
static inline void unregister_cpu_notifier(struct notifier_block *nb)
{
}
+static inline void __unregister_cpu_notifier(struct notifier_block *nb)
+{
+}
+
static inline void cpu_maps_update_begin(void)
{
}
@@ -163,6 +196,14 @@ static inline void cpu_maps_update_done(void)
{
}
+static inline void cpu_notifier_register_begin(void)
+{
+}
+
+static inline void cpu_notifier_register_done(void)
+{
+}
+
#endif /* CONFIG_SMP */
extern struct bus_type cpu_subsys;
@@ -176,8 +217,11 @@ extern void put_online_cpus(void);
extern void cpu_hotplug_disable(void);
extern void cpu_hotplug_enable(void);
#define hotcpu_notifier(fn, pri) cpu_notifier(fn, pri)
+#define __hotcpu_notifier(fn, pri) __cpu_notifier(fn, pri)
#define register_hotcpu_notifier(nb) register_cpu_notifier(nb)
+#define __register_hotcpu_notifier(nb) __register_cpu_notifier(nb)
#define unregister_hotcpu_notifier(nb) unregister_cpu_notifier(nb)
+#define __unregister_hotcpu_notifier(nb) __unregister_cpu_notifier(nb)
void clear_tasks_mm_cpumask(int cpu);
int cpu_down(unsigned int cpu);
@@ -190,9 +234,12 @@ static inline void cpu_hotplug_done(void) {}
#define cpu_hotplug_disable() do { } while (0)
#define cpu_hotplug_enable() do { } while (0)
#define hotcpu_notifier(fn, pri) do { (void)(fn); } while (0)
+#define __hotcpu_notifier(fn, pri) do { (void)(fn); } while (0)
/* These aren't inline functions due to a GCC bug. */
#define register_hotcpu_notifier(nb) ({ (void)(nb); 0; })
+#define __register_hotcpu_notifier(nb) ({ (void)(nb); 0; })
#define unregister_hotcpu_notifier(nb) ({ (void)(nb); })
+#define __unregister_hotcpu_notifier(nb) ({ (void)(nb); })
#endif /* CONFIG_HOTPLUG_CPU */
#ifdef CONFIG_PM_SLEEP_SMP
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 50fcbb0ac4e7..b0238cba440b 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -119,7 +119,15 @@ struct cpuidle_driver {
#ifdef CONFIG_CPU_IDLE
extern void disable_cpuidle(void);
-extern int cpuidle_idle_call(void);
+
+extern int cpuidle_enabled(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev);
+extern int cpuidle_select(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev);
+extern int cpuidle_enter(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev, int index);
+extern void cpuidle_reflect(struct cpuidle_device *dev, int index);
+
extern int cpuidle_register_driver(struct cpuidle_driver *drv);
extern struct cpuidle_driver *cpuidle_get_driver(void);
extern struct cpuidle_driver *cpuidle_driver_ref(void);
@@ -141,7 +149,16 @@ extern int cpuidle_play_dead(void);
extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev);
#else
static inline void disable_cpuidle(void) { }
-static inline int cpuidle_idle_call(void) { return -ENODEV; }
+static inline int cpuidle_enabled(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{return -ENODEV; }
+static inline int cpuidle_select(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{return -ENODEV; }
+static inline int cpuidle_enter(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev, int index)
+{return -ENODEV; }
+static inline void cpuidle_reflect(struct cpuidle_device *dev, int index) { }
static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
{return -ENODEV; }
static inline struct cpuidle_driver *cpuidle_get_driver(void) {return NULL; }
@@ -163,6 +180,8 @@ static inline int cpuidle_enable_device(struct cpuidle_device *dev)
{return -ENODEV; }
static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
static inline int cpuidle_play_dead(void) {return -ENODEV; }
+static inline struct cpuidle_driver *cpuidle_get_cpu_driver(
+ struct cpuidle_device *dev) {return NULL; }
#endif
#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
index 3fe661fe96d1..b19d3dc2e651 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -87,25 +87,26 @@ extern void rebuild_sched_domains(void);
extern void cpuset_print_task_mems_allowed(struct task_struct *p);
/*
- * get_mems_allowed is required when making decisions involving mems_allowed
- * such as during page allocation. mems_allowed can be updated in parallel
- * and depending on the new value an operation can fail potentially causing
- * process failure. A retry loop with get_mems_allowed and put_mems_allowed
- * prevents these artificial failures.
+ * read_mems_allowed_begin is required when making decisions involving
+ * mems_allowed such as during page allocation. mems_allowed can be updated in
+ * parallel and depending on the new value an operation can fail potentially
+ * causing process failure. A retry loop with read_mems_allowed_begin and
+ * read_mems_allowed_retry prevents these artificial failures.
*/
-static inline unsigned int get_mems_allowed(void)
+static inline unsigned int read_mems_allowed_begin(void)
{
return read_seqcount_begin(&current->mems_allowed_seq);
}
/*
- * If this returns false, the operation that took place after get_mems_allowed
- * may have failed. It is up to the caller to retry the operation if
+ * If this returns true, the operation that took place after
+ * read_mems_allowed_begin may have failed artificially due to a concurrent
+ * update of mems_allowed. It is up to the caller to retry the operation if
* appropriate.
*/
-static inline bool put_mems_allowed(unsigned int seq)
+static inline bool read_mems_allowed_retry(unsigned int seq)
{
- return !read_seqcount_retry(&current->mems_allowed_seq, seq);
+ return read_seqcount_retry(&current->mems_allowed_seq, seq);
}
static inline void set_mems_allowed(nodemask_t nodemask)
@@ -225,14 +226,14 @@ static inline void set_mems_allowed(nodemask_t nodemask)
{
}
-static inline unsigned int get_mems_allowed(void)
+static inline unsigned int read_mems_allowed_begin(void)
{
return 0;
}
-static inline bool put_mems_allowed(unsigned int seq)
+static inline bool read_mems_allowed_retry(unsigned int seq)
{
- return true;
+ return false;
}
#endif /* !CONFIG_CPUSETS */
diff --git a/include/linux/cred.h b/include/linux/cred.h
index 04421e825365..f61d6c8f5ef3 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -66,7 +66,7 @@ extern struct group_info *groups_alloc(int);
extern struct group_info init_groups;
extern void groups_free(struct group_info *);
extern int set_current_groups(struct group_info *);
-extern int set_groups(struct cred *, struct group_info *);
+extern void set_groups(struct cred *, struct group_info *);
extern int groups_search(const struct group_info *, kgid_t);
/* access the groups "array" with this macro */
diff --git a/include/linux/crush/crush.h b/include/linux/crush/crush.h
index acaa5615d634..4fad5f8ee01d 100644
--- a/include/linux/crush/crush.h
+++ b/include/linux/crush/crush.h
@@ -51,6 +51,7 @@ enum {
CRUSH_RULE_SET_CHOOSELEAF_TRIES = 9, /* override chooseleaf_descend_once */
CRUSH_RULE_SET_CHOOSE_LOCAL_TRIES = 10,
CRUSH_RULE_SET_CHOOSE_LOCAL_FALLBACK_TRIES = 11,
+ CRUSH_RULE_SET_CHOOSELEAF_VARY_R = 12
};
/*
@@ -173,6 +174,12 @@ struct crush_map {
* apply to a collision: in that case we will retry as we used
* to. */
__u32 chooseleaf_descend_once;
+
+ /* if non-zero, feed r into chooseleaf, bit-shifted right by (r-1)
+ * bits. a value of 1 is best for new clusters. for legacy clusters
+ * that want to limit reshuffling, a value of 3 or 4 will make the
+ * mappings line up a bit better with previous mappings. */
+ __u8 chooseleaf_vary_r;
};
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index bf72e9ac6de0..3b9bfdb83ba6 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -308,6 +308,7 @@ extern void dentry_update_name_case(struct dentry *, struct qstr *);
/* used for rename() and baskets */
extern void d_move(struct dentry *, struct dentry *);
+extern void d_exchange(struct dentry *, struct dentry *);
extern struct dentry *d_ancestor(struct dentry *, struct dentry *);
/* appendix may either be NULL or be used for transname suffixes */
@@ -429,7 +430,7 @@ static inline unsigned __d_entry_type(const struct dentry *dentry)
return dentry->d_flags & DCACHE_ENTRY_TYPE;
}
-static inline bool d_is_directory(const struct dentry *dentry)
+static inline bool d_can_lookup(const struct dentry *dentry)
{
return __d_entry_type(dentry) == DCACHE_DIRECTORY_TYPE;
}
@@ -439,6 +440,11 @@ static inline bool d_is_autodir(const struct dentry *dentry)
return __d_entry_type(dentry) == DCACHE_AUTODIR_TYPE;
}
+static inline bool d_is_dir(const struct dentry *dentry)
+{
+ return d_can_lookup(dentry) || d_is_autodir(dentry);
+}
+
static inline bool d_is_symlink(const struct dentry *dentry)
{
return __d_entry_type(dentry) == DCACHE_SYMLINK_TYPE;
diff --git a/include/linux/decompress/inflate.h b/include/linux/decompress/inflate.h
index 8c0aef1ba5f5..1d0aedef9822 100644
--- a/include/linux/decompress/inflate.h
+++ b/include/linux/decompress/inflate.h
@@ -1,5 +1,5 @@
-#ifndef INFLATE_H
-#define INFLATE_H
+#ifndef LINUX_DECOMPRESS_INFLATE_H
+#define LINUX_DECOMPRESS_INFLATE_H
int gunzip(unsigned char *inbuf, int len,
int(*fill)(void*, unsigned int),
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index ed419c62dde1..63da56ed9796 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -23,7 +23,6 @@ typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t;
union map_info {
void *ptr;
- unsigned long long ll;
};
/*
@@ -291,7 +290,6 @@ struct dm_target_callbacks {
struct dm_target_io {
struct dm_io *io;
struct dm_target *ti;
- union map_info info;
unsigned target_bio_nr;
struct bio clone;
};
@@ -403,7 +401,6 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid);
struct gendisk *dm_disk(struct mapped_device *md);
int dm_suspended(struct dm_target *ti);
int dm_noflush_suspending(struct dm_target *ti);
-union map_info *dm_get_mapinfo(struct bio *bio);
union map_info *dm_get_rq_mapinfo(struct request *rq);
struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
@@ -466,6 +463,11 @@ struct mapped_device *dm_table_get_md(struct dm_table *t);
void dm_table_event(struct dm_table *t);
/*
+ * Run the queue for request-based targets.
+ */
+void dm_table_run_md_queue_async(struct dm_table *t);
+
+/*
* The device must be suspended before calling this method.
* Returns the previous table, which the caller must destroy.
*/
diff --git a/include/linux/dmar.h b/include/linux/dmar.h
index eccb0c0c6cf6..23c8db129560 100644
--- a/include/linux/dmar.h
+++ b/include/linux/dmar.h
@@ -25,6 +25,8 @@
#include <linux/types.h>
#include <linux/msi.h>
#include <linux/irqreturn.h>
+#include <linux/rwsem.h>
+#include <linux/rcupdate.h>
struct acpi_dmar_header;
@@ -34,13 +36,19 @@ struct acpi_dmar_header;
struct intel_iommu;
+struct dmar_dev_scope {
+ struct device __rcu *dev;
+ u8 bus;
+ u8 devfn;
+};
+
#ifdef CONFIG_DMAR_TABLE
extern struct acpi_table_header *dmar_tbl;
struct dmar_drhd_unit {
struct list_head list; /* list of drhd units */
struct acpi_dmar_header *hdr; /* ACPI header */
u64 reg_base_addr; /* register base address*/
- struct pci_dev **devices; /* target device array */
+ struct dmar_dev_scope *devices;/* target device array */
int devices_cnt; /* target device count */
u16 segment; /* PCI domain */
u8 ignored:1; /* ignore drhd */
@@ -48,33 +56,66 @@ struct dmar_drhd_unit {
struct intel_iommu *iommu;
};
+struct dmar_pci_notify_info {
+ struct pci_dev *dev;
+ unsigned long event;
+ int bus;
+ u16 seg;
+ u16 level;
+ struct acpi_dmar_pci_path path[];
+} __attribute__((packed));
+
+extern struct rw_semaphore dmar_global_lock;
extern struct list_head dmar_drhd_units;
#define for_each_drhd_unit(drhd) \
- list_for_each_entry(drhd, &dmar_drhd_units, list)
+ list_for_each_entry_rcu(drhd, &dmar_drhd_units, list)
#define for_each_active_drhd_unit(drhd) \
- list_for_each_entry(drhd, &dmar_drhd_units, list) \
+ list_for_each_entry_rcu(drhd, &dmar_drhd_units, list) \
if (drhd->ignored) {} else
#define for_each_active_iommu(i, drhd) \
- list_for_each_entry(drhd, &dmar_drhd_units, list) \
+ list_for_each_entry_rcu(drhd, &dmar_drhd_units, list) \
if (i=drhd->iommu, drhd->ignored) {} else
#define for_each_iommu(i, drhd) \
- list_for_each_entry(drhd, &dmar_drhd_units, list) \
+ list_for_each_entry_rcu(drhd, &dmar_drhd_units, list) \
if (i=drhd->iommu, 0) {} else
+static inline bool dmar_rcu_check(void)
+{
+ return rwsem_is_locked(&dmar_global_lock) ||
+ system_state == SYSTEM_BOOTING;
+}
+
+#define dmar_rcu_dereference(p) rcu_dereference_check((p), dmar_rcu_check())
+
+#define for_each_dev_scope(a, c, p, d) \
+ for ((p) = 0; ((d) = (p) < (c) ? dmar_rcu_dereference((a)[(p)].dev) : \
+ NULL, (p) < (c)); (p)++)
+
+#define for_each_active_dev_scope(a, c, p, d) \
+ for_each_dev_scope((a), (c), (p), (d)) if (!(d)) { continue; } else
+
extern int dmar_table_init(void);
extern int dmar_dev_scope_init(void);
extern int dmar_parse_dev_scope(void *start, void *end, int *cnt,
- struct pci_dev ***devices, u16 segment);
-extern void dmar_free_dev_scope(struct pci_dev ***devices, int *cnt);
-
+ struct dmar_dev_scope **devices, u16 segment);
+extern void *dmar_alloc_dev_scope(void *start, void *end, int *cnt);
+extern void dmar_free_dev_scope(struct dmar_dev_scope **devices, int *cnt);
+extern int dmar_insert_dev_scope(struct dmar_pci_notify_info *info,
+ void *start, void*end, u16 segment,
+ struct dmar_dev_scope *devices,
+ int devices_cnt);
+extern int dmar_remove_dev_scope(struct dmar_pci_notify_info *info,
+ u16 segment, struct dmar_dev_scope *devices,
+ int count);
/* Intel IOMMU detection */
extern int detect_intel_iommu(void);
extern int enable_drhd_fault_handling(void);
#else
+struct dmar_pci_notify_info;
static inline int detect_intel_iommu(void)
{
return -ENODEV;
@@ -138,30 +179,9 @@ extern int arch_setup_dmar_msi(unsigned int irq);
#ifdef CONFIG_INTEL_IOMMU
extern int iommu_detected, no_iommu;
-extern struct list_head dmar_rmrr_units;
-struct dmar_rmrr_unit {
- struct list_head list; /* list of rmrr units */
- struct acpi_dmar_header *hdr; /* ACPI header */
- u64 base_address; /* reserved base address*/
- u64 end_address; /* reserved end address */
- struct pci_dev **devices; /* target devices */
- int devices_cnt; /* target device count */
-};
-
-#define for_each_rmrr_units(rmrr) \
- list_for_each_entry(rmrr, &dmar_rmrr_units, list)
-
-struct dmar_atsr_unit {
- struct list_head list; /* list of ATSR units */
- struct acpi_dmar_header *hdr; /* ACPI header */
- struct pci_dev **devices; /* target devices */
- int devices_cnt; /* target device count */
- u8 include_all:1; /* include all ports */
-};
-
-int dmar_parse_rmrr_atsr_dev(void);
extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header);
extern int dmar_parse_one_atsr(struct acpi_dmar_header *header);
+extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info);
extern int intel_iommu_init(void);
#else /* !CONFIG_INTEL_IOMMU: */
static inline int intel_iommu_init(void) { return -ENODEV; }
@@ -173,7 +193,7 @@ static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header)
{
return 0;
}
-static inline int dmar_parse_rmrr_atsr_dev(void)
+static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
{
return 0;
}
diff --git a/include/linux/err.h b/include/linux/err.h
index 15f92e072450..a729120644d5 100644
--- a/include/linux/err.h
+++ b/include/linux/err.h
@@ -2,12 +2,13 @@
#define _LINUX_ERR_H
#include <linux/compiler.h>
+#include <linux/types.h>
#include <asm/errno.h>
/*
* Kernel pointers have redundant information, so we can use a
- * scheme where we can return either an error code or a dentry
+ * scheme where we can return either an error code or a normal
* pointer with the same return value.
*
* This should be a per-architecture thing, to allow different
@@ -29,12 +30,12 @@ static inline long __must_check PTR_ERR(__force const void *ptr)
return (long) ptr;
}
-static inline long __must_check IS_ERR(__force const void *ptr)
+static inline bool __must_check IS_ERR(__force const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}
-static inline long __must_check IS_ERR_OR_NULL(__force const void *ptr)
+static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr)
{
return !ptr || IS_ERR_VALUE((unsigned long)ptr);
}
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index c8e3e7e39c6b..0a114d05f68d 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -183,6 +183,9 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
* hold the RTNL lock.
*
* See the structures used by these operations for further documentation.
+ * Note that for all operations using a structure ending with a zero-
+ * length array, the array is allocated separately in the kernel and
+ * is passed to the driver as an additional parameter.
*
* See &struct net_device and &struct net_device_ops for documentation
* of the generic netdev features interface.
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index da74d878dc4f..df53e1753a76 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -183,7 +183,7 @@ struct f2fs_inode {
__le32 i_pino; /* parent inode number */
__le32 i_namelen; /* file name length */
__u8 i_name[F2FS_NAME_LEN]; /* file name for SPOR */
- __u8 i_reserved2; /* for backward compatibility */
+ __u8 i_dir_level; /* dentry_level for large dir */
struct f2fs_extent i_ext; /* caching a largest extent */
diff --git a/include/linux/filter.h b/include/linux/filter.h
index e568c8ef896b..262dcbb75ffe 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -9,28 +9,81 @@
#include <linux/workqueue.h>
#include <uapi/linux/filter.h>
-#ifdef CONFIG_COMPAT
-/*
- * A struct sock_filter is architecture independent.
+/* Internally used and optimized filter representation with extended
+ * instruction set based on top of classic BPF.
*/
+
+/* instruction classes */
+#define BPF_ALU64 0x07 /* alu mode in double word width */
+
+/* ld/ldx fields */
+#define BPF_DW 0x18 /* double word */
+#define BPF_XADD 0xc0 /* exclusive add */
+
+/* alu/jmp fields */
+#define BPF_MOV 0xb0 /* mov reg to reg */
+#define BPF_ARSH 0xc0 /* sign extending arithmetic shift right */
+
+/* change endianness of a register */
+#define BPF_END 0xd0 /* flags for endianness conversion: */
+#define BPF_TO_LE 0x00 /* convert to little-endian */
+#define BPF_TO_BE 0x08 /* convert to big-endian */
+#define BPF_FROM_LE BPF_TO_LE
+#define BPF_FROM_BE BPF_TO_BE
+
+#define BPF_JNE 0x50 /* jump != */
+#define BPF_JSGT 0x60 /* SGT is signed '>', GT in x86 */
+#define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */
+#define BPF_CALL 0x80 /* function call */
+#define BPF_EXIT 0x90 /* function return */
+
+/* BPF has 10 general purpose 64-bit registers and stack frame. */
+#define MAX_BPF_REG 11
+
+/* BPF program can access up to 512 bytes of stack space. */
+#define MAX_BPF_STACK 512
+
+/* Arg1, context and stack frame pointer register positions. */
+#define ARG1_REG 1
+#define CTX_REG 6
+#define FP_REG 10
+
+struct sock_filter_int {
+ __u8 code; /* opcode */
+ __u8 a_reg:4; /* dest register */
+ __u8 x_reg:4; /* source register */
+ __s16 off; /* signed offset */
+ __s32 imm; /* signed immediate constant */
+};
+
+#ifdef CONFIG_COMPAT
+/* A struct sock_filter is architecture independent. */
struct compat_sock_fprog {
u16 len;
- compat_uptr_t filter; /* struct sock_filter * */
+ compat_uptr_t filter; /* struct sock_filter * */
};
#endif
+struct sock_fprog_kern {
+ u16 len;
+ struct sock_filter *filter;
+};
+
struct sk_buff;
struct sock;
+struct seccomp_data;
-struct sk_filter
-{
+struct sk_filter {
atomic_t refcnt;
- unsigned int len; /* Number of filter blocks */
+ u32 jited:1, /* Is our filter JIT'ed? */
+ len:31; /* Number of filter blocks */
+ struct sock_fprog_kern *orig_prog; /* Original BPF program */
struct rcu_head rcu;
unsigned int (*bpf_func)(const struct sk_buff *skb,
- const struct sock_filter *filter);
+ const struct sock_filter_int *filter);
union {
- struct sock_filter insns[0];
+ struct sock_filter insns[0];
+ struct sock_filter_int insnsi[0];
struct work_struct work;
};
};
@@ -41,25 +94,44 @@ static inline unsigned int sk_filter_size(unsigned int proglen)
offsetof(struct sk_filter, insns[proglen]));
}
-extern int sk_filter(struct sock *sk, struct sk_buff *skb);
-extern unsigned int sk_run_filter(const struct sk_buff *skb,
- const struct sock_filter *filter);
-extern int sk_unattached_filter_create(struct sk_filter **pfp,
- struct sock_fprog *fprog);
-extern void sk_unattached_filter_destroy(struct sk_filter *fp);
-extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
-extern int sk_detach_filter(struct sock *sk);
-extern int sk_chk_filter(struct sock_filter *filter, unsigned int flen);
-extern int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, unsigned len);
-extern void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to);
+#define sk_filter_proglen(fprog) \
+ (fprog->len * sizeof(fprog->filter[0]))
+
+#define SK_RUN_FILTER(filter, ctx) \
+ (*filter->bpf_func)(ctx, filter->insnsi)
+
+int sk_filter(struct sock *sk, struct sk_buff *skb);
+
+u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx,
+ const struct sock_filter_int *insni);
+u32 sk_run_filter_int_skb(const struct sk_buff *ctx,
+ const struct sock_filter_int *insni);
+
+int sk_convert_filter(struct sock_filter *prog, int len,
+ struct sock_filter_int *new_prog, int *new_len);
+
+int sk_unattached_filter_create(struct sk_filter **pfp,
+ struct sock_fprog *fprog);
+void sk_unattached_filter_destroy(struct sk_filter *fp);
+
+int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+int sk_detach_filter(struct sock *sk);
+
+int sk_chk_filter(struct sock_filter *filter, unsigned int flen);
+int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
+ unsigned int len);
+void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to);
+
+void sk_filter_charge(struct sock *sk, struct sk_filter *fp);
+void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp);
#ifdef CONFIG_BPF_JIT
#include <stdarg.h>
#include <linux/linkage.h>
#include <linux/printk.h>
-extern void bpf_jit_compile(struct sk_filter *fp);
-extern void bpf_jit_free(struct sk_filter *fp);
+void bpf_jit_compile(struct sk_filter *fp);
+void bpf_jit_free(struct sk_filter *fp);
static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen,
u32 pass, void *image)
@@ -70,7 +142,6 @@ static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen,
print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_OFFSET,
16, 1, image, proglen, false);
}
-#define SK_RUN_FILTER(FILTER, SKB) (*FILTER->bpf_func)(SKB, FILTER->insns)
#else
#include <linux/slab.h>
static inline void bpf_jit_compile(struct sk_filter *fp)
@@ -80,7 +151,6 @@ static inline void bpf_jit_free(struct sk_filter *fp)
{
kfree(fp);
}
-#define SK_RUN_FILTER(FILTER, SKB) sk_run_filter(SKB, FILTER->insns)
#endif
static inline int bpf_tell_extensions(void)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 23b2a35d712e..81048f9bc783 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -419,6 +419,7 @@ struct address_space {
struct mutex i_mmap_mutex; /* protect tree, count, list */
/* Protected by tree_lock together with the radix tree */
unsigned long nrpages; /* number of total pages */
+ unsigned long nrshadows; /* number of shadow entries */
pgoff_t writeback_index;/* writeback starts here */
const struct address_space_operations *a_ops; /* methods */
unsigned long flags; /* error bits/gfp mask */
@@ -589,6 +590,9 @@ struct inode {
atomic_t i_count;
atomic_t i_dio_count;
atomic_t i_writecount;
+#ifdef CONFIG_IMA
+ atomic_t i_readcount; /* struct files open RO */
+#endif
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct file_lock *i_flock;
struct address_space i_data;
@@ -609,9 +613,6 @@ struct inode {
struct hlist_head i_fsnotify_marks;
#endif
-#ifdef CONFIG_IMA
- atomic_t i_readcount; /* struct files open RO */
-#endif
void *i_private; /* fs or device private pointer */
};
@@ -892,6 +893,7 @@ static inline int file_check_writeable(struct file *filp)
#define FL_SLEEP 128 /* A blocking lock */
#define FL_DOWNGRADE_PENDING 256 /* Lease is being downgraded */
#define FL_UNLOCK_PENDING 512 /* Lease is being broken */
+#define FL_FILE_PVT 1024 /* lock is private to the file */
/*
* Special return value from posix_lock_file() and vfs_lock_file() for
@@ -996,12 +998,12 @@ struct file_lock {
extern void send_sigio(struct fown_struct *fown, int fd, int band);
#ifdef CONFIG_FILE_LOCKING
-extern int fcntl_getlk(struct file *, struct flock __user *);
+extern int fcntl_getlk(struct file *, unsigned int, struct flock __user *);
extern int fcntl_setlk(unsigned int, struct file *, unsigned int,
struct flock __user *);
#if BITS_PER_LONG == 32
-extern int fcntl_getlk64(struct file *, struct flock64 __user *);
+extern int fcntl_getlk64(struct file *, unsigned int, struct flock64 __user *);
extern int fcntl_setlk64(unsigned int, struct file *, unsigned int,
struct flock64 __user *);
#endif
@@ -1016,7 +1018,7 @@ extern struct file_lock * locks_alloc_lock(void);
extern void locks_copy_lock(struct file_lock *, struct file_lock *);
extern void __locks_copy_lock(struct file_lock *, const struct file_lock *);
extern void locks_remove_posix(struct file *, fl_owner_t);
-extern void locks_remove_flock(struct file *);
+extern void locks_remove_file(struct file *);
extern void locks_release_private(struct file_lock *);
extern void posix_test_lock(struct file *, struct file_lock *);
extern int posix_lock_file(struct file *, struct file_lock *, struct file_lock *);
@@ -1034,7 +1036,8 @@ extern int lease_modify(struct file_lock **, int);
extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
#else /* !CONFIG_FILE_LOCKING */
-static inline int fcntl_getlk(struct file *file, struct flock __user *user)
+static inline int fcntl_getlk(struct file *file, unsigned int cmd,
+ struct flock __user *user)
{
return -EINVAL;
}
@@ -1046,7 +1049,8 @@ static inline int fcntl_setlk(unsigned int fd, struct file *file,
}
#if BITS_PER_LONG == 32
-static inline int fcntl_getlk64(struct file *file, struct flock64 __user *user)
+static inline int fcntl_getlk64(struct file *file, unsigned int cmd,
+ struct flock64 __user *user)
{
return -EINVAL;
}
@@ -1087,7 +1091,7 @@ static inline void locks_remove_posix(struct file *filp, fl_owner_t owner)
return;
}
-static inline void locks_remove_flock(struct file *filp)
+static inline void locks_remove_file(struct file *filp)
{
return;
}
@@ -1460,7 +1464,7 @@ extern int vfs_symlink(struct inode *, struct dentry *, const char *);
extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **);
extern int vfs_rmdir(struct inode *, struct dentry *);
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
-extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **);
+extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
/*
* VFS dentry helper functions.
@@ -1571,6 +1575,8 @@ struct inode_operations {
int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
+ int (*rename2) (struct inode *, struct dentry *,
+ struct inode *, struct dentry *, unsigned int);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
@@ -1913,6 +1919,11 @@ extern int current_umask(void);
extern void ihold(struct inode * inode);
extern void iput(struct inode *);
+static inline struct inode *file_inode(struct file *f)
+{
+ return f->f_inode;
+}
+
/* /sys/fs */
extern struct kobject *fs_kobj;
@@ -1922,7 +1933,7 @@ extern struct kobject *fs_kobj;
#define FLOCK_VERIFY_WRITE 2
#ifdef CONFIG_FILE_LOCKING
-extern int locks_mandatory_locked(struct inode *);
+extern int locks_mandatory_locked(struct file *);
extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t);
/*
@@ -1945,10 +1956,10 @@ static inline int mandatory_lock(struct inode *ino)
return IS_MANDLOCK(ino) && __mandatory_lock(ino);
}
-static inline int locks_verify_locked(struct inode *inode)
+static inline int locks_verify_locked(struct file *file)
{
- if (mandatory_lock(inode))
- return locks_mandatory_locked(inode);
+ if (mandatory_lock(file_inode(file)))
+ return locks_mandatory_locked(file);
return 0;
}
@@ -1968,6 +1979,12 @@ static inline int locks_verify_truncate(struct inode *inode,
static inline int break_lease(struct inode *inode, unsigned int mode)
{
+ /*
+ * Since this check is lockless, we must ensure that any refcounts
+ * taken are done before checking inode->i_flock. Otherwise, we could
+ * end up racing with tasks trying to set a new lease on this file.
+ */
+ smp_mb();
if (inode->i_flock)
return __break_lease(inode, mode, FL_LEASE);
return 0;
@@ -2003,7 +2020,7 @@ static inline int break_deleg_wait(struct inode **delegated_inode)
}
#else /* !CONFIG_FILE_LOCKING */
-static inline int locks_mandatory_locked(struct inode *inode)
+static inline int locks_mandatory_locked(struct file *file)
{
return 0;
}
@@ -2025,7 +2042,7 @@ static inline int mandatory_lock(struct inode *inode)
return 0;
}
-static inline int locks_verify_locked(struct inode *inode)
+static inline int locks_verify_locked(struct file *file)
{
return 0;
}
@@ -2299,11 +2316,6 @@ static inline bool execute_ok(struct inode *inode)
return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode);
}
-static inline struct inode *file_inode(struct file *f)
-{
- return f->f_inode;
-}
-
static inline void file_start_write(struct file *file)
{
if (!S_ISREG(file_inode(file)->i_mode))
@@ -2538,6 +2550,9 @@ enum {
/* filesystem does not support filling holes */
DIO_SKIP_HOLES = 0x02,
+
+ /* filesystem can handle aio writes beyond i_size */
+ DIO_ASYNC_EXTEND = 0x04,
};
void dio_end_io(struct bio *bio, int error);
@@ -2560,6 +2575,9 @@ static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb,
void inode_dio_wait(struct inode *inode);
void inode_dio_done(struct inode *inode);
+extern void inode_set_flags(struct inode *inode, unsigned int flags,
+ unsigned int mask);
+
extern const struct file_operations generic_ro_fops;
#define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m))
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 64cf3ef50696..fc7718c6bd3e 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -178,7 +178,7 @@ struct fsnotify_group {
struct fanotify_group_private_data {
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
/* allows a group to block waiting for a userspace response */
- struct mutex access_mutex;
+ spinlock_t access_lock;
struct list_head access_list;
wait_queue_head_t access_waitq;
atomic_t bypass_perm;
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index f4233b195dab..9212b017bc72 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -92,6 +92,7 @@ typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
* STUB - The ftrace_ops is just a place holder.
* INITIALIZED - The ftrace_ops has already been initialized (first use time
* register_ftrace_function() is called, it will initialized the ops)
+ * DELETED - The ops are being deleted, do not let them be registered again.
*/
enum {
FTRACE_OPS_FL_ENABLED = 1 << 0,
@@ -103,13 +104,26 @@ enum {
FTRACE_OPS_FL_RECURSION_SAFE = 1 << 6,
FTRACE_OPS_FL_STUB = 1 << 7,
FTRACE_OPS_FL_INITIALIZED = 1 << 8,
+ FTRACE_OPS_FL_DELETED = 1 << 9,
};
+/*
+ * 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.
+ *
+ * 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
+ * ftrace_ops must perform a schedule_on_each_cpu() before freeing it.
+ */
struct ftrace_ops {
ftrace_func_t func;
struct ftrace_ops *next;
unsigned long flags;
int __percpu *disabled;
+ void *private;
#ifdef CONFIG_DYNAMIC_FTRACE
struct ftrace_hash *notrace_hash;
struct ftrace_hash *filter_hash;
@@ -285,7 +299,7 @@ extern void
unregister_ftrace_function_probe_func(char *glob, struct ftrace_probe_ops *ops);
extern void unregister_ftrace_function_probe_all(char *glob);
-extern int ftrace_text_reserved(void *start, void *end);
+extern int ftrace_text_reserved(const void *start, const void *end);
extern int ftrace_nr_registered_ops(void);
@@ -316,12 +330,9 @@ enum {
#define FTRACE_REF_MAX ((1UL << 29) - 1)
struct dyn_ftrace {
- union {
- unsigned long ip; /* address of mcount call-site */
- struct dyn_ftrace *freelist;
- };
+ unsigned long ip; /* address of mcount call-site */
unsigned long flags;
- struct dyn_arch_ftrace arch;
+ struct dyn_arch_ftrace arch;
};
int ftrace_force_update(void);
@@ -409,7 +420,7 @@ ftrace_set_early_filter(struct ftrace_ops *ops, char *buf, int enable);
/* defined in arch */
extern int ftrace_ip_converted(unsigned long ip);
-extern int ftrace_dyn_arch_init(void *data);
+extern int ftrace_dyn_arch_init(void);
extern void ftrace_replace_code(int enable);
extern int ftrace_update_ftrace_func(ftrace_func_t func);
extern void ftrace_caller(void);
@@ -541,7 +552,7 @@ static inline __init int unregister_ftrace_command(char *cmd_name)
{
return -EINVAL;
}
-static inline int ftrace_text_reserved(void *start, void *end)
+static inline int ftrace_text_reserved(const void *start, const void *end)
{
return 0;
}
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h
index 4cdb3a17bcb5..cdc30111d2f8 100644
--- a/include/linux/ftrace_event.h
+++ b/include/linux/ftrace_event.h
@@ -163,6 +163,8 @@ void trace_current_buffer_discard_commit(struct ring_buffer *buffer,
void tracing_record_cmdline(struct task_struct *tsk);
+int ftrace_output_call(struct trace_iterator *iter, char *name, char *fmt, ...);
+
struct event_filter;
enum trace_reg {
@@ -197,6 +199,32 @@ struct ftrace_event_class {
extern int ftrace_event_reg(struct ftrace_event_call *event,
enum trace_reg type, void *data);
+int ftrace_output_event(struct trace_iterator *iter, struct ftrace_event_call *event,
+ char *fmt, ...);
+
+int ftrace_event_define_field(struct ftrace_event_call *call,
+ char *type, int len, char *item, int offset,
+ int field_size, int sign, int filter);
+
+struct ftrace_event_buffer {
+ struct ring_buffer *buffer;
+ struct ring_buffer_event *event;
+ struct ftrace_event_file *ftrace_file;
+ void *entry;
+ unsigned long flags;
+ int pc;
+};
+
+void *ftrace_event_buffer_reserve(struct ftrace_event_buffer *fbuffer,
+ struct ftrace_event_file *ftrace_file,
+ unsigned long len);
+
+void ftrace_event_buffer_commit(struct ftrace_event_buffer *fbuffer);
+
+int ftrace_event_define_field(struct ftrace_event_call *call,
+ char *type, int len, char *item, int offset,
+ int field_size, int sign, int filter);
+
enum {
TRACE_EVENT_FL_FILTERED_BIT,
TRACE_EVENT_FL_CAP_ANY_BIT,
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index b581b13d29d9..85aa5d0b9357 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -3,7 +3,7 @@
#include <linux/errno.h>
-/* see Documentation/gpio.txt */
+/* see Documentation/gpio/gpio-legacy.txt */
/* make these flag values available regardless of GPIO kconfig options */
#define GPIOF_DIR_OUT (0 << 0)
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 7a8144fef406..bed128e8f4b1 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -5,7 +5,6 @@
#include <linux/kernel.h>
struct device;
-struct gpio_chip;
/**
* Opaque descriptor for a GPIO. These are obtained using gpiod_get() and are
@@ -36,6 +35,7 @@ void devm_gpiod_put(struct device *dev, struct gpio_desc *desc);
int gpiod_get_direction(const struct gpio_desc *desc);
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);
+int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
/* Value get/set from non-sleeping context */
int gpiod_get_value(const struct gpio_desc *desc);
@@ -59,7 +59,6 @@ int gpiod_to_irq(const struct gpio_desc *desc);
/* Convert between the old gpio_ and new gpiod_ interfaces */
struct gpio_desc *gpio_to_desc(unsigned gpio);
int desc_to_gpio(const struct gpio_desc *desc);
-struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc);
#else /* CONFIG_GPIOLIB */
@@ -121,6 +120,12 @@ static inline int gpiod_direction_output(struct gpio_desc *desc, int value)
WARN_ON(1);
return -ENOSYS;
}
+static inline int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
+{
+ /* GPIO can never have been requested */
+ WARN_ON(1);
+ return -ENOSYS;
+}
static inline int gpiod_get_value(const struct gpio_desc *desc)
@@ -207,12 +212,6 @@ static inline int desc_to_gpio(const struct gpio_desc *desc)
WARN_ON(1);
return -EINVAL;
}
-static inline struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc)
-{
- /* GPIO can never have been requested */
- WARN_ON(1);
- return ERR_PTR(-ENODEV);
-}
#endif /* CONFIG_GPIOLIB */
diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
index a3e181e09636..1827b43966d9 100644
--- a/include/linux/gpio/driver.h
+++ b/include/linux/gpio/driver.h
@@ -3,6 +3,9 @@
#include <linux/types.h>
#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
struct device;
struct gpio_desc;
@@ -10,6 +13,8 @@ struct of_phandle_args;
struct device_node;
struct seq_file;
+#ifdef CONFIG_GPIOLIB
+
/**
* struct gpio_chip - abstract a GPIO controller
* @label: for diagnostics
@@ -95,6 +100,18 @@ struct gpio_chip {
bool can_sleep;
bool exported;
+#ifdef CONFIG_GPIOLIB_IRQCHIP
+ /*
+ * With CONFIG_GPIO_IRQCHIP we get an irqchip inside the gpiolib
+ * to handle IRQs for most practical cases.
+ */
+ struct irq_chip *irqchip;
+ struct irq_domain *irqdomain;
+ unsigned int irq_base;
+ irq_flow_handler_t irq_handler;
+ unsigned int irq_default_type;
+#endif
+
#if defined(CONFIG_OF_GPIO)
/*
* If CONFIG_OF is enabled, then all GPIO controllers described in the
@@ -129,6 +146,11 @@ extern struct gpio_chip *gpiochip_find(void *data,
int gpiod_lock_as_irq(struct gpio_desc *desc);
void gpiod_unlock_as_irq(struct gpio_desc *desc);
+struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc);
+
+struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
+ u16 hwnum);
+
enum gpio_lookup_flags {
GPIO_ACTIVE_HIGH = (0 << 0),
GPIO_ACTIVE_LOW = (1 << 0),
@@ -183,4 +205,30 @@ struct gpiod_lookup_table {
void gpiod_add_lookup_table(struct gpiod_lookup_table *table);
+#ifdef CONFIG_GPIOLIB_IRQCHIP
+
+void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
+ struct irq_chip *irqchip,
+ int parent_irq,
+ irq_flow_handler_t parent_handler);
+
+int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
+ struct irq_chip *irqchip,
+ unsigned int first_irq,
+ irq_flow_handler_t handler,
+ unsigned int type);
+
+#endif /* CONFIG_GPIO_IRQCHIP */
+
+#else /* CONFIG_GPIOLIB */
+
+static inline struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc)
+{
+ /* GPIO can never have been requested */
+ WARN_ON(1);
+ return ERR_PTR(-ENODEV);
+}
+
+#endif /* CONFIG_GPIOLIB */
+
#endif
diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h
index b914ca3f57ba..b70cfd7ff29c 100644
--- a/include/linux/hid-sensor-hub.h
+++ b/include/linux/hid-sensor-hub.h
@@ -51,13 +51,15 @@ struct hid_sensor_hub_attribute_info {
* @hdev: Stores the hid instance.
* @vendor_id: Vendor id of hub device.
* @product_id: Product id of hub device.
- * @ref_cnt: Number of MFD clients have opened this device
+ * @start_collection_index: Starting index for a phy type collection
+ * @end_collection_index: Last index for a phy type collection
*/
struct hid_sensor_hub_device {
struct hid_device *hdev;
u32 vendor_id;
u32 product_id;
- int ref_cnt;
+ int start_collection_index;
+ int end_collection_index;
};
/**
@@ -218,4 +220,7 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st,
int *val1, int *val2);
+int hid_sensor_get_usage_index(struct hid_sensor_hub_device *hsdev,
+ u32 report_id, int field_index, u32 usage_id);
+
#endif
diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h
index 537161a997ab..14ead9e8eda8 100644
--- a/include/linux/hid-sensor-ids.h
+++ b/include/linux/hid-sensor-ids.h
@@ -140,15 +140,15 @@
#define HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS 0x1000
/* Power state enumerations */
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_UNDEFINED_ENUM 0x00
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM 0x01
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_D1_LOW_POWER_ENUM 0x02
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_D2_STANDBY_WITH_WAKE_ENUM 0x03
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_D3_SLEEP_WITH_WAKE_ENUM 0x04
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM 0x05
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_UNDEFINED_ENUM 0x200850
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM 0x200851
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_D1_LOW_POWER_ENUM 0x200852
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_D2_STANDBY_WITH_WAKE_ENUM 0x200853
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_D3_SLEEP_WITH_WAKE_ENUM 0x200854
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM 0x200855
/* Report State enumerations */
-#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM 0x00
-#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM 0x01
+#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM 0x200840
+#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM 0x200841
#endif
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 31b9d299ef6c..720e3a10608c 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -201,6 +201,7 @@ struct hid_item {
#define HID_GD_VBRZ 0x00010045
#define HID_GD_VNO 0x00010046
#define HID_GD_FEATURE 0x00010047
+#define HID_GD_SYSTEM_CONTROL 0x00010080
#define HID_GD_UP 0x00010090
#define HID_GD_DOWN 0x00010091
#define HID_GD_RIGHT 0x00010092
@@ -208,6 +209,8 @@ struct hid_item {
#define HID_DC_BATTERYSTRENGTH 0x00060020
+#define HID_CP_CONSUMER_CONTROL 0x000c0001
+
#define HID_DG_DIGITIZER 0x000d0001
#define HID_DG_PEN 0x000d0002
#define HID_DG_LIGHTPEN 0x000d0003
@@ -287,6 +290,8 @@ struct hid_item {
#define HID_QUIRK_NO_EMPTY_INPUT 0x00000100
#define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
+#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID 0x00020000
+#define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP 0x00040000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000
#define HID_QUIRK_NO_IGNORE 0x40000000
@@ -508,12 +513,6 @@ struct hid_device { /* device report descriptor */
struct hid_usage *, __s32);
void (*hiddev_report_event) (struct hid_device *, struct hid_report *);
- /* handler for raw input (Get_Report) data, used by hidraw */
- int (*hid_get_raw_report) (struct hid_device *, unsigned char, __u8 *, size_t, unsigned char);
-
- /* handler for raw output data, used by hidraw */
- int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char);
-
/* debugging support via debugfs */
unsigned short debug;
struct dentry *debug_dir;
@@ -675,11 +674,12 @@ struct hid_driver {
* @stop: called on remove
* @open: called by input layer on open
* @close: called by input layer on close
- * @hidinput_input_event: event input event (e.g. ff or leds)
* @parse: this method is called only once to parse the device data,
* shouldn't allocate anything to not leak memory
* @request: send report request to device (e.g. feature report)
* @wait: wait for buffered io to complete (send/recv reports)
+ * @raw_request: send raw report request to device (e.g. feature report)
+ * @output_report: send output report to device
* @idle: send idle request to device
*/
struct hid_ll_driver {
@@ -691,17 +691,20 @@ struct hid_ll_driver {
int (*power)(struct hid_device *hdev, int level);
- int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
- unsigned int code, int value);
-
int (*parse)(struct hid_device *hdev);
void (*request)(struct hid_device *hdev,
struct hid_report *report, int reqtype);
int (*wait)(struct hid_device *hdev);
- int (*idle)(struct hid_device *hdev, int report, int idle, int reqtype);
+ int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype,
+ int reqtype);
+
+ int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len);
+
+ int (*idle)(struct hid_device *hdev, int report, int idle, int reqtype);
};
#define PM_HINT_FULLON 1<<5
@@ -752,6 +755,7 @@ struct hid_field *hidinput_get_led_field(struct hid_device *hid);
unsigned int hidinput_count_leds(struct hid_device *hid);
__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code);
void hid_output_report(struct hid_report *report, __u8 *data);
+void __hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype);
u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags);
struct hid_device *hid_allocate_device(void);
struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
@@ -964,7 +968,55 @@ static inline void hid_hw_request(struct hid_device *hdev,
struct hid_report *report, int reqtype)
{
if (hdev->ll_driver->request)
- hdev->ll_driver->request(hdev, report, reqtype);
+ return hdev->ll_driver->request(hdev, report, reqtype);
+
+ __hid_request(hdev, report, reqtype);
+}
+
+/**
+ * hid_hw_raw_request - send report request to device
+ *
+ * @hdev: hid device
+ * @reportnum: report ID
+ * @buf: in/out data to transfer
+ * @len: length of buf
+ * @rtype: HID report type
+ * @reqtype: HID_REQ_GET_REPORT or HID_REQ_SET_REPORT
+ *
+ * @return: count of data transfered, negative if error
+ *
+ * Same behavior as hid_hw_request, but with raw buffers instead.
+ */
+static inline int hid_hw_raw_request(struct hid_device *hdev,
+ unsigned char reportnum, __u8 *buf,
+ size_t len, unsigned char rtype, int reqtype)
+{
+ if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf)
+ return -EINVAL;
+
+ return hdev->ll_driver->raw_request(hdev, reportnum, buf, len,
+ rtype, reqtype);
+}
+
+/**
+ * hid_hw_output_report - send output report to device
+ *
+ * @hdev: hid device
+ * @buf: raw data to transfer
+ * @len: length of buf
+ *
+ * @return: count of data transfered, negative if error
+ */
+static inline int hid_hw_output_report(struct hid_device *hdev, __u8 *buf,
+ size_t len)
+{
+ if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf)
+ return -EINVAL;
+
+ if (hdev->ll_driver->output_report)
+ return hdev->ll_driver->output_report(hdev, buf, len);
+
+ return -ENOSYS;
}
/**
diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h
index 0dca785288cf..39bfd5b89077 100644
--- a/include/linux/hsi/hsi.h
+++ b/include/linux/hsi/hsi.h
@@ -178,7 +178,7 @@ static inline void hsi_unregister_client_driver(struct hsi_client_driver *drv)
* @complete: Transfer completion callback
* @destructor: Destructor to free resources when flushing
* @status: Status of the transfer when completed
- * @actual_len: Actual length of data transfered on completion
+ * @actual_len: Actual length of data transferred on completion
* @channel: Channel were to TX/RX the message
* @ttype: Transfer type (TX if set, RX otherwise)
* @break_frame: if true HSI will send/receive a break frame. Data buffers are
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 8c43cc469d78..5b337cf8fb86 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -6,6 +6,8 @@
#include <linux/fs.h>
#include <linux/hugetlb_inline.h>
#include <linux/cgroup.h>
+#include <linux/list.h>
+#include <linux/kref.h>
struct ctl_table;
struct user_struct;
@@ -23,6 +25,14 @@ struct hugepage_subpool {
long max_hpages, used_hpages;
};
+struct resv_map {
+ struct kref refs;
+ spinlock_t lock;
+ struct list_head regions;
+};
+extern struct resv_map *resv_map_alloc(void);
+void resv_map_release(struct kref *ref);
+
extern spinlock_t hugetlb_lock;
extern int hugetlb_max_hstate __read_mostly;
#define for_each_hstate(h) \
diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h
index 787bba3bf552..0129f89cf98d 100644
--- a/include/linux/hugetlb_cgroup.h
+++ b/include/linux/hugetlb_cgroup.h
@@ -49,7 +49,7 @@ int set_hugetlb_cgroup(struct page *page, struct hugetlb_cgroup *h_cg)
static inline bool hugetlb_cgroup_disabled(void)
{
- if (hugetlb_subsys.disabled)
+ if (hugetlb_cgrp_subsys.disabled)
return true;
return false;
}
diff --git a/include/linux/i2c/adp5588.h b/include/linux/i2c/adp5588.h
index d8341cb47b60..c2153049cfbd 100644
--- a/include/linux/i2c/adp5588.h
+++ b/include/linux/i2c/adp5588.h
@@ -161,10 +161,10 @@ struct adp5588_gpio_platform_data {
unsigned irq_base; /* interrupt base # */
unsigned pullup_dis_mask; /* Pull-Up Disable Mask */
int (*setup)(struct i2c_client *client,
- int gpio, unsigned ngpio,
+ unsigned gpio, unsigned ngpio,
void *context);
int (*teardown)(struct i2c_client *client,
- int gpio, unsigned ngpio,
+ unsigned gpio, unsigned ngpio,
void *context);
void *context;
};
diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h
index ade1c06d4ceb..d2b16704624c 100644
--- a/include/linux/i2c/twl.h
+++ b/include/linux/i2c/twl.h
@@ -195,6 +195,18 @@ static inline int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg) {
return twl_i2c_read(mod_no, val, reg, 1);
}
+static inline int twl_i2c_write_u16(u8 mod_no, u16 val, u8 reg) {
+ val = cpu_to_le16(val);
+ return twl_i2c_write(mod_no, (u8*) &val, reg, 2);
+}
+
+static inline int twl_i2c_read_u16(u8 mod_no, u16 *val, u8 reg) {
+ int ret;
+ ret = twl_i2c_read(mod_no, (u8*) val, reg, 2);
+ *val = le16_to_cpu(*val);
+ return ret;
+}
+
int twl_get_type(void);
int twl_get_version(void);
int twl_get_hfclk_rate(void);
diff --git a/include/linux/i2c/twl4030-madc.h b/include/linux/i2c/twl4030-madc.h
index 01f595107048..1c0134dd3271 100644
--- a/include/linux/i2c/twl4030-madc.h
+++ b/include/linux/i2c/twl4030-madc.h
@@ -44,7 +44,7 @@ struct twl4030_madc_conversion_method {
struct twl4030_madc_request {
unsigned long channels;
- u16 do_avg;
+ bool do_avg;
u16 method;
u16 type;
bool active;
diff --git a/include/linux/idr.h b/include/linux/idr.h
index 9c95d210458b..f669585c4fc5 100644
--- a/include/linux/idr.h
+++ b/include/linux/idr.h
@@ -82,7 +82,6 @@ int idr_for_each(struct idr *idp,
void *idr_get_next(struct idr *idp, int *nextid);
void *idr_replace(struct idr *idp, void *ptr, int id);
void idr_remove(struct idr *idp, int id);
-void idr_free(struct idr *idp, int id);
void idr_destroy(struct idr *idp);
void idr_init(struct idr *idp);
bool idr_is_empty(struct idr *idp);
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index e526a8cecb70..f194ccb8539c 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -154,6 +154,10 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
802.11e clarifies the figure in section 7.1.2. The frame body is
up to 2304 octets long (maximum MSDU size) plus any crypt overhead. */
#define IEEE80211_MAX_DATA_LEN 2304
+/* 802.11ad extends maximum MSDU size for DMG (freq > 40Ghz) networks
+ * to 7920 bytes, see 8.2.3 General frame format
+ */
+#define IEEE80211_MAX_DATA_LEN_DMG 7920
/* 30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS */
#define IEEE80211_MAX_FRAME_LEN 2352
@@ -597,6 +601,20 @@ static inline int ieee80211_is_qos_nullfunc(__le16 fc)
}
/**
+ * ieee80211_is_bufferable_mmpdu - check if frame is bufferable MMPDU
+ * @fc: frame control field in little-endian byteorder
+ */
+static inline bool ieee80211_is_bufferable_mmpdu(__le16 fc)
+{
+ /* IEEE 802.11-2012, definition of "bufferable management frame";
+ * note that this ignores the IBSS special case. */
+ return ieee80211_is_mgmt(fc) &&
+ (ieee80211_is_action(fc) ||
+ ieee80211_is_disassoc(fc) ||
+ ieee80211_is_deauth(fc));
+}
+
+/**
* ieee80211_is_first_frag - check if IEEE80211_SCTL_FRAG is not set
* @seq_ctrl: frame sequence control bytes in little-endian byteorder
*/
@@ -1636,51 +1654,22 @@ enum ieee80211_reasoncode {
enum ieee80211_eid {
WLAN_EID_SSID = 0,
WLAN_EID_SUPP_RATES = 1,
- WLAN_EID_FH_PARAMS = 2,
+ WLAN_EID_FH_PARAMS = 2, /* reserved now */
WLAN_EID_DS_PARAMS = 3,
WLAN_EID_CF_PARAMS = 4,
WLAN_EID_TIM = 5,
WLAN_EID_IBSS_PARAMS = 6,
- WLAN_EID_CHALLENGE = 16,
-
WLAN_EID_COUNTRY = 7,
WLAN_EID_HP_PARAMS = 8,
WLAN_EID_HP_TABLE = 9,
WLAN_EID_REQUEST = 10,
-
WLAN_EID_QBSS_LOAD = 11,
WLAN_EID_EDCA_PARAM_SET = 12,
WLAN_EID_TSPEC = 13,
WLAN_EID_TCLAS = 14,
WLAN_EID_SCHEDULE = 15,
- WLAN_EID_TS_DELAY = 43,
- WLAN_EID_TCLAS_PROCESSING = 44,
- WLAN_EID_QOS_CAPA = 46,
- /* 802.11z */
- WLAN_EID_LINK_ID = 101,
- /* 802.11s */
- WLAN_EID_MESH_CONFIG = 113,
- WLAN_EID_MESH_ID = 114,
- WLAN_EID_LINK_METRIC_REPORT = 115,
- WLAN_EID_CONGESTION_NOTIFICATION = 116,
- WLAN_EID_PEER_MGMT = 117,
- WLAN_EID_CHAN_SWITCH_PARAM = 118,
- WLAN_EID_MESH_AWAKE_WINDOW = 119,
- WLAN_EID_BEACON_TIMING = 120,
- WLAN_EID_MCCAOP_SETUP_REQ = 121,
- WLAN_EID_MCCAOP_SETUP_RESP = 122,
- WLAN_EID_MCCAOP_ADVERT = 123,
- WLAN_EID_MCCAOP_TEARDOWN = 124,
- WLAN_EID_GANN = 125,
- WLAN_EID_RANN = 126,
- WLAN_EID_PREQ = 130,
- WLAN_EID_PREP = 131,
- WLAN_EID_PERR = 132,
- WLAN_EID_PXU = 137,
- WLAN_EID_PXUC = 138,
- WLAN_EID_AUTH_MESH_PEER_EXCH = 139,
- WLAN_EID_MIC = 140,
-
+ WLAN_EID_CHALLENGE = 16,
+ /* 17-31 reserved for challenge text extension */
WLAN_EID_PWR_CONSTRAINT = 32,
WLAN_EID_PWR_CAPABILITY = 33,
WLAN_EID_TPC_REQUEST = 34,
@@ -1691,66 +1680,114 @@ enum ieee80211_eid {
WLAN_EID_MEASURE_REPORT = 39,
WLAN_EID_QUIET = 40,
WLAN_EID_IBSS_DFS = 41,
-
WLAN_EID_ERP_INFO = 42,
- WLAN_EID_EXT_SUPP_RATES = 50,
-
+ WLAN_EID_TS_DELAY = 43,
+ WLAN_EID_TCLAS_PROCESSING = 44,
WLAN_EID_HT_CAPABILITY = 45,
- WLAN_EID_HT_OPERATION = 61,
- WLAN_EID_SECONDARY_CHANNEL_OFFSET = 62,
-
+ WLAN_EID_QOS_CAPA = 46,
+ /* 47 reserved for Broadcom */
WLAN_EID_RSN = 48,
- WLAN_EID_MMIE = 76,
- WLAN_EID_VENDOR_SPECIFIC = 221,
- WLAN_EID_QOS_PARAMETER = 222,
-
+ WLAN_EID_802_15_COEX = 49,
+ WLAN_EID_EXT_SUPP_RATES = 50,
WLAN_EID_AP_CHAN_REPORT = 51,
WLAN_EID_NEIGHBOR_REPORT = 52,
WLAN_EID_RCPI = 53,
+ WLAN_EID_MOBILITY_DOMAIN = 54,
+ WLAN_EID_FAST_BSS_TRANSITION = 55,
+ WLAN_EID_TIMEOUT_INTERVAL = 56,
+ WLAN_EID_RIC_DATA = 57,
+ WLAN_EID_DSE_REGISTERED_LOCATION = 58,
+ WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59,
+ WLAN_EID_EXT_CHANSWITCH_ANN = 60,
+ WLAN_EID_HT_OPERATION = 61,
+ WLAN_EID_SECONDARY_CHANNEL_OFFSET = 62,
WLAN_EID_BSS_AVG_ACCESS_DELAY = 63,
WLAN_EID_ANTENNA_INFO = 64,
WLAN_EID_RSNI = 65,
WLAN_EID_MEASUREMENT_PILOT_TX_INFO = 66,
WLAN_EID_BSS_AVAILABLE_CAPACITY = 67,
WLAN_EID_BSS_AC_ACCESS_DELAY = 68,
+ WLAN_EID_TIME_ADVERTISEMENT = 69,
WLAN_EID_RRM_ENABLED_CAPABILITIES = 70,
WLAN_EID_MULTIPLE_BSSID = 71,
WLAN_EID_BSS_COEX_2040 = 72,
WLAN_EID_OVERLAP_BSS_SCAN_PARAM = 74,
- WLAN_EID_EXT_CAPABILITY = 127,
-
- WLAN_EID_MOBILITY_DOMAIN = 54,
- WLAN_EID_FAST_BSS_TRANSITION = 55,
- WLAN_EID_TIMEOUT_INTERVAL = 56,
- WLAN_EID_RIC_DATA = 57,
WLAN_EID_RIC_DESCRIPTOR = 75,
-
- WLAN_EID_DSE_REGISTERED_LOCATION = 58,
- WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59,
- WLAN_EID_EXT_CHANSWITCH_ANN = 60,
-
- WLAN_EID_VHT_CAPABILITY = 191,
- WLAN_EID_VHT_OPERATION = 192,
- WLAN_EID_OPMODE_NOTIF = 199,
- WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
- WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
- WLAN_EID_EXTENDED_BSS_LOAD = 193,
- WLAN_EID_VHT_TX_POWER_ENVELOPE = 195,
- WLAN_EID_AID = 197,
- WLAN_EID_QUIET_CHANNEL = 198,
-
- /* 802.11ad */
+ WLAN_EID_MMIE = 76,
+ WLAN_EID_ASSOC_COMEBACK_TIME = 77,
+ WLAN_EID_EVENT_REQUEST = 78,
+ WLAN_EID_EVENT_REPORT = 79,
+ WLAN_EID_DIAGNOSTIC_REQUEST = 80,
+ WLAN_EID_DIAGNOSTIC_REPORT = 81,
+ WLAN_EID_LOCATION_PARAMS = 82,
WLAN_EID_NON_TX_BSSID_CAP = 83,
+ WLAN_EID_SSID_LIST = 84,
+ WLAN_EID_MULTI_BSSID_IDX = 85,
+ WLAN_EID_FMS_DESCRIPTOR = 86,
+ WLAN_EID_FMS_REQUEST = 87,
+ WLAN_EID_FMS_RESPONSE = 88,
+ WLAN_EID_QOS_TRAFFIC_CAPA = 89,
+ WLAN_EID_BSS_MAX_IDLE_PERIOD = 90,
+ WLAN_EID_TSF_REQUEST = 91,
+ WLAN_EID_TSF_RESPOSNE = 92,
+ WLAN_EID_WNM_SLEEP_MODE = 93,
+ WLAN_EID_TIM_BCAST_REQ = 94,
+ WLAN_EID_TIM_BCAST_RESP = 95,
+ WLAN_EID_COLL_IF_REPORT = 96,
+ WLAN_EID_CHANNEL_USAGE = 97,
+ WLAN_EID_TIME_ZONE = 98,
+ WLAN_EID_DMS_REQUEST = 99,
+ WLAN_EID_DMS_RESPONSE = 100,
+ WLAN_EID_LINK_ID = 101,
+ WLAN_EID_WAKEUP_SCHEDUL = 102,
+ /* 103 reserved */
+ WLAN_EID_CHAN_SWITCH_TIMING = 104,
+ WLAN_EID_PTI_CONTROL = 105,
+ WLAN_EID_PU_BUFFER_STATUS = 106,
+ WLAN_EID_INTERWORKING = 107,
+ WLAN_EID_ADVERTISEMENT_PROTOCOL = 108,
+ WLAN_EID_EXPEDITED_BW_REQ = 109,
+ WLAN_EID_QOS_MAP_SET = 110,
+ WLAN_EID_ROAMING_CONSORTIUM = 111,
+ WLAN_EID_EMERGENCY_ALERT = 112,
+ WLAN_EID_MESH_CONFIG = 113,
+ WLAN_EID_MESH_ID = 114,
+ WLAN_EID_LINK_METRIC_REPORT = 115,
+ WLAN_EID_CONGESTION_NOTIFICATION = 116,
+ WLAN_EID_PEER_MGMT = 117,
+ WLAN_EID_CHAN_SWITCH_PARAM = 118,
+ WLAN_EID_MESH_AWAKE_WINDOW = 119,
+ WLAN_EID_BEACON_TIMING = 120,
+ WLAN_EID_MCCAOP_SETUP_REQ = 121,
+ WLAN_EID_MCCAOP_SETUP_RESP = 122,
+ WLAN_EID_MCCAOP_ADVERT = 123,
+ WLAN_EID_MCCAOP_TEARDOWN = 124,
+ WLAN_EID_GANN = 125,
+ WLAN_EID_RANN = 126,
+ WLAN_EID_EXT_CAPABILITY = 127,
+ /* 128, 129 reserved for Agere */
+ WLAN_EID_PREQ = 130,
+ WLAN_EID_PREP = 131,
+ WLAN_EID_PERR = 132,
+ /* 133-136 reserved for Cisco */
+ WLAN_EID_PXU = 137,
+ WLAN_EID_PXUC = 138,
+ WLAN_EID_AUTH_MESH_PEER_EXCH = 139,
+ WLAN_EID_MIC = 140,
+ WLAN_EID_DESTINATION_URI = 141,
+ WLAN_EID_UAPSD_COEX = 142,
WLAN_EID_WAKEUP_SCHEDULE = 143,
WLAN_EID_EXT_SCHEDULE = 144,
WLAN_EID_STA_AVAILABILITY = 145,
WLAN_EID_DMG_TSPEC = 146,
WLAN_EID_DMG_AT = 147,
WLAN_EID_DMG_CAP = 148,
+ /* 149-150 reserved for Cisco */
WLAN_EID_DMG_OPERATION = 151,
WLAN_EID_DMG_BSS_PARAM_CHANGE = 152,
WLAN_EID_DMG_BEAM_REFINEMENT = 153,
WLAN_EID_CHANNEL_MEASURE_FEEDBACK = 154,
+ /* 155-156 reserved for Cisco */
WLAN_EID_AWAKE_WINDOW = 157,
WLAN_EID_MULTI_BAND = 158,
WLAN_EID_ADDBA_EXT = 159,
@@ -1767,11 +1804,34 @@ enum ieee80211_eid {
WLAN_EID_MULTIPLE_MAC_ADDR = 170,
WLAN_EID_U_PID = 171,
WLAN_EID_DMG_LINK_ADAPT_ACK = 172,
+ /* 173 reserved for Symbol */
+ WLAN_EID_MCCAOP_ADV_OVERVIEW = 174,
WLAN_EID_QUIET_PERIOD_REQ = 175,
+ /* 176 reserved for Symbol */
WLAN_EID_QUIET_PERIOD_RESP = 177,
+ /* 178-179 reserved for Symbol */
+ /* 180 reserved for ISO/IEC 20011 */
WLAN_EID_EPAC_POLICY = 182,
WLAN_EID_CLISTER_TIME_OFF = 183,
+ WLAN_EID_INTER_AC_PRIO = 184,
+ WLAN_EID_SCS_DESCRIPTOR = 185,
+ WLAN_EID_QLOAD_REPORT = 186,
+ WLAN_EID_HCCA_TXOP_UPDATE_COUNT = 187,
+ WLAN_EID_HL_STREAM_ID = 188,
+ WLAN_EID_GCR_GROUP_ADDR = 189,
WLAN_EID_ANTENNA_SECTOR_ID_PATTERN = 190,
+ WLAN_EID_VHT_CAPABILITY = 191,
+ WLAN_EID_VHT_OPERATION = 192,
+ WLAN_EID_EXTENDED_BSS_LOAD = 193,
+ WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
+ WLAN_EID_VHT_TX_POWER_ENVELOPE = 195,
+ WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
+ WLAN_EID_AID = 197,
+ WLAN_EID_QUIET_CHANNEL = 198,
+ WLAN_EID_OPMODE_NOTIF = 199,
+
+ WLAN_EID_VENDOR_SPECIFIC = 221,
+ WLAN_EID_QOS_PARAMETER = 222,
};
/* Action category code */
@@ -2192,10 +2252,10 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr)
}
/**
- * ieee80211_is_robust_mgmt_frame - check if frame is a robust management frame
+ * _ieee80211_is_robust_mgmt_frame - check if frame is a robust management frame
* @hdr: the frame (buffer must include at least the first octet of payload)
*/
-static inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr)
+static inline bool _ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr)
{
if (ieee80211_is_disassoc(hdr->frame_control) ||
ieee80211_is_deauth(hdr->frame_control))
@@ -2224,6 +2284,17 @@ static inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr)
}
/**
+ * ieee80211_is_robust_mgmt_frame - check if skb contains a robust mgmt frame
+ * @skb: the skb containing the frame, length will be checked
+ */
+static inline bool ieee80211_is_robust_mgmt_frame(struct sk_buff *skb)
+{
+ if (skb->len < 25)
+ return false;
+ return _ieee80211_is_robust_mgmt_frame((void *)skb->data);
+}
+
+/**
* ieee80211_is_public_action - check if frame is a public action frame
* @hdr: the frame
* @len: length of the frame
@@ -2241,42 +2312,6 @@ static inline bool ieee80211_is_public_action(struct ieee80211_hdr *hdr,
}
/**
- * ieee80211_dsss_chan_to_freq - get channel center frequency
- * @channel: the DSSS channel
- *
- * Convert IEEE802.11 DSSS channel to the center frequency (MHz).
- * Ref IEEE 802.11-2007 section 15.6
- */
-static inline int ieee80211_dsss_chan_to_freq(int channel)
-{
- if ((channel > 0) && (channel < 14))
- return 2407 + (channel * 5);
- else if (channel == 14)
- return 2484;
- else
- return -1;
-}
-
-/**
- * ieee80211_freq_to_dsss_chan - get channel
- * @freq: the frequency
- *
- * Convert frequency (MHz) to IEEE802.11 DSSS channel
- * Ref IEEE 802.11-2007 section 15.6
- *
- * This routine selects the channel with the closest center frequency.
- */
-static inline int ieee80211_freq_to_dsss_chan(int freq)
-{
- if ((freq >= 2410) && (freq < 2475))
- return (freq - 2405) / 5;
- else if ((freq >= 2482) && (freq < 2487))
- return 14;
- else
- return -1;
-}
-
-/**
* ieee80211_tu_to_usec - convert time units (TU) to microseconds
* @tu: the TUs
*/
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index bbedfb56bd66..13bbbde00e68 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -110,6 +110,7 @@ extern struct net_device *__vlan_find_dev_deep(struct net_device *real_dev,
__be16 vlan_proto, u16 vlan_id);
extern struct net_device *vlan_dev_real_dev(const struct net_device *dev);
extern u16 vlan_dev_vlan_id(const struct net_device *dev);
+extern __be16 vlan_dev_vlan_proto(const struct net_device *dev);
/**
* struct vlan_priority_tci_mapping - vlan egress priority mappings
@@ -216,6 +217,12 @@ static inline u16 vlan_dev_vlan_id(const struct net_device *dev)
return 0;
}
+static inline __be16 vlan_dev_vlan_proto(const struct net_device *dev)
+{
+ BUG();
+ return 0;
+}
+
static inline u16 vlan_dev_get_egress_qos_mask(struct net_device *dev,
u32 skprio)
{
@@ -288,7 +295,7 @@ static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb,
struct vlan_ethhdr *veth;
if (skb_cow_head(skb, VLAN_HLEN) < 0) {
- kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NULL;
}
veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN);
diff --git a/include/linux/input/pmic8xxx-keypad.h b/include/linux/input/pmic8xxx-keypad.h
deleted file mode 100644
index 5f1e2f9ad959..000000000000
--- a/include/linux/input/pmic8xxx-keypad.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __PMIC8XXX_KEYPAD_H__
-#define __PMIC8XXX_KEYPAD_H__
-
-#include <linux/input/matrix_keypad.h>
-
-#define PM8XXX_KEYPAD_DEV_NAME "pm8xxx-keypad"
-
-/**
- * struct pm8xxx_keypad_platform_data - platform data for keypad
- * @keymap_data - matrix keymap data
- * @input_name - input device name
- * @input_phys_device - input device name
- * @num_cols - number of columns of keypad
- * @num_rows - number of row of keypad
- * @debounce_ms - debounce period in milliseconds
- * @scan_delay_ms - scan delay in milliseconds
- * @row_hold_ns - row hold period in nanoseconds
- * @wakeup - configure keypad as wakeup
- * @rep - enable or disable key repeat bit
- */
-struct pm8xxx_keypad_platform_data {
- const struct matrix_keymap_data *keymap_data;
-
- const char *input_name;
- const char *input_phys_device;
-
- unsigned int num_cols;
- unsigned int num_rows;
- unsigned int rows_gpio_start;
- unsigned int cols_gpio_start;
-
- unsigned int debounce_ms;
- unsigned int scan_delay_ms;
- unsigned int row_hold_ns;
-
- bool wakeup;
- bool rep;
-};
-
-#endif /*__PMIC8XXX_KEYPAD_H__ */
diff --git a/include/linux/input/pmic8xxx-pwrkey.h b/include/linux/input/pmic8xxx-pwrkey.h
deleted file mode 100644
index 6d2974e57109..000000000000
--- a/include/linux/input/pmic8xxx-pwrkey.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __PMIC8XXX_PWRKEY_H__
-#define __PMIC8XXX_PWRKEY_H__
-
-#define PM8XXX_PWRKEY_DEV_NAME "pm8xxx-pwrkey"
-
-/**
- * struct pm8xxx_pwrkey_platform_data - platform data for pwrkey driver
- * @pull up: power on register control for pull up/down configuration
- * @kpd_trigger_delay_us: time delay for power key state change interrupt
- * trigger.
- * @wakeup: configure power key as wakeup source
- */
-struct pm8xxx_pwrkey_platform_data {
- bool pull_up;
- u32 kpd_trigger_delay_us;
- u32 wakeup;
-};
-
-#endif /* __PMIC8XXX_PWRKEY_H__ */
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index 2c4bed593b32..0a2da5188217 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -319,6 +319,7 @@ struct intel_iommu {
int agaw; /* agaw of this iommu */
int msagaw; /* max sagaw of this iommu */
unsigned int irq;
+ u16 segment; /* PCI segment# */
unsigned char name[13]; /* Device Name */
#ifdef CONFIG_INTEL_IOMMU
diff --git a/include/linux/iova.h b/include/linux/iova.h
index 76a0759e88ec..3277f4711349 100644
--- a/include/linux/iova.h
+++ b/include/linux/iova.h
@@ -47,5 +47,7 @@ void copy_reserved_iova(struct iova_domain *from, struct iova_domain *to);
void init_iova_domain(struct iova_domain *iovad, unsigned long pfn_32bit);
struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn);
void put_iova_domain(struct iova_domain *iovad);
+struct iova *split_and_remove_iova(struct iova_domain *iovad,
+ struct iova *iova, unsigned long pfn_lo, unsigned long pfn_hi);
#endif
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
index 0ceb389dba6c..7ed92d0560d5 100644
--- a/include/linux/irqchip/arm-gic.h
+++ b/include/linux/irqchip/arm-gic.h
@@ -93,6 +93,11 @@ int gic_get_cpu_id(unsigned int cpu);
void gic_migrate_target(unsigned int new_cpu_id);
unsigned long gic_get_sgir_physaddr(void);
+extern const struct irq_domain_ops *gic_routable_irq_domain_ops;
+static inline void __init register_routable_domain_ops
+ (const struct irq_domain_ops *ops)
+{
+ gic_routable_irq_domain_ops = ops;
+}
#endif /* __ASSEMBLY */
-
#endif
diff --git a/include/linux/irqchip/arm-vic.h b/include/linux/irqchip/arm-vic.h
index e3c82dc95756..ba46c794b4e5 100644
--- a/include/linux/irqchip/arm-vic.h
+++ b/include/linux/irqchip/arm-vic.h
@@ -29,8 +29,10 @@
struct device_node;
struct pt_regs;
-void __vic_init(void __iomem *base, int irq_start, u32 vic_sources,
- u32 resume_sources, struct device_node *node);
+void __vic_init(void __iomem *base, int parent_irq, int irq_start,
+ u32 vic_sources, u32 resume_sources, struct device_node *node);
void vic_init(void __iomem *base, unsigned int irq_start, u32 vic_sources, u32 resume_sources);
+int vic_init_cascaded(void __iomem *base, unsigned int parent_irq,
+ u32 vic_sources, u32 resume_sources);
#endif
diff --git a/arch/arm/mach-mmp/include/mach/timex.h b/include/linux/irqchip/irq-crossbar.h
index 70c9f1d88c02..e5537b81df8d 100644
--- a/arch/arm/mach-mmp/include/mach/timex.h
+++ b/include/linux/irqchip/irq-crossbar.h
@@ -1,13 +1,11 @@
/*
- * linux/arch/arm/mach-mmp/include/mach/timex.h
+ * drivers/irqchip/irq-crossbar.h
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.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.
+ *
*/
-
-#ifdef CONFIG_CPU_MMP2
-#define CLOCK_TICK_RATE 6500000
-#else
-#define CLOCK_TICK_RATE 3250000
-#endif
+int irqcrossbar_init(void);
diff --git a/include/linux/isapnp.h b/include/linux/isapnp.h
index e2d28b026a8c..3c77bf9b1efd 100644
--- a/include/linux/isapnp.h
+++ b/include/linux/isapnp.h
@@ -56,10 +56,6 @@
#define ISAPNP_DEVICE_ID(_va, _vb, _vc, _function) \
{ .vendor = ISAPNP_VENDOR(_va, _vb, _vc), .function = ISAPNP_FUNCTION(_function) }
-/* export used IDs outside module */
-#define ISAPNP_CARD_TABLE(name) \
- MODULE_GENERIC_TABLE(isapnp_card, name)
-
struct isapnp_card_id {
unsigned long driver_data; /* data private to the driver */
unsigned short card_vendor, card_device;
diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h
index d5f62bc5f4be..8e10f57f109f 100644
--- a/include/linux/isdn_ppp.h
+++ b/include/linux/isdn_ppp.h
@@ -180,9 +180,8 @@ struct ippp_struct {
struct slcompress *slcomp;
#endif
#ifdef CONFIG_IPPP_FILTER
- struct sock_filter *pass_filter; /* filter for packets to pass */
- struct sock_filter *active_filter; /* filter for pkts to reset idle */
- unsigned pass_len, active_len;
+ struct sk_filter *pass_filter; /* filter for packets to pass */
+ struct sk_filter *active_filter; /* filter for pkts to reset idle */
#endif
unsigned long debug;
struct isdn_ppp_compressor *compressor,*decompressor;
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 08fb02477641..4c52907a6d8b 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -469,6 +469,7 @@ extern enum system_states {
#define TAINT_CRAP 10
#define TAINT_FIRMWARE_WORKAROUND 11
#define TAINT_OOT_MODULE 12
+#define TAINT_UNSIGNED_MODULE 13
extern const char hex_asc[];
#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
@@ -841,4 +842,12 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
# define REBUILD_DUE_TO_FTRACE_MCOUNT_RECORD
#endif
+/* Permissions on a sysfs file: you didn't miss the 0 prefix did you? */
+#define VERIFY_OCTAL_PERMISSIONS(perms) \
+ (BUILD_BUG_ON_ZERO((perms) < 0) + \
+ BUILD_BUG_ON_ZERO((perms) > 0777) + \
+ /* User perms >= group perms >= other perms */ \
+ BUILD_BUG_ON_ZERO(((perms) >> 6) < (((perms) >> 3) & 7)) + \
+ BUILD_BUG_ON_ZERO((((perms) >> 3) & 7) < ((perms) & 7)) + \
+ (perms))
#endif
diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h
index 552d51efb429..554fde3a3927 100644
--- a/include/linux/kfifo.h
+++ b/include/linux/kfifo.h
@@ -722,7 +722,7 @@ __kfifo_uint_must_check_helper( \
/**
* kfifo_dma_out_finish - finish a DMA OUT operation
* @fifo: address of the fifo to be used
- * @len: number of bytes transferd
+ * @len: number of bytes transferrd
*
* This macro finish a DMA OUT operation. The out counter will be updated by
* the len parameter. No error checking will be done.
diff --git a/include/linux/kmemleak.h b/include/linux/kmemleak.h
index 2a5e5548a1d2..5bb424659c04 100644
--- a/include/linux/kmemleak.h
+++ b/include/linux/kmemleak.h
@@ -30,8 +30,6 @@ extern void kmemleak_alloc_percpu(const void __percpu *ptr, size_t size) __ref;
extern void kmemleak_free(const void *ptr) __ref;
extern void kmemleak_free_part(const void *ptr, size_t size) __ref;
extern void kmemleak_free_percpu(const void __percpu *ptr) __ref;
-extern void kmemleak_padding(const void *ptr, unsigned long offset,
- size_t size) __ref;
extern void kmemleak_not_leak(const void *ptr) __ref;
extern void kmemleak_ignore(const void *ptr) __ref;
extern void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) __ref;
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index 926afb6f6b5f..f896a33e8341 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -119,6 +119,7 @@ struct kobj_type {
};
struct kobj_uevent_env {
+ char *argv[3];
char *envp[UEVENT_NUM_ENVP];
int envp_idx;
char buf[UEVENT_BUFFER_SIZE];
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index b8e9a43e501a..7d21cf9f4380 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -192,7 +192,7 @@ struct kvm_async_pf {
void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu);
void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu);
-int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn,
+int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, unsigned long hva,
struct kvm_arch_async_pf *arch);
int kvm_async_pf_wakeup_all(struct kvm_vcpu *vcpu);
#endif
@@ -297,6 +297,14 @@ static inline unsigned long kvm_dirty_bitmap_bytes(struct kvm_memory_slot *memsl
return ALIGN(memslot->npages, BITS_PER_LONG) / 8;
}
+struct kvm_s390_adapter_int {
+ u64 ind_addr;
+ u64 summary_addr;
+ u64 ind_offset;
+ u32 summary_offset;
+ u32 adapter_id;
+};
+
struct kvm_kernel_irq_routing_entry {
u32 gsi;
u32 type;
@@ -309,6 +317,7 @@ struct kvm_kernel_irq_routing_entry {
unsigned pin;
} irqchip;
struct msi_msg msi;
+ struct kvm_s390_adapter_int adapter;
};
struct hlist_node link;
};
@@ -401,7 +410,9 @@ struct kvm {
unsigned long mmu_notifier_seq;
long mmu_notifier_count;
#endif
- long tlbs_dirty;
+ /* Protected by mmu_lock */
+ bool tlbs_dirty;
+
struct list_head devices;
};
@@ -911,7 +922,11 @@ static inline int mmu_notifier_retry(struct kvm *kvm, unsigned long mmu_seq)
#ifdef CONFIG_HAVE_KVM_IRQ_ROUTING
+#ifdef CONFIG_S390
+#define KVM_MAX_IRQ_ROUTES 4096 //FIXME: we can have more than that...
+#else
#define KVM_MAX_IRQ_ROUTES 1024
+#endif
int kvm_setup_default_irq_routing(struct kvm *kvm);
int kvm_set_irq_routing(struct kvm *kvm,
@@ -1064,6 +1079,7 @@ extern struct kvm_device_ops kvm_mpic_ops;
extern struct kvm_device_ops kvm_xics_ops;
extern struct kvm_device_ops kvm_vfio_ops;
extern struct kvm_device_ops kvm_arm_vgic_v2_ops;
+extern struct kvm_device_ops kvm_flic_ops;
#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT
diff --git a/include/linux/list_lru.h b/include/linux/list_lru.h
index 3ce541753c88..f3434533fbf8 100644
--- a/include/linux/list_lru.h
+++ b/include/linux/list_lru.h
@@ -13,6 +13,8 @@
/* list_lru_walk_cb has to always return one of those */
enum lru_status {
LRU_REMOVED, /* item removed from list */
+ LRU_REMOVED_RETRY, /* item removed, but lock has been
+ dropped and reacquired */
LRU_ROTATE, /* item referenced, give another pass */
LRU_SKIP, /* item cannot be locked, skip */
LRU_RETRY, /* item not freeable. May drop the lock
@@ -32,7 +34,11 @@ struct list_lru {
};
void list_lru_destroy(struct list_lru *lru);
-int list_lru_init(struct list_lru *lru);
+int list_lru_init_key(struct list_lru *lru, struct lock_class_key *key);
+static inline int list_lru_init(struct list_lru *lru)
+{
+ return list_lru_init_key(lru, NULL);
+}
/**
* list_lru_add: add an element to the lru list's tail
diff --git a/include/linux/mbcache.h b/include/linux/mbcache.h
index 5525d370701d..6a392e7a723a 100644
--- a/include/linux/mbcache.h
+++ b/include/linux/mbcache.h
@@ -3,19 +3,21 @@
(C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
*/
-
struct mb_cache_entry {
struct list_head e_lru_list;
struct mb_cache *e_cache;
unsigned short e_used;
unsigned short e_queued;
+ atomic_t e_refcnt;
struct block_device *e_bdev;
sector_t e_block;
- struct list_head e_block_list;
+ struct hlist_bl_node e_block_list;
struct {
- struct list_head o_list;
+ struct hlist_bl_node o_list;
unsigned int o_key;
} e_index;
+ struct hlist_bl_head *e_block_hash_p;
+ struct hlist_bl_head *e_index_hash_p;
};
struct mb_cache {
@@ -25,8 +27,8 @@ struct mb_cache {
int c_max_entries;
int c_bucket_bits;
struct kmem_cache *c_entry_cache;
- struct list_head *c_block_hash;
- struct list_head *c_index_hash;
+ struct hlist_bl_head *c_block_hash;
+ struct hlist_bl_head *c_index_hash;
};
/* Functions on caches */
diff --git a/include/linux/memblock.h b/include/linux/memblock.h
index 1ef66360f0b0..8a20a51ed42d 100644
--- a/include/linux/memblock.h
+++ b/include/linux/memblock.h
@@ -252,6 +252,8 @@ static inline void memblock_dump_all(void)
void memblock_set_current_limit(phys_addr_t limit);
+phys_addr_t memblock_get_current_limit(void);
+
/*
* pfn conversion functions
*
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index abd0113b6620..eccfb4a4b379 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -162,7 +162,7 @@ extern int do_swap_account;
static inline bool mem_cgroup_disabled(void)
{
- if (mem_cgroup_subsys.disabled)
+ if (memory_cgrp_subsys.disabled)
return true;
return false;
}
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index a86ca1406fb8..4e7fe7417fc9 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -347,7 +347,6 @@ struct ab8500 {
struct mutex lock;
struct mutex irq_lock;
atomic_t transfer_ongoing;
- int irq_base;
int irq;
struct irq_domain *domain;
enum ab8500_version version;
@@ -378,7 +377,6 @@ struct ab8500_sysctrl_platform_data;
* @regulator: machine-specific constraints for regulators
*/
struct ab8500_platform_data {
- int irq_base;
void (*init) (struct ab8500 *);
struct ab8500_regulator_platform_data *regulator;
struct ab8500_codec_platform_data *codec;
diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h
index 3ddaa634b19d..7b35c21170d5 100644
--- a/include/linux/mfd/arizona/registers.h
+++ b/include/linux/mfd/arizona/registers.h
@@ -1034,6 +1034,27 @@
#define ARIZONA_DSP1_STATUS_1 0x1104
#define ARIZONA_DSP1_STATUS_2 0x1105
#define ARIZONA_DSP1_STATUS_3 0x1106
+#define ARIZONA_DSP1_STATUS_4 0x1107
+#define ARIZONA_DSP1_WDMA_BUFFER_1 0x1110
+#define ARIZONA_DSP1_WDMA_BUFFER_2 0x1111
+#define ARIZONA_DSP1_WDMA_BUFFER_3 0x1112
+#define ARIZONA_DSP1_WDMA_BUFFER_4 0x1113
+#define ARIZONA_DSP1_WDMA_BUFFER_5 0x1114
+#define ARIZONA_DSP1_WDMA_BUFFER_6 0x1115
+#define ARIZONA_DSP1_WDMA_BUFFER_7 0x1116
+#define ARIZONA_DSP1_WDMA_BUFFER_8 0x1117
+#define ARIZONA_DSP1_RDMA_BUFFER_1 0x1120
+#define ARIZONA_DSP1_RDMA_BUFFER_2 0x1121
+#define ARIZONA_DSP1_RDMA_BUFFER_3 0x1122
+#define ARIZONA_DSP1_RDMA_BUFFER_4 0x1123
+#define ARIZONA_DSP1_RDMA_BUFFER_5 0x1124
+#define ARIZONA_DSP1_RDMA_BUFFER_6 0x1125
+#define ARIZONA_DSP1_WDMA_CONFIG_1 0x1130
+#define ARIZONA_DSP1_WDMA_CONFIG_2 0x1131
+#define ARIZONA_DSP1_WDMA_OFFSET_1 0x1132
+#define ARIZONA_DSP1_RDMA_CONFIG_1 0x1134
+#define ARIZONA_DSP1_RDMA_OFFSET_1 0x1135
+#define ARIZONA_DSP1_EXTERNAL_START_SELECT_1 0x1138
#define ARIZONA_DSP1_SCRATCH_0 0x1140
#define ARIZONA_DSP1_SCRATCH_1 0x1141
#define ARIZONA_DSP1_SCRATCH_2 0x1142
@@ -1043,6 +1064,27 @@
#define ARIZONA_DSP2_STATUS_1 0x1204
#define ARIZONA_DSP2_STATUS_2 0x1205
#define ARIZONA_DSP2_STATUS_3 0x1206
+#define ARIZONA_DSP2_STATUS_4 0x1207
+#define ARIZONA_DSP2_WDMA_BUFFER_1 0x1210
+#define ARIZONA_DSP2_WDMA_BUFFER_2 0x1211
+#define ARIZONA_DSP2_WDMA_BUFFER_3 0x1212
+#define ARIZONA_DSP2_WDMA_BUFFER_4 0x1213
+#define ARIZONA_DSP2_WDMA_BUFFER_5 0x1214
+#define ARIZONA_DSP2_WDMA_BUFFER_6 0x1215
+#define ARIZONA_DSP2_WDMA_BUFFER_7 0x1216
+#define ARIZONA_DSP2_WDMA_BUFFER_8 0x1217
+#define ARIZONA_DSP2_RDMA_BUFFER_1 0x1220
+#define ARIZONA_DSP2_RDMA_BUFFER_2 0x1221
+#define ARIZONA_DSP2_RDMA_BUFFER_3 0x1222
+#define ARIZONA_DSP2_RDMA_BUFFER_4 0x1223
+#define ARIZONA_DSP2_RDMA_BUFFER_5 0x1224
+#define ARIZONA_DSP2_RDMA_BUFFER_6 0x1225
+#define ARIZONA_DSP2_WDMA_CONFIG_1 0x1230
+#define ARIZONA_DSP2_WDMA_CONFIG_2 0x1231
+#define ARIZONA_DSP2_WDMA_OFFSET_1 0x1232
+#define ARIZONA_DSP2_RDMA_CONFIG_1 0x1234
+#define ARIZONA_DSP2_RDMA_OFFSET_1 0x1235
+#define ARIZONA_DSP2_EXTERNAL_START_SELECT_1 0x1238
#define ARIZONA_DSP2_SCRATCH_0 0x1240
#define ARIZONA_DSP2_SCRATCH_1 0x1241
#define ARIZONA_DSP2_SCRATCH_2 0x1242
@@ -1052,6 +1094,27 @@
#define ARIZONA_DSP3_STATUS_1 0x1304
#define ARIZONA_DSP3_STATUS_2 0x1305
#define ARIZONA_DSP3_STATUS_3 0x1306
+#define ARIZONA_DSP3_STATUS_4 0x1307
+#define ARIZONA_DSP3_WDMA_BUFFER_1 0x1310
+#define ARIZONA_DSP3_WDMA_BUFFER_2 0x1311
+#define ARIZONA_DSP3_WDMA_BUFFER_3 0x1312
+#define ARIZONA_DSP3_WDMA_BUFFER_4 0x1313
+#define ARIZONA_DSP3_WDMA_BUFFER_5 0x1314
+#define ARIZONA_DSP3_WDMA_BUFFER_6 0x1315
+#define ARIZONA_DSP3_WDMA_BUFFER_7 0x1316
+#define ARIZONA_DSP3_WDMA_BUFFER_8 0x1317
+#define ARIZONA_DSP3_RDMA_BUFFER_1 0x1320
+#define ARIZONA_DSP3_RDMA_BUFFER_2 0x1321
+#define ARIZONA_DSP3_RDMA_BUFFER_3 0x1322
+#define ARIZONA_DSP3_RDMA_BUFFER_4 0x1323
+#define ARIZONA_DSP3_RDMA_BUFFER_5 0x1324
+#define ARIZONA_DSP3_RDMA_BUFFER_6 0x1325
+#define ARIZONA_DSP3_WDMA_CONFIG_1 0x1330
+#define ARIZONA_DSP3_WDMA_CONFIG_2 0x1331
+#define ARIZONA_DSP3_WDMA_OFFSET_1 0x1332
+#define ARIZONA_DSP3_RDMA_CONFIG_1 0x1334
+#define ARIZONA_DSP3_RDMA_OFFSET_1 0x1335
+#define ARIZONA_DSP3_EXTERNAL_START_SELECT_1 0x1338
#define ARIZONA_DSP3_SCRATCH_0 0x1340
#define ARIZONA_DSP3_SCRATCH_1 0x1341
#define ARIZONA_DSP3_SCRATCH_2 0x1342
@@ -1061,6 +1124,27 @@
#define ARIZONA_DSP4_STATUS_1 0x1404
#define ARIZONA_DSP4_STATUS_2 0x1405
#define ARIZONA_DSP4_STATUS_3 0x1406
+#define ARIZONA_DSP4_STATUS_4 0x1407
+#define ARIZONA_DSP4_WDMA_BUFFER_1 0x1410
+#define ARIZONA_DSP4_WDMA_BUFFER_2 0x1411
+#define ARIZONA_DSP4_WDMA_BUFFER_3 0x1412
+#define ARIZONA_DSP4_WDMA_BUFFER_4 0x1413
+#define ARIZONA_DSP4_WDMA_BUFFER_5 0x1414
+#define ARIZONA_DSP4_WDMA_BUFFER_6 0x1415
+#define ARIZONA_DSP4_WDMA_BUFFER_7 0x1416
+#define ARIZONA_DSP4_WDMA_BUFFER_8 0x1417
+#define ARIZONA_DSP4_RDMA_BUFFER_1 0x1420
+#define ARIZONA_DSP4_RDMA_BUFFER_2 0x1421
+#define ARIZONA_DSP4_RDMA_BUFFER_3 0x1422
+#define ARIZONA_DSP4_RDMA_BUFFER_4 0x1423
+#define ARIZONA_DSP4_RDMA_BUFFER_5 0x1424
+#define ARIZONA_DSP4_RDMA_BUFFER_6 0x1425
+#define ARIZONA_DSP4_WDMA_CONFIG_1 0x1430
+#define ARIZONA_DSP4_WDMA_CONFIG_2 0x1431
+#define ARIZONA_DSP4_WDMA_OFFSET_1 0x1432
+#define ARIZONA_DSP4_RDMA_CONFIG_1 0x1434
+#define ARIZONA_DSP4_RDMA_OFFSET_1 0x1435
+#define ARIZONA_DSP4_EXTERNAL_START_SELECT_1 0x1438
#define ARIZONA_DSP4_SCRATCH_0 0x1440
#define ARIZONA_DSP4_SCRATCH_1 0x1441
#define ARIZONA_DSP4_SCRATCH_2 0x1442
diff --git a/include/linux/mfd/bcm590xx.h b/include/linux/mfd/bcm590xx.h
new file mode 100644
index 000000000000..434df2d4e587
--- /dev/null
+++ b/include/linux/mfd/bcm590xx.h
@@ -0,0 +1,31 @@
+/*
+ * Broadcom BCM590xx PMU
+ *
+ * Copyright 2014 Linaro Limited
+ * Author: Matt Porter <mporter@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_BCM590XX_H
+#define __LINUX_MFD_BCM590XX_H
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+/* max register address */
+#define BCM590XX_MAX_REGISTER 0xe7
+
+struct bcm590xx {
+ struct device *dev;
+ struct i2c_client *i2c_client;
+ struct regmap *regmap;
+ unsigned int id;
+};
+
+#endif /* __LINUX_MFD_BCM590XX_H */
diff --git a/include/linux/mfd/da9052/da9052.h b/include/linux/mfd/da9052/da9052.h
index 21e21b81cc75..bba65f51a0b5 100644
--- a/include/linux/mfd/da9052/da9052.h
+++ b/include/linux/mfd/da9052/da9052.h
@@ -83,6 +83,7 @@ enum da9052_chip_id {
DA9053_AA,
DA9053_BA,
DA9053_BB,
+ DA9053_BC,
};
struct da9052_pdata;
diff --git a/include/linux/mfd/da9063/core.h b/include/linux/mfd/da9063/core.h
index 2d2a0af675fd..00a9aac5d1e8 100644
--- a/include/linux/mfd/da9063/core.h
+++ b/include/linux/mfd/da9063/core.h
@@ -33,6 +33,10 @@ enum da9063_models {
PMIC_DA9063 = 0x61,
};
+enum da9063_variant_codes {
+ PMIC_DA9063_BB = 0x5
+};
+
/* Interrupts */
enum da9063_irqs {
DA9063_IRQ_ONKEY = 0,
@@ -72,7 +76,7 @@ struct da9063 {
/* Device */
struct device *dev;
unsigned short model;
- unsigned short revision;
+ unsigned char variant_code;
unsigned int flags;
/* Control interface */
diff --git a/include/linux/mfd/da9063/registers.h b/include/linux/mfd/da9063/registers.h
index 5834813fb5f3..09a85c699da1 100644
--- a/include/linux/mfd/da9063/registers.h
+++ b/include/linux/mfd/da9063/registers.h
@@ -17,11 +17,7 @@
#define _DA9063_REG_H
#define DA9063_I2C_PAGE_SEL_SHIFT 1
-
#define DA9063_EVENT_REG_NUM 4
-#define DA9210_EVENT_REG_NUM 2
-#define DA9063_EXT_EVENT_REG_NUM (DA9063_EVENT_REG_NUM + \
- DA9210_EVENT_REG_NUM)
/* Page selection I2C or SPI always in the begining of any page. */
/* Page 0 : I2C access 0x000 - 0x0FF SPI access 0x000 - 0x07F */
@@ -61,9 +57,9 @@
#define DA9063_REG_GPIO_10_11 0x1A
#define DA9063_REG_GPIO_12_13 0x1B
#define DA9063_REG_GPIO_14_15 0x1C
-#define DA9063_REG_GPIO_MODE_0_7 0x1D
-#define DA9063_REG_GPIO_MODE_8_15 0x1E
-#define DA9063_REG_GPIO_SWITCH_CONT 0x1F
+#define DA9063_REG_GPIO_MODE0_7 0x1D
+#define DA9063_REG_GPIO_MODE8_15 0x1E
+#define DA9063_REG_SWITCH_CONT 0x1F
/* Regulator Control Registers */
#define DA9063_REG_BCORE2_CONT 0x20
@@ -83,7 +79,7 @@
#define DA9063_REG_LDO9_CONT 0x2E
#define DA9063_REG_LDO10_CONT 0x2F
#define DA9063_REG_LDO11_CONT 0x30
-#define DA9063_REG_VIB 0x31
+#define DA9063_REG_SUPPLIES 0x31
#define DA9063_REG_DVC_1 0x32
#define DA9063_REG_DVC_2 0x33
@@ -97,9 +93,9 @@
#define DA9063_REG_ADCIN1_RES 0x3A
#define DA9063_REG_ADCIN2_RES 0x3B
#define DA9063_REG_ADCIN3_RES 0x3C
-#define DA9063_REG_MON1_RES 0x3D
-#define DA9063_REG_MON2_RES 0x3E
-#define DA9063_REG_MON3_RES 0x3F
+#define DA9063_REG_MON_A8_RES 0x3D
+#define DA9063_REG_MON_A9_RES 0x3E
+#define DA9063_REG_MON_A10_RES 0x3F
/* RTC Calendar and Alarm Registers */
#define DA9063_REG_COUNT_S 0x40
@@ -108,15 +104,16 @@
#define DA9063_REG_COUNT_D 0x43
#define DA9063_REG_COUNT_MO 0x44
#define DA9063_REG_COUNT_Y 0x45
-#define DA9063_REG_ALARM_MI 0x46
-#define DA9063_REG_ALARM_H 0x47
-#define DA9063_REG_ALARM_D 0x48
-#define DA9063_REG_ALARM_MO 0x49
-#define DA9063_REG_ALARM_Y 0x4A
-#define DA9063_REG_SECOND_A 0x4B
-#define DA9063_REG_SECOND_B 0x4C
-#define DA9063_REG_SECOND_C 0x4D
-#define DA9063_REG_SECOND_D 0x4E
+#define DA9063_REG_ALARM_S 0x46
+#define DA9063_REG_ALARM_MI 0x47
+#define DA9063_REG_ALARM_H 0x48
+#define DA9063_REG_ALARM_D 0x49
+#define DA9063_REG_ALARM_MO 0x4A
+#define DA9063_REG_ALARM_Y 0x4B
+#define DA9063_REG_SECOND_A 0x4C
+#define DA9063_REG_SECOND_B 0x4D
+#define DA9063_REG_SECOND_C 0x4E
+#define DA9063_REG_SECOND_D 0x4F
/* Sequencer Control Registers */
#define DA9063_REG_SEQ 0x81
@@ -226,35 +223,37 @@
#define DA9063_REG_CONFIG_J 0x10F
#define DA9063_REG_CONFIG_K 0x110
#define DA9063_REG_CONFIG_L 0x111
-#define DA9063_REG_MON_REG_1 0x112
-#define DA9063_REG_MON_REG_2 0x113
-#define DA9063_REG_MON_REG_3 0x114
-#define DA9063_REG_MON_REG_4 0x115
-#define DA9063_REG_MON_REG_5 0x116
-#define DA9063_REG_MON_REG_6 0x117
-#define DA9063_REG_TRIM_CLDR 0x118
-
+#define DA9063_REG_CONFIG_M 0x112
+#define DA9063_REG_CONFIG_N 0x113
+
+#define DA9063_REG_MON_REG_1 0x114
+#define DA9063_REG_MON_REG_2 0x115
+#define DA9063_REG_MON_REG_3 0x116
+#define DA9063_REG_MON_REG_4 0x117
+#define DA9063_REG_MON_REG_5 0x11E
+#define DA9063_REG_MON_REG_6 0x11F
+#define DA9063_REG_TRIM_CLDR 0x120
/* General Purpose Registers */
-#define DA9063_REG_GP_ID_0 0x119
-#define DA9063_REG_GP_ID_1 0x11A
-#define DA9063_REG_GP_ID_2 0x11B
-#define DA9063_REG_GP_ID_3 0x11C
-#define DA9063_REG_GP_ID_4 0x11D
-#define DA9063_REG_GP_ID_5 0x11E
-#define DA9063_REG_GP_ID_6 0x11F
-#define DA9063_REG_GP_ID_7 0x120
-#define DA9063_REG_GP_ID_8 0x121
-#define DA9063_REG_GP_ID_9 0x122
-#define DA9063_REG_GP_ID_10 0x123
-#define DA9063_REG_GP_ID_11 0x124
-#define DA9063_REG_GP_ID_12 0x125
-#define DA9063_REG_GP_ID_13 0x126
-#define DA9063_REG_GP_ID_14 0x127
-#define DA9063_REG_GP_ID_15 0x128
-#define DA9063_REG_GP_ID_16 0x129
-#define DA9063_REG_GP_ID_17 0x12A
-#define DA9063_REG_GP_ID_18 0x12B
-#define DA9063_REG_GP_ID_19 0x12C
+#define DA9063_REG_GP_ID_0 0x121
+#define DA9063_REG_GP_ID_1 0x122
+#define DA9063_REG_GP_ID_2 0x123
+#define DA9063_REG_GP_ID_3 0x124
+#define DA9063_REG_GP_ID_4 0x125
+#define DA9063_REG_GP_ID_5 0x126
+#define DA9063_REG_GP_ID_6 0x127
+#define DA9063_REG_GP_ID_7 0x128
+#define DA9063_REG_GP_ID_8 0x129
+#define DA9063_REG_GP_ID_9 0x12A
+#define DA9063_REG_GP_ID_10 0x12B
+#define DA9063_REG_GP_ID_11 0x12C
+#define DA9063_REG_GP_ID_12 0x12D
+#define DA9063_REG_GP_ID_13 0x12E
+#define DA9063_REG_GP_ID_14 0x12F
+#define DA9063_REG_GP_ID_15 0x130
+#define DA9063_REG_GP_ID_16 0x131
+#define DA9063_REG_GP_ID_17 0x132
+#define DA9063_REG_GP_ID_18 0x133
+#define DA9063_REG_GP_ID_19 0x134
/* Chip ID and variant */
#define DA9063_REG_CHIP_ID 0x181
@@ -405,8 +404,10 @@
/* DA9063_REG_CONTROL_B (addr=0x0F) */
#define DA9063_CHG_SEL 0x01
#define DA9063_WATCHDOG_PD 0x02
+#define DA9063_RESET_BLINKING 0x04
#define DA9063_NRES_MODE 0x08
#define DA9063_NONKEY_LOCK 0x10
+#define DA9063_BUCK_SLOWSTART 0x80
/* DA9063_REG_CONTROL_C (addr=0x10) */
#define DA9063_DEBOUNCING_MASK 0x07
@@ -466,6 +467,7 @@
#define DA9063_GPADC_PAUSE 0x02
#define DA9063_PMIF_DIS 0x04
#define DA9063_HS2WIRE_DIS 0x08
+#define DA9063_CLDR_PAUSE 0x10
#define DA9063_BBAT_DIS 0x20
#define DA9063_OUT_32K_PAUSE 0x40
#define DA9063_PMCONT_DIS 0x80
@@ -660,7 +662,7 @@
#define DA9063_GPIO15_TYPE_GPO 0x04
#define DA9063_GPIO15_NO_WAKEUP 0x80
-/* DA9063_REG_GPIO_MODE_0_7 (addr=0x1D) */
+/* DA9063_REG_GPIO_MODE0_7 (addr=0x1D) */
#define DA9063_GPIO0_MODE 0x01
#define DA9063_GPIO1_MODE 0x02
#define DA9063_GPIO2_MODE 0x04
@@ -670,7 +672,7 @@
#define DA9063_GPIO6_MODE 0x40
#define DA9063_GPIO7_MODE 0x80
-/* DA9063_REG_GPIO_MODE_8_15 (addr=0x1E) */
+/* DA9063_REG_GPIO_MODE8_15 (addr=0x1E) */
#define DA9063_GPIO8_MODE 0x01
#define DA9063_GPIO9_MODE 0x02
#define DA9063_GPIO10_MODE 0x04
@@ -702,12 +704,12 @@
#define DA9063_SWITCH_SR_5MV 0x10
#define DA9063_SWITCH_SR_10MV 0x20
#define DA9063_SWITCH_SR_50MV 0x30
-#define DA9063_SWITCH_SR_DIS 0x40
+#define DA9063_CORE_SW_INTERNAL 0x40
#define DA9063_CP_EN_MODE 0x80
/* DA9063_REGL_Bxxxx_CONT common bits (addr=0x20-0x25) */
#define DA9063_BUCK_EN 0x01
-#define DA9063_BUCK_GPI_MASK 0x06
+#define DA9063_BUCK_GPI_MASK 0x06
#define DA9063_BUCK_GPI_OFF 0x00
#define DA9063_BUCK_GPI_GPIO1 0x02
#define DA9063_BUCK_GPI_GPIO2 0x04
@@ -841,25 +843,27 @@
#define DA9063_COUNT_YEAR_MASK 0x3F
#define DA9063_MONITOR 0x40
-/* DA9063_REG_ALARM_MI (addr=0x46) */
+/* DA9063_REG_ALARM_S (addr=0x46) */
+#define DA9063_ALARM_S_MASK 0x3F
#define DA9063_ALARM_STATUS_ALARM 0x80
#define DA9063_ALARM_STATUS_TICK 0x40
+/* DA9063_REG_ALARM_MI (addr=0x47) */
#define DA9063_ALARM_MIN_MASK 0x3F
-/* DA9063_REG_ALARM_H (addr=0x47) */
+/* DA9063_REG_ALARM_H (addr=0x48) */
#define DA9063_ALARM_HOUR_MASK 0x1F
-/* DA9063_REG_ALARM_D (addr=0x48) */
+/* DA9063_REG_ALARM_D (addr=0x49) */
#define DA9063_ALARM_DAY_MASK 0x1F
-/* DA9063_REG_ALARM_MO (addr=0x49) */
+/* DA9063_REG_ALARM_MO (addr=0x4A) */
#define DA9063_TICK_WAKE 0x20
#define DA9063_TICK_TYPE 0x10
#define DA9063_TICK_TYPE_SEC 0x00
#define DA9063_TICK_TYPE_MIN 0x10
#define DA9063_ALARM_MONTH_MASK 0x0F
-/* DA9063_REG_ALARM_Y (addr=0x4A) */
+/* DA9063_REG_ALARM_Y (addr=0x4B) */
#define DA9063_TICK_ON 0x80
#define DA9063_ALARM_ON 0x40
#define DA9063_ALARM_YEAR_MASK 0x3F
@@ -906,7 +910,7 @@
/* DA9063_REG_Bxxxx_CFG common bits (addr=0x9D-0xA2) */
#define DA9063_BUCK_FB_MASK 0x07
-#define DA9063_BUCK_PD_DIS_SHIFT 5
+#define DA9063_BUCK_PD_DIS_MASK 0x20
#define DA9063_BUCK_MODE_MASK 0xC0
#define DA9063_BUCK_MODE_MANUAL 0x00
#define DA9063_BUCK_MODE_SLEEP 0x40
diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h
index 060e11256fbc..bf5109d38a26 100644
--- a/include/linux/mfd/dbx500-prcmu.h
+++ b/include/linux/mfd/dbx500-prcmu.h
@@ -183,8 +183,6 @@ struct prcmu_pdata
bool enable_set_ddr_opp;
bool enable_ape_opp_100_voltage;
struct ab8500_platform_data *ab_platdata;
- int ab_irq;
- int irq_base;
u32 version_offset;
u32 legacy_offset;
u32 adt_offset;
diff --git a/include/linux/mfd/lpc_ich.h b/include/linux/mfd/lpc_ich.h
index 3e1df644c407..8feac782fa83 100644
--- a/include/linux/mfd/lpc_ich.h
+++ b/include/linux/mfd/lpc_ich.h
@@ -21,23 +21,26 @@
#define LPC_ICH_H
/* Watchdog resources */
-#define ICH_RES_IO_TCO 0
-#define ICH_RES_IO_SMI 1
-#define ICH_RES_MEM_OFF 2
-#define ICH_RES_MEM_GCS 0
+#define ICH_RES_IO_TCO 0
+#define ICH_RES_IO_SMI 1
+#define ICH_RES_MEM_OFF 2
+#define ICH_RES_MEM_GCS_PMC 0
/* GPIO resources */
#define ICH_RES_GPIO 0
#define ICH_RES_GPE0 1
/* GPIO compatibility */
-#define ICH_I3100_GPIO 0x401
-#define ICH_V5_GPIO 0x501
-#define ICH_V6_GPIO 0x601
-#define ICH_V7_GPIO 0x701
-#define ICH_V9_GPIO 0x801
-#define ICH_V10CORP_GPIO 0xa01
-#define ICH_V10CONS_GPIO 0xa11
+enum {
+ ICH_I3100_GPIO,
+ ICH_V5_GPIO,
+ ICH_V6_GPIO,
+ ICH_V7_GPIO,
+ ICH_V9_GPIO,
+ ICH_V10CORP_GPIO,
+ ICH_V10CONS_GPIO,
+ AVOTON_GPIO,
+};
struct lpc_ich_info {
char name[32];
diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h
index a3d0185196d3..c9b332fb0d5d 100644
--- a/include/linux/mfd/max14577-private.h
+++ b/include/linux/mfd/max14577-private.h
@@ -248,14 +248,6 @@ enum max14577_charger_reg {
/* MAX14577 regulator SFOUT LDO voltage, fixed, uV */
#define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000
-enum max14577_irq_source {
- MAX14577_IRQ_INT1 = 0,
- MAX14577_IRQ_INT2,
- MAX14577_IRQ_INT3,
-
- MAX14577_IRQ_REGS_NUM,
-};
-
enum max14577_irq {
/* INT1 */
MAX14577_IRQ_INT1_ADC,
diff --git a/include/linux/mfd/max14577.h b/include/linux/mfd/max14577.h
index 247b021dfaaf..736d39c3ec0d 100644
--- a/include/linux/mfd/max14577.h
+++ b/include/linux/mfd/max14577.h
@@ -25,13 +25,8 @@
#ifndef __MAX14577_H__
#define __MAX14577_H__
-#include <linux/mfd/max14577-private.h>
#include <linux/regulator/consumer.h>
-/*
- * MAX14577 Regulator
- */
-
/* MAX14577 regulator IDs */
enum max14577_regulators {
MAX14577_SAFEOUT = 0,
diff --git a/include/linux/mfd/pm8xxx/irq.h b/include/linux/mfd/pm8xxx/irq.h
deleted file mode 100644
index f83d6b43ecbb..000000000000
--- a/include/linux/mfd/pm8xxx/irq.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-/*
- * Qualcomm PMIC irq 8xxx driver header file
- *
- */
-
-#ifndef __MFD_PM8XXX_IRQ_H
-#define __MFD_PM8XXX_IRQ_H
-
-#include <linux/errno.h>
-#include <linux/err.h>
-
-struct pm8xxx_irq_core_data {
- u32 rev;
- int nirqs;
-};
-
-struct pm8xxx_irq_platform_data {
- int irq_base;
- struct pm8xxx_irq_core_data irq_cdata;
- int devirq;
- int irq_trigger_flag;
-};
-
-struct pm_irq_chip;
-
-#ifdef CONFIG_MFD_PM8XXX_IRQ
-int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq);
-struct pm_irq_chip *pm8xxx_irq_init(struct device *dev,
- const struct pm8xxx_irq_platform_data *pdata);
-int pm8xxx_irq_exit(struct pm_irq_chip *chip);
-#else
-static inline int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
-{
- return -ENXIO;
-}
-static inline struct pm_irq_chip *pm8xxx_irq_init(
- const struct device *dev,
- const struct pm8xxx_irq_platform_data *pdata)
-{
- return ERR_PTR(-ENXIO);
-}
-static inline int pm8xxx_irq_exit(struct pm_irq_chip *chip)
-{
- return -ENXIO;
-}
-#endif /* CONFIG_MFD_PM8XXX_IRQ */
-#endif /* __MFD_PM8XXX_IRQ_H */
diff --git a/include/linux/mfd/pm8xxx/rtc.h b/include/linux/mfd/pm8xxx/rtc.h
deleted file mode 100644
index 14f1983eaecc..000000000000
--- a/include/linux/mfd/pm8xxx/rtc.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __RTC_PM8XXX_H__
-#define __RTC_PM8XXX_H__
-
-#define PM8XXX_RTC_DEV_NAME "rtc-pm8xxx"
-/**
- * struct pm8xxx_rtc_pdata - RTC driver platform data
- * @rtc_write_enable: variable stating RTC write capability
- */
-struct pm8xxx_rtc_platform_data {
- bool rtc_write_enable;
-};
-
-#endif /* __RTC_PM8XXX_H__ */
diff --git a/include/linux/mfd/rtsx_usb.h b/include/linux/mfd/rtsx_usb.h
new file mode 100644
index 000000000000..c446e4fd6b5c
--- /dev/null
+++ b/include/linux/mfd/rtsx_usb.h
@@ -0,0 +1,628 @@
+/* Driver for Realtek RTS5139 USB card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Roger Tseng <rogerable@realtek.com>
+ */
+
+#ifndef __RTSX_USB_H
+#define __RTSX_USB_H
+
+#include <linux/usb.h>
+
+/* related module names */
+#define RTSX_USB_SD_CARD 0
+#define RTSX_USB_MS_CARD 1
+
+/* endpoint numbers */
+#define EP_BULK_OUT 1
+#define EP_BULK_IN 2
+#define EP_INTR_IN 3
+
+/* USB vendor requests */
+#define RTSX_USB_REQ_REG_OP 0x00
+#define RTSX_USB_REQ_POLL 0x02
+
+/* miscellaneous parameters */
+#define MIN_DIV_N 60
+#define MAX_DIV_N 120
+
+#define MAX_PHASE 15
+#define RX_TUNING_CNT 3
+
+#define QFN24 0
+#define LQFP48 1
+#define CHECK_PKG(ucr, pkg) ((ucr)->package == (pkg))
+
+/* data structures */
+struct rtsx_ucr {
+ u16 vendor_id;
+ u16 product_id;
+
+ int package;
+ u8 ic_version;
+ bool is_rts5179;
+
+ unsigned int cur_clk;
+
+ u8 *cmd_buf;
+ unsigned int cmd_idx;
+ u8 *rsp_buf;
+
+ struct usb_device *pusb_dev;
+ struct usb_interface *pusb_intf;
+ struct usb_sg_request current_sg;
+ unsigned char *iobuf;
+ dma_addr_t iobuf_dma;
+
+ struct timer_list sg_timer;
+ struct mutex dev_mutex;
+};
+
+/* buffer size */
+#define IOBUF_SIZE 1024
+
+/* prototypes of exported functions */
+extern int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status);
+
+extern int rtsx_usb_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data);
+extern int rtsx_usb_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask,
+ u8 data);
+
+extern int rtsx_usb_ep0_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask,
+ u8 data);
+extern int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr,
+ u8 *data);
+
+extern void rtsx_usb_add_cmd(struct rtsx_ucr *ucr, u8 cmd_type,
+ u16 reg_addr, u8 mask, u8 data);
+extern int rtsx_usb_send_cmd(struct rtsx_ucr *ucr, u8 flag, int timeout);
+extern int rtsx_usb_get_rsp(struct rtsx_ucr *ucr, int rsp_len, int timeout);
+extern int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe,
+ void *buf, unsigned int len, int use_sg,
+ unsigned int *act_len, int timeout);
+
+extern int rtsx_usb_read_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len);
+extern int rtsx_usb_write_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len);
+extern int rtsx_usb_switch_clock(struct rtsx_ucr *ucr, unsigned int card_clock,
+ u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk);
+extern int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card);
+
+/* card status */
+#define SD_CD 0x01
+#define MS_CD 0x02
+#define XD_CD 0x04
+#define CD_MASK (SD_CD | MS_CD | XD_CD)
+#define SD_WP 0x08
+
+/* reader command field offset & parameters */
+#define READ_REG_CMD 0
+#define WRITE_REG_CMD 1
+#define CHECK_REG_CMD 2
+
+#define PACKET_TYPE 4
+#define CNT_H 5
+#define CNT_L 6
+#define STAGE_FLAG 7
+#define CMD_OFFSET 8
+#define SEQ_WRITE_DATA_OFFSET 12
+
+#define BATCH_CMD 0
+#define SEQ_READ 1
+#define SEQ_WRITE 2
+
+#define STAGE_R 0x01
+#define STAGE_DI 0x02
+#define STAGE_DO 0x04
+#define STAGE_MS_STATUS 0x08
+#define STAGE_XD_STATUS 0x10
+#define MODE_C 0x00
+#define MODE_CR (STAGE_R)
+#define MODE_CDIR (STAGE_R | STAGE_DI)
+#define MODE_CDOR (STAGE_R | STAGE_DO)
+
+#define EP0_OP_SHIFT 14
+#define EP0_READ_REG_CMD 2
+#define EP0_WRITE_REG_CMD 3
+
+#define rtsx_usb_cmd_hdr_tag(ucr) \
+ do { \
+ ucr->cmd_buf[0] = 'R'; \
+ ucr->cmd_buf[1] = 'T'; \
+ ucr->cmd_buf[2] = 'C'; \
+ ucr->cmd_buf[3] = 'R'; \
+ } while (0)
+
+static inline void rtsx_usb_init_cmd(struct rtsx_ucr *ucr)
+{
+ rtsx_usb_cmd_hdr_tag(ucr);
+ ucr->cmd_idx = 0;
+ ucr->cmd_buf[PACKET_TYPE] = BATCH_CMD;
+}
+
+/* internal register address */
+#define FPDCTL 0xFC00
+#define SSC_DIV_N_0 0xFC07
+#define SSC_CTL1 0xFC09
+#define SSC_CTL2 0xFC0A
+#define CFG_MODE 0xFC0E
+#define CFG_MODE_1 0xFC0F
+#define RCCTL 0xFC14
+#define SOF_WDOG 0xFC28
+#define SYS_DUMMY0 0xFC30
+
+#define MS_BLKEND 0xFD30
+#define MS_READ_START 0xFD31
+#define MS_READ_COUNT 0xFD32
+#define MS_WRITE_START 0xFD33
+#define MS_WRITE_COUNT 0xFD34
+#define MS_COMMAND 0xFD35
+#define MS_OLD_BLOCK_0 0xFD36
+#define MS_OLD_BLOCK_1 0xFD37
+#define MS_NEW_BLOCK_0 0xFD38
+#define MS_NEW_BLOCK_1 0xFD39
+#define MS_LOG_BLOCK_0 0xFD3A
+#define MS_LOG_BLOCK_1 0xFD3B
+#define MS_BUS_WIDTH 0xFD3C
+#define MS_PAGE_START 0xFD3D
+#define MS_PAGE_LENGTH 0xFD3E
+#define MS_CFG 0xFD40
+#define MS_TPC 0xFD41
+#define MS_TRANS_CFG 0xFD42
+#define MS_TRANSFER 0xFD43
+#define MS_INT_REG 0xFD44
+#define MS_BYTE_CNT 0xFD45
+#define MS_SECTOR_CNT_L 0xFD46
+#define MS_SECTOR_CNT_H 0xFD47
+#define MS_DBUS_H 0xFD48
+
+#define CARD_DMA1_CTL 0xFD5C
+#define CARD_PULL_CTL1 0xFD60
+#define CARD_PULL_CTL2 0xFD61
+#define CARD_PULL_CTL3 0xFD62
+#define CARD_PULL_CTL4 0xFD63
+#define CARD_PULL_CTL5 0xFD64
+#define CARD_PULL_CTL6 0xFD65
+#define CARD_EXIST 0xFD6F
+#define CARD_INT_PEND 0xFD71
+
+#define LDO_POWER_CFG 0xFD7B
+
+#define SD_CFG1 0xFDA0
+#define SD_CFG2 0xFDA1
+#define SD_CFG3 0xFDA2
+#define SD_STAT1 0xFDA3
+#define SD_STAT2 0xFDA4
+#define SD_BUS_STAT 0xFDA5
+#define SD_PAD_CTL 0xFDA6
+#define SD_SAMPLE_POINT_CTL 0xFDA7
+#define SD_PUSH_POINT_CTL 0xFDA8
+#define SD_CMD0 0xFDA9
+#define SD_CMD1 0xFDAA
+#define SD_CMD2 0xFDAB
+#define SD_CMD3 0xFDAC
+#define SD_CMD4 0xFDAD
+#define SD_CMD5 0xFDAE
+#define SD_BYTE_CNT_L 0xFDAF
+#define SD_BYTE_CNT_H 0xFDB0
+#define SD_BLOCK_CNT_L 0xFDB1
+#define SD_BLOCK_CNT_H 0xFDB2
+#define SD_TRANSFER 0xFDB3
+#define SD_CMD_STATE 0xFDB5
+#define SD_DATA_STATE 0xFDB6
+#define SD_VPCLK0_CTL 0xFC2A
+#define SD_VPCLK1_CTL 0xFC2B
+#define SD_DCMPS0_CTL 0xFC2C
+#define SD_DCMPS1_CTL 0xFC2D
+
+#define CARD_DMA1_CTL 0xFD5C
+
+#define HW_VERSION 0xFC01
+
+#define SSC_CLK_FPGA_SEL 0xFC02
+#define CLK_DIV 0xFC03
+#define SFSM_ED 0xFC04
+
+#define CD_DEGLITCH_WIDTH 0xFC20
+#define CD_DEGLITCH_EN 0xFC21
+#define AUTO_DELINK_EN 0xFC23
+
+#define FPGA_PULL_CTL 0xFC1D
+#define CARD_CLK_SOURCE 0xFC2E
+
+#define CARD_SHARE_MODE 0xFD51
+#define CARD_DRIVE_SEL 0xFD52
+#define CARD_STOP 0xFD53
+#define CARD_OE 0xFD54
+#define CARD_AUTO_BLINK 0xFD55
+#define CARD_GPIO 0xFD56
+#define SD30_DRIVE_SEL 0xFD57
+
+#define CARD_DATA_SOURCE 0xFD5D
+#define CARD_SELECT 0xFD5E
+
+#define CARD_CLK_EN 0xFD79
+#define CARD_PWR_CTL 0xFD7A
+
+#define OCPCTL 0xFD80
+#define OCPPARA1 0xFD81
+#define OCPPARA2 0xFD82
+#define OCPSTAT 0xFD83
+
+#define HS_USB_STAT 0xFE01
+#define HS_VCONTROL 0xFE26
+#define HS_VSTAIN 0xFE27
+#define HS_VLOADM 0xFE28
+#define HS_VSTAOUT 0xFE29
+
+#define MC_IRQ 0xFF00
+#define MC_IRQEN 0xFF01
+#define MC_FIFO_CTL 0xFF02
+#define MC_FIFO_BC0 0xFF03
+#define MC_FIFO_BC1 0xFF04
+#define MC_FIFO_STAT 0xFF05
+#define MC_FIFO_MODE 0xFF06
+#define MC_FIFO_RD_PTR0 0xFF07
+#define MC_FIFO_RD_PTR1 0xFF08
+#define MC_DMA_CTL 0xFF10
+#define MC_DMA_TC0 0xFF11
+#define MC_DMA_TC1 0xFF12
+#define MC_DMA_TC2 0xFF13
+#define MC_DMA_TC3 0xFF14
+#define MC_DMA_RST 0xFF15
+
+#define RBUF_SIZE_MASK 0xFBFF
+#define RBUF_BASE 0xF000
+#define PPBUF_BASE1 0xF800
+#define PPBUF_BASE2 0xFA00
+
+/* internal register value macros */
+#define POWER_OFF 0x03
+#define PARTIAL_POWER_ON 0x02
+#define POWER_ON 0x00
+#define POWER_MASK 0x03
+#define LDO3318_PWR_MASK 0x0C
+#define LDO_ON 0x00
+#define LDO_SUSPEND 0x08
+#define LDO_OFF 0x0C
+#define DV3318_AUTO_PWR_OFF 0x10
+#define FORCE_LDO_POWERB 0x60
+
+/* LDO_POWER_CFG */
+#define TUNE_SD18_MASK 0x1C
+#define TUNE_SD18_1V7 0x00
+#define TUNE_SD18_1V8 (0x01 << 2)
+#define TUNE_SD18_1V9 (0x02 << 2)
+#define TUNE_SD18_2V0 (0x03 << 2)
+#define TUNE_SD18_2V7 (0x04 << 2)
+#define TUNE_SD18_2V8 (0x05 << 2)
+#define TUNE_SD18_2V9 (0x06 << 2)
+#define TUNE_SD18_3V3 (0x07 << 2)
+
+/* CLK_DIV */
+#define CLK_CHANGE 0x80
+#define CLK_DIV_1 0x00
+#define CLK_DIV_2 0x01
+#define CLK_DIV_4 0x02
+#define CLK_DIV_8 0x03
+
+#define SSC_POWER_MASK 0x01
+#define SSC_POWER_DOWN 0x01
+#define SSC_POWER_ON 0x00
+
+#define FPGA_VER 0x80
+#define HW_VER_MASK 0x0F
+
+#define EXTEND_DMA1_ASYNC_SIGNAL 0x02
+
+/* CFG_MODE*/
+#define XTAL_FREE 0x80
+#define CLK_MODE_MASK 0x03
+#define CLK_MODE_12M_XTAL 0x00
+#define CLK_MODE_NON_XTAL 0x01
+#define CLK_MODE_24M_OSC 0x02
+#define CLK_MODE_48M_OSC 0x03
+
+/* CFG_MODE_1*/
+#define RTS5179 0x02
+
+#define NYET_EN 0x01
+#define NYET_MSAK 0x01
+
+#define SD30_DRIVE_MASK 0x07
+#define SD20_DRIVE_MASK 0x03
+
+#define DISABLE_SD_CD 0x08
+#define DISABLE_MS_CD 0x10
+#define DISABLE_XD_CD 0x20
+#define SD_CD_DEGLITCH_EN 0x01
+#define MS_CD_DEGLITCH_EN 0x02
+#define XD_CD_DEGLITCH_EN 0x04
+
+#define CARD_SHARE_LQFP48 0x04
+#define CARD_SHARE_QFN24 0x00
+#define CARD_SHARE_LQFP_SEL 0x04
+#define CARD_SHARE_XD 0x00
+#define CARD_SHARE_SD 0x01
+#define CARD_SHARE_MS 0x02
+#define CARD_SHARE_MASK 0x03
+
+
+/* SD30_DRIVE_SEL */
+#define DRIVER_TYPE_A 0x05
+#define DRIVER_TYPE_B 0x03
+#define DRIVER_TYPE_C 0x02
+#define DRIVER_TYPE_D 0x01
+
+/* SD_BUS_STAT */
+#define SD_CLK_TOGGLE_EN 0x80
+#define SD_CLK_FORCE_STOP 0x40
+#define SD_DAT3_STATUS 0x10
+#define SD_DAT2_STATUS 0x08
+#define SD_DAT1_STATUS 0x04
+#define SD_DAT0_STATUS 0x02
+#define SD_CMD_STATUS 0x01
+
+/* SD_PAD_CTL */
+#define SD_IO_USING_1V8 0x80
+#define SD_IO_USING_3V3 0x7F
+#define TYPE_A_DRIVING 0x00
+#define TYPE_B_DRIVING 0x01
+#define TYPE_C_DRIVING 0x02
+#define TYPE_D_DRIVING 0x03
+
+/* CARD_CLK_EN */
+#define SD_CLK_EN 0x04
+#define MS_CLK_EN 0x08
+
+/* CARD_SELECT */
+#define SD_MOD_SEL 2
+#define MS_MOD_SEL 3
+
+/* CARD_SHARE_MODE */
+#define CARD_SHARE_LQFP48 0x04
+#define CARD_SHARE_QFN24 0x00
+#define CARD_SHARE_LQFP_SEL 0x04
+#define CARD_SHARE_XD 0x00
+#define CARD_SHARE_SD 0x01
+#define CARD_SHARE_MS 0x02
+#define CARD_SHARE_MASK 0x03
+
+/* SSC_CTL1 */
+#define SSC_RSTB 0x80
+#define SSC_8X_EN 0x40
+#define SSC_FIX_FRAC 0x20
+#define SSC_SEL_1M 0x00
+#define SSC_SEL_2M 0x08
+#define SSC_SEL_4M 0x10
+#define SSC_SEL_8M 0x18
+
+/* SSC_CTL2 */
+#define SSC_DEPTH_MASK 0x03
+#define SSC_DEPTH_DISALBE 0x00
+#define SSC_DEPTH_2M 0x01
+#define SSC_DEPTH_1M 0x02
+#define SSC_DEPTH_512K 0x03
+
+/* SD_VPCLK0_CTL */
+#define PHASE_CHANGE 0x80
+#define PHASE_NOT_RESET 0x40
+
+/* SD_TRANSFER */
+#define SD_TRANSFER_START 0x80
+#define SD_TRANSFER_END 0x40
+#define SD_STAT_IDLE 0x20
+#define SD_TRANSFER_ERR 0x10
+#define SD_TM_NORMAL_WRITE 0x00
+#define SD_TM_AUTO_WRITE_3 0x01
+#define SD_TM_AUTO_WRITE_4 0x02
+#define SD_TM_AUTO_READ_3 0x05
+#define SD_TM_AUTO_READ_4 0x06
+#define SD_TM_CMD_RSP 0x08
+#define SD_TM_AUTO_WRITE_1 0x09
+#define SD_TM_AUTO_WRITE_2 0x0A
+#define SD_TM_NORMAL_READ 0x0C
+#define SD_TM_AUTO_READ_1 0x0D
+#define SD_TM_AUTO_READ_2 0x0E
+#define SD_TM_AUTO_TUNING 0x0F
+
+/* SD_CFG1 */
+#define SD_CLK_DIVIDE_0 0x00
+#define SD_CLK_DIVIDE_256 0xC0
+#define SD_CLK_DIVIDE_128 0x80
+#define SD_CLK_DIVIDE_MASK 0xC0
+#define SD_BUS_WIDTH_1BIT 0x00
+#define SD_BUS_WIDTH_4BIT 0x01
+#define SD_BUS_WIDTH_8BIT 0x02
+#define SD_ASYNC_FIFO_RST 0x10
+#define SD_20_MODE 0x00
+#define SD_DDR_MODE 0x04
+#define SD_30_MODE 0x08
+
+/* SD_CFG2 */
+#define SD_CALCULATE_CRC7 0x00
+#define SD_NO_CALCULATE_CRC7 0x80
+#define SD_CHECK_CRC16 0x00
+#define SD_NO_CHECK_CRC16 0x40
+#define SD_WAIT_CRC_TO_EN 0x20
+#define SD_WAIT_BUSY_END 0x08
+#define SD_NO_WAIT_BUSY_END 0x00
+#define SD_CHECK_CRC7 0x00
+#define SD_NO_CHECK_CRC7 0x04
+#define SD_RSP_LEN_0 0x00
+#define SD_RSP_LEN_6 0x01
+#define SD_RSP_LEN_17 0x02
+#define SD_RSP_TYPE_R0 0x04
+#define SD_RSP_TYPE_R1 0x01
+#define SD_RSP_TYPE_R1b 0x09
+#define SD_RSP_TYPE_R2 0x02
+#define SD_RSP_TYPE_R3 0x05
+#define SD_RSP_TYPE_R4 0x05
+#define SD_RSP_TYPE_R5 0x01
+#define SD_RSP_TYPE_R6 0x01
+#define SD_RSP_TYPE_R7 0x01
+
+/* SD_STAT1 */
+#define SD_CRC7_ERR 0x80
+#define SD_CRC16_ERR 0x40
+#define SD_CRC_WRITE_ERR 0x20
+#define SD_CRC_WRITE_ERR_MASK 0x1C
+#define GET_CRC_TIME_OUT 0x02
+#define SD_TUNING_COMPARE_ERR 0x01
+
+/* SD_DATA_STATE */
+#define SD_DATA_IDLE 0x80
+
+/* CARD_DATA_SOURCE */
+#define PINGPONG_BUFFER 0x01
+#define RING_BUFFER 0x00
+
+/* CARD_OE */
+#define SD_OUTPUT_EN 0x04
+#define MS_OUTPUT_EN 0x08
+
+/* CARD_STOP */
+#define SD_STOP 0x04
+#define MS_STOP 0x08
+#define SD_CLR_ERR 0x40
+#define MS_CLR_ERR 0x80
+
+/* CARD_CLK_SOURCE */
+#define CRC_FIX_CLK (0x00 << 0)
+#define CRC_VAR_CLK0 (0x01 << 0)
+#define CRC_VAR_CLK1 (0x02 << 0)
+#define SD30_FIX_CLK (0x00 << 2)
+#define SD30_VAR_CLK0 (0x01 << 2)
+#define SD30_VAR_CLK1 (0x02 << 2)
+#define SAMPLE_FIX_CLK (0x00 << 4)
+#define SAMPLE_VAR_CLK0 (0x01 << 4)
+#define SAMPLE_VAR_CLK1 (0x02 << 4)
+
+/* SD_SAMPLE_POINT_CTL */
+#define DDR_FIX_RX_DAT 0x00
+#define DDR_VAR_RX_DAT 0x80
+#define DDR_FIX_RX_DAT_EDGE 0x00
+#define DDR_FIX_RX_DAT_14_DELAY 0x40
+#define DDR_FIX_RX_CMD 0x00
+#define DDR_VAR_RX_CMD 0x20
+#define DDR_FIX_RX_CMD_POS_EDGE 0x00
+#define DDR_FIX_RX_CMD_14_DELAY 0x10
+#define SD20_RX_POS_EDGE 0x00
+#define SD20_RX_14_DELAY 0x08
+#define SD20_RX_SEL_MASK 0x08
+
+/* SD_PUSH_POINT_CTL */
+#define DDR_FIX_TX_CMD_DAT 0x00
+#define DDR_VAR_TX_CMD_DAT 0x80
+#define DDR_FIX_TX_DAT_14_TSU 0x00
+#define DDR_FIX_TX_DAT_12_TSU 0x40
+#define DDR_FIX_TX_CMD_NEG_EDGE 0x00
+#define DDR_FIX_TX_CMD_14_AHEAD 0x20
+#define SD20_TX_NEG_EDGE 0x00
+#define SD20_TX_14_AHEAD 0x10
+#define SD20_TX_SEL_MASK 0x10
+#define DDR_VAR_SDCLK_POL_SWAP 0x01
+
+/* MS_CFG */
+#define SAMPLE_TIME_RISING 0x00
+#define SAMPLE_TIME_FALLING 0x80
+#define PUSH_TIME_DEFAULT 0x00
+#define PUSH_TIME_ODD 0x40
+#define NO_EXTEND_TOGGLE 0x00
+#define EXTEND_TOGGLE_CHK 0x20
+#define MS_BUS_WIDTH_1 0x00
+#define MS_BUS_WIDTH_4 0x10
+#define MS_BUS_WIDTH_8 0x18
+#define MS_2K_SECTOR_MODE 0x04
+#define MS_512_SECTOR_MODE 0x00
+#define MS_TOGGLE_TIMEOUT_EN 0x00
+#define MS_TOGGLE_TIMEOUT_DISEN 0x01
+#define MS_NO_CHECK_INT 0x02
+
+/* MS_TRANS_CFG */
+#define WAIT_INT 0x80
+#define NO_WAIT_INT 0x00
+#define NO_AUTO_READ_INT_REG 0x00
+#define AUTO_READ_INT_REG 0x40
+#define MS_CRC16_ERR 0x20
+#define MS_RDY_TIMEOUT 0x10
+#define MS_INT_CMDNK 0x08
+#define MS_INT_BREQ 0x04
+#define MS_INT_ERR 0x02
+#define MS_INT_CED 0x01
+
+/* MS_TRANSFER */
+#define MS_TRANSFER_START 0x80
+#define MS_TRANSFER_END 0x40
+#define MS_TRANSFER_ERR 0x20
+#define MS_BS_STATE 0x10
+#define MS_TM_READ_BYTES 0x00
+#define MS_TM_NORMAL_READ 0x01
+#define MS_TM_WRITE_BYTES 0x04
+#define MS_TM_NORMAL_WRITE 0x05
+#define MS_TM_AUTO_READ 0x08
+#define MS_TM_AUTO_WRITE 0x0C
+#define MS_TM_SET_CMD 0x06
+#define MS_TM_COPY_PAGE 0x07
+#define MS_TM_MULTI_READ 0x02
+#define MS_TM_MULTI_WRITE 0x03
+
+/* MC_FIFO_CTL */
+#define FIFO_FLUSH 0x01
+
+/* MC_DMA_RST */
+#define DMA_RESET 0x01
+
+/* MC_DMA_CTL */
+#define DMA_TC_EQ_0 0x80
+#define DMA_DIR_TO_CARD 0x00
+#define DMA_DIR_FROM_CARD 0x02
+#define DMA_EN 0x01
+#define DMA_128 (0 << 2)
+#define DMA_256 (1 << 2)
+#define DMA_512 (2 << 2)
+#define DMA_1024 (3 << 2)
+#define DMA_PACK_SIZE_MASK 0x0C
+
+/* CARD_INT_PEND */
+#define XD_INT 0x10
+#define MS_INT 0x08
+#define SD_INT 0x04
+
+/* LED operations*/
+static inline int rtsx_usb_turn_on_led(struct rtsx_ucr *ucr)
+{
+ return rtsx_usb_ep0_write_register(ucr, CARD_GPIO, 0x03, 0x02);
+}
+
+static inline int rtsx_usb_turn_off_led(struct rtsx_ucr *ucr)
+{
+ return rtsx_usb_ep0_write_register(ucr, CARD_GPIO, 0x03, 0x03);
+}
+
+/* HW error clearing */
+static inline void rtsx_usb_clear_fsm_err(struct rtsx_ucr *ucr)
+{
+ rtsx_usb_ep0_write_register(ucr, SFSM_ED, 0xf8, 0xf8);
+}
+
+static inline void rtsx_usb_clear_dma_err(struct rtsx_ucr *ucr)
+{
+ rtsx_usb_ep0_write_register(ucr, MC_FIFO_CTL,
+ FIFO_FLUSH, FIFO_FLUSH);
+ rtsx_usb_ep0_write_register(ucr, MC_DMA_RST, DMA_RESET, DMA_RESET);
+}
+#endif /* __RTS51139_H */
diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
index 866e355fa409..ff44374a1a4e 100644
--- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
+++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
@@ -242,6 +242,24 @@
#define IMX6Q_GPR5_L2_CLK_STOP BIT(8)
+#define IMX6Q_GPR6_IPU1_ID00_WR_QOS_MASK (0xf << 0)
+#define IMX6Q_GPR6_IPU1_ID01_WR_QOS_MASK (0xf << 4)
+#define IMX6Q_GPR6_IPU1_ID10_WR_QOS_MASK (0xf << 8)
+#define IMX6Q_GPR6_IPU1_ID11_WR_QOS_MASK (0xf << 12)
+#define IMX6Q_GPR6_IPU1_ID00_RD_QOS_MASK (0xf << 16)
+#define IMX6Q_GPR6_IPU1_ID01_RD_QOS_MASK (0xf << 20)
+#define IMX6Q_GPR6_IPU1_ID10_RD_QOS_MASK (0xf << 24)
+#define IMX6Q_GPR6_IPU1_ID11_RD_QOS_MASK (0xf << 28)
+
+#define IMX6Q_GPR7_IPU2_ID00_WR_QOS_MASK (0xf << 0)
+#define IMX6Q_GPR7_IPU2_ID01_WR_QOS_MASK (0xf << 4)
+#define IMX6Q_GPR7_IPU2_ID10_WR_QOS_MASK (0xf << 8)
+#define IMX6Q_GPR7_IPU2_ID11_WR_QOS_MASK (0xf << 12)
+#define IMX6Q_GPR7_IPU2_ID00_RD_QOS_MASK (0xf << 16)
+#define IMX6Q_GPR7_IPU2_ID01_RD_QOS_MASK (0xf << 20)
+#define IMX6Q_GPR7_IPU2_ID10_RD_QOS_MASK (0xf << 24)
+#define IMX6Q_GPR7_IPU2_ID11_RD_QOS_MASK (0xf << 28)
+
#define IMX6Q_GPR8_TX_SWING_LOW (0x7f << 25)
#define IMX6Q_GPR8_TX_SWING_FULL (0x7f << 18)
#define IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB (0x3f << 12)
diff --git a/include/linux/mfd/tps65218.h b/include/linux/mfd/tps65218.h
new file mode 100644
index 000000000000..d2e357df5a0e
--- /dev/null
+++ b/include/linux/mfd/tps65218.h
@@ -0,0 +1,284 @@
+/*
+ * linux/mfd/tps65218.h
+ *
+ * Functions to access TPS65219 power management chip.
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.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.
+ */
+
+#ifndef __LINUX_MFD_TPS65218_H
+#define __LINUX_MFD_TPS65218_H
+
+#include <linux/i2c.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/bitops.h>
+
+/* TPS chip id list */
+#define TPS65218 0xF0
+
+/* I2C ID for TPS65218 part */
+#define TPS65218_I2C_ID 0x24
+
+/* All register addresses */
+#define TPS65218_REG_CHIPID 0x00
+#define TPS65218_REG_INT1 0x01
+#define TPS65218_REG_INT2 0x02
+#define TPS65218_REG_INT_MASK1 0x03
+#define TPS65218_REG_INT_MASK2 0x04
+#define TPS65218_REG_STATUS 0x05
+#define TPS65218_REG_CONTROL 0x06
+#define TPS65218_REG_FLAG 0x07
+
+#define TPS65218_REG_PASSWORD 0x10
+#define TPS65218_REG_ENABLE1 0x11
+#define TPS65218_REG_ENABLE2 0x12
+#define TPS65218_REG_CONFIG1 0x13
+#define TPS65218_REG_CONFIG2 0x14
+#define TPS65218_REG_CONFIG3 0x15
+#define TPS65218_REG_CONTROL_DCDC1 0x16
+#define TPS65218_REG_CONTROL_DCDC2 0x17
+#define TPS65218_REG_CONTROL_DCDC3 0x18
+#define TPS65218_REG_CONTROL_DCDC4 0x19
+#define TPS65218_REG_CONTRL_SLEW_RATE 0x1A
+#define TPS65218_REG_CONTROL_LDO1 0x1B
+#define TPS65218_REG_SEQ1 0x20
+#define TPS65218_REG_SEQ2 0x21
+#define TPS65218_REG_SEQ3 0x22
+#define TPS65218_REG_SEQ4 0x23
+#define TPS65218_REG_SEQ5 0x24
+#define TPS65218_REG_SEQ6 0x25
+#define TPS65218_REG_SEQ7 0x26
+
+/* Register field definitions */
+#define TPS65218_CHIPID_CHIP_MASK 0xF8
+#define TPS65218_CHIPID_REV_MASK 0x07
+
+#define TPS65218_INT1_VPRG BIT(5)
+#define TPS65218_INT1_AC BIT(4)
+#define TPS65218_INT1_PB BIT(3)
+#define TPS65218_INT1_HOT BIT(2)
+#define TPS65218_INT1_CC_AQC BIT(1)
+#define TPS65218_INT1_PRGC BIT(0)
+
+#define TPS65218_INT2_LS3_F BIT(5)
+#define TPS65218_INT2_LS2_F BIT(4)
+#define TPS65218_INT2_LS1_F BIT(3)
+#define TPS65218_INT2_LS3_I BIT(2)
+#define TPS65218_INT2_LS2_I BIT(1)
+#define TPS65218_INT2_LS1_I BIT(0)
+
+#define TPS65218_INT_MASK1_VPRG BIT(5)
+#define TPS65218_INT_MASK1_AC BIT(4)
+#define TPS65218_INT_MASK1_PB BIT(3)
+#define TPS65218_INT_MASK1_HOT BIT(2)
+#define TPS65218_INT_MASK1_CC_AQC BIT(1)
+#define TPS65218_INT_MASK1_PRGC BIT(0)
+
+#define TPS65218_INT_MASK2_LS3_F BIT(5)
+#define TPS65218_INT_MASK2_LS2_F BIT(4)
+#define TPS65218_INT_MASK2_LS1_F BIT(3)
+#define TPS65218_INT_MASK2_LS3_I BIT(2)
+#define TPS65218_INT_MASK2_LS2_I BIT(1)
+#define TPS65218_INT_MASK2_LS1_I BIT(0)
+
+#define TPS65218_STATUS_FSEAL BIT(7)
+#define TPS65218_STATUS_EE BIT(6)
+#define TPS65218_STATUS_AC_STATE BIT(5)
+#define TPS65218_STATUS_PB_STATE BIT(4)
+#define TPS65218_STATUS_STATE_MASK 0xC
+#define TPS65218_STATUS_CC_STAT 0x3
+
+#define TPS65218_CONTROL_OFFNPFO BIT(1)
+#define TPS65218_CONTROL_CC_AQ BIT(0)
+
+#define TPS65218_FLAG_GPO3_FLG BIT(7)
+#define TPS65218_FLAG_GPO2_FLG BIT(6)
+#define TPS65218_FLAG_GPO1_FLG BIT(5)
+#define TPS65218_FLAG_LDO1_FLG BIT(4)
+#define TPS65218_FLAG_DC4_FLG BIT(3)
+#define TPS65218_FLAG_DC3_FLG BIT(2)
+#define TPS65218_FLAG_DC2_FLG BIT(1)
+#define TPS65218_FLAG_DC1_FLG BIT(0)
+
+#define TPS65218_ENABLE1_DC6_EN BIT(5)
+#define TPS65218_ENABLE1_DC5_EN BIT(4)
+#define TPS65218_ENABLE1_DC4_EN BIT(3)
+#define TPS65218_ENABLE1_DC3_EN BIT(2)
+#define TPS65218_ENABLE1_DC2_EN BIT(1)
+#define TPS65218_ENABLE1_DC1_EN BIT(0)
+
+#define TPS65218_ENABLE2_GPIO3 BIT(6)
+#define TPS65218_ENABLE2_GPIO2 BIT(5)
+#define TPS65218_ENABLE2_GPIO1 BIT(4)
+#define TPS65218_ENABLE2_LS3_EN BIT(3)
+#define TPS65218_ENABLE2_LS2_EN BIT(2)
+#define TPS65218_ENABLE2_LS1_EN BIT(1)
+#define TPS65218_ENABLE2_LDO1_EN BIT(0)
+
+
+#define TPS65218_CONFIG1_TRST BIT(7)
+#define TPS65218_CONFIG1_GPO2_BUF BIT(6)
+#define TPS65218_CONFIG1_IO1_SEL BIT(5)
+#define TPS65218_CONFIG1_PGDLY_MASK 0x18
+#define TPS65218_CONFIG1_STRICT BIT(2)
+#define TPS65218_CONFIG1_UVLO_MASK 0x3
+
+#define TPS65218_CONFIG2_DC12_RST BIT(7)
+#define TPS65218_CONFIG2_UVLOHYS BIT(6)
+#define TPS65218_CONFIG2_LS3ILIM_MASK 0xC
+#define TPS65218_CONFIG2_LS2ILIM_MASK 0x3
+
+#define TPS65218_CONFIG3_LS3NPFO BIT(5)
+#define TPS65218_CONFIG3_LS2NPFO BIT(4)
+#define TPS65218_CONFIG3_LS1NPFO BIT(3)
+#define TPS65218_CONFIG3_LS3DCHRG BIT(2)
+#define TPS65218_CONFIG3_LS2DCHRG BIT(1)
+#define TPS65218_CONFIG3_LS1DCHRG BIT(0)
+
+#define TPS65218_CONTROL_DCDC1_PFM BIT(7)
+#define TPS65218_CONTROL_DCDC1_MASK 0x7F
+
+#define TPS65218_CONTROL_DCDC2_PFM BIT(7)
+#define TPS65218_CONTROL_DCDC2_MASK 0x3F
+
+#define TPS65218_CONTROL_DCDC3_PFM BIT(7)
+#define TPS65218_CONTROL_DCDC3_MASK 0x3F
+
+#define TPS65218_CONTROL_DCDC4_PFM BIT(7)
+#define TPS65218_CONTROL_DCDC4_MASK 0x3F
+
+#define TPS65218_SLEW_RATE_GO BIT(7)
+#define TPS65218_SLEW_RATE_GODSBL BIT(6)
+#define TPS65218_SLEW_RATE_SLEW_MASK 0x7
+
+#define TPS65218_CONTROL_LDO1_MASK 0x3F
+
+#define TPS65218_SEQ1_DLY8 BIT(7)
+#define TPS65218_SEQ1_DLY7 BIT(6)
+#define TPS65218_SEQ1_DLY6 BIT(5)
+#define TPS65218_SEQ1_DLY5 BIT(4)
+#define TPS65218_SEQ1_DLY4 BIT(3)
+#define TPS65218_SEQ1_DLY3 BIT(2)
+#define TPS65218_SEQ1_DLY2 BIT(1)
+#define TPS65218_SEQ1_DLY1 BIT(0)
+
+#define TPS65218_SEQ2_DLYFCTR BIT(7)
+#define TPS65218_SEQ2_DLY9 BIT(0)
+
+#define TPS65218_SEQ3_DC2_SEQ_MASK 0xF0
+#define TPS65218_SEQ3_DC1_SEQ_MASK 0xF
+
+#define TPS65218_SEQ4_DC4_SEQ_MASK 0xF0
+#define TPS65218_SEQ4_DC3_SEQ_MASK 0xF
+
+#define TPS65218_SEQ5_DC6_SEQ_MASK 0xF0
+#define TPS65218_SEQ5_DC5_SEQ_MASK 0xF
+
+#define TPS65218_SEQ6_LS1_SEQ_MASK 0xF0
+#define TPS65218_SEQ6_LDO1_SEQ_MASK 0xF
+
+#define TPS65218_SEQ7_GPO3_SEQ_MASK 0xF0
+#define TPS65218_SEQ7_GPO1_SEQ_MASK 0xF
+#define TPS65218_PROTECT_NONE 0
+#define TPS65218_PROTECT_L1 1
+
+enum tps65218_regulator_id {
+ /* DCDC's */
+ TPS65218_DCDC_1,
+ TPS65218_DCDC_2,
+ TPS65218_DCDC_3,
+ TPS65218_DCDC_4,
+ TPS65218_DCDC_5,
+ TPS65218_DCDC_6,
+ /* LDOs */
+ TPS65218_LDO_1,
+};
+
+#define TPS65218_MAX_REG_ID TPS65218_LDO_1
+
+/* Number of step-down converters available */
+#define TPS65218_NUM_DCDC 6
+/* Number of LDO voltage regulators available */
+#define TPS65218_NUM_LDO 1
+/* Number of total regulators available */
+#define TPS65218_NUM_REGULATOR (TPS65218_NUM_DCDC + TPS65218_NUM_LDO)
+
+/* Define the TPS65218 IRQ numbers */
+enum tps65218_irqs {
+ /* INT1 registers */
+ TPS65218_PRGC_IRQ,
+ TPS65218_CC_AQC_IRQ,
+ TPS65218_HOT_IRQ,
+ TPS65218_PB_IRQ,
+ TPS65218_AC_IRQ,
+ TPS65218_VPRG_IRQ,
+ TPS65218_INVALID1_IRQ,
+ TPS65218_INVALID2_IRQ,
+ /* INT2 registers */
+ TPS65218_LS1_I_IRQ,
+ TPS65218_LS2_I_IRQ,
+ TPS65218_LS3_I_IRQ,
+ TPS65218_LS1_F_IRQ,
+ TPS65218_LS2_F_IRQ,
+ TPS65218_LS3_F_IRQ,
+ TPS65218_INVALID3_IRQ,
+ TPS65218_INVALID4_IRQ,
+};
+
+/**
+ * struct tps_info - packages regulator constraints
+ * @id: Id of the regulator
+ * @name: Voltage regulator name
+ * @min_uV: minimum micro volts
+ * @max_uV: minimum micro volts
+ *
+ * This data is used to check the regualtor voltage limits while setting.
+ */
+struct tps_info {
+ int id;
+ const char *name;
+ int min_uV;
+ int max_uV;
+};
+
+/**
+ * struct tps65218 - tps65218 sub-driver chip access routines
+ *
+ * Device data may be used to access the TPS65218 chip
+ */
+
+struct tps65218 {
+ struct device *dev;
+ unsigned int id;
+
+ struct mutex tps_lock; /* lock guarding the data structure */
+ /* IRQ Data */
+ int irq;
+ u32 irq_mask;
+ struct regmap_irq_chip_data *irq_data;
+ struct regulator_desc desc[TPS65218_NUM_REGULATOR];
+ struct regulator_dev *rdev[TPS65218_NUM_REGULATOR];
+ struct tps_info *info[TPS65218_NUM_REGULATOR];
+ struct regmap *regmap;
+};
+
+int tps65218_reg_read(struct tps65218 *tps, unsigned int reg,
+ unsigned int *val);
+int tps65218_reg_write(struct tps65218 *tps, unsigned int reg,
+ unsigned int val, unsigned int level);
+int tps65218_set_bits(struct tps65218 *tps, unsigned int reg,
+ unsigned int mask, unsigned int val, unsigned int level);
+int tps65218_clear_bits(struct tps65218 *tps, unsigned int reg,
+ unsigned int mask, unsigned int level);
+
+#endif /* __LINUX_MFD_TPS65218_H */
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
index 2cf1547096d9..51e26f3cd3b3 100644
--- a/include/linux/miscdevice.h
+++ b/include/linux/miscdevice.h
@@ -23,6 +23,7 @@
#define TEMP_MINOR 131 /* Temperature Sensor */
#define RTC_MINOR 135
#define EFI_RTC_MINOR 136 /* EFI Time services */
+#define VHCI_MINOR 137
#define SUN_OPENPROM_MINOR 139
#define DMAPI_MINOR 140 /* unused */
#define NVRAM_MINOR 144
diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h
index 79a347238168..c8450366c130 100644
--- a/include/linux/mlx4/cmd.h
+++ b/include/linux/mlx4/cmd.h
@@ -125,6 +125,7 @@ enum {
/* miscellaneous commands */
MLX4_CMD_DIAG_RPRT = 0x30,
MLX4_CMD_NOP = 0x31,
+ MLX4_CMD_CONFIG_DEV = 0x3a,
MLX4_CMD_ACCESS_MEM = 0x2e,
MLX4_CMD_SET_VEP = 0x52,
@@ -240,6 +241,13 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos);
int mlx4_set_vf_spoofchk(struct mlx4_dev *dev, int port, int vf, bool setting);
int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_info *ivf);
int mlx4_set_vf_link_state(struct mlx4_dev *dev, int port, int vf, int link_state);
+/*
+ * mlx4_get_slave_default_vlan -
+ * return true if VST ( default vlan)
+ * if VST, will return vlan & qos (if not NULL)
+ */
+bool mlx4_get_slave_default_vlan(struct mlx4_dev *dev, int port, int slave,
+ u16 *vlan, u8 *qos);
#define MLX4_COMM_GET_IF_REV(cmd_chan_ver) (u8)((cmd_chan_ver) >> 8)
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h
index 5edd2c68274d..ba87bd21295a 100644
--- a/include/linux/mlx4/device.h
+++ b/include/linux/mlx4/device.h
@@ -48,6 +48,9 @@
#define MSIX_LEGACY_SZ 4
#define MIN_MSIX_P_PORT 5
+#define MLX4_ROCE_MAX_GIDS 128
+#define MLX4_ROCE_PF_GIDS 16
+
enum {
MLX4_FLAG_MSI_X = 1 << 0,
MLX4_FLAG_OLD_PORT_CMDS = 1 << 1,
@@ -81,6 +84,7 @@ enum {
enum {
MLX4_MAX_NUM_PF = 16,
MLX4_MAX_NUM_VF = 64,
+ MLX4_MAX_NUM_VF_P_PORT = 64,
MLX4_MFUNC_MAX = 80,
MLX4_MAX_EQ_NUM = 1024,
MLX4_MFUNC_EQ_NUM = 4,
@@ -629,7 +633,8 @@ struct mlx4_eth_av {
u8 hop_limit;
__be32 sl_tclass_flowlabel;
u8 dgid[16];
- u32 reserved4[2];
+ u8 s_mac[6];
+ u8 reserved4[2];
__be16 vlan;
u8 mac[ETH_ALEN];
};
@@ -660,6 +665,11 @@ struct mlx4_quotas {
int xrcd;
};
+struct mlx4_vf_dev {
+ u8 min_port;
+ u8 n_ports;
+};
+
struct mlx4_dev {
struct pci_dev *pdev;
unsigned long flags;
@@ -675,6 +685,7 @@ struct mlx4_dev {
int oper_log_mgm_entry_size;
u64 regid_promisc_array[MLX4_MAX_PORTS + 1];
u64 regid_allmulti_array[MLX4_MAX_PORTS + 1];
+ struct mlx4_vf_dev *dev_vfs;
};
struct mlx4_eqe {
@@ -1131,7 +1142,7 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn,
int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc);
int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw,
u8 *pg, u16 *ratelimit);
-int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering);
+int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering, int enable);
int mlx4_find_cached_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *idx);
int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx);
int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index);
@@ -1183,9 +1194,44 @@ int set_and_calc_slave_port_state(struct mlx4_dev *dev, int slave, u8 port, int
void mlx4_put_slave_node_guid(struct mlx4_dev *dev, int slave, __be64 guid);
__be64 mlx4_get_slave_node_guid(struct mlx4_dev *dev, int slave);
+int mlx4_get_slave_from_roce_gid(struct mlx4_dev *dev, int port, u8 *gid,
+ int *slave_id);
+int mlx4_get_roce_gid_from_slave(struct mlx4_dev *dev, int port, int slave_id,
+ u8 *gid);
+
int mlx4_FLOW_STEERING_IB_UC_QP_RANGE(struct mlx4_dev *dev, u32 min_range_qpn,
u32 max_range_qpn);
cycle_t mlx4_read_clock(struct mlx4_dev *dev);
+struct mlx4_active_ports {
+ DECLARE_BITMAP(ports, MLX4_MAX_PORTS);
+};
+/* Returns a bitmap of the physical ports which are assigned to slave */
+struct mlx4_active_ports mlx4_get_active_ports(struct mlx4_dev *dev, int slave);
+
+/* Returns the physical port that represents the virtual port of the slave, */
+/* or a value < 0 in case of an error. If a slave has 2 ports, the identity */
+/* mapping is returned. */
+int mlx4_slave_convert_port(struct mlx4_dev *dev, int slave, int port);
+
+struct mlx4_slaves_pport {
+ DECLARE_BITMAP(slaves, MLX4_MFUNC_MAX);
+};
+/* Returns a bitmap of all slaves that are assigned to port. */
+struct mlx4_slaves_pport mlx4_phys_to_slaves_pport(struct mlx4_dev *dev,
+ int port);
+
+/* Returns a bitmap of all slaves that are assigned exactly to all the */
+/* the ports that are set in crit_ports. */
+struct mlx4_slaves_pport mlx4_phys_to_slaves_pport_actv(
+ struct mlx4_dev *dev,
+ const struct mlx4_active_ports *crit_ports);
+
+/* Returns the slave's virtual port that represents the physical port. */
+int mlx4_phys_to_slave_port(struct mlx4_dev *dev, int slave, int port);
+
+int mlx4_get_base_gid_ix(struct mlx4_dev *dev, int slave, int port);
+
+int mlx4_config_vxlan_port(struct mlx4_dev *dev, __be16 udp_port);
#endif /* MLX4_DEVICE_H */
diff --git a/include/linux/mlx4/driver.h b/include/linux/mlx4/driver.h
index c257e1b211be..022055c8fb26 100644
--- a/include/linux/mlx4/driver.h
+++ b/include/linux/mlx4/driver.h
@@ -64,4 +64,16 @@ void mlx4_unregister_interface(struct mlx4_interface *intf);
void *mlx4_get_protocol_dev(struct mlx4_dev *dev, enum mlx4_protocol proto, int port);
+static inline u64 mlx4_mac_to_u64(u8 *addr)
+{
+ u64 mac = 0;
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ mac <<= 8;
+ mac |= addr[i];
+ }
+ return mac;
+}
+
#endif /* MLX4_DRIVER_H */
diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h
index 59f8ba84568b..b66e7610d4ee 100644
--- a/include/linux/mlx4/qp.h
+++ b/include/linux/mlx4/qp.h
@@ -270,9 +270,14 @@ enum {
struct mlx4_wqe_ctrl_seg {
__be32 owner_opcode;
- __be16 vlan_tag;
- u8 ins_vlan;
- u8 fence_size;
+ union {
+ struct {
+ __be16 vlan_tag;
+ u8 ins_vlan;
+ u8 fence_size;
+ };
+ __be32 bf_qpn;
+ };
/*
* High 24 bits are SRC remote buffer; low 8 bits are flags:
* [7] SO (strong ordering)
diff --git a/include/linux/mlx5/cq.h b/include/linux/mlx5/cq.h
index 2202c7f72b75..f6b17ac601bd 100644
--- a/include/linux/mlx5/cq.h
+++ b/include/linux/mlx5/cq.h
@@ -80,6 +80,7 @@ enum {
MLX5_CQE_RESP_SEND_IMM = 3,
MLX5_CQE_RESP_SEND_INV = 4,
MLX5_CQE_RESIZE_CQ = 5,
+ MLX5_CQE_SIG_ERR = 12,
MLX5_CQE_REQ_ERR = 13,
MLX5_CQE_RESP_ERR = 14,
MLX5_CQE_INVALID = 15,
diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h
index 817a6fae6d2c..407bdb67fd4f 100644
--- a/include/linux/mlx5/device.h
+++ b/include/linux/mlx5/device.h
@@ -48,6 +48,8 @@ enum {
MLX5_MAX_COMMANDS = 32,
MLX5_CMD_DATA_BLOCK_SIZE = 512,
MLX5_PCI_CMD_XPORT = 7,
+ MLX5_MKEY_BSF_OCTO_SIZE = 4,
+ MLX5_MAX_PSVS = 4,
};
enum {
@@ -116,6 +118,7 @@ enum {
MLX5_MKEY_MASK_START_ADDR = 1ull << 6,
MLX5_MKEY_MASK_PD = 1ull << 7,
MLX5_MKEY_MASK_EN_RINVAL = 1ull << 8,
+ MLX5_MKEY_MASK_EN_SIGERR = 1ull << 9,
MLX5_MKEY_MASK_BSF_EN = 1ull << 12,
MLX5_MKEY_MASK_KEY = 1ull << 13,
MLX5_MKEY_MASK_QPN = 1ull << 14,
@@ -555,6 +558,23 @@ struct mlx5_cqe64 {
u8 op_own;
};
+struct mlx5_sig_err_cqe {
+ u8 rsvd0[16];
+ __be32 expected_trans_sig;
+ __be32 actual_trans_sig;
+ __be32 expected_reftag;
+ __be32 actual_reftag;
+ __be16 syndrome;
+ u8 rsvd22[2];
+ __be32 mkey;
+ __be64 err_offset;
+ u8 rsvd30[8];
+ __be32 qpn;
+ u8 rsvd38[2];
+ u8 signature;
+ u8 op_own;
+};
+
struct mlx5_wqe_srq_next_seg {
u8 rsvd0[2];
__be16 next_wqe_index;
@@ -936,4 +956,27 @@ enum {
MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO = 1 << 0
};
+struct mlx5_allocate_psv_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 npsv_pd;
+ __be32 rsvd_psv0;
+};
+
+struct mlx5_allocate_psv_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+ __be32 psv_idx[4];
+};
+
+struct mlx5_destroy_psv_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 psv_number;
+ u8 rsvd[4];
+};
+
+struct mlx5_destroy_psv_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
#endif /* MLX5_DEVICE_H */
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index 130bc8d77fa5..93cef6313e72 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -401,6 +401,26 @@ struct mlx5_eq {
struct mlx5_rsc_debug *dbg;
};
+struct mlx5_core_psv {
+ u32 psv_idx;
+ struct psv_layout {
+ u32 pd;
+ u16 syndrome;
+ u16 reserved;
+ u16 bg;
+ u16 app_tag;
+ u32 ref_tag;
+ } psv;
+};
+
+struct mlx5_core_sig_ctx {
+ struct mlx5_core_psv psv_memory;
+ struct mlx5_core_psv psv_wire;
+ struct ib_sig_err err_item;
+ bool sig_status_checked;
+ bool sig_err_exists;
+ u32 sigerr_count;
+};
struct mlx5_core_mr {
u64 iova;
@@ -475,6 +495,13 @@ struct mlx5_srq_table {
struct radix_tree_root tree;
};
+struct mlx5_mr_table {
+ /* protect radix tree
+ */
+ rwlock_t lock;
+ struct radix_tree_root tree;
+};
+
struct mlx5_priv {
char name[MLX5_MAX_NAME_LEN];
struct mlx5_eq_table eq_table;
@@ -504,6 +531,10 @@ struct mlx5_priv {
struct mlx5_cq_table cq_table;
/* end: cq staff */
+ /* start: mr staff */
+ struct mlx5_mr_table mr_table;
+ /* end: mr staff */
+
/* start: alloc staff */
struct mutex pgdir_mutex;
struct list_head pgdir_list;
@@ -651,6 +682,11 @@ static inline void mlx5_vfree(const void *addr)
kfree(addr);
}
+static inline u32 mlx5_base_mkey(const u32 key)
+{
+ return key & 0xffffff00u;
+}
+
int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev);
void mlx5_dev_cleanup(struct mlx5_core_dev *dev);
int mlx5_cmd_init(struct mlx5_core_dev *dev);
@@ -685,6 +721,8 @@ int mlx5_core_query_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_query_srq_mbox_out *out);
int mlx5_core_arm_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
u16 lwm, int is_srq);
+void mlx5_init_mr_table(struct mlx5_core_dev *dev);
+void mlx5_cleanup_mr_table(struct mlx5_core_dev *dev);
int mlx5_core_create_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
struct mlx5_create_mkey_mbox_in *in, int inlen,
mlx5_cmd_cbk_t callback, void *context,
@@ -746,6 +784,9 @@ void mlx5_db_free(struct mlx5_core_dev *dev, struct mlx5_db *db);
const char *mlx5_command_str(int command);
int mlx5_cmdif_debugfs_init(struct mlx5_core_dev *dev);
void mlx5_cmdif_debugfs_cleanup(struct mlx5_core_dev *dev);
+int mlx5_core_create_psv(struct mlx5_core_dev *dev, u32 pdn,
+ int npsvs, u32 *sig_index);
+int mlx5_core_destroy_psv(struct mlx5_core_dev *dev, int psv_num);
static inline u32 mlx5_mkey_to_idx(u32 mkey)
{
diff --git a/include/linux/mlx5/qp.h b/include/linux/mlx5/qp.h
index d51eff713549..f829ad80ff28 100644
--- a/include/linux/mlx5/qp.h
+++ b/include/linux/mlx5/qp.h
@@ -37,6 +37,9 @@
#include <linux/mlx5/driver.h>
#define MLX5_INVALID_LKEY 0x100
+#define MLX5_SIG_WQE_SIZE (MLX5_SEND_WQE_BB * 5)
+#define MLX5_DIF_SIZE 8
+#define MLX5_STRIDE_BLOCK_OP 0x400
enum mlx5_qp_optpar {
MLX5_QP_OPTPAR_ALT_ADDR_PATH = 1 << 0,
@@ -151,6 +154,11 @@ enum {
MLX5_SND_DBR = 1,
};
+enum {
+ MLX5_FLAGS_INLINE = 1<<7,
+ MLX5_FLAGS_CHECK_FREE = 1<<5,
+};
+
struct mlx5_wqe_fmr_seg {
__be32 flags;
__be32 mem_key;
@@ -278,6 +286,60 @@ struct mlx5_wqe_inline_seg {
__be32 byte_count;
};
+struct mlx5_bsf {
+ struct mlx5_bsf_basic {
+ u8 bsf_size_sbs;
+ u8 check_byte_mask;
+ union {
+ u8 copy_byte_mask;
+ u8 bs_selector;
+ u8 rsvd_wflags;
+ } wire;
+ union {
+ u8 bs_selector;
+ u8 rsvd_mflags;
+ } mem;
+ __be32 raw_data_size;
+ __be32 w_bfs_psv;
+ __be32 m_bfs_psv;
+ } basic;
+ struct mlx5_bsf_ext {
+ __be32 t_init_gen_pro_size;
+ __be32 rsvd_epi_size;
+ __be32 w_tfs_psv;
+ __be32 m_tfs_psv;
+ } ext;
+ struct mlx5_bsf_inl {
+ __be32 w_inl_vld;
+ __be32 w_rsvd;
+ __be64 w_block_format;
+ __be32 m_inl_vld;
+ __be32 m_rsvd;
+ __be64 m_block_format;
+ } inl;
+};
+
+struct mlx5_klm {
+ __be32 bcount;
+ __be32 key;
+ __be64 va;
+};
+
+struct mlx5_stride_block_entry {
+ __be16 stride;
+ __be16 bcount;
+ __be32 key;
+ __be64 va;
+};
+
+struct mlx5_stride_block_ctrl_seg {
+ __be32 bcount_per_cycle;
+ __be32 op;
+ __be32 repeat_count;
+ u16 rsvd;
+ __be16 num_entries;
+};
+
struct mlx5_core_qp {
void (*event) (struct mlx5_core_qp *, int);
int qpn;
@@ -444,6 +506,11 @@ static inline struct mlx5_core_qp *__mlx5_qp_lookup(struct mlx5_core_dev *dev, u
return radix_tree_lookup(&dev->priv.qp_table.tree, qpn);
}
+static inline struct mlx5_core_mr *__mlx5_mr_lookup(struct mlx5_core_dev *dev, u32 key)
+{
+ return radix_tree_lookup(&dev->priv.mr_table.tree, key);
+}
+
int mlx5_core_create_qp(struct mlx5_core_dev *dev,
struct mlx5_core_qp *qp,
struct mlx5_create_qp_mbox_in *in,
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 2eec61fe75c9..35300f390eb6 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1041,6 +1041,14 @@ extern void show_free_areas(unsigned int flags);
extern bool skip_free_areas_node(unsigned int flags, int nid);
int shmem_zero_setup(struct vm_area_struct *);
+#ifdef CONFIG_SHMEM
+bool shmem_mapping(struct address_space *mapping);
+#else
+static inline bool shmem_mapping(struct address_space *mapping)
+{
+ return false;
+}
+#endif
extern int can_do_mlock(void);
extern int user_shm_lock(size_t, struct user_struct *);
@@ -1658,10 +1666,8 @@ static inline int __early_pfn_to_nid(unsigned long pfn)
#else
/* please see mm/page_alloc.c */
extern int __meminit early_pfn_to_nid(unsigned long pfn);
-#ifdef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID
/* there is a per-arch backend function. */
extern int __meminit __early_pfn_to_nid(unsigned long pfn);
-#endif /* CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID */
#endif
extern void set_dma_reserve(unsigned long new_dma_reserve);
@@ -1826,6 +1832,7 @@ vm_unmapped_area(struct vm_unmapped_area_info *info)
extern void truncate_inode_pages(struct address_space *, loff_t);
extern void truncate_inode_pages_range(struct address_space *,
loff_t lstart, loff_t lend);
+extern void truncate_inode_pages_final(struct address_space *);
/* generic vm_area_ops exported for stackable file systems */
extern int filemap_fault(struct vm_area_struct *, struct vm_fault *);
diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
index d8836623f36a..0f01fe065424 100644
--- a/include/linux/mmc/sdio_ids.h
+++ b/include/linux/mmc/sdio_ids.h
@@ -31,6 +31,7 @@
#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334
#define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335
#define SDIO_DEVICE_ID_BROADCOM_43362 43362
+#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354
#define SDIO_VENDOR_ID_INTEL 0x0089
#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 9b61b9bf81ac..fac5509c18f0 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -142,6 +142,9 @@ enum zone_stat_item {
NUMA_LOCAL, /* allocation from local node */
NUMA_OTHER, /* allocation from other node */
#endif
+ WORKINGSET_REFAULT,
+ WORKINGSET_ACTIVATE,
+ WORKINGSET_NODERECLAIM,
NR_ANON_TRANSPARENT_HUGEPAGES,
NR_FREE_CMA_PAGES,
NR_VM_ZONE_STAT_ITEMS };
@@ -392,6 +395,9 @@ struct zone {
spinlock_t lru_lock;
struct lruvec lruvec;
+ /* Evictions & activations on the inactive file list */
+ atomic_long_t inactive_age;
+
unsigned long pages_scanned; /* since last reclaim */
unsigned long flags; /* zone flags, see below */
diff --git a/include/linux/module.h b/include/linux/module.h
index eaf60ff9ba94..f520a767c86c 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -15,7 +15,7 @@
#include <linux/stringify.h>
#include <linux/kobject.h>
#include <linux/moduleparam.h>
-#include <linux/tracepoint.h>
+#include <linux/jump_label.h>
#include <linux/export.h>
#include <linux/percpu.h>
@@ -82,15 +82,6 @@ void sort_extable(struct exception_table_entry *start,
void sort_main_extable(void);
void trim_init_extable(struct module *m);
-#ifdef MODULE
-#define MODULE_GENERIC_TABLE(gtype, name) \
-extern const struct gtype##_id __mod_##gtype##_table \
- __attribute__ ((unused, alias(__stringify(name))))
-
-#else /* !MODULE */
-#define MODULE_GENERIC_TABLE(gtype, name)
-#endif
-
/* Generic info of form tag = "info" */
#define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
@@ -141,8 +132,14 @@ extern const struct gtype##_id __mod_##gtype##_table \
/* What your module does. */
#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
-#define MODULE_DEVICE_TABLE(type, name) \
- MODULE_GENERIC_TABLE(type##_device, name)
+#ifdef MODULE
+/* Creates an alias so file2alias.c can find device table. */
+#define MODULE_DEVICE_TABLE(type, name) \
+ extern const struct type##_device_id __mod_##type##__##name##_device_table \
+ __attribute__ ((unused, alias(__stringify(name))))
+#else /* !MODULE */
+#define MODULE_DEVICE_TABLE(type, name)
+#endif
/* Version of form [<epoch>:]<version>[-<extra-version>].
* Or for CVS/RCS ID version, everything but the number is stripped.
diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
index c3eb102a9cc8..204a67743804 100644
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -186,14 +186,12 @@ struct kparam_array
parameters. */
#define __module_param_call(prefix, name, ops, arg, perm, level) \
/* Default value instead of permissions? */ \
- static int __param_perm_check_##name __attribute__((unused)) = \
- BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2)) \
- + BUILD_BUG_ON_ZERO(sizeof(""prefix) > MAX_PARAM_PREFIX_LEN); \
- static const char __param_str_##name[] = prefix #name; \
+ static const char __param_str_##name[] = prefix #name; \
static struct kernel_param __moduleparam_const __param_##name \
__used \
__attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
- = { __param_str_##name, ops, perm, level, { arg } }
+ = { __param_str_##name, ops, VERIFY_OCTAL_PERMISSIONS(perm), \
+ level, { arg } }
/* Obsolete - use module_param_cb() */
#define module_param_call(name, set, get, arg, perm) \
@@ -346,7 +344,7 @@ static inline void destroy_params(const struct kernel_param *params,
/* The macros to do compile-time type checking stolen from Jakub
Jelinek, who IIRC came up with this idea for the 2.4 module init code. */
#define __param_check(name, p, type) \
- static inline type *__check_##name(void) { return(p); }
+ static inline type __always_unused *__check_##name(void) { return(p); }
extern struct kernel_param_ops param_ops_byte;
extern int param_set_byte(const char *val, const struct kernel_param *kp);
diff --git a/include/linux/mpls.h b/include/linux/mpls.h
new file mode 100644
index 000000000000..9999145bc190
--- /dev/null
+++ b/include/linux/mpls.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_MPLS_H
+#define _LINUX_MPLS_H
+
+#include <uapi/linux/mpls.h>
+
+#endif /* _LINUX_MPLS_H */
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 8cc0e2fb6894..a1b0b4c8fd79 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -204,12 +204,12 @@ struct mtd_info {
struct mtd_oob_ops *ops);
int (*_write_oob) (struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);
- int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
- size_t len);
+ int (*_get_fact_prot_info) (struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf);
int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf);
- int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
- size_t len);
+ int (*_get_user_prot_info) (struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf);
int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf);
int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,
@@ -278,12 +278,12 @@ static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to,
return mtd->_write_oob(mtd, to, ops);
}
-int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
- size_t len);
+int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+ struct otp_info *buf);
int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
-int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
- size_t len);
+int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+ struct otp_info *buf);
int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 32f8612469d8..450d61ec7f06 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -52,14 +52,6 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
#define NAND_MAX_CHIPS 8
/*
- * This constant declares the max. oobsize / page, which
- * is supported now. If you add a chip with bigger oobsize/page
- * adjust this accordingly.
- */
-#define NAND_MAX_OOBSIZE 744
-#define NAND_MAX_PAGESIZE 8192
-
-/*
* Constants for hardware specific CLE/ALE/NCE function
*
* These are bits which can be or'ed to set/clear multiple
@@ -350,6 +342,84 @@ struct nand_onfi_vendor_micron {
u8 param_revision;
} __packed;
+struct jedec_ecc_info {
+ u8 ecc_bits;
+ u8 codeword_size;
+ __le16 bb_per_lun;
+ __le16 block_endurance;
+ u8 reserved[2];
+} __packed;
+
+/* JEDEC features */
+#define JEDEC_FEATURE_16_BIT_BUS (1 << 0)
+
+struct nand_jedec_params {
+ /* rev info and features block */
+ /* 'J' 'E' 'S' 'D' */
+ u8 sig[4];
+ __le16 revision;
+ __le16 features;
+ u8 opt_cmd[3];
+ __le16 sec_cmd;
+ u8 num_of_param_pages;
+ u8 reserved0[18];
+
+ /* manufacturer information block */
+ char manufacturer[12];
+ char model[20];
+ u8 jedec_id[6];
+ u8 reserved1[10];
+
+ /* memory organization block */
+ __le32 byte_per_page;
+ __le16 spare_bytes_per_page;
+ u8 reserved2[6];
+ __le32 pages_per_block;
+ __le32 blocks_per_lun;
+ u8 lun_count;
+ u8 addr_cycles;
+ u8 bits_per_cell;
+ u8 programs_per_page;
+ u8 multi_plane_addr;
+ u8 multi_plane_op_attr;
+ u8 reserved3[38];
+
+ /* electrical parameter block */
+ __le16 async_sdr_speed_grade;
+ __le16 toggle_ddr_speed_grade;
+ __le16 sync_ddr_speed_grade;
+ u8 async_sdr_features;
+ u8 toggle_ddr_features;
+ u8 sync_ddr_features;
+ __le16 t_prog;
+ __le16 t_bers;
+ __le16 t_r;
+ __le16 t_r_multi_plane;
+ __le16 t_ccs;
+ __le16 io_pin_capacitance_typ;
+ __le16 input_pin_capacitance_typ;
+ __le16 clk_pin_capacitance_typ;
+ u8 driver_strength_support;
+ __le16 t_ald;
+ u8 reserved4[36];
+
+ /* ECC and endurance block */
+ u8 guaranteed_good_blocks;
+ __le16 guaranteed_block_endurance;
+ struct jedec_ecc_info ecc_info[4];
+ u8 reserved5[29];
+
+ /* reserved */
+ u8 reserved6[148];
+
+ /* vendor */
+ __le16 vendor_rev_num;
+ u8 reserved7[88];
+
+ /* CRC for Parameter Page */
+ __le16 crc;
+} __packed;
+
/**
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
* @lock: protection lock
@@ -418,7 +488,7 @@ struct nand_ecc_ctrl {
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page);
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t offs, uint32_t len, uint8_t *buf);
+ uint32_t offs, uint32_t len, uint8_t *buf, int page);
int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, uint32_t data_len,
const uint8_t *data_buf, int oob_required);
@@ -435,17 +505,17 @@ struct nand_ecc_ctrl {
/**
* struct nand_buffers - buffer structure for read/write
- * @ecccalc: buffer for calculated ECC
- * @ecccode: buffer for ECC read from flash
- * @databuf: buffer for data - dynamically sized
+ * @ecccalc: buffer pointer for calculated ECC, size is oobsize.
+ * @ecccode: buffer pointer for ECC read from flash, size is oobsize.
+ * @databuf: buffer pointer for data, size is (page size + oobsize).
*
* Do not change the order of buffers. databuf and oobrbuf must be in
* consecutive order.
*/
struct nand_buffers {
- uint8_t ecccalc[NAND_MAX_OOBSIZE];
- uint8_t ecccode[NAND_MAX_OOBSIZE];
- uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE];
+ uint8_t *ecccalc;
+ uint8_t *ecccode;
+ uint8_t *databuf;
};
/**
@@ -523,8 +593,12 @@ struct nand_buffers {
* @subpagesize: [INTERN] holds the subpagesize
* @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded),
* non 0 if ONFI supported.
+ * @jedec_version: [INTERN] holds the chip JEDEC version (BCD encoded),
+ * non 0 if JEDEC supported.
* @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is
* supported, 0 otherwise.
+ * @jedec_params: [INTERN] holds the JEDEC parameter page when JEDEC is
+ * supported, 0 otherwise.
* @read_retries: [INTERN] the number of read retry modes supported
* @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
* @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
@@ -597,7 +671,11 @@ struct nand_chip {
int badblockbits;
int onfi_version;
- struct nand_onfi_params onfi_params;
+ int jedec_version;
+ union {
+ struct nand_onfi_params onfi_params;
+ struct nand_jedec_params jedec_params;
+ };
int read_retries;
@@ -840,4 +918,29 @@ static inline bool nand_is_slc(struct nand_chip *chip)
{
return chip->bits_per_cell == 1;
}
+
+/**
+ * Check if the opcode's address should be sent only on the lower 8 bits
+ * @command: opcode to check
+ */
+static inline int nand_opcode_8bits(unsigned int command)
+{
+ switch (command) {
+ case NAND_CMD_READID:
+ case NAND_CMD_PARAM:
+ case NAND_CMD_GET_FEATURES:
+ case NAND_CMD_SET_FEATURES:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* return the supported JEDEC features. */
+static inline int jedec_feature(struct nand_chip *chip)
+{
+ return chip->jedec_version ? le16_to_cpu(chip->jedec_params.features)
+ : 0;
+}
#endif /* __LINUX_MTD_NAND_H */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index daafd9561cbc..775cc956ff78 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -63,13 +63,6 @@ struct wireless_dev;
void netdev_set_default_ethtool_ops(struct net_device *dev,
const struct ethtool_ops *ops);
-/* hardware address assignment types */
-#define NET_ADDR_PERM 0 /* address is permanent (default) */
-#define NET_ADDR_RANDOM 1 /* address is generated randomly */
-#define NET_ADDR_STOLEN 2 /* address is stolen from other device */
-#define NET_ADDR_SET 3 /* address is set using
- * dev_set_mac_address() */
-
/* Backlog congestion levels */
#define NET_RX_SUCCESS 0 /* keep 'em coming, baby */
#define NET_RX_DROP 1 /* packet dropped */
@@ -1037,8 +1030,7 @@ struct net_device_ops {
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*ndo_poll_controller)(struct net_device *dev);
int (*ndo_netpoll_setup)(struct net_device *dev,
- struct netpoll_info *info,
- gfp_t gfp);
+ struct netpoll_info *info);
void (*ndo_netpoll_cleanup)(struct net_device *dev);
#endif
#ifdef CONFIG_NET_RX_BUSY_POLL
@@ -1147,6 +1139,89 @@ struct net_device_ops {
void *priv);
};
+/**
+ * enum net_device_priv_flags - &struct net_device priv_flags
+ *
+ * These are the &struct net_device, they are only set internally
+ * by drivers and used in the kernel. These flags are invisible to
+ * userspace, this means that the order of these flags can change
+ * during any kernel release.
+ *
+ * You should have a pretty good reason to be extending these flags.
+ *
+ * @IFF_802_1Q_VLAN: 802.1Q VLAN device
+ * @IFF_EBRIDGE: Ethernet bridging device
+ * @IFF_SLAVE_INACTIVE: bonding slave not the curr. active
+ * @IFF_MASTER_8023AD: bonding master, 802.3ad
+ * @IFF_MASTER_ALB: bonding master, balance-alb
+ * @IFF_BONDING: bonding master or slave
+ * @IFF_SLAVE_NEEDARP: need ARPs for validation
+ * @IFF_ISATAP: ISATAP interface (RFC4214)
+ * @IFF_MASTER_ARPMON: bonding master, ARP mon in use
+ * @IFF_WAN_HDLC: WAN HDLC device
+ * @IFF_XMIT_DST_RELEASE: dev_hard_start_xmit() is allowed to
+ * release skb->dst
+ * @IFF_DONT_BRIDGE: disallow bridging this ether dev
+ * @IFF_DISABLE_NETPOLL: disable netpoll at run-time
+ * @IFF_MACVLAN_PORT: device used as macvlan port
+ * @IFF_BRIDGE_PORT: device used as bridge port
+ * @IFF_OVS_DATAPATH: device used as Open vSwitch datapath port
+ * @IFF_TX_SKB_SHARING: The interface supports sharing skbs on transmit
+ * @IFF_UNICAST_FLT: Supports unicast filtering
+ * @IFF_TEAM_PORT: device used as team port
+ * @IFF_SUPP_NOFCS: device supports sending custom FCS
+ * @IFF_LIVE_ADDR_CHANGE: device supports hardware address
+ * change when it's running
+ * @IFF_MACVLAN: Macvlan device
+ */
+enum netdev_priv_flags {
+ IFF_802_1Q_VLAN = 1<<0,
+ IFF_EBRIDGE = 1<<1,
+ IFF_SLAVE_INACTIVE = 1<<2,
+ IFF_MASTER_8023AD = 1<<3,
+ IFF_MASTER_ALB = 1<<4,
+ IFF_BONDING = 1<<5,
+ IFF_SLAVE_NEEDARP = 1<<6,
+ IFF_ISATAP = 1<<7,
+ IFF_MASTER_ARPMON = 1<<8,
+ IFF_WAN_HDLC = 1<<9,
+ IFF_XMIT_DST_RELEASE = 1<<10,
+ IFF_DONT_BRIDGE = 1<<11,
+ IFF_DISABLE_NETPOLL = 1<<12,
+ IFF_MACVLAN_PORT = 1<<13,
+ IFF_BRIDGE_PORT = 1<<14,
+ IFF_OVS_DATAPATH = 1<<15,
+ IFF_TX_SKB_SHARING = 1<<16,
+ IFF_UNICAST_FLT = 1<<17,
+ IFF_TEAM_PORT = 1<<18,
+ IFF_SUPP_NOFCS = 1<<19,
+ IFF_LIVE_ADDR_CHANGE = 1<<20,
+ IFF_MACVLAN = 1<<21,
+};
+
+#define IFF_802_1Q_VLAN IFF_802_1Q_VLAN
+#define IFF_EBRIDGE IFF_EBRIDGE
+#define IFF_SLAVE_INACTIVE IFF_SLAVE_INACTIVE
+#define IFF_MASTER_8023AD IFF_MASTER_8023AD
+#define IFF_MASTER_ALB IFF_MASTER_ALB
+#define IFF_BONDING IFF_BONDING
+#define IFF_SLAVE_NEEDARP IFF_SLAVE_NEEDARP
+#define IFF_ISATAP IFF_ISATAP
+#define IFF_MASTER_ARPMON IFF_MASTER_ARPMON
+#define IFF_WAN_HDLC IFF_WAN_HDLC
+#define IFF_XMIT_DST_RELEASE IFF_XMIT_DST_RELEASE
+#define IFF_DONT_BRIDGE IFF_DONT_BRIDGE
+#define IFF_DISABLE_NETPOLL IFF_DISABLE_NETPOLL
+#define IFF_MACVLAN_PORT IFF_MACVLAN_PORT
+#define IFF_BRIDGE_PORT IFF_BRIDGE_PORT
+#define IFF_OVS_DATAPATH IFF_OVS_DATAPATH
+#define IFF_TX_SKB_SHARING IFF_TX_SKB_SHARING
+#define IFF_UNICAST_FLT IFF_UNICAST_FLT
+#define IFF_TEAM_PORT IFF_TEAM_PORT
+#define IFF_SUPP_NOFCS IFF_SUPP_NOFCS
+#define IFF_LIVE_ADDR_CHANGE IFF_LIVE_ADDR_CHANGE
+#define IFF_MACVLAN IFF_MACVLAN
+
/*
* The DEVICE structure.
* Actually, this whole structure is a big mistake. It mixes I/O
@@ -1228,9 +1303,13 @@ struct net_device {
int iflink;
struct net_device_stats stats;
- atomic_long_t rx_dropped; /* dropped packets by core network
- * Do not use this in drivers.
- */
+
+ /* dropped packets by core network, Do not use this in drivers */
+ atomic_long_t rx_dropped;
+ atomic_long_t tx_dropped;
+
+ /* Stats to monitor carrier on<->off transitions */
+ atomic_t carrier_changes;
#ifdef CONFIG_WIRELESS_EXT
/* List of functions to handle Wireless Extensions (instead of ioctl).
@@ -1279,6 +1358,10 @@ struct net_device {
* that share the same link
* layer address
*/
+ unsigned short dev_port; /* Used to differentiate
+ * devices that share the same
+ * function
+ */
spinlock_t addr_list_lock;
struct netdev_hw_addr_list uc; /* Unicast mac addresses */
struct netdev_hw_addr_list mc; /* Multicast mac addresses */
@@ -1316,13 +1399,7 @@ struct net_device {
/*
* Cache lines mostly used on receive path (including eth_type_trans())
*/
- unsigned long last_rx; /* Time of last Rx
- * This should not be set in
- * drivers, unless really needed,
- * because network stack (bonding)
- * use it if/when necessary, to
- * avoid dirtying this cache line.
- */
+ unsigned long last_rx; /* Time of last Rx */
/* Interface address info used in eth_type_trans() */
unsigned char *dev_addr; /* hw address, (before bcast
@@ -1729,6 +1806,20 @@ struct pcpu_sw_netstats {
struct u64_stats_sync syncp;
};
+#define netdev_alloc_pcpu_stats(type) \
+({ \
+ typeof(type) __percpu *pcpu_stats = alloc_percpu(type); \
+ if (pcpu_stats) { \
+ int i; \
+ for_each_possible_cpu(i) { \
+ typeof(type) *stat; \
+ stat = per_cpu_ptr(pcpu_stats, i); \
+ u64_stats_init(&stat->syncp); \
+ } \
+ } \
+ pcpu_stats; \
+})
+
#include <linux/notifier.h>
/* netdevice notifier chain. Please remember to update the rtnetlink
@@ -1884,9 +1975,6 @@ struct net_device *__dev_get_by_index(struct net *net, int ifindex);
struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex);
int netdev_get_name(struct net *net, char *name, int ifindex);
int dev_restart(struct net_device *dev);
-#ifdef CONFIG_NETPOLL_TRAP
-int netpoll_trap(void);
-#endif
int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb);
static inline unsigned int skb_gro_offset(const struct sk_buff *skb)
@@ -1926,11 +2014,6 @@ static inline void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen,
return skb->data + offset;
}
-static inline void *skb_gro_mac_header(struct sk_buff *skb)
-{
- return NAPI_GRO_CB(skb)->frag0 ?: skb_mac_header(skb);
-}
-
static inline void *skb_gro_network_header(struct sk_buff *skb)
{
return (NAPI_GRO_CB(skb)->frag0 ?: skb->data) +
@@ -2091,12 +2174,6 @@ static inline void netif_tx_start_all_queues(struct net_device *dev)
static inline void netif_tx_wake_queue(struct netdev_queue *dev_queue)
{
-#ifdef CONFIG_NETPOLL_TRAP
- if (netpoll_trap()) {
- netif_tx_start_queue(dev_queue);
- return;
- }
-#endif
if (test_and_clear_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state))
__netif_schedule(dev_queue->qdisc);
}
@@ -2340,10 +2417,6 @@ static inline void netif_start_subqueue(struct net_device *dev, u16 queue_index)
static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index)
{
struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index);
-#ifdef CONFIG_NETPOLL_TRAP
- if (netpoll_trap())
- return;
-#endif
netif_tx_stop_queue(txq);
}
@@ -2378,10 +2451,6 @@ static inline bool netif_subqueue_stopped(const struct net_device *dev,
static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index)
{
struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index);
-#ifdef CONFIG_NETPOLL_TRAP
- if (netpoll_trap())
- return;
-#endif
if (test_and_clear_bit(__QUEUE_STATE_DRV_XOFF, &txq->state))
__netif_schedule(txq->qdisc);
}
@@ -2551,6 +2620,7 @@ int dev_get_phys_port_id(struct net_device *dev,
int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq);
int dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
+bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb);
extern int netdev_budget;
@@ -2831,6 +2901,11 @@ static inline void netif_tx_unlock_bh(struct net_device *dev)
} \
}
+#define HARD_TX_TRYLOCK(dev, txq) \
+ (((dev->features & NETIF_F_LLTX) == 0) ? \
+ __netif_tx_trylock(txq) : \
+ true )
+
#define HARD_TX_UNLOCK(dev, txq) { \
if ((dev->features & NETIF_F_LLTX) == 0) { \
__netif_tx_unlock(txq); \
diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
index 0c7d01eae56c..96afc29184be 100644
--- a/include/linux/netfilter/ipset/ip_set.h
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -39,11 +39,13 @@ enum ip_set_feature {
IPSET_TYPE_NAME = (1 << IPSET_TYPE_NAME_FLAG),
IPSET_TYPE_IFACE_FLAG = 5,
IPSET_TYPE_IFACE = (1 << IPSET_TYPE_IFACE_FLAG),
- IPSET_TYPE_NOMATCH_FLAG = 6,
+ IPSET_TYPE_MARK_FLAG = 6,
+ IPSET_TYPE_MARK = (1 << IPSET_TYPE_MARK_FLAG),
+ IPSET_TYPE_NOMATCH_FLAG = 7,
IPSET_TYPE_NOMATCH = (1 << IPSET_TYPE_NOMATCH_FLAG),
/* Strictly speaking not a feature, but a flag for dumping:
* this settype must be dumped last */
- IPSET_DUMP_LAST_FLAG = 7,
+ IPSET_DUMP_LAST_FLAG = 8,
IPSET_DUMP_LAST = (1 << IPSET_DUMP_LAST_FLAG),
};
@@ -63,6 +65,7 @@ enum ip_set_extension {
#define SET_WITH_TIMEOUT(s) ((s)->extensions & IPSET_EXT_TIMEOUT)
#define SET_WITH_COUNTER(s) ((s)->extensions & IPSET_EXT_COUNTER)
#define SET_WITH_COMMENT(s) ((s)->extensions & IPSET_EXT_COMMENT)
+#define SET_WITH_FORCEADD(s) ((s)->flags & IPSET_CREATE_FLAG_FORCEADD)
/* Extension id, in size order */
enum ip_set_ext_id {
@@ -171,8 +174,6 @@ struct ip_set_type {
char name[IPSET_MAXNAMELEN];
/* Protocol version */
u8 protocol;
- /* Set features to control swapping */
- u8 features;
/* Set type dimension */
u8 dimension;
/*
@@ -182,6 +183,8 @@ struct ip_set_type {
u8 family;
/* Type revisions */
u8 revision_min, revision_max;
+ /* Set features to control swapping */
+ u16 features;
/* Create set */
int (*create)(struct net *net, struct ip_set *set,
@@ -217,6 +220,8 @@ struct ip_set {
u8 revision;
/* Extensions */
u8 extensions;
+ /* Create flags */
+ u8 flags;
/* Default timeout value, if enabled */
u32 timeout;
/* Element data size */
@@ -251,6 +256,8 @@ ip_set_put_flags(struct sk_buff *skb, struct ip_set *set)
cadt_flags |= IPSET_FLAG_WITH_COUNTERS;
if (SET_WITH_COMMENT(set))
cadt_flags |= IPSET_FLAG_WITH_COMMENT;
+ if (SET_WITH_FORCEADD(set))
+ cadt_flags |= IPSET_FLAG_WITH_FORCEADD;
if (!cadt_flags)
return 0;
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index 28c74367e900..e955d4730625 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -44,6 +44,27 @@ int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid,
void nfnl_lock(__u8 subsys_id);
void nfnl_unlock(__u8 subsys_id);
+#ifdef CONFIG_PROVE_LOCKING
+int lockdep_nfnl_is_held(__u8 subsys_id);
+#else
+static inline int lockdep_nfnl_is_held(__u8 subsys_id)
+{
+ return 1;
+}
+#endif /* CONFIG_PROVE_LOCKING */
+
+/*
+ * nfnl_dereference - fetch RCU pointer when updates are prevented by subsys mutex
+ *
+ * @p: The pointer to read, prior to dereferencing
+ * @ss: The nfnetlink subsystem ID
+ *
+ * Return the value of the specified RCU-protected pointer, but omit
+ * both the smp_read_barrier_depends() and the ACCESS_ONCE(), because
+ * caller holds the NFNL subsystem mutex.
+ */
+#define nfnl_dereference(p, ss) \
+ rcu_dereference_protected(p, lockdep_nfnl_is_held(ss))
#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \
MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys))
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index fbfdb9d8d3a7..b25ee9ffdbe6 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -24,27 +24,20 @@ struct netpoll {
struct net_device *dev;
char dev_name[IFNAMSIZ];
const char *name;
- void (*rx_skb_hook)(struct netpoll *np, int source, struct sk_buff *skb,
- int offset, int len);
union inet_addr local_ip, remote_ip;
bool ipv6;
u16 local_port, remote_port;
u8 remote_mac[ETH_ALEN];
- struct list_head rx; /* rx_np list element */
struct work_struct cleanup_work;
};
struct netpoll_info {
atomic_t refcnt;
- unsigned long rx_flags;
- spinlock_t rx_lock;
struct semaphore dev_lock;
- struct list_head rx_np; /* netpolls that registered an rx_skb_hook */
- struct sk_buff_head neigh_tx; /* list of neigh requests to reply to */
struct sk_buff_head txq;
struct delayed_work tx_work;
@@ -54,24 +47,21 @@ struct netpoll_info {
};
#ifdef CONFIG_NETPOLL
-extern void netpoll_rx_disable(struct net_device *dev);
-extern void netpoll_rx_enable(struct net_device *dev);
+extern void netpoll_poll_disable(struct net_device *dev);
+extern void netpoll_poll_enable(struct net_device *dev);
#else
-static inline void netpoll_rx_disable(struct net_device *dev) { return; }
-static inline void netpoll_rx_enable(struct net_device *dev) { return; }
+static inline void netpoll_poll_disable(struct net_device *dev) { return; }
+static inline void netpoll_poll_enable(struct net_device *dev) { return; }
#endif
void netpoll_send_udp(struct netpoll *np, const char *msg, int len);
void netpoll_print_options(struct netpoll *np);
int netpoll_parse_options(struct netpoll *np, char *opt);
-int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp);
+int __netpoll_setup(struct netpoll *np, struct net_device *ndev);
int netpoll_setup(struct netpoll *np);
-int netpoll_trap(void);
-void netpoll_set_trap(int trap);
void __netpoll_cleanup(struct netpoll *np);
void __netpoll_free_async(struct netpoll *np);
void netpoll_cleanup(struct netpoll *np);
-int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo);
void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
struct net_device *dev);
static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
@@ -82,46 +72,7 @@ static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
local_irq_restore(flags);
}
-
-
#ifdef CONFIG_NETPOLL
-static inline bool netpoll_rx_on(struct sk_buff *skb)
-{
- struct netpoll_info *npinfo = rcu_dereference_bh(skb->dev->npinfo);
-
- return npinfo && (!list_empty(&npinfo->rx_np) || npinfo->rx_flags);
-}
-
-static inline bool netpoll_rx(struct sk_buff *skb)
-{
- struct netpoll_info *npinfo;
- unsigned long flags;
- bool ret = false;
-
- local_irq_save(flags);
-
- if (!netpoll_rx_on(skb))
- goto out;
-
- npinfo = rcu_dereference_bh(skb->dev->npinfo);
- spin_lock(&npinfo->rx_lock);
- /* check rx_flags again with the lock held */
- if (npinfo->rx_flags && __netpoll_rx(skb, npinfo))
- ret = true;
- spin_unlock(&npinfo->rx_lock);
-
-out:
- local_irq_restore(flags);
- return ret;
-}
-
-static inline int netpoll_receive_skb(struct sk_buff *skb)
-{
- if (!list_empty(&skb->dev->napi_list))
- return netpoll_rx(skb);
- return 0;
-}
-
static inline void *netpoll_poll_lock(struct napi_struct *napi)
{
struct net_device *dev = napi->dev;
@@ -150,18 +101,6 @@ static inline bool netpoll_tx_running(struct net_device *dev)
}
#else
-static inline bool netpoll_rx(struct sk_buff *skb)
-{
- return false;
-}
-static inline bool netpoll_rx_on(struct sk_buff *skb)
-{
- return false;
-}
-static inline int netpoll_receive_skb(struct sk_buff *skb)
-{
- return 0;
-}
static inline void *netpoll_poll_lock(struct napi_struct *napi)
{
return NULL;
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 0ae5807480f4..fa6918b0f829 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -92,6 +92,7 @@ struct nfs_open_context {
};
struct nfs_open_dir_context {
+ struct list_head list;
struct rpc_cred *cred;
unsigned long attr_gencount;
__u64 dir_cookie;
@@ -510,7 +511,6 @@ extern void nfs_complete_unlink(struct dentry *dentry, struct inode *);
extern void nfs_wait_on_sillyrename(struct dentry *dentry);
extern void nfs_block_sillyrename(struct dentry *dentry);
extern void nfs_unblock_sillyrename(struct dentry *dentry);
-extern int nfs_sillyrename(struct inode *dir, struct dentry *dentry);
/*
* linux/fs/nfs/write.c
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 5624e4e2763c..6fb5b2335b59 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1402,6 +1402,7 @@ struct nfs_renamedata {
struct inode *new_dir;
struct dentry *new_dentry;
struct nfs_fattr new_fattr;
+ void (*complete)(struct rpc_task *, struct nfs_renamedata *);
};
struct nfs_access_entry;
@@ -1444,8 +1445,6 @@ struct nfs_rpc_ops {
void (*unlink_setup) (struct rpc_message *, struct inode *dir);
void (*unlink_rpc_prepare) (struct rpc_task *, struct nfs_unlinkdata *);
int (*unlink_done) (struct rpc_task *, struct inode *);
- int (*rename) (struct inode *, struct qstr *,
- struct inode *, struct qstr *);
void (*rename_setup) (struct rpc_message *msg, struct inode *dir);
void (*rename_rpc_prepare)(struct rpc_task *task, struct nfs_renamedata *);
int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir);
diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h
index 98755767c7b0..ff3fea3194c6 100644
--- a/include/linux/nilfs2_fs.h
+++ b/include/linux/nilfs2_fs.h
@@ -82,6 +82,8 @@ struct nilfs_inode {
__le32 i_pad;
};
+#define NILFS_MIN_INODE_SIZE 128
+
/**
* struct nilfs_super_root - structure of super root
* @sr_sum: check sum
@@ -482,6 +484,8 @@ struct nilfs_dat_entry {
__le64 de_rsv;
};
+#define NILFS_MIN_DAT_ENTRY_SIZE 32
+
/**
* struct nilfs_snapshot_list - snapshot list
* @ssl_next: next checkpoint number on snapshot list
@@ -520,6 +524,8 @@ struct nilfs_checkpoint {
struct nilfs_inode cp_ifile_inode;
};
+#define NILFS_MIN_CHECKPOINT_SIZE (64 + NILFS_MIN_INODE_SIZE)
+
/* checkpoint flags */
enum {
NILFS_CHECKPOINT_SNAPSHOT,
@@ -615,6 +621,8 @@ struct nilfs_segment_usage {
__le32 su_flags;
};
+#define NILFS_MIN_SEGMENT_USAGE_SIZE 16
+
/* segment usage flag */
enum {
NILFS_SEGMENT_USAGE_ACTIVE,
@@ -710,6 +718,48 @@ static inline int nilfs_suinfo_clean(const struct nilfs_suinfo *si)
}
/* ioctl */
+/**
+ * nilfs_suinfo_update - segment usage information update
+ * @sup_segnum: segment number
+ * @sup_flags: flags for which fields are active in sup_sui
+ * @sup_reserved: reserved necessary for alignment
+ * @sup_sui: segment usage information
+ */
+struct nilfs_suinfo_update {
+ __u64 sup_segnum;
+ __u32 sup_flags;
+ __u32 sup_reserved;
+ struct nilfs_suinfo sup_sui;
+};
+
+enum {
+ NILFS_SUINFO_UPDATE_LASTMOD,
+ NILFS_SUINFO_UPDATE_NBLOCKS,
+ NILFS_SUINFO_UPDATE_FLAGS,
+ __NR_NILFS_SUINFO_UPDATE_FIELDS,
+};
+
+#define NILFS_SUINFO_UPDATE_FNS(flag, name) \
+static inline void \
+nilfs_suinfo_update_set_##name(struct nilfs_suinfo_update *sup) \
+{ \
+ sup->sup_flags |= 1UL << NILFS_SUINFO_UPDATE_##flag; \
+} \
+static inline void \
+nilfs_suinfo_update_clear_##name(struct nilfs_suinfo_update *sup) \
+{ \
+ sup->sup_flags &= ~(1UL << NILFS_SUINFO_UPDATE_##flag); \
+} \
+static inline int \
+nilfs_suinfo_update_##name(const struct nilfs_suinfo_update *sup) \
+{ \
+ return !!(sup->sup_flags & (1UL << NILFS_SUINFO_UPDATE_##flag));\
+}
+
+NILFS_SUINFO_UPDATE_FNS(LASTMOD, lastmod)
+NILFS_SUINFO_UPDATE_FNS(NBLOCKS, nblocks)
+NILFS_SUINFO_UPDATE_FNS(FLAGS, flags)
+
enum {
NILFS_CHECKPOINT,
NILFS_SNAPSHOT,
@@ -863,5 +913,7 @@ struct nilfs_bdesc {
_IOW(NILFS_IOCTL_IDENT, 0x8B, __u64)
#define NILFS_IOCTL_SET_ALLOC_RANGE \
_IOW(NILFS_IOCTL_IDENT, 0x8C, __u64[2])
+#define NILFS_IOCTL_SET_SUINFO \
+ _IOW(NILFS_IOCTL_IDENT, 0x8D, struct nilfs_argv)
#endif /* _LINUX_NILFS_FS_H */
diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h
index fd4f2d1cdf6c..c8d7f3965fff 100644
--- a/include/linux/nl802154.h
+++ b/include/linux/nl802154.h
@@ -70,6 +70,16 @@ enum {
IEEE802154_ATTR_PHY_NAME,
IEEE802154_ATTR_DEV_TYPE,
+ IEEE802154_ATTR_TXPOWER,
+ IEEE802154_ATTR_LBT_ENABLED,
+ IEEE802154_ATTR_CCA_MODE,
+ IEEE802154_ATTR_CCA_ED_LEVEL,
+ IEEE802154_ATTR_CSMA_RETRIES,
+ IEEE802154_ATTR_CSMA_MIN_BE,
+ IEEE802154_ATTR_CSMA_MAX_BE,
+
+ IEEE802154_ATTR_FRAME_RETRIES,
+
__IEEE802154_ATTR_MAX,
};
@@ -122,6 +132,8 @@ enum {
IEEE802154_ADD_IFACE,
IEEE802154_DEL_IFACE,
+ IEEE802154_SET_MACPARAMS,
+
__IEEE802154_CMD_MAX,
};
diff --git a/include/linux/of.h b/include/linux/of.h
index 83d1ac80c91e..919bf211877d 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -18,7 +18,7 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/errno.h>
-#include <linux/kref.h>
+#include <linux/kobject.h>
#include <linux/mod_devicetable.h>
#include <linux/spinlock.h>
#include <linux/topology.h>
@@ -37,6 +37,7 @@ struct property {
struct property *next;
unsigned long _flags;
unsigned int unique_id;
+ struct bin_attribute attr;
};
#if defined(CONFIG_SPARC)
@@ -56,8 +57,7 @@ struct device_node {
struct device_node *sibling;
struct device_node *next; /* next device of same type */
struct device_node *allnext; /* next in list of all nodes */
- struct proc_dir_entry *pde; /* this node's proc directory */
- struct kref kref;
+ struct kobject kobj;
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
@@ -67,13 +67,34 @@ struct device_node {
#endif
};
-#define MAX_PHANDLE_ARGS 8
+#define MAX_PHANDLE_ARGS 16
struct of_phandle_args {
struct device_node *np;
int args_count;
uint32_t args[MAX_PHANDLE_ARGS];
};
+extern int of_node_add(struct device_node *node);
+
+/* initialize a node */
+extern struct kobj_type of_node_ktype;
+static inline void of_node_init(struct device_node *node)
+{
+ kobject_init(&node->kobj, &of_node_ktype);
+}
+
+/* true when node is initialized */
+static inline int of_node_is_initialized(struct device_node *node)
+{
+ return node && node->kobj.state_initialized;
+}
+
+/* true when node is attached (i.e. present on sysfs) */
+static inline int of_node_is_attached(struct device_node *node)
+{
+ return node && node->kobj.state_in_sysfs;
+}
+
#ifdef CONFIG_OF_DYNAMIC
extern struct device_node *of_node_get(struct device_node *node);
extern void of_node_put(struct device_node *node);
@@ -114,6 +135,26 @@ static inline void of_node_set_flag(struct device_node *n, unsigned long flag)
set_bit(flag, &n->_flags);
}
+static inline void of_node_clear_flag(struct device_node *n, unsigned long flag)
+{
+ clear_bit(flag, &n->_flags);
+}
+
+static inline int of_property_check_flag(struct property *p, unsigned long flag)
+{
+ return test_bit(flag, &p->_flags);
+}
+
+static inline void of_property_set_flag(struct property *p, unsigned long flag)
+{
+ set_bit(flag, &p->_flags);
+}
+
+static inline void of_property_clear_flag(struct property *p, unsigned long flag)
+{
+ clear_bit(flag, &p->_flags);
+}
+
extern struct device_node *of_find_all_nodes(struct device_node *prev);
/*
@@ -167,6 +208,8 @@ static inline const char *of_node_full_name(const struct device_node *np)
return np ? np->full_name : "<no-node>";
}
+#define for_each_of_allnodes(dn) \
+ for (dn = of_allnodes; dn; dn = dn->allnext)
extern struct device_node *of_find_node_by_name(struct device_node *from,
const char *name);
extern struct device_node *of_find_node_by_type(struct device_node *from,
@@ -709,14 +752,4 @@ static inline int of_get_available_child_count(const struct device_node *np)
return num;
}
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_PROC_DEVICETREE)
-extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *);
-extern void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop);
-extern void proc_device_tree_remove_prop(struct proc_dir_entry *pde,
- struct property *prop);
-extern void proc_device_tree_update_prop(struct proc_dir_entry *pde,
- struct property *newprop,
- struct property *oldprop);
-#endif
-
#endif /* _LINUX_OF_H */
diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
index 2b77058a7335..ddd7219af8ac 100644
--- a/include/linux/of_fdt.h
+++ b/include/linux/of_fdt.h
@@ -98,7 +98,10 @@ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname,
int depth, void *data);
extern int early_init_dt_scan_memory(unsigned long node, const char *uname,
int depth, void *data);
+extern void early_init_fdt_scan_reserved_mem(void);
extern void early_init_dt_add_memory_arch(u64 base, u64 size);
+extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size,
+ bool no_map);
extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align);
extern u64 dt_mem_next_cell(int s, __be32 **cellp);
@@ -118,6 +121,7 @@ extern void unflatten_and_copy_device_tree(void);
extern void early_init_devtree(void *);
extern void early_get_first_memblock_info(void *, phys_addr_t *);
#else /* CONFIG_OF_FLATTREE */
+static inline void early_init_fdt_scan_reserved_mem(void) {}
static inline const char *of_flat_dt_get_machine_name(void) { return NULL; }
static inline void unflatten_device_tree(void) {}
static inline void unflatten_and_copy_device_tree(void) {}
diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h
index cb32d9c1e8dc..e266caa36402 100644
--- a/include/linux/of_mtd.h
+++ b/include/linux/of_mtd.h
@@ -13,6 +13,8 @@
#include <linux/of.h>
int of_get_nand_ecc_mode(struct device_node *np);
+int of_get_nand_ecc_step_size(struct device_node *np);
+int of_get_nand_ecc_strength(struct device_node *np);
int of_get_nand_bus_width(struct device_node *np);
bool of_get_nand_on_flash_bbt(struct device_node *np);
@@ -23,6 +25,16 @@ static inline int of_get_nand_ecc_mode(struct device_node *np)
return -ENOSYS;
}
+static inline int of_get_nand_ecc_step_size(struct device_node *np)
+{
+ return -ENOSYS;
+}
+
+static inline int of_get_nand_ecc_strength(struct device_node *np)
+{
+ return -ENOSYS;
+}
+
static inline int of_get_nand_bus_width(struct device_node *np)
{
return -ENOSYS;
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
new file mode 100644
index 000000000000..9b1fbb7f29fc
--- /dev/null
+++ b/include/linux/of_reserved_mem.h
@@ -0,0 +1,53 @@
+#ifndef __OF_RESERVED_MEM_H
+#define __OF_RESERVED_MEM_H
+
+struct device;
+struct of_phandle_args;
+struct reserved_mem_ops;
+
+struct reserved_mem {
+ const char *name;
+ unsigned long fdt_node;
+ const struct reserved_mem_ops *ops;
+ phys_addr_t base;
+ phys_addr_t size;
+ void *priv;
+};
+
+struct reserved_mem_ops {
+ void (*device_init)(struct reserved_mem *rmem,
+ struct device *dev);
+ void (*device_release)(struct reserved_mem *rmem,
+ struct device *dev);
+};
+
+typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem,
+ unsigned long node, const char *uname);
+
+#ifdef CONFIG_OF_RESERVED_MEM
+void fdt_init_reserved_mem(void);
+void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
+ phys_addr_t base, phys_addr_t size);
+
+#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
+ static const struct of_device_id __reservedmem_of_table_##name \
+ __used __section(__reservedmem_of_table) \
+ = { .compatible = compat, \
+ .data = (init == (reservedmem_of_init_fn)NULL) ? \
+ init : init }
+
+#else
+static inline void fdt_init_reserved_mem(void) { }
+static inline void fdt_reserved_mem_save_node(unsigned long node,
+ const char *uname, phys_addr_t base, phys_addr_t size) { }
+
+#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
+ static const struct of_device_id __reservedmem_of_table_##name \
+ __attribute__((unused)) \
+ = { .compatible = compat, \
+ .data = (init == (reservedmem_of_init_fn)NULL) ? \
+ init : init }
+
+#endif
+
+#endif /* __OF_RESERVED_MEM_H */
diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h
index 7af25a9c9c51..41a13e70f41f 100644
--- a/include/linux/omap-dma.h
+++ b/include/linux/omap-dma.h
@@ -268,14 +268,27 @@ struct omap_dma_dev_attr {
u32 dev_caps;
u16 lch_count;
u16 chan_count;
- struct omap_dma_lch *chan;
+};
+
+enum {
+ OMAP_DMA_REG_NONE,
+ OMAP_DMA_REG_16BIT,
+ OMAP_DMA_REG_2X16BIT,
+ OMAP_DMA_REG_32BIT,
+};
+
+struct omap_dma_reg {
+ u16 offset;
+ u8 stride;
+ u8 type;
};
/* System DMA platform data structure */
struct omap_system_dma_plat_info {
+ const struct omap_dma_reg *reg_map;
+ unsigned channel_stride;
struct omap_dma_dev_attr *dma_attr;
u32 errata;
- void (*disable_irq_lch)(int lch);
void (*show_dma_caps)(void);
void (*clear_lch_regs)(int lch);
void (*clear_dma)(int lch);
@@ -289,8 +302,12 @@ struct omap_system_dma_plat_info {
#define dma_omap2plus() 0
#endif
#define dma_omap1() (!dma_omap2plus())
-#define dma_omap15xx() ((dma_omap1() && (d->dev_caps & ENABLE_1510_MODE)))
-#define dma_omap16xx() ((dma_omap1() && (d->dev_caps & ENABLE_16XX_MODE)))
+#define __dma_omap15xx(d) (dma_omap1() && (d)->dev_caps & ENABLE_1510_MODE)
+#define __dma_omap16xx(d) (dma_omap1() && (d)->dev_caps & ENABLE_16XX_MODE)
+#define dma_omap15xx() __dma_omap15xx(d)
+#define dma_omap16xx() __dma_omap16xx(d)
+
+extern struct omap_system_dma_plat_info *omap_get_plat_info(void);
extern void omap_set_dma_priority(int lch, int dst_port, int priority);
extern int omap_request_dma(int dev_id, const char *dev_name,
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 1710d1b060ba..45598f1e9aa3 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -25,6 +25,7 @@ enum mapping_flags {
AS_MM_ALL_LOCKS = __GFP_BITS_SHIFT + 2, /* under mm_take_all_locks() */
AS_UNEVICTABLE = __GFP_BITS_SHIFT + 3, /* e.g., ramdisk, SHM_LOCK */
AS_BALLOON_MAP = __GFP_BITS_SHIFT + 4, /* balloon page special map */
+ AS_EXITING = __GFP_BITS_SHIFT + 5, /* final truncate in progress */
};
static inline void mapping_set_error(struct address_space *mapping, int error)
@@ -69,6 +70,16 @@ static inline int mapping_balloon(struct address_space *mapping)
return mapping && test_bit(AS_BALLOON_MAP, &mapping->flags);
}
+static inline void mapping_set_exiting(struct address_space *mapping)
+{
+ set_bit(AS_EXITING, &mapping->flags);
+}
+
+static inline int mapping_exiting(struct address_space *mapping)
+{
+ return test_bit(AS_EXITING, &mapping->flags);
+}
+
static inline gfp_t mapping_gfp_mask(struct address_space * mapping)
{
return (__force gfp_t)mapping->flags & __GFP_BITS_MASK;
@@ -243,12 +254,20 @@ static inline struct page *page_cache_alloc_readahead(struct address_space *x)
typedef int filler_t(void *, struct page *);
-extern struct page * find_get_page(struct address_space *mapping,
- pgoff_t index);
-extern struct page * find_lock_page(struct address_space *mapping,
- pgoff_t index);
-extern struct page * find_or_create_page(struct address_space *mapping,
- pgoff_t index, gfp_t gfp_mask);
+pgoff_t page_cache_next_hole(struct address_space *mapping,
+ pgoff_t index, unsigned long max_scan);
+pgoff_t page_cache_prev_hole(struct address_space *mapping,
+ pgoff_t index, unsigned long max_scan);
+
+struct page *find_get_entry(struct address_space *mapping, pgoff_t offset);
+struct page *find_get_page(struct address_space *mapping, pgoff_t offset);
+struct page *find_lock_entry(struct address_space *mapping, pgoff_t offset);
+struct page *find_lock_page(struct address_space *mapping, pgoff_t offset);
+struct page *find_or_create_page(struct address_space *mapping, pgoff_t index,
+ gfp_t gfp_mask);
+unsigned find_get_entries(struct address_space *mapping, pgoff_t start,
+ unsigned int nr_entries, struct page **entries,
+ pgoff_t *indices);
unsigned find_get_pages(struct address_space *mapping, pgoff_t start,
unsigned int nr_pages, struct page **pages);
unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t start,
@@ -270,8 +289,6 @@ static inline struct page *grab_cache_page(struct address_space *mapping,
extern struct page * grab_cache_page_nowait(struct address_space *mapping,
pgoff_t index);
-extern struct page * read_cache_page_async(struct address_space *mapping,
- pgoff_t index, filler_t *filler, void *data);
extern struct page * read_cache_page(struct address_space *mapping,
pgoff_t index, filler_t *filler, void *data);
extern struct page * read_cache_page_gfp(struct address_space *mapping,
@@ -279,14 +296,6 @@ extern struct page * read_cache_page_gfp(struct address_space *mapping,
extern int read_cache_pages(struct address_space *mapping,
struct list_head *pages, filler_t *filler, void *data);
-static inline struct page *read_mapping_page_async(
- struct address_space *mapping,
- pgoff_t index, void *data)
-{
- filler_t *filler = (filler_t *)mapping->a_ops->readpage;
- return read_cache_page_async(mapping, index, filler, data);
-}
-
static inline struct page *read_mapping_page(struct address_space *mapping,
pgoff_t index, void *data)
{
@@ -539,7 +548,7 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
int add_to_page_cache_lru(struct page *page, struct address_space *mapping,
pgoff_t index, gfp_t gfp_mask);
extern void delete_from_page_cache(struct page *page);
-extern void __delete_from_page_cache(struct page *page);
+extern void __delete_from_page_cache(struct page *page, void *shadow);
int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask);
/*
diff --git a/include/linux/pagevec.h b/include/linux/pagevec.h
index e4dbfab37729..b45d391b4540 100644
--- a/include/linux/pagevec.h
+++ b/include/linux/pagevec.h
@@ -22,6 +22,11 @@ struct pagevec {
void __pagevec_release(struct pagevec *pvec);
void __pagevec_lru_add(struct pagevec *pvec);
+unsigned pagevec_lookup_entries(struct pagevec *pvec,
+ struct address_space *mapping,
+ pgoff_t start, unsigned nr_entries,
+ pgoff_t *indices);
+void pagevec_remove_exceptionals(struct pagevec *pvec);
unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
pgoff_t start, unsigned nr_pages);
unsigned pagevec_lookup_tag(struct pagevec *pvec,
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index e56b07f5c9b6..3356abcfff18 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -835,6 +835,8 @@ do { \
{ .notifier_call = fn, .priority = CPU_PRI_PERF }; \
unsigned long cpu = smp_processor_id(); \
unsigned long flags; \
+ \
+ cpu_notifier_register_begin(); \
fn(&fn##_nb, (unsigned long)CPU_UP_PREPARE, \
(void *)(unsigned long)cpu); \
local_irq_save(flags); \
@@ -843,9 +845,21 @@ do { \
local_irq_restore(flags); \
fn(&fn##_nb, (unsigned long)CPU_ONLINE, \
(void *)(unsigned long)cpu); \
- register_cpu_notifier(&fn##_nb); \
+ __register_cpu_notifier(&fn##_nb); \
+ cpu_notifier_register_done(); \
} while (0)
+/*
+ * Bare-bones version of perf_cpu_notifier(), which doesn't invoke the
+ * callback for already online CPUs.
+ */
+#define __perf_cpu_notifier(fn) \
+do { \
+ static struct notifier_block fn##_nb = \
+ { .notifier_call = fn, .priority = CPU_PRI_PERF }; \
+ \
+ __register_cpu_notifier(&fn##_nb); \
+} while (0)
struct perf_pmu_events_attr {
struct device_attribute attr;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 565188ca328f..24126c4b27b5 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -74,8 +74,53 @@ typedef enum {
PHY_INTERFACE_MODE_RTBI,
PHY_INTERFACE_MODE_SMII,
PHY_INTERFACE_MODE_XGMII,
+ PHY_INTERFACE_MODE_MOCA,
+ PHY_INTERFACE_MODE_MAX,
} phy_interface_t;
+/**
+ * It maps 'enum phy_interface_t' found in include/linux/phy.h
+ * into the device tree binding of 'phy-mode', so that Ethernet
+ * device driver can get phy interface from device tree.
+ */
+static inline const char *phy_modes(phy_interface_t interface)
+{
+ switch (interface) {
+ case PHY_INTERFACE_MODE_NA:
+ return "";
+ case PHY_INTERFACE_MODE_MII:
+ return "mii";
+ case PHY_INTERFACE_MODE_GMII:
+ return "gmii";
+ case PHY_INTERFACE_MODE_SGMII:
+ return "sgmii";
+ case PHY_INTERFACE_MODE_TBI:
+ return "tbi";
+ case PHY_INTERFACE_MODE_REVMII:
+ return "rev-mii";
+ case PHY_INTERFACE_MODE_RMII:
+ return "rmii";
+ case PHY_INTERFACE_MODE_RGMII:
+ return "rgmii";
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ return "rgmii-id";
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ return "rgmii-rxid";
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ return "rgmii-txid";
+ case PHY_INTERFACE_MODE_RTBI:
+ return "rtbi";
+ case PHY_INTERFACE_MODE_SMII:
+ return "smii";
+ case PHY_INTERFACE_MODE_XGMII:
+ return "xgmii";
+ case PHY_INTERFACE_MODE_MOCA:
+ return "moca";
+ default:
+ return "unknown";
+ }
+}
+
#define PHY_INIT_TIMEOUT 100000
#define PHY_STATE_TIME 1
@@ -308,6 +353,7 @@ struct phy_device {
struct phy_c45_device_ids c45_ids;
bool is_c45;
bool is_internal;
+ bool has_fixups;
enum phy_state state;
@@ -394,6 +440,11 @@ struct phy_driver {
u32 flags;
/*
+ * Called to issue a PHY software reset
+ */
+ int (*soft_reset)(struct phy_device *phydev);
+
+ /*
* Called to initialize the PHY,
* including after a reset
*/
@@ -417,6 +468,9 @@ struct phy_driver {
*/
int (*config_aneg)(struct phy_device *phydev);
+ /* Determines the auto negotiation result */
+ int (*aneg_done)(struct phy_device *phydev);
+
/* Determines the negotiated speed and duplex */
int (*read_status)(struct phy_device *phydev);
@@ -612,10 +666,12 @@ static inline int phy_read_status(struct phy_device *phydev)
int genphy_setup_forced(struct phy_device *phydev);
int genphy_restart_aneg(struct phy_device *phydev);
int genphy_config_aneg(struct phy_device *phydev);
+int genphy_aneg_done(struct phy_device *phydev);
int genphy_update_link(struct phy_device *phydev);
int genphy_read_status(struct phy_device *phydev);
int genphy_suspend(struct phy_device *phydev);
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);
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index ab5752692113..4d9389c79e61 100644
--- a/include/linux/pipe_fs_i.h
+++ b/include/linux/pipe_fs_i.h
@@ -35,7 +35,7 @@ struct pipe_buffer {
* @tmp_page: cached released page
* @readers: number of current readers of this pipe
* @writers: number of current writers of this pipe
- * @files: number of struct file refering this pipe (protected by ->i_lock)
+ * @files: number of struct file referring this pipe (protected by ->i_lock)
* @waiting_writers: number of writers blocked waiting for room
* @r_counter: reader counter
* @w_counter: writer counter
diff --git a/include/linux/platform_data/ata-samsung_cf.h b/include/linux/platform_data/ata-samsung_cf.h
index 2a3855a8372a..c2049e3d7444 100644
--- a/include/linux/platform_data/ata-samsung_cf.h
+++ b/include/linux/platform_data/ata-samsung_cf.h
@@ -1,5 +1,4 @@
-/* linux/arch/arm/plat-samsung/include/plat/ata.h
- *
+/*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
@@ -10,8 +9,8 @@
* published by the Free Software Foundation.
*/
-#ifndef __ASM_PLAT_ATA_H
-#define __ASM_PLAT_ATA_H __FILE__
+#ifndef __ATA_SAMSUNG_CF_H
+#define __ATA_SAMSUNG_CF_H __FILE__
/**
* struct s3c_ide_platdata - S3C IDE driver platform data.
@@ -33,4 +32,4 @@ extern void s3c64xx_ide_setup_gpio(void);
extern void s5pc100_ide_setup_gpio(void);
extern void s5pv210_ide_setup_gpio(void);
-#endif /*__ASM_PLAT_ATA_H */
+#endif /*__ATA_SAMSUNG_CF_H */
diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h
index cea9f70133c5..e26b0c14edea 100644
--- a/include/linux/platform_data/atmel.h
+++ b/include/linux/platform_data/atmel.h
@@ -84,6 +84,7 @@ struct atmel_uart_data {
short use_dma_rx; /* use receive DMA? */
void __iomem *regs; /* virt. base address, if any */
struct serial_rs485 rs485; /* rs485 settings */
+ int rts_gpio; /* optional RTS GPIO */
};
/* Touchscreen Controller */
diff --git a/include/linux/platform_data/clk-integrator.h b/include/linux/platform_data/clk-integrator.h
index 280edac9d0a5..addd48cac625 100644
--- a/include/linux/platform_data/clk-integrator.h
+++ b/include/linux/platform_data/clk-integrator.h
@@ -1,3 +1,2 @@
-void integrator_clk_init(bool is_cp);
void integrator_impd1_clk_init(void __iomem *base, unsigned int id);
void integrator_impd1_clk_exit(unsigned int id);
diff --git a/include/linux/platform_data/elm.h b/include/linux/platform_data/elm.h
index bf0a83b7ed9d..4edb40676b3f 100644
--- a/include/linux/platform_data/elm.h
+++ b/include/linux/platform_data/elm.h
@@ -26,13 +26,6 @@ enum bch_ecc {
/* ELM support 8 error syndrome process */
#define ERROR_VECTOR_MAX 8
-#define BCH8_ECC_OOB_BYTES 13
-#define BCH4_ECC_OOB_BYTES 7
-/* RBL requires 14 byte even though BCH8 uses only 13 byte */
-#define BCH8_SIZE (BCH8_ECC_OOB_BYTES + 1)
-/* Uses 1 extra byte to handle erased pages */
-#define BCH4_SIZE (BCH4_ECC_OOB_BYTES + 1)
-
/**
* struct elm_errorvec - error vector for elm
* @error_reported: set true for vectors error is reported
@@ -50,5 +43,6 @@ struct elm_errorvec {
void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
struct elm_errorvec *err_vec);
-int elm_config(struct device *dev, enum bch_ecc bch_type);
+int elm_config(struct device *dev, enum bch_ecc bch_type,
+ int ecc_steps, int ecc_step_size, int ecc_syndrome_size);
#endif /* __ELM_H */
diff --git a/include/linux/platform_data/gpio-davinci.h b/include/linux/platform_data/gpio-davinci.h
index fbe2f7535741..6ace3fd32b6a 100644
--- a/include/linux/platform_data/gpio-davinci.h
+++ b/include/linux/platform_data/gpio-davinci.h
@@ -21,10 +21,6 @@
#include <asm-generic/gpio.h>
-enum davinci_gpio_type {
- GPIO_TYPE_TNETV107X = 0,
-};
-
struct davinci_gpio_platform_data {
u32 ngpio;
u32 gpio_unbanked;
diff --git a/include/linux/platform_data/mtd-davinci-aemif.h b/include/linux/platform_data/mtd-davinci-aemif.h
index 05b293443097..97948ac2bb9b 100644
--- a/include/linux/platform_data/mtd-davinci-aemif.h
+++ b/include/linux/platform_data/mtd-davinci-aemif.h
@@ -10,6 +10,8 @@
#ifndef _MACH_DAVINCI_AEMIF_H
#define _MACH_DAVINCI_AEMIF_H
+#include <linux/platform_device.h>
+
#define NRCSR_OFFSET 0x00
#define AWCCR_OFFSET 0x04
#define A1CR_OFFSET 0x10
@@ -31,6 +33,5 @@ struct davinci_aemif_timing {
u8 ta;
};
-int davinci_aemif_setup_timing(struct davinci_aemif_timing *t,
- void __iomem *base, unsigned cs);
+int davinci_aemif_setup(struct platform_device *pdev);
#endif
diff --git a/include/linux/platform_data/mtd-nand-s3c2410.h b/include/linux/platform_data/mtd-nand-s3c2410.h
index b64115fa93a4..36bb92172f47 100644
--- a/include/linux/platform_data/mtd-nand-s3c2410.h
+++ b/include/linux/platform_data/mtd-nand-s3c2410.h
@@ -1,5 +1,4 @@
-/* arch/arm/mach-s3c2410/include/mach/nand.h
- *
+/*
* Copyright (c) 2004 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
@@ -10,6 +9,9 @@
* published by the Free Software Foundation.
*/
+#ifndef __MTD_NAND_S3C2410_H
+#define __MTD_NAND_S3C2410_H
+
/**
* struct s3c2410_nand_set - define a set of one or more nand chips
* @disable_ecc: Entirely disable ECC - Dangerous
@@ -65,3 +67,5 @@ struct s3c2410_platform_nand {
* it with the s3c_device_nand. This allows @nand to be __initdata.
*/
extern void s3c_nand_set_platdata(struct s3c2410_platform_nand *nand);
+
+#endif /*__MTD_NAND_S3C2410_H */
diff --git a/include/linux/platform_data/touchscreen-s3c2410.h b/include/linux/platform_data/touchscreen-s3c2410.h
index 26fdb22e0fc2..58dc7c5ae63b 100644
--- a/include/linux/platform_data/touchscreen-s3c2410.h
+++ b/include/linux/platform_data/touchscreen-s3c2410.h
@@ -1,5 +1,4 @@
-/* arch/arm/plat-samsung/include/plat/ts.h
- *
+/*
* Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
*
* This program is free software; you can redistribute it and/or modify
@@ -7,14 +6,14 @@
* published by the Free Software Foundation.
*/
-#ifndef __ASM_ARM_TS_H
-#define __ASM_ARM_TS_H
+#ifndef __TOUCHSCREEN_S3C2410_H
+#define __TOUCHSCREEN_S3C2410_H
struct s3c2410_ts_mach_info {
- int delay;
- int presc;
- int oversampling_shift;
- void (*cfg_gpio)(struct platform_device *dev);
+ int delay;
+ int presc;
+ int oversampling_shift;
+ void (*cfg_gpio)(struct platform_device *dev);
};
extern void s3c24xx_ts_set_platdata(struct s3c2410_ts_mach_info *);
@@ -22,4 +21,4 @@ extern void s3c24xx_ts_set_platdata(struct s3c2410_ts_mach_info *);
/* defined by architecture to configure gpio */
extern void s3c24xx_ts_cfg_gpio(struct platform_device *dev);
-#endif /* __ASM_ARM_TS_H */
+#endif /*__TOUCHSCREEN_S3C2410_H */
diff --git a/include/linux/platform_data/video-imxfb.h b/include/linux/platform_data/video-imxfb.h
index 9de8f062ad5d..18e908324549 100644
--- a/include/linux/platform_data/video-imxfb.h
+++ b/include/linux/platform_data/video-imxfb.h
@@ -61,24 +61,12 @@ struct imx_fb_platform_data {
struct imx_fb_videomode *mode;
int num_modes;
- u_int cmap_greyscale:1,
- cmap_inverse:1,
- cmap_static:1,
- unused:29;
-
u_int pwmr;
u_int lscr1;
u_int dmacr;
- u_char * fixed_screen_cpu;
- dma_addr_t fixed_screen_dma;
-
int (*init)(struct platform_device *);
void (*exit)(struct platform_device *);
-
- void (*lcd_power)(int);
- void (*backlight_power)(int);
};
-void set_imx_fb_info(struct imx_fb_platform_data *);
#endif /* ifndef __MACH_IMXFB_H__ */
diff --git a/include/linux/printk.h b/include/linux/printk.h
index fa47e2708c01..8752f7595b27 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -24,13 +24,9 @@ static inline int printk_get_level(const char *buffer)
static inline const char *printk_skip_level(const char *buffer)
{
- if (printk_get_level(buffer)) {
- switch (buffer[1]) {
- case '0' ... '7':
- case 'd': /* KERN_DEFAULT */
- return buffer + 2;
- }
- }
+ if (printk_get_level(buffer))
+ return buffer + 2;
+
return buffer;
}
@@ -124,9 +120,9 @@ asmlinkage __printf(1, 0)
int vprintk(const char *fmt, va_list args);
asmlinkage __printf(5, 6) __cold
-asmlinkage int printk_emit(int facility, int level,
- const char *dict, size_t dictlen,
- const char *fmt, ...);
+int printk_emit(int facility, int level,
+ const char *dict, size_t dictlen,
+ const char *fmt, ...);
asmlinkage __printf(1, 2) __cold
int printk(const char *fmt, ...);
diff --git a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h
index 1dc420ba213a..7dfed71d76a6 100644
--- a/include/linux/ptp_classify.h
+++ b/include/linux/ptp_classify.h
@@ -23,15 +23,8 @@
#ifndef _PTP_CLASSIFY_H_
#define _PTP_CLASSIFY_H_
-#include <linux/if_ether.h>
-#include <linux/if_vlan.h>
#include <linux/ip.h>
-#include <linux/filter.h>
-#ifdef __KERNEL__
-#include <linux/in.h>
-#else
-#include <netinet/in.h>
-#endif
+#include <linux/skbuff.h>
#define PTP_CLASS_NONE 0x00 /* not a PTP event message */
#define PTP_CLASS_V1 0x01 /* protocol version 1 */
@@ -44,7 +37,7 @@
#define PTP_CLASS_PMASK 0xf0 /* mask for the packet type field */
#define PTP_CLASS_V1_IPV4 (PTP_CLASS_V1 | PTP_CLASS_IPV4)
-#define PTP_CLASS_V1_IPV6 (PTP_CLASS_V1 | PTP_CLASS_IPV6) /*probably DNE*/
+#define PTP_CLASS_V1_IPV6 (PTP_CLASS_V1 | PTP_CLASS_IPV6) /* probably DNE */
#define PTP_CLASS_V2_IPV4 (PTP_CLASS_V2 | PTP_CLASS_IPV4)
#define PTP_CLASS_V2_IPV6 (PTP_CLASS_V2 | PTP_CLASS_IPV6)
#define PTP_CLASS_V2_L2 (PTP_CLASS_V2 | PTP_CLASS_L2)
@@ -53,88 +46,34 @@
#define PTP_EV_PORT 319
#define PTP_GEN_BIT 0x08 /* indicates general message, if set in message type */
-#define OFF_ETYPE 12
-#define OFF_IHL 14
-#define OFF_FRAG 20
-#define OFF_PROTO4 23
-#define OFF_NEXT 6
-#define OFF_UDP_DST 2
-
#define OFF_PTP_SOURCE_UUID 22 /* PTPv1 only */
#define OFF_PTP_SEQUENCE_ID 30
#define OFF_PTP_CONTROL 32 /* PTPv1 only */
-#define IPV4_HLEN(data) (((struct iphdr *)(data + OFF_IHL))->ihl << 2)
-
+/* Below defines should actually be removed at some point in time. */
#define IP6_HLEN 40
#define UDP_HLEN 8
-
-#define RELOFF_DST4 (ETH_HLEN + OFF_UDP_DST)
-#define OFF_DST6 (ETH_HLEN + IP6_HLEN + OFF_UDP_DST)
+#define OFF_IHL 14
#define OFF_PTP6 (ETH_HLEN + IP6_HLEN + UDP_HLEN)
+#define IPV4_HLEN(data) (((struct iphdr *)(data + OFF_IHL))->ihl << 2)
-#define OP_AND (BPF_ALU | BPF_AND | BPF_K)
-#define OP_JEQ (BPF_JMP | BPF_JEQ | BPF_K)
-#define OP_JSET (BPF_JMP | BPF_JSET | BPF_K)
-#define OP_LDB (BPF_LD | BPF_B | BPF_ABS)
-#define OP_LDH (BPF_LD | BPF_H | BPF_ABS)
-#define OP_LDHI (BPF_LD | BPF_H | BPF_IND)
-#define OP_LDX (BPF_LDX | BPF_B | BPF_MSH)
-#define OP_OR (BPF_ALU | BPF_OR | BPF_K)
-#define OP_RETA (BPF_RET | BPF_A)
-#define OP_RETK (BPF_RET | BPF_K)
+#if defined(CONFIG_NET_PTP_CLASSIFY)
+/**
+ * ptp_classify_raw - classify a PTP packet
+ * @skb: buffer
+ *
+ * Runs a minimal BPF dissector to classify a network packet to
+ * determine the PTP class. In case the skb does not contain any
+ * PTP protocol data, PTP_CLASS_NONE will be returned, otherwise
+ * PTP_CLASS_V1_IPV{4,6}, PTP_CLASS_V2_IPV{4,6} or
+ * PTP_CLASS_V2_{L2,VLAN}, depending on the packet content.
+ */
+unsigned int ptp_classify_raw(const struct sk_buff *skb);
-static inline int ptp_filter_init(struct sock_filter *f, int len)
+void __init ptp_classifier_init(void);
+#else
+static inline void ptp_classifier_init(void)
{
- if (OP_LDH == f[0].code)
- return sk_chk_filter(f, len);
- else
- return 0;
}
-
-#define PTP_FILTER \
- {OP_LDH, 0, 0, OFF_ETYPE }, /* */ \
- {OP_JEQ, 0, 12, ETH_P_IP }, /* f goto L20 */ \
- {OP_LDB, 0, 0, OFF_PROTO4 }, /* */ \
- {OP_JEQ, 0, 9, IPPROTO_UDP }, /* f goto L10 */ \
- {OP_LDH, 0, 0, OFF_FRAG }, /* */ \
- {OP_JSET, 7, 0, 0x1fff }, /* t goto L11 */ \
- {OP_LDX, 0, 0, OFF_IHL }, /* */ \
- {OP_LDHI, 0, 0, RELOFF_DST4 }, /* */ \
- {OP_JEQ, 0, 4, PTP_EV_PORT }, /* f goto L12 */ \
- {OP_LDHI, 0, 0, ETH_HLEN + UDP_HLEN }, /* */ \
- {OP_AND, 0, 0, PTP_CLASS_VMASK }, /* */ \
- {OP_OR, 0, 0, PTP_CLASS_IPV4 }, /* */ \
- {OP_RETA, 0, 0, 0 }, /* */ \
-/*L1x*/ {OP_RETK, 0, 0, PTP_CLASS_NONE }, /* */ \
-/*L20*/ {OP_JEQ, 0, 9, ETH_P_IPV6 }, /* f goto L40 */ \
- {OP_LDB, 0, 0, ETH_HLEN + OFF_NEXT }, /* */ \
- {OP_JEQ, 0, 6, IPPROTO_UDP }, /* f goto L30 */ \
- {OP_LDH, 0, 0, OFF_DST6 }, /* */ \
- {OP_JEQ, 0, 4, PTP_EV_PORT }, /* f goto L31 */ \
- {OP_LDH, 0, 0, OFF_PTP6 }, /* */ \
- {OP_AND, 0, 0, PTP_CLASS_VMASK }, /* */ \
- {OP_OR, 0, 0, PTP_CLASS_IPV6 }, /* */ \
- {OP_RETA, 0, 0, 0 }, /* */ \
-/*L3x*/ {OP_RETK, 0, 0, PTP_CLASS_NONE }, /* */ \
-/*L40*/ {OP_JEQ, 0, 9, ETH_P_8021Q }, /* f goto L50 */ \
- {OP_LDH, 0, 0, OFF_ETYPE + 4 }, /* */ \
- {OP_JEQ, 0, 15, ETH_P_1588 }, /* f goto L60 */ \
- {OP_LDB, 0, 0, ETH_HLEN + VLAN_HLEN }, /* */ \
- {OP_AND, 0, 0, PTP_GEN_BIT }, /* */ \
- {OP_JEQ, 0, 12, 0 }, /* f goto L6x */ \
- {OP_LDH, 0, 0, ETH_HLEN + VLAN_HLEN }, /* */ \
- {OP_AND, 0, 0, PTP_CLASS_VMASK }, /* */ \
- {OP_OR, 0, 0, PTP_CLASS_VLAN }, /* */ \
- {OP_RETA, 0, 0, 0 }, /* */ \
-/*L50*/ {OP_JEQ, 0, 7, ETH_P_1588 }, /* f goto L61 */ \
- {OP_LDB, 0, 0, ETH_HLEN }, /* */ \
- {OP_AND, 0, 0, PTP_GEN_BIT }, /* */ \
- {OP_JEQ, 0, 4, 0 }, /* f goto L6x */ \
- {OP_LDH, 0, 0, ETH_HLEN }, /* */ \
- {OP_AND, 0, 0, PTP_CLASS_VMASK }, /* */ \
- {OP_OR, 0, 0, PTP_CLASS_L2 }, /* */ \
- {OP_RETA, 0, 0, 0 }, /* */ \
-/*L6x*/ {OP_RETK, 0, 0, PTP_CLASS_NONE },
-
#endif
+#endif /* _PTP_CLASSIFY_H_ */
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
index 38a993508327..0d8ff3fb84ba 100644
--- a/include/linux/ptp_clock_kernel.h
+++ b/include/linux/ptp_clock_kernel.h
@@ -49,7 +49,11 @@ struct ptp_clock_request {
* @n_alarm: The number of programmable alarms.
* @n_ext_ts: The number of external time stamp channels.
* @n_per_out: The number of programmable periodic signals.
+ * @n_pins: The number of programmable pins.
* @pps: Indicates whether the clock supports a PPS callback.
+ * @pin_config: Array of length 'n_pins'. If the number of
+ * programmable pins is nonzero, then drivers must
+ * allocate and initialize this array.
*
* clock operations
*
@@ -70,6 +74,18 @@ struct ptp_clock_request {
* parameter request: Desired resource to enable or disable.
* parameter on: Caller passes one to enable or zero to disable.
*
+ * @verify: Confirm that a pin can perform a given function. The PTP
+ * Hardware Clock subsystem maintains the 'pin_config'
+ * array on behalf of the drivers, but the PHC subsystem
+ * assumes that every pin can perform every function. This
+ * hook gives drivers a way of telling the core about
+ * limitations on specific pins. This function must return
+ * zero if the function can be assigned to this pin, and
+ * nonzero otherwise.
+ * parameter pin: index of the pin in question.
+ * parameter func: the desired function to use.
+ * parameter chan: the function channel index to use.
+ *
* Drivers should embed their ptp_clock_info within a private
* structure, obtaining a reference to it using container_of().
*
@@ -83,13 +99,17 @@ struct ptp_clock_info {
int n_alarm;
int n_ext_ts;
int n_per_out;
+ int n_pins;
int pps;
+ struct ptp_pin_desc *pin_config;
int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta);
int (*adjtime)(struct ptp_clock_info *ptp, s64 delta);
int (*gettime)(struct ptp_clock_info *ptp, struct timespec *ts);
int (*settime)(struct ptp_clock_info *ptp, const struct timespec *ts);
int (*enable)(struct ptp_clock_info *ptp,
struct ptp_clock_request *request, int on);
+ int (*verify)(struct ptp_clock_info *ptp, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan);
};
struct ptp_clock;
@@ -156,4 +176,17 @@ extern void ptp_clock_event(struct ptp_clock *ptp,
extern int ptp_clock_index(struct ptp_clock *ptp);
+/**
+ * ptp_find_pin() - obtain the pin index of a given auxiliary function
+ *
+ * @ptp: The clock obtained from ptp_clock_register().
+ * @func: One of the ptp_pin_function enumerated values.
+ * @chan: The particular functional channel to find.
+ * Return: Pin index in the range of zero to ptp_clock_caps.n_pins - 1,
+ * or -1 if the auxiliary function cannot be found.
+ */
+
+int ptp_find_pin(struct ptp_clock *ptp,
+ enum ptp_pin_function func, unsigned int chan);
+
#endif
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index f0feafd184a0..4717f54051cb 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -7,7 +7,7 @@
struct pwm_device;
struct seq_file;
-#if IS_ENABLED(CONFIG_PWM) || IS_ENABLED(CONFIG_HAVE_PWM)
+#if IS_ENABLED(CONFIG_PWM)
/*
* pwm_request - request a PWM device
*/
diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h
index 49444203328a..f2b405116166 100644
--- a/include/linux/pxa2xx_ssp.h
+++ b/include/linux/pxa2xx_ssp.h
@@ -219,7 +219,7 @@ static inline u32 pxa_ssp_read_reg(struct ssp_device *dev, u32 reg)
return __raw_readl(dev->mmio_base + reg);
}
-#ifdef CONFIG_ARCH_PXA
+#if IS_ENABLED(CONFIG_PXA_SSP)
struct ssp_device *pxa_ssp_request(int port, const char *label);
void pxa_ssp_free(struct ssp_device *);
struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node,
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index 6965fe394c3b..1d3eee594cd6 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -46,6 +46,14 @@ void inode_reclaim_rsv_space(struct inode *inode, qsize_t number);
void dquot_initialize(struct inode *inode);
void dquot_drop(struct inode *inode);
struct dquot *dqget(struct super_block *sb, struct kqid qid);
+static inline struct dquot *dqgrab(struct dquot *dquot)
+{
+ /* Make sure someone else has active reference to dquot */
+ WARN_ON_ONCE(!atomic_read(&dquot->dq_count));
+ WARN_ON_ONCE(!test_bit(DQ_ACTIVE_B, &dquot->dq_flags));
+ atomic_inc(&dquot->dq_count);
+ return dquot;
+}
void dqput(struct dquot *dquot);
int dquot_scan_active(struct super_block *sb,
int (*fn)(struct dquot *dquot, unsigned long priv),
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h
index 403940787be1..33170dbd9db4 100644
--- a/include/linux/radix-tree.h
+++ b/include/linux/radix-tree.h
@@ -60,6 +60,49 @@ static inline int radix_tree_is_indirect_ptr(void *ptr)
#define RADIX_TREE_MAX_TAGS 3
+#ifdef __KERNEL__
+#define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6)
+#else
+#define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */
+#endif
+
+#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT)
+#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1)
+
+#define RADIX_TREE_TAG_LONGS \
+ ((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG)
+
+#define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long))
+#define RADIX_TREE_MAX_PATH (DIV_ROUND_UP(RADIX_TREE_INDEX_BITS, \
+ RADIX_TREE_MAP_SHIFT))
+
+/* Height component in node->path */
+#define RADIX_TREE_HEIGHT_SHIFT (RADIX_TREE_MAX_PATH + 1)
+#define RADIX_TREE_HEIGHT_MASK ((1UL << RADIX_TREE_HEIGHT_SHIFT) - 1)
+
+/* Internally used bits of node->count */
+#define RADIX_TREE_COUNT_SHIFT (RADIX_TREE_MAP_SHIFT + 1)
+#define RADIX_TREE_COUNT_MASK ((1UL << RADIX_TREE_COUNT_SHIFT) - 1)
+
+struct radix_tree_node {
+ unsigned int path; /* Offset in parent & height from the bottom */
+ unsigned int count;
+ union {
+ struct {
+ /* Used when ascending tree */
+ struct radix_tree_node *parent;
+ /* For tree user */
+ void *private_data;
+ };
+ /* Used when freeing node */
+ struct rcu_head rcu_head;
+ };
+ /* For tree user */
+ struct list_head private_list;
+ void __rcu *slots[RADIX_TREE_MAP_SIZE];
+ unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
+};
+
/* root tags are stored in gfp_mask, shifted by __GFP_BITS_SHIFT */
struct radix_tree_root {
unsigned int height;
@@ -101,6 +144,7 @@ do { \
* concurrently with other readers.
*
* The notable exceptions to this rule are the following functions:
+ * __radix_tree_lookup
* radix_tree_lookup
* radix_tree_lookup_slot
* radix_tree_tag_get
@@ -216,9 +260,16 @@ static inline void radix_tree_replace_slot(void **pslot, void *item)
rcu_assign_pointer(*pslot, item);
}
+int __radix_tree_create(struct radix_tree_root *root, unsigned long index,
+ struct radix_tree_node **nodep, void ***slotp);
int radix_tree_insert(struct radix_tree_root *, unsigned long, void *);
+void *__radix_tree_lookup(struct radix_tree_root *root, unsigned long index,
+ struct radix_tree_node **nodep, void ***slotp);
void *radix_tree_lookup(struct radix_tree_root *, unsigned long);
void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long);
+bool __radix_tree_delete_node(struct radix_tree_root *root,
+ struct radix_tree_node *node);
+void *radix_tree_delete_item(struct radix_tree_root *, unsigned long, void *);
void *radix_tree_delete(struct radix_tree_root *, unsigned long);
unsigned int
radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
@@ -226,10 +277,6 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
unsigned int radix_tree_gang_lookup_slot(struct radix_tree_root *root,
void ***results, unsigned long *indices,
unsigned long first_index, unsigned int max_items);
-unsigned long radix_tree_next_hole(struct radix_tree_root *root,
- unsigned long index, unsigned long max_scan);
-unsigned long radix_tree_prev_hole(struct radix_tree_root *root,
- unsigned long index, unsigned long max_scan);
int radix_tree_preload(gfp_t gfp_mask);
int radix_tree_maybe_preload(gfp_t gfp_mask);
void radix_tree_init(void);
diff --git a/include/linux/random.h b/include/linux/random.h
index 1cfce0e24dbd..57fbbffd77a0 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -88,6 +88,22 @@ static inline int arch_get_random_int(unsigned int *v)
{
return 0;
}
+static inline int arch_has_random(void)
+{
+ return 0;
+}
+static inline int arch_get_random_seed_long(unsigned long *v)
+{
+ return 0;
+}
+static inline int arch_get_random_seed_int(unsigned int *v)
+{
+ return 0;
+}
+static inline int arch_has_random_seed(void)
+{
+ return 0;
+}
#endif
/* Pseudo random number generator from numerical recipes. */
diff --git a/include/linux/reset.h b/include/linux/reset.h
index 6082247feab1..c0eda5023d74 100644
--- a/include/linux/reset.h
+++ b/include/linux/reset.h
@@ -4,6 +4,8 @@
struct device;
struct reset_control;
+#ifdef CONFIG_RESET_CONTROLLER
+
int reset_control_reset(struct reset_control *rstc);
int reset_control_assert(struct reset_control *rstc);
int reset_control_deassert(struct reset_control *rstc);
@@ -12,6 +14,67 @@ struct reset_control *reset_control_get(struct device *dev, const char *id);
void reset_control_put(struct reset_control *rstc);
struct reset_control *devm_reset_control_get(struct device *dev, const char *id);
-int device_reset(struct device *dev);
+int __must_check device_reset(struct device *dev);
+
+static inline int device_reset_optional(struct device *dev)
+{
+ return device_reset(dev);
+}
+
+static inline struct reset_control *reset_control_get_optional(
+ struct device *dev, const char *id)
+{
+ return reset_control_get(dev, id);
+}
+
+static inline struct reset_control *devm_reset_control_get_optional(
+ struct device *dev, const char *id)
+{
+ return devm_reset_control_get(dev, id);
+}
+
+#else
+
+static inline int reset_control_reset(struct reset_control *rstc)
+{
+ WARN_ON(1);
+ return 0;
+}
+
+static inline int reset_control_assert(struct reset_control *rstc)
+{
+ WARN_ON(1);
+ return 0;
+}
+
+static inline int reset_control_deassert(struct reset_control *rstc)
+{
+ WARN_ON(1);
+ return 0;
+}
+
+static inline void reset_control_put(struct reset_control *rstc)
+{
+ WARN_ON(1);
+}
+
+static inline int device_reset_optional(struct device *dev)
+{
+ return -ENOSYS;
+}
+
+static inline struct reset_control *reset_control_get_optional(
+ struct device *dev, const char *id)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static inline struct reset_control *devm_reset_control_get_optional(
+ struct device *dev, const char *id)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+#endif /* CONFIG_RESET_CONTROLLER */
#endif
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
index 6f19cfd1840e..4054b0994071 100644
--- a/include/linux/seccomp.h
+++ b/include/linux/seccomp.h
@@ -76,7 +76,6 @@ static inline int seccomp_mode(struct seccomp *s)
#ifdef CONFIG_SECCOMP_FILTER
extern void put_seccomp_filter(struct task_struct *tsk);
extern void get_seccomp_filter(struct task_struct *tsk);
-extern u32 seccomp_bpf_load(int off);
#else /* CONFIG_SECCOMP_FILTER */
static inline void put_seccomp_filter(struct task_struct *tsk)
{
diff --git a/include/linux/security.h b/include/linux/security.h
index 2fc42d191f79..6478ce3252c7 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1793,7 +1793,8 @@ int security_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
int security_inode_rmdir(struct inode *dir, struct dentry *dentry);
int security_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev);
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry);
+ struct inode *new_dir, struct dentry *new_dentry,
+ unsigned int flags);
int security_inode_readlink(struct dentry *dentry);
int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
int security_inode_permission(struct inode *inode, int mask);
@@ -2161,7 +2162,8 @@ static inline int security_inode_mknod(struct inode *dir,
static inline int security_inode_rename(struct inode *old_dir,
struct dentry *old_dentry,
struct inode *new_dir,
- struct dentry *new_dentry)
+ struct dentry *new_dentry,
+ unsigned int flags)
{
return 0;
}
@@ -2955,7 +2957,8 @@ int security_path_symlink(struct path *dir, struct dentry *dentry,
int security_path_link(struct dentry *old_dentry, struct path *new_dir,
struct dentry *new_dentry);
int security_path_rename(struct path *old_dir, struct dentry *old_dentry,
- struct path *new_dir, struct dentry *new_dentry);
+ struct path *new_dir, struct dentry *new_dentry,
+ unsigned int flags);
int security_path_chmod(struct path *path, umode_t mode);
int security_path_chown(struct path *path, kuid_t uid, kgid_t gid);
int security_path_chroot(struct path *path);
@@ -3003,7 +3006,8 @@ static inline int security_path_link(struct dentry *old_dentry,
static inline int security_path_rename(struct path *old_dir,
struct dentry *old_dentry,
struct path *new_dir,
- struct dentry *new_dentry)
+ struct dentry *new_dentry,
+ unsigned int flags)
{
return 0;
}
diff --git a/include/linux/serial_s3c.h b/include/linux/serial_s3c.h
index 907d9d1d56cf..e6fc9567690b 100644
--- a/include/linux/serial_s3c.h
+++ b/include/linux/serial_s3c.h
@@ -233,6 +233,8 @@
#ifndef __ASSEMBLY__
+#include <linux/serial_core.h>
+
/* configuration structure for per-machine configurations for the
* serial port
*
diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h
index 60c72395ec6b..1f208b2a1ed6 100644
--- a/include/linux/sh_clk.h
+++ b/include/linux/sh_clk.h
@@ -52,6 +52,7 @@ struct clk {
unsigned long flags;
void __iomem *enable_reg;
+ void __iomem *status_reg;
unsigned int enable_bit;
void __iomem *mapped_reg;
@@ -116,22 +117,26 @@ long clk_round_parent(struct clk *clk, unsigned long target,
unsigned long *best_freq, unsigned long *parent_freq,
unsigned int div_min, unsigned int div_max);
-#define SH_CLK_MSTP(_parent, _enable_reg, _enable_bit, _flags) \
+#define SH_CLK_MSTP(_parent, _enable_reg, _enable_bit, _status_reg, _flags) \
{ \
.parent = _parent, \
.enable_reg = (void __iomem *)_enable_reg, \
.enable_bit = _enable_bit, \
+ .status_reg = _status_reg, \
.flags = _flags, \
}
-#define SH_CLK_MSTP32(_p, _r, _b, _f) \
- SH_CLK_MSTP(_p, _r, _b, _f | CLK_ENABLE_REG_32BIT)
+#define SH_CLK_MSTP32(_p, _r, _b, _f) \
+ SH_CLK_MSTP(_p, _r, _b, 0, _f | CLK_ENABLE_REG_32BIT)
-#define SH_CLK_MSTP16(_p, _r, _b, _f) \
- SH_CLK_MSTP(_p, _r, _b, _f | CLK_ENABLE_REG_16BIT)
+#define SH_CLK_MSTP32_STS(_p, _r, _b, _s, _f) \
+ SH_CLK_MSTP(_p, _r, _b, _s, _f | CLK_ENABLE_REG_32BIT)
-#define SH_CLK_MSTP8(_p, _r, _b, _f) \
- SH_CLK_MSTP(_p, _r, _b, _f | CLK_ENABLE_REG_8BIT)
+#define SH_CLK_MSTP16(_p, _r, _b, _f) \
+ SH_CLK_MSTP(_p, _r, _b, 0, _f | CLK_ENABLE_REG_16BIT)
+
+#define SH_CLK_MSTP8(_p, _r, _b, _f) \
+ SH_CLK_MSTP(_p, _r, _b, 0, _f | CLK_ENABLE_REG_8BIT)
int sh_clk_mstp_register(struct clk *clks, int nr);
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index 9d55438bc4ad..4d1771c2d29f 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -51,6 +51,7 @@ extern struct file *shmem_kernel_file_setup(const char *name, loff_t size,
unsigned long flags);
extern int shmem_zero_setup(struct vm_area_struct *);
extern int shmem_lock(struct file *file, int lock, struct user_struct *user);
+extern bool shmem_mapping(struct address_space *mapping);
extern void shmem_unlock_mapping(struct address_space *mapping);
extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
pgoff_t index, gfp_t gfp_mask);
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 15ede6a823a6..08074a810164 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -32,6 +32,7 @@
#include <linux/hrtimer.h>
#include <linux/dma-mapping.h>
#include <linux/netdev_features.h>
+#include <linux/sched.h>
#include <net/flow_keys.h>
/* A. Checksumming of received packets by device.
@@ -356,11 +357,62 @@ typedef unsigned int sk_buff_data_t;
typedef unsigned char *sk_buff_data_t;
#endif
+/**
+ * struct skb_mstamp - multi resolution time stamps
+ * @stamp_us: timestamp in us resolution
+ * @stamp_jiffies: timestamp in jiffies
+ */
+struct skb_mstamp {
+ union {
+ u64 v64;
+ struct {
+ u32 stamp_us;
+ u32 stamp_jiffies;
+ };
+ };
+};
+
+/**
+ * skb_mstamp_get - get current timestamp
+ * @cl: place to store timestamps
+ */
+static inline void skb_mstamp_get(struct skb_mstamp *cl)
+{
+ u64 val = local_clock();
+
+ do_div(val, NSEC_PER_USEC);
+ cl->stamp_us = (u32)val;
+ cl->stamp_jiffies = (u32)jiffies;
+}
+
+/**
+ * skb_mstamp_delta - compute the difference in usec between two skb_mstamp
+ * @t1: pointer to newest sample
+ * @t0: pointer to oldest sample
+ */
+static inline u32 skb_mstamp_us_delta(const struct skb_mstamp *t1,
+ const struct skb_mstamp *t0)
+{
+ s32 delta_us = t1->stamp_us - t0->stamp_us;
+ u32 delta_jiffies = t1->stamp_jiffies - t0->stamp_jiffies;
+
+ /* If delta_us is negative, this might be because interval is too big,
+ * or local_clock() drift is too big : fallback using jiffies.
+ */
+ if (delta_us <= 0 ||
+ delta_jiffies >= (INT_MAX / (USEC_PER_SEC / HZ)))
+
+ delta_us = jiffies_to_usecs(delta_jiffies);
+
+ return delta_us;
+}
+
+
/**
* struct sk_buff - socket buffer
* @next: Next buffer in list
* @prev: Previous buffer in list
- * @tstamp: Time we arrived
+ * @tstamp: Time we arrived/left
* @sk: Socket we are owned by
* @dev: Device we arrived on/are leaving by
* @cb: Control buffer. Free for use by every layer. Put private vars here
@@ -392,11 +444,11 @@ typedef unsigned char *sk_buff_data_t;
* @skb_iif: ifindex of device we arrived on
* @tc_index: Traffic control index
* @tc_verd: traffic control verdict
- * @rxhash: the packet hash computed on receive
+ * @hash: the packet hash
* @queue_mapping: Queue mapping for multiqueue devices
* @ndisc_nodetype: router type (from link layer)
* @ooo_okay: allow the mapping of a socket to a queue to be changed
- * @l4_rxhash: indicate rxhash is a canonical 4-tuple hash over transport
+ * @l4_hash: indicate hash is a canonical 4-tuple hash over transport
* ports.
* @wifi_acked_valid: wifi_acked was set
* @wifi_acked: whether frame was acked on wifi or not
@@ -429,7 +481,10 @@ struct sk_buff {
struct sk_buff *next;
struct sk_buff *prev;
- ktime_t tstamp;
+ union {
+ ktime_t tstamp;
+ struct skb_mstamp skb_mstamp;
+ };
struct sock *sk;
struct net_device *dev;
@@ -482,7 +537,7 @@ struct sk_buff {
int skb_iif;
- __u32 rxhash;
+ __u32 hash;
__be16 vlan_proto;
__u16 vlan_tci;
@@ -501,7 +556,7 @@ struct sk_buff {
#endif
__u8 pfmemalloc:1;
__u8 ooo_okay:1;
- __u8 l4_rxhash:1;
+ __u8 l4_hash:1;
__u8 wifi_acked_valid:1;
__u8 wifi_acked:1;
__u8 no_fcs:1;
@@ -691,6 +746,8 @@ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb,
unsigned int headroom);
struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom,
int newtailroom, gfp_t priority);
+int skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg,
+ int offset, int len);
int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset,
int len);
int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer);
@@ -758,40 +815,40 @@ enum pkt_hash_types {
static inline void
skb_set_hash(struct sk_buff *skb, __u32 hash, enum pkt_hash_types type)
{
- skb->l4_rxhash = (type == PKT_HASH_TYPE_L4);
- skb->rxhash = hash;
+ skb->l4_hash = (type == PKT_HASH_TYPE_L4);
+ skb->hash = hash;
}
void __skb_get_hash(struct sk_buff *skb);
static inline __u32 skb_get_hash(struct sk_buff *skb)
{
- if (!skb->l4_rxhash)
+ if (!skb->l4_hash)
__skb_get_hash(skb);
- return skb->rxhash;
+ return skb->hash;
}
static inline __u32 skb_get_hash_raw(const struct sk_buff *skb)
{
- return skb->rxhash;
+ return skb->hash;
}
static inline void skb_clear_hash(struct sk_buff *skb)
{
- skb->rxhash = 0;
- skb->l4_rxhash = 0;
+ skb->hash = 0;
+ skb->l4_hash = 0;
}
static inline void skb_clear_hash_if_not_l4(struct sk_buff *skb)
{
- if (!skb->l4_rxhash)
+ if (!skb->l4_hash)
skb_clear_hash(skb);
}
static inline void skb_copy_hash(struct sk_buff *to, const struct sk_buff *from)
{
- to->rxhash = from->rxhash;
- to->l4_rxhash = from->l4_rxhash;
+ to->hash = from->hash;
+ to->l4_hash = from->l4_hash;
};
#ifdef NET_SKBUFF_DATA_USES_OFFSET
@@ -2038,7 +2095,7 @@ static inline void skb_propagate_pfmemalloc(struct page *page,
}
/**
- * skb_frag_page - retrieve the page refered to by a paged fragment
+ * skb_frag_page - retrieve the page referred to by a paged fragment
* @frag: the paged fragment
*
* Returns the &struct page associated with @frag.
@@ -2573,8 +2630,6 @@ static inline ktime_t net_invalid_timestamp(void)
return ktime_set(0, 0);
}
-void skb_timestamping_init(void);
-
#ifdef CONFIG_NETWORK_PHY_TIMESTAMPING
void skb_clone_tx_timestamp(struct sk_buff *skb);
@@ -2776,6 +2831,19 @@ static inline void skb_init_secmark(struct sk_buff *skb)
{ }
#endif
+static inline bool skb_irq_freeable(const struct sk_buff *skb)
+{
+ return !skb->destructor &&
+#if IS_ENABLED(CONFIG_XFRM)
+ !skb->sp &&
+#endif
+#if IS_ENABLED(CONFIG_NF_CONNTRACK)
+ !skb->nfct &&
+#endif
+ !skb->_skb_refdst &&
+ !skb_has_frag_list(skb);
+}
+
static inline void skb_set_queue_mapping(struct sk_buff *skb, u16 queue_mapping)
{
skb->queue_mapping = queue_mapping;
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 36c86ef51ff3..e713543336f1 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -237,7 +237,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @mode_bits: flags understood by this controller driver
* @bits_per_word_mask: A mask indicating which values of bits_per_word are
* supported by the driver. Bit n indicates that a bits_per_word n+1 is
- * suported. If set, the SPI core will reject any transfer with an
+ * supported. If set, the SPI core will reject any transfer with an
* unsupported bits_per_word. If not set, this value is simply ignored,
* and it's up to the individual driver to perform any validation.
* @min_speed_hz: Lowest supported transfer speed
@@ -262,7 +262,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @cur_msg: the currently in-flight message
* @cur_msg_prepared: spi_prepare_message was called for the currently
* in-flight message
- * @xfer_completion: used by core tranfer_one_message()
+ * @xfer_completion: used by core transfer_one_message()
* @busy: message pump is busy
* @running: message pump is running
* @rt: whether this queue is set to run as a realtime task
@@ -525,7 +525,7 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum);
* @rx_buf: data to be read (dma-safe memory), or NULL
* @tx_dma: DMA address of tx_buf, if @spi_message.is_dma_mapped
* @rx_dma: DMA address of rx_buf, if @spi_message.is_dma_mapped
- * @tx_nbits: number of bits used for writting. If 0 the default
+ * @tx_nbits: number of bits used for writing. If 0 the default
* (SPI_NBITS_SINGLE) is used.
* @rx_nbits: number of bits used for reading. If 0 the default
* (SPI_NBITS_SINGLE) is used.
@@ -585,7 +585,7 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum);
* by the results of previous messages and where the whole transaction
* ends when the chipselect goes intactive.
*
- * When SPI can transfer in 1x,2x or 4x. It can get this tranfer information
+ * When SPI can transfer in 1x,2x or 4x. It can get this transfer information
* from device through @tx_nbits and @rx_nbits. In Bi-direction, these
* two should both be set. User can set transfer mode with SPI_NBITS_SINGLE(1x)
* SPI_NBITS_DUAL(2x) and SPI_NBITS_QUAD(4x) to support these three transfer.
diff --git a/include/linux/ssbi.h b/include/linux/ssbi.h
index bcbb642a7641..087b08a4d333 100644
--- a/include/linux/ssbi.h
+++ b/include/linux/ssbi.h
@@ -20,4 +20,24 @@
int ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len);
int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len);
+static inline int
+ssbi_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ int ret;
+ u8 v;
+
+ ret = ssbi_read(context, reg, &v, 1);
+ if (!ret)
+ *val = v;
+
+ return ret;
+}
+
+static inline int
+ssbi_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ u8 v = val;
+ return ssbi_write(context, reg, &v, 1);
+}
+
#endif
diff --git a/include/linux/sunrpc/bc_xprt.h b/include/linux/sunrpc/bc_xprt.h
index 969c0a671dbf..2ca67b55e0fe 100644
--- a/include/linux/sunrpc/bc_xprt.h
+++ b/include/linux/sunrpc/bc_xprt.h
@@ -32,7 +32,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <linux/sunrpc/sched.h>
#ifdef CONFIG_SUNRPC_BACKCHANNEL
-struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt);
+struct rpc_rqst *xprt_lookup_bc_request(struct rpc_xprt *xprt, __be32 xid);
+void xprt_complete_bc_request(struct rpc_rqst *req, uint32_t copied);
void xprt_free_bc_request(struct rpc_rqst *req);
int xprt_setup_backchannel(struct rpc_xprt *, unsigned int min_reqs);
void xprt_destroy_backchannel(struct rpc_xprt *, unsigned int max_reqs);
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 46ba0c6c219f..350711560753 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -260,6 +260,42 @@ struct swap_list_t {
int next; /* swapfile to be used next */
};
+/* linux/mm/workingset.c */
+void *workingset_eviction(struct address_space *mapping, struct page *page);
+bool workingset_refault(void *shadow);
+void workingset_activation(struct page *page);
+extern struct list_lru workingset_shadow_nodes;
+
+static inline unsigned int workingset_node_pages(struct radix_tree_node *node)
+{
+ return node->count & RADIX_TREE_COUNT_MASK;
+}
+
+static inline void workingset_node_pages_inc(struct radix_tree_node *node)
+{
+ node->count++;
+}
+
+static inline void workingset_node_pages_dec(struct radix_tree_node *node)
+{
+ node->count--;
+}
+
+static inline unsigned int workingset_node_shadows(struct radix_tree_node *node)
+{
+ return node->count >> RADIX_TREE_COUNT_SHIFT;
+}
+
+static inline void workingset_node_shadows_inc(struct radix_tree_node *node)
+{
+ node->count += 1U << RADIX_TREE_COUNT_SHIFT;
+}
+
+static inline void workingset_node_shadows_dec(struct radix_tree_node *node)
+{
+ node->count -= 1U << RADIX_TREE_COUNT_SHIFT;
+}
+
/* linux/mm/page_alloc.c */
extern unsigned long totalram_pages;
extern unsigned long totalreserve_pages;
diff --git a/include/linux/sxgbe_platform.h b/include/linux/sxgbe_platform.h
new file mode 100644
index 000000000000..a62442cf0037
--- /dev/null
+++ b/include/linux/sxgbe_platform.h
@@ -0,0 +1,54 @@
+/*
+ * 10G controller driver for Samsung EXYNOS SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __SXGBE_PLATFORM_H__
+#define __SXGBE_PLATFORM_H__
+
+/* MDC Clock Selection define*/
+#define SXGBE_CSR_100_150M 0x0 /* MDC = clk_scr_i/62 */
+#define SXGBE_CSR_150_250M 0x1 /* MDC = clk_scr_i/102 */
+#define SXGBE_CSR_250_300M 0x2 /* MDC = clk_scr_i/122 */
+#define SXGBE_CSR_300_350M 0x3 /* MDC = clk_scr_i/142 */
+#define SXGBE_CSR_350_400M 0x4 /* MDC = clk_scr_i/162 */
+#define SXGBE_CSR_400_500M 0x5 /* MDC = clk_scr_i/202 */
+
+/* Platfrom data for platform device structure's
+ * platform_data field
+ */
+struct sxgbe_mdio_bus_data {
+ unsigned int phy_mask;
+ int *irqs;
+ int probed_phy_irq;
+};
+
+struct sxgbe_dma_cfg {
+ int pbl;
+ int fixed_burst;
+ int burst_map;
+ int adv_addr_mode;
+};
+
+struct sxgbe_plat_data {
+ char *phy_bus_name;
+ int bus_id;
+ int phy_addr;
+ int interface;
+ struct sxgbe_mdio_bus_data *mdio_bus_data;
+ struct sxgbe_dma_cfg *dma_cfg;
+ int clk_csr;
+ int pmt;
+ int force_sf_dma_mode;
+ int force_thresh_dma_mode;
+ int riwt_off;
+};
+
+#endif /* __SXGBE_PLATFORM_H__ */
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 1e67b7a5968c..2aa8b749f13d 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -200,6 +200,8 @@ extern struct trace_event_functions exit_syscall_print_funcs;
} \
static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))
+asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special,
+ qid_t id, void __user *addr);
asmlinkage long sys_time(time_t __user *tloc);
asmlinkage long sys_stime(time_t __user *tptr);
asmlinkage long sys_gettimeofday(struct timeval __user *tv,
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index e0bf210ddffd..084354b0e814 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -71,7 +71,8 @@ struct attribute_group {
*/
#define __ATTR(_name, _mode, _show, _store) { \
- .attr = {.name = __stringify(_name), .mode = _mode }, \
+ .attr = {.name = __stringify(_name), \
+ .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 4ad0706d40eb..239946868142 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -201,10 +201,10 @@ struct tcp_sock {
u32 tlp_high_seq; /* snd_nxt at the time of TLP retransmit. */
/* RTT measurement */
- u32 srtt; /* smoothed round trip time << 3 */
- u32 mdev; /* medium deviation */
- u32 mdev_max; /* maximal mdev for the last rtt period */
- u32 rttvar; /* smoothed mdev_max */
+ u32 srtt_us; /* smoothed round trip time << 3 in usecs */
+ u32 mdev_us; /* medium deviation */
+ u32 mdev_max_us; /* maximal mdev for the last rtt period */
+ u32 rttvar_us; /* smoothed mdev_max */
u32 rtt_seq; /* sequence number to update rttvar */
u32 packets_out; /* Packets which are "in flight" */
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index 7159a0a933df..812b2553dfd8 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -48,12 +48,6 @@ extern int tracepoint_probe_register(const char *name, void *probe, void *data);
extern int
tracepoint_probe_unregister(const char *name, void *probe, void *data);
-extern int tracepoint_probe_register_noupdate(const char *name, void *probe,
- void *data);
-extern int tracepoint_probe_unregister_noupdate(const char *name, void *probe,
- void *data);
-extern void tracepoint_probe_update_all(void);
-
#ifdef CONFIG_MODULES
struct tp_module {
struct list_head list;
@@ -68,18 +62,6 @@ static inline bool trace_module_has_bad_taint(struct module *mod)
}
#endif /* CONFIG_MODULES */
-struct tracepoint_iter {
-#ifdef CONFIG_MODULES
- struct tp_module *module;
-#endif /* CONFIG_MODULES */
- struct tracepoint * const *tracepoint;
-};
-
-extern void tracepoint_iter_start(struct tracepoint_iter *iter);
-extern void tracepoint_iter_next(struct tracepoint_iter *iter);
-extern void tracepoint_iter_stop(struct tracepoint_iter *iter);
-extern void tracepoint_iter_reset(struct tracepoint_iter *iter);
-
/*
* tracepoint_synchronize_unregister must be called between the last tracepoint
* probe unregistration and the end of module exit to make sure there is no
diff --git a/include/linux/tty.h b/include/linux/tty.h
index b90b5c221ff0..1c3316a47d7e 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -518,9 +518,9 @@ extern void tty_port_put(struct tty_port *port);
static inline struct tty_port *tty_port_get(struct tty_port *port)
{
- if (port)
- kref_get(&port->kref);
- return port;
+ if (port && kref_get_unless_zero(&port->kref))
+ return port;
+ return NULL;
}
/* If the cts flow control is enabled, return true. */
diff --git a/include/linux/u64_stats_sync.h b/include/linux/u64_stats_sync.h
index 7bfabd20204c..4b4439e75f45 100644
--- a/include/linux/u64_stats_sync.h
+++ b/include/linux/u64_stats_sync.h
@@ -27,8 +27,8 @@
* (On UP, there is no seqcount_t protection, a reader allowing interrupts could
* read partial values)
*
- * 7) For softirq uses, readers can use u64_stats_fetch_begin_bh() and
- * u64_stats_fetch_retry_bh() helpers
+ * 7) For irq and softirq uses, readers can use u64_stats_fetch_begin_irq() and
+ * u64_stats_fetch_retry_irq() helpers
*
* Usage :
*
@@ -114,31 +114,31 @@ static inline bool u64_stats_fetch_retry(const struct u64_stats_sync *syncp,
}
/*
- * In case softirq handlers can update u64 counters, readers can use following helpers
+ * In case irq handlers can update u64 counters, readers can use following helpers
* - SMP 32bit arches use seqcount protection, irq safe.
- * - UP 32bit must disable BH.
+ * - UP 32bit must disable irqs.
* - 64bit have no problem atomically reading u64 values, irq safe.
*/
-static inline unsigned int u64_stats_fetch_begin_bh(const struct u64_stats_sync *syncp)
+static inline unsigned int u64_stats_fetch_begin_irq(const struct u64_stats_sync *syncp)
{
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
return read_seqcount_begin(&syncp->seq);
#else
#if BITS_PER_LONG==32
- local_bh_disable();
+ local_irq_disable();
#endif
return 0;
#endif
}
-static inline bool u64_stats_fetch_retry_bh(const struct u64_stats_sync *syncp,
+static inline bool u64_stats_fetch_retry_irq(const struct u64_stats_sync *syncp,
unsigned int start)
{
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
return read_seqcount_retry(&syncp->seq, start);
#else
#if BITS_PER_LONG==32
- local_bh_enable();
+ local_irq_enable();
#endif
return false;
#endif
diff --git a/include/linux/uinput.h b/include/linux/uinput.h
index 0a4487d3fb1e..0994c0d01a09 100644
--- a/include/linux/uinput.h
+++ b/include/linux/uinput.h
@@ -20,6 +20,8 @@
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
+ * 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>)
* - update ff support for the changes in kernel interface
* - add UINPUT_VERSION
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index e32251e00e62..edff2b97b864 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -126,6 +126,7 @@ extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs);
+extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs);
#else /* !CONFIG_UPROBES */
struct uprobes_state {
};
diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h
index 2c14d9cdd57a..44b38b92236a 100644
--- a/include/linux/usb/cdc_ncm.h
+++ b/include/linux/usb/cdc_ncm.h
@@ -94,6 +94,7 @@ struct cdc_ncm_ctx {
const struct usb_cdc_ncm_desc *func_desc;
const struct usb_cdc_mbim_desc *mbim_desc;
+ const struct usb_cdc_mbim_extended_desc *mbim_extended_desc;
const struct usb_cdc_ether_desc *ether_desc;
struct usb_interface *control;
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index dba63f53906c..d3ca3b53837c 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -92,7 +92,7 @@ struct usb_configuration;
* @suspend: Notifies functions when the host stops sending USB traffic.
* @resume: Notifies functions when the host restarts USB traffic.
* @get_status: Returns function status as a reply to
- * GetStatus() request when the recepient is Interface.
+ * GetStatus() request when the recipient is Interface.
* @func_suspend: callback to be called when
* SetFeature(FUNCTION_SUSPEND) is reseived
*
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index 24579a0312a0..81022a52bc34 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -96,5 +96,7 @@ extern void vfio_unregister_iommu_driver(
extern struct vfio_group *vfio_group_get_external_user(struct file *filep);
extern void vfio_group_put_external_user(struct vfio_group *group);
extern int vfio_external_user_iommu_id(struct vfio_group *group);
+extern long vfio_external_check_extension(struct vfio_group *group,
+ unsigned long arg);
#endif /* VFIO_H */
diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h
index 3a712e2e7d76..486c3972c0be 100644
--- a/include/linux/vm_event_item.h
+++ b/include/linux/vm_event_item.h
@@ -37,6 +37,7 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,
PGINODESTEAL, SLABS_SCANNED, KSWAPD_INODESTEAL,
KSWAPD_LOW_WMARK_HIT_QUICKLY, KSWAPD_HIGH_WMARK_HIT_QUICKLY,
PAGEOUTRUN, ALLOCSTALL, PGROTATED,
+ DROP_PAGECACHE, DROP_SLAB,
#ifdef CONFIG_NUMA_BALANCING
NUMA_PTE_UPDATES,
NUMA_HUGE_PTE_UPDATES,
diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h
index 67ce70c8279b..ea4476157e00 100644
--- a/include/linux/vmstat.h
+++ b/include/linux/vmstat.h
@@ -187,8 +187,6 @@ extern void zone_statistics(struct zone *, struct zone *, gfp_t gfp);
#define add_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, __d)
#define sub_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, -(__d))
-extern void inc_zone_state(struct zone *, enum zone_stat_item);
-
#ifdef CONFIG_SMP
void __mod_zone_page_state(struct zone *, enum zone_stat_item item, int);
void __inc_zone_page_state(struct page *, enum zone_stat_item);
@@ -230,18 +228,18 @@ static inline void __inc_zone_state(struct zone *zone, enum zone_stat_item item)
atomic_long_inc(&vm_stat[item]);
}
-static inline void __inc_zone_page_state(struct page *page,
- enum zone_stat_item item)
-{
- __inc_zone_state(page_zone(page), item);
-}
-
static inline void __dec_zone_state(struct zone *zone, enum zone_stat_item item)
{
atomic_long_dec(&zone->vm_stat[item]);
atomic_long_dec(&vm_stat[item]);
}
+static inline void __inc_zone_page_state(struct page *page,
+ enum zone_stat_item item)
+{
+ __inc_zone_state(page_zone(page), item);
+}
+
static inline void __dec_zone_page_state(struct page *page,
enum zone_stat_item item)
{
@@ -256,6 +254,9 @@ static inline void __dec_zone_page_state(struct page *page,
#define dec_zone_page_state __dec_zone_page_state
#define mod_zone_page_state __mod_zone_page_state
+#define inc_zone_state __inc_zone_state
+#define dec_zone_state __dec_zone_state
+
#define set_pgdat_percpu_threshold(pgdat, callback) { }
static inline void refresh_cpu_vm_stats(int cpu) { }
diff --git a/include/linux/wait.h b/include/linux/wait.h
index 559044c79232..e7d9d9ed14f5 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -803,17 +803,6 @@ do { \
__ret; \
})
-
-/*
- * These are the old interfaces to sleep waiting for an event.
- * They are racy. DO NOT use them, use the wait_event* interfaces above.
- * We plan to remove these interfaces.
- */
-extern void sleep_on(wait_queue_head_t *q);
-extern long sleep_on_timeout(wait_queue_head_t *q, signed long timeout);
-extern void interruptible_sleep_on(wait_queue_head_t *q);
-extern long interruptible_sleep_on_timeout(wait_queue_head_t *q, signed long timeout);
-
/*
* Waitqueues which are removed from the waitqueue_head at wakeup time
*/
diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h
index a54fe82e704b..a9c723be1acf 100644
--- a/include/linux/wl12xx.h
+++ b/include/linux/wl12xx.h
@@ -48,11 +48,15 @@ enum {
WL12XX_TCXOCLOCK_33_6 = 7, /* 33.6 MHz */
};
-struct wl12xx_platform_data {
- void (*set_power)(bool enable);
+struct wl1251_platform_data {
+ int power_gpio;
/* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */
int irq;
bool use_eeprom;
+};
+
+struct wl12xx_platform_data {
+ int irq;
int board_ref_clock;
int board_tcxo_clock;
unsigned long platform_quirks;
@@ -68,6 +72,10 @@ int wl12xx_set_platform_data(const struct wl12xx_platform_data *data);
struct wl12xx_platform_data *wl12xx_get_platform_data(void);
+int wl1251_set_platform_data(const struct wl1251_platform_data *data);
+
+struct wl1251_platform_data *wl1251_get_platform_data(void);
+
#else
static inline
@@ -82,6 +90,18 @@ struct wl12xx_platform_data *wl12xx_get_platform_data(void)
return ERR_PTR(-ENODATA);
}
+static inline
+int wl1251_set_platform_data(const struct wl1251_platform_data *data)
+{
+ return -ENOSYS;
+}
+
+static inline
+struct wl1251_platform_data *wl1251_get_platform_data(void)
+{
+ return ERR_PTR(-ENODATA);
+}
+
#endif
#endif
diff --git a/include/linux/xilinxfb.h b/include/linux/xilinxfb.h
deleted file mode 100644
index 5a155a968054..000000000000
--- a/include/linux/xilinxfb.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Platform device data for Xilinx Framebuffer device
- *
- * Copyright 2007 Secret Lab Technologies Ltd.
- *
- * 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 __XILINXFB_H__
-#define __XILINXFB_H__
-
-#include <linux/types.h>
-
-/* ML300/403 reference design framebuffer driver platform data struct */
-struct xilinxfb_platform_data {
- u32 rotate_screen; /* Flag to rotate display 180 degrees */
- u32 screen_height_mm; /* Physical dimensions of screen in mm */
- u32 screen_width_mm;
- u32 xres, yres; /* resolution of screen in pixels */
- u32 xvirt, yvirt; /* resolution of memory buffer */
-
- /* Physical address of framebuffer memory; If non-zero, driver
- * will use provided memory address instead of allocating one from
- * the consistent pool. */
- u32 fb_phys;
-};
-
-#endif /* __XILINXFB_H__ */
diff --git a/include/media/adv7842.h b/include/media/adv7842.h
index 39322091e8b0..924cbb8d004a 100644
--- a/include/media/adv7842.h
+++ b/include/media/adv7842.h
@@ -220,6 +220,9 @@ struct adv7842_platform_data {
unsigned sdp_free_run_cbar_en:1;
unsigned sdp_free_run_force:1;
+ /* HPA manual (0) or auto (1), affects HDMI register 0x69 */
+ unsigned hpa_auto:1;
+
struct adv7842_sdp_csc_coeff sdp_csc_coeff;
struct adv7842_sdp_io_sync_adjustment sdp_io_sync_625;
diff --git a/include/media/lm3646.h b/include/media/lm3646.h
new file mode 100644
index 000000000000..c6acf5a1d640
--- /dev/null
+++ b/include/media/lm3646.h
@@ -0,0 +1,87 @@
+/*
+ * include/media/lm3646.h
+ *
+ * Copyright (C) 2014 Texas Instruments
+ *
+ * Contact: Daniel Jeong <gshark.jeong@gmail.com>
+ * Ldd-Mlp <ldd-mlp@list.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.
+ */
+
+#ifndef __LM3646_H__
+#define __LM3646_H__
+
+#include <media/v4l2-subdev.h>
+
+#define LM3646_NAME "lm3646"
+#define LM3646_I2C_ADDR_REV1 (0x67)
+#define LM3646_I2C_ADDR_REV0 (0x63)
+
+/* TOTAL FLASH Brightness Max
+ * min 93350uA, step 93750uA, max 1499600uA
+ */
+#define LM3646_TOTAL_FLASH_BRT_MIN 93350
+#define LM3646_TOTAL_FLASH_BRT_STEP 93750
+#define LM3646_TOTAL_FLASH_BRT_MAX 1499600
+#define LM3646_TOTAL_FLASH_BRT_uA_TO_REG(a) \
+ ((a) < LM3646_TOTAL_FLASH_BRT_MIN ? 0 : \
+ ((((a) - LM3646_TOTAL_FLASH_BRT_MIN) / LM3646_TOTAL_FLASH_BRT_STEP)))
+
+/* TOTAL TORCH Brightness Max
+ * min 23040uA, step 23430uA, max 187100uA
+ */
+#define LM3646_TOTAL_TORCH_BRT_MIN 23040
+#define LM3646_TOTAL_TORCH_BRT_STEP 23430
+#define LM3646_TOTAL_TORCH_BRT_MAX 187100
+#define LM3646_TOTAL_TORCH_BRT_uA_TO_REG(a) \
+ ((a) < LM3646_TOTAL_TORCH_BRT_MIN ? 0 : \
+ ((((a) - LM3646_TOTAL_TORCH_BRT_MIN) / LM3646_TOTAL_TORCH_BRT_STEP)))
+
+/* LED1 FLASH Brightness
+ * min 23040uA, step 11718uA, max 1499600uA
+ */
+#define LM3646_LED1_FLASH_BRT_MIN 23040
+#define LM3646_LED1_FLASH_BRT_STEP 11718
+#define LM3646_LED1_FLASH_BRT_MAX 1499600
+#define LM3646_LED1_FLASH_BRT_uA_TO_REG(a) \
+ ((a) <= LM3646_LED1_FLASH_BRT_MIN ? 0 : \
+ ((((a) - LM3646_LED1_FLASH_BRT_MIN) / LM3646_LED1_FLASH_BRT_STEP))+1)
+
+/* LED1 TORCH Brightness
+ * min 2530uA, step 1460uA, max 187100uA
+ */
+#define LM3646_LED1_TORCH_BRT_MIN 2530
+#define LM3646_LED1_TORCH_BRT_STEP 1460
+#define LM3646_LED1_TORCH_BRT_MAX 187100
+#define LM3646_LED1_TORCH_BRT_uA_TO_REG(a) \
+ ((a) <= LM3646_LED1_TORCH_BRT_MIN ? 0 : \
+ ((((a) - LM3646_LED1_TORCH_BRT_MIN) / LM3646_LED1_TORCH_BRT_STEP))+1)
+
+/* FLASH TIMEOUT DURATION
+ * min 50ms, step 50ms, max 400ms
+ */
+#define LM3646_FLASH_TOUT_MIN 50
+#define LM3646_FLASH_TOUT_STEP 50
+#define LM3646_FLASH_TOUT_MAX 400
+#define LM3646_FLASH_TOUT_ms_TO_REG(a) \
+ ((a) <= LM3646_FLASH_TOUT_MIN ? 0 : \
+ (((a) - LM3646_FLASH_TOUT_MIN) / LM3646_FLASH_TOUT_STEP))
+
+/* struct lm3646_platform_data
+ *
+ * @flash_timeout: flash timeout
+ * @led1_flash_brt: led1 flash mode brightness, uA
+ * @led1_torch_brt: led1 torch mode brightness, uA
+ */
+struct lm3646_platform_data {
+
+ u32 flash_timeout;
+
+ u32 led1_flash_brt;
+ u32 led1_torch_brt;
+};
+
+#endif /* __LM3646_H__ */
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index 2f6f1f78d958..0b9f890ce431 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -1,7 +1,7 @@
/*
* Remote Controller core header
*
- * Copyright (C) 2009-2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2009-2010 by Mauro Carvalho Chehab
*
* 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
@@ -35,6 +35,29 @@ enum rc_driver_type {
};
/**
+ * struct rc_scancode_filter - Filter scan codes.
+ * @data: Scancode data to match.
+ * @mask: Mask of bits of scancode to compare.
+ */
+struct rc_scancode_filter {
+ u32 data;
+ u32 mask;
+};
+
+/**
+ * enum rc_filter_type - Filter type constants.
+ * @RC_FILTER_NORMAL: Filter for normal operation.
+ * @RC_FILTER_WAKEUP: Filter for waking from suspend.
+ * @RC_FILTER_MAX: Number of filter types.
+ */
+enum rc_filter_type {
+ RC_FILTER_NORMAL = 0,
+ RC_FILTER_WAKEUP,
+
+ RC_FILTER_MAX
+};
+
+/**
* struct rc_dev - represents a remote control device
* @dev: driver model's view of this device
* @input_name: name of the input child device
@@ -50,8 +73,10 @@ enum rc_driver_type {
* @input_dev: the input child device used to communicate events to userspace
* @driver_type: specifies if protocol decoding is done in hardware or software
* @idle: used to keep track of RX state
- * @allowed_protos: bitmask with the supported RC_BIT_* protocols
- * @enabled_protocols: bitmask with the enabled RC_BIT_* protocols
+ * @allowed_protocols: bitmask with the supported RC_BIT_* protocols for each
+ * filter type
+ * @enabled_protocols: bitmask with the enabled RC_BIT_* protocols for each
+ * filter type
* @scanmask: some hardware decoders are not capable of providing the full
* scancode to the application. As this is a hardware limit, we can't do
* anything with it. Yet, as the same keycode table can be used with other
@@ -70,7 +95,10 @@ enum rc_driver_type {
* @max_timeout: maximum timeout supported by device
* @rx_resolution : resolution (in ns) of input sampler
* @tx_resolution: resolution (in ns) of output sampler
+ * @scancode_filters: scancode filters (indexed by enum rc_filter_type)
* @change_protocol: allow changing the protocol used on hardware decoders
+ * @change_wakeup_protocol: allow changing the protocol used for wakeup
+ * filtering
* @open: callback to allow drivers to enable polling/irq when IR input device
* is opened.
* @close: callback to allow drivers to disable polling/irq when IR input device
@@ -84,6 +112,7 @@ enum rc_driver_type {
* device doesn't interrupt host until it sees IR pulses
* @s_learning_mode: enable wide band receiver used for learning
* @s_carrier_report: enable carrier reports
+ * @s_filter: set the scancode filter of a given type
*/
struct rc_dev {
struct device dev;
@@ -99,8 +128,8 @@ struct rc_dev {
struct input_dev *input_dev;
enum rc_driver_type driver_type;
bool idle;
- u64 allowed_protos;
- u64 enabled_protocols;
+ u64 allowed_protocols[RC_FILTER_MAX];
+ u64 enabled_protocols[RC_FILTER_MAX];
u32 users;
u32 scanmask;
void *priv;
@@ -116,7 +145,9 @@ struct rc_dev {
u32 max_timeout;
u32 rx_resolution;
u32 tx_resolution;
+ struct rc_scancode_filter scancode_filters[RC_FILTER_MAX];
int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
+ int (*change_wakeup_protocol)(struct rc_dev *dev, u64 *rc_type);
int (*open)(struct rc_dev *dev);
void (*close)(struct rc_dev *dev);
int (*s_tx_mask)(struct rc_dev *dev, u32 mask);
@@ -127,10 +158,49 @@ struct rc_dev {
void (*s_idle)(struct rc_dev *dev, bool enable);
int (*s_learning_mode)(struct rc_dev *dev, int enable);
int (*s_carrier_report) (struct rc_dev *dev, int enable);
+ int (*s_filter)(struct rc_dev *dev,
+ enum rc_filter_type type,
+ struct rc_scancode_filter *filter);
};
#define to_rc_dev(d) container_of(d, struct rc_dev, dev)
+static inline bool rc_protocols_allowed(struct rc_dev *rdev, u64 protos)
+{
+ return rdev->allowed_protocols[RC_FILTER_NORMAL] & protos;
+}
+
+/* should be called prior to registration or with mutex held */
+static inline void rc_set_allowed_protocols(struct rc_dev *rdev, u64 protos)
+{
+ rdev->allowed_protocols[RC_FILTER_NORMAL] = protos;
+}
+
+static inline bool rc_protocols_enabled(struct rc_dev *rdev, u64 protos)
+{
+ return rdev->enabled_protocols[RC_FILTER_NORMAL] & protos;
+}
+
+/* should be called prior to registration or with mutex held */
+static inline void rc_set_enabled_protocols(struct rc_dev *rdev, u64 protos)
+{
+ rdev->enabled_protocols[RC_FILTER_NORMAL] = protos;
+}
+
+/* should be called prior to registration or with mutex held */
+static inline void rc_set_allowed_wakeup_protocols(struct rc_dev *rdev,
+ u64 protos)
+{
+ rdev->allowed_protocols[RC_FILTER_WAKEUP] = protos;
+}
+
+/* should be called prior to registration or with mutex held */
+static inline void rc_set_enabled_wakeup_protocols(struct rc_dev *rdev,
+ u64 protos)
+{
+ rdev->enabled_protocols[RC_FILTER_WAKEUP] = protos;
+}
+
/*
* From rc-main.c
* Those functions can be used on any type of Remote Controller. They
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index a20ed97d7d8a..e5aa2409c0ea 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -1,7 +1,7 @@
/*
* rc-map.h - define RC map names used by RC drivers
*
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
*
* 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
@@ -30,6 +30,7 @@ enum rc_type {
RC_TYPE_RC6_6A_24 = 15, /* Philips RC6-6A-24 protocol */
RC_TYPE_RC6_6A_32 = 16, /* Philips RC6-6A-32 protocol */
RC_TYPE_RC6_MCE = 17, /* MCE (Philips RC6-6A-32 subtype) protocol */
+ RC_TYPE_SHARP = 18, /* Sharp protocol */
};
#define RC_BIT_NONE 0
@@ -51,6 +52,7 @@ enum rc_type {
#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_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | RC_BIT_LIRC | \
RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \
@@ -58,7 +60,7 @@ enum rc_type {
RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20 | \
RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \
RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \
- RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE)
+ RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP)
struct rc_map_table {
u32 scancode;
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index c768c9f8abc2..eec6e460f649 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -24,7 +24,8 @@
#define VFL_TYPE_VBI 1
#define VFL_TYPE_RADIO 2
#define VFL_TYPE_SUBDEV 3
-#define VFL_TYPE_MAX 4
+#define VFL_TYPE_SDR 4
+#define VFL_TYPE_MAX 5
/* Is this a receiver, transmitter or mem-to-mem? */
/* Ignored for VFL_TYPE_SUBDEV. */
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index e0b74a430b3a..50cf7c110a70 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -40,6 +40,8 @@ struct v4l2_ioctl_ops {
struct v4l2_fmtdesc *f);
int (*vidioc_enum_fmt_vid_out_mplane)(struct file *file, void *fh,
struct v4l2_fmtdesc *f);
+ int (*vidioc_enum_fmt_sdr_cap) (struct file *file, void *fh,
+ struct v4l2_fmtdesc *f);
/* VIDIOC_G_FMT handlers */
int (*vidioc_g_fmt_vid_cap) (struct file *file, void *fh,
@@ -62,6 +64,8 @@ struct v4l2_ioctl_ops {
struct v4l2_format *f);
int (*vidioc_g_fmt_vid_out_mplane)(struct file *file, void *fh,
struct v4l2_format *f);
+ int (*vidioc_g_fmt_sdr_cap) (struct file *file, void *fh,
+ struct v4l2_format *f);
/* VIDIOC_S_FMT handlers */
int (*vidioc_s_fmt_vid_cap) (struct file *file, void *fh,
@@ -84,6 +88,8 @@ struct v4l2_ioctl_ops {
struct v4l2_format *f);
int (*vidioc_s_fmt_vid_out_mplane)(struct file *file, void *fh,
struct v4l2_format *f);
+ int (*vidioc_s_fmt_sdr_cap) (struct file *file, void *fh,
+ struct v4l2_format *f);
/* VIDIOC_TRY_FMT handlers */
int (*vidioc_try_fmt_vid_cap) (struct file *file, void *fh,
@@ -106,6 +112,8 @@ struct v4l2_ioctl_ops {
struct v4l2_format *f);
int (*vidioc_try_fmt_vid_out_mplane)(struct file *file, void *fh,
struct v4l2_format *f);
+ int (*vidioc_try_fmt_sdr_cap) (struct file *file, void *fh,
+ struct v4l2_format *f);
/* Buffer handlers */
int (*vidioc_reqbufs) (struct file *file, void *fh, struct v4l2_requestbuffers *b);
@@ -265,6 +273,8 @@ struct v4l2_ioctl_ops {
struct v4l2_enum_dv_timings *timings);
int (*vidioc_dv_timings_cap) (struct file *file, void *fh,
struct v4l2_dv_timings_cap *cap);
+ int (*vidioc_g_edid) (struct file *file, void *fh, struct v4l2_edid *edid);
+ int (*vidioc_s_edid) (struct file *file, void *fh, struct v4l2_edid *edid);
int (*vidioc_subscribe_event) (struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub);
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index d67210a37ef3..28f4d8c3cf7d 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -162,6 +162,10 @@ struct v4l2_subdev_core_ops {
int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm);
int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);
long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
+#ifdef CONFIG_COMPAT
+ long (*compat_ioctl32)(struct v4l2_subdev *sd, unsigned int cmd,
+ unsigned long arg);
+#endif
#ifdef CONFIG_VIDEO_ADV_DEBUG
int (*g_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);
int (*s_register)(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg);
@@ -192,6 +196,7 @@ struct v4l2_subdev_tuner_ops {
int (*s_radio)(struct v4l2_subdev *sd);
int (*s_frequency)(struct v4l2_subdev *sd, const struct v4l2_frequency *freq);
int (*g_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq);
+ int (*enum_freq_bands)(struct v4l2_subdev *sd, struct v4l2_frequency_band *band);
int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
int (*s_tuner)(struct v4l2_subdev *sd, const struct v4l2_tuner *vt);
int (*g_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm);
@@ -503,8 +508,8 @@ struct v4l2_subdev_pad_ops {
struct v4l2_subdev_selection *sel);
int (*set_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel);
- int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid);
- int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid);
+ int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
+ int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
#ifdef CONFIG_MEDIA_CONTROLLER
int (*link_validate)(struct v4l2_subdev *sd, struct media_link *link,
struct v4l2_subdev_format *source_fmt,
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index bef53ce555d2..af4621109726 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -34,49 +34,49 @@ struct vb2_fileio_data;
* usually will result in the allocator freeing the buffer (if
* no other users of this buffer are present); the buf_priv
* argument is the allocator private per-buffer structure
- * previously returned from the alloc callback
+ * previously returned from the alloc callback.
* @get_userptr: acquire userspace memory for a hardware operation; used for
* USERPTR memory types; vaddr is the address passed to the
* videobuf layer when queuing a video buffer of USERPTR type;
* should return an allocator private per-buffer structure
* associated with the buffer on success, NULL on failure;
* the returned private structure will then be passed as buf_priv
- * argument to other ops in this structure
+ * argument to other ops in this structure.
* @put_userptr: inform the allocator that a USERPTR buffer will no longer
- * be used
+ * be used.
* @attach_dmabuf: attach a shared struct dma_buf for a hardware operation;
* used for DMABUF memory types; alloc_ctx is the alloc context
* dbuf is the shared dma_buf; returns NULL on failure;
* allocator private per-buffer structure on success;
- * this needs to be used for further accesses to the buffer
+ * this needs to be used for further accesses to the buffer.
* @detach_dmabuf: inform the exporter of the buffer that the current DMABUF
* buffer is no longer used; the buf_priv argument is the
* allocator private per-buffer structure previously returned
- * from the attach_dmabuf callback
+ * from the attach_dmabuf callback.
* @map_dmabuf: request for access to the dmabuf from allocator; the allocator
* of dmabuf is informed that this driver is going to use the
- * dmabuf
+ * dmabuf.
* @unmap_dmabuf: releases access control to the dmabuf - allocator is notified
- * that this driver is done using the dmabuf for now
+ * that this driver is done using the dmabuf for now.
* @prepare: called every time the buffer is passed from userspace to the
- * driver, useful for cache synchronisation, optional
+ * driver, useful for cache synchronisation, optional.
* @finish: called every time the buffer is passed back from the driver
- * to the userspace, also optional
+ * to the userspace, also optional.
* @vaddr: return a kernel virtual address to a given memory buffer
* associated with the passed private structure or NULL if no
- * such mapping exists
+ * such mapping exists.
* @cookie: return allocator specific cookie for a given memory buffer
* associated with the passed private structure or NULL if not
- * available
+ * available.
* @num_users: return the current number of users of a memory buffer;
* return 1 if the videobuf layer (or actually the driver using
- * it) is the only user
+ * it) is the only user.
* @mmap: setup a userspace mapping for a given memory buffer under
- * the provided virtual memory region
+ * the provided virtual memory region.
*
* Required ops for USERPTR types: get_userptr, put_userptr.
* Required ops for MMAP types: alloc, put, num_users, mmap.
- * Required ops for read/write access types: alloc, put, num_users, vaddr
+ * Required ops for read/write access types: alloc, put, num_users, vaddr.
* Required ops for DMABUF types: attach_dmabuf, detach_dmabuf, map_dmabuf,
* unmap_dmabuf.
*/
@@ -203,6 +203,37 @@ struct vb2_buffer {
struct list_head done_entry;
struct vb2_plane planes[VIDEO_MAX_PLANES];
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ /*
+ * Counters for how often these buffer-related ops are
+ * called. Used to check for unbalanced ops.
+ */
+ u32 cnt_mem_alloc;
+ u32 cnt_mem_put;
+ u32 cnt_mem_get_dmabuf;
+ u32 cnt_mem_get_userptr;
+ u32 cnt_mem_put_userptr;
+ u32 cnt_mem_prepare;
+ u32 cnt_mem_finish;
+ u32 cnt_mem_attach_dmabuf;
+ u32 cnt_mem_detach_dmabuf;
+ u32 cnt_mem_map_dmabuf;
+ u32 cnt_mem_unmap_dmabuf;
+ u32 cnt_mem_vaddr;
+ u32 cnt_mem_cookie;
+ u32 cnt_mem_num_users;
+ u32 cnt_mem_mmap;
+
+ u32 cnt_buf_init;
+ u32 cnt_buf_prepare;
+ u32 cnt_buf_finish;
+ u32 cnt_buf_cleanup;
+ u32 cnt_buf_queue;
+
+ /* This counts the number of calls to vb2_buffer_done() */
+ u32 cnt_buf_done;
+#endif
};
/**
@@ -227,27 +258,35 @@ struct vb2_buffer {
* @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
- * blocking access type
+ * blocking access type.
* @wait_finish: reacquire all locks released in the previous callback;
* required to continue operation after sleeping while
- * waiting for a new buffer to arrive
+ * waiting for a new buffer to arrive.
* @buf_init: called once after allocating a buffer (in MMAP case)
* or after acquiring a new USERPTR buffer; drivers may
* perform additional buffer-related initialization;
* initialization failure (return != 0) will prevent
- * queue setup from completing successfully; optional
+ * queue setup from completing successfully; optional.
* @buf_prepare: called every time the buffer is queued from userspace
* and from the VIDIOC_PREPARE_BUF ioctl; drivers may
* perform any initialization required before each hardware
* operation in this callback; drivers that support
* VIDIOC_CREATE_BUFS must also validate the buffer size;
* if an error is returned, the buffer will not be queued
- * in driver; optional
+ * in driver; optional.
* @buf_finish: called before every dequeue of the buffer back to
* userspace; drivers may perform any operations required
- * before userspace accesses the buffer; optional
+ * before userspace accesses the buffer; optional. The
+ * buffer state can be one of the following: DONE and
+ * ERROR occur while streaming is in progress, and the
+ * PREPARED state occurs when the queue has been canceled
+ * and all pending buffers are being returned to their
+ * default DEQUEUED state. Typically you only have to do
+ * something if the state is VB2_BUF_STATE_DONE, since in
+ * all other cases the buffer contents will be ignored
+ * anyway.
* @buf_cleanup: called once before the buffer is freed; drivers may
- * perform any additional cleanup; optional
+ * perform any additional cleanup; optional.
* @start_streaming: called once to enter 'streaming' state; the driver may
* receive buffers with @buf_queue callback before
* @start_streaming is called; the driver gets the number
@@ -268,7 +307,7 @@ struct vb2_buffer {
* the buffer back by calling vb2_buffer_done() function;
* it is allways called after calling STREAMON ioctl;
* might be called before start_streaming callback if user
- * pre-queued buffers before calling STREAMON
+ * pre-queued buffers before calling STREAMON.
*/
struct vb2_ops {
int (*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt,
@@ -280,7 +319,7 @@ struct vb2_ops {
int (*buf_init)(struct vb2_buffer *vb);
int (*buf_prepare)(struct vb2_buffer *vb);
- int (*buf_finish)(struct vb2_buffer *vb);
+ void (*buf_finish)(struct vb2_buffer *vb);
void (*buf_cleanup)(struct vb2_buffer *vb);
int (*start_streaming)(struct vb2_queue *q, unsigned int count);
@@ -312,23 +351,29 @@ struct v4l2_fh;
* @buf_struct_size: size of the driver-specific buffer structure;
* "0" indicates the driver doesn't want to use a custom buffer
* structure type, so sizeof(struct vb2_buffer) will is used
+ * @timestamp_flags: Timestamp flags; V4L2_BUF_FLAGS_TIMESTAMP_* and
+ * V4L2_BUF_FLAGS_TSTAMP_SRC_*
* @gfp_flags: additional gfp flags used when allocating the buffers.
* Typically this is 0, but it may be e.g. GFP_DMA or __GFP_DMA32
* to force the buffer allocation to a specific memory zone.
+ * @min_buffers_needed: the minimum number of buffers needed before
+ * start_streaming() can be called. Used when a DMA engine
+ * cannot be started unless at least this number of buffers
+ * have been queued into the driver.
*
* @memory: current memory type used
* @bufs: videobuf buffer structures
* @num_buffers: number of allocated/used buffers
* @queued_list: list of buffers currently queued from userspace
- * @queued_count: number of buffers owned by the driver
+ * @queued_count: number of buffers queued and ready for streaming.
+ * @owned_by_drv_count: number of buffers owned by the driver
* @done_list: list of buffers ready to be dequeued to userspace
* @done_lock: lock to protect done_list list
* @done_wq: waitqueue for processes waiting for buffers ready to be dequeued
* @alloc_ctx: memory type/allocator-specific contexts for each plane
* @streaming: current streaming state
- * @retry_start_streaming: start_streaming() was called, but there were not enough
- * buffers queued. If set, then retry calling start_streaming when
- * queuing a new buffer.
+ * @start_streaming_called: start_streaming() was called successfully and we
+ * started streaming.
* @fileio: file io emulator internal data, used only if emulator is active
*/
struct vb2_queue {
@@ -342,8 +387,9 @@ struct vb2_queue {
const struct vb2_mem_ops *mem_ops;
void *drv_priv;
unsigned int buf_struct_size;
- u32 timestamp_type;
+ u32 timestamp_flags;
gfp_t gfp_flags;
+ u32 min_buffers_needed;
/* private: internal use only */
enum v4l2_memory memory;
@@ -351,8 +397,9 @@ struct vb2_queue {
unsigned int num_buffers;
struct list_head queued_list;
+ unsigned int queued_count;
- atomic_t queued_count;
+ atomic_t owned_by_drv_count;
struct list_head done_list;
spinlock_t done_lock;
wait_queue_head_t done_wq;
@@ -361,9 +408,21 @@ struct vb2_queue {
unsigned int plane_sizes[VIDEO_MAX_PLANES];
unsigned int streaming:1;
- unsigned int retry_start_streaming:1;
+ unsigned int start_streaming_called:1;
struct vb2_fileio_data *fileio;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ /*
+ * Counters for how often these queue-related ops are
+ * called. Used to check for unbalanced ops.
+ */
+ u32 cnt_queue_setup;
+ u32 cnt_wait_prepare;
+ u32 cnt_wait_finish;
+ u32 cnt_start_streaming;
+ u32 cnt_stop_streaming;
+#endif
};
void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
diff --git a/net/ieee802154/6lowpan.h b/include/net/6lowpan.h
index 2b835db3bda8..f7d372b7d4ff 100644
--- a/net/ieee802154/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -53,6 +53,8 @@
#ifndef __6LOWPAN_H__
#define __6LOWPAN_H__
+#include <net/ipv6.h>
+
#define UIP_802154_SHORTADDR_LEN 2 /* compressed ipv6 address length */
#define UIP_IPH_LEN 40 /* ipv6 fixed header size */
#define UIP_PROTO_UDP 17 /* ipv6 next header value for UDP */
@@ -306,6 +308,119 @@ static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data,
*hc_ptr += len;
}
+static inline u8 lowpan_addr_mode_size(const u8 addr_mode)
+{
+ static const u8 addr_sizes[] = {
+ [LOWPAN_IPHC_ADDR_00] = 16,
+ [LOWPAN_IPHC_ADDR_01] = 8,
+ [LOWPAN_IPHC_ADDR_02] = 2,
+ [LOWPAN_IPHC_ADDR_03] = 0,
+ };
+ return addr_sizes[addr_mode];
+}
+
+static inline u8 lowpan_next_hdr_size(const u8 h_enc, u16 *uncomp_header)
+{
+ u8 ret = 1;
+
+ if ((h_enc & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
+ *uncomp_header += sizeof(struct udphdr);
+
+ switch (h_enc & LOWPAN_NHC_UDP_CS_P_11) {
+ case LOWPAN_NHC_UDP_CS_P_00:
+ ret += 4;
+ break;
+ case LOWPAN_NHC_UDP_CS_P_01:
+ case LOWPAN_NHC_UDP_CS_P_10:
+ ret += 3;
+ break;
+ case LOWPAN_NHC_UDP_CS_P_11:
+ ret++;
+ break;
+ default:
+ break;
+ }
+
+ if (!(h_enc & LOWPAN_NHC_UDP_CS_C))
+ ret += 2;
+ }
+
+ return ret;
+}
+
+/**
+ * lowpan_uncompress_size - returns skb->len size with uncompressed header
+ * @skb: sk_buff with 6lowpan header inside
+ * @datagram_offset: optional to get the datagram_offset value
+ *
+ * Returns the skb->len with uncompressed header
+ */
+static inline u16
+lowpan_uncompress_size(const struct sk_buff *skb, u16 *dgram_offset)
+{
+ u16 ret = 2, uncomp_header = sizeof(struct ipv6hdr);
+ u8 iphc0, iphc1, h_enc;
+
+ iphc0 = skb_network_header(skb)[0];
+ iphc1 = skb_network_header(skb)[1];
+
+ switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
+ case 0:
+ ret += 4;
+ break;
+ case 1:
+ ret += 3;
+ break;
+ case 2:
+ ret++;
+ break;
+ default:
+ break;
+ }
+
+ if (!(iphc0 & LOWPAN_IPHC_NH_C))
+ ret++;
+
+ if (!(iphc0 & 0x03))
+ ret++;
+
+ ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_SAM) >>
+ LOWPAN_IPHC_SAM_BIT);
+
+ if (iphc1 & LOWPAN_IPHC_M) {
+ switch ((iphc1 & LOWPAN_IPHC_DAM_11) >>
+ LOWPAN_IPHC_DAM_BIT) {
+ case LOWPAN_IPHC_DAM_00:
+ ret += 16;
+ break;
+ case LOWPAN_IPHC_DAM_01:
+ ret += 6;
+ break;
+ case LOWPAN_IPHC_DAM_10:
+ ret += 4;
+ break;
+ case LOWPAN_IPHC_DAM_11:
+ ret++;
+ break;
+ default:
+ break;
+ }
+ } else {
+ ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_DAM_11) >>
+ LOWPAN_IPHC_DAM_BIT);
+ }
+
+ if (iphc0 & LOWPAN_IPHC_NH_C) {
+ h_enc = skb_network_header(skb)[ret];
+ ret += lowpan_next_hdr_size(h_enc, &uncomp_header);
+ }
+
+ if (dgram_offset)
+ *dgram_offset = uncomp_header;
+
+ return skb->len + uncomp_header - ret;
+}
+
typedef int (*skb_delivery_cb)(struct sk_buff *skb, struct net_device *dev);
int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
diff --git a/include/net/act_api.h b/include/net/act_api.h
index 788d8378e587..3ee4c92afd1b 100644
--- a/include/net/act_api.h
+++ b/include/net/act_api.h
@@ -89,7 +89,7 @@ struct tc_action_ops {
struct module *owner;
int (*act)(struct sk_buff *, const struct tc_action *, struct tcf_result *);
int (*dump)(struct sk_buff *, struct tc_action *, int, int);
- int (*cleanup)(struct tc_action *, int bind);
+ void (*cleanup)(struct tc_action *, int bind);
int (*lookup)(struct tc_action *, u32);
int (*init)(struct net *net, struct nlattr *nla,
struct nlattr *est, struct tc_action *act, int ovr,
@@ -98,20 +98,18 @@ struct tc_action_ops {
};
int tcf_hash_search(struct tc_action *a, u32 index);
-void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo);
-int tcf_hash_release(struct tcf_common *p, int bind,
- struct tcf_hashinfo *hinfo);
+void tcf_hash_destroy(struct tc_action *a);
+int tcf_hash_release(struct tc_action *a, int bind);
u32 tcf_hash_new_index(struct tcf_hashinfo *hinfo);
-struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a,
- int bind);
-struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est,
- struct tc_action *a, int size,
- int bind);
-void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo);
+int tcf_hash_check(u32 index, struct tc_action *a, int bind);
+int tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a,
+ int size, int bind);
+void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est);
+void tcf_hash_insert(struct tc_action *a);
-int tcf_register_action(struct tc_action_ops *a);
+int tcf_register_action(struct tc_action_ops *a, unsigned int mask);
int tcf_unregister_action(struct tc_action_ops *a);
-void tcf_action_destroy(struct list_head *actions, int bind);
+int tcf_action_destroy(struct list_head *actions, int bind);
int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions,
struct tcf_result *res);
int tcf_action_init(struct net *net, struct nlattr *nla,
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 50e39a8822b4..933a9f22a05f 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -314,7 +314,7 @@ static inline bool ipv6_addr_is_multicast(const struct in6_addr *addr)
static inline bool ipv6_addr_is_ll_all_nodes(const struct in6_addr *addr)
{
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
- __u64 *p = (__u64 *)addr;
+ __be64 *p = (__be64 *)addr;
return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) | (p[1] ^ cpu_to_be64(1))) == 0UL;
#else
return ((addr->s6_addr32[0] ^ htonl(0xff020000)) |
@@ -326,7 +326,7 @@ static inline bool ipv6_addr_is_ll_all_nodes(const struct in6_addr *addr)
static inline bool ipv6_addr_is_ll_all_routers(const struct in6_addr *addr)
{
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
- __u64 *p = (__u64 *)addr;
+ __be64 *p = (__be64 *)addr;
return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) | (p[1] ^ cpu_to_be64(2))) == 0UL;
#else
return ((addr->s6_addr32[0] ^ htonl(0xff020000)) |
@@ -343,7 +343,7 @@ static inline bool ipv6_addr_is_isatap(const struct in6_addr *addr)
static inline bool ipv6_addr_is_solict_mult(const struct in6_addr *addr)
{
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
- __u64 *p = (__u64 *)addr;
+ __be64 *p = (__be64 *)addr;
return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) |
((p[1] ^ cpu_to_be64(0x00000001ff000000UL)) &
cpu_to_be64(0xffffffffff000000UL))) == 0UL;
diff --git a/include/net/af_ieee802154.h b/include/net/af_ieee802154.h
index 75e64c7a2960..f79ae2aa76d6 100644
--- a/include/net/af_ieee802154.h
+++ b/include/net/af_ieee802154.h
@@ -36,7 +36,7 @@ enum {
/* address length, octets */
#define IEEE802154_ADDR_LEN 8
-struct ieee802154_addr {
+struct ieee802154_addr_sa {
int addr_type;
u16 pan_id;
union {
@@ -51,7 +51,7 @@ struct ieee802154_addr {
struct sockaddr_ieee802154 {
sa_family_t family; /* AF_IEEE802154 */
- struct ieee802154_addr addr;
+ struct ieee802154_addr_sa addr;
};
/* get/setsockopt */
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index f4f9ee466791..904777c1cd24 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -65,6 +65,7 @@ struct bt_security {
#define BT_SECURITY_LOW 1
#define BT_SECURITY_MEDIUM 2
#define BT_SECURITY_HIGH 3
+#define BT_SECURITY_FIPS 4
#define BT_DEFER_SETUP 7
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 66c1cd87bfe7..be150cf8cd43 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -117,11 +117,18 @@ enum {
HCI_SERVICE_CACHE,
HCI_DEBUG_KEYS,
HCI_DUT_MODE,
+ HCI_FORCE_SC,
+ HCI_FORCE_STATIC_ADDR,
HCI_UNREGISTER,
HCI_USER_CHANNEL,
HCI_LE_SCAN,
HCI_SSP_ENABLED,
+ HCI_SC_ENABLED,
+ HCI_SC_ONLY,
+ HCI_PRIVACY,
+ HCI_RPA_EXPIRED,
+ HCI_RPA_RESOLVING,
HCI_HS_ENABLED,
HCI_LE_ENABLED,
HCI_ADVERTISING,
@@ -133,6 +140,7 @@ enum {
HCI_FAST_CONNECTABLE,
HCI_BREDR_ENABLED,
HCI_6LOWPAN_ENABLED,
+ HCI_LE_SCAN_INTERRUPTED,
};
/* A mask for the flags that are supposed to remain when a reset happens
@@ -175,6 +183,8 @@ enum {
#define HCI_CMD_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
#define HCI_ACL_TX_TIMEOUT msecs_to_jiffies(45000) /* 45 seconds */
#define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
+#define HCI_POWER_OFF_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */
+#define HCI_LE_CONN_TIMEOUT msecs_to_jiffies(20000) /* 20 seconds */
/* HCI data types */
#define HCI_COMMAND_PKT 0x01
@@ -282,10 +292,14 @@ enum {
#define LMP_SYNC_TRAIN 0x04
#define LMP_SYNC_SCAN 0x08
+#define LMP_SC 0x01
+#define LMP_PING 0x02
+
/* Host features */
#define LMP_HOST_SSP 0x01
#define LMP_HOST_LE 0x02
#define LMP_HOST_LE_BREDR 0x04
+#define LMP_HOST_SC 0x08
/* Connection modes */
#define HCI_CM_ACTIVE 0x0000
@@ -307,6 +321,7 @@ enum {
#define HCI_LM_TRUSTED 0x0008
#define HCI_LM_RELIABLE 0x0010
#define HCI_LM_SECURE 0x0020
+#define HCI_LM_FIPS 0x0040
/* Authentication types */
#define HCI_AT_NO_BONDING 0x00
@@ -327,17 +342,24 @@ enum {
#define HCI_LK_LOCAL_UNIT 0x01
#define HCI_LK_REMOTE_UNIT 0x02
#define HCI_LK_DEBUG_COMBINATION 0x03
-#define HCI_LK_UNAUTH_COMBINATION 0x04
-#define HCI_LK_AUTH_COMBINATION 0x05
+#define HCI_LK_UNAUTH_COMBINATION_P192 0x04
+#define HCI_LK_AUTH_COMBINATION_P192 0x05
#define HCI_LK_CHANGED_COMBINATION 0x06
+#define HCI_LK_UNAUTH_COMBINATION_P256 0x07
+#define HCI_LK_AUTH_COMBINATION_P256 0x08
/* The spec doesn't define types for SMP keys, the _MASTER suffix is implied */
#define HCI_SMP_STK 0x80
#define HCI_SMP_STK_SLAVE 0x81
#define HCI_SMP_LTK 0x82
#define HCI_SMP_LTK_SLAVE 0x83
+/* Long Term Key types */
+#define HCI_LTK_UNAUTH 0x00
+#define HCI_LTK_AUTH 0x01
+
/* ---- HCI Error Codes ---- */
#define HCI_ERROR_AUTH_FAILURE 0x05
+#define HCI_ERROR_MEMORY_EXCEEDED 0x07
#define HCI_ERROR_CONNECTION_TIMEOUT 0x08
#define HCI_ERROR_REJ_BAD_ADDR 0x0f
#define HCI_ERROR_REMOTE_USER_TERM 0x13
@@ -660,6 +682,15 @@ struct hci_rp_set_csb {
#define HCI_OP_START_SYNC_TRAIN 0x0443
+#define HCI_OP_REMOTE_OOB_EXT_DATA_REPLY 0x0445
+struct hci_cp_remote_oob_ext_data_reply {
+ bdaddr_t bdaddr;
+ __u8 hash192[16];
+ __u8 randomizer192[16];
+ __u8 hash256[16];
+ __u8 randomizer256[16];
+} __packed;
+
#define HCI_OP_SNIFF_MODE 0x0803
struct hci_cp_sniff_mode {
__le16 handle;
@@ -933,6 +964,26 @@ struct hci_rp_write_sync_train_params {
__le16 sync_train_int;
} __packed;
+#define HCI_OP_READ_SC_SUPPORT 0x0c79
+struct hci_rp_read_sc_support {
+ __u8 status;
+ __u8 support;
+} __packed;
+
+#define HCI_OP_WRITE_SC_SUPPORT 0x0c7a
+struct hci_cp_write_sc_support {
+ __u8 support;
+} __packed;
+
+#define HCI_OP_READ_LOCAL_OOB_EXT_DATA 0x0c7d
+struct hci_rp_read_local_oob_ext_data {
+ __u8 status;
+ __u8 hash192[16];
+ __u8 randomizer192[16];
+ __u8 hash256[16];
+ __u8 randomizer256[16];
+} __packed;
+
#define HCI_OP_READ_LOCAL_VERSION 0x1001
struct hci_rp_read_local_version {
__u8 status;
@@ -1133,6 +1184,9 @@ struct hci_cp_le_set_scan_enable {
__u8 filter_dup;
} __packed;
+#define HCI_LE_USE_PEER_ADDR 0x00
+#define HCI_LE_USE_WHITELIST 0x01
+
#define HCI_OP_LE_CREATE_CONN 0x200d
struct hci_cp_le_create_conn {
__le16 scan_interval;
@@ -1157,6 +1211,20 @@ struct hci_rp_le_read_white_list_size {
__u8 size;
} __packed;
+#define HCI_OP_LE_CLEAR_WHITE_LIST 0x2010
+
+#define HCI_OP_LE_ADD_TO_WHITE_LIST 0x2011
+struct hci_cp_le_add_to_white_list {
+ __u8 bdaddr_type;
+ bdaddr_t bdaddr;
+} __packed;
+
+#define HCI_OP_LE_DEL_FROM_WHITE_LIST 0x2012
+struct hci_cp_le_del_from_white_list {
+ __u8 bdaddr_type;
+ bdaddr_t bdaddr;
+} __packed;
+
#define HCI_OP_LE_CONN_UPDATE 0x2013
struct hci_cp_le_conn_update {
__le16 handle;
@@ -1171,7 +1239,7 @@ struct hci_cp_le_conn_update {
#define HCI_OP_LE_START_ENC 0x2019
struct hci_cp_le_start_enc {
__le16 handle;
- __u8 rand[8];
+ __le64 rand;
__le16 ediv;
__u8 ltk[16];
} __packed;
@@ -1583,7 +1651,7 @@ struct hci_ev_le_conn_complete {
#define HCI_EV_LE_LTK_REQ 0x05
struct hci_ev_le_ltk_req {
__le16 handle;
- __u8 random[8];
+ __le64 rand;
__le16 ediv;
} __packed;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index f2f0cf5865c4..5f8bc05694ac 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -91,6 +91,13 @@ struct bt_uuid {
u8 svc_hint;
};
+struct smp_csrk {
+ bdaddr_t bdaddr;
+ u8 bdaddr_type;
+ u8 master;
+ u8 val[16];
+};
+
struct smp_ltk {
struct list_head list;
bdaddr_t bdaddr;
@@ -99,9 +106,17 @@ struct smp_ltk {
u8 type;
u8 enc_size;
__le16 ediv;
- u8 rand[8];
+ __le64 rand;
u8 val[16];
-} __packed;
+};
+
+struct smp_irk {
+ struct list_head list;
+ bdaddr_t rpa;
+ bdaddr_t bdaddr;
+ u8 addr_type;
+ u8 val[16];
+};
struct link_key {
struct list_head list;
@@ -114,12 +129,17 @@ struct link_key {
struct oob_data {
struct list_head list;
bdaddr_t bdaddr;
- u8 hash[16];
- u8 randomizer[16];
+ u8 hash192[16];
+ u8 randomizer192[16];
+ u8 hash256[16];
+ u8 randomizer256[16];
};
#define HCI_MAX_SHORT_NAME_LENGTH 10
+/* Default LE RPA expiry time, 15 minutes */
+#define HCI_DEFAULT_RPA_TIMEOUT (15 * 60)
+
struct amp_assoc {
__u16 len;
__u16 offset;
@@ -141,8 +161,9 @@ struct hci_dev {
__u8 bus;
__u8 dev_type;
bdaddr_t bdaddr;
+ bdaddr_t random_addr;
bdaddr_t static_addr;
- __u8 own_addr_type;
+ __u8 adv_addr_type;
__u8 dev_name[HCI_MAX_NAME_LENGTH];
__u8 short_name[HCI_MAX_SHORT_NAME_LENGTH];
__u8 eir[HCI_MAX_EIR_LENGTH];
@@ -167,6 +188,8 @@ struct hci_dev {
__u16 page_scan_interval;
__u16 page_scan_window;
__u8 page_scan_type;
+ __u8 le_adv_channel_map;
+ __u8 le_scan_type;
__u16 le_scan_interval;
__u16 le_scan_window;
__u16 le_conn_min_interval;
@@ -257,19 +280,21 @@ struct hci_dev {
__u32 req_status;
__u32 req_result;
- struct list_head mgmt_pending;
+ struct crypto_blkcipher *tfm_aes;
struct discovery_state discovery;
struct hci_conn_hash conn_hash;
- struct list_head blacklist;
+ struct list_head mgmt_pending;
+ struct list_head blacklist;
struct list_head uuids;
-
struct list_head link_keys;
-
struct list_head long_term_keys;
-
+ struct list_head identity_resolving_keys;
struct list_head remote_oob_data;
+ struct list_head le_white_list;
+ struct list_head le_conn_params;
+ struct list_head pend_le_conns;
struct hci_dev_stats stat;
@@ -291,6 +316,11 @@ struct hci_dev {
__u8 scan_rsp_data[HCI_MAX_AD_LENGTH];
__u8 scan_rsp_data_len;
+ __u8 irk[16];
+ __u32 rpa_timeout;
+ struct delayed_work rpa_expired;
+ bdaddr_t rpa;
+
int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev);
@@ -310,6 +340,10 @@ struct hci_conn {
__u8 dst_type;
bdaddr_t src;
__u8 src_type;
+ bdaddr_t init_addr;
+ __u8 init_addr_type;
+ bdaddr_t resp_addr;
+ __u8 resp_addr_type;
__u16 handle;
__u16 state;
__u8 mode;
@@ -332,6 +366,8 @@ struct hci_conn {
__u8 passkey_entered;
__u16 disc_timeout;
__u16 setting;
+ __u16 le_conn_min_interval;
+ __u16 le_conn_max_interval;
unsigned long flags;
__u8 remote_cap;
@@ -347,6 +383,7 @@ struct hci_conn {
struct delayed_work disc_work;
struct delayed_work auto_accept_work;
struct delayed_work idle_work;
+ struct delayed_work le_conn_timeout;
struct device dev;
@@ -372,6 +409,22 @@ struct hci_chan {
__u8 state;
};
+struct hci_conn_params {
+ struct list_head list;
+
+ bdaddr_t addr;
+ u8 addr_type;
+
+ u16 conn_min_interval;
+ u16 conn_max_interval;
+
+ enum {
+ HCI_AUTO_CONN_DISABLED,
+ HCI_AUTO_CONN_ALWAYS,
+ HCI_AUTO_CONN_LINK_LOSS,
+ } auto_connect;
+};
+
extern struct list_head hci_dev_list;
extern struct list_head hci_cb_list;
extern rwlock_t hci_dev_list_lock;
@@ -446,6 +499,8 @@ enum {
HCI_CONN_LE_SMP_PEND,
HCI_CONN_MGMT_CONNECTED,
HCI_CONN_SSP_ENABLED,
+ HCI_CONN_SC_ENABLED,
+ HCI_CONN_AES_CCM,
HCI_CONN_POWER_SAVE,
HCI_CONN_REMOTE_OOB,
HCI_CONN_6LOWPAN,
@@ -458,6 +513,13 @@ static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
test_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
}
+static inline bool hci_conn_sc_enabled(struct hci_conn *conn)
+{
+ struct hci_dev *hdev = conn->hdev;
+ return test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
+ test_bit(HCI_CONN_SC_ENABLED, &conn->flags);
+}
+
static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
{
struct hci_conn_hash *h = &hdev->conn_hash;
@@ -521,6 +583,13 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type)
}
}
+static inline unsigned int hci_conn_count(struct hci_dev *hdev)
+{
+ struct hci_conn_hash *c = &hdev->conn_hash;
+
+ return c->acl_num + c->amp_num + c->sco_num + c->le_num;
+}
+
static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
__u16 handle)
{
@@ -594,8 +663,10 @@ void hci_chan_del(struct hci_chan *chan);
void hci_chan_list_flush(struct hci_conn *conn);
struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle);
-struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
- __u8 dst_type, __u8 sec_level, __u8 auth_type);
+struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
+ u8 dst_type, u8 sec_level, u8 auth_type);
+struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
+ u8 sec_level, u8 auth_type);
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
__u16 setting);
int hci_conn_check_link_mode(struct hci_conn *conn);
@@ -606,6 +677,8 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active);
+void hci_le_conn_failed(struct hci_conn *conn, u8 status);
+
/*
* hci_conn_get() and hci_conn_put() are used to control the life-time of an
* "hci_conn" object. They do not guarantee that the hci_conn object is running,
@@ -737,31 +810,64 @@ int hci_inquiry(void __user *arg);
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 type);
-int hci_blacklist_clear(struct hci_dev *hdev);
int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
-int hci_uuids_clear(struct hci_dev *hdev);
+struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev,
+ bdaddr_t *bdaddr, u8 type);
+void hci_white_list_clear(struct hci_dev *hdev);
+int hci_white_list_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
+int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
+
+struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
+ bdaddr_t *addr, u8 addr_type);
+int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
+ u8 auto_connect, u16 conn_min_interval,
+ u16 conn_max_interval);
+void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
+void hci_conn_params_clear(struct hci_dev *hdev);
+
+struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev,
+ bdaddr_t *addr, u8 addr_type);
+void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
+void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
+void hci_pend_le_conns_clear(struct hci_dev *hdev);
+
+void hci_update_background_scan(struct hci_dev *hdev);
-int hci_link_keys_clear(struct hci_dev *hdev);
+void hci_uuids_clear(struct hci_dev *hdev);
+
+void hci_link_keys_clear(struct hci_dev *hdev);
struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len);
-struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]);
-int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type,
- int new_key, u8 authenticated, u8 tk[16], u8 enc_size,
- __le16 ediv, u8 rand[8]);
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
+ bool master);
+struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type, u8 type, u8 authenticated,
+ u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand);
struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 addr_type);
-int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr);
-int hci_smp_ltks_clear(struct hci_dev *hdev);
+ u8 addr_type, bool master);
+int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type);
+void hci_smp_ltks_clear(struct hci_dev *hdev);
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
-int hci_remote_oob_data_clear(struct hci_dev *hdev);
+struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa);
+struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type);
+struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type, u8 val[16], bdaddr_t *rpa);
+void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type);
+void hci_smp_irks_clear(struct hci_dev *hdev);
+
+void hci_remote_oob_data_clear(struct hci_dev *hdev);
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
- bdaddr_t *bdaddr);
-int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
- u8 *randomizer);
+ bdaddr_t *bdaddr);
+int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 *hash, u8 *randomizer);
+int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 *hash192, u8 *randomizer192,
+ u8 *hash256, u8 *randomizer256);
int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
@@ -803,9 +909,12 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#define lmp_csb_slave_capable(dev) ((dev)->features[2][0] & LMP_CSB_SLAVE)
#define lmp_sync_train_capable(dev) ((dev)->features[2][0] & LMP_SYNC_TRAIN)
#define lmp_sync_scan_capable(dev) ((dev)->features[2][0] & LMP_SYNC_SCAN)
+#define lmp_sc_capable(dev) ((dev)->features[2][1] & LMP_SC)
+#define lmp_ping_capable(dev) ((dev)->features[2][1] & LMP_PING)
/* ----- Host capabilities ----- */
#define lmp_host_ssp_capable(dev) ((dev)->features[1][0] & LMP_HOST_SSP)
+#define lmp_host_sc_capable(dev) ((dev)->features[1][0] & LMP_HOST_SC)
#define lmp_host_le_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE))
#define lmp_host_le_br_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE_BREDR))
@@ -1019,6 +1128,26 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
return false;
}
+static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
+{
+ if (addr_type != 0x01)
+ return false;
+
+ if ((bdaddr->b[5] & 0xc0) == 0x40)
+ return true;
+
+ return false;
+}
+
+static inline struct smp_irk *hci_get_irk(struct hci_dev *hdev,
+ bdaddr_t *bdaddr, u8 addr_type)
+{
+ if (!hci_bdaddr_is_rpa(bdaddr, addr_type))
+ return NULL;
+
+ return hci_find_irk_by_rpa(hdev, bdaddr);
+}
+
int hci_register_cb(struct hci_cb *hcb);
int hci_unregister_cb(struct hci_cb *hcb);
@@ -1040,6 +1169,9 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
const void *param, u8 event);
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
+void hci_req_add_le_scan_disable(struct hci_request *req);
+void hci_req_add_le_passive_scan(struct hci_request *req);
+
struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param, u32 timeout);
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
@@ -1085,6 +1217,7 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered);
void mgmt_discoverable_timeout(struct hci_dev *hdev);
void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable);
void mgmt_connectable(struct hci_dev *hdev, u8 connectable);
+void mgmt_advertising(struct hci_dev *hdev, u8 advertising);
void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
bool persistent);
@@ -1092,7 +1225,8 @@ void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u32 flags, u8 *name, u8 name_len,
u8 *dev_class);
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 link_type, u8 addr_type, u8 reason);
+ u8 link_type, u8 addr_type, u8 reason,
+ bool mgmt_connected);
void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 status);
void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
@@ -1103,7 +1237,7 @@ void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status);
int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 link_type, u8 addr_type, __le32 value,
+ u8 link_type, u8 addr_type, u32 value,
u8 confirm_hint);
int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 status);
@@ -1122,11 +1256,13 @@ void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 status);
void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
+void mgmt_sc_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_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
- u8 *randomizer, u8 status);
+void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
+ u8 *randomizer192, u8 *hash256,
+ u8 *randomizer256, u8 status);
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
u8 ssp, u8 *eir, u16 eir_len);
@@ -1135,8 +1271,12 @@ void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
void mgmt_discovering(struct hci_dev *hdev, u8 discovering);
int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
-void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent);
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent);
+void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk);
+void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk,
+ bool persistent);
void mgmt_reenable_advertising(struct hci_dev *hdev);
+void mgmt_smp_complete(struct hci_conn *conn, bool complete);
/* HCI info for socket */
#define hci_pi(sk) ((struct hci_pinfo *) sk)
@@ -1168,9 +1308,14 @@ struct hci_sec_filter {
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
u16 latency, u16 to_multiplier);
-void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
+void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
__u8 ltk[16]);
+int hci_update_random_address(struct hci_request *req, bool require_privacy,
+ u8 *own_addr_type);
+void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 *bdaddr_type);
+
#define SCO_AIRMODE_MASK 0x0003
#define SCO_AIRMODE_CVSD 0x0000
#define SCO_AIRMODE_TRANSP 0x0003
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index dbc4a89984ca..4abdcb220e3a 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -91,6 +91,7 @@ struct l2cap_conninfo {
#define L2CAP_LM_TRUSTED 0x0008
#define L2CAP_LM_RELIABLE 0x0010
#define L2CAP_LM_SECURE 0x0020
+#define L2CAP_LM_FIPS 0x0040
/* L2CAP command codes */
#define L2CAP_COMMAND_REJ 0x01
@@ -623,6 +624,9 @@ struct l2cap_conn {
__u32 rx_len;
__u8 tx_ident;
+ struct sk_buff_head pending_rx;
+ struct work_struct pending_rx_work;
+
__u8 disc_reason;
struct delayed_work security_timer;
@@ -647,7 +651,7 @@ struct l2cap_user {
#define L2CAP_CHAN_RAW 1
#define L2CAP_CHAN_CONN_LESS 2
#define L2CAP_CHAN_CONN_ORIENTED 3
-#define L2CAP_CHAN_CONN_FIX_A2MP 4
+#define L2CAP_CHAN_FIXED 4
/* ----- L2CAP socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
@@ -853,7 +857,6 @@ static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan)
}
extern bool disable_ertm;
-extern bool enable_lecoc;
int l2cap_init_sockets(void);
void l2cap_cleanup_sockets(void);
@@ -878,6 +881,7 @@ int l2cap_ertm_init(struct l2cap_chan *chan);
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
void l2cap_chan_del(struct l2cap_chan *chan, int err);
+void l2cap_conn_update_id_addr(struct hci_conn *hcon);
void l2cap_send_conn_req(struct l2cap_chan *chan);
void l2cap_move_start(struct l2cap_chan *chan);
void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 518c5c84e39a..d4b571c2f9fd 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -94,6 +94,9 @@ struct mgmt_rp_read_index_list {
#define MGMT_SETTING_HS 0x00000100
#define MGMT_SETTING_LE 0x00000200
#define MGMT_SETTING_ADVERTISING 0x00000400
+#define MGMT_SETTING_SECURE_CONN 0x00000800
+#define MGMT_SETTING_DEBUG_KEYS 0x00001000
+#define MGMT_SETTING_PRIVACY 0x00002000
#define MGMT_OP_READ_INFO 0x0004
#define MGMT_READ_INFO_SIZE 0
@@ -180,11 +183,11 @@ struct mgmt_cp_load_link_keys {
struct mgmt_ltk_info {
struct mgmt_addr_info addr;
- __u8 authenticated;
+ __u8 type;
__u8 master;
__u8 enc_size;
__le16 ediv;
- __u8 rand[8];
+ __le64 rand;
__u8 val[16];
} __packed;
@@ -294,6 +297,12 @@ struct mgmt_rp_read_local_oob_data {
__u8 hash[16];
__u8 randomizer[16];
} __packed;
+struct mgmt_rp_read_local_oob_ext_data {
+ __u8 hash192[16];
+ __u8 randomizer192[16];
+ __u8 hash256[16];
+ __u8 randomizer256[16];
+} __packed;
#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021
struct mgmt_cp_add_remote_oob_data {
@@ -302,6 +311,14 @@ struct mgmt_cp_add_remote_oob_data {
__u8 randomizer[16];
} __packed;
#define MGMT_ADD_REMOTE_OOB_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 32)
+struct mgmt_cp_add_remote_oob_ext_data {
+ struct mgmt_addr_info addr;
+ __u8 hash192[16];
+ __u8 randomizer192[16];
+ __u8 hash256[16];
+ __u8 randomizer256[16];
+} __packed;
+#define MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 64)
#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0022
struct mgmt_cp_remove_remote_oob_data {
@@ -369,6 +386,29 @@ struct mgmt_cp_set_scan_params {
} __packed;
#define MGMT_SET_SCAN_PARAMS_SIZE 4
+#define MGMT_OP_SET_SECURE_CONN 0x002D
+
+#define MGMT_OP_SET_DEBUG_KEYS 0x002E
+
+#define MGMT_OP_SET_PRIVACY 0x002F
+struct mgmt_cp_set_privacy {
+ __u8 privacy;
+ __u8 irk[16];
+} __packed;
+#define MGMT_SET_PRIVACY_SIZE 17
+
+struct mgmt_irk_info {
+ struct mgmt_addr_info addr;
+ __u8 val[16];
+} __packed;
+
+#define MGMT_OP_LOAD_IRKS 0x0030
+struct mgmt_cp_load_irks {
+ __le16 irk_count;
+ struct mgmt_irk_info irks[0];
+} __packed;
+#define MGMT_LOAD_IRKS_SIZE 2
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
@@ -504,3 +544,22 @@ struct mgmt_ev_passkey_notify {
__le32 passkey;
__u8 entered;
} __packed;
+
+#define MGMT_EV_NEW_IRK 0x0018
+struct mgmt_ev_new_irk {
+ __u8 store_hint;
+ bdaddr_t rpa;
+ struct mgmt_irk_info irk;
+} __packed;
+
+struct mgmt_csrk_info {
+ struct mgmt_addr_info addr;
+ __u8 master;
+ __u8 val[16];
+} __packed;
+
+#define MGMT_EV_NEW_CSRK 0x0019
+struct mgmt_ev_new_csrk {
+ __u8 store_hint;
+ struct mgmt_csrk_info key;
+} __packed;
diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h
index 486213a1aed8..2611cc389d7d 100644
--- a/include/net/bluetooth/rfcomm.h
+++ b/include/net/bluetooth/rfcomm.h
@@ -238,9 +238,11 @@ int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
u8 channel);
int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason);
int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb);
+void rfcomm_dlc_send_noerror(struct rfcomm_dlc *d, struct sk_buff *skb);
int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig);
int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig);
void rfcomm_dlc_accept(struct rfcomm_dlc *d);
+struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel);
#define rfcomm_dlc_lock(d) spin_lock(&d->lock)
#define rfcomm_dlc_unlock(d) spin_unlock(&d->lock)
@@ -295,6 +297,7 @@ struct rfcomm_conninfo {
#define RFCOMM_LM_TRUSTED 0x0008
#define RFCOMM_LM_RELIABLE 0x0010
#define RFCOMM_LM_SECURE 0x0020
+#define RFCOMM_LM_FIPS 0x0040
#define rfcomm_pi(sk) ((struct rfcomm_pinfo *) sk)
@@ -323,11 +326,16 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel,
#define RFCOMMGETDEVINFO _IOR('R', 211, int)
#define RFCOMMSTEALDLC _IOW('R', 220, int)
+/* rfcomm_dev.flags bit definitions */
#define RFCOMM_REUSE_DLC 0
#define RFCOMM_RELEASE_ONHUP 1
#define RFCOMM_HANGUP_NOW 2
#define RFCOMM_TTY_ATTACHED 3
-#define RFCOMM_TTY_RELEASED 4
+#define RFCOMM_DEFUNCT_BIT4 4 /* don't reuse this bit - userspace visible */
+
+/* rfcomm_dev.status bit definitions */
+#define RFCOMM_DEV_RELEASED 0
+#define RFCOMM_TTY_OWNED 1
struct rfcomm_dev_req {
s16 dev_id;
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index b1f84b05c67e..f3539a15c411 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -151,6 +151,7 @@ enum ieee80211_channel_flags {
* @dfs_state: current state of this channel. Only relevant if radar is required
* on this channel.
* @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
+ * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
*/
struct ieee80211_channel {
enum ieee80211_band band;
@@ -165,6 +166,7 @@ struct ieee80211_channel {
int orig_mag, orig_mpwr;
enum nl80211_dfs_state dfs_state;
unsigned long dfs_state_entered;
+ unsigned int dfs_cac_ms;
};
/**
@@ -1394,10 +1396,12 @@ struct cfg80211_scan_request {
/**
* struct cfg80211_match_set - sets of attributes to match
*
- * @ssid: SSID to be matched
+ * @ssid: SSID to be matched; may be zero-length for no match (RSSI only)
+ * @rssi_thold: don't report scan results below this threshold (in s32 dBm)
*/
struct cfg80211_match_set {
struct cfg80211_ssid ssid;
+ s32 rssi_thold;
};
/**
@@ -1420,7 +1424,8 @@ struct cfg80211_match_set {
* @dev: the interface
* @scan_start: start time of the scheduled scan
* @channels: channels to scan
- * @rssi_thold: don't report scan results below this threshold (in s32 dBm)
+ * @min_rssi_thold: for drivers only supporting a single threshold, this
+ * contains the minimum over all matchsets
*/
struct cfg80211_sched_scan_request {
struct cfg80211_ssid *ssids;
@@ -1433,7 +1438,7 @@ struct cfg80211_sched_scan_request {
u32 flags;
struct cfg80211_match_set *match_sets;
int n_match_sets;
- s32 rssi_thold;
+ s32 min_rssi_thold;
/* internal */
struct wiphy *wiphy;
@@ -1701,8 +1706,14 @@ struct cfg80211_ibss_params {
*
* @channel: The channel to use or %NULL if not specified (auto-select based
* on scan results)
+ * @channel_hint: The channel of the recommended BSS for initial connection or
+ * %NULL if not specified
* @bssid: The AP BSSID or %NULL if not specified (auto-select based on scan
* results)
+ * @bssid_hint: The recommended AP BSSID for initial connection to the BSS or
+ * %NULL if not specified. Unlike the @bssid parameter, the driver is
+ * allowed to ignore this @bssid_hint if it has knowledge of a better BSS
+ * to use.
* @ssid: SSID
* @ssid_len: Length of ssid in octets
* @auth_type: Authentication type (algorithm)
@@ -1725,11 +1736,13 @@ struct cfg80211_ibss_params {
*/
struct cfg80211_connect_params {
struct ieee80211_channel *channel;
- u8 *bssid;
- u8 *ssid;
+ struct ieee80211_channel *channel_hint;
+ const u8 *bssid;
+ const u8 *bssid_hint;
+ const u8 *ssid;
size_t ssid_len;
enum nl80211_auth_type auth_type;
- u8 *ie;
+ const u8 *ie;
size_t ie_len;
bool privacy;
enum nl80211_mfp mfp;
@@ -1768,6 +1781,7 @@ struct cfg80211_bitrate_mask {
u32 legacy;
u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
u16 vht_mcs[NL80211_VHT_NSS_MAX];
+ enum nl80211_txrate_gi gi;
} control[IEEE80211_NUM_BANDS];
};
/**
@@ -2194,7 +2208,12 @@ struct cfg80211_qos_map {
* @set_cqm_txe_config: Configure connection quality monitor TX error
* thresholds.
* @sched_scan_start: Tell the driver to start a scheduled scan.
- * @sched_scan_stop: Tell the driver to stop an ongoing scheduled scan.
+ * @sched_scan_stop: Tell the driver to stop an ongoing scheduled scan. This
+ * call must stop the scheduled scan and be ready for starting a new one
+ * before it returns, i.e. @sched_scan_start may be called immediately
+ * after that again and should not fail in that case. The driver should
+ * not call cfg80211_sched_scan_stopped() for a requested stop (when this
+ * method returns 0.)
*
* @mgmt_frame_register: Notify driver that a management frame type was
* registered. Note that this callback may not sleep, and cannot run
@@ -2453,7 +2472,8 @@ struct cfg80211_ops {
int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev,
u8 *peer, u8 action_code, u8 dialog_token,
- u16 status_code, const u8 *buf, size_t len);
+ u16 status_code, u32 peer_capability,
+ const u8 *buf, size_t len);
int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
u8 *peer, enum nl80211_tdls_operation oper);
@@ -2485,7 +2505,8 @@ struct cfg80211_ops {
int (*start_radar_detection)(struct wiphy *wiphy,
struct net_device *dev,
- struct cfg80211_chan_def *chandef);
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms);
int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_update_ft_ies_params *ftie);
int (*crit_proto_start)(struct wiphy *wiphy,
@@ -2598,9 +2619,12 @@ struct ieee80211_iface_limit {
* only in special cases.
* @radar_detect_widths: bitmap of channel widths supported for radar detection
*
- * These examples can be expressed as follows:
+ * With this structure the driver can describe which interface
+ * combinations it supports concurrently.
+ *
+ * Examples:
*
- * Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total:
+ * 1. Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total:
*
* struct ieee80211_iface_limit limits1[] = {
* { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
@@ -2614,7 +2638,7 @@ struct ieee80211_iface_limit {
* };
*
*
- * Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total:
+ * 2. Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total:
*
* struct ieee80211_iface_limit limits2[] = {
* { .max = 8, .types = BIT(NL80211_IFTYPE_AP) |
@@ -2628,7 +2652,8 @@ struct ieee80211_iface_limit {
* };
*
*
- * Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total.
+ * 3. Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total.
+ *
* This allows for an infrastructure connection and three P2P connections.
*
* struct ieee80211_iface_limit limits3[] = {
@@ -2778,7 +2803,7 @@ struct wiphy_vendor_command {
* @perm_addr: permanent MAC address of this device
* @addr_mask: If the device supports multiple MAC addresses by masking,
* set this to a mask with variable bits set to 1, e.g. if the last
- * four bits are variable then set it to 00:...:00:0f. The actual
+ * four bits are variable then set it to 00-00-00-00-00-0f. The actual
* variable bits shall be determined by the interfaces added, with
* interfaces not matching the mask being rejected to be brought up.
* @n_addresses: number of addresses in @addresses.
@@ -2875,6 +2900,11 @@ struct wiphy_vendor_command {
* @n_vendor_commands: number of vendor commands
* @vendor_events: array of vendor events supported by the hardware
* @n_vendor_events: number of vendor events
+ *
+ * @max_ap_assoc_sta: maximum number of associated stations supported in AP mode
+ * (including P2P GO) or 0 to indicate no such limit is advertised. The
+ * driver is allowed to advertise a theoretical limit that it can reach in
+ * some cases, but may not always reach.
*/
struct wiphy {
/* assign these fields before you register the wiphy */
@@ -2990,6 +3020,8 @@ struct wiphy {
const struct nl80211_vendor_cmd_info *vendor_events;
int n_vendor_commands, n_vendor_events;
+ u16 max_ap_assoc_sta;
+
char priv[0] __aligned(NETDEV_ALIGN);
};
@@ -3127,8 +3159,8 @@ struct cfg80211_cached_keys;
* @identifier: (private) Identifier used in nl80211 to identify this
* wireless device if it has no netdev
* @current_bss: (private) Used by the internal configuration code
- * @channel: (private) Used by the internal configuration code to track
- * the user-set AP, monitor and WDS channel
+ * @chandef: (private) Used by the internal configuration code to track
+ * the user-set channel definition.
* @preset_chandef: (private) Used by the internal configuration code to
* track the channel to be used for AP later
* @bssid: (private) Used by the internal configuration code
@@ -3151,6 +3183,7 @@ struct cfg80211_cached_keys;
* @p2p_started: true if this is a P2P Device that has been started
* @cac_started: true if DFS channel availability check has been started
* @cac_start_time: timestamp (jiffies) when the dfs state was entered.
+ * @cac_time_ms: CAC time in ms
* @ps: powersave mode is enabled
* @ps_timeout: dynamic powersave timeout
* @ap_unexpected_nlportid: (private) netlink port ID of application
@@ -3192,9 +3225,7 @@ struct wireless_dev {
struct cfg80211_internal_bss *current_bss; /* associated / joined */
struct cfg80211_chan_def preset_chandef;
-
- /* for AP and mesh channel tracking */
- struct ieee80211_channel *channel;
+ struct cfg80211_chan_def chandef;
bool ibss_fixed;
bool ibss_dfs_possible;
@@ -3208,6 +3239,7 @@ struct wireless_dev {
bool cac_started;
unsigned long cac_start_time;
+ unsigned int cac_time_ms;
#ifdef CONFIG_CFG80211_WEXT
/* wext data */
@@ -3640,7 +3672,7 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
* cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame
*
* @wiphy: the wiphy reporting the BSS
- * @channel: The channel the frame was received on
+ * @rx_channel: The channel the frame was received on
* @scan_width: width of the control channel
* @mgmt: the management frame (probe response or beacon)
* @len: length of the management frame
@@ -3655,18 +3687,18 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
*/
struct cfg80211_bss * __must_check
cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
- struct ieee80211_channel *channel,
+ struct ieee80211_channel *rx_channel,
enum nl80211_bss_scan_width scan_width,
struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp);
static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss_frame(struct wiphy *wiphy,
- struct ieee80211_channel *channel,
+ struct ieee80211_channel *rx_channel,
struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp)
{
- return cfg80211_inform_bss_width_frame(wiphy, channel,
+ return cfg80211_inform_bss_width_frame(wiphy, rx_channel,
NL80211_BSS_CHAN_WIDTH_20,
mgmt, len, signal, gfp);
}
@@ -3675,7 +3707,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
* cfg80211_inform_bss - inform cfg80211 of a new BSS
*
* @wiphy: the wiphy reporting the BSS
- * @channel: The channel the frame was received on
+ * @rx_channel: The channel the frame was received on
* @scan_width: width of the control channel
* @bssid: the BSSID of the BSS
* @tsf: the TSF sent by the peer in the beacon/probe response (or 0)
@@ -3694,7 +3726,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
*/
struct cfg80211_bss * __must_check
cfg80211_inform_bss_width(struct wiphy *wiphy,
- struct ieee80211_channel *channel,
+ struct ieee80211_channel *rx_channel,
enum nl80211_bss_scan_width scan_width,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
@@ -3702,12 +3734,12 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss(struct wiphy *wiphy,
- struct ieee80211_channel *channel,
+ struct ieee80211_channel *rx_channel,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp)
{
- return cfg80211_inform_bss_width(wiphy, channel,
+ return cfg80211_inform_bss_width(wiphy, rx_channel,
NL80211_BSS_CHAN_WIDTH_20,
bssid, tsf, capability,
beacon_interval, ie, ielen, signal,
@@ -3876,6 +3908,7 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
*
* @dev: network device
* @bssid: the BSSID of the IBSS joined
+ * @channel: the channel of the IBSS joined
* @gfp: allocation flags
*
* This function notifies cfg80211 that the device joined an IBSS or
@@ -3885,7 +3918,8 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
* with the locally generated beacon -- this guarantees that there is
* always a scan result for this IBSS. cfg80211 will handle the rest.
*/
-void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp);
+void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+ struct ieee80211_channel *channel, gfp_t gfp);
/**
* cfg80211_notify_new_candidate - notify cfg80211 of a new mesh peer candidate
diff --git a/include/net/checksum.h b/include/net/checksum.h
index 37a0e24adbe7..a28f4e0f6251 100644
--- a/include/net/checksum.h
+++ b/include/net/checksum.h
@@ -69,6 +69,19 @@ static inline __wsum csum_sub(__wsum csum, __wsum addend)
return csum_add(csum, ~addend);
}
+static inline __sum16 csum16_add(__sum16 csum, __be16 addend)
+{
+ u16 res = (__force u16)csum;
+
+ res += (__force u16)addend;
+ return (__force __sum16)(res + (res < (__force u16)addend));
+}
+
+static inline __sum16 csum16_sub(__sum16 csum, __be16 addend)
+{
+ return csum16_add(csum, ~addend);
+}
+
static inline __wsum
csum_block_add(__wsum csum, __wsum csum2, int offset)
{
@@ -112,9 +125,15 @@ static inline void csum_replace4(__sum16 *sum, __be32 from, __be32 to)
*sum = csum_fold(csum_partial(diff, sizeof(diff), ~csum_unfold(*sum)));
}
-static inline void csum_replace2(__sum16 *sum, __be16 from, __be16 to)
+/* Implements RFC 1624 (Incremental Internet Checksum)
+ * 3. Discussion states :
+ * HC' = ~(~HC + ~m + m')
+ * m : old value of a 16bit field
+ * m' : new value of a 16bit field
+ */
+static inline void csum_replace2(__sum16 *sum, __be16 old, __be16 new)
{
- csum_replace4(sum, (__force __be32)from, (__force __be32)to);
+ *sum = ~csum16_add(csum16_sub(~(*sum), old), new);
}
struct sk_buff;
diff --git a/include/net/cls_cgroup.h b/include/net/cls_cgroup.h
index 9cf2d5ef38d9..c15d39456e14 100644
--- a/include/net/cls_cgroup.h
+++ b/include/net/cls_cgroup.h
@@ -34,7 +34,7 @@ static inline u32 task_cls_classid(struct task_struct *p)
return 0;
rcu_read_lock();
- classid = container_of(task_css(p, net_cls_subsys_id),
+ classid = container_of(task_css(p, net_cls_cgrp_id),
struct cgroup_cls_state, css)->classid;
rcu_read_unlock();
diff --git a/include/net/dst.h b/include/net/dst.h
index 77eb53fabfb0..46ed958e0c6e 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -54,10 +54,9 @@ struct dst_entry {
#define DST_NOHASH 0x0008
#define DST_NOCACHE 0x0010
#define DST_NOCOUNT 0x0020
-#define DST_NOPEER 0x0040
-#define DST_FAKE_RTABLE 0x0080
-#define DST_XFRM_TUNNEL 0x0100
-#define DST_XFRM_QUEUE 0x0200
+#define DST_FAKE_RTABLE 0x0040
+#define DST_XFRM_TUNNEL 0x0080
+#define DST_XFRM_QUEUE 0x0100
unsigned short pending_confirm;
@@ -109,9 +108,11 @@ struct dst_entry {
u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old);
extern const u32 dst_default_metrics[];
-#define DST_METRICS_READ_ONLY 0x1UL
+#define DST_METRICS_READ_ONLY 0x1UL
+#define DST_METRICS_FORCE_OVERWRITE 0x2UL
+#define DST_METRICS_FLAGS 0x3UL
#define __DST_METRICS_PTR(Y) \
- ((u32 *)((Y) & ~DST_METRICS_READ_ONLY))
+ ((u32 *)((Y) & ~DST_METRICS_FLAGS))
#define DST_METRICS_PTR(X) __DST_METRICS_PTR((X)->_metrics)
static inline bool dst_metrics_read_only(const struct dst_entry *dst)
@@ -119,6 +120,11 @@ static inline bool dst_metrics_read_only(const struct dst_entry *dst)
return dst->_metrics & DST_METRICS_READ_ONLY;
}
+static inline void dst_metrics_set_force_overwrite(struct dst_entry *dst)
+{
+ dst->_metrics |= DST_METRICS_FORCE_OVERWRITE;
+}
+
void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old);
static inline void dst_destroy_metrics_generic(struct dst_entry *dst)
diff --git a/include/net/flow.h b/include/net/flow.h
index d23e7fa2042e..64fd24836650 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -218,9 +218,11 @@ struct flow_cache_object *flow_cache_lookup(struct net *net,
const struct flowi *key, u16 family,
u8 dir, flow_resolve_t resolver,
void *ctx);
+int flow_cache_init(struct net *net);
+void flow_cache_fini(struct net *net);
-void flow_cache_flush(void);
-void flow_cache_flush_deferred(void);
+void flow_cache_flush(struct net *net);
+void flow_cache_flush_deferred(struct net *net);
extern atomic_t flow_cache_genid;
#endif
diff --git a/include/net/flowcache.h b/include/net/flowcache.h
new file mode 100644
index 000000000000..c8f665ec6e0d
--- /dev/null
+++ b/include/net/flowcache.h
@@ -0,0 +1,25 @@
+#ifndef _NET_FLOWCACHE_H
+#define _NET_FLOWCACHE_H
+
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/notifier.h>
+
+struct flow_cache_percpu {
+ struct hlist_head *hash_table;
+ int hash_count;
+ u32 hash_rnd;
+ int hash_rnd_recalc;
+ struct tasklet_struct flush_tasklet;
+};
+
+struct flow_cache {
+ u32 hash_shift;
+ struct flow_cache_percpu __percpu *percpu;
+ struct notifier_block hotcpu_notifier;
+ int low_watermark;
+ int high_watermark;
+ struct timer_list rnd_timer;
+};
+#endif /* _NET_FLOWCACHE_H */
diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h
index 8b5b71433297..b0fd9476c538 100644
--- a/include/net/ieee80211_radiotap.h
+++ b/include/net/ieee80211_radiotap.h
@@ -316,6 +316,10 @@ enum ieee80211_radiotap_type {
#define IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM 0x10
#define IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED 0x20
+#define IEEE80211_RADIOTAP_CODING_LDPC_USER0 0x01
+#define IEEE80211_RADIOTAP_CODING_LDPC_USER1 0x02
+#define IEEE80211_RADIOTAP_CODING_LDPC_USER2 0x04
+#define IEEE80211_RADIOTAP_CODING_LDPC_USER3 0x08
/* helpers */
static inline int ieee80211_get_radiotap_len(unsigned char *data)
diff --git a/include/net/ieee802154.h b/include/net/ieee802154.h
index ee59f8b188dd..c7ae0ac528dc 100644
--- a/include/net/ieee802154.h
+++ b/include/net/ieee802154.h
@@ -42,22 +42,42 @@
(((x) << IEEE802154_FC_TYPE_SHIFT) & IEEE802154_FC_TYPE_MASK)); \
} while (0)
-#define IEEE802154_FC_SECEN (1 << 3)
-#define IEEE802154_FC_FRPEND (1 << 4)
-#define IEEE802154_FC_ACK_REQ (1 << 5)
-#define IEEE802154_FC_INTRA_PAN (1 << 6)
+#define IEEE802154_FC_SECEN_SHIFT 3
+#define IEEE802154_FC_SECEN (1 << IEEE802154_FC_SECEN_SHIFT)
+#define IEEE802154_FC_FRPEND_SHIFT 4
+#define IEEE802154_FC_FRPEND (1 << IEEE802154_FC_FRPEND_SHIFT)
+#define IEEE802154_FC_ACK_REQ_SHIFT 5
+#define IEEE802154_FC_ACK_REQ (1 << IEEE802154_FC_ACK_REQ_SHIFT)
+#define IEEE802154_FC_INTRA_PAN_SHIFT 6
+#define IEEE802154_FC_INTRA_PAN (1 << IEEE802154_FC_INTRA_PAN_SHIFT)
#define IEEE802154_FC_SAMODE_SHIFT 14
#define IEEE802154_FC_SAMODE_MASK (3 << IEEE802154_FC_SAMODE_SHIFT)
#define IEEE802154_FC_DAMODE_SHIFT 10
#define IEEE802154_FC_DAMODE_MASK (3 << IEEE802154_FC_DAMODE_SHIFT)
+#define IEEE802154_FC_VERSION_SHIFT 12
+#define IEEE802154_FC_VERSION_MASK (3 << IEEE802154_FC_VERSION_SHIFT)
+#define IEEE802154_FC_VERSION(x) ((x & IEEE802154_FC_VERSION_MASK) >> IEEE802154_FC_VERSION_SHIFT)
+
#define IEEE802154_FC_SAMODE(x) \
(((x) & IEEE802154_FC_SAMODE_MASK) >> IEEE802154_FC_SAMODE_SHIFT)
#define IEEE802154_FC_DAMODE(x) \
(((x) & IEEE802154_FC_DAMODE_MASK) >> IEEE802154_FC_DAMODE_SHIFT)
+#define IEEE802154_SCF_SECLEVEL_MASK 7
+#define IEEE802154_SCF_SECLEVEL_SHIFT 0
+#define IEEE802154_SCF_SECLEVEL(x) (x & IEEE802154_SCF_SECLEVEL_MASK)
+#define IEEE802154_SCF_KEY_ID_MODE_SHIFT 3
+#define IEEE802154_SCF_KEY_ID_MODE_MASK (3 << IEEE802154_SCF_KEY_ID_MODE_SHIFT)
+#define IEEE802154_SCF_KEY_ID_MODE(x) \
+ ((x & IEEE802154_SCF_KEY_ID_MODE_MASK) >> IEEE802154_SCF_KEY_ID_MODE_SHIFT)
+
+#define IEEE802154_SCF_KEY_IMPLICIT 0
+#define IEEE802154_SCF_KEY_INDEX 1
+#define IEEE802154_SCF_KEY_SHORT_INDEX 2
+#define IEEE802154_SCF_KEY_HW_INDEX 3
/* MAC footer size */
#define IEEE802154_MFR_SIZE 2 /* 2 octets */
diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h
index 8196d5d40359..5a719ca892f4 100644
--- a/include/net/ieee802154_netdev.h
+++ b/include/net/ieee802154_netdev.h
@@ -28,6 +28,164 @@
#define IEEE802154_NETDEVICE_H
#include <net/af_ieee802154.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+struct ieee802154_sechdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ u8 level:3,
+ key_id_mode:2,
+ reserved:3;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ u8 reserved:3,
+ key_id_mode:2,
+ level:3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ u8 key_id;
+ __le32 frame_counter;
+ union {
+ __le32 short_src;
+ __le64 extended_src;
+ };
+};
+
+struct ieee802154_addr {
+ u8 mode;
+ __le16 pan_id;
+ union {
+ __le16 short_addr;
+ __le64 extended_addr;
+ };
+};
+
+struct ieee802154_hdr_fc {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ u16 type:3,
+ security_enabled:1,
+ frame_pending:1,
+ ack_request:1,
+ intra_pan:1,
+ reserved:3,
+ dest_addr_mode:2,
+ version:2,
+ source_addr_mode:2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ u16 reserved:1,
+ intra_pan:1,
+ ack_request:1,
+ frame_pending:1,
+ security_enabled:1,
+ type:3,
+ source_addr_mode:2,
+ version:2,
+ dest_addr_mode:2,
+ reserved2:2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+};
+
+struct ieee802154_hdr {
+ struct ieee802154_hdr_fc fc;
+ u8 seq;
+ struct ieee802154_addr source;
+ struct ieee802154_addr dest;
+ struct ieee802154_sechdr sec;
+};
+
+/* pushes hdr onto the skb. fields of hdr->fc that can be calculated from
+ * the contents of hdr will be, and the actual value of those bits in
+ * hdr->fc will be ignored. this includes the INTRA_PAN bit and the frame
+ * version, if SECEN is set.
+ */
+int ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr);
+
+/* pulls the entire 802.15.4 header off of the skb, including the security
+ * header, and performs pan id decompression
+ */
+int ieee802154_hdr_pull(struct sk_buff *skb, struct ieee802154_hdr *hdr);
+
+/* parses the frame control, sequence number of address fields in a given skb
+ * and stores them into hdr, performing pan id decompression and length checks
+ * to be suitable for use in header_ops.parse
+ */
+int ieee802154_hdr_peek_addrs(const struct sk_buff *skb,
+ struct ieee802154_hdr *hdr);
+
+static inline int ieee802154_hdr_length(struct sk_buff *skb)
+{
+ struct ieee802154_hdr hdr;
+ int len = ieee802154_hdr_pull(skb, &hdr);
+
+ if (len > 0)
+ skb_push(skb, len);
+
+ return len;
+}
+
+static inline bool ieee802154_addr_equal(const struct ieee802154_addr *a1,
+ const struct ieee802154_addr *a2)
+{
+ if (a1->pan_id != a2->pan_id || a1->mode != a2->mode)
+ return false;
+
+ if ((a1->mode == IEEE802154_ADDR_LONG &&
+ a1->extended_addr != a2->extended_addr) ||
+ (a1->mode == IEEE802154_ADDR_SHORT &&
+ a1->short_addr != a2->short_addr))
+ return false;
+
+ return true;
+}
+
+static inline __le64 ieee802154_devaddr_from_raw(const void *raw)
+{
+ u64 temp;
+
+ memcpy(&temp, raw, IEEE802154_ADDR_LEN);
+ return (__force __le64)swab64(temp);
+}
+
+static inline void ieee802154_devaddr_to_raw(void *raw, __le64 addr)
+{
+ u64 temp = swab64((__force u64)addr);
+
+ memcpy(raw, &temp, IEEE802154_ADDR_LEN);
+}
+
+static inline void ieee802154_addr_from_sa(struct ieee802154_addr *a,
+ const struct ieee802154_addr_sa *sa)
+{
+ a->mode = sa->addr_type;
+ a->pan_id = cpu_to_le16(sa->pan_id);
+
+ switch (a->mode) {
+ case IEEE802154_ADDR_SHORT:
+ a->short_addr = cpu_to_le16(sa->short_addr);
+ break;
+ case IEEE802154_ADDR_LONG:
+ a->extended_addr = ieee802154_devaddr_from_raw(sa->hwaddr);
+ break;
+ }
+}
+
+static inline void ieee802154_addr_to_sa(struct ieee802154_addr_sa *sa,
+ const struct ieee802154_addr *a)
+{
+ sa->addr_type = a->mode;
+ sa->pan_id = le16_to_cpu(a->pan_id);
+
+ switch (a->mode) {
+ case IEEE802154_ADDR_SHORT:
+ sa->short_addr = le16_to_cpu(a->short_addr);
+ break;
+ case IEEE802154_ADDR_LONG:
+ ieee802154_devaddr_to_raw(sa->hwaddr, a->extended_addr);
+ break;
+ }
+}
/*
* A control block of skb passed between the ARPHRD_IEEE802154 device
@@ -35,10 +193,10 @@
*/
struct ieee802154_mac_cb {
u8 lqi;
- struct ieee802154_addr sa;
- struct ieee802154_addr da;
u8 flags;
u8 seq;
+ struct ieee802154_addr source;
+ struct ieee802154_addr dest;
};
static inline struct ieee802154_mac_cb *mac_cb(struct sk_buff *skb)
@@ -50,23 +208,17 @@ static inline struct ieee802154_mac_cb *mac_cb(struct sk_buff *skb)
#define MAC_CB_FLAG_ACKREQ (1 << 3)
#define MAC_CB_FLAG_SECEN (1 << 4)
-#define MAC_CB_FLAG_INTRAPAN (1 << 5)
-static inline int mac_cb_is_ackreq(struct sk_buff *skb)
+static inline bool mac_cb_is_ackreq(struct sk_buff *skb)
{
return mac_cb(skb)->flags & MAC_CB_FLAG_ACKREQ;
}
-static inline int mac_cb_is_secen(struct sk_buff *skb)
+static inline bool mac_cb_is_secen(struct sk_buff *skb)
{
return mac_cb(skb)->flags & MAC_CB_FLAG_SECEN;
}
-static inline int mac_cb_is_intrapan(struct sk_buff *skb)
-{
- return mac_cb(skb)->flags & MAC_CB_FLAG_INTRAPAN;
-}
-
static inline int mac_cb_type(struct sk_buff *skb)
{
return mac_cb(skb)->flags & MAC_CB_FLAG_TYPEMASK;
@@ -77,6 +229,18 @@ static inline int mac_cb_type(struct sk_buff *skb)
#define IEEE802154_MAC_SCAN_PASSIVE 2
#define IEEE802154_MAC_SCAN_ORPHAN 3
+struct ieee802154_mac_params {
+ s8 transmit_power;
+ u8 min_be;
+ u8 max_be;
+ u8 csma_retries;
+ s8 frame_retries;
+
+ bool lbt;
+ u8 cca_mode;
+ s32 cca_ed_level;
+};
+
struct wpan_phy;
/*
* This should be located at net_device->ml_priv
@@ -92,7 +256,7 @@ struct ieee802154_mlme_ops {
u8 channel, u8 page, u8 cap);
int (*assoc_resp)(struct net_device *dev,
struct ieee802154_addr *addr,
- u16 short_addr, u8 status);
+ __le16 short_addr, u8 status);
int (*disassoc_req)(struct net_device *dev,
struct ieee802154_addr *addr,
u8 reason);
@@ -103,6 +267,11 @@ struct ieee802154_mlme_ops {
int (*scan_req)(struct net_device *dev,
u8 type, u32 channels, u8 page, u8 duration);
+ int (*set_mac_params)(struct net_device *dev,
+ const struct ieee802154_mac_params *params);
+ void (*get_mac_params)(struct net_device *dev,
+ struct ieee802154_mac_params *params);
+
/* The fields below are required. */
struct wpan_phy *(*get_phy)(const struct net_device *dev);
@@ -111,8 +280,8 @@ struct ieee802154_mlme_ops {
* FIXME: these should become the part of PIB/MIB interface.
* However we still don't have IB interface of any kind
*/
- u16 (*get_pan_id)(const struct net_device *dev);
- u16 (*get_short_addr)(const struct net_device *dev);
+ __le16 (*get_pan_id)(const struct net_device *dev);
+ __le16 (*get_short_addr)(const struct net_device *dev);
u8 (*get_dsn)(const struct net_device *dev);
};
diff --git a/include/net/ip.h b/include/net/ip.h
index 23be0fd37937..25064c28e059 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -187,6 +187,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
#define NET_INC_STATS(net, field) SNMP_INC_STATS((net)->mib.net_statistics, field)
#define NET_INC_STATS_BH(net, field) SNMP_INC_STATS_BH((net)->mib.net_statistics, field)
#define NET_INC_STATS_USER(net, field) SNMP_INC_STATS_USER((net)->mib.net_statistics, field)
+#define NET_ADD_STATS(net, field, adnd) SNMP_ADD_STATS((net)->mib.net_statistics, field, adnd)
#define NET_ADD_STATS_BH(net, field, adnd) SNMP_ADD_STATS_BH((net)->mib.net_statistics, field, adnd)
#define NET_ADD_STATS_USER(net, field, adnd) SNMP_ADD_STATS_USER((net)->mib.net_statistics, field, adnd)
@@ -266,7 +267,8 @@ int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
static inline bool ip_sk_accept_pmtu(const struct sock *sk)
{
- return inet_sk(sk)->pmtudisc != IP_PMTUDISC_INTERFACE;
+ return inet_sk(sk)->pmtudisc != IP_PMTUDISC_INTERFACE &&
+ inet_sk(sk)->pmtudisc != IP_PMTUDISC_OMIT;
}
static inline bool ip_sk_use_pmtu(const struct sock *sk)
@@ -274,6 +276,12 @@ static inline bool ip_sk_use_pmtu(const struct sock *sk)
return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE;
}
+static inline bool ip_sk_local_df(const struct sock *sk)
+{
+ return inet_sk(sk)->pmtudisc < IP_PMTUDISC_DO ||
+ inet_sk(sk)->pmtudisc == IP_PMTUDISC_OMIT;
+}
+
static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
bool forwarding)
{
@@ -489,7 +497,8 @@ int ip_options_rcv_srr(struct sk_buff *skb);
void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb);
void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb);
-int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc);
+int ip_cmsg_send(struct net *net, struct msghdr *msg,
+ struct ipcm_cookie *ipc, bool allow_ipv6);
int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
unsigned int optlen);
int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index aca0c2709fd6..9bcb220bd4ad 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -284,7 +284,8 @@ struct fib6_node *fib6_locate(struct fib6_node *root,
void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg),
void *arg);
-int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info);
+int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info,
+ struct nlattr *mx, int mx_len);
int fib6_del(struct rt6_info *rt, struct nl_info *info);
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 017badb1aec7..3c3bb184eb8f 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -51,6 +51,11 @@ static inline unsigned int rt6_flags2srcprefs(int flags)
return (flags >> 3) & 7;
}
+static inline bool rt6_need_strict(const struct in6_addr *daddr)
+{
+ return ipv6_addr_type(daddr) &
+ (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
+}
void ip6_route_input(struct sk_buff *skb);
@@ -171,7 +176,14 @@ static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
static inline bool ip6_sk_accept_pmtu(const struct sock *sk)
{
- return inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_INTERFACE;
+ return inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_INTERFACE &&
+ inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_OMIT;
+}
+
+static inline bool ip6_sk_local_df(const struct sock *sk)
+{
+ return inet6_sk(sk)->pmtudisc < IPV6_PMTUDISC_DO ||
+ inet6_sk(sk)->pmtudisc == IPV6_PMTUDISC_OMIT;
}
static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index f4ab2fb4d50c..8248e3909fdf 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -66,10 +66,6 @@
*
* Secondly, when the hardware handles fragmentation, the frame handed to
* the driver from mac80211 is the MSDU, not the MPDU.
- *
- * Finally, for received frames, the driver is able to indicate that it has
- * filled a radiotap header and put that in front of the frame; if it does
- * not do so then mac80211 may add this under certain circumstances.
*/
/**
@@ -701,11 +697,11 @@ struct ieee80211_tx_info {
} control;
struct {
struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
- int ack_signal;
+ s32 ack_signal;
u8 ampdu_ack_len;
u8 ampdu_len;
u8 antenna;
- /* 21 bytes free */
+ void *status_driver_data[21 / sizeof(void *)];
} status;
struct {
struct ieee80211_tx_rate driver_rates[
@@ -808,9 +804,6 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index
* @RX_FLAG_VHT: VHT MCS was used and rate_index is MCS index
* @RX_FLAG_40MHZ: HT40 (40 MHz) was used
- * @RX_FLAG_80MHZ: 80 MHz was used
- * @RX_FLAG_80P80MHZ: 80+80 MHz was used
- * @RX_FLAG_160MHZ: 160 MHz was used
* @RX_FLAG_SHORT_GI: Short guard interval was used
* @RX_FLAG_NO_SIGNAL_VAL: The signal strength value is not present.
* Valid only for data frames (mainly A-MPDU)
@@ -830,6 +823,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* on this subframe
* @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
* is stored in the @ampdu_delimiter_crc field)
+ * @RX_FLAG_LDPC: LDPC was used
* @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
* @RX_FLAG_10MHZ: 10 MHz (half channel) was used
* @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
@@ -866,9 +860,7 @@ enum mac80211_rx_flags {
RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(20),
RX_FLAG_MACTIME_END = BIT(21),
RX_FLAG_VHT = BIT(22),
- RX_FLAG_80MHZ = BIT(23),
- RX_FLAG_80P80MHZ = BIT(24),
- RX_FLAG_160MHZ = BIT(25),
+ RX_FLAG_LDPC = BIT(23),
RX_FLAG_STBC_MASK = BIT(26) | BIT(27),
RX_FLAG_10MHZ = BIT(28),
RX_FLAG_5MHZ = BIT(29),
@@ -878,6 +870,23 @@ enum mac80211_rx_flags {
#define RX_FLAG_STBC_SHIFT 26
/**
+ * enum mac80211_rx_vht_flags - receive VHT flags
+ *
+ * These flags are used with the @vht_flag member of
+ * &struct ieee80211_rx_status.
+ * @RX_VHT_FLAG_80MHZ: 80 MHz was used
+ * @RX_VHT_FLAG_80P80MHZ: 80+80 MHz was used
+ * @RX_VHT_FLAG_160MHZ: 160 MHz was used
+ * @RX_VHT_FLAG_BF: packet was beamformed
+ */
+enum mac80211_rx_vht_flags {
+ RX_VHT_FLAG_80MHZ = BIT(0),
+ RX_VHT_FLAG_80P80MHZ = BIT(1),
+ RX_VHT_FLAG_160MHZ = BIT(2),
+ RX_VHT_FLAG_BF = BIT(3),
+};
+
+/**
* struct ieee80211_rx_status - receive status
*
* The low-level driver should provide this information (the subset
@@ -902,26 +911,19 @@ enum mac80211_rx_flags {
* HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT)
* @vht_nss: number of streams (VHT only)
* @flag: %RX_FLAG_*
+ * @vht_flag: %RX_VHT_FLAG_*
* @rx_flags: internal RX flags for mac80211
* @ampdu_reference: A-MPDU reference number, must be a different value for
* each A-MPDU but the same for each subframe within one A-MPDU
* @ampdu_delimiter_crc: A-MPDU delimiter CRC
- * @vendor_radiotap_bitmap: radiotap vendor namespace presence bitmap
- * @vendor_radiotap_len: radiotap vendor namespace length
- * @vendor_radiotap_align: radiotap vendor namespace alignment. Note
- * that the actual data must be at the start of the SKB data
- * already.
- * @vendor_radiotap_oui: radiotap vendor namespace OUI
- * @vendor_radiotap_subns: radiotap vendor sub namespace
*/
struct ieee80211_rx_status {
u64 mactime;
u32 device_timestamp;
u32 ampdu_reference;
u32 flag;
- u32 vendor_radiotap_bitmap;
- u16 vendor_radiotap_len;
u16 freq;
+ u8 vht_flag;
u8 rate_idx;
u8 vht_nss;
u8 rx_flags;
@@ -931,9 +933,6 @@ struct ieee80211_rx_status {
u8 chains;
s8 chain_signal[IEEE80211_MAX_CHAINS];
u8 ampdu_delimiter_crc;
- u8 vendor_radiotap_align;
- u8 vendor_radiotap_oui[3];
- u8 vendor_radiotap_subns;
};
/**
@@ -1506,8 +1505,6 @@ struct ieee80211_tx_control {
* @IEEE80211_HW_CONNECTION_MONITOR:
* The hardware performs its own connection monitoring, including
* periodic keep-alives to the AP and probing the AP on beacon loss.
- * When this flag is set, signaling beacon-loss will cause an immediate
- * change to disassociated state.
*
* @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC:
* This device needs to get data from beacon before association (i.e.
@@ -1643,10 +1640,6 @@ enum ieee80211_hw_flags {
* the hw can report back.
* @max_rate_tries: maximum number of tries for each stage
*
- * @napi_weight: weight used for NAPI polling. You must specify an
- * appropriate value here if a napi_poll operation is provided
- * by your driver.
- *
* @max_rx_aggregation_subframes: maximum buffer size (number of
* sub-frames) to be used for A-MPDU block ack receiver
* aggregation.
@@ -1700,7 +1693,6 @@ struct ieee80211_hw {
int vif_data_size;
int sta_data_size;
int chanctx_data_size;
- int napi_weight;
u16 queues;
u16 max_listen_interval;
s8 max_signal;
@@ -1895,7 +1887,7 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
*
* Driver informs U-APSD client support by enabling
* %IEEE80211_HW_SUPPORTS_UAPSD flag. The mode is configured through the
- * uapsd paramater in conf_tx() operation. Hardware needs to send the QoS
+ * uapsd parameter in conf_tx() operation. Hardware needs to send the QoS
* Nullfunc frames and stay awake until the service period has ended. To
* utilize U-APSD, dynamic powersave is disabled for voip AC and all frames
* from that AC are transmitted with powersave enabled.
@@ -2101,7 +2093,7 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
* with the number of frames to be released and which TIDs they are
* to come from. In this case, the driver is responsible for setting
* the EOSP (for uAPSD) and MORE_DATA bits in the released frames,
- * to help the @more_data paramter is passed to tell the driver if
+ * to help the @more_data parameter is passed to tell the driver if
* there is more data on other TIDs -- the TIDs to release frames
* from are ignored since mac80211 doesn't know how many frames the
* buffers for those TIDs contain.
@@ -2470,6 +2462,7 @@ enum ieee80211_roc_type {
* This process will continue until sched_scan_stop is called.
*
* @sched_scan_stop: Tell the hardware to stop an ongoing scheduled scan.
+ * In this case, ieee80211_sched_scan_stopped() must not be called.
*
* @sw_scan_start: Notifier function that is called just before a software scan
* is started. Can be NULL, if the driver doesn't need this notification.
@@ -2623,8 +2616,6 @@ enum ieee80211_roc_type {
* callback. They must then call ieee80211_chswitch_done() to indicate
* completion of the channel switch.
*
- * @napi_poll: Poll Rx queue for incoming data frames.
- *
* @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device.
* Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may
* reject TX/RX mask combinations they cannot support by returning -EINVAL
@@ -2662,7 +2653,7 @@ enum ieee80211_roc_type {
* parameters. In the case where the driver buffers some frames for
* sleeping stations mac80211 will use this callback to tell the driver
* to release some frames, either for PS-poll or uAPSD.
- * Note that if the @more_data paramter is %false the driver must check
+ * Note that if the @more_data parameter is %false the driver must check
* if there are more frames on the given TIDs, and if there are more than
* the frames being released then it must still set the more-data bit in
* the frame. If the @more_data parameter is %true, then of course the
@@ -2750,11 +2741,13 @@ enum ieee80211_roc_type {
* @channel_switch_beacon: Starts a channel switch to a new channel.
* Beacons are modified to include CSA or ECSA IEs before calling this
* function. The corresponding count fields in these IEs must be
- * decremented, and when they reach zero the driver must call
+ * decremented, and when they reach 1 the driver must call
* ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
* get the csa counter decremented by mac80211, but must check if it is
- * zero using ieee80211_csa_is_complete() after the beacon has been
+ * 1 using ieee80211_csa_is_complete() after the beacon has been
* transmitted and then call ieee80211_csa_finish().
+ * If the CSA count starts as zero or 1, this function will not be called,
+ * since there won't be any time to beacon before the switch anyway.
*
* @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
* information in bss_conf is set up and the beacon can be retrieved. A
@@ -2817,7 +2810,7 @@ struct ieee80211_ops {
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies);
- void (*sched_scan_stop)(struct ieee80211_hw *hw,
+ int (*sched_scan_stop)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
void (*sw_scan_start)(struct ieee80211_hw *hw);
void (*sw_scan_complete)(struct ieee80211_hw *hw);
@@ -2881,7 +2874,6 @@ struct ieee80211_ops {
void (*flush)(struct ieee80211_hw *hw, u32 queues, bool drop);
void (*channel_switch)(struct ieee80211_hw *hw,
struct ieee80211_channel_switch *ch_switch);
- int (*napi_poll)(struct ieee80211_hw *hw, int budget);
int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
@@ -3163,21 +3155,21 @@ void ieee80211_free_hw(struct ieee80211_hw *hw);
*/
void ieee80211_restart_hw(struct ieee80211_hw *hw);
-/** ieee80211_napi_schedule - schedule NAPI poll
- *
- * Use this function to schedule NAPI polling on a device.
- *
- * @hw: the hardware to start polling
- */
-void ieee80211_napi_schedule(struct ieee80211_hw *hw);
-
-/** ieee80211_napi_complete - complete NAPI polling
- *
- * Use this function to finish NAPI polling on a device.
+/**
+ * ieee80211_napi_add - initialize mac80211 NAPI context
+ * @hw: the hardware to initialize the NAPI context on
+ * @napi: the NAPI context to initialize
+ * @napi_dev: dummy NAPI netdevice, here to not waste the space if the
+ * driver doesn't use NAPI
+ * @poll: poll function
+ * @weight: default weight
*
- * @hw: the hardware to stop polling
+ * See also netif_napi_add().
*/
-void ieee80211_napi_complete(struct ieee80211_hw *hw);
+void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
+ struct net_device *napi_dev,
+ int (*poll)(struct napi_struct *, int),
+ int weight);
/**
* ieee80211_rx - receive frame
@@ -3452,13 +3444,13 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
*
* After a channel switch announcement was scheduled and the counter in this
- * announcement hit zero, this function must be called by the driver to
+ * announcement hits 1, this function must be called by the driver to
* notify mac80211 that the channel can be changed.
*/
void ieee80211_csa_finish(struct ieee80211_vif *vif);
/**
- * ieee80211_csa_is_complete - find out if counters reached zero
+ * ieee80211_csa_is_complete - find out if counters reached 1
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
*
* This function returns whether the channel switch counters reached zero.
@@ -4451,7 +4443,6 @@ struct ieee80211_tx_rate_control {
};
struct rate_control_ops {
- struct module *module;
const char *name;
void *(*alloc)(struct ieee80211_hw *hw, struct dentry *debugfsdir);
void (*free)(void *priv);
@@ -4553,8 +4544,8 @@ int rate_control_set_rates(struct ieee80211_hw *hw,
struct ieee80211_sta *pubsta,
struct ieee80211_sta_rates *rates);
-int ieee80211_rate_control_register(struct rate_control_ops *ops);
-void ieee80211_rate_control_unregister(struct rate_control_ops *ops);
+int ieee80211_rate_control_register(const struct rate_control_ops *ops);
+void ieee80211_rate_control_unregister(const struct rate_control_ops *ops);
static inline bool
conf_is_ht20(struct ieee80211_conf *conf)
diff --git a/include/net/mac802154.h b/include/net/mac802154.h
index 807d6b7a943f..a591053cae63 100644
--- a/include/net/mac802154.h
+++ b/include/net/mac802154.h
@@ -20,6 +20,7 @@
#define NET_MAC802154_H
#include <net/af_ieee802154.h>
+#include <linux/skbuff.h>
/* General MAC frame format:
* 2 bytes: Frame Control
@@ -50,7 +51,7 @@ struct ieee802154_hw_addr_filt {
* devices across independent networks.
*/
__le16 short_addr;
- u8 ieee_addr[IEEE802154_ADDR_LEN];
+ __le64 ieee_addr;
u8 pan_coord;
};
@@ -113,6 +114,32 @@ struct ieee802154_dev {
* Set radio for listening on specific address.
* Set the device for listening on specified address.
* Returns either zero, or negative errno.
+ *
+ * set_txpower:
+ * Set radio transmit power in dB. Called with pib_lock held.
+ * Returns either zero, or negative errno.
+ *
+ * set_lbt
+ * Enables or disables listen before talk on the device. Called with
+ * pib_lock held.
+ * Returns either zero, or negative errno.
+ *
+ * set_cca_mode
+ * Sets the CCA mode used by the device. Called with pib_lock held.
+ * Returns either zero, or negative errno.
+ *
+ * set_cca_ed_level
+ * Sets the CCA energy detection threshold in dBm. Called with pib_lock
+ * held.
+ * Returns either zero, or negative errno.
+ *
+ * set_csma_params
+ * Sets the CSMA parameter set for the PHY. Called with pib_lock held.
+ * Returns either zero, or negative errno.
+ *
+ * set_frame_retries
+ * Sets the retransmission attempt limit. Called with pib_lock held.
+ * Returns either zero, or negative errno.
*/
struct ieee802154_ops {
struct module *owner;
@@ -127,8 +154,16 @@ struct ieee802154_ops {
int (*set_hw_addr_filt)(struct ieee802154_dev *dev,
struct ieee802154_hw_addr_filt *filt,
unsigned long changed);
- int (*ieee_addr)(struct ieee802154_dev *dev,
- u8 addr[IEEE802154_ADDR_LEN]);
+ int (*ieee_addr)(struct ieee802154_dev *dev, __le64 addr);
+ int (*set_txpower)(struct ieee802154_dev *dev, int db);
+ int (*set_lbt)(struct ieee802154_dev *dev, bool on);
+ int (*set_cca_mode)(struct ieee802154_dev *dev, u8 mode);
+ int (*set_cca_ed_level)(struct ieee802154_dev *dev,
+ s32 level);
+ int (*set_csma_params)(struct ieee802154_dev *dev,
+ u8 min_be, u8 max_be, u8 retries);
+ int (*set_frame_retries)(struct ieee802154_dev *dev,
+ s8 retries);
};
/* Basic interface to register ieee802154 device */
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 991dcd94cbbf..79387f73f875 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -15,6 +15,7 @@
#include <net/netns/packet.h>
#include <net/netns/ipv4.h>
#include <net/netns/ipv6.h>
+#include <net/netns/ieee802154_6lowpan.h>
#include <net/netns/sctp.h>
#include <net/netns/dccp.h>
#include <net/netns/netfilter.h>
@@ -90,6 +91,9 @@ struct net {
#if IS_ENABLED(CONFIG_IPV6)
struct netns_ipv6 ipv6;
#endif
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+ struct netns_ieee802154_lowpan ieee802154_lowpan;
+#endif
#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)
struct netns_sctp sctp;
#endif
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index b2ac6246b7e0..37252f71a380 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -73,10 +73,17 @@ struct nf_conn_help {
struct nf_conn {
/* Usage count in here is 1 for hash table/destruct timer, 1 per skb,
- plus 1 for any connection(s) we are `master' for */
+ * plus 1 for any connection(s) we are `master' for
+ *
+ * Hint, SKB address this struct and refcnt via skb->nfct and
+ * helpers nf_conntrack_get() and nf_conntrack_put().
+ * Helper nf_ct_put() equals nf_conntrack_put() by dec refcnt,
+ * beware nf_ct_get() is different and don't inc refcnt.
+ */
struct nf_conntrack ct_general;
- spinlock_t lock;
+ spinlock_t lock;
+ u16 cpu;
/* XXX should I move this to the tail ? - Y.K */
/* These are my tuples; original and reply */
diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h
index 15308b8eb5b5..cc0c18827602 100644
--- a/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -77,6 +77,13 @@ print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_l3proto *l3proto,
const struct nf_conntrack_l4proto *proto);
-extern spinlock_t nf_conntrack_lock ;
+#ifdef CONFIG_LOCKDEP
+# define CONNTRACK_LOCKS 8
+#else
+# define CONNTRACK_LOCKS 1024
+#endif
+extern spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS];
+
+extern spinlock_t nf_conntrack_expect_lock;
#endif /* _NF_CONNTRACK_CORE_H */
diff --git a/include/net/netfilter/nf_conntrack_labels.h b/include/net/netfilter/nf_conntrack_labels.h
index c985695283b3..dec6336bf850 100644
--- a/include/net/netfilter/nf_conntrack_labels.h
+++ b/include/net/netfilter/nf_conntrack_labels.h
@@ -7,6 +7,8 @@
#include <uapi/linux/netfilter/xt_connlabel.h>
+#define NF_CT_LABELS_MAX_SIZE ((XT_CONNLABEL_MAXBIT + 1) / BITS_PER_BYTE)
+
struct nf_conn_labels {
u8 words;
unsigned long bits[];
@@ -29,7 +31,7 @@ static inline struct nf_conn_labels *nf_ct_labels_ext_add(struct nf_conn *ct)
u8 words;
words = ACCESS_ONCE(net->ct.label_words);
- if (words == 0 || WARN_ON_ONCE(words > 8))
+ if (words == 0)
return NULL;
cl_ext = nf_ct_ext_add_length(ct, NF_CT_EXT_LABELS,
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index e7e14ffe0f6a..e6bc14d8fa9a 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -3,6 +3,7 @@
#include <linux/list.h>
#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netlink.h>
@@ -288,7 +289,8 @@ struct nft_expr_ops {
int (*init)(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[]);
- void (*destroy)(const struct nft_expr *expr);
+ void (*destroy)(const struct nft_ctx *ctx,
+ const struct nft_expr *expr);
int (*dump)(struct sk_buff *skb,
const struct nft_expr *expr);
int (*validate)(const struct nft_ctx *ctx,
@@ -325,13 +327,15 @@ static inline void *nft_expr_priv(const struct nft_expr *expr)
* @handle: rule handle
* @genmask: generation mask
* @dlen: length of expression data
+ * @ulen: length of user data (used for comments)
* @data: expression data
*/
struct nft_rule {
struct list_head list;
- u64 handle:46,
+ u64 handle:42,
genmask:2,
- dlen:16;
+ dlen:12,
+ ulen:8;
unsigned char data[]
__attribute__((aligned(__alignof__(struct nft_expr))));
};
@@ -340,19 +344,13 @@ struct nft_rule {
* struct nft_rule_trans - nf_tables rule update in transaction
*
* @list: used internally
+ * @ctx: rule context
* @rule: rule that needs to be updated
- * @chain: chain that this rule belongs to
- * @table: table for which this chain applies
- * @nlh: netlink header of the message that contain this update
- * @family: family expressesed as AF_*
*/
struct nft_rule_trans {
struct list_head list;
+ struct nft_ctx ctx;
struct nft_rule *rule;
- const struct nft_chain *chain;
- const struct nft_table *table;
- const struct nlmsghdr *nlh;
- u8 family;
};
static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
@@ -370,6 +368,11 @@ static inline struct nft_expr *nft_expr_last(const struct nft_rule *rule)
return (struct nft_expr *)&rule->data[rule->dlen];
}
+static inline void *nft_userdata(const struct nft_rule *rule)
+{
+ return (void *)&rule->data[rule->dlen];
+}
+
/*
* The last pointer isn't really necessary, but the compiler isn't able to
* determine that the result of nft_expr_last() is always the same since it
@@ -521,6 +524,9 @@ 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 *);
+#define nft_dereference(p) \
+ nfnl_dereference(p, NFNL_SUBSYS_NFTABLES)
+
#define MODULE_ALIAS_NFT_FAMILY(family) \
MODULE_ALIAS("nft-afinfo-" __stringify(family))
diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h
index fbcc7fa536dc..773cce308bc6 100644
--- a/include/net/netns/conntrack.h
+++ b/include/net/netns/conntrack.h
@@ -5,6 +5,7 @@
#include <linux/list_nulls.h>
#include <linux/atomic.h>
#include <linux/netfilter/nf_conntrack_tcp.h>
+#include <linux/seqlock.h>
struct ctl_table_header;
struct nf_conntrack_ecache;
@@ -62,6 +63,13 @@ struct nf_ip_net {
#endif
};
+struct ct_pcpu {
+ spinlock_t lock;
+ struct hlist_nulls_head unconfirmed;
+ struct hlist_nulls_head dying;
+ struct hlist_nulls_head tmpl;
+};
+
struct netns_ct {
atomic_t count;
unsigned int expect_count;
@@ -83,12 +91,11 @@ struct netns_ct {
int sysctl_checksum;
unsigned int htable_size;
+ seqcount_t generation;
struct kmem_cache *nf_conntrack_cachep;
struct hlist_nulls_head *hash;
struct hlist_head *expect_hash;
- struct hlist_nulls_head unconfirmed;
- struct hlist_nulls_head dying;
- struct hlist_nulls_head tmpl;
+ struct ct_pcpu __percpu *pcpu_lists;
struct ip_conntrack_stat __percpu *stat;
struct nf_ct_event_notifier __rcu *nf_conntrack_event_cb;
struct nf_exp_event_notifier __rcu *nf_expect_event_cb;
diff --git a/include/net/netns/ieee802154_6lowpan.h b/include/net/netns/ieee802154_6lowpan.h
new file mode 100644
index 000000000000..079030c853d8
--- /dev/null
+++ b/include/net/netns/ieee802154_6lowpan.h
@@ -0,0 +1,22 @@
+/*
+ * ieee802154 6lowpan in net namespaces
+ */
+
+#include <net/inet_frag.h>
+
+#ifndef __NETNS_IEEE802154_6LOWPAN_H__
+#define __NETNS_IEEE802154_6LOWPAN_H__
+
+struct netns_sysctl_lowpan {
+#ifdef CONFIG_SYSCTL
+ struct ctl_table_header *frags_hdr;
+#endif
+};
+
+struct netns_ieee802154_lowpan {
+ struct netns_sysctl_lowpan sysctl;
+ struct netns_frags frags;
+ u16 max_dsize;
+};
+
+#endif
diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h
index 1006a265beb3..3492434baf88 100644
--- a/include/net/netns/xfrm.h
+++ b/include/net/netns/xfrm.h
@@ -6,6 +6,7 @@
#include <linux/workqueue.h>
#include <linux/xfrm.h>
#include <net/dst_ops.h>
+#include <net/flowcache.h>
struct ctl_table_header;
@@ -58,9 +59,17 @@ struct netns_xfrm {
struct dst_ops xfrm6_dst_ops;
#endif
spinlock_t xfrm_state_lock;
- spinlock_t xfrm_policy_sk_bundle_lock;
rwlock_t xfrm_policy_lock;
struct mutex xfrm_cfg_mutex;
+
+ /* flow cache part */
+ struct flow_cache flow_cache_global;
+ atomic_t flow_cache_genid;
+ struct list_head flow_cache_gc_list;
+ spinlock_t flow_cache_gc_lock;
+ struct work_struct flow_cache_gc_work;
+ struct work_struct flow_cache_flush_work;
+ struct mutex flow_flush_sem;
};
#endif
diff --git a/include/net/netprio_cgroup.h b/include/net/netprio_cgroup.h
index dafc09f0fdbc..f2a9597ff53c 100644
--- a/include/net/netprio_cgroup.h
+++ b/include/net/netprio_cgroup.h
@@ -27,32 +27,17 @@ struct netprio_map {
void sock_update_netprioidx(struct sock *sk);
-#if IS_BUILTIN(CONFIG_CGROUP_NET_PRIO)
static inline u32 task_netprioidx(struct task_struct *p)
{
struct cgroup_subsys_state *css;
u32 idx;
rcu_read_lock();
- css = task_css(p, net_prio_subsys_id);
+ css = task_css(p, net_prio_cgrp_id);
idx = css->cgroup->id;
rcu_read_unlock();
return idx;
}
-#elif IS_MODULE(CONFIG_CGROUP_NET_PRIO)
-static inline u32 task_netprioidx(struct task_struct *p)
-{
- struct cgroup_subsys_state *css;
- u32 idx = 0;
-
- rcu_read_lock();
- css = task_css(p, net_prio_subsys_id);
- if (css)
- idx = css->cgroup->id;
- rcu_read_unlock();
- return idx;
-}
-#endif
#else /* !CONFIG_CGROUP_NET_PRIO */
static inline u32 task_netprioidx(struct task_struct *p)
{
diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h
index 81af21e9bcd4..7655cfe27c34 100644
--- a/include/net/nfc/digital.h
+++ b/include/net/nfc/digital.h
@@ -35,6 +35,7 @@ enum {
NFC_DIGITAL_RF_TECH_106A = 0,
NFC_DIGITAL_RF_TECH_212F,
NFC_DIGITAL_RF_TECH_424F,
+ NFC_DIGITAL_RF_TECH_ISO15693,
NFC_DIGITAL_RF_TECH_LAST,
};
@@ -50,6 +51,7 @@ enum {
NFC_DIGITAL_FRAMING_NFCA_T1T,
NFC_DIGITAL_FRAMING_NFCA_T2T,
+ NFC_DIGITAL_FRAMING_NFCA_T4T,
NFC_DIGITAL_FRAMING_NFCA_NFC_DEP,
NFC_DIGITAL_FRAMING_NFCF,
@@ -57,6 +59,9 @@ enum {
NFC_DIGITAL_FRAMING_NFCF_NFC_DEP,
NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED,
+ NFC_DIGITAL_FRAMING_ISO15693_INVENTORY,
+ NFC_DIGITAL_FRAMING_ISO15693_T5T,
+
NFC_DIGITAL_FRAMING_LAST,
};
@@ -204,6 +209,8 @@ struct nfc_digital_dev {
u8 curr_rf_tech;
u8 curr_nfc_dep_pni;
+ u16 target_fsc;
+
int (*skb_check_crc)(struct sk_buff *skb);
void (*skb_add_crc)(struct sk_buff *skb);
};
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index e80894bca1d0..2e8b40c16274 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -111,6 +111,9 @@ struct nfc_target {
u8 sensf_res[NFC_SENSF_RES_MAXSIZE];
u8 hci_reader_gate;
u8 logical_idx;
+ u8 is_iso15693;
+ u8 iso15693_dsfid;
+ u8 iso15693_uid[NFC_ISO15693_UID_MAXSIZE];
};
/**
diff --git a/include/net/nl802154.h b/include/net/nl802154.h
index 99d2ba1c7e03..b23548e04098 100644
--- a/include/net/nl802154.h
+++ b/include/net/nl802154.h
@@ -52,7 +52,7 @@ int ieee802154_nl_assoc_indic(struct net_device *dev,
* Note: This is in section 7.3.2 of the IEEE 802.15.4 document.
*/
int ieee802154_nl_assoc_confirm(struct net_device *dev,
- u16 short_addr, u8 status);
+ __le16 short_addr, u8 status);
/**
* ieee802154_nl_disassoc_indic - Notify userland of disassociation.
@@ -111,8 +111,8 @@ int ieee802154_nl_scan_confirm(struct net_device *dev,
* Note: This API cannot indicate a beacon frame for a coordinator
* operating in long addressing mode.
*/
-int ieee802154_nl_beacon_indic(struct net_device *dev, u16 panid,
- u16 coord_addr);
+int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid,
+ __le16 coord_addr);
/**
* ieee802154_nl_start_confirm - Notify userland of completion of start.
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index b07cdc9fa454..75fc1f5a948d 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -155,6 +155,7 @@ struct ieee80211_reg_rule {
struct ieee80211_freq_range freq_range;
struct ieee80211_power_rule power_rule;
u32 flags;
+ u32 dfs_cac_ms;
};
struct ieee80211_regdomain {
@@ -172,14 +173,18 @@ struct ieee80211_regdomain {
#define DBM_TO_MBM(gain) ((gain) * 100)
#define MBM_TO_DBM(gain) ((gain) / 100)
-#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
-{ \
- .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \
- .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \
- .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \
- .power_rule.max_antenna_gain = DBI_TO_MBI(gain),\
- .power_rule.max_eirp = DBM_TO_MBM(eirp), \
- .flags = reg_flags, \
+#define REG_RULE_EXT(start, end, bw, gain, eirp, dfs_cac, reg_flags) \
+{ \
+ .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \
+ .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \
+ .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \
+ .power_rule.max_antenna_gain = DBI_TO_MBI(gain), \
+ .power_rule.max_eirp = DBM_TO_MBM(eirp), \
+ .flags = reg_flags, \
+ .dfs_cac_ms = dfs_cac, \
}
+#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
+ REG_RULE_EXT(start, end, bw, gain, eirp, 0, reg_flags)
+
#endif
diff --git a/include/net/route.h b/include/net/route.h
index 9d1f423d5944..b17cf28f996e 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -191,7 +191,6 @@ unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev,
void ip_rt_multicast_event(struct in_device *);
int ip_rt_ioctl(struct net *, unsigned int cmd, void __user *arg);
void ip_rt_get_source(u8 *src, struct sk_buff *skb, struct rtable *rt);
-int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb);
struct in_ifaddr;
void fib_add_ifaddr(struct in_ifaddr *);
diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h
index 661e45d38051..72240e5ac2c4 100644
--- a/include/net/rtnetlink.h
+++ b/include/net/rtnetlink.h
@@ -140,7 +140,7 @@ struct net_device *rtnl_create_link(struct net *net, char *ifname,
struct nlattr *tb[]);
int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm);
-extern const struct nla_policy ifla_policy[IFLA_MAX+1];
+int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len);
#define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind)
diff --git a/include/net/sock.h b/include/net/sock.h
index b9586a137cad..06a5668f05c9 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -862,9 +862,9 @@ static inline void sock_rps_save_rxhash(struct sock *sk,
const struct sk_buff *skb)
{
#ifdef CONFIG_RPS
- if (unlikely(sk->sk_rxhash != skb->rxhash)) {
+ if (unlikely(sk->sk_rxhash != skb->hash)) {
sock_rps_reset_flow(sk);
- sk->sk_rxhash = skb->rxhash;
+ sk->sk_rxhash = skb->hash;
}
#endif
}
@@ -1621,33 +1621,6 @@ void sk_common_release(struct sock *sk);
/* Initialise core socket variables */
void sock_init_data(struct socket *sock, struct sock *sk);
-void sk_filter_release_rcu(struct rcu_head *rcu);
-
-/**
- * sk_filter_release - release a socket filter
- * @fp: filter to remove
- *
- * Remove a filter from a socket and release its resources.
- */
-
-static inline void sk_filter_release(struct sk_filter *fp)
-{
- if (atomic_dec_and_test(&fp->refcnt))
- call_rcu(&fp->rcu, sk_filter_release_rcu);
-}
-
-static inline void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp)
-{
- atomic_sub(sk_filter_size(fp->len), &sk->sk_omem_alloc);
- sk_filter_release(fp);
-}
-
-static inline void sk_filter_charge(struct sock *sk, struct sk_filter *fp)
-{
- atomic_inc(&fp->refcnt);
- atomic_add(sk_filter_size(fp->len), &sk->sk_omem_alloc);
-}
-
/*
* Socket reference counting postulates.
*
@@ -2256,8 +2229,12 @@ void sock_net_set(struct sock *sk, struct net *net)
*/
static inline void sk_change_net(struct sock *sk, struct net *net)
{
- put_net(sock_net(sk));
- sock_net_set(sk, hold_net(net));
+ struct net *current_net = sock_net(sk);
+
+ if (!net_eq(current_net, net)) {
+ put_net(current_net);
+ sock_net_set(sk, hold_net(net));
+ }
}
static inline struct sock *skb_steal_sock(struct sk_buff *skb)
diff --git a/include/net/tc_act/tc_csum.h b/include/net/tc_act/tc_csum.h
index 9e8710be7a04..fa8f5fac65e9 100644
--- a/include/net/tc_act/tc_csum.h
+++ b/include/net/tc_act/tc_csum.h
@@ -9,7 +9,7 @@ struct tcf_csum {
u32 update_flags;
};
-#define to_tcf_csum(pc) \
- container_of(pc,struct tcf_csum,common)
+#define to_tcf_csum(a) \
+ container_of(a->priv,struct tcf_csum,common)
#endif /* __NET_TC_CSUM_H */
diff --git a/include/net/tc_act/tc_defact.h b/include/net/tc_act/tc_defact.h
index 65f024b80958..9763dcbb9bc3 100644
--- a/include/net/tc_act/tc_defact.h
+++ b/include/net/tc_act/tc_defact.h
@@ -8,7 +8,7 @@ struct tcf_defact {
u32 tcfd_datalen;
void *tcfd_defdata;
};
-#define to_defact(pc) \
- container_of(pc, struct tcf_defact, common)
+#define to_defact(a) \
+ container_of(a->priv, struct tcf_defact, common)
#endif /* __NET_TC_DEF_H */
diff --git a/include/net/tc_act/tc_gact.h b/include/net/tc_act/tc_gact.h
index 9e3f6767b80e..9fc9b578908a 100644
--- a/include/net/tc_act/tc_gact.h
+++ b/include/net/tc_act/tc_gact.h
@@ -11,7 +11,7 @@ struct tcf_gact {
int tcfg_paction;
#endif
};
-#define to_gact(pc) \
- container_of(pc, struct tcf_gact, common)
+#define to_gact(a) \
+ container_of(a->priv, struct tcf_gact, common)
#endif /* __NET_TC_GACT_H */
diff --git a/include/net/tc_act/tc_ipt.h b/include/net/tc_act/tc_ipt.h
index f7d25dfcc4b7..c0f4193f432c 100644
--- a/include/net/tc_act/tc_ipt.h
+++ b/include/net/tc_act/tc_ipt.h
@@ -11,7 +11,7 @@ struct tcf_ipt {
char *tcfi_tname;
struct xt_entry_target *tcfi_t;
};
-#define to_ipt(pc) \
- container_of(pc, struct tcf_ipt, common)
+#define to_ipt(a) \
+ container_of(a->priv, struct tcf_ipt, common)
#endif /* __NET_TC_IPT_H */
diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
index cfe2943690ff..4dd77a1c106b 100644
--- a/include/net/tc_act/tc_mirred.h
+++ b/include/net/tc_act/tc_mirred.h
@@ -11,7 +11,7 @@ struct tcf_mirred {
struct net_device *tcfm_dev;
struct list_head tcfm_list;
};
-#define to_mirred(pc) \
- container_of(pc, struct tcf_mirred, common)
+#define to_mirred(a) \
+ container_of(a->priv, struct tcf_mirred, common)
#endif /* __NET_TC_MIR_H */
diff --git a/include/net/tc_act/tc_nat.h b/include/net/tc_act/tc_nat.h
index 4a691f34d703..63d8e9ca9d99 100644
--- a/include/net/tc_act/tc_nat.h
+++ b/include/net/tc_act/tc_nat.h
@@ -13,9 +13,9 @@ struct tcf_nat {
u32 flags;
};
-static inline struct tcf_nat *to_tcf_nat(struct tcf_common *pc)
+static inline struct tcf_nat *to_tcf_nat(struct tc_action *a)
{
- return container_of(pc, struct tcf_nat, common);
+ return container_of(a->priv, struct tcf_nat, common);
}
#endif /* __NET_TC_NAT_H */
diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h
index e6f6e15956f5..5b80998879c7 100644
--- a/include/net/tc_act/tc_pedit.h
+++ b/include/net/tc_act/tc_pedit.h
@@ -9,7 +9,7 @@ struct tcf_pedit {
unsigned char tcfp_flags;
struct tc_pedit_key *tcfp_keys;
};
-#define to_pedit(pc) \
- container_of(pc, struct tcf_pedit, common)
+#define to_pedit(a) \
+ container_of(a->priv, struct tcf_pedit, common)
#endif /* __NET_TC_PED_H */
diff --git a/include/net/tc_act/tc_skbedit.h b/include/net/tc_act/tc_skbedit.h
index dd5d86fab030..0df9a0db4a8e 100644
--- a/include/net/tc_act/tc_skbedit.h
+++ b/include/net/tc_act/tc_skbedit.h
@@ -29,7 +29,7 @@ struct tcf_skbedit {
u16 queue_mapping;
/* XXX: 16-bit pad here? */
};
-#define to_skbedit(pc) \
- container_of(pc, struct tcf_skbedit, common)
+#define to_skbedit(a) \
+ container_of(a->priv, struct tcf_skbedit, common)
#endif /* __NET_TC_SKBEDIT_H */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 743accec6c76..87d877408188 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -31,6 +31,7 @@
#include <linux/crypto.h>
#include <linux/cryptohash.h>
#include <linux/kref.h>
+#include <linux/ktime.h>
#include <net/inet_connection_sock.h>
#include <net/inet_timewait_sock.h>
@@ -478,7 +479,6 @@ int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th,
struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
struct ip_options *opt);
#ifdef CONFIG_SYN_COOKIES
-#include <linux/ktime.h>
/* Syncookies use a monotonic timer which increments every 60 seconds.
* This counter is used both as a hash input and partially encoded into
@@ -620,7 +620,7 @@ static inline void tcp_bound_rto(const struct sock *sk)
static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
{
- return (tp->srtt >> 3) + tp->rttvar;
+ return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us);
}
static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)
@@ -657,6 +657,11 @@ static inline u32 tcp_rto_min(struct sock *sk)
return rto_min;
}
+static inline u32 tcp_rto_min_us(struct sock *sk)
+{
+ return jiffies_to_usecs(tcp_rto_min(sk));
+}
+
/* Compute the actual receive window we are currently advertising.
* Rcv_nxt can be after the window if our peer push more data
* than the offered window.
@@ -779,7 +784,6 @@ enum tcp_ca_event {
#define TCP_CA_BUF_MAX (TCP_CA_NAME_MAX*TCP_CA_MAX)
#define TCP_CONG_NON_RESTRICTED 0x1
-#define TCP_CONG_RTT_STAMP 0x2
struct tcp_congestion_ops {
struct list_head list;
@@ -792,8 +796,6 @@ struct tcp_congestion_ops {
/* return slow start threshold (required) */
u32 (*ssthresh)(struct sock *sk);
- /* lower bound for congestion window (optional) */
- u32 (*min_cwnd)(const struct sock *sk);
/* do new cwnd calculation (required) */
void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked, u32 in_flight);
/* call before changing ca_state (optional) */
@@ -828,7 +830,6 @@ void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w);
extern struct tcp_congestion_ops tcp_init_congestion_ops;
u32 tcp_reno_ssthresh(struct sock *sk);
void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight);
-u32 tcp_reno_min_cwnd(const struct sock *sk);
extern struct tcp_congestion_ops tcp_reno;
static inline void tcp_set_ca_state(struct sock *sk, const u8 ca_state)
diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h
index b52bda8d13b1..10ab0fc6d4f7 100644
--- a/include/net/wpan-phy.h
+++ b/include/net/wpan-phy.h
@@ -37,15 +37,22 @@ struct wpan_phy {
struct mutex pib_lock;
/*
- * This is a PIB according to 802.15.4-2006.
+ * This is a PIB according to 802.15.4-2011.
* We do not provide timing-related variables, as they
* aren't used outside of driver
*/
u8 current_channel;
u8 current_page;
u32 channels_supported[32];
- u8 transmit_power;
+ s8 transmit_power;
u8 cca_mode;
+ u8 min_be;
+ u8 max_be;
+ u8 csma_retries;
+ s8 frame_retries;
+
+ bool lbt;
+ s32 cca_ed_level;
struct device dev;
int idx;
@@ -54,6 +61,14 @@ struct wpan_phy {
const char *name, int type);
void (*del_iface)(struct wpan_phy *phy, struct net_device *dev);
+ int (*set_txpower)(struct wpan_phy *phy, int db);
+ int (*set_lbt)(struct wpan_phy *phy, bool on);
+ int (*set_cca_mode)(struct wpan_phy *phy, u8 cca_mode);
+ int (*set_cca_ed_level)(struct wpan_phy *phy, int level);
+ int (*set_csma_params)(struct wpan_phy *phy, u8 min_be, u8 max_be,
+ u8 retries);
+ int (*set_frame_retries)(struct wpan_phy *phy, s8 retries);
+
char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
};
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index fb5654a8ca3c..32682ae47b3f 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -118,11 +118,10 @@
struct xfrm_state_walk {
struct list_head all;
u8 state;
- union {
- u8 dying;
- u8 proto;
- };
+ u8 dying;
+ u8 proto;
u32 seq;
+ struct xfrm_address_filter *filter;
};
/* Full description of state of transformer. */
@@ -350,6 +349,16 @@ int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
+struct xfrm_input_afinfo {
+ unsigned int family;
+ struct module *owner;
+ int (*callback)(struct sk_buff *skb, u8 protocol,
+ int err);
+};
+
+int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo);
+int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo);
+
void xfrm_state_delete_tunnel(struct xfrm_state *x);
struct xfrm_type {
@@ -594,21 +603,33 @@ struct xfrm_mgr {
const struct xfrm_migrate *m,
int num_bundles,
const struct xfrm_kmaddress *k);
+ bool (*is_alive)(const struct km_event *c);
};
int xfrm_register_km(struct xfrm_mgr *km);
int xfrm_unregister_km(struct xfrm_mgr *km);
+struct xfrm_tunnel_skb_cb {
+ union {
+ struct inet_skb_parm h4;
+ struct inet6_skb_parm h6;
+ } header;
+
+ union {
+ struct ip_tunnel *ip4;
+ struct ip6_tnl *ip6;
+ } tunnel;
+};
+
+#define XFRM_TUNNEL_SKB_CB(__skb) ((struct xfrm_tunnel_skb_cb *)&((__skb)->cb[0]))
+
/*
* This structure is used for the duration where packets are being
* transformed by IPsec. As soon as the packet leaves IPsec the
* area beyond the generic IP part may be overwritten.
*/
struct xfrm_skb_cb {
- union {
- struct inet_skb_parm h4;
- struct inet6_skb_parm h6;
- } header;
+ struct xfrm_tunnel_skb_cb header;
/* Sequence number for replay protection. */
union {
@@ -630,10 +651,7 @@ struct xfrm_skb_cb {
* to transmit header information to the mode input/output functions.
*/
struct xfrm_mode_skb_cb {
- union {
- struct inet_skb_parm h4;
- struct inet6_skb_parm h6;
- } header;
+ struct xfrm_tunnel_skb_cb header;
/* Copied from header for IPv4, always set to zero and DF for IPv6. */
__be16 id;
@@ -665,10 +683,7 @@ struct xfrm_mode_skb_cb {
* related information.
*/
struct xfrm_spi_skb_cb {
- union {
- struct inet_skb_parm h4;
- struct inet6_skb_parm h6;
- } header;
+ struct xfrm_tunnel_skb_cb header;
unsigned int daddroff;
unsigned int family;
@@ -1347,18 +1362,34 @@ struct xfrm_algo_desc {
struct sadb_alg desc;
};
-/* XFRM tunnel handlers. */
-struct xfrm_tunnel {
+/* XFRM protocol handlers. */
+struct xfrm4_protocol {
int (*handler)(struct sk_buff *skb);
+ int (*input_handler)(struct sk_buff *skb, int nexthdr, __be32 spi,
+ int encap_type);
+ int (*cb_handler)(struct sk_buff *skb, int err);
int (*err_handler)(struct sk_buff *skb, u32 info);
- struct xfrm_tunnel __rcu *next;
+ struct xfrm4_protocol __rcu *next;
+ int priority;
+};
+
+struct xfrm6_protocol {
+ int (*handler)(struct sk_buff *skb);
+ int (*cb_handler)(struct sk_buff *skb, int err);
+ int (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info);
+
+ struct xfrm6_protocol __rcu *next;
int priority;
};
-struct xfrm_tunnel_notifier {
+/* XFRM tunnel handlers. */
+struct xfrm_tunnel {
int (*handler)(struct sk_buff *skb);
- struct xfrm_tunnel_notifier __rcu *next;
+ int (*err_handler)(struct sk_buff *skb, u32 info);
+
+ struct xfrm_tunnel __rcu *next;
int priority;
};
@@ -1375,11 +1406,14 @@ void xfrm4_init(void);
int xfrm_state_init(struct net *net);
void xfrm_state_fini(struct net *net);
void xfrm4_state_init(void);
+void xfrm4_protocol_init(void);
#ifdef CONFIG_XFRM
int xfrm6_init(void);
void xfrm6_fini(void);
int xfrm6_state_init(void);
void xfrm6_state_fini(void);
+int xfrm6_protocol_init(void);
+void xfrm6_protocol_fini(void);
#else
static inline int xfrm6_init(void)
{
@@ -1405,7 +1439,8 @@ static inline void xfrm_sysctl_fini(struct net *net)
}
#endif
-void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto);
+void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto,
+ struct xfrm_address_filter *filter);
int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
int (*func)(struct xfrm_state *, int, void*), void *);
void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net);
@@ -1497,20 +1532,22 @@ int xfrm4_rcv(struct sk_buff *skb);
static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
{
- return xfrm4_rcv_encap(skb, nexthdr, spi, 0);
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
+ XFRM_SPI_SKB_CB(skb)->family = AF_INET;
+ XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
+ return xfrm_input(skb, nexthdr, spi, 0);
}
int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm4_output(struct sk_buff *skb);
int xfrm4_output_finish(struct sk_buff *skb);
+int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err);
+int xfrm4_protocol_register(struct xfrm4_protocol *handler, unsigned char protocol);
+int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char protocol);
int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family);
int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
void xfrm4_local_error(struct sk_buff *skb, u32 mtu);
-int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler);
-int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler);
-int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler);
-int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler);
int xfrm6_extract_header(struct sk_buff *skb);
int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb);
int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi);
@@ -1519,6 +1556,9 @@ int xfrm6_rcv(struct sk_buff *skb);
int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
xfrm_address_t *saddr, u8 proto);
void xfrm6_local_error(struct sk_buff *skb, u32 mtu);
+int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err);
+int xfrm6_protocol_register(struct xfrm6_protocol *handler, unsigned char protocol);
+int xfrm6_protocol_deregister(struct xfrm6_protocol *handler, unsigned char protocol);
int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family);
int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family);
__be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr);
@@ -1646,6 +1686,20 @@ static inline int xfrm_aevent_is_on(struct net *net)
rcu_read_unlock();
return ret;
}
+
+static inline int xfrm_acquire_is_on(struct net *net)
+{
+ struct sock *nlsk;
+ int ret = 0;
+
+ rcu_read_lock();
+ nlsk = rcu_dereference(net->xfrm.nlsk);
+ if (nlsk)
+ ret = netlink_has_listeners(nlsk, XFRMNLGRP_ACQUIRE);
+ rcu_read_unlock();
+
+ return ret;
+}
#endif
static inline int aead_len(struct xfrm_algo_aead *alg)
@@ -1748,4 +1802,24 @@ static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m)
return ret;
}
+static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x,
+ unsigned int family)
+{
+ bool tunnel = false;
+
+ switch(family) {
+ case AF_INET:
+ if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4)
+ tunnel = true;
+ break;
+ case AF_INET6:
+ if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6)
+ tunnel = true;
+ break;
+ }
+ if (tunnel && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL))
+ return -EINVAL;
+
+ return 0;
+}
#endif /* _NET_XFRM_H */
diff --git a/include/rdma/ib_cm.h b/include/rdma/ib_cm.h
index f29e3a27c2cc..0e3ff30647d5 100644
--- a/include/rdma/ib_cm.h
+++ b/include/rdma/ib_cm.h
@@ -601,5 +601,4 @@ struct ib_cm_sidr_rep_param {
int ib_send_cm_sidr_rep(struct ib_cm_id *cm_id,
struct ib_cm_sidr_rep_param *param);
-int ib_update_cm_av(struct ib_cm_id *id, const u8 *smac, const u8 *alt_smac);
#endif /* IB_CM_H */
diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h
index 9ee0d2e51b16..1ea0b65c4cfb 100644
--- a/include/rdma/ib_umem.h
+++ b/include/rdma/ib_umem.h
@@ -46,17 +46,12 @@ struct ib_umem {
int page_size;
int writable;
int hugetlb;
- struct list_head chunk_list;
struct work_struct work;
struct mm_struct *mm;
unsigned long diff;
-};
-
-struct ib_umem_chunk {
- struct list_head list;
- int nents;
- int nmap;
- struct scatterlist page_list[0];
+ struct sg_table sg_head;
+ int nmap;
+ int npages;
};
#ifdef CONFIG_INFINIBAND_USER_MEM
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index 6793f32ccb58..acd825182977 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -122,7 +122,19 @@ enum ib_device_cap_flags {
IB_DEVICE_BLOCK_MULTICAST_LOOPBACK = (1<<22),
IB_DEVICE_MEM_WINDOW_TYPE_2A = (1<<23),
IB_DEVICE_MEM_WINDOW_TYPE_2B = (1<<24),
- IB_DEVICE_MANAGED_FLOW_STEERING = (1<<29)
+ IB_DEVICE_MANAGED_FLOW_STEERING = (1<<29),
+ IB_DEVICE_SIGNATURE_HANDOVER = (1<<30)
+};
+
+enum ib_signature_prot_cap {
+ IB_PROT_T10DIF_TYPE_1 = 1,
+ IB_PROT_T10DIF_TYPE_2 = 1 << 1,
+ IB_PROT_T10DIF_TYPE_3 = 1 << 2,
+};
+
+enum ib_signature_guard_cap {
+ IB_GUARD_T10DIF_CRC = 1,
+ IB_GUARD_T10DIF_CSUM = 1 << 1,
};
enum ib_atomic_cap {
@@ -172,6 +184,8 @@ struct ib_device_attr {
unsigned int max_fast_reg_page_list_len;
u16 max_pkeys;
u8 local_ca_ack_delay;
+ int sig_prot_cap;
+ int sig_guard_cap;
};
enum ib_mtu {
@@ -461,6 +475,130 @@ int ib_rate_to_mult(enum ib_rate rate) __attribute_const__;
*/
int ib_rate_to_mbps(enum ib_rate rate) __attribute_const__;
+enum ib_mr_create_flags {
+ IB_MR_SIGNATURE_EN = 1,
+};
+
+/**
+ * ib_mr_init_attr - Memory region init attributes passed to routine
+ * ib_create_mr.
+ * @max_reg_descriptors: max number of registration descriptors that
+ * may be used with registration work requests.
+ * @flags: MR creation flags bit mask.
+ */
+struct ib_mr_init_attr {
+ int max_reg_descriptors;
+ u32 flags;
+};
+
+enum ib_signature_type {
+ IB_SIG_TYPE_T10_DIF,
+};
+
+/**
+ * T10-DIF Signature types
+ * T10-DIF types are defined by SCSI
+ * specifications.
+ */
+enum ib_t10_dif_type {
+ IB_T10DIF_NONE,
+ IB_T10DIF_TYPE1,
+ IB_T10DIF_TYPE2,
+ IB_T10DIF_TYPE3
+};
+
+/**
+ * Signature T10-DIF block-guard types
+ * IB_T10DIF_CRC: Corresponds to T10-PI mandated CRC checksum rules.
+ * IB_T10DIF_CSUM: Corresponds to IP checksum rules.
+ */
+enum ib_t10_dif_bg_type {
+ IB_T10DIF_CRC,
+ IB_T10DIF_CSUM
+};
+
+/**
+ * struct ib_t10_dif_domain - Parameters specific for T10-DIF
+ * domain.
+ * @type: T10-DIF type (0|1|2|3)
+ * @bg_type: T10-DIF block guard type (CRC|CSUM)
+ * @pi_interval: protection information interval.
+ * @bg: seed of guard computation.
+ * @app_tag: application tag of guard block
+ * @ref_tag: initial guard block reference tag.
+ * @type3_inc_reftag: T10-DIF type 3 does not state
+ * about the reference tag, it is the user
+ * choice to increment it or not.
+ */
+struct ib_t10_dif_domain {
+ enum ib_t10_dif_type type;
+ enum ib_t10_dif_bg_type bg_type;
+ u16 pi_interval;
+ u16 bg;
+ u16 app_tag;
+ u32 ref_tag;
+ bool type3_inc_reftag;
+};
+
+/**
+ * struct ib_sig_domain - Parameters for signature domain
+ * @sig_type: specific signauture type
+ * @sig: union of all signature domain attributes that may
+ * be used to set domain layout.
+ */
+struct ib_sig_domain {
+ enum ib_signature_type sig_type;
+ union {
+ struct ib_t10_dif_domain dif;
+ } sig;
+};
+
+/**
+ * struct ib_sig_attrs - Parameters for signature handover operation
+ * @check_mask: bitmask for signature byte check (8 bytes)
+ * @mem: memory domain layout desciptor.
+ * @wire: wire domain layout desciptor.
+ */
+struct ib_sig_attrs {
+ u8 check_mask;
+ struct ib_sig_domain mem;
+ struct ib_sig_domain wire;
+};
+
+enum ib_sig_err_type {
+ IB_SIG_BAD_GUARD,
+ IB_SIG_BAD_REFTAG,
+ IB_SIG_BAD_APPTAG,
+};
+
+/**
+ * struct ib_sig_err - signature error descriptor
+ */
+struct ib_sig_err {
+ enum ib_sig_err_type err_type;
+ u32 expected;
+ u32 actual;
+ u64 sig_err_offset;
+ u32 key;
+};
+
+enum ib_mr_status_check {
+ IB_MR_CHECK_SIG_STATUS = 1,
+};
+
+/**
+ * struct ib_mr_status - Memory region status container
+ *
+ * @fail_status: Bitmask of MR checks status. For each
+ * failed check a corresponding status bit is set.
+ * @sig_err: Additional info for IB_MR_CEHCK_SIG_STATUS
+ * failure.
+ */
+struct ib_mr_status {
+ u32 fail_status;
+ struct ib_sig_err sig_err;
+};
+
/**
* mult_to_ib_rate - Convert a multiple of 2.5 Gbit/sec to an IB rate
* enum.
@@ -644,6 +782,7 @@ enum ib_qp_create_flags {
IB_QP_CREATE_IPOIB_UD_LSO = 1 << 0,
IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK = 1 << 1,
IB_QP_CREATE_NETIF_QP = 1 << 5,
+ IB_QP_CREATE_SIGNATURE_EN = 1 << 6,
/* reserve bits 26-31 for low level drivers' internal use */
IB_QP_CREATE_RESERVED_START = 1 << 26,
IB_QP_CREATE_RESERVED_END = 1 << 31,
@@ -808,6 +947,7 @@ enum ib_wr_opcode {
IB_WR_MASKED_ATOMIC_CMP_AND_SWP,
IB_WR_MASKED_ATOMIC_FETCH_AND_ADD,
IB_WR_BIND_MW,
+ IB_WR_REG_SIG_MR,
/* reserve values for low level drivers' internal use.
* These values will not be used at all in the ib core layer.
*/
@@ -913,6 +1053,12 @@ struct ib_send_wr {
u32 rkey;
struct ib_mw_bind_info bind_info;
} bind_mw;
+ struct {
+ struct ib_sig_attrs *sig_attrs;
+ struct ib_mr *sig_mr;
+ int access_flags;
+ struct ib_sge *prot;
+ } sig_handover;
} wr;
u32 xrc_remote_srq_num; /* XRC TGT QPs only */
};
@@ -1266,10 +1412,6 @@ struct ib_dma_mapping_ops {
void (*unmap_sg)(struct ib_device *dev,
struct scatterlist *sg, int nents,
enum dma_data_direction direction);
- u64 (*dma_address)(struct ib_device *dev,
- struct scatterlist *sg);
- unsigned int (*dma_len)(struct ib_device *dev,
- struct scatterlist *sg);
void (*sync_single_for_cpu)(struct ib_device *dev,
u64 dma_handle,
size_t size,
@@ -1407,6 +1549,9 @@ struct ib_device {
int (*query_mr)(struct ib_mr *mr,
struct ib_mr_attr *mr_attr);
int (*dereg_mr)(struct ib_mr *mr);
+ int (*destroy_mr)(struct ib_mr *mr);
+ struct ib_mr * (*create_mr)(struct ib_pd *pd,
+ struct ib_mr_init_attr *mr_init_attr);
struct ib_mr * (*alloc_fast_reg_mr)(struct ib_pd *pd,
int max_page_list_len);
struct ib_fast_reg_page_list * (*alloc_fast_reg_page_list)(struct ib_device *device,
@@ -1455,6 +1600,8 @@ struct ib_device {
*flow_attr,
int domain);
int (*destroy_flow)(struct ib_flow *flow_id);
+ int (*check_mr_status)(struct ib_mr *mr, u32 check_mask,
+ struct ib_mr_status *mr_status);
struct ib_dma_mapping_ops *dma_ops;
@@ -2089,12 +2236,13 @@ static inline void ib_dma_unmap_sg_attrs(struct ib_device *dev,
* ib_sg_dma_address - Return the DMA address from a scatter/gather entry
* @dev: The device for which the DMA addresses were created
* @sg: The scatter/gather entry
+ *
+ * Note: this function is obsolete. To do: change all occurrences of
+ * ib_sg_dma_address() into sg_dma_address().
*/
static inline u64 ib_sg_dma_address(struct ib_device *dev,
struct scatterlist *sg)
{
- if (dev->dma_ops)
- return dev->dma_ops->dma_address(dev, sg);
return sg_dma_address(sg);
}
@@ -2102,12 +2250,13 @@ static inline u64 ib_sg_dma_address(struct ib_device *dev,
* ib_sg_dma_len - Return the DMA length from a scatter/gather entry
* @dev: The device for which the DMA addresses were created
* @sg: The scatter/gather entry
+ *
+ * Note: this function is obsolete. To do: change all occurrences of
+ * ib_sg_dma_len() into sg_dma_len().
*/
static inline unsigned int ib_sg_dma_len(struct ib_device *dev,
struct scatterlist *sg)
{
- if (dev->dma_ops)
- return dev->dma_ops->dma_len(dev, sg);
return sg_dma_len(sg);
}
@@ -2250,6 +2399,25 @@ int ib_query_mr(struct ib_mr *mr, struct ib_mr_attr *mr_attr);
*/
int ib_dereg_mr(struct ib_mr *mr);
+
+/**
+ * ib_create_mr - Allocates a memory region that may be used for
+ * signature handover operations.
+ * @pd: The protection domain associated with the region.
+ * @mr_init_attr: memory region init attributes.
+ */
+struct ib_mr *ib_create_mr(struct ib_pd *pd,
+ struct ib_mr_init_attr *mr_init_attr);
+
+/**
+ * ib_destroy_mr - Destroys a memory region that was created using
+ * ib_create_mr and removes it from HW translation tables.
+ * @mr: The memory region to destroy.
+ *
+ * This function can fail, if the memory region has memory windows bound to it.
+ */
+int ib_destroy_mr(struct ib_mr *mr);
+
/**
* ib_alloc_fast_reg_mr - Allocates memory region usable with the
* IB_WR_FAST_REG_MR send work request.
@@ -2435,4 +2603,19 @@ static inline int ib_check_mr_access(int flags)
return 0;
}
+/**
+ * ib_check_mr_status: lightweight check of MR status.
+ * This routine may provide status checks on a selected
+ * ib_mr. first use is for signature status check.
+ *
+ * @mr: A memory region.
+ * @check_mask: Bitmask of which checks to perform from
+ * ib_mr_status_check enumeration.
+ * @mr_status: The container of relevant status checks.
+ * failed checks will be indicated in the status bitmask
+ * and the relevant info shall be in the error item.
+ */
+int ib_check_mr_status(struct ib_mr *mr, u32 check_mask,
+ struct ib_mr_status *mr_status);
+
#endif /* IB_VERBS_H */
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index 7221a24e821b..728c9ad9feb0 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -133,6 +133,10 @@ struct iscsi_task {
unsigned long last_xfer;
unsigned long last_timeout;
bool have_checked_conn;
+
+ /* T10 protection information */
+ bool protected;
+
/* state set/tested under session->lock */
int state;
atomic_t refcount;
diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
index 88640a47216c..2555ee5343fd 100644
--- a/include/scsi/scsi_transport_iscsi.h
+++ b/include/scsi/scsi_transport_iscsi.h
@@ -167,6 +167,7 @@ struct iscsi_transport {
struct iscsi_bus_flash_conn *fnode_conn);
int (*logout_flashnode_sid) (struct iscsi_cls_session *cls_sess);
int (*get_host_stats) (struct Scsi_Host *shost, char *buf, int len);
+ u8 (*check_protection)(struct iscsi_task *task, sector_t *sector);
};
/*
diff --git a/include/scsi/scsi_transport_srp.h b/include/scsi/scsi_transport_srp.h
index b11da5c1331e..cdb05dd1d440 100644
--- a/include/scsi/scsi_transport_srp.h
+++ b/include/scsi/scsi_transport_srp.h
@@ -41,7 +41,6 @@ enum srp_rport_state {
* @mutex: Protects against concurrent rport reconnect /
* fast_io_fail / dev_loss_tmo activity.
* @state: rport state.
- * @deleted: Whether or not srp_rport_del() has already been invoked.
* @reconnect_delay: Reconnect delay in seconds.
* @failed_reconnects: Number of failed reconnect attempts.
* @reconnect_work: Work structure used for scheduling reconnect attempts.
diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h
index 3176cdc32937..4ee4e30d26d9 100644
--- a/include/trace/events/btrfs.h
+++ b/include/trace/events/btrfs.h
@@ -21,6 +21,8 @@ struct btrfs_block_group_cache;
struct btrfs_free_cluster;
struct map_lookup;
struct extent_buffer;
+struct btrfs_work;
+struct __btrfs_workqueue;
#define show_ref_type(type) \
__print_symbolic(type, \
@@ -982,6 +984,141 @@ TRACE_EVENT(free_extent_state,
(void *)__entry->ip)
);
+DECLARE_EVENT_CLASS(btrfs__work,
+
+ TP_PROTO(struct btrfs_work *work),
+
+ TP_ARGS(work),
+
+ TP_STRUCT__entry(
+ __field( void *, work )
+ __field( void *, wq )
+ __field( void *, func )
+ __field( void *, ordered_func )
+ __field( void *, ordered_free )
+ ),
+
+ TP_fast_assign(
+ __entry->work = work;
+ __entry->wq = work->wq;
+ __entry->func = work->func;
+ __entry->ordered_func = work->ordered_func;
+ __entry->ordered_free = work->ordered_free;
+ ),
+
+ TP_printk("work=%p, wq=%p, func=%p, ordered_func=%p, ordered_free=%p",
+ __entry->work, __entry->wq, __entry->func,
+ __entry->ordered_func, __entry->ordered_free)
+);
+
+/* For situiations that the work is freed */
+DECLARE_EVENT_CLASS(btrfs__work__done,
+
+ TP_PROTO(struct btrfs_work *work),
+
+ TP_ARGS(work),
+
+ TP_STRUCT__entry(
+ __field( void *, work )
+ ),
+
+ TP_fast_assign(
+ __entry->work = work;
+ ),
+
+ TP_printk("work->%p", __entry->work)
+);
+
+DEFINE_EVENT(btrfs__work, btrfs_work_queued,
+
+ TP_PROTO(struct btrfs_work *work),
+
+ TP_ARGS(work)
+);
+
+DEFINE_EVENT(btrfs__work, btrfs_work_sched,
+
+ TP_PROTO(struct btrfs_work *work),
+
+ TP_ARGS(work)
+);
+
+DEFINE_EVENT(btrfs__work, btrfs_normal_work_done,
+
+ TP_PROTO(struct btrfs_work *work),
+
+ TP_ARGS(work)
+);
+
+DEFINE_EVENT(btrfs__work__done, btrfs_all_work_done,
+
+ TP_PROTO(struct btrfs_work *work),
+
+ TP_ARGS(work)
+);
+
+DEFINE_EVENT(btrfs__work, btrfs_ordered_sched,
+
+ TP_PROTO(struct btrfs_work *work),
+
+ TP_ARGS(work)
+);
+
+DECLARE_EVENT_CLASS(btrfs__workqueue,
+
+ TP_PROTO(struct __btrfs_workqueue *wq, const char *name, int high),
+
+ TP_ARGS(wq, name, high),
+
+ TP_STRUCT__entry(
+ __field( void *, wq )
+ __string( name, name )
+ __field( int , high )
+ ),
+
+ TP_fast_assign(
+ __entry->wq = wq;
+ __assign_str(name, name);
+ __entry->high = high;
+ ),
+
+ TP_printk("name=%s%s, wq=%p", __get_str(name),
+ __print_flags(__entry->high, "",
+ {(WQ_HIGHPRI), "-high"}),
+ __entry->wq)
+);
+
+DEFINE_EVENT(btrfs__workqueue, btrfs_workqueue_alloc,
+
+ TP_PROTO(struct __btrfs_workqueue *wq, const char *name, int high),
+
+ TP_ARGS(wq, name, high)
+);
+
+DECLARE_EVENT_CLASS(btrfs__workqueue_done,
+
+ TP_PROTO(struct __btrfs_workqueue *wq),
+
+ TP_ARGS(wq),
+
+ TP_STRUCT__entry(
+ __field( void *, wq )
+ ),
+
+ TP_fast_assign(
+ __entry->wq = wq;
+ ),
+
+ TP_printk("wq=%p", __entry->wq)
+);
+
+DEFINE_EVENT(btrfs__workqueue_done, btrfs_workqueue_destroy,
+
+ TP_PROTO(struct __btrfs_workqueue *wq),
+
+ TP_ARGS(wq)
+);
+
#endif /* _TRACE_BTRFS_H */
/* This part must be outside protection */
diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h
index 197d3125df2a..010ea89eeb0e 100644
--- a/include/trace/events/ext4.h
+++ b/include/trace/events/ext4.h
@@ -16,6 +16,15 @@ struct mpage_da_data;
struct ext4_map_blocks;
struct extent_status;
+/* shim until we merge in the xfs_collapse_range branch */
+#ifndef FALLOC_FL_COLLAPSE_RANGE
+#define FALLOC_FL_COLLAPSE_RANGE 0x08
+#endif
+
+#ifndef FALLOC_FL_ZERO_RANGE
+#define FALLOC_FL_ZERO_RANGE 0x10
+#endif
+
#define EXT4_I(inode) (container_of(inode, struct ext4_inode_info, vfs_inode))
#define show_mballoc_flags(flags) __print_flags(flags, "|", \
@@ -68,6 +77,13 @@ struct extent_status;
{ EXTENT_STATUS_DELAYED, "D" }, \
{ EXTENT_STATUS_HOLE, "H" })
+#define show_falloc_mode(mode) __print_flags(mode, "|", \
+ { FALLOC_FL_KEEP_SIZE, "KEEP_SIZE"}, \
+ { FALLOC_FL_PUNCH_HOLE, "PUNCH_HOLE"}, \
+ { FALLOC_FL_NO_HIDE_STALE, "NO_HIDE_STALE"}, \
+ { FALLOC_FL_COLLAPSE_RANGE, "COLLAPSE_RANGE"}, \
+ { FALLOC_FL_ZERO_RANGE, "ZERO_RANGE"})
+
TRACE_EVENT(ext4_free_inode,
TP_PROTO(struct inode *inode),
@@ -1328,7 +1344,7 @@ TRACE_EVENT(ext4_direct_IO_exit,
__entry->rw, __entry->ret)
);
-TRACE_EVENT(ext4_fallocate_enter,
+DECLARE_EVENT_CLASS(ext4__fallocate_mode,
TP_PROTO(struct inode *inode, loff_t offset, loff_t len, int mode),
TP_ARGS(inode, offset, len, mode),
@@ -1336,23 +1352,45 @@ TRACE_EVENT(ext4_fallocate_enter,
TP_STRUCT__entry(
__field( dev_t, dev )
__field( ino_t, ino )
- __field( loff_t, pos )
- __field( loff_t, len )
+ __field( loff_t, offset )
+ __field( loff_t, len )
__field( int, mode )
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
- __entry->pos = offset;
+ __entry->offset = offset;
__entry->len = len;
__entry->mode = mode;
),
- TP_printk("dev %d,%d ino %lu pos %lld len %lld mode %d",
+ TP_printk("dev %d,%d ino %lu offset %lld len %lld mode %s",
MAJOR(__entry->dev), MINOR(__entry->dev),
- (unsigned long) __entry->ino, __entry->pos,
- __entry->len, __entry->mode)
+ (unsigned long) __entry->ino,
+ __entry->offset, __entry->len,
+ show_falloc_mode(__entry->mode))
+);
+
+DEFINE_EVENT(ext4__fallocate_mode, ext4_fallocate_enter,
+
+ TP_PROTO(struct inode *inode, loff_t offset, loff_t len, int mode),
+
+ TP_ARGS(inode, offset, len, mode)
+);
+
+DEFINE_EVENT(ext4__fallocate_mode, ext4_punch_hole,
+
+ TP_PROTO(struct inode *inode, loff_t offset, loff_t len, int mode),
+
+ TP_ARGS(inode, offset, len, mode)
+);
+
+DEFINE_EVENT(ext4__fallocate_mode, ext4_zero_range,
+
+ TP_PROTO(struct inode *inode, loff_t offset, loff_t len, int mode),
+
+ TP_ARGS(inode, offset, len, mode)
);
TRACE_EVENT(ext4_fallocate_exit,
@@ -1384,31 +1422,6 @@ TRACE_EVENT(ext4_fallocate_exit,
__entry->ret)
);
-TRACE_EVENT(ext4_punch_hole,
- TP_PROTO(struct inode *inode, loff_t offset, loff_t len),
-
- TP_ARGS(inode, offset, len),
-
- TP_STRUCT__entry(
- __field( dev_t, dev )
- __field( ino_t, ino )
- __field( loff_t, offset )
- __field( loff_t, len )
- ),
-
- TP_fast_assign(
- __entry->dev = inode->i_sb->s_dev;
- __entry->ino = inode->i_ino;
- __entry->offset = offset;
- __entry->len = len;
- ),
-
- TP_printk("dev %d,%d ino %lu offset %lld len %lld",
- MAJOR(__entry->dev), MINOR(__entry->dev),
- (unsigned long) __entry->ino,
- __entry->offset, __entry->len)
-);
-
TRACE_EVENT(ext4_unlink_enter,
TP_PROTO(struct inode *parent, struct dentry *dentry),
@@ -2410,6 +2423,31 @@ TRACE_EVENT(ext4_es_shrink_exit,
__entry->shrunk_nr, __entry->cache_cnt)
);
+TRACE_EVENT(ext4_collapse_range,
+ TP_PROTO(struct inode *inode, loff_t offset, loff_t len),
+
+ TP_ARGS(inode, offset, len),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(ino_t, ino)
+ __field(loff_t, offset)
+ __field(loff_t, len)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->offset = offset;
+ __entry->len = len;
+ ),
+
+ TP_printk("dev %d,%d ino %lu offset %lld len %lld",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ (unsigned long) __entry->ino,
+ __entry->offset, __entry->len)
+);
+
#endif /* _TRACE_EXT4_H */
/* This part must be outside protection */
diff --git a/include/trace/events/migrate.h b/include/trace/events/migrate.h
index 3075ffbb9a83..4e4f2f8b1ac2 100644
--- a/include/trace/events/migrate.h
+++ b/include/trace/events/migrate.h
@@ -4,6 +4,8 @@
#if !defined(_TRACE_MIGRATE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_MIGRATE_H
+#include <linux/tracepoint.h>
+
#define MIGRATE_MODE \
{MIGRATE_ASYNC, "MIGRATE_ASYNC"}, \
{MIGRATE_SYNC_LIGHT, "MIGRATE_SYNC_LIGHT"}, \
diff --git a/include/trace/events/module.h b/include/trace/events/module.h
index 161932737416..11fd51b413de 100644
--- a/include/trace/events/module.h
+++ b/include/trace/events/module.h
@@ -22,8 +22,10 @@ struct module;
#define show_module_flags(flags) __print_flags(flags, "", \
{ (1UL << TAINT_PROPRIETARY_MODULE), "P" }, \
+ { (1UL << TAINT_OOT_MODULE), "O" }, \
{ (1UL << TAINT_FORCED_MODULE), "F" }, \
- { (1UL << TAINT_CRAP), "C" })
+ { (1UL << TAINT_CRAP), "C" }, \
+ { (1UL << TAINT_UNSIGNED_MODULE), "X" })
TRACE_EVENT(module_load,
diff --git a/include/trace/events/net.h b/include/trace/events/net.h
index a34f27b2e394..1de256b35807 100644
--- a/include/trace/events/net.h
+++ b/include/trace/events/net.h
@@ -153,8 +153,8 @@ DECLARE_EVENT_CLASS(net_dev_rx_verbose_template,
__field( u16, vlan_tci )
__field( u16, protocol )
__field( u8, ip_summed )
- __field( u32, rxhash )
- __field( bool, l4_rxhash )
+ __field( u32, hash )
+ __field( bool, l4_hash )
__field( unsigned int, len )
__field( unsigned int, data_len )
__field( unsigned int, truesize )
@@ -179,8 +179,8 @@ DECLARE_EVENT_CLASS(net_dev_rx_verbose_template,
__entry->vlan_tci = vlan_tx_tag_get(skb);
__entry->protocol = ntohs(skb->protocol);
__entry->ip_summed = skb->ip_summed;
- __entry->rxhash = skb->rxhash;
- __entry->l4_rxhash = skb->l4_rxhash;
+ __entry->hash = skb->hash;
+ __entry->l4_hash = skb->l4_hash;
__entry->len = skb->len;
__entry->data_len = skb->data_len;
__entry->truesize = skb->truesize;
@@ -191,11 +191,11 @@ DECLARE_EVENT_CLASS(net_dev_rx_verbose_template,
__entry->gso_type = skb_shinfo(skb)->gso_type;
),
- TP_printk("dev=%s napi_id=%#x queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d rxhash=0x%08x l4_rxhash=%d len=%u data_len=%u truesize=%u mac_header_valid=%d mac_header=%d nr_frags=%d gso_size=%d gso_type=%#x",
+ TP_printk("dev=%s napi_id=%#x queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d hash=0x%08x l4_hash=%d len=%u data_len=%u truesize=%u mac_header_valid=%d mac_header=%d nr_frags=%d gso_size=%d gso_type=%#x",
__get_str(name), __entry->napi_id, __entry->queue_mapping,
__entry->skbaddr, __entry->vlan_tagged, __entry->vlan_proto,
__entry->vlan_tci, __entry->protocol, __entry->ip_summed,
- __entry->rxhash, __entry->l4_rxhash, __entry->len,
+ __entry->hash, __entry->l4_hash, __entry->len,
__entry->data_len, __entry->truesize,
__entry->mac_header_valid, __entry->mac_header,
__entry->nr_frags, __entry->gso_size, __entry->gso_type)
diff --git a/include/trace/events/v4l2.h b/include/trace/events/v4l2.h
index ef94ecad1c94..b9bb1f204693 100644
--- a/include/trace/events/v4l2.h
+++ b/include/trace/events/v4l2.h
@@ -18,6 +18,7 @@
{ V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY, "VIDEO_OUTPUT_OVERLAY" },\
{ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, "VIDEO_CAPTURE_MPLANE" },\
{ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, "VIDEO_OUTPUT_MPLANE" }, \
+ { V4L2_BUF_TYPE_SDR_CAPTURE, "SDR_CAPTURE" }, \
{ V4L2_BUF_TYPE_PRIVATE, "PRIVATE" })
#define show_field(field) \
diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h
index 464ea82e10db..cee02d65ab3f 100644
--- a/include/trace/events/writeback.h
+++ b/include/trace/events/writeback.h
@@ -4,6 +4,7 @@
#if !defined(_TRACE_WRITEBACK_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_WRITEBACK_H
+#include <linux/tracepoint.h>
#include <linux/backing-dev.h>
#include <linux/writeback.h>
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h
index 1ee19a24cc5f..8765126b328c 100644
--- a/include/trace/ftrace.h
+++ b/include/trace/ftrace.h
@@ -265,11 +265,9 @@ static notrace enum print_line_t \
ftrace_raw_output_##call(struct trace_iterator *iter, int flags, \
struct trace_event *event) \
{ \
- struct trace_seq *s = &iter->seq; \
struct ftrace_raw_##template *field; \
struct trace_entry *entry; \
struct trace_seq *p = &iter->tmp_seq; \
- int ret; \
\
entry = iter->ent; \
\
@@ -281,13 +279,7 @@ ftrace_raw_output_##call(struct trace_iterator *iter, int flags, \
field = (typeof(field))entry; \
\
trace_seq_init(p); \
- ret = trace_seq_printf(s, "%s: ", #call); \
- if (ret) \
- ret = trace_seq_printf(s, print); \
- if (!ret) \
- return TRACE_TYPE_PARTIAL_LINE; \
- \
- return TRACE_TYPE_HANDLED; \
+ return ftrace_output_call(iter, #call, print); \
} \
static struct trace_event_functions ftrace_event_type_funcs_##call = { \
.trace = ftrace_raw_output_##call, \
@@ -370,10 +362,11 @@ ftrace_define_fields_##call(struct ftrace_event_call *event_call) \
#undef __dynamic_array
#define __dynamic_array(type, item, len) \
+ __item_length = (len) * sizeof(type); \
__data_offsets->item = __data_size + \
offsetof(typeof(*entry), __data); \
- __data_offsets->item |= (len * sizeof(type)) << 16; \
- __data_size += (len) * sizeof(type);
+ __data_offsets->item |= __item_length << 16; \
+ __data_size += __item_length;
#undef __string
#define __string(item, src) __dynamic_array(char, item, \
@@ -385,6 +378,7 @@ static inline notrace int ftrace_get_offsets_##call( \
struct ftrace_data_offsets_##call *__data_offsets, proto) \
{ \
int __data_size = 0; \
+ int __maybe_unused __item_length; \
struct ftrace_raw_##call __maybe_unused *entry; \
\
tstruct; \
@@ -541,37 +535,27 @@ static notrace void \
ftrace_raw_event_##call(void *__data, proto) \
{ \
struct ftrace_event_file *ftrace_file = __data; \
- struct ftrace_event_call *event_call = ftrace_file->event_call; \
struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\
- struct ring_buffer_event *event; \
+ struct ftrace_event_buffer fbuffer; \
struct ftrace_raw_##call *entry; \
- struct ring_buffer *buffer; \
- unsigned long irq_flags; \
int __data_size; \
- int pc; \
\
if (ftrace_trigger_soft_disabled(ftrace_file)) \
return; \
\
- local_save_flags(irq_flags); \
- pc = preempt_count(); \
- \
__data_size = ftrace_get_offsets_##call(&__data_offsets, args); \
\
- event = trace_event_buffer_lock_reserve(&buffer, ftrace_file, \
- event_call->event.type, \
- sizeof(*entry) + __data_size, \
- irq_flags, pc); \
- if (!event) \
+ entry = ftrace_event_buffer_reserve(&fbuffer, ftrace_file, \
+ sizeof(*entry) + __data_size); \
+ \
+ if (!entry) \
return; \
- entry = ring_buffer_event_data(event); \
\
tstruct \
\
{ assign; } \
\
- event_trigger_unlock_commit(ftrace_file, buffer, event, entry, \
- irq_flags, pc); \
+ ftrace_event_buffer_commit(&fbuffer); \
}
/*
* The ftrace_test_probe is compiled out, it is only here as a build time check
diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h
index 95e46c8e05f9..a9b13f8b3595 100644
--- a/include/uapi/asm-generic/fcntl.h
+++ b/include/uapi/asm-generic/fcntl.h
@@ -132,6 +132,22 @@
#define F_GETOWNER_UIDS 17
#endif
+/*
+ * fd "private" POSIX locks.
+ *
+ * Usually POSIX locks held by a process are released on *any* close and are
+ * not inherited across a fork().
+ *
+ * These cmd values will set locks that conflict with normal POSIX locks, but
+ * are "owned" by the opened file, not the process. This means that they are
+ * inherited across fork() like BSD (flock) locks, and they are only released
+ * automatically when the last reference to the the open file against which
+ * they were acquired is put.
+ */
+#define F_GETLKP 36
+#define F_SETLKP 37
+#define F_SETLKPW 38
+
#define F_OWNER_TID 0
#define F_OWNER_PID 1
#define F_OWNER_PGRP 2
@@ -186,8 +202,6 @@ struct flock {
};
#endif
-#ifndef CONFIG_64BIT
-
#ifndef HAVE_ARCH_STRUCT_FLOCK64
#ifndef __ARCH_FLOCK64_PAD
#define __ARCH_FLOCK64_PAD
@@ -202,6 +216,5 @@ struct flock64 {
__ARCH_FLOCK64_PAD
};
#endif
-#endif /* !CONFIG_64BIT */
#endif /* _ASM_GENERIC_FCNTL_H */
diff --git a/include/uapi/linux/can.h b/include/uapi/linux/can.h
index e52958d7c2d1..5d9d1d140718 100644
--- a/include/uapi/linux/can.h
+++ b/include/uapi/linux/can.h
@@ -8,6 +8,38 @@
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
*/
#ifndef CAN_H
diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h
index df944ed206a8..7e2e1863db16 100644
--- a/include/uapi/linux/can/netlink.h
+++ b/include/uapi/linux/can/netlink.h
@@ -96,6 +96,7 @@ struct can_ctrlmode {
#define CAN_CTRLMODE_3_SAMPLES 0x04 /* Triple sampling mode */
#define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */
#define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */
+#define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */
/*
* CAN device statistics
@@ -122,6 +123,8 @@ enum {
IFLA_CAN_RESTART_MS,
IFLA_CAN_RESTART,
IFLA_CAN_BERR_COUNTER,
+ IFLA_CAN_DATA_BITTIMING,
+ IFLA_CAN_DATA_BITTIMING_CONST,
__IFLA_CAN_MAX
};
diff --git a/include/uapi/linux/capi.h b/include/uapi/linux/capi.h
index 65100d6cb89b..7b145fd7afb6 100644
--- a/include/uapi/linux/capi.h
+++ b/include/uapi/linux/capi.h
@@ -102,7 +102,7 @@ typedef struct capi_manufacturer_cmd {
/*
* member contr is input for
- * CAPI_GET_MANUFACTURER, CAPI_VERSION, CAPI_GET_SERIAL
+ * CAPI_GET_MANUFACTURER, CAPI_GET_VERSION, CAPI_GET_SERIAL
* and CAPI_GET_PROFILE
*/
typedef union capi_ioctl_struct {
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 38dbafaa5341..fd161e91b6d7 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -16,37 +16,97 @@
#include <linux/types.h>
#include <linux/if_ether.h>
-/* This should work for both 32 and 64 bit userland. */
+/* All structures exposed to userland should be defined such that they
+ * have the same layout for 32-bit and 64-bit userland.
+ */
+
+/**
+ * struct ethtool_cmd - link control and status
+ * @cmd: Command number = %ETHTOOL_GSET or %ETHTOOL_SSET
+ * @supported: Bitmask of %SUPPORTED_* flags for the link modes,
+ * physical connectors and other link features for which the
+ * interface supports autonegotiation or auto-detection.
+ * Read-only.
+ * @advertising: Bitmask of %ADVERTISED_* flags for the link modes,
+ * physical connectors and other link features that are
+ * advertised through autonegotiation or enabled for
+ * auto-detection.
+ * @speed: Low bits of the speed
+ * @duplex: Duplex mode; one of %DUPLEX_*
+ * @port: Physical connector type; one of %PORT_*
+ * @phy_address: MDIO address of PHY (transceiver); 0 or 255 if not
+ * applicable. For clause 45 PHYs this is the PRTAD.
+ * @transceiver: Historically used to distinguish different possible
+ * PHY types, but not in a consistent way. Deprecated.
+ * @autoneg: Enable/disable autonegotiation and auto-detection;
+ * either %AUTONEG_DISABLE or %AUTONEG_ENABLE
+ * @mdio_support: Bitmask of %ETH_MDIO_SUPPORTS_* flags for the MDIO
+ * protocols supported by the interface; 0 if unknown.
+ * Read-only.
+ * @maxtxpkt: Historically used to report TX IRQ coalescing; now
+ * obsoleted by &struct ethtool_coalesce. Read-only; deprecated.
+ * @maxrxpkt: Historically used to report RX IRQ coalescing; now
+ * obsoleted by &struct ethtool_coalesce. Read-only; deprecated.
+ * @speed_hi: High bits of the speed
+ * @eth_tp_mdix: Ethernet twisted-pair MDI(-X) status; one of
+ * %ETH_TP_MDI_*. If the status is unknown or not applicable, the
+ * value will be %ETH_TP_MDI_INVALID. Read-only.
+ * @eth_tp_mdix_ctrl: Ethernet twisted pair MDI(-X) control; one of
+ * %ETH_TP_MDI_*. If MDI(-X) control is not implemented, reads
+ * yield %ETH_TP_MDI_INVALID and writes may be ignored or rejected.
+ * When written successfully, the link should be renegotiated if
+ * necessary.
+ * @lp_advertising: Bitmask of %ADVERTISED_* flags for the link modes
+ * and other link features that the link partner advertised
+ * through autonegotiation; 0 if unknown or not applicable.
+ * Read-only.
+ *
+ * The link speed in Mbps is split between @speed and @speed_hi. Use
+ * the ethtool_cmd_speed() and ethtool_cmd_speed_set() functions to
+ * access it.
+ *
+ * If autonegotiation is disabled, the speed and @duplex represent the
+ * fixed link mode and are writable if the driver supports multiple
+ * link modes. If it is enabled then they are read-only; if the link
+ * is up they represent the negotiated link mode; if the link is down,
+ * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and
+ * @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
+ *
+ * Some hardware interfaces may have multiple PHYs and/or physical
+ * connectors fitted or do not allow the driver to detect which are
+ * fitted. For these interfaces @port and/or @phy_address may be
+ * writable, possibly dependent on @autoneg being %AUTONEG_DISABLE.
+ * Otherwise, attempts to write different values may be ignored or
+ * rejected.
+ *
+ * Users should assume that all fields not marked read-only are
+ * writable and subject to validation by the driver. They should use
+ * %ETHTOOL_GSET to get the current values before making specific
+ * changes and then applying them with %ETHTOOL_SSET.
+ *
+ * Drivers that implement set_settings() should validate all fields
+ * other than @cmd that are not described as read-only or deprecated,
+ * and must ignore all fields described as read-only.
+ *
+ * Deprecated fields should be ignored by both users and drivers.
+ */
struct ethtool_cmd {
__u32 cmd;
- __u32 supported; /* Features this interface supports */
- __u32 advertising; /* Features this interface advertises */
- __u16 speed; /* The forced speed (lower bits) in
- * Mbps. Please use
- * ethtool_cmd_speed()/_set() to
- * access it */
- __u8 duplex; /* Duplex, half or full */
- __u8 port; /* Which connector port */
- __u8 phy_address; /* MDIO PHY address (PRTAD for clause 45).
- * May be read-only or read-write
- * depending on the driver.
- */
- __u8 transceiver; /* Which transceiver to use */
- __u8 autoneg; /* Enable or disable autonegotiation */
- __u8 mdio_support; /* MDIO protocols supported. Read-only.
- * Not set by all drivers.
- */
- __u32 maxtxpkt; /* Tx pkts before generating tx int */
- __u32 maxrxpkt; /* Rx pkts before generating rx int */
- __u16 speed_hi; /* The forced speed (upper
- * bits) in Mbps. Please use
- * ethtool_cmd_speed()/_set() to
- * access it */
- __u8 eth_tp_mdix; /* twisted pair MDI-X status */
- __u8 eth_tp_mdix_ctrl; /* twisted pair MDI-X control, when set,
- * link should be renegotiated if necessary
- */
- __u32 lp_advertising; /* Features the link partner advertises */
+ __u32 supported;
+ __u32 advertising;
+ __u16 speed;
+ __u8 duplex;
+ __u8 port;
+ __u8 phy_address;
+ __u8 transceiver;
+ __u8 autoneg;
+ __u8 mdio_support;
+ __u32 maxtxpkt;
+ __u32 maxrxpkt;
+ __u16 speed_hi;
+ __u8 eth_tp_mdix;
+ __u8 eth_tp_mdix_ctrl;
+ __u32 lp_advertising;
__u32 reserved[2];
};
@@ -79,37 +139,68 @@ static inline __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep)
#define ETHTOOL_FWVERS_LEN 32
#define ETHTOOL_BUSINFO_LEN 32
-/* these strings are set to whatever the driver author decides... */
+
+/**
+ * struct ethtool_drvinfo - general driver and device information
+ * @cmd: Command number = %ETHTOOL_GDRVINFO
+ * @driver: Driver short name. This should normally match the name
+ * in its bus driver structure (e.g. pci_driver::name). Must
+ * not be an empty string.
+ * @version: Driver version string; may be an empty string
+ * @fw_version: Firmware version string; may be an empty string
+ * @bus_info: Device bus address. This should match the dev_name()
+ * string for the underlying bus device, if there is one. May be
+ * an empty string.
+ * @n_priv_flags: Number of flags valid for %ETHTOOL_GPFLAGS and
+ * %ETHTOOL_SPFLAGS commands; also the number of strings in the
+ * %ETH_SS_PRIV_FLAGS set
+ * @n_stats: Number of u64 statistics returned by the %ETHTOOL_GSTATS
+ * command; also the number of strings in the %ETH_SS_STATS set
+ * @testinfo_len: Number of results returned by the %ETHTOOL_TEST
+ * command; also the number of strings in the %ETH_SS_TEST set
+ * @eedump_len: Size of EEPROM accessible through the %ETHTOOL_GEEPROM
+ * and %ETHTOOL_SEEPROM commands, in bytes
+ * @regdump_len: Size of register dump returned by the %ETHTOOL_GREGS
+ * command, in bytes
+ *
+ * Users can use the %ETHTOOL_GSSET_INFO command to get the number of
+ * strings in any string set (from Linux 2.6.34).
+ *
+ * Drivers should set at most @driver, @version, @fw_version and
+ * @bus_info in their get_drvinfo() implementation. The ethtool
+ * core fills in the other fields using other driver operations.
+ */
struct ethtool_drvinfo {
__u32 cmd;
- char driver[32]; /* driver short name, "tulip", "eepro100" */
- char version[32]; /* driver version string */
- char fw_version[ETHTOOL_FWVERS_LEN]; /* firmware version string */
- char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */
- /* For PCI devices, use pci_name(pci_dev). */
+ char driver[32];
+ char version[32];
+ char fw_version[ETHTOOL_FWVERS_LEN];
+ char bus_info[ETHTOOL_BUSINFO_LEN];
char reserved1[32];
char reserved2[12];
- /*
- * Some struct members below are filled in
- * using ops->get_sset_count(). Obtaining
- * this info from ethtool_drvinfo is now
- * deprecated; Use ETHTOOL_GSSET_INFO
- * instead.
- */
- __u32 n_priv_flags; /* number of flags valid in ETHTOOL_GPFLAGS */
- __u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */
+ __u32 n_priv_flags;
+ __u32 n_stats;
__u32 testinfo_len;
- __u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */
- __u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */
+ __u32 eedump_len;
+ __u32 regdump_len;
};
#define SOPASS_MAX 6
-/* wake-on-lan settings */
+
+/**
+ * struct ethtool_wolinfo - Wake-On-Lan configuration
+ * @cmd: Command number = %ETHTOOL_GWOL or %ETHTOOL_SWOL
+ * @supported: Bitmask of %WAKE_* flags for supported Wake-On-Lan modes.
+ * Read-only.
+ * @wolopts: Bitmask of %WAKE_* flags for enabled Wake-On-Lan modes.
+ * @sopass: SecureOn(tm) password; meaningful only if %WAKE_MAGICSECURE
+ * is set in @wolopts.
+ */
struct ethtool_wolinfo {
__u32 cmd;
__u32 supported;
__u32 wolopts;
- __u8 sopass[SOPASS_MAX]; /* SecureOn(tm) password */
+ __u8 sopass[SOPASS_MAX];
};
/* for passing single values */
@@ -118,20 +209,51 @@ struct ethtool_value {
__u32 data;
};
-/* for passing big chunks of data */
+/**
+ * struct ethtool_regs - hardware register dump
+ * @cmd: Command number = %ETHTOOL_GREGS
+ * @version: Dump format version. This is driver-specific and may
+ * distinguish different chips/revisions. Drivers must use new
+ * version numbers whenever the dump format changes in an
+ * incompatible way.
+ * @len: On entry, the real length of @data. On return, the number of
+ * bytes used.
+ * @data: Buffer for the register dump
+ *
+ * Users should use %ETHTOOL_GDRVINFO to find the maximum length of
+ * a register dump for the interface. They must allocate the buffer
+ * immediately following this structure.
+ */
struct ethtool_regs {
__u32 cmd;
- __u32 version; /* driver-specific, indicates different chips/revs */
- __u32 len; /* bytes */
+ __u32 version;
+ __u32 len;
__u8 data[0];
};
-/* for passing EEPROM chunks */
+/**
+ * struct ethtool_eeprom - EEPROM dump
+ * @cmd: Command number = %ETHTOOL_GEEPROM, %ETHTOOL_GMODULEEEPROM or
+ * %ETHTOOL_SEEPROM
+ * @magic: A 'magic cookie' value to guard against accidental changes.
+ * The value passed in to %ETHTOOL_SEEPROM must match the value
+ * returned by %ETHTOOL_GEEPROM for the same device. This is
+ * unused when @cmd is %ETHTOOL_GMODULEEEPROM.
+ * @offset: Offset within the EEPROM to begin reading/writing, in bytes
+ * @len: On entry, number of bytes to read/write. On successful
+ * return, number of bytes actually read/written. In case of
+ * error, this may indicate at what point the error occurred.
+ * @data: Buffer to read/write from
+ *
+ * Users may use %ETHTOOL_GDRVINFO or %ETHTOOL_GMODULEINFO to find
+ * the length of an on-board or module EEPROM, respectively. They
+ * must allocate the buffer immediately following this structure.
+ */
struct ethtool_eeprom {
__u32 cmd;
__u32 magic;
- __u32 offset; /* in bytes */
- __u32 len; /* in bytes */
+ __u32 offset;
+ __u32 len;
__u8 data[0];
};
@@ -229,17 +351,18 @@ struct ethtool_modinfo {
* @rate_sample_interval: How often to do adaptive coalescing packet rate
* sampling, measured in seconds. Must not be zero.
*
- * Each pair of (usecs, max_frames) fields specifies this exit
- * condition for interrupt coalescing:
+ * 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
+ * condition time_since_first_completion >= usecs
+ *
* This is deprecated. Drivers for hardware that does not support
* counting completions should validate that max_frames == !rx_usecs.
*
@@ -279,22 +402,37 @@ struct ethtool_coalesce {
__u32 rate_sample_interval;
};
-/* for configuring RX/TX ring parameters */
+/**
+ * struct ethtool_ringparam - RX/TX ring parameters
+ * @cmd: Command number = %ETHTOOL_GRINGPARAM or %ETHTOOL_SRINGPARAM
+ * @rx_max_pending: Maximum supported number of pending entries per
+ * RX ring. Read-only.
+ * @rx_mini_max_pending: Maximum supported number of pending entries
+ * per RX mini ring. Read-only.
+ * @rx_jumbo_max_pending: Maximum supported number of pending entries
+ * per RX jumbo ring. Read-only.
+ * @tx_max_pending: Maximum supported number of pending entries per
+ * TX ring. Read-only.
+ * @rx_pending: Current maximum number of pending entries per RX ring
+ * @rx_mini_pending: Current maximum number of pending entries per RX
+ * mini ring
+ * @rx_jumbo_pending: Current maximum number of pending entries per RX
+ * jumbo ring
+ * @tx_pending: Current maximum supported number of pending entries
+ * per TX ring
+ *
+ * If the interface does not have separate RX mini and/or jumbo rings,
+ * @rx_mini_max_pending and/or @rx_jumbo_max_pending will be 0.
+ *
+ * There may also be driver-dependent minimum values for the number
+ * of entries per ring.
+ */
struct ethtool_ringparam {
- __u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */
-
- /* Read only attributes. These indicate the maximum number
- * of pending RX/TX ring entries the driver will allow the
- * user to set.
- */
+ __u32 cmd;
__u32 rx_max_pending;
__u32 rx_mini_max_pending;
__u32 rx_jumbo_max_pending;
__u32 tx_max_pending;
-
- /* Values changeable by the user. The valid values are
- * in the range 1 to the "*_max_pending" counterpart above.
- */
__u32 rx_pending;
__u32 rx_mini_pending;
__u32 rx_jumbo_pending;
@@ -329,51 +467,96 @@ struct ethtool_channels {
__u32 combined_count;
};
-/* for configuring link flow control parameters */
+/**
+ * struct ethtool_pauseparam - Ethernet pause (flow control) parameters
+ * @cmd: Command number = %ETHTOOL_GPAUSEPARAM or %ETHTOOL_SPAUSEPARAM
+ * @autoneg: Flag to enable autonegotiation of pause frame use
+ * @rx_pause: Flag to enable reception of pause frames
+ * @tx_pause: Flag to enable transmission of pause frames
+ *
+ * Drivers should reject a non-zero setting of @autoneg when
+ * autoneogotiation is disabled (or not supported) for the link.
+ *
+ * If the link is autonegotiated, drivers should use
+ * mii_advertise_flowctrl() or similar code to set the advertised
+ * pause frame capabilities based on the @rx_pause and @tx_pause flags,
+ * even if @autoneg is zero. They should also allow the advertised
+ * pause frame capabilities to be controlled directly through the
+ * advertising field of &struct ethtool_cmd.
+ *
+ * If @autoneg is non-zero, the MAC is configured to send and/or
+ * receive pause frames according to the result of autonegotiation.
+ * Otherwise, it is configured directly based on the @rx_pause and
+ * @tx_pause flags.
+ */
struct ethtool_pauseparam {
- __u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */
-
- /* If the link is being auto-negotiated (via ethtool_cmd.autoneg
- * being true) the user may set 'autoneg' here non-zero to have the
- * pause parameters be auto-negotiated too. In such a case, the
- * {rx,tx}_pause values below determine what capabilities are
- * advertised.
- *
- * If 'autoneg' is zero or the link is not being auto-negotiated,
- * then {rx,tx}_pause force the driver to use/not-use pause
- * flow control.
- */
+ __u32 cmd;
__u32 autoneg;
__u32 rx_pause;
__u32 tx_pause;
};
#define ETH_GSTRING_LEN 32
+
+/**
+ * enum ethtool_stringset - string set ID
+ * @ETH_SS_TEST: Self-test result names, for use with %ETHTOOL_TEST
+ * @ETH_SS_STATS: Statistic names, for use with %ETHTOOL_GSTATS
+ * @ETH_SS_PRIV_FLAGS: Driver private flag names, for use with
+ * %ETHTOOL_GPFLAGS and %ETHTOOL_SPFLAGS
+ * @ETH_SS_NTUPLE_FILTERS: Previously used with %ETHTOOL_GRXNTUPLE;
+ * now deprecated
+ * @ETH_SS_FEATURES: Device feature names
+ */
enum ethtool_stringset {
ETH_SS_TEST = 0,
ETH_SS_STATS,
ETH_SS_PRIV_FLAGS,
- ETH_SS_NTUPLE_FILTERS, /* Do not use, GRXNTUPLE is now deprecated */
+ ETH_SS_NTUPLE_FILTERS,
ETH_SS_FEATURES,
};
-/* for passing string sets for data tagging */
+/**
+ * struct ethtool_gstrings - string set for data tagging
+ * @cmd: Command number = %ETHTOOL_GSTRINGS
+ * @string_set: String set ID; one of &enum ethtool_stringset
+ * @len: On return, the number of strings in the string set
+ * @data: Buffer for strings. Each string is null-padded to a size of
+ * %ETH_GSTRING_LEN.
+ *
+ * Users must use %ETHTOOL_GSSET_INFO to find the number of strings in
+ * the string set. They must allocate a buffer of the appropriate
+ * size immediately following this structure.
+ */
struct ethtool_gstrings {
- __u32 cmd; /* ETHTOOL_GSTRINGS */
- __u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/
- __u32 len; /* number of strings in the string set */
+ __u32 cmd;
+ __u32 string_set;
+ __u32 len;
__u8 data[0];
};
+/**
+ * struct ethtool_sset_info - string set information
+ * @cmd: Command number = %ETHTOOL_GSSET_INFO
+ * @sset_mask: On entry, a bitmask of string sets to query, with bits
+ * numbered according to &enum ethtool_stringset. On return, a
+ * bitmask of those string sets queried that are supported.
+ * @data: Buffer for string set sizes. On return, this contains the
+ * size of each string set that was queried and supported, in
+ * order of ID.
+ *
+ * Example: The user passes in @sset_mask = 0x7 (sets 0, 1, 2) and on
+ * return @sset_mask == 0x6 (sets 1, 2). Then @data[0] contains the
+ * size of set 1 and @data[1] contains the size of set 2.
+ *
+ * Users must allocate a buffer of the appropriate size (4 * number of
+ * sets queried) immediately following this structure.
+ */
struct ethtool_sset_info {
- __u32 cmd; /* ETHTOOL_GSSET_INFO */
+ __u32 cmd;
__u32 reserved;
- __u64 sset_mask; /* input: each bit selects an sset to query */
- /* output: each bit a returned sset */
- __u32 data[0]; /* ETH_SS_xxx count, in order, based on bits
- in sset_mask. One bit implies one
- __u32, two bits implies two
- __u32's, etc. */
+ __u64 sset_mask;
+ __u32 data[0];
};
/**
@@ -393,24 +576,58 @@ enum ethtool_test_flags {
ETH_TEST_FL_EXTERNAL_LB_DONE = (1 << 3),
};
-/* for requesting NIC test and getting results*/
+/**
+ * struct ethtool_test - device self-test invocation
+ * @cmd: Command number = %ETHTOOL_TEST
+ * @flags: A bitmask of flags from &enum ethtool_test_flags. Some
+ * flags may be set by the user on entry; others may be set by
+ * the driver on return.
+ * @len: On return, the number of test results
+ * @data: Array of test results
+ *
+ * Users must use %ETHTOOL_GSSET_INFO or %ETHTOOL_GDRVINFO to find the
+ * number of test results that will be returned. They must allocate a
+ * buffer of the appropriate size (8 * number of results) immediately
+ * following this structure.
+ */
struct ethtool_test {
- __u32 cmd; /* ETHTOOL_TEST */
- __u32 flags; /* ETH_TEST_FL_xxx */
+ __u32 cmd;
+ __u32 flags;
__u32 reserved;
- __u32 len; /* result length, in number of u64 elements */
+ __u32 len;
__u64 data[0];
};
-/* for dumping NIC-specific statistics */
+/**
+ * struct ethtool_stats - device-specific statistics
+ * @cmd: Command number = %ETHTOOL_GSTATS
+ * @n_stats: On return, the number of statistics
+ * @data: Array of statistics
+ *
+ * Users must use %ETHTOOL_GSSET_INFO or %ETHTOOL_GDRVINFO to find the
+ * number of statistics that will be returned. They must allocate a
+ * buffer of the appropriate size (8 * number of statistics)
+ * immediately following this structure.
+ */
struct ethtool_stats {
- __u32 cmd; /* ETHTOOL_GSTATS */
- __u32 n_stats; /* number of u64's being returned */
+ __u32 cmd;
+ __u32 n_stats;
__u64 data[0];
};
+/**
+ * struct ethtool_perm_addr - permanent hardware address
+ * @cmd: Command number = %ETHTOOL_GPERMADDR
+ * @size: On entry, the size of the buffer. On return, the size of the
+ * address. The command fails if the buffer is too small.
+ * @data: Buffer for the address
+ *
+ * Users must allocate the buffer immediately following this structure.
+ * A buffer size of %MAX_ADDR_LEN should be sufficient for any address
+ * type.
+ */
struct ethtool_perm_addr {
- __u32 cmd; /* ETHTOOL_GPERMADDR */
+ __u32 cmd;
__u32 size;
__u8 data[0];
};
@@ -593,7 +810,7 @@ struct ethtool_rx_flow_spec {
* %ETHTOOL_SRXCLSRLINS may add the rule at any suitable unused
* location, and may remove a rule at a later location (lower
* priority) that matches exactly the same set of flows. The special
- * values are: %RX_CLS_LOC_ANY, selecting any location;
+ * values are %RX_CLS_LOC_ANY, selecting any location;
* %RX_CLS_LOC_FIRST, selecting the first suitable location (maximum
* priority); and %RX_CLS_LOC_LAST, selecting the last suitable
* location (minimum priority). Additional special values may be
@@ -704,9 +921,6 @@ struct ethtool_flash {
* for %ETHTOOL_GET_DUMP_FLAG command
* @data: data collected for get dump data operation
*/
-
-#define ETH_FW_DUMP_DISABLE 0
-
struct ethtool_dump {
__u32 cmd;
__u32 version;
@@ -715,6 +929,8 @@ struct ethtool_dump {
__u8 data[0];
};
+#define ETH_FW_DUMP_DISABLE 0
+
/* for returning and changing feature sets */
/**
@@ -734,8 +950,9 @@ struct ethtool_get_features_block {
/**
* struct ethtool_gfeatures - command to get state of device's features
* @cmd: command number = %ETHTOOL_GFEATURES
- * @size: in: number of elements in the features[] array;
- * out: number of elements in features[] needed to hold all features
+ * @size: On entry, the number of elements in the features[] array;
+ * on return, the number of elements in features[] needed to hold
+ * all features
* @features: state of features
*/
struct ethtool_gfeatures {
@@ -905,7 +1122,6 @@ enum ethtool_sfeatures_retval_bits {
#define SPARC_ETH_GSET ETHTOOL_GSET
#define SPARC_ETH_SSET ETHTOOL_SSET
-/* Indicates what features are supported by the interface. */
#define SUPPORTED_10baseT_Half (1 << 0)
#define SUPPORTED_10baseT_Full (1 << 1)
#define SUPPORTED_100baseT_Half (1 << 2)
@@ -934,7 +1150,6 @@ enum ethtool_sfeatures_retval_bits {
#define SUPPORTED_40000baseSR4_Full (1 << 25)
#define SUPPORTED_40000baseLR4_Full (1 << 26)
-/* Indicates what features are advertised by the interface. */
#define ADVERTISED_10baseT_Half (1 << 0)
#define ADVERTISED_10baseT_Full (1 << 1)
#define ADVERTISED_100baseT_Half (1 << 2)
@@ -999,9 +1214,7 @@ enum ethtool_sfeatures_retval_bits {
#define XCVR_DUMMY2 0x03
#define XCVR_DUMMY3 0x04
-/* Enable or disable autonegotiation. If this is set to enable,
- * the forced link modes above are completely ignored.
- */
+/* Enable or disable autonegotiation. */
#define AUTONEG_DISABLE 0x00
#define AUTONEG_ENABLE 0x01
diff --git a/include/uapi/linux/falloc.h b/include/uapi/linux/falloc.h
index 990c4ccf8b61..d1197ae3723c 100644
--- a/include/uapi/linux/falloc.h
+++ b/include/uapi/linux/falloc.h
@@ -5,5 +5,40 @@
#define FALLOC_FL_PUNCH_HOLE 0x02 /* de-allocates range */
#define FALLOC_FL_NO_HIDE_STALE 0x04 /* reserved codepoint */
+/*
+ * FALLOC_FL_COLLAPSE_RANGE is used to remove a range of a file
+ * without leaving a hole in the file. The contents of the file beyond
+ * the range being removed is appended to the start offset of the range
+ * being removed (i.e. the hole that was punched is "collapsed"),
+ * resulting in a file layout that looks like the range that was
+ * removed never existed. As such collapsing a range of a file changes
+ * the size of the file, reducing it by the same length of the range
+ * that has been removed by the operation.
+ *
+ * Different filesystems may implement different limitations on the
+ * granularity of the operation. Most will limit operations to
+ * filesystem block size boundaries, but this boundary may be larger or
+ * smaller depending on the filesystem and/or the configuration of the
+ * filesystem or file.
+ *
+ * Attempting to collapse a range that crosses the end of the file is
+ * considered an illegal operation - just use ftruncate(2) if you need
+ * to collapse a range that crosses EOF.
+ */
+#define FALLOC_FL_COLLAPSE_RANGE 0x08
+
+/*
+ * FALLOC_FL_ZERO_RANGE is used to convert a range of file to zeros preferably
+ * without issuing data IO. Blocks should be preallocated for the regions that
+ * span holes in the file, and the entire range is preferable converted to
+ * unwritten extents - even though file system may choose to zero out the
+ * extent or do whatever which will result in reading zeros from the range
+ * while the range remains allocated for the file.
+ *
+ * This can be also used to preallocate blocks past EOF in the same way as
+ * with fallocate. Flag FALLOC_FL_KEEP_SIZE should cause the inode
+ * size to remain the same.
+ */
+#define FALLOC_FL_ZERO_RANGE 0x10
#endif /* _UAPI_FALLOC_H_ */
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 6c28b61bb690..ca1a11bb4443 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -35,6 +35,9 @@
#define SEEK_HOLE 4 /* seek to the next hole */
#define SEEK_MAX SEEK_HOLE
+#define RENAME_NOREPLACE (1 << 0) /* Don't overwrite target */
+#define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */
+
struct fstrim_range {
__u64 start;
__u64 len;
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 60bb2f9f7b74..cf4750e1bb49 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -93,6 +93,9 @@
*
* 7.22
* - add FUSE_ASYNC_DIO
+ *
+ * 7.23
+ * - add FUSE_WRITEBACK_CACHE
*/
#ifndef _LINUX_FUSE_H
@@ -128,7 +131,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 22
+#define FUSE_KERNEL_MINOR_VERSION 23
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -219,6 +222,7 @@ struct fuse_file_lock {
* FUSE_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one)
* FUSE_READDIRPLUS_AUTO: adaptive readdirplus
* FUSE_ASYNC_DIO: asynchronous direct I/O submission
+ * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -236,6 +240,7 @@ struct fuse_file_lock {
#define FUSE_DO_READDIRPLUS (1 << 13)
#define FUSE_READDIRPLUS_AUTO (1 << 14)
#define FUSE_ASYNC_DIO (1 << 15)
+#define FUSE_WRITEBACK_CACHE (1 << 16)
/**
* CUSE INIT request/reply flags
diff --git a/include/uapi/linux/gfs2_ondisk.h b/include/uapi/linux/gfs2_ondisk.h
index 0f24c07aed51..db3fdd083882 100644
--- a/include/uapi/linux/gfs2_ondisk.h
+++ b/include/uapi/linux/gfs2_ondisk.h
@@ -304,7 +304,13 @@ struct gfs2_dirent {
__be16 de_rec_len;
__be16 de_name_len;
__be16 de_type;
- __u8 __pad[14];
+ union {
+ __u8 __pad[14];
+ struct {
+ __be16 de_rahead;
+ __u8 pad2[12];
+ };
+ };
};
/*
@@ -347,9 +353,9 @@ struct gfs2_leaf {
* metadata header. Each inode, if it has extended attributes, will
* have either a single block containing the extended attribute headers
* or a single indirect block pointing to blocks containing the
- * extended attribure headers.
+ * extended attribute headers.
*
- * The maximim size of the data part of an extended attribute is 64k
+ * The maximum size of the data part of an extended attribute is 64k
* so the number of blocks required depends upon block size. Since the
* block size also determines the number of pointers in an indirect
* block, its a fairly complicated calculation to work out the maximum
diff --git a/include/uapi/linux/if.h b/include/uapi/linux/if.h
index d758163b0e43..9cf2394f0bcf 100644
--- a/include/uapi/linux/if.h
+++ b/include/uapi/linux/if.h
@@ -27,65 +27,91 @@
#define IFALIASZ 256
#include <linux/hdlc/ioctl.h>
-/* Standard interface flags (netdevice->flags). */
-#define IFF_UP 0x1 /* interface is up */
-#define IFF_BROADCAST 0x2 /* broadcast address valid */
-#define IFF_DEBUG 0x4 /* turn on debugging */
-#define IFF_LOOPBACK 0x8 /* is a loopback net */
-#define IFF_POINTOPOINT 0x10 /* interface is has p-p link */
-#define IFF_NOTRAILERS 0x20 /* avoid use of trailers */
-#define IFF_RUNNING 0x40 /* interface RFC2863 OPER_UP */
-#define IFF_NOARP 0x80 /* no ARP protocol */
-#define IFF_PROMISC 0x100 /* receive all packets */
-#define IFF_ALLMULTI 0x200 /* receive all multicast packets*/
-
-#define IFF_MASTER 0x400 /* master of a load balancer */
-#define IFF_SLAVE 0x800 /* slave of a load balancer */
-
-#define IFF_MULTICAST 0x1000 /* Supports multicast */
-
-#define IFF_PORTSEL 0x2000 /* can set media type */
-#define IFF_AUTOMEDIA 0x4000 /* auto media select active */
-#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/
-
-#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
-#define IFF_DORMANT 0x20000 /* driver signals dormant */
+/**
+ * enum net_device_flags - &struct net_device flags
+ *
+ * These are the &struct net_device flags, they can be set by drivers, the
+ * kernel and some can be triggered by userspace. Userspace can query and
+ * set these flags using userspace utilities but there is also a sysfs
+ * entry available for all dev flags which can be queried and set. These flags
+ * are shared for all types of net_devices. The sysfs entries are available
+ * via /sys/class/net/<dev>/flags. Flags which can be toggled through sysfs
+ * are annotated below, note that only a few flags can be toggled and some
+ * other flags are always always preserved from the original net_device flags
+ * even if you try to set them via sysfs. Flags which are always preserved
+ * are kept under the flag grouping @IFF_VOLATILE. Flags which are volatile
+ * are annotated below as such.
+ *
+ * You should have a pretty good reason to be extending these flags.
+ *
+ * @IFF_UP: interface is up. Can be toggled through sysfs.
+ * @IFF_BROADCAST: broadcast address valid. Volatile.
+ * @IFF_DEBUG: turn on debugging. Can be toggled through sysfs.
+ * @IFF_LOOPBACK: is a loopback net. Volatile.
+ * @IFF_POINTOPOINT: interface is has p-p link. Volatile.
+ * @IFF_NOTRAILERS: avoid use of trailers. Can be toggled through sysfs.
+ * Volatile.
+ * @IFF_RUNNING: interface RFC2863 OPER_UP. Volatile.
+ * @IFF_NOARP: no ARP protocol. Can be toggled through sysfs. Volatile.
+ * @IFF_PROMISC: receive all packets. Can be toggled through sysfs.
+ * @IFF_ALLMULTI: receive all multicast packets. Can be toggled through
+ * sysfs.
+ * @IFF_MASTER: master of a load balancer. Volatile.
+ * @IFF_SLAVE: slave of a load balancer. Volatile.
+ * @IFF_MULTICAST: Supports multicast. Can be toggled through sysfs.
+ * @IFF_PORTSEL: can set media type. Can be toggled through sysfs.
+ * @IFF_AUTOMEDIA: auto media select active. Can be toggled through sysfs.
+ * @IFF_DYNAMIC: dialup device with changing addresses. Can be toggled
+ * through sysfs.
+ * @IFF_LOWER_UP: driver signals L1 up. Volatile.
+ * @IFF_DORMANT: driver signals dormant. Volatile.
+ * @IFF_ECHO: echo sent packets. Volatile.
+ */
+enum net_device_flags {
+ IFF_UP = 1<<0, /* sysfs */
+ IFF_BROADCAST = 1<<1, /* volatile */
+ IFF_DEBUG = 1<<2, /* sysfs */
+ IFF_LOOPBACK = 1<<3, /* volatile */
+ IFF_POINTOPOINT = 1<<4, /* volatile */
+ IFF_NOTRAILERS = 1<<5, /* sysfs */
+ IFF_RUNNING = 1<<6, /* volatile */
+ IFF_NOARP = 1<<7, /* sysfs */
+ IFF_PROMISC = 1<<8, /* sysfs */
+ IFF_ALLMULTI = 1<<9, /* sysfs */
+ IFF_MASTER = 1<<10, /* volatile */
+ IFF_SLAVE = 1<<11, /* volatile */
+ IFF_MULTICAST = 1<<12, /* sysfs */
+ IFF_PORTSEL = 1<<13, /* sysfs */
+ IFF_AUTOMEDIA = 1<<14, /* sysfs */
+ IFF_DYNAMIC = 1<<15, /* sysfs */
+ IFF_LOWER_UP = 1<<16, /* volatile */
+ IFF_DORMANT = 1<<17, /* volatile */
+ IFF_ECHO = 1<<18, /* volatile */
+};
-#define IFF_ECHO 0x40000 /* echo sent packets */
+#define IFF_UP IFF_UP
+#define IFF_BROADCAST IFF_BROADCAST
+#define IFF_DEBUG IFF_DEBUG
+#define IFF_LOOPBACK IFF_LOOPBACK
+#define IFF_POINTOPOINT IFF_POINTOPOINT
+#define IFF_NOTRAILERS IFF_NOTRAILERS
+#define IFF_RUNNING IFF_RUNNING
+#define IFF_NOARP IFF_NOARP
+#define IFF_PROMISC IFF_PROMISC
+#define IFF_ALLMULTI IFF_ALLMULTI
+#define IFF_MASTER IFF_MASTER
+#define IFF_SLAVE IFF_SLAVE
+#define IFF_MULTICAST IFF_MULTICAST
+#define IFF_PORTSEL IFF_PORTSEL
+#define IFF_AUTOMEDIA IFF_AUTOMEDIA
+#define IFF_DYNAMIC IFF_DYNAMIC
+#define IFF_LOWER_UP IFF_LOWER_UP
+#define IFF_DORMANT IFF_DORMANT
+#define IFF_ECHO IFF_ECHO
#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
-/* Private (from user) interface flags (netdevice->priv_flags). */
-#define IFF_802_1Q_VLAN 0x1 /* 802.1Q VLAN device. */
-#define IFF_EBRIDGE 0x2 /* Ethernet bridging device. */
-#define IFF_SLAVE_INACTIVE 0x4 /* bonding slave not the curr. active */
-#define IFF_MASTER_8023AD 0x8 /* bonding master, 802.3ad. */
-#define IFF_MASTER_ALB 0x10 /* bonding master, balance-alb. */
-#define IFF_BONDING 0x20 /* bonding master or slave */
-#define IFF_SLAVE_NEEDARP 0x40 /* need ARPs for validation */
-#define IFF_ISATAP 0x80 /* ISATAP interface (RFC4214) */
-#define IFF_MASTER_ARPMON 0x100 /* bonding master, ARP mon in use */
-#define IFF_WAN_HDLC 0x200 /* WAN HDLC device */
-#define IFF_XMIT_DST_RELEASE 0x400 /* dev_hard_start_xmit() is allowed to
- * release skb->dst
- */
-#define IFF_DONT_BRIDGE 0x800 /* disallow bridging this ether dev */
-#define IFF_DISABLE_NETPOLL 0x1000 /* disable netpoll at run-time */
-#define IFF_MACVLAN_PORT 0x2000 /* device used as macvlan port */
-#define IFF_BRIDGE_PORT 0x4000 /* device used as bridge port */
-#define IFF_OVS_DATAPATH 0x8000 /* device used as Open vSwitch
- * datapath port */
-#define IFF_TX_SKB_SHARING 0x10000 /* The interface supports sharing
- * skbs on transmit */
-#define IFF_UNICAST_FLT 0x20000 /* Supports unicast filtering */
-#define IFF_TEAM_PORT 0x40000 /* device used as team port */
-#define IFF_SUPP_NOFCS 0x80000 /* device supports sending custom FCS */
-#define IFF_LIVE_ADDR_CHANGE 0x100000 /* device supports hardware address
- * change when it's running */
-#define IFF_MACVLAN 0x200000 /* Macvlan device */
-
-
#define IF_GET_IFACE 0x0001 /* for querying only */
#define IF_GET_PROTO 0x0002
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index 2ce0f6a78fa5..0f8210b8e0bc 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -68,11 +68,11 @@
#define ETH_P_SLOW 0x8809 /* Slow Protocol. See 802.3ad 43B */
#define ETH_P_WCCP 0x883E /* Web-cache coordination protocol
* defined in draft-wilson-wrec-wccp-v2-00.txt */
-#define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */
-#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */
#define ETH_P_MPLS_UC 0x8847 /* MPLS Unicast traffic */
#define ETH_P_MPLS_MC 0x8848 /* MPLS Multicast traffic */
#define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */
+#define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */
+#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */
#define ETH_P_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */
#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport
* over Ethernet
@@ -89,6 +89,8 @@
#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */
#define ETH_P_TDLS 0x890D /* TDLS */
#define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */
+#define ETH_P_80221 0x8917 /* IEEE 802.21 Media Independent Handover Protocol */
+#define ETH_P_LOOPBACK 0x9000 /* Ethernet loopback packet, per IEEE 802.3 */
#define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 16410b6e7819..9a7f7ace6649 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -144,6 +144,7 @@ enum {
IFLA_NUM_RX_QUEUES,
IFLA_CARRIER,
IFLA_PHYS_PORT_ID,
+ IFLA_CARRIER_CHANGES,
__IFLA_MAX
};
diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h
index 393c5de09d42..c33a65e3d62c 100644
--- a/include/uapi/linux/in.h
+++ b/include/uapi/linux/in.h
@@ -120,6 +120,10 @@ struct in_addr {
* this socket to prevent accepting spoofed ones.
*/
#define IP_PMTUDISC_INTERFACE 4
+/* weaker version of IP_PMTUDISC_INTERFACE, which allos packets to get
+ * fragmented if they exeed the interface mtu
+ */
+#define IP_PMTUDISC_OMIT 5
#define IP_MULTICAST_IF 32
#define IP_MULTICAST_TTL 33
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index e9a1d2d973b6..0d8e0f0342dc 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -185,6 +185,10 @@ struct in6_flowlabel_req {
* also see comments on IP_PMTUDISC_INTERFACE
*/
#define IPV6_PMTUDISC_INTERFACE 4
+/* weaker version of IPV6_PMTUDISC_INTERFACE, which allows packets to
+ * get fragmented if they exceed the interface mtu
+ */
+#define IPV6_PMTUDISC_OMIT 5
/* Flowlabel */
#define IPV6_FLOWLABEL_MGR 32
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 932d7f2637d6..a8f4ee5d2e82 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -413,6 +413,8 @@ struct kvm_s390_psw {
#define KVM_S390_PROGRAM_INT 0xfffe0001u
#define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u
#define KVM_S390_RESTART 0xfffe0003u
+#define KVM_S390_INT_PFAULT_INIT 0xfffe0004u
+#define KVM_S390_INT_PFAULT_DONE 0xfffe0005u
#define KVM_S390_MCHK 0xfffe1000u
#define KVM_S390_INT_VIRTIO 0xffff2603u
#define KVM_S390_INT_SERVICE 0xffff2401u
@@ -434,6 +436,69 @@ struct kvm_s390_interrupt {
__u64 parm64;
};
+struct kvm_s390_io_info {
+ __u16 subchannel_id;
+ __u16 subchannel_nr;
+ __u32 io_int_parm;
+ __u32 io_int_word;
+};
+
+struct kvm_s390_ext_info {
+ __u32 ext_params;
+ __u32 pad;
+ __u64 ext_params2;
+};
+
+struct kvm_s390_pgm_info {
+ __u64 trans_exc_code;
+ __u64 mon_code;
+ __u64 per_address;
+ __u32 data_exc_code;
+ __u16 code;
+ __u16 mon_class_nr;
+ __u8 per_code;
+ __u8 per_atmid;
+ __u8 exc_access_id;
+ __u8 per_access_id;
+ __u8 op_access_id;
+ __u8 pad[3];
+};
+
+struct kvm_s390_prefix_info {
+ __u32 address;
+};
+
+struct kvm_s390_extcall_info {
+ __u16 code;
+};
+
+struct kvm_s390_emerg_info {
+ __u16 code;
+};
+
+struct kvm_s390_mchk_info {
+ __u64 cr14;
+ __u64 mcic;
+ __u64 failing_storage_address;
+ __u32 ext_damage_code;
+ __u32 pad;
+ __u8 fixed_logout[16];
+};
+
+struct kvm_s390_irq {
+ __u64 type;
+ union {
+ struct kvm_s390_io_info io;
+ struct kvm_s390_ext_info ext;
+ struct kvm_s390_pgm_info pgm;
+ struct kvm_s390_emerg_info emerg;
+ struct kvm_s390_extcall_info extcall;
+ struct kvm_s390_prefix_info prefix;
+ struct kvm_s390_mchk_info mchk;
+ char reserved[64];
+ } u;
+};
+
/* for KVM_SET_GUEST_DEBUG */
#define KVM_GUESTDBG_ENABLE 0x00000001
@@ -675,6 +740,9 @@ struct kvm_ppc_smmu_info {
#define KVM_CAP_SPAPR_MULTITCE 94
#define KVM_CAP_EXT_EMUL_CPUID 95
#define KVM_CAP_HYPERV_TIME 96
+#define KVM_CAP_IOAPIC_POLARITY_IGNORED 97
+#define KVM_CAP_ENABLE_CAP_VM 98
+#define KVM_CAP_S390_IRQCHIP 99
#ifdef KVM_CAP_IRQ_ROUTING
@@ -690,9 +758,18 @@ struct kvm_irq_routing_msi {
__u32 pad;
};
+struct kvm_irq_routing_s390_adapter {
+ __u64 ind_addr;
+ __u64 summary_addr;
+ __u64 ind_offset;
+ __u32 summary_offset;
+ __u32 adapter_id;
+};
+
/* gsi routing entry types */
#define KVM_IRQ_ROUTING_IRQCHIP 1
#define KVM_IRQ_ROUTING_MSI 2
+#define KVM_IRQ_ROUTING_S390_ADAPTER 3
struct kvm_irq_routing_entry {
__u32 gsi;
@@ -702,6 +779,7 @@ struct kvm_irq_routing_entry {
union {
struct kvm_irq_routing_irqchip irqchip;
struct kvm_irq_routing_msi msi;
+ struct kvm_irq_routing_s390_adapter adapter;
__u32 pad[8];
} u;
};
@@ -855,6 +933,7 @@ struct kvm_device_attr {
#define KVM_DEV_VFIO_GROUP_ADD 1
#define KVM_DEV_VFIO_GROUP_DEL 2
#define KVM_DEV_TYPE_ARM_VGIC_V2 5
+#define KVM_DEV_TYPE_FLIC 6
/*
* ioctls for VM fds
@@ -1009,6 +1088,10 @@ struct kvm_s390_ucas_mapping {
/* Available with KVM_CAP_DEBUGREGS */
#define KVM_GET_DEBUGREGS _IOR(KVMIO, 0xa1, struct kvm_debugregs)
#define KVM_SET_DEBUGREGS _IOW(KVMIO, 0xa2, struct kvm_debugregs)
+/*
+ * vcpu version available with KVM_ENABLE_CAP
+ * vm version available with KVM_CAP_ENABLE_CAP_VM
+ */
#define KVM_ENABLE_CAP _IOW(KVMIO, 0xa3, struct kvm_enable_cap)
/* Available with KVM_CAP_XSAVE */
#define KVM_GET_XSAVE _IOR(KVMIO, 0xa4, struct kvm_xsave)
diff --git a/include/uapi/linux/libc-compat.h b/include/uapi/linux/libc-compat.h
index 335e8a7cad39..c140620dad92 100644
--- a/include/uapi/linux/libc-compat.h
+++ b/include/uapi/linux/libc-compat.h
@@ -85,6 +85,12 @@
#endif /* _NETINET_IN_H */
+/* Definitions for xattr.h */
+#if defined(_SYS_XATTR_H)
+#define __UAPI_DEF_XATTR 0
+#else
+#define __UAPI_DEF_XATTR 1
+#endif
/* If we did not see any headers from any supported C libraries,
* or we are being included in the kernel, then define everything
@@ -98,6 +104,9 @@
#define __UAPI_DEF_IPV6_MREQ 1
#define __UAPI_DEF_IPPROTO_V6 1
+/* Definitions for xattr.h */
+#define __UAPI_DEF_XATTR 1
+
#endif /* __GLIBC__ */
#endif /* _UAPI_LIBC_COMPAT_H */
diff --git a/include/uapi/linux/mpls.h b/include/uapi/linux/mpls.h
new file mode 100644
index 000000000000..bc9abfe88c9a
--- /dev/null
+++ b/include/uapi/linux/mpls.h
@@ -0,0 +1,34 @@
+#ifndef _UAPI_MPLS_H
+#define _UAPI_MPLS_H
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+/* Reference: RFC 5462, RFC 3032
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Label | TC |S| TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Label: Label Value, 20 bits
+ * TC: Traffic Class field, 3 bits
+ * S: Bottom of Stack, 1 bit
+ * TTL: Time to Live, 8 bits
+ */
+
+struct mpls_label {
+ __be32 entry;
+};
+
+#define MPLS_LS_LABEL_MASK 0xFFFFF000
+#define MPLS_LS_LABEL_SHIFT 12
+#define MPLS_LS_TC_MASK 0x00000E00
+#define MPLS_LS_TC_SHIFT 9
+#define MPLS_LS_S_MASK 0x00000100
+#define MPLS_LS_S_SHIFT 8
+#define MPLS_LS_TTL_MASK 0x000000FF
+#define MPLS_LS_TTL_SHIFT 0
+
+#endif /* _UAPI_MPLS_H */
diff --git a/include/uapi/linux/netdevice.h b/include/uapi/linux/netdevice.h
index 6b9500bc2d56..fdfbd1c17065 100644
--- a/include/uapi/linux/netdevice.h
+++ b/include/uapi/linux/netdevice.h
@@ -49,5 +49,11 @@ enum {
IF_PORT_100BASEFX
};
+/* hardware address assignment types */
+#define NET_ADDR_PERM 0 /* address is permanent (default) */
+#define NET_ADDR_RANDOM 1 /* address is generated randomly */
+#define NET_ADDR_STOLEN 2 /* address is stolen from other device */
+#define NET_ADDR_SET 3 /* address is set using
+ * dev_set_mac_address() */
#endif /* _UAPI_LINUX_NETDEVICE_H */
diff --git a/include/uapi/linux/netfilter/ipset/ip_set.h b/include/uapi/linux/netfilter/ipset/ip_set.h
index 25d3b2f79c02..78c2f2e79920 100644
--- a/include/uapi/linux/netfilter/ipset/ip_set.h
+++ b/include/uapi/linux/netfilter/ipset/ip_set.h
@@ -82,6 +82,8 @@ enum {
IPSET_ATTR_PROTO, /* 7 */
IPSET_ATTR_CADT_FLAGS, /* 8 */
IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */
+ IPSET_ATTR_MARK, /* 10 */
+ IPSET_ATTR_MARKMASK, /* 11 */
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
/* Create-only specific attributes */
@@ -144,6 +146,7 @@ enum ipset_errno {
IPSET_ERR_IPADDR_IPV6,
IPSET_ERR_COUNTER,
IPSET_ERR_COMMENT,
+ IPSET_ERR_INVALID_MARKMASK,
/* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 4352,
@@ -182,9 +185,18 @@ enum ipset_cadt_flags {
IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS),
IPSET_FLAG_BIT_WITH_COMMENT = 4,
IPSET_FLAG_WITH_COMMENT = (1 << IPSET_FLAG_BIT_WITH_COMMENT),
+ IPSET_FLAG_BIT_WITH_FORCEADD = 5,
+ IPSET_FLAG_WITH_FORCEADD = (1 << IPSET_FLAG_BIT_WITH_FORCEADD),
IPSET_FLAG_CADT_MAX = 15,
};
+/* The flag bits which correspond to the non-extension create flags */
+enum ipset_create_flags {
+ IPSET_CREATE_FLAG_BIT_FORCEADD = 0,
+ IPSET_CREATE_FLAG_FORCEADD = (1 << IPSET_CREATE_FLAG_BIT_FORCEADD),
+ IPSET_CREATE_FLAG_BIT_MAX = 7,
+};
+
/* Commands with settype-specific attributes */
enum ipset_adt {
IPSET_ADD,
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 83c985a6170b..c88ccbfda5f1 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -1,7 +1,8 @@
#ifndef _LINUX_NF_TABLES_H
#define _LINUX_NF_TABLES_H
-#define NFT_CHAIN_MAXNAMELEN 32
+#define NFT_CHAIN_MAXNAMELEN 32
+#define NFT_USERDATA_MAXLEN 256
enum nft_registers {
NFT_REG_VERDICT,
@@ -156,6 +157,7 @@ enum nft_chain_attributes {
* @NFTA_RULE_EXPRESSIONS: list of expressions (NLA_NESTED: nft_expr_attributes)
* @NFTA_RULE_COMPAT: compatibility specifications of the rule (NLA_NESTED: nft_rule_compat_attributes)
* @NFTA_RULE_POSITION: numeric handle of the previous rule (NLA_U64)
+ * @NFTA_RULE_USERDATA: user data (NLA_BINARY, NFT_USERDATA_MAXLEN)
*/
enum nft_rule_attributes {
NFTA_RULE_UNSPEC,
@@ -165,6 +167,7 @@ enum nft_rule_attributes {
NFTA_RULE_EXPRESSIONS,
NFTA_RULE_COMPAT,
NFTA_RULE_POSITION,
+ NFTA_RULE_USERDATA,
__NFTA_RULE_MAX
};
#define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1)
@@ -601,6 +604,7 @@ enum nft_ct_keys {
NFT_CT_PROTOCOL,
NFT_CT_PROTO_SRC,
NFT_CT_PROTO_DST,
+ NFT_CT_LABELS,
};
/**
diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h
index 6ad6cc03ccd3..9789dc95b6a8 100644
--- a/include/uapi/linux/nfc.h
+++ b/include/uapi/linux/nfc.h
@@ -150,6 +150,8 @@ enum nfc_commands {
* @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED)
* @NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS: Firmware download operation status
* @NFC_ATTR_APDU: Secure element APDU
+ * @NFC_ATTR_TARGET_ISO15693_DSFID: ISO 15693 Data Storage Format Identifier
+ * @NFC_ATTR_TARGET_ISO15693_UID: ISO 15693 Unique Identifier
*/
enum nfc_attrs {
NFC_ATTR_UNSPEC,
@@ -178,6 +180,8 @@ enum nfc_attrs {
NFC_ATTR_SE_AID,
NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS,
NFC_ATTR_SE_APDU,
+ NFC_ATTR_TARGET_ISO15693_DSFID,
+ NFC_ATTR_TARGET_ISO15693_UID,
/* private: internal use only */
__NFC_ATTR_AFTER_LAST
};
@@ -200,6 +204,7 @@ enum nfc_sdp_attr {
#define NFC_SENSF_RES_MAXSIZE 18
#define NFC_GB_MAXSIZE 48
#define NFC_FIRMWARE_NAME_MAXSIZE 32
+#define NFC_ISO15693_UID_MAXSIZE 8
/* NFC protocols */
#define NFC_PROTO_JEWEL 1
@@ -208,8 +213,9 @@ enum nfc_sdp_attr {
#define NFC_PROTO_ISO14443 4
#define NFC_PROTO_NFC_DEP 5
#define NFC_PROTO_ISO14443_B 6
+#define NFC_PROTO_ISO15693 7
-#define NFC_PROTO_MAX 7
+#define NFC_PROTO_MAX 8
/* NFC communication modes */
#define NFC_COMM_ACTIVE 0
@@ -227,6 +233,7 @@ enum nfc_sdp_attr {
#define NFC_PROTO_ISO14443_MASK (1 << NFC_PROTO_ISO14443)
#define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP)
#define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B)
+#define NFC_PROTO_ISO15693_MASK (1 << NFC_PROTO_ISO15693)
/* NFC Secure Elements */
#define NFC_SE_UICC 0x1
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 91054fd660e0..1ba9d626aa83 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -303,8 +303,9 @@
* passed, all channels allowed for the current regulatory domain
* are used. Extra IEs can also be passed from the userspace by
* using the %NL80211_ATTR_IE attribute.
- * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT
- * if scheduled scan is not running.
+ * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
+ * scheduled scan is not running. The caller may assume that as soon
+ * as the call returns, it is safe to start a new scheduled scan again.
* @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan
* results available.
* @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has
@@ -418,8 +419,18 @@
* %NL80211_ATTR_SSID attribute, and can optionally specify the association
* IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
* %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
- * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
- * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and
+ * %NL80211_ATTR_WIPHY_FREQ_HINT.
+ * If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
+ * restrictions on BSS selection, i.e., they effectively prevent roaming
+ * within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT
+ * can be included to provide a recommendation of the initial BSS while
+ * allowing the driver to roam to other BSSes within the ESS and also to
+ * ignore this recommendation if the indicated BSS is not ideal. Only one
+ * set of BSSID,frequency parameters is used (i.e., either the enforcing
+ * %NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict
+ * %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT).
* Background scan period can optionally be
* specified in %NL80211_ATTR_BG_SCAN_PERIOD,
* if not specified default background scan configuration
@@ -1555,6 +1566,19 @@ enum nl80211_commands {
* data is in the format defined for the payload of the QoS Map Set element
* in IEEE Std 802.11-2012, 8.4.2.97.
*
+ * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS
+ * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS
+ *
+ * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many
+ * associated stations are supported in AP mode (including P2P GO); u32.
+ * Since drivers may not have a fixed limit on the maximum number (e.g.,
+ * other concurrent operations may affect this), drivers are allowed to
+ * advertise values that cannot always be met. In such cases, an attempt
+ * to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
+ *
+ * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
+ * As specified in the &enum nl80211_tdls_peer_capability.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1883,6 +1907,13 @@ enum nl80211_attrs {
NL80211_ATTR_QOS_MAP,
+ NL80211_ATTR_MAC_HINT,
+ NL80211_ATTR_WIPHY_FREQ_HINT,
+
+ NL80211_ATTR_MAX_AP_ASSOC_STA,
+
+ NL80211_ATTR_TDLS_PEER_CAPABILITY,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -2304,6 +2335,7 @@ enum nl80211_band_attr {
* @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel
* using this channel as the primary or any of the secondary channels
* isn't possible
+ * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -2322,6 +2354,7 @@ enum nl80211_frequency_attr {
NL80211_FREQUENCY_ATTR_NO_HT40_PLUS,
NL80211_FREQUENCY_ATTR_NO_80MHZ,
NL80211_FREQUENCY_ATTR_NO_160MHZ,
+ NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -2412,12 +2445,14 @@ enum nl80211_reg_type {
* in KHz. This is not a center a frequency but an actual regulatory
* band edge.
* @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
- * frequency range, in KHz.
+ * frequency range, in KHz.
* @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
* for a given frequency range. The value is in mBi (100 * dBi).
* If you don't have one then don't send this.
* @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
* a given frequency range. The value is in mBm (100 * dBm).
+ * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * If not present or 0 default CAC time will be used.
* @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
* currently defined
* @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
@@ -2433,6 +2468,8 @@ enum nl80211_reg_rule_attr {
NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
NL80211_ATTR_POWER_RULE_MAX_EIRP,
+ NL80211_ATTR_DFS_CAC_TIME,
+
/* keep last */
__NL80211_REG_RULE_ATTR_AFTER_LAST,
NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
@@ -2442,9 +2479,15 @@ enum nl80211_reg_rule_attr {
* enum nl80211_sched_scan_match_attr - scheduled scan match attributes
* @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
* @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
- * only report BSS with matching SSID.
+ * only report BSS with matching SSID.
* @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
- * BSS in scan results. Filtering is turned off if not specified.
+ * BSS in scan results. Filtering is turned off if not specified. Note that
+ * if this attribute is in a match set of its own, then it is treated as
+ * the default value for all matchsets with an SSID, rather than being a
+ * matchset of its own without an RSSI filter. This is due to problems with
+ * how this API was implemented in the past. Also, due to the same problem,
+ * the only way to create a matchset with only an RSSI filter (with this
+ * attribute) is if there's only a single matchset with the RSSI attribute.
* @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
* attribute number currently defined
* @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -2477,6 +2520,9 @@ enum nl80211_sched_scan_match_attr {
* @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed,
* this includes probe requests or modes of operation that require
* beaconing.
+ * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
+ * base on contiguous rules and wider channels will be allowed to cross
+ * multiple contiguous/overlapping frequency ranges.
*/
enum nl80211_reg_rule_flags {
NL80211_RRF_NO_OFDM = 1<<0,
@@ -2488,6 +2534,7 @@ enum nl80211_reg_rule_flags {
NL80211_RRF_PTMP_ONLY = 1<<6,
NL80211_RRF_NO_IR = 1<<7,
__NL80211_RRF_NO_IBSS = 1<<8,
+ NL80211_RRF_AUTO_BW = 1<<11,
};
#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
@@ -3131,6 +3178,7 @@ enum nl80211_key_attributes {
* in an array of MCS numbers.
* @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection,
* see &struct nl80211_txrate_vht
+ * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi
* @__NL80211_TXRATE_AFTER_LAST: internal
* @NL80211_TXRATE_MAX: highest TX rate attribute
*/
@@ -3139,6 +3187,7 @@ enum nl80211_tx_rate_attributes {
NL80211_TXRATE_LEGACY,
NL80211_TXRATE_HT,
NL80211_TXRATE_VHT,
+ NL80211_TXRATE_GI,
/* keep last */
__NL80211_TXRATE_AFTER_LAST,
@@ -3156,6 +3205,12 @@ struct nl80211_txrate_vht {
__u16 mcs[NL80211_VHT_NSS_MAX];
};
+enum nl80211_txrate_gi {
+ NL80211_TXRATE_DEFAULT_GI,
+ NL80211_TXRATE_FORCE_SGI,
+ NL80211_TXRATE_FORCE_LGI,
+};
+
/**
* enum nl80211_band - Frequency band
* @NL80211_BAND_2GHZ: 2.4 GHz ISM band
@@ -3801,11 +3856,6 @@ enum nl80211_ap_sme_features {
* @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
* to work properly to suppport receiving regulatory hints from
* cellular base stations.
- * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: If this is set, an active
- * P2P Device (%NL80211_IFTYPE_P2P_DEVICE) requires its own channel
- * in the interface combinations, even when it's only used for scan
- * and remain-on-channel. This could be due to, for example, the
- * remain-on-channel implementation requiring a channel context.
* @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of
* equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station
* mode
@@ -3847,7 +3897,7 @@ enum nl80211_feature_flags {
NL80211_FEATURE_HT_IBSS = 1 << 1,
NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2,
NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3,
- NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4,
+ /* bit 4 is reserved - don't use */
NL80211_FEATURE_SAE = 1 << 5,
NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6,
NL80211_FEATURE_SCAN_FLUSH = 1 << 7,
@@ -4037,4 +4087,20 @@ struct nl80211_vendor_cmd_info {
__u32 subcmd;
};
+/**
+ * enum nl80211_tdls_peer_capability - TDLS peer flags.
+ *
+ * Used by tdls_mgmt() to determine which conditional elements need
+ * to be added to TDLS Setup frames.
+ *
+ * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
+ * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
+ * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
+ */
+enum nl80211_tdls_peer_capability {
+ NL80211_TDLS_PEER_HT = 1<<0,
+ NL80211_TDLS_PEER_VHT = 1<<1,
+ NL80211_TDLS_PEER_WMM = 1<<2,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/include/uapi/linux/pfkeyv2.h b/include/uapi/linux/pfkeyv2.h
index 0b80c806631f..ada7f0171ccc 100644
--- a/include/uapi/linux/pfkeyv2.h
+++ b/include/uapi/linux/pfkeyv2.h
@@ -235,6 +235,18 @@ struct sadb_x_kmaddress {
} __attribute__((packed));
/* sizeof(struct sadb_x_kmaddress) == 8 */
+/* To specify the SA dump filter */
+struct sadb_x_filter {
+ __u16 sadb_x_filter_len;
+ __u16 sadb_x_filter_exttype;
+ __u32 sadb_x_filter_saddr[4];
+ __u32 sadb_x_filter_daddr[4];
+ __u16 sadb_x_filter_family;
+ __u8 sadb_x_filter_splen;
+ __u8 sadb_x_filter_dplen;
+} __attribute__((packed));
+/* sizeof(struct sadb_x_filter) == 40 */
+
/* Message types */
#define SADB_RESERVED 0
#define SADB_GETSPI 1
@@ -358,7 +370,8 @@ struct sadb_x_kmaddress {
#define SADB_X_EXT_SEC_CTX 24
/* Used with MIGRATE to pass @ to IKE for negotiation */
#define SADB_X_EXT_KMADDRESS 25
-#define SADB_EXT_MAX 25
+#define SADB_X_EXT_FILTER 26
+#define SADB_EXT_MAX 26
/* Identity Extension values */
#define SADB_IDENTTYPE_RESERVED 0
diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h
index b65c834f83e9..f0b7bfe5da92 100644
--- a/include/uapi/linux/ptp_clock.h
+++ b/include/uapi/linux/ptp_clock.h
@@ -50,7 +50,8 @@ struct ptp_clock_caps {
int n_ext_ts; /* Number of external time stamp channels. */
int n_per_out; /* Number of programmable periodic signals. */
int pps; /* Whether the clock supports a PPS callback. */
- int rsv[15]; /* Reserved for future use. */
+ int n_pins; /* Number of input/output pins. */
+ int rsv[14]; /* Reserved for future use. */
};
struct ptp_extts_request {
@@ -80,6 +81,40 @@ struct ptp_sys_offset {
struct ptp_clock_time ts[2 * PTP_MAX_SAMPLES + 1];
};
+enum ptp_pin_function {
+ PTP_PF_NONE,
+ PTP_PF_EXTTS,
+ PTP_PF_PEROUT,
+ PTP_PF_PHYSYNC,
+};
+
+struct ptp_pin_desc {
+ /*
+ * Hardware specific human readable pin name. This field is
+ * set by the kernel during the PTP_PIN_GETFUNC ioctl and is
+ * ignored for the PTP_PIN_SETFUNC ioctl.
+ */
+ char name[64];
+ /*
+ * Pin index in the range of zero to ptp_clock_caps.n_pins - 1.
+ */
+ unsigned int index;
+ /*
+ * Which of the PTP_PF_xxx functions to use on this pin.
+ */
+ unsigned int func;
+ /*
+ * The specific channel to use for this function.
+ * This corresponds to the 'index' field of the
+ * PTP_EXTTS_REQUEST and PTP_PEROUT_REQUEST ioctls.
+ */
+ unsigned int chan;
+ /*
+ * Reserved for future use.
+ */
+ unsigned int rsv[5];
+};
+
#define PTP_CLK_MAGIC '='
#define PTP_CLOCK_GETCAPS _IOR(PTP_CLK_MAGIC, 1, struct ptp_clock_caps)
@@ -87,6 +122,8 @@ struct ptp_sys_offset {
#define PTP_PEROUT_REQUEST _IOW(PTP_CLK_MAGIC, 3, struct ptp_perout_request)
#define PTP_ENABLE_PPS _IOW(PTP_CLK_MAGIC, 4, int)
#define PTP_SYS_OFFSET _IOW(PTP_CLK_MAGIC, 5, struct ptp_sys_offset)
+#define PTP_PIN_GETFUNC _IOWR(PTP_CLK_MAGIC, 6, struct ptp_pin_desc)
+#define PTP_PIN_SETFUNC _IOW(PTP_CLK_MAGIC, 7, struct ptp_pin_desc)
struct ptp_extts_event {
struct ptp_clock_time t; /* Time event occured. */
diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h
index bbaba22f2d1b..df40137f33dd 100644
--- a/include/uapi/linux/snmp.h
+++ b/include/uapi/linux/snmp.h
@@ -252,6 +252,7 @@ enum
LINUX_MIB_TCPCHALLENGEACK, /* TCPChallengeACK */
LINUX_MIB_TCPSYNCHALLENGE, /* TCPSYNChallenge */
LINUX_MIB_TCPFASTOPENACTIVE, /* TCPFastOpenActive */
+ LINUX_MIB_TCPFASTOPENACTIVEFAIL, /* TCPFastOpenActiveFail */
LINUX_MIB_TCPFASTOPENPASSIVE, /* TCPFastOpenPassive*/
LINUX_MIB_TCPFASTOPENPASSIVEFAIL, /* TCPFastOpenPassiveFail */
LINUX_MIB_TCPFASTOPENLISTENOVERFLOW, /* TCPFastOpenListenOverflow */
@@ -259,6 +260,11 @@ enum
LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */
LINUX_MIB_BUSYPOLLRXPACKETS, /* BusyPollRxPackets */
LINUX_MIB_TCPAUTOCORKING, /* TCPAutoCorking */
+ LINUX_MIB_TCPFROMZEROWINDOWADV, /* TCPFromZeroWindowAdv */
+ LINUX_MIB_TCPTOZEROWINDOWADV, /* TCPToZeroWindowAdv */
+ LINUX_MIB_TCPWANTZEROWINDOWADV, /* TCPWantZeroWindowAdv */
+ LINUX_MIB_TCPSYNRETRANS, /* TCPSynRetrans */
+ LINUX_MIB_TCPORIGDATASENT, /* TCPOrigDataSent */
__LINUX_MIB_MAX
};
diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
index 377f1e59411d..3b9718328d8b 100644
--- a/include/uapi/linux/tcp.h
+++ b/include/uapi/linux/tcp.h
@@ -186,6 +186,9 @@ struct tcp_info {
__u32 tcpi_rcv_space;
__u32 tcpi_total_retrans;
+
+ __u64 tcpi_pacing_rate;
+ __u64 tcpi_max_pacing_rate;
};
/* for TCP_MD5SIG socket option */
diff --git a/include/uapi/linux/tcp_metrics.h b/include/uapi/linux/tcp_metrics.h
index 54a37b13f2c4..93533926035c 100644
--- a/include/uapi/linux/tcp_metrics.h
+++ b/include/uapi/linux/tcp_metrics.h
@@ -11,12 +11,15 @@
#define TCP_METRICS_GENL_VERSION 0x1
enum tcp_metric_index {
- TCP_METRIC_RTT,
- TCP_METRIC_RTTVAR,
+ TCP_METRIC_RTT, /* in ms units */
+ TCP_METRIC_RTTVAR, /* in ms units */
TCP_METRIC_SSTHRESH,
TCP_METRIC_CWND,
TCP_METRIC_REORDERING,
+ TCP_METRIC_RTT_US, /* in usec units */
+ TCP_METRIC_RTTVAR_US, /* in usec units */
+
/* Always last. */
__TCP_METRIC_MAX,
};
diff --git a/include/uapi/linux/uhid.h b/include/uapi/linux/uhid.h
index 414b74be4da1..1e3b09c191cd 100644
--- a/include/uapi/linux/uhid.h
+++ b/include/uapi/linux/uhid.h
@@ -21,6 +21,7 @@
#include <linux/input.h>
#include <linux/types.h>
+#include <linux/hid.h>
enum uhid_event_type {
UHID_CREATE,
@@ -34,6 +35,8 @@ enum uhid_event_type {
UHID_INPUT,
UHID_FEATURE,
UHID_FEATURE_ANSWER,
+ UHID_CREATE2,
+ UHID_INPUT2,
};
struct uhid_create_req {
@@ -50,6 +53,19 @@ struct uhid_create_req {
__u32 country;
} __attribute__((__packed__));
+struct uhid_create2_req {
+ __u8 name[128];
+ __u8 phys[64];
+ __u8 uniq[64];
+ __u16 rd_size;
+ __u16 bus;
+ __u32 vendor;
+ __u32 product;
+ __u32 version;
+ __u32 country;
+ __u8 rd_data[HID_MAX_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
#define UHID_DATA_MAX 4096
enum uhid_report_type {
@@ -63,6 +79,11 @@ struct uhid_input_req {
__u16 size;
} __attribute__((__packed__));
+struct uhid_input2_req {
+ __u16 size;
+ __u8 data[UHID_DATA_MAX];
+} __attribute__((__packed__));
+
struct uhid_output_req {
__u8 data[UHID_DATA_MAX];
__u16 size;
@@ -100,6 +121,8 @@ struct uhid_event {
struct uhid_output_ev_req output_ev;
struct uhid_feature_req feature;
struct uhid_feature_answer_req feature_answer;
+ struct uhid_create2_req create2;
+ struct uhid_input2_req input2;
} u;
} __attribute__((__packed__));
diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
index fe46431593f9..0389b489bbba 100644
--- a/include/uapi/linux/uinput.h
+++ b/include/uapi/linux/uinput.h
@@ -20,6 +20,8 @@
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
+ * 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>)
* - update ff support for the changes in kernel interface
* - add UINPUT_VERSION
@@ -35,7 +37,7 @@
#include <linux/types.h>
#include <linux/input.h>
-#define UINPUT_VERSION 3
+#define UINPUT_VERSION 4
struct uinput_ff_upload {
@@ -73,6 +75,15 @@ struct uinput_ff_erase {
#define UI_BEGIN_FF_ERASE _IOWR(UINPUT_IOCTL_BASE, 202, struct uinput_ff_erase)
#define UI_END_FF_ERASE _IOW(UINPUT_IOCTL_BASE, 203, struct uinput_ff_erase)
+/**
+ * UI_GET_SYSNAME - get the sysfs name of the created uinput device
+ *
+ * @return the sysfs name of the created virtual input device.
+ * The complete sysfs path is then /sys/devices/virtual/input/--NAME--
+ * Usually, it is in the form "inputN"
+ */
+#define UI_GET_SYSNAME(len) _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 300, len)
+
/*
* To write a force-feedback-capable driver, the upload_effect
* and erase_effect callbacks in input_dev must be implemented.
diff --git a/include/uapi/linux/usb/cdc.h b/include/uapi/linux/usb/cdc.h
index f35aa0a338c7..b6a9cdd6e096 100644
--- a/include/uapi/linux/usb/cdc.h
+++ b/include/uapi/linux/usb/cdc.h
@@ -56,6 +56,7 @@
#define USB_CDC_OBEX_TYPE 0x15
#define USB_CDC_NCM_TYPE 0x1a
#define USB_CDC_MBIM_TYPE 0x1b
+#define USB_CDC_MBIM_EXTENDED_TYPE 0x1c
/* "Header Functional Descriptor" from CDC spec 5.2.3.1 */
struct usb_cdc_header_desc {
@@ -205,6 +206,17 @@ struct usb_cdc_mbim_desc {
__u8 bmNetworkCapabilities;
} __attribute__ ((packed));
+/* "MBIM Extended Functional Descriptor" from CDC MBIM spec 1.0 errata-1 */
+struct usb_cdc_mbim_extended_desc {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubType;
+
+ __le16 bcdMBIMExtendedVersion;
+ __u8 bMaxOutstandingCommandMessages;
+ __le16 wMTU;
+} __attribute__ ((packed));
+
/*-------------------------------------------------------------------------*/
/*
diff --git a/include/uapi/linux/v4l2-common.h b/include/uapi/linux/v4l2-common.h
index 4f0667e010dd..270db8914c01 100644
--- a/include/uapi/linux/v4l2-common.h
+++ b/include/uapi/linux/v4l2-common.h
@@ -68,4 +68,12 @@
#define V4L2_SUBDEV_SEL_FLAG_SIZE_LE V4L2_SEL_FLAG_LE
#define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG V4L2_SEL_FLAG_KEEP_CONFIG
+struct v4l2_edid {
+ __u32 pad;
+ __u32 start_block;
+ __u32 blocks;
+ __u32 reserved[5];
+ __u8 __user *edid;
+};
+
#endif /* __V4L2_COMMON__ */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 2cbe605bbe04..2ac5597f3ee1 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -60,6 +60,7 @@
#define V4L2_CTRL_CLASS_IMAGE_PROC 0x009f0000 /* Image processing controls */
#define V4L2_CTRL_CLASS_DV 0x00a00000 /* Digital Video controls */
#define V4L2_CTRL_CLASS_FM_RX 0x00a10000 /* FM Receiver controls */
+#define V4L2_CTRL_CLASS_RF_TUNER 0x00a20000 /* RF tuner controls */
/* User-class control IDs */
@@ -376,6 +377,8 @@ enum v4l2_mpeg_video_multi_slice_mode {
#define V4L2_CID_MPEG_VIDEO_DEC_FRAME (V4L2_CID_MPEG_BASE+224)
#define V4L2_CID_MPEG_VIDEO_VBV_DELAY (V4L2_CID_MPEG_BASE+225)
#define V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER (V4L2_CID_MPEG_BASE+226)
+#define V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE (V4L2_CID_MPEG_BASE+227)
+#define V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (V4L2_CID_MPEG_BASE+228)
#define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (V4L2_CID_MPEG_BASE+300)
#define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (V4L2_CID_MPEG_BASE+301)
@@ -812,6 +815,9 @@ enum v4l2_flash_strobe_source {
#define V4L2_FLASH_FAULT_SHORT_CIRCUIT (1 << 3)
#define V4L2_FLASH_FAULT_OVER_CURRENT (1 << 4)
#define V4L2_FLASH_FAULT_INDICATOR (1 << 5)
+#define V4L2_FLASH_FAULT_UNDER_VOLTAGE (1 << 6)
+#define V4L2_FLASH_FAULT_INPUT_VOLTAGE (1 << 7)
+#define V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE (1 << 8)
#define V4L2_CID_FLASH_CHARGE (V4L2_CID_FLASH_CLASS_BASE + 11)
#define V4L2_CID_FLASH_READY (V4L2_CID_FLASH_CLASS_BASE + 12)
@@ -895,4 +901,17 @@ enum v4l2_deemphasis {
#define V4L2_CID_RDS_RECEPTION (V4L2_CID_FM_RX_CLASS_BASE + 2)
+#define V4L2_CID_RF_TUNER_CLASS_BASE (V4L2_CTRL_CLASS_RF_TUNER | 0x900)
+#define V4L2_CID_RF_TUNER_CLASS (V4L2_CTRL_CLASS_RF_TUNER | 1)
+
+#define V4L2_CID_RF_TUNER_BANDWIDTH_AUTO (V4L2_CID_RF_TUNER_CLASS_BASE + 11)
+#define V4L2_CID_RF_TUNER_BANDWIDTH (V4L2_CID_RF_TUNER_CLASS_BASE + 12)
+#define V4L2_CID_RF_TUNER_LNA_GAIN_AUTO (V4L2_CID_RF_TUNER_CLASS_BASE + 41)
+#define V4L2_CID_RF_TUNER_LNA_GAIN (V4L2_CID_RF_TUNER_CLASS_BASE + 42)
+#define V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO (V4L2_CID_RF_TUNER_CLASS_BASE + 51)
+#define V4L2_CID_RF_TUNER_MIXER_GAIN (V4L2_CID_RF_TUNER_CLASS_BASE + 52)
+#define V4L2_CID_RF_TUNER_IF_GAIN_AUTO (V4L2_CID_RF_TUNER_CLASS_BASE + 61)
+#define V4L2_CID_RF_TUNER_IF_GAIN (V4L2_CID_RF_TUNER_CLASS_BASE + 62)
+#define V4L2_CID_RF_TUNER_PLL_LOCK (V4L2_CID_RF_TUNER_CLASS_BASE + 91)
+
#endif
diff --git a/include/uapi/linux/v4l2-dv-timings.h b/include/uapi/linux/v4l2-dv-timings.h
index be709fe29552..b6a5fe00a470 100644
--- a/include/uapi/linux/v4l2-dv-timings.h
+++ b/include/uapi/linux/v4l2-dv-timings.h
@@ -823,4 +823,21 @@
V4L2_DV_FL_REDUCED_BLANKING) \
}
+/* 4K resolutions */
+#define V4L2_DV_BT_DMT_4096X2160P60_RB { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ 556744000, 8, 32, 40, 48, 8, 6, 0, 0, 0, \
+ V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \
+ V4L2_DV_FL_REDUCED_BLANKING) \
+}
+
+#define V4L2_DV_BT_DMT_4096X2160P59_94_RB { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ 556188000, 8, 32, 40, 48, 8, 6, 0, 0, 0, \
+ V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \
+ V4L2_DV_FL_REDUCED_BLANKING) \
+}
+
#endif
diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h
index a33c4daadce3..87e05159f637 100644
--- a/include/uapi/linux/v4l2-subdev.h
+++ b/include/uapi/linux/v4l2-subdev.h
@@ -148,13 +148,8 @@ struct v4l2_subdev_selection {
__u32 reserved[8];
};
-struct v4l2_subdev_edid {
- __u32 pad;
- __u32 start_block;
- __u32 blocks;
- __u32 reserved[5];
- __u8 __user *edid;
-};
+/* Backwards compatibility define --- to be removed */
+#define v4l2_subdev_edid v4l2_edid
#define VIDIOC_SUBDEV_G_FMT _IOWR('V', 4, struct v4l2_subdev_format)
#define VIDIOC_SUBDEV_S_FMT _IOWR('V', 5, struct v4l2_subdev_format)
@@ -174,7 +169,8 @@ struct v4l2_subdev_edid {
_IOWR('V', 61, struct v4l2_subdev_selection)
#define VIDIOC_SUBDEV_S_SELECTION \
_IOWR('V', 62, struct v4l2_subdev_selection)
-#define VIDIOC_SUBDEV_G_EDID _IOWR('V', 40, struct v4l2_subdev_edid)
-#define VIDIOC_SUBDEV_S_EDID _IOWR('V', 41, struct v4l2_subdev_edid)
+/* These two G/S_EDID ioctls are identical to the ioctls in videodev2.h */
+#define VIDIOC_SUBDEV_G_EDID _IOWR('V', 40, struct v4l2_edid)
+#define VIDIOC_SUBDEV_S_EDID _IOWR('V', 41, struct v4l2_edid)
#endif
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 0fd47f5bc146..cb9023d4f063 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -23,6 +23,12 @@
#define VFIO_TYPE1_IOMMU 1
#define VFIO_SPAPR_TCE_IOMMU 2
+#define VFIO_TYPE1v2_IOMMU 3
+/*
+ * IOMMU enforces DMA cache coherence (ex. PCIe NoSnoop stripping). This
+ * capability is subject to change as groups are added or removed.
+ */
+#define VFIO_DMA_CC_IOMMU 4
/*
* The IOCTL interface is designed for extensibility by embedding the
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 6ae7bbe988cc..ea468ee8fe21 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -139,6 +139,7 @@ enum v4l2_buf_type {
#endif
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10,
+ V4L2_BUF_TYPE_SDR_CAPTURE = 11,
/* Deprecated, do not use */
V4L2_BUF_TYPE_PRIVATE = 0x80,
};
@@ -159,6 +160,8 @@ enum v4l2_tuner_type {
V4L2_TUNER_RADIO = 1,
V4L2_TUNER_ANALOG_TV = 2,
V4L2_TUNER_DIGITAL_TV = 3,
+ V4L2_TUNER_ADC = 4,
+ V4L2_TUNER_RF = 5,
};
enum v4l2_memory {
@@ -264,6 +267,8 @@ struct v4l2_capability {
#define V4L2_CAP_RADIO 0x00040000 /* is a radio device */
#define V4L2_CAP_MODULATOR 0x00080000 /* has a modulator */
+#define V4L2_CAP_SDR_CAPTURE 0x00100000 /* Is a SDR capture device */
+
#define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */
#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */
#define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */
@@ -431,6 +436,10 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_SE401 v4l2_fourcc('S', '4', '0', '1') /* se401 janggu compressed rgb */
#define V4L2_PIX_FMT_S5C_UYVY_JPG v4l2_fourcc('S', '5', 'C', 'I') /* S5C73M3 interleaved UYVY/JPEG */
+/* SDR formats - used only for Software Defined Radio devices */
+#define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
+#define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */
+
/*
* F O R M A T E N U M E R A T I O N
*/
@@ -669,24 +678,36 @@ struct v4l2_buffer {
};
/* Flags for 'flags' field */
-#define V4L2_BUF_FLAG_MAPPED 0x0001 /* Buffer is mapped (flag) */
-#define V4L2_BUF_FLAG_QUEUED 0x0002 /* Buffer is queued for processing */
-#define V4L2_BUF_FLAG_DONE 0x0004 /* Buffer is ready */
-#define V4L2_BUF_FLAG_KEYFRAME 0x0008 /* Image is a keyframe (I-frame) */
-#define V4L2_BUF_FLAG_PFRAME 0x0010 /* Image is a P-frame */
-#define V4L2_BUF_FLAG_BFRAME 0x0020 /* Image is a B-frame */
+/* Buffer is mapped (flag) */
+#define V4L2_BUF_FLAG_MAPPED 0x00000001
+/* Buffer is queued for processing */
+#define V4L2_BUF_FLAG_QUEUED 0x00000002
+/* Buffer is ready */
+#define V4L2_BUF_FLAG_DONE 0x00000004
+/* Image is a keyframe (I-frame) */
+#define V4L2_BUF_FLAG_KEYFRAME 0x00000008
+/* Image is a P-frame */
+#define V4L2_BUF_FLAG_PFRAME 0x00000010
+/* Image is a B-frame */
+#define V4L2_BUF_FLAG_BFRAME 0x00000020
/* Buffer is ready, but the data contained within is corrupted. */
-#define V4L2_BUF_FLAG_ERROR 0x0040
-#define V4L2_BUF_FLAG_TIMECODE 0x0100 /* timecode field is valid */
-#define V4L2_BUF_FLAG_PREPARED 0x0400 /* Buffer is prepared for queuing */
+#define V4L2_BUF_FLAG_ERROR 0x00000040
+/* timecode field is valid */
+#define V4L2_BUF_FLAG_TIMECODE 0x00000100
+/* Buffer is prepared for queuing */
+#define V4L2_BUF_FLAG_PREPARED 0x00000400
/* Cache handling flags */
-#define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800
-#define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x1000
+#define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x00000800
+#define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x00001000
/* Timestamp type */
-#define V4L2_BUF_FLAG_TIMESTAMP_MASK 0xe000
-#define V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN 0x0000
-#define V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC 0x2000
-#define V4L2_BUF_FLAG_TIMESTAMP_COPY 0x4000
+#define V4L2_BUF_FLAG_TIMESTAMP_MASK 0x0000e000
+#define V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN 0x00000000
+#define V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC 0x00002000
+#define V4L2_BUF_FLAG_TIMESTAMP_COPY 0x00004000
+/* Timestamp sources. */
+#define V4L2_BUF_FLAG_TSTAMP_SRC_MASK 0x00070000
+#define V4L2_BUF_FLAG_TSTAMP_SRC_EOF 0x00000000
+#define V4L2_BUF_FLAG_TSTAMP_SRC_SOE 0x00010000
/**
* struct v4l2_exportbuffer - export of video buffer as DMABUF file descriptor
@@ -1059,14 +1080,14 @@ struct v4l2_bt_timings {
/* A few useful defines to calculate the total blanking and frame sizes */
#define V4L2_DV_BT_BLANKING_WIDTH(bt) \
- (bt->hfrontporch + bt->hsync + bt->hbackporch)
+ ((bt)->hfrontporch + (bt)->hsync + (bt)->hbackporch)
#define V4L2_DV_BT_FRAME_WIDTH(bt) \
- (bt->width + V4L2_DV_BT_BLANKING_WIDTH(bt))
+ ((bt)->width + V4L2_DV_BT_BLANKING_WIDTH(bt))
#define V4L2_DV_BT_BLANKING_HEIGHT(bt) \
- (bt->vfrontporch + bt->vsync + bt->vbackporch + \
- bt->il_vfrontporch + bt->il_vsync + bt->il_vbackporch)
+ ((bt)->vfrontporch + (bt)->vsync + (bt)->vbackporch + \
+ (bt)->il_vfrontporch + (bt)->il_vsync + (bt)->il_vbackporch)
#define V4L2_DV_BT_FRAME_HEIGHT(bt) \
- (bt->height + V4L2_DV_BT_BLANKING_HEIGHT(bt))
+ ((bt)->height + V4L2_DV_BT_BLANKING_HEIGHT(bt))
/** struct v4l2_dv_timings - DV timings
* @type: the type of the timings
@@ -1339,6 +1360,7 @@ struct v4l2_modulator {
#define V4L2_TUNER_CAP_RDS_CONTROLS 0x0200
#define V4L2_TUNER_CAP_FREQ_BANDS 0x0400
#define V4L2_TUNER_CAP_HWSEEK_PROG_LIM 0x0800
+#define V4L2_TUNER_CAP_1HZ 0x1000
/* Flags for the 'rxsubchans' field */
#define V4L2_TUNER_SUB_MONO 0x0001
@@ -1692,6 +1714,15 @@ struct v4l2_pix_format_mplane {
} __attribute__ ((packed));
/**
+ * struct v4l2_sdr_format - SDR format definition
+ * @pixelformat: little endian four character code (fourcc)
+ */
+struct v4l2_sdr_format {
+ __u32 pixelformat;
+ __u8 reserved[28];
+} __attribute__ ((packed));
+
+/**
* struct v4l2_format - stream data format
* @type: enum v4l2_buf_type; type of the data stream
* @pix: definition of an image format
@@ -1709,6 +1740,7 @@ struct v4l2_format {
struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
+ struct v4l2_sdr_format sdr; /* V4L2_BUF_TYPE_SDR_CAPTURE */
__u8 raw_data[200]; /* user-defined */
} fmt;
};
@@ -1885,6 +1917,8 @@ struct v4l2_create_buffers {
#define VIDIOC_QUERYMENU _IOWR('V', 37, struct v4l2_querymenu)
#define VIDIOC_G_INPUT _IOR('V', 38, int)
#define VIDIOC_S_INPUT _IOWR('V', 39, int)
+#define VIDIOC_G_EDID _IOWR('V', 40, struct v4l2_edid)
+#define VIDIOC_S_EDID _IOWR('V', 41, struct v4l2_edid)
#define VIDIOC_G_OUTPUT _IOR('V', 46, int)
#define VIDIOC_S_OUTPUT _IOWR('V', 47, int)
#define VIDIOC_ENUMOUTPUT _IOWR('V', 48, struct v4l2_output)
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index 40bbc04b6f81..c38355c1f3c9 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -7,11 +7,18 @@
Copyright (c) 2001-2002 Silicon Graphics, Inc. All Rights Reserved.
Copyright (c) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
*/
+
+#include <linux/libc-compat.h>
+
#ifndef _UAPI_LINUX_XATTR_H
#define _UAPI_LINUX_XATTR_H
+#ifdef __UAPI_DEF_XATTR
+#define __USE_KERNEL_XATTR_DEFS
+
#define XATTR_CREATE 0x1 /* set value, fail if attr already exists */
#define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */
+#endif
/* Namespaces */
#define XATTR_OS2_PREFIX "os2."
diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h
index a8cd6a4a2970..25e5dd916ba4 100644
--- a/include/uapi/linux/xfrm.h
+++ b/include/uapi/linux/xfrm.h
@@ -298,6 +298,8 @@ enum xfrm_attr_type_t {
XFRMA_TFCPAD, /* __u32 */
XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_esn */
XFRMA_SA_EXTRA_FLAGS, /* __u32 */
+ XFRMA_PROTO, /* __u8 */
+ XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */
__XFRMA_MAX
#define XFRMA_MAX (__XFRMA_MAX - 1)
@@ -474,6 +476,14 @@ struct xfrm_user_mapping {
__be16 new_sport;
};
+struct xfrm_address_filter {
+ xfrm_address_t saddr;
+ xfrm_address_t daddr;
+ __u16 family;
+ __u8 splen;
+ __u8 dplen;
+};
+
#ifndef __KERNEL__
/* backwards compatibility for userspace */
#define XFRMGRP_ACQUIRE 1
diff --git a/include/uapi/mtd/ubi-user.h b/include/uapi/mtd/ubi-user.h
index 723c324590c1..1927b0d78a99 100644
--- a/include/uapi/mtd/ubi-user.h
+++ b/include/uapi/mtd/ubi-user.h
@@ -134,6 +134,16 @@
* used. A pointer to a &struct ubi_set_vol_prop_req object is expected to be
* passed. The object describes which property should be set, and to which value
* it should be set.
+ *
+ * Block devices on UBI volumes
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * To create a R/O block device on top of an UBI volume the %UBI_IOCVOLCRBLK
+ * should be used. A pointer to a &struct ubi_blkcreate_req object is expected
+ * to be passed, which is not used and reserved for future usage.
+ *
+ * Conversely, to remove a block device the %UBI_IOCVOLRMBLK should be used,
+ * which takes no arguments.
*/
/*
@@ -191,6 +201,10 @@
/* Set an UBI volume property */
#define UBI_IOCSETVOLPROP _IOW(UBI_VOL_IOC_MAGIC, 6, \
struct ubi_set_vol_prop_req)
+/* Create a R/O block device on top of an UBI volume */
+#define UBI_IOCVOLCRBLK _IOW(UBI_VOL_IOC_MAGIC, 7, struct ubi_blkcreate_req)
+/* Remove the R/O block device */
+#define UBI_IOCVOLRMBLK _IO(UBI_VOL_IOC_MAGIC, 8)
/* Maximum MTD device name length supported by UBI */
#define MAX_UBI_MTD_NAME_LEN 127
@@ -420,4 +434,12 @@ struct ubi_set_vol_prop_req {
__u64 value;
} __packed;
+/**
+ * struct ubi_blkcreate_req - a data structure used in block creation requests.
+ * @padding: reserved for future, not used, has to be zeroed
+ */
+struct ubi_blkcreate_req {
+ __s8 padding[128];
+} __packed;
+
#endif /* __UBI_USER_H__ */
diff --git a/include/video/omapdss.h b/include/video/omapdss.h
index 3d7c51a6f9ff..6adb44534606 100644
--- a/include/video/omapdss.h
+++ b/include/video/omapdss.h
@@ -323,7 +323,6 @@ enum omapdss_version {
/* Board specific data */
struct omap_dss_board_info {
- int (*get_context_loss_count)(struct device *dev);
int num_devices;
struct omap_dss_device **devices;
struct omap_dss_device *default_device;
@@ -344,8 +343,8 @@ struct omap_video_timings {
u16 x_res;
/* Unit: pixels */
u16 y_res;
- /* Unit: KHz */
- u32 pixel_clock;
+ /* Unit: Hz */
+ u32 pixelclock;
/* Unit: pixel clocks */
u16 hsw; /* Horizontal synchronization pulse width */
/* Unit: pixel clocks */
@@ -1019,4 +1018,18 @@ static inline bool omapdss_device_is_enabled(struct omap_dss_device *dssdev)
return dssdev->state == OMAP_DSS_DISPLAY_ACTIVE;
}
+struct device_node *
+omapdss_of_get_next_port(const struct device_node *parent,
+ struct device_node *prev);
+
+struct device_node *
+omapdss_of_get_next_endpoint(const struct device_node *parent,
+ struct device_node *prev);
+
+struct device_node *
+omapdss_of_get_first_endpoint(const struct device_node *parent);
+
+struct omap_dss_device *
+omapdss_of_find_source_for_first_ep(struct device_node *node);
+
#endif
diff --git a/include/xen/events.h b/include/xen/events.h
index c9c85cf84895..8bee7a75e850 100644
--- a/include/xen/events.h
+++ b/include/xen/events.h
@@ -2,6 +2,9 @@
#define _XEN_EVENTS_H
#include <linux/interrupt.h>
+#ifdef CONFIG_PCI_MSI
+#include <linux/msi.h>
+#endif
#include <xen/interface/event_channel.h>
#include <asm/xen/hypercall.h>
@@ -52,7 +55,6 @@ int evtchn_get(unsigned int evtchn);
void evtchn_put(unsigned int evtchn);
void xen_send_IPI_one(unsigned int cpu, enum ipi_vector vector);
-int resend_irq_on_evtchn(unsigned int irq);
void rebind_evtchn_irq(int evtchn, int irq);
static inline void notify_remote_via_evtchn(int port)
@@ -102,7 +104,7 @@ int xen_bind_pirq_gsi_to_irq(unsigned gsi,
int xen_allocate_pirq_msi(struct pci_dev *dev, struct msi_desc *msidesc);
/* Bind an PSI pirq to an irq. */
int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc,
- int pirq, const char *name, domid_t domid);
+ int pirq, int nvec, const char *name, domid_t domid);
#endif
/* De-allocates the above mentioned physical interrupt. */
diff --git a/include/xen/interface/physdev.h b/include/xen/interface/physdev.h
index 42721d13a106..610dba9b620a 100644
--- a/include/xen/interface/physdev.h
+++ b/include/xen/interface/physdev.h
@@ -131,6 +131,7 @@ struct physdev_irq {
#define MAP_PIRQ_TYPE_GSI 0x1
#define MAP_PIRQ_TYPE_UNKNOWN 0x2
#define MAP_PIRQ_TYPE_MSI_SEG 0x3
+#define MAP_PIRQ_TYPE_MULTI_MSI 0x4
#define PHYSDEVOP_map_pirq 13
struct physdev_map_pirq {
@@ -141,11 +142,16 @@ struct physdev_map_pirq {
int index;
/* IN or OUT */
int pirq;
- /* IN - high 16 bits hold segment for MAP_PIRQ_TYPE_MSI_SEG */
+ /* IN - high 16 bits hold segment for ..._MSI_SEG and ..._MULTI_MSI */
int bus;
/* IN */
int devfn;
- /* IN */
+ /* IN
+ * - For MSI-X contains entry number.
+ * - For MSI with ..._MULTI_MSI contains number of vectors.
+ * OUT (..._MULTI_MSI only)
+ * - Number of vectors allocated.
+ */
int entry_nr;
/* IN */
uint64_t table_base;
diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h
index fb2ea8f26552..2cf47175b12b 100644
--- a/include/xen/xen-ops.h
+++ b/include/xen/xen-ops.h
@@ -2,6 +2,7 @@
#define INCLUDE_XEN_OPS_H
#include <linux/percpu.h>
+#include <linux/notifier.h>
#include <asm/xen/interface.h>
DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu);
@@ -16,6 +17,9 @@ void xen_mm_unpin_all(void);
void xen_timer_resume(void);
void xen_arch_resume(void);
+void xen_resume_notifier_register(struct notifier_block *nb);
+void xen_resume_notifier_unregister(struct notifier_block *nb);
+
int xen_setup_shutdown_event(void);
extern unsigned long *xen_contiguous_bitmap;
diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h
index 569c07f2e344..0324c6d340c1 100644
--- a/include/xen/xenbus.h
+++ b/include/xen/xenbus.h
@@ -207,7 +207,6 @@ int xenbus_unmap_ring(struct xenbus_device *dev,
grant_handle_t handle, void *vaddr);
int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port);
-int xenbus_bind_evtchn(struct xenbus_device *dev, int remote_port, int *port);
int xenbus_free_evtchn(struct xenbus_device *dev, int port);
enum xenbus_state xenbus_read_driver_state(const char *path);
diff --git a/init/Kconfig b/init/Kconfig
index d56cb03c1b49..8851c6417880 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -273,6 +273,16 @@ config FHANDLE
get renamed. Enables open_by_handle_at(2) and name_to_handle_at(2)
syscalls.
+config USELIB
+ bool "uselib syscall"
+ default y
+ help
+ This option enables the uselib syscall, a system call used in the
+ dynamic linker from libc5 and earlier. glibc does not use this
+ system call. If you intend to run programs built on libc5 or
+ earlier, you may need to enable this syscall. Current systems
+ running glibc can safely disable this.
+
config AUDIT
bool "Auditing support"
depends on NET
@@ -854,6 +864,7 @@ config NUMA_BALANCING
menuconfig CGROUPS
boolean "Control Group support"
+ select KERNFS
help
This option adds support for grouping sets of processes together, for
use with process control subsystems such as Cpusets, CFS, memory
@@ -1290,6 +1301,16 @@ config UID16
help
This enables the legacy 16-bit UID syscall wrappers.
+config SYSFS_SYSCALL
+ bool "Sysfs syscall support" if EXPERT
+ default y
+ ---help---
+ sys_sysfs is an obsolete system call no longer supported in libc.
+ Note that disabling this option is more secure but might break
+ compatibility with some systems.
+
+ If unsure say Y here.
+
config SYSCTL_SYSCALL
bool "Sysctl syscall support" if EXPERT
depends on PROC_SYSCTL
diff --git a/init/do_mounts.c b/init/do_mounts.c
index 8e5addc45874..82f22885c87e 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -102,13 +102,13 @@ no_match:
/**
* devt_from_partuuid - looks up the dev_t of a partition by its UUID
- * @uuid: char array containing ascii UUID
+ * @uuid_str: char array containing ascii UUID
*
* The function will return the first partition which contains a matching
* UUID value in its partition_meta_info struct. This does not search
* by filesystem UUIDs.
*
- * If @uuid is followed by a "/PARTNROFF=%d", then the number will be
+ * If @uuid_str is followed by a "/PARTNROFF=%d", then the number will be
* extracted and used as an offset from the partition identified by the UUID.
*
* Returns the matching dev_t on success or 0 on failure.
diff --git a/kernel/capability.c b/kernel/capability.c
index 34019c57888d..a8d63df0c322 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -7,6 +7,8 @@
* 30 May 2002: Cleanup, Robert M. Love <rml@tech9.net>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/audit.h>
#include <linux/capability.h>
#include <linux/mm.h>
@@ -42,15 +44,10 @@ __setup("no_file_caps", file_caps_disable);
static void warn_legacy_capability_use(void)
{
- static int warned;
- if (!warned) {
- char name[sizeof(current->comm)];
-
- printk(KERN_INFO "warning: `%s' uses 32-bit capabilities"
- " (legacy support in use)\n",
- get_task_comm(name, current));
- warned = 1;
- }
+ char name[sizeof(current->comm)];
+
+ pr_info_once("warning: `%s' uses 32-bit capabilities (legacy support in use)\n",
+ get_task_comm(name, current));
}
/*
@@ -71,16 +68,10 @@ static void warn_legacy_capability_use(void)
static void warn_deprecated_v2(void)
{
- static int warned;
+ char name[sizeof(current->comm)];
- if (!warned) {
- char name[sizeof(current->comm)];
-
- printk(KERN_INFO "warning: `%s' uses deprecated v2"
- " capabilities in a way that may be insecure.\n",
- get_task_comm(name, current));
- warned = 1;
- }
+ pr_info_once("warning: `%s' uses deprecated v2 capabilities in a way that may be insecure\n",
+ get_task_comm(name, current));
}
/*
@@ -380,7 +371,7 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
bool ns_capable(struct user_namespace *ns, int cap)
{
if (unlikely(!cap_valid(cap))) {
- printk(KERN_CRIT "capable() called with invalid cap=%u\n", cap);
+ pr_crit("capable() called with invalid cap=%u\n", cap);
BUG();
}
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 0c753ddd223b..fede3d3f28ff 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -40,23 +40,20 @@
#include <linux/proc_fs.h>
#include <linux/rcupdate.h>
#include <linux/sched.h>
-#include <linux/backing-dev.h>
#include <linux/slab.h>
-#include <linux/magic.h>
#include <linux/spinlock.h>
+#include <linux/rwsem.h>
#include <linux/string.h>
#include <linux/sort.h>
#include <linux/kmod.h>
-#include <linux/module.h>
#include <linux/delayacct.h>
#include <linux/cgroupstats.h>
#include <linux/hashtable.h>
-#include <linux/namei.h>
#include <linux/pid_namespace.h>
#include <linux/idr.h>
#include <linux/vmalloc.h> /* TODO: replace with more sophisticated array */
-#include <linux/flex_array.h> /* used in cgroup_attach_task */
#include <linux/kthread.h>
+#include <linux/delay.h>
#include <linux/atomic.h>
@@ -68,43 +65,49 @@
*/
#define CGROUP_PIDLIST_DESTROY_DELAY HZ
+#define CGROUP_FILE_NAME_MAX (MAX_CGROUP_TYPE_NAMELEN + \
+ MAX_CFTYPE_NAME + 2)
+
+/*
+ * cgroup_tree_mutex nests above cgroup_mutex and protects cftypes, file
+ * creation/removal and hierarchy changing operations including cgroup
+ * creation, removal, css association and controller rebinding. This outer
+ * lock is needed mainly to resolve the circular dependency between kernfs
+ * active ref and cgroup_mutex. cgroup_tree_mutex nests above both.
+ */
+static DEFINE_MUTEX(cgroup_tree_mutex);
+
/*
* cgroup_mutex is the master lock. Any modification to cgroup or its
* hierarchy must be performed while holding it.
*
- * cgroup_root_mutex nests inside cgroup_mutex and should be held to modify
- * cgroupfs_root of any cgroup hierarchy - subsys list, flags,
- * release_agent_path and so on. Modifying requires both cgroup_mutex and
- * cgroup_root_mutex. Readers can acquire either of the two. This is to
- * break the following locking order cycle.
- *
- * A. cgroup_mutex -> cred_guard_mutex -> s_type->i_mutex_key -> namespace_sem
- * B. namespace_sem -> cgroup_mutex
+ * css_set_rwsem protects task->cgroups pointer, the list of css_set
+ * objects, and the chain of tasks off each css_set.
*
- * B happens only through cgroup_show_options() and using cgroup_root_mutex
- * breaks it.
+ * These locks are exported if CONFIG_PROVE_RCU so that accessors in
+ * cgroup.h can use them for lockdep annotations.
*/
#ifdef CONFIG_PROVE_RCU
DEFINE_MUTEX(cgroup_mutex);
-EXPORT_SYMBOL_GPL(cgroup_mutex); /* only for lockdep */
+DECLARE_RWSEM(css_set_rwsem);
+EXPORT_SYMBOL_GPL(cgroup_mutex);
+EXPORT_SYMBOL_GPL(css_set_rwsem);
#else
static DEFINE_MUTEX(cgroup_mutex);
+static DECLARE_RWSEM(css_set_rwsem);
#endif
-static DEFINE_MUTEX(cgroup_root_mutex);
+/*
+ * Protects cgroup_subsys->release_agent_path. Modifying it also requires
+ * cgroup_mutex. Reading requires either cgroup_mutex or this spinlock.
+ */
+static DEFINE_SPINLOCK(release_agent_path_lock);
-#define cgroup_assert_mutex_or_rcu_locked() \
+#define cgroup_assert_mutexes_or_rcu_locked() \
rcu_lockdep_assert(rcu_read_lock_held() || \
+ lockdep_is_held(&cgroup_tree_mutex) || \
lockdep_is_held(&cgroup_mutex), \
- "cgroup_mutex or RCU read lock required");
-
-#ifdef CONFIG_LOCKDEP
-#define cgroup_assert_mutex_or_root_locked() \
- WARN_ON_ONCE(debug_locks && (!lockdep_is_held(&cgroup_mutex) && \
- !lockdep_is_held(&cgroup_root_mutex)))
-#else
-#define cgroup_assert_mutex_or_root_locked() do { } while (0)
-#endif
+ "cgroup_[tree_]mutex or RCU read lock required");
/*
* cgroup destruction makes heavy use of work items and there can be a lot
@@ -120,42 +123,41 @@ static struct workqueue_struct *cgroup_destroy_wq;
*/
static struct workqueue_struct *cgroup_pidlist_destroy_wq;
-/*
- * Generate an array of cgroup subsystem pointers. At boot time, this is
- * populated with the built in subsystems, and modular subsystems are
- * registered after that. The mutable section of this array is protected by
- * cgroup_mutex.
- */
-#define SUBSYS(_x) [_x ## _subsys_id] = &_x ## _subsys,
-#define IS_SUBSYS_ENABLED(option) IS_BUILTIN(option)
-static struct cgroup_subsys *cgroup_subsys[CGROUP_SUBSYS_COUNT] = {
+/* generate an array of cgroup subsystem pointers */
+#define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys,
+static struct cgroup_subsys *cgroup_subsys[] = {
+#include <linux/cgroup_subsys.h>
+};
+#undef SUBSYS
+
+/* array of cgroup subsystem names */
+#define SUBSYS(_x) [_x ## _cgrp_id] = #_x,
+static const char *cgroup_subsys_name[] = {
#include <linux/cgroup_subsys.h>
};
+#undef SUBSYS
/*
- * The dummy hierarchy, reserved for the subsystems that are otherwise
+ * The default hierarchy, reserved for the subsystems that are otherwise
* unattached - it never has more than a single cgroup, and all tasks are
* part of that cgroup.
*/
-static struct cgroupfs_root cgroup_dummy_root;
+struct cgroup_root cgrp_dfl_root;
-/* dummy_top is a shorthand for the dummy hierarchy's top cgroup */
-static struct cgroup * const cgroup_dummy_top = &cgroup_dummy_root.top_cgroup;
+/*
+ * The default hierarchy always exists but is hidden until mounted for the
+ * first time. This is for backward compatibility.
+ */
+static bool cgrp_dfl_root_visible;
/* The list of hierarchy roots */
static LIST_HEAD(cgroup_roots);
static int cgroup_root_count;
-/*
- * Hierarchy ID allocation and mapping. It follows the same exclusion
- * rules as other root ops - both cgroup_mutex and cgroup_root_mutex for
- * writes, either for reads.
- */
+/* hierarchy ID allocation and mapping, protected by cgroup_mutex */
static DEFINE_IDR(cgroup_hierarchy_idr);
-static struct cgroup_name root_cgroup_name = { .name = "/" };
-
/*
* Assign a monotonically increasing serial number to cgroups. It
* guarantees cgroups with bigger numbers are newer than those with smaller
@@ -175,11 +177,13 @@ static int need_forkexit_callback __read_mostly;
static struct cftype cgroup_base_files[];
+static void cgroup_put(struct cgroup *cgrp);
+static int rebind_subsystems(struct cgroup_root *dst_root,
+ unsigned long ss_mask);
static void cgroup_destroy_css_killed(struct cgroup *cgrp);
static int cgroup_destroy_locked(struct cgroup *cgrp);
static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[],
bool is_add);
-static int cgroup_file_release(struct inode *inode, struct file *file);
static void cgroup_pidlist_destroy_all(struct cgroup *cgrp);
/**
@@ -197,8 +201,9 @@ static struct cgroup_subsys_state *cgroup_css(struct cgroup *cgrp,
struct cgroup_subsys *ss)
{
if (ss)
- return rcu_dereference_check(cgrp->subsys[ss->subsys_id],
- lockdep_is_held(&cgroup_mutex));
+ return rcu_dereference_check(cgrp->subsys[ss->id],
+ lockdep_is_held(&cgroup_tree_mutex) ||
+ lockdep_is_held(&cgroup_mutex));
else
return &cgrp->dummy_css;
}
@@ -209,6 +214,27 @@ static inline bool cgroup_is_dead(const struct cgroup *cgrp)
return test_bit(CGRP_DEAD, &cgrp->flags);
}
+struct cgroup_subsys_state *seq_css(struct seq_file *seq)
+{
+ struct kernfs_open_file *of = seq->private;
+ struct cgroup *cgrp = of->kn->parent->priv;
+ struct cftype *cft = seq_cft(seq);
+
+ /*
+ * This is open and unprotected implementation of cgroup_css().
+ * seq_css() is only called from a kernfs file operation which has
+ * an active reference on the file. Because all the subsystem
+ * files are drained before a css is disassociated with a cgroup,
+ * the matching css from the cgroup's subsys table is guaranteed to
+ * be and stay valid until the enclosing operation is complete.
+ */
+ if (cft->ss)
+ return rcu_dereference_raw(cgrp->subsys[cft->ss->id]);
+ else
+ return &cgrp->dummy_css;
+}
+EXPORT_SYMBOL_GPL(seq_css);
+
/**
* cgroup_is_descendant - test ancestry
* @cgrp: the cgroup to be tested
@@ -227,7 +253,6 @@ bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor)
}
return false;
}
-EXPORT_SYMBOL_GPL(cgroup_is_descendant);
static int cgroup_is_releasable(const struct cgroup *cgrp)
{
@@ -254,54 +279,23 @@ static int notify_on_release(const struct cgroup *cgrp)
for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT; (ssid)++) \
if (!((css) = rcu_dereference_check( \
(cgrp)->subsys[(ssid)], \
+ lockdep_is_held(&cgroup_tree_mutex) || \
lockdep_is_held(&cgroup_mutex)))) { } \
else
/**
- * for_each_subsys - iterate all loaded cgroup subsystems
+ * for_each_subsys - iterate all enabled cgroup subsystems
* @ss: the iteration cursor
* @ssid: the index of @ss, CGROUP_SUBSYS_COUNT after reaching the end
- *
- * Iterates through all loaded subsystems. Should be called under
- * cgroup_mutex or cgroup_root_mutex.
*/
#define for_each_subsys(ss, ssid) \
- for (({ cgroup_assert_mutex_or_root_locked(); (ssid) = 0; }); \
- (ssid) < CGROUP_SUBSYS_COUNT; (ssid)++) \
- if (!((ss) = cgroup_subsys[(ssid)])) { } \
- else
+ for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT && \
+ (((ss) = cgroup_subsys[ssid]) || true); (ssid)++)
-/**
- * for_each_builtin_subsys - iterate all built-in cgroup subsystems
- * @ss: the iteration cursor
- * @i: the index of @ss, CGROUP_BUILTIN_SUBSYS_COUNT after reaching the end
- *
- * Bulit-in subsystems are always present and iteration itself doesn't
- * require any synchronization.
- */
-#define for_each_builtin_subsys(ss, i) \
- for ((i) = 0; (i) < CGROUP_BUILTIN_SUBSYS_COUNT && \
- (((ss) = cgroup_subsys[i]) || true); (i)++)
-
-/* iterate across the active hierarchies */
-#define for_each_active_root(root) \
+/* iterate across the hierarchies */
+#define for_each_root(root) \
list_for_each_entry((root), &cgroup_roots, root_list)
-static inline struct cgroup *__d_cgrp(struct dentry *dentry)
-{
- return dentry->d_fsdata;
-}
-
-static inline struct cfent *__d_cfe(struct dentry *dentry)
-{
- return dentry->d_fsdata;
-}
-
-static inline struct cftype *__d_cft(struct dentry *dentry)
-{
- return __d_cfe(dentry)->type;
-}
-
/**
* cgroup_lock_live_group - take cgroup_mutex and check that cgrp is alive.
* @cgrp: the cgroup to be checked for liveness
@@ -347,23 +341,23 @@ struct cgrp_cset_link {
struct list_head cgrp_link;
};
-/* The default css_set - used by init and its children prior to any
+/*
+ * The default css_set - used by init and its children prior to any
* hierarchies being mounted. It contains a pointer to the root state
* for each subsystem. Also used to anchor the list of css_sets. Not
* reference-counted, to improve performance when child cgroups
* haven't been created.
*/
+static struct css_set init_css_set = {
+ .refcount = ATOMIC_INIT(1),
+ .cgrp_links = LIST_HEAD_INIT(init_css_set.cgrp_links),
+ .tasks = LIST_HEAD_INIT(init_css_set.tasks),
+ .mg_tasks = LIST_HEAD_INIT(init_css_set.mg_tasks),
+ .mg_preload_node = LIST_HEAD_INIT(init_css_set.mg_preload_node),
+ .mg_node = LIST_HEAD_INIT(init_css_set.mg_node),
+};
-static struct css_set init_css_set;
-static struct cgrp_cset_link init_cgrp_cset_link;
-
-/*
- * css_set_lock protects the list of css_set objects, and the chain of
- * tasks off each css_set. Nests outside task->alloc_lock due to
- * css_task_iter_start().
- */
-static DEFINE_RWLOCK(css_set_lock);
-static int css_set_count;
+static int css_set_count = 1; /* 1 for init_css_set */
/*
* hash table for cgroup groups. This improves the performance to find
@@ -386,30 +380,14 @@ static unsigned long css_set_hash(struct cgroup_subsys_state *css[])
return key;
}
-/*
- * We don't maintain the lists running through each css_set to its task
- * until after the first call to css_task_iter_start(). This reduces the
- * fork()/exit() overhead for people who have cgroups compiled into their
- * kernel but not actually in use.
- */
-static int use_task_css_set_links __read_mostly;
-
-static void __put_css_set(struct css_set *cset, int taskexit)
+static void put_css_set_locked(struct css_set *cset, bool taskexit)
{
struct cgrp_cset_link *link, *tmp_link;
- /*
- * Ensure that the refcount doesn't hit zero while any readers
- * can see it. Similar to atomic_dec_and_lock(), but for an
- * rwlock
- */
- if (atomic_add_unless(&cset->refcount, -1, 1))
- return;
- write_lock(&css_set_lock);
- if (!atomic_dec_and_test(&cset->refcount)) {
- write_unlock(&css_set_lock);
+ lockdep_assert_held(&css_set_rwsem);
+
+ if (!atomic_dec_and_test(&cset->refcount))
return;
- }
/* This css_set is dead. unlink it and release cgroup refcounts */
hash_del(&cset->hlist);
@@ -421,7 +399,7 @@ static void __put_css_set(struct css_set *cset, int taskexit)
list_del(&link->cset_link);
list_del(&link->cgrp_link);
- /* @cgrp can't go away while we're holding css_set_lock */
+ /* @cgrp can't go away while we're holding css_set_rwsem */
if (list_empty(&cgrp->cset_links) && notify_on_release(cgrp)) {
if (taskexit)
set_bit(CGRP_RELEASABLE, &cgrp->flags);
@@ -431,10 +409,24 @@ static void __put_css_set(struct css_set *cset, int taskexit)
kfree(link);
}
- write_unlock(&css_set_lock);
kfree_rcu(cset, rcu_head);
}
+static void put_css_set(struct css_set *cset, bool taskexit)
+{
+ /*
+ * Ensure that the refcount doesn't hit zero while any readers
+ * can see it. Similar to atomic_dec_and_lock(), but for an
+ * rwlock
+ */
+ if (atomic_add_unless(&cset->refcount, -1, 1))
+ return;
+
+ down_write(&css_set_rwsem);
+ put_css_set_locked(cset, taskexit);
+ up_write(&css_set_rwsem);
+}
+
/*
* refcounted get/put for css_set objects
*/
@@ -443,16 +435,6 @@ static inline void get_css_set(struct css_set *cset)
atomic_inc(&cset->refcount);
}
-static inline void put_css_set(struct css_set *cset)
-{
- __put_css_set(cset, 0);
-}
-
-static inline void put_css_set_taskexit(struct css_set *cset)
-{
- __put_css_set(cset, 1);
-}
-
/**
* compare_css_sets - helper function for find_existing_css_set().
* @cset: candidate css_set being tested
@@ -535,7 +517,7 @@ static struct css_set *find_existing_css_set(struct css_set *old_cset,
struct cgroup *cgrp,
struct cgroup_subsys_state *template[])
{
- struct cgroupfs_root *root = cgrp->root;
+ struct cgroup_root *root = cgrp->root;
struct cgroup_subsys *ss;
struct css_set *cset;
unsigned long key;
@@ -547,7 +529,7 @@ static struct css_set *find_existing_css_set(struct css_set *old_cset,
* won't change, so no need for locking.
*/
for_each_subsys(ss, i) {
- if (root->subsys_mask & (1UL << i)) {
+ if (root->cgrp.subsys_mask & (1UL << i)) {
/* Subsystem is in this hierarchy. So we want
* the subsystem state from the new
* cgroup */
@@ -652,11 +634,11 @@ static struct css_set *find_css_set(struct css_set *old_cset,
/* First see if we already have a cgroup group that matches
* the desired set */
- read_lock(&css_set_lock);
+ down_read(&css_set_rwsem);
cset = find_existing_css_set(old_cset, cgrp, template);
if (cset)
get_css_set(cset);
- read_unlock(&css_set_lock);
+ up_read(&css_set_rwsem);
if (cset)
return cset;
@@ -674,13 +656,16 @@ static struct css_set *find_css_set(struct css_set *old_cset,
atomic_set(&cset->refcount, 1);
INIT_LIST_HEAD(&cset->cgrp_links);
INIT_LIST_HEAD(&cset->tasks);
+ INIT_LIST_HEAD(&cset->mg_tasks);
+ INIT_LIST_HEAD(&cset->mg_preload_node);
+ INIT_LIST_HEAD(&cset->mg_node);
INIT_HLIST_NODE(&cset->hlist);
/* Copy the set of subsystem state objects generated in
* find_existing_css_set() */
memcpy(cset->subsys, template, sizeof(cset->subsys));
- write_lock(&css_set_lock);
+ down_write(&css_set_rwsem);
/* Add reference counts and links from the new css_set. */
list_for_each_entry(link, &old_cset->cgrp_links, cgrp_link) {
struct cgroup *c = link->cgrp;
@@ -698,31 +683,105 @@ 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);
- write_unlock(&css_set_lock);
+ up_write(&css_set_rwsem);
return cset;
}
-/*
- * Return the cgroup for "task" from the given hierarchy. Must be
- * called with cgroup_mutex held.
- */
-static struct cgroup *task_cgroup_from_root(struct task_struct *task,
- struct cgroupfs_root *root)
+static struct cgroup_root *cgroup_root_from_kf(struct kernfs_root *kf_root)
{
- struct css_set *cset;
- struct cgroup *res = NULL;
+ struct cgroup *root_cgrp = kf_root->kn->priv;
+
+ return root_cgrp->root;
+}
+
+static int cgroup_init_root_id(struct cgroup_root *root)
+{
+ int id;
+
+ lockdep_assert_held(&cgroup_mutex);
+
+ id = idr_alloc_cyclic(&cgroup_hierarchy_idr, root, 0, 0, GFP_KERNEL);
+ if (id < 0)
+ return id;
+
+ root->hierarchy_id = id;
+ return 0;
+}
+
+static void cgroup_exit_root_id(struct cgroup_root *root)
+{
+ lockdep_assert_held(&cgroup_mutex);
+
+ if (root->hierarchy_id) {
+ idr_remove(&cgroup_hierarchy_idr, root->hierarchy_id);
+ root->hierarchy_id = 0;
+ }
+}
+
+static void cgroup_free_root(struct cgroup_root *root)
+{
+ if (root) {
+ /* hierarhcy ID shoulid already have been released */
+ WARN_ON_ONCE(root->hierarchy_id);
+
+ idr_destroy(&root->cgroup_idr);
+ kfree(root);
+ }
+}
+
+static void cgroup_destroy_root(struct cgroup_root *root)
+{
+ struct cgroup *cgrp = &root->cgrp;
+ struct cgrp_cset_link *link, *tmp_link;
+
+ mutex_lock(&cgroup_tree_mutex);
+ mutex_lock(&cgroup_mutex);
+
+ BUG_ON(atomic_read(&root->nr_cgrps));
+ BUG_ON(!list_empty(&cgrp->children));
+
+ /* Rebind all subsystems back to the default hierarchy */
+ rebind_subsystems(&cgrp_dfl_root, cgrp->subsys_mask);
- BUG_ON(!mutex_is_locked(&cgroup_mutex));
- read_lock(&css_set_lock);
/*
- * No need to lock the task - since we hold cgroup_mutex the
- * task can't change groups, so the only thing that can happen
- * is that it exits and its css is set back to init_css_set.
+ * Release all the links from cset_links to this hierarchy's
+ * root cgroup
*/
- cset = task_css_set(task);
+ down_write(&css_set_rwsem);
+
+ list_for_each_entry_safe(link, tmp_link, &cgrp->cset_links, cset_link) {
+ list_del(&link->cset_link);
+ list_del(&link->cgrp_link);
+ kfree(link);
+ }
+ up_write(&css_set_rwsem);
+
+ if (!list_empty(&root->root_list)) {
+ list_del(&root->root_list);
+ cgroup_root_count--;
+ }
+
+ cgroup_exit_root_id(root);
+
+ mutex_unlock(&cgroup_mutex);
+ mutex_unlock(&cgroup_tree_mutex);
+
+ kernfs_destroy_root(root->kf_root);
+ cgroup_free_root(root);
+}
+
+/* look up cgroup associated with given css_set on the specified hierarchy */
+static struct cgroup *cset_cgroup_from_root(struct css_set *cset,
+ struct cgroup_root *root)
+{
+ struct cgroup *res = NULL;
+
+ lockdep_assert_held(&cgroup_mutex);
+ lockdep_assert_held(&css_set_rwsem);
+
if (cset == &init_css_set) {
- res = &root->top_cgroup;
+ res = &root->cgrp;
} else {
struct cgrp_cset_link *link;
@@ -735,16 +794,27 @@ static struct cgroup *task_cgroup_from_root(struct task_struct *task,
}
}
}
- read_unlock(&css_set_lock);
+
BUG_ON(!res);
return res;
}
/*
- * There is one global cgroup mutex. We also require taking
- * task_lock() when dereferencing a task's cgroup subsys pointers.
- * See "The task_lock() exception", at the end of this comment.
- *
+ * Return the cgroup for "task" from the given hierarchy. Must be
+ * called with cgroup_mutex and css_set_rwsem held.
+ */
+static struct cgroup *task_cgroup_from_root(struct task_struct *task,
+ struct cgroup_root *root)
+{
+ /*
+ * No need to lock the task - since we hold cgroup_mutex the
+ * task can't change groups, so the only thing that can happen
+ * is that it exits and its css is set back to init_css_set.
+ */
+ return cset_cgroup_from_root(task_css_set(task), root);
+}
+
+/*
* A task must hold cgroup_mutex to modify cgroups.
*
* Any task can increment and decrement the count field without lock.
@@ -770,98 +840,79 @@ static struct cgroup *task_cgroup_from_root(struct task_struct *task,
* A cgroup can only be deleted if both its 'count' of using tasks
* is zero, and its list of 'children' cgroups is empty. Since all
* tasks in the system use _some_ cgroup, and since there is always at
- * least one task in the system (init, pid == 1), therefore, top_cgroup
+ * least one task in the system (init, pid == 1), therefore, root cgroup
* always has either children cgroups and/or using tasks. So we don't
- * need a special hack to ensure that top_cgroup cannot be deleted.
- *
- * The task_lock() exception
- *
- * The need for this exception arises from the action of
- * cgroup_attach_task(), which overwrites one task's cgroup pointer with
- * another. It does so using cgroup_mutex, however there are
- * several performance critical places that need to reference
- * task->cgroup without the expense of grabbing a system global
- * mutex. Therefore except as noted below, when dereferencing or, as
- * in cgroup_attach_task(), modifying a task's cgroup pointer we use
- * task_lock(), which acts on a spinlock (task->alloc_lock) already in
- * the task_struct routinely used for such matters.
+ * need a special hack to ensure that root cgroup cannot be deleted.
*
* P.S. One more locking exception. RCU is used to guard the
* update of a tasks cgroup pointer by cgroup_attach_task()
*/
-/*
- * A couple of forward declarations required, due to cyclic reference loop:
- * cgroup_mkdir -> cgroup_create -> cgroup_populate_dir ->
- * cgroup_add_file -> cgroup_create_file -> cgroup_dir_inode_operations
- * -> cgroup_mkdir.
- */
-
-static int cgroup_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode);
-static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry);
static int cgroup_populate_dir(struct cgroup *cgrp, unsigned long subsys_mask);
-static const struct inode_operations cgroup_dir_inode_operations;
+static struct kernfs_syscall_ops cgroup_kf_syscall_ops;
static const struct file_operations proc_cgroupstats_operations;
-static struct backing_dev_info cgroup_backing_dev_info = {
- .name = "cgroup",
- .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
-};
-
-static struct inode *cgroup_new_inode(umode_t mode, struct super_block *sb)
+static char *cgroup_file_name(struct cgroup *cgrp, const struct cftype *cft,
+ char *buf)
{
- struct inode *inode = new_inode(sb);
-
- if (inode) {
- inode->i_ino = get_next_ino();
- inode->i_mode = mode;
- inode->i_uid = current_fsuid();
- inode->i_gid = current_fsgid();
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->i_mapping->backing_dev_info = &cgroup_backing_dev_info;
- }
- return inode;
+ if (cft->ss && !(cft->flags & CFTYPE_NO_PREFIX) &&
+ !(cgrp->root->flags & CGRP_ROOT_NOPREFIX))
+ snprintf(buf, CGROUP_FILE_NAME_MAX, "%s.%s",
+ cft->ss->name, cft->name);
+ else
+ strncpy(buf, cft->name, CGROUP_FILE_NAME_MAX);
+ return buf;
}
-static struct cgroup_name *cgroup_alloc_name(struct dentry *dentry)
+/**
+ * cgroup_file_mode - deduce file mode of a control file
+ * @cft: the control file in question
+ *
+ * returns cft->mode if ->mode is not 0
+ * returns S_IRUGO|S_IWUSR if it has both a read and a write handler
+ * returns S_IRUGO if it has only a read handler
+ * returns S_IWUSR if it has only a write hander
+ */
+static umode_t cgroup_file_mode(const struct cftype *cft)
{
- struct cgroup_name *name;
+ umode_t mode = 0;
- name = kmalloc(sizeof(*name) + dentry->d_name.len + 1, GFP_KERNEL);
- if (!name)
- return NULL;
- strcpy(name->name, dentry->d_name.name);
- return name;
+ if (cft->mode)
+ return cft->mode;
+
+ if (cft->read_u64 || cft->read_s64 || cft->seq_show)
+ mode |= S_IRUGO;
+
+ if (cft->write_u64 || cft->write_s64 || cft->write_string ||
+ cft->trigger)
+ mode |= S_IWUSR;
+
+ return mode;
}
static void cgroup_free_fn(struct work_struct *work)
{
struct cgroup *cgrp = container_of(work, struct cgroup, destroy_work);
- mutex_lock(&cgroup_mutex);
- cgrp->root->number_of_cgroups--;
- mutex_unlock(&cgroup_mutex);
-
- /*
- * We get a ref to the parent's dentry, and put the ref when
- * this cgroup is being freed, so it's guaranteed that the
- * parent won't be destroyed before its children.
- */
- dput(cgrp->parent->dentry);
-
- /*
- * Drop the active superblock reference that we took when we
- * created the cgroup. This will free cgrp->root, if we are
- * holding the last reference to @sb.
- */
- deactivate_super(cgrp->root->sb);
-
+ atomic_dec(&cgrp->root->nr_cgrps);
cgroup_pidlist_destroy_all(cgrp);
- simple_xattrs_free(&cgrp->xattrs);
-
- kfree(rcu_dereference_raw(cgrp->name));
- kfree(cgrp);
+ if (cgrp->parent) {
+ /*
+ * We get a ref to the parent, and put the ref when this
+ * cgroup is being freed, so it's guaranteed that the
+ * parent won't be destroyed before its children.
+ */
+ cgroup_put(cgrp->parent);
+ kernfs_put(cgrp->kn);
+ kfree(cgrp);
+ } else {
+ /*
+ * This is root cgroup's refcnt reaching zero, which
+ * indicates that the root should be released.
+ */
+ cgroup_destroy_root(cgrp->root);
+ }
}
static void cgroup_free_rcu(struct rcu_head *head)
@@ -872,73 +923,40 @@ static void cgroup_free_rcu(struct rcu_head *head)
queue_work(cgroup_destroy_wq, &cgrp->destroy_work);
}
-static void cgroup_diput(struct dentry *dentry, struct inode *inode)
-{
- /* is dentry a directory ? if so, kfree() associated cgroup */
- if (S_ISDIR(inode->i_mode)) {
- struct cgroup *cgrp = dentry->d_fsdata;
-
- BUG_ON(!(cgroup_is_dead(cgrp)));
-
- /*
- * XXX: cgrp->id is only used to look up css's. As cgroup
- * and css's lifetimes will be decoupled, it should be made
- * per-subsystem and moved to css->id so that lookups are
- * successful until the target css is released.
- */
- mutex_lock(&cgroup_mutex);
- idr_remove(&cgrp->root->cgroup_idr, cgrp->id);
- mutex_unlock(&cgroup_mutex);
- cgrp->id = -1;
-
- call_rcu(&cgrp->rcu_head, cgroup_free_rcu);
- } else {
- struct cfent *cfe = __d_cfe(dentry);
- struct cgroup *cgrp = dentry->d_parent->d_fsdata;
-
- WARN_ONCE(!list_empty(&cfe->node) &&
- cgrp != &cgrp->root->top_cgroup,
- "cfe still linked for %s\n", cfe->type->name);
- simple_xattrs_free(&cfe->xattrs);
- kfree(cfe);
- }
- iput(inode);
-}
-
-static void remove_dir(struct dentry *d)
+static void cgroup_get(struct cgroup *cgrp)
{
- struct dentry *parent = dget(d->d_parent);
-
- d_delete(d);
- simple_rmdir(parent->d_inode, d);
- dput(parent);
+ WARN_ON_ONCE(cgroup_is_dead(cgrp));
+ WARN_ON_ONCE(atomic_read(&cgrp->refcnt) <= 0);
+ atomic_inc(&cgrp->refcnt);
}
-static void cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
+static void cgroup_put(struct cgroup *cgrp)
{
- struct cfent *cfe;
-
- lockdep_assert_held(&cgrp->dentry->d_inode->i_mutex);
- lockdep_assert_held(&cgroup_mutex);
+ if (!atomic_dec_and_test(&cgrp->refcnt))
+ return;
+ if (WARN_ON_ONCE(cgrp->parent && !cgroup_is_dead(cgrp)))
+ return;
/*
- * If we're doing cleanup due to failure of cgroup_create(),
- * the corresponding @cfe may not exist.
+ * XXX: cgrp->id is only used to look up css's. As cgroup and
+ * css's lifetimes will be decoupled, it should be made
+ * per-subsystem and moved to css->id so that lookups are
+ * successful until the target css is released.
*/
- list_for_each_entry(cfe, &cgrp->files, node) {
- struct dentry *d = cfe->dentry;
+ mutex_lock(&cgroup_mutex);
+ idr_remove(&cgrp->root->cgroup_idr, cgrp->id);
+ mutex_unlock(&cgroup_mutex);
+ cgrp->id = -1;
- if (cft && cfe->type != cft)
- continue;
+ call_rcu(&cgrp->rcu_head, cgroup_free_rcu);
+}
- dget(d);
- d_delete(d);
- simple_unlink(cgrp->dentry->d_inode, d);
- list_del_init(&cfe->node);
- dput(d);
+static void cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
+{
+ char name[CGROUP_FILE_NAME_MAX];
- break;
- }
+ lockdep_assert_held(&cgroup_tree_mutex);
+ kernfs_remove_by_name(cgrp->kn, cgroup_file_name(cgrp, cft, name));
}
/**
@@ -952,144 +970,106 @@ static void cgroup_clear_dir(struct cgroup *cgrp, unsigned long subsys_mask)
int i;
for_each_subsys(ss, i) {
- struct cftype_set *set;
+ struct cftype *cfts;
if (!test_bit(i, &subsys_mask))
continue;
- list_for_each_entry(set, &ss->cftsets, node)
- cgroup_addrm_files(cgrp, set->cfts, false);
+ list_for_each_entry(cfts, &ss->cfts, node)
+ cgroup_addrm_files(cgrp, cfts, false);
}
}
-/*
- * NOTE : the dentry must have been dget()'ed
- */
-static void cgroup_d_remove_dir(struct dentry *dentry)
-{
- struct dentry *parent;
-
- parent = dentry->d_parent;
- spin_lock(&parent->d_lock);
- spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
- list_del_init(&dentry->d_u.d_child);
- spin_unlock(&dentry->d_lock);
- spin_unlock(&parent->d_lock);
- remove_dir(dentry);
-}
-
-/*
- * Call with cgroup_mutex held. Drops reference counts on modules, including
- * any duplicate ones that parse_cgroupfs_options took. If this function
- * returns an error, no reference counts are touched.
- */
-static int rebind_subsystems(struct cgroupfs_root *root,
- unsigned long added_mask, unsigned removed_mask)
+static int rebind_subsystems(struct cgroup_root *dst_root,
+ unsigned long ss_mask)
{
- struct cgroup *cgrp = &root->top_cgroup;
struct cgroup_subsys *ss;
- unsigned long pinned = 0;
- int i, ret;
+ int ssid, ret;
- BUG_ON(!mutex_is_locked(&cgroup_mutex));
- BUG_ON(!mutex_is_locked(&cgroup_root_mutex));
+ lockdep_assert_held(&cgroup_tree_mutex);
+ lockdep_assert_held(&cgroup_mutex);
- /* Check that any added subsystems are currently free */
- for_each_subsys(ss, i) {
- if (!(added_mask & (1 << i)))
+ for_each_subsys(ss, ssid) {
+ if (!(ss_mask & (1 << ssid)))
continue;
- /* is the subsystem mounted elsewhere? */
- if (ss->root != &cgroup_dummy_root) {
- ret = -EBUSY;
- goto out_put;
- }
+ /* if @ss is on the dummy_root, we can always move it */
+ if (ss->root == &cgrp_dfl_root)
+ continue;
- /* pin the module */
- if (!try_module_get(ss->module)) {
- ret = -ENOENT;
- goto out_put;
- }
- pinned |= 1 << i;
- }
+ /* if @ss has non-root cgroups attached to it, can't move */
+ if (!list_empty(&ss->root->cgrp.children))
+ return -EBUSY;
- /* subsys could be missing if unloaded between parsing and here */
- if (added_mask != pinned) {
- ret = -ENOENT;
- goto out_put;
+ /* can't move between two non-dummy roots either */
+ if (dst_root != &cgrp_dfl_root)
+ return -EBUSY;
}
- ret = cgroup_populate_dir(cgrp, added_mask);
- if (ret)
- goto out_put;
+ ret = cgroup_populate_dir(&dst_root->cgrp, ss_mask);
+ if (ret) {
+ if (dst_root != &cgrp_dfl_root)
+ return ret;
+
+ /*
+ * Rebinding back to the default root is not allowed to
+ * fail. Using both default and non-default roots should
+ * be rare. Moving subsystems back and forth even more so.
+ * Just warn about it and continue.
+ */
+ if (cgrp_dfl_root_visible) {
+ pr_warning("cgroup: failed to create files (%d) while rebinding 0x%lx to default root\n",
+ ret, ss_mask);
+ pr_warning("cgroup: you may retry by moving them to a different hierarchy and unbinding\n");
+ }
+ }
/*
* Nothing can fail from this point on. Remove files for the
* removed subsystems and rebind each subsystem.
*/
- cgroup_clear_dir(cgrp, removed_mask);
-
- for_each_subsys(ss, i) {
- unsigned long bit = 1UL << i;
-
- if (bit & added_mask) {
- /* We're binding this subsystem to this hierarchy */
- BUG_ON(cgroup_css(cgrp, ss));
- BUG_ON(!cgroup_css(cgroup_dummy_top, ss));
- BUG_ON(cgroup_css(cgroup_dummy_top, ss)->cgroup != cgroup_dummy_top);
+ mutex_unlock(&cgroup_mutex);
+ for_each_subsys(ss, ssid)
+ if (ss_mask & (1 << ssid))
+ cgroup_clear_dir(&ss->root->cgrp, 1 << ssid);
+ mutex_lock(&cgroup_mutex);
- rcu_assign_pointer(cgrp->subsys[i],
- cgroup_css(cgroup_dummy_top, ss));
- cgroup_css(cgrp, ss)->cgroup = cgrp;
+ for_each_subsys(ss, ssid) {
+ struct cgroup_root *src_root;
+ struct cgroup_subsys_state *css;
- ss->root = root;
- if (ss->bind)
- ss->bind(cgroup_css(cgrp, ss));
+ if (!(ss_mask & (1 << ssid)))
+ continue;
- /* refcount was already taken, and we're keeping it */
- root->subsys_mask |= bit;
- } else if (bit & removed_mask) {
- /* We're removing this subsystem */
- BUG_ON(cgroup_css(cgrp, ss) != cgroup_css(cgroup_dummy_top, ss));
- BUG_ON(cgroup_css(cgrp, ss)->cgroup != cgrp);
+ src_root = ss->root;
+ css = cgroup_css(&src_root->cgrp, ss);
- if (ss->bind)
- ss->bind(cgroup_css(cgroup_dummy_top, ss));
+ WARN_ON(!css || cgroup_css(&dst_root->cgrp, ss));
- cgroup_css(cgroup_dummy_top, ss)->cgroup = cgroup_dummy_top;
- RCU_INIT_POINTER(cgrp->subsys[i], NULL);
+ RCU_INIT_POINTER(src_root->cgrp.subsys[ssid], NULL);
+ rcu_assign_pointer(dst_root->cgrp.subsys[ssid], css);
+ ss->root = dst_root;
+ css->cgroup = &dst_root->cgrp;
- cgroup_subsys[i]->root = &cgroup_dummy_root;
+ src_root->cgrp.subsys_mask &= ~(1 << ssid);
+ dst_root->cgrp.subsys_mask |= 1 << ssid;
- /* subsystem is now free - drop reference on module */
- module_put(ss->module);
- root->subsys_mask &= ~bit;
- }
+ if (ss->bind)
+ ss->bind(css);
}
- /*
- * Mark @root has finished binding subsystems. @root->subsys_mask
- * now matches the bound subsystems.
- */
- root->flags |= CGRP_ROOT_SUBSYS_BOUND;
-
+ kernfs_activate(dst_root->cgrp.kn);
return 0;
-
-out_put:
- for_each_subsys(ss, i)
- if (pinned & (1 << i))
- module_put(ss->module);
- return ret;
}
-static int cgroup_show_options(struct seq_file *seq, struct dentry *dentry)
+static int cgroup_show_options(struct seq_file *seq,
+ struct kernfs_root *kf_root)
{
- struct cgroupfs_root *root = dentry->d_sb->s_fs_info;
+ struct cgroup_root *root = cgroup_root_from_kf(kf_root);
struct cgroup_subsys *ss;
int ssid;
- mutex_lock(&cgroup_root_mutex);
for_each_subsys(ss, ssid)
- if (root->subsys_mask & (1 << ssid))
+ if (root->cgrp.subsys_mask & (1 << ssid))
seq_printf(seq, ",%s", ss->name);
if (root->flags & CGRP_ROOT_SANE_BEHAVIOR)
seq_puts(seq, ",sane_behavior");
@@ -1097,13 +1077,16 @@ static int cgroup_show_options(struct seq_file *seq, struct dentry *dentry)
seq_puts(seq, ",noprefix");
if (root->flags & CGRP_ROOT_XATTR)
seq_puts(seq, ",xattr");
+
+ spin_lock(&release_agent_path_lock);
if (strlen(root->release_agent_path))
seq_printf(seq, ",release_agent=%s", root->release_agent_path);
- if (test_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->top_cgroup.flags))
+ spin_unlock(&release_agent_path_lock);
+
+ if (test_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags))
seq_puts(seq, ",clone_children");
if (strlen(root->name))
seq_printf(seq, ",name=%s", root->name);
- mutex_unlock(&cgroup_root_mutex);
return 0;
}
@@ -1115,9 +1098,6 @@ struct cgroup_sb_opts {
char *name;
/* User explicitly requested empty subsystem */
bool none;
-
- struct cgroupfs_root *new_root;
-
};
/*
@@ -1137,7 +1117,7 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
BUG_ON(!mutex_is_locked(&cgroup_mutex));
#ifdef CONFIG_CPUSETS
- mask = ~(1UL << cpuset_subsys_id);
+ mask = ~(1UL << cpuset_cgrp_id);
#endif
memset(opts, 0, sizeof(*opts));
@@ -1227,30 +1207,34 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
return -ENOENT;
}
- /*
- * If the 'all' option was specified select all the subsystems,
- * otherwise if 'none', 'name=' and a subsystem name options
- * were not specified, let's default to 'all'
- */
- if (all_ss || (!one_ss && !opts->none && !opts->name))
- for_each_subsys(ss, i)
- if (!ss->disabled)
- set_bit(i, &opts->subsys_mask);
-
/* Consistency checks */
if (opts->flags & CGRP_ROOT_SANE_BEHAVIOR) {
pr_warning("cgroup: sane_behavior: this is still under development and its behaviors will change, proceed at your own risk\n");
- if (opts->flags & CGRP_ROOT_NOPREFIX) {
- pr_err("cgroup: sane_behavior: noprefix is not allowed\n");
+ if ((opts->flags & (CGRP_ROOT_NOPREFIX | CGRP_ROOT_XATTR)) ||
+ opts->cpuset_clone_children || opts->release_agent ||
+ opts->name) {
+ pr_err("cgroup: sane_behavior: noprefix, xattr, clone_children, release_agent and name are not allowed\n");
return -EINVAL;
}
+ } else {
+ /*
+ * If the 'all' option was specified select all the
+ * subsystems, otherwise if 'none', 'name=' and a subsystem
+ * name options were not specified, let's default to 'all'
+ */
+ if (all_ss || (!one_ss && !opts->none && !opts->name))
+ for_each_subsys(ss, i)
+ if (!ss->disabled)
+ set_bit(i, &opts->subsys_mask);
- if (opts->cpuset_clone_children) {
- pr_err("cgroup: sane_behavior: clone_children is not allowed\n");
+ /*
+ * We either have to specify by name or by subsystems. (So
+ * all empty hierarchies must have a name).
+ */
+ if (!opts->subsys_mask && !opts->name)
return -EINVAL;
- }
}
/*
@@ -1266,21 +1250,13 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
if (opts->subsys_mask && opts->none)
return -EINVAL;
- /*
- * We either have to specify by name or by subsystems. (So all
- * empty hierarchies must have a name).
- */
- if (!opts->subsys_mask && !opts->name)
- return -EINVAL;
-
return 0;
}
-static int cgroup_remount(struct super_block *sb, int *flags, char *data)
+static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data)
{
int ret = 0;
- struct cgroupfs_root *root = sb->s_fs_info;
- struct cgroup *cgrp = &root->top_cgroup;
+ struct cgroup_root *root = cgroup_root_from_kf(kf_root);
struct cgroup_sb_opts opts;
unsigned long added_mask, removed_mask;
@@ -1289,21 +1265,20 @@ static int cgroup_remount(struct super_block *sb, int *flags, char *data)
return -EINVAL;
}
- mutex_lock(&cgrp->dentry->d_inode->i_mutex);
+ mutex_lock(&cgroup_tree_mutex);
mutex_lock(&cgroup_mutex);
- mutex_lock(&cgroup_root_mutex);
/* See what subsystems are wanted */
ret = parse_cgroupfs_options(data, &opts);
if (ret)
goto out_unlock;
- if (opts.subsys_mask != root->subsys_mask || opts.release_agent)
+ if (opts.subsys_mask != root->cgrp.subsys_mask || opts.release_agent)
pr_warning("cgroup: option changes via remount are deprecated (pid=%d comm=%s)\n",
task_tgid_nr(current), current->comm);
- added_mask = opts.subsys_mask & ~root->subsys_mask;
- removed_mask = root->subsys_mask & ~opts.subsys_mask;
+ added_mask = opts.subsys_mask & ~root->cgrp.subsys_mask;
+ removed_mask = root->cgrp.subsys_mask & ~opts.subsys_mask;
/* Don't allow flags or name to change at remount */
if (((opts.flags ^ root->flags) & CGRP_ROOT_OPTION_MASK) ||
@@ -1316,422 +1291,331 @@ static int cgroup_remount(struct super_block *sb, int *flags, char *data)
}
/* remounting is not allowed for populated hierarchies */
- if (root->number_of_cgroups > 1) {
+ if (!list_empty(&root->cgrp.children)) {
ret = -EBUSY;
goto out_unlock;
}
- ret = rebind_subsystems(root, added_mask, removed_mask);
+ ret = rebind_subsystems(root, added_mask);
if (ret)
goto out_unlock;
- if (opts.release_agent)
+ rebind_subsystems(&cgrp_dfl_root, removed_mask);
+
+ if (opts.release_agent) {
+ spin_lock(&release_agent_path_lock);
strcpy(root->release_agent_path, opts.release_agent);
+ spin_unlock(&release_agent_path_lock);
+ }
out_unlock:
kfree(opts.release_agent);
kfree(opts.name);
- mutex_unlock(&cgroup_root_mutex);
mutex_unlock(&cgroup_mutex);
- mutex_unlock(&cgrp->dentry->d_inode->i_mutex);
+ mutex_unlock(&cgroup_tree_mutex);
return ret;
}
-static const struct super_operations cgroup_ops = {
- .statfs = simple_statfs,
- .drop_inode = generic_delete_inode,
- .show_options = cgroup_show_options,
- .remount_fs = cgroup_remount,
-};
+/*
+ * To reduce the fork() overhead for systems that are not actually using
+ * their cgroups capability, we don't maintain the lists running through
+ * each css_set to its tasks until we see the list actually used - in other
+ * words after the first mount.
+ */
+static bool use_task_css_set_links __read_mostly;
+
+static void cgroup_enable_task_cg_lists(void)
+{
+ struct task_struct *p, *g;
+
+ down_write(&css_set_rwsem);
+
+ if (use_task_css_set_links)
+ goto out_unlock;
+
+ use_task_css_set_links = true;
+
+ /*
+ * We need tasklist_lock because RCU is not safe against
+ * while_each_thread(). Besides, a forking task that has passed
+ * cgroup_post_fork() without seeing use_task_css_set_links = 1
+ * is not guaranteed to have its child immediately visible in the
+ * tasklist if we walk through it with RCU.
+ */
+ read_lock(&tasklist_lock);
+ do_each_thread(g, p) {
+ WARN_ON_ONCE(!list_empty(&p->cg_list) ||
+ task_css_set(p) != &init_css_set);
+
+ /*
+ * We should check if the process is exiting, otherwise
+ * it will race with cgroup_exit() in that the list
+ * entry won't be deleted though the process has exited.
+ * Do it while holding siglock so that we don't end up
+ * racing against cgroup_exit().
+ */
+ spin_lock_irq(&p->sighand->siglock);
+ if (!(p->flags & PF_EXITING)) {
+ struct css_set *cset = task_css_set(p);
+
+ list_add(&p->cg_list, &cset->tasks);
+ get_css_set(cset);
+ }
+ spin_unlock_irq(&p->sighand->siglock);
+ } while_each_thread(g, p);
+ read_unlock(&tasklist_lock);
+out_unlock:
+ up_write(&css_set_rwsem);
+}
static void init_cgroup_housekeeping(struct cgroup *cgrp)
{
+ atomic_set(&cgrp->refcnt, 1);
INIT_LIST_HEAD(&cgrp->sibling);
INIT_LIST_HEAD(&cgrp->children);
- INIT_LIST_HEAD(&cgrp->files);
INIT_LIST_HEAD(&cgrp->cset_links);
INIT_LIST_HEAD(&cgrp->release_list);
INIT_LIST_HEAD(&cgrp->pidlists);
mutex_init(&cgrp->pidlist_mutex);
cgrp->dummy_css.cgroup = cgrp;
- simple_xattrs_init(&cgrp->xattrs);
}
-static void init_cgroup_root(struct cgroupfs_root *root)
+static void init_cgroup_root(struct cgroup_root *root,
+ struct cgroup_sb_opts *opts)
{
- struct cgroup *cgrp = &root->top_cgroup;
+ struct cgroup *cgrp = &root->cgrp;
INIT_LIST_HEAD(&root->root_list);
- root->number_of_cgroups = 1;
+ atomic_set(&root->nr_cgrps, 1);
cgrp->root = root;
- RCU_INIT_POINTER(cgrp->name, &root_cgroup_name);
init_cgroup_housekeeping(cgrp);
idr_init(&root->cgroup_idr);
-}
-
-static int cgroup_init_root_id(struct cgroupfs_root *root, int start, int end)
-{
- int id;
-
- lockdep_assert_held(&cgroup_mutex);
- lockdep_assert_held(&cgroup_root_mutex);
-
- id = idr_alloc_cyclic(&cgroup_hierarchy_idr, root, start, end,
- GFP_KERNEL);
- if (id < 0)
- return id;
-
- root->hierarchy_id = id;
- return 0;
-}
-
-static void cgroup_exit_root_id(struct cgroupfs_root *root)
-{
- lockdep_assert_held(&cgroup_mutex);
- lockdep_assert_held(&cgroup_root_mutex);
-
- if (root->hierarchy_id) {
- idr_remove(&cgroup_hierarchy_idr, root->hierarchy_id);
- root->hierarchy_id = 0;
- }
-}
-
-static int cgroup_test_super(struct super_block *sb, void *data)
-{
- struct cgroup_sb_opts *opts = data;
- struct cgroupfs_root *root = sb->s_fs_info;
-
- /* If we asked for a name then it must match */
- if (opts->name && strcmp(opts->name, root->name))
- return 0;
-
- /*
- * If we asked for subsystems (or explicitly for no
- * subsystems) then they must match
- */
- if ((opts->subsys_mask || opts->none)
- && (opts->subsys_mask != root->subsys_mask))
- return 0;
-
- return 1;
-}
-
-static struct cgroupfs_root *cgroup_root_from_opts(struct cgroup_sb_opts *opts)
-{
- struct cgroupfs_root *root;
- if (!opts->subsys_mask && !opts->none)
- return NULL;
-
- root = kzalloc(sizeof(*root), GFP_KERNEL);
- if (!root)
- return ERR_PTR(-ENOMEM);
-
- init_cgroup_root(root);
-
- /*
- * We need to set @root->subsys_mask now so that @root can be
- * matched by cgroup_test_super() before it finishes
- * initialization; otherwise, competing mounts with the same
- * options may try to bind the same subsystems instead of waiting
- * for the first one leading to unexpected mount errors.
- * SUBSYS_BOUND will be set once actual binding is complete.
- */
- root->subsys_mask = opts->subsys_mask;
root->flags = opts->flags;
if (opts->release_agent)
strcpy(root->release_agent_path, opts->release_agent);
if (opts->name)
strcpy(root->name, opts->name);
if (opts->cpuset_clone_children)
- set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->top_cgroup.flags);
- return root;
+ set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags);
}
-static void cgroup_free_root(struct cgroupfs_root *root)
+static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask)
{
- if (root) {
- /* hierarhcy ID shoulid already have been released */
- WARN_ON_ONCE(root->hierarchy_id);
-
- idr_destroy(&root->cgroup_idr);
- kfree(root);
- }
-}
+ LIST_HEAD(tmp_links);
+ struct cgroup *root_cgrp = &root->cgrp;
+ struct css_set *cset;
+ int i, ret;
-static int cgroup_set_super(struct super_block *sb, void *data)
-{
- int ret;
- struct cgroup_sb_opts *opts = data;
+ lockdep_assert_held(&cgroup_tree_mutex);
+ lockdep_assert_held(&cgroup_mutex);
- /* If we don't have a new root, we can't set up a new sb */
- if (!opts->new_root)
- return -EINVAL;
+ ret = idr_alloc(&root->cgroup_idr, root_cgrp, 0, 1, GFP_KERNEL);
+ if (ret < 0)
+ goto out;
+ root_cgrp->id = ret;
- BUG_ON(!opts->subsys_mask && !opts->none);
+ /*
+ * We're accessing css_set_count without locking css_set_rwsem here,
+ * but that's OK - it can only be increased by someone holding
+ * cgroup_lock, and that's us. The worst that can happen is that we
+ * have some link structures left over
+ */
+ ret = allocate_cgrp_cset_links(css_set_count, &tmp_links);
+ if (ret)
+ goto out;
- ret = set_anon_super(sb, NULL);
+ ret = cgroup_init_root_id(root);
if (ret)
- return ret;
+ goto out;
- sb->s_fs_info = opts->new_root;
- opts->new_root->sb = sb;
+ root->kf_root = kernfs_create_root(&cgroup_kf_syscall_ops,
+ KERNFS_ROOT_CREATE_DEACTIVATED,
+ root_cgrp);
+ if (IS_ERR(root->kf_root)) {
+ ret = PTR_ERR(root->kf_root);
+ goto exit_root_id;
+ }
+ root_cgrp->kn = root->kf_root->kn;
- sb->s_blocksize = PAGE_CACHE_SIZE;
- sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
- sb->s_magic = CGROUP_SUPER_MAGIC;
- sb->s_op = &cgroup_ops;
+ ret = cgroup_addrm_files(root_cgrp, cgroup_base_files, true);
+ if (ret)
+ goto destroy_root;
- return 0;
-}
+ ret = rebind_subsystems(root, ss_mask);
+ if (ret)
+ goto destroy_root;
-static int cgroup_get_rootdir(struct super_block *sb)
-{
- static const struct dentry_operations cgroup_dops = {
- .d_iput = cgroup_diput,
- .d_delete = always_delete_dentry,
- };
+ /*
+ * There must be no failure case after here, since rebinding takes
+ * care of subsystems' refcounts, which are explicitly dropped in
+ * the failure exit path.
+ */
+ list_add(&root->root_list, &cgroup_roots);
+ cgroup_root_count++;
- struct inode *inode =
- cgroup_new_inode(S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR, sb);
+ /*
+ * Link the root cgroup in this hierarchy into all the css_set
+ * objects.
+ */
+ down_write(&css_set_rwsem);
+ hash_for_each(css_set_table, i, cset, hlist)
+ link_css_set(&tmp_links, cset, root_cgrp);
+ up_write(&css_set_rwsem);
- if (!inode)
- return -ENOMEM;
+ BUG_ON(!list_empty(&root_cgrp->children));
+ BUG_ON(atomic_read(&root->nr_cgrps) != 1);
- inode->i_fop = &simple_dir_operations;
- inode->i_op = &cgroup_dir_inode_operations;
- /* directories start off with i_nlink == 2 (for "." entry) */
- inc_nlink(inode);
- sb->s_root = d_make_root(inode);
- if (!sb->s_root)
- return -ENOMEM;
- /* for everything else we want ->d_op set */
- sb->s_d_op = &cgroup_dops;
- return 0;
+ kernfs_activate(root_cgrp->kn);
+ ret = 0;
+ goto out;
+
+destroy_root:
+ kernfs_destroy_root(root->kf_root);
+ root->kf_root = NULL;
+exit_root_id:
+ cgroup_exit_root_id(root);
+out:
+ free_cgrp_cset_links(&tmp_links);
+ return ret;
}
static struct dentry *cgroup_mount(struct file_system_type *fs_type,
int flags, const char *unused_dev_name,
void *data)
{
+ struct cgroup_root *root;
struct cgroup_sb_opts opts;
- struct cgroupfs_root *root;
- int ret = 0;
- struct super_block *sb;
- struct cgroupfs_root *new_root;
- struct list_head tmp_links;
- struct inode *inode;
- const struct cred *cred;
+ struct dentry *dentry;
+ int ret;
- /* First find the desired set of subsystems */
+ /*
+ * The first time anyone tries to mount a cgroup, enable the list
+ * linking each css_set to its tasks and fix up all existing tasks.
+ */
+ if (!use_task_css_set_links)
+ cgroup_enable_task_cg_lists();
+retry:
+ mutex_lock(&cgroup_tree_mutex);
mutex_lock(&cgroup_mutex);
+
+ /* First find the desired set of subsystems */
ret = parse_cgroupfs_options(data, &opts);
- mutex_unlock(&cgroup_mutex);
if (ret)
- goto out_err;
-
- /*
- * Allocate a new cgroup root. We may not need it if we're
- * reusing an existing hierarchy.
- */
- new_root = cgroup_root_from_opts(&opts);
- if (IS_ERR(new_root)) {
- ret = PTR_ERR(new_root);
- goto out_err;
- }
- opts.new_root = new_root;
+ goto out_unlock;
- /* Locate an existing or new sb for this hierarchy */
- sb = sget(fs_type, cgroup_test_super, cgroup_set_super, 0, &opts);
- if (IS_ERR(sb)) {
- ret = PTR_ERR(sb);
- cgroup_free_root(opts.new_root);
- goto out_err;
+ /* look for a matching existing root */
+ if (!opts.subsys_mask && !opts.none && !opts.name) {
+ cgrp_dfl_root_visible = true;
+ root = &cgrp_dfl_root;
+ cgroup_get(&root->cgrp);
+ ret = 0;
+ goto out_unlock;
}
- root = sb->s_fs_info;
- BUG_ON(!root);
- if (root == opts.new_root) {
- /* We used the new root structure, so this is a new hierarchy */
- struct cgroup *root_cgrp = &root->top_cgroup;
- struct cgroupfs_root *existing_root;
- int i;
- struct css_set *cset;
-
- BUG_ON(sb->s_root != NULL);
-
- ret = cgroup_get_rootdir(sb);
- if (ret)
- goto drop_new_super;
- inode = sb->s_root->d_inode;
-
- mutex_lock(&inode->i_mutex);
- mutex_lock(&cgroup_mutex);
- mutex_lock(&cgroup_root_mutex);
-
- ret = idr_alloc(&root->cgroup_idr, root_cgrp, 0, 1, GFP_KERNEL);
- if (ret < 0)
- goto unlock_drop;
- root_cgrp->id = ret;
-
- /* Check for name clashes with existing mounts */
- ret = -EBUSY;
- if (strlen(root->name))
- for_each_active_root(existing_root)
- if (!strcmp(existing_root->name, root->name))
- goto unlock_drop;
-
- /*
- * We're accessing css_set_count without locking
- * css_set_lock here, but that's OK - it can only be
- * increased by someone holding cgroup_lock, and
- * that's us. The worst that can happen is that we
- * have some link structures left over
- */
- ret = allocate_cgrp_cset_links(css_set_count, &tmp_links);
- if (ret)
- goto unlock_drop;
+ for_each_root(root) {
+ bool name_match = false;
- /* ID 0 is reserved for dummy root, 1 for unified hierarchy */
- ret = cgroup_init_root_id(root, 2, 0);
- if (ret)
- goto unlock_drop;
-
- sb->s_root->d_fsdata = root_cgrp;
- root_cgrp->dentry = sb->s_root;
-
- /*
- * We're inside get_sb() and will call lookup_one_len() to
- * create the root files, which doesn't work if SELinux is
- * in use. The following cred dancing somehow works around
- * it. See 2ce9738ba ("cgroupfs: use init_cred when
- * populating new cgroupfs mount") for more details.
- */
- cred = override_creds(&init_cred);
-
- ret = cgroup_addrm_files(root_cgrp, cgroup_base_files, true);
- if (ret)
- goto rm_base_files;
-
- ret = rebind_subsystems(root, root->subsys_mask, 0);
- if (ret)
- goto rm_base_files;
-
- revert_creds(cred);
+ if (root == &cgrp_dfl_root)
+ continue;
/*
- * There must be no failure case after here, since rebinding
- * takes care of subsystems' refcounts, which are explicitly
- * dropped in the failure exit path.
+ * If we asked for a name then it must match. Also, if
+ * name matches but sybsys_mask doesn't, we should fail.
+ * Remember whether name matched.
*/
+ if (opts.name) {
+ if (strcmp(opts.name, root->name))
+ continue;
+ name_match = true;
+ }
- list_add(&root->root_list, &cgroup_roots);
- cgroup_root_count++;
-
- /* Link the top cgroup in this hierarchy into all
- * the css_set objects */
- write_lock(&css_set_lock);
- hash_for_each(css_set_table, i, cset, hlist)
- link_css_set(&tmp_links, cset, root_cgrp);
- write_unlock(&css_set_lock);
-
- free_cgrp_cset_links(&tmp_links);
-
- BUG_ON(!list_empty(&root_cgrp->children));
- BUG_ON(root->number_of_cgroups != 1);
-
- mutex_unlock(&cgroup_root_mutex);
- mutex_unlock(&cgroup_mutex);
- mutex_unlock(&inode->i_mutex);
- } else {
/*
- * We re-used an existing hierarchy - the new root (if
- * any) is not needed
+ * If we asked for subsystems (or explicitly for no
+ * subsystems) then they must match.
*/
- cgroup_free_root(opts.new_root);
+ if ((opts.subsys_mask || opts.none) &&
+ (opts.subsys_mask != root->cgrp.subsys_mask)) {
+ if (!name_match)
+ continue;
+ ret = -EBUSY;
+ goto out_unlock;
+ }
if ((root->flags ^ opts.flags) & CGRP_ROOT_OPTION_MASK) {
if ((root->flags | opts.flags) & CGRP_ROOT_SANE_BEHAVIOR) {
pr_err("cgroup: sane_behavior: new mount options should match the existing superblock\n");
ret = -EINVAL;
- goto drop_new_super;
+ goto out_unlock;
} else {
pr_warning("cgroup: new mount options do not match the existing superblock, will be ignored\n");
}
}
- }
-
- kfree(opts.release_agent);
- kfree(opts.name);
- return dget(sb->s_root);
-
- rm_base_files:
- free_cgrp_cset_links(&tmp_links);
- cgroup_addrm_files(&root->top_cgroup, cgroup_base_files, false);
- revert_creds(cred);
- unlock_drop:
- cgroup_exit_root_id(root);
- mutex_unlock(&cgroup_root_mutex);
- mutex_unlock(&cgroup_mutex);
- mutex_unlock(&inode->i_mutex);
- drop_new_super:
- deactivate_locked_super(sb);
- out_err:
- kfree(opts.release_agent);
- kfree(opts.name);
- return ERR_PTR(ret);
-}
-
-static void cgroup_kill_sb(struct super_block *sb)
-{
- struct cgroupfs_root *root = sb->s_fs_info;
- struct cgroup *cgrp = &root->top_cgroup;
- struct cgrp_cset_link *link, *tmp_link;
- int ret;
-
- BUG_ON(!root);
-
- BUG_ON(root->number_of_cgroups != 1);
- BUG_ON(!list_empty(&cgrp->children));
- mutex_lock(&cgrp->dentry->d_inode->i_mutex);
- mutex_lock(&cgroup_mutex);
- mutex_lock(&cgroup_root_mutex);
+ /*
+ * A root's lifetime is governed by its root cgroup. Zero
+ * ref indicate that the root is being destroyed. Wait for
+ * destruction to complete so that the subsystems are free.
+ * We can use wait_queue for the wait but this path is
+ * super cold. Let's just sleep for a bit and retry.
+ */
+ if (!atomic_inc_not_zero(&root->cgrp.refcnt)) {
+ mutex_unlock(&cgroup_mutex);
+ mutex_unlock(&cgroup_tree_mutex);
+ kfree(opts.release_agent);
+ kfree(opts.name);
+ msleep(10);
+ goto retry;
+ }
- /* Rebind all subsystems back to the default hierarchy */
- if (root->flags & CGRP_ROOT_SUBSYS_BOUND) {
- ret = rebind_subsystems(root, 0, root->subsys_mask);
- /* Shouldn't be able to fail ... */
- BUG_ON(ret);
+ ret = 0;
+ goto out_unlock;
}
/*
- * Release all the links from cset_links to this hierarchy's
- * root cgroup
+ * No such thing, create a new one. name= matching without subsys
+ * specification is allowed for already existing hierarchies but we
+ * can't create new one without subsys specification.
*/
- write_lock(&css_set_lock);
-
- list_for_each_entry_safe(link, tmp_link, &cgrp->cset_links, cset_link) {
- list_del(&link->cset_link);
- list_del(&link->cgrp_link);
- kfree(link);
+ if (!opts.subsys_mask && !opts.none) {
+ ret = -EINVAL;
+ goto out_unlock;
}
- write_unlock(&css_set_lock);
- if (!list_empty(&root->root_list)) {
- list_del(&root->root_list);
- cgroup_root_count--;
+ root = kzalloc(sizeof(*root), GFP_KERNEL);
+ if (!root) {
+ ret = -ENOMEM;
+ goto out_unlock;
}
- cgroup_exit_root_id(root);
+ init_cgroup_root(root, &opts);
- mutex_unlock(&cgroup_root_mutex);
+ ret = cgroup_setup_root(root, opts.subsys_mask);
+ if (ret)
+ cgroup_free_root(root);
+
+out_unlock:
mutex_unlock(&cgroup_mutex);
- mutex_unlock(&cgrp->dentry->d_inode->i_mutex);
+ mutex_unlock(&cgroup_tree_mutex);
- simple_xattrs_free(&cgrp->xattrs);
+ kfree(opts.release_agent);
+ kfree(opts.name);
- kill_litter_super(sb);
- cgroup_free_root(root);
+ if (ret)
+ return ERR_PTR(ret);
+
+ dentry = kernfs_mount(fs_type, flags, root->kf_root, NULL);
+ if (IS_ERR(dentry))
+ cgroup_put(&root->cgrp);
+ return dentry;
+}
+
+static void cgroup_kill_sb(struct super_block *sb)
+{
+ struct kernfs_root *kf_root = kernfs_root_from_sb(sb);
+ struct cgroup_root *root = cgroup_root_from_kf(kf_root);
+
+ cgroup_put(&root->cgrp);
+ kernfs_kill_sb(sb);
}
static struct file_system_type cgroup_fs_type = {
@@ -1743,57 +1627,6 @@ static struct file_system_type cgroup_fs_type = {
static struct kobject *cgroup_kobj;
/**
- * cgroup_path - generate the path of a cgroup
- * @cgrp: the cgroup in question
- * @buf: the buffer to write the path into
- * @buflen: the length of the buffer
- *
- * Writes path of cgroup into buf. Returns 0 on success, -errno on error.
- *
- * We can't generate cgroup path using dentry->d_name, as accessing
- * dentry->name must be protected by irq-unsafe dentry->d_lock or parent
- * inode's i_mutex, while on the other hand cgroup_path() can be called
- * with some irq-safe spinlocks held.
- */
-int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen)
-{
- int ret = -ENAMETOOLONG;
- char *start;
-
- if (!cgrp->parent) {
- if (strlcpy(buf, "/", buflen) >= buflen)
- return -ENAMETOOLONG;
- return 0;
- }
-
- start = buf + buflen - 1;
- *start = '\0';
-
- rcu_read_lock();
- do {
- const char *name = cgroup_name(cgrp);
- int len;
-
- len = strlen(name);
- if ((start -= len) < buf)
- goto out;
- memcpy(start, name, len);
-
- if (--start < buf)
- goto out;
- *start = '/';
-
- cgrp = cgrp->parent;
- } while (cgrp->parent);
- ret = 0;
- memmove(buf, start, buf + buflen - start);
-out:
- rcu_read_unlock();
- return ret;
-}
-EXPORT_SYMBOL_GPL(cgroup_path);
-
-/**
* task_cgroup_path - cgroup path of a task in the first cgroup hierarchy
* @task: target task
* @buf: the buffer to write the path into
@@ -1804,49 +1637,55 @@ EXPORT_SYMBOL_GPL(cgroup_path);
* function grabs cgroup_mutex and shouldn't be used inside locks used by
* cgroup controller callbacks.
*
- * Returns 0 on success, fails with -%ENAMETOOLONG if @buflen is too short.
+ * Return value is the same as kernfs_path().
*/
-int task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
+char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
{
- struct cgroupfs_root *root;
+ struct cgroup_root *root;
struct cgroup *cgrp;
- int hierarchy_id = 1, ret = 0;
-
- if (buflen < 2)
- return -ENAMETOOLONG;
+ int hierarchy_id = 1;
+ char *path = NULL;
mutex_lock(&cgroup_mutex);
+ down_read(&css_set_rwsem);
root = idr_get_next(&cgroup_hierarchy_idr, &hierarchy_id);
if (root) {
cgrp = task_cgroup_from_root(task, root);
- ret = cgroup_path(cgrp, buf, buflen);
+ path = cgroup_path(cgrp, buf, buflen);
} else {
/* if no hierarchy exists, everyone is in "/" */
- memcpy(buf, "/", 2);
+ if (strlcpy(buf, "/", buflen) < buflen)
+ path = buf;
}
+ up_read(&css_set_rwsem);
mutex_unlock(&cgroup_mutex);
- return ret;
+ return path;
}
EXPORT_SYMBOL_GPL(task_cgroup_path);
-/*
- * Control Group taskset
- */
-struct task_and_cgroup {
- struct task_struct *task;
- struct cgroup *cgrp;
- struct css_set *cset;
-};
-
+/* used to track tasks and other necessary states during migration */
struct cgroup_taskset {
- struct task_and_cgroup single;
- struct flex_array *tc_array;
- int tc_array_len;
- int idx;
- struct cgroup *cur_cgrp;
+ /* the src and dst cset list running through cset->mg_node */
+ struct list_head src_csets;
+ struct list_head dst_csets;
+
+ /*
+ * Fields for cgroup_taskset_*() iteration.
+ *
+ * Before migration is committed, the target migration tasks are on
+ * ->mg_tasks of the csets on ->src_csets. After, on ->mg_tasks of
+ * the csets on ->dst_csets. ->csets point to either ->src_csets
+ * or ->dst_csets depending on whether migration is committed.
+ *
+ * ->cur_csets and ->cur_task point to the current task position
+ * during iteration.
+ */
+ struct list_head *csets;
+ struct css_set *cur_cset;
+ struct task_struct *cur_task;
};
/**
@@ -1857,15 +1696,11 @@ struct cgroup_taskset {
*/
struct task_struct *cgroup_taskset_first(struct cgroup_taskset *tset)
{
- if (tset->tc_array) {
- tset->idx = 0;
- return cgroup_taskset_next(tset);
- } else {
- tset->cur_cgrp = tset->single.cgrp;
- return tset->single.task;
- }
+ tset->cur_cset = list_first_entry(tset->csets, struct css_set, mg_node);
+ tset->cur_task = NULL;
+
+ return cgroup_taskset_next(tset);
}
-EXPORT_SYMBOL_GPL(cgroup_taskset_first);
/**
* cgroup_taskset_next - iterate to the next task in taskset
@@ -1876,48 +1711,36 @@ EXPORT_SYMBOL_GPL(cgroup_taskset_first);
*/
struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset)
{
- struct task_and_cgroup *tc;
+ struct css_set *cset = tset->cur_cset;
+ struct task_struct *task = tset->cur_task;
- if (!tset->tc_array || tset->idx >= tset->tc_array_len)
- return NULL;
+ while (&cset->mg_node != tset->csets) {
+ if (!task)
+ task = list_first_entry(&cset->mg_tasks,
+ struct task_struct, cg_list);
+ else
+ task = list_next_entry(task, cg_list);
- tc = flex_array_get(tset->tc_array, tset->idx++);
- tset->cur_cgrp = tc->cgrp;
- return tc->task;
-}
-EXPORT_SYMBOL_GPL(cgroup_taskset_next);
+ if (&task->cg_list != &cset->mg_tasks) {
+ tset->cur_cset = cset;
+ tset->cur_task = task;
+ return task;
+ }
-/**
- * cgroup_taskset_cur_css - return the matching css for the current task
- * @tset: taskset of interest
- * @subsys_id: the ID of the target subsystem
- *
- * Return the css for the current (last returned) task of @tset for
- * subsystem specified by @subsys_id. This function must be preceded by
- * either cgroup_taskset_first() or cgroup_taskset_next().
- */
-struct cgroup_subsys_state *cgroup_taskset_cur_css(struct cgroup_taskset *tset,
- int subsys_id)
-{
- return cgroup_css(tset->cur_cgrp, cgroup_subsys[subsys_id]);
-}
-EXPORT_SYMBOL_GPL(cgroup_taskset_cur_css);
+ cset = list_next_entry(cset, mg_node);
+ task = NULL;
+ }
-/**
- * cgroup_taskset_size - return the number of tasks in taskset
- * @tset: taskset of interest
- */
-int cgroup_taskset_size(struct cgroup_taskset *tset)
-{
- return tset->tc_array ? tset->tc_array_len : 1;
+ return NULL;
}
-EXPORT_SYMBOL_GPL(cgroup_taskset_size);
-
-/*
+/**
* cgroup_task_migrate - move a task from one cgroup to another.
+ * @old_cgrp; the cgroup @tsk is being migrated from
+ * @tsk: the task being migrated
+ * @new_cset: the new css_set @tsk is being attached to
*
- * Must be called with cgroup_mutex and threadgroup locked.
+ * Must be called with cgroup_mutex, threadgroup and css_set_rwsem locked.
*/
static void cgroup_task_migrate(struct cgroup *old_cgrp,
struct task_struct *tsk,
@@ -1925,6 +1748,9 @@ static void cgroup_task_migrate(struct cgroup *old_cgrp,
{
struct css_set *old_cset;
+ lockdep_assert_held(&cgroup_mutex);
+ lockdep_assert_held(&css_set_rwsem);
+
/*
* We are synchronized through threadgroup_lock() against PF_EXITING
* setting such that we can't race against cgroup_exit() changing the
@@ -1933,15 +1759,16 @@ static void cgroup_task_migrate(struct cgroup *old_cgrp,
WARN_ON_ONCE(tsk->flags & PF_EXITING);
old_cset = task_css_set(tsk);
- task_lock(tsk);
+ get_css_set(new_cset);
rcu_assign_pointer(tsk->cgroups, new_cset);
- task_unlock(tsk);
- /* Update the css_set linked lists if we're using them */
- write_lock(&css_set_lock);
- if (!list_empty(&tsk->cg_list))
- list_move(&tsk->cg_list, &new_cset->tasks);
- write_unlock(&css_set_lock);
+ /*
+ * Use move_tail so that cgroup_taskset_first() still returns the
+ * leader after migration. This works because cgroup_migrate()
+ * ensures that the dst_cset of the leader is the first on the
+ * tset's dst_csets list.
+ */
+ list_move_tail(&tsk->cg_list, &new_cset->mg_tasks);
/*
* We just gained a reference on old_cset by taking it from the
@@ -1949,100 +1776,199 @@ static void cgroup_task_migrate(struct cgroup *old_cgrp,
* we're safe to drop it here; it will be freed under RCU.
*/
set_bit(CGRP_RELEASABLE, &old_cgrp->flags);
- put_css_set(old_cset);
+ put_css_set_locked(old_cset, false);
}
/**
- * cgroup_attach_task - attach a task or a whole threadgroup to a cgroup
- * @cgrp: the cgroup to attach to
- * @tsk: the task or the leader of the threadgroup to be attached
- * @threadgroup: attach the whole threadgroup?
+ * cgroup_migrate_finish - cleanup after attach
+ * @preloaded_csets: list of preloaded css_sets
*
- * Call holding cgroup_mutex and the group_rwsem of the leader. Will take
- * task_lock of @tsk or each thread in the threadgroup individually in turn.
+ * Undo cgroup_migrate_add_src() and cgroup_migrate_prepare_dst(). See
+ * those functions for details.
*/
-static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk,
- bool threadgroup)
+static void cgroup_migrate_finish(struct list_head *preloaded_csets)
{
- int retval, i, group_size;
- struct cgroupfs_root *root = cgrp->root;
- struct cgroup_subsys_state *css, *failed_css = NULL;
- /* threadgroup list cursor and array */
- struct task_struct *leader = tsk;
- struct task_and_cgroup *tc;
- struct flex_array *group;
- struct cgroup_taskset tset = { };
+ struct css_set *cset, *tmp_cset;
- /*
- * step 0: in order to do expensive, possibly blocking operations for
- * every thread, we cannot iterate the thread group list, since it needs
- * rcu or tasklist locked. instead, build an array of all threads in the
- * group - group_rwsem prevents new threads from appearing, and if
- * threads exit, this will just be an over-estimate.
- */
- if (threadgroup)
- group_size = get_nr_threads(tsk);
- else
- group_size = 1;
- /* flex_array supports very large thread-groups better than kmalloc. */
- group = flex_array_alloc(sizeof(*tc), group_size, GFP_KERNEL);
- if (!group)
- return -ENOMEM;
- /* pre-allocate to guarantee space while iterating in rcu read-side. */
- retval = flex_array_prealloc(group, 0, group_size, GFP_KERNEL);
- if (retval)
- goto out_free_group_list;
+ lockdep_assert_held(&cgroup_mutex);
+
+ down_write(&css_set_rwsem);
+ list_for_each_entry_safe(cset, tmp_cset, preloaded_csets, mg_preload_node) {
+ cset->mg_src_cgrp = NULL;
+ cset->mg_dst_cset = NULL;
+ list_del_init(&cset->mg_preload_node);
+ put_css_set_locked(cset, false);
+ }
+ up_write(&css_set_rwsem);
+}
+
+/**
+ * cgroup_migrate_add_src - add a migration source css_set
+ * @src_cset: the source css_set to add
+ * @dst_cgrp: the destination cgroup
+ * @preloaded_csets: list of preloaded css_sets
+ *
+ * Tasks belonging to @src_cset are about to be migrated to @dst_cgrp. Pin
+ * @src_cset and add it to @preloaded_csets, which should later be cleaned
+ * up by cgroup_migrate_finish().
+ *
+ * This function may be called without holding threadgroup_lock even if the
+ * target is a process. Threads may be created and destroyed but as long
+ * as cgroup_mutex is not dropped, no new css_set can be put into play and
+ * the preloaded css_sets are guaranteed to cover all migrations.
+ */
+static void cgroup_migrate_add_src(struct css_set *src_cset,
+ struct cgroup *dst_cgrp,
+ struct list_head *preloaded_csets)
+{
+ struct cgroup *src_cgrp;
+
+ lockdep_assert_held(&cgroup_mutex);
+ lockdep_assert_held(&css_set_rwsem);
+
+ src_cgrp = cset_cgroup_from_root(src_cset, dst_cgrp->root);
+
+ /* nothing to do if this cset already belongs to the cgroup */
+ if (src_cgrp == dst_cgrp)
+ return;
+
+ if (!list_empty(&src_cset->mg_preload_node))
+ return;
+
+ WARN_ON(src_cset->mg_src_cgrp);
+ WARN_ON(!list_empty(&src_cset->mg_tasks));
+ WARN_ON(!list_empty(&src_cset->mg_node));
+
+ src_cset->mg_src_cgrp = src_cgrp;
+ get_css_set(src_cset);
+ list_add(&src_cset->mg_preload_node, preloaded_csets);
+}
+
+/**
+ * cgroup_migrate_prepare_dst - prepare destination css_sets for migration
+ * @dst_cgrp: the destination cgroup
+ * @preloaded_csets: list of preloaded source css_sets
+ *
+ * Tasks are about to be moved to @dst_cgrp and all the source css_sets
+ * have been preloaded to @preloaded_csets. This function looks up and
+ * pins all destination css_sets, links each to its source, and put them on
+ * @preloaded_csets.
+ *
+ * This function must be called after cgroup_migrate_add_src() has been
+ * called on each migration source css_set. After migration is performed
+ * using cgroup_migrate(), cgroup_migrate_finish() must be called on
+ * @preloaded_csets.
+ */
+static int cgroup_migrate_prepare_dst(struct cgroup *dst_cgrp,
+ struct list_head *preloaded_csets)
+{
+ LIST_HEAD(csets);
+ struct css_set *src_cset;
+
+ lockdep_assert_held(&cgroup_mutex);
+
+ /* look up the dst cset for each src cset and link it to src */
+ list_for_each_entry(src_cset, preloaded_csets, mg_preload_node) {
+ struct css_set *dst_cset;
+
+ dst_cset = find_css_set(src_cset, dst_cgrp);
+ if (!dst_cset)
+ goto err;
+
+ WARN_ON_ONCE(src_cset->mg_dst_cset || dst_cset->mg_dst_cset);
+ src_cset->mg_dst_cset = dst_cset;
+
+ if (list_empty(&dst_cset->mg_preload_node))
+ list_add(&dst_cset->mg_preload_node, &csets);
+ else
+ put_css_set(dst_cset, false);
+ }
+
+ list_splice(&csets, preloaded_csets);
+ return 0;
+err:
+ cgroup_migrate_finish(&csets);
+ return -ENOMEM;
+}
+
+/**
+ * cgroup_migrate - migrate a process or task to a cgroup
+ * @cgrp: the destination cgroup
+ * @leader: the leader of the process or the task to migrate
+ * @threadgroup: whether @leader points to the whole process or a single task
+ *
+ * Migrate a process or task denoted by @leader to @cgrp. If migrating a
+ * process, the caller must be holding threadgroup_lock of @leader. The
+ * caller is also responsible for invoking cgroup_migrate_add_src() and
+ * cgroup_migrate_prepare_dst() on the targets before invoking this
+ * function and following up with cgroup_migrate_finish().
+ *
+ * As long as a controller's ->can_attach() doesn't fail, this function is
+ * guaranteed to succeed. This means that, excluding ->can_attach()
+ * failure, when migrating multiple targets, the success or failure can be
+ * decided for all targets by invoking group_migrate_prepare_dst() before
+ * actually starting migrating.
+ */
+static int cgroup_migrate(struct cgroup *cgrp, struct task_struct *leader,
+ bool threadgroup)
+{
+ struct cgroup_taskset tset = {
+ .src_csets = LIST_HEAD_INIT(tset.src_csets),
+ .dst_csets = LIST_HEAD_INIT(tset.dst_csets),
+ .csets = &tset.src_csets,
+ };
+ struct cgroup_subsys_state *css, *failed_css = NULL;
+ struct css_set *cset, *tmp_cset;
+ struct task_struct *task, *tmp_task;
+ int i, ret;
- i = 0;
/*
* Prevent freeing of tasks while we take a snapshot. Tasks that are
* already PF_EXITING could be freed from underneath us unless we
* take an rcu_read_lock.
*/
+ down_write(&css_set_rwsem);
rcu_read_lock();
+ task = leader;
do {
- struct task_and_cgroup ent;
+ /* @task either already exited or can't exit until the end */
+ if (task->flags & PF_EXITING)
+ goto next;
- /* @tsk either already exited or can't exit until the end */
- if (tsk->flags & PF_EXITING)
+ /* leave @task alone if post_fork() hasn't linked it yet */
+ if (list_empty(&task->cg_list))
goto next;
- /* as per above, nr_threads may decrease, but not increase. */
- BUG_ON(i >= group_size);
- ent.task = tsk;
- ent.cgrp = task_cgroup_from_root(tsk, root);
- /* nothing to do if this task is already in the cgroup */
- if (ent.cgrp == cgrp)
+ cset = task_css_set(task);
+ if (!cset->mg_src_cgrp)
goto next;
+
/*
- * saying GFP_ATOMIC has no effect here because we did prealloc
- * earlier, but it's good form to communicate our expectations.
+ * cgroup_taskset_first() must always return the leader.
+ * Take care to avoid disturbing the ordering.
*/
- retval = flex_array_put(group, i, &ent, GFP_ATOMIC);
- BUG_ON(retval != 0);
- i++;
+ list_move_tail(&task->cg_list, &cset->mg_tasks);
+ if (list_empty(&cset->mg_node))
+ list_add_tail(&cset->mg_node, &tset.src_csets);
+ if (list_empty(&cset->mg_dst_cset->mg_node))
+ list_move_tail(&cset->mg_dst_cset->mg_node,
+ &tset.dst_csets);
next:
if (!threadgroup)
break;
- } while_each_thread(leader, tsk);
+ } while_each_thread(leader, task);
rcu_read_unlock();
- /* remember the number of threads in the array for later. */
- group_size = i;
- tset.tc_array = group;
- tset.tc_array_len = group_size;
+ up_write(&css_set_rwsem);
/* methods shouldn't be called if no task is actually migrating */
- retval = 0;
- if (!group_size)
- goto out_free_group_list;
+ if (list_empty(&tset.src_csets))
+ return 0;
- /*
- * step 1: check that we can legitimately attach to the cgroup.
- */
+ /* check that we can legitimately attach to the cgroup */
for_each_css(css, i, cgrp) {
if (css->ss->can_attach) {
- retval = css->ss->can_attach(css, &tset);
- if (retval) {
+ ret = css->ss->can_attach(css, &tset);
+ if (ret) {
failed_css = css;
goto out_cancel_attach;
}
@@ -2050,70 +1976,91 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk,
}
/*
- * step 2: make sure css_sets exist for all threads to be migrated.
- * we use find_css_set, which allocates a new one if necessary.
+ * Now that we're guaranteed success, proceed to move all tasks to
+ * the new cgroup. There are no failure cases after here, so this
+ * is the commit point.
*/
- for (i = 0; i < group_size; i++) {
- struct css_set *old_cset;
-
- tc = flex_array_get(group, i);
- old_cset = task_css_set(tc->task);
- tc->cset = find_css_set(old_cset, cgrp);
- if (!tc->cset) {
- retval = -ENOMEM;
- goto out_put_css_set_refs;
- }
+ down_write(&css_set_rwsem);
+ list_for_each_entry(cset, &tset.src_csets, mg_node) {
+ list_for_each_entry_safe(task, tmp_task, &cset->mg_tasks, cg_list)
+ cgroup_task_migrate(cset->mg_src_cgrp, task,
+ cset->mg_dst_cset);
}
+ up_write(&css_set_rwsem);
/*
- * step 3: now that we're guaranteed success wrt the css_sets,
- * proceed to move all tasks to the new cgroup. There are no
- * failure cases after here, so this is the commit point.
+ * Migration is committed, all target tasks are now on dst_csets.
+ * Nothing is sensitive to fork() after this point. Notify
+ * controllers that migration is complete.
*/
- for (i = 0; i < group_size; i++) {
- tc = flex_array_get(group, i);
- cgroup_task_migrate(tc->cgrp, tc->task, tc->cset);
- }
- /* nothing is sensitive to fork() after this point. */
+ tset.csets = &tset.dst_csets;
- /*
- * step 4: do subsystem attach callbacks.
- */
for_each_css(css, i, cgrp)
if (css->ss->attach)
css->ss->attach(css, &tset);
- /*
- * step 5: success! and cleanup
- */
- retval = 0;
-out_put_css_set_refs:
- if (retval) {
- for (i = 0; i < group_size; i++) {
- tc = flex_array_get(group, i);
- if (!tc->cset)
- break;
- put_css_set(tc->cset);
- }
- }
+ ret = 0;
+ goto out_release_tset;
+
out_cancel_attach:
- if (retval) {
- for_each_css(css, i, cgrp) {
- if (css == failed_css)
- break;
- if (css->ss->cancel_attach)
- css->ss->cancel_attach(css, &tset);
- }
+ for_each_css(css, i, cgrp) {
+ if (css == failed_css)
+ break;
+ if (css->ss->cancel_attach)
+ css->ss->cancel_attach(css, &tset);
}
-out_free_group_list:
- flex_array_free(group);
- return retval;
+out_release_tset:
+ down_write(&css_set_rwsem);
+ list_splice_init(&tset.dst_csets, &tset.src_csets);
+ list_for_each_entry_safe(cset, tmp_cset, &tset.src_csets, mg_node) {
+ list_splice_tail_init(&cset->mg_tasks, &cset->tasks);
+ list_del_init(&cset->mg_node);
+ }
+ up_write(&css_set_rwsem);
+ return ret;
+}
+
+/**
+ * cgroup_attach_task - attach a task or a whole threadgroup to a cgroup
+ * @dst_cgrp: the cgroup to attach to
+ * @leader: the task or the leader of the threadgroup to be attached
+ * @threadgroup: attach the whole threadgroup?
+ *
+ * Call holding cgroup_mutex and threadgroup_lock of @leader.
+ */
+static int cgroup_attach_task(struct cgroup *dst_cgrp,
+ struct task_struct *leader, bool threadgroup)
+{
+ LIST_HEAD(preloaded_csets);
+ struct task_struct *task;
+ int ret;
+
+ /* look up all src csets */
+ down_read(&css_set_rwsem);
+ rcu_read_lock();
+ task = leader;
+ do {
+ cgroup_migrate_add_src(task_css_set(task), dst_cgrp,
+ &preloaded_csets);
+ if (!threadgroup)
+ break;
+ } while_each_thread(leader, task);
+ rcu_read_unlock();
+ up_read(&css_set_rwsem);
+
+ /* prepare dst csets and commit */
+ ret = cgroup_migrate_prepare_dst(dst_cgrp, &preloaded_csets);
+ if (!ret)
+ ret = cgroup_migrate(dst_cgrp, leader, threadgroup);
+
+ cgroup_migrate_finish(&preloaded_csets);
+ return ret;
}
/*
* Find the task_struct of the task to attach by vpid and pass it along to the
* function to attach either it or all tasks in its threadgroup. Will lock
- * cgroup_mutex and threadgroup; may take task_lock of task.
+ * cgroup_mutex and threadgroup.
*/
static int attach_task_by_pid(struct cgroup *cgrp, u64 pid, bool threadgroup)
{
@@ -2198,12 +2145,19 @@ out_unlock_cgroup:
*/
int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk)
{
- struct cgroupfs_root *root;
+ struct cgroup_root *root;
int retval = 0;
mutex_lock(&cgroup_mutex);
- for_each_active_root(root) {
- struct cgroup *from_cgrp = task_cgroup_from_root(from, root);
+ for_each_root(root) {
+ struct cgroup *from_cgrp;
+
+ if (root == &cgrp_dfl_root)
+ continue;
+
+ down_read(&css_set_rwsem);
+ from_cgrp = task_cgroup_from_root(from, root);
+ up_read(&css_set_rwsem);
retval = cgroup_attach_task(from_cgrp, tsk, false);
if (retval)
@@ -2228,16 +2182,17 @@ static int cgroup_procs_write(struct cgroup_subsys_state *css,
}
static int cgroup_release_agent_write(struct cgroup_subsys_state *css,
- struct cftype *cft, const char *buffer)
+ struct cftype *cft, char *buffer)
{
- BUILD_BUG_ON(sizeof(css->cgroup->root->release_agent_path) < PATH_MAX);
- if (strlen(buffer) >= PATH_MAX)
- return -EINVAL;
+ struct cgroup_root *root = css->cgroup->root;
+
+ BUILD_BUG_ON(sizeof(root->release_agent_path) < PATH_MAX);
if (!cgroup_lock_live_group(css->cgroup))
return -ENODEV;
- mutex_lock(&cgroup_root_mutex);
- strcpy(css->cgroup->root->release_agent_path, buffer);
- mutex_unlock(&cgroup_root_mutex);
+ spin_lock(&release_agent_path_lock);
+ strlcpy(root->release_agent_path, buffer,
+ sizeof(root->release_agent_path));
+ spin_unlock(&release_agent_path_lock);
mutex_unlock(&cgroup_mutex);
return 0;
}
@@ -2262,32 +2217,23 @@ static int cgroup_sane_behavior_show(struct seq_file *seq, void *v)
return 0;
}
-/* A buffer size big enough for numbers or short strings */
-#define CGROUP_LOCAL_BUFFER_SIZE 64
-
-static ssize_t cgroup_file_write(struct file *file, const char __user *userbuf,
- size_t nbytes, loff_t *ppos)
+static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf,
+ size_t nbytes, loff_t off)
{
- struct cfent *cfe = __d_cfe(file->f_dentry);
- struct cftype *cft = __d_cft(file->f_dentry);
- struct cgroup_subsys_state *css = cfe->css;
- size_t max_bytes = cft->max_write_len ?: CGROUP_LOCAL_BUFFER_SIZE - 1;
- char *buf;
+ struct cgroup *cgrp = of->kn->parent->priv;
+ struct cftype *cft = of->kn->priv;
+ struct cgroup_subsys_state *css;
int ret;
- if (nbytes >= max_bytes)
- return -E2BIG;
-
- buf = kmalloc(nbytes + 1, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- if (copy_from_user(buf, userbuf, nbytes)) {
- ret = -EFAULT;
- goto out_free;
- }
-
- buf[nbytes] = '\0';
+ /*
+ * kernfs guarantees that a file isn't deleted with operations in
+ * flight, which means that the matching css is and stays alive and
+ * doesn't need to be pinned. The RCU locking is not necessary
+ * either. It's just for the convenience of using cgroup_css().
+ */
+ rcu_read_lock();
+ css = cgroup_css(cgrp, cft->ss);
+ rcu_read_unlock();
if (cft->write_string) {
ret = cft->write_string(css, cft, strstrip(buf));
@@ -2306,53 +2252,23 @@ static ssize_t cgroup_file_write(struct file *file, const char __user *userbuf,
} else {
ret = -EINVAL;
}
-out_free:
- kfree(buf);
+
return ret ?: nbytes;
}
-/*
- * seqfile ops/methods for returning structured data. Currently just
- * supports string->u64 maps, but can be extended in future.
- */
-
static void *cgroup_seqfile_start(struct seq_file *seq, loff_t *ppos)
{
- struct cftype *cft = seq_cft(seq);
-
- if (cft->seq_start) {
- return cft->seq_start(seq, ppos);
- } else {
- /*
- * The same behavior and code as single_open(). Returns
- * !NULL if pos is at the beginning; otherwise, NULL.
- */
- return NULL + !*ppos;
- }
+ return seq_cft(seq)->seq_start(seq, ppos);
}
static void *cgroup_seqfile_next(struct seq_file *seq, void *v, loff_t *ppos)
{
- struct cftype *cft = seq_cft(seq);
-
- if (cft->seq_next) {
- return cft->seq_next(seq, v, ppos);
- } else {
- /*
- * The same behavior and code as single_open(), always
- * terminate after the initial read.
- */
- ++*ppos;
- return NULL;
- }
+ return seq_cft(seq)->seq_next(seq, v, ppos);
}
static void cgroup_seqfile_stop(struct seq_file *seq, void *v)
{
- struct cftype *cft = seq_cft(seq);
-
- if (cft->seq_stop)
- cft->seq_stop(seq, v);
+ seq_cft(seq)->seq_stop(seq, v);
}
static int cgroup_seqfile_show(struct seq_file *m, void *arg)
@@ -2372,96 +2288,35 @@ static int cgroup_seqfile_show(struct seq_file *m, void *arg)
return 0;
}
-static struct seq_operations cgroup_seq_operations = {
- .start = cgroup_seqfile_start,
- .next = cgroup_seqfile_next,
- .stop = cgroup_seqfile_stop,
- .show = cgroup_seqfile_show,
+static struct kernfs_ops cgroup_kf_single_ops = {
+ .atomic_write_len = PAGE_SIZE,
+ .write = cgroup_file_write,
+ .seq_show = cgroup_seqfile_show,
};
-static int cgroup_file_open(struct inode *inode, struct file *file)
-{
- struct cfent *cfe = __d_cfe(file->f_dentry);
- struct cftype *cft = __d_cft(file->f_dentry);
- struct cgroup *cgrp = __d_cgrp(cfe->dentry->d_parent);
- struct cgroup_subsys_state *css;
- struct cgroup_open_file *of;
- int err;
-
- err = generic_file_open(inode, file);
- if (err)
- return err;
-
- /*
- * If the file belongs to a subsystem, pin the css. Will be
- * unpinned either on open failure or release. This ensures that
- * @css stays alive for all file operations.
- */
- rcu_read_lock();
- css = cgroup_css(cgrp, cft->ss);
- if (cft->ss && !css_tryget(css))
- css = NULL;
- rcu_read_unlock();
-
- if (!css)
- return -ENODEV;
-
- /*
- * @cfe->css is used by read/write/close to determine the
- * associated css. @file->private_data would be a better place but
- * that's already used by seqfile. Multiple accessors may use it
- * simultaneously which is okay as the association never changes.
- */
- WARN_ON_ONCE(cfe->css && cfe->css != css);
- cfe->css = css;
-
- of = __seq_open_private(file, &cgroup_seq_operations,
- sizeof(struct cgroup_open_file));
- if (of) {
- of->cfe = cfe;
- return 0;
- }
-
- if (css->ss)
- css_put(css);
- return -ENOMEM;
-}
-
-static int cgroup_file_release(struct inode *inode, struct file *file)
-{
- struct cfent *cfe = __d_cfe(file->f_dentry);
- struct cgroup_subsys_state *css = cfe->css;
-
- if (css->ss)
- css_put(css);
- return seq_release_private(inode, file);
-}
+static struct kernfs_ops cgroup_kf_ops = {
+ .atomic_write_len = PAGE_SIZE,
+ .write = cgroup_file_write,
+ .seq_start = cgroup_seqfile_start,
+ .seq_next = cgroup_seqfile_next,
+ .seq_stop = cgroup_seqfile_stop,
+ .seq_show = cgroup_seqfile_show,
+};
/*
* cgroup_rename - Only allow simple rename of directories in place.
*/
-static int cgroup_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
+static int cgroup_rename(struct kernfs_node *kn, struct kernfs_node *new_parent,
+ const char *new_name_str)
{
+ struct cgroup *cgrp = kn->priv;
int ret;
- struct cgroup_name *name, *old_name;
- struct cgroup *cgrp;
-
- /*
- * It's convinient to use parent dir's i_mutex to protected
- * cgrp->name.
- */
- lockdep_assert_held(&old_dir->i_mutex);
- if (!S_ISDIR(old_dentry->d_inode->i_mode))
+ if (kernfs_type(kn) != KERNFS_DIR)
return -ENOTDIR;
- if (new_dentry->d_inode)
- return -EEXIST;
- if (old_dir != new_dir)
+ if (kn->parent != new_parent)
return -EIO;
- cgrp = __d_cgrp(old_dentry);
-
/*
* This isn't a proper migration and its usefulness is very
* limited. Disallow if sane_behavior.
@@ -2469,218 +2324,40 @@ static int cgroup_rename(struct inode *old_dir, struct dentry *old_dentry,
if (cgroup_sane_behavior(cgrp))
return -EPERM;
- name = cgroup_alloc_name(new_dentry);
- if (!name)
- return -ENOMEM;
-
- ret = simple_rename(old_dir, old_dentry, new_dir, new_dentry);
- if (ret) {
- kfree(name);
- return ret;
- }
-
- old_name = rcu_dereference_protected(cgrp->name, true);
- rcu_assign_pointer(cgrp->name, name);
-
- kfree_rcu(old_name, rcu_head);
- return 0;
-}
-
-static struct simple_xattrs *__d_xattrs(struct dentry *dentry)
-{
- if (S_ISDIR(dentry->d_inode->i_mode))
- return &__d_cgrp(dentry)->xattrs;
- else
- return &__d_cfe(dentry)->xattrs;
-}
-
-static inline int xattr_enabled(struct dentry *dentry)
-{
- struct cgroupfs_root *root = dentry->d_sb->s_fs_info;
- return root->flags & CGRP_ROOT_XATTR;
-}
-
-static bool is_valid_xattr(const char *name)
-{
- if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) ||
- !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN))
- return true;
- return false;
-}
-
-static int cgroup_setxattr(struct dentry *dentry, const char *name,
- const void *val, size_t size, int flags)
-{
- if (!xattr_enabled(dentry))
- return -EOPNOTSUPP;
- if (!is_valid_xattr(name))
- return -EINVAL;
- return simple_xattr_set(__d_xattrs(dentry), name, val, size, flags);
-}
-
-static int cgroup_removexattr(struct dentry *dentry, const char *name)
-{
- if (!xattr_enabled(dentry))
- return -EOPNOTSUPP;
- if (!is_valid_xattr(name))
- return -EINVAL;
- return simple_xattr_remove(__d_xattrs(dentry), name);
-}
-
-static ssize_t cgroup_getxattr(struct dentry *dentry, const char *name,
- void *buf, size_t size)
-{
- if (!xattr_enabled(dentry))
- return -EOPNOTSUPP;
- if (!is_valid_xattr(name))
- return -EINVAL;
- return simple_xattr_get(__d_xattrs(dentry), name, buf, size);
-}
-
-static ssize_t cgroup_listxattr(struct dentry *dentry, char *buf, size_t size)
-{
- if (!xattr_enabled(dentry))
- return -EOPNOTSUPP;
- return simple_xattr_list(__d_xattrs(dentry), buf, size);
-}
-
-static const struct file_operations cgroup_file_operations = {
- .read = seq_read,
- .write = cgroup_file_write,
- .llseek = generic_file_llseek,
- .open = cgroup_file_open,
- .release = cgroup_file_release,
-};
-
-static const struct inode_operations cgroup_file_inode_operations = {
- .setxattr = cgroup_setxattr,
- .getxattr = cgroup_getxattr,
- .listxattr = cgroup_listxattr,
- .removexattr = cgroup_removexattr,
-};
-
-static const struct inode_operations cgroup_dir_inode_operations = {
- .lookup = simple_lookup,
- .mkdir = cgroup_mkdir,
- .rmdir = cgroup_rmdir,
- .rename = cgroup_rename,
- .setxattr = cgroup_setxattr,
- .getxattr = cgroup_getxattr,
- .listxattr = cgroup_listxattr,
- .removexattr = cgroup_removexattr,
-};
-
-static int cgroup_create_file(struct dentry *dentry, umode_t mode,
- struct super_block *sb)
-{
- struct inode *inode;
-
- if (!dentry)
- return -ENOENT;
- if (dentry->d_inode)
- return -EEXIST;
-
- inode = cgroup_new_inode(mode, sb);
- if (!inode)
- return -ENOMEM;
-
- if (S_ISDIR(mode)) {
- inode->i_op = &cgroup_dir_inode_operations;
- inode->i_fop = &simple_dir_operations;
-
- /* start off with i_nlink == 2 (for "." entry) */
- inc_nlink(inode);
- inc_nlink(dentry->d_parent->d_inode);
-
- /*
- * Control reaches here with cgroup_mutex held.
- * @inode->i_mutex should nest outside cgroup_mutex but we
- * want to populate it immediately without releasing
- * cgroup_mutex. As @inode isn't visible to anyone else
- * yet, trylock will always succeed without affecting
- * lockdep checks.
- */
- WARN_ON_ONCE(!mutex_trylock(&inode->i_mutex));
- } else if (S_ISREG(mode)) {
- inode->i_size = 0;
- inode->i_fop = &cgroup_file_operations;
- inode->i_op = &cgroup_file_inode_operations;
- }
- d_instantiate(dentry, inode);
- dget(dentry); /* Extra count - pin the dentry in core */
- return 0;
-}
-
-/**
- * cgroup_file_mode - deduce file mode of a control file
- * @cft: the control file in question
- *
- * returns cft->mode if ->mode is not 0
- * returns S_IRUGO|S_IWUSR if it has both a read and a write handler
- * returns S_IRUGO if it has only a read handler
- * returns S_IWUSR if it has only a write hander
- */
-static umode_t cgroup_file_mode(const struct cftype *cft)
-{
- umode_t mode = 0;
+ /*
+ * We're gonna grab cgroup_tree_mutex which nests outside kernfs
+ * active_ref. kernfs_rename() doesn't require active_ref
+ * protection. Break them before grabbing cgroup_tree_mutex.
+ */
+ kernfs_break_active_protection(new_parent);
+ kernfs_break_active_protection(kn);
- if (cft->mode)
- return cft->mode;
+ mutex_lock(&cgroup_tree_mutex);
+ mutex_lock(&cgroup_mutex);
- if (cft->read_u64 || cft->read_s64 || cft->seq_show)
- mode |= S_IRUGO;
+ ret = kernfs_rename(kn, new_parent, new_name_str);
- if (cft->write_u64 || cft->write_s64 || cft->write_string ||
- cft->trigger)
- mode |= S_IWUSR;
+ mutex_unlock(&cgroup_mutex);
+ mutex_unlock(&cgroup_tree_mutex);
- return mode;
+ kernfs_unbreak_active_protection(kn);
+ kernfs_unbreak_active_protection(new_parent);
+ return ret;
}
static int cgroup_add_file(struct cgroup *cgrp, struct cftype *cft)
{
- struct dentry *dir = cgrp->dentry;
- struct cgroup *parent = __d_cgrp(dir);
- struct dentry *dentry;
- struct cfent *cfe;
- int error;
- umode_t mode;
- char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 };
-
- if (cft->ss && !(cft->flags & CFTYPE_NO_PREFIX) &&
- !(cgrp->root->flags & CGRP_ROOT_NOPREFIX)) {
- strcpy(name, cft->ss->name);
- strcat(name, ".");
- }
- strcat(name, cft->name);
-
- BUG_ON(!mutex_is_locked(&dir->d_inode->i_mutex));
-
- cfe = kzalloc(sizeof(*cfe), GFP_KERNEL);
- if (!cfe)
- return -ENOMEM;
+ char name[CGROUP_FILE_NAME_MAX];
+ struct kernfs_node *kn;
+ struct lock_class_key *key = NULL;
- dentry = lookup_one_len(name, dir, strlen(name));
- if (IS_ERR(dentry)) {
- error = PTR_ERR(dentry);
- goto out;
- }
-
- cfe->type = (void *)cft;
- cfe->dentry = dentry;
- dentry->d_fsdata = cfe;
- simple_xattrs_init(&cfe->xattrs);
-
- mode = cgroup_file_mode(cft);
- error = cgroup_create_file(dentry, mode | S_IFREG, cgrp->root->sb);
- if (!error) {
- list_add_tail(&cfe->node, &parent->files);
- cfe = NULL;
- }
- dput(dentry);
-out:
- kfree(cfe);
- return error;
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ key = &cft->lockdep_key;
+#endif
+ kn = __kernfs_create_file(cgrp->kn, cgroup_file_name(cgrp, cft, name),
+ cgroup_file_mode(cft), 0, cft->kf_ops, cft,
+ NULL, false, key);
+ return PTR_ERR_OR_ZERO(kn);
}
/**
@@ -2700,11 +2377,12 @@ static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[],
struct cftype *cft;
int ret;
- lockdep_assert_held(&cgrp->dentry->d_inode->i_mutex);
- lockdep_assert_held(&cgroup_mutex);
+ lockdep_assert_held(&cgroup_tree_mutex);
for (cft = cfts; cft->name[0] != '\0'; cft++) {
/* does cft->flags tell us to skip this file on @cgrp? */
+ if ((cft->flags & CFTYPE_ONLY_ON_DFL) && !cgroup_on_dfl(cgrp))
+ continue;
if ((cft->flags & CFTYPE_INSANE) && cgroup_sane_behavior(cgrp))
continue;
if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgrp->parent)
@@ -2726,44 +2404,19 @@ static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[],
return 0;
}
-static void cgroup_cfts_prepare(void)
- __acquires(&cgroup_mutex)
-{
- /*
- * Thanks to the entanglement with vfs inode locking, we can't walk
- * the existing cgroups under cgroup_mutex and create files.
- * Instead, we use css_for_each_descendant_pre() and drop RCU read
- * lock before calling cgroup_addrm_files().
- */
- mutex_lock(&cgroup_mutex);
-}
-
-static int cgroup_cfts_commit(struct cftype *cfts, bool is_add)
- __releases(&cgroup_mutex)
+static int cgroup_apply_cftypes(struct cftype *cfts, bool is_add)
{
LIST_HEAD(pending);
struct cgroup_subsys *ss = cfts[0].ss;
- struct cgroup *root = &ss->root->top_cgroup;
- struct super_block *sb = ss->root->sb;
- struct dentry *prev = NULL;
- struct inode *inode;
+ struct cgroup *root = &ss->root->cgrp;
struct cgroup_subsys_state *css;
- u64 update_before;
int ret = 0;
- /* %NULL @cfts indicates abort and don't bother if @ss isn't attached */
- if (!cfts || ss->root == &cgroup_dummy_root ||
- !atomic_inc_not_zero(&sb->s_active)) {
- mutex_unlock(&cgroup_mutex);
- return 0;
- }
+ lockdep_assert_held(&cgroup_tree_mutex);
- /*
- * All cgroups which are created after we drop cgroup_mutex will
- * have the updated set of files, so we only need to update the
- * cgroups created before the current @cgroup_serial_nr_next.
- */
- update_before = cgroup_serial_nr_next;
+ /* don't bother if @ss isn't attached */
+ if (ss->root == &cgrp_dfl_root)
+ return 0;
/* add/rm files for all cgroups created before */
css_for_each_descendant_pre(css, cgroup_css(root, ss)) {
@@ -2772,62 +2425,75 @@ static int cgroup_cfts_commit(struct cftype *cfts, bool is_add)
if (cgroup_is_dead(cgrp))
continue;
- inode = cgrp->dentry->d_inode;
- dget(cgrp->dentry);
- dput(prev);
- prev = cgrp->dentry;
-
- mutex_unlock(&cgroup_mutex);
- mutex_lock(&inode->i_mutex);
- mutex_lock(&cgroup_mutex);
- if (cgrp->serial_nr < update_before && !cgroup_is_dead(cgrp))
- ret = cgroup_addrm_files(cgrp, cfts, is_add);
- mutex_unlock(&inode->i_mutex);
+ ret = cgroup_addrm_files(cgrp, cfts, is_add);
if (ret)
break;
}
- mutex_unlock(&cgroup_mutex);
- dput(prev);
- deactivate_super(sb);
+
+ if (is_add && !ret)
+ kernfs_activate(root->kn);
return ret;
}
-/**
- * cgroup_add_cftypes - add an array of cftypes to a subsystem
- * @ss: target cgroup subsystem
- * @cfts: zero-length name terminated array of cftypes
- *
- * Register @cfts to @ss. Files described by @cfts are created for all
- * existing cgroups to which @ss is attached and all future cgroups will
- * have them too. This function can be called anytime whether @ss is
- * attached or not.
- *
- * Returns 0 on successful registration, -errno on failure. Note that this
- * function currently returns 0 as long as @cfts registration is successful
- * even if some file creation attempts on existing cgroups fail.
- */
-int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
+static void cgroup_exit_cftypes(struct cftype *cfts)
{
- struct cftype_set *set;
struct cftype *cft;
- int ret;
- set = kzalloc(sizeof(*set), GFP_KERNEL);
- if (!set)
- return -ENOMEM;
+ for (cft = cfts; cft->name[0] != '\0'; cft++) {
+ /* free copy for custom atomic_write_len, see init_cftypes() */
+ if (cft->max_write_len && cft->max_write_len != PAGE_SIZE)
+ kfree(cft->kf_ops);
+ cft->kf_ops = NULL;
+ cft->ss = NULL;
+ }
+}
- for (cft = cfts; cft->name[0] != '\0'; cft++)
+static int cgroup_init_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
+{
+ struct cftype *cft;
+
+ for (cft = cfts; cft->name[0] != '\0'; cft++) {
+ struct kernfs_ops *kf_ops;
+
+ WARN_ON(cft->ss || cft->kf_ops);
+
+ if (cft->seq_start)
+ kf_ops = &cgroup_kf_ops;
+ else
+ kf_ops = &cgroup_kf_single_ops;
+
+ /*
+ * Ugh... if @cft wants a custom max_write_len, we need to
+ * make a copy of kf_ops to set its atomic_write_len.
+ */
+ if (cft->max_write_len && cft->max_write_len != PAGE_SIZE) {
+ kf_ops = kmemdup(kf_ops, sizeof(*kf_ops), GFP_KERNEL);
+ if (!kf_ops) {
+ cgroup_exit_cftypes(cfts);
+ return -ENOMEM;
+ }
+ kf_ops->atomic_write_len = cft->max_write_len;
+ }
+
+ cft->kf_ops = kf_ops;
cft->ss = ss;
+ }
- cgroup_cfts_prepare();
- set->cfts = cfts;
- list_add_tail(&set->node, &ss->cftsets);
- ret = cgroup_cfts_commit(cfts, true);
- if (ret)
- cgroup_rm_cftypes(cfts);
- return ret;
+ return 0;
+}
+
+static int cgroup_rm_cftypes_locked(struct cftype *cfts)
+{
+ lockdep_assert_held(&cgroup_tree_mutex);
+
+ if (!cfts || !cfts[0].ss)
+ return -ENOENT;
+
+ list_del(&cfts->node);
+ cgroup_apply_cftypes(cfts, false);
+ cgroup_exit_cftypes(cfts);
+ return 0;
}
-EXPORT_SYMBOL_GPL(cgroup_add_cftypes);
/**
* cgroup_rm_cftypes - remove an array of cftypes from a subsystem
@@ -2842,24 +2508,48 @@ EXPORT_SYMBOL_GPL(cgroup_add_cftypes);
*/
int cgroup_rm_cftypes(struct cftype *cfts)
{
- struct cftype_set *set;
+ int ret;
- if (!cfts || !cfts[0].ss)
- return -ENOENT;
+ mutex_lock(&cgroup_tree_mutex);
+ ret = cgroup_rm_cftypes_locked(cfts);
+ mutex_unlock(&cgroup_tree_mutex);
+ return ret;
+}
- cgroup_cfts_prepare();
+/**
+ * cgroup_add_cftypes - add an array of cftypes to a subsystem
+ * @ss: target cgroup subsystem
+ * @cfts: zero-length name terminated array of cftypes
+ *
+ * Register @cfts to @ss. Files described by @cfts are created for all
+ * existing cgroups to which @ss is attached and all future cgroups will
+ * have them too. This function can be called anytime whether @ss is
+ * attached or not.
+ *
+ * Returns 0 on successful registration, -errno on failure. Note that this
+ * function currently returns 0 as long as @cfts registration is successful
+ * even if some file creation attempts on existing cgroups fail.
+ */
+int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
+{
+ int ret;
- list_for_each_entry(set, &cfts[0].ss->cftsets, node) {
- if (set->cfts == cfts) {
- list_del(&set->node);
- kfree(set);
- cgroup_cfts_commit(cfts, false);
- return 0;
- }
- }
+ if (!cfts || cfts[0].name[0] == '\0')
+ return 0;
+
+ ret = cgroup_init_cftypes(ss, cfts);
+ if (ret)
+ return ret;
+
+ mutex_lock(&cgroup_tree_mutex);
- cgroup_cfts_commit(NULL, false);
- return -ENOENT;
+ list_add_tail(&cfts->node, &ss->cfts);
+ ret = cgroup_apply_cftypes(cfts, true);
+ if (ret)
+ cgroup_rm_cftypes_locked(cfts);
+
+ mutex_unlock(&cgroup_tree_mutex);
+ return ret;
}
/**
@@ -2868,57 +2558,18 @@ int cgroup_rm_cftypes(struct cftype *cfts)
*
* Return the number of tasks in the cgroup.
*/
-int cgroup_task_count(const struct cgroup *cgrp)
+static int cgroup_task_count(const struct cgroup *cgrp)
{
int count = 0;
struct cgrp_cset_link *link;
- read_lock(&css_set_lock);
+ down_read(&css_set_rwsem);
list_for_each_entry(link, &cgrp->cset_links, cset_link)
count += atomic_read(&link->cset->refcount);
- read_unlock(&css_set_lock);
+ up_read(&css_set_rwsem);
return count;
}
-/*
- * To reduce the fork() overhead for systems that are not actually using
- * their cgroups capability, we don't maintain the lists running through
- * each css_set to its tasks until we see the list actually used - in other
- * words after the first call to css_task_iter_start().
- */
-static void cgroup_enable_task_cg_lists(void)
-{
- struct task_struct *p, *g;
- write_lock(&css_set_lock);
- use_task_css_set_links = 1;
- /*
- * We need tasklist_lock because RCU is not safe against
- * while_each_thread(). Besides, a forking task that has passed
- * cgroup_post_fork() without seeing use_task_css_set_links = 1
- * is not guaranteed to have its child immediately visible in the
- * tasklist if we walk through it with RCU.
- */
- read_lock(&tasklist_lock);
- do_each_thread(g, p) {
- task_lock(p);
- /*
- * We should check if the process is exiting, otherwise
- * it will race with cgroup_exit() in that the list
- * entry won't be deleted though the process has exited.
- * Do it while holding siglock so that we don't end up
- * racing against cgroup_exit().
- */
- spin_lock_irq(&p->sighand->siglock);
- if (!(p->flags & PF_EXITING) && list_empty(&p->cg_list))
- list_add(&p->cg_list, &task_css_set(p)->tasks);
- spin_unlock_irq(&p->sighand->siglock);
-
- task_unlock(p);
- } while_each_thread(g, p);
- read_unlock(&tasklist_lock);
- write_unlock(&css_set_lock);
-}
-
/**
* css_next_child - find the next child of a given css
* @pos_css: the current position (%NULL to initiate traversal)
@@ -2937,7 +2588,7 @@ css_next_child(struct cgroup_subsys_state *pos_css,
struct cgroup *cgrp = parent_css->cgroup;
struct cgroup *next;
- cgroup_assert_mutex_or_rcu_locked();
+ cgroup_assert_mutexes_or_rcu_locked();
/*
* @pos could already have been removed. Once a cgroup is removed,
@@ -2973,7 +2624,6 @@ css_next_child(struct cgroup_subsys_state *pos_css,
return cgroup_css(next, parent_css->ss);
}
-EXPORT_SYMBOL_GPL(css_next_child);
/**
* css_next_descendant_pre - find the next descendant for pre-order walk
@@ -2995,7 +2645,7 @@ css_next_descendant_pre(struct cgroup_subsys_state *pos,
{
struct cgroup_subsys_state *next;
- cgroup_assert_mutex_or_rcu_locked();
+ cgroup_assert_mutexes_or_rcu_locked();
/* if first iteration, visit @root */
if (!pos)
@@ -3016,7 +2666,6 @@ css_next_descendant_pre(struct cgroup_subsys_state *pos,
return NULL;
}
-EXPORT_SYMBOL_GPL(css_next_descendant_pre);
/**
* css_rightmost_descendant - return the rightmost descendant of a css
@@ -3036,7 +2685,7 @@ css_rightmost_descendant(struct cgroup_subsys_state *pos)
{
struct cgroup_subsys_state *last, *tmp;
- cgroup_assert_mutex_or_rcu_locked();
+ cgroup_assert_mutexes_or_rcu_locked();
do {
last = pos;
@@ -3048,7 +2697,6 @@ css_rightmost_descendant(struct cgroup_subsys_state *pos)
return last;
}
-EXPORT_SYMBOL_GPL(css_rightmost_descendant);
static struct cgroup_subsys_state *
css_leftmost_descendant(struct cgroup_subsys_state *pos)
@@ -3084,7 +2732,7 @@ css_next_descendant_post(struct cgroup_subsys_state *pos,
{
struct cgroup_subsys_state *next;
- cgroup_assert_mutex_or_rcu_locked();
+ cgroup_assert_mutexes_or_rcu_locked();
/* if first iteration, visit leftmost descendant which may be @root */
if (!pos)
@@ -3102,7 +2750,6 @@ css_next_descendant_post(struct cgroup_subsys_state *pos,
/* no sibling left, visit parent */
return css_parent(pos);
}
-EXPORT_SYMBOL_GPL(css_next_descendant_post);
/**
* css_advance_task_iter - advance a task itererator to the next css_set
@@ -3125,9 +2772,14 @@ static void css_advance_task_iter(struct css_task_iter *it)
}
link = list_entry(l, struct cgrp_cset_link, cset_link);
cset = link->cset;
- } while (list_empty(&cset->tasks));
+ } while (list_empty(&cset->tasks) && list_empty(&cset->mg_tasks));
+
it->cset_link = l;
- it->task = cset->tasks.next;
+
+ if (!list_empty(&cset->tasks))
+ it->task = cset->tasks.next;
+ else
+ it->task = cset->mg_tasks.next;
}
/**
@@ -3146,17 +2798,12 @@ static void css_advance_task_iter(struct css_task_iter *it)
*/
void css_task_iter_start(struct cgroup_subsys_state *css,
struct css_task_iter *it)
- __acquires(css_set_lock)
+ __acquires(css_set_rwsem)
{
- /*
- * The first time anyone tries to iterate across a css, we need to
- * enable the list linking each css_set to its tasks, and fix up
- * all existing tasks.
- */
- if (!use_task_css_set_links)
- cgroup_enable_task_cg_lists();
+ /* no one should try to iterate before mounting cgroups */
+ WARN_ON_ONCE(!use_task_css_set_links);
- read_lock(&css_set_lock);
+ down_read(&css_set_rwsem);
it->origin_css = css;
it->cset_link = &css->cgroup->cset_links;
@@ -3176,24 +2823,29 @@ struct task_struct *css_task_iter_next(struct css_task_iter *it)
{
struct task_struct *res;
struct list_head *l = it->task;
- struct cgrp_cset_link *link;
+ struct cgrp_cset_link *link = list_entry(it->cset_link,
+ struct cgrp_cset_link, cset_link);
/* If the iterator cg is NULL, we have no tasks */
if (!it->cset_link)
return NULL;
res = list_entry(l, struct task_struct, cg_list);
- /* Advance iterator to find next entry */
+
+ /*
+ * Advance iterator to find next entry. cset->tasks is consumed
+ * first and then ->mg_tasks. After ->mg_tasks, we move onto the
+ * next cset.
+ */
l = l->next;
- link = list_entry(it->cset_link, struct cgrp_cset_link, cset_link);
- if (l == &link->cset->tasks) {
- /*
- * We reached the end of this task list - move on to the
- * next cgrp_cset_link.
- */
+
+ if (l == &link->cset->tasks)
+ l = link->cset->mg_tasks.next;
+
+ if (l == &link->cset->mg_tasks)
css_advance_task_iter(it);
- } else {
+ else
it->task = l;
- }
+
return res;
}
@@ -3204,191 +2856,62 @@ struct task_struct *css_task_iter_next(struct css_task_iter *it)
* Finish task iteration started by css_task_iter_start().
*/
void css_task_iter_end(struct css_task_iter *it)
- __releases(css_set_lock)
-{
- read_unlock(&css_set_lock);
-}
-
-static inline int started_after_time(struct task_struct *t1,
- struct timespec *time,
- struct task_struct *t2)
-{
- int start_diff = timespec_compare(&t1->start_time, time);
- if (start_diff > 0) {
- return 1;
- } else if (start_diff < 0) {
- return 0;
- } else {
- /*
- * Arbitrarily, if two processes started at the same
- * time, we'll say that the lower pointer value
- * started first. Note that t2 may have exited by now
- * so this may not be a valid pointer any longer, but
- * that's fine - it still serves to distinguish
- * between two tasks started (effectively) simultaneously.
- */
- return t1 > t2;
- }
-}
-
-/*
- * This function is a callback from heap_insert() and is used to order
- * the heap.
- * In this case we order the heap in descending task start time.
- */
-static inline int started_after(void *p1, void *p2)
+ __releases(css_set_rwsem)
{
- struct task_struct *t1 = p1;
- struct task_struct *t2 = p2;
- return started_after_time(t1, &t2->start_time, t2);
+ up_read(&css_set_rwsem);
}
/**
- * css_scan_tasks - iterate though all the tasks in a css
- * @css: the css to iterate tasks of
- * @test: optional test callback
- * @process: process callback
- * @data: data passed to @test and @process
- * @heap: optional pre-allocated heap used for task iteration
- *
- * Iterate through all the tasks in @css, calling @test for each, and if it
- * returns %true, call @process for it also.
- *
- * @test may be NULL, meaning always true (select all tasks), which
- * effectively duplicates css_task_iter_{start,next,end}() but does not
- * lock css_set_lock for the call to @process.
- *
- * It is guaranteed that @process will act on every task that is a member
- * of @css for the duration of this call. This function may or may not
- * call @process for tasks that exit or move to a different css during the
- * call, or are forked or move into the css during the call.
- *
- * Note that @test may be called with locks held, and may in some
- * situations be called multiple times for the same task, so it should be
- * cheap.
+ * cgroup_trasnsfer_tasks - move tasks from one cgroup to another
+ * @to: cgroup to which the tasks will be moved
+ * @from: cgroup in which the tasks currently reside
*
- * If @heap is non-NULL, a heap has been pre-allocated and will be used for
- * heap operations (and its "gt" member will be overwritten), else a
- * temporary heap will be used (allocation of which may cause this function
- * to fail).
+ * Locking rules between cgroup_post_fork() and the migration path
+ * guarantee that, if a task is forking while being migrated, the new child
+ * is guaranteed to be either visible in the source cgroup after the
+ * parent's migration is complete or put into the target cgroup. No task
+ * can slip out of migration through forking.
*/
-int css_scan_tasks(struct cgroup_subsys_state *css,
- bool (*test)(struct task_struct *, void *),
- void (*process)(struct task_struct *, void *),
- void *data, struct ptr_heap *heap)
+int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
{
- int retval, i;
+ LIST_HEAD(preloaded_csets);
+ struct cgrp_cset_link *link;
struct css_task_iter it;
- struct task_struct *p, *dropped;
- /* Never dereference latest_task, since it's not refcounted */
- struct task_struct *latest_task = NULL;
- struct ptr_heap tmp_heap;
- struct timespec latest_time = { 0, 0 };
-
- if (heap) {
- /* The caller supplied our heap and pre-allocated its memory */
- heap->gt = &started_after;
- } else {
- /* We need to allocate our own heap memory */
- heap = &tmp_heap;
- retval = heap_init(heap, PAGE_SIZE, GFP_KERNEL, &started_after);
- if (retval)
- /* cannot allocate the heap */
- return retval;
- }
+ struct task_struct *task;
+ int ret;
- again:
- /*
- * Scan tasks in the css, using the @test callback to determine
- * which are of interest, and invoking @process callback on the
- * ones which need an update. Since we don't want to hold any
- * locks during the task updates, gather tasks to be processed in a
- * heap structure. The heap is sorted by descending task start
- * time. If the statically-sized heap fills up, we overflow tasks
- * that started later, and in future iterations only consider tasks
- * that started after the latest task in the previous pass. This
- * guarantees forward progress and that we don't miss any tasks.
- */
- heap->size = 0;
- css_task_iter_start(css, &it);
- while ((p = css_task_iter_next(&it))) {
- /*
- * Only affect tasks that qualify per the caller's callback,
- * if he provided one
- */
- if (test && !test(p, data))
- continue;
- /*
- * Only process tasks that started after the last task
- * we processed
- */
- if (!started_after_time(p, &latest_time, latest_task))
- continue;
- dropped = heap_insert(heap, p);
- if (dropped == NULL) {
- /*
- * The new task was inserted; the heap wasn't
- * previously full
- */
- get_task_struct(p);
- } else if (dropped != p) {
- /*
- * The new task was inserted, and pushed out a
- * different task
- */
- get_task_struct(p);
- put_task_struct(dropped);
- }
- /*
- * Else the new task was newer than anything already in
- * the heap and wasn't inserted
- */
- }
- css_task_iter_end(&it);
+ mutex_lock(&cgroup_mutex);
- if (heap->size) {
- for (i = 0; i < heap->size; i++) {
- struct task_struct *q = heap->ptrs[i];
- if (i == 0) {
- latest_time = q->start_time;
- latest_task = q;
- }
- /* Process the task per the caller's callback */
- process(q, data);
- put_task_struct(q);
- }
- /*
- * If we had to process any tasks at all, scan again
- * in case some of them were in the middle of forking
- * children that didn't get processed.
- * Not the most efficient way to do it, but it avoids
- * having to take callback_mutex in the fork path
- */
- goto again;
- }
- if (heap == &tmp_heap)
- heap_free(&tmp_heap);
- return 0;
-}
+ /* all tasks in @from are being moved, all csets are source */
+ down_read(&css_set_rwsem);
+ list_for_each_entry(link, &from->cset_links, cset_link)
+ cgroup_migrate_add_src(link->cset, to, &preloaded_csets);
+ up_read(&css_set_rwsem);
-static void cgroup_transfer_one_task(struct task_struct *task, void *data)
-{
- struct cgroup *new_cgroup = data;
+ ret = cgroup_migrate_prepare_dst(to, &preloaded_csets);
+ if (ret)
+ goto out_err;
- mutex_lock(&cgroup_mutex);
- cgroup_attach_task(new_cgroup, task, false);
+ /*
+ * Migrate tasks one-by-one until @form is empty. This fails iff
+ * ->can_attach() fails.
+ */
+ do {
+ css_task_iter_start(&from->dummy_css, &it);
+ task = css_task_iter_next(&it);
+ if (task)
+ get_task_struct(task);
+ css_task_iter_end(&it);
+
+ if (task) {
+ ret = cgroup_migrate(to, task, false);
+ put_task_struct(task);
+ }
+ } while (task && !ret);
+out_err:
+ cgroup_migrate_finish(&preloaded_csets);
mutex_unlock(&cgroup_mutex);
-}
-
-/**
- * cgroup_trasnsfer_tasks - move tasks from one cgroup to another
- * @to: cgroup to which the tasks will be moved
- * @from: cgroup in which the tasks currently reside
- */
-int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
-{
- return css_scan_tasks(&from->dummy_css, NULL, cgroup_transfer_one_task,
- to, NULL);
+ return ret;
}
/*
@@ -3687,21 +3210,31 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type,
*/
int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry)
{
- int ret = -EINVAL;
+ struct kernfs_node *kn = kernfs_node_from_dentry(dentry);
struct cgroup *cgrp;
struct css_task_iter it;
struct task_struct *tsk;
+ /* it should be kernfs_node belonging to cgroupfs and is a directory */
+ if (dentry->d_sb->s_type != &cgroup_fs_type || !kn ||
+ kernfs_type(kn) != KERNFS_DIR)
+ return -EINVAL;
+
+ mutex_lock(&cgroup_mutex);
+
/*
- * Validate dentry by checking the superblock operations,
- * and make sure it's a directory.
+ * We aren't being called from kernfs and there's no guarantee on
+ * @kn->priv's validity. For this and css_tryget_from_dir(),
+ * @kn->priv is RCU safe. Let's do the RCU dancing.
*/
- if (dentry->d_sb->s_op != &cgroup_ops ||
- !S_ISDIR(dentry->d_inode->i_mode))
- goto err;
-
- ret = 0;
- cgrp = dentry->d_fsdata;
+ rcu_read_lock();
+ cgrp = rcu_dereference(kn->priv);
+ if (!cgrp || cgroup_is_dead(cgrp)) {
+ rcu_read_unlock();
+ mutex_unlock(&cgroup_mutex);
+ return -ENOENT;
+ }
+ rcu_read_unlock();
css_task_iter_start(&cgrp->dummy_css, &it);
while ((tsk = css_task_iter_next(&it))) {
@@ -3726,8 +3259,8 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry)
}
css_task_iter_end(&it);
-err:
- return ret;
+ mutex_unlock(&cgroup_mutex);
+ return 0;
}
@@ -3745,7 +3278,7 @@ static void *cgroup_pidlist_start(struct seq_file *s, loff_t *pos)
* after a seek to the start). Use a binary-search to find the
* next pid to display, if any
*/
- struct cgroup_open_file *of = s->private;
+ struct kernfs_open_file *of = s->private;
struct cgroup *cgrp = seq_css(s)->cgroup;
struct cgroup_pidlist *l;
enum cgroup_filetype type = seq_cft(s)->private;
@@ -3800,7 +3333,7 @@ static void *cgroup_pidlist_start(struct seq_file *s, loff_t *pos)
static void cgroup_pidlist_stop(struct seq_file *s, void *v)
{
- struct cgroup_open_file *of = s->private;
+ struct kernfs_open_file *of = s->private;
struct cgroup_pidlist *l = of->priv;
if (l)
@@ -3811,7 +3344,7 @@ static void cgroup_pidlist_stop(struct seq_file *s, void *v)
static void *cgroup_pidlist_next(struct seq_file *s, void *v, loff_t *pos)
{
- struct cgroup_open_file *of = s->private;
+ struct kernfs_open_file *of = s->private;
struct cgroup_pidlist *l = of->priv;
pid_t *p = v;
pid_t *end = l->list + l->length;
@@ -3861,23 +3394,6 @@ static int cgroup_write_notify_on_release(struct cgroup_subsys_state *css,
return 0;
}
-/*
- * When dput() is called asynchronously, if umount has been done and
- * then deactivate_super() in cgroup_free_fn() kills the superblock,
- * there's a small window that vfs will see the root dentry with non-zero
- * refcnt and trigger BUG().
- *
- * That's why we hold a reference before dput() and drop it right after.
- */
-static void cgroup_dput(struct cgroup *cgrp)
-{
- struct super_block *sb = cgrp->root->sb;
-
- atomic_inc(&sb->s_active);
- dput(cgrp->dentry);
- deactivate_super(sb);
-}
-
static u64 cgroup_clone_children_read(struct cgroup_subsys_state *css,
struct cftype *cft)
{
@@ -3944,7 +3460,7 @@ static struct cftype cgroup_base_files[] = {
.flags = CFTYPE_INSANE | CFTYPE_ONLY_ON_ROOT,
.seq_show = cgroup_release_agent_show,
.write_string = cgroup_release_agent_write,
- .max_write_len = PATH_MAX,
+ .max_write_len = PATH_MAX - 1,
},
{ } /* terminate */
};
@@ -3963,13 +3479,13 @@ static int cgroup_populate_dir(struct cgroup *cgrp, unsigned long subsys_mask)
/* process cftsets of each subsystem */
for_each_subsys(ss, i) {
- struct cftype_set *set;
+ struct cftype *cfts;
if (!test_bit(i, &subsys_mask))
continue;
- list_for_each_entry(set, &ss->cftsets, node) {
- ret = cgroup_addrm_files(cgrp, set->cfts, true);
+ list_for_each_entry(cfts, &ss->cfts, node) {
+ ret = cgroup_addrm_files(cgrp, cfts, true);
if (ret < 0)
goto err;
}
@@ -4012,7 +3528,7 @@ static void css_free_work_fn(struct work_struct *work)
css_put(css->parent);
css->ss->css_free(css);
- cgroup_dput(cgrp);
+ cgroup_put(cgrp);
}
static void css_free_rcu_fn(struct rcu_head *rcu_head)
@@ -4020,10 +3536,6 @@ static void css_free_rcu_fn(struct rcu_head *rcu_head)
struct cgroup_subsys_state *css =
container_of(rcu_head, struct cgroup_subsys_state, rcu_head);
- /*
- * css holds an extra ref to @cgrp->dentry which is put on the last
- * css_put(). dput() requires process context which we don't have.
- */
INIT_WORK(&css->destroy_work, css_free_work_fn);
queue_work(cgroup_destroy_wq, &css->destroy_work);
}
@@ -4033,7 +3545,7 @@ static void css_release(struct percpu_ref *ref)
struct cgroup_subsys_state *css =
container_of(ref, struct cgroup_subsys_state, refcnt);
- rcu_assign_pointer(css->cgroup->subsys[css->ss->subsys_id], NULL);
+ RCU_INIT_POINTER(css->cgroup->subsys[css->ss->id], NULL);
call_rcu(&css->rcu_head, css_free_rcu_fn);
}
@@ -4058,6 +3570,7 @@ static int online_css(struct cgroup_subsys_state *css)
struct cgroup_subsys *ss = css->ss;
int ret = 0;
+ lockdep_assert_held(&cgroup_tree_mutex);
lockdep_assert_held(&cgroup_mutex);
if (ss->css_online)
@@ -4065,7 +3578,7 @@ static int online_css(struct cgroup_subsys_state *css)
if (!ret) {
css->flags |= CSS_ONLINE;
css->cgroup->nr_css++;
- rcu_assign_pointer(css->cgroup->subsys[ss->subsys_id], css);
+ rcu_assign_pointer(css->cgroup->subsys[ss->id], css);
}
return ret;
}
@@ -4075,6 +3588,7 @@ static void offline_css(struct cgroup_subsys_state *css)
{
struct cgroup_subsys *ss = css->ss;
+ lockdep_assert_held(&cgroup_tree_mutex);
lockdep_assert_held(&cgroup_mutex);
if (!(css->flags & CSS_ONLINE))
@@ -4085,7 +3599,7 @@ static void offline_css(struct cgroup_subsys_state *css)
css->flags &= ~CSS_ONLINE;
css->cgroup->nr_css--;
- RCU_INIT_POINTER(css->cgroup->subsys[ss->subsys_id], css);
+ RCU_INIT_POINTER(css->cgroup->subsys[ss->id], css);
}
/**
@@ -4103,7 +3617,6 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss)
struct cgroup_subsys_state *css;
int err;
- lockdep_assert_held(&cgrp->dentry->d_inode->i_mutex);
lockdep_assert_held(&cgroup_mutex);
css = ss->css_alloc(cgroup_css(parent, ss));
@@ -4116,7 +3629,7 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss)
init_css(css, ss, cgrp);
- err = cgroup_populate_dir(cgrp, 1 << ss->subsys_id);
+ err = cgroup_populate_dir(cgrp, 1 << ss->id);
if (err)
goto err_free_percpu_ref;
@@ -4124,9 +3637,11 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss)
if (err)
goto err_clear_dir;
- dget(cgrp->dentry);
+ cgroup_get(cgrp);
css_get(css->parent);
+ cgrp->subsys_mask |= 1 << ss->id;
+
if (ss->broken_hierarchy && !ss->warned_broken_hierarchy &&
parent->parent) {
pr_warning("cgroup: %s (%d) created nested cgroup for controller \"%s\" which has incomplete hierarchy support. Nested cgroups may change behavior in the future.\n",
@@ -4139,7 +3654,7 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss)
return 0;
err_clear_dir:
- cgroup_clear_dir(css->cgroup, 1 << css->ss->subsys_id);
+ cgroup_clear_dir(css->cgroup, 1 << css->ss->id);
err_free_percpu_ref:
percpu_ref_cancel_init(&css->refcnt);
err_free_css:
@@ -4147,35 +3662,34 @@ err_free_css:
return err;
}
-/*
+/**
* cgroup_create - create a cgroup
* @parent: cgroup that will be parent of the new cgroup
- * @dentry: dentry of the new cgroup
- * @mode: mode to set on new inode
- *
- * Must be called with the mutex on the parent inode held
+ * @name: name of the new cgroup
+ * @mode: mode to set on new cgroup
*/
-static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
- umode_t mode)
+static long cgroup_create(struct cgroup *parent, const char *name,
+ umode_t mode)
{
struct cgroup *cgrp;
- struct cgroup_name *name;
- struct cgroupfs_root *root = parent->root;
+ struct cgroup_root *root = parent->root;
int ssid, err;
struct cgroup_subsys *ss;
- struct super_block *sb = root->sb;
+ struct kernfs_node *kn;
+
+ /*
+ * XXX: The default hierarchy isn't fully implemented yet. Block
+ * !root cgroup creation on it for now.
+ */
+ if (root == &cgrp_dfl_root)
+ return -EINVAL;
/* allocate the cgroup and its ID, 0 is reserved for the root */
cgrp = kzalloc(sizeof(*cgrp), GFP_KERNEL);
if (!cgrp)
return -ENOMEM;
- name = cgroup_alloc_name(dentry);
- if (!name) {
- err = -ENOMEM;
- goto err_free_cgrp;
- }
- rcu_assign_pointer(cgrp->name, name);
+ mutex_lock(&cgroup_tree_mutex);
/*
* Only live parents can have children. Note that the liveliness
@@ -4186,7 +3700,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
*/
if (!cgroup_lock_live_group(parent)) {
err = -ENODEV;
- goto err_free_name;
+ goto err_unlock_tree;
}
/*
@@ -4199,18 +3713,8 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
goto err_unlock;
}
- /* Grab a reference on the superblock so the hierarchy doesn't
- * get deleted on unmount if there are child cgroups. This
- * can be done outside cgroup_mutex, since the sb can't
- * disappear while someone has an open control file on the
- * fs */
- atomic_inc(&sb->s_active);
-
init_cgroup_housekeeping(cgrp);
- dentry->d_fsdata = cgrp;
- cgrp->dentry = dentry;
-
cgrp->parent = parent;
cgrp->dummy_css.parent = &parent->dummy_css;
cgrp->root = parent->root;
@@ -4221,24 +3725,26 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
if (test_bit(CGRP_CPUSET_CLONE_CHILDREN, &parent->flags))
set_bit(CGRP_CPUSET_CLONE_CHILDREN, &cgrp->flags);
+ /* create the directory */
+ kn = kernfs_create_dir(parent->kn, name, mode, cgrp);
+ if (IS_ERR(kn)) {
+ err = PTR_ERR(kn);
+ goto err_free_id;
+ }
+ cgrp->kn = kn;
+
/*
- * Create directory. cgroup_create_file() returns with the new
- * directory locked on success so that it can be populated without
- * dropping cgroup_mutex.
+ * This extra ref will be put in cgroup_free_fn() and guarantees
+ * that @cgrp->kn is always accessible.
*/
- err = cgroup_create_file(dentry, S_IFDIR | mode, sb);
- if (err < 0)
- goto err_free_id;
- lockdep_assert_held(&dentry->d_inode->i_mutex);
+ kernfs_get(kn);
cgrp->serial_nr = cgroup_serial_nr_next++;
/* allocation complete, commit to creation */
list_add_tail_rcu(&cgrp->sibling, &cgrp->parent->children);
- root->number_of_cgroups++;
-
- /* hold a ref to the parent's dentry */
- dget(parent->dentry);
+ atomic_inc(&root->nr_cgrps);
+ cgroup_get(parent);
/*
* @cgrp is now fully operational. If something fails after this
@@ -4252,43 +3758,56 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
/* let's create and online css's */
for_each_subsys(ss, ssid) {
- if (root->subsys_mask & (1 << ssid)) {
+ if (root->cgrp.subsys_mask & (1 << ssid)) {
err = create_css(cgrp, ss);
if (err)
goto err_destroy;
}
}
+ kernfs_activate(kn);
+
mutex_unlock(&cgroup_mutex);
- mutex_unlock(&cgrp->dentry->d_inode->i_mutex);
+ mutex_unlock(&cgroup_tree_mutex);
return 0;
err_free_id:
idr_remove(&root->cgroup_idr, cgrp->id);
- /* Release the reference count that we took on the superblock */
- deactivate_super(sb);
err_unlock:
mutex_unlock(&cgroup_mutex);
-err_free_name:
- kfree(rcu_dereference_raw(cgrp->name));
-err_free_cgrp:
+err_unlock_tree:
+ mutex_unlock(&cgroup_tree_mutex);
kfree(cgrp);
return err;
err_destroy:
cgroup_destroy_locked(cgrp);
mutex_unlock(&cgroup_mutex);
- mutex_unlock(&dentry->d_inode->i_mutex);
+ mutex_unlock(&cgroup_tree_mutex);
return err;
}
-static int cgroup_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
+ umode_t mode)
{
- struct cgroup *c_parent = dentry->d_parent->d_fsdata;
+ struct cgroup *parent = parent_kn->priv;
+ int ret;
+
+ /*
+ * cgroup_create() grabs cgroup_tree_mutex which nests outside
+ * kernfs active_ref and cgroup_create() already synchronizes
+ * properly against removal through cgroup_lock_live_group().
+ * Break it before calling cgroup_create().
+ */
+ cgroup_get(parent);
+ kernfs_break_active_protection(parent_kn);
+
+ ret = cgroup_create(parent, name, mode);
- /* the vfs holds inode->i_mutex already */
- return cgroup_create(c_parent, dentry, mode | S_IFDIR);
+ kernfs_unbreak_active_protection(parent_kn);
+ cgroup_put(parent);
+ return ret;
}
/*
@@ -4301,6 +3820,7 @@ static void css_killed_work_fn(struct work_struct *work)
container_of(work, struct cgroup_subsys_state, destroy_work);
struct cgroup *cgrp = css->cgroup;
+ mutex_lock(&cgroup_tree_mutex);
mutex_lock(&cgroup_mutex);
/*
@@ -4318,6 +3838,7 @@ static void css_killed_work_fn(struct work_struct *work)
cgroup_destroy_css_killed(cgrp);
mutex_unlock(&cgroup_mutex);
+ mutex_unlock(&cgroup_tree_mutex);
/*
* Put the css refs from kill_css(). Each css holds an extra
@@ -4339,18 +3860,15 @@ static void css_killed_ref_fn(struct percpu_ref *ref)
queue_work(cgroup_destroy_wq, &css->destroy_work);
}
-/**
- * kill_css - destroy a css
- * @css: css to destroy
- *
- * This function initiates destruction of @css by removing cgroup interface
- * files and putting its base reference. ->css_offline() will be invoked
- * asynchronously once css_tryget() is guaranteed to fail and when the
- * reference count reaches zero, @css will be released.
- */
-static void kill_css(struct cgroup_subsys_state *css)
+static void __kill_css(struct cgroup_subsys_state *css)
{
- cgroup_clear_dir(css->cgroup, 1 << css->ss->subsys_id);
+ lockdep_assert_held(&cgroup_tree_mutex);
+
+ /*
+ * This must happen before css is disassociated with its cgroup.
+ * See seq_css() for details.
+ */
+ cgroup_clear_dir(css->cgroup, 1 << css->ss->id);
/*
* Killing would put the base ref, but we need to keep it alive
@@ -4372,6 +3890,28 @@ static void kill_css(struct cgroup_subsys_state *css)
}
/**
+ * kill_css - destroy a css
+ * @css: css to destroy
+ *
+ * This function initiates destruction of @css by removing cgroup interface
+ * files and putting its base reference. ->css_offline() will be invoked
+ * asynchronously once css_tryget() is guaranteed to fail and when the
+ * reference count reaches zero, @css will be released.
+ */
+static void kill_css(struct cgroup_subsys_state *css)
+{
+ struct cgroup *cgrp = css->cgroup;
+
+ lockdep_assert_held(&cgroup_tree_mutex);
+
+ /* if already killed, noop */
+ if (cgrp->subsys_mask & (1 << css->ss->id)) {
+ cgrp->subsys_mask &= ~(1 << css->ss->id);
+ __kill_css(css);
+ }
+}
+
+/**
* cgroup_destroy_locked - the first stage of cgroup destruction
* @cgrp: cgroup to be destroyed
*
@@ -4398,22 +3938,21 @@ static void kill_css(struct cgroup_subsys_state *css)
static int cgroup_destroy_locked(struct cgroup *cgrp)
__releases(&cgroup_mutex) __acquires(&cgroup_mutex)
{
- struct dentry *d = cgrp->dentry;
- struct cgroup_subsys_state *css;
struct cgroup *child;
+ struct cgroup_subsys_state *css;
bool empty;
int ssid;
- lockdep_assert_held(&d->d_inode->i_mutex);
+ lockdep_assert_held(&cgroup_tree_mutex);
lockdep_assert_held(&cgroup_mutex);
/*
- * css_set_lock synchronizes access to ->cset_links and prevents
- * @cgrp from being removed while __put_css_set() is in progress.
+ * css_set_rwsem synchronizes access to ->cset_links and prevents
+ * @cgrp from being removed while put_css_set() is in progress.
*/
- read_lock(&css_set_lock);
+ down_read(&css_set_rwsem);
empty = list_empty(&cgrp->cset_links);
- read_unlock(&css_set_lock);
+ up_read(&css_set_rwsem);
if (!empty)
return -EBUSY;
@@ -4434,14 +3973,6 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
return -EBUSY;
/*
- * Initiate massacre of all css's. cgroup_destroy_css_killed()
- * will be invoked to perform the rest of destruction once the
- * percpu refs of all css's are confirmed to be killed.
- */
- for_each_css(css, ssid, cgrp)
- kill_css(css);
-
- /*
* Mark @cgrp dead. This prevents further task migration and child
* creation by disabling cgroup_lock_live_group(). Note that
* CGRP_DEAD assertion is depended upon by css_next_child() to
@@ -4450,6 +3981,17 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
*/
set_bit(CGRP_DEAD, &cgrp->flags);
+ /*
+ * Initiate massacre of all css's. cgroup_destroy_css_killed()
+ * will be invoked to perform the rest of destruction once the
+ * percpu refs of all css's are confirmed to be killed. This
+ * involves removing the subsystem's files, drop cgroup_mutex.
+ */
+ mutex_unlock(&cgroup_mutex);
+ for_each_css(css, ssid, cgrp)
+ kill_css(css);
+ mutex_lock(&cgroup_mutex);
+
/* CGRP_DEAD is set, remove from ->release_list for the last time */
raw_spin_lock(&release_list_lock);
if (!list_empty(&cgrp->release_list))
@@ -4465,14 +4007,20 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
if (!cgrp->nr_css)
cgroup_destroy_css_killed(cgrp);
+ /* remove @cgrp directory along with the base files */
+ mutex_unlock(&cgroup_mutex);
+
/*
- * Clear the base files and remove @cgrp directory. The removal
- * puts the base ref but we aren't quite done with @cgrp yet, so
- * hold onto it.
+ * There are two control paths which try to determine cgroup from
+ * dentry without going through kernfs - cgroupstats_build() and
+ * css_tryget_from_dir(). Those are supported by RCU protecting
+ * clearing of cgrp->kn->priv backpointer, which should happen
+ * after all files under it have been removed.
*/
- cgroup_addrm_files(cgrp, cgroup_base_files, false);
- dget(d);
- cgroup_d_remove_dir(d);
+ kernfs_remove(cgrp->kn); /* @cgrp has an extra ref on its kn */
+ RCU_INIT_POINTER(*(void __rcu __force **)&cgrp->kn->priv, NULL);
+
+ mutex_lock(&cgroup_mutex);
return 0;
};
@@ -4489,72 +4037,82 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
static void cgroup_destroy_css_killed(struct cgroup *cgrp)
{
struct cgroup *parent = cgrp->parent;
- struct dentry *d = cgrp->dentry;
+ lockdep_assert_held(&cgroup_tree_mutex);
lockdep_assert_held(&cgroup_mutex);
/* delete this cgroup from parent->children */
list_del_rcu(&cgrp->sibling);
- dput(d);
+ cgroup_put(cgrp);
set_bit(CGRP_RELEASABLE, &parent->flags);
check_for_release(parent);
}
-static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry)
+static int cgroup_rmdir(struct kernfs_node *kn)
{
- int ret;
-
- mutex_lock(&cgroup_mutex);
- ret = cgroup_destroy_locked(dentry->d_fsdata);
- mutex_unlock(&cgroup_mutex);
+ struct cgroup *cgrp = kn->priv;
+ int ret = 0;
- return ret;
-}
+ /*
+ * This is self-destruction but @kn can't be removed while this
+ * callback is in progress. Let's break active protection. Once
+ * the protection is broken, @cgrp can be destroyed at any point.
+ * Pin it so that it stays accessible.
+ */
+ cgroup_get(cgrp);
+ kernfs_break_active_protection(kn);
-static void __init_or_module cgroup_init_cftsets(struct cgroup_subsys *ss)
-{
- INIT_LIST_HEAD(&ss->cftsets);
+ mutex_lock(&cgroup_tree_mutex);
+ mutex_lock(&cgroup_mutex);
/*
- * base_cftset is embedded in subsys itself, no need to worry about
- * deregistration.
+ * @cgrp might already have been destroyed while we're trying to
+ * grab the mutexes.
*/
- if (ss->base_cftypes) {
- struct cftype *cft;
+ if (!cgroup_is_dead(cgrp))
+ ret = cgroup_destroy_locked(cgrp);
- for (cft = ss->base_cftypes; cft->name[0] != '\0'; cft++)
- cft->ss = ss;
+ mutex_unlock(&cgroup_mutex);
+ mutex_unlock(&cgroup_tree_mutex);
- ss->base_cftset.cfts = ss->base_cftypes;
- list_add_tail(&ss->base_cftset.node, &ss->cftsets);
- }
+ kernfs_unbreak_active_protection(kn);
+ cgroup_put(cgrp);
+ return ret;
}
+static struct kernfs_syscall_ops cgroup_kf_syscall_ops = {
+ .remount_fs = cgroup_remount,
+ .show_options = cgroup_show_options,
+ .mkdir = cgroup_mkdir,
+ .rmdir = cgroup_rmdir,
+ .rename = cgroup_rename,
+};
+
static void __init cgroup_init_subsys(struct cgroup_subsys *ss)
{
struct cgroup_subsys_state *css;
printk(KERN_INFO "Initializing cgroup subsys %s\n", ss->name);
+ mutex_lock(&cgroup_tree_mutex);
mutex_lock(&cgroup_mutex);
- /* init base cftset */
- cgroup_init_cftsets(ss);
+ INIT_LIST_HEAD(&ss->cfts);
- /* Create the top cgroup state for this subsystem */
- ss->root = &cgroup_dummy_root;
- css = ss->css_alloc(cgroup_css(cgroup_dummy_top, ss));
+ /* Create the root cgroup state for this subsystem */
+ ss->root = &cgrp_dfl_root;
+ css = ss->css_alloc(cgroup_css(&cgrp_dfl_root.cgrp, ss));
/* We don't handle early failures gracefully */
BUG_ON(IS_ERR(css));
- init_css(css, ss, cgroup_dummy_top);
+ init_css(css, ss, &cgrp_dfl_root.cgrp);
/* Update the init_css_set to contain a subsys
* pointer to this state - since the subsystem is
* newly registered, all tasks and hence the
- * init_css_set is in the subsystem's top cgroup. */
- init_css_set.subsys[ss->subsys_id] = css;
+ * init_css_set is in the subsystem's root cgroup. */
+ init_css_set.subsys[ss->id] = css;
need_forkexit_callback |= ss->fork || ss->exit;
@@ -4565,185 +4123,11 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss)
BUG_ON(online_css(css));
- mutex_unlock(&cgroup_mutex);
-
- /* this function shouldn't be used with modular subsystems, since they
- * need to register a subsys_id, among other things */
- BUG_ON(ss->module);
-}
-
-/**
- * cgroup_load_subsys: load and register a modular subsystem at runtime
- * @ss: the subsystem to load
- *
- * This function should be called in a modular subsystem's initcall. If the
- * subsystem is built as a module, it will be assigned a new subsys_id and set
- * up for use. If the subsystem is built-in anyway, work is delegated to the
- * simpler cgroup_init_subsys.
- */
-int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss)
-{
- struct cgroup_subsys_state *css;
- int i, ret;
- struct hlist_node *tmp;
- struct css_set *cset;
- unsigned long key;
-
- /* check name and function validity */
- if (ss->name == NULL || strlen(ss->name) > MAX_CGROUP_TYPE_NAMELEN ||
- ss->css_alloc == NULL || ss->css_free == NULL)
- return -EINVAL;
-
- /*
- * we don't support callbacks in modular subsystems. this check is
- * before the ss->module check for consistency; a subsystem that could
- * be a module should still have no callbacks even if the user isn't
- * compiling it as one.
- */
- if (ss->fork || ss->exit)
- return -EINVAL;
-
- /*
- * an optionally modular subsystem is built-in: we want to do nothing,
- * since cgroup_init_subsys will have already taken care of it.
- */
- if (ss->module == NULL) {
- /* a sanity check */
- BUG_ON(cgroup_subsys[ss->subsys_id] != ss);
- return 0;
- }
-
- /* init base cftset */
- cgroup_init_cftsets(ss);
-
- mutex_lock(&cgroup_mutex);
- mutex_lock(&cgroup_root_mutex);
- cgroup_subsys[ss->subsys_id] = ss;
-
- /*
- * no ss->css_alloc seems to need anything important in the ss
- * struct, so this can happen first (i.e. before the dummy root
- * attachment).
- */
- css = ss->css_alloc(cgroup_css(cgroup_dummy_top, ss));
- if (IS_ERR(css)) {
- /* failure case - need to deassign the cgroup_subsys[] slot. */
- cgroup_subsys[ss->subsys_id] = NULL;
- mutex_unlock(&cgroup_root_mutex);
- mutex_unlock(&cgroup_mutex);
- return PTR_ERR(css);
- }
-
- ss->root = &cgroup_dummy_root;
-
- /* our new subsystem will be attached to the dummy hierarchy. */
- init_css(css, ss, cgroup_dummy_top);
-
- /*
- * Now we need to entangle the css into the existing css_sets. unlike
- * in cgroup_init_subsys, there are now multiple css_sets, so each one
- * will need a new pointer to it; done by iterating the css_set_table.
- * furthermore, modifying the existing css_sets will corrupt the hash
- * table state, so each changed css_set will need its hash recomputed.
- * this is all done under the css_set_lock.
- */
- write_lock(&css_set_lock);
- hash_for_each_safe(css_set_table, i, tmp, cset, hlist) {
- /* skip entries that we already rehashed */
- if (cset->subsys[ss->subsys_id])
- continue;
- /* remove existing entry */
- hash_del(&cset->hlist);
- /* set new value */
- cset->subsys[ss->subsys_id] = css;
- /* recompute hash and restore entry */
- key = css_set_hash(cset->subsys);
- hash_add(css_set_table, &cset->hlist, key);
- }
- write_unlock(&css_set_lock);
-
- ret = online_css(css);
- if (ret) {
- ss->css_free(css);
- goto err_unload;
- }
-
- /* success! */
- mutex_unlock(&cgroup_root_mutex);
- mutex_unlock(&cgroup_mutex);
- return 0;
-
-err_unload:
- mutex_unlock(&cgroup_root_mutex);
- mutex_unlock(&cgroup_mutex);
- /* @ss can't be mounted here as try_module_get() would fail */
- cgroup_unload_subsys(ss);
- return ret;
-}
-EXPORT_SYMBOL_GPL(cgroup_load_subsys);
-
-/**
- * cgroup_unload_subsys: unload a modular subsystem
- * @ss: the subsystem to unload
- *
- * This function should be called in a modular subsystem's exitcall. When this
- * function is invoked, the refcount on the subsystem's module will be 0, so
- * the subsystem will not be attached to any hierarchy.
- */
-void cgroup_unload_subsys(struct cgroup_subsys *ss)
-{
- struct cgrp_cset_link *link;
- struct cgroup_subsys_state *css;
-
- BUG_ON(ss->module == NULL);
-
- /*
- * we shouldn't be called if the subsystem is in use, and the use of
- * try_module_get() in rebind_subsystems() should ensure that it
- * doesn't start being used while we're killing it off.
- */
- BUG_ON(ss->root != &cgroup_dummy_root);
-
- mutex_lock(&cgroup_mutex);
- mutex_lock(&cgroup_root_mutex);
-
- css = cgroup_css(cgroup_dummy_top, ss);
- if (css)
- offline_css(css);
+ cgrp_dfl_root.cgrp.subsys_mask |= 1 << ss->id;
- /* deassign the subsys_id */
- cgroup_subsys[ss->subsys_id] = NULL;
-
- /*
- * disentangle the css from all css_sets attached to the dummy
- * top. as in loading, we need to pay our respects to the hashtable
- * gods.
- */
- write_lock(&css_set_lock);
- list_for_each_entry(link, &cgroup_dummy_top->cset_links, cset_link) {
- struct css_set *cset = link->cset;
- unsigned long key;
-
- hash_del(&cset->hlist);
- cset->subsys[ss->subsys_id] = NULL;
- key = css_set_hash(cset->subsys);
- hash_add(css_set_table, &cset->hlist, key);
- }
- write_unlock(&css_set_lock);
-
- /*
- * remove subsystem's css from the cgroup_dummy_top and free it -
- * need to free before marking as null because ss->css_free needs
- * the cgrp->subsys pointer to find their state.
- */
- if (css)
- ss->css_free(css);
- RCU_INIT_POINTER(cgroup_dummy_top->subsys[ss->subsys_id], NULL);
-
- mutex_unlock(&cgroup_root_mutex);
mutex_unlock(&cgroup_mutex);
+ mutex_unlock(&cgroup_tree_mutex);
}
-EXPORT_SYMBOL_GPL(cgroup_unload_subsys);
/**
* cgroup_init_early - cgroup initialization at system boot
@@ -4753,34 +4137,24 @@ EXPORT_SYMBOL_GPL(cgroup_unload_subsys);
*/
int __init cgroup_init_early(void)
{
+ static struct cgroup_sb_opts __initdata opts =
+ { .flags = CGRP_ROOT_SANE_BEHAVIOR };
struct cgroup_subsys *ss;
int i;
- atomic_set(&init_css_set.refcount, 1);
- INIT_LIST_HEAD(&init_css_set.cgrp_links);
- INIT_LIST_HEAD(&init_css_set.tasks);
- INIT_HLIST_NODE(&init_css_set.hlist);
- css_set_count = 1;
- init_cgroup_root(&cgroup_dummy_root);
- cgroup_root_count = 1;
+ init_cgroup_root(&cgrp_dfl_root, &opts);
RCU_INIT_POINTER(init_task.cgroups, &init_css_set);
- init_cgrp_cset_link.cset = &init_css_set;
- init_cgrp_cset_link.cgrp = cgroup_dummy_top;
- list_add(&init_cgrp_cset_link.cset_link, &cgroup_dummy_top->cset_links);
- list_add(&init_cgrp_cset_link.cgrp_link, &init_css_set.cgrp_links);
-
- /* at bootup time, we don't worry about modular subsystems */
- for_each_builtin_subsys(ss, i) {
- BUG_ON(!ss->name);
- BUG_ON(strlen(ss->name) > MAX_CGROUP_TYPE_NAMELEN);
- BUG_ON(!ss->css_alloc);
- BUG_ON(!ss->css_free);
- if (ss->subsys_id != i) {
- printk(KERN_ERR "cgroup: Subsys %s id == %d\n",
- ss->name, ss->subsys_id);
- BUG();
- }
+ for_each_subsys(ss, i) {
+ WARN(!ss->css_alloc || !ss->css_free || ss->name || ss->id,
+ "invalid cgroup_subsys %d:%s css_alloc=%p css_free=%p name:id=%d:%s\n",
+ i, cgroup_subsys_name[i], ss->css_alloc, ss->css_free,
+ ss->id, ss->name);
+ WARN(strlen(cgroup_subsys_name[i]) > MAX_CGROUP_TYPE_NAMELEN,
+ "cgroup_subsys_name %s too long\n", cgroup_subsys_name[i]);
+
+ ss->id = i;
+ ss->name = cgroup_subsys_name[i];
if (ss->early_init)
cgroup_init_subsys(ss);
@@ -4798,53 +4172,46 @@ int __init cgroup_init(void)
{
struct cgroup_subsys *ss;
unsigned long key;
- int i, err;
+ int ssid, err;
- err = bdi_init(&cgroup_backing_dev_info);
- if (err)
- return err;
+ BUG_ON(cgroup_init_cftypes(NULL, cgroup_base_files));
- for_each_builtin_subsys(ss, i) {
- if (!ss->early_init)
- cgroup_init_subsys(ss);
- }
-
- /* allocate id for the dummy hierarchy */
+ mutex_lock(&cgroup_tree_mutex);
mutex_lock(&cgroup_mutex);
- mutex_lock(&cgroup_root_mutex);
/* Add init_css_set to the hash table */
key = css_set_hash(init_css_set.subsys);
hash_add(css_set_table, &init_css_set.hlist, key);
- BUG_ON(cgroup_init_root_id(&cgroup_dummy_root, 0, 1));
+ BUG_ON(cgroup_setup_root(&cgrp_dfl_root, 0));
- err = idr_alloc(&cgroup_dummy_root.cgroup_idr, cgroup_dummy_top,
- 0, 1, GFP_KERNEL);
- BUG_ON(err < 0);
-
- mutex_unlock(&cgroup_root_mutex);
mutex_unlock(&cgroup_mutex);
+ mutex_unlock(&cgroup_tree_mutex);
- cgroup_kobj = kobject_create_and_add("cgroup", fs_kobj);
- if (!cgroup_kobj) {
- err = -ENOMEM;
- goto out;
+ for_each_subsys(ss, ssid) {
+ if (!ss->early_init)
+ cgroup_init_subsys(ss);
+
+ /*
+ * cftype registration needs kmalloc and can't be done
+ * during early_init. Register base cftypes separately.
+ */
+ if (ss->base_cftypes)
+ WARN_ON(cgroup_add_cftypes(ss, ss->base_cftypes));
}
+ cgroup_kobj = kobject_create_and_add("cgroup", fs_kobj);
+ if (!cgroup_kobj)
+ return -ENOMEM;
+
err = register_filesystem(&cgroup_fs_type);
if (err < 0) {
kobject_put(cgroup_kobj);
- goto out;
+ return err;
}
proc_create("cgroups", 0, NULL, &proc_cgroupstats_operations);
-
-out:
- if (err)
- bdi_destroy(&cgroup_backing_dev_info);
-
- return err;
+ return 0;
}
static int __init cgroup_wq_init(void)
@@ -4876,12 +4243,6 @@ core_initcall(cgroup_wq_init);
* proc_cgroup_show()
* - Print task's cgroup paths into seq_file, one line for each hierarchy
* - Used for /proc/<pid>/cgroup.
- * - No need to task_lock(tsk) on this tsk->cgroup reference, as it
- * doesn't really matter if tsk->cgroup changes after we read it,
- * and we take cgroup_mutex, keeping cgroup_attach_task() from changing it
- * anyway. No need to check that tsk->cgroup != NULL, thanks to
- * the_top_cgroup_hack in cgroup_exit(), which sets an exiting tasks
- * cgroup to top_cgroup.
*/
/* TODO: Use a proper seq_file iterator */
@@ -4889,12 +4250,12 @@ int proc_cgroup_show(struct seq_file *m, void *v)
{
struct pid *pid;
struct task_struct *tsk;
- char *buf;
+ char *buf, *path;
int retval;
- struct cgroupfs_root *root;
+ struct cgroup_root *root;
retval = -ENOMEM;
- buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ buf = kmalloc(PATH_MAX, GFP_KERNEL);
if (!buf)
goto out;
@@ -4907,29 +4268,36 @@ int proc_cgroup_show(struct seq_file *m, void *v)
retval = 0;
mutex_lock(&cgroup_mutex);
+ down_read(&css_set_rwsem);
- for_each_active_root(root) {
+ for_each_root(root) {
struct cgroup_subsys *ss;
struct cgroup *cgrp;
int ssid, count = 0;
+ if (root == &cgrp_dfl_root && !cgrp_dfl_root_visible)
+ continue;
+
seq_printf(m, "%d:", root->hierarchy_id);
for_each_subsys(ss, ssid)
- if (root->subsys_mask & (1 << ssid))
+ if (root->cgrp.subsys_mask & (1 << ssid))
seq_printf(m, "%s%s", count++ ? "," : "", ss->name);
if (strlen(root->name))
seq_printf(m, "%sname=%s", count ? "," : "",
root->name);
seq_putc(m, ':');
cgrp = task_cgroup_from_root(tsk, root);
- retval = cgroup_path(cgrp, buf, PAGE_SIZE);
- if (retval < 0)
+ path = cgroup_path(cgrp, buf, PATH_MAX);
+ if (!path) {
+ retval = -ENAMETOOLONG;
goto out_unlock;
- seq_puts(m, buf);
+ }
+ seq_puts(m, path);
seq_putc(m, '\n');
}
out_unlock:
+ up_read(&css_set_rwsem);
mutex_unlock(&cgroup_mutex);
put_task_struct(tsk);
out_free:
@@ -4955,7 +4323,7 @@ static int proc_cgroupstats_show(struct seq_file *m, void *v)
for_each_subsys(ss, i)
seq_printf(m, "%s\t%d\t%d\t%d\n",
ss->name, ss->root->hierarchy_id,
- ss->root->number_of_cgroups, !ss->disabled);
+ atomic_read(&ss->root->nr_cgrps), !ss->disabled);
mutex_unlock(&cgroup_mutex);
return 0;
@@ -4974,27 +4342,16 @@ static const struct file_operations proc_cgroupstats_operations = {
};
/**
- * cgroup_fork - attach newly forked task to its parents cgroup.
+ * cgroup_fork - initialize cgroup related fields during copy_process()
* @child: pointer to task_struct of forking parent process.
*
- * Description: A task inherits its parent's cgroup at fork().
- *
- * A pointer to the shared css_set was automatically copied in
- * fork.c by dup_task_struct(). However, we ignore that copy, since
- * it was not made under the protection of RCU or cgroup_mutex, so
- * might no longer be a valid cgroup pointer. cgroup_attach_task() might
- * have already changed current->cgroups, allowing the previously
- * referenced cgroup group to be removed and freed.
- *
- * At the point that cgroup_fork() is called, 'current' is the parent
- * task, and the passed argument 'child' points to the child task.
+ * A task is associated with the init_css_set until cgroup_post_fork()
+ * attaches it to the parent's css_set. Empty cg_list indicates that
+ * @child isn't holding reference to its css_set.
*/
void cgroup_fork(struct task_struct *child)
{
- task_lock(current);
- get_css_set(task_css_set(current));
- child->cgroups = current->cgroups;
- task_unlock(current);
+ RCU_INIT_POINTER(child->cgroups, &init_css_set);
INIT_LIST_HEAD(&child->cg_list);
}
@@ -5014,23 +4371,37 @@ void cgroup_post_fork(struct task_struct *child)
int i;
/*
- * use_task_css_set_links is set to 1 before we walk the tasklist
- * under the tasklist_lock and we read it here after we added the child
- * to the tasklist under the tasklist_lock as well. If the child wasn't
- * yet in the tasklist when we walked through it from
- * cgroup_enable_task_cg_lists(), then use_task_css_set_links value
- * should be visible now due to the paired locking and barriers implied
- * by LOCK/UNLOCK: it is written before the tasklist_lock unlock
- * in cgroup_enable_task_cg_lists() and read here after the tasklist_lock
- * lock on fork.
+ * This may race against cgroup_enable_task_cg_links(). As that
+ * function sets use_task_css_set_links before grabbing
+ * tasklist_lock and we just went through tasklist_lock to add
+ * @child, it's guaranteed that either we see the set
+ * use_task_css_set_links or cgroup_enable_task_cg_lists() sees
+ * @child during its iteration.
+ *
+ * If we won the race, @child is associated with %current's
+ * css_set. Grabbing css_set_rwsem guarantees both that the
+ * association is stable, and, on completion of the parent's
+ * migration, @child is visible in the source of migration or
+ * already in the destination cgroup. This guarantee is necessary
+ * when implementing operations which need to migrate all tasks of
+ * a cgroup to another.
+ *
+ * Note that if we lose to cgroup_enable_task_cg_links(), @child
+ * will remain in init_css_set. This is safe because all tasks are
+ * in the init_css_set before cg_links is enabled and there's no
+ * operation which transfers all tasks out of init_css_set.
*/
if (use_task_css_set_links) {
- write_lock(&css_set_lock);
- task_lock(child);
- if (list_empty(&child->cg_list))
- list_add(&child->cg_list, &task_css_set(child)->tasks);
- task_unlock(child);
- write_unlock(&css_set_lock);
+ struct css_set *cset;
+
+ down_write(&css_set_rwsem);
+ cset = task_css_set(current);
+ if (list_empty(&child->cg_list)) {
+ rcu_assign_pointer(child->cgroups, cset);
+ list_add(&child->cg_list, &cset->tasks);
+ get_css_set(cset);
+ }
+ up_write(&css_set_rwsem);
}
/*
@@ -5039,15 +4410,7 @@ void cgroup_post_fork(struct task_struct *child)
* and addition to css_set.
*/
if (need_forkexit_callback) {
- /*
- * fork/exit callbacks are supported only for builtin
- * subsystems, and the builtin section of the subsys
- * array is immutable, so we don't need to lock the
- * subsys array here. On the other hand, modular section
- * of the array can be freed at module unload, so we
- * can't touch that.
- */
- for_each_builtin_subsys(ss, i)
+ for_each_subsys(ss, i)
if (ss->fork)
ss->fork(child);
}
@@ -5056,7 +4419,6 @@ void cgroup_post_fork(struct task_struct *child)
/**
* cgroup_exit - detach cgroup from exiting task
* @tsk: pointer to task_struct of exiting process
- * @run_callback: run exit callbacks?
*
* Description: Detach cgroup from @tsk and release it.
*
@@ -5066,57 +4428,38 @@ void cgroup_post_fork(struct task_struct *child)
* use notify_on_release cgroups where very high task exit scaling
* is required on large systems.
*
- * the_top_cgroup_hack:
- *
- * Set the exiting tasks cgroup to the root cgroup (top_cgroup).
- *
- * We call cgroup_exit() while the task is still competent to
- * handle notify_on_release(), then leave the task attached to the
- * root cgroup in each hierarchy for the remainder of its exit.
- *
- * To do this properly, we would increment the reference count on
- * top_cgroup, and near the very end of the kernel/exit.c do_exit()
- * code we would add a second cgroup function call, to drop that
- * reference. This would just create an unnecessary hot spot on
- * the top_cgroup reference count, to no avail.
- *
- * Normally, holding a reference to a cgroup without bumping its
- * count is unsafe. The cgroup could go away, or someone could
- * attach us to a different cgroup, decrementing the count on
- * the first cgroup that we never incremented. But in this case,
- * top_cgroup isn't going away, and either task has PF_EXITING set,
- * which wards off any cgroup_attach_task() attempts, or task is a failed
- * fork, never visible to cgroup_attach_task.
+ * We set the exiting tasks cgroup to the root cgroup (top_cgroup). We
+ * call cgroup_exit() while the task is still competent to handle
+ * notify_on_release(), then leave the task attached to the root cgroup in
+ * each hierarchy for the remainder of its exit. No need to bother with
+ * init_css_set refcnting. init_css_set never goes away and we can't race
+ * with migration path - PF_EXITING is visible to migration path.
*/
-void cgroup_exit(struct task_struct *tsk, int run_callbacks)
+void cgroup_exit(struct task_struct *tsk)
{
struct cgroup_subsys *ss;
struct css_set *cset;
+ bool put_cset = false;
int i;
/*
- * Unlink from the css_set task list if necessary.
- * Optimistically check cg_list before taking
- * css_set_lock
+ * Unlink from @tsk from its css_set. As migration path can't race
+ * with us, we can check cg_list without grabbing css_set_rwsem.
*/
if (!list_empty(&tsk->cg_list)) {
- write_lock(&css_set_lock);
- if (!list_empty(&tsk->cg_list))
- list_del_init(&tsk->cg_list);
- write_unlock(&css_set_lock);
+ down_write(&css_set_rwsem);
+ list_del_init(&tsk->cg_list);
+ up_write(&css_set_rwsem);
+ put_cset = true;
}
/* Reassign the task to the init_css_set. */
- task_lock(tsk);
cset = task_css_set(tsk);
RCU_INIT_POINTER(tsk->cgroups, &init_css_set);
- if (run_callbacks && need_forkexit_callback) {
- /*
- * fork/exit callbacks are supported only for builtin
- * subsystems, see cgroup_post_fork() for details.
- */
- for_each_builtin_subsys(ss, i) {
+ if (need_forkexit_callback) {
+ /* see cgroup_post_fork() for details */
+ for_each_subsys(ss, i) {
if (ss->exit) {
struct cgroup_subsys_state *old_css = cset->subsys[i];
struct cgroup_subsys_state *css = task_css(tsk, i);
@@ -5125,9 +4468,9 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks)
}
}
}
- task_unlock(tsk);
- put_css_set_taskexit(cset);
+ if (put_cset)
+ put_css_set(cset, true);
}
static void check_for_release(struct cgroup *cgrp)
@@ -5184,16 +4527,17 @@ static void cgroup_release_agent(struct work_struct *work)
while (!list_empty(&release_list)) {
char *argv[3], *envp[3];
int i;
- char *pathbuf = NULL, *agentbuf = NULL;
+ char *pathbuf = NULL, *agentbuf = NULL, *path;
struct cgroup *cgrp = list_entry(release_list.next,
struct cgroup,
release_list);
list_del_init(&cgrp->release_list);
raw_spin_unlock(&release_list_lock);
- pathbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
if (!pathbuf)
goto continue_free;
- if (cgroup_path(cgrp, pathbuf, PAGE_SIZE) < 0)
+ path = cgroup_path(cgrp, pathbuf, PATH_MAX);
+ if (!path)
goto continue_free;
agentbuf = kstrdup(cgrp->root->release_agent_path, GFP_KERNEL);
if (!agentbuf)
@@ -5201,7 +4545,7 @@ static void cgroup_release_agent(struct work_struct *work)
i = 0;
argv[i++] = agentbuf;
- argv[i++] = pathbuf;
+ argv[i++] = path;
argv[i] = NULL;
i = 0;
@@ -5235,11 +4579,7 @@ static int __init cgroup_disable(char *str)
if (!*token)
continue;
- /*
- * cgroup_disable, being at boot time, can't know about
- * module subsystems, so we don't worry about them.
- */
- for_each_builtin_subsys(ss, i) {
+ for_each_subsys(ss, i) {
if (!strcmp(token, ss->name)) {
ss->disabled = 1;
printk(KERN_INFO "Disabling %s control group"
@@ -5253,28 +4593,42 @@ static int __init cgroup_disable(char *str)
__setup("cgroup_disable=", cgroup_disable);
/**
- * css_from_dir - get corresponding css from the dentry of a cgroup dir
+ * css_tryget_from_dir - get corresponding css from the dentry of a cgroup dir
* @dentry: directory dentry of interest
* @ss: subsystem of interest
*
- * Must be called under cgroup_mutex or RCU read lock. The caller is
- * responsible for pinning the returned css if it needs to be accessed
- * outside the critical section.
+ * If @dentry is a directory for a cgroup which has @ss enabled on it, try
+ * to get the corresponding css and return it. If such css doesn't exist
+ * or can't be pinned, an ERR_PTR value is returned.
*/
-struct cgroup_subsys_state *css_from_dir(struct dentry *dentry,
- struct cgroup_subsys *ss)
+struct cgroup_subsys_state *css_tryget_from_dir(struct dentry *dentry,
+ struct cgroup_subsys *ss)
{
+ struct kernfs_node *kn = kernfs_node_from_dentry(dentry);
+ struct cgroup_subsys_state *css = NULL;
struct cgroup *cgrp;
- cgroup_assert_mutex_or_rcu_locked();
-
/* is @dentry a cgroup dir? */
- if (!dentry->d_inode ||
- dentry->d_inode->i_op != &cgroup_dir_inode_operations)
+ if (dentry->d_sb->s_type != &cgroup_fs_type || !kn ||
+ kernfs_type(kn) != KERNFS_DIR)
return ERR_PTR(-EBADF);
- cgrp = __d_cgrp(dentry);
- return cgroup_css(cgrp, ss) ?: ERR_PTR(-ENOENT);
+ rcu_read_lock();
+
+ /*
+ * This path doesn't originate from kernfs and @kn could already
+ * have been or be removed at any point. @kn->priv is RCU
+ * protected for this access. See destroy_locked() for details.
+ */
+ cgrp = rcu_dereference(kn->priv);
+ if (cgrp)
+ css = cgroup_css(cgrp, ss);
+
+ if (!css || !css_tryget(css))
+ css = ERR_PTR(-ENOENT);
+
+ rcu_read_unlock();
+ return css;
}
/**
@@ -5289,7 +4643,7 @@ struct cgroup_subsys_state *css_from_id(int id, struct cgroup_subsys *ss)
{
struct cgroup *cgrp;
- cgroup_assert_mutex_or_rcu_locked();
+ cgroup_assert_mutexes_or_rcu_locked();
cgrp = idr_find(&ss->root->cgroup_idr, id);
if (cgrp)
@@ -5341,23 +4695,25 @@ static int current_css_set_cg_links_read(struct seq_file *seq, void *v)
{
struct cgrp_cset_link *link;
struct css_set *cset;
+ char *name_buf;
- read_lock(&css_set_lock);
+ name_buf = kmalloc(NAME_MAX + 1, GFP_KERNEL);
+ if (!name_buf)
+ return -ENOMEM;
+
+ down_read(&css_set_rwsem);
rcu_read_lock();
cset = rcu_dereference(current->cgroups);
list_for_each_entry(link, &cset->cgrp_links, cgrp_link) {
struct cgroup *c = link->cgrp;
- const char *name;
- if (c->dentry)
- name = c->dentry->d_name.name;
- else
- name = "?";
+ cgroup_name(c, name_buf, NAME_MAX + 1);
seq_printf(seq, "Root %d group %s\n",
- c->root->hierarchy_id, name);
+ c->root->hierarchy_id, name_buf);
}
rcu_read_unlock();
- read_unlock(&css_set_lock);
+ up_read(&css_set_rwsem);
+ kfree(name_buf);
return 0;
}
@@ -5367,23 +4723,30 @@ static int cgroup_css_links_read(struct seq_file *seq, void *v)
struct cgroup_subsys_state *css = seq_css(seq);
struct cgrp_cset_link *link;
- read_lock(&css_set_lock);
+ down_read(&css_set_rwsem);
list_for_each_entry(link, &css->cgroup->cset_links, cset_link) {
struct css_set *cset = link->cset;
struct task_struct *task;
int count = 0;
+
seq_printf(seq, "css_set %p\n", cset);
+
list_for_each_entry(task, &cset->tasks, cg_list) {
- if (count++ > MAX_TASKS_SHOWN_PER_CSS) {
- seq_puts(seq, " ...\n");
- break;
- } else {
- seq_printf(seq, " task %d\n",
- task_pid_vnr(task));
- }
+ if (count++ > MAX_TASKS_SHOWN_PER_CSS)
+ goto overflow;
+ seq_printf(seq, " task %d\n", task_pid_vnr(task));
+ }
+
+ list_for_each_entry(task, &cset->mg_tasks, cg_list) {
+ if (count++ > MAX_TASKS_SHOWN_PER_CSS)
+ goto overflow;
+ seq_printf(seq, " task %d\n", task_pid_vnr(task));
}
+ continue;
+ overflow:
+ seq_puts(seq, " ...\n");
}
- read_unlock(&css_set_lock);
+ up_read(&css_set_rwsem);
return 0;
}
@@ -5426,11 +4789,9 @@ static struct cftype debug_files[] = {
{ } /* terminate */
};
-struct cgroup_subsys debug_subsys = {
- .name = "debug",
+struct cgroup_subsys debug_cgrp_subsys = {
.css_alloc = debug_css_alloc,
.css_free = debug_css_free,
- .subsys_id = debug_subsys_id,
.base_cftypes = debug_files,
};
#endif /* CONFIG_CGROUP_DEBUG */
diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c
index 6c3154e477f6..2bc4a2256444 100644
--- a/kernel/cgroup_freezer.c
+++ b/kernel/cgroup_freezer.c
@@ -52,7 +52,7 @@ static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
static inline struct freezer *task_freezer(struct task_struct *task)
{
- return css_freezer(task_css(task, freezer_subsys_id));
+ return css_freezer(task_css(task, freezer_cgrp_id));
}
static struct freezer *parent_freezer(struct freezer *freezer)
@@ -84,8 +84,6 @@ static const char *freezer_state_strs(unsigned int state)
return "THAWED";
};
-struct cgroup_subsys freezer_subsys;
-
static struct cgroup_subsys_state *
freezer_css_alloc(struct cgroup_subsys_state *parent_css)
{
@@ -189,7 +187,7 @@ 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, new_css, tset) {
+ cgroup_taskset_for_each(task, tset) {
if (!(freezer->state & CGROUP_FREEZING)) {
__thaw_task(task);
} else {
@@ -216,6 +214,16 @@ static void freezer_attach(struct cgroup_subsys_state *new_css,
}
}
+/**
+ * freezer_fork - cgroup post fork callback
+ * @task: a task which has just been forked
+ *
+ * @task has just been created and should conform to the current state of
+ * the cgroup_freezer it belongs to. This function may race against
+ * freezer_attach(). Losing to freezer_attach() means that we don't have
+ * to do anything as freezer_attach() will put @task into the appropriate
+ * state.
+ */
static void freezer_fork(struct task_struct *task)
{
struct freezer *freezer;
@@ -224,14 +232,26 @@ static void freezer_fork(struct task_struct *task)
freezer = task_freezer(task);
/*
- * The root cgroup is non-freezable, so we can skip the
- * following check.
+ * The root cgroup is non-freezable, so we can skip locking the
+ * freezer. This is safe regardless of race with task migration.
+ * If we didn't race or won, skipping is obviously the right thing
+ * to do. If we lost and root is the new cgroup, noop is still the
+ * right thing to do.
*/
if (!parent_freezer(freezer))
goto out;
+ /*
+ * Grab @freezer->lock and freeze @task after verifying @task still
+ * belongs to @freezer and it's freezing. The former is for the
+ * case where we have raced against task migration and lost and
+ * @task is already in a different cgroup which may not be frozen.
+ * This isn't strictly necessary as freeze_task() is allowed to be
+ * called spuriously but let's do it anyway for, if nothing else,
+ * documentation.
+ */
spin_lock_irq(&freezer->lock);
- if (freezer->state & CGROUP_FREEZING)
+ if (freezer == task_freezer(task) && (freezer->state & CGROUP_FREEZING))
freeze_task(task);
spin_unlock_irq(&freezer->lock);
out:
@@ -422,7 +442,7 @@ static void freezer_change_state(struct freezer *freezer, bool freeze)
}
static int freezer_write(struct cgroup_subsys_state *css, struct cftype *cft,
- const char *buffer)
+ char *buffer)
{
bool freeze;
@@ -473,13 +493,11 @@ static struct cftype files[] = {
{ } /* terminate */
};
-struct cgroup_subsys freezer_subsys = {
- .name = "freezer",
+struct cgroup_subsys freezer_cgrp_subsys = {
.css_alloc = freezer_css_alloc,
.css_online = freezer_css_online,
.css_offline = freezer_css_offline,
.css_free = freezer_css_free,
- .subsys_id = freezer_subsys_id,
.attach = freezer_attach,
.fork = freezer_fork,
.base_cftypes = files,
diff --git a/kernel/cpu.c b/kernel/cpu.c
index deff2e693766..a9e710eef0e2 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -19,6 +19,7 @@
#include <linux/mutex.h>
#include <linux/gfp.h>
#include <linux/suspend.h>
+#include <linux/lockdep.h>
#include "smpboot.h"
@@ -27,18 +28,23 @@
static DEFINE_MUTEX(cpu_add_remove_lock);
/*
- * The following two API's must be used when attempting
- * to serialize the updates to cpu_online_mask, cpu_present_mask.
+ * The following two APIs (cpu_maps_update_begin/done) must be used when
+ * attempting to serialize the updates to cpu_online_mask & cpu_present_mask.
+ * The APIs cpu_notifier_register_begin/done() must be used to protect CPU
+ * hotplug callback (un)registration performed using __register_cpu_notifier()
+ * or __unregister_cpu_notifier().
*/
void cpu_maps_update_begin(void)
{
mutex_lock(&cpu_add_remove_lock);
}
+EXPORT_SYMBOL(cpu_notifier_register_begin);
void cpu_maps_update_done(void)
{
mutex_unlock(&cpu_add_remove_lock);
}
+EXPORT_SYMBOL(cpu_notifier_register_done);
static RAW_NOTIFIER_HEAD(cpu_chain);
@@ -57,17 +63,30 @@ static struct {
* an ongoing cpu hotplug operation.
*/
int refcount;
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ struct lockdep_map dep_map;
+#endif
} cpu_hotplug = {
.active_writer = NULL,
.lock = __MUTEX_INITIALIZER(cpu_hotplug.lock),
.refcount = 0,
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ .dep_map = {.name = "cpu_hotplug.lock" },
+#endif
};
+/* Lockdep annotations for get/put_online_cpus() and cpu_hotplug_begin/end() */
+#define cpuhp_lock_acquire_read() lock_map_acquire_read(&cpu_hotplug.dep_map)
+#define cpuhp_lock_acquire() lock_map_acquire(&cpu_hotplug.dep_map)
+#define cpuhp_lock_release() lock_map_release(&cpu_hotplug.dep_map)
+
void get_online_cpus(void)
{
might_sleep();
if (cpu_hotplug.active_writer == current)
return;
+ cpuhp_lock_acquire_read();
mutex_lock(&cpu_hotplug.lock);
cpu_hotplug.refcount++;
mutex_unlock(&cpu_hotplug.lock);
@@ -87,6 +106,7 @@ void put_online_cpus(void)
if (!--cpu_hotplug.refcount && unlikely(cpu_hotplug.active_writer))
wake_up_process(cpu_hotplug.active_writer);
mutex_unlock(&cpu_hotplug.lock);
+ cpuhp_lock_release();
}
EXPORT_SYMBOL_GPL(put_online_cpus);
@@ -117,6 +137,7 @@ void cpu_hotplug_begin(void)
{
cpu_hotplug.active_writer = current;
+ cpuhp_lock_acquire();
for (;;) {
mutex_lock(&cpu_hotplug.lock);
if (likely(!cpu_hotplug.refcount))
@@ -131,6 +152,7 @@ void cpu_hotplug_done(void)
{
cpu_hotplug.active_writer = NULL;
mutex_unlock(&cpu_hotplug.lock);
+ cpuhp_lock_release();
}
/*
@@ -166,6 +188,11 @@ int __ref register_cpu_notifier(struct notifier_block *nb)
return ret;
}
+int __ref __register_cpu_notifier(struct notifier_block *nb)
+{
+ return raw_notifier_chain_register(&cpu_chain, nb);
+}
+
static int __cpu_notify(unsigned long val, void *v, int nr_to_call,
int *nr_calls)
{
@@ -189,6 +216,7 @@ static void cpu_notify_nofail(unsigned long val, void *v)
BUG_ON(cpu_notify(val, v));
}
EXPORT_SYMBOL(register_cpu_notifier);
+EXPORT_SYMBOL(__register_cpu_notifier);
void __ref unregister_cpu_notifier(struct notifier_block *nb)
{
@@ -198,6 +226,12 @@ void __ref unregister_cpu_notifier(struct notifier_block *nb)
}
EXPORT_SYMBOL(unregister_cpu_notifier);
+void __ref __unregister_cpu_notifier(struct notifier_block *nb)
+{
+ raw_notifier_chain_unregister(&cpu_chain, nb);
+}
+EXPORT_SYMBOL(__unregister_cpu_notifier);
+
/**
* clear_tasks_mm_cpumask - Safely clear tasks' mm_cpumask for a CPU
* @cpu: a CPU id
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index e6b1b66afe52..3d54c418bd06 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -119,7 +119,7 @@ static inline struct cpuset *css_cs(struct cgroup_subsys_state *css)
/* Retrieve the cpuset for a task */
static inline struct cpuset *task_cs(struct task_struct *task)
{
- return css_cs(task_css(task, cpuset_subsys_id));
+ return css_cs(task_css(task, cpuset_cgrp_id));
}
static inline struct cpuset *parent_cs(struct cpuset *cs)
@@ -467,7 +467,7 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial)
* be changed to have empty cpus_allowed or mems_allowed.
*/
ret = -ENOSPC;
- if ((cgroup_task_count(cur->css.cgroup) || cur->attach_in_progress)) {
+ if ((cgroup_has_tasks(cur->css.cgroup) || cur->attach_in_progress)) {
if (!cpumask_empty(cur->cpus_allowed) &&
cpumask_empty(trial->cpus_allowed))
goto out;
@@ -829,55 +829,36 @@ static struct cpuset *effective_nodemask_cpuset(struct cpuset *cs)
}
/**
- * cpuset_change_cpumask - make a task's cpus_allowed the same as its cpuset's
- * @tsk: task to test
- * @data: cpuset to @tsk belongs to
- *
- * Called by css_scan_tasks() for each task in a cgroup whose cpus_allowed
- * mask needs to be changed.
- *
- * We don't need to re-check for the cgroup/cpuset membership, since we're
- * holding cpuset_mutex at this point.
- */
-static void cpuset_change_cpumask(struct task_struct *tsk, void *data)
-{
- struct cpuset *cs = data;
- struct cpuset *cpus_cs = effective_cpumask_cpuset(cs);
-
- set_cpus_allowed_ptr(tsk, cpus_cs->cpus_allowed);
-}
-
-/**
* update_tasks_cpumask - Update the cpumasks of tasks in the cpuset.
* @cs: the cpuset in which each task's cpus_allowed mask needs to be changed
- * @heap: if NULL, defer allocating heap memory to css_scan_tasks()
- *
- * Called with cpuset_mutex held
*
- * The css_scan_tasks() function will scan all the tasks in a cgroup,
- * calling callback functions for each.
- *
- * No return value. It's guaranteed that css_scan_tasks() always returns 0
- * if @heap != NULL.
+ * Iterate through each task of @cs updating its cpus_allowed to the
+ * effective cpuset's. As this function is called with cpuset_mutex held,
+ * cpuset membership stays stable.
*/
-static void update_tasks_cpumask(struct cpuset *cs, struct ptr_heap *heap)
+static void update_tasks_cpumask(struct cpuset *cs)
{
- css_scan_tasks(&cs->css, NULL, cpuset_change_cpumask, cs, heap);
+ struct cpuset *cpus_cs = effective_cpumask_cpuset(cs);
+ struct css_task_iter it;
+ struct task_struct *task;
+
+ css_task_iter_start(&cs->css, &it);
+ while ((task = css_task_iter_next(&it)))
+ set_cpus_allowed_ptr(task, cpus_cs->cpus_allowed);
+ css_task_iter_end(&it);
}
/*
* update_tasks_cpumask_hier - Update the cpumasks of tasks in the hierarchy.
* @root_cs: the root cpuset of the hierarchy
* @update_root: update root cpuset or not?
- * @heap: the heap used by css_scan_tasks()
*
* This will update cpumasks of tasks in @root_cs and all other empty cpusets
* which take on cpumask of @root_cs.
*
* Called with cpuset_mutex held
*/
-static void update_tasks_cpumask_hier(struct cpuset *root_cs,
- bool update_root, struct ptr_heap *heap)
+static void update_tasks_cpumask_hier(struct cpuset *root_cs, bool update_root)
{
struct cpuset *cp;
struct cgroup_subsys_state *pos_css;
@@ -898,7 +879,7 @@ static void update_tasks_cpumask_hier(struct cpuset *root_cs,
continue;
rcu_read_unlock();
- update_tasks_cpumask(cp, heap);
+ update_tasks_cpumask(cp);
rcu_read_lock();
css_put(&cp->css);
@@ -914,7 +895,6 @@ static void update_tasks_cpumask_hier(struct cpuset *root_cs,
static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,
const char *buf)
{
- struct ptr_heap heap;
int retval;
int is_load_balanced;
@@ -947,19 +927,13 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,
if (retval < 0)
return retval;
- retval = heap_init(&heap, PAGE_SIZE, GFP_KERNEL, NULL);
- if (retval)
- return retval;
-
is_load_balanced = is_sched_load_balance(trialcs);
mutex_lock(&callback_mutex);
cpumask_copy(cs->cpus_allowed, trialcs->cpus_allowed);
mutex_unlock(&callback_mutex);
- update_tasks_cpumask_hier(cs, true, &heap);
-
- heap_free(&heap);
+ update_tasks_cpumask_hier(cs, true);
if (is_load_balanced)
rebuild_sched_domains_locked();
@@ -1022,7 +996,7 @@ static void cpuset_change_task_nodemask(struct task_struct *tsk,
task_lock(tsk);
/*
* Determine if a loop is necessary if another thread is doing
- * get_mems_allowed(). If at least one node remains unchanged and
+ * read_mems_allowed_begin(). If at least one node remains unchanged and
* tsk does not have a mempolicy, then an empty nodemask will not be
* possible when mems_allowed is larger than a word.
*/
@@ -1048,53 +1022,22 @@ static void cpuset_change_task_nodemask(struct task_struct *tsk,
task_unlock(tsk);
}
-struct cpuset_change_nodemask_arg {
- struct cpuset *cs;
- nodemask_t *newmems;
-};
-
-/*
- * Update task's mems_allowed and rebind its mempolicy and vmas' mempolicy
- * of it to cpuset's new mems_allowed, and migrate pages to new nodes if
- * memory_migrate flag is set. Called with cpuset_mutex held.
- */
-static void cpuset_change_nodemask(struct task_struct *p, void *data)
-{
- struct cpuset_change_nodemask_arg *arg = data;
- struct cpuset *cs = arg->cs;
- struct mm_struct *mm;
- int migrate;
-
- cpuset_change_task_nodemask(p, arg->newmems);
-
- mm = get_task_mm(p);
- if (!mm)
- return;
-
- migrate = is_memory_migrate(cs);
-
- mpol_rebind_mm(mm, &cs->mems_allowed);
- if (migrate)
- cpuset_migrate_mm(mm, &cs->old_mems_allowed, arg->newmems);
- mmput(mm);
-}
-
static void *cpuset_being_rebound;
/**
* update_tasks_nodemask - Update the nodemasks of tasks in the cpuset.
* @cs: the cpuset in which each task's mems_allowed mask needs to be changed
- * @heap: if NULL, defer allocating heap memory to css_scan_tasks()
*
- * Called with cpuset_mutex held. No return value. It's guaranteed that
- * css_scan_tasks() always returns 0 if @heap != NULL.
+ * Iterate through each task of @cs updating its mems_allowed to the
+ * effective cpuset's. As this function is called with cpuset_mutex held,
+ * cpuset membership stays stable.
*/
-static void update_tasks_nodemask(struct cpuset *cs, struct ptr_heap *heap)
+static void update_tasks_nodemask(struct cpuset *cs)
{
static nodemask_t newmems; /* protected by cpuset_mutex */
struct cpuset *mems_cs = effective_nodemask_cpuset(cs);
- struct cpuset_change_nodemask_arg arg = { .cs = cs,
- .newmems = &newmems };
+ struct css_task_iter it;
+ struct task_struct *task;
cpuset_being_rebound = cs; /* causes mpol_dup() rebind */
@@ -1110,7 +1053,25 @@ static void update_tasks_nodemask(struct cpuset *cs, struct ptr_heap *heap)
* It's ok if we rebind the same mm twice; mpol_rebind_mm()
* is idempotent. Also migrate pages in each mm to new nodes.
*/
- css_scan_tasks(&cs->css, NULL, cpuset_change_nodemask, &arg, heap);
+ css_task_iter_start(&cs->css, &it);
+ while ((task = css_task_iter_next(&it))) {
+ struct mm_struct *mm;
+ bool migrate;
+
+ cpuset_change_task_nodemask(task, &newmems);
+
+ mm = get_task_mm(task);
+ if (!mm)
+ continue;
+
+ migrate = is_memory_migrate(cs);
+
+ mpol_rebind_mm(mm, &cs->mems_allowed);
+ if (migrate)
+ cpuset_migrate_mm(mm, &cs->old_mems_allowed, &newmems);
+ mmput(mm);
+ }
+ css_task_iter_end(&it);
/*
* All the tasks' nodemasks have been updated, update
@@ -1126,15 +1087,13 @@ static void update_tasks_nodemask(struct cpuset *cs, struct ptr_heap *heap)
* update_tasks_nodemask_hier - Update the nodemasks of tasks in the hierarchy.
* @cs: the root cpuset of the hierarchy
* @update_root: update the root cpuset or not?
- * @heap: the heap used by css_scan_tasks()
*
* This will update nodemasks of tasks in @root_cs and all other empty cpusets
* which take on nodemask of @root_cs.
*
* Called with cpuset_mutex held
*/
-static void update_tasks_nodemask_hier(struct cpuset *root_cs,
- bool update_root, struct ptr_heap *heap)
+static void update_tasks_nodemask_hier(struct cpuset *root_cs, bool update_root)
{
struct cpuset *cp;
struct cgroup_subsys_state *pos_css;
@@ -1155,7 +1114,7 @@ static void update_tasks_nodemask_hier(struct cpuset *root_cs,
continue;
rcu_read_unlock();
- update_tasks_nodemask(cp, heap);
+ update_tasks_nodemask(cp);
rcu_read_lock();
css_put(&cp->css);
@@ -1180,7 +1139,6 @@ static int update_nodemask(struct cpuset *cs, struct cpuset *trialcs,
const char *buf)
{
int retval;
- struct ptr_heap heap;
/*
* top_cpuset.mems_allowed tracks node_stats[N_MEMORY];
@@ -1219,17 +1177,11 @@ static int update_nodemask(struct cpuset *cs, struct cpuset *trialcs,
if (retval < 0)
goto done;
- retval = heap_init(&heap, PAGE_SIZE, GFP_KERNEL, NULL);
- if (retval < 0)
- goto done;
-
mutex_lock(&callback_mutex);
cs->mems_allowed = trialcs->mems_allowed;
mutex_unlock(&callback_mutex);
- update_tasks_nodemask_hier(cs, true, &heap);
-
- heap_free(&heap);
+ update_tasks_nodemask_hier(cs, true);
done:
return retval;
}
@@ -1257,38 +1209,22 @@ static int update_relax_domain_level(struct cpuset *cs, s64 val)
}
/**
- * cpuset_change_flag - make a task's spread flags the same as its cpuset's
- * @tsk: task to be updated
- * @data: cpuset to @tsk belongs to
- *
- * Called by css_scan_tasks() for each task in a cgroup.
- *
- * We don't need to re-check for the cgroup/cpuset membership, since we're
- * holding cpuset_mutex at this point.
- */
-static void cpuset_change_flag(struct task_struct *tsk, void *data)
-{
- struct cpuset *cs = data;
-
- cpuset_update_task_spread_flag(cs, tsk);
-}
-
-/**
* update_tasks_flags - update the spread flags of tasks in the cpuset.
* @cs: the cpuset in which each task's spread flags needs to be changed
- * @heap: if NULL, defer allocating heap memory to css_scan_tasks()
- *
- * Called with cpuset_mutex held
*
- * The css_scan_tasks() function will scan all the tasks in a cgroup,
- * calling callback functions for each.
- *
- * No return value. It's guaranteed that css_scan_tasks() always returns 0
- * if @heap != NULL.
+ * Iterate through each task of @cs updating its spread flags. As this
+ * function is called with cpuset_mutex held, cpuset membership stays
+ * stable.
*/
-static void update_tasks_flags(struct cpuset *cs, struct ptr_heap *heap)
+static void update_tasks_flags(struct cpuset *cs)
{
- css_scan_tasks(&cs->css, NULL, cpuset_change_flag, cs, heap);
+ struct css_task_iter it;
+ struct task_struct *task;
+
+ css_task_iter_start(&cs->css, &it);
+ while ((task = css_task_iter_next(&it)))
+ cpuset_update_task_spread_flag(cs, task);
+ css_task_iter_end(&it);
}
/*
@@ -1306,7 +1242,6 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
struct cpuset *trialcs;
int balance_flag_changed;
int spread_flag_changed;
- struct ptr_heap heap;
int err;
trialcs = alloc_trial_cpuset(cs);
@@ -1322,10 +1257,6 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
if (err < 0)
goto out;
- err = heap_init(&heap, PAGE_SIZE, GFP_KERNEL, NULL);
- if (err < 0)
- goto out;
-
balance_flag_changed = (is_sched_load_balance(cs) !=
is_sched_load_balance(trialcs));
@@ -1340,8 +1271,7 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
rebuild_sched_domains_locked();
if (spread_flag_changed)
- update_tasks_flags(cs, &heap);
- heap_free(&heap);
+ update_tasks_flags(cs);
out:
free_trial_cpuset(trialcs);
return err;
@@ -1445,6 +1375,8 @@ static int fmeter_getrate(struct fmeter *fmp)
return val;
}
+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)
@@ -1453,6 +1385,9 @@ static int cpuset_can_attach(struct cgroup_subsys_state *css,
struct task_struct *task;
int ret;
+ /* used later by cpuset_attach() */
+ cpuset_attach_old_cs = task_cs(cgroup_taskset_first(tset));
+
mutex_lock(&cpuset_mutex);
/*
@@ -1464,7 +1399,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, css, tset) {
+ cgroup_taskset_for_each(task, tset) {
/*
* Kthreads which disallow setaffinity shouldn't be moved
* to a new cpuset; we don't want to change their cpu
@@ -1516,10 +1451,8 @@ static void cpuset_attach(struct cgroup_subsys_state *css,
struct mm_struct *mm;
struct task_struct *task;
struct task_struct *leader = cgroup_taskset_first(tset);
- struct cgroup_subsys_state *oldcss = cgroup_taskset_cur_css(tset,
- cpuset_subsys_id);
struct cpuset *cs = css_cs(css);
- struct cpuset *oldcs = css_cs(oldcss);
+ struct cpuset *oldcs = cpuset_attach_old_cs;
struct cpuset *cpus_cs = effective_cpumask_cpuset(cs);
struct cpuset *mems_cs = effective_nodemask_cpuset(cs);
@@ -1533,7 +1466,7 @@ static void cpuset_attach(struct cgroup_subsys_state *css,
guarantee_online_mems(mems_cs, &cpuset_attach_nodemask_to);
- cgroup_taskset_for_each(task, css, tset) {
+ cgroup_taskset_for_each(task, tset) {
/*
* can_attach beforehand should guarantee that this doesn't
* fail. TODO: have a better way to handle failure here
@@ -1673,7 +1606,7 @@ out_unlock:
* Common handling for a write to a "cpus" or "mems" file.
*/
static int cpuset_write_resmask(struct cgroup_subsys_state *css,
- struct cftype *cft, const char *buf)
+ struct cftype *cft, char *buf)
{
struct cpuset *cs = css_cs(css);
struct cpuset *trialcs;
@@ -2020,8 +1953,7 @@ static void cpuset_css_free(struct cgroup_subsys_state *css)
kfree(cs);
}
-struct cgroup_subsys cpuset_subsys = {
- .name = "cpuset",
+struct cgroup_subsys cpuset_cgrp_subsys = {
.css_alloc = cpuset_css_alloc,
.css_online = cpuset_css_online,
.css_offline = cpuset_css_offline,
@@ -2029,7 +1961,6 @@ struct cgroup_subsys cpuset_subsys = {
.can_attach = cpuset_can_attach,
.cancel_attach = cpuset_cancel_attach,
.attach = cpuset_attach,
- .subsys_id = cpuset_subsys_id,
.base_cftypes = files,
.early_init = 1,
};
@@ -2086,10 +2017,9 @@ static void remove_tasks_in_empty_cpuset(struct cpuset *cs)
parent = parent_cs(parent);
if (cgroup_transfer_tasks(parent->css.cgroup, cs->css.cgroup)) {
- rcu_read_lock();
- printk(KERN_ERR "cpuset: failed to transfer tasks out of empty cpuset %s\n",
- cgroup_name(cs->css.cgroup));
- rcu_read_unlock();
+ printk(KERN_ERR "cpuset: failed to transfer tasks out of empty cpuset ");
+ pr_cont_cgroup_name(cs->css.cgroup);
+ pr_cont("\n");
}
}
@@ -2137,7 +2067,7 @@ retry:
*/
if ((sane && cpumask_empty(cs->cpus_allowed)) ||
(!cpumask_empty(&off_cpus) && !cpumask_empty(cs->cpus_allowed)))
- update_tasks_cpumask(cs, NULL);
+ update_tasks_cpumask(cs);
mutex_lock(&callback_mutex);
nodes_andnot(cs->mems_allowed, cs->mems_allowed, off_mems);
@@ -2151,7 +2081,7 @@ retry:
*/
if ((sane && nodes_empty(cs->mems_allowed)) ||
(!nodes_empty(off_mems) && !nodes_empty(cs->mems_allowed)))
- update_tasks_nodemask(cs, NULL);
+ update_tasks_nodemask(cs);
is_empty = cpumask_empty(cs->cpus_allowed) ||
nodes_empty(cs->mems_allowed);
@@ -2213,7 +2143,7 @@ static void cpuset_hotplug_workfn(struct work_struct *work)
mutex_lock(&callback_mutex);
top_cpuset.mems_allowed = new_mems;
mutex_unlock(&callback_mutex);
- update_tasks_nodemask(&top_cpuset, NULL);
+ update_tasks_nodemask(&top_cpuset);
}
mutex_unlock(&cpuset_mutex);
@@ -2305,10 +2235,10 @@ void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)
struct cpuset *cpus_cs;
mutex_lock(&callback_mutex);
- task_lock(tsk);
+ rcu_read_lock();
cpus_cs = effective_cpumask_cpuset(task_cs(tsk));
guarantee_online_cpus(cpus_cs, pmask);
- task_unlock(tsk);
+ rcu_read_unlock();
mutex_unlock(&callback_mutex);
}
@@ -2361,10 +2291,10 @@ nodemask_t cpuset_mems_allowed(struct task_struct *tsk)
nodemask_t mask;
mutex_lock(&callback_mutex);
- task_lock(tsk);
+ rcu_read_lock();
mems_cs = effective_nodemask_cpuset(task_cs(tsk));
guarantee_online_mems(mems_cs, &mask);
- task_unlock(tsk);
+ rcu_read_unlock();
mutex_unlock(&callback_mutex);
return mask;
@@ -2480,10 +2410,10 @@ int __cpuset_node_allowed_softwall(int node, gfp_t gfp_mask)
/* Not hardwall and node outside mems_allowed: scan up cpusets */
mutex_lock(&callback_mutex);
- task_lock(current);
+ rcu_read_lock();
cs = nearest_hardwall_ancestor(task_cs(current));
allowed = node_isset(node, cs->mems_allowed);
- task_unlock(current);
+ rcu_read_unlock();
mutex_unlock(&callback_mutex);
return allowed;
@@ -2609,27 +2539,27 @@ int cpuset_mems_allowed_intersects(const struct task_struct *tsk1,
* @task: pointer to task_struct of some task.
*
* Description: Prints @task's name, cpuset name, and cached copy of its
- * mems_allowed to the kernel log. Must hold task_lock(task) to allow
- * dereferencing task_cs(task).
+ * mems_allowed to the kernel log.
*/
void cpuset_print_task_mems_allowed(struct task_struct *tsk)
{
/* Statically allocated to prevent using excess stack. */
static char cpuset_nodelist[CPUSET_NODELIST_LEN];
static DEFINE_SPINLOCK(cpuset_buffer_lock);
+ struct cgroup *cgrp;
- struct cgroup *cgrp = task_cs(tsk)->css.cgroup;
-
- rcu_read_lock();
spin_lock(&cpuset_buffer_lock);
+ rcu_read_lock();
+ cgrp = task_cs(tsk)->css.cgroup;
nodelist_scnprintf(cpuset_nodelist, CPUSET_NODELIST_LEN,
tsk->mems_allowed);
- printk(KERN_INFO "%s cpuset=%s mems_allowed=%s\n",
- tsk->comm, cgroup_name(cgrp), cpuset_nodelist);
+ printk(KERN_INFO "%s cpuset=", tsk->comm);
+ pr_cont_cgroup_name(cgrp);
+ pr_cont(" mems_allowed=%s\n", cpuset_nodelist);
- spin_unlock(&cpuset_buffer_lock);
rcu_read_unlock();
+ spin_unlock(&cpuset_buffer_lock);
}
/*
@@ -2660,9 +2590,9 @@ int cpuset_memory_pressure_enabled __read_mostly;
void __cpuset_memory_pressure_bump(void)
{
- task_lock(current);
+ rcu_read_lock();
fmeter_markevent(&task_cs(current)->fmeter);
- task_unlock(current);
+ rcu_read_unlock();
}
#ifdef CONFIG_PROC_PID_CPUSET
@@ -2679,12 +2609,12 @@ int proc_cpuset_show(struct seq_file *m, void *unused_v)
{
struct pid *pid;
struct task_struct *tsk;
- char *buf;
+ char *buf, *p;
struct cgroup_subsys_state *css;
int retval;
retval = -ENOMEM;
- buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ buf = kmalloc(PATH_MAX, GFP_KERNEL);
if (!buf)
goto out;
@@ -2694,14 +2624,16 @@ int proc_cpuset_show(struct seq_file *m, void *unused_v)
if (!tsk)
goto out_free;
+ retval = -ENAMETOOLONG;
rcu_read_lock();
- css = task_css(tsk, cpuset_subsys_id);
- retval = cgroup_path(css->cgroup, buf, PAGE_SIZE);
+ css = task_css(tsk, cpuset_cgrp_id);
+ p = cgroup_path(css->cgroup, buf, PATH_MAX);
rcu_read_unlock();
- if (retval < 0)
+ if (!p)
goto out_put_task;
- seq_puts(m, buf);
+ seq_puts(m, p);
seq_putc(m, '\n');
+ retval = 0;
out_put_task:
put_task_struct(tsk);
out_free:
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 661951ab8ae7..f83a71a3e46d 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -361,7 +361,7 @@ struct perf_cgroup {
static inline struct perf_cgroup *
perf_cgroup_from_task(struct task_struct *task)
{
- return container_of(task_css(task, perf_subsys_id),
+ return container_of(task_css(task, perf_event_cgrp_id),
struct perf_cgroup, css);
}
@@ -389,11 +389,6 @@ perf_cgroup_match(struct perf_event *event)
event->cgrp->css.cgroup);
}
-static inline bool perf_tryget_cgroup(struct perf_event *event)
-{
- return css_tryget(&event->cgrp->css);
-}
-
static inline void perf_put_cgroup(struct perf_event *event)
{
css_put(&event->cgrp->css);
@@ -612,9 +607,7 @@ static inline int perf_cgroup_connect(int fd, struct perf_event *event,
if (!f.file)
return -EBADF;
- rcu_read_lock();
-
- css = css_from_dir(f.file->f_dentry, &perf_subsys);
+ css = css_tryget_from_dir(f.file->f_dentry, &perf_event_cgrp_subsys);
if (IS_ERR(css)) {
ret = PTR_ERR(css);
goto out;
@@ -623,13 +616,6 @@ static inline int perf_cgroup_connect(int fd, struct perf_event *event,
cgrp = container_of(css, struct perf_cgroup, css);
event->cgrp = cgrp;
- /* must be done before we fput() the file */
- if (!perf_tryget_cgroup(event)) {
- event->cgrp = NULL;
- ret = -ENOENT;
- goto out;
- }
-
/*
* all events in a group must monitor
* the same cgroup because a task belongs
@@ -640,7 +626,6 @@ static inline int perf_cgroup_connect(int fd, struct perf_event *event,
ret = -EINVAL;
}
out:
- rcu_read_unlock();
fdput(f);
return ret;
}
@@ -8053,7 +8038,7 @@ static void perf_cgroup_attach(struct cgroup_subsys_state *css,
{
struct task_struct *task;
- cgroup_taskset_for_each(task, css, tset)
+ cgroup_taskset_for_each(task, tset)
task_function_call(task, __perf_cgroup_move, task);
}
@@ -8072,9 +8057,7 @@ static void perf_cgroup_exit(struct cgroup_subsys_state *css,
task_function_call(task, __perf_cgroup_move, task);
}
-struct cgroup_subsys perf_subsys = {
- .name = "perf_event",
- .subsys_id = perf_subsys_id,
+struct cgroup_subsys perf_event_cgrp_subsys = {
.css_alloc = perf_cgroup_css_alloc,
.css_free = perf_cgroup_css_free,
.exit = perf_cgroup_exit,
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 307d87c0991a..04709b66369d 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1804,6 +1804,11 @@ static bool handle_trampoline(struct pt_regs *regs)
return true;
}
+bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs)
+{
+ return false;
+}
+
/*
* Run handler and ask thread to singlestep.
* Ensure all non-fatal signals cannot interrupt thread while it singlesteps.
@@ -1858,7 +1863,11 @@ static void handle_swbp(struct pt_regs *regs)
if (!get_utask())
goto out;
+ if (arch_uprobe_ignore(&uprobe->arch, regs))
+ goto out;
+
handler_chain(uprobe, regs);
+
if (can_skip_sstep(uprobe, regs))
goto out;
diff --git a/kernel/exit.c b/kernel/exit.c
index 1e77fc645317..6480d1c85d7a 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -797,7 +797,7 @@ void do_exit(long code)
*/
perf_event_exit_task(tsk);
- cgroup_exit(tsk, 1);
+ cgroup_exit(tsk);
if (group_dead)
disassociate_ctty(1);
diff --git a/kernel/fork.c b/kernel/fork.c
index 332688e5e7b4..abc45890f0a5 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1272,7 +1272,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
if (IS_ERR(p->mempolicy)) {
retval = PTR_ERR(p->mempolicy);
p->mempolicy = NULL;
- goto bad_fork_cleanup_cgroup;
+ goto bad_fork_cleanup_threadgroup_lock;
}
mpol_fix_fork_child_flag(p);
#endif
@@ -1525,11 +1525,10 @@ bad_fork_cleanup_policy:
perf_event_free_task(p);
#ifdef CONFIG_NUMA
mpol_put(p->mempolicy);
-bad_fork_cleanup_cgroup:
+bad_fork_cleanup_threadgroup_lock:
#endif
if (clone_flags & CLONE_THREAD)
threadgroup_change_end(current);
- cgroup_exit(p, 0);
delayacct_tsk_free(p);
module_put(task_thread_info(p)->exec_domain->module);
bad_fork_cleanup_count:
diff --git a/kernel/groups.c b/kernel/groups.c
index 90cf1c38c8ea..451698f86cfa 100644
--- a/kernel/groups.c
+++ b/kernel/groups.c
@@ -157,17 +157,13 @@ int groups_search(const struct group_info *group_info, kgid_t grp)
* set_groups - Change a group subscription in a set of credentials
* @new: The newly prepared set of credentials to alter
* @group_info: The group list to install
- *
- * Validate a group subscription and, if valid, insert it into a set
- * of credentials.
*/
-int set_groups(struct cred *new, struct group_info *group_info)
+void set_groups(struct cred *new, struct group_info *group_info)
{
put_group_info(new->group_info);
groups_sort(group_info);
get_group_info(group_info);
new->group_info = group_info;
- return 0;
}
EXPORT_SYMBOL(set_groups);
@@ -182,18 +178,12 @@ EXPORT_SYMBOL(set_groups);
int set_current_groups(struct group_info *group_info)
{
struct cred *new;
- int ret;
new = prepare_creds();
if (!new)
return -ENOMEM;
- ret = set_groups(new, group_info);
- if (ret < 0) {
- abort_creds(new);
- return ret;
- }
-
+ set_groups(new, group_info);
return commit_creds(new);
}
diff --git a/kernel/hung_task.c b/kernel/hung_task.c
index 0b9c169d577f..06bb1417b063 100644
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -246,5 +246,4 @@ static int __init hung_task_init(void)
return 0;
}
-
-module_init(hung_task_init);
+subsys_initcall(hung_task_init);
diff --git a/kernel/kexec.c b/kernel/kexec.c
index 45601cf41bee..c0d261c7db7b 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -1235,7 +1235,7 @@ static int __init crash_notes_memory_init(void)
}
return 0;
}
-module_init(crash_notes_memory_init)
+subsys_initcall(crash_notes_memory_init);
/*
@@ -1629,7 +1629,7 @@ static int __init crash_save_vmcoreinfo_init(void)
return 0;
}
-module_init(crash_save_vmcoreinfo_init)
+subsys_initcall(crash_save_vmcoreinfo_init);
/*
* Move into place and start executing a preloaded standalone
diff --git a/kernel/kthread.c b/kernel/kthread.c
index b5ae3ee860a9..9a130ec06f7a 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -217,7 +217,7 @@ int tsk_fork_get_node(struct task_struct *tsk)
if (tsk == kthreadd_task)
return tsk->pref_node_fork;
#endif
- return numa_node_id();
+ return NUMA_NO_NODE;
}
static void create_kthread(struct kthread_create_info *create)
@@ -369,7 +369,7 @@ struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
{
struct task_struct *p;
- p = kthread_create_on_node(threadfn, data, cpu_to_node(cpu), namefmt,
+ p = kthread_create_on_node(threadfn, data, cpu_to_mem(cpu), namefmt,
cpu);
if (IS_ERR(p))
return p;
diff --git a/kernel/module.c b/kernel/module.c
index 8dc7f5e80dd8..29f7790eaa14 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1013,6 +1013,8 @@ static size_t module_flags_taint(struct module *mod, char *buf)
buf[l++] = 'F';
if (mod->taints & (1 << TAINT_CRAP))
buf[l++] = 'C';
+ if (mod->taints & (1 << TAINT_UNSIGNED_MODULE))
+ buf[l++] = 'E';
/*
* TAINT_FORCED_RMMOD: could be added.
* TAINT_CPU_OUT_OF_SPEC, TAINT_MACHINE_CHECK, TAINT_BAD_PAGE don't
@@ -3218,7 +3220,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
pr_notice_once("%s: module verification failed: signature "
"and/or required key missing - tainting "
"kernel\n", mod->name);
- add_taint_module(mod, TAINT_FORCED_MODULE, LOCKDEP_STILL_OK);
+ add_taint_module(mod, TAINT_UNSIGNED_MODULE, LOCKDEP_STILL_OK);
}
#endif
@@ -3813,12 +3815,12 @@ void print_modules(void)
list_for_each_entry_rcu(mod, &modules, list) {
if (mod->state == MODULE_STATE_UNFORMED)
continue;
- printk(" %s%s", mod->name, module_flags(mod, buf));
+ pr_cont(" %s%s", mod->name, module_flags(mod, buf));
}
preempt_enable();
if (last_unloaded_module[0])
- printk(" [last unloaded: %s]", last_unloaded_module);
- printk("\n");
+ pr_cont(" [last unloaded: %s]", last_unloaded_module);
+ pr_cont("\n");
}
#ifdef CONFIG_MODVERSIONS
diff --git a/kernel/panic.c b/kernel/panic.c
index cca8a913ae7c..79fd820bb5e8 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -210,6 +210,7 @@ static const struct tnt tnts[] = {
{ TAINT_CRAP, 'C', ' ' },
{ TAINT_FIRMWARE_WORKAROUND, 'I', ' ' },
{ TAINT_OOT_MODULE, 'O', ' ' },
+ { TAINT_UNSIGNED_MODULE, 'E', ' ' },
};
/**
@@ -228,6 +229,7 @@ static const struct tnt tnts[] = {
* 'C' - modules from drivers/staging are loaded.
* 'I' - Working around severe firmware bug.
* 'O' - Out-of-tree module has been loaded.
+ * 'E' - Unsigned module has been loaded.
*
* The string is overwritten by the next call to print_tainted().
*/
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index 06c62de9c711..db95d8eb761b 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -318,7 +318,9 @@ static void *pidns_get(struct task_struct *task)
struct pid_namespace *ns;
rcu_read_lock();
- ns = get_pid_ns(task_active_pid_ns(task));
+ ns = task_active_pid_ns(task);
+ if (ns)
+ get_pid_ns(ns);
rcu_read_unlock();
return ns;
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 4dae9cbe9259..a45b50962295 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -319,7 +319,7 @@ static void log_store(int facility, int level,
else
free = log_first_idx - log_next_idx;
- if (free > size + sizeof(struct printk_log))
+ if (free >= size + sizeof(struct printk_log))
break;
/* drop old messages until we have enough contiuous space */
@@ -327,7 +327,7 @@ static void log_store(int facility, int level,
log_first_seq++;
}
- if (log_next_idx + size + sizeof(struct printk_log) >= log_buf_len) {
+ if (log_next_idx + size + sizeof(struct printk_log) > log_buf_len) {
/*
* This message + an additional empty header does not fit
* at the end of the buffer. Add an empty header with len == 0
@@ -351,7 +351,7 @@ static void log_store(int facility, int level,
else
msg->ts_nsec = local_clock();
memset(log_dict(msg) + dict_len, 0, pad_len);
- msg->len = sizeof(struct printk_log) + text_len + dict_len + pad_len;
+ msg->len = size;
/* insert message */
log_next_idx += msg->len;
@@ -1560,9 +1560,12 @@ asmlinkage int vprintk_emit(int facility, int level,
level = kern_level - '0';
case 'd': /* KERN_DEFAULT */
lflags |= LOG_PREFIX;
- case 'c': /* KERN_CONT */
- break;
}
+ /*
+ * No need to check length here because vscnprintf
+ * put '\0' at the end of the string. Only valid and
+ * newly printed level is detected.
+ */
text_len -= end_of_header - text;
text = (char *)end_of_header;
}
@@ -1880,6 +1883,7 @@ void suspend_console(void)
console_lock();
console_suspended = 1;
up(&console_sem);
+ mutex_release(&console_lock_dep_map, 1, _RET_IP_);
}
void resume_console(void)
@@ -1887,6 +1891,7 @@ void resume_console(void)
if (!console_suspend_enabled)
return;
down(&console_sem);
+ mutex_acquire(&console_lock_dep_map, 0, 0, _RET_IP_);
console_suspended = 0;
console_unlock();
}
diff --git a/kernel/profile.c b/kernel/profile.c
index ebdd9c1a86b4..cb980f0c731b 100644
--- a/kernel/profile.c
+++ b/kernel/profile.c
@@ -591,18 +591,28 @@ out_cleanup:
int __ref create_proc_profile(void) /* false positive from hotcpu_notifier */
{
struct proc_dir_entry *entry;
+ int err = 0;
if (!prof_on)
return 0;
- if (create_hash_tables())
- return -ENOMEM;
+
+ cpu_notifier_register_begin();
+
+ if (create_hash_tables()) {
+ err = -ENOMEM;
+ goto out;
+ }
+
entry = proc_create("profile", S_IWUSR | S_IRUGO,
NULL, &proc_profile_operations);
if (!entry)
- return 0;
+ goto out;
proc_set_size(entry, (1 + prof_len) * sizeof(atomic_t));
- hotcpu_notifier(profile_cpu_callback, 0);
- return 0;
+ __hotcpu_notifier(profile_cpu_callback, 0);
+
+out:
+ cpu_notifier_register_done();
+ return err;
}
-module_init(create_proc_profile);
+subsys_initcall(create_proc_profile);
#endif /* CONFIG_PROC_FS */
diff --git a/kernel/relay.c b/kernel/relay.c
index 5001c9887db1..52d6a6f56261 100644
--- a/kernel/relay.c
+++ b/kernel/relay.c
@@ -227,7 +227,7 @@ static void relay_destroy_buf(struct rchan_buf *buf)
* relay_remove_buf - remove a channel buffer
* @kref: target kernel reference that contains the relay buffer
*
- * Removes the file from the fileystem, which also frees the
+ * Removes the file from the filesystem, which also frees the
* rchan_buf_struct and the channel buffer. Should only be called from
* kref_put().
*/
diff --git a/kernel/resource.c b/kernel/resource.c
index 673061c06da1..8957d686e29b 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -511,7 +511,7 @@ static int find_resource(struct resource *root, struct resource *new,
* @newsize: new size of the resource descriptor
* @constraint: the size and alignment constraints to be met.
*/
-int reallocate_resource(struct resource *root, struct resource *old,
+static int reallocate_resource(struct resource *root, struct resource *old,
resource_size_t newsize,
struct resource_constraint *constraint)
{
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 9cae286824bb..0ff3f34bc7e3 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2845,52 +2845,6 @@ int default_wake_function(wait_queue_t *curr, unsigned mode, int wake_flags,
}
EXPORT_SYMBOL(default_wake_function);
-static long __sched
-sleep_on_common(wait_queue_head_t *q, int state, long timeout)
-{
- unsigned long flags;
- wait_queue_t wait;
-
- init_waitqueue_entry(&wait, current);
-
- __set_current_state(state);
-
- spin_lock_irqsave(&q->lock, flags);
- __add_wait_queue(q, &wait);
- spin_unlock(&q->lock);
- timeout = schedule_timeout(timeout);
- spin_lock_irq(&q->lock);
- __remove_wait_queue(q, &wait);
- spin_unlock_irqrestore(&q->lock, flags);
-
- return timeout;
-}
-
-void __sched interruptible_sleep_on(wait_queue_head_t *q)
-{
- sleep_on_common(q, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
-}
-EXPORT_SYMBOL(interruptible_sleep_on);
-
-long __sched
-interruptible_sleep_on_timeout(wait_queue_head_t *q, long timeout)
-{
- return sleep_on_common(q, TASK_INTERRUPTIBLE, timeout);
-}
-EXPORT_SYMBOL(interruptible_sleep_on_timeout);
-
-void __sched sleep_on(wait_queue_head_t *q)
-{
- sleep_on_common(q, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
-}
-EXPORT_SYMBOL(sleep_on);
-
-long __sched sleep_on_timeout(wait_queue_head_t *q, long timeout)
-{
- return sleep_on_common(q, TASK_UNINTERRUPTIBLE, timeout);
-}
-EXPORT_SYMBOL(sleep_on_timeout);
-
#ifdef CONFIG_RT_MUTEXES
/*
@@ -7230,7 +7184,7 @@ void sched_move_task(struct task_struct *tsk)
if (unlikely(running))
tsk->sched_class->put_prev_task(rq, tsk);
- tg = container_of(task_css_check(tsk, cpu_cgroup_subsys_id,
+ tg = container_of(task_css_check(tsk, cpu_cgrp_id,
lockdep_is_held(&tsk->sighand->siglock)),
struct task_group, css);
tg = autogroup_task_group(tsk, tg);
@@ -7657,7 +7611,7 @@ static int cpu_cgroup_can_attach(struct cgroup_subsys_state *css,
{
struct task_struct *task;
- cgroup_taskset_for_each(task, css, tset) {
+ cgroup_taskset_for_each(task, tset) {
#ifdef CONFIG_RT_GROUP_SCHED
if (!sched_rt_can_attach(css_tg(css), task))
return -EINVAL;
@@ -7675,7 +7629,7 @@ static void cpu_cgroup_attach(struct cgroup_subsys_state *css,
{
struct task_struct *task;
- cgroup_taskset_for_each(task, css, tset)
+ cgroup_taskset_for_each(task, tset)
sched_move_task(task);
}
@@ -8014,8 +7968,7 @@ static struct cftype cpu_files[] = {
{ } /* terminate */
};
-struct cgroup_subsys cpu_cgroup_subsys = {
- .name = "cpu",
+struct cgroup_subsys cpu_cgrp_subsys = {
.css_alloc = cpu_cgroup_css_alloc,
.css_free = cpu_cgroup_css_free,
.css_online = cpu_cgroup_css_online,
@@ -8023,7 +7976,6 @@ struct cgroup_subsys cpu_cgroup_subsys = {
.can_attach = cpu_cgroup_can_attach,
.attach = cpu_cgroup_attach,
.exit = cpu_cgroup_exit,
- .subsys_id = cpu_cgroup_subsys_id,
.base_cftypes = cpu_files,
.early_init = 1,
};
diff --git a/kernel/sched/cpuacct.c b/kernel/sched/cpuacct.c
index 622e0818f905..c143ee380e3a 100644
--- a/kernel/sched/cpuacct.c
+++ b/kernel/sched/cpuacct.c
@@ -41,7 +41,7 @@ static inline struct cpuacct *css_ca(struct cgroup_subsys_state *css)
/* return cpu accounting group to which this task belongs */
static inline struct cpuacct *task_ca(struct task_struct *tsk)
{
- return css_ca(task_css(tsk, cpuacct_subsys_id));
+ return css_ca(task_css(tsk, cpuacct_cgrp_id));
}
static inline struct cpuacct *parent_ca(struct cpuacct *ca)
@@ -275,11 +275,9 @@ void cpuacct_account_field(struct task_struct *p, int index, u64 val)
rcu_read_unlock();
}
-struct cgroup_subsys cpuacct_subsys = {
- .name = "cpuacct",
+struct cgroup_subsys cpuacct_cgrp_subsys = {
.css_alloc = cpuacct_css_alloc,
.css_free = cpuacct_css_free,
- .subsys_id = cpuacct_subsys_id,
.base_cftypes = files,
.early_init = 1,
};
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index f3344c31632a..695f9773bb60 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -111,8 +111,7 @@ static char *task_group_path(struct task_group *tg)
if (autogroup_path(tg, group_path, PATH_MAX))
return group_path;
- cgroup_path(tg->css.cgroup, group_path, PATH_MAX);
- return group_path;
+ return cgroup_path(tg->css.cgroup, group_path, PATH_MAX);
}
#endif
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index b7976a127178..8f4390a079c7 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -63,6 +63,136 @@ void __weak arch_cpu_idle(void)
local_irq_enable();
}
+/**
+ * cpuidle_idle_call - the main idle function
+ *
+ * NOTE: no locks or semaphores should be used here
+ * return non-zero on failure
+ */
+static int cpuidle_idle_call(void)
+{
+ struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
+ struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
+ int next_state, entered_state, ret;
+ bool broadcast;
+
+ /*
+ * Check if the idle task must be rescheduled. If it is the
+ * case, exit the function after re-enabling the local irq and
+ * set again the polling flag
+ */
+ if (current_clr_polling_and_test()) {
+ local_irq_enable();
+ __current_set_polling();
+ return 0;
+ }
+
+ /*
+ * During the idle period, stop measuring the disabled irqs
+ * critical sections latencies
+ */
+ stop_critical_timings();
+
+ /*
+ * Tell the RCU framework we are entering an idle section,
+ * so no more rcu read side critical sections and one more
+ * step to the grace period
+ */
+ rcu_idle_enter();
+
+ /*
+ * Check if the cpuidle framework is ready, otherwise fallback
+ * to the default arch specific idle method
+ */
+ ret = cpuidle_enabled(drv, dev);
+
+ if (!ret) {
+ /*
+ * Ask the governor to choose an idle state it thinks
+ * it is convenient to go to. There is *always* a
+ * convenient idle state
+ */
+ next_state = cpuidle_select(drv, dev);
+
+ /*
+ * The idle task must be scheduled, it is pointless to
+ * go to idle, just update no idle residency and get
+ * out of this function
+ */
+ if (current_clr_polling_and_test()) {
+ dev->last_residency = 0;
+ entered_state = next_state;
+ local_irq_enable();
+ } else {
+ broadcast = !!(drv->states[next_state].flags &
+ CPUIDLE_FLAG_TIMER_STOP);
+
+ if (broadcast)
+ /*
+ * Tell the time framework to switch
+ * to a broadcast timer because our
+ * local timer will be shutdown. If a
+ * local timer is used from another
+ * cpu as a broadcast timer, this call
+ * may fail if it is not available
+ */
+ ret = clockevents_notify(
+ CLOCK_EVT_NOTIFY_BROADCAST_ENTER,
+ &dev->cpu);
+
+ if (!ret) {
+ trace_cpu_idle_rcuidle(next_state, dev->cpu);
+
+ /*
+ * Enter the idle state previously
+ * returned by the governor
+ * decision. This function will block
+ * until an interrupt occurs and will
+ * take care of re-enabling the local
+ * interrupts
+ */
+ entered_state = cpuidle_enter(drv, dev,
+ next_state);
+
+ trace_cpu_idle_rcuidle(PWR_EVENT_EXIT,
+ dev->cpu);
+
+ if (broadcast)
+ clockevents_notify(
+ CLOCK_EVT_NOTIFY_BROADCAST_EXIT,
+ &dev->cpu);
+
+ /*
+ * Give the governor an opportunity to reflect on the
+ * outcome
+ */
+ cpuidle_reflect(dev, entered_state);
+ }
+ }
+ }
+
+ /*
+ * We can't use the cpuidle framework, let's use the default
+ * idle routine
+ */
+ if (ret)
+ arch_cpu_idle();
+
+ __current_set_polling();
+
+ /*
+ * It is up to the idle functions to enable back the local
+ * interrupt
+ */
+ if (WARN_ON_ONCE(irqs_disabled()))
+ local_irq_enable();
+
+ rcu_idle_exit();
+ start_critical_timings();
+
+ return 0;
+}
+
/*
* Generic idle loop implementation
*/
@@ -90,23 +220,11 @@ static void cpu_idle_loop(void)
* know that the IPI is going to arrive right
* away
*/
- if (cpu_idle_force_poll || tick_check_broadcast_expired()) {
+ if (cpu_idle_force_poll || tick_check_broadcast_expired())
cpu_idle_poll();
- } else {
- if (!current_clr_polling_and_test()) {
- stop_critical_timings();
- rcu_idle_enter();
- if (cpuidle_idle_call())
- arch_cpu_idle();
- if (WARN_ON_ONCE(irqs_disabled()))
- local_irq_enable();
- rcu_idle_exit();
- start_critical_timings();
- } else {
- local_irq_enable();
- }
- __current_set_polling();
- }
+ else
+ cpuidle_idle_call();
+
arch_cpu_idle_exit();
}
diff --git a/kernel/sched/stats.c b/kernel/sched/stats.c
index da98af347e8b..a476bea17fbc 100644
--- a/kernel/sched/stats.c
+++ b/kernel/sched/stats.c
@@ -142,4 +142,4 @@ static int __init proc_schedstat_init(void)
proc_create("schedstat", 0, NULL, &proc_schedstat_operations);
return 0;
}
-module_init(proc_schedstat_init);
+subsys_initcall(proc_schedstat_init);
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index b7a10048a32c..fd609bd9d6dd 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -55,60 +55,33 @@ struct seccomp_filter {
atomic_t usage;
struct seccomp_filter *prev;
unsigned short len; /* Instruction count */
- struct sock_filter insns[];
+ struct sock_filter_int insnsi[];
};
/* Limit any path through the tree to 256KB worth of instructions. */
#define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter))
-/**
- * get_u32 - returns a u32 offset into data
- * @data: a unsigned 64 bit value
- * @index: 0 or 1 to return the first or second 32-bits
- *
- * This inline exists to hide the length of unsigned long. If a 32-bit
- * unsigned long is passed in, it will be extended and the top 32-bits will be
- * 0. If it is a 64-bit unsigned long, then whatever data is resident will be
- * properly returned.
- *
+/*
* Endianness is explicitly ignored and left for BPF program authors to manage
* as per the specific architecture.
*/
-static inline u32 get_u32(u64 data, int index)
+static void populate_seccomp_data(struct seccomp_data *sd)
{
- return ((u32 *)&data)[index];
-}
+ struct task_struct *task = current;
+ struct pt_regs *regs = task_pt_regs(task);
-/* Helper for bpf_load below. */
-#define BPF_DATA(_name) offsetof(struct seccomp_data, _name)
-/**
- * bpf_load: checks and returns a pointer to the requested offset
- * @off: offset into struct seccomp_data to load from
- *
- * Returns the requested 32-bits of data.
- * seccomp_check_filter() should assure that @off is 32-bit aligned
- * and not out of bounds. Failure to do so is a BUG.
- */
-u32 seccomp_bpf_load(int off)
-{
- struct pt_regs *regs = task_pt_regs(current);
- if (off == BPF_DATA(nr))
- return syscall_get_nr(current, regs);
- if (off == BPF_DATA(arch))
- return syscall_get_arch(current, regs);
- if (off >= BPF_DATA(args[0]) && off < BPF_DATA(args[6])) {
- unsigned long value;
- int arg = (off - BPF_DATA(args[0])) / sizeof(u64);
- int index = !!(off % sizeof(u64));
- syscall_get_arguments(current, regs, arg, 1, &value);
- return get_u32(value, index);
- }
- if (off == BPF_DATA(instruction_pointer))
- return get_u32(KSTK_EIP(current), 0);
- if (off == BPF_DATA(instruction_pointer) + sizeof(u32))
- return get_u32(KSTK_EIP(current), 1);
- /* seccomp_check_filter should make this impossible. */
- BUG();
+ sd->nr = syscall_get_nr(task, regs);
+ sd->arch = syscall_get_arch(task, regs);
+
+ /* Unroll syscall_get_args to help gcc on arm. */
+ syscall_get_arguments(task, regs, 0, 1, (unsigned long *) &sd->args[0]);
+ syscall_get_arguments(task, regs, 1, 1, (unsigned long *) &sd->args[1]);
+ syscall_get_arguments(task, regs, 2, 1, (unsigned long *) &sd->args[2]);
+ syscall_get_arguments(task, regs, 3, 1, (unsigned long *) &sd->args[3]);
+ syscall_get_arguments(task, regs, 4, 1, (unsigned long *) &sd->args[4]);
+ syscall_get_arguments(task, regs, 5, 1, (unsigned long *) &sd->args[5]);
+
+ sd->instruction_pointer = KSTK_EIP(task);
}
/**
@@ -133,17 +106,17 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
switch (code) {
case BPF_S_LD_W_ABS:
- ftest->code = BPF_S_ANC_SECCOMP_LD_W;
+ ftest->code = BPF_LDX | BPF_W | BPF_ABS;
/* 32-bit aligned and not out of bounds. */
if (k >= sizeof(struct seccomp_data) || k & 3)
return -EINVAL;
continue;
case BPF_S_LD_W_LEN:
- ftest->code = BPF_S_LD_IMM;
+ ftest->code = BPF_LD | BPF_IMM;
ftest->k = sizeof(struct seccomp_data);
continue;
case BPF_S_LDX_W_LEN:
- ftest->code = BPF_S_LDX_IMM;
+ ftest->code = BPF_LDX | BPF_IMM;
ftest->k = sizeof(struct seccomp_data);
continue;
/* Explicitly include allowed calls. */
@@ -185,6 +158,7 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
case BPF_S_JMP_JGT_X:
case BPF_S_JMP_JSET_K:
case BPF_S_JMP_JSET_X:
+ sk_decode_filter(ftest, ftest);
continue;
default:
return -EINVAL;
@@ -202,18 +176,21 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
static u32 seccomp_run_filters(int syscall)
{
struct seccomp_filter *f;
+ struct seccomp_data sd;
u32 ret = SECCOMP_RET_ALLOW;
/* Ensure unexpected behavior doesn't result in failing open. */
if (WARN_ON(current->seccomp.filter == NULL))
return SECCOMP_RET_KILL;
+ populate_seccomp_data(&sd);
+
/*
* All filters in the list are evaluated and the lowest BPF return
* value always takes priority (ignoring the DATA).
*/
for (f = current->seccomp.filter; f; f = f->prev) {
- u32 cur_ret = sk_run_filter(NULL, f->insns);
+ u32 cur_ret = sk_run_filter_int_seccomp(&sd, f->insnsi);
if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION))
ret = cur_ret;
}
@@ -231,6 +208,8 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
struct seccomp_filter *filter;
unsigned long fp_size = fprog->len * sizeof(struct sock_filter);
unsigned long total_insns = fprog->len;
+ struct sock_filter *fp;
+ int new_len;
long ret;
if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
@@ -252,28 +231,43 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
CAP_SYS_ADMIN) != 0)
return -EACCES;
- /* Allocate a new seccomp_filter */
- filter = kzalloc(sizeof(struct seccomp_filter) + fp_size,
- GFP_KERNEL|__GFP_NOWARN);
- if (!filter)
+ fp = kzalloc(fp_size, GFP_KERNEL|__GFP_NOWARN);
+ if (!fp)
return -ENOMEM;
- atomic_set(&filter->usage, 1);
- filter->len = fprog->len;
/* Copy the instructions from fprog. */
ret = -EFAULT;
- if (copy_from_user(filter->insns, fprog->filter, fp_size))
- goto fail;
+ if (copy_from_user(fp, fprog->filter, fp_size))
+ goto free_prog;
/* Check and rewrite the fprog via the skb checker */
- ret = sk_chk_filter(filter->insns, filter->len);
+ ret = sk_chk_filter(fp, fprog->len);
if (ret)
- goto fail;
+ goto free_prog;
/* Check and rewrite the fprog for seccomp use */
- ret = seccomp_check_filter(filter->insns, filter->len);
+ ret = seccomp_check_filter(fp, fprog->len);
+ if (ret)
+ goto free_prog;
+
+ /* Convert 'sock_filter' insns to 'sock_filter_int' insns */
+ ret = sk_convert_filter(fp, fprog->len, NULL, &new_len);
+ if (ret)
+ goto free_prog;
+
+ /* Allocate a new seccomp_filter */
+ filter = kzalloc(sizeof(struct seccomp_filter) +
+ sizeof(struct sock_filter_int) * new_len,
+ GFP_KERNEL|__GFP_NOWARN);
+ if (!filter)
+ goto free_prog;
+
+ ret = sk_convert_filter(fp, fprog->len, filter->insnsi, &new_len);
if (ret)
- goto fail;
+ goto free_filter;
+
+ atomic_set(&filter->usage, 1);
+ filter->len = new_len;
/*
* If there is an existing filter, make it the prev and don't drop its
@@ -282,8 +276,11 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
filter->prev = current->seccomp.filter;
current->seccomp.filter = filter;
return 0;
-fail:
+
+free_filter:
kfree(filter);
+free_prog:
+ kfree(fp);
return ret;
}
@@ -293,7 +290,7 @@ fail:
*
* Returns 0 on success and non-zero otherwise.
*/
-long seccomp_attach_user_filter(char __user *user_filter)
+static long seccomp_attach_user_filter(char __user *user_filter)
{
struct sock_fprog fprog;
long ret = -EFAULT;
diff --git a/kernel/signal.c b/kernel/signal.c
index 52f881db1ca0..5d4b05a229a6 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2382,7 +2382,7 @@ relock:
* @regs: user register state
* @stepping: nonzero if debugger single-step or block-step in use
*
- * This function should be called when a signal has succesfully been
+ * This function should be called when a signal has successfully been
* delivered. It updates the blocked signals accordingly (@ka->sa.sa_mask
* is always blocked, and the signal itself is blocked unless %SA_NODEFER
* is set in @ka->sa.sa_flags. Tracing is notified.
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index 7078052284fd..bc8d1b74a6b9 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -146,11 +146,13 @@ cond_syscall(sys_io_destroy);
cond_syscall(sys_io_submit);
cond_syscall(sys_io_cancel);
cond_syscall(sys_io_getevents);
+cond_syscall(sys_sysfs);
cond_syscall(sys_syslog);
cond_syscall(sys_process_vm_readv);
cond_syscall(sys_process_vm_writev);
cond_syscall(compat_sys_process_vm_readv);
cond_syscall(compat_sys_process_vm_writev);
+cond_syscall(sys_uselib);
/* arch-specific weak syscall entries */
cond_syscall(sys_pciconfig_read);
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 09d2e2413605..5c14b547882e 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -123,7 +123,7 @@ static int __maybe_unused neg_one = -1;
static int zero;
static int __maybe_unused one = 1;
static int __maybe_unused two = 2;
-static int __maybe_unused three = 3;
+static int __maybe_unused four = 4;
static unsigned long one_ul = 1;
static int one_hundred = 100;
#ifdef CONFIG_PRINTK
@@ -1264,7 +1264,7 @@ static struct ctl_table vm_table[] = {
.mode = 0644,
.proc_handler = drop_caches_sysctl_handler,
.extra1 = &one,
- .extra2 = &three,
+ .extra2 = &four,
},
#ifdef CONFIG_COMPACTION
{
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 015f85aaca08..8639819f6cef 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -424,6 +424,7 @@ config UPROBE_EVENT
bool "Enable uprobes-based dynamic events"
depends on ARCH_SUPPORTS_UPROBES
depends on MMU
+ depends on PERF_EVENTS
select UPROBES
select PROBE_EVENTS
select TRACING
diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c
index 4f3a3c03eadb..c1bd4ada2a04 100644
--- a/kernel/trace/blktrace.c
+++ b/kernel/trace/blktrace.c
@@ -1429,7 +1429,8 @@ static enum print_line_t blk_tracer_print_line(struct trace_iterator *iter)
return print_one_line(iter, true);
}
-static int blk_tracer_set_flag(u32 old_flags, u32 bit, int set)
+static int
+blk_tracer_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{
/* don't output context-info for blk_classic output */
if (bit == TRACE_BLK_OPT_CLASSIC) {
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index cd7f76d1eb86..1fd4b9479210 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -237,14 +237,13 @@ static int control_ops_alloc(struct ftrace_ops *ops)
return 0;
}
-static void control_ops_free(struct ftrace_ops *ops)
-{
- free_percpu(ops->disabled);
-}
-
static void update_global_ops(void)
{
- ftrace_func_t func;
+ ftrace_func_t func = ftrace_global_list_func;
+ void *private = NULL;
+
+ /* The list has its own recursion protection. */
+ global_ops.flags |= FTRACE_OPS_FL_RECURSION_SAFE;
/*
* If there's only one function registered, then call that
@@ -254,23 +253,17 @@ static void update_global_ops(void)
if (ftrace_global_list == &ftrace_list_end ||
ftrace_global_list->next == &ftrace_list_end) {
func = ftrace_global_list->func;
+ private = ftrace_global_list->private;
/*
* As we are calling the function directly.
* If it does not have recursion protection,
* the function_trace_op needs to be updated
* accordingly.
*/
- if (ftrace_global_list->flags & FTRACE_OPS_FL_RECURSION_SAFE)
- global_ops.flags |= FTRACE_OPS_FL_RECURSION_SAFE;
- else
+ if (!(ftrace_global_list->flags & FTRACE_OPS_FL_RECURSION_SAFE))
global_ops.flags &= ~FTRACE_OPS_FL_RECURSION_SAFE;
- } else {
- func = ftrace_global_list_func;
- /* The list has its own recursion protection. */
- global_ops.flags |= FTRACE_OPS_FL_RECURSION_SAFE;
}
-
/* If we filter on pids, update to use the pid function */
if (!list_empty(&ftrace_pids)) {
set_ftrace_pid_function(func);
@@ -278,6 +271,7 @@ static void update_global_ops(void)
}
global_ops.func = func;
+ global_ops.private = private;
}
static void ftrace_sync(struct work_struct *work)
@@ -437,6 +431,9 @@ static int remove_ftrace_list_ops(struct ftrace_ops **list,
static int __register_ftrace_function(struct ftrace_ops *ops)
{
+ if (ops->flags & FTRACE_OPS_FL_DELETED)
+ return -EINVAL;
+
if (FTRACE_WARN_ON(ops == &global_ops))
return -EINVAL;
@@ -1172,8 +1169,6 @@ struct ftrace_page {
int size;
};
-static struct ftrace_page *ftrace_new_pgs;
-
#define ENTRY_SIZE sizeof(struct dyn_ftrace)
#define ENTRIES_PER_PAGE (PAGE_SIZE / ENTRY_SIZE)
@@ -1560,7 +1555,7 @@ unsigned long ftrace_location(unsigned long ip)
* the function tracer. It checks the ftrace internal tables to
* determine if the address belongs or not.
*/
-int ftrace_text_reserved(void *start, void *end)
+int ftrace_text_reserved(const void *start, const void *end)
{
unsigned long ret;
@@ -1994,6 +1989,7 @@ int __weak ftrace_arch_code_modify_post_process(void)
void ftrace_modify_all_code(int command)
{
int update = command & FTRACE_UPDATE_TRACE_FUNC;
+ int err = 0;
/*
* If the ftrace_caller calls a ftrace_ops func directly,
@@ -2005,8 +2001,11 @@ void ftrace_modify_all_code(int command)
* to make sure the ops are having the right functions
* traced.
*/
- if (update)
- ftrace_update_ftrace_func(ftrace_ops_list_func);
+ if (update) {
+ err = ftrace_update_ftrace_func(ftrace_ops_list_func);
+ if (FTRACE_WARN_ON(err))
+ return;
+ }
if (command & FTRACE_UPDATE_CALLS)
ftrace_replace_code(1);
@@ -2019,13 +2018,16 @@ void ftrace_modify_all_code(int command)
/* If irqs are disabled, we are in stop machine */
if (!irqs_disabled())
smp_call_function(ftrace_sync_ipi, NULL, 1);
- ftrace_update_ftrace_func(ftrace_trace_function);
+ err = ftrace_update_ftrace_func(ftrace_trace_function);
+ if (FTRACE_WARN_ON(err))
+ return;
}
if (command & FTRACE_START_FUNC_RET)
- ftrace_enable_ftrace_graph_caller();
+ err = ftrace_enable_ftrace_graph_caller();
else if (command & FTRACE_STOP_FUNC_RET)
- ftrace_disable_ftrace_graph_caller();
+ err = ftrace_disable_ftrace_graph_caller();
+ FTRACE_WARN_ON(err);
}
static int __ftrace_modify_code(void *data)
@@ -2093,6 +2095,11 @@ static ftrace_func_t saved_ftrace_func;
static int ftrace_start_up;
static int global_start_up;
+static void control_ops_free(struct ftrace_ops *ops)
+{
+ free_percpu(ops->disabled);
+}
+
static void ftrace_startup_enable(int command)
{
if (saved_ftrace_func != ftrace_trace_function) {
@@ -2244,7 +2251,6 @@ static void ftrace_shutdown_sysctl(void)
}
static cycle_t ftrace_update_time;
-static unsigned long ftrace_update_cnt;
unsigned long ftrace_update_tot_cnt;
static inline int ops_traces_mod(struct ftrace_ops *ops)
@@ -2300,11 +2306,12 @@ static int referenced_filters(struct dyn_ftrace *rec)
return cnt;
}
-static int ftrace_update_code(struct module *mod)
+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;
int i;
@@ -2330,9 +2337,8 @@ static int ftrace_update_code(struct module *mod)
}
start = ftrace_now(raw_smp_processor_id());
- ftrace_update_cnt = 0;
- for (pg = ftrace_new_pgs; pg; pg = pg->next) {
+ for (pg = new_pgs; pg; pg = pg->next) {
for (i = 0; i < pg->index; i++) {
int cnt = ref;
@@ -2353,7 +2359,7 @@ static int ftrace_update_code(struct module *mod)
if (!ftrace_code_disable(mod, p))
break;
- ftrace_update_cnt++;
+ update_cnt++;
/*
* If the tracing is enabled, go ahead and enable the record.
@@ -2372,11 +2378,9 @@ static int ftrace_update_code(struct module *mod)
}
}
- ftrace_new_pgs = NULL;
-
stop = ftrace_now(raw_smp_processor_id());
ftrace_update_time = stop - start;
- ftrace_update_tot_cnt += ftrace_update_cnt;
+ ftrace_update_tot_cnt += update_cnt;
return 0;
}
@@ -2468,22 +2472,6 @@ ftrace_allocate_pages(unsigned long num_to_init)
return NULL;
}
-static int __init ftrace_dyn_table_alloc(unsigned long num_to_init)
-{
- int cnt;
-
- if (!num_to_init) {
- pr_info("ftrace: No functions to be traced?\n");
- return -1;
- }
-
- cnt = num_to_init / ENTRIES_PER_PAGE;
- pr_info("ftrace: allocating %ld entries in %d pages\n",
- num_to_init, cnt + 1);
-
- return 0;
-}
-
#define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */
struct ftrace_iterator {
@@ -2871,7 +2859,9 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag,
static int
ftrace_filter_open(struct inode *inode, struct file *file)
{
- return ftrace_regex_open(&global_ops,
+ struct ftrace_ops *ops = inode->i_private;
+
+ return ftrace_regex_open(ops,
FTRACE_ITER_FILTER | FTRACE_ITER_DO_HASH,
inode, file);
}
@@ -2879,7 +2869,9 @@ ftrace_filter_open(struct inode *inode, struct file *file)
static int
ftrace_notrace_open(struct inode *inode, struct file *file)
{
- return ftrace_regex_open(&global_ops, FTRACE_ITER_NOTRACE,
+ struct ftrace_ops *ops = inode->i_private;
+
+ return ftrace_regex_open(ops, FTRACE_ITER_NOTRACE,
inode, file);
}
@@ -4109,6 +4101,36 @@ static const struct file_operations ftrace_graph_notrace_fops = {
};
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+void ftrace_create_filter_files(struct ftrace_ops *ops,
+ struct dentry *parent)
+{
+
+ trace_create_file("set_ftrace_filter", 0644, parent,
+ ops, &ftrace_filter_fops);
+
+ trace_create_file("set_ftrace_notrace", 0644, parent,
+ ops, &ftrace_notrace_fops);
+}
+
+/*
+ * The name "destroy_filter_files" is really a misnomer. Although
+ * in the future, it may actualy delete the files, but this is
+ * really intended to make sure the ops passed in are disabled
+ * and that when this function returns, the caller is free to
+ * free the ops.
+ *
+ * The "destroy" name is only to match the "create" name that this
+ * should be paired with.
+ */
+void ftrace_destroy_filter_files(struct ftrace_ops *ops)
+{
+ mutex_lock(&ftrace_lock);
+ if (ops->flags & FTRACE_OPS_FL_ENABLED)
+ ftrace_shutdown(ops, 0);
+ ops->flags |= FTRACE_OPS_FL_DELETED;
+ mutex_unlock(&ftrace_lock);
+}
+
static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
{
@@ -4118,11 +4140,7 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
trace_create_file("enabled_functions", 0444,
d_tracer, NULL, &ftrace_enabled_fops);
- trace_create_file("set_ftrace_filter", 0644, d_tracer,
- NULL, &ftrace_filter_fops);
-
- trace_create_file("set_ftrace_notrace", 0644, d_tracer,
- NULL, &ftrace_notrace_fops);
+ ftrace_create_filter_files(&global_ops, d_tracer);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
trace_create_file("set_graph_function", 0444, d_tracer,
@@ -4238,9 +4256,6 @@ static int ftrace_process_locs(struct module *mod,
/* Assign the last page to ftrace_pages */
ftrace_pages = pg;
- /* These new locations need to be initialized */
- ftrace_new_pgs = start_pg;
-
/*
* We only need to disable interrupts on start up
* because we are modifying code that an interrupt
@@ -4251,7 +4266,7 @@ static int ftrace_process_locs(struct module *mod,
*/
if (!mod)
local_irq_save(flags);
- ftrace_update_code(mod);
+ ftrace_update_code(mod, start_pg);
if (!mod)
local_irq_restore(flags);
ret = 0;
@@ -4360,30 +4375,27 @@ struct notifier_block ftrace_module_exit_nb = {
.priority = INT_MIN, /* Run after anything that can remove kprobes */
};
-extern unsigned long __start_mcount_loc[];
-extern unsigned long __stop_mcount_loc[];
-
void __init ftrace_init(void)
{
- unsigned long count, addr, flags;
+ extern unsigned long __start_mcount_loc[];
+ extern unsigned long __stop_mcount_loc[];
+ unsigned long count, flags;
int ret;
- /* Keep the ftrace pointer to the stub */
- addr = (unsigned long)ftrace_stub;
-
local_irq_save(flags);
- ftrace_dyn_arch_init(&addr);
+ ret = ftrace_dyn_arch_init();
local_irq_restore(flags);
-
- /* ftrace_dyn_arch_init places the return code in addr */
- if (addr)
+ if (ret)
goto failed;
count = __stop_mcount_loc - __start_mcount_loc;
-
- ret = ftrace_dyn_table_alloc(count);
- if (ret)
+ if (!count) {
+ pr_info("ftrace: No functions to be traced?\n");
goto failed;
+ }
+
+ pr_info("ftrace: allocating %ld entries in %ld pages\n",
+ count, count / ENTRIES_PER_PAGE + 1);
last_ftrace_enabled = ftrace_enabled = 1;
@@ -4431,7 +4443,13 @@ static inline void ftrace_startup_enable(int command) { }
(ops)->flags |= FTRACE_OPS_FL_ENABLED; \
___ret; \
})
-# define ftrace_shutdown(ops, command) __unregister_ftrace_function(ops)
+# define ftrace_shutdown(ops, command) \
+ ({ \
+ int ___ret = __unregister_ftrace_function(ops); \
+ if (!___ret) \
+ (ops)->flags &= ~FTRACE_OPS_FL_ENABLED; \
+ ___ret; \
+ })
# define ftrace_startup_sysctl() do { } while (0)
# define ftrace_shutdown_sysctl() do { } while (0)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index fc4da2d97f9b..c634868c2921 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -1301,7 +1301,7 @@ struct ring_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags,
* In that off case, we need to allocate for all possible cpus.
*/
#ifdef CONFIG_HOTPLUG_CPU
- get_online_cpus();
+ cpu_notifier_register_begin();
cpumask_copy(buffer->cpumask, cpu_online_mask);
#else
cpumask_copy(buffer->cpumask, cpu_possible_mask);
@@ -1324,10 +1324,10 @@ struct ring_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags,
#ifdef CONFIG_HOTPLUG_CPU
buffer->cpu_notify.notifier_call = rb_cpu_notify;
buffer->cpu_notify.priority = 0;
- register_cpu_notifier(&buffer->cpu_notify);
+ __register_cpu_notifier(&buffer->cpu_notify);
+ cpu_notifier_register_done();
#endif
- put_online_cpus();
mutex_init(&buffer->mutex);
return buffer;
@@ -1341,7 +1341,9 @@ struct ring_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags,
fail_free_cpumask:
free_cpumask_var(buffer->cpumask);
- put_online_cpus();
+#ifdef CONFIG_HOTPLUG_CPU
+ cpu_notifier_register_done();
+#endif
fail_free_buffer:
kfree(buffer);
@@ -1358,16 +1360,17 @@ ring_buffer_free(struct ring_buffer *buffer)
{
int cpu;
- get_online_cpus();
-
#ifdef CONFIG_HOTPLUG_CPU
- unregister_cpu_notifier(&buffer->cpu_notify);
+ cpu_notifier_register_begin();
+ __unregister_cpu_notifier(&buffer->cpu_notify);
#endif
for_each_buffer_cpu(buffer, cpu)
rb_free_cpu_buffer(buffer->buffers[cpu]);
- put_online_cpus();
+#ifdef CONFIG_HOTPLUG_CPU
+ cpu_notifier_register_done();
+#endif
kfree(buffer->buffers);
free_cpumask_var(buffer->cpumask);
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 24c1f2382557..9be67c5e5b0f 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -73,7 +73,8 @@ static struct tracer_flags dummy_tracer_flags = {
.opts = dummy_tracer_opt
};
-static int dummy_set_flag(u32 old_flags, u32 bit, int set)
+static int
+dummy_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{
return 0;
}
@@ -118,7 +119,7 @@ enum ftrace_dump_mode ftrace_dump_on_oops;
/* When set, tracing will stop when a WARN*() is hit */
int __disable_trace_on_warning;
-static int tracing_set_tracer(const char *buf);
+static int tracing_set_tracer(struct trace_array *tr, const char *buf);
#define MAX_TRACER_SIZE 100
static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata;
@@ -180,6 +181,17 @@ static int __init set_trace_boot_options(char *str)
}
__setup("trace_options=", set_trace_boot_options);
+static char trace_boot_clock_buf[MAX_TRACER_SIZE] __initdata;
+static char *trace_boot_clock __initdata;
+
+static int __init set_trace_boot_clock(char *str)
+{
+ strlcpy(trace_boot_clock_buf, str, MAX_TRACER_SIZE);
+ trace_boot_clock = trace_boot_clock_buf;
+ return 0;
+}
+__setup("trace_clock=", set_trace_boot_clock);
+
unsigned long long ns2usecs(cycle_t nsec)
{
@@ -1230,7 +1242,7 @@ int register_tracer(struct tracer *type)
printk(KERN_INFO "Starting tracer '%s'\n", type->name);
/* Do we want this tracer to start on bootup? */
- tracing_set_tracer(type->name);
+ tracing_set_tracer(&global_trace, type->name);
default_bootup_tracer = NULL;
/* disable other selftests, since this will break it. */
tracing_selftest_disabled = true;
@@ -3137,27 +3149,52 @@ static int tracing_open(struct inode *inode, struct file *file)
return ret;
}
+/*
+ * Some tracers are not suitable for instance buffers.
+ * A tracer is always available for the global array (toplevel)
+ * or if it explicitly states that it is.
+ */
+static bool
+trace_ok_for_array(struct tracer *t, struct trace_array *tr)
+{
+ return (tr->flags & TRACE_ARRAY_FL_GLOBAL) || t->allow_instances;
+}
+
+/* Find the next tracer that this trace array may use */
+static struct tracer *
+get_tracer_for_array(struct trace_array *tr, struct tracer *t)
+{
+ while (t && !trace_ok_for_array(t, tr))
+ t = t->next;
+
+ return t;
+}
+
static void *
t_next(struct seq_file *m, void *v, loff_t *pos)
{
+ struct trace_array *tr = m->private;
struct tracer *t = v;
(*pos)++;
if (t)
- t = t->next;
+ t = get_tracer_for_array(tr, t->next);
return t;
}
static void *t_start(struct seq_file *m, loff_t *pos)
{
+ struct trace_array *tr = m->private;
struct tracer *t;
loff_t l = 0;
mutex_lock(&trace_types_lock);
- for (t = trace_types; t && l < *pos; t = t_next(m, t, &l))
- ;
+
+ t = get_tracer_for_array(tr, trace_types);
+ for (; t && l < *pos; t = t_next(m, t, &l))
+ ;
return t;
}
@@ -3192,10 +3229,21 @@ static const struct seq_operations show_traces_seq_ops = {
static int show_traces_open(struct inode *inode, struct file *file)
{
+ struct trace_array *tr = inode->i_private;
+ struct seq_file *m;
+ int ret;
+
if (tracing_disabled)
return -ENODEV;
- return seq_open(file, &show_traces_seq_ops);
+ ret = seq_open(file, &show_traces_seq_ops);
+ if (ret)
+ return ret;
+
+ m = file->private_data;
+ m->private = tr;
+
+ return 0;
}
static ssize_t
@@ -3355,13 +3403,14 @@ static int tracing_trace_options_show(struct seq_file *m, void *v)
return 0;
}
-static int __set_tracer_option(struct tracer *trace,
+static int __set_tracer_option(struct trace_array *tr,
struct tracer_flags *tracer_flags,
struct tracer_opt *opts, int neg)
{
+ struct tracer *trace = tr->current_trace;
int ret;
- ret = trace->set_flag(tracer_flags->val, opts->bit, !neg);
+ ret = trace->set_flag(tr, tracer_flags->val, opts->bit, !neg);
if (ret)
return ret;
@@ -3373,8 +3422,9 @@ static int __set_tracer_option(struct tracer *trace,
}
/* Try to assign a tracer specific option */
-static int set_tracer_option(struct tracer *trace, char *cmp, int neg)
+static int set_tracer_option(struct trace_array *tr, char *cmp, int neg)
{
+ struct tracer *trace = tr->current_trace;
struct tracer_flags *tracer_flags = trace->flags;
struct tracer_opt *opts = NULL;
int i;
@@ -3383,8 +3433,7 @@ static int set_tracer_option(struct tracer *trace, char *cmp, int neg)
opts = &tracer_flags->opts[i];
if (strcmp(cmp, opts->name) == 0)
- return __set_tracer_option(trace, trace->flags,
- opts, neg);
+ return __set_tracer_option(tr, trace->flags, opts, neg);
}
return -EINVAL;
@@ -3407,7 +3456,7 @@ int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled)
/* Give the tracer a chance to approve the change */
if (tr->current_trace->flag_changed)
- if (tr->current_trace->flag_changed(tr->current_trace, mask, !!enabled))
+ if (tr->current_trace->flag_changed(tr, mask, !!enabled))
return -EINVAL;
if (enabled)
@@ -3456,7 +3505,7 @@ static int trace_set_options(struct trace_array *tr, char *option)
/* If no option could be set, test the specific tracer options */
if (!trace_options[i])
- ret = set_tracer_option(tr->current_trace, cmp, neg);
+ ret = set_tracer_option(tr, cmp, neg);
mutex_unlock(&trace_types_lock);
@@ -3885,10 +3934,26 @@ create_trace_option_files(struct trace_array *tr, struct tracer *tracer);
static void
destroy_trace_option_files(struct trace_option_dentry *topts);
-static int tracing_set_tracer(const char *buf)
+/*
+ * Used to clear out the tracer before deletion of an instance.
+ * Must have trace_types_lock held.
+ */
+static void tracing_set_nop(struct trace_array *tr)
+{
+ if (tr->current_trace == &nop_trace)
+ return;
+
+ tr->current_trace->enabled--;
+
+ if (tr->current_trace->reset)
+ tr->current_trace->reset(tr);
+
+ tr->current_trace = &nop_trace;
+}
+
+static int tracing_set_tracer(struct trace_array *tr, const char *buf)
{
static struct trace_option_dentry *topts;
- struct trace_array *tr = &global_trace;
struct tracer *t;
#ifdef CONFIG_TRACER_MAX_TRACE
bool had_max_tr;
@@ -3916,9 +3981,15 @@ static int tracing_set_tracer(const char *buf)
if (t == tr->current_trace)
goto out;
+ /* Some tracers are only allowed for the top level buffer */
+ if (!trace_ok_for_array(t, tr)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
trace_branch_disable();
- tr->current_trace->enabled = false;
+ tr->current_trace->enabled--;
if (tr->current_trace->reset)
tr->current_trace->reset(tr);
@@ -3941,9 +4012,11 @@ static int tracing_set_tracer(const char *buf)
free_snapshot(tr);
}
#endif
- destroy_trace_option_files(topts);
-
- topts = create_trace_option_files(tr, t);
+ /* Currently, only the top instance has options */
+ if (tr->flags & TRACE_ARRAY_FL_GLOBAL) {
+ destroy_trace_option_files(topts);
+ topts = create_trace_option_files(tr, t);
+ }
#ifdef CONFIG_TRACER_MAX_TRACE
if (t->use_max_tr && !had_max_tr) {
@@ -3960,7 +4033,7 @@ static int tracing_set_tracer(const char *buf)
}
tr->current_trace = t;
- tr->current_trace->enabled = true;
+ tr->current_trace->enabled++;
trace_branch_enable(tr);
out:
mutex_unlock(&trace_types_lock);
@@ -3972,6 +4045,7 @@ static ssize_t
tracing_set_trace_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
+ struct trace_array *tr = filp->private_data;
char buf[MAX_TRACER_SIZE+1];
int i;
size_t ret;
@@ -3991,7 +4065,7 @@ tracing_set_trace_write(struct file *filp, const char __user *ubuf,
for (i = cnt - 1; i > 0 && isspace(buf[i]); i--)
buf[i] = 0;
- err = tracing_set_tracer(buf);
+ err = tracing_set_tracer(tr, buf);
if (err)
return err;
@@ -4699,25 +4773,10 @@ static int tracing_clock_show(struct seq_file *m, void *v)
return 0;
}
-static ssize_t tracing_clock_write(struct file *filp, const char __user *ubuf,
- size_t cnt, loff_t *fpos)
+static int tracing_set_clock(struct trace_array *tr, const char *clockstr)
{
- struct seq_file *m = filp->private_data;
- struct trace_array *tr = m->private;
- char buf[64];
- const char *clockstr;
int i;
- if (cnt >= sizeof(buf))
- return -EINVAL;
-
- if (copy_from_user(&buf, ubuf, cnt))
- return -EFAULT;
-
- buf[cnt] = 0;
-
- clockstr = strstrip(buf);
-
for (i = 0; i < ARRAY_SIZE(trace_clocks); i++) {
if (strcmp(trace_clocks[i].name, clockstr) == 0)
break;
@@ -4745,6 +4804,32 @@ static ssize_t tracing_clock_write(struct file *filp, const char __user *ubuf,
mutex_unlock(&trace_types_lock);
+ return 0;
+}
+
+static ssize_t tracing_clock_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *fpos)
+{
+ struct seq_file *m = filp->private_data;
+ struct trace_array *tr = m->private;
+ char buf[64];
+ const char *clockstr;
+ int ret;
+
+ if (cnt >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = 0;
+
+ clockstr = strstrip(buf);
+
+ ret = tracing_set_clock(tr, clockstr);
+ if (ret)
+ return ret;
+
*fpos += cnt;
return cnt;
@@ -5705,7 +5790,7 @@ trace_options_write(struct file *filp, const char __user *ubuf, size_t cnt,
if (!!(topt->flags->val & topt->opt->bit) != val) {
mutex_lock(&trace_types_lock);
- ret = __set_tracer_option(topt->tr->current_trace, topt->flags,
+ ret = __set_tracer_option(topt->tr, topt->flags,
topt->opt, !val);
mutex_unlock(&trace_types_lock);
if (ret)
@@ -6112,7 +6197,9 @@ static int instance_delete(const char *name)
list_del(&tr->list);
+ tracing_set_nop(tr);
event_trace_del_tracer(tr);
+ ftrace_destroy_function_files(tr);
debugfs_remove_recursive(tr->dir);
free_percpu(tr->trace_buffer.data);
ring_buffer_free(tr->trace_buffer.buffer);
@@ -6207,6 +6294,12 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer)
{
int cpu;
+ trace_create_file("available_tracers", 0444, d_tracer,
+ tr, &show_traces_fops);
+
+ trace_create_file("current_tracer", 0644, d_tracer,
+ tr, &set_tracer_fops);
+
trace_create_file("tracing_cpumask", 0644, d_tracer,
tr, &tracing_cpumask_fops);
@@ -6237,6 +6330,9 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer)
trace_create_file("tracing_on", 0644, d_tracer,
tr, &rb_simple_fops);
+ if (ftrace_create_function_files(tr, d_tracer))
+ WARN(1, "Could not allocate function filter files");
+
#ifdef CONFIG_TRACER_SNAPSHOT
trace_create_file("snapshot", 0644, d_tracer,
tr, &snapshot_fops);
@@ -6259,12 +6355,6 @@ static __init int tracer_init_debugfs(void)
init_tracer_debugfs(&global_trace, d_tracer);
- trace_create_file("available_tracers", 0444, d_tracer,
- &global_trace, &show_traces_fops);
-
- trace_create_file("current_tracer", 0644, d_tracer,
- &global_trace, &set_tracer_fops);
-
#ifdef CONFIG_TRACER_MAX_TRACE
trace_create_file("tracing_max_latency", 0644, d_tracer,
&tracing_max_latency, &tracing_max_lat_fops);
@@ -6527,6 +6617,13 @@ __init static int tracer_alloc_buffers(void)
trace_init_cmdlines();
+ if (trace_boot_clock) {
+ ret = tracing_set_clock(&global_trace, trace_boot_clock);
+ if (ret < 0)
+ pr_warning("Trace clock %s not defined, going back to default\n",
+ trace_boot_clock);
+ }
+
/*
* register_tracer() might reference current_trace, so it
* needs to be set before we register anything. This is
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 02b592f2d4b7..ffc314b7e92b 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -210,6 +210,11 @@ struct trace_array {
struct list_head events;
cpumask_var_t tracing_cpumask; /* only trace on set CPUs */
int ref;
+#ifdef CONFIG_FUNCTION_TRACER
+ struct ftrace_ops *ops;
+ /* function tracing enabled */
+ int function_enabled;
+#endif
};
enum {
@@ -355,14 +360,16 @@ struct tracer {
void (*print_header)(struct seq_file *m);
enum print_line_t (*print_line)(struct trace_iterator *iter);
/* If you handled the flag setting, return 0 */
- int (*set_flag)(u32 old_flags, u32 bit, int set);
+ int (*set_flag)(struct trace_array *tr,
+ u32 old_flags, u32 bit, int set);
/* Return 0 if OK with change, else return non-zero */
- int (*flag_changed)(struct tracer *tracer,
+ int (*flag_changed)(struct trace_array *tr,
u32 mask, int set);
struct tracer *next;
struct tracer_flags *flags;
+ int enabled;
bool print_max;
- bool enabled;
+ bool allow_instances;
#ifdef CONFIG_TRACER_MAX_TRACE
bool use_max_tr;
#endif
@@ -812,13 +819,36 @@ static inline int ftrace_trace_task(struct task_struct *task)
return test_tsk_trace_trace(task);
}
extern int ftrace_is_dead(void);
+int ftrace_create_function_files(struct trace_array *tr,
+ struct dentry *parent);
+void ftrace_destroy_function_files(struct trace_array *tr);
#else
static inline int ftrace_trace_task(struct task_struct *task)
{
return 1;
}
static inline int ftrace_is_dead(void) { return 0; }
-#endif
+static inline int
+ftrace_create_function_files(struct trace_array *tr,
+ struct dentry *parent)
+{
+ return 0;
+}
+static inline void ftrace_destroy_function_files(struct trace_array *tr) { }
+#endif /* CONFIG_FUNCTION_TRACER */
+
+#if defined(CONFIG_FUNCTION_TRACER) && defined(CONFIG_DYNAMIC_FTRACE)
+void ftrace_create_filter_files(struct ftrace_ops *ops,
+ struct dentry *parent);
+void ftrace_destroy_filter_files(struct ftrace_ops *ops);
+#else
+/*
+ * The ops parameter passed in is usually undefined.
+ * This must be a macro.
+ */
+#define ftrace_create_filter_files(ops, parent) do { } while (0)
+#define ftrace_destroy_filter_files(ops) do { } while (0)
+#endif /* CONFIG_FUNCTION_TRACER && CONFIG_DYNAMIC_FTRACE */
int ftrace_event_is_function(struct ftrace_event_call *call);
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 7b16d40bd64d..83a4378dc5e0 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -188,6 +188,36 @@ int trace_event_raw_init(struct ftrace_event_call *call)
}
EXPORT_SYMBOL_GPL(trace_event_raw_init);
+void *ftrace_event_buffer_reserve(struct ftrace_event_buffer *fbuffer,
+ struct ftrace_event_file *ftrace_file,
+ unsigned long len)
+{
+ struct ftrace_event_call *event_call = ftrace_file->event_call;
+
+ local_save_flags(fbuffer->flags);
+ fbuffer->pc = preempt_count();
+ fbuffer->ftrace_file = ftrace_file;
+
+ fbuffer->event =
+ trace_event_buffer_lock_reserve(&fbuffer->buffer, ftrace_file,
+ event_call->event.type, len,
+ fbuffer->flags, fbuffer->pc);
+ if (!fbuffer->event)
+ return NULL;
+
+ fbuffer->entry = ring_buffer_event_data(fbuffer->event);
+ return fbuffer->entry;
+}
+EXPORT_SYMBOL_GPL(ftrace_event_buffer_reserve);
+
+void ftrace_event_buffer_commit(struct ftrace_event_buffer *fbuffer)
+{
+ event_trigger_unlock_commit(fbuffer->ftrace_file, fbuffer->buffer,
+ fbuffer->event, fbuffer->entry,
+ fbuffer->flags, fbuffer->pc);
+}
+EXPORT_SYMBOL_GPL(ftrace_event_buffer_commit);
+
int ftrace_event_reg(struct ftrace_event_call *call,
enum trace_reg type, void *data)
{
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
index 38fe1483c508..5b781d2be383 100644
--- a/kernel/trace/trace_functions.c
+++ b/kernel/trace/trace_functions.c
@@ -13,32 +13,106 @@
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/ftrace.h>
+#include <linux/slab.h>
#include <linux/fs.h>
#include "trace.h"
-/* function tracing enabled */
-static int ftrace_function_enabled;
+static void tracing_start_function_trace(struct trace_array *tr);
+static void tracing_stop_function_trace(struct trace_array *tr);
+static void
+function_trace_call(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *pt_regs);
+static void
+function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *pt_regs);
+static struct ftrace_ops trace_ops;
+static struct ftrace_ops trace_stack_ops;
+static struct tracer_flags func_flags;
+
+/* Our option */
+enum {
+ TRACE_FUNC_OPT_STACK = 0x1,
+};
+
+static int allocate_ftrace_ops(struct trace_array *tr)
+{
+ struct ftrace_ops *ops;
+
+ ops = kzalloc(sizeof(*ops), GFP_KERNEL);
+ if (!ops)
+ return -ENOMEM;
-static struct trace_array *func_trace;
+ /* Currently only the non stack verision is supported */
+ ops->func = function_trace_call;
+ ops->flags = FTRACE_OPS_FL_RECURSION_SAFE;
+
+ tr->ops = ops;
+ ops->private = tr;
+ return 0;
+}
+
+
+int ftrace_create_function_files(struct trace_array *tr,
+ struct dentry *parent)
+{
+ int ret;
+
+ /* The top level array uses the "global_ops". */
+ if (!(tr->flags & TRACE_ARRAY_FL_GLOBAL)) {
+ ret = allocate_ftrace_ops(tr);
+ if (ret)
+ return ret;
+ }
+
+ ftrace_create_filter_files(tr->ops, parent);
+
+ return 0;
+}
-static void tracing_start_function_trace(void);
-static void tracing_stop_function_trace(void);
+void ftrace_destroy_function_files(struct trace_array *tr)
+{
+ ftrace_destroy_filter_files(tr->ops);
+ kfree(tr->ops);
+ tr->ops = NULL;
+}
static int function_trace_init(struct trace_array *tr)
{
- func_trace = tr;
+ struct ftrace_ops *ops;
+
+ if (tr->flags & TRACE_ARRAY_FL_GLOBAL) {
+ /* There's only one global tr */
+ if (!trace_ops.private) {
+ trace_ops.private = tr;
+ trace_stack_ops.private = tr;
+ }
+
+ if (func_flags.val & TRACE_FUNC_OPT_STACK)
+ ops = &trace_stack_ops;
+ else
+ ops = &trace_ops;
+ tr->ops = ops;
+ } else if (!tr->ops) {
+ /*
+ * Instance trace_arrays get their ops allocated
+ * at instance creation. Unless it failed
+ * the allocation.
+ */
+ return -ENOMEM;
+ }
+
tr->trace_buffer.cpu = get_cpu();
put_cpu();
tracing_start_cmdline_record();
- tracing_start_function_trace();
+ tracing_start_function_trace(tr);
return 0;
}
static void function_trace_reset(struct trace_array *tr)
{
- tracing_stop_function_trace();
+ tracing_stop_function_trace(tr);
tracing_stop_cmdline_record();
}
@@ -47,25 +121,18 @@ static void function_trace_start(struct trace_array *tr)
tracing_reset_online_cpus(&tr->trace_buffer);
}
-/* Our option */
-enum {
- TRACE_FUNC_OPT_STACK = 0x1,
-};
-
-static struct tracer_flags func_flags;
-
static void
function_trace_call(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *pt_regs)
{
- struct trace_array *tr = func_trace;
+ struct trace_array *tr = op->private;
struct trace_array_cpu *data;
unsigned long flags;
int bit;
int cpu;
int pc;
- if (unlikely(!ftrace_function_enabled))
+ if (unlikely(!tr->function_enabled))
return;
pc = preempt_count();
@@ -91,14 +158,14 @@ static void
function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *pt_regs)
{
- struct trace_array *tr = func_trace;
+ struct trace_array *tr = op->private;
struct trace_array_cpu *data;
unsigned long flags;
long disabled;
int cpu;
int pc;
- if (unlikely(!ftrace_function_enabled))
+ if (unlikely(!tr->function_enabled))
return;
/*
@@ -128,7 +195,6 @@ function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
local_irq_restore(flags);
}
-
static struct ftrace_ops trace_ops __read_mostly =
{
.func = function_trace_call,
@@ -153,29 +219,21 @@ static struct tracer_flags func_flags = {
.opts = func_opts
};
-static void tracing_start_function_trace(void)
+static void tracing_start_function_trace(struct trace_array *tr)
{
- ftrace_function_enabled = 0;
-
- if (func_flags.val & TRACE_FUNC_OPT_STACK)
- register_ftrace_function(&trace_stack_ops);
- else
- register_ftrace_function(&trace_ops);
-
- ftrace_function_enabled = 1;
+ tr->function_enabled = 0;
+ register_ftrace_function(tr->ops);
+ tr->function_enabled = 1;
}
-static void tracing_stop_function_trace(void)
+static void tracing_stop_function_trace(struct trace_array *tr)
{
- ftrace_function_enabled = 0;
-
- if (func_flags.val & TRACE_FUNC_OPT_STACK)
- unregister_ftrace_function(&trace_stack_ops);
- else
- unregister_ftrace_function(&trace_ops);
+ tr->function_enabled = 0;
+ unregister_ftrace_function(tr->ops);
}
-static int func_set_flag(u32 old_flags, u32 bit, int set)
+static int
+func_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{
switch (bit) {
case TRACE_FUNC_OPT_STACK:
@@ -183,12 +241,14 @@ static int func_set_flag(u32 old_flags, u32 bit, int set)
if (!!set == !!(func_flags.val & TRACE_FUNC_OPT_STACK))
break;
+ unregister_ftrace_function(tr->ops);
+
if (set) {
- unregister_ftrace_function(&trace_ops);
- register_ftrace_function(&trace_stack_ops);
+ tr->ops = &trace_stack_ops;
+ register_ftrace_function(tr->ops);
} else {
- unregister_ftrace_function(&trace_stack_ops);
- register_ftrace_function(&trace_ops);
+ tr->ops = &trace_ops;
+ register_ftrace_function(tr->ops);
}
break;
@@ -208,6 +268,7 @@ static struct tracer function_trace __tracer_data =
.wait_pipe = poll_wait_pipe,
.flags = &func_flags,
.set_flag = func_set_flag,
+ .allow_instances = true,
#ifdef CONFIG_FTRACE_SELFTEST
.selftest = trace_selftest_startup_function,
#endif
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index 0b99120d395c..deff11200261 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -1476,7 +1476,8 @@ void graph_trace_close(struct trace_iterator *iter)
}
}
-static int func_graph_set_flag(u32 old_flags, u32 bit, int set)
+static int
+func_graph_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{
if (bit == TRACE_GRAPH_PRINT_IRQS)
ftrace_graph_skip_irqs = !set;
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index 887ef88b0bc7..8ff02cbb892f 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -160,7 +160,8 @@ static struct ftrace_ops trace_ops __read_mostly =
#endif /* CONFIG_FUNCTION_TRACER */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-static int irqsoff_set_flag(u32 old_flags, u32 bit, int set)
+static int
+irqsoff_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{
int cpu;
@@ -266,7 +267,8 @@ __trace_function(struct trace_array *tr,
#else
#define __trace_function trace_function
-static int irqsoff_set_flag(u32 old_flags, u32 bit, int set)
+static int
+irqsoff_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{
return -EINVAL;
}
@@ -570,8 +572,10 @@ static void irqsoff_function_set(int set)
unregister_irqsoff_function(is_graph());
}
-static int irqsoff_flag_changed(struct tracer *tracer, u32 mask, int set)
+static int irqsoff_flag_changed(struct trace_array *tr, u32 mask, int set)
{
+ struct tracer *tracer = tr->current_trace;
+
if (mask & TRACE_ITER_FUNCTION)
irqsoff_function_set(set);
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index bdbae450c13e..d021d21dd150 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -35,11 +35,6 @@ struct trace_kprobe {
struct trace_probe tp;
};
-struct event_file_link {
- struct ftrace_event_file *file;
- struct list_head list;
-};
-
#define SIZEOF_TRACE_KPROBE(n) \
(offsetof(struct trace_kprobe, tp.args) + \
(sizeof(struct probe_arg) * (n)))
@@ -387,18 +382,6 @@ enable_trace_kprobe(struct trace_kprobe *tk, struct ftrace_event_file *file)
return ret;
}
-static struct event_file_link *
-find_event_file_link(struct trace_probe *tp, struct ftrace_event_file *file)
-{
- struct event_file_link *link;
-
- list_for_each_entry(link, &tp->files, list)
- if (link->file == file)
- return link;
-
- return NULL;
-}
-
/*
* Disable trace_probe
* if the file is NULL, disable "perf" handler, or disable "trace" handler.
diff --git a/kernel/trace/trace_nop.c b/kernel/trace/trace_nop.c
index 394f94417e2f..69a5cc94c01a 100644
--- a/kernel/trace/trace_nop.c
+++ b/kernel/trace/trace_nop.c
@@ -62,7 +62,7 @@ static void nop_trace_reset(struct trace_array *tr)
* If you don't implement it, then the flag setting will be
* automatically accepted.
*/
-static int nop_set_flag(u32 old_flags, u32 bit, int set)
+static int nop_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{
/*
* Note that you don't need to update nop_flags.val yourself.
@@ -96,6 +96,7 @@ struct tracer nop_trace __read_mostly =
.selftest = trace_selftest_startup_nop,
#endif
.flags = &nop_flags,
- .set_flag = nop_set_flag
+ .set_flag = nop_set_flag,
+ .allow_instances = true,
};
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c
index ed32284fbe32..ca0e79e2abaa 100644
--- a/kernel/trace/trace_output.c
+++ b/kernel/trace/trace_output.c
@@ -439,6 +439,37 @@ int ftrace_raw_output_prep(struct trace_iterator *iter,
}
EXPORT_SYMBOL(ftrace_raw_output_prep);
+static int ftrace_output_raw(struct trace_iterator *iter, char *name,
+ char *fmt, va_list ap)
+{
+ struct trace_seq *s = &iter->seq;
+ int ret;
+
+ ret = trace_seq_printf(s, "%s: ", name);
+ if (!ret)
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ ret = trace_seq_vprintf(s, fmt, ap);
+
+ if (!ret)
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ return TRACE_TYPE_HANDLED;
+}
+
+int ftrace_output_call(struct trace_iterator *iter, char *name, char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = ftrace_output_raw(iter, name, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ftrace_output_call);
+
#ifdef CONFIG_KRETPROBES
static inline const char *kretprobed(const char *name)
{
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index b73574a5f429..fb1ab5dfbd42 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -288,6 +288,11 @@ struct trace_probe {
struct probe_arg args[];
};
+struct event_file_link {
+ struct ftrace_event_file *file;
+ struct list_head list;
+};
+
static inline bool trace_probe_is_enabled(struct trace_probe *tp)
{
return !!(tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE));
@@ -316,6 +321,18 @@ static inline int is_good_name(const char *name)
return 1;
}
+static inline struct event_file_link *
+find_event_file_link(struct trace_probe *tp, struct ftrace_event_file *file)
+{
+ struct event_file_link *link;
+
+ list_for_each_entry(link, &tp->files, list)
+ if (link->file == file)
+ return link;
+
+ return NULL;
+}
+
extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
struct probe_arg *parg, bool is_return, bool is_kprobe);
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
index 6e32635e5e57..e14da5e97a69 100644
--- a/kernel/trace/trace_sched_wakeup.c
+++ b/kernel/trace/trace_sched_wakeup.c
@@ -179,8 +179,10 @@ static void wakeup_function_set(int set)
unregister_wakeup_function(is_graph());
}
-static int wakeup_flag_changed(struct tracer *tracer, u32 mask, int set)
+static int wakeup_flag_changed(struct trace_array *tr, u32 mask, int set)
{
+ struct tracer *tracer = tr->current_trace;
+
if (mask & TRACE_ITER_FUNCTION)
wakeup_function_set(set);
@@ -209,7 +211,8 @@ static void stop_func_tracer(int graph)
}
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-static int wakeup_set_flag(u32 old_flags, u32 bit, int set)
+static int
+wakeup_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{
if (!(bit & TRACE_DISPLAY_GRAPH))
@@ -311,7 +314,8 @@ __trace_function(struct trace_array *tr,
#else
#define __trace_function trace_function
-static int wakeup_set_flag(u32 old_flags, u32 bit, int set)
+static int
+wakeup_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
{
return -EINVAL;
}
diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c
index e6be585cf06a..21b320e5d163 100644
--- a/kernel/trace/trace_stack.c
+++ b/kernel/trace/trace_stack.c
@@ -13,6 +13,7 @@
#include <linux/sysctl.h>
#include <linux/init.h>
#include <linux/fs.h>
+#include <linux/magic.h>
#include <asm/setup.h>
@@ -144,6 +145,8 @@ check_stack(unsigned long ip, unsigned long *stack)
i++;
}
+ BUG_ON(current != &init_task &&
+ *(end_of_stack(current)) != STACK_END_MAGIC);
out:
arch_spin_unlock(&max_stack_lock);
local_irq_restore(flags);
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index 79e52d93860b..e4473367e7a4 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -260,6 +260,7 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret)
goto error;
INIT_LIST_HEAD(&tu->list);
+ INIT_LIST_HEAD(&tu->tp.files);
tu->consumer.handler = uprobe_dispatcher;
if (is_ret)
tu->consumer.ret_handler = uretprobe_dispatcher;
@@ -758,31 +759,32 @@ static void uprobe_buffer_put(struct uprobe_cpu_buffer *ucb)
mutex_unlock(&ucb->mutex);
}
-static void uprobe_trace_print(struct trace_uprobe *tu,
- unsigned long func, struct pt_regs *regs)
+static void __uprobe_trace_func(struct trace_uprobe *tu,
+ unsigned long func, struct pt_regs *regs,
+ struct uprobe_cpu_buffer *ucb, int dsize,
+ struct ftrace_event_file *ftrace_file)
{
struct uprobe_trace_entry_head *entry;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
- struct uprobe_cpu_buffer *ucb;
void *data;
- int size, dsize, esize;
+ int size, esize;
struct ftrace_event_call *call = &tu->tp.call;
- dsize = __get_data_size(&tu->tp, regs);
- esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
+ WARN_ON(call != ftrace_file->event_call);
- if (WARN_ON_ONCE(!uprobe_cpu_buffer || tu->tp.size + dsize > PAGE_SIZE))
+ if (WARN_ON_ONCE(tu->tp.size + dsize > PAGE_SIZE))
return;
- ucb = uprobe_buffer_get();
- store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize);
+ if (ftrace_trigger_soft_disabled(ftrace_file))
+ return;
+ esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
size = esize + tu->tp.size + dsize;
- event = trace_current_buffer_lock_reserve(&buffer, call->event.type,
- size, 0, 0);
+ event = trace_event_buffer_lock_reserve(&buffer, ftrace_file,
+ call->event.type, size, 0, 0);
if (!event)
- goto out;
+ return;
entry = ring_buffer_event_data(event);
if (is_ret_probe(tu)) {
@@ -796,25 +798,36 @@ static void uprobe_trace_print(struct trace_uprobe *tu,
memcpy(data, ucb->buf, tu->tp.size + dsize);
- if (!call_filter_check_discard(call, entry, buffer, event))
- trace_buffer_unlock_commit(buffer, event, 0, 0);
-
-out:
- uprobe_buffer_put(ucb);
+ event_trigger_unlock_commit(ftrace_file, buffer, event, entry, 0, 0);
}
/* uprobe handler */
-static int uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs)
+static int uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs,
+ struct uprobe_cpu_buffer *ucb, int dsize)
{
- if (!is_ret_probe(tu))
- uprobe_trace_print(tu, 0, regs);
+ struct event_file_link *link;
+
+ if (is_ret_probe(tu))
+ return 0;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(link, &tu->tp.files, list)
+ __uprobe_trace_func(tu, 0, regs, ucb, dsize, link->file);
+ rcu_read_unlock();
+
return 0;
}
static void uretprobe_trace_func(struct trace_uprobe *tu, unsigned long func,
- struct pt_regs *regs)
+ struct pt_regs *regs,
+ struct uprobe_cpu_buffer *ucb, int dsize)
{
- uprobe_trace_print(tu, func, regs);
+ struct event_file_link *link;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(link, &tu->tp.files, list)
+ __uprobe_trace_func(tu, func, regs, ucb, dsize, link->file);
+ rcu_read_unlock();
}
/* Event entry printers */
@@ -861,12 +874,24 @@ typedef bool (*filter_func_t)(struct uprobe_consumer *self,
struct mm_struct *mm);
static int
-probe_event_enable(struct trace_uprobe *tu, int flag, filter_func_t filter)
+probe_event_enable(struct trace_uprobe *tu, struct ftrace_event_file *file,
+ filter_func_t filter)
{
- int ret = 0;
+ bool enabled = trace_probe_is_enabled(&tu->tp);
+ struct event_file_link *link = NULL;
+ int ret;
+
+ if (file) {
+ link = kmalloc(sizeof(*link), GFP_KERNEL);
+ if (!link)
+ return -ENOMEM;
- if (trace_probe_is_enabled(&tu->tp))
- return -EINTR;
+ link->file = file;
+ list_add_tail_rcu(&link->list, &tu->tp.files);
+
+ tu->tp.flags |= TP_FLAG_TRACE;
+ } else
+ tu->tp.flags |= TP_FLAG_PROFILE;
ret = uprobe_buffer_enable();
if (ret < 0)
@@ -874,24 +899,49 @@ probe_event_enable(struct trace_uprobe *tu, int flag, filter_func_t filter)
WARN_ON(!uprobe_filter_is_empty(&tu->filter));
- tu->tp.flags |= flag;
+ if (enabled)
+ return 0;
+
tu->consumer.filter = filter;
ret = uprobe_register(tu->inode, tu->offset, &tu->consumer);
- if (ret)
- tu->tp.flags &= ~flag;
+ if (ret) {
+ if (file) {
+ list_del(&link->list);
+ kfree(link);
+ tu->tp.flags &= ~TP_FLAG_TRACE;
+ } else
+ tu->tp.flags &= ~TP_FLAG_PROFILE;
+ }
return ret;
}
-static void probe_event_disable(struct trace_uprobe *tu, int flag)
+static void
+probe_event_disable(struct trace_uprobe *tu, struct ftrace_event_file *file)
{
if (!trace_probe_is_enabled(&tu->tp))
return;
+ if (file) {
+ struct event_file_link *link;
+
+ link = find_event_file_link(&tu->tp, file);
+ if (!link)
+ return;
+
+ list_del_rcu(&link->list);
+ /* synchronize with u{,ret}probe_trace_func */
+ synchronize_sched();
+ kfree(link);
+
+ if (!list_empty(&tu->tp.files))
+ return;
+ }
+
WARN_ON(!uprobe_filter_is_empty(&tu->filter));
uprobe_unregister(tu->inode, tu->offset, &tu->consumer);
- tu->tp.flags &= ~flag;
+ tu->tp.flags &= file ? ~TP_FLAG_TRACE : ~TP_FLAG_PROFILE;
uprobe_buffer_disable();
}
@@ -1014,31 +1064,24 @@ static bool uprobe_perf_filter(struct uprobe_consumer *uc,
return ret;
}
-static void uprobe_perf_print(struct trace_uprobe *tu,
- unsigned long func, struct pt_regs *regs)
+static void __uprobe_perf_func(struct trace_uprobe *tu,
+ unsigned long func, struct pt_regs *regs,
+ struct uprobe_cpu_buffer *ucb, int dsize)
{
struct ftrace_event_call *call = &tu->tp.call;
struct uprobe_trace_entry_head *entry;
struct hlist_head *head;
- struct uprobe_cpu_buffer *ucb;
void *data;
- int size, dsize, esize;
+ int size, esize;
int rctx;
- dsize = __get_data_size(&tu->tp, regs);
esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
- if (WARN_ON_ONCE(!uprobe_cpu_buffer))
- return;
-
size = esize + tu->tp.size + dsize;
size = ALIGN(size + sizeof(u32), sizeof(u64)) - sizeof(u32);
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough"))
return;
- ucb = uprobe_buffer_get();
- store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize);
-
preempt_disable();
head = this_cpu_ptr(call->perf_events);
if (hlist_empty(head))
@@ -1068,46 +1111,49 @@ static void uprobe_perf_print(struct trace_uprobe *tu,
perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL);
out:
preempt_enable();
- uprobe_buffer_put(ucb);
}
/* uprobe profile handler */
-static int uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs)
+static int uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs,
+ struct uprobe_cpu_buffer *ucb, int dsize)
{
if (!uprobe_perf_filter(&tu->consumer, 0, current->mm))
return UPROBE_HANDLER_REMOVE;
if (!is_ret_probe(tu))
- uprobe_perf_print(tu, 0, regs);
+ __uprobe_perf_func(tu, 0, regs, ucb, dsize);
return 0;
}
static void uretprobe_perf_func(struct trace_uprobe *tu, unsigned long func,
- struct pt_regs *regs)
+ struct pt_regs *regs,
+ struct uprobe_cpu_buffer *ucb, int dsize)
{
- uprobe_perf_print(tu, func, regs);
+ __uprobe_perf_func(tu, func, regs, ucb, dsize);
}
#endif /* CONFIG_PERF_EVENTS */
-static
-int trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type, void *data)
+static int
+trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type,
+ void *data)
{
struct trace_uprobe *tu = event->data;
+ struct ftrace_event_file *file = data;
switch (type) {
case TRACE_REG_REGISTER:
- return probe_event_enable(tu, TP_FLAG_TRACE, NULL);
+ return probe_event_enable(tu, file, NULL);
case TRACE_REG_UNREGISTER:
- probe_event_disable(tu, TP_FLAG_TRACE);
+ probe_event_disable(tu, file);
return 0;
#ifdef CONFIG_PERF_EVENTS
case TRACE_REG_PERF_REGISTER:
- return probe_event_enable(tu, TP_FLAG_PROFILE, uprobe_perf_filter);
+ return probe_event_enable(tu, NULL, uprobe_perf_filter);
case TRACE_REG_PERF_UNREGISTER:
- probe_event_disable(tu, TP_FLAG_PROFILE);
+ probe_event_disable(tu, NULL);
return 0;
case TRACE_REG_PERF_OPEN:
@@ -1127,8 +1173,11 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs)
{
struct trace_uprobe *tu;
struct uprobe_dispatch_data udd;
+ struct uprobe_cpu_buffer *ucb;
+ int dsize, esize;
int ret = 0;
+
tu = container_of(con, struct trace_uprobe, consumer);
tu->nhit++;
@@ -1137,13 +1186,29 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs)
current->utask->vaddr = (unsigned long) &udd;
+#ifdef CONFIG_PERF_EVENTS
+ if ((tu->tp.flags & TP_FLAG_TRACE) == 0 &&
+ !uprobe_perf_filter(&tu->consumer, 0, current->mm))
+ return UPROBE_HANDLER_REMOVE;
+#endif
+
+ if (WARN_ON_ONCE(!uprobe_cpu_buffer))
+ return 0;
+
+ dsize = __get_data_size(&tu->tp, regs);
+ esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
+
+ ucb = uprobe_buffer_get();
+ store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize);
+
if (tu->tp.flags & TP_FLAG_TRACE)
- ret |= uprobe_trace_func(tu, regs);
+ ret |= uprobe_trace_func(tu, regs, ucb, dsize);
#ifdef CONFIG_PERF_EVENTS
if (tu->tp.flags & TP_FLAG_PROFILE)
- ret |= uprobe_perf_func(tu, regs);
+ ret |= uprobe_perf_func(tu, regs, ucb, dsize);
#endif
+ uprobe_buffer_put(ucb);
return ret;
}
@@ -1152,6 +1217,8 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con,
{
struct trace_uprobe *tu;
struct uprobe_dispatch_data udd;
+ struct uprobe_cpu_buffer *ucb;
+ int dsize, esize;
tu = container_of(con, struct trace_uprobe, consumer);
@@ -1160,13 +1227,23 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con,
current->utask->vaddr = (unsigned long) &udd;
+ if (WARN_ON_ONCE(!uprobe_cpu_buffer))
+ return 0;
+
+ dsize = __get_data_size(&tu->tp, regs);
+ esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
+
+ ucb = uprobe_buffer_get();
+ store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize);
+
if (tu->tp.flags & TP_FLAG_TRACE)
- uretprobe_trace_func(tu, func, regs);
+ uretprobe_trace_func(tu, func, regs, ucb, dsize);
#ifdef CONFIG_PERF_EVENTS
if (tu->tp.flags & TP_FLAG_PROFILE)
- uretprobe_perf_func(tu, func, regs);
+ uretprobe_perf_func(tu, func, regs, ucb, dsize);
#endif
+ uprobe_buffer_put(ucb);
return 0;
}
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c
index 031cc5655a51..fb0a38a26555 100644
--- a/kernel/tracepoint.c
+++ b/kernel/tracepoint.c
@@ -62,14 +62,12 @@ struct tracepoint_entry {
struct hlist_node hlist;
struct tracepoint_func *funcs;
int refcount; /* Number of times armed. 0 if disarmed. */
+ int enabled; /* Tracepoint enabled */
char name[0];
};
struct tp_probes {
- union {
- struct rcu_head rcu;
- struct list_head list;
- } u;
+ struct rcu_head rcu;
struct tracepoint_func probes[0];
};
@@ -82,7 +80,7 @@ static inline void *allocate_probes(int count)
static void rcu_free_old_probes(struct rcu_head *head)
{
- kfree(container_of(head, struct tp_probes, u.rcu));
+ kfree(container_of(head, struct tp_probes, rcu));
}
static inline void release_probes(struct tracepoint_func *old)
@@ -90,7 +88,7 @@ static inline void release_probes(struct tracepoint_func *old)
if (old) {
struct tp_probes *tp_probes = container_of(old,
struct tp_probes, probes[0]);
- call_rcu_sched(&tp_probes->u.rcu, rcu_free_old_probes);
+ call_rcu_sched(&tp_probes->rcu, rcu_free_old_probes);
}
}
@@ -237,6 +235,7 @@ static struct tracepoint_entry *add_tracepoint(const char *name)
memcpy(&e->name[0], name, name_len);
e->funcs = NULL;
e->refcount = 0;
+ e->enabled = 0;
hlist_add_head(&e->hlist, head);
return e;
}
@@ -316,6 +315,7 @@ static void tracepoint_update_probe_range(struct tracepoint * const *begin,
if (mark_entry) {
set_tracepoint(&mark_entry, *iter,
!!mark_entry->refcount);
+ mark_entry->enabled = !!mark_entry->refcount;
} else {
disable_tracepoint(*iter);
}
@@ -373,13 +373,26 @@ tracepoint_add_probe(const char *name, void *probe, void *data)
* tracepoint_probe_register - Connect a probe to a tracepoint
* @name: tracepoint name
* @probe: probe handler
+ * @data: probe private data
+ *
+ * Returns:
+ * - 0 if the probe was successfully registered, and tracepoint
+ * callsites are currently loaded for that probe,
+ * - -ENODEV if the probe was successfully registered, but no tracepoint
+ * callsite is currently loaded for that probe,
+ * - other negative error value on error.
+ *
+ * When tracepoint_probe_register() returns either 0 or -ENODEV,
+ * parameters @name, @probe, and @data may be used by the tracepoint
+ * infrastructure until the probe is unregistered.
*
- * Returns 0 if ok, error value on error.
* The probe address must at least be aligned on the architecture pointer size.
*/
int tracepoint_probe_register(const char *name, void *probe, void *data)
{
struct tracepoint_func *old;
+ struct tracepoint_entry *entry;
+ int ret = 0;
mutex_lock(&tracepoints_mutex);
old = tracepoint_add_probe(name, probe, data);
@@ -388,9 +401,13 @@ int tracepoint_probe_register(const char *name, void *probe, void *data)
return PTR_ERR(old);
}
tracepoint_update_probes(); /* may update entry */
+ entry = get_tracepoint(name);
+ /* Make sure the entry was enabled */
+ if (!entry || !entry->enabled)
+ ret = -ENODEV;
mutex_unlock(&tracepoints_mutex);
release_probes(old);
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(tracepoint_probe_register);
@@ -415,6 +432,7 @@ tracepoint_remove_probe(const char *name, void *probe, void *data)
* tracepoint_probe_unregister - Disconnect a probe from a tracepoint
* @name: tracepoint name
* @probe: probe function pointer
+ * @data: probe private data
*
* We do not need to call a synchronize_sched to make sure the probes have
* finished running before doing a module unload, because the module unload
@@ -438,213 +456,26 @@ int tracepoint_probe_unregister(const char *name, void *probe, void *data)
}
EXPORT_SYMBOL_GPL(tracepoint_probe_unregister);
-static LIST_HEAD(old_probes);
-static int need_update;
-
-static void tracepoint_add_old_probes(void *old)
-{
- need_update = 1;
- if (old) {
- struct tp_probes *tp_probes = container_of(old,
- struct tp_probes, probes[0]);
- list_add(&tp_probes->u.list, &old_probes);
- }
-}
-
-/**
- * tracepoint_probe_register_noupdate - register a probe but not connect
- * @name: tracepoint name
- * @probe: probe handler
- *
- * caller must call tracepoint_probe_update_all()
- */
-int tracepoint_probe_register_noupdate(const char *name, void *probe,
- void *data)
-{
- struct tracepoint_func *old;
-
- mutex_lock(&tracepoints_mutex);
- old = tracepoint_add_probe(name, probe, data);
- if (IS_ERR(old)) {
- mutex_unlock(&tracepoints_mutex);
- return PTR_ERR(old);
- }
- tracepoint_add_old_probes(old);
- mutex_unlock(&tracepoints_mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(tracepoint_probe_register_noupdate);
-
-/**
- * tracepoint_probe_unregister_noupdate - remove a probe but not disconnect
- * @name: tracepoint name
- * @probe: probe function pointer
- *
- * caller must call tracepoint_probe_update_all()
- */
-int tracepoint_probe_unregister_noupdate(const char *name, void *probe,
- void *data)
-{
- struct tracepoint_func *old;
-
- mutex_lock(&tracepoints_mutex);
- old = tracepoint_remove_probe(name, probe, data);
- if (IS_ERR(old)) {
- mutex_unlock(&tracepoints_mutex);
- return PTR_ERR(old);
- }
- tracepoint_add_old_probes(old);
- mutex_unlock(&tracepoints_mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(tracepoint_probe_unregister_noupdate);
-
-/**
- * tracepoint_probe_update_all - update tracepoints
- */
-void tracepoint_probe_update_all(void)
-{
- LIST_HEAD(release_probes);
- struct tp_probes *pos, *next;
-
- mutex_lock(&tracepoints_mutex);
- if (!need_update) {
- mutex_unlock(&tracepoints_mutex);
- return;
- }
- if (!list_empty(&old_probes))
- list_replace_init(&old_probes, &release_probes);
- need_update = 0;
- tracepoint_update_probes();
- mutex_unlock(&tracepoints_mutex);
- list_for_each_entry_safe(pos, next, &release_probes, u.list) {
- list_del(&pos->u.list);
- call_rcu_sched(&pos->u.rcu, rcu_free_old_probes);
- }
-}
-EXPORT_SYMBOL_GPL(tracepoint_probe_update_all);
-
-/**
- * tracepoint_get_iter_range - Get a next tracepoint iterator given a range.
- * @tracepoint: current tracepoints (in), next tracepoint (out)
- * @begin: beginning of the range
- * @end: end of the range
- *
- * Returns whether a next tracepoint has been found (1) or not (0).
- * Will return the first tracepoint in the range if the input tracepoint is
- * NULL.
- */
-static int tracepoint_get_iter_range(struct tracepoint * const **tracepoint,
- struct tracepoint * const *begin, struct tracepoint * const *end)
-{
- if (!*tracepoint && begin != end) {
- *tracepoint = begin;
- return 1;
- }
- if (*tracepoint >= begin && *tracepoint < end)
- return 1;
- return 0;
-}
-
-#ifdef CONFIG_MODULES
-static void tracepoint_get_iter(struct tracepoint_iter *iter)
-{
- int found = 0;
- struct tp_module *iter_mod;
-
- /* Core kernel tracepoints */
- if (!iter->module) {
- found = tracepoint_get_iter_range(&iter->tracepoint,
- __start___tracepoints_ptrs,
- __stop___tracepoints_ptrs);
- if (found)
- goto end;
- }
- /* Tracepoints in modules */
- mutex_lock(&tracepoints_mutex);
- list_for_each_entry(iter_mod, &tracepoint_module_list, list) {
- /*
- * Sorted module list
- */
- if (iter_mod < iter->module)
- continue;
- else if (iter_mod > iter->module)
- iter->tracepoint = NULL;
- found = tracepoint_get_iter_range(&iter->tracepoint,
- iter_mod->tracepoints_ptrs,
- iter_mod->tracepoints_ptrs
- + iter_mod->num_tracepoints);
- if (found) {
- iter->module = iter_mod;
- break;
- }
- }
- mutex_unlock(&tracepoints_mutex);
-end:
- if (!found)
- tracepoint_iter_reset(iter);
-}
-#else /* CONFIG_MODULES */
-static void tracepoint_get_iter(struct tracepoint_iter *iter)
-{
- int found = 0;
-
- /* Core kernel tracepoints */
- found = tracepoint_get_iter_range(&iter->tracepoint,
- __start___tracepoints_ptrs,
- __stop___tracepoints_ptrs);
- if (!found)
- tracepoint_iter_reset(iter);
-}
-#endif /* CONFIG_MODULES */
-
-void tracepoint_iter_start(struct tracepoint_iter *iter)
-{
- tracepoint_get_iter(iter);
-}
-EXPORT_SYMBOL_GPL(tracepoint_iter_start);
-
-void tracepoint_iter_next(struct tracepoint_iter *iter)
-{
- iter->tracepoint++;
- /*
- * iter->tracepoint may be invalid because we blindly incremented it.
- * Make sure it is valid by marshalling on the tracepoints, getting the
- * tracepoints from following modules if necessary.
- */
- tracepoint_get_iter(iter);
-}
-EXPORT_SYMBOL_GPL(tracepoint_iter_next);
-
-void tracepoint_iter_stop(struct tracepoint_iter *iter)
-{
-}
-EXPORT_SYMBOL_GPL(tracepoint_iter_stop);
-
-void tracepoint_iter_reset(struct tracepoint_iter *iter)
-{
-#ifdef CONFIG_MODULES
- iter->module = NULL;
-#endif /* CONFIG_MODULES */
- iter->tracepoint = NULL;
-}
-EXPORT_SYMBOL_GPL(tracepoint_iter_reset);
#ifdef CONFIG_MODULES
bool trace_module_has_bad_taint(struct module *mod)
{
- return mod->taints & ~((1 << TAINT_OOT_MODULE) | (1 << TAINT_CRAP));
+ return mod->taints & ~((1 << TAINT_OOT_MODULE) | (1 << TAINT_CRAP) |
+ (1 << TAINT_UNSIGNED_MODULE));
}
static int tracepoint_module_coming(struct module *mod)
{
- struct tp_module *tp_mod, *iter;
+ struct tp_module *tp_mod;
int ret = 0;
+ if (!mod->num_tracepoints)
+ return 0;
+
/*
* We skip modules that taint the kernel, especially those with different
* module headers (for forced load), to make sure we don't cause a crash.
- * Staging and out-of-tree GPL modules are fine.
+ * Staging, out-of-tree, and unsigned GPL modules are fine.
*/
if (trace_module_has_bad_taint(mod))
return 0;
@@ -656,23 +487,7 @@ static int tracepoint_module_coming(struct module *mod)
}
tp_mod->num_tracepoints = mod->num_tracepoints;
tp_mod->tracepoints_ptrs = mod->tracepoints_ptrs;
-
- /*
- * tracepoint_module_list is kept sorted by struct module pointer
- * address for iteration on tracepoints from a seq_file that can release
- * the mutex between calls.
- */
- list_for_each_entry_reverse(iter, &tracepoint_module_list, list) {
- BUG_ON(iter == tp_mod); /* Should never be in the list twice */
- if (iter < tp_mod) {
- /* We belong to the location right after iter. */
- list_add(&tp_mod->list, &iter->list);
- goto module_added;
- }
- }
- /* We belong to the beginning of the list */
- list_add(&tp_mod->list, &tracepoint_module_list);
-module_added:
+ list_add_tail(&tp_mod->list, &tracepoint_module_list);
tracepoint_update_probe_range(mod->tracepoints_ptrs,
mod->tracepoints_ptrs + mod->num_tracepoints);
end:
@@ -684,6 +499,9 @@ static int tracepoint_module_going(struct module *mod)
{
struct tp_module *pos;
+ if (!mod->num_tracepoints)
+ return 0;
+
mutex_lock(&tracepoints_mutex);
tracepoint_update_probe_range(mod->tracepoints_ptrs,
mod->tracepoints_ptrs + mod->num_tracepoints);
diff --git a/kernel/user.c b/kernel/user.c
index c006131beb77..294fc6a94168 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -222,5 +222,4 @@ static int __init uid_cache_init(void)
return 0;
}
-
-module_init(uid_cache_init);
+subsys_initcall(uid_cache_init);
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index dd06439b9c84..0d8f6023fd8d 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -902,4 +902,4 @@ static __init int user_namespaces_init(void)
user_ns_cachep = KMEM_CACHE(user_namespace, SLAB_PANIC);
return 0;
}
-module_init(user_namespaces_init);
+subsys_initcall(user_namespaces_init);
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 01c6f979486f..e90089fd78e0 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -158,14 +158,14 @@ void touch_all_softlockup_watchdogs(void)
#ifdef CONFIG_HARDLOCKUP_DETECTOR
void touch_nmi_watchdog(void)
{
- if (watchdog_user_enabled) {
- unsigned cpu;
-
- for_each_present_cpu(cpu) {
- if (per_cpu(watchdog_nmi_touch, cpu) != true)
- per_cpu(watchdog_nmi_touch, cpu) = true;
- }
- }
+ /*
+ * Using __raw here because some code paths have
+ * preemption enabled. If preemption is enabled
+ * then interrupts should be enabled too, in which
+ * case we shouldn't have to worry about the watchdog
+ * going off.
+ */
+ __raw_get_cpu_var(watchdog_nmi_touch) = true;
touch_softlockup_watchdog();
}
EXPORT_SYMBOL(touch_nmi_watchdog);
diff --git a/lib/clz_ctz.c b/lib/clz_ctz.c
index a8f8379eb49f..2e11e48446ab 100644
--- a/lib/clz_ctz.c
+++ b/lib/clz_ctz.c
@@ -6,6 +6,9 @@
* 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.
+ * The functions in this file aren't called directly, but are required by
+ * GCC builtins such as __builtin_ctz, and therefore they can't be removed
+ * despite appearing unreferenced in kernel source.
*
* __c[lt]z[sd]i2 can be overridden by linking arch-specific versions.
*/
@@ -13,18 +16,22 @@
#include <linux/export.h>
#include <linux/kernel.h>
+int __weak __ctzsi2(int val);
int __weak __ctzsi2(int val)
{
return __ffs(val);
}
EXPORT_SYMBOL(__ctzsi2);
+int __weak __clzsi2(int val);
int __weak __clzsi2(int val)
{
return 32 - fls(val);
}
EXPORT_SYMBOL(__clzsi2);
+int __weak __clzdi2(long val);
+int __weak __ctzdi2(long val);
#if BITS_PER_LONG == 32
int __weak __clzdi2(long val)
diff --git a/lib/decompress_inflate.c b/lib/decompress_inflate.c
index d619b28c456f..0edfd742a154 100644
--- a/lib/decompress_inflate.c
+++ b/lib/decompress_inflate.c
@@ -19,6 +19,7 @@
#include "zlib_inflate/inflate.h"
#include "zlib_inflate/infutil.h"
+#include <linux/decompress/inflate.h>
#endif /* STATIC */
diff --git a/lib/devres.c b/lib/devres.c
index 823533138fa0..48cb3c7bd7de 100644
--- a/lib/devres.c
+++ b/lib/devres.c
@@ -81,11 +81,13 @@ EXPORT_SYMBOL(devm_ioremap_nocache);
void devm_iounmap(struct device *dev, void __iomem *addr)
{
WARN_ON(devres_destroy(dev, devm_ioremap_release, devm_ioremap_match,
- (void *)addr));
+ (__force void *)addr));
iounmap(addr);
}
EXPORT_SYMBOL(devm_iounmap);
+#define IOMEM_ERR_PTR(err) (__force void __iomem *)ERR_PTR(err)
+
/**
* devm_ioremap_resource() - check, request region, and ioremap resource
* @dev: generic device to handle the resource for
@@ -114,7 +116,7 @@ void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res)
if (!res || resource_type(res) != IORESOURCE_MEM) {
dev_err(dev, "invalid resource\n");
- return ERR_PTR(-EINVAL);
+ return IOMEM_ERR_PTR(-EINVAL);
}
size = resource_size(res);
@@ -122,7 +124,7 @@ void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res)
if (!devm_request_mem_region(dev, res->start, size, name)) {
dev_err(dev, "can't request region for resource %pR\n", res);
- return ERR_PTR(-EBUSY);
+ return IOMEM_ERR_PTR(-EBUSY);
}
if (res->flags & IORESOURCE_CACHEABLE)
@@ -133,7 +135,7 @@ void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res)
if (!dest_ptr) {
dev_err(dev, "ioremap failed for resource %pR\n", res);
devm_release_mem_region(dev, res->start, size);
- dest_ptr = ERR_PTR(-ENOMEM);
+ dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
}
return dest_ptr;
@@ -224,7 +226,7 @@ void devm_ioport_unmap(struct device *dev, void __iomem *addr)
{
ioport_unmap(addr);
WARN_ON(devres_destroy(dev, devm_ioport_map_release,
- devm_ioport_map_match, (void *)addr));
+ devm_ioport_map_match, (__force void *)addr));
}
EXPORT_SYMBOL(devm_ioport_unmap);
#endif /* CONFIG_HAS_IOPORT */
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index 5f72767ddd9b..4e3bd71bd949 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -124,6 +124,30 @@ static int kobj_usermode_filter(struct kobject *kobj)
return 0;
}
+static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem)
+{
+ int len;
+
+ len = strlcpy(&env->buf[env->buflen], subsystem,
+ sizeof(env->buf) - env->buflen);
+ if (len >= (sizeof(env->buf) - env->buflen)) {
+ WARN(1, KERN_ERR "init_uevent_argv: buffer size too small\n");
+ return -ENOMEM;
+ }
+
+ env->argv[0] = uevent_helper;
+ env->argv[1] = &env->buf[env->buflen];
+ env->argv[2] = NULL;
+
+ env->buflen += len + 1;
+ return 0;
+}
+
+static void cleanup_uevent_env(struct subprocess_info *info)
+{
+ kfree(info->data);
+}
+
/**
* kobject_uevent_env - send an uevent with environmental data
*
@@ -301,11 +325,8 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
/* call uevent_helper, usually only enabled during early boot */
if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
- char *argv [3];
+ struct subprocess_info *info;
- argv [0] = uevent_helper;
- argv [1] = (char *)subsystem;
- argv [2] = NULL;
retval = add_uevent_var(env, "HOME=/");
if (retval)
goto exit;
@@ -313,9 +334,18 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
if (retval)
goto exit;
+ retval = init_uevent_argv(env, subsystem);
+ if (retval)
+ goto exit;
- retval = call_usermodehelper(argv[0], argv,
- env->envp, UMH_WAIT_EXEC);
+ retval = -ENOMEM;
+ info = call_usermodehelper_setup(env->argv[0], env->argv,
+ env->envp, GFP_KERNEL,
+ NULL, cleanup_uevent_env, env);
+ if (info) {
+ retval = call_usermodehelper_exec(info, UMH_NO_WAIT);
+ env = NULL; /* freed by cleanup_uevent_env */
+ }
}
exit:
diff --git a/lib/nlattr.c b/lib/nlattr.c
index 18eca7809b08..fc6754720ced 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -303,9 +303,15 @@ int nla_memcmp(const struct nlattr *nla, const void *data,
*/
int nla_strcmp(const struct nlattr *nla, const char *str)
{
- int len = strlen(str) + 1;
- int d = nla_len(nla) - len;
+ int len = strlen(str);
+ char *buf = nla_data(nla);
+ int attrlen = nla_len(nla);
+ int d;
+ if (attrlen > 0 && buf[attrlen - 1] == '\0')
+ attrlen--;
+
+ d = attrlen - len;
if (d == 0)
d = memcmp(nla_data(nla), str, len);
diff --git a/lib/radix-tree.c b/lib/radix-tree.c
index bd4a8dfdf0b8..9599aa72d7a0 100644
--- a/lib/radix-tree.c
+++ b/lib/radix-tree.c
@@ -35,33 +35,6 @@
#include <linux/hardirq.h> /* in_interrupt() */
-#ifdef __KERNEL__
-#define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6)
-#else
-#define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */
-#endif
-
-#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT)
-#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1)
-
-#define RADIX_TREE_TAG_LONGS \
- ((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG)
-
-struct radix_tree_node {
- unsigned int height; /* Height from the bottom */
- unsigned int count;
- union {
- struct radix_tree_node *parent; /* Used when ascending tree */
- struct rcu_head rcu_head; /* Used when freeing node */
- };
- void __rcu *slots[RADIX_TREE_MAP_SIZE];
- unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
-};
-
-#define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long))
-#define RADIX_TREE_MAX_PATH (DIV_ROUND_UP(RADIX_TREE_INDEX_BITS, \
- RADIX_TREE_MAP_SHIFT))
-
/*
* The height_to_maxindex array needs to be one deeper than the maximum
* path as height 0 holds only 1 entry.
@@ -369,7 +342,8 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index)
/* Increase the height. */
newheight = root->height+1;
- node->height = newheight;
+ BUG_ON(newheight & ~RADIX_TREE_HEIGHT_MASK);
+ node->path = newheight;
node->count = 1;
node->parent = NULL;
slot = root->rnode;
@@ -387,23 +361,28 @@ out:
}
/**
- * radix_tree_insert - insert into a radix tree
+ * __radix_tree_create - create a slot in a radix tree
* @root: radix tree root
* @index: index key
- * @item: item to insert
+ * @nodep: returns node
+ * @slotp: returns slot
*
- * Insert an item into the radix tree at position @index.
+ * Create, if necessary, and return the node and slot for an item
+ * at position @index in the radix tree @root.
+ *
+ * Until there is more than one item in the tree, no nodes are
+ * allocated and @root->rnode is used as a direct slot instead of
+ * pointing to a node, in which case *@nodep will be NULL.
+ *
+ * Returns -ENOMEM, or 0 for success.
*/
-int radix_tree_insert(struct radix_tree_root *root,
- unsigned long index, void *item)
+int __radix_tree_create(struct radix_tree_root *root, unsigned long index,
+ struct radix_tree_node **nodep, void ***slotp)
{
struct radix_tree_node *node = NULL, *slot;
- unsigned int height, shift;
- int offset;
+ unsigned int height, shift, offset;
int error;
- BUG_ON(radix_tree_is_indirect_ptr(item));
-
/* Make sure the tree is high enough. */
if (index > radix_tree_maxindex(root->height)) {
error = radix_tree_extend(root, index);
@@ -422,11 +401,12 @@ int radix_tree_insert(struct radix_tree_root *root,
/* Have to add a child node. */
if (!(slot = radix_tree_node_alloc(root)))
return -ENOMEM;
- slot->height = height;
+ slot->path = height;
slot->parent = node;
if (node) {
rcu_assign_pointer(node->slots[offset], slot);
node->count++;
+ slot->path |= offset << RADIX_TREE_HEIGHT_SHIFT;
} else
rcu_assign_pointer(root->rnode, ptr_to_indirect(slot));
}
@@ -439,16 +419,42 @@ int radix_tree_insert(struct radix_tree_root *root,
height--;
}
- if (slot != NULL)
+ if (nodep)
+ *nodep = node;
+ if (slotp)
+ *slotp = node ? node->slots + offset : (void **)&root->rnode;
+ return 0;
+}
+
+/**
+ * radix_tree_insert - insert into a radix tree
+ * @root: radix tree root
+ * @index: index key
+ * @item: item to insert
+ *
+ * Insert an item into the radix tree at position @index.
+ */
+int radix_tree_insert(struct radix_tree_root *root,
+ unsigned long index, void *item)
+{
+ struct radix_tree_node *node;
+ void **slot;
+ int error;
+
+ BUG_ON(radix_tree_is_indirect_ptr(item));
+
+ error = __radix_tree_create(root, index, &node, &slot);
+ if (error)
+ return error;
+ if (*slot != NULL)
return -EEXIST;
+ rcu_assign_pointer(*slot, item);
if (node) {
node->count++;
- rcu_assign_pointer(node->slots[offset], item);
- BUG_ON(tag_get(node, 0, offset));
- BUG_ON(tag_get(node, 1, offset));
+ BUG_ON(tag_get(node, 0, index & RADIX_TREE_MAP_MASK));
+ BUG_ON(tag_get(node, 1, index & RADIX_TREE_MAP_MASK));
} else {
- rcu_assign_pointer(root->rnode, item);
BUG_ON(root_tag_get(root, 0));
BUG_ON(root_tag_get(root, 1));
}
@@ -457,15 +463,26 @@ int radix_tree_insert(struct radix_tree_root *root,
}
EXPORT_SYMBOL(radix_tree_insert);
-/*
- * is_slot == 1 : search for the slot.
- * is_slot == 0 : search for the node.
+/**
+ * __radix_tree_lookup - lookup an item in a radix tree
+ * @root: radix tree root
+ * @index: index key
+ * @nodep: returns node
+ * @slotp: returns slot
+ *
+ * Lookup and return the item at position @index in the radix
+ * tree @root.
+ *
+ * Until there is more than one item in the tree, no nodes are
+ * allocated and @root->rnode is used as a direct slot instead of
+ * pointing to a node, in which case *@nodep will be NULL.
*/
-static void *radix_tree_lookup_element(struct radix_tree_root *root,
- unsigned long index, int is_slot)
+void *__radix_tree_lookup(struct radix_tree_root *root, unsigned long index,
+ struct radix_tree_node **nodep, void ***slotp)
{
+ struct radix_tree_node *node, *parent;
unsigned int height, shift;
- struct radix_tree_node *node, **slot;
+ void **slot;
node = rcu_dereference_raw(root->rnode);
if (node == NULL)
@@ -474,19 +491,24 @@ static void *radix_tree_lookup_element(struct radix_tree_root *root,
if (!radix_tree_is_indirect_ptr(node)) {
if (index > 0)
return NULL;
- return is_slot ? (void *)&root->rnode : node;
+
+ if (nodep)
+ *nodep = NULL;
+ if (slotp)
+ *slotp = (void **)&root->rnode;
+ return node;
}
node = indirect_to_ptr(node);
- height = node->height;
+ height = node->path & RADIX_TREE_HEIGHT_MASK;
if (index > radix_tree_maxindex(height))
return NULL;
shift = (height-1) * RADIX_TREE_MAP_SHIFT;
do {
- slot = (struct radix_tree_node **)
- (node->slots + ((index>>shift) & RADIX_TREE_MAP_MASK));
+ parent = node;
+ slot = node->slots + ((index >> shift) & RADIX_TREE_MAP_MASK);
node = rcu_dereference_raw(*slot);
if (node == NULL)
return NULL;
@@ -495,7 +517,11 @@ static void *radix_tree_lookup_element(struct radix_tree_root *root,
height--;
} while (height > 0);
- return is_slot ? (void *)slot : indirect_to_ptr(node);
+ if (nodep)
+ *nodep = parent;
+ if (slotp)
+ *slotp = slot;
+ return node;
}
/**
@@ -513,7 +539,11 @@ static void *radix_tree_lookup_element(struct radix_tree_root *root,
*/
void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index)
{
- return (void **)radix_tree_lookup_element(root, index, 1);
+ void **slot;
+
+ if (!__radix_tree_lookup(root, index, NULL, &slot))
+ return NULL;
+ return slot;
}
EXPORT_SYMBOL(radix_tree_lookup_slot);
@@ -531,7 +561,7 @@ EXPORT_SYMBOL(radix_tree_lookup_slot);
*/
void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
{
- return radix_tree_lookup_element(root, index, 0);
+ return __radix_tree_lookup(root, index, NULL, NULL);
}
EXPORT_SYMBOL(radix_tree_lookup);
@@ -676,7 +706,7 @@ int radix_tree_tag_get(struct radix_tree_root *root,
return (index == 0);
node = indirect_to_ptr(node);
- height = node->height;
+ height = node->path & RADIX_TREE_HEIGHT_MASK;
if (index > radix_tree_maxindex(height))
return 0;
@@ -713,7 +743,7 @@ void **radix_tree_next_chunk(struct radix_tree_root *root,
{
unsigned shift, tag = flags & RADIX_TREE_ITER_TAG_MASK;
struct radix_tree_node *rnode, *node;
- unsigned long index, offset;
+ unsigned long index, offset, height;
if ((flags & RADIX_TREE_ITER_TAGGED) && !root_tag_get(root, tag))
return NULL;
@@ -744,7 +774,8 @@ void **radix_tree_next_chunk(struct radix_tree_root *root,
return NULL;
restart:
- shift = (rnode->height - 1) * RADIX_TREE_MAP_SHIFT;
+ height = rnode->path & RADIX_TREE_HEIGHT_MASK;
+ shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
offset = index >> shift;
/* Index outside of the tree */
@@ -946,81 +977,6 @@ next:
}
EXPORT_SYMBOL(radix_tree_range_tag_if_tagged);
-
-/**
- * radix_tree_next_hole - find the next hole (not-present entry)
- * @root: tree root
- * @index: index key
- * @max_scan: maximum range to search
- *
- * Search the set [index, min(index+max_scan-1, MAX_INDEX)] for the lowest
- * indexed hole.
- *
- * Returns: the index of the hole if found, otherwise returns an index
- * outside of the set specified (in which case 'return - index >= max_scan'
- * will be true). In rare cases of index wrap-around, 0 will be returned.
- *
- * radix_tree_next_hole may be called under rcu_read_lock. However, like
- * radix_tree_gang_lookup, this will not atomically search a snapshot of
- * the tree at a single point in time. For example, if a hole is created
- * at index 5, then subsequently a hole is created at index 10,
- * radix_tree_next_hole covering both indexes may return 10 if called
- * under rcu_read_lock.
- */
-unsigned long radix_tree_next_hole(struct radix_tree_root *root,
- unsigned long index, unsigned long max_scan)
-{
- unsigned long i;
-
- for (i = 0; i < max_scan; i++) {
- if (!radix_tree_lookup(root, index))
- break;
- index++;
- if (index == 0)
- break;
- }
-
- return index;
-}
-EXPORT_SYMBOL(radix_tree_next_hole);
-
-/**
- * radix_tree_prev_hole - find the prev hole (not-present entry)
- * @root: tree root
- * @index: index key
- * @max_scan: maximum range to search
- *
- * Search backwards in the range [max(index-max_scan+1, 0), index]
- * for the first hole.
- *
- * Returns: the index of the hole if found, otherwise returns an index
- * outside of the set specified (in which case 'index - return >= max_scan'
- * will be true). In rare cases of wrap-around, ULONG_MAX will be returned.
- *
- * radix_tree_next_hole may be called under rcu_read_lock. However, like
- * radix_tree_gang_lookup, this will not atomically search a snapshot of
- * the tree at a single point in time. For example, if a hole is created
- * at index 10, then subsequently a hole is created at index 5,
- * radix_tree_prev_hole covering both indexes may return 5 if called under
- * rcu_read_lock.
- */
-unsigned long radix_tree_prev_hole(struct radix_tree_root *root,
- unsigned long index, unsigned long max_scan)
-{
- unsigned long i;
-
- for (i = 0; i < max_scan; i++) {
- if (!radix_tree_lookup(root, index))
- break;
- index--;
- if (index == ULONG_MAX)
- break;
- }
-
- return index;
-}
-EXPORT_SYMBOL(radix_tree_prev_hole);
-
/**
* radix_tree_gang_lookup - perform multiple lookup on a radix tree
* @root: radix tree root
@@ -1189,7 +1145,7 @@ static unsigned long __locate(struct radix_tree_node *slot, void *item,
unsigned int shift, height;
unsigned long i;
- height = slot->height;
+ height = slot->path & RADIX_TREE_HEIGHT_MASK;
shift = (height-1) * RADIX_TREE_MAP_SHIFT;
for ( ; height > 1; height--) {
@@ -1252,7 +1208,8 @@ unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item)
}
node = indirect_to_ptr(node);
- max_index = radix_tree_maxindex(node->height);
+ max_index = radix_tree_maxindex(node->path &
+ RADIX_TREE_HEIGHT_MASK);
if (cur_index > max_index) {
rcu_read_unlock();
break;
@@ -1337,48 +1294,90 @@ static inline void radix_tree_shrink(struct radix_tree_root *root)
}
/**
- * radix_tree_delete - delete an item from a radix tree
+ * __radix_tree_delete_node - try to free node after clearing a slot
* @root: radix tree root
* @index: index key
+ * @node: node containing @index
*
- * Remove the item at @index from the radix tree rooted at @root.
+ * After clearing the slot at @index in @node from radix tree
+ * rooted at @root, call this function to attempt freeing the
+ * node and shrinking the tree.
*
- * Returns the address of the deleted item, or NULL if it was not present.
+ * Returns %true if @node was freed, %false otherwise.
*/
-void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
+bool __radix_tree_delete_node(struct radix_tree_root *root,
+ struct radix_tree_node *node)
{
- struct radix_tree_node *node = NULL;
- struct radix_tree_node *slot = NULL;
- struct radix_tree_node *to_free;
- unsigned int height, shift;
+ bool deleted = false;
+
+ do {
+ struct radix_tree_node *parent;
+
+ if (node->count) {
+ if (node == indirect_to_ptr(root->rnode)) {
+ radix_tree_shrink(root);
+ if (root->height == 0)
+ deleted = true;
+ }
+ return deleted;
+ }
+
+ parent = node->parent;
+ if (parent) {
+ unsigned int offset;
+
+ offset = node->path >> RADIX_TREE_HEIGHT_SHIFT;
+ parent->slots[offset] = NULL;
+ parent->count--;
+ } else {
+ root_tag_clear_all(root);
+ root->height = 0;
+ root->rnode = NULL;
+ }
+
+ radix_tree_node_free(node);
+ deleted = true;
+
+ node = parent;
+ } while (node);
+
+ return deleted;
+}
+
+/**
+ * radix_tree_delete_item - delete an item from a radix tree
+ * @root: radix tree root
+ * @index: index key
+ * @item: expected item
+ *
+ * Remove @item at @index from the radix tree rooted at @root.
+ *
+ * Returns the address of the deleted item, or NULL if it was not present
+ * or the entry at the given @index was not @item.
+ */
+void *radix_tree_delete_item(struct radix_tree_root *root,
+ unsigned long index, void *item)
+{
+ struct radix_tree_node *node;
+ unsigned int offset;
+ void **slot;
+ void *entry;
int tag;
- int uninitialized_var(offset);
- height = root->height;
- if (index > radix_tree_maxindex(height))
- goto out;
+ entry = __radix_tree_lookup(root, index, &node, &slot);
+ if (!entry)
+ return NULL;
- slot = root->rnode;
- if (height == 0) {
+ if (item && entry != item)
+ return NULL;
+
+ if (!node) {
root_tag_clear_all(root);
root->rnode = NULL;
- goto out;
+ return entry;
}
- slot = indirect_to_ptr(slot);
- shift = height * RADIX_TREE_MAP_SHIFT;
- do {
- if (slot == NULL)
- goto out;
-
- shift -= RADIX_TREE_MAP_SHIFT;
- offset = (index >> shift) & RADIX_TREE_MAP_MASK;
- node = slot;
- slot = slot->slots[offset];
- } while (shift);
-
- if (slot == NULL)
- goto out;
+ offset = index & RADIX_TREE_MAP_MASK;
/*
* Clear all tags associated with the item to be deleted.
@@ -1389,40 +1388,27 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
radix_tree_tag_clear(root, index, tag);
}
- to_free = NULL;
- /* Now free the nodes we do not need anymore */
- while (node) {
- node->slots[offset] = NULL;
- node->count--;
- /*
- * Queue the node for deferred freeing after the
- * last reference to it disappears (set NULL, above).
- */
- if (to_free)
- radix_tree_node_free(to_free);
-
- if (node->count) {
- if (node == indirect_to_ptr(root->rnode))
- radix_tree_shrink(root);
- goto out;
- }
+ node->slots[offset] = NULL;
+ node->count--;
- /* Node with zero slots in use so free it */
- to_free = node;
+ __radix_tree_delete_node(root, node);
- index >>= RADIX_TREE_MAP_SHIFT;
- offset = index & RADIX_TREE_MAP_MASK;
- node = node->parent;
- }
-
- root_tag_clear_all(root);
- root->height = 0;
- root->rnode = NULL;
- if (to_free)
- radix_tree_node_free(to_free);
+ return entry;
+}
+EXPORT_SYMBOL(radix_tree_delete_item);
-out:
- return slot;
+/**
+ * radix_tree_delete - delete an item from a radix tree
+ * @root: radix tree root
+ * @index: index key
+ *
+ * Remove the item at @index from the radix tree rooted at @root.
+ *
+ * Returns the address of the deleted item, or NULL if it was not present.
+ */
+void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
+{
+ return radix_tree_delete_item(root, index, NULL);
}
EXPORT_SYMBOL(radix_tree_delete);
@@ -1438,9 +1424,12 @@ int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag)
EXPORT_SYMBOL(radix_tree_tagged);
static void
-radix_tree_node_ctor(void *node)
+radix_tree_node_ctor(void *arg)
{
- memset(node, 0, sizeof(struct radix_tree_node));
+ struct radix_tree_node *node = arg;
+
+ memset(node, 0, sizeof(*node));
+ INIT_LIST_HEAD(&node->private_list);
}
static __init unsigned long __maxindex(unsigned int height)
diff --git a/lib/random32.c b/lib/random32.c
index 614896778700..fa5da61ce7ad 100644
--- a/lib/random32.c
+++ b/lib/random32.c
@@ -1,37 +1,35 @@
/*
- This is a maximally equidistributed combined Tausworthe generator
- based on code from GNU Scientific Library 1.5 (30 Jun 2004)
-
- lfsr113 version:
-
- x_n = (s1_n ^ s2_n ^ s3_n ^ s4_n)
-
- s1_{n+1} = (((s1_n & 4294967294) << 18) ^ (((s1_n << 6) ^ s1_n) >> 13))
- s2_{n+1} = (((s2_n & 4294967288) << 2) ^ (((s2_n << 2) ^ s2_n) >> 27))
- s3_{n+1} = (((s3_n & 4294967280) << 7) ^ (((s3_n << 13) ^ s3_n) >> 21))
- s4_{n+1} = (((s4_n & 4294967168) << 13) ^ (((s4_n << 3) ^ s4_n) >> 12))
-
- The period of this generator is about 2^113 (see erratum paper).
-
- From: P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe
- Generators", Mathematics of Computation, 65, 213 (1996), 203--213:
- http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme.ps
- ftp://ftp.iro.umontreal.ca/pub/simulation/lecuyer/papers/tausme.ps
-
- There is an erratum in the paper "Tables of Maximally
- Equidistributed Combined LFSR Generators", Mathematics of
- Computation, 68, 225 (1999), 261--269:
- http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme2.ps
-
- ... the k_j most significant bits of z_j must be non-
- zero, for each j. (Note: this restriction also applies to the
- computer code given in [4], but was mistakenly not mentioned in
- that paper.)
-
- This affects the seeding procedure by imposing the requirement
- s1 > 1, s2 > 7, s3 > 15, s4 > 127.
-
-*/
+ * This is a maximally equidistributed combined Tausworthe generator
+ * based on code from GNU Scientific Library 1.5 (30 Jun 2004)
+ *
+ * lfsr113 version:
+ *
+ * x_n = (s1_n ^ s2_n ^ s3_n ^ s4_n)
+ *
+ * s1_{n+1} = (((s1_n & 4294967294) << 18) ^ (((s1_n << 6) ^ s1_n) >> 13))
+ * s2_{n+1} = (((s2_n & 4294967288) << 2) ^ (((s2_n << 2) ^ s2_n) >> 27))
+ * s3_{n+1} = (((s3_n & 4294967280) << 7) ^ (((s3_n << 13) ^ s3_n) >> 21))
+ * s4_{n+1} = (((s4_n & 4294967168) << 13) ^ (((s4_n << 3) ^ s4_n) >> 12))
+ *
+ * The period of this generator is about 2^113 (see erratum paper).
+ *
+ * From: P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe
+ * Generators", Mathematics of Computation, 65, 213 (1996), 203--213:
+ * http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme.ps
+ * ftp://ftp.iro.umontreal.ca/pub/simulation/lecuyer/papers/tausme.ps
+ *
+ * There is an erratum in the paper "Tables of Maximally Equidistributed
+ * Combined LFSR Generators", Mathematics of Computation, 68, 225 (1999),
+ * 261--269: http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme2.ps
+ *
+ * ... the k_j most significant bits of z_j must be non-zero,
+ * for each j. (Note: this restriction also applies to the
+ * computer code given in [4], but was mistakenly not mentioned
+ * in that paper.)
+ *
+ * This affects the seeding procedure by imposing the requirement
+ * s1 > 1, s2 > 7, s3 > 15, s4 > 127.
+ */
#include <linux/types.h>
#include <linux/percpu.h>
@@ -75,15 +73,17 @@ EXPORT_SYMBOL(prandom_u32_state);
*/
u32 prandom_u32(void)
{
- unsigned long r;
struct rnd_state *state = &get_cpu_var(net_rand_state);
- r = prandom_u32_state(state);
+ u32 res;
+
+ res = prandom_u32_state(state);
put_cpu_var(state);
- return r;
+
+ return res;
}
EXPORT_SYMBOL(prandom_u32);
-/*
+/**
* prandom_bytes_state - get the requested number of pseudo-random bytes
*
* @state: pointer to state structure holding seeded state.
@@ -204,6 +204,7 @@ static int __init prandom_init(void)
prandom_seed_very_weak(state, (i + jiffies) ^ random_get_entropy());
prandom_warmup(state);
}
+
return 0;
}
core_initcall(prandom_init);
@@ -259,6 +260,7 @@ static void __prandom_reseed(bool late)
if (latch && !late)
goto out;
+
latch = true;
for_each_possible_cpu(i) {
diff --git a/lib/syscall.c b/lib/syscall.c
index 58710eefeac8..e30e03932480 100644
--- a/lib/syscall.c
+++ b/lib/syscall.c
@@ -72,4 +72,3 @@ int task_current_syscall(struct task_struct *target, long *callno,
return 0;
}
-EXPORT_SYMBOL_GPL(task_current_syscall);
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 5e2cf6f342f8..0648291cdafe 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -364,7 +364,6 @@ enum format_type {
FORMAT_TYPE_SHORT,
FORMAT_TYPE_UINT,
FORMAT_TYPE_INT,
- FORMAT_TYPE_NRCHARS,
FORMAT_TYPE_SIZE_T,
FORMAT_TYPE_PTRDIFF
};
@@ -1538,10 +1537,6 @@ qualifier:
return fmt - start;
/* skip alnum */
- case 'n':
- spec->type = FORMAT_TYPE_NRCHARS;
- return ++fmt - start;
-
case '%':
spec->type = FORMAT_TYPE_PERCENT_CHAR;
return ++fmt - start;
@@ -1564,6 +1559,15 @@ qualifier:
case 'u':
break;
+ case 'n':
+ /*
+ * Since %n poses a greater security risk than utility, treat
+ * it as an invalid format specifier. Warn about its use so
+ * that new instances don't get added.
+ */
+ WARN_ONCE(1, "Please remove ignored %%n in '%s'\n", fmt);
+ /* Fall-through */
+
default:
spec->type = FORMAT_TYPE_INVALID;
return fmt - start;
@@ -1737,20 +1741,6 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
++str;
break;
- case FORMAT_TYPE_NRCHARS: {
- /*
- * Since %n poses a greater security risk than
- * utility, ignore %n and skip its argument.
- */
- void *skip_arg;
-
- WARN_ONCE(1, "Please remove ignored %%n in '%s'\n",
- old_fmt);
-
- skip_arg = va_arg(args, void *);
- break;
- }
-
default:
switch (spec.type) {
case FORMAT_TYPE_LONG_LONG:
@@ -2025,19 +2015,6 @@ do { \
fmt++;
break;
- case FORMAT_TYPE_NRCHARS: {
- /* skip %n 's argument */
- u8 qualifier = spec.qualifier;
- void *skip_arg;
- if (qualifier == 'l')
- skip_arg = va_arg(args, long *);
- else if (_tolower(qualifier) == 'z')
- skip_arg = va_arg(args, size_t *);
- else
- skip_arg = va_arg(args, int *);
- break;
- }
-
default:
switch (spec.type) {
@@ -2196,10 +2173,6 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
++str;
break;
- case FORMAT_TYPE_NRCHARS:
- /* skip */
- break;
-
default: {
unsigned long long num;
diff --git a/mm/Makefile b/mm/Makefile
index 310c90a09264..cdd741519ee0 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -17,7 +17,7 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \
util.o mmzone.o vmstat.o backing-dev.o \
mm_init.o mmu_context.o percpu.o slab_common.o \
compaction.o balloon_compaction.o \
- interval_tree.o list_lru.o $(mmu-y)
+ interval_tree.o list_lru.o workingset.o $(mmu-y)
obj-y += init-mm.o
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index ce682f7a4f29..09d9591b7708 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -288,13 +288,19 @@ int bdi_has_dirty_io(struct backing_dev_info *bdi)
* Note, we wouldn't bother setting up the timer, but this function is on the
* fast-path (used by '__mark_inode_dirty()'), so we save few context switches
* by delaying the wake-up.
+ *
+ * We have to be careful not to postpone flush work if it is scheduled for
+ * earlier. Thus we use queue_delayed_work().
*/
void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi)
{
unsigned long timeout;
timeout = msecs_to_jiffies(dirty_writeback_interval * 10);
- mod_delayed_work(bdi_wq, &bdi->wb.dwork, timeout);
+ spin_lock_bh(&bdi->wb_lock);
+ if (test_bit(BDI_registered, &bdi->state))
+ queue_delayed_work(bdi_wq, &bdi->wb.dwork, timeout);
+ spin_unlock_bh(&bdi->wb_lock);
}
/*
@@ -307,9 +313,6 @@ static void bdi_remove_from_list(struct backing_dev_info *bdi)
spin_unlock_bh(&bdi_lock);
synchronize_rcu_expedited();
-
- /* bdi_list is now unused, clear it to mark @bdi dying */
- INIT_LIST_HEAD(&bdi->bdi_list);
}
int bdi_register(struct backing_dev_info *bdi, struct device *parent,
@@ -360,6 +363,11 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi)
*/
bdi_remove_from_list(bdi);
+ /* Make sure nobody queues further work */
+ spin_lock_bh(&bdi->wb_lock);
+ clear_bit(BDI_registered, &bdi->state);
+ spin_unlock_bh(&bdi->wb_lock);
+
/*
* Drain work list and shutdown the delayed_work. At this point,
* @bdi->bdi_list is empty telling bdi_Writeback_workfn() that @bdi
diff --git a/mm/compaction.c b/mm/compaction.c
index 918577595ea8..b6ab77160068 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -584,6 +584,15 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
continue;
}
+ /*
+ * Migration will fail if an anonymous page is pinned in memory,
+ * so avoid taking lru_lock and isolating it unnecessarily in an
+ * admittedly racy check.
+ */
+ if (!page_mapping(page) &&
+ page_count(page) > page_mapcount(page))
+ continue;
+
/* Check if it is ok to still hold the lock */
locked = compact_checklock_irqsave(&zone->lru_lock, &flags,
locked, cc);
@@ -1186,6 +1195,7 @@ static void compact_node(int nid)
struct compact_control cc = {
.order = -1,
.sync = true,
+ .ignore_skip_hint = true,
};
__compact_pgdat(NODE_DATA(nid), &cc);
@@ -1225,7 +1235,7 @@ int sysctl_extfrag_handler(struct ctl_table *table, int write,
}
#if defined(CONFIG_SYSFS) && defined(CONFIG_NUMA)
-ssize_t sysfs_compact_node(struct device *dev,
+static ssize_t sysfs_compact_node(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
diff --git a/mm/filemap.c b/mm/filemap.c
index 7a13f6ac5421..21781f1fe52b 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -107,12 +107,75 @@
* ->tasklist_lock (memory_failure, collect_procs_ao)
*/
+static void page_cache_tree_delete(struct address_space *mapping,
+ struct page *page, void *shadow)
+{
+ struct radix_tree_node *node;
+ unsigned long index;
+ unsigned int offset;
+ unsigned int tag;
+ void **slot;
+
+ VM_BUG_ON(!PageLocked(page));
+
+ __radix_tree_lookup(&mapping->page_tree, page->index, &node, &slot);
+
+ if (shadow) {
+ mapping->nrshadows++;
+ /*
+ * Make sure the nrshadows update is committed before
+ * the nrpages update so that final truncate racing
+ * with reclaim does not see both counters 0 at the
+ * same time and miss a shadow entry.
+ */
+ smp_wmb();
+ }
+ mapping->nrpages--;
+
+ if (!node) {
+ /* Clear direct pointer tags in root node */
+ mapping->page_tree.gfp_mask &= __GFP_BITS_MASK;
+ radix_tree_replace_slot(slot, shadow);
+ return;
+ }
+
+ /* Clear tree tags for the removed page */
+ index = page->index;
+ offset = index & RADIX_TREE_MAP_MASK;
+ for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
+ if (test_bit(offset, node->tags[tag]))
+ radix_tree_tag_clear(&mapping->page_tree, index, tag);
+ }
+
+ /* Delete page, swap shadow entry */
+ radix_tree_replace_slot(slot, shadow);
+ workingset_node_pages_dec(node);
+ if (shadow)
+ workingset_node_shadows_inc(node);
+ else
+ if (__radix_tree_delete_node(&mapping->page_tree, node))
+ return;
+
+ /*
+ * Track node that only contains shadow entries.
+ *
+ * Avoid acquiring the list_lru lock if already tracked. The
+ * list_empty() test is safe as node->private_list is
+ * protected by mapping->tree_lock.
+ */
+ if (!workingset_node_pages(node) &&
+ list_empty(&node->private_list)) {
+ node->private_data = mapping;
+ list_lru_add(&workingset_shadow_nodes, &node->private_list);
+ }
+}
+
/*
* Delete a page from the page cache and free it. Caller has to make
* sure the page is locked and that nobody else uses it - or that usage
* is safe. The caller must hold the mapping's tree_lock.
*/
-void __delete_from_page_cache(struct page *page)
+void __delete_from_page_cache(struct page *page, void *shadow)
{
struct address_space *mapping = page->mapping;
@@ -127,10 +190,11 @@ void __delete_from_page_cache(struct page *page)
else
cleancache_invalidate_page(mapping, page);
- radix_tree_delete(&mapping->page_tree, page->index);
+ page_cache_tree_delete(mapping, page, shadow);
+
page->mapping = NULL;
/* Leave page->index set: truncation lookup relies upon it */
- mapping->nrpages--;
+
__dec_zone_page_state(page, NR_FILE_PAGES);
if (PageSwapBacked(page))
__dec_zone_page_state(page, NR_SHMEM);
@@ -166,7 +230,7 @@ void delete_from_page_cache(struct page *page)
freepage = mapping->a_ops->freepage;
spin_lock_irq(&mapping->tree_lock);
- __delete_from_page_cache(page);
+ __delete_from_page_cache(page, NULL);
spin_unlock_irq(&mapping->tree_lock);
mem_cgroup_uncharge_cache_page(page);
@@ -426,7 +490,7 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
new->index = offset;
spin_lock_irq(&mapping->tree_lock);
- __delete_from_page_cache(old);
+ __delete_from_page_cache(old, NULL);
error = radix_tree_insert(&mapping->page_tree, offset, new);
BUG_ON(error);
mapping->nrpages++;
@@ -446,18 +510,52 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
}
EXPORT_SYMBOL_GPL(replace_page_cache_page);
-/**
- * add_to_page_cache_locked - add a locked page to the pagecache
- * @page: page to add
- * @mapping: the page's address_space
- * @offset: page index
- * @gfp_mask: page allocation mode
- *
- * This function is used to add a page to the pagecache. It must be locked.
- * This function does not add the page to the LRU. The caller must do that.
- */
-int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
- pgoff_t offset, gfp_t gfp_mask)
+static int page_cache_tree_insert(struct address_space *mapping,
+ struct page *page, void **shadowp)
+{
+ struct radix_tree_node *node;
+ void **slot;
+ int error;
+
+ error = __radix_tree_create(&mapping->page_tree, page->index,
+ &node, &slot);
+ if (error)
+ return error;
+ if (*slot) {
+ void *p;
+
+ p = radix_tree_deref_slot_protected(slot, &mapping->tree_lock);
+ if (!radix_tree_exceptional_entry(p))
+ return -EEXIST;
+ if (shadowp)
+ *shadowp = p;
+ mapping->nrshadows--;
+ if (node)
+ workingset_node_shadows_dec(node);
+ }
+ radix_tree_replace_slot(slot, page);
+ mapping->nrpages++;
+ if (node) {
+ workingset_node_pages_inc(node);
+ /*
+ * Don't track node that contains actual pages.
+ *
+ * Avoid acquiring the list_lru lock if already
+ * untracked. The list_empty() test is safe as
+ * node->private_list is protected by
+ * mapping->tree_lock.
+ */
+ if (!list_empty(&node->private_list))
+ list_lru_del(&workingset_shadow_nodes,
+ &node->private_list);
+ }
+ return 0;
+}
+
+static int __add_to_page_cache_locked(struct page *page,
+ struct address_space *mapping,
+ pgoff_t offset, gfp_t gfp_mask,
+ void **shadowp)
{
int error;
@@ -480,11 +578,10 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
page->index = offset;
spin_lock_irq(&mapping->tree_lock);
- error = radix_tree_insert(&mapping->page_tree, offset, page);
+ error = page_cache_tree_insert(mapping, page, shadowp);
radix_tree_preload_end();
if (unlikely(error))
goto err_insert;
- mapping->nrpages++;
__inc_zone_page_state(page, NR_FILE_PAGES);
spin_unlock_irq(&mapping->tree_lock);
trace_mm_filemap_add_to_page_cache(page);
@@ -497,16 +594,49 @@ err_insert:
page_cache_release(page);
return error;
}
+
+/**
+ * add_to_page_cache_locked - add a locked page to the pagecache
+ * @page: page to add
+ * @mapping: the page's address_space
+ * @offset: page index
+ * @gfp_mask: page allocation mode
+ *
+ * This function is used to add a page to the pagecache. It must be locked.
+ * This function does not add the page to the LRU. The caller must do that.
+ */
+int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
+ pgoff_t offset, gfp_t gfp_mask)
+{
+ return __add_to_page_cache_locked(page, mapping, offset,
+ gfp_mask, NULL);
+}
EXPORT_SYMBOL(add_to_page_cache_locked);
int add_to_page_cache_lru(struct page *page, struct address_space *mapping,
pgoff_t offset, gfp_t gfp_mask)
{
+ void *shadow = NULL;
int ret;
- ret = add_to_page_cache(page, mapping, offset, gfp_mask);
- if (ret == 0)
- lru_cache_add_file(page);
+ __set_page_locked(page);
+ ret = __add_to_page_cache_locked(page, mapping, offset,
+ gfp_mask, &shadow);
+ if (unlikely(ret))
+ __clear_page_locked(page);
+ else {
+ /*
+ * The page might have been evicted from cache only
+ * recently, in which case it should be activated like
+ * any other repeatedly accessed page.
+ */
+ if (shadow && workingset_refault(shadow)) {
+ SetPageActive(page);
+ workingset_activation(page);
+ } else
+ ClearPageActive(page);
+ lru_cache_add(page);
+ }
return ret;
}
EXPORT_SYMBOL_GPL(add_to_page_cache_lru);
@@ -520,10 +650,10 @@ struct page *__page_cache_alloc(gfp_t gfp)
if (cpuset_do_page_mem_spread()) {
unsigned int cpuset_mems_cookie;
do {
- cpuset_mems_cookie = get_mems_allowed();
+ cpuset_mems_cookie = read_mems_allowed_begin();
n = cpuset_mem_spread_node();
page = alloc_pages_exact_node(n, gfp, 0);
- } while (!put_mems_allowed(cpuset_mems_cookie) && !page);
+ } while (!page && read_mems_allowed_retry(cpuset_mems_cookie));
return page;
}
@@ -686,14 +816,101 @@ int __lock_page_or_retry(struct page *page, struct mm_struct *mm,
}
/**
- * find_get_page - find and get a page reference
+ * page_cache_next_hole - find the next hole (not-present entry)
+ * @mapping: mapping
+ * @index: index
+ * @max_scan: maximum range to search
+ *
+ * Search the set [index, min(index+max_scan-1, MAX_INDEX)] for the
+ * lowest indexed hole.
+ *
+ * Returns: the index of the hole if found, otherwise returns an index
+ * outside of the set specified (in which case 'return - index >=
+ * max_scan' will be true). In rare cases of index wrap-around, 0 will
+ * be returned.
+ *
+ * page_cache_next_hole may be called under rcu_read_lock. However,
+ * like radix_tree_gang_lookup, this will not atomically search a
+ * snapshot of the tree at a single point in time. For example, if a
+ * hole is created at index 5, then subsequently a hole is created at
+ * index 10, page_cache_next_hole covering both indexes may return 10
+ * if called under rcu_read_lock.
+ */
+pgoff_t page_cache_next_hole(struct address_space *mapping,
+ pgoff_t index, unsigned long max_scan)
+{
+ unsigned long i;
+
+ for (i = 0; i < max_scan; i++) {
+ struct page *page;
+
+ page = radix_tree_lookup(&mapping->page_tree, index);
+ if (!page || radix_tree_exceptional_entry(page))
+ break;
+ index++;
+ if (index == 0)
+ break;
+ }
+
+ return index;
+}
+EXPORT_SYMBOL(page_cache_next_hole);
+
+/**
+ * page_cache_prev_hole - find the prev hole (not-present entry)
+ * @mapping: mapping
+ * @index: index
+ * @max_scan: maximum range to search
+ *
+ * Search backwards in the range [max(index-max_scan+1, 0), index] for
+ * the first hole.
+ *
+ * Returns: the index of the hole if found, otherwise returns an index
+ * outside of the set specified (in which case 'index - return >=
+ * max_scan' will be true). In rare cases of wrap-around, ULONG_MAX
+ * will be returned.
+ *
+ * page_cache_prev_hole may be called under rcu_read_lock. However,
+ * like radix_tree_gang_lookup, this will not atomically search a
+ * snapshot of the tree at a single point in time. For example, if a
+ * hole is created at index 10, then subsequently a hole is created at
+ * index 5, page_cache_prev_hole covering both indexes may return 5 if
+ * called under rcu_read_lock.
+ */
+pgoff_t page_cache_prev_hole(struct address_space *mapping,
+ pgoff_t index, unsigned long max_scan)
+{
+ unsigned long i;
+
+ for (i = 0; i < max_scan; i++) {
+ struct page *page;
+
+ page = radix_tree_lookup(&mapping->page_tree, index);
+ if (!page || radix_tree_exceptional_entry(page))
+ break;
+ index--;
+ if (index == ULONG_MAX)
+ break;
+ }
+
+ return index;
+}
+EXPORT_SYMBOL(page_cache_prev_hole);
+
+/**
+ * find_get_entry - find and get a page cache entry
* @mapping: the address_space to search
- * @offset: the page index
+ * @offset: the page cache index
+ *
+ * Looks up the page cache slot at @mapping & @offset. If there is a
+ * page cache page, it is returned with an increased refcount.
+ *
+ * If the slot holds a shadow entry of a previously evicted page, it
+ * is returned.
*
- * Is there a pagecache struct page at the given (mapping, offset) tuple?
- * If yes, increment its refcount and return it; if no, return NULL.
+ * Otherwise, %NULL is returned.
*/
-struct page *find_get_page(struct address_space *mapping, pgoff_t offset)
+struct page *find_get_entry(struct address_space *mapping, pgoff_t offset)
{
void **pagep;
struct page *page;
@@ -734,24 +951,50 @@ out:
return page;
}
-EXPORT_SYMBOL(find_get_page);
+EXPORT_SYMBOL(find_get_entry);
/**
- * find_lock_page - locate, pin and lock a pagecache page
+ * find_get_page - find and get a page reference
* @mapping: the address_space to search
* @offset: the page index
*
- * Locates the desired pagecache page, locks it, increments its reference
- * count and returns its address.
+ * Looks up the page cache slot at @mapping & @offset. If there is a
+ * page cache page, it is returned with an increased refcount.
*
- * Returns zero if the page was not present. find_lock_page() may sleep.
+ * Otherwise, %NULL is returned.
*/
-struct page *find_lock_page(struct address_space *mapping, pgoff_t offset)
+struct page *find_get_page(struct address_space *mapping, pgoff_t offset)
+{
+ struct page *page = find_get_entry(mapping, offset);
+
+ if (radix_tree_exceptional_entry(page))
+ page = NULL;
+ return page;
+}
+EXPORT_SYMBOL(find_get_page);
+
+/**
+ * find_lock_entry - locate, pin and lock a page cache entry
+ * @mapping: the address_space to search
+ * @offset: the page cache index
+ *
+ * Looks up the page cache slot at @mapping & @offset. If there is a
+ * page cache page, it is returned locked and with an increased
+ * refcount.
+ *
+ * If the slot holds a shadow entry of a previously evicted page, it
+ * is returned.
+ *
+ * Otherwise, %NULL is returned.
+ *
+ * find_lock_entry() may sleep.
+ */
+struct page *find_lock_entry(struct address_space *mapping, pgoff_t offset)
{
struct page *page;
repeat:
- page = find_get_page(mapping, offset);
+ page = find_get_entry(mapping, offset);
if (page && !radix_tree_exception(page)) {
lock_page(page);
/* Has the page been truncated? */
@@ -764,6 +1007,29 @@ repeat:
}
return page;
}
+EXPORT_SYMBOL(find_lock_entry);
+
+/**
+ * find_lock_page - locate, pin and lock a pagecache page
+ * @mapping: the address_space to search
+ * @offset: the page index
+ *
+ * Looks up the page cache slot at @mapping & @offset. If there is a
+ * page cache page, it is returned locked and with an increased
+ * refcount.
+ *
+ * Otherwise, %NULL is returned.
+ *
+ * find_lock_page() may sleep.
+ */
+struct page *find_lock_page(struct address_space *mapping, pgoff_t offset)
+{
+ struct page *page = find_lock_entry(mapping, offset);
+
+ if (radix_tree_exceptional_entry(page))
+ page = NULL;
+ return page;
+}
EXPORT_SYMBOL(find_lock_page);
/**
@@ -772,16 +1038,18 @@ EXPORT_SYMBOL(find_lock_page);
* @index: the page's index into the mapping
* @gfp_mask: page allocation mode
*
- * Locates a page in the pagecache. If the page is not present, a new page
- * is allocated using @gfp_mask and is added to the pagecache and to the VM's
- * LRU list. The returned page is locked and has its reference count
- * incremented.
+ * Looks up the page cache slot at @mapping & @offset. If there is a
+ * page cache page, it is returned locked and with an increased
+ * refcount.
*
- * find_or_create_page() may sleep, even if @gfp_flags specifies an atomic
- * allocation!
+ * If the page is not present, a new page is allocated using @gfp_mask
+ * and added to the page cache and the VM's LRU list. The page is
+ * returned locked and with an increased refcount.
*
- * find_or_create_page() returns the desired page's address, or zero on
- * memory exhaustion.
+ * On memory exhaustion, %NULL is returned.
+ *
+ * find_or_create_page() may sleep, even if @gfp_flags specifies an
+ * atomic allocation!
*/
struct page *find_or_create_page(struct address_space *mapping,
pgoff_t index, gfp_t gfp_mask)
@@ -814,6 +1082,76 @@ repeat:
EXPORT_SYMBOL(find_or_create_page);
/**
+ * find_get_entries - gang pagecache lookup
+ * @mapping: The address_space to search
+ * @start: The starting page cache index
+ * @nr_entries: The maximum number of entries
+ * @entries: Where the resulting entries are placed
+ * @indices: The cache indices corresponding to the entries in @entries
+ *
+ * find_get_entries() will search for and return a group of up to
+ * @nr_entries entries in the mapping. The entries are placed at
+ * @entries. find_get_entries() takes a reference against any actual
+ * pages it returns.
+ *
+ * The search returns a group of mapping-contiguous page cache entries
+ * with ascending indexes. There may be holes in the indices due to
+ * not-present pages.
+ *
+ * Any shadow entries of evicted pages are included in the returned
+ * array.
+ *
+ * find_get_entries() returns the number of pages and shadow entries
+ * which were found.
+ */
+unsigned find_get_entries(struct address_space *mapping,
+ pgoff_t start, unsigned int nr_entries,
+ struct page **entries, pgoff_t *indices)
+{
+ void **slot;
+ unsigned int ret = 0;
+ struct radix_tree_iter iter;
+
+ if (!nr_entries)
+ return 0;
+
+ rcu_read_lock();
+restart:
+ radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) {
+ struct page *page;
+repeat:
+ page = radix_tree_deref_slot(slot);
+ if (unlikely(!page))
+ continue;
+ if (radix_tree_exception(page)) {
+ if (radix_tree_deref_retry(page))
+ goto restart;
+ /*
+ * Otherwise, we must be storing a swap entry
+ * here as an exceptional entry: so return it
+ * without attempting to raise page count.
+ */
+ goto export;
+ }
+ if (!page_cache_get_speculative(page))
+ goto repeat;
+
+ /* Has the page moved? */
+ if (unlikely(page != *slot)) {
+ page_cache_release(page);
+ goto repeat;
+ }
+export:
+ indices[ret] = iter.index;
+ entries[ret] = page;
+ if (++ret == nr_entries)
+ break;
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+/**
* find_get_pages - gang pagecache lookup
* @mapping: The address_space to search
* @start: The starting page index
@@ -1795,6 +2133,18 @@ int generic_file_readonly_mmap(struct file * file, struct vm_area_struct * vma)
EXPORT_SYMBOL(generic_file_mmap);
EXPORT_SYMBOL(generic_file_readonly_mmap);
+static struct page *wait_on_page_read(struct page *page)
+{
+ if (!IS_ERR(page)) {
+ wait_on_page_locked(page);
+ if (!PageUptodate(page)) {
+ page_cache_release(page);
+ page = ERR_PTR(-EIO);
+ }
+ }
+ return page;
+}
+
static struct page *__read_cache_page(struct address_space *mapping,
pgoff_t index,
int (*filler)(void *, struct page *),
@@ -1821,6 +2171,8 @@ repeat:
if (err < 0) {
page_cache_release(page);
page = ERR_PTR(err);
+ } else {
+ page = wait_on_page_read(page);
}
}
return page;
@@ -1857,6 +2209,10 @@ retry:
if (err < 0) {
page_cache_release(page);
return ERR_PTR(err);
+ } else {
+ page = wait_on_page_read(page);
+ if (IS_ERR(page))
+ return page;
}
out:
mark_page_accessed(page);
@@ -1864,40 +2220,25 @@ out:
}
/**
- * read_cache_page_async - read into page cache, fill it if needed
+ * read_cache_page - read into page cache, fill it if needed
* @mapping: the page's address_space
* @index: the page index
* @filler: function to perform the read
* @data: first arg to filler(data, page) function, often left as NULL
*
- * Same as read_cache_page, but don't wait for page to become unlocked
- * after submitting it to the filler.
- *
* Read into the page cache. If a page already exists, and PageUptodate() is
- * not set, try to fill the page but don't wait for it to become unlocked.
+ * not set, try to fill the page and wait for it to become unlocked.
*
* If the page does not get brought uptodate, return -EIO.
*/
-struct page *read_cache_page_async(struct address_space *mapping,
+struct page *read_cache_page(struct address_space *mapping,
pgoff_t index,
int (*filler)(void *, struct page *),
void *data)
{
return do_read_cache_page(mapping, index, filler, data, mapping_gfp_mask(mapping));
}
-EXPORT_SYMBOL(read_cache_page_async);
-
-static struct page *wait_on_page_read(struct page *page)
-{
- if (!IS_ERR(page)) {
- wait_on_page_locked(page);
- if (!PageUptodate(page)) {
- page_cache_release(page);
- page = ERR_PTR(-EIO);
- }
- }
- return page;
-}
+EXPORT_SYMBOL(read_cache_page);
/**
* read_cache_page_gfp - read into page cache, using specified page allocation flags.
@@ -1916,31 +2257,10 @@ struct page *read_cache_page_gfp(struct address_space *mapping,
{
filler_t *filler = (filler_t *)mapping->a_ops->readpage;
- return wait_on_page_read(do_read_cache_page(mapping, index, filler, NULL, gfp));
+ return do_read_cache_page(mapping, index, filler, NULL, gfp);
}
EXPORT_SYMBOL(read_cache_page_gfp);
-/**
- * read_cache_page - read into page cache, fill it if needed
- * @mapping: the page's address_space
- * @index: the page index
- * @filler: function to perform the read
- * @data: first arg to filler(data, page) function, often left as NULL
- *
- * Read into the page cache. If a page already exists, and PageUptodate() is
- * not set, try to fill the page then wait for it to become unlocked.
- *
- * If the page does not get brought uptodate, return -EIO.
- */
-struct page *read_cache_page(struct address_space *mapping,
- pgoff_t index,
- int (*filler)(void *, struct page *),
- void *data)
-{
- return wait_on_page_read(read_cache_page_async(mapping, index, filler, data));
-}
-EXPORT_SYMBOL(read_cache_page);
-
static size_t __iovec_copy_from_user_inatomic(char *vaddr,
const struct iovec *iov, size_t base, size_t bytes)
{
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 1546655a2d78..6ac89e9f82ef 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -941,81 +941,6 @@ unlock:
spin_unlock(ptl);
}
-static int do_huge_pmd_wp_zero_page_fallback(struct mm_struct *mm,
- struct vm_area_struct *vma, unsigned long address,
- pmd_t *pmd, pmd_t orig_pmd, unsigned long haddr)
-{
- spinlock_t *ptl;
- pgtable_t pgtable;
- pmd_t _pmd;
- struct page *page;
- int i, ret = 0;
- unsigned long mmun_start; /* For mmu_notifiers */
- unsigned long mmun_end; /* For mmu_notifiers */
-
- page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);
- if (!page) {
- ret |= VM_FAULT_OOM;
- goto out;
- }
-
- if (mem_cgroup_newpage_charge(page, mm, GFP_KERNEL)) {
- put_page(page);
- ret |= VM_FAULT_OOM;
- goto out;
- }
-
- clear_user_highpage(page, address);
- __SetPageUptodate(page);
-
- mmun_start = haddr;
- mmun_end = haddr + HPAGE_PMD_SIZE;
- mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
-
- ptl = pmd_lock(mm, pmd);
- if (unlikely(!pmd_same(*pmd, orig_pmd)))
- goto out_free_page;
-
- pmdp_clear_flush(vma, haddr, pmd);
- /* leave pmd empty until pte is filled */
-
- pgtable = pgtable_trans_huge_withdraw(mm, pmd);
- pmd_populate(mm, &_pmd, pgtable);
-
- for (i = 0; i < HPAGE_PMD_NR; i++, haddr += PAGE_SIZE) {
- pte_t *pte, entry;
- if (haddr == (address & PAGE_MASK)) {
- entry = mk_pte(page, vma->vm_page_prot);
- entry = maybe_mkwrite(pte_mkdirty(entry), vma);
- page_add_new_anon_rmap(page, vma, haddr);
- } else {
- entry = pfn_pte(my_zero_pfn(haddr), vma->vm_page_prot);
- entry = pte_mkspecial(entry);
- }
- pte = pte_offset_map(&_pmd, haddr);
- VM_BUG_ON(!pte_none(*pte));
- set_pte_at(mm, haddr, pte, entry);
- pte_unmap(pte);
- }
- smp_wmb(); /* make pte visible before pmd */
- pmd_populate(mm, pmd, pgtable);
- spin_unlock(ptl);
- put_huge_zero_page();
- inc_mm_counter(mm, MM_ANONPAGES);
-
- mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
-
- ret |= VM_FAULT_WRITE;
-out:
- return ret;
-out_free_page:
- spin_unlock(ptl);
- mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
- mem_cgroup_uncharge_page(page);
- put_page(page);
- goto out;
-}
-
static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm,
struct vm_area_struct *vma,
unsigned long address,
@@ -1161,8 +1086,8 @@ alloc:
if (unlikely(!new_page)) {
if (!page) {
- ret = do_huge_pmd_wp_zero_page_fallback(mm, vma,
- address, pmd, orig_pmd, haddr);
+ split_huge_page_pmd(vma, address, pmd);
+ ret |= VM_FAULT_FALLBACK;
} else {
ret = do_huge_pmd_wp_page_fallback(mm, vma, address,
pmd, orig_pmd, page, haddr);
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index c01cb9fedb18..7c02b9dadfb0 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -22,6 +22,7 @@
#include <linux/swap.h>
#include <linux/swapops.h>
#include <linux/page-isolation.h>
+#include <linux/jhash.h>
#include <asm/page.h>
#include <asm/pgtable.h>
@@ -53,6 +54,13 @@ static unsigned long __initdata default_hstate_size;
*/
DEFINE_SPINLOCK(hugetlb_lock);
+/*
+ * Serializes faults on the same logical page. This is used to
+ * prevent spurious OOMs when the hugepage pool is fully utilized.
+ */
+static int num_fault_mutexes;
+static struct mutex *htlb_fault_mutex_table ____cacheline_aligned_in_smp;
+
static inline void unlock_or_release_subpool(struct hugepage_subpool *spool)
{
bool free = (spool->count == 0) && (spool->used_hpages == 0);
@@ -135,15 +143,8 @@ static inline struct hugepage_subpool *subpool_vma(struct vm_area_struct *vma)
* Region tracking -- allows tracking of reservations and instantiated pages
* across the pages in a mapping.
*
- * The region data structures are protected by a combination of the mmap_sem
- * and the hugetlb_instantiation_mutex. To access or modify a region the caller
- * must either hold the mmap_sem for write, or the mmap_sem for read and
- * the hugetlb_instantiation_mutex:
- *
- * down_write(&mm->mmap_sem);
- * or
- * down_read(&mm->mmap_sem);
- * mutex_lock(&hugetlb_instantiation_mutex);
+ * The region data structures are embedded into a resv_map and
+ * protected by a resv_map's lock
*/
struct file_region {
struct list_head link;
@@ -151,10 +152,12 @@ struct file_region {
long to;
};
-static long region_add(struct list_head *head, long f, long t)
+static long region_add(struct resv_map *resv, long f, long t)
{
+ struct list_head *head = &resv->regions;
struct file_region *rg, *nrg, *trg;
+ spin_lock(&resv->lock);
/* Locate the region we are either in or before. */
list_for_each_entry(rg, head, link)
if (f <= rg->to)
@@ -184,14 +187,18 @@ static long region_add(struct list_head *head, long f, long t)
}
nrg->from = f;
nrg->to = t;
+ spin_unlock(&resv->lock);
return 0;
}
-static long region_chg(struct list_head *head, long f, long t)
+static long region_chg(struct resv_map *resv, long f, long t)
{
- struct file_region *rg, *nrg;
+ struct list_head *head = &resv->regions;
+ struct file_region *rg, *nrg = NULL;
long chg = 0;
+retry:
+ spin_lock(&resv->lock);
/* Locate the region we are before or in. */
list_for_each_entry(rg, head, link)
if (f <= rg->to)
@@ -201,15 +208,21 @@ static long region_chg(struct list_head *head, long f, long t)
* Subtle, allocate a new region at the position but make it zero
* size such that we can guarantee to record the reservation. */
if (&rg->link == head || t < rg->from) {
- nrg = kmalloc(sizeof(*nrg), GFP_KERNEL);
- if (!nrg)
- return -ENOMEM;
- nrg->from = f;
- nrg->to = f;
- INIT_LIST_HEAD(&nrg->link);
- list_add(&nrg->link, rg->link.prev);
+ if (!nrg) {
+ spin_unlock(&resv->lock);
+ nrg = kmalloc(sizeof(*nrg), GFP_KERNEL);
+ if (!nrg)
+ return -ENOMEM;
+
+ nrg->from = f;
+ nrg->to = f;
+ INIT_LIST_HEAD(&nrg->link);
+ goto retry;
+ }
- return t - f;
+ list_add(&nrg->link, rg->link.prev);
+ chg = t - f;
+ goto out_nrg;
}
/* Round our left edge to the current segment if it encloses us. */
@@ -222,7 +235,7 @@ static long region_chg(struct list_head *head, long f, long t)
if (&rg->link == head)
break;
if (rg->from > t)
- return chg;
+ goto out;
/* We overlap with this area, if it extends further than
* us then we must extend ourselves. Account for its
@@ -233,20 +246,30 @@ static long region_chg(struct list_head *head, long f, long t)
}
chg -= rg->to - rg->from;
}
+
+out:
+ spin_unlock(&resv->lock);
+ /* We already know we raced and no longer need the new region */
+ kfree(nrg);
+ return chg;
+out_nrg:
+ spin_unlock(&resv->lock);
return chg;
}
-static long region_truncate(struct list_head *head, long end)
+static long region_truncate(struct resv_map *resv, long end)
{
+ struct list_head *head = &resv->regions;
struct file_region *rg, *trg;
long chg = 0;
+ spin_lock(&resv->lock);
/* Locate the region we are either in or before. */
list_for_each_entry(rg, head, link)
if (end <= rg->to)
break;
if (&rg->link == head)
- return 0;
+ goto out;
/* If we are in the middle of a region then adjust it. */
if (end > rg->from) {
@@ -263,14 +286,19 @@ static long region_truncate(struct list_head *head, long end)
list_del(&rg->link);
kfree(rg);
}
+
+out:
+ spin_unlock(&resv->lock);
return chg;
}
-static long region_count(struct list_head *head, long f, long t)
+static long region_count(struct resv_map *resv, long f, long t)
{
+ struct list_head *head = &resv->regions;
struct file_region *rg;
long chg = 0;
+ spin_lock(&resv->lock);
/* Locate each segment we overlap with, and count that overlap. */
list_for_each_entry(rg, head, link) {
long seg_from;
@@ -286,6 +314,7 @@ static long region_count(struct list_head *head, long f, long t)
chg += seg_to - seg_from;
}
+ spin_unlock(&resv->lock);
return chg;
}
@@ -376,39 +405,46 @@ static void set_vma_private_data(struct vm_area_struct *vma,
vma->vm_private_data = (void *)value;
}
-struct resv_map {
- struct kref refs;
- struct list_head regions;
-};
-
-static struct resv_map *resv_map_alloc(void)
+struct resv_map *resv_map_alloc(void)
{
struct resv_map *resv_map = kmalloc(sizeof(*resv_map), GFP_KERNEL);
if (!resv_map)
return NULL;
kref_init(&resv_map->refs);
+ spin_lock_init(&resv_map->lock);
INIT_LIST_HEAD(&resv_map->regions);
return resv_map;
}
-static void resv_map_release(struct kref *ref)
+void resv_map_release(struct kref *ref)
{
struct resv_map *resv_map = container_of(ref, struct resv_map, refs);
/* Clear out any active regions before we release the map. */
- region_truncate(&resv_map->regions, 0);
+ region_truncate(resv_map, 0);
kfree(resv_map);
}
+static inline struct resv_map *inode_resv_map(struct inode *inode)
+{
+ return inode->i_mapping->private_data;
+}
+
static struct resv_map *vma_resv_map(struct vm_area_struct *vma)
{
VM_BUG_ON(!is_vm_hugetlb_page(vma));
- if (!(vma->vm_flags & VM_MAYSHARE))
+ if (vma->vm_flags & VM_MAYSHARE) {
+ struct address_space *mapping = vma->vm_file->f_mapping;
+ struct inode *inode = mapping->host;
+
+ return inode_resv_map(inode);
+
+ } else {
return (struct resv_map *)(get_vma_private_data(vma) &
~HPAGE_RESV_MASK);
- return NULL;
+ }
}
static void set_vma_resv_map(struct vm_area_struct *vma, struct resv_map *map)
@@ -540,7 +576,7 @@ static struct page *dequeue_huge_page_vma(struct hstate *h,
goto err;
retry_cpuset:
- cpuset_mems_cookie = get_mems_allowed();
+ cpuset_mems_cookie = read_mems_allowed_begin();
zonelist = huge_zonelist(vma, address,
htlb_alloc_mask(h), &mpol, &nodemask);
@@ -562,7 +598,7 @@ retry_cpuset:
}
mpol_cond_put(mpol);
- if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
+ if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie)))
goto retry_cpuset;
return page;
@@ -653,7 +689,8 @@ static void prep_new_huge_page(struct hstate *h, struct page *page, int nid)
put_page(page); /* free it into the hugepage allocator */
}
-static void prep_compound_gigantic_page(struct page *page, unsigned long order)
+static void __init prep_compound_gigantic_page(struct page *page,
+ unsigned long order)
{
int i;
int nr_pages = 1 << order;
@@ -1150,45 +1187,34 @@ static void return_unused_surplus_pages(struct hstate *h,
static long vma_needs_reservation(struct hstate *h,
struct vm_area_struct *vma, unsigned long addr)
{
- struct address_space *mapping = vma->vm_file->f_mapping;
- struct inode *inode = mapping->host;
-
- if (vma->vm_flags & VM_MAYSHARE) {
- pgoff_t idx = vma_hugecache_offset(h, vma, addr);
- return region_chg(&inode->i_mapping->private_list,
- idx, idx + 1);
+ struct resv_map *resv;
+ pgoff_t idx;
+ long chg;
- } else if (!is_vma_resv_set(vma, HPAGE_RESV_OWNER)) {
+ resv = vma_resv_map(vma);
+ if (!resv)
return 1;
- } else {
- long err;
- pgoff_t idx = vma_hugecache_offset(h, vma, addr);
- struct resv_map *resv = vma_resv_map(vma);
+ idx = vma_hugecache_offset(h, vma, addr);
+ chg = region_chg(resv, idx, idx + 1);
- err = region_chg(&resv->regions, idx, idx + 1);
- if (err < 0)
- return err;
- return 0;
- }
+ if (vma->vm_flags & VM_MAYSHARE)
+ return chg;
+ else
+ return chg < 0 ? chg : 0;
}
static void vma_commit_reservation(struct hstate *h,
struct vm_area_struct *vma, unsigned long addr)
{
- struct address_space *mapping = vma->vm_file->f_mapping;
- struct inode *inode = mapping->host;
-
- if (vma->vm_flags & VM_MAYSHARE) {
- pgoff_t idx = vma_hugecache_offset(h, vma, addr);
- region_add(&inode->i_mapping->private_list, idx, idx + 1);
+ struct resv_map *resv;
+ pgoff_t idx;
- } else if (is_vma_resv_set(vma, HPAGE_RESV_OWNER)) {
- pgoff_t idx = vma_hugecache_offset(h, vma, addr);
- struct resv_map *resv = vma_resv_map(vma);
+ resv = vma_resv_map(vma);
+ if (!resv)
+ return;
- /* Mark this page used in the map. */
- region_add(&resv->regions, idx, idx + 1);
- }
+ idx = vma_hugecache_offset(h, vma, addr);
+ region_add(resv, idx, idx + 1);
}
static struct page *alloc_huge_page(struct vm_area_struct *vma,
@@ -1294,7 +1320,7 @@ found:
return 1;
}
-static void prep_compound_huge_page(struct page *page, int order)
+static void __init prep_compound_huge_page(struct page *page, int order)
{
if (unlikely(order > (MAX_ORDER - 1)))
prep_compound_gigantic_page(page, order);
@@ -1944,11 +1970,14 @@ static void __exit hugetlb_exit(void)
}
kobject_put(hugepages_kobj);
+ kfree(htlb_fault_mutex_table);
}
module_exit(hugetlb_exit);
static int __init hugetlb_init(void)
{
+ int i;
+
/* Some platform decide whether they support huge pages at boot
* time. On these, such as powerpc, HPAGE_SHIFT is set to 0 when
* there is no such support
@@ -1973,6 +2002,17 @@ static int __init hugetlb_init(void)
hugetlb_register_all_nodes();
hugetlb_cgroup_file_init();
+#ifdef CONFIG_SMP
+ num_fault_mutexes = roundup_pow_of_two(8 * num_possible_cpus());
+#else
+ num_fault_mutexes = 1;
+#endif
+ htlb_fault_mutex_table =
+ kmalloc(sizeof(struct mutex) * num_fault_mutexes, GFP_KERNEL);
+ BUG_ON(!htlb_fault_mutex_table);
+
+ for (i = 0; i < num_fault_mutexes; i++)
+ mutex_init(&htlb_fault_mutex_table[i]);
return 0;
}
module_init(hugetlb_init);
@@ -2251,41 +2291,30 @@ static void hugetlb_vm_op_open(struct vm_area_struct *vma)
* after this open call completes. It is therefore safe to take a
* new reference here without additional locking.
*/
- if (resv)
+ if (resv && is_vma_resv_set(vma, HPAGE_RESV_OWNER))
kref_get(&resv->refs);
}
-static void resv_map_put(struct vm_area_struct *vma)
-{
- struct resv_map *resv = vma_resv_map(vma);
-
- if (!resv)
- return;
- kref_put(&resv->refs, resv_map_release);
-}
-
static void hugetlb_vm_op_close(struct vm_area_struct *vma)
{
struct hstate *h = hstate_vma(vma);
struct resv_map *resv = vma_resv_map(vma);
struct hugepage_subpool *spool = subpool_vma(vma);
- unsigned long reserve;
- unsigned long start;
- unsigned long end;
+ unsigned long reserve, start, end;
- if (resv) {
- start = vma_hugecache_offset(h, vma, vma->vm_start);
- end = vma_hugecache_offset(h, vma, vma->vm_end);
+ if (!resv || !is_vma_resv_set(vma, HPAGE_RESV_OWNER))
+ return;
- reserve = (end - start) -
- region_count(&resv->regions, start, end);
+ start = vma_hugecache_offset(h, vma, vma->vm_start);
+ end = vma_hugecache_offset(h, vma, vma->vm_end);
- resv_map_put(vma);
+ reserve = (end - start) - region_count(resv, start, end);
- if (reserve) {
- hugetlb_acct_memory(h, -reserve);
- hugepage_subpool_put_pages(spool, reserve);
- }
+ kref_put(&resv->refs, resv_map_release);
+
+ if (reserve) {
+ hugetlb_acct_memory(h, -reserve);
+ hugepage_subpool_put_pages(spool, reserve);
}
}
@@ -2761,15 +2790,14 @@ static bool hugetlbfs_pagecache_present(struct hstate *h,
}
static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long address, pte_t *ptep, unsigned int flags)
+ struct address_space *mapping, pgoff_t idx,
+ unsigned long address, pte_t *ptep, unsigned int flags)
{
struct hstate *h = hstate_vma(vma);
int ret = VM_FAULT_SIGBUS;
int anon_rmap = 0;
- pgoff_t idx;
unsigned long size;
struct page *page;
- struct address_space *mapping;
pte_t new_pte;
spinlock_t *ptl;
@@ -2784,9 +2812,6 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma,
return ret;
}
- mapping = vma->vm_file->f_mapping;
- idx = vma_hugecache_offset(h, vma, address);
-
/*
* Use page lock to guard against racing truncation
* before we get page_table_lock.
@@ -2896,17 +2921,53 @@ backout_unlocked:
goto out;
}
+#ifdef CONFIG_SMP
+static u32 fault_mutex_hash(struct hstate *h, struct mm_struct *mm,
+ struct vm_area_struct *vma,
+ struct address_space *mapping,
+ pgoff_t idx, unsigned long address)
+{
+ unsigned long key[2];
+ u32 hash;
+
+ if (vma->vm_flags & VM_SHARED) {
+ key[0] = (unsigned long) mapping;
+ key[1] = idx;
+ } else {
+ key[0] = (unsigned long) mm;
+ key[1] = address >> huge_page_shift(h);
+ }
+
+ hash = jhash2((u32 *)&key, sizeof(key)/sizeof(u32), 0);
+
+ return hash & (num_fault_mutexes - 1);
+}
+#else
+/*
+ * For uniprocesor systems we always use a single mutex, so just
+ * return 0 and avoid the hashing overhead.
+ */
+static u32 fault_mutex_hash(struct hstate *h, struct mm_struct *mm,
+ struct vm_area_struct *vma,
+ struct address_space *mapping,
+ pgoff_t idx, unsigned long address)
+{
+ return 0;
+}
+#endif
+
int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long address, unsigned int flags)
{
- pte_t *ptep;
- pte_t entry;
+ pte_t *ptep, entry;
spinlock_t *ptl;
int ret;
+ u32 hash;
+ pgoff_t idx;
struct page *page = NULL;
struct page *pagecache_page = NULL;
- static DEFINE_MUTEX(hugetlb_instantiation_mutex);
struct hstate *h = hstate_vma(vma);
+ struct address_space *mapping;
address &= huge_page_mask(h);
@@ -2925,15 +2986,20 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
if (!ptep)
return VM_FAULT_OOM;
+ mapping = vma->vm_file->f_mapping;
+ idx = vma_hugecache_offset(h, vma, address);
+
/*
* Serialize hugepage allocation and instantiation, so that we don't
* get spurious allocation failures if two CPUs race to instantiate
* the same page in the page cache.
*/
- mutex_lock(&hugetlb_instantiation_mutex);
+ hash = fault_mutex_hash(h, mm, vma, mapping, idx, address);
+ mutex_lock(&htlb_fault_mutex_table[hash]);
+
entry = huge_ptep_get(ptep);
if (huge_pte_none(entry)) {
- ret = hugetlb_no_page(mm, vma, address, ptep, flags);
+ ret = hugetlb_no_page(mm, vma, mapping, idx, address, ptep, flags);
goto out_mutex;
}
@@ -3002,8 +3068,7 @@ out_ptl:
put_page(page);
out_mutex:
- mutex_unlock(&hugetlb_instantiation_mutex);
-
+ mutex_unlock(&htlb_fault_mutex_table[hash]);
return ret;
}
@@ -3161,6 +3226,7 @@ int hugetlb_reserve_pages(struct inode *inode,
long ret, chg;
struct hstate *h = hstate_inode(inode);
struct hugepage_subpool *spool = subpool_inode(inode);
+ struct resv_map *resv_map;
/*
* Only apply hugepage reservation if asked. At fault time, an
@@ -3176,10 +3242,13 @@ int hugetlb_reserve_pages(struct inode *inode,
* to reserve the full area even if read-only as mprotect() may be
* called to make the mapping read-write. Assume !vma is a shm mapping
*/
- if (!vma || vma->vm_flags & VM_MAYSHARE)
- chg = region_chg(&inode->i_mapping->private_list, from, to);
- else {
- struct resv_map *resv_map = resv_map_alloc();
+ if (!vma || vma->vm_flags & VM_MAYSHARE) {
+ resv_map = inode_resv_map(inode);
+
+ chg = region_chg(resv_map, from, to);
+
+ } else {
+ resv_map = resv_map_alloc();
if (!resv_map)
return -ENOMEM;
@@ -3222,20 +3291,23 @@ int hugetlb_reserve_pages(struct inode *inode,
* else has to be done for private mappings here
*/
if (!vma || vma->vm_flags & VM_MAYSHARE)
- region_add(&inode->i_mapping->private_list, from, to);
+ region_add(resv_map, from, to);
return 0;
out_err:
- if (vma)
- resv_map_put(vma);
+ if (vma && is_vma_resv_set(vma, HPAGE_RESV_OWNER))
+ kref_put(&resv_map->refs, resv_map_release);
return ret;
}
void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed)
{
struct hstate *h = hstate_inode(inode);
- long chg = region_truncate(&inode->i_mapping->private_list, offset);
+ struct resv_map *resv_map = inode_resv_map(inode);
+ long chg = 0;
struct hugepage_subpool *spool = subpool_inode(inode);
+ if (resv_map)
+ chg = region_truncate(resv_map, offset);
spin_lock(&inode->i_lock);
inode->i_blocks -= (blocks_per_huge_page(h) * freed);
spin_unlock(&inode->i_lock);
diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c
index cb00829bb466..595d7fd795e1 100644
--- a/mm/hugetlb_cgroup.c
+++ b/mm/hugetlb_cgroup.c
@@ -30,7 +30,6 @@ struct hugetlb_cgroup {
#define MEMFILE_IDX(val) (((val) >> 16) & 0xffff)
#define MEMFILE_ATTR(val) ((val) & 0xffff)
-struct cgroup_subsys hugetlb_subsys __read_mostly;
static struct hugetlb_cgroup *root_h_cgroup __read_mostly;
static inline
@@ -42,7 +41,7 @@ struct hugetlb_cgroup *hugetlb_cgroup_from_css(struct cgroup_subsys_state *s)
static inline
struct hugetlb_cgroup *hugetlb_cgroup_from_task(struct task_struct *task)
{
- return hugetlb_cgroup_from_css(task_css(task, hugetlb_subsys_id));
+ return hugetlb_cgroup_from_css(task_css(task, hugetlb_cgrp_id));
}
static inline bool hugetlb_cgroup_is_root(struct hugetlb_cgroup *h_cg)
@@ -255,7 +254,7 @@ static u64 hugetlb_cgroup_read_u64(struct cgroup_subsys_state *css,
}
static int hugetlb_cgroup_write(struct cgroup_subsys_state *css,
- struct cftype *cft, const char *buffer)
+ struct cftype *cft, char *buffer)
{
int idx, name, ret;
unsigned long long val;
@@ -358,7 +357,7 @@ static void __init __hugetlb_cgroup_file_init(int idx)
cft = &h->cgroup_files[4];
memset(cft, 0, sizeof(*cft));
- WARN_ON(cgroup_add_cftypes(&hugetlb_subsys, h->cgroup_files));
+ WARN_ON(cgroup_add_cftypes(&hugetlb_cgrp_subsys, h->cgroup_files));
return;
}
@@ -402,10 +401,8 @@ void hugetlb_cgroup_migrate(struct page *oldhpage, struct page *newhpage)
return;
}
-struct cgroup_subsys hugetlb_subsys = {
- .name = "hugetlb",
+struct cgroup_subsys hugetlb_cgrp_subsys = {
.css_alloc = hugetlb_cgroup_css_alloc,
.css_offline = hugetlb_cgroup_css_offline,
.css_free = hugetlb_cgroup_css_free,
- .subsys_id = hugetlb_subsys_id,
};
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 31f01c5011e5..91d67eaee050 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -192,15 +192,15 @@ static struct kmem_cache *object_cache;
static struct kmem_cache *scan_area_cache;
/* set if tracing memory operations is enabled */
-static atomic_t kmemleak_enabled = ATOMIC_INIT(0);
+static int kmemleak_enabled;
/* set in the late_initcall if there were no errors */
-static atomic_t kmemleak_initialized = ATOMIC_INIT(0);
+static int kmemleak_initialized;
/* enables or disables early logging of the memory operations */
-static atomic_t kmemleak_early_log = ATOMIC_INIT(1);
+static int kmemleak_early_log = 1;
/* set if a kmemleak warning was issued */
-static atomic_t kmemleak_warning = ATOMIC_INIT(0);
+static int kmemleak_warning;
/* set if a fatal kmemleak error has occurred */
-static atomic_t kmemleak_error = ATOMIC_INIT(0);
+static int kmemleak_error;
/* minimum and maximum address that may be valid pointers */
static unsigned long min_addr = ULONG_MAX;
@@ -218,7 +218,8 @@ static int kmemleak_stack_scan = 1;
static DEFINE_MUTEX(scan_mutex);
/* setting kmemleak=on, will set this var, skipping the disable */
static int kmemleak_skip_disable;
-
+/* If there are leaks that can be reported */
+static bool kmemleak_found_leaks;
/*
* Early object allocation/freeing logging. Kmemleak is initialized after the
@@ -267,7 +268,7 @@ static void kmemleak_disable(void);
#define kmemleak_warn(x...) do { \
pr_warning(x); \
dump_stack(); \
- atomic_set(&kmemleak_warning, 1); \
+ kmemleak_warning = 1; \
} while (0)
/*
@@ -805,7 +806,7 @@ static void __init log_early(int op_type, const void *ptr, size_t size,
unsigned long flags;
struct early_log *log;
- if (atomic_read(&kmemleak_error)) {
+ if (kmemleak_error) {
/* kmemleak stopped recording, just count the requests */
crt_early_log++;
return;
@@ -840,7 +841,7 @@ static void early_alloc(struct early_log *log)
unsigned long flags;
int i;
- if (!atomic_read(&kmemleak_enabled) || !log->ptr || IS_ERR(log->ptr))
+ if (!kmemleak_enabled || !log->ptr || IS_ERR(log->ptr))
return;
/*
@@ -893,9 +894,9 @@ void __ref kmemleak_alloc(const void *ptr, size_t size, int min_count,
{
pr_debug("%s(0x%p, %zu, %d)\n", __func__, ptr, size, min_count);
- if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
+ if (kmemleak_enabled && ptr && !IS_ERR(ptr))
create_object((unsigned long)ptr, size, min_count, gfp);
- else if (atomic_read(&kmemleak_early_log))
+ else if (kmemleak_early_log)
log_early(KMEMLEAK_ALLOC, ptr, size, min_count);
}
EXPORT_SYMBOL_GPL(kmemleak_alloc);
@@ -919,11 +920,11 @@ void __ref kmemleak_alloc_percpu(const void __percpu *ptr, size_t size)
* Percpu allocations are only scanned and not reported as leaks
* (min_count is set to 0).
*/
- if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
+ if (kmemleak_enabled && ptr && !IS_ERR(ptr))
for_each_possible_cpu(cpu)
create_object((unsigned long)per_cpu_ptr(ptr, cpu),
size, 0, GFP_KERNEL);
- else if (atomic_read(&kmemleak_early_log))
+ else if (kmemleak_early_log)
log_early(KMEMLEAK_ALLOC_PERCPU, ptr, size, 0);
}
EXPORT_SYMBOL_GPL(kmemleak_alloc_percpu);
@@ -939,9 +940,9 @@ void __ref kmemleak_free(const void *ptr)
{
pr_debug("%s(0x%p)\n", __func__, ptr);
- if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
+ if (kmemleak_enabled && ptr && !IS_ERR(ptr))
delete_object_full((unsigned long)ptr);
- else if (atomic_read(&kmemleak_early_log))
+ else if (kmemleak_early_log)
log_early(KMEMLEAK_FREE, ptr, 0, 0);
}
EXPORT_SYMBOL_GPL(kmemleak_free);
@@ -959,9 +960,9 @@ void __ref kmemleak_free_part(const void *ptr, size_t size)
{
pr_debug("%s(0x%p)\n", __func__, ptr);
- if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
+ if (kmemleak_enabled && ptr && !IS_ERR(ptr))
delete_object_part((unsigned long)ptr, size);
- else if (atomic_read(&kmemleak_early_log))
+ else if (kmemleak_early_log)
log_early(KMEMLEAK_FREE_PART, ptr, size, 0);
}
EXPORT_SYMBOL_GPL(kmemleak_free_part);
@@ -979,11 +980,11 @@ void __ref kmemleak_free_percpu(const void __percpu *ptr)
pr_debug("%s(0x%p)\n", __func__, ptr);
- if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
+ if (kmemleak_enabled && ptr && !IS_ERR(ptr))
for_each_possible_cpu(cpu)
delete_object_full((unsigned long)per_cpu_ptr(ptr,
cpu));
- else if (atomic_read(&kmemleak_early_log))
+ else if (kmemleak_early_log)
log_early(KMEMLEAK_FREE_PERCPU, ptr, 0, 0);
}
EXPORT_SYMBOL_GPL(kmemleak_free_percpu);
@@ -999,9 +1000,9 @@ void __ref kmemleak_not_leak(const void *ptr)
{
pr_debug("%s(0x%p)\n", __func__, ptr);
- if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
+ if (kmemleak_enabled && ptr && !IS_ERR(ptr))
make_gray_object((unsigned long)ptr);
- else if (atomic_read(&kmemleak_early_log))
+ else if (kmemleak_early_log)
log_early(KMEMLEAK_NOT_LEAK, ptr, 0, 0);
}
EXPORT_SYMBOL(kmemleak_not_leak);
@@ -1019,9 +1020,9 @@ void __ref kmemleak_ignore(const void *ptr)
{
pr_debug("%s(0x%p)\n", __func__, ptr);
- if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
+ if (kmemleak_enabled && ptr && !IS_ERR(ptr))
make_black_object((unsigned long)ptr);
- else if (atomic_read(&kmemleak_early_log))
+ else if (kmemleak_early_log)
log_early(KMEMLEAK_IGNORE, ptr, 0, 0);
}
EXPORT_SYMBOL(kmemleak_ignore);
@@ -1041,9 +1042,9 @@ void __ref kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp)
{
pr_debug("%s(0x%p)\n", __func__, ptr);
- if (atomic_read(&kmemleak_enabled) && ptr && size && !IS_ERR(ptr))
+ if (kmemleak_enabled && ptr && size && !IS_ERR(ptr))
add_scan_area((unsigned long)ptr, size, gfp);
- else if (atomic_read(&kmemleak_early_log))
+ else if (kmemleak_early_log)
log_early(KMEMLEAK_SCAN_AREA, ptr, size, 0);
}
EXPORT_SYMBOL(kmemleak_scan_area);
@@ -1061,9 +1062,9 @@ void __ref kmemleak_no_scan(const void *ptr)
{
pr_debug("%s(0x%p)\n", __func__, ptr);
- if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
+ if (kmemleak_enabled && ptr && !IS_ERR(ptr))
object_no_scan((unsigned long)ptr);
- else if (atomic_read(&kmemleak_early_log))
+ else if (kmemleak_early_log)
log_early(KMEMLEAK_NO_SCAN, ptr, 0, 0);
}
EXPORT_SYMBOL(kmemleak_no_scan);
@@ -1088,7 +1089,7 @@ static bool update_checksum(struct kmemleak_object *object)
*/
static int scan_should_stop(void)
{
- if (!atomic_read(&kmemleak_enabled))
+ if (!kmemleak_enabled)
return 1;
/*
@@ -1382,9 +1383,12 @@ static void kmemleak_scan(void)
}
rcu_read_unlock();
- if (new_leaks)
+ if (new_leaks) {
+ kmemleak_found_leaks = true;
+
pr_info("%d new suspected memory leaks (see "
"/sys/kernel/debug/kmemleak)\n", new_leaks);
+ }
}
@@ -1545,11 +1549,6 @@ static int kmemleak_open(struct inode *inode, struct file *file)
return seq_open(file, &kmemleak_seq_ops);
}
-static int kmemleak_release(struct inode *inode, struct file *file)
-{
- return seq_release(inode, file);
-}
-
static int dump_str_object_info(const char *str)
{
unsigned long flags;
@@ -1592,8 +1591,12 @@ static void kmemleak_clear(void)
spin_unlock_irqrestore(&object->lock, flags);
}
rcu_read_unlock();
+
+ kmemleak_found_leaks = false;
}
+static void __kmemleak_do_cleanup(void);
+
/*
* File write operation to configure kmemleak at run-time. The following
* commands can be written to the /sys/kernel/debug/kmemleak file:
@@ -1606,7 +1609,8 @@ static void kmemleak_clear(void)
* disable it)
* scan - trigger a memory scan
* clear - mark all current reported unreferenced kmemleak objects as
- * grey to ignore printing them
+ * grey to ignore printing them, or free all kmemleak objects
+ * if kmemleak has been disabled.
* dump=... - dump information about the object found at the given address
*/
static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
@@ -1616,9 +1620,6 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
int buf_size;
int ret;
- if (!atomic_read(&kmemleak_enabled))
- return -EBUSY;
-
buf_size = min(size, (sizeof(buf) - 1));
if (strncpy_from_user(buf, user_buf, buf_size) < 0)
return -EFAULT;
@@ -1628,6 +1629,19 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
if (ret < 0)
return ret;
+ if (strncmp(buf, "clear", 5) == 0) {
+ if (kmemleak_enabled)
+ kmemleak_clear();
+ else
+ __kmemleak_do_cleanup();
+ goto out;
+ }
+
+ if (!kmemleak_enabled) {
+ ret = -EBUSY;
+ goto out;
+ }
+
if (strncmp(buf, "off", 3) == 0)
kmemleak_disable();
else if (strncmp(buf, "stack=on", 8) == 0)
@@ -1651,8 +1665,6 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
}
} else if (strncmp(buf, "scan", 4) == 0)
kmemleak_scan();
- else if (strncmp(buf, "clear", 5) == 0)
- kmemleak_clear();
else if (strncmp(buf, "dump=", 5) == 0)
ret = dump_str_object_info(buf + 5);
else
@@ -1674,9 +1686,19 @@ static const struct file_operations kmemleak_fops = {
.read = seq_read,
.write = kmemleak_write,
.llseek = seq_lseek,
- .release = kmemleak_release,
+ .release = seq_release,
};
+static void __kmemleak_do_cleanup(void)
+{
+ struct kmemleak_object *object;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(object, &object_list, object_list)
+ delete_object_full(object->pointer);
+ rcu_read_unlock();
+}
+
/*
* Stop the memory scanning thread and free the kmemleak internal objects if
* no previous scan thread (otherwise, kmemleak may still have some useful
@@ -1684,18 +1706,14 @@ static const struct file_operations kmemleak_fops = {
*/
static void kmemleak_do_cleanup(struct work_struct *work)
{
- struct kmemleak_object *object;
- bool cleanup = scan_thread == NULL;
-
mutex_lock(&scan_mutex);
stop_scan_thread();
- if (cleanup) {
- rcu_read_lock();
- list_for_each_entry_rcu(object, &object_list, object_list)
- delete_object_full(object->pointer);
- rcu_read_unlock();
- }
+ if (!kmemleak_found_leaks)
+ __kmemleak_do_cleanup();
+ else
+ pr_info("Kmemleak disabled without freeing internal data. "
+ "Reclaim the memory with \"echo clear > /sys/kernel/debug/kmemleak\"\n");
mutex_unlock(&scan_mutex);
}
@@ -1708,14 +1726,14 @@ static DECLARE_WORK(cleanup_work, kmemleak_do_cleanup);
static void kmemleak_disable(void)
{
/* atomically check whether it was already invoked */
- if (atomic_cmpxchg(&kmemleak_error, 0, 1))
+ if (cmpxchg(&kmemleak_error, 0, 1))
return;
/* stop any memory operation tracing */
- atomic_set(&kmemleak_enabled, 0);
+ kmemleak_enabled = 0;
/* check whether it is too early for a kernel thread */
- if (atomic_read(&kmemleak_initialized))
+ if (kmemleak_initialized)
schedule_work(&cleanup_work);
pr_info("Kernel memory leak detector disabled\n");
@@ -1757,9 +1775,10 @@ void __init kmemleak_init(void)
int i;
unsigned long flags;
+ kmemleak_early_log = 0;
+
#ifdef CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF
if (!kmemleak_skip_disable) {
- atomic_set(&kmemleak_early_log, 0);
kmemleak_disable();
return;
}
@@ -1777,12 +1796,11 @@ void __init kmemleak_init(void)
/* the kernel is still in UP mode, so disabling the IRQs is enough */
local_irq_save(flags);
- atomic_set(&kmemleak_early_log, 0);
- if (atomic_read(&kmemleak_error)) {
+ if (kmemleak_error) {
local_irq_restore(flags);
return;
} else
- atomic_set(&kmemleak_enabled, 1);
+ kmemleak_enabled = 1;
local_irq_restore(flags);
/*
@@ -1826,9 +1844,9 @@ void __init kmemleak_init(void)
log->op_type);
}
- if (atomic_read(&kmemleak_warning)) {
+ if (kmemleak_warning) {
print_log_trace(log);
- atomic_set(&kmemleak_warning, 0);
+ kmemleak_warning = 0;
}
}
}
@@ -1840,9 +1858,9 @@ static int __init kmemleak_late_init(void)
{
struct dentry *dentry;
- atomic_set(&kmemleak_initialized, 1);
+ kmemleak_initialized = 1;
- if (atomic_read(&kmemleak_error)) {
+ if (kmemleak_error) {
/*
* Some error occurred and kmemleak was disabled. There is a
* small chance that kmemleak_disable() was called immediately
diff --git a/mm/list_lru.c b/mm/list_lru.c
index 72f9decb0104..f1a0db194173 100644
--- a/mm/list_lru.c
+++ b/mm/list_lru.c
@@ -87,11 +87,20 @@ restart:
ret = isolate(item, &nlru->lock, cb_arg);
switch (ret) {
+ case LRU_REMOVED_RETRY:
+ assert_spin_locked(&nlru->lock);
case LRU_REMOVED:
if (--nlru->nr_items == 0)
node_clear(nid, lru->active_nodes);
WARN_ON_ONCE(nlru->nr_items < 0);
isolated++;
+ /*
+ * If the lru lock has been dropped, our list
+ * traversal is now invalid and so we have to
+ * restart from scratch.
+ */
+ if (ret == LRU_REMOVED_RETRY)
+ goto restart;
break;
case LRU_ROTATE:
list_move_tail(item, &nlru->list);
@@ -103,6 +112,7 @@ restart:
* The lru lock has been dropped, our list traversal is
* now invalid and so we have to restart from scratch.
*/
+ assert_spin_locked(&nlru->lock);
goto restart;
default:
BUG();
@@ -114,7 +124,7 @@ restart:
}
EXPORT_SYMBOL_GPL(list_lru_walk_node);
-int list_lru_init(struct list_lru *lru)
+int list_lru_init_key(struct list_lru *lru, struct lock_class_key *key)
{
int i;
size_t size = sizeof(*lru->node) * nr_node_ids;
@@ -126,12 +136,14 @@ int list_lru_init(struct list_lru *lru)
nodes_clear(lru->active_nodes);
for (i = 0; i < nr_node_ids; i++) {
spin_lock_init(&lru->node[i].lock);
+ if (key)
+ lockdep_set_class(&lru->node[i].lock, key);
INIT_LIST_HEAD(&lru->node[i].list);
lru->node[i].nr_items = 0;
}
return 0;
}
-EXPORT_SYMBOL_GPL(list_lru_init);
+EXPORT_SYMBOL_GPL(list_lru_init_key);
void list_lru_destroy(struct list_lru *lru)
{
diff --git a/mm/memblock.c b/mm/memblock.c
index 39a31e7f0045..7fe5354e7552 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -1407,6 +1407,11 @@ void __init_memblock memblock_set_current_limit(phys_addr_t limit)
memblock.current_limit = limit;
}
+phys_addr_t __init_memblock memblock_get_current_limit(void)
+{
+ return memblock.current_limit;
+}
+
static void __init_memblock memblock_dump(struct memblock_type *type, char *name)
{
unsigned long long base, size;
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 5b6b0039f725..dcc8153a1681 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -66,8 +66,8 @@
#include <trace/events/vmscan.h>
-struct cgroup_subsys mem_cgroup_subsys __read_mostly;
-EXPORT_SYMBOL(mem_cgroup_subsys);
+struct cgroup_subsys memory_cgrp_subsys __read_mostly;
+EXPORT_SYMBOL(memory_cgrp_subsys);
#define MEM_CGROUP_RECLAIM_RETRIES 5
static struct mem_cgroup *root_mem_cgroup __read_mostly;
@@ -538,7 +538,7 @@ static inline struct mem_cgroup *mem_cgroup_from_id(unsigned short id)
{
struct cgroup_subsys_state *css;
- css = css_from_id(id - 1, &mem_cgroup_subsys);
+ css = css_from_id(id - 1, &memory_cgrp_subsys);
return mem_cgroup_from_css(css);
}
@@ -1072,7 +1072,7 @@ struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p)
if (unlikely(!p))
return NULL;
- return mem_cgroup_from_css(task_css(p, mem_cgroup_subsys_id));
+ return mem_cgroup_from_css(task_css(p, memory_cgrp_id));
}
struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm)
@@ -1683,15 +1683,8 @@ static void move_unlock_mem_cgroup(struct mem_cgroup *memcg,
*/
void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p)
{
- /*
- * protects memcg_name and makes sure that parallel ooms do not
- * interleave
- */
+ /* oom_info_lock ensures that parallel ooms do not interleave */
static DEFINE_MUTEX(oom_info_lock);
- struct cgroup *task_cgrp;
- struct cgroup *mem_cgrp;
- static char memcg_name[PATH_MAX];
- int ret;
struct mem_cgroup *iter;
unsigned int i;
@@ -1701,36 +1694,14 @@ void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p)
mutex_lock(&oom_info_lock);
rcu_read_lock();
- mem_cgrp = memcg->css.cgroup;
- task_cgrp = task_cgroup(p, mem_cgroup_subsys_id);
-
- ret = cgroup_path(task_cgrp, memcg_name, PATH_MAX);
- if (ret < 0) {
- /*
- * Unfortunately, we are unable to convert to a useful name
- * But we'll still print out the usage information
- */
- rcu_read_unlock();
- goto done;
- }
- rcu_read_unlock();
-
- pr_info("Task in %s killed", memcg_name);
+ pr_info("Task in ");
+ pr_cont_cgroup_path(task_cgroup(p, memory_cgrp_id));
+ pr_info(" killed as a result of limit of ");
+ pr_cont_cgroup_path(memcg->css.cgroup);
+ pr_info("\n");
- rcu_read_lock();
- ret = cgroup_path(mem_cgrp, memcg_name, PATH_MAX);
- if (ret < 0) {
- rcu_read_unlock();
- goto done;
- }
rcu_read_unlock();
- /*
- * Continues from above, so we don't need an KERN_ level
- */
- pr_cont(" as a result of limit of %s\n", memcg_name);
-done:
-
pr_info("memory: usage %llukB, limit %llukB, failcnt %llu\n",
res_counter_read_u64(&memcg->res, RES_USAGE) >> 10,
res_counter_read_u64(&memcg->res, RES_LIMIT) >> 10,
@@ -1745,13 +1716,8 @@ done:
res_counter_read_u64(&memcg->kmem, RES_FAILCNT));
for_each_mem_cgroup_tree(iter, memcg) {
- pr_info("Memory cgroup stats");
-
- rcu_read_lock();
- ret = cgroup_path(iter->css.cgroup, memcg_name, PATH_MAX);
- if (!ret)
- pr_cont(" for %s", memcg_name);
- rcu_read_unlock();
+ pr_info("Memory cgroup stats for ");
+ pr_cont_cgroup_path(iter->css.cgroup);
pr_cont(":");
for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
@@ -3401,7 +3367,7 @@ static struct kmem_cache *memcg_create_kmem_cache(struct mem_cgroup *memcg,
struct kmem_cache *s)
{
struct kmem_cache *new = NULL;
- static char *tmp_name = NULL;
+ static char *tmp_path = NULL, *tmp_name = NULL;
static DEFINE_MUTEX(mutex); /* protects tmp_name */
BUG_ON(!memcg_can_account_kmem(memcg));
@@ -3413,18 +3379,20 @@ static struct kmem_cache *memcg_create_kmem_cache(struct mem_cgroup *memcg,
* This static temporary buffer is used to prevent from
* pointless shortliving allocation.
*/
- if (!tmp_name) {
- tmp_name = kmalloc(PATH_MAX, GFP_KERNEL);
+ if (!tmp_path || !tmp_name) {
+ if (!tmp_path)
+ tmp_path = kmalloc(PATH_MAX, GFP_KERNEL);
if (!tmp_name)
+ tmp_name = kmalloc(NAME_MAX + 1, GFP_KERNEL);
+ if (!tmp_path || !tmp_name)
goto out;
}
- rcu_read_lock();
- snprintf(tmp_name, PATH_MAX, "%s(%d:%s)", s->name,
- memcg_cache_id(memcg), cgroup_name(memcg->css.cgroup));
- rcu_read_unlock();
+ cgroup_name(memcg->css.cgroup, tmp_name, NAME_MAX + 1);
+ snprintf(tmp_path, PATH_MAX, "%s(%d:%s)", s->name,
+ memcg_cache_id(memcg), tmp_name);
- new = kmem_cache_create_memcg(memcg, tmp_name, s->object_size, s->align,
+ new = kmem_cache_create_memcg(memcg, tmp_path, s->object_size, s->align,
(s->flags & ~SLAB_PANIC), s->ctor, s);
if (new)
new->allocflags |= __GFP_KMEMCG;
@@ -4990,7 +4958,7 @@ static int mem_cgroup_force_empty(struct mem_cgroup *memcg)
struct cgroup *cgrp = memcg->css.cgroup;
/* returns EBUSY if there is a task or if we come here twice. */
- if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children))
+ if (cgroup_has_tasks(cgrp) || !list_empty(&cgrp->children))
return -EBUSY;
/* we call try-to-free pages for make this cgroup empty */
@@ -5172,7 +5140,7 @@ static int __memcg_activate_kmem(struct mem_cgroup *memcg,
* of course permitted.
*/
mutex_lock(&memcg_create_mutex);
- if (cgroup_task_count(memcg->css.cgroup) || memcg_has_children(memcg))
+ if (cgroup_has_tasks(memcg->css.cgroup) || memcg_has_children(memcg))
err = -EBUSY;
mutex_unlock(&memcg_create_mutex);
if (err)
@@ -5274,7 +5242,7 @@ static int memcg_update_kmem_limit(struct mem_cgroup *memcg,
* RES_LIMIT.
*/
static int mem_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft,
- const char *buffer)
+ char *buffer)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
enum res_type type;
@@ -6095,7 +6063,7 @@ static void memcg_event_ptable_queue_proc(struct file *file,
* Interpretation of args is defined by control file implementation.
*/
static int memcg_write_event_control(struct cgroup_subsys_state *css,
- struct cftype *cft, const char *buffer)
+ struct cftype *cft, char *buffer)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
struct mem_cgroup_event *event;
@@ -6183,17 +6151,15 @@ static int memcg_write_event_control(struct cgroup_subsys_state *css,
* automatically removed on cgroup destruction but the removal is
* asynchronous, so take an extra ref on @css.
*/
- rcu_read_lock();
-
+ cfile_css = css_tryget_from_dir(cfile.file->f_dentry->d_parent,
+ &memory_cgrp_subsys);
ret = -EINVAL;
- cfile_css = css_from_dir(cfile.file->f_dentry->d_parent,
- &mem_cgroup_subsys);
- if (cfile_css == css && css_tryget(css))
- ret = 0;
-
- rcu_read_unlock();
- if (ret)
+ if (IS_ERR(cfile_css))
+ goto out_put_cfile;
+ if (cfile_css != css) {
+ css_put(cfile_css);
goto out_put_cfile;
+ }
ret = event->register_event(memcg, event->eventfd, buffer);
if (ret)
@@ -6566,11 +6532,11 @@ mem_cgroup_css_online(struct cgroup_subsys_state *css)
* unfortunate state in our controller.
*/
if (parent != root_mem_cgroup)
- mem_cgroup_subsys.broken_hierarchy = true;
+ memory_cgrp_subsys.broken_hierarchy = true;
}
mutex_unlock(&memcg_create_mutex);
- return memcg_init_kmem(memcg, &mem_cgroup_subsys);
+ return memcg_init_kmem(memcg, &memory_cgrp_subsys);
}
/*
@@ -7272,9 +7238,7 @@ static void mem_cgroup_bind(struct cgroup_subsys_state *root_css)
mem_cgroup_from_css(root_css)->use_hierarchy = true;
}
-struct cgroup_subsys mem_cgroup_subsys = {
- .name = "memory",
- .subsys_id = mem_cgroup_subsys_id,
+struct cgroup_subsys memory_cgrp_subsys = {
.css_alloc = mem_cgroup_css_alloc,
.css_online = mem_cgroup_css_online,
.css_offline = mem_cgroup_css_offline,
@@ -7300,7 +7264,7 @@ __setup("swapaccount=", enable_swap_account);
static void __init memsw_file_init(void)
{
- WARN_ON(cgroup_add_cftypes(&mem_cgroup_subsys, memsw_cgroup_files));
+ WARN_ON(cgroup_add_cftypes(&memory_cgrp_subsys, memsw_cgroup_files));
}
static void __init enable_swap_cgroup(void)
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 90002ea43638..35ef28acf137 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -145,14 +145,10 @@ static int hwpoison_filter_task(struct page *p)
return -EINVAL;
css = mem_cgroup_css(mem);
- /* root_mem_cgroup has NULL dentries */
- if (!css->cgroup->dentry)
- return -EINVAL;
-
- ino = css->cgroup->dentry->d_inode->i_ino;
+ ino = cgroup_ino(css->cgroup);
css_put(css);
- if (ino != hwpoison_filter_memcg)
+ if (!ino || ino != hwpoison_filter_memcg)
return -EINVAL;
return 0;
diff --git a/mm/memory.c b/mm/memory.c
index 22dfa617bddb..82c1e4cf00d1 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1705,15 +1705,6 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
VM_BUG_ON(!!pages != !!(gup_flags & FOLL_GET));
- /*
- * Require read or write permissions.
- * If FOLL_FORCE is set, we only require the "MAY" flags.
- */
- vm_flags = (gup_flags & FOLL_WRITE) ?
- (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD);
- vm_flags &= (gup_flags & FOLL_FORCE) ?
- (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE);
-
/*
* If FOLL_FORCE and FOLL_NUMA are both set, handle_mm_fault
* would be called on PROT_NONE ranges. We must never invoke
@@ -1741,7 +1732,7 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
/* user gate pages are read-only */
if (gup_flags & FOLL_WRITE)
- return i ? : -EFAULT;
+ goto efault;
if (pg > TASK_SIZE)
pgd = pgd_offset_k(pg);
else
@@ -1751,12 +1742,12 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
BUG_ON(pud_none(*pud));
pmd = pmd_offset(pud, pg);
if (pmd_none(*pmd))
- return i ? : -EFAULT;
+ goto efault;
VM_BUG_ON(pmd_trans_huge(*pmd));
pte = pte_offset_map(pmd, pg);
if (pte_none(*pte)) {
pte_unmap(pte);
- return i ? : -EFAULT;
+ goto efault;
}
vma = get_gate_vma(mm);
if (pages) {
@@ -1769,7 +1760,7 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
page = pte_page(*pte);
else {
pte_unmap(pte);
- return i ? : -EFAULT;
+ goto efault;
}
}
pages[i] = page;
@@ -1780,10 +1771,42 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
goto next_page;
}
- if (!vma ||
- (vma->vm_flags & (VM_IO | VM_PFNMAP)) ||
- !(vm_flags & vma->vm_flags))
- return i ? : -EFAULT;
+ if (!vma)
+ goto efault;
+ vm_flags = vma->vm_flags;
+ if (vm_flags & (VM_IO | VM_PFNMAP))
+ goto efault;
+
+ if (gup_flags & FOLL_WRITE) {
+ if (!(vm_flags & VM_WRITE)) {
+ if (!(gup_flags & FOLL_FORCE))
+ goto efault;
+ /*
+ * We used to let the write,force case do COW
+ * in a VM_MAYWRITE VM_SHARED !VM_WRITE vma, so
+ * ptrace could set a breakpoint in a read-only
+ * mapping of an executable, without corrupting
+ * the file (yet only when that file had been
+ * opened for writing!). Anon pages in shared
+ * mappings are surprising: now just reject it.
+ */
+ if (!is_cow_mapping(vm_flags)) {
+ WARN_ON_ONCE(vm_flags & VM_MAYWRITE);
+ goto efault;
+ }
+ }
+ } else {
+ if (!(vm_flags & VM_READ)) {
+ if (!(gup_flags & FOLL_FORCE))
+ goto efault;
+ /*
+ * Is there actually any vma we can reach here
+ * which does not have VM_MAYREAD set?
+ */
+ if (!(vm_flags & VM_MAYREAD))
+ goto efault;
+ }
+ }
if (is_vm_hugetlb_page(vma)) {
i = follow_hugetlb_page(mm, vma, pages, vmas,
@@ -1837,7 +1860,7 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
return -EFAULT;
}
if (ret & VM_FAULT_SIGBUS)
- return i ? i : -EFAULT;
+ goto efault;
BUG();
}
@@ -1895,6 +1918,8 @@ next_page:
} while (nr_pages && start < vma->vm_end);
} while (nr_pages);
return i;
+efault:
+ return i ? : -EFAULT;
}
EXPORT_SYMBOL(__get_user_pages);
@@ -1962,9 +1987,8 @@ int fixup_user_fault(struct task_struct *tsk, struct mm_struct *mm,
* @start: starting user address
* @nr_pages: number of pages from start to pin
* @write: whether pages will be written to by the caller
- * @force: whether to force write access even if user mapping is
- * readonly. This will result in the page being COWed even
- * in MAP_SHARED mappings. You do not want this.
+ * @force: whether to force access even when user mapping is currently
+ * protected (but never forces write access to shared mapping).
* @pages: array that receives pointers to the pages pinned.
* Should be at least nr_pages long. Or NULL, if caller
* only intends to ensure the pages are faulted in.
@@ -2587,6 +2611,38 @@ static inline void cow_user_page(struct page *dst, struct page *src, unsigned lo
}
/*
+ * 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.
+ *
+ * We do this without the lock held, so that it can sleep if it needs to.
+ */
+static int do_page_mkwrite(struct vm_area_struct *vma, struct page *page,
+ unsigned long address)
+{
+ struct vm_fault vmf;
+ int ret;
+
+ vmf.virtual_address = (void __user *)(address & PAGE_MASK);
+ vmf.pgoff = page->index;
+ vmf.flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE;
+ vmf.page = page;
+
+ ret = vma->vm_ops->page_mkwrite(vma, &vmf);
+ if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE)))
+ return ret;
+ if (unlikely(!(ret & VM_FAULT_LOCKED))) {
+ lock_page(page);
+ if (!page->mapping) {
+ unlock_page(page);
+ return 0; /* retry */
+ }
+ ret |= VM_FAULT_LOCKED;
+ } else
+ VM_BUG_ON_PAGE(!PageLocked(page), page);
+ return ret;
+}
+
+/*
* This routine handles present pages, when users try to write
* to a shared page. It is done by copying the page to a new address
* and decrementing the shared-page counter for the old page.
@@ -2668,42 +2724,15 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
* get_user_pages(.write=1, .force=1).
*/
if (vma->vm_ops && vma->vm_ops->page_mkwrite) {
- struct vm_fault vmf;
int tmp;
-
- vmf.virtual_address = (void __user *)(address &
- PAGE_MASK);
- vmf.pgoff = old_page->index;
- vmf.flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE;
- vmf.page = old_page;
-
- /*
- * 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.
- *
- * We do this without the lock held, so that it can
- * sleep if it needs to.
- */
page_cache_get(old_page);
pte_unmap_unlock(page_table, ptl);
-
- tmp = vma->vm_ops->page_mkwrite(vma, &vmf);
- if (unlikely(tmp &
- (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) {
- ret = tmp;
- goto unwritable_page;
+ tmp = do_page_mkwrite(vma, old_page, address);
+ if (unlikely(!tmp || (tmp &
+ (VM_FAULT_ERROR | VM_FAULT_NOPAGE)))) {
+ page_cache_release(old_page);
+ return tmp;
}
- if (unlikely(!(tmp & VM_FAULT_LOCKED))) {
- lock_page(old_page);
- if (!old_page->mapping) {
- ret = 0; /* retry the fault */
- unlock_page(old_page);
- goto unwritable_page;
- }
- } else
- VM_BUG_ON_PAGE(!PageLocked(old_page), old_page);
-
/*
* Since we dropped the lock we need to revalidate
* the PTE as someone else may have changed it. If
@@ -2748,7 +2777,7 @@ reuse:
* bit after it clear all dirty ptes, but before a racing
* do_wp_page installs a dirty pte.
*
- * __do_fault is protected similarly.
+ * do_shared_fault is protected similarly.
*/
if (!page_mkwrite) {
wait_on_page_locked(dirty_page);
@@ -2892,10 +2921,6 @@ oom:
if (old_page)
page_cache_release(old_page);
return VM_FAULT_OOM;
-
-unwritable_page:
- page_cache_release(old_page);
- return ret;
}
static void unmap_mapping_range_vma(struct vm_area_struct *vma,
@@ -3286,53 +3311,11 @@ oom:
return VM_FAULT_OOM;
}
-/*
- * __do_fault() tries to create a new page mapping. It aggressively
- * tries to share with existing pages, but makes a separate copy if
- * the FAULT_FLAG_WRITE is set in the flags parameter in order to avoid
- * the next page fault.
- *
- * As this is called only for pages that do not currently exist, we
- * do not need to flush old virtual caches or the TLB.
- *
- * We enter with non-exclusive mmap_sem (to exclude vma changes,
- * but allow concurrent faults), and pte neither mapped nor locked.
- * We return with mmap_sem still held, but pte unmapped and unlocked.
- */
-static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long address, pmd_t *pmd,
- pgoff_t pgoff, unsigned int flags, pte_t orig_pte)
+static int __do_fault(struct vm_area_struct *vma, unsigned long address,
+ pgoff_t pgoff, unsigned int flags, struct page **page)
{
- pte_t *page_table;
- spinlock_t *ptl;
- struct page *page;
- struct page *cow_page;
- pte_t entry;
- int anon = 0;
- struct page *dirty_page = NULL;
struct vm_fault vmf;
int ret;
- int page_mkwrite = 0;
-
- /*
- * If we do COW later, allocate page befor taking lock_page()
- * on the file cache page. This will reduce lock holding time.
- */
- if ((flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED)) {
-
- if (unlikely(anon_vma_prepare(vma)))
- return VM_FAULT_OOM;
-
- cow_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);
- if (!cow_page)
- return VM_FAULT_OOM;
-
- if (mem_cgroup_newpage_charge(cow_page, mm, GFP_KERNEL)) {
- page_cache_release(cow_page);
- return VM_FAULT_OOM;
- }
- } else
- cow_page = NULL;
vmf.virtual_address = (void __user *)(address & PAGE_MASK);
vmf.pgoff = pgoff;
@@ -3340,151 +3323,176 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
vmf.page = NULL;
ret = vma->vm_ops->fault(vma, &vmf);
- if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE |
- VM_FAULT_RETRY)))
- goto uncharge_out;
+ if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY)))
+ return ret;
if (unlikely(PageHWPoison(vmf.page))) {
if (ret & VM_FAULT_LOCKED)
unlock_page(vmf.page);
- ret = VM_FAULT_HWPOISON;
page_cache_release(vmf.page);
- goto uncharge_out;
+ return VM_FAULT_HWPOISON;
}
- /*
- * For consistency in subsequent calls, make the faulted page always
- * locked.
- */
if (unlikely(!(ret & VM_FAULT_LOCKED)))
lock_page(vmf.page);
else
VM_BUG_ON_PAGE(!PageLocked(vmf.page), vmf.page);
- /*
- * Should we do an early C-O-W break?
- */
- page = vmf.page;
- if (flags & FAULT_FLAG_WRITE) {
- if (!(vma->vm_flags & VM_SHARED)) {
- page = cow_page;
- anon = 1;
- copy_user_highpage(page, vmf.page, address, vma);
- __SetPageUptodate(page);
- } else {
- /*
- * If the page will be shareable, see if the backing
- * address space wants to know that the page is about
- * to become writable
- */
- if (vma->vm_ops->page_mkwrite) {
- int tmp;
-
- unlock_page(page);
- vmf.flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE;
- tmp = vma->vm_ops->page_mkwrite(vma, &vmf);
- if (unlikely(tmp &
- (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) {
- ret = tmp;
- goto unwritable_page;
- }
- if (unlikely(!(tmp & VM_FAULT_LOCKED))) {
- lock_page(page);
- if (!page->mapping) {
- ret = 0; /* retry the fault */
- unlock_page(page);
- goto unwritable_page;
- }
- } else
- VM_BUG_ON_PAGE(!PageLocked(page), page);
- page_mkwrite = 1;
- }
- }
+ *page = vmf.page;
+ return ret;
+}
+static void do_set_pte(struct vm_area_struct *vma, unsigned long address,
+ struct page *page, pte_t *pte, bool write, bool anon)
+{
+ pte_t entry;
+
+ flush_icache_page(vma, page);
+ entry = mk_pte(page, vma->vm_page_prot);
+ if (write)
+ entry = maybe_mkwrite(pte_mkdirty(entry), vma);
+ else if (pte_file(*pte) && pte_file_soft_dirty(*pte))
+ pte_mksoft_dirty(entry);
+ if (anon) {
+ 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);
+ page_add_file_rmap(page);
}
+ set_pte_at(vma->vm_mm, address, pte, entry);
- page_table = pte_offset_map_lock(mm, pmd, address, &ptl);
+ /* no need to invalidate: a not-present page won't be cached */
+ update_mmu_cache(vma, address, pte);
+}
- /*
- * This silly early PAGE_DIRTY setting removes a race
- * due to the bad i386 page protection. But it's valid
- * for other architectures too.
- *
- * Note that if FAULT_FLAG_WRITE is set, we either now have
- * an exclusive copy of the page, or this is a shared mapping,
- * so we can make it writable and dirty to avoid having to
- * handle that later.
- */
- /* Only go through if we didn't race with anybody else... */
- if (likely(pte_same(*page_table, orig_pte))) {
- flush_icache_page(vma, page);
- entry = mk_pte(page, vma->vm_page_prot);
- if (flags & FAULT_FLAG_WRITE)
- entry = maybe_mkwrite(pte_mkdirty(entry), vma);
- else if (pte_file(orig_pte) && pte_file_soft_dirty(orig_pte))
- pte_mksoft_dirty(entry);
- if (anon) {
- inc_mm_counter_fast(mm, MM_ANONPAGES);
- page_add_new_anon_rmap(page, vma, address);
- } else {
- inc_mm_counter_fast(mm, MM_FILEPAGES);
- page_add_file_rmap(page);
- if (flags & FAULT_FLAG_WRITE) {
- dirty_page = page;
- get_page(dirty_page);
- }
- }
- set_pte_at(mm, address, page_table, entry);
+static int do_read_fault(struct mm_struct *mm, struct vm_area_struct *vma,
+ unsigned long address, pmd_t *pmd,
+ pgoff_t pgoff, unsigned int flags, pte_t orig_pte)
+{
+ struct page *fault_page;
+ spinlock_t *ptl;
+ pte_t *pte;
+ int ret;
- /* no need to invalidate: a not-present page won't be cached */
- update_mmu_cache(vma, address, page_table);
- } else {
- if (cow_page)
- mem_cgroup_uncharge_page(cow_page);
- if (anon)
- page_cache_release(page);
- else
- anon = 1; /* no anon but release faulted_page */
+ ret = __do_fault(vma, address, pgoff, flags, &fault_page);
+ if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY)))
+ return ret;
+
+ pte = pte_offset_map_lock(mm, pmd, address, &ptl);
+ if (unlikely(!pte_same(*pte, orig_pte))) {
+ pte_unmap_unlock(pte, ptl);
+ unlock_page(fault_page);
+ page_cache_release(fault_page);
+ return ret;
}
+ do_set_pte(vma, address, fault_page, pte, false, false);
+ pte_unmap_unlock(pte, ptl);
+ unlock_page(fault_page);
+ return ret;
+}
- pte_unmap_unlock(page_table, ptl);
+static int do_cow_fault(struct mm_struct *mm, struct vm_area_struct *vma,
+ unsigned long address, pmd_t *pmd,
+ pgoff_t pgoff, unsigned int flags, pte_t orig_pte)
+{
+ struct page *fault_page, *new_page;
+ spinlock_t *ptl;
+ pte_t *pte;
+ int ret;
- if (dirty_page) {
- struct address_space *mapping = page->mapping;
- int dirtied = 0;
+ if (unlikely(anon_vma_prepare(vma)))
+ return VM_FAULT_OOM;
- if (set_page_dirty(dirty_page))
- dirtied = 1;
- unlock_page(dirty_page);
- put_page(dirty_page);
- if ((dirtied || page_mkwrite) && mapping) {
- /*
- * Some device drivers do not set page.mapping but still
- * dirty their pages
- */
- balance_dirty_pages_ratelimited(mapping);
- }
+ new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);
+ if (!new_page)
+ return VM_FAULT_OOM;
- /* file_update_time outside page_lock */
- if (vma->vm_file && !page_mkwrite)
- file_update_time(vma->vm_file);
- } else {
- unlock_page(vmf.page);
- if (anon)
- page_cache_release(vmf.page);
+ if (mem_cgroup_newpage_charge(new_page, mm, GFP_KERNEL)) {
+ page_cache_release(new_page);
+ return VM_FAULT_OOM;
}
- return ret;
+ ret = __do_fault(vma, address, pgoff, flags, &fault_page);
+ if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY)))
+ goto uncharge_out;
-unwritable_page:
- page_cache_release(page);
+ copy_user_highpage(new_page, fault_page, address, vma);
+ __SetPageUptodate(new_page);
+
+ pte = pte_offset_map_lock(mm, pmd, address, &ptl);
+ if (unlikely(!pte_same(*pte, orig_pte))) {
+ pte_unmap_unlock(pte, ptl);
+ unlock_page(fault_page);
+ page_cache_release(fault_page);
+ goto uncharge_out;
+ }
+ do_set_pte(vma, address, new_page, pte, true, true);
+ pte_unmap_unlock(pte, ptl);
+ unlock_page(fault_page);
+ page_cache_release(fault_page);
return ret;
uncharge_out:
- /* fs's fault handler get error */
- if (cow_page) {
- mem_cgroup_uncharge_page(cow_page);
- page_cache_release(cow_page);
+ mem_cgroup_uncharge_page(new_page);
+ page_cache_release(new_page);
+ return ret;
+}
+
+static int do_shared_fault(struct mm_struct *mm, struct vm_area_struct *vma,
+ unsigned long address, pmd_t *pmd,
+ pgoff_t pgoff, unsigned int flags, pte_t orig_pte)
+{
+ struct page *fault_page;
+ struct address_space *mapping;
+ spinlock_t *ptl;
+ pte_t *pte;
+ int dirtied = 0;
+ int ret, tmp;
+
+ ret = __do_fault(vma, address, pgoff, flags, &fault_page);
+ if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY)))
+ return ret;
+
+ /*
+ * Check if the backing address space wants to know that the page is
+ * about to become writable
+ */
+ if (vma->vm_ops->page_mkwrite) {
+ unlock_page(fault_page);
+ tmp = do_page_mkwrite(vma, fault_page, address);
+ if (unlikely(!tmp ||
+ (tmp & (VM_FAULT_ERROR | VM_FAULT_NOPAGE)))) {
+ page_cache_release(fault_page);
+ return tmp;
+ }
}
+
+ pte = pte_offset_map_lock(mm, pmd, address, &ptl);
+ if (unlikely(!pte_same(*pte, orig_pte))) {
+ pte_unmap_unlock(pte, ptl);
+ unlock_page(fault_page);
+ page_cache_release(fault_page);
+ return ret;
+ }
+ do_set_pte(vma, address, fault_page, pte, true, false);
+ pte_unmap_unlock(pte, ptl);
+
+ if (set_page_dirty(fault_page))
+ dirtied = 1;
+ mapping = fault_page->mapping;
+ unlock_page(fault_page);
+ if ((dirtied || vma->vm_ops->page_mkwrite) && mapping) {
+ /*
+ * Some device drivers do not set page.mapping but still
+ * dirty their pages
+ */
+ balance_dirty_pages_ratelimited(mapping);
+ }
+
+ /* file_update_time outside page_lock */
+ if (vma->vm_file && !vma->vm_ops->page_mkwrite)
+ file_update_time(vma->vm_file);
+
return ret;
}
@@ -3496,7 +3504,13 @@ static int do_linear_fault(struct mm_struct *mm, struct vm_area_struct *vma,
- vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
pte_unmap(page_table);
- return __do_fault(mm, vma, address, pmd, pgoff, flags, orig_pte);
+ if (!(flags & FAULT_FLAG_WRITE))
+ return do_read_fault(mm, vma, address, pmd, pgoff, flags,
+ orig_pte);
+ if (!(vma->vm_flags & VM_SHARED))
+ return do_cow_fault(mm, vma, address, pmd, pgoff, flags,
+ orig_pte);
+ return do_shared_fault(mm, vma, address, pmd, pgoff, flags, orig_pte);
}
/*
@@ -3528,10 +3542,16 @@ static int do_nonlinear_fault(struct mm_struct *mm, struct vm_area_struct *vma,
}
pgoff = pte_to_pgoff(orig_pte);
- return __do_fault(mm, vma, address, pmd, pgoff, flags, orig_pte);
+ if (!(flags & FAULT_FLAG_WRITE))
+ return do_read_fault(mm, vma, address, pmd, pgoff, flags,
+ orig_pte);
+ if (!(vma->vm_flags & VM_SHARED))
+ return do_cow_fault(mm, vma, address, pmd, pgoff, flags,
+ orig_pte);
+ return do_shared_fault(mm, vma, address, pmd, pgoff, flags, orig_pte);
}
-int numa_migrate_prep(struct page *page, struct vm_area_struct *vma,
+static int numa_migrate_prep(struct page *page, struct vm_area_struct *vma,
unsigned long addr, int page_nid,
int *flags)
{
@@ -3546,7 +3566,7 @@ int numa_migrate_prep(struct page *page, struct vm_area_struct *vma,
return mpol_misplaced(page, vma, addr);
}
-int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
+static int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long addr, pte_t pte, pte_t *ptep, pmd_t *pmd)
{
struct page *page = NULL;
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 4755c8576942..e3ab02822799 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1899,7 +1899,7 @@ int node_random(const nodemask_t *maskp)
* If the effective policy is 'BIND, returns a pointer to the mempolicy's
* @nodemask for filtering the zonelist.
*
- * Must be protected by get_mems_allowed()
+ * Must be protected by read_mems_allowed_begin()
*/
struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
gfp_t gfp_flags, struct mempolicy **mpol,
@@ -2063,7 +2063,7 @@ alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma,
retry_cpuset:
pol = get_vma_policy(current, vma, addr);
- cpuset_mems_cookie = get_mems_allowed();
+ cpuset_mems_cookie = read_mems_allowed_begin();
if (unlikely(pol->mode == MPOL_INTERLEAVE)) {
unsigned nid;
@@ -2071,7 +2071,7 @@ retry_cpuset:
nid = interleave_nid(pol, vma, addr, PAGE_SHIFT + order);
mpol_cond_put(pol);
page = alloc_page_interleave(gfp, order, nid);
- if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
+ if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie)))
goto retry_cpuset;
return page;
@@ -2081,7 +2081,7 @@ retry_cpuset:
policy_nodemask(gfp, pol));
if (unlikely(mpol_needs_cond_ref(pol)))
__mpol_put(pol);
- if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
+ if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie)))
goto retry_cpuset;
return page;
}
@@ -2115,7 +2115,7 @@ struct page *alloc_pages_current(gfp_t gfp, unsigned order)
pol = &default_policy;
retry_cpuset:
- cpuset_mems_cookie = get_mems_allowed();
+ cpuset_mems_cookie = read_mems_allowed_begin();
/*
* No reference counting needed for current->mempolicy
@@ -2128,7 +2128,7 @@ retry_cpuset:
policy_zonelist(gfp, pol, numa_node_id()),
policy_nodemask(gfp, pol));
- if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
+ if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie)))
goto retry_cpuset;
return page;
diff --git a/mm/mincore.c b/mm/mincore.c
index 101623378fbf..725c80961048 100644
--- a/mm/mincore.c
+++ b/mm/mincore.c
@@ -70,13 +70,21 @@ static unsigned char mincore_page(struct address_space *mapping, pgoff_t pgoff)
* any other file mapping (ie. marked !present and faulted in with
* tmpfs's .fault). So swapped out tmpfs mappings are tested here.
*/
- page = find_get_page(mapping, pgoff);
#ifdef CONFIG_SWAP
- /* shmem/tmpfs may return swap: account for swapcache page too. */
- if (radix_tree_exceptional_entry(page)) {
- swp_entry_t swap = radix_to_swp_entry(page);
- page = find_get_page(swap_address_space(swap), swap.val);
- }
+ if (shmem_mapping(mapping)) {
+ page = find_get_entry(mapping, pgoff);
+ /*
+ * shmem/tmpfs may return swap: account for swapcache
+ * page too.
+ */
+ if (radix_tree_exceptional_entry(page)) {
+ swp_entry_t swp = radix_to_swp_entry(page);
+ page = find_get_page(swap_address_space(swp), swp.val);
+ }
+ } else
+ page = find_get_page(mapping, pgoff);
+#else
+ page = find_get_page(mapping, pgoff);
#endif
if (page) {
present = PageUptodate(page);
diff --git a/mm/mmap.c b/mm/mmap.c
index 81ba54ff96c7..46433e137abc 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -405,7 +405,7 @@ static void validate_mm_rb(struct rb_root *root, struct vm_area_struct *ignore)
}
}
-void validate_mm(struct mm_struct *mm)
+static void validate_mm(struct mm_struct *mm)
{
int bug = 0;
int i = 0;
@@ -1299,7 +1299,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
/*
* Make sure there are no mandatory locks on the file.
*/
- if (locks_verify_locked(inode))
+ if (locks_verify_locked(file))
return -EAGAIN;
vm_flags |= VM_SHARED | VM_MAYSHARE;
diff --git a/mm/nobootmem.c b/mm/nobootmem.c
index f73f2987a852..04a9d94333a5 100644
--- a/mm/nobootmem.c
+++ b/mm/nobootmem.c
@@ -334,7 +334,7 @@ void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
return ___alloc_bootmem_node_nopanic(pgdat, size, align, goal, 0);
}
-void * __init ___alloc_bootmem_node(pg_data_t *pgdat, unsigned long size,
+static void * __init ___alloc_bootmem_node(pg_data_t *pgdat, unsigned long size,
unsigned long align, unsigned long goal,
unsigned long limit)
{
diff --git a/mm/nommu.c b/mm/nommu.c
index 8740213b1647..a554e5a451cd 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -995,7 +995,7 @@ static int validate_mmap_request(struct file *file,
(file->f_mode & FMODE_WRITE))
return -EACCES;
- if (locks_verify_locked(file_inode(file)))
+ if (locks_verify_locked(file))
return -EAGAIN;
if (!(capabilities & BDI_CAP_MAP_DIRECT))
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 3bac76ae4b30..979378deccbf 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -2739,7 +2739,7 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
return NULL;
retry_cpuset:
- cpuset_mems_cookie = get_mems_allowed();
+ cpuset_mems_cookie = read_mems_allowed_begin();
/* The preferred zone is used for statistics later */
first_zones_zonelist(zonelist, high_zoneidx,
@@ -2777,7 +2777,7 @@ out:
* the mask is being updated. If a page allocation is about to fail,
* check if the cpuset changed during allocation and if so, retry.
*/
- if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
+ if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie)))
goto retry_cpuset;
memcg_kmem_commit_charge(page, memcg, order);
@@ -3045,9 +3045,9 @@ bool skip_free_areas_node(unsigned int flags, int nid)
goto out;
do {
- cpuset_mems_cookie = get_mems_allowed();
+ cpuset_mems_cookie = read_mems_allowed_begin();
ret = !node_isset(nid, cpuset_current_mems_allowed);
- } while (!put_mems_allowed(cpuset_mems_cookie));
+ } while (read_mems_allowed_retry(cpuset_mems_cookie));
out:
return ret;
}
diff --git a/mm/page_cgroup.c b/mm/page_cgroup.c
index cfd162882c00..3708264d2833 100644
--- a/mm/page_cgroup.c
+++ b/mm/page_cgroup.c
@@ -175,7 +175,7 @@ static void free_page_cgroup(void *addr)
}
}
-void __free_page_cgroup(unsigned long pfn)
+static void __free_page_cgroup(unsigned long pfn)
{
struct mem_section *ms;
struct page_cgroup *base;
@@ -188,9 +188,9 @@ void __free_page_cgroup(unsigned long pfn)
ms->page_cgroup = NULL;
}
-int __meminit online_page_cgroup(unsigned long start_pfn,
- unsigned long nr_pages,
- int nid)
+static int __meminit online_page_cgroup(unsigned long start_pfn,
+ unsigned long nr_pages,
+ int nid)
{
unsigned long start, end, pfn;
int fail = 0;
@@ -223,8 +223,8 @@ int __meminit online_page_cgroup(unsigned long start_pfn,
return -ENOMEM;
}
-int __meminit offline_page_cgroup(unsigned long start_pfn,
- unsigned long nr_pages, int nid)
+static int __meminit offline_page_cgroup(unsigned long start_pfn,
+ unsigned long nr_pages, int nid)
{
unsigned long start, end, pfn;
diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c
index 3c5cf68566ec..cb79065c19e5 100644
--- a/mm/process_vm_access.c
+++ b/mm/process_vm_access.c
@@ -412,7 +412,7 @@ SYSCALL_DEFINE6(process_vm_writev, pid_t, pid,
#ifdef CONFIG_COMPAT
-asmlinkage ssize_t
+static ssize_t
compat_process_vm_rw(compat_pid_t pid,
const struct compat_iovec __user *lvec,
unsigned long liovcnt,
diff --git a/mm/readahead.c b/mm/readahead.c
index 0de2360d65f3..29c5e1af5a0c 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -179,7 +179,7 @@ __do_page_cache_readahead(struct address_space *mapping, struct file *filp,
rcu_read_lock();
page = radix_tree_lookup(&mapping->page_tree, page_offset);
rcu_read_unlock();
- if (page)
+ if (page && !radix_tree_exceptional_entry(page))
continue;
page = page_cache_alloc_readahead(mapping);
@@ -233,14 +233,14 @@ int force_page_cache_readahead(struct address_space *mapping, struct file *filp,
return 0;
}
+#define MAX_READAHEAD ((512*4096)/PAGE_CACHE_SIZE)
/*
* Given a desired number of PAGE_CACHE_SIZE readahead pages, return a
* sensible upper limit.
*/
unsigned long max_sane_readahead(unsigned long nr)
{
- return min(nr, (node_page_state(numa_node_id(), NR_INACTIVE_FILE)
- + node_page_state(numa_node_id(), NR_FREE_PAGES)) / 2);
+ return min(nr, MAX_READAHEAD);
}
/*
@@ -347,7 +347,7 @@ static pgoff_t count_history_pages(struct address_space *mapping,
pgoff_t head;
rcu_read_lock();
- head = radix_tree_prev_hole(&mapping->page_tree, offset - 1, max);
+ head = page_cache_prev_hole(mapping, offset - 1, max);
rcu_read_unlock();
return offset - 1 - head;
@@ -427,7 +427,7 @@ ondemand_readahead(struct address_space *mapping,
pgoff_t start;
rcu_read_lock();
- start = radix_tree_next_hole(&mapping->page_tree, offset+1,max);
+ start = page_cache_next_hole(mapping, offset + 1, max);
rcu_read_unlock();
if (!start || start - offset > max)
diff --git a/mm/shmem.c b/mm/shmem.c
index 1f18c9d0d93e..a3ba988ec946 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -242,19 +242,17 @@ static int shmem_radix_tree_replace(struct address_space *mapping,
pgoff_t index, void *expected, void *replacement)
{
void **pslot;
- void *item = NULL;
+ void *item;
VM_BUG_ON(!expected);
+ VM_BUG_ON(!replacement);
pslot = radix_tree_lookup_slot(&mapping->page_tree, index);
- if (pslot)
- item = radix_tree_deref_slot_protected(pslot,
- &mapping->tree_lock);
+ if (!pslot)
+ return -ENOENT;
+ item = radix_tree_deref_slot_protected(pslot, &mapping->tree_lock);
if (item != expected)
return -ENOENT;
- if (replacement)
- radix_tree_replace_slot(pslot, replacement);
- else
- radix_tree_delete(&mapping->page_tree, index);
+ radix_tree_replace_slot(pslot, replacement);
return 0;
}
@@ -331,84 +329,20 @@ static void shmem_delete_from_page_cache(struct page *page, void *radswap)
}
/*
- * Like find_get_pages, but collecting swap entries as well as pages.
- */
-static unsigned shmem_find_get_pages_and_swap(struct address_space *mapping,
- pgoff_t start, unsigned int nr_pages,
- struct page **pages, pgoff_t *indices)
-{
- void **slot;
- unsigned int ret = 0;
- struct radix_tree_iter iter;
-
- if (!nr_pages)
- return 0;
-
- rcu_read_lock();
-restart:
- radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) {
- struct page *page;
-repeat:
- page = radix_tree_deref_slot(slot);
- if (unlikely(!page))
- continue;
- if (radix_tree_exception(page)) {
- if (radix_tree_deref_retry(page))
- goto restart;
- /*
- * Otherwise, we must be storing a swap entry
- * here as an exceptional entry: so return it
- * without attempting to raise page count.
- */
- goto export;
- }
- if (!page_cache_get_speculative(page))
- goto repeat;
-
- /* Has the page moved? */
- if (unlikely(page != *slot)) {
- page_cache_release(page);
- goto repeat;
- }
-export:
- indices[ret] = iter.index;
- pages[ret] = page;
- if (++ret == nr_pages)
- break;
- }
- rcu_read_unlock();
- return ret;
-}
-
-/*
* Remove swap entry from radix tree, free the swap and its page cache.
*/
static int shmem_free_swap(struct address_space *mapping,
pgoff_t index, void *radswap)
{
- int error;
+ void *old;
spin_lock_irq(&mapping->tree_lock);
- error = shmem_radix_tree_replace(mapping, index, radswap, NULL);
+ old = radix_tree_delete_item(&mapping->page_tree, index, radswap);
spin_unlock_irq(&mapping->tree_lock);
- if (!error)
- free_swap_and_cache(radix_to_swp_entry(radswap));
- return error;
-}
-
-/*
- * Pagevec may contain swap entries, so shuffle up pages before releasing.
- */
-static void shmem_deswap_pagevec(struct pagevec *pvec)
-{
- int i, j;
-
- for (i = 0, j = 0; i < pagevec_count(pvec); i++) {
- struct page *page = pvec->pages[i];
- if (!radix_tree_exceptional_entry(page))
- pvec->pages[j++] = page;
- }
- pvec->nr = j;
+ if (old != radswap)
+ return -ENOENT;
+ free_swap_and_cache(radix_to_swp_entry(radswap));
+ return 0;
}
/*
@@ -429,12 +363,12 @@ void shmem_unlock_mapping(struct address_space *mapping)
* Avoid pagevec_lookup(): find_get_pages() returns 0 as if it
* has finished, if it hits a row of PAGEVEC_SIZE swap entries.
*/
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
- PAGEVEC_SIZE, pvec.pages, indices);
+ pvec.nr = find_get_entries(mapping, index,
+ PAGEVEC_SIZE, pvec.pages, indices);
if (!pvec.nr)
break;
index = indices[pvec.nr - 1] + 1;
- shmem_deswap_pagevec(&pvec);
+ pagevec_remove_exceptionals(&pvec);
check_move_unevictable_pages(pvec.pages, pvec.nr);
pagevec_release(&pvec);
cond_resched();
@@ -466,9 +400,9 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
pagevec_init(&pvec, 0);
index = start;
while (index < end) {
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
- min(end - index, (pgoff_t)PAGEVEC_SIZE),
- pvec.pages, indices);
+ pvec.nr = find_get_entries(mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE),
+ pvec.pages, indices);
if (!pvec.nr)
break;
mem_cgroup_uncharge_start();
@@ -497,7 +431,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
}
unlock_page(page);
}
- shmem_deswap_pagevec(&pvec);
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
cond_resched();
@@ -535,9 +469,10 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
index = start;
for ( ; ; ) {
cond_resched();
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
+
+ pvec.nr = find_get_entries(mapping, index,
min(end - index, (pgoff_t)PAGEVEC_SIZE),
- pvec.pages, indices);
+ pvec.pages, indices);
if (!pvec.nr) {
if (index == start || unfalloc)
break;
@@ -545,7 +480,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
continue;
}
if ((index == start || unfalloc) && indices[0] >= end) {
- shmem_deswap_pagevec(&pvec);
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
break;
}
@@ -574,7 +509,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
}
unlock_page(page);
}
- shmem_deswap_pagevec(&pvec);
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
index++;
@@ -1080,7 +1015,7 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
return -EFBIG;
repeat:
swap.val = 0;
- page = find_lock_page(mapping, index);
+ page = find_lock_entry(mapping, index);
if (radix_tree_exceptional_entry(page)) {
swap = radix_to_swp_entry(page);
page = NULL;
@@ -1417,6 +1352,11 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode
return inode;
}
+bool shmem_mapping(struct address_space *mapping)
+{
+ return mapping->backing_dev_info == &shmem_backing_dev_info;
+}
+
#ifdef CONFIG_TMPFS
static const struct inode_operations shmem_symlink_inode_operations;
static const struct inode_operations shmem_short_symlink_operations;
@@ -1729,7 +1669,7 @@ static pgoff_t shmem_seek_hole_data(struct address_space *mapping,
pagevec_init(&pvec, 0);
pvec.nr = 1; /* start small: we may be there already */
while (!done) {
- pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
+ pvec.nr = find_get_entries(mapping, index,
pvec.nr, pvec.pages, indices);
if (!pvec.nr) {
if (whence == SEEK_DATA)
@@ -1756,7 +1696,7 @@ static pgoff_t shmem_seek_hole_data(struct address_space *mapping,
break;
}
}
- shmem_deswap_pagevec(&pvec);
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
pvec.nr = PAGEVEC_SIZE;
cond_resched();
diff --git a/mm/slab.c b/mm/slab.c
index b264214c77ea..9153c802e2fe 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -3073,7 +3073,7 @@ static void *fallback_alloc(struct kmem_cache *cache, gfp_t flags)
local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);
retry_cpuset:
- cpuset_mems_cookie = get_mems_allowed();
+ cpuset_mems_cookie = read_mems_allowed_begin();
zonelist = node_zonelist(slab_node(), flags);
retry:
@@ -3131,7 +3131,7 @@ retry:
}
}
- if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !obj))
+ if (unlikely(!obj && read_mems_allowed_retry(cpuset_mems_cookie)))
goto retry_cpuset;
return obj;
}
diff --git a/mm/slub.c b/mm/slub.c
index 25f14ad8f817..fe6d7be22ef0 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -1684,7 +1684,7 @@ static void *get_any_partial(struct kmem_cache *s, gfp_t flags,
return NULL;
do {
- cpuset_mems_cookie = get_mems_allowed();
+ cpuset_mems_cookie = read_mems_allowed_begin();
zonelist = node_zonelist(slab_node(), flags);
for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) {
struct kmem_cache_node *n;
@@ -1696,19 +1696,17 @@ static void *get_any_partial(struct kmem_cache *s, gfp_t flags,
object = get_partial_node(s, n, c, flags);
if (object) {
/*
- * Return the object even if
- * put_mems_allowed indicated that
- * the cpuset mems_allowed was
- * updated in parallel. It's a
- * harmless race between the alloc
- * and the cpuset update.
+ * Don't check read_mems_allowed_retry()
+ * here - if mems_allowed was updated in
+ * parallel, that was a harmless race
+ * between allocation and the cpuset
+ * update
*/
- put_mems_allowed(cpuset_mems_cookie);
return object;
}
}
}
- } while (!put_mems_allowed(cpuset_mems_cookie));
+ } while (read_mems_allowed_retry(cpuset_mems_cookie));
#endif
return NULL;
}
@@ -3239,8 +3237,9 @@ int __kmem_cache_shutdown(struct kmem_cache *s)
if (!rc) {
/*
- * We do the same lock strategy around sysfs_slab_add, see
- * __kmem_cache_create. Because this is pretty much the last
+ * Since slab_attr_store may take the slab_mutex, we should
+ * release the lock while removing the sysfs entry in order to
+ * avoid a deadlock. Because this is pretty much the last
* operation we do and the lock will be released shortly after
* that in slab_common.c, we could just move sysfs_slab_remove
* to a later point in common code. We should do that when we
@@ -3780,10 +3779,7 @@ int __kmem_cache_create(struct kmem_cache *s, unsigned long flags)
return 0;
memcg_propagate_slab_attrs(s);
- mutex_unlock(&slab_mutex);
err = sysfs_slab_add(s);
- mutex_lock(&slab_mutex);
-
if (err)
kmem_cache_close(s);
diff --git a/mm/sparse.c b/mm/sparse.c
index 63c3ea5c119c..38cad8fd7397 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -268,7 +268,7 @@ sparse_early_usemaps_alloc_pgdat_section(struct pglist_data *pgdat,
/*
* A page may contain usemaps for other sections preventing the
* page being freed and making a section unremovable while
- * other sections referencing the usemap retmain active. Similarly,
+ * other sections referencing the usemap remain active. Similarly,
* a pgdat can prevent a section being removed. If section A
* contains a pgdat and section B contains the usemap, both
* sections become inter-dependent. This allocates usemaps
diff --git a/mm/swap.c b/mm/swap.c
index 0092097b3f4c..9ce43ba4498b 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -574,6 +574,8 @@ void mark_page_accessed(struct page *page)
else
__lru_cache_activate_page(page);
ClearPageReferenced(page);
+ if (page_is_file_cache(page))
+ workingset_activation(page);
} else if (!PageReferenced(page)) {
SetPageReferenced(page);
}
@@ -948,6 +950,57 @@ void __pagevec_lru_add(struct pagevec *pvec)
EXPORT_SYMBOL(__pagevec_lru_add);
/**
+ * pagevec_lookup_entries - gang pagecache lookup
+ * @pvec: Where the resulting entries are placed
+ * @mapping: The address_space to search
+ * @start: The starting entry index
+ * @nr_entries: The maximum number of entries
+ * @indices: The cache indices corresponding to the entries in @pvec
+ *
+ * pagevec_lookup_entries() will search for and return a group of up
+ * to @nr_entries pages and shadow entries in the mapping. All
+ * entries are placed in @pvec. pagevec_lookup_entries() takes a
+ * reference against actual pages in @pvec.
+ *
+ * The search returns a group of mapping-contiguous entries with
+ * ascending indexes. There may be holes in the indices due to
+ * not-present entries.
+ *
+ * pagevec_lookup_entries() returns the number of entries which were
+ * found.
+ */
+unsigned pagevec_lookup_entries(struct pagevec *pvec,
+ struct address_space *mapping,
+ pgoff_t start, unsigned nr_pages,
+ pgoff_t *indices)
+{
+ pvec->nr = find_get_entries(mapping, start, nr_pages,
+ pvec->pages, indices);
+ return pagevec_count(pvec);
+}
+
+/**
+ * pagevec_remove_exceptionals - pagevec exceptionals pruning
+ * @pvec: The pagevec to prune
+ *
+ * pagevec_lookup_entries() fills both pages and exceptional radix
+ * tree entries into the pagevec. This function prunes all
+ * exceptionals from @pvec without leaving holes, so that it can be
+ * passed on to page-only pagevec operations.
+ */
+void pagevec_remove_exceptionals(struct pagevec *pvec)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < pagevec_count(pvec); i++) {
+ struct page *page = pvec->pages[i];
+ if (!radix_tree_exceptional_entry(page))
+ pvec->pages[j++] = page;
+ }
+ pvec->nr = j;
+}
+
+/**
* pagevec_lookup - gang pagecache lookup
* @pvec: Where the resulting pages are placed
* @mapping: The address_space to search
diff --git a/mm/truncate.c b/mm/truncate.c
index 353b683afd6e..e5cc39ab0751 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -22,6 +22,45 @@
#include <linux/cleancache.h>
#include "internal.h"
+static void clear_exceptional_entry(struct address_space *mapping,
+ pgoff_t index, void *entry)
+{
+ struct radix_tree_node *node;
+ void **slot;
+
+ /* Handled by shmem itself */
+ if (shmem_mapping(mapping))
+ return;
+
+ spin_lock_irq(&mapping->tree_lock);
+ /*
+ * Regular page slots are stabilized by the page lock even
+ * without the tree itself locked. These unlocked entries
+ * need verification under the tree lock.
+ */
+ if (!__radix_tree_lookup(&mapping->page_tree, index, &node, &slot))
+ goto unlock;
+ if (*slot != entry)
+ goto unlock;
+ radix_tree_replace_slot(slot, NULL);
+ mapping->nrshadows--;
+ if (!node)
+ goto unlock;
+ workingset_node_shadows_dec(node);
+ /*
+ * Don't track node without shadow entries.
+ *
+ * Avoid acquiring the list_lru lock if already untracked.
+ * The list_empty() test is safe as node->private_list is
+ * protected by mapping->tree_lock.
+ */
+ if (!workingset_node_shadows(node) &&
+ !list_empty(&node->private_list))
+ list_lru_del(&workingset_shadow_nodes, &node->private_list);
+ __radix_tree_delete_node(&mapping->page_tree, node);
+unlock:
+ spin_unlock_irq(&mapping->tree_lock);
+}
/**
* do_invalidatepage - invalidate part or all of a page
@@ -208,11 +247,12 @@ void truncate_inode_pages_range(struct address_space *mapping,
unsigned int partial_start; /* inclusive */
unsigned int partial_end; /* exclusive */
struct pagevec pvec;
+ pgoff_t indices[PAGEVEC_SIZE];
pgoff_t index;
int i;
cleancache_invalidate_inode(mapping);
- if (mapping->nrpages == 0)
+ if (mapping->nrpages == 0 && mapping->nrshadows == 0)
return;
/* Offsets within partial pages */
@@ -238,17 +278,23 @@ void truncate_inode_pages_range(struct address_space *mapping,
pagevec_init(&pvec, 0);
index = start;
- while (index < end && pagevec_lookup(&pvec, mapping, index,
- min(end - index, (pgoff_t)PAGEVEC_SIZE))) {
+ while (index < end && pagevec_lookup_entries(&pvec, mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE),
+ indices)) {
mem_cgroup_uncharge_start();
for (i = 0; i < pagevec_count(&pvec); i++) {
struct page *page = pvec.pages[i];
/* We rely upon deletion not changing page->index */
- index = page->index;
+ index = indices[i];
if (index >= end)
break;
+ if (radix_tree_exceptional_entry(page)) {
+ clear_exceptional_entry(mapping, index, page);
+ continue;
+ }
+
if (!trylock_page(page))
continue;
WARN_ON(page->index != index);
@@ -259,6 +305,7 @@ void truncate_inode_pages_range(struct address_space *mapping,
truncate_inode_page(mapping, page);
unlock_page(page);
}
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
cond_resched();
@@ -307,14 +354,16 @@ void truncate_inode_pages_range(struct address_space *mapping,
index = start;
for ( ; ; ) {
cond_resched();
- if (!pagevec_lookup(&pvec, mapping, index,
- min(end - index, (pgoff_t)PAGEVEC_SIZE))) {
+ if (!pagevec_lookup_entries(&pvec, mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE),
+ indices)) {
if (index == start)
break;
index = start;
continue;
}
- if (index == start && pvec.pages[0]->index >= end) {
+ if (index == start && indices[0] >= end) {
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
break;
}
@@ -323,16 +372,22 @@ void truncate_inode_pages_range(struct address_space *mapping,
struct page *page = pvec.pages[i];
/* We rely upon deletion not changing page->index */
- index = page->index;
+ index = indices[i];
if (index >= end)
break;
+ if (radix_tree_exceptional_entry(page)) {
+ clear_exceptional_entry(mapping, index, page);
+ continue;
+ }
+
lock_page(page);
WARN_ON(page->index != index);
wait_on_page_writeback(page);
truncate_inode_page(mapping, page);
unlock_page(page);
}
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
index++;
@@ -360,6 +415,53 @@ void truncate_inode_pages(struct address_space *mapping, loff_t lstart)
EXPORT_SYMBOL(truncate_inode_pages);
/**
+ * truncate_inode_pages_final - truncate *all* pages before inode dies
+ * @mapping: mapping to truncate
+ *
+ * Called under (and serialized by) inode->i_mutex.
+ *
+ * Filesystems have to use this in the .evict_inode path to inform the
+ * VM that this is the final truncate and the inode is going away.
+ */
+void truncate_inode_pages_final(struct address_space *mapping)
+{
+ unsigned long nrshadows;
+ unsigned long nrpages;
+
+ /*
+ * Page reclaim can not participate in regular inode lifetime
+ * management (can't call iput()) and thus can race with the
+ * inode teardown. Tell it when the address space is exiting,
+ * so that it does not install eviction information after the
+ * final truncate has begun.
+ */
+ mapping_set_exiting(mapping);
+
+ /*
+ * When reclaim installs eviction entries, it increases
+ * nrshadows first, then decreases nrpages. Make sure we see
+ * this in the right order or we might miss an entry.
+ */
+ nrpages = mapping->nrpages;
+ smp_rmb();
+ nrshadows = mapping->nrshadows;
+
+ if (nrpages || nrshadows) {
+ /*
+ * As truncation uses a lockless tree lookup, cycle
+ * the tree lock to make sure any ongoing tree
+ * modification that does not see AS_EXITING is
+ * completed before starting the final truncate.
+ */
+ spin_lock_irq(&mapping->tree_lock);
+ spin_unlock_irq(&mapping->tree_lock);
+
+ truncate_inode_pages(mapping, 0);
+ }
+}
+EXPORT_SYMBOL(truncate_inode_pages_final);
+
+/**
* invalidate_mapping_pages - Invalidate all the unlocked pages of one inode
* @mapping: the address_space which holds the pages to invalidate
* @start: the offset 'from' which to invalidate
@@ -375,6 +477,7 @@ EXPORT_SYMBOL(truncate_inode_pages);
unsigned long invalidate_mapping_pages(struct address_space *mapping,
pgoff_t start, pgoff_t end)
{
+ pgoff_t indices[PAGEVEC_SIZE];
struct pagevec pvec;
pgoff_t index = start;
unsigned long ret;
@@ -390,17 +493,23 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping,
*/
pagevec_init(&pvec, 0);
- while (index <= end && pagevec_lookup(&pvec, mapping, index,
- min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
+ while (index <= end && pagevec_lookup_entries(&pvec, mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
+ indices)) {
mem_cgroup_uncharge_start();
for (i = 0; i < pagevec_count(&pvec); i++) {
struct page *page = pvec.pages[i];
/* We rely upon deletion not changing page->index */
- index = page->index;
+ index = indices[i];
if (index > end)
break;
+ if (radix_tree_exceptional_entry(page)) {
+ clear_exceptional_entry(mapping, index, page);
+ continue;
+ }
+
if (!trylock_page(page))
continue;
WARN_ON(page->index != index);
@@ -414,6 +523,7 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping,
deactivate_page(page);
count += ret;
}
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
cond_resched();
@@ -444,7 +554,7 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page)
goto failed;
BUG_ON(page_has_private(page));
- __delete_from_page_cache(page);
+ __delete_from_page_cache(page, NULL);
spin_unlock_irq(&mapping->tree_lock);
mem_cgroup_uncharge_cache_page(page);
@@ -481,6 +591,7 @@ static int do_launder_page(struct address_space *mapping, struct page *page)
int invalidate_inode_pages2_range(struct address_space *mapping,
pgoff_t start, pgoff_t end)
{
+ pgoff_t indices[PAGEVEC_SIZE];
struct pagevec pvec;
pgoff_t index;
int i;
@@ -491,17 +602,23 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
cleancache_invalidate_inode(mapping);
pagevec_init(&pvec, 0);
index = start;
- while (index <= end && pagevec_lookup(&pvec, mapping, index,
- min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
+ while (index <= end && pagevec_lookup_entries(&pvec, mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
+ indices)) {
mem_cgroup_uncharge_start();
for (i = 0; i < pagevec_count(&pvec); i++) {
struct page *page = pvec.pages[i];
/* We rely upon deletion not changing page->index */
- index = page->index;
+ index = indices[i];
if (index > end)
break;
+ if (radix_tree_exceptional_entry(page)) {
+ clear_exceptional_entry(mapping, index, page);
+ continue;
+ }
+
lock_page(page);
WARN_ON(page->index != index);
if (page->mapping != mapping) {
@@ -539,6 +656,7 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
ret = ret2;
unlock_page(page);
}
+ pagevec_remove_exceptionals(&pvec);
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
cond_resched();
diff --git a/mm/vmscan.c b/mm/vmscan.c
index a9c74b409681..1f56a80a7c41 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -224,15 +224,15 @@ shrink_slab_node(struct shrink_control *shrinkctl, struct shrinker *shrinker,
unsigned long freed = 0;
unsigned long long delta;
long total_scan;
- long max_pass;
+ long freeable;
long nr;
long new_nr;
int nid = shrinkctl->nid;
long batch_size = shrinker->batch ? shrinker->batch
: SHRINK_BATCH;
- max_pass = shrinker->count_objects(shrinker, shrinkctl);
- if (max_pass == 0)
+ freeable = shrinker->count_objects(shrinker, shrinkctl);
+ if (freeable == 0)
return 0;
/*
@@ -244,14 +244,14 @@ shrink_slab_node(struct shrink_control *shrinkctl, struct shrinker *shrinker,
total_scan = nr;
delta = (4 * nr_pages_scanned) / shrinker->seeks;
- delta *= max_pass;
+ delta *= freeable;
do_div(delta, lru_pages + 1);
total_scan += delta;
if (total_scan < 0) {
printk(KERN_ERR
"shrink_slab: %pF negative objects to delete nr=%ld\n",
shrinker->scan_objects, total_scan);
- total_scan = max_pass;
+ total_scan = freeable;
}
/*
@@ -260,26 +260,26 @@ shrink_slab_node(struct shrink_control *shrinkctl, struct shrinker *shrinker,
* shrinkers to return -1 all the time. This results in a large
* nr being built up so when a shrink that can do some work
* comes along it empties the entire cache due to nr >>>
- * max_pass. This is bad for sustaining a working set in
+ * freeable. This is bad for sustaining a working set in
* memory.
*
* Hence only allow the shrinker to scan the entire cache when
* a large delta change is calculated directly.
*/
- if (delta < max_pass / 4)
- total_scan = min(total_scan, max_pass / 2);
+ if (delta < freeable / 4)
+ total_scan = min(total_scan, freeable / 2);
/*
* Avoid risking looping forever due to too large nr value:
* never try to free more than twice the estimate number of
* freeable entries.
*/
- if (total_scan > max_pass * 2)
- total_scan = max_pass * 2;
+ if (total_scan > freeable * 2)
+ total_scan = freeable * 2;
trace_mm_shrink_slab_start(shrinker, shrinkctl, nr,
nr_pages_scanned, lru_pages,
- max_pass, delta, total_scan);
+ freeable, delta, total_scan);
/*
* Normally, we should not scan less than batch_size objects in one
@@ -292,12 +292,12 @@ shrink_slab_node(struct shrink_control *shrinkctl, struct shrinker *shrinker,
*
* We detect the "tight on memory" situations by looking at the total
* number of objects we want to scan (total_scan). If it is greater
- * than the total number of objects on slab (max_pass), we must be
+ * than the total number of objects on slab (freeable), we must be
* scanning at high prio and therefore should try to reclaim as much as
* possible.
*/
while (total_scan >= batch_size ||
- total_scan >= max_pass) {
+ total_scan >= freeable) {
unsigned long ret;
unsigned long nr_to_scan = min(batch_size, total_scan);
@@ -523,7 +523,8 @@ static pageout_t pageout(struct page *page, struct address_space *mapping,
* Same as remove_mapping, but if the page is removed from the mapping, it
* gets returned with a refcount of 0.
*/
-static int __remove_mapping(struct address_space *mapping, struct page *page)
+static int __remove_mapping(struct address_space *mapping, struct page *page,
+ bool reclaimed)
{
BUG_ON(!PageLocked(page));
BUG_ON(mapping != page_mapping(page));
@@ -569,10 +570,23 @@ static int __remove_mapping(struct address_space *mapping, struct page *page)
swapcache_free(swap, page);
} else {
void (*freepage)(struct page *);
+ void *shadow = NULL;
freepage = mapping->a_ops->freepage;
-
- __delete_from_page_cache(page);
+ /*
+ * Remember a shadow entry for reclaimed file cache in
+ * order to detect refaults, thus thrashing, later on.
+ *
+ * But don't store shadows in an address space that is
+ * already exiting. This is not just an optizimation,
+ * inode reclaim needs to empty out the radix tree or
+ * the nodes are lost. Don't plant shadows behind its
+ * back.
+ */
+ if (reclaimed && page_is_file_cache(page) &&
+ !mapping_exiting(mapping))
+ shadow = workingset_eviction(mapping, page);
+ __delete_from_page_cache(page, shadow);
spin_unlock_irq(&mapping->tree_lock);
mem_cgroup_uncharge_cache_page(page);
@@ -595,7 +609,7 @@ cannot_free:
*/
int remove_mapping(struct address_space *mapping, struct page *page)
{
- if (__remove_mapping(mapping, page)) {
+ if (__remove_mapping(mapping, page, false)) {
/*
* Unfreezing the refcount with 1 rather than 2 effectively
* drops the pagecache ref for us without requiring another
@@ -1065,7 +1079,7 @@ static unsigned long shrink_page_list(struct list_head *page_list,
}
}
- if (!mapping || !__remove_mapping(mapping, page))
+ if (!mapping || !__remove_mapping(mapping, page, true))
goto keep_locked;
/*
@@ -2297,7 +2311,12 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
struct zone *zone;
unsigned long nr_soft_reclaimed;
unsigned long nr_soft_scanned;
+ unsigned long lru_pages = 0;
bool aborted_reclaim = false;
+ struct reclaim_state *reclaim_state = current->reclaim_state;
+ struct shrink_control shrink = {
+ .gfp_mask = sc->gfp_mask,
+ };
/*
* If the number of buffer_heads in the machine exceeds the maximum
@@ -2307,6 +2326,8 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
if (buffer_heads_over_limit)
sc->gfp_mask |= __GFP_HIGHMEM;
+ nodes_clear(shrink.nodes_to_scan);
+
for_each_zone_zonelist_nodemask(zone, z, zonelist,
gfp_zone(sc->gfp_mask), sc->nodemask) {
if (!populated_zone(zone))
@@ -2318,6 +2339,10 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
if (global_reclaim(sc)) {
if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL))
continue;
+
+ lru_pages += zone_reclaimable_pages(zone);
+ node_set(zone_to_nid(zone), shrink.nodes_to_scan);
+
if (sc->priority != DEF_PRIORITY &&
!zone_reclaimable(zone))
continue; /* Let kswapd poll it */
@@ -2354,6 +2379,20 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
shrink_zone(zone, sc);
}
+ /*
+ * Don't shrink slabs when reclaiming memory from over limit cgroups
+ * but do shrink slab at least once when aborting reclaim for
+ * compaction to avoid unevenly scanning file/anon LRU pages over slab
+ * pages.
+ */
+ if (global_reclaim(sc)) {
+ shrink_slab(&shrink, sc->nr_scanned, lru_pages);
+ if (reclaim_state) {
+ sc->nr_reclaimed += reclaim_state->reclaimed_slab;
+ reclaim_state->reclaimed_slab = 0;
+ }
+ }
+
return aborted_reclaim;
}
@@ -2394,13 +2433,9 @@ static bool all_unreclaimable(struct zonelist *zonelist,
* else, the number of pages reclaimed
*/
static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
- struct scan_control *sc,
- struct shrink_control *shrink)
+ struct scan_control *sc)
{
unsigned long total_scanned = 0;
- struct reclaim_state *reclaim_state = current->reclaim_state;
- struct zoneref *z;
- struct zone *zone;
unsigned long writeback_threshold;
bool aborted_reclaim;
@@ -2415,32 +2450,6 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
sc->nr_scanned = 0;
aborted_reclaim = shrink_zones(zonelist, sc);
- /*
- * Don't shrink slabs when reclaiming memory from over limit
- * cgroups but do shrink slab at least once when aborting
- * reclaim for compaction to avoid unevenly scanning file/anon
- * LRU pages over slab pages.
- */
- if (global_reclaim(sc)) {
- unsigned long lru_pages = 0;
-
- nodes_clear(shrink->nodes_to_scan);
- for_each_zone_zonelist(zone, z, zonelist,
- gfp_zone(sc->gfp_mask)) {
- if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL))
- continue;
-
- lru_pages += zone_reclaimable_pages(zone);
- node_set(zone_to_nid(zone),
- shrink->nodes_to_scan);
- }
-
- shrink_slab(shrink, sc->nr_scanned, lru_pages);
- if (reclaim_state) {
- sc->nr_reclaimed += reclaim_state->reclaimed_slab;
- reclaim_state->reclaimed_slab = 0;
- }
- }
total_scanned += sc->nr_scanned;
if (sc->nr_reclaimed >= sc->nr_to_reclaim)
goto out;
@@ -2602,9 +2611,6 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
.target_mem_cgroup = NULL,
.nodemask = nodemask,
};
- struct shrink_control shrink = {
- .gfp_mask = sc.gfp_mask,
- };
/*
* Do not enter reclaim if fatal signal was delivered while throttled.
@@ -2618,7 +2624,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
sc.may_writepage,
gfp_mask);
- nr_reclaimed = do_try_to_free_pages(zonelist, &sc, &shrink);
+ nr_reclaimed = do_try_to_free_pages(zonelist, &sc);
trace_mm_vmscan_direct_reclaim_end(nr_reclaimed);
@@ -2685,9 +2691,6 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
(GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK),
};
- struct shrink_control shrink = {
- .gfp_mask = sc.gfp_mask,
- };
/*
* Unlike direct reclaim via alloc_pages(), memcg's reclaim doesn't
@@ -2702,7 +2705,7 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
sc.may_writepage,
sc.gfp_mask);
- nr_reclaimed = do_try_to_free_pages(zonelist, &sc, &shrink);
+ nr_reclaimed = do_try_to_free_pages(zonelist, &sc);
trace_mm_vmscan_memcg_reclaim_end(nr_reclaimed);
@@ -3337,9 +3340,6 @@ unsigned long shrink_all_memory(unsigned long nr_to_reclaim)
.order = 0,
.priority = DEF_PRIORITY,
};
- struct shrink_control shrink = {
- .gfp_mask = sc.gfp_mask,
- };
struct zonelist *zonelist = node_zonelist(numa_node_id(), sc.gfp_mask);
struct task_struct *p = current;
unsigned long nr_reclaimed;
@@ -3349,7 +3349,7 @@ unsigned long shrink_all_memory(unsigned long nr_to_reclaim)
reclaim_state.reclaimed_slab = 0;
p->reclaim_state = &reclaim_state;
- nr_reclaimed = do_try_to_free_pages(zonelist, &sc, &shrink);
+ nr_reclaimed = do_try_to_free_pages(zonelist, &sc);
p->reclaim_state = NULL;
lockdep_clear_current_reclaim_state();
diff --git a/mm/vmstat.c b/mm/vmstat.c
index def5dd2fbe61..302dd076b8bf 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -770,6 +770,9 @@ const char * const vmstat_text[] = {
"numa_local",
"numa_other",
#endif
+ "workingset_refault",
+ "workingset_activate",
+ "workingset_nodereclaim",
"nr_anon_transparent_hugepages",
"nr_free_cma",
"nr_dirty_threshold",
@@ -810,6 +813,9 @@ const char * const vmstat_text[] = {
"pgrotated",
+ "drop_pagecache",
+ "drop_slab",
+
#ifdef CONFIG_NUMA_BALANCING
"numa_pte_updates",
"numa_huge_pte_updates",
@@ -1292,14 +1298,14 @@ static int __init setup_vmstat(void)
#ifdef CONFIG_SMP
int cpu;
- register_cpu_notifier(&vmstat_notifier);
+ cpu_notifier_register_begin();
+ __register_cpu_notifier(&vmstat_notifier);
- get_online_cpus();
for_each_online_cpu(cpu) {
start_cpu_timer(cpu);
node_set_state(cpu_to_node(cpu), N_CPU);
}
- put_online_cpus();
+ cpu_notifier_register_done();
#endif
#ifdef CONFIG_PROC_FS
proc_create("buddyinfo", S_IRUGO, NULL, &fragmentation_file_operations);
diff --git a/mm/workingset.c b/mm/workingset.c
new file mode 100644
index 000000000000..f7216fa7da27
--- /dev/null
+++ b/mm/workingset.c
@@ -0,0 +1,414 @@
+/*
+ * Workingset detection
+ *
+ * Copyright (C) 2013 Red Hat, Inc., Johannes Weiner
+ */
+
+#include <linux/memcontrol.h>
+#include <linux/writeback.h>
+#include <linux/pagemap.h>
+#include <linux/atomic.h>
+#include <linux/module.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+
+/*
+ * Double CLOCK lists
+ *
+ * Per zone, two clock lists are maintained for file pages: the
+ * inactive and the active list. Freshly faulted pages start out at
+ * the head of the inactive list and page reclaim scans pages from the
+ * tail. Pages that are accessed multiple times on the inactive list
+ * are promoted to the active list, to protect them from reclaim,
+ * whereas active pages are demoted to the inactive list when the
+ * active list grows too big.
+ *
+ * fault ------------------------+
+ * |
+ * +--------------+ | +-------------+
+ * reclaim <- | inactive | <-+-- demotion | active | <--+
+ * +--------------+ +-------------+ |
+ * | |
+ * +-------------- promotion ------------------+
+ *
+ *
+ * Access frequency and refault distance
+ *
+ * A workload is thrashing when its pages are frequently used but they
+ * are evicted from the inactive list every time before another access
+ * would have promoted them to the active list.
+ *
+ * In cases where the average access distance between thrashing pages
+ * is bigger than the size of memory there is nothing that can be
+ * done - the thrashing set could never fit into memory under any
+ * circumstance.
+ *
+ * However, the average access distance could be bigger than the
+ * inactive list, yet smaller than the size of memory. In this case,
+ * the set could fit into memory if it weren't for the currently
+ * active pages - which may be used more, hopefully less frequently:
+ *
+ * +-memory available to cache-+
+ * | |
+ * +-inactive------+-active----+
+ * a b | c d e f g h i | J K L M N |
+ * +---------------+-----------+
+ *
+ * It is prohibitively expensive to accurately track access frequency
+ * of pages. But a reasonable approximation can be made to measure
+ * thrashing on the inactive list, after which refaulting pages can be
+ * activated optimistically to compete with the existing active pages.
+ *
+ * Approximating inactive page access frequency - Observations:
+ *
+ * 1. When a page is accessed for the first time, it is added to the
+ * head of the inactive list, slides every existing inactive page
+ * towards the tail by one slot, and pushes the current tail page
+ * out of memory.
+ *
+ * 2. When a page is accessed for the second time, it is promoted to
+ * the active list, shrinking the inactive list by one slot. This
+ * also slides all inactive pages that were faulted into the cache
+ * more recently than the activated page towards the tail of the
+ * inactive list.
+ *
+ * Thus:
+ *
+ * 1. The sum of evictions and activations between any two points in
+ * time indicate the minimum number of inactive pages accessed in
+ * between.
+ *
+ * 2. Moving one inactive page N page slots towards the tail of the
+ * list requires at least N inactive page accesses.
+ *
+ * Combining these:
+ *
+ * 1. When a page is finally evicted from memory, the number of
+ * inactive pages accessed while the page was in cache is at least
+ * the number of page slots on the inactive list.
+ *
+ * 2. In addition, measuring the sum of evictions and activations (E)
+ * at the time of a page's eviction, and comparing it to another
+ * reading (R) at the time the page faults back into memory tells
+ * the minimum number of accesses while the page was not cached.
+ * This is called the refault distance.
+ *
+ * Because the first access of the page was the fault and the second
+ * access the refault, we combine the in-cache distance with the
+ * out-of-cache distance to get the complete minimum access distance
+ * of this page:
+ *
+ * NR_inactive + (R - E)
+ *
+ * And knowing the minimum access distance of a page, we can easily
+ * tell if the page would be able to stay in cache assuming all page
+ * slots in the cache were available:
+ *
+ * NR_inactive + (R - E) <= NR_inactive + NR_active
+ *
+ * which can be further simplified to
+ *
+ * (R - E) <= NR_active
+ *
+ * Put into words, the refault distance (out-of-cache) can be seen as
+ * a deficit in inactive list space (in-cache). If the inactive list
+ * had (R - E) more page slots, the page would not have been evicted
+ * in between accesses, but activated instead. And on a full system,
+ * the only thing eating into inactive list space is active pages.
+ *
+ *
+ * Activating refaulting pages
+ *
+ * All that is known about the active list is that the pages have been
+ * accessed more than once in the past. This means that at any given
+ * time there is actually a good chance that pages on the active list
+ * are no longer in active use.
+ *
+ * So when a refault distance of (R - E) is observed and there are at
+ * least (R - E) active pages, the refaulting page is activated
+ * optimistically in the hope that (R - E) active pages are actually
+ * used less frequently than the refaulting page - or even not used at
+ * all anymore.
+ *
+ * If this is wrong and demotion kicks in, the pages which are truly
+ * used more frequently will be reactivated while the less frequently
+ * used once will be evicted from memory.
+ *
+ * But if this is right, the stale pages will be pushed out of memory
+ * and the used pages get to stay in cache.
+ *
+ *
+ * Implementation
+ *
+ * For each zone's file LRU lists, a counter for inactive evictions
+ * and activations is maintained (zone->inactive_age).
+ *
+ * On eviction, a snapshot of this counter (along with some bits to
+ * identify the zone) is stored in the now empty page cache radix tree
+ * slot of the evicted page. This is called a shadow entry.
+ *
+ * On cache misses for which there are shadow entries, an eligible
+ * refault distance will immediately activate the refaulting page.
+ */
+
+static void *pack_shadow(unsigned long eviction, struct zone *zone)
+{
+ eviction = (eviction << NODES_SHIFT) | zone_to_nid(zone);
+ eviction = (eviction << ZONES_SHIFT) | zone_idx(zone);
+ eviction = (eviction << RADIX_TREE_EXCEPTIONAL_SHIFT);
+
+ return (void *)(eviction | RADIX_TREE_EXCEPTIONAL_ENTRY);
+}
+
+static void unpack_shadow(void *shadow,
+ struct zone **zone,
+ unsigned long *distance)
+{
+ unsigned long entry = (unsigned long)shadow;
+ unsigned long eviction;
+ unsigned long refault;
+ unsigned long mask;
+ int zid, nid;
+
+ entry >>= RADIX_TREE_EXCEPTIONAL_SHIFT;
+ zid = entry & ((1UL << ZONES_SHIFT) - 1);
+ entry >>= ZONES_SHIFT;
+ nid = entry & ((1UL << NODES_SHIFT) - 1);
+ entry >>= NODES_SHIFT;
+ eviction = entry;
+
+ *zone = NODE_DATA(nid)->node_zones + zid;
+
+ refault = atomic_long_read(&(*zone)->inactive_age);
+ mask = ~0UL >> (NODES_SHIFT + ZONES_SHIFT +
+ RADIX_TREE_EXCEPTIONAL_SHIFT);
+ /*
+ * The unsigned subtraction here gives an accurate distance
+ * across inactive_age overflows in most cases.
+ *
+ * There is a special case: usually, shadow entries have a
+ * short lifetime and are either refaulted or reclaimed along
+ * with the inode before they get too old. But it is not
+ * impossible for the inactive_age to lap a shadow entry in
+ * the field, which can then can result in a false small
+ * refault distance, leading to a false activation should this
+ * old entry actually refault again. However, earlier kernels
+ * used to deactivate unconditionally with *every* reclaim
+ * invocation for the longest time, so the occasional
+ * inappropriate activation leading to pressure on the active
+ * list is not a problem.
+ */
+ *distance = (refault - eviction) & mask;
+}
+
+/**
+ * workingset_eviction - note the eviction of a page from memory
+ * @mapping: address space the page was backing
+ * @page: the page being evicted
+ *
+ * Returns a shadow entry to be stored in @mapping->page_tree in place
+ * of the evicted @page so that a later refault can be detected.
+ */
+void *workingset_eviction(struct address_space *mapping, struct page *page)
+{
+ struct zone *zone = page_zone(page);
+ unsigned long eviction;
+
+ eviction = atomic_long_inc_return(&zone->inactive_age);
+ return pack_shadow(eviction, zone);
+}
+
+/**
+ * workingset_refault - evaluate the refault of a previously evicted page
+ * @shadow: shadow entry of the evicted page
+ *
+ * Calculates and evaluates the refault distance of the previously
+ * evicted page in the context of the zone it was allocated in.
+ *
+ * Returns %true if the page should be activated, %false otherwise.
+ */
+bool workingset_refault(void *shadow)
+{
+ unsigned long refault_distance;
+ struct zone *zone;
+
+ unpack_shadow(shadow, &zone, &refault_distance);
+ inc_zone_state(zone, WORKINGSET_REFAULT);
+
+ if (refault_distance <= zone_page_state(zone, NR_ACTIVE_FILE)) {
+ inc_zone_state(zone, WORKINGSET_ACTIVATE);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * workingset_activation - note a page activation
+ * @page: page that is being activated
+ */
+void workingset_activation(struct page *page)
+{
+ atomic_long_inc(&page_zone(page)->inactive_age);
+}
+
+/*
+ * Shadow entries reflect the share of the working set that does not
+ * fit into memory, so their number depends on the access pattern of
+ * the workload. In most cases, they will refault or get reclaimed
+ * along with the inode, but a (malicious) workload that streams
+ * through files with a total size several times that of available
+ * memory, while preventing the inodes from being reclaimed, can
+ * create excessive amounts of shadow nodes. To keep a lid on this,
+ * track shadow nodes and reclaim them when they grow way past the
+ * point where they would still be useful.
+ */
+
+struct list_lru workingset_shadow_nodes;
+
+static unsigned long count_shadow_nodes(struct shrinker *shrinker,
+ struct shrink_control *sc)
+{
+ unsigned long shadow_nodes;
+ unsigned long max_nodes;
+ unsigned long pages;
+
+ /* list_lru lock nests inside IRQ-safe mapping->tree_lock */
+ local_irq_disable();
+ shadow_nodes = list_lru_count_node(&workingset_shadow_nodes, sc->nid);
+ local_irq_enable();
+
+ pages = node_present_pages(sc->nid);
+ /*
+ * Active cache pages are limited to 50% of memory, and shadow
+ * entries that represent a refault distance bigger than that
+ * do not have any effect. Limit the number of shadow nodes
+ * such that shadow entries do not exceed the number of active
+ * cache pages, assuming a worst-case node population density
+ * of 1/8th on average.
+ *
+ * On 64-bit with 7 radix_tree_nodes per page and 64 slots
+ * each, this will reclaim shadow entries when they consume
+ * ~2% of available memory:
+ *
+ * PAGE_SIZE / radix_tree_nodes / node_entries / PAGE_SIZE
+ */
+ max_nodes = pages >> (1 + RADIX_TREE_MAP_SHIFT - 3);
+
+ if (shadow_nodes <= max_nodes)
+ return 0;
+
+ return shadow_nodes - max_nodes;
+}
+
+static enum lru_status shadow_lru_isolate(struct list_head *item,
+ spinlock_t *lru_lock,
+ void *arg)
+{
+ struct address_space *mapping;
+ struct radix_tree_node *node;
+ unsigned int i;
+ int ret;
+
+ /*
+ * Page cache insertions and deletions synchroneously maintain
+ * the shadow node LRU under the mapping->tree_lock and the
+ * lru_lock. Because the page cache tree is emptied before
+ * the inode can be destroyed, holding the lru_lock pins any
+ * address_space that has radix tree nodes on the LRU.
+ *
+ * We can then safely transition to the mapping->tree_lock to
+ * pin only the address_space of the particular node we want
+ * to reclaim, take the node off-LRU, and drop the lru_lock.
+ */
+
+ node = container_of(item, struct radix_tree_node, private_list);
+ mapping = node->private_data;
+
+ /* Coming from the list, invert the lock order */
+ if (!spin_trylock(&mapping->tree_lock)) {
+ spin_unlock(lru_lock);
+ ret = LRU_RETRY;
+ goto out;
+ }
+
+ list_del_init(item);
+ spin_unlock(lru_lock);
+
+ /*
+ * The nodes should only contain one or more shadow entries,
+ * no pages, so we expect to be able to remove them all and
+ * delete and free the empty node afterwards.
+ */
+
+ BUG_ON(!node->count);
+ BUG_ON(node->count & RADIX_TREE_COUNT_MASK);
+
+ for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) {
+ if (node->slots[i]) {
+ BUG_ON(!radix_tree_exceptional_entry(node->slots[i]));
+ node->slots[i] = NULL;
+ BUG_ON(node->count < (1U << RADIX_TREE_COUNT_SHIFT));
+ node->count -= 1U << RADIX_TREE_COUNT_SHIFT;
+ BUG_ON(!mapping->nrshadows);
+ mapping->nrshadows--;
+ }
+ }
+ BUG_ON(node->count);
+ inc_zone_state(page_zone(virt_to_page(node)), WORKINGSET_NODERECLAIM);
+ if (!__radix_tree_delete_node(&mapping->page_tree, node))
+ BUG();
+
+ spin_unlock(&mapping->tree_lock);
+ ret = LRU_REMOVED_RETRY;
+out:
+ local_irq_enable();
+ cond_resched();
+ local_irq_disable();
+ spin_lock(lru_lock);
+ return ret;
+}
+
+static unsigned long scan_shadow_nodes(struct shrinker *shrinker,
+ struct shrink_control *sc)
+{
+ unsigned long ret;
+
+ /* list_lru lock nests inside IRQ-safe mapping->tree_lock */
+ local_irq_disable();
+ ret = list_lru_walk_node(&workingset_shadow_nodes, sc->nid,
+ shadow_lru_isolate, NULL, &sc->nr_to_scan);
+ local_irq_enable();
+ return ret;
+}
+
+static struct shrinker workingset_shadow_shrinker = {
+ .count_objects = count_shadow_nodes,
+ .scan_objects = scan_shadow_nodes,
+ .seeks = DEFAULT_SEEKS,
+ .flags = SHRINKER_NUMA_AWARE,
+};
+
+/*
+ * Our list_lru->lock is IRQ-safe as it nests inside the IRQ-safe
+ * mapping->tree_lock.
+ */
+static struct lock_class_key shadow_nodes_key;
+
+static int __init workingset_init(void)
+{
+ int ret;
+
+ ret = list_lru_init_key(&workingset_shadow_nodes, &shadow_nodes_key);
+ if (ret)
+ goto err;
+ ret = register_shrinker(&workingset_shadow_shrinker);
+ if (ret)
+ goto err_list_lru;
+ return 0;
+err_list_lru:
+ list_lru_destroy(&workingset_shadow_nodes);
+err:
+ return ret;
+}
+module_init(workingset_init);
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index c03ca5e9fe15..36b4591a7a2d 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -814,21 +814,32 @@ static void zs_exit(void)
{
int cpu;
+ cpu_notifier_register_begin();
+
for_each_online_cpu(cpu)
zs_cpu_notifier(NULL, CPU_DEAD, (void *)(long)cpu);
- unregister_cpu_notifier(&zs_cpu_nb);
+ __unregister_cpu_notifier(&zs_cpu_nb);
+
+ cpu_notifier_register_done();
}
static int zs_init(void)
{
int cpu, ret;
- register_cpu_notifier(&zs_cpu_nb);
+ cpu_notifier_register_begin();
+
+ __register_cpu_notifier(&zs_cpu_nb);
for_each_online_cpu(cpu) {
ret = zs_cpu_notifier(NULL, CPU_UP_PREPARE, (void *)(long)cpu);
- if (notifier_to_errno(ret))
+ if (notifier_to_errno(ret)) {
+ cpu_notifier_register_done();
goto fail;
+ }
}
+
+ cpu_notifier_register_done();
+
return 0;
fail:
zs_exit();
diff --git a/mm/zswap.c b/mm/zswap.c
index e55bab9dc41f..d7337fbf6605 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -387,18 +387,18 @@ static int zswap_cpu_init(void)
{
unsigned long cpu;
- get_online_cpus();
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
if (__zswap_cpu_notifier(CPU_UP_PREPARE, cpu) != NOTIFY_OK)
goto cleanup;
- register_cpu_notifier(&zswap_cpu_notifier_block);
- put_online_cpus();
+ __register_cpu_notifier(&zswap_cpu_notifier_block);
+ cpu_notifier_register_done();
return 0;
cleanup:
for_each_online_cpu(cpu)
__zswap_cpu_notifier(CPU_UP_CANCELED, cpu);
- put_online_cpus();
+ cpu_notifier_register_done();
return -ENOMEM;
}
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h
index 5704ed9c3a23..9d010a09ab98 100644
--- a/net/8021q/vlan.h
+++ b/net/8021q/vlan.h
@@ -38,9 +38,9 @@ struct vlan_info {
static inline unsigned int vlan_proto_idx(__be16 proto)
{
switch (proto) {
- case __constant_htons(ETH_P_8021Q):
+ case htons(ETH_P_8021Q):
return VLAN_PROTO_8021Q;
- case __constant_htons(ETH_P_8021AD):
+ case htons(ETH_P_8021AD):
return VLAN_PROTO_8021AD;
default:
BUG();
diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c
index 6ee48aac776f..3c32bd257b73 100644
--- a/net/8021q/vlan_core.c
+++ b/net/8021q/vlan_core.c
@@ -22,11 +22,11 @@ bool vlan_do_receive(struct sk_buff **skbp)
return false;
skb->dev = vlan_dev;
- if (skb->pkt_type == PACKET_OTHERHOST) {
+ if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) {
/* Our lower layer thinks this is not local, let's make sure.
* This allows the VLAN to have a different MAC than the
* underlying device, and still route correctly. */
- if (ether_addr_equal(eth_hdr(skb)->h_dest, vlan_dev->dev_addr))
+ if (ether_addr_equal_64bits(eth_hdr(skb)->h_dest, vlan_dev->dev_addr))
skb->pkt_type = PACKET_HOST;
}
@@ -106,6 +106,12 @@ u16 vlan_dev_vlan_id(const struct net_device *dev)
}
EXPORT_SYMBOL(vlan_dev_vlan_id);
+__be16 vlan_dev_vlan_proto(const struct net_device *dev)
+{
+ return vlan_dev_priv(dev)->vlan_proto;
+}
+EXPORT_SYMBOL(vlan_dev_vlan_proto);
+
static struct sk_buff *vlan_reorder_header(struct sk_buff *skb)
{
if (skb_cow(skb, skb_headroom(skb)) < 0)
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 27bfe2f8e2de..6f142f03716d 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -559,7 +559,7 @@ static const struct net_device_ops vlan_netdev_ops;
static int vlan_dev_init(struct net_device *dev)
{
struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
- int subclass = 0, i;
+ int subclass = 0;
netif_carrier_off(dev);
@@ -613,17 +613,10 @@ static int vlan_dev_init(struct net_device *dev)
vlan_dev_set_lockdep_class(dev, subclass);
- vlan_dev_priv(dev)->vlan_pcpu_stats = alloc_percpu(struct vlan_pcpu_stats);
+ vlan_dev_priv(dev)->vlan_pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats);
if (!vlan_dev_priv(dev)->vlan_pcpu_stats)
return -ENOMEM;
- for_each_possible_cpu(i) {
- struct vlan_pcpu_stats *vlan_stat;
- vlan_stat = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i);
- u64_stats_init(&vlan_stat->syncp);
- }
-
-
return 0;
}
@@ -689,13 +682,13 @@ static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, st
p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i);
do {
- start = u64_stats_fetch_begin_bh(&p->syncp);
+ start = u64_stats_fetch_begin_irq(&p->syncp);
rxpackets = p->rx_packets;
rxbytes = p->rx_bytes;
rxmulticast = p->rx_multicast;
txpackets = p->tx_packets;
txbytes = p->tx_bytes;
- } while (u64_stats_fetch_retry_bh(&p->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&p->syncp, start));
stats->rx_packets += rxpackets;
stats->rx_bytes += rxbytes;
@@ -718,20 +711,19 @@ static void vlan_dev_poll_controller(struct net_device *dev)
return;
}
-static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo,
- gfp_t gfp)
+static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo)
{
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
struct net_device *real_dev = vlan->real_dev;
struct netpoll *netpoll;
int err = 0;
- netpoll = kzalloc(sizeof(*netpoll), gfp);
+ netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
err = -ENOMEM;
if (!netpoll)
goto out;
- err = __netpoll_setup(netpoll, real_dev, gfp);
+ err = __netpoll_setup(netpoll, real_dev);
if (err) {
kfree(netpoll);
goto out;
diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c
index c7e634af8516..8ac8a5cc2143 100644
--- a/net/8021q/vlan_netlink.c
+++ b/net/8021q/vlan_netlink.c
@@ -56,8 +56,8 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[])
if (data[IFLA_VLAN_PROTOCOL]) {
switch (nla_get_be16(data[IFLA_VLAN_PROTOCOL])) {
- case __constant_htons(ETH_P_8021Q):
- case __constant_htons(ETH_P_8021AD):
+ case htons(ETH_P_8021Q):
+ case htons(ETH_P_8021AD):
break;
default:
return -EPROTONOSUPPORT;
diff --git a/net/Kconfig b/net/Kconfig
index e411046a62e3..d92afe4204d9 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -89,8 +89,12 @@ config NETWORK_SECMARK
to nfmark, but designated for security purposes.
If you are unsure how to answer this question, answer N.
+config NET_PTP_CLASSIFY
+ def_bool n
+
config NETWORK_PHY_TIMESTAMPING
bool "Timestamping in PHY devices"
+ select NET_PTP_CLASSIFY
help
This allows timestamping of network packets by PHYs with
hardware timestamping capabilities. This option adds some
@@ -239,7 +243,7 @@ config XPS
default y
config CGROUP_NET_PRIO
- tristate "Network priority cgroup"
+ bool "Network priority cgroup"
depends on CGROUPS
---help---
Cgroup subsystem for use in assigning processes to network priorities on
diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c
index d27b86dfb0e9..d1c55d8dd0a2 100644
--- a/net/appletalk/aarp.c
+++ b/net/appletalk/aarp.c
@@ -926,7 +926,7 @@ static struct aarp_entry *iter_next(struct aarp_iter_state *iter, loff_t *pos)
struct aarp_entry *entry;
rescan:
- while(ct < AARP_HASH_SIZE) {
+ while (ct < AARP_HASH_SIZE) {
for (entry = table[ct]; entry; entry = entry->next) {
if (!pos || ++off == *pos) {
iter->table = table;
@@ -995,7 +995,7 @@ static const char *dt2str(unsigned long ticks)
{
static char buf[32];
- sprintf(buf, "%ld.%02ld", ticks / HZ, ((ticks % HZ) * 100 ) / HZ);
+ sprintf(buf, "%ld.%02ld", ticks / HZ, ((ticks % HZ) * 100) / HZ);
return buf;
}
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index 02806c6b2ff3..786ee2f83d5f 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -293,7 +293,7 @@ static int atif_probe_device(struct atalk_iface *atif)
/* Perform AARP probing for a proxy address */
static int atif_proxy_probe_device(struct atalk_iface *atif,
- struct atalk_addr* proxy_addr)
+ struct atalk_addr *proxy_addr)
{
int netrange = ntohs(atif->nets.nr_lastnet) -
ntohs(atif->nets.nr_firstnet) + 1;
@@ -581,7 +581,7 @@ out:
}
/* Delete a route. Find it and discard it */
-static int atrtr_delete(struct atalk_addr * addr)
+static int atrtr_delete(struct atalk_addr *addr)
{
struct atalk_route **r = &atalk_routes;
int retval = 0;
@@ -936,11 +936,11 @@ static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset,
int i, copy;
/* checksum stuff in header space */
- if ( (copy = start - offset) > 0) {
+ if ((copy = start - offset) > 0) {
if (copy > len)
copy = len;
sum = atalk_sum_partial(skb->data + offset, copy, sum);
- if ( (len -= copy) == 0)
+ if ((len -= copy) == 0)
return sum;
offset += copy;
@@ -1151,7 +1151,7 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
goto out;
at->src_net = addr->sat_addr.s_net = ap->s_net;
- at->src_node = addr->sat_addr.s_node= ap->s_node;
+ at->src_node = addr->sat_addr.s_node = ap->s_node;
} else {
err = -EADDRNOTAVAIL;
if (!atalk_find_interface(addr->sat_addr.s_net,
@@ -1790,53 +1790,53 @@ static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
void __user *argp = (void __user *)arg;
switch (cmd) {
- /* Protocol layer */
- case TIOCOUTQ: {
- long amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
+ /* Protocol layer */
+ case TIOCOUTQ: {
+ long amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
- if (amount < 0)
- amount = 0;
- rc = put_user(amount, (int __user *)argp);
- break;
- }
- case TIOCINQ: {
- /*
- * These two are safe on a single CPU system as only
- * user tasks fiddle here
- */
- struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
- long amount = 0;
+ if (amount < 0)
+ amount = 0;
+ rc = put_user(amount, (int __user *)argp);
+ break;
+ }
+ case TIOCINQ: {
+ /*
+ * These two are safe on a single CPU system as only
+ * user tasks fiddle here
+ */
+ struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
+ long amount = 0;
- if (skb)
- amount = skb->len - sizeof(struct ddpehdr);
- rc = put_user(amount, (int __user *)argp);
- break;
- }
- case SIOCGSTAMP:
- rc = sock_get_timestamp(sk, argp);
- break;
- case SIOCGSTAMPNS:
- rc = sock_get_timestampns(sk, argp);
- break;
- /* Routing */
- case SIOCADDRT:
- case SIOCDELRT:
- rc = -EPERM;
- if (capable(CAP_NET_ADMIN))
- rc = atrtr_ioctl(cmd, argp);
- break;
- /* Interface */
- case SIOCGIFADDR:
- case SIOCSIFADDR:
- case SIOCGIFBRDADDR:
- case SIOCATALKDIFADDR:
- case SIOCDIFADDR:
- case SIOCSARP: /* proxy AARP */
- case SIOCDARP: /* proxy AARP */
- rtnl_lock();
- rc = atif_ioctl(cmd, argp);
- rtnl_unlock();
- break;
+ if (skb)
+ amount = skb->len - sizeof(struct ddpehdr);
+ rc = put_user(amount, (int __user *)argp);
+ break;
+ }
+ case SIOCGSTAMP:
+ rc = sock_get_timestamp(sk, argp);
+ break;
+ case SIOCGSTAMPNS:
+ rc = sock_get_timestampns(sk, argp);
+ break;
+ /* Routing */
+ case SIOCADDRT:
+ case SIOCDELRT:
+ rc = -EPERM;
+ if (capable(CAP_NET_ADMIN))
+ rc = atrtr_ioctl(cmd, argp);
+ break;
+ /* Interface */
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCATALKDIFADDR:
+ case SIOCDIFADDR:
+ case SIOCSARP: /* proxy AARP */
+ case SIOCDARP: /* proxy AARP */
+ rtnl_lock();
+ rc = atif_ioctl(cmd, argp);
+ rtnl_unlock();
+ break;
}
return rc;
diff --git a/net/atm/mpc.c b/net/atm/mpc.c
index b71ff6b234f2..91dc58f1124d 100644
--- a/net/atm/mpc.c
+++ b/net/atm/mpc.c
@@ -1492,7 +1492,7 @@ static void __exit atm_mpoa_cleanup(void)
mpc_proc_clean();
- del_timer(&mpc_timer);
+ del_timer_sync(&mpc_timer);
unregister_netdevice_notifier(&mpoa_notifier);
deregister_atm_ioctl(&atm_ioctl_ops);
diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig
index fa780b76630e..11660a3aab5a 100644
--- a/net/batman-adv/Kconfig
+++ b/net/batman-adv/Kconfig
@@ -50,6 +50,15 @@ config BATMAN_ADV_NC
If you think that your network does not need this feature you
can safely disable it and save some space.
+config BATMAN_ADV_MCAST
+ bool "Multicast optimisation"
+ depends on BATMAN_ADV
+ default n
+ help
+ This option enables the multicast optimisation which aims to
+ reduce the air overhead while improving the reliability of
+ multicast messages.
+
config BATMAN_ADV_DEBUG
bool "B.A.T.M.A.N. debugging"
depends on BATMAN_ADV
diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile
index 42df18f877e9..eb7d8c0388e4 100644
--- a/net/batman-adv/Makefile
+++ b/net/batman-adv/Makefile
@@ -36,3 +36,4 @@ batman-adv-y += send.o
batman-adv-y += soft-interface.o
batman-adv-y += sysfs.o
batman-adv-y += translation-table.o
+batman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast.o
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 8323bced8e5b..b3bd4ec3fd94 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -347,10 +347,10 @@ static void batadv_iv_ogm_iface_update_mac(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;
- memcpy(batadv_ogm_packet->orig,
- hard_iface->net_dev->dev_addr, ETH_ALEN);
- memcpy(batadv_ogm_packet->prev_sender,
- hard_iface->net_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(batadv_ogm_packet->orig,
+ hard_iface->net_dev->dev_addr);
+ ether_addr_copy(batadv_ogm_packet->prev_sender,
+ hard_iface->net_dev->dev_addr);
}
static void
@@ -830,7 +830,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
tvlv_len = ntohs(batadv_ogm_packet->tvlv_len);
batadv_ogm_packet->ttl--;
- memcpy(batadv_ogm_packet->prev_sender, ethhdr->h_source, ETH_ALEN);
+ ether_addr_copy(batadv_ogm_packet->prev_sender, ethhdr->h_source);
/* apply hop penalty */
batadv_ogm_packet->tq = batadv_hop_penalty(batadv_ogm_packet->tq,
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 05f0712be5e7..6f0d9ec37950 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -191,7 +191,7 @@ batadv_backbone_hash_find(struct batadv_priv *bat_priv,
if (!hash)
return NULL;
- memcpy(search_entry.orig, addr, ETH_ALEN);
+ ether_addr_copy(search_entry.orig, addr);
search_entry.vid = vid;
index = batadv_choose_backbone_gw(&search_entry, hash->size);
@@ -305,7 +305,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,
/* normal claim frame
* set Ethernet SRC to the clients mac
*/
- memcpy(ethhdr->h_source, mac, ETH_ALEN);
+ ether_addr_copy(ethhdr->h_source, mac);
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"bla_send_claim(): CLAIM %pM on vid %d\n", mac,
BATADV_PRINT_VID(vid));
@@ -314,7 +314,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,
/* unclaim frame
* set HW SRC to the clients mac
*/
- memcpy(hw_src, mac, ETH_ALEN);
+ ether_addr_copy(hw_src, mac);
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"bla_send_claim(): UNCLAIM %pM on vid %d\n", mac,
BATADV_PRINT_VID(vid));
@@ -323,7 +323,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,
/* announcement frame
* set HW SRC to the special mac containg the crc
*/
- memcpy(hw_src, mac, ETH_ALEN);
+ ether_addr_copy(hw_src, mac);
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"bla_send_claim(): ANNOUNCE of %pM on vid %d\n",
ethhdr->h_source, BATADV_PRINT_VID(vid));
@@ -333,8 +333,8 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,
* set HW SRC and header destination to the receiving backbone
* gws mac
*/
- memcpy(hw_src, mac, ETH_ALEN);
- memcpy(ethhdr->h_dest, mac, ETH_ALEN);
+ ether_addr_copy(hw_src, mac);
+ ether_addr_copy(ethhdr->h_dest, mac);
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"bla_send_claim(): REQUEST of %pM to %pM on vid %d\n",
ethhdr->h_source, ethhdr->h_dest,
@@ -395,7 +395,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig,
entry->bat_priv = bat_priv;
atomic_set(&entry->request_sent, 0);
atomic_set(&entry->wait_periods, 0);
- memcpy(entry->orig, orig, ETH_ALEN);
+ ether_addr_copy(entry->orig, orig);
/* one for the hash, one for returning */
atomic_set(&entry->refcount, 2);
@@ -563,7 +563,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
struct batadv_bla_claim search_claim;
int hash_added;
- memcpy(search_claim.addr, mac, ETH_ALEN);
+ ether_addr_copy(search_claim.addr, mac);
search_claim.vid = vid;
claim = batadv_claim_hash_find(bat_priv, &search_claim);
@@ -573,7 +573,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
if (!claim)
return;
- memcpy(claim->addr, mac, ETH_ALEN);
+ ether_addr_copy(claim->addr, mac);
claim->vid = vid;
claim->lasttime = jiffies;
claim->backbone_gw = backbone_gw;
@@ -624,7 +624,7 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv,
{
struct batadv_bla_claim search_claim, *claim;
- memcpy(search_claim.addr, mac, ETH_ALEN);
+ ether_addr_copy(search_claim.addr, mac);
search_claim.vid = vid;
claim = batadv_claim_hash_find(bat_priv, &search_claim);
if (!claim)
@@ -882,7 +882,7 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
proto = ethhdr->h_proto;
headlen = ETH_HLEN;
if (vid & BATADV_VLAN_HAS_TAG) {
- vhdr = (struct vlan_ethhdr *)ethhdr;
+ vhdr = vlan_eth_hdr(skb);
proto = vhdr->h_vlan_encapsulated_proto;
headlen += VLAN_HLEN;
}
@@ -1103,8 +1103,8 @@ void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
oldif->net_dev->dev_addr))
continue;
- memcpy(backbone_gw->orig,
- primary_if->net_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(backbone_gw->orig,
+ primary_if->net_dev->dev_addr);
/* send an announce frame so others will ask for our
* claims and update their tables.
*/
@@ -1310,7 +1310,7 @@ int batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
entry = &bat_priv->bla.bcast_duplist[curr];
entry->crc = crc;
entry->entrytime = jiffies;
- memcpy(entry->orig, bcast_packet->orig, ETH_ALEN);
+ ether_addr_copy(entry->orig, bcast_packet->orig);
bat_priv->bla.bcast_duplist_curr = curr;
out:
@@ -1458,7 +1458,7 @@ int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
if (is_multicast_ether_addr(ethhdr->h_dest) && is_bcast)
goto handled;
- memcpy(search_claim.addr, ethhdr->h_source, ETH_ALEN);
+ ether_addr_copy(search_claim.addr, ethhdr->h_source);
search_claim.vid = vid;
claim = batadv_claim_hash_find(bat_priv, &search_claim);
@@ -1547,9 +1547,6 @@ int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
if (!atomic_read(&bat_priv->bridge_loop_avoidance))
goto allow;
- /* in VLAN case, the mac header might not be set. */
- skb_reset_mac_header(skb);
-
if (batadv_bla_process_claim(bat_priv, primary_if, skb))
goto handled;
@@ -1560,7 +1557,7 @@ int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
if (is_multicast_ether_addr(ethhdr->h_dest))
goto handled;
- memcpy(search_claim.addr, ethhdr->h_source, ETH_ALEN);
+ ether_addr_copy(search_claim.addr, ethhdr->h_source);
search_claim.vid = vid;
claim = batadv_claim_hash_find(bat_priv, &search_claim);
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
index edee50411892..b25fd64d727b 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -277,7 +277,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
/* if this entry is already known, just update it */
if (dat_entry) {
if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
- memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
+ ether_addr_copy(dat_entry->mac_addr, mac_addr);
dat_entry->last_update = jiffies;
batadv_dbg(BATADV_DBG_DAT, bat_priv,
"Entry updated: %pI4 %pM (vid: %d)\n",
@@ -292,7 +292,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
dat_entry->ip = ip;
dat_entry->vid = vid;
- memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
+ ether_addr_copy(dat_entry->mac_addr, mac_addr);
dat_entry->last_update = jiffies;
atomic_set(&dat_entry->refcount, 2);
@@ -1027,6 +1027,11 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
if (!skb_new)
goto out;
+ /* the rest of the TX path assumes that the mac_header offset pointing
+ * to the inner Ethernet header has been set, therefore reset it now.
+ */
+ skb_reset_mac_header(skb_new);
+
if (vid & BATADV_VLAN_HAS_TAG)
skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
vid & VLAN_VID_MASK);
diff --git a/net/batman-adv/distributed-arp-table.h b/net/batman-adv/distributed-arp-table.h
index ac9be9b67a25..d76e1d06c5b5 100644
--- a/net/batman-adv/distributed-arp-table.h
+++ b/net/batman-adv/distributed-arp-table.h
@@ -25,6 +25,9 @@
#include <linux/if_arp.h>
+/**
+ * BATADV_DAT_ADDR_MAX - maximum address value in the DHT space
+ */
#define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0)
void batadv_dat_status_update(struct net_device *net_dev);
diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
index 88df9b1d552d..bcc4bea632fa 100644
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -449,8 +449,8 @@ bool batadv_frag_send_packet(struct sk_buff *skb,
frag_header.reserved = 0;
frag_header.no = 0;
frag_header.total_size = htons(skb->len);
- memcpy(frag_header.orig, primary_if->net_dev->dev_addr, ETH_ALEN);
- memcpy(frag_header.dest, orig_node->orig, ETH_ALEN);
+ ether_addr_copy(frag_header.orig, primary_if->net_dev->dev_addr);
+ ether_addr_copy(frag_header.dest, orig_node->orig);
/* Eat and send fragments from the tail of skb */
while (skb->len > max_fragment_size) {
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
index 55cf2260d295..c835e137423b 100644
--- a/net/batman-adv/gateway_client.c
+++ b/net/batman-adv/gateway_client.c
@@ -389,8 +389,6 @@ out:
batadv_neigh_ifinfo_free_ref(router_gw_tq);
if (router_orig_tq)
batadv_neigh_ifinfo_free_ref(router_orig_tq);
-
- return;
}
/**
@@ -680,7 +678,7 @@ batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
if (!pskb_may_pull(skb, *header_len + ETH_HLEN))
return BATADV_DHCP_NO;
- ethhdr = (struct ethhdr *)skb->data;
+ ethhdr = eth_hdr(skb);
proto = ethhdr->h_proto;
*header_len += ETH_HLEN;
@@ -689,7 +687,7 @@ batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
if (!pskb_may_pull(skb, *header_len + VLAN_HLEN))
return BATADV_DHCP_NO;
- vhdr = (struct vlan_ethhdr *)skb->data;
+ vhdr = vlan_eth_hdr(skb);
proto = vhdr->h_vlan_encapsulated_proto;
*header_len += VLAN_HLEN;
}
@@ -728,7 +726,7 @@ batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
return BATADV_DHCP_NO;
/* skb->data might have been reallocated by pskb_may_pull() */
- ethhdr = (struct ethhdr *)skb->data;
+ ethhdr = eth_hdr(skb);
if (ntohs(ethhdr->h_proto) == ETH_P_8021Q)
ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
@@ -765,7 +763,7 @@ batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
if (*p != ETH_ALEN)
return BATADV_DHCP_NO;
- memcpy(chaddr, skb->data + chaddr_offset, ETH_ALEN);
+ ether_addr_copy(chaddr, skb->data + chaddr_offset);
}
return ret;
diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c
index abb9d6e0388b..161ef8f17d2e 100644
--- a/net/batman-adv/icmp_socket.c
+++ b/net/batman-adv/icmp_socket.c
@@ -158,6 +158,7 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
struct batadv_orig_node *orig_node = NULL;
struct batadv_neigh_node *neigh_node = NULL;
size_t packet_len = sizeof(struct batadv_icmp_packet);
+ uint8_t *addr;
if (len < sizeof(struct batadv_icmp_header)) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
@@ -227,10 +228,10 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
goto dst_unreach;
icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmp_header;
- if (packet_len == sizeof(*icmp_packet_rr))
- memcpy(icmp_packet_rr->rr,
- neigh_node->if_incoming->net_dev->dev_addr,
- ETH_ALEN);
+ if (packet_len == sizeof(*icmp_packet_rr)) {
+ addr = neigh_node->if_incoming->net_dev->dev_addr;
+ ether_addr_copy(icmp_packet_rr->rr[0], addr);
+ }
break;
default:
@@ -250,7 +251,7 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
goto free_skb;
}
- memcpy(icmp_header->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(icmp_header->orig, primary_if->net_dev->dev_addr);
batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
goto out;
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 66ae135b9f27..d1183e882167 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -34,6 +34,7 @@
#include "gateway_client.h"
#include "bridge_loop_avoidance.h"
#include "distributed-arp-table.h"
+#include "multicast.h"
#include "gateway_common.h"
#include "hash.h"
#include "bat_algo.h"
@@ -110,6 +111,9 @@ int batadv_mesh_init(struct net_device *soft_iface)
spin_lock_init(&bat_priv->tt.last_changeset_lock);
spin_lock_init(&bat_priv->tt.commit_lock);
spin_lock_init(&bat_priv->gw.list_lock);
+#ifdef CONFIG_BATMAN_ADV_MCAST
+ spin_lock_init(&bat_priv->mcast.want_lists_lock);
+#endif
spin_lock_init(&bat_priv->tvlv.container_list_lock);
spin_lock_init(&bat_priv->tvlv.handler_list_lock);
spin_lock_init(&bat_priv->softif_vlan_list_lock);
@@ -117,9 +121,17 @@ int batadv_mesh_init(struct net_device *soft_iface)
INIT_HLIST_HEAD(&bat_priv->forw_bat_list);
INIT_HLIST_HEAD(&bat_priv->forw_bcast_list);
INIT_HLIST_HEAD(&bat_priv->gw.list);
+#ifdef CONFIG_BATMAN_ADV_MCAST
+ INIT_HLIST_HEAD(&bat_priv->mcast.want_all_unsnoopables_list);
+ INIT_HLIST_HEAD(&bat_priv->mcast.want_all_ipv4_list);
+ INIT_HLIST_HEAD(&bat_priv->mcast.want_all_ipv6_list);
+#endif
INIT_LIST_HEAD(&bat_priv->tt.changes_list);
INIT_LIST_HEAD(&bat_priv->tt.req_list);
INIT_LIST_HEAD(&bat_priv->tt.roam_list);
+#ifdef CONFIG_BATMAN_ADV_MCAST
+ INIT_HLIST_HEAD(&bat_priv->mcast.mla_list);
+#endif
INIT_HLIST_HEAD(&bat_priv->tvlv.container_list);
INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list);
INIT_HLIST_HEAD(&bat_priv->softif_vlan_list);
@@ -145,6 +157,7 @@ int batadv_mesh_init(struct net_device *soft_iface)
goto err;
batadv_gw_init(bat_priv);
+ batadv_mcast_init(bat_priv);
atomic_set(&bat_priv->gw.reselect, 0);
atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE);
@@ -169,6 +182,8 @@ void batadv_mesh_free(struct net_device *soft_iface)
batadv_dat_free(bat_priv);
batadv_bla_free(bat_priv);
+ batadv_mcast_free(bat_priv);
+
/* Free the TT and the originator tables only after having terminated
* all the other depending components which may use these structures for
* their purposes.
@@ -1133,8 +1148,8 @@ void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, uint8_t *src,
unicast_tvlv_packet->reserved = 0;
unicast_tvlv_packet->tvlv_len = htons(tvlv_len);
unicast_tvlv_packet->align = 0;
- memcpy(unicast_tvlv_packet->src, src, ETH_ALEN);
- memcpy(unicast_tvlv_packet->dst, dst, ETH_ALEN);
+ ether_addr_copy(unicast_tvlv_packet->src, src);
+ ether_addr_copy(unicast_tvlv_packet->dst, dst);
tvlv_buff = (unsigned char *)(unicast_tvlv_packet + 1);
tvlv_hdr = (struct batadv_tvlv_hdr *)tvlv_buff;
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index 9374f1a51348..770dc890ceef 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 "2014.1.0"
+#define BATADV_SOURCE_VERSION "2014.2.0"
#endif
/* B.A.T.M.A.N. parameters */
@@ -176,6 +176,8 @@ enum batadv_uev_type {
#include <linux/percpu.h>
#include <linux/slab.h>
#include <net/sock.h> /* struct sock */
+#include <net/addrconf.h> /* ipv6 address stuff */
+#include <linux/ip.h>
#include <net/rtnetlink.h>
#include <linux/jiffies.h>
#include <linux/seq_file.h>
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
new file mode 100644
index 000000000000..8c7ca811de6e
--- /dev/null
+++ b/net/batman-adv/multicast.c
@@ -0,0 +1,748 @@
+/* Copyright (C) 2014 B.A.T.M.A.N. contributors:
+ *
+ * Linus Lüssing
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "main.h"
+#include "multicast.h"
+#include "originator.h"
+#include "hard-interface.h"
+#include "translation-table.h"
+#include "multicast.h"
+
+/**
+ * batadv_mcast_mla_softif_get - get softif multicast listeners
+ * @dev: the device to collect multicast addresses from
+ * @mcast_list: a list to put found addresses into
+ *
+ * Collect multicast addresses of the local multicast listeners
+ * on the given soft interface, dev, in the given mcast_list.
+ *
+ * Returns -ENOMEM on memory allocation error or the number of
+ * items added to the mcast_list otherwise.
+ */
+static int batadv_mcast_mla_softif_get(struct net_device *dev,
+ struct hlist_head *mcast_list)
+{
+ struct netdev_hw_addr *mc_list_entry;
+ struct batadv_hw_addr *new;
+ int ret = 0;
+
+ netif_addr_lock_bh(dev);
+ netdev_for_each_mc_addr(mc_list_entry, dev) {
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
+ if (!new) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ ether_addr_copy(new->addr, mc_list_entry->addr);
+ hlist_add_head(&new->list, mcast_list);
+ ret++;
+ }
+ netif_addr_unlock_bh(dev);
+
+ return ret;
+}
+
+/**
+ * batadv_mcast_mla_is_duplicate - check whether an address is in a list
+ * @mcast_addr: the multicast address to check
+ * @mcast_list: the list with multicast addresses to search in
+ *
+ * Returns true if the given address is already in the given list.
+ * Otherwise returns false.
+ */
+static bool batadv_mcast_mla_is_duplicate(uint8_t *mcast_addr,
+ struct hlist_head *mcast_list)
+{
+ struct batadv_hw_addr *mcast_entry;
+
+ hlist_for_each_entry(mcast_entry, mcast_list, list)
+ if (batadv_compare_eth(mcast_entry->addr, mcast_addr))
+ return true;
+
+ return false;
+}
+
+/**
+ * batadv_mcast_mla_list_free - free a list of multicast addresses
+ * @mcast_list: the list to free
+ *
+ * Removes and frees all items in the given mcast_list.
+ */
+static void batadv_mcast_mla_list_free(struct hlist_head *mcast_list)
+{
+ struct batadv_hw_addr *mcast_entry;
+ struct hlist_node *tmp;
+
+ hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
+ hlist_del(&mcast_entry->list);
+ kfree(mcast_entry);
+ }
+}
+
+/**
+ * batadv_mcast_mla_tt_retract - clean up multicast listener announcements
+ * @bat_priv: the bat priv with all the soft interface information
+ * @mcast_list: a list of addresses which should _not_ be removed
+ *
+ * Retracts the announcement of any multicast listener from the
+ * translation table except the ones listed in the given mcast_list.
+ *
+ * If mcast_list is NULL then all are retracted.
+ */
+static void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv,
+ struct hlist_head *mcast_list)
+{
+ struct batadv_hw_addr *mcast_entry;
+ struct hlist_node *tmp;
+
+ hlist_for_each_entry_safe(mcast_entry, tmp, &bat_priv->mcast.mla_list,
+ list) {
+ if (mcast_list &&
+ batadv_mcast_mla_is_duplicate(mcast_entry->addr,
+ mcast_list))
+ continue;
+
+ batadv_tt_local_remove(bat_priv, mcast_entry->addr,
+ BATADV_NO_FLAGS,
+ "mcast TT outdated", false);
+
+ hlist_del(&mcast_entry->list);
+ kfree(mcast_entry);
+ }
+}
+
+/**
+ * batadv_mcast_mla_tt_add - add multicast listener announcements
+ * @bat_priv: the bat priv with all the soft interface information
+ * @mcast_list: a list of addresses which are going to get added
+ *
+ * Adds multicast listener announcements from the given mcast_list to the
+ * translation table if they have not been added yet.
+ */
+static void batadv_mcast_mla_tt_add(struct batadv_priv *bat_priv,
+ struct hlist_head *mcast_list)
+{
+ struct batadv_hw_addr *mcast_entry;
+ struct hlist_node *tmp;
+
+ if (!mcast_list)
+ return;
+
+ hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
+ if (batadv_mcast_mla_is_duplicate(mcast_entry->addr,
+ &bat_priv->mcast.mla_list))
+ continue;
+
+ if (!batadv_tt_local_add(bat_priv->soft_iface,
+ mcast_entry->addr, BATADV_NO_FLAGS,
+ BATADV_NULL_IFINDEX, BATADV_NO_MARK))
+ continue;
+
+ hlist_del(&mcast_entry->list);
+ hlist_add_head(&mcast_entry->list, &bat_priv->mcast.mla_list);
+ }
+}
+
+/**
+ * batadv_mcast_has_bridge - check whether the soft-iface is bridged
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Checks whether there is a bridge on top of our soft interface. Returns
+ * true if so, false otherwise.
+ */
+static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
+{
+ struct net_device *upper = bat_priv->soft_iface;
+
+ rcu_read_lock();
+ do {
+ upper = netdev_master_upper_dev_get_rcu(upper);
+ } while (upper && !(upper->priv_flags & IFF_EBRIDGE));
+ rcu_read_unlock();
+
+ return upper;
+}
+
+/**
+ * batadv_mcast_mla_tvlv_update - update multicast tvlv
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Updates the own multicast tvlv with our current multicast related settings,
+ * capabilities and inabilities.
+ *
+ * Returns true if the tvlv container is registered afterwards. Otherwise
+ * returns false.
+ */
+static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
+{
+ struct batadv_tvlv_mcast_data mcast_data;
+
+ mcast_data.flags = BATADV_NO_FLAGS;
+ memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved));
+
+ /* Avoid attaching MLAs, if there is a bridge on top of our soft
+ * interface, we don't support that yet (TODO)
+ */
+ if (batadv_mcast_has_bridge(bat_priv)) {
+ if (bat_priv->mcast.enabled) {
+ batadv_tvlv_container_unregister(bat_priv,
+ BATADV_TVLV_MCAST, 1);
+ bat_priv->mcast.enabled = false;
+ }
+
+ return false;
+ }
+
+ if (!bat_priv->mcast.enabled ||
+ mcast_data.flags != bat_priv->mcast.flags) {
+ batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 1,
+ &mcast_data, sizeof(mcast_data));
+ bat_priv->mcast.flags = mcast_data.flags;
+ bat_priv->mcast.enabled = true;
+ }
+
+ return true;
+}
+
+/**
+ * batadv_mcast_mla_update - update the own MLAs
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Updates the own multicast listener announcements in the translation
+ * table as well as the own, announced multicast tvlv container.
+ */
+void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
+{
+ struct net_device *soft_iface = bat_priv->soft_iface;
+ struct hlist_head mcast_list = HLIST_HEAD_INIT;
+ int ret;
+
+ if (!batadv_mcast_mla_tvlv_update(bat_priv))
+ goto update;
+
+ ret = batadv_mcast_mla_softif_get(soft_iface, &mcast_list);
+ if (ret < 0)
+ goto out;
+
+update:
+ batadv_mcast_mla_tt_retract(bat_priv, &mcast_list);
+ batadv_mcast_mla_tt_add(bat_priv, &mcast_list);
+
+out:
+ batadv_mcast_mla_list_free(&mcast_list);
+}
+
+/**
+ * batadv_mcast_forw_mode_check_ipv4 - check for optimized forwarding potential
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the IPv4 packet to check
+ * @is_unsnoopable: stores whether the destination is snoopable
+ *
+ * Checks whether the given IPv4 packet has the potential to be forwarded with a
+ * mode more optimal than classic flooding.
+ *
+ * If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM in case of
+ * memory allocation failure.
+ */
+static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ bool *is_unsnoopable)
+{
+ struct iphdr *iphdr;
+
+ /* We might fail due to out-of-memory -> drop it */
+ if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*iphdr)))
+ return -ENOMEM;
+
+ iphdr = ip_hdr(skb);
+
+ /* TODO: Implement Multicast Router Discovery (RFC4286),
+ * then allow scope > link local, too
+ */
+ if (!ipv4_is_local_multicast(iphdr->daddr))
+ return -EINVAL;
+
+ /* link-local multicast listeners behind a bridge are
+ * not snoopable (see RFC4541, section 2.1.2.2)
+ */
+ *is_unsnoopable = true;
+
+ return 0;
+}
+
+/**
+ * batadv_mcast_forw_mode_check_ipv6 - check for optimized forwarding potential
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the IPv6 packet to check
+ * @is_unsnoopable: stores whether the destination is snoopable
+ *
+ * Checks whether the given IPv6 packet has the potential to be forwarded with a
+ * mode more optimal than classic flooding.
+ *
+ * If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM if we are out
+ * of memory.
+ */
+static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ bool *is_unsnoopable)
+{
+ struct ipv6hdr *ip6hdr;
+
+ /* We might fail due to out-of-memory -> drop it */
+ if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*ip6hdr)))
+ return -ENOMEM;
+
+ ip6hdr = ipv6_hdr(skb);
+
+ /* TODO: Implement Multicast Router Discovery (RFC4286),
+ * then allow scope > link local, too
+ */
+ if (IPV6_ADDR_MC_SCOPE(&ip6hdr->daddr) != IPV6_ADDR_SCOPE_LINKLOCAL)
+ return -EINVAL;
+
+ /* link-local-all-nodes multicast listeners behind a bridge are
+ * not snoopable (see RFC4541, section 3, paragraph 3)
+ */
+ if (ipv6_addr_is_ll_all_nodes(&ip6hdr->daddr))
+ *is_unsnoopable = true;
+
+ return 0;
+}
+
+/**
+ * batadv_mcast_forw_mode_check - check for optimized forwarding potential
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the multicast frame to check
+ * @is_unsnoopable: stores whether the destination is snoopable
+ *
+ * Checks whether the given multicast ethernet frame has the potential to be
+ * forwarded with a mode more optimal than classic flooding.
+ *
+ * If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM if we are out
+ * of memory.
+ */
+static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ bool *is_unsnoopable)
+{
+ struct ethhdr *ethhdr = eth_hdr(skb);
+
+ if (!atomic_read(&bat_priv->multicast_mode))
+ return -EINVAL;
+
+ if (atomic_read(&bat_priv->mcast.num_disabled))
+ return -EINVAL;
+
+ switch (ntohs(ethhdr->h_proto)) {
+ case ETH_P_IP:
+ return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb,
+ is_unsnoopable);
+ case ETH_P_IPV6:
+ return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb,
+ is_unsnoopable);
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * batadv_mcast_want_all_ip_count - count nodes with unspecific mcast interest
+ * @bat_priv: the bat priv with all the soft interface information
+ * @ethhdr: ethernet header of a packet
+ *
+ * Returns the number of nodes which want all IPv4 multicast traffic if the
+ * given ethhdr is from an IPv4 packet or the number of nodes which want all
+ * IPv6 traffic if it matches an IPv6 packet.
+ */
+static int batadv_mcast_forw_want_all_ip_count(struct batadv_priv *bat_priv,
+ struct ethhdr *ethhdr)
+{
+ switch (ntohs(ethhdr->h_proto)) {
+ case ETH_P_IP:
+ return atomic_read(&bat_priv->mcast.num_want_all_ipv4);
+ case ETH_P_IPV6:
+ return atomic_read(&bat_priv->mcast.num_want_all_ipv6);
+ default:
+ /* we shouldn't be here... */
+ return 0;
+ }
+}
+
+/**
+ * batadv_mcast_forw_tt_node_get - get a multicast tt node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @ethhdr: the ether header containing the multicast destination
+ *
+ * Returns an orig_node matching the multicast address provided by ethhdr
+ * via a translation table lookup. This increases the returned nodes refcount.
+ */
+static struct batadv_orig_node *
+batadv_mcast_forw_tt_node_get(struct batadv_priv *bat_priv,
+ struct ethhdr *ethhdr)
+{
+ return batadv_transtable_search(bat_priv, ethhdr->h_source,
+ ethhdr->h_dest, BATADV_NO_FLAGS);
+}
+
+/**
+ * batadv_mcast_want_forw_ipv4_node_get - get a node with an ipv4 flag
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 flag set and
+ * increases its refcount.
+ */
+static struct batadv_orig_node *
+batadv_mcast_forw_ipv4_node_get(struct batadv_priv *bat_priv)
+{
+ struct batadv_orig_node *tmp_orig_node, *orig_node = NULL;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(tmp_orig_node,
+ &bat_priv->mcast.want_all_ipv4_list,
+ mcast_want_all_ipv4_node) {
+ if (!atomic_inc_not_zero(&orig_node->refcount))
+ continue;
+
+ orig_node = tmp_orig_node;
+ break;
+ }
+ rcu_read_unlock();
+
+ return orig_node;
+}
+
+/**
+ * batadv_mcast_want_forw_ipv6_node_get - get a node with an ipv6 flag
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV6 flag set
+ * and increases its refcount.
+ */
+static struct batadv_orig_node *
+batadv_mcast_forw_ipv6_node_get(struct batadv_priv *bat_priv)
+{
+ struct batadv_orig_node *tmp_orig_node, *orig_node = NULL;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(tmp_orig_node,
+ &bat_priv->mcast.want_all_ipv6_list,
+ mcast_want_all_ipv6_node) {
+ if (!atomic_inc_not_zero(&orig_node->refcount))
+ continue;
+
+ orig_node = tmp_orig_node;
+ break;
+ }
+ rcu_read_unlock();
+
+ return orig_node;
+}
+
+/**
+ * batadv_mcast_want_forw_ip_node_get - get a node with an ipv4/ipv6 flag
+ * @bat_priv: the bat priv with all the soft interface information
+ * @ethhdr: an ethernet header to determine the protocol family from
+ *
+ * Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 or
+ * BATADV_MCAST_WANT_ALL_IPV6 flag, depending on the provided ethhdr, set and
+ * increases its refcount.
+ */
+static struct batadv_orig_node *
+batadv_mcast_forw_ip_node_get(struct batadv_priv *bat_priv,
+ struct ethhdr *ethhdr)
+{
+ switch (ntohs(ethhdr->h_proto)) {
+ case ETH_P_IP:
+ return batadv_mcast_forw_ipv4_node_get(bat_priv);
+ case ETH_P_IPV6:
+ return batadv_mcast_forw_ipv6_node_get(bat_priv);
+ default:
+ /* we shouldn't be here... */
+ return NULL;
+ }
+}
+
+/**
+ * batadv_mcast_want_forw_unsnoop_node_get - get a node with an unsnoopable flag
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Returns an orig_node which has the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag
+ * set and increases its refcount.
+ */
+static struct batadv_orig_node *
+batadv_mcast_forw_unsnoop_node_get(struct batadv_priv *bat_priv)
+{
+ struct batadv_orig_node *tmp_orig_node, *orig_node = NULL;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(tmp_orig_node,
+ &bat_priv->mcast.want_all_unsnoopables_list,
+ mcast_want_all_unsnoopables_node) {
+ if (!atomic_inc_not_zero(&orig_node->refcount))
+ continue;
+
+ orig_node = tmp_orig_node;
+ break;
+ }
+ rcu_read_unlock();
+
+ return orig_node;
+}
+
+/**
+ * batadv_mcast_forw_mode - check on how to forward a multicast packet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: The multicast packet to check
+ * @orig: an originator to be set to forward the skb to
+ *
+ * Returns the forwarding mode as enum batadv_forw_mode and in case of
+ * BATADV_FORW_SINGLE set the orig to the single originator the skb
+ * should be forwarded to.
+ */
+enum batadv_forw_mode
+batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
+ struct batadv_orig_node **orig)
+{
+ int ret, tt_count, ip_count, unsnoop_count, total_count;
+ bool is_unsnoopable = false;
+ struct ethhdr *ethhdr;
+
+ ret = batadv_mcast_forw_mode_check(bat_priv, skb, &is_unsnoopable);
+ if (ret == -ENOMEM)
+ return BATADV_FORW_NONE;
+ else if (ret < 0)
+ return BATADV_FORW_ALL;
+
+ ethhdr = eth_hdr(skb);
+
+ tt_count = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest,
+ BATADV_NO_FLAGS);
+ ip_count = batadv_mcast_forw_want_all_ip_count(bat_priv, ethhdr);
+ unsnoop_count = !is_unsnoopable ? 0 :
+ atomic_read(&bat_priv->mcast.num_want_all_unsnoopables);
+
+ total_count = tt_count + ip_count + unsnoop_count;
+
+ switch (total_count) {
+ case 1:
+ if (tt_count)
+ *orig = batadv_mcast_forw_tt_node_get(bat_priv, ethhdr);
+ else if (ip_count)
+ *orig = batadv_mcast_forw_ip_node_get(bat_priv, ethhdr);
+ else if (unsnoop_count)
+ *orig = batadv_mcast_forw_unsnoop_node_get(bat_priv);
+
+ if (*orig)
+ return BATADV_FORW_SINGLE;
+
+ /* fall through */
+ case 0:
+ return BATADV_FORW_NONE;
+ default:
+ return BATADV_FORW_ALL;
+ }
+}
+
+/**
+ * batadv_mcast_want_unsnoop_update - update unsnoop counter and list
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node which multicast state might have changed of
+ * @mcast_flags: flags indicating the new multicast state
+ *
+ * If the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag of this originator,
+ * orig, has toggled then this method updates counter and list accordingly.
+ */
+static void batadv_mcast_want_unsnoop_update(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig,
+ uint8_t mcast_flags)
+{
+ /* switched from flag unset to set */
+ if (mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES &&
+ !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES)) {
+ atomic_inc(&bat_priv->mcast.num_want_all_unsnoopables);
+
+ spin_lock_bh(&bat_priv->mcast.want_lists_lock);
+ hlist_add_head_rcu(&orig->mcast_want_all_unsnoopables_node,
+ &bat_priv->mcast.want_all_unsnoopables_list);
+ spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
+ /* switched from flag set to unset */
+ } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) &&
+ orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) {
+ atomic_dec(&bat_priv->mcast.num_want_all_unsnoopables);
+
+ spin_lock_bh(&bat_priv->mcast.want_lists_lock);
+ hlist_del_rcu(&orig->mcast_want_all_unsnoopables_node);
+ spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
+ }
+}
+
+/**
+ * batadv_mcast_want_ipv4_update - update want-all-ipv4 counter and list
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node which multicast state might have changed of
+ * @mcast_flags: flags indicating the new multicast state
+ *
+ * If the BATADV_MCAST_WANT_ALL_IPV4 flag of this originator, orig, has
+ * toggled then this method updates counter and list accordingly.
+ */
+static void batadv_mcast_want_ipv4_update(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig,
+ uint8_t mcast_flags)
+{
+ /* switched from flag unset to set */
+ if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV4 &&
+ !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4)) {
+ atomic_inc(&bat_priv->mcast.num_want_all_ipv4);
+
+ spin_lock_bh(&bat_priv->mcast.want_lists_lock);
+ hlist_add_head_rcu(&orig->mcast_want_all_ipv4_node,
+ &bat_priv->mcast.want_all_ipv4_list);
+ spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
+ /* switched from flag set to unset */
+ } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) &&
+ orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) {
+ atomic_dec(&bat_priv->mcast.num_want_all_ipv4);
+
+ spin_lock_bh(&bat_priv->mcast.want_lists_lock);
+ hlist_del_rcu(&orig->mcast_want_all_ipv4_node);
+ spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
+ }
+}
+
+/**
+ * batadv_mcast_want_ipv6_update - update want-all-ipv6 counter and list
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node which multicast state might have changed of
+ * @mcast_flags: flags indicating the new multicast state
+ *
+ * If the BATADV_MCAST_WANT_ALL_IPV6 flag of this originator, orig, has
+ * toggled then this method updates counter and list accordingly.
+ */
+static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig,
+ uint8_t mcast_flags)
+{
+ /* switched from flag unset to set */
+ if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV6 &&
+ !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6)) {
+ atomic_inc(&bat_priv->mcast.num_want_all_ipv6);
+
+ spin_lock_bh(&bat_priv->mcast.want_lists_lock);
+ hlist_add_head_rcu(&orig->mcast_want_all_ipv6_node,
+ &bat_priv->mcast.want_all_ipv6_list);
+ spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
+ /* switched from flag set to unset */
+ } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) &&
+ orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) {
+ atomic_dec(&bat_priv->mcast.num_want_all_ipv6);
+
+ spin_lock_bh(&bat_priv->mcast.want_lists_lock);
+ hlist_del_rcu(&orig->mcast_want_all_ipv6_node);
+ spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
+ }
+}
+
+/**
+ * batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node of the ogm
+ * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
+ * @tvlv_value: tvlv buffer containing the multicast data
+ * @tvlv_value_len: tvlv buffer length
+ */
+static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig,
+ uint8_t flags,
+ void *tvlv_value,
+ uint16_t tvlv_value_len)
+{
+ bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+ uint8_t mcast_flags = BATADV_NO_FLAGS;
+ bool orig_initialized;
+
+ orig_initialized = orig->capa_initialized & BATADV_ORIG_CAPA_HAS_MCAST;
+
+ /* If mcast support is turned on decrease the disabled mcast node
+ * counter only if we had increased it for this node before. If this
+ * is a completely new orig_node no need to decrease the counter.
+ */
+ if (orig_mcast_enabled &&
+ !(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST)) {
+ if (orig_initialized)
+ atomic_dec(&bat_priv->mcast.num_disabled);
+ orig->capabilities |= BATADV_ORIG_CAPA_HAS_MCAST;
+ /* If mcast support is being switched off increase the disabled
+ * mcast node counter.
+ */
+ } else if (!orig_mcast_enabled &&
+ orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST) {
+ atomic_inc(&bat_priv->mcast.num_disabled);
+ orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_MCAST;
+ }
+
+ orig->capa_initialized |= BATADV_ORIG_CAPA_HAS_MCAST;
+
+ if (orig_mcast_enabled && tvlv_value &&
+ (tvlv_value_len >= sizeof(mcast_flags)))
+ mcast_flags = *(uint8_t *)tvlv_value;
+
+ batadv_mcast_want_unsnoop_update(bat_priv, orig, mcast_flags);
+ batadv_mcast_want_ipv4_update(bat_priv, orig, mcast_flags);
+ batadv_mcast_want_ipv6_update(bat_priv, orig, mcast_flags);
+
+ orig->mcast_flags = mcast_flags;
+}
+
+/**
+ * batadv_mcast_init - initialize the multicast optimizations structures
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_mcast_init(struct batadv_priv *bat_priv)
+{
+ batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler_v1,
+ NULL, BATADV_TVLV_MCAST, 1,
+ BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+}
+
+/**
+ * batadv_mcast_free - free the multicast optimizations structures
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_mcast_free(struct batadv_priv *bat_priv)
+{
+ batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 1);
+ batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 1);
+
+ batadv_mcast_mla_tt_retract(bat_priv, NULL);
+}
+
+/**
+ * batadv_mcast_purge_orig - reset originator global mcast state modifications
+ * @orig: the originator which is going to get purged
+ */
+void batadv_mcast_purge_orig(struct batadv_orig_node *orig)
+{
+ struct batadv_priv *bat_priv = orig->bat_priv;
+
+ if (!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST))
+ atomic_dec(&bat_priv->mcast.num_disabled);
+
+ batadv_mcast_want_unsnoop_update(bat_priv, orig, BATADV_NO_FLAGS);
+ batadv_mcast_want_ipv4_update(bat_priv, orig, BATADV_NO_FLAGS);
+ batadv_mcast_want_ipv6_update(bat_priv, orig, BATADV_NO_FLAGS);
+}
diff --git a/net/batman-adv/multicast.h b/net/batman-adv/multicast.h
new file mode 100644
index 000000000000..73b5d45819c1
--- /dev/null
+++ b/net/batman-adv/multicast.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 2014 B.A.T.M.A.N. contributors:
+ *
+ * Linus Lüssing
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NET_BATMAN_ADV_MULTICAST_H_
+#define _NET_BATMAN_ADV_MULTICAST_H_
+
+/**
+ * batadv_forw_mode - the way a packet should be forwarded as
+ * @BATADV_FORW_ALL: forward the packet to all nodes (currently via classic
+ * flooding)
+ * @BATADV_FORW_SINGLE: forward the packet to a single node (currently via the
+ * BATMAN unicast routing protocol)
+ * @BATADV_FORW_NONE: don't forward, drop it
+ */
+enum batadv_forw_mode {
+ BATADV_FORW_ALL,
+ BATADV_FORW_SINGLE,
+ BATADV_FORW_NONE,
+};
+
+#ifdef CONFIG_BATMAN_ADV_MCAST
+
+void batadv_mcast_mla_update(struct batadv_priv *bat_priv);
+
+enum batadv_forw_mode
+batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
+ struct batadv_orig_node **mcast_single_orig);
+
+void batadv_mcast_init(struct batadv_priv *bat_priv);
+
+void batadv_mcast_free(struct batadv_priv *bat_priv);
+
+void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node);
+
+#else
+
+static inline void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
+{
+ return;
+}
+
+static inline enum batadv_forw_mode
+batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
+ struct batadv_orig_node **mcast_single_orig)
+{
+ return BATADV_FORW_ALL;
+}
+
+static inline int batadv_mcast_init(struct batadv_priv *bat_priv)
+{
+ return 0;
+}
+
+static inline void batadv_mcast_free(struct batadv_priv *bat_priv)
+{
+ return;
+}
+
+static inline void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node)
+{
+ return;
+}
+
+#endif /* CONFIG_BATMAN_ADV_MCAST */
+
+#endif /* _NET_BATMAN_ADV_MULTICAST_H_ */
diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c
index f1b604d88dc3..a9546fe541eb 100644
--- a/net/batman-adv/network-coding.c
+++ b/net/batman-adv/network-coding.c
@@ -819,7 +819,7 @@ static struct batadv_nc_node
/* Initialize nc_node */
INIT_LIST_HEAD(&nc_node->list);
- memcpy(nc_node->addr, orig_node->orig, ETH_ALEN);
+ ether_addr_copy(nc_node->addr, orig_node->orig);
nc_node->orig_node = orig_neigh_node;
atomic_set(&nc_node->refcount, 2);
@@ -941,8 +941,8 @@ static struct batadv_nc_path *batadv_nc_get_path(struct batadv_priv *bat_priv,
spin_lock_init(&nc_path->packet_list_lock);
atomic_set(&nc_path->refcount, 2);
nc_path->last_valid = jiffies;
- memcpy(nc_path->next_hop, dst, ETH_ALEN);
- memcpy(nc_path->prev_hop, src, ETH_ALEN);
+ ether_addr_copy(nc_path->next_hop, dst);
+ ether_addr_copy(nc_path->prev_hop, src);
batadv_dbg(BATADV_DBG_NC, bat_priv, "Adding nc_path %pM -> %pM\n",
nc_path->prev_hop,
@@ -1114,15 +1114,15 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
coded_packet->ttl = packet1->ttl;
/* Info about first unicast packet */
- memcpy(coded_packet->first_source, first_source, ETH_ALEN);
- memcpy(coded_packet->first_orig_dest, packet1->dest, ETH_ALEN);
+ ether_addr_copy(coded_packet->first_source, first_source);
+ ether_addr_copy(coded_packet->first_orig_dest, packet1->dest);
coded_packet->first_crc = packet_id1;
coded_packet->first_ttvn = packet1->ttvn;
/* Info about second unicast packet */
- memcpy(coded_packet->second_dest, second_dest, ETH_ALEN);
- memcpy(coded_packet->second_source, second_source, ETH_ALEN);
- memcpy(coded_packet->second_orig_dest, packet2->dest, ETH_ALEN);
+ ether_addr_copy(coded_packet->second_dest, second_dest);
+ ether_addr_copy(coded_packet->second_source, second_source);
+ ether_addr_copy(coded_packet->second_orig_dest, packet2->dest);
coded_packet->second_crc = packet_id2;
coded_packet->second_ttl = packet2->ttl;
coded_packet->second_ttvn = packet2->ttvn;
@@ -1349,8 +1349,8 @@ static void batadv_nc_skb_store_before_coding(struct batadv_priv *bat_priv,
/* Set the mac header as if we actually sent the packet uncoded */
ethhdr = eth_hdr(skb);
- memcpy(ethhdr->h_source, ethhdr->h_dest, ETH_ALEN);
- memcpy(ethhdr->h_dest, eth_dst_new, ETH_ALEN);
+ ether_addr_copy(ethhdr->h_source, ethhdr->h_dest);
+ ether_addr_copy(ethhdr->h_dest, eth_dst_new);
/* Set data pointer to MAC header to mimic packets from our tx path */
skb_push(skb, ETH_HLEN);
@@ -1636,7 +1636,7 @@ batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb,
/* Reconstruct original mac header */
ethhdr = eth_hdr(skb);
- memcpy(ethhdr, &ethhdr_tmp, sizeof(*ethhdr));
+ *ethhdr = ethhdr_tmp;
/* Select the correct unicast header information based on the location
* of our mac address in the coded_packet header
@@ -1646,7 +1646,7 @@ batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb,
* so the Ethernet address must be copied to h_dest and
* pkt_type changed from PACKET_OTHERHOST to PACKET_HOST
*/
- memcpy(ethhdr->h_dest, coded_packet_tmp.second_dest, ETH_ALEN);
+ ether_addr_copy(ethhdr->h_dest, coded_packet_tmp.second_dest);
skb->pkt_type = PACKET_HOST;
orig_dest = coded_packet_tmp.second_orig_dest;
@@ -1682,7 +1682,7 @@ batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb,
unicast_packet->packet_type = BATADV_UNICAST;
unicast_packet->version = BATADV_COMPAT_VERSION;
unicast_packet->ttl = ttl;
- memcpy(unicast_packet->dest, orig_dest, ETH_ALEN);
+ ether_addr_copy(unicast_packet->dest, orig_dest);
unicast_packet->ttvn = ttvn;
batadv_nc_packet_free(nc_packet);
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 853941629dc1..ffd9dfbd9b0e 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -27,6 +27,7 @@
#include "bridge_loop_avoidance.h"
#include "network-coding.h"
#include "fragmentation.h"
+#include "multicast.h"
/* hash class keys */
static struct lock_class_key batadv_orig_hash_lock_class_key;
@@ -446,7 +447,7 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
INIT_HLIST_HEAD(&neigh_node->ifinfo_list);
spin_lock_init(&neigh_node->ifinfo_lock);
- memcpy(neigh_node->addr, neigh_addr, ETH_ALEN);
+ ether_addr_copy(neigh_node->addr, neigh_addr);
neigh_node->if_incoming = hard_iface;
neigh_node->orig_node = orig_node;
@@ -557,6 +558,8 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
}
spin_unlock_bh(&orig_node->neigh_list_lock);
+ batadv_mcast_purge_orig(orig_node);
+
/* Free nc_nodes */
batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL);
@@ -664,15 +667,17 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
/* extra reference for return */
atomic_set(&orig_node->refcount, 2);
- orig_node->tt_initialised = false;
orig_node->bat_priv = bat_priv;
- memcpy(orig_node->orig, addr, ETH_ALEN);
+ ether_addr_copy(orig_node->orig, addr);
batadv_dat_init_orig_node_addr(orig_node);
atomic_set(&orig_node->last_ttvn, 0);
orig_node->tt_buff = NULL;
orig_node->tt_buff_len = 0;
reset_time = jiffies - 1 - msecs_to_jiffies(BATADV_RESET_PROTECTION_MS);
orig_node->bcast_seqno_reset = reset_time;
+#ifdef CONFIG_BATMAN_ADV_MCAST
+ orig_node->mcast_flags = BATADV_NO_FLAGS;
+#endif
/* create a vlan object for the "untagged" LAN */
vlan = batadv_orig_node_vlan_new(orig_node, BATADV_NO_FLAGS);
diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h
index 0a381d1174c1..34e096d2dce1 100644
--- a/net/batman-adv/packet.h
+++ b/net/batman-adv/packet.h
@@ -89,6 +89,19 @@ enum batadv_icmp_packettype {
BATADV_PARAMETER_PROBLEM = 12,
};
+/**
+ * enum batadv_mcast_flags - flags for multicast capabilities and settings
+ * @BATADV_MCAST_WANT_ALL_UNSNOOPABLES: we want all packets destined for
+ * 224.0.0.0/24 or ff02::1
+ * @BATADV_MCAST_WANT_ALL_IPV4: we want all IPv4 multicast packets
+ * @BATADV_MCAST_WANT_ALL_IPV6: we want all IPv6 multicast packets
+ */
+enum batadv_mcast_flags {
+ BATADV_MCAST_WANT_ALL_UNSNOOPABLES = BIT(0),
+ BATADV_MCAST_WANT_ALL_IPV4 = BIT(1),
+ BATADV_MCAST_WANT_ALL_IPV6 = BIT(2),
+};
+
/* tt data subtypes */
#define BATADV_TT_DATA_TYPE_MASK 0x0F
@@ -106,10 +119,30 @@ enum batadv_tt_data_flags {
BATADV_TT_FULL_TABLE = BIT(4),
};
-/* BATADV_TT_CLIENT flags.
- * Flags from BIT(0) to BIT(7) are sent on the wire, while flags from BIT(8) to
- * BIT(15) are used for local computation only.
- * Flags from BIT(4) to BIT(7) are kept in sync with the rest of the network.
+/**
+ * enum batadv_tt_client_flags - TT client specific flags
+ * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table
+ * @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and the new
+ * update telling its new real location has not been received/sent yet
+ * @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi interface.
+ * This information is used by the "AP Isolation" feature
+ * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This
+ * information is used by the Extended Isolation feature
+ * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from the table
+ * @BATADV_TT_CLIENT_NEW: this client has been added to the local table but has
+ * not been announced yet
+ * @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it is kept
+ * in the table for one more originator interval for consistency purposes
+ * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be part of
+ * the network but no nnode has already announced it
+ *
+ * Bits from 0 to 7 are called _remote flags_ because they are sent on the wire.
+ * Bits from 8 to 15 are called _local flags_ because they are used for local
+ * computations only.
+ *
+ * Bits from 4 to 7 - a subset of remote flags - are ensured to be in sync with
+ * the other nodes in the network. To achieve this goal these flags are included
+ * in the TT CRC computation.
*/
enum batadv_tt_client_flags {
BATADV_TT_CLIENT_DEL = BIT(0),
@@ -145,6 +178,7 @@ enum batadv_bla_claimframe {
* @BATADV_TVLV_NC: network coding tvlv
* @BATADV_TVLV_TT: translation table tvlv
* @BATADV_TVLV_ROAM: roaming advertisement tvlv
+ * @BATADV_TVLV_MCAST: multicast capability tvlv
*/
enum batadv_tvlv_type {
BATADV_TVLV_GW = 0x01,
@@ -152,6 +186,7 @@ enum batadv_tvlv_type {
BATADV_TVLV_NC = 0x03,
BATADV_TVLV_TT = 0x04,
BATADV_TVLV_ROAM = 0x05,
+ BATADV_TVLV_MCAST = 0x06,
};
#pragma pack(2)
@@ -504,4 +539,14 @@ struct batadv_tvlv_roam_adv {
__be16 vid;
};
+/**
+ * struct batadv_tvlv_mcast_data - payload of a multicast tvlv
+ * @flags: multicast flags announced by the orig node
+ * @reserved: reserved field
+ */
+struct batadv_tvlv_mcast_data {
+ uint8_t flags;
+ uint8_t reserved[3];
+};
+
#endif /* _NET_BATMAN_ADV_PACKET_H_ */
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index a953d5b196a3..35141534938e 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -222,8 +222,8 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
icmph = (struct batadv_icmp_header *)skb->data;
- memcpy(icmph->dst, icmph->orig, ETH_ALEN);
- memcpy(icmph->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(icmph->dst, icmph->orig);
+ ether_addr_copy(icmph->orig, primary_if->net_dev->dev_addr);
icmph->msg_type = BATADV_ECHO_REPLY;
icmph->ttl = BATADV_TTL;
@@ -276,9 +276,8 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
icmp_packet = (struct batadv_icmp_packet *)skb->data;
- memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
- memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr,
- ETH_ALEN);
+ ether_addr_copy(icmp_packet->dst, icmp_packet->orig);
+ ether_addr_copy(icmp_packet->orig, primary_if->net_dev->dev_addr);
icmp_packet->msg_type = BATADV_TTL_EXCEEDED;
icmp_packet->ttl = BATADV_TTL;
@@ -341,8 +340,8 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
if (icmp_packet_rr->rr_cur >= BATADV_RR_LEN)
goto out;
- memcpy(&(icmp_packet_rr->rr[icmp_packet_rr->rr_cur]),
- ethhdr->h_dest, ETH_ALEN);
+ ether_addr_copy(icmp_packet_rr->rr[icmp_packet_rr->rr_cur],
+ ethhdr->h_dest);
icmp_packet_rr->rr_cur++;
}
@@ -664,7 +663,7 @@ batadv_reroute_unicast_packet(struct batadv_priv *bat_priv,
}
/* update the packet header */
- memcpy(unicast_packet->dest, orig_addr, ETH_ALEN);
+ ether_addr_copy(unicast_packet->dest, orig_addr);
unicast_packet->ttvn = orig_ttvn;
ret = true;
@@ -774,7 +773,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
if (!primary_if)
return 0;
- memcpy(unicast_packet->dest, primary_if->net_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(unicast_packet->dest, primary_if->net_dev->dev_addr);
batadv_hardif_free_ref(primary_if);
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index 843febd1e519..3d64ed20c393 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -27,6 +27,7 @@
#include "originator.h"
#include "network-coding.h"
#include "fragmentation.h"
+#include "multicast.h"
static void batadv_send_outstanding_bcast_packet(struct work_struct *work);
@@ -59,8 +60,8 @@ int batadv_send_skb_packet(struct sk_buff *skb,
skb_reset_mac_header(skb);
ethhdr = eth_hdr(skb);
- memcpy(ethhdr->h_source, hard_iface->net_dev->dev_addr, ETH_ALEN);
- memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
+ ether_addr_copy(ethhdr->h_source, hard_iface->net_dev->dev_addr);
+ ether_addr_copy(ethhdr->h_dest, dst_addr);
ethhdr->h_proto = htons(ETH_P_BATMAN);
skb_set_network_header(skb, ETH_HLEN);
@@ -165,7 +166,7 @@ batadv_send_skb_push_fill_unicast(struct sk_buff *skb, int hdr_size,
/* set unicast ttl */
unicast_packet->ttl = BATADV_TTL;
/* copy the destination for faster routing */
- memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
+ ether_addr_copy(unicast_packet->dest, orig_node->orig);
/* set the destination tt version number */
unicast_packet->ttvn = ttvn;
@@ -220,7 +221,7 @@ bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
uc_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
uc_4addr_packet->u.packet_type = BATADV_UNICAST_4ADDR;
- memcpy(uc_4addr_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(uc_4addr_packet->src, primary_if->net_dev->dev_addr);
uc_4addr_packet->subtype = packet_subtype;
uc_4addr_packet->reserved = 0;
@@ -248,15 +249,15 @@ out:
*
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
*/
-static int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
- struct sk_buff *skb, int packet_type,
- int packet_subtype,
- struct batadv_orig_node *orig_node,
- unsigned short vid)
+int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int packet_type,
+ int packet_subtype,
+ struct batadv_orig_node *orig_node,
+ unsigned short vid)
{
struct ethhdr *ethhdr;
struct batadv_unicast_packet *unicast_packet;
- int ret = NET_XMIT_DROP, hdr_size;
+ int ret = NET_XMIT_DROP;
if (!orig_node)
goto out;
@@ -265,16 +266,12 @@ static int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
case BATADV_UNICAST:
if (!batadv_send_skb_prepare_unicast(skb, orig_node))
goto out;
-
- hdr_size = sizeof(*unicast_packet);
break;
case BATADV_UNICAST_4ADDR:
if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, skb,
orig_node,
packet_subtype))
goto out;
-
- hdr_size = sizeof(struct batadv_unicast_4addr_packet);
break;
default:
/* this function supports UNICAST and UNICAST_4ADDR only. It
@@ -283,7 +280,10 @@ static int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
goto out;
}
- ethhdr = (struct ethhdr *)(skb->data + hdr_size);
+ /* skb->data might have been reallocated by
+ * batadv_send_skb_prepare_unicast{,_4addr}()
+ */
+ ethhdr = eth_hdr(skb);
unicast_packet = (struct batadv_unicast_packet *)skb->data;
/* inform the destination node that we are still missing a correct route
@@ -312,6 +312,7 @@ out:
* @packet_type: the batman unicast packet type to use
* @packet_subtype: the unicast 4addr packet subtype (only relevant for unicast
* 4addr packets)
+ * @dst_hint: can be used to override the destination contained in the skb
* @vid: the vid to be used to search the translation table
*
* Look up the recipient node for the destination address in the ethernet
diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h
index aaddaa9661ce..38d0ec1833ae 100644
--- a/net/batman-adv/send.h
+++ b/net/batman-adv/send.h
@@ -36,6 +36,11 @@ bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
struct sk_buff *skb,
struct batadv_orig_node *orig_node,
int packet_subtype);
+int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int packet_type,
+ int packet_subtype,
+ struct batadv_orig_node *orig_node,
+ unsigned short vid);
int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
struct sk_buff *skb, int packet_type,
int packet_subtype, uint8_t *dst_hint,
@@ -47,6 +52,7 @@ int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
* batadv_send_skb_via_tt - send an skb via TT lookup
* @bat_priv: the bat priv with all the soft interface information
* @skb: the payload to send
+ * @dst_hint: can be used to override the destination contained in the skb
* @vid: the vid to be used to search the translation table
*
* Look up the recipient node for the destination address in the ethernet
@@ -68,6 +74,7 @@ static inline int batadv_send_skb_via_tt(struct batadv_priv *bat_priv,
* @bat_priv: the bat priv with all the soft interface information
* @skb: the payload to send
* @packet_subtype: the unicast 4addr packet subtype to use
+ * @dst_hint: can be used to override the destination contained in the skb
* @vid: the vid to be used to search the translation table
*
* Look up the recipient node for the destination address in the ethernet
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index f82c267e1886..744a59b85e15 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -32,6 +32,7 @@
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
+#include "multicast.h"
#include "bridge_loop_avoidance.h"
#include "network-coding.h"
@@ -111,8 +112,8 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
- memcpy(old_addr, dev->dev_addr, ETH_ALEN);
- memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+ ether_addr_copy(old_addr, dev->dev_addr);
+ ether_addr_copy(dev->dev_addr, addr->sa_data);
/* only modify transtable if it has been initialized before */
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE) {
@@ -170,17 +171,19 @@ static int batadv_interface_tx(struct sk_buff *skb,
unsigned short vid;
uint32_t seqno;
int gw_mode;
+ enum batadv_forw_mode forw_mode;
+ struct batadv_orig_node *mcast_single_orig = NULL;
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
goto dropped;
soft_iface->trans_start = jiffies;
vid = batadv_get_vid(skb, 0);
- ethhdr = (struct ethhdr *)skb->data;
+ ethhdr = eth_hdr(skb);
switch (ntohs(ethhdr->h_proto)) {
case ETH_P_8021Q:
- vhdr = (struct vlan_ethhdr *)skb->data;
+ vhdr = vlan_eth_hdr(skb);
if (vhdr->h_vlan_encapsulated_proto != ethertype)
break;
@@ -194,7 +197,7 @@ static int batadv_interface_tx(struct sk_buff *skb,
goto dropped;
/* skb->data might have been reallocated by batadv_bla_tx() */
- ethhdr = (struct ethhdr *)skb->data;
+ ethhdr = eth_hdr(skb);
/* Register the client MAC in the transtable */
if (!is_multicast_ether_addr(ethhdr->h_source)) {
@@ -230,7 +233,7 @@ static int batadv_interface_tx(struct sk_buff *skb,
/* skb->data may have been modified by
* batadv_gw_dhcp_recipient_get()
*/
- ethhdr = (struct ethhdr *)skb->data;
+ ethhdr = eth_hdr(skb);
/* if gw_mode is on, broadcast any non-DHCP message.
* All the DHCP packets are going to be sent as unicast
*/
@@ -247,9 +250,19 @@ static int batadv_interface_tx(struct sk_buff *skb,
* directed to a DHCP server
*/
goto dropped;
- }
send:
+ if (do_bcast && !is_broadcast_ether_addr(ethhdr->h_dest)) {
+ forw_mode = batadv_mcast_forw_mode(bat_priv, skb,
+ &mcast_single_orig);
+ if (forw_mode == BATADV_FORW_NONE)
+ goto dropped;
+
+ if (forw_mode == BATADV_FORW_SINGLE)
+ do_bcast = false;
+ }
+ }
+
batadv_skb_set_priority(skb, 0);
/* ethernet packet should be broadcasted */
@@ -279,8 +292,8 @@ send:
/* hw address of first interface is the orig mac because only
* this mac is known throughout the mesh
*/
- memcpy(bcast_packet->orig,
- primary_if->net_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(bcast_packet->orig,
+ primary_if->net_dev->dev_addr);
/* set broadcast sequence number */
seqno = atomic_inc_return(&bat_priv->bcast_seqno);
@@ -301,6 +314,10 @@ send:
if (ret)
goto dropped;
ret = batadv_send_skb_via_gw(bat_priv, skb, vid);
+ } else if (mcast_single_orig) {
+ ret = batadv_send_skb_unicast(bat_priv, skb,
+ BATADV_UNICAST, 0,
+ mcast_single_orig, vid);
} else {
if (batadv_dat_snoop_outgoing_arp_request(bat_priv,
skb))
@@ -652,10 +669,7 @@ static void batadv_softif_destroy_finish(struct work_struct *work)
}
batadv_sysfs_del_meshif(soft_iface);
-
- rtnl_lock();
- unregister_netdevice(soft_iface);
- rtnl_unlock();
+ unregister_netdev(soft_iface);
}
/**
@@ -692,6 +706,14 @@ static int batadv_softif_init_late(struct net_device *dev)
#ifdef CONFIG_BATMAN_ADV_DAT
atomic_set(&bat_priv->distributed_arp_table, 1);
#endif
+#ifdef CONFIG_BATMAN_ADV_MCAST
+ bat_priv->mcast.flags = BATADV_NO_FLAGS;
+ atomic_set(&bat_priv->multicast_mode, 1);
+ atomic_set(&bat_priv->mcast.num_disabled, 0);
+ atomic_set(&bat_priv->mcast.num_want_all_unsnoopables, 0);
+ atomic_set(&bat_priv->mcast.num_want_all_ipv4, 0);
+ atomic_set(&bat_priv->mcast.num_want_all_ipv6, 0);
+#endif
atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF);
atomic_set(&bat_priv->gw_sel_class, 20);
atomic_set(&bat_priv->gw.bandwidth_down, 100);
diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c
index e456bf6bb284..1ebb0d9e2ea5 100644
--- a/net/batman-adv/sysfs.c
+++ b/net/batman-adv/sysfs.c
@@ -539,6 +539,9 @@ BATADV_ATTR_SIF_UINT(gw_sel_class, S_IRUGO | S_IWUSR, 1, BATADV_TQ_MAX_VALUE,
batadv_post_gw_reselect);
static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth,
batadv_store_gw_bwidth);
+#ifdef CONFIG_BATMAN_ADV_MCAST
+BATADV_ATTR_SIF_BOOL(multicast_mode, S_IRUGO | S_IWUSR, NULL);
+#endif
#ifdef CONFIG_BATMAN_ADV_DEBUG
BATADV_ATTR_SIF_UINT(log_level, S_IRUGO | S_IWUSR, 0, BATADV_DBG_ALL, NULL);
#endif
@@ -558,6 +561,9 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
#ifdef CONFIG_BATMAN_ADV_DAT
&batadv_attr_distributed_arp_table,
#endif
+#ifdef CONFIG_BATMAN_ADV_MCAST
+ &batadv_attr_multicast_mode,
+#endif
&batadv_attr_fragmentation,
&batadv_attr_routing_algo,
&batadv_attr_gw_mode,
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 959dde721c46..d636bde72c9a 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -24,6 +24,7 @@
#include "originator.h"
#include "routing.h"
#include "bridge_loop_avoidance.h"
+#include "multicast.h"
#include <linux/crc32c.h>
@@ -96,7 +97,7 @@ batadv_tt_hash_find(struct batadv_hashtable *hash, const uint8_t *addr,
if (!hash)
return NULL;
- memcpy(to_search.addr, addr, ETH_ALEN);
+ ether_addr_copy(to_search.addr, addr);
to_search.vid = vid;
index = batadv_choose_tt(&to_search, hash->size);
@@ -192,6 +193,31 @@ batadv_tt_global_entry_free_ref(struct batadv_tt_global_entry *tt_global_entry)
}
}
+/**
+ * batadv_tt_global_hash_count - count the number of orig entries
+ * @hash: hash table containing the tt entries
+ * @addr: the mac address of the client to count entries for
+ * @vid: VLAN identifier
+ *
+ * Return the number of originators advertising the given address/data
+ * (excluding ourself).
+ */
+int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
+ const uint8_t *addr, unsigned short vid)
+{
+ struct batadv_tt_global_entry *tt_global_entry;
+ int count;
+
+ tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
+ if (!tt_global_entry)
+ return 0;
+
+ count = atomic_read(&tt_global_entry->orig_list_count);
+ batadv_tt_global_entry_free_ref(tt_global_entry);
+
+ return count;
+}
+
static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu)
{
struct batadv_tt_orig_list_entry *orig_entry;
@@ -333,7 +359,7 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
tt_change_node->change.flags = flags;
memset(tt_change_node->change.reserved, 0,
sizeof(tt_change_node->change.reserved));
- memcpy(tt_change_node->change.addr, common->addr, ETH_ALEN);
+ ether_addr_copy(tt_change_node->change.addr, common->addr);
tt_change_node->change.vid = htons(common->vid);
del_op_requested = flags & BATADV_TT_CLIENT_DEL;
@@ -484,7 +510,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
{
struct batadv_priv *bat_priv = netdev_priv(soft_iface);
struct batadv_tt_local_entry *tt_local;
- struct batadv_tt_global_entry *tt_global;
+ struct batadv_tt_global_entry *tt_global = NULL;
struct net_device *in_dev = NULL;
struct hlist_head *head;
struct batadv_tt_orig_list_entry *orig_entry;
@@ -497,7 +523,9 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
in_dev = dev_get_by_index(&init_net, ifindex);
tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid);
- tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid);
+
+ if (!is_multicast_ether_addr(addr))
+ tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid);
if (tt_local) {
tt_local->last_seen = jiffies;
@@ -549,7 +577,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
addr, BATADV_PRINT_VID(vid),
(uint8_t)atomic_read(&bat_priv->tt.vn));
- memcpy(tt_local->common.addr, addr, ETH_ALEN);
+ ether_addr_copy(tt_local->common.addr, addr);
/* The local entry has to be marked as NEW to avoid to send it in
* a full table response going out before the next ttvn increment
* (consistency check)
@@ -562,8 +590,11 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
tt_local->last_seen = jiffies;
tt_local->common.added_at = tt_local->last_seen;
- /* the batman interface mac address should never be purged */
- if (batadv_compare_eth(addr, soft_iface->dev_addr))
+ /* the batman interface mac and multicast addresses should never be
+ * purged
+ */
+ if (batadv_compare_eth(addr, soft_iface->dev_addr) ||
+ is_multicast_ether_addr(addr))
tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE;
hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt,
@@ -1219,6 +1250,8 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
hlist_add_head_rcu(&orig_entry->list,
&tt_global->orig_list);
spin_unlock_bh(&tt_global->list_lock);
+ atomic_inc(&tt_global->orig_list_count);
+
out:
if (orig_entry)
batadv_tt_orig_list_entry_free_ref(orig_entry);
@@ -1277,7 +1310,7 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
goto out;
common = &tt_global_entry->common;
- memcpy(common->addr, tt_addr, ETH_ALEN);
+ ether_addr_copy(common->addr, tt_addr);
common->vid = vid;
common->flags = flags;
@@ -1292,6 +1325,7 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
common->added_at = jiffies;
INIT_HLIST_HEAD(&tt_global_entry->orig_list);
+ atomic_set(&tt_global_entry->orig_list_count, 0);
spin_lock_init(&tt_global_entry->list_lock);
hash_added = batadv_hash_add(bat_priv->tt.global_hash,
@@ -1361,6 +1395,11 @@ add_orig_entry:
ret = true;
out_remove:
+ /* Do not remove multicast addresses from the local hash on
+ * global additions
+ */
+ if (is_multicast_ether_addr(tt_addr))
+ goto out;
/* remove address from local hash if present */
local_flags = batadv_tt_local_remove(bat_priv, tt_addr, vid,
@@ -1552,6 +1591,25 @@ out:
return 0;
}
+/**
+ * batadv_tt_global_del_orig_entry - remove and free an orig_entry
+ * @tt_global_entry: the global entry to remove the orig_entry from
+ * @orig_entry: the orig entry to remove and free
+ *
+ * Remove an orig_entry from its list in the given tt_global_entry and
+ * free this orig_entry afterwards.
+ */
+static void
+batadv_tt_global_del_orig_entry(struct batadv_tt_global_entry *tt_global_entry,
+ struct batadv_tt_orig_list_entry *orig_entry)
+{
+ batadv_tt_global_size_dec(orig_entry->orig_node,
+ tt_global_entry->common.vid);
+ atomic_dec(&tt_global_entry->orig_list_count);
+ hlist_del_rcu(&orig_entry->list);
+ batadv_tt_orig_list_entry_free_ref(orig_entry);
+}
+
/* deletes the orig list of a tt_global_entry */
static void
batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry)
@@ -1562,20 +1620,26 @@ batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry)
spin_lock_bh(&tt_global_entry->list_lock);
head = &tt_global_entry->orig_list;
- hlist_for_each_entry_safe(orig_entry, safe, head, list) {
- hlist_del_rcu(&orig_entry->list);
- batadv_tt_global_size_dec(orig_entry->orig_node,
- tt_global_entry->common.vid);
- batadv_tt_orig_list_entry_free_ref(orig_entry);
- }
+ hlist_for_each_entry_safe(orig_entry, safe, head, list)
+ batadv_tt_global_del_orig_entry(tt_global_entry, orig_entry);
spin_unlock_bh(&tt_global_entry->list_lock);
}
+/**
+ * batadv_tt_global_del_orig_node - remove orig_node from a global tt entry
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_global_entry: the global entry to remove the orig_node from
+ * @orig_node: the originator announcing the client
+ * @message: message to append to the log on deletion
+ *
+ * Remove the given orig_node and its according orig_entry from the given
+ * global tt entry.
+ */
static void
-batadv_tt_global_del_orig_entry(struct batadv_priv *bat_priv,
- struct batadv_tt_global_entry *tt_global_entry,
- struct batadv_orig_node *orig_node,
- const char *message)
+batadv_tt_global_del_orig_node(struct batadv_priv *bat_priv,
+ struct batadv_tt_global_entry *tt_global_entry,
+ struct batadv_orig_node *orig_node,
+ const char *message)
{
struct hlist_head *head;
struct hlist_node *safe;
@@ -1592,10 +1656,8 @@ batadv_tt_global_del_orig_entry(struct batadv_priv *bat_priv,
orig_node->orig,
tt_global_entry->common.addr,
BATADV_PRINT_VID(vid), message);
- hlist_del_rcu(&orig_entry->list);
- batadv_tt_global_size_dec(orig_node,
- tt_global_entry->common.vid);
- batadv_tt_orig_list_entry_free_ref(orig_entry);
+ batadv_tt_global_del_orig_entry(tt_global_entry,
+ orig_entry);
}
}
spin_unlock_bh(&tt_global_entry->list_lock);
@@ -1637,8 +1699,8 @@ batadv_tt_global_del_roaming(struct batadv_priv *bat_priv,
/* there is another entry, we can simply delete this
* one and can still use the other one.
*/
- batadv_tt_global_del_orig_entry(bat_priv, tt_global_entry,
- orig_node, message);
+ batadv_tt_global_del_orig_node(bat_priv, tt_global_entry,
+ orig_node, message);
}
/**
@@ -1664,8 +1726,8 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv,
goto out;
if (!roaming) {
- batadv_tt_global_del_orig_entry(bat_priv, tt_global_entry,
- orig_node, message);
+ batadv_tt_global_del_orig_node(bat_priv, tt_global_entry,
+ orig_node, message);
if (hlist_empty(&tt_global_entry->orig_list))
batadv_tt_global_free(bat_priv, tt_global_entry,
@@ -1748,8 +1810,8 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
struct batadv_tt_global_entry,
common);
- batadv_tt_global_del_orig_entry(bat_priv, tt_global,
- orig_node, message);
+ batadv_tt_global_del_orig_node(bat_priv, tt_global,
+ orig_node, message);
if (hlist_empty(&tt_global->orig_list)) {
vid = tt_global->common.vid;
@@ -1763,7 +1825,7 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
}
spin_unlock_bh(list_lock);
}
- orig_node->tt_initialised = false;
+ orig_node->capa_initialized &= ~BATADV_ORIG_CAPA_HAS_TT;
}
static bool batadv_tt_global_to_purge(struct batadv_tt_global_entry *tt_global,
@@ -2160,7 +2222,7 @@ batadv_new_tt_req_node(struct batadv_priv *bat_priv,
if (!tt_req_node)
goto unlock;
- memcpy(tt_req_node->addr, orig_node->orig, ETH_ALEN);
+ ether_addr_copy(tt_req_node->addr, orig_node->orig);
tt_req_node->issued_at = jiffies;
list_add(&tt_req_node->list, &bat_priv->tt.req_list);
@@ -2240,8 +2302,7 @@ static void batadv_tt_tvlv_generate(struct batadv_priv *bat_priv,
if ((valid_cb) && (!valid_cb(tt_common_entry, cb_data)))
continue;
- memcpy(tt_change->addr, tt_common_entry->addr,
- ETH_ALEN);
+ ether_addr_copy(tt_change->addr, tt_common_entry->addr);
tt_change->flags = tt_common_entry->flags;
tt_change->vid = htons(tt_common_entry->vid);
memset(tt_change->reserved, 0,
@@ -2724,7 +2785,7 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv,
return;
}
}
- orig_node->tt_initialised = true;
+ orig_node->capa_initialized |= BATADV_ORIG_CAPA_HAS_TT;
}
static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv,
@@ -2932,7 +2993,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv,
tt_roam_node->first_time = jiffies;
atomic_set(&tt_roam_node->counter,
BATADV_ROAMING_MAX_COUNT - 1);
- memcpy(tt_roam_node->addr, client, ETH_ALEN);
+ ether_addr_copy(tt_roam_node->addr, client);
list_add(&tt_roam_node->list, &bat_priv->tt.roam_list);
ret = true;
@@ -3121,6 +3182,9 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
*/
static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv)
{
+ /* Update multicast addresses in local translation table */
+ batadv_mcast_mla_update(bat_priv);
+
if (atomic_read(&bat_priv->tt.local_changes) < 1) {
if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt))
batadv_tt_tvlv_container_update(bat_priv);
@@ -3211,13 +3275,15 @@ static void batadv_tt_update_orig(struct batadv_priv *bat_priv,
uint8_t orig_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
struct batadv_tvlv_tt_vlan_data *tt_vlan;
bool full_table = true;
+ bool has_tt_init;
tt_vlan = (struct batadv_tvlv_tt_vlan_data *)tt_buff;
+ has_tt_init = orig_node->capa_initialized & BATADV_ORIG_CAPA_HAS_TT;
+
/* orig table not initialised AND first diff is in the OGM OR the ttvn
* increased by one -> we can apply the attached changes
*/
- if ((!orig_node->tt_initialised && ttvn == 1) ||
- ttvn - orig_ttvn == 1) {
+ if ((!has_tt_init && ttvn == 1) || ttvn - orig_ttvn == 1) {
/* the OGM could not contain the changes due to their size or
* because they have already been sent BATADV_TT_OGM_APPEND_MAX
* times.
@@ -3257,7 +3323,7 @@ static void batadv_tt_update_orig(struct batadv_priv *bat_priv,
/* if we missed more than one change or our tables are not
* in sync anymore -> request fresh tt data
*/
- if (!orig_node->tt_initialised || ttvn != orig_ttvn ||
+ if (!has_tt_init || ttvn != orig_ttvn ||
!batadv_tt_global_check_crc(orig_node, tt_vlan,
tt_num_vlan)) {
request_table:
diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h
index 20a1d7861ded..ad84d7b89e39 100644
--- a/net/batman-adv/translation-table.h
+++ b/net/batman-adv/translation-table.h
@@ -29,6 +29,8 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset);
void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
int32_t match_vid, const char *message);
+int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
+ const uint8_t *addr, unsigned short vid);
struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
const uint8_t *src,
const uint8_t *addr,
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 78370ab31f9c..34891a56773f 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -24,8 +24,9 @@
#ifdef CONFIG_BATMAN_ADV_DAT
-/* batadv_dat_addr_t is the type used for all DHT addresses. If it is changed,
- * BATADV_DAT_ADDR_MAX is changed as well.
+/**
+ * batadv_dat_addr_t - it is the type used for all DHT addresses. If it is
+ * changed, BATADV_DAT_ADDR_MAX is changed as well.
*
* *Please be careful: batadv_dat_addr_t must be UNSIGNED*
*/
@@ -163,7 +164,7 @@ struct batadv_vlan_tt {
};
/**
- * batadv_orig_node_vlan - VLAN specific data per orig_node
+ * struct batadv_orig_node_vlan - VLAN specific data per orig_node
* @vid: the VLAN identifier
* @tt: VLAN specific TT attributes
* @list: list node for orig_node::vlan_list
@@ -204,14 +205,18 @@ struct batadv_orig_bat_iv {
* @batadv_dat_addr_t: 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_flags: multicast flags announced by the orig node
+ * @mcast_want_all_unsnoop_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
* @capabilities: announced capabilities of this originator
+ * @capa_initialized: bitfield to remember whether a capability was initialized
* @last_ttvn: last seen translation table version number
* @tt_buff: last tt changeset this node received from the orig node
* @tt_buff_len: length of the last tt changeset this node received from the
* orig node
* @tt_buff_lock: lock that protects tt_buff and tt_buff_len
- * @tt_initialised: bool keeping track of whether or not this node have received
- * any translation table information from the orig node yet
* @tt_lock: prevents from updating the table while reading it. Table update is
* made up by two operations (data structure update and metdata -CRC/TTVN-
* recalculation) and they have to be executed atomically in order to avoid
@@ -247,12 +252,18 @@ struct batadv_orig_node {
#endif
unsigned long last_seen;
unsigned long bcast_seqno_reset;
+#ifdef CONFIG_BATMAN_ADV_MCAST
+ uint8_t mcast_flags;
+ struct hlist_node mcast_want_all_unsnoopables_node;
+ struct hlist_node mcast_want_all_ipv4_node;
+ struct hlist_node mcast_want_all_ipv6_node;
+#endif
uint8_t capabilities;
+ uint8_t capa_initialized;
atomic_t last_ttvn;
unsigned char *tt_buff;
int16_t tt_buff_len;
spinlock_t tt_buff_lock; /* protects tt_buff & tt_buff_len */
- bool tt_initialised;
/* prevents from changing the table while reading it */
spinlock_t tt_lock;
DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
@@ -282,10 +293,15 @@ struct batadv_orig_node {
* enum batadv_orig_capabilities - orig node capabilities
* @BATADV_ORIG_CAPA_HAS_DAT: orig node has distributed arp table enabled
* @BATADV_ORIG_CAPA_HAS_NC: orig node has network coding enabled
+ * @BATADV_ORIG_CAPA_HAS_TT: orig node has tt capability
+ * @BATADV_ORIG_CAPA_HAS_MCAST: orig node has some multicast capability
+ * (= orig node announces a tvlv of type BATADV_TVLV_MCAST)
*/
enum batadv_orig_capabilities {
BATADV_ORIG_CAPA_HAS_DAT = BIT(0),
BATADV_ORIG_CAPA_HAS_NC = BIT(1),
+ BATADV_ORIG_CAPA_HAS_TT = BIT(2),
+ BATADV_ORIG_CAPA_HAS_MCAST = BIT(3),
};
/**
@@ -334,7 +350,7 @@ struct batadv_neigh_node {
};
/**
- * struct batadv_neigh_node_bat_iv - neighbor information per outgoing
+ * struct batadv_neigh_ifinfo_bat_iv - neighbor information per outgoing
* interface for BATMAN IV
* @tq_recv: ring buffer of received TQ values from this neigh node
* @tq_index: ring buffer index
@@ -544,7 +560,7 @@ struct batadv_priv_bla {
#endif
/**
- * struct batadv_debug_log - debug logging data
+ * struct batadv_priv_debug_log - debug logging data
* @log_buff: buffer holding the logs (ring bufer)
* @log_start: index of next character to read
* @log_end: index of next character to write
@@ -607,6 +623,39 @@ struct batadv_priv_dat {
};
#endif
+#ifdef CONFIG_BATMAN_ADV_MCAST
+/**
+ * struct batadv_priv_mcast - per mesh interface mcast data
+ * @mla_list: list of multicast addresses we are currently announcing via TT
+ * @want_all_unsnoopables_list: a list of orig_nodes wanting all unsnoopable
+ * multicast traffic
+ * @want_all_ipv4_list: a list of orig_nodes wanting all IPv4 multicast traffic
+ * @want_all_ipv6_list: a list of orig_nodes wanting all IPv6 multicast traffic
+ * @flags: the flags we have last sent in our mcast tvlv
+ * @enabled: whether the multicast tvlv is currently enabled
+ * @num_disabled: number of nodes that have no mcast tvlv
+ * @num_want_all_unsnoopables: number of nodes wanting unsnoopable IP traffic
+ * @num_want_all_ipv4: counter for items in want_all_ipv4_list
+ * @num_want_all_ipv6: counter for items in want_all_ipv6_list
+ * @want_lists_lock: lock for protecting modifications to mcast want lists
+ * (traversals are rcu-locked)
+ */
+struct batadv_priv_mcast {
+ struct hlist_head mla_list;
+ struct hlist_head want_all_unsnoopables_list;
+ struct hlist_head want_all_ipv4_list;
+ struct hlist_head want_all_ipv6_list;
+ uint8_t flags;
+ bool enabled;
+ atomic_t num_disabled;
+ atomic_t num_want_all_unsnoopables;
+ atomic_t num_want_all_ipv4;
+ atomic_t num_want_all_ipv6;
+ /* protects want_all_{unsnoopables,ipv4,ipv6}_list */
+ spinlock_t want_lists_lock;
+};
+#endif
+
/**
* struct batadv_priv_nc - per mesh interface network coding private data
* @work: work queue callback item for cleanup
@@ -672,6 +721,8 @@ struct batadv_softif_vlan {
* enabled
* @distributed_arp_table: bool indicating whether distributed ARP table is
* enabled
+ * @multicast_mode: Enable or disable multicast optimizations on this node's
+ * sender/originating side
* @gw_mode: gateway operation: off, client or server (see batadv_gw_modes)
* @gw_sel_class: gateway selection class (applies if gw_mode client)
* @orig_interval: OGM broadcast interval in milliseconds
@@ -702,6 +753,7 @@ struct batadv_softif_vlan {
* @tt: translation table data
* @tvlv: type-version-length-value data
* @dat: distributed arp table data
+ * @mcast: multicast data
* @network_coding: bool indicating whether network coding is enabled
* @batadv_priv_nc: network coding data
*/
@@ -721,6 +773,9 @@ struct batadv_priv {
#ifdef CONFIG_BATMAN_ADV_DAT
atomic_t distributed_arp_table;
#endif
+#ifdef CONFIG_BATMAN_ADV_MCAST
+ atomic_t multicast_mode;
+#endif
atomic_t gw_mode;
atomic_t gw_sel_class;
atomic_t orig_interval;
@@ -759,6 +814,9 @@ struct batadv_priv {
#ifdef CONFIG_BATMAN_ADV_DAT
struct batadv_priv_dat dat;
#endif
+#ifdef CONFIG_BATMAN_ADV_MCAST
+ struct batadv_priv_mcast mcast;
+#endif
#ifdef CONFIG_BATMAN_ADV_NC
atomic_t network_coding;
struct batadv_priv_nc nc;
@@ -881,12 +939,14 @@ struct batadv_tt_local_entry {
* struct batadv_tt_global_entry - translation table global entry data
* @common: general translation table data
* @orig_list: list of orig nodes announcing this non-mesh client
+ * @orig_list_count: number of items in the orig_list
* @list_lock: lock protecting orig_list
* @roam_at: time at which TT_GLOBAL_ROAM was set
*/
struct batadv_tt_global_entry {
struct batadv_tt_common_entry common;
struct hlist_head orig_list;
+ atomic_t orig_list_count;
spinlock_t list_lock; /* protects orig_list */
unsigned long roam_at;
};
@@ -1004,8 +1064,8 @@ struct batadv_nc_packet {
};
/**
- * batadv_skb_cb - control buffer structure used to store private data relevant
- * to batman-adv in the skb->cb buffer in skbs.
+ * struct batadv_skb_cb - control buffer structure used to store private data
+ * relevant to batman-adv in the skb->cb buffer in skbs.
* @decoded: Marks a skb as decoded, which is checked when searching for coding
* opportunities in network-coding.c
*/
@@ -1116,6 +1176,16 @@ struct batadv_dat_entry {
};
/**
+ * struct batadv_hw_addr - a list entry for a MAC address
+ * @list: list node for the linking of entries
+ * @addr: the MAC address of this list entry
+ */
+struct batadv_hw_addr {
+ struct hlist_node list;
+ unsigned char addr[ETH_ALEN];
+};
+
+/**
* struct batadv_dat_candidate - candidate destination for DAT operations
* @type: the type of the selected candidate. It can one of the following:
* - BATADV_DAT_CANDIDATE_NOT_FOUND
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index adb3ea04adaa..73492b91105a 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -27,7 +27,7 @@
#include "6lowpan.h"
-#include "../ieee802154/6lowpan.h" /* for the compression support */
+#include <net/6lowpan.h> /* for the compression support */
#define IFACE_NAME_TEMPLATE "bt%d"
#define EUI64_ADDR_LEN 8
diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h
index 680eac808d74..5d281f1eaf55 100644
--- a/net/bluetooth/6lowpan.h
+++ b/net/bluetooth/6lowpan.h
@@ -14,13 +14,34 @@
#ifndef __6LOWPAN_H
#define __6LOWPAN_H
+#include <linux/errno.h>
#include <linux/skbuff.h>
#include <net/bluetooth/l2cap.h>
+#if IS_ENABLED(CONFIG_BT_6LOWPAN)
int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb);
int bt_6lowpan_add_conn(struct l2cap_conn *conn);
int bt_6lowpan_del_conn(struct l2cap_conn *conn);
int bt_6lowpan_init(void);
void bt_6lowpan_cleanup(void);
+#else
+static int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ return -EOPNOTSUPP;
+}
+static int bt_6lowpan_add_conn(struct l2cap_conn *conn)
+{
+ return -EOPNOTSUPP;
+}
+int bt_6lowpan_del_conn(struct l2cap_conn *conn)
+{
+ return -EOPNOTSUPP;
+}
+static int bt_6lowpan_init(void)
+{
+ return -EOPNOTSUPP;
+}
+static void bt_6lowpan_cleanup(void) { }
+#endif
#endif /* __6LOWPAN_H */
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 985b56070d26..06ec14499ca1 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -6,13 +6,13 @@ menuconfig BT
tristate "Bluetooth subsystem support"
depends on NET && !S390
depends on RFKILL || !RFKILL
+ select 6LOWPAN_IPHC if BT_6LOWPAN
select CRC16
select CRYPTO
select CRYPTO_BLKCIPHER
select CRYPTO_AES
select CRYPTO_ECB
select CRYPTO_SHA256
- select 6LOWPAN_IPHC
help
Bluetooth is low-cost, low-power, short-range wireless technology.
It was designed as a replacement for cables and other short-range
@@ -40,6 +40,12 @@ menuconfig BT
to Bluetooth kernel modules are provided in the BlueZ packages. For
more information, see <http://www.bluez.org/>.
+config BT_6LOWPAN
+ bool "Bluetooth 6LoWPAN support"
+ depends on BT && IPV6
+ help
+ IPv6 compression over Bluetooth.
+
source "net/bluetooth/rfcomm/Kconfig"
source "net/bluetooth/bnep/Kconfig"
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 80cb215826e8..ca51246b1016 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_BT_HIDP) += hidp/
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
- a2mp.o amp.o 6lowpan.o
+ a2mp.o amp.o
+bluetooth-$(CONFIG_BT_6LOWPAN) += 6lowpan.o
subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index efcd108822c4..9514cc9e850c 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -162,7 +162,7 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
return -ENOMEM;
}
- rsp->mtu = __constant_cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
+ rsp->mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
rsp->ext_feat = 0;
__a2mp_add_cl(mgr, rsp->cl);
@@ -235,7 +235,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
BT_DBG("chan %p state %s", chan,
state_to_string(chan->state));
- if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
+ if (chan->scid == L2CAP_CID_A2MP)
continue;
l2cap_chan_lock(chan);
@@ -649,7 +649,7 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
if (err) {
struct a2mp_cmd_rej rej;
- rej.reason = __constant_cpu_to_le16(0);
+ rej.reason = cpu_to_le16(0);
hdr = (void *) skb->data;
BT_DBG("Send A2MP Rej: cmd 0x%2.2x err %d", hdr->code, err);
@@ -695,7 +695,13 @@ static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state,
static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
unsigned long len, int nb)
{
- return bt_skb_alloc(len, GFP_KERNEL);
+ struct sk_buff *skb;
+
+ skb = bt_skb_alloc(len, GFP_KERNEL);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ return skb;
}
static struct l2cap_ops a2mp_chan_ops = {
@@ -726,7 +732,11 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
BT_DBG("chan %p", chan);
- chan->chan_type = L2CAP_CHAN_CONN_FIX_A2MP;
+ chan->chan_type = L2CAP_CHAN_FIXED;
+ chan->scid = L2CAP_CID_A2MP;
+ chan->dcid = L2CAP_CID_A2MP;
+ chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
chan->ops = &a2mp_chan_ops;
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 0c5866bb49b6..2021c481cdb6 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -31,7 +31,7 @@
#include <net/bluetooth/bluetooth.h>
#include <linux/proc_fs.h>
-#define VERSION "2.18"
+#define VERSION "2.19"
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index ba5366c320da..d958e2dca52f 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -82,7 +82,7 @@ static void hci_acl_create_connection(struct hci_conn *conn)
cp.pscan_rep_mode = ie->data.pscan_rep_mode;
cp.pscan_mode = ie->data.pscan_mode;
cp.clock_offset = ie->data.clock_offset |
- __constant_cpu_to_le16(0x8000);
+ cpu_to_le16(0x8000);
}
memcpy(conn->dev_class, ie->data.dev_class, 3);
@@ -182,8 +182,8 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
cp.handle = cpu_to_le16(handle);
- cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
- cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
+ cp.tx_bandwidth = cpu_to_le32(0x00001f40);
+ cp.rx_bandwidth = cpu_to_le32(0x00001f40);
cp.voice_setting = cpu_to_le16(conn->setting);
switch (conn->setting & SCO_AIRMODE_MASK) {
@@ -225,13 +225,13 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
cp.conn_interval_max = cpu_to_le16(max);
cp.conn_latency = cpu_to_le16(latency);
cp.supervision_timeout = cpu_to_le16(to_multiplier);
- cp.min_ce_len = __constant_cpu_to_le16(0x0001);
- cp.max_ce_len = __constant_cpu_to_le16(0x0001);
+ cp.min_ce_len = cpu_to_le16(0x0000);
+ cp.max_ce_len = cpu_to_le16(0x0000);
hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp);
}
-void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
+void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
__u8 ltk[16])
{
struct hci_dev *hdev = conn->hdev;
@@ -242,9 +242,9 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
memset(&cp, 0, sizeof(cp));
cp.handle = cpu_to_le16(conn->handle);
- memcpy(cp.ltk, ltk, sizeof(cp.ltk));
+ cp.rand = rand;
cp.ediv = ediv;
- memcpy(cp.rand, rand, sizeof(cp.rand));
+ memcpy(cp.ltk, ltk, sizeof(cp.ltk));
hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
}
@@ -337,9 +337,9 @@ static void hci_conn_idle(struct work_struct *work)
if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) {
struct hci_cp_sniff_subrate cp;
cp.handle = cpu_to_le16(conn->handle);
- cp.max_latency = __constant_cpu_to_le16(0);
- cp.min_remote_timeout = __constant_cpu_to_le16(0);
- cp.min_local_timeout = __constant_cpu_to_le16(0);
+ cp.max_latency = cpu_to_le16(0);
+ cp.min_remote_timeout = cpu_to_le16(0);
+ cp.min_local_timeout = cpu_to_le16(0);
hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp);
}
@@ -348,8 +348,8 @@ static void hci_conn_idle(struct work_struct *work)
cp.handle = cpu_to_le16(conn->handle);
cp.max_interval = cpu_to_le16(hdev->sniff_max_interval);
cp.min_interval = cpu_to_le16(hdev->sniff_min_interval);
- cp.attempt = __constant_cpu_to_le16(4);
- cp.timeout = __constant_cpu_to_le16(1);
+ cp.attempt = cpu_to_le16(4);
+ cp.timeout = cpu_to_le16(1);
hci_send_cmd(hdev, HCI_OP_SNIFF_MODE, sizeof(cp), &cp);
}
}
@@ -363,6 +363,16 @@ static void hci_conn_auto_accept(struct work_struct *work)
&conn->dst);
}
+static void le_conn_timeout(struct work_struct *work)
+{
+ struct hci_conn *conn = container_of(work, struct hci_conn,
+ le_conn_timeout.work);
+
+ BT_DBG("");
+
+ hci_le_create_connection_cancel(conn);
+}
+
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
{
struct hci_conn *conn;
@@ -410,6 +420,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept);
INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
+ INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
atomic_set(&conn->refcnt, 0);
@@ -442,6 +453,8 @@ int hci_conn_del(struct hci_conn *conn)
/* Unacked frames */
hdev->acl_cnt += conn->sent;
} else if (conn->type == LE_LINK) {
+ cancel_delayed_work_sync(&conn->le_conn_timeout);
+
if (hdev->le_pkts)
hdev->le_cnt += conn->sent;
else
@@ -514,6 +527,26 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
}
EXPORT_SYMBOL(hci_get_route);
+/* This function requires the caller holds hdev->lock */
+void hci_le_conn_failed(struct hci_conn *conn, u8 status)
+{
+ struct hci_dev *hdev = conn->hdev;
+
+ conn->state = BT_CLOSED;
+
+ mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type,
+ status);
+
+ hci_proto_connect_cfm(conn, status);
+
+ hci_conn_del(conn);
+
+ /* Since we may have temporarily stopped the background scanning in
+ * favor of connection establishment, we should restart it.
+ */
+ hci_update_background_scan(hdev);
+}
+
static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
{
struct hci_conn *conn;
@@ -530,55 +563,55 @@ static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
if (!conn)
goto done;
- conn->state = BT_CLOSED;
-
- mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type,
- status);
-
- hci_proto_connect_cfm(conn, status);
-
- hci_conn_del(conn);
+ hci_le_conn_failed(conn, status);
done:
hci_dev_unlock(hdev);
}
-static int hci_create_le_conn(struct hci_conn *conn)
+static void hci_req_add_le_create_conn(struct hci_request *req,
+ struct hci_conn *conn)
{
- struct hci_dev *hdev = conn->hdev;
struct hci_cp_le_create_conn cp;
- struct hci_request req;
- int err;
-
- hci_req_init(&req, hdev);
+ struct hci_dev *hdev = conn->hdev;
+ u8 own_addr_type;
memset(&cp, 0, sizeof(cp));
+
+ /* Update random address, but set require_privacy to false so
+ * that we never connect with an unresolvable address.
+ */
+ if (hci_update_random_address(req, false, &own_addr_type))
+ return;
+
+ /* Save the address type used for this connnection attempt so we able
+ * to retrieve this information if we need it.
+ */
+ conn->src_type = own_addr_type;
+
cp.scan_interval = cpu_to_le16(hdev->le_scan_interval);
cp.scan_window = cpu_to_le16(hdev->le_scan_window);
bacpy(&cp.peer_addr, &conn->dst);
cp.peer_addr_type = conn->dst_type;
- cp.own_address_type = conn->src_type;
- cp.conn_interval_min = cpu_to_le16(hdev->le_conn_min_interval);
- cp.conn_interval_max = cpu_to_le16(hdev->le_conn_max_interval);
- cp.supervision_timeout = __constant_cpu_to_le16(0x002a);
- cp.min_ce_len = __constant_cpu_to_le16(0x0000);
- cp.max_ce_len = __constant_cpu_to_le16(0x0000);
+ cp.own_address_type = own_addr_type;
+ cp.conn_interval_min = cpu_to_le16(conn->le_conn_min_interval);
+ cp.conn_interval_max = cpu_to_le16(conn->le_conn_max_interval);
+ cp.supervision_timeout = cpu_to_le16(0x002a);
+ cp.min_ce_len = cpu_to_le16(0x0000);
+ cp.max_ce_len = cpu_to_le16(0x0000);
- hci_req_add(&req, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
+ hci_req_add(req, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
- err = hci_req_run(&req, create_le_conn_complete);
- if (err) {
- hci_conn_del(conn);
- return err;
- }
-
- return 0;
+ conn->state = BT_CONNECT;
}
-static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
- u8 dst_type, u8 sec_level, u8 auth_type)
+struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
+ u8 dst_type, u8 sec_level, u8 auth_type)
{
+ struct hci_conn_params *params;
struct hci_conn *conn;
+ struct smp_irk *irk;
+ struct hci_request req;
int err;
if (test_bit(HCI_ADVERTISING, &hdev->flags))
@@ -607,35 +640,74 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
if (conn)
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.
+ *
+ * This uses the cached random resolvable address from
+ * a previous scan. When no cached address is available,
+ * try connecting to the identity address instead.
+ *
+ * Storing the resolvable random address is required here
+ * to handle connection failures. The address will later
+ * be resolved back into the original identity address
+ * from the connect request.
+ */
+ irk = hci_find_irk_by_addr(hdev, dst, dst_type);
+ if (irk && bacmp(&irk->rpa, BDADDR_ANY)) {
+ dst = &irk->rpa;
+ dst_type = ADDR_LE_DEV_RANDOM;
+ }
+
conn = hci_conn_add(hdev, LE_LINK, dst);
if (!conn)
return ERR_PTR(-ENOMEM);
- if (dst_type == BDADDR_LE_PUBLIC)
- conn->dst_type = ADDR_LE_DEV_PUBLIC;
- else
- conn->dst_type = ADDR_LE_DEV_RANDOM;
-
- conn->src_type = hdev->own_addr_type;
+ conn->dst_type = dst_type;
- conn->state = BT_CONNECT;
conn->out = true;
conn->link_mode |= HCI_LM_MASTER;
conn->sec_level = BT_SECURITY_LOW;
conn->pending_sec_level = sec_level;
conn->auth_type = auth_type;
- err = hci_create_le_conn(conn);
- if (err)
+ params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
+ if (params) {
+ conn->le_conn_min_interval = params->conn_min_interval;
+ conn->le_conn_max_interval = params->conn_max_interval;
+ } else {
+ conn->le_conn_min_interval = hdev->le_conn_min_interval;
+ conn->le_conn_max_interval = hdev->le_conn_max_interval;
+ }
+
+ hci_req_init(&req, hdev);
+
+ /* If controller is scanning, we stop it since some controllers are
+ * not able to scan and connect at the same time. Also set the
+ * HCI_LE_SCAN_INTERRUPTED flag so that the command complete
+ * handler for scan disabling knows to set the correct discovery
+ * state.
+ */
+ if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
+ hci_req_add_le_scan_disable(&req);
+ set_bit(HCI_LE_SCAN_INTERRUPTED, &hdev->dev_flags);
+ }
+
+ hci_req_add_le_create_conn(&req, conn);
+
+ err = hci_req_run(&req, create_le_conn_complete);
+ if (err) {
+ hci_conn_del(conn);
return ERR_PTR(err);
+ }
done:
hci_conn_hold(conn);
return conn;
}
-static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
- u8 sec_level, u8 auth_type)
+struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
+ u8 sec_level, u8 auth_type)
{
struct hci_conn *acl;
@@ -704,27 +776,22 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
return sco;
}
-/* Create SCO, ACL or LE connection. */
-struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
- __u8 dst_type, __u8 sec_level, __u8 auth_type)
-{
- BT_DBG("%s dst %pMR type 0x%x", hdev->name, dst, type);
-
- switch (type) {
- case LE_LINK:
- return hci_connect_le(hdev, dst, dst_type, sec_level, auth_type);
- case ACL_LINK:
- return hci_connect_acl(hdev, dst, sec_level, auth_type);
- }
-
- return ERR_PTR(-EINVAL);
-}
-
/* Check link security requirement */
int hci_conn_check_link_mode(struct hci_conn *conn)
{
BT_DBG("hcon %p", conn);
+ /* In Secure Connections Only mode, it is required that Secure
+ * Connections is used and the link is encrypted with AES-CCM
+ * using a P-256 authenticated combination key.
+ */
+ if (test_bit(HCI_SC_ONLY, &conn->hdev->flags)) {
+ if (!hci_conn_sc_enabled(conn) ||
+ !test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
+ conn->key_type != HCI_LK_AUTH_COMBINATION_P256)
+ return 0;
+ }
+
if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT))
return 0;
@@ -800,14 +867,23 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
if (!(conn->link_mode & HCI_LM_AUTH))
goto auth;
- /* An authenticated combination key has sufficient security for any
- security level. */
- if (conn->key_type == HCI_LK_AUTH_COMBINATION)
+ /* An authenticated FIPS approved combination key has sufficient
+ * security for security level 4. */
+ if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256 &&
+ sec_level == BT_SECURITY_FIPS)
+ goto encrypt;
+
+ /* An authenticated combination key has sufficient security for
+ security level 3. */
+ if ((conn->key_type == HCI_LK_AUTH_COMBINATION_P192 ||
+ conn->key_type == HCI_LK_AUTH_COMBINATION_P256) &&
+ sec_level == BT_SECURITY_HIGH)
goto encrypt;
/* An unauthenticated combination key has sufficient security for
security level 1 and 2. */
- if (conn->key_type == HCI_LK_UNAUTH_COMBINATION &&
+ if ((conn->key_type == HCI_LK_UNAUTH_COMBINATION_P192 ||
+ conn->key_type == HCI_LK_UNAUTH_COMBINATION_P256) &&
(sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW))
goto encrypt;
@@ -816,7 +892,8 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
is generated using maximum PIN code length (16).
For pre 2.1 units. */
if (conn->key_type == HCI_LK_COMBINATION &&
- (sec_level != BT_SECURITY_HIGH || conn->pin_length == 16))
+ (sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW ||
+ conn->pin_length == 16))
goto encrypt;
auth:
@@ -840,13 +917,17 @@ int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level)
{
BT_DBG("hcon %p", conn);
- if (sec_level != BT_SECURITY_HIGH)
- return 1; /* Accept if non-secure is required */
+ /* Accept if non-secure or higher security level is required */
+ if (sec_level != BT_SECURITY_HIGH && sec_level != BT_SECURITY_FIPS)
+ return 1;
- if (conn->sec_level == BT_SECURITY_HIGH)
+ /* Accept if secure or higher security level is already present */
+ if (conn->sec_level == BT_SECURITY_HIGH ||
+ conn->sec_level == BT_SECURITY_FIPS)
return 1;
- return 0; /* Reject not secure link */
+ /* Reject not secure link */
+ return 0;
}
EXPORT_SYMBOL(hci_conn_check_secure);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 5e8663c194c1..1c6ffaa8902f 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -29,11 +29,14 @@
#include <linux/idr.h>
#include <linux/rfkill.h>
#include <linux/debugfs.h>
+#include <linux/crypto.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include "smp.h"
+
static void hci_rx_work(struct work_struct *work);
static void hci_cmd_work(struct work_struct *work);
static void hci_tx_work(struct work_struct *work);
@@ -285,24 +288,6 @@ static const struct file_operations link_keys_fops = {
.release = single_release,
};
-static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct hci_dev *hdev = file->private_data;
- char buf[3];
-
- buf[0] = test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) ? 'Y': 'N';
- buf[1] = '\n';
- buf[2] = '\0';
- return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static const struct file_operations use_debug_keys_fops = {
- .open = simple_open,
- .read = use_debug_keys_read,
- .llseek = default_llseek,
-};
-
static int dev_class_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
@@ -415,6 +400,70 @@ static int ssp_debug_mode_get(void *data, u64 *val)
DEFINE_SIMPLE_ATTRIBUTE(ssp_debug_mode_fops, ssp_debug_mode_get,
ssp_debug_mode_set, "%llu\n");
+static ssize_t force_sc_support_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[3];
+
+ buf[0] = test_bit(HCI_FORCE_SC, &hdev->dev_flags) ? 'Y': 'N';
+ buf[1] = '\n';
+ buf[2] = '\0';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_sc_support_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[32];
+ size_t buf_size = min(count, (sizeof(buf)-1));
+ bool enable;
+
+ if (test_bit(HCI_UP, &hdev->flags))
+ return -EBUSY;
+
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ if (strtobool(buf, &enable))
+ return -EINVAL;
+
+ if (enable == test_bit(HCI_FORCE_SC, &hdev->dev_flags))
+ return -EALREADY;
+
+ change_bit(HCI_FORCE_SC, &hdev->dev_flags);
+
+ return count;
+}
+
+static const struct file_operations force_sc_support_fops = {
+ .open = simple_open,
+ .read = force_sc_support_read,
+ .write = force_sc_support_write,
+ .llseek = default_llseek,
+};
+
+static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[3];
+
+ buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N';
+ buf[1] = '\n';
+ buf[2] = '\0';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations sc_only_mode_fops = {
+ .open = simple_open,
+ .read = sc_only_mode_read,
+ .llseek = default_llseek,
+};
+
static int idle_timeout_set(void *data, u64 val)
{
struct hci_dev *hdev = data;
@@ -443,6 +492,37 @@ static int idle_timeout_get(void *data, u64 *val)
DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
idle_timeout_set, "%llu\n");
+static int rpa_timeout_set(void *data, u64 val)
+{
+ struct hci_dev *hdev = data;
+
+ /* Require the RPA timeout to be at least 30 seconds and at most
+ * 24 hours.
+ */
+ if (val < 30 || val > (60 * 60 * 24))
+ return -EINVAL;
+
+ hci_dev_lock(hdev);
+ hdev->rpa_timeout = val;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int rpa_timeout_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ *val = hdev->rpa_timeout;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(rpa_timeout_fops, rpa_timeout_get,
+ rpa_timeout_set, "%llu\n");
+
static int sniff_min_interval_set(void *data, u64 val)
{
struct hci_dev *hdev = data;
@@ -499,6 +579,59 @@ static int sniff_max_interval_get(void *data, u64 *val)
DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
sniff_max_interval_set, "%llu\n");
+static int identity_show(struct seq_file *f, void *p)
+{
+ struct hci_dev *hdev = f->private;
+ bdaddr_t addr;
+ u8 addr_type;
+
+ hci_dev_lock(hdev);
+
+ hci_copy_identity_address(hdev, &addr, &addr_type);
+
+ seq_printf(f, "%pMR (type %u) %*phN %pMR\n", &addr, addr_type,
+ 16, hdev->irk, &hdev->rpa);
+
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int identity_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, identity_show, inode->i_private);
+}
+
+static const struct file_operations identity_fops = {
+ .open = identity_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int random_address_show(struct seq_file *f, void *p)
+{
+ struct hci_dev *hdev = f->private;
+
+ hci_dev_lock(hdev);
+ seq_printf(f, "%pMR\n", &hdev->random_addr);
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int random_address_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, random_address_show, inode->i_private);
+}
+
+static const struct file_operations random_address_fops = {
+ .open = random_address_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static int static_address_show(struct seq_file *f, void *p)
{
struct hci_dev *hdev = f->private;
@@ -522,33 +655,107 @@ static const struct file_operations static_address_fops = {
.release = single_release,
};
-static int own_address_type_set(void *data, u64 val)
+static ssize_t force_static_address_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
{
- struct hci_dev *hdev = data;
+ struct hci_dev *hdev = file->private_data;
+ char buf[3];
- if (val != 0 && val != 1)
+ buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) ? 'Y': 'N';
+ buf[1] = '\n';
+ buf[2] = '\0';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_static_address_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[32];
+ size_t buf_size = min(count, (sizeof(buf)-1));
+ bool enable;
+
+ if (test_bit(HCI_UP, &hdev->flags))
+ return -EBUSY;
+
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ if (strtobool(buf, &enable))
return -EINVAL;
+ if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags))
+ return -EALREADY;
+
+ change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags);
+
+ return count;
+}
+
+static const struct file_operations force_static_address_fops = {
+ .open = simple_open,
+ .read = force_static_address_read,
+ .write = force_static_address_write,
+ .llseek = default_llseek,
+};
+
+static int white_list_show(struct seq_file *f, void *ptr)
+{
+ struct hci_dev *hdev = f->private;
+ struct bdaddr_list *b;
+
hci_dev_lock(hdev);
- hdev->own_addr_type = val;
+ list_for_each_entry(b, &hdev->le_white_list, list)
+ seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
hci_dev_unlock(hdev);
return 0;
}
-static int own_address_type_get(void *data, u64 *val)
+static int white_list_open(struct inode *inode, struct file *file)
{
- struct hci_dev *hdev = data;
+ return single_open(file, white_list_show, inode->i_private);
+}
+
+static const struct file_operations white_list_fops = {
+ .open = white_list_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
+{
+ struct hci_dev *hdev = f->private;
+ struct list_head *p, *n;
hci_dev_lock(hdev);
- *val = hdev->own_addr_type;
+ list_for_each_safe(p, n, &hdev->identity_resolving_keys) {
+ struct smp_irk *irk = list_entry(p, struct smp_irk, list);
+ seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
+ &irk->bdaddr, irk->addr_type,
+ 16, irk->val, &irk->rpa);
+ }
hci_dev_unlock(hdev);
return 0;
}
-DEFINE_SIMPLE_ATTRIBUTE(own_address_type_fops, own_address_type_get,
- own_address_type_set, "%llu\n");
+static int identity_resolving_keys_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, identity_resolving_keys_show,
+ inode->i_private);
+}
+
+static const struct file_operations identity_resolving_keys_fops = {
+ .open = identity_resolving_keys_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
static int long_term_keys_show(struct seq_file *f, void *ptr)
{
@@ -556,12 +763,12 @@ static int long_term_keys_show(struct seq_file *f, void *ptr)
struct list_head *p, *n;
hci_dev_lock(hdev);
- list_for_each_safe(p, n, &hdev->link_keys) {
+ list_for_each_safe(p, n, &hdev->long_term_keys) {
struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list);
- seq_printf(f, "%pMR (type %u) %u %u %u %.4x %*phN %*phN\\n",
+ seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
&ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
- 8, ltk->rand, 16, ltk->val);
+ __le64_to_cpu(ltk->rand), 16, ltk->val);
}
hci_dev_unlock(hdev);
@@ -636,6 +843,34 @@ static int conn_max_interval_get(void *data, u64 *val)
DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
conn_max_interval_set, "%llu\n");
+static int adv_channel_map_set(void *data, u64 val)
+{
+ struct hci_dev *hdev = data;
+
+ if (val < 0x01 || val > 0x07)
+ return -EINVAL;
+
+ hci_dev_lock(hdev);
+ hdev->le_adv_channel_map = val;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int adv_channel_map_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ *val = hdev->le_adv_channel_map;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get,
+ adv_channel_map_set, "%llu\n");
+
static ssize_t lowpan_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -679,6 +914,115 @@ static const struct file_operations lowpan_debugfs_fops = {
.llseek = default_llseek,
};
+static int le_auto_conn_show(struct seq_file *sf, void *ptr)
+{
+ struct hci_dev *hdev = sf->private;
+ struct hci_conn_params *p;
+
+ hci_dev_lock(hdev);
+
+ list_for_each_entry(p, &hdev->le_conn_params, list) {
+ seq_printf(sf, "%pMR %u %u\n", &p->addr, p->addr_type,
+ p->auto_connect);
+ }
+
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int le_auto_conn_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, le_auto_conn_show, inode->i_private);
+}
+
+static ssize_t le_auto_conn_write(struct file *file, const char __user *data,
+ size_t count, loff_t *offset)
+{
+ struct seq_file *sf = file->private_data;
+ struct hci_dev *hdev = sf->private;
+ u8 auto_connect = 0;
+ bdaddr_t addr;
+ u8 addr_type;
+ char *buf;
+ int err = 0;
+ int n;
+
+ /* Don't allow partial write */
+ if (*offset != 0)
+ return -EINVAL;
+
+ if (count < 3)
+ return -EINVAL;
+
+ buf = kzalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, data, count)) {
+ err = -EFAULT;
+ goto done;
+ }
+
+ if (memcmp(buf, "add", 3) == 0) {
+ n = sscanf(&buf[4], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu %hhu",
+ &addr.b[5], &addr.b[4], &addr.b[3], &addr.b[2],
+ &addr.b[1], &addr.b[0], &addr_type,
+ &auto_connect);
+
+ if (n < 7) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ hci_dev_lock(hdev);
+ err = hci_conn_params_add(hdev, &addr, addr_type, auto_connect,
+ hdev->le_conn_min_interval,
+ hdev->le_conn_max_interval);
+ hci_dev_unlock(hdev);
+
+ if (err)
+ goto done;
+ } else if (memcmp(buf, "del", 3) == 0) {
+ n = sscanf(&buf[4], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
+ &addr.b[5], &addr.b[4], &addr.b[3], &addr.b[2],
+ &addr.b[1], &addr.b[0], &addr_type);
+
+ if (n < 7) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ hci_dev_lock(hdev);
+ hci_conn_params_del(hdev, &addr, addr_type);
+ hci_dev_unlock(hdev);
+ } else if (memcmp(buf, "clr", 3) == 0) {
+ hci_dev_lock(hdev);
+ hci_conn_params_clear(hdev);
+ hci_pend_le_conns_clear(hdev);
+ hci_update_background_scan(hdev);
+ hci_dev_unlock(hdev);
+ } else {
+ err = -EINVAL;
+ }
+
+done:
+ kfree(buf);
+
+ if (err)
+ return err;
+ else
+ return count;
+}
+
+static const struct file_operations le_auto_conn_fops = {
+ .open = le_auto_conn_open,
+ .read = seq_read,
+ .write = le_auto_conn_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
/* ---- HCI requests ---- */
static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
@@ -1005,7 +1349,7 @@ static void bredr_setup(struct hci_request *req)
hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
/* Connection accept timeout ~20 secs */
- param = __constant_cpu_to_le16(0x7d00);
+ param = cpu_to_le16(0x7d00);
hci_req_add(req, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
/* AVM Berlin (31), aka "BlueFRITZ!", reports version 1.2,
@@ -1027,14 +1371,17 @@ static void le_setup(struct hci_request *req)
/* Read LE Local Supported Features */
hci_req_add(req, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL);
+ /* Read LE Supported States */
+ hci_req_add(req, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL);
+
/* Read LE Advertising Channel TX Power */
hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
/* Read LE White List Size */
hci_req_add(req, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL);
- /* Read LE Supported States */
- hci_req_add(req, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL);
+ /* Clear LE White List */
+ hci_req_add(req, HCI_OP_LE_CLEAR_WHITE_LIST, 0, NULL);
/* LE-only controllers have LE implicitly enabled */
if (!lmp_bredr_capable(hdev))
@@ -1288,6 +1635,10 @@ static void hci_set_event_mask_page_2(struct hci_request *req)
events[2] |= 0x08; /* Truncated Page Complete */
}
+ /* Enable Authenticated Payload Timeout Expired event if supported */
+ if (lmp_ping_capable(hdev))
+ events[2] |= 0x80;
+
hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events);
}
@@ -1322,21 +1673,8 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
if (hdev->commands[5] & 0x10)
hci_setup_link_policy(req);
- if (lmp_le_capable(hdev)) {
- if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
- /* If the controller has a public BD_ADDR, then
- * by default use that one. If this is a LE only
- * controller without a public address, default
- * to the random address.
- */
- if (bacmp(&hdev->bdaddr, BDADDR_ANY))
- hdev->own_addr_type = ADDR_LE_DEV_PUBLIC;
- else
- hdev->own_addr_type = ADDR_LE_DEV_RANDOM;
- }
-
+ if (lmp_le_capable(hdev))
hci_set_le_support(req);
- }
/* Read features beyond page 1 if available */
for (p = 2; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) {
@@ -1359,6 +1697,15 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt)
/* Check for Synchronization Train support */
if (lmp_sync_train_capable(hdev))
hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
+
+ /* Enable Secure Connections if supported and configured */
+ if ((lmp_sc_capable(hdev) ||
+ test_bit(HCI_FORCE_SC, &hdev->dev_flags)) &&
+ test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+ u8 support = 0x01;
+ hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
+ sizeof(support), &support);
+ }
}
static int __hci_init(struct hci_dev *hdev)
@@ -1417,8 +1764,6 @@ static int __hci_init(struct hci_dev *hdev)
hdev, &inquiry_cache_fops);
debugfs_create_file("link_keys", 0400, hdev->debugfs,
hdev, &link_keys_fops);
- debugfs_create_file("use_debug_keys", 0444, hdev->debugfs,
- hdev, &use_debug_keys_fops);
debugfs_create_file("dev_class", 0444, hdev->debugfs,
hdev, &dev_class_fops);
debugfs_create_file("voice_setting", 0444, hdev->debugfs,
@@ -1430,6 +1775,10 @@ static int __hci_init(struct hci_dev *hdev)
hdev, &auto_accept_delay_fops);
debugfs_create_file("ssp_debug_mode", 0644, hdev->debugfs,
hdev, &ssp_debug_mode_fops);
+ debugfs_create_file("force_sc_support", 0644, hdev->debugfs,
+ hdev, &force_sc_support_fops);
+ debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
+ hdev, &sc_only_mode_fops);
}
if (lmp_sniff_capable(hdev)) {
@@ -1442,20 +1791,43 @@ static int __hci_init(struct hci_dev *hdev)
}
if (lmp_le_capable(hdev)) {
+ debugfs_create_file("identity", 0400, hdev->debugfs,
+ hdev, &identity_fops);
+ debugfs_create_file("rpa_timeout", 0644, hdev->debugfs,
+ hdev, &rpa_timeout_fops);
+ debugfs_create_file("random_address", 0444, hdev->debugfs,
+ hdev, &random_address_fops);
+ debugfs_create_file("static_address", 0444, hdev->debugfs,
+ hdev, &static_address_fops);
+
+ /* For controllers with a public address, provide a debug
+ * option to force the usage of the configured static
+ * address. By default the public address is used.
+ */
+ if (bacmp(&hdev->bdaddr, BDADDR_ANY))
+ debugfs_create_file("force_static_address", 0644,
+ hdev->debugfs, hdev,
+ &force_static_address_fops);
+
debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
&hdev->le_white_list_size);
- debugfs_create_file("static_address", 0444, hdev->debugfs,
- hdev, &static_address_fops);
- debugfs_create_file("own_address_type", 0644, hdev->debugfs,
- hdev, &own_address_type_fops);
+ debugfs_create_file("white_list", 0444, hdev->debugfs, hdev,
+ &white_list_fops);
+ debugfs_create_file("identity_resolving_keys", 0400,
+ hdev->debugfs, hdev,
+ &identity_resolving_keys_fops);
debugfs_create_file("long_term_keys", 0400, hdev->debugfs,
hdev, &long_term_keys_fops);
debugfs_create_file("conn_min_interval", 0644, hdev->debugfs,
hdev, &conn_min_interval_fops);
debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
hdev, &conn_max_interval_fops);
+ debugfs_create_file("adv_channel_map", 0644, hdev->debugfs,
+ hdev, &adv_channel_map_fops);
debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev,
&lowpan_debugfs_fops);
+ debugfs_create_file("le_auto_conn", 0644, hdev->debugfs, hdev,
+ &le_auto_conn_fops);
}
return 0;
@@ -1548,6 +1920,8 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state)
switch (state) {
case DISCOVERY_STOPPED:
+ hci_update_background_scan(hdev);
+
if (hdev->discovery.state != DISCOVERY_STARTING)
mgmt_discovering(hdev, 0);
break;
@@ -1876,10 +2250,15 @@ static int hci_dev_do_open(struct hci_dev *hdev)
* be able to determine if there is a public address
* or not.
*
+ * In case of user channel usage, it is not important
+ * if a public address or static random address is
+ * available.
+ *
* This check is only valid for BR/EDR controllers
* since AMP controllers do not have an address.
*/
- if (hdev->dev_type == HCI_BREDR &&
+ if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
+ hdev->dev_type == HCI_BREDR &&
!bacmp(&hdev->bdaddr, BDADDR_ANY) &&
!bacmp(&hdev->static_addr, BDADDR_ANY)) {
ret = -EADDRNOTAVAIL;
@@ -1916,6 +2295,7 @@ static int hci_dev_do_open(struct hci_dev *hdev)
if (!ret) {
hci_dev_hold(hdev);
+ set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP);
if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
@@ -2014,9 +2394,13 @@ static int hci_dev_do_close(struct hci_dev *hdev)
cancel_delayed_work_sync(&hdev->le_scan_disable);
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ cancel_delayed_work_sync(&hdev->rpa_expired);
+
hci_dev_lock(hdev);
hci_inquiry_cache_flush(hdev);
hci_conn_hash_flush(hdev);
+ hci_pend_le_conns_clear(hdev);
hci_dev_unlock(hdev);
hci_notify(hdev, HCI_DEV_DOWN);
@@ -2074,6 +2458,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
memset(hdev->eir, 0, sizeof(hdev->eir));
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
+ bacpy(&hdev->random_addr, BDADDR_ANY);
hci_req_unlock(hdev);
@@ -2437,7 +2822,7 @@ static void hci_discov_off(struct work_struct *work)
mgmt_discoverable_timeout(hdev);
}
-int hci_uuids_clear(struct hci_dev *hdev)
+void hci_uuids_clear(struct hci_dev *hdev)
{
struct bt_uuid *uuid, *tmp;
@@ -2445,11 +2830,9 @@ int hci_uuids_clear(struct hci_dev *hdev)
list_del(&uuid->list);
kfree(uuid);
}
-
- return 0;
}
-int hci_link_keys_clear(struct hci_dev *hdev)
+void hci_link_keys_clear(struct hci_dev *hdev)
{
struct list_head *p, *n;
@@ -2461,11 +2844,9 @@ int hci_link_keys_clear(struct hci_dev *hdev)
list_del(p);
kfree(key);
}
-
- return 0;
}
-int hci_smp_ltks_clear(struct hci_dev *hdev)
+void hci_smp_ltks_clear(struct hci_dev *hdev)
{
struct smp_ltk *k, *tmp;
@@ -2473,8 +2854,16 @@ int hci_smp_ltks_clear(struct hci_dev *hdev)
list_del(&k->list);
kfree(k);
}
+}
- return 0;
+void hci_smp_irks_clear(struct hci_dev *hdev)
+{
+ struct smp_irk *k, *tmp;
+
+ list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
+ list_del(&k->list);
+ kfree(k);
+ }
}
struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
@@ -2524,13 +2913,24 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
return false;
}
-struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
+static bool ltk_type_master(u8 type)
+{
+ if (type == HCI_SMP_STK || type == HCI_SMP_LTK)
+ return true;
+
+ return false;
+}
+
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
+ bool master)
{
struct smp_ltk *k;
list_for_each_entry(k, &hdev->long_term_keys, list) {
- if (k->ediv != ediv ||
- memcmp(rand, k->rand, sizeof(k->rand)))
+ if (k->ediv != ediv || k->rand != rand)
+ continue;
+
+ if (ltk_type_master(k->type) != master)
continue;
return k;
@@ -2540,18 +2940,56 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
}
struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 addr_type)
+ u8 addr_type, bool master)
{
struct smp_ltk *k;
list_for_each_entry(k, &hdev->long_term_keys, list)
if (addr_type == k->bdaddr_type &&
- bacmp(bdaddr, &k->bdaddr) == 0)
+ bacmp(bdaddr, &k->bdaddr) == 0 &&
+ ltk_type_master(k->type) == master)
return k;
return NULL;
}
+struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa)
+{
+ struct smp_irk *irk;
+
+ list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+ if (!bacmp(&irk->rpa, rpa))
+ return irk;
+ }
+
+ list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+ if (smp_irk_matches(hdev->tfm_aes, irk->val, rpa)) {
+ bacpy(&irk->rpa, rpa);
+ return irk;
+ }
+ }
+
+ return NULL;
+}
+
+struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type)
+{
+ struct smp_irk *irk;
+
+ /* Identity Address must be public or static random */
+ if (addr_type == ADDR_LE_DEV_RANDOM && (bdaddr->b[5] & 0xc0) != 0xc0)
+ return NULL;
+
+ list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+ if (addr_type == irk->addr_type &&
+ bacmp(bdaddr, &irk->bdaddr) == 0)
+ return irk;
+ }
+
+ return NULL;
+}
+
int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len)
{
@@ -2565,7 +3003,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
key = old_key;
} else {
old_key_type = conn ? conn->key_type : 0xff;
- key = kzalloc(sizeof(*key), GFP_ATOMIC);
+ key = kzalloc(sizeof(*key), GFP_KERNEL);
if (!key)
return -ENOMEM;
list_add(&key->list, &hdev->link_keys);
@@ -2605,22 +3043,20 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
return 0;
}
-int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type,
- int new_key, u8 authenticated, u8 tk[16], u8 enc_size, __le16
- ediv, u8 rand[8])
+struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type, u8 type, u8 authenticated,
+ u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand)
{
struct smp_ltk *key, *old_key;
+ bool master = ltk_type_master(type);
- if (!(type & HCI_SMP_STK) && !(type & HCI_SMP_LTK))
- return 0;
-
- old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type);
+ old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, master);
if (old_key)
key = old_key;
else {
- key = kzalloc(sizeof(*key), GFP_ATOMIC);
+ key = kzalloc(sizeof(*key), GFP_KERNEL);
if (!key)
- return -ENOMEM;
+ return NULL;
list_add(&key->list, &hdev->long_term_keys);
}
@@ -2629,17 +3065,34 @@ int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type,
memcpy(key->val, tk, sizeof(key->val));
key->authenticated = authenticated;
key->ediv = ediv;
+ key->rand = rand;
key->enc_size = enc_size;
key->type = type;
- memcpy(key->rand, rand, sizeof(key->rand));
- if (!new_key)
- return 0;
+ return key;
+}
+
+struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type, u8 val[16], bdaddr_t *rpa)
+{
+ struct smp_irk *irk;
- if (type & HCI_SMP_LTK)
- mgmt_new_ltk(hdev, key, 1);
+ irk = hci_find_irk_by_addr(hdev, bdaddr, addr_type);
+ if (!irk) {
+ irk = kzalloc(sizeof(*irk), GFP_KERNEL);
+ if (!irk)
+ return NULL;
- return 0;
+ bacpy(&irk->bdaddr, bdaddr);
+ irk->addr_type = addr_type;
+
+ list_add(&irk->list, &hdev->identity_resolving_keys);
+ }
+
+ memcpy(irk->val, val, 16);
+ bacpy(&irk->rpa, rpa);
+
+ return irk;
}
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
@@ -2658,21 +3111,38 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
return 0;
}
-int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
{
struct smp_ltk *k, *tmp;
+ int removed = 0;
list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
- if (bacmp(bdaddr, &k->bdaddr))
+ if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type)
continue;
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
list_del(&k->list);
kfree(k);
+ removed++;
}
- return 0;
+ return removed ? 0 : -ENOENT;
+}
+
+void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type)
+{
+ struct smp_irk *k, *tmp;
+
+ list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
+ if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type)
+ continue;
+
+ BT_DBG("%s removing %pMR", hdev->name, bdaddr);
+
+ list_del(&k->list);
+ kfree(k);
+ }
}
/* HCI command timer function */
@@ -2721,7 +3191,7 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr)
return 0;
}
-int hci_remote_oob_data_clear(struct hci_dev *hdev)
+void hci_remote_oob_data_clear(struct hci_dev *hdev)
{
struct oob_data *data, *n;
@@ -2729,19 +3199,43 @@ int hci_remote_oob_data_clear(struct hci_dev *hdev)
list_del(&data->list);
kfree(data);
}
+}
+
+int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 *hash, u8 *randomizer)
+{
+ struct oob_data *data;
+
+ data = hci_find_remote_oob_data(hdev, bdaddr);
+ if (!data) {
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ bacpy(&data->bdaddr, bdaddr);
+ list_add(&data->list, &hdev->remote_oob_data);
+ }
+
+ memcpy(data->hash192, hash, sizeof(data->hash192));
+ memcpy(data->randomizer192, randomizer, sizeof(data->randomizer192));
+
+ memset(data->hash256, 0, sizeof(data->hash256));
+ memset(data->randomizer256, 0, sizeof(data->randomizer256));
+
+ BT_DBG("%s for %pMR", hdev->name, bdaddr);
return 0;
}
-int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
- u8 *randomizer)
+int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 *hash192, u8 *randomizer192,
+ u8 *hash256, u8 *randomizer256)
{
struct oob_data *data;
data = hci_find_remote_oob_data(hdev, bdaddr);
-
if (!data) {
- data = kmalloc(sizeof(*data), GFP_ATOMIC);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -2749,8 +3243,11 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
list_add(&data->list, &hdev->remote_oob_data);
}
- memcpy(data->hash, hash, sizeof(data->hash));
- memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
+ memcpy(data->hash192, hash192, sizeof(data->hash192));
+ memcpy(data->randomizer192, randomizer192, sizeof(data->randomizer192));
+
+ memcpy(data->hash256, hash256, sizeof(data->hash256));
+ memcpy(data->randomizer256, randomizer256, sizeof(data->randomizer256));
BT_DBG("%s for %pMR", hdev->name, bdaddr);
@@ -2770,7 +3267,7 @@ struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
return NULL;
}
-int hci_blacklist_clear(struct hci_dev *hdev)
+static void hci_blacklist_clear(struct hci_dev *hdev)
{
struct list_head *p, *n;
@@ -2780,8 +3277,6 @@ int hci_blacklist_clear(struct hci_dev *hdev)
list_del(p);
kfree(b);
}
-
- return 0;
}
int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
@@ -2810,8 +3305,10 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
struct bdaddr_list *entry;
- if (!bacmp(bdaddr, BDADDR_ANY))
- return hci_blacklist_clear(hdev);
+ if (!bacmp(bdaddr, BDADDR_ANY)) {
+ hci_blacklist_clear(hdev);
+ return 0;
+ }
entry = hci_blacklist_lookup(hdev, bdaddr, type);
if (!entry)
@@ -2823,6 +3320,262 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
return mgmt_device_unblocked(hdev, bdaddr, type);
}
+struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev,
+ bdaddr_t *bdaddr, u8 type)
+{
+ struct bdaddr_list *b;
+
+ list_for_each_entry(b, &hdev->le_white_list, list) {
+ if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type)
+ return b;
+ }
+
+ return NULL;
+}
+
+void hci_white_list_clear(struct hci_dev *hdev)
+{
+ struct list_head *p, *n;
+
+ list_for_each_safe(p, n, &hdev->le_white_list) {
+ struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list);
+
+ list_del(p);
+ kfree(b);
+ }
+}
+
+int hci_white_list_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
+{
+ struct bdaddr_list *entry;
+
+ if (!bacmp(bdaddr, BDADDR_ANY))
+ return -EBADF;
+
+ entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ bacpy(&entry->bdaddr, bdaddr);
+ entry->bdaddr_type = type;
+
+ list_add(&entry->list, &hdev->le_white_list);
+
+ return 0;
+}
+
+int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
+{
+ struct bdaddr_list *entry;
+
+ if (!bacmp(bdaddr, BDADDR_ANY))
+ return -EBADF;
+
+ entry = hci_white_list_lookup(hdev, bdaddr, type);
+ if (!entry)
+ return -ENOENT;
+
+ list_del(&entry->list);
+ kfree(entry);
+
+ return 0;
+}
+
+/* This function requires the caller holds hdev->lock */
+struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
+ bdaddr_t *addr, u8 addr_type)
+{
+ struct hci_conn_params *params;
+
+ list_for_each_entry(params, &hdev->le_conn_params, list) {
+ if (bacmp(&params->addr, addr) == 0 &&
+ params->addr_type == addr_type) {
+ return params;
+ }
+ }
+
+ return NULL;
+}
+
+static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
+{
+ struct hci_conn *conn;
+
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
+ if (!conn)
+ return false;
+
+ if (conn->dst_type != type)
+ return false;
+
+ if (conn->state != BT_CONNECTED)
+ return false;
+
+ return true;
+}
+
+static bool is_identity_address(bdaddr_t *addr, u8 addr_type)
+{
+ if (addr_type == ADDR_LE_DEV_PUBLIC)
+ return true;
+
+ /* Check for Random Static address type */
+ if ((addr->b[5] & 0xc0) == 0xc0)
+ return true;
+
+ return false;
+}
+
+/* This function requires the caller holds hdev->lock */
+int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
+ u8 auto_connect, u16 conn_min_interval,
+ u16 conn_max_interval)
+{
+ struct hci_conn_params *params;
+
+ if (!is_identity_address(addr, addr_type))
+ return -EINVAL;
+
+ params = hci_conn_params_lookup(hdev, addr, addr_type);
+ if (params)
+ goto update;
+
+ params = kzalloc(sizeof(*params), GFP_KERNEL);
+ if (!params) {
+ BT_ERR("Out of memory");
+ return -ENOMEM;
+ }
+
+ bacpy(&params->addr, addr);
+ params->addr_type = addr_type;
+
+ list_add(&params->list, &hdev->le_conn_params);
+
+update:
+ params->conn_min_interval = conn_min_interval;
+ params->conn_max_interval = conn_max_interval;
+ params->auto_connect = auto_connect;
+
+ switch (auto_connect) {
+ case HCI_AUTO_CONN_DISABLED:
+ case HCI_AUTO_CONN_LINK_LOSS:
+ hci_pend_le_conn_del(hdev, addr, addr_type);
+ break;
+ case HCI_AUTO_CONN_ALWAYS:
+ if (!is_connected(hdev, addr, addr_type))
+ hci_pend_le_conn_add(hdev, addr, addr_type);
+ break;
+ }
+
+ BT_DBG("addr %pMR (type %u) auto_connect %u conn_min_interval 0x%.4x "
+ "conn_max_interval 0x%.4x", addr, addr_type, auto_connect,
+ conn_min_interval, conn_max_interval);
+
+ return 0;
+}
+
+/* This function requires the caller holds hdev->lock */
+void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type)
+{
+ struct hci_conn_params *params;
+
+ params = hci_conn_params_lookup(hdev, addr, addr_type);
+ if (!params)
+ return;
+
+ hci_pend_le_conn_del(hdev, addr, addr_type);
+
+ list_del(&params->list);
+ kfree(params);
+
+ BT_DBG("addr %pMR (type %u)", addr, addr_type);
+}
+
+/* This function requires the caller holds hdev->lock */
+void hci_conn_params_clear(struct hci_dev *hdev)
+{
+ struct hci_conn_params *params, *tmp;
+
+ list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) {
+ list_del(&params->list);
+ kfree(params);
+ }
+
+ BT_DBG("All LE connection parameters were removed");
+}
+
+/* This function requires the caller holds hdev->lock */
+struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev,
+ bdaddr_t *addr, u8 addr_type)
+{
+ struct bdaddr_list *entry;
+
+ list_for_each_entry(entry, &hdev->pend_le_conns, list) {
+ if (bacmp(&entry->bdaddr, addr) == 0 &&
+ entry->bdaddr_type == addr_type)
+ return entry;
+ }
+
+ return NULL;
+}
+
+/* This function requires the caller holds hdev->lock */
+void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type)
+{
+ struct bdaddr_list *entry;
+
+ entry = hci_pend_le_conn_lookup(hdev, addr, addr_type);
+ if (entry)
+ goto done;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ BT_ERR("Out of memory");
+ return;
+ }
+
+ bacpy(&entry->bdaddr, addr);
+ entry->bdaddr_type = addr_type;
+
+ list_add(&entry->list, &hdev->pend_le_conns);
+
+ BT_DBG("addr %pMR (type %u)", addr, addr_type);
+
+done:
+ hci_update_background_scan(hdev);
+}
+
+/* This function requires the caller holds hdev->lock */
+void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type)
+{
+ struct bdaddr_list *entry;
+
+ entry = hci_pend_le_conn_lookup(hdev, addr, addr_type);
+ if (!entry)
+ goto done;
+
+ list_del(&entry->list);
+ kfree(entry);
+
+ BT_DBG("addr %pMR (type %u)", addr, addr_type);
+
+done:
+ hci_update_background_scan(hdev);
+}
+
+/* This function requires the caller holds hdev->lock */
+void hci_pend_le_conns_clear(struct hci_dev *hdev)
+{
+ struct bdaddr_list *entry, *tmp;
+
+ list_for_each_entry_safe(entry, tmp, &hdev->pend_le_conns, list) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+
+ BT_DBG("All LE pending connections cleared");
+}
+
static void inquiry_complete(struct hci_dev *hdev, u8 status)
{
if (status) {
@@ -2882,7 +3635,6 @@ 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_cp_le_set_scan_enable cp;
struct hci_request req;
int err;
@@ -2890,15 +3642,128 @@ static void le_scan_disable_work(struct work_struct *work)
hci_req_init(&req, hdev);
- memset(&cp, 0, sizeof(cp));
- cp.enable = LE_SCAN_DISABLE;
- hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+ 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 set_random_addr(struct hci_request *req, bdaddr_t *rpa)
+{
+ struct hci_dev *hdev = req->hdev;
+
+ /* If we're advertising or initiating an LE connection we can't
+ * go ahead and change the random address at this time. This is
+ * because the eventual initiator address used for the
+ * subsequently created connection will be undefined (some
+ * controllers use the new address and others the one we had
+ * when the operation started).
+ *
+ * In this kind of scenario skip the update and let the random
+ * address be updated at the next cycle.
+ */
+ if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) ||
+ hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) {
+ BT_DBG("Deferring random address update");
+ return;
+ }
+
+ hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa);
+}
+
+int hci_update_random_address(struct hci_request *req, bool require_privacy,
+ u8 *own_addr_type)
+{
+ struct hci_dev *hdev = req->hdev;
+ int err;
+
+ /* If privacy is enabled use a resolvable private address. If
+ * current RPA has expired or there is something else than
+ * the current RPA in use, then generate a new one.
+ */
+ if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) {
+ int to;
+
+ *own_addr_type = ADDR_LE_DEV_RANDOM;
+
+ if (!test_and_clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags) &&
+ !bacmp(&hdev->random_addr, &hdev->rpa))
+ return 0;
+
+ err = smp_generate_rpa(hdev->tfm_aes, hdev->irk, &hdev->rpa);
+ if (err < 0) {
+ BT_ERR("%s failed to generate new RPA", hdev->name);
+ return err;
+ }
+
+ set_random_addr(req, &hdev->rpa);
+
+ to = msecs_to_jiffies(hdev->rpa_timeout * 1000);
+ queue_delayed_work(hdev->workqueue, &hdev->rpa_expired, to);
+
+ return 0;
+ }
+
+ /* In case of required privacy without resolvable private address,
+ * use an unresolvable private address. This is useful for active
+ * scanning and non-connectable advertising.
+ */
+ if (require_privacy) {
+ bdaddr_t urpa;
+
+ get_random_bytes(&urpa, 6);
+ urpa.b[5] &= 0x3f; /* Clear two most significant bits */
+
+ *own_addr_type = ADDR_LE_DEV_RANDOM;
+ set_random_addr(req, &urpa);
+ return 0;
+ }
+
+ /* If forcing static address is in use or there is no public
+ * address use the static address as random address (but skip
+ * the HCI command if the current random address is already the
+ * static one.
+ */
+ if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) ||
+ !bacmp(&hdev->bdaddr, BDADDR_ANY)) {
+ *own_addr_type = ADDR_LE_DEV_RANDOM;
+ if (bacmp(&hdev->static_addr, &hdev->random_addr))
+ hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
+ &hdev->static_addr);
+ return 0;
+ }
+
+ /* Neither privacy nor static address is being used so use a
+ * public address.
+ */
+ *own_addr_type = ADDR_LE_DEV_PUBLIC;
+
+ return 0;
+}
+
+/* Copy the Identity Address of the controller.
+ *
+ * If the controller has a public BD_ADDR, then by default use that one.
+ * If this is a LE only controller without a public address, default to
+ * the static random address.
+ *
+ * For debugging purposes it is possible to force controllers with a
+ * public address to use the static random address instead.
+ */
+void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 *bdaddr_type)
+{
+ if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) ||
+ !bacmp(&hdev->bdaddr, BDADDR_ANY)) {
+ bacpy(bdaddr, &hdev->static_addr);
+ *bdaddr_type = ADDR_LE_DEV_RANDOM;
+ } else {
+ bacpy(bdaddr, &hdev->bdaddr);
+ *bdaddr_type = ADDR_LE_DEV_PUBLIC;
+ }
+}
+
/* Alloc HCI device */
struct hci_dev *hci_alloc_dev(void)
{
@@ -2919,11 +3784,14 @@ struct hci_dev *hci_alloc_dev(void)
hdev->sniff_max_interval = 800;
hdev->sniff_min_interval = 80;
+ hdev->le_adv_channel_map = 0x07;
hdev->le_scan_interval = 0x0060;
hdev->le_scan_window = 0x0030;
hdev->le_conn_min_interval = 0x0028;
hdev->le_conn_max_interval = 0x0038;
+ hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT;
+
mutex_init(&hdev->lock);
mutex_init(&hdev->req_lock);
@@ -2932,7 +3800,11 @@ struct hci_dev *hci_alloc_dev(void)
INIT_LIST_HEAD(&hdev->uuids);
INIT_LIST_HEAD(&hdev->link_keys);
INIT_LIST_HEAD(&hdev->long_term_keys);
+ INIT_LIST_HEAD(&hdev->identity_resolving_keys);
INIT_LIST_HEAD(&hdev->remote_oob_data);
+ INIT_LIST_HEAD(&hdev->le_white_list);
+ INIT_LIST_HEAD(&hdev->le_conn_params);
+ INIT_LIST_HEAD(&hdev->pend_le_conns);
INIT_LIST_HEAD(&hdev->conn_hash.list);
INIT_WORK(&hdev->rx_work, hci_rx_work);
@@ -3017,9 +3889,18 @@ int hci_register_dev(struct hci_dev *hdev)
dev_set_name(&hdev->dev, "%s", hdev->name);
+ hdev->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(hdev->tfm_aes)) {
+ BT_ERR("Unable to create crypto context");
+ error = PTR_ERR(hdev->tfm_aes);
+ hdev->tfm_aes = NULL;
+ goto err_wqueue;
+ }
+
error = device_add(&hdev->dev);
if (error < 0)
- goto err_wqueue;
+ goto err_tfm;
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops,
@@ -3055,6 +3936,8 @@ int hci_register_dev(struct hci_dev *hdev)
return id;
+err_tfm:
+ crypto_free_blkcipher(hdev->tfm_aes);
err_wqueue:
destroy_workqueue(hdev->workqueue);
destroy_workqueue(hdev->req_workqueue);
@@ -3105,6 +3988,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
rfkill_destroy(hdev->rfkill);
}
+ if (hdev->tfm_aes)
+ crypto_free_blkcipher(hdev->tfm_aes);
+
device_del(&hdev->dev);
debugfs_remove_recursive(hdev->debugfs);
@@ -3117,7 +4003,11 @@ void hci_unregister_dev(struct hci_dev *hdev)
hci_uuids_clear(hdev);
hci_link_keys_clear(hdev);
hci_smp_ltks_clear(hdev);
+ hci_smp_irks_clear(hdev);
hci_remote_oob_data_clear(hdev);
+ hci_white_list_clear(hdev);
+ hci_conn_params_clear(hdev);
+ hci_pend_le_conns_clear(hdev);
hci_dev_unlock(hdev);
hci_dev_put(hdev);
@@ -4345,3 +5235,104 @@ static void hci_cmd_work(struct work_struct *work)
}
}
}
+
+void hci_req_add_le_scan_disable(struct hci_request *req)
+{
+ struct hci_cp_le_set_scan_enable cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.enable = LE_SCAN_DISABLE;
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+}
+
+void hci_req_add_le_passive_scan(struct hci_request *req)
+{
+ struct hci_cp_le_set_scan_param param_cp;
+ struct hci_cp_le_set_scan_enable enable_cp;
+ struct hci_dev *hdev = req->hdev;
+ u8 own_addr_type;
+
+ /* Set require_privacy to true to avoid identification from
+ * unknown peer devices. Since this is passive scanning, no
+ * SCAN_REQ using the local identity should be sent. Mandating
+ * privacy is just an extra precaution.
+ */
+ if (hci_update_random_address(req, true, &own_addr_type))
+ return;
+
+ memset(&param_cp, 0, sizeof(param_cp));
+ param_cp.type = LE_SCAN_PASSIVE;
+ param_cp.interval = cpu_to_le16(hdev->le_scan_interval);
+ param_cp.window = cpu_to_le16(hdev->le_scan_window);
+ 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);
+}
+
+static void update_background_scan_complete(struct hci_dev *hdev, u8 status)
+{
+ if (status)
+ BT_DBG("HCI request failed to update background scanning: "
+ "status 0x%2.2x", status);
+}
+
+/* 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_dev *hdev)
+{
+ struct hci_request req;
+ struct hci_conn *conn;
+ int err;
+
+ hci_req_init(&req, hdev);
+
+ if (list_empty(&hdev->pend_le_conns)) {
+ /* If there is no pending LE connections, we should stop
+ * the background scanning.
+ */
+
+ /* If controller is not scanning we are done. */
+ if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+ 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.
+ */
+ conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+ if (conn)
+ return;
+
+ /* If controller is currently scanning, we stop it to ensure we
+ * don't miss any advertising (due to duplicates filter).
+ */
+ if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+ hci_req_add_le_scan_disable(&req);
+
+ hci_req_add_le_passive_scan(&req);
+
+ BT_DBG("%s starting background scanning", hdev->name);
+ }
+
+ err = hci_req_run(&req, update_background_scan_complete);
+ if (err)
+ BT_ERR("Failed to run HCI request: err %d", err);
+}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 5f812455a450..49774912cb01 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -199,6 +199,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
memset(hdev->scan_rsp_data, 0, sizeof(hdev->scan_rsp_data));
hdev->scan_rsp_data_len = 0;
+ hdev->le_scan_type = LE_SCAN_PASSIVE;
+
hdev->ssp_debug_mode = 0;
}
@@ -461,6 +463,34 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
}
}
+static void hci_cc_write_sc_support(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ u8 status = *((u8 *) skb->data);
+ struct hci_cp_write_sc_support *sent;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SC_SUPPORT);
+ if (!sent)
+ return;
+
+ if (!status) {
+ if (sent->support)
+ hdev->features[1][0] |= LMP_HOST_SC;
+ else
+ hdev->features[1][0] &= ~LMP_HOST_SC;
+ }
+
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_sc_enable_complete(hdev, sent->support, status);
+ else if (!status) {
+ if (sent->support)
+ set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+ else
+ clear_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+ }
+}
+
static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_rp_read_local_version *rp = (void *) skb->data;
@@ -904,16 +934,50 @@ static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev,
hci_dev_unlock(hdev);
}
-static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
- struct sk_buff *skb)
+static void hci_cc_read_local_oob_data(struct hci_dev *hdev,
+ struct sk_buff *skb)
{
struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
hci_dev_lock(hdev);
- mgmt_read_local_oob_data_reply_complete(hdev, rp->hash,
- rp->randomizer, rp->status);
+ mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->randomizer,
+ NULL, NULL, rp->status);
+ hci_dev_unlock(hdev);
+}
+
+static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+ hci_dev_lock(hdev);
+ mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->randomizer192,
+ rp->hash256, rp->randomizer256,
+ rp->status);
+ hci_dev_unlock(hdev);
+}
+
+
+static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ __u8 status = *((__u8 *) skb->data);
+ bdaddr_t *sent;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_RANDOM_ADDR);
+ if (!sent)
+ return;
+
+ hci_dev_lock(hdev);
+
+ if (!status)
+ bacpy(&hdev->random_addr, sent);
+
hci_dev_unlock(hdev);
}
@@ -929,12 +993,27 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (!status) {
- if (*sent)
- set_bit(HCI_ADVERTISING, &hdev->dev_flags);
- else
- clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
- }
+ if (!status)
+ mgmt_advertising(hdev, *sent);
+
+ hci_dev_unlock(hdev);
+}
+
+static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_cp_le_set_scan_param *cp;
+ __u8 status = *((__u8 *) skb->data);
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_PARAM);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ if (!status)
+ hdev->le_scan_type = cp->type;
hci_dev_unlock(hdev);
}
@@ -960,7 +1039,19 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
break;
case LE_SCAN_DISABLE:
+ /* Cancel this timer so that we don't try to disable scanning
+ * when it's already disabled.
+ */
+ cancel_delayed_work(&hdev->le_scan_disable);
+
clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
+ /* The HCI_LE_SCAN_INTERRUPTED flag indicates that we
+ * interrupted scanning due to a connect request. Mark
+ * therefore discovery as stopped.
+ */
+ if (test_and_clear_bit(HCI_LE_SCAN_INTERRUPTED,
+ &hdev->dev_flags))
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
break;
default:
@@ -980,6 +1071,49 @@ static void hci_cc_le_read_white_list_size(struct hci_dev *hdev,
hdev->le_white_list_size = rp->size;
}
+static void hci_cc_le_clear_white_list(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ __u8 status = *((__u8 *) skb->data);
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ if (!status)
+ hci_white_list_clear(hdev);
+}
+
+static void hci_cc_le_add_to_white_list(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_cp_le_add_to_white_list *sent;
+ __u8 status = *((__u8 *) skb->data);
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ sent = hci_sent_cmd_data(hdev, HCI_OP_LE_ADD_TO_WHITE_LIST);
+ if (!sent)
+ return;
+
+ if (!status)
+ hci_white_list_add(hdev, &sent->bdaddr, sent->bdaddr_type);
+}
+
+static void hci_cc_le_del_from_white_list(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_cp_le_del_from_white_list *sent;
+ __u8 status = *((__u8 *) skb->data);
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ sent = hci_sent_cmd_data(hdev, HCI_OP_LE_DEL_FROM_WHITE_LIST);
+ if (!sent)
+ return;
+
+ if (!status)
+ hci_white_list_del(hdev, &sent->bdaddr, sent->bdaddr_type);
+}
+
static void hci_cc_le_read_supported_states(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -1020,6 +1154,25 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
}
}
+static void hci_cc_set_adv_param(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_cp_le_set_adv_param *cp;
+ u8 status = *((u8 *) skb->data);
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ if (status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_PARAM);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+ hdev->adv_addr_type = cp->own_address_type;
+ hci_dev_unlock(hdev);
+}
+
static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -1185,9 +1338,12 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev,
return 0;
/* Only request authentication for SSP connections or non-SSP
- * devices with sec_level HIGH or if MITM protection is requested */
+ * devices with sec_level MEDIUM or HIGH or if MITM protection
+ * is requested.
+ */
if (!hci_conn_ssp_enabled(conn) && !(conn->auth_type & 0x01) &&
- conn->pending_sec_level != BT_SECURITY_HIGH)
+ conn->pending_sec_level != BT_SECURITY_HIGH &&
+ conn->pending_sec_level != BT_SECURITY_MEDIUM)
return 0;
return 1;
@@ -1518,6 +1674,87 @@ static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status)
amp_write_remote_assoc(hdev, cp->phy_handle);
}
+static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_le_create_conn *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ /* All connection failure handling is taken care of by the
+ * hci_le_conn_failed function which is triggered by the HCI
+ * request completion callbacks used for connecting.
+ */
+ if (status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr);
+ if (!conn)
+ goto unlock;
+
+ /* Store the initiator and responder address information which
+ * is needed for SMP. These values will not change during the
+ * lifetime of the connection.
+ */
+ conn->init_addr_type = cp->own_address_type;
+ if (cp->own_address_type == ADDR_LE_DEV_RANDOM)
+ bacpy(&conn->init_addr, &hdev->random_addr);
+ else
+ bacpy(&conn->init_addr, &hdev->bdaddr);
+
+ conn->resp_addr_type = cp->peer_addr_type;
+ bacpy(&conn->resp_addr, &cp->peer_addr);
+
+ /* We don't want the connection attempt to stick around
+ * indefinitely since LE doesn't have a page timeout concept
+ * like BR/EDR. Set a timer for any connection that doesn't use
+ * the white list for connecting.
+ */
+ if (cp->filter_policy == HCI_LE_USE_PEER_ADDR)
+ queue_delayed_work(conn->hdev->workqueue,
+ &conn->le_conn_timeout,
+ HCI_LE_CONN_TIMEOUT);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_le_start_enc *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ hci_dev_lock(hdev);
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_START_ENC);
+ if (!cp)
+ goto unlock;
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (!conn)
+ goto unlock;
+
+ if (conn->state != BT_CONNECTED)
+ goto unlock;
+
+ hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE);
+ hci_conn_drop(conn);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -1659,7 +1896,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
} else {
conn->state = BT_CLOSED;
if (conn->type == ACL_LINK)
- mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
+ mgmt_connect_failed(hdev, &conn->dst, conn->type,
conn->dst_type, ev->status);
}
@@ -1738,9 +1975,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
bacpy(&cp.bdaddr, &ev->bdaddr);
cp.pkt_type = cpu_to_le16(conn->pkt_type);
- cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
- cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
- cp.max_latency = __constant_cpu_to_le16(0xffff);
+ cp.tx_bandwidth = cpu_to_le32(0x00001f40);
+ cp.rx_bandwidth = cpu_to_le32(0x00001f40);
+ cp.max_latency = cpu_to_le16(0xffff);
cp.content_format = cpu_to_le16(hdev->voice_setting);
cp.retrans_effort = 0xff;
@@ -1780,7 +2017,9 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_disconn_complete *ev = (void *) skb->data;
u8 reason = hci_to_mgmt_reason(ev->reason);
+ struct hci_conn_params *params;
struct hci_conn *conn;
+ bool mgmt_connected;
u8 type;
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
@@ -1799,13 +2038,30 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn->state = BT_CLOSED;
- if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_disconnected(hdev, &conn->dst, conn->type,
- conn->dst_type, reason);
+ mgmt_connected = test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags);
+ mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type,
+ reason, mgmt_connected);
if (conn->type == ACL_LINK && conn->flush_key)
hci_remove_link_key(hdev, &conn->dst);
+ params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
+ if (params) {
+ switch (params->auto_connect) {
+ case HCI_AUTO_CONN_LINK_LOSS:
+ if (ev->reason != HCI_ERROR_CONNECTION_TIMEOUT)
+ break;
+ /* Fall through */
+
+ case HCI_AUTO_CONN_ALWAYS:
+ hci_pend_le_conn_add(hdev, &conn->dst, conn->dst_type);
+ break;
+
+ default:
+ break;
+ }
+ }
+
type = conn->type;
hci_proto_disconn_cfm(conn, ev->reason);
@@ -1943,34 +2199,57 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
- if (conn) {
- if (!ev->status) {
- if (ev->encrypt) {
- /* Encryption implies authentication */
- conn->link_mode |= HCI_LM_AUTH;
- conn->link_mode |= HCI_LM_ENCRYPT;
- conn->sec_level = conn->pending_sec_level;
- } else
- conn->link_mode &= ~HCI_LM_ENCRYPT;
+ if (!conn)
+ goto unlock;
+
+ if (!ev->status) {
+ if (ev->encrypt) {
+ /* Encryption implies authentication */
+ conn->link_mode |= HCI_LM_AUTH;
+ conn->link_mode |= HCI_LM_ENCRYPT;
+ conn->sec_level = conn->pending_sec_level;
+
+ /* P-256 authentication key implies FIPS */
+ if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256)
+ conn->link_mode |= HCI_LM_FIPS;
+
+ if ((conn->type == ACL_LINK && ev->encrypt == 0x02) ||
+ conn->type == LE_LINK)
+ set_bit(HCI_CONN_AES_CCM, &conn->flags);
+ } else {
+ conn->link_mode &= ~HCI_LM_ENCRYPT;
+ clear_bit(HCI_CONN_AES_CCM, &conn->flags);
}
+ }
- clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
- if (ev->status && conn->state == BT_CONNECTED) {
- hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE);
+ if (ev->status && conn->state == BT_CONNECTED) {
+ hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE);
+ hci_conn_drop(conn);
+ goto unlock;
+ }
+
+ if (conn->state == BT_CONFIG) {
+ if (!ev->status)
+ conn->state = BT_CONNECTED;
+
+ /* In Secure Connections Only mode, do not allow any
+ * connections that are not encrypted with AES-CCM
+ * using a P-256 authenticated combination key.
+ */
+ if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) &&
+ (!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
+ conn->key_type != HCI_LK_AUTH_COMBINATION_P256)) {
+ hci_proto_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE);
hci_conn_drop(conn);
goto unlock;
}
- if (conn->state == BT_CONFIG) {
- if (!ev->status)
- conn->state = BT_CONNECTED;
-
- hci_proto_connect_cfm(conn, ev->status);
- hci_conn_drop(conn);
- } else
- hci_encrypt_cfm(conn, ev->status, ev->encrypt);
- }
+ hci_proto_connect_cfm(conn, ev->status);
+ hci_conn_drop(conn);
+ } else
+ hci_encrypt_cfm(conn, ev->status, ev->encrypt);
unlock:
hci_dev_unlock(hdev);
@@ -2144,6 +2423,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_write_ssp_mode(hdev, skb);
break;
+ case HCI_OP_WRITE_SC_SUPPORT:
+ hci_cc_write_sc_support(hdev, skb);
+ break;
+
case HCI_OP_READ_LOCAL_VERSION:
hci_cc_read_local_version(hdev, skb);
break;
@@ -2213,7 +2496,11 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
break;
case HCI_OP_READ_LOCAL_OOB_DATA:
- hci_cc_read_local_oob_data_reply(hdev, skb);
+ hci_cc_read_local_oob_data(hdev, skb);
+ break;
+
+ case HCI_OP_READ_LOCAL_OOB_EXT_DATA:
+ hci_cc_read_local_oob_ext_data(hdev, skb);
break;
case HCI_OP_LE_READ_BUFFER_SIZE:
@@ -2244,10 +2531,18 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_user_passkey_neg_reply(hdev, skb);
break;
+ case HCI_OP_LE_SET_RANDOM_ADDR:
+ hci_cc_le_set_random_addr(hdev, skb);
+ break;
+
case HCI_OP_LE_SET_ADV_ENABLE:
hci_cc_le_set_adv_enable(hdev, skb);
break;
+ case HCI_OP_LE_SET_SCAN_PARAM:
+ hci_cc_le_set_scan_param(hdev, skb);
+ break;
+
case HCI_OP_LE_SET_SCAN_ENABLE:
hci_cc_le_set_scan_enable(hdev, skb);
break;
@@ -2256,6 +2551,18 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_le_read_white_list_size(hdev, skb);
break;
+ case HCI_OP_LE_CLEAR_WHITE_LIST:
+ hci_cc_le_clear_white_list(hdev, skb);
+ break;
+
+ case HCI_OP_LE_ADD_TO_WHITE_LIST:
+ hci_cc_le_add_to_white_list(hdev, skb);
+ break;
+
+ case HCI_OP_LE_DEL_FROM_WHITE_LIST:
+ hci_cc_le_del_from_white_list(hdev, skb);
+ break;
+
case HCI_OP_LE_READ_SUPPORTED_STATES:
hci_cc_le_read_supported_states(hdev, skb);
break;
@@ -2264,6 +2571,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_write_le_host_supported(hdev, skb);
break;
+ case HCI_OP_LE_SET_ADV_PARAM:
+ hci_cc_set_adv_param(hdev, skb);
+ break;
+
case HCI_OP_WRITE_REMOTE_AMP_ASSOC:
hci_cc_write_remote_amp_assoc(hdev, skb);
break;
@@ -2351,6 +2662,14 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_accept_phylink(hdev, ev->status);
break;
+ case HCI_OP_LE_CREATE_CONN:
+ hci_cs_le_create_conn(hdev, ev->status);
+ break;
+
+ case HCI_OP_LE_START_ENC:
+ hci_cs_le_start_enc(hdev, ev->status);
+ break;
+
default:
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
break;
@@ -2630,7 +2949,8 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (conn) {
- if (key->type == HCI_LK_UNAUTH_COMBINATION &&
+ if ((key->type == HCI_LK_UNAUTH_COMBINATION_P192 ||
+ key->type == HCI_LK_UNAUTH_COMBINATION_P256) &&
conn->auth_type != 0xff && (conn->auth_type & 0x01)) {
BT_DBG("%s ignoring unauthenticated key", hdev->name);
goto not_found;
@@ -2844,6 +3164,9 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev,
* features do not indicate SSP support */
clear_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
}
+
+ if (ev->features[0] & LMP_HOST_SC)
+ set_bit(HCI_CONN_SC_ENABLED, &conn->flags);
}
if (conn->state != BT_CONFIG)
@@ -2905,6 +3228,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
case 0x1c: /* SCO interval rejected */
case 0x1a: /* Unsupported Remote Feature */
case 0x1f: /* Unspecified error */
+ case 0x20: /* Unsupported LMP Parameter value */
if (conn->out) {
conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
(hdev->esco_type & EDR_ESCO_MASK);
@@ -3194,8 +3518,8 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
}
confirm:
- mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, ev->passkey,
- confirm_hint);
+ mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0,
+ le32_to_cpu(ev->passkey), confirm_hint);
unlock:
hci_dev_unlock(hdev);
@@ -3337,20 +3661,36 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
if (data) {
- struct hci_cp_remote_oob_data_reply cp;
+ if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+ struct hci_cp_remote_oob_ext_data_reply cp;
- bacpy(&cp.bdaddr, &ev->bdaddr);
- memcpy(cp.hash, data->hash, sizeof(cp.hash));
- memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer));
+ bacpy(&cp.bdaddr, &ev->bdaddr);
+ memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
+ memcpy(cp.randomizer192, data->randomizer192,
+ sizeof(cp.randomizer192));
+ memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
+ memcpy(cp.randomizer256, data->randomizer256,
+ sizeof(cp.randomizer256));
+
+ hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
+ sizeof(cp), &cp);
+ } else {
+ struct hci_cp_remote_oob_data_reply cp;
+
+ bacpy(&cp.bdaddr, &ev->bdaddr);
+ memcpy(cp.hash, data->hash192, sizeof(cp.hash));
+ memcpy(cp.randomizer, data->randomizer192,
+ sizeof(cp.randomizer));
- hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, sizeof(cp),
- &cp);
+ hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
+ sizeof(cp), &cp);
+ }
} else {
struct hci_cp_remote_oob_data_neg_reply cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
- hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY, sizeof(cp),
- &cp);
+ hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY,
+ sizeof(cp), &cp);
}
unlock:
@@ -3484,6 +3824,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_le_conn_complete *ev = (void *) skb->data;
struct hci_conn *conn;
+ struct smp_irk *irk;
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
@@ -3514,19 +3855,70 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn->out = true;
conn->link_mode |= HCI_LM_MASTER;
}
+
+ /* If we didn't have a hci_conn object previously
+ * but we're in master role this must be something
+ * initiated using a white list. Since white list based
+ * connections are not "first class citizens" we don't
+ * have full tracking of them. Therefore, we go ahead
+ * with a "best effort" approach of determining the
+ * initiator address based on the HCI_PRIVACY flag.
+ */
+ if (conn->out) {
+ conn->resp_addr_type = ev->bdaddr_type;
+ bacpy(&conn->resp_addr, &ev->bdaddr);
+ if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) {
+ conn->init_addr_type = ADDR_LE_DEV_RANDOM;
+ bacpy(&conn->init_addr, &hdev->rpa);
+ } else {
+ hci_copy_identity_address(hdev,
+ &conn->init_addr,
+ &conn->init_addr_type);
+ }
+ } else {
+ /* Set the responder (our side) address type based on
+ * the advertising address type.
+ */
+ conn->resp_addr_type = hdev->adv_addr_type;
+ if (hdev->adv_addr_type == ADDR_LE_DEV_RANDOM)
+ bacpy(&conn->resp_addr, &hdev->random_addr);
+ else
+ bacpy(&conn->resp_addr, &hdev->bdaddr);
+
+ conn->init_addr_type = ev->bdaddr_type;
+ bacpy(&conn->init_addr, &ev->bdaddr);
+ }
+ } else {
+ cancel_delayed_work(&conn->le_conn_timeout);
+ }
+
+ /* Ensure that the hci_conn contains the identity address type
+ * regardless of which address the connection was made with.
+ */
+ hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
+
+ /* Lookup the identity address from the stored connection
+ * address and address type.
+ *
+ * When establishing connections to an identity address, the
+ * connection procedure will store the resolvable random
+ * address first. Now if it can be converted back into the
+ * identity address, start using the identity address from
+ * now on.
+ */
+ irk = hci_get_irk(hdev, &conn->dst, conn->dst_type);
+ if (irk) {
+ bacpy(&conn->dst, &irk->bdaddr);
+ conn->dst_type = irk->addr_type;
}
if (ev->status) {
- mgmt_connect_failed(hdev, &conn->dst, conn->type,
- conn->dst_type, ev->status);
- hci_proto_connect_cfm(conn, ev->status);
- conn->state = BT_CLOSED;
- hci_conn_del(conn);
+ hci_le_conn_failed(conn, ev->status);
goto unlock;
}
if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &ev->bdaddr, conn->type,
+ mgmt_device_connected(hdev, &conn->dst, conn->type,
conn->dst_type, 0, NULL, 0, NULL);
conn->sec_level = BT_SECURITY_LOW;
@@ -3540,25 +3932,73 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_proto_connect_cfm(conn, ev->status);
+ hci_pend_le_conn_del(hdev, &conn->dst, conn->dst_type);
+
unlock:
hci_dev_unlock(hdev);
}
+/* This function requires the caller holds hdev->lock */
+static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
+ u8 addr_type)
+{
+ struct hci_conn *conn;
+ struct smp_irk *irk;
+
+ /* If this is a resolvable address, we should resolve it and then
+ * update address and address type variables.
+ */
+ irk = hci_get_irk(hdev, addr, addr_type);
+ if (irk) {
+ addr = &irk->bdaddr;
+ addr_type = irk->addr_type;
+ }
+
+ if (!hci_pend_le_conn_lookup(hdev, addr, addr_type))
+ return;
+
+ conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
+ HCI_AT_NO_BONDING);
+ if (!IS_ERR(conn))
+ return;
+
+ switch (PTR_ERR(conn)) {
+ case -EBUSY:
+ /* If hci_connect() returns -EBUSY it means there is already
+ * an LE connection attempt going on. Since controllers don't
+ * support more than one connection attempt at the time, we
+ * don't consider this an error case.
+ */
+ break;
+ default:
+ BT_DBG("Failed to connect: err %ld", PTR_ERR(conn));
+ }
+}
+
static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
u8 num_reports = skb->data[0];
void *ptr = &skb->data[1];
s8 rssi;
+ hci_dev_lock(hdev);
+
while (num_reports--) {
struct hci_ev_le_advertising_info *ev = ptr;
+ if (ev->evt_type == LE_ADV_IND ||
+ ev->evt_type == LE_ADV_DIRECT_IND)
+ check_pending_le_conn(hdev, &ev->bdaddr,
+ ev->bdaddr_type);
+
rssi = ev->data[ev->length];
mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type,
NULL, rssi, 0, 1, ev->data, ev->length);
ptr += sizeof(*ev) + ev->length + 1;
}
+
+ hci_dev_unlock(hdev);
}
static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
@@ -3577,7 +4017,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (conn == NULL)
goto not_found;
- ltk = hci_find_ltk(hdev, ev->ediv, ev->random);
+ ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->out);
if (ltk == NULL)
goto not_found;
@@ -3593,7 +4033,13 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
- if (ltk->type & HCI_SMP_STK) {
+ /* Ref. Bluetooth Core SPEC pages 1975 and 2004. STK is a
+ * temporary key used to encrypt a connection following
+ * pairing. It is used during the Encrypted Session Setup to
+ * distribute the keys. Later, security can be re-established
+ * using a distributed LTK.
+ */
+ if (ltk->type == HCI_SMP_STK_SLAVE) {
list_del(&ltk->list);
kfree(ltk);
}
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 7552f9e3089c..b9a418e578e0 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -211,22 +211,22 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
switch (bt_cb(skb)->pkt_type) {
case HCI_COMMAND_PKT:
- opcode = __constant_cpu_to_le16(HCI_MON_COMMAND_PKT);
+ opcode = cpu_to_le16(HCI_MON_COMMAND_PKT);
break;
case HCI_EVENT_PKT:
- opcode = __constant_cpu_to_le16(HCI_MON_EVENT_PKT);
+ opcode = cpu_to_le16(HCI_MON_EVENT_PKT);
break;
case HCI_ACLDATA_PKT:
if (bt_cb(skb)->incoming)
- opcode = __constant_cpu_to_le16(HCI_MON_ACL_RX_PKT);
+ opcode = cpu_to_le16(HCI_MON_ACL_RX_PKT);
else
- opcode = __constant_cpu_to_le16(HCI_MON_ACL_TX_PKT);
+ opcode = cpu_to_le16(HCI_MON_ACL_TX_PKT);
break;
case HCI_SCODATA_PKT:
if (bt_cb(skb)->incoming)
- opcode = __constant_cpu_to_le16(HCI_MON_SCO_RX_PKT);
+ opcode = cpu_to_le16(HCI_MON_SCO_RX_PKT);
else
- opcode = __constant_cpu_to_le16(HCI_MON_SCO_TX_PKT);
+ opcode = cpu_to_le16(HCI_MON_SCO_TX_PKT);
break;
default:
return;
@@ -319,7 +319,7 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
bacpy(&ni->bdaddr, &hdev->bdaddr);
memcpy(ni->name, hdev->name, 8);
- opcode = __constant_cpu_to_le16(HCI_MON_NEW_INDEX);
+ opcode = cpu_to_le16(HCI_MON_NEW_INDEX);
break;
case HCI_DEV_UNREG:
@@ -327,7 +327,7 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
if (!skb)
return NULL;
- opcode = __constant_cpu_to_le16(HCI_MON_DEL_INDEX);
+ opcode = cpu_to_le16(HCI_MON_DEL_INDEX);
break;
default:
@@ -716,6 +716,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
err = hci_dev_open(hdev->id);
if (err) {
clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+ mgmt_index_added(hdev);
hci_dev_put(hdev);
goto done;
}
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 0b61250cfdf9..555982a78a58 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -49,14 +49,7 @@ static struct attribute *bt_link_attrs[] = {
NULL
};
-static struct attribute_group bt_link_group = {
- .attrs = bt_link_attrs,
-};
-
-static const struct attribute_group *bt_link_groups[] = {
- &bt_link_group,
- NULL
-};
+ATTRIBUTE_GROUPS(bt_link);
static void bt_link_release(struct device *dev)
{
@@ -182,14 +175,7 @@ static struct attribute *bt_host_attrs[] = {
NULL
};
-static struct attribute_group bt_host_group = {
- .attrs = bt_host_attrs,
-};
-
-static const struct attribute_group *bt_host_groups[] = {
- &bt_host_group,
- NULL
-};
+ATTRIBUTE_GROUPS(bt_host);
static void bt_host_release(struct device *dev)
{
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index d9fb93451442..8181ea4bc2f2 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -223,51 +223,6 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
input_sync(dev);
}
-static int hidp_send_report(struct hidp_session *session, struct hid_report *report)
-{
- unsigned char hdr;
- u8 *buf;
- int rsize, ret;
-
- buf = hid_alloc_report_buf(report, GFP_ATOMIC);
- if (!buf)
- return -EIO;
-
- hid_output_report(report, buf);
- hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
-
- rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0);
- ret = hidp_send_intr_message(session, hdr, buf, rsize);
-
- kfree(buf);
- return ret;
-}
-
-static int hidp_hidinput_event(struct input_dev *dev, unsigned int type,
- unsigned int code, int value)
-{
- struct hid_device *hid = input_get_drvdata(dev);
- struct hidp_session *session = hid->driver_data;
- struct hid_field *field;
- int offset;
-
- BT_DBG("session %p type %d code %d value %d",
- session, type, code, value);
-
- if (type != EV_LED)
- return -1;
-
- offset = hidinput_find_field(hid, type, code, &field);
- if (offset == -1) {
- hid_warn(dev, "event field not found\n");
- return -1;
- }
-
- hid_set_field(field, offset, value);
-
- return hidp_send_report(session, field->report);
-}
-
static int hidp_get_raw_report(struct hid_device *hid,
unsigned char report_number,
unsigned char *data, size_t count,
@@ -353,17 +308,24 @@ err:
return ret;
}
-static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
- unsigned char report_type)
+static int hidp_set_raw_report(struct hid_device *hid, unsigned char reportnum,
+ unsigned char *data, size_t count,
+ unsigned char report_type)
{
struct hidp_session *session = hid->driver_data;
int ret;
- if (report_type == HID_OUTPUT_REPORT) {
- report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
- return hidp_send_intr_message(session, report_type,
- data, count);
- } else if (report_type != HID_FEATURE_REPORT) {
+ switch (report_type) {
+ case HID_FEATURE_REPORT:
+ report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
+ break;
+ case HID_INPUT_REPORT:
+ report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_INPUT;
+ break;
+ case HID_OUTPUT_REPORT:
+ report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT;
+ break;
+ default:
return -EINVAL;
}
@@ -371,8 +333,8 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s
return -ERESTARTSYS;
/* Set up our wait, and send the report request to the device. */
+ data[0] = reportnum;
set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
- report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
ret = hidp_send_ctrl_message(session, report_type, data, count);
if (ret)
goto err;
@@ -411,6 +373,29 @@ err:
return ret;
}
+static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count)
+{
+ struct hidp_session *session = hid->driver_data;
+
+ return hidp_send_intr_message(session,
+ HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT,
+ data, count);
+}
+
+static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype,
+ int reqtype)
+{
+ switch (reqtype) {
+ case HID_REQ_GET_REPORT:
+ return hidp_get_raw_report(hid, reportnum, buf, len, rtype);
+ case HID_REQ_SET_REPORT:
+ return hidp_set_raw_report(hid, reportnum, buf, len, rtype);
+ default:
+ return -EIO;
+ }
+}
+
static void hidp_idle_timeout(unsigned long arg)
{
struct hidp_session *session = (struct hidp_session *) arg;
@@ -739,7 +724,8 @@ static struct hid_ll_driver hidp_hid_driver = {
.stop = hidp_stop,
.open = hidp_open,
.close = hidp_close,
- .hidinput_input_event = hidp_hidinput_event,
+ .raw_request = hidp_raw_request,
+ .output_report = hidp_output_report,
};
/* This function sets up the hid device. It does not add it
@@ -781,15 +767,15 @@ static int hidp_setup_hid(struct hidp_session *session,
snprintf(hid->phys, sizeof(hid->phys), "%pMR",
&l2cap_pi(session->ctrl_sock->sk)->chan->src);
+ /* NOTE: Some device modules depend on the dst address being stored in
+ * uniq. Please be aware of this before making changes to this behavior.
+ */
snprintf(hid->uniq, sizeof(hid->uniq), "%pMR",
&l2cap_pi(session->ctrl_sock->sk)->chan->dst);
hid->dev.parent = &session->conn->hcon->dev;
hid->ll_driver = &hidp_hid_driver;
- hid->hid_get_raw_report = hidp_get_raw_report;
- hid->hid_output_raw_report = hidp_output_raw_report;
-
/* True if device is blacklisted in drivers/hid/hid-core.c */
if (hid_ignore(hid)) {
hid_destroy_device(session->hid);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index b0ad2c752d73..a1e5bb7d06e8 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -42,6 +42,8 @@
#include "amp.h"
#include "6lowpan.h"
+#define LE_FLOWCTL_MAX_CREDITS 65535
+
bool disable_ertm;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
@@ -330,44 +332,20 @@ static inline bool l2cap_seq_list_contains(struct l2cap_seq_list *seq_list,
return seq_list->list[seq & seq_list->mask] != L2CAP_SEQ_LIST_CLEAR;
}
-static u16 l2cap_seq_list_remove(struct l2cap_seq_list *seq_list, u16 seq)
+static inline u16 l2cap_seq_list_pop(struct l2cap_seq_list *seq_list)
{
+ u16 seq = seq_list->head;
u16 mask = seq_list->mask;
- if (seq_list->head == L2CAP_SEQ_LIST_CLEAR) {
- /* In case someone tries to pop the head of an empty list */
- return L2CAP_SEQ_LIST_CLEAR;
- } else if (seq_list->head == seq) {
- /* Head can be removed in constant time */
- seq_list->head = seq_list->list[seq & mask];
- seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR;
-
- if (seq_list->head == L2CAP_SEQ_LIST_TAIL) {
- seq_list->head = L2CAP_SEQ_LIST_CLEAR;
- seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
- }
- } else {
- /* Walk the list to find the sequence number */
- u16 prev = seq_list->head;
- while (seq_list->list[prev & mask] != seq) {
- prev = seq_list->list[prev & mask];
- if (prev == L2CAP_SEQ_LIST_TAIL)
- return L2CAP_SEQ_LIST_CLEAR;
- }
+ seq_list->head = seq_list->list[seq & mask];
+ seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR;
- /* Unlink the number from the list and clear it */
- seq_list->list[prev & mask] = seq_list->list[seq & mask];
- seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR;
- if (seq_list->tail == seq)
- seq_list->tail = prev;
+ if (seq_list->head == L2CAP_SEQ_LIST_TAIL) {
+ seq_list->head = L2CAP_SEQ_LIST_CLEAR;
+ seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
}
- return seq;
-}
-static inline u16 l2cap_seq_list_pop(struct l2cap_seq_list *seq_list)
-{
- /* Remove the head in constant time */
- return l2cap_seq_list_remove(seq_list, seq_list->head);
+ return seq;
}
static void l2cap_seq_list_clear(struct l2cap_seq_list *seq_list)
@@ -506,7 +484,7 @@ static void l2cap_le_flowctl_init(struct l2cap_chan *chan)
chan->sdu_len = 0;
chan->tx_credits = 0;
chan->rx_credits = le_max_credits;
- chan->mps = min_t(u16, chan->imtu, L2CAP_LE_DEFAULT_MPS);
+ chan->mps = min_t(u16, chan->imtu, le_default_mps);
skb_queue_head_init(&chan->tx_q);
}
@@ -522,18 +500,10 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
switch (chan->chan_type) {
case L2CAP_CHAN_CONN_ORIENTED:
- if (conn->hcon->type == LE_LINK) {
- if (chan->dcid == L2CAP_CID_ATT) {
- chan->omtu = L2CAP_DEFAULT_MTU;
- chan->scid = L2CAP_CID_ATT;
- } else {
- chan->scid = l2cap_alloc_cid(conn);
- }
- } else {
- /* Alloc CID for connection-oriented socket */
- chan->scid = l2cap_alloc_cid(conn);
+ /* Alloc CID for connection-oriented socket */
+ chan->scid = l2cap_alloc_cid(conn);
+ if (conn->hcon->type == ACL_LINK)
chan->omtu = L2CAP_DEFAULT_MTU;
- }
break;
case L2CAP_CHAN_CONN_LESS:
@@ -543,11 +513,8 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->omtu = L2CAP_DEFAULT_MTU;
break;
- case L2CAP_CHAN_CONN_FIX_A2MP:
- chan->scid = L2CAP_CID_A2MP;
- chan->dcid = L2CAP_CID_A2MP;
- chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
- chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ case L2CAP_CHAN_FIXED:
+ /* Caller will set CID and CID specific MTU values */
break;
default:
@@ -595,7 +562,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
chan->conn = NULL;
- if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP)
+ if (chan->scid != L2CAP_CID_A2MP)
hci_conn_drop(conn->hcon);
if (mgr && mgr->bredr_chan == chan)
@@ -642,6 +609,23 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
return;
}
+void l2cap_conn_update_id_addr(struct hci_conn *hcon)
+{
+ struct l2cap_conn *conn = hcon->l2cap_data;
+ struct l2cap_chan *chan;
+
+ mutex_lock(&conn->chan_lock);
+
+ list_for_each_entry(chan, &conn->chan_l, list) {
+ l2cap_chan_lock(chan);
+ bacpy(&chan->dst, &hcon->dst);
+ chan->dst_type = bdaddr_type(hcon, hcon->dst_type);
+ l2cap_chan_unlock(chan);
+ }
+
+ mutex_unlock(&conn->chan_lock);
+}
+
static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
@@ -681,7 +665,7 @@ static void l2cap_chan_connect_reject(struct l2cap_chan *chan)
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = cpu_to_le16(result);
- rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
}
@@ -699,10 +683,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
case BT_CONNECTED:
case BT_CONFIG:
- /* ATT uses L2CAP_CHAN_CONN_ORIENTED so we must also
- * check for chan->psm.
- */
- if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && chan->psm) {
+ if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
l2cap_send_disconn_req(chan, reason);
} else
@@ -737,6 +718,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
case L2CAP_CHAN_RAW:
switch (chan->sec_level) {
case BT_SECURITY_HIGH:
+ case BT_SECURITY_FIPS:
return HCI_AT_DEDICATED_BONDING_MITM;
case BT_SECURITY_MEDIUM:
return HCI_AT_DEDICATED_BONDING;
@@ -745,21 +727,23 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
}
break;
case L2CAP_CHAN_CONN_LESS:
- if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_3DSP)) {
+ if (chan->psm == cpu_to_le16(L2CAP_PSM_3DSP)) {
if (chan->sec_level == BT_SECURITY_LOW)
chan->sec_level = BT_SECURITY_SDP;
}
- if (chan->sec_level == BT_SECURITY_HIGH)
+ if (chan->sec_level == BT_SECURITY_HIGH ||
+ chan->sec_level == BT_SECURITY_FIPS)
return HCI_AT_NO_BONDING_MITM;
else
return HCI_AT_NO_BONDING;
break;
case L2CAP_CHAN_CONN_ORIENTED:
- if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) {
+ if (chan->psm == cpu_to_le16(L2CAP_PSM_SDP)) {
if (chan->sec_level == BT_SECURITY_LOW)
chan->sec_level = BT_SECURITY_SDP;
- if (chan->sec_level == BT_SECURITY_HIGH)
+ if (chan->sec_level == BT_SECURITY_HIGH ||
+ chan->sec_level == BT_SECURITY_FIPS)
return HCI_AT_NO_BONDING_MITM;
else
return HCI_AT_NO_BONDING;
@@ -768,6 +752,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
default:
switch (chan->sec_level) {
case BT_SECURITY_HIGH:
+ case BT_SECURITY_FIPS:
return HCI_AT_GENERAL_BONDING_MITM;
case BT_SECURITY_MEDIUM:
return HCI_AT_GENERAL_BONDING;
@@ -1288,7 +1273,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
}
} else {
struct l2cap_info_req req;
- req.type = __constant_cpu_to_le16(L2CAP_IT_FEAT_MASK);
+ req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
conn->info_ident = l2cap_get_ident(conn);
@@ -1330,7 +1315,7 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
__clear_ack_timer(chan);
}
- if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+ if (chan->scid == L2CAP_CID_A2MP) {
l2cap_state_change(chan, BT_DISCONN);
return;
}
@@ -1385,18 +1370,18 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
if (l2cap_chan_check_security(chan)) {
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
- rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
- rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
+ rsp.result = cpu_to_le16(L2CAP_CR_PEND);
+ rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
chan->ops->defer(chan);
} else {
l2cap_state_change(chan, BT_CONFIG);
- rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
- rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+ rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
+ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
}
} else {
- rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
- rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
+ rsp.result = cpu_to_le16(L2CAP_CR_PEND);
+ rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
}
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
@@ -1493,8 +1478,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
if (!chan)
goto clean;
- chan->dcid = L2CAP_CID_ATT;
-
bacpy(&chan->src, &hcon->src);
bacpy(&chan->dst, &hcon->dst);
chan->src_type = bdaddr_type(hcon, hcon->src_type);
@@ -1528,7 +1511,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
l2cap_chan_lock(chan);
- if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+ if (chan->scid == L2CAP_CID_A2MP) {
l2cap_chan_unlock(chan);
continue;
}
@@ -1546,6 +1529,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
}
mutex_unlock(&conn->chan_lock);
+
+ queue_work(hcon->hdev->workqueue, &conn->pending_rx_work);
}
/* Notify sockets that we cannot guaranty reliability anymore */
@@ -1671,6 +1656,9 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
kfree_skb(conn->rx_skb);
+ skb_queue_purge(&conn->pending_rx);
+ flush_work(&conn->pending_rx_work);
+
l2cap_unregister_all_users(conn);
mutex_lock(&conn->chan_lock);
@@ -1718,66 +1706,6 @@ static void security_timeout(struct work_struct *work)
}
}
-static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
-{
- struct l2cap_conn *conn = hcon->l2cap_data;
- struct hci_chan *hchan;
-
- if (conn)
- return conn;
-
- hchan = hci_chan_create(hcon);
- if (!hchan)
- return NULL;
-
- conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
- if (!conn) {
- hci_chan_del(hchan);
- return NULL;
- }
-
- kref_init(&conn->ref);
- hcon->l2cap_data = conn;
- conn->hcon = hcon;
- hci_conn_get(conn->hcon);
- conn->hchan = hchan;
-
- BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
-
- switch (hcon->type) {
- case LE_LINK:
- if (hcon->hdev->le_mtu) {
- conn->mtu = hcon->hdev->le_mtu;
- break;
- }
- /* fall through */
- default:
- conn->mtu = hcon->hdev->acl_mtu;
- break;
- }
-
- conn->feat_mask = 0;
-
- if (hcon->type == ACL_LINK)
- conn->hs_enabled = test_bit(HCI_HS_ENABLED,
- &hcon->hdev->dev_flags);
-
- spin_lock_init(&conn->lock);
- mutex_init(&conn->chan_lock);
-
- INIT_LIST_HEAD(&conn->chan_l);
- INIT_LIST_HEAD(&conn->users);
-
- if (hcon->type == LE_LINK)
- INIT_DELAYED_WORK(&conn->security_timer, security_timeout);
- else
- INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
-
- conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
-
- return conn;
-}
-
static void l2cap_conn_free(struct kref *ref)
{
struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
@@ -1848,154 +1776,6 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
return c1;
}
-static bool is_valid_psm(u16 psm, u8 dst_type)
-{
- if (!psm)
- return false;
-
- if (bdaddr_type_is_le(dst_type))
- return (psm <= 0x00ff);
-
- /* PSM must be odd and lsb of upper byte must be 0 */
- return ((psm & 0x0101) == 0x0001);
-}
-
-int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
- bdaddr_t *dst, u8 dst_type)
-{
- struct l2cap_conn *conn;
- struct hci_conn *hcon;
- struct hci_dev *hdev;
- __u8 auth_type;
- int err;
-
- BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
- dst_type, __le16_to_cpu(psm));
-
- hdev = hci_get_route(dst, &chan->src);
- if (!hdev)
- return -EHOSTUNREACH;
-
- hci_dev_lock(hdev);
-
- l2cap_chan_lock(chan);
-
- if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
- chan->chan_type != L2CAP_CHAN_RAW) {
- err = -EINVAL;
- goto done;
- }
-
- if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) {
- err = -EINVAL;
- goto done;
- }
-
- switch (chan->mode) {
- case L2CAP_MODE_BASIC:
- break;
- case L2CAP_MODE_LE_FLOWCTL:
- l2cap_le_flowctl_init(chan);
- break;
- case L2CAP_MODE_ERTM:
- case L2CAP_MODE_STREAMING:
- if (!disable_ertm)
- break;
- /* fall through */
- default:
- err = -ENOTSUPP;
- goto done;
- }
-
- switch (chan->state) {
- case BT_CONNECT:
- case BT_CONNECT2:
- case BT_CONFIG:
- /* Already connecting */
- err = 0;
- goto done;
-
- case BT_CONNECTED:
- /* Already connected */
- err = -EISCONN;
- goto done;
-
- case BT_OPEN:
- case BT_BOUND:
- /* Can connect */
- break;
-
- default:
- err = -EBADFD;
- goto done;
- }
-
- /* Set destination address and psm */
- bacpy(&chan->dst, dst);
- chan->dst_type = dst_type;
-
- chan->psm = psm;
- chan->dcid = cid;
-
- auth_type = l2cap_get_auth_type(chan);
-
- if (bdaddr_type_is_le(dst_type))
- hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
- chan->sec_level, auth_type);
- else
- hcon = hci_connect(hdev, ACL_LINK, dst, dst_type,
- chan->sec_level, auth_type);
-
- if (IS_ERR(hcon)) {
- err = PTR_ERR(hcon);
- goto done;
- }
-
- conn = l2cap_conn_add(hcon);
- if (!conn) {
- hci_conn_drop(hcon);
- err = -ENOMEM;
- goto done;
- }
-
- if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
- hci_conn_drop(hcon);
- err = -EBUSY;
- goto done;
- }
-
- /* Update source addr of the socket */
- bacpy(&chan->src, &hcon->src);
- chan->src_type = bdaddr_type(hcon, hcon->src_type);
-
- l2cap_chan_unlock(chan);
- l2cap_chan_add(conn, chan);
- l2cap_chan_lock(chan);
-
- /* l2cap_chan_add takes its own ref so we can drop this one */
- hci_conn_drop(hcon);
-
- l2cap_state_change(chan, BT_CONNECT);
- __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
-
- if (hcon->state == BT_CONNECTED) {
- if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
- __clear_chan_timer(chan);
- if (l2cap_chan_check_security(chan))
- l2cap_state_change(chan, BT_CONNECTED);
- } else
- l2cap_do_start(chan);
- }
-
- err = 0;
-
-done:
- l2cap_chan_unlock(chan);
- hci_dev_unlock(hdev);
- hci_dev_put(hdev);
- return err;
-}
-
static void l2cap_monitor_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
@@ -2654,6 +2434,14 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
if (IS_ERR(skb))
return PTR_ERR(skb);
+ /* Channel lock is released before requesting new skb and then
+ * reacquired thus we need to recheck channel state.
+ */
+ if (chan->state != BT_CONNECTED) {
+ kfree_skb(skb);
+ return -ENOTCONN;
+ }
+
l2cap_do_send(chan, skb);
return len;
}
@@ -2703,6 +2491,14 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
if (IS_ERR(skb))
return PTR_ERR(skb);
+ /* Channel lock is released before requesting new skb and then
+ * reacquired thus we need to recheck channel state.
+ */
+ if (chan->state != BT_CONNECTED) {
+ kfree_skb(skb);
+ return -ENOTCONN;
+ }
+
l2cap_do_send(chan, skb);
err = len;
break;
@@ -3099,9 +2895,9 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code,
lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);
if (conn->hcon->type == LE_LINK)
- lh->cid = __constant_cpu_to_le16(L2CAP_CID_LE_SIGNALING);
+ lh->cid = cpu_to_le16(L2CAP_CID_LE_SIGNALING);
else
- lh->cid = __constant_cpu_to_le16(L2CAP_CID_SIGNALING);
+ lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING);
cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
cmd->code = code;
@@ -3214,8 +3010,8 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
efs.stype = chan->local_stype;
efs.msdu = cpu_to_le16(chan->local_msdu);
efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
- efs.acc_lat = __constant_cpu_to_le32(L2CAP_DEFAULT_ACC_LAT);
- efs.flush_to = __constant_cpu_to_le32(L2CAP_EFS_DEFAULT_FLUSH_TO);
+ efs.acc_lat = cpu_to_le32(L2CAP_DEFAULT_ACC_LAT);
+ efs.flush_to = cpu_to_le32(L2CAP_EFS_DEFAULT_FLUSH_TO);
break;
case L2CAP_MODE_STREAMING:
@@ -3356,8 +3152,8 @@ static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
rfc->retrans_timeout = cpu_to_le16((u16) ertm_to);
rfc->monitor_timeout = rfc->retrans_timeout;
} else {
- rfc->retrans_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
- rfc->monitor_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
+ rfc->retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
+ rfc->monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
}
}
@@ -3489,7 +3285,7 @@ done:
}
req->dcid = cpu_to_le16(chan->dcid);
- req->flags = __constant_cpu_to_le16(0);
+ req->flags = cpu_to_le16(0);
return ptr - data;
}
@@ -3703,7 +3499,7 @@ done:
}
rsp->scid = cpu_to_le16(chan->dcid);
rsp->result = cpu_to_le16(result);
- rsp->flags = __constant_cpu_to_le16(0);
+ rsp->flags = cpu_to_le16(0);
return ptr - data;
}
@@ -3812,7 +3608,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
}
req->dcid = cpu_to_le16(chan->dcid);
- req->flags = __constant_cpu_to_le16(0);
+ req->flags = cpu_to_le16(0);
return ptr - data;
}
@@ -3843,7 +3639,7 @@ void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan)
rsp.mtu = cpu_to_le16(chan->imtu);
rsp.mps = cpu_to_le16(chan->mps);
rsp.credits = cpu_to_le16(chan->rx_credits);
- rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
+ rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
&rsp);
@@ -3858,8 +3654,8 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
- rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
- rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+ rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
+ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
if (chan->hs_hcon)
rsp_code = L2CAP_CREATE_CHAN_RSP;
@@ -3888,8 +3684,8 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
u16 txwin_ext = chan->ack_win;
struct l2cap_conf_rfc rfc = {
.mode = chan->mode,
- .retrans_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO),
- .monitor_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO),
+ .retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO),
+ .monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO),
.max_pdu_size = cpu_to_le16(chan->imtu),
.txwin_size = min_t(u16, chan->ack_win, L2CAP_DEFAULT_TX_WINDOW),
};
@@ -3980,7 +3776,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
l2cap_chan_lock(pchan);
/* Check if the ACL is secure enough (if not SDP) */
- if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) &&
+ if (psm != cpu_to_le16(L2CAP_PSM_SDP) &&
!hci_conn_check_link_mode(conn->hcon)) {
conn->disc_reason = HCI_ERROR_AUTH_FAILURE;
result = L2CAP_CR_SEC_BLOCK;
@@ -4065,7 +3861,7 @@ sendresp:
if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) {
struct l2cap_info_req info;
- info.type = __constant_cpu_to_le16(L2CAP_IT_FEAT_MASK);
+ info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
conn->info_ident = l2cap_get_ident(conn);
@@ -4214,7 +4010,7 @@ static void cmd_reject_invalid_cid(struct l2cap_conn *conn, u8 ident,
{
struct l2cap_cmd_rej_cid rej;
- rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
+ rej.reason = cpu_to_le16(L2CAP_REJ_INVALID_CID);
rej.scid = __cpu_to_le16(scid);
rej.dcid = __cpu_to_le16(dcid);
@@ -4546,8 +4342,8 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
u8 buf[8];
u32 feat_mask = l2cap_feat_mask;
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
- rsp->type = __constant_cpu_to_le16(L2CAP_IT_FEAT_MASK);
- rsp->result = __constant_cpu_to_le16(L2CAP_IR_SUCCESS);
+ rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
+ rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
if (!disable_ertm)
feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
| L2CAP_FEAT_FCS;
@@ -4567,15 +4363,15 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
else
l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP;
- rsp->type = __constant_cpu_to_le16(L2CAP_IT_FIXED_CHAN);
- rsp->result = __constant_cpu_to_le16(L2CAP_IR_SUCCESS);
+ rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
+ rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan));
l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf),
buf);
} else {
struct l2cap_info_rsp rsp;
rsp.type = cpu_to_le16(type);
- rsp.result = __constant_cpu_to_le16(L2CAP_IR_NOTSUPP);
+ rsp.result = cpu_to_le16(L2CAP_IR_NOTSUPP);
l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp),
&rsp);
}
@@ -4620,7 +4416,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) {
struct l2cap_info_req req;
- req.type = __constant_cpu_to_le16(L2CAP_IT_FIXED_CHAN);
+ req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
conn->info_ident = l2cap_get_ident(conn);
@@ -4714,8 +4510,8 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
error:
rsp.dcid = 0;
rsp.scid = cpu_to_le16(scid);
- rsp.result = __constant_cpu_to_le16(L2CAP_CR_BAD_AMP);
- rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+ rsp.result = cpu_to_le16(L2CAP_CR_BAD_AMP);
+ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
sizeof(rsp), &rsp);
@@ -4779,7 +4575,7 @@ static void l2cap_send_move_chan_cfm_icid(struct l2cap_conn *conn, u16 icid)
BT_DBG("conn %p, icid 0x%4.4x", conn, icid);
cfm.icid = cpu_to_le16(icid);
- cfm.result = __constant_cpu_to_le16(L2CAP_MC_UNCONFIRMED);
+ cfm.result = cpu_to_le16(L2CAP_MC_UNCONFIRMED);
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_MOVE_CHAN_CFM,
sizeof(cfm), &cfm);
@@ -4962,12 +4758,12 @@ static void l2cap_do_create(struct l2cap_chan *chan, int result,
if (result == L2CAP_CR_SUCCESS) {
/* Send successful response */
- rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
- rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+ rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
+ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
} else {
/* Send negative response */
- rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM);
- rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+ rsp.result = cpu_to_le16(L2CAP_CR_NO_MEM);
+ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
}
l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_RSP,
@@ -5095,7 +4891,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
chan = l2cap_get_chan_by_dcid(conn, icid);
if (!chan) {
rsp.icid = cpu_to_le16(icid);
- rsp.result = __constant_cpu_to_le16(L2CAP_MR_NOT_ALLOWED);
+ rsp.result = cpu_to_le16(L2CAP_MR_NOT_ALLOWED);
l2cap_send_cmd(conn, cmd->ident, L2CAP_MOVE_CHAN_RSP,
sizeof(rsp), &rsp);
return 0;
@@ -5439,9 +5235,9 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
err = l2cap_check_conn_param(min, max, latency, to_multiplier);
if (err)
- rsp.result = __constant_cpu_to_le16(L2CAP_CONN_PARAM_REJECTED);
+ rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED);
else
- rsp.result = __constant_cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED);
+ rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED);
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP,
sizeof(rsp), &rsp);
@@ -5709,7 +5505,7 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
{
struct l2cap_le_credits *pkt;
struct l2cap_chan *chan;
- u16 cid, credits;
+ u16 cid, credits, max_credits;
if (cmd_len != sizeof(*pkt))
return -EPROTO;
@@ -5724,6 +5520,17 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
if (!chan)
return -EBADSLT;
+ max_credits = LE_FLOWCTL_MAX_CREDITS - chan->tx_credits;
+ if (credits > max_credits) {
+ BT_ERR("LE credits overflow");
+ l2cap_send_disconn_req(chan, ECONNRESET);
+
+ /* Return 0 so that we don't trigger an unnecessary
+ * command reject packet.
+ */
+ return 0;
+ }
+
chan->tx_credits += credits;
while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
@@ -5770,17 +5577,6 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
{
int err = 0;
- if (!enable_lecoc) {
- switch (cmd->code) {
- case L2CAP_LE_CONN_REQ:
- case L2CAP_LE_CONN_RSP:
- case L2CAP_LE_CREDITS:
- case L2CAP_DISCONN_REQ:
- case L2CAP_DISCONN_RSP:
- return -EINVAL;
- }
- }
-
switch (cmd->code) {
case L2CAP_COMMAND_REJ:
l2cap_le_command_rej(conn, cmd, cmd_len, data);
@@ -5854,7 +5650,7 @@ static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
BT_ERR("Wrong link type (%d)", err);
- rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
+ rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
sizeof(rej), &rej);
}
@@ -5899,7 +5695,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
BT_ERR("Wrong link type (%d)", err);
- rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
+ rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
sizeof(rej), &rej);
}
@@ -6871,6 +6667,7 @@ static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
if (!chan->rx_credits) {
BT_ERR("No credits to receive LE L2CAP data");
+ l2cap_send_disconn_req(chan, ECONNRESET);
return -ENOBUFS;
}
@@ -6995,8 +6792,10 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
* But we don't have any other choice. L2CAP doesn't
* provide flow control mechanism. */
- if (chan->imtu < skb->len)
+ if (chan->imtu < skb->len) {
+ BT_ERR("Dropping L2CAP data: receive buffer overflow");
goto drop;
+ }
if (!chan->ops->recv(chan, skb))
goto done;
@@ -7084,9 +6883,16 @@ drop:
static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct l2cap_hdr *lh = (void *) skb->data;
+ struct hci_conn *hcon = conn->hcon;
u16 cid, len;
__le16 psm;
+ if (hcon->state != BT_CONNECTED) {
+ BT_DBG("queueing pending rx skb");
+ skb_queue_tail(&conn->pending_rx, skb);
+ return;
+ }
+
skb_pull(skb, L2CAP_HDR_SIZE);
cid = __le16_to_cpu(lh->cid);
len = __le16_to_cpu(lh->len);
@@ -7132,6 +6938,247 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
}
}
+static void process_pending_rx(struct work_struct *work)
+{
+ struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
+ pending_rx_work);
+ struct sk_buff *skb;
+
+ BT_DBG("");
+
+ while ((skb = skb_dequeue(&conn->pending_rx)))
+ l2cap_recv_frame(conn, skb);
+}
+
+static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
+{
+ struct l2cap_conn *conn = hcon->l2cap_data;
+ struct hci_chan *hchan;
+
+ if (conn)
+ return conn;
+
+ hchan = hci_chan_create(hcon);
+ if (!hchan)
+ return NULL;
+
+ conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
+ if (!conn) {
+ hci_chan_del(hchan);
+ return NULL;
+ }
+
+ kref_init(&conn->ref);
+ hcon->l2cap_data = conn;
+ conn->hcon = hcon;
+ hci_conn_get(conn->hcon);
+ conn->hchan = hchan;
+
+ BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
+
+ switch (hcon->type) {
+ case LE_LINK:
+ if (hcon->hdev->le_mtu) {
+ conn->mtu = hcon->hdev->le_mtu;
+ break;
+ }
+ /* fall through */
+ default:
+ conn->mtu = hcon->hdev->acl_mtu;
+ break;
+ }
+
+ conn->feat_mask = 0;
+
+ if (hcon->type == ACL_LINK)
+ conn->hs_enabled = test_bit(HCI_HS_ENABLED,
+ &hcon->hdev->dev_flags);
+
+ spin_lock_init(&conn->lock);
+ mutex_init(&conn->chan_lock);
+
+ INIT_LIST_HEAD(&conn->chan_l);
+ INIT_LIST_HEAD(&conn->users);
+
+ if (hcon->type == LE_LINK)
+ INIT_DELAYED_WORK(&conn->security_timer, security_timeout);
+ else
+ INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
+
+ skb_queue_head_init(&conn->pending_rx);
+ INIT_WORK(&conn->pending_rx_work, process_pending_rx);
+
+ conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
+
+ return conn;
+}
+
+static bool is_valid_psm(u16 psm, u8 dst_type) {
+ if (!psm)
+ return false;
+
+ if (bdaddr_type_is_le(dst_type))
+ return (psm <= 0x00ff);
+
+ /* PSM must be odd and lsb of upper byte must be 0 */
+ return ((psm & 0x0101) == 0x0001);
+}
+
+int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
+ bdaddr_t *dst, u8 dst_type)
+{
+ struct l2cap_conn *conn;
+ struct hci_conn *hcon;
+ struct hci_dev *hdev;
+ __u8 auth_type;
+ int err;
+
+ BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
+ dst_type, __le16_to_cpu(psm));
+
+ hdev = hci_get_route(dst, &chan->src);
+ if (!hdev)
+ return -EHOSTUNREACH;
+
+ hci_dev_lock(hdev);
+
+ l2cap_chan_lock(chan);
+
+ if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
+ chan->chan_type != L2CAP_CHAN_RAW) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !psm) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (chan->chan_type == L2CAP_CHAN_FIXED && !cid) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ switch (chan->mode) {
+ case L2CAP_MODE_BASIC:
+ break;
+ case L2CAP_MODE_LE_FLOWCTL:
+ l2cap_le_flowctl_init(chan);
+ break;
+ case L2CAP_MODE_ERTM:
+ case L2CAP_MODE_STREAMING:
+ if (!disable_ertm)
+ break;
+ /* fall through */
+ default:
+ err = -ENOTSUPP;
+ goto done;
+ }
+
+ switch (chan->state) {
+ case BT_CONNECT:
+ case BT_CONNECT2:
+ case BT_CONFIG:
+ /* Already connecting */
+ err = 0;
+ goto done;
+
+ case BT_CONNECTED:
+ /* Already connected */
+ err = -EISCONN;
+ goto done;
+
+ case BT_OPEN:
+ case BT_BOUND:
+ /* Can connect */
+ break;
+
+ default:
+ err = -EBADFD;
+ goto done;
+ }
+
+ /* Set destination address and psm */
+ bacpy(&chan->dst, dst);
+ chan->dst_type = dst_type;
+
+ chan->psm = psm;
+ chan->dcid = cid;
+
+ auth_type = l2cap_get_auth_type(chan);
+
+ if (bdaddr_type_is_le(dst_type)) {
+ /* Convert from L2CAP channel address type to HCI address type
+ */
+ if (dst_type == BDADDR_LE_PUBLIC)
+ dst_type = ADDR_LE_DEV_PUBLIC;
+ else
+ dst_type = ADDR_LE_DEV_RANDOM;
+
+ hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level,
+ auth_type);
+ } else {
+ hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type);
+ }
+
+ if (IS_ERR(hcon)) {
+ err = PTR_ERR(hcon);
+ goto done;
+ }
+
+ conn = l2cap_conn_add(hcon);
+ if (!conn) {
+ hci_conn_drop(hcon);
+ err = -ENOMEM;
+ goto done;
+ }
+
+ if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
+ hci_conn_drop(hcon);
+ err = -EBUSY;
+ goto done;
+ }
+
+ /* Update source addr of the socket */
+ bacpy(&chan->src, &hcon->src);
+ chan->src_type = bdaddr_type(hcon, hcon->src_type);
+
+ l2cap_chan_unlock(chan);
+ l2cap_chan_add(conn, chan);
+ l2cap_chan_lock(chan);
+
+ /* l2cap_chan_add takes its own ref so we can drop this one */
+ hci_conn_drop(hcon);
+
+ l2cap_state_change(chan, BT_CONNECT);
+ __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
+
+ /* Release chan->sport so that it can be reused by other
+ * sockets (as it's only used for listening sockets).
+ */
+ write_lock(&chan_list_lock);
+ chan->sport = 0;
+ write_unlock(&chan_list_lock);
+
+ if (hcon->state == BT_CONNECTED) {
+ if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+ __clear_chan_timer(chan);
+ if (l2cap_chan_check_security(chan))
+ l2cap_state_change(chan, BT_CONNECTED);
+ } else
+ l2cap_do_start(chan);
+ }
+
+ err = 0;
+
+done:
+ l2cap_chan_unlock(chan);
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+ return err;
+}
+
/* ---- L2CAP interface with lower layer (HCI) ---- */
int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
@@ -7206,7 +7253,8 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
if (encrypt == 0x00) {
if (chan->sec_level == BT_SECURITY_MEDIUM) {
__set_chan_timer(chan, L2CAP_ENC_TIMEOUT);
- } else if (chan->sec_level == BT_SECURITY_HIGH)
+ } else if (chan->sec_level == BT_SECURITY_HIGH ||
+ chan->sec_level == BT_SECURITY_FIPS)
l2cap_chan_close(chan, ECONNREFUSED);
} else {
if (chan->sec_level == BT_SECURITY_MEDIUM)
@@ -7226,7 +7274,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (hcon->type == LE_LINK) {
if (!status && encrypt)
- smp_distribute_keys(conn, 0);
+ smp_distribute_keys(conn);
cancel_delayed_work(&conn->security_timer);
}
@@ -7238,7 +7286,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
BT_DBG("chan %p scid 0x%4.4x state %s", chan, chan->scid,
state_to_string(chan->state));
- if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+ if (chan->scid == L2CAP_CID_A2MP) {
l2cap_chan_unlock(chan);
continue;
}
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index d58f76bcebd1..f59e00c2daa9 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -36,8 +36,6 @@
#include "smp.h"
-bool enable_lecoc;
-
static struct bt_sock_list l2cap_sk_list = {
.lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
};
@@ -101,12 +99,19 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
return -EINVAL;
+ if (la.l2_cid) {
+ /* When the socket gets created it defaults to
+ * CHAN_CONN_ORIENTED, so we need to overwrite the
+ * default here.
+ */
+ chan->chan_type = L2CAP_CHAN_FIXED;
+ chan->omtu = L2CAP_DEFAULT_MTU;
+ }
+
if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
- if (!enable_lecoc && la.l2_psm)
- return -EINVAL;
/* We only allow ATT user space socket */
if (la.l2_cid &&
- la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+ la.l2_cid != cpu_to_le16(L2CAP_CID_ATT))
return -EINVAL;
}
@@ -204,7 +209,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
* ATT. Anything else is an invalid combination.
*/
if (chan->scid != L2CAP_CID_ATT ||
- la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+ la.l2_cid != cpu_to_le16(L2CAP_CID_ATT))
return -EINVAL;
/* We don't have the hdev available here to make a
@@ -220,11 +225,9 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
return -EINVAL;
if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
- if (!enable_lecoc && la.l2_psm)
- return -EINVAL;
/* We only allow ATT user space socket */
if (la.l2_cid &&
- la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+ la.l2_cid != cpu_to_le16(L2CAP_CID_ATT))
return -EINVAL;
}
@@ -357,17 +360,21 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
BT_DBG("sock %p, sk %p", sock, sk);
+ if (peer && sk->sk_state != BT_CONNECTED &&
+ sk->sk_state != BT_CONNECT && sk->sk_state != BT_CONNECT2)
+ return -ENOTCONN;
+
memset(la, 0, sizeof(struct sockaddr_l2));
addr->sa_family = AF_BLUETOOTH;
*len = sizeof(struct sockaddr_l2);
+ la->l2_psm = chan->psm;
+
if (peer) {
- la->l2_psm = chan->psm;
bacpy(&la->l2_bdaddr, &chan->dst);
la->l2_cid = cpu_to_le16(chan->dcid);
la->l2_bdaddr_type = chan->dst_type;
} else {
- la->l2_psm = chan->sport;
bacpy(&la->l2_bdaddr, &chan->src);
la->l2_cid = cpu_to_le16(chan->scid);
la->l2_bdaddr_type = chan->src_type;
@@ -432,6 +439,10 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
L2CAP_LM_SECURE;
break;
+ case BT_SECURITY_FIPS:
+ opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
+ L2CAP_LM_SECURE | L2CAP_LM_FIPS;
+ break;
default:
opt = 0;
break;
@@ -445,6 +456,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
if (put_user(opt, (u32 __user *) optval))
err = -EFAULT;
+
break;
case L2CAP_CONNINFO:
@@ -499,6 +511,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
switch (optname) {
case BT_SECURITY:
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
+ chan->chan_type != L2CAP_CHAN_FIXED &&
chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
break;
@@ -560,11 +573,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
break;
case BT_SNDMTU:
- if (!enable_lecoc) {
- err = -EPROTONOSUPPORT;
- break;
- }
-
if (!bdaddr_type_is_le(chan->src_type)) {
err = -EINVAL;
break;
@@ -580,11 +588,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
break;
case BT_RCVMTU:
- if (!enable_lecoc) {
- err = -EPROTONOSUPPORT;
- break;
- }
-
if (!bdaddr_type_is_le(chan->src_type)) {
err = -EINVAL;
break;
@@ -699,6 +702,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
break;
}
+ if (opt & L2CAP_LM_FIPS) {
+ err = -EINVAL;
+ break;
+ }
+
if (opt & L2CAP_LM_AUTH)
chan->sec_level = BT_SECURITY_LOW;
if (opt & L2CAP_LM_ENCRYPT)
@@ -750,6 +758,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
switch (optname) {
case BT_SECURITY:
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
+ chan->chan_type != L2CAP_CHAN_FIXED &&
chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
break;
@@ -895,11 +904,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
case BT_SNDMTU:
- if (!enable_lecoc) {
- err = -EPROTONOSUPPORT;
- break;
- }
-
if (!bdaddr_type_is_le(chan->src_type)) {
err = -EINVAL;
break;
@@ -912,11 +916,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
case BT_RCVMTU:
- if (!enable_lecoc) {
- err = -EPROTONOSUPPORT;
- break;
- }
-
if (!bdaddr_type_is_le(chan->src_type)) {
err = -EINVAL;
break;
@@ -1449,6 +1448,11 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
chan->tx_credits = pchan->tx_credits;
chan->rx_credits = pchan->rx_credits;
+ if (chan->chan_type == L2CAP_CHAN_FIXED) {
+ chan->scid = pchan->scid;
+ chan->dcid = pchan->scid;
+ }
+
security_sk_clone(parent, sk);
} else {
switch (sk->sk_type) {
@@ -1614,6 +1618,3 @@ void l2cap_cleanup_sockets(void)
bt_sock_unregister(BTPROTO_L2CAP);
proto_unregister(&l2cap_proto);
}
-
-module_param(enable_lecoc, bool, 0644);
-MODULE_PARM_DESC(enable_lecoc, "Enable support for LE CoC");
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a03ca3ca91bf..d2d4e0d5aed0 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -34,7 +34,7 @@
#include "smp.h"
#define MGMT_VERSION 1
-#define MGMT_REVISION 4
+#define MGMT_REVISION 5
static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST,
@@ -79,6 +79,10 @@ static const u16 mgmt_commands[] = {
MGMT_OP_SET_BREDR,
MGMT_OP_SET_STATIC_ADDRESS,
MGMT_OP_SET_SCAN_PARAMS,
+ MGMT_OP_SET_SECURE_CONN,
+ MGMT_OP_SET_DEBUG_KEYS,
+ MGMT_OP_SET_PRIVACY,
+ MGMT_OP_LOAD_IRKS,
};
static const u16 mgmt_events[] = {
@@ -103,6 +107,8 @@ static const u16 mgmt_events[] = {
MGMT_EV_DEVICE_UNBLOCKED,
MGMT_EV_DEVICE_UNPAIRED,
MGMT_EV_PASSKEY_NOTIFY,
+ MGMT_EV_NEW_IRK,
+ MGMT_EV_NEW_CSRK,
};
#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
@@ -127,7 +133,7 @@ static u8 mgmt_status_table[] = {
MGMT_STATUS_FAILED, /* Hardware Failure */
MGMT_STATUS_CONNECT_FAILED, /* Page Timeout */
MGMT_STATUS_AUTH_FAILED, /* Authentication Failed */
- MGMT_STATUS_NOT_PAIRED, /* PIN or Key Missing */
+ MGMT_STATUS_AUTH_FAILED, /* PIN or Key Missing */
MGMT_STATUS_NO_RESOURCES, /* Memory Full */
MGMT_STATUS_TIMEOUT, /* Connection Timeout */
MGMT_STATUS_NO_RESOURCES, /* Max Number of Connections */
@@ -207,7 +213,7 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
hdr = (void *) skb_put(skb, sizeof(*hdr));
- hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_STATUS);
+ hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev));
@@ -238,7 +244,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
hdr = (void *) skb_put(skb, sizeof(*hdr));
- hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_COMPLETE);
+ hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
@@ -264,7 +270,7 @@ static int read_version(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("sock %p", sk);
rp.version = MGMT_VERSION;
- rp.revision = __constant_cpu_to_le16(MGMT_REVISION);
+ rp.revision = cpu_to_le16(MGMT_REVISION);
return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, &rp,
sizeof(rp));
@@ -288,8 +294,8 @@ static int read_commands(struct sock *sk, struct hci_dev *hdev, void *data,
if (!rp)
return -ENOMEM;
- rp->num_commands = __constant_cpu_to_le16(num_commands);
- rp->num_events = __constant_cpu_to_le16(num_events);
+ rp->num_commands = cpu_to_le16(num_commands);
+ rp->num_events = cpu_to_le16(num_events);
for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++)
put_unaligned_le16(mgmt_commands[i], opcode);
@@ -363,6 +369,7 @@ static u32 get_supported_settings(struct hci_dev *hdev)
settings |= MGMT_SETTING_POWERED;
settings |= MGMT_SETTING_PAIRABLE;
+ settings |= MGMT_SETTING_DEBUG_KEYS;
if (lmp_bredr_capable(hdev)) {
settings |= MGMT_SETTING_CONNECTABLE;
@@ -376,11 +383,16 @@ static u32 get_supported_settings(struct hci_dev *hdev)
settings |= MGMT_SETTING_SSP;
settings |= MGMT_SETTING_HS;
}
+
+ if (lmp_sc_capable(hdev) ||
+ test_bit(HCI_FORCE_SC, &hdev->dev_flags))
+ settings |= MGMT_SETTING_SECURE_CONN;
}
if (lmp_le_capable(hdev)) {
settings |= MGMT_SETTING_LE;
settings |= MGMT_SETTING_ADVERTISING;
+ settings |= MGMT_SETTING_PRIVACY;
}
return settings;
@@ -423,6 +435,15 @@ static u32 get_current_settings(struct hci_dev *hdev)
if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
settings |= MGMT_SETTING_ADVERTISING;
+ if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+ settings |= MGMT_SETTING_SECURE_CONN;
+
+ if (test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags))
+ settings |= MGMT_SETTING_DEBUG_KEYS;
+
+ if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
+ settings |= MGMT_SETTING_PRIVACY;
+
return settings;
}
@@ -629,14 +650,8 @@ static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr)
flags |= get_adv_discov_flags(hdev);
- if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
- if (lmp_le_br_capable(hdev))
- flags |= LE_AD_SIM_LE_BREDR_CTRL;
- if (lmp_host_le_br_capable(hdev))
- flags |= LE_AD_SIM_LE_BREDR_HOST;
- } else {
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
flags |= LE_AD_NO_BREDR;
- }
if (flags) {
BT_DBG("adv flags 0x%02x", flags);
@@ -803,6 +818,64 @@ static void update_class(struct hci_request *req)
hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
}
+static bool get_connectable(struct hci_dev *hdev)
+{
+ struct pending_cmd *cmd;
+
+ /* If there's a pending mgmt command the flag will not yet have
+ * it's final value, so check for this first.
+ */
+ cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
+ if (cmd) {
+ struct mgmt_mode *cp = cmd->param;
+ return cp->val;
+ }
+
+ return test_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+}
+
+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;
+
+ /* Clear the HCI_ADVERTISING 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.
+ */
+ clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+
+ 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(0x0800);
+ cp.max_interval = cpu_to_le16(0x0800);
+ cp.type = connectable ? LE_ADV_IND : 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 disable_advertising(struct hci_request *req)
+{
+ u8 enable = 0x00;
+
+ 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,
@@ -824,12 +897,39 @@ static void service_cache_off(struct work_struct *work)
hci_req_run(&req, NULL);
}
+static void rpa_expired(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ rpa_expired.work);
+ struct hci_request req;
+
+ BT_DBG("");
+
+ set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+
+ if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags) ||
+ hci_conn_num(hdev, LE_LINK) > 0)
+ return;
+
+ /* The generation of a new RPA and programming it into the
+ * controller happens in the enable_advertising() function.
+ */
+
+ hci_req_init(&req, hdev);
+
+ disable_advertising(&req);
+ enable_advertising(&req);
+
+ hci_req_run(&req, NULL);
+}
+
static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev)
{
if (test_and_set_bit(HCI_MGMT, &hdev->dev_flags))
return;
INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
+ INIT_DELAYED_WORK(&hdev->rpa_expired, rpa_expired);
/* Non-mgmt controlled devices get this bit set
* implicitly so that pairing works for them, however
@@ -935,6 +1035,71 @@ static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
sizeof(settings));
}
+static void clean_up_hci_complete(struct hci_dev *hdev, u8 status)
+{
+ BT_DBG("%s status 0x%02x", hdev->name, status);
+
+ if (hci_conn_count(hdev) == 0) {
+ cancel_delayed_work(&hdev->power_off);
+ queue_work(hdev->req_workqueue, &hdev->power_off.work);
+ }
+}
+
+static int clean_up_hci_state(struct hci_dev *hdev)
+{
+ struct hci_request req;
+ struct hci_conn *conn;
+
+ hci_req_init(&req, hdev);
+
+ if (test_bit(HCI_ISCAN, &hdev->flags) ||
+ test_bit(HCI_PSCAN, &hdev->flags)) {
+ u8 scan = 0x00;
+ hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ }
+
+ if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+ disable_advertising(&req);
+
+ if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
+ hci_req_add_le_scan_disable(&req);
+ }
+
+ list_for_each_entry(conn, &hdev->conn_hash.list, list) {
+ struct hci_cp_disconnect dc;
+ struct hci_cp_reject_conn_req rej;
+
+ switch (conn->state) {
+ case BT_CONNECTED:
+ case BT_CONFIG:
+ dc.handle = cpu_to_le16(conn->handle);
+ dc.reason = 0x15; /* Terminated due to Power Off */
+ hci_req_add(&req, HCI_OP_DISCONNECT, sizeof(dc), &dc);
+ break;
+ case BT_CONNECT:
+ if (conn->type == LE_LINK)
+ hci_req_add(&req, HCI_OP_LE_CREATE_CONN_CANCEL,
+ 0, NULL);
+ else if (conn->type == ACL_LINK)
+ hci_req_add(&req, HCI_OP_CREATE_CONN_CANCEL,
+ 6, &conn->dst);
+ break;
+ case BT_CONNECT2:
+ bacpy(&rej.bdaddr, &conn->dst);
+ rej.reason = 0x15; /* Terminated due to Power Off */
+ if (conn->type == ACL_LINK)
+ hci_req_add(&req, HCI_OP_REJECT_CONN_REQ,
+ sizeof(rej), &rej);
+ else if (conn->type == SCO_LINK)
+ hci_req_add(&req, HCI_OP_REJECT_SYNC_CONN_REQ,
+ sizeof(rej), &rej);
+ break;
+ }
+ }
+
+ return hci_req_run(&req, clean_up_hci_complete);
+}
+
static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len)
{
@@ -978,12 +1143,23 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
- if (cp->val)
+ if (cp->val) {
queue_work(hdev->req_workqueue, &hdev->power_on);
- else
- queue_work(hdev->req_workqueue, &hdev->power_off.work);
-
- err = 0;
+ err = 0;
+ } else {
+ /* Disconnect connections, stop scans, etc */
+ err = clean_up_hci_state(hdev);
+ if (!err)
+ queue_delayed_work(hdev->req_workqueue, &hdev->power_off,
+ HCI_POWER_OFF_TIMEOUT);
+
+ /* ENODATA means there were no HCI commands queued */
+ if (err == -ENODATA) {
+ cancel_delayed_work(&hdev->power_off);
+ queue_work(hdev->req_workqueue, &hdev->power_off.work);
+ err = 0;
+ }
+ }
failed:
hci_dev_unlock(hdev);
@@ -1005,7 +1181,7 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
if (hdev)
hdr->index = cpu_to_le16(hdev->id);
else
- hdr->index = __constant_cpu_to_le16(MGMT_INDEX_NONE);
+ hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
hdr->len = cpu_to_le16(data_len);
if (data)
@@ -1317,15 +1493,15 @@ static void write_fast_connectable(struct hci_request *req, bool enable)
type = PAGE_SCAN_TYPE_INTERLACED;
/* 160 msec page scan interval */
- acp.interval = __constant_cpu_to_le16(0x0100);
+ acp.interval = cpu_to_le16(0x0100);
} else {
type = PAGE_SCAN_TYPE_STANDARD; /* default */
/* default 1.28 sec page scan */
- acp.interval = __constant_cpu_to_le16(0x0800);
+ acp.interval = cpu_to_le16(0x0800);
}
- acp.window = __constant_cpu_to_le16(0x0012);
+ 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)
@@ -1336,50 +1512,6 @@ static void write_fast_connectable(struct hci_request *req, bool enable)
hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
}
-static u8 get_adv_type(struct hci_dev *hdev)
-{
- struct pending_cmd *cmd;
- bool connectable;
-
- /* If there's a pending mgmt command the flag will not yet have
- * it's final value, so check for this first.
- */
- cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
- if (cmd) {
- struct mgmt_mode *cp = cmd->param;
- connectable = !!cp->val;
- } else {
- connectable = test_bit(HCI_CONNECTABLE, &hdev->dev_flags);
- }
-
- return connectable ? LE_ADV_IND : LE_ADV_NONCONN_IND;
-}
-
-static void enable_advertising(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_le_set_adv_param cp;
- u8 enable = 0x01;
-
- memset(&cp, 0, sizeof(cp));
- cp.min_interval = __constant_cpu_to_le16(0x0800);
- cp.max_interval = __constant_cpu_to_le16(0x0800);
- cp.type = get_adv_type(hdev);
- cp.own_address_type = hdev->own_addr_type;
- cp.channel_map = 0x07;
-
- 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 disable_advertising(struct hci_request *req)
-{
- u8 enable = 0x00;
-
- hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
-}
-
static void set_connectable_complete(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
@@ -2065,7 +2197,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
}
if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
- err = hci_uuids_clear(hdev);
+ hci_uuids_clear(hdev);
if (enable_service_cache(hdev)) {
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID,
@@ -2205,6 +2337,7 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
{
struct mgmt_cp_load_link_keys *cp = data;
u16 key_count, expected_len;
+ bool changed;
int i;
BT_DBG("request for %s", hdev->name);
@@ -2219,7 +2352,7 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
sizeof(struct mgmt_link_key_info);
if (expected_len != len) {
BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
- len, expected_len);
+ expected_len, len);
return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
MGMT_STATUS_INVALID_PARAMS);
}
@@ -2234,7 +2367,7 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
for (i = 0; i < key_count; i++) {
struct mgmt_link_key_info *key = &cp->keys[i];
- if (key->addr.type != BDADDR_BREDR)
+ if (key->addr.type != BDADDR_BREDR || key->type > 0x08)
return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
MGMT_STATUS_INVALID_PARAMS);
}
@@ -2244,9 +2377,12 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
hci_link_keys_clear(hdev);
if (cp->debug_keys)
- set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+ changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
else
- clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+ changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+
+ if (changed)
+ new_settings(hdev, NULL);
for (i = 0; i < key_count; i++) {
struct mgmt_link_key_info *key = &cp->keys[i];
@@ -2306,10 +2442,22 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
- if (cp->addr.type == BDADDR_BREDR)
+ if (cp->addr.type == BDADDR_BREDR) {
err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
- else
- err = hci_remove_ltk(hdev, &cp->addr.bdaddr);
+ } else {
+ u8 addr_type;
+
+ if (cp->addr.type == BDADDR_LE_PUBLIC)
+ addr_type = ADDR_LE_DEV_PUBLIC;
+ else
+ addr_type = ADDR_LE_DEV_RANDOM;
+
+ hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type);
+
+ hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type);
+
+ err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type);
+ }
if (err < 0) {
err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
@@ -2633,6 +2781,16 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
mgmt_pending_remove(cmd);
}
+void mgmt_smp_complete(struct hci_conn *conn, bool complete)
+{
+ u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED;
+ struct pending_cmd *cmd;
+
+ cmd = find_pairing(conn);
+ if (cmd)
+ pairing_complete(cmd, status);
+}
+
static void pairing_complete_cb(struct hci_conn *conn, u8 status)
{
struct pending_cmd *cmd;
@@ -2646,7 +2804,7 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
pairing_complete(cmd, mgmt_status(status));
}
-static void le_connect_complete_cb(struct hci_conn *conn, u8 status)
+static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
{
struct pending_cmd *cmd;
@@ -2697,12 +2855,22 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
else
auth_type = HCI_AT_DEDICATED_BONDING_MITM;
- if (cp->addr.type == BDADDR_BREDR)
- conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr,
- cp->addr.type, sec_level, auth_type);
- else
- conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr,
- cp->addr.type, sec_level, auth_type);
+ if (cp->addr.type == BDADDR_BREDR) {
+ conn = hci_connect_acl(hdev, &cp->addr.bdaddr, sec_level,
+ auth_type);
+ } else {
+ u8 addr_type;
+
+ /* Convert from L2CAP channel address type to HCI address type
+ */
+ if (cp->addr.type == BDADDR_LE_PUBLIC)
+ addr_type = ADDR_LE_DEV_PUBLIC;
+ else
+ addr_type = ADDR_LE_DEV_RANDOM;
+
+ conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type,
+ sec_level, auth_type);
+ }
if (IS_ERR(conn)) {
int status;
@@ -2733,13 +2901,16 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
}
/* For LE, just connecting isn't a proof that the pairing finished */
- if (cp->addr.type == BDADDR_BREDR)
+ if (cp->addr.type == BDADDR_BREDR) {
conn->connect_cfm_cb = pairing_complete_cb;
- else
- conn->connect_cfm_cb = le_connect_complete_cb;
+ conn->security_cfm_cb = pairing_complete_cb;
+ conn->disconn_cfm_cb = pairing_complete_cb;
+ } else {
+ conn->connect_cfm_cb = le_pairing_complete_cb;
+ conn->security_cfm_cb = le_pairing_complete_cb;
+ conn->disconn_cfm_cb = le_pairing_complete_cb;
+ }
- conn->security_cfm_cb = pairing_complete_cb;
- conn->disconn_cfm_cb = pairing_complete_cb;
conn->io_capability = cp->io_cap;
cmd->user_data = conn;
@@ -3071,7 +3242,12 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
goto unlock;
}
- err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+ if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+ err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA,
+ 0, NULL);
+ else
+ err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+
if (err < 0)
mgmt_pending_remove(cmd);
@@ -3083,23 +3259,46 @@ unlock:
static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
- struct mgmt_cp_add_remote_oob_data *cp = data;
- u8 status;
int err;
BT_DBG("%s ", hdev->name);
hci_dev_lock(hdev);
- err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
- cp->randomizer);
- if (err < 0)
- status = MGMT_STATUS_FAILED;
- else
- status = MGMT_STATUS_SUCCESS;
+ if (len == MGMT_ADD_REMOTE_OOB_DATA_SIZE) {
+ struct mgmt_cp_add_remote_oob_data *cp = data;
+ u8 status;
- err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status,
- &cp->addr, sizeof(cp->addr));
+ err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
+ cp->hash, cp->randomizer);
+ if (err < 0)
+ status = MGMT_STATUS_FAILED;
+ else
+ status = MGMT_STATUS_SUCCESS;
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
+ status, &cp->addr, sizeof(cp->addr));
+ } else if (len == MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE) {
+ struct mgmt_cp_add_remote_oob_ext_data *cp = data;
+ u8 status;
+
+ err = hci_add_remote_oob_ext_data(hdev, &cp->addr.bdaddr,
+ cp->hash192,
+ cp->randomizer192,
+ cp->hash256,
+ cp->randomizer256);
+ if (err < 0)
+ status = MGMT_STATUS_FAILED;
+ else
+ status = MGMT_STATUS_SUCCESS;
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
+ status, &cp->addr, sizeof(cp->addr));
+ } else {
+ BT_ERR("add_remote_oob_data: invalid length of %u bytes", len);
+ err = cmd_status(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
+ MGMT_STATUS_INVALID_PARAMS);
+ }
hci_dev_unlock(hdev);
return err;
@@ -3195,7 +3394,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
struct hci_request req;
/* General inquiry access code (GIAC) */
u8 lap[3] = { 0x33, 0x8b, 0x9e };
- u8 status;
+ u8 status, own_addr_type;
int err;
BT_DBG("%s", hdev->name);
@@ -3280,18 +3479,31 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
goto failed;
}
- if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
+ /* 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 (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+ hci_req_add_le_scan_disable(&req);
+
+ memset(&param_cp, 0, sizeof(param_cp));
+
+ /* All active scans will be done with either a resolvable
+ * private address (when privacy feature has been enabled)
+ * or unresolvable private address.
+ */
+ err = hci_update_random_address(&req, true, &own_addr_type);
+ if (err < 0) {
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY);
+ MGMT_STATUS_FAILED);
mgmt_pending_remove(cmd);
goto failed;
}
- memset(&param_cp, 0, sizeof(param_cp));
param_cp.type = LE_SCAN_ACTIVE;
param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
- param_cp.own_address_type = hdev->own_addr_type;
+ param_cp.own_address_type = own_addr_type;
hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
&param_cp);
@@ -3361,7 +3573,6 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
struct hci_cp_remote_name_req_cancel cp;
struct inquiry_entry *e;
struct hci_request req;
- struct hci_cp_le_set_scan_enable enable_cp;
int err;
BT_DBG("%s", hdev->name);
@@ -3397,10 +3608,7 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
} else {
cancel_delayed_work(&hdev->le_scan_disable);
- memset(&enable_cp, 0, sizeof(enable_cp));
- enable_cp.enable = LE_SCAN_DISABLE;
- hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE,
- sizeof(enable_cp), &enable_cp);
+ hci_req_add_le_scan_disable(&req);
}
break;
@@ -3457,15 +3665,17 @@ static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data,
hci_dev_lock(hdev);
if (!hci_discovery_active(hdev)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
- MGMT_STATUS_FAILED);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
+ MGMT_STATUS_FAILED, &cp->addr,
+ sizeof(cp->addr));
goto failed;
}
e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr);
if (!e) {
- err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
- MGMT_STATUS_INVALID_PARAMS);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
+ MGMT_STATUS_INVALID_PARAMS, &cp->addr,
+ sizeof(cp->addr));
goto failed;
}
@@ -3754,6 +3964,21 @@ static int set_scan_params(struct sock *sk, struct hci_dev *hdev,
err = cmd_complete(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 0, NULL, 0);
+ /* If background scan is running, restart it so new parameters are
+ * loaded.
+ */
+ if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
+ hdev->discovery.state == DISCOVERY_STOPPED) {
+ struct hci_request req;
+
+ hci_req_init(&req, hdev);
+
+ hci_req_add_le_scan_disable(&req);
+ hci_req_add_le_passive_scan(&req);
+
+ hci_req_run(&req, NULL);
+ }
+
hci_dev_unlock(hdev);
return err;
@@ -3999,15 +4224,269 @@ unlock:
return err;
}
+static int set_secure_conn(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_mode *cp = data;
+ struct pending_cmd *cmd;
+ u8 val, status;
+ int err;
+
+ BT_DBG("request for %s", hdev->name);
+
+ status = mgmt_bredr_support(hdev);
+ if (status)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+ status);
+
+ if (!lmp_sc_capable(hdev) &&
+ !test_bit(HCI_FORCE_SC, &hdev->dev_flags))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ hci_dev_lock(hdev);
+
+ if (!hdev_is_powered(hdev)) {
+ bool changed;
+
+ if (cp->val) {
+ changed = !test_and_set_bit(HCI_SC_ENABLED,
+ &hdev->dev_flags);
+ if (cp->val == 0x02)
+ set_bit(HCI_SC_ONLY, &hdev->dev_flags);
+ else
+ clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+ } else {
+ changed = test_and_clear_bit(HCI_SC_ENABLED,
+ &hdev->dev_flags);
+ clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+ }
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev);
+ if (err < 0)
+ goto failed;
+
+ if (changed)
+ err = new_settings(hdev, sk);
+
+ goto failed;
+ }
+
+ if (mgmt_pending_find(MGMT_OP_SET_SECURE_CONN, hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+ MGMT_STATUS_BUSY);
+ goto failed;
+ }
+
+ val = !!cp->val;
+
+ if (val == test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
+ (cp->val == 0x02) == test_bit(HCI_SC_ONLY, &hdev->dev_flags)) {
+ err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev);
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_SECURE_CONN, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_SC_SUPPORT, 1, &val);
+ if (err < 0) {
+ mgmt_pending_remove(cmd);
+ goto failed;
+ }
+
+ if (cp->val == 0x02)
+ set_bit(HCI_SC_ONLY, &hdev->dev_flags);
+ else
+ clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+
+failed:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
+static int set_debug_keys(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_mode *cp = data;
+ bool changed;
+ int err;
+
+ BT_DBG("request for %s", hdev->name);
+
+ if (cp->val != 0x00 && cp->val != 0x01)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_DEBUG_KEYS,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ hci_dev_lock(hdev);
+
+ if (cp->val)
+ changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+ else
+ changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_DEBUG_KEYS, hdev);
+ if (err < 0)
+ goto unlock;
+
+ if (changed)
+ err = new_settings(hdev, sk);
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
+static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data,
+ u16 len)
+{
+ struct mgmt_cp_set_privacy *cp = cp_data;
+ bool changed;
+ int err;
+
+ BT_DBG("request for %s", hdev->name);
+
+ if (!lmp_le_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ if (cp->privacy != 0x00 && cp->privacy != 0x01)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ if (hdev_is_powered(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
+ MGMT_STATUS_REJECTED);
+
+ hci_dev_lock(hdev);
+
+ /* If user space supports this command it is also expected to
+ * handle IRKs. Therefore, set the HCI_RPA_RESOLVING flag.
+ */
+ set_bit(HCI_RPA_RESOLVING, &hdev->dev_flags);
+
+ if (cp->privacy) {
+ changed = !test_and_set_bit(HCI_PRIVACY, &hdev->dev_flags);
+ memcpy(hdev->irk, cp->irk, sizeof(hdev->irk));
+ set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+ } else {
+ changed = test_and_clear_bit(HCI_PRIVACY, &hdev->dev_flags);
+ memset(hdev->irk, 0, sizeof(hdev->irk));
+ clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+ }
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_PRIVACY, hdev);
+ if (err < 0)
+ goto unlock;
+
+ if (changed)
+ err = new_settings(hdev, sk);
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
+static bool irk_is_valid(struct mgmt_irk_info *irk)
+{
+ switch (irk->addr.type) {
+ case BDADDR_LE_PUBLIC:
+ return true;
+
+ case BDADDR_LE_RANDOM:
+ /* Two most significant bits shall be set */
+ if ((irk->addr.bdaddr.b[5] & 0xc0) != 0xc0)
+ return false;
+ return true;
+ }
+
+ return false;
+}
+
+static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
+ u16 len)
+{
+ struct mgmt_cp_load_irks *cp = cp_data;
+ u16 irk_count, expected_len;
+ int i, err;
+
+ BT_DBG("request for %s", hdev->name);
+
+ if (!lmp_le_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ irk_count = __le16_to_cpu(cp->irk_count);
+
+ expected_len = sizeof(*cp) + irk_count * sizeof(struct mgmt_irk_info);
+ if (expected_len != len) {
+ BT_ERR("load_irks: expected %u bytes, got %u bytes",
+ expected_len, len);
+ return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
+ MGMT_STATUS_INVALID_PARAMS);
+ }
+
+ BT_DBG("%s irk_count %u", hdev->name, irk_count);
+
+ for (i = 0; i < irk_count; i++) {
+ struct mgmt_irk_info *key = &cp->irks[i];
+
+ if (!irk_is_valid(key))
+ return cmd_status(sk, hdev->id,
+ MGMT_OP_LOAD_IRKS,
+ MGMT_STATUS_INVALID_PARAMS);
+ }
+
+ hci_dev_lock(hdev);
+
+ hci_smp_irks_clear(hdev);
+
+ for (i = 0; i < irk_count; i++) {
+ struct mgmt_irk_info *irk = &cp->irks[i];
+ u8 addr_type;
+
+ if (irk->addr.type == BDADDR_LE_PUBLIC)
+ addr_type = ADDR_LE_DEV_PUBLIC;
+ else
+ addr_type = ADDR_LE_DEV_RANDOM;
+
+ hci_add_irk(hdev, &irk->addr.bdaddr, addr_type, irk->val,
+ BDADDR_ANY);
+ }
+
+ set_bit(HCI_RPA_RESOLVING, &hdev->dev_flags);
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_IRKS, 0, NULL, 0);
+
+ hci_dev_unlock(hdev);
+
+ return err;
+}
+
static bool ltk_is_valid(struct mgmt_ltk_info *key)
{
- if (key->authenticated != 0x00 && key->authenticated != 0x01)
- return false;
if (key->master != 0x00 && key->master != 0x01)
return false;
- if (!bdaddr_type_is_le(key->addr.type))
- return false;
- return true;
+
+ switch (key->addr.type) {
+ case BDADDR_LE_PUBLIC:
+ return true;
+
+ case BDADDR_LE_RANDOM:
+ /* Two most significant bits shall be set */
+ if ((key->addr.bdaddr.b[5] & 0xc0) != 0xc0)
+ return false;
+ return true;
+ }
+
+ return false;
}
static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
@@ -4029,7 +4508,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
sizeof(struct mgmt_ltk_info);
if (expected_len != len) {
BT_ERR("load_keys: expected %u bytes, got %u bytes",
- len, expected_len);
+ expected_len, len);
return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
MGMT_STATUS_INVALID_PARAMS);
}
@@ -4063,9 +4542,9 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
else
type = HCI_SMP_LTK_SLAVE;
- hci_add_ltk(hdev, &key->addr.bdaddr, addr_type,
- type, 0, key->authenticated, key->val,
- key->enc_size, key->ediv, key->rand);
+ hci_add_ltk(hdev, &key->addr.bdaddr, addr_type, type,
+ key->type, key->val, key->enc_size, key->ediv,
+ key->rand);
}
err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0,
@@ -4115,7 +4594,7 @@ static const struct mgmt_handler {
{ user_passkey_reply, false, MGMT_USER_PASSKEY_REPLY_SIZE },
{ user_passkey_neg_reply, false, MGMT_USER_PASSKEY_NEG_REPLY_SIZE },
{ read_local_oob_data, false, MGMT_READ_LOCAL_OOB_DATA_SIZE },
- { add_remote_oob_data, false, MGMT_ADD_REMOTE_OOB_DATA_SIZE },
+ { add_remote_oob_data, true, MGMT_ADD_REMOTE_OOB_DATA_SIZE },
{ remove_remote_oob_data, false, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE },
{ start_discovery, false, MGMT_START_DISCOVERY_SIZE },
{ stop_discovery, false, MGMT_STOP_DISCOVERY_SIZE },
@@ -4127,6 +4606,10 @@ static const struct mgmt_handler {
{ set_bredr, false, MGMT_SETTING_SIZE },
{ set_static_address, false, MGMT_SET_STATIC_ADDRESS_SIZE },
{ set_scan_params, false, MGMT_SET_SCAN_PARAMS_SIZE },
+ { set_secure_conn, false, MGMT_SETTING_SIZE },
+ { set_debug_keys, false, MGMT_SETTING_SIZE },
+ { set_privacy, false, MGMT_SET_PRIVACY_SIZE },
+ { load_irks, true, MGMT_LOAD_IRKS_SIZE },
};
@@ -4243,6 +4726,17 @@ void mgmt_index_removed(struct hci_dev *hdev)
mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
}
+/* This function requires the caller holds hdev->lock */
+static void restart_le_auto_conns(struct hci_dev *hdev)
+{
+ struct hci_conn_params *p;
+
+ list_for_each_entry(p, &hdev->le_conn_params, list) {
+ if (p->auto_connect == HCI_AUTO_CONN_ALWAYS)
+ hci_pend_le_conn_add(hdev, &p->addr, p->addr_type);
+ }
+}
+
static void powered_complete(struct hci_dev *hdev, u8 status)
{
struct cmd_lookup match = { NULL, hdev };
@@ -4251,6 +4745,8 @@ static void powered_complete(struct hci_dev *hdev, u8 status)
hci_dev_lock(hdev);
+ restart_le_auto_conns(hdev);
+
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
new_settings(hdev, match.sk);
@@ -4292,11 +4788,6 @@ static int powered_update_hci(struct hci_dev *hdev)
}
if (lmp_le_capable(hdev)) {
- /* Set random address to static address if configured */
- if (bacmp(&hdev->static_addr, BDADDR_ANY))
- hci_req_add(&req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
- &hdev->static_addr);
-
/* 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.
@@ -4422,6 +4913,10 @@ void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev))
return;
+ /* Powering off may clear the scan mode - don't let that interfere */
+ if (!discoverable && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
+ return;
+
if (discoverable) {
changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
} else {
@@ -4455,6 +4950,10 @@ void mgmt_connectable(struct hci_dev *hdev, u8 connectable)
if (mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev))
return;
+ /* Powering off may clear the scan mode - don't let that interfere */
+ if (!connectable && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
+ return;
+
if (connectable)
changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
else
@@ -4464,6 +4963,18 @@ void mgmt_connectable(struct hci_dev *hdev, u8 connectable)
new_settings(hdev, NULL);
}
+void mgmt_advertising(struct hci_dev *hdev, u8 advertising)
+{
+ /* Powering off may stop advertising - don't let that interfere */
+ if (!advertising && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
+ return;
+
+ if (advertising)
+ set_bit(HCI_ADVERTISING, &hdev->dev_flags);
+ else
+ clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+}
+
void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
{
u8 mgmt_err = mgmt_status(status);
@@ -4494,28 +5005,104 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
}
-void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent)
{
struct mgmt_ev_new_long_term_key ev;
memset(&ev, 0, sizeof(ev));
- ev.store_hint = persistent;
+ /* Devices using resolvable or non-resolvable random addresses
+ * without providing an indentity resolving key don't require
+ * to store long term keys. Their addresses will change the
+ * next time around.
+ *
+ * Only when a remote device provides an identity address
+ * make sure the long term key is stored. If the remote
+ * identity is known, the long term keys are internally
+ * mapped to the identity address. So allow static random
+ * and public addresses here.
+ */
+ if (key->bdaddr_type == ADDR_LE_DEV_RANDOM &&
+ (key->bdaddr.b[5] & 0xc0) != 0xc0)
+ ev.store_hint = 0x00;
+ else
+ ev.store_hint = persistent;
+
bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
ev.key.addr.type = link_to_bdaddr(LE_LINK, key->bdaddr_type);
- ev.key.authenticated = key->authenticated;
+ ev.key.type = key->authenticated;
ev.key.enc_size = key->enc_size;
ev.key.ediv = key->ediv;
+ ev.key.rand = key->rand;
if (key->type == HCI_SMP_LTK)
ev.key.master = 1;
- memcpy(ev.key.rand, key->rand, sizeof(key->rand));
memcpy(ev.key.val, key->val, sizeof(key->val));
mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
}
+void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk)
+{
+ struct mgmt_ev_new_irk ev;
+
+ memset(&ev, 0, sizeof(ev));
+
+ /* For identity resolving keys from devices that are already
+ * using a public address or static random address, do not
+ * ask for storing this key. The identity resolving key really
+ * is only mandatory for devices using resovlable random
+ * addresses.
+ *
+ * Storing all identity resolving keys has the downside that
+ * they will be also loaded on next boot of they system. More
+ * identity resolving keys, means more time during scanning is
+ * needed to actually resolve these addresses.
+ */
+ if (bacmp(&irk->rpa, BDADDR_ANY))
+ ev.store_hint = 0x01;
+ else
+ ev.store_hint = 0x00;
+
+ bacpy(&ev.rpa, &irk->rpa);
+ bacpy(&ev.irk.addr.bdaddr, &irk->bdaddr);
+ ev.irk.addr.type = link_to_bdaddr(LE_LINK, irk->addr_type);
+ memcpy(ev.irk.val, irk->val, sizeof(irk->val));
+
+ mgmt_event(MGMT_EV_NEW_IRK, hdev, &ev, sizeof(ev), NULL);
+}
+
+void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk,
+ bool persistent)
+{
+ struct mgmt_ev_new_csrk ev;
+
+ memset(&ev, 0, sizeof(ev));
+
+ /* Devices using resolvable or non-resolvable random addresses
+ * without providing an indentity resolving key don't require
+ * to store signature resolving keys. Their addresses will change
+ * the next time around.
+ *
+ * Only when a remote device provides an identity address
+ * make sure the signature resolving key is stored. So allow
+ * static random and public addresses here.
+ */
+ if (csrk->bdaddr_type == ADDR_LE_DEV_RANDOM &&
+ (csrk->bdaddr.b[5] & 0xc0) != 0xc0)
+ ev.store_hint = 0x00;
+ else
+ ev.store_hint = persistent;
+
+ bacpy(&ev.key.addr.bdaddr, &csrk->bdaddr);
+ ev.key.addr.type = link_to_bdaddr(LE_LINK, csrk->bdaddr_type);
+ ev.key.master = csrk->master;
+ memcpy(ev.key.val, csrk->val, sizeof(csrk->val));
+
+ mgmt_event(MGMT_EV_NEW_CSRK, hdev, &ev, sizeof(ev), NULL);
+}
+
static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
u8 data_len)
{
@@ -4590,11 +5177,29 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
}
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 link_type, u8 addr_type, u8 reason)
+ u8 link_type, u8 addr_type, u8 reason,
+ bool mgmt_connected)
{
struct mgmt_ev_device_disconnected ev;
+ struct pending_cmd *power_off;
struct sock *sk = NULL;
+ power_off = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
+ if (power_off) {
+ struct mgmt_mode *cp = power_off->param;
+
+ /* The connection is still in hci_conn_hash so test for 1
+ * instead of 0 to know if this is the last one.
+ */
+ if (!cp->val && hci_conn_count(hdev) == 1) {
+ cancel_delayed_work(&hdev->power_off);
+ queue_work(hdev->req_workqueue, &hdev->power_off.work);
+ }
+ }
+
+ if (!mgmt_connected)
+ return;
+
if (link_type != ACL_LINK && link_type != LE_LINK)
return;
@@ -4649,6 +5254,20 @@ void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 status)
{
struct mgmt_ev_connect_failed ev;
+ struct pending_cmd *power_off;
+
+ power_off = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
+ if (power_off) {
+ struct mgmt_mode *cp = power_off->param;
+
+ /* The connection is still in hci_conn_hash so test for 1
+ * instead of 0 to know if this is the last one.
+ */
+ if (!cp->val && hci_conn_count(hdev) == 1) {
+ cancel_delayed_work(&hdev->power_off);
+ queue_work(hdev->req_workqueue, &hdev->power_off.work);
+ }
+ }
bacpy(&ev.addr.bdaddr, bdaddr);
ev.addr.type = link_to_bdaddr(link_type, addr_type);
@@ -4707,7 +5326,7 @@ void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
}
int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 link_type, u8 addr_type, __le32 value,
+ u8 link_type, u8 addr_type, u32 value,
u8 confirm_hint)
{
struct mgmt_ev_user_confirm_request ev;
@@ -4717,7 +5336,7 @@ int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
bacpy(&ev.addr.bdaddr, bdaddr);
ev.addr.type = link_to_bdaddr(link_type, addr_type);
ev.confirm_hint = confirm_hint;
- ev.value = value;
+ ev.value = cpu_to_le32(value);
return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev),
NULL);
@@ -4910,6 +5529,43 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
hci_req_run(&req, NULL);
}
+void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
+{
+ struct cmd_lookup match = { NULL, hdev };
+ bool changed = false;
+
+ if (status) {
+ u8 mgmt_err = mgmt_status(status);
+
+ if (enable) {
+ if (test_and_clear_bit(HCI_SC_ENABLED,
+ &hdev->dev_flags))
+ new_settings(hdev, NULL);
+ clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+ }
+
+ mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev,
+ cmd_status_rsp, &mgmt_err);
+ return;
+ }
+
+ if (enable) {
+ changed = !test_and_set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+ } else {
+ changed = test_and_clear_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+ clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+ }
+
+ mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev,
+ settings_rsp, &match);
+
+ if (changed)
+ new_settings(hdev, match.sk);
+
+ if (match.sk)
+ sock_put(match.sk);
+}
+
static void sk_lookup(struct pending_cmd *cmd, void *data)
{
struct cmd_lookup *match = data;
@@ -4964,8 +5620,9 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
cmd ? cmd->sk : NULL);
}
-void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
- u8 *randomizer, u8 status)
+void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
+ u8 *randomizer192, u8 *hash256,
+ u8 *randomizer256, u8 status)
{
struct pending_cmd *cmd;
@@ -4979,13 +5636,32 @@ void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
mgmt_status(status));
} else {
- struct mgmt_rp_read_local_oob_data rp;
+ if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
+ hash256 && randomizer256) {
+ struct mgmt_rp_read_local_oob_ext_data rp;
+
+ memcpy(rp.hash192, hash192, sizeof(rp.hash192));
+ memcpy(rp.randomizer192, randomizer192,
+ sizeof(rp.randomizer192));
- memcpy(rp.hash, hash, sizeof(rp.hash));
- memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
+ memcpy(rp.hash256, hash256, sizeof(rp.hash256));
+ memcpy(rp.randomizer256, randomizer256,
+ sizeof(rp.randomizer256));
+
+ cmd_complete(cmd->sk, hdev->id,
+ MGMT_OP_READ_LOCAL_OOB_DATA, 0,
+ &rp, sizeof(rp));
+ } else {
+ struct mgmt_rp_read_local_oob_data rp;
- cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
- 0, &rp, sizeof(rp));
+ memcpy(rp.hash, hash192, sizeof(rp.hash));
+ memcpy(rp.randomizer, randomizer192,
+ sizeof(rp.randomizer));
+
+ cmd_complete(cmd->sk, hdev->id,
+ MGMT_OP_READ_LOCAL_OOB_DATA, 0,
+ &rp, sizeof(rp));
+ }
}
mgmt_pending_remove(cmd);
@@ -4997,6 +5673,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
{
char buf[512];
struct mgmt_ev_device_found *ev = (void *) buf;
+ struct smp_irk *irk;
size_t ev_size;
if (!hci_discovery_active(hdev))
@@ -5008,13 +5685,20 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
memset(buf, 0, sizeof(buf));
- bacpy(&ev->addr.bdaddr, bdaddr);
- ev->addr.type = link_to_bdaddr(link_type, addr_type);
+ irk = hci_get_irk(hdev, bdaddr, addr_type);
+ if (irk) {
+ bacpy(&ev->addr.bdaddr, &irk->bdaddr);
+ ev->addr.type = link_to_bdaddr(link_type, irk->addr_type);
+ } else {
+ bacpy(&ev->addr.bdaddr, bdaddr);
+ ev->addr.type = link_to_bdaddr(link_type, addr_type);
+ }
+
ev->rssi = rssi;
if (cfm_name)
- ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
+ ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
if (!ssp)
- ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
+ ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
if (eir_len > 0)
memcpy(ev->eir, eir, eir_len);
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index facd8a79c038..633cceeb943e 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -216,6 +216,7 @@ static int rfcomm_check_security(struct rfcomm_dlc *d)
switch (d->sec_level) {
case BT_SECURITY_HIGH:
+ case BT_SECURITY_FIPS:
auth_type = HCI_AT_GENERAL_BONDING_MITM;
break;
case BT_SECURITY_MEDIUM:
@@ -359,6 +360,11 @@ static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
return NULL;
}
+static int rfcomm_check_channel(u8 channel)
+{
+ return channel < 1 || channel > 30;
+}
+
static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
{
struct rfcomm_session *s;
@@ -368,7 +374,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
BT_DBG("dlc %p state %ld %pMR -> %pMR channel %d",
d, d->state, src, dst, channel);
- if (channel < 1 || channel > 30)
+ if (rfcomm_check_channel(channel))
return -EINVAL;
if (d->state != BT_OPEN && d->state != BT_CLOSED)
@@ -425,6 +431,20 @@ int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 chann
return r;
}
+static void __rfcomm_dlc_disconn(struct rfcomm_dlc *d)
+{
+ struct rfcomm_session *s = d->session;
+
+ d->state = BT_DISCONN;
+ if (skb_queue_empty(&d->tx_queue)) {
+ rfcomm_send_disc(s, d->dlci);
+ rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT);
+ } else {
+ rfcomm_queue_disc(d);
+ rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2);
+ }
+}
+
static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
{
struct rfcomm_session *s = d->session;
@@ -437,32 +457,29 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
switch (d->state) {
case BT_CONNECT:
case BT_CONFIG:
+ case BT_OPEN:
+ case BT_CONNECT2:
if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
rfcomm_schedule();
- break;
+ return 0;
}
- /* Fall through */
+ }
+ switch (d->state) {
+ case BT_CONNECT:
case BT_CONNECTED:
- d->state = BT_DISCONN;
- if (skb_queue_empty(&d->tx_queue)) {
- rfcomm_send_disc(s, d->dlci);
- rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT);
- } else {
- rfcomm_queue_disc(d);
- rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2);
- }
+ __rfcomm_dlc_disconn(d);
break;
- case BT_OPEN:
- case BT_CONNECT2:
- if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
- set_bit(RFCOMM_AUTH_REJECT, &d->flags);
- rfcomm_schedule();
+ case BT_CONFIG:
+ if (s->state != BT_BOUND) {
+ __rfcomm_dlc_disconn(d);
break;
}
- /* Fall through */
+ /* if closing a dlc in a session that hasn't been started,
+ * just close and unlink the dlc
+ */
default:
rfcomm_dlc_clear_timer(d);
@@ -513,6 +530,25 @@ no_session:
return r;
}
+struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel)
+{
+ struct rfcomm_session *s;
+ struct rfcomm_dlc *dlc = NULL;
+ u8 dlci;
+
+ if (rfcomm_check_channel(channel))
+ return ERR_PTR(-EINVAL);
+
+ rfcomm_lock();
+ s = rfcomm_session_get(src, dst);
+ if (s) {
+ dlci = __dlci(!s->initiator, channel);
+ dlc = rfcomm_dlc_get(s, dlci);
+ }
+ rfcomm_unlock();
+ return dlc;
+}
+
int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
{
int len = skb->len;
@@ -533,6 +569,20 @@ int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
return len;
}
+void rfcomm_dlc_send_noerror(struct rfcomm_dlc *d, struct sk_buff *skb)
+{
+ int len = skb->len;
+
+ BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);
+
+ rfcomm_make_uih(skb, d->addr);
+ skb_queue_tail(&d->tx_queue, skb);
+
+ if (d->state == BT_CONNECTED &&
+ !test_bit(RFCOMM_TX_THROTTLED, &d->flags))
+ rfcomm_schedule();
+}
+
void __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
{
BT_DBG("dlc %p state %ld", d, d->state);
@@ -718,7 +768,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
bacpy(&addr.l2_bdaddr, dst);
addr.l2_family = AF_BLUETOOTH;
- addr.l2_psm = __constant_cpu_to_le16(RFCOMM_PSM);
+ addr.l2_psm = cpu_to_le16(RFCOMM_PSM);
addr.l2_cid = 0;
addr.l2_bdaddr_type = BDADDR_BREDR;
*err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
@@ -1943,12 +1993,11 @@ static void rfcomm_process_sessions(void)
continue;
}
- if (s->state == BT_LISTEN) {
+ switch (s->state) {
+ case BT_LISTEN:
rfcomm_accept_connection(s);
continue;
- }
- switch (s->state) {
case BT_BOUND:
s = rfcomm_check_connection(s);
break;
@@ -1983,7 +2032,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
/* Bind socket */
bacpy(&addr.l2_bdaddr, ba);
addr.l2_family = AF_BLUETOOTH;
- addr.l2_psm = __constant_cpu_to_le16(RFCOMM_PSM);
+ addr.l2_psm = cpu_to_le16(RFCOMM_PSM);
addr.l2_cid = 0;
addr.l2_bdaddr_type = BDADDR_BREDR;
err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
@@ -2085,7 +2134,8 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
set_bit(RFCOMM_SEC_PENDING, &d->flags);
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
continue;
- } else if (d->sec_level == BT_SECURITY_HIGH) {
+ } else if (d->sec_level == BT_SECURITY_HIGH ||
+ d->sec_level == BT_SECURITY_FIPS) {
set_bit(RFCOMM_ENC_DROP, &d->flags);
continue;
}
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 3c2d3e4aa2f5..eabd25ab5ad9 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -105,13 +105,18 @@ static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
}
/* ---- Socket functions ---- */
-static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src)
+static struct sock *__rfcomm_get_listen_sock_by_addr(u8 channel, bdaddr_t *src)
{
struct sock *sk = NULL;
sk_for_each(sk, &rfcomm_sk_list.head) {
- if (rfcomm_pi(sk)->channel == channel &&
- !bacmp(&rfcomm_pi(sk)->src, src))
+ if (rfcomm_pi(sk)->channel != channel)
+ continue;
+
+ if (bacmp(&rfcomm_pi(sk)->src, src))
+ continue;
+
+ if (sk->sk_state == BT_BOUND || sk->sk_state == BT_LISTEN)
break;
}
@@ -331,6 +336,7 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr
{
struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
struct sock *sk = sock->sk;
+ int chan = sa->rc_channel;
int err = 0;
BT_DBG("sk %p %pMR", sk, &sa->rc_bdaddr);
@@ -352,12 +358,12 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr
write_lock(&rfcomm_sk_list.lock);
- if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) {
+ if (chan && __rfcomm_get_listen_sock_by_addr(chan, &sa->rc_bdaddr)) {
err = -EADDRINUSE;
} else {
/* Save source address */
bacpy(&rfcomm_pi(sk)->src, &sa->rc_bdaddr);
- rfcomm_pi(sk)->channel = sa->rc_channel;
+ rfcomm_pi(sk)->channel = chan;
sk->sk_state = BT_BOUND;
}
@@ -439,7 +445,7 @@ static int rfcomm_sock_listen(struct socket *sock, int backlog)
write_lock(&rfcomm_sk_list.lock);
for (channel = 1; channel < 31; channel++)
- if (!__rfcomm_get_sock_by_addr(channel, src)) {
+ if (!__rfcomm_get_listen_sock_by_addr(channel, src)) {
rfcomm_pi(sk)->channel = channel;
err = 0;
break;
@@ -528,6 +534,10 @@ static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *
BT_DBG("sock %p, sk %p", sock, sk);
+ if (peer && sk->sk_state != BT_CONNECTED &&
+ sk->sk_state != BT_CONNECT && sk->sk_state != BT_CONNECT2)
+ return -ENOTCONN;
+
memset(sa, 0, sizeof(*sa));
sa->rc_family = AF_BLUETOOTH;
sa->rc_channel = rfcomm_pi(sk)->channel;
@@ -648,6 +658,11 @@ static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __u
break;
}
+ if (opt & RFCOMM_LM_FIPS) {
+ err = -EINVAL;
+ break;
+ }
+
if (opt & RFCOMM_LM_AUTH)
rfcomm_pi(sk)->sec_level = BT_SECURITY_LOW;
if (opt & RFCOMM_LM_ENCRYPT)
@@ -762,7 +777,11 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
break;
case BT_SECURITY_HIGH:
opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT |
- RFCOMM_LM_SECURE;
+ RFCOMM_LM_SECURE;
+ break;
+ case BT_SECURITY_FIPS:
+ opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT |
+ RFCOMM_LM_SECURE | RFCOMM_LM_FIPS;
break;
default:
opt = 0;
@@ -774,6 +793,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
if (put_user(opt, (u32 __user *) optval))
err = -EFAULT;
+
break;
case RFCOMM_CONNINFO:
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index f9c0980abeea..403ec09f480a 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -40,6 +40,7 @@
#define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */
#define RFCOMM_TTY_MINOR 0
+static DEFINE_MUTEX(rfcomm_ioctl_mutex);
static struct tty_driver *rfcomm_tty_driver;
struct rfcomm_dev {
@@ -51,6 +52,8 @@ struct rfcomm_dev {
unsigned long flags;
int err;
+ unsigned long status; /* don't export to userspace */
+
bdaddr_t src;
bdaddr_t dst;
u8 channel;
@@ -58,7 +61,6 @@ struct rfcomm_dev {
uint modem_status;
struct rfcomm_dlc *dlc;
- wait_queue_head_t conn_wait;
struct device *tty_dev;
@@ -83,10 +85,6 @@ static void rfcomm_dev_destruct(struct tty_port *port)
BT_DBG("dev %p dlc %p", dev, dlc);
- spin_lock(&rfcomm_dev_lock);
- list_del(&dev->list);
- spin_unlock(&rfcomm_dev_lock);
-
rfcomm_dlc_lock(dlc);
/* Detach DLC if it's owned by this dev */
if (dlc->owner == dev)
@@ -95,7 +93,12 @@ static void rfcomm_dev_destruct(struct tty_port *port)
rfcomm_dlc_put(dlc);
- tty_unregister_device(rfcomm_tty_driver, dev->id);
+ if (dev->tty_dev)
+ tty_unregister_device(rfcomm_tty_driver, dev->id);
+
+ spin_lock(&rfcomm_dev_lock);
+ list_del(&dev->list);
+ spin_unlock(&rfcomm_dev_lock);
kfree(dev);
@@ -104,60 +107,24 @@ static void rfcomm_dev_destruct(struct tty_port *port)
module_put(THIS_MODULE);
}
-static struct device *rfcomm_get_device(struct rfcomm_dev *dev)
-{
- struct hci_dev *hdev;
- struct hci_conn *conn;
-
- hdev = hci_get_route(&dev->dst, &dev->src);
- if (!hdev)
- return NULL;
-
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &dev->dst);
-
- hci_dev_put(hdev);
-
- return conn ? &conn->dev : NULL;
-}
-
/* device-specific initialization: open the dlc */
static int rfcomm_dev_activate(struct tty_port *port, struct tty_struct *tty)
{
struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
- DEFINE_WAIT(wait);
int err;
err = rfcomm_dlc_open(dev->dlc, &dev->src, &dev->dst, dev->channel);
if (err)
- return err;
-
- while (1) {
- prepare_to_wait(&dev->conn_wait, &wait, TASK_INTERRUPTIBLE);
-
- if (dev->dlc->state == BT_CLOSED) {
- err = -dev->err;
- break;
- }
-
- if (dev->dlc->state == BT_CONNECTED)
- break;
-
- if (signal_pending(current)) {
- err = -ERESTARTSYS;
- break;
- }
-
- tty_unlock(tty);
- schedule();
- tty_lock(tty);
- }
- finish_wait(&dev->conn_wait, &wait);
+ set_bit(TTY_IO_ERROR, &tty->flags);
+ return err;
+}
- if (!err)
- device_move(dev->tty_dev, rfcomm_get_device(dev),
- DPM_ORDER_DEV_AFTER_PARENT);
+/* we block the open until the dlc->state becomes BT_CONNECTED */
+static int rfcomm_dev_carrier_raised(struct tty_port *port)
+{
+ struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
- return err;
+ return (dev->dlc->state == BT_CONNECTED);
}
/* device-specific cleanup: close the dlc */
@@ -176,9 +143,10 @@ static const struct tty_port_operations rfcomm_port_ops = {
.destruct = rfcomm_dev_destruct,
.activate = rfcomm_dev_activate,
.shutdown = rfcomm_dev_shutdown,
+ .carrier_raised = rfcomm_dev_carrier_raised,
};
-static struct rfcomm_dev *__rfcomm_dev_get(int id)
+static struct rfcomm_dev *__rfcomm_dev_lookup(int id)
{
struct rfcomm_dev *dev;
@@ -195,20 +163,41 @@ static struct rfcomm_dev *rfcomm_dev_get(int id)
spin_lock(&rfcomm_dev_lock);
- dev = __rfcomm_dev_get(id);
+ dev = __rfcomm_dev_lookup(id);
- if (dev) {
- if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
- dev = NULL;
- else
- tty_port_get(&dev->port);
- }
+ if (dev && !tty_port_get(&dev->port))
+ dev = NULL;
spin_unlock(&rfcomm_dev_lock);
return dev;
}
+static void rfcomm_reparent_device(struct rfcomm_dev *dev)
+{
+ struct hci_dev *hdev;
+ struct hci_conn *conn;
+
+ hdev = hci_get_route(&dev->dst, &dev->src);
+ if (!hdev)
+ return;
+
+ /* The lookup results are unsafe to access without the
+ * hci device lock (FIXME: why is this not documented?)
+ */
+ hci_dev_lock(hdev);
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &dev->dst);
+
+ /* Just because the acl link is in the hash table is no
+ * guarantee the sysfs device has been added ...
+ */
+ if (conn && device_is_registered(&conn->dev))
+ device_move(dev->tty_dev, &conn->dev, DPM_ORDER_DEV_AFTER_PARENT);
+
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+}
+
static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf)
{
struct rfcomm_dev *dev = dev_get_drvdata(tty_dev);
@@ -224,17 +213,16 @@ static ssize_t show_channel(struct device *tty_dev, struct device_attribute *att
static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL);
-static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
+static struct rfcomm_dev *__rfcomm_dev_add(struct rfcomm_dev_req *req,
+ struct rfcomm_dlc *dlc)
{
struct rfcomm_dev *dev, *entry;
struct list_head *head = &rfcomm_dev_list;
int err = 0;
- BT_DBG("id %d channel %d", req->dev_id, req->channel);
-
dev = kzalloc(sizeof(struct rfcomm_dev), GFP_KERNEL);
if (!dev)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
spin_lock(&rfcomm_dev_lock);
@@ -282,7 +270,6 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
tty_port_init(&dev->port);
dev->port.ops = &rfcomm_port_ops;
- init_waitqueue_head(&dev->conn_wait);
skb_queue_head_init(&dev->pending);
@@ -318,22 +305,37 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
holds reference to this module. */
__module_get(THIS_MODULE);
+ spin_unlock(&rfcomm_dev_lock);
+ return dev;
+
out:
spin_unlock(&rfcomm_dev_lock);
+ kfree(dev);
+ return ERR_PTR(err);
+}
+
+static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
+{
+ struct rfcomm_dev *dev;
+ struct device *tty;
+
+ BT_DBG("id %d channel %d", req->dev_id, req->channel);
- if (err < 0)
- goto free;
+ dev = __rfcomm_dev_add(req, dlc);
+ if (IS_ERR(dev)) {
+ rfcomm_dlc_put(dlc);
+ return PTR_ERR(dev);
+ }
- dev->tty_dev = tty_port_register_device(&dev->port, rfcomm_tty_driver,
+ tty = tty_port_register_device(&dev->port, rfcomm_tty_driver,
dev->id, NULL);
- if (IS_ERR(dev->tty_dev)) {
- err = PTR_ERR(dev->tty_dev);
- spin_lock(&rfcomm_dev_lock);
- list_del(&dev->list);
- spin_unlock(&rfcomm_dev_lock);
- goto free;
+ if (IS_ERR(tty)) {
+ tty_port_put(&dev->port);
+ return PTR_ERR(tty);
}
+ dev->tty_dev = tty;
+ rfcomm_reparent_device(dev);
dev_set_drvdata(dev->tty_dev, dev);
if (device_create_file(dev->tty_dev, &dev_attr_address) < 0)
@@ -343,24 +345,23 @@ out:
BT_ERR("Failed to create channel attribute");
return dev->id;
-
-free:
- kfree(dev);
- return err;
}
/* ---- Send buffer ---- */
-static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
+static inline unsigned int rfcomm_room(struct rfcomm_dev *dev)
{
- /* We can't let it be zero, because we don't get a callback
- when tx_credits becomes nonzero, hence we'd never wake up */
- return dlc->mtu * (dlc->tx_credits?:1);
+ struct rfcomm_dlc *dlc = dev->dlc;
+
+ /* Limit the outstanding number of packets not yet sent to 40 */
+ int pending = 40 - atomic_read(&dev->wmem_alloc);
+
+ return max(0, pending) * dlc->mtu;
}
static void rfcomm_wfree(struct sk_buff *skb)
{
struct rfcomm_dev *dev = (void *) skb->sk;
- atomic_sub(skb->truesize, &dev->wmem_alloc);
+ atomic_dec(&dev->wmem_alloc);
if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
tty_port_tty_wakeup(&dev->port);
tty_port_put(&dev->port);
@@ -369,28 +370,24 @@ static void rfcomm_wfree(struct sk_buff *skb)
static void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev)
{
tty_port_get(&dev->port);
- atomic_add(skb->truesize, &dev->wmem_alloc);
+ atomic_inc(&dev->wmem_alloc);
skb->sk = (void *) dev;
skb->destructor = rfcomm_wfree;
}
static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, gfp_t priority)
{
- if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
- struct sk_buff *skb = alloc_skb(size, priority);
- if (skb) {
- rfcomm_set_owner_w(skb, dev);
- return skb;
- }
- }
- return NULL;
+ struct sk_buff *skb = alloc_skb(size, priority);
+ if (skb)
+ rfcomm_set_owner_w(skb, dev);
+ return skb;
}
/* ---- Device IOCTLs ---- */
#define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP))
-static int rfcomm_create_dev(struct sock *sk, void __user *arg)
+static int __rfcomm_create_dev(struct sock *sk, void __user *arg)
{
struct rfcomm_dev_req req;
struct rfcomm_dlc *dlc;
@@ -412,16 +409,22 @@ static int rfcomm_create_dev(struct sock *sk, void __user *arg)
dlc = rfcomm_pi(sk)->dlc;
rfcomm_dlc_hold(dlc);
} else {
+ /* Validate the channel is unused */
+ dlc = rfcomm_dlc_exists(&req.src, &req.dst, req.channel);
+ if (IS_ERR(dlc))
+ return PTR_ERR(dlc);
+ else if (dlc) {
+ rfcomm_dlc_put(dlc);
+ return -EBUSY;
+ }
dlc = rfcomm_dlc_alloc(GFP_KERNEL);
if (!dlc)
return -ENOMEM;
}
id = rfcomm_dev_add(&req, dlc);
- if (id < 0) {
- rfcomm_dlc_put(dlc);
+ if (id < 0)
return id;
- }
if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
/* DLC is now used by device.
@@ -432,7 +435,7 @@ static int rfcomm_create_dev(struct sock *sk, void __user *arg)
return id;
}
-static int rfcomm_release_dev(void __user *arg)
+static int __rfcomm_release_dev(void __user *arg)
{
struct rfcomm_dev_req req;
struct rfcomm_dev *dev;
@@ -452,6 +455,12 @@ static int rfcomm_release_dev(void __user *arg)
return -EPERM;
}
+ /* only release once */
+ if (test_and_set_bit(RFCOMM_DEV_RELEASED, &dev->status)) {
+ tty_port_put(&dev->port);
+ return -EALREADY;
+ }
+
if (req.flags & (1 << RFCOMM_HANGUP_NOW))
rfcomm_dlc_close(dev->dlc, 0);
@@ -462,14 +471,35 @@ static int rfcomm_release_dev(void __user *arg)
tty_kref_put(tty);
}
- if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) &&
- !test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+ if (!test_bit(RFCOMM_TTY_OWNED, &dev->status))
tty_port_put(&dev->port);
tty_port_put(&dev->port);
return 0;
}
+static int rfcomm_create_dev(struct sock *sk, void __user *arg)
+{
+ int ret;
+
+ mutex_lock(&rfcomm_ioctl_mutex);
+ ret = __rfcomm_create_dev(sk, arg);
+ mutex_unlock(&rfcomm_ioctl_mutex);
+
+ return ret;
+}
+
+static int rfcomm_release_dev(void __user *arg)
+{
+ int ret;
+
+ mutex_lock(&rfcomm_ioctl_mutex);
+ ret = __rfcomm_release_dev(arg);
+ mutex_unlock(&rfcomm_ioctl_mutex);
+
+ return ret;
+}
+
static int rfcomm_get_dev_list(void __user *arg)
{
struct rfcomm_dev *dev;
@@ -497,7 +527,7 @@ static int rfcomm_get_dev_list(void __user *arg)
spin_lock(&rfcomm_dev_lock);
list_for_each_entry(dev, &rfcomm_dev_list, list) {
- if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+ if (!tty_port_get(&dev->port))
continue;
(di + n)->id = dev->id;
(di + n)->flags = dev->flags;
@@ -505,6 +535,7 @@ static int rfcomm_get_dev_list(void __user *arg)
(di + n)->channel = dev->channel;
bacpy(&(di + n)->src, &dev->src);
bacpy(&(di + n)->dst, &dev->dst);
+ tty_port_put(&dev->port);
if (++n >= dev_num)
break;
}
@@ -601,9 +632,11 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
dev->err = err;
- wake_up_interruptible(&dev->conn_wait);
+ if (dlc->state == BT_CONNECTED) {
+ rfcomm_reparent_device(dev);
- if (dlc->state == BT_CLOSED)
+ wake_up_interruptible(&dev->port.open_wait);
+ } else if (dlc->state == BT_CLOSED)
tty_port_tty_hangup(&dev->port, false);
}
@@ -703,8 +736,10 @@ static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
* when the last process closes the tty. The behaviour is expected by
* userspace.
*/
- if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
+ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
+ set_bit(RFCOMM_TTY_OWNED, &dev->status);
tty_port_put(&dev->port);
+ }
return 0;
}
@@ -750,7 +785,7 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
struct rfcomm_dlc *dlc = dev->dlc;
struct sk_buff *skb;
- int err = 0, sent = 0, size;
+ int sent = 0, size;
BT_DBG("tty %p count %d", tty, count);
@@ -758,7 +793,6 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in
size = min_t(uint, count, dlc->mtu);
skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC);
-
if (!skb)
break;
@@ -766,32 +800,24 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in
memcpy(skb_put(skb, size), buf + sent, size);
- err = rfcomm_dlc_send(dlc, skb);
- if (err < 0) {
- kfree_skb(skb);
- break;
- }
+ rfcomm_dlc_send_noerror(dlc, skb);
sent += size;
count -= size;
}
- return sent ? sent : err;
+ return sent;
}
static int rfcomm_tty_write_room(struct tty_struct *tty)
{
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
- int room;
+ int room = 0;
- BT_DBG("tty %p", tty);
-
- if (!dev || !dev->dlc)
- return 0;
+ if (dev && dev->dlc)
+ room = rfcomm_room(dev);
- room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc);
- if (room < 0)
- room = 0;
+ BT_DBG("tty %p room %d", tty, room);
return room;
}
@@ -1125,7 +1151,7 @@ int __init rfcomm_init_ttys(void)
rfcomm_tty_driver->subtype = SERIAL_TYPE_NORMAL;
rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
rfcomm_tty_driver->init_termios = tty_std_termios;
- rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 24fa3964b3c8..ab1e6fcca4c5 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -676,20 +676,20 @@ static void sco_conn_defer_accept(struct hci_conn *conn, u16 setting)
bacpy(&cp.bdaddr, &conn->dst);
cp.pkt_type = cpu_to_le16(conn->pkt_type);
- cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
- cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
+ cp.tx_bandwidth = cpu_to_le32(0x00001f40);
+ cp.rx_bandwidth = cpu_to_le32(0x00001f40);
cp.content_format = cpu_to_le16(setting);
switch (setting & SCO_AIRMODE_MASK) {
case SCO_AIRMODE_TRANSP:
if (conn->pkt_type & ESCO_2EV3)
- cp.max_latency = __constant_cpu_to_le16(0x0008);
+ cp.max_latency = cpu_to_le16(0x0008);
else
- cp.max_latency = __constant_cpu_to_le16(0x000D);
+ cp.max_latency = cpu_to_le16(0x000D);
cp.retrans_effort = 0x02;
break;
case SCO_AIRMODE_CVSD:
- cp.max_latency = __constant_cpu_to_le16(0xffff);
+ cp.max_latency = cpu_to_le16(0xffff);
cp.retrans_effort = 0xff;
break;
}
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 45007362683b..dfb4e1161c10 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -35,14 +35,14 @@
#define AUTH_REQ_MASK 0x07
-static inline void swap128(u8 src[16], u8 dst[16])
+static inline void swap128(const u8 src[16], u8 dst[16])
{
int i;
for (i = 0; i < 16; i++)
dst[15 - i] = src[i];
}
-static inline void swap56(u8 src[7], u8 dst[7])
+static inline void swap56(const u8 src[7], u8 dst[7])
{
int i;
for (i = 0; i < 7; i++)
@@ -53,6 +53,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
{
struct blkcipher_desc desc;
struct scatterlist sg;
+ uint8_t tmp[16], data[16];
int err;
if (tfm == NULL) {
@@ -63,21 +64,89 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
desc.tfm = tfm;
desc.flags = 0;
- err = crypto_blkcipher_setkey(tfm, k, 16);
+ /* The most significant octet of key corresponds to k[0] */
+ swap128(k, tmp);
+
+ err = crypto_blkcipher_setkey(tfm, tmp, 16);
if (err) {
BT_ERR("cipher setkey failed: %d", err);
return err;
}
- sg_init_one(&sg, r, 16);
+ /* Most significant octet of plaintextData corresponds to data[0] */
+ swap128(r, data);
+
+ sg_init_one(&sg, data, 16);
err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
if (err)
BT_ERR("Encrypt data error %d", err);
+ /* Most significant octet of encryptedData corresponds to data[0] */
+ swap128(data, r);
+
return err;
}
+static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3])
+{
+ u8 _res[16];
+ int err;
+
+ /* r' = padding || r */
+ memcpy(_res, r, 3);
+ memset(_res + 3, 0, 13);
+
+ err = smp_e(tfm, irk, _res);
+ if (err) {
+ BT_ERR("Encrypt error");
+ return err;
+ }
+
+ /* The output of the random address function ah is:
+ * ah(h, r) = e(k, r') mod 2^24
+ * The output of the security function e is then truncated to 24 bits
+ * by taking the least significant 24 bits of the output of e as the
+ * result of ah.
+ */
+ memcpy(res, _res, 3);
+
+ return 0;
+}
+
+bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16],
+ bdaddr_t *bdaddr)
+{
+ u8 hash[3];
+ int err;
+
+ BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk);
+
+ err = smp_ah(tfm, irk, &bdaddr->b[3], hash);
+ if (err)
+ return false;
+
+ return !memcmp(bdaddr->b, hash, 3);
+}
+
+int smp_generate_rpa(struct crypto_blkcipher *tfm, u8 irk[16], bdaddr_t *rpa)
+{
+ int err;
+
+ get_random_bytes(&rpa->b[3], 3);
+
+ rpa->b[5] &= 0x3f; /* Clear two most significant bits */
+ rpa->b[5] |= 0x40; /* Set second most significant bit */
+
+ err = smp_ah(tfm, irk, &rpa->b[3], rpa->b);
+ if (err < 0)
+ return err;
+
+ BT_DBG("RPA %pMR", rpa);
+
+ return 0;
+}
+
static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
u8 _rat, bdaddr_t *ra, u8 res[16])
@@ -88,16 +157,15 @@ static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
memset(p1, 0, 16);
/* p1 = pres || preq || _rat || _iat */
- swap56(pres, p1);
- swap56(preq, p1 + 7);
- p1[14] = _rat;
- p1[15] = _iat;
-
- memset(p2, 0, 16);
+ p1[0] = _iat;
+ p1[1] = _rat;
+ memcpy(p1 + 2, preq, 7);
+ memcpy(p1 + 9, pres, 7);
/* p2 = padding || ia || ra */
- baswap((bdaddr_t *) (p2 + 4), ia);
- baswap((bdaddr_t *) (p2 + 10), ra);
+ memcpy(p2, ra, 6);
+ memcpy(p2 + 6, ia, 6);
+ memset(p2 + 12, 0, 4);
/* res = r XOR p1 */
u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
@@ -126,8 +194,8 @@ static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], u8 r1[16],
int err;
/* Just least significant octets from r1 and r2 are considered */
- memcpy(_r, r1 + 8, 8);
- memcpy(_r + 8, r2 + 8, 8);
+ memcpy(_r, r2, 8);
+ memcpy(_r + 8, r1, 8);
err = smp_e(tfm, k, _r);
if (err)
@@ -154,7 +222,7 @@ static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->len = cpu_to_le16(sizeof(code) + dlen);
- lh->cid = __constant_cpu_to_le16(L2CAP_CID_SMP);
+ lh->cid = cpu_to_le16(L2CAP_CID_SMP);
memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
@@ -203,31 +271,45 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
struct smp_cmd_pairing *req,
struct smp_cmd_pairing *rsp, __u8 authreq)
{
- u8 dist_keys = 0;
+ struct smp_chan *smp = conn->smp_chan;
+ struct hci_conn *hcon = conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
+ u8 local_dist = 0, remote_dist = 0;
if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->dev_flags)) {
- dist_keys = SMP_DIST_ENC_KEY;
+ local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
+ remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
authreq |= SMP_AUTH_BONDING;
} else {
authreq &= ~SMP_AUTH_BONDING;
}
+ if (test_bit(HCI_RPA_RESOLVING, &hdev->dev_flags))
+ remote_dist |= SMP_DIST_ID_KEY;
+
+ if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
+ local_dist |= SMP_DIST_ID_KEY;
+
if (rsp == NULL) {
req->io_capability = conn->hcon->io_capability;
req->oob_flag = SMP_OOB_NOT_PRESENT;
req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
- req->init_key_dist = 0;
- req->resp_key_dist = dist_keys;
+ req->init_key_dist = local_dist;
+ req->resp_key_dist = remote_dist;
req->auth_req = (authreq & AUTH_REQ_MASK);
+
+ smp->remote_key_dist = remote_dist;
return;
}
rsp->io_capability = conn->hcon->io_capability;
rsp->oob_flag = SMP_OOB_NOT_PRESENT;
rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
- rsp->init_key_dist = 0;
- rsp->resp_key_dist = req->resp_key_dist & dist_keys;
+ rsp->init_key_dist = req->init_key_dist & remote_dist;
+ rsp->resp_key_dist = req->resp_key_dist & local_dist;
rsp->auth_req = (authreq & AUTH_REQ_MASK);
+
+ smp->remote_key_dist = rsp->init_key_dist;
}
static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
@@ -305,6 +387,11 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM)
method = JUST_WORKS;
+ /* Don't confirm locally initiated pairing attempts */
+ if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR,
+ &smp->smp_flags))
+ method = JUST_WORKS;
+
/* If Just Works, Continue with Zero TK */
if (method == JUST_WORKS) {
set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
@@ -325,16 +412,14 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
method = REQ_PASSKEY;
}
- /* Generate random passkey. Not valid until confirmed. */
+ /* Generate random passkey. */
if (method == CFM_PASSKEY) {
- u8 key[16];
-
- memset(key, 0, sizeof(key));
+ memset(smp->tk, 0, sizeof(smp->tk));
get_random_bytes(&passkey, sizeof(passkey));
passkey %= 1000000;
- put_unaligned_le32(passkey, key);
- swap128(key, smp->tk);
+ put_unaligned_le32(passkey, smp->tk);
BT_DBG("PassKey: %d", passkey);
+ set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
}
hci_dev_lock(hcon->hdev);
@@ -342,10 +427,14 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
if (method == REQ_PASSKEY)
ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst,
hcon->type, hcon->dst_type);
- else
+ else if (method == JUST_CFM)
ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
hcon->type, hcon->dst_type,
- cpu_to_le32(passkey), 0);
+ passkey, 1);
+ else
+ ret = mgmt_user_passkey_notify(hcon->hdev, &hcon->dst,
+ hcon->type, hcon->dst_type,
+ passkey, 0);
hci_dev_unlock(hcon->hdev);
@@ -356,29 +445,24 @@ static void confirm_work(struct work_struct *work)
{
struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
struct l2cap_conn *conn = smp->conn;
- struct crypto_blkcipher *tfm;
+ struct hci_dev *hdev = conn->hcon->hdev;
+ struct crypto_blkcipher *tfm = hdev->tfm_aes;
struct smp_cmd_pairing_confirm cp;
int ret;
- u8 res[16], reason;
+ u8 reason;
BT_DBG("conn %p", conn);
- tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(tfm)) {
- reason = SMP_UNSPECIFIED;
- goto error;
- }
+ /* Prevent mutual access to hdev->tfm_aes */
+ hci_dev_lock(hdev);
- smp->tfm = tfm;
+ ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
+ conn->hcon->init_addr_type, &conn->hcon->init_addr,
+ conn->hcon->resp_addr_type, &conn->hcon->resp_addr,
+ cp.confirm_val);
+
+ hci_dev_unlock(hdev);
- if (conn->hcon->out)
- ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
- conn->hcon->src_type, &conn->hcon->src,
- conn->hcon->dst_type, &conn->hcon->dst, res);
- else
- ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
- conn->hcon->dst_type, &conn->hcon->dst,
- conn->hcon->src_type, &conn->hcon->src, res);
if (ret) {
reason = SMP_UNSPECIFIED;
goto error;
@@ -386,7 +470,6 @@ static void confirm_work(struct work_struct *work)
clear_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
- swap128(res, cp.confirm_val);
smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
return;
@@ -400,8 +483,9 @@ static void random_work(struct work_struct *work)
struct smp_chan *smp = container_of(work, struct smp_chan, random);
struct l2cap_conn *conn = smp->conn;
struct hci_conn *hcon = conn->hcon;
- struct crypto_blkcipher *tfm = smp->tfm;
- u8 reason, confirm[16], res[16], key[16];
+ struct hci_dev *hdev = hcon->hdev;
+ struct crypto_blkcipher *tfm = hdev->tfm_aes;
+ u8 reason, confirm[16];
int ret;
if (IS_ERR_OR_NULL(tfm)) {
@@ -411,21 +495,20 @@ static void random_work(struct work_struct *work)
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
- if (hcon->out)
- ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
- hcon->src_type, &hcon->src,
- hcon->dst_type, &hcon->dst, res);
- else
- ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
- hcon->dst_type, &hcon->dst,
- hcon->src_type, &hcon->src, res);
+ /* Prevent mutual access to hdev->tfm_aes */
+ hci_dev_lock(hdev);
+
+ ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
+ hcon->init_addr_type, &hcon->init_addr,
+ hcon->resp_addr_type, &hcon->resp_addr, confirm);
+
+ hci_dev_unlock(hdev);
+
if (ret) {
reason = SMP_UNSPECIFIED;
goto error;
}
- swap128(res, confirm);
-
if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) {
BT_ERR("Pairing failed (confirmation values mismatch)");
reason = SMP_CONFIRM_FAILED;
@@ -433,14 +516,11 @@ static void random_work(struct work_struct *work)
}
if (hcon->out) {
- u8 stk[16], rand[8];
- __le16 ediv;
-
- memset(rand, 0, sizeof(rand));
- ediv = 0;
+ u8 stk[16];
+ __le64 rand = 0;
+ __le16 ediv = 0;
- smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key);
- swap128(key, stk);
+ smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, stk);
memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
@@ -453,23 +533,20 @@ static void random_work(struct work_struct *work)
hci_le_start_enc(hcon, ediv, rand, stk);
hcon->enc_key_size = smp->enc_key_size;
} else {
- u8 stk[16], r[16], rand[8];
- __le16 ediv;
-
- memset(rand, 0, sizeof(rand));
- ediv = 0;
+ u8 stk[16];
+ __le64 rand = 0;
+ __le16 ediv = 0;
- swap128(smp->prnd, r);
- smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
+ smp->prnd);
- smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key);
- swap128(key, stk);
+ smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, stk);
memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
- HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size,
+ HCI_SMP_STK_SLAVE, 0, stk, smp->enc_key_size,
ediv, rand);
}
@@ -502,11 +579,33 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
void smp_chan_destroy(struct l2cap_conn *conn)
{
struct smp_chan *smp = conn->smp_chan;
+ bool complete;
BUG_ON(!smp);
- if (smp->tfm)
- crypto_free_blkcipher(smp->tfm);
+ complete = test_bit(SMP_FLAG_COMPLETE, &smp->smp_flags);
+ mgmt_smp_complete(conn->hcon, complete);
+
+ kfree(smp->csrk);
+ kfree(smp->slave_csrk);
+
+ /* If pairing failed clean up any keys we might have */
+ if (!complete) {
+ if (smp->ltk) {
+ list_del(&smp->ltk->list);
+ kfree(smp->ltk);
+ }
+
+ if (smp->slave_ltk) {
+ list_del(&smp->slave_ltk->list);
+ kfree(smp->slave_ltk);
+ }
+
+ if (smp->remote_irk) {
+ list_del(&smp->remote_irk->list);
+ kfree(smp->remote_irk);
+ }
+ }
kfree(smp);
conn->smp_chan = NULL;
@@ -519,7 +618,6 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
struct l2cap_conn *conn = hcon->smp_conn;
struct smp_chan *smp;
u32 value;
- u8 key[16];
BT_DBG("");
@@ -531,10 +629,9 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
switch (mgmt_op) {
case MGMT_OP_USER_PASSKEY_REPLY:
value = le32_to_cpu(passkey);
- memset(key, 0, sizeof(key));
+ memset(smp->tk, 0, sizeof(smp->tk));
BT_DBG("PassKey: %d", value);
- put_unaligned_le32(value, key);
- swap128(key, smp->tk);
+ put_unaligned_le32(value, smp->tk);
/* Fall Through */
case MGMT_OP_USER_CONFIRM_REPLY:
set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
@@ -565,6 +662,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("conn %p", conn);
+ if (skb->len < sizeof(*req))
+ return SMP_UNSPECIFIED;
+
if (conn->hcon->link_mode & HCI_LM_MASTER)
return SMP_CMD_NOTSUPP;
@@ -604,6 +704,8 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (ret)
return SMP_UNSPECIFIED;
+ clear_bit(SMP_FLAG_INITIATOR, &smp->smp_flags);
+
return 0;
}
@@ -617,6 +719,9 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("conn %p", conn);
+ if (skb->len < sizeof(*rsp))
+ return SMP_UNSPECIFIED;
+
if (!(conn->hcon->link_mode & HCI_LM_MASTER))
return SMP_CMD_NOTSUPP;
@@ -633,6 +738,11 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
smp->prsp[0] = SMP_CMD_PAIRING_RSP;
memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
+ /* Update remote key distribution in case the remote cleared
+ * some bits that we had enabled in our request.
+ */
+ smp->remote_key_dist &= rsp->resp_key_dist;
+
if ((req->auth_req & SMP_AUTH_BONDING) &&
(rsp->auth_req & SMP_AUTH_BONDING))
auth = SMP_AUTH_BONDING;
@@ -646,10 +756,8 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
/* Can't compose response until we have been confirmed */
- if (!test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags))
- return 0;
-
- queue_work(hdev->workqueue, &smp->confirm);
+ if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags))
+ queue_work(hdev->workqueue, &smp->confirm);
return 0;
}
@@ -661,20 +769,19 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
+ if (skb->len < sizeof(smp->pcnf))
+ return SMP_UNSPECIFIED;
+
memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
skb_pull(skb, sizeof(smp->pcnf));
- if (conn->hcon->out) {
- u8 random[16];
-
- swap128(smp->prnd, random);
- smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
- random);
- } else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) {
+ if (conn->hcon->out)
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
+ smp->prnd);
+ else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags))
queue_work(hdev->workqueue, &smp->confirm);
- } else {
+ else
set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
- }
return 0;
}
@@ -686,7 +793,10 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("conn %p", conn);
- swap128(skb->data, smp->rrnd);
+ if (skb->len < sizeof(smp->rrnd))
+ return SMP_UNSPECIFIED;
+
+ memcpy(smp->rrnd, skb->data, sizeof(smp->rrnd));
skb_pull(skb, sizeof(smp->rrnd));
queue_work(hdev->workqueue, &smp->random);
@@ -699,7 +809,8 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
struct smp_ltk *key;
struct hci_conn *hcon = conn->hcon;
- key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type);
+ key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
+ hcon->out);
if (!key)
return 0;
@@ -724,6 +835,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("conn %p", conn);
+ if (skb->len < sizeof(*rp))
+ return SMP_UNSPECIFIED;
+
if (!(conn->hcon->link_mode & HCI_LM_MASTER))
return SMP_CMD_NOTSUPP;
@@ -747,6 +861,8 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+ clear_bit(SMP_FLAG_INITIATOR, &smp->smp_flags);
+
return 0;
}
@@ -764,11 +880,15 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
{
struct l2cap_conn *conn = hcon->l2cap_data;
- struct smp_chan *smp = conn->smp_chan;
+ struct smp_chan *smp;
__u8 authreq;
BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
+ /* This may be NULL if there's an unexpected disconnection */
+ if (!conn)
+ return 1;
+
if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
return 1;
@@ -788,6 +908,12 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
authreq = seclevel_to_authreq(sec_level);
+ /* hcon->auth_type is set by pair_device in mgmt.c. If the MITM
+ * flag is set we should also set it for the SMP request.
+ */
+ if ((hcon->auth_type & 0x01))
+ authreq |= SMP_AUTH_MITM;
+
if (hcon->link_mode & HCI_LM_MASTER) {
struct smp_cmd_pairing cp;
@@ -802,6 +928,8 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
}
+ set_bit(SMP_FLAG_INITIATOR, &smp->smp_flags);
+
done:
hcon->pending_sec_level = sec_level;
@@ -813,6 +941,15 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
struct smp_cmd_encrypt_info *rp = (void *) skb->data;
struct smp_chan *smp = conn->smp_chan;
+ BT_DBG("conn %p", conn);
+
+ if (skb->len < sizeof(*rp))
+ return SMP_UNSPECIFIED;
+
+ /* Ignore this PDU if it wasn't requested */
+ if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
+ return 0;
+
skb_pull(skb, sizeof(*rp));
memcpy(smp->tk, rp->ltk, sizeof(smp->tk));
@@ -826,16 +963,138 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
struct smp_chan *smp = conn->smp_chan;
struct hci_dev *hdev = conn->hcon->hdev;
struct hci_conn *hcon = conn->hcon;
+ struct smp_ltk *ltk;
u8 authenticated;
+ BT_DBG("conn %p", conn);
+
+ if (skb->len < sizeof(*rp))
+ return SMP_UNSPECIFIED;
+
+ /* Ignore this PDU if it wasn't requested */
+ if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
+ return 0;
+
+ /* Mark the information as received */
+ smp->remote_key_dist &= ~SMP_DIST_ENC_KEY;
+
skb_pull(skb, sizeof(*rp));
hci_dev_lock(hdev);
authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
- hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK, 1,
- authenticated, smp->tk, smp->enc_key_size,
- rp->ediv, rp->rand);
- smp_distribute_keys(conn, 1);
+ ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK,
+ authenticated, smp->tk, smp->enc_key_size,
+ rp->ediv, rp->rand);
+ smp->ltk = ltk;
+ if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
+ smp_distribute_keys(conn);
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int smp_cmd_ident_info(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_ident_info *info = (void *) skb->data;
+ struct smp_chan *smp = conn->smp_chan;
+
+ BT_DBG("");
+
+ if (skb->len < sizeof(*info))
+ return SMP_UNSPECIFIED;
+
+ /* Ignore this PDU if it wasn't requested */
+ if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
+ return 0;
+
+ skb_pull(skb, sizeof(*info));
+
+ memcpy(smp->irk, info->irk, 16);
+
+ return 0;
+}
+
+static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
+ struct sk_buff *skb)
+{
+ struct smp_cmd_ident_addr_info *info = (void *) skb->data;
+ struct smp_chan *smp = conn->smp_chan;
+ struct hci_conn *hcon = conn->hcon;
+ bdaddr_t rpa;
+
+ BT_DBG("");
+
+ if (skb->len < sizeof(*info))
+ return SMP_UNSPECIFIED;
+
+ /* Ignore this PDU if it wasn't requested */
+ if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
+ return 0;
+
+ /* Mark the information as received */
+ smp->remote_key_dist &= ~SMP_DIST_ID_KEY;
+
+ skb_pull(skb, sizeof(*info));
+
+ /* Strictly speaking the Core Specification (4.1) allows sending
+ * an empty address which would force us to rely on just the IRK
+ * as "identity information". However, since such
+ * implementations are not known of and in order to not over
+ * complicate our implementation, simply pretend that we never
+ * received an IRK for such a device.
+ */
+ if (!bacmp(&info->bdaddr, BDADDR_ANY)) {
+ BT_ERR("Ignoring IRK with no identity address");
+ smp_distribute_keys(conn);
+ return 0;
+ }
+
+ bacpy(&smp->id_addr, &info->bdaddr);
+ smp->id_addr_type = info->addr_type;
+
+ if (hci_bdaddr_is_rpa(&hcon->dst, hcon->dst_type))
+ bacpy(&rpa, &hcon->dst);
+ else
+ bacpy(&rpa, BDADDR_ANY);
+
+ smp->remote_irk = hci_add_irk(conn->hcon->hdev, &smp->id_addr,
+ smp->id_addr_type, smp->irk, &rpa);
+
+ smp_distribute_keys(conn);
+
+ return 0;
+}
+
+static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_sign_info *rp = (void *) skb->data;
+ struct smp_chan *smp = conn->smp_chan;
+ struct hci_dev *hdev = conn->hcon->hdev;
+ struct smp_csrk *csrk;
+
+ BT_DBG("conn %p", conn);
+
+ if (skb->len < sizeof(*rp))
+ return SMP_UNSPECIFIED;
+
+ /* Ignore this PDU if it wasn't requested */
+ if (!(smp->remote_key_dist & SMP_DIST_SIGN))
+ return 0;
+
+ /* Mark the information as received */
+ smp->remote_key_dist &= ~SMP_DIST_SIGN;
+
+ skb_pull(skb, sizeof(*rp));
+
+ hci_dev_lock(hdev);
+ csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
+ if (csrk) {
+ csrk->master = 0x01;
+ memcpy(csrk->val, rp->csrk, sizeof(csrk->val));
+ }
+ smp->csrk = csrk;
+ if (!(smp->remote_key_dist & SMP_DIST_SIGN))
+ smp_distribute_keys(conn);
hci_dev_unlock(hdev);
return 0;
@@ -915,10 +1174,15 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
break;
case SMP_CMD_IDENT_INFO:
+ reason = smp_cmd_ident_info(conn, skb);
+ break;
+
case SMP_CMD_IDENT_ADDR_INFO:
+ reason = smp_cmd_ident_addr_info(conn, skb);
+ break;
+
case SMP_CMD_SIGN_INFO:
- /* Just ignored */
- reason = 0;
+ reason = smp_cmd_sign_info(conn, skb);
break;
default:
@@ -937,26 +1201,78 @@ done:
return err;
}
-int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
+static void smp_notify_keys(struct l2cap_conn *conn)
+{
+ struct smp_chan *smp = conn->smp_chan;
+ struct hci_conn *hcon = conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
+ struct smp_cmd_pairing *req = (void *) &smp->preq[1];
+ struct smp_cmd_pairing *rsp = (void *) &smp->prsp[1];
+ bool persistent;
+
+ if (smp->remote_irk) {
+ mgmt_new_irk(hdev, smp->remote_irk);
+ /* Now that user space can be considered to know the
+ * identity address track the connection based on it
+ * from now on.
+ */
+ bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
+ hcon->dst_type = smp->remote_irk->addr_type;
+ l2cap_conn_update_id_addr(hcon);
+ }
+
+ /* The LTKs and CSRKs should be persistent only if both sides
+ * had the bonding bit set in their authentication requests.
+ */
+ persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING);
+
+ if (smp->csrk) {
+ smp->csrk->bdaddr_type = hcon->dst_type;
+ bacpy(&smp->csrk->bdaddr, &hcon->dst);
+ mgmt_new_csrk(hdev, smp->csrk, persistent);
+ }
+
+ if (smp->slave_csrk) {
+ smp->slave_csrk->bdaddr_type = hcon->dst_type;
+ bacpy(&smp->slave_csrk->bdaddr, &hcon->dst);
+ mgmt_new_csrk(hdev, smp->slave_csrk, persistent);
+ }
+
+ if (smp->ltk) {
+ smp->ltk->bdaddr_type = hcon->dst_type;
+ bacpy(&smp->ltk->bdaddr, &hcon->dst);
+ mgmt_new_ltk(hdev, smp->ltk, persistent);
+ }
+
+ if (smp->slave_ltk) {
+ smp->slave_ltk->bdaddr_type = hcon->dst_type;
+ bacpy(&smp->slave_ltk->bdaddr, &hcon->dst);
+ mgmt_new_ltk(hdev, smp->slave_ltk, persistent);
+ }
+}
+
+int smp_distribute_keys(struct l2cap_conn *conn)
{
struct smp_cmd_pairing *req, *rsp;
struct smp_chan *smp = conn->smp_chan;
+ struct hci_conn *hcon = conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
__u8 *keydist;
- BT_DBG("conn %p force %d", conn, force);
+ BT_DBG("conn %p", conn);
- if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
+ if (!test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
return 0;
rsp = (void *) &smp->prsp[1];
/* The responder sends its keys first */
- if (!force && conn->hcon->out && (rsp->resp_key_dist & 0x07))
+ if (hcon->out && (smp->remote_key_dist & 0x07))
return 0;
req = (void *) &smp->preq[1];
- if (conn->hcon->out) {
+ if (hcon->out) {
keydist = &rsp->init_key_dist;
*keydist &= req->init_key_dist;
} else {
@@ -964,28 +1280,30 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
*keydist &= req->resp_key_dist;
}
-
BT_DBG("keydist 0x%x", *keydist);
if (*keydist & SMP_DIST_ENC_KEY) {
struct smp_cmd_encrypt_info enc;
struct smp_cmd_master_ident ident;
- struct hci_conn *hcon = conn->hcon;
+ struct smp_ltk *ltk;
u8 authenticated;
__le16 ediv;
+ __le64 rand;
get_random_bytes(enc.ltk, sizeof(enc.ltk));
get_random_bytes(&ediv, sizeof(ediv));
- get_random_bytes(ident.rand, sizeof(ident.rand));
+ get_random_bytes(&rand, sizeof(rand));
smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
authenticated = hcon->sec_level == BT_SECURITY_HIGH;
- hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
- HCI_SMP_LTK_SLAVE, 1, authenticated,
- enc.ltk, smp->enc_key_size, ediv, ident.rand);
+ ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type,
+ HCI_SMP_LTK_SLAVE, authenticated, enc.ltk,
+ smp->enc_key_size, ediv, rand);
+ smp->slave_ltk = ltk;
ident.ediv = ediv;
+ ident.rand = rand;
smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident);
@@ -996,14 +1314,18 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
struct smp_cmd_ident_addr_info addrinfo;
struct smp_cmd_ident_info idinfo;
- /* Send a dummy key */
- get_random_bytes(idinfo.irk, sizeof(idinfo.irk));
+ memcpy(idinfo.irk, hdev->irk, sizeof(idinfo.irk));
smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo);
- /* Just public address */
- memset(&addrinfo, 0, sizeof(addrinfo));
- bacpy(&addrinfo.bdaddr, &conn->hcon->src);
+ /* The hci_conn contains the local identity address
+ * after the connection has been established.
+ *
+ * This is true even when the connection has been
+ * established using a resolvable random address.
+ */
+ bacpy(&addrinfo.bdaddr, &hcon->src);
+ addrinfo.addr_type = hcon->src_type;
smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo),
&addrinfo);
@@ -1013,20 +1335,33 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
if (*keydist & SMP_DIST_SIGN) {
struct smp_cmd_sign_info sign;
+ struct smp_csrk *csrk;
- /* Send a dummy key */
+ /* Generate a new random key */
get_random_bytes(sign.csrk, sizeof(sign.csrk));
+ csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
+ if (csrk) {
+ csrk->master = 0x00;
+ memcpy(csrk->val, sign.csrk, sizeof(csrk->val));
+ }
+ smp->slave_csrk = csrk;
+
smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign);
*keydist &= ~SMP_DIST_SIGN;
}
- if (conn->hcon->out || force) {
- clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags);
- cancel_delayed_work_sync(&conn->security_timer);
- smp_chan_destroy(conn);
- }
+ /* If there are still keys to be received wait for them */
+ if ((smp->remote_key_dist & 0x07))
+ return 0;
+
+ clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags);
+ cancel_delayed_work_sync(&conn->security_timer);
+ set_bit(SMP_FLAG_COMPLETE, &smp->smp_flags);
+ smp_notify_keys(conn);
+
+ smp_chan_destroy(conn);
return 0;
}
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
index a700bcb490d7..1277147a9150 100644
--- a/net/bluetooth/smp.h
+++ b/net/bluetooth/smp.h
@@ -78,7 +78,7 @@ struct smp_cmd_encrypt_info {
#define SMP_CMD_MASTER_IDENT 0x07
struct smp_cmd_master_ident {
__le16 ediv;
- __u8 rand[8];
+ __le64 rand;
} __packed;
#define SMP_CMD_IDENT_INFO 0x08
@@ -118,6 +118,8 @@ struct smp_cmd_security_req {
#define SMP_FLAG_TK_VALID 1
#define SMP_FLAG_CFM_PENDING 2
#define SMP_FLAG_MITM_AUTH 3
+#define SMP_FLAG_COMPLETE 4
+#define SMP_FLAG_INITIATOR 5
struct smp_chan {
struct l2cap_conn *conn;
@@ -128,20 +130,31 @@ struct smp_chan {
u8 pcnf[16]; /* SMP Pairing Confirm */
u8 tk[16]; /* SMP Temporary Key */
u8 enc_key_size;
+ u8 remote_key_dist;
+ bdaddr_t id_addr;
+ u8 id_addr_type;
+ u8 irk[16];
+ struct smp_csrk *csrk;
+ struct smp_csrk *slave_csrk;
+ struct smp_ltk *ltk;
+ struct smp_ltk *slave_ltk;
+ struct smp_irk *remote_irk;
unsigned long smp_flags;
- struct crypto_blkcipher *tfm;
struct work_struct confirm;
struct work_struct random;
-
};
/* SMP Commands */
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
-int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
+int smp_distribute_keys(struct l2cap_conn *conn);
int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
void smp_chan_destroy(struct l2cap_conn *conn);
+bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16],
+ bdaddr_t *bdaddr);
+int smp_generate_rpa(struct crypto_blkcipher *tfm, u8 irk[16], bdaddr_t *rpa);
+
#endif /* __SMP_H */
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 8fe8b71b487a..3e2da2cb72db 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -88,18 +88,11 @@ out:
static int br_dev_init(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
- int i;
- br->stats = alloc_percpu(struct pcpu_sw_netstats);
+ br->stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!br->stats)
return -ENOMEM;
- for_each_possible_cpu(i) {
- struct pcpu_sw_netstats *br_dev_stats;
- br_dev_stats = per_cpu_ptr(br->stats, i);
- u64_stats_init(&br_dev_stats->syncp);
- }
-
return 0;
}
@@ -143,9 +136,9 @@ static struct rtnl_link_stats64 *br_get_stats64(struct net_device *dev,
const struct pcpu_sw_netstats *bstats
= per_cpu_ptr(br->stats, cpu);
do {
- start = u64_stats_fetch_begin_bh(&bstats->syncp);
+ start = u64_stats_fetch_begin_irq(&bstats->syncp);
memcpy(&tmp, bstats, sizeof(tmp));
- } while (u64_stats_fetch_retry_bh(&bstats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&bstats->syncp, start));
sum.tx_bytes += tmp.tx_bytes;
sum.tx_packets += tmp.tx_packets;
sum.rx_bytes += tmp.rx_bytes;
@@ -225,16 +218,16 @@ static void br_netpoll_cleanup(struct net_device *dev)
br_netpoll_disable(p);
}
-static int __br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp)
+static int __br_netpoll_enable(struct net_bridge_port *p)
{
struct netpoll *np;
int err;
- np = kzalloc(sizeof(*p->np), gfp);
+ np = kzalloc(sizeof(*p->np), GFP_KERNEL);
if (!np)
return -ENOMEM;
- err = __netpoll_setup(np, p->dev, gfp);
+ err = __netpoll_setup(np, p->dev);
if (err) {
kfree(np);
return err;
@@ -244,16 +237,15 @@ static int __br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp)
return err;
}
-int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp)
+int br_netpoll_enable(struct net_bridge_port *p)
{
if (!p->br->dev->npinfo)
return 0;
- return __br_netpoll_enable(p, gfp);
+ return __br_netpoll_enable(p);
}
-static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni,
- gfp_t gfp)
+static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni)
{
struct net_bridge *br = netdev_priv(dev);
struct net_bridge_port *p;
@@ -262,7 +254,7 @@ static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni,
list_for_each_entry(p, &br->port_list, list) {
if (!p->dev)
continue;
- err = __br_netpoll_enable(p, gfp);
+ err = __br_netpoll_enable(p);
if (err)
goto fail;
}
@@ -374,7 +366,7 @@ void br_dev_setup(struct net_device *dev)
br->bridge_id.prio[0] = 0x80;
br->bridge_id.prio[1] = 0x00;
- memcpy(br->group_addr, eth_reserved_addr_base, ETH_ALEN);
+ ether_addr_copy(br->group_addr, eth_reserved_addr_base);
br->stp_enabled = BR_NO_STP;
br->group_fwd_mask = BR_GROUPFWD_DEFAULT;
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index d3409e6b5453..056b67b0e277 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -35,16 +35,11 @@ static inline int should_deliver(const struct net_bridge_port *p,
p->state == BR_STATE_FORWARDING;
}
-static inline unsigned int packet_length(const struct sk_buff *skb)
-{
- return skb->len - (skb->protocol == htons(ETH_P_8021Q) ? VLAN_HLEN : 0);
-}
-
int br_dev_queue_push_xmit(struct sk_buff *skb)
{
/* ip_fragment doesn't copy the MAC header */
if (nf_bridge_maybe_copy_header(skb) ||
- (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))) {
+ !is_skb_forwardable(skb->dev, skb)) {
kfree_skb(skb);
} else {
skb_push(skb, ETH_HLEN);
@@ -71,7 +66,7 @@ static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
skb->dev = to->dev;
if (unlikely(netpoll_tx_running(to->br->dev))) {
- if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
+ if (!is_skb_forwardable(skb->dev, skb))
kfree_skb(skb);
else {
skb_push(skb, ETH_HLEN);
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 54d207d3a31c..5262b8617eb9 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -366,7 +366,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if (err)
goto err2;
- err = br_netpoll_enable(p, GFP_KERNEL);
+ err = br_netpoll_enable(p);
if (err)
goto err3;
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 93067ecdb9a2..7b757b5dc773 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -363,7 +363,7 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
skb_reset_mac_header(skb);
eth = eth_hdr(skb);
- memcpy(eth->h_source, br->dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(eth->h_source, br->dev->dev_addr);
eth->h_dest[0] = 1;
eth->h_dest[1] = 0;
eth->h_dest[2] = 0x5e;
@@ -433,7 +433,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
skb_reset_mac_header(skb);
eth = eth_hdr(skb);
- memcpy(eth->h_source, br->dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(eth->h_source, br->dev->dev_addr);
eth->h_proto = htons(ETH_P_IPV6);
skb_put(skb, sizeof(*eth));
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index b008c59a92c4..80e1b0f60a30 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -167,7 +167,7 @@ void br_netfilter_rtable_init(struct net_bridge *br)
rt->dst.dev = br->dev;
rt->dst.path = &rt->dst;
dst_init_metrics(&rt->dst, br_dst_default_metrics, true);
- rt->dst.flags = DST_NOXFRM | DST_NOPEER | DST_FAKE_RTABLE;
+ rt->dst.flags = DST_NOXFRM | DST_FAKE_RTABLE;
rt->dst.ops = &fake_dst_ops;
}
@@ -506,7 +506,7 @@ bridged_dnat:
1);
return 0;
}
- memcpy(eth_hdr(skb)->h_dest, dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
skb->pkt_type = PACKET_HOST;
}
} else {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 3ba11bc99b65..06811d79f89f 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -46,12 +46,12 @@ typedef __u16 port_id;
struct bridge_id
{
unsigned char prio[2];
- unsigned char addr[6];
+ unsigned char addr[ETH_ALEN];
};
struct mac_addr
{
- unsigned char addr[6];
+ unsigned char addr[ETH_ALEN];
};
struct br_ip
@@ -349,7 +349,7 @@ static inline void br_netpoll_send_skb(const struct net_bridge_port *p,
netpoll_send_skb(np, skb);
}
-int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp);
+int br_netpoll_enable(struct net_bridge_port *p);
void br_netpoll_disable(struct net_bridge_port *p);
#else
static inline void br_netpoll_send_skb(const struct net_bridge_port *p,
@@ -357,7 +357,7 @@ static inline void br_netpoll_send_skb(const struct net_bridge_port *p,
{
}
-static inline int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp)
+static inline int br_netpoll_enable(struct net_bridge_port *p)
{
return 0;
}
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index f23c74b3a953..91510712c7a7 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -99,9 +99,9 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
v->num_vlans--;
if (bitmap_empty(v->vlan_bitmap, VLAN_N_VID)) {
if (v->port_idx)
- rcu_assign_pointer(v->parent.port->vlan_info, NULL);
+ RCU_INIT_POINTER(v->parent.port->vlan_info, NULL);
else
- rcu_assign_pointer(v->parent.br->vlan_info, NULL);
+ RCU_INIT_POINTER(v->parent.br->vlan_info, NULL);
kfree_rcu(v, rcu);
}
return 0;
@@ -113,9 +113,9 @@ static void __vlan_flush(struct net_port_vlans *v)
v->pvid = 0;
bitmap_zero(v->vlan_bitmap, VLAN_N_VID);
if (v->port_idx)
- rcu_assign_pointer(v->parent.port->vlan_info, NULL);
+ RCU_INIT_POINTER(v->parent.port->vlan_info, NULL);
else
- rcu_assign_pointer(v->parent.br->vlan_info, NULL);
+ RCU_INIT_POINTER(v->parent.br->vlan_info, NULL);
kfree_rcu(v, rcu);
}
diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c
index 3fb3c848affe..9024283d2bca 100644
--- a/net/bridge/netfilter/ebt_among.c
+++ b/net/bridge/netfilter/ebt_among.c
@@ -28,7 +28,7 @@ static bool ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
uint32_t cmp[2] = { 0, 0 };
int key = ((const unsigned char *)mac)[5];
- memcpy(((char *) cmp) + 2, mac, ETH_ALEN);
+ ether_addr_copy(((char *) cmp) + 2, mac);
start = wh->table[key];
limit = wh->table[key + 1];
if (ip) {
diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c
index c59f7bfae6e2..4e0b0c359325 100644
--- a/net/bridge/netfilter/ebt_dnat.c
+++ b/net/bridge/netfilter/ebt_dnat.c
@@ -22,7 +22,7 @@ ebt_dnat_tg(struct sk_buff *skb, const struct xt_action_param *par)
if (!skb_make_writable(skb, 0))
return EBT_DROP;
- memcpy(eth_hdr(skb)->h_dest, info->mac, ETH_ALEN);
+ ether_addr_copy(eth_hdr(skb)->h_dest, info->mac);
return info->target;
}
diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c
index 46624bb6d9be..203964997a51 100644
--- a/net/bridge/netfilter/ebt_redirect.c
+++ b/net/bridge/netfilter/ebt_redirect.c
@@ -25,10 +25,10 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par)
if (par->hooknum != NF_BR_BROUTING)
/* rcu_read_lock()ed by nf_hook_slow */
- memcpy(eth_hdr(skb)->h_dest,
- br_port_get_rcu(par->in)->br->dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(eth_hdr(skb)->h_dest,
+ br_port_get_rcu(par->in)->br->dev->dev_addr);
else
- memcpy(eth_hdr(skb)->h_dest, par->in->dev_addr, ETH_ALEN);
+ ether_addr_copy(eth_hdr(skb)->h_dest, par->in->dev_addr);
skb->pkt_type = PACKET_HOST;
return info->target;
}
diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c
index 0f6b118d6cb2..e56ccd060d26 100644
--- a/net/bridge/netfilter/ebt_snat.c
+++ b/net/bridge/netfilter/ebt_snat.c
@@ -24,7 +24,7 @@ ebt_snat_tg(struct sk_buff *skb, const struct xt_action_param *par)
if (!skb_make_writable(skb, 0))
return EBT_DROP;
- memcpy(eth_hdr(skb)->h_source, info->mac, ETH_ALEN);
+ ether_addr_copy(eth_hdr(skb)->h_source, info->mac);
if (!(info->target & NAT_ARP_BIT) &&
eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
const struct arphdr *ap;
diff --git a/net/ceph/crush/mapper.c b/net/ceph/crush/mapper.c
index b703790b4e44..a1ef53c04415 100644
--- a/net/ceph/crush/mapper.c
+++ b/net/ceph/crush/mapper.c
@@ -292,10 +292,12 @@ static int is_out(const struct crush_map *map,
* @outpos: our position in that vector
* @tries: number of attempts to make
* @recurse_tries: number of attempts to have recursive chooseleaf make
- * @local_tries: localized retries
- * @local_fallback_tries: localized fallback retries
+ * @local_retries: localized retries
+ * @local_fallback_retries: localized fallback retries
* @recurse_to_leaf: true if we want one device under each item of given type (chooseleaf instead of choose)
+ * @vary_r: pass r to recursive calls
* @out2: second output vector for leaf items (if @recurse_to_leaf)
+ * @parent_r: r value passed from the parent
*/
static int crush_choose_firstn(const struct crush_map *map,
struct crush_bucket *bucket,
@@ -304,10 +306,12 @@ static int crush_choose_firstn(const struct crush_map *map,
int *out, int outpos,
unsigned int tries,
unsigned int recurse_tries,
- unsigned int local_tries,
- unsigned int local_fallback_tries,
+ unsigned int local_retries,
+ unsigned int local_fallback_retries,
int recurse_to_leaf,
- int *out2)
+ unsigned int vary_r,
+ int *out2,
+ int parent_r)
{
int rep;
unsigned int ftotal, flocal;
@@ -319,8 +323,11 @@ static int crush_choose_firstn(const struct crush_map *map,
int itemtype;
int collide, reject;
- dprintk("CHOOSE%s bucket %d x %d outpos %d numrep %d\n", recurse_to_leaf ? "_LEAF" : "",
- bucket->id, x, outpos, numrep);
+ dprintk("CHOOSE%s bucket %d x %d outpos %d numrep %d tries %d recurse_tries %d local_retries %d local_fallback_retries %d parent_r %d\n",
+ recurse_to_leaf ? "_LEAF" : "",
+ bucket->id, x, outpos, numrep,
+ tries, recurse_tries, local_retries, local_fallback_retries,
+ parent_r);
for (rep = outpos; rep < numrep; rep++) {
/* keep trying until we get a non-out, non-colliding item */
@@ -335,7 +342,7 @@ static int crush_choose_firstn(const struct crush_map *map,
do {
collide = 0;
retry_bucket = 0;
- r = rep;
+ r = rep + parent_r;
/* r' = r + f_total */
r += ftotal;
@@ -344,9 +351,9 @@ static int crush_choose_firstn(const struct crush_map *map,
reject = 1;
goto reject;
}
- if (local_fallback_tries > 0 &&
+ if (local_fallback_retries > 0 &&
flocal >= (in->size>>1) &&
- flocal > local_fallback_tries)
+ flocal > local_fallback_retries)
item = bucket_perm_choose(in, x, r);
else
item = crush_bucket_choose(in, x, r);
@@ -387,16 +394,23 @@ static int crush_choose_firstn(const struct crush_map *map,
reject = 0;
if (!collide && recurse_to_leaf) {
if (item < 0) {
+ int sub_r;
+ if (vary_r)
+ sub_r = r >> (vary_r-1);
+ else
+ sub_r = 0;
if (crush_choose_firstn(map,
map->buckets[-1-item],
weight, weight_max,
x, outpos+1, 0,
out2, outpos,
recurse_tries, 0,
- local_tries,
- local_fallback_tries,
+ local_retries,
+ local_fallback_retries,
0,
- NULL) <= outpos)
+ vary_r,
+ NULL,
+ sub_r) <= outpos)
/* didn't get leaf */
reject = 1;
} else {
@@ -420,14 +434,14 @@ reject:
ftotal++;
flocal++;
- if (collide && flocal <= local_tries)
+ if (collide && flocal <= local_retries)
/* retry locally a few times */
retry_bucket = 1;
- else if (local_fallback_tries > 0 &&
- flocal <= in->size + local_fallback_tries)
+ else if (local_fallback_retries > 0 &&
+ flocal <= in->size + local_fallback_retries)
/* exhaustive bucket search */
retry_bucket = 1;
- else if (ftotal <= tries)
+ else if (ftotal < tries)
/* then retry descent */
retry_descent = 1;
else
@@ -640,10 +654,20 @@ int crush_do_rule(const struct crush_map *map,
__u32 step;
int i, j;
int numrep;
- int choose_tries = map->choose_total_tries;
- int choose_local_tries = map->choose_local_tries;
- int choose_local_fallback_tries = map->choose_local_fallback_tries;
+ /*
+ * the original choose_total_tries value was off by one (it
+ * counted "retries" and not "tries"). add one.
+ */
+ int choose_tries = map->choose_total_tries + 1;
int choose_leaf_tries = 0;
+ /*
+ * the local tries values were counted as "retries", though,
+ * and need no adjustment
+ */
+ int choose_local_retries = map->choose_local_tries;
+ int choose_local_fallback_retries = map->choose_local_fallback_tries;
+
+ int vary_r = map->chooseleaf_vary_r;
if ((__u32)ruleno >= map->max_rules) {
dprintk(" bad ruleno %d\n", ruleno);
@@ -676,13 +700,18 @@ int crush_do_rule(const struct crush_map *map,
break;
case CRUSH_RULE_SET_CHOOSE_LOCAL_TRIES:
- if (curstep->arg1 > 0)
- choose_local_tries = curstep->arg1;
+ if (curstep->arg1 >= 0)
+ choose_local_retries = curstep->arg1;
break;
case CRUSH_RULE_SET_CHOOSE_LOCAL_FALLBACK_TRIES:
- if (curstep->arg1 > 0)
- choose_local_fallback_tries = curstep->arg1;
+ if (curstep->arg1 >= 0)
+ choose_local_fallback_retries = curstep->arg1;
+ break;
+
+ case CRUSH_RULE_SET_CHOOSELEAF_VARY_R:
+ if (curstep->arg1 >= 0)
+ vary_r = curstep->arg1;
break;
case CRUSH_RULE_CHOOSELEAF_FIRSTN:
@@ -734,10 +763,12 @@ int crush_do_rule(const struct crush_map *map,
o+osize, j,
choose_tries,
recurse_tries,
- choose_local_tries,
- choose_local_fallback_tries,
+ choose_local_retries,
+ choose_local_fallback_retries,
recurse_to_leaf,
- c+osize);
+ vary_r,
+ c+osize,
+ 0);
} else {
crush_choose_indep(
map,
diff --git a/net/ceph/debugfs.c b/net/ceph/debugfs.c
index 258a382e75ed..10421a4b76f8 100644
--- a/net/ceph/debugfs.c
+++ b/net/ceph/debugfs.c
@@ -53,34 +53,55 @@ static int osdmap_show(struct seq_file *s, void *p)
{
int i;
struct ceph_client *client = s->private;
+ struct ceph_osdmap *map = client->osdc.osdmap;
struct rb_node *n;
- if (client->osdc.osdmap == NULL)
+ if (map == NULL)
return 0;
- seq_printf(s, "epoch %d\n", client->osdc.osdmap->epoch);
+
+ seq_printf(s, "epoch %d\n", map->epoch);
seq_printf(s, "flags%s%s\n",
- (client->osdc.osdmap->flags & CEPH_OSDMAP_NEARFULL) ?
- " NEARFULL" : "",
- (client->osdc.osdmap->flags & CEPH_OSDMAP_FULL) ?
- " FULL" : "");
- for (n = rb_first(&client->osdc.osdmap->pg_pools); n; n = rb_next(n)) {
+ (map->flags & CEPH_OSDMAP_NEARFULL) ? " NEARFULL" : "",
+ (map->flags & CEPH_OSDMAP_FULL) ? " FULL" : "");
+
+ for (n = rb_first(&map->pg_pools); n; n = rb_next(n)) {
struct ceph_pg_pool_info *pool =
rb_entry(n, struct ceph_pg_pool_info, node);
- seq_printf(s, "pg_pool %llu pg_num %d / %d\n",
- (unsigned long long)pool->id, pool->pg_num,
- pool->pg_num_mask);
+
+ seq_printf(s, "pool %lld pg_num %u (%d) read_tier %lld write_tier %lld\n",
+ pool->id, pool->pg_num, pool->pg_num_mask,
+ pool->read_tier, pool->write_tier);
}
- for (i = 0; i < client->osdc.osdmap->max_osd; i++) {
- struct ceph_entity_addr *addr =
- &client->osdc.osdmap->osd_addr[i];
- int state = client->osdc.osdmap->osd_state[i];
+ for (i = 0; i < map->max_osd; i++) {
+ struct ceph_entity_addr *addr = &map->osd_addr[i];
+ int state = map->osd_state[i];
char sb[64];
- seq_printf(s, "\tosd%d\t%s\t%3d%%\t(%s)\n",
+ seq_printf(s, "osd%d\t%s\t%3d%%\t(%s)\t%3d%%\n",
i, ceph_pr_addr(&addr->in_addr),
- ((client->osdc.osdmap->osd_weight[i]*100) >> 16),
- ceph_osdmap_state_str(sb, sizeof(sb), state));
+ ((map->osd_weight[i]*100) >> 16),
+ ceph_osdmap_state_str(sb, sizeof(sb), state),
+ ((ceph_get_primary_affinity(map, i)*100) >> 16));
+ }
+ for (n = rb_first(&map->pg_temp); n; n = rb_next(n)) {
+ struct ceph_pg_mapping *pg =
+ rb_entry(n, struct ceph_pg_mapping, node);
+
+ seq_printf(s, "pg_temp %llu.%x [", pg->pgid.pool,
+ pg->pgid.seed);
+ for (i = 0; i < pg->pg_temp.len; i++)
+ seq_printf(s, "%s%d", (i == 0 ? "" : ","),
+ pg->pg_temp.osds[i]);
+ seq_printf(s, "]\n");
}
+ for (n = rb_first(&map->primary_temp); n; n = rb_next(n)) {
+ struct ceph_pg_mapping *pg =
+ rb_entry(n, struct ceph_pg_mapping, node);
+
+ seq_printf(s, "primary_temp %llu.%x %d\n", pg->pgid.pool,
+ pg->pgid.seed, pg->primary_temp.osd);
+ }
+
return 0;
}
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 30efc5c18622..4f55f9ce63fa 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -919,6 +919,9 @@ static bool ceph_msg_data_pages_advance(struct ceph_msg_data_cursor *cursor,
if (!bytes || cursor->page_offset)
return false; /* more bytes to process in the current page */
+ if (!cursor->resid)
+ return false; /* no more data */
+
/* Move on to the next page; offset is already at 0 */
BUG_ON(cursor->page_index >= cursor->page_count);
@@ -1004,6 +1007,9 @@ static bool ceph_msg_data_pagelist_advance(struct ceph_msg_data_cursor *cursor,
if (!bytes || cursor->offset & ~PAGE_MASK)
return false; /* more bytes to process in the current page */
+ if (!cursor->resid)
+ return false; /* no more data */
+
/* Move on to the next page */
BUG_ON(list_is_last(&cursor->page->lru, &pagelist->head));
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 0676f2b199d6..b0dfce77656a 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -436,6 +436,7 @@ static bool osd_req_opcode_valid(u16 opcode)
case CEPH_OSD_OP_OMAPCLEAR:
case CEPH_OSD_OP_OMAPRMKEYS:
case CEPH_OSD_OP_OMAP_CMP:
+ case CEPH_OSD_OP_SETALLOCHINT:
case CEPH_OSD_OP_CLONERANGE:
case CEPH_OSD_OP_ASSERT_SRC_VERSION:
case CEPH_OSD_OP_SRC_CMPXATTR:
@@ -591,6 +592,26 @@ void osd_req_op_watch_init(struct ceph_osd_request *osd_req,
}
EXPORT_SYMBOL(osd_req_op_watch_init);
+void osd_req_op_alloc_hint_init(struct ceph_osd_request *osd_req,
+ unsigned int which,
+ u64 expected_object_size,
+ u64 expected_write_size)
+{
+ struct ceph_osd_req_op *op = _osd_req_op_init(osd_req, which,
+ CEPH_OSD_OP_SETALLOCHINT);
+
+ op->alloc_hint.expected_object_size = expected_object_size;
+ op->alloc_hint.expected_write_size = expected_write_size;
+
+ /*
+ * CEPH_OSD_OP_SETALLOCHINT op is advisory and therefore deemed
+ * not worth a feature bit. Set FAILOK per-op flag to make
+ * sure older osds don't trip over an unsupported opcode.
+ */
+ op->flags |= CEPH_OSD_OP_FLAG_FAILOK;
+}
+EXPORT_SYMBOL(osd_req_op_alloc_hint_init);
+
static void ceph_osdc_msg_data_add(struct ceph_msg *msg,
struct ceph_osd_data *osd_data)
{
@@ -681,6 +702,12 @@ static u64 osd_req_encode_op(struct ceph_osd_request *req,
dst->watch.ver = cpu_to_le64(src->watch.ver);
dst->watch.flag = src->watch.flag;
break;
+ case CEPH_OSD_OP_SETALLOCHINT:
+ dst->alloc_hint.expected_object_size =
+ cpu_to_le64(src->alloc_hint.expected_object_size);
+ dst->alloc_hint.expected_write_size =
+ cpu_to_le64(src->alloc_hint.expected_write_size);
+ break;
default:
pr_err("unsupported osd opcode %s\n",
ceph_osd_op_name(src->op));
@@ -688,7 +715,9 @@ static u64 osd_req_encode_op(struct ceph_osd_request *req,
return 0;
}
+
dst->op = cpu_to_le16(src->op);
+ dst->flags = cpu_to_le32(src->flags);
dst->payload_len = cpu_to_le32(src->payload_len);
return request_data_len;
@@ -1304,7 +1333,7 @@ static int __map_request(struct ceph_osd_client *osdc,
{
struct ceph_pg pgid;
int acting[CEPH_PG_MAX_SIZE];
- int o = -1, num = 0;
+ int num, o;
int err;
bool was_paused;
@@ -1317,11 +1346,9 @@ static int __map_request(struct ceph_osd_client *osdc,
}
req->r_pgid = pgid;
- err = ceph_calc_pg_acting(osdc->osdmap, pgid, acting);
- if (err > 0) {
- o = acting[0];
- num = err;
- }
+ num = ceph_calc_pg_acting(osdc->osdmap, pgid, acting, &o);
+ if (num < 0)
+ num = 0;
was_paused = req->r_paused;
req->r_paused = __req_should_be_paused(osdc, req);
@@ -2033,7 +2060,7 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg)
int skipped_map = 0;
dout("taking full map %u len %d\n", epoch, maplen);
- newmap = osdmap_decode(&p, p+maplen);
+ newmap = ceph_osdmap_decode(&p, p+maplen);
if (IS_ERR(newmap)) {
err = PTR_ERR(newmap);
goto bad;
@@ -2082,7 +2109,6 @@ bad:
pr_err("osdc handle_map corrupt msg\n");
ceph_msg_dump(msg);
up_write(&osdc->map_sem);
- return;
}
/*
@@ -2281,7 +2307,6 @@ done_err:
bad:
pr_err("osdc handle_watch_notify corrupt msg\n");
- return;
}
/*
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index aade4a5c1c07..e632b5a52f5b 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -343,7 +343,7 @@ bad:
/*
* rbtree of pg_mapping for handling pg_temp (explicit mapping of pgid
- * to a set of osds)
+ * to a set of osds) and primary_temp (explicit primary setting)
*/
static int pgid_cmp(struct ceph_pg l, struct ceph_pg r)
{
@@ -506,7 +506,7 @@ static void __remove_pg_pool(struct rb_root *root, struct ceph_pg_pool_info *pi)
kfree(pi);
}
-static int __decode_pool(void **p, void *end, struct ceph_pg_pool_info *pi)
+static int decode_pool(void **p, void *end, struct ceph_pg_pool_info *pi)
{
u8 ev, cv;
unsigned len, num;
@@ -587,7 +587,7 @@ bad:
return -EINVAL;
}
-static int __decode_pool_names(void **p, void *end, struct ceph_osdmap *map)
+static int decode_pool_names(void **p, void *end, struct ceph_osdmap *map)
{
struct ceph_pg_pool_info *pi;
u32 num, len;
@@ -633,6 +633,13 @@ void ceph_osdmap_destroy(struct ceph_osdmap *map)
rb_erase(&pg->node, &map->pg_temp);
kfree(pg);
}
+ while (!RB_EMPTY_ROOT(&map->primary_temp)) {
+ struct ceph_pg_mapping *pg =
+ rb_entry(rb_first(&map->primary_temp),
+ struct ceph_pg_mapping, node);
+ rb_erase(&pg->node, &map->primary_temp);
+ kfree(pg);
+ }
while (!RB_EMPTY_ROOT(&map->pg_pools)) {
struct ceph_pg_pool_info *pi =
rb_entry(rb_first(&map->pg_pools),
@@ -642,186 +649,516 @@ void ceph_osdmap_destroy(struct ceph_osdmap *map)
kfree(map->osd_state);
kfree(map->osd_weight);
kfree(map->osd_addr);
+ kfree(map->osd_primary_affinity);
kfree(map);
}
/*
- * adjust max osd value. reallocate arrays.
+ * Adjust max_osd value, (re)allocate arrays.
+ *
+ * The new elements are properly initialized.
*/
static int osdmap_set_max_osd(struct ceph_osdmap *map, int max)
{
u8 *state;
- struct ceph_entity_addr *addr;
u32 *weight;
+ struct ceph_entity_addr *addr;
+ int i;
- state = kcalloc(max, sizeof(*state), GFP_NOFS);
- addr = kcalloc(max, sizeof(*addr), GFP_NOFS);
- weight = kcalloc(max, sizeof(*weight), GFP_NOFS);
- if (state == NULL || addr == NULL || weight == NULL) {
+ state = krealloc(map->osd_state, max*sizeof(*state), GFP_NOFS);
+ weight = krealloc(map->osd_weight, max*sizeof(*weight), GFP_NOFS);
+ addr = krealloc(map->osd_addr, max*sizeof(*addr), GFP_NOFS);
+ if (!state || !weight || !addr) {
kfree(state);
- kfree(addr);
kfree(weight);
+ kfree(addr);
+
return -ENOMEM;
}
- /* copy old? */
- if (map->osd_state) {
- memcpy(state, map->osd_state, map->max_osd*sizeof(*state));
- memcpy(addr, map->osd_addr, map->max_osd*sizeof(*addr));
- memcpy(weight, map->osd_weight, map->max_osd*sizeof(*weight));
- kfree(map->osd_state);
- kfree(map->osd_addr);
- kfree(map->osd_weight);
+ for (i = map->max_osd; i < max; i++) {
+ state[i] = 0;
+ weight[i] = CEPH_OSD_OUT;
+ memset(addr + i, 0, sizeof(*addr));
}
map->osd_state = state;
map->osd_weight = weight;
map->osd_addr = addr;
+
+ if (map->osd_primary_affinity) {
+ u32 *affinity;
+
+ affinity = krealloc(map->osd_primary_affinity,
+ max*sizeof(*affinity), GFP_NOFS);
+ if (!affinity)
+ return -ENOMEM;
+
+ for (i = map->max_osd; i < max; i++)
+ affinity[i] = CEPH_OSD_DEFAULT_PRIMARY_AFFINITY;
+
+ map->osd_primary_affinity = affinity;
+ }
+
map->max_osd = max;
+
return 0;
}
+#define OSDMAP_WRAPPER_COMPAT_VER 7
+#define OSDMAP_CLIENT_DATA_COMPAT_VER 1
+
/*
- * decode a full map.
+ * Return 0 or error. On success, *v is set to 0 for old (v6) osdmaps,
+ * to struct_v of the client_data section for new (v7 and above)
+ * osdmaps.
*/
-struct ceph_osdmap *osdmap_decode(void **p, void *end)
+static int get_osdmap_client_data_v(void **p, void *end,
+ const char *prefix, u8 *v)
{
- struct ceph_osdmap *map;
- u16 version;
- u32 len, max, i;
- int err = -EINVAL;
- void *start = *p;
- struct ceph_pg_pool_info *pi;
+ u8 struct_v;
+
+ ceph_decode_8_safe(p, end, struct_v, e_inval);
+ if (struct_v >= 7) {
+ u8 struct_compat;
+
+ ceph_decode_8_safe(p, end, struct_compat, e_inval);
+ if (struct_compat > OSDMAP_WRAPPER_COMPAT_VER) {
+ pr_warning("got v %d cv %d > %d of %s ceph_osdmap\n",
+ struct_v, struct_compat,
+ OSDMAP_WRAPPER_COMPAT_VER, prefix);
+ return -EINVAL;
+ }
+ *p += 4; /* ignore wrapper struct_len */
+
+ ceph_decode_8_safe(p, end, struct_v, e_inval);
+ ceph_decode_8_safe(p, end, struct_compat, e_inval);
+ if (struct_compat > OSDMAP_CLIENT_DATA_COMPAT_VER) {
+ pr_warning("got v %d cv %d > %d of %s ceph_osdmap client data\n",
+ struct_v, struct_compat,
+ OSDMAP_CLIENT_DATA_COMPAT_VER, prefix);
+ return -EINVAL;
+ }
+ *p += 4; /* ignore client data struct_len */
+ } else {
+ u16 version;
+
+ *p -= 1;
+ ceph_decode_16_safe(p, end, version, e_inval);
+ if (version < 6) {
+ pr_warning("got v %d < 6 of %s ceph_osdmap\n", version,
+ prefix);
+ return -EINVAL;
+ }
- dout("osdmap_decode %p to %p len %d\n", *p, end, (int)(end - *p));
+ /* old osdmap enconding */
+ struct_v = 0;
+ }
- map = kzalloc(sizeof(*map), GFP_NOFS);
- if (map == NULL)
- return ERR_PTR(-ENOMEM);
- map->pg_temp = RB_ROOT;
+ *v = struct_v;
+ return 0;
- ceph_decode_16_safe(p, end, version, bad);
- if (version > 6) {
- pr_warning("got unknown v %d > 6 of osdmap\n", version);
- goto bad;
+e_inval:
+ return -EINVAL;
+}
+
+static int __decode_pools(void **p, void *end, struct ceph_osdmap *map,
+ bool incremental)
+{
+ u32 n;
+
+ ceph_decode_32_safe(p, end, n, e_inval);
+ while (n--) {
+ struct ceph_pg_pool_info *pi;
+ u64 pool;
+ int ret;
+
+ ceph_decode_64_safe(p, end, pool, e_inval);
+
+ pi = __lookup_pg_pool(&map->pg_pools, pool);
+ if (!incremental || !pi) {
+ pi = kzalloc(sizeof(*pi), GFP_NOFS);
+ if (!pi)
+ return -ENOMEM;
+
+ pi->id = pool;
+
+ ret = __insert_pg_pool(&map->pg_pools, pi);
+ if (ret) {
+ kfree(pi);
+ return ret;
+ }
+ }
+
+ ret = decode_pool(p, end, pi);
+ if (ret)
+ return ret;
}
- if (version < 6) {
- pr_warning("got old v %d < 6 of osdmap\n", version);
- goto bad;
+
+ return 0;
+
+e_inval:
+ return -EINVAL;
+}
+
+static int decode_pools(void **p, void *end, struct ceph_osdmap *map)
+{
+ return __decode_pools(p, end, map, false);
+}
+
+static int decode_new_pools(void **p, void *end, struct ceph_osdmap *map)
+{
+ return __decode_pools(p, end, map, true);
+}
+
+static int __decode_pg_temp(void **p, void *end, struct ceph_osdmap *map,
+ bool incremental)
+{
+ u32 n;
+
+ ceph_decode_32_safe(p, end, n, e_inval);
+ while (n--) {
+ struct ceph_pg pgid;
+ u32 len, i;
+ int ret;
+
+ ret = ceph_decode_pgid(p, end, &pgid);
+ if (ret)
+ return ret;
+
+ ceph_decode_32_safe(p, end, len, e_inval);
+
+ ret = __remove_pg_mapping(&map->pg_temp, pgid);
+ BUG_ON(!incremental && ret != -ENOENT);
+
+ if (!incremental || len > 0) {
+ struct ceph_pg_mapping *pg;
+
+ ceph_decode_need(p, end, len*sizeof(u32), e_inval);
+
+ if (len > (UINT_MAX - sizeof(*pg)) / sizeof(u32))
+ return -EINVAL;
+
+ pg = kzalloc(sizeof(*pg) + len*sizeof(u32), GFP_NOFS);
+ if (!pg)
+ return -ENOMEM;
+
+ pg->pgid = pgid;
+ pg->pg_temp.len = len;
+ for (i = 0; i < len; i++)
+ pg->pg_temp.osds[i] = ceph_decode_32(p);
+
+ ret = __insert_pg_mapping(pg, &map->pg_temp);
+ if (ret) {
+ kfree(pg);
+ return ret;
+ }
+ }
}
- ceph_decode_need(p, end, 2*sizeof(u64)+6*sizeof(u32), bad);
+ return 0;
+
+e_inval:
+ return -EINVAL;
+}
+
+static int decode_pg_temp(void **p, void *end, struct ceph_osdmap *map)
+{
+ return __decode_pg_temp(p, end, map, false);
+}
+
+static int decode_new_pg_temp(void **p, void *end, struct ceph_osdmap *map)
+{
+ return __decode_pg_temp(p, end, map, true);
+}
+
+static int __decode_primary_temp(void **p, void *end, struct ceph_osdmap *map,
+ bool incremental)
+{
+ u32 n;
+
+ ceph_decode_32_safe(p, end, n, e_inval);
+ while (n--) {
+ struct ceph_pg pgid;
+ u32 osd;
+ int ret;
+
+ ret = ceph_decode_pgid(p, end, &pgid);
+ if (ret)
+ return ret;
+
+ ceph_decode_32_safe(p, end, osd, e_inval);
+
+ ret = __remove_pg_mapping(&map->primary_temp, pgid);
+ BUG_ON(!incremental && ret != -ENOENT);
+
+ if (!incremental || osd != (u32)-1) {
+ struct ceph_pg_mapping *pg;
+
+ pg = kzalloc(sizeof(*pg), GFP_NOFS);
+ if (!pg)
+ return -ENOMEM;
+
+ pg->pgid = pgid;
+ pg->primary_temp.osd = osd;
+
+ ret = __insert_pg_mapping(pg, &map->primary_temp);
+ if (ret) {
+ kfree(pg);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+
+e_inval:
+ return -EINVAL;
+}
+
+static int decode_primary_temp(void **p, void *end, struct ceph_osdmap *map)
+{
+ return __decode_primary_temp(p, end, map, false);
+}
+
+static int decode_new_primary_temp(void **p, void *end,
+ struct ceph_osdmap *map)
+{
+ return __decode_primary_temp(p, end, map, true);
+}
+
+u32 ceph_get_primary_affinity(struct ceph_osdmap *map, int osd)
+{
+ BUG_ON(osd >= map->max_osd);
+
+ if (!map->osd_primary_affinity)
+ return CEPH_OSD_DEFAULT_PRIMARY_AFFINITY;
+
+ return map->osd_primary_affinity[osd];
+}
+
+static int set_primary_affinity(struct ceph_osdmap *map, int osd, u32 aff)
+{
+ BUG_ON(osd >= map->max_osd);
+
+ if (!map->osd_primary_affinity) {
+ int i;
+
+ map->osd_primary_affinity = kmalloc(map->max_osd*sizeof(u32),
+ GFP_NOFS);
+ if (!map->osd_primary_affinity)
+ return -ENOMEM;
+
+ for (i = 0; i < map->max_osd; i++)
+ map->osd_primary_affinity[i] =
+ CEPH_OSD_DEFAULT_PRIMARY_AFFINITY;
+ }
+
+ map->osd_primary_affinity[osd] = aff;
+
+ return 0;
+}
+
+static int decode_primary_affinity(void **p, void *end,
+ struct ceph_osdmap *map)
+{
+ u32 len, i;
+
+ ceph_decode_32_safe(p, end, len, e_inval);
+ if (len == 0) {
+ kfree(map->osd_primary_affinity);
+ map->osd_primary_affinity = NULL;
+ return 0;
+ }
+ if (len != map->max_osd)
+ goto e_inval;
+
+ ceph_decode_need(p, end, map->max_osd*sizeof(u32), e_inval);
+
+ for (i = 0; i < map->max_osd; i++) {
+ int ret;
+
+ ret = set_primary_affinity(map, i, ceph_decode_32(p));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+
+e_inval:
+ return -EINVAL;
+}
+
+static int decode_new_primary_affinity(void **p, void *end,
+ struct ceph_osdmap *map)
+{
+ u32 n;
+
+ ceph_decode_32_safe(p, end, n, e_inval);
+ while (n--) {
+ u32 osd, aff;
+ int ret;
+
+ ceph_decode_32_safe(p, end, osd, e_inval);
+ ceph_decode_32_safe(p, end, aff, e_inval);
+
+ ret = set_primary_affinity(map, osd, aff);
+ if (ret)
+ return ret;
+
+ pr_info("osd%d primary-affinity 0x%x\n", osd, aff);
+ }
+
+ return 0;
+
+e_inval:
+ return -EINVAL;
+}
+
+/*
+ * decode a full map.
+ */
+static int osdmap_decode(void **p, void *end, struct ceph_osdmap *map)
+{
+ u8 struct_v;
+ u32 epoch = 0;
+ void *start = *p;
+ u32 max;
+ u32 len, i;
+ int err;
+
+ dout("%s %p to %p len %d\n", __func__, *p, end, (int)(end - *p));
+
+ err = get_osdmap_client_data_v(p, end, "full", &struct_v);
+ if (err)
+ goto bad;
+
+ /* fsid, epoch, created, modified */
+ ceph_decode_need(p, end, sizeof(map->fsid) + sizeof(u32) +
+ sizeof(map->created) + sizeof(map->modified), e_inval);
ceph_decode_copy(p, &map->fsid, sizeof(map->fsid));
- map->epoch = ceph_decode_32(p);
+ epoch = map->epoch = ceph_decode_32(p);
ceph_decode_copy(p, &map->created, sizeof(map->created));
ceph_decode_copy(p, &map->modified, sizeof(map->modified));
- ceph_decode_32_safe(p, end, max, bad);
- while (max--) {
- ceph_decode_need(p, end, 8 + 2, bad);
- err = -ENOMEM;
- pi = kzalloc(sizeof(*pi), GFP_NOFS);
- if (!pi)
- goto bad;
- pi->id = ceph_decode_64(p);
- err = __decode_pool(p, end, pi);
- if (err < 0) {
- kfree(pi);
- goto bad;
- }
- __insert_pg_pool(&map->pg_pools, pi);
- }
+ /* pools */
+ err = decode_pools(p, end, map);
+ if (err)
+ goto bad;
- err = __decode_pool_names(p, end, map);
- if (err < 0) {
- dout("fail to decode pool names");
+ /* pool_name */
+ err = decode_pool_names(p, end, map);
+ if (err)
goto bad;
- }
- ceph_decode_32_safe(p, end, map->pool_max, bad);
+ ceph_decode_32_safe(p, end, map->pool_max, e_inval);
- ceph_decode_32_safe(p, end, map->flags, bad);
+ ceph_decode_32_safe(p, end, map->flags, e_inval);
- max = ceph_decode_32(p);
+ /* max_osd */
+ ceph_decode_32_safe(p, end, max, e_inval);
/* (re)alloc osd arrays */
err = osdmap_set_max_osd(map, max);
- if (err < 0)
+ if (err)
goto bad;
- dout("osdmap_decode max_osd = %d\n", map->max_osd);
- /* osds */
- err = -EINVAL;
+ /* osd_state, osd_weight, osd_addrs->client_addr */
ceph_decode_need(p, end, 3*sizeof(u32) +
map->max_osd*(1 + sizeof(*map->osd_weight) +
- sizeof(*map->osd_addr)), bad);
- *p += 4; /* skip length field (should match max) */
+ sizeof(*map->osd_addr)), e_inval);
+
+ if (ceph_decode_32(p) != map->max_osd)
+ goto e_inval;
+
ceph_decode_copy(p, map->osd_state, map->max_osd);
- *p += 4; /* skip length field (should match max) */
+ if (ceph_decode_32(p) != map->max_osd)
+ goto e_inval;
+
for (i = 0; i < map->max_osd; i++)
map->osd_weight[i] = ceph_decode_32(p);
- *p += 4; /* skip length field (should match max) */
+ if (ceph_decode_32(p) != map->max_osd)
+ goto e_inval;
+
ceph_decode_copy(p, map->osd_addr, map->max_osd*sizeof(*map->osd_addr));
for (i = 0; i < map->max_osd; i++)
ceph_decode_addr(&map->osd_addr[i]);
/* pg_temp */
- ceph_decode_32_safe(p, end, len, bad);
- for (i = 0; i < len; i++) {
- int n, j;
- struct ceph_pg pgid;
- struct ceph_pg_mapping *pg;
+ err = decode_pg_temp(p, end, map);
+ if (err)
+ goto bad;
- err = ceph_decode_pgid(p, end, &pgid);
+ /* primary_temp */
+ if (struct_v >= 1) {
+ err = decode_primary_temp(p, end, map);
if (err)
goto bad;
- ceph_decode_need(p, end, sizeof(u32), bad);
- n = ceph_decode_32(p);
- err = -EINVAL;
- if (n > (UINT_MAX - sizeof(*pg)) / sizeof(u32))
- goto bad;
- ceph_decode_need(p, end, n * sizeof(u32), bad);
- err = -ENOMEM;
- pg = kmalloc(sizeof(*pg) + n*sizeof(u32), GFP_NOFS);
- if (!pg)
- goto bad;
- pg->pgid = pgid;
- pg->len = n;
- for (j = 0; j < n; j++)
- pg->osds[j] = ceph_decode_32(p);
+ }
- err = __insert_pg_mapping(pg, &map->pg_temp);
+ /* primary_affinity */
+ if (struct_v >= 2) {
+ err = decode_primary_affinity(p, end, map);
if (err)
goto bad;
- dout(" added pg_temp %lld.%x len %d\n", pgid.pool, pgid.seed,
- len);
+ } else {
+ /* XXX can this happen? */
+ kfree(map->osd_primary_affinity);
+ map->osd_primary_affinity = NULL;
}
/* crush */
- ceph_decode_32_safe(p, end, len, bad);
- dout("osdmap_decode crush len %d from off 0x%x\n", len,
- (int)(*p - start));
- ceph_decode_need(p, end, len, bad);
- map->crush = crush_decode(*p, end);
- *p += len;
+ ceph_decode_32_safe(p, end, len, e_inval);
+ map->crush = crush_decode(*p, min(*p + len, end));
if (IS_ERR(map->crush)) {
err = PTR_ERR(map->crush);
map->crush = NULL;
goto bad;
}
+ *p += len;
- /* ignore the rest of the map */
+ /* ignore the rest */
*p = end;
- dout("osdmap_decode done %p %p\n", *p, end);
- return map;
+ dout("full osdmap epoch %d max_osd %d\n", map->epoch, map->max_osd);
+ return 0;
+e_inval:
+ err = -EINVAL;
bad:
- dout("osdmap_decode fail err %d\n", err);
- ceph_osdmap_destroy(map);
- return ERR_PTR(err);
+ pr_err("corrupt full osdmap (%d) epoch %d off %d (%p of %p-%p)\n",
+ err, epoch, (int)(*p - start), *p, start, end);
+ print_hex_dump(KERN_DEBUG, "osdmap: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ start, end - start, true);
+ return err;
+}
+
+/*
+ * Allocate and decode a full map.
+ */
+struct ceph_osdmap *ceph_osdmap_decode(void **p, void *end)
+{
+ struct ceph_osdmap *map;
+ int ret;
+
+ map = kzalloc(sizeof(*map), GFP_NOFS);
+ if (!map)
+ return ERR_PTR(-ENOMEM);
+
+ map->pg_temp = RB_ROOT;
+ map->primary_temp = RB_ROOT;
+ mutex_init(&map->crush_scratch_mutex);
+
+ ret = osdmap_decode(p, end, map);
+ if (ret) {
+ ceph_osdmap_destroy(map);
+ return ERR_PTR(ret);
+ }
+
+ return map;
}
/*
@@ -840,17 +1177,18 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
__s64 new_pool_max;
__s32 new_flags, max;
void *start = *p;
- int err = -EINVAL;
- u16 version;
+ int err;
+ u8 struct_v;
+
+ dout("%s %p to %p len %d\n", __func__, *p, end, (int)(end - *p));
- ceph_decode_16_safe(p, end, version, bad);
- if (version != 6) {
- pr_warning("got unknown v %d != 6 of inc osdmap\n", version);
+ err = get_osdmap_client_data_v(p, end, "inc", &struct_v);
+ if (err)
goto bad;
- }
- ceph_decode_need(p, end, sizeof(fsid)+sizeof(modified)+2*sizeof(u32),
- bad);
+ /* fsid, epoch, modified, new_pool_max, new_flags */
+ ceph_decode_need(p, end, sizeof(fsid) + sizeof(u32) + sizeof(modified) +
+ sizeof(u64) + sizeof(u32), e_inval);
ceph_decode_copy(p, &fsid, sizeof(fsid));
epoch = ceph_decode_32(p);
BUG_ON(epoch != map->epoch+1);
@@ -859,21 +1197,22 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
new_flags = ceph_decode_32(p);
/* full map? */
- ceph_decode_32_safe(p, end, len, bad);
+ ceph_decode_32_safe(p, end, len, e_inval);
if (len > 0) {
dout("apply_incremental full map len %d, %p to %p\n",
len, *p, end);
- return osdmap_decode(p, min(*p+len, end));
+ return ceph_osdmap_decode(p, min(*p+len, end));
}
/* new crush? */
- ceph_decode_32_safe(p, end, len, bad);
+ ceph_decode_32_safe(p, end, len, e_inval);
if (len > 0) {
- dout("apply_incremental new crush map len %d, %p to %p\n",
- len, *p, end);
newcrush = crush_decode(*p, min(*p+len, end));
- if (IS_ERR(newcrush))
- return ERR_CAST(newcrush);
+ if (IS_ERR(newcrush)) {
+ err = PTR_ERR(newcrush);
+ newcrush = NULL;
+ goto bad;
+ }
*p += len;
}
@@ -883,13 +1222,11 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
if (new_pool_max >= 0)
map->pool_max = new_pool_max;
- ceph_decode_need(p, end, 5*sizeof(u32), bad);
-
/* new max? */
- max = ceph_decode_32(p);
+ ceph_decode_32_safe(p, end, max, e_inval);
if (max >= 0) {
err = osdmap_set_max_osd(map, max);
- if (err < 0)
+ if (err)
goto bad;
}
@@ -902,51 +1239,34 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
newcrush = NULL;
}
- /* new_pool */
- ceph_decode_32_safe(p, end, len, bad);
- while (len--) {
- struct ceph_pg_pool_info *pi;
+ /* new_pools */
+ err = decode_new_pools(p, end, map);
+ if (err)
+ goto bad;
- ceph_decode_64_safe(p, end, pool, bad);
- pi = __lookup_pg_pool(&map->pg_pools, pool);
- if (!pi) {
- pi = kzalloc(sizeof(*pi), GFP_NOFS);
- if (!pi) {
- err = -ENOMEM;
- goto bad;
- }
- pi->id = pool;
- __insert_pg_pool(&map->pg_pools, pi);
- }
- err = __decode_pool(p, end, pi);
- if (err < 0)
- goto bad;
- }
- if (version >= 5) {
- err = __decode_pool_names(p, end, map);
- if (err < 0)
- goto bad;
- }
+ /* new_pool_names */
+ err = decode_pool_names(p, end, map);
+ if (err)
+ goto bad;
/* old_pool */
- ceph_decode_32_safe(p, end, len, bad);
+ ceph_decode_32_safe(p, end, len, e_inval);
while (len--) {
struct ceph_pg_pool_info *pi;
- ceph_decode_64_safe(p, end, pool, bad);
+ ceph_decode_64_safe(p, end, pool, e_inval);
pi = __lookup_pg_pool(&map->pg_pools, pool);
if (pi)
__remove_pg_pool(&map->pg_pools, pi);
}
/* new_up */
- err = -EINVAL;
- ceph_decode_32_safe(p, end, len, bad);
+ ceph_decode_32_safe(p, end, len, e_inval);
while (len--) {
u32 osd;
struct ceph_entity_addr addr;
- ceph_decode_32_safe(p, end, osd, bad);
- ceph_decode_copy_safe(p, end, &addr, sizeof(addr), bad);
+ ceph_decode_32_safe(p, end, osd, e_inval);
+ ceph_decode_copy_safe(p, end, &addr, sizeof(addr), e_inval);
ceph_decode_addr(&addr);
pr_info("osd%d up\n", osd);
BUG_ON(osd >= map->max_osd);
@@ -955,11 +1275,11 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
}
/* new_state */
- ceph_decode_32_safe(p, end, len, bad);
+ ceph_decode_32_safe(p, end, len, e_inval);
while (len--) {
u32 osd;
u8 xorstate;
- ceph_decode_32_safe(p, end, osd, bad);
+ ceph_decode_32_safe(p, end, osd, e_inval);
xorstate = **(u8 **)p;
(*p)++; /* clean flag */
if (xorstate == 0)
@@ -971,10 +1291,10 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
}
/* new_weight */
- ceph_decode_32_safe(p, end, len, bad);
+ ceph_decode_32_safe(p, end, len, e_inval);
while (len--) {
u32 osd, off;
- ceph_decode_need(p, end, sizeof(u32)*2, bad);
+ ceph_decode_need(p, end, sizeof(u32)*2, e_inval);
osd = ceph_decode_32(p);
off = ceph_decode_32(p);
pr_info("osd%d weight 0x%x %s\n", osd, off,
@@ -985,56 +1305,35 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
}
/* new_pg_temp */
- ceph_decode_32_safe(p, end, len, bad);
- while (len--) {
- struct ceph_pg_mapping *pg;
- int j;
- struct ceph_pg pgid;
- u32 pglen;
+ err = decode_new_pg_temp(p, end, map);
+ if (err)
+ goto bad;
- err = ceph_decode_pgid(p, end, &pgid);
+ /* new_primary_temp */
+ if (struct_v >= 1) {
+ err = decode_new_primary_temp(p, end, map);
if (err)
goto bad;
- ceph_decode_need(p, end, sizeof(u32), bad);
- pglen = ceph_decode_32(p);
- if (pglen) {
- ceph_decode_need(p, end, pglen*sizeof(u32), bad);
-
- /* removing existing (if any) */
- (void) __remove_pg_mapping(&map->pg_temp, pgid);
+ }
- /* insert */
- err = -EINVAL;
- if (pglen > (UINT_MAX - sizeof(*pg)) / sizeof(u32))
- goto bad;
- err = -ENOMEM;
- pg = kmalloc(sizeof(*pg) + sizeof(u32)*pglen, GFP_NOFS);
- if (!pg)
- goto bad;
- pg->pgid = pgid;
- pg->len = pglen;
- for (j = 0; j < pglen; j++)
- pg->osds[j] = ceph_decode_32(p);
- err = __insert_pg_mapping(pg, &map->pg_temp);
- if (err) {
- kfree(pg);
- goto bad;
- }
- dout(" added pg_temp %lld.%x len %d\n", pgid.pool,
- pgid.seed, pglen);
- } else {
- /* remove */
- __remove_pg_mapping(&map->pg_temp, pgid);
- }
+ /* new_primary_affinity */
+ if (struct_v >= 2) {
+ err = decode_new_primary_affinity(p, end, map);
+ if (err)
+ goto bad;
}
/* ignore the rest */
*p = end;
+
+ dout("inc osdmap epoch %d max_osd %d\n", map->epoch, map->max_osd);
return map;
+e_inval:
+ err = -EINVAL;
bad:
- pr_err("corrupt inc osdmap epoch %d off %d (%p of %p-%p)\n",
- epoch, (int)(*p - start), *p, start, end);
+ pr_err("corrupt inc osdmap (%d) epoch %d off %d (%p of %p-%p)\n",
+ err, epoch, (int)(*p - start), *p, start, end);
print_hex_dump(KERN_DEBUG, "osdmap: ",
DUMP_PREFIX_OFFSET, 16, 1,
start, end - start, true);
@@ -1142,61 +1441,249 @@ int ceph_oloc_oid_to_pg(struct ceph_osdmap *osdmap,
}
EXPORT_SYMBOL(ceph_oloc_oid_to_pg);
-static int crush_do_rule_ary(const struct crush_map *map, int ruleno, int x,
- int *result, int result_max,
- const __u32 *weight, int weight_max)
+static int do_crush(struct ceph_osdmap *map, int ruleno, int x,
+ int *result, int result_max,
+ const __u32 *weight, int weight_max)
{
- int scratch[result_max * 3];
+ int r;
- return crush_do_rule(map, ruleno, x, result, result_max,
- weight, weight_max, scratch);
+ BUG_ON(result_max > CEPH_PG_MAX_SIZE);
+
+ mutex_lock(&map->crush_scratch_mutex);
+ r = crush_do_rule(map->crush, ruleno, x, result, result_max,
+ weight, weight_max, map->crush_scratch_ary);
+ mutex_unlock(&map->crush_scratch_mutex);
+
+ return r;
}
/*
- * Calculate raw osd vector for the given pgid. Return pointer to osd
- * array, or NULL on failure.
+ * Calculate raw (crush) set for given pgid.
+ *
+ * Return raw set length, or error.
*/
-static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid,
- int *osds, int *num)
+static int pg_to_raw_osds(struct ceph_osdmap *osdmap,
+ struct ceph_pg_pool_info *pool,
+ struct ceph_pg pgid, u32 pps, int *osds)
{
- struct ceph_pg_mapping *pg;
- struct ceph_pg_pool_info *pool;
int ruleno;
- int r;
- u32 pps;
+ int len;
- pool = __lookup_pg_pool(&osdmap->pg_pools, pgid.pool);
- if (!pool)
- return NULL;
+ /* crush */
+ ruleno = crush_find_rule(osdmap->crush, pool->crush_ruleset,
+ pool->type, pool->size);
+ if (ruleno < 0) {
+ pr_err("no crush rule: pool %lld ruleset %d type %d size %d\n",
+ pgid.pool, pool->crush_ruleset, pool->type,
+ pool->size);
+ return -ENOENT;
+ }
- /* pg_temp? */
+ len = do_crush(osdmap, ruleno, pps, osds,
+ min_t(int, pool->size, CEPH_PG_MAX_SIZE),
+ osdmap->osd_weight, osdmap->max_osd);
+ if (len < 0) {
+ pr_err("error %d from crush rule %d: pool %lld ruleset %d type %d size %d\n",
+ len, ruleno, pgid.pool, pool->crush_ruleset,
+ pool->type, pool->size);
+ return len;
+ }
+
+ return len;
+}
+
+/*
+ * Given raw set, calculate up set and up primary.
+ *
+ * Return up set length. *primary is set to up primary osd id, or -1
+ * if up set is empty.
+ */
+static int raw_to_up_osds(struct ceph_osdmap *osdmap,
+ struct ceph_pg_pool_info *pool,
+ int *osds, int len, int *primary)
+{
+ int up_primary = -1;
+ int i;
+
+ if (ceph_can_shift_osds(pool)) {
+ int removed = 0;
+
+ for (i = 0; i < len; i++) {
+ if (ceph_osd_is_down(osdmap, osds[i])) {
+ removed++;
+ continue;
+ }
+ if (removed)
+ osds[i - removed] = osds[i];
+ }
+
+ len -= removed;
+ if (len > 0)
+ up_primary = osds[0];
+ } else {
+ for (i = len - 1; i >= 0; i--) {
+ if (ceph_osd_is_down(osdmap, osds[i]))
+ osds[i] = CRUSH_ITEM_NONE;
+ else
+ up_primary = osds[i];
+ }
+ }
+
+ *primary = up_primary;
+ return len;
+}
+
+static void apply_primary_affinity(struct ceph_osdmap *osdmap, u32 pps,
+ struct ceph_pg_pool_info *pool,
+ int *osds, int len, int *primary)
+{
+ int i;
+ int pos = -1;
+
+ /*
+ * Do we have any non-default primary_affinity values for these
+ * osds?
+ */
+ if (!osdmap->osd_primary_affinity)
+ return;
+
+ for (i = 0; i < len; i++) {
+ if (osds[i] != CRUSH_ITEM_NONE &&
+ osdmap->osd_primary_affinity[i] !=
+ CEPH_OSD_DEFAULT_PRIMARY_AFFINITY) {
+ break;
+ }
+ }
+ if (i == len)
+ return;
+
+ /*
+ * Pick the primary. Feed both the seed (for the pg) and the
+ * osd into the hash/rng so that a proportional fraction of an
+ * osd's pgs get rejected as primary.
+ */
+ for (i = 0; i < len; i++) {
+ int osd;
+ u32 aff;
+
+ osd = osds[i];
+ if (osd == CRUSH_ITEM_NONE)
+ continue;
+
+ aff = osdmap->osd_primary_affinity[osd];
+ if (aff < CEPH_OSD_MAX_PRIMARY_AFFINITY &&
+ (crush_hash32_2(CRUSH_HASH_RJENKINS1,
+ pps, osd) >> 16) >= aff) {
+ /*
+ * We chose not to use this primary. Note it
+ * anyway as a fallback in case we don't pick
+ * anyone else, but keep looking.
+ */
+ if (pos < 0)
+ pos = i;
+ } else {
+ pos = i;
+ break;
+ }
+ }
+ if (pos < 0)
+ return;
+
+ *primary = osds[pos];
+
+ if (ceph_can_shift_osds(pool) && pos > 0) {
+ /* move the new primary to the front */
+ for (i = pos; i > 0; i--)
+ osds[i] = osds[i - 1];
+ osds[0] = *primary;
+ }
+}
+
+/*
+ * Given up set, apply pg_temp and primary_temp mappings.
+ *
+ * Return acting set length. *primary is set to acting primary osd id,
+ * or -1 if acting set is empty.
+ */
+static int apply_temps(struct ceph_osdmap *osdmap,
+ struct ceph_pg_pool_info *pool, struct ceph_pg pgid,
+ int *osds, int len, int *primary)
+{
+ struct ceph_pg_mapping *pg;
+ int temp_len;
+ int temp_primary;
+ int i;
+
+ /* raw_pg -> pg */
pgid.seed = ceph_stable_mod(pgid.seed, pool->pg_num,
pool->pg_num_mask);
+
+ /* pg_temp? */
pg = __lookup_pg_mapping(&osdmap->pg_temp, pgid);
if (pg) {
- *num = pg->len;
- return pg->osds;
+ temp_len = 0;
+ temp_primary = -1;
+
+ for (i = 0; i < pg->pg_temp.len; i++) {
+ if (ceph_osd_is_down(osdmap, pg->pg_temp.osds[i])) {
+ if (ceph_can_shift_osds(pool))
+ continue;
+ else
+ osds[temp_len++] = CRUSH_ITEM_NONE;
+ } else {
+ osds[temp_len++] = pg->pg_temp.osds[i];
+ }
+ }
+
+ /* apply pg_temp's primary */
+ for (i = 0; i < temp_len; i++) {
+ if (osds[i] != CRUSH_ITEM_NONE) {
+ temp_primary = osds[i];
+ break;
+ }
+ }
+ } else {
+ temp_len = len;
+ temp_primary = *primary;
}
- /* crush */
- ruleno = crush_find_rule(osdmap->crush, pool->crush_ruleset,
- pool->type, pool->size);
- if (ruleno < 0) {
- pr_err("no crush rule pool %lld ruleset %d type %d size %d\n",
- pgid.pool, pool->crush_ruleset, pool->type,
- pool->size);
- return NULL;
+ /* primary_temp? */
+ pg = __lookup_pg_mapping(&osdmap->primary_temp, pgid);
+ if (pg)
+ temp_primary = pg->primary_temp.osd;
+
+ *primary = temp_primary;
+ return temp_len;
+}
+
+/*
+ * Calculate acting set for given pgid.
+ *
+ * Return acting set length, or error. *primary is set to acting
+ * primary osd id, or -1 if acting set is empty or on error.
+ */
+int ceph_calc_pg_acting(struct ceph_osdmap *osdmap, struct ceph_pg pgid,
+ int *osds, int *primary)
+{
+ struct ceph_pg_pool_info *pool;
+ u32 pps;
+ int len;
+
+ pool = __lookup_pg_pool(&osdmap->pg_pools, pgid.pool);
+ if (!pool) {
+ *primary = -1;
+ return -ENOENT;
}
if (pool->flags & CEPH_POOL_FLAG_HASHPSPOOL) {
- /* hash pool id and seed sothat pool PGs do not overlap */
+ /* hash pool id and seed so that pool PGs do not overlap */
pps = crush_hash32_2(CRUSH_HASH_RJENKINS1,
ceph_stable_mod(pgid.seed, pool->pgp_num,
pool->pgp_num_mask),
pgid.pool);
} else {
/*
- * legacy ehavior: add ps and pool together. this is
+ * legacy behavior: add ps and pool together. this is
* not a great approach because the PGs from each pool
* will overlap on top of each other: 0.5 == 1.4 ==
* 2.3 == ...
@@ -1205,38 +1692,20 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid,
pool->pgp_num_mask) +
(unsigned)pgid.pool;
}
- r = crush_do_rule_ary(osdmap->crush, ruleno, pps,
- osds, min_t(int, pool->size, *num),
- osdmap->osd_weight, osdmap->max_osd);
- if (r < 0) {
- pr_err("error %d from crush rule: pool %lld ruleset %d type %d"
- " size %d\n", r, pgid.pool, pool->crush_ruleset,
- pool->type, pool->size);
- return NULL;
+
+ len = pg_to_raw_osds(osdmap, pool, pgid, pps, osds);
+ if (len < 0) {
+ *primary = -1;
+ return len;
}
- *num = r;
- return osds;
-}
-/*
- * Return acting set for given pgid.
- */
-int ceph_calc_pg_acting(struct ceph_osdmap *osdmap, struct ceph_pg pgid,
- int *acting)
-{
- int rawosds[CEPH_PG_MAX_SIZE], *osds;
- int i, o, num = CEPH_PG_MAX_SIZE;
+ len = raw_to_up_osds(osdmap, pool, osds, len, primary);
- osds = calc_pg_raw(osdmap, pgid, rawosds, &num);
- if (!osds)
- return -1;
+ apply_primary_affinity(osdmap, pps, pool, osds, len, primary);
- /* primary is first up osd */
- o = 0;
- for (i = 0; i < num; i++)
- if (ceph_osd_is_up(osdmap, osds[i]))
- acting[o++] = osds[i];
- return o;
+ len = apply_temps(osdmap, pool, pgid, osds, len, primary);
+
+ return len;
}
/*
@@ -1244,17 +1713,11 @@ int ceph_calc_pg_acting(struct ceph_osdmap *osdmap, struct ceph_pg pgid,
*/
int ceph_calc_pg_primary(struct ceph_osdmap *osdmap, struct ceph_pg pgid)
{
- int rawosds[CEPH_PG_MAX_SIZE], *osds;
- int i, num = CEPH_PG_MAX_SIZE;
+ int osds[CEPH_PG_MAX_SIZE];
+ int primary;
- osds = calc_pg_raw(osdmap, pgid, rawosds, &num);
- if (!osds)
- return -1;
+ ceph_calc_pg_acting(osdmap, pgid, osds, &primary);
- /* primary is first up osd */
- for (i = 0; i < num; i++)
- if (ceph_osd_is_up(osdmap, osds[i]))
- return osds[i];
- return -1;
+ return primary;
}
EXPORT_SYMBOL(ceph_calc_pg_primary);
diff --git a/net/core/Makefile b/net/core/Makefile
index 9628c20acff6..826b925aa453 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -21,5 +21,6 @@ obj-$(CONFIG_FIB_RULES) += fib_rules.o
obj-$(CONFIG_TRACEPOINTS) += net-traces.o
obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o
obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o
+obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o
obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o
obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o
diff --git a/net/core/dev.c b/net/core/dev.c
index bc3c89792b0c..757063420ce0 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1245,7 +1245,7 @@ static int __dev_open(struct net_device *dev)
* If we don't do this there is a chance ndo_poll_controller
* or ndo_poll may be running while we open the device
*/
- netpoll_rx_disable(dev);
+ netpoll_poll_disable(dev);
ret = call_netdevice_notifiers(NETDEV_PRE_UP, dev);
ret = notifier_to_errno(ret);
@@ -1260,7 +1260,7 @@ static int __dev_open(struct net_device *dev)
if (!ret && ops->ndo_open)
ret = ops->ndo_open(dev);
- netpoll_rx_enable(dev);
+ netpoll_poll_enable(dev);
if (ret)
clear_bit(__LINK_STATE_START, &dev->state);
@@ -1313,6 +1313,9 @@ static int __dev_close_many(struct list_head *head)
might_sleep();
list_for_each_entry(dev, head, close_list) {
+ /* Temporarily disable netpoll until the interface is down */
+ netpoll_poll_disable(dev);
+
call_netdevice_notifiers(NETDEV_GOING_DOWN, dev);
clear_bit(__LINK_STATE_START, &dev->state);
@@ -1343,6 +1346,7 @@ static int __dev_close_many(struct list_head *head)
dev->flags &= ~IFF_UP;
net_dmaengine_put();
+ netpoll_poll_enable(dev);
}
return 0;
@@ -1353,14 +1357,10 @@ static int __dev_close(struct net_device *dev)
int retval;
LIST_HEAD(single);
- /* Temporarily disable netpoll until the interface is down */
- netpoll_rx_disable(dev);
-
list_add(&dev->close_list, &single);
retval = __dev_close_many(&single);
list_del(&single);
- netpoll_rx_enable(dev);
return retval;
}
@@ -1398,14 +1398,9 @@ int dev_close(struct net_device *dev)
if (dev->flags & IFF_UP) {
LIST_HEAD(single);
- /* Block netpoll rx while the interface is going down */
- netpoll_rx_disable(dev);
-
list_add(&dev->close_list, &single);
dev_close_many(&single);
list_del(&single);
-
- netpoll_rx_enable(dev);
}
return 0;
}
@@ -1645,8 +1640,7 @@ static inline void net_timestamp_set(struct sk_buff *skb)
__net_timestamp(SKB); \
} \
-static inline bool is_skb_forwardable(struct net_device *dev,
- struct sk_buff *skb)
+bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb)
{
unsigned int len;
@@ -1665,6 +1659,7 @@ static inline bool is_skb_forwardable(struct net_device *dev,
return false;
}
+EXPORT_SYMBOL_GPL(is_skb_forwardable);
/**
* dev_forward_skb - loopback an skb to another netif
@@ -2885,6 +2880,7 @@ recursion_alert:
rc = -ENETDOWN;
rcu_read_unlock_bh();
+ atomic_long_inc(&dev->tx_dropped);
kfree_skb(skb);
return rc;
out:
@@ -2957,7 +2953,7 @@ set_rps_cpu(struct net_device *dev, struct sk_buff *skb,
flow_table = rcu_dereference(rxqueue->rps_flow_table);
if (!flow_table)
goto out;
- flow_id = skb->rxhash & flow_table->mask;
+ flow_id = skb_get_hash(skb) & flow_table->mask;
rc = dev->netdev_ops->ndo_rx_flow_steer(dev, skb,
rxq_index, flow_id);
if (rc < 0)
@@ -2991,6 +2987,7 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb,
struct rps_sock_flow_table *sock_flow_table;
int cpu = -1;
u16 tcpu;
+ u32 hash;
if (skb_rx_queue_recorded(skb)) {
u16 index = skb_get_rx_queue(skb);
@@ -3019,7 +3016,8 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb,
}
skb_reset_network_header(skb);
- if (!skb_get_hash(skb))
+ hash = skb_get_hash(skb);
+ if (!hash)
goto done;
flow_table = rcu_dereference(rxqueue->rps_flow_table);
@@ -3028,11 +3026,10 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb,
u16 next_cpu;
struct rps_dev_flow *rflow;
- rflow = &flow_table->flows[skb->rxhash & flow_table->mask];
+ rflow = &flow_table->flows[hash & flow_table->mask];
tcpu = rflow->cpu;
- next_cpu = sock_flow_table->ents[skb->rxhash &
- sock_flow_table->mask];
+ next_cpu = sock_flow_table->ents[hash & sock_flow_table->mask];
/*
* If the desired CPU (where last recvmsg was done) is
@@ -3061,7 +3058,7 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb,
}
if (map) {
- tcpu = map->cpus[((u64) skb->rxhash * map->len) >> 32];
+ tcpu = map->cpus[((u64) hash * map->len) >> 32];
if (cpu_online(tcpu)) {
cpu = tcpu;
@@ -3236,10 +3233,6 @@ static int netif_rx_internal(struct sk_buff *skb)
{
int ret;
- /* if netpoll wants it, pretend we never saw it */
- if (netpoll_rx(skb))
- return NET_RX_DROP;
-
net_timestamp_check(netdev_tstamp_prequeue, skb);
trace_netif_rx(skb);
@@ -3446,7 +3439,7 @@ out:
* @rx_handler: receive handler to register
* @rx_handler_data: data pointer that is used by rx handler
*
- * Register a receive hander for a device. This handler will then be
+ * Register a receive handler for a device. This handler will then be
* called from __netif_receive_skb. A negative errno code is returned
* on a failure.
*
@@ -3500,11 +3493,11 @@ EXPORT_SYMBOL_GPL(netdev_rx_handler_unregister);
static bool skb_pfmemalloc_protocol(struct sk_buff *skb)
{
switch (skb->protocol) {
- case __constant_htons(ETH_P_ARP):
- case __constant_htons(ETH_P_IP):
- case __constant_htons(ETH_P_IPV6):
- case __constant_htons(ETH_P_8021Q):
- case __constant_htons(ETH_P_8021AD):
+ case htons(ETH_P_ARP):
+ case htons(ETH_P_IP):
+ case htons(ETH_P_IPV6):
+ case htons(ETH_P_8021Q):
+ case htons(ETH_P_8021AD):
return true;
default:
return false;
@@ -3525,10 +3518,6 @@ static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
trace_netif_receive_skb(skb);
- /* if we've gotten here through NAPI, check netpoll */
- if (netpoll_receive_skb(skb))
- goto out;
-
orig_dev = skb->dev;
skb_reset_network_header(skb);
@@ -3655,7 +3644,6 @@ drop:
unlock:
rcu_read_unlock();
-out:
return ret;
}
@@ -3845,10 +3833,10 @@ static void gro_list_prepare(struct napi_struct *napi, struct sk_buff *skb)
diffs |= p->vlan_tci ^ skb->vlan_tci;
if (maclen == ETH_HLEN)
diffs |= compare_ether_header(skb_mac_header(p),
- skb_gro_mac_header(skb));
+ skb_mac_header(skb));
else if (!diffs)
diffs = memcmp(skb_mac_header(p),
- skb_gro_mac_header(skb),
+ skb_mac_header(skb),
maclen);
NAPI_GRO_CB(p)->same_flow = !diffs;
}
@@ -3871,6 +3859,27 @@ static void skb_gro_reset_offset(struct sk_buff *skb)
}
}
+static void gro_pull_from_frag0(struct sk_buff *skb, int grow)
+{
+ struct skb_shared_info *pinfo = skb_shinfo(skb);
+
+ BUG_ON(skb->end - skb->tail < grow);
+
+ memcpy(skb_tail_pointer(skb), NAPI_GRO_CB(skb)->frag0, grow);
+
+ skb->data_len -= grow;
+ skb->tail += grow;
+
+ pinfo->frags[0].page_offset += grow;
+ skb_frag_size_sub(&pinfo->frags[0], grow);
+
+ if (unlikely(!skb_frag_size(&pinfo->frags[0]))) {
+ skb_frag_unref(skb, 0);
+ memmove(pinfo->frags, pinfo->frags + 1,
+ --pinfo->nr_frags * sizeof(pinfo->frags[0]));
+ }
+}
+
static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
struct sk_buff **pp = NULL;
@@ -3879,14 +3888,14 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
struct list_head *head = &offload_base;
int same_flow;
enum gro_result ret;
+ int grow;
- if (!(skb->dev->features & NETIF_F_GRO) || netpoll_rx_on(skb))
+ if (!(skb->dev->features & NETIF_F_GRO))
goto normal;
if (skb_is_gso(skb) || skb_has_frag_list(skb))
goto normal;
- skb_gro_reset_offset(skb);
gro_list_prepare(napi, skb);
NAPI_GRO_CB(skb)->csum = skb->csum; /* Needed for CHECKSUM_COMPLETE */
@@ -3950,27 +3959,9 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
ret = GRO_HELD;
pull:
- if (skb_headlen(skb) < skb_gro_offset(skb)) {
- int grow = skb_gro_offset(skb) - skb_headlen(skb);
-
- BUG_ON(skb->end - skb->tail < grow);
-
- memcpy(skb_tail_pointer(skb), NAPI_GRO_CB(skb)->frag0, grow);
-
- skb->tail += grow;
- skb->data_len -= grow;
-
- skb_shinfo(skb)->frags[0].page_offset += grow;
- skb_frag_size_sub(&skb_shinfo(skb)->frags[0], grow);
-
- if (unlikely(!skb_frag_size(&skb_shinfo(skb)->frags[0]))) {
- skb_frag_unref(skb, 0);
- memmove(skb_shinfo(skb)->frags,
- skb_shinfo(skb)->frags + 1,
- --skb_shinfo(skb)->nr_frags * sizeof(skb_frag_t));
- }
- }
-
+ grow = skb_gro_offset(skb) - skb_headlen(skb);
+ if (grow > 0)
+ gro_pull_from_frag0(skb, grow);
ok:
return ret;
@@ -4038,6 +4029,8 @@ gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
trace_napi_gro_receive_entry(skb);
+ skb_gro_reset_offset(skb);
+
return napi_skb_finish(dev_gro_receive(napi, skb), skb);
}
EXPORT_SYMBOL(napi_gro_receive);
@@ -4066,12 +4059,16 @@ struct sk_buff *napi_get_frags(struct napi_struct *napi)
}
EXPORT_SYMBOL(napi_get_frags);
-static gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb,
- gro_result_t ret)
+static gro_result_t napi_frags_finish(struct napi_struct *napi,
+ struct sk_buff *skb,
+ gro_result_t ret)
{
switch (ret) {
case GRO_NORMAL:
- if (netif_receive_skb_internal(skb))
+ case GRO_HELD:
+ __skb_push(skb, ETH_HLEN);
+ skb->protocol = eth_type_trans(skb, skb->dev);
+ if (ret == GRO_NORMAL && netif_receive_skb_internal(skb))
ret = GRO_DROP;
break;
@@ -4080,7 +4077,6 @@ static gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *
napi_reuse_skb(napi, skb);
break;
- case GRO_HELD:
case GRO_MERGED:
break;
}
@@ -4088,17 +4084,41 @@ static gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *
return ret;
}
+/* Upper GRO stack assumes network header starts at gro_offset=0
+ * Drivers could call both napi_gro_frags() and napi_gro_receive()
+ * We copy ethernet header into skb->data to have a common layout.
+ */
static struct sk_buff *napi_frags_skb(struct napi_struct *napi)
{
struct sk_buff *skb = napi->skb;
+ const struct ethhdr *eth;
+ unsigned int hlen = sizeof(*eth);
napi->skb = NULL;
- if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr)))) {
- napi_reuse_skb(napi, skb);
- return NULL;
+ skb_reset_mac_header(skb);
+ skb_gro_reset_offset(skb);
+
+ eth = skb_gro_header_fast(skb, 0);
+ if (unlikely(skb_gro_header_hard(skb, hlen))) {
+ eth = skb_gro_header_slow(skb, hlen, 0);
+ if (unlikely(!eth)) {
+ napi_reuse_skb(napi, skb);
+ return NULL;
+ }
+ } else {
+ gro_pull_from_frag0(skb, hlen);
+ NAPI_GRO_CB(skb)->frag0 += hlen;
+ NAPI_GRO_CB(skb)->frag0_len -= hlen;
}
- skb->protocol = eth_type_trans(skb, skb->dev);
+ __skb_pull(skb, hlen);
+
+ /*
+ * This works because the only protocols we care about don't require
+ * special handling.
+ * We'll fix it up properly in napi_frags_finish()
+ */
+ skb->protocol = eth->h_proto;
return skb;
}
@@ -6251,6 +6271,7 @@ struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
netdev_stats_to_stats64(storage, &dev->stats);
}
storage->rx_dropped += atomic_long_read(&dev->rx_dropped);
+ storage->tx_dropped += atomic_long_read(&dev->tx_dropped);
return storage;
}
EXPORT_SYMBOL(dev_get_stats);
diff --git a/net/core/filter.c b/net/core/filter.c
index ad30d626a5bd..765556ba32ef 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1,11 +1,16 @@
/*
* Linux Socket Filter - Kernel level socket filtering
*
- * Author:
- * Jay Schulist <jschlst@samba.org>
+ * Based on the design of the Berkeley Packet Filter. The new
+ * internal format has been designed by PLUMgrid:
*
- * Based on the design of:
- * - The Berkeley Packet Filter
+ * Copyright (c) 2011 - 2014 PLUMgrid, http://plumgrid.com
+ *
+ * Authors:
+ *
+ * Jay Schulist <jschlst@samba.org>
+ * Alexei Starovoitov <ast@plumgrid.com>
+ * Daniel Borkmann <dborkman@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -108,304 +113,1045 @@ int sk_filter(struct sock *sk, struct sk_buff *skb)
}
EXPORT_SYMBOL(sk_filter);
+/* Base function for offset calculation. Needs to go into .text section,
+ * therefore keeping it non-static as well; will also be used by JITs
+ * anyway later on, so do not let the compiler omit it.
+ */
+noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
+{
+ return 0;
+}
+
/**
- * sk_run_filter - run a filter on a socket
- * @skb: buffer to run the filter on
- * @fentry: filter to apply
+ * __sk_run_filter - run a filter on a given context
+ * @ctx: buffer to run the filter on
+ * @insn: filter to apply
*
- * Decode and apply filter instructions to the skb->data.
- * Return length to keep, 0 for none. @skb is the data we are
- * filtering, @filter is the array of filter instructions.
- * Because all jumps are guaranteed to be before last instruction,
- * and last instruction guaranteed to be a RET, we dont need to check
- * flen. (We used to pass to this function the length of filter)
+ * Decode and apply filter instructions to the skb->data. Return length to
+ * keep, 0 for none. @ctx is the data we are operating on, @insn is the
+ * array of filter instructions.
*/
-unsigned int sk_run_filter(const struct sk_buff *skb,
- const struct sock_filter *fentry)
+unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn)
{
+ u64 stack[MAX_BPF_STACK / sizeof(u64)];
+ u64 regs[MAX_BPF_REG], tmp;
void *ptr;
- u32 A = 0; /* Accumulator */
- u32 X = 0; /* Index Register */
- u32 mem[BPF_MEMWORDS]; /* Scratch Memory Store */
- u32 tmp;
- int k;
+ int off;
- /*
- * Process array of filter instructions.
- */
- for (;; fentry++) {
-#if defined(CONFIG_X86_32)
-#define K (fentry->k)
-#else
- const u32 K = fentry->k;
-#endif
-
- switch (fentry->code) {
- case BPF_S_ALU_ADD_X:
- A += X;
- continue;
- case BPF_S_ALU_ADD_K:
- A += K;
- continue;
- case BPF_S_ALU_SUB_X:
- A -= X;
- continue;
- case BPF_S_ALU_SUB_K:
- A -= K;
- continue;
- case BPF_S_ALU_MUL_X:
- A *= X;
- continue;
- case BPF_S_ALU_MUL_K:
- A *= K;
- continue;
- case BPF_S_ALU_DIV_X:
- if (X == 0)
- return 0;
- A /= X;
- continue;
- case BPF_S_ALU_DIV_K:
- A /= K;
- continue;
- case BPF_S_ALU_MOD_X:
- if (X == 0)
- return 0;
- A %= X;
- continue;
- case BPF_S_ALU_MOD_K:
- A %= K;
- continue;
- case BPF_S_ALU_AND_X:
- A &= X;
- continue;
- case BPF_S_ALU_AND_K:
- A &= K;
- continue;
- case BPF_S_ALU_OR_X:
- A |= X;
- continue;
- case BPF_S_ALU_OR_K:
- A |= K;
- continue;
- case BPF_S_ANC_ALU_XOR_X:
- case BPF_S_ALU_XOR_X:
- A ^= X;
- continue;
- case BPF_S_ALU_XOR_K:
- A ^= K;
- continue;
- case BPF_S_ALU_LSH_X:
- A <<= X;
- continue;
- case BPF_S_ALU_LSH_K:
- A <<= K;
- continue;
- case BPF_S_ALU_RSH_X:
- A >>= X;
- continue;
- case BPF_S_ALU_RSH_K:
- A >>= K;
- continue;
- case BPF_S_ALU_NEG:
- A = -A;
- continue;
- case BPF_S_JMP_JA:
- fentry += K;
- continue;
- case BPF_S_JMP_JGT_K:
- fentry += (A > K) ? fentry->jt : fentry->jf;
- continue;
- case BPF_S_JMP_JGE_K:
- fentry += (A >= K) ? fentry->jt : fentry->jf;
- continue;
- case BPF_S_JMP_JEQ_K:
- fentry += (A == K) ? fentry->jt : fentry->jf;
- continue;
- case BPF_S_JMP_JSET_K:
- fentry += (A & K) ? fentry->jt : fentry->jf;
- continue;
- case BPF_S_JMP_JGT_X:
- fentry += (A > X) ? fentry->jt : fentry->jf;
- continue;
- case BPF_S_JMP_JGE_X:
- fentry += (A >= X) ? fentry->jt : fentry->jf;
- continue;
- case BPF_S_JMP_JEQ_X:
- fentry += (A == X) ? fentry->jt : fentry->jf;
- continue;
- case BPF_S_JMP_JSET_X:
- fentry += (A & X) ? fentry->jt : fentry->jf;
- continue;
- case BPF_S_LD_W_ABS:
- k = K;
-load_w:
- ptr = load_pointer(skb, k, 4, &tmp);
- if (ptr != NULL) {
- A = get_unaligned_be32(ptr);
- continue;
- }
- return 0;
- case BPF_S_LD_H_ABS:
- k = K;
-load_h:
- ptr = load_pointer(skb, k, 2, &tmp);
- if (ptr != NULL) {
- A = get_unaligned_be16(ptr);
- continue;
+#define K insn->imm
+#define A regs[insn->a_reg]
+#define X regs[insn->x_reg]
+#define R0 regs[0]
+
+#define CONT ({insn++; goto select_insn; })
+#define CONT_JMP ({insn++; goto select_insn; })
+
+ static const void *jumptable[256] = {
+ [0 ... 255] = &&default_label,
+ /* Now overwrite non-defaults ... */
+#define DL(A, B, C) [A|B|C] = &&A##_##B##_##C
+ DL(BPF_ALU, BPF_ADD, BPF_X),
+ DL(BPF_ALU, BPF_ADD, BPF_K),
+ DL(BPF_ALU, BPF_SUB, BPF_X),
+ DL(BPF_ALU, BPF_SUB, BPF_K),
+ DL(BPF_ALU, BPF_AND, BPF_X),
+ DL(BPF_ALU, BPF_AND, BPF_K),
+ DL(BPF_ALU, BPF_OR, BPF_X),
+ DL(BPF_ALU, BPF_OR, BPF_K),
+ DL(BPF_ALU, BPF_LSH, BPF_X),
+ DL(BPF_ALU, BPF_LSH, BPF_K),
+ DL(BPF_ALU, BPF_RSH, BPF_X),
+ DL(BPF_ALU, BPF_RSH, BPF_K),
+ DL(BPF_ALU, BPF_XOR, BPF_X),
+ DL(BPF_ALU, BPF_XOR, BPF_K),
+ DL(BPF_ALU, BPF_MUL, BPF_X),
+ DL(BPF_ALU, BPF_MUL, BPF_K),
+ DL(BPF_ALU, BPF_MOV, BPF_X),
+ DL(BPF_ALU, BPF_MOV, BPF_K),
+ DL(BPF_ALU, BPF_DIV, BPF_X),
+ DL(BPF_ALU, BPF_DIV, BPF_K),
+ DL(BPF_ALU, BPF_MOD, BPF_X),
+ DL(BPF_ALU, BPF_MOD, BPF_K),
+ DL(BPF_ALU, BPF_NEG, 0),
+ DL(BPF_ALU, BPF_END, BPF_TO_BE),
+ DL(BPF_ALU, BPF_END, BPF_TO_LE),
+ DL(BPF_ALU64, BPF_ADD, BPF_X),
+ DL(BPF_ALU64, BPF_ADD, BPF_K),
+ DL(BPF_ALU64, BPF_SUB, BPF_X),
+ DL(BPF_ALU64, BPF_SUB, BPF_K),
+ DL(BPF_ALU64, BPF_AND, BPF_X),
+ DL(BPF_ALU64, BPF_AND, BPF_K),
+ DL(BPF_ALU64, BPF_OR, BPF_X),
+ DL(BPF_ALU64, BPF_OR, BPF_K),
+ DL(BPF_ALU64, BPF_LSH, BPF_X),
+ DL(BPF_ALU64, BPF_LSH, BPF_K),
+ DL(BPF_ALU64, BPF_RSH, BPF_X),
+ DL(BPF_ALU64, BPF_RSH, BPF_K),
+ DL(BPF_ALU64, BPF_XOR, BPF_X),
+ DL(BPF_ALU64, BPF_XOR, BPF_K),
+ DL(BPF_ALU64, BPF_MUL, BPF_X),
+ DL(BPF_ALU64, BPF_MUL, BPF_K),
+ DL(BPF_ALU64, BPF_MOV, BPF_X),
+ DL(BPF_ALU64, BPF_MOV, BPF_K),
+ DL(BPF_ALU64, BPF_ARSH, BPF_X),
+ DL(BPF_ALU64, BPF_ARSH, BPF_K),
+ DL(BPF_ALU64, BPF_DIV, BPF_X),
+ DL(BPF_ALU64, BPF_DIV, BPF_K),
+ DL(BPF_ALU64, BPF_MOD, BPF_X),
+ DL(BPF_ALU64, BPF_MOD, BPF_K),
+ DL(BPF_ALU64, BPF_NEG, 0),
+ DL(BPF_JMP, BPF_CALL, 0),
+ DL(BPF_JMP, BPF_JA, 0),
+ DL(BPF_JMP, BPF_JEQ, BPF_X),
+ DL(BPF_JMP, BPF_JEQ, BPF_K),
+ DL(BPF_JMP, BPF_JNE, BPF_X),
+ DL(BPF_JMP, BPF_JNE, BPF_K),
+ DL(BPF_JMP, BPF_JGT, BPF_X),
+ DL(BPF_JMP, BPF_JGT, BPF_K),
+ DL(BPF_JMP, BPF_JGE, BPF_X),
+ DL(BPF_JMP, BPF_JGE, BPF_K),
+ DL(BPF_JMP, BPF_JSGT, BPF_X),
+ DL(BPF_JMP, BPF_JSGT, BPF_K),
+ DL(BPF_JMP, BPF_JSGE, BPF_X),
+ DL(BPF_JMP, BPF_JSGE, BPF_K),
+ DL(BPF_JMP, BPF_JSET, BPF_X),
+ DL(BPF_JMP, BPF_JSET, BPF_K),
+ DL(BPF_JMP, BPF_EXIT, 0),
+ DL(BPF_STX, BPF_MEM, BPF_B),
+ DL(BPF_STX, BPF_MEM, BPF_H),
+ DL(BPF_STX, BPF_MEM, BPF_W),
+ DL(BPF_STX, BPF_MEM, BPF_DW),
+ DL(BPF_STX, BPF_XADD, BPF_W),
+ DL(BPF_STX, BPF_XADD, BPF_DW),
+ DL(BPF_ST, BPF_MEM, BPF_B),
+ DL(BPF_ST, BPF_MEM, BPF_H),
+ DL(BPF_ST, BPF_MEM, BPF_W),
+ DL(BPF_ST, BPF_MEM, BPF_DW),
+ DL(BPF_LDX, BPF_MEM, BPF_B),
+ DL(BPF_LDX, BPF_MEM, BPF_H),
+ DL(BPF_LDX, BPF_MEM, BPF_W),
+ DL(BPF_LDX, BPF_MEM, BPF_DW),
+ DL(BPF_LD, BPF_ABS, BPF_W),
+ DL(BPF_LD, BPF_ABS, BPF_H),
+ DL(BPF_LD, BPF_ABS, BPF_B),
+ DL(BPF_LD, BPF_IND, BPF_W),
+ DL(BPF_LD, BPF_IND, BPF_H),
+ DL(BPF_LD, BPF_IND, BPF_B),
+#undef DL
+ };
+
+ regs[FP_REG] = (u64) (unsigned long) &stack[ARRAY_SIZE(stack)];
+ regs[ARG1_REG] = (u64) (unsigned long) ctx;
+
+select_insn:
+ goto *jumptable[insn->code];
+
+ /* ALU */
+#define ALU(OPCODE, OP) \
+ BPF_ALU64_##OPCODE##_BPF_X: \
+ A = A OP X; \
+ CONT; \
+ BPF_ALU_##OPCODE##_BPF_X: \
+ A = (u32) A OP (u32) X; \
+ CONT; \
+ BPF_ALU64_##OPCODE##_BPF_K: \
+ A = A OP K; \
+ CONT; \
+ BPF_ALU_##OPCODE##_BPF_K: \
+ A = (u32) A OP (u32) K; \
+ CONT;
+
+ ALU(BPF_ADD, +)
+ ALU(BPF_SUB, -)
+ ALU(BPF_AND, &)
+ ALU(BPF_OR, |)
+ ALU(BPF_LSH, <<)
+ ALU(BPF_RSH, >>)
+ ALU(BPF_XOR, ^)
+ ALU(BPF_MUL, *)
+#undef ALU
+ BPF_ALU_BPF_NEG_0:
+ A = (u32) -A;
+ CONT;
+ BPF_ALU64_BPF_NEG_0:
+ A = -A;
+ CONT;
+ BPF_ALU_BPF_MOV_BPF_X:
+ A = (u32) X;
+ CONT;
+ BPF_ALU_BPF_MOV_BPF_K:
+ A = (u32) K;
+ CONT;
+ BPF_ALU64_BPF_MOV_BPF_X:
+ A = X;
+ CONT;
+ BPF_ALU64_BPF_MOV_BPF_K:
+ A = K;
+ CONT;
+ BPF_ALU64_BPF_ARSH_BPF_X:
+ (*(s64 *) &A) >>= X;
+ CONT;
+ BPF_ALU64_BPF_ARSH_BPF_K:
+ (*(s64 *) &A) >>= K;
+ CONT;
+ BPF_ALU64_BPF_MOD_BPF_X:
+ tmp = A;
+ if (X)
+ A = do_div(tmp, X);
+ CONT;
+ BPF_ALU_BPF_MOD_BPF_X:
+ tmp = (u32) A;
+ if (X)
+ A = do_div(tmp, (u32) X);
+ CONT;
+ BPF_ALU64_BPF_MOD_BPF_K:
+ tmp = A;
+ if (K)
+ A = do_div(tmp, K);
+ CONT;
+ BPF_ALU_BPF_MOD_BPF_K:
+ tmp = (u32) A;
+ if (K)
+ A = do_div(tmp, (u32) K);
+ CONT;
+ BPF_ALU64_BPF_DIV_BPF_X:
+ if (X)
+ do_div(A, X);
+ CONT;
+ BPF_ALU_BPF_DIV_BPF_X:
+ tmp = (u32) A;
+ if (X)
+ do_div(tmp, (u32) X);
+ A = (u32) tmp;
+ CONT;
+ BPF_ALU64_BPF_DIV_BPF_K:
+ if (K)
+ do_div(A, K);
+ CONT;
+ BPF_ALU_BPF_DIV_BPF_K:
+ tmp = (u32) A;
+ if (K)
+ do_div(tmp, (u32) K);
+ A = (u32) tmp;
+ CONT;
+ BPF_ALU_BPF_END_BPF_TO_BE:
+ switch (K) {
+ case 16:
+ A = (__force u16) cpu_to_be16(A);
+ break;
+ case 32:
+ A = (__force u32) cpu_to_be32(A);
+ break;
+ case 64:
+ A = (__force u64) cpu_to_be64(A);
+ break;
+ }
+ CONT;
+ BPF_ALU_BPF_END_BPF_TO_LE:
+ switch (K) {
+ case 16:
+ A = (__force u16) cpu_to_le16(A);
+ break;
+ case 32:
+ A = (__force u32) cpu_to_le32(A);
+ break;
+ case 64:
+ A = (__force u64) cpu_to_le64(A);
+ break;
+ }
+ CONT;
+
+ /* CALL */
+ BPF_JMP_BPF_CALL_0:
+ /* Function call scratches R1-R5 registers, preserves R6-R9,
+ * and stores return value into R0.
+ */
+ R0 = (__bpf_call_base + insn->imm)(regs[1], regs[2], regs[3],
+ regs[4], regs[5]);
+ CONT;
+
+ /* JMP */
+ BPF_JMP_BPF_JA_0:
+ insn += insn->off;
+ CONT;
+ BPF_JMP_BPF_JEQ_BPF_X:
+ if (A == X) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JEQ_BPF_K:
+ if (A == K) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JNE_BPF_X:
+ if (A != X) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JNE_BPF_K:
+ if (A != K) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JGT_BPF_X:
+ if (A > X) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JGT_BPF_K:
+ if (A > K) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JGE_BPF_X:
+ if (A >= X) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JGE_BPF_K:
+ if (A >= K) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JSGT_BPF_X:
+ if (((s64)A) > ((s64)X)) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JSGT_BPF_K:
+ if (((s64)A) > ((s64)K)) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JSGE_BPF_X:
+ if (((s64)A) >= ((s64)X)) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JSGE_BPF_K:
+ if (((s64)A) >= ((s64)K)) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JSET_BPF_X:
+ if (A & X) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_JSET_BPF_K:
+ if (A & K) {
+ insn += insn->off;
+ CONT_JMP;
+ }
+ CONT;
+ BPF_JMP_BPF_EXIT_0:
+ return R0;
+
+ /* STX and ST and LDX*/
+#define LDST(SIZEOP, SIZE) \
+ BPF_STX_BPF_MEM_##SIZEOP: \
+ *(SIZE *)(unsigned long) (A + insn->off) = X; \
+ CONT; \
+ BPF_ST_BPF_MEM_##SIZEOP: \
+ *(SIZE *)(unsigned long) (A + insn->off) = K; \
+ CONT; \
+ BPF_LDX_BPF_MEM_##SIZEOP: \
+ A = *(SIZE *)(unsigned long) (X + insn->off); \
+ CONT;
+
+ LDST(BPF_B, u8)
+ LDST(BPF_H, u16)
+ LDST(BPF_W, u32)
+ LDST(BPF_DW, u64)
+#undef LDST
+ BPF_STX_BPF_XADD_BPF_W: /* lock xadd *(u32 *)(A + insn->off) += X */
+ atomic_add((u32) X, (atomic_t *)(unsigned long)
+ (A + insn->off));
+ CONT;
+ BPF_STX_BPF_XADD_BPF_DW: /* lock xadd *(u64 *)(A + insn->off) += X */
+ atomic64_add((u64) X, (atomic64_t *)(unsigned long)
+ (A + insn->off));
+ CONT;
+ BPF_LD_BPF_ABS_BPF_W: /* R0 = ntohl(*(u32 *) (skb->data + K)) */
+ off = K;
+load_word:
+ /* BPF_LD + BPD_ABS and BPF_LD + BPF_IND insns are only
+ * appearing in the programs where ctx == skb. All programs
+ * keep 'ctx' in regs[CTX_REG] == R6, sk_convert_filter()
+ * saves it in R6, internal BPF verifier will check that
+ * R6 == ctx.
+ *
+ * BPF_ABS and BPF_IND are wrappers of function calls, so
+ * they scratch R1-R5 registers, preserve R6-R9, and store
+ * return value into R0.
+ *
+ * Implicit input:
+ * ctx
+ *
+ * Explicit input:
+ * X == any register
+ * K == 32-bit immediate
+ *
+ * Output:
+ * R0 - 8/16/32-bit skb data converted to cpu endianness
+ */
+ ptr = load_pointer((struct sk_buff *) ctx, off, 4, &tmp);
+ if (likely(ptr != NULL)) {
+ R0 = get_unaligned_be32(ptr);
+ CONT;
+ }
+ return 0;
+ BPF_LD_BPF_ABS_BPF_H: /* R0 = ntohs(*(u16 *) (skb->data + K)) */
+ off = K;
+load_half:
+ ptr = load_pointer((struct sk_buff *) ctx, off, 2, &tmp);
+ if (likely(ptr != NULL)) {
+ R0 = get_unaligned_be16(ptr);
+ CONT;
+ }
+ return 0;
+ BPF_LD_BPF_ABS_BPF_B: /* R0 = *(u8 *) (ctx + K) */
+ off = K;
+load_byte:
+ ptr = load_pointer((struct sk_buff *) ctx, off, 1, &tmp);
+ if (likely(ptr != NULL)) {
+ R0 = *(u8 *)ptr;
+ CONT;
+ }
+ return 0;
+ BPF_LD_BPF_IND_BPF_W: /* R0 = ntohl(*(u32 *) (skb->data + X + K)) */
+ off = K + X;
+ goto load_word;
+ BPF_LD_BPF_IND_BPF_H: /* R0 = ntohs(*(u16 *) (skb->data + X + K)) */
+ off = K + X;
+ goto load_half;
+ BPF_LD_BPF_IND_BPF_B: /* R0 = *(u8 *) (skb->data + X + K) */
+ off = K + X;
+ goto load_byte;
+
+ default_label:
+ /* If we ever reach this, we have a bug somewhere. */
+ WARN_RATELIMIT(1, "unknown opcode %02x\n", insn->code);
+ return 0;
+#undef CONT_JMP
+#undef CONT
+
+#undef R0
+#undef X
+#undef A
+#undef K
+}
+
+u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx,
+ const struct sock_filter_int *insni)
+ __attribute__ ((alias ("__sk_run_filter")));
+
+u32 sk_run_filter_int_skb(const struct sk_buff *ctx,
+ const struct sock_filter_int *insni)
+ __attribute__ ((alias ("__sk_run_filter")));
+EXPORT_SYMBOL_GPL(sk_run_filter_int_skb);
+
+/* Helper to find the offset of pkt_type in sk_buff structure. We want
+ * to make sure its still a 3bit field starting at a byte boundary;
+ * taken from arch/x86/net/bpf_jit_comp.c.
+ */
+#define PKT_TYPE_MAX 7
+static unsigned int pkt_type_offset(void)
+{
+ struct sk_buff skb_probe = { .pkt_type = ~0, };
+ u8 *ct = (u8 *) &skb_probe;
+ unsigned int off;
+
+ for (off = 0; off < sizeof(struct sk_buff); off++) {
+ if (ct[off] == PKT_TYPE_MAX)
+ return off;
+ }
+
+ pr_err_once("Please fix %s, as pkt_type couldn't be found!\n", __func__);
+ return -1;
+}
+
+static u64 __skb_get_pay_offset(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+{
+ struct sk_buff *skb = (struct sk_buff *)(long) ctx;
+
+ return __skb_get_poff(skb);
+}
+
+static u64 __skb_get_nlattr(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+{
+ struct sk_buff *skb = (struct sk_buff *)(long) ctx;
+ struct nlattr *nla;
+
+ if (skb_is_nonlinear(skb))
+ return 0;
+
+ if (A > skb->len - sizeof(struct nlattr))
+ return 0;
+
+ nla = nla_find((struct nlattr *) &skb->data[A], skb->len - A, X);
+ if (nla)
+ return (void *) nla - (void *) skb->data;
+
+ return 0;
+}
+
+static u64 __skb_get_nlattr_nest(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+{
+ struct sk_buff *skb = (struct sk_buff *)(long) ctx;
+ struct nlattr *nla;
+
+ if (skb_is_nonlinear(skb))
+ return 0;
+
+ if (A > skb->len - sizeof(struct nlattr))
+ return 0;
+
+ nla = (struct nlattr *) &skb->data[A];
+ if (nla->nla_len > A - skb->len)
+ return 0;
+
+ nla = nla_find_nested(nla, X);
+ if (nla)
+ return (void *) nla - (void *) skb->data;
+
+ return 0;
+}
+
+static u64 __get_raw_cpu_id(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+{
+ return raw_smp_processor_id();
+}
+
+/* Register mappings for user programs. */
+#define A_REG 0
+#define X_REG 7
+#define TMP_REG 8
+#define ARG2_REG 2
+#define ARG3_REG 3
+
+static bool convert_bpf_extensions(struct sock_filter *fp,
+ struct sock_filter_int **insnp)
+{
+ struct sock_filter_int *insn = *insnp;
+
+ switch (fp->k) {
+ case SKF_AD_OFF + SKF_AD_PROTOCOL:
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
+
+ insn->code = BPF_LDX | BPF_MEM | BPF_H;
+ insn->a_reg = A_REG;
+ insn->x_reg = CTX_REG;
+ insn->off = offsetof(struct sk_buff, protocol);
+ insn++;
+
+ /* A = ntohs(A) [emitting a nop or swap16] */
+ insn->code = BPF_ALU | BPF_END | BPF_FROM_BE;
+ insn->a_reg = A_REG;
+ insn->imm = 16;
+ break;
+
+ case SKF_AD_OFF + SKF_AD_PKTTYPE:
+ insn->code = BPF_LDX | BPF_MEM | BPF_B;
+ insn->a_reg = A_REG;
+ insn->x_reg = CTX_REG;
+ insn->off = pkt_type_offset();
+ if (insn->off < 0)
+ return false;
+ insn++;
+
+ insn->code = BPF_ALU | BPF_AND | BPF_K;
+ insn->a_reg = A_REG;
+ insn->imm = PKT_TYPE_MAX;
+ break;
+
+ case SKF_AD_OFF + SKF_AD_IFINDEX:
+ case SKF_AD_OFF + SKF_AD_HATYPE:
+ if (FIELD_SIZEOF(struct sk_buff, dev) == 8)
+ insn->code = BPF_LDX | BPF_MEM | BPF_DW;
+ else
+ insn->code = BPF_LDX | BPF_MEM | BPF_W;
+ insn->a_reg = TMP_REG;
+ insn->x_reg = CTX_REG;
+ insn->off = offsetof(struct sk_buff, dev);
+ insn++;
+
+ insn->code = BPF_JMP | BPF_JNE | BPF_K;
+ insn->a_reg = TMP_REG;
+ insn->imm = 0;
+ insn->off = 1;
+ insn++;
+
+ insn->code = BPF_JMP | BPF_EXIT;
+ insn++;
+
+ BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
+ BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2);
+
+ insn->a_reg = A_REG;
+ insn->x_reg = TMP_REG;
+
+ if (fp->k == SKF_AD_OFF + SKF_AD_IFINDEX) {
+ insn->code = BPF_LDX | BPF_MEM | BPF_W;
+ insn->off = offsetof(struct net_device, ifindex);
+ } else {
+ insn->code = BPF_LDX | BPF_MEM | BPF_H;
+ insn->off = offsetof(struct net_device, type);
+ }
+ break;
+
+ case SKF_AD_OFF + SKF_AD_MARK:
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
+
+ insn->code = BPF_LDX | BPF_MEM | BPF_W;
+ insn->a_reg = A_REG;
+ insn->x_reg = CTX_REG;
+ insn->off = offsetof(struct sk_buff, mark);
+ break;
+
+ case SKF_AD_OFF + SKF_AD_RXHASH:
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
+
+ insn->code = BPF_LDX | BPF_MEM | BPF_W;
+ insn->a_reg = A_REG;
+ insn->x_reg = CTX_REG;
+ insn->off = offsetof(struct sk_buff, hash);
+ break;
+
+ case SKF_AD_OFF + SKF_AD_QUEUE:
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2);
+
+ insn->code = BPF_LDX | BPF_MEM | BPF_H;
+ insn->a_reg = A_REG;
+ insn->x_reg = CTX_REG;
+ insn->off = offsetof(struct sk_buff, queue_mapping);
+ break;
+
+ case SKF_AD_OFF + SKF_AD_VLAN_TAG:
+ case SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT:
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
+
+ insn->code = BPF_LDX | BPF_MEM | BPF_H;
+ insn->a_reg = A_REG;
+ insn->x_reg = CTX_REG;
+ insn->off = offsetof(struct sk_buff, vlan_tci);
+ insn++;
+
+ BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
+
+ if (fp->k == SKF_AD_OFF + SKF_AD_VLAN_TAG) {
+ insn->code = BPF_ALU | BPF_AND | BPF_K;
+ insn->a_reg = A_REG;
+ insn->imm = ~VLAN_TAG_PRESENT;
+ } else {
+ insn->code = BPF_ALU | BPF_RSH | BPF_K;
+ insn->a_reg = A_REG;
+ insn->imm = 12;
+ insn++;
+
+ insn->code = BPF_ALU | BPF_AND | BPF_K;
+ insn->a_reg = A_REG;
+ insn->imm = 1;
+ }
+ break;
+
+ case SKF_AD_OFF + SKF_AD_PAY_OFFSET:
+ case SKF_AD_OFF + SKF_AD_NLATTR:
+ case SKF_AD_OFF + SKF_AD_NLATTR_NEST:
+ case SKF_AD_OFF + SKF_AD_CPU:
+ /* arg1 = ctx */
+ insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
+ insn->a_reg = ARG1_REG;
+ insn->x_reg = CTX_REG;
+ insn++;
+
+ /* arg2 = A */
+ insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
+ insn->a_reg = ARG2_REG;
+ insn->x_reg = A_REG;
+ insn++;
+
+ /* arg3 = X */
+ insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
+ insn->a_reg = ARG3_REG;
+ insn->x_reg = X_REG;
+ insn++;
+
+ /* Emit call(ctx, arg2=A, arg3=X) */
+ insn->code = BPF_JMP | BPF_CALL;
+ switch (fp->k) {
+ case SKF_AD_OFF + SKF_AD_PAY_OFFSET:
+ insn->imm = __skb_get_pay_offset - __bpf_call_base;
+ break;
+ case SKF_AD_OFF + SKF_AD_NLATTR:
+ insn->imm = __skb_get_nlattr - __bpf_call_base;
+ break;
+ case SKF_AD_OFF + SKF_AD_NLATTR_NEST:
+ insn->imm = __skb_get_nlattr_nest - __bpf_call_base;
+ break;
+ case SKF_AD_OFF + SKF_AD_CPU:
+ insn->imm = __get_raw_cpu_id - __bpf_call_base;
+ break;
+ }
+ break;
+
+ case SKF_AD_OFF + SKF_AD_ALU_XOR_X:
+ insn->code = BPF_ALU | BPF_XOR | BPF_X;
+ insn->a_reg = A_REG;
+ insn->x_reg = X_REG;
+ break;
+
+ default:
+ /* This is just a dummy call to avoid letting the compiler
+ * evict __bpf_call_base() as an optimization. Placed here
+ * where no-one bothers.
+ */
+ BUG_ON(__bpf_call_base(0, 0, 0, 0, 0) != 0);
+ return false;
+ }
+
+ *insnp = insn;
+ return true;
+}
+
+/**
+ * sk_convert_filter - convert filter program
+ * @prog: the user passed filter program
+ * @len: the length of the user passed filter program
+ * @new_prog: buffer where converted program will be stored
+ * @new_len: pointer to store length of converted program
+ *
+ * Remap 'sock_filter' style BPF instruction set to 'sock_filter_ext' style.
+ * Conversion workflow:
+ *
+ * 1) First pass for calculating the new program length:
+ * sk_convert_filter(old_prog, old_len, NULL, &new_len)
+ *
+ * 2) 2nd pass to remap in two passes: 1st pass finds new
+ * jump offsets, 2nd pass remapping:
+ * new_prog = kmalloc(sizeof(struct sock_filter_int) * new_len);
+ * sk_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 *'.
+ */
+int sk_convert_filter(struct sock_filter *prog, int len,
+ struct sock_filter_int *new_prog, int *new_len)
+{
+ int new_flen = 0, pass = 0, target, i;
+ struct sock_filter_int *new_insn;
+ struct sock_filter *fp;
+ int *addrs = NULL;
+ u8 bpf_src;
+
+ BUILD_BUG_ON(BPF_MEMWORDS * sizeof(u32) > MAX_BPF_STACK);
+ BUILD_BUG_ON(FP_REG + 1 != MAX_BPF_REG);
+
+ if (len <= 0 || len >= BPF_MAXINSNS)
+ return -EINVAL;
+
+ if (new_prog) {
+ addrs = kzalloc(len * sizeof(*addrs), GFP_KERNEL);
+ if (!addrs)
+ return -ENOMEM;
+ }
+
+do_pass:
+ new_insn = new_prog;
+ fp = prog;
+
+ if (new_insn) {
+ new_insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
+ new_insn->a_reg = CTX_REG;
+ new_insn->x_reg = ARG1_REG;
+ }
+ new_insn++;
+
+ for (i = 0; i < len; fp++, i++) {
+ struct sock_filter_int tmp_insns[6] = { };
+ struct sock_filter_int *insn = tmp_insns;
+
+ if (addrs)
+ addrs[i] = new_insn - new_prog;
+
+ switch (fp->code) {
+ /* All arithmetic insns and skb loads map as-is. */
+ case BPF_ALU | BPF_ADD | BPF_X:
+ case BPF_ALU | BPF_ADD | BPF_K:
+ case BPF_ALU | BPF_SUB | BPF_X:
+ case BPF_ALU | BPF_SUB | BPF_K:
+ case BPF_ALU | BPF_AND | BPF_X:
+ case BPF_ALU | BPF_AND | BPF_K:
+ case BPF_ALU | BPF_OR | BPF_X:
+ case BPF_ALU | BPF_OR | BPF_K:
+ case BPF_ALU | BPF_LSH | BPF_X:
+ case BPF_ALU | BPF_LSH | BPF_K:
+ case BPF_ALU | BPF_RSH | BPF_X:
+ case BPF_ALU | BPF_RSH | BPF_K:
+ case BPF_ALU | BPF_XOR | BPF_X:
+ case BPF_ALU | BPF_XOR | BPF_K:
+ case BPF_ALU | BPF_MUL | BPF_X:
+ case BPF_ALU | BPF_MUL | BPF_K:
+ case BPF_ALU | BPF_DIV | BPF_X:
+ case BPF_ALU | BPF_DIV | BPF_K:
+ case BPF_ALU | BPF_MOD | BPF_X:
+ case BPF_ALU | BPF_MOD | BPF_K:
+ case BPF_ALU | BPF_NEG:
+ case BPF_LD | BPF_ABS | BPF_W:
+ case BPF_LD | BPF_ABS | BPF_H:
+ case BPF_LD | BPF_ABS | BPF_B:
+ case BPF_LD | BPF_IND | BPF_W:
+ case BPF_LD | BPF_IND | BPF_H:
+ case BPF_LD | BPF_IND | BPF_B:
+ /* Check for overloaded BPF extension and
+ * directly convert it if found, otherwise
+ * just move on with mapping.
+ */
+ if (BPF_CLASS(fp->code) == BPF_LD &&
+ BPF_MODE(fp->code) == BPF_ABS &&
+ convert_bpf_extensions(fp, &insn))
+ break;
+
+ insn->code = fp->code;
+ insn->a_reg = A_REG;
+ insn->x_reg = X_REG;
+ insn->imm = fp->k;
+ break;
+
+ /* Jump opcodes map as-is, but offsets need adjustment. */
+ case BPF_JMP | BPF_JA:
+ target = i + fp->k + 1;
+ insn->code = fp->code;
+#define EMIT_JMP \
+ do { \
+ if (target >= len || target < 0) \
+ goto err; \
+ insn->off = addrs ? addrs[target] - addrs[i] - 1 : 0; \
+ /* Adjust pc relative offset for 2nd or 3rd insn. */ \
+ insn->off -= insn - tmp_insns; \
+ } while (0)
+
+ EMIT_JMP;
+ break;
+
+ case BPF_JMP | BPF_JEQ | BPF_K:
+ case BPF_JMP | BPF_JEQ | BPF_X:
+ case BPF_JMP | BPF_JSET | BPF_K:
+ case BPF_JMP | BPF_JSET | BPF_X:
+ case BPF_JMP | BPF_JGT | BPF_K:
+ case BPF_JMP | BPF_JGT | BPF_X:
+ case BPF_JMP | BPF_JGE | BPF_K:
+ case BPF_JMP | BPF_JGE | BPF_X:
+ if (BPF_SRC(fp->code) == BPF_K && (int) fp->k < 0) {
+ /* BPF immediates are signed, zero extend
+ * immediate into tmp register and use it
+ * in compare insn.
+ */
+ insn->code = BPF_ALU | BPF_MOV | BPF_K;
+ insn->a_reg = TMP_REG;
+ insn->imm = fp->k;
+ insn++;
+
+ insn->a_reg = A_REG;
+ insn->x_reg = TMP_REG;
+ bpf_src = BPF_X;
+ } else {
+ insn->a_reg = A_REG;
+ insn->x_reg = X_REG;
+ insn->imm = fp->k;
+ bpf_src = BPF_SRC(fp->code);
}
- return 0;
- case BPF_S_LD_B_ABS:
- k = K;
-load_b:
- ptr = load_pointer(skb, k, 1, &tmp);
- if (ptr != NULL) {
- A = *(u8 *)ptr;
- continue;
+
+ /* Common case where 'jump_false' is next insn. */
+ if (fp->jf == 0) {
+ insn->code = BPF_JMP | BPF_OP(fp->code) | bpf_src;
+ target = i + fp->jt + 1;
+ EMIT_JMP;
+ break;
}
- return 0;
- case BPF_S_LD_W_LEN:
- A = skb->len;
- continue;
- case BPF_S_LDX_W_LEN:
- X = skb->len;
- continue;
- case BPF_S_LD_W_IND:
- k = X + K;
- goto load_w;
- case BPF_S_LD_H_IND:
- k = X + K;
- goto load_h;
- case BPF_S_LD_B_IND:
- k = X + K;
- goto load_b;
- case BPF_S_LDX_B_MSH:
- ptr = load_pointer(skb, K, 1, &tmp);
- if (ptr != NULL) {
- X = (*(u8 *)ptr & 0xf) << 2;
- continue;
+
+ /* Convert JEQ into JNE when 'jump_true' is next insn. */
+ if (fp->jt == 0 && BPF_OP(fp->code) == BPF_JEQ) {
+ insn->code = BPF_JMP | BPF_JNE | bpf_src;
+ target = i + fp->jf + 1;
+ EMIT_JMP;
+ break;
}
- return 0;
- case BPF_S_LD_IMM:
- A = K;
- continue;
- case BPF_S_LDX_IMM:
- X = K;
- continue;
- case BPF_S_LD_MEM:
- A = mem[K];
- continue;
- case BPF_S_LDX_MEM:
- X = mem[K];
- continue;
- case BPF_S_MISC_TAX:
- X = A;
- continue;
- case BPF_S_MISC_TXA:
- A = X;
- continue;
- case BPF_S_RET_K:
- return K;
- case BPF_S_RET_A:
- return A;
- case BPF_S_ST:
- mem[K] = A;
- continue;
- case BPF_S_STX:
- mem[K] = X;
- continue;
- case BPF_S_ANC_PROTOCOL:
- A = ntohs(skb->protocol);
- continue;
- case BPF_S_ANC_PKTTYPE:
- A = skb->pkt_type;
- continue;
- case BPF_S_ANC_IFINDEX:
- if (!skb->dev)
- return 0;
- A = skb->dev->ifindex;
- continue;
- case BPF_S_ANC_MARK:
- A = skb->mark;
- continue;
- case BPF_S_ANC_QUEUE:
- A = skb->queue_mapping;
- continue;
- case BPF_S_ANC_HATYPE:
- if (!skb->dev)
- return 0;
- A = skb->dev->type;
- continue;
- case BPF_S_ANC_RXHASH:
- A = skb->rxhash;
- continue;
- case BPF_S_ANC_CPU:
- A = raw_smp_processor_id();
- continue;
- case BPF_S_ANC_VLAN_TAG:
- A = vlan_tx_tag_get(skb);
- continue;
- case BPF_S_ANC_VLAN_TAG_PRESENT:
- A = !!vlan_tx_tag_present(skb);
- continue;
- case BPF_S_ANC_PAY_OFFSET:
- A = __skb_get_poff(skb);
- continue;
- case BPF_S_ANC_NLATTR: {
- struct nlattr *nla;
-
- if (skb_is_nonlinear(skb))
- return 0;
- if (A > skb->len - sizeof(struct nlattr))
- return 0;
-
- nla = nla_find((struct nlattr *)&skb->data[A],
- skb->len - A, X);
- if (nla)
- A = (void *)nla - (void *)skb->data;
- else
- A = 0;
- continue;
- }
- case BPF_S_ANC_NLATTR_NEST: {
- struct nlattr *nla;
-
- if (skb_is_nonlinear(skb))
- return 0;
- if (A > skb->len - sizeof(struct nlattr))
- return 0;
-
- nla = (struct nlattr *)&skb->data[A];
- if (nla->nla_len > A - skb->len)
- return 0;
-
- nla = nla_find_nested(nla, X);
- if (nla)
- A = (void *)nla - (void *)skb->data;
- else
- A = 0;
- continue;
- }
-#ifdef CONFIG_SECCOMP_FILTER
- case BPF_S_ANC_SECCOMP_LD_W:
- A = seccomp_bpf_load(fentry->k);
- continue;
-#endif
+
+ /* Other jumps are mapped into two insns: Jxx and JA. */
+ target = i + fp->jt + 1;
+ insn->code = BPF_JMP | BPF_OP(fp->code) | bpf_src;
+ EMIT_JMP;
+ insn++;
+
+ insn->code = BPF_JMP | BPF_JA;
+ target = i + fp->jf + 1;
+ EMIT_JMP;
+ break;
+
+ /* ldxb 4 * ([14] & 0xf) is remaped into 6 insns. */
+ case BPF_LDX | BPF_MSH | BPF_B:
+ insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
+ insn->a_reg = TMP_REG;
+ insn->x_reg = A_REG;
+ insn++;
+
+ insn->code = BPF_LD | BPF_ABS | BPF_B;
+ insn->a_reg = A_REG;
+ insn->imm = fp->k;
+ insn++;
+
+ insn->code = BPF_ALU | BPF_AND | BPF_K;
+ insn->a_reg = A_REG;
+ insn->imm = 0xf;
+ insn++;
+
+ insn->code = BPF_ALU | BPF_LSH | BPF_K;
+ insn->a_reg = A_REG;
+ insn->imm = 2;
+ insn++;
+
+ insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
+ insn->a_reg = X_REG;
+ insn->x_reg = A_REG;
+ insn++;
+
+ insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
+ insn->a_reg = A_REG;
+ insn->x_reg = TMP_REG;
+ break;
+
+ /* RET_K, RET_A are remaped into 2 insns. */
+ case BPF_RET | BPF_A:
+ case BPF_RET | BPF_K:
+ insn->code = BPF_ALU | BPF_MOV |
+ (BPF_RVAL(fp->code) == BPF_K ?
+ BPF_K : BPF_X);
+ insn->a_reg = 0;
+ insn->x_reg = A_REG;
+ insn->imm = fp->k;
+ insn++;
+
+ insn->code = BPF_JMP | BPF_EXIT;
+ break;
+
+ /* Store to stack. */
+ case BPF_ST:
+ case BPF_STX:
+ insn->code = BPF_STX | BPF_MEM | BPF_W;
+ insn->a_reg = FP_REG;
+ insn->x_reg = fp->code == BPF_ST ? A_REG : X_REG;
+ insn->off = -(BPF_MEMWORDS - fp->k) * 4;
+ break;
+
+ /* Load from stack. */
+ case BPF_LD | BPF_MEM:
+ case BPF_LDX | BPF_MEM:
+ insn->code = BPF_LDX | BPF_MEM | BPF_W;
+ insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ?
+ A_REG : X_REG;
+ insn->x_reg = FP_REG;
+ insn->off = -(BPF_MEMWORDS - fp->k) * 4;
+ break;
+
+ /* A = K or X = K */
+ case BPF_LD | BPF_IMM:
+ case BPF_LDX | BPF_IMM:
+ insn->code = BPF_ALU | BPF_MOV | BPF_K;
+ insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ?
+ A_REG : X_REG;
+ insn->imm = fp->k;
+ break;
+
+ /* X = A */
+ case BPF_MISC | BPF_TAX:
+ insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
+ insn->a_reg = X_REG;
+ insn->x_reg = A_REG;
+ break;
+
+ /* A = X */
+ case BPF_MISC | BPF_TXA:
+ insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
+ insn->a_reg = A_REG;
+ insn->x_reg = X_REG;
+ break;
+
+ /* A = skb->len or X = skb->len */
+ case BPF_LD | BPF_W | BPF_LEN:
+ case BPF_LDX | BPF_W | BPF_LEN:
+ insn->code = BPF_LDX | BPF_MEM | BPF_W;
+ insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ?
+ A_REG : X_REG;
+ insn->x_reg = CTX_REG;
+ insn->off = offsetof(struct sk_buff, len);
+ break;
+
+ /* access seccomp_data fields */
+ case BPF_LDX | BPF_ABS | BPF_W:
+ insn->code = BPF_LDX | BPF_MEM | BPF_W;
+ insn->a_reg = A_REG;
+ insn->x_reg = CTX_REG;
+ insn->off = fp->k;
+ break;
+
default:
- WARN_RATELIMIT(1, "Unknown code:%u jt:%u tf:%u k:%u\n",
- fentry->code, fentry->jt,
- fentry->jf, fentry->k);
- return 0;
+ goto err;
}
+
+ insn++;
+ if (new_prog)
+ memcpy(new_insn, tmp_insns,
+ sizeof(*insn) * (insn - tmp_insns));
+
+ new_insn += insn - tmp_insns;
+ }
+
+ if (!new_prog) {
+ /* Only calculating new length. */
+ *new_len = new_insn - new_prog;
+ return 0;
}
+ pass++;
+ if (new_flen != new_insn - new_prog) {
+ new_flen = new_insn - new_prog;
+ if (pass > 2)
+ goto err;
+
+ goto do_pass;
+ }
+
+ kfree(addrs);
+ BUG_ON(*new_len != new_flen);
return 0;
+err:
+ kfree(addrs);
+ return -EINVAL;
}
-EXPORT_SYMBOL(sk_run_filter);
-/*
- * Security :
+/* Security:
+ *
* A BPF program is able to use 16 cells of memory to store intermediate
- * values (check u32 mem[BPF_MEMWORDS] in sk_run_filter())
+ * values (check u32 mem[BPF_MEMWORDS] in sk_run_filter()).
+ *
* As we dont want to clear mem[] array for each packet going through
* sk_run_filter(), we check that filter loaded by user never try to read
* a cell if not previously written, and we check all branches to be sure
@@ -629,30 +1375,197 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
}
EXPORT_SYMBOL(sk_chk_filter);
+static int sk_store_orig_filter(struct sk_filter *fp,
+ const struct sock_fprog *fprog)
+{
+ unsigned int fsize = sk_filter_proglen(fprog);
+ struct sock_fprog_kern *fkprog;
+
+ fp->orig_prog = kmalloc(sizeof(*fkprog), GFP_KERNEL);
+ if (!fp->orig_prog)
+ return -ENOMEM;
+
+ fkprog = fp->orig_prog;
+ fkprog->len = fprog->len;
+ fkprog->filter = kmemdup(fp->insns, fsize, GFP_KERNEL);
+ if (!fkprog->filter) {
+ kfree(fp->orig_prog);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void sk_release_orig_filter(struct sk_filter *fp)
+{
+ struct sock_fprog_kern *fprog = fp->orig_prog;
+
+ if (fprog) {
+ kfree(fprog->filter);
+ kfree(fprog);
+ }
+}
+
/**
* sk_filter_release_rcu - Release a socket filter by rcu_head
* @rcu: rcu_head that contains the sk_filter to free
*/
-void sk_filter_release_rcu(struct rcu_head *rcu)
+static void sk_filter_release_rcu(struct rcu_head *rcu)
{
struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu);
+ sk_release_orig_filter(fp);
bpf_jit_free(fp);
}
-EXPORT_SYMBOL(sk_filter_release_rcu);
-static int __sk_prepare_filter(struct sk_filter *fp)
+/**
+ * sk_filter_release - release a socket filter
+ * @fp: filter to remove
+ *
+ * Remove a filter from a socket and release its resources.
+ */
+static void sk_filter_release(struct sk_filter *fp)
+{
+ if (atomic_dec_and_test(&fp->refcnt))
+ call_rcu(&fp->rcu, sk_filter_release_rcu);
+}
+
+void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp)
+{
+ atomic_sub(sk_filter_size(fp->len), &sk->sk_omem_alloc);
+ sk_filter_release(fp);
+}
+
+void sk_filter_charge(struct sock *sk, struct sk_filter *fp)
+{
+ atomic_inc(&fp->refcnt);
+ atomic_add(sk_filter_size(fp->len), &sk->sk_omem_alloc);
+}
+
+static struct sk_filter *__sk_migrate_realloc(struct sk_filter *fp,
+ struct sock *sk,
+ unsigned int len)
+{
+ struct sk_filter *fp_new;
+
+ if (sk == NULL)
+ return krealloc(fp, len, GFP_KERNEL);
+
+ fp_new = sock_kmalloc(sk, len, GFP_KERNEL);
+ if (fp_new) {
+ memcpy(fp_new, fp, sizeof(struct sk_filter));
+ /* As we're kepping orig_prog in fp_new along,
+ * we need to make sure we're not evicting it
+ * from the old fp.
+ */
+ fp->orig_prog = NULL;
+ sk_filter_uncharge(sk, fp);
+ }
+
+ return fp_new;
+}
+
+static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
+ struct sock *sk)
+{
+ struct sock_filter *old_prog;
+ struct sk_filter *old_fp;
+ int i, err, new_len, old_len = fp->len;
+
+ /* We are free to overwrite insns et al right here as it
+ * won't be used at this point in time anymore internally
+ * after the migration to the internal BPF instruction
+ * representation.
+ */
+ BUILD_BUG_ON(sizeof(struct sock_filter) !=
+ sizeof(struct sock_filter_int));
+
+ /* For now, we need to unfiddle BPF_S_* identifiers in place.
+ * This can sooner or later on be subject to removal, e.g. when
+ * JITs have been converted.
+ */
+ for (i = 0; i < fp->len; i++)
+ sk_decode_filter(&fp->insns[i], &fp->insns[i]);
+
+ /* Conversion cannot happen on overlapping memory areas,
+ * so we need to keep the user BPF around until the 2nd
+ * pass. At this time, the user BPF is stored in fp->insns.
+ */
+ old_prog = kmemdup(fp->insns, old_len * sizeof(struct sock_filter),
+ GFP_KERNEL);
+ if (!old_prog) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ /* 1st pass: calculate the new program length. */
+ err = sk_convert_filter(old_prog, old_len, NULL, &new_len);
+ if (err)
+ goto out_err_free;
+
+ /* Expand fp for appending the new filter representation. */
+ old_fp = fp;
+ fp = __sk_migrate_realloc(old_fp, sk, sk_filter_size(new_len));
+ if (!fp) {
+ /* The old_fp is still around in case we couldn't
+ * allocate new memory, so uncharge on that one.
+ */
+ fp = old_fp;
+ err = -ENOMEM;
+ goto out_err_free;
+ }
+
+ fp->bpf_func = sk_run_filter_int_skb;
+ fp->len = new_len;
+
+ /* 2nd pass: remap sock_filter insns into sock_filter_int insns. */
+ err = sk_convert_filter(old_prog, old_len, fp->insnsi, &new_len);
+ if (err)
+ /* 2nd sk_convert_filter() can fail only if it fails
+ * to allocate memory, remapping must succeed. Note,
+ * that at this time old_fp has already been released
+ * by __sk_migrate_realloc().
+ */
+ goto out_err_free;
+
+ kfree(old_prog);
+ return fp;
+
+out_err_free:
+ kfree(old_prog);
+out_err:
+ /* Rollback filter setup. */
+ if (sk != NULL)
+ sk_filter_uncharge(sk, fp);
+ else
+ kfree(fp);
+ return ERR_PTR(err);
+}
+
+static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp,
+ struct sock *sk)
{
int err;
- fp->bpf_func = sk_run_filter;
+ fp->bpf_func = NULL;
+ fp->jited = 0;
err = sk_chk_filter(fp->insns, fp->len);
if (err)
- return err;
+ return ERR_PTR(err);
+ /* Probe if we can JIT compile the filter and if so, do
+ * the compilation of the filter.
+ */
bpf_jit_compile(fp);
- return 0;
+
+ /* JIT compiler couldn't process this filter, so do the
+ * internal BPF translation for the optimized interpreter.
+ */
+ if (!fp->jited)
+ fp = __sk_migrate_filter(fp, sk);
+
+ return fp;
}
/**
@@ -668,9 +1581,8 @@ static int __sk_prepare_filter(struct sk_filter *fp)
int sk_unattached_filter_create(struct sk_filter **pfp,
struct sock_fprog *fprog)
{
+ unsigned int fsize = sk_filter_proglen(fprog);
struct sk_filter *fp;
- unsigned int fsize = sizeof(struct sock_filter) * fprog->len;
- int err;
/* Make sure new filter is there and in the right amounts. */
if (fprog->filter == NULL)
@@ -679,20 +1591,26 @@ int sk_unattached_filter_create(struct sk_filter **pfp,
fp = kmalloc(sk_filter_size(fprog->len), GFP_KERNEL);
if (!fp)
return -ENOMEM;
+
memcpy(fp->insns, fprog->filter, fsize);
atomic_set(&fp->refcnt, 1);
fp->len = fprog->len;
+ /* Since unattached filters are not copied back to user
+ * space through sk_get_filter(), we do not need to hold
+ * a copy here, and can spare us the work.
+ */
+ fp->orig_prog = NULL;
- err = __sk_prepare_filter(fp);
- if (err)
- goto free_mem;
+ /* __sk_prepare_filter() already takes care of uncharging
+ * memory in case something goes wrong.
+ */
+ fp = __sk_prepare_filter(fp, NULL);
+ if (IS_ERR(fp))
+ return PTR_ERR(fp);
*pfp = fp;
return 0;
-free_mem:
- kfree(fp);
- return err;
}
EXPORT_SYMBOL_GPL(sk_unattached_filter_create);
@@ -715,7 +1633,7 @@ EXPORT_SYMBOL_GPL(sk_unattached_filter_destroy);
int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
{
struct sk_filter *fp, *old_fp;
- unsigned int fsize = sizeof(struct sock_filter) * fprog->len;
+ unsigned int fsize = sk_filter_proglen(fprog);
unsigned int sk_fsize = sk_filter_size(fprog->len);
int err;
@@ -729,6 +1647,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
fp = sock_kmalloc(sk, sk_fsize, GFP_KERNEL);
if (!fp)
return -ENOMEM;
+
if (copy_from_user(fp->insns, fprog->filter, fsize)) {
sock_kfree_s(sk, fp, sk_fsize);
return -EFAULT;
@@ -737,18 +1656,26 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
atomic_set(&fp->refcnt, 1);
fp->len = fprog->len;
- err = __sk_prepare_filter(fp);
+ err = sk_store_orig_filter(fp, fprog);
if (err) {
sk_filter_uncharge(sk, fp);
- return err;
+ return -ENOMEM;
}
+ /* __sk_prepare_filter() already takes care of uncharging
+ * memory in case something goes wrong.
+ */
+ fp = __sk_prepare_filter(fp, sk);
+ if (IS_ERR(fp))
+ return PTR_ERR(fp);
+
old_fp = rcu_dereference_protected(sk->sk_filter,
sock_owned_by_user(sk));
rcu_assign_pointer(sk->sk_filter, fp);
if (old_fp)
sk_filter_uncharge(sk, old_fp);
+
return 0;
}
EXPORT_SYMBOL_GPL(sk_attach_filter);
@@ -768,6 +1695,7 @@ int sk_detach_filter(struct sock *sk)
sk_filter_uncharge(sk, filter);
ret = 0;
}
+
return ret;
}
EXPORT_SYMBOL_GPL(sk_detach_filter);
@@ -850,34 +1778,41 @@ void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to)
to->k = filt->k;
}
-int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, unsigned int len)
+int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf,
+ unsigned int len)
{
+ struct sock_fprog_kern *fprog;
struct sk_filter *filter;
- int i, ret;
+ int ret = 0;
lock_sock(sk);
filter = rcu_dereference_protected(sk->sk_filter,
- sock_owned_by_user(sk));
- ret = 0;
+ sock_owned_by_user(sk));
if (!filter)
goto out;
- ret = filter->len;
+
+ /* We're copying the filter that has been originally attached,
+ * so no conversion/decode needed anymore.
+ */
+ fprog = filter->orig_prog;
+
+ ret = fprog->len;
if (!len)
+ /* User space only enquires number of filter blocks. */
goto out;
+
ret = -EINVAL;
- if (len < filter->len)
+ if (len < fprog->len)
goto out;
ret = -EFAULT;
- for (i = 0; i < filter->len; i++) {
- struct sock_filter fb;
-
- sk_decode_filter(&filter->insns[i], &fb);
- if (copy_to_user(&ubuf[i], &fb, sizeof(fb)))
- goto out;
- }
+ if (copy_to_user(ubuf, fprog->filter, sk_filter_proglen(fprog)))
+ goto out;
- ret = filter->len;
+ /* Instead of bytes, the API requests to return the number
+ * of filter blocks.
+ */
+ ret = fprog->len;
out:
release_sock(sk);
return ret;
diff --git a/net/core/flow.c b/net/core/flow.c
index dfa602ceb8cd..a0348fde1fdf 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -24,6 +24,7 @@
#include <net/flow.h>
#include <linux/atomic.h>
#include <linux/security.h>
+#include <net/net_namespace.h>
struct flow_cache_entry {
union {
@@ -38,37 +39,14 @@ struct flow_cache_entry {
struct flow_cache_object *object;
};
-struct flow_cache_percpu {
- struct hlist_head *hash_table;
- int hash_count;
- u32 hash_rnd;
- int hash_rnd_recalc;
- struct tasklet_struct flush_tasklet;
-};
-
struct flow_flush_info {
struct flow_cache *cache;
atomic_t cpuleft;
struct completion completion;
};
-struct flow_cache {
- u32 hash_shift;
- struct flow_cache_percpu __percpu *percpu;
- struct notifier_block hotcpu_notifier;
- int low_watermark;
- int high_watermark;
- struct timer_list rnd_timer;
-};
-
-atomic_t flow_cache_genid = ATOMIC_INIT(0);
-EXPORT_SYMBOL(flow_cache_genid);
-static struct flow_cache flow_cache_global;
static struct kmem_cache *flow_cachep __read_mostly;
-static DEFINE_SPINLOCK(flow_cache_gc_lock);
-static LIST_HEAD(flow_cache_gc_list);
-
#define flow_cache_hash_size(cache) (1 << (cache)->hash_shift)
#define FLOW_HASH_RND_PERIOD (10 * 60 * HZ)
@@ -84,16 +62,18 @@ static void flow_cache_new_hashrnd(unsigned long arg)
add_timer(&fc->rnd_timer);
}
-static int flow_entry_valid(struct flow_cache_entry *fle)
+static int flow_entry_valid(struct flow_cache_entry *fle,
+ struct netns_xfrm *xfrm)
{
- if (atomic_read(&flow_cache_genid) != fle->genid)
+ if (atomic_read(&xfrm->flow_cache_genid) != fle->genid)
return 0;
if (fle->object && !fle->object->ops->check(fle->object))
return 0;
return 1;
}
-static void flow_entry_kill(struct flow_cache_entry *fle)
+static void flow_entry_kill(struct flow_cache_entry *fle,
+ struct netns_xfrm *xfrm)
{
if (fle->object)
fle->object->ops->delete(fle->object);
@@ -104,26 +84,28 @@ static void flow_cache_gc_task(struct work_struct *work)
{
struct list_head gc_list;
struct flow_cache_entry *fce, *n;
+ struct netns_xfrm *xfrm = container_of(work, struct netns_xfrm,
+ flow_cache_gc_work);
INIT_LIST_HEAD(&gc_list);
- spin_lock_bh(&flow_cache_gc_lock);
- list_splice_tail_init(&flow_cache_gc_list, &gc_list);
- spin_unlock_bh(&flow_cache_gc_lock);
+ spin_lock_bh(&xfrm->flow_cache_gc_lock);
+ list_splice_tail_init(&xfrm->flow_cache_gc_list, &gc_list);
+ spin_unlock_bh(&xfrm->flow_cache_gc_lock);
list_for_each_entry_safe(fce, n, &gc_list, u.gc_list)
- flow_entry_kill(fce);
+ flow_entry_kill(fce, xfrm);
}
-static DECLARE_WORK(flow_cache_gc_work, flow_cache_gc_task);
static void flow_cache_queue_garbage(struct flow_cache_percpu *fcp,
- int deleted, struct list_head *gc_list)
+ int deleted, struct list_head *gc_list,
+ struct netns_xfrm *xfrm)
{
if (deleted) {
fcp->hash_count -= deleted;
- spin_lock_bh(&flow_cache_gc_lock);
- list_splice_tail(gc_list, &flow_cache_gc_list);
- spin_unlock_bh(&flow_cache_gc_lock);
- schedule_work(&flow_cache_gc_work);
+ spin_lock_bh(&xfrm->flow_cache_gc_lock);
+ list_splice_tail(gc_list, &xfrm->flow_cache_gc_list);
+ spin_unlock_bh(&xfrm->flow_cache_gc_lock);
+ schedule_work(&xfrm->flow_cache_gc_work);
}
}
@@ -135,6 +117,8 @@ static void __flow_cache_shrink(struct flow_cache *fc,
struct hlist_node *tmp;
LIST_HEAD(gc_list);
int i, deleted = 0;
+ struct netns_xfrm *xfrm = container_of(fc, struct netns_xfrm,
+ flow_cache_global);
for (i = 0; i < flow_cache_hash_size(fc); i++) {
int saved = 0;
@@ -142,7 +126,7 @@ static void __flow_cache_shrink(struct flow_cache *fc,
hlist_for_each_entry_safe(fle, tmp,
&fcp->hash_table[i], u.hlist) {
if (saved < shrink_to &&
- flow_entry_valid(fle)) {
+ flow_entry_valid(fle, xfrm)) {
saved++;
} else {
deleted++;
@@ -152,7 +136,7 @@ static void __flow_cache_shrink(struct flow_cache *fc,
}
}
- flow_cache_queue_garbage(fcp, deleted, &gc_list);
+ flow_cache_queue_garbage(fcp, deleted, &gc_list, xfrm);
}
static void flow_cache_shrink(struct flow_cache *fc,
@@ -208,7 +192,7 @@ struct flow_cache_object *
flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir,
flow_resolve_t resolver, void *ctx)
{
- struct flow_cache *fc = &flow_cache_global;
+ struct flow_cache *fc = &net->xfrm.flow_cache_global;
struct flow_cache_percpu *fcp;
struct flow_cache_entry *fle, *tfle;
struct flow_cache_object *flo;
@@ -258,7 +242,7 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir,
hlist_add_head(&fle->u.hlist, &fcp->hash_table[hash]);
fcp->hash_count++;
}
- } else if (likely(fle->genid == atomic_read(&flow_cache_genid))) {
+ } else if (likely(fle->genid == atomic_read(&net->xfrm.flow_cache_genid))) {
flo = fle->object;
if (!flo)
goto ret_object;
@@ -279,7 +263,7 @@ nocache:
}
flo = resolver(net, key, family, dir, flo, ctx);
if (fle) {
- fle->genid = atomic_read(&flow_cache_genid);
+ fle->genid = atomic_read(&net->xfrm.flow_cache_genid);
if (!IS_ERR(flo))
fle->object = flo;
else
@@ -303,12 +287,14 @@ static void flow_cache_flush_tasklet(unsigned long data)
struct hlist_node *tmp;
LIST_HEAD(gc_list);
int i, deleted = 0;
+ struct netns_xfrm *xfrm = container_of(fc, struct netns_xfrm,
+ flow_cache_global);
fcp = this_cpu_ptr(fc->percpu);
for (i = 0; i < flow_cache_hash_size(fc); i++) {
hlist_for_each_entry_safe(fle, tmp,
&fcp->hash_table[i], u.hlist) {
- if (flow_entry_valid(fle))
+ if (flow_entry_valid(fle, xfrm))
continue;
deleted++;
@@ -317,7 +303,7 @@ static void flow_cache_flush_tasklet(unsigned long data)
}
}
- flow_cache_queue_garbage(fcp, deleted, &gc_list);
+ flow_cache_queue_garbage(fcp, deleted, &gc_list, xfrm);
if (atomic_dec_and_test(&info->cpuleft))
complete(&info->completion);
@@ -351,10 +337,9 @@ static void flow_cache_flush_per_cpu(void *data)
tasklet_schedule(tasklet);
}
-void flow_cache_flush(void)
+void flow_cache_flush(struct net *net)
{
struct flow_flush_info info;
- static DEFINE_MUTEX(flow_flush_sem);
cpumask_var_t mask;
int i, self;
@@ -365,8 +350,8 @@ void flow_cache_flush(void)
/* Don't want cpus going down or up during this. */
get_online_cpus();
- mutex_lock(&flow_flush_sem);
- info.cache = &flow_cache_global;
+ mutex_lock(&net->xfrm.flow_flush_sem);
+ info.cache = &net->xfrm.flow_cache_global;
for_each_online_cpu(i)
if (!flow_cache_percpu_empty(info.cache, i))
cpumask_set_cpu(i, mask);
@@ -386,21 +371,23 @@ void flow_cache_flush(void)
wait_for_completion(&info.completion);
done:
- mutex_unlock(&flow_flush_sem);
+ mutex_unlock(&net->xfrm.flow_flush_sem);
put_online_cpus();
free_cpumask_var(mask);
}
static void flow_cache_flush_task(struct work_struct *work)
{
- flow_cache_flush();
-}
+ struct netns_xfrm *xfrm = container_of(work, struct netns_xfrm,
+ flow_cache_gc_work);
+ struct net *net = container_of(xfrm, struct net, xfrm);
-static DECLARE_WORK(flow_cache_flush_work, flow_cache_flush_task);
+ flow_cache_flush(net);
+}
-void flow_cache_flush_deferred(void)
+void flow_cache_flush_deferred(struct net *net)
{
- schedule_work(&flow_cache_flush_work);
+ schedule_work(&net->xfrm.flow_cache_flush_work);
}
static int flow_cache_cpu_prepare(struct flow_cache *fc, int cpu)
@@ -425,7 +412,8 @@ static int flow_cache_cpu(struct notifier_block *nfb,
unsigned long action,
void *hcpu)
{
- struct flow_cache *fc = container_of(nfb, struct flow_cache, hotcpu_notifier);
+ struct flow_cache *fc = container_of(nfb, struct flow_cache,
+ hotcpu_notifier);
int res, cpu = (unsigned long) hcpu;
struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, cpu);
@@ -444,9 +432,20 @@ static int flow_cache_cpu(struct notifier_block *nfb,
return NOTIFY_OK;
}
-static int __init flow_cache_init(struct flow_cache *fc)
+int flow_cache_init(struct net *net)
{
int i;
+ struct flow_cache *fc = &net->xfrm.flow_cache_global;
+
+ if (!flow_cachep)
+ flow_cachep = kmem_cache_create("flow_cache",
+ sizeof(struct flow_cache_entry),
+ 0, SLAB_PANIC, NULL);
+ spin_lock_init(&net->xfrm.flow_cache_gc_lock);
+ INIT_LIST_HEAD(&net->xfrm.flow_cache_gc_list);
+ INIT_WORK(&net->xfrm.flow_cache_gc_work, flow_cache_gc_task);
+ INIT_WORK(&net->xfrm.flow_cache_flush_work, flow_cache_flush_task);
+ mutex_init(&net->xfrm.flow_flush_sem);
fc->hash_shift = 10;
fc->low_watermark = 2 * flow_cache_hash_size(fc);
@@ -456,6 +455,8 @@ static int __init flow_cache_init(struct flow_cache *fc)
if (!fc->percpu)
return -ENOMEM;
+ cpu_notifier_register_begin();
+
for_each_online_cpu(i) {
if (flow_cache_cpu_prepare(fc, i))
goto err;
@@ -463,7 +464,9 @@ static int __init flow_cache_init(struct flow_cache *fc)
fc->hotcpu_notifier = (struct notifier_block){
.notifier_call = flow_cache_cpu,
};
- register_hotcpu_notifier(&fc->hotcpu_notifier);
+ __register_hotcpu_notifier(&fc->hotcpu_notifier);
+
+ cpu_notifier_register_done();
setup_timer(&fc->rnd_timer, flow_cache_new_hashrnd,
(unsigned long) fc);
@@ -479,19 +482,30 @@ err:
fcp->hash_table = NULL;
}
+ cpu_notifier_register_done();
+
free_percpu(fc->percpu);
fc->percpu = NULL;
return -ENOMEM;
}
+EXPORT_SYMBOL(flow_cache_init);
-static int __init flow_cache_init_global(void)
+void flow_cache_fini(struct net *net)
{
- flow_cachep = kmem_cache_create("flow_cache",
- sizeof(struct flow_cache_entry),
- 0, SLAB_PANIC, NULL);
+ int i;
+ struct flow_cache *fc = &net->xfrm.flow_cache_global;
- return flow_cache_init(&flow_cache_global);
-}
+ del_timer_sync(&fc->rnd_timer);
+ unregister_hotcpu_notifier(&fc->hotcpu_notifier);
+
+ for_each_possible_cpu(i) {
+ struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, i);
+ kfree(fcp->hash_table);
+ fcp->hash_table = NULL;
+ }
-module_init(flow_cache_init_global);
+ free_percpu(fc->percpu);
+ fc->percpu = NULL;
+}
+EXPORT_SYMBOL(flow_cache_fini);
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index e29e810663d7..107ed12a5323 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -61,7 +61,7 @@ bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow)
again:
switch (proto) {
- case __constant_htons(ETH_P_IP): {
+ case htons(ETH_P_IP): {
const struct iphdr *iph;
struct iphdr _iph;
ip:
@@ -77,7 +77,7 @@ ip:
iph_to_flow_copy_addrs(flow, iph);
break;
}
- case __constant_htons(ETH_P_IPV6): {
+ case htons(ETH_P_IPV6): {
const struct ipv6hdr *iph;
struct ipv6hdr _iph;
ipv6:
@@ -91,8 +91,8 @@ ipv6:
nhoff += sizeof(struct ipv6hdr);
break;
}
- case __constant_htons(ETH_P_8021AD):
- case __constant_htons(ETH_P_8021Q): {
+ case htons(ETH_P_8021AD):
+ case htons(ETH_P_8021Q): {
const struct vlan_hdr *vlan;
struct vlan_hdr _vlan;
@@ -104,7 +104,7 @@ ipv6:
nhoff += sizeof(*vlan);
goto again;
}
- case __constant_htons(ETH_P_PPP_SES): {
+ case htons(ETH_P_PPP_SES): {
struct {
struct pppoe_hdr hdr;
__be16 proto;
@@ -115,9 +115,9 @@ ipv6:
proto = hdr->proto;
nhoff += PPPOE_SES_HLEN;
switch (proto) {
- case __constant_htons(PPP_IP):
+ case htons(PPP_IP):
goto ip;
- case __constant_htons(PPP_IPV6):
+ case htons(PPP_IPV6):
goto ipv6;
default:
return false;
@@ -203,8 +203,8 @@ static __always_inline u32 __flow_hash_1word(u32 a)
/*
* __skb_get_hash: calculate a flow hash based on src/dst addresses
- * and src/dst port numbers. Sets rxhash in skb to non-zero hash value
- * on success, zero indicates no valid hash. Also, sets l4_rxhash in skb
+ * and src/dst port numbers. Sets hash in skb to non-zero hash value
+ * on success, zero indicates no valid hash. Also, sets l4_hash in skb
* if hash is a canonical 4-tuple hash over transport ports.
*/
void __skb_get_hash(struct sk_buff *skb)
@@ -216,7 +216,7 @@ void __skb_get_hash(struct sk_buff *skb)
return;
if (keys.ports)
- skb->l4_rxhash = 1;
+ skb->l4_hash = 1;
/* get a consistent hash (same value on both flow directions) */
if (((__force u32)keys.dst < (__force u32)keys.src) ||
@@ -232,7 +232,7 @@ void __skb_get_hash(struct sk_buff *skb)
if (!hash)
hash = 1;
- skb->rxhash = hash;
+ skb->hash = hash;
}
EXPORT_SYMBOL(__skb_get_hash);
@@ -344,7 +344,7 @@ static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb)
hash = skb->sk->sk_hash;
else
hash = (__force u16) skb->protocol ^
- skb->rxhash;
+ skb->hash;
hash = __flow_hash_1word(hash);
queue_index = map->queues[
((u64)hash * map->len) >> 32];
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index e16129019c66..8f8a96ef9f3f 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -836,10 +836,10 @@ out:
static __inline__ int neigh_max_probes(struct neighbour *n)
{
struct neigh_parms *p = n->parms;
- return (n->nud_state & NUD_PROBE) ?
- NEIGH_VAR(p, UCAST_PROBES) :
- NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES) +
- NEIGH_VAR(p, MCAST_PROBES);
+ int max_probes = NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES);
+ if (!(n->nud_state & NUD_PROBE))
+ max_probes += NEIGH_VAR(p, MCAST_PROBES);
+ return max_probes;
}
static void neigh_invalidate(struct neighbour *neigh)
@@ -945,6 +945,7 @@ static void neigh_timer_handler(unsigned long arg)
neigh->nud_state = NUD_FAILED;
notify = 1;
neigh_invalidate(neigh);
+ goto out;
}
if (neigh->nud_state & NUD_IN_TIMER) {
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 93886246a0b4..1cac29ebb05b 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -104,6 +104,7 @@ static ssize_t netdev_store(struct device *dev, struct device_attribute *attr,
}
NETDEVICE_SHOW_RO(dev_id, fmt_hex);
+NETDEVICE_SHOW_RO(dev_port, fmt_dec);
NETDEVICE_SHOW_RO(addr_assign_type, fmt_dec);
NETDEVICE_SHOW_RO(addr_len, fmt_dec);
NETDEVICE_SHOW_RO(iflink, fmt_dec);
@@ -252,6 +253,16 @@ static ssize_t operstate_show(struct device *dev,
}
static DEVICE_ATTR_RO(operstate);
+static ssize_t carrier_changes_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct net_device *netdev = to_net_dev(dev);
+ return sprintf(buf, fmt_dec,
+ atomic_read(&netdev->carrier_changes));
+}
+static DEVICE_ATTR_RO(carrier_changes);
+
/* read-write attributes */
static int change_mtu(struct net_device *net, unsigned long new_mtu)
@@ -373,6 +384,7 @@ static struct attribute *net_class_attrs[] = {
&dev_attr_netdev_group.attr,
&dev_attr_type.attr,
&dev_attr_dev_id.attr,
+ &dev_attr_dev_port.attr,
&dev_attr_iflink.attr,
&dev_attr_ifindex.attr,
&dev_attr_addr_assign_type.attr,
@@ -384,6 +396,7 @@ static struct attribute *net_class_attrs[] = {
&dev_attr_duplex.attr,
&dev_attr_dormant.attr,
&dev_attr_operstate.attr,
+ &dev_attr_carrier_changes.attr,
&dev_attr_ifalias.attr,
&dev_attr_carrier.attr,
&dev_attr_mtu.attr,
@@ -789,7 +802,7 @@ exit:
kobject_put(kobj);
return error;
}
-#endif /* CONFIG_SYFS */
+#endif /* CONFIG_SYSFS */
int
net_rx_queue_update_kobjects(struct net_device *net, int old_num, int new_num)
@@ -996,15 +1009,12 @@ static struct attribute_group dql_group = {
#endif /* CONFIG_BQL */
#ifdef CONFIG_XPS
-static inline unsigned int get_netdev_queue_index(struct netdev_queue *queue)
+static unsigned int get_netdev_queue_index(struct netdev_queue *queue)
{
struct net_device *dev = queue->dev;
- int i;
-
- for (i = 0; i < dev->num_tx_queues; i++)
- if (queue == &dev->_tx[i])
- break;
+ unsigned int i;
+ i = queue - dev->_tx;
BUG_ON(i >= dev->num_tx_queues);
return i;
diff --git a/net/core/netclassid_cgroup.c b/net/core/netclassid_cgroup.c
index 719efd541668..22931e1b99b4 100644
--- a/net/core/netclassid_cgroup.c
+++ b/net/core/netclassid_cgroup.c
@@ -23,7 +23,7 @@ static inline struct cgroup_cls_state *css_cls_state(struct cgroup_subsys_state
struct cgroup_cls_state *task_cls_state(struct task_struct *p)
{
- return css_cls_state(task_css(p, net_cls_subsys_id));
+ return css_cls_state(task_css(p, net_cls_cgrp_id));
}
EXPORT_SYMBOL_GPL(task_cls_state);
@@ -73,7 +73,7 @@ static void cgrp_attach(struct cgroup_subsys_state *css,
void *v = (void *)(unsigned long)cs->classid;
struct task_struct *p;
- cgroup_taskset_for_each(p, css, tset) {
+ cgroup_taskset_for_each(p, tset) {
task_lock(p);
iterate_fd(p->files, 0, update_classid, v);
task_unlock(p);
@@ -102,19 +102,10 @@ static struct cftype ss_files[] = {
{ } /* terminate */
};
-struct cgroup_subsys net_cls_subsys = {
- .name = "net_cls",
+struct cgroup_subsys net_cls_cgrp_subsys = {
.css_alloc = cgrp_css_alloc,
.css_online = cgrp_css_online,
.css_free = cgrp_css_free,
.attach = cgrp_attach,
- .subsys_id = net_cls_subsys_id,
.base_cftypes = ss_files,
- .module = THIS_MODULE,
};
-
-static int __init init_netclassid_cgroup(void)
-{
- return cgroup_load_subsys(&net_cls_subsys);
-}
-__initcall(init_netclassid_cgroup);
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index df9e6b1a9759..e33937fb32a0 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -46,13 +46,9 @@
static struct sk_buff_head skb_pool;
-static atomic_t trapped;
-
DEFINE_STATIC_SRCU(netpoll_srcu);
#define USEC_PER_POLL 50
-#define NETPOLL_RX_ENABLED 1
-#define NETPOLL_RX_DROP 2
#define MAX_SKB_SIZE \
(sizeof(struct ethhdr) + \
@@ -61,7 +57,6 @@ DEFINE_STATIC_SRCU(netpoll_srcu);
MAX_UDP_CHUNK)
static void zap_completion_queue(void);
-static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo);
static void netpoll_async_cleanup(struct work_struct *work);
static unsigned int carrier_timeout = 4;
@@ -74,6 +69,37 @@ module_param(carrier_timeout, uint, 0644);
#define np_notice(np, fmt, ...) \
pr_notice("%s: " fmt, np->name, ##__VA_ARGS__)
+static int netpoll_start_xmit(struct sk_buff *skb, struct net_device *dev,
+ struct netdev_queue *txq)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+ int status = NETDEV_TX_OK;
+ netdev_features_t features;
+
+ features = netif_skb_features(skb);
+
+ if (vlan_tx_tag_present(skb) &&
+ !vlan_hw_offload_capable(features, skb->vlan_proto)) {
+ skb = __vlan_put_tag(skb, skb->vlan_proto,
+ vlan_tx_tag_get(skb));
+ if (unlikely(!skb)) {
+ /* This is actually a packet drop, but we
+ * don't want the code that calls this
+ * function to try and operate on a NULL skb.
+ */
+ goto out;
+ }
+ skb->vlan_tci = 0;
+ }
+
+ status = ops->ndo_start_xmit(skb, dev);
+ if (status == NETDEV_TX_OK)
+ txq_trans_update(txq);
+
+out:
+ return status;
+}
+
static void queue_process(struct work_struct *work)
{
struct netpoll_info *npinfo =
@@ -83,51 +109,31 @@ static void queue_process(struct work_struct *work)
while ((skb = skb_dequeue(&npinfo->txq))) {
struct net_device *dev = skb->dev;
- const struct net_device_ops *ops = dev->netdev_ops;
struct netdev_queue *txq;
if (!netif_device_present(dev) || !netif_running(dev)) {
- __kfree_skb(skb);
+ kfree_skb(skb);
continue;
}
txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
local_irq_save(flags);
- __netif_tx_lock(txq, smp_processor_id());
+ HARD_TX_LOCK(dev, txq, smp_processor_id());
if (netif_xmit_frozen_or_stopped(txq) ||
- ops->ndo_start_xmit(skb, dev) != NETDEV_TX_OK) {
+ netpoll_start_xmit(skb, dev, txq) != NETDEV_TX_OK) {
skb_queue_head(&npinfo->txq, skb);
- __netif_tx_unlock(txq);
+ HARD_TX_UNLOCK(dev, txq);
local_irq_restore(flags);
schedule_delayed_work(&npinfo->tx_work, HZ/10);
return;
}
- __netif_tx_unlock(txq);
+ HARD_TX_UNLOCK(dev, txq);
local_irq_restore(flags);
}
}
-static __sum16 checksum_udp(struct sk_buff *skb, struct udphdr *uh,
- unsigned short ulen, __be32 saddr, __be32 daddr)
-{
- __wsum psum;
-
- if (uh->check == 0 || skb_csum_unnecessary(skb))
- return 0;
-
- psum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
-
- if (skb->ip_summed == CHECKSUM_COMPLETE &&
- !csum_fold(csum_add(psum, skb->csum)))
- return 0;
-
- skb->csum = psum;
-
- return __skb_checksum_complete(skb);
-}
-
/*
* Check whether delayed processing was scheduled for our NIC. If so,
* we attempt to grab the poll lock and use ->poll() to pump the card.
@@ -138,14 +144,8 @@ static __sum16 checksum_udp(struct sk_buff *skb, struct udphdr *uh,
* trylock here and interrupts are already disabled in the softirq
* case. Further, we test the poll_owner to avoid recursion on UP
* systems where the lock doesn't exist.
- *
- * In cases where there is bi-directional communications, reading only
- * one message at a time can lead to packets being dropped by the
- * network adapter, forcing superfluous retries and possibly timeouts.
- * Thus, we set our budget to greater than 1.
*/
-static int poll_one_napi(struct netpoll_info *npinfo,
- struct napi_struct *napi, int budget)
+static int poll_one_napi(struct napi_struct *napi, int budget)
{
int work;
@@ -156,52 +156,35 @@ static int poll_one_napi(struct netpoll_info *npinfo,
if (!test_bit(NAPI_STATE_SCHED, &napi->state))
return budget;
- npinfo->rx_flags |= NETPOLL_RX_DROP;
- atomic_inc(&trapped);
set_bit(NAPI_STATE_NPSVC, &napi->state);
work = napi->poll(napi, budget);
+ WARN_ONCE(work > budget, "%pF exceeded budget in poll\n", napi->poll);
trace_napi_poll(napi);
clear_bit(NAPI_STATE_NPSVC, &napi->state);
- atomic_dec(&trapped);
- npinfo->rx_flags &= ~NETPOLL_RX_DROP;
return budget - work;
}
-static void poll_napi(struct net_device *dev)
+static void poll_napi(struct net_device *dev, int budget)
{
struct napi_struct *napi;
- int budget = 16;
list_for_each_entry(napi, &dev->napi_list, dev_list) {
if (napi->poll_owner != smp_processor_id() &&
spin_trylock(&napi->poll_lock)) {
- budget = poll_one_napi(rcu_dereference_bh(dev->npinfo),
- napi, budget);
+ budget = poll_one_napi(napi, budget);
spin_unlock(&napi->poll_lock);
-
- if (!budget)
- break;
}
}
}
-static void service_neigh_queue(struct netpoll_info *npi)
-{
- if (npi) {
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(&npi->neigh_tx)))
- netpoll_neigh_reply(skb, npi);
- }
-}
-
static void netpoll_poll_dev(struct net_device *dev)
{
const struct net_device_ops *ops;
struct netpoll_info *ni = rcu_dereference_bh(dev->npinfo);
+ int budget = 0;
/* Don't do any rx activity if the dev_lock mutex is held
* the dev_open/close paths use this to block netpoll activity
@@ -224,31 +207,14 @@ static void netpoll_poll_dev(struct net_device *dev)
/* Process pending work on NIC */
ops->ndo_poll_controller(dev);
- poll_napi(dev);
+ poll_napi(dev, budget);
up(&ni->dev_lock);
- if (dev->flags & IFF_SLAVE) {
- if (ni) {
- struct net_device *bond_dev;
- struct sk_buff *skb;
- struct netpoll_info *bond_ni;
-
- bond_dev = netdev_master_upper_dev_get_rcu(dev);
- bond_ni = rcu_dereference_bh(bond_dev->npinfo);
- while ((skb = skb_dequeue(&ni->neigh_tx))) {
- skb->dev = bond_dev;
- skb_queue_tail(&bond_ni->neigh_tx, skb);
- }
- }
- }
-
- service_neigh_queue(ni);
-
zap_completion_queue();
}
-void netpoll_rx_disable(struct net_device *dev)
+void netpoll_poll_disable(struct net_device *dev)
{
struct netpoll_info *ni;
int idx;
@@ -259,9 +225,9 @@ void netpoll_rx_disable(struct net_device *dev)
down(&ni->dev_lock);
srcu_read_unlock(&netpoll_srcu, idx);
}
-EXPORT_SYMBOL(netpoll_rx_disable);
+EXPORT_SYMBOL(netpoll_poll_disable);
-void netpoll_rx_enable(struct net_device *dev)
+void netpoll_poll_enable(struct net_device *dev)
{
struct netpoll_info *ni;
rcu_read_lock();
@@ -270,7 +236,7 @@ void netpoll_rx_enable(struct net_device *dev)
up(&ni->dev_lock);
rcu_read_unlock();
}
-EXPORT_SYMBOL(netpoll_rx_enable);
+EXPORT_SYMBOL(netpoll_poll_enable);
static void refill_skbs(void)
{
@@ -304,7 +270,7 @@ static void zap_completion_queue(void)
while (clist != NULL) {
struct sk_buff *skb = clist;
clist = clist->next;
- if (skb->destructor) {
+ if (!skb_irq_freeable(skb)) {
atomic_inc(&skb->users);
dev_kfree_skb_any(skb); /* put this one back */
} else {
@@ -359,7 +325,6 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
{
int status = NETDEV_TX_BUSY;
unsigned long tries;
- const struct net_device_ops *ops = dev->netdev_ops;
/* It is up to the caller to keep npinfo alive. */
struct netpoll_info *npinfo;
@@ -367,7 +332,7 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
npinfo = rcu_dereference_bh(np->dev->npinfo);
if (!npinfo || !netif_running(dev) || !netif_device_present(dev)) {
- __kfree_skb(skb);
+ dev_kfree_skb_irq(skb);
return;
}
@@ -380,29 +345,11 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
/* try until next clock tick */
for (tries = jiffies_to_usecs(1)/USEC_PER_POLL;
tries > 0; --tries) {
- if (__netif_tx_trylock(txq)) {
- if (!netif_xmit_stopped(txq)) {
- if (vlan_tx_tag_present(skb) &&
- !vlan_hw_offload_capable(netif_skb_features(skb),
- skb->vlan_proto)) {
- skb = __vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb));
- if (unlikely(!skb)) {
- /* This is actually a packet drop, but we
- * don't want the code at the end of this
- * function to try and re-queue a NULL skb.
- */
- status = NETDEV_TX_OK;
- goto unlock_txq;
- }
- skb->vlan_tci = 0;
- }
-
- status = ops->ndo_start_xmit(skb, dev);
- if (status == NETDEV_TX_OK)
- txq_trans_update(txq);
- }
- unlock_txq:
- __netif_tx_unlock(txq);
+ if (HARD_TX_TRYLOCK(dev, txq)) {
+ if (!netif_xmit_stopped(txq))
+ status = netpoll_start_xmit(skb, dev, txq);
+
+ HARD_TX_UNLOCK(dev, txq);
if (status == NETDEV_TX_OK)
break;
@@ -417,7 +364,7 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
WARN_ONCE(!irqs_disabled(),
"netpoll_send_skb_on_dev(): %s enabled interrupts in poll (%pF)\n",
- dev->name, ops->ndo_start_xmit);
+ dev->name, dev->netdev_ops->ndo_start_xmit);
}
@@ -529,384 +476,6 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
}
EXPORT_SYMBOL(netpoll_send_udp);
-static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo)
-{
- int size, type = ARPOP_REPLY;
- __be32 sip, tip;
- unsigned char *sha;
- struct sk_buff *send_skb;
- struct netpoll *np, *tmp;
- unsigned long flags;
- int hlen, tlen;
- int hits = 0, proto;
-
- if (list_empty(&npinfo->rx_np))
- return;
-
- /* Before checking the packet, we do some early
- inspection whether this is interesting at all */
- spin_lock_irqsave(&npinfo->rx_lock, flags);
- list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) {
- if (np->dev == skb->dev)
- hits++;
- }
- spin_unlock_irqrestore(&npinfo->rx_lock, flags);
-
- /* No netpoll struct is using this dev */
- if (!hits)
- return;
-
- proto = ntohs(eth_hdr(skb)->h_proto);
- if (proto == ETH_P_ARP) {
- struct arphdr *arp;
- unsigned char *arp_ptr;
- /* No arp on this interface */
- if (skb->dev->flags & IFF_NOARP)
- return;
-
- if (!pskb_may_pull(skb, arp_hdr_len(skb->dev)))
- return;
-
- skb_reset_network_header(skb);
- skb_reset_transport_header(skb);
- arp = arp_hdr(skb);
-
- if ((arp->ar_hrd != htons(ARPHRD_ETHER) &&
- arp->ar_hrd != htons(ARPHRD_IEEE802)) ||
- arp->ar_pro != htons(ETH_P_IP) ||
- arp->ar_op != htons(ARPOP_REQUEST))
- return;
-
- arp_ptr = (unsigned char *)(arp+1);
- /* save the location of the src hw addr */
- sha = arp_ptr;
- arp_ptr += skb->dev->addr_len;
- memcpy(&sip, arp_ptr, 4);
- arp_ptr += 4;
- /* If we actually cared about dst hw addr,
- it would get copied here */
- arp_ptr += skb->dev->addr_len;
- memcpy(&tip, arp_ptr, 4);
-
- /* Should we ignore arp? */
- if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip))
- return;
-
- size = arp_hdr_len(skb->dev);
-
- spin_lock_irqsave(&npinfo->rx_lock, flags);
- list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) {
- if (tip != np->local_ip.ip)
- continue;
-
- hlen = LL_RESERVED_SPACE(np->dev);
- tlen = np->dev->needed_tailroom;
- send_skb = find_skb(np, size + hlen + tlen, hlen);
- if (!send_skb)
- continue;
-
- skb_reset_network_header(send_skb);
- arp = (struct arphdr *) skb_put(send_skb, size);
- send_skb->dev = skb->dev;
- send_skb->protocol = htons(ETH_P_ARP);
-
- /* Fill the device header for the ARP frame */
- if (dev_hard_header(send_skb, skb->dev, ETH_P_ARP,
- sha, np->dev->dev_addr,
- send_skb->len) < 0) {
- kfree_skb(send_skb);
- continue;
- }
-
- /*
- * Fill out the arp protocol part.
- *
- * we only support ethernet device type,
- * which (according to RFC 1390) should
- * always equal 1 (Ethernet).
- */
-
- arp->ar_hrd = htons(np->dev->type);
- arp->ar_pro = htons(ETH_P_IP);
- arp->ar_hln = np->dev->addr_len;
- arp->ar_pln = 4;
- arp->ar_op = htons(type);
-
- arp_ptr = (unsigned char *)(arp + 1);
- memcpy(arp_ptr, np->dev->dev_addr, np->dev->addr_len);
- arp_ptr += np->dev->addr_len;
- memcpy(arp_ptr, &tip, 4);
- arp_ptr += 4;
- memcpy(arp_ptr, sha, np->dev->addr_len);
- arp_ptr += np->dev->addr_len;
- memcpy(arp_ptr, &sip, 4);
-
- netpoll_send_skb(np, send_skb);
-
- /* If there are several rx_skb_hooks for the same
- * address we're fine by sending a single reply
- */
- break;
- }
- spin_unlock_irqrestore(&npinfo->rx_lock, flags);
- } else if( proto == ETH_P_IPV6) {
-#if IS_ENABLED(CONFIG_IPV6)
- struct nd_msg *msg;
- u8 *lladdr = NULL;
- struct ipv6hdr *hdr;
- struct icmp6hdr *icmp6h;
- const struct in6_addr *saddr;
- const struct in6_addr *daddr;
- struct inet6_dev *in6_dev = NULL;
- struct in6_addr *target;
-
- in6_dev = in6_dev_get(skb->dev);
- if (!in6_dev || !in6_dev->cnf.accept_ra)
- return;
-
- if (!pskb_may_pull(skb, skb->len))
- return;
-
- msg = (struct nd_msg *)skb_transport_header(skb);
-
- __skb_push(skb, skb->data - skb_transport_header(skb));
-
- if (ipv6_hdr(skb)->hop_limit != 255)
- return;
- if (msg->icmph.icmp6_code != 0)
- return;
- if (msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION)
- return;
-
- saddr = &ipv6_hdr(skb)->saddr;
- daddr = &ipv6_hdr(skb)->daddr;
-
- size = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
-
- spin_lock_irqsave(&npinfo->rx_lock, flags);
- list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) {
- if (!ipv6_addr_equal(daddr, &np->local_ip.in6))
- continue;
-
- hlen = LL_RESERVED_SPACE(np->dev);
- tlen = np->dev->needed_tailroom;
- send_skb = find_skb(np, size + hlen + tlen, hlen);
- if (!send_skb)
- continue;
-
- send_skb->protocol = htons(ETH_P_IPV6);
- send_skb->dev = skb->dev;
-
- skb_reset_network_header(send_skb);
- hdr = (struct ipv6hdr *) skb_put(send_skb, sizeof(struct ipv6hdr));
- *(__be32*)hdr = htonl(0x60000000);
- hdr->payload_len = htons(size);
- hdr->nexthdr = IPPROTO_ICMPV6;
- hdr->hop_limit = 255;
- hdr->saddr = *saddr;
- hdr->daddr = *daddr;
-
- icmp6h = (struct icmp6hdr *) skb_put(send_skb, sizeof(struct icmp6hdr));
- icmp6h->icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
- icmp6h->icmp6_router = 0;
- icmp6h->icmp6_solicited = 1;
-
- target = (struct in6_addr *) skb_put(send_skb, sizeof(struct in6_addr));
- *target = msg->target;
- icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, size,
- IPPROTO_ICMPV6,
- csum_partial(icmp6h,
- size, 0));
-
- if (dev_hard_header(send_skb, skb->dev, ETH_P_IPV6,
- lladdr, np->dev->dev_addr,
- send_skb->len) < 0) {
- kfree_skb(send_skb);
- continue;
- }
-
- netpoll_send_skb(np, send_skb);
-
- /* If there are several rx_skb_hooks for the same
- * address, we're fine by sending a single reply
- */
- break;
- }
- spin_unlock_irqrestore(&npinfo->rx_lock, flags);
-#endif
- }
-}
-
-static bool pkt_is_ns(struct sk_buff *skb)
-{
- struct nd_msg *msg;
- struct ipv6hdr *hdr;
-
- if (skb->protocol != htons(ETH_P_IPV6))
- return false;
- if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + sizeof(struct nd_msg)))
- return false;
-
- msg = (struct nd_msg *)skb_transport_header(skb);
- __skb_push(skb, skb->data - skb_transport_header(skb));
- hdr = ipv6_hdr(skb);
-
- if (hdr->nexthdr != IPPROTO_ICMPV6)
- return false;
- if (hdr->hop_limit != 255)
- return false;
- if (msg->icmph.icmp6_code != 0)
- return false;
- if (msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION)
- return false;
-
- return true;
-}
-
-int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
-{
- int proto, len, ulen, data_len;
- int hits = 0, offset;
- const struct iphdr *iph;
- struct udphdr *uh;
- struct netpoll *np, *tmp;
- uint16_t source;
-
- if (list_empty(&npinfo->rx_np))
- goto out;
-
- if (skb->dev->type != ARPHRD_ETHER)
- goto out;
-
- /* check if netpoll clients need ARP */
- if (skb->protocol == htons(ETH_P_ARP) && atomic_read(&trapped)) {
- skb_queue_tail(&npinfo->neigh_tx, skb);
- return 1;
- } else if (pkt_is_ns(skb) && atomic_read(&trapped)) {
- skb_queue_tail(&npinfo->neigh_tx, skb);
- return 1;
- }
-
- if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) {
- skb = vlan_untag(skb);
- if (unlikely(!skb))
- goto out;
- }
-
- proto = ntohs(eth_hdr(skb)->h_proto);
- if (proto != ETH_P_IP && proto != ETH_P_IPV6)
- goto out;
- if (skb->pkt_type == PACKET_OTHERHOST)
- goto out;
- if (skb_shared(skb))
- goto out;
-
- if (proto == ETH_P_IP) {
- if (!pskb_may_pull(skb, sizeof(struct iphdr)))
- goto out;
- iph = (struct iphdr *)skb->data;
- if (iph->ihl < 5 || iph->version != 4)
- goto out;
- if (!pskb_may_pull(skb, iph->ihl*4))
- goto out;
- iph = (struct iphdr *)skb->data;
- if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)
- goto out;
-
- len = ntohs(iph->tot_len);
- if (skb->len < len || len < iph->ihl*4)
- goto out;
-
- /*
- * Our transport medium may have padded the buffer out.
- * Now We trim to the true length of the frame.
- */
- if (pskb_trim_rcsum(skb, len))
- goto out;
-
- iph = (struct iphdr *)skb->data;
- if (iph->protocol != IPPROTO_UDP)
- goto out;
-
- len -= iph->ihl*4;
- uh = (struct udphdr *)(((char *)iph) + iph->ihl*4);
- offset = (unsigned char *)(uh + 1) - skb->data;
- ulen = ntohs(uh->len);
- data_len = skb->len - offset;
- source = ntohs(uh->source);
-
- if (ulen != len)
- goto out;
- if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr))
- goto out;
- list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) {
- if (np->local_ip.ip && np->local_ip.ip != iph->daddr)
- continue;
- if (np->remote_ip.ip && np->remote_ip.ip != iph->saddr)
- continue;
- if (np->local_port && np->local_port != ntohs(uh->dest))
- continue;
-
- np->rx_skb_hook(np, source, skb, offset, data_len);
- hits++;
- }
- } else {
-#if IS_ENABLED(CONFIG_IPV6)
- const struct ipv6hdr *ip6h;
-
- if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
- goto out;
- ip6h = (struct ipv6hdr *)skb->data;
- if (ip6h->version != 6)
- goto out;
- len = ntohs(ip6h->payload_len);
- if (!len)
- goto out;
- if (len + sizeof(struct ipv6hdr) > skb->len)
- goto out;
- if (pskb_trim_rcsum(skb, len + sizeof(struct ipv6hdr)))
- goto out;
- ip6h = ipv6_hdr(skb);
- if (!pskb_may_pull(skb, sizeof(struct udphdr)))
- goto out;
- uh = udp_hdr(skb);
- offset = (unsigned char *)(uh + 1) - skb->data;
- ulen = ntohs(uh->len);
- data_len = skb->len - offset;
- source = ntohs(uh->source);
- if (ulen != skb->len)
- goto out;
- if (udp6_csum_init(skb, uh, IPPROTO_UDP))
- goto out;
- list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) {
- if (!ipv6_addr_equal(&np->local_ip.in6, &ip6h->daddr))
- continue;
- if (!ipv6_addr_equal(&np->remote_ip.in6, &ip6h->saddr))
- continue;
- if (np->local_port && np->local_port != ntohs(uh->dest))
- continue;
-
- np->rx_skb_hook(np, source, skb, offset, data_len);
- hits++;
- }
-#endif
- }
-
- if (!hits)
- goto out;
-
- kfree_skb(skb);
- return 1;
-
-out:
- if (atomic_read(&trapped)) {
- kfree_skb(skb);
- return 1;
- }
-
- return 0;
-}
-
void netpoll_print_options(struct netpoll *np)
{
np_info(np, "local port %d\n", np->local_port);
@@ -1026,11 +595,10 @@ int netpoll_parse_options(struct netpoll *np, char *opt)
}
EXPORT_SYMBOL(netpoll_parse_options);
-int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp)
+int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
{
struct netpoll_info *npinfo;
const struct net_device_ops *ops;
- unsigned long flags;
int err;
np->dev = ndev;
@@ -1046,18 +614,13 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp)
}
if (!ndev->npinfo) {
- npinfo = kmalloc(sizeof(*npinfo), gfp);
+ npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL);
if (!npinfo) {
err = -ENOMEM;
goto out;
}
- npinfo->rx_flags = 0;
- INIT_LIST_HEAD(&npinfo->rx_np);
-
- spin_lock_init(&npinfo->rx_lock);
sema_init(&npinfo->dev_lock, 1);
- skb_queue_head_init(&npinfo->neigh_tx);
skb_queue_head_init(&npinfo->txq);
INIT_DELAYED_WORK(&npinfo->tx_work, queue_process);
@@ -1065,7 +628,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp)
ops = np->dev->netdev_ops;
if (ops->ndo_netpoll_setup) {
- err = ops->ndo_netpoll_setup(ndev, npinfo, gfp);
+ err = ops->ndo_netpoll_setup(ndev, npinfo);
if (err)
goto free_npinfo;
}
@@ -1076,13 +639,6 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp)
npinfo->netpoll = np;
- if (np->rx_skb_hook) {
- spin_lock_irqsave(&npinfo->rx_lock, flags);
- npinfo->rx_flags |= NETPOLL_RX_ENABLED;
- list_add_tail(&np->rx, &npinfo->rx_np);
- spin_unlock_irqrestore(&npinfo->rx_lock, flags);
- }
-
/* last thing to do is link it to the net device structure */
rcu_assign_pointer(ndev->npinfo, npinfo);
@@ -1204,7 +760,7 @@ int netpoll_setup(struct netpoll *np)
/* fill up the skb queue */
refill_skbs();
- err = __netpoll_setup(np, ndev, GFP_KERNEL);
+ err = __netpoll_setup(np, ndev);
if (err)
goto put;
@@ -1231,7 +787,6 @@ static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head)
struct netpoll_info *npinfo =
container_of(rcu_head, struct netpoll_info, rcu);
- skb_queue_purge(&npinfo->neigh_tx);
skb_queue_purge(&npinfo->txq);
/* we can't call cancel_delayed_work_sync here, as we are in softirq */
@@ -1247,7 +802,6 @@ static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head)
void __netpoll_cleanup(struct netpoll *np)
{
struct netpoll_info *npinfo;
- unsigned long flags;
/* rtnl_dereference would be preferable here but
* rcu_cleanup_netpoll path can put us in here safely without
@@ -1257,14 +811,6 @@ void __netpoll_cleanup(struct netpoll *np)
if (!npinfo)
return;
- if (!list_empty(&npinfo->rx_np)) {
- spin_lock_irqsave(&npinfo->rx_lock, flags);
- list_del(&np->rx);
- if (list_empty(&npinfo->rx_np))
- npinfo->rx_flags &= ~NETPOLL_RX_ENABLED;
- spin_unlock_irqrestore(&npinfo->rx_lock, flags);
- }
-
synchronize_srcu(&netpoll_srcu);
if (atomic_dec_and_test(&npinfo->refcnt)) {
@@ -1274,7 +820,7 @@ void __netpoll_cleanup(struct netpoll *np)
if (ops->ndo_netpoll_cleanup)
ops->ndo_netpoll_cleanup(np->dev);
- rcu_assign_pointer(np->dev->npinfo, NULL);
+ RCU_INIT_POINTER(np->dev->npinfo, NULL);
call_rcu_bh(&npinfo->rcu, rcu_cleanup_netpoll_info);
}
}
@@ -1308,18 +854,3 @@ out:
rtnl_unlock();
}
EXPORT_SYMBOL(netpoll_cleanup);
-
-int netpoll_trap(void)
-{
- return atomic_read(&trapped);
-}
-EXPORT_SYMBOL(netpoll_trap);
-
-void netpoll_set_trap(int trap)
-{
- if (trap)
- atomic_inc(&trapped);
- else
- atomic_dec(&trapped);
-}
-EXPORT_SYMBOL(netpoll_set_trap);
diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c
index 9043caedcd08..3825f669147b 100644
--- a/net/core/netprio_cgroup.c
+++ b/net/core/netprio_cgroup.c
@@ -186,7 +186,7 @@ static int read_priomap(struct seq_file *sf, void *v)
}
static int write_priomap(struct cgroup_subsys_state *css, struct cftype *cft,
- const char *buffer)
+ char *buffer)
{
char devname[IFNAMSIZ + 1];
struct net_device *dev;
@@ -224,7 +224,7 @@ static void net_prio_attach(struct cgroup_subsys_state *css,
struct task_struct *p;
void *v = (void *)(unsigned long)css->cgroup->id;
- cgroup_taskset_for_each(p, css, tset) {
+ cgroup_taskset_for_each(p, tset) {
task_lock(p);
iterate_fd(p->files, 0, update_netprio, v);
task_unlock(p);
@@ -244,15 +244,12 @@ static struct cftype ss_files[] = {
{ } /* terminate */
};
-struct cgroup_subsys net_prio_subsys = {
- .name = "net_prio",
+struct cgroup_subsys net_prio_cgrp_subsys = {
.css_alloc = cgrp_css_alloc,
.css_online = cgrp_css_online,
.css_free = cgrp_css_free,
.attach = net_prio_attach,
- .subsys_id = net_prio_subsys_id,
.base_cftypes = ss_files,
- .module = THIS_MODULE,
};
static int netprio_device_event(struct notifier_block *unused,
@@ -283,37 +280,9 @@ static struct notifier_block netprio_device_notifier = {
static int __init init_cgroup_netprio(void)
{
- int ret;
-
- ret = cgroup_load_subsys(&net_prio_subsys);
- if (ret)
- goto out;
-
register_netdevice_notifier(&netprio_device_notifier);
-
-out:
- return ret;
-}
-
-static void __exit exit_cgroup_netprio(void)
-{
- struct netprio_map *old;
- struct net_device *dev;
-
- unregister_netdevice_notifier(&netprio_device_notifier);
-
- cgroup_unload_subsys(&net_prio_subsys);
-
- rtnl_lock();
- for_each_netdev(&init_net, dev) {
- old = rtnl_dereference(dev->priomap);
- RCU_INIT_POINTER(dev->priomap, NULL);
- if (old)
- kfree_rcu(old, rcu);
- }
- rtnl_unlock();
+ return 0;
}
-module_init(init_cgroup_netprio);
-module_exit(exit_cgroup_netprio);
+subsys_initcall(init_cgroup_netprio);
MODULE_LICENSE("GPL v2");
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index fdac61cac1bd..d0dac57291af 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -476,23 +476,22 @@ static int pgctrl_show(struct seq_file *seq, void *v)
static ssize_t pgctrl_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- int err = 0;
char data[128];
struct pktgen_net *pn = net_generic(current->nsproxy->net_ns, pg_net_id);
- if (!capable(CAP_NET_ADMIN)) {
- err = -EPERM;
- goto out;
- }
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (count == 0)
+ return -EINVAL;
if (count > sizeof(data))
count = sizeof(data);
- if (copy_from_user(data, buf, count)) {
- err = -EFAULT;
- goto out;
- }
- data[count - 1] = 0; /* Make string */
+ if (copy_from_user(data, buf, count))
+ return -EFAULT;
+
+ data[count - 1] = 0; /* Strip trailing '\n' and terminate string */
if (!strcmp(data, "stop"))
pktgen_stop_all_threads_ifs(pn);
@@ -506,10 +505,7 @@ static ssize_t pgctrl_write(struct file *file, const char __user *buf,
else
pr_warning("Unknown command: %s\n", data);
- err = count;
-
-out:
- return err;
+ return count;
}
static int pgctrl_open(struct inode *inode, struct file *file)
@@ -1251,7 +1247,13 @@ static ssize_t pktgen_if_write(struct file *file,
"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
f,
"IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, "
- "MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ, IPSEC, NODE_ALLOC\n");
+ "MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, "
+ "MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ, "
+ "QUEUE_MAP_RND, QUEUE_MAP_CPU, UDPCSUM, "
+#ifdef CONFIG_XFRM
+ "IPSEC, "
+#endif
+ "NODE_ALLOC\n");
return count;
}
sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags);
diff --git a/net/core/ptp_classifier.c b/net/core/ptp_classifier.c
new file mode 100644
index 000000000000..eaba0f68f860
--- /dev/null
+++ b/net/core/ptp_classifier.c
@@ -0,0 +1,141 @@
+/* PTP classifier
+ *
+ * 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 below program is the bpf_asm (tools/net/) representation of
+ * the opcode array in the ptp_filter structure.
+ *
+ * For convenience, this can easily be altered and reviewed with
+ * bpf_asm and bpf_dbg, e.g. `./bpf_asm -c prog` where prog is a
+ * simple file containing the below program:
+ *
+ * ldh [12] ; load ethertype
+ *
+ * ; PTP over UDP over IPv4 over Ethernet
+ * test_ipv4:
+ * jneq #0x800, test_ipv6 ; ETH_P_IP ?
+ * ldb [23] ; load proto
+ * jneq #17, drop_ipv4 ; IPPROTO_UDP ?
+ * ldh [20] ; load frag offset field
+ * jset #0x1fff, drop_ipv4 ; don't allow fragments
+ * ldxb 4*([14]&0xf) ; load IP header len
+ * ldh [x + 16] ; load UDP dst port
+ * jneq #319, drop_ipv4 ; is port PTP_EV_PORT ?
+ * ldh [x + 22] ; load payload
+ * and #0xf ; mask PTP_CLASS_VMASK
+ * or #0x10 ; PTP_CLASS_IPV4
+ * ret a ; return PTP class
+ * drop_ipv4: ret #0x0 ; PTP_CLASS_NONE
+ *
+ * ; PTP over UDP over IPv6 over Ethernet
+ * test_ipv6:
+ * jneq #0x86dd, test_8021q ; ETH_P_IPV6 ?
+ * ldb [20] ; load proto
+ * jneq #17, drop_ipv6 ; IPPROTO_UDP ?
+ * ldh [56] ; load UDP dst port
+ * jneq #319, drop_ipv6 ; is port PTP_EV_PORT ?
+ * ldh [62] ; load payload
+ * and #0xf ; mask PTP_CLASS_VMASK
+ * or #0x20 ; PTP_CLASS_IPV6
+ * ret a ; return PTP class
+ * drop_ipv6: ret #0x0 ; PTP_CLASS_NONE
+ *
+ * ; PTP over 802.1Q over Ethernet
+ * test_8021q:
+ * jneq #0x8100, test_ieee1588 ; ETH_P_8021Q ?
+ * ldh [16] ; load inner type
+ * jneq #0x88f7, drop_ieee1588 ; ETH_P_1588 ?
+ * ldb [18] ; load payload
+ * and #0x8 ; as we don't have ports here, test
+ * jneq #0x0, drop_ieee1588 ; for PTP_GEN_BIT and drop these
+ * ldh [18] ; reload payload
+ * and #0xf ; mask PTP_CLASS_VMASK
+ * or #0x40 ; PTP_CLASS_V2_VLAN
+ * ret a ; return PTP class
+ *
+ * ; PTP over Ethernet
+ * test_ieee1588:
+ * jneq #0x88f7, drop_ieee1588 ; ETH_P_1588 ?
+ * ldb [14] ; load payload
+ * and #0x8 ; as we don't have ports here, test
+ * jneq #0x0, drop_ieee1588 ; for PTP_GEN_BIT and drop these
+ * ldh [14] ; reload payload
+ * and #0xf ; mask PTP_CLASS_VMASK
+ * or #0x30 ; PTP_CLASS_L2
+ * ret a ; return PTP class
+ * drop_ieee1588: ret #0x0 ; PTP_CLASS_NONE
+ */
+
+#include <linux/skbuff.h>
+#include <linux/filter.h>
+#include <linux/ptp_classify.h>
+
+static struct sk_filter *ptp_insns __read_mostly;
+
+unsigned int ptp_classify_raw(const struct sk_buff *skb)
+{
+ return SK_RUN_FILTER(ptp_insns, skb);
+}
+EXPORT_SYMBOL_GPL(ptp_classify_raw);
+
+void __init ptp_classifier_init(void)
+{
+ static struct sock_filter ptp_filter[] = {
+ { 0x28, 0, 0, 0x0000000c },
+ { 0x15, 0, 12, 0x00000800 },
+ { 0x30, 0, 0, 0x00000017 },
+ { 0x15, 0, 9, 0x00000011 },
+ { 0x28, 0, 0, 0x00000014 },
+ { 0x45, 7, 0, 0x00001fff },
+ { 0xb1, 0, 0, 0x0000000e },
+ { 0x48, 0, 0, 0x00000010 },
+ { 0x15, 0, 4, 0x0000013f },
+ { 0x48, 0, 0, 0x00000016 },
+ { 0x54, 0, 0, 0x0000000f },
+ { 0x44, 0, 0, 0x00000010 },
+ { 0x16, 0, 0, 0x00000000 },
+ { 0x06, 0, 0, 0x00000000 },
+ { 0x15, 0, 9, 0x000086dd },
+ { 0x30, 0, 0, 0x00000014 },
+ { 0x15, 0, 6, 0x00000011 },
+ { 0x28, 0, 0, 0x00000038 },
+ { 0x15, 0, 4, 0x0000013f },
+ { 0x28, 0, 0, 0x0000003e },
+ { 0x54, 0, 0, 0x0000000f },
+ { 0x44, 0, 0, 0x00000020 },
+ { 0x16, 0, 0, 0x00000000 },
+ { 0x06, 0, 0, 0x00000000 },
+ { 0x15, 0, 9, 0x00008100 },
+ { 0x28, 0, 0, 0x00000010 },
+ { 0x15, 0, 15, 0x000088f7 },
+ { 0x30, 0, 0, 0x00000012 },
+ { 0x54, 0, 0, 0x00000008 },
+ { 0x15, 0, 12, 0x00000000 },
+ { 0x28, 0, 0, 0x00000012 },
+ { 0x54, 0, 0, 0x0000000f },
+ { 0x44, 0, 0, 0x00000040 },
+ { 0x16, 0, 0, 0x00000000 },
+ { 0x15, 0, 7, 0x000088f7 },
+ { 0x30, 0, 0, 0x0000000e },
+ { 0x54, 0, 0, 0x00000008 },
+ { 0x15, 0, 4, 0x00000000 },
+ { 0x28, 0, 0, 0x0000000e },
+ { 0x54, 0, 0, 0x0000000f },
+ { 0x44, 0, 0, 0x00000030 },
+ { 0x16, 0, 0, 0x00000000 },
+ { 0x06, 0, 0, 0x00000000 },
+ };
+ struct sock_fprog ptp_prog = {
+ .len = ARRAY_SIZE(ptp_filter), .filter = ptp_filter,
+ };
+
+ BUG_ON(sk_unattached_filter_create(&ptp_insns, &ptp_prog));
+}
diff --git a/net/core/request_sock.c b/net/core/request_sock.c
index 4425148d2b51..467f326126e0 100644
--- a/net/core/request_sock.c
+++ b/net/core/request_sock.c
@@ -221,5 +221,4 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req,
out:
spin_unlock_bh(&fastopenq->lock);
sock_put(lsk);
- return;
}
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 120eecc0f5a4..d4ff41739b0f 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -822,6 +822,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
+ nla_total_size(4) /* IFLA_NUM_RX_QUEUES */
+ nla_total_size(1) /* IFLA_OPERSTATE */
+ nla_total_size(1) /* IFLA_LINKMODE */
+ + nla_total_size(4) /* IFLA_CARRIER_CHANGES */
+ nla_total_size(ext_filter_mask
& RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */
+ rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */
@@ -970,7 +971,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
(dev->qdisc &&
nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) ||
(dev->ifalias &&
- nla_put_string(skb, IFLA_IFALIAS, dev->ifalias)))
+ nla_put_string(skb, IFLA_IFALIAS, dev->ifalias)) ||
+ nla_put_u32(skb, IFLA_CARRIER_CHANGES,
+ atomic_read(&dev->carrier_changes)))
goto nla_put_failure;
if (1) {
@@ -1121,56 +1124,7 @@ nla_put_failure:
return -EMSGSIZE;
}
-static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
-{
- struct net *net = sock_net(skb->sk);
- int h, s_h;
- int idx = 0, s_idx;
- struct net_device *dev;
- struct hlist_head *head;
- struct nlattr *tb[IFLA_MAX+1];
- u32 ext_filter_mask = 0;
-
- s_h = cb->args[0];
- s_idx = cb->args[1];
-
- rcu_read_lock();
- cb->seq = net->dev_base_seq;
-
- if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
- ifla_policy) >= 0) {
-
- if (tb[IFLA_EXT_MASK])
- ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
- }
-
- for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
- idx = 0;
- head = &net->dev_index_head[h];
- hlist_for_each_entry_rcu(dev, head, index_hlist) {
- if (idx < s_idx)
- goto cont;
- if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
- NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq, 0,
- NLM_F_MULTI,
- ext_filter_mask) <= 0)
- goto out;
-
- nl_dump_check_consistent(cb, nlmsg_hdr(skb));
-cont:
- idx++;
- }
- }
-out:
- rcu_read_unlock();
- cb->args[1] = idx;
- cb->args[0] = h;
-
- return skb->len;
-}
-
-const struct nla_policy ifla_policy[IFLA_MAX+1] = {
+static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
[IFLA_IFNAME] = { .type = NLA_STRING, .len = IFNAMSIZ-1 },
[IFLA_ADDRESS] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
[IFLA_BROADCAST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
@@ -1196,8 +1150,8 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {
[IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 },
[IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 },
[IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_PORT_ID_LEN },
+ [IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, /* ignored */
};
-EXPORT_SYMBOL(ifla_policy);
static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
[IFLA_INFO_KIND] = { .type = NLA_STRING },
@@ -1235,6 +1189,61 @@ static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
[IFLA_PORT_RESPONSE] = { .type = NLA_U16, },
};
+static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ int h, s_h;
+ int idx = 0, s_idx;
+ struct net_device *dev;
+ struct hlist_head *head;
+ struct nlattr *tb[IFLA_MAX+1];
+ u32 ext_filter_mask = 0;
+
+ s_h = cb->args[0];
+ s_idx = cb->args[1];
+
+ rcu_read_lock();
+ cb->seq = net->dev_base_seq;
+
+ if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
+ ifla_policy) >= 0) {
+
+ if (tb[IFLA_EXT_MASK])
+ ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
+ }
+
+ for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
+ idx = 0;
+ head = &net->dev_index_head[h];
+ hlist_for_each_entry_rcu(dev, head, index_hlist) {
+ if (idx < s_idx)
+ goto cont;
+ if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, 0,
+ NLM_F_MULTI,
+ ext_filter_mask) <= 0)
+ goto out;
+
+ nl_dump_check_consistent(cb, nlmsg_hdr(skb));
+cont:
+ idx++;
+ }
+ }
+out:
+ rcu_read_unlock();
+ cb->args[1] = idx;
+ cb->args[0] = h;
+
+ return skb->len;
+}
+
+int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len)
+{
+ return nla_parse(tb, IFLA_MAX, head, len, ifla_policy);
+}
+EXPORT_SYMBOL(rtnl_nla_parse_ifla);
+
struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[])
{
struct net *net;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 90b96a11b974..30c7d35dd862 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3300,6 +3300,32 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
return elt;
}
+/* As compared with skb_to_sgvec, skb_to_sgvec_nomark only map skb to given
+ * sglist without mark the sg which contain last skb data as the end.
+ * So the caller can mannipulate sg list as will when padding new data after
+ * the first call without calling sg_unmark_end to expend sg list.
+ *
+ * Scenario to use skb_to_sgvec_nomark:
+ * 1. sg_init_table
+ * 2. skb_to_sgvec_nomark(payload1)
+ * 3. skb_to_sgvec_nomark(payload2)
+ *
+ * This is equivalent to:
+ * 1. sg_init_table
+ * 2. skb_to_sgvec(payload1)
+ * 3. sg_unmark_end
+ * 4. skb_to_sgvec(payload2)
+ *
+ * When mapping mutilple payload conditionally, skb_to_sgvec_nomark
+ * is more preferable.
+ */
+int skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg,
+ int offset, int len)
+{
+ return __skb_to_sgvec(skb, sg, offset, len);
+}
+EXPORT_SYMBOL_GPL(skb_to_sgvec_nomark);
+
int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
{
int nsg = __skb_to_sgvec(skb, sg, offset, len);
@@ -3562,15 +3588,47 @@ static int skb_maybe_pull_tail(struct sk_buff *skb, unsigned int len,
return 0;
}
+#define MAX_TCP_HDR_LEN (15 * 4)
+
+static __sum16 *skb_checksum_setup_ip(struct sk_buff *skb,
+ typeof(IPPROTO_IP) proto,
+ unsigned int off)
+{
+ switch (proto) {
+ int err;
+
+ case IPPROTO_TCP:
+ err = skb_maybe_pull_tail(skb, off + sizeof(struct tcphdr),
+ off + MAX_TCP_HDR_LEN);
+ if (!err && !skb_partial_csum_set(skb, off,
+ offsetof(struct tcphdr,
+ check)))
+ err = -EPROTO;
+ return err ? ERR_PTR(err) : &tcp_hdr(skb)->check;
+
+ case IPPROTO_UDP:
+ err = skb_maybe_pull_tail(skb, off + sizeof(struct udphdr),
+ off + sizeof(struct udphdr));
+ if (!err && !skb_partial_csum_set(skb, off,
+ offsetof(struct udphdr,
+ check)))
+ err = -EPROTO;
+ return err ? ERR_PTR(err) : &udp_hdr(skb)->check;
+ }
+
+ return ERR_PTR(-EPROTO);
+}
+
/* This value should be large enough to cover a tagged ethernet header plus
* maximally sized IP and TCP or UDP headers.
*/
#define MAX_IP_HDR_LEN 128
-static int skb_checksum_setup_ip(struct sk_buff *skb, bool recalculate)
+static int skb_checksum_setup_ipv4(struct sk_buff *skb, bool recalculate)
{
unsigned int off;
bool fragment;
+ __sum16 *csum;
int err;
fragment = false;
@@ -3591,51 +3649,15 @@ static int skb_checksum_setup_ip(struct sk_buff *skb, bool recalculate)
if (fragment)
goto out;
- switch (ip_hdr(skb)->protocol) {
- case IPPROTO_TCP:
- err = skb_maybe_pull_tail(skb,
- off + sizeof(struct tcphdr),
- MAX_IP_HDR_LEN);
- if (err < 0)
- goto out;
-
- if (!skb_partial_csum_set(skb, off,
- offsetof(struct tcphdr, check))) {
- err = -EPROTO;
- goto out;
- }
-
- if (recalculate)
- tcp_hdr(skb)->check =
- ~csum_tcpudp_magic(ip_hdr(skb)->saddr,
- ip_hdr(skb)->daddr,
- skb->len - off,
- IPPROTO_TCP, 0);
- break;
- case IPPROTO_UDP:
- err = skb_maybe_pull_tail(skb,
- off + sizeof(struct udphdr),
- MAX_IP_HDR_LEN);
- if (err < 0)
- goto out;
-
- if (!skb_partial_csum_set(skb, off,
- offsetof(struct udphdr, check))) {
- err = -EPROTO;
- goto out;
- }
-
- if (recalculate)
- udp_hdr(skb)->check =
- ~csum_tcpudp_magic(ip_hdr(skb)->saddr,
- ip_hdr(skb)->daddr,
- skb->len - off,
- IPPROTO_UDP, 0);
- break;
- default:
- goto out;
- }
+ csum = skb_checksum_setup_ip(skb, ip_hdr(skb)->protocol, off);
+ if (IS_ERR(csum))
+ return PTR_ERR(csum);
+ if (recalculate)
+ *csum = ~csum_tcpudp_magic(ip_hdr(skb)->saddr,
+ ip_hdr(skb)->daddr,
+ skb->len - off,
+ ip_hdr(skb)->protocol, 0);
err = 0;
out:
@@ -3658,6 +3680,7 @@ static int skb_checksum_setup_ipv6(struct sk_buff *skb, bool recalculate)
unsigned int len;
bool fragment;
bool done;
+ __sum16 *csum;
fragment = false;
done = false;
@@ -3735,51 +3758,14 @@ static int skb_checksum_setup_ipv6(struct sk_buff *skb, bool recalculate)
if (!done || fragment)
goto out;
- switch (nexthdr) {
- case IPPROTO_TCP:
- err = skb_maybe_pull_tail(skb,
- off + sizeof(struct tcphdr),
- MAX_IPV6_HDR_LEN);
- if (err < 0)
- goto out;
-
- if (!skb_partial_csum_set(skb, off,
- offsetof(struct tcphdr, check))) {
- err = -EPROTO;
- goto out;
- }
-
- if (recalculate)
- tcp_hdr(skb)->check =
- ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
- &ipv6_hdr(skb)->daddr,
- skb->len - off,
- IPPROTO_TCP, 0);
- break;
- case IPPROTO_UDP:
- err = skb_maybe_pull_tail(skb,
- off + sizeof(struct udphdr),
- MAX_IPV6_HDR_LEN);
- if (err < 0)
- goto out;
-
- if (!skb_partial_csum_set(skb, off,
- offsetof(struct udphdr, check))) {
- err = -EPROTO;
- goto out;
- }
-
- if (recalculate)
- udp_hdr(skb)->check =
- ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
- &ipv6_hdr(skb)->daddr,
- skb->len - off,
- IPPROTO_UDP, 0);
- break;
- default:
- goto out;
- }
+ csum = skb_checksum_setup_ip(skb, nexthdr, off);
+ if (IS_ERR(csum))
+ return PTR_ERR(csum);
+ if (recalculate)
+ *csum = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+ skb->len - off, nexthdr, 0);
err = 0;
out:
@@ -3797,7 +3783,7 @@ int skb_checksum_setup(struct sk_buff *skb, bool recalculate)
switch (skb->protocol) {
case htons(ETH_P_IP):
- err = skb_checksum_setup_ip(skb, recalculate);
+ err = skb_checksum_setup_ipv4(skb, recalculate);
break;
case htons(ETH_P_IPV6):
diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c
index a0e9cf6379de..d7af18859322 100644
--- a/net/core/sock_diag.c
+++ b/net/core/sock_diag.c
@@ -52,9 +52,10 @@ EXPORT_SYMBOL_GPL(sock_diag_put_meminfo);
int sock_diag_put_filterinfo(struct user_namespace *user_ns, struct sock *sk,
struct sk_buff *skb, int attrtype)
{
- struct nlattr *attr;
+ struct sock_fprog_kern *fprog;
struct sk_filter *filter;
- unsigned int len;
+ struct nlattr *attr;
+ unsigned int flen;
int err = 0;
if (!ns_capable(user_ns, CAP_NET_ADMIN)) {
@@ -63,24 +64,20 @@ int sock_diag_put_filterinfo(struct user_namespace *user_ns, struct sock *sk,
}
rcu_read_lock();
-
filter = rcu_dereference(sk->sk_filter);
- len = filter ? filter->len * sizeof(struct sock_filter) : 0;
+ if (!filter)
+ goto out;
- attr = nla_reserve(skb, attrtype, len);
+ fprog = filter->orig_prog;
+ flen = sk_filter_proglen(fprog);
+
+ attr = nla_reserve(skb, attrtype, flen);
if (attr == NULL) {
err = -EMSGSIZE;
goto out;
}
- if (filter) {
- struct sock_filter *fb = (struct sock_filter *)nla_data(attr);
- int i;
-
- for (i = 0; i < filter->len; i++, fb++)
- sk_decode_filter(&filter->insns[i], fb);
- }
-
+ memcpy(nla_data(attr), fprog->filter, flen);
out:
rcu_read_unlock();
return err;
diff --git a/net/core/timestamping.c b/net/core/timestamping.c
index 661b5a40ec10..6521dfd8b7c8 100644
--- a/net/core/timestamping.c
+++ b/net/core/timestamping.c
@@ -23,16 +23,11 @@
#include <linux/skbuff.h>
#include <linux/export.h>
-static struct sock_filter ptp_filter[] = {
- PTP_FILTER
-};
-
static unsigned int classify(const struct sk_buff *skb)
{
- if (likely(skb->dev &&
- skb->dev->phydev &&
+ if (likely(skb->dev && skb->dev->phydev &&
skb->dev->phydev->drv))
- return sk_run_filter(skb, ptp_filter);
+ return ptp_classify_raw(skb);
else
return PTP_CLASS_NONE;
}
@@ -60,11 +55,13 @@ void skb_clone_tx_timestamp(struct sk_buff *skb)
if (likely(phydev->drv->txtstamp)) {
if (!atomic_inc_not_zero(&sk->sk_refcnt))
return;
+
clone = skb_clone(skb, GFP_ATOMIC);
if (!clone) {
sock_put(sk);
return;
}
+
clone->sk = sk;
phydev->drv->txtstamp(phydev, clone, type);
}
@@ -89,12 +86,15 @@ void skb_complete_tx_timestamp(struct sk_buff *skb,
}
*skb_hwtstamps(skb) = *hwtstamps;
+
serr = SKB_EXT_ERR(skb);
memset(serr, 0, sizeof(*serr));
serr->ee.ee_errno = ENOMSG;
serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING;
skb->sk = NULL;
+
err = sock_queue_err_skb(sk, skb);
+
sock_put(sk);
if (err)
kfree_skb(skb);
@@ -132,8 +132,3 @@ bool skb_defer_rx_timestamp(struct sk_buff *skb)
return false;
}
EXPORT_SYMBOL_GPL(skb_defer_rx_timestamp);
-
-void __init skb_timestamping_init(void)
-{
- BUG_ON(sk_chk_filter(ptp_filter, ARRAY_SIZE(ptp_filter)));
-}
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index cac505f166d5..e5302b7f7ca9 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -209,7 +209,7 @@ static int slave_xmit(struct sk_buff *skb, struct hsr_priv *hsr_priv,
/* Address substitution (IEC62439-3 pp 26, 50): replace mac
* address of outgoing frame with that of the outgoing slave's.
*/
- memcpy(hsr_ethhdr->ethhdr.h_source, skb->dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(hsr_ethhdr->ethhdr.h_source, skb->dev->dev_addr);
return dev_queue_xmit(skb);
}
@@ -346,7 +346,7 @@ static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type)
/* Payload: MacAddressA */
hsr_sp = (typeof(hsr_sp)) skb_put(skb, sizeof(*hsr_sp));
- memcpy(hsr_sp->MacAddressA, hsr_dev->dev_addr, ETH_ALEN);
+ ether_addr_copy(hsr_sp->MacAddressA, hsr_dev->dev_addr);
dev_queue_xmit(skb);
return;
@@ -493,7 +493,7 @@ static int check_slave_ok(struct net_device *dev)
/* Default multicast address for HSR Supervision frames */
-static const unsigned char def_multicast_addr[ETH_ALEN] = {
+static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = {
0x01, 0x15, 0x4e, 0x00, 0x01, 0x00
};
@@ -519,7 +519,7 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
hsr_priv->announce_timer.function = hsr_announce;
hsr_priv->announce_timer.data = (unsigned long) hsr_priv;
- memcpy(hsr_priv->sup_multicast_addr, def_multicast_addr, ETH_ALEN);
+ ether_addr_copy(hsr_priv->sup_multicast_addr, def_multicast_addr);
hsr_priv->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec;
/* FIXME: should I modify the value of these?
@@ -547,7 +547,7 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
hsr_dev->features |= NETIF_F_VLAN_CHALLENGED;
/* Set hsr_dev's MAC address to that of mac_slave1 */
- memcpy(hsr_dev->dev_addr, hsr_priv->slave[0]->dev_addr, ETH_ALEN);
+ ether_addr_copy(hsr_dev->dev_addr, hsr_priv->slave[0]->dev_addr);
/* Set required header length */
for (i = 0; i < HSR_MAX_SLAVE; i++) {
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index 7ae0d7f6dbd0..83e58449366a 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -108,8 +108,8 @@ int hsr_create_self_node(struct list_head *self_node_db,
if (!node)
return -ENOMEM;
- memcpy(node->MacAddressA, addr_a, ETH_ALEN);
- memcpy(node->MacAddressB, addr_b, ETH_ALEN);
+ ether_addr_copy(node->MacAddressA, addr_a);
+ ether_addr_copy(node->MacAddressB, addr_b);
rcu_read_lock();
oldnode = list_first_or_null_rcu(self_node_db,
@@ -199,7 +199,7 @@ struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv,
/* Node is known, but frame was received from an unknown
* address. Node is PICS_SUBS capable; merge its AddrB.
*/
- memcpy(node->MacAddressB, hsr_ethsup->ethhdr.h_source, ETH_ALEN);
+ ether_addr_copy(node->MacAddressB, hsr_ethsup->ethhdr.h_source);
node->AddrB_if = dev_idx;
return node;
}
@@ -208,8 +208,8 @@ struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv,
if (!node)
return NULL;
- memcpy(node->MacAddressA, hsr_sp->MacAddressA, ETH_ALEN);
- memcpy(node->MacAddressB, hsr_ethsup->ethhdr.h_source, ETH_ALEN);
+ ether_addr_copy(node->MacAddressA, hsr_sp->MacAddressA);
+ ether_addr_copy(node->MacAddressB, hsr_ethsup->ethhdr.h_source);
if (!ether_addr_equal(hsr_sp->MacAddressA, hsr_ethsup->ethhdr.h_source))
node->AddrB_if = dev_idx;
else
@@ -250,7 +250,7 @@ void hsr_addr_subst_source(struct hsr_priv *hsr_priv, struct sk_buff *skb)
rcu_read_lock();
node = find_node_by_AddrB(&hsr_priv->node_db, ethhdr->h_source);
if (node)
- memcpy(ethhdr->h_source, node->MacAddressA, ETH_ALEN);
+ ether_addr_copy(ethhdr->h_source, node->MacAddressA);
rcu_read_unlock();
}
@@ -272,7 +272,7 @@ void hsr_addr_subst_dest(struct hsr_priv *hsr_priv, struct ethhdr *ethhdr,
rcu_read_lock();
node = find_node_by_AddrA(&hsr_priv->node_db, ethhdr->h_dest);
if (node && (node->AddrB_if == dev_idx))
- memcpy(ethhdr->h_dest, node->MacAddressB, ETH_ALEN);
+ ether_addr_copy(ethhdr->h_dest, node->MacAddressB);
rcu_read_unlock();
}
@@ -428,13 +428,13 @@ void *hsr_get_next_node(struct hsr_priv *hsr_priv, void *_pos,
node = list_first_or_null_rcu(&hsr_priv->node_db,
struct node_entry, mac_list);
if (node)
- memcpy(addr, node->MacAddressA, ETH_ALEN);
+ ether_addr_copy(addr, node->MacAddressA);
return node;
}
node = _pos;
list_for_each_entry_continue_rcu(node, &hsr_priv->node_db, mac_list) {
- memcpy(addr, node->MacAddressA, ETH_ALEN);
+ ether_addr_copy(addr, node->MacAddressA);
return node;
}
@@ -462,7 +462,7 @@ int hsr_get_node_data(struct hsr_priv *hsr_priv,
return -ENOENT; /* No such entry */
}
- memcpy(addr_b, node->MacAddressB, ETH_ALEN);
+ ether_addr_copy(addr_b, node->MacAddressB);
tdiff = jiffies - node->time_in[HSR_DEV_SLAVE_A];
if (node->time_in_stale[HSR_DEV_SLAVE_A])
diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c
index af68dd83a4e3..3fee5218a691 100644
--- a/net/hsr/hsr_main.c
+++ b/net/hsr/hsr_main.c
@@ -138,8 +138,8 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
break;
if (dev == hsr_priv->slave[0])
- memcpy(hsr_priv->dev->dev_addr,
- hsr_priv->slave[0]->dev_addr, ETH_ALEN);
+ ether_addr_copy(hsr_priv->dev->dev_addr,
+ hsr_priv->slave[0]->dev_addr);
/* Make sure we recognize frames from ourselves in hsr_rcv() */
res = hsr_create_self_node(&hsr_priv->self_node_db,
@@ -459,7 +459,7 @@ static int __init hsr_init(void)
static void __exit hsr_exit(void)
{
unregister_netdevice_notifier(&hsr_nb);
- del_timer(&prune_timer);
+ del_timer_sync(&prune_timer);
hsr_netlink_exit();
dev_remove_pack(&hsr_pt);
}
diff --git a/net/ieee802154/6lowpan_iphc.c b/net/ieee802154/6lowpan_iphc.c
index 860aa2d445ba..211b5686d719 100644
--- a/net/ieee802154/6lowpan_iphc.c
+++ b/net/ieee802154/6lowpan_iphc.c
@@ -54,11 +54,10 @@
#include <linux/if_arp.h>
#include <linux/module.h>
#include <linux/netdevice.h>
+#include <net/6lowpan.h>
#include <net/ipv6.h>
#include <net/af_ieee802154.h>
-#include "6lowpan.h"
-
/*
* Uncompress address function for source and
* destination address(non-multicast).
diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan_rtnl.c
index 8edfea5da572..0f5a69ed746d 100644
--- a/net/ieee802154/6lowpan.c
+++ b/net/ieee802154/6lowpan_rtnl.c
@@ -1,10 +1,8 @@
-/*
- * Copyright 2011, Siemens AG
+/* Copyright 2011, Siemens AG
* written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*/
-/*
- * Based on patches from Jon Smirl <jonsmirl@gmail.com>
+/* Based on patches from Jon Smirl <jonsmirl@gmail.com>
* Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -15,10 +13,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.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Jon's code is based on 6lowpan implementation for Contiki which is:
@@ -58,9 +52,10 @@
#include <net/af_ieee802154.h>
#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
+#include <net/6lowpan.h>
#include <net/ipv6.h>
-#include "6lowpan.h"
+#include "reassembly.h"
static LIST_HEAD(lowpan_devices);
@@ -68,7 +63,7 @@ static LIST_HEAD(lowpan_devices);
struct lowpan_dev_info {
struct net_device *real_dev; /* real WPAN device ptr */
struct mutex dev_list_mtx; /* mutex for list ops */
- unsigned short fragment_tag;
+ __be16 fragment_tag;
};
struct lowpan_dev_record {
@@ -76,18 +71,6 @@ struct lowpan_dev_record {
struct list_head list;
};
-struct lowpan_fragment {
- struct sk_buff *skb; /* skb to be assembled */
- u16 length; /* length to be assemled */
- u32 bytes_rcv; /* bytes received */
- u16 tag; /* current fragment tag */
- struct timer_list timer; /* assembling timer */
- struct list_head list; /* fragments list */
-};
-
-static LIST_HEAD(lowpan_fragments);
-static DEFINE_SPINLOCK(flist_lock);
-
static inline struct
lowpan_dev_info *lowpan_dev_info(const struct net_device *dev)
{
@@ -124,13 +107,11 @@ static int lowpan_header_create(struct sk_buff *skb,
lowpan_header_compress(skb, dev, type, daddr, saddr, len);
- /*
- * NOTE1: I'm still unsure about the fact that compression and WPAN
+ /* NOTE1: I'm still unsure about the fact that compression and WPAN
* header are created here and not later in the xmit. So wait for
* an opinion of net maintainers.
*/
- /*
- * NOTE2: to be absolutely correct, we must derive PANid information
+ /* NOTE2: to be absolutely correct, we must derive PANid information
* from MAC subif of the 'dev' and 'real_dev' network devices, but
* this isn't implemented in mainline yet, so currently we assign 0xff
*/
@@ -138,30 +119,29 @@ static int lowpan_header_create(struct sk_buff *skb,
mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
/* prepare wpan address data */
- sa.addr_type = IEEE802154_ADDR_LONG;
+ sa.mode = IEEE802154_ADDR_LONG;
sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
+ sa.extended_addr = ieee802154_devaddr_from_raw(saddr);
- memcpy(&(sa.hwaddr), saddr, 8);
/* intra-PAN communications */
- da.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
+ da.pan_id = sa.pan_id;
- /*
- * if the destination address is the broadcast address, use the
+ /* if the destination address is the broadcast address, use the
* corresponding short address
*/
if (lowpan_is_addr_broadcast(daddr)) {
- da.addr_type = IEEE802154_ADDR_SHORT;
- da.short_addr = IEEE802154_ADDR_BROADCAST;
+ da.mode = IEEE802154_ADDR_SHORT;
+ da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
} else {
- da.addr_type = IEEE802154_ADDR_LONG;
- memcpy(&(da.hwaddr), daddr, IEEE802154_ADDR_LEN);
+ da.mode = IEEE802154_ADDR_LONG;
+ da.extended_addr = ieee802154_devaddr_from_raw(daddr);
/* request acknowledgment */
mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
}
return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
- type, (void *)&da, (void *)&sa, skb->len);
+ type, (void *)&da, (void *)&sa, 0);
}
static int lowpan_give_skb_to_devices(struct sk_buff *skb,
@@ -188,73 +168,11 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb,
return stat;
}
-static void lowpan_fragment_timer_expired(unsigned long entry_addr)
-{
- struct lowpan_fragment *entry = (struct lowpan_fragment *)entry_addr;
-
- pr_debug("timer expired for frame with tag %d\n", entry->tag);
-
- list_del(&entry->list);
- dev_kfree_skb(entry->skb);
- kfree(entry);
-}
-
-static struct lowpan_fragment *
-lowpan_alloc_new_frame(struct sk_buff *skb, u16 len, u16 tag)
-{
- struct lowpan_fragment *frame;
-
- frame = kzalloc(sizeof(struct lowpan_fragment),
- GFP_ATOMIC);
- if (!frame)
- goto frame_err;
-
- INIT_LIST_HEAD(&frame->list);
-
- frame->length = len;
- frame->tag = tag;
-
- /* allocate buffer for frame assembling */
- frame->skb = netdev_alloc_skb_ip_align(skb->dev, frame->length +
- sizeof(struct ipv6hdr));
-
- if (!frame->skb)
- goto skb_err;
-
- frame->skb->priority = skb->priority;
-
- /* reserve headroom for uncompressed ipv6 header */
- skb_reserve(frame->skb, sizeof(struct ipv6hdr));
- skb_put(frame->skb, frame->length);
-
- /* copy the first control block to keep a
- * trace of the link-layer addresses in case
- * of a link-local compressed address
- */
- memcpy(frame->skb->cb, skb->cb, sizeof(skb->cb));
-
- init_timer(&frame->timer);
- /* time out is the same as for ipv6 - 60 sec */
- frame->timer.expires = jiffies + LOWPAN_FRAG_TIMEOUT;
- frame->timer.data = (unsigned long)frame;
- frame->timer.function = lowpan_fragment_timer_expired;
-
- add_timer(&frame->timer);
-
- list_add_tail(&frame->list, &lowpan_fragments);
-
- return frame;
-
-skb_err:
- kfree(frame);
-frame_err:
- return NULL;
-}
-
-static int process_data(struct sk_buff *skb)
+static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
{
u8 iphc0, iphc1;
- const struct ieee802154_addr *_saddr, *_daddr;
+ struct ieee802154_addr_sa sa, da;
+ void *sap, *dap;
raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len);
/* at least two bytes will be used for the encoding */
@@ -264,108 +182,27 @@ static int process_data(struct sk_buff *skb)
if (lowpan_fetch_skb_u8(skb, &iphc0))
goto drop;
- /* fragments assembling */
- switch (iphc0 & LOWPAN_DISPATCH_MASK) {
- case LOWPAN_DISPATCH_FRAG1:
- case LOWPAN_DISPATCH_FRAGN:
- {
- struct lowpan_fragment *frame;
- /* slen stores the rightmost 8 bits of the 11 bits length */
- u8 slen, offset = 0;
- u16 len, tag;
- bool found = false;
-
- if (lowpan_fetch_skb_u8(skb, &slen) || /* frame length */
- lowpan_fetch_skb_u16(skb, &tag)) /* fragment tag */
- goto drop;
-
- /* adds the 3 MSB to the 8 LSB to retrieve the 11 bits length */
- len = ((iphc0 & 7) << 8) | slen;
-
- if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1) {
- pr_debug("%s received a FRAG1 packet (tag: %d, "
- "size of the entire IP packet: %d)",
- __func__, tag, len);
- } else { /* FRAGN */
- if (lowpan_fetch_skb_u8(skb, &offset))
- goto unlock_and_drop;
- pr_debug("%s received a FRAGN packet (tag: %d, "
- "size of the entire IP packet: %d, "
- "offset: %d)", __func__, tag, len, offset * 8);
- }
-
- /*
- * check if frame assembling with the same tag is
- * already in progress
- */
- spin_lock_bh(&flist_lock);
-
- list_for_each_entry(frame, &lowpan_fragments, list)
- if (frame->tag == tag) {
- found = true;
- break;
- }
-
- /* alloc new frame structure */
- if (!found) {
- pr_debug("%s first fragment received for tag %d, "
- "begin packet reassembly", __func__, tag);
- frame = lowpan_alloc_new_frame(skb, len, tag);
- if (!frame)
- goto unlock_and_drop;
- }
-
- /* if payload fits buffer, copy it */
- if (likely((offset * 8 + skb->len) <= frame->length))
- skb_copy_to_linear_data_offset(frame->skb, offset * 8,
- skb->data, skb->len);
- else
- goto unlock_and_drop;
-
- frame->bytes_rcv += skb->len;
-
- /* frame assembling complete */
- if ((frame->bytes_rcv == frame->length) &&
- frame->timer.expires > jiffies) {
- /* if timer haven't expired - first of all delete it */
- del_timer_sync(&frame->timer);
- list_del(&frame->list);
- spin_unlock_bh(&flist_lock);
-
- pr_debug("%s successfully reassembled fragment "
- "(tag %d)", __func__, tag);
-
- dev_kfree_skb(skb);
- skb = frame->skb;
- kfree(frame);
-
- if (lowpan_fetch_skb_u8(skb, &iphc0))
- goto drop;
-
- break;
- }
- spin_unlock_bh(&flist_lock);
-
- return kfree_skb(skb), 0;
- }
- default:
- break;
- }
-
if (lowpan_fetch_skb_u8(skb, &iphc1))
goto drop;
- _saddr = &mac_cb(skb)->sa;
- _daddr = &mac_cb(skb)->da;
+ ieee802154_addr_to_sa(&sa, &hdr->source);
+ ieee802154_addr_to_sa(&da, &hdr->dest);
+
+ if (sa.addr_type == IEEE802154_ADDR_SHORT)
+ sap = &sa.short_addr;
+ else
+ sap = &sa.hwaddr;
- return lowpan_process_data(skb, skb->dev, (u8 *)_saddr->hwaddr,
- _saddr->addr_type, IEEE802154_ADDR_LEN,
- (u8 *)_daddr->hwaddr, _daddr->addr_type,
- IEEE802154_ADDR_LEN, iphc0, iphc1,
- lowpan_give_skb_to_devices);
+ if (da.addr_type == IEEE802154_ADDR_SHORT)
+ dap = &da.short_addr;
+ else
+ dap = &da.hwaddr;
+
+ return lowpan_process_data(skb, skb->dev, sap, sa.addr_type,
+ IEEE802154_ADDR_LEN, dap, da.addr_type,
+ IEEE802154_ADDR_LEN, iphc0, iphc1,
+ lowpan_give_skb_to_devices);
-unlock_and_drop:
- spin_unlock_bh(&flist_lock);
drop:
kfree_skb(skb);
return -EINVAL;
@@ -386,7 +223,7 @@ static int lowpan_set_address(struct net_device *dev, void *p)
static int
lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
- int mlen, int plen, int offset, int type)
+ int mlen, int plen, int offset, int type)
{
struct sk_buff *frag;
int hlen;
@@ -422,51 +259,68 @@ lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
static int
lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev)
{
- int err, header_length, payload_length, tag, offset = 0;
+ int err;
+ u16 dgram_offset, dgram_size, payload_length, header_length,
+ lowpan_size, frag_plen, offset;
+ __be16 tag;
u8 head[5];
header_length = skb->mac_len;
payload_length = skb->len - header_length;
tag = lowpan_dev_info(dev)->fragment_tag++;
+ lowpan_size = skb_network_header_len(skb);
+ dgram_size = lowpan_uncompress_size(skb, &dgram_offset) -
+ header_length;
/* first fragment header */
- head[0] = LOWPAN_DISPATCH_FRAG1 | ((payload_length >> 8) & 0x7);
- head[1] = payload_length & 0xff;
- head[2] = tag >> 8;
- head[3] = tag & 0xff;
+ head[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x7);
+ head[1] = dgram_size & 0xff;
+ memcpy(head + 2, &tag, sizeof(tag));
- err = lowpan_fragment_xmit(skb, head, header_length, LOWPAN_FRAG_SIZE,
- 0, LOWPAN_DISPATCH_FRAG1);
+ /* calc the nearest payload length(divided to 8) for first fragment
+ * which fits into a IEEE802154_MTU
+ */
+ frag_plen = round_down(IEEE802154_MTU - header_length -
+ LOWPAN_FRAG1_HEAD_SIZE - lowpan_size -
+ IEEE802154_MFR_SIZE, 8);
+ err = lowpan_fragment_xmit(skb, head, header_length,
+ frag_plen + lowpan_size, 0,
+ LOWPAN_DISPATCH_FRAG1);
if (err) {
pr_debug("%s unable to send FRAG1 packet (tag: %d)",
__func__, tag);
goto exit;
}
- offset = LOWPAN_FRAG_SIZE;
+ offset = lowpan_size + frag_plen;
+ dgram_offset += frag_plen;
/* next fragment header */
head[0] &= ~LOWPAN_DISPATCH_FRAG1;
head[0] |= LOWPAN_DISPATCH_FRAGN;
+ frag_plen = round_down(IEEE802154_MTU - header_length -
+ LOWPAN_FRAGN_HEAD_SIZE - IEEE802154_MFR_SIZE, 8);
+
while (payload_length - offset > 0) {
- int len = LOWPAN_FRAG_SIZE;
+ int len = frag_plen;
- head[4] = offset / 8;
+ head[4] = dgram_offset >> 3;
if (payload_length - offset < len)
len = payload_length - offset;
- err = lowpan_fragment_xmit(skb, head, header_length,
- len, offset, LOWPAN_DISPATCH_FRAGN);
+ err = lowpan_fragment_xmit(skb, head, header_length, len,
+ offset, LOWPAN_DISPATCH_FRAGN);
if (err) {
- pr_debug("%s unable to send a subsequent FRAGN packet "
- "(tag: %d, offset: %d", __func__, tag, offset);
+ pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
+ __func__, tag, offset);
goto exit;
}
offset += len;
+ dgram_offset += len;
}
exit:
@@ -508,13 +362,13 @@ static struct wpan_phy *lowpan_get_phy(const struct net_device *dev)
return ieee802154_mlme_ops(real_dev)->get_phy(real_dev);
}
-static u16 lowpan_get_pan_id(const struct net_device *dev)
+static __le16 lowpan_get_pan_id(const struct net_device *dev)
{
struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev);
}
-static u16 lowpan_get_short_addr(const struct net_device *dev)
+static __le16 lowpan_get_short_addr(const struct net_device *dev)
{
struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev);
@@ -593,45 +447,55 @@ static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[])
static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
- struct sk_buff *local_skb;
+ struct ieee802154_hdr hdr;
+ int ret;
- if (!netif_running(dev))
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
goto drop;
+ if (!netif_running(dev))
+ goto drop_skb;
+
if (dev->type != ARPHRD_IEEE802154)
- goto drop;
+ goto drop_skb;
+
+ if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
+ goto drop_skb;
/* check that it's our buffer */
if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
- /* Copy the packet so that the IPv6 header is
- * properly aligned.
- */
- local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1,
- skb_tailroom(skb), GFP_ATOMIC);
- if (!local_skb)
- goto drop;
-
- local_skb->protocol = htons(ETH_P_IPV6);
- local_skb->pkt_type = PACKET_HOST;
+ skb->protocol = htons(ETH_P_IPV6);
+ skb->pkt_type = PACKET_HOST;
/* Pull off the 1-byte of 6lowpan header. */
- skb_pull(local_skb, 1);
-
- lowpan_give_skb_to_devices(local_skb, NULL);
+ skb_pull(skb, 1);
- kfree_skb(local_skb);
- kfree_skb(skb);
+ ret = lowpan_give_skb_to_devices(skb, NULL);
+ if (ret == NET_RX_DROP)
+ goto drop;
} else {
switch (skb->data[0] & 0xe0) {
case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
+ ret = process_data(skb, &hdr);
+ if (ret == NET_RX_DROP)
+ goto drop;
+ break;
case LOWPAN_DISPATCH_FRAG1: /* first fragment header */
+ ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1);
+ if (ret == 1) {
+ ret = process_data(skb, &hdr);
+ if (ret == NET_RX_DROP)
+ goto drop;
+ }
+ break;
case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */
- local_skb = skb_clone(skb, GFP_ATOMIC);
- if (!local_skb)
- goto drop;
- process_data(local_skb);
-
- kfree_skb(skb);
+ ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN);
+ if (ret == 1) {
+ ret = process_data(skb, &hdr);
+ if (ret == NET_RX_DROP)
+ goto drop;
+ }
break;
default:
break;
@@ -639,9 +503,9 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
}
return NET_RX_SUCCESS;
-
-drop:
+drop_skb:
kfree_skb(skb);
+drop:
return NET_RX_DROP;
}
@@ -665,10 +529,9 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
}
lowpan_dev_info(dev)->real_dev = real_dev;
- lowpan_dev_info(dev)->fragment_tag = 0;
mutex_init(&lowpan_dev_info(dev)->dev_list_mtx);
- entry = kzalloc(sizeof(struct lowpan_dev_record), GFP_KERNEL);
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
dev_put(real_dev);
lowpan_dev_info(dev)->real_dev = NULL;
@@ -761,7 +624,7 @@ static struct notifier_block lowpan_dev_notifier = {
};
static struct packet_type lowpan_packet_type = {
- .type = __constant_htons(ETH_P_IEEE802154),
+ .type = htons(ETH_P_IEEE802154),
.func = lowpan_rcv,
};
@@ -769,43 +632,40 @@ static int __init lowpan_init_module(void)
{
int err = 0;
- err = lowpan_netlink_init();
+ err = lowpan_net_frag_init();
if (err < 0)
goto out;
+ err = lowpan_netlink_init();
+ if (err < 0)
+ goto out_frag;
+
dev_add_pack(&lowpan_packet_type);
err = register_netdevice_notifier(&lowpan_dev_notifier);
- if (err < 0) {
- dev_remove_pack(&lowpan_packet_type);
- lowpan_netlink_fini();
- }
+ if (err < 0)
+ goto out_pack;
+
+ return 0;
+
+out_pack:
+ dev_remove_pack(&lowpan_packet_type);
+ lowpan_netlink_fini();
+out_frag:
+ lowpan_net_frag_exit();
out:
return err;
}
static void __exit lowpan_cleanup_module(void)
{
- struct lowpan_fragment *frame, *tframe;
-
lowpan_netlink_fini();
dev_remove_pack(&lowpan_packet_type);
- unregister_netdevice_notifier(&lowpan_dev_notifier);
+ lowpan_net_frag_exit();
- /* Now 6lowpan packet_type is removed, so no new fragments are
- * expected on RX, therefore that's the time to clean incomplete
- * fragments.
- */
- spin_lock_bh(&flist_lock);
- list_for_each_entry_safe(frame, tframe, &lowpan_fragments, list) {
- del_timer_sync(&frame->timer);
- list_del(&frame->list);
- dev_kfree_skb(frame->skb);
- kfree(frame);
- }
- spin_unlock_bh(&flist_lock);
+ unregister_netdevice_notifier(&lowpan_dev_notifier);
}
module_init(lowpan_init_module);
diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig
index 9c9879d5ea64..8af1330b3137 100644
--- a/net/ieee802154/Kconfig
+++ b/net/ieee802154/Kconfig
@@ -15,7 +15,7 @@ config IEEE802154_6LOWPAN
depends on IEEE802154 && IPV6
select 6LOWPAN_IPHC
---help---
- IPv6 compression over IEEE 802.15.4.
+ IPv6 compression over IEEE 802.15.4.
config 6LOWPAN_IPHC
tristate
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index e8f05885ced6..bf1b51497a41 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -2,5 +2,9 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o
obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o
-ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o
+6lowpan-y := 6lowpan_rtnl.o reassembly.o
+ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o \
+ header_ops.o
af_802154-y := af_ieee802154.o raw.o dgram.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/ieee802154/af802154.h b/net/ieee802154/af802154.h
index b1ec52537522..8330a09bfc95 100644
--- a/net/ieee802154/af802154.h
+++ b/net/ieee802154/af802154.h
@@ -25,12 +25,13 @@
#define AF802154_H
struct sk_buff;
-struct net_devce;
+struct net_device;
+struct ieee802154_addr;
extern struct proto ieee802154_raw_prot;
extern struct proto ieee802154_dgram_prot;
void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb);
int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb);
struct net_device *ieee802154_get_dev(struct net *net,
- struct ieee802154_addr *addr);
+ const struct ieee802154_addr *addr);
#endif
diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c
index 40e606f3788f..351d9a94ec2f 100644
--- a/net/ieee802154/af_ieee802154.c
+++ b/net/ieee802154/af_ieee802154.c
@@ -43,25 +43,27 @@
/*
* Utility function for families
*/
-struct net_device *ieee802154_get_dev(struct net *net,
- struct ieee802154_addr *addr)
+struct net_device*
+ieee802154_get_dev(struct net *net, const struct ieee802154_addr *addr)
{
struct net_device *dev = NULL;
struct net_device *tmp;
- u16 pan_id, short_addr;
+ __le16 pan_id, short_addr;
+ u8 hwaddr[IEEE802154_ADDR_LEN];
- switch (addr->addr_type) {
+ switch (addr->mode) {
case IEEE802154_ADDR_LONG:
+ ieee802154_devaddr_to_raw(hwaddr, addr->extended_addr);
rcu_read_lock();
- dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, addr->hwaddr);
+ dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, hwaddr);
if (dev)
dev_hold(dev);
rcu_read_unlock();
break;
case IEEE802154_ADDR_SHORT:
- if (addr->pan_id == 0xffff ||
- addr->short_addr == IEEE802154_ADDR_UNDEF ||
- addr->short_addr == 0xffff)
+ if (addr->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST) ||
+ addr->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
+ addr->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST))
break;
rtnl_lock();
@@ -86,7 +88,7 @@ struct net_device *ieee802154_get_dev(struct net *net,
break;
default:
pr_warning("Unsupported ieee802154 address type: %d\n",
- addr->addr_type);
+ addr->mode);
break;
}
@@ -326,7 +328,7 @@ drop:
static struct packet_type ieee802154_packet_type = {
- .type = __constant_htons(ETH_P_IEEE802154),
+ .type = htons(ETH_P_IEEE802154),
.func = ieee802154_rcv,
};
diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c
index 1846c1fe0d06..786437bc0c08 100644
--- a/net/ieee802154/dgram.c
+++ b/net/ieee802154/dgram.c
@@ -73,10 +73,10 @@ static int dgram_init(struct sock *sk)
{
struct dgram_sock *ro = dgram_sk(sk);
- ro->dst_addr.addr_type = IEEE802154_ADDR_LONG;
- ro->dst_addr.pan_id = 0xffff;
+ ro->dst_addr.mode = IEEE802154_ADDR_LONG;
+ ro->dst_addr.pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
ro->want_ack = 1;
- memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr));
+ memset(&ro->dst_addr.extended_addr, 0xff, IEEE802154_ADDR_LEN);
return 0;
}
@@ -88,6 +88,7 @@ static void dgram_close(struct sock *sk, long timeout)
static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
{
struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
+ struct ieee802154_addr haddr;
struct dgram_sock *ro = dgram_sk(sk);
int err = -EINVAL;
struct net_device *dev;
@@ -102,7 +103,8 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
if (addr->family != AF_IEEE802154)
goto out;
- dev = ieee802154_get_dev(sock_net(sk), &addr->addr);
+ ieee802154_addr_from_sa(&haddr, &addr->addr);
+ dev = ieee802154_get_dev(sock_net(sk), &haddr);
if (!dev) {
err = -ENODEV;
goto out;
@@ -113,7 +115,7 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
goto out_put;
}
- memcpy(&ro->src_addr, &addr->addr, sizeof(struct ieee802154_addr));
+ ro->src_addr = haddr;
ro->bound = 1;
err = 0;
@@ -149,8 +151,7 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
* of this packet since that is all
* that will be read.
*/
- /* FIXME: parse the header for more correct value */
- amount = skb->len - (3+8+8);
+ amount = skb->len - ieee802154_hdr_length(skb);
}
spin_unlock_bh(&sk->sk_receive_queue.lock);
return put_user(amount, (int __user *)arg);
@@ -181,7 +182,7 @@ static int dgram_connect(struct sock *sk, struct sockaddr *uaddr,
goto out;
}
- memcpy(&ro->dst_addr, &addr->addr, sizeof(struct ieee802154_addr));
+ ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr);
out:
release_sock(sk);
@@ -194,8 +195,8 @@ static int dgram_disconnect(struct sock *sk, int flags)
lock_sock(sk);
- ro->dst_addr.addr_type = IEEE802154_ADDR_LONG;
- memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr));
+ ro->dst_addr.mode = IEEE802154_ADDR_LONG;
+ memset(&ro->dst_addr.extended_addr, 0xff, IEEE802154_ADDR_LEN);
release_sock(sk);
@@ -232,7 +233,7 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
if (size > mtu) {
pr_debug("size = %Zu, mtu = %u\n", size, mtu);
- err = -EINVAL;
+ err = -EMSGSIZE;
goto out_dev;
}
@@ -312,7 +313,7 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,
if (saddr) {
saddr->family = AF_IEEE802154;
- saddr->addr = mac_cb(skb)->sa;
+ ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source);
*addr_len = sizeof(*saddr);
}
@@ -328,6 +329,10 @@ out:
static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return NET_RX_DROP;
+
if (sock_queue_rcv_skb(sk, skb) < 0) {
kfree_skb(skb);
return NET_RX_DROP;
@@ -336,40 +341,43 @@ static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb)
return NET_RX_SUCCESS;
}
-static inline int ieee802154_match_sock(u8 *hw_addr, u16 pan_id,
- u16 short_addr, struct dgram_sock *ro)
+static inline bool
+ieee802154_match_sock(__le64 hw_addr, __le16 pan_id, __le16 short_addr,
+ struct dgram_sock *ro)
{
if (!ro->bound)
- return 1;
+ return true;
- if (ro->src_addr.addr_type == IEEE802154_ADDR_LONG &&
- !memcmp(ro->src_addr.hwaddr, hw_addr, IEEE802154_ADDR_LEN))
- return 1;
+ if (ro->src_addr.mode == IEEE802154_ADDR_LONG &&
+ hw_addr == ro->src_addr.extended_addr)
+ return true;
- if (ro->src_addr.addr_type == IEEE802154_ADDR_SHORT &&
- pan_id == ro->src_addr.pan_id &&
- short_addr == ro->src_addr.short_addr)
- return 1;
+ if (ro->src_addr.mode == IEEE802154_ADDR_SHORT &&
+ pan_id == ro->src_addr.pan_id &&
+ short_addr == ro->src_addr.short_addr)
+ return true;
- return 0;
+ return false;
}
int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
{
struct sock *sk, *prev = NULL;
int ret = NET_RX_SUCCESS;
- u16 pan_id, short_addr;
+ __le16 pan_id, short_addr;
+ __le64 hw_addr;
/* Data frame processing */
BUG_ON(dev->type != ARPHRD_IEEE802154);
pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev);
+ hw_addr = ieee802154_devaddr_from_raw(dev->dev_addr);
read_lock(&dgram_lock);
sk_for_each(sk, &dgram_head) {
- if (ieee802154_match_sock(dev->dev_addr, pan_id, short_addr,
- dgram_sk(sk))) {
+ if (ieee802154_match_sock(hw_addr, pan_id, short_addr,
+ dgram_sk(sk))) {
if (prev) {
struct sk_buff *clone;
clone = skb_clone(skb, GFP_ATOMIC);
diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c
new file mode 100644
index 000000000000..bed42a48408c
--- /dev/null
+++ b/net/ieee802154/header_ops.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2014 Fraunhofer ITWM
+ *
+ * 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.
+ *
+ * Written by:
+ * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
+ */
+
+#include <net/mac802154.h>
+#include <net/ieee802154.h>
+#include <net/ieee802154_netdev.h>
+
+static int
+ieee802154_hdr_push_addr(u8 *buf, const struct ieee802154_addr *addr,
+ bool omit_pan)
+{
+ int pos = 0;
+
+ if (addr->mode == IEEE802154_ADDR_NONE)
+ return 0;
+
+ if (!omit_pan) {
+ memcpy(buf + pos, &addr->pan_id, 2);
+ pos += 2;
+ }
+
+ switch (addr->mode) {
+ case IEEE802154_ADDR_SHORT:
+ memcpy(buf + pos, &addr->short_addr, 2);
+ pos += 2;
+ break;
+
+ case IEEE802154_ADDR_LONG:
+ memcpy(buf + pos, &addr->extended_addr, IEEE802154_ADDR_LEN);
+ pos += IEEE802154_ADDR_LEN;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return pos;
+}
+
+static int
+ieee802154_hdr_push_sechdr(u8 *buf, const struct ieee802154_sechdr *hdr)
+{
+ int pos = 5;
+
+ memcpy(buf, hdr, 1);
+ memcpy(buf + 1, &hdr->frame_counter, 4);
+
+ switch (hdr->key_id_mode) {
+ case IEEE802154_SCF_KEY_IMPLICIT:
+ return pos;
+
+ case IEEE802154_SCF_KEY_INDEX:
+ break;
+
+ case IEEE802154_SCF_KEY_SHORT_INDEX:
+ memcpy(buf + pos, &hdr->short_src, 4);
+ pos += 4;
+ break;
+
+ case IEEE802154_SCF_KEY_HW_INDEX:
+ memcpy(buf + pos, &hdr->extended_src, IEEE802154_ADDR_LEN);
+ pos += IEEE802154_ADDR_LEN;
+ break;
+ }
+
+ buf[pos++] = hdr->key_id;
+
+ return pos;
+}
+
+int
+ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
+{
+ u8 buf[MAC802154_FRAME_HARD_HEADER_LEN];
+ int pos = 2;
+ int rc;
+ struct ieee802154_hdr_fc fc = hdr->fc;
+
+ buf[pos++] = hdr->seq;
+
+ fc.dest_addr_mode = hdr->dest.mode;
+
+ rc = ieee802154_hdr_push_addr(buf + pos, &hdr->dest, false);
+ if (rc < 0)
+ return -EINVAL;
+ pos += rc;
+
+ fc.source_addr_mode = hdr->source.mode;
+
+ if (hdr->source.pan_id == hdr->dest.pan_id &&
+ hdr->dest.mode != IEEE802154_ADDR_NONE)
+ fc.intra_pan = true;
+
+ rc = ieee802154_hdr_push_addr(buf + pos, &hdr->source, fc.intra_pan);
+ if (rc < 0)
+ return -EINVAL;
+ pos += rc;
+
+ if (fc.security_enabled) {
+ fc.version = 1;
+
+ rc = ieee802154_hdr_push_sechdr(buf + pos, &hdr->sec);
+ if (rc < 0)
+ return -EINVAL;
+
+ pos += rc;
+ }
+
+ memcpy(buf, &fc, 2);
+
+ memcpy(skb_push(skb, pos), buf, pos);
+
+ return pos;
+}
+EXPORT_SYMBOL_GPL(ieee802154_hdr_push);
+
+static int
+ieee802154_hdr_get_addr(const u8 *buf, int mode, bool omit_pan,
+ struct ieee802154_addr *addr)
+{
+ int pos = 0;
+
+ addr->mode = mode;
+
+ if (mode == IEEE802154_ADDR_NONE)
+ return 0;
+
+ if (!omit_pan) {
+ memcpy(&addr->pan_id, buf + pos, 2);
+ pos += 2;
+ }
+
+ if (mode == IEEE802154_ADDR_SHORT) {
+ memcpy(&addr->short_addr, buf + pos, 2);
+ return pos + 2;
+ } else {
+ memcpy(&addr->extended_addr, buf + pos, IEEE802154_ADDR_LEN);
+ return pos + IEEE802154_ADDR_LEN;
+ }
+}
+
+static int ieee802154_hdr_addr_len(int mode, bool omit_pan)
+{
+ int pan_len = omit_pan ? 0 : 2;
+
+ switch (mode) {
+ case IEEE802154_ADDR_NONE: return 0;
+ case IEEE802154_ADDR_SHORT: return 2 + pan_len;
+ case IEEE802154_ADDR_LONG: return IEEE802154_ADDR_LEN + pan_len;
+ default: return -EINVAL;
+ }
+}
+
+static int
+ieee802154_hdr_get_sechdr(const u8 *buf, struct ieee802154_sechdr *hdr)
+{
+ int pos = 5;
+
+ memcpy(hdr, buf, 1);
+ memcpy(&hdr->frame_counter, buf + 1, 4);
+
+ switch (hdr->key_id_mode) {
+ case IEEE802154_SCF_KEY_IMPLICIT:
+ return pos;
+
+ case IEEE802154_SCF_KEY_INDEX:
+ break;
+
+ case IEEE802154_SCF_KEY_SHORT_INDEX:
+ memcpy(&hdr->short_src, buf + pos, 4);
+ pos += 4;
+ break;
+
+ case IEEE802154_SCF_KEY_HW_INDEX:
+ memcpy(&hdr->extended_src, buf + pos, IEEE802154_ADDR_LEN);
+ pos += IEEE802154_ADDR_LEN;
+ break;
+ }
+
+ hdr->key_id = buf[pos++];
+
+ return pos;
+}
+
+static int ieee802154_hdr_sechdr_len(u8 sc)
+{
+ switch (IEEE802154_SCF_KEY_ID_MODE(sc)) {
+ case IEEE802154_SCF_KEY_IMPLICIT: return 5;
+ case IEEE802154_SCF_KEY_INDEX: return 6;
+ case IEEE802154_SCF_KEY_SHORT_INDEX: return 10;
+ case IEEE802154_SCF_KEY_HW_INDEX: return 14;
+ default: return -EINVAL;
+ }
+}
+
+static int ieee802154_hdr_minlen(const struct ieee802154_hdr *hdr)
+{
+ int dlen, slen;
+
+ dlen = ieee802154_hdr_addr_len(hdr->fc.dest_addr_mode, false);
+ slen = ieee802154_hdr_addr_len(hdr->fc.source_addr_mode,
+ hdr->fc.intra_pan);
+
+ if (slen < 0 || dlen < 0)
+ return -EINVAL;
+
+ return 3 + dlen + slen + hdr->fc.security_enabled;
+}
+
+static int
+ieee802154_hdr_get_addrs(const u8 *buf, struct ieee802154_hdr *hdr)
+{
+ int pos = 0;
+
+ pos += ieee802154_hdr_get_addr(buf + pos, hdr->fc.dest_addr_mode,
+ false, &hdr->dest);
+ pos += ieee802154_hdr_get_addr(buf + pos, hdr->fc.source_addr_mode,
+ hdr->fc.intra_pan, &hdr->source);
+
+ if (hdr->fc.intra_pan)
+ hdr->source.pan_id = hdr->dest.pan_id;
+
+ return pos;
+}
+
+int
+ieee802154_hdr_pull(struct sk_buff *skb, struct ieee802154_hdr *hdr)
+{
+ int pos = 3, rc;
+
+ if (!pskb_may_pull(skb, 3))
+ return -EINVAL;
+
+ memcpy(hdr, skb->data, 3);
+
+ rc = ieee802154_hdr_minlen(hdr);
+ if (rc < 0 || !pskb_may_pull(skb, rc))
+ return -EINVAL;
+
+ pos += ieee802154_hdr_get_addrs(skb->data + pos, hdr);
+
+ if (hdr->fc.security_enabled) {
+ int want = pos + ieee802154_hdr_sechdr_len(skb->data[pos]);
+
+ if (!pskb_may_pull(skb, want))
+ return -EINVAL;
+
+ pos += ieee802154_hdr_get_sechdr(skb->data + pos, &hdr->sec);
+ }
+
+ skb_pull(skb, pos);
+ return pos;
+}
+EXPORT_SYMBOL_GPL(ieee802154_hdr_pull);
+
+int
+ieee802154_hdr_peek_addrs(const struct sk_buff *skb, struct ieee802154_hdr *hdr)
+{
+ const u8 *buf = skb_mac_header(skb);
+ int pos = 3, rc;
+
+ if (buf + 3 > skb_tail_pointer(skb))
+ return -EINVAL;
+
+ memcpy(hdr, buf, 3);
+
+ rc = ieee802154_hdr_minlen(hdr);
+ if (rc < 0 || buf + rc > skb_tail_pointer(skb))
+ return -EINVAL;
+
+ pos += ieee802154_hdr_get_addrs(buf + pos, hdr);
+ return pos;
+}
+EXPORT_SYMBOL_GPL(ieee802154_hdr_peek_addrs);
diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h
index cee4425b9956..6693a5cf01ce 100644
--- a/net/ieee802154/ieee802154.h
+++ b/net/ieee802154/ieee802154.h
@@ -66,5 +66,6 @@ int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info);
int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info);
int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info);
int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb);
+int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info);
#endif
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
index 43f1b2bf469f..04b20589d97a 100644
--- a/net/ieee802154/netlink.c
+++ b/net/ieee802154/netlink.c
@@ -123,6 +123,7 @@ static const struct genl_ops ieee8021154_ops[] = {
IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req),
IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface,
ieee802154_dump_iface),
+ IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams),
};
static const struct genl_multicast_group ieee802154_mcgrps[] = {
diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c
index ba5c1e002f37..5d285498c0f6 100644
--- a/net/ieee802154/nl-mac.c
+++ b/net/ieee802154/nl-mac.c
@@ -39,6 +39,26 @@
#include "ieee802154.h"
+static int nla_put_hwaddr(struct sk_buff *msg, int type, __le64 hwaddr)
+{
+ return nla_put_u64(msg, type, swab64((__force u64)hwaddr));
+}
+
+static __le64 nla_get_hwaddr(const struct nlattr *nla)
+{
+ return ieee802154_devaddr_from_raw(nla_data(nla));
+}
+
+static int nla_put_shortaddr(struct sk_buff *msg, int type, __le16 addr)
+{
+ return nla_put_u16(msg, type, le16_to_cpu(addr));
+}
+
+static __le16 nla_get_shortaddr(const struct nlattr *nla)
+{
+ return cpu_to_le16(nla_get_u16(nla));
+}
+
int ieee802154_nl_assoc_indic(struct net_device *dev,
struct ieee802154_addr *addr, u8 cap)
{
@@ -46,7 +66,7 @@ int ieee802154_nl_assoc_indic(struct net_device *dev,
pr_debug("%s\n", __func__);
- if (addr->addr_type != IEEE802154_ADDR_LONG) {
+ if (addr->mode != IEEE802154_ADDR_LONG) {
pr_err("%s: received non-long source address!\n", __func__);
return -EINVAL;
}
@@ -59,8 +79,8 @@ int ieee802154_nl_assoc_indic(struct net_device *dev,
nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr) ||
- nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
- addr->hwaddr) ||
+ nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
+ addr->extended_addr) ||
nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
goto nla_put_failure;
@@ -72,7 +92,7 @@ nla_put_failure:
}
EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
-int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr,
+int ieee802154_nl_assoc_confirm(struct net_device *dev, __le16 short_addr,
u8 status)
{
struct sk_buff *msg;
@@ -87,7 +107,7 @@ int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr,
nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr) ||
- nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
+ nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
goto nla_put_failure;
return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
@@ -114,13 +134,13 @@ int ieee802154_nl_disassoc_indic(struct net_device *dev,
nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr))
goto nla_put_failure;
- if (addr->addr_type == IEEE802154_ADDR_LONG) {
- if (nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
- addr->hwaddr))
+ if (addr->mode == IEEE802154_ADDR_LONG) {
+ if (nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
+ addr->extended_addr))
goto nla_put_failure;
} else {
- if (nla_put_u16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
- addr->short_addr))
+ if (nla_put_shortaddr(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
+ addr->short_addr))
goto nla_put_failure;
}
if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
@@ -157,8 +177,8 @@ nla_put_failure:
}
EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
-int ieee802154_nl_beacon_indic(struct net_device *dev,
- u16 panid, u16 coord_addr)
+int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid,
+ __le16 coord_addr)
{
struct sk_buff *msg;
@@ -172,8 +192,9 @@ int ieee802154_nl_beacon_indic(struct net_device *dev,
nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr) ||
- nla_put_u16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr) ||
- nla_put_u16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
+ nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_SHORT_ADDR,
+ coord_addr) ||
+ nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
goto nla_put_failure;
return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
@@ -243,6 +264,8 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
{
void *hdr;
struct wpan_phy *phy;
+ struct ieee802154_mlme_ops *ops;
+ __le16 short_addr, pan_id;
pr_debug("%s\n", __func__);
@@ -251,19 +274,45 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
if (!hdr)
goto out;
- phy = ieee802154_mlme_ops(dev)->get_phy(dev);
+ ops = ieee802154_mlme_ops(dev);
+ phy = ops->get_phy(dev);
BUG_ON(!phy);
+ short_addr = ops->get_short_addr(dev);
+ pan_id = ops->get_pan_id(dev);
+
if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
dev->dev_addr) ||
- nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR,
- ieee802154_mlme_ops(dev)->get_short_addr(dev)) ||
- nla_put_u16(msg, IEEE802154_ATTR_PAN_ID,
- ieee802154_mlme_ops(dev)->get_pan_id(dev)))
+ nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
+ nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, pan_id))
goto nla_put_failure;
+
+ if (ops->get_mac_params) {
+ struct ieee802154_mac_params params;
+
+ ops->get_mac_params(dev, &params);
+
+ if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER,
+ params.transmit_power) ||
+ nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, params.lbt) ||
+ nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE,
+ params.cca_mode) ||
+ nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL,
+ params.cca_ed_level) ||
+ nla_put_u8(msg, IEEE802154_ATTR_CSMA_RETRIES,
+ params.csma_retries) ||
+ nla_put_u8(msg, IEEE802154_ATTR_CSMA_MIN_BE,
+ params.min_be) ||
+ nla_put_u8(msg, IEEE802154_ATTR_CSMA_MAX_BE,
+ params.max_be) ||
+ nla_put_s8(msg, IEEE802154_ATTR_FRAME_RETRIES,
+ params.frame_retries))
+ goto nla_put_failure;
+ }
+
wpan_phy_put(phy);
return genlmsg_end(msg, hdr);
@@ -322,16 +371,16 @@ int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info)
goto out;
if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
- addr.addr_type = IEEE802154_ADDR_LONG;
- nla_memcpy(addr.hwaddr,
- info->attrs[IEEE802154_ATTR_COORD_HW_ADDR],
- IEEE802154_ADDR_LEN);
+ addr.mode = IEEE802154_ADDR_LONG;
+ addr.extended_addr = nla_get_hwaddr(
+ info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]);
} else {
- addr.addr_type = IEEE802154_ADDR_SHORT;
- addr.short_addr = nla_get_u16(
+ addr.mode = IEEE802154_ADDR_SHORT;
+ addr.short_addr = nla_get_shortaddr(
info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
}
- addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
+ addr.pan_id = nla_get_shortaddr(
+ info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
if (info->attrs[IEEE802154_ATTR_PAGE])
page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
@@ -365,14 +414,13 @@ int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info)
if (!ieee802154_mlme_ops(dev)->assoc_resp)
goto out;
- addr.addr_type = IEEE802154_ADDR_LONG;
- nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
- IEEE802154_ADDR_LEN);
+ addr.mode = IEEE802154_ADDR_LONG;
+ addr.extended_addr = nla_get_hwaddr(
+ info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]);
addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
-
ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr,
- nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
+ nla_get_shortaddr(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));
out:
@@ -398,13 +446,12 @@ int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info)
goto out;
if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
- addr.addr_type = IEEE802154_ADDR_LONG;
- nla_memcpy(addr.hwaddr,
- info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
- IEEE802154_ADDR_LEN);
+ addr.mode = IEEE802154_ADDR_LONG;
+ addr.extended_addr = nla_get_hwaddr(
+ info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]);
} else {
- addr.addr_type = IEEE802154_ADDR_SHORT;
- addr.short_addr = nla_get_u16(
+ addr.mode = IEEE802154_ADDR_SHORT;
+ addr.short_addr = nla_get_shortaddr(
info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
}
addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
@@ -449,10 +496,11 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
if (!ieee802154_mlme_ops(dev)->start_req)
goto out;
- addr.addr_type = IEEE802154_ADDR_SHORT;
- addr.short_addr = nla_get_u16(
+ addr.mode = IEEE802154_ADDR_SHORT;
+ addr.short_addr = nla_get_shortaddr(
info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
- addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
+ addr.pan_id = nla_get_shortaddr(
+ info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);
bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]);
@@ -467,7 +515,7 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
page = 0;
- if (addr.short_addr == IEEE802154_ADDR_BROADCAST) {
+ if (addr.short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) {
ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
dev_put(dev);
return -EINVAL;
@@ -577,3 +625,93 @@ cont:
return skb->len;
}
+
+int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net_device *dev = NULL;
+ struct ieee802154_mlme_ops *ops;
+ struct ieee802154_mac_params params;
+ struct wpan_phy *phy;
+ int rc = -EINVAL;
+
+ pr_debug("%s\n", __func__);
+
+ dev = ieee802154_nl_get_dev(info);
+ if (!dev)
+ return -ENODEV;
+
+ ops = ieee802154_mlme_ops(dev);
+
+ if (!ops->get_mac_params || !ops->set_mac_params) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (netif_running(dev)) {
+ rc = -EBUSY;
+ goto out;
+ }
+
+ if (!info->attrs[IEEE802154_ATTR_LBT_ENABLED] &&
+ !info->attrs[IEEE802154_ATTR_CCA_MODE] &&
+ !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL] &&
+ !info->attrs[IEEE802154_ATTR_CSMA_RETRIES] &&
+ !info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] &&
+ !info->attrs[IEEE802154_ATTR_CSMA_MAX_BE] &&
+ !info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
+ goto out;
+
+ phy = ops->get_phy(dev);
+
+ if ((!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) ||
+ (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) ||
+ (!phy->set_cca_ed_level &&
+ info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) ||
+ (!phy->set_csma_params &&
+ (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] ||
+ info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] ||
+ info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])) ||
+ (!phy->set_frame_retries &&
+ info->attrs[IEEE802154_ATTR_FRAME_RETRIES])) {
+ rc = -EOPNOTSUPP;
+ goto out_phy;
+ }
+
+ ops->get_mac_params(dev, &params);
+
+ if (info->attrs[IEEE802154_ATTR_TXPOWER])
+ params.transmit_power = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]);
+
+ if (info->attrs[IEEE802154_ATTR_LBT_ENABLED])
+ params.lbt = nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]);
+
+ if (info->attrs[IEEE802154_ATTR_CCA_MODE])
+ params.cca_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]);
+
+ if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL])
+ params.cca_ed_level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]);
+
+ if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES])
+ params.csma_retries = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_RETRIES]);
+
+ if (info->attrs[IEEE802154_ATTR_CSMA_MIN_BE])
+ params.min_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]);
+
+ if (info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])
+ params.max_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]);
+
+ if (info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
+ params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
+
+ rc = ops->set_mac_params(dev, &params);
+
+ wpan_phy_put(phy);
+ dev_put(dev);
+ return rc;
+
+out_phy:
+ wpan_phy_put(phy);
+out:
+ dev_put(dev);
+ return rc;
+}
diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c
index 6adda4d46f95..fd7be5e45cef 100644
--- a/net/ieee802154/nl_policy.c
+++ b/net/ieee802154/nl_policy.c
@@ -52,5 +52,15 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
[IEEE802154_ATTR_DURATION] = { .type = NLA_U8, },
[IEEE802154_ATTR_ED_LIST] = { .len = 27 },
[IEEE802154_ATTR_CHANNEL_PAGE_LIST] = { .len = 32 * 4, },
+
+ [IEEE802154_ATTR_TXPOWER] = { .type = NLA_S8, },
+ [IEEE802154_ATTR_LBT_ENABLED] = { .type = NLA_U8, },
+ [IEEE802154_ATTR_CCA_MODE] = { .type = NLA_U8, },
+ [IEEE802154_ATTR_CCA_ED_LEVEL] = { .type = NLA_S32, },
+ [IEEE802154_ATTR_CSMA_RETRIES] = { .type = NLA_U8, },
+ [IEEE802154_ATTR_CSMA_MIN_BE] = { .type = NLA_U8, },
+ [IEEE802154_ATTR_CSMA_MAX_BE] = { .type = NLA_U8, },
+
+ [IEEE802154_ATTR_FRAME_RETRIES] = { .type = NLA_S8, },
};
diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c
index 41f538b8e59c..74d54fae33d7 100644
--- a/net/ieee802154/raw.c
+++ b/net/ieee802154/raw.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <net/sock.h>
#include <net/af_ieee802154.h>
+#include <net/ieee802154_netdev.h>
#include "af802154.h"
@@ -55,21 +56,24 @@ static void raw_close(struct sock *sk, long timeout)
sk_common_release(sk);
}
-static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+static int raw_bind(struct sock *sk, struct sockaddr *_uaddr, int len)
{
- struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
+ struct ieee802154_addr addr;
+ struct sockaddr_ieee802154 *uaddr = (struct sockaddr_ieee802154 *)_uaddr;
int err = 0;
struct net_device *dev = NULL;
- if (len < sizeof(*addr))
+ if (len < sizeof(*uaddr))
return -EINVAL;
- if (addr->family != AF_IEEE802154)
+ uaddr = (struct sockaddr_ieee802154 *)_uaddr;
+ if (uaddr->family != AF_IEEE802154)
return -EINVAL;
lock_sock(sk);
- dev = ieee802154_get_dev(sock_net(sk), &addr->addr);
+ ieee802154_addr_from_sa(&addr, &uaddr->addr);
+ dev = ieee802154_get_dev(sock_net(sk), &addr);
if (!dev) {
err = -ENODEV;
goto out;
@@ -209,6 +213,10 @@ out:
static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return NET_RX_DROP;
+
if (sock_queue_rcv_skb(sk, skb) < 0) {
kfree_skb(skb);
return NET_RX_DROP;
diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c
new file mode 100644
index 000000000000..ef2d54372b13
--- /dev/null
+++ b/net/ieee802154/reassembly.c
@@ -0,0 +1,571 @@
+/* 6LoWPAN fragment reassembly
+ *
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/ipv6/reassembly.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "6LoWPAN: " fmt
+
+#include <linux/net.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/random.h>
+#include <linux/jhash.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include <net/ieee802154_netdev.h>
+#include <net/6lowpan.h>
+#include <net/ipv6.h>
+#include <net/inet_frag.h>
+
+#include "reassembly.h"
+
+struct lowpan_frag_info {
+ __be16 d_tag;
+ u16 d_size;
+ u8 d_offset;
+};
+
+struct lowpan_frag_info *lowpan_cb(struct sk_buff *skb)
+{
+ return (struct lowpan_frag_info *)skb->cb;
+}
+
+static struct inet_frags lowpan_frags;
+
+static int lowpan_frag_reasm(struct lowpan_frag_queue *fq,
+ struct sk_buff *prev, struct net_device *dev);
+
+static unsigned int lowpan_hash_frag(__be16 tag, u16 d_size,
+ const struct ieee802154_addr *saddr,
+ const struct ieee802154_addr *daddr)
+{
+ u32 c;
+
+ net_get_random_once(&lowpan_frags.rnd, sizeof(lowpan_frags.rnd));
+ c = jhash_3words(ieee802154_addr_hash(saddr),
+ ieee802154_addr_hash(daddr),
+ (__force u32)(tag + (d_size << 16)),
+ lowpan_frags.rnd);
+
+ return c & (INETFRAGS_HASHSZ - 1);
+}
+
+static unsigned int lowpan_hashfn(struct inet_frag_queue *q)
+{
+ struct lowpan_frag_queue *fq;
+
+ fq = container_of(q, struct lowpan_frag_queue, q);
+ return lowpan_hash_frag(fq->tag, fq->d_size, &fq->saddr, &fq->daddr);
+}
+
+static bool lowpan_frag_match(struct inet_frag_queue *q, void *a)
+{
+ struct lowpan_frag_queue *fq;
+ struct lowpan_create_arg *arg = a;
+
+ fq = container_of(q, struct lowpan_frag_queue, q);
+ return fq->tag == arg->tag && fq->d_size == arg->d_size &&
+ ieee802154_addr_equal(&fq->saddr, arg->src) &&
+ ieee802154_addr_equal(&fq->daddr, arg->dst);
+}
+
+static void lowpan_frag_init(struct inet_frag_queue *q, void *a)
+{
+ struct lowpan_frag_queue *fq;
+ struct lowpan_create_arg *arg = a;
+
+ fq = container_of(q, struct lowpan_frag_queue, q);
+
+ fq->tag = arg->tag;
+ fq->d_size = arg->d_size;
+ fq->saddr = *arg->src;
+ fq->daddr = *arg->dst;
+}
+
+static void lowpan_frag_expire(unsigned long data)
+{
+ struct frag_queue *fq;
+ struct net *net;
+
+ fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q);
+ net = container_of(fq->q.net, struct net, ieee802154_lowpan.frags);
+
+ spin_lock(&fq->q.lock);
+
+ if (fq->q.last_in & INET_FRAG_COMPLETE)
+ goto out;
+
+ inet_frag_kill(&fq->q, &lowpan_frags);
+out:
+ spin_unlock(&fq->q.lock);
+ inet_frag_put(&fq->q, &lowpan_frags);
+}
+
+static inline struct lowpan_frag_queue *
+fq_find(struct net *net, const struct lowpan_frag_info *frag_info,
+ const struct ieee802154_addr *src,
+ const struct ieee802154_addr *dst)
+{
+ struct inet_frag_queue *q;
+ struct lowpan_create_arg arg;
+ unsigned int hash;
+
+ arg.tag = frag_info->d_tag;
+ arg.d_size = frag_info->d_size;
+ arg.src = src;
+ arg.dst = dst;
+
+ read_lock(&lowpan_frags.lock);
+ hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst);
+
+ q = inet_frag_find(&net->ieee802154_lowpan.frags,
+ &lowpan_frags, &arg, hash);
+ if (IS_ERR_OR_NULL(q)) {
+ inet_frag_maybe_warn_overflow(q, pr_fmt());
+ return NULL;
+ }
+ return container_of(q, struct lowpan_frag_queue, q);
+}
+
+static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
+ struct sk_buff *skb, const u8 frag_type)
+{
+ struct sk_buff *prev, *next;
+ struct net_device *dev;
+ int end, offset;
+
+ if (fq->q.last_in & INET_FRAG_COMPLETE)
+ goto err;
+
+ offset = lowpan_cb(skb)->d_offset << 3;
+ end = lowpan_cb(skb)->d_size;
+
+ /* Is this the final fragment? */
+ if (offset + skb->len == end) {
+ /* If we already have some bits beyond end
+ * or have different end, the segment is corrupted.
+ */
+ if (end < fq->q.len ||
+ ((fq->q.last_in & INET_FRAG_LAST_IN) && end != fq->q.len))
+ goto err;
+ fq->q.last_in |= INET_FRAG_LAST_IN;
+ fq->q.len = end;
+ } else {
+ if (end > fq->q.len) {
+ /* Some bits beyond end -> corruption. */
+ if (fq->q.last_in & INET_FRAG_LAST_IN)
+ goto err;
+ fq->q.len = end;
+ }
+ }
+
+ /* Find out which fragments are in front and at the back of us
+ * in the chain of fragments so far. We must know where to put
+ * this fragment, right?
+ */
+ prev = fq->q.fragments_tail;
+ if (!prev || lowpan_cb(prev)->d_offset < lowpan_cb(skb)->d_offset) {
+ next = NULL;
+ goto found;
+ }
+ prev = NULL;
+ for (next = fq->q.fragments; next != NULL; next = next->next) {
+ if (lowpan_cb(next)->d_offset >= lowpan_cb(skb)->d_offset)
+ break; /* bingo! */
+ prev = next;
+ }
+
+found:
+ /* Insert this fragment in the chain of fragments. */
+ skb->next = next;
+ if (!next)
+ fq->q.fragments_tail = skb;
+ if (prev)
+ prev->next = skb;
+ else
+ fq->q.fragments = skb;
+
+ dev = skb->dev;
+ if (dev)
+ skb->dev = NULL;
+
+ fq->q.stamp = skb->tstamp;
+ if (frag_type == LOWPAN_DISPATCH_FRAG1) {
+ /* Calculate uncomp. 6lowpan header to estimate full size */
+ fq->q.meat += lowpan_uncompress_size(skb, NULL);
+ fq->q.last_in |= INET_FRAG_FIRST_IN;
+ } else {
+ fq->q.meat += skb->len;
+ }
+ add_frag_mem_limit(&fq->q, skb->truesize);
+
+ if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
+ fq->q.meat == fq->q.len) {
+ int res;
+ unsigned long orefdst = skb->_skb_refdst;
+
+ skb->_skb_refdst = 0UL;
+ res = lowpan_frag_reasm(fq, prev, dev);
+ skb->_skb_refdst = orefdst;
+ return res;
+ }
+
+ inet_frag_lru_move(&fq->q);
+ return -1;
+err:
+ kfree_skb(skb);
+ return -1;
+}
+
+/* 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.
+ */
+static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev,
+ struct net_device *dev)
+{
+ struct sk_buff *fp, *head = fq->q.fragments;
+ int sum_truesize;
+
+ inet_frag_kill(&fq->q, &lowpan_frags);
+
+ /* Make the one we just received the head. */
+ if (prev) {
+ head = prev->next;
+ fp = skb_clone(head, GFP_ATOMIC);
+
+ if (!fp)
+ goto out_oom;
+
+ fp->next = head->next;
+ if (!fp->next)
+ fq->q.fragments_tail = fp;
+ prev->next = fp;
+
+ skb_morph(head, fq->q.fragments);
+ head->next = fq->q.fragments->next;
+
+ consume_skb(fq->q.fragments);
+ fq->q.fragments = head;
+ }
+
+ /* Head of list must not be cloned. */
+ if (skb_unclone(head, GFP_ATOMIC))
+ goto out_oom;
+
+ /* If the first fragment is fragmented itself, we split
+ * it to two chunks: the first with data and paged part
+ * and the second, holding only fragments.
+ */
+ if (skb_has_frag_list(head)) {
+ struct sk_buff *clone;
+ int i, plen = 0;
+
+ clone = alloc_skb(0, GFP_ATOMIC);
+ if (!clone)
+ goto out_oom;
+ clone->next = head->next;
+ head->next = clone;
+ skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
+ skb_frag_list_init(head);
+ for (i = 0; i < skb_shinfo(head)->nr_frags; i++)
+ plen += skb_frag_size(&skb_shinfo(head)->frags[i]);
+ clone->len = head->data_len - plen;
+ clone->data_len = clone->len;
+ head->data_len -= clone->len;
+ head->len -= clone->len;
+ add_frag_mem_limit(&fq->q, clone->truesize);
+ }
+
+ WARN_ON(head == NULL);
+
+ sum_truesize = head->truesize;
+ for (fp = head->next; fp;) {
+ bool headstolen;
+ int delta;
+ struct sk_buff *next = fp->next;
+
+ sum_truesize += fp->truesize;
+ if (skb_try_coalesce(head, fp, &headstolen, &delta)) {
+ kfree_skb_partial(fp, headstolen);
+ } else {
+ if (!skb_shinfo(head)->frag_list)
+ skb_shinfo(head)->frag_list = fp;
+ head->data_len += fp->len;
+ head->len += fp->len;
+ head->truesize += fp->truesize;
+ }
+ fp = next;
+ }
+ sub_frag_mem_limit(&fq->q, sum_truesize);
+
+ head->next = NULL;
+ head->dev = dev;
+ head->tstamp = fq->q.stamp;
+
+ fq->q.fragments = NULL;
+ fq->q.fragments_tail = NULL;
+
+ return 1;
+out_oom:
+ net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n");
+ return -1;
+}
+
+static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type,
+ struct lowpan_frag_info *frag_info)
+{
+ bool fail;
+ u8 pattern = 0, low = 0;
+
+ fail = lowpan_fetch_skb(skb, &pattern, 1);
+ fail |= lowpan_fetch_skb(skb, &low, 1);
+ frag_info->d_size = (pattern & 7) << 8 | low;
+ fail |= lowpan_fetch_skb(skb, &frag_info->d_tag, 2);
+
+ if (frag_type == LOWPAN_DISPATCH_FRAGN) {
+ fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1);
+ } else {
+ skb_reset_network_header(skb);
+ frag_info->d_offset = 0;
+ }
+
+ if (unlikely(fail))
+ return -EIO;
+
+ return 0;
+}
+
+int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type)
+{
+ struct lowpan_frag_queue *fq;
+ struct net *net = dev_net(skb->dev);
+ struct lowpan_frag_info *frag_info = lowpan_cb(skb);
+ struct ieee802154_addr source, dest;
+ int err;
+
+ source = mac_cb(skb)->source;
+ dest = mac_cb(skb)->dest;
+
+ err = lowpan_get_frag_info(skb, frag_type, frag_info);
+ if (err < 0)
+ goto err;
+
+ if (frag_info->d_size > net->ieee802154_lowpan.max_dsize)
+ goto err;
+
+ inet_frag_evictor(&net->ieee802154_lowpan.frags, &lowpan_frags, false);
+
+ fq = fq_find(net, frag_info, &source, &dest);
+ if (fq != NULL) {
+ int ret;
+ spin_lock(&fq->q.lock);
+ ret = lowpan_frag_queue(fq, skb, frag_type);
+ spin_unlock(&fq->q.lock);
+
+ inet_frag_put(&fq->q, &lowpan_frags);
+ return ret;
+ }
+
+err:
+ kfree_skb(skb);
+ return -1;
+}
+EXPORT_SYMBOL(lowpan_frag_rcv);
+
+#ifdef CONFIG_SYSCTL
+static struct ctl_table lowpan_frags_ns_ctl_table[] = {
+ {
+ .procname = "6lowpanfrag_high_thresh",
+ .data = &init_net.ieee802154_lowpan.frags.high_thresh,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {
+ .procname = "6lowpanfrag_low_thresh",
+ .data = &init_net.ieee802154_lowpan.frags.low_thresh,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {
+ .procname = "6lowpanfrag_time",
+ .data = &init_net.ieee802154_lowpan.frags.timeout,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ },
+ {
+ .procname = "6lowpanfrag_max_datagram_size",
+ .data = &init_net.ieee802154_lowpan.max_dsize,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ { }
+};
+
+static struct ctl_table lowpan_frags_ctl_table[] = {
+ {
+ .procname = "6lowpanfrag_secret_interval",
+ .data = &lowpan_frags.secret_interval,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ },
+ { }
+};
+
+static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
+{
+ struct ctl_table *table;
+ struct ctl_table_header *hdr;
+
+ table = lowpan_frags_ns_ctl_table;
+ if (!net_eq(net, &init_net)) {
+ table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table),
+ GFP_KERNEL);
+ if (table == NULL)
+ goto err_alloc;
+
+ table[0].data = &net->ieee802154_lowpan.frags.high_thresh;
+ table[1].data = &net->ieee802154_lowpan.frags.low_thresh;
+ table[2].data = &net->ieee802154_lowpan.frags.timeout;
+ table[3].data = &net->ieee802154_lowpan.max_dsize;
+
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
+ }
+
+ hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table);
+ if (hdr == NULL)
+ goto err_reg;
+
+ net->ieee802154_lowpan.sysctl.frags_hdr = hdr;
+ return 0;
+
+err_reg:
+ if (!net_eq(net, &init_net))
+ kfree(table);
+err_alloc:
+ return -ENOMEM;
+}
+
+static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net)
+{
+ struct ctl_table *table;
+
+ table = net->ieee802154_lowpan.sysctl.frags_hdr->ctl_table_arg;
+ unregister_net_sysctl_table(net->ieee802154_lowpan.sysctl.frags_hdr);
+ if (!net_eq(net, &init_net))
+ kfree(table);
+}
+
+static struct ctl_table_header *lowpan_ctl_header;
+
+static int lowpan_frags_sysctl_register(void)
+{
+ lowpan_ctl_header = register_net_sysctl(&init_net,
+ "net/ieee802154/6lowpan",
+ lowpan_frags_ctl_table);
+ return lowpan_ctl_header == NULL ? -ENOMEM : 0;
+}
+
+static void lowpan_frags_sysctl_unregister(void)
+{
+ unregister_net_sysctl_table(lowpan_ctl_header);
+}
+#else
+static inline int lowpan_frags_ns_sysctl_register(struct net *net)
+{
+ return 0;
+}
+
+static inline void lowpan_frags_ns_sysctl_unregister(struct net *net)
+{
+}
+
+static inline int lowpan_frags_sysctl_register(void)
+{
+ return 0;
+}
+
+static inline void lowpan_frags_sysctl_unregister(void)
+{
+}
+#endif
+
+static int __net_init lowpan_frags_init_net(struct net *net)
+{
+ net->ieee802154_lowpan.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
+ net->ieee802154_lowpan.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
+ net->ieee802154_lowpan.frags.timeout = IPV6_FRAG_TIMEOUT;
+ net->ieee802154_lowpan.max_dsize = 0xFFFF;
+
+ inet_frags_init_net(&net->ieee802154_lowpan.frags);
+
+ return lowpan_frags_ns_sysctl_register(net);
+}
+
+static void __net_exit lowpan_frags_exit_net(struct net *net)
+{
+ lowpan_frags_ns_sysctl_unregister(net);
+ inet_frags_exit_net(&net->ieee802154_lowpan.frags, &lowpan_frags);
+}
+
+static struct pernet_operations lowpan_frags_ops = {
+ .init = lowpan_frags_init_net,
+ .exit = lowpan_frags_exit_net,
+};
+
+int __init lowpan_net_frag_init(void)
+{
+ int ret;
+
+ ret = lowpan_frags_sysctl_register();
+ if (ret)
+ return ret;
+
+ ret = register_pernet_subsys(&lowpan_frags_ops);
+ if (ret)
+ goto err_pernet;
+
+ 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;
+ lowpan_frags.secret_interval = 10 * 60 * HZ;
+ inet_frags_init(&lowpan_frags);
+
+ return ret;
+err_pernet:
+ lowpan_frags_sysctl_unregister();
+ return ret;
+}
+
+void lowpan_net_frag_exit(void)
+{
+ inet_frags_fini(&lowpan_frags);
+ lowpan_frags_sysctl_unregister();
+ unregister_pernet_subsys(&lowpan_frags_ops);
+}
diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h
new file mode 100644
index 000000000000..74e4a7c98191
--- /dev/null
+++ b/net/ieee802154/reassembly.h
@@ -0,0 +1,41 @@
+#ifndef __IEEE802154_6LOWPAN_REASSEMBLY_H__
+#define __IEEE802154_6LOWPAN_REASSEMBLY_H__
+
+#include <net/inet_frag.h>
+
+struct lowpan_create_arg {
+ __be16 tag;
+ u16 d_size;
+ const struct ieee802154_addr *src;
+ const struct ieee802154_addr *dst;
+};
+
+/* Equivalent of ipv4 struct ip
+ */
+struct lowpan_frag_queue {
+ struct inet_frag_queue q;
+
+ __be16 tag;
+ u16 d_size;
+ struct ieee802154_addr saddr;
+ struct ieee802154_addr daddr;
+};
+
+static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a)
+{
+ switch (a->mode) {
+ case IEEE802154_ADDR_LONG:
+ return (((__force u64)a->extended_addr) >> 32) ^
+ (((__force u64)a->extended_addr) & 0xffffffff);
+ case IEEE802154_ADDR_SHORT:
+ return (__force u32)(a->short_addr);
+ default:
+ return 0;
+ }
+}
+
+int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type);
+void lowpan_net_frag_exit(void);
+int lowpan_net_frag_init(void);
+
+#endif /* __IEEE802154_6LOWPAN_REASSEMBLY_H__ */
diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c
index 4dd37615a749..8d6f6704da84 100644
--- a/net/ieee802154/wpan-class.c
+++ b/net/ieee802154/wpan-class.c
@@ -44,9 +44,7 @@ static DEVICE_ATTR_RO(name);
MASTER_SHOW(current_channel, "%d");
MASTER_SHOW(current_page, "%d");
-MASTER_SHOW_COMPLEX(transmit_power, "%d +- %d dB",
- ((signed char) (phy->transmit_power << 2)) >> 2,
- (phy->transmit_power >> 6) ? (phy->transmit_power >> 6) * 3 : 1);
+MASTER_SHOW(transmit_power, "%d +- 1 dB");
MASTER_SHOW(cca_mode, "%d");
static ssize_t channels_supported_show(struct device *dev,
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index f8c49ce5b283..f032688d20d3 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -55,4 +55,4 @@ obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o
obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
- xfrm4_output.o
+ xfrm4_output.o xfrm4_protocol.o
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 19ab78aca547..8c54870db792 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1505,9 +1505,9 @@ u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset)
bhptr = per_cpu_ptr(mib[0], cpu);
syncp = (struct u64_stats_sync *)(bhptr + syncp_offset);
do {
- start = u64_stats_fetch_begin_bh(syncp);
+ start = u64_stats_fetch_begin_irq(syncp);
v = *(((u64 *) bhptr) + offt);
- } while (u64_stats_fetch_retry_bh(syncp, start));
+ } while (u64_stats_fetch_retry_irq(syncp, start));
res += v;
}
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c
index 717902669d2f..a2afa89513a0 100644
--- a/net/ipv4/ah4.c
+++ b/net/ipv4/ah4.c
@@ -155,6 +155,10 @@ static int ah_output(struct xfrm_state *x, struct sk_buff *skb)
struct iphdr *iph, *top_iph;
struct ip_auth_hdr *ah;
struct ah_data *ahp;
+ int seqhi_len = 0;
+ __be32 *seqhi;
+ int sglists = 0;
+ struct scatterlist *seqhisg;
ahp = x->data;
ahash = ahp->ahash;
@@ -167,14 +171,19 @@ static int ah_output(struct xfrm_state *x, struct sk_buff *skb)
ah = ip_auth_hdr(skb);
ihl = ip_hdrlen(skb);
+ if (x->props.flags & XFRM_STATE_ESN) {
+ sglists = 1;
+ seqhi_len = sizeof(*seqhi);
+ }
err = -ENOMEM;
- iph = ah_alloc_tmp(ahash, nfrags, ihl);
+ iph = ah_alloc_tmp(ahash, nfrags + sglists, ihl + seqhi_len);
if (!iph)
goto out;
-
- icv = ah_tmp_icv(ahash, iph, ihl);
+ seqhi = (__be32 *)((char *)iph + ihl);
+ icv = ah_tmp_icv(ahash, seqhi, seqhi_len);
req = ah_tmp_req(ahash, icv);
sg = ah_req_sg(ahash, req);
+ seqhisg = sg + nfrags;
memset(ah->auth_data, 0, ahp->icv_trunc_len);
@@ -210,10 +219,15 @@ static int ah_output(struct xfrm_state *x, struct sk_buff *skb)
ah->spi = x->id.spi;
ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
- sg_init_table(sg, nfrags);
- skb_to_sgvec(skb, sg, 0, skb->len);
+ sg_init_table(sg, nfrags + sglists);
+ skb_to_sgvec_nomark(skb, sg, 0, skb->len);
- ahash_request_set_crypt(req, sg, icv, skb->len);
+ if (x->props.flags & XFRM_STATE_ESN) {
+ /* Attach seqhi sg right after packet payload */
+ *seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
+ sg_set_buf(seqhisg, seqhi, seqhi_len);
+ }
+ ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len);
ahash_request_set_callback(req, 0, ah_output_done, skb);
AH_SKB_CB(skb)->tmp = iph;
@@ -295,6 +309,10 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
struct ip_auth_hdr *ah;
struct ah_data *ahp;
int err = -ENOMEM;
+ int seqhi_len = 0;
+ __be32 *seqhi;
+ int sglists = 0;
+ struct scatterlist *seqhisg;
if (!pskb_may_pull(skb, sizeof(*ah)))
goto out;
@@ -335,14 +353,22 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
iph = ip_hdr(skb);
ihl = ip_hdrlen(skb);
- work_iph = ah_alloc_tmp(ahash, nfrags, ihl + ahp->icv_trunc_len);
+ if (x->props.flags & XFRM_STATE_ESN) {
+ sglists = 1;
+ seqhi_len = sizeof(*seqhi);
+ }
+
+ work_iph = ah_alloc_tmp(ahash, nfrags + sglists, ihl +
+ ahp->icv_trunc_len + seqhi_len);
if (!work_iph)
goto out;
- auth_data = ah_tmp_auth(work_iph, ihl);
+ seqhi = (__be32 *)((char *)work_iph + ihl);
+ auth_data = ah_tmp_auth(seqhi, seqhi_len);
icv = ah_tmp_icv(ahash, auth_data, ahp->icv_trunc_len);
req = ah_tmp_req(ahash, icv);
sg = ah_req_sg(ahash, req);
+ seqhisg = sg + nfrags;
memcpy(work_iph, iph, ihl);
memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
@@ -361,10 +387,15 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
skb_push(skb, ihl);
- sg_init_table(sg, nfrags);
- skb_to_sgvec(skb, sg, 0, skb->len);
+ sg_init_table(sg, nfrags + sglists);
+ skb_to_sgvec_nomark(skb, sg, 0, skb->len);
- ahash_request_set_crypt(req, sg, icv, skb->len);
+ if (x->props.flags & XFRM_STATE_ESN) {
+ /* Attach seqhi sg right after packet payload */
+ *seqhi = XFRM_SKB_CB(skb)->seq.input.hi;
+ sg_set_buf(seqhisg, seqhi, seqhi_len);
+ }
+ ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len);
ahash_request_set_callback(req, 0, ah_input_done, skb);
AH_SKB_CB(skb)->tmp = work_iph;
@@ -397,7 +428,7 @@ out:
return err;
}
-static void ah4_err(struct sk_buff *skb, u32 info)
+static int ah4_err(struct sk_buff *skb, u32 info)
{
struct net *net = dev_net(skb->dev);
const struct iphdr *iph = (const struct iphdr *)skb->data;
@@ -407,23 +438,25 @@ static void ah4_err(struct sk_buff *skb, u32 info)
switch (icmp_hdr(skb)->type) {
case ICMP_DEST_UNREACH:
if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
- return;
+ return 0;
case ICMP_REDIRECT:
break;
default:
- return;
+ return 0;
}
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
ah->spi, IPPROTO_AH, AF_INET);
if (!x)
- return;
+ return 0;
if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0);
else
ipv4_redirect(skb, net, 0, 0, IPPROTO_AH, 0);
xfrm_state_put(x);
+
+ return 0;
}
static int ah_init_state(struct xfrm_state *x)
@@ -505,6 +538,10 @@ static void ah_destroy(struct xfrm_state *x)
kfree(ahp);
}
+static int ah4_rcv_cb(struct sk_buff *skb, int err)
+{
+ return 0;
+}
static const struct xfrm_type ah_type =
{
@@ -518,11 +555,12 @@ static const struct xfrm_type ah_type =
.output = ah_output
};
-static const struct net_protocol ah4_protocol = {
+static struct xfrm4_protocol ah4_protocol = {
.handler = xfrm4_rcv,
+ .input_handler = xfrm_input,
+ .cb_handler = ah4_rcv_cb,
.err_handler = ah4_err,
- .no_policy = 1,
- .netns_ok = 1,
+ .priority = 0,
};
static int __init ah4_init(void)
@@ -531,7 +569,7 @@ static int __init ah4_init(void)
pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
- if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) {
+ if (xfrm4_protocol_register(&ah4_protocol, IPPROTO_AH) < 0) {
pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&ah_type, AF_INET);
return -EAGAIN;
@@ -541,7 +579,7 @@ static int __init ah4_init(void)
static void __exit ah4_fini(void)
{
- if (inet_del_protocol(&ah4_protocol, IPPROTO_AH) < 0)
+ if (xfrm4_protocol_deregister(&ah4_protocol, IPPROTO_AH) < 0)
pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&ah_type, AF_INET) < 0)
pr_info("%s: can't remove xfrm type\n", __func__);
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 7785b28061ac..360b565918c4 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -473,7 +473,7 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
net_adj) & ~(blksize - 1)) + net_adj - 2;
}
-static void esp4_err(struct sk_buff *skb, u32 info)
+static int esp4_err(struct sk_buff *skb, u32 info)
{
struct net *net = dev_net(skb->dev);
const struct iphdr *iph = (const struct iphdr *)skb->data;
@@ -483,23 +483,25 @@ static void esp4_err(struct sk_buff *skb, u32 info)
switch (icmp_hdr(skb)->type) {
case ICMP_DEST_UNREACH:
if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
- return;
+ return 0;
case ICMP_REDIRECT:
break;
default:
- return;
+ return 0;
}
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
esph->spi, IPPROTO_ESP, AF_INET);
if (!x)
- return;
+ return 0;
if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0);
else
ipv4_redirect(skb, net, 0, 0, IPPROTO_ESP, 0);
xfrm_state_put(x);
+
+ return 0;
}
static void esp_destroy(struct xfrm_state *x)
@@ -672,6 +674,11 @@ error:
return err;
}
+static int esp4_rcv_cb(struct sk_buff *skb, int err)
+{
+ return 0;
+}
+
static const struct xfrm_type esp_type =
{
.description = "ESP4",
@@ -685,11 +692,12 @@ static const struct xfrm_type esp_type =
.output = esp_output
};
-static const struct net_protocol esp4_protocol = {
+static struct xfrm4_protocol esp4_protocol = {
.handler = xfrm4_rcv,
+ .input_handler = xfrm_input,
+ .cb_handler = esp4_rcv_cb,
.err_handler = esp4_err,
- .no_policy = 1,
- .netns_ok = 1,
+ .priority = 0,
};
static int __init esp4_init(void)
@@ -698,7 +706,7 @@ static int __init esp4_init(void)
pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
- if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
+ if (xfrm4_protocol_register(&esp4_protocol, IPPROTO_ESP) < 0) {
pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&esp_type, AF_INET);
return -EAGAIN;
@@ -708,7 +716,7 @@ static int __init esp4_init(void)
static void __exit esp4_fini(void)
{
- if (inet_del_protocol(&esp4_protocol, IPPROTO_ESP) < 0)
+ if (xfrm4_protocol_deregister(&esp4_protocol, IPPROTO_ESP) < 0)
pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&esp_type, AF_INET) < 0)
pr_info("%s: can't remove xfrm type\n", __func__);
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index c7539e22868b..1a629f870274 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -659,7 +659,7 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
if (nlmsg_len(cb->nlh) >= sizeof(struct rtmsg) &&
((struct rtmsg *) nlmsg_data(cb->nlh))->rtm_flags & RTM_F_CLONED)
- return ip_rt_dump(skb, cb);
+ return skb->len;
s_h = cb->args[0];
s_e = cb->args[1];
diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
index f3869c186d97..be8abe73bb9f 100644
--- a/net/ipv4/ip_forward.c
+++ b/net/ipv4/ip_forward.c
@@ -127,6 +127,10 @@ int ip_forward(struct sk_buff *skb)
struct rtable *rt; /* Route we use */
struct ip_options *opt = &(IPCB(skb)->opt);
+ /* that should never happen */
+ if (skb->pkt_type != PACKET_HOST)
+ goto drop;
+
if (skb_warn_if_lro(skb))
goto drop;
@@ -136,9 +140,6 @@ int ip_forward(struct sk_buff *skb)
if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
return NET_RX_SUCCESS;
- if (skb->pkt_type != PACKET_HOST)
- goto drop;
-
skb_forward_csum(skb);
/*
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 73c6b63bba74..1a0755fea491 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -446,7 +446,6 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
__be16 not_last_frag;
struct rtable *rt = skb_rtable(skb);
int err = 0;
- bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED;
dev = rt->dst.dev;
@@ -456,7 +455,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
iph = ip_hdr(skb);
- mtu = ip_dst_mtu_maybe_forward(&rt->dst, forwarding);
+ mtu = ip_skb_dst_mtu(skb);
if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->local_df) ||
(IPCB(skb)->frag_max_size &&
IPCB(skb)->frag_max_size > mtu))) {
@@ -822,8 +821,7 @@ static int __ip_append_data(struct sock *sk,
fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
- maxnonfragsize = (inet->pmtudisc >= IP_PMTUDISC_DO) ?
- mtu : 0xFFFF;
+ maxnonfragsize = ip_sk_local_df(sk) ? 0xFFFF : mtu;
if (cork->length + length > maxnonfragsize - fragheaderlen) {
ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport,
@@ -1146,8 +1144,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
- maxnonfragsize = (inet->pmtudisc >= IP_PMTUDISC_DO) ?
- mtu : 0xFFFF;
+ maxnonfragsize = ip_sk_local_df(sk) ? 0xFFFF : mtu;
if (cork->length + size > maxnonfragsize - fragheaderlen) {
ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport,
@@ -1308,8 +1305,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
* to fragment the frame generated here. No matter, what transforms
* how transforms change size of the packet, it will come out.
*/
- if (inet->pmtudisc < IP_PMTUDISC_DO)
- skb->local_df = 1;
+ skb->local_df = ip_sk_local_df(sk);
/* DF bit is set when we want to see DF on outgoing frames.
* If local_df is set too, we still allow to fragment this frame
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 580dd96666e0..64741b938632 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -186,7 +186,8 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
}
EXPORT_SYMBOL(ip_cmsg_recv);
-int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc)
+int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc,
+ bool allow_ipv6)
{
int err, val;
struct cmsghdr *cmsg;
@@ -194,6 +195,22 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc)
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (!CMSG_OK(msg, cmsg))
return -EINVAL;
+#if defined(CONFIG_IPV6)
+ if (allow_ipv6 &&
+ cmsg->cmsg_level == SOL_IPV6 &&
+ cmsg->cmsg_type == IPV6_PKTINFO) {
+ struct in6_pktinfo *src_info;
+
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info)))
+ return -EINVAL;
+ src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+ if (!ipv6_addr_v4mapped(&src_info->ipi6_addr))
+ return -EINVAL;
+ ipc->oif = src_info->ipi6_ifindex;
+ ipc->addr = src_info->ipi6_addr.s6_addr32[3];
+ continue;
+ }
+#endif
if (cmsg->cmsg_level != SOL_IP)
continue;
switch (cmsg->cmsg_type) {
@@ -626,7 +643,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
inet->nodefrag = val ? 1 : 0;
break;
case IP_MTU_DISCOVER:
- if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_INTERFACE)
+ if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)
goto e_inval;
inet->pmtudisc = val;
break;
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index a82a22d8f77f..e77381d1df9a 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -235,13 +235,17 @@ static struct hlist_head *ip_bucket(struct ip_tunnel_net *itn,
{
unsigned int h;
__be32 remote;
+ __be32 i_key = parms->i_key;
if (parms->iph.daddr && !ipv4_is_multicast(parms->iph.daddr))
remote = parms->iph.daddr;
else
remote = 0;
- h = ip_tunnel_hash(parms->i_key, remote);
+ if (!(parms->i_flags & TUNNEL_KEY) && (parms->i_flags & VTI_ISVTI))
+ i_key = 0;
+
+ h = ip_tunnel_hash(i_key, remote);
return &itn->tunnels[h];
}
@@ -398,7 +402,7 @@ static struct ip_tunnel *ip_tunnel_create(struct net *net,
fbt = netdev_priv(itn->fb_tunnel_dev);
dev = __ip_tunnel_create(net, itn->fb_tunnel_dev->rtnl_link_ops, parms);
if (IS_ERR(dev))
- return NULL;
+ return ERR_CAST(dev);
dev->mtu = ip_tunnel_bind_dev(dev);
@@ -748,9 +752,13 @@ int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd)
t = ip_tunnel_find(itn, p, itn->fb_tunnel_dev->type);
- if (!t && (cmd == SIOCADDTUNNEL))
+ if (!t && (cmd == SIOCADDTUNNEL)) {
t = ip_tunnel_create(net, itn, p);
-
+ if (IS_ERR(t)) {
+ err = PTR_ERR(t);
+ break;
+ }
+ }
if (dev != itn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
if (t != NULL) {
if (t->dev != dev) {
@@ -777,8 +785,9 @@ int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd)
if (t) {
err = 0;
ip_tunnel_update(itn, t, dev, p, true);
- } else
- err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
+ } else {
+ err = -ENOENT;
+ }
break;
case SIOCDELTUNNEL:
@@ -993,19 +1002,13 @@ int ip_tunnel_init(struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
struct iphdr *iph = &tunnel->parms.iph;
- int i, err;
+ int err;
dev->destructor = ip_tunnel_dev_free;
- dev->tstats = alloc_percpu(struct pcpu_sw_netstats);
+ dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!dev->tstats)
return -ENOMEM;
- for_each_possible_cpu(i) {
- struct pcpu_sw_netstats *ipt_stats;
- ipt_stats = per_cpu_ptr(dev->tstats, i);
- u64_stats_init(&ipt_stats->syncp);
- }
-
tunnel->dst_cache = alloc_percpu(struct ip_tunnel_dst);
if (!tunnel->dst_cache) {
free_percpu(dev->tstats);
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index 8d69626f2206..e0c2b1d2ea4e 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -162,12 +162,12 @@ struct rtnl_link_stats64 *ip_tunnel_get_stats64(struct net_device *dev,
unsigned int start;
do {
- start = u64_stats_fetch_begin_bh(&tstats->syncp);
+ start = u64_stats_fetch_begin_irq(&tstats->syncp);
rx_packets = tstats->rx_packets;
tx_packets = tstats->tx_packets;
rx_bytes = tstats->rx_bytes;
tx_bytes = tstats->tx_bytes;
- } while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&tstats->syncp, start));
tot->rx_packets += rx_packets;
tot->tx_packets += tx_packets;
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 48eafae51769..687ddef4e574 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -34,6 +34,7 @@
#include <linux/init.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_ether.h>
+#include <linux/icmpv6.h>
#include <net/sock.h>
#include <net/ip.h>
@@ -49,8 +50,8 @@ static struct rtnl_link_ops vti_link_ops __read_mostly;
static int vti_net_id __read_mostly;
static int vti_tunnel_init(struct net_device *dev);
-/* We dont digest the packet therefore let the packet pass */
-static int vti_rcv(struct sk_buff *skb)
+static int vti_input(struct sk_buff *skb, int nexthdr, __be32 spi,
+ int encap_type)
{
struct ip_tunnel *tunnel;
const struct iphdr *iph = ip_hdr(skb);
@@ -60,79 +61,120 @@ static int vti_rcv(struct sk_buff *skb)
tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
iph->saddr, iph->daddr, 0);
if (tunnel != NULL) {
- struct pcpu_sw_netstats *tstats;
- u32 oldmark = skb->mark;
- int ret;
-
-
- /* temporarily mark the skb with the tunnel o_key, to
- * only match policies with this mark.
- */
- skb->mark = be32_to_cpu(tunnel->parms.o_key);
- ret = xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb);
- skb->mark = oldmark;
- if (!ret)
- return -1;
-
- tstats = this_cpu_ptr(tunnel->dev->tstats);
- u64_stats_update_begin(&tstats->syncp);
- tstats->rx_packets++;
- tstats->rx_bytes += skb->len;
- u64_stats_update_end(&tstats->syncp);
-
- secpath_reset(skb);
- skb->dev = tunnel->dev;
+ if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
+ goto drop;
+
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = tunnel;
+ skb->mark = be32_to_cpu(tunnel->parms.i_key);
+
+ return xfrm_input(skb, nexthdr, spi, encap_type);
+ }
+
+ return -EINVAL;
+drop:
+ kfree_skb(skb);
+ return 0;
+}
+
+static int vti_rcv(struct sk_buff *skb)
+{
+ XFRM_SPI_SKB_CB(skb)->family = AF_INET;
+ XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
+
+ return vti_input(skb, ip_hdr(skb)->protocol, 0, 0);
+}
+
+static int vti_rcv_cb(struct sk_buff *skb, int err)
+{
+ unsigned short family;
+ struct net_device *dev;
+ struct pcpu_sw_netstats *tstats;
+ struct xfrm_state *x;
+ struct ip_tunnel *tunnel = XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4;
+
+ if (!tunnel)
return 1;
+
+ dev = tunnel->dev;
+
+ if (err) {
+ dev->stats.rx_errors++;
+ dev->stats.rx_dropped++;
+
+ return 0;
}
- return -1;
+ x = xfrm_input_state(skb);
+ family = x->inner_mode->afinfo->family;
+
+ if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family))
+ return -EPERM;
+
+ skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(skb->dev)));
+ skb->dev = dev;
+
+ tstats = this_cpu_ptr(dev->tstats);
+
+ u64_stats_update_begin(&tstats->syncp);
+ tstats->rx_packets++;
+ tstats->rx_bytes += skb->len;
+ u64_stats_update_end(&tstats->syncp);
+
+ return 0;
}
-/* This function assumes it is being called from dev_queue_xmit()
- * and that skb is filled properly by that function.
- */
+static bool vti_state_check(const struct xfrm_state *x, __be32 dst, __be32 src)
+{
+ xfrm_address_t *daddr = (xfrm_address_t *)&dst;
+ xfrm_address_t *saddr = (xfrm_address_t *)&src;
-static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+ /* if there is no transform then this tunnel is not functional.
+ * Or if the xfrm is not mode tunnel.
+ */
+ if (!x || x->props.mode != XFRM_MODE_TUNNEL ||
+ x->props.family != AF_INET)
+ return false;
+
+ if (!dst)
+ return xfrm_addr_equal(saddr, &x->props.saddr, AF_INET);
+
+ if (!xfrm_state_addr_check(x, daddr, saddr, AF_INET))
+ return false;
+
+ return true;
+}
+
+static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev,
+ struct flowi *fl)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
- struct iphdr *tiph = &tunnel->parms.iph;
- u8 tos;
- struct rtable *rt; /* Route to the other host */
+ struct ip_tunnel_parm *parms = &tunnel->parms;
+ struct dst_entry *dst = skb_dst(skb);
struct net_device *tdev; /* Device to other host */
- struct iphdr *old_iph = ip_hdr(skb);
- __be32 dst = tiph->daddr;
- struct flowi4 fl4;
int err;
- if (skb->protocol != htons(ETH_P_IP))
- goto tx_error;
-
- tos = old_iph->tos;
+ if (!dst) {
+ dev->stats.tx_carrier_errors++;
+ goto tx_error_icmp;
+ }
- memset(&fl4, 0, sizeof(fl4));
- flowi4_init_output(&fl4, tunnel->parms.link,
- be32_to_cpu(tunnel->parms.o_key), RT_TOS(tos),
- RT_SCOPE_UNIVERSE,
- IPPROTO_IPIP, 0,
- dst, tiph->saddr, 0, 0);
- rt = ip_route_output_key(dev_net(dev), &fl4);
- if (IS_ERR(rt)) {
+ dst_hold(dst);
+ dst = xfrm_lookup(tunnel->net, dst, fl, NULL, 0);
+ if (IS_ERR(dst)) {
dev->stats.tx_carrier_errors++;
goto tx_error_icmp;
}
- /* if there is no transform then this tunnel is not functional.
- * Or if the xfrm is not mode tunnel.
- */
- if (!rt->dst.xfrm ||
- rt->dst.xfrm->props.mode != XFRM_MODE_TUNNEL) {
+
+ if (!vti_state_check(dst->xfrm, parms->iph.daddr, parms->iph.saddr)) {
dev->stats.tx_carrier_errors++;
- ip_rt_put(rt);
+ dst_release(dst);
goto tx_error_icmp;
}
- tdev = rt->dst.dev;
+
+ tdev = dst->dev;
if (tdev == dev) {
- ip_rt_put(rt);
+ dst_release(dst);
dev->stats.collisions++;
goto tx_error;
}
@@ -146,10 +188,8 @@ static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
tunnel->err_count = 0;
}
- memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
- skb_dst_drop(skb);
- skb_dst_set(skb, &rt->dst);
- nf_reset(skb);
+ skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(dev)));
+ skb_dst_set(skb, dst);
skb->dev = skb_dst(skb)->dev;
err = dst_output(skb);
@@ -166,6 +206,95 @@ tx_error:
return NETDEV_TX_OK;
}
+/* This function assumes it is being called from dev_queue_xmit()
+ * and that skb is filled properly by that function.
+ */
+static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ip_tunnel *tunnel = netdev_priv(dev);
+ struct flowi fl;
+
+ memset(&fl, 0, sizeof(fl));
+
+ skb->mark = be32_to_cpu(tunnel->parms.o_key);
+
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ xfrm_decode_session(skb, &fl, AF_INET);
+ memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
+ break;
+ case htons(ETH_P_IPV6):
+ xfrm_decode_session(skb, &fl, AF_INET6);
+ memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
+ break;
+ default:
+ dev->stats.tx_errors++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ return vti_xmit(skb, dev, &fl);
+}
+
+static int vti4_err(struct sk_buff *skb, u32 info)
+{
+ __be32 spi;
+ struct xfrm_state *x;
+ struct ip_tunnel *tunnel;
+ struct ip_esp_hdr *esph;
+ struct ip_auth_hdr *ah ;
+ struct ip_comp_hdr *ipch;
+ struct net *net = dev_net(skb->dev);
+ const struct iphdr *iph = (const struct iphdr *)skb->data;
+ int protocol = iph->protocol;
+ struct ip_tunnel_net *itn = net_generic(net, vti_net_id);
+
+ tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
+ iph->daddr, iph->saddr, 0);
+ if (!tunnel)
+ return -1;
+
+ switch (protocol) {
+ case IPPROTO_ESP:
+ esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2));
+ spi = esph->spi;
+ break;
+ case IPPROTO_AH:
+ ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2));
+ spi = ah->spi;
+ break;
+ case IPPROTO_COMP:
+ ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2));
+ spi = htonl(ntohs(ipch->cpi));
+ break;
+ default:
+ return 0;
+ }
+
+ switch (icmp_hdr(skb)->type) {
+ case ICMP_DEST_UNREACH:
+ if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
+ return 0;
+ case ICMP_REDIRECT:
+ break;
+ default:
+ return 0;
+ }
+
+ x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
+ spi, protocol, AF_INET);
+ if (!x)
+ return 0;
+
+ if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
+ ipv4_update_pmtu(skb, net, info, 0, 0, protocol, 0);
+ else
+ ipv4_redirect(skb, net, 0, 0, protocol, 0);
+ xfrm_state_put(x);
+
+ return 0;
+}
+
static int
vti_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
@@ -181,12 +310,13 @@ vti_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -EINVAL;
}
+ p.i_flags |= VTI_ISVTI;
err = ip_tunnel_ioctl(dev, &p, cmd);
if (err)
return err;
if (cmd != SIOCDELTUNNEL) {
- p.i_flags |= GRE_KEY | VTI_ISVTI;
+ p.i_flags |= GRE_KEY;
p.o_flags |= GRE_KEY;
}
@@ -224,7 +354,6 @@ static int vti_tunnel_init(struct net_device *dev)
dev->flags = IFF_NOARP;
dev->iflink = 0;
dev->addr_len = 4;
- dev->features |= NETIF_F_NETNS_LOCAL;
dev->features |= NETIF_F_LLTX;
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
@@ -241,9 +370,28 @@ static void __net_init vti_fb_tunnel_init(struct net_device *dev)
iph->ihl = 5;
}
-static struct xfrm_tunnel_notifier vti_handler __read_mostly = {
+static struct xfrm4_protocol vti_esp4_protocol __read_mostly = {
.handler = vti_rcv,
- .priority = 1,
+ .input_handler = vti_input,
+ .cb_handler = vti_rcv_cb,
+ .err_handler = vti4_err,
+ .priority = 100,
+};
+
+static struct xfrm4_protocol vti_ah4_protocol __read_mostly = {
+ .handler = vti_rcv,
+ .input_handler = vti_input,
+ .cb_handler = vti_rcv_cb,
+ .err_handler = vti4_err,
+ .priority = 100,
+};
+
+static struct xfrm4_protocol vti_ipcomp4_protocol __read_mostly = {
+ .handler = vti_rcv,
+ .input_handler = vti_input,
+ .cb_handler = vti_rcv_cb,
+ .err_handler = vti4_err,
+ .priority = 100,
};
static int __net_init vti_init_net(struct net *net)
@@ -287,6 +435,8 @@ static void vti_netlink_parms(struct nlattr *data[],
if (!data)
return;
+ parms->i_flags = VTI_ISVTI;
+
if (data[IFLA_VTI_LINK])
parms->link = nla_get_u32(data[IFLA_VTI_LINK]);
@@ -382,10 +532,31 @@ static int __init vti_init(void)
err = register_pernet_device(&vti_net_ops);
if (err < 0)
return err;
- err = xfrm4_mode_tunnel_input_register(&vti_handler);
+ err = xfrm4_protocol_register(&vti_esp4_protocol, IPPROTO_ESP);
+ if (err < 0) {
+ unregister_pernet_device(&vti_net_ops);
+ pr_info("vti init: can't register tunnel\n");
+
+ return err;
+ }
+
+ err = xfrm4_protocol_register(&vti_ah4_protocol, IPPROTO_AH);
+ if (err < 0) {
+ xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP);
+ unregister_pernet_device(&vti_net_ops);
+ pr_info("vti init: can't register tunnel\n");
+
+ return err;
+ }
+
+ err = xfrm4_protocol_register(&vti_ipcomp4_protocol, IPPROTO_COMP);
if (err < 0) {
+ xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH);
+ xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP);
unregister_pernet_device(&vti_net_ops);
pr_info("vti init: can't register tunnel\n");
+
+ return err;
}
err = rtnl_link_register(&vti_link_ops);
@@ -395,7 +566,9 @@ static int __init vti_init(void)
return err;
rtnl_link_failed:
- xfrm4_mode_tunnel_input_deregister(&vti_handler);
+ xfrm4_protocol_deregister(&vti_ipcomp4_protocol, IPPROTO_COMP);
+ xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH);
+ xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP);
unregister_pernet_device(&vti_net_ops);
return err;
}
@@ -403,8 +576,13 @@ rtnl_link_failed:
static void __exit vti_fini(void)
{
rtnl_link_unregister(&vti_link_ops);
- if (xfrm4_mode_tunnel_input_deregister(&vti_handler))
+ if (xfrm4_protocol_deregister(&vti_ipcomp4_protocol, IPPROTO_COMP))
pr_info("vti close: can't deregister tunnel\n");
+ if (xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH))
+ pr_info("vti close: can't deregister tunnel\n");
+ if (xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP))
+ pr_info("vti close: can't deregister tunnel\n");
+
unregister_pernet_device(&vti_net_ops);
}
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index 826be4cb482a..c0855d50a3fa 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -23,7 +23,7 @@
#include <net/protocol.h>
#include <net/sock.h>
-static void ipcomp4_err(struct sk_buff *skb, u32 info)
+static int ipcomp4_err(struct sk_buff *skb, u32 info)
{
struct net *net = dev_net(skb->dev);
__be32 spi;
@@ -34,24 +34,26 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info)
switch (icmp_hdr(skb)->type) {
case ICMP_DEST_UNREACH:
if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
- return;
+ return 0;
case ICMP_REDIRECT:
break;
default:
- return;
+ return 0;
}
spi = htonl(ntohs(ipch->cpi));
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
spi, IPPROTO_COMP, AF_INET);
if (!x)
- return;
+ return 0;
if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_COMP, 0);
else
ipv4_redirect(skb, net, 0, 0, IPPROTO_COMP, 0);
xfrm_state_put(x);
+
+ return 0;
}
/* We always hold one tunnel user reference to indicate a tunnel */
@@ -147,6 +149,11 @@ out:
return err;
}
+static int ipcomp4_rcv_cb(struct sk_buff *skb, int err)
+{
+ return 0;
+}
+
static const struct xfrm_type ipcomp_type = {
.description = "IPCOMP4",
.owner = THIS_MODULE,
@@ -157,11 +164,12 @@ static const struct xfrm_type ipcomp_type = {
.output = ipcomp_output
};
-static const struct net_protocol ipcomp4_protocol = {
+static struct xfrm4_protocol ipcomp4_protocol = {
.handler = xfrm4_rcv,
+ .input_handler = xfrm_input,
+ .cb_handler = ipcomp4_rcv_cb,
.err_handler = ipcomp4_err,
- .no_policy = 1,
- .netns_ok = 1,
+ .priority = 0,
};
static int __init ipcomp4_init(void)
@@ -170,7 +178,7 @@ static int __init ipcomp4_init(void)
pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
- if (inet_add_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0) {
+ if (xfrm4_protocol_register(&ipcomp4_protocol, IPPROTO_COMP) < 0) {
pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&ipcomp_type, AF_INET);
return -EAGAIN;
@@ -180,7 +188,7 @@ static int __init ipcomp4_init(void)
static void __exit ipcomp4_fini(void)
{
- if (inet_del_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0)
+ if (xfrm4_protocol_deregister(&ipcomp4_protocol, IPPROTO_COMP) < 0)
pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&ipcomp_type, AF_INET) < 0)
pr_info("%s: can't remove xfrm type\n", __func__);
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
index c3e0adea9c27..7ebd6e37875c 100644
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -61,7 +61,7 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
skb_dst_set(skb, NULL);
dst = xfrm_lookup(net, dst, flowi4_to_flowi(&fl4), skb->sk, 0);
if (IS_ERR(dst))
- return PTR_ERR(dst);;
+ return PTR_ERR(dst);
skb_dst_set(skb, dst);
}
#endif
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 2d11c094296e..f4b19e5dde54 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -727,7 +727,7 @@ static int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
sock_tx_timestamp(sk, &ipc.tx_flags);
if (msg->msg_controllen) {
- err = ip_cmsg_send(sock_net(sk), msg, &ipc);
+ err = ip_cmsg_send(sock_net(sk), msg, &ipc, false);
if (err)
return err;
if (ipc.opt)
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index a6c8a80ec9d6..ad737fad6d8b 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -273,6 +273,7 @@ static const struct snmp_mib snmp4_net_list[] = {
SNMP_MIB_ITEM("TCPChallengeACK", LINUX_MIB_TCPCHALLENGEACK),
SNMP_MIB_ITEM("TCPSYNChallenge", LINUX_MIB_TCPSYNCHALLENGE),
SNMP_MIB_ITEM("TCPFastOpenActive", LINUX_MIB_TCPFASTOPENACTIVE),
+ SNMP_MIB_ITEM("TCPFastOpenActiveFail", LINUX_MIB_TCPFASTOPENACTIVEFAIL),
SNMP_MIB_ITEM("TCPFastOpenPassive", LINUX_MIB_TCPFASTOPENPASSIVE),
SNMP_MIB_ITEM("TCPFastOpenPassiveFail", LINUX_MIB_TCPFASTOPENPASSIVEFAIL),
SNMP_MIB_ITEM("TCPFastOpenListenOverflow", LINUX_MIB_TCPFASTOPENLISTENOVERFLOW),
@@ -280,6 +281,11 @@ static const struct snmp_mib snmp4_net_list[] = {
SNMP_MIB_ITEM("TCPSpuriousRtxHostQueues", LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES),
SNMP_MIB_ITEM("BusyPollRxPackets", LINUX_MIB_BUSYPOLLRXPACKETS),
SNMP_MIB_ITEM("TCPAutoCorking", LINUX_MIB_TCPAUTOCORKING),
+ SNMP_MIB_ITEM("TCPFromZeroWindowAdv", LINUX_MIB_TCPFROMZEROWINDOWADV),
+ SNMP_MIB_ITEM("TCPToZeroWindowAdv", LINUX_MIB_TCPTOZEROWINDOWADV),
+ SNMP_MIB_ITEM("TCPWantZeroWindowAdv", LINUX_MIB_TCPWANTZEROWINDOWADV),
+ SNMP_MIB_ITEM("TCPSynRetrans", LINUX_MIB_TCPSYNRETRANS),
+ SNMP_MIB_ITEM("TCPOrigDataSent", LINUX_MIB_TCPORIGDATASENT),
SNMP_MIB_SENTINEL
};
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index c04518f4850a..a9dbe58bdfe7 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -524,7 +524,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
ipc.oif = sk->sk_bound_dev_if;
if (msg->msg_controllen) {
- err = ip_cmsg_send(sock_net(sk), msg, &ipc);
+ err = ip_cmsg_send(sock_net(sk), msg, &ipc, false);
if (err)
goto out;
if (ipc.opt)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 4c011ec69ed4..1be9e990514d 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -139,11 +139,6 @@ static void ip_do_redirect(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb);
static void ipv4_dst_destroy(struct dst_entry *dst);
-static void ipv4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
- int how)
-{
-}
-
static u32 *ipv4_cow_metrics(struct dst_entry *dst, unsigned long old)
{
WARN_ON(1);
@@ -162,7 +157,6 @@ static struct dst_ops ipv4_dst_ops = {
.mtu = ipv4_mtu,
.cow_metrics = ipv4_cow_metrics,
.destroy = ipv4_dst_destroy,
- .ifdown = ipv4_dst_ifdown,
.negative_advice = ipv4_negative_advice,
.link_failure = ipv4_link_failure,
.update_pmtu = ip_rt_update_pmtu,
@@ -697,7 +691,6 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
out_unlock:
spin_unlock_bh(&fnhe_lock);
- return;
}
static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flowi4 *fl4,
@@ -2475,11 +2468,6 @@ errout_free:
goto errout;
}
-int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb)
-{
- return skb->len;
-}
-
void ip_rt_multicast_event(struct in_device *in_dev)
{
rt_cache_flush(dev_net(in_dev->dev));
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 97c8f5620c43..4bd6d52eeffb 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -387,7 +387,7 @@ void tcp_init_sock(struct sock *sk)
INIT_LIST_HEAD(&tp->tsq_node);
icsk->icsk_rto = TCP_TIMEOUT_INIT;
- tp->mdev = TCP_TIMEOUT_INIT;
+ tp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT);
/* So many TCP implementations out there (incorrectly) count the
* initial SYN frame in their delayed-ACK and congestion control
@@ -2341,7 +2341,7 @@ int tcp_disconnect(struct sock *sk, int flags)
sk->sk_shutdown = 0;
sock_reset_flag(sk, SOCK_DONE);
- tp->srtt = 0;
+ tp->srtt_us = 0;
if ((tp->write_seq += tp->max_window + 2) == 0)
tp->write_seq = 1;
icsk->icsk_backoff = 0;
@@ -2785,8 +2785,8 @@ void tcp_get_info(const struct sock *sk, struct tcp_info *info)
info->tcpi_pmtu = icsk->icsk_pmtu_cookie;
info->tcpi_rcv_ssthresh = tp->rcv_ssthresh;
- info->tcpi_rtt = jiffies_to_usecs(tp->srtt)>>3;
- info->tcpi_rttvar = jiffies_to_usecs(tp->mdev)>>2;
+ info->tcpi_rtt = tp->srtt_us >> 3;
+ info->tcpi_rttvar = tp->mdev_us >> 2;
info->tcpi_snd_ssthresh = tp->snd_ssthresh;
info->tcpi_snd_cwnd = tp->snd_cwnd;
info->tcpi_advmss = tp->advmss;
@@ -2796,6 +2796,11 @@ void tcp_get_info(const struct sock *sk, struct tcp_info *info)
info->tcpi_rcv_space = tp->rcvq_space.space;
info->tcpi_total_retrans = tp->total_retrans;
+
+ info->tcpi_pacing_rate = sk->sk_pacing_rate != ~0U ?
+ sk->sk_pacing_rate : ~0ULL;
+ info->tcpi_max_pacing_rate = sk->sk_max_pacing_rate != ~0U ?
+ sk->sk_max_pacing_rate : ~0ULL;
}
EXPORT_SYMBOL_GPL(tcp_get_info);
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index 2388275adb9b..2b9464c93b88 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -361,21 +361,12 @@ u32 tcp_reno_ssthresh(struct sock *sk)
}
EXPORT_SYMBOL_GPL(tcp_reno_ssthresh);
-/* Lower bound on congestion window with halving. */
-u32 tcp_reno_min_cwnd(const struct sock *sk)
-{
- const struct tcp_sock *tp = tcp_sk(sk);
- return tp->snd_ssthresh/2;
-}
-EXPORT_SYMBOL_GPL(tcp_reno_min_cwnd);
-
struct tcp_congestion_ops tcp_reno = {
.flags = TCP_CONG_NON_RESTRICTED,
.name = "reno",
.owner = THIS_MODULE,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_reno_cong_avoid,
- .min_cwnd = tcp_reno_min_cwnd,
};
/* Initial congestion control used (until SYN)
@@ -387,6 +378,5 @@ struct tcp_congestion_ops tcp_init_congestion_ops = {
.owner = THIS_MODULE,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_reno_cong_avoid,
- .min_cwnd = tcp_reno_min_cwnd,
};
EXPORT_SYMBOL_GPL(tcp_init_congestion_ops);
diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c
index 828e4c3ffbaf..8bf224516ba2 100644
--- a/net/ipv4/tcp_cubic.c
+++ b/net/ipv4/tcp_cubic.c
@@ -476,10 +476,6 @@ static int __init cubictcp_register(void)
/* divide by bic_scale and by constant Srtt (100ms) */
do_div(cube_factor, bic_scale * 10);
- /* hystart needs ms clock resolution */
- if (hystart && HZ < 1000)
- cubictcp.flags |= TCP_CONG_RTT_STAMP;
-
return tcp_register_congestion_control(&cubictcp);
}
diff --git a/net/ipv4/tcp_highspeed.c b/net/ipv4/tcp_highspeed.c
index 8ed9305dfdf4..8b9e7bad77c0 100644
--- a/net/ipv4/tcp_highspeed.c
+++ b/net/ipv4/tcp_highspeed.c
@@ -162,7 +162,6 @@ static struct tcp_congestion_ops tcp_highspeed __read_mostly = {
.init = hstcp_init,
.ssthresh = hstcp_ssthresh,
.cong_avoid = hstcp_cong_avoid,
- .min_cwnd = tcp_reno_min_cwnd,
.owner = THIS_MODULE,
.name = "highspeed"
diff --git a/net/ipv4/tcp_hybla.c b/net/ipv4/tcp_hybla.c
index 478fe82611bf..a15a799bf768 100644
--- a/net/ipv4/tcp_hybla.c
+++ b/net/ipv4/tcp_hybla.c
@@ -21,7 +21,7 @@ struct hybla {
u32 rho2; /* Rho * Rho, integer part */
u32 rho_3ls; /* Rho parameter, <<3 */
u32 rho2_7ls; /* Rho^2, <<7 */
- u32 minrtt; /* Minimum smoothed round trip time value seen */
+ u32 minrtt_us; /* Minimum smoothed round trip time value seen */
};
/* Hybla reference round trip time (default= 1/40 sec = 25 ms), in ms */
@@ -35,7 +35,9 @@ static inline void hybla_recalc_param (struct sock *sk)
{
struct hybla *ca = inet_csk_ca(sk);
- ca->rho_3ls = max_t(u32, tcp_sk(sk)->srtt / msecs_to_jiffies(rtt0), 8);
+ ca->rho_3ls = max_t(u32,
+ tcp_sk(sk)->srtt_us / (rtt0 * USEC_PER_MSEC),
+ 8U);
ca->rho = ca->rho_3ls >> 3;
ca->rho2_7ls = (ca->rho_3ls * ca->rho_3ls) << 1;
ca->rho2 = ca->rho2_7ls >> 7;
@@ -59,7 +61,7 @@ static void hybla_init(struct sock *sk)
hybla_recalc_param(sk);
/* set minimum rtt as this is the 1st ever seen */
- ca->minrtt = tp->srtt;
+ ca->minrtt_us = tp->srtt_us;
tp->snd_cwnd = ca->rho;
}
@@ -94,9 +96,9 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked,
int is_slowstart = 0;
/* Recalculate rho only if this srtt is the lowest */
- if (tp->srtt < ca->minrtt){
+ if (tp->srtt_us < ca->minrtt_us) {
hybla_recalc_param(sk);
- ca->minrtt = tp->srtt;
+ ca->minrtt_us = tp->srtt_us;
}
if (!tcp_is_cwnd_limited(sk, in_flight))
@@ -166,7 +168,6 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked,
static struct tcp_congestion_ops tcp_hybla __read_mostly = {
.init = hybla_init,
.ssthresh = tcp_reno_ssthresh,
- .min_cwnd = tcp_reno_min_cwnd,
.cong_avoid = hybla_cong_avoid,
.set_state = hybla_state,
diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c
index e498a62b8f97..863d105e3015 100644
--- a/net/ipv4/tcp_illinois.c
+++ b/net/ipv4/tcp_illinois.c
@@ -325,10 +325,8 @@ static void tcp_illinois_info(struct sock *sk, u32 ext,
}
static struct tcp_congestion_ops tcp_illinois __read_mostly = {
- .flags = TCP_CONG_RTT_STAMP,
.init = tcp_illinois_init,
.ssthresh = tcp_illinois_ssthresh,
- .min_cwnd = tcp_reno_min_cwnd,
.cong_avoid = tcp_illinois_cong_avoid,
.set_state = tcp_illinois_state,
.get_info = tcp_illinois_info,
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index eeaac399420d..e1661f46fd19 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -667,11 +667,11 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb)
* To save cycles in the RFC 1323 implementation it was better to break
* it up into three procedures. -- erics
*/
-static void tcp_rtt_estimator(struct sock *sk, const __u32 mrtt)
+static void tcp_rtt_estimator(struct sock *sk, long mrtt_us)
{
struct tcp_sock *tp = tcp_sk(sk);
- long m = mrtt; /* RTT */
- u32 srtt = tp->srtt;
+ long m = mrtt_us; /* RTT */
+ u32 srtt = tp->srtt_us;
/* The following amusing code comes from Jacobson's
* article in SIGCOMM '88. Note that rtt and mdev
@@ -694,7 +694,7 @@ static void tcp_rtt_estimator(struct sock *sk, const __u32 mrtt)
srtt += m; /* rtt = 7/8 rtt + 1/8 new */
if (m < 0) {
m = -m; /* m is now abs(error) */
- m -= (tp->mdev >> 2); /* similar update on mdev */
+ m -= (tp->mdev_us >> 2); /* similar update on mdev */
/* This is similar to one of Eifel findings.
* Eifel blocks mdev updates when rtt decreases.
* This solution is a bit different: we use finer gain
@@ -706,28 +706,29 @@ static void tcp_rtt_estimator(struct sock *sk, const __u32 mrtt)
if (m > 0)
m >>= 3;
} else {
- m -= (tp->mdev >> 2); /* similar update on mdev */
+ m -= (tp->mdev_us >> 2); /* similar update on mdev */
}
- tp->mdev += m; /* mdev = 3/4 mdev + 1/4 new */
- if (tp->mdev > tp->mdev_max) {
- tp->mdev_max = tp->mdev;
- if (tp->mdev_max > tp->rttvar)
- tp->rttvar = tp->mdev_max;
+ tp->mdev_us += m; /* mdev = 3/4 mdev + 1/4 new */
+ if (tp->mdev_us > tp->mdev_max_us) {
+ tp->mdev_max_us = tp->mdev_us;
+ if (tp->mdev_max_us > tp->rttvar_us)
+ tp->rttvar_us = tp->mdev_max_us;
}
if (after(tp->snd_una, tp->rtt_seq)) {
- if (tp->mdev_max < tp->rttvar)
- tp->rttvar -= (tp->rttvar - tp->mdev_max) >> 2;
+ if (tp->mdev_max_us < tp->rttvar_us)
+ tp->rttvar_us -= (tp->rttvar_us - tp->mdev_max_us) >> 2;
tp->rtt_seq = tp->snd_nxt;
- tp->mdev_max = tcp_rto_min(sk);
+ tp->mdev_max_us = tcp_rto_min_us(sk);
}
} else {
/* no previous measure. */
srtt = m << 3; /* take the measured time to be rtt */
- tp->mdev = m << 1; /* make sure rto = 3*rtt */
- tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk));
+ tp->mdev_us = m << 1; /* make sure rto = 3*rtt */
+ tp->rttvar_us = max(tp->mdev_us, tcp_rto_min_us(sk));
+ tp->mdev_max_us = tp->rttvar_us;
tp->rtt_seq = tp->snd_nxt;
}
- tp->srtt = max(1U, srtt);
+ tp->srtt_us = max(1U, srtt);
}
/* Set the sk_pacing_rate to allow proper sizing of TSO packets.
@@ -742,20 +743,12 @@ static void tcp_update_pacing_rate(struct sock *sk)
u64 rate;
/* set sk_pacing_rate to 200 % of current rate (mss * cwnd / srtt) */
- rate = (u64)tp->mss_cache * 2 * (HZ << 3);
+ rate = (u64)tp->mss_cache * 2 * (USEC_PER_SEC << 3);
rate *= max(tp->snd_cwnd, tp->packets_out);
- /* Correction for small srtt and scheduling constraints.
- * For small rtt, consider noise is too high, and use
- * the minimal value (srtt = 1 -> 125 us for HZ=1000)
- *
- * We probably need usec resolution in the future.
- * Note: This also takes care of possible srtt=0 case,
- * when tcp_rtt_estimator() was not yet called.
- */
- if (tp->srtt > 8 + 2)
- do_div(rate, tp->srtt);
+ if (likely(tp->srtt_us))
+ do_div(rate, tp->srtt_us);
/* ACCESS_ONCE() is needed because sch_fq fetches sk_pacing_rate
* without any lock. We want to make sure compiler wont store
@@ -1122,10 +1115,10 @@ static bool tcp_check_dsack(struct sock *sk, const struct sk_buff *ack_skb,
}
struct tcp_sacktag_state {
- int reord;
- int fack_count;
- int flag;
- s32 rtt; /* RTT measured by SACKing never-retransmitted data */
+ int reord;
+ int fack_count;
+ long rtt_us; /* RTT measured by SACKing never-retransmitted data */
+ int flag;
};
/* Check if skb is fully within the SACK block. In presence of GSO skbs,
@@ -1186,7 +1179,8 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb,
static u8 tcp_sacktag_one(struct sock *sk,
struct tcp_sacktag_state *state, u8 sacked,
u32 start_seq, u32 end_seq,
- int dup_sack, int pcount, u32 xmit_time)
+ int dup_sack, int pcount,
+ const struct skb_mstamp *xmit_time)
{
struct tcp_sock *tp = tcp_sk(sk);
int fack_count = state->fack_count;
@@ -1227,8 +1221,13 @@ static u8 tcp_sacktag_one(struct sock *sk,
if (!after(end_seq, tp->high_seq))
state->flag |= FLAG_ORIG_SACK_ACKED;
/* Pick the earliest sequence sacked for RTT */
- if (state->rtt < 0)
- state->rtt = tcp_time_stamp - xmit_time;
+ if (state->rtt_us < 0) {
+ struct skb_mstamp now;
+
+ skb_mstamp_get(&now);
+ state->rtt_us = skb_mstamp_us_delta(&now,
+ xmit_time);
+ }
}
if (sacked & TCPCB_LOST) {
@@ -1287,7 +1286,7 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
*/
tcp_sacktag_one(sk, state, TCP_SKB_CB(skb)->sacked,
start_seq, end_seq, dup_sack, pcount,
- TCP_SKB_CB(skb)->when);
+ &skb->skb_mstamp);
if (skb == tp->lost_skb_hint)
tp->lost_cnt_hint += pcount;
@@ -1565,7 +1564,7 @@ static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk,
TCP_SKB_CB(skb)->end_seq,
dup_sack,
tcp_skb_pcount(skb),
- TCP_SKB_CB(skb)->when);
+ &skb->skb_mstamp);
if (!before(TCP_SKB_CB(skb)->seq,
tcp_highest_sack_seq(tp)))
@@ -1622,7 +1621,7 @@ static int tcp_sack_cache_ok(const struct tcp_sock *tp, const struct tcp_sack_bl
static int
tcp_sacktag_write_queue(struct sock *sk, const struct sk_buff *ack_skb,
- u32 prior_snd_una, s32 *sack_rtt)
+ u32 prior_snd_una, long *sack_rtt_us)
{
struct tcp_sock *tp = tcp_sk(sk);
const unsigned char *ptr = (skb_transport_header(ack_skb) +
@@ -1640,7 +1639,7 @@ tcp_sacktag_write_queue(struct sock *sk, const struct sk_buff *ack_skb,
state.flag = 0;
state.reord = tp->packets_out;
- state.rtt = -1;
+ state.rtt_us = -1L;
if (!tp->sacked_out) {
if (WARN_ON(tp->fackets_out))
@@ -1824,7 +1823,7 @@ out:
WARN_ON((int)tp->retrans_out < 0);
WARN_ON((int)tcp_packets_in_flight(tp) < 0);
#endif
- *sack_rtt = state.rtt;
+ *sack_rtt_us = state.rtt_us;
return state.flag;
}
@@ -2035,10 +2034,12 @@ static bool tcp_pause_early_retransmit(struct sock *sk, int flag)
* available, or RTO is scheduled to fire first.
*/
if (sysctl_tcp_early_retrans < 2 || sysctl_tcp_early_retrans > 3 ||
- (flag & FLAG_ECE) || !tp->srtt)
+ (flag & FLAG_ECE) || !tp->srtt_us)
return false;
- delay = max_t(unsigned long, (tp->srtt >> 5), msecs_to_jiffies(2));
+ delay = max(usecs_to_jiffies(tp->srtt_us >> 5),
+ msecs_to_jiffies(2));
+
if (!time_after(inet_csk(sk)->icsk_timeout, (jiffies + delay)))
return false;
@@ -2885,7 +2886,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked,
}
static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
- s32 seq_rtt, s32 sack_rtt)
+ long seq_rtt_us, long sack_rtt_us)
{
const struct tcp_sock *tp = tcp_sk(sk);
@@ -2895,10 +2896,10 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
* is acked (RFC6298).
*/
if (flag & FLAG_RETRANS_DATA_ACKED)
- seq_rtt = -1;
+ seq_rtt_us = -1L;
- if (seq_rtt < 0)
- seq_rtt = sack_rtt;
+ if (seq_rtt_us < 0)
+ seq_rtt_us = sack_rtt_us;
/* RTTM Rule: A TSecr value received in a segment is used to
* update the averaged RTT measurement only if the segment
@@ -2906,14 +2907,14 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
* left edge of the send window.
* See draft-ietf-tcplw-high-performance-00, section 3.3.
*/
- if (seq_rtt < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
+ if (seq_rtt_us < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
flag & FLAG_ACKED)
- seq_rtt = tcp_time_stamp - tp->rx_opt.rcv_tsecr;
+ seq_rtt_us = jiffies_to_usecs(tcp_time_stamp - tp->rx_opt.rcv_tsecr);
- if (seq_rtt < 0)
+ if (seq_rtt_us < 0)
return false;
- tcp_rtt_estimator(sk, seq_rtt);
+ tcp_rtt_estimator(sk, seq_rtt_us);
tcp_set_rto(sk);
/* RFC6298: only reset backoff on valid RTT measurement. */
@@ -2925,16 +2926,16 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
static void tcp_synack_rtt_meas(struct sock *sk, const u32 synack_stamp)
{
struct tcp_sock *tp = tcp_sk(sk);
- s32 seq_rtt = -1;
+ long seq_rtt_us = -1L;
if (synack_stamp && !tp->total_retrans)
- seq_rtt = tcp_time_stamp - synack_stamp;
+ seq_rtt_us = jiffies_to_usecs(tcp_time_stamp - synack_stamp);
/* If the ACK acks both the SYNACK and the (Fast Open'd) data packets
* sent in SYN_RECV, SYNACK RTT is the smooth RTT computed in tcp_ack()
*/
- if (!tp->srtt)
- tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt, -1);
+ if (!tp->srtt_us)
+ tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt_us, -1L);
}
static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight)
@@ -3023,26 +3024,27 @@ static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb)
* arrived at the other end.
*/
static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
- u32 prior_snd_una, s32 sack_rtt)
+ u32 prior_snd_una, long sack_rtt_us)
{
- struct tcp_sock *tp = tcp_sk(sk);
const struct inet_connection_sock *icsk = inet_csk(sk);
- struct sk_buff *skb;
- u32 now = tcp_time_stamp;
+ struct skb_mstamp first_ackt, last_ackt, now;
+ struct tcp_sock *tp = tcp_sk(sk);
+ u32 prior_sacked = tp->sacked_out;
+ u32 reord = tp->packets_out;
bool fully_acked = true;
- int flag = 0;
+ long ca_seq_rtt_us = -1L;
+ long seq_rtt_us = -1L;
+ struct sk_buff *skb;
u32 pkts_acked = 0;
- u32 reord = tp->packets_out;
- u32 prior_sacked = tp->sacked_out;
- s32 seq_rtt = -1;
- s32 ca_seq_rtt = -1;
- ktime_t last_ackt = net_invalid_timestamp();
bool rtt_update;
+ int flag = 0;
+
+ first_ackt.v64 = 0;
while ((skb = tcp_write_queue_head(sk)) && skb != tcp_send_head(sk)) {
struct tcp_skb_cb *scb = TCP_SKB_CB(skb);
- u32 acked_pcount;
u8 sacked = scb->sacked;
+ u32 acked_pcount;
/* Determine how many packets and what bytes were acked, tso and else */
if (after(scb->end_seq, tp->snd_una)) {
@@ -3064,11 +3066,11 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
tp->retrans_out -= acked_pcount;
flag |= FLAG_RETRANS_DATA_ACKED;
} else {
- ca_seq_rtt = now - scb->when;
- last_ackt = skb->tstamp;
- if (seq_rtt < 0) {
- seq_rtt = ca_seq_rtt;
- }
+ last_ackt = skb->skb_mstamp;
+ WARN_ON_ONCE(last_ackt.v64 == 0);
+ if (!first_ackt.v64)
+ first_ackt = last_ackt;
+
if (!(sacked & TCPCB_SACKED_ACKED))
reord = min(pkts_acked, reord);
if (!after(scb->end_seq, tp->high_seq))
@@ -3114,7 +3116,13 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
if (skb && (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED))
flag |= FLAG_SACK_RENEGING;
- rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt, sack_rtt);
+ skb_mstamp_get(&now);
+ if (first_ackt.v64) {
+ seq_rtt_us = skb_mstamp_us_delta(&now, &first_ackt);
+ ca_seq_rtt_us = skb_mstamp_us_delta(&now, &last_ackt);
+ }
+
+ rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt_us, sack_rtt_us);
if (flag & FLAG_ACKED) {
const struct tcp_congestion_ops *ca_ops
@@ -3142,25 +3150,11 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
tp->fackets_out -= min(pkts_acked, tp->fackets_out);
- if (ca_ops->pkts_acked) {
- s32 rtt_us = -1;
-
- /* Is the ACK triggering packet unambiguous? */
- if (!(flag & FLAG_RETRANS_DATA_ACKED)) {
- /* High resolution needed and available? */
- if (ca_ops->flags & TCP_CONG_RTT_STAMP &&
- !ktime_equal(last_ackt,
- net_invalid_timestamp()))
- rtt_us = ktime_us_delta(ktime_get_real(),
- last_ackt);
- else if (ca_seq_rtt >= 0)
- rtt_us = jiffies_to_usecs(ca_seq_rtt);
- }
+ if (ca_ops->pkts_acked)
+ ca_ops->pkts_acked(sk, pkts_acked, ca_seq_rtt_us);
- ca_ops->pkts_acked(sk, pkts_acked, rtt_us);
- }
- } else if (skb && rtt_update && sack_rtt >= 0 &&
- sack_rtt > (s32)(now - TCP_SKB_CB(skb)->when)) {
+ } else if (skb && rtt_update && sack_rtt_us >= 0 &&
+ sack_rtt_us > skb_mstamp_us_delta(&now, &skb->skb_mstamp)) {
/* Do not re-arm RTO if the sack RTT is measured from data sent
* after when the head was last (re)transmitted. Otherwise the
* timeout may continue to extend in loss recovery.
@@ -3370,12 +3364,12 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
u32 ack_seq = TCP_SKB_CB(skb)->seq;
u32 ack = TCP_SKB_CB(skb)->ack_seq;
bool is_dupack = false;
- u32 prior_in_flight, prior_cwnd = tp->snd_cwnd, prior_rtt = tp->srtt;
+ u32 prior_in_flight;
u32 prior_fackets;
int prior_packets = tp->packets_out;
const int prior_unsacked = tp->packets_out - tp->sacked_out;
int acked = 0; /* Number of packets newly acked */
- s32 sack_rtt = -1;
+ long sack_rtt_us = -1L;
/* If the ack is older than previous acks
* then we can probably ignore it.
@@ -3433,7 +3427,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
if (TCP_SKB_CB(skb)->sacked)
flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una,
- &sack_rtt);
+ &sack_rtt_us);
if (TCP_ECN_rcv_ecn_echo(tp, tcp_hdr(skb)))
flag |= FLAG_ECE;
@@ -3452,7 +3446,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
/* See if we can take anything off of the retransmit queue. */
acked = tp->packets_out;
- flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una, sack_rtt);
+ flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una,
+ sack_rtt_us);
acked -= tp->packets_out;
/* Advance cwnd if state allows */
@@ -3475,8 +3470,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
if (icsk->icsk_pending == ICSK_TIME_RETRANS)
tcp_schedule_loss_probe(sk);
- if (tp->srtt != prior_rtt || tp->snd_cwnd != prior_cwnd)
- tcp_update_pacing_rate(sk);
+ tcp_update_pacing_rate(sk);
return 1;
no_queue:
@@ -3505,7 +3499,7 @@ old_ack:
*/
if (TCP_SKB_CB(skb)->sacked) {
flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una,
- &sack_rtt);
+ &sack_rtt_us);
tcp_fastretrans_alert(sk, acked, prior_unsacked,
is_dupack, flag);
}
@@ -5401,9 +5395,12 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
break;
}
tcp_rearm_rto(sk);
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENACTIVEFAIL);
return true;
}
tp->syn_data_acked = tp->syn_data;
+ if (tp->syn_data_acked)
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENACTIVE);
return false;
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 1e4eac779f51..6379894ec210 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -435,7 +435,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
break;
icsk->icsk_backoff--;
- inet_csk(sk)->icsk_rto = (tp->srtt ? __tcp_set_rto(tp) :
+ inet_csk(sk)->icsk_rto = (tp->srtt_us ? __tcp_set_rto(tp) :
TCP_TIMEOUT_INIT) << icsk->icsk_backoff;
tcp_bound_rto(sk);
@@ -854,8 +854,10 @@ static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req)
{
int res = tcp_v4_send_synack(sk, NULL, req, 0);
- if (!res)
+ if (!res) {
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
+ }
return res;
}
@@ -878,8 +880,6 @@ bool tcp_syn_flood_action(struct sock *sk,
bool want_cookie = false;
struct listen_sock *lopt;
-
-
#ifdef CONFIG_SYN_COOKIES
if (sysctl_tcp_syncookies) {
msg = "Sending cookies";
diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c
index 991d62a2f9bb..c9aecae31327 100644
--- a/net/ipv4/tcp_lp.c
+++ b/net/ipv4/tcp_lp.c
@@ -315,11 +315,9 @@ static void tcp_lp_pkts_acked(struct sock *sk, u32 num_acked, s32 rtt_us)
}
static struct tcp_congestion_ops tcp_lp __read_mostly = {
- .flags = TCP_CONG_RTT_STAMP,
.init = tcp_lp_init,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_lp_cong_avoid,
- .min_cwnd = tcp_reno_min_cwnd,
.pkts_acked = tcp_lp_pkts_acked,
.owner = THIS_MODULE,
diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c
index f7e522c558ba..d4f015ad6c84 100644
--- a/net/ipv4/tcp_memcontrol.c
+++ b/net/ipv4/tcp_memcontrol.c
@@ -103,7 +103,7 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val)
}
static int tcp_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft,
- const char *buffer)
+ char *buffer)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
unsigned long long val;
@@ -219,7 +219,7 @@ static struct cftype tcp_files[] = {
static int __init tcp_memcontrol_init(void)
{
- WARN_ON(cgroup_add_cftypes(&mem_cgroup_subsys, tcp_files));
+ WARN_ON(cgroup_add_cftypes(&memory_cgrp_subsys, tcp_files));
return 0;
}
__initcall(tcp_memcontrol_init);
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index d547075d8300..dcaf72f10216 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -33,6 +33,11 @@ struct tcp_fastopen_metrics {
struct tcp_fastopen_cookie cookie;
};
+/* TCP_METRIC_MAX includes 2 extra fields for userspace compatibility
+ * Kernel only stores RTT and RTTVAR in usec resolution
+ */
+#define TCP_METRIC_MAX_KERNEL (TCP_METRIC_MAX - 2)
+
struct tcp_metrics_block {
struct tcp_metrics_block __rcu *tcpm_next;
struct inetpeer_addr tcpm_saddr;
@@ -41,7 +46,7 @@ struct tcp_metrics_block {
u32 tcpm_ts;
u32 tcpm_ts_stamp;
u32 tcpm_lock;
- u32 tcpm_vals[TCP_METRIC_MAX + 1];
+ u32 tcpm_vals[TCP_METRIC_MAX_KERNEL + 1];
struct tcp_fastopen_metrics tcpm_fastopen;
struct rcu_head rcu_head;
@@ -59,12 +64,6 @@ static u32 tcp_metric_get(struct tcp_metrics_block *tm,
return tm->tcpm_vals[idx];
}
-static u32 tcp_metric_get_jiffies(struct tcp_metrics_block *tm,
- enum tcp_metric_index idx)
-{
- return msecs_to_jiffies(tm->tcpm_vals[idx]);
-}
-
static void tcp_metric_set(struct tcp_metrics_block *tm,
enum tcp_metric_index idx,
u32 val)
@@ -72,13 +71,6 @@ static void tcp_metric_set(struct tcp_metrics_block *tm,
tm->tcpm_vals[idx] = val;
}
-static void tcp_metric_set_msecs(struct tcp_metrics_block *tm,
- enum tcp_metric_index idx,
- u32 val)
-{
- tm->tcpm_vals[idx] = jiffies_to_msecs(val);
-}
-
static bool addr_same(const struct inetpeer_addr *a,
const struct inetpeer_addr *b)
{
@@ -101,9 +93,11 @@ struct tcpm_hash_bucket {
static DEFINE_SPINLOCK(tcp_metrics_lock);
-static void tcpm_suck_dst(struct tcp_metrics_block *tm, struct dst_entry *dst,
+static void tcpm_suck_dst(struct tcp_metrics_block *tm,
+ const struct dst_entry *dst,
bool fastopen_clear)
{
+ u32 msval;
u32 val;
tm->tcpm_stamp = jiffies;
@@ -121,8 +115,11 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm, struct dst_entry *dst,
val |= 1 << TCP_METRIC_REORDERING;
tm->tcpm_lock = val;
- tm->tcpm_vals[TCP_METRIC_RTT] = dst_metric_raw(dst, RTAX_RTT);
- tm->tcpm_vals[TCP_METRIC_RTTVAR] = dst_metric_raw(dst, RTAX_RTTVAR);
+ msval = dst_metric_raw(dst, RTAX_RTT);
+ tm->tcpm_vals[TCP_METRIC_RTT] = msval * USEC_PER_MSEC;
+
+ msval = dst_metric_raw(dst, RTAX_RTTVAR);
+ tm->tcpm_vals[TCP_METRIC_RTTVAR] = msval * USEC_PER_MSEC;
tm->tcpm_vals[TCP_METRIC_SSTHRESH] = dst_metric_raw(dst, RTAX_SSTHRESH);
tm->tcpm_vals[TCP_METRIC_CWND] = dst_metric_raw(dst, RTAX_CWND);
tm->tcpm_vals[TCP_METRIC_REORDERING] = dst_metric_raw(dst, RTAX_REORDERING);
@@ -384,7 +381,7 @@ void tcp_update_metrics(struct sock *sk)
dst_confirm(dst);
rcu_read_lock();
- if (icsk->icsk_backoff || !tp->srtt) {
+ if (icsk->icsk_backoff || !tp->srtt_us) {
/* This session failed to estimate rtt. Why?
* Probably, no packets returned in time. Reset our
* results.
@@ -399,8 +396,8 @@ void tcp_update_metrics(struct sock *sk)
if (!tm)
goto out_unlock;
- rtt = tcp_metric_get_jiffies(tm, TCP_METRIC_RTT);
- m = rtt - tp->srtt;
+ rtt = tcp_metric_get(tm, TCP_METRIC_RTT);
+ m = rtt - tp->srtt_us;
/* If newly calculated rtt larger than stored one, store new
* one. Otherwise, use EWMA. Remember, rtt overestimation is
@@ -408,10 +405,10 @@ void tcp_update_metrics(struct sock *sk)
*/
if (!tcp_metric_locked(tm, TCP_METRIC_RTT)) {
if (m <= 0)
- rtt = tp->srtt;
+ rtt = tp->srtt_us;
else
rtt -= (m >> 3);
- tcp_metric_set_msecs(tm, TCP_METRIC_RTT, rtt);
+ tcp_metric_set(tm, TCP_METRIC_RTT, rtt);
}
if (!tcp_metric_locked(tm, TCP_METRIC_RTTVAR)) {
@@ -422,16 +419,16 @@ void tcp_update_metrics(struct sock *sk)
/* Scale deviation to rttvar fixed point */
m >>= 1;
- if (m < tp->mdev)
- m = tp->mdev;
+ if (m < tp->mdev_us)
+ m = tp->mdev_us;
- var = tcp_metric_get_jiffies(tm, TCP_METRIC_RTTVAR);
+ var = tcp_metric_get(tm, TCP_METRIC_RTTVAR);
if (m >= var)
var = m;
else
var -= (var - m) >> 2;
- tcp_metric_set_msecs(tm, TCP_METRIC_RTTVAR, var);
+ tcp_metric_set(tm, TCP_METRIC_RTTVAR, var);
}
if (tcp_in_initial_slowstart(tp)) {
@@ -528,7 +525,7 @@ void tcp_init_metrics(struct sock *sk)
tp->reordering = val;
}
- crtt = tcp_metric_get_jiffies(tm, TCP_METRIC_RTT);
+ crtt = tcp_metric_get(tm, TCP_METRIC_RTT);
rcu_read_unlock();
reset:
/* The initial RTT measurement from the SYN/SYN-ACK is not ideal
@@ -551,18 +548,20 @@ reset:
* to low value, and then abruptly stops to do it and starts to delay
* ACKs, wait for troubles.
*/
- if (crtt > tp->srtt) {
+ if (crtt > tp->srtt_us) {
/* Set RTO like tcp_rtt_estimator(), but from cached RTT. */
- crtt >>= 3;
+ crtt /= 8 * USEC_PER_MSEC;
inet_csk(sk)->icsk_rto = crtt + max(2 * crtt, tcp_rto_min(sk));
- } else if (tp->srtt == 0) {
+ } else if (tp->srtt_us == 0) {
/* RFC6298: 5.7 We've failed to get a valid RTT sample from
* 3WHS. This is most likely due to retransmission,
* including spurious one. Reset the RTO back to 3secs
* from the more aggressive 1sec to avoid more spurious
* retransmission.
*/
- tp->mdev = tp->mdev_max = tp->rttvar = TCP_TIMEOUT_FALLBACK;
+ tp->rttvar_us = jiffies_to_usecs(TCP_TIMEOUT_FALLBACK);
+ tp->mdev_us = tp->mdev_max_us = tp->rttvar_us;
+
inet_csk(sk)->icsk_rto = TCP_TIMEOUT_FALLBACK;
}
/* Cut cwnd down to 1 per RFC5681 if SYN or SYN-ACK has been
@@ -809,10 +808,26 @@ static int tcp_metrics_fill_info(struct sk_buff *msg,
nest = nla_nest_start(msg, TCP_METRICS_ATTR_VALS);
if (!nest)
goto nla_put_failure;
- for (i = 0; i < TCP_METRIC_MAX + 1; i++) {
- if (!tm->tcpm_vals[i])
+ for (i = 0; i < TCP_METRIC_MAX_KERNEL + 1; i++) {
+ u32 val = tm->tcpm_vals[i];
+
+ if (!val)
continue;
- if (nla_put_u32(msg, i + 1, tm->tcpm_vals[i]) < 0)
+ if (i == TCP_METRIC_RTT) {
+ if (nla_put_u32(msg, TCP_METRIC_RTT_US + 1,
+ val) < 0)
+ goto nla_put_failure;
+ n++;
+ val = max(val / 1000, 1U);
+ }
+ if (i == TCP_METRIC_RTTVAR) {
+ if (nla_put_u32(msg, TCP_METRIC_RTTVAR_US + 1,
+ val) < 0)
+ goto nla_put_failure;
+ n++;
+ val = max(val / 1000, 1U);
+ }
+ if (nla_put_u32(msg, i + 1, val) < 0)
goto nla_put_failure;
n++;
}
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 7a436c517e44..ca788ada5bd3 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -398,8 +398,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
tcp_init_wl(newtp, treq->rcv_isn);
- newtp->srtt = 0;
- newtp->mdev = TCP_TIMEOUT_INIT;
+ newtp->srtt_us = 0;
+ newtp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT);
newicsk->icsk_rto = TCP_TIMEOUT_INIT;
newtp->packets_out = 0;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 17a11e65e57f..699fb102e971 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -86,6 +86,9 @@ static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb)
icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
tcp_rearm_rto(sk);
}
+
+ NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPORIGDATASENT,
+ tcp_skb_pcount(skb));
}
/* SND.NXT, if window was not shrunk.
@@ -269,6 +272,7 @@ EXPORT_SYMBOL(tcp_select_initial_window);
static u16 tcp_select_window(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
+ u32 old_win = tp->rcv_wnd;
u32 cur_win = tcp_receive_window(tp);
u32 new_win = __tcp_select_window(sk);
@@ -281,6 +285,9 @@ static u16 tcp_select_window(struct sock *sk)
*
* Relax Will Robinson.
*/
+ if (new_win == 0)
+ NET_INC_STATS(sock_net(sk),
+ LINUX_MIB_TCPWANTZEROWINDOWADV);
new_win = ALIGN(cur_win, 1 << tp->rx_opt.rcv_wscale);
}
tp->rcv_wnd = new_win;
@@ -298,8 +305,14 @@ static u16 tcp_select_window(struct sock *sk)
new_win >>= tp->rx_opt.rcv_wscale;
/* If we advertise zero window, disable fast path. */
- if (new_win == 0)
+ if (new_win == 0) {
tp->pred_flags = 0;
+ if (old_win)
+ NET_INC_STATS(sock_net(sk),
+ LINUX_MIB_TCPTOZEROWINDOWADV);
+ } else if (old_win == 0) {
+ NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPFROMZEROWINDOWADV);
+ }
return new_win;
}
@@ -867,11 +880,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
if (clone_it) {
const struct sk_buff *fclone = skb + 1;
- /* If congestion control is doing timestamping, we must
- * take such a timestamp before we potentially clone/copy.
- */
- if (icsk->icsk_ca_ops->flags & TCP_CONG_RTT_STAMP)
- __net_timestamp(skb);
+ skb_mstamp_get(&skb->skb_mstamp);
if (unlikely(skb->fclone == SKB_FCLONE_ORIG &&
fclone->fclone == SKB_FCLONE_CLONE))
@@ -884,6 +893,8 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
skb = skb_clone(skb, gfp_mask);
if (unlikely(!skb))
return -ENOBUFS;
+ /* Our usage of tstamp should remain private */
+ skb->tstamp.tv64 = 0;
}
inet = inet_sk(sk);
@@ -1426,7 +1437,7 @@ static void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now,
* With Minshall's modification: all sent small packets are ACKed.
*/
static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp,
- unsigned int mss_now, int nonagle)
+ int nonagle)
{
return partial &&
((nonagle & TCP_NAGLE_CORK) ||
@@ -1458,7 +1469,7 @@ static unsigned int tcp_mss_split_point(const struct sock *sk,
* to include this last segment in this skb.
* Otherwise, we'll split the skb at last MSS boundary
*/
- if (tcp_nagle_check(partial != 0, tp, mss_now, nonagle))
+ if (tcp_nagle_check(partial != 0, tp, nonagle))
return needed - partial;
return needed;
@@ -1521,7 +1532,7 @@ static inline bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buf
if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN))
return true;
- if (!tcp_nagle_check(skb->len < cur_mss, tp, cur_mss, nonagle))
+ if (!tcp_nagle_check(skb->len < cur_mss, tp, nonagle))
return true;
return false;
@@ -1975,7 +1986,7 @@ bool tcp_schedule_loss_probe(struct sock *sk)
struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_sock *tp = tcp_sk(sk);
u32 timeout, tlp_time_stamp, rto_time_stamp;
- u32 rtt = tp->srtt >> 3;
+ u32 rtt = usecs_to_jiffies(tp->srtt_us >> 3);
if (WARN_ON(icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS))
return false;
@@ -1997,7 +2008,7 @@ bool tcp_schedule_loss_probe(struct sock *sk)
/* Schedule a loss probe in 2*RTT for SACK capable connections
* in Open state, that are either limited by cwnd or application.
*/
- if (sysctl_tcp_early_retrans < 3 || !tp->srtt || !tp->packets_out ||
+ if (sysctl_tcp_early_retrans < 3 || !tp->srtt_us || !tp->packets_out ||
!tcp_is_sack(tp) || inet_csk(sk)->icsk_ca_state != TCP_CA_Open)
return false;
@@ -2082,7 +2093,6 @@ rearm_timer:
if (likely(!err))
NET_INC_STATS_BH(sock_net(sk),
LINUX_MIB_TCPLOSSPROBES);
- return;
}
/* Push out any pending frames which were held back due to
@@ -2180,7 +2190,8 @@ u32 __tcp_select_window(struct sock *sk)
*/
int mss = icsk->icsk_ack.rcv_mss;
int free_space = tcp_space(sk);
- int full_space = min_t(int, tp->window_clamp, tcp_full_space(sk));
+ int allowed_space = tcp_full_space(sk);
+ int full_space = min_t(int, tp->window_clamp, allowed_space);
int window;
if (mss > full_space)
@@ -2193,7 +2204,19 @@ u32 __tcp_select_window(struct sock *sk)
tp->rcv_ssthresh = min(tp->rcv_ssthresh,
4U * tp->advmss);
- if (free_space < mss)
+ /* free_space might become our new window, make sure we don't
+ * increase it due to wscale.
+ */
+ free_space = round_down(free_space, 1 << tp->rx_opt.rcv_wscale);
+
+ /* if free space is less than mss estimate, or is below 1/16th
+ * of the maximum allowed, try to move to zero-window, else
+ * tcp_clamp_window() will grow rcv buf up to tcp_rmem[2], and
+ * new incoming data is dropped due to memory limits.
+ * With large window, mss test triggers way too late in order
+ * to announce zero window in time before rmem limit kicks in.
+ */
+ if (free_space < (allowed_space >> 4) || free_space < mss)
return 0;
}
@@ -2431,7 +2454,8 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
if (err == 0) {
/* Update global TCP statistics. */
TCP_INC_STATS(sock_net(sk), TCP_MIB_RETRANSSEGS);
-
+ if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN)
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
tp->total_retrans++;
#if FASTRETRANS_DEBUG > 0
@@ -2717,7 +2741,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
int tcp_header_size;
int mss;
- skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC);
+ skb = sock_wmalloc(sk, MAX_TCP_HEADER, 1, GFP_ATOMIC);
if (unlikely(!skb)) {
dst_release(dst);
return NULL;
@@ -2787,7 +2811,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
th->window = htons(min(req->rcv_wnd, 65535U));
tcp_options_write((__be32 *)(th + 1), tp, &opts);
th->doff = (tcp_header_size >> 2);
- TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS, tcp_skb_pcount(skb));
+ TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_OUTSEGS);
#ifdef CONFIG_TCP_MD5SIG
/* Okay, we have all we need - do the md5 hash if needed */
@@ -2959,9 +2983,15 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn)
tcp_connect_queue_skb(sk, data);
fo->copied = data->len;
+ /* syn_data is about to be sent, we need to take current time stamps
+ * for the packets that are in write queue : SYN packet and DATA
+ */
+ skb_mstamp_get(&syn->skb_mstamp);
+ data->skb_mstamp = syn->skb_mstamp;
+
if (tcp_transmit_skb(sk, syn_data, 0, sk->sk_allocation) == 0) {
tp->syn_data = (fo->copied > 0);
- NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPFASTOPENACTIVE);
+ NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPORIGDATASENT);
goto done;
}
syn_data = NULL;
@@ -3049,8 +3079,9 @@ void tcp_send_delayed_ack(struct sock *sk)
* Do not use inet_csk(sk)->icsk_rto here, use results of rtt measurements
* directly.
*/
- if (tp->srtt) {
- int rtt = max(tp->srtt >> 3, TCP_DELACK_MIN);
+ if (tp->srtt_us) {
+ int rtt = max_t(int, usecs_to_jiffies(tp->srtt_us >> 3),
+ TCP_DELACK_MIN);
if (rtt < max_ato)
max_ato = rtt;
diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c
index 1f2d37613c9e..3b66610d4156 100644
--- a/net/ipv4/tcp_probe.c
+++ b/net/ipv4/tcp_probe.c
@@ -154,7 +154,7 @@ static void jtcp_rcv_established(struct sock *sk, struct sk_buff *skb,
p->snd_wnd = tp->snd_wnd;
p->rcv_wnd = tp->rcv_wnd;
p->ssthresh = tcp_current_ssthresh(sk);
- p->srtt = tp->srtt >> 3;
+ p->srtt = tp->srtt_us >> 3;
tcp_probe.head = (tcp_probe.head + 1) & (bufsize - 1);
}
diff --git a/net/ipv4/tcp_scalable.c b/net/ipv4/tcp_scalable.c
index 19ea6c2951f3..0ac50836da4d 100644
--- a/net/ipv4/tcp_scalable.c
+++ b/net/ipv4/tcp_scalable.c
@@ -39,7 +39,6 @@ static u32 tcp_scalable_ssthresh(struct sock *sk)
static struct tcp_congestion_ops tcp_scalable __read_mostly = {
.ssthresh = tcp_scalable_ssthresh,
.cong_avoid = tcp_scalable_cong_avoid,
- .min_cwnd = tcp_reno_min_cwnd,
.owner = THIS_MODULE,
.name = "scalable",
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 64f0354c84c7..286227abed10 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -165,6 +165,9 @@ 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);
+ if (tp->syn_data)
+ NET_INC_STATS_BH(sock_net(sk),
+ LINUX_MIB_TCPFASTOPENACTIVEFAIL);
}
retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries;
syn_set = true;
diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c
index 06cae62bf208..48539fff6357 100644
--- a/net/ipv4/tcp_vegas.c
+++ b/net/ipv4/tcp_vegas.c
@@ -306,11 +306,9 @@ void tcp_vegas_get_info(struct sock *sk, u32 ext, struct sk_buff *skb)
EXPORT_SYMBOL_GPL(tcp_vegas_get_info);
static struct tcp_congestion_ops tcp_vegas __read_mostly = {
- .flags = TCP_CONG_RTT_STAMP,
.init = tcp_vegas_init,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_vegas_cong_avoid,
- .min_cwnd = tcp_reno_min_cwnd,
.pkts_acked = tcp_vegas_pkts_acked,
.set_state = tcp_vegas_state,
.cwnd_event = tcp_vegas_cwnd_event,
diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c
index 326475a94865..1b8e28fcd7e1 100644
--- a/net/ipv4/tcp_veno.c
+++ b/net/ipv4/tcp_veno.c
@@ -203,7 +203,6 @@ static u32 tcp_veno_ssthresh(struct sock *sk)
}
static struct tcp_congestion_ops tcp_veno __read_mostly = {
- .flags = TCP_CONG_RTT_STAMP,
.init = tcp_veno_init,
.ssthresh = tcp_veno_ssthresh,
.cong_avoid = tcp_veno_cong_avoid,
diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c
index 76a1e23259e1..b94a04ae2ed5 100644
--- a/net/ipv4/tcp_westwood.c
+++ b/net/ipv4/tcp_westwood.c
@@ -276,7 +276,6 @@ static struct tcp_congestion_ops tcp_westwood __read_mostly = {
.init = tcp_westwood_init,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_reno_cong_avoid,
- .min_cwnd = tcp_westwood_bw_rttmin,
.cwnd_event = tcp_westwood_event,
.get_info = tcp_westwood_info,
.pkts_acked = tcp_westwood_pkts_acked,
diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c
index 1a8d271f994d..5ede0e727945 100644
--- a/net/ipv4/tcp_yeah.c
+++ b/net/ipv4/tcp_yeah.c
@@ -227,11 +227,9 @@ static u32 tcp_yeah_ssthresh(struct sock *sk) {
}
static struct tcp_congestion_ops tcp_yeah __read_mostly = {
- .flags = TCP_CONG_RTT_STAMP,
.init = tcp_yeah_init,
.ssthresh = tcp_yeah_ssthresh,
.cong_avoid = tcp_yeah_cong_avoid,
- .min_cwnd = tcp_reno_min_cwnd,
.set_state = tcp_vegas_state,
.cwnd_event = tcp_vegas_cwnd_event,
.get_info = tcp_vegas_get_info,
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 77bd16fa9f34..4468e1adc094 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -931,7 +931,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
sock_tx_timestamp(sk, &ipc.tx_flags);
if (msg->msg_controllen) {
- err = ip_cmsg_send(sock_net(sk), msg, &ipc);
+ err = ip_cmsg_send(sock_net(sk), msg, &ipc,
+ sk->sk_family == AF_INET6);
if (err)
return err;
if (ipc.opt)
diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c
index 1f12c8b45864..aac6197b7a71 100644
--- a/net/ipv4/xfrm4_input.c
+++ b/net/ipv4/xfrm4_input.c
@@ -37,15 +37,6 @@ drop:
return NET_RX_DROP;
}
-int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
- int encap_type)
-{
- XFRM_SPI_SKB_CB(skb)->family = AF_INET;
- XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
- return xfrm_input(skb, nexthdr, spi, encap_type);
-}
-EXPORT_SYMBOL(xfrm4_rcv_encap);
-
int xfrm4_transport_finish(struct sk_buff *skb, int async)
{
struct iphdr *iph = ip_hdr(skb);
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
index 31b18152528f..05f2b484954f 100644
--- a/net/ipv4/xfrm4_mode_tunnel.c
+++ b/net/ipv4/xfrm4_mode_tunnel.c
@@ -15,65 +15,6 @@
#include <net/ip.h>
#include <net/xfrm.h>
-/* Informational hook. The decap is still done here. */
-static struct xfrm_tunnel_notifier __rcu *rcv_notify_handlers __read_mostly;
-static DEFINE_MUTEX(xfrm4_mode_tunnel_input_mutex);
-
-int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler)
-{
- struct xfrm_tunnel_notifier __rcu **pprev;
- struct xfrm_tunnel_notifier *t;
- int ret = -EEXIST;
- int priority = handler->priority;
-
- mutex_lock(&xfrm4_mode_tunnel_input_mutex);
-
- for (pprev = &rcv_notify_handlers;
- (t = rcu_dereference_protected(*pprev,
- lockdep_is_held(&xfrm4_mode_tunnel_input_mutex))) != NULL;
- pprev = &t->next) {
- if (t->priority > priority)
- break;
- if (t->priority == priority)
- goto err;
-
- }
-
- handler->next = *pprev;
- rcu_assign_pointer(*pprev, handler);
-
- ret = 0;
-
-err:
- mutex_unlock(&xfrm4_mode_tunnel_input_mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(xfrm4_mode_tunnel_input_register);
-
-int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler)
-{
- struct xfrm_tunnel_notifier __rcu **pprev;
- struct xfrm_tunnel_notifier *t;
- int ret = -ENOENT;
-
- mutex_lock(&xfrm4_mode_tunnel_input_mutex);
- for (pprev = &rcv_notify_handlers;
- (t = rcu_dereference_protected(*pprev,
- lockdep_is_held(&xfrm4_mode_tunnel_input_mutex))) != NULL;
- pprev = &t->next) {
- if (t == handler) {
- *pprev = handler->next;
- ret = 0;
- break;
- }
- }
- mutex_unlock(&xfrm4_mode_tunnel_input_mutex);
- synchronize_net();
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(xfrm4_mode_tunnel_input_deregister);
-
static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
{
struct iphdr *inner_iph = ipip_hdr(skb);
@@ -127,14 +68,8 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
return 0;
}
-#define for_each_input_rcu(head, handler) \
- for (handler = rcu_dereference(head); \
- handler != NULL; \
- handler = rcu_dereference(handler->next))
-
static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
{
- struct xfrm_tunnel_notifier *handler;
int err = -EINVAL;
if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP)
@@ -143,9 +78,6 @@ static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto out;
- for_each_input_rcu(rcv_notify_handlers, handler)
- handler->handler(skb);
-
err = skb_unclone(skb, GFP_ATOMIC);
if (err)
goto out;
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index e1a63930a967..6156f68a1e90 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -325,6 +325,7 @@ void __init xfrm4_init(void)
xfrm4_state_init();
xfrm4_policy_init();
+ xfrm4_protocol_init();
#ifdef CONFIG_SYSCTL
register_pernet_subsys(&xfrm4_net_ops);
#endif
diff --git a/net/ipv4/xfrm4_protocol.c b/net/ipv4/xfrm4_protocol.c
new file mode 100644
index 000000000000..7f7b243e8139
--- /dev/null
+++ b/net/ipv4/xfrm4_protocol.c
@@ -0,0 +1,286 @@
+/* xfrm4_protocol.c - Generic xfrm protocol multiplexer.
+ *
+ * Copyright (C) 2013 secunet Security Networks AG
+ *
+ * Author:
+ * Steffen Klassert <steffen.klassert@secunet.com>
+ *
+ * Based on:
+ * net/ipv4/tunnel4.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/skbuff.h>
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/xfrm.h>
+
+static struct xfrm4_protocol __rcu *esp4_handlers __read_mostly;
+static struct xfrm4_protocol __rcu *ah4_handlers __read_mostly;
+static struct xfrm4_protocol __rcu *ipcomp4_handlers __read_mostly;
+static DEFINE_MUTEX(xfrm4_protocol_mutex);
+
+static inline struct xfrm4_protocol __rcu **proto_handlers(u8 protocol)
+{
+ switch (protocol) {
+ case IPPROTO_ESP:
+ return &esp4_handlers;
+ case IPPROTO_AH:
+ return &ah4_handlers;
+ case IPPROTO_COMP:
+ return &ipcomp4_handlers;
+ }
+
+ return NULL;
+}
+
+#define for_each_protocol_rcu(head, handler) \
+ for (handler = rcu_dereference(head); \
+ handler != NULL; \
+ handler = rcu_dereference(handler->next)) \
+
+int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
+{
+ int ret;
+ struct xfrm4_protocol *handler;
+
+ for_each_protocol_rcu(*proto_handlers(protocol), handler)
+ if ((ret = handler->cb_handler(skb, err)) <= 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(xfrm4_rcv_cb);
+
+int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
+ int encap_type)
+{
+ int ret;
+ struct xfrm4_protocol *handler;
+
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
+ XFRM_SPI_SKB_CB(skb)->family = AF_INET;
+ XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
+
+ for_each_protocol_rcu(*proto_handlers(nexthdr), handler)
+ if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL)
+ return ret;
+
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+
+ kfree_skb(skb);
+ return 0;
+}
+EXPORT_SYMBOL(xfrm4_rcv_encap);
+
+static int xfrm4_esp_rcv(struct sk_buff *skb)
+{
+ int ret;
+ struct xfrm4_protocol *handler;
+
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
+
+ for_each_protocol_rcu(esp4_handlers, handler)
+ if ((ret = handler->handler(skb)) != -EINVAL)
+ return ret;
+
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static void xfrm4_esp_err(struct sk_buff *skb, u32 info)
+{
+ struct xfrm4_protocol *handler;
+
+ for_each_protocol_rcu(esp4_handlers, handler)
+ if (!handler->err_handler(skb, info))
+ break;
+}
+
+static int xfrm4_ah_rcv(struct sk_buff *skb)
+{
+ int ret;
+ struct xfrm4_protocol *handler;
+
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
+
+ for_each_protocol_rcu(ah4_handlers, handler)
+ if ((ret = handler->handler(skb)) != -EINVAL)
+ return ret;;
+
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static void xfrm4_ah_err(struct sk_buff *skb, u32 info)
+{
+ struct xfrm4_protocol *handler;
+
+ for_each_protocol_rcu(ah4_handlers, handler)
+ if (!handler->err_handler(skb, info))
+ break;
+}
+
+static int xfrm4_ipcomp_rcv(struct sk_buff *skb)
+{
+ int ret;
+ struct xfrm4_protocol *handler;
+
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
+
+ for_each_protocol_rcu(ipcomp4_handlers, handler)
+ if ((ret = handler->handler(skb)) != -EINVAL)
+ return ret;
+
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static void xfrm4_ipcomp_err(struct sk_buff *skb, u32 info)
+{
+ struct xfrm4_protocol *handler;
+
+ for_each_protocol_rcu(ipcomp4_handlers, handler)
+ if (!handler->err_handler(skb, info))
+ break;
+}
+
+static const struct net_protocol esp4_protocol = {
+ .handler = xfrm4_esp_rcv,
+ .err_handler = xfrm4_esp_err,
+ .no_policy = 1,
+ .netns_ok = 1,
+};
+
+static const struct net_protocol ah4_protocol = {
+ .handler = xfrm4_ah_rcv,
+ .err_handler = xfrm4_ah_err,
+ .no_policy = 1,
+ .netns_ok = 1,
+};
+
+static const struct net_protocol ipcomp4_protocol = {
+ .handler = xfrm4_ipcomp_rcv,
+ .err_handler = xfrm4_ipcomp_err,
+ .no_policy = 1,
+ .netns_ok = 1,
+};
+
+static struct xfrm_input_afinfo xfrm4_input_afinfo = {
+ .family = AF_INET,
+ .owner = THIS_MODULE,
+ .callback = xfrm4_rcv_cb,
+};
+
+static inline const struct net_protocol *netproto(unsigned char protocol)
+{
+ switch (protocol) {
+ case IPPROTO_ESP:
+ return &esp4_protocol;
+ case IPPROTO_AH:
+ return &ah4_protocol;
+ case IPPROTO_COMP:
+ return &ipcomp4_protocol;
+ }
+
+ return NULL;
+}
+
+int xfrm4_protocol_register(struct xfrm4_protocol *handler,
+ unsigned char protocol)
+{
+ struct xfrm4_protocol __rcu **pprev;
+ struct xfrm4_protocol *t;
+ bool add_netproto = false;
+ int ret = -EEXIST;
+ int priority = handler->priority;
+
+ mutex_lock(&xfrm4_protocol_mutex);
+
+ if (!rcu_dereference_protected(*proto_handlers(protocol),
+ lockdep_is_held(&xfrm4_protocol_mutex)))
+ add_netproto = true;
+
+ for (pprev = proto_handlers(protocol);
+ (t = rcu_dereference_protected(*pprev,
+ lockdep_is_held(&xfrm4_protocol_mutex))) != NULL;
+ pprev = &t->next) {
+ if (t->priority < priority)
+ break;
+ if (t->priority == priority)
+ goto err;
+ }
+
+ handler->next = *pprev;
+ rcu_assign_pointer(*pprev, handler);
+
+ ret = 0;
+
+err:
+ mutex_unlock(&xfrm4_protocol_mutex);
+
+ if (add_netproto) {
+ if (inet_add_protocol(netproto(protocol), protocol)) {
+ pr_err("%s: can't add protocol\n", __func__);
+ ret = -EAGAIN;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(xfrm4_protocol_register);
+
+int xfrm4_protocol_deregister(struct xfrm4_protocol *handler,
+ unsigned char protocol)
+{
+ struct xfrm4_protocol __rcu **pprev;
+ struct xfrm4_protocol *t;
+ int ret = -ENOENT;
+
+ mutex_lock(&xfrm4_protocol_mutex);
+
+ for (pprev = proto_handlers(protocol);
+ (t = rcu_dereference_protected(*pprev,
+ lockdep_is_held(&xfrm4_protocol_mutex))) != NULL;
+ pprev = &t->next) {
+ if (t == handler) {
+ *pprev = handler->next;
+ ret = 0;
+ break;
+ }
+ }
+
+ if (!rcu_dereference_protected(*proto_handlers(protocol),
+ lockdep_is_held(&xfrm4_protocol_mutex))) {
+ if (inet_del_protocol(netproto(protocol), protocol) < 0) {
+ pr_err("%s: can't remove protocol\n", __func__);
+ ret = -EAGAIN;
+ }
+ }
+
+ mutex_unlock(&xfrm4_protocol_mutex);
+
+ synchronize_net();
+
+ return ret;
+}
+EXPORT_SYMBOL(xfrm4_protocol_deregister);
+
+void __init xfrm4_protocol_init(void)
+{
+ xfrm_input_register_afinfo(&xfrm4_input_afinfo);
+}
+EXPORT_SYMBOL(xfrm4_protocol_init);
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 17bb830872db..2fe68364bb20 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -16,7 +16,7 @@ ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \
- xfrm6_output.o
+ xfrm6_output.o xfrm6_protocol.o
ipv6-$(CONFIG_NETFILTER) += netfilter.o
ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
ipv6-$(CONFIG_PROC_FS) += proc.o
diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c
index b30ad3741b46..731e1e1722d9 100644
--- a/net/ipv6/addrlabel.c
+++ b/net/ipv6/addrlabel.c
@@ -6,7 +6,7 @@
*/
/*
* Author:
- * YOSHIFUJI Hideaki @ USAGI/WIDE Project <yoshfuji@linux-ipv6.org>
+ * YOSHIFUJI Hideaki @ USAGI/WIDE Project <yoshfuji@linux-ipv6.org>
*/
#include <linux/kernel.h>
@@ -22,14 +22,13 @@
#if 0
#define ADDRLABEL(x...) printk(x)
#else
-#define ADDRLABEL(x...) do { ; } while(0)
+#define ADDRLABEL(x...) do { ; } while (0)
#endif
/*
* Policy Table
*/
-struct ip6addrlbl_entry
-{
+struct ip6addrlbl_entry {
#ifdef CONFIG_NET_NS
struct net *lbl_net;
#endif
@@ -88,39 +87,39 @@ static const __net_initconst struct ip6addrlbl_init_table
{ /* ::/0 */
.prefix = &in6addr_any,
.label = 1,
- },{ /* fc00::/7 */
- .prefix = &(struct in6_addr){{{ 0xfc }}},
+ }, { /* fc00::/7 */
+ .prefix = &(struct in6_addr){ { { 0xfc } } } ,
.prefixlen = 7,
.label = 5,
- },{ /* fec0::/10 */
- .prefix = &(struct in6_addr){{{ 0xfe, 0xc0 }}},
+ }, { /* fec0::/10 */
+ .prefix = &(struct in6_addr){ { { 0xfe, 0xc0 } } },
.prefixlen = 10,
.label = 11,
- },{ /* 2002::/16 */
- .prefix = &(struct in6_addr){{{ 0x20, 0x02 }}},
+ }, { /* 2002::/16 */
+ .prefix = &(struct in6_addr){ { { 0x20, 0x02 } } },
.prefixlen = 16,
.label = 2,
- },{ /* 3ffe::/16 */
- .prefix = &(struct in6_addr){{{ 0x3f, 0xfe }}},
+ }, { /* 3ffe::/16 */
+ .prefix = &(struct in6_addr){ { { 0x3f, 0xfe } } },
.prefixlen = 16,
.label = 12,
- },{ /* 2001::/32 */
- .prefix = &(struct in6_addr){{{ 0x20, 0x01 }}},
+ }, { /* 2001::/32 */
+ .prefix = &(struct in6_addr){ { { 0x20, 0x01 } } },
.prefixlen = 32,
.label = 6,
- },{ /* 2001:10::/28 */
- .prefix = &(struct in6_addr){{{ 0x20, 0x01, 0x00, 0x10 }}},
+ }, { /* 2001:10::/28 */
+ .prefix = &(struct in6_addr){ { { 0x20, 0x01, 0x00, 0x10 } } },
.prefixlen = 28,
.label = 7,
- },{ /* ::ffff:0:0 */
- .prefix = &(struct in6_addr){{{ [10] = 0xff, [11] = 0xff }}},
+ }, { /* ::ffff:0:0 */
+ .prefix = &(struct in6_addr){ { { [10] = 0xff, [11] = 0xff } } },
.prefixlen = 96,
.label = 4,
- },{ /* ::/96 */
+ }, { /* ::/96 */
.prefix = &in6addr_any,
.prefixlen = 96,
.label = 3,
- },{ /* ::1/128 */
+ }, { /* ::1/128 */
.prefix = &in6addr_loopback,
.prefixlen = 128,
.label = 0,
@@ -441,7 +440,7 @@ static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh)
if (label == IPV6_ADDR_LABEL_DEFAULT)
return -EINVAL;
- switch(nlh->nlmsg_type) {
+ switch (nlh->nlmsg_type) {
case RTM_NEWADDRLABEL:
if (ifal->ifal_index &&
!__dev_get_by_index(net, ifal->ifal_index))
@@ -505,12 +504,13 @@ static int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb)
hlist_for_each_entry_rcu(p, &ip6addrlbl_table.head, list) {
if (idx >= s_idx &&
net_eq(ip6addrlbl_net(p), net)) {
- if ((err = ip6addrlbl_fill(skb, p,
- ip6addrlbl_table.seq,
- NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq,
- RTM_NEWADDRLABEL,
- NLM_F_MULTI)) <= 0)
+ err = ip6addrlbl_fill(skb, p,
+ ip6addrlbl_table.seq,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ RTM_NEWADDRLABEL,
+ NLM_F_MULTI);
+ if (err <= 0)
break;
}
idx++;
@@ -527,7 +527,7 @@ static inline int ip6addrlbl_msgsize(void)
+ nla_total_size(4); /* IFAL_LABEL */
}
-static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr* nlh)
+static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh)
{
struct net *net = sock_net(in_skb->sk);
struct ifaddrlblmsg *ifal;
@@ -568,7 +568,8 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr* nlh)
goto out;
}
- if (!(skb = nlmsg_new(ip6addrlbl_msgsize(), GFP_KERNEL))) {
+ skb = nlmsg_new(ip6addrlbl_msgsize(), GFP_KERNEL);
+ if (!skb) {
ip6addrlbl_put(p);
return -ENOBUFS;
}
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index 81e496a2e008..72a4930bdc0a 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -346,6 +346,10 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
struct ip_auth_hdr *ah;
struct ah_data *ahp;
struct tmp_ext *iph_ext;
+ int seqhi_len = 0;
+ __be32 *seqhi;
+ int sglists = 0;
+ struct scatterlist *seqhisg;
ahp = x->data;
ahash = ahp->ahash;
@@ -359,15 +363,22 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
if (extlen)
extlen += sizeof(*iph_ext);
+ if (x->props.flags & XFRM_STATE_ESN) {
+ sglists = 1;
+ seqhi_len = sizeof(*seqhi);
+ }
err = -ENOMEM;
- iph_base = ah_alloc_tmp(ahash, nfrags, IPV6HDR_BASELEN + extlen);
+ iph_base = ah_alloc_tmp(ahash, nfrags + sglists, IPV6HDR_BASELEN +
+ extlen + seqhi_len);
if (!iph_base)
goto out;
iph_ext = ah_tmp_ext(iph_base);
- icv = ah_tmp_icv(ahash, iph_ext, extlen);
+ seqhi = (__be32 *)((char *)iph_ext + extlen);
+ icv = ah_tmp_icv(ahash, seqhi, seqhi_len);
req = ah_tmp_req(ahash, icv);
sg = ah_req_sg(ahash, req);
+ seqhisg = sg + nfrags;
ah = ip_auth_hdr(skb);
memset(ah->auth_data, 0, ahp->icv_trunc_len);
@@ -411,10 +422,15 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
ah->spi = x->id.spi;
ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
- sg_init_table(sg, nfrags);
- skb_to_sgvec(skb, sg, 0, skb->len);
+ sg_init_table(sg, nfrags + sglists);
+ skb_to_sgvec_nomark(skb, sg, 0, skb->len);
- ahash_request_set_crypt(req, sg, icv, skb->len);
+ if (x->props.flags & XFRM_STATE_ESN) {
+ /* Attach seqhi sg right after packet payload */
+ *seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
+ sg_set_buf(seqhisg, seqhi, seqhi_len);
+ }
+ ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len);
ahash_request_set_callback(req, 0, ah6_output_done, skb);
AH_SKB_CB(skb)->tmp = iph_base;
@@ -514,6 +530,10 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
int nexthdr;
int nfrags;
int err = -ENOMEM;
+ int seqhi_len = 0;
+ __be32 *seqhi;
+ int sglists = 0;
+ struct scatterlist *seqhisg;
if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr)))
goto out;
@@ -550,14 +570,22 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
skb_push(skb, hdr_len);
- work_iph = ah_alloc_tmp(ahash, nfrags, hdr_len + ahp->icv_trunc_len);
+ if (x->props.flags & XFRM_STATE_ESN) {
+ sglists = 1;
+ seqhi_len = sizeof(*seqhi);
+ }
+
+ work_iph = ah_alloc_tmp(ahash, nfrags + sglists, hdr_len +
+ ahp->icv_trunc_len + seqhi_len);
if (!work_iph)
goto out;
- auth_data = ah_tmp_auth(work_iph, hdr_len);
- icv = ah_tmp_icv(ahash, auth_data, ahp->icv_trunc_len);
+ auth_data = ah_tmp_auth((u8 *)work_iph, hdr_len);
+ seqhi = (__be32 *)(auth_data + ahp->icv_trunc_len);
+ icv = ah_tmp_icv(ahash, seqhi, seqhi_len);
req = ah_tmp_req(ahash, icv);
sg = ah_req_sg(ahash, req);
+ seqhisg = sg + nfrags;
memcpy(work_iph, ip6h, hdr_len);
memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
@@ -572,10 +600,16 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
ip6h->flow_lbl[2] = 0;
ip6h->hop_limit = 0;
- sg_init_table(sg, nfrags);
- skb_to_sgvec(skb, sg, 0, skb->len);
+ sg_init_table(sg, nfrags + sglists);
+ skb_to_sgvec_nomark(skb, sg, 0, skb->len);
+
+ if (x->props.flags & XFRM_STATE_ESN) {
+ /* Attach seqhi sg right after packet payload */
+ *seqhi = XFRM_SKB_CB(skb)->seq.input.hi;
+ sg_set_buf(seqhisg, seqhi, seqhi_len);
+ }
- ahash_request_set_crypt(req, sg, icv, skb->len);
+ ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len);
ahash_request_set_callback(req, 0, ah6_input_done, skb);
AH_SKB_CB(skb)->tmp = work_iph;
@@ -609,8 +643,8 @@ out:
return err;
}
-static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
- u8 type, u8 code, int offset, __be32 info)
+static int ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info)
{
struct net *net = dev_net(skb->dev);
struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
@@ -619,17 +653,19 @@ static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (type != ICMPV6_PKT_TOOBIG &&
type != NDISC_REDIRECT)
- return;
+ return 0;
x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6);
if (!x)
- return;
+ return 0;
if (type == NDISC_REDIRECT)
ip6_redirect(skb, net, skb->dev->ifindex, 0);
else
ip6_update_pmtu(skb, net, info, 0, 0);
xfrm_state_put(x);
+
+ return 0;
}
static int ah6_init_state(struct xfrm_state *x)
@@ -714,6 +750,11 @@ static void ah6_destroy(struct xfrm_state *x)
kfree(ahp);
}
+static int ah6_rcv_cb(struct sk_buff *skb, int err)
+{
+ return 0;
+}
+
static const struct xfrm_type ah6_type =
{
.description = "AH6",
@@ -727,10 +768,11 @@ static const struct xfrm_type ah6_type =
.hdr_offset = xfrm6_find_1stfragopt,
};
-static const struct inet6_protocol ah6_protocol = {
+static struct xfrm6_protocol ah6_protocol = {
.handler = xfrm6_rcv,
+ .cb_handler = ah6_rcv_cb,
.err_handler = ah6_err,
- .flags = INET6_PROTO_NOPOLICY,
+ .priority = 0,
};
static int __init ah6_init(void)
@@ -740,7 +782,7 @@ static int __init ah6_init(void)
return -EAGAIN;
}
- if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) {
+ if (xfrm6_protocol_register(&ah6_protocol, IPPROTO_AH) < 0) {
pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&ah6_type, AF_INET6);
return -EAGAIN;
@@ -751,7 +793,7 @@ static int __init ah6_init(void)
static void __exit ah6_fini(void)
{
- if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0)
+ if (xfrm6_protocol_deregister(&ah6_protocol, IPPROTO_AH) < 0)
pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&ah6_type, AF_INET6) < 0)
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 6eef8a7e35f2..d15da1377149 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -421,8 +421,8 @@ static u32 esp6_get_mtu(struct xfrm_state *x, int mtu)
net_adj) & ~(blksize - 1)) + net_adj - 2;
}
-static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
- u8 type, u8 code, int offset, __be32 info)
+static int esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info)
{
struct net *net = dev_net(skb->dev);
const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data;
@@ -431,18 +431,20 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (type != ICMPV6_PKT_TOOBIG &&
type != NDISC_REDIRECT)
- return;
+ return 0;
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
esph->spi, IPPROTO_ESP, AF_INET6);
if (!x)
- return;
+ return 0;
if (type == NDISC_REDIRECT)
ip6_redirect(skb, net, skb->dev->ifindex, 0);
else
ip6_update_pmtu(skb, net, info, 0, 0);
xfrm_state_put(x);
+
+ return 0;
}
static void esp6_destroy(struct xfrm_state *x)
@@ -614,6 +616,11 @@ error:
return err;
}
+static int esp6_rcv_cb(struct sk_buff *skb, int err)
+{
+ return 0;
+}
+
static const struct xfrm_type esp6_type =
{
.description = "ESP6",
@@ -628,10 +635,11 @@ static const struct xfrm_type esp6_type =
.hdr_offset = xfrm6_find_1stfragopt,
};
-static const struct inet6_protocol esp6_protocol = {
- .handler = xfrm6_rcv,
+static struct xfrm6_protocol esp6_protocol = {
+ .handler = xfrm6_rcv,
+ .cb_handler = esp6_rcv_cb,
.err_handler = esp6_err,
- .flags = INET6_PROTO_NOPOLICY,
+ .priority = 0,
};
static int __init esp6_init(void)
@@ -640,7 +648,7 @@ static int __init esp6_init(void)
pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
- if (inet6_add_protocol(&esp6_protocol, IPPROTO_ESP) < 0) {
+ if (xfrm6_protocol_register(&esp6_protocol, IPPROTO_ESP) < 0) {
pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&esp6_type, AF_INET6);
return -EAGAIN;
@@ -651,7 +659,7 @@ static int __init esp6_init(void)
static void __exit esp6_fini(void)
{
- if (inet6_del_protocol(&esp6_protocol, IPPROTO_ESP) < 0)
+ if (xfrm6_protocol_deregister(&esp6_protocol, IPPROTO_ESP) < 0)
pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&esp6_type, AF_INET6) < 0)
pr_info("%s: can't remove xfrm type\n", __func__);
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index f2610e157660..7b326529e6a2 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -520,7 +520,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
np->tclass, NULL, &fl6, (struct rt6_info *)dst,
MSG_DONTWAIT, np->dontfrag);
if (err) {
- ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS);
+ ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS);
ip6_flush_pending_frames(sk);
} else {
err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr,
diff --git a/net/ipv6/ip6_checksum.c b/net/ipv6/ip6_checksum.c
index 72d198b8e4d2..ee7a97f510cb 100644
--- a/net/ipv6/ip6_checksum.c
+++ b/net/ipv6/ip6_checksum.c
@@ -79,7 +79,9 @@ int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto)
/* RFC 2460 section 8.1 says that we SHOULD log
this error. Well, it is reasonable.
*/
- LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0\n");
+ LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n",
+ &ipv6_hdr(skb)->saddr, ntohs(uh->source),
+ &ipv6_hdr(skb)->daddr, ntohs(uh->dest));
return 1;
}
if (skb->ip_summed == CHECKSUM_COMPLETE &&
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 075602fc6b6a..34e0ded5c14b 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -9,14 +9,12 @@
* 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.
- */
-
-/*
- * Changes:
- * Yuji SEKIYA @USAGI: Support default route on router node;
- * remove ip6_null_entry from the top of
- * routing table.
- * Ville Nuorvala: Fixed routing subtrees.
+ *
+ * Changes:
+ * Yuji SEKIYA @USAGI: Support default route on router node;
+ * remove ip6_null_entry from the top of
+ * routing table.
+ * Ville Nuorvala: Fixed routing subtrees.
*/
#define pr_fmt(fmt) "IPv6: " fmt
@@ -46,10 +44,9 @@
#define RT6_TRACE(x...) do { ; } while (0)
#endif
-static struct kmem_cache * fib6_node_kmem __read_mostly;
+static struct kmem_cache *fib6_node_kmem __read_mostly;
-enum fib_walk_state_t
-{
+enum fib_walk_state_t {
#ifdef CONFIG_IPV6_SUBTREES
FWS_S,
#endif
@@ -59,8 +56,7 @@ enum fib_walk_state_t
FWS_U
};
-struct fib6_cleaner_t
-{
+struct fib6_cleaner_t {
struct fib6_walker_t w;
struct net *net;
int (*func)(struct rt6_info *, void *arg);
@@ -138,7 +134,7 @@ static __inline__ __be32 addr_bit_set(const void *token, int fn_bit)
const __be32 *addr = token;
/*
* Here,
- * 1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)
+ * 1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)
* is optimized version of
* htonl(1 << ((~fn_bit)&0x1F))
* See include/asm-generic/bitops/le.h.
@@ -147,7 +143,7 @@ static __inline__ __be32 addr_bit_set(const void *token, int fn_bit)
addr[fn_bit >> 5];
}
-static __inline__ struct fib6_node * node_alloc(void)
+static __inline__ struct fib6_node *node_alloc(void)
{
struct fib6_node *fn;
@@ -156,7 +152,7 @@ static __inline__ struct fib6_node * node_alloc(void)
return fn;
}
-static __inline__ void node_free(struct fib6_node * fn)
+static __inline__ void node_free(struct fib6_node *fn)
{
kmem_cache_free(fib6_node_kmem, fn);
}
@@ -292,7 +288,7 @@ static int fib6_dump_node(struct fib6_walker_t *w)
static void fib6_dump_end(struct netlink_callback *cb)
{
- struct fib6_walker_t *w = (void*)cb->args[2];
+ struct fib6_walker_t *w = (void *)cb->args[2];
if (w) {
if (cb->args[4]) {
@@ -302,7 +298,7 @@ static void fib6_dump_end(struct netlink_callback *cb)
cb->args[2] = 0;
kfree(w);
}
- cb->done = (void*)cb->args[3];
+ cb->done = (void *)cb->args[3];
cb->args[1] = 3;
}
@@ -485,7 +481,7 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root,
fn->fn_sernum = sernum;
dir = addr_bit_set(addr, fn->fn_bit);
pn = fn;
- fn = dir ? fn->right: fn->left;
+ fn = dir ? fn->right : fn->left;
} while (fn);
if (!allow_create) {
@@ -638,12 +634,41 @@ static inline bool rt6_qualify_for_ecmp(struct rt6_info *rt)
RTF_GATEWAY;
}
+static int fib6_commit_metrics(struct dst_entry *dst,
+ struct nlattr *mx, int mx_len)
+{
+ struct nlattr *nla;
+ int remaining;
+ u32 *mp;
+
+ if (dst->flags & DST_HOST) {
+ mp = dst_metrics_write_ptr(dst);
+ } else {
+ mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
+ if (!mp)
+ return -ENOMEM;
+ dst_init_metrics(dst, mp, 0);
+ }
+
+ nla_for_each_attr(nla, mx, mx_len, remaining) {
+ int type = nla_type(nla);
+
+ if (type) {
+ if (type > RTAX_MAX)
+ return -EINVAL;
+
+ mp[type - 1] = nla_get_u32(nla);
+ }
+ }
+ return 0;
+}
+
/*
* Insert routing information in a node.
*/
static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
- struct nl_info *info)
+ struct nl_info *info, struct nlattr *mx, int mx_len)
{
struct rt6_info *iter = NULL;
struct rt6_info **ins;
@@ -653,6 +678,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
(info->nlh->nlmsg_flags & NLM_F_CREATE));
int found = 0;
bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
+ int err;
ins = &fn->leaf;
@@ -751,6 +777,11 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
pr_warn("NLM_F_CREATE should be set when creating new route\n");
add:
+ if (mx) {
+ err = fib6_commit_metrics(&rt->dst, mx, mx_len);
+ if (err)
+ return err;
+ }
rt->dst.rt6_next = iter;
*ins = rt;
rt->rt6i_node = fn;
@@ -770,6 +801,11 @@ add:
pr_warn("NLM_F_REPLACE set, but no existing node found!\n");
return -ENOENT;
}
+ if (mx) {
+ err = fib6_commit_metrics(&rt->dst, mx, mx_len);
+ if (err)
+ return err;
+ }
*ins = rt;
rt->rt6i_node = fn;
rt->dst.rt6_next = iter->dst.rt6_next;
@@ -806,7 +842,8 @@ void fib6_force_start_gc(struct net *net)
* with source addr info in sub-trees
*/
-int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
+int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info,
+ struct nlattr *mx, int mx_len)
{
struct fib6_node *fn, *pn = NULL;
int err = -ENOMEM;
@@ -900,7 +937,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
}
#endif
- err = fib6_add_rt2node(fn, rt, info);
+ err = fib6_add_rt2node(fn, rt, info, mx, mx_len);
if (!err) {
fib6_start_gc(info->nl_net, rt);
if (!(rt->rt6i_flags & RTF_CACHE))
@@ -955,8 +992,8 @@ struct lookup_args {
const struct in6_addr *addr; /* search key */
};
-static struct fib6_node * fib6_lookup_1(struct fib6_node *root,
- struct lookup_args *args)
+static struct fib6_node *fib6_lookup_1(struct fib6_node *root,
+ struct lookup_args *args)
{
struct fib6_node *fn;
__be32 dir;
@@ -1018,8 +1055,8 @@ backtrack:
return NULL;
}
-struct fib6_node * fib6_lookup(struct fib6_node *root, const struct in6_addr *daddr,
- const struct in6_addr *saddr)
+struct fib6_node *fib6_lookup(struct fib6_node *root, const struct in6_addr *daddr,
+ const struct in6_addr *saddr)
{
struct fib6_node *fn;
struct lookup_args args[] = {
@@ -1051,9 +1088,9 @@ struct fib6_node * fib6_lookup(struct fib6_node *root, const struct in6_addr *da
*/
-static struct fib6_node * fib6_locate_1(struct fib6_node *root,
- const struct in6_addr *addr,
- int plen, int offset)
+static struct fib6_node *fib6_locate_1(struct fib6_node *root,
+ const struct in6_addr *addr,
+ int plen, int offset)
{
struct fib6_node *fn;
@@ -1081,9 +1118,9 @@ static struct fib6_node * fib6_locate_1(struct fib6_node *root,
return NULL;
}
-struct fib6_node * fib6_locate(struct fib6_node *root,
- const struct in6_addr *daddr, int dst_len,
- const struct in6_addr *saddr, int src_len)
+struct fib6_node *fib6_locate(struct fib6_node *root,
+ const struct in6_addr *daddr, int dst_len,
+ const struct in6_addr *saddr, int src_len)
{
struct fib6_node *fn;
@@ -1151,8 +1188,10 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
children = 0;
child = NULL;
- if (fn->right) child = fn->right, children |= 1;
- if (fn->left) child = fn->left, children |= 2;
+ if (fn->right)
+ child = fn->right, children |= 1;
+ if (fn->left)
+ child = fn->left, children |= 2;
if (children == 3 || FIB6_SUBTREE(fn)
#ifdef CONFIG_IPV6_SUBTREES
@@ -1180,8 +1219,10 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
} else {
WARN_ON(fn->fn_flags & RTN_ROOT);
#endif
- if (pn->right == fn) pn->right = child;
- else if (pn->left == fn) pn->left = child;
+ if (pn->right == fn)
+ pn->right = child;
+ else if (pn->left == fn)
+ pn->left = child;
#if RT6_DEBUG >= 2
else
WARN_ON(1);
@@ -1213,10 +1254,10 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
w->node = child;
if (children&2) {
RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
- w->state = w->state>=FWS_R ? FWS_U : FWS_INIT;
+ w->state = w->state >= FWS_R ? FWS_U : FWS_INIT;
} else {
RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
- w->state = w->state>=FWS_C ? FWS_U : FWS_INIT;
+ w->state = w->state >= FWS_C ? FWS_U : FWS_INIT;
}
}
}
@@ -1314,7 +1355,7 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info)
struct rt6_info **rtp;
#if RT6_DEBUG >= 2
- if (rt->dst.obsolete>0) {
+ if (rt->dst.obsolete > 0) {
WARN_ON(fn != NULL);
return -ENOENT;
}
@@ -1707,7 +1748,7 @@ out_rt6_stats:
kfree(net->ipv6.rt6_stats);
out_timer:
return -ENOMEM;
- }
+}
static void fib6_net_exit(struct net *net)
{
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index dfa41bb4e0dc..0961b5ef866d 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -15,9 +15,7 @@
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/netdevice.h>
-#include <linux/if_arp.h>
#include <linux/in6.h>
-#include <linux/route.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
@@ -28,12 +26,8 @@
#include <net/sock.h>
#include <net/ipv6.h>
-#include <net/ndisc.h>
-#include <net/protocol.h>
-#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/rawv6.h>
-#include <net/icmp.h>
#include <net/transp_v6.h>
#include <asm/uaccess.h>
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index f3ffb43f59c0..c98338b81d30 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -1454,7 +1454,6 @@ static void ip6gre_netlink_parms(struct nlattr *data[],
static int ip6gre_tap_init(struct net_device *dev)
{
struct ip6_tnl *tunnel;
- int i;
tunnel = netdev_priv(dev);
@@ -1464,16 +1463,10 @@ static int ip6gre_tap_init(struct net_device *dev)
ip6gre_tnl_link_config(tunnel, 1);
- dev->tstats = alloc_percpu(struct pcpu_sw_netstats);
+ dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!dev->tstats)
return -ENOMEM;
- for_each_possible_cpu(i) {
- struct pcpu_sw_netstats *ip6gre_tap_stats;
- ip6gre_tap_stats = per_cpu_ptr(dev->tstats, i);
- u64_stats_init(&ip6gre_tap_stats->syncp);
- }
-
return 0;
}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 64d6073731d3..3284d61577c0 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -367,6 +367,9 @@ int ip6_forward(struct sk_buff *skb)
if (net->ipv6.devconf_all->forwarding == 0)
goto error;
+ if (skb->pkt_type != PACKET_HOST)
+ goto drop;
+
if (skb_warn_if_lro(skb))
goto drop;
@@ -376,9 +379,6 @@ int ip6_forward(struct sk_buff *skb)
goto drop;
}
- if (skb->pkt_type != PACKET_HOST)
- goto drop;
-
skb_forward_csum(skb);
/*
@@ -1230,8 +1230,10 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
sizeof(struct frag_hdr) : 0) +
rt->rt6i_nfheader_len;
- maxnonfragsize = (np->pmtudisc >= IPV6_PMTUDISC_DO) ?
- mtu : sizeof(struct ipv6hdr) + IPV6_MAXPLEN;
+ if (ip6_sk_local_df(sk))
+ maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN;
+ else
+ maxnonfragsize = mtu;
/* dontfrag active */
if ((cork->length + length > mtu - headersize) && dontfrag &&
@@ -1538,8 +1540,7 @@ int ip6_push_pending_frames(struct sock *sk)
}
/* Allow local fragmentation. */
- if (np->pmtudisc < IPV6_PMTUDISC_DO)
- skb->local_df = 1;
+ skb->local_df = ip6_sk_local_df(sk);
*final_dst = fl6->daddr;
__skb_pull(skb, skb_network_header_len(skb));
@@ -1566,8 +1567,8 @@ int ip6_push_pending_frames(struct sock *sk)
if (proto == IPPROTO_ICMPV6) {
struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
- ICMP6MSGOUT_INC_STATS_BH(net, idev, icmp6_hdr(skb)->icmp6_type);
- ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS);
+ ICMP6MSGOUT_INC_STATS(net, idev, icmp6_hdr(skb)->icmp6_type);
+ ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
}
err = ip6_local_out(skb);
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 5db8d310f9c0..e1df691d78be 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -108,12 +108,12 @@ static struct net_device_stats *ip6_get_stats(struct net_device *dev)
per_cpu_ptr(dev->tstats, i);
do {
- start = u64_stats_fetch_begin_bh(&tstats->syncp);
+ start = u64_stats_fetch_begin_irq(&tstats->syncp);
tmp.rx_packets = tstats->rx_packets;
tmp.rx_bytes = tstats->rx_bytes;
tmp.tx_packets = tstats->tx_packets;
tmp.tx_bytes = tstats->tx_bytes;
- } while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&tstats->syncp, start));
sum.rx_packets += tmp.rx_packets;
sum.rx_bytes += tmp.rx_bytes;
@@ -1502,19 +1502,12 @@ static inline int
ip6_tnl_dev_init_gen(struct net_device *dev)
{
struct ip6_tnl *t = netdev_priv(dev);
- int i;
t->dev = dev;
t->net = dev_net(dev);
- dev->tstats = alloc_percpu(struct pcpu_sw_netstats);
+ dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!dev->tstats)
return -ENOMEM;
-
- for_each_possible_cpu(i) {
- struct pcpu_sw_netstats *ip6_tnl_stats;
- ip6_tnl_stats = per_cpu_ptr(dev->tstats, i);
- u64_stats_init(&ip6_tnl_stats->syncp);
- }
return 0;
}
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index 2d19272b8cee..b7c0f827140b 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -278,7 +278,6 @@ static void vti6_dev_uninit(struct net_device *dev)
RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL);
else
vti6_tnl_unlink(ip6n, t);
- ip6_tnl_dst_reset(t);
dev_put(dev);
}
@@ -288,11 +287,8 @@ static int vti6_rcv(struct sk_buff *skb)
const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
rcu_read_lock();
-
if ((t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr,
&ipv6h->daddr)) != NULL) {
- struct pcpu_sw_netstats *tstats;
-
if (t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) {
rcu_read_unlock();
goto discard;
@@ -309,27 +305,58 @@ static int vti6_rcv(struct sk_buff *skb)
goto discard;
}
- tstats = this_cpu_ptr(t->dev->tstats);
- u64_stats_update_begin(&tstats->syncp);
- tstats->rx_packets++;
- tstats->rx_bytes += skb->len;
- u64_stats_update_end(&tstats->syncp);
-
- skb->mark = 0;
- secpath_reset(skb);
- skb->dev = t->dev;
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = t;
+ skb->mark = be32_to_cpu(t->parms.i_key);
rcu_read_unlock();
- return 0;
+
+ return xfrm6_rcv(skb);
}
rcu_read_unlock();
- return 1;
-
+ return -EINVAL;
discard:
kfree_skb(skb);
return 0;
}
+static int vti6_rcv_cb(struct sk_buff *skb, int err)
+{
+ unsigned short family;
+ struct net_device *dev;
+ struct pcpu_sw_netstats *tstats;
+ struct xfrm_state *x;
+ struct ip6_tnl *t = XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6;
+
+ if (!t)
+ return 1;
+
+ dev = t->dev;
+
+ if (err) {
+ dev->stats.rx_errors++;
+ dev->stats.rx_dropped++;
+
+ return 0;
+ }
+
+ x = xfrm_input_state(skb);
+ family = x->inner_mode->afinfo->family;
+
+ if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family))
+ return -EPERM;
+
+ skb_scrub_packet(skb, !net_eq(t->net, dev_net(skb->dev)));
+ skb->dev = dev;
+
+ tstats = this_cpu_ptr(dev->tstats);
+ u64_stats_update_begin(&tstats->syncp);
+ tstats->rx_packets++;
+ tstats->rx_bytes += skb->len;
+ u64_stats_update_end(&tstats->syncp);
+
+ return 0;
+}
+
/**
* vti6_addr_conflict - compare packet addresses to tunnel's own
* @t: the outgoing tunnel device
@@ -349,44 +376,56 @@ vti6_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr)
return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
}
+static bool vti6_state_check(const struct xfrm_state *x,
+ const struct in6_addr *dst,
+ const struct in6_addr *src)
+{
+ xfrm_address_t *daddr = (xfrm_address_t *)dst;
+ xfrm_address_t *saddr = (xfrm_address_t *)src;
+
+ /* if there is no transform then this tunnel is not functional.
+ * Or if the xfrm is not mode tunnel.
+ */
+ if (!x || x->props.mode != XFRM_MODE_TUNNEL ||
+ x->props.family != AF_INET6)
+ return false;
+
+ if (ipv6_addr_any(dst))
+ return xfrm_addr_equal(saddr, &x->props.saddr, AF_INET6);
+
+ if (!xfrm_state_addr_check(x, daddr, saddr, AF_INET6))
+ return false;
+
+ return true;
+}
+
/**
* vti6_xmit - send a packet
* @skb: the outgoing socket buffer
* @dev: the outgoing tunnel device
+ * @fl: the flow informations for the xfrm_lookup
**/
-static int vti6_xmit(struct sk_buff *skb, struct net_device *dev)
+static int
+vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
{
- struct net *net = dev_net(dev);
struct ip6_tnl *t = netdev_priv(dev);
struct net_device_stats *stats = &t->dev->stats;
- struct dst_entry *dst = NULL, *ndst = NULL;
- struct flowi6 fl6;
- struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+ struct dst_entry *dst = skb_dst(skb);
struct net_device *tdev;
int err = -1;
- if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) ||
- !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h))
- return err;
-
- dst = ip6_tnl_dst_check(t);
- if (!dst) {
- memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
-
- ndst = ip6_route_output(net, NULL, &fl6);
+ if (!dst)
+ goto tx_err_link_failure;
- if (ndst->error)
- goto tx_err_link_failure;
- ndst = xfrm_lookup(net, ndst, flowi6_to_flowi(&fl6), NULL, 0);
- if (IS_ERR(ndst)) {
- err = PTR_ERR(ndst);
- ndst = NULL;
- goto tx_err_link_failure;
- }
- dst = ndst;
+ dst_hold(dst);
+ dst = xfrm_lookup(t->net, dst, fl, NULL, 0);
+ if (IS_ERR(dst)) {
+ err = PTR_ERR(dst);
+ dst = NULL;
+ goto tx_err_link_failure;
}
- if (!dst->xfrm || dst->xfrm->props.mode != XFRM_MODE_TUNNEL)
+ if (!vti6_state_check(dst->xfrm, &t->parms.raddr, &t->parms.laddr))
goto tx_err_link_failure;
tdev = dst->dev;
@@ -398,14 +437,21 @@ static int vti6_xmit(struct sk_buff *skb, struct net_device *dev)
goto tx_err_dst_release;
}
+ skb_scrub_packet(skb, !net_eq(t->net, dev_net(dev)));
+ skb_dst_set(skb, dst);
+ skb->dev = skb_dst(skb)->dev;
- skb_dst_drop(skb);
- skb_dst_set_noref(skb, dst);
+ err = dst_output(skb);
+ if (net_xmit_eval(err) == 0) {
+ struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
- ip6tunnel_xmit(skb, dev);
- if (ndst) {
- dev->mtu = dst_mtu(ndst);
- ip6_tnl_dst_store(t, ndst);
+ u64_stats_update_begin(&tstats->syncp);
+ tstats->tx_bytes += skb->len;
+ tstats->tx_packets++;
+ u64_stats_update_end(&tstats->syncp);
+ } else {
+ stats->tx_errors++;
+ stats->tx_aborted_errors++;
}
return 0;
@@ -413,7 +459,7 @@ tx_err_link_failure:
stats->tx_carrier_errors++;
dst_link_failure(skb);
tx_err_dst_release:
- dst_release(ndst);
+ dst_release(dst);
return err;
}
@@ -422,16 +468,33 @@ vti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ip6_tnl *t = netdev_priv(dev);
struct net_device_stats *stats = &t->dev->stats;
+ struct ipv6hdr *ipv6h;
+ struct flowi fl;
int ret;
+ memset(&fl, 0, sizeof(fl));
+ skb->mark = be32_to_cpu(t->parms.o_key);
+
switch (skb->protocol) {
case htons(ETH_P_IPV6):
- ret = vti6_xmit(skb, dev);
+ ipv6h = ipv6_hdr(skb);
+
+ if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) ||
+ !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h))
+ goto tx_err;
+
+ xfrm_decode_session(skb, &fl, AF_INET6);
+ memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
+ break;
+ case htons(ETH_P_IP):
+ xfrm_decode_session(skb, &fl, AF_INET);
+ memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
break;
default:
goto tx_err;
}
+ ret = vti6_xmit(skb, dev, &fl);
if (ret < 0)
goto tx_err;
@@ -444,24 +507,66 @@ tx_err:
return NETDEV_TX_OK;
}
+static int vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info)
+{
+ __be32 spi;
+ struct xfrm_state *x;
+ struct ip6_tnl *t;
+ struct ip_esp_hdr *esph;
+ struct ip_auth_hdr *ah;
+ struct ip_comp_hdr *ipch;
+ struct net *net = dev_net(skb->dev);
+ const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data;
+ int protocol = iph->nexthdr;
+
+ t = vti6_tnl_lookup(dev_net(skb->dev), &iph->daddr, &iph->saddr);
+ if (!t)
+ return -1;
+
+ switch (protocol) {
+ case IPPROTO_ESP:
+ esph = (struct ip_esp_hdr *)(skb->data + offset);
+ spi = esph->spi;
+ break;
+ case IPPROTO_AH:
+ ah = (struct ip_auth_hdr *)(skb->data + offset);
+ spi = ah->spi;
+ break;
+ case IPPROTO_COMP:
+ ipch = (struct ip_comp_hdr *)(skb->data + offset);
+ spi = htonl(ntohs(ipch->cpi));
+ break;
+ default:
+ return 0;
+ }
+
+ if (type != ICMPV6_PKT_TOOBIG &&
+ type != NDISC_REDIRECT)
+ return 0;
+
+ x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
+ spi, protocol, AF_INET6);
+ if (!x)
+ return 0;
+
+ if (type == NDISC_REDIRECT)
+ ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ else
+ ip6_update_pmtu(skb, net, info, 0, 0);
+ xfrm_state_put(x);
+
+ return 0;
+}
+
static void vti6_link_config(struct ip6_tnl *t)
{
- struct dst_entry *dst;
struct net_device *dev = t->dev;
struct __ip6_tnl_parm *p = &t->parms;
- struct flowi6 *fl6 = &t->fl.u.ip6;
memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));
- /* Set up flowi template */
- fl6->saddr = p->laddr;
- fl6->daddr = p->raddr;
- fl6->flowi6_oif = p->link;
- fl6->flowi6_mark = be32_to_cpu(p->i_key);
- fl6->flowi6_proto = p->proto;
- fl6->flowlabel = 0;
-
p->flags &= ~(IP6_TNL_F_CAP_XMIT | IP6_TNL_F_CAP_RCV |
IP6_TNL_F_CAP_PER_PACKET);
p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr);
@@ -472,28 +577,6 @@ static void vti6_link_config(struct ip6_tnl *t)
dev->flags &= ~IFF_POINTOPOINT;
dev->iflink = p->link;
-
- if (p->flags & IP6_TNL_F_CAP_XMIT) {
-
- dst = ip6_route_output(dev_net(dev), NULL, fl6);
- if (dst->error)
- return;
-
- dst = xfrm_lookup(dev_net(dev), dst, flowi6_to_flowi(fl6),
- NULL, 0);
- if (IS_ERR(dst))
- return;
-
- if (dst->dev) {
- dev->hard_header_len = dst->dev->hard_header_len;
-
- dev->mtu = dst_mtu(dst);
-
- if (dev->mtu < IPV6_MIN_MTU)
- dev->mtu = IPV6_MIN_MTU;
- }
- dst_release(dst);
- }
}
/**
@@ -720,7 +803,6 @@ static void vti6_dev_setup(struct net_device *dev)
t = netdev_priv(dev);
dev->flags |= IFF_NOARP;
dev->addr_len = sizeof(struct in6_addr);
- dev->features |= NETIF_F_NETNS_LOCAL;
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
}
@@ -731,18 +813,12 @@ static void vti6_dev_setup(struct net_device *dev)
static inline int vti6_dev_init_gen(struct net_device *dev)
{
struct ip6_tnl *t = netdev_priv(dev);
- int i;
t->dev = dev;
t->net = dev_net(dev);
- dev->tstats = alloc_percpu(struct pcpu_sw_netstats);
+ dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!dev->tstats)
return -ENOMEM;
- for_each_possible_cpu(i) {
- struct pcpu_sw_netstats *stats;
- stats = per_cpu_ptr(dev->tstats, i);
- u64_stats_init(&stats->syncp);
- }
return 0;
}
@@ -914,11 +990,6 @@ static struct rtnl_link_ops vti6_link_ops __read_mostly = {
.fill_info = vti6_fill_info,
};
-static struct xfrm_tunnel_notifier vti6_handler __read_mostly = {
- .handler = vti6_rcv,
- .priority = 1,
-};
-
static void __net_exit vti6_destroy_tunnels(struct vti6_net *ip6n)
{
int h;
@@ -990,6 +1061,27 @@ static struct pernet_operations vti6_net_ops = {
.size = sizeof(struct vti6_net),
};
+static struct xfrm6_protocol vti_esp6_protocol __read_mostly = {
+ .handler = vti6_rcv,
+ .cb_handler = vti6_rcv_cb,
+ .err_handler = vti6_err,
+ .priority = 100,
+};
+
+static struct xfrm6_protocol vti_ah6_protocol __read_mostly = {
+ .handler = vti6_rcv,
+ .cb_handler = vti6_rcv_cb,
+ .err_handler = vti6_err,
+ .priority = 100,
+};
+
+static struct xfrm6_protocol vti_ipcomp6_protocol __read_mostly = {
+ .handler = vti6_rcv,
+ .cb_handler = vti6_rcv_cb,
+ .err_handler = vti6_err,
+ .priority = 100,
+};
+
/**
* vti6_tunnel_init - register protocol and reserve needed resources
*
@@ -1003,11 +1095,33 @@ static int __init vti6_tunnel_init(void)
if (err < 0)
goto out_pernet;
- err = xfrm6_mode_tunnel_input_register(&vti6_handler);
+ err = xfrm6_protocol_register(&vti_esp6_protocol, IPPROTO_ESP);
if (err < 0) {
- pr_err("%s: can't register vti6\n", __func__);
+ unregister_pernet_device(&vti6_net_ops);
+ pr_err("%s: can't register vti6 protocol\n", __func__);
+
goto out;
}
+
+ err = xfrm6_protocol_register(&vti_ah6_protocol, IPPROTO_AH);
+ if (err < 0) {
+ xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP);
+ unregister_pernet_device(&vti6_net_ops);
+ pr_err("%s: can't register vti6 protocol\n", __func__);
+
+ goto out;
+ }
+
+ err = xfrm6_protocol_register(&vti_ipcomp6_protocol, IPPROTO_COMP);
+ if (err < 0) {
+ xfrm6_protocol_deregister(&vti_ah6_protocol, IPPROTO_AH);
+ xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP);
+ unregister_pernet_device(&vti6_net_ops);
+ pr_err("%s: can't register vti6 protocol\n", __func__);
+
+ goto out;
+ }
+
err = rtnl_link_register(&vti6_link_ops);
if (err < 0)
goto rtnl_link_failed;
@@ -1015,7 +1129,9 @@ static int __init vti6_tunnel_init(void)
return 0;
rtnl_link_failed:
- xfrm6_mode_tunnel_input_deregister(&vti6_handler);
+ xfrm6_protocol_deregister(&vti_ipcomp6_protocol, IPPROTO_COMP);
+ xfrm6_protocol_deregister(&vti_ah6_protocol, IPPROTO_AH);
+ xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP);
out:
unregister_pernet_device(&vti6_net_ops);
out_pernet:
@@ -1028,8 +1144,12 @@ out_pernet:
static void __exit vti6_tunnel_cleanup(void)
{
rtnl_link_unregister(&vti6_link_ops);
- if (xfrm6_mode_tunnel_input_deregister(&vti6_handler))
- pr_info("%s: can't deregister vti6\n", __func__);
+ if (xfrm6_protocol_deregister(&vti_ipcomp6_protocol, IPPROTO_COMP))
+ pr_info("%s: can't deregister protocol\n", __func__);
+ if (xfrm6_protocol_deregister(&vti_ah6_protocol, IPPROTO_AH))
+ pr_info("%s: can't deregister protocol\n", __func__);
+ if (xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP))
+ pr_info("%s: can't deregister protocol\n", __func__);
unregister_pernet_device(&vti6_net_ops);
}
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index da9becb42e81..d1c793cffcb5 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -53,7 +53,7 @@
#include <linux/icmpv6.h>
#include <linux/mutex.h>
-static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info)
{
struct net *net = dev_net(skb->dev);
@@ -65,19 +65,21 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (type != ICMPV6_PKT_TOOBIG &&
type != NDISC_REDIRECT)
- return;
+ return 0;
spi = htonl(ntohs(ipcomph->cpi));
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
spi, IPPROTO_COMP, AF_INET6);
if (!x)
- return;
+ return 0;
if (type == NDISC_REDIRECT)
ip6_redirect(skb, net, skb->dev->ifindex, 0);
else
ip6_update_pmtu(skb, net, info, 0, 0);
xfrm_state_put(x);
+
+ return 0;
}
static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
@@ -174,6 +176,11 @@ out:
return err;
}
+static int ipcomp6_rcv_cb(struct sk_buff *skb, int err)
+{
+ return 0;
+}
+
static const struct xfrm_type ipcomp6_type =
{
.description = "IPCOMP6",
@@ -186,11 +193,12 @@ static const struct xfrm_type ipcomp6_type =
.hdr_offset = xfrm6_find_1stfragopt,
};
-static const struct inet6_protocol ipcomp6_protocol =
+static struct xfrm6_protocol ipcomp6_protocol =
{
.handler = xfrm6_rcv,
+ .cb_handler = ipcomp6_rcv_cb,
.err_handler = ipcomp6_err,
- .flags = INET6_PROTO_NOPOLICY,
+ .priority = 0,
};
static int __init ipcomp6_init(void)
@@ -199,7 +207,7 @@ static int __init ipcomp6_init(void)
pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
- if (inet6_add_protocol(&ipcomp6_protocol, IPPROTO_COMP) < 0) {
+ if (xfrm6_protocol_register(&ipcomp6_protocol, IPPROTO_COMP) < 0) {
pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&ipcomp6_type, AF_INET6);
return -EAGAIN;
@@ -209,7 +217,7 @@ static int __init ipcomp6_init(void)
static void __exit ipcomp6_fini(void)
{
- if (inet6_del_protocol(&ipcomp6_protocol, IPPROTO_COMP) < 0)
+ if (xfrm6_protocol_deregister(&ipcomp6_protocol, IPPROTO_COMP) < 0)
pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&ipcomp6_type, AF_INET6) < 0)
pr_info("%s: can't remove xfrm type\n", __func__);
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 0a00f449de5e..edb58aff4ae7 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -722,7 +722,7 @@ done:
case IPV6_MTU_DISCOVER:
if (optlen < sizeof(int))
goto e_inval;
- if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_INTERFACE)
+ if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_OMIT)
goto e_inval;
np->pmtudisc = val;
retv = 0;
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index e1e47350784b..08b367c6b9cf 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1620,11 +1620,12 @@ static void mld_sendpack(struct sk_buff *skb)
dst_output);
out:
if (!err) {
- ICMP6MSGOUT_INC_STATS_BH(net, idev, ICMPV6_MLD2_REPORT);
- ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS);
- IP6_UPD_PO_STATS_BH(net, idev, IPSTATS_MIB_OUTMCAST, payload_len);
- } else
- IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_OUTDISCARDS);
+ ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT);
+ ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
+ IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, payload_len);
+ } else {
+ IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
+ }
rcu_read_unlock();
return;
diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c
index 827f795209cf..6313abd53c9d 100644
--- a/net/ipv6/output_core.c
+++ b/net/ipv6/output_core.c
@@ -6,24 +6,24 @@
#include <net/ipv6.h>
#include <net/ip6_fib.h>
#include <net/addrconf.h>
+#include <net/secure_seq.h>
void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
{
static atomic_t ipv6_fragmentation_id;
+ struct in6_addr addr;
int old, new;
#if IS_ENABLED(CONFIG_IPV6)
- if (rt && !(rt->dst.flags & DST_NOPEER)) {
- struct inet_peer *peer;
- struct net *net;
-
- net = dev_net(rt->dst.dev);
- peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
- if (peer) {
- fhdr->identification = htonl(inet_getid(peer, 0));
- inet_putpeer(peer);
- return;
- }
+ struct inet_peer *peer;
+ struct net *net;
+
+ net = dev_net(rt->dst.dev);
+ peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
+ if (peer) {
+ fhdr->identification = htonl(inet_getid(peer, 0));
+ inet_putpeer(peer);
+ return;
}
#endif
do {
@@ -32,7 +32,10 @@ void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
if (!new)
new = 1;
} while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old);
- fhdr->identification = htonl(new);
+
+ addr = rt->rt6i_dst.addr;
+ addr.s6_addr32[0] ^= (__force __be32)new;
+ fhdr->identification = htonl(secure_ipv6_id(addr.s6_addr32));
}
EXPORT_SYMBOL(ipv6_select_ident);
diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
index 587bbdcb22b4..bda74291c3e0 100644
--- a/net/ipv6/ping.c
+++ b/net/ipv6/ping.c
@@ -182,8 +182,8 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
MSG_DONTWAIT, np->dontfrag);
if (err) {
- ICMP6_INC_STATS_BH(sock_net(sk), rt->rt6i_idev,
- ICMP6_MIB_OUTERRORS);
+ ICMP6_INC_STATS(sock_net(sk), rt->rt6i_idev,
+ ICMP6_MIB_OUTERRORS);
ip6_flush_pending_frames(sk);
} else {
err = icmpv6_push_pending_frames(sk, &fl6,
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index fba54a407bb2..5015c50a5ba7 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -149,7 +149,8 @@ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
unsigned long prev, new;
p = peer->metrics;
- if (inet_metrics_new(peer))
+ if (inet_metrics_new(peer) ||
+ (old & DST_METRICS_FORCE_OVERWRITE))
memcpy(p, old_p, sizeof(u32) * RTAX_MAX);
new = (unsigned long) p;
@@ -373,12 +374,6 @@ static bool rt6_check_expired(const struct rt6_info *rt)
return false;
}
-static bool rt6_need_strict(const struct in6_addr *daddr)
-{
- return ipv6_addr_type(daddr) &
- (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
-}
-
/* Multipath route selection:
* Hash based function using packet header and flowlabel.
* Adapted from fib_info_hashfn()
@@ -857,14 +852,15 @@ EXPORT_SYMBOL(rt6_lookup);
be destroyed.
*/
-static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info)
+static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
+ struct nlattr *mx, int mx_len)
{
int err;
struct fib6_table *table;
table = rt->rt6i_table;
write_lock_bh(&table->tb6_lock);
- err = fib6_add(&table->tb6_root, rt, info);
+ err = fib6_add(&table->tb6_root, rt, info, mx, mx_len);
write_unlock_bh(&table->tb6_lock);
return err;
@@ -875,7 +871,7 @@ int ip6_ins_rt(struct rt6_info *rt)
struct nl_info info = {
.nl_net = dev_net(rt->dst.dev),
};
- return __ip6_ins_rt(rt, &info);
+ return __ip6_ins_rt(rt, &info, NULL, 0);
}
static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
@@ -1543,17 +1539,11 @@ int ip6_route_add(struct fib6_config *cfg)
ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
rt->rt6i_dst.plen = cfg->fc_dst_len;
- if (rt->rt6i_dst.plen == 128)
- rt->dst.flags |= DST_HOST;
-
- if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) {
- u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
- if (!metrics) {
- err = -ENOMEM;
- goto out;
- }
- dst_init_metrics(&rt->dst, metrics, 0);
+ if (rt->rt6i_dst.plen == 128) {
+ rt->dst.flags |= DST_HOST;
+ dst_metrics_set_force_overwrite(&rt->dst);
}
+
#ifdef CONFIG_IPV6_SUBTREES
ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len);
rt->rt6i_src.plen = cfg->fc_src_len;
@@ -1672,31 +1662,13 @@ int ip6_route_add(struct fib6_config *cfg)
rt->rt6i_flags = cfg->fc_flags;
install_route:
- if (cfg->fc_mx) {
- struct nlattr *nla;
- int remaining;
-
- nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
- int type = nla_type(nla);
-
- if (type) {
- if (type > RTAX_MAX) {
- err = -EINVAL;
- goto out;
- }
-
- dst_metric_set(&rt->dst, type, nla_get_u32(nla));
- }
- }
- }
-
rt->dst.dev = dev;
rt->rt6i_idev = idev;
rt->rt6i_table = table;
cfg->fc_nlinfo.nl_net = dev_net(dev);
- return __ip6_ins_rt(rt, &cfg->fc_nlinfo);
+ return __ip6_ins_rt(rt, &cfg->fc_nlinfo, cfg->fc_mx, cfg->fc_mx_len);
out:
if (dev)
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index b4d74c86586c..1693c8d885f0 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -1363,7 +1363,6 @@ static void ipip6_tunnel_setup(struct net_device *dev)
static int ipip6_tunnel_init(struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
- int i;
tunnel->dev = dev;
tunnel->net = dev_net(dev);
@@ -1372,16 +1371,10 @@ static int ipip6_tunnel_init(struct net_device *dev)
memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
ipip6_tunnel_bind_dev(dev);
- dev->tstats = alloc_percpu(struct pcpu_sw_netstats);
+ dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!dev->tstats)
return -ENOMEM;
- for_each_possible_cpu(i) {
- struct pcpu_sw_netstats *ipip6_tunnel_stats;
- ipip6_tunnel_stats = per_cpu_ptr(dev->tstats, i);
- u64_stats_init(&ipip6_tunnel_stats->syncp);
- }
-
tunnel->dst_cache = alloc_percpu(struct ip_tunnel_dst);
if (!tunnel->dst_cache) {
free_percpu(dev->tstats);
@@ -1397,7 +1390,6 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
struct iphdr *iph = &tunnel->parms.iph;
struct net *net = dev_net(dev);
struct sit_net *sitn = net_generic(net, sit_net_id);
- int i;
tunnel->dev = dev;
tunnel->net = dev_net(dev);
@@ -1408,16 +1400,10 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
iph->ihl = 5;
iph->ttl = 64;
- dev->tstats = alloc_percpu(struct pcpu_sw_netstats);
+ dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!dev->tstats)
return -ENOMEM;
- for_each_possible_cpu(i) {
- struct pcpu_sw_netstats *ipip6_fb_stats;
- ipip6_fb_stats = per_cpu_ptr(dev->tstats, i);
- u64_stats_init(&ipip6_fb_stats->syncp);
- }
-
tunnel->dst_cache = alloc_percpu(struct ip_tunnel_dst);
if (!tunnel->dst_cache) {
free_percpu(dev->tstats);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 889079b2ea85..5ca56cee2dae 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -39,7 +39,7 @@
#include <linux/ipsec.h>
#include <linux/times.h>
#include <linux/slab.h>
-
+#include <linux/uaccess.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
#include <linux/random.h>
@@ -65,8 +65,6 @@
#include <net/tcp_memcontrol.h>
#include <net/busy_poll.h>
-#include <asm/uaccess.h>
-
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
@@ -501,8 +499,10 @@ static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req)
int res;
res = tcp_v6_send_synack(sk, NULL, &fl6, req, 0);
- if (!res)
+ if (!res) {
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
+ }
return res;
}
@@ -530,8 +530,8 @@ static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk,
return tcp_v6_md5_do_lookup(sk, &inet_rsk(req)->ir_v6_rmt_addr);
}
-static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
- int optlen)
+static int tcp_v6_parse_md5_keys(struct sock *sk, char __user *optval,
+ int optlen)
{
struct tcp_md5sig cmd;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr;
@@ -715,7 +715,7 @@ struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
.send_ack = tcp_v6_reqsk_send_ack,
.destructor = tcp_v6_reqsk_destructor,
.send_reset = tcp_v6_send_reset,
- .syn_ack_timeout = tcp_syn_ack_timeout,
+ .syn_ack_timeout = tcp_syn_ack_timeout,
};
#ifdef CONFIG_TCP_MD5SIG
@@ -726,7 +726,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
#endif
static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
- u32 tsval, u32 tsecr,
+ u32 tsval, u32 tsecr, int oif,
struct tcp_md5sig_key *key, int rst, u8 tclass,
u32 label)
{
@@ -798,8 +798,10 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
__tcp_v6_send_check(buff, &fl6.saddr, &fl6.daddr);
fl6.flowi6_proto = IPPROTO_TCP;
- if (ipv6_addr_type(&fl6.daddr) & IPV6_ADDR_LINKLOCAL)
+ if (rt6_need_strict(&fl6.daddr) || !oif)
fl6.flowi6_oif = inet6_iif(skb);
+ else
+ fl6.flowi6_oif = oif;
fl6.fl6_dport = t1->dest;
fl6.fl6_sport = t1->source;
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
@@ -833,6 +835,7 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
int genhash;
struct sock *sk1 = NULL;
#endif
+ int oif;
if (th->rst)
return;
@@ -876,7 +879,8 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
ack_seq = ntohl(th->seq) + th->syn + th->fin + skb->len -
(th->doff << 2);
- tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, key, 1, 0, 0);
+ oif = sk ? sk->sk_bound_dev_if : 0;
+ tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0, 0);
#ifdef CONFIG_TCP_MD5SIG
release_sk1:
@@ -888,11 +892,11 @@ release_sk1:
}
static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
- u32 win, u32 tsval, u32 tsecr,
+ u32 win, u32 tsval, u32 tsecr, int oif,
struct tcp_md5sig_key *key, u8 tclass,
u32 label)
{
- tcp_v6_send_response(skb, seq, ack, win, tsval, tsecr, key, 0, tclass,
+ tcp_v6_send_response(skb, seq, ack, win, tsval, tsecr, oif, key, 0, tclass,
label);
}
@@ -904,7 +908,7 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
tcp_v6_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
tcp_time_stamp + tcptw->tw_ts_offset,
- tcptw->tw_ts_recent, tcp_twsk_md5_key(tcptw),
+ tcptw->tw_ts_recent, tw->tw_bound_dev_if, tcp_twsk_md5_key(tcptw),
tw->tw_tclass, (tw->tw_flowlabel << 12));
inet_twsk_put(tw);
@@ -914,7 +918,7 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
tcp_v6_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1,
- req->rcv_wnd, tcp_time_stamp, req->ts_recent,
+ req->rcv_wnd, tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if,
tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr),
0, 0);
}
@@ -1259,7 +1263,8 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
#ifdef CONFIG_TCP_MD5SIG
/* Copy over the MD5 key from the original socket */
- if ((key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr)) != NULL) {
+ key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr);
+ if (key != NULL) {
/* We're using one, so create a matching key
* on the newsk structure. If we fail to get
* memory, then we end up not copying the key
@@ -1303,9 +1308,8 @@ static __sum16 tcp_v6_checksum_init(struct sk_buff *skb)
&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr, 0));
- if (skb->len <= 76) {
+ if (skb->len <= 76)
return __skb_checksum_complete(skb);
- }
return 0;
}
@@ -1335,7 +1339,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
return tcp_v4_do_rcv(sk, skb);
#ifdef CONFIG_TCP_MD5SIG
- if (tcp_v6_inbound_md5_hash (sk, skb))
+ if (tcp_v6_inbound_md5_hash(sk, skb))
goto discard;
#endif
@@ -1602,7 +1606,8 @@ do_time_wait:
break;
case TCP_TW_RST:
goto no_tcp_socket;
- case TCP_TW_SUCCESS:;
+ case TCP_TW_SUCCESS:
+ ;
}
goto discard_it;
}
@@ -1647,7 +1652,7 @@ static void tcp_v6_early_demux(struct sk_buff *skb)
static struct timewait_sock_ops tcp6_timewait_sock_ops = {
.twsk_obj_size = sizeof(struct tcp6_timewait_sock),
.twsk_unique = tcp_twsk_unique,
- .twsk_destructor= tcp_twsk_destructor,
+ .twsk_destructor = tcp_twsk_destructor,
};
static const struct inet_connection_sock_af_ops ipv6_specific = {
@@ -1681,7 +1686,6 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
/*
* TCP over IPv4 via INET6 API
*/
-
static const struct inet_connection_sock_af_ops ipv6_mapped = {
.queue_xmit = ip_queue_xmit,
.send_check = tcp_v4_send_check,
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
index cb04f7a16b5e..901ef6f8addc 100644
--- a/net/ipv6/xfrm6_mode_tunnel.c
+++ b/net/ipv6/xfrm6_mode_tunnel.c
@@ -18,65 +18,6 @@
#include <net/ipv6.h>
#include <net/xfrm.h>
-/* Informational hook. The decap is still done here. */
-static struct xfrm_tunnel_notifier __rcu *rcv_notify_handlers __read_mostly;
-static DEFINE_MUTEX(xfrm6_mode_tunnel_input_mutex);
-
-int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler)
-{
- struct xfrm_tunnel_notifier __rcu **pprev;
- struct xfrm_tunnel_notifier *t;
- int ret = -EEXIST;
- int priority = handler->priority;
-
- mutex_lock(&xfrm6_mode_tunnel_input_mutex);
-
- for (pprev = &rcv_notify_handlers;
- (t = rcu_dereference_protected(*pprev,
- lockdep_is_held(&xfrm6_mode_tunnel_input_mutex))) != NULL;
- pprev = &t->next) {
- if (t->priority > priority)
- break;
- if (t->priority == priority)
- goto err;
-
- }
-
- handler->next = *pprev;
- rcu_assign_pointer(*pprev, handler);
-
- ret = 0;
-
-err:
- mutex_unlock(&xfrm6_mode_tunnel_input_mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(xfrm6_mode_tunnel_input_register);
-
-int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler)
-{
- struct xfrm_tunnel_notifier __rcu **pprev;
- struct xfrm_tunnel_notifier *t;
- int ret = -ENOENT;
-
- mutex_lock(&xfrm6_mode_tunnel_input_mutex);
- for (pprev = &rcv_notify_handlers;
- (t = rcu_dereference_protected(*pprev,
- lockdep_is_held(&xfrm6_mode_tunnel_input_mutex))) != NULL;
- pprev = &t->next) {
- if (t == handler) {
- *pprev = handler->next;
- ret = 0;
- break;
- }
- }
- mutex_unlock(&xfrm6_mode_tunnel_input_mutex);
- synchronize_net();
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(xfrm6_mode_tunnel_input_deregister);
-
static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
{
const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
@@ -130,7 +71,6 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
{
- struct xfrm_tunnel_notifier *handler;
int err = -EINVAL;
if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPV6)
@@ -138,9 +78,6 @@ static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
goto out;
- for_each_input_rcu(rcv_notify_handlers, handler)
- handler->handler(skb);
-
err = skb_unclone(skb, GFP_ATOMIC);
if (err)
goto out;
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 5f8e128c512d..2a0bbda2c76a 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -389,11 +389,17 @@ int __init xfrm6_init(void)
if (ret)
goto out_policy;
+ ret = xfrm6_protocol_init();
+ if (ret)
+ goto out_state;
+
#ifdef CONFIG_SYSCTL
register_pernet_subsys(&xfrm6_net_ops);
#endif
out:
return ret;
+out_state:
+ xfrm6_state_fini();
out_policy:
xfrm6_policy_fini();
goto out;
@@ -404,6 +410,7 @@ 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/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c
new file mode 100644
index 000000000000..6ab989c486f7
--- /dev/null
+++ b/net/ipv6/xfrm6_protocol.c
@@ -0,0 +1,270 @@
+/* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6.
+ *
+ * Copyright (C) 2013 secunet Security Networks AG
+ *
+ * Author:
+ * Steffen Klassert <steffen.klassert@secunet.com>
+ *
+ * Based on:
+ * net/ipv4/xfrm4_protocol.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/skbuff.h>
+#include <linux/icmpv6.h>
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/xfrm.h>
+
+static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly;
+static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly;
+static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly;
+static DEFINE_MUTEX(xfrm6_protocol_mutex);
+
+static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol)
+{
+ switch (protocol) {
+ case IPPROTO_ESP:
+ return &esp6_handlers;
+ case IPPROTO_AH:
+ return &ah6_handlers;
+ case IPPROTO_COMP:
+ return &ipcomp6_handlers;
+ }
+
+ return NULL;
+}
+
+#define for_each_protocol_rcu(head, handler) \
+ for (handler = rcu_dereference(head); \
+ handler != NULL; \
+ handler = rcu_dereference(handler->next)) \
+
+int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
+{
+ int ret;
+ struct xfrm6_protocol *handler;
+
+ for_each_protocol_rcu(*proto_handlers(protocol), handler)
+ if ((ret = handler->cb_handler(skb, err)) <= 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(xfrm6_rcv_cb);
+
+static int xfrm6_esp_rcv(struct sk_buff *skb)
+{
+ int ret;
+ struct xfrm6_protocol *handler;
+
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
+
+ for_each_protocol_rcu(esp6_handlers, handler)
+ if ((ret = handler->handler(skb)) != -EINVAL)
+ return ret;
+
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static void xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info)
+{
+ struct xfrm6_protocol *handler;
+
+ for_each_protocol_rcu(esp6_handlers, handler)
+ if (!handler->err_handler(skb, opt, type, code, offset, info))
+ break;
+}
+
+static int xfrm6_ah_rcv(struct sk_buff *skb)
+{
+ int ret;
+ struct xfrm6_protocol *handler;
+
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
+
+ for_each_protocol_rcu(ah6_handlers, handler)
+ if ((ret = handler->handler(skb)) != -EINVAL)
+ return ret;
+
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static void xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info)
+{
+ struct xfrm6_protocol *handler;
+
+ for_each_protocol_rcu(ah6_handlers, handler)
+ if (!handler->err_handler(skb, opt, type, code, offset, info))
+ break;
+}
+
+static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
+{
+ int ret;
+ struct xfrm6_protocol *handler;
+
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
+
+ for_each_protocol_rcu(ipcomp6_handlers, handler)
+ if ((ret = handler->handler(skb)) != -EINVAL)
+ return ret;
+
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static void xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info)
+{
+ struct xfrm6_protocol *handler;
+
+ for_each_protocol_rcu(ipcomp6_handlers, handler)
+ if (!handler->err_handler(skb, opt, type, code, offset, info))
+ break;
+}
+
+static const struct inet6_protocol esp6_protocol = {
+ .handler = xfrm6_esp_rcv,
+ .err_handler = xfrm6_esp_err,
+ .flags = INET6_PROTO_NOPOLICY,
+};
+
+static const struct inet6_protocol ah6_protocol = {
+ .handler = xfrm6_ah_rcv,
+ .err_handler = xfrm6_ah_err,
+ .flags = INET6_PROTO_NOPOLICY,
+};
+
+static const struct inet6_protocol ipcomp6_protocol = {
+ .handler = xfrm6_ipcomp_rcv,
+ .err_handler = xfrm6_ipcomp_err,
+ .flags = INET6_PROTO_NOPOLICY,
+};
+
+static struct xfrm_input_afinfo xfrm6_input_afinfo = {
+ .family = AF_INET6,
+ .owner = THIS_MODULE,
+ .callback = xfrm6_rcv_cb,
+};
+
+static inline const struct inet6_protocol *netproto(unsigned char protocol)
+{
+ switch (protocol) {
+ case IPPROTO_ESP:
+ return &esp6_protocol;
+ case IPPROTO_AH:
+ return &ah6_protocol;
+ case IPPROTO_COMP:
+ return &ipcomp6_protocol;
+ }
+
+ return NULL;
+}
+
+int xfrm6_protocol_register(struct xfrm6_protocol *handler,
+ unsigned char protocol)
+{
+ struct xfrm6_protocol __rcu **pprev;
+ struct xfrm6_protocol *t;
+ bool add_netproto = false;
+
+ int ret = -EEXIST;
+ int priority = handler->priority;
+
+ mutex_lock(&xfrm6_protocol_mutex);
+
+ if (!rcu_dereference_protected(*proto_handlers(protocol),
+ lockdep_is_held(&xfrm6_protocol_mutex)))
+ add_netproto = true;
+
+ for (pprev = proto_handlers(protocol);
+ (t = rcu_dereference_protected(*pprev,
+ lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
+ pprev = &t->next) {
+ if (t->priority < priority)
+ break;
+ if (t->priority == priority)
+ goto err;
+ }
+
+ handler->next = *pprev;
+ rcu_assign_pointer(*pprev, handler);
+
+ ret = 0;
+
+err:
+ mutex_unlock(&xfrm6_protocol_mutex);
+
+ if (add_netproto) {
+ if (inet6_add_protocol(netproto(protocol), protocol)) {
+ pr_err("%s: can't add protocol\n", __func__);
+ ret = -EAGAIN;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(xfrm6_protocol_register);
+
+int xfrm6_protocol_deregister(struct xfrm6_protocol *handler,
+ unsigned char protocol)
+{
+ struct xfrm6_protocol __rcu **pprev;
+ struct xfrm6_protocol *t;
+ int ret = -ENOENT;
+
+ mutex_lock(&xfrm6_protocol_mutex);
+
+ for (pprev = proto_handlers(protocol);
+ (t = rcu_dereference_protected(*pprev,
+ lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
+ pprev = &t->next) {
+ if (t == handler) {
+ *pprev = handler->next;
+ ret = 0;
+ break;
+ }
+ }
+
+ if (!rcu_dereference_protected(*proto_handlers(protocol),
+ lockdep_is_held(&xfrm6_protocol_mutex))) {
+ if (inet6_del_protocol(netproto(protocol), protocol) < 0) {
+ pr_err("%s: can't remove protocol\n", __func__);
+ ret = -EAGAIN;
+ }
+ }
+
+ mutex_unlock(&xfrm6_protocol_mutex);
+
+ synchronize_net();
+
+ return ret;
+}
+EXPORT_SYMBOL(xfrm6_protocol_deregister);
+
+int __init xfrm6_protocol_init(void)
+{
+ return xfrm_input_register_afinfo(&xfrm6_input_afinfo);
+}
+
+void xfrm6_protocol_fini(void)
+{
+ xfrm_input_unregister_afinfo(&xfrm6_input_afinfo);
+}
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
index 00b2a6d1c009..41e4e93cb3aa 100644
--- a/net/ipx/af_ipx.c
+++ b/net/ipx/af_ipx.c
@@ -1368,6 +1368,7 @@ static int ipx_release(struct socket *sock)
goto out;
lock_sock(sk);
+ sk->sk_shutdown = SHUTDOWN_MASK;
if (!sock_flag(sk, SOCK_DEAD))
sk->sk_state_change(sk);
@@ -1791,8 +1792,11 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &rc);
- if (!skb)
+ if (!skb) {
+ if (rc == -EAGAIN && (sk->sk_shutdown & RCV_SHUTDOWN))
+ rc = 0;
goto out;
+ }
ipx = ipx_hdr(skb);
copied = ntohs(ipx->ipx_pktsize) - sizeof(struct ipxhdr);
@@ -1922,6 +1926,26 @@ static int ipx_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long
}
#endif
+static int ipx_shutdown(struct socket *sock, int mode)
+{
+ struct sock *sk = sock->sk;
+
+ if (mode < SHUT_RD || mode > SHUT_RDWR)
+ return -EINVAL;
+ /* This maps:
+ * SHUT_RD (0) -> RCV_SHUTDOWN (1)
+ * SHUT_WR (1) -> SEND_SHUTDOWN (2)
+ * SHUT_RDWR (2) -> SHUTDOWN_MASK (3)
+ */
+ ++mode;
+
+ lock_sock(sk);
+ sk->sk_shutdown |= mode;
+ release_sock(sk);
+ sk->sk_state_change(sk);
+
+ return 0;
+}
/*
* Socket family declarations
@@ -1948,7 +1972,7 @@ static const struct proto_ops ipx_dgram_ops = {
.compat_ioctl = ipx_compat_ioctl,
#endif
.listen = sock_no_listen,
- .shutdown = sock_no_shutdown, /* FIXME: support shutdown */
+ .shutdown = ipx_shutdown,
.setsockopt = ipx_setsockopt,
.getsockopt = ipx_getsockopt,
.sendmsg = ipx_sendmsg,
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index c4b7218058b6..a5e03119107a 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -1382,6 +1382,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (sk->sk_type == SOCK_STREAM) {
if (copied < rlen) {
IUCV_SKB_CB(skb)->offset = offset + copied;
+ skb_queue_head(&sk->sk_receive_queue, skb);
goto done;
}
}
diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c
index cd5b8ec9be04..79a0ce95799f 100644
--- a/net/iucv/iucv.c
+++ b/net/iucv/iucv.c
@@ -621,6 +621,42 @@ static void iucv_disable(void)
put_online_cpus();
}
+static void free_iucv_data(int cpu)
+{
+ kfree(iucv_param_irq[cpu]);
+ iucv_param_irq[cpu] = NULL;
+ kfree(iucv_param[cpu]);
+ iucv_param[cpu] = NULL;
+ kfree(iucv_irq_data[cpu]);
+ iucv_irq_data[cpu] = NULL;
+}
+
+static int alloc_iucv_data(int cpu)
+{
+ /* Note: GFP_DMA used to get memory below 2G */
+ iucv_irq_data[cpu] = kmalloc_node(sizeof(struct iucv_irq_data),
+ GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
+ if (!iucv_irq_data[cpu])
+ goto out_free;
+
+ /* Allocate parameter blocks. */
+ iucv_param[cpu] = kmalloc_node(sizeof(union iucv_param),
+ GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
+ if (!iucv_param[cpu])
+ goto out_free;
+
+ iucv_param_irq[cpu] = kmalloc_node(sizeof(union iucv_param),
+ GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
+ if (!iucv_param_irq[cpu])
+ goto out_free;
+
+ return 0;
+
+out_free:
+ free_iucv_data(cpu);
+ return -ENOMEM;
+}
+
static int iucv_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
@@ -630,38 +666,14 @@ static int iucv_cpu_notify(struct notifier_block *self,
switch (action) {
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
- iucv_irq_data[cpu] = kmalloc_node(sizeof(struct iucv_irq_data),
- GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
- if (!iucv_irq_data[cpu])
- return notifier_from_errno(-ENOMEM);
-
- iucv_param[cpu] = kmalloc_node(sizeof(union iucv_param),
- GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
- if (!iucv_param[cpu]) {
- kfree(iucv_irq_data[cpu]);
- iucv_irq_data[cpu] = NULL;
+ if (alloc_iucv_data(cpu))
return notifier_from_errno(-ENOMEM);
- }
- iucv_param_irq[cpu] = kmalloc_node(sizeof(union iucv_param),
- GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
- if (!iucv_param_irq[cpu]) {
- kfree(iucv_param[cpu]);
- iucv_param[cpu] = NULL;
- kfree(iucv_irq_data[cpu]);
- iucv_irq_data[cpu] = NULL;
- return notifier_from_errno(-ENOMEM);
- }
break;
case CPU_UP_CANCELED:
case CPU_UP_CANCELED_FROZEN:
case CPU_DEAD:
case CPU_DEAD_FROZEN:
- kfree(iucv_param_irq[cpu]);
- iucv_param_irq[cpu] = NULL;
- kfree(iucv_param[cpu]);
- iucv_param[cpu] = NULL;
- kfree(iucv_irq_data[cpu]);
- iucv_irq_data[cpu] = NULL;
+ free_iucv_data(cpu);
break;
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
@@ -2025,33 +2037,20 @@ static int __init iucv_init(void)
goto out_int;
}
- for_each_online_cpu(cpu) {
- /* Note: GFP_DMA used to get memory below 2G */
- iucv_irq_data[cpu] = kmalloc_node(sizeof(struct iucv_irq_data),
- GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
- if (!iucv_irq_data[cpu]) {
- rc = -ENOMEM;
- goto out_free;
- }
+ cpu_notifier_register_begin();
- /* Allocate parameter blocks. */
- iucv_param[cpu] = kmalloc_node(sizeof(union iucv_param),
- GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
- if (!iucv_param[cpu]) {
- rc = -ENOMEM;
- goto out_free;
- }
- iucv_param_irq[cpu] = kmalloc_node(sizeof(union iucv_param),
- GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
- if (!iucv_param_irq[cpu]) {
+ for_each_online_cpu(cpu) {
+ if (alloc_iucv_data(cpu)) {
rc = -ENOMEM;
goto out_free;
}
-
}
- rc = register_hotcpu_notifier(&iucv_cpu_notifier);
+ rc = __register_hotcpu_notifier(&iucv_cpu_notifier);
if (rc)
goto out_free;
+
+ cpu_notifier_register_done();
+
rc = register_reboot_notifier(&iucv_reboot_notifier);
if (rc)
goto out_cpu;
@@ -2069,16 +2068,14 @@ static int __init iucv_init(void)
out_reboot:
unregister_reboot_notifier(&iucv_reboot_notifier);
out_cpu:
- unregister_hotcpu_notifier(&iucv_cpu_notifier);
+ cpu_notifier_register_begin();
+ __unregister_hotcpu_notifier(&iucv_cpu_notifier);
out_free:
- for_each_possible_cpu(cpu) {
- kfree(iucv_param_irq[cpu]);
- iucv_param_irq[cpu] = NULL;
- kfree(iucv_param[cpu]);
- iucv_param[cpu] = NULL;
- kfree(iucv_irq_data[cpu]);
- iucv_irq_data[cpu] = NULL;
- }
+ for_each_possible_cpu(cpu)
+ free_iucv_data(cpu);
+
+ cpu_notifier_register_done();
+
root_device_unregister(iucv_root);
out_int:
unregister_external_interrupt(0x4000, iucv_external_interrupt);
@@ -2105,15 +2102,11 @@ static void __exit iucv_exit(void)
kfree(p);
spin_unlock_irq(&iucv_queue_lock);
unregister_reboot_notifier(&iucv_reboot_notifier);
- unregister_hotcpu_notifier(&iucv_cpu_notifier);
- for_each_possible_cpu(cpu) {
- kfree(iucv_param_irq[cpu]);
- iucv_param_irq[cpu] = NULL;
- kfree(iucv_param[cpu]);
- iucv_param[cpu] = NULL;
- kfree(iucv_irq_data[cpu]);
- iucv_irq_data[cpu] = NULL;
- }
+ cpu_notifier_register_begin();
+ __unregister_hotcpu_notifier(&iucv_cpu_notifier);
+ for_each_possible_cpu(cpu)
+ free_iucv_data(cpu);
+ cpu_notifier_register_done();
root_device_unregister(iucv_root);
bus_unregister(&iucv_bus);
unregister_external_interrupt(0x4000, iucv_external_interrupt);
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 79326978517a..e72589a8400d 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -365,6 +365,7 @@ static const u8 sadb_ext_min_len[] = {
[SADB_X_EXT_NAT_T_OA] = (u8) sizeof(struct sadb_address),
[SADB_X_EXT_SEC_CTX] = (u8) sizeof(struct sadb_x_sec_ctx),
[SADB_X_EXT_KMADDRESS] = (u8) sizeof(struct sadb_x_kmaddress),
+ [SADB_X_EXT_FILTER] = (u8) sizeof(struct sadb_x_filter),
};
/* Verify sadb_address_{len,prefixlen} against sa_family. */
@@ -1799,6 +1800,7 @@ static void pfkey_dump_sa_done(struct pfkey_sock *pfk)
static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
{
u8 proto;
+ struct xfrm_address_filter *filter = NULL;
struct pfkey_sock *pfk = pfkey_sk(sk);
if (pfk->dump.dump != NULL)
@@ -1808,11 +1810,27 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms
if (proto == 0)
return -EINVAL;
+ if (ext_hdrs[SADB_X_EXT_FILTER - 1]) {
+ struct sadb_x_filter *xfilter = ext_hdrs[SADB_X_EXT_FILTER - 1];
+
+ filter = kmalloc(sizeof(*filter), GFP_KERNEL);
+ if (filter == NULL)
+ return -ENOMEM;
+
+ memcpy(&filter->saddr, &xfilter->sadb_x_filter_saddr,
+ sizeof(xfrm_address_t));
+ memcpy(&filter->daddr, &xfilter->sadb_x_filter_daddr,
+ sizeof(xfrm_address_t));
+ filter->family = xfilter->sadb_x_filter_family;
+ filter->splen = xfilter->sadb_x_filter_splen;
+ filter->dplen = xfilter->sadb_x_filter_dplen;
+ }
+
pfk->dump.msg_version = hdr->sadb_msg_version;
pfk->dump.msg_portid = hdr->sadb_msg_pid;
pfk->dump.dump = pfkey_dump_sa;
pfk->dump.done = pfkey_dump_sa_done;
- xfrm_state_walk_init(&pfk->dump.u.state, proto);
+ xfrm_state_walk_init(&pfk->dump.u.state, proto, filter);
return pfkey_do_dump(pfk);
}
@@ -3060,6 +3078,24 @@ static u32 get_acqseq(void)
return res;
}
+static bool pfkey_is_alive(const struct km_event *c)
+{
+ struct netns_pfkey *net_pfkey = net_generic(c->net, pfkey_net_id);
+ struct sock *sk;
+ bool is_alive = false;
+
+ rcu_read_lock();
+ sk_for_each_rcu(sk, &net_pfkey->table) {
+ if (pfkey_sk(sk)->registered) {
+ is_alive = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return is_alive;
+}
+
static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp)
{
struct sk_buff *skb;
@@ -3785,6 +3821,7 @@ static struct xfrm_mgr pfkeyv2_mgr =
.new_mapping = pfkey_send_new_mapping,
.notify_policy = pfkey_send_policy_notify,
.migrate = pfkey_send_migrate,
+ .is_alive = pfkey_is_alive,
};
static int __net_init pfkey_net_init(struct net *net)
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index c83827e7c324..47f7a5490555 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1130,7 +1130,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
/* Queue the packet to IP for output */
skb->local_df = 1;
#if IS_ENABLED(CONFIG_IPV6)
- if (skb->sk->sk_family == PF_INET6 && !tunnel->v4mapped)
+ if (tunnel->sock->sk_family == PF_INET6 && !tunnel->v4mapped)
error = inet6_csk_xmit(skb, NULL);
else
#endif
@@ -1150,23 +1150,6 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
return 0;
}
-/* Automatically called when the skb is freed.
- */
-static void l2tp_sock_wfree(struct sk_buff *skb)
-{
- sock_put(skb->sk);
-}
-
-/* For data skbs that we transmit, we associate with the tunnel socket
- * but don't do accounting.
- */
-static inline void l2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
-{
- sock_hold(sk);
- skb->sk = sk;
- skb->destructor = l2tp_sock_wfree;
-}
-
#if IS_ENABLED(CONFIG_IPV6)
static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb,
int udp_len)
@@ -1220,7 +1203,6 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
return NET_XMIT_DROP;
}
- skb_orphan(skb);
/* Setup L2TP header */
session->build_header(session, __skb_push(skb, hdr_len));
@@ -1286,8 +1268,6 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
break;
}
- l2tp_skb_set_owner_w(skb, sk);
-
l2tp_xmit_core(session, skb, fl, data_len);
out_unlock:
bh_unlock_sock(sk);
@@ -1808,8 +1788,6 @@ void l2tp_session_free(struct l2tp_session *session)
}
kfree(session);
-
- return;
}
EXPORT_SYMBOL_GPL(l2tp_session_free);
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 5990919356a5..d276e2d4a589 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -456,13 +456,11 @@ static void pppol2tp_session_close(struct l2tp_session *session)
BUG_ON(session->magic != L2TP_SESSION_MAGIC);
-
if (sock) {
inet_shutdown(sock, 2);
/* Don't let the session go away before our socket does */
l2tp_session_inc_refcount(session);
}
- return;
}
/* Really kill the session socket. (Called from sock_put() if
@@ -476,7 +474,6 @@ static void pppol2tp_session_destruct(struct sock *sk)
BUG_ON(session->magic != L2TP_SESSION_MAGIC);
l2tp_session_dec_refcount(session);
}
- return;
}
/* Called when the PPPoX socket (session) is closed.
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 13b7683de5a4..ce9633a3cfb0 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -107,7 +107,7 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
mgmt->u.action.u.addba_req.start_seq_num =
cpu_to_le16(start_seq_num << 4);
- ieee80211_tx_skb_tid(sdata, skb, tid);
+ ieee80211_tx_skb(sdata, skb);
}
void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 453e974287d1..aaa59d719592 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -451,11 +451,11 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
- if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
+ if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ)
rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
- if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
+ if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80P80MHZ)
rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
- if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
+ if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ)
rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
}
@@ -970,9 +970,9 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
/* TODO: make hostapd tell us what it wants */
sdata->smps_mode = IEEE80211_SMPS_OFF;
sdata->needed_rx_chains = sdata->local->rx_chains;
- sdata->radar_required = params->radar_required;
mutex_lock(&local->mtx);
+ sdata->radar_required = params->radar_required;
err = ieee80211_vif_use_channel(sdata, &params->chandef,
IEEE80211_CHANCTX_SHARED);
mutex_unlock(&local->mtx);
@@ -1056,6 +1056,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
int err;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ sdata_assert_lock(sdata);
/* don't allow changing the beacon while CSA is in place - offset
* of channel switch counter may change
@@ -1083,6 +1084,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
struct probe_resp *old_probe_resp;
struct cfg80211_chan_def chandef;
+ sdata_assert_lock(sdata);
+
old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
if (!old_beacon)
return -ENOENT;
@@ -1343,6 +1346,15 @@ static int sta_apply_parameters(struct ieee80211_local *local,
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
params->vht_capa, sta);
+ if (params->opmode_notif_used) {
+ /* returned value is only needed for rc update, but the
+ * rc isn't initialized here yet, so ignore it
+ */
+ __ieee80211_vht_handle_opmode(sdata, sta,
+ params->opmode_notif,
+ band, false);
+ }
+
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
u32 changed = 0;
@@ -2630,6 +2642,18 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
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;
@@ -2671,18 +2695,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
/* otherwise actually kick it off here (for error handling) */
- /*
- * 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;
-
ret = drv_remain_on_channel(local, sdata, channel, duration, type);
if (ret) {
kfree(roc);
@@ -2902,11 +2914,11 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
static int ieee80211_start_radar_detection(struct wiphy *wiphy,
struct net_device *dev,
- struct cfg80211_chan_def *chandef)
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
- unsigned long timeout;
int err;
mutex_lock(&local->mtx);
@@ -2925,9 +2937,9 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
if (err)
goto out_unlock;
- timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
ieee80211_queue_delayed_work(&sdata->local->hw,
- &sdata->dfs_cac_timer_work, timeout);
+ &sdata->dfs_cac_timer_work,
+ msecs_to_jiffies(cac_time_ms));
out_unlock:
mutex_unlock(&local->mtx);
@@ -2990,136 +3002,135 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
return new_beacon;
}
-void ieee80211_csa_finalize_work(struct work_struct *work)
+void ieee80211_csa_finish(struct ieee80211_vif *vif)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ ieee80211_queue_work(&sdata->local->hw,
+ &sdata->csa_finalize_work);
+}
+EXPORT_SYMBOL(ieee80211_csa_finish);
+
+static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata =
- container_of(work, struct ieee80211_sub_if_data,
- csa_finalize_work);
struct ieee80211_local *local = sdata->local;
int err, changed = 0;
- sdata_lock(sdata);
- /* AP might have been stopped while waiting for the lock. */
- if (!sdata->vif.csa_active)
- goto unlock;
-
- if (!ieee80211_sdata_running(sdata))
- goto unlock;
+ sdata_assert_lock(sdata);
- sdata->radar_required = sdata->csa_radar_required;
mutex_lock(&local->mtx);
+ sdata->radar_required = sdata->csa_radar_required;
err = ieee80211_vif_change_channel(sdata, &changed);
mutex_unlock(&local->mtx);
if (WARN_ON(err < 0))
- goto unlock;
+ return;
if (!local->use_chanctx) {
local->_oper_chandef = sdata->csa_chandef;
ieee80211_hw_config(local, 0);
}
- ieee80211_bss_info_change_notify(sdata, changed);
-
sdata->vif.csa_active = false;
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
- if (err < 0)
- goto unlock;
-
- changed |= err;
kfree(sdata->u.ap.next_beacon);
sdata->u.ap.next_beacon = NULL;
- ieee80211_bss_info_change_notify(sdata, err);
+ if (err < 0)
+ return;
+ changed |= err;
break;
case NL80211_IFTYPE_ADHOC:
- ieee80211_ibss_finish_csa(sdata);
+ err = ieee80211_ibss_finish_csa(sdata);
+ if (err < 0)
+ return;
+ changed |= err;
break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
err = ieee80211_mesh_finish_csa(sdata);
if (err < 0)
- goto unlock;
+ return;
+ changed |= err;
break;
#endif
default:
WARN_ON(1);
- goto unlock;
+ return;
}
+ ieee80211_bss_info_change_notify(sdata, changed);
+
ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA);
cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
-
-unlock:
- sdata_unlock(sdata);
}
-int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_csa_settings *params)
+void ieee80211_csa_finalize_work(struct work_struct *work)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_chanctx_conf *chanctx_conf;
- struct ieee80211_chanctx *chanctx;
- struct ieee80211_if_mesh __maybe_unused *ifmsh;
- int err, num_chanctx;
-
- lockdep_assert_held(&sdata->wdev.mtx);
-
- if (!list_empty(&local->roc_list) || local->scanning)
- return -EBUSY;
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ csa_finalize_work);
- if (sdata->wdev.cac_started)
- return -EBUSY;
+ sdata_lock(sdata);
+ /* AP might have been stopped while waiting for the lock. */
+ if (!sdata->vif.csa_active)
+ goto unlock;
- if (cfg80211_chandef_identical(&params->chandef,
- &sdata->vif.bss_conf.chandef))
- return -EINVAL;
+ if (!ieee80211_sdata_running(sdata))
+ goto unlock;
- rcu_read_lock();
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf) {
- rcu_read_unlock();
- return -EBUSY;
- }
+ ieee80211_csa_finalize(sdata);
- /* don't handle for multi-VIF cases */
- chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
- if (chanctx->refcount > 1) {
- rcu_read_unlock();
- return -EBUSY;
- }
- num_chanctx = 0;
- list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
- num_chanctx++;
- rcu_read_unlock();
-
- if (num_chanctx > 1)
- return -EBUSY;
+unlock:
+ sdata_unlock(sdata);
+}
- /* don't allow another channel switch if one is already active. */
- if (sdata->vif.csa_active)
- return -EBUSY;
+static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_csa_settings *params,
+ u32 *changed)
+{
+ int err;
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
- sdata->csa_counter_offset_beacon =
- params->counter_offset_beacon;
- sdata->csa_counter_offset_presp = params->counter_offset_presp;
sdata->u.ap.next_beacon =
cfg80211_beacon_dup(&params->beacon_after);
if (!sdata->u.ap.next_beacon)
return -ENOMEM;
+ /*
+ * With a count of 0, we don't have to wait for any
+ * TBTT before switching, so complete the CSA
+ * immediately. In theory, with a count == 1 we
+ * should delay the switch until just before the next
+ * TBTT, but that would complicate things so we switch
+ * immediately too. If we would delay the switch
+ * until the next TBTT, we would have to set the probe
+ * response here.
+ *
+ * TODO: A channel switch with count <= 1 without
+ * sending a CSA action frame is kind of useless,
+ * because the clients won't know we're changing
+ * channels. The action frame must be implemented
+ * either here or in the userspace.
+ */
+ if (params->count <= 1)
+ break;
+
+ sdata->csa_counter_offset_beacon =
+ params->counter_offset_beacon;
+ sdata->csa_counter_offset_presp = params->counter_offset_presp;
err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
if (err < 0) {
kfree(sdata->u.ap.next_beacon);
return err;
}
+ *changed |= err;
+
break;
case NL80211_IFTYPE_ADHOC:
if (!sdata->vif.bss_conf.ibss_joined)
@@ -3147,16 +3158,20 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
params->chandef.chan->band)
return -EINVAL;
- err = ieee80211_ibss_csa_beacon(sdata, params);
- if (err < 0)
- return err;
+ /* see comments in the NL80211_IFTYPE_AP block */
+ if (params->count > 1) {
+ err = ieee80211_ibss_csa_beacon(sdata, params);
+ if (err < 0)
+ return err;
+ *changed |= err;
+ }
+
+ ieee80211_send_action_csa(sdata, params);
+
break;
#ifdef CONFIG_MAC80211_MESH
- case NL80211_IFTYPE_MESH_POINT:
- ifmsh = &sdata->u.mesh;
-
- if (!ifmsh->mesh_id)
- return -EINVAL;
+ case NL80211_IFTYPE_MESH_POINT: {
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
return -EINVAL;
@@ -3166,23 +3181,87 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
params->chandef.chan->band)
return -EINVAL;
- ifmsh->chsw_init = true;
- if (!ifmsh->pre_value)
- ifmsh->pre_value = 1;
- else
- ifmsh->pre_value++;
+ if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) {
+ ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT;
+ if (!ifmsh->pre_value)
+ ifmsh->pre_value = 1;
+ else
+ ifmsh->pre_value++;
+ }
- err = ieee80211_mesh_csa_beacon(sdata, params, true);
- if (err < 0) {
- ifmsh->chsw_init = false;
- return err;
+ /* see comments in the NL80211_IFTYPE_AP block */
+ if (params->count > 1) {
+ err = ieee80211_mesh_csa_beacon(sdata, params);
+ if (err < 0) {
+ ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
+ return err;
+ }
+ *changed |= err;
}
+
+ if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT)
+ ieee80211_send_action_csa(sdata, params);
+
break;
+ }
#endif
default:
return -EOPNOTSUPP;
}
+ return 0;
+}
+
+int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_csa_settings *params)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_chanctx *chanctx;
+ int err, num_chanctx, changed = 0;
+
+ sdata_assert_lock(sdata);
+
+ if (!list_empty(&local->roc_list) || local->scanning)
+ return -EBUSY;
+
+ if (sdata->wdev.cac_started)
+ return -EBUSY;
+
+ if (cfg80211_chandef_identical(&params->chandef,
+ &sdata->vif.bss_conf.chandef))
+ return -EINVAL;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ rcu_read_unlock();
+ return -EBUSY;
+ }
+
+ /* don't handle for multi-VIF cases */
+ chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+ if (chanctx->refcount > 1) {
+ rcu_read_unlock();
+ return -EBUSY;
+ }
+ num_chanctx = 0;
+ list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
+ num_chanctx++;
+ rcu_read_unlock();
+
+ if (num_chanctx > 1)
+ return -EBUSY;
+
+ /* don't allow another channel switch if one is already active. */
+ if (sdata->vif.csa_active)
+ return -EBUSY;
+
+ err = ieee80211_set_csa_beacon(sdata, params, &changed);
+ if (err)
+ return err;
+
sdata->csa_radar_required = params->radar_required;
if (params->block_tx)
@@ -3193,8 +3272,13 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
sdata->csa_chandef = params->chandef;
sdata->vif.csa_active = true;
- ieee80211_bss_info_change_notify(sdata, err);
- drv_channel_switch_beacon(sdata, &params->chandef);
+ if (changed) {
+ ieee80211_bss_info_change_notify(sdata, changed);
+ drv_channel_switch_beacon(sdata, &params->chandef);
+ } else {
+ /* if the beacon didn't change, we can finalize immediately */
+ ieee80211_csa_finalize(sdata);
+ }
return 0;
}
@@ -3573,8 +3657,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
u8 *peer, u8 action_code, u8 dialog_token,
- u16 status_code, const u8 *extra_ies,
- size_t extra_ies_len)
+ u16 status_code, u32 peer_capability,
+ const u8 *extra_ies, size_t extra_ies_len)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
@@ -3865,7 +3949,7 @@ static int ieee80211_set_qos_map(struct wiphy *wiphy,
return 0;
}
-struct cfg80211_ops mac80211_config_ops = {
+const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
.change_virtual_intf = ieee80211_change_iface,
diff --git a/net/mac80211/cfg.h b/net/mac80211/cfg.h
index 7d7879f5b00b..2d51f62dc76c 100644
--- a/net/mac80211/cfg.h
+++ b/net/mac80211/cfg.h
@@ -4,6 +4,6 @@
#ifndef __CFG_H
#define __CFG_H
-extern struct cfg80211_ops mac80211_config_ops;
+extern const struct cfg80211_ops mac80211_config_ops;
#endif /* __CFG_H */
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 0c1ecfdf9a12..bd1fd8ea5105 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -202,6 +202,8 @@ static bool ieee80211_is_radar_required(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
+ lockdep_assert_held(&local->mtx);
+
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (sdata->radar_required) {
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index ebf80f3abd83..40a648938985 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -358,6 +358,18 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
}
IEEE80211_IF_FILE_W(tkip_mic_test);
+static ssize_t ieee80211_if_parse_beacon_loss(
+ struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
+{
+ if (!ieee80211_sdata_running(sdata) || !sdata->vif.bss_conf.assoc)
+ return -ENOTCONN;
+
+ ieee80211_beacon_loss(&sdata->vif);
+
+ return buflen;
+}
+IEEE80211_IF_FILE_W(beacon_loss);
+
static ssize_t ieee80211_if_fmt_uapsd_queues(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
{
@@ -569,6 +581,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(beacon_timeout);
DEBUGFS_ADD_MODE(smps, 0600);
DEBUGFS_ADD_MODE(tkip_mic_test, 0200);
+ DEBUGFS_ADD_MODE(beacon_loss, 0200);
DEBUGFS_ADD_MODE(uapsd_queues, 0600);
DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600);
}
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 80194b557a0c..2ecb4deddb5d 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -195,7 +195,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
static ssize_t sta_agg_status_write(struct file *file, const char __user *userbuf,
size_t count, loff_t *ppos)
{
- char _buf[12], *buf = _buf;
+ char _buf[12] = {}, *buf = _buf;
struct sta_info *sta = file->private_data;
bool start, tx;
unsigned long tid;
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index ef8b385eff04..fc689f5d971e 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -354,16 +354,20 @@ drv_sched_scan_start(struct ieee80211_local *local,
return ret;
}
-static inline void drv_sched_scan_stop(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata)
+static inline int drv_sched_scan_stop(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
{
+ int ret;
+
might_sleep();
check_sdata_in_driver(sdata);
trace_drv_sched_scan_stop(local, sdata);
- local->ops->sched_scan_stop(&local->hw, &sdata->vif);
- trace_drv_return_void(local);
+ ret = local->ops->sched_scan_stop(&local->hw, &sdata->vif);
+ trace_drv_return_int(local, ret);
+
+ return ret;
}
static inline void drv_sw_scan_start(struct ieee80211_local *local)
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 70dd013de836..c150b68436d7 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -375,7 +375,7 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
mgmt->u.action.u.delba.params = cpu_to_le16(params);
mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
- ieee80211_tx_skb_tid(sdata, skb, tid);
+ ieee80211_tx_skb(sdata, skb);
}
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
@@ -482,8 +482,6 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
return;
if (vif->type == NL80211_IFTYPE_STATION) {
- if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
- smps_mode = IEEE80211_SMPS_AUTOMATIC;
if (sdata->u.mgd.driver_smps_mode == smps_mode)
return;
sdata->u.mgd.driver_smps_mode = smps_mode;
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 2796a198728f..06d28787945b 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -220,7 +220,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
- struct ieee80211_supported_band *sband;
struct ieee80211_mgmt *mgmt;
struct cfg80211_bss *bss;
u32 bss_change;
@@ -284,6 +283,11 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
&chandef);
+ if (err < 0) {
+ sdata_info(sdata,
+ "Failed to join IBSS, invalid chandef\n");
+ return;
+ }
if (err > 0) {
if (!ifibss->userspace_handles_dfs) {
sdata_info(sdata,
@@ -294,7 +298,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
}
mutex_lock(&local->mtx);
- ieee80211_vif_release_channel(sdata);
if (ieee80211_vif_use_channel(sdata, &chandef,
ifibss->fixed_channel ?
IEEE80211_CHANCTX_SHARED :
@@ -303,12 +306,11 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&local->mtx);
return;
}
+ sdata->radar_required = radar_required;
mutex_unlock(&local->mtx);
memcpy(ifibss->bssid, bssid, ETH_ALEN);
- sband = local->hw.wiphy->bands[chan->band];
-
presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
capability, tsf, &chandef,
&have_higher_than_11mbit, NULL);
@@ -318,7 +320,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
rcu_assign_pointer(ifibss->presp, presp);
mgmt = (void *)presp->head;
- sdata->radar_required = radar_required;
sdata->vif.bss_conf.enable_beacon = true;
sdata->vif.bss_conf.beacon_int = beacon_int;
sdata->vif.bss_conf.basic_rates = basic_rates;
@@ -386,7 +387,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
presp->head_len, 0, GFP_KERNEL);
cfg80211_put_bss(local->hw.wiphy, bss);
netif_carrier_on(sdata->dev);
- cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
+ cfg80211_ibss_joined(sdata->dev, ifibss->bssid, chan, GFP_KERNEL);
}
static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
@@ -521,12 +522,6 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
if (old_presp)
kfree_rcu(old_presp, rcu_head);
- /* it might not send the beacon for a while. send an action frame
- * immediately to announce the channel switch.
- */
- if (csa_settings)
- ieee80211_send_action_csa(sdata, csa_settings);
-
return BSS_CHANGED_BEACON;
out:
return ret;
@@ -536,7 +531,7 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct cfg80211_bss *cbss;
- int err;
+ int err, changed = 0;
u16 capability;
sdata_assert_lock(sdata);
@@ -568,10 +563,9 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
if (err < 0)
return err;
- if (err)
- ieee80211_bss_info_change_notify(sdata, err);
+ changed |= err;
- return 0;
+ return changed;
}
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata)
@@ -799,6 +793,8 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
int err;
u32 sta_flags;
+ sdata_assert_lock(sdata);
+
sta_flags = IEEE80211_STA_DISABLE_VHT;
switch (ifibss->chandef.width) {
case NL80211_CHAN_WIDTH_5:
@@ -995,7 +991,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems)
{
struct ieee80211_local *local = sdata->local;
- int freq;
struct cfg80211_bss *cbss;
struct ieee80211_bss *bss;
struct sta_info *sta;
@@ -1007,15 +1002,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
bool rates_updated = false;
- if (elems->ds_params)
- freq = ieee80211_channel_to_frequency(elems->ds_params[0],
- band);
- else
- freq = rx_status->freq;
-
- channel = ieee80211_get_channel(local->hw.wiphy, freq);
-
- if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
+ channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
+ if (!channel)
return;
if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
@@ -1468,6 +1456,11 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
memcpy(((struct ieee80211_mgmt *) skb->data)->da, mgmt->sa, ETH_ALEN);
ibss_dbg(sdata, "Sending ProbeResp to %pM\n", mgmt->sa);
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+
+ /* avoid excessive retries for probe request to wildcard SSIDs */
+ if (pos[1] == 0)
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_NO_ACK;
+
ieee80211_tx_skb(sdata, skb);
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 5e44e3179e02..222c28b75315 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -616,7 +616,11 @@ struct ieee80211_if_mesh {
struct ps_data ps;
/* Channel Switching Support */
struct mesh_csa_settings __rcu *csa;
- bool chsw_init;
+ enum {
+ IEEE80211_MESH_CSA_ROLE_NONE,
+ IEEE80211_MESH_CSA_ROLE_INIT,
+ IEEE80211_MESH_CSA_ROLE_REPEATER,
+ } csa_role;
u8 chsw_ttl;
u16 pre_value;
@@ -1238,6 +1242,8 @@ struct ieee80211_local {
struct ieee80211_sub_if_data __rcu *p2p_sdata;
+ struct napi_struct *napi;
+
/* virtual monitor interface */
struct ieee80211_sub_if_data __rcu *monitor_sdata;
struct cfg80211_chan_def monitor_chandef;
@@ -1385,6 +1391,7 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata);
void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata);
void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
__le16 fc, bool acked);
+void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
/* IBSS code */
@@ -1408,8 +1415,7 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
- struct cfg80211_csa_settings *csa_settings,
- bool csa_action);
+ struct cfg80211_csa_settings *csa_settings);
int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
/* scan/BSS handling */
@@ -1553,6 +1559,9 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta);
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);
void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 opmode,
enum ieee80211_band band, bool nss_only);
@@ -1605,7 +1614,7 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
}
/* utility functions/constants */
-extern void *mac80211_wiphy_privid; /* for wiphy privid */
+extern const void *const mac80211_wiphy_privid; /* for wiphy privid */
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
enum nl80211_iftype type);
int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index ce1c44370610..b8d331e7d883 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -101,9 +101,8 @@ static u32 __ieee80211_idle_on(struct ieee80211_local *local)
static u32 __ieee80211_recalc_idle(struct ieee80211_local *local,
bool force_active)
{
- bool working = false, scanning, active;
+ bool working, scanning, active;
unsigned int led_trig_start = 0, led_trig_stop = 0;
- struct ieee80211_roc_work *roc;
lockdep_assert_held(&local->mtx);
@@ -111,12 +110,8 @@ static u32 __ieee80211_recalc_idle(struct ieee80211_local *local,
!list_empty(&local->chanctx_list) ||
local->monitors;
- if (!local->ops->remain_on_channel) {
- list_for_each_entry(roc, &local->roc_list, list) {
- working = true;
- break;
- }
- }
+ working = !local->ops->remain_on_channel &&
+ !list_empty(&local->roc_list);
scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning);
@@ -833,7 +828,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
cancel_work_sync(&local->dynamic_ps_enable_work);
cancel_work_sync(&sdata->recalc_smps);
+ sdata_lock(sdata);
sdata->vif.csa_active = false;
+ sdata_unlock(sdata);
cancel_work_sync(&sdata->csa_finalize_work);
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index d767cfb9b45f..b055f6a55c68 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -893,10 +893,15 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
/* mac80211 supports control port protocol changing */
local->hw.wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
- if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
- else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
+ } else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) {
local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
+ if (hw->max_signal <= 0) {
+ result = -EINVAL;
+ goto fail_wiphy_register;
+ }
+ }
WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
&& (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
@@ -1071,6 +1076,18 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL(ieee80211_register_hw);
+void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
+ struct net_device *napi_dev,
+ int (*poll)(struct napi_struct *, int),
+ int weight)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ netif_napi_add(napi_dev, napi, poll, weight);
+ local->napi = napi;
+}
+EXPORT_SYMBOL_GPL(ieee80211_napi_add);
+
void ieee80211_unregister_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 5b919cab1de0..f70e9cd10552 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -688,7 +688,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
*pos++ = csa->settings.count;
*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
*pos++ = 6;
- if (ifmsh->chsw_init) {
+ if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT) {
*pos++ = ifmsh->mshcfg.dot11MeshTTL;
*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
} else {
@@ -859,18 +859,12 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
{
struct cfg80211_csa_settings params;
struct ieee80211_csa_ie csa_ie;
- struct ieee80211_chanctx_conf *chanctx_conf;
- struct ieee80211_chanctx *chanctx;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
- int err, num_chanctx;
+ int err;
u32 sta_flags;
- if (sdata->vif.csa_active)
- return true;
-
- if (!ifmsh->mesh_id)
- return false;
+ sdata_assert_lock(sdata);
sta_flags = IEEE80211_STA_DISABLE_VHT;
switch (sdata->vif.bss_conf.chandef.width) {
@@ -896,10 +890,6 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
params.chandef = csa_ie.chandef;
params.count = csa_ie.count;
- if (sdata->vif.bss_conf.chandef.chan->band !=
- params.chandef.chan->band)
- return false;
-
if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &params.chandef,
IEEE80211_CHAN_DISABLED)) {
sdata_info(sdata,
@@ -922,24 +912,12 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
return false;
}
- rcu_read_lock();
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto failed_chswitch;
-
- /* don't handle for multi-VIF cases */
- chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
- if (chanctx->refcount > 1)
- goto failed_chswitch;
-
- num_chanctx = 0;
- list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
- num_chanctx++;
-
- if (num_chanctx > 1)
- goto failed_chswitch;
-
- rcu_read_unlock();
+ if (cfg80211_chandef_identical(&params.chandef,
+ &sdata->vif.bss_conf.chandef)) {
+ mcsa_dbg(sdata,
+ "received csa with an identical chandef, ignoring\n");
+ return true;
+ }
mcsa_dbg(sdata,
"received channel switch announcement to go to channel %d MHz\n",
@@ -953,30 +931,16 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
ifmsh->pre_value = csa_ie.pre_value;
}
- if (ifmsh->chsw_ttl < ifmsh->mshcfg.dot11MeshTTL) {
- if (ieee80211_mesh_csa_beacon(sdata, &params, false) < 0)
- return false;
- } else {
+ if (ifmsh->chsw_ttl >= ifmsh->mshcfg.dot11MeshTTL)
return false;
- }
- sdata->csa_radar_required = params.radar_required;
+ ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_REPEATER;
- if (params.block_tx)
- ieee80211_stop_queues_by_reason(&sdata->local->hw,
- IEEE80211_MAX_QUEUE_MAP,
- IEEE80211_QUEUE_STOP_REASON_CSA);
-
- sdata->csa_chandef = params.chandef;
- sdata->vif.csa_active = true;
-
- ieee80211_bss_info_change_notify(sdata, err);
- drv_channel_switch_beacon(sdata, &params.chandef);
+ if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev,
+ &params) < 0)
+ return false;
return true;
-failed_chswitch:
- rcu_read_unlock();
- return false;
}
static void
@@ -1086,7 +1050,8 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
ifmsh->sync_ops->rx_bcn_presp(sdata,
stype, mgmt, &elems, rx_status);
- if (!ifmsh->chsw_init)
+ if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT &&
+ !sdata->vif.csa_active)
ieee80211_mesh_process_chnswitch(sdata, &elems, true);
}
@@ -1095,29 +1060,30 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_csa_settings *tmp_csa_settings;
int ret = 0;
+ int changed = 0;
/* Reset the TTL value and Initiator flag */
- ifmsh->chsw_init = false;
+ ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
ifmsh->chsw_ttl = 0;
/* Remove the CSA and MCSP elements from the beacon */
tmp_csa_settings = rcu_dereference(ifmsh->csa);
rcu_assign_pointer(ifmsh->csa, NULL);
- kfree_rcu(tmp_csa_settings, rcu_head);
+ if (tmp_csa_settings)
+ kfree_rcu(tmp_csa_settings, rcu_head);
ret = ieee80211_mesh_rebuild_beacon(sdata);
if (ret)
return -EINVAL;
- ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+ changed |= BSS_CHANGED_BEACON;
mcsa_dbg(sdata, "complete switching to center freq %d MHz",
sdata->vif.bss_conf.chandef.chan->center_freq);
- return 0;
+ return changed;
}
int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
- struct cfg80211_csa_settings *csa_settings,
- bool csa_action)
+ struct cfg80211_csa_settings *csa_settings)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_csa_settings *tmp_csa_settings;
@@ -1141,12 +1107,7 @@ int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
return ret;
}
- ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
-
- if (csa_action)
- ieee80211_send_action_csa(sdata, csa_settings);
-
- return 0;
+ return BSS_CHANGED_BEACON;
}
static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
@@ -1210,7 +1171,8 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
ifmsh->pre_value = pre_value;
- if (!ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
+ if (!sdata->vif.csa_active &&
+ !ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
mcsa_dbg(sdata, "Failed to process CSA action frame");
return;
}
@@ -1257,7 +1219,7 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
sdata_lock(sdata);
/* mesh already went down */
- if (!sdata->wdev.mesh_id_len)
+ if (!sdata->u.mesh.mesh_id_len)
goto out;
rx_status = IEEE80211_SKB_RXCB(skb);
@@ -1310,7 +1272,7 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
sdata_lock(sdata);
/* mesh already went down */
- if (!sdata->wdev.mesh_id_len)
+ if (!sdata->u.mesh.mesh_id_len)
goto out;
if (ifmsh->preq_queue_len &&
@@ -1365,7 +1327,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
mesh_rmc_init(sdata);
ifmsh->last_preq = jiffies;
ifmsh->next_perr = jiffies;
- ifmsh->chsw_init = false;
+ ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
/* Allocate all mesh structures when creating the first mesh interface. */
if (!mesh_allocated)
ieee80211s_init();
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 245dce969b31..dee50aefd6e8 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -131,13 +131,13 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)
if (unlikely(!sdata->u.mgd.associated))
return;
+ ifmgd->probe_send_count = 0;
+
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
return;
mod_timer(&sdata->u.mgd.conn_mon_timer,
round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
-
- ifmgd->probe_send_count = 0;
}
static int ecw2cw(int ecw)
@@ -531,6 +531,7 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
u8 *pos;
u32 cap;
struct ieee80211_sta_vht_cap vht_cap;
+ u32 mask, ap_bf_sts, our_bf_sts;
BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
@@ -558,6 +559,16 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+ mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
+
+ ap_bf_sts = le32_to_cpu(ap_vht_cap->vht_cap_info) & mask;
+ our_bf_sts = cap & mask;
+
+ if (ap_bf_sts < our_bf_sts) {
+ cap &= ~mask;
+ cap |= ap_bf_sts;
+ }
+
/* reserve and fill IE */
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
@@ -768,6 +779,34 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
sband, chan, sdata->smps_mode);
+ /* if present, add any custom IEs that go before VHT */
+ if (assoc_data->ie_len) {
+ static const u8 before_vht[] = {
+ WLAN_EID_SSID,
+ WLAN_EID_SUPP_RATES,
+ WLAN_EID_EXT_SUPP_RATES,
+ WLAN_EID_PWR_CAPABILITY,
+ WLAN_EID_SUPPORTED_CHANNELS,
+ WLAN_EID_RSN,
+ WLAN_EID_QOS_CAPA,
+ WLAN_EID_RRM_ENABLED_CAPABILITIES,
+ WLAN_EID_MOBILITY_DOMAIN,
+ WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+ WLAN_EID_HT_CAPABILITY,
+ WLAN_EID_BSS_COEX_2040,
+ WLAN_EID_EXT_CAPABILITY,
+ WLAN_EID_QOS_TRAFFIC_CAPA,
+ WLAN_EID_TIM_BCAST_REQ,
+ WLAN_EID_INTERWORKING,
+ };
+ noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
+ before_vht, ARRAY_SIZE(before_vht),
+ offset);
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, assoc_data->ie + offset, noffset - offset);
+ offset = noffset;
+ }
+
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
ieee80211_add_vht_ie(sdata, skb, sband,
&assoc_data->ap_vht_cap);
@@ -1024,7 +1063,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
}
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
- sdata->vif.csa_active = true;
mutex_lock(&local->chanctx_mtx);
if (local->use_chanctx) {
@@ -1062,6 +1100,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&local->chanctx_mtx);
sdata->csa_chandef = csa_ie.chandef;
+ sdata->vif.csa_active = true;
if (csa_ie.mode)
ieee80211_stop_queues_by_reason(&local->hw,
@@ -2233,6 +2272,62 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
/* ignore frame -- wait for timeout */
}
+#define case_WLAN(type) \
+ case WLAN_REASON_##type: return #type
+
+static const char *ieee80211_get_reason_code_string(u16 reason_code)
+{
+ switch (reason_code) {
+ case_WLAN(UNSPECIFIED);
+ case_WLAN(PREV_AUTH_NOT_VALID);
+ case_WLAN(DEAUTH_LEAVING);
+ case_WLAN(DISASSOC_DUE_TO_INACTIVITY);
+ case_WLAN(DISASSOC_AP_BUSY);
+ case_WLAN(CLASS2_FRAME_FROM_NONAUTH_STA);
+ case_WLAN(CLASS3_FRAME_FROM_NONASSOC_STA);
+ case_WLAN(DISASSOC_STA_HAS_LEFT);
+ case_WLAN(STA_REQ_ASSOC_WITHOUT_AUTH);
+ case_WLAN(DISASSOC_BAD_POWER);
+ case_WLAN(DISASSOC_BAD_SUPP_CHAN);
+ case_WLAN(INVALID_IE);
+ case_WLAN(MIC_FAILURE);
+ case_WLAN(4WAY_HANDSHAKE_TIMEOUT);
+ case_WLAN(GROUP_KEY_HANDSHAKE_TIMEOUT);
+ case_WLAN(IE_DIFFERENT);
+ case_WLAN(INVALID_GROUP_CIPHER);
+ case_WLAN(INVALID_PAIRWISE_CIPHER);
+ case_WLAN(INVALID_AKMP);
+ case_WLAN(UNSUPP_RSN_VERSION);
+ case_WLAN(INVALID_RSN_IE_CAP);
+ case_WLAN(IEEE8021X_FAILED);
+ case_WLAN(CIPHER_SUITE_REJECTED);
+ case_WLAN(DISASSOC_UNSPECIFIED_QOS);
+ case_WLAN(DISASSOC_QAP_NO_BANDWIDTH);
+ case_WLAN(DISASSOC_LOW_ACK);
+ case_WLAN(DISASSOC_QAP_EXCEED_TXOP);
+ case_WLAN(QSTA_LEAVE_QBSS);
+ case_WLAN(QSTA_NOT_USE);
+ case_WLAN(QSTA_REQUIRE_SETUP);
+ case_WLAN(QSTA_TIMEOUT);
+ case_WLAN(QSTA_CIPHER_NOT_SUPP);
+ case_WLAN(MESH_PEER_CANCELED);
+ case_WLAN(MESH_MAX_PEERS);
+ case_WLAN(MESH_CONFIG);
+ case_WLAN(MESH_CLOSE);
+ case_WLAN(MESH_MAX_RETRIES);
+ case_WLAN(MESH_CONFIRM_TIMEOUT);
+ case_WLAN(MESH_INVALID_GTK);
+ case_WLAN(MESH_INCONSISTENT_PARAM);
+ case_WLAN(MESH_INVALID_SECURITY);
+ case_WLAN(MESH_PATH_ERROR);
+ case_WLAN(MESH_PATH_NOFORWARD);
+ case_WLAN(MESH_PATH_DEST_UNREACHABLE);
+ case_WLAN(MAC_EXISTS_IN_MBSS);
+ case_WLAN(MESH_CHAN_REGULATORY);
+ case_WLAN(MESH_CHAN);
+ default: return "<unknown>";
+ }
+}
static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len)
@@ -2254,8 +2349,8 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
- sdata_info(sdata, "deauthenticated from %pM (Reason: %u)\n",
- bssid, reason_code);
+ sdata_info(sdata, "deauthenticated from %pM (Reason: %u=%s)\n",
+ bssid, reason_code, ieee80211_get_reason_code_string(reason_code));
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
@@ -2688,28 +2783,20 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems)
{
struct ieee80211_local *local = sdata->local;
- int freq;
struct ieee80211_bss *bss;
struct ieee80211_channel *channel;
sdata_assert_lock(sdata);
- if (elems->ds_params)
- freq = ieee80211_channel_to_frequency(elems->ds_params[0],
- rx_status->band);
- else
- freq = rx_status->freq;
-
- channel = ieee80211_get_channel(local->hw.wiphy, freq);
-
- if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
+ channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
+ if (!channel)
return;
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
channel);
if (bss) {
- ieee80211_rx_bss_put(local, bss);
sdata->vif.bss_conf.beacon_rate = bss->beacon_rate;
+ ieee80211_rx_bss_put(local, bss);
}
}
@@ -3504,6 +3591,32 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
}
#ifdef CONFIG_PM
+void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
+
+ sdata_lock(sdata);
+
+ if (ifmgd->auth_data) {
+ /*
+ * If we are trying to authenticate while suspending, cfg80211
+ * won't know and won't actually abort those attempts, thus we
+ * need to do that ourselves.
+ */
+ ieee80211_send_deauth_disassoc(sdata,
+ ifmgd->auth_data->bss->bssid,
+ IEEE80211_STYPE_DEAUTH,
+ WLAN_REASON_DEAUTH_LEAVING,
+ false, frame_buf);
+ ieee80211_destroy_auth_data(sdata, false);
+ cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+ IEEE80211_DEAUTH_FRAME_LEN);
+ }
+
+ sdata_unlock(sdata);
+}
+
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -4322,37 +4435,41 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
bool tx = !req->local_state_change;
- bool report_frame = false;
- sdata_info(sdata,
- "deauthenticating from %pM by local choice (reason=%d)\n",
- req->bssid, req->reason_code);
+ if (ifmgd->auth_data &&
+ ether_addr_equal(ifmgd->auth_data->bss->bssid, req->bssid)) {
+ sdata_info(sdata,
+ "aborting authentication with %pM by local choice (Reason: %u=%s)\n",
+ req->bssid, req->reason_code,
+ ieee80211_get_reason_code_string(req->reason_code));
- if (ifmgd->auth_data) {
drv_mgd_prepare_tx(sdata->local, sdata);
ieee80211_send_deauth_disassoc(sdata, req->bssid,
IEEE80211_STYPE_DEAUTH,
req->reason_code, tx,
frame_buf);
ieee80211_destroy_auth_data(sdata, false);
+ cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+ IEEE80211_DEAUTH_FRAME_LEN);
- report_frame = true;
- goto out;
+ return 0;
}
if (ifmgd->associated &&
ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
+ sdata_info(sdata,
+ "deauthenticating from %pM by local choice (Reason: %u=%s)\n",
+ req->bssid, req->reason_code,
+ ieee80211_get_reason_code_string(req->reason_code));
+
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
req->reason_code, tx, frame_buf);
- report_frame = true;
- }
-
- out:
- if (report_frame)
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
IEEE80211_DEAUTH_FRAME_LEN);
+ return 0;
+ }
- return 0;
+ return -ENOTCONN;
}
int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
@@ -4372,8 +4489,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
return -ENOLINK;
sdata_info(sdata,
- "disassociating from %pM by local choice (reason=%d)\n",
- req->bss->bssid, req->reason_code);
+ "disassociating from %pM by local choice (Reason: %u=%s)\n",
+ req->bss->bssid, req->reason_code, ieee80211_get_reason_code_string(req->reason_code));
memcpy(bssid, req->bss->bssid, ETH_ALEN);
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC,
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index af64fb8e8add..d478b880a0af 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -100,10 +100,18 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
/* remove all interfaces that were created in the driver */
list_for_each_entry(sdata, &local->interfaces, list) {
- if (!ieee80211_sdata_running(sdata) ||
- sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
- sdata->vif.type == NL80211_IFTYPE_MONITOR)
+ if (!ieee80211_sdata_running(sdata))
continue;
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MONITOR:
+ continue;
+ case NL80211_IFTYPE_STATION:
+ ieee80211_mgd_quiesce(sdata);
+ break;
+ default:
+ break;
+ }
drv_remove_interface(local, sdata);
}
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 22b223f13c9f..8fdadfd94ba8 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -10,15 +10,15 @@
#include <linux/kernel.h>
#include <linux/rtnetlink.h>
-#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include "rate.h"
#include "ieee80211_i.h"
#include "debugfs.h"
struct rate_control_alg {
struct list_head list;
- struct rate_control_ops *ops;
+ const struct rate_control_ops *ops;
};
static LIST_HEAD(rate_ctrl_algs);
@@ -29,7 +29,7 @@ module_param(ieee80211_default_rc_algo, charp, 0644);
MODULE_PARM_DESC(ieee80211_default_rc_algo,
"Default rate control algorithm for mac80211 to use");
-int ieee80211_rate_control_register(struct rate_control_ops *ops)
+int ieee80211_rate_control_register(const struct rate_control_ops *ops)
{
struct rate_control_alg *alg;
@@ -60,7 +60,7 @@ int ieee80211_rate_control_register(struct rate_control_ops *ops)
}
EXPORT_SYMBOL(ieee80211_rate_control_register);
-void ieee80211_rate_control_unregister(struct rate_control_ops *ops)
+void ieee80211_rate_control_unregister(const struct rate_control_ops *ops)
{
struct rate_control_alg *alg;
@@ -76,32 +76,31 @@ void ieee80211_rate_control_unregister(struct rate_control_ops *ops)
}
EXPORT_SYMBOL(ieee80211_rate_control_unregister);
-static struct rate_control_ops *
+static const struct rate_control_ops *
ieee80211_try_rate_control_ops_get(const char *name)
{
struct rate_control_alg *alg;
- struct rate_control_ops *ops = NULL;
+ const struct rate_control_ops *ops = NULL;
if (!name)
return NULL;
mutex_lock(&rate_ctrl_mutex);
list_for_each_entry(alg, &rate_ctrl_algs, list) {
- if (!strcmp(alg->ops->name, name))
- if (try_module_get(alg->ops->module)) {
- ops = alg->ops;
- break;
- }
+ if (!strcmp(alg->ops->name, name)) {
+ ops = alg->ops;
+ break;
+ }
}
mutex_unlock(&rate_ctrl_mutex);
return ops;
}
/* Get the rate control algorithm. */
-static struct rate_control_ops *
+static const struct rate_control_ops *
ieee80211_rate_control_ops_get(const char *name)
{
- struct rate_control_ops *ops;
+ const struct rate_control_ops *ops;
const char *alg_name;
kparam_block_sysfs_write(ieee80211_default_rc_algo);
@@ -111,10 +110,6 @@ ieee80211_rate_control_ops_get(const char *name)
alg_name = name;
ops = ieee80211_try_rate_control_ops_get(alg_name);
- if (!ops) {
- request_module("rc80211_%s", alg_name);
- ops = ieee80211_try_rate_control_ops_get(alg_name);
- }
if (!ops && name)
/* try default if specific alg requested but not found */
ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo);
@@ -127,11 +122,6 @@ ieee80211_rate_control_ops_get(const char *name)
return ops;
}
-static void ieee80211_rate_control_ops_put(struct rate_control_ops *ops)
-{
- module_put(ops->module);
-}
-
#ifdef CONFIG_MAC80211_DEBUGFS
static ssize_t rcname_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
@@ -158,11 +148,11 @@ static struct rate_control_ref *rate_control_alloc(const char *name,
ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL);
if (!ref)
- goto fail_ref;
+ return NULL;
ref->local = local;
ref->ops = ieee80211_rate_control_ops_get(name);
if (!ref->ops)
- goto fail_ops;
+ goto free;
#ifdef CONFIG_MAC80211_DEBUGFS
debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir);
@@ -172,14 +162,11 @@ static struct rate_control_ref *rate_control_alloc(const char *name,
ref->priv = ref->ops->alloc(&local->hw, debugfsdir);
if (!ref->priv)
- goto fail_priv;
+ goto free;
return ref;
-fail_priv:
- ieee80211_rate_control_ops_put(ref->ops);
-fail_ops:
+free:
kfree(ref);
-fail_ref:
return NULL;
}
@@ -192,7 +179,6 @@ static void rate_control_free(struct rate_control_ref *ctrl_ref)
ctrl_ref->local->debugfs.rcdir = NULL;
#endif
- ieee80211_rate_control_ops_put(ctrl_ref->ops);
kfree(ctrl_ref);
}
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index b95e16c07081..9aa2a1190a86 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -21,7 +21,7 @@
struct rate_control_ref {
struct ieee80211_local *local;
- struct rate_control_ops *ops;
+ const struct rate_control_ops *ops;
void *priv;
};
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index f3d88b0c054c..26fd94fa0aed 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -657,7 +657,7 @@ minstrel_free(void *priv)
kfree(priv);
}
-struct rate_control_ops mac80211_minstrel = {
+const struct rate_control_ops mac80211_minstrel = {
.name = "minstrel",
.tx_status = minstrel_tx_status,
.get_rate = minstrel_get_rate,
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
index f4301f4b2e41..046d1bd598a8 100644
--- a/net/mac80211/rc80211_minstrel.h
+++ b/net/mac80211/rc80211_minstrel.h
@@ -123,7 +123,7 @@ struct minstrel_debugfs_info {
char buf[];
};
-extern struct rate_control_ops mac80211_minstrel;
+extern const struct rate_control_ops mac80211_minstrel;
void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
void minstrel_remove_sta_debugfs(void *priv, void *priv_sta);
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index c1b5b73c5b91..bccaf854a309 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -124,7 +124,7 @@ const struct mcs_group minstrel_mcs_groups[] = {
#define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1)
-static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES];
+static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
static void
minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
@@ -1031,7 +1031,7 @@ minstrel_ht_free(void *priv)
mac80211_minstrel.free(priv);
}
-static struct rate_control_ops mac80211_minstrel_ht = {
+static const struct rate_control_ops mac80211_minstrel_ht = {
.name = "minstrel_ht",
.tx_status = minstrel_ht_tx_status,
.get_rate = minstrel_ht_get_rate,
@@ -1048,8 +1048,7 @@ static struct rate_control_ops mac80211_minstrel_ht = {
};
-static void
-init_sample_table(void)
+static void __init init_sample_table(void)
{
int col, i, new_idx;
u8 rnd[MCS_GROUP_RATES];
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
index 958fad07b54c..d0da2a70fe68 100644
--- a/net/mac80211/rc80211_pid_algo.c
+++ b/net/mac80211/rc80211_pid_algo.c
@@ -452,7 +452,7 @@ static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta,
kfree(priv_sta);
}
-static struct rate_control_ops mac80211_rcpid = {
+static const struct rate_control_ops mac80211_rcpid = {
.name = "pid",
.tx_status = rate_control_pid_tx_status,
.get_rate = rate_control_pid_get_rate,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 3e57f96c9666..216c45b949e5 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -40,8 +40,6 @@
static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
struct sk_buff *skb)
{
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
if (likely(skb->len > FCS_LEN))
__pskb_trim(skb, skb->len - FCS_LEN);
@@ -53,9 +51,6 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
}
}
- if (status->vendor_radiotap_len)
- __pskb_pull(skb, status->vendor_radiotap_len);
-
return skb;
}
@@ -64,14 +59,13 @@ static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len)
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr;
- hdr = (void *)(skb->data + status->vendor_radiotap_len);
+ hdr = (void *)(skb->data);
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
RX_FLAG_FAILED_PLCP_CRC |
RX_FLAG_AMPDU_IS_ZEROLEN))
return 1;
- if (unlikely(skb->len < 16 + present_fcs_len +
- status->vendor_radiotap_len))
+ if (unlikely(skb->len < 16 + present_fcs_len))
return 1;
if (ieee80211_is_ctl(hdr->frame_control) &&
!ieee80211_is_pspoll(hdr->frame_control) &&
@@ -90,8 +84,6 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
len = sizeof(struct ieee80211_radiotap_header) + 8;
/* allocate extra bitmaps */
- if (status->vendor_radiotap_len)
- len += 4;
if (status->chains)
len += 4 * hweight8(status->chains);
@@ -127,18 +119,6 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
len += 2 * hweight8(status->chains);
}
- if (status->vendor_radiotap_len) {
- if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
- status->vendor_radiotap_align = 1;
- /* align standard part of vendor namespace */
- len = ALIGN(len, 2);
- /* allocate standard part of vendor namespace */
- len += 6;
- /* align vendor-defined part */
- len = ALIGN(len, status->vendor_radiotap_align);
- /* vendor-defined part is already in skb */
- }
-
return len;
}
@@ -172,7 +152,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
it_present = &rthdr->it_present;
/* radiotap header, set always present flags */
- rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
+ rthdr->it_len = cpu_to_le16(rtap_len);
it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) |
BIT(IEEE80211_RADIOTAP_CHANNEL) |
BIT(IEEE80211_RADIOTAP_RX_FLAGS);
@@ -190,14 +170,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
}
- if (status->vendor_radiotap_len) {
- it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
- BIT(IEEE80211_RADIOTAP_EXT);
- put_unaligned_le32(it_present_val, it_present);
- it_present++;
- it_present_val = status->vendor_radiotap_bitmap;
- }
-
put_unaligned_le32(it_present_val, it_present);
pos = (void *)(it_present + 1);
@@ -307,6 +279,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos |= IEEE80211_RADIOTAP_MCS_BW_40;
if (status->flag & RX_FLAG_HT_GF)
*pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
+ if (status->flag & RX_FLAG_LDPC)
+ *pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT;
*pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
pos++;
@@ -349,20 +323,25 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
/* known field - how to handle 80+80? */
- if (status->flag & RX_FLAG_80P80MHZ)
+ if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)
known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
put_unaligned_le16(known, pos);
pos += 2;
/* flags */
if (status->flag & RX_FLAG_SHORT_GI)
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+ /* in VHT, STBC is binary */
+ if (status->flag & RX_FLAG_STBC_MASK)
+ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
+ if (status->vht_flag & RX_VHT_FLAG_BF)
+ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED;
pos++;
/* bandwidth */
- if (status->flag & RX_FLAG_80MHZ)
+ if (status->vht_flag & RX_VHT_FLAG_80MHZ)
*pos++ = 4;
- else if (status->flag & RX_FLAG_80P80MHZ)
+ else if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)
*pos++ = 0; /* marked not known above */
- else if (status->flag & RX_FLAG_160MHZ)
+ else if (status->vht_flag & RX_VHT_FLAG_160MHZ)
*pos++ = 11;
else if (status->flag & RX_FLAG_40MHZ)
*pos++ = 1;
@@ -372,6 +351,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos = (status->rate_idx << 4) | status->vht_nss;
pos += 4;
/* coding field */
+ if (status->flag & RX_FLAG_LDPC)
+ *pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
pos++;
/* group ID */
pos++;
@@ -383,21 +364,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos++ = status->chain_signal[chain];
*pos++ = chain;
}
-
- if (status->vendor_radiotap_len) {
- /* ensure 2 byte alignment for the vendor field as required */
- if ((pos - (u8 *)rthdr) & 1)
- *pos++ = 0;
- *pos++ = status->vendor_radiotap_oui[0];
- *pos++ = status->vendor_radiotap_oui[1];
- *pos++ = status->vendor_radiotap_oui[2];
- *pos++ = status->vendor_radiotap_subns;
- put_unaligned_le16(status->vendor_radiotap_len, pos);
- pos += 2;
- /* align the actual payload as requested */
- while ((pos - (u8 *)rthdr) & (status->vendor_radiotap_align - 1))
- *pos++ = 0;
- }
}
/*
@@ -428,8 +394,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
present_fcs_len = FCS_LEN;
- /* ensure hdr->frame_control and vendor radiotap data are in skb head */
- if (!pskb_may_pull(origskb, 2 + status->vendor_radiotap_len)) {
+ /* ensure hdr->frame_control is in skb head */
+ if (!pskb_may_pull(origskb, 2)) {
dev_kfree_skb(origskb);
return NULL;
}
@@ -599,10 +565,10 @@ static int ieee80211_is_unicast_robust_mgmt_frame(struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- if (skb->len < 24 || is_multicast_ether_addr(hdr->addr1))
+ if (is_multicast_ether_addr(hdr->addr1))
return 0;
- return ieee80211_is_robust_mgmt_frame(hdr);
+ return ieee80211_is_robust_mgmt_frame(skb);
}
@@ -610,10 +576,10 @@ static int ieee80211_is_multicast_robust_mgmt_frame(struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- if (skb->len < 24 || !is_multicast_ether_addr(hdr->addr1))
+ if (!is_multicast_ether_addr(hdr->addr1))
return 0;
- return ieee80211_is_robust_mgmt_frame(hdr);
+ return ieee80211_is_robust_mgmt_frame(skb);
}
@@ -626,7 +592,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))
return -1;
- if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr))
+ if (!ieee80211_is_robust_mgmt_frame(skb))
return -1; /* not a robust management frame */
mmie = (struct ieee80211_mmie *)
@@ -1268,6 +1234,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control)) {
sta->last_rx_rate_idx = status->rate_idx;
sta->last_rx_rate_flag = status->flag;
+ sta->last_rx_rate_vht_flag = status->vht_flag;
sta->last_rx_rate_vht_nss = status->vht_nss;
}
}
@@ -1280,6 +1247,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control)) {
sta->last_rx_rate_idx = status->rate_idx;
sta->last_rx_rate_flag = status->flag;
+ sta->last_rx_rate_vht_flag = status->vht_flag;
sta->last_rx_rate_vht_nss = status->vht_nss;
}
}
@@ -1318,18 +1286,15 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
!ieee80211_has_morefrags(hdr->frame_control) &&
!(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) &&
(rx->sdata->vif.type == NL80211_IFTYPE_AP ||
- rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
+ rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
+ /* PM bit is only checked in frames where it isn't reserved,
+ * in AP mode it's reserved in non-bufferable management frames
+ * (cf. IEEE 802.11-2012 8.2.4.1.7 Power Management field)
+ */
+ (!ieee80211_is_mgmt(hdr->frame_control) ||
+ ieee80211_is_bufferable_mmpdu(hdr->frame_control))) {
if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
- /*
- * Ignore doze->wake transitions that are
- * indicated by non-data frames, the standard
- * is unclear here, but for example going to
- * PS mode and then scanning would cause a
- * doze->wake transition for the probe request,
- * and that is clearly undesirable.
- */
- if (ieee80211_is_data(hdr->frame_control) &&
- !ieee80211_has_pm(hdr->frame_control))
+ if (!ieee80211_has_pm(hdr->frame_control))
sta_ps_end(sta);
} else {
if (ieee80211_has_pm(hdr->frame_control))
@@ -1852,8 +1817,7 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
* having configured keys.
*/
if (unlikely(ieee80211_is_action(fc) && !rx->key &&
- ieee80211_is_robust_mgmt_frame(
- (struct ieee80211_hdr *) rx->skb->data)))
+ ieee80211_is_robust_mgmt_frame(rx->skb)))
return -EACCES;
}
@@ -2000,7 +1964,10 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
/* deliver to local stack */
skb->protocol = eth_type_trans(skb, dev);
memset(skb->cb, 0, sizeof(skb->cb));
- netif_receive_skb(skb);
+ if (rx->local->napi)
+ napi_gro_receive(rx->local->napi, skb);
+ else
+ netif_receive_skb(skb);
}
if (xmit_skb) {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 88c81616f8f7..3ce7f2c8539a 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -472,9 +472,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->ops->hw_scan) {
u8 *ies;
- local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN +
- local->scan_ies_len +
- req->ie_len;
+ local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;
local->hw_scan_req = kmalloc(
sizeof(*local->hw_scan_req) +
req->n_channels * sizeof(req->channels[0]) +
@@ -979,8 +977,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
struct cfg80211_chan_def chandef;
int ret, i, iebufsz;
- iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
- local->scan_ies_len + req->ie_len;
+ iebufsz = local->scan_ies_len + req->ie_len;
lockdep_assert_held(&local->mtx);
@@ -1058,9 +1055,11 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
/* We don't want to restart sched scan anymore. */
local->sched_scan_req = NULL;
- if (rcu_access_pointer(local->sched_scan_sdata))
- drv_sched_scan_stop(local, sdata);
-
+ if (rcu_access_pointer(local->sched_scan_sdata)) {
+ ret = drv_sched_scan_stop(local, sdata);
+ if (!ret)
+ rcu_assign_pointer(local->sched_scan_sdata, NULL);
+ }
out:
mutex_unlock(&local->mtx);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index d3a6d8208f2f..4acc5fc402fa 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -261,6 +261,7 @@ struct ieee80211_tx_latency_stat {
* "the" transmit rate
* @last_rx_rate_idx: rx status rate index of the last data packet
* @last_rx_rate_flag: rx status flag of the last data packet
+ * @last_rx_rate_vht_flag: rx status vht flag of the last data packet
* @last_rx_rate_vht_nss: rx status nss of last data packet
* @lock: used for locking all fields that require locking, see comments
* in the header file.
@@ -396,6 +397,7 @@ struct sta_info {
struct ieee80211_tx_rate last_tx_rate;
int last_rx_rate_idx;
u32 last_rx_rate_flag;
+ u32 last_rx_rate_vht_flag;
u8 last_rx_rate_vht_nss;
u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 1ee85c402439..e6e574a307c8 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -479,7 +479,7 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
u32 msrmnt;
u16 tid;
u8 *qc;
- int i, bin_range_count, bin_count;
+ int i, bin_range_count;
u32 *bin_ranges;
__le16 fc;
struct ieee80211_tx_latency_stat *tx_lat;
@@ -522,7 +522,6 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
/* count how many Tx frames transmitted with the appropriate latency */
bin_range_count = tx_latency->n_ranges;
bin_ranges = tx_latency->ranges;
- bin_count = tx_lat->bin_count;
for (i = 0; i < bin_range_count; i++) {
if (msrmnt <= bin_ranges[i]) {
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 4080c615636f..19d36d4117e0 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -452,8 +452,7 @@ static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta,
if (sta == NULL || !test_sta_flag(sta, WLAN_STA_MFP))
return 0;
- if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *)
- skb->data))
+ if (!ieee80211_is_robust_mgmt_frame(skb))
return 0;
return 1;
@@ -538,11 +537,8 @@ ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED))
return TX_CONTINUE;
- /* only deauth, disassoc and action are bufferable MMPDUs */
if (ieee80211_is_mgmt(hdr->frame_control) &&
- !ieee80211_is_deauth(hdr->frame_control) &&
- !ieee80211_is_disassoc(hdr->frame_control) &&
- !ieee80211_is_action(hdr->frame_control)) {
+ !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {
if (tx->flags & IEEE80211_TX_UNICAST)
info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
return TX_CONTINUE;
@@ -582,7 +578,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
tx->key = key;
else if (ieee80211_is_mgmt(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1) &&
- ieee80211_is_robust_mgmt_frame(hdr) &&
+ ieee80211_is_robust_mgmt_frame(tx->skb) &&
(key = rcu_dereference(tx->sdata->default_mgmt_key)))
tx->key = key;
else if (is_multicast_ether_addr(hdr->addr1) &&
@@ -597,12 +593,12 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
tx->key = NULL;
else if (tx->skb->protocol == tx->sdata->control_port_protocol)
tx->key = NULL;
- else if (ieee80211_is_robust_mgmt_frame(hdr) &&
+ else if (ieee80211_is_robust_mgmt_frame(tx->skb) &&
!(ieee80211_is_action(hdr->frame_control) &&
tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))
tx->key = NULL;
else if (ieee80211_is_mgmt(hdr->frame_control) &&
- !ieee80211_is_robust_mgmt_frame(hdr))
+ !ieee80211_is_robust_mgmt_frame(tx->skb))
tx->key = NULL;
else {
I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
@@ -2417,15 +2413,6 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
return 0;
}
-void ieee80211_csa_finish(struct ieee80211_vif *vif)
-{
- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
-
- ieee80211_queue_work(&sdata->local->hw,
- &sdata->csa_finalize_work);
-}
-EXPORT_SYMBOL(ieee80211_csa_finish);
-
static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
struct beacon_data *beacon)
{
@@ -2454,8 +2441,12 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
if (WARN_ON(counter_offset_beacon >= beacon_data_len))
return;
- /* warn if the driver did not check for/react to csa completeness */
- if (WARN_ON(beacon_data[counter_offset_beacon] == 0))
+ /* Warn if the driver did not check for/react to csa
+ * completeness. A beacon with CSA counter set to 0 should
+ * never occur, because a counter of 1 means switch just
+ * before the next beacon.
+ */
+ if (WARN_ON(beacon_data[counter_offset_beacon] == 1))
return;
beacon_data[counter_offset_beacon]--;
@@ -2521,7 +2512,7 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
if (WARN_ON(counter_beacon > beacon_data_len))
goto out;
- if (beacon_data[counter_beacon] == 0)
+ if (beacon_data[counter_beacon] == 1)
ret = true;
out:
rcu_read_unlock();
@@ -2909,7 +2900,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
}
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
if (!ieee80211_tx_prepare(sdata, &tx, skb))
break;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index b8700d417a9c..275c94f995f7 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -34,7 +34,7 @@
#include "wep.h"
/* privid for wiphys to determine whether they belong to us or not */
-void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
+const void *const mac80211_wiphy_privid = &mac80211_wiphy_privid;
struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
{
@@ -1277,13 +1277,32 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
* that calculates local->scan_ies_len.
*/
- /* add any remaining custom IEs */
+ /* insert custom IEs that go before VHT */
if (ie && ie_len) {
- noffset = ie_len;
+ static const u8 before_vht[] = {
+ WLAN_EID_SSID,
+ WLAN_EID_SUPP_RATES,
+ WLAN_EID_REQUEST,
+ WLAN_EID_EXT_SUPP_RATES,
+ WLAN_EID_DS_PARAMS,
+ WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+ WLAN_EID_HT_CAPABILITY,
+ WLAN_EID_BSS_COEX_2040,
+ WLAN_EID_EXT_CAPABILITY,
+ WLAN_EID_SSID_LIST,
+ WLAN_EID_CHANNEL_USAGE,
+ WLAN_EID_INTERWORKING,
+ /* mesh ID can't happen here */
+ /* 60 GHz can't happen here right now */
+ };
+ noffset = ieee80211_ie_split(ie, ie_len,
+ before_vht, ARRAY_SIZE(before_vht),
+ offset);
if (end - pos < noffset - offset)
goto out_err;
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
+ offset = noffset;
}
if (sband->vht_cap.vht_supported) {
@@ -1293,6 +1312,15 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
sband->vht_cap.cap);
}
+ /* add any remaining custom IEs */
+ if (ie && ie_len) {
+ noffset = ie_len;
+ if (end - pos < noffset - offset)
+ goto out_err;
+ memcpy(pos, ie + offset, noffset - offset);
+ pos += noffset - offset;
+ }
+
return pos - buffer;
out_err:
WARN_ONCE(1, "not enough space for preq IEs\n");
@@ -1370,7 +1398,6 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band, u32 *basic_rates)
{
struct ieee80211_supported_band *sband;
- struct ieee80211_rate *bitrates;
size_t num_rates;
u32 supp_rates, rate_flags;
int i, j, shift;
@@ -1382,7 +1409,6 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
if (WARN_ON(!sband))
return 1;
- bitrates = sband->bitrates;
num_rates = sband->n_bitrates;
supp_rates = 0;
for (i = 0; i < elems->supp_rates_len +
@@ -2268,11 +2294,11 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
ri.nss = status->vht_nss;
if (status->flag & RX_FLAG_40MHZ)
ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
- if (status->flag & RX_FLAG_80MHZ)
+ if (status->vht_flag & RX_VHT_FLAG_80MHZ)
ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
- if (status->flag & RX_FLAG_80P80MHZ)
+ if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)
ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
- if (status->flag & RX_FLAG_160MHZ)
+ if (status->vht_flag & RX_VHT_FLAG_160MHZ)
ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
if (status->flag & RX_FLAG_SHORT_GI)
ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index d75f35c6e1a0..e9e36a256165 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -349,9 +349,9 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta)
sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss);
}
-void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
- struct sta_info *sta, u8 opmode,
- enum ieee80211_band band, bool nss_only)
+u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, u8 opmode,
+ enum ieee80211_band band, bool nss_only)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
@@ -363,7 +363,7 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
/* ignore - no support for BF yet */
if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
- return;
+ return 0;
nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
@@ -375,7 +375,7 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
}
if (nss_only)
- goto change;
+ return changed;
switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ:
@@ -398,7 +398,19 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
changed |= IEEE80211_RC_BW_CHANGED;
}
- change:
- if (changed)
+ return changed;
+}
+
+void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, u8 opmode,
+ enum ieee80211_band band, bool nss_only)
+{
+ 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);
+
+ if (changed > 0)
rate_control_rate_update(local, sband, sta, changed);
}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 21448d629b15..b8600e3c29c8 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -301,8 +301,7 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
}
-static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
- int encrypted)
+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad)
{
__le16 mask_fc;
int a4_included, mgmt;
@@ -456,7 +455,7 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
return 0;
pos += IEEE80211_CCMP_HDR_LEN;
- ccmp_special_blocks(skb, pn, b_0, aad, 0);
+ ccmp_special_blocks(skb, pn, b_0, aad);
ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
skb_put(skb, IEEE80211_CCMP_MIC_LEN));
@@ -495,7 +494,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (!ieee80211_is_data(hdr->frame_control) &&
- !ieee80211_is_robust_mgmt_frame(hdr))
+ !ieee80211_is_robust_mgmt_frame(skb))
return RX_CONTINUE;
data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN -
@@ -524,7 +523,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
u8 aad[2 * AES_BLOCK_SIZE];
u8 b_0[AES_BLOCK_SIZE];
/* hardware didn't decrypt/verify MIC */
- ccmp_special_blocks(skb, pn, b_0, aad, 1);
+ ccmp_special_blocks(skb, pn, b_0, aad);
if (ieee80211_aes_ccm_decrypt(
key->u.ccmp.tfm, b_0, aad,
diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile
index 57cf5d1a2e4a..15d62df52182 100644
--- a/net/mac802154/Makefile
+++ b/net/mac802154/Makefile
@@ -1,2 +1,4 @@
obj-$(CONFIG_MAC802154) += mac802154.o
mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o monitor.o wpan.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c
index 52ae6646a411..2cf66d885e68 100644
--- a/net/mac802154/ieee802154_dev.c
+++ b/net/mac802154/ieee802154_dev.c
@@ -27,6 +27,7 @@
#include <net/netlink.h>
#include <linux/nl802154.h>
#include <net/mac802154.h>
+#include <net/ieee802154_netdev.h>
#include <net/route.h>
#include <net/wpan-phy.h>
@@ -35,9 +36,28 @@
int mac802154_slave_open(struct net_device *dev)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct mac802154_sub_if_data *subif;
struct mac802154_priv *ipriv = priv->hw;
int res = 0;
+ ASSERT_RTNL();
+
+ if (priv->type == IEEE802154_DEV_WPAN) {
+ mutex_lock(&priv->hw->slaves_mtx);
+ list_for_each_entry(subif, &priv->hw->slaves, list) {
+ if (subif != priv && subif->type == priv->type &&
+ subif->running) {
+ mutex_unlock(&priv->hw->slaves_mtx);
+ return -EBUSY;
+ }
+ }
+ mutex_unlock(&priv->hw->slaves_mtx);
+ }
+
+ mutex_lock(&priv->hw->slaves_mtx);
+ priv->running = true;
+ mutex_unlock(&priv->hw->slaves_mtx);
+
if (ipriv->open_count++ == 0) {
res = ipriv->ops->start(&ipriv->hw);
WARN_ON(res);
@@ -46,7 +66,9 @@ int mac802154_slave_open(struct net_device *dev)
}
if (ipriv->ops->ieee_addr) {
- res = ipriv->ops->ieee_addr(&ipriv->hw, dev->dev_addr);
+ __le64 addr = ieee802154_devaddr_from_raw(dev->dev_addr);
+
+ res = ipriv->ops->ieee_addr(&ipriv->hw, addr);
WARN_ON(res);
if (res)
goto err;
@@ -66,8 +88,14 @@ int mac802154_slave_close(struct net_device *dev)
struct mac802154_sub_if_data *priv = netdev_priv(dev);
struct mac802154_priv *ipriv = priv->hw;
+ ASSERT_RTNL();
+
netif_stop_queue(dev);
+ mutex_lock(&priv->hw->slaves_mtx);
+ priv->running = false;
+ mutex_unlock(&priv->hw->slaves_mtx);
+
if (!--ipriv->open_count)
ipriv->ops->stop(&ipriv->hw);
@@ -165,6 +193,49 @@ err:
return ERR_PTR(err);
}
+static int mac802154_set_txpower(struct wpan_phy *phy, int db)
+{
+ struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+ return priv->ops->set_txpower(&priv->hw, db);
+}
+
+static int mac802154_set_lbt(struct wpan_phy *phy, bool on)
+{
+ struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+ return priv->ops->set_lbt(&priv->hw, on);
+}
+
+static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode)
+{
+ struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+ return priv->ops->set_cca_mode(&priv->hw, mode);
+}
+
+static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level)
+{
+ struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+ return priv->ops->set_cca_ed_level(&priv->hw, level);
+}
+
+static int mac802154_set_csma_params(struct wpan_phy *phy, u8 min_be,
+ u8 max_be, u8 retries)
+{
+ struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+ return priv->ops->set_csma_params(&priv->hw, min_be, max_be, retries);
+}
+
+static int mac802154_set_frame_retries(struct wpan_phy *phy, s8 retries)
+{
+ struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+ return priv->ops->set_frame_retries(&priv->hw, retries);
+}
+
struct ieee802154_dev *
ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
{
@@ -242,6 +313,18 @@ int ieee802154_register_device(struct ieee802154_dev *dev)
priv->phy->add_iface = mac802154_add_iface;
priv->phy->del_iface = mac802154_del_iface;
+ if (priv->ops->set_txpower)
+ priv->phy->set_txpower = mac802154_set_txpower;
+ if (priv->ops->set_lbt)
+ priv->phy->set_lbt = mac802154_set_lbt;
+ if (priv->ops->set_cca_mode)
+ priv->phy->set_cca_mode = mac802154_set_cca_mode;
+ if (priv->ops->set_cca_ed_level)
+ priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level;
+ if (priv->ops->set_csma_params)
+ priv->phy->set_csma_params = mac802154_set_csma_params;
+ if (priv->ops->set_frame_retries)
+ priv->phy->set_frame_retries = mac802154_set_frame_retries;
rc = wpan_phy_register(priv->phy);
if (rc < 0)
diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h
index d48422e27110..28ef59c566e6 100644
--- a/net/mac802154/mac802154.h
+++ b/net/mac802154/mac802154.h
@@ -23,6 +23,8 @@
#ifndef MAC802154_H
#define MAC802154_H
+#include <net/ieee802154_netdev.h>
+
/* mac802154 device private data */
struct mac802154_priv {
struct ieee802154_dev hw;
@@ -71,15 +73,19 @@ struct mac802154_sub_if_data {
struct net_device *dev;
int type;
+ bool running;
spinlock_t mib_lock;
__le16 pan_id;
__le16 short_addr;
+ __le64 extended_addr;
u8 chan;
u8 page;
+ struct ieee802154_mac_params mac_params;
+
/* MAC BSN field */
u8 bsn;
/* MAC DSN field */
@@ -106,12 +112,17 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
u8 page, u8 chan);
/* MIB callbacks */
-void mac802154_dev_set_short_addr(struct net_device *dev, u16 val);
-u16 mac802154_dev_get_short_addr(const struct net_device *dev);
+void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val);
+__le16 mac802154_dev_get_short_addr(const struct net_device *dev);
void mac802154_dev_set_ieee_addr(struct net_device *dev);
-u16 mac802154_dev_get_pan_id(const struct net_device *dev);
-void mac802154_dev_set_pan_id(struct net_device *dev, u16 val);
+__le16 mac802154_dev_get_pan_id(const struct net_device *dev);
+void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val);
void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan);
u8 mac802154_dev_get_dsn(const struct net_device *dev);
+int mac802154_set_mac_params(struct net_device *dev,
+ const struct ieee802154_mac_params *params);
+void mac802154_get_mac_params(struct net_device *dev,
+ struct ieee802154_mac_params *params);
+
#endif /* MAC802154_H */
diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
index a99910d4d52f..d40c0928bc62 100644
--- a/net/mac802154/mac_cmd.c
+++ b/net/mac802154/mac_cmd.c
@@ -40,7 +40,7 @@ static int mac802154_mlme_start_req(struct net_device *dev,
u8 pan_coord, u8 blx,
u8 coord_realign)
{
- BUG_ON(addr->addr_type != IEEE802154_ADDR_SHORT);
+ BUG_ON(addr->mode != IEEE802154_ADDR_SHORT);
mac802154_dev_set_pan_id(dev, addr->pan_id);
mac802154_dev_set_short_addr(dev, addr->short_addr);
@@ -74,4 +74,7 @@ struct ieee802154_mlme_ops mac802154_mlme_wpan = {
.get_pan_id = mac802154_dev_get_pan_id,
.get_short_addr = mac802154_dev_get_short_addr,
.get_dsn = mac802154_dev_get_dsn,
+
+ .set_mac_params = mac802154_set_mac_params,
+ .get_mac_params = mac802154_get_mac_params,
};
diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c
index 8ded97cf1c33..153bd1ddbfbb 100644
--- a/net/mac802154/mib.c
+++ b/net/mac802154/mib.c
@@ -24,7 +24,9 @@
#include <linux/if_arp.h>
#include <net/mac802154.h>
+#include <net/ieee802154_netdev.h>
#include <net/wpan-phy.h>
+#include <net/ieee802154_netdev.h>
#include "mac802154.h"
@@ -62,8 +64,6 @@ static void hw_addr_notify(struct work_struct *work)
pr_debug("failed changed mask %lx\n", nw->changed);
kfree(nw);
-
- return;
}
static void set_hw_addr_filt(struct net_device *dev, unsigned long changed)
@@ -79,11 +79,9 @@ static void set_hw_addr_filt(struct net_device *dev, unsigned long changed)
work->dev = dev;
work->changed = changed;
queue_work(priv->hw->dev_workqueue, &work->work);
-
- return;
}
-void mac802154_dev_set_short_addr(struct net_device *dev, u16 val)
+void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
@@ -100,10 +98,10 @@ void mac802154_dev_set_short_addr(struct net_device *dev, u16 val)
}
}
-u16 mac802154_dev_get_short_addr(const struct net_device *dev)
+__le16 mac802154_dev_get_short_addr(const struct net_device *dev)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
- u16 ret;
+ __le16 ret;
BUG_ON(dev->type != ARPHRD_IEEE802154);
@@ -119,19 +117,19 @@ void mac802154_dev_set_ieee_addr(struct net_device *dev)
struct mac802154_sub_if_data *priv = netdev_priv(dev);
struct mac802154_priv *mac = priv->hw;
+ priv->extended_addr = ieee802154_devaddr_from_raw(dev->dev_addr);
+
if (mac->ops->set_hw_addr_filt &&
- memcmp(mac->hw.hw_filt.ieee_addr,
- dev->dev_addr, IEEE802154_ADDR_LEN)) {
- memcpy(mac->hw.hw_filt.ieee_addr,
- dev->dev_addr, IEEE802154_ADDR_LEN);
+ mac->hw.hw_filt.ieee_addr != priv->extended_addr) {
+ mac->hw.hw_filt.ieee_addr = priv->extended_addr;
set_hw_addr_filt(dev, IEEE802515_AFILT_IEEEADDR_CHANGED);
}
}
-u16 mac802154_dev_get_pan_id(const struct net_device *dev)
+__le16 mac802154_dev_get_pan_id(const struct net_device *dev)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
- u16 ret;
+ __le16 ret;
BUG_ON(dev->type != ARPHRD_IEEE802154);
@@ -142,7 +140,7 @@ u16 mac802154_dev_get_pan_id(const struct net_device *dev)
return ret;
}
-void mac802154_dev_set_pan_id(struct net_device *dev, u16 val)
+void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
index 38548ec2098f..03855b0677cc 100644
--- a/net/mac802154/rx.c
+++ b/net/mac802154/rx.c
@@ -80,7 +80,6 @@ mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi)
mac802154_wpans_rx(priv, skb);
out:
dev_kfree_skb(skb);
- return;
}
static void mac802154_rx_worker(struct work_struct *work)
diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c
index 372d8a222b91..1df7a6a57386 100644
--- a/net/mac802154/wpan.c
+++ b/net/mac802154/wpan.c
@@ -35,35 +35,6 @@
#include "mac802154.h"
-static inline int mac802154_fetch_skb_u8(struct sk_buff *skb, u8 *val)
-{
- if (unlikely(!pskb_may_pull(skb, 1)))
- return -EINVAL;
-
- *val = skb->data[0];
- skb_pull(skb, 1);
-
- return 0;
-}
-
-static inline int mac802154_fetch_skb_u16(struct sk_buff *skb, u16 *val)
-{
- if (unlikely(!pskb_may_pull(skb, 2)))
- return -EINVAL;
-
- *val = skb->data[0] | (skb->data[1] << 8);
- skb_pull(skb, 2);
-
- return 0;
-}
-
-static inline void mac802154_haddr_copy_swap(u8 *dest, const u8 *src)
-{
- int i;
- for (i = 0; i < IEEE802154_ADDR_LEN; i++)
- dest[IEEE802154_ADDR_LEN - i - 1] = src[i];
-}
-
static int
mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
@@ -76,19 +47,25 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch (cmd) {
case SIOCGIFADDR:
- if (priv->pan_id == IEEE802154_PANID_BROADCAST ||
- priv->short_addr == IEEE802154_ADDR_BROADCAST) {
+ {
+ u16 pan_id, short_addr;
+
+ pan_id = le16_to_cpu(priv->pan_id);
+ short_addr = le16_to_cpu(priv->short_addr);
+ if (pan_id == IEEE802154_PANID_BROADCAST ||
+ short_addr == IEEE802154_ADDR_BROADCAST) {
err = -EADDRNOTAVAIL;
break;
}
sa->family = AF_IEEE802154;
sa->addr.addr_type = IEEE802154_ADDR_SHORT;
- sa->addr.pan_id = priv->pan_id;
- sa->addr.short_addr = priv->short_addr;
+ sa->addr.pan_id = pan_id;
+ sa->addr.short_addr = short_addr;
err = 0;
break;
+ }
case SIOCSIFADDR:
dev_warn(&dev->dev,
"Using DEBUGing ioctl SIOCSIFADDR isn't recommened!\n");
@@ -101,8 +78,8 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
}
- priv->pan_id = sa->addr.pan_id;
- priv->short_addr = sa->addr.short_addr;
+ priv->pan_id = cpu_to_le16(sa->addr.pan_id);
+ priv->short_addr = cpu_to_le16(sa->addr.short_addr);
err = 0;
break;
@@ -125,190 +102,154 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
return 0;
}
-static int mac802154_header_create(struct sk_buff *skb,
- struct net_device *dev,
- unsigned short type,
- const void *_daddr,
- const void *_saddr,
- unsigned len)
+int mac802154_set_mac_params(struct net_device *dev,
+ const struct ieee802154_mac_params *params)
{
- const struct ieee802154_addr *saddr = _saddr;
- const struct ieee802154_addr *daddr = _daddr;
- struct ieee802154_addr dev_addr;
struct mac802154_sub_if_data *priv = netdev_priv(dev);
- int pos = 2;
- u8 head[MAC802154_FRAME_HARD_HEADER_LEN];
- u16 fc;
- if (!daddr)
- return -EINVAL;
+ mutex_lock(&priv->hw->slaves_mtx);
+ priv->mac_params = *params;
+ mutex_unlock(&priv->hw->slaves_mtx);
- head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */
- fc = mac_cb_type(skb);
- if (mac_cb_is_ackreq(skb))
- fc |= IEEE802154_FC_ACK_REQ;
+ return 0;
+}
- if (!saddr) {
- spin_lock_bh(&priv->mib_lock);
+void mac802154_get_mac_params(struct net_device *dev,
+ struct ieee802154_mac_params *params)
+{
+ struct mac802154_sub_if_data *priv = netdev_priv(dev);
- if (priv->short_addr == IEEE802154_ADDR_BROADCAST ||
- priv->short_addr == IEEE802154_ADDR_UNDEF ||
- priv->pan_id == IEEE802154_PANID_BROADCAST) {
- dev_addr.addr_type = IEEE802154_ADDR_LONG;
- memcpy(dev_addr.hwaddr, dev->dev_addr,
- IEEE802154_ADDR_LEN);
- } else {
- dev_addr.addr_type = IEEE802154_ADDR_SHORT;
- dev_addr.short_addr = priv->short_addr;
- }
+ mutex_lock(&priv->hw->slaves_mtx);
+ *params = priv->mac_params;
+ mutex_unlock(&priv->hw->slaves_mtx);
+}
- dev_addr.pan_id = priv->pan_id;
- saddr = &dev_addr;
+int mac802154_wpan_open(struct net_device *dev)
+{
+ int rc;
+ struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct wpan_phy *phy = priv->hw->phy;
- spin_unlock_bh(&priv->mib_lock);
- }
+ rc = mac802154_slave_open(dev);
+ if (rc < 0)
+ return rc;
- if (daddr->addr_type != IEEE802154_ADDR_NONE) {
- fc |= (daddr->addr_type << IEEE802154_FC_DAMODE_SHIFT);
+ mutex_lock(&phy->pib_lock);
- head[pos++] = daddr->pan_id & 0xff;
- head[pos++] = daddr->pan_id >> 8;
+ if (phy->set_txpower) {
+ rc = phy->set_txpower(phy, priv->mac_params.transmit_power);
+ if (rc < 0)
+ goto out;
+ }
- if (daddr->addr_type == IEEE802154_ADDR_SHORT) {
- head[pos++] = daddr->short_addr & 0xff;
- head[pos++] = daddr->short_addr >> 8;
- } else {
- mac802154_haddr_copy_swap(head + pos, daddr->hwaddr);
- pos += IEEE802154_ADDR_LEN;
- }
+ if (phy->set_lbt) {
+ rc = phy->set_lbt(phy, priv->mac_params.lbt);
+ if (rc < 0)
+ goto out;
}
- if (saddr->addr_type != IEEE802154_ADDR_NONE) {
- fc |= (saddr->addr_type << IEEE802154_FC_SAMODE_SHIFT);
+ if (phy->set_cca_mode) {
+ rc = phy->set_cca_mode(phy, priv->mac_params.cca_mode);
+ if (rc < 0)
+ goto out;
+ }
- if ((saddr->pan_id == daddr->pan_id) &&
- (saddr->pan_id != IEEE802154_PANID_BROADCAST)) {
- /* PANID compression/intra PAN */
- fc |= IEEE802154_FC_INTRA_PAN;
- } else {
- head[pos++] = saddr->pan_id & 0xff;
- head[pos++] = saddr->pan_id >> 8;
- }
+ if (phy->set_cca_ed_level) {
+ rc = phy->set_cca_ed_level(phy, priv->mac_params.cca_ed_level);
+ if (rc < 0)
+ goto out;
+ }
- if (saddr->addr_type == IEEE802154_ADDR_SHORT) {
- head[pos++] = saddr->short_addr & 0xff;
- head[pos++] = saddr->short_addr >> 8;
- } else {
- mac802154_haddr_copy_swap(head + pos, saddr->hwaddr);
- pos += IEEE802154_ADDR_LEN;
- }
+ if (phy->set_csma_params) {
+ rc = phy->set_csma_params(phy, priv->mac_params.min_be,
+ priv->mac_params.max_be,
+ priv->mac_params.csma_retries);
+ if (rc < 0)
+ goto out;
}
- head[0] = fc;
- head[1] = fc >> 8;
+ if (phy->set_frame_retries) {
+ rc = phy->set_frame_retries(phy,
+ priv->mac_params.frame_retries);
+ if (rc < 0)
+ goto out;
+ }
- memcpy(skb_push(skb, pos), head, pos);
- skb_reset_mac_header(skb);
- skb->mac_len = pos;
+ mutex_unlock(&phy->pib_lock);
+ return 0;
- return pos;
+out:
+ mutex_unlock(&phy->pib_lock);
+ return rc;
}
-static int
-mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+static int mac802154_header_create(struct sk_buff *skb,
+ struct net_device *dev,
+ unsigned short type,
+ const void *daddr,
+ const void *saddr,
+ unsigned len)
{
- const u8 *hdr = skb_mac_header(skb);
- const u8 *tail = skb_tail_pointer(skb);
- struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
- u16 fc;
- int da_type;
-
- if (hdr + 3 > tail)
- goto malformed;
+ struct ieee802154_hdr hdr;
+ struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ int hlen;
- fc = hdr[0] | (hdr[1] << 8);
+ if (!daddr)
+ return -EINVAL;
- hdr += 3;
+ memset(&hdr.fc, 0, sizeof(hdr.fc));
+ hdr.fc.type = mac_cb_type(skb);
+ hdr.fc.security_enabled = mac_cb_is_secen(skb);
+ hdr.fc.ack_request = mac_cb_is_ackreq(skb);
- da_type = IEEE802154_FC_DAMODE(fc);
- addr->addr_type = IEEE802154_FC_SAMODE(fc);
+ if (!saddr) {
+ spin_lock_bh(&priv->mib_lock);
- switch (da_type) {
- case IEEE802154_ADDR_NONE:
- if (fc & IEEE802154_FC_INTRA_PAN)
- goto malformed;
- break;
- case IEEE802154_ADDR_LONG:
- if (fc & IEEE802154_FC_INTRA_PAN) {
- if (hdr + 2 > tail)
- goto malformed;
- addr->pan_id = hdr[0] | (hdr[1] << 8);
- hdr += 2;
+ if (priv->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
+ priv->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
+ priv->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
+ hdr.source.mode = IEEE802154_ADDR_LONG;
+ hdr.source.extended_addr = priv->extended_addr;
+ } else {
+ hdr.source.mode = IEEE802154_ADDR_SHORT;
+ hdr.source.short_addr = priv->short_addr;
}
- if (hdr + IEEE802154_ADDR_LEN > tail)
- goto malformed;
+ hdr.source.pan_id = priv->pan_id;
- hdr += IEEE802154_ADDR_LEN;
- break;
- case IEEE802154_ADDR_SHORT:
- if (fc & IEEE802154_FC_INTRA_PAN) {
- if (hdr + 2 > tail)
- goto malformed;
- addr->pan_id = hdr[0] | (hdr[1] << 8);
- hdr += 2;
- }
-
- if (hdr + 2 > tail)
- goto malformed;
+ spin_unlock_bh(&priv->mib_lock);
+ } else {
+ hdr.source = *(const struct ieee802154_addr *)saddr;
+ }
- hdr += 2;
- break;
- default:
- goto malformed;
+ hdr.dest = *(const struct ieee802154_addr *)daddr;
- }
+ hlen = ieee802154_hdr_push(skb, &hdr);
+ if (hlen < 0)
+ return -EINVAL;
- switch (addr->addr_type) {
- case IEEE802154_ADDR_NONE:
- break;
- case IEEE802154_ADDR_LONG:
- if (!(fc & IEEE802154_FC_INTRA_PAN)) {
- if (hdr + 2 > tail)
- goto malformed;
- addr->pan_id = hdr[0] | (hdr[1] << 8);
- hdr += 2;
- }
+ skb_reset_mac_header(skb);
+ skb->mac_len = hlen;
- if (hdr + IEEE802154_ADDR_LEN > tail)
- goto malformed;
+ if (hlen + len + 2 > dev->mtu)
+ return -EMSGSIZE;
- mac802154_haddr_copy_swap(addr->hwaddr, hdr);
- hdr += IEEE802154_ADDR_LEN;
- break;
- case IEEE802154_ADDR_SHORT:
- if (!(fc & IEEE802154_FC_INTRA_PAN)) {
- if (hdr + 2 > tail)
- goto malformed;
- addr->pan_id = hdr[0] | (hdr[1] << 8);
- hdr += 2;
- }
+ return hlen;
+}
- if (hdr + 2 > tail)
- goto malformed;
+static int
+mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+ struct ieee802154_hdr hdr;
+ struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
- addr->short_addr = hdr[0] | (hdr[1] << 8);
- hdr += 2;
- break;
- default:
- goto malformed;
+ if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
+ pr_debug("malformed packet\n");
+ return 0;
}
- return sizeof(struct ieee802154_addr);
-
-malformed:
- pr_debug("malformed packet\n");
- return 0;
+ *addr = hdr.source;
+ return sizeof(*addr);
}
static netdev_tx_t
@@ -344,7 +285,7 @@ static struct header_ops mac802154_header_ops = {
};
static const struct net_device_ops mac802154_wpan_ops = {
- .ndo_open = mac802154_slave_open,
+ .ndo_open = mac802154_wpan_open,
.ndo_stop = mac802154_slave_close,
.ndo_start_xmit = mac802154_wpan_xmit,
.ndo_do_ioctl = mac802154_wpan_ioctl,
@@ -382,8 +323,14 @@ void mac802154_wpan_setup(struct net_device *dev)
get_random_bytes(&priv->bsn, 1);
get_random_bytes(&priv->dsn, 1);
- priv->pan_id = IEEE802154_PANID_BROADCAST;
- priv->short_addr = IEEE802154_ADDR_BROADCAST;
+ /* defaults per 802.15.4-2011 */
+ priv->mac_params.min_be = 3;
+ priv->mac_params.max_be = 5;
+ priv->mac_params.csma_retries = 4;
+ priv->mac_params.frame_retries = -1; /* for compatibility, actual default is 3 */
+
+ priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
+ priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
}
static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb)
@@ -394,13 +341,18 @@ static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb)
static int
mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb)
{
+ __le16 span, sshort;
+
pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
spin_lock_bh(&sdata->mib_lock);
- switch (mac_cb(skb)->da.addr_type) {
+ span = sdata->pan_id;
+ sshort = sdata->short_addr;
+
+ switch (mac_cb(skb)->dest.mode) {
case IEEE802154_ADDR_NONE:
- if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE)
+ if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE)
/* FIXME: check if we are PAN coordinator */
skb->pkt_type = PACKET_OTHERHOST;
else
@@ -408,23 +360,22 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb)
skb->pkt_type = PACKET_HOST;
break;
case IEEE802154_ADDR_LONG:
- if (mac_cb(skb)->da.pan_id != sdata->pan_id &&
- mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST)
+ if (mac_cb(skb)->dest.pan_id != span &&
+ mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
skb->pkt_type = PACKET_OTHERHOST;
- else if (!memcmp(mac_cb(skb)->da.hwaddr, sdata->dev->dev_addr,
- IEEE802154_ADDR_LEN))
+ else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr)
skb->pkt_type = PACKET_HOST;
else
skb->pkt_type = PACKET_OTHERHOST;
break;
case IEEE802154_ADDR_SHORT:
- if (mac_cb(skb)->da.pan_id != sdata->pan_id &&
- mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST)
+ if (mac_cb(skb)->dest.pan_id != span &&
+ mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
skb->pkt_type = PACKET_OTHERHOST;
- else if (mac_cb(skb)->da.short_addr == sdata->short_addr)
+ else if (mac_cb(skb)->dest.short_addr == sshort)
skb->pkt_type = PACKET_HOST;
- else if (mac_cb(skb)->da.short_addr ==
- IEEE802154_ADDR_BROADCAST)
+ else if (mac_cb(skb)->dest.short_addr ==
+ cpu_to_le16(IEEE802154_ADDR_BROADCAST))
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_OTHERHOST;
@@ -451,88 +402,82 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb)
}
}
-static int mac802154_parse_frame_start(struct sk_buff *skb)
+static void mac802154_print_addr(const char *name,
+ const struct ieee802154_addr *addr)
{
- u8 *head = skb->data;
- u16 fc;
-
- if (mac802154_fetch_skb_u16(skb, &fc) ||
- mac802154_fetch_skb_u8(skb, &(mac_cb(skb)->seq)))
- goto err;
-
- pr_debug("fc: %04x dsn: %02x\n", fc, head[2]);
-
- mac_cb(skb)->flags = IEEE802154_FC_TYPE(fc);
- mac_cb(skb)->sa.addr_type = IEEE802154_FC_SAMODE(fc);
- mac_cb(skb)->da.addr_type = IEEE802154_FC_DAMODE(fc);
+ if (addr->mode == IEEE802154_ADDR_NONE)
+ pr_debug("%s not present\n", name);
- if (fc & IEEE802154_FC_INTRA_PAN)
- mac_cb(skb)->flags |= MAC_CB_FLAG_INTRAPAN;
+ pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id));
+ if (addr->mode == IEEE802154_ADDR_SHORT) {
+ pr_debug("%s is short: %04x\n", name,
+ le16_to_cpu(addr->short_addr));
+ } else {
+ u64 hw = swab64((__force u64) addr->extended_addr);
- if (mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE) {
- if (mac802154_fetch_skb_u16(skb, &(mac_cb(skb)->da.pan_id)))
- goto err;
-
- /* source PAN id compression */
- if (mac_cb_is_intrapan(skb))
- mac_cb(skb)->sa.pan_id = mac_cb(skb)->da.pan_id;
+ pr_debug("%s is hardware: %8phC\n", name, &hw);
+ }
+}
- pr_debug("dest PAN addr: %04x\n", mac_cb(skb)->da.pan_id);
+static int mac802154_parse_frame_start(struct sk_buff *skb)
+{
+ int hlen;
+ struct ieee802154_hdr hdr;
- if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_SHORT) {
- u16 *da = &(mac_cb(skb)->da.short_addr);
+ hlen = ieee802154_hdr_pull(skb, &hdr);
+ if (hlen < 0)
+ return -EINVAL;
- if (mac802154_fetch_skb_u16(skb, da))
- goto err;
+ skb->mac_len = hlen;
- pr_debug("destination address is short: %04x\n",
- mac_cb(skb)->da.short_addr);
- } else {
- if (!pskb_may_pull(skb, IEEE802154_ADDR_LEN))
- goto err;
+ pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr.fc),
+ hdr.seq);
- mac802154_haddr_copy_swap(mac_cb(skb)->da.hwaddr,
- skb->data);
- skb_pull(skb, IEEE802154_ADDR_LEN);
+ mac_cb(skb)->flags = hdr.fc.type;
- pr_debug("destination address is hardware\n");
- }
- }
+ if (hdr.fc.ack_request)
+ mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
+ if (hdr.fc.security_enabled)
+ mac_cb(skb)->flags |= MAC_CB_FLAG_SECEN;
- if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) {
- /* non PAN-compression, fetch source address id */
- if (!(mac_cb_is_intrapan(skb))) {
- u16 *sa_pan = &(mac_cb(skb)->sa.pan_id);
+ mac802154_print_addr("destination", &hdr.dest);
+ mac802154_print_addr("source", &hdr.source);
- if (mac802154_fetch_skb_u16(skb, sa_pan))
- goto err;
- }
+ mac_cb(skb)->source = hdr.source;
+ mac_cb(skb)->dest = hdr.dest;
- pr_debug("source PAN addr: %04x\n", mac_cb(skb)->da.pan_id);
+ if (hdr.fc.security_enabled) {
+ u64 key;
- if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_SHORT) {
- u16 *sa = &(mac_cb(skb)->sa.short_addr);
+ pr_debug("seclevel %i\n", hdr.sec.level);
- if (mac802154_fetch_skb_u16(skb, sa))
- goto err;
+ switch (hdr.sec.key_id_mode) {
+ case IEEE802154_SCF_KEY_IMPLICIT:
+ pr_debug("implicit key\n");
+ break;
- pr_debug("source address is short: %04x\n",
- mac_cb(skb)->sa.short_addr);
- } else {
- if (!pskb_may_pull(skb, IEEE802154_ADDR_LEN))
- goto err;
+ case IEEE802154_SCF_KEY_INDEX:
+ pr_debug("key %02x\n", hdr.sec.key_id);
+ break;
- mac802154_haddr_copy_swap(mac_cb(skb)->sa.hwaddr,
- skb->data);
- skb_pull(skb, IEEE802154_ADDR_LEN);
+ case IEEE802154_SCF_KEY_SHORT_INDEX:
+ pr_debug("key %04x:%04x %02x\n",
+ le32_to_cpu(hdr.sec.short_src) >> 16,
+ le32_to_cpu(hdr.sec.short_src) & 0xffff,
+ hdr.sec.key_id);
+ break;
- pr_debug("source address is hardware\n");
+ case IEEE802154_SCF_KEY_HW_INDEX:
+ key = swab64((__force u64) hdr.sec.extended_src);
+ pr_debug("key source %8phC %02x\n", &key,
+ hdr.sec.key_id);
+ break;
}
+
+ return -EINVAL;
}
return 0;
-err:
- return -EINVAL;
}
void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb)
diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig
index 44cd4f58adf0..2f7f5c32c6f9 100644
--- a/net/netfilter/ipset/Kconfig
+++ b/net/netfilter/ipset/Kconfig
@@ -61,6 +61,15 @@ config IP_SET_HASH_IP
To compile it as a module, choose M here. If unsure, say N.
+config IP_SET_HASH_IPMARK
+ tristate "hash:ip,mark set support"
+ depends on IP_SET
+ help
+ This option adds the hash:ip,mark set type support, by which one
+ can store IPv4/IPv6 address and mark pairs.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
config IP_SET_HASH_IPPORT
tristate "hash:ip,port set support"
depends on IP_SET
diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile
index 44b2d38476fa..231f10196cb9 100644
--- a/net/netfilter/ipset/Makefile
+++ b/net/netfilter/ipset/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_IP_SET_BITMAP_PORT) += ip_set_bitmap_port.o
# hash types
obj-$(CONFIG_IP_SET_HASH_IP) += ip_set_hash_ip.o
+obj-$(CONFIG_IP_SET_HASH_IPMARK) += ip_set_hash_ipmark.o
obj-$(CONFIG_IP_SET_HASH_IPPORT) += ip_set_hash_ipport.o
obj-$(CONFIG_IP_SET_HASH_IPPORTIP) += ip_set_hash_ipportip.o
obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index de770ec39e51..117208321f16 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -54,10 +54,10 @@ MODULE_DESCRIPTION("core IP set support");
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET);
/* When the nfnl mutex is held: */
-#define nfnl_dereference(p) \
+#define ip_set_dereference(p) \
rcu_dereference_protected(p, 1)
-#define nfnl_set(inst, id) \
- nfnl_dereference((inst)->ip_set_list)[id]
+#define ip_set(inst, id) \
+ ip_set_dereference((inst)->ip_set_list)[id]
/*
* The set types are implemented in modules and registered set types
@@ -368,6 +368,8 @@ ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], size_t len)
if (tb[IPSET_ATTR_CADT_FLAGS])
cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+ if (cadt_flags & IPSET_FLAG_WITH_FORCEADD)
+ set->flags |= IPSET_CREATE_FLAG_FORCEADD;
for (id = 0; id < IPSET_EXT_ID_MAX; id++) {
if (!add_extension(id, cadt_flags, tb))
continue;
@@ -510,7 +512,7 @@ ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
if (opt->dim < set->type->dimension ||
!(opt->family == set->family || set->family == NFPROTO_UNSPEC))
- return 0;
+ return -IPSET_ERR_TYPE_MISMATCH;
write_lock_bh(&set->lock);
ret = set->variant->kadt(set, skb, par, IPSET_ADD, opt);
@@ -533,7 +535,7 @@ ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
if (opt->dim < set->type->dimension ||
!(opt->family == set->family || set->family == NFPROTO_UNSPEC))
- return 0;
+ return -IPSET_ERR_TYPE_MISMATCH;
write_lock_bh(&set->lock);
ret = set->variant->kadt(set, skb, par, IPSET_DEL, opt);
@@ -640,7 +642,7 @@ ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index)
return IPSET_INVALID_ID;
nfnl_lock(NFNL_SUBSYS_IPSET);
- set = nfnl_set(inst, index);
+ set = ip_set(inst, index);
if (set)
__ip_set_get(set);
else
@@ -666,7 +668,7 @@ ip_set_nfnl_put(struct net *net, ip_set_id_t index)
nfnl_lock(NFNL_SUBSYS_IPSET);
if (!inst->is_deleted) { /* already deleted from ip_set_net_exit() */
- set = nfnl_set(inst, index);
+ set = ip_set(inst, index);
if (set != NULL)
__ip_set_put(set);
}
@@ -734,7 +736,7 @@ find_set_and_id(struct ip_set_net *inst, const char *name, ip_set_id_t *id)
*id = IPSET_INVALID_ID;
for (i = 0; i < inst->ip_set_max; i++) {
- set = nfnl_set(inst, i);
+ set = ip_set(inst, i);
if (set != NULL && STREQ(set->name, name)) {
*id = i;
break;
@@ -760,7 +762,7 @@ find_free_id(struct ip_set_net *inst, const char *name, ip_set_id_t *index,
*index = IPSET_INVALID_ID;
for (i = 0; i < inst->ip_set_max; i++) {
- s = nfnl_set(inst, i);
+ s = ip_set(inst, i);
if (s == NULL) {
if (*index == IPSET_INVALID_ID)
*index = i;
@@ -883,7 +885,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
if (!list)
goto cleanup;
/* nfnl mutex is held, both lists are valid */
- tmp = nfnl_dereference(inst->ip_set_list);
+ tmp = ip_set_dereference(inst->ip_set_list);
memcpy(list, tmp, sizeof(struct ip_set *) * inst->ip_set_max);
rcu_assign_pointer(inst->ip_set_list, list);
/* Make sure all current packets have passed through */
@@ -900,7 +902,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
* Finally! Add our shiny new set to the list, and be done.
*/
pr_debug("create: '%s' created with index %u!\n", set->name, index);
- nfnl_set(inst, index) = set;
+ ip_set(inst, index) = set;
return ret;
@@ -925,10 +927,10 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = {
static void
ip_set_destroy_set(struct ip_set_net *inst, ip_set_id_t index)
{
- struct ip_set *set = nfnl_set(inst, index);
+ struct ip_set *set = ip_set(inst, index);
pr_debug("set: %s\n", set->name);
- nfnl_set(inst, index) = NULL;
+ ip_set(inst, index) = NULL;
/* Must call it without holding any lock */
set->variant->destroy(set);
@@ -962,7 +964,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
read_lock_bh(&ip_set_ref_lock);
if (!attr[IPSET_ATTR_SETNAME]) {
for (i = 0; i < inst->ip_set_max; i++) {
- s = nfnl_set(inst, i);
+ s = ip_set(inst, i);
if (s != NULL && s->ref) {
ret = -IPSET_ERR_BUSY;
goto out;
@@ -970,7 +972,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
}
read_unlock_bh(&ip_set_ref_lock);
for (i = 0; i < inst->ip_set_max; i++) {
- s = nfnl_set(inst, i);
+ s = ip_set(inst, i);
if (s != NULL)
ip_set_destroy_set(inst, i);
}
@@ -1020,7 +1022,7 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb,
if (!attr[IPSET_ATTR_SETNAME]) {
for (i = 0; i < inst->ip_set_max; i++) {
- s = nfnl_set(inst, i);
+ s = ip_set(inst, i);
if (s != NULL)
ip_set_flush_set(s);
}
@@ -1074,7 +1076,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
name2 = nla_data(attr[IPSET_ATTR_SETNAME2]);
for (i = 0; i < inst->ip_set_max; i++) {
- s = nfnl_set(inst, i);
+ s = ip_set(inst, i);
if (s != NULL && STREQ(s->name, name2)) {
ret = -IPSET_ERR_EXIST_SETNAME2;
goto out;
@@ -1134,8 +1136,8 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
write_lock_bh(&ip_set_ref_lock);
swap(from->ref, to->ref);
- nfnl_set(inst, from_id) = to;
- nfnl_set(inst, to_id) = from;
+ ip_set(inst, from_id) = to;
+ ip_set(inst, to_id) = from;
write_unlock_bh(&ip_set_ref_lock);
return 0;
@@ -1157,7 +1159,7 @@ ip_set_dump_done(struct netlink_callback *cb)
struct ip_set_net *inst = (struct ip_set_net *)cb->args[IPSET_CB_NET];
if (cb->args[IPSET_CB_ARG0]) {
pr_debug("release set %s\n",
- nfnl_set(inst, cb->args[IPSET_CB_INDEX])->name);
+ ip_set(inst, cb->args[IPSET_CB_INDEX])->name);
__ip_set_put_byindex(inst,
(ip_set_id_t) cb->args[IPSET_CB_INDEX]);
}
@@ -1254,7 +1256,7 @@ dump_last:
dump_type, dump_flags, cb->args[IPSET_CB_INDEX]);
for (; cb->args[IPSET_CB_INDEX] < max; cb->args[IPSET_CB_INDEX]++) {
index = (ip_set_id_t) cb->args[IPSET_CB_INDEX];
- set = nfnl_set(inst, index);
+ set = ip_set(inst, index);
if (set == NULL) {
if (dump_type == DUMP_ONE) {
ret = -ENOENT;
@@ -1332,7 +1334,7 @@ next_set:
release_refcount:
/* If there was an error or set is done, release set */
if (ret || !cb->args[IPSET_CB_ARG0]) {
- pr_debug("release set %s\n", nfnl_set(inst, index)->name);
+ pr_debug("release set %s\n", ip_set(inst, index)->name);
__ip_set_put_byindex(inst, index);
cb->args[IPSET_CB_ARG0] = 0;
}
@@ -1887,7 +1889,7 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
find_set_and_id(inst, req_get->set.name, &id);
req_get->set.index = id;
if (id != IPSET_INVALID_ID)
- req_get->family = nfnl_set(inst, id)->family;
+ req_get->family = ip_set(inst, id)->family;
nfnl_unlock(NFNL_SUBSYS_IPSET);
goto copy;
}
@@ -1901,7 +1903,7 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
goto done;
}
nfnl_lock(NFNL_SUBSYS_IPSET);
- set = nfnl_set(inst, req_get->set.index);
+ set = ip_set(inst, req_get->set.index);
strncpy(req_get->set.name, set ? set->name : "",
IPSET_MAXNAMELEN);
nfnl_unlock(NFNL_SUBSYS_IPSET);
@@ -1945,7 +1947,6 @@ ip_set_net_init(struct net *net)
return -ENOMEM;
inst->is_deleted = 0;
rcu_assign_pointer(inst->ip_set_list, list);
- pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL);
return 0;
}
@@ -1960,7 +1961,7 @@ ip_set_net_exit(struct net *net)
inst->is_deleted = 1; /* flag for ip_set_nfnl_put */
for (i = 0; i < inst->ip_set_max; i++) {
- set = nfnl_set(inst, i);
+ set = ip_set(inst, i);
if (set != NULL)
ip_set_destroy_set(inst, i);
}
@@ -1996,6 +1997,7 @@ ip_set_init(void)
nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
return ret;
}
+ pr_info("ip_set: protocol %u\n", IPSET_PROTOCOL);
return 0;
}
diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h
index be6932ad3a86..61c7fb052802 100644
--- a/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/net/netfilter/ipset/ip_set_hash_gen.h
@@ -263,6 +263,9 @@ struct htype {
u32 maxelem; /* max elements in the hash */
u32 elements; /* current element (vs timeout) */
u32 initval; /* random jhash init value */
+#ifdef IP_SET_HASH_WITH_MARKMASK
+ u32 markmask; /* markmask value for mark mask to store */
+#endif
struct timer_list gc; /* garbage collection when timeout enabled */
struct mtype_elem next; /* temporary storage for uadd */
#ifdef IP_SET_HASH_WITH_MULTI
@@ -454,6 +457,9 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b)
#ifdef IP_SET_HASH_WITH_NETMASK
x->netmask == y->netmask &&
#endif
+#ifdef IP_SET_HASH_WITH_MARKMASK
+ x->markmask == y->markmask &&
+#endif
a->extensions == b->extensions;
}
@@ -627,6 +633,18 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
bool flag_exist = flags & IPSET_FLAG_EXIST;
u32 key, multi = 0;
+ if (h->elements >= h->maxelem && SET_WITH_FORCEADD(set)) {
+ rcu_read_lock_bh();
+ t = rcu_dereference_bh(h->table);
+ key = HKEY(value, h->initval, t->htable_bits);
+ n = hbucket(t,key);
+ if (n->pos) {
+ /* Choosing the first entry in the array to replace */
+ j = 0;
+ goto reuse_slot;
+ }
+ rcu_read_unlock_bh();
+ }
if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem)
/* FIXME: when set is full, we slow down here */
mtype_expire(set, h, NLEN(set->family), set->dsize);
@@ -908,6 +926,10 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask))
goto nla_put_failure;
#endif
+#ifdef IP_SET_HASH_WITH_MARKMASK
+ if (nla_put_u32(skb, IPSET_ATTR_MARKMASK, h->markmask))
+ goto nla_put_failure;
+#endif
if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)))
goto nla_put_failure;
@@ -1016,6 +1038,9 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
struct nlattr *tb[], u32 flags)
{
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+#ifdef IP_SET_HASH_WITH_MARKMASK
+ u32 markmask;
+#endif
u8 hbits;
#ifdef IP_SET_HASH_WITH_NETMASK
u8 netmask;
@@ -1026,6 +1051,10 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
+
+#ifdef IP_SET_HASH_WITH_MARKMASK
+ markmask = 0xffffffff;
+#endif
#ifdef IP_SET_HASH_WITH_NETMASK
netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
pr_debug("Create set %s with family %s\n",
@@ -1034,6 +1063,9 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
+#ifdef IP_SET_HASH_WITH_MARKMASK
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_MARKMASK) ||
+#endif
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
@@ -1057,6 +1089,14 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
return -IPSET_ERR_INVALID_NETMASK;
}
#endif
+#ifdef IP_SET_HASH_WITH_MARKMASK
+ if (tb[IPSET_ATTR_MARKMASK]) {
+ markmask = ntohl(nla_get_u32(tb[IPSET_ATTR_MARKMASK]));
+
+ if ((markmask > 4294967295u) || markmask == 0)
+ return -IPSET_ERR_INVALID_MARKMASK;
+ }
+#endif
hsize = sizeof(*h);
#ifdef IP_SET_HASH_WITH_NETS
@@ -1071,6 +1111,9 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
#ifdef IP_SET_HASH_WITH_NETMASK
h->netmask = netmask;
#endif
+#ifdef IP_SET_HASH_WITH_MARKMASK
+ h->markmask = markmask;
+#endif
get_random_bytes(&h->initval, sizeof(h->initval));
set->timeout = IPSET_NO_TIMEOUT;
diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c
index e65fc2423d56..dd40607f878e 100644
--- a/net/netfilter/ipset/ip_set_hash_ip.c
+++ b/net/netfilter/ipset/ip_set_hash_ip.c
@@ -25,7 +25,8 @@
#define IPSET_TYPE_REV_MIN 0
/* 1 Counters support */
-#define IPSET_TYPE_REV_MAX 2 /* Comments support */
+/* 2 Comments support */
+#define IPSET_TYPE_REV_MAX 3 /* Forceadd support */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
diff --git a/net/netfilter/ipset/ip_set_hash_ipmark.c b/net/netfilter/ipset/ip_set_hash_ipmark.c
new file mode 100644
index 000000000000..4eff0a297254
--- /dev/null
+++ b/net/netfilter/ipset/ip_set_hash_ipmark.c
@@ -0,0 +1,321 @@
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * Copyright (C) 2013 Smoothwall Ltd. <vytas.dauksa@smoothwall.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.
+ */
+
+/* Kernel module implementing an IP set type: the hash:ip,mark type */
+
+#include <linux/jhash.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/netlink.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/ipset/pfxlen.h>
+#include <linux/netfilter/ipset/ip_set.h>
+#include <linux/netfilter/ipset/ip_set_hash.h>
+
+#define IPSET_TYPE_REV_MIN 0
+#define IPSET_TYPE_REV_MAX 1 /* Forceadd support */
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vytas Dauksa <vytas.dauksa@smoothwall.net>");
+IP_SET_MODULE_DESC("hash:ip,mark", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
+MODULE_ALIAS("ip_set_hash:ip,mark");
+
+/* Type specific function prefix */
+#define HTYPE hash_ipmark
+#define IP_SET_HASH_WITH_MARKMASK
+
+/* IPv4 variant */
+
+/* Member elements */
+struct hash_ipmark4_elem {
+ __be32 ip;
+ __u32 mark;
+};
+
+/* Common functions */
+
+static inline bool
+hash_ipmark4_data_equal(const struct hash_ipmark4_elem *ip1,
+ const struct hash_ipmark4_elem *ip2,
+ u32 *multi)
+{
+ return ip1->ip == ip2->ip &&
+ ip1->mark == ip2->mark;
+}
+
+static bool
+hash_ipmark4_data_list(struct sk_buff *skb,
+ const struct hash_ipmark4_elem *data)
+{
+ if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) ||
+ nla_put_net32(skb, IPSET_ATTR_MARK, htonl(data->mark)))
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ return 1;
+}
+
+static inline void
+hash_ipmark4_data_next(struct hash_ipmark4_elem *next,
+ const struct hash_ipmark4_elem *d)
+{
+ next->ip = d->ip;
+}
+
+#define MTYPE hash_ipmark4
+#define PF 4
+#define HOST_MASK 32
+#define HKEY_DATALEN sizeof(struct hash_ipmark4_elem)
+#include "ip_set_hash_gen.h"
+
+static int
+hash_ipmark4_kadt(struct ip_set *set, const struct sk_buff *skb,
+ const struct xt_action_param *par,
+ enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+ const struct hash_ipmark *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipmark4_elem e = { };
+ struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+
+ e.mark = skb->mark;
+ e.mark &= h->markmask;
+
+ ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
+ return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
+}
+
+static int
+hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
+{
+ const struct hash_ipmark *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipmark4_elem e = { };
+ struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+ u32 ip, ip_to = 0;
+ int ret;
+
+ if (unlikely(!tb[IPSET_ATTR_IP] ||
+ !ip_set_attr_netorder(tb, IPSET_ATTR_MARK) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
+ ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &e.ip) ||
+ ip_set_get_extensions(set, tb, &ext);
+ if (ret)
+ return ret;
+
+ e.mark = ntohl(nla_get_u32(tb[IPSET_ATTR_MARK]));
+ e.mark &= h->markmask;
+
+ if (adt == IPSET_TEST ||
+ !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR])) {
+ ret = adtfn(set, &e, &ext, &ext, flags);
+ return ip_set_eexist(ret, flags) ? 0 : ret;
+ }
+
+ ip_to = ip = ntohl(e.ip);
+ if (tb[IPSET_ATTR_IP_TO]) {
+ ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
+ if (ret)
+ return ret;
+ if (ip > ip_to)
+ swap(ip, ip_to);
+ } else if (tb[IPSET_ATTR_CIDR]) {
+ u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+
+ if (!cidr || cidr > 32)
+ return -IPSET_ERR_INVALID_CIDR;
+ ip_set_mask_from_to(ip, ip_to, cidr);
+ }
+
+ if (retried)
+ ip = ntohl(h->next.ip);
+ for (; !before(ip_to, ip); ip++) {
+ e.ip = htonl(ip);
+ ret = adtfn(set, &e, &ext, &ext, flags);
+
+ if (ret && !ip_set_eexist(ret, flags))
+ return ret;
+ else
+ ret = 0;
+ }
+ return ret;
+}
+
+/* IPv6 variant */
+
+struct hash_ipmark6_elem {
+ union nf_inet_addr ip;
+ __u32 mark;
+};
+
+/* Common functions */
+
+static inline bool
+hash_ipmark6_data_equal(const struct hash_ipmark6_elem *ip1,
+ const struct hash_ipmark6_elem *ip2,
+ u32 *multi)
+{
+ return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) &&
+ ip1->mark == ip2->mark;
+}
+
+static bool
+hash_ipmark6_data_list(struct sk_buff *skb,
+ const struct hash_ipmark6_elem *data)
+{
+ if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) ||
+ nla_put_net32(skb, IPSET_ATTR_MARK, htonl(data->mark)))
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ return 1;
+}
+
+static inline void
+hash_ipmark6_data_next(struct hash_ipmark4_elem *next,
+ const struct hash_ipmark6_elem *d)
+{
+}
+
+#undef MTYPE
+#undef PF
+#undef HOST_MASK
+#undef HKEY_DATALEN
+
+#define MTYPE hash_ipmark6
+#define PF 6
+#define HOST_MASK 128
+#define HKEY_DATALEN sizeof(struct hash_ipmark6_elem)
+#define IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
+
+
+static int
+hash_ipmark6_kadt(struct ip_set *set, const struct sk_buff *skb,
+ const struct xt_action_param *par,
+ enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+ const struct hash_ipmark *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipmark6_elem e = { };
+ struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+
+ e.mark = skb->mark;
+ e.mark &= h->markmask;
+
+ ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
+ return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
+}
+
+static int
+hash_ipmark6_uadt(struct ip_set *set, struct nlattr *tb[],
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
+{
+ const struct hash_ipmark *h = set->data;
+ ipset_adtfn adtfn = set->variant->adt[adt];
+ struct hash_ipmark6_elem e = { };
+ struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+ int ret;
+
+ if (unlikely(!tb[IPSET_ATTR_IP] ||
+ !ip_set_attr_netorder(tb, IPSET_ATTR_MARK) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) ||
+ tb[IPSET_ATTR_IP_TO] ||
+ tb[IPSET_ATTR_CIDR]))
+ return -IPSET_ERR_PROTOCOL;
+
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
+ ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
+ ip_set_get_extensions(set, tb, &ext);
+ if (ret)
+ return ret;
+
+ e.mark = ntohl(nla_get_u32(tb[IPSET_ATTR_MARK]));
+ e.mark &= h->markmask;
+
+ if (adt == IPSET_TEST) {
+ ret = adtfn(set, &e, &ext, &ext, flags);
+ return ip_set_eexist(ret, flags) ? 0 : ret;
+ }
+
+ ret = adtfn(set, &e, &ext, &ext, flags);
+ if (ret && !ip_set_eexist(ret, flags))
+ return ret;
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static struct ip_set_type hash_ipmark_type __read_mostly = {
+ .name = "hash:ip,mark",
+ .protocol = IPSET_PROTOCOL,
+ .features = IPSET_TYPE_IP | IPSET_TYPE_MARK,
+ .dimension = IPSET_DIM_TWO,
+ .family = NFPROTO_UNSPEC,
+ .revision_min = IPSET_TYPE_REV_MIN,
+ .revision_max = IPSET_TYPE_REV_MAX,
+ .create = hash_ipmark_create,
+ .create_policy = {
+ [IPSET_ATTR_MARKMASK] = { .type = NLA_U32 },
+ [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
+ [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
+ [IPSET_ATTR_PROBES] = { .type = NLA_U8 },
+ [IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+ },
+ .adt_policy = {
+ [IPSET_ATTR_IP] = { .type = NLA_NESTED },
+ [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
+ [IPSET_ATTR_MARK] = { .type = NLA_U32 },
+ [IPSET_ATTR_CIDR] = { .type = NLA_U8 },
+ [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
+ [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
+ [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
+ [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING },
+ },
+ .me = THIS_MODULE,
+};
+
+static int __init
+hash_ipmark_init(void)
+{
+ return ip_set_type_register(&hash_ipmark_type);
+}
+
+static void __exit
+hash_ipmark_fini(void)
+{
+ ip_set_type_unregister(&hash_ipmark_type);
+}
+
+module_init(hash_ipmark_init);
+module_exit(hash_ipmark_fini);
diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c
index 525a595dd1fe..7597b82a8b03 100644
--- a/net/netfilter/ipset/ip_set_hash_ipport.c
+++ b/net/netfilter/ipset/ip_set_hash_ipport.c
@@ -27,7 +27,8 @@
#define IPSET_TYPE_REV_MIN 0
/* 1 SCTP and UDPLITE support added */
/* 2 Counters support added */
-#define IPSET_TYPE_REV_MAX 3 /* Comments support added */
+/* 3 Comments support added */
+#define IPSET_TYPE_REV_MAX 4 /* Forceadd support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c
index f5636631466e..672655ffd573 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportip.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportip.c
@@ -27,7 +27,8 @@
#define IPSET_TYPE_REV_MIN 0
/* 1 SCTP and UDPLITE support added */
/* 2 Counters support added */
-#define IPSET_TYPE_REV_MAX 3 /* Comments support added */
+/* 3 Comments support added */
+#define IPSET_TYPE_REV_MAX 4 /* Forceadd support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c
index 5d87fe8a41ff..7308d84f9277 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -29,7 +29,8 @@
/* 2 Range as input support for IPv4 added */
/* 3 nomatch flag support added */
/* 4 Counters support added */
-#define IPSET_TYPE_REV_MAX 5 /* Comments support added */
+/* 5 Comments support added */
+#define IPSET_TYPE_REV_MAX 6 /* Forceadd support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c
index 8295cf4f9fdc..4c7d495783a3 100644
--- a/net/netfilter/ipset/ip_set_hash_net.c
+++ b/net/netfilter/ipset/ip_set_hash_net.c
@@ -26,7 +26,8 @@
/* 1 Range as input support for IPv4 added */
/* 2 nomatch flag support added */
/* 3 Counters support added */
-#define IPSET_TYPE_REV_MAX 4 /* Comments support added */
+/* 4 Comments support added */
+#define IPSET_TYPE_REV_MAX 5 /* Forceadd support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c
index b827a0f1f351..db2606805b35 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -27,7 +27,8 @@
/* 1 nomatch flag support added */
/* 2 /0 support added */
/* 3 Counters support added */
-#define IPSET_TYPE_REV_MAX 4 /* Comments support added */
+/* 4 Comments support added */
+#define IPSET_TYPE_REV_MAX 5 /* Forceadd support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c
index 6226803fc490..3e99987e4bf2 100644
--- a/net/netfilter/ipset/ip_set_hash_netnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netnet.c
@@ -24,7 +24,7 @@
#include <linux/netfilter/ipset/ip_set_hash.h>
#define IPSET_TYPE_REV_MIN 0
-#define IPSET_TYPE_REV_MAX 0
+#define IPSET_TYPE_REV_MAX 1 /* Forceadd support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
@@ -112,10 +112,10 @@ hash_netnet4_data_list(struct sk_buff *skb,
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
- return 0;
+ return false;
nla_put_failure:
- return 1;
+ return true;
}
static inline void
@@ -334,10 +334,10 @@ hash_netnet6_data_list(struct sk_buff *skb,
(flags &&
nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
goto nla_put_failure;
- return 0;
+ return false;
nla_put_failure:
- return 1;
+ return true;
}
static inline void
diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c
index 7097fb0141bf..1c645fbd09c7 100644
--- a/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/net/netfilter/ipset/ip_set_hash_netport.c
@@ -28,7 +28,8 @@
/* 2 Range as input support for IPv4 added */
/* 3 nomatch flag support added */
/* 4 Counters support added */
-#define IPSET_TYPE_REV_MAX 5 /* Comments support added */
+/* 5 Comments support added */
+#define IPSET_TYPE_REV_MAX 6 /* Forceadd support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c
index 703d1192a6a2..c0d2ba73f8b2 100644
--- a/net/netfilter/ipset/ip_set_hash_netportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netportnet.c
@@ -25,7 +25,8 @@
#include <linux/netfilter/ipset/ip_set_hash.h>
#define IPSET_TYPE_REV_MIN 0
-#define IPSET_TYPE_REV_MAX 0 /* Comments support added */
+/* 0 Comments support added */
+#define IPSET_TYPE_REV_MAX 1 /* Forceadd support added */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
diff --git a/net/netfilter/ipset/pfxlen.c b/net/netfilter/ipset/pfxlen.c
index 4f29fa97044b..04d15fdc99ee 100644
--- a/net/netfilter/ipset/pfxlen.c
+++ b/net/netfilter/ipset/pfxlen.c
@@ -7,8 +7,8 @@
#define E(a, b, c, d) \
{.ip6 = { \
- __constant_htonl(a), __constant_htonl(b), \
- __constant_htonl(c), __constant_htonl(d), \
+ htonl(a), htonl(b), \
+ htonl(c), htonl(d), \
} }
/*
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 35be035ee0ce..c42e83d2751c 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -2177,10 +2177,10 @@ static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v)
__u64 inbytes, outbytes;
do {
- start = u64_stats_fetch_begin_bh(&u->syncp);
+ start = u64_stats_fetch_begin_irq(&u->syncp);
inbytes = u->ustats.inbytes;
outbytes = u->ustats.outbytes;
- } while (u64_stats_fetch_retry_bh(&u->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&u->syncp, start));
seq_printf(seq, "%3X %8X %8X %8X %16LX %16LX\n",
i, u->ustats.conns, u->ustats.inpkts,
@@ -3580,7 +3580,7 @@ out:
}
-static const struct genl_ops ip_vs_genl_ops[] __read_mostly = {
+static const struct genl_ops ip_vs_genl_ops[] = {
{
.cmd = IPVS_CMD_NEW_SERVICE,
.flags = GENL_ADMIN_PERM,
diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c
index ca056a331e60..547ff33c1efd 100644
--- a/net/netfilter/ipvs/ip_vs_lblc.c
+++ b/net/netfilter/ipvs/ip_vs_lblc.c
@@ -238,7 +238,7 @@ static void ip_vs_lblc_flush(struct ip_vs_service *svc)
spin_lock_bh(&svc->sched_lock);
tbl->dead = 1;
- for (i=0; i<IP_VS_LBLC_TAB_SIZE; i++) {
+ for (i = 0; i < IP_VS_LBLC_TAB_SIZE; i++) {
hlist_for_each_entry_safe(en, next, &tbl->bucket[i], list) {
ip_vs_lblc_del(en);
atomic_dec(&tbl->entries);
@@ -265,7 +265,7 @@ static inline void ip_vs_lblc_full_check(struct ip_vs_service *svc)
unsigned long now = jiffies;
int i, j;
- for (i=0, j=tbl->rover; i<IP_VS_LBLC_TAB_SIZE; i++) {
+ for (i = 0, j = tbl->rover; i < IP_VS_LBLC_TAB_SIZE; i++) {
j = (j + 1) & IP_VS_LBLC_TAB_MASK;
spin_lock(&svc->sched_lock);
@@ -321,7 +321,7 @@ static void ip_vs_lblc_check_expire(unsigned long data)
if (goal > tbl->max_size/2)
goal = tbl->max_size/2;
- for (i=0, j=tbl->rover; i<IP_VS_LBLC_TAB_SIZE; i++) {
+ for (i = 0, j = tbl->rover; i < IP_VS_LBLC_TAB_SIZE; i++) {
j = (j + 1) & IP_VS_LBLC_TAB_MASK;
spin_lock(&svc->sched_lock);
@@ -340,7 +340,7 @@ static void ip_vs_lblc_check_expire(unsigned long data)
tbl->rover = j;
out:
- mod_timer(&tbl->periodic_timer, jiffies+CHECK_EXPIRE_INTERVAL);
+ mod_timer(&tbl->periodic_timer, jiffies + CHECK_EXPIRE_INTERVAL);
}
@@ -363,7 +363,7 @@ static int ip_vs_lblc_init_svc(struct ip_vs_service *svc)
/*
* Initialize the hash buckets
*/
- for (i=0; i<IP_VS_LBLC_TAB_SIZE; i++) {
+ for (i = 0; i < IP_VS_LBLC_TAB_SIZE; i++) {
INIT_HLIST_HEAD(&tbl->bucket[i]);
}
tbl->max_size = IP_VS_LBLC_TAB_SIZE*16;
@@ -536,8 +536,7 @@ out:
/*
* IPVS LBLC Scheduler structure
*/
-static struct ip_vs_scheduler ip_vs_lblc_scheduler =
-{
+static struct ip_vs_scheduler ip_vs_lblc_scheduler = {
.name = "lblc",
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 356bef519fe5..6dba48efe01e 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -60,8 +60,59 @@ int (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct,
const struct nlattr *attr) __read_mostly;
EXPORT_SYMBOL_GPL(nfnetlink_parse_nat_setup_hook);
-DEFINE_SPINLOCK(nf_conntrack_lock);
-EXPORT_SYMBOL_GPL(nf_conntrack_lock);
+__cacheline_aligned_in_smp spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS];
+EXPORT_SYMBOL_GPL(nf_conntrack_locks);
+
+__cacheline_aligned_in_smp DEFINE_SPINLOCK(nf_conntrack_expect_lock);
+EXPORT_SYMBOL_GPL(nf_conntrack_expect_lock);
+
+static void nf_conntrack_double_unlock(unsigned int h1, unsigned int h2)
+{
+ h1 %= CONNTRACK_LOCKS;
+ h2 %= CONNTRACK_LOCKS;
+ spin_unlock(&nf_conntrack_locks[h1]);
+ if (h1 != h2)
+ spin_unlock(&nf_conntrack_locks[h2]);
+}
+
+/* return true if we need to recompute hashes (in case hash table was resized) */
+static bool nf_conntrack_double_lock(struct net *net, unsigned int h1,
+ unsigned int h2, unsigned int sequence)
+{
+ h1 %= CONNTRACK_LOCKS;
+ h2 %= CONNTRACK_LOCKS;
+ if (h1 <= h2) {
+ spin_lock(&nf_conntrack_locks[h1]);
+ if (h1 != h2)
+ spin_lock_nested(&nf_conntrack_locks[h2],
+ SINGLE_DEPTH_NESTING);
+ } else {
+ spin_lock(&nf_conntrack_locks[h2]);
+ spin_lock_nested(&nf_conntrack_locks[h1],
+ SINGLE_DEPTH_NESTING);
+ }
+ if (read_seqcount_retry(&net->ct.generation, sequence)) {
+ nf_conntrack_double_unlock(h1, h2);
+ return true;
+ }
+ return false;
+}
+
+static void nf_conntrack_all_lock(void)
+{
+ int i;
+
+ for (i = 0; i < CONNTRACK_LOCKS; i++)
+ spin_lock_nested(&nf_conntrack_locks[i], i);
+}
+
+static void nf_conntrack_all_unlock(void)
+{
+ int i;
+
+ for (i = 0; i < CONNTRACK_LOCKS; i++)
+ spin_unlock(&nf_conntrack_locks[i]);
+}
unsigned int nf_conntrack_htable_size __read_mostly;
EXPORT_SYMBOL_GPL(nf_conntrack_htable_size);
@@ -192,6 +243,50 @@ clean_from_lists(struct nf_conn *ct)
nf_ct_remove_expectations(ct);
}
+/* must be called with local_bh_disable */
+static void nf_ct_add_to_dying_list(struct nf_conn *ct)
+{
+ struct ct_pcpu *pcpu;
+
+ /* add this conntrack to the (per cpu) dying list */
+ ct->cpu = smp_processor_id();
+ pcpu = per_cpu_ptr(nf_ct_net(ct)->ct.pcpu_lists, ct->cpu);
+
+ spin_lock(&pcpu->lock);
+ hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
+ &pcpu->dying);
+ spin_unlock(&pcpu->lock);
+}
+
+/* must be called with local_bh_disable */
+static void nf_ct_add_to_unconfirmed_list(struct nf_conn *ct)
+{
+ struct ct_pcpu *pcpu;
+
+ /* add this conntrack to the (per cpu) unconfirmed list */
+ ct->cpu = smp_processor_id();
+ pcpu = per_cpu_ptr(nf_ct_net(ct)->ct.pcpu_lists, ct->cpu);
+
+ spin_lock(&pcpu->lock);
+ hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
+ &pcpu->unconfirmed);
+ spin_unlock(&pcpu->lock);
+}
+
+/* must be called with local_bh_disable */
+static void nf_ct_del_from_dying_or_unconfirmed_list(struct nf_conn *ct)
+{
+ struct ct_pcpu *pcpu;
+
+ /* We overload first tuple to link into unconfirmed or dying list.*/
+ pcpu = per_cpu_ptr(nf_ct_net(ct)->ct.pcpu_lists, ct->cpu);
+
+ spin_lock(&pcpu->lock);
+ BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode));
+ hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
+ spin_unlock(&pcpu->lock);
+}
+
static void
destroy_conntrack(struct nf_conntrack *nfct)
{
@@ -203,9 +298,6 @@ destroy_conntrack(struct nf_conntrack *nfct)
NF_CT_ASSERT(atomic_read(&nfct->use) == 0);
NF_CT_ASSERT(!timer_pending(&ct->timeout));
- /* To make sure we don't get any weird locking issues here:
- * destroy_conntrack() MUST NOT be called with a write lock
- * to nf_conntrack_lock!!! -HW */
rcu_read_lock();
l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
if (l4proto && l4proto->destroy)
@@ -213,19 +305,18 @@ destroy_conntrack(struct nf_conntrack *nfct)
rcu_read_unlock();
- spin_lock_bh(&nf_conntrack_lock);
+ local_bh_disable();
/* Expectations will have been removed in clean_from_lists,
* except TFTP can create an expectation on the first packet,
* before connection is in the list, so we need to clean here,
- * too. */
+ * too.
+ */
nf_ct_remove_expectations(ct);
- /* We overload first tuple to link into unconfirmed or dying list.*/
- BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode));
- hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
+ nf_ct_del_from_dying_or_unconfirmed_list(ct);
NF_CT_STAT_INC(net, delete);
- spin_unlock_bh(&nf_conntrack_lock);
+ local_bh_enable();
if (ct->master)
nf_ct_put(ct->master);
@@ -237,17 +328,28 @@ destroy_conntrack(struct nf_conntrack *nfct)
static void nf_ct_delete_from_lists(struct nf_conn *ct)
{
struct net *net = nf_ct_net(ct);
+ unsigned int hash, reply_hash;
+ u16 zone = nf_ct_zone(ct);
+ unsigned int sequence;
nf_ct_helper_destroy(ct);
- spin_lock_bh(&nf_conntrack_lock);
- /* Inside lock so preempt is disabled on module removal path.
- * Otherwise we can get spurious warnings. */
- NF_CT_STAT_INC(net, delete_list);
+
+ local_bh_disable();
+ do {
+ sequence = read_seqcount_begin(&net->ct.generation);
+ hash = hash_conntrack(net, zone,
+ &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+ reply_hash = hash_conntrack(net, zone,
+ &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+ } while (nf_conntrack_double_lock(net, hash, reply_hash, sequence));
+
clean_from_lists(ct);
- /* add this conntrack to the dying list */
- hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
- &net->ct.dying);
- spin_unlock_bh(&nf_conntrack_lock);
+ nf_conntrack_double_unlock(hash, reply_hash);
+
+ nf_ct_add_to_dying_list(ct);
+
+ NF_CT_STAT_INC(net, delete_list);
+ local_bh_enable();
}
static void death_by_event(unsigned long ul_conntrack)
@@ -331,8 +433,6 @@ nf_ct_key_equal(struct nf_conntrack_tuple_hash *h,
* Warning :
* - Caller must take a reference on returned object
* and recheck nf_ct_tuple_equal(tuple, &h->tuple)
- * OR
- * - Caller must lock nf_conntrack_lock before calling this function
*/
static struct nf_conntrack_tuple_hash *
____nf_conntrack_find(struct net *net, u16 zone,
@@ -408,32 +508,36 @@ EXPORT_SYMBOL_GPL(nf_conntrack_find_get);
static void __nf_conntrack_hash_insert(struct nf_conn *ct,
unsigned int hash,
- unsigned int repl_hash)
+ unsigned int reply_hash)
{
struct net *net = nf_ct_net(ct);
hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
&net->ct.hash[hash]);
hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode,
- &net->ct.hash[repl_hash]);
+ &net->ct.hash[reply_hash]);
}
int
nf_conntrack_hash_check_insert(struct nf_conn *ct)
{
struct net *net = nf_ct_net(ct);
- unsigned int hash, repl_hash;
+ unsigned int hash, reply_hash;
struct nf_conntrack_tuple_hash *h;
struct hlist_nulls_node *n;
u16 zone;
+ unsigned int sequence;
zone = nf_ct_zone(ct);
- hash = hash_conntrack(net, zone,
- &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
- repl_hash = hash_conntrack(net, zone,
- &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
- spin_lock_bh(&nf_conntrack_lock);
+ local_bh_disable();
+ do {
+ sequence = read_seqcount_begin(&net->ct.generation);
+ hash = hash_conntrack(net, zone,
+ &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+ reply_hash = hash_conntrack(net, zone,
+ &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+ } while (nf_conntrack_double_lock(net, hash, reply_hash, sequence));
/* See if there's one in the list already, including reverse */
hlist_nulls_for_each_entry(h, n, &net->ct.hash[hash], hnnode)
@@ -441,7 +545,7 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)
&h->tuple) &&
zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h)))
goto out;
- hlist_nulls_for_each_entry(h, n, &net->ct.hash[repl_hash], hnnode)
+ hlist_nulls_for_each_entry(h, n, &net->ct.hash[reply_hash], hnnode)
if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple,
&h->tuple) &&
zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h)))
@@ -451,15 +555,16 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)
smp_wmb();
/* The caller holds a reference to this object */
atomic_set(&ct->ct_general.use, 2);
- __nf_conntrack_hash_insert(ct, hash, repl_hash);
+ __nf_conntrack_hash_insert(ct, hash, reply_hash);
+ nf_conntrack_double_unlock(hash, reply_hash);
NF_CT_STAT_INC(net, insert);
- spin_unlock_bh(&nf_conntrack_lock);
-
+ local_bh_enable();
return 0;
out:
+ nf_conntrack_double_unlock(hash, reply_hash);
NF_CT_STAT_INC(net, insert_failed);
- spin_unlock_bh(&nf_conntrack_lock);
+ local_bh_enable();
return -EEXIST;
}
EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert);
@@ -467,15 +572,22 @@ EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert);
/* deletion from this larval template list happens via nf_ct_put() */
void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl)
{
+ struct ct_pcpu *pcpu;
+
__set_bit(IPS_TEMPLATE_BIT, &tmpl->status);
__set_bit(IPS_CONFIRMED_BIT, &tmpl->status);
nf_conntrack_get(&tmpl->ct_general);
- spin_lock_bh(&nf_conntrack_lock);
+ /* add this conntrack to the (per cpu) tmpl list */
+ local_bh_disable();
+ tmpl->cpu = smp_processor_id();
+ pcpu = per_cpu_ptr(nf_ct_net(tmpl)->ct.pcpu_lists, tmpl->cpu);
+
+ spin_lock(&pcpu->lock);
/* Overload tuple linked list to put us in template list. */
hlist_nulls_add_head_rcu(&tmpl->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
- &net->ct.tmpl);
- spin_unlock_bh(&nf_conntrack_lock);
+ &pcpu->tmpl);
+ spin_unlock_bh(&pcpu->lock);
}
EXPORT_SYMBOL_GPL(nf_conntrack_tmpl_insert);
@@ -483,7 +595,7 @@ EXPORT_SYMBOL_GPL(nf_conntrack_tmpl_insert);
int
__nf_conntrack_confirm(struct sk_buff *skb)
{
- unsigned int hash, repl_hash;
+ unsigned int hash, reply_hash;
struct nf_conntrack_tuple_hash *h;
struct nf_conn *ct;
struct nf_conn_help *help;
@@ -492,6 +604,7 @@ __nf_conntrack_confirm(struct sk_buff *skb)
enum ip_conntrack_info ctinfo;
struct net *net;
u16 zone;
+ unsigned int sequence;
ct = nf_ct_get(skb, &ctinfo);
net = nf_ct_net(ct);
@@ -504,31 +617,37 @@ __nf_conntrack_confirm(struct sk_buff *skb)
return NF_ACCEPT;
zone = nf_ct_zone(ct);
- /* reuse the hash saved before */
- hash = *(unsigned long *)&ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev;
- hash = hash_bucket(hash, net);
- repl_hash = hash_conntrack(net, zone,
- &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+ local_bh_disable();
+
+ do {
+ sequence = read_seqcount_begin(&net->ct.generation);
+ /* reuse the hash saved before */
+ hash = *(unsigned long *)&ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev;
+ hash = hash_bucket(hash, net);
+ reply_hash = hash_conntrack(net, zone,
+ &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+
+ } while (nf_conntrack_double_lock(net, hash, reply_hash, sequence));
/* We're not in hash table, and we refuse to set up related
- connections for unconfirmed conns. But packet copies and
- REJECT will give spurious warnings here. */
+ * connections for unconfirmed conns. But packet copies and
+ * REJECT will give spurious warnings here.
+ */
/* NF_CT_ASSERT(atomic_read(&ct->ct_general.use) == 1); */
/* No external references means no one else could have
- confirmed us. */
+ * confirmed us.
+ */
NF_CT_ASSERT(!nf_ct_is_confirmed(ct));
pr_debug("Confirming conntrack %p\n", ct);
-
- spin_lock_bh(&nf_conntrack_lock);
-
/* We have to check the DYING flag inside the lock to prevent
a race against nf_ct_get_next_corpse() possibly called from
user context, else we insert an already 'dead' hash, blocking
further use of that particular connection -JM */
if (unlikely(nf_ct_is_dying(ct))) {
- spin_unlock_bh(&nf_conntrack_lock);
+ nf_conntrack_double_unlock(hash, reply_hash);
+ local_bh_enable();
return NF_ACCEPT;
}
@@ -540,14 +659,13 @@ __nf_conntrack_confirm(struct sk_buff *skb)
&h->tuple) &&
zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h)))
goto out;
- hlist_nulls_for_each_entry(h, n, &net->ct.hash[repl_hash], hnnode)
+ hlist_nulls_for_each_entry(h, n, &net->ct.hash[reply_hash], hnnode)
if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple,
&h->tuple) &&
zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h)))
goto out;
- /* Remove from unconfirmed list */
- hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
+ nf_ct_del_from_dying_or_unconfirmed_list(ct);
/* Timer relative to confirmation time, not original
setting time, otherwise we'd get timer wrap in
@@ -570,9 +688,10 @@ __nf_conntrack_confirm(struct sk_buff *skb)
* guarantee that no other CPU can find the conntrack before the above
* stores are visible.
*/
- __nf_conntrack_hash_insert(ct, hash, repl_hash);
+ __nf_conntrack_hash_insert(ct, hash, reply_hash);
+ nf_conntrack_double_unlock(hash, reply_hash);
NF_CT_STAT_INC(net, insert);
- spin_unlock_bh(&nf_conntrack_lock);
+ local_bh_enable();
help = nfct_help(ct);
if (help && help->helper)
@@ -583,8 +702,9 @@ __nf_conntrack_confirm(struct sk_buff *skb)
return NF_ACCEPT;
out:
+ nf_conntrack_double_unlock(hash, reply_hash);
NF_CT_STAT_INC(net, insert_failed);
- spin_unlock_bh(&nf_conntrack_lock);
+ local_bh_enable();
return NF_DROP;
}
EXPORT_SYMBOL_GPL(__nf_conntrack_confirm);
@@ -627,39 +747,48 @@ EXPORT_SYMBOL_GPL(nf_conntrack_tuple_taken);
/* There's a small race here where we may free a just-assured
connection. Too bad: we're in trouble anyway. */
-static noinline int early_drop(struct net *net, unsigned int hash)
+static noinline int early_drop(struct net *net, unsigned int _hash)
{
/* Use oldest entry, which is roughly LRU */
struct nf_conntrack_tuple_hash *h;
struct nf_conn *ct = NULL, *tmp;
struct hlist_nulls_node *n;
- unsigned int i, cnt = 0;
+ unsigned int i = 0, cnt = 0;
int dropped = 0;
+ unsigned int hash, sequence;
+ spinlock_t *lockp;
- rcu_read_lock();
- for (i = 0; i < net->ct.htable_size; i++) {
+ local_bh_disable();
+restart:
+ sequence = read_seqcount_begin(&net->ct.generation);
+ hash = hash_bucket(_hash, net);
+ for (; i < net->ct.htable_size; i++) {
+ lockp = &nf_conntrack_locks[hash % CONNTRACK_LOCKS];
+ spin_lock(lockp);
+ if (read_seqcount_retry(&net->ct.generation, sequence)) {
+ spin_unlock(lockp);
+ goto restart;
+ }
hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash],
hnnode) {
tmp = nf_ct_tuplehash_to_ctrack(h);
- if (!test_bit(IPS_ASSURED_BIT, &tmp->status))
+ if (!test_bit(IPS_ASSURED_BIT, &tmp->status) &&
+ !nf_ct_is_dying(tmp) &&
+ atomic_inc_not_zero(&tmp->ct_general.use)) {
ct = tmp;
+ break;
+ }
cnt++;
}
- if (ct != NULL) {
- if (likely(!nf_ct_is_dying(ct) &&
- atomic_inc_not_zero(&ct->ct_general.use)))
- break;
- else
- ct = NULL;
- }
+ hash = (hash + 1) % net->ct.htable_size;
+ spin_unlock(lockp);
- if (cnt >= NF_CT_EVICTION_RANGE)
+ if (ct || cnt >= NF_CT_EVICTION_RANGE)
break;
- hash = (hash + 1) % net->ct.htable_size;
}
- rcu_read_unlock();
+ local_bh_enable();
if (!ct)
return dropped;
@@ -708,7 +837,7 @@ __nf_conntrack_alloc(struct net *net, u16 zone,
if (nf_conntrack_max &&
unlikely(atomic_read(&net->ct.count) > nf_conntrack_max)) {
- if (!early_drop(net, hash_bucket(hash, net))) {
+ if (!early_drop(net, hash)) {
atomic_dec(&net->ct.count);
net_warn_ratelimited("nf_conntrack: table full, dropping packet\n");
return ERR_PTR(-ENOMEM);
@@ -805,7 +934,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
struct nf_conn_help *help;
struct nf_conntrack_tuple repl_tuple;
struct nf_conntrack_ecache *ecache;
- struct nf_conntrack_expect *exp;
+ struct nf_conntrack_expect *exp = NULL;
u16 zone = tmpl ? nf_ct_zone(tmpl) : NF_CT_DEFAULT_ZONE;
struct nf_conn_timeout *timeout_ext;
unsigned int *timeouts;
@@ -849,42 +978,44 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
ecache ? ecache->expmask : 0,
GFP_ATOMIC);
- spin_lock_bh(&nf_conntrack_lock);
- exp = nf_ct_find_expectation(net, zone, tuple);
- if (exp) {
- pr_debug("conntrack: expectation arrives ct=%p exp=%p\n",
- ct, exp);
- /* Welcome, Mr. Bond. We've been expecting you... */
- __set_bit(IPS_EXPECTED_BIT, &ct->status);
- ct->master = exp->master;
- if (exp->helper) {
- help = nf_ct_helper_ext_add(ct, exp->helper,
- GFP_ATOMIC);
- if (help)
- rcu_assign_pointer(help->helper, exp->helper);
- }
+ local_bh_disable();
+ if (net->ct.expect_count) {
+ spin_lock(&nf_conntrack_expect_lock);
+ exp = nf_ct_find_expectation(net, zone, tuple);
+ if (exp) {
+ pr_debug("conntrack: expectation arrives ct=%p exp=%p\n",
+ ct, exp);
+ /* Welcome, Mr. Bond. We've been expecting you... */
+ __set_bit(IPS_EXPECTED_BIT, &ct->status);
+ /* exp->master safe, refcnt bumped in nf_ct_find_expectation */
+ ct->master = exp->master;
+ if (exp->helper) {
+ help = nf_ct_helper_ext_add(ct, exp->helper,
+ GFP_ATOMIC);
+ if (help)
+ rcu_assign_pointer(help->helper, exp->helper);
+ }
#ifdef CONFIG_NF_CONNTRACK_MARK
- ct->mark = exp->master->mark;
+ ct->mark = exp->master->mark;
#endif
#ifdef CONFIG_NF_CONNTRACK_SECMARK
- ct->secmark = exp->master->secmark;
+ ct->secmark = exp->master->secmark;
#endif
- nf_conntrack_get(&ct->master->ct_general);
- NF_CT_STAT_INC(net, expect_new);
- } else {
+ NF_CT_STAT_INC(net, expect_new);
+ }
+ spin_unlock(&nf_conntrack_expect_lock);
+ }
+ if (!exp) {
__nf_ct_try_assign_helper(ct, tmpl, GFP_ATOMIC);
NF_CT_STAT_INC(net, new);
}
/* Now it is inserted into the unconfirmed list, bump refcount */
nf_conntrack_get(&ct->ct_general);
+ nf_ct_add_to_unconfirmed_list(ct);
- /* Overload tuple linked list to put us in unconfirmed list. */
- hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
- &net->ct.unconfirmed);
-
- spin_unlock_bh(&nf_conntrack_lock);
+ local_bh_enable();
if (exp) {
if (exp->expectfn)
@@ -1254,27 +1385,42 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data),
struct nf_conntrack_tuple_hash *h;
struct nf_conn *ct;
struct hlist_nulls_node *n;
+ int cpu;
+ spinlock_t *lockp;
- spin_lock_bh(&nf_conntrack_lock);
for (; *bucket < net->ct.htable_size; (*bucket)++) {
- hlist_nulls_for_each_entry(h, n, &net->ct.hash[*bucket], hnnode) {
- if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
- continue;
+ lockp = &nf_conntrack_locks[*bucket % CONNTRACK_LOCKS];
+ local_bh_disable();
+ spin_lock(lockp);
+ if (*bucket < net->ct.htable_size) {
+ hlist_nulls_for_each_entry(h, n, &net->ct.hash[*bucket], hnnode) {
+ if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
+ continue;
+ ct = nf_ct_tuplehash_to_ctrack(h);
+ if (iter(ct, data))
+ goto found;
+ }
+ }
+ spin_unlock(lockp);
+ local_bh_enable();
+ }
+
+ for_each_possible_cpu(cpu) {
+ struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu);
+
+ spin_lock_bh(&pcpu->lock);
+ hlist_nulls_for_each_entry(h, n, &pcpu->unconfirmed, hnnode) {
ct = nf_ct_tuplehash_to_ctrack(h);
if (iter(ct, data))
- goto found;
+ set_bit(IPS_DYING_BIT, &ct->status);
}
+ spin_unlock_bh(&pcpu->lock);
}
- hlist_nulls_for_each_entry(h, n, &net->ct.unconfirmed, hnnode) {
- ct = nf_ct_tuplehash_to_ctrack(h);
- if (iter(ct, data))
- set_bit(IPS_DYING_BIT, &ct->status);
- }
- spin_unlock_bh(&nf_conntrack_lock);
return NULL;
found:
atomic_inc(&ct->ct_general.use);
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock(lockp);
+ local_bh_enable();
return ct;
}
@@ -1323,14 +1469,19 @@ static void nf_ct_release_dying_list(struct net *net)
struct nf_conntrack_tuple_hash *h;
struct nf_conn *ct;
struct hlist_nulls_node *n;
+ int cpu;
- spin_lock_bh(&nf_conntrack_lock);
- hlist_nulls_for_each_entry(h, n, &net->ct.dying, hnnode) {
- ct = nf_ct_tuplehash_to_ctrack(h);
- /* never fails to remove them, no listeners at this point */
- nf_ct_kill(ct);
+ for_each_possible_cpu(cpu) {
+ struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu);
+
+ spin_lock_bh(&pcpu->lock);
+ hlist_nulls_for_each_entry(h, n, &pcpu->dying, hnnode) {
+ ct = nf_ct_tuplehash_to_ctrack(h);
+ /* never fails to remove them, no listeners at this point */
+ nf_ct_kill(ct);
+ }
+ spin_unlock_bh(&pcpu->lock);
}
- spin_unlock_bh(&nf_conntrack_lock);
}
static int untrack_refs(void)
@@ -1417,6 +1568,7 @@ i_see_dead_people:
kmem_cache_destroy(net->ct.nf_conntrack_cachep);
kfree(net->ct.slabname);
free_percpu(net->ct.stat);
+ free_percpu(net->ct.pcpu_lists);
}
}
@@ -1469,12 +1621,16 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
if (!hash)
return -ENOMEM;
+ local_bh_disable();
+ nf_conntrack_all_lock();
+ write_seqcount_begin(&init_net.ct.generation);
+
/* Lookups in the old hash might happen in parallel, which means we
* might get false negatives during connection lookup. New connections
* created because of a false negative won't make it into the hash
- * though since that required taking the lock.
+ * though since that required taking the locks.
*/
- spin_lock_bh(&nf_conntrack_lock);
+
for (i = 0; i < init_net.ct.htable_size; i++) {
while (!hlist_nulls_empty(&init_net.ct.hash[i])) {
h = hlist_nulls_entry(init_net.ct.hash[i].first,
@@ -1491,7 +1647,10 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
init_net.ct.htable_size = nf_conntrack_htable_size = hashsize;
init_net.ct.hash = hash;
- spin_unlock_bh(&nf_conntrack_lock);
+
+ write_seqcount_end(&init_net.ct.generation);
+ nf_conntrack_all_unlock();
+ local_bh_enable();
nf_ct_free_hashtable(old_hash, old_size);
return 0;
@@ -1513,7 +1672,10 @@ EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or);
int nf_conntrack_init_start(void)
{
int max_factor = 8;
- int ret, cpu;
+ int i, ret, cpu;
+
+ for (i = 0; i < CONNTRACK_LOCKS; i++)
+ spin_lock_init(&nf_conntrack_locks[i]);
/* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB
* machine has 512 buckets. >= 1GB machines have 16384 buckets. */
@@ -1629,37 +1791,43 @@ void nf_conntrack_init_end(void)
int nf_conntrack_init_net(struct net *net)
{
- int ret;
+ int ret = -ENOMEM;
+ int cpu;
atomic_set(&net->ct.count, 0);
- INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, UNCONFIRMED_NULLS_VAL);
- INIT_HLIST_NULLS_HEAD(&net->ct.dying, DYING_NULLS_VAL);
- INIT_HLIST_NULLS_HEAD(&net->ct.tmpl, TEMPLATE_NULLS_VAL);
- net->ct.stat = alloc_percpu(struct ip_conntrack_stat);
- if (!net->ct.stat) {
- ret = -ENOMEM;
+
+ net->ct.pcpu_lists = alloc_percpu(struct ct_pcpu);
+ if (!net->ct.pcpu_lists)
goto err_stat;
+
+ for_each_possible_cpu(cpu) {
+ struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu);
+
+ spin_lock_init(&pcpu->lock);
+ INIT_HLIST_NULLS_HEAD(&pcpu->unconfirmed, UNCONFIRMED_NULLS_VAL);
+ INIT_HLIST_NULLS_HEAD(&pcpu->dying, DYING_NULLS_VAL);
+ INIT_HLIST_NULLS_HEAD(&pcpu->tmpl, TEMPLATE_NULLS_VAL);
}
+ net->ct.stat = alloc_percpu(struct ip_conntrack_stat);
+ if (!net->ct.stat)
+ goto err_pcpu_lists;
+
net->ct.slabname = kasprintf(GFP_KERNEL, "nf_conntrack_%p", net);
- if (!net->ct.slabname) {
- ret = -ENOMEM;
+ if (!net->ct.slabname)
goto err_slabname;
- }
net->ct.nf_conntrack_cachep = kmem_cache_create(net->ct.slabname,
sizeof(struct nf_conn), 0,
SLAB_DESTROY_BY_RCU, NULL);
if (!net->ct.nf_conntrack_cachep) {
printk(KERN_ERR "Unable to create nf_conn slab cache\n");
- ret = -ENOMEM;
goto err_cache;
}
net->ct.htable_size = nf_conntrack_htable_size;
net->ct.hash = nf_ct_alloc_hashtable(&net->ct.htable_size, 1);
if (!net->ct.hash) {
- ret = -ENOMEM;
printk(KERN_ERR "Unable to create nf_conntrack_hash\n");
goto err_hash;
}
@@ -1701,6 +1869,8 @@ err_cache:
kfree(net->ct.slabname);
err_slabname:
free_percpu(net->ct.stat);
+err_pcpu_lists:
+ free_percpu(net->ct.pcpu_lists);
err_stat:
return ret;
}
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index 4fd1ca94fd4a..f87e8f68ad45 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -66,9 +66,9 @@ static void nf_ct_expectation_timed_out(unsigned long ul_expect)
{
struct nf_conntrack_expect *exp = (void *)ul_expect;
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
nf_ct_unlink_expect(exp);
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
nf_ct_expect_put(exp);
}
@@ -155,6 +155,18 @@ nf_ct_find_expectation(struct net *net, u16 zone,
if (!nf_ct_is_confirmed(exp->master))
return NULL;
+ /* Avoid race with other CPUs, that for exp->master ct, is
+ * about to invoke ->destroy(), or nf_ct_delete() via timeout
+ * or early_drop().
+ *
+ * The atomic_inc_not_zero() check tells: If that fails, we
+ * know that the ct is being destroyed. If it succeeds, we
+ * can be sure the ct cannot disappear underneath.
+ */
+ if (unlikely(nf_ct_is_dying(exp->master) ||
+ !atomic_inc_not_zero(&exp->master->ct_general.use)))
+ return NULL;
+
if (exp->flags & NF_CT_EXPECT_PERMANENT) {
atomic_inc(&exp->use);
return exp;
@@ -162,6 +174,8 @@ nf_ct_find_expectation(struct net *net, u16 zone,
nf_ct_unlink_expect(exp);
return exp;
}
+ /* Undo exp->master refcnt increase, if del_timer() failed */
+ nf_ct_put(exp->master);
return NULL;
}
@@ -177,12 +191,14 @@ void nf_ct_remove_expectations(struct nf_conn *ct)
if (!help)
return;
+ spin_lock_bh(&nf_conntrack_expect_lock);
hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) {
if (del_timer(&exp->timeout)) {
nf_ct_unlink_expect(exp);
nf_ct_expect_put(exp);
}
}
+ spin_unlock_bh(&nf_conntrack_expect_lock);
}
EXPORT_SYMBOL_GPL(nf_ct_remove_expectations);
@@ -217,12 +233,12 @@ static inline int expect_matches(const struct nf_conntrack_expect *a,
/* Generally a bad idea to call this: could have matched already. */
void nf_ct_unexpect_related(struct nf_conntrack_expect *exp)
{
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
if (del_timer(&exp->timeout)) {
nf_ct_unlink_expect(exp);
nf_ct_expect_put(exp);
}
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
}
EXPORT_SYMBOL_GPL(nf_ct_unexpect_related);
@@ -335,7 +351,7 @@ static int nf_ct_expect_insert(struct nf_conntrack_expect *exp)
setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
(unsigned long)exp);
helper = rcu_dereference_protected(master_help->helper,
- lockdep_is_held(&nf_conntrack_lock));
+ lockdep_is_held(&nf_conntrack_expect_lock));
if (helper) {
exp->timeout.expires = jiffies +
helper->expect_policy[exp->class].timeout * HZ;
@@ -395,7 +411,7 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
}
/* Will be over limit? */
helper = rcu_dereference_protected(master_help->helper,
- lockdep_is_held(&nf_conntrack_lock));
+ lockdep_is_held(&nf_conntrack_expect_lock));
if (helper) {
p = &helper->expect_policy[expect->class];
if (p->max_expected &&
@@ -417,12 +433,12 @@ out:
return ret;
}
-int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
+int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
u32 portid, int report)
{
int ret;
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
ret = __nf_ct_expect_check(expect);
if (ret <= 0)
goto out;
@@ -430,11 +446,11 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
ret = nf_ct_expect_insert(expect);
if (ret < 0)
goto out;
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
return ret;
out:
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
index 70866d192efc..3a3a60b126e0 100644
--- a/net/netfilter/nf_conntrack_h323_main.c
+++ b/net/netfilter/nf_conntrack_h323_main.c
@@ -1476,7 +1476,7 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct,
nf_ct_refresh(ct, skb, info->timeout * HZ);
/* Set expect timeout */
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
exp = find_expect(ct, &ct->tuplehash[dir].tuple.dst.u3,
info->sig_port[!dir]);
if (exp) {
@@ -1486,7 +1486,7 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct,
nf_ct_dump_tuple(&exp->tuple);
set_expect_timeout(exp, info->timeout);
}
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
}
return 0;
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index 974a2a4adefa..5b3eae7d4c9a 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -250,16 +250,14 @@ out:
}
EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);
+/* appropiate ct lock protecting must be taken by caller */
static inline int unhelp(struct nf_conntrack_tuple_hash *i,
const struct nf_conntrack_helper *me)
{
struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(i);
struct nf_conn_help *help = nfct_help(ct);
- if (help && rcu_dereference_protected(
- help->helper,
- lockdep_is_held(&nf_conntrack_lock)
- ) == me) {
+ if (help && rcu_dereference_raw(help->helper) == me) {
nf_conntrack_event(IPCT_HELPER, ct);
RCU_INIT_POINTER(help->helper, NULL);
}
@@ -284,17 +282,17 @@ static LIST_HEAD(nf_ct_helper_expectfn_list);
void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n)
{
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
list_add_rcu(&n->head, &nf_ct_helper_expectfn_list);
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
}
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register);
void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n)
{
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
list_del_rcu(&n->head);
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
}
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister);
@@ -396,15 +394,17 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me,
const struct hlist_node *next;
const struct hlist_nulls_node *nn;
unsigned int i;
+ int cpu;
/* Get rid of expectations */
+ spin_lock_bh(&nf_conntrack_expect_lock);
for (i = 0; i < nf_ct_expect_hsize; i++) {
hlist_for_each_entry_safe(exp, next,
&net->ct.expect_hash[i], hnode) {
struct nf_conn_help *help = nfct_help(exp->master);
if ((rcu_dereference_protected(
help->helper,
- lockdep_is_held(&nf_conntrack_lock)
+ lockdep_is_held(&nf_conntrack_expect_lock)
) == me || exp->helper == me) &&
del_timer(&exp->timeout)) {
nf_ct_unlink_expect(exp);
@@ -412,14 +412,27 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me,
}
}
}
+ spin_unlock_bh(&nf_conntrack_expect_lock);
/* Get rid of expecteds, set helpers to NULL. */
- hlist_nulls_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode)
- unhelp(h, me);
- for (i = 0; i < net->ct.htable_size; i++) {
- hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode)
+ for_each_possible_cpu(cpu) {
+ struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu);
+
+ spin_lock_bh(&pcpu->lock);
+ hlist_nulls_for_each_entry(h, nn, &pcpu->unconfirmed, hnnode)
unhelp(h, me);
+ spin_unlock_bh(&pcpu->lock);
+ }
+ local_bh_disable();
+ for (i = 0; i < net->ct.htable_size; i++) {
+ spin_lock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]);
+ if (i < net->ct.htable_size) {
+ hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode)
+ unhelp(h, me);
+ }
+ spin_unlock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]);
}
+ local_bh_enable();
}
void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
@@ -437,10 +450,8 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
synchronize_rcu();
rtnl_lock();
- spin_lock_bh(&nf_conntrack_lock);
for_each_net(net)
__nf_conntrack_helper_unregister(me, net);
- spin_unlock_bh(&nf_conntrack_lock);
rtnl_unlock();
}
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index b9f0e0374322..ccc46fa5edbc 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -764,14 +764,23 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
u_int8_t l3proto = nfmsg->nfgen_family;
int res;
+ spinlock_t *lockp;
+
#ifdef CONFIG_NF_CONNTRACK_MARK
const struct ctnetlink_dump_filter *filter = cb->data;
#endif
- spin_lock_bh(&nf_conntrack_lock);
last = (struct nf_conn *)cb->args[1];
+
+ local_bh_disable();
for (; cb->args[0] < net->ct.htable_size; cb->args[0]++) {
restart:
+ lockp = &nf_conntrack_locks[cb->args[0] % CONNTRACK_LOCKS];
+ spin_lock(lockp);
+ if (cb->args[0] >= net->ct.htable_size) {
+ spin_unlock(lockp);
+ goto out;
+ }
hlist_nulls_for_each_entry(h, n, &net->ct.hash[cb->args[0]],
hnnode) {
if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
@@ -803,16 +812,18 @@ restart:
if (res < 0) {
nf_conntrack_get(&ct->ct_general);
cb->args[1] = (unsigned long)ct;
+ spin_unlock(lockp);
goto out;
}
}
+ spin_unlock(lockp);
if (cb->args[1]) {
cb->args[1] = 0;
goto restart;
}
}
out:
- spin_unlock_bh(&nf_conntrack_lock);
+ local_bh_enable();
if (last)
nf_ct_put(last);
@@ -966,7 +977,6 @@ ctnetlink_parse_help(const struct nlattr *attr, char **helper_name,
return 0;
}
-#define __CTA_LABELS_MAX_LENGTH ((XT_CONNLABEL_MAXBIT + 1) / BITS_PER_BYTE)
static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
[CTA_TUPLE_ORIG] = { .type = NLA_NESTED },
[CTA_TUPLE_REPLY] = { .type = NLA_NESTED },
@@ -984,9 +994,9 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
[CTA_ZONE] = { .type = NLA_U16 },
[CTA_MARK_MASK] = { .type = NLA_U32 },
[CTA_LABELS] = { .type = NLA_BINARY,
- .len = __CTA_LABELS_MAX_LENGTH },
+ .len = NF_CT_LABELS_MAX_SIZE },
[CTA_LABELS_MASK] = { .type = NLA_BINARY,
- .len = __CTA_LABELS_MAX_LENGTH },
+ .len = NF_CT_LABELS_MAX_SIZE },
};
static int
@@ -1138,50 +1148,65 @@ static int ctnetlink_done_list(struct netlink_callback *cb)
}
static int
-ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb,
- struct hlist_nulls_head *list)
+ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb, bool dying)
{
- struct nf_conn *ct, *last;
+ struct nf_conn *ct, *last = NULL;
struct nf_conntrack_tuple_hash *h;
struct hlist_nulls_node *n;
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
u_int8_t l3proto = nfmsg->nfgen_family;
int res;
+ int cpu;
+ struct hlist_nulls_head *list;
+ struct net *net = sock_net(skb->sk);
if (cb->args[2])
return 0;
- spin_lock_bh(&nf_conntrack_lock);
- last = (struct nf_conn *)cb->args[1];
-restart:
- hlist_nulls_for_each_entry(h, n, list, hnnode) {
- ct = nf_ct_tuplehash_to_ctrack(h);
- if (l3proto && nf_ct_l3num(ct) != l3proto)
+ if (cb->args[0] == nr_cpu_ids)
+ return 0;
+
+ for (cpu = cb->args[0]; cpu < nr_cpu_ids; cpu++) {
+ struct ct_pcpu *pcpu;
+
+ if (!cpu_possible(cpu))
continue;
- if (cb->args[1]) {
- if (ct != last)
+
+ pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu);
+ spin_lock_bh(&pcpu->lock);
+ last = (struct nf_conn *)cb->args[1];
+ list = dying ? &pcpu->dying : &pcpu->unconfirmed;
+restart:
+ hlist_nulls_for_each_entry(h, n, list, hnnode) {
+ ct = nf_ct_tuplehash_to_ctrack(h);
+ if (l3proto && nf_ct_l3num(ct) != l3proto)
continue;
- cb->args[1] = 0;
- }
- rcu_read_lock();
- res = ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq,
- NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
- ct);
- rcu_read_unlock();
- if (res < 0) {
- nf_conntrack_get(&ct->ct_general);
- cb->args[1] = (unsigned long)ct;
- goto out;
+ if (cb->args[1]) {
+ if (ct != last)
+ continue;
+ cb->args[1] = 0;
+ }
+ rcu_read_lock();
+ res = ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
+ ct);
+ rcu_read_unlock();
+ if (res < 0) {
+ nf_conntrack_get(&ct->ct_general);
+ cb->args[1] = (unsigned long)ct;
+ spin_unlock_bh(&pcpu->lock);
+ goto out;
+ }
}
+ if (cb->args[1]) {
+ cb->args[1] = 0;
+ goto restart;
+ } else
+ cb->args[2] = 1;
+ spin_unlock_bh(&pcpu->lock);
}
- if (cb->args[1]) {
- cb->args[1] = 0;
- goto restart;
- } else
- cb->args[2] = 1;
out:
- spin_unlock_bh(&nf_conntrack_lock);
if (last)
nf_ct_put(last);
@@ -1191,9 +1216,7 @@ out:
static int
ctnetlink_dump_dying(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct net *net = sock_net(skb->sk);
-
- return ctnetlink_dump_list(skb, cb, &net->ct.dying);
+ return ctnetlink_dump_list(skb, cb, true);
}
static int
@@ -1215,9 +1238,7 @@ ctnetlink_get_ct_dying(struct sock *ctnl, struct sk_buff *skb,
static int
ctnetlink_dump_unconfirmed(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct net *net = sock_net(skb->sk);
-
- return ctnetlink_dump_list(skb, cb, &net->ct.unconfirmed);
+ return ctnetlink_dump_list(skb, cb, false);
}
static int
@@ -1361,14 +1382,14 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[])
nf_ct_protonum(ct));
if (helper == NULL) {
#ifdef CONFIG_MODULES
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
if (request_module("nfct-helper-%s", helpname) < 0) {
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
return -EOPNOTSUPP;
}
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
nf_ct_protonum(ct));
if (helper)
@@ -1804,9 +1825,9 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
err = -EEXIST;
ct = nf_ct_tuplehash_to_ctrack(h);
if (!(nlh->nlmsg_flags & NLM_F_EXCL)) {
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
err = ctnetlink_change_conntrack(ct, cda);
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
if (err == 0) {
nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
(1 << IPCT_ASSURED) |
@@ -2135,9 +2156,9 @@ ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct)
if (ret < 0)
return ret;
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
ret = ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct);
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
return ret;
}
@@ -2692,13 +2713,13 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
}
/* after list removal, usage count == 1 */
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
if (del_timer(&exp->timeout)) {
nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid,
nlmsg_report(nlh));
nf_ct_expect_put(exp);
}
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
/* have to put what we 'get' above.
* after this line usage count == 0 */
nf_ct_expect_put(exp);
@@ -2707,7 +2728,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
struct nf_conn_help *m_help;
/* delete all expectations for this helper */
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
for (i = 0; i < nf_ct_expect_hsize; i++) {
hlist_for_each_entry_safe(exp, next,
&net->ct.expect_hash[i],
@@ -2722,10 +2743,10 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
}
}
}
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
} else {
/* This basically means we have to flush everything*/
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
for (i = 0; i < nf_ct_expect_hsize; i++) {
hlist_for_each_entry_safe(exp, next,
&net->ct.expect_hash[i],
@@ -2738,7 +2759,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
}
}
}
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
}
return 0;
@@ -2964,11 +2985,11 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
if (err < 0)
return err;
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
exp = __nf_ct_expect_find(net, zone, &tuple);
if (!exp) {
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
err = -ENOENT;
if (nlh->nlmsg_flags & NLM_F_CREATE) {
err = ctnetlink_create_expect(net, zone, cda,
@@ -2982,7 +3003,7 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
err = -EEXIST;
if (!(nlh->nlmsg_flags & NLM_F_EXCL))
err = ctnetlink_change_expect(exp, cda);
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
return err;
}
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 466410eaa482..4c3ba1c8d682 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -800,7 +800,7 @@ static int refresh_signalling_expectation(struct nf_conn *ct,
struct hlist_node *next;
int found = 0;
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) {
if (exp->class != SIP_EXPECT_SIGNALLING ||
!nf_inet_addr_cmp(&exp->tuple.dst.u3, addr) ||
@@ -815,7 +815,7 @@ static int refresh_signalling_expectation(struct nf_conn *ct,
found = 1;
break;
}
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
return found;
}
@@ -825,7 +825,7 @@ static void flush_expectations(struct nf_conn *ct, bool media)
struct nf_conntrack_expect *exp;
struct hlist_node *next;
- spin_lock_bh(&nf_conntrack_lock);
+ spin_lock_bh(&nf_conntrack_expect_lock);
hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) {
if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media)
continue;
@@ -836,7 +836,7 @@ static void flush_expectations(struct nf_conn *ct, bool media)
if (!media)
break;
}
- spin_unlock_bh(&nf_conntrack_lock);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
}
static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index adce01e8bb57..33045a562297 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -794,9 +794,8 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
if (chain->stats) {
- /* nfnl_lock is held, add some nfnl function for this, later */
struct nft_stats __percpu *oldstats =
- rcu_dereference_protected(chain->stats, 1);
+ nft_dereference(chain->stats);
rcu_assign_pointer(chain->stats, newstats);
synchronize_rcu();
@@ -1254,10 +1253,11 @@ err1:
return err;
}
-static void nf_tables_expr_destroy(struct nft_expr *expr)
+static void nf_tables_expr_destroy(const struct nft_ctx *ctx,
+ struct nft_expr *expr)
{
if (expr->ops->destroy)
- expr->ops->destroy(expr);
+ expr->ops->destroy(ctx, expr);
module_put(expr->ops->type->owner);
}
@@ -1296,6 +1296,8 @@ static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = {
[NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED },
[NFTA_RULE_COMPAT] = { .type = NLA_NESTED },
[NFTA_RULE_POSITION] = { .type = NLA_U64 },
+ [NFTA_RULE_USERDATA] = { .type = NLA_BINARY,
+ .len = NFT_USERDATA_MAXLEN },
};
static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
@@ -1348,6 +1350,10 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
}
nla_nest_end(skb, list);
+ if (rule->ulen &&
+ nla_put(skb, NFTA_RULE_USERDATA, rule->ulen, nft_userdata(rule)))
+ goto nla_put_failure;
+
return nlmsg_end(skb, nlh);
nla_put_failure:
@@ -1531,7 +1537,8 @@ err:
return err;
}
-static void nf_tables_rule_destroy(struct nft_rule *rule)
+static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
+ struct nft_rule *rule)
{
struct nft_expr *expr;
@@ -1541,7 +1548,7 @@ static void nf_tables_rule_destroy(struct nft_rule *rule)
*/
expr = nft_expr_first(rule);
while (expr->ops && expr != nft_expr_last(rule)) {
- nf_tables_expr_destroy(expr);
+ nf_tables_expr_destroy(ctx, expr);
expr = nft_expr_next(expr);
}
kfree(rule);
@@ -1552,7 +1559,7 @@ static void nf_tables_rule_destroy(struct nft_rule *rule)
static struct nft_expr_info *info;
static struct nft_rule_trans *
-nf_tables_trans_add(struct nft_rule *rule, const struct nft_ctx *ctx)
+nf_tables_trans_add(struct nft_ctx *ctx, struct nft_rule *rule)
{
struct nft_rule_trans *rupd;
@@ -1560,11 +1567,8 @@ nf_tables_trans_add(struct nft_rule *rule, const struct nft_ctx *ctx)
if (rupd == NULL)
return NULL;
- rupd->chain = ctx->chain;
- rupd->table = ctx->table;
+ rupd->ctx = *ctx;
rupd->rule = rule;
- rupd->family = ctx->afi->family;
- rupd->nlh = ctx->nlh;
list_add_tail(&rupd->list, &ctx->net->nft.commit_list);
return rupd;
@@ -1584,7 +1588,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
struct nft_expr *expr;
struct nft_ctx ctx;
struct nlattr *tmp;
- unsigned int size, i, n;
+ unsigned int size, i, n, ulen = 0;
int err, rem;
bool create;
u64 handle, pos_handle;
@@ -1650,8 +1654,11 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
}
}
+ if (nla[NFTA_RULE_USERDATA])
+ ulen = nla_len(nla[NFTA_RULE_USERDATA]);
+
err = -ENOMEM;
- rule = kzalloc(sizeof(*rule) + size, GFP_KERNEL);
+ rule = kzalloc(sizeof(*rule) + size + ulen, GFP_KERNEL);
if (rule == NULL)
goto err1;
@@ -1659,6 +1666,10 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
rule->handle = handle;
rule->dlen = size;
+ rule->ulen = ulen;
+
+ if (ulen)
+ nla_memcpy(nft_userdata(rule), nla[NFTA_RULE_USERDATA], ulen);
expr = nft_expr_first(rule);
for (i = 0; i < n; i++) {
@@ -1671,7 +1682,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
if (nlh->nlmsg_flags & NLM_F_REPLACE) {
if (nft_rule_is_active_next(net, old_rule)) {
- repl = nf_tables_trans_add(old_rule, &ctx);
+ repl = nf_tables_trans_add(&ctx, old_rule);
if (repl == NULL) {
err = -ENOMEM;
goto err2;
@@ -1694,7 +1705,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
list_add_rcu(&rule->list, &chain->rules);
}
- if (nf_tables_trans_add(rule, &ctx) == NULL) {
+ if (nf_tables_trans_add(&ctx, rule) == NULL) {
err = -ENOMEM;
goto err3;
}
@@ -1709,7 +1720,7 @@ err3:
kfree(repl);
}
err2:
- nf_tables_rule_destroy(rule);
+ nf_tables_rule_destroy(&ctx, rule);
err1:
for (i = 0; i < n; i++) {
if (info[i].ops != NULL)
@@ -1723,7 +1734,7 @@ nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule)
{
/* You cannot delete the same rule twice */
if (nft_rule_is_active_next(ctx->net, rule)) {
- if (nf_tables_trans_add(rule, ctx) == NULL)
+ if (nf_tables_trans_add(ctx, rule) == NULL)
return -ENOMEM;
nft_rule_disactivate_next(ctx->net, rule);
return 0;
@@ -1819,10 +1830,10 @@ static int nf_tables_commit(struct sk_buff *skb)
*/
if (nft_rule_is_active(net, rupd->rule)) {
nft_rule_clear(net, rupd->rule);
- nf_tables_rule_notify(skb, rupd->nlh, rupd->table,
- rupd->chain, rupd->rule,
- NFT_MSG_NEWRULE, 0,
- rupd->family);
+ nf_tables_rule_notify(skb, rupd->ctx.nlh,
+ rupd->ctx.table, rupd->ctx.chain,
+ rupd->rule, NFT_MSG_NEWRULE, 0,
+ rupd->ctx.afi->family);
list_del(&rupd->list);
kfree(rupd);
continue;
@@ -1830,9 +1841,10 @@ static int nf_tables_commit(struct sk_buff *skb)
/* This rule is in the past, get rid of it */
list_del_rcu(&rupd->rule->list);
- nf_tables_rule_notify(skb, rupd->nlh, rupd->table, rupd->chain,
+ nf_tables_rule_notify(skb, rupd->ctx.nlh,
+ rupd->ctx.table, rupd->ctx.chain,
rupd->rule, NFT_MSG_DELRULE, 0,
- rupd->family);
+ rupd->ctx.afi->family);
}
/* Make sure we don't see any packet traversing old rules */
@@ -1840,7 +1852,7 @@ static int nf_tables_commit(struct sk_buff *skb)
/* Now we can safely release unused old rules */
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
- nf_tables_rule_destroy(rupd->rule);
+ nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
list_del(&rupd->list);
kfree(rupd);
}
@@ -1869,7 +1881,7 @@ static int nf_tables_abort(struct sk_buff *skb)
synchronize_rcu();
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
- nf_tables_rule_destroy(rupd->rule);
+ nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
list_del(&rupd->list);
kfree(rupd);
}
@@ -2430,8 +2442,7 @@ err1:
static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
{
list_del(&set->list);
- if (!(set->flags & NFT_SET_ANONYMOUS))
- nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
+ nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
set->ops->destroy(set);
module_put(set->ops->owner);
@@ -3175,9 +3186,16 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
data->verdict = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE]));
switch (data->verdict) {
- case NF_ACCEPT:
- case NF_DROP:
- case NF_QUEUE:
+ default:
+ switch (data->verdict & NF_VERDICT_MASK) {
+ case NF_ACCEPT:
+ case NF_DROP:
+ case NF_QUEUE:
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* fall through */
case NFT_CONTINUE:
case NFT_BREAK:
case NFT_RETURN:
@@ -3198,8 +3216,6 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
data->chain = chain;
desc->len = sizeof(data);
break;
- default:
- return -EINVAL;
}
desc->type = NFT_DATA_VERDICT;
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index 046aa13b4fea..e8138da4c14f 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -61,6 +61,14 @@ void nfnl_unlock(__u8 subsys_id)
}
EXPORT_SYMBOL_GPL(nfnl_unlock);
+#ifdef CONFIG_PROVE_LOCKING
+int lockdep_nfnl_is_held(u8 subsys_id)
+{
+ return lockdep_is_held(&table[subsys_id].mutex);
+}
+EXPORT_SYMBOL_GPL(lockdep_nfnl_is_held);
+#endif
+
int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
{
nfnl_lock(n->subsys_id);
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index a155d19a225e..d292c8d286eb 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -28,8 +28,6 @@
#include <linux/proc_fs.h>
#include <linux/security.h>
#include <linux/list.h>
-#include <linux/jhash.h>
-#include <linux/random.h>
#include <linux/slab.h>
#include <net/sock.h>
#include <net/netfilter/nf_log.h>
@@ -75,7 +73,6 @@ struct nfulnl_instance {
};
#define INSTANCE_BUCKETS 16
-static unsigned int hash_init;
static int nfnl_log_net_id __read_mostly;
@@ -1067,11 +1064,6 @@ static int __init nfnetlink_log_init(void)
{
int status = -ENOMEM;
- /* it's not really all that important to have a random value, so
- * we can do this from the init function, even if there hasn't
- * been that much entropy yet */
- get_random_bytes(&hash_init, sizeof(hash_init));
-
netlink_register_notifier(&nfulnl_rtnl_notifier);
status = nfnetlink_subsys_register(&nfulnl_subsys);
if (status < 0) {
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index 82cb8236f8a1..8a779be832fb 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -192,7 +192,7 @@ err:
}
static void
-nft_target_destroy(const struct nft_expr *expr)
+nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
{
struct xt_target *target = expr->ops->data;
@@ -379,7 +379,7 @@ err:
}
static void
-nft_match_destroy(const struct nft_expr *expr)
+nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
{
struct xt_match *match = expr->ops->data;
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index 46e275403838..bd0d41e69341 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -19,15 +19,15 @@
#include <net/netfilter/nf_conntrack_tuple.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_ecache.h>
+#include <net/netfilter/nf_conntrack_labels.h>
struct nft_ct {
enum nft_ct_keys key:8;
enum ip_conntrack_dir dir:8;
- union{
+ union {
enum nft_registers dreg:8;
enum nft_registers sreg:8;
};
- uint8_t family;
};
static void nft_ct_get_eval(const struct nft_expr *expr,
@@ -97,6 +97,26 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
goto err;
strncpy((char *)dest->data, helper->name, sizeof(dest->data));
return;
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+ case NFT_CT_LABELS: {
+ struct nf_conn_labels *labels = nf_ct_labels_find(ct);
+ unsigned int size;
+
+ if (!labels) {
+ memset(dest->data, 0, sizeof(dest->data));
+ return;
+ }
+
+ BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE > sizeof(dest->data));
+ size = labels->words * sizeof(long);
+
+ memcpy(dest->data, labels->bits, size);
+ if (size < sizeof(dest->data))
+ memset(((char *) dest->data) + size, 0,
+ sizeof(dest->data) - size);
+ return;
+ }
+#endif
}
tuple = &ct->tuplehash[priv->dir].tuple;
@@ -221,6 +241,9 @@ static int nft_ct_init_validate_get(const struct nft_expr *expr,
#ifdef CONFIG_NF_CONNTRACK_SECMARK
case NFT_CT_SECMARK:
#endif
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+ case NFT_CT_LABELS:
+#endif
case NFT_CT_EXPIRATION:
case NFT_CT_HELPER:
if (tb[NFTA_CT_DIRECTION] != NULL)
@@ -292,16 +315,13 @@ static int nft_ct_init(const struct nft_ctx *ctx,
if (err < 0)
return err;
- priv->family = ctx->afi->family;
-
return 0;
}
-static void nft_ct_destroy(const struct nft_expr *expr)
+static void nft_ct_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
{
- struct nft_ct *priv = nft_expr_priv(expr);
-
- nft_ct_l3proto_module_put(priv->family);
+ nft_ct_l3proto_module_put(ctx->afi->family);
}
static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c
index 3d3f8fce10a5..3b1ad876d6b0 100644
--- a/net/netfilter/nft_hash.c
+++ b/net/netfilter/nft_hash.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.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
@@ -14,21 +14,34 @@
#include <linux/list.h>
#include <linux/jhash.h>
#include <linux/netlink.h>
+#include <linux/vmalloc.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
+#define NFT_HASH_MIN_SIZE 4
+
struct nft_hash {
- struct hlist_head *hash;
- unsigned int hsize;
+ struct nft_hash_table __rcu *tbl;
+};
+
+struct nft_hash_table {
+ unsigned int size;
+ unsigned int elements;
+ struct nft_hash_elem __rcu *buckets[];
};
struct nft_hash_elem {
- struct hlist_node hnode;
- struct nft_data key;
- struct nft_data data[];
+ struct nft_hash_elem __rcu *next;
+ struct nft_data key;
+ struct nft_data data[];
};
+#define nft_hash_for_each_entry(i, head) \
+ for (i = nft_dereference(head); i != NULL; i = nft_dereference(i->next))
+#define nft_hash_for_each_entry_rcu(i, head) \
+ for (i = rcu_dereference(head); i != NULL; i = rcu_dereference(i->next))
+
static u32 nft_hash_rnd __read_mostly;
static bool nft_hash_rnd_initted __read_mostly;
@@ -38,7 +51,7 @@ static unsigned int nft_hash_data(const struct nft_data *data,
unsigned int h;
h = jhash(data->data, len, nft_hash_rnd);
- return ((u64)h * hsize) >> 32;
+ return h & (hsize - 1);
}
static bool nft_hash_lookup(const struct nft_set *set,
@@ -46,11 +59,12 @@ static bool nft_hash_lookup(const struct nft_set *set,
struct nft_data *data)
{
const struct nft_hash *priv = nft_set_priv(set);
+ const struct nft_hash_table *tbl = rcu_dereference(priv->tbl);
const struct nft_hash_elem *he;
unsigned int h;
- h = nft_hash_data(key, priv->hsize, set->klen);
- hlist_for_each_entry(he, &priv->hash[h], hnode) {
+ h = nft_hash_data(key, tbl->size, set->klen);
+ nft_hash_for_each_entry_rcu(he, tbl->buckets[h]) {
if (nft_data_cmp(&he->key, key, set->klen))
continue;
if (set->flags & NFT_SET_MAP)
@@ -60,19 +74,148 @@ static bool nft_hash_lookup(const struct nft_set *set,
return false;
}
-static void nft_hash_elem_destroy(const struct nft_set *set,
- struct nft_hash_elem *he)
+static void nft_hash_tbl_free(const struct nft_hash_table *tbl)
{
- nft_data_uninit(&he->key, NFT_DATA_VALUE);
- if (set->flags & NFT_SET_MAP)
- nft_data_uninit(he->data, set->dtype);
- kfree(he);
+ if (is_vmalloc_addr(tbl))
+ vfree(tbl);
+ else
+ kfree(tbl);
+}
+
+static struct nft_hash_table *nft_hash_tbl_alloc(unsigned int nbuckets)
+{
+ struct nft_hash_table *tbl;
+ size_t size;
+
+ size = sizeof(*tbl) + nbuckets * sizeof(tbl->buckets[0]);
+ tbl = kzalloc(size, GFP_KERNEL | __GFP_REPEAT | __GFP_NOWARN);
+ if (tbl == NULL)
+ tbl = vzalloc(size);
+ if (tbl == NULL)
+ return NULL;
+ tbl->size = nbuckets;
+
+ return tbl;
+}
+
+static void nft_hash_chain_unzip(const struct nft_set *set,
+ const struct nft_hash_table *ntbl,
+ struct nft_hash_table *tbl, unsigned int n)
+{
+ struct nft_hash_elem *he, *last, *next;
+ unsigned int h;
+
+ he = nft_dereference(tbl->buckets[n]);
+ if (he == NULL)
+ return;
+ h = nft_hash_data(&he->key, ntbl->size, set->klen);
+
+ /* Find last element of first chain hashing to bucket h */
+ last = he;
+ nft_hash_for_each_entry(he, he->next) {
+ if (nft_hash_data(&he->key, ntbl->size, set->klen) != h)
+ break;
+ last = he;
+ }
+
+ /* Unlink first chain from the old table */
+ RCU_INIT_POINTER(tbl->buckets[n], last->next);
+
+ /* If end of chain reached, done */
+ if (he == NULL)
+ return;
+
+ /* Find first element of second chain hashing to bucket h */
+ next = NULL;
+ nft_hash_for_each_entry(he, he->next) {
+ if (nft_hash_data(&he->key, ntbl->size, set->klen) != h)
+ continue;
+ next = he;
+ break;
+ }
+
+ /* Link the two chains */
+ RCU_INIT_POINTER(last->next, next);
+}
+
+static int nft_hash_tbl_expand(const struct nft_set *set, struct nft_hash *priv)
+{
+ struct nft_hash_table *tbl = nft_dereference(priv->tbl), *ntbl;
+ struct nft_hash_elem *he;
+ unsigned int i, h;
+ bool complete;
+
+ ntbl = nft_hash_tbl_alloc(tbl->size * 2);
+ if (ntbl == NULL)
+ return -ENOMEM;
+
+ /* Link new table's buckets to first element in the old table
+ * hashing to the new bucket.
+ */
+ for (i = 0; i < ntbl->size; i++) {
+ h = i < tbl->size ? i : i - tbl->size;
+ nft_hash_for_each_entry(he, tbl->buckets[h]) {
+ if (nft_hash_data(&he->key, ntbl->size, set->klen) != i)
+ continue;
+ RCU_INIT_POINTER(ntbl->buckets[i], he);
+ break;
+ }
+ }
+ ntbl->elements = tbl->elements;
+
+ /* Publish new table */
+ rcu_assign_pointer(priv->tbl, ntbl);
+
+ /* Unzip interleaved hash chains */
+ do {
+ /* Wait for readers to use new table/unzipped chains */
+ synchronize_rcu();
+
+ complete = true;
+ for (i = 0; i < tbl->size; i++) {
+ nft_hash_chain_unzip(set, ntbl, tbl, i);
+ if (tbl->buckets[i] != NULL)
+ complete = false;
+ }
+ } while (!complete);
+
+ nft_hash_tbl_free(tbl);
+ return 0;
+}
+
+static int nft_hash_tbl_shrink(const struct nft_set *set, struct nft_hash *priv)
+{
+ struct nft_hash_table *tbl = nft_dereference(priv->tbl), *ntbl;
+ struct nft_hash_elem __rcu **pprev;
+ unsigned int i;
+
+ ntbl = nft_hash_tbl_alloc(tbl->size / 2);
+ if (ntbl == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ntbl->size; i++) {
+ ntbl->buckets[i] = tbl->buckets[i];
+
+ for (pprev = &ntbl->buckets[i]; *pprev != NULL;
+ pprev = &nft_dereference(*pprev)->next)
+ ;
+ RCU_INIT_POINTER(*pprev, tbl->buckets[i + ntbl->size]);
+ }
+ ntbl->elements = tbl->elements;
+
+ /* Publish new table */
+ rcu_assign_pointer(priv->tbl, ntbl);
+ synchronize_rcu();
+
+ nft_hash_tbl_free(tbl);
+ return 0;
}
static int nft_hash_insert(const struct nft_set *set,
const struct nft_set_elem *elem)
{
struct nft_hash *priv = nft_set_priv(set);
+ struct nft_hash_table *tbl = nft_dereference(priv->tbl);
struct nft_hash_elem *he;
unsigned int size, h;
@@ -91,33 +234,66 @@ static int nft_hash_insert(const struct nft_set *set,
if (set->flags & NFT_SET_MAP)
nft_data_copy(he->data, &elem->data);
- h = nft_hash_data(&he->key, priv->hsize, set->klen);
- hlist_add_head_rcu(&he->hnode, &priv->hash[h]);
+ h = nft_hash_data(&he->key, tbl->size, set->klen);
+ RCU_INIT_POINTER(he->next, tbl->buckets[h]);
+ rcu_assign_pointer(tbl->buckets[h], he);
+ tbl->elements++;
+
+ /* Expand table when exceeding 75% load */
+ if (tbl->elements > tbl->size / 4 * 3)
+ nft_hash_tbl_expand(set, priv);
+
return 0;
}
+static void nft_hash_elem_destroy(const struct nft_set *set,
+ struct nft_hash_elem *he)
+{
+ nft_data_uninit(&he->key, NFT_DATA_VALUE);
+ if (set->flags & NFT_SET_MAP)
+ nft_data_uninit(he->data, set->dtype);
+ kfree(he);
+}
+
static void nft_hash_remove(const struct nft_set *set,
const struct nft_set_elem *elem)
{
- struct nft_hash_elem *he = elem->cookie;
+ struct nft_hash *priv = nft_set_priv(set);
+ struct nft_hash_table *tbl = nft_dereference(priv->tbl);
+ struct nft_hash_elem *he, __rcu **pprev;
- hlist_del_rcu(&he->hnode);
+ pprev = elem->cookie;
+ he = nft_dereference((*pprev));
+
+ RCU_INIT_POINTER(*pprev, he->next);
+ synchronize_rcu();
kfree(he);
+ tbl->elements--;
+
+ /* Shrink table beneath 30% load */
+ if (tbl->elements < tbl->size * 3 / 10 &&
+ tbl->size > NFT_HASH_MIN_SIZE)
+ nft_hash_tbl_shrink(set, priv);
}
static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem)
{
const struct nft_hash *priv = nft_set_priv(set);
+ const struct nft_hash_table *tbl = nft_dereference(priv->tbl);
+ struct nft_hash_elem __rcu * const *pprev;
struct nft_hash_elem *he;
unsigned int h;
- h = nft_hash_data(&elem->key, priv->hsize, set->klen);
- hlist_for_each_entry(he, &priv->hash[h], hnode) {
- if (nft_data_cmp(&he->key, &elem->key, set->klen))
+ h = nft_hash_data(&elem->key, tbl->size, set->klen);
+ pprev = &tbl->buckets[h];
+ nft_hash_for_each_entry(he, tbl->buckets[h]) {
+ if (nft_data_cmp(&he->key, &elem->key, set->klen)) {
+ pprev = &he->next;
continue;
+ }
- elem->cookie = he;
- elem->flags = 0;
+ elem->cookie = (void *)pprev;
+ elem->flags = 0;
if (set->flags & NFT_SET_MAP)
nft_data_copy(&elem->data, he->data);
return 0;
@@ -129,12 +305,13 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
struct nft_set_iter *iter)
{
const struct nft_hash *priv = nft_set_priv(set);
+ const struct nft_hash_table *tbl = nft_dereference(priv->tbl);
const struct nft_hash_elem *he;
struct nft_set_elem elem;
unsigned int i;
- for (i = 0; i < priv->hsize; i++) {
- hlist_for_each_entry(he, &priv->hash[i], hnode) {
+ for (i = 0; i < tbl->size; i++) {
+ nft_hash_for_each_entry(he, tbl->buckets[i]) {
if (iter->count < iter->skip)
goto cont;
@@ -161,43 +338,35 @@ static int nft_hash_init(const struct nft_set *set,
const struct nlattr * const tb[])
{
struct nft_hash *priv = nft_set_priv(set);
- unsigned int cnt, i;
+ struct nft_hash_table *tbl;
if (unlikely(!nft_hash_rnd_initted)) {
get_random_bytes(&nft_hash_rnd, 4);
nft_hash_rnd_initted = true;
}
- /* Aim for a load factor of 0.75 */
- // FIXME: temporarily broken until we have set descriptions
- cnt = 100;
- cnt = cnt * 4 / 3;
-
- priv->hash = kcalloc(cnt, sizeof(struct hlist_head), GFP_KERNEL);
- if (priv->hash == NULL)
+ tbl = nft_hash_tbl_alloc(NFT_HASH_MIN_SIZE);
+ if (tbl == NULL)
return -ENOMEM;
- priv->hsize = cnt;
-
- for (i = 0; i < cnt; i++)
- INIT_HLIST_HEAD(&priv->hash[i]);
-
+ RCU_INIT_POINTER(priv->tbl, tbl);
return 0;
}
static void nft_hash_destroy(const struct nft_set *set)
{
const struct nft_hash *priv = nft_set_priv(set);
- const struct hlist_node *next;
- struct nft_hash_elem *elem;
+ const struct nft_hash_table *tbl = nft_dereference(priv->tbl);
+ struct nft_hash_elem *he, *next;
unsigned int i;
- for (i = 0; i < priv->hsize; i++) {
- hlist_for_each_entry_safe(elem, next, &priv->hash[i], hnode) {
- hlist_del(&elem->hnode);
- nft_hash_elem_destroy(set, elem);
+ for (i = 0; i < tbl->size; i++) {
+ for (he = nft_dereference(tbl->buckets[i]); he != NULL;
+ he = next) {
+ next = nft_dereference(he->next);
+ nft_hash_elem_destroy(set, he);
}
}
- kfree(priv->hash);
+ kfree(tbl);
}
static struct nft_set_ops nft_hash_ops __read_mostly = {
diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c
index f169501f1ad4..810385eb7249 100644
--- a/net/netfilter/nft_immediate.c
+++ b/net/netfilter/nft_immediate.c
@@ -70,7 +70,8 @@ err1:
return err;
}
-static void nft_immediate_destroy(const struct nft_expr *expr)
+static void nft_immediate_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
{
const struct nft_immediate_expr *priv = nft_expr_priv(expr);
return nft_data_uninit(&priv->data, nft_dreg_to_type(priv->dreg));
diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c
index 26c5154e05f3..10cfb156cdf4 100644
--- a/net/netfilter/nft_log.c
+++ b/net/netfilter/nft_log.c
@@ -74,7 +74,8 @@ static int nft_log_init(const struct nft_ctx *ctx,
return 0;
}
-static void nft_log_destroy(const struct nft_expr *expr)
+static void nft_log_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
{
struct nft_log *priv = nft_expr_priv(expr);
diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c
index bb4ef4cccb6e..7fd2bea8aa23 100644
--- a/net/netfilter/nft_lookup.c
+++ b/net/netfilter/nft_lookup.c
@@ -89,11 +89,12 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
return 0;
}
-static void nft_lookup_destroy(const struct nft_expr *expr)
+static void nft_lookup_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
{
struct nft_lookup *priv = nft_expr_priv(expr);
- nf_tables_unbind_set(NULL, priv->set, &priv->binding);
+ nf_tables_unbind_set(ctx, priv->set, &priv->binding);
}
static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr)
diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c
index d3b1ffe26181..a0195d28bcfc 100644
--- a/net/netfilter/nft_nat.c
+++ b/net/netfilter/nft_nat.c
@@ -31,8 +31,8 @@ struct nft_nat {
enum nft_registers sreg_addr_max:8;
enum nft_registers sreg_proto_min:8;
enum nft_registers sreg_proto_max:8;
- int family;
- enum nf_nat_manip_type type;
+ enum nf_nat_manip_type type:8;
+ u8 family;
};
static void nft_nat_eval(const struct nft_expr *expr,
@@ -88,6 +88,7 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_nat *priv = nft_expr_priv(expr);
+ u32 family;
int err;
if (tb[NFTA_NAT_TYPE] == NULL)
@@ -107,9 +108,12 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
if (tb[NFTA_NAT_FAMILY] == NULL)
return -EINVAL;
- priv->family = ntohl(nla_get_be32(tb[NFTA_NAT_FAMILY]));
- if (priv->family != AF_INET && priv->family != AF_INET6)
- return -EINVAL;
+ family = ntohl(nla_get_be32(tb[NFTA_NAT_FAMILY]));
+ if (family != AF_INET && family != AF_INET6)
+ return -EAFNOSUPPORT;
+ if (family != ctx->afi->family)
+ return -EOPNOTSUPP;
+ priv->family = family;
if (tb[NFTA_NAT_REG_ADDR_MIN]) {
priv->sreg_addr_min = ntohl(nla_get_be32(
@@ -202,13 +206,7 @@ static struct nft_expr_type nft_nat_type __read_mostly = {
static int __init nft_nat_module_init(void)
{
- int err;
-
- err = nft_register_expr(&nft_nat_type);
- if (err < 0)
- return err;
-
- return 0;
+ return nft_register_expr(&nft_nat_type);
}
static void __exit nft_nat_module_exit(void)
diff --git a/net/netfilter/xt_AUDIT.c b/net/netfilter/xt_AUDIT.c
index 3228d7f24eb4..4973cbddc446 100644
--- a/net/netfilter/xt_AUDIT.c
+++ b/net/netfilter/xt_AUDIT.c
@@ -146,11 +146,11 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par)
if (par->family == NFPROTO_BRIDGE) {
switch (eth_hdr(skb)->h_proto) {
- case __constant_htons(ETH_P_IP):
+ case htons(ETH_P_IP):
audit_ip4(ab, skb);
break;
- case __constant_htons(ETH_P_IPV6):
+ case htons(ETH_P_IPV6):
audit_ip6(ab, skb);
break;
}
diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c
index c40b2695633b..458464e7bd7a 100644
--- a/net/netfilter/xt_connlimit.c
+++ b/net/netfilter/xt_connlimit.c
@@ -19,6 +19,7 @@
#include <linux/jhash.h>
#include <linux/slab.h>
#include <linux/list.h>
+#include <linux/rbtree.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/skbuff.h>
@@ -31,6 +32,10 @@
#include <net/netfilter/nf_conntrack_tuple.h>
#include <net/netfilter/nf_conntrack_zones.h>
+#define CONNLIMIT_SLOTS 32
+#define CONNLIMIT_LOCK_SLOTS 32
+#define CONNLIMIT_GC_MAX_NODES 8
+
/* we will save the tuples of all connections we care about */
struct xt_connlimit_conn {
struct hlist_node node;
@@ -38,16 +43,26 @@ struct xt_connlimit_conn {
union nf_inet_addr addr;
};
+struct xt_connlimit_rb {
+ struct rb_node node;
+ struct hlist_head hhead; /* connections/hosts in same subnet */
+ union nf_inet_addr addr; /* search key */
+};
+
struct xt_connlimit_data {
- struct hlist_head iphash[256];
- spinlock_t lock;
+ struct rb_root climit_root4[CONNLIMIT_SLOTS];
+ struct rb_root climit_root6[CONNLIMIT_SLOTS];
+ spinlock_t locks[CONNLIMIT_LOCK_SLOTS];
};
static u_int32_t connlimit_rnd __read_mostly;
+static struct kmem_cache *connlimit_rb_cachep __read_mostly;
+static struct kmem_cache *connlimit_conn_cachep __read_mostly;
static inline unsigned int connlimit_iphash(__be32 addr)
{
- return jhash_1word((__force __u32)addr, connlimit_rnd) & 0xFF;
+ return jhash_1word((__force __u32)addr,
+ connlimit_rnd) % CONNLIMIT_SLOTS;
}
static inline unsigned int
@@ -60,7 +75,8 @@ connlimit_iphash6(const union nf_inet_addr *addr,
for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i)
res.ip6[i] = addr->ip6[i] & mask->ip6[i];
- return jhash2((u32 *)res.ip6, ARRAY_SIZE(res.ip6), connlimit_rnd) & 0xFF;
+ return jhash2((u32 *)res.ip6, ARRAY_SIZE(res.ip6),
+ connlimit_rnd) % CONNLIMIT_SLOTS;
}
static inline bool already_closed(const struct nf_conn *conn)
@@ -72,13 +88,14 @@ static inline bool already_closed(const struct nf_conn *conn)
return 0;
}
-static inline unsigned int
+static int
same_source_net(const union nf_inet_addr *addr,
const union nf_inet_addr *mask,
const union nf_inet_addr *u3, u_int8_t family)
{
if (family == NFPROTO_IPV4) {
- return (addr->ip & mask->ip) == (u3->ip & mask->ip);
+ return ntohl(addr->ip & mask->ip) -
+ ntohl(u3->ip & mask->ip);
} else {
union nf_inet_addr lh, rh;
unsigned int i;
@@ -88,89 +105,205 @@ same_source_net(const union nf_inet_addr *addr,
rh.ip6[i] = u3->ip6[i] & mask->ip6[i];
}
- return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6)) == 0;
+ return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6));
}
}
-static int count_them(struct net *net,
- struct xt_connlimit_data *data,
+static bool add_hlist(struct hlist_head *head,
const struct nf_conntrack_tuple *tuple,
- const union nf_inet_addr *addr,
- const union nf_inet_addr *mask,
- u_int8_t family)
+ const union nf_inet_addr *addr)
+{
+ struct xt_connlimit_conn *conn;
+
+ conn = kmem_cache_alloc(connlimit_conn_cachep, GFP_ATOMIC);
+ if (conn == NULL)
+ return false;
+ conn->tuple = *tuple;
+ conn->addr = *addr;
+ hlist_add_head(&conn->node, head);
+ return true;
+}
+
+static unsigned int check_hlist(struct net *net,
+ struct hlist_head *head,
+ const struct nf_conntrack_tuple *tuple,
+ bool *addit)
{
const struct nf_conntrack_tuple_hash *found;
struct xt_connlimit_conn *conn;
struct hlist_node *n;
struct nf_conn *found_ct;
- struct hlist_head *hash;
- bool addit = true;
- int matches = 0;
-
- if (family == NFPROTO_IPV6)
- hash = &data->iphash[connlimit_iphash6(addr, mask)];
- else
- hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)];
+ unsigned int length = 0;
+ *addit = true;
rcu_read_lock();
/* check the saved connections */
- hlist_for_each_entry_safe(conn, n, hash, node) {
+ hlist_for_each_entry_safe(conn, n, head, node) {
found = nf_conntrack_find_get(net, NF_CT_DEFAULT_ZONE,
&conn->tuple);
- found_ct = NULL;
+ if (found == NULL) {
+ hlist_del(&conn->node);
+ kmem_cache_free(connlimit_conn_cachep, conn);
+ continue;
+ }
- if (found != NULL)
- found_ct = nf_ct_tuplehash_to_ctrack(found);
+ found_ct = nf_ct_tuplehash_to_ctrack(found);
- if (found_ct != NULL &&
- nf_ct_tuple_equal(&conn->tuple, tuple) &&
- !already_closed(found_ct))
+ if (nf_ct_tuple_equal(&conn->tuple, tuple)) {
/*
* Just to be sure we have it only once in the list.
* We should not see tuples twice unless someone hooks
* this into a table without "-p tcp --syn".
*/
- addit = false;
-
- if (found == NULL) {
- /* this one is gone */
- hlist_del(&conn->node);
- kfree(conn);
- continue;
- }
-
- if (already_closed(found_ct)) {
+ *addit = false;
+ } else if (already_closed(found_ct)) {
/*
* we do not care about connections which are
* closed already -> ditch it
*/
nf_ct_put(found_ct);
hlist_del(&conn->node);
- kfree(conn);
+ kmem_cache_free(connlimit_conn_cachep, conn);
continue;
}
- if (same_source_net(addr, mask, &conn->addr, family))
- /* same source network -> be counted! */
- ++matches;
nf_ct_put(found_ct);
+ length++;
}
rcu_read_unlock();
- if (addit) {
- /* save the new connection in our list */
- conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
- if (conn == NULL)
- return -ENOMEM;
- conn->tuple = *tuple;
- conn->addr = *addr;
- hlist_add_head(&conn->node, hash);
- ++matches;
+ return length;
+}
+
+static void tree_nodes_free(struct rb_root *root,
+ struct xt_connlimit_rb *gc_nodes[],
+ unsigned int gc_count)
+{
+ struct xt_connlimit_rb *rbconn;
+
+ while (gc_count) {
+ rbconn = gc_nodes[--gc_count];
+ rb_erase(&rbconn->node, root);
+ kmem_cache_free(connlimit_rb_cachep, rbconn);
+ }
+}
+
+static unsigned int
+count_tree(struct net *net, struct rb_root *root,
+ const struct nf_conntrack_tuple *tuple,
+ const union nf_inet_addr *addr, const union nf_inet_addr *mask,
+ u8 family)
+{
+ struct xt_connlimit_rb *gc_nodes[CONNLIMIT_GC_MAX_NODES];
+ struct rb_node **rbnode, *parent;
+ struct xt_connlimit_rb *rbconn;
+ struct xt_connlimit_conn *conn;
+ unsigned int gc_count;
+ bool no_gc = false;
+
+ restart:
+ gc_count = 0;
+ parent = NULL;
+ rbnode = &(root->rb_node);
+ while (*rbnode) {
+ int diff;
+ bool addit;
+
+ rbconn = container_of(*rbnode, struct xt_connlimit_rb, node);
+
+ parent = *rbnode;
+ diff = same_source_net(addr, mask, &rbconn->addr, family);
+ if (diff < 0) {
+ rbnode = &((*rbnode)->rb_left);
+ } else if (diff > 0) {
+ rbnode = &((*rbnode)->rb_right);
+ } else {
+ /* same source network -> be counted! */
+ unsigned int count;
+ count = check_hlist(net, &rbconn->hhead, tuple, &addit);
+
+ tree_nodes_free(root, gc_nodes, gc_count);
+ if (!addit)
+ return count;
+
+ if (!add_hlist(&rbconn->hhead, tuple, addr))
+ return 0; /* hotdrop */
+
+ return count + 1;
+ }
+
+ if (no_gc || gc_count >= ARRAY_SIZE(gc_nodes))
+ continue;
+
+ /* only used for GC on hhead, retval and 'addit' ignored */
+ check_hlist(net, &rbconn->hhead, tuple, &addit);
+ if (hlist_empty(&rbconn->hhead))
+ gc_nodes[gc_count++] = rbconn;
+ }
+
+ if (gc_count) {
+ no_gc = true;
+ tree_nodes_free(root, gc_nodes, gc_count);
+ /* tree_node_free before new allocation permits
+ * allocator to re-use newly free'd object.
+ *
+ * This is a rare event; in most cases we will find
+ * existing node to re-use. (or gc_count is 0).
+ */
+ goto restart;
+ }
+
+ /* no match, need to insert new node */
+ rbconn = kmem_cache_alloc(connlimit_rb_cachep, GFP_ATOMIC);
+ if (rbconn == NULL)
+ return 0;
+
+ conn = kmem_cache_alloc(connlimit_conn_cachep, GFP_ATOMIC);
+ if (conn == NULL) {
+ kmem_cache_free(connlimit_rb_cachep, rbconn);
+ return 0;
+ }
+
+ conn->tuple = *tuple;
+ conn->addr = *addr;
+ rbconn->addr = *addr;
+
+ INIT_HLIST_HEAD(&rbconn->hhead);
+ hlist_add_head(&conn->node, &rbconn->hhead);
+
+ rb_link_node(&rbconn->node, parent, rbnode);
+ rb_insert_color(&rbconn->node, root);
+ return 1;
+}
+
+static int count_them(struct net *net,
+ struct xt_connlimit_data *data,
+ const struct nf_conntrack_tuple *tuple,
+ const union nf_inet_addr *addr,
+ const union nf_inet_addr *mask,
+ u_int8_t family)
+{
+ struct rb_root *root;
+ int count;
+ u32 hash;
+
+ if (family == NFPROTO_IPV6) {
+ hash = connlimit_iphash6(addr, mask);
+ root = &data->climit_root6[hash];
+ } else {
+ hash = connlimit_iphash(addr->ip & mask->ip);
+ root = &data->climit_root4[hash];
}
- return matches;
+ spin_lock_bh(&data->locks[hash % CONNLIMIT_LOCK_SLOTS]);
+
+ count = count_tree(net, root, tuple, addr, mask, family);
+
+ spin_unlock_bh(&data->locks[hash % CONNLIMIT_LOCK_SLOTS]);
+
+ return count;
}
static bool
@@ -183,7 +316,7 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
const struct nf_conntrack_tuple *tuple_ptr = &tuple;
enum ip_conntrack_info ctinfo;
const struct nf_conn *ct;
- int connections;
+ unsigned int connections;
ct = nf_ct_get(skb, &ctinfo);
if (ct != NULL)
@@ -202,12 +335,9 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
iph->daddr : iph->saddr;
}
- spin_lock_bh(&info->data->lock);
connections = count_them(net, info->data, tuple_ptr, &addr,
&info->mask, par->family);
- spin_unlock_bh(&info->data->lock);
-
- if (connections < 0)
+ if (connections == 0)
/* kmalloc failed, drop it entirely */
goto hotdrop;
@@ -247,29 +377,47 @@ static int connlimit_mt_check(const struct xt_mtchk_param *par)
return -ENOMEM;
}
- spin_lock_init(&info->data->lock);
- for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
- INIT_HLIST_HEAD(&info->data->iphash[i]);
+ for (i = 0; i < ARRAY_SIZE(info->data->locks); ++i)
+ spin_lock_init(&info->data->locks[i]);
+
+ for (i = 0; i < ARRAY_SIZE(info->data->climit_root4); ++i)
+ info->data->climit_root4[i] = RB_ROOT;
+ for (i = 0; i < ARRAY_SIZE(info->data->climit_root6); ++i)
+ info->data->climit_root6[i] = RB_ROOT;
return 0;
}
-static void connlimit_mt_destroy(const struct xt_mtdtor_param *par)
+static void destroy_tree(struct rb_root *r)
{
- const struct xt_connlimit_info *info = par->matchinfo;
struct xt_connlimit_conn *conn;
+ struct xt_connlimit_rb *rbconn;
struct hlist_node *n;
- struct hlist_head *hash = info->data->iphash;
+ struct rb_node *node;
+
+ while ((node = rb_first(r)) != NULL) {
+ rbconn = container_of(node, struct xt_connlimit_rb, node);
+
+ rb_erase(node, r);
+
+ hlist_for_each_entry_safe(conn, n, &rbconn->hhead, node)
+ kmem_cache_free(connlimit_conn_cachep, conn);
+
+ kmem_cache_free(connlimit_rb_cachep, rbconn);
+ }
+}
+
+static void connlimit_mt_destroy(const struct xt_mtdtor_param *par)
+{
+ const struct xt_connlimit_info *info = par->matchinfo;
unsigned int i;
nf_ct_l3proto_module_put(par->family);
- for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
- hlist_for_each_entry_safe(conn, n, &hash[i], node) {
- hlist_del(&conn->node);
- kfree(conn);
- }
- }
+ for (i = 0; i < ARRAY_SIZE(info->data->climit_root4); ++i)
+ destroy_tree(&info->data->climit_root4[i]);
+ for (i = 0; i < ARRAY_SIZE(info->data->climit_root6); ++i)
+ destroy_tree(&info->data->climit_root6[i]);
kfree(info->data);
}
@@ -287,12 +435,37 @@ static struct xt_match connlimit_mt_reg __read_mostly = {
static int __init connlimit_mt_init(void)
{
- return xt_register_match(&connlimit_mt_reg);
+ int ret;
+
+ BUILD_BUG_ON(CONNLIMIT_LOCK_SLOTS > CONNLIMIT_SLOTS);
+ BUILD_BUG_ON((CONNLIMIT_SLOTS % CONNLIMIT_LOCK_SLOTS) != 0);
+
+ connlimit_conn_cachep = kmem_cache_create("xt_connlimit_conn",
+ sizeof(struct xt_connlimit_conn),
+ 0, 0, NULL);
+ if (!connlimit_conn_cachep)
+ return -ENOMEM;
+
+ connlimit_rb_cachep = kmem_cache_create("xt_connlimit_rb",
+ sizeof(struct xt_connlimit_rb),
+ 0, 0, NULL);
+ if (!connlimit_rb_cachep) {
+ kmem_cache_destroy(connlimit_conn_cachep);
+ return -ENOMEM;
+ }
+ ret = xt_register_match(&connlimit_mt_reg);
+ if (ret != 0) {
+ kmem_cache_destroy(connlimit_conn_cachep);
+ kmem_cache_destroy(connlimit_rb_cachep);
+ }
+ return ret;
}
static void __exit connlimit_mt_exit(void)
{
xt_unregister_match(&connlimit_mt_reg);
+ kmem_cache_destroy(connlimit_conn_cachep);
+ kmem_cache_destroy(connlimit_rb_cachep);
}
module_init(connlimit_mt_init);
diff --git a/net/netfilter/xt_ipcomp.c b/net/netfilter/xt_ipcomp.c
index a4c7561698c5..89d53104c6b3 100644
--- a/net/netfilter/xt_ipcomp.c
+++ b/net/netfilter/xt_ipcomp.c
@@ -60,7 +60,7 @@ static bool comp_mt(const struct sk_buff *skb, struct xt_action_param *par)
}
return spi_match(compinfo->spis[0], compinfo->spis[1],
- ntohl(chdr->cpi << 16),
+ ntohs(chdr->cpi),
!!(compinfo->invflags & XT_IPCOMP_INV_SPI));
}
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 04748ab649c2..c2d585c4f7c5 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1460,7 +1460,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
if (nlk->netlink_bind && nlk->groups[0]) {
int i;
- for (i=0; i<nlk->ngroups; i++) {
+ for (i = 0; i < nlk->ngroups; i++) {
if (test_bit(i, nlk->groups))
nlk->netlink_bind(i);
}
@@ -2343,6 +2343,11 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
}
#endif
+ /* Record the max length of recvmsg() calls for future allocations */
+ nlk->max_recvmsg_len = max(nlk->max_recvmsg_len, len);
+ nlk->max_recvmsg_len = min_t(size_t, nlk->max_recvmsg_len,
+ 16384);
+
copied = data_skb->len;
if (len < copied) {
msg->msg_flags |= MSG_TRUNC;
@@ -2549,7 +2554,7 @@ __nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, int type, int len, int fla
struct nlmsghdr *nlh;
int size = nlmsg_msg_size(len);
- nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size));
+ nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_ALIGN(size));
nlh->nlmsg_type = type;
nlh->nlmsg_len = size;
nlh->nlmsg_flags = flags;
@@ -2587,7 +2592,27 @@ static int netlink_dump(struct sock *sk)
if (!netlink_rx_is_mmaped(sk) &&
atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
goto errout_skb;
- skb = netlink_alloc_skb(sk, alloc_size, nlk->portid, GFP_KERNEL);
+
+ /* NLMSG_GOODSIZE is small to avoid high order allocations being
+ * required, but it makes sense to _attempt_ a 16K bytes allocation
+ * to reduce number of system calls on dump operations, if user
+ * ever provided a big enough buffer.
+ */
+ if (alloc_size < nlk->max_recvmsg_len) {
+ skb = netlink_alloc_skb(sk,
+ nlk->max_recvmsg_len,
+ nlk->portid,
+ GFP_KERNEL |
+ __GFP_NOWARN |
+ __GFP_NORETRY);
+ /* available room should be exact amount to avoid MSG_TRUNC */
+ if (skb)
+ skb_reserve(skb, skb_tailroom(skb) -
+ nlk->max_recvmsg_len);
+ }
+ if (!skb)
+ skb = netlink_alloc_skb(sk, alloc_size, nlk->portid,
+ GFP_KERNEL);
if (!skb)
goto errout_skb;
netlink_skb_set_owner_r(skb, sk);
diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h
index acbd774eeb7c..ed13a790b00e 100644
--- a/net/netlink/af_netlink.h
+++ b/net/netlink/af_netlink.h
@@ -31,6 +31,7 @@ struct netlink_sock {
u32 ngroups;
unsigned long *groups;
unsigned long state;
+ size_t max_recvmsg_len;
wait_queue_head_t wait;
bool cb_running;
struct netlink_callback cb;
diff --git a/net/nfc/core.c b/net/nfc/core.c
index ca1e65f4b133..819b87702b70 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -280,9 +280,6 @@ static struct nfc_target *nfc_find_target(struct nfc_dev *dev, u32 target_idx)
{
int i;
- if (dev->n_targets == 0)
- return NULL;
-
for (i = 0; i < dev->n_targets; i++) {
if (dev->targets[i].idx == target_idx)
return &dev->targets[i];
@@ -546,9 +543,9 @@ error:
struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx)
{
- struct nfc_se *se, *n;
+ struct nfc_se *se;
- list_for_each_entry_safe(se, n, &dev->secure_elements, list)
+ list_for_each_entry(se, &dev->secure_elements, list)
if (se->idx == se_idx)
return se;
@@ -655,9 +652,6 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len)
{
pr_debug("dev_name=%s gb_len=%d\n", dev_name(&dev->dev), gb_len);
- if (gb_len > NFC_MAX_GT_LEN)
- return -EINVAL;
-
return nfc_llcp_set_remote_gb(dev, gb, gb_len);
}
EXPORT_SYMBOL(nfc_set_remote_general_bytes);
diff --git a/net/nfc/digital.h b/net/nfc/digital.h
index 08b29b55ea63..3759add68b1b 100644
--- a/net/nfc/digital.h
+++ b/net/nfc/digital.h
@@ -72,6 +72,12 @@ void digital_poll_next_tech(struct nfc_digital_dev *ddev);
int digital_in_send_sens_req(struct nfc_digital_dev *ddev, u8 rf_tech);
int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech);
+int digital_in_send_iso15693_inv_req(struct nfc_digital_dev *ddev, u8 rf_tech);
+
+int digital_in_iso_dep_pull_sod(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb);
+int digital_in_iso_dep_push_sod(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb);
int digital_target_found(struct nfc_digital_dev *ddev,
struct nfc_target *target, u8 protocol);
diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c
index c129d1571ca6..e01e15dbf1ab 100644
--- a/net/nfc/digital_core.c
+++ b/net/nfc/digital_core.c
@@ -25,6 +25,8 @@
#define DIGITAL_PROTO_NFCF_RF_TECH \
(NFC_PROTO_FELICA_MASK | NFC_PROTO_NFC_DEP_MASK)
+#define DIGITAL_PROTO_ISO15693_RF_TECH NFC_PROTO_ISO15693_MASK
+
struct digital_cmd {
struct list_head queue;
@@ -331,6 +333,18 @@ int digital_target_found(struct nfc_digital_dev *ddev,
}
break;
+ case NFC_PROTO_ISO15693:
+ framing = NFC_DIGITAL_FRAMING_ISO15693_T5T;
+ check_crc = digital_skb_check_crc_b;
+ add_crc = digital_skb_add_crc_b;
+ break;
+
+ case NFC_PROTO_ISO14443:
+ framing = NFC_DIGITAL_FRAMING_NFCA_T4T;
+ check_crc = digital_skb_check_crc_a;
+ add_crc = digital_skb_add_crc_a;
+ break;
+
default:
pr_err("Invalid protocol %d\n", protocol);
return -EINVAL;
@@ -461,7 +475,7 @@ static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols,
digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A,
digital_in_send_sens_req);
- if (im_protocols & DIGITAL_PROTO_NFCF_RF_TECH) {
+ if (matching_im_protocols & DIGITAL_PROTO_NFCF_RF_TECH) {
digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F,
digital_in_send_sensf_req);
@@ -469,7 +483,11 @@ static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols,
digital_in_send_sensf_req);
}
- if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ if (matching_im_protocols & DIGITAL_PROTO_ISO15693_RF_TECH)
+ digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_ISO15693,
+ digital_in_send_iso15693_inv_req);
+
+ if (matching_tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
if (ddev->ops->tg_listen_mdaa) {
digital_add_poll_tech(ddev, 0,
digital_tg_listen_mdaa);
@@ -607,20 +625,30 @@ static void digital_in_send_complete(struct nfc_digital_dev *ddev, void *arg,
if (IS_ERR(resp)) {
rc = PTR_ERR(resp);
+ resp = NULL;
goto done;
}
- if (ddev->curr_protocol == NFC_PROTO_MIFARE)
+ if (ddev->curr_protocol == NFC_PROTO_MIFARE) {
rc = digital_in_recv_mifare_res(resp);
- else
- rc = ddev->skb_check_crc(resp);
+ /* crc check is done in digital_in_recv_mifare_res() */
+ goto done;
+ }
+ if (ddev->curr_protocol == NFC_PROTO_ISO14443) {
+ rc = digital_in_iso_dep_pull_sod(ddev, resp);
+ if (rc)
+ goto done;
+ }
+
+ rc = ddev->skb_check_crc(resp);
+
+done:
if (rc) {
kfree_skb(resp);
resp = NULL;
}
-done:
data_exch->cb(data_exch->cb_context, resp, rc);
kfree(data_exch);
@@ -632,6 +660,7 @@ static int digital_in_send(struct nfc_dev *nfc_dev, struct nfc_target *target,
{
struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
struct digital_data_exch *data_exch;
+ int rc;
data_exch = kzalloc(sizeof(struct digital_data_exch), GFP_KERNEL);
if (!data_exch) {
@@ -642,13 +671,27 @@ static int digital_in_send(struct nfc_dev *nfc_dev, struct nfc_target *target,
data_exch->cb = cb;
data_exch->cb_context = cb_context;
- if (ddev->curr_protocol == NFC_PROTO_NFC_DEP)
- return digital_in_send_dep_req(ddev, target, skb, data_exch);
+ if (ddev->curr_protocol == NFC_PROTO_NFC_DEP) {
+ rc = digital_in_send_dep_req(ddev, target, skb, data_exch);
+ goto exit;
+ }
+
+ if (ddev->curr_protocol == NFC_PROTO_ISO14443) {
+ rc = digital_in_iso_dep_push_sod(ddev, skb);
+ if (rc)
+ goto exit;
+ }
ddev->skb_add_crc(skb);
- return digital_in_send_cmd(ddev, skb, 500, digital_in_send_complete,
- data_exch);
+ rc = digital_in_send_cmd(ddev, skb, 500, digital_in_send_complete,
+ data_exch);
+
+exit:
+ if (rc)
+ kfree(data_exch);
+
+ return rc;
}
static struct nfc_ops digital_nfc_ops = {
@@ -700,6 +743,10 @@ struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops,
ddev->protocols |= NFC_PROTO_FELICA_MASK;
if (supported_protocols & NFC_PROTO_NFC_DEP_MASK)
ddev->protocols |= NFC_PROTO_NFC_DEP_MASK;
+ if (supported_protocols & NFC_PROTO_ISO15693_MASK)
+ ddev->protocols |= NFC_PROTO_ISO15693_MASK;
+ if (supported_protocols & NFC_PROTO_ISO14443_MASK)
+ ddev->protocols |= NFC_PROTO_ISO14443_MASK;
ddev->tx_headroom = tx_headroom + DIGITAL_MAX_HEADER_LEN;
ddev->tx_tailroom = tx_tailroom + DIGITAL_CRC_LEN;
diff --git a/net/nfc/digital_technology.c b/net/nfc/digital_technology.c
index 251c8c753ebe..278c3fed27e0 100644
--- a/net/nfc/digital_technology.c
+++ b/net/nfc/digital_technology.c
@@ -30,6 +30,7 @@
#define DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res) (!((sel_res) & 0x04))
#define DIGITAL_SEL_RES_IS_T2T(sel_res) (!((sel_res) & 0x60))
+#define DIGITAL_SEL_RES_IS_T4T(sel_res) ((sel_res) & 0x20)
#define DIGITAL_SEL_RES_IS_NFC_DEP(sel_res) ((sel_res) & 0x40)
#define DIGITAL_SENS_RES_IS_T1T(sens_res) (((sens_res) & 0x0C00) == 0x0C00)
@@ -51,6 +52,34 @@
#define DIGITAL_SENSF_REQ_RC_SC 1
#define DIGITAL_SENSF_REQ_RC_AP 2
+#define DIGITAL_CMD_ISO15693_INVENTORY_REQ 0x01
+
+#define DIGITAL_ISO15693_REQ_FLAG_DATA_RATE BIT(1)
+#define DIGITAL_ISO15693_REQ_FLAG_INVENTORY BIT(2)
+#define DIGITAL_ISO15693_REQ_FLAG_NB_SLOTS BIT(5)
+#define DIGITAL_ISO15693_RES_FLAG_ERROR BIT(0)
+#define DIGITAL_ISO15693_RES_IS_VALID(flags) \
+ (!((flags) & DIGITAL_ISO15693_RES_FLAG_ERROR))
+
+#define DIGITAL_ISO_DEP_I_PCB 0x02
+#define DIGITAL_ISO_DEP_PNI(pni) ((pni) & 0x01)
+
+#define DIGITAL_ISO_DEP_PCB_TYPE(pcb) ((pcb) & 0xC0)
+
+#define DIGITAL_ISO_DEP_I_BLOCK 0x00
+
+#define DIGITAL_ISO_DEP_BLOCK_HAS_DID(pcb) ((pcb) & 0x08)
+
+static const u8 digital_ats_fsc[] = {
+ 16, 24, 32, 40, 48, 64, 96, 128,
+};
+
+#define DIGITAL_ATS_FSCI(t0) ((t0) & 0x0F)
+#define DIGITAL_ATS_MAX_FSC 256
+
+#define DIGITAL_RATS_BYTE1 0xE0
+#define DIGITAL_RATS_PARAM 0x80
+
struct digital_sdd_res {
u8 nfcid1[4];
u8 bcc;
@@ -82,9 +111,127 @@ struct digital_sensf_res {
u8 rd[2];
} __packed;
+struct digital_iso15693_inv_req {
+ u8 flags;
+ u8 cmd;
+ u8 mask_len;
+ u64 mask;
+} __packed;
+
+struct digital_iso15693_inv_res {
+ u8 flags;
+ u8 dsfid;
+ u64 uid;
+} __packed;
+
static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev,
struct nfc_target *target);
+int digital_in_iso_dep_pull_sod(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb)
+{
+ u8 pcb;
+ u8 block_type;
+
+ if (skb->len < 1)
+ return -EIO;
+
+ pcb = *skb->data;
+ block_type = DIGITAL_ISO_DEP_PCB_TYPE(pcb);
+
+ /* No support fo R-block nor S-block */
+ if (block_type != DIGITAL_ISO_DEP_I_BLOCK) {
+ pr_err("ISO_DEP R-block and S-block not supported\n");
+ return -EIO;
+ }
+
+ if (DIGITAL_ISO_DEP_BLOCK_HAS_DID(pcb)) {
+ pr_err("DID field in ISO_DEP PCB not supported\n");
+ return -EIO;
+ }
+
+ skb_pull(skb, 1);
+
+ return 0;
+}
+
+int digital_in_iso_dep_push_sod(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb)
+{
+ /*
+ * Chaining not supported so skb->len + 1 PCB byte + 2 CRC bytes must
+ * not be greater than remote FSC
+ */
+ if (skb->len + 3 > ddev->target_fsc)
+ return -EIO;
+
+ skb_push(skb, 1);
+
+ *skb->data = DIGITAL_ISO_DEP_I_PCB | ddev->curr_nfc_dep_pni;
+
+ ddev->curr_nfc_dep_pni =
+ DIGITAL_ISO_DEP_PNI(ddev->curr_nfc_dep_pni + 1);
+
+ return 0;
+}
+
+static void digital_in_recv_ats(struct nfc_digital_dev *ddev, void *arg,
+ struct sk_buff *resp)
+{
+ struct nfc_target *target = arg;
+ u8 fsdi;
+ int rc;
+
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+ resp = NULL;
+ goto exit;
+ }
+
+ if (resp->len < 2) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ fsdi = DIGITAL_ATS_FSCI(resp->data[1]);
+ if (fsdi >= 8)
+ ddev->target_fsc = DIGITAL_ATS_MAX_FSC;
+ else
+ ddev->target_fsc = digital_ats_fsc[fsdi];
+
+ ddev->curr_nfc_dep_pni = 0;
+
+ rc = digital_target_found(ddev, target, NFC_PROTO_ISO14443);
+
+exit:
+ dev_kfree_skb(resp);
+ kfree(target);
+
+ if (rc)
+ digital_poll_next_tech(ddev);
+}
+
+static int digital_in_send_rats(struct nfc_digital_dev *ddev,
+ struct nfc_target *target)
+{
+ int rc;
+ struct sk_buff *skb;
+
+ skb = digital_skb_alloc(ddev, 2);
+ if (!skb)
+ return -ENOMEM;
+
+ *skb_put(skb, 1) = DIGITAL_RATS_BYTE1;
+ *skb_put(skb, 1) = DIGITAL_RATS_PARAM;
+
+ rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_ats,
+ target);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
+}
+
static void digital_in_recv_sel_res(struct nfc_digital_dev *ddev, void *arg,
struct sk_buff *resp)
{
@@ -122,8 +269,19 @@ static void digital_in_recv_sel_res(struct nfc_digital_dev *ddev, void *arg,
goto exit_free_skb;
}
+ target->sel_res = sel_res;
+
if (DIGITAL_SEL_RES_IS_T2T(sel_res)) {
nfc_proto = NFC_PROTO_MIFARE;
+ } else if (DIGITAL_SEL_RES_IS_T4T(sel_res)) {
+ rc = digital_in_send_rats(ddev, target);
+ if (rc)
+ goto exit;
+ /*
+ * Skip target_found and don't free it for now. This will be
+ * done when receiving the ATS
+ */
+ goto exit_free_skb;
} else if (DIGITAL_SEL_RES_IS_NFC_DEP(sel_res)) {
nfc_proto = NFC_PROTO_NFC_DEP;
} else {
@@ -131,8 +289,6 @@ static void digital_in_recv_sel_res(struct nfc_digital_dev *ddev, void *arg,
goto exit;
}
- target->sel_res = sel_res;
-
rc = digital_target_found(ddev, target, nfc_proto);
exit:
@@ -473,6 +629,93 @@ int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech)
return rc;
}
+static void digital_in_recv_iso15693_inv_res(struct nfc_digital_dev *ddev,
+ void *arg, struct sk_buff *resp)
+{
+ struct digital_iso15693_inv_res *res;
+ struct nfc_target *target = NULL;
+ int rc;
+
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+ resp = NULL;
+ goto out_free_skb;
+ }
+
+ if (resp->len != sizeof(*res)) {
+ rc = -EIO;
+ goto out_free_skb;
+ }
+
+ res = (struct digital_iso15693_inv_res *)resp->data;
+
+ if (!DIGITAL_ISO15693_RES_IS_VALID(res->flags)) {
+ PROTOCOL_ERR("ISO15693 - 10.3.1");
+ rc = -EINVAL;
+ goto out_free_skb;
+ }
+
+ target = kzalloc(sizeof(*target), GFP_KERNEL);
+ if (!target) {
+ rc = -ENOMEM;
+ goto out_free_skb;
+ }
+
+ target->is_iso15693 = 1;
+ target->iso15693_dsfid = res->dsfid;
+ memcpy(target->iso15693_uid, &res->uid, sizeof(target->iso15693_uid));
+
+ rc = digital_target_found(ddev, target, NFC_PROTO_ISO15693);
+
+ kfree(target);
+
+out_free_skb:
+ dev_kfree_skb(resp);
+
+ if (rc)
+ digital_poll_next_tech(ddev);
+}
+
+int digital_in_send_iso15693_inv_req(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+ struct digital_iso15693_inv_req *req;
+ struct sk_buff *skb;
+ int rc;
+
+ rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH,
+ NFC_DIGITAL_RF_TECH_ISO15693);
+ if (rc)
+ return rc;
+
+ rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+ NFC_DIGITAL_FRAMING_ISO15693_INVENTORY);
+ if (rc)
+ return rc;
+
+ skb = digital_skb_alloc(ddev, sizeof(*req));
+ if (!skb)
+ return -ENOMEM;
+
+ skb_put(skb, sizeof(*req) - sizeof(req->mask)); /* No mask */
+ req = (struct digital_iso15693_inv_req *)skb->data;
+
+ /* Single sub-carrier, high data rate, no AFI, single slot
+ * Inventory command
+ */
+ req->flags = DIGITAL_ISO15693_REQ_FLAG_DATA_RATE |
+ DIGITAL_ISO15693_REQ_FLAG_INVENTORY |
+ DIGITAL_ISO15693_REQ_FLAG_NB_SLOTS;
+ req->cmd = DIGITAL_CMD_ISO15693_INVENTORY_REQ;
+ req->mask_len = 0;
+
+ rc = digital_in_send_cmd(ddev, skb, 30,
+ digital_in_recv_iso15693_inv_res, NULL);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
+}
+
static int digital_tg_send_sel_res(struct nfc_digital_dev *ddev)
{
struct sk_buff *skb;
diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c
index a07d2b818487..1b90c0531852 100644
--- a/net/nfc/hci/llc.c
+++ b/net/nfc/hci/llc.c
@@ -20,14 +20,12 @@
#include "llc.h"
-static struct list_head llc_engines;
+static LIST_HEAD(llc_engines);
int nfc_llc_init(void)
{
int r;
- INIT_LIST_HEAD(&llc_engines);
-
r = nfc_llc_nop_register();
if (r)
goto exit;
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index 6184bd1fba3a..b486f12ae243 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -27,7 +27,7 @@
static u8 llcp_magic[3] = {0x46, 0x66, 0x6d};
-static struct list_head llcp_devices;
+static LIST_HEAD(llcp_devices);
static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb);
@@ -293,9 +293,9 @@ static void nfc_llcp_sdreq_timer(unsigned long data)
struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
{
- struct nfc_llcp_local *local, *n;
+ struct nfc_llcp_local *local;
- list_for_each_entry_safe(local, n, &llcp_devices, list)
+ list_for_each_entry(local, &llcp_devices, list)
if (local->dev == dev)
return local;
@@ -609,14 +609,16 @@ u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)
int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len)
{
- struct nfc_llcp_local *local = nfc_llcp_find_local(dev);
+ struct nfc_llcp_local *local;
+
+ if (gb_len < 3 || gb_len > NFC_MAX_GT_LEN)
+ return -EINVAL;
+ local = nfc_llcp_find_local(dev);
if (local == NULL) {
pr_err("No LLCP device\n");
return -ENODEV;
}
- if (gb_len < 3)
- return -EINVAL;
memset(local->remote_gb, 0, NFC_MAX_GT_LEN);
memcpy(local->remote_gb, gb, gb_len);
@@ -1622,8 +1624,6 @@ void nfc_llcp_unregister_device(struct nfc_dev *dev)
int __init nfc_llcp_init(void)
{
- INIT_LIST_HEAD(&llcp_devices);
-
return nfc_llcp_sock_init();
}
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 56db888b1cd5..6c34ac978501 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -74,7 +74,7 @@ static int __nci_request(struct nci_dev *ndev,
ndev->req_status = NCI_REQ_PEND;
- init_completion(&ndev->req_completion);
+ reinit_completion(&ndev->req_completion);
req(ndev, opt);
completion_rc =
wait_for_completion_interruptible_timeout(&ndev->req_completion,
@@ -709,6 +709,7 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,
ndev->ops = ops;
ndev->tx_headroom = tx_headroom;
ndev->tx_tailroom = tx_tailroom;
+ init_completion(&ndev->req_completion);
ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
supported_protocols,
diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c
index f1d426f10cce..ec250e77763a 100644
--- a/net/nfc/nci/spi.c
+++ b/net/nfc/nci/spi.c
@@ -105,7 +105,7 @@ int nci_spi_send(struct nci_spi *nspi,
if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED)
goto done;
- init_completion(&nspi->req_completion);
+ reinit_completion(&nspi->req_completion);
completion_rc = wait_for_completion_interruptible_timeout(
&nspi->req_completion,
NCI_SPI_SEND_TIMEOUT);
@@ -145,6 +145,7 @@ struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
nspi->spi = spi;
nspi->ndev = ndev;
+ init_completion(&nspi->req_completion);
return nspi;
}
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index ebbf6fb88b35..43cb1c17e267 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -94,6 +94,14 @@ static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
target->sensf_res))
goto nla_put_failure;
+ if (target->is_iso15693) {
+ if (nla_put_u8(msg, NFC_ATTR_TARGET_ISO15693_DSFID,
+ target->iso15693_dsfid) ||
+ nla_put(msg, NFC_ATTR_TARGET_ISO15693_UID,
+ sizeof(target->iso15693_uid), target->iso15693_uid))
+ goto nla_put_failure;
+ }
+
return genlmsg_end(msg, hdr);
nla_put_failure:
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 270b77dfac30..a3276e3c4feb 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -256,10 +256,10 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
out:
/* Update datapath statistics. */
- u64_stats_update_begin(&stats->sync);
+ u64_stats_update_begin(&stats->syncp);
(*stats_counter)++;
stats->n_mask_hit += n_mask_hit;
- u64_stats_update_end(&stats->sync);
+ u64_stats_update_end(&stats->syncp);
}
static struct genl_family dp_packet_genl_family = {
@@ -295,9 +295,9 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
err:
stats = this_cpu_ptr(dp->stats_percpu);
- u64_stats_update_begin(&stats->sync);
+ u64_stats_update_begin(&stats->syncp);
stats->n_lost++;
- u64_stats_update_end(&stats->sync);
+ u64_stats_update_end(&stats->syncp);
return err;
}
@@ -610,9 +610,9 @@ static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats,
percpu_stats = per_cpu_ptr(dp->stats_percpu, i);
do {
- start = u64_stats_fetch_begin_bh(&percpu_stats->sync);
+ start = u64_stats_fetch_begin_irq(&percpu_stats->syncp);
local_stats = *percpu_stats;
- } while (u64_stats_fetch_retry_bh(&percpu_stats->sync, start));
+ } while (u64_stats_fetch_retry_irq(&percpu_stats->syncp, start));
stats->n_hit += local_stats.n_hit;
stats->n_missed += local_stats.n_missed;
@@ -1219,18 +1219,12 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
if (err)
goto err_free_dp;
- dp->stats_percpu = alloc_percpu(struct dp_stats_percpu);
+ dp->stats_percpu = netdev_alloc_pcpu_stats(struct dp_stats_percpu);
if (!dp->stats_percpu) {
err = -ENOMEM;
goto err_destroy_table;
}
- for_each_possible_cpu(i) {
- struct dp_stats_percpu *dpath_stats;
- dpath_stats = per_cpu_ptr(dp->stats_percpu, i);
- u64_stats_init(&dpath_stats->sync);
- }
-
dp->ports = kmalloc(DP_VPORT_HASH_BUCKETS * sizeof(struct hlist_head),
GFP_KERNEL);
if (!dp->ports) {
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h
index 6be9fbb5e9cb..05317380fc03 100644
--- a/net/openvswitch/datapath.h
+++ b/net/openvswitch/datapath.h
@@ -55,7 +55,7 @@ struct dp_stats_percpu {
u64 n_missed;
u64 n_lost;
u64 n_mask_hit;
- struct u64_stats_sync sync;
+ struct u64_stats_sync syncp;
};
/**
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 208dd9a26dd1..42c0f4a0b78c 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -121,7 +121,6 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
{
struct vport *vport;
size_t alloc_size;
- int i;
alloc_size = sizeof(struct vport);
if (priv_size) {
@@ -139,19 +138,12 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
vport->ops = ops;
INIT_HLIST_NODE(&vport->dp_hash_node);
- vport->percpu_stats = alloc_percpu(struct pcpu_sw_netstats);
+ vport->percpu_stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!vport->percpu_stats) {
kfree(vport);
return ERR_PTR(-ENOMEM);
}
- for_each_possible_cpu(i) {
- struct pcpu_sw_netstats *vport_stats;
- vport_stats = per_cpu_ptr(vport->percpu_stats, i);
- u64_stats_init(&vport_stats->syncp);
- }
-
-
spin_lock_init(&vport->stats_lock);
return vport;
@@ -285,9 +277,9 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
percpu_stats = per_cpu_ptr(vport->percpu_stats, i);
do {
- start = u64_stats_fetch_begin_bh(&percpu_stats->syncp);
+ start = u64_stats_fetch_begin_irq(&percpu_stats->syncp);
local_stats = *percpu_stats;
- } while (u64_stats_fetch_retry_bh(&percpu_stats->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&percpu_stats->syncp, start));
stats->rx_bytes += local_stats.rx_bytes;
stats->rx_packets += local_stats.rx_packets;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 48a6a93db296..01039d2b1695 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -243,40 +243,40 @@ static int packet_direct_xmit(struct sk_buff *skb)
const struct net_device_ops *ops = dev->netdev_ops;
netdev_features_t features;
struct netdev_queue *txq;
+ int ret = NETDEV_TX_BUSY;
u16 queue_map;
- int ret;
if (unlikely(!netif_running(dev) ||
- !netif_carrier_ok(dev))) {
- kfree_skb(skb);
- return NET_XMIT_DROP;
- }
+ !netif_carrier_ok(dev)))
+ goto drop;
features = netif_skb_features(skb);
if (skb_needs_linearize(skb, features) &&
- __skb_linearize(skb)) {
- kfree_skb(skb);
- return NET_XMIT_DROP;
- }
+ __skb_linearize(skb))
+ goto drop;
queue_map = skb_get_queue_mapping(skb);
txq = netdev_get_tx_queue(dev, queue_map);
- __netif_tx_lock_bh(txq);
- if (unlikely(netif_xmit_frozen_or_stopped(txq))) {
- ret = NETDEV_TX_BUSY;
- kfree_skb(skb);
- goto out;
+ local_bh_disable();
+
+ HARD_TX_LOCK(dev, txq, smp_processor_id());
+ if (!netif_xmit_frozen_or_stopped(txq)) {
+ ret = ops->ndo_start_xmit(skb, dev);
+ if (ret == NETDEV_TX_OK)
+ txq_trans_update(txq);
}
+ HARD_TX_UNLOCK(dev, txq);
- ret = ops->ndo_start_xmit(skb, dev);
- if (likely(dev_xmit_complete(ret)))
- txq_trans_update(txq);
- else
+ local_bh_enable();
+
+ if (!dev_xmit_complete(ret))
kfree_skb(skb);
-out:
- __netif_tx_unlock_bh(txq);
+
return ret;
+drop:
+ kfree_skb(skb);
+ return NET_XMIT_DROP;
}
static struct net_device *packet_cached_dev_get(struct packet_sock *po)
@@ -1277,7 +1277,7 @@ static unsigned int fanout_demux_hash(struct packet_fanout *f,
struct sk_buff *skb,
unsigned int num)
{
- return reciprocal_scale(skb->rxhash, num);
+ return reciprocal_scale(skb_get_hash(skb), num);
}
static unsigned int fanout_demux_lb(struct packet_fanout *f,
@@ -1362,7 +1362,6 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev,
if (!skb)
return 0;
}
- skb_get_hash(skb);
idx = fanout_demux_hash(f, skb, num);
break;
case PACKET_FANOUT_LB:
@@ -2257,8 +2256,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
if (unlikely(!(dev->flags & IFF_UP)))
goto out_put;
- reserve = dev->hard_header_len;
-
+ reserve = dev->hard_header_len + VLAN_HLEN;
size_max = po->tx_ring.frame_size
- (po->tp_hdrlen - sizeof(struct sockaddr_ll));
@@ -2285,8 +2283,19 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
goto out_status;
tp_len = tpacket_fill_skb(po, skb, ph, dev, size_max, proto,
- addr, hlen);
+ addr, hlen);
+ if (tp_len > dev->mtu + dev->hard_header_len) {
+ struct ethhdr *ehdr;
+ /* Earlier code assumed this would be a VLAN pkt,
+ * double-check this now that we have the actual
+ * packet in hand.
+ */
+ skb_reset_mac_header(skb);
+ ehdr = eth_hdr(skb);
+ if (ehdr->h_proto != htons(ETH_P_8021Q))
+ tp_len = -EMSGSIZE;
+ }
if (unlikely(tp_len < 0)) {
if (po->tp_loss) {
__packet_set_status(po, ph,
diff --git a/net/rds/iw.c b/net/rds/iw.c
index 7826d46baa70..589935661d66 100644
--- a/net/rds/iw.c
+++ b/net/rds/iw.c
@@ -239,7 +239,8 @@ static int rds_iw_laddr_check(__be32 addr)
ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin);
/* due to this, we will claim to support IB devices unless we
check node_type. */
- if (ret || cm_id->device->node_type != RDMA_NODE_RNIC)
+ if (ret || !cm_id->device ||
+ cm_id->device->node_type != RDMA_NODE_RNIC)
ret = -EADDRNOTAVAIL;
rdsdebug("addr %pI4 ret %d node type %d\n",
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index ed7e0b4e7f90..b3b16c070a7f 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -789,7 +789,8 @@ void rfkill_resume_polling(struct rfkill *rfkill)
if (!rfkill->ops->poll)
return;
- schedule_work(&rfkill->poll_work.work);
+ queue_delayed_work(system_power_efficient_wq,
+ &rfkill->poll_work, 0);
}
EXPORT_SYMBOL(rfkill_resume_polling);
@@ -894,7 +895,8 @@ static void rfkill_poll(struct work_struct *work)
*/
rfkill->ops->poll(rfkill, rfkill->data);
- schedule_delayed_work(&rfkill->poll_work,
+ queue_delayed_work(system_power_efficient_wq,
+ &rfkill->poll_work,
round_jiffies_relative(POLL_INTERVAL));
}
@@ -958,7 +960,8 @@ int __must_check rfkill_register(struct rfkill *rfkill)
INIT_WORK(&rfkill->sync_work, rfkill_sync_work);
if (rfkill->ops->poll)
- schedule_delayed_work(&rfkill->poll_work,
+ queue_delayed_work(system_power_efficient_wq,
+ &rfkill->poll_work,
round_jiffies_relative(POLL_INTERVAL));
if (!rfkill->persistent || rfkill_epo_lock_active) {
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile
index d1c3429b69ed..ec126f91276b 100644
--- a/net/rxrpc/Makefile
+++ b/net/rxrpc/Makefile
@@ -20,9 +20,8 @@ af-rxrpc-y := \
ar-skbuff.o \
ar-transport.o
-ifeq ($(CONFIG_PROC_FS),y)
-af-rxrpc-y += ar-proc.o
-endif
+af-rxrpc-$(CONFIG_PROC_FS) += ar-proc.o
+af-rxrpc-$(CONFIG_SYSCTL) += sysctl.o
obj-$(CONFIG_AF_RXRPC) += af-rxrpc.o
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index e61aa6001c65..7b1670489638 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -838,6 +838,12 @@ static int __init af_rxrpc_init(void)
goto error_key_type_s;
}
+ ret = rxrpc_sysctl_init();
+ if (ret < 0) {
+ printk(KERN_CRIT "RxRPC: Cannot register sysctls\n");
+ goto error_sysctls;
+ }
+
#ifdef CONFIG_PROC_FS
proc_create("rxrpc_calls", 0, init_net.proc_net, &rxrpc_call_seq_fops);
proc_create("rxrpc_conns", 0, init_net.proc_net,
@@ -845,6 +851,8 @@ static int __init af_rxrpc_init(void)
#endif
return 0;
+error_sysctls:
+ unregister_key_type(&key_type_rxrpc_s);
error_key_type_s:
unregister_key_type(&key_type_rxrpc);
error_key_type:
@@ -865,6 +873,7 @@ error_call_jar:
static void __exit af_rxrpc_exit(void)
{
_enter("");
+ rxrpc_sysctl_exit();
unregister_key_type(&key_type_rxrpc_s);
unregister_key_type(&key_type_rxrpc);
sock_unregister(PF_RXRPC);
diff --git a/net/rxrpc/ar-ack.c b/net/rxrpc/ar-ack.c
index cd97a0ce48d8..c6be17a959a6 100644
--- a/net/rxrpc/ar-ack.c
+++ b/net/rxrpc/ar-ack.c
@@ -19,7 +19,49 @@
#include <net/af_rxrpc.h>
#include "ar-internal.h"
-static unsigned int rxrpc_ack_defer = 1;
+/*
+ * How long to wait before scheduling ACK generation after seeing a
+ * packet with RXRPC_REQUEST_ACK set (in jiffies).
+ */
+unsigned rxrpc_requested_ack_delay = 1;
+
+/*
+ * How long to wait before scheduling an ACK with subtype DELAY (in jiffies).
+ *
+ * We use this when we've received new data packets. If those packets aren't
+ * all consumed within this time we will send a DELAY ACK if an ACK was not
+ * requested to let the sender know it doesn't need to resend.
+ */
+unsigned rxrpc_soft_ack_delay = 1 * HZ;
+
+/*
+ * How long to wait before scheduling an ACK with subtype IDLE (in jiffies).
+ *
+ * We use this when we've consumed some previously soft-ACK'd packets when
+ * further packets aren't immediately received to decide when to send an IDLE
+ * ACK let the other end know that it can free up its Tx buffer space.
+ */
+unsigned rxrpc_idle_ack_delay = 0.5 * HZ;
+
+/*
+ * Receive window size in packets. This indicates the maximum number of
+ * unconsumed received packets we're willing to retain in memory. Once this
+ * limit is hit, we should generate an EXCEEDS_WINDOW ACK and discard further
+ * packets.
+ */
+unsigned rxrpc_rx_window_size = 32;
+
+/*
+ * Maximum Rx MTU size. This indicates to the sender the size of jumbo packet
+ * made by gluing normal packets together that we're willing to handle.
+ */
+unsigned rxrpc_rx_mtu = 5692;
+
+/*
+ * The maximum number of fragments in a received jumbo packet that we tell the
+ * sender that we're willing to handle.
+ */
+unsigned rxrpc_rx_jumbo_max = 4;
static const char *rxrpc_acks(u8 reason)
{
@@ -82,24 +124,23 @@ void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
switch (ack_reason) {
case RXRPC_ACK_DELAY:
_debug("run delay timer");
- call->ack_timer.expires = jiffies + rxrpc_ack_timeout * HZ;
- add_timer(&call->ack_timer);
- return;
+ expiry = rxrpc_soft_ack_delay;
+ goto run_timer;
case RXRPC_ACK_IDLE:
if (!immediate) {
_debug("run defer timer");
- expiry = 1;
+ expiry = rxrpc_idle_ack_delay;
goto run_timer;
}
goto cancel_timer;
case RXRPC_ACK_REQUESTED:
- if (!rxrpc_ack_defer)
+ expiry = rxrpc_requested_ack_delay;
+ if (!expiry)
goto cancel_timer;
if (!immediate || serial == cpu_to_be32(1)) {
_debug("run defer timer");
- expiry = rxrpc_ack_defer;
goto run_timer;
}
@@ -1174,11 +1215,11 @@ send_ACK:
mtu = call->conn->trans->peer->if_mtu;
mtu -= call->conn->trans->peer->hdrsize;
ackinfo.maxMTU = htonl(mtu);
- ackinfo.rwind = htonl(32);
+ ackinfo.rwind = htonl(rxrpc_rx_window_size);
/* permit the peer to send us jumbo packets if it wants to */
- ackinfo.rxMTU = htonl(5692);
- ackinfo.jumbo_max = htonl(4);
+ ackinfo.rxMTU = htonl(rxrpc_rx_mtu);
+ ackinfo.jumbo_max = htonl(rxrpc_rx_jumbo_max);
hdr.serial = htonl(atomic_inc_return(&call->conn->serial));
_proto("Tx ACK %%%u { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }",
diff --git a/net/rxrpc/ar-call.c b/net/rxrpc/ar-call.c
index a3bbb360a3f9..a9e05db0f5d5 100644
--- a/net/rxrpc/ar-call.c
+++ b/net/rxrpc/ar-call.c
@@ -12,10 +12,22 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/circ_buf.h>
+#include <linux/hashtable.h>
+#include <linux/spinlock_types.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
#include "ar-internal.h"
+/*
+ * Maximum lifetime of a call (in jiffies).
+ */
+unsigned rxrpc_max_call_lifetime = 60 * HZ;
+
+/*
+ * Time till dead call expires after last use (in jiffies).
+ */
+unsigned rxrpc_dead_call_expiry = 2 * HZ;
+
const char *const rxrpc_call_states[] = {
[RXRPC_CALL_CLIENT_SEND_REQUEST] = "ClSndReq",
[RXRPC_CALL_CLIENT_AWAIT_REPLY] = "ClAwtRpl",
@@ -38,8 +50,6 @@ const char *const rxrpc_call_states[] = {
struct kmem_cache *rxrpc_call_jar;
LIST_HEAD(rxrpc_calls);
DEFINE_RWLOCK(rxrpc_call_lock);
-static unsigned int rxrpc_call_max_lifetime = 60;
-static unsigned int rxrpc_dead_call_timeout = 2;
static void rxrpc_destroy_call(struct work_struct *work);
static void rxrpc_call_life_expired(unsigned long _call);
@@ -47,6 +57,145 @@ static void rxrpc_dead_call_expired(unsigned long _call);
static void rxrpc_ack_time_expired(unsigned long _call);
static void rxrpc_resend_time_expired(unsigned long _call);
+static DEFINE_SPINLOCK(rxrpc_call_hash_lock);
+static DEFINE_HASHTABLE(rxrpc_call_hash, 10);
+
+/*
+ * Hash function for rxrpc_call_hash
+ */
+static unsigned long rxrpc_call_hashfunc(
+ u8 clientflag,
+ __be32 cid,
+ __be32 call_id,
+ __be32 epoch,
+ __be16 service_id,
+ sa_family_t proto,
+ void *localptr,
+ unsigned int addr_size,
+ const u8 *peer_addr)
+{
+ const u16 *p;
+ unsigned int i;
+ unsigned long key;
+ u32 hcid = ntohl(cid);
+
+ _enter("");
+
+ key = (unsigned long)localptr;
+ /* We just want to add up the __be32 values, so forcing the
+ * cast should be okay.
+ */
+ key += (__force u32)epoch;
+ key += (__force u16)service_id;
+ key += (__force u32)call_id;
+ key += (hcid & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT;
+ key += hcid & RXRPC_CHANNELMASK;
+ key += clientflag;
+ key += proto;
+ /* Step through the peer address in 16-bit portions for speed */
+ for (i = 0, p = (const u16 *)peer_addr; i < addr_size >> 1; i++, p++)
+ key += *p;
+ _leave(" key = 0x%lx", key);
+ return key;
+}
+
+/*
+ * Add a call to the hashtable
+ */
+static void rxrpc_call_hash_add(struct rxrpc_call *call)
+{
+ unsigned long key;
+ unsigned int addr_size = 0;
+
+ _enter("");
+ switch (call->proto) {
+ case AF_INET:
+ addr_size = sizeof(call->peer_ip.ipv4_addr);
+ break;
+ case AF_INET6:
+ addr_size = sizeof(call->peer_ip.ipv6_addr);
+ break;
+ default:
+ break;
+ }
+ key = rxrpc_call_hashfunc(call->in_clientflag, call->cid,
+ call->call_id, call->epoch,
+ call->service_id, call->proto,
+ call->conn->trans->local, addr_size,
+ call->peer_ip.ipv6_addr);
+ /* Store the full key in the call */
+ call->hash_key = key;
+ spin_lock(&rxrpc_call_hash_lock);
+ hash_add_rcu(rxrpc_call_hash, &call->hash_node, key);
+ spin_unlock(&rxrpc_call_hash_lock);
+ _leave("");
+}
+
+/*
+ * Remove a call from the hashtable
+ */
+static void rxrpc_call_hash_del(struct rxrpc_call *call)
+{
+ _enter("");
+ spin_lock(&rxrpc_call_hash_lock);
+ hash_del_rcu(&call->hash_node);
+ spin_unlock(&rxrpc_call_hash_lock);
+ _leave("");
+}
+
+/*
+ * Find a call in the hashtable and return it, or NULL if it
+ * isn't there.
+ */
+struct rxrpc_call *rxrpc_find_call_hash(
+ u8 clientflag,
+ __be32 cid,
+ __be32 call_id,
+ __be32 epoch,
+ __be16 service_id,
+ void *localptr,
+ sa_family_t proto,
+ const u8 *peer_addr)
+{
+ unsigned long key;
+ unsigned int addr_size = 0;
+ struct rxrpc_call *call = NULL;
+ struct rxrpc_call *ret = NULL;
+
+ _enter("");
+ switch (proto) {
+ case AF_INET:
+ addr_size = sizeof(call->peer_ip.ipv4_addr);
+ break;
+ case AF_INET6:
+ addr_size = sizeof(call->peer_ip.ipv6_addr);
+ break;
+ default:
+ break;
+ }
+
+ key = rxrpc_call_hashfunc(clientflag, cid, call_id, epoch,
+ service_id, proto, localptr, addr_size,
+ peer_addr);
+ hash_for_each_possible_rcu(rxrpc_call_hash, call, hash_node, key) {
+ if (call->hash_key == key &&
+ call->call_id == call_id &&
+ call->cid == cid &&
+ call->in_clientflag == clientflag &&
+ call->service_id == service_id &&
+ call->proto == proto &&
+ call->local == localptr &&
+ memcmp(call->peer_ip.ipv6_addr, peer_addr,
+ addr_size) == 0 &&
+ call->epoch == epoch) {
+ ret = call;
+ break;
+ }
+ }
+ _leave(" = %p", ret);
+ return ret;
+}
+
/*
* allocate a new call
*/
@@ -91,7 +240,7 @@ static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp)
call->rx_data_expect = 1;
call->rx_data_eaten = 0;
call->rx_first_oos = 0;
- call->ackr_win_top = call->rx_data_eaten + 1 + RXRPC_MAXACKS;
+ call->ackr_win_top = call->rx_data_eaten + 1 + rxrpc_rx_window_size;
call->creation_jif = jiffies;
return call;
}
@@ -128,11 +277,31 @@ static struct rxrpc_call *rxrpc_alloc_client_call(
return ERR_PTR(ret);
}
+ /* Record copies of information for hashtable lookup */
+ call->proto = rx->proto;
+ call->local = trans->local;
+ switch (call->proto) {
+ case AF_INET:
+ call->peer_ip.ipv4_addr =
+ trans->peer->srx.transport.sin.sin_addr.s_addr;
+ break;
+ case AF_INET6:
+ memcpy(call->peer_ip.ipv6_addr,
+ trans->peer->srx.transport.sin6.sin6_addr.in6_u.u6_addr8,
+ sizeof(call->peer_ip.ipv6_addr));
+ break;
+ }
+ call->epoch = call->conn->epoch;
+ call->service_id = call->conn->service_id;
+ call->in_clientflag = call->conn->in_clientflag;
+ /* Add the new call to the hashtable */
+ rxrpc_call_hash_add(call);
+
spin_lock(&call->conn->trans->peer->lock);
list_add(&call->error_link, &call->conn->trans->peer->error_targets);
spin_unlock(&call->conn->trans->peer->lock);
- call->lifetimer.expires = jiffies + rxrpc_call_max_lifetime * HZ;
+ call->lifetimer.expires = jiffies + rxrpc_max_call_lifetime;
add_timer(&call->lifetimer);
_leave(" = %p", call);
@@ -320,9 +489,12 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx,
parent = *p;
call = rb_entry(parent, struct rxrpc_call, conn_node);
- if (call_id < call->call_id)
+ /* The tree is sorted in order of the __be32 value without
+ * turning it into host order.
+ */
+ if ((__force u32)call_id < (__force u32)call->call_id)
p = &(*p)->rb_left;
- else if (call_id > call->call_id)
+ else if ((__force u32)call_id > (__force u32)call->call_id)
p = &(*p)->rb_right;
else
goto old_call;
@@ -347,9 +519,31 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx,
list_add_tail(&call->link, &rxrpc_calls);
write_unlock_bh(&rxrpc_call_lock);
+ /* Record copies of information for hashtable lookup */
+ call->proto = rx->proto;
+ call->local = conn->trans->local;
+ switch (call->proto) {
+ case AF_INET:
+ call->peer_ip.ipv4_addr =
+ conn->trans->peer->srx.transport.sin.sin_addr.s_addr;
+ break;
+ case AF_INET6:
+ memcpy(call->peer_ip.ipv6_addr,
+ conn->trans->peer->srx.transport.sin6.sin6_addr.in6_u.u6_addr8,
+ sizeof(call->peer_ip.ipv6_addr));
+ break;
+ default:
+ break;
+ }
+ call->epoch = conn->epoch;
+ call->service_id = conn->service_id;
+ call->in_clientflag = conn->in_clientflag;
+ /* Add the new call to the hashtable */
+ rxrpc_call_hash_add(call);
+
_net("CALL incoming %d on CONN %d", call->debug_id, call->conn->debug_id);
- call->lifetimer.expires = jiffies + rxrpc_call_max_lifetime * HZ;
+ call->lifetimer.expires = jiffies + rxrpc_max_call_lifetime;
add_timer(&call->lifetimer);
_leave(" = %p {%d} [new]", call, call->debug_id);
return call;
@@ -533,7 +727,7 @@ void rxrpc_release_call(struct rxrpc_call *call)
del_timer_sync(&call->resend_timer);
del_timer_sync(&call->ack_timer);
del_timer_sync(&call->lifetimer);
- call->deadspan.expires = jiffies + rxrpc_dead_call_timeout * HZ;
+ call->deadspan.expires = jiffies + rxrpc_dead_call_expiry;
add_timer(&call->deadspan);
_leave("");
@@ -665,6 +859,9 @@ static void rxrpc_cleanup_call(struct rxrpc_call *call)
rxrpc_put_connection(call->conn);
}
+ /* Remove the call from the hash */
+ rxrpc_call_hash_del(call);
+
if (call->acks_window) {
_debug("kill Tx window %d",
CIRC_CNT(call->acks_head, call->acks_tail,
diff --git a/net/rxrpc/ar-connection.c b/net/rxrpc/ar-connection.c
index 7bf5b5b9e8b9..6631f4f1e39b 100644
--- a/net/rxrpc/ar-connection.c
+++ b/net/rxrpc/ar-connection.c
@@ -18,11 +18,15 @@
#include <net/af_rxrpc.h>
#include "ar-internal.h"
+/*
+ * Time till a connection expires after last use (in seconds).
+ */
+unsigned rxrpc_connection_expiry = 10 * 60;
+
static void rxrpc_connection_reaper(struct work_struct *work);
LIST_HEAD(rxrpc_connections);
DEFINE_RWLOCK(rxrpc_connection_lock);
-static unsigned long rxrpc_connection_timeout = 10 * 60;
static DECLARE_DELAYED_WORK(rxrpc_connection_reap, rxrpc_connection_reaper);
/*
@@ -862,7 +866,7 @@ static void rxrpc_connection_reaper(struct work_struct *work)
spin_lock(&conn->trans->client_lock);
write_lock(&conn->trans->conn_lock);
- reap_time = conn->put_time + rxrpc_connection_timeout;
+ reap_time = conn->put_time + rxrpc_connection_expiry;
if (atomic_read(&conn->usage) > 0) {
;
@@ -916,7 +920,7 @@ void __exit rxrpc_destroy_all_connections(void)
{
_enter("");
- rxrpc_connection_timeout = 0;
+ rxrpc_connection_expiry = 0;
cancel_delayed_work(&rxrpc_connection_reap);
rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0);
diff --git a/net/rxrpc/ar-error.c b/net/rxrpc/ar-error.c
index a9206087b4d7..db57458c824c 100644
--- a/net/rxrpc/ar-error.c
+++ b/net/rxrpc/ar-error.c
@@ -83,6 +83,7 @@ void rxrpc_UDP_error_report(struct sock *sk)
if (mtu == 0) {
/* they didn't give us a size, estimate one */
+ mtu = peer->if_mtu;
if (mtu > 1500) {
mtu >>= 1;
if (mtu < 1500)
diff --git a/net/rxrpc/ar-input.c b/net/rxrpc/ar-input.c
index 529572f18d1f..73742647c135 100644
--- a/net/rxrpc/ar-input.c
+++ b/net/rxrpc/ar-input.c
@@ -25,8 +25,6 @@
#include <net/net_namespace.h>
#include "ar-internal.h"
-unsigned long rxrpc_ack_timeout = 1;
-
const char *rxrpc_pkts[] = {
"?00",
"DATA", "ACK", "BUSY", "ABORT", "ACKALL", "CHALL", "RESP", "DEBUG",
@@ -349,8 +347,7 @@ void rxrpc_fast_process_packet(struct rxrpc_call *call, struct sk_buff *skb)
* it */
if (sp->hdr.flags & RXRPC_REQUEST_ACK) {
_proto("ACK Requested on %%%u", serial);
- rxrpc_propose_ACK(call, RXRPC_ACK_REQUESTED, sp->hdr.serial,
- !(sp->hdr.flags & RXRPC_MORE_PACKETS));
+ rxrpc_propose_ACK(call, RXRPC_ACK_REQUESTED, sp->hdr.serial, false);
}
switch (sp->hdr.type) {
@@ -526,36 +523,38 @@ protocol_error:
* post an incoming packet to the appropriate call/socket to deal with
* - must get rid of the sk_buff, either by freeing it or by queuing it
*/
-static void rxrpc_post_packet_to_call(struct rxrpc_connection *conn,
+static void rxrpc_post_packet_to_call(struct rxrpc_call *call,
struct sk_buff *skb)
{
struct rxrpc_skb_priv *sp;
- struct rxrpc_call *call;
- struct rb_node *p;
- __be32 call_id;
-
- _enter("%p,%p", conn, skb);
- read_lock_bh(&conn->lock);
+ _enter("%p,%p", call, skb);
sp = rxrpc_skb(skb);
- /* look at extant calls by channel number first */
- call = conn->channels[ntohl(sp->hdr.cid) & RXRPC_CHANNELMASK];
- if (!call || call->call_id != sp->hdr.callNumber)
- goto call_not_extant;
-
_debug("extant call [%d]", call->state);
- ASSERTCMP(call->conn, ==, conn);
read_lock(&call->state_lock);
switch (call->state) {
case RXRPC_CALL_LOCALLY_ABORTED:
- if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events))
+ if (!test_and_set_bit(RXRPC_CALL_ABORT, &call->events)) {
rxrpc_queue_call(call);
+ goto free_unlock;
+ }
case RXRPC_CALL_REMOTELY_ABORTED:
case RXRPC_CALL_NETWORK_ERROR:
case RXRPC_CALL_DEAD:
+ goto dead_call;
+ case RXRPC_CALL_COMPLETE:
+ case RXRPC_CALL_CLIENT_FINAL_ACK:
+ /* complete server call */
+ if (call->conn->in_clientflag)
+ goto dead_call;
+ /* resend last packet of a completed call */
+ _debug("final ack again");
+ rxrpc_get_call(call);
+ set_bit(RXRPC_CALL_ACK_FINAL, &call->events);
+ rxrpc_queue_call(call);
goto free_unlock;
default:
break;
@@ -563,7 +562,6 @@ static void rxrpc_post_packet_to_call(struct rxrpc_connection *conn,
read_unlock(&call->state_lock);
rxrpc_get_call(call);
- read_unlock_bh(&conn->lock);
if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA &&
sp->hdr.flags & RXRPC_JUMBO_PACKET)
@@ -574,78 +572,16 @@ static void rxrpc_post_packet_to_call(struct rxrpc_connection *conn,
rxrpc_put_call(call);
goto done;
-call_not_extant:
- /* search the completed calls in case what we're dealing with is
- * there */
- _debug("call not extant");
-
- call_id = sp->hdr.callNumber;
- p = conn->calls.rb_node;
- while (p) {
- call = rb_entry(p, struct rxrpc_call, conn_node);
-
- if (call_id < call->call_id)
- p = p->rb_left;
- else if (call_id > call->call_id)
- p = p->rb_right;
- else
- goto found_completed_call;
- }
-
dead_call:
- /* it's a either a really old call that we no longer remember or its a
- * new incoming call */
- read_unlock_bh(&conn->lock);
-
- if (sp->hdr.flags & RXRPC_CLIENT_INITIATED &&
- sp->hdr.seq == cpu_to_be32(1)) {
- _debug("incoming call");
- skb_queue_tail(&conn->trans->local->accept_queue, skb);
- rxrpc_queue_work(&conn->trans->local->acceptor);
- goto done;
- }
-
- _debug("dead call");
- skb->priority = RX_CALL_DEAD;
- rxrpc_reject_packet(conn->trans->local, skb);
- goto done;
-
- /* resend last packet of a completed call
- * - client calls may have been aborted or ACK'd
- * - server calls may have been aborted
- */
-found_completed_call:
- _debug("completed call");
-
- if (atomic_read(&call->usage) == 0)
- goto dead_call;
-
- /* synchronise any state changes */
- read_lock(&call->state_lock);
- ASSERTIFCMP(call->state != RXRPC_CALL_CLIENT_FINAL_ACK,
- call->state, >=, RXRPC_CALL_COMPLETE);
-
- if (call->state == RXRPC_CALL_LOCALLY_ABORTED ||
- call->state == RXRPC_CALL_REMOTELY_ABORTED ||
- call->state == RXRPC_CALL_DEAD) {
- read_unlock(&call->state_lock);
- goto dead_call;
- }
-
- if (call->conn->in_clientflag) {
- read_unlock(&call->state_lock);
- goto dead_call; /* complete server call */
+ if (sp->hdr.type != RXRPC_PACKET_TYPE_ABORT) {
+ skb->priority = RX_CALL_DEAD;
+ rxrpc_reject_packet(call->conn->trans->local, skb);
+ goto unlock;
}
-
- _debug("final ack again");
- rxrpc_get_call(call);
- set_bit(RXRPC_CALL_ACK_FINAL, &call->events);
- rxrpc_queue_call(call);
-
free_unlock:
- read_unlock(&call->state_lock);
- read_unlock_bh(&conn->lock);
rxrpc_free_skb(skb);
+unlock:
+ read_unlock(&call->state_lock);
done:
_leave("");
}
@@ -664,17 +600,42 @@ static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn,
rxrpc_queue_conn(conn);
}
+static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local,
+ struct sk_buff *skb,
+ struct rxrpc_skb_priv *sp)
+{
+ struct rxrpc_peer *peer;
+ struct rxrpc_transport *trans;
+ struct rxrpc_connection *conn;
+
+ peer = rxrpc_find_peer(local, ip_hdr(skb)->saddr,
+ udp_hdr(skb)->source);
+ if (IS_ERR(peer))
+ goto cant_find_conn;
+
+ trans = rxrpc_find_transport(local, peer);
+ rxrpc_put_peer(peer);
+ if (!trans)
+ goto cant_find_conn;
+
+ conn = rxrpc_find_connection(trans, &sp->hdr);
+ rxrpc_put_transport(trans);
+ if (!conn)
+ goto cant_find_conn;
+
+ return conn;
+cant_find_conn:
+ return NULL;
+}
+
/*
* handle data received on the local endpoint
* - may be called in interrupt context
*/
void rxrpc_data_ready(struct sock *sk, int count)
{
- struct rxrpc_connection *conn;
- struct rxrpc_transport *trans;
struct rxrpc_skb_priv *sp;
struct rxrpc_local *local;
- struct rxrpc_peer *peer;
struct sk_buff *skb;
int ret;
@@ -749,27 +710,34 @@ void rxrpc_data_ready(struct sock *sk, int count)
(sp->hdr.callNumber == 0 || sp->hdr.seq == 0))
goto bad_message;
- peer = rxrpc_find_peer(local, ip_hdr(skb)->saddr, udp_hdr(skb)->source);
- if (IS_ERR(peer))
- goto cant_route_call;
+ if (sp->hdr.callNumber == 0) {
+ /* This is a connection-level packet. These should be
+ * fairly rare, so the extra overhead of looking them up the
+ * old-fashioned way doesn't really hurt */
+ struct rxrpc_connection *conn;
- trans = rxrpc_find_transport(local, peer);
- rxrpc_put_peer(peer);
- if (!trans)
- goto cant_route_call;
+ conn = rxrpc_conn_from_local(local, skb, sp);
+ if (!conn)
+ goto cant_route_call;
- conn = rxrpc_find_connection(trans, &sp->hdr);
- rxrpc_put_transport(trans);
- if (!conn)
- goto cant_route_call;
-
- _debug("CONN %p {%d}", conn, conn->debug_id);
-
- if (sp->hdr.callNumber == 0)
+ _debug("CONN %p {%d}", conn, conn->debug_id);
rxrpc_post_packet_to_conn(conn, skb);
- else
- rxrpc_post_packet_to_call(conn, skb);
- rxrpc_put_connection(conn);
+ rxrpc_put_connection(conn);
+ } else {
+ struct rxrpc_call *call;
+ u8 in_clientflag = 0;
+
+ if (sp->hdr.flags & RXRPC_CLIENT_INITIATED)
+ in_clientflag = RXRPC_CLIENT_INITIATED;
+ call = rxrpc_find_call_hash(in_clientflag, sp->hdr.cid,
+ sp->hdr.callNumber, sp->hdr.epoch,
+ sp->hdr.serviceId, local, AF_INET,
+ (u8 *)&ip_hdr(skb)->saddr);
+ if (call)
+ rxrpc_post_packet_to_call(call, skb);
+ else
+ goto cant_route_call;
+ }
rxrpc_put_local(local);
return;
@@ -790,8 +758,10 @@ cant_route_call:
skb->priority = RX_CALL_DEAD;
}
- _debug("reject");
- rxrpc_reject_packet(local, skb);
+ if (sp->hdr.type != RXRPC_PACKET_TYPE_ABORT) {
+ _debug("reject type %d",sp->hdr.type);
+ rxrpc_reject_packet(local, skb);
+ }
rxrpc_put_local(local);
_leave(" [no call]");
return;
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 5f43675ee1df..c831d44b0841 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -396,9 +396,20 @@ struct rxrpc_call {
#define RXRPC_ACKR_WINDOW_ASZ DIV_ROUND_UP(RXRPC_MAXACKS, BITS_PER_LONG)
unsigned long ackr_window[RXRPC_ACKR_WINDOW_ASZ + 1];
+ struct hlist_node hash_node;
+ unsigned long hash_key; /* Full hash key */
+ u8 in_clientflag; /* Copy of conn->in_clientflag for hashing */
+ struct rxrpc_local *local; /* Local endpoint. Used for hashing. */
+ sa_family_t proto; /* Frame protocol */
/* the following should all be in net order */
__be32 cid; /* connection ID + channel index */
__be32 call_id; /* call ID on connection */
+ __be32 epoch; /* epoch of this connection */
+ __be16 service_id; /* service ID */
+ union { /* Peer IP address for hashing */
+ __be32 ipv4_addr;
+ __u8 ipv6_addr[16]; /* Anticipates eventual IPv6 support */
+ } peer_ip;
};
/*
@@ -433,6 +444,13 @@ int rxrpc_reject_call(struct rxrpc_sock *);
/*
* ar-ack.c
*/
+extern unsigned rxrpc_requested_ack_delay;
+extern unsigned rxrpc_soft_ack_delay;
+extern unsigned rxrpc_idle_ack_delay;
+extern unsigned rxrpc_rx_window_size;
+extern unsigned rxrpc_rx_mtu;
+extern unsigned rxrpc_rx_jumbo_max;
+
void __rxrpc_propose_ACK(struct rxrpc_call *, u8, __be32, bool);
void rxrpc_propose_ACK(struct rxrpc_call *, u8, __be32, bool);
void rxrpc_process_call(struct work_struct *);
@@ -440,10 +458,14 @@ void rxrpc_process_call(struct work_struct *);
/*
* ar-call.c
*/
+extern unsigned rxrpc_max_call_lifetime;
+extern unsigned rxrpc_dead_call_expiry;
extern struct kmem_cache *rxrpc_call_jar;
extern struct list_head rxrpc_calls;
extern rwlock_t rxrpc_call_lock;
+struct rxrpc_call *rxrpc_find_call_hash(u8, __be32, __be32, __be32,
+ __be16, void *, sa_family_t, const u8 *);
struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *,
struct rxrpc_transport *,
struct rxrpc_conn_bundle *,
@@ -460,6 +482,7 @@ void __exit rxrpc_destroy_all_calls(void);
/*
* ar-connection.c
*/
+extern unsigned rxrpc_connection_expiry;
extern struct list_head rxrpc_connections;
extern rwlock_t rxrpc_connection_lock;
@@ -493,7 +516,6 @@ void rxrpc_UDP_error_handler(struct work_struct *);
/*
* ar-input.c
*/
-extern unsigned long rxrpc_ack_timeout;
extern const char *rxrpc_pkts[];
void rxrpc_data_ready(struct sock *, int);
@@ -504,6 +526,7 @@ void rxrpc_fast_process_packet(struct rxrpc_call *, struct sk_buff *);
* ar-local.c
*/
extern rwlock_t rxrpc_local_lock;
+
struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *);
void rxrpc_put_local(struct rxrpc_local *);
void __exit rxrpc_destroy_all_locals(void);
@@ -522,7 +545,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *, time_t,
/*
* ar-output.c
*/
-extern int rxrpc_resend_timeout;
+extern unsigned rxrpc_resend_timeout;
int rxrpc_send_packet(struct rxrpc_transport *, struct sk_buff *);
int rxrpc_client_sendmsg(struct kiocb *, struct rxrpc_sock *,
@@ -572,6 +595,8 @@ void rxrpc_packet_destructor(struct sk_buff *);
/*
* ar-transport.c
*/
+extern unsigned rxrpc_transport_expiry;
+
struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *,
struct rxrpc_peer *, gfp_t);
void rxrpc_put_transport(struct rxrpc_transport *);
@@ -580,6 +605,17 @@ struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *,
struct rxrpc_peer *);
/*
+ * sysctl.c
+ */
+#ifdef CONFIG_SYSCTL
+extern int __init rxrpc_sysctl_init(void);
+extern void rxrpc_sysctl_exit(void);
+#else
+static inline int __init rxrpc_sysctl_init(void) { return 0; }
+static inline void rxrpc_sysctl_exit(void) {}
+#endif
+
+/*
* debug tracing
*/
extern unsigned int rxrpc_debug;
diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c
index d0e8f1c1898a..0b4b9a79f5ab 100644
--- a/net/rxrpc/ar-output.c
+++ b/net/rxrpc/ar-output.c
@@ -18,7 +18,10 @@
#include <net/af_rxrpc.h>
#include "ar-internal.h"
-int rxrpc_resend_timeout = 4;
+/*
+ * Time till packet resend (in jiffies).
+ */
+unsigned rxrpc_resend_timeout = 4 * HZ;
static int rxrpc_send_data(struct kiocb *iocb,
struct rxrpc_sock *rx,
@@ -487,7 +490,7 @@ static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb,
ntohl(sp->hdr.serial), ntohl(sp->hdr.seq));
sp->need_resend = false;
- sp->resend_at = jiffies + rxrpc_resend_timeout * HZ;
+ sp->resend_at = jiffies + rxrpc_resend_timeout;
if (!test_and_set_bit(RXRPC_CALL_RUN_RTIMER, &call->flags)) {
_debug("run timer");
call->resend_timer.expires = sp->resend_at;
@@ -666,6 +669,7 @@ static int rxrpc_send_data(struct kiocb *iocb,
/* add the packet to the send queue if it's now full */
if (sp->remain <= 0 || (segment == 0 && !more)) {
struct rxrpc_connection *conn = call->conn;
+ uint32_t seq;
size_t pad;
/* pad out if we're using security */
@@ -678,11 +682,12 @@ static int rxrpc_send_data(struct kiocb *iocb,
memset(skb_put(skb, pad), 0, pad);
}
+ seq = atomic_inc_return(&call->sequence);
+
sp->hdr.epoch = conn->epoch;
sp->hdr.cid = call->cid;
sp->hdr.callNumber = call->call_id;
- sp->hdr.seq =
- htonl(atomic_inc_return(&call->sequence));
+ sp->hdr.seq = htonl(seq);
sp->hdr.serial =
htonl(atomic_inc_return(&conn->serial));
sp->hdr.type = RXRPC_PACKET_TYPE_DATA;
@@ -697,6 +702,8 @@ static int rxrpc_send_data(struct kiocb *iocb,
else if (CIRC_SPACE(call->acks_head, call->acks_tail,
call->acks_winsz) > 1)
sp->hdr.flags |= RXRPC_MORE_PACKETS;
+ if (more && seq & 1)
+ sp->hdr.flags |= RXRPC_REQUEST_ACK;
ret = rxrpc_secure_packet(
call, skb, skb->mark,
diff --git a/net/rxrpc/ar-recvmsg.c b/net/rxrpc/ar-recvmsg.c
index 34b5490dde65..e9aaa65c0778 100644
--- a/net/rxrpc/ar-recvmsg.c
+++ b/net/rxrpc/ar-recvmsg.c
@@ -180,16 +180,7 @@ int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock,
if (copy > len - copied)
copy = len - copied;
- if (skb->ip_summed == CHECKSUM_UNNECESSARY ||
- skb->ip_summed == CHECKSUM_PARTIAL) {
- ret = skb_copy_datagram_iovec(skb, offset,
- msg->msg_iov, copy);
- } else {
- ret = skb_copy_and_csum_datagram_iovec(skb, offset,
- msg->msg_iov);
- if (ret == -EINVAL)
- goto csum_copy_error;
- }
+ ret = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copy);
if (ret < 0)
goto copy_error;
@@ -348,20 +339,6 @@ copy_error:
_leave(" = %d", ret);
return ret;
-csum_copy_error:
- _debug("csum error");
- release_sock(&rx->sk);
- if (continue_call)
- rxrpc_put_call(continue_call);
- rxrpc_kill_skb(skb);
- if (!(flags & MSG_PEEK)) {
- if (skb_dequeue(&rx->sk.sk_receive_queue) != skb)
- BUG();
- }
- skb_kill_datagram(&rx->sk, skb, flags);
- rxrpc_put_call(call);
- return -EAGAIN;
-
wait_interrupted:
ret = sock_intr_errno(timeo);
wait_error:
diff --git a/net/rxrpc/ar-skbuff.c b/net/rxrpc/ar-skbuff.c
index de755e04d29c..4cfab49e329d 100644
--- a/net/rxrpc/ar-skbuff.c
+++ b/net/rxrpc/ar-skbuff.c
@@ -83,9 +83,14 @@ static void rxrpc_hard_ACK_data(struct rxrpc_call *call,
rxrpc_request_final_ACK(call);
} else if (atomic_dec_and_test(&call->ackr_not_idle) &&
test_and_clear_bit(RXRPC_CALL_TX_SOFT_ACK, &call->flags)) {
+ /* We previously soft-ACK'd some received packets that have now
+ * been consumed, so send a hard-ACK if no more packets are
+ * immediately forthcoming to allow the transmitter to free up
+ * its Tx bufferage.
+ */
_debug("send Rx idle ACK");
__rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, sp->hdr.serial,
- true);
+ false);
}
spin_unlock_bh(&call->lock);
diff --git a/net/rxrpc/ar-transport.c b/net/rxrpc/ar-transport.c
index 92df566930b9..1976dec84f29 100644
--- a/net/rxrpc/ar-transport.c
+++ b/net/rxrpc/ar-transport.c
@@ -17,11 +17,15 @@
#include <net/af_rxrpc.h>
#include "ar-internal.h"
+/*
+ * Time after last use at which transport record is cleaned up.
+ */
+unsigned rxrpc_transport_expiry = 3600 * 24;
+
static void rxrpc_transport_reaper(struct work_struct *work);
static LIST_HEAD(rxrpc_transports);
static DEFINE_RWLOCK(rxrpc_transport_lock);
-static unsigned long rxrpc_transport_timeout = 3600 * 24;
static DECLARE_DELAYED_WORK(rxrpc_transport_reap, rxrpc_transport_reaper);
/*
@@ -235,7 +239,7 @@ static void rxrpc_transport_reaper(struct work_struct *work)
if (likely(atomic_read(&trans->usage) > 0))
continue;
- reap_time = trans->put_time + rxrpc_transport_timeout;
+ reap_time = trans->put_time + rxrpc_transport_expiry;
if (reap_time <= now)
list_move_tail(&trans->link, &graveyard);
else if (reap_time < earliest)
@@ -271,7 +275,7 @@ void __exit rxrpc_destroy_all_transports(void)
{
_enter("");
- rxrpc_transport_timeout = 0;
+ rxrpc_transport_expiry = 0;
cancel_delayed_work(&rxrpc_transport_reap);
rxrpc_queue_delayed_work(&rxrpc_transport_reap, 0);
diff --git a/net/rxrpc/sysctl.c b/net/rxrpc/sysctl.c
new file mode 100644
index 000000000000..50a98a910eb1
--- /dev/null
+++ b/net/rxrpc/sysctl.c
@@ -0,0 +1,146 @@
+/* sysctls for configuring RxRPC operating parameters
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/sysctl.h>
+#include <net/sock.h>
+#include <net/af_rxrpc.h>
+#include "ar-internal.h"
+
+static struct ctl_table_header *rxrpc_sysctl_reg_table;
+static const unsigned zero = 0;
+static const unsigned one = 1;
+static const unsigned four = 4;
+static const unsigned n_65535 = 65535;
+static const unsigned n_max_acks = RXRPC_MAXACKS;
+
+/*
+ * RxRPC operating parameters.
+ *
+ * See Documentation/networking/rxrpc.txt and the variable definitions for more
+ * information on the individual parameters.
+ */
+static struct ctl_table rxrpc_sysctl_table[] = {
+ /* Values measured in milliseconds */
+ {
+ .procname = "req_ack_delay",
+ .data = &rxrpc_requested_ack_delay,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_ms_jiffies,
+ .extra1 = (void *)&zero,
+ },
+ {
+ .procname = "soft_ack_delay",
+ .data = &rxrpc_soft_ack_delay,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_ms_jiffies,
+ .extra1 = (void *)&one,
+ },
+ {
+ .procname = "idle_ack_delay",
+ .data = &rxrpc_idle_ack_delay,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_ms_jiffies,
+ .extra1 = (void *)&one,
+ },
+ {
+ .procname = "resend_timeout",
+ .data = &rxrpc_resend_timeout,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_ms_jiffies,
+ .extra1 = (void *)&one,
+ },
+
+ /* Values measured in seconds but used in jiffies */
+ {
+ .procname = "max_call_lifetime",
+ .data = &rxrpc_max_call_lifetime,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ .extra1 = (void *)&one,
+ },
+ {
+ .procname = "dead_call_expiry",
+ .data = &rxrpc_dead_call_expiry,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ .extra1 = (void *)&one,
+ },
+
+ /* Values measured in seconds */
+ {
+ .procname = "connection_expiry",
+ .data = &rxrpc_connection_expiry,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = (void *)&one,
+ },
+ {
+ .procname = "transport_expiry",
+ .data = &rxrpc_transport_expiry,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = (void *)&one,
+ },
+
+ /* Non-time values */
+ {
+ .procname = "rx_window_size",
+ .data = &rxrpc_rx_window_size,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = (void *)&one,
+ .extra2 = (void *)&n_max_acks,
+ },
+ {
+ .procname = "rx_mtu",
+ .data = &rxrpc_rx_mtu,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = (void *)&one,
+ .extra1 = (void *)&n_65535,
+ },
+ {
+ .procname = "rx_jumbo_max",
+ .data = &rxrpc_rx_jumbo_max,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = (void *)&one,
+ .extra2 = (void *)&four,
+ },
+
+ { }
+};
+
+int __init rxrpc_sysctl_init(void)
+{
+ rxrpc_sysctl_reg_table = register_net_sysctl(&init_net, "net/rxrpc",
+ rxrpc_sysctl_table);
+ if (!rxrpc_sysctl_reg_table)
+ return -ENOMEM;
+ return 0;
+}
+
+void rxrpc_sysctl_exit(void)
+{
+ if (rxrpc_sysctl_reg_table)
+ unregister_net_sysctl_table(rxrpc_sysctl_reg_table);
+}
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 72bdc7166345..8a5ba5add4bc 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -27,8 +27,11 @@
#include <net/act_api.h>
#include <net/netlink.h>
-void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
+void tcf_hash_destroy(struct tc_action *a)
{
+ struct tcf_common *p = a->priv;
+ struct tcf_hashinfo *hinfo = a->ops->hinfo;
+
spin_lock_bh(&hinfo->lock);
hlist_del(&p->tcfc_head);
spin_unlock_bh(&hinfo->lock);
@@ -42,18 +45,22 @@ void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
}
EXPORT_SYMBOL(tcf_hash_destroy);
-int tcf_hash_release(struct tcf_common *p, int bind,
- struct tcf_hashinfo *hinfo)
+int tcf_hash_release(struct tc_action *a, int bind)
{
+ struct tcf_common *p = a->priv;
int ret = 0;
if (p) {
if (bind)
p->tcfc_bindcnt--;
+ else if (p->tcfc_bindcnt > 0)
+ return -EPERM;
p->tcfc_refcnt--;
if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) {
- tcf_hash_destroy(p, hinfo);
+ if (a->ops->cleanup)
+ a->ops->cleanup(a, bind);
+ tcf_hash_destroy(a);
ret = 1;
}
}
@@ -118,6 +125,7 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a)
struct tcf_common *p;
struct nlattr *nest;
int i = 0, n_i = 0;
+ int ret = -EINVAL;
nest = nla_nest_start(skb, a->order);
if (nest == NULL)
@@ -127,10 +135,13 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a)
for (i = 0; i < (hinfo->hmask + 1); i++) {
head = &hinfo->htab[tcf_hash(i, hinfo->hmask)];
hlist_for_each_entry_safe(p, n, head, tcfc_head) {
- if (ACT_P_DELETED == tcf_hash_release(p, 0, hinfo)) {
+ a->priv = p;
+ ret = tcf_hash_release(a, 0);
+ if (ret == ACT_P_DELETED) {
module_put(a->ops->owner);
n_i++;
- }
+ } else if (ret < 0)
+ goto nla_put_failure;
}
}
if (nla_put_u32(skb, TCA_FCNT, n_i))
@@ -140,7 +151,7 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a)
return n_i;
nla_put_failure:
nla_nest_cancel(skb, nest);
- return -EINVAL;
+ return ret;
}
static int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb,
@@ -198,7 +209,7 @@ int tcf_hash_search(struct tc_action *a, u32 index)
}
EXPORT_SYMBOL(tcf_hash_search);
-struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind)
+int tcf_hash_check(u32 index, struct tc_action *a, int bind)
{
struct tcf_hashinfo *hinfo = a->ops->hinfo;
struct tcf_common *p = NULL;
@@ -207,19 +218,30 @@ struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind)
p->tcfc_bindcnt++;
p->tcfc_refcnt++;
a->priv = p;
+ return 1;
}
- return p;
+ return 0;
}
EXPORT_SYMBOL(tcf_hash_check);
-struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est,
- struct tc_action *a, int size, int bind)
+void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est)
+{
+ struct tcf_common *pc = a->priv;
+ if (est)
+ gen_kill_estimator(&pc->tcfc_bstats,
+ &pc->tcfc_rate_est);
+ kfree_rcu(pc, tcfc_rcu);
+}
+EXPORT_SYMBOL(tcf_hash_cleanup);
+
+int tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a,
+ int size, int bind)
{
struct tcf_hashinfo *hinfo = a->ops->hinfo;
struct tcf_common *p = kzalloc(size, GFP_KERNEL);
if (unlikely(!p))
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
p->tcfc_refcnt = 1;
if (bind)
p->tcfc_bindcnt = 1;
@@ -234,17 +256,19 @@ struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est,
&p->tcfc_lock, est);
if (err) {
kfree(p);
- return ERR_PTR(err);
+ return err;
}
}
a->priv = (void *) p;
- return p;
+ return 0;
}
EXPORT_SYMBOL(tcf_hash_create);
-void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo)
+void tcf_hash_insert(struct tc_action *a)
{
+ struct tcf_common *p = a->priv;
+ struct tcf_hashinfo *hinfo = a->ops->hinfo;
unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
spin_lock_bh(&hinfo->lock);
@@ -256,12 +280,13 @@ EXPORT_SYMBOL(tcf_hash_insert);
static LIST_HEAD(act_base);
static DEFINE_RWLOCK(act_mod_lock);
-int tcf_register_action(struct tc_action_ops *act)
+int tcf_register_action(struct tc_action_ops *act, unsigned int mask)
{
struct tc_action_ops *a;
+ int err;
- /* Must supply act, dump, cleanup and init */
- if (!act->act || !act->dump || !act->cleanup || !act->init)
+ /* Must supply act, dump and init */
+ if (!act->act || !act->dump || !act->init)
return -EINVAL;
/* Supply defaults */
@@ -270,10 +295,21 @@ int tcf_register_action(struct tc_action_ops *act)
if (!act->walk)
act->walk = tcf_generic_walker;
+ act->hinfo = kmalloc(sizeof(struct tcf_hashinfo), GFP_KERNEL);
+ if (!act->hinfo)
+ return -ENOMEM;
+ err = tcf_hashinfo_init(act->hinfo, mask);
+ if (err) {
+ kfree(act->hinfo);
+ return err;
+ }
+
write_lock(&act_mod_lock);
list_for_each_entry(a, &act_base, head) {
if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
write_unlock(&act_mod_lock);
+ tcf_hashinfo_destroy(act->hinfo);
+ kfree(act->hinfo);
return -EEXIST;
}
}
@@ -292,6 +328,8 @@ int tcf_unregister_action(struct tc_action_ops *act)
list_for_each_entry(a, &act_base, head) {
if (a == act) {
list_del(&act->head);
+ tcf_hashinfo_destroy(act->hinfo);
+ kfree(act->hinfo);
err = 0;
break;
}
@@ -368,16 +406,21 @@ exec_done:
}
EXPORT_SYMBOL(tcf_action_exec);
-void tcf_action_destroy(struct list_head *actions, int bind)
+int tcf_action_destroy(struct list_head *actions, int bind)
{
struct tc_action *a, *tmp;
+ int ret = 0;
list_for_each_entry_safe(a, tmp, actions, list) {
- if (a->ops->cleanup(a, bind) == ACT_P_DELETED)
+ ret = tcf_hash_release(a, bind);
+ if (ret == ACT_P_DELETED)
module_put(a->ops->owner);
+ else if (ret < 0)
+ return ret;
list_del(&a->list);
kfree(a);
}
+ return ret;
}
int
@@ -642,6 +685,20 @@ act_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
return rtnl_unicast(skb, net, portid);
}
+static struct tc_action *create_a(int i)
+{
+ struct tc_action *act;
+
+ act = kzalloc(sizeof(*act), GFP_KERNEL);
+ if (act == NULL) {
+ pr_debug("create_a: failed to alloc!\n");
+ return NULL;
+ }
+ act->order = i;
+ INIT_LIST_HEAD(&act->list);
+ return act;
+}
+
static struct tc_action *
tcf_action_get_1(struct nlattr *nla, struct nlmsghdr *n, u32 portid)
{
@@ -661,11 +718,10 @@ tcf_action_get_1(struct nlattr *nla, struct nlmsghdr *n, u32 portid)
index = nla_get_u32(tb[TCA_ACT_INDEX]);
err = -ENOMEM;
- a = kzalloc(sizeof(struct tc_action), GFP_KERNEL);
+ a = create_a(0);
if (a == NULL)
goto err_out;
- INIT_LIST_HEAD(&a->list);
err = -EINVAL;
a->ops = tc_lookup_action(tb[TCA_ACT_KIND]);
if (a->ops == NULL) /* could happen in batch of actions */
@@ -695,20 +751,6 @@ static void cleanup_a(struct list_head *actions)
}
}
-static struct tc_action *create_a(int i)
-{
- struct tc_action *act;
-
- act = kzalloc(sizeof(*act), GFP_KERNEL);
- if (act == NULL) {
- pr_debug("create_a: failed to alloc!\n");
- return NULL;
- }
- act->order = i;
- INIT_LIST_HEAD(&act->list);
- return act;
-}
-
static int tca_action_flush(struct net *net, struct nlattr *nla,
struct nlmsghdr *n, u32 portid)
{
@@ -720,18 +762,12 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
struct nlattr *nest;
struct nlattr *tb[TCA_ACT_MAX + 1];
struct nlattr *kind;
- struct tc_action *a = create_a(0);
+ struct tc_action a;
int err = -ENOMEM;
- if (a == NULL) {
- pr_debug("tca_action_flush: couldnt create tc_action\n");
- return err;
- }
-
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {
pr_debug("tca_action_flush: failed skb alloc\n");
- kfree(a);
return err;
}
@@ -743,8 +779,10 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
err = -EINVAL;
kind = tb[TCA_ACT_KIND];
- a->ops = tc_lookup_action(kind);
- if (a->ops == NULL) /*some idjot trying to flush unknown action */
+ memset(&a, 0, sizeof(struct tc_action));
+ INIT_LIST_HEAD(&a.list);
+ a.ops = tc_lookup_action(kind);
+ if (a.ops == NULL) /*some idjot trying to flush unknown action */
goto err_out;
nlh = nlmsg_put(skb, portid, n->nlmsg_seq, RTM_DELACTION, sizeof(*t), 0);
@@ -759,7 +797,7 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
if (nest == NULL)
goto out_module_put;
- err = a->ops->walk(skb, &dcb, RTM_DELACTION, a);
+ err = a.ops->walk(skb, &dcb, RTM_DELACTION, &a);
if (err < 0)
goto out_module_put;
if (err == 0)
@@ -769,8 +807,7 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
nlh->nlmsg_len = skb_tail_pointer(skb) - b;
nlh->nlmsg_flags |= NLM_F_ROOT;
- module_put(a->ops->owner);
- kfree(a);
+ module_put(a.ops->owner);
err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
n->nlmsg_flags & NLM_F_ECHO);
if (err > 0)
@@ -779,11 +816,10 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
return err;
out_module_put:
- module_put(a->ops->owner);
+ module_put(a.ops->owner);
err_out:
noflush_out:
kfree_skb(skb);
- kfree(a);
return err;
}
@@ -805,7 +841,11 @@ tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
}
/* now do the delete */
- tcf_action_destroy(actions, 0);
+ ret = tcf_action_destroy(actions, 0);
+ if (ret < 0) {
+ kfree_skb(skb);
+ return ret;
+ }
ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
n->nlmsg_flags & NLM_F_ECHO);
diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
index 2210187c45c2..edbf40dac709 100644
--- a/net/sched/act_csum.c
+++ b/net/sched/act_csum.c
@@ -37,7 +37,6 @@
#include <net/tc_act/tc_csum.h>
#define CSUM_TAB_MASK 15
-static struct tcf_hashinfo csum_hash_info;
static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = {
[TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), },
@@ -48,7 +47,6 @@ static int tcf_csum_init(struct net *n, struct nlattr *nla, struct nlattr *est,
{
struct nlattr *tb[TCA_CSUM_MAX + 1];
struct tc_csum *parm;
- struct tcf_common *pc;
struct tcf_csum *p;
int ret = 0, err;
@@ -63,38 +61,31 @@ static int tcf_csum_init(struct net *n, struct nlattr *nla, struct nlattr *est,
return -EINVAL;
parm = nla_data(tb[TCA_CSUM_PARMS]);
- pc = tcf_hash_check(parm->index, a, bind);
- if (!pc) {
- pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind);
- if (IS_ERR(pc))
- return PTR_ERR(pc);
+ if (!tcf_hash_check(parm->index, a, bind)) {
+ ret = tcf_hash_create(parm->index, est, a, sizeof(*p), bind);
+ if (ret)
+ return ret;
ret = ACT_P_CREATED;
} else {
if (bind)/* dont override defaults */
return 0;
- tcf_hash_release(pc, bind, a->ops->hinfo);
+ tcf_hash_release(a, bind);
if (!ovr)
return -EEXIST;
}
- p = to_tcf_csum(pc);
+ p = to_tcf_csum(a);
spin_lock_bh(&p->tcf_lock);
p->tcf_action = parm->action;
p->update_flags = parm->update_flags;
spin_unlock_bh(&p->tcf_lock);
if (ret == ACT_P_CREATED)
- tcf_hash_insert(pc, a->ops->hinfo);
+ tcf_hash_insert(a);
return ret;
}
-static int tcf_csum_cleanup(struct tc_action *a, int bind)
-{
- struct tcf_csum *p = a->priv;
- return tcf_hash_release(&p->common, bind, &csum_hash_info);
-}
-
/**
* tcf_csum_skb_nextlayer - Get next layer pointer
* @skb: sk_buff to use
@@ -569,12 +560,10 @@ nla_put_failure:
static struct tc_action_ops act_csum_ops = {
.kind = "csum",
- .hinfo = &csum_hash_info,
.type = TCA_ACT_CSUM,
.owner = THIS_MODULE,
.act = tcf_csum,
.dump = tcf_csum_dump,
- .cleanup = tcf_csum_cleanup,
.init = tcf_csum_init,
};
@@ -583,11 +572,7 @@ MODULE_LICENSE("GPL");
static int __init csum_init_module(void)
{
- int err = tcf_hashinfo_init(&csum_hash_info, CSUM_TAB_MASK);
- if (err)
- return err;
-
- return tcf_register_action(&act_csum_ops);
+ return tcf_register_action(&act_csum_ops, CSUM_TAB_MASK);
}
static void __exit csum_cleanup_module(void)
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index a0eed30d5811..d6bcbd9f7791 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -24,7 +24,6 @@
#include <net/tc_act/tc_gact.h>
#define GACT_TAB_MASK 15
-static struct tcf_hashinfo gact_hash_info;
#ifdef CONFIG_GACT_PROB
static int gact_net_rand(struct tcf_gact *gact)
@@ -57,7 +56,6 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
struct nlattr *tb[TCA_GACT_MAX + 1];
struct tc_gact *parm;
struct tcf_gact *gact;
- struct tcf_common *pc;
int ret = 0;
int err;
#ifdef CONFIG_GACT_PROB
@@ -86,21 +84,20 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
}
#endif
- pc = tcf_hash_check(parm->index, a, bind);
- if (!pc) {
- pc = tcf_hash_create(parm->index, est, a, sizeof(*gact), bind);
- if (IS_ERR(pc))
- return PTR_ERR(pc);
+ if (!tcf_hash_check(parm->index, a, bind)) {
+ ret = tcf_hash_create(parm->index, est, a, sizeof(*gact), bind);
+ if (ret)
+ return ret;
ret = ACT_P_CREATED;
} else {
if (bind)/* dont override defaults */
return 0;
- tcf_hash_release(pc, bind, a->ops->hinfo);
+ tcf_hash_release(a, bind);
if (!ovr)
return -EEXIST;
}
- gact = to_gact(pc);
+ gact = to_gact(a);
spin_lock_bh(&gact->tcf_lock);
gact->tcf_action = parm->action;
@@ -113,19 +110,10 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
#endif
spin_unlock_bh(&gact->tcf_lock);
if (ret == ACT_P_CREATED)
- tcf_hash_insert(pc, a->ops->hinfo);
+ tcf_hash_insert(a);
return ret;
}
-static int tcf_gact_cleanup(struct tc_action *a, int bind)
-{
- struct tcf_gact *gact = a->priv;
-
- if (gact)
- return tcf_hash_release(&gact->common, bind, a->ops->hinfo);
- return 0;
-}
-
static int tcf_gact(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res)
{
@@ -191,12 +179,10 @@ nla_put_failure:
static struct tc_action_ops act_gact_ops = {
.kind = "gact",
- .hinfo = &gact_hash_info,
.type = TCA_ACT_GACT,
.owner = THIS_MODULE,
.act = tcf_gact,
.dump = tcf_gact_dump,
- .cleanup = tcf_gact_cleanup,
.init = tcf_gact_init,
};
@@ -206,21 +192,17 @@ MODULE_LICENSE("GPL");
static int __init gact_init_module(void)
{
- int err = tcf_hashinfo_init(&gact_hash_info, GACT_TAB_MASK);
- if (err)
- return err;
#ifdef CONFIG_GACT_PROB
pr_info("GACT probability on\n");
#else
pr_info("GACT probability NOT on\n");
#endif
- return tcf_register_action(&act_gact_ops);
+ return tcf_register_action(&act_gact_ops, GACT_TAB_MASK);
}
static void __exit gact_cleanup_module(void)
{
tcf_unregister_action(&act_gact_ops);
- tcf_hashinfo_destroy(&gact_hash_info);
}
module_init(gact_init_module);
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
index 0a6d62174027..8a64a0734aee 100644
--- a/net/sched/act_ipt.c
+++ b/net/sched/act_ipt.c
@@ -29,7 +29,6 @@
#define IPT_TAB_MASK 15
-static struct tcf_hashinfo ipt_hash_info;
static int ipt_init_target(struct xt_entry_target *t, char *table, unsigned int hook)
{
@@ -69,22 +68,12 @@ static void ipt_destroy_target(struct xt_entry_target *t)
module_put(par.target->me);
}
-static int tcf_ipt_release(struct tcf_ipt *ipt, int bind)
+static void tcf_ipt_release(struct tc_action *a, int bind)
{
- int ret = 0;
- if (ipt) {
- if (bind)
- ipt->tcf_bindcnt--;
- ipt->tcf_refcnt--;
- if (ipt->tcf_bindcnt <= 0 && ipt->tcf_refcnt <= 0) {
- ipt_destroy_target(ipt->tcfi_t);
- kfree(ipt->tcfi_tname);
- kfree(ipt->tcfi_t);
- tcf_hash_destroy(&ipt->common, &ipt_hash_info);
- ret = ACT_P_DELETED;
- }
- }
- return ret;
+ struct tcf_ipt *ipt = to_ipt(a);
+ ipt_destroy_target(ipt->tcfi_t);
+ kfree(ipt->tcfi_tname);
+ kfree(ipt->tcfi_t);
}
static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = {
@@ -99,7 +88,6 @@ static int tcf_ipt_init(struct net *net, struct nlattr *nla, struct nlattr *est,
{
struct nlattr *tb[TCA_IPT_MAX + 1];
struct tcf_ipt *ipt;
- struct tcf_common *pc;
struct xt_entry_target *td, *t;
char *tname;
int ret = 0, err;
@@ -125,21 +113,20 @@ static int tcf_ipt_init(struct net *net, struct nlattr *nla, struct nlattr *est,
if (tb[TCA_IPT_INDEX] != NULL)
index = nla_get_u32(tb[TCA_IPT_INDEX]);
- pc = tcf_hash_check(index, a, bind);
- if (!pc) {
- pc = tcf_hash_create(index, est, a, sizeof(*ipt), bind);
- if (IS_ERR(pc))
- return PTR_ERR(pc);
+ if (!tcf_hash_check(index, a, bind) ) {
+ ret = tcf_hash_create(index, est, a, sizeof(*ipt), bind);
+ if (ret)
+ return ret;
ret = ACT_P_CREATED;
} else {
if (bind)/* dont override defaults */
return 0;
- tcf_ipt_release(to_ipt(pc), bind);
+ tcf_hash_release(a, bind);
if (!ovr)
return -EEXIST;
}
- ipt = to_ipt(pc);
+ ipt = to_ipt(a);
hook = nla_get_u32(tb[TCA_IPT_HOOK]);
@@ -170,7 +157,7 @@ static int tcf_ipt_init(struct net *net, struct nlattr *nla, struct nlattr *est,
ipt->tcfi_hook = hook;
spin_unlock_bh(&ipt->tcf_lock);
if (ret == ACT_P_CREATED)
- tcf_hash_insert(pc, a->ops->hinfo);
+ tcf_hash_insert(a);
return ret;
err3:
@@ -178,21 +165,11 @@ err3:
err2:
kfree(tname);
err1:
- if (ret == ACT_P_CREATED) {
- if (est)
- gen_kill_estimator(&pc->tcfc_bstats,
- &pc->tcfc_rate_est);
- kfree_rcu(pc, tcfc_rcu);
- }
+ if (ret == ACT_P_CREATED)
+ tcf_hash_cleanup(a, est);
return err;
}
-static int tcf_ipt_cleanup(struct tc_action *a, int bind)
-{
- struct tcf_ipt *ipt = a->priv;
- return tcf_ipt_release(ipt, bind);
-}
-
static int tcf_ipt(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res)
{
@@ -284,23 +261,21 @@ nla_put_failure:
static struct tc_action_ops act_ipt_ops = {
.kind = "ipt",
- .hinfo = &ipt_hash_info,
.type = TCA_ACT_IPT,
.owner = THIS_MODULE,
.act = tcf_ipt,
.dump = tcf_ipt_dump,
- .cleanup = tcf_ipt_cleanup,
+ .cleanup = tcf_ipt_release,
.init = tcf_ipt_init,
};
static struct tc_action_ops act_xt_ops = {
.kind = "xt",
- .hinfo = &ipt_hash_info,
.type = TCA_ACT_XT,
.owner = THIS_MODULE,
.act = tcf_ipt,
.dump = tcf_ipt_dump,
- .cleanup = tcf_ipt_cleanup,
+ .cleanup = tcf_ipt_release,
.init = tcf_ipt_init,
};
@@ -311,20 +286,16 @@ MODULE_ALIAS("act_xt");
static int __init ipt_init_module(void)
{
- int ret1, ret2, err;
- err = tcf_hashinfo_init(&ipt_hash_info, IPT_TAB_MASK);
- if (err)
- return err;
+ int ret1, ret2;
- ret1 = tcf_register_action(&act_xt_ops);
+ ret1 = tcf_register_action(&act_xt_ops, IPT_TAB_MASK);
if (ret1 < 0)
printk("Failed to load xt action\n");
- ret2 = tcf_register_action(&act_ipt_ops);
+ ret2 = tcf_register_action(&act_ipt_ops, IPT_TAB_MASK);
if (ret2 < 0)
printk("Failed to load ipt action\n");
if (ret1 < 0 && ret2 < 0) {
- tcf_hashinfo_destroy(&ipt_hash_info);
return ret1;
} else
return 0;
@@ -334,7 +305,6 @@ static void __exit ipt_cleanup_module(void)
{
tcf_unregister_action(&act_xt_ops);
tcf_unregister_action(&act_ipt_ops);
- tcf_hashinfo_destroy(&ipt_hash_info);
}
module_init(ipt_init_module);
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index 0b2c6d39d396..4f912c0e225b 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -31,23 +31,13 @@
#define MIRRED_TAB_MASK 7
static LIST_HEAD(mirred_list);
-static struct tcf_hashinfo mirred_hash_info;
-static int tcf_mirred_release(struct tcf_mirred *m, int bind)
+static void tcf_mirred_release(struct tc_action *a, int bind)
{
- if (m) {
- if (bind)
- m->tcf_bindcnt--;
- m->tcf_refcnt--;
- if (!m->tcf_bindcnt && m->tcf_refcnt <= 0) {
- list_del(&m->tcfm_list);
- if (m->tcfm_dev)
- dev_put(m->tcfm_dev);
- tcf_hash_destroy(&m->common, &mirred_hash_info);
- return 1;
- }
- }
- return 0;
+ struct tcf_mirred *m = to_mirred(a);
+ list_del(&m->tcfm_list);
+ if (m->tcfm_dev)
+ dev_put(m->tcfm_dev);
}
static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
@@ -61,7 +51,6 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
struct nlattr *tb[TCA_MIRRED_MAX + 1];
struct tc_mirred *parm;
struct tcf_mirred *m;
- struct tcf_common *pc;
struct net_device *dev;
int ret, ok_push = 0;
@@ -101,21 +90,20 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
dev = NULL;
}
- pc = tcf_hash_check(parm->index, a, bind);
- if (!pc) {
+ if (!tcf_hash_check(parm->index, a, bind)) {
if (dev == NULL)
return -EINVAL;
- pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind);
- if (IS_ERR(pc))
- return PTR_ERR(pc);
+ ret = tcf_hash_create(parm->index, est, a, sizeof(*m), bind);
+ if (ret)
+ return ret;
ret = ACT_P_CREATED;
} else {
if (!ovr) {
- tcf_mirred_release(to_mirred(pc), bind);
+ tcf_hash_release(a, bind);
return -EEXIST;
}
}
- m = to_mirred(pc);
+ m = to_mirred(a);
spin_lock_bh(&m->tcf_lock);
m->tcf_action = parm->action;
@@ -131,21 +119,12 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
spin_unlock_bh(&m->tcf_lock);
if (ret == ACT_P_CREATED) {
list_add(&m->tcfm_list, &mirred_list);
- tcf_hash_insert(pc, a->ops->hinfo);
+ tcf_hash_insert(a);
}
return ret;
}
-static int tcf_mirred_cleanup(struct tc_action *a, int bind)
-{
- struct tcf_mirred *m = a->priv;
-
- if (m)
- return tcf_mirred_release(m, bind);
- return 0;
-}
-
static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res)
{
@@ -254,12 +233,11 @@ static struct notifier_block mirred_device_notifier = {
static struct tc_action_ops act_mirred_ops = {
.kind = "mirred",
- .hinfo = &mirred_hash_info,
.type = TCA_ACT_MIRRED,
.owner = THIS_MODULE,
.act = tcf_mirred,
.dump = tcf_mirred_dump,
- .cleanup = tcf_mirred_cleanup,
+ .cleanup = tcf_mirred_release,
.init = tcf_mirred_init,
};
@@ -273,19 +251,13 @@ static int __init mirred_init_module(void)
if (err)
return err;
- err = tcf_hashinfo_init(&mirred_hash_info, MIRRED_TAB_MASK);
- if (err) {
- unregister_netdevice_notifier(&mirred_device_notifier);
- return err;
- }
pr_info("Mirror/redirect action on\n");
- return tcf_register_action(&act_mirred_ops);
+ return tcf_register_action(&act_mirred_ops, MIRRED_TAB_MASK);
}
static void __exit mirred_cleanup_module(void)
{
tcf_unregister_action(&act_mirred_ops);
- tcf_hashinfo_destroy(&mirred_hash_info);
unregister_netdevice_notifier(&mirred_device_notifier);
}
diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c
index 81f0404bb335..270a030d5fd0 100644
--- a/net/sched/act_nat.c
+++ b/net/sched/act_nat.c
@@ -31,8 +31,6 @@
#define NAT_TAB_MASK 15
-static struct tcf_hashinfo nat_hash_info;
-
static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = {
[TCA_NAT_PARMS] = { .len = sizeof(struct tc_nat) },
};
@@ -44,7 +42,6 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
struct tc_nat *parm;
int ret = 0, err;
struct tcf_nat *p;
- struct tcf_common *pc;
if (nla == NULL)
return -EINVAL;
@@ -57,20 +54,19 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
return -EINVAL;
parm = nla_data(tb[TCA_NAT_PARMS]);
- pc = tcf_hash_check(parm->index, a, bind);
- if (!pc) {
- pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind);
- if (IS_ERR(pc))
- return PTR_ERR(pc);
+ if (!tcf_hash_check(parm->index, a, bind)) {
+ ret = tcf_hash_create(parm->index, est, a, sizeof(*p), bind);
+ if (ret)
+ return ret;
ret = ACT_P_CREATED;
} else {
if (bind)
return 0;
- tcf_hash_release(pc, bind, a->ops->hinfo);
+ tcf_hash_release(a, bind);
if (!ovr)
return -EEXIST;
}
- p = to_tcf_nat(pc);
+ p = to_tcf_nat(a);
spin_lock_bh(&p->tcf_lock);
p->old_addr = parm->old_addr;
@@ -82,18 +78,11 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
spin_unlock_bh(&p->tcf_lock);
if (ret == ACT_P_CREATED)
- tcf_hash_insert(pc, a->ops->hinfo);
+ tcf_hash_insert(a);
return ret;
}
-static int tcf_nat_cleanup(struct tc_action *a, int bind)
-{
- struct tcf_nat *p = a->priv;
-
- return tcf_hash_release(&p->common, bind, &nat_hash_info);
-}
-
static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res)
{
@@ -293,12 +282,10 @@ nla_put_failure:
static struct tc_action_ops act_nat_ops = {
.kind = "nat",
- .hinfo = &nat_hash_info,
.type = TCA_ACT_NAT,
.owner = THIS_MODULE,
.act = tcf_nat,
.dump = tcf_nat_dump,
- .cleanup = tcf_nat_cleanup,
.init = tcf_nat_init,
};
@@ -307,16 +294,12 @@ MODULE_LICENSE("GPL");
static int __init nat_init_module(void)
{
- int err = tcf_hashinfo_init(&nat_hash_info, NAT_TAB_MASK);
- if (err)
- return err;
- return tcf_register_action(&act_nat_ops);
+ return tcf_register_action(&act_nat_ops, NAT_TAB_MASK);
}
static void __exit nat_cleanup_module(void)
{
tcf_unregister_action(&act_nat_ops);
- tcf_hashinfo_destroy(&nat_hash_info);
}
module_init(nat_init_module);
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index be3f0f6875bb..5f9bcb2e080b 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -25,8 +25,6 @@
#define PEDIT_TAB_MASK 15
-static struct tcf_hashinfo pedit_hash_info;
-
static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
[TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) },
};
@@ -39,7 +37,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
struct tc_pedit *parm;
int ret = 0, err;
struct tcf_pedit *p;
- struct tcf_common *pc;
struct tc_pedit_key *keys = NULL;
int ksize;
@@ -57,26 +54,22 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
if (nla_len(tb[TCA_PEDIT_PARMS]) < sizeof(*parm) + ksize)
return -EINVAL;
- pc = tcf_hash_check(parm->index, a, bind);
- if (!pc) {
+ if (!tcf_hash_check(parm->index, a, bind)) {
if (!parm->nkeys)
return -EINVAL;
- pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind);
- if (IS_ERR(pc))
- return PTR_ERR(pc);
- p = to_pedit(pc);
+ ret = tcf_hash_create(parm->index, est, a, sizeof(*p), bind);
+ if (ret)
+ return ret;
+ p = to_pedit(a);
keys = kmalloc(ksize, GFP_KERNEL);
if (keys == NULL) {
- if (est)
- gen_kill_estimator(&pc->tcfc_bstats,
- &pc->tcfc_rate_est);
- kfree_rcu(pc, tcfc_rcu);
+ tcf_hash_cleanup(a, est);
return -ENOMEM;
}
ret = ACT_P_CREATED;
} else {
- p = to_pedit(pc);
- tcf_hash_release(pc, bind, a->ops->hinfo);
+ p = to_pedit(a);
+ tcf_hash_release(a, bind);
if (bind)
return 0;
if (!ovr)
@@ -100,22 +93,15 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
memcpy(p->tcfp_keys, parm->keys, ksize);
spin_unlock_bh(&p->tcf_lock);
if (ret == ACT_P_CREATED)
- tcf_hash_insert(pc, a->ops->hinfo);
+ tcf_hash_insert(a);
return ret;
}
-static int tcf_pedit_cleanup(struct tc_action *a, int bind)
+static void tcf_pedit_cleanup(struct tc_action *a, int bind)
{
struct tcf_pedit *p = a->priv;
-
- if (p) {
- struct tc_pedit_key *keys = p->tcfp_keys;
- if (tcf_hash_release(&p->common, bind, &pedit_hash_info)) {
- kfree(keys);
- return 1;
- }
- }
- return 0;
+ struct tc_pedit_key *keys = p->tcfp_keys;
+ kfree(keys);
}
static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,
@@ -230,7 +216,6 @@ nla_put_failure:
static struct tc_action_ops act_pedit_ops = {
.kind = "pedit",
- .hinfo = &pedit_hash_info,
.type = TCA_ACT_PEDIT,
.owner = THIS_MODULE,
.act = tcf_pedit,
@@ -245,15 +230,11 @@ MODULE_LICENSE("GPL");
static int __init pedit_init_module(void)
{
- int err = tcf_hashinfo_init(&pedit_hash_info, PEDIT_TAB_MASK);
- if (err)
- return err;
- return tcf_register_action(&act_pedit_ops);
+ return tcf_register_action(&act_pedit_ops, PEDIT_TAB_MASK);
}
static void __exit pedit_cleanup_module(void)
{
- tcf_hashinfo_destroy(&pedit_hash_info);
tcf_unregister_action(&act_pedit_ops);
}
diff --git a/net/sched/act_police.c b/net/sched/act_police.c
index 1778209a332f..0566e4606a4a 100644
--- a/net/sched/act_police.c
+++ b/net/sched/act_police.c
@@ -41,7 +41,6 @@ struct tcf_police {
container_of(pc, struct tcf_police, common)
#define POL_TAB_MASK 15
-static struct tcf_hashinfo police_hash_info;
/* old policer structure from before tc actions */
struct tc_police_compat {
@@ -234,7 +233,7 @@ override:
police->tcfp_t_c = ktime_to_ns(ktime_get());
police->tcf_index = parm->index ? parm->index :
- tcf_hash_new_index(a->ops->hinfo);
+ tcf_hash_new_index(hinfo);
h = tcf_hash(police->tcf_index, POL_TAB_MASK);
spin_lock_bh(&hinfo->lock);
hlist_add_head(&police->tcf_head, &hinfo->htab[h]);
@@ -253,14 +252,6 @@ failure:
return err;
}
-static int tcf_act_police_cleanup(struct tc_action *a, int bind)
-{
- struct tcf_police *p = a->priv;
- if (p)
- return tcf_hash_release(&p->common, bind, &police_hash_info);
- return 0;
-}
-
static int tcf_act_police(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res)
{
@@ -357,12 +348,10 @@ MODULE_LICENSE("GPL");
static struct tc_action_ops act_police_ops = {
.kind = "police",
- .hinfo = &police_hash_info,
.type = TCA_ID_POLICE,
.owner = THIS_MODULE,
.act = tcf_act_police,
.dump = tcf_act_police_dump,
- .cleanup = tcf_act_police_cleanup,
.init = tcf_act_police_locate,
.walk = tcf_act_police_walker
};
@@ -370,19 +359,12 @@ static struct tc_action_ops act_police_ops = {
static int __init
police_init_module(void)
{
- int err = tcf_hashinfo_init(&police_hash_info, POL_TAB_MASK);
- if (err)
- return err;
- err = tcf_register_action(&act_police_ops);
- if (err)
- tcf_hashinfo_destroy(&police_hash_info);
- return err;
+ return tcf_register_action(&act_police_ops, POL_TAB_MASK);
}
static void __exit
police_cleanup_module(void)
{
- tcf_hashinfo_destroy(&police_hash_info);
tcf_unregister_action(&act_police_ops);
}
diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c
index 8ef2f1fcbfba..992c2317ce88 100644
--- a/net/sched/act_simple.c
+++ b/net/sched/act_simple.c
@@ -25,7 +25,6 @@
#include <net/tc_act/tc_defact.h>
#define SIMP_TAB_MASK 7
-static struct tcf_hashinfo simp_hash_info;
#define SIMP_MAX_DATA 32
static int tcf_simp(struct sk_buff *skb, const struct tc_action *a,
@@ -47,20 +46,10 @@ static int tcf_simp(struct sk_buff *skb, const struct tc_action *a,
return d->tcf_action;
}
-static int tcf_simp_release(struct tcf_defact *d, int bind)
+static void tcf_simp_release(struct tc_action *a, int bind)
{
- int ret = 0;
- if (d) {
- if (bind)
- d->tcf_bindcnt--;
- d->tcf_refcnt--;
- if (d->tcf_bindcnt <= 0 && d->tcf_refcnt <= 0) {
- kfree(d->tcfd_defdata);
- tcf_hash_destroy(&d->common, &simp_hash_info);
- ret = 1;
- }
- }
- return ret;
+ struct tcf_defact *d = to_defact(a);
+ kfree(d->tcfd_defdata);
}
static int alloc_defdata(struct tcf_defact *d, char *defdata)
@@ -94,7 +83,6 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla,
struct nlattr *tb[TCA_DEF_MAX + 1];
struct tc_defact *parm;
struct tcf_defact *d;
- struct tcf_common *pc;
char *defdata;
int ret = 0, err;
@@ -114,29 +102,25 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla,
parm = nla_data(tb[TCA_DEF_PARMS]);
defdata = nla_data(tb[TCA_DEF_DATA]);
- pc = tcf_hash_check(parm->index, a, bind);
- if (!pc) {
- pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind);
- if (IS_ERR(pc))
- return PTR_ERR(pc);
+ if (!tcf_hash_check(parm->index, a, bind)) {
+ ret = tcf_hash_create(parm->index, est, a, sizeof(*d), bind);
+ if (ret)
+ return ret;
- d = to_defact(pc);
+ d = to_defact(a);
ret = alloc_defdata(d, defdata);
if (ret < 0) {
- if (est)
- gen_kill_estimator(&pc->tcfc_bstats,
- &pc->tcfc_rate_est);
- kfree_rcu(pc, tcfc_rcu);
+ tcf_hash_cleanup(a, est);
return ret;
}
d->tcf_action = parm->action;
ret = ACT_P_CREATED;
} else {
- d = to_defact(pc);
+ d = to_defact(a);
if (bind)
return 0;
- tcf_simp_release(d, bind);
+ tcf_hash_release(a, bind);
if (!ovr)
return -EEXIST;
@@ -144,19 +128,10 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla,
}
if (ret == ACT_P_CREATED)
- tcf_hash_insert(pc, a->ops->hinfo);
+ tcf_hash_insert(a);
return ret;
}
-static int tcf_simp_cleanup(struct tc_action *a, int bind)
-{
- struct tcf_defact *d = a->priv;
-
- if (d)
- return tcf_simp_release(d, bind);
- return 0;
-}
-
static int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a,
int bind, int ref)
{
@@ -187,12 +162,11 @@ nla_put_failure:
static struct tc_action_ops act_simp_ops = {
.kind = "simple",
- .hinfo = &simp_hash_info,
.type = TCA_ACT_SIMP,
.owner = THIS_MODULE,
.act = tcf_simp,
.dump = tcf_simp_dump,
- .cleanup = tcf_simp_cleanup,
+ .cleanup = tcf_simp_release,
.init = tcf_simp_init,
};
@@ -202,23 +176,15 @@ MODULE_LICENSE("GPL");
static int __init simp_init_module(void)
{
- int err, ret;
- err = tcf_hashinfo_init(&simp_hash_info, SIMP_TAB_MASK);
- if (err)
- return err;
-
- ret = tcf_register_action(&act_simp_ops);
+ int ret;
+ ret = tcf_register_action(&act_simp_ops, SIMP_TAB_MASK);
if (!ret)
pr_info("Simple TC action Loaded\n");
- else
- tcf_hashinfo_destroy(&simp_hash_info);
-
return ret;
}
static void __exit simp_cleanup_module(void)
{
- tcf_hashinfo_destroy(&simp_hash_info);
tcf_unregister_action(&act_simp_ops);
}
diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c
index 98725080b5aa..fcfeeaf838be 100644
--- a/net/sched/act_skbedit.c
+++ b/net/sched/act_skbedit.c
@@ -28,7 +28,6 @@
#include <net/tc_act/tc_skbedit.h>
#define SKBEDIT_TAB_MASK 15
-static struct tcf_hashinfo skbedit_hash_info;
static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res)
@@ -65,7 +64,6 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
struct nlattr *tb[TCA_SKBEDIT_MAX + 1];
struct tc_skbedit *parm;
struct tcf_skbedit *d;
- struct tcf_common *pc;
u32 flags = 0, *priority = NULL, *mark = NULL;
u16 *queue_mapping = NULL;
int ret = 0, err;
@@ -100,19 +98,18 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
parm = nla_data(tb[TCA_SKBEDIT_PARMS]);
- pc = tcf_hash_check(parm->index, a, bind);
- if (!pc) {
- pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind);
- if (IS_ERR(pc))
- return PTR_ERR(pc);
+ if (!tcf_hash_check(parm->index, a, bind)) {
+ ret = tcf_hash_create(parm->index, est, a, sizeof(*d), bind);
+ if (ret)
+ return ret;
- d = to_skbedit(pc);
+ d = to_skbedit(a);
ret = ACT_P_CREATED;
} else {
- d = to_skbedit(pc);
+ d = to_skbedit(a);
if (bind)
return 0;
- tcf_hash_release(pc, bind, a->ops->hinfo);
+ tcf_hash_release(a, bind);
if (!ovr)
return -EEXIST;
}
@@ -132,19 +129,10 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
spin_unlock_bh(&d->tcf_lock);
if (ret == ACT_P_CREATED)
- tcf_hash_insert(pc, a->ops->hinfo);
+ tcf_hash_insert(a);
return ret;
}
-static int tcf_skbedit_cleanup(struct tc_action *a, int bind)
-{
- struct tcf_skbedit *d = a->priv;
-
- if (d)
- return tcf_hash_release(&d->common, bind, &skbedit_hash_info);
- return 0;
-}
-
static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a,
int bind, int ref)
{
@@ -186,12 +174,10 @@ nla_put_failure:
static struct tc_action_ops act_skbedit_ops = {
.kind = "skbedit",
- .hinfo = &skbedit_hash_info,
.type = TCA_ACT_SKBEDIT,
.owner = THIS_MODULE,
.act = tcf_skbedit,
.dump = tcf_skbedit_dump,
- .cleanup = tcf_skbedit_cleanup,
.init = tcf_skbedit_init,
};
@@ -201,15 +187,11 @@ MODULE_LICENSE("GPL");
static int __init skbedit_init_module(void)
{
- int err = tcf_hashinfo_init(&skbedit_hash_info, SKBEDIT_TAB_MASK);
- if (err)
- return err;
- return tcf_register_action(&act_skbedit_ops);
+ return tcf_register_action(&act_skbedit_ops, SKBEDIT_TAB_MASK);
}
static void __exit skbedit_cleanup_module(void)
{
- tcf_hashinfo_destroy(&skbedit_hash_info);
tcf_unregister_action(&act_skbedit_ops);
}
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index a366537f82c6..63a3ce75c02e 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -29,11 +29,11 @@
#include <net/act_api.h>
#include <net/pkt_cls.h>
-#define HTSIZE (PAGE_SIZE/sizeof(struct fw_filter *))
+#define HTSIZE 256
struct fw_head {
- struct fw_filter *ht[HTSIZE];
- u32 mask;
+ u32 mask;
+ struct fw_filter *ht[HTSIZE];
};
struct fw_filter {
@@ -46,30 +46,11 @@ struct fw_filter {
struct tcf_exts exts;
};
-static inline int fw_hash(u32 handle)
+static u32 fw_hash(u32 handle)
{
- if (HTSIZE == 4096)
- return ((handle >> 24) & 0xFFF) ^
- ((handle >> 12) & 0xFFF) ^
- (handle & 0xFFF);
- else if (HTSIZE == 2048)
- return ((handle >> 22) & 0x7FF) ^
- ((handle >> 11) & 0x7FF) ^
- (handle & 0x7FF);
- else if (HTSIZE == 1024)
- return ((handle >> 20) & 0x3FF) ^
- ((handle >> 10) & 0x3FF) ^
- (handle & 0x3FF);
- else if (HTSIZE == 512)
- return (handle >> 27) ^
- ((handle >> 18) & 0x1FF) ^
- ((handle >> 9) & 0x1FF) ^
- (handle & 0x1FF);
- else if (HTSIZE == 256) {
- u8 *t = (u8 *) &handle;
- return t[0] ^ t[1] ^ t[2] ^ t[3];
- } else
- return handle & (HTSIZE - 1);
+ handle ^= (handle >> 16);
+ handle ^= (handle >> 8);
+ return handle % HTSIZE;
}
static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp,
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index a07d55e75698..a0b84e0e22de 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1304,6 +1304,7 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
struct gnet_dump d;
struct qdisc_size_table *stab;
+ cond_resched();
nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
if (!nlh)
goto out_nlmsg_trim;
@@ -1435,9 +1436,9 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
s_idx = cb->args[0];
s_q_idx = q_idx = cb->args[1];
- rcu_read_lock();
idx = 0;
- for_each_netdev_rcu(net, dev) {
+ ASSERT_RTNL();
+ for_each_netdev(net, dev) {
struct netdev_queue *dev_queue;
if (idx < s_idx)
@@ -1460,8 +1461,6 @@ cont:
}
done:
- rcu_read_unlock();
-
cb->args[0] = idx;
cb->args[1] = q_idx;
@@ -1618,6 +1617,7 @@ static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,
struct gnet_dump d;
const struct Qdisc_class_ops *cl_ops = q->ops->cl_ops;
+ cond_resched();
nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
if (!nlh)
goto out_nlmsg_trim;
diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c
index 1f9c31411f19..8449b337f9e3 100644
--- a/net/sched/sch_atm.c
+++ b/net/sched/sch_atm.c
@@ -623,8 +623,7 @@ static int atm_tc_dump_class(struct Qdisc *sch, unsigned long cl,
if (nla_put_u32(skb, TCA_ATM_EXCESS, 0))
goto nla_put_failure;
}
- nla_nest_end(skb, nest);
- return skb->len;
+ return nla_nest_end(skb, nest);
nla_put_failure:
nla_nest_cancel(skb, nest);
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index 2f80d01d42a6..ead526467cca 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -1563,8 +1563,7 @@ static int cbq_dump(struct Qdisc *sch, struct sk_buff *skb)
goto nla_put_failure;
if (cbq_dump_attr(skb, &q->link) < 0)
goto nla_put_failure;
- nla_nest_end(skb, nest);
- return skb->len;
+ return nla_nest_end(skb, nest);
nla_put_failure:
nla_nest_cancel(skb, nest);
@@ -1599,8 +1598,7 @@ cbq_dump_class(struct Qdisc *sch, unsigned long arg,
goto nla_put_failure;
if (cbq_dump_attr(skb, cl) < 0)
goto nla_put_failure;
- nla_nest_end(skb, nest);
- return skb->len;
+ return nla_nest_end(skb, nest);
nla_put_failure:
nla_nest_cancel(skb, nest);
diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c
index 21e251766eb1..23c682b42f99 100644
--- a/net/sched/sch_fq.c
+++ b/net/sched/sch_fq.c
@@ -781,8 +781,7 @@ static int fq_dump(struct Qdisc *sch, struct sk_buff *skb)
nla_put_u32(skb, TCA_FQ_BUCKETS_LOG, q->fq_trees_log))
goto nla_put_failure;
- nla_nest_end(skb, opts);
- return skb->len;
+ return nla_nest_end(skb, opts);
nla_put_failure:
return -1;
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index ba5bc929eac7..0bf432c782c1 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -450,8 +450,7 @@ static int fq_codel_dump(struct Qdisc *sch, struct sk_buff *skb)
q->flows_cnt))
goto nla_put_failure;
- nla_nest_end(skb, opts);
- return skb->len;
+ return nla_nest_end(skb, opts);
nla_put_failure:
return -1;
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index e82e43b69c33..e1543b03e39d 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -310,6 +310,7 @@ void netif_carrier_on(struct net_device *dev)
if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
if (dev->reg_state == NETREG_UNINITIALIZED)
return;
+ atomic_inc(&dev->carrier_changes);
linkwatch_fire_event(dev);
if (netif_running(dev))
__netdev_watchdog_up(dev);
@@ -328,6 +329,7 @@ void netif_carrier_off(struct net_device *dev)
if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
if (dev->reg_state == NETREG_UNINITIALIZED)
return;
+ atomic_inc(&dev->carrier_changes);
linkwatch_fire_event(dev);
}
}
diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
index c4075610502c..ec8aeaac1dd7 100644
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -1353,8 +1353,7 @@ hfsc_dump_class(struct Qdisc *sch, unsigned long arg, struct sk_buff *skb,
goto nla_put_failure;
if (hfsc_dump_curves(skb, cl) < 0)
goto nla_put_failure;
- nla_nest_end(skb, nest);
- return skb->len;
+ return nla_nest_end(skb, nest);
nla_put_failure:
nla_nest_cancel(skb, nest);
diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c
index 647680b1c625..edee03d922e2 100644
--- a/net/sched/sch_hhf.c
+++ b/net/sched/sch_hhf.c
@@ -691,8 +691,7 @@ static int hhf_dump(struct Qdisc *sch, struct sk_buff *skb)
nla_put_u32(skb, TCA_HHF_NON_HH_WEIGHT, q->hhf_non_hh_weight))
goto nla_put_failure;
- nla_nest_end(skb, opts);
- return skb->len;
+ return nla_nest_end(skb, opts);
nla_put_failure:
return -1;
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index 722e137df244..9f949abcacef 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -1062,12 +1062,13 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt)
static int htb_dump(struct Qdisc *sch, struct sk_buff *skb)
{
- spinlock_t *root_lock = qdisc_root_sleeping_lock(sch);
struct htb_sched *q = qdisc_priv(sch);
struct nlattr *nest;
struct tc_htb_glob gopt;
- spin_lock_bh(root_lock);
+ /* Its safe to not acquire qdisc lock. As we hold RTNL,
+ * no change can happen on the qdisc parameters.
+ */
gopt.direct_pkts = q->direct_pkts;
gopt.version = HTB_VER;
@@ -1081,13 +1082,10 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb)
if (nla_put(skb, TCA_HTB_INIT, sizeof(gopt), &gopt) ||
nla_put_u32(skb, TCA_HTB_DIRECT_QLEN, q->direct_qlen))
goto nla_put_failure;
- nla_nest_end(skb, nest);
- spin_unlock_bh(root_lock);
- return skb->len;
+ return nla_nest_end(skb, nest);
nla_put_failure:
- spin_unlock_bh(root_lock);
nla_nest_cancel(skb, nest);
return -1;
}
@@ -1096,11 +1094,12 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg,
struct sk_buff *skb, struct tcmsg *tcm)
{
struct htb_class *cl = (struct htb_class *)arg;
- spinlock_t *root_lock = qdisc_root_sleeping_lock(sch);
struct nlattr *nest;
struct tc_htb_opt opt;
- spin_lock_bh(root_lock);
+ /* Its safe to not acquire qdisc lock. As we hold RTNL,
+ * no change can happen on the class parameters.
+ */
tcm->tcm_parent = cl->parent ? cl->parent->common.classid : TC_H_ROOT;
tcm->tcm_handle = cl->common.classid;
if (!cl->level && cl->un.leaf.q)
@@ -1128,12 +1127,9 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg,
nla_put_u64(skb, TCA_HTB_CEIL64, cl->ceil.rate_bytes_ps))
goto nla_put_failure;
- nla_nest_end(skb, nest);
- spin_unlock_bh(root_lock);
- return skb->len;
+ return nla_nest_end(skb, nest);
nla_put_failure:
- spin_unlock_bh(root_lock);
nla_nest_cancel(skb, nest);
return -1;
}
diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index bce1665239b8..62871c14e1f9 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -100,8 +100,7 @@ static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb)
nest = nla_nest_start(skb, TCA_OPTIONS);
if (nest == NULL)
goto nla_put_failure;
- nla_nest_end(skb, nest);
- return skb->len;
+ return nla_nest_end(skb, nest);
nla_put_failure:
nla_nest_cancel(skb, nest);
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index de1059af6da1..f1669a00f571 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -117,6 +117,11 @@ struct netem_sched_data {
LOST_IN_BURST_PERIOD,
} _4_state_model;
+ enum {
+ GOOD_STATE = 1,
+ BAD_STATE,
+ } GE_state_model;
+
/* Correlated Loss Generation models */
struct clgstate {
/* state of the Markov chain */
@@ -272,15 +277,15 @@ static bool loss_gilb_ell(struct netem_sched_data *q)
struct clgstate *clg = &q->clg;
switch (clg->state) {
- case 1:
+ case GOOD_STATE:
if (prandom_u32() < clg->a1)
- clg->state = 2;
+ clg->state = BAD_STATE;
if (prandom_u32() < clg->a4)
return true;
break;
- case 2:
+ case BAD_STATE:
if (prandom_u32() < clg->a2)
- clg->state = 1;
+ clg->state = GOOD_STATE;
if (prandom_u32() > clg->a3)
return true;
}
@@ -689,9 +694,8 @@ static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr)
return 0;
}
-static void get_correlation(struct Qdisc *sch, const struct nlattr *attr)
+static void get_correlation(struct netem_sched_data *q, const struct nlattr *attr)
{
- struct netem_sched_data *q = qdisc_priv(sch);
const struct tc_netem_corr *c = nla_data(attr);
init_crandom(&q->delay_cor, c->delay_corr);
@@ -699,27 +703,24 @@ static void get_correlation(struct Qdisc *sch, const struct nlattr *attr)
init_crandom(&q->dup_cor, c->dup_corr);
}
-static void get_reorder(struct Qdisc *sch, const struct nlattr *attr)
+static void get_reorder(struct netem_sched_data *q, const struct nlattr *attr)
{
- struct netem_sched_data *q = qdisc_priv(sch);
const struct tc_netem_reorder *r = nla_data(attr);
q->reorder = r->probability;
init_crandom(&q->reorder_cor, r->correlation);
}
-static void get_corrupt(struct Qdisc *sch, const struct nlattr *attr)
+static void get_corrupt(struct netem_sched_data *q, const struct nlattr *attr)
{
- struct netem_sched_data *q = qdisc_priv(sch);
const struct tc_netem_corrupt *r = nla_data(attr);
q->corrupt = r->probability;
init_crandom(&q->corrupt_cor, r->correlation);
}
-static void get_rate(struct Qdisc *sch, const struct nlattr *attr)
+static void get_rate(struct netem_sched_data *q, const struct nlattr *attr)
{
- struct netem_sched_data *q = qdisc_priv(sch);
const struct tc_netem_rate *r = nla_data(attr);
q->rate = r->rate;
@@ -732,9 +733,8 @@ static void get_rate(struct Qdisc *sch, const struct nlattr *attr)
q->cell_size_reciprocal = (struct reciprocal_value) { 0 };
}
-static int get_loss_clg(struct Qdisc *sch, const struct nlattr *attr)
+static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr)
{
- struct netem_sched_data *q = qdisc_priv(sch);
const struct nlattr *la;
int rem;
@@ -752,7 +752,7 @@ static int get_loss_clg(struct Qdisc *sch, const struct nlattr *attr)
q->loss_model = CLG_4_STATES;
- q->clg.state = 1;
+ q->clg.state = TX_IN_GAP_PERIOD;
q->clg.a1 = gi->p13;
q->clg.a2 = gi->p31;
q->clg.a3 = gi->p32;
@@ -770,7 +770,7 @@ static int get_loss_clg(struct Qdisc *sch, const struct nlattr *attr)
}
q->loss_model = CLG_GILB_ELL;
- q->clg.state = 1;
+ q->clg.state = GOOD_STATE;
q->clg.a1 = ge->p;
q->clg.a2 = ge->r;
q->clg.a3 = ge->h;
@@ -821,6 +821,8 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
struct netem_sched_data *q = qdisc_priv(sch);
struct nlattr *tb[TCA_NETEM_MAX + 1];
struct tc_netem_qopt *qopt;
+ struct clgstate old_clg;
+ int old_loss_model = CLG_RANDOM;
int ret;
if (opt == NULL)
@@ -831,6 +833,33 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
if (ret < 0)
return ret;
+ /* backup q->clg and q->loss_model */
+ old_clg = q->clg;
+ old_loss_model = q->loss_model;
+
+ if (tb[TCA_NETEM_LOSS]) {
+ ret = get_loss_clg(q, tb[TCA_NETEM_LOSS]);
+ if (ret) {
+ q->loss_model = old_loss_model;
+ return ret;
+ }
+ } else {
+ q->loss_model = CLG_RANDOM;
+ }
+
+ if (tb[TCA_NETEM_DELAY_DIST]) {
+ ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST]);
+ if (ret) {
+ /* recover clg and loss_model, in case of
+ * q->clg and q->loss_model were modified
+ * in get_loss_clg()
+ */
+ q->clg = old_clg;
+ q->loss_model = old_loss_model;
+ return ret;
+ }
+ }
+
sch->limit = qopt->limit;
q->latency = qopt->latency;
@@ -848,22 +877,16 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
q->reorder = ~0;
if (tb[TCA_NETEM_CORR])
- get_correlation(sch, tb[TCA_NETEM_CORR]);
-
- if (tb[TCA_NETEM_DELAY_DIST]) {
- ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST]);
- if (ret)
- return ret;
- }
+ get_correlation(q, tb[TCA_NETEM_CORR]);
if (tb[TCA_NETEM_REORDER])
- get_reorder(sch, tb[TCA_NETEM_REORDER]);
+ get_reorder(q, tb[TCA_NETEM_REORDER]);
if (tb[TCA_NETEM_CORRUPT])
- get_corrupt(sch, tb[TCA_NETEM_CORRUPT]);
+ get_corrupt(q, tb[TCA_NETEM_CORRUPT]);
if (tb[TCA_NETEM_RATE])
- get_rate(sch, tb[TCA_NETEM_RATE]);
+ get_rate(q, tb[TCA_NETEM_RATE]);
if (tb[TCA_NETEM_RATE64])
q->rate = max_t(u64, q->rate,
@@ -872,10 +895,6 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
if (tb[TCA_NETEM_ECN])
q->ecn = nla_get_u32(tb[TCA_NETEM_ECN]);
- q->loss_model = CLG_RANDOM;
- if (tb[TCA_NETEM_LOSS])
- ret = get_loss_clg(sch, tb[TCA_NETEM_LOSS]);
-
return ret;
}
diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
index 4f505a006896..18ff63433709 100644
--- a/net/sched/sch_tbf.c
+++ b/net/sched/sch_tbf.c
@@ -101,12 +101,11 @@
struct tbf_sched_data {
/* Parameters */
u32 limit; /* Maximal length of backlog: bytes */
+ u32 max_size;
s64 buffer; /* Token bucket depth/rate: MUST BE >= MTU/B */
s64 mtu;
- u32 max_size;
struct psched_ratecfg rate;
struct psched_ratecfg peak;
- bool peak_present;
/* Variables */
s64 tokens; /* Current number of B tokens */
@@ -222,6 +221,11 @@ static unsigned int tbf_drop(struct Qdisc *sch)
return len;
}
+static bool tbf_peak_present(const struct tbf_sched_data *q)
+{
+ return q->peak.rate_bytes_ps;
+}
+
static struct sk_buff *tbf_dequeue(struct Qdisc *sch)
{
struct tbf_sched_data *q = qdisc_priv(sch);
@@ -238,7 +242,7 @@ static struct sk_buff *tbf_dequeue(struct Qdisc *sch)
now = ktime_to_ns(ktime_get());
toks = min_t(s64, now - q->t_c, q->buffer);
- if (q->peak_present) {
+ if (tbf_peak_present(q)) {
ptoks = toks + q->ptokens;
if (ptoks > q->mtu)
ptoks = q->mtu;
@@ -366,6 +370,8 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt)
} else {
max_size = min_t(u64, max_size, psched_ns_t2l(&peak, mtu));
}
+ } else {
+ memset(&peak, 0, sizeof(peak));
}
if (max_size < psched_mtu(qdisc_dev(sch)))
@@ -410,12 +416,7 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt)
q->ptokens = q->mtu;
memcpy(&q->rate, &rate, sizeof(struct psched_ratecfg));
- if (qopt->peakrate.rate) {
- memcpy(&q->peak, &peak, sizeof(struct psched_ratecfg));
- q->peak_present = true;
- } else {
- q->peak_present = false;
- }
+ memcpy(&q->peak, &peak, sizeof(struct psched_ratecfg));
sch_tree_unlock(sch);
err = 0;
@@ -458,7 +459,7 @@ static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb)
opt.limit = q->limit;
psched_ratecfg_getrate(&opt.rate, &q->rate);
- if (q->peak_present)
+ if (tbf_peak_present(q))
psched_ratecfg_getrate(&opt.peakrate, &q->peak);
else
memset(&opt.peakrate, 0, sizeof(opt.peakrate));
@@ -469,13 +470,12 @@ static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb)
if (q->rate.rate_bytes_ps >= (1ULL << 32) &&
nla_put_u64(skb, TCA_TBF_RATE64, q->rate.rate_bytes_ps))
goto nla_put_failure;
- if (q->peak_present &&
+ if (tbf_peak_present(q) &&
q->peak.rate_bytes_ps >= (1ULL << 32) &&
nla_put_u64(skb, TCA_TBF_PRATE64, q->peak.rate_bytes_ps))
goto nla_put_failure;
- nla_nest_end(skb, nest);
- return skb->len;
+ return nla_nest_end(skb, nest);
nla_put_failure:
nla_nest_cancel(skb, nest);
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index ee13d28d39d1..4f6d6f9d1274 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1319,8 +1319,7 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
break;
}
- if (trans_next != NULL)
- asoc->peer.retran_path = trans_next;
+ asoc->peer.retran_path = trans_next;
pr_debug("%s: association:%p updated new path to addr:%pISpc\n",
__func__, asoc, &asoc->peer.retran_path->ipaddr.sa);
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index d0810dc5f079..1d348d15b33d 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -652,5 +652,4 @@ void sctp_transport_immediate_rtx(struct sctp_transport *t)
if (!mod_timer(&t->T3_rtx_timer, jiffies + t->rto))
sctp_transport_hold(t);
}
- return;
}
diff --git a/net/socket.c b/net/socket.c
index a19ae1968d37..1b1e7e6a960f 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -72,6 +72,7 @@
#include <linux/if_bridge.h>
#include <linux/if_frad.h>
#include <linux/if_vlan.h>
+#include <linux/ptp_classify.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/cache.h>
@@ -594,7 +595,7 @@ void sock_release(struct socket *sock)
}
if (rcu_dereference_protected(sock->wq, 1)->fasync_list)
- printk(KERN_ERR "sock_release: fasync list not empty!\n");
+ pr_err("%s: fasync list not empty!\n", __func__);
if (test_bit(SOCK_EXTERNALLY_ALLOCATED, &sock->flags))
return;
@@ -1266,8 +1267,8 @@ int __sock_create(struct net *net, int family, int type, int protocol,
static int warned;
if (!warned) {
warned = 1;
- printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n",
- current->comm);
+ pr_info("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
+ current->comm);
}
family = PF_PACKET;
}
@@ -2600,8 +2601,7 @@ int sock_register(const struct net_proto_family *ops)
int err;
if (ops->family >= NPROTO) {
- printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n", ops->family,
- NPROTO);
+ pr_crit("protocol %d >= NPROTO(%d)\n", ops->family, NPROTO);
return -ENOBUFS;
}
@@ -2615,7 +2615,7 @@ int sock_register(const struct net_proto_family *ops)
}
spin_unlock(&net_family_lock);
- printk(KERN_INFO "NET: Registered protocol family %d\n", ops->family);
+ pr_info("NET: Registered protocol family %d\n", ops->family);
return err;
}
EXPORT_SYMBOL(sock_register);
@@ -2643,7 +2643,7 @@ void sock_unregister(int family)
synchronize_rcu();
- printk(KERN_INFO "NET: Unregistered protocol family %d\n", family);
+ pr_info("NET: Unregistered protocol family %d\n", family);
}
EXPORT_SYMBOL(sock_unregister);
@@ -2686,9 +2686,7 @@ static int __init sock_init(void)
goto out;
#endif
-#ifdef CONFIG_NETWORK_PHY_TIMESTAMPING
- skb_timestamping_init();
-#endif
+ ptp_classifier_init();
out:
return err;
diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c
index e860d4f7ed2a..3513d559bc45 100644
--- a/net/sunrpc/backchannel_rqst.c
+++ b/net/sunrpc/backchannel_rqst.c
@@ -212,39 +212,23 @@ out:
}
EXPORT_SYMBOL_GPL(xprt_destroy_backchannel);
-/*
- * One or more rpc_rqst structure have been preallocated during the
- * backchannel setup. Buffer space for the send and private XDR buffers
- * has been preallocated as well. Use xprt_alloc_bc_request to allocate
- * to this request. Use xprt_free_bc_request to return it.
- *
- * We know that we're called in soft interrupt context, grab the spin_lock
- * since there is no need to grab the bottom half spin_lock.
- *
- * Return an available rpc_rqst, otherwise NULL if non are available.
- */
-struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt)
+static struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt, __be32 xid)
{
- struct rpc_rqst *req;
+ struct rpc_rqst *req = NULL;
dprintk("RPC: allocate a backchannel request\n");
- spin_lock(&xprt->bc_pa_lock);
- if (!list_empty(&xprt->bc_pa_list)) {
- req = list_first_entry(&xprt->bc_pa_list, struct rpc_rqst,
- rq_bc_pa_list);
- list_del(&req->rq_bc_pa_list);
- } else {
- req = NULL;
- }
- spin_unlock(&xprt->bc_pa_lock);
+ if (list_empty(&xprt->bc_pa_list))
+ goto not_found;
- if (req != NULL) {
- set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
- req->rq_reply_bytes_recvd = 0;
- req->rq_bytes_sent = 0;
- memcpy(&req->rq_private_buf, &req->rq_rcv_buf,
+ req = list_first_entry(&xprt->bc_pa_list, struct rpc_rqst,
+ rq_bc_pa_list);
+ req->rq_reply_bytes_recvd = 0;
+ req->rq_bytes_sent = 0;
+ memcpy(&req->rq_private_buf, &req->rq_rcv_buf,
sizeof(req->rq_private_buf));
- }
+ req->rq_xid = xid;
+ req->rq_connect_cookie = xprt->connect_cookie;
+not_found:
dprintk("RPC: backchannel req=%p\n", req);
return req;
}
@@ -259,6 +243,7 @@ void xprt_free_bc_request(struct rpc_rqst *req)
dprintk("RPC: free backchannel req=%p\n", req);
+ req->rq_connect_cookie = xprt->connect_cookie - 1;
smp_mb__before_clear_bit();
WARN_ON_ONCE(!test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state));
clear_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
@@ -281,7 +266,57 @@ void xprt_free_bc_request(struct rpc_rqst *req)
* may be reused by a new callback request.
*/
spin_lock_bh(&xprt->bc_pa_lock);
- list_add(&req->rq_bc_pa_list, &xprt->bc_pa_list);
+ list_add_tail(&req->rq_bc_pa_list, &xprt->bc_pa_list);
spin_unlock_bh(&xprt->bc_pa_lock);
}
+/*
+ * One or more rpc_rqst structure have been preallocated during the
+ * backchannel setup. Buffer space for the send and private XDR buffers
+ * has been preallocated as well. Use xprt_alloc_bc_request to allocate
+ * to this request. Use xprt_free_bc_request to return it.
+ *
+ * We know that we're called in soft interrupt context, grab the spin_lock
+ * since there is no need to grab the bottom half spin_lock.
+ *
+ * Return an available rpc_rqst, otherwise NULL if non are available.
+ */
+struct rpc_rqst *xprt_lookup_bc_request(struct rpc_xprt *xprt, __be32 xid)
+{
+ struct rpc_rqst *req;
+
+ spin_lock(&xprt->bc_pa_lock);
+ list_for_each_entry(req, &xprt->bc_pa_list, rq_bc_pa_list) {
+ if (req->rq_connect_cookie != xprt->connect_cookie)
+ continue;
+ if (req->rq_xid == xid)
+ goto found;
+ }
+ req = xprt_alloc_bc_request(xprt, xid);
+found:
+ spin_unlock(&xprt->bc_pa_lock);
+ return req;
+}
+
+/*
+ * Add callback request to callback list. The callback
+ * service sleeps on the sv_cb_waitq waiting for new
+ * requests. Wake it up after adding enqueing the
+ * request.
+ */
+void xprt_complete_bc_request(struct rpc_rqst *req, uint32_t copied)
+{
+ struct rpc_xprt *xprt = req->rq_xprt;
+ struct svc_serv *bc_serv = xprt->bc_serv;
+
+ req->rq_private_buf.len = copied;
+ set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
+
+ dprintk("RPC: add callback request to list\n");
+ spin_lock(&bc_serv->sv_cb_lock);
+ list_del(&req->rq_bc_pa_list);
+ list_add(&req->rq_bc_list, &bc_serv->sv_cb_list);
+ wake_up(&bc_serv->sv_cb_waitq);
+ spin_unlock(&bc_serv->sv_cb_lock);
+}
+
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 0edada973434..f400445d1a44 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -1363,6 +1363,7 @@ rpc_restart_call_prepare(struct rpc_task *task)
if (RPC_ASSASSINATED(task))
return 0;
task->tk_action = call_start;
+ task->tk_status = 0;
if (task->tk_ops->rpc_call_prepare != NULL)
task->tk_action = rpc_prepare_task;
return 1;
@@ -1379,6 +1380,7 @@ rpc_restart_call(struct rpc_task *task)
if (RPC_ASSASSINATED(task))
return 0;
task->tk_action = call_start;
+ task->tk_status = 0;
return 1;
}
EXPORT_SYMBOL_GPL(rpc_restart_call);
@@ -1728,9 +1730,7 @@ call_bind_status(struct rpc_task *task)
case -EPROTONOSUPPORT:
dprintk("RPC: %5u remote rpcbind version unavailable, retrying\n",
task->tk_pid);
- task->tk_status = 0;
- task->tk_action = call_bind;
- return;
+ goto retry_timeout;
case -ECONNREFUSED: /* connection problems */
case -ECONNRESET:
case -ECONNABORTED:
@@ -1756,6 +1756,7 @@ call_bind_status(struct rpc_task *task)
return;
retry_timeout:
+ task->tk_status = 0;
task->tk_action = call_timeout;
}
@@ -1798,21 +1799,19 @@ call_connect_status(struct rpc_task *task)
trace_rpc_connect_status(task, status);
task->tk_status = 0;
switch (status) {
- /* if soft mounted, test if we've timed out */
- case -ETIMEDOUT:
- task->tk_action = call_timeout;
- return;
case -ECONNREFUSED:
case -ECONNRESET:
case -ECONNABORTED:
case -ENETUNREACH:
case -EHOSTUNREACH:
- /* retry with existing socket, after a delay */
- rpc_delay(task, 3*HZ);
if (RPC_IS_SOFTCONN(task))
break;
+ /* retry with existing socket, after a delay */
+ rpc_delay(task, 3*HZ);
case -EAGAIN:
- task->tk_action = call_bind;
+ /* Check for timeouts before looping back to call_bind */
+ case -ETIMEDOUT:
+ task->tk_action = call_timeout;
return;
case 0:
clnt->cl_stats->netreconn++;
@@ -2007,6 +2006,10 @@ call_status(struct rpc_task *task)
case -EHOSTDOWN:
case -EHOSTUNREACH:
case -ENETUNREACH:
+ if (RPC_IS_SOFTCONN(task)) {
+ rpc_exit(task, status);
+ break;
+ }
/*
* Delay any retries for 3 seconds, then handle as if it
* were a timeout.
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index ff3cc4bf4b24..25578afe1548 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -637,7 +637,8 @@ static void __rpc_queue_timer_fn(unsigned long ptr)
static void __rpc_atrun(struct rpc_task *task)
{
- task->tk_status = 0;
+ if (task->tk_status == -ETIMEDOUT)
+ task->tk_status = 0;
}
/*
diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
index e03725bfe2b8..96ead526b125 100644
--- a/net/sunrpc/xprtrdma/rpc_rdma.c
+++ b/net/sunrpc/xprtrdma/rpc_rdma.c
@@ -649,9 +649,7 @@ rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len, int pad)
break;
page_base = 0;
}
- rqst->rq_rcv_buf.page_len = olen - copy_len;
- } else
- rqst->rq_rcv_buf.page_len = 0;
+ }
if (copy_len && rqst->rq_rcv_buf.tail[0].iov_len) {
curlen = copy_len;
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c
index 285dc0884115..1eb9c468d0c9 100644
--- a/net/sunrpc/xprtrdma/transport.c
+++ b/net/sunrpc/xprtrdma/transport.c
@@ -733,7 +733,7 @@ static void __exit xprt_rdma_cleanup(void)
{
int rc;
- dprintk(KERN_INFO "RPCRDMA Module Removed, deregister RPC RDMA transport\n");
+ dprintk("RPCRDMA Module Removed, deregister RPC RDMA transport\n");
#ifdef RPC_DEBUG
if (sunrpc_table_header) {
unregister_sysctl_table(sunrpc_table_header);
@@ -755,14 +755,14 @@ static int __init xprt_rdma_init(void)
if (rc)
return rc;
- dprintk(KERN_INFO "RPCRDMA Module Init, register RPC RDMA transport\n");
+ dprintk("RPCRDMA Module Init, register RPC RDMA transport\n");
- dprintk(KERN_INFO "Defaults:\n");
- dprintk(KERN_INFO "\tSlots %d\n"
+ dprintk("Defaults:\n");
+ dprintk("\tSlots %d\n"
"\tMaxInlineRead %d\n\tMaxInlineWrite %d\n",
xprt_rdma_slot_table_entries,
xprt_rdma_max_inline_read, xprt_rdma_max_inline_write);
- dprintk(KERN_INFO "\tPadding %d\n\tMemreg %d\n",
+ dprintk("\tPadding %d\n\tMemreg %d\n",
xprt_rdma_inline_write_padding, xprt_rdma_memreg_strategy);
#ifdef RPC_DEBUG
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 0addefca8e77..966763d735e9 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -1306,41 +1306,29 @@ static inline int xs_tcp_read_reply(struct rpc_xprt *xprt,
* If we're unable to obtain the rpc_rqst we schedule the closing of the
* connection and return -1.
*/
-static inline int xs_tcp_read_callback(struct rpc_xprt *xprt,
+static int xs_tcp_read_callback(struct rpc_xprt *xprt,
struct xdr_skb_reader *desc)
{
struct sock_xprt *transport =
container_of(xprt, struct sock_xprt, xprt);
struct rpc_rqst *req;
- req = xprt_alloc_bc_request(xprt);
+ /* Look up and lock the request corresponding to the given XID */
+ spin_lock(&xprt->transport_lock);
+ req = xprt_lookup_bc_request(xprt, transport->tcp_xid);
if (req == NULL) {
+ spin_unlock(&xprt->transport_lock);
printk(KERN_WARNING "Callback slot table overflowed\n");
xprt_force_disconnect(xprt);
return -1;
}
- req->rq_xid = transport->tcp_xid;
dprintk("RPC: read callback XID %08x\n", ntohl(req->rq_xid));
xs_tcp_read_common(xprt, desc, req);
- if (!(transport->tcp_flags & TCP_RCV_COPY_DATA)) {
- struct svc_serv *bc_serv = xprt->bc_serv;
-
- /*
- * Add callback request to callback list. The callback
- * service sleeps on the sv_cb_waitq waiting for new
- * requests. Wake it up after adding enqueing the
- * request.
- */
- dprintk("RPC: add callback request to list\n");
- spin_lock(&bc_serv->sv_cb_lock);
- list_add(&req->rq_bc_list, &bc_serv->sv_cb_list);
- spin_unlock(&bc_serv->sv_cb_lock);
- wake_up(&bc_serv->sv_cb_waitq);
- }
-
- req->rq_private_buf.len = transport->tcp_copied;
+ if (!(transport->tcp_flags & TCP_RCV_COPY_DATA))
+ xprt_complete_bc_request(req, transport->tcp_copied);
+ spin_unlock(&xprt->transport_lock);
return 0;
}
diff --git a/net/tipc/addr.h b/net/tipc/addr.h
index 60b00ab93d74..a74acf9ee804 100644
--- a/net/tipc/addr.h
+++ b/net/tipc/addr.h
@@ -37,6 +37,8 @@
#ifndef _TIPC_ADDR_H
#define _TIPC_ADDR_H
+#include "core.h"
+
#define TIPC_ZONE_MASK 0xff000000u
#define TIPC_CLUSTER_MASK 0xfffff000u
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c
index bf860d9e75af..95ab5ef92920 100644
--- a/net/tipc/bcast.c
+++ b/net/tipc/bcast.c
@@ -41,9 +41,9 @@
#include "bcast.h"
#include "name_distr.h"
-#define MAX_PKT_DEFAULT_MCAST 1500 /* bcast link max packet size (fixed) */
-
-#define BCLINK_WIN_DEFAULT 20 /* bcast link window size (default) */
+#define MAX_PKT_DEFAULT_MCAST 1500 /* bcast link max packet size (fixed) */
+#define BCLINK_WIN_DEFAULT 20 /* bcast link window size (default) */
+#define BCBEARER MAX_BEARERS
/**
* struct tipc_bcbearer_pair - a pair of bearers used by broadcast link
@@ -356,9 +356,9 @@ static void bclink_peek_nack(struct tipc_msg *msg)
}
/*
- * tipc_bclink_send_msg - broadcast a packet to all nodes in cluster
+ * tipc_bclink_xmit - broadcast a packet to all nodes in cluster
*/
-int tipc_bclink_send_msg(struct sk_buff *buf)
+int tipc_bclink_xmit(struct sk_buff *buf)
{
int res;
@@ -370,7 +370,7 @@ int tipc_bclink_send_msg(struct sk_buff *buf)
goto exit;
}
- res = tipc_link_send_buf(bcl, buf);
+ res = __tipc_link_xmit(bcl, buf);
if (likely(res >= 0)) {
bclink_set_last_sent();
bcl->stats.queue_sz_counts++;
@@ -399,19 +399,18 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)
*/
if (((seqno - tipc_own_addr) % TIPC_MIN_LINK_WIN) == 0) {
- tipc_link_send_proto_msg(
- node->active_links[node->addr & 1],
- STATE_MSG, 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(node->active_links[node->addr & 1],
+ STATE_MSG, 0, 0, 0, 0, 0);
bcl->stats.sent_acks++;
}
}
/**
- * tipc_bclink_recv_pkt - receive a broadcast packet, and deliver upwards
+ * tipc_bclink_rcv - receive a broadcast packet, and deliver upwards
*
* tipc_net_lock is read_locked, no other locks set
*/
-void tipc_bclink_recv_pkt(struct sk_buff *buf)
+void tipc_bclink_rcv(struct sk_buff *buf)
{
struct tipc_msg *msg = buf_msg(buf);
struct tipc_node *node;
@@ -468,7 +467,7 @@ receive:
spin_unlock_bh(&bc_lock);
tipc_node_unlock(node);
if (likely(msg_mcast(msg)))
- tipc_port_recv_mcast(buf, NULL);
+ tipc_port_mcast_rcv(buf, NULL);
else
kfree_skb(buf);
} else if (msg_user(msg) == MSG_BUNDLER) {
@@ -478,12 +477,12 @@ receive:
bcl->stats.recv_bundled += msg_msgcnt(msg);
spin_unlock_bh(&bc_lock);
tipc_node_unlock(node);
- tipc_link_recv_bundle(buf);
+ tipc_link_bundle_rcv(buf);
} else if (msg_user(msg) == MSG_FRAGMENTER) {
int ret;
- ret = tipc_link_recv_fragment(&node->bclink.reasm_head,
- &node->bclink.reasm_tail,
- &buf);
+ ret = tipc_link_frag_rcv(&node->bclink.reasm_head,
+ &node->bclink.reasm_tail,
+ &buf);
if (ret == LINK_REASM_ERROR)
goto unlock;
spin_lock_bh(&bc_lock);
@@ -503,7 +502,7 @@ receive:
bclink_accept_pkt(node, seqno);
spin_unlock_bh(&bc_lock);
tipc_node_unlock(node);
- tipc_named_recv(buf);
+ tipc_named_rcv(buf);
} else {
spin_lock_bh(&bc_lock);
bclink_accept_pkt(node, seqno);
@@ -669,9 +668,8 @@ void tipc_bcbearer_sort(void)
memset(bp_temp, 0, sizeof(bcbearer->bpairs_temp));
for (b_index = 0; b_index < MAX_BEARERS; b_index++) {
- struct tipc_bearer *b = &tipc_bearers[b_index];
-
- if (!b->active || !b->nodes.count)
+ struct tipc_bearer *b = bearer_list[b_index];
+ if (!b || !b->nodes.count)
continue;
if (!bp_temp[b->priority].primary)
@@ -785,8 +783,8 @@ void tipc_bclink_init(void)
bcl->owner = &bclink->node;
bcl->max_pkt = MAX_PKT_DEFAULT_MCAST;
tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT);
- spin_lock_init(&bcbearer->bearer.lock);
bcl->b_ptr = &bcbearer->bearer;
+ bearer_list[BCBEARER] = &bcbearer->bearer;
bcl->state = WORKING_WORKING;
strlcpy(bcl->name, tipc_bclink_name, TIPC_MAX_LINK_NAME);
}
@@ -797,6 +795,7 @@ void tipc_bclink_stop(void)
tipc_link_purge_queues(bcl);
spin_unlock_bh(&bc_lock);
+ bearer_list[BCBEARER] = NULL;
memset(bclink, 0, sizeof(*bclink));
memset(bcbearer, 0, sizeof(*bcbearer));
}
diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h
index 6ee587b469fd..a80ef54b818e 100644
--- a/net/tipc/bcast.h
+++ b/net/tipc/bcast.h
@@ -90,8 +90,8 @@ void tipc_bclink_add_node(u32 addr);
void tipc_bclink_remove_node(u32 addr);
struct tipc_node *tipc_bclink_retransmit_to(void);
void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked);
-int tipc_bclink_send_msg(struct sk_buff *buf);
-void tipc_bclink_recv_pkt(struct sk_buff *buf);
+int tipc_bclink_xmit(struct sk_buff *buf);
+void tipc_bclink_rcv(struct sk_buff *buf);
u32 tipc_bclink_get_last_sent(void);
u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr);
void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent);
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 574b86193b15..3fef7eb776dc 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -49,9 +49,9 @@ static struct tipc_media * const media_info_array[] = {
NULL
};
-struct tipc_bearer tipc_bearers[MAX_BEARERS];
+struct tipc_bearer *bearer_list[MAX_BEARERS + 1];
-static void bearer_disable(struct tipc_bearer *b_ptr);
+static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down);
/**
* tipc_media_find - locates specified media object by name
@@ -177,8 +177,9 @@ struct tipc_bearer *tipc_bearer_find(const char *name)
struct tipc_bearer *b_ptr;
u32 i;
- for (i = 0, b_ptr = tipc_bearers; i < MAX_BEARERS; i++, b_ptr++) {
- if (b_ptr->active && (!strcmp(b_ptr->name, name)))
+ for (i = 0; i < MAX_BEARERS; i++) {
+ b_ptr = bearer_list[i];
+ if (b_ptr && (!strcmp(b_ptr->name, name)))
return b_ptr;
}
return NULL;
@@ -200,8 +201,10 @@ struct sk_buff *tipc_bearer_get_names(void)
read_lock_bh(&tipc_net_lock);
for (i = 0; media_info_array[i] != NULL; i++) {
for (j = 0; j < MAX_BEARERS; j++) {
- b = &tipc_bearers[j];
- if (b->active && (b->media == media_info_array[i])) {
+ b = bearer_list[j];
+ if (!b)
+ continue;
+ if (b->media == media_info_array[i]) {
tipc_cfg_append_tlv(buf, TIPC_TLV_BEARER_NAME,
b->name,
strlen(b->name) + 1);
@@ -284,16 +287,17 @@ restart:
bearer_id = MAX_BEARERS;
with_this_prio = 1;
for (i = MAX_BEARERS; i-- != 0; ) {
- if (!tipc_bearers[i].active) {
+ b_ptr = bearer_list[i];
+ if (!b_ptr) {
bearer_id = i;
continue;
}
- if (!strcmp(name, tipc_bearers[i].name)) {
+ if (!strcmp(name, b_ptr->name)) {
pr_warn("Bearer <%s> rejected, already enabled\n",
name);
goto exit;
}
- if ((tipc_bearers[i].priority == priority) &&
+ if ((b_ptr->priority == priority) &&
(++with_this_prio > 2)) {
if (priority-- == 0) {
pr_warn("Bearer <%s> rejected, duplicate priority\n",
@@ -311,7 +315,11 @@ restart:
goto exit;
}
- b_ptr = &tipc_bearers[bearer_id];
+ b_ptr = kzalloc(sizeof(*b_ptr), GFP_ATOMIC);
+ if (!b_ptr) {
+ res = -ENOMEM;
+ goto exit;
+ }
strcpy(b_ptr->name, name);
b_ptr->media = m_ptr;
res = m_ptr->enable_media(b_ptr);
@@ -324,19 +332,20 @@ restart:
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->active = 1;
b_ptr->priority = priority;
- INIT_LIST_HEAD(&b_ptr->links);
- spin_lock_init(&b_ptr->lock);
- res = tipc_disc_create(b_ptr, &b_ptr->bcast_addr, disc_domain);
+ res = tipc_disc_create(b_ptr, &b_ptr->bcast_addr);
if (res) {
- bearer_disable(b_ptr);
+ bearer_disable(b_ptr, false);
pr_warn("Bearer <%s> rejected, discovery object creation failed\n",
name);
goto exit;
}
+
+ bearer_list[bearer_id] = b_ptr;
+
pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",
name,
tipc_addr_string_fill(addr_string, disc_domain), priority);
@@ -350,20 +359,11 @@ exit:
*/
static int tipc_reset_bearer(struct tipc_bearer *b_ptr)
{
- struct tipc_link *l_ptr;
- struct tipc_link *temp_l_ptr;
-
read_lock_bh(&tipc_net_lock);
pr_info("Resetting bearer <%s>\n", b_ptr->name);
- spin_lock_bh(&b_ptr->lock);
- list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
- struct tipc_node *n_ptr = l_ptr->owner;
-
- spin_lock_bh(&n_ptr->lock);
- tipc_link_reset(l_ptr);
- spin_unlock_bh(&n_ptr->lock);
- }
- spin_unlock_bh(&b_ptr->lock);
+ tipc_disc_delete(b_ptr->link_req);
+ tipc_link_reset_list(b_ptr->identity);
+ tipc_disc_create(b_ptr, &b_ptr->bcast_addr);
read_unlock_bh(&tipc_net_lock);
return 0;
}
@@ -373,26 +373,24 @@ static int tipc_reset_bearer(struct tipc_bearer *b_ptr)
*
* Note: This routine assumes caller holds tipc_net_lock.
*/
-static void bearer_disable(struct tipc_bearer *b_ptr)
+static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down)
{
- struct tipc_link *l_ptr;
- struct tipc_link *temp_l_ptr;
- struct tipc_link_req *temp_req;
+ u32 i;
pr_info("Disabling bearer <%s>\n", b_ptr->name);
- spin_lock_bh(&b_ptr->lock);
b_ptr->media->disable_media(b_ptr);
- list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
- tipc_link_delete(l_ptr);
- }
- temp_req = b_ptr->link_req;
- b_ptr->link_req = NULL;
- spin_unlock_bh(&b_ptr->lock);
- if (temp_req)
- tipc_disc_delete(temp_req);
+ tipc_link_delete_list(b_ptr->identity, shutting_down);
+ if (b_ptr->link_req)
+ tipc_disc_delete(b_ptr->link_req);
- memset(b_ptr, 0, sizeof(struct tipc_bearer));
+ for (i = 0; i < MAX_BEARERS; i++) {
+ if (b_ptr == bearer_list[i]) {
+ bearer_list[i] = NULL;
+ break;
+ }
+ }
+ kfree(b_ptr);
}
int tipc_disable_bearer(const char *name)
@@ -406,7 +404,7 @@ int tipc_disable_bearer(const char *name)
pr_warn("Attempt to disable unknown bearer <%s>\n", name);
res = -EINVAL;
} else {
- bearer_disable(b_ptr);
+ bearer_disable(b_ptr, false);
res = 0;
}
write_unlock_bh(&tipc_net_lock);
@@ -585,7 +583,11 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
break;
case NETDEV_DOWN:
case NETDEV_CHANGEMTU:
+ tipc_reset_bearer(b_ptr);
+ break;
case NETDEV_CHANGEADDR:
+ tipc_l2_media_addr_set(b_ptr, &b_ptr->addr,
+ (char *)dev->dev_addr);
tipc_reset_bearer(b_ptr);
break;
case NETDEV_UNREGISTER:
@@ -599,7 +601,7 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
}
static struct packet_type tipc_packet_type __read_mostly = {
- .type = __constant_htons(ETH_P_TIPC),
+ .type = htons(ETH_P_TIPC),
.func = tipc_l2_rcv_msg,
};
@@ -627,10 +629,14 @@ void tipc_bearer_cleanup(void)
void tipc_bearer_stop(void)
{
+ struct tipc_bearer *b_ptr;
u32 i;
for (i = 0; i < MAX_BEARERS; i++) {
- if (tipc_bearers[i].active)
- bearer_disable(&tipc_bearers[i]);
+ b_ptr = bearer_list[i];
+ if (b_ptr) {
+ bearer_disable(b_ptr, true);
+ bearer_list[i] = NULL;
+ }
}
}
diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h
index 4f5db9ad5bf6..ba48145e871d 100644
--- a/net/tipc/bearer.h
+++ b/net/tipc/bearer.h
@@ -107,10 +107,8 @@ struct tipc_media {
/**
* struct tipc_bearer - Generic TIPC bearer structure
- * @dev: ptr to associated network device
- * @usr_handle: pointer to additional media-specific information about bearer
+ * @media_ptr: pointer to additional media-specific information about bearer
* @mtu: max packet size bearer can support
- * @lock: spinlock for controlling access to bearer
* @addr: media-specific address associated with bearer
* @name: bearer name (format = media:interface)
* @media: ptr to media structure associated with bearer
@@ -118,10 +116,9 @@ struct tipc_media {
* @priority: default link priority for bearer
* @window: default window size for bearer
* @tolerance: default link tolerance for bearer
+ * @domain: network domain to which links can be established
* @identity: array index of this bearer within TIPC bearer array
* @link_req: ptr to (optional) structure making periodic link setup requests
- * @links: list of non-congested links associated with bearer
- * @active: non-zero if bearer structure is represents a bearer
* @net_plane: network plane ('A' through 'H') currently associated with bearer
* @nodes: indicates which nodes in cluster can be reached through bearer
*
@@ -134,16 +131,14 @@ struct tipc_bearer {
u32 mtu; /* initalized by media */
struct tipc_media_addr addr; /* initalized by media */
char name[TIPC_MAX_BEARER_NAME];
- spinlock_t lock;
struct tipc_media *media;
struct tipc_media_addr bcast_addr;
u32 priority;
u32 window;
u32 tolerance;
+ u32 domain;
u32 identity;
struct tipc_link_req *link_req;
- struct list_head links;
- int active;
char net_plane;
struct tipc_node_map nodes;
};
@@ -155,7 +150,7 @@ struct tipc_bearer_names {
struct tipc_link;
-extern struct tipc_bearer tipc_bearers[];
+extern struct tipc_bearer *bearer_list[];
/*
* TIPC routines available to supported media types
diff --git a/net/tipc/config.c b/net/tipc/config.c
index e6d721692ae0..4b981c053823 100644
--- a/net/tipc/config.c
+++ b/net/tipc/config.c
@@ -43,13 +43,11 @@
#define REPLY_TRUNCATED "<truncated>\n"
static DEFINE_MUTEX(config_mutex);
-static struct tipc_server cfgsrv;
static const void *req_tlv_area; /* request message TLV area */
static int req_tlv_space; /* request message TLV area size */
static int rep_headroom; /* reply message headroom to use */
-
struct sk_buff *tipc_cfg_reply_alloc(int payload_size)
{
struct sk_buff *buf;
@@ -185,18 +183,6 @@ static struct sk_buff *cfg_set_own_addr(void)
return tipc_cfg_reply_none();
}
-static struct sk_buff *cfg_set_remote_mng(void)
-{
- u32 value;
-
- if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
- return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
-
- value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
- tipc_remote_management = (value != 0);
- return tipc_cfg_reply_none();
-}
-
static struct sk_buff *cfg_set_max_ports(void)
{
u32 value;
@@ -247,21 +233,10 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area
/* Check command authorization */
if (likely(in_own_node(orig_node))) {
/* command is permitted */
- } else if (cmd >= 0x8000) {
+ } else {
rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
" (cannot be done remotely)");
goto exit;
- } else if (!tipc_remote_management) {
- rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NO_REMOTE);
- goto exit;
- } else if (cmd >= 0x4000) {
- u32 domain = 0;
-
- if ((tipc_nametbl_translate(TIPC_ZM_SRV, 0, &domain) == 0) ||
- (domain != orig_node)) {
- rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_ZONE_MSTR);
- goto exit;
- }
}
/* Call appropriate processing routine */
@@ -310,18 +285,12 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area
case TIPC_CMD_SET_NODE_ADDR:
rep_tlv_buf = cfg_set_own_addr();
break;
- case TIPC_CMD_SET_REMOTE_MNG:
- rep_tlv_buf = cfg_set_remote_mng();
- break;
case TIPC_CMD_SET_MAX_PORTS:
rep_tlv_buf = cfg_set_max_ports();
break;
case TIPC_CMD_SET_NETID:
rep_tlv_buf = cfg_set_netid();
break;
- case TIPC_CMD_GET_REMOTE_MNG:
- rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_remote_management);
- break;
case TIPC_CMD_GET_MAX_PORTS:
rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_ports);
break;
@@ -345,6 +314,8 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area
case TIPC_CMD_SET_MAX_PUBL:
case TIPC_CMD_GET_MAX_PUBL:
case TIPC_CMD_SET_LOG_SIZE:
+ case TIPC_CMD_SET_REMOTE_MNG:
+ case TIPC_CMD_GET_REMOTE_MNG:
case TIPC_CMD_DUMP_LOG:
rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
" (obsolete command)");
@@ -369,75 +340,3 @@ exit:
mutex_unlock(&config_mutex);
return rep_tlv_buf;
}
-
-static void cfg_conn_msg_event(int conid, struct sockaddr_tipc *addr,
- void *usr_data, void *buf, size_t len)
-{
- struct tipc_cfg_msg_hdr *req_hdr;
- struct tipc_cfg_msg_hdr *rep_hdr;
- struct sk_buff *rep_buf;
-
- /* Validate configuration message header (ignore invalid message) */
- req_hdr = (struct tipc_cfg_msg_hdr *)buf;
- if ((len < sizeof(*req_hdr)) ||
- (len != TCM_ALIGN(ntohl(req_hdr->tcm_len))) ||
- (ntohs(req_hdr->tcm_flags) != TCM_F_REQUEST)) {
- pr_warn("Invalid configuration message discarded\n");
- return;
- }
-
- /* Generate reply for request (if can't, return request) */
- rep_buf = tipc_cfg_do_cmd(addr->addr.id.node, ntohs(req_hdr->tcm_type),
- buf + sizeof(*req_hdr),
- len - sizeof(*req_hdr),
- BUF_HEADROOM + MAX_H_SIZE + sizeof(*rep_hdr));
- if (rep_buf) {
- skb_push(rep_buf, sizeof(*rep_hdr));
- rep_hdr = (struct tipc_cfg_msg_hdr *)rep_buf->data;
- memcpy(rep_hdr, req_hdr, sizeof(*rep_hdr));
- rep_hdr->tcm_len = htonl(rep_buf->len);
- rep_hdr->tcm_flags &= htons(~TCM_F_REQUEST);
- tipc_conn_sendmsg(&cfgsrv, conid, addr, rep_buf->data,
- rep_buf->len);
- kfree_skb(rep_buf);
- }
-}
-
-static struct sockaddr_tipc cfgsrv_addr __read_mostly = {
- .family = AF_TIPC,
- .addrtype = TIPC_ADDR_NAMESEQ,
- .addr.nameseq.type = TIPC_CFG_SRV,
- .addr.nameseq.lower = 0,
- .addr.nameseq.upper = 0,
- .scope = TIPC_ZONE_SCOPE
-};
-
-static struct tipc_server cfgsrv __read_mostly = {
- .saddr = &cfgsrv_addr,
- .imp = TIPC_CRITICAL_IMPORTANCE,
- .type = SOCK_RDM,
- .max_rcvbuf_size = 64 * 1024,
- .name = "cfg_server",
- .tipc_conn_recvmsg = cfg_conn_msg_event,
- .tipc_conn_new = NULL,
- .tipc_conn_shutdown = NULL
-};
-
-int tipc_cfg_init(void)
-{
- return tipc_server_start(&cfgsrv);
-}
-
-void tipc_cfg_reinit(void)
-{
- tipc_server_stop(&cfgsrv);
-
- cfgsrv_addr.addr.nameseq.lower = tipc_own_addr;
- cfgsrv_addr.addr.nameseq.upper = tipc_own_addr;
- tipc_server_start(&cfgsrv);
-}
-
-void tipc_cfg_stop(void)
-{
- tipc_server_stop(&cfgsrv);
-}
diff --git a/net/tipc/config.h b/net/tipc/config.h
index 1f252f3fa058..47b1bf181612 100644
--- a/net/tipc/config.h
+++ b/net/tipc/config.h
@@ -64,9 +64,4 @@ static inline struct sk_buff *tipc_cfg_reply_ultra_string(char *string)
struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd,
const void *req_tlv_area, int req_tlv_space,
int headroom);
-
-int tipc_cfg_init(void);
-void tipc_cfg_reinit(void);
-void tipc_cfg_stop(void);
-
#endif
diff --git a/net/tipc/core.c b/net/tipc/core.c
index 80c20647b3d2..50d57429ebca 100644
--- a/net/tipc/core.c
+++ b/net/tipc/core.c
@@ -1,7 +1,7 @@
/*
* net/tipc/core.c: TIPC module code
*
- * Copyright (c) 2003-2006, Ericsson AB
+ * Copyright (c) 2003-2006, 2013, Ericsson AB
* Copyright (c) 2005-2006, 2010-2013, Wind River Systems
* All rights reserved.
*
@@ -50,7 +50,6 @@ int tipc_random __read_mostly;
u32 tipc_own_addr __read_mostly;
int tipc_max_ports __read_mostly;
int tipc_net_id __read_mostly;
-int tipc_remote_management __read_mostly;
int sysctl_tipc_rmem[3] __read_mostly; /* min/default/max */
/**
@@ -85,7 +84,6 @@ static void tipc_core_stop(void)
tipc_net_stop();
tipc_bearer_cleanup();
tipc_netlink_stop();
- tipc_cfg_stop();
tipc_subscr_stop();
tipc_nametbl_stop();
tipc_ref_table_stop();
@@ -130,18 +128,12 @@ static int tipc_core_start(void)
if (err)
goto out_subscr;
- err = tipc_cfg_init();
- if (err)
- goto out_cfg;
-
err = tipc_bearer_setup();
if (err)
goto out_bearer;
return 0;
out_bearer:
- tipc_cfg_stop();
-out_cfg:
tipc_subscr_stop();
out_subscr:
tipc_unregister_sysctl();
@@ -166,7 +158,6 @@ static int __init tipc_init(void)
pr_info("Activated (version " TIPC_MOD_VER ")\n");
tipc_own_addr = 0;
- tipc_remote_management = 1;
tipc_max_ports = CONFIG_TIPC_PORTS;
tipc_net_id = 4711;
diff --git a/net/tipc/core.h b/net/tipc/core.h
index 4dfe137587bb..8985bbcb942b 100644
--- a/net/tipc/core.h
+++ b/net/tipc/core.h
@@ -79,7 +79,6 @@ int tipc_snprintf(char *buf, int len, const char *fmt, ...);
extern u32 tipc_own_addr __read_mostly;
extern int tipc_max_ports __read_mostly;
extern int tipc_net_id __read_mostly;
-extern int tipc_remote_management __read_mostly;
extern int sysctl_tipc_rmem[3] __read_mostly;
/*
diff --git a/net/tipc/discover.c b/net/tipc/discover.c
index 412ff41b8611..542fe3413dc4 100644
--- a/net/tipc/discover.c
+++ b/net/tipc/discover.c
@@ -48,7 +48,6 @@
* struct tipc_link_req - information about an ongoing link setup request
* @bearer: bearer issuing requests
* @dest: destination address for request messages
- * @domain: network domain to which links can be established
* @num_nodes: number of nodes currently discovered (i.e. with an active link)
* @lock: spinlock for controlling access to requests
* @buf: request message to be (repeatedly) sent
@@ -58,7 +57,6 @@
struct tipc_link_req {
struct tipc_bearer *bearer;
struct tipc_media_addr dest;
- u32 domain;
int num_nodes;
spinlock_t lock;
struct sk_buff *buf;
@@ -69,14 +67,13 @@ struct tipc_link_req {
/**
* tipc_disc_init_msg - initialize a link setup message
* @type: message type (request or response)
- * @dest_domain: network domain of node(s) which should respond to message
* @b_ptr: ptr to bearer issuing message
*/
-static struct sk_buff *tipc_disc_init_msg(u32 type, u32 dest_domain,
- struct tipc_bearer *b_ptr)
+static struct sk_buff *tipc_disc_init_msg(u32 type, struct tipc_bearer *b_ptr)
{
struct sk_buff *buf = tipc_buf_acquire(INT_H_SIZE);
struct tipc_msg *msg;
+ u32 dest_domain = b_ptr->domain;
if (buf) {
msg = buf_msg(buf);
@@ -110,11 +107,11 @@ static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr,
}
/**
- * tipc_disc_recv_msg - handle incoming link setup message (request or response)
+ * tipc_disc_rcv - handle incoming link setup message (request or response)
* @buf: buffer containing message
* @b_ptr: bearer that message arrived on
*/
-void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr)
+void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *b_ptr)
{
struct tipc_node *n_ptr;
struct tipc_link *link;
@@ -149,7 +146,7 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr)
}
if (!tipc_in_scope(dest, tipc_own_addr))
return;
- if (!tipc_in_scope(b_ptr->link_req->domain, orig))
+ if (!tipc_in_scope(b_ptr->domain, orig))
return;
/* Locate structure corresponding to requesting node */
@@ -242,7 +239,7 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr)
link_fully_up = link_working_working(link);
if ((type == DSC_REQ_MSG) && !link_fully_up) {
- rbuf = tipc_disc_init_msg(DSC_RESP_MSG, orig, b_ptr);
+ rbuf = tipc_disc_init_msg(DSC_RESP_MSG, b_ptr);
if (rbuf) {
tipc_bearer_send(b_ptr, rbuf, &media_addr);
kfree_skb(rbuf);
@@ -306,7 +303,7 @@ static void disc_timeout(struct tipc_link_req *req)
spin_lock_bh(&req->lock);
/* Stop searching if only desired node has been found */
- if (tipc_node(req->domain) && req->num_nodes) {
+ if (tipc_node(req->bearer->domain) && req->num_nodes) {
req->timer_intv = TIPC_LINK_REQ_INACTIVE;
goto exit;
}
@@ -342,8 +339,7 @@ exit:
*
* Returns 0 if successful, otherwise -errno.
*/
-int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest,
- u32 dest_domain)
+int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest)
{
struct tipc_link_req *req;
@@ -351,7 +347,7 @@ int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest,
if (!req)
return -ENOMEM;
- req->buf = tipc_disc_init_msg(DSC_REQ_MSG, dest_domain, b_ptr);
+ req->buf = tipc_disc_init_msg(DSC_REQ_MSG, b_ptr);
if (!req->buf) {
kfree(req);
return -ENOMSG;
@@ -359,7 +355,6 @@ int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest,
memcpy(&req->dest, dest, sizeof(*dest));
req->bearer = b_ptr;
- req->domain = dest_domain;
req->num_nodes = 0;
req->timer_intv = TIPC_LINK_REQ_INIT;
spin_lock_init(&req->lock);
diff --git a/net/tipc/discover.h b/net/tipc/discover.h
index 75b67c403aa3..07f34729459d 100644
--- a/net/tipc/discover.h
+++ b/net/tipc/discover.h
@@ -39,11 +39,10 @@
struct tipc_link_req;
-int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest,
- u32 dest_domain);
+int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest);
void tipc_disc_delete(struct tipc_link_req *req);
void tipc_disc_add_dest(struct tipc_link_req *req);
void tipc_disc_remove_dest(struct tipc_link_req *req);
-void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr);
+void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *b_ptr);
#endif
diff --git a/net/tipc/link.c b/net/tipc/link.c
index da6018beb6eb..c5190ab75290 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -77,19 +77,19 @@ static const char *link_unk_evt = "Unknown link event ";
static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr,
struct sk_buff *buf);
-static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf);
-static int tipc_link_tunnel_rcv(struct tipc_link **l_ptr,
+static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf);
+static int tipc_link_tunnel_rcv(struct tipc_node *n_ptr,
struct sk_buff **buf);
static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance);
-static int link_send_sections_long(struct tipc_port *sender,
- struct iovec const *msg_sect,
- unsigned int len, u32 destnode);
+static int tipc_link_iovec_long_xmit(struct tipc_port *sender,
+ struct iovec const *msg_sect,
+ unsigned int len, u32 destnode);
static void link_state_event(struct tipc_link *l_ptr, u32 event);
static void link_reset_statistics(struct tipc_link *l_ptr);
static void link_print(struct tipc_link *l_ptr, const char *str);
-static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf);
-static void tipc_link_send_sync(struct tipc_link *l);
-static void tipc_link_recv_sync(struct tipc_node *n, struct sk_buff *buf);
+static int tipc_link_frag_xmit(struct tipc_link *l_ptr, struct sk_buff *buf);
+static void tipc_link_sync_xmit(struct tipc_link *l);
+static void tipc_link_sync_rcv(struct tipc_node *n, struct sk_buff *buf);
/*
* Simple link routines
@@ -147,11 +147,6 @@ int tipc_link_is_active(struct tipc_link *l_ptr)
/**
* link_timeout - handle expiration of link timer
* @l_ptr: pointer to link
- *
- * This routine must not grab "tipc_net_lock" to avoid a potential deadlock conflict
- * with tipc_link_delete(). (There is no risk that the node will be deleted by
- * another thread because tipc_link_delete() always cancels the link timer before
- * tipc_node_delete() is called.)
*/
static void link_timeout(struct tipc_link *l_ptr)
{
@@ -213,8 +208,8 @@ static void link_set_timer(struct tipc_link *l_ptr, u32 time)
* Returns pointer to link.
*/
struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
- struct tipc_bearer *b_ptr,
- const struct tipc_media_addr *media_addr)
+ struct tipc_bearer *b_ptr,
+ const struct tipc_media_addr *media_addr)
{
struct tipc_link *l_ptr;
struct tipc_msg *msg;
@@ -279,41 +274,44 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
k_init_timer(&l_ptr->timer, (Handler)link_timeout,
(unsigned long)l_ptr);
- list_add_tail(&l_ptr->link_list, &b_ptr->links);
link_state_event(l_ptr, STARTING_EVT);
return l_ptr;
}
-/**
- * tipc_link_delete - delete a link
- * @l_ptr: pointer to link
- *
- * Note: 'tipc_net_lock' is write_locked, bearer is locked.
- * This routine must not grab the node lock until after link timer cancellation
- * to avoid a potential deadlock situation.
- */
-void tipc_link_delete(struct tipc_link *l_ptr)
+void tipc_link_delete_list(unsigned int bearer_id, bool shutting_down)
{
- if (!l_ptr) {
- pr_err("Attempt to delete non-existent link\n");
- return;
- }
-
- k_cancel_timer(&l_ptr->timer);
+ struct tipc_link *l_ptr;
+ struct tipc_node *n_ptr;
- tipc_node_lock(l_ptr->owner);
- tipc_link_reset(l_ptr);
- tipc_node_detach_link(l_ptr->owner, l_ptr);
- tipc_link_purge_queues(l_ptr);
- list_del_init(&l_ptr->link_list);
- tipc_node_unlock(l_ptr->owner);
- k_term_timer(&l_ptr->timer);
- kfree(l_ptr);
+ rcu_read_lock();
+ list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
+ spin_lock_bh(&n_ptr->lock);
+ l_ptr = n_ptr->links[bearer_id];
+ if (l_ptr) {
+ tipc_link_reset(l_ptr);
+ if (shutting_down || !tipc_node_is_up(n_ptr)) {
+ tipc_node_detach_link(l_ptr->owner, l_ptr);
+ tipc_link_reset_fragments(l_ptr);
+ spin_unlock_bh(&n_ptr->lock);
+
+ /* Nobody else can access this link now: */
+ del_timer_sync(&l_ptr->timer);
+ kfree(l_ptr);
+ } else {
+ /* Detach/delete when failover is finished: */
+ l_ptr->flags |= LINK_STOPPED;
+ spin_unlock_bh(&n_ptr->lock);
+ del_timer_sync(&l_ptr->timer);
+ }
+ continue;
+ }
+ spin_unlock_bh(&n_ptr->lock);
+ }
+ rcu_read_unlock();
}
-
/**
* link_schedule_port - schedule port for deferred sending
* @l_ptr: pointer to link
@@ -330,8 +328,6 @@ static int link_schedule_port(struct tipc_link *l_ptr, u32 origport, u32 sz)
spin_lock_bh(&tipc_port_list_lock);
p_ptr = tipc_port_lock(origport);
if (p_ptr) {
- if (!p_ptr->wakeup)
- goto exit;
if (!list_empty(&p_ptr->wait_list))
goto exit;
p_ptr->congested = 1;
@@ -366,7 +362,7 @@ void tipc_link_wakeup_ports(struct tipc_link *l_ptr, int all)
list_del_init(&p_ptr->wait_list);
spin_lock_bh(p_ptr->lock);
p_ptr->congested = 0;
- p_ptr->wakeup(p_ptr);
+ tipc_port_wakeup(p_ptr);
win -= p_ptr->waiting_pkts;
spin_unlock_bh(p_ptr->lock);
}
@@ -461,6 +457,21 @@ void tipc_link_reset(struct tipc_link *l_ptr)
link_reset_statistics(l_ptr);
}
+void tipc_link_reset_list(unsigned int bearer_id)
+{
+ struct tipc_link *l_ptr;
+ struct tipc_node *n_ptr;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
+ spin_lock_bh(&n_ptr->lock);
+ l_ptr = n_ptr->links[bearer_id];
+ if (l_ptr)
+ tipc_link_reset(l_ptr);
+ spin_unlock_bh(&n_ptr->lock);
+ }
+ rcu_read_unlock();
+}
static void link_activate(struct tipc_link *l_ptr)
{
@@ -479,7 +490,10 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
struct tipc_link *other;
u32 cont_intv = l_ptr->continuity_interval;
- if (!l_ptr->started && (event != STARTING_EVT))
+ if (l_ptr->flags & LINK_STOPPED)
+ return;
+
+ if (!(l_ptr->flags & LINK_STARTED) && (event != STARTING_EVT))
return; /* Not yet. */
/* Check whether changeover is going on */
@@ -499,12 +513,12 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
if (l_ptr->next_in_no != l_ptr->checkpoint) {
l_ptr->checkpoint = l_ptr->next_in_no;
if (tipc_bclink_acks_missing(l_ptr->owner)) {
- tipc_link_send_proto_msg(l_ptr, STATE_MSG,
- 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG,
+ 0, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
} else if (l_ptr->max_pkt < l_ptr->max_pkt_target) {
- tipc_link_send_proto_msg(l_ptr, STATE_MSG,
- 1, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG,
+ 1, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
}
link_set_timer(l_ptr, cont_intv);
@@ -512,7 +526,7 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
}
l_ptr->state = WORKING_UNKNOWN;
l_ptr->fsm_msg_cnt = 0;
- tipc_link_send_proto_msg(l_ptr, STATE_MSG, 1, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv / 4);
break;
@@ -522,7 +536,8 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
tipc_link_reset(l_ptr);
l_ptr->state = RESET_RESET;
l_ptr->fsm_msg_cnt = 0;
- tipc_link_send_proto_msg(l_ptr, ACTIVATE_MSG, 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
+ 0, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
break;
@@ -544,7 +559,8 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
tipc_link_reset(l_ptr);
l_ptr->state = RESET_RESET;
l_ptr->fsm_msg_cnt = 0;
- tipc_link_send_proto_msg(l_ptr, ACTIVATE_MSG, 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
+ 0, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
break;
@@ -554,14 +570,14 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
l_ptr->fsm_msg_cnt = 0;
l_ptr->checkpoint = l_ptr->next_in_no;
if (tipc_bclink_acks_missing(l_ptr->owner)) {
- tipc_link_send_proto_msg(l_ptr, STATE_MSG,
- 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG,
+ 0, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
}
link_set_timer(l_ptr, cont_intv);
} else if (l_ptr->fsm_msg_cnt < l_ptr->abort_limit) {
- tipc_link_send_proto_msg(l_ptr, STATE_MSG,
- 1, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG,
+ 1, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv / 4);
} else { /* Link has failed */
@@ -570,8 +586,8 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
tipc_link_reset(l_ptr);
l_ptr->state = RESET_UNKNOWN;
l_ptr->fsm_msg_cnt = 0;
- tipc_link_send_proto_msg(l_ptr, RESET_MSG,
- 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, RESET_MSG,
+ 0, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
}
@@ -591,24 +607,25 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
l_ptr->state = WORKING_WORKING;
l_ptr->fsm_msg_cnt = 0;
link_activate(l_ptr);
- tipc_link_send_proto_msg(l_ptr, STATE_MSG, 1, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
if (l_ptr->owner->working_links == 1)
- tipc_link_send_sync(l_ptr);
+ tipc_link_sync_xmit(l_ptr);
link_set_timer(l_ptr, cont_intv);
break;
case RESET_MSG:
l_ptr->state = RESET_RESET;
l_ptr->fsm_msg_cnt = 0;
- tipc_link_send_proto_msg(l_ptr, ACTIVATE_MSG, 1, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
+ 1, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
break;
case STARTING_EVT:
- l_ptr->started = 1;
+ l_ptr->flags |= LINK_STARTED;
/* fall through */
case TIMEOUT_EVT:
- tipc_link_send_proto_msg(l_ptr, RESET_MSG, 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, RESET_MSG, 0, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
break;
@@ -626,16 +643,17 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
l_ptr->state = WORKING_WORKING;
l_ptr->fsm_msg_cnt = 0;
link_activate(l_ptr);
- tipc_link_send_proto_msg(l_ptr, STATE_MSG, 1, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
if (l_ptr->owner->working_links == 1)
- tipc_link_send_sync(l_ptr);
+ tipc_link_sync_xmit(l_ptr);
link_set_timer(l_ptr, cont_intv);
break;
case RESET_MSG:
break;
case TIMEOUT_EVT:
- tipc_link_send_proto_msg(l_ptr, ACTIVATE_MSG, 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
+ 0, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
break;
@@ -721,11 +739,11 @@ static void link_add_chain_to_outqueue(struct tipc_link *l_ptr,
}
/*
- * tipc_link_send_buf() is the 'full path' for messages, called from
- * inside TIPC when the 'fast path' in tipc_send_buf
+ * tipc_link_xmit() is the 'full path' for messages, called from
+ * inside TIPC when the 'fast path' in tipc_send_xmit
* has failed, and from link_send()
*/
-int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
+int __tipc_link_xmit(struct tipc_link *l_ptr, struct sk_buff *buf)
{
struct tipc_msg *msg = buf_msg(buf);
u32 size = msg_size(msg);
@@ -753,7 +771,7 @@ int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
/* Fragmentation needed ? */
if (size > max_packet)
- return link_send_long_buf(l_ptr, buf);
+ return tipc_link_frag_xmit(l_ptr, buf);
/* Packet can be queued or sent. */
if (likely(!link_congested(l_ptr))) {
@@ -797,11 +815,11 @@ int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
}
/*
- * tipc_link_send(): same as tipc_link_send_buf(), but the link to use has
- * not been selected yet, and the the owner node is not locked
+ * tipc_link_xmit(): same as __tipc_link_xmit(), but the link to use
+ * has not been selected yet, and the the owner node is not locked
* Called by TIPC internal users, e.g. the name distributor
*/
-int tipc_link_send(struct sk_buff *buf, u32 dest, u32 selector)
+int tipc_link_xmit(struct sk_buff *buf, u32 dest, u32 selector)
{
struct tipc_link *l_ptr;
struct tipc_node *n_ptr;
@@ -813,7 +831,7 @@ int tipc_link_send(struct sk_buff *buf, u32 dest, u32 selector)
tipc_node_lock(n_ptr);
l_ptr = n_ptr->active_links[selector & 1];
if (l_ptr)
- res = tipc_link_send_buf(l_ptr, buf);
+ res = __tipc_link_xmit(l_ptr, buf);
else
kfree_skb(buf);
tipc_node_unlock(n_ptr);
@@ -825,14 +843,14 @@ int tipc_link_send(struct sk_buff *buf, u32 dest, u32 selector)
}
/*
- * tipc_link_send_sync - synchronize broadcast link endpoints.
+ * tipc_link_sync_xmit - synchronize broadcast link endpoints.
*
* Give a newly added peer node the sequence number where it should
* start receiving and acking broadcast packets.
*
* Called with node locked
*/
-static void tipc_link_send_sync(struct tipc_link *l)
+static void tipc_link_sync_xmit(struct tipc_link *l)
{
struct sk_buff *buf;
struct tipc_msg *msg;
@@ -849,14 +867,14 @@ static void tipc_link_send_sync(struct tipc_link *l)
}
/*
- * tipc_link_recv_sync - synchronize broadcast link endpoints.
+ * tipc_link_sync_rcv - synchronize broadcast link endpoints.
* Receive the sequence number where we should start receiving and
* acking broadcast packets from a newly added peer node, and open
* up for reception of such packets.
*
* Called with node locked
*/
-static void tipc_link_recv_sync(struct tipc_node *n, struct sk_buff *buf)
+static void tipc_link_sync_rcv(struct tipc_node *n, struct sk_buff *buf)
{
struct tipc_msg *msg = buf_msg(buf);
@@ -866,7 +884,7 @@ static void tipc_link_recv_sync(struct tipc_node *n, struct sk_buff *buf)
}
/*
- * tipc_link_send_names - send name table entries to new neighbor
+ * tipc_link_names_xmit - send name table entries to new neighbor
*
* Send routine for bulk delivery of name table messages when contact
* with a new neighbor occurs. No link congestion checking is performed
@@ -874,7 +892,7 @@ static void tipc_link_recv_sync(struct tipc_node *n, struct sk_buff *buf)
* small enough not to require fragmentation.
* Called without any locks held.
*/
-void tipc_link_send_names(struct list_head *message_list, u32 dest)
+void tipc_link_names_xmit(struct list_head *message_list, u32 dest)
{
struct tipc_node *n_ptr;
struct tipc_link *l_ptr;
@@ -909,13 +927,13 @@ void tipc_link_send_names(struct list_head *message_list, u32 dest)
}
/*
- * link_send_buf_fast: Entry for data messages where the
+ * tipc_link_xmit_fast: Entry for data messages where the
* destination link is known and the header is complete,
* inclusive total message length. Very time critical.
* Link is locked. Returns user data length.
*/
-static int link_send_buf_fast(struct tipc_link *l_ptr, struct sk_buff *buf,
- u32 *used_max_pkt)
+static int tipc_link_xmit_fast(struct tipc_link *l_ptr, struct sk_buff *buf,
+ u32 *used_max_pkt)
{
struct tipc_msg *msg = buf_msg(buf);
int res = msg_data_sz(msg);
@@ -931,18 +949,18 @@ static int link_send_buf_fast(struct tipc_link *l_ptr, struct sk_buff *buf,
else
*used_max_pkt = l_ptr->max_pkt;
}
- return tipc_link_send_buf(l_ptr, buf); /* All other cases */
+ return __tipc_link_xmit(l_ptr, buf); /* All other cases */
}
/*
- * tipc_link_send_sections_fast: Entry for messages where the
+ * tipc_link_iovec_xmit_fast: Entry for messages where the
* destination processor is known and the header is complete,
* except for total message length.
* Returns user data length or errno.
*/
-int tipc_link_send_sections_fast(struct tipc_port *sender,
- struct iovec const *msg_sect,
- unsigned int len, u32 destaddr)
+int tipc_link_iovec_xmit_fast(struct tipc_port *sender,
+ struct iovec const *msg_sect,
+ unsigned int len, u32 destaddr)
{
struct tipc_msg *hdr = &sender->phdr;
struct tipc_link *l_ptr;
@@ -968,8 +986,8 @@ again:
l_ptr = node->active_links[selector];
if (likely(l_ptr)) {
if (likely(buf)) {
- res = link_send_buf_fast(l_ptr, buf,
- &sender->max_pkt);
+ res = tipc_link_xmit_fast(l_ptr, buf,
+ &sender->max_pkt);
exit:
tipc_node_unlock(node);
read_unlock_bh(&tipc_net_lock);
@@ -995,24 +1013,21 @@ exit:
if ((msg_hdr_sz(hdr) + res) <= sender->max_pkt)
goto again;
- return link_send_sections_long(sender, msg_sect, len,
- destaddr);
+ return tipc_link_iovec_long_xmit(sender, msg_sect,
+ len, destaddr);
}
tipc_node_unlock(node);
}
read_unlock_bh(&tipc_net_lock);
/* Couldn't find a link to the destination node */
- if (buf)
- return tipc_reject_msg(buf, TIPC_ERR_NO_NODE);
- if (res >= 0)
- return tipc_port_reject_sections(sender, hdr, msg_sect,
- len, TIPC_ERR_NO_NODE);
- return res;
+ kfree_skb(buf);
+ tipc_port_iovec_reject(sender, hdr, msg_sect, len, TIPC_ERR_NO_NODE);
+ return -ENETUNREACH;
}
/*
- * link_send_sections_long(): Entry for long messages where the
+ * tipc_link_iovec_long_xmit(): Entry for long messages where the
* destination node is known and the header is complete,
* inclusive total message length.
* Link and bearer congestion status have been checked to be ok,
@@ -1025,9 +1040,9 @@ exit:
*
* Returns user data length or errno.
*/
-static int link_send_sections_long(struct tipc_port *sender,
- struct iovec const *msg_sect,
- unsigned int len, u32 destaddr)
+static int tipc_link_iovec_long_xmit(struct tipc_port *sender,
+ struct iovec const *msg_sect,
+ unsigned int len, u32 destaddr)
{
struct tipc_link *l_ptr;
struct tipc_node *node;
@@ -1146,8 +1161,9 @@ error:
} else {
reject:
kfree_skb_list(buf_chain);
- return tipc_port_reject_sections(sender, hdr, msg_sect,
- len, TIPC_ERR_NO_NODE);
+ tipc_port_iovec_reject(sender, hdr, msg_sect, len,
+ TIPC_ERR_NO_NODE);
+ return -ENETUNREACH;
}
/* Append chain of fragments to send queue & send them */
@@ -1441,15 +1457,10 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
u32 seq_no;
u32 ackd;
u32 released = 0;
- int type;
head = head->next;
buf->next = NULL;
- /* Ensure bearer is still enabled */
- if (unlikely(!b_ptr->active))
- goto discard;
-
/* Ensure message is well-formed */
if (unlikely(!link_recv_buf_validate(buf)))
goto discard;
@@ -1463,9 +1474,9 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
if (unlikely(msg_non_seq(msg))) {
if (msg_user(msg) == LINK_CONFIG)
- tipc_disc_recv_msg(buf, b_ptr);
+ tipc_disc_rcv(buf, b_ptr);
else
- tipc_bclink_recv_pkt(buf);
+ tipc_bclink_rcv(buf);
continue;
}
@@ -1489,7 +1500,7 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
if ((n_ptr->block_setup & WAIT_PEER_DOWN) &&
msg_user(msg) == LINK_PROTOCOL &&
(msg_type(msg) == RESET_MSG ||
- msg_type(msg) == ACTIVATE_MSG) &&
+ msg_type(msg) == ACTIVATE_MSG) &&
!msg_redundant_link(msg))
n_ptr->block_setup &= ~WAIT_PEER_DOWN;
@@ -1508,7 +1519,6 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
while ((crs != l_ptr->next_out) &&
less_eq(buf_seqno(crs), ackd)) {
struct sk_buff *next = crs->next;
-
kfree_skb(crs);
crs = next;
released++;
@@ -1521,18 +1531,19 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
/* Try sending any messages link endpoint has pending */
if (unlikely(l_ptr->next_out))
tipc_link_push_queue(l_ptr);
+
if (unlikely(!list_empty(&l_ptr->waiting_ports)))
tipc_link_wakeup_ports(l_ptr, 0);
+
if (unlikely(++l_ptr->unacked_window >= TIPC_MIN_LINK_WIN)) {
l_ptr->stats.sent_acks++;
- tipc_link_send_proto_msg(l_ptr, STATE_MSG, 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0, 0);
}
- /* Now (finally!) process the incoming message */
-protocol_check:
+ /* Process the incoming packet */
if (unlikely(!link_working_working(l_ptr))) {
if (msg_user(msg) == LINK_PROTOCOL) {
- link_recv_proto_msg(l_ptr, buf);
+ tipc_link_proto_rcv(l_ptr, buf);
head = link_insert_deferred_queue(l_ptr, head);
tipc_node_unlock(n_ptr);
continue;
@@ -1561,67 +1572,65 @@ protocol_check:
l_ptr->next_in_no++;
if (unlikely(l_ptr->oldest_deferred_in))
head = link_insert_deferred_queue(l_ptr, head);
-deliver:
- if (likely(msg_isdata(msg))) {
- tipc_node_unlock(n_ptr);
- tipc_port_recv_msg(buf);
- continue;
+
+ /* Deliver packet/message to correct user: */
+ if (unlikely(msg_user(msg) == CHANGEOVER_PROTOCOL)) {
+ if (!tipc_link_tunnel_rcv(n_ptr, &buf)) {
+ tipc_node_unlock(n_ptr);
+ continue;
+ }
+ msg = buf_msg(buf);
+ } else if (msg_user(msg) == MSG_FRAGMENTER) {
+ int rc;
+
+ l_ptr->stats.recv_fragments++;
+ rc = tipc_link_frag_rcv(&l_ptr->reasm_head,
+ &l_ptr->reasm_tail,
+ &buf);
+ if (rc == LINK_REASM_COMPLETE) {
+ l_ptr->stats.recv_fragmented++;
+ msg = buf_msg(buf);
+ } else {
+ if (rc == LINK_REASM_ERROR)
+ tipc_link_reset(l_ptr);
+ tipc_node_unlock(n_ptr);
+ continue;
+ }
}
+
switch (msg_user(msg)) {
- int ret;
+ case TIPC_LOW_IMPORTANCE:
+ case TIPC_MEDIUM_IMPORTANCE:
+ case TIPC_HIGH_IMPORTANCE:
+ case TIPC_CRITICAL_IMPORTANCE:
+ tipc_node_unlock(n_ptr);
+ tipc_port_rcv(buf);
+ continue;
case MSG_BUNDLER:
l_ptr->stats.recv_bundles++;
l_ptr->stats.recv_bundled += msg_msgcnt(msg);
tipc_node_unlock(n_ptr);
- tipc_link_recv_bundle(buf);
+ tipc_link_bundle_rcv(buf);
continue;
case NAME_DISTRIBUTOR:
n_ptr->bclink.recv_permitted = true;
tipc_node_unlock(n_ptr);
- tipc_named_recv(buf);
- continue;
- case BCAST_PROTOCOL:
- tipc_link_recv_sync(n_ptr, buf);
- tipc_node_unlock(n_ptr);
+ tipc_named_rcv(buf);
continue;
case CONN_MANAGER:
tipc_node_unlock(n_ptr);
- tipc_port_recv_proto_msg(buf);
+ tipc_port_proto_rcv(buf);
continue;
- case MSG_FRAGMENTER:
- l_ptr->stats.recv_fragments++;
- ret = tipc_link_recv_fragment(&l_ptr->reasm_head,
- &l_ptr->reasm_tail,
- &buf);
- if (ret == LINK_REASM_COMPLETE) {
- l_ptr->stats.recv_fragmented++;
- msg = buf_msg(buf);
- goto deliver;
- }
- if (ret == LINK_REASM_ERROR)
- tipc_link_reset(l_ptr);
- tipc_node_unlock(n_ptr);
- continue;
- case CHANGEOVER_PROTOCOL:
- type = msg_type(msg);
- if (tipc_link_tunnel_rcv(&l_ptr, &buf)) {
- msg = buf_msg(buf);
- seq_no = msg_seqno(msg);
- if (type == ORIGINAL_MSG)
- goto deliver;
- goto protocol_check;
- }
+ case BCAST_PROTOCOL:
+ tipc_link_sync_rcv(n_ptr, buf);
break;
default:
kfree_skb(buf);
- buf = NULL;
break;
}
tipc_node_unlock(n_ptr);
- tipc_net_route_msg(buf);
continue;
unlock_discard:
-
tipc_node_unlock(n_ptr);
discard:
kfree_skb(buf);
@@ -1688,7 +1697,7 @@ static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr,
u32 seq_no = buf_seqno(buf);
if (likely(msg_user(buf_msg(buf)) == LINK_PROTOCOL)) {
- link_recv_proto_msg(l_ptr, buf);
+ tipc_link_proto_rcv(l_ptr, buf);
return;
}
@@ -1711,7 +1720,7 @@ static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr,
l_ptr->stats.deferred_recv++;
TIPC_SKB_CB(buf)->deferred = true;
if ((l_ptr->deferred_inqueue_sz % 16) == 1)
- tipc_link_send_proto_msg(l_ptr, STATE_MSG, 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0, 0);
} else
l_ptr->stats.duplicates++;
}
@@ -1719,9 +1728,8 @@ static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr,
/*
* Send protocol message to the other endpoint.
*/
-void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ,
- int probe_msg, u32 gap, u32 tolerance,
- u32 priority, u32 ack_mtu)
+void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
+ u32 gap, u32 tolerance, u32 priority, u32 ack_mtu)
{
struct sk_buff *buf = NULL;
struct tipc_msg *msg = l_ptr->pmsg;
@@ -1820,7 +1828,7 @@ void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ,
* Note that network plane id propagates through the network, and may
* change at any time. The node with lowest address rules
*/
-static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf)
+static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf)
{
u32 rec_gap = 0;
u32 max_pkt_info;
@@ -1939,8 +1947,8 @@ static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf)
msg_last_bcast(msg));
if (rec_gap || (msg_probe(msg))) {
- tipc_link_send_proto_msg(l_ptr, STATE_MSG,
- 0, rec_gap, 0, 0, max_pkt_ack);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, rec_gap, 0,
+ 0, max_pkt_ack);
}
if (msg_seq_gap(msg)) {
l_ptr->stats.recv_nacks++;
@@ -1979,7 +1987,7 @@ static void tipc_link_tunnel_xmit(struct tipc_link *l_ptr,
}
skb_copy_to_linear_data(buf, tunnel_hdr, INT_H_SIZE);
skb_copy_to_linear_data_offset(buf, INT_H_SIZE, msg, length);
- tipc_link_send_buf(tunnel, buf);
+ __tipc_link_xmit(tunnel, buf);
}
@@ -2012,7 +2020,7 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
if (buf) {
skb_copy_to_linear_data(buf, &tunnel_hdr, INT_H_SIZE);
msg_set_size(&tunnel_hdr, INT_H_SIZE);
- tipc_link_send_buf(tunnel, buf);
+ __tipc_link_xmit(tunnel, buf);
} else {
pr_warn("%sunable to send changeover msg\n",
link_co_err);
@@ -2046,7 +2054,7 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
}
}
-/* tipc_link_dup_send_queue(): A second link has become active. Tunnel a
+/* tipc_link_dup_queue_xmit(): A second link has become active. Tunnel a
* duplicate of the first link's send queue via the new link. This way, we
* are guaranteed that currently queued packets from a socket are delivered
* before future traffic from the same socket, even if this is using the
@@ -2055,7 +2063,7 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
* and sequence order is preserved per sender/receiver socket pair.
* Owner node is locked.
*/
-void tipc_link_dup_send_queue(struct tipc_link *l_ptr,
+void tipc_link_dup_queue_xmit(struct tipc_link *l_ptr,
struct tipc_link *tunnel)
{
struct sk_buff *iter;
@@ -2085,7 +2093,7 @@ void tipc_link_dup_send_queue(struct tipc_link *l_ptr,
skb_copy_to_linear_data(outbuf, &tunnel_hdr, INT_H_SIZE);
skb_copy_to_linear_data_offset(outbuf, INT_H_SIZE, iter->data,
length);
- tipc_link_send_buf(tunnel, outbuf);
+ __tipc_link_xmit(tunnel, outbuf);
if (!tipc_link_is_up(l_ptr))
return;
iter = iter->next;
@@ -2112,89 +2120,114 @@ static struct sk_buff *buf_extract(struct sk_buff *skb, u32 from_pos)
return eb;
}
-/* tipc_link_tunnel_rcv(): Receive a tunneled packet, sent
- * via other link as result of a failover (ORIGINAL_MSG) or
- * a new active link (DUPLICATE_MSG). Failover packets are
- * returned to the active link for delivery upwards.
+
+
+/* tipc_link_dup_rcv(): Receive a tunnelled DUPLICATE_MSG packet.
+ * Owner node is locked.
+ */
+static void tipc_link_dup_rcv(struct tipc_link *l_ptr,
+ struct sk_buff *t_buf)
+{
+ struct sk_buff *buf;
+
+ if (!tipc_link_is_up(l_ptr))
+ return;
+
+ buf = buf_extract(t_buf, INT_H_SIZE);
+ if (buf == NULL) {
+ pr_warn("%sfailed to extract inner dup pkt\n", link_co_err);
+ return;
+ }
+
+ /* Add buffer to deferred queue, if applicable: */
+ link_handle_out_of_seq_msg(l_ptr, buf);
+}
+
+/* tipc_link_failover_rcv(): Receive a tunnelled ORIGINAL_MSG packet
* Owner node is locked.
*/
-static int tipc_link_tunnel_rcv(struct tipc_link **l_ptr,
- struct sk_buff **buf)
+static struct sk_buff *tipc_link_failover_rcv(struct tipc_link *l_ptr,
+ struct sk_buff *t_buf)
{
- struct sk_buff *tunnel_buf = *buf;
- struct tipc_link *dest_link;
+ struct tipc_msg *t_msg = buf_msg(t_buf);
+ struct sk_buff *buf = NULL;
struct tipc_msg *msg;
- struct tipc_msg *tunnel_msg = buf_msg(tunnel_buf);
- u32 msg_typ = msg_type(tunnel_msg);
- u32 msg_count = msg_msgcnt(tunnel_msg);
- u32 bearer_id = msg_bearer_id(tunnel_msg);
- if (bearer_id >= MAX_BEARERS)
- goto exit;
- dest_link = (*l_ptr)->owner->links[bearer_id];
- if (!dest_link)
- goto exit;
- if (dest_link == *l_ptr) {
- pr_err("Unexpected changeover message on link <%s>\n",
- (*l_ptr)->name);
- goto exit;
- }
- *l_ptr = dest_link;
- msg = msg_get_wrapped(tunnel_msg);
+ if (tipc_link_is_up(l_ptr))
+ tipc_link_reset(l_ptr);
- if (msg_typ == DUPLICATE_MSG) {
- if (less(msg_seqno(msg), mod(dest_link->next_in_no)))
- goto exit;
- *buf = buf_extract(tunnel_buf, INT_H_SIZE);
- if (*buf == NULL) {
- pr_warn("%sduplicate msg dropped\n", link_co_err);
+ /* First failover packet? */
+ if (l_ptr->exp_msg_count == START_CHANGEOVER)
+ l_ptr->exp_msg_count = msg_msgcnt(t_msg);
+
+ /* Should there be an inner packet? */
+ if (l_ptr->exp_msg_count) {
+ l_ptr->exp_msg_count--;
+ buf = buf_extract(t_buf, INT_H_SIZE);
+ if (buf == NULL) {
+ pr_warn("%sno inner failover pkt\n", link_co_err);
goto exit;
}
- kfree_skb(tunnel_buf);
- return 1;
- }
+ msg = buf_msg(buf);
- /* First original message ?: */
- if (tipc_link_is_up(dest_link)) {
- pr_info("%s<%s>, changeover initiated by peer\n", link_rst_msg,
- dest_link->name);
- tipc_link_reset(dest_link);
- dest_link->exp_msg_count = msg_count;
- if (!msg_count)
- goto exit;
- } else if (dest_link->exp_msg_count == START_CHANGEOVER) {
- dest_link->exp_msg_count = msg_count;
- if (!msg_count)
+ if (less(msg_seqno(msg), l_ptr->reset_checkpoint)) {
+ kfree_skb(buf);
+ buf = NULL;
goto exit;
+ }
+ if (msg_user(msg) == MSG_FRAGMENTER) {
+ l_ptr->stats.recv_fragments++;
+ tipc_link_frag_rcv(&l_ptr->reasm_head,
+ &l_ptr->reasm_tail,
+ &buf);
+ }
+ }
+exit:
+ if ((l_ptr->exp_msg_count == 0) && (l_ptr->flags & LINK_STOPPED)) {
+ tipc_node_detach_link(l_ptr->owner, l_ptr);
+ kfree(l_ptr);
}
+ return buf;
+}
- /* Receive original message */
- if (dest_link->exp_msg_count == 0) {
- pr_warn("%sgot too many tunnelled messages\n", link_co_err);
+/* tipc_link_tunnel_rcv(): Receive a tunnelled packet, sent
+ * via other link as result of a failover (ORIGINAL_MSG) or
+ * a new active link (DUPLICATE_MSG). Failover packets are
+ * returned to the active link for delivery upwards.
+ * Owner node is locked.
+ */
+static int tipc_link_tunnel_rcv(struct tipc_node *n_ptr,
+ struct sk_buff **buf)
+{
+ struct sk_buff *t_buf = *buf;
+ struct tipc_link *l_ptr;
+ struct tipc_msg *t_msg = buf_msg(t_buf);
+ u32 bearer_id = msg_bearer_id(t_msg);
+
+ *buf = NULL;
+
+ if (bearer_id >= MAX_BEARERS)
goto exit;
- }
- dest_link->exp_msg_count--;
- if (less(msg_seqno(msg), dest_link->reset_checkpoint)) {
+
+ l_ptr = n_ptr->links[bearer_id];
+ if (!l_ptr)
goto exit;
- } else {
- *buf = buf_extract(tunnel_buf, INT_H_SIZE);
- if (*buf != NULL) {
- kfree_skb(tunnel_buf);
- return 1;
- } else {
- pr_warn("%soriginal msg dropped\n", link_co_err);
- }
- }
+
+ if (msg_type(t_msg) == DUPLICATE_MSG)
+ tipc_link_dup_rcv(l_ptr, t_buf);
+ else if (msg_type(t_msg) == ORIGINAL_MSG)
+ *buf = tipc_link_failover_rcv(l_ptr, t_buf);
+ else
+ pr_warn("%sunknown tunnel pkt received\n", link_co_err);
exit:
- *buf = NULL;
- kfree_skb(tunnel_buf);
- return 0;
+ kfree_skb(t_buf);
+ return *buf != NULL;
}
/*
* Bundler functionality:
*/
-void tipc_link_recv_bundle(struct sk_buff *buf)
+void tipc_link_bundle_rcv(struct sk_buff *buf)
{
u32 msgcount = msg_msgcnt(buf_msg(buf));
u32 pos = INT_H_SIZE;
@@ -2217,11 +2250,11 @@ void tipc_link_recv_bundle(struct sk_buff *buf)
*/
/*
- * link_send_long_buf: Entry for buffers needing fragmentation.
+ * tipc_link_frag_xmit: Entry for buffers needing fragmentation.
* The buffer is complete, inclusive total message length.
* Returns user data length.
*/
-static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
+static int tipc_link_frag_xmit(struct tipc_link *l_ptr, struct sk_buff *buf)
{
struct sk_buff *buf_chain = NULL;
struct sk_buff *buf_chain_tail = (struct sk_buff *)&buf_chain;
@@ -2284,12 +2317,11 @@ static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
return dsz;
}
-/*
- * tipc_link_recv_fragment(): Called with node lock on. Returns
+/* tipc_link_frag_rcv(): Called with node lock on. Returns
* the reassembled buffer if message is complete.
*/
-int tipc_link_recv_fragment(struct sk_buff **head, struct sk_buff **tail,
- struct sk_buff **fbuf)
+int tipc_link_frag_rcv(struct sk_buff **head, struct sk_buff **tail,
+ struct sk_buff **fbuf)
{
struct sk_buff *frag = *fbuf;
struct tipc_msg *msg = buf_msg(frag);
@@ -2303,6 +2335,7 @@ int tipc_link_recv_fragment(struct sk_buff **head, struct sk_buff **tail,
goto out_free;
*head = frag;
skb_frag_list_init(*head);
+ *fbuf = NULL;
return 0;
} else if (*head &&
skb_try_coalesce(*head, frag, &headstolen, &delta)) {
@@ -2322,10 +2355,12 @@ int tipc_link_recv_fragment(struct sk_buff **head, struct sk_buff **tail,
*tail = *head = NULL;
return LINK_REASM_COMPLETE;
}
+ *fbuf = NULL;
return 0;
out_free:
pr_warn_ratelimited("Link unable to reassemble fragmented message\n");
kfree_skb(*fbuf);
+ *fbuf = NULL;
return LINK_REASM_ERROR;
}
@@ -2359,35 +2394,41 @@ void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window)
l_ptr->queue_limit[MSG_FRAGMENTER] = 4000;
}
-/**
- * link_find_link - locate link by name
- * @name: ptr to link name string
- * @node: ptr to area to be filled with ptr to associated node
- *
+/* tipc_link_find_owner - locate owner node of link by link's name
+ * @name: pointer to link name string
+ * @bearer_id: pointer to index in 'node->links' array where the link was found.
* Caller must hold 'tipc_net_lock' to ensure node and bearer are not deleted;
* this also prevents link deletion.
*
- * Returns pointer to link (or 0 if invalid link name).
+ * Returns pointer to node owning the link, or 0 if no matching link is found.
*/
-static struct tipc_link *link_find_link(const char *name,
- struct tipc_node **node)
+static struct tipc_node *tipc_link_find_owner(const char *link_name,
+ unsigned int *bearer_id)
{
struct tipc_link *l_ptr;
struct tipc_node *n_ptr;
+ struct tipc_node *found_node = 0;
int i;
- list_for_each_entry(n_ptr, &tipc_node_list, list) {
+ *bearer_id = 0;
+ rcu_read_lock();
+ list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
+ tipc_node_lock(n_ptr);
for (i = 0; i < MAX_BEARERS; i++) {
l_ptr = n_ptr->links[i];
- if (l_ptr && !strcmp(l_ptr->name, name))
- goto found;
+ 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;
}
- l_ptr = NULL;
- n_ptr = NULL;
-found:
- *node = n_ptr;
- return l_ptr;
+ rcu_read_unlock();
+
+ return found_node;
}
/**
@@ -2429,32 +2470,33 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd)
struct tipc_link *l_ptr;
struct tipc_bearer *b_ptr;
struct tipc_media *m_ptr;
+ int bearer_id;
int res = 0;
- l_ptr = link_find_link(name, &node);
- if (l_ptr) {
- /*
- * acquire node lock for tipc_link_send_proto_msg().
- * see "TIPC locking policy" in net.c.
- */
+ node = tipc_link_find_owner(name, &bearer_id);
+ if (node) {
tipc_node_lock(node);
- switch (cmd) {
- case TIPC_CMD_SET_LINK_TOL:
- link_set_supervision_props(l_ptr, new_value);
- tipc_link_send_proto_msg(l_ptr,
- STATE_MSG, 0, 0, new_value, 0, 0);
- break;
- case TIPC_CMD_SET_LINK_PRI:
- l_ptr->priority = new_value;
- tipc_link_send_proto_msg(l_ptr,
- STATE_MSG, 0, 0, 0, new_value, 0);
- break;
- case TIPC_CMD_SET_LINK_WINDOW:
- tipc_link_set_queue_limits(l_ptr, new_value);
- break;
- default:
- res = -EINVAL;
- break;
+ l_ptr = node->links[bearer_id];
+
+ if (l_ptr) {
+ switch (cmd) {
+ case TIPC_CMD_SET_LINK_TOL:
+ link_set_supervision_props(l_ptr, new_value);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0,
+ new_value, 0, 0);
+ break;
+ case TIPC_CMD_SET_LINK_PRI:
+ l_ptr->priority = new_value;
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0,
+ 0, new_value, 0);
+ break;
+ case TIPC_CMD_SET_LINK_WINDOW:
+ tipc_link_set_queue_limits(l_ptr, new_value);
+ break;
+ default:
+ res = -EINVAL;
+ break;
+ }
}
tipc_node_unlock(node);
return res;
@@ -2549,6 +2591,7 @@ struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, int req_tlv_
char *link_name;
struct tipc_link *l_ptr;
struct tipc_node *node;
+ unsigned int bearer_id;
if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_LINK_NAME))
return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
@@ -2559,15 +2602,19 @@ struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, int req_tlv_
return tipc_cfg_reply_error_string("link not found");
return tipc_cfg_reply_none();
}
-
read_lock_bh(&tipc_net_lock);
- l_ptr = link_find_link(link_name, &node);
- if (!l_ptr) {
+ node = tipc_link_find_owner(link_name, &bearer_id);
+ if (!node) {
read_unlock_bh(&tipc_net_lock);
return tipc_cfg_reply_error_string("link not found");
}
-
tipc_node_lock(node);
+ l_ptr = node->links[bearer_id];
+ if (!l_ptr) {
+ tipc_node_unlock(node);
+ read_unlock_bh(&tipc_net_lock);
+ return tipc_cfg_reply_error_string("link not found");
+ }
link_reset_statistics(l_ptr);
tipc_node_unlock(node);
read_unlock_bh(&tipc_net_lock);
@@ -2597,18 +2644,27 @@ static int tipc_link_stats(const char *name, char *buf, const u32 buf_size)
struct tipc_node *node;
char *status;
u32 profile_total = 0;
+ unsigned int bearer_id;
int ret;
if (!strcmp(name, tipc_bclink_name))
return tipc_bclink_stats(buf, buf_size);
read_lock_bh(&tipc_net_lock);
- l = link_find_link(name, &node);
- if (!l) {
+ node = tipc_link_find_owner(name, &bearer_id);
+ if (!node) {
read_unlock_bh(&tipc_net_lock);
return 0;
}
tipc_node_lock(node);
+
+ l = node->links[bearer_id];
+ if (!l) {
+ tipc_node_unlock(node);
+ read_unlock_bh(&tipc_net_lock);
+ return 0;
+ }
+
s = &l->stats;
if (tipc_link_is_active(l))
diff --git a/net/tipc/link.h b/net/tipc/link.h
index 3b6aa65b608c..8c0b49b5b2ee 100644
--- a/net/tipc/link.h
+++ b/net/tipc/link.h
@@ -1,7 +1,7 @@
/*
* net/tipc/link.h: Include file for TIPC link code
*
- * Copyright (c) 1995-2006, Ericsson AB
+ * Copyright (c) 1995-2006, 2013, Ericsson AB
* Copyright (c) 2004-2005, 2010-2011, Wind River Systems
* All rights reserved.
*
@@ -40,27 +40,28 @@
#include "msg.h"
#include "node.h"
-/*
- * Link reassembly status codes
+/* Link reassembly status codes
*/
#define LINK_REASM_ERROR -1
#define LINK_REASM_COMPLETE 1
-/*
- * Out-of-range value for link sequence numbers
+/* Out-of-range value for link sequence numbers
*/
#define INVALID_LINK_SEQ 0x10000
-/*
- * Link states
+/* Link working states
*/
#define WORKING_WORKING 560810u
#define WORKING_UNKNOWN 560811u
#define RESET_UNKNOWN 560812u
#define RESET_RESET 560813u
-/*
- * Starting value for maximum packet size negotiation on unicast links
+/* Link endpoint execution states
+ */
+#define LINK_STARTED 0x0001
+#define LINK_STOPPED 0x0002
+
+/* Starting value for maximum packet size negotiation on unicast links
* (unless bearer MTU is less)
*/
#define MAX_PKT_DEFAULT 1500
@@ -102,8 +103,7 @@ struct tipc_stats {
* @media_addr: media address to use when sending messages over link
* @timer: link timer
* @owner: pointer to peer node
- * @link_list: adjacent links in bearer's list of links
- * @started: indicates if link has been started
+ * @flags: execution state flags for link endpoint instance
* @checkpoint: reference point for triggering link continuity checking
* @peer_session: link session # being used by peer end of link
* @peer_bearer_id: bearer id used by link's peer endpoint
@@ -149,10 +149,9 @@ struct tipc_link {
struct tipc_media_addr media_addr;
struct timer_list timer;
struct tipc_node *owner;
- struct list_head link_list;
/* Management and link supervision data */
- int started;
+ unsigned int flags;
u32 checkpoint;
u32 peer_session;
u32 peer_bearer_id;
@@ -215,10 +214,9 @@ struct tipc_port;
struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
struct tipc_bearer *b_ptr,
const struct tipc_media_addr *media_addr);
-void tipc_link_delete(struct tipc_link *l_ptr);
+void tipc_link_delete_list(unsigned int bearer_id, bool shutting_down);
void tipc_link_failover_send_queue(struct tipc_link *l_ptr);
-void tipc_link_dup_send_queue(struct tipc_link *l_ptr,
- struct tipc_link *dest);
+void tipc_link_dup_queue_xmit(struct tipc_link *l_ptr, struct tipc_link *dest);
void tipc_link_reset_fragments(struct tipc_link *l_ptr);
int tipc_link_is_up(struct tipc_link *l_ptr);
int tipc_link_is_active(struct tipc_link *l_ptr);
@@ -231,23 +229,24 @@ struct sk_buff *tipc_link_cmd_show_stats(const void *req_tlv_area,
struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area,
int req_tlv_space);
void tipc_link_reset(struct tipc_link *l_ptr);
-int tipc_link_send(struct sk_buff *buf, u32 dest, u32 selector);
-void tipc_link_send_names(struct list_head *message_list, u32 dest);
+void tipc_link_reset_list(unsigned int bearer_id);
+int tipc_link_xmit(struct sk_buff *buf, u32 dest, u32 selector);
+void tipc_link_names_xmit(struct list_head *message_list, u32 dest);
+int __tipc_link_xmit(struct tipc_link *l_ptr, struct sk_buff *buf);
int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf);
u32 tipc_link_get_max_pkt(u32 dest, u32 selector);
-int tipc_link_send_sections_fast(struct tipc_port *sender,
- struct iovec const *msg_sect,
- unsigned int len, u32 destnode);
-void tipc_link_recv_bundle(struct sk_buff *buf);
-int tipc_link_recv_fragment(struct sk_buff **reasm_head,
- struct sk_buff **reasm_tail,
- struct sk_buff **fbuf);
-void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ, int prob,
- u32 gap, u32 tolerance, u32 priority,
- u32 acked_mtu);
+int tipc_link_iovec_xmit_fast(struct tipc_port *sender,
+ struct iovec const *msg_sect,
+ unsigned int len, u32 destnode);
+void tipc_link_bundle_rcv(struct sk_buff *buf);
+int tipc_link_frag_rcv(struct sk_buff **reasm_head,
+ struct sk_buff **reasm_tail,
+ struct sk_buff **fbuf);
+void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int prob,
+ u32 gap, u32 tolerance, u32 priority, u32 acked_mtu);
void tipc_link_push_queue(struct tipc_link *l_ptr);
u32 tipc_link_defer_pkt(struct sk_buff **head, struct sk_buff **tail,
- struct sk_buff *buf);
+ struct sk_buff *buf);
void tipc_link_wakeup_ports(struct tipc_link *l_ptr, int all);
void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window);
void tipc_link_retransmit(struct tipc_link *l_ptr,
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index e0d08055754e..aff8041dc157 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -131,16 +131,24 @@ static void named_cluster_distribute(struct sk_buff *buf)
{
struct sk_buff *buf_copy;
struct tipc_node *n_ptr;
+ struct tipc_link *l_ptr;
- list_for_each_entry(n_ptr, &tipc_node_list, list) {
- if (tipc_node_active_links(n_ptr)) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
+ spin_lock_bh(&n_ptr->lock);
+ l_ptr = n_ptr->active_links[n_ptr->addr & 1];
+ if (l_ptr) {
buf_copy = skb_copy(buf, GFP_ATOMIC);
- if (!buf_copy)
+ if (!buf_copy) {
+ spin_unlock_bh(&n_ptr->lock);
break;
+ }
msg_set_destnode(buf_msg(buf_copy), n_ptr->addr);
- tipc_link_send(buf_copy, n_ptr->addr, n_ptr->addr);
+ __tipc_link_xmit(l_ptr, buf_copy);
}
+ spin_unlock_bh(&n_ptr->lock);
}
+ rcu_read_unlock();
kfree_skb(buf);
}
@@ -262,7 +270,7 @@ void tipc_named_node_up(unsigned long nodearg)
named_distribute(&message_list, node, &publ_zone, max_item_buf);
read_unlock_bh(&tipc_nametbl_lock);
- tipc_link_send_names(&message_list, node);
+ tipc_link_names_xmit(&message_list, node);
}
/**
@@ -293,9 +301,9 @@ static void named_purge_publ(struct publication *publ)
}
/**
- * tipc_named_recv - process name table update message sent by another node
+ * tipc_named_rcv - process name table update message sent by another node
*/
-void tipc_named_recv(struct sk_buff *buf)
+void tipc_named_rcv(struct sk_buff *buf)
{
struct publication *publ;
struct tipc_msg *msg = buf_msg(buf);
diff --git a/net/tipc/name_distr.h b/net/tipc/name_distr.h
index 1e41bdd4f255..9b312ccfd43e 100644
--- a/net/tipc/name_distr.h
+++ b/net/tipc/name_distr.h
@@ -42,7 +42,7 @@
void tipc_named_publish(struct publication *publ);
void tipc_named_withdraw(struct publication *publ);
void tipc_named_node_up(unsigned long node);
-void tipc_named_recv(struct sk_buff *buf);
+void tipc_named_rcv(struct sk_buff *buf);
void tipc_named_reinit(void);
#endif
diff --git a/net/tipc/net.c b/net/tipc/net.c
index 7d305ecc09c2..0374a817631e 100644
--- a/net/tipc/net.c
+++ b/net/tipc/net.c
@@ -146,19 +146,19 @@ void tipc_net_route_msg(struct sk_buff *buf)
if (tipc_in_scope(dnode, tipc_own_addr)) {
if (msg_isdata(msg)) {
if (msg_mcast(msg))
- tipc_port_recv_mcast(buf, NULL);
+ tipc_port_mcast_rcv(buf, NULL);
else if (msg_destport(msg))
- tipc_port_recv_msg(buf);
+ tipc_port_rcv(buf);
else
net_route_named_msg(buf);
return;
}
switch (msg_user(msg)) {
case NAME_DISTRIBUTOR:
- tipc_named_recv(buf);
+ tipc_named_rcv(buf);
break;
case CONN_MANAGER:
- tipc_port_recv_proto_msg(buf);
+ tipc_port_proto_rcv(buf);
break;
default:
kfree_skb(buf);
@@ -168,7 +168,7 @@ void tipc_net_route_msg(struct sk_buff *buf)
/* Handle message for another node */
skb_trim(buf, msg_size(msg));
- tipc_link_send(buf, dnode, msg_link_selector(msg));
+ tipc_link_xmit(buf, dnode, msg_link_selector(msg));
}
void tipc_net_start(u32 addr)
@@ -182,8 +182,6 @@ void tipc_net_start(u32 addr)
tipc_bclink_init();
write_unlock_bh(&tipc_net_lock);
- tipc_cfg_reinit();
-
pr_info("Started in network mode\n");
pr_info("Own node address %s, network identity %u\n",
tipc_addr_string_fill(addr_string, tipc_own_addr), tipc_net_id);
@@ -191,15 +189,14 @@ void tipc_net_start(u32 addr)
void tipc_net_stop(void)
{
- struct tipc_node *node, *t_node;
-
if (!tipc_own_addr)
return;
+
write_lock_bh(&tipc_net_lock);
tipc_bearer_stop();
tipc_bclink_stop();
- list_for_each_entry_safe(node, t_node, &tipc_node_list, list)
- tipc_node_delete(node);
+ tipc_node_stop();
write_unlock_bh(&tipc_net_lock);
+
pr_info("Left network mode\n");
}
diff --git a/net/tipc/node.c b/net/tipc/node.c
index efe4d41bf11b..1d3a4999a70f 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -2,7 +2,7 @@
* net/tipc/node.c: TIPC node management routines
*
* Copyright (c) 2000-2006, 2012 Ericsson AB
- * Copyright (c) 2005-2006, 2010-2011, Wind River Systems
+ * Copyright (c) 2005-2006, 2010-2014, Wind River Systems
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,13 +44,11 @@
static void node_lost_contact(struct tipc_node *n_ptr);
static void node_established_contact(struct tipc_node *n_ptr);
-static DEFINE_SPINLOCK(node_create_lock);
-
static struct hlist_head node_htable[NODE_HTABLE_SIZE];
LIST_HEAD(tipc_node_list);
static u32 tipc_num_nodes;
-
-static atomic_t tipc_num_links = ATOMIC_INIT(0);
+static u32 tipc_num_links;
+static DEFINE_SPINLOCK(node_list_lock);
/*
* A trivial power-of-two bitmask technique is used for speed, since this
@@ -73,37 +71,26 @@ struct tipc_node *tipc_node_find(u32 addr)
if (unlikely(!in_own_cluster_exact(addr)))
return NULL;
- hlist_for_each_entry(node, &node_htable[tipc_hashfn(addr)], hash) {
- if (node->addr == addr)
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(node, &node_htable[tipc_hashfn(addr)], hash) {
+ if (node->addr == addr) {
+ rcu_read_unlock();
return node;
+ }
}
+ rcu_read_unlock();
return NULL;
}
-/**
- * tipc_node_create - create neighboring node
- *
- * Currently, this routine is called by neighbor discovery code, which holds
- * net_lock for reading only. We must take node_create_lock to ensure a node
- * isn't created twice if two different bearers discover the node at the same
- * time. (It would be preferable to switch to holding net_lock in write mode,
- * but this is a non-trivial change.)
- */
struct tipc_node *tipc_node_create(u32 addr)
{
struct tipc_node *n_ptr, *temp_node;
- spin_lock_bh(&node_create_lock);
-
- n_ptr = tipc_node_find(addr);
- if (n_ptr) {
- spin_unlock_bh(&node_create_lock);
- return n_ptr;
- }
+ spin_lock_bh(&node_list_lock);
n_ptr = kzalloc(sizeof(*n_ptr), GFP_ATOMIC);
if (!n_ptr) {
- spin_unlock_bh(&node_create_lock);
+ spin_unlock_bh(&node_list_lock);
pr_warn("Node creation failed, no memory\n");
return NULL;
}
@@ -114,31 +101,41 @@ struct tipc_node *tipc_node_create(u32 addr)
INIT_LIST_HEAD(&n_ptr->list);
INIT_LIST_HEAD(&n_ptr->nsub);
- hlist_add_head(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]);
+ hlist_add_head_rcu(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]);
- list_for_each_entry(temp_node, &tipc_node_list, list) {
+ list_for_each_entry_rcu(temp_node, &tipc_node_list, list) {
if (n_ptr->addr < temp_node->addr)
break;
}
- list_add_tail(&n_ptr->list, &temp_node->list);
+ list_add_tail_rcu(&n_ptr->list, &temp_node->list);
n_ptr->block_setup = WAIT_PEER_DOWN;
n_ptr->signature = INVALID_NODE_SIG;
tipc_num_nodes++;
- spin_unlock_bh(&node_create_lock);
+ spin_unlock_bh(&node_list_lock);
return n_ptr;
}
-void tipc_node_delete(struct tipc_node *n_ptr)
+static void tipc_node_delete(struct tipc_node *n_ptr)
{
- list_del(&n_ptr->list);
- hlist_del(&n_ptr->hash);
- kfree(n_ptr);
+ list_del_rcu(&n_ptr->list);
+ hlist_del_rcu(&n_ptr->hash);
+ kfree_rcu(n_ptr, rcu);
tipc_num_nodes--;
}
+void tipc_node_stop(void)
+{
+ struct tipc_node *node, *t_node;
+
+ spin_lock_bh(&node_list_lock);
+ list_for_each_entry_safe(node, t_node, &tipc_node_list, list)
+ tipc_node_delete(node);
+ spin_unlock_bh(&node_list_lock);
+}
+
/**
* tipc_node_link_up - handle addition of link
*
@@ -162,7 +159,7 @@ void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
pr_info("New link <%s> becomes standby\n", l_ptr->name);
return;
}
- tipc_link_dup_send_queue(active[0], l_ptr);
+ tipc_link_dup_queue_xmit(active[0], l_ptr);
if (l_ptr->priority == active[0]->priority) {
active[0] = l_ptr;
return;
@@ -243,15 +240,25 @@ int tipc_node_is_up(struct tipc_node *n_ptr)
void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
{
n_ptr->links[l_ptr->b_ptr->identity] = l_ptr;
- atomic_inc(&tipc_num_links);
+ spin_lock_bh(&node_list_lock);
+ tipc_num_links++;
+ spin_unlock_bh(&node_list_lock);
n_ptr->link_cnt++;
}
void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
{
- n_ptr->links[l_ptr->b_ptr->identity] = NULL;
- atomic_dec(&tipc_num_links);
- n_ptr->link_cnt--;
+ int i;
+
+ for (i = 0; i < MAX_BEARERS; i++) {
+ if (l_ptr != n_ptr->links[i])
+ continue;
+ n_ptr->links[i] = NULL;
+ spin_lock_bh(&node_list_lock);
+ tipc_num_links--;
+ spin_unlock_bh(&node_list_lock);
+ n_ptr->link_cnt--;
+ }
}
static void node_established_contact(struct tipc_node *n_ptr)
@@ -335,27 +342,28 @@ struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space)
return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
" (network address)");
- read_lock_bh(&tipc_net_lock);
+ spin_lock_bh(&node_list_lock);
if (!tipc_num_nodes) {
- read_unlock_bh(&tipc_net_lock);
+ spin_unlock_bh(&node_list_lock);
return tipc_cfg_reply_none();
}
/* For now, get space for all other nodes */
payload_size = TLV_SPACE(sizeof(node_info)) * tipc_num_nodes;
if (payload_size > 32768u) {
- read_unlock_bh(&tipc_net_lock);
+ spin_unlock_bh(&node_list_lock);
return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
" (too many nodes)");
}
+ spin_unlock_bh(&node_list_lock);
+
buf = tipc_cfg_reply_alloc(payload_size);
- if (!buf) {
- read_unlock_bh(&tipc_net_lock);
+ if (!buf)
return NULL;
- }
/* Add TLVs for all nodes in scope */
- list_for_each_entry(n_ptr, &tipc_node_list, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
if (!tipc_in_scope(domain, n_ptr->addr))
continue;
node_info.addr = htonl(n_ptr->addr);
@@ -363,8 +371,7 @@ struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space)
tipc_cfg_append_tlv(buf, TIPC_TLV_NODE_INFO,
&node_info, sizeof(node_info));
}
-
- read_unlock_bh(&tipc_net_lock);
+ rcu_read_unlock();
return buf;
}
@@ -387,21 +394,19 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space)
if (!tipc_own_addr)
return tipc_cfg_reply_none();
- read_lock_bh(&tipc_net_lock);
-
+ spin_lock_bh(&node_list_lock);
/* Get space for all unicast links + broadcast link */
- payload_size = TLV_SPACE(sizeof(link_info)) *
- (atomic_read(&tipc_num_links) + 1);
+ payload_size = TLV_SPACE((sizeof(link_info)) * (tipc_num_links + 1));
if (payload_size > 32768u) {
- read_unlock_bh(&tipc_net_lock);
+ spin_unlock_bh(&node_list_lock);
return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
" (too many links)");
}
+ spin_unlock_bh(&node_list_lock);
+
buf = tipc_cfg_reply_alloc(payload_size);
- if (!buf) {
- read_unlock_bh(&tipc_net_lock);
+ if (!buf)
return NULL;
- }
/* Add TLV for broadcast link */
link_info.dest = htonl(tipc_cluster_mask(tipc_own_addr));
@@ -410,7 +415,8 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space)
tipc_cfg_append_tlv(buf, TIPC_TLV_LINK_INFO, &link_info, sizeof(link_info));
/* Add TLVs for any other links in scope */
- list_for_each_entry(n_ptr, &tipc_node_list, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
u32 i;
if (!tipc_in_scope(domain, n_ptr->addr))
@@ -427,7 +433,6 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space)
}
tipc_node_unlock(n_ptr);
}
-
- read_unlock_bh(&tipc_net_lock);
+ rcu_read_unlock();
return buf;
}
diff --git a/net/tipc/node.h b/net/tipc/node.h
index 63e2e8ead2fe..7cbb8cec1a93 100644
--- a/net/tipc/node.h
+++ b/net/tipc/node.h
@@ -2,7 +2,7 @@
* net/tipc/node.h: Include file for TIPC node management routines
*
* Copyright (c) 2000-2006, Ericsson AB
- * Copyright (c) 2005, 2010-2011, Wind River Systems
+ * Copyright (c) 2005, 2010-2014, Wind River Systems
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -66,6 +66,7 @@
* @link_cnt: number of links to node
* @signature: node instance identifier
* @bclink: broadcast-related info
+ * @rcu: rcu struct for tipc_node
* @acked: sequence # of last outbound b'cast message acknowledged by node
* @last_in: sequence # of last in-sequence b'cast message received from node
* @last_sent: sequence # of last b'cast message sent by node
@@ -89,6 +90,7 @@ struct tipc_node {
int working_links;
int block_setup;
u32 signature;
+ struct rcu_head rcu;
struct {
u32 acked;
u32 last_in;
@@ -107,7 +109,7 @@ extern struct list_head tipc_node_list;
struct tipc_node *tipc_node_find(u32 addr);
struct tipc_node *tipc_node_create(u32 addr);
-void tipc_node_delete(struct tipc_node *n_ptr);
+void tipc_node_stop(void);
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);
void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr);
diff --git a/net/tipc/port.c b/net/tipc/port.c
index b742b2654525..5c14c7801ee6 100644
--- a/net/tipc/port.c
+++ b/net/tipc/port.c
@@ -1,7 +1,7 @@
/*
* net/tipc/port.c: TIPC port code
*
- * Copyright (c) 1992-2007, Ericsson AB
+ * Copyright (c) 1992-2007, 2014, Ericsson AB
* Copyright (c) 2004-2008, 2010-2013, Wind River Systems
* All rights reserved.
*
@@ -38,6 +38,7 @@
#include "config.h"
#include "port.h"
#include "name_table.h"
+#include "socket.h"
/* Connection management: */
#define PROBING_INTERVAL 3600000 /* [ms] => 1 h */
@@ -54,17 +55,6 @@ static struct sk_buff *port_build_self_abort_msg(struct tipc_port *, u32 err);
static struct sk_buff *port_build_peer_abort_msg(struct tipc_port *, u32 err);
static void port_timeout(unsigned long ref);
-
-static u32 port_peernode(struct tipc_port *p_ptr)
-{
- return msg_destnode(&p_ptr->phdr);
-}
-
-static u32 port_peerport(struct tipc_port *p_ptr)
-{
- return msg_destport(&p_ptr->phdr);
-}
-
/**
* tipc_port_peer_msg - verify message was sent by connected port's peer
*
@@ -76,33 +66,32 @@ int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg)
u32 peernode;
u32 orignode;
- if (msg_origport(msg) != port_peerport(p_ptr))
+ if (msg_origport(msg) != tipc_port_peerport(p_ptr))
return 0;
orignode = msg_orignode(msg);
- peernode = port_peernode(p_ptr);
+ peernode = tipc_port_peernode(p_ptr);
return (orignode == peernode) ||
(!orignode && (peernode == tipc_own_addr)) ||
(!peernode && (orignode == tipc_own_addr));
}
/**
- * tipc_multicast - send a multicast message to local and remote destinations
+ * tipc_port_mcast_xmit - send a multicast message to local and remote
+ * destinations
*/
-int tipc_multicast(u32 ref, struct tipc_name_seq const *seq,
- struct iovec const *msg_sect, unsigned int len)
+int tipc_port_mcast_xmit(struct tipc_port *oport,
+ struct tipc_name_seq const *seq,
+ struct iovec const *msg_sect,
+ unsigned int len)
{
struct tipc_msg *hdr;
struct sk_buff *buf;
struct sk_buff *ibuf = NULL;
struct tipc_port_list dports = {0, NULL, };
- struct tipc_port *oport = tipc_port_deref(ref);
int ext_targets;
int res;
- if (unlikely(!oport))
- return -EINVAL;
-
/* Create multicast message */
hdr = &oport->phdr;
msg_set_type(hdr, TIPC_MCAST_MSG);
@@ -131,7 +120,7 @@ int tipc_multicast(u32 ref, struct tipc_name_seq const *seq,
return -ENOMEM;
}
}
- res = tipc_bclink_send_msg(buf);
+ res = tipc_bclink_xmit(buf);
if ((res < 0) && (dports.count != 0))
kfree_skb(ibuf);
} else {
@@ -140,7 +129,7 @@ int tipc_multicast(u32 ref, struct tipc_name_seq const *seq,
if (res >= 0) {
if (ibuf)
- tipc_port_recv_mcast(ibuf, &dports);
+ tipc_port_mcast_rcv(ibuf, &dports);
} else {
tipc_port_list_free(&dports);
}
@@ -148,11 +137,11 @@ int tipc_multicast(u32 ref, struct tipc_name_seq const *seq,
}
/**
- * tipc_port_recv_mcast - deliver multicast message to all destination ports
+ * tipc_port_mcast_rcv - deliver multicast message to all destination ports
*
* If there is no port list, perform a lookup to create one
*/
-void tipc_port_recv_mcast(struct sk_buff *buf, struct tipc_port_list *dp)
+void tipc_port_mcast_rcv(struct sk_buff *buf, struct tipc_port_list *dp)
{
struct tipc_msg *msg;
struct tipc_port_list dports = {0, NULL, };
@@ -176,7 +165,7 @@ void tipc_port_recv_mcast(struct sk_buff *buf, struct tipc_port_list *dp)
msg_set_destnode(msg, tipc_own_addr);
if (dp->count == 1) {
msg_set_destport(msg, dp->ports[0]);
- tipc_port_recv_msg(buf);
+ tipc_port_rcv(buf);
tipc_port_list_free(dp);
return;
}
@@ -191,7 +180,7 @@ void tipc_port_recv_mcast(struct sk_buff *buf, struct tipc_port_list *dp)
if ((index == 0) && (cnt != 0))
item = item->next;
msg_set_destport(buf_msg(b), item->ports[index]);
- tipc_port_recv_msg(b);
+ tipc_port_rcv(b);
}
}
exit:
@@ -199,40 +188,32 @@ exit:
tipc_port_list_free(dp);
}
-/**
- * tipc_createport - create a generic TIPC port
+
+void tipc_port_wakeup(struct tipc_port *port)
+{
+ tipc_sock_wakeup(tipc_port_to_sock(port));
+}
+
+/* tipc_port_init - intiate TIPC port and lock it
*
- * Returns pointer to (locked) TIPC port, or NULL if unable to create it
+ * Returns obtained reference if initialization is successful, zero otherwise
*/
-struct tipc_port *tipc_createport(struct sock *sk,
- u32 (*dispatcher)(struct tipc_port *,
- struct sk_buff *),
- void (*wakeup)(struct tipc_port *),
- const u32 importance)
+u32 tipc_port_init(struct tipc_port *p_ptr,
+ const unsigned int importance)
{
- struct tipc_port *p_ptr;
struct tipc_msg *msg;
u32 ref;
- p_ptr = kzalloc(sizeof(*p_ptr), GFP_ATOMIC);
- if (!p_ptr) {
- pr_warn("Port creation failed, no memory\n");
- return NULL;
- }
ref = tipc_ref_acquire(p_ptr, &p_ptr->lock);
if (!ref) {
- pr_warn("Port creation failed, ref. table exhausted\n");
- kfree(p_ptr);
- return NULL;
+ pr_warn("Port registration failed, ref. table exhausted\n");
+ return 0;
}
- p_ptr->sk = sk;
p_ptr->max_pkt = MAX_PKT_DEFAULT;
p_ptr->ref = ref;
INIT_LIST_HEAD(&p_ptr->wait_list);
INIT_LIST_HEAD(&p_ptr->subscription.nodesub_list);
- p_ptr->dispatcher = dispatcher;
- p_ptr->wakeup = wakeup;
k_init_timer(&p_ptr->timer, (Handler)port_timeout, ref);
INIT_LIST_HEAD(&p_ptr->publications);
INIT_LIST_HEAD(&p_ptr->port_list);
@@ -248,10 +229,10 @@ struct tipc_port *tipc_createport(struct sock *sk,
msg_set_origport(msg, ref);
list_add_tail(&p_ptr->port_list, &ports);
spin_unlock_bh(&tipc_port_list_lock);
- return p_ptr;
+ return ref;
}
-int tipc_deleteport(struct tipc_port *p_ptr)
+void tipc_port_destroy(struct tipc_port *p_ptr)
{
struct sk_buff *buf = NULL;
@@ -272,67 +253,7 @@ int tipc_deleteport(struct tipc_port *p_ptr)
list_del(&p_ptr->wait_list);
spin_unlock_bh(&tipc_port_list_lock);
k_term_timer(&p_ptr->timer);
- kfree(p_ptr);
tipc_net_route_msg(buf);
- return 0;
-}
-
-static int port_unreliable(struct tipc_port *p_ptr)
-{
- return msg_src_droppable(&p_ptr->phdr);
-}
-
-int tipc_portunreliable(u32 ref, unsigned int *isunreliable)
-{
- struct tipc_port *p_ptr;
-
- p_ptr = tipc_port_lock(ref);
- if (!p_ptr)
- return -EINVAL;
- *isunreliable = port_unreliable(p_ptr);
- tipc_port_unlock(p_ptr);
- return 0;
-}
-
-int tipc_set_portunreliable(u32 ref, unsigned int isunreliable)
-{
- struct tipc_port *p_ptr;
-
- p_ptr = tipc_port_lock(ref);
- if (!p_ptr)
- return -EINVAL;
- msg_set_src_droppable(&p_ptr->phdr, (isunreliable != 0));
- tipc_port_unlock(p_ptr);
- return 0;
-}
-
-static int port_unreturnable(struct tipc_port *p_ptr)
-{
- return msg_dest_droppable(&p_ptr->phdr);
-}
-
-int tipc_portunreturnable(u32 ref, unsigned int *isunrejectable)
-{
- struct tipc_port *p_ptr;
-
- p_ptr = tipc_port_lock(ref);
- if (!p_ptr)
- return -EINVAL;
- *isunrejectable = port_unreturnable(p_ptr);
- tipc_port_unlock(p_ptr);
- return 0;
-}
-
-int tipc_set_portunreturnable(u32 ref, unsigned int isunrejectable)
-{
- struct tipc_port *p_ptr;
-
- p_ptr = tipc_port_lock(ref);
- if (!p_ptr)
- return -EINVAL;
- msg_set_dest_droppable(&p_ptr->phdr, (isunrejectable != 0));
- tipc_port_unlock(p_ptr);
- return 0;
}
/*
@@ -350,8 +271,8 @@ static struct sk_buff *port_build_proto_msg(struct tipc_port *p_ptr,
if (buf) {
msg = buf_msg(buf);
tipc_msg_init(msg, CONN_MANAGER, type, INT_H_SIZE,
- port_peernode(p_ptr));
- msg_set_destport(msg, port_peerport(p_ptr));
+ tipc_port_peernode(p_ptr));
+ msg_set_destport(msg, tipc_port_peerport(p_ptr));
msg_set_origport(msg, p_ptr->ref);
msg_set_msgcnt(msg, ack);
}
@@ -422,17 +343,17 @@ int tipc_reject_msg(struct sk_buff *buf, u32 err)
/* send returned message & dispose of rejected message */
src_node = msg_prevnode(msg);
if (in_own_node(src_node))
- tipc_port_recv_msg(rbuf);
+ tipc_port_rcv(rbuf);
else
- tipc_link_send(rbuf, src_node, msg_link_selector(rmsg));
+ tipc_link_xmit(rbuf, src_node, msg_link_selector(rmsg));
exit:
kfree_skb(buf);
return data_sz;
}
-int tipc_port_reject_sections(struct tipc_port *p_ptr, struct tipc_msg *hdr,
- struct iovec const *msg_sect, unsigned int len,
- int err)
+int tipc_port_iovec_reject(struct tipc_port *p_ptr, struct tipc_msg *hdr,
+ struct iovec const *msg_sect, unsigned int len,
+ int err)
{
struct sk_buff *buf;
int res;
@@ -519,7 +440,7 @@ static struct sk_buff *port_build_peer_abort_msg(struct tipc_port *p_ptr, u32 er
return buf;
}
-void tipc_port_recv_proto_msg(struct sk_buff *buf)
+void tipc_port_proto_rcv(struct sk_buff *buf)
{
struct tipc_msg *msg = buf_msg(buf);
struct tipc_port *p_ptr;
@@ -547,13 +468,12 @@ void tipc_port_recv_proto_msg(struct sk_buff *buf)
/* Process protocol message sent by peer */
switch (msg_type(msg)) {
case CONN_ACK:
- wakeable = tipc_port_congested(p_ptr) && p_ptr->congested &&
- p_ptr->wakeup;
+ wakeable = tipc_port_congested(p_ptr) && p_ptr->congested;
p_ptr->acked += msg_msgcnt(msg);
if (!tipc_port_congested(p_ptr)) {
p_ptr->congested = 0;
if (wakeable)
- p_ptr->wakeup(p_ptr);
+ tipc_port_wakeup(p_ptr);
}
break;
case CONN_PROBE:
@@ -584,8 +504,8 @@ static int port_print(struct tipc_port *p_ptr, char *buf, int len, int full_id)
ret = tipc_snprintf(buf, len, "%-10u:", p_ptr->ref);
if (p_ptr->connected) {
- u32 dport = port_peerport(p_ptr);
- u32 destnode = port_peernode(p_ptr);
+ u32 dport = tipc_port_peerport(p_ptr);
+ u32 destnode = tipc_port_peernode(p_ptr);
ret += tipc_snprintf(buf + ret, len - ret,
" connected to <%u.%u.%u:%u>",
@@ -673,34 +593,6 @@ void tipc_acknowledge(u32 ref, u32 ack)
tipc_net_route_msg(buf);
}
-int tipc_portimportance(u32 ref, unsigned int *importance)
-{
- struct tipc_port *p_ptr;
-
- p_ptr = tipc_port_lock(ref);
- if (!p_ptr)
- return -EINVAL;
- *importance = (unsigned int)msg_importance(&p_ptr->phdr);
- tipc_port_unlock(p_ptr);
- return 0;
-}
-
-int tipc_set_portimportance(u32 ref, unsigned int imp)
-{
- struct tipc_port *p_ptr;
-
- if (imp > TIPC_CRITICAL_IMPORTANCE)
- return -EINVAL;
-
- p_ptr = tipc_port_lock(ref);
- if (!p_ptr)
- return -EINVAL;
- msg_set_importance(&p_ptr->phdr, (u32)imp);
- tipc_port_unlock(p_ptr);
- return 0;
-}
-
-
int tipc_publish(struct tipc_port *p_ptr, unsigned int scope,
struct tipc_name_seq const *seq)
{
@@ -760,7 +652,7 @@ int tipc_withdraw(struct tipc_port *p_ptr, unsigned int scope,
return res;
}
-int tipc_connect(u32 ref, struct tipc_portid const *peer)
+int tipc_port_connect(u32 ref, struct tipc_portid const *peer)
{
struct tipc_port *p_ptr;
int res;
@@ -768,17 +660,17 @@ int tipc_connect(u32 ref, struct tipc_portid const *peer)
p_ptr = tipc_port_lock(ref);
if (!p_ptr)
return -EINVAL;
- res = __tipc_connect(ref, p_ptr, peer);
+ res = __tipc_port_connect(ref, p_ptr, peer);
tipc_port_unlock(p_ptr);
return res;
}
/*
- * __tipc_connect - connect to a remote peer
+ * __tipc_port_connect - connect to a remote peer
*
* Port must be locked.
*/
-int __tipc_connect(u32 ref, struct tipc_port *p_ptr,
+int __tipc_port_connect(u32 ref, struct tipc_port *p_ptr,
struct tipc_portid const *peer)
{
struct tipc_msg *msg;
@@ -815,7 +707,7 @@ exit:
*
* Port must be locked.
*/
-int __tipc_disconnect(struct tipc_port *tp_ptr)
+int __tipc_port_disconnect(struct tipc_port *tp_ptr)
{
if (tp_ptr->connected) {
tp_ptr->connected = 0;
@@ -828,10 +720,10 @@ int __tipc_disconnect(struct tipc_port *tp_ptr)
}
/*
- * tipc_disconnect(): Disconnect port form peer.
+ * tipc_port_disconnect(): Disconnect port form peer.
* This is a node local operation.
*/
-int tipc_disconnect(u32 ref)
+int tipc_port_disconnect(u32 ref)
{
struct tipc_port *p_ptr;
int res;
@@ -839,15 +731,15 @@ int tipc_disconnect(u32 ref)
p_ptr = tipc_port_lock(ref);
if (!p_ptr)
return -EINVAL;
- res = __tipc_disconnect(p_ptr);
+ res = __tipc_port_disconnect(p_ptr);
tipc_port_unlock(p_ptr);
return res;
}
/*
- * tipc_shutdown(): Send a SHUTDOWN msg to peer and disconnect
+ * tipc_port_shutdown(): Send a SHUTDOWN msg to peer and disconnect
*/
-int tipc_shutdown(u32 ref)
+int tipc_port_shutdown(u32 ref)
{
struct tipc_port *p_ptr;
struct sk_buff *buf = NULL;
@@ -859,13 +751,13 @@ int tipc_shutdown(u32 ref)
buf = port_build_peer_abort_msg(p_ptr, TIPC_CONN_SHUTDOWN);
tipc_port_unlock(p_ptr);
tipc_net_route_msg(buf);
- return tipc_disconnect(ref);
+ return tipc_port_disconnect(ref);
}
/**
- * tipc_port_recv_msg - receive message from lower layer and deliver to port user
+ * tipc_port_rcv - receive message from lower layer and deliver to port user
*/
-int tipc_port_recv_msg(struct sk_buff *buf)
+int tipc_port_rcv(struct sk_buff *buf)
{
struct tipc_port *p_ptr;
struct tipc_msg *msg = buf_msg(buf);
@@ -882,7 +774,7 @@ int tipc_port_recv_msg(struct sk_buff *buf)
/* validate destination & pass to port, otherwise reject message */
p_ptr = tipc_port_lock(destport);
if (likely(p_ptr)) {
- err = p_ptr->dispatcher(p_ptr, buf);
+ err = tipc_sk_rcv(&tipc_port_to_sock(p_ptr)->sk, buf);
tipc_port_unlock(p_ptr);
if (likely(!err))
return dsz;
@@ -894,43 +786,43 @@ int tipc_port_recv_msg(struct sk_buff *buf)
}
/*
- * tipc_port_recv_sections(): Concatenate and deliver sectioned
- * message for this node.
+ * tipc_port_iovec_rcv: Concatenate and deliver sectioned
+ * message for this node.
*/
-static int tipc_port_recv_sections(struct tipc_port *sender,
- struct iovec const *msg_sect,
- unsigned int len)
+static int tipc_port_iovec_rcv(struct tipc_port *sender,
+ struct iovec const *msg_sect,
+ unsigned int len)
{
struct sk_buff *buf;
int res;
res = tipc_msg_build(&sender->phdr, msg_sect, len, MAX_MSG_SIZE, &buf);
if (likely(buf))
- tipc_port_recv_msg(buf);
+ tipc_port_rcv(buf);
return res;
}
/**
* tipc_send - send message sections on connection
*/
-int tipc_send(u32 ref, struct iovec const *msg_sect, unsigned int len)
+int tipc_send(struct tipc_port *p_ptr,
+ struct iovec const *msg_sect,
+ unsigned int len)
{
- struct tipc_port *p_ptr;
u32 destnode;
int res;
- p_ptr = tipc_port_deref(ref);
- if (!p_ptr || !p_ptr->connected)
+ if (!p_ptr->connected)
return -EINVAL;
p_ptr->congested = 1;
if (!tipc_port_congested(p_ptr)) {
- destnode = port_peernode(p_ptr);
+ destnode = tipc_port_peernode(p_ptr);
if (likely(!in_own_node(destnode)))
- res = tipc_link_send_sections_fast(p_ptr, msg_sect,
- len, destnode);
+ res = tipc_link_iovec_xmit_fast(p_ptr, msg_sect, len,
+ destnode);
else
- res = tipc_port_recv_sections(p_ptr, msg_sect, len);
+ res = tipc_port_iovec_rcv(p_ptr, msg_sect, len);
if (likely(res != -ELINKCONG)) {
p_ptr->congested = 0;
@@ -939,7 +831,7 @@ int tipc_send(u32 ref, struct iovec const *msg_sect, unsigned int len)
return res;
}
}
- if (port_unreliable(p_ptr)) {
+ if (tipc_port_unreliable(p_ptr)) {
p_ptr->congested = 0;
return len;
}
@@ -949,17 +841,18 @@ int tipc_send(u32 ref, struct iovec const *msg_sect, unsigned int len)
/**
* tipc_send2name - send message sections to port name
*/
-int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain,
- struct iovec const *msg_sect, unsigned int len)
+int tipc_send2name(struct tipc_port *p_ptr,
+ struct tipc_name const *name,
+ unsigned int domain,
+ struct iovec const *msg_sect,
+ unsigned int len)
{
- struct tipc_port *p_ptr;
struct tipc_msg *msg;
u32 destnode = domain;
u32 destport;
int res;
- p_ptr = tipc_port_deref(ref);
- if (!p_ptr || p_ptr->connected)
+ if (p_ptr->connected)
return -EINVAL;
msg = &p_ptr->phdr;
@@ -974,39 +867,39 @@ int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain,
if (likely(destport || destnode)) {
if (likely(in_own_node(destnode)))
- res = tipc_port_recv_sections(p_ptr, msg_sect, len);
+ res = tipc_port_iovec_rcv(p_ptr, msg_sect, len);
else if (tipc_own_addr)
- res = tipc_link_send_sections_fast(p_ptr, msg_sect,
- len, destnode);
+ res = tipc_link_iovec_xmit_fast(p_ptr, msg_sect, len,
+ destnode);
else
- res = tipc_port_reject_sections(p_ptr, msg, msg_sect,
- len, TIPC_ERR_NO_NODE);
+ res = tipc_port_iovec_reject(p_ptr, msg, msg_sect,
+ len, TIPC_ERR_NO_NODE);
if (likely(res != -ELINKCONG)) {
if (res > 0)
p_ptr->sent++;
return res;
}
- if (port_unreliable(p_ptr)) {
+ if (tipc_port_unreliable(p_ptr))
return len;
- }
+
return -ELINKCONG;
}
- return tipc_port_reject_sections(p_ptr, msg, msg_sect, len,
- TIPC_ERR_NO_NAME);
+ return tipc_port_iovec_reject(p_ptr, msg, msg_sect, len,
+ TIPC_ERR_NO_NAME);
}
/**
* tipc_send2port - send message sections to port identity
*/
-int tipc_send2port(u32 ref, struct tipc_portid const *dest,
- struct iovec const *msg_sect, unsigned int len)
+int tipc_send2port(struct tipc_port *p_ptr,
+ struct tipc_portid const *dest,
+ struct iovec const *msg_sect,
+ unsigned int len)
{
- struct tipc_port *p_ptr;
struct tipc_msg *msg;
int res;
- p_ptr = tipc_port_deref(ref);
- if (!p_ptr || p_ptr->connected)
+ if (p_ptr->connected)
return -EINVAL;
msg = &p_ptr->phdr;
@@ -1017,20 +910,20 @@ int tipc_send2port(u32 ref, struct tipc_portid const *dest,
msg_set_hdr_sz(msg, BASIC_H_SIZE);
if (in_own_node(dest->node))
- res = tipc_port_recv_sections(p_ptr, msg_sect, len);
+ res = tipc_port_iovec_rcv(p_ptr, msg_sect, len);
else if (tipc_own_addr)
- res = tipc_link_send_sections_fast(p_ptr, msg_sect, len,
- dest->node);
+ res = tipc_link_iovec_xmit_fast(p_ptr, msg_sect, len,
+ dest->node);
else
- res = tipc_port_reject_sections(p_ptr, msg, msg_sect, len,
+ res = tipc_port_iovec_reject(p_ptr, msg, msg_sect, len,
TIPC_ERR_NO_NODE);
if (likely(res != -ELINKCONG)) {
if (res > 0)
p_ptr->sent++;
return res;
}
- if (port_unreliable(p_ptr)) {
+ if (tipc_port_unreliable(p_ptr))
return len;
- }
+
return -ELINKCONG;
}
diff --git a/net/tipc/port.h b/net/tipc/port.h
index 34f12bd4074e..a00397393bd1 100644
--- a/net/tipc/port.h
+++ b/net/tipc/port.h
@@ -1,7 +1,7 @@
/*
* net/tipc/port.h: Include file for TIPC port code
*
- * Copyright (c) 1994-2007, Ericsson AB
+ * Copyright (c) 1994-2007, 2014, Ericsson AB
* Copyright (c) 2004-2007, 2010-2013, Wind River Systems
* All rights reserved.
*
@@ -48,7 +48,6 @@
/**
* struct tipc_port - TIPC port structure
- * @sk: pointer to socket handle
* @lock: pointer to spinlock for controlling access to port
* @connected: non-zero if port is currently connected to a peer port
* @conn_type: TIPC type used when connection was established
@@ -60,8 +59,6 @@
* @ref: unique reference to port in TIPC object registry
* @phdr: preformatted message header used when sending messages
* @port_list: adjacent ports in TIPC's global list of ports
- * @dispatcher: ptr to routine which handles received messages
- * @wakeup: ptr to routine to call when port is no longer congested
* @wait_list: adjacent ports in list of ports waiting on link congestion
* @waiting_pkts:
* @sent: # of non-empty messages sent by port
@@ -74,7 +71,6 @@
* @subscription: "node down" subscription used to terminate failed connections
*/
struct tipc_port {
- struct sock *sk;
spinlock_t *lock;
int connected;
u32 conn_type;
@@ -86,8 +82,6 @@ struct tipc_port {
u32 ref;
struct tipc_msg phdr;
struct list_head port_list;
- u32 (*dispatcher)(struct tipc_port *, struct sk_buff *);
- void (*wakeup)(struct tipc_port *);
struct list_head wait_list;
u32 waiting_pkts;
u32 sent;
@@ -106,68 +100,71 @@ struct tipc_port_list;
/*
* TIPC port manipulation routines
*/
-struct tipc_port *tipc_createport(struct sock *sk,
- u32 (*dispatcher)(struct tipc_port *,
- struct sk_buff *),
- void (*wakeup)(struct tipc_port *),
- const u32 importance);
+u32 tipc_port_init(struct tipc_port *p_ptr,
+ const unsigned int importance);
int tipc_reject_msg(struct sk_buff *buf, u32 err);
void tipc_acknowledge(u32 port_ref, u32 ack);
-int tipc_deleteport(struct tipc_port *p_ptr);
-
-int tipc_portimportance(u32 portref, unsigned int *importance);
-int tipc_set_portimportance(u32 portref, unsigned int importance);
-
-int tipc_portunreliable(u32 portref, unsigned int *isunreliable);
-int tipc_set_portunreliable(u32 portref, unsigned int isunreliable);
-
-int tipc_portunreturnable(u32 portref, unsigned int *isunreturnable);
-int tipc_set_portunreturnable(u32 portref, unsigned int isunreturnable);
+void tipc_port_destroy(struct tipc_port *p_ptr);
int tipc_publish(struct tipc_port *p_ptr, unsigned int scope,
struct tipc_name_seq const *name_seq);
+
int tipc_withdraw(struct tipc_port *p_ptr, unsigned int scope,
struct tipc_name_seq const *name_seq);
-int tipc_connect(u32 portref, struct tipc_portid const *port);
+int tipc_port_connect(u32 portref, struct tipc_portid const *port);
-int tipc_disconnect(u32 portref);
+int tipc_port_disconnect(u32 portref);
-int tipc_shutdown(u32 ref);
+int tipc_port_shutdown(u32 ref);
+void tipc_port_wakeup(struct tipc_port *port);
/*
* The following routines require that the port be locked on entry
*/
-int __tipc_disconnect(struct tipc_port *tp_ptr);
-int __tipc_connect(u32 ref, struct tipc_port *p_ptr,
+int __tipc_port_disconnect(struct tipc_port *tp_ptr);
+int __tipc_port_connect(u32 ref, struct tipc_port *p_ptr,
struct tipc_portid const *peer);
int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg);
/*
* TIPC messaging routines
*/
-int tipc_port_recv_msg(struct sk_buff *buf);
-int tipc_send(u32 portref, struct iovec const *msg_sect, unsigned int len);
-
-int tipc_send2name(u32 portref, struct tipc_name const *name, u32 domain,
- struct iovec const *msg_sect, unsigned int len);
+int tipc_port_rcv(struct sk_buff *buf);
+
+int tipc_send(struct tipc_port *port,
+ struct iovec const *msg_sect,
+ unsigned int len);
+
+int tipc_send2name(struct tipc_port *port,
+ struct tipc_name const *name,
+ u32 domain,
+ struct iovec const *msg_sect,
+ unsigned int len);
+
+int tipc_send2port(struct tipc_port *port,
+ struct tipc_portid const *dest,
+ struct iovec const *msg_sect,
+ unsigned int len);
+
+int tipc_port_mcast_xmit(struct tipc_port *port,
+ struct tipc_name_seq const *seq,
+ struct iovec const *msg,
+ unsigned int len);
+
+int tipc_port_iovec_reject(struct tipc_port *p_ptr,
+ struct tipc_msg *hdr,
+ struct iovec const *msg_sect,
+ unsigned int len,
+ int err);
-int tipc_send2port(u32 portref, struct tipc_portid const *dest,
- struct iovec const *msg_sect, unsigned int len);
-
-int tipc_multicast(u32 portref, struct tipc_name_seq const *seq,
- struct iovec const *msg, unsigned int len);
-
-int tipc_port_reject_sections(struct tipc_port *p_ptr, struct tipc_msg *hdr,
- struct iovec const *msg_sect, unsigned int len,
- int err);
struct sk_buff *tipc_port_get_ports(void);
-void tipc_port_recv_proto_msg(struct sk_buff *buf);
-void tipc_port_recv_mcast(struct sk_buff *buf, struct tipc_port_list *dp);
+void tipc_port_proto_rcv(struct sk_buff *buf);
+void tipc_port_mcast_rcv(struct sk_buff *buf, struct tipc_port_list *dp);
void tipc_port_reinit(void);
/**
@@ -188,14 +185,53 @@ static inline void tipc_port_unlock(struct tipc_port *p_ptr)
spin_unlock_bh(p_ptr->lock);
}
-static inline struct tipc_port *tipc_port_deref(u32 ref)
+static inline int tipc_port_congested(struct tipc_port *p_ptr)
{
- return (struct tipc_port *)tipc_ref_deref(ref);
+ return (p_ptr->sent - p_ptr->acked) >= (TIPC_FLOW_CONTROL_WIN * 2);
}
-static inline int tipc_port_congested(struct tipc_port *p_ptr)
+
+static inline u32 tipc_port_peernode(struct tipc_port *p_ptr)
{
- return (p_ptr->sent - p_ptr->acked) >= (TIPC_FLOW_CONTROL_WIN * 2);
+ return msg_destnode(&p_ptr->phdr);
+}
+
+static inline u32 tipc_port_peerport(struct tipc_port *p_ptr)
+{
+ return msg_destport(&p_ptr->phdr);
+}
+
+static inline bool tipc_port_unreliable(struct tipc_port *port)
+{
+ return msg_src_droppable(&port->phdr) != 0;
+}
+
+static inline void tipc_port_set_unreliable(struct tipc_port *port,
+ bool unreliable)
+{
+ msg_set_src_droppable(&port->phdr, unreliable ? 1 : 0);
+}
+
+static inline bool tipc_port_unreturnable(struct tipc_port *port)
+{
+ return msg_dest_droppable(&port->phdr) != 0;
+}
+
+static inline void tipc_port_set_unreturnable(struct tipc_port *port,
+ bool unreturnable)
+{
+ msg_set_dest_droppable(&port->phdr, unreturnable ? 1 : 0);
+}
+
+
+static inline int tipc_port_importance(struct tipc_port *port)
+{
+ return msg_importance(&port->phdr);
+}
+
+static inline void tipc_port_set_importance(struct tipc_port *port, int imp)
+{
+ msg_set_importance(&port->phdr, (u32)imp);
}
#endif
diff --git a/net/tipc/ref.c b/net/tipc/ref.c
index de3d593e2fee..3d4ecd754eee 100644
--- a/net/tipc/ref.c
+++ b/net/tipc/ref.c
@@ -89,7 +89,7 @@ struct ref_table {
static struct ref_table tipc_ref_table;
-static DEFINE_RWLOCK(ref_table_lock);
+static DEFINE_SPINLOCK(ref_table_lock);
/**
* tipc_ref_table_init - create reference table for objects
@@ -159,7 +159,7 @@ u32 tipc_ref_acquire(void *object, spinlock_t **lock)
}
/* take a free entry, if available; otherwise initialize a new entry */
- write_lock_bh(&ref_table_lock);
+ spin_lock_bh(&ref_table_lock);
if (tipc_ref_table.first_free) {
index = tipc_ref_table.first_free;
entry = &(tipc_ref_table.entries[index]);
@@ -175,7 +175,7 @@ u32 tipc_ref_acquire(void *object, spinlock_t **lock)
} else {
ref = 0;
}
- write_unlock_bh(&ref_table_lock);
+ spin_unlock_bh(&ref_table_lock);
/*
* Grab the lock so no one else can modify this entry
@@ -216,7 +216,7 @@ void tipc_ref_discard(u32 ref)
index = ref & index_mask;
entry = &(tipc_ref_table.entries[index]);
- write_lock_bh(&ref_table_lock);
+ spin_lock_bh(&ref_table_lock);
if (!entry->object) {
pr_err("Attempt to discard ref. to non-existent obj\n");
@@ -242,7 +242,7 @@ void tipc_ref_discard(u32 ref)
tipc_ref_table.last_free = index;
exit:
- write_unlock_bh(&ref_table_lock);
+ spin_unlock_bh(&ref_table_lock);
}
/**
@@ -264,20 +264,3 @@ void *tipc_ref_lock(u32 ref)
}
return NULL;
}
-
-
-/**
- * tipc_ref_deref - return pointer referenced object (without locking it)
- */
-void *tipc_ref_deref(u32 ref)
-{
- if (likely(tipc_ref_table.entries)) {
- struct reference *entry;
-
- entry = &tipc_ref_table.entries[ref &
- tipc_ref_table.index_mask];
- if (likely(entry->ref == ref))
- return entry->object;
- }
- return NULL;
-}
diff --git a/net/tipc/ref.h b/net/tipc/ref.h
index 5bc8e7ab84de..d01aa1df63b8 100644
--- a/net/tipc/ref.h
+++ b/net/tipc/ref.h
@@ -44,6 +44,5 @@ u32 tipc_ref_acquire(void *object, spinlock_t **lock);
void tipc_ref_discard(u32 ref);
void *tipc_ref_lock(u32 ref);
-void *tipc_ref_deref(u32 ref);
#endif
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 0ed0eaa62f29..29b7f26a12cf 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -1,7 +1,7 @@
/*
* net/tipc/socket.c: TIPC socket API
*
- * Copyright (c) 2001-2007, 2012 Ericsson AB
+ * Copyright (c) 2001-2007, 2012-2014, Ericsson AB
* Copyright (c) 2004-2008, 2010-2013, Wind River Systems
* All rights reserved.
*
@@ -38,30 +38,17 @@
#include "port.h"
#include <linux/export.h>
-#include <net/sock.h>
#define SS_LISTENING -1 /* socket is listening */
#define SS_READY -2 /* socket is connectionless */
#define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */
-struct tipc_sock {
- struct sock sk;
- struct tipc_port *p;
- struct tipc_portid peer_name;
- unsigned int conn_timeout;
-};
-
-#define tipc_sk(sk) ((struct tipc_sock *)(sk))
-#define tipc_sk_port(sk) (tipc_sk(sk)->p)
-
static int backlog_rcv(struct sock *sk, struct sk_buff *skb);
-static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf);
-static void wakeupdispatch(struct tipc_port *tport);
static void tipc_data_ready(struct sock *sk, int len);
static void tipc_write_space(struct sock *sk);
-static int release(struct socket *sock);
-static int accept(struct socket *sock, struct socket *new_sock, int flags);
+static int tipc_release(struct socket *sock);
+static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags);
static const struct proto_ops packet_ops;
static const struct proto_ops stream_ops;
@@ -115,6 +102,8 @@ static struct proto tipc_proto_kern;
* - port reference
*/
+#include "socket.h"
+
/**
* advance_rx_queue - discard first buffer in socket receive queue
*
@@ -150,13 +139,15 @@ static void reject_rx_queue(struct sock *sk)
*
* Returns 0 on success, errno otherwise
*/
-static int tipc_sk_create(struct net *net, struct socket *sock, int protocol,
- int kern)
+static int tipc_sk_create(struct net *net, struct socket *sock,
+ int protocol, int kern)
{
const struct proto_ops *ops;
socket_state state;
struct sock *sk;
- struct tipc_port *tp_ptr;
+ struct tipc_sock *tsk;
+ struct tipc_port *port;
+ u32 ref;
/* Validate arguments */
if (unlikely(protocol != 0))
@@ -189,10 +180,12 @@ static int tipc_sk_create(struct net *net, struct socket *sock, int protocol,
if (sk == NULL)
return -ENOMEM;
- /* Allocate TIPC port for socket to use */
- tp_ptr = tipc_createport(sk, &dispatch, &wakeupdispatch,
- TIPC_LOW_IMPORTANCE);
- if (unlikely(!tp_ptr)) {
+ tsk = tipc_sk(sk);
+ port = &tsk->port;
+
+ ref = tipc_port_init(port, TIPC_LOW_IMPORTANCE);
+ if (!ref) {
+ pr_warn("Socket registration failed, ref. table exhausted\n");
sk_free(sk);
return -ENOMEM;
}
@@ -206,17 +199,14 @@ static int tipc_sk_create(struct net *net, struct socket *sock, int protocol,
sk->sk_rcvbuf = sysctl_tipc_rmem[1];
sk->sk_data_ready = tipc_data_ready;
sk->sk_write_space = tipc_write_space;
- tipc_sk(sk)->p = tp_ptr;
tipc_sk(sk)->conn_timeout = CONN_TIMEOUT_DEFAULT;
-
- spin_unlock_bh(tp_ptr->lock);
+ tipc_port_unlock(port);
if (sock->state == SS_READY) {
- tipc_set_portunreturnable(tp_ptr->ref, 1);
+ tipc_port_set_unreturnable(port, true);
if (sock->type == SOCK_DGRAM)
- tipc_set_portunreliable(tp_ptr->ref, 1);
+ tipc_port_set_unreliable(port, true);
}
-
return 0;
}
@@ -254,7 +244,7 @@ int tipc_sock_create_local(int type, struct socket **res)
*/
void tipc_sock_release_local(struct socket *sock)
{
- release(sock);
+ tipc_release(sock);
sock->ops = NULL;
sock_release(sock);
}
@@ -280,7 +270,7 @@ int tipc_sock_accept_local(struct socket *sock, struct socket **newsock,
if (ret < 0)
return ret;
- ret = accept(sock, *newsock, flags);
+ ret = tipc_accept(sock, *newsock, flags);
if (ret < 0) {
sock_release(*newsock);
return ret;
@@ -290,7 +280,7 @@ int tipc_sock_accept_local(struct socket *sock, struct socket **newsock,
}
/**
- * release - destroy a TIPC socket
+ * tipc_release - destroy a TIPC socket
* @sock: socket to destroy
*
* This routine cleans up any messages that are still queued on the socket.
@@ -305,10 +295,11 @@ int tipc_sock_accept_local(struct socket *sock, struct socket **newsock,
*
* Returns 0 on success, errno otherwise
*/
-static int release(struct socket *sock)
+static int tipc_release(struct socket *sock)
{
struct sock *sk = sock->sk;
- struct tipc_port *tport;
+ struct tipc_sock *tsk;
+ struct tipc_port *port;
struct sk_buff *buf;
int res;
@@ -319,7 +310,8 @@ static int release(struct socket *sock)
if (sk == NULL)
return 0;
- tport = tipc_sk_port(sk);
+ tsk = tipc_sk(sk);
+ port = &tsk->port;
lock_sock(sk);
/*
@@ -336,17 +328,16 @@ static int release(struct socket *sock)
if ((sock->state == SS_CONNECTING) ||
(sock->state == SS_CONNECTED)) {
sock->state = SS_DISCONNECTING;
- tipc_disconnect(tport->ref);
+ tipc_port_disconnect(port->ref);
}
tipc_reject_msg(buf, TIPC_ERR_NO_PORT);
}
}
- /*
- * Delete TIPC port; this ensures no more messages are queued
- * (also disconnects an active connection & sends a 'FIN-' to peer)
+ /* Destroy TIPC port; also disconnects an active connection and
+ * sends a 'FIN-' to peer.
*/
- res = tipc_deleteport(tport);
+ tipc_port_destroy(port);
/* Discard any remaining (connection-based) messages in receive queue */
__skb_queue_purge(&sk->sk_receive_queue);
@@ -362,7 +353,7 @@ static int release(struct socket *sock)
}
/**
- * bind - associate or disassocate TIPC name(s) with a socket
+ * tipc_bind - associate or disassocate TIPC name(s) with a socket
* @sock: socket structure
* @uaddr: socket address describing name(s) and desired operation
* @uaddr_len: size of socket address data structure
@@ -376,16 +367,17 @@ static int release(struct socket *sock)
* NOTE: This routine doesn't need to take the socket lock since it doesn't
* access any non-constant socket information.
*/
-static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len)
+static int tipc_bind(struct socket *sock, struct sockaddr *uaddr,
+ int uaddr_len)
{
struct sock *sk = sock->sk;
struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr;
- struct tipc_port *tport = tipc_sk_port(sock->sk);
+ struct tipc_sock *tsk = tipc_sk(sk);
int res = -EINVAL;
lock_sock(sk);
if (unlikely(!uaddr_len)) {
- res = tipc_withdraw(tport, 0, NULL);
+ res = tipc_withdraw(&tsk->port, 0, NULL);
goto exit;
}
@@ -413,15 +405,15 @@ static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len)
}
res = (addr->scope > 0) ?
- tipc_publish(tport, addr->scope, &addr->addr.nameseq) :
- tipc_withdraw(tport, -addr->scope, &addr->addr.nameseq);
+ tipc_publish(&tsk->port, addr->scope, &addr->addr.nameseq) :
+ tipc_withdraw(&tsk->port, -addr->scope, &addr->addr.nameseq);
exit:
release_sock(sk);
return res;
}
/**
- * get_name - get port ID of socket or peer socket
+ * tipc_getname - get port ID of socket or peer socket
* @sock: socket structure
* @uaddr: area for returned socket address
* @uaddr_len: area for returned length of socket address
@@ -433,21 +425,21 @@ exit:
* accesses socket information that is unchanging (or which changes in
* a completely predictable manner).
*/
-static int get_name(struct socket *sock, struct sockaddr *uaddr,
- int *uaddr_len, int peer)
+static int tipc_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *uaddr_len, int peer)
{
struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr;
- struct tipc_sock *tsock = tipc_sk(sock->sk);
+ struct tipc_sock *tsk = tipc_sk(sock->sk);
memset(addr, 0, sizeof(*addr));
if (peer) {
if ((sock->state != SS_CONNECTED) &&
((peer != 2) || (sock->state != SS_DISCONNECTING)))
return -ENOTCONN;
- addr->addr.id.ref = tsock->peer_name.ref;
- addr->addr.id.node = tsock->peer_name.node;
+ addr->addr.id.ref = tipc_port_peerport(&tsk->port);
+ addr->addr.id.node = tipc_port_peernode(&tsk->port);
} else {
- addr->addr.id.ref = tsock->p->ref;
+ addr->addr.id.ref = tsk->port.ref;
addr->addr.id.node = tipc_own_addr;
}
@@ -461,7 +453,7 @@ static int get_name(struct socket *sock, struct sockaddr *uaddr,
}
/**
- * poll - read and possibly block on pollmask
+ * tipc_poll - read and possibly block on pollmask
* @file: file structure associated with the socket
* @sock: socket for which to calculate the poll bits
* @wait: ???
@@ -500,22 +492,23 @@ static int get_name(struct socket *sock, struct sockaddr *uaddr,
* imply that the operation will succeed, merely that it should be performed
* and will not block.
*/
-static unsigned int poll(struct file *file, struct socket *sock,
- poll_table *wait)
+static unsigned int tipc_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
{
struct sock *sk = sock->sk;
+ struct tipc_sock *tsk = tipc_sk(sk);
u32 mask = 0;
sock_poll_wait(file, sk_sleep(sk), wait);
switch ((int)sock->state) {
case SS_UNCONNECTED:
- if (!tipc_sk_port(sk)->congested)
+ if (!tsk->port.congested)
mask |= POLLOUT;
break;
case SS_READY:
case SS_CONNECTED:
- if (!tipc_sk_port(sk)->congested)
+ if (!tsk->port.congested)
mask |= POLLOUT;
/* fall thru' */
case SS_CONNECTING:
@@ -565,7 +558,7 @@ static int dest_name_check(struct sockaddr_tipc *dest, struct msghdr *m)
static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p)
{
struct sock *sk = sock->sk;
- struct tipc_port *tport = tipc_sk_port(sk);
+ struct tipc_sock *tsk = tipc_sk(sk);
DEFINE_WAIT(wait);
int done;
@@ -581,14 +574,15 @@ static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p)
return sock_intr_errno(*timeo_p);
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
- done = sk_wait_event(sk, timeo_p, !tport->congested);
+ done = sk_wait_event(sk, timeo_p, !tsk->port.congested);
finish_wait(sk_sleep(sk), &wait);
} while (!done);
return 0;
}
+
/**
- * send_msg - send message in connectionless manner
+ * tipc_sendmsg - send message in connectionless manner
* @iocb: if NULL, indicates that socket lock is already held
* @sock: socket structure
* @m: message to send
@@ -601,11 +595,12 @@ static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p)
*
* Returns the number of bytes sent on success, or errno otherwise
*/
-static int send_msg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *m, size_t total_len)
+static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len)
{
struct sock *sk = sock->sk;
- struct tipc_port *tport = tipc_sk_port(sk);
+ struct tipc_sock *tsk = tipc_sk(sk);
+ struct tipc_port *port = &tsk->port;
DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);
int needs_conn;
long timeo;
@@ -632,13 +627,13 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,
res = -EISCONN;
goto exit;
}
- if (tport->published) {
+ if (tsk->port.published) {
res = -EOPNOTSUPP;
goto exit;
}
if (dest->addrtype == TIPC_ADDR_NAME) {
- tport->conn_type = dest->addr.name.name.type;
- tport->conn_instance = dest->addr.name.name.instance;
+ tsk->port.conn_type = dest->addr.name.name.type;
+ tsk->port.conn_instance = dest->addr.name.name.instance;
}
/* Abort any pending connection attempts (very unlikely) */
@@ -651,13 +646,13 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,
res = dest_name_check(dest, m);
if (res)
break;
- res = tipc_send2name(tport->ref,
+ res = tipc_send2name(port,
&dest->addr.name.name,
dest->addr.name.domain,
m->msg_iov,
total_len);
} else if (dest->addrtype == TIPC_ADDR_ID) {
- res = tipc_send2port(tport->ref,
+ res = tipc_send2port(port,
&dest->addr.id,
m->msg_iov,
total_len);
@@ -669,10 +664,10 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,
res = dest_name_check(dest, m);
if (res)
break;
- res = tipc_multicast(tport->ref,
- &dest->addr.nameseq,
- m->msg_iov,
- total_len);
+ res = tipc_port_mcast_xmit(port,
+ &dest->addr.nameseq,
+ m->msg_iov,
+ total_len);
}
if (likely(res != -ELINKCONG)) {
if (needs_conn && (res >= 0))
@@ -693,7 +688,8 @@ exit:
static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p)
{
struct sock *sk = sock->sk;
- struct tipc_port *tport = tipc_sk_port(sk);
+ struct tipc_sock *tsk = tipc_sk(sk);
+ struct tipc_port *port = &tsk->port;
DEFINE_WAIT(wait);
int done;
@@ -712,14 +708,14 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p)
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
done = sk_wait_event(sk, timeo_p,
- (!tport->congested || !tport->connected));
+ (!port->congested || !port->connected));
finish_wait(sk_sleep(sk), &wait);
} while (!done);
return 0;
}
/**
- * send_packet - send a connection-oriented message
+ * tipc_send_packet - send a connection-oriented message
* @iocb: if NULL, indicates that socket lock is already held
* @sock: socket structure
* @m: message to send
@@ -729,18 +725,18 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p)
*
* Returns the number of bytes sent on success, or errno otherwise
*/
-static int send_packet(struct kiocb *iocb, struct socket *sock,
- struct msghdr *m, size_t total_len)
+static int tipc_send_packet(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len)
{
struct sock *sk = sock->sk;
- struct tipc_port *tport = tipc_sk_port(sk);
+ struct tipc_sock *tsk = tipc_sk(sk);
DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);
int res = -EINVAL;
long timeo;
/* Handle implied connection establishment */
if (unlikely(dest))
- return send_msg(iocb, sock, m, total_len);
+ return tipc_sendmsg(iocb, sock, m, total_len);
if (total_len > TIPC_MAX_USER_MSG_SIZE)
return -EMSGSIZE;
@@ -758,7 +754,7 @@ static int send_packet(struct kiocb *iocb, struct socket *sock,
timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
do {
- res = tipc_send(tport->ref, m->msg_iov, total_len);
+ res = tipc_send(&tsk->port, m->msg_iov, total_len);
if (likely(res != -ELINKCONG))
break;
res = tipc_wait_for_sndpkt(sock, &timeo);
@@ -772,7 +768,7 @@ exit:
}
/**
- * send_stream - send stream-oriented data
+ * tipc_send_stream - send stream-oriented data
* @iocb: (unused)
* @sock: socket structure
* @m: data to send
@@ -783,11 +779,11 @@ exit:
* Returns the number of bytes sent on success (or partial success),
* or errno if no data sent
*/
-static int send_stream(struct kiocb *iocb, struct socket *sock,
- struct msghdr *m, size_t total_len)
+static int tipc_send_stream(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len)
{
struct sock *sk = sock->sk;
- struct tipc_port *tport = tipc_sk_port(sk);
+ struct tipc_sock *tsk = tipc_sk(sk);
struct msghdr my_msg;
struct iovec my_iov;
struct iovec *curr_iov;
@@ -804,7 +800,7 @@ static int send_stream(struct kiocb *iocb, struct socket *sock,
/* Handle special cases where there is no connection */
if (unlikely(sock->state != SS_CONNECTED)) {
if (sock->state == SS_UNCONNECTED)
- res = send_packet(NULL, sock, m, total_len);
+ res = tipc_send_packet(NULL, sock, m, total_len);
else
res = sock->state == SS_DISCONNECTING ? -EPIPE : -ENOTCONN;
goto exit;
@@ -835,21 +831,22 @@ static int send_stream(struct kiocb *iocb, struct socket *sock,
my_msg.msg_name = NULL;
bytes_sent = 0;
- hdr_size = msg_hdr_sz(&tport->phdr);
+ hdr_size = msg_hdr_sz(&tsk->port.phdr);
while (curr_iovlen--) {
curr_start = curr_iov->iov_base;
curr_left = curr_iov->iov_len;
while (curr_left) {
- bytes_to_send = tport->max_pkt - hdr_size;
+ bytes_to_send = tsk->port.max_pkt - hdr_size;
if (bytes_to_send > TIPC_MAX_USER_MSG_SIZE)
bytes_to_send = TIPC_MAX_USER_MSG_SIZE;
if (curr_left < bytes_to_send)
bytes_to_send = curr_left;
my_iov.iov_base = curr_start;
my_iov.iov_len = bytes_to_send;
- res = send_packet(NULL, sock, &my_msg, bytes_to_send);
+ res = tipc_send_packet(NULL, sock, &my_msg,
+ bytes_to_send);
if (res < 0) {
if (bytes_sent)
res = bytes_sent;
@@ -870,27 +867,25 @@ exit:
/**
* auto_connect - complete connection setup to a remote port
- * @sock: socket structure
+ * @tsk: tipc socket structure
* @msg: peer's response message
*
* Returns 0 on success, errno otherwise
*/
-static int auto_connect(struct socket *sock, struct tipc_msg *msg)
+static int auto_connect(struct tipc_sock *tsk, struct tipc_msg *msg)
{
- struct tipc_sock *tsock = tipc_sk(sock->sk);
- struct tipc_port *p_ptr;
+ struct tipc_port *port = &tsk->port;
+ struct socket *sock = tsk->sk.sk_socket;
+ struct tipc_portid peer;
- tsock->peer_name.ref = msg_origport(msg);
- tsock->peer_name.node = msg_orignode(msg);
- p_ptr = tipc_port_deref(tsock->p->ref);
- if (!p_ptr)
- return -EINVAL;
+ peer.ref = msg_origport(msg);
+ peer.node = msg_orignode(msg);
- __tipc_connect(tsock->p->ref, p_ptr, &tsock->peer_name);
+ __tipc_port_connect(port->ref, port, &peer);
if (msg_importance(msg) > TIPC_CRITICAL_IMPORTANCE)
return -EINVAL;
- msg_set_importance(&p_ptr->phdr, (u32)msg_importance(msg));
+ msg_set_importance(&port->phdr, (u32)msg_importance(msg));
sock->state = SS_CONNECTED;
return 0;
}
@@ -1021,7 +1016,7 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long timeo)
}
/**
- * recv_msg - receive packet-oriented message
+ * tipc_recvmsg - receive packet-oriented message
* @iocb: (unused)
* @m: descriptor for message info
* @buf_len: total size of user buffer area
@@ -1032,11 +1027,12 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long timeo)
*
* Returns size of returned message data, errno otherwise
*/
-static int recv_msg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *m, size_t buf_len, int flags)
+static int tipc_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t buf_len, int flags)
{
struct sock *sk = sock->sk;
- struct tipc_port *tport = tipc_sk_port(sk);
+ struct tipc_sock *tsk = tipc_sk(sk);
+ struct tipc_port *port = &tsk->port;
struct sk_buff *buf;
struct tipc_msg *msg;
long timeo;
@@ -1079,7 +1075,7 @@ restart:
set_orig_addr(m, msg);
/* Capture ancillary data (optional) */
- res = anc_data_recv(m, msg, tport);
+ res = anc_data_recv(m, msg, port);
if (res)
goto exit;
@@ -1105,8 +1101,8 @@ restart:
/* Consume received message (optional) */
if (likely(!(flags & MSG_PEEK))) {
if ((sock->state != SS_READY) &&
- (++tport->conn_unacked >= TIPC_FLOW_CONTROL_WIN))
- tipc_acknowledge(tport->ref, tport->conn_unacked);
+ (++port->conn_unacked >= TIPC_FLOW_CONTROL_WIN))
+ tipc_acknowledge(port->ref, port->conn_unacked);
advance_rx_queue(sk);
}
exit:
@@ -1115,7 +1111,7 @@ exit:
}
/**
- * recv_stream - receive stream-oriented data
+ * tipc_recv_stream - receive stream-oriented data
* @iocb: (unused)
* @m: descriptor for message info
* @buf_len: total size of user buffer area
@@ -1126,11 +1122,12 @@ exit:
*
* Returns size of returned message data, errno otherwise
*/
-static int recv_stream(struct kiocb *iocb, struct socket *sock,
- struct msghdr *m, size_t buf_len, int flags)
+static int tipc_recv_stream(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t buf_len, int flags)
{
struct sock *sk = sock->sk;
- struct tipc_port *tport = tipc_sk_port(sk);
+ struct tipc_sock *tsk = tipc_sk(sk);
+ struct tipc_port *port = &tsk->port;
struct sk_buff *buf;
struct tipc_msg *msg;
long timeo;
@@ -1175,7 +1172,7 @@ restart:
/* Optionally capture sender's address & ancillary data of first msg */
if (sz_copied == 0) {
set_orig_addr(m, msg);
- res = anc_data_recv(m, msg, tport);
+ res = anc_data_recv(m, msg, port);
if (res)
goto exit;
}
@@ -1213,8 +1210,8 @@ restart:
/* Consume received message (optional) */
if (likely(!(flags & MSG_PEEK))) {
- if (unlikely(++tport->conn_unacked >= TIPC_FLOW_CONTROL_WIN))
- tipc_acknowledge(tport->ref, tport->conn_unacked);
+ if (unlikely(++port->conn_unacked >= TIPC_FLOW_CONTROL_WIN))
+ tipc_acknowledge(port->ref, port->conn_unacked);
advance_rx_queue(sk);
}
@@ -1266,17 +1263,19 @@ static void tipc_data_ready(struct sock *sk, int len)
/**
* filter_connect - Handle all incoming messages for a connection-based socket
- * @tsock: TIPC socket
+ * @tsk: TIPC socket
* @msg: message
*
* Returns TIPC error status code and socket error status code
* once it encounters some errors
*/
-static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf)
+static u32 filter_connect(struct tipc_sock *tsk, struct sk_buff **buf)
{
- struct socket *sock = tsock->sk.sk_socket;
+ struct sock *sk = &tsk->sk;
+ struct tipc_port *port = &tsk->port;
+ struct socket *sock = sk->sk_socket;
struct tipc_msg *msg = buf_msg(*buf);
- struct sock *sk = &tsock->sk;
+
u32 retval = TIPC_ERR_NO_PORT;
int res;
@@ -1286,10 +1285,10 @@ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf)
switch ((int)sock->state) {
case SS_CONNECTED:
/* Accept only connection-based messages sent by peer */
- if (msg_connected(msg) && tipc_port_peer_msg(tsock->p, msg)) {
+ if (msg_connected(msg) && tipc_port_peer_msg(port, msg)) {
if (unlikely(msg_errcode(msg))) {
sock->state = SS_DISCONNECTING;
- __tipc_disconnect(tsock->p);
+ __tipc_port_disconnect(port);
}
retval = TIPC_OK;
}
@@ -1306,7 +1305,7 @@ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf)
if (unlikely(!msg_connected(msg)))
break;
- res = auto_connect(sock, msg);
+ res = auto_connect(tsk, msg);
if (res) {
sock->state = SS_DISCONNECTING;
sk->sk_err = -res;
@@ -1385,6 +1384,7 @@ static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *buf)
static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
{
struct socket *sock = sk->sk_socket;
+ struct tipc_sock *tsk = tipc_sk(sk);
struct tipc_msg *msg = buf_msg(buf);
unsigned int limit = rcvbuf_limit(sk, buf);
u32 res = TIPC_OK;
@@ -1397,7 +1397,7 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
if (msg_connected(msg))
return TIPC_ERR_NO_PORT;
} else {
- res = filter_connect(tipc_sk(sk), &buf);
+ res = filter_connect(tsk, &buf);
if (res != TIPC_OK || buf == NULL)
return res;
}
@@ -1435,17 +1435,16 @@ static int backlog_rcv(struct sock *sk, struct sk_buff *buf)
}
/**
- * dispatch - handle incoming message
- * @tport: TIPC port that received message
+ * tipc_sk_rcv - handle incoming message
+ * @sk: socket receiving message
* @buf: message
*
* Called with port lock already taken.
*
* Returns TIPC error status code (TIPC_OK if message is not to be rejected)
*/
-static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf)
+u32 tipc_sk_rcv(struct sock *sk, struct sk_buff *buf)
{
- struct sock *sk = tport->sk;
u32 res;
/*
@@ -1468,19 +1467,6 @@ static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf)
return res;
}
-/**
- * wakeupdispatch - wake up port after congestion
- * @tport: port to wakeup
- *
- * Called with port lock already taken.
- */
-static void wakeupdispatch(struct tipc_port *tport)
-{
- struct sock *sk = tport->sk;
-
- sk->sk_write_space(sk);
-}
-
static int tipc_wait_for_connect(struct socket *sock, long *timeo_p)
{
struct sock *sk = sock->sk;
@@ -1504,7 +1490,7 @@ static int tipc_wait_for_connect(struct socket *sock, long *timeo_p)
}
/**
- * connect - establish a connection to another TIPC port
+ * tipc_connect - establish a connection to another TIPC port
* @sock: socket structure
* @dest: socket address for destination port
* @destlen: size of socket address data structure
@@ -1512,8 +1498,8 @@ static int tipc_wait_for_connect(struct socket *sock, long *timeo_p)
*
* Returns 0 on success, errno otherwise
*/
-static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
- int flags)
+static int tipc_connect(struct socket *sock, struct sockaddr *dest,
+ int destlen, int flags)
{
struct sock *sk = sock->sk;
struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest;
@@ -1554,7 +1540,7 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
if (!timeout)
m.msg_flags = MSG_DONTWAIT;
- res = send_msg(NULL, sock, &m, 0);
+ res = tipc_sendmsg(NULL, sock, &m, 0);
if ((res < 0) && (res != -EWOULDBLOCK))
goto exit;
@@ -1585,13 +1571,13 @@ exit:
}
/**
- * listen - allow socket to listen for incoming connections
+ * tipc_listen - allow socket to listen for incoming connections
* @sock: socket structure
* @len: (unused)
*
* Returns 0 on success, errno otherwise
*/
-static int listen(struct socket *sock, int len)
+static int tipc_listen(struct socket *sock, int len)
{
struct sock *sk = sock->sk;
int res;
@@ -1646,20 +1632,20 @@ static int tipc_wait_for_accept(struct socket *sock, long timeo)
}
/**
- * accept - wait for connection request
+ * tipc_accept - wait for connection request
* @sock: listening socket
* @newsock: new socket that is to be connected
* @flags: file-related flags associated with socket
*
* Returns 0 on success, errno otherwise
*/
-static int accept(struct socket *sock, struct socket *new_sock, int flags)
+static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags)
{
struct sock *new_sk, *sk = sock->sk;
struct sk_buff *buf;
- struct tipc_sock *new_tsock;
- struct tipc_port *new_tport;
+ struct tipc_port *new_port;
struct tipc_msg *msg;
+ struct tipc_portid peer;
u32 new_ref;
long timeo;
int res;
@@ -1670,7 +1656,6 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags)
res = -EINVAL;
goto exit;
}
-
timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
res = tipc_wait_for_accept(sock, timeo);
if (res)
@@ -1683,9 +1668,8 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags)
goto exit;
new_sk = new_sock->sk;
- new_tsock = tipc_sk(new_sk);
- new_tport = new_tsock->p;
- new_ref = new_tport->ref;
+ new_port = &tipc_sk(new_sk)->port;
+ new_ref = new_port->ref;
msg = buf_msg(buf);
/* we lock on new_sk; but lockdep sees the lock on sk */
@@ -1698,15 +1682,15 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags)
reject_rx_queue(new_sk);
/* Connect new socket to it's peer */
- new_tsock->peer_name.ref = msg_origport(msg);
- new_tsock->peer_name.node = msg_orignode(msg);
- tipc_connect(new_ref, &new_tsock->peer_name);
+ peer.ref = msg_origport(msg);
+ peer.node = msg_orignode(msg);
+ tipc_port_connect(new_ref, &peer);
new_sock->state = SS_CONNECTED;
- tipc_set_portimportance(new_ref, msg_importance(msg));
+ tipc_port_set_importance(new_port, msg_importance(msg));
if (msg_named(msg)) {
- new_tport->conn_type = msg_nametype(msg);
- new_tport->conn_instance = msg_nameinst(msg);
+ new_port->conn_type = msg_nametype(msg);
+ new_port->conn_instance = msg_nameinst(msg);
}
/*
@@ -1717,21 +1701,20 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags)
struct msghdr m = {NULL,};
advance_rx_queue(sk);
- send_packet(NULL, new_sock, &m, 0);
+ tipc_send_packet(NULL, new_sock, &m, 0);
} else {
__skb_dequeue(&sk->sk_receive_queue);
__skb_queue_head(&new_sk->sk_receive_queue, buf);
skb_set_owner_r(buf, new_sk);
}
release_sock(new_sk);
-
exit:
release_sock(sk);
return res;
}
/**
- * shutdown - shutdown socket connection
+ * tipc_shutdown - shutdown socket connection
* @sock: socket structure
* @how: direction to close (must be SHUT_RDWR)
*
@@ -1739,10 +1722,11 @@ exit:
*
* Returns 0 on success, errno otherwise
*/
-static int shutdown(struct socket *sock, int how)
+static int tipc_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
- struct tipc_port *tport = tipc_sk_port(sk);
+ struct tipc_sock *tsk = tipc_sk(sk);
+ struct tipc_port *port = &tsk->port;
struct sk_buff *buf;
int res;
@@ -1763,10 +1747,10 @@ restart:
kfree_skb(buf);
goto restart;
}
- tipc_disconnect(tport->ref);
+ tipc_port_disconnect(port->ref);
tipc_reject_msg(buf, TIPC_CONN_SHUTDOWN);
} else {
- tipc_shutdown(tport->ref);
+ tipc_port_shutdown(port->ref);
}
sock->state = SS_DISCONNECTING;
@@ -1792,7 +1776,7 @@ restart:
}
/**
- * setsockopt - set socket option
+ * tipc_setsockopt - set socket option
* @sock: socket structure
* @lvl: option level
* @opt: option identifier
@@ -1804,11 +1788,12 @@ restart:
*
* Returns 0 on success, errno otherwise
*/
-static int setsockopt(struct socket *sock, int lvl, int opt, char __user *ov,
- unsigned int ol)
+static int tipc_setsockopt(struct socket *sock, int lvl, int opt,
+ char __user *ov, unsigned int ol)
{
struct sock *sk = sock->sk;
- struct tipc_port *tport = tipc_sk_port(sk);
+ struct tipc_sock *tsk = tipc_sk(sk);
+ struct tipc_port *port = &tsk->port;
u32 value;
int res;
@@ -1826,16 +1811,16 @@ static int setsockopt(struct socket *sock, int lvl, int opt, char __user *ov,
switch (opt) {
case TIPC_IMPORTANCE:
- res = tipc_set_portimportance(tport->ref, value);
+ tipc_port_set_importance(port, value);
break;
case TIPC_SRC_DROPPABLE:
if (sock->type != SOCK_STREAM)
- res = tipc_set_portunreliable(tport->ref, value);
+ tipc_port_set_unreliable(port, value);
else
res = -ENOPROTOOPT;
break;
case TIPC_DEST_DROPPABLE:
- res = tipc_set_portunreturnable(tport->ref, value);
+ tipc_port_set_unreturnable(port, value);
break;
case TIPC_CONN_TIMEOUT:
tipc_sk(sk)->conn_timeout = value;
@@ -1851,7 +1836,7 @@ static int setsockopt(struct socket *sock, int lvl, int opt, char __user *ov,
}
/**
- * getsockopt - get socket option
+ * tipc_getsockopt - get socket option
* @sock: socket structure
* @lvl: option level
* @opt: option identifier
@@ -1863,11 +1848,12 @@ static int setsockopt(struct socket *sock, int lvl, int opt, char __user *ov,
*
* Returns 0 on success, errno otherwise
*/
-static int getsockopt(struct socket *sock, int lvl, int opt, char __user *ov,
- int __user *ol)
+static int tipc_getsockopt(struct socket *sock, int lvl, int opt,
+ char __user *ov, int __user *ol)
{
struct sock *sk = sock->sk;
- struct tipc_port *tport = tipc_sk_port(sk);
+ struct tipc_sock *tsk = tipc_sk(sk);
+ struct tipc_port *port = &tsk->port;
int len;
u32 value;
int res;
@@ -1884,13 +1870,13 @@ static int getsockopt(struct socket *sock, int lvl, int opt, char __user *ov,
switch (opt) {
case TIPC_IMPORTANCE:
- res = tipc_portimportance(tport->ref, &value);
+ value = tipc_port_importance(port);
break;
case TIPC_SRC_DROPPABLE:
- res = tipc_portunreliable(tport->ref, &value);
+ value = tipc_port_unreliable(port);
break;
case TIPC_DEST_DROPPABLE:
- res = tipc_portunreturnable(tport->ref, &value);
+ value = tipc_port_unreturnable(port);
break;
case TIPC_CONN_TIMEOUT:
value = tipc_sk(sk)->conn_timeout;
@@ -1925,20 +1911,20 @@ static int getsockopt(struct socket *sock, int lvl, int opt, char __user *ov,
static const struct proto_ops msg_ops = {
.owner = THIS_MODULE,
.family = AF_TIPC,
- .release = release,
- .bind = bind,
- .connect = connect,
+ .release = tipc_release,
+ .bind = tipc_bind,
+ .connect = tipc_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
- .getname = get_name,
- .poll = poll,
+ .getname = tipc_getname,
+ .poll = tipc_poll,
.ioctl = sock_no_ioctl,
.listen = sock_no_listen,
- .shutdown = shutdown,
- .setsockopt = setsockopt,
- .getsockopt = getsockopt,
- .sendmsg = send_msg,
- .recvmsg = recv_msg,
+ .shutdown = tipc_shutdown,
+ .setsockopt = tipc_setsockopt,
+ .getsockopt = tipc_getsockopt,
+ .sendmsg = tipc_sendmsg,
+ .recvmsg = tipc_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage
};
@@ -1946,20 +1932,20 @@ static const struct proto_ops msg_ops = {
static const struct proto_ops packet_ops = {
.owner = THIS_MODULE,
.family = AF_TIPC,
- .release = release,
- .bind = bind,
- .connect = connect,
+ .release = tipc_release,
+ .bind = tipc_bind,
+ .connect = tipc_connect,
.socketpair = sock_no_socketpair,
- .accept = accept,
- .getname = get_name,
- .poll = poll,
+ .accept = tipc_accept,
+ .getname = tipc_getname,
+ .poll = tipc_poll,
.ioctl = sock_no_ioctl,
- .listen = listen,
- .shutdown = shutdown,
- .setsockopt = setsockopt,
- .getsockopt = getsockopt,
- .sendmsg = send_packet,
- .recvmsg = recv_msg,
+ .listen = tipc_listen,
+ .shutdown = tipc_shutdown,
+ .setsockopt = tipc_setsockopt,
+ .getsockopt = tipc_getsockopt,
+ .sendmsg = tipc_send_packet,
+ .recvmsg = tipc_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage
};
@@ -1967,20 +1953,20 @@ static const struct proto_ops packet_ops = {
static const struct proto_ops stream_ops = {
.owner = THIS_MODULE,
.family = AF_TIPC,
- .release = release,
- .bind = bind,
- .connect = connect,
+ .release = tipc_release,
+ .bind = tipc_bind,
+ .connect = tipc_connect,
.socketpair = sock_no_socketpair,
- .accept = accept,
- .getname = get_name,
- .poll = poll,
+ .accept = tipc_accept,
+ .getname = tipc_getname,
+ .poll = tipc_poll,
.ioctl = sock_no_ioctl,
- .listen = listen,
- .shutdown = shutdown,
- .setsockopt = setsockopt,
- .getsockopt = getsockopt,
- .sendmsg = send_stream,
- .recvmsg = recv_stream,
+ .listen = tipc_listen,
+ .shutdown = tipc_shutdown,
+ .setsockopt = tipc_setsockopt,
+ .getsockopt = tipc_getsockopt,
+ .sendmsg = tipc_send_stream,
+ .recvmsg = tipc_recv_stream,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage
};
diff --git a/net/tipc/socket.h b/net/tipc/socket.h
new file mode 100644
index 000000000000..74e5c7f195a6
--- /dev/null
+++ b/net/tipc/socket.h
@@ -0,0 +1,72 @@
+/* net/tipc/socket.h: Include file for TIPC socket code
+ *
+ * Copyright (c) 2014, Ericsson AB
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TIPC_SOCK_H
+#define _TIPC_SOCK_H
+
+#include "port.h"
+#include <net/sock.h>
+
+/**
+ * struct tipc_sock - TIPC socket structure
+ * @sk: socket - interacts with 'port' and with user via the socket API
+ * @port: port - interacts with 'sk' and with the rest of the TIPC stack
+ * @peer_name: the peer of the connection, if any
+ * @conn_timeout: the time we can wait for an unresponded setup request
+ */
+
+struct tipc_sock {
+ struct sock sk;
+ struct tipc_port port;
+ unsigned int conn_timeout;
+};
+
+static inline struct tipc_sock *tipc_sk(const struct sock *sk)
+{
+ return container_of(sk, struct tipc_sock, sk);
+}
+
+static inline struct tipc_sock *tipc_port_to_sock(const struct tipc_port *port)
+{
+ return container_of(port, struct tipc_sock, port);
+}
+
+static inline void tipc_sock_wakeup(struct tipc_sock *tsk)
+{
+ tsk->sk.sk_write_space(&tsk->sk);
+}
+
+u32 tipc_sk_rcv(struct sock *sk, struct sk_buff *buf);
+
+#endif
diff --git a/net/wireless/ap.c b/net/wireless/ap.c
index 11ee4ed04f73..3e02ade508d8 100644
--- a/net/wireless/ap.c
+++ b/net/wireless/ap.c
@@ -7,7 +7,7 @@
static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
- struct net_device *dev)
+ struct net_device *dev, bool notify)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
@@ -27,22 +27,24 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
err = rdev_stop_ap(rdev, dev);
if (!err) {
wdev->beacon_interval = 0;
- wdev->channel = NULL;
+ memset(&wdev->chandef, 0, sizeof(wdev->chandef));
wdev->ssid_len = 0;
rdev_set_qos_map(rdev, dev, NULL);
+ if (notify)
+ nl80211_send_ap_stopped(wdev);
}
return err;
}
int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
- struct net_device *dev)
+ struct net_device *dev, bool notify)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
wdev_lock(wdev);
- err = __cfg80211_stop_ap(rdev, dev);
+ err = __cfg80211_stop_ap(rdev, dev, notify);
wdev_unlock(wdev);
return err;
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 78559b5bbd1f..9c9501a35fb5 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -490,6 +490,62 @@ static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
return r;
}
+static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
+{
+ struct ieee80211_channel *c;
+ u32 start_freq, end_freq, freq;
+ unsigned int dfs_cac_ms = 0;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c)
+ return 0;
+
+ if (c->flags & IEEE80211_CHAN_DISABLED)
+ return 0;
+
+ if (!(c->flags & IEEE80211_CHAN_RADAR))
+ continue;
+
+ if (c->dfs_cac_ms > dfs_cac_ms)
+ dfs_cac_ms = c->dfs_cac_ms;
+ }
+
+ return dfs_cac_ms;
+}
+
+unsigned int
+cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+ unsigned int t1 = 0, t2 = 0;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return 0;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return 0;
+
+ t1 = cfg80211_get_chans_dfs_cac_time(wiphy,
+ chandef->center_freq1,
+ width);
+
+ if (!chandef->center_freq2)
+ return t1;
+
+ t2 = cfg80211_get_chans_dfs_cac_time(wiphy,
+ chandef->center_freq2,
+ width);
+
+ return max(t1, t2);
+}
static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
u32 center_freq, u32 bandwidth,
@@ -642,7 +698,8 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
void
cfg80211_get_chan_state(struct wireless_dev *wdev,
struct ieee80211_channel **chan,
- enum cfg80211_chan_mode *chanmode)
+ enum cfg80211_chan_mode *chanmode,
+ u8 *radar_detect)
{
*chan = NULL;
*chanmode = CHAN_MODE_UNDEFINED;
@@ -660,6 +717,11 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
!wdev->ibss_dfs_possible)
? CHAN_MODE_SHARED
: CHAN_MODE_EXCLUSIVE;
+
+ /* consider worst-case - IBSS can try to return to the
+ * original user-specified channel as creator */
+ if (wdev->ibss_dfs_possible)
+ *radar_detect |= BIT(wdev->chandef.width);
return;
}
break;
@@ -674,33 +736,36 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
if (wdev->cac_started) {
- *chan = wdev->channel;
+ *chan = wdev->chandef.chan;
*chanmode = CHAN_MODE_SHARED;
+ *radar_detect |= BIT(wdev->chandef.width);
} else if (wdev->beacon_interval) {
- *chan = wdev->channel;
+ *chan = wdev->chandef.chan;
*chanmode = CHAN_MODE_SHARED;
+
+ if (cfg80211_chandef_dfs_required(wdev->wiphy,
+ &wdev->chandef))
+ *radar_detect |= BIT(wdev->chandef.width);
}
return;
case NL80211_IFTYPE_MESH_POINT:
if (wdev->mesh_id_len) {
- *chan = wdev->channel;
+ *chan = wdev->chandef.chan;
*chanmode = CHAN_MODE_SHARED;
+
+ if (cfg80211_chandef_dfs_required(wdev->wiphy,
+ &wdev->chandef))
+ *radar_detect |= BIT(wdev->chandef.width);
}
return;
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_WDS:
- /* these interface types don't really have a channel */
- return;
case NL80211_IFTYPE_P2P_DEVICE:
- if (wdev->wiphy->features &
- NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL)
- *chanmode = CHAN_MODE_EXCLUSIVE;
+ /* these interface types don't really have a channel */
return;
case NL80211_IFTYPE_UNSPECIFIED:
case NUM_NL80211_IFTYPES:
WARN_ON(1);
}
-
- return;
}
diff --git a/net/wireless/core.c b/net/wireless/core.c
index a3bf18d11609..086cddd03ba6 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -737,7 +737,7 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
}
EXPORT_SYMBOL(cfg80211_unregister_wdev);
-static struct device_type wiphy_type = {
+static const struct device_type wiphy_type = {
.name = "wlan",
};
@@ -783,7 +783,7 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
- cfg80211_stop_ap(rdev, dev);
+ cfg80211_stop_ap(rdev, dev, true);
break;
default:
break;
diff --git a/net/wireless/core.h b/net/wireless/core.h
index f1d193b557b6..5b1fdcadd469 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -166,7 +166,6 @@ static inline void wdev_unlock(struct wireless_dev *wdev)
mutex_unlock(&wdev->mtx);
}
-#define ASSERT_RDEV_LOCK(rdev) ASSERT_RTNL()
#define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx)
static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev)
@@ -211,6 +210,7 @@ struct cfg80211_event {
} dc;
struct {
u8 bssid[ETH_ALEN];
+ struct ieee80211_channel *channel;
} ij;
};
};
@@ -245,10 +245,6 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev,
unsigned long age_secs);
/* IBSS */
-int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct cfg80211_ibss_params *params,
- struct cfg80211_cached_keys *connkeys);
int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_ibss_params *params,
@@ -258,7 +254,8 @@ int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
struct net_device *dev, bool nowext);
int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
struct net_device *dev, bool nowext);
-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid);
+void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+ struct ieee80211_channel *channel);
int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
@@ -281,7 +278,7 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
/* AP */
int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
- struct net_device *dev);
+ struct net_device *dev, bool notify);
/* MLME */
int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
@@ -400,6 +397,9 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
void cfg80211_dfs_channels_update_work(struct work_struct *work);
+unsigned int
+cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef);
static inline int
cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
@@ -443,7 +443,8 @@ static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
void
cfg80211_get_chan_state(struct wireless_dev *wdev,
struct ieee80211_channel **chan,
- enum cfg80211_chan_mode *chanmode);
+ enum cfg80211_chan_mode *chanmode,
+ u8 *radar_detect);
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
struct cfg80211_chan_def *chandef);
diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk
index 9a8217d2a908..b35da8dc85de 100644
--- a/net/wireless/genregdb.awk
+++ b/net/wireless/genregdb.awk
@@ -66,6 +66,7 @@ function parse_reg_rule()
units = $8
sub(/\)/, "", units)
sub(/,/, "", units)
+ dfs_cac = $9
if (units == "mW") {
if (power == 100) {
power = 20
@@ -78,7 +79,12 @@ function parse_reg_rule()
} else {
print "Unknown power value in database!"
}
+ } else {
+ dfs_cac = $8
}
+ sub(/,/, "", dfs_cac)
+ sub(/\(/, "", dfs_cac)
+ sub(/\)/, "", dfs_cac)
flagstr = ""
for (i=8; i<=NF; i++)
flagstr = flagstr $i
@@ -105,11 +111,13 @@ function parse_reg_rule()
flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
} else if (flagarray[arg] == "NO-IR") {
flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
+ } else if (flagarray[arg] == "AUTO-BW") {
+ flags = flags "\n\t\t\tNL80211_RRF_AUTO_BW | "
}
}
flags = flags "0"
- printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags
+ printf "\t\tREG_RULE_EXT(%d, %d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, dfs_cac, flags
rules++
}
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index f911c5f9f903..a6b5bdad039c 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -14,7 +14,8 @@
#include "rdev-ops.h"
-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
+void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+ struct ieee80211_channel *channel)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_bss *bss;
@@ -28,8 +29,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
if (!wdev->ssid_len)
return;
- bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
- wdev->ssid, wdev->ssid_len,
+ bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
if (WARN_ON(!bss))
@@ -54,21 +54,26 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
#endif
}
-void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
+void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+ struct ieee80211_channel *channel, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
struct cfg80211_event *ev;
unsigned long flags;
- trace_cfg80211_ibss_joined(dev, bssid);
+ trace_cfg80211_ibss_joined(dev, bssid, channel);
+
+ if (WARN_ON(!channel))
+ return;
ev = kzalloc(sizeof(*ev), gfp);
if (!ev)
return;
ev->type = EVENT_IBSS_JOINED;
- memcpy(ev->cr.bssid, bssid, ETH_ALEN);
+ memcpy(ev->ij.bssid, bssid, ETH_ALEN);
+ ev->ij.channel = channel;
spin_lock_irqsave(&wdev->event_lock, flags);
list_add_tail(&ev->list, &wdev->event_list);
@@ -77,10 +82,10 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
}
EXPORT_SYMBOL(cfg80211_ibss_joined);
-int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct cfg80211_ibss_params *params,
- struct cfg80211_cached_keys *connkeys)
+static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ibss_params *params,
+ struct cfg80211_cached_keys *connkeys)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct ieee80211_channel *check_chan;
@@ -117,17 +122,17 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
wdev->ibss_fixed = params->channel_fixed;
wdev->ibss_dfs_possible = params->userspace_handles_dfs;
+ wdev->chandef = params->chandef;
#ifdef CONFIG_CFG80211_WEXT
wdev->wext.ibss.chandef = params->chandef;
#endif
check_chan = params->chandef.chan;
if (params->userspace_handles_dfs) {
- /* use channel NULL to check for radar even if the current
- * channel is not a radar channel - it might decide to change
- * to DFS channel later.
+ /* Check for radar even if the current channel is not
+ * a radar channel - it might decide to change to DFS
+ * channel later.
*/
radar_detect_width = BIT(params->chandef.width);
- check_chan = NULL;
}
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
@@ -200,6 +205,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
wdev->current_bss = NULL;
wdev->ssid_len = 0;
+ memset(&wdev->chandef, 0, sizeof(wdev->chandef));
#ifdef CONFIG_CFG80211_WEXT
if (!nowext)
wdev->wext.ibss.ssid_len = 0;
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 885862447b63..5af5cc6b2c4c 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -195,7 +195,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
if (!err) {
memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);
wdev->mesh_id_len = setup->mesh_id_len;
- wdev->channel = setup->chandef.chan;
+ wdev->chandef = setup->chandef;
}
return err;
@@ -236,6 +236,12 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
if (!netif_running(wdev->netdev))
return -ENETDOWN;
+ /* cfg80211_can_use_chan() calls
+ * cfg80211_can_use_iftype_chan() with no radar
+ * detection, so if we're trying to use a radar
+ * channel here, something is wrong.
+ */
+ WARN_ON_ONCE(chandef->chan->flags & IEEE80211_CHAN_RADAR);
err = cfg80211_can_use_chan(rdev, wdev, chandef->chan,
CHAN_MODE_SHARED);
if (err)
@@ -244,7 +250,7 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev,
chandef->chan);
if (!err)
- wdev->channel = chandef->chan;
+ wdev->chandef = *chandef;
return err;
}
@@ -276,7 +282,7 @@ static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
err = rdev_leave_mesh(rdev, dev);
if (!err) {
wdev->mesh_id_len = 0;
- wdev->channel = NULL;
+ memset(&wdev->chandef, 0, sizeof(wdev->chandef));
rdev_set_qos_map(rdev, dev, NULL);
}
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 52cca05044a8..c52ff59a3e96 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -772,13 +772,13 @@ void cfg80211_cac_event(struct net_device *netdev,
if (WARN_ON(!wdev->cac_started))
return;
- if (WARN_ON(!wdev->channel))
+ if (WARN_ON(!wdev->chandef.chan))
return;
switch (event) {
case NL80211_RADAR_CAC_FINISHED:
timeout = wdev->cac_start_time +
- msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
+ msecs_to_jiffies(wdev->cac_time_ms);
WARN_ON(!time_after_eq(jiffies, timeout));
cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
break;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 4fe2e6e2bc76..052c1bf8ffac 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -382,6 +382,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
[NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY,
.len = IEEE80211_QOS_MAP_LEN_MAX },
+ [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
+ [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
+ [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
};
/* policy for the key attributes */
@@ -590,6 +593,10 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,
time))
goto nla_put_failure;
+ if (nla_put_u32(msg,
+ NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
+ chan->dfs_cac_ms))
+ goto nla_put_failure;
}
}
@@ -855,6 +862,19 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
return 0;
}
+static struct ieee80211_channel *nl80211_get_valid_chan(struct wiphy *wiphy,
+ struct nlattr *tb)
+{
+ struct ieee80211_channel *chan;
+
+ if (tb == NULL)
+ return NULL;
+ chan = ieee80211_get_channel(wiphy, nla_get_u32(tb));
+ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+ return NULL;
+ return chan;
+}
+
static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
{
struct nlattr *nl_modes = nla_nest_start(msg, attr);
@@ -1586,6 +1606,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
(nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
goto nla_put_failure;
+
+ if (dev->wiphy.max_ap_assoc_sta &&
+ nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA,
+ dev->wiphy.max_ap_assoc_sta))
+ goto nla_put_failure;
+
state->split_start++;
break;
case 11:
@@ -2035,10 +2061,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
nla_for_each_nested(nl_txq_params,
info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
rem_txq_params) {
- nla_parse(tb, NL80211_TXQ_ATTR_MAX,
- nla_data(nl_txq_params),
- nla_len(nl_txq_params),
- txq_params_policy);
+ result = nla_parse(tb, NL80211_TXQ_ATTR_MAX,
+ nla_data(nl_txq_params),
+ nla_len(nl_txq_params),
+ txq_params_policy);
+ if (result)
+ return result;
result = parse_txq_params(tb, &txq_params);
if (result)
return result;
@@ -3259,7 +3287,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
if (!err) {
wdev->preset_chandef = params.chandef;
wdev->beacon_interval = params.beacon_interval;
- wdev->channel = params.chandef.chan;
+ wdev->chandef = params.chandef;
wdev->ssid_len = params.ssid_len;
memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
}
@@ -3304,7 +3332,7 @@ static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
- return cfg80211_stop_ap(rdev, dev);
+ return cfg80211_stop_ap(rdev, dev, false);
}
static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
@@ -3902,8 +3930,8 @@ static struct net_device *get_vlan(struct genl_info *info,
return ERR_PTR(ret);
}
-static struct nla_policy
-nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
+static const struct nla_policy
+nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {
[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
};
@@ -4590,6 +4618,7 @@ static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] =
[NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
[NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
+ [NL80211_ATTR_DFS_CAC_TIME] = { .type = NLA_U32 },
};
static int parse_reg_rule(struct nlattr *tb[],
@@ -4625,6 +4654,10 @@ static int parse_reg_rule(struct nlattr *tb[],
power_rule->max_antenna_gain =
nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
+ if (tb[NL80211_ATTR_DFS_CAC_TIME])
+ reg_rule->dfs_cac_ms =
+ nla_get_u32(tb[NL80211_ATTR_DFS_CAC_TIME]);
+
return 0;
}
@@ -5086,6 +5119,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
const struct ieee80211_reg_rule *reg_rule;
const struct ieee80211_freq_range *freq_range;
const struct ieee80211_power_rule *power_rule;
+ unsigned int max_bandwidth_khz;
reg_rule = &regdom->reg_rules[i];
freq_range = &reg_rule->freq_range;
@@ -5095,6 +5129,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
if (!nl_reg_rule)
goto nla_put_failure_rcu;
+ max_bandwidth_khz = freq_range->max_bandwidth_khz;
+ if (!max_bandwidth_khz)
+ max_bandwidth_khz = reg_get_max_bandwidth(regdom,
+ reg_rule);
+
if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,
reg_rule->flags) ||
nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_START,
@@ -5102,11 +5141,13 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_END,
freq_range->end_freq_khz) ||
nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
- freq_range->max_bandwidth_khz) ||
+ max_bandwidth_khz) ||
nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
power_rule->max_antenna_gain) ||
nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
- power_rule->max_eirp))
+ power_rule->max_eirp) ||
+ nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME,
+ reg_rule->dfs_cac_ms))
goto nla_put_failure_rcu;
nla_nest_end(msg, nl_reg_rule);
@@ -5178,9 +5219,11 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
rem_reg_rules) {
- nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
- nla_data(nl_reg_rule), nla_len(nl_reg_rule),
- reg_rule_policy);
+ r = nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
+ nla_data(nl_reg_rule), nla_len(nl_reg_rule),
+ reg_rule_policy);
+ if (r)
+ goto bad_reg;
r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
if (r)
goto bad_reg;
@@ -5443,6 +5486,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
enum ieee80211_band band;
size_t ie_len;
struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
+ s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
!rdev->ops->sched_scan_start)
@@ -5477,11 +5521,40 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
if (n_ssids > wiphy->max_sched_scan_ssids)
return -EINVAL;
- if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
+ /*
+ * First, count the number of 'real' matchsets. Due to an issue with
+ * the old implementation, matchsets containing only the RSSI attribute
+ * (NL80211_SCHED_SCAN_MATCH_ATTR_RSSI) are considered as the 'default'
+ * RSSI for all matchsets, rather than their own matchset for reporting
+ * all APs with a strong RSSI. This is needed to be compatible with
+ * older userspace that treated a matchset with only the RSSI as the
+ * global RSSI for all other matchsets - if there are other matchsets.
+ */
+ if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
nla_for_each_nested(attr,
info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
- tmp)
- n_match_sets++;
+ tmp) {
+ struct nlattr *rssi;
+
+ err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
+ nla_data(attr), nla_len(attr),
+ nl80211_match_policy);
+ if (err)
+ return err;
+ /* add other standalone attributes here */
+ if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
+ n_match_sets++;
+ continue;
+ }
+ rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
+ if (rssi)
+ default_match_rssi = nla_get_s32(rssi);
+ }
+ }
+
+ /* However, if there's no other matchset, add the RSSI one */
+ if (!n_match_sets && default_match_rssi != NL80211_SCAN_RSSI_THOLD_OFF)
+ n_match_sets = 1;
if (n_match_sets > wiphy->max_match_sets)
return -EINVAL;
@@ -5602,11 +5675,22 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
tmp) {
struct nlattr *ssid, *rssi;
- nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
- nla_data(attr), nla_len(attr),
- nl80211_match_policy);
+ err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
+ nla_data(attr), nla_len(attr),
+ nl80211_match_policy);
+ if (err)
+ goto out_free;
ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
if (ssid) {
+ if (WARN_ON(i >= n_match_sets)) {
+ /* this indicates a programming error,
+ * the loop above should have verified
+ * things properly
+ */
+ err = -EINVAL;
+ goto out_free;
+ }
+
if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
err = -EINVAL;
goto out_free;
@@ -5615,19 +5699,32 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
nla_data(ssid), nla_len(ssid));
request->match_sets[i].ssid.ssid_len =
nla_len(ssid);
+ /* special attribute - old implemenation w/a */
+ request->match_sets[i].rssi_thold =
+ default_match_rssi;
+ rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
+ if (rssi)
+ request->match_sets[i].rssi_thold =
+ nla_get_s32(rssi);
}
- rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
- if (rssi)
- request->rssi_thold = nla_get_u32(rssi);
- else
- request->rssi_thold =
- NL80211_SCAN_RSSI_THOLD_OFF;
i++;
}
+
+ /* there was no other matchset, so the RSSI one is alone */
+ if (i == 0)
+ request->match_sets[0].rssi_thold = default_match_rssi;
+
+ request->min_rssi_thold = INT_MAX;
+ for (i = 0; i < n_match_sets; i++)
+ request->min_rssi_thold =
+ min(request->match_sets[i].rssi_thold,
+ request->min_rssi_thold);
+ } else {
+ request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF;
}
- if (info->attrs[NL80211_ATTR_IE]) {
- request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ if (ie_len) {
+ request->ie_len = ie_len;
memcpy((void *)request->ie,
nla_data(info->attrs[NL80211_ATTR_IE]),
request->ie_len);
@@ -5682,6 +5779,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_chan_def chandef;
enum nl80211_dfs_regions dfs_region;
+ unsigned int cac_time_ms;
int err;
dfs_region = reg_get_dfs_region(wdev->wiphy);
@@ -5717,11 +5815,17 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (err)
return err;
- err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
+ cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef);
+ 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);
if (!err) {
- wdev->channel = chandef.chan;
+ wdev->chandef = chandef;
wdev->cac_started = true;
wdev->cac_start_time = jiffies;
+ wdev->cac_time_ms = cac_time_ms;
}
return err;
}
@@ -5751,10 +5855,15 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
/* useless if AP is not running */
if (!wdev->beacon_interval)
- return -EINVAL;
+ return -ENOTCONN;
break;
case NL80211_IFTYPE_ADHOC:
+ if (!wdev->ssid_len)
+ return -ENOTCONN;
+ break;
case NL80211_IFTYPE_MESH_POINT:
+ if (!wdev->mesh_id_len)
+ return -ENOTCONN;
break;
default:
return -EOPNOTSUPP;
@@ -5822,17 +5931,22 @@ skip_beacons:
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
return -EINVAL;
- if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
- dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO ||
- dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) {
+ switch (dev->ieee80211_ptr->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
err = cfg80211_chandef_dfs_required(wdev->wiphy,
&params.chandef);
- if (err < 0) {
+ if (err < 0)
return err;
- } else if (err) {
+ if (err) {
radar_detect_width = BIT(params.chandef.width);
params.radar_required = true;
}
+ break;
+ default:
+ break;
}
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
@@ -6192,9 +6306,9 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
return -EOPNOTSUPP;
bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
- chan = ieee80211_get_channel(&rdev->wiphy,
- nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
- if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
+ chan = nl80211_get_valid_chan(&rdev->wiphy,
+ info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ if (!chan)
return -EINVAL;
ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
@@ -6347,9 +6461,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
- chan = ieee80211_get_channel(&rdev->wiphy,
- nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
- if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
+ chan = nl80211_get_valid_chan(&rdev->wiphy,
+ info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ if (!chan)
return -EINVAL;
ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
@@ -6985,6 +7099,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_MAC])
connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ else if (info->attrs[NL80211_ATTR_MAC_HINT])
+ connect.bssid_hint =
+ nla_data(info->attrs[NL80211_ATTR_MAC_HINT]);
connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
@@ -7003,11 +7120,14 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
}
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
- connect.channel =
- ieee80211_get_channel(wiphy,
- nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
- if (!connect.channel ||
- connect.channel->flags & IEEE80211_CHAN_DISABLED)
+ connect.channel = nl80211_get_valid_chan(
+ wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ if (!connect.channel)
+ return -EINVAL;
+ } else if (info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]) {
+ connect.channel_hint = nl80211_get_valid_chan(
+ wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]);
+ if (!connect.channel_hint)
return -EINVAL;
}
@@ -7174,6 +7294,7 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
u8 action_code, dialog_token;
+ u32 peer_capability = 0;
u16 status_code;
u8 *peer;
@@ -7192,9 +7313,12 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
+ if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY])
+ peer_capability =
+ nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]);
return rdev_tdls_mgmt(rdev, dev, peer, action_code,
- dialog_token, status_code,
+ dialog_token, status_code, peer_capability,
nla_data(info->attrs[NL80211_ATTR_IE]),
nla_len(info->attrs[NL80211_ATTR_IE]));
}
@@ -7421,6 +7545,7 @@ static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
[NL80211_TXRATE_HT] = { .type = NLA_BINARY,
.len = NL80211_MAX_SUPP_HT_RATES },
[NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
+ [NL80211_TXRATE_GI] = { .type = NLA_U8 },
};
static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
@@ -7467,16 +7592,19 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
* directly to the enum ieee80211_band values used in cfg80211.
*/
BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
- nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
- {
+ nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
enum ieee80211_band band = nla_type(tx_rates);
+ int err;
+
if (band < 0 || band >= IEEE80211_NUM_BANDS)
return -EINVAL;
sband = rdev->wiphy.bands[band];
if (sband == NULL)
return -EINVAL;
- nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
- nla_len(tx_rates), nl80211_txattr_policy);
+ err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
+ nla_len(tx_rates), nl80211_txattr_policy);
+ if (err)
+ return err;
if (tb[NL80211_TXRATE_LEGACY]) {
mask.control[band].legacy = rateset_to_mask(
sband,
@@ -7501,6 +7629,12 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
mask.control[band].vht_mcs))
return -EINVAL;
}
+ if (tb[NL80211_TXRATE_GI]) {
+ mask.control[band].gi =
+ nla_get_u8(tb[NL80211_TXRATE_GI]);
+ if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI)
+ return -EINVAL;
+ }
if (mask.control[band].legacy == 0) {
/* don't allow empty legacy rates if HT or VHT
@@ -7777,8 +7911,8 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
return err;
}
-static struct nla_policy
-nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
+static const struct nla_policy
+nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
@@ -11107,7 +11241,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
wdev->iftype != NL80211_IFTYPE_MESH_POINT))
return;
- wdev->channel = chandef->chan;
+ wdev->chandef = *chandef;
+ wdev->preset_chandef = *chandef;
nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
}
EXPORT_SYMBOL(cfg80211_ch_switch_notify);
@@ -11621,6 +11756,35 @@ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp)
}
EXPORT_SYMBOL(cfg80211_crit_proto_stopped);
+void nl80211_send_ap_stopped(struct wireless_dev *wdev)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_STOP_AP);
+ if (!hdr)
+ goto out;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex) ||
+ nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
+ goto out;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(wiphy), msg, 0,
+ NL80211_MCGRP_MLME, GFP_KERNEL);
+ return;
+ out:
+ nlmsg_free(msg);
+}
+
/* initialisation/exit functions */
int nl80211_init(void)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 75799746d845..1e6df9630f42 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -74,6 +74,8 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
enum nl80211_radar_event event,
struct net_device *netdev, gfp_t gfp);
+void nl80211_send_ap_stopped(struct wireless_dev *wdev);
+
void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index c8e225947adb..74d97d33c938 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -769,13 +769,16 @@ static inline int rdev_set_rekey_data(struct cfg80211_registered_device *rdev,
static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev,
struct net_device *dev, u8 *peer,
u8 action_code, u8 dialog_token,
- u16 status_code, const u8 *buf, size_t len)
+ u16 status_code, u32 peer_capability,
+ const u8 *buf, size_t len)
{
int ret;
trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
- dialog_token, status_code, buf, len);
+ dialog_token, status_code, peer_capability,
+ buf, len);
ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
- dialog_token, status_code, buf, len);
+ dialog_token, status_code, peer_capability,
+ buf, len);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index f0541370e68e..f59aaac586f8 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -91,10 +91,6 @@ static struct regulatory_request __rcu *last_request =
/* To trigger userspace events */
static struct platform_device *reg_pdev;
-static struct device_type reg_device_type = {
- .uevent = reg_device_uevent,
-};
-
/*
* Central wireless core regulatory domains, we only need two,
* the current one and a world regulatory domain in case we have no
@@ -244,19 +240,21 @@ static char user_alpha2[2];
module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
-static void reg_kfree_last_request(void)
+static void reg_free_request(struct regulatory_request *lr)
{
- struct regulatory_request *lr;
-
- lr = get_last_request();
-
if (lr != &core_request_world && lr)
kfree_rcu(lr, rcu_head);
}
static void reg_update_last_request(struct regulatory_request *request)
{
- reg_kfree_last_request();
+ struct regulatory_request *lr;
+
+ lr = get_last_request();
+ if (lr == request)
+ return;
+
+ reg_free_request(lr);
rcu_assign_pointer(last_request, request);
}
@@ -487,11 +485,16 @@ static inline void reg_regdb_query(const char *alpha2) {}
/*
* This lets us keep regulatory code which is updated on a regulatory
- * basis in userspace. Country information is filled in by
- * reg_device_uevent
+ * basis in userspace.
*/
static int call_crda(const char *alpha2)
{
+ char country[12];
+ char *env[] = { country, NULL };
+
+ snprintf(country, sizeof(country), "COUNTRY=%c%c",
+ alpha2[0], alpha2[1]);
+
if (!is_world_regdom((char *) alpha2))
pr_info("Calling CRDA for country: %c%c\n",
alpha2[0], alpha2[1]);
@@ -501,7 +504,7 @@ static int call_crda(const char *alpha2)
/* query internal regulatory database (if it exists) */
reg_regdb_query(alpha2);
- return kobject_uevent(&reg_pdev->dev.kobj, KOBJ_CHANGE);
+ return kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, env);
}
static enum reg_request_treatment
@@ -522,6 +525,71 @@ bool reg_is_valid_request(const char *alpha2)
return alpha2_equal(lr->alpha2, alpha2);
}
+static const struct ieee80211_regdomain *reg_get_regdomain(struct wiphy *wiphy)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ /*
+ * Follow the driver's regulatory domain, if present, unless a country
+ * IE has been processed or a user wants to help complaince further
+ */
+ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ lr->initiator != NL80211_REGDOM_SET_BY_USER &&
+ wiphy->regd)
+ return get_wiphy_regdom(wiphy);
+
+ return get_cfg80211_regdom();
+}
+
+unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
+ const struct ieee80211_reg_rule *rule)
+{
+ const struct ieee80211_freq_range *freq_range = &rule->freq_range;
+ const struct ieee80211_freq_range *freq_range_tmp;
+ const struct ieee80211_reg_rule *tmp;
+ u32 start_freq, end_freq, idx, no;
+
+ for (idx = 0; idx < rd->n_reg_rules; idx++)
+ if (rule == &rd->reg_rules[idx])
+ break;
+
+ if (idx == rd->n_reg_rules)
+ return 0;
+
+ /* get start_freq */
+ no = idx;
+
+ while (no) {
+ tmp = &rd->reg_rules[--no];
+ freq_range_tmp = &tmp->freq_range;
+
+ if (freq_range_tmp->end_freq_khz < freq_range->start_freq_khz)
+ break;
+
+ freq_range = freq_range_tmp;
+ }
+
+ start_freq = freq_range->start_freq_khz;
+
+ /* get end_freq */
+ freq_range = &rule->freq_range;
+ no = idx;
+
+ while (no < rd->n_reg_rules - 1) {
+ tmp = &rd->reg_rules[++no];
+ freq_range_tmp = &tmp->freq_range;
+
+ if (freq_range_tmp->start_freq_khz > freq_range->end_freq_khz)
+ break;
+
+ freq_range = freq_range_tmp;
+ }
+
+ end_freq = freq_range->end_freq_khz;
+
+ return end_freq - start_freq;
+}
+
/* Sanity check on a regulatory rule */
static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
{
@@ -630,7 +698,9 @@ reg_intersect_dfs_region(const enum nl80211_dfs_regions dfs_region1,
* Helper for regdom_intersect(), this does the real
* mathematical intersection fun
*/
-static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,
+static int reg_rules_intersect(const struct ieee80211_regdomain *rd1,
+ const struct ieee80211_regdomain *rd2,
+ const struct ieee80211_reg_rule *rule1,
const struct ieee80211_reg_rule *rule2,
struct ieee80211_reg_rule *intersected_rule)
{
@@ -638,7 +708,7 @@ static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,
struct ieee80211_freq_range *freq_range;
const struct ieee80211_power_rule *power_rule1, *power_rule2;
struct ieee80211_power_rule *power_rule;
- u32 freq_diff;
+ u32 freq_diff, max_bandwidth1, max_bandwidth2;
freq_range1 = &rule1->freq_range;
freq_range2 = &rule2->freq_range;
@@ -652,8 +722,32 @@ static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,
freq_range2->start_freq_khz);
freq_range->end_freq_khz = min(freq_range1->end_freq_khz,
freq_range2->end_freq_khz);
- freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz,
- freq_range2->max_bandwidth_khz);
+
+ max_bandwidth1 = freq_range1->max_bandwidth_khz;
+ max_bandwidth2 = freq_range2->max_bandwidth_khz;
+
+ if (rule1->flags & NL80211_RRF_AUTO_BW)
+ max_bandwidth1 = reg_get_max_bandwidth(rd1, rule1);
+ if (rule2->flags & NL80211_RRF_AUTO_BW)
+ max_bandwidth2 = reg_get_max_bandwidth(rd2, rule2);
+
+ freq_range->max_bandwidth_khz = min(max_bandwidth1, max_bandwidth2);
+
+ intersected_rule->flags = rule1->flags | rule2->flags;
+
+ /*
+ * In case NL80211_RRF_AUTO_BW requested for both rules
+ * set AUTO_BW in intersected rule also. Next we will
+ * calculate BW correctly in handle_channel function.
+ * In other case remove AUTO_BW flag while we calculate
+ * maximum bandwidth correctly and auto calculation is
+ * not required.
+ */
+ if ((rule1->flags & NL80211_RRF_AUTO_BW) &&
+ (rule2->flags & NL80211_RRF_AUTO_BW))
+ intersected_rule->flags |= NL80211_RRF_AUTO_BW;
+ else
+ intersected_rule->flags &= ~NL80211_RRF_AUTO_BW;
freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
if (freq_range->max_bandwidth_khz > freq_diff)
@@ -664,7 +758,8 @@ static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,
power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain,
power_rule2->max_antenna_gain);
- intersected_rule->flags = rule1->flags | rule2->flags;
+ intersected_rule->dfs_cac_ms = max(rule1->dfs_cac_ms,
+ rule2->dfs_cac_ms);
if (!is_valid_reg_rule(intersected_rule))
return -EINVAL;
@@ -713,7 +808,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
rule1 = &rd1->reg_rules[x];
for (y = 0; y < rd2->n_reg_rules; y++) {
rule2 = &rd2->reg_rules[y];
- if (!reg_rules_intersect(rule1, rule2, &dummy_rule))
+ if (!reg_rules_intersect(rd1, rd2, rule1, rule2,
+ &dummy_rule))
num_rules++;
}
}
@@ -738,7 +834,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
* a memcpy()
*/
intersected_rule = &rd->reg_rules[rule_idx];
- r = reg_rules_intersect(rule1, rule2, intersected_rule);
+ r = reg_rules_intersect(rd1, rd2, rule1, rule2,
+ intersected_rule);
/*
* No need to memset here the intersected rule here as
* we're not using the stack anymore
@@ -821,18 +918,8 @@ const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
u32 center_freq)
{
const struct ieee80211_regdomain *regd;
- struct regulatory_request *lr = get_last_request();
- /*
- * Follow the driver's regulatory domain, if present, unless a country
- * IE has been processed or a user wants to help complaince further
- */
- if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- lr->initiator != NL80211_REGDOM_SET_BY_USER &&
- wiphy->regd)
- regd = get_wiphy_regdom(wiphy);
- else
- regd = get_cfg80211_regdom();
+ regd = reg_get_regdomain(wiphy);
return freq_reg_info_regd(wiphy, center_freq, regd);
}
@@ -857,31 +944,42 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
EXPORT_SYMBOL(reg_initiator_name);
#ifdef CONFIG_CFG80211_REG_DEBUG
-static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
+static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
+ struct ieee80211_channel *chan,
const struct ieee80211_reg_rule *reg_rule)
{
const struct ieee80211_power_rule *power_rule;
const struct ieee80211_freq_range *freq_range;
- char max_antenna_gain[32];
+ char max_antenna_gain[32], bw[32];
power_rule = &reg_rule->power_rule;
freq_range = &reg_rule->freq_range;
if (!power_rule->max_antenna_gain)
- snprintf(max_antenna_gain, 32, "N/A");
+ snprintf(max_antenna_gain, sizeof(max_antenna_gain), "N/A");
else
- snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain);
+ snprintf(max_antenna_gain, sizeof(max_antenna_gain), "%d",
+ power_rule->max_antenna_gain);
+
+ if (reg_rule->flags & NL80211_RRF_AUTO_BW)
+ snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO",
+ freq_range->max_bandwidth_khz,
+ reg_get_max_bandwidth(regd, reg_rule));
+ else
+ snprintf(bw, sizeof(bw), "%d KHz",
+ freq_range->max_bandwidth_khz);
REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n",
chan->center_freq);
- REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n",
+ REG_DBG_PRINT("%d KHz - %d KHz @ %s), (%s mBi, %d mBm)\n",
freq_range->start_freq_khz, freq_range->end_freq_khz,
- freq_range->max_bandwidth_khz, max_antenna_gain,
+ bw, max_antenna_gain,
power_rule->max_eirp);
}
#else
-static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
+static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
+ struct ieee80211_channel *chan,
const struct ieee80211_reg_rule *reg_rule)
{
return;
@@ -903,6 +1001,8 @@ static void handle_channel(struct wiphy *wiphy,
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);
@@ -939,16 +1039,22 @@ static void handle_channel(struct wiphy *wiphy,
return;
}
- chan_reg_rule_print_dbg(chan, reg_rule);
+ regd = reg_get_regdomain(wiphy);
+ chan_reg_rule_print_dbg(regd, chan, reg_rule);
power_rule = &reg_rule->power_rule;
freq_range = &reg_rule->freq_range;
- if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
+ 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 (max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags = IEEE80211_CHAN_NO_HT40;
- if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80))
+ if (max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
- if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160))
+ if (max_bandwidth_khz < MHZ_TO_KHZ(160))
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
@@ -977,6 +1083,14 @@ static void handle_channel(struct wiphy *wiphy,
min_t(int, chan->orig_mag,
MBI_TO_DBI(power_rule->max_antenna_gain));
chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
+
+ if (chan->flags & IEEE80211_CHAN_RADAR) {
+ if (reg_rule->dfs_cac_ms)
+ chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
+ else
+ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ }
+
if (chan->orig_mpwr) {
/*
* Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER
@@ -1334,6 +1448,7 @@ static void handle_channel_custom(struct wiphy *wiphy,
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;
reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
regd);
@@ -1346,16 +1461,21 @@ static void handle_channel_custom(struct wiphy *wiphy,
return;
}
- chan_reg_rule_print_dbg(chan, reg_rule);
+ chan_reg_rule_print_dbg(regd, chan, reg_rule);
power_rule = &reg_rule->power_rule;
freq_range = &reg_rule->freq_range;
- if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
+ 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 (max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags = IEEE80211_CHAN_NO_HT40;
- if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80))
+ if (max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
- if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160))
+ if (max_bandwidth_khz < MHZ_TO_KHZ(160))
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
@@ -1683,17 +1803,9 @@ static void reg_process_hint(struct regulatory_request *reg_request)
struct wiphy *wiphy = NULL;
enum reg_request_treatment treatment;
- if (WARN_ON(!reg_request->alpha2))
- return;
-
if (reg_request->wiphy_idx != WIPHY_IDX_INVALID)
wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
- if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) {
- kfree(reg_request);
- return;
- }
-
switch (reg_request->initiator) {
case NL80211_REGDOM_SET_BY_CORE:
reg_process_hint_core(reg_request);
@@ -1703,23 +1815,33 @@ static void reg_process_hint(struct regulatory_request *reg_request)
if (treatment == REG_REQ_IGNORE ||
treatment == REG_REQ_ALREADY_SET)
return;
- schedule_delayed_work(&reg_timeout, msecs_to_jiffies(3142));
+ queue_delayed_work(system_power_efficient_wq,
+ &reg_timeout, msecs_to_jiffies(3142));
return;
case NL80211_REGDOM_SET_BY_DRIVER:
+ if (!wiphy)
+ goto out_free;
treatment = reg_process_hint_driver(wiphy, reg_request);
break;
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ if (!wiphy)
+ goto out_free;
treatment = reg_process_hint_country_ie(wiphy, reg_request);
break;
default:
WARN(1, "invalid initiator %d\n", reg_request->initiator);
- return;
+ goto out_free;
}
/* This is required so that the orig_* parameters are saved */
if (treatment == REG_REQ_ALREADY_SET && wiphy &&
wiphy->regulatory_flags & REGULATORY_STRICT_REG)
wiphy_update_regulatory(wiphy, reg_request->initiator);
+
+ return;
+
+out_free:
+ kfree(reg_request);
}
/*
@@ -2147,31 +2269,49 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_freq_range *freq_range = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
+ char bw[32], cac_time[32];
- pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n");
+ pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp), (dfs_cac_time)\n");
for (i = 0; i < rd->n_reg_rules; i++) {
reg_rule = &rd->reg_rules[i];
freq_range = &reg_rule->freq_range;
power_rule = &reg_rule->power_rule;
+ if (reg_rule->flags & NL80211_RRF_AUTO_BW)
+ snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO",
+ freq_range->max_bandwidth_khz,
+ reg_get_max_bandwidth(rd, reg_rule));
+ else
+ snprintf(bw, sizeof(bw), "%d KHz",
+ freq_range->max_bandwidth_khz);
+
+ if (reg_rule->flags & NL80211_RRF_DFS)
+ scnprintf(cac_time, sizeof(cac_time), "%u s",
+ reg_rule->dfs_cac_ms/1000);
+ else
+ scnprintf(cac_time, sizeof(cac_time), "N/A");
+
+
/*
* There may not be documentation for max antenna gain
* in certain regions
*/
if (power_rule->max_antenna_gain)
- pr_info(" (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n",
+ pr_info(" (%d KHz - %d KHz @ %s), (%d mBi, %d mBm), (%s)\n",
freq_range->start_freq_khz,
freq_range->end_freq_khz,
- freq_range->max_bandwidth_khz,
+ bw,
power_rule->max_antenna_gain,
- power_rule->max_eirp);
+ power_rule->max_eirp,
+ cac_time);
else
- pr_info(" (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n",
+ pr_info(" (%d KHz - %d KHz @ %s), (N/A, %d mBm), (%s)\n",
freq_range->start_freq_khz,
freq_range->end_freq_khz,
- freq_range->max_bandwidth_khz,
- power_rule->max_eirp);
+ bw,
+ power_rule->max_eirp,
+ cac_time);
}
}
@@ -2244,9 +2384,6 @@ static int reg_set_rd_user(const struct ieee80211_regdomain *rd,
{
const struct ieee80211_regdomain *intersected_rd = NULL;
- if (is_world_regdom(rd->alpha2))
- return -EINVAL;
-
if (!regdom_changes(rd->alpha2))
return -EALREADY;
@@ -2294,7 +2431,8 @@ static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx);
if (!request_wiphy) {
- schedule_delayed_work(&reg_timeout, 0);
+ queue_delayed_work(system_power_efficient_wq,
+ &reg_timeout, 0);
return -ENODEV;
}
@@ -2354,7 +2492,8 @@ static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd,
request_wiphy = wiphy_idx_to_wiphy(country_ie_request->wiphy_idx);
if (!request_wiphy) {
- schedule_delayed_work(&reg_timeout, 0);
+ queue_delayed_work(system_power_efficient_wq,
+ &reg_timeout, 0);
return -ENODEV;
}
@@ -2433,26 +2572,6 @@ int set_regdom(const struct ieee80211_regdomain *rd)
return 0;
}
-int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- struct regulatory_request *lr;
- u8 alpha2[2];
- bool add = false;
-
- rcu_read_lock();
- lr = get_last_request();
- if (lr && !lr->processed) {
- memcpy(alpha2, lr->alpha2, 2);
- add = true;
- }
- rcu_read_unlock();
-
- if (add)
- return add_uevent_var(env, "COUNTRY=%c%c",
- alpha2[0], alpha2[1]);
- return 0;
-}
-
void wiphy_regulatory_register(struct wiphy *wiphy)
{
struct regulatory_request *lr;
@@ -2503,8 +2622,6 @@ int __init regulatory_init(void)
if (IS_ERR(reg_pdev))
return PTR_ERR(reg_pdev);
- reg_pdev->dev.type = &reg_device_type;
-
spin_lock_init(&reg_requests_lock);
spin_lock_init(&reg_pending_beacons_lock);
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 02bd8f4b0921..37c180df34b7 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -26,7 +26,6 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);
int regulatory_hint_user(const char *alpha2,
enum nl80211_user_reg_hint_type user_reg_hint_type);
-int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env);
void wiphy_regulatory_register(struct wiphy *wiphy);
void wiphy_regulatory_deregister(struct wiphy *wiphy);
@@ -34,6 +33,8 @@ int __init regulatory_init(void);
void regulatory_exit(void);
int set_regdom(const struct ieee80211_regdomain *rd);
+unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
+ const struct ieee80211_reg_rule *rule);
bool reg_last_request_cell_base(void);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index d1ed4aebbbb7..7d09a712cb1f 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -659,9 +659,6 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
continue;
if (ssidlen && ie[1] != ssidlen)
continue;
- /* that would be odd ... */
- if (bss->pub.beacon_ies)
- continue;
if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss))
continue;
if (WARN_ON_ONCE(!list_empty(&bss->hidden_list)))
@@ -680,7 +677,8 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
/* Returned bss is reference counted and must be cleaned up appropriately. */
static struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *dev,
- struct cfg80211_internal_bss *tmp)
+ struct cfg80211_internal_bss *tmp,
+ bool signal_valid)
{
struct cfg80211_internal_bss *found = NULL;
@@ -765,7 +763,12 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
}
found->pub.beacon_interval = tmp->pub.beacon_interval;
- found->pub.signal = tmp->pub.signal;
+ /*
+ * don't update the signal if beacon was heard on
+ * adjacent channel.
+ */
+ if (signal_valid)
+ found->pub.signal = tmp->pub.signal;
found->pub.capability = tmp->pub.capability;
found->ts = tmp->ts;
} else {
@@ -869,13 +872,14 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
/* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss*
cfg80211_inform_bss_width(struct wiphy *wiphy,
- struct ieee80211_channel *channel,
+ struct ieee80211_channel *rx_channel,
enum nl80211_bss_scan_width scan_width,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp)
{
struct cfg80211_bss_ies *ies;
+ struct ieee80211_channel *channel;
struct cfg80211_internal_bss tmp = {}, *res;
if (WARN_ON(!wiphy))
@@ -885,7 +889,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
(signal < 0 || signal > 100)))
return NULL;
- channel = cfg80211_get_bss_channel(wiphy, ie, ielen, channel);
+ channel = cfg80211_get_bss_channel(wiphy, ie, ielen, rx_channel);
if (!channel)
return NULL;
@@ -913,7 +917,8 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
rcu_assign_pointer(tmp.pub.beacon_ies, ies);
rcu_assign_pointer(tmp.pub.ies, ies);
- res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
+ res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp,
+ rx_channel == channel);
if (!res)
return NULL;
@@ -929,20 +934,21 @@ EXPORT_SYMBOL(cfg80211_inform_bss_width);
/* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss *
cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
- struct ieee80211_channel *channel,
+ struct ieee80211_channel *rx_channel,
enum nl80211_bss_scan_width scan_width,
struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp)
{
struct cfg80211_internal_bss tmp = {}, *res;
struct cfg80211_bss_ies *ies;
+ struct ieee80211_channel *channel;
size_t ielen = len - offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
offsetof(struct ieee80211_mgmt, u.beacon.variable));
- trace_cfg80211_inform_bss_width_frame(wiphy, channel, scan_width, mgmt,
+ trace_cfg80211_inform_bss_width_frame(wiphy, rx_channel, scan_width, mgmt,
len, signal);
if (WARN_ON(!mgmt))
@@ -959,7 +965,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
return NULL;
channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
- ielen, channel);
+ ielen, rx_channel);
if (!channel)
return NULL;
@@ -983,7 +989,8 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
- res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
+ res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp,
+ rx_channel == channel);
if (!res)
return NULL;
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index f04d4c32e96e..acdcb4a81817 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -64,7 +64,6 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
int n_channels, err;
ASSERT_RTNL();
- ASSERT_RDEV_LOCK(rdev);
ASSERT_WDEV_LOCK(wdev);
if (rdev->scan_req || rdev->scan_msg)
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index fbcc23edee54..aabccf13e07b 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1468,9 +1468,10 @@ TRACE_EVENT(rdev_sched_scan_start,
TRACE_EVENT(rdev_tdls_mgmt,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
u8 *peer, u8 action_code, u8 dialog_token,
- u16 status_code, const u8 *buf, size_t len),
+ u16 status_code, u32 peer_capability,
+ const u8 *buf, size_t len),
TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code,
- buf, len),
+ peer_capability, buf, len),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
@@ -1478,6 +1479,7 @@ TRACE_EVENT(rdev_tdls_mgmt,
__field(u8, action_code)
__field(u8, dialog_token)
__field(u16, status_code)
+ __field(u32, peer_capability)
__dynamic_array(u8, buf, len)
),
TP_fast_assign(
@@ -1487,13 +1489,15 @@ TRACE_EVENT(rdev_tdls_mgmt,
__entry->action_code = action_code;
__entry->dialog_token = dialog_token;
__entry->status_code = status_code;
+ __entry->peer_capability = peer_capability;
memcpy(__get_dynamic_array(buf), buf, len);
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, "
- "dialog_token: %u, status_code: %u, buf: %#.2x ",
+ "dialog_token: %u, status_code: %u, peer_capability: %u buf: %#.2x ",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer),
__entry->action_code, __entry->dialog_token,
- __entry->status_code, ((u8 *)__get_dynamic_array(buf))[0])
+ __entry->status_code, __entry->peer_capability,
+ ((u8 *)__get_dynamic_array(buf))[0])
);
TRACE_EVENT(rdev_dump_survey,
@@ -2278,11 +2282,6 @@ DECLARE_EVENT_CLASS(cfg80211_rx_evt,
TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr))
);
-DEFINE_EVENT(cfg80211_rx_evt, cfg80211_ibss_joined,
- TP_PROTO(struct net_device *netdev, const u8 *addr),
- TP_ARGS(netdev, addr)
-);
-
DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_spurious_frame,
TP_PROTO(struct net_device *netdev, const u8 *addr),
TP_ARGS(netdev, addr)
@@ -2293,6 +2292,24 @@ DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_unexpected_4addr_frame,
TP_ARGS(netdev, addr)
);
+TRACE_EVENT(cfg80211_ibss_joined,
+ TP_PROTO(struct net_device *netdev, const u8 *bssid,
+ struct ieee80211_channel *channel),
+ TP_ARGS(netdev, bssid, channel),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, bssid);
+ CHAN_ASSIGN(channel);
+ ),
+ TP_printk(NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", " CHAN_PR_FMT,
+ NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG)
+);
+
TRACE_EVENT(cfg80211_probe_status,
TP_PROTO(struct net_device *netdev, const u8 *addr, u64 cookie,
bool acked),
diff --git a/net/wireless/util.c b/net/wireless/util.c
index d39c37104ae2..e5872ff2c27c 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -11,6 +11,7 @@
#include <net/ip.h>
#include <net/dsfield.h>
#include <linux/if_vlan.h>
+#include <linux/mpls.h>
#include "core.h"
#include "rdev-ops.h"
@@ -717,6 +718,21 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
case htons(ETH_P_IPV6):
dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc;
break;
+ case htons(ETH_P_MPLS_UC):
+ case htons(ETH_P_MPLS_MC): {
+ struct mpls_label mpls_tmp, *mpls;
+
+ mpls = skb_header_pointer(skb, sizeof(struct ethhdr),
+ sizeof(*mpls), &mpls_tmp);
+ if (!mpls)
+ return 0;
+
+ return (ntohl(mpls->entry) & MPLS_LS_TC_MASK)
+ >> MPLS_LS_TC_SHIFT;
+ }
+ case htons(ETH_P_80221):
+ /* 802.21 is always network control traffic */
+ return 7;
default:
return 0;
}
@@ -820,7 +836,8 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
ev->dc.reason, true);
break;
case EVENT_IBSS_JOINED:
- __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid);
+ __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid,
+ ev->ij.channel);
break;
}
wdev_unlock(wdev);
@@ -837,7 +854,6 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev)
struct wireless_dev *wdev;
ASSERT_RTNL();
- ASSERT_RDEV_LOCK(rdev);
list_for_each_entry(wdev, &rdev->wdev_list, list)
cfg80211_process_wdev_events(wdev);
@@ -850,7 +866,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
int err;
enum nl80211_iftype otype = dev->ieee80211_ptr->iftype;
- ASSERT_RDEV_LOCK(rdev);
+ ASSERT_RTNL();
/* don't support changing VLANs, you just re-create them */
if (otype == NL80211_IFTYPE_AP_VLAN)
@@ -885,7 +901,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
switch (otype) {
case NL80211_IFTYPE_AP:
- cfg80211_stop_ap(rdev, dev);
+ cfg80211_stop_ap(rdev, dev, true);
break;
case NL80211_IFTYPE_ADHOC:
cfg80211_leave_ibss(rdev, dev, false);
@@ -1268,7 +1284,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
enum cfg80211_chan_mode chmode;
int num_different_channels = 0;
int total = 1;
- bool radar_required = false;
int i, j;
ASSERT_RTNL();
@@ -1276,35 +1291,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
if (WARN_ON(hweight32(radar_detect) > 1))
return -EINVAL;
- switch (iftype) {
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_AP_VLAN:
- case NL80211_IFTYPE_MESH_POINT:
- case NL80211_IFTYPE_P2P_GO:
- case NL80211_IFTYPE_WDS:
- /* if the interface could potentially choose a DFS channel,
- * then mark DFS as required.
- */
- if (!chan) {
- if (chanmode != CHAN_MODE_UNDEFINED && radar_detect)
- radar_required = true;
- break;
- }
- radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR);
- break;
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_P2P_DEVICE:
- case NL80211_IFTYPE_MONITOR:
- break;
- case NUM_NL80211_IFTYPES:
- case NL80211_IFTYPE_UNSPECIFIED:
- default:
- return -EINVAL;
- }
-
- if (radar_required && !radar_detect)
+ if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))
return -EINVAL;
/* Always allow software iftypes */
@@ -1356,7 +1343,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
*/
mutex_lock_nested(&wdev_iter->mtx, 1);
__acquire(wdev_iter->mtx);
- cfg80211_get_chan_state(wdev_iter, &ch, &chmode);
+ cfg80211_get_chan_state(wdev_iter, &ch, &chmode, &radar_detect);
wdev_unlock(wdev_iter);
switch (chmode) {
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index 14c9a2583ba0..86c331a65664 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -21,7 +21,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
const u8 *prev_bssid = NULL;
int err, i;
- ASSERT_RDEV_LOCK(rdev);
+ ASSERT_RTNL();
ASSERT_WDEV_LOCK(wdev);
if (!netif_running(wdev->netdev))
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 6c7ac016ce3a..85d1d4764612 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -16,6 +16,81 @@
static struct kmem_cache *secpath_cachep __read_mostly;
+static DEFINE_SPINLOCK(xfrm_input_afinfo_lock);
+static struct xfrm_input_afinfo __rcu *xfrm_input_afinfo[NPROTO];
+
+int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo)
+{
+ int err = 0;
+
+ if (unlikely(afinfo == NULL))
+ return -EINVAL;
+ if (unlikely(afinfo->family >= NPROTO))
+ return -EAFNOSUPPORT;
+ spin_lock_bh(&xfrm_input_afinfo_lock);
+ if (unlikely(xfrm_input_afinfo[afinfo->family] != NULL))
+ err = -ENOBUFS;
+ else
+ rcu_assign_pointer(xfrm_input_afinfo[afinfo->family], afinfo);
+ spin_unlock_bh(&xfrm_input_afinfo_lock);
+ return err;
+}
+EXPORT_SYMBOL(xfrm_input_register_afinfo);
+
+int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo)
+{
+ int err = 0;
+
+ if (unlikely(afinfo == NULL))
+ return -EINVAL;
+ if (unlikely(afinfo->family >= NPROTO))
+ return -EAFNOSUPPORT;
+ spin_lock_bh(&xfrm_input_afinfo_lock);
+ if (likely(xfrm_input_afinfo[afinfo->family] != NULL)) {
+ if (unlikely(xfrm_input_afinfo[afinfo->family] != afinfo))
+ err = -EINVAL;
+ else
+ RCU_INIT_POINTER(xfrm_input_afinfo[afinfo->family], NULL);
+ }
+ spin_unlock_bh(&xfrm_input_afinfo_lock);
+ synchronize_rcu();
+ return err;
+}
+EXPORT_SYMBOL(xfrm_input_unregister_afinfo);
+
+static struct xfrm_input_afinfo *xfrm_input_get_afinfo(unsigned int family)
+{
+ struct xfrm_input_afinfo *afinfo;
+
+ if (unlikely(family >= NPROTO))
+ return NULL;
+ rcu_read_lock();
+ afinfo = rcu_dereference(xfrm_input_afinfo[family]);
+ if (unlikely(!afinfo))
+ rcu_read_unlock();
+ return afinfo;
+}
+
+static void xfrm_input_put_afinfo(struct xfrm_input_afinfo *afinfo)
+{
+ rcu_read_unlock();
+}
+
+static int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, u8 protocol,
+ int err)
+{
+ int ret;
+ struct xfrm_input_afinfo *afinfo = xfrm_input_get_afinfo(family);
+
+ if (!afinfo)
+ return -EAFNOSUPPORT;
+
+ ret = afinfo->callback(skb, protocol, err);
+ xfrm_input_put_afinfo(afinfo);
+
+ return ret;
+}
+
void __secpath_destroy(struct sec_path *sp)
{
int i;
@@ -108,7 +183,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
int err;
__be32 seq;
__be32 seq_hi;
- struct xfrm_state *x;
+ struct xfrm_state *x = NULL;
xfrm_address_t *daddr;
struct xfrm_mode *inner_mode;
unsigned int family;
@@ -120,9 +195,14 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
async = 1;
x = xfrm_input_state(skb);
seq = XFRM_SKB_CB(skb)->seq.input.low;
+ family = x->outer_mode->afinfo->family;
goto resume;
}
+ daddr = (xfrm_address_t *)(skb_network_header(skb) +
+ XFRM_SPI_SKB_CB(skb)->daddroff);
+ family = XFRM_SPI_SKB_CB(skb)->family;
+
/* Allocate new secpath or COW existing one. */
if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
struct sec_path *sp;
@@ -137,10 +217,6 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
skb->sp = sp;
}
- daddr = (xfrm_address_t *)(skb_network_header(skb) +
- XFRM_SPI_SKB_CB(skb)->daddroff);
- family = XFRM_SPI_SKB_CB(skb)->family;
-
seq = 0;
if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
@@ -162,6 +238,11 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
skb->sp->xvec[skb->sp->len++] = x;
+ if (xfrm_tunnel_check(skb, x, family)) {
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
+ goto drop;
+ }
+
spin_lock(&x->lock);
if (unlikely(x->km.state == XFRM_STATE_ACQ)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR);
@@ -201,7 +282,6 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
if (nexthdr == -EINPROGRESS)
return 0;
-
resume:
spin_lock(&x->lock);
if (nexthdr <= 0) {
@@ -263,6 +343,10 @@ resume:
}
} while (!err);
+ err = xfrm_rcv_cb(skb, family, x->type->proto, 0);
+ if (err)
+ goto drop;
+
nf_reset(skb);
if (decaps) {
@@ -276,6 +360,7 @@ resume:
drop_unlock:
spin_unlock(&x->lock);
drop:
+ xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1);
kfree_skb(skb);
return 0;
}
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 1d5c7bf29938..f02f511b7107 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -39,8 +39,6 @@
#define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ))
#define XFRM_MAX_QUEUE_LEN 100
-static struct dst_entry *xfrm_policy_sk_bundles;
-
static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);
static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO]
__read_mostly;
@@ -661,7 +659,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
hlist_add_head(&policy->bydst, chain);
xfrm_pol_hold(policy);
net->xfrm.policy_count[dir]++;
- atomic_inc(&flow_cache_genid);
+ atomic_inc(&net->xfrm.flow_cache_genid);
/* After previous checking, family can either be AF_INET or AF_INET6 */
if (policy->family == AF_INET)
@@ -2109,13 +2107,6 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
goto no_transform;
}
- dst_hold(&xdst->u.dst);
-
- spin_lock_bh(&net->xfrm.xfrm_policy_sk_bundle_lock);
- xdst->u.dst.next = xfrm_policy_sk_bundles;
- xfrm_policy_sk_bundles = &xdst->u.dst;
- spin_unlock_bh(&net->xfrm.xfrm_policy_sk_bundle_lock);
-
route = xdst->route;
}
}
@@ -2549,33 +2540,15 @@ static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst)
return dst;
}
-static void __xfrm_garbage_collect(struct net *net)
-{
- struct dst_entry *head, *next;
-
- spin_lock_bh(&net->xfrm.xfrm_policy_sk_bundle_lock);
- head = xfrm_policy_sk_bundles;
- xfrm_policy_sk_bundles = NULL;
- spin_unlock_bh(&net->xfrm.xfrm_policy_sk_bundle_lock);
-
- while (head) {
- next = head->next;
- dst_free(head);
- head = next;
- }
-}
-
void xfrm_garbage_collect(struct net *net)
{
- flow_cache_flush();
- __xfrm_garbage_collect(net);
+ flow_cache_flush(net);
}
EXPORT_SYMBOL(xfrm_garbage_collect);
static void xfrm_garbage_collect_deferred(struct net *net)
{
- flow_cache_flush_deferred();
- __xfrm_garbage_collect(net);
+ flow_cache_flush_deferred(net);
}
static void xfrm_init_pmtu(struct dst_entry *dst)
@@ -2940,15 +2913,19 @@ static int __net_init xfrm_net_init(struct net *net)
rv = xfrm_sysctl_init(net);
if (rv < 0)
goto out_sysctl;
+ rv = flow_cache_init(net);
+ if (rv < 0)
+ goto out;
/* Initialize the per-net locks here */
spin_lock_init(&net->xfrm.xfrm_state_lock);
rwlock_init(&net->xfrm.xfrm_policy_lock);
- spin_lock_init(&net->xfrm.xfrm_policy_sk_bundle_lock);
mutex_init(&net->xfrm.xfrm_cfg_mutex);
return 0;
+out:
+ xfrm_sysctl_fini(net);
out_sysctl:
xfrm_policy_fini(net);
out_policy:
@@ -2961,6 +2938,7 @@ out_statistics:
static void __net_exit xfrm_net_exit(struct net *net)
{
+ flow_cache_fini(net);
xfrm_sysctl_fini(net);
xfrm_policy_fini(net);
xfrm_state_fini(net);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 40f1b3e92e78..8e9c781a6bba 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -161,6 +161,7 @@ static DEFINE_SPINLOCK(xfrm_state_gc_lock);
int __xfrm_state_delete(struct xfrm_state *x);
int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
+bool km_is_alive(const struct km_event *c);
void km_state_expired(struct xfrm_state *x, int hard, u32 portid);
static DEFINE_SPINLOCK(xfrm_type_lock);
@@ -788,6 +789,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
struct xfrm_state *best = NULL;
u32 mark = pol->mark.v & pol->mark.m;
unsigned short encap_family = tmpl->encap_family;
+ struct km_event c;
to_put = NULL;
@@ -832,6 +834,17 @@ found:
error = -EEXIST;
goto out;
}
+
+ c.net = net;
+ /* If the KMs have no listeners (yet...), avoid allocating an SA
+ * for each and every packet - garbage collection might not
+ * handle the flood.
+ */
+ if (!km_is_alive(&c)) {
+ error = -ESRCH;
+ goto out;
+ }
+
x = xfrm_state_alloc(net);
if (x == NULL) {
error = -ENOMEM;
@@ -1135,10 +1148,9 @@ out:
EXPORT_SYMBOL(xfrm_state_add);
#ifdef CONFIG_XFRM_MIGRATE
-static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
+static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig)
{
struct net *net = xs_net(orig);
- int err = -ENOMEM;
struct xfrm_state *x = xfrm_state_alloc(net);
if (!x)
goto out;
@@ -1192,15 +1204,13 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
}
if (orig->replay_esn) {
- err = xfrm_replay_clone(x, orig);
- if (err)
+ if (xfrm_replay_clone(x, orig))
goto error;
}
memcpy(&x->mark, &orig->mark, sizeof(x->mark));
- err = xfrm_init_state(x);
- if (err)
+ if (xfrm_init_state(x) < 0)
goto error;
x->props.flags = orig->props.flags;
@@ -1218,8 +1228,6 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
error:
xfrm_state_put(x);
out:
- if (errp)
- *errp = err;
return NULL;
}
@@ -1274,9 +1282,8 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
struct xfrm_migrate *m)
{
struct xfrm_state *xc;
- int err;
- xc = xfrm_state_clone(x, &err);
+ xc = xfrm_state_clone(x);
if (!xc)
return NULL;
@@ -1289,7 +1296,7 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
state is to be updated as it is a part of triplet */
xfrm_state_insert(xc);
} else {
- if ((err = xfrm_state_add(xc)) < 0)
+ if (xfrm_state_add(xc) < 0)
goto error;
}
@@ -1601,6 +1608,23 @@ unlock:
}
EXPORT_SYMBOL(xfrm_alloc_spi);
+static bool __xfrm_state_filter_match(struct xfrm_state *x,
+ struct xfrm_address_filter *filter)
+{
+ if (filter) {
+ if ((filter->family == AF_INET ||
+ filter->family == AF_INET6) &&
+ x->props.family != filter->family)
+ return false;
+
+ return addr_match(&x->props.saddr, &filter->saddr,
+ filter->splen) &&
+ addr_match(&x->id.daddr, &filter->daddr,
+ filter->dplen);
+ }
+ return true;
+}
+
int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
int (*func)(struct xfrm_state *, int, void*),
void *data)
@@ -1623,6 +1647,8 @@ int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
state = container_of(x, struct xfrm_state, km);
if (!xfrm_id_proto_match(state->id.proto, walk->proto))
continue;
+ if (!__xfrm_state_filter_match(state, walk->filter))
+ continue;
err = func(state, walk->seq, data);
if (err) {
list_move_tail(&walk->all, &x->all);
@@ -1641,17 +1667,21 @@ out:
}
EXPORT_SYMBOL(xfrm_state_walk);
-void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
+void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto,
+ struct xfrm_address_filter *filter)
{
INIT_LIST_HEAD(&walk->all);
walk->proto = proto;
walk->state = XFRM_STATE_DEAD;
walk->seq = 0;
+ walk->filter = filter;
}
EXPORT_SYMBOL(xfrm_state_walk_init);
void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net)
{
+ kfree(walk->filter);
+
if (list_empty(&walk->all))
return;
@@ -1804,6 +1834,24 @@ int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address
}
EXPORT_SYMBOL(km_report);
+bool km_is_alive(const struct km_event *c)
+{
+ struct xfrm_mgr *km;
+ bool is_alive = false;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(km, &xfrm_km_list, list) {
+ if (km->is_alive && km->is_alive(c)) {
+ is_alive = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return is_alive;
+}
+EXPORT_SYMBOL(km_is_alive);
+
int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
{
int err;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 2f7ddc3a59b4..8f131c10a6f3 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -137,7 +137,8 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
if (!rt)
return 0;
- if (p->id.proto != IPPROTO_ESP)
+ /* As only ESP and AH support ESN feature. */
+ if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH))
return -EINVAL;
if (p->replay_window != 0)
@@ -881,6 +882,7 @@ static int xfrm_dump_sa_done(struct netlink_callback *cb)
return 0;
}
+static const struct nla_policy xfrma_policy[XFRMA_MAX+1];
static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
@@ -896,8 +898,31 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
info.nlmsg_flags = NLM_F_MULTI;
if (!cb->args[0]) {
+ struct nlattr *attrs[XFRMA_MAX+1];
+ struct xfrm_address_filter *filter = NULL;
+ u8 proto = 0;
+ int err;
+
cb->args[0] = 1;
- xfrm_state_walk_init(walk, 0);
+
+ err = nlmsg_parse(cb->nlh, 0, attrs, XFRMA_MAX,
+ xfrma_policy);
+ if (err < 0)
+ return err;
+
+ if (attrs[XFRMA_ADDRESS_FILTER]) {
+ filter = kmalloc(sizeof(*filter), GFP_KERNEL);
+ if (filter == NULL)
+ return -ENOMEM;
+
+ memcpy(filter, nla_data(attrs[XFRMA_ADDRESS_FILTER]),
+ sizeof(*filter));
+ }
+
+ if (attrs[XFRMA_PROTO])
+ proto = nla_get_u8(attrs[XFRMA_PROTO]);
+
+ xfrm_state_walk_init(walk, proto, filter);
}
(void) xfrm_state_walk(net, walk, dump_one_state, &info);
@@ -2303,6 +2328,8 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
[XFRMA_TFCPAD] = { .type = NLA_U32 },
[XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) },
[XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 },
+ [XFRMA_PROTO] = { .type = NLA_U8 },
+ [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) },
};
static const struct xfrm_link {
@@ -2976,6 +3003,11 @@ static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC);
}
+static bool xfrm_is_alive(const struct km_event *c)
+{
+ return (bool)xfrm_acquire_is_on(c->net);
+}
+
static struct xfrm_mgr netlink_mgr = {
.id = "netlink",
.notify = xfrm_send_state_notify,
@@ -2985,6 +3017,7 @@ static struct xfrm_mgr netlink_mgr = {
.report = xfrm_send_report,
.migrate = xfrm_send_migrate,
.new_mapping = xfrm_send_mapping,
+ .is_alive = xfrm_is_alive,
};
static int __net_init xfrm_user_net_init(struct net *net)
diff --git a/samples/seccomp/Makefile b/samples/seccomp/Makefile
index 7203e66dcd6f..1b4e4b8f5e47 100644
--- a/samples/seccomp/Makefile
+++ b/samples/seccomp/Makefile
@@ -18,8 +18,8 @@ HOSTCFLAGS_bpf-direct.o += -idirafter $(objtree)/include
bpf-direct-objs := bpf-direct.o
# Try to match the kernel target.
-ifndef CONFIG_64BIT
ifndef CROSS_COMPILE
+ifndef CONFIG_64BIT
# s390 has -m31 flag to build 31 bit binaries
ifndef CONFIG_S390
@@ -36,7 +36,13 @@ HOSTLOADLIBES_bpf-direct += $(MFLAG)
HOSTLOADLIBES_bpf-fancy += $(MFLAG)
HOSTLOADLIBES_dropper += $(MFLAG)
endif
-endif
-
-# Tell kbuild to always build the programs
always := $(hostprogs-y)
+else
+# MIPS system calls are defined based on the -mabi that is passed
+# to the toolchain which may or may not be a valid option
+# for the host toolchain. So disable tests if target architecture
+# is MIPS but the host isn't.
+ifndef CONFIG_MIPS
+always := $(hostprogs-y)
+endif
+endif
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 79c059e70860..72105d104357 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -274,6 +274,18 @@ $(obj)/%.dtb: $(src)/%.dts FORCE
dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
+# Helper targets for Installing DTBs into the boot directory
+quiet_cmd_dtb_install = INSTALL $<
+ cmd_dtb_install = cp $< $(2)
+
+_dtbinst_pre_:
+ $(Q)if [ -d $(INSTALL_DTBS_PATH).old ]; then rm -rf $(INSTALL_DTBS_PATH).old; fi
+ $(Q)if [ -d $(INSTALL_DTBS_PATH) ]; then mv $(INSTALL_DTBS_PATH) $(INSTALL_DTBS_PATH).old; fi
+ $(Q)mkdir -p $(INSTALL_DTBS_PATH)
+
+%.dtb_dtbinst_: $(obj)/%.dtb _dtbinst_pre_
+ $(call cmd,dtb_install,$(INSTALL_DTBS_PATH))
+
# Bzip2
# ---------------------------------------------------------------------------
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 464dcef79b35..34eb2160489d 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -281,7 +281,7 @@ our $Attribute = qr{
__weak
}x;
our $Modifier;
-our $Inline = qr{inline|__always_inline|noinline};
+our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__};
our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]};
our $Lval = qr{$Ident(?:$Member)*};
@@ -289,13 +289,14 @@ our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u};
our $Binary = qr{(?i)0b[01]+$Int_type?};
our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?};
our $Int = qr{[0-9]+$Int_type?};
+our $Octal = qr{0[0-7]+$Int_type?};
our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?};
our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?};
our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?};
our $Float = qr{$Float_hex|$Float_dec|$Float_int};
-our $Constant = qr{$Float|$Binary|$Hex|$Int};
+our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int};
our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=};
-our $Compare = qr{<=|>=|==|!=|<|>};
+our $Compare = qr{<=|>=|==|!=|<|(?<!-)>};
our $Arithmetic = qr{\+|-|\*|\/|%};
our $Operators = qr{
<=|>=|==|!=|
@@ -303,6 +304,8 @@ our $Operators = qr{
&&|\|\||,|\^|\+\+|--|&|\||$Arithmetic
}x;
+our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x;
+
our $NonptrType;
our $NonptrTypeWithAttr;
our $Type;
@@ -378,6 +381,22 @@ our @modifierList = (
qr{fastcall},
);
+our @mode_permission_funcs = (
+ ["module_param", 3],
+ ["module_param_(?:array|named|string)", 4],
+ ["module_param_array_named", 5],
+ ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2],
+ ["proc_create(?:_data|)", 2],
+ ["(?:CLASS|DEVICE|SENSOR)_ATTR", 2],
+);
+
+#Create a search pattern for all these functions to speed up a loop below
+our $mode_perms_search = "";
+foreach my $entry (@mode_permission_funcs) {
+ $mode_perms_search .= '|' if ($mode_perms_search ne "");
+ $mode_perms_search .= $entry->[0];
+}
+
our $allowed_asm_includes = qr{(?x:
irq|
memory
@@ -412,7 +431,7 @@ sub build_types {
(?:(?:\s|\*|\[\])+\s*const|(?:\s|\*|\[\])+|(?:\s*\[\s*\])+)?
(?:\s+$Inline|\s+$Modifier)*
}x;
- $Declare = qr{(?:$Storage\s+)?$Type};
+ $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type};
}
build_types();
@@ -423,15 +442,20 @@ our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*};
# Any use must be runtime checked with $^V
our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/;
-our $LvalOrFunc = qr{($Lval)\s*($balanced_parens{0,1})\s*};
+our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*};
our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant)};
sub deparenthesize {
my ($string) = @_;
return "" if (!defined($string));
- $string =~ s@^\s*\(\s*@@g;
- $string =~ s@\s*\)\s*$@@g;
+
+ while ($string =~ /^\s*\(.*\)\s*$/) {
+ $string =~ s@^\s*\(\s*@@;
+ $string =~ s@\s*\)\s*$@@;
+ }
+
$string =~ s@\s+@ @g;
+
return $string;
}
@@ -1421,21 +1445,25 @@ sub possible {
my $prefix = '';
sub show_type {
- return defined $use_type{$_[0]} if (scalar keys %use_type > 0);
+ my ($type) = @_;
+
+ return defined $use_type{$type} if (scalar keys %use_type > 0);
- return !defined $ignore_type{$_[0]};
+ return !defined $ignore_type{$type};
}
sub report {
- if (!show_type($_[1]) ||
- (defined $tst_only && $_[2] !~ /\Q$tst_only\E/)) {
+ my ($level, $type, $msg) = @_;
+
+ if (!show_type($type) ||
+ (defined $tst_only && $msg !~ /\Q$tst_only\E/)) {
return 0;
}
my $line;
if ($show_types) {
- $line = "$prefix$_[0]:$_[1]: $_[2]\n";
+ $line = "$prefix$level:$type: $msg\n";
} else {
- $line = "$prefix$_[0]: $_[2]\n";
+ $line = "$prefix$level: $msg\n";
}
$line = (split('\n', $line))[0] . "\n" if ($terse);
@@ -1443,12 +1471,15 @@ sub report {
return 1;
}
+
sub report_dump {
our @report;
}
sub ERROR {
- if (report("ERROR", $_[0], $_[1])) {
+ my ($type, $msg) = @_;
+
+ if (report("ERROR", $type, $msg)) {
our $clean = 0;
our $cnt_error++;
return 1;
@@ -1456,7 +1487,9 @@ sub ERROR {
return 0;
}
sub WARN {
- if (report("WARNING", $_[0], $_[1])) {
+ my ($type, $msg) = @_;
+
+ if (report("WARNING", $type, $msg)) {
our $clean = 0;
our $cnt_warn++;
return 1;
@@ -1464,7 +1497,9 @@ sub WARN {
return 0;
}
sub CHK {
- if ($check && report("CHECK", $_[0], $_[1])) {
+ my ($type, $msg) = @_;
+
+ if ($check && report("CHECK", $type, $msg)) {
our $clean = 0;
our $cnt_chk++;
return 1;
@@ -1574,7 +1609,7 @@ sub pos_last_openparen {
}
}
- return $last_openparen + 1;
+ return length(expand_tabs(substr($line, 0, $last_openparen))) + 1;
}
sub process {
@@ -1891,6 +1926,12 @@ sub process {
}
}
+# Check for unwanted Gerrit info
+ if ($in_commit_log && $line =~ /^\s*change-id:/i) {
+ ERROR("GERRIT_CHANGE_ID",
+ "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr);
+ }
+
# Check for wrappage within a valid hunk of the file
if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) {
ERROR("CORRUPTED_PATCH",
@@ -2041,13 +2082,17 @@ sub process {
}
# check for DT compatible documentation
- if (defined $root && $realfile =~ /\.dts/ &&
- $rawline =~ /^\+\s*compatible\s*=/) {
+ if (defined $root &&
+ (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) ||
+ ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) {
+
my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g;
+ my $dt_path = $root . "/Documentation/devicetree/bindings/";
+ my $vp_file = $dt_path . "vendor-prefixes.txt";
+
foreach my $compat (@compats) {
my $compat2 = $compat;
- my $dt_path = $root . "/Documentation/devicetree/bindings/";
$compat2 =~ s/\,[a-z]*\-/\,<\.\*>\-/;
`grep -Erq "$compat|$compat2" $dt_path`;
if ( $? >> 8 ) {
@@ -2055,14 +2100,12 @@ sub process {
"DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr);
}
- my $vendor = $compat;
- my $vendor_path = $dt_path . "vendor-prefixes.txt";
- next if (! -f $vendor_path);
- $vendor =~ s/^([a-zA-Z0-9]+)\,.*/$1/;
- `grep -Eq "$vendor" $vendor_path`;
+ next if $compat !~ /^([a-zA-Z0-9\-]+)\,/;
+ my $vendor = $1;
+ `grep -Eq "^$vendor\\b" $vp_file`;
if ( $? >> 8 ) {
WARN("UNDOCUMENTED_DT_STRING",
- "DT compatible string vendor \"$vendor\" appears un-documented -- check $vendor_path\n" . $herecurr);
+ "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr);
}
}
}
@@ -2159,7 +2202,7 @@ sub process {
# check multi-line statement indentation matches previous line
if ($^V && $^V ge 5.10.0 &&
- $prevline =~ /^\+(\t*)(if \(|$Ident\().*(\&\&|\|\||,)\s*$/) {
+ $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|$Ident\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) {
$prevline =~ /^\+(\t*)(.*)$/;
my $oldindent = $1;
my $rest = $2;
@@ -2198,7 +2241,8 @@ sub process {
if ($realfile =~ m@^(drivers/net/|net/)@ &&
$prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ &&
- $rawline =~ /^\+[ \t]*\*/) {
+ $rawline =~ /^\+[ \t]*\*/ &&
+ $realline > 2) {
WARN("NETWORKING_BLOCK_COMMENT_STYLE",
"networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev);
}
@@ -2221,6 +2265,21 @@ sub process {
"networking block comments put the trailing */ on a separate line\n" . $herecurr);
}
+# check for missing blank lines after declarations
+ if ($realfile =~ m@^(drivers/net/|net/)@ &&
+ $prevline =~ /^\+\s+$Declare\s+$Ident/ &&
+ !($prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ ||
+ $prevline =~ /(?:\{\s*|\\)$/) && #extended lines
+ $sline =~ /^\+\s+/ && #Not at char 1
+ !($sline =~ /^\+\s+$Declare/ ||
+ $sline =~ /^\+\s+$Ident\s+$Ident/ || #eg: typedef foo
+ $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ ||
+ $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(])/ ||
+ $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/)) {
+ WARN("SPACING",
+ "networking uses a blank line after declarations\n" . $hereprev);
+ }
+
# check for spaces at the beginning of a line.
# Exceptions:
# 1) within comments
@@ -2665,6 +2724,13 @@ sub process {
$herecurr);
}
+# check for non-global char *foo[] = {"bar", ...} declarations.
+ if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) {
+ WARN("STATIC_CONST_CHAR_ARRAY",
+ "char * array declaration might be better as static const\n" .
+ $herecurr);
+ }
+
# check for function declarations without arguments like "int foo()"
if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) {
if (ERROR("FUNCTION_WITHOUT_ARGS",
@@ -2799,7 +2865,7 @@ sub process {
my $level2 = $level;
$level2 = "dbg" if ($level eq "debug");
WARN("PREFER_PR_LEVEL",
- "Prefer netdev_$level2(netdev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr);
+ "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr);
}
if ($line =~ /\bpr_warning\s*\(/) {
@@ -2848,10 +2914,7 @@ sub process {
# Function pointer declarations
# check spacing between type, funcptr, and args
# canonical declaration is "type (*funcptr)(args...)"
-#
-# the $Declare variable will capture all spaces after the type
-# so check it for trailing missing spaces or multiple spaces
- if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)$Ident(\s*)\)(\s*)\(/) {
+ if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) {
my $declare = $1;
my $pre_pointer_space = $2;
my $post_pointer_space = $3;
@@ -2859,16 +2922,30 @@ sub process {
my $post_funcname_space = $5;
my $pre_args_space = $6;
- if ($declare !~ /\s$/) {
+# the $Declare variable will capture all spaces after the type
+# so check it for a missing trailing missing space but pointer return types
+# don't need a space so don't warn for those.
+ my $post_declare_space = "";
+ if ($declare =~ /(\s+)$/) {
+ $post_declare_space = $1;
+ $declare = rtrim($declare);
+ }
+ if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) {
WARN("SPACING",
"missing space after return type\n" . $herecurr);
+ $post_declare_space = " ";
}
# unnecessary space "type (*funcptr)(args...)"
- elsif ($declare =~ /\s{2,}$/) {
- WARN("SPACING",
- "Multiple spaces after return type\n" . $herecurr);
- }
+# This test is not currently implemented because these declarations are
+# equivalent to
+# int foo(int bar, ...)
+# and this is form shouldn't/doesn't generate a checkpatch warning.
+#
+# elsif ($declare =~ /\s{2,}$/) {
+# WARN("SPACING",
+# "Multiple spaces after return type\n" . $herecurr);
+# }
# unnecessary space "type ( *funcptr)(args...)"
if (defined $pre_pointer_space &&
@@ -2900,7 +2977,7 @@ sub process {
if (show_type("SPACING") && $fix) {
$fixed[$linenr - 1] =~
- s/^(.\s*$Declare)\(\s*\*\s*($Ident)\s*\)\s*\(/rtrim($1) . " " . "\(\*$2\)\("/ex;
+ s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex;
}
}
@@ -3061,10 +3138,13 @@ sub process {
# // is a comment
} elsif ($op eq '//') {
+ # : when part of a bitfield
+ } elsif ($opv eq ':B') {
+ # skip the bitfield test for now
+
# No spaces for:
# ->
- # : when part of a bitfield
- } elsif ($op eq '->' || $opv eq ':B') {
+ } elsif ($op eq '->') {
if ($ctx =~ /Wx.|.xW/) {
if (ERROR("SPACING",
"spaces prohibited around that '$op' $at\n" . $hereptr)) {
@@ -3334,14 +3414,17 @@ sub process {
}
}
-# Return is not a function.
+# return is not a function
if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) {
my $spacing = $1;
if ($^V && $^V ge 5.10.0 &&
- $stat =~ /^.\s*return\s*$balanced_parens\s*;\s*$/) {
- ERROR("RETURN_PARENTHESES",
- "return is not a function, parentheses are not required\n" . $herecurr);
-
+ $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) {
+ my $value = $1;
+ $value = deparenthesize($value);
+ if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) {
+ ERROR("RETURN_PARENTHESES",
+ "return is not a function, parentheses are not required\n" . $herecurr);
+ }
} elsif ($spacing !~ /\s+/) {
ERROR("SPACING",
"space required before the open parenthesis '('\n" . $herecurr);
@@ -3910,12 +3993,30 @@ sub process {
}
}
+# don't use __constant_<foo> functions outside of include/uapi/
+ if ($realfile !~ m@^include/uapi/@ &&
+ $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) {
+ my $constant_func = $1;
+ my $func = $constant_func;
+ $func =~ s/^__constant_//;
+ if (WARN("CONSTANT_CONVERSION",
+ "$constant_func should be $func\n" . $herecurr) &&
+ $fix) {
+ $fixed[$linenr - 1] =~ s/\b$constant_func\b/$func/g;
+ }
+ }
+
# prefer usleep_range over udelay
if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) {
+ my $delay = $1;
# ignore udelay's < 10, however
- if (! ($1 < 10) ) {
+ if (! ($delay < 10) ) {
CHK("USLEEP_RANGE",
- "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $line);
+ "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr);
+ }
+ if ($delay > 2000) {
+ WARN("LONG_UDELAY",
+ "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr);
}
}
@@ -3923,7 +4024,7 @@ sub process {
if ($line =~ /\bmsleep\s*\((\d+)\);/) {
if ($1 < 20) {
WARN("MSLEEP",
- "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $line);
+ "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr);
}
}
@@ -4149,7 +4250,7 @@ sub process {
# check for naked sscanf
if ($^V && $^V ge 5.10.0 &&
defined $stat &&
- $stat =~ /\bsscanf\b/ &&
+ $line =~ /\bsscanf\b/ &&
($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ &&
$stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ &&
$stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) {
@@ -4240,12 +4341,6 @@ sub process {
"$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr);
}
-# check for GFP_NOWAIT use
- if ($line =~ /\b__GFP_NOFAIL\b/) {
- WARN("__GFP_NOFAIL",
- "Use of __GFP_NOFAIL is deprecated, no new users should be added\n" . $herecurr);
- }
-
# check for multiple semicolons
if ($line =~ /;\s*;\s*$/) {
if (WARN("ONE_SEMICOLON",
@@ -4457,6 +4552,34 @@ sub process {
WARN("EXPORTED_WORLD_WRITABLE",
"Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr);
}
+
+# Mode permission misuses where it seems decimal should be octal
+# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop
+ if ($^V && $^V ge 5.10.0 &&
+ $line =~ /$mode_perms_search/) {
+ foreach my $entry (@mode_permission_funcs) {
+ my $func = $entry->[0];
+ my $arg_pos = $entry->[1];
+
+ my $skip_args = "";
+ if ($arg_pos > 1) {
+ $arg_pos--;
+ $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}";
+ }
+ my $test = "\\b$func\\s*\\(${skip_args}([\\d]+)\\s*[,\\)]";
+ if ($line =~ /$test/) {
+ my $val = $1;
+ $val = $6 if ($skip_args ne "");
+
+ if ($val !~ /^0$/ &&
+ (($val =~ /^$Int$/ && $val !~ /^$Octal$/) ||
+ length($val) ne 4)) {
+ ERROR("NON_OCTAL_PERMISSIONS",
+ "Use 4 digit octal (0777) not decimal permissions\n" . $herecurr);
+ }
+ }
+ }
+ }
}
# If we have no input at all, then there is nothing to report on
diff --git a/scripts/dtc/dtc-parser.tab.c_shipped b/scripts/dtc/dtc-parser.tab.c_shipped
index ee1d8c3042fb..c8769d550cfb 100644
--- a/scripts/dtc/dtc-parser.tab.c_shipped
+++ b/scripts/dtc/dtc-parser.tab.c_shipped
@@ -1,8 +1,8 @@
-/* A Bison parser, made by GNU Bison 2.5. */
+/* A Bison parser, made by GNU Bison 2.7.12-4996. */
/* Bison implementation for Yacc-like parsers in C
- Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+ Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, 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
@@ -44,7 +44,7 @@
#define YYBISON 1
/* Bison version. */
-#define YYBISON_VERSION "2.5"
+#define YYBISON_VERSION "2.7.12-4996"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
@@ -58,14 +58,11 @@
/* Pull parsers. */
#define YYPULL 1
-/* Using locations. */
-#define YYLSP_NEEDED 0
/* Copy the first part of user declarations. */
-
-/* Line 268 of yacc.c */
+/* Line 371 of yacc.c */
#line 21 "dtc-parser.y"
#include <stdio.h>
@@ -85,14 +82,16 @@ extern int treesource_error;
static unsigned long long eval_literal(const char *s, int base, int bits);
static unsigned char eval_char_literal(const char *s);
+/* Line 371 of yacc.c */
+#line 87 "dtc-parser.tab.c"
-/* Line 268 of yacc.c */
-#line 91 "dtc-parser.tab.c"
-
-/* Enabling traces. */
-#ifndef YYDEBUG
-# define YYDEBUG 0
-#endif
+# ifndef YY_NULL
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULL nullptr
+# else
+# define YY_NULL 0
+# endif
+# endif
/* Enabling verbose error messages. */
#ifdef YYERROR_VERBOSE
@@ -102,11 +101,17 @@ static unsigned char eval_char_literal(const char *s);
# define YYERROR_VERBOSE 0
#endif
-/* Enabling the token table. */
-#ifndef YYTOKEN_TABLE
-# define YYTOKEN_TABLE 0
+/* In a future release of Bison, this section will be replaced
+ by #include "dtc-parser.tab.h". */
+#ifndef YY_YY_DTC_PARSER_TAB_H_INCLUDED
+# define YY_YY_DTC_PARSER_TAB_H_INCLUDED
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
#endif
-
/* Tokens. */
#ifndef YYTOKENTYPE
@@ -140,12 +145,10 @@ static unsigned char eval_char_literal(const char *s);
#endif
-
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
{
-
-/* Line 293 of yacc.c */
+/* Line 387 of yacc.c */
#line 40 "dtc-parser.y"
char *propnodename;
@@ -168,21 +171,36 @@ typedef union YYSTYPE
uint64_t integer;
-
-/* Line 293 of yacc.c */
-#line 174 "dtc-parser.tab.c"
+/* Line 387 of yacc.c */
+#line 176 "dtc-parser.tab.c"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
#endif
+extern YYSTYPE yylval;
-/* Copy the second part of user declarations. */
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+#endif /* !YY_YY_DTC_PARSER_TAB_H_INCLUDED */
-/* Line 343 of yacc.c */
-#line 186 "dtc-parser.tab.c"
+/* Copy the second part of user declarations. */
+
+/* Line 390 of yacc.c */
+#line 204 "dtc-parser.tab.c"
#ifdef short
# undef short
@@ -235,24 +253,33 @@ typedef short int yytype_int16;
# if defined YYENABLE_NLS && YYENABLE_NLS
# if ENABLE_NLS
# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
-# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# define YY_(Msgid) dgettext ("bison-runtime", Msgid)
# endif
# endif
# ifndef YY_
-# define YY_(msgid) msgid
+# define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef __attribute__
+/* This feature is available in gcc versions 2.5 and later. */
+# if (! defined __GNUC__ || __GNUC__ < 2 \
+ || (__GNUC__ == 2 && __GNUC_MINOR__ < 5))
+# define __attribute__(Spec) /* empty */
# endif
#endif
/* Suppress unused-variable warnings by "using" E. */
#if ! defined lint || defined __GNUC__
-# define YYUSE(e) ((void) (e))
+# define YYUSE(E) ((void) (E))
#else
-# define YYUSE(e) /* empty */
+# define YYUSE(E) /* empty */
#endif
+
/* Identity function, used to suppress warnings about constant conditions. */
#ifndef lint
-# define YYID(n) (n)
+# define YYID(N) (N)
#else
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
@@ -288,6 +315,7 @@ YYID (yyi)
# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
# ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
# endif
@@ -379,20 +407,20 @@ union yyalloc
#endif
#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
-/* Copy COUNT objects from FROM to TO. The source and destination do
+/* Copy COUNT objects from SRC to DST. The source and destination do
not overlap. */
# ifndef YYCOPY
# if defined __GNUC__ && 1 < __GNUC__
-# define YYCOPY(To, From, Count) \
- __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
# else
-# define YYCOPY(To, From, Count) \
- do \
- { \
- YYSIZE_T yyi; \
- for (yyi = 0; yyi < (Count); yyi++) \
- (To)[yyi] = (From)[yyi]; \
- } \
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
while (YYID (0))
# endif
# endif
@@ -513,7 +541,7 @@ static const yytype_uint16 yyrline[] =
};
#endif
-#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+#if YYDEBUG || YYERROR_VERBOSE || 0
/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
First, the terminals, then, starting at YYNTOKENS, nonterminals. */
static const char *const yytname[] =
@@ -530,7 +558,7 @@ static const char *const yytname[] =
"integer_expr", "integer_trinary", "integer_or", "integer_and",
"integer_bitor", "integer_bitxor", "integer_bitand", "integer_eq",
"integer_rela", "integer_shift", "integer_add", "integer_mul",
- "integer_unary", "bytestring", "subnodes", "subnode", 0
+ "integer_unary", "bytestring", "subnodes", "subnode", YY_NULL
};
#endif
@@ -655,10 +683,10 @@ static const yytype_uint8 yytable[] =
137, 0, 73, 139
};
-#define yypact_value_is_default(yystate) \
- ((yystate) == (-78))
+#define yypact_value_is_default(Yystate) \
+ (!!((Yystate) == (-78)))
-#define yytable_value_is_error(yytable_value) \
+#define yytable_value_is_error(Yytable_value) \
YYID (0)
static const yytype_int16 yycheck[] =
@@ -727,62 +755,35 @@ static const yytype_uint8 yystos[] =
#define YYRECOVERING() (!!yyerrstatus)
-#define YYBACKUP(Token, Value) \
-do \
- if (yychar == YYEMPTY && yylen == 1) \
- { \
- yychar = (Token); \
- yylval = (Value); \
- YYPOPSTACK (1); \
- goto yybackup; \
- } \
- else \
- { \
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
yyerror (YY_("syntax error: cannot back up")); \
YYERROR; \
} \
while (YYID (0))
-
+/* Error token number */
#define YYTERROR 1
#define YYERRCODE 256
-/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
- If N is 0, then set CURRENT to the empty location which ends
- the previous symbol: RHS[0] (always defined). */
-
-#define YYRHSLOC(Rhs, K) ((Rhs)[K])
-#ifndef YYLLOC_DEFAULT
-# define YYLLOC_DEFAULT(Current, Rhs, N) \
- do \
- if (YYID (N)) \
- { \
- (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
- (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
- (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
- (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
- } \
- else \
- { \
- (Current).first_line = (Current).last_line = \
- YYRHSLOC (Rhs, 0).last_line; \
- (Current).first_column = (Current).last_column = \
- YYRHSLOC (Rhs, 0).last_column; \
- } \
- while (YYID (0))
-#endif
-
-
/* This macro is provided for backward compatibility. */
-
#ifndef YY_LOCATION_PRINT
# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
#endif
/* YYLEX -- calling `yylex' with the right arguments. */
-
#ifdef YYLEX_PARAM
# define YYLEX yylex (YYLEX_PARAM)
#else
@@ -832,6 +833,8 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep)
YYSTYPE const * const yyvaluep;
#endif
{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
if (!yyvaluep)
return;
# ifdef YYPRINT
@@ -840,11 +843,7 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep)
# else
YYUSE (yyoutput);
# endif
- switch (yytype)
- {
- default:
- break;
- }
+ YYUSE (yytype);
}
@@ -1083,12 +1082,11 @@ static int
yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
yytype_int16 *yyssp, int yytoken)
{
- YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+ YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]);
YYSIZE_T yysize = yysize0;
- YYSIZE_T yysize1;
enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
/* Internationalized format string. */
- const char *yyformat = 0;
+ const char *yyformat = YY_NULL;
/* Arguments of yyformat. */
char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
/* Number of reported tokens (one for the "unexpected", one per
@@ -1148,11 +1146,13 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
break;
}
yyarg[yycount++] = yytname[yyx];
- yysize1 = yysize + yytnamerr (0, yytname[yyx]);
- if (! (yysize <= yysize1
- && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
- return 2;
- yysize = yysize1;
+ {
+ YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
}
}
}
@@ -1172,10 +1172,12 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
# undef YYCASE_
}
- yysize1 = yysize + yystrlen (yyformat);
- if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
- return 2;
- yysize = yysize1;
+ {
+ YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
if (*yymsg_alloc < yysize)
{
@@ -1231,36 +1233,26 @@ yydestruct (yymsg, yytype, yyvaluep)
yymsg = "Deleting";
YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
- switch (yytype)
- {
-
- default:
- break;
- }
+ YYUSE (yytype);
}
-/* Prevent warnings from -Wmissing-prototypes. */
-#ifdef YYPARSE_PARAM
-#if defined __STDC__ || defined __cplusplus
-int yyparse (void *YYPARSE_PARAM);
-#else
-int yyparse ();
-#endif
-#else /* ! YYPARSE_PARAM */
-#if defined __STDC__ || defined __cplusplus
-int yyparse (void);
-#else
-int yyparse ();
-#endif
-#endif /* ! YYPARSE_PARAM */
/* The lookahead symbol. */
int yychar;
+
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
/* The semantic value of the lookahead symbol. */
-YYSTYPE yylval;
+YYSTYPE yylval YY_INITIAL_VALUE(yyval_default);
/* Number of syntax errors so far. */
int yynerrs;
@@ -1300,7 +1292,7 @@ yyparse ()
`yyss': related to states.
`yyvs': related to semantic values.
- Refer to the stacks thru separate pointers, to allow yyoverflow
+ Refer to the stacks through separate pointers, to allow yyoverflow
to reallocate them elsewhere. */
/* The state stack. */
@@ -1318,7 +1310,7 @@ yyparse ()
int yyn;
int yyresult;
/* Lookahead token as an internal (translated) token number. */
- int yytoken;
+ int yytoken = 0;
/* The variables used to return semantic value and location from the
action routines. */
YYSTYPE yyval;
@@ -1336,9 +1328,8 @@ yyparse ()
Keep to zero when no symbol should be popped. */
int yylen = 0;
- yytoken = 0;
- yyss = yyssa;
- yyvs = yyvsa;
+ yyssp = yyss = yyssa;
+ yyvsp = yyvs = yyvsa;
yystacksize = YYINITDEPTH;
YYDPRINTF ((stderr, "Starting parse\n"));
@@ -1347,14 +1338,6 @@ yyparse ()
yyerrstatus = 0;
yynerrs = 0;
yychar = YYEMPTY; /* Cause a token to be read. */
-
- /* Initialize stack pointers.
- Waste one element of value and location stack
- so that they stay on the same level as the state stack.
- The wasted elements are never initialized. */
- yyssp = yyss;
- yyvsp = yyvs;
-
goto yysetstate;
/*------------------------------------------------------------.
@@ -1495,7 +1478,9 @@ yybackup:
yychar = YYEMPTY;
yystate = yyn;
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
*++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
goto yynewstate;
@@ -1532,8 +1517,7 @@ yyreduce:
switch (yyn)
{
case 2:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 110 "dtc-parser.y"
{
the_boot_info = build_boot_info((yyvsp[(3) - (4)].re), (yyvsp[(4) - (4)].node),
@@ -1542,8 +1526,7 @@ yyreduce:
break;
case 3:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 118 "dtc-parser.y"
{
(yyval.re) = NULL;
@@ -1551,8 +1534,7 @@ yyreduce:
break;
case 4:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 122 "dtc-parser.y"
{
(yyval.re) = chain_reserve_entry((yyvsp[(1) - (2)].re), (yyvsp[(2) - (2)].re));
@@ -1560,8 +1542,7 @@ yyreduce:
break;
case 5:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 129 "dtc-parser.y"
{
(yyval.re) = build_reserve_entry((yyvsp[(2) - (4)].integer), (yyvsp[(3) - (4)].integer));
@@ -1569,8 +1550,7 @@ yyreduce:
break;
case 6:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 133 "dtc-parser.y"
{
add_label(&(yyvsp[(2) - (2)].re)->labels, (yyvsp[(1) - (2)].labelref));
@@ -1579,8 +1559,7 @@ yyreduce:
break;
case 7:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 141 "dtc-parser.y"
{
(yyval.node) = name_node((yyvsp[(2) - (2)].node), "");
@@ -1588,8 +1567,7 @@ yyreduce:
break;
case 8:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 145 "dtc-parser.y"
{
(yyval.node) = merge_nodes((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
@@ -1597,8 +1575,7 @@ yyreduce:
break;
case 9:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 149 "dtc-parser.y"
{
struct node *target = get_node_by_ref((yyvsp[(1) - (3)].node), (yyvsp[(2) - (3)].labelref));
@@ -1612,8 +1589,7 @@ yyreduce:
break;
case 10:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 159 "dtc-parser.y"
{
struct node *target = get_node_by_ref((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].labelref));
@@ -1628,8 +1604,7 @@ yyreduce:
break;
case 11:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 173 "dtc-parser.y"
{
(yyval.node) = build_node((yyvsp[(2) - (5)].proplist), (yyvsp[(3) - (5)].nodelist));
@@ -1637,8 +1612,7 @@ yyreduce:
break;
case 12:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 180 "dtc-parser.y"
{
(yyval.proplist) = NULL;
@@ -1646,8 +1620,7 @@ yyreduce:
break;
case 13:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 184 "dtc-parser.y"
{
(yyval.proplist) = chain_property((yyvsp[(2) - (2)].prop), (yyvsp[(1) - (2)].proplist));
@@ -1655,8 +1628,7 @@ yyreduce:
break;
case 14:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 191 "dtc-parser.y"
{
(yyval.prop) = build_property((yyvsp[(1) - (4)].propnodename), (yyvsp[(3) - (4)].data));
@@ -1664,8 +1636,7 @@ yyreduce:
break;
case 15:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 195 "dtc-parser.y"
{
(yyval.prop) = build_property((yyvsp[(1) - (2)].propnodename), empty_data);
@@ -1673,8 +1644,7 @@ yyreduce:
break;
case 16:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 199 "dtc-parser.y"
{
(yyval.prop) = build_property_delete((yyvsp[(2) - (3)].propnodename));
@@ -1682,8 +1652,7 @@ yyreduce:
break;
case 17:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 203 "dtc-parser.y"
{
add_label(&(yyvsp[(2) - (2)].prop)->labels, (yyvsp[(1) - (2)].labelref));
@@ -1692,8 +1661,7 @@ yyreduce:
break;
case 18:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 211 "dtc-parser.y"
{
(yyval.data) = data_merge((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].data));
@@ -1701,8 +1669,7 @@ yyreduce:
break;
case 19:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 215 "dtc-parser.y"
{
(yyval.data) = data_merge((yyvsp[(1) - (3)].data), (yyvsp[(2) - (3)].array).data);
@@ -1710,8 +1677,7 @@ yyreduce:
break;
case 20:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 219 "dtc-parser.y"
{
(yyval.data) = data_merge((yyvsp[(1) - (4)].data), (yyvsp[(3) - (4)].data));
@@ -1719,8 +1685,7 @@ yyreduce:
break;
case 21:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 223 "dtc-parser.y"
{
(yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), REF_PATH, (yyvsp[(2) - (2)].labelref));
@@ -1728,8 +1693,7 @@ yyreduce:
break;
case 22:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 227 "dtc-parser.y"
{
FILE *f = srcfile_relative_open((yyvsp[(4) - (9)].data).val, NULL);
@@ -1750,8 +1714,7 @@ yyreduce:
break;
case 23:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 244 "dtc-parser.y"
{
FILE *f = srcfile_relative_open((yyvsp[(4) - (5)].data).val, NULL);
@@ -1765,8 +1728,7 @@ yyreduce:
break;
case 24:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 254 "dtc-parser.y"
{
(yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref));
@@ -1774,8 +1736,7 @@ yyreduce:
break;
case 25:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 261 "dtc-parser.y"
{
(yyval.data) = empty_data;
@@ -1783,8 +1744,7 @@ yyreduce:
break;
case 26:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 265 "dtc-parser.y"
{
(yyval.data) = (yyvsp[(1) - (2)].data);
@@ -1792,8 +1752,7 @@ yyreduce:
break;
case 27:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 269 "dtc-parser.y"
{
(yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref));
@@ -1801,8 +1760,7 @@ yyreduce:
break;
case 28:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 276 "dtc-parser.y"
{
(yyval.array).data = empty_data;
@@ -1821,8 +1779,7 @@ yyreduce:
break;
case 29:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 291 "dtc-parser.y"
{
(yyval.array).data = empty_data;
@@ -1831,8 +1788,7 @@ yyreduce:
break;
case 30:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 296 "dtc-parser.y"
{
if ((yyvsp[(1) - (2)].array).bits < 64) {
@@ -1856,8 +1812,7 @@ yyreduce:
break;
case 31:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 316 "dtc-parser.y"
{
uint64_t val = ~0ULL >> (64 - (yyvsp[(1) - (2)].array).bits);
@@ -1875,8 +1830,7 @@ yyreduce:
break;
case 32:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 330 "dtc-parser.y"
{
(yyval.array).data = data_add_marker((yyvsp[(1) - (2)].array).data, LABEL, (yyvsp[(2) - (2)].labelref));
@@ -1884,8 +1838,7 @@ yyreduce:
break;
case 33:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 337 "dtc-parser.y"
{
(yyval.integer) = eval_literal((yyvsp[(1) - (1)].literal), 0, 64);
@@ -1893,8 +1846,7 @@ yyreduce:
break;
case 34:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 341 "dtc-parser.y"
{
(yyval.integer) = eval_char_literal((yyvsp[(1) - (1)].literal));
@@ -1902,8 +1854,7 @@ yyreduce:
break;
case 35:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 345 "dtc-parser.y"
{
(yyval.integer) = (yyvsp[(2) - (3)].integer);
@@ -1911,162 +1862,139 @@ yyreduce:
break;
case 38:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 356 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (5)].integer) ? (yyvsp[(3) - (5)].integer) : (yyvsp[(5) - (5)].integer); }
break;
case 40:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 361 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) || (yyvsp[(3) - (3)].integer); }
break;
case 42:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 366 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) && (yyvsp[(3) - (3)].integer); }
break;
case 44:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 371 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) | (yyvsp[(3) - (3)].integer); }
break;
case 46:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 376 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) ^ (yyvsp[(3) - (3)].integer); }
break;
case 48:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 381 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) & (yyvsp[(3) - (3)].integer); }
break;
case 50:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 386 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) == (yyvsp[(3) - (3)].integer); }
break;
case 51:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 387 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) != (yyvsp[(3) - (3)].integer); }
break;
case 53:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 392 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) < (yyvsp[(3) - (3)].integer); }
break;
case 54:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 393 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) > (yyvsp[(3) - (3)].integer); }
break;
case 55:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 394 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) <= (yyvsp[(3) - (3)].integer); }
break;
case 56:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 395 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) >= (yyvsp[(3) - (3)].integer); }
break;
case 57:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 399 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) << (yyvsp[(3) - (3)].integer); }
break;
case 58:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 400 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) >> (yyvsp[(3) - (3)].integer); }
break;
case 60:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 405 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) + (yyvsp[(3) - (3)].integer); }
break;
case 61:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 406 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) - (yyvsp[(3) - (3)].integer); }
break;
case 63:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 411 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) * (yyvsp[(3) - (3)].integer); }
break;
case 64:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 412 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) / (yyvsp[(3) - (3)].integer); }
break;
case 65:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 413 "dtc-parser.y"
{ (yyval.integer) = (yyvsp[(1) - (3)].integer) % (yyvsp[(3) - (3)].integer); }
break;
case 68:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 419 "dtc-parser.y"
{ (yyval.integer) = -(yyvsp[(2) - (2)].integer); }
break;
case 69:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 420 "dtc-parser.y"
{ (yyval.integer) = ~(yyvsp[(2) - (2)].integer); }
break;
case 70:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 421 "dtc-parser.y"
{ (yyval.integer) = !(yyvsp[(2) - (2)].integer); }
break;
case 71:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 426 "dtc-parser.y"
{
(yyval.data) = empty_data;
@@ -2074,8 +2002,7 @@ yyreduce:
break;
case 72:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 430 "dtc-parser.y"
{
(yyval.data) = data_append_byte((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].byte));
@@ -2083,8 +2010,7 @@ yyreduce:
break;
case 73:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 434 "dtc-parser.y"
{
(yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref));
@@ -2092,8 +2018,7 @@ yyreduce:
break;
case 74:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 441 "dtc-parser.y"
{
(yyval.nodelist) = NULL;
@@ -2101,8 +2026,7 @@ yyreduce:
break;
case 75:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 445 "dtc-parser.y"
{
(yyval.nodelist) = chain_node((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].nodelist));
@@ -2110,8 +2034,7 @@ yyreduce:
break;
case 76:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 449 "dtc-parser.y"
{
print_error("syntax error: properties must precede subnodes");
@@ -2120,8 +2043,7 @@ yyreduce:
break;
case 77:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 457 "dtc-parser.y"
{
(yyval.node) = name_node((yyvsp[(2) - (2)].node), (yyvsp[(1) - (2)].propnodename));
@@ -2129,8 +2051,7 @@ yyreduce:
break;
case 78:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 461 "dtc-parser.y"
{
(yyval.node) = name_node(build_node_delete(), (yyvsp[(2) - (3)].propnodename));
@@ -2138,8 +2059,7 @@ yyreduce:
break;
case 79:
-
-/* Line 1806 of yacc.c */
+/* Line 1787 of yacc.c */
#line 465 "dtc-parser.y"
{
add_label(&(yyvsp[(2) - (2)].node)->labels, (yyvsp[(1) - (2)].labelref));
@@ -2148,9 +2068,8 @@ yyreduce:
break;
-
-/* Line 1806 of yacc.c */
-#line 2154 "dtc-parser.tab.c"
+/* Line 1787 of yacc.c */
+#line 2073 "dtc-parser.tab.c"
default: break;
}
/* User semantic actions sometimes alter yychar, and that requires
@@ -2313,7 +2232,9 @@ yyerrlab1:
YY_STACK_PRINT (yyss, yyssp);
}
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
*++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
/* Shift the error token. */
@@ -2337,7 +2258,7 @@ yyabortlab:
yyresult = 1;
goto yyreturn;
-#if !defined(yyoverflow) || YYERROR_VERBOSE
+#if !defined yyoverflow || YYERROR_VERBOSE
/*-------------------------------------------------.
| yyexhaustedlab -- memory exhaustion comes here. |
`-------------------------------------------------*/
@@ -2379,8 +2300,7 @@ yyreturn:
}
-
-/* Line 2067 of yacc.c */
+/* Line 2050 of yacc.c */
#line 471 "dtc-parser.y"
@@ -2444,4 +2364,3 @@ static unsigned char eval_char_literal(const char *s)
return c;
}
-
diff --git a/scripts/dtc/dtc-parser.tab.h_shipped b/scripts/dtc/dtc-parser.tab.h_shipped
index 25d3b88c6132..b2e7a86cd85e 100644
--- a/scripts/dtc/dtc-parser.tab.h_shipped
+++ b/scripts/dtc/dtc-parser.tab.h_shipped
@@ -1,8 +1,8 @@
-/* A Bison parser, made by GNU Bison 2.5. */
+/* A Bison parser, made by GNU Bison 2.7.12-4996. */
/* Bison interface for Yacc-like parsers in C
- Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+ Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, 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
@@ -30,6 +30,15 @@
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
+#ifndef YY_YY_DTC_PARSER_TAB_H_INCLUDED
+# define YY_YY_DTC_PARSER_TAB_H_INCLUDED
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
/* Tokens. */
#ifndef YYTOKENTYPE
@@ -63,12 +72,10 @@
#endif
-
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
{
-
-/* Line 2068 of yacc.c */
+/* Line 2053 of yacc.c */
#line 40 "dtc-parser.y"
char *propnodename;
@@ -91,9 +98,8 @@ typedef union YYSTYPE
uint64_t integer;
-
-/* Line 2068 of yacc.c */
-#line 97 "dtc-parser.tab.h"
+/* Line 2053 of yacc.c */
+#line 103 "dtc-parser.tab.h"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
@@ -102,4 +108,18 @@ typedef union YYSTYPE
extern YYSTYPE yylval;
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+#endif /* !YY_YY_DTC_PARSER_TAB_H_INCLUDED */
diff --git a/scripts/dtc/dtc.c b/scripts/dtc/dtc.c
index a375683c1534..e3c96536fd9d 100644
--- a/scripts/dtc/dtc.c
+++ b/scripts/dtc/dtc.c
@@ -21,8 +21,6 @@
#include "dtc.h"
#include "srcpos.h"
-#include "version_gen.h"
-
/*
* Command line options
*/
@@ -49,55 +47,60 @@ static void fill_fullpaths(struct node *tree, const char *prefix)
fill_fullpaths(child, tree->fullpath);
}
-static void __attribute__ ((noreturn)) usage(void)
-{
- fprintf(stderr, "Usage:\n");
- fprintf(stderr, "\tdtc [options] <input file>\n");
- fprintf(stderr, "\nOptions:\n");
- fprintf(stderr, "\t-h\n");
- fprintf(stderr, "\t\tThis help text\n");
- fprintf(stderr, "\t-q\n");
- fprintf(stderr, "\t\tQuiet: -q suppress warnings, -qq errors, -qqq all\n");
- fprintf(stderr, "\t-I <input format>\n");
- fprintf(stderr, "\t\tInput formats are:\n");
- fprintf(stderr, "\t\t\tdts - device tree source text\n");
- fprintf(stderr, "\t\t\tdtb - device tree blob\n");
- fprintf(stderr, "\t\t\tfs - /proc/device-tree style directory\n");
- fprintf(stderr, "\t-o <output file>\n");
- fprintf(stderr, "\t-O <output format>\n");
- fprintf(stderr, "\t\tOutput formats are:\n");
- fprintf(stderr, "\t\t\tdts - device tree source text\n");
- fprintf(stderr, "\t\t\tdtb - device tree blob\n");
- fprintf(stderr, "\t\t\tasm - assembler source\n");
- fprintf(stderr, "\t-V <output version>\n");
- fprintf(stderr, "\t\tBlob version to produce, defaults to %d (relevant for dtb\n\t\tand asm output only)\n", DEFAULT_FDT_VERSION);
- fprintf(stderr, "\t-d <output dependency file>\n");
- fprintf(stderr, "\t-R <number>\n");
- fprintf(stderr, "\t\tMake space for <number> reserve map entries (relevant for \n\t\tdtb and asm output only)\n");
- fprintf(stderr, "\t-S <bytes>\n");
- fprintf(stderr, "\t\tMake the blob at least <bytes> long (extra space)\n");
- fprintf(stderr, "\t-p <bytes>\n");
- fprintf(stderr, "\t\tAdd padding to the blob of <bytes> long (extra space)\n");
- fprintf(stderr, "\t-b <number>\n");
- fprintf(stderr, "\t\tSet the physical boot cpu\n");
- fprintf(stderr, "\t-f\n");
- fprintf(stderr, "\t\tForce - try to produce output even if the input tree has errors\n");
- fprintf(stderr, "\t-i\n");
- fprintf(stderr, "\t\tAdd a path to search for include files\n");
- fprintf(stderr, "\t-s\n");
- fprintf(stderr, "\t\tSort nodes and properties before outputting (only useful for\n\t\tcomparing trees)\n");
- fprintf(stderr, "\t-v\n");
- fprintf(stderr, "\t\tPrint DTC version and exit\n");
- fprintf(stderr, "\t-H <phandle format>\n");
- fprintf(stderr, "\t\tphandle formats are:\n");
- fprintf(stderr, "\t\t\tlegacy - \"linux,phandle\" properties only\n");
- fprintf(stderr, "\t\t\tepapr - \"phandle\" properties only\n");
- fprintf(stderr, "\t\t\tboth - Both \"linux,phandle\" and \"phandle\" properties\n");
- fprintf(stderr, "\t-W [no-]<checkname>\n");
- fprintf(stderr, "\t-E [no-]<checkname>\n");
- fprintf(stderr, "\t\t\tenable or disable warnings and errors\n");
- exit(3);
-}
+/* Usage related data. */
+static const char usage_synopsis[] = "dtc [options] <input file>";
+static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv";
+static struct option const usage_long_opts[] = {
+ {"quiet", no_argument, NULL, 'q'},
+ {"in-format", a_argument, NULL, 'I'},
+ {"out", a_argument, NULL, 'o'},
+ {"out-format", a_argument, NULL, 'O'},
+ {"out-version", a_argument, NULL, 'V'},
+ {"out-dependency", a_argument, NULL, 'd'},
+ {"reserve", a_argument, NULL, 'R'},
+ {"space", a_argument, NULL, 'S'},
+ {"pad", a_argument, NULL, 'p'},
+ {"boot-cpu", a_argument, NULL, 'b'},
+ {"force", no_argument, NULL, 'f'},
+ {"include", a_argument, NULL, 'i'},
+ {"sort", no_argument, NULL, 's'},
+ {"phandle", a_argument, NULL, 'H'},
+ {"warning", a_argument, NULL, 'W'},
+ {"error", a_argument, NULL, 'E'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+ {NULL, no_argument, NULL, 0x0},
+};
+static const char * const usage_opts_help[] = {
+ "\n\tQuiet: -q suppress warnings, -qq errors, -qqq all",
+ "\n\tInput formats are:\n"
+ "\t\tdts - device tree source text\n"
+ "\t\tdtb - device tree blob\n"
+ "\t\tfs - /proc/device-tree style directory",
+ "\n\tOutput file",
+ "\n\tOutput formats are:\n"
+ "\t\tdts - device tree source text\n"
+ "\t\tdtb - device tree blob\n"
+ "\t\tasm - assembler source",
+ "\n\tBlob version to produce, defaults to %d (for dtb and asm output)", //, DEFAULT_FDT_VERSION);
+ "\n\tOutput dependency file",
+ "\n\ttMake space for <number> reserve map entries (for dtb and asm output)",
+ "\n\tMake the blob at least <bytes> long (extra space)",
+ "\n\tAdd padding to the blob of <bytes> long (extra space)",
+ "\n\tSet the physical boot cpu",
+ "\n\tTry to produce output even if the input tree has errors",
+ "\n\tAdd a path to search for include files",
+ "\n\tSort nodes and properties before outputting (useful for comparing trees)",
+ "\n\tValid phandle formats are:\n"
+ "\t\tlegacy - \"linux,phandle\" properties only\n"
+ "\t\tepapr - \"phandle\" properties only\n"
+ "\t\tboth - Both \"linux,phandle\" and \"phandle\" properties",
+ "\n\tEnable/disable warnings (prefix with \"no-\")",
+ "\n\tEnable/disable errors (prefix with \"no-\")",
+ "\n\tPrint this help and exit",
+ "\n\tPrint version and exit",
+ NULL,
+};
int main(int argc, char *argv[])
{
@@ -118,8 +121,7 @@ int main(int argc, char *argv[])
minsize = 0;
padsize = 0;
- while ((opt = getopt(argc, argv, "hI:O:o:V:d:R:S:p:fqb:i:vH:sW:E:"))
- != EOF) {
+ while ((opt = util_getopt_long()) != EOF) {
switch (opt) {
case 'I':
inform = optarg;
@@ -158,8 +160,7 @@ int main(int argc, char *argv[])
srcfile_add_search_path(optarg);
break;
case 'v':
- printf("Version: %s\n", DTC_VERSION);
- exit(0);
+ util_version();
case 'H':
if (streq(optarg, "legacy"))
phandle_format = PHANDLE_LEGACY;
@@ -185,13 +186,14 @@ int main(int argc, char *argv[])
break;
case 'h':
+ usage(NULL);
default:
- usage();
+ usage("unknown option");
}
}
if (argc > (optind+1))
- usage();
+ usage("missing files");
else if (argc < (optind+1))
arg = "-";
else
@@ -201,9 +203,6 @@ int main(int argc, char *argv[])
if (minsize && padsize)
die("Can't set both -p and -S\n");
- if (minsize)
- fprintf(stderr, "DTC: Use of \"-S\" is deprecated; it will be removed soon, use \"-p\" instead\n");
-
if (depname) {
depfile = fopen(depname, "w");
if (!depfile)
diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h
index 3e42a071070e..264a20cf66a8 100644
--- a/scripts/dtc/dtc.h
+++ b/scripts/dtc/dtc.h
@@ -66,7 +66,6 @@ typedef uint32_t cell_t;
#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/* Data blobs */
enum markertype {
diff --git a/scripts/dtc/srcpos.c b/scripts/dtc/srcpos.c
index 246ab4bc0d9d..c20bc5315bc1 100644
--- a/scripts/dtc/srcpos.c
+++ b/scripts/dtc/srcpos.c
@@ -297,9 +297,9 @@ srcpos_verror(struct srcpos *pos, char const *fmt, va_list va)
srcstr = srcpos_string(pos);
- fprintf(stdout, "Error: %s ", srcstr);
- vfprintf(stdout, fmt, va);
- fprintf(stdout, "\n");
+ fprintf(stderr, "Error: %s ", srcstr);
+ vfprintf(stderr, fmt, va);
+ fprintf(stderr, "\n");
}
void
diff --git a/scripts/dtc/update-dtc-source.sh b/scripts/dtc/update-dtc-source.sh
new file mode 100755
index 000000000000..feb01ef26be4
--- /dev/null
+++ b/scripts/dtc/update-dtc-source.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+# Simple script to update the version of DTC carried by the Linux kernel
+#
+# This script assumes that the dtc and the linux git trees are in the
+# same directory. After building dtc in the dtc directory, it copies the
+# source files and generated source files into the scripts/dtc directory
+# in the kernel and creates a git commit updating them to the new
+# version.
+#
+# Usage: from the top level Linux source tree, run:
+# $ ./scripts/dtc/update-dtc-source.sh
+#
+# The script will change into the dtc tree, build and test dtc, copy the
+# relevant files into the kernel tree and create a git commit. The commit
+# message will need to be modified to reflect the version of DTC being
+# imported
+#
+# TODO:
+# This script is pretty basic, but it is seldom used so a few manual tasks
+# aren't a big deal. If anyone is interested in making it more robust, the
+# the following would be nice:
+# * Actually fail to complete if any testcase fails.
+# - The dtc "make check" target needs to return a failure
+# * Extract the version number from the dtc repo for the commit message
+# * Build dtc in the kernel tree
+# * run 'make check" on dtc built from the kernel tree
+
+set -ev
+
+DTC_UPSTREAM_PATH=`pwd`/../dtc
+DTC_LINUX_PATH=`pwd`/scripts/dtc
+
+DTC_SOURCE="checks.c data.c dtc.c dtc.h flattree.c fstree.c livetree.c srcpos.c \
+ srcpos.h treesource.c util.c util.h version_gen.h Makefile.dtc \
+ dtc-lexer.l dtc-parser.y"
+DTC_GENERATED="dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h"
+
+# Build DTC
+cd $DTC_UPSTREAM_PATH
+make clean
+make check
+
+# Copy the files into the Linux tree
+cd $DTC_LINUX_PATH
+for f in $DTC_SOURCE; do
+ cp ${DTC_UPSTREAM_PATH}/${f} ${f}
+ git add ${f}
+done
+for f in $DTC_GENERATED; do
+ cp ${DTC_UPSTREAM_PATH}/$f ${f}_shipped
+ git add ${f}_shipped
+done
+
+git commit -e -v -m "scripts/dtc: Update to upstream version [CHANGEME]"
diff --git a/scripts/dtc/util.c b/scripts/dtc/util.c
index 2422c34e11df..3055c16e980d 100644
--- a/scripts/dtc/util.c
+++ b/scripts/dtc/util.c
@@ -34,6 +34,7 @@
#include "libfdt.h"
#include "util.h"
+#include "version_gen.h"
char *xstrdup(const char *s)
{
@@ -72,7 +73,7 @@ char *join_path(const char *path, const char *name)
int util_is_printable_string(const void *data, int len)
{
const char *s = data;
- const char *ss;
+ const char *ss, *se;
/* zero length is not */
if (len == 0)
@@ -82,13 +83,19 @@ int util_is_printable_string(const void *data, int len)
if (s[len - 1] != '\0')
return 0;
- ss = s;
- while (*s && isprint(*s))
- s++;
+ se = s + len;
- /* not zero, or not done yet */
- if (*s != '\0' || (s + 1 - ss) < len)
- return 0;
+ while (s < se) {
+ ss = s;
+ while (s < se && *s && isprint(*s))
+ s++;
+
+ /* not zero, or not done yet */
+ if (*s != '\0' || s == ss)
+ return 0;
+
+ s++;
+ }
return 1;
}
@@ -191,7 +198,7 @@ char get_escape_char(const char *s, int *i)
return val;
}
-int utilfdt_read_err(const char *filename, char **buffp)
+int utilfdt_read_err_len(const char *filename, char **buffp, off_t *len)
{
int fd = 0; /* assume stdin */
char *buf = NULL;
@@ -206,12 +213,12 @@ int utilfdt_read_err(const char *filename, char **buffp)
}
/* Loop until we have read everything */
- buf = malloc(bufsize);
+ buf = xmalloc(bufsize);
do {
/* Expand the buffer to hold the next chunk */
if (offset == bufsize) {
bufsize *= 2;
- buf = realloc(buf, bufsize);
+ buf = xrealloc(buf, bufsize);
if (!buf) {
ret = ENOMEM;
break;
@@ -232,13 +239,20 @@ int utilfdt_read_err(const char *filename, char **buffp)
free(buf);
else
*buffp = buf;
+ *len = bufsize;
return ret;
}
-char *utilfdt_read(const char *filename)
+int utilfdt_read_err(const char *filename, char **buffp)
+{
+ off_t len;
+ return utilfdt_read_err_len(filename, buffp, &len);
+}
+
+char *utilfdt_read_len(const char *filename, off_t *len)
{
char *buff;
- int ret = utilfdt_read_err(filename, &buff);
+ int ret = utilfdt_read_err_len(filename, &buff, len);
if (ret) {
fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename,
@@ -249,6 +263,12 @@ char *utilfdt_read(const char *filename)
return buff;
}
+char *utilfdt_read(const char *filename)
+{
+ off_t len;
+ return utilfdt_read_len(filename, &len);
+}
+
int utilfdt_write_err(const char *filename, const void *blob)
{
int fd = 1; /* assume stdout */
@@ -329,3 +349,100 @@ int utilfdt_decode_type(const char *fmt, int *type, int *size)
return -1;
return 0;
}
+
+void utilfdt_print_data(const char *data, int len)
+{
+ int i;
+ const char *p = data;
+ const char *s;
+
+ /* no data, don't print */
+ if (len == 0)
+ return;
+
+ if (util_is_printable_string(data, len)) {
+ printf(" = ");
+
+ s = data;
+ do {
+ printf("\"%s\"", s);
+ s += strlen(s) + 1;
+ if (s < data + len)
+ printf(", ");
+ } while (s < data + len);
+
+ } else if ((len % 4) == 0) {
+ const uint32_t *cell = (const uint32_t *)data;
+
+ printf(" = <");
+ for (i = 0; i < len; i += 4)
+ printf("0x%08x%s", fdt32_to_cpu(cell[i]),
+ i < (len - 4) ? " " : "");
+ printf(">");
+ } else {
+ printf(" = [");
+ for (i = 0; i < len; i++)
+ printf("%02x%s", *p++, i < len - 1 ? " " : "");
+ printf("]");
+ }
+}
+
+void util_version(void)
+{
+ printf("Version: %s\n", DTC_VERSION);
+ exit(0);
+}
+
+void util_usage(const char *errmsg, const char *synopsis,
+ const char *short_opts, struct option const long_opts[],
+ const char * const opts_help[])
+{
+ FILE *fp = errmsg ? stderr : stdout;
+ const char a_arg[] = "<arg>";
+ size_t a_arg_len = strlen(a_arg) + 1;
+ size_t i;
+ int optlen;
+
+ fprintf(fp,
+ "Usage: %s\n"
+ "\n"
+ "Options: -[%s]\n", synopsis, short_opts);
+
+ /* prescan the --long opt length to auto-align */
+ optlen = 0;
+ for (i = 0; long_opts[i].name; ++i) {
+ /* +1 is for space between --opt and help text */
+ int l = strlen(long_opts[i].name) + 1;
+ if (long_opts[i].has_arg == a_argument)
+ l += a_arg_len;
+ if (optlen < l)
+ optlen = l;
+ }
+
+ for (i = 0; long_opts[i].name; ++i) {
+ /* helps when adding new applets or options */
+ assert(opts_help[i] != NULL);
+
+ /* first output the short flag if it has one */
+ if (long_opts[i].val > '~')
+ fprintf(fp, " ");
+ else
+ fprintf(fp, " -%c, ", long_opts[i].val);
+
+ /* then the long flag */
+ if (long_opts[i].has_arg == no_argument)
+ fprintf(fp, "--%-*s", optlen, long_opts[i].name);
+ else
+ fprintf(fp, "--%s %s%*s", long_opts[i].name, a_arg,
+ (int)(optlen - strlen(long_opts[i].name) - a_arg_len), "");
+
+ /* finally the help text */
+ fprintf(fp, "%s\n", opts_help[i]);
+ }
+
+ if (errmsg) {
+ fprintf(fp, "\nError: %s\n", errmsg);
+ exit(EXIT_FAILURE);
+ } else
+ exit(EXIT_SUCCESS);
+}
diff --git a/scripts/dtc/util.h b/scripts/dtc/util.h
index c8eb45d9f04b..8f40b4499359 100644
--- a/scripts/dtc/util.h
+++ b/scripts/dtc/util.h
@@ -2,6 +2,7 @@
#define _UTIL_H
#include <stdarg.h>
+#include <getopt.h>
/*
* Copyright 2011 The Chromium Authors, All Rights Reserved.
@@ -23,7 +24,9 @@
* USA
*/
-static inline void __attribute__((noreturn)) die(char * str, ...)
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+static inline void __attribute__((noreturn)) die(const char *str, ...)
{
va_list ap;
@@ -57,12 +60,14 @@ extern char *xstrdup(const char *s);
extern char *join_path(const char *path, const char *name);
/**
- * Check a string of a given length to see if it is all printable and
- * has a valid terminator.
+ * Check a property of a given length to see if it is all printable and
+ * has a valid terminator. The property can contain either a single string,
+ * or multiple strings each of non-zero length.
*
* @param data The string to check
* @param len The string length including terminator
- * @return 1 if a valid printable string, 0 if not */
+ * @return 1 if a valid printable string, 0 if not
+ */
int util_is_printable_string(const void *data, int len);
/*
@@ -83,6 +88,13 @@ char get_escape_char(const char *s, int *i);
char *utilfdt_read(const char *filename);
/**
+ * Like utilfdt_read(), but also passes back the size of the file read.
+ *
+ * @param len If non-NULL, the amount of data we managed to read
+ */
+char *utilfdt_read_len(const char *filename, off_t *len);
+
+/**
* Read a device tree file into a buffer. Does not report errors, but only
* returns them. The value returned can be passed to strerror() to obtain
* an error message for the user.
@@ -93,6 +105,12 @@ char *utilfdt_read(const char *filename);
*/
int utilfdt_read_err(const char *filename, char **buffp);
+/**
+ * Like utilfdt_read_err(), but also passes back the size of the file read.
+ *
+ * @param len If non-NULL, the amount of data we managed to read
+ */
+int utilfdt_read_err_len(const char *filename, char **buffp, off_t *len);
/**
* Write a device tree buffer to a file. This will report any errors on
@@ -148,6 +166,85 @@ int utilfdt_decode_type(const char *fmt, int *type, int *size);
#define USAGE_TYPE_MSG \
"<type>\ts=string, i=int, u=unsigned, x=hex\n" \
"\tOptional modifier prefix:\n" \
- "\t\thh or b=byte, h=2 byte, l=4 byte (default)\n";
+ "\t\thh or b=byte, h=2 byte, l=4 byte (default)";
+
+/**
+ * Print property data in a readable format to stdout
+ *
+ * Properties that look like strings will be printed as strings. Otherwise
+ * the data will be displayed either as cells (if len is a multiple of 4
+ * bytes) or bytes.
+ *
+ * If len is 0 then this function does nothing.
+ *
+ * @param data Pointers to property data
+ * @param len Length of property data
+ */
+void utilfdt_print_data(const char *data, int len);
+
+/**
+ * Show source version and exit
+ */
+void util_version(void) __attribute__((noreturn));
+
+/**
+ * Show usage and exit
+ *
+ * This helps standardize the output of various utils. You most likely want
+ * to use the usage() helper below rather than call this.
+ *
+ * @param errmsg If non-NULL, an error message to display
+ * @param synopsis The initial example usage text (and possible examples)
+ * @param short_opts The string of short options
+ * @param long_opts The structure of long options
+ * @param opts_help An array of help strings (should align with long_opts)
+ */
+void util_usage(const char *errmsg, const char *synopsis,
+ const char *short_opts, struct option const long_opts[],
+ const char * const opts_help[]) __attribute__((noreturn));
+
+/**
+ * Show usage and exit
+ *
+ * If you name all your usage variables with usage_xxx, then you can call this
+ * help macro rather than expanding all arguments yourself.
+ *
+ * @param errmsg If non-NULL, an error message to display
+ */
+#define usage(errmsg) \
+ util_usage(errmsg, usage_synopsis, usage_short_opts, \
+ usage_long_opts, usage_opts_help)
+
+/**
+ * Call getopt_long() with standard options
+ *
+ * Since all util code runs getopt in the same way, provide a helper.
+ */
+#define util_getopt_long() getopt_long(argc, argv, usage_short_opts, \
+ usage_long_opts, NULL)
+
+/* Helper for aligning long_opts array */
+#define a_argument required_argument
+
+/* Helper for usage_short_opts string constant */
+#define USAGE_COMMON_SHORT_OPTS "hV"
+
+/* Helper for usage_long_opts option array */
+#define USAGE_COMMON_LONG_OPTS \
+ {"help", no_argument, NULL, 'h'}, \
+ {"version", no_argument, NULL, 'V'}, \
+ {NULL, no_argument, NULL, 0x0}
+
+/* Helper for usage_opts_help array */
+#define USAGE_COMMON_OPTS_HELP \
+ "Print this help and exit", \
+ "Print version and exit", \
+ NULL
+
+/* Helper for getopt case statements */
+#define case_USAGE_COMMON_FLAGS \
+ case 'h': usage(NULL); \
+ case 'V': util_version(); \
+ case '?': usage("unknown option");
#endif /* _UTIL_H */
diff --git a/scripts/dtc/version_gen.h b/scripts/dtc/version_gen.h
index 6158b867df99..54d4e904433a 100644
--- a/scripts/dtc/version_gen.h
+++ b/scripts/dtc/version_gen.h
@@ -1 +1 @@
-#define DTC_VERSION "DTC 1.2.0-g37c0b6a0"
+#define DTC_VERSION "DTC 1.4.0-dirty"
diff --git a/scripts/genksyms/keywords.gperf b/scripts/genksyms/keywords.gperf
index 3e77a943e7b7..a9096d993172 100644
--- a/scripts/genksyms/keywords.gperf
+++ b/scripts/genksyms/keywords.gperf
@@ -23,6 +23,8 @@ __inline, INLINE_KEYW
__inline__, INLINE_KEYW
__signed, SIGNED_KEYW
__signed__, SIGNED_KEYW
+__typeof, TYPEOF_KEYW
+__typeof__, TYPEOF_KEYW
__volatile, VOLATILE_KEYW
__volatile__, VOLATILE_KEYW
# According to rth, c99 defines _Bool, __restrict, __restrict__, restrict. KAO
@@ -51,9 +53,8 @@ signed, SIGNED_KEYW
static, STATIC_KEYW
struct, STRUCT_KEYW
typedef, TYPEDEF_KEYW
+typeof, TYPEOF_KEYW
union, UNION_KEYW
unsigned, UNSIGNED_KEYW
void, VOID_KEYW
volatile, VOLATILE_KEYW
-typeof, TYPEOF_KEYW
-__typeof__, TYPEOF_KEYW
diff --git a/scripts/genksyms/keywords.hash.c_shipped b/scripts/genksyms/keywords.hash.c_shipped
index 82062607e8c0..e9452482e198 100644
--- a/scripts/genksyms/keywords.hash.c_shipped
+++ b/scripts/genksyms/keywords.hash.c_shipped
@@ -34,7 +34,7 @@ struct resword;
static const struct resword *is_reserved_word(register const char *str, register unsigned int len);
#line 8 "scripts/genksyms/keywords.gperf"
struct resword { const char *name; int token; };
-/* maximum key range = 64, duplicates = 0 */
+/* maximum key range = 98, duplicates = 0 */
#ifdef __GNUC__
__inline
@@ -48,32 +48,32 @@ is_reserved_hash (register const char *str, register unsigned int len)
{
static const unsigned char asso_values[] =
{
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 0,
- 67, 67, 67, 67, 67, 67, 15, 67, 67, 67,
- 0, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 0, 67, 0, 67, 5,
- 25, 20, 15, 30, 67, 15, 67, 67, 10, 0,
- 10, 40, 20, 67, 10, 5, 0, 10, 15, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
- 67, 67, 67, 67, 67, 67
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 0,
+ 101, 101, 101, 101, 101, 101, 15, 101, 101, 101,
+ 0, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 0, 101, 0, 101, 5,
+ 25, 20, 55, 30, 101, 15, 101, 101, 10, 0,
+ 10, 40, 10, 101, 10, 5, 0, 10, 15, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101
};
return len + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[0]] + asso_values[(unsigned char)str[len - 1]];
}
@@ -89,17 +89,17 @@ is_reserved_word (register const char *str, register unsigned int len)
{
enum
{
- TOTAL_KEYWORDS = 45,
+ TOTAL_KEYWORDS = 46,
MIN_WORD_LENGTH = 3,
MAX_WORD_LENGTH = 24,
MIN_HASH_VALUE = 3,
- MAX_HASH_VALUE = 66
+ MAX_HASH_VALUE = 100
};
static const struct resword wordlist[] =
{
{""}, {""}, {""},
-#line 33 "scripts/genksyms/keywords.gperf"
+#line 35 "scripts/genksyms/keywords.gperf"
{"asm", ASM_KEYW},
{""},
#line 15 "scripts/genksyms/keywords.gperf"
@@ -108,7 +108,7 @@ is_reserved_word (register const char *str, register unsigned int len)
#line 16 "scripts/genksyms/keywords.gperf"
{"__asm__", ASM_KEYW},
{""}, {""},
-#line 59 "scripts/genksyms/keywords.gperf"
+#line 27 "scripts/genksyms/keywords.gperf"
{"__typeof__", TYPEOF_KEYW},
{""},
#line 19 "scripts/genksyms/keywords.gperf"
@@ -119,31 +119,31 @@ is_reserved_word (register const char *str, register unsigned int len)
{"__const__", CONST_KEYW},
#line 25 "scripts/genksyms/keywords.gperf"
{"__signed__", SIGNED_KEYW},
-#line 51 "scripts/genksyms/keywords.gperf"
+#line 53 "scripts/genksyms/keywords.gperf"
{"static", STATIC_KEYW},
{""},
-#line 46 "scripts/genksyms/keywords.gperf"
+#line 48 "scripts/genksyms/keywords.gperf"
{"int", INT_KEYW},
-#line 39 "scripts/genksyms/keywords.gperf"
+#line 41 "scripts/genksyms/keywords.gperf"
{"char", CHAR_KEYW},
-#line 40 "scripts/genksyms/keywords.gperf"
+#line 42 "scripts/genksyms/keywords.gperf"
{"const", CONST_KEYW},
-#line 52 "scripts/genksyms/keywords.gperf"
+#line 54 "scripts/genksyms/keywords.gperf"
{"struct", STRUCT_KEYW},
-#line 31 "scripts/genksyms/keywords.gperf"
+#line 33 "scripts/genksyms/keywords.gperf"
{"__restrict__", RESTRICT_KEYW},
-#line 32 "scripts/genksyms/keywords.gperf"
+#line 34 "scripts/genksyms/keywords.gperf"
{"restrict", RESTRICT_KEYW},
#line 12 "scripts/genksyms/keywords.gperf"
{"EXPORT_SYMBOL_GPL_FUTURE", EXPORT_SYMBOL_KEYW},
#line 23 "scripts/genksyms/keywords.gperf"
{"__inline__", INLINE_KEYW},
{""},
-#line 27 "scripts/genksyms/keywords.gperf"
+#line 29 "scripts/genksyms/keywords.gperf"
{"__volatile__", VOLATILE_KEYW},
#line 10 "scripts/genksyms/keywords.gperf"
{"EXPORT_SYMBOL", EXPORT_SYMBOL_KEYW},
-#line 30 "scripts/genksyms/keywords.gperf"
+#line 32 "scripts/genksyms/keywords.gperf"
{"_restrict", RESTRICT_KEYW},
{""},
#line 17 "scripts/genksyms/keywords.gperf"
@@ -152,56 +152,65 @@ is_reserved_word (register const char *str, register unsigned int len)
{"EXPORT_SYMBOL_GPL", EXPORT_SYMBOL_KEYW},
#line 21 "scripts/genksyms/keywords.gperf"
{"__extension__", EXTENSION_KEYW},
-#line 42 "scripts/genksyms/keywords.gperf"
+#line 44 "scripts/genksyms/keywords.gperf"
{"enum", ENUM_KEYW},
#line 13 "scripts/genksyms/keywords.gperf"
{"EXPORT_UNUSED_SYMBOL", EXPORT_SYMBOL_KEYW},
-#line 43 "scripts/genksyms/keywords.gperf"
+#line 45 "scripts/genksyms/keywords.gperf"
{"extern", EXTERN_KEYW},
{""},
#line 24 "scripts/genksyms/keywords.gperf"
{"__signed", SIGNED_KEYW},
#line 14 "scripts/genksyms/keywords.gperf"
{"EXPORT_UNUSED_SYMBOL_GPL", EXPORT_SYMBOL_KEYW},
-#line 54 "scripts/genksyms/keywords.gperf"
+#line 57 "scripts/genksyms/keywords.gperf"
{"union", UNION_KEYW},
-#line 58 "scripts/genksyms/keywords.gperf"
- {"typeof", TYPEOF_KEYW},
-#line 53 "scripts/genksyms/keywords.gperf"
- {"typedef", TYPEDEF_KEYW},
+ {""}, {""},
#line 22 "scripts/genksyms/keywords.gperf"
{"__inline", INLINE_KEYW},
-#line 38 "scripts/genksyms/keywords.gperf"
+#line 40 "scripts/genksyms/keywords.gperf"
{"auto", AUTO_KEYW},
-#line 26 "scripts/genksyms/keywords.gperf"
+#line 28 "scripts/genksyms/keywords.gperf"
{"__volatile", VOLATILE_KEYW},
{""}, {""},
-#line 55 "scripts/genksyms/keywords.gperf"
+#line 58 "scripts/genksyms/keywords.gperf"
{"unsigned", UNSIGNED_KEYW},
{""},
-#line 49 "scripts/genksyms/keywords.gperf"
+#line 51 "scripts/genksyms/keywords.gperf"
{"short", SHORT_KEYW},
-#line 45 "scripts/genksyms/keywords.gperf"
+#line 47 "scripts/genksyms/keywords.gperf"
{"inline", INLINE_KEYW},
{""},
-#line 57 "scripts/genksyms/keywords.gperf"
+#line 60 "scripts/genksyms/keywords.gperf"
{"volatile", VOLATILE_KEYW},
-#line 47 "scripts/genksyms/keywords.gperf"
+#line 49 "scripts/genksyms/keywords.gperf"
{"long", LONG_KEYW},
-#line 29 "scripts/genksyms/keywords.gperf"
+#line 31 "scripts/genksyms/keywords.gperf"
{"_Bool", BOOL_KEYW},
{""}, {""},
-#line 48 "scripts/genksyms/keywords.gperf"
+#line 50 "scripts/genksyms/keywords.gperf"
{"register", REGISTER_KEYW},
-#line 56 "scripts/genksyms/keywords.gperf"
+#line 59 "scripts/genksyms/keywords.gperf"
{"void", VOID_KEYW},
-#line 44 "scripts/genksyms/keywords.gperf"
- {"float", FLOAT_KEYW},
-#line 41 "scripts/genksyms/keywords.gperf"
+ {""},
+#line 43 "scripts/genksyms/keywords.gperf"
{"double", DOUBLE_KEYW},
+ {""},
+#line 26 "scripts/genksyms/keywords.gperf"
+ {"__typeof", TYPEOF_KEYW},
+ {""}, {""},
+#line 52 "scripts/genksyms/keywords.gperf"
+ {"signed", SIGNED_KEYW},
{""}, {""}, {""}, {""},
-#line 50 "scripts/genksyms/keywords.gperf"
- {"signed", SIGNED_KEYW}
+#line 56 "scripts/genksyms/keywords.gperf"
+ {"typeof", TYPEOF_KEYW},
+#line 55 "scripts/genksyms/keywords.gperf"
+ {"typedef", TYPEDEF_KEYW},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 46 "scripts/genksyms/keywords.gperf"
+ {"float", FLOAT_KEYW}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
diff --git a/scripts/genksyms/lex.l b/scripts/genksyms/lex.l
index f770071719cb..e583565f2011 100644
--- a/scripts/genksyms/lex.l
+++ b/scripts/genksyms/lex.l
@@ -129,8 +129,9 @@ int
yylex(void)
{
static enum {
- ST_NOTSTARTED, ST_NORMAL, ST_ATTRIBUTE, ST_ASM, ST_BRACKET, ST_BRACE,
- ST_EXPRESSION, ST_TABLE_1, ST_TABLE_2, ST_TABLE_3, ST_TABLE_4,
+ ST_NOTSTARTED, ST_NORMAL, ST_ATTRIBUTE, ST_ASM, ST_TYPEOF, ST_TYPEOF_1,
+ ST_BRACKET, ST_BRACE, ST_EXPRESSION,
+ ST_TABLE_1, ST_TABLE_2, ST_TABLE_3, ST_TABLE_4,
ST_TABLE_5, ST_TABLE_6
} lexstate = ST_NOTSTARTED;
@@ -198,6 +199,10 @@ repeat:
lexstate = ST_ASM;
count = 0;
goto repeat;
+ case TYPEOF_KEYW:
+ lexstate = ST_TYPEOF;
+ count = 0;
+ goto repeat;
case STRUCT_KEYW:
case UNION_KEYW:
@@ -284,6 +289,48 @@ repeat:
}
break;
+ case ST_TYPEOF:
+ switch (token)
+ {
+ case '(':
+ if ( ++count == 1 )
+ lexstate = ST_TYPEOF_1;
+ else
+ APP;
+ goto repeat;
+ case ')':
+ APP;
+ if (--count == 0)
+ {
+ lexstate = ST_NORMAL;
+ token = TYPEOF_PHRASE;
+ break;
+ }
+ goto repeat;
+ default:
+ APP;
+ goto repeat;
+ }
+ break;
+
+ case ST_TYPEOF_1:
+ if (token == IDENT)
+ {
+ if (is_reserved_word(yytext, yyleng)
+ || find_symbol(yytext, SYM_TYPEDEF, 1))
+ {
+ yyless(0);
+ unput('(');
+ lexstate = ST_NORMAL;
+ token = TYPEOF_KEYW;
+ break;
+ }
+ _APP("(", 1);
+ }
+ APP;
+ lexstate = ST_TYPEOF;
+ goto repeat;
+
case ST_BRACKET:
APP;
switch (token)
diff --git a/scripts/genksyms/lex.lex.c_shipped b/scripts/genksyms/lex.lex.c_shipped
index 0bf4157e6161..f82740a69b85 100644
--- a/scripts/genksyms/lex.lex.c_shipped
+++ b/scripts/genksyms/lex.lex.c_shipped
@@ -1938,8 +1938,9 @@ int
yylex(void)
{
static enum {
- ST_NOTSTARTED, ST_NORMAL, ST_ATTRIBUTE, ST_ASM, ST_BRACKET, ST_BRACE,
- ST_EXPRESSION, ST_TABLE_1, ST_TABLE_2, ST_TABLE_3, ST_TABLE_4,
+ ST_NOTSTARTED, ST_NORMAL, ST_ATTRIBUTE, ST_ASM, ST_TYPEOF, ST_TYPEOF_1,
+ ST_BRACKET, ST_BRACE, ST_EXPRESSION,
+ ST_TABLE_1, ST_TABLE_2, ST_TABLE_3, ST_TABLE_4,
ST_TABLE_5, ST_TABLE_6
} lexstate = ST_NOTSTARTED;
@@ -2007,6 +2008,10 @@ repeat:
lexstate = ST_ASM;
count = 0;
goto repeat;
+ case TYPEOF_KEYW:
+ lexstate = ST_TYPEOF;
+ count = 0;
+ goto repeat;
case STRUCT_KEYW:
case UNION_KEYW:
@@ -2093,6 +2098,48 @@ repeat:
}
break;
+ case ST_TYPEOF:
+ switch (token)
+ {
+ case '(':
+ if ( ++count == 1 )
+ lexstate = ST_TYPEOF_1;
+ else
+ APP;
+ goto repeat;
+ case ')':
+ APP;
+ if (--count == 0)
+ {
+ lexstate = ST_NORMAL;
+ token = TYPEOF_PHRASE;
+ break;
+ }
+ goto repeat;
+ default:
+ APP;
+ goto repeat;
+ }
+ break;
+
+ case ST_TYPEOF_1:
+ if (token == IDENT)
+ {
+ if (is_reserved_word(yytext, yyleng)
+ || find_symbol(yytext, SYM_TYPEDEF, 1))
+ {
+ yyless(0);
+ unput('(');
+ lexstate = ST_NORMAL;
+ token = TYPEOF_KEYW;
+ break;
+ }
+ _APP("(", 1);
+ }
+ APP;
+ lexstate = ST_TYPEOF;
+ goto repeat;
+
case ST_BRACKET:
APP;
switch (token)
diff --git a/scripts/genksyms/parse.tab.c_shipped b/scripts/genksyms/parse.tab.c_shipped
index ece53c79bb59..c9f0f0ce82ff 100644
--- a/scripts/genksyms/parse.tab.c_shipped
+++ b/scripts/genksyms/parse.tab.c_shipped
@@ -1,8 +1,8 @@
-/* A Bison parser, made by GNU Bison 2.5. */
+/* A Bison parser, made by GNU Bison 2.5.1. */
/* Bison implementation for Yacc-like parsers in C
- Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+ Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, 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
@@ -44,7 +44,7 @@
#define YYBISON 1
/* Bison version. */
-#define YYBISON_VERSION "2.5"
+#define YYBISON_VERSION "2.5.1"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
@@ -117,6 +117,14 @@ static void record_compound(struct string_list **keyw,
+# ifndef YY_NULL
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULL nullptr
+# else
+# define YY_NULL 0
+# endif
+# endif
+
/* Enabling traces. */
#ifndef YYDEBUG
# define YYDEBUG 1
@@ -171,18 +179,19 @@ static void record_compound(struct string_list **keyw,
EXPORT_SYMBOL_KEYW = 284,
ASM_PHRASE = 285,
ATTRIBUTE_PHRASE = 286,
- BRACE_PHRASE = 287,
- BRACKET_PHRASE = 288,
- EXPRESSION_PHRASE = 289,
- CHAR = 290,
- DOTS = 291,
- IDENT = 292,
- INT = 293,
- REAL = 294,
- STRING = 295,
- TYPE = 296,
- OTHER = 297,
- FILENAME = 298
+ TYPEOF_PHRASE = 287,
+ BRACE_PHRASE = 288,
+ BRACKET_PHRASE = 289,
+ EXPRESSION_PHRASE = 290,
+ CHAR = 291,
+ DOTS = 292,
+ IDENT = 293,
+ INT = 294,
+ REAL = 295,
+ STRING = 296,
+ TYPE = 297,
+ OTHER = 298,
+ FILENAME = 299
};
#endif
@@ -304,6 +313,7 @@ YYID (yyi)
# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
# ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
# endif
@@ -395,20 +405,20 @@ union yyalloc
#endif
#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
-/* Copy COUNT objects from FROM to TO. The source and destination do
+/* Copy COUNT objects from SRC to DST. The source and destination do
not overlap. */
# ifndef YYCOPY
# if defined __GNUC__ && 1 < __GNUC__
-# define YYCOPY(To, From, Count) \
- __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
# else
-# define YYCOPY(To, From, Count) \
- do \
- { \
- YYSIZE_T yyi; \
- for (yyi = 0; yyi < (Count); yyi++) \
- (To)[yyi] = (From)[yyi]; \
- } \
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
while (YYID (0))
# endif
# endif
@@ -417,20 +427,20 @@ union yyalloc
/* YYFINAL -- State number of the termination state. */
#define YYFINAL 4
/* YYLAST -- Last index in YYTABLE. */
-#define YYLAST 532
+#define YYLAST 514
/* YYNTOKENS -- Number of terminals. */
-#define YYNTOKENS 53
+#define YYNTOKENS 54
/* YYNNTS -- Number of nonterminals. */
#define YYNNTS 49
/* YYNRULES -- Number of rules. */
#define YYNRULES 132
/* YYNRULES -- Number of states. */
-#define YYNSTATES 188
+#define YYNSTATES 187
/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
#define YYUNDEFTOK 2
-#define YYMAXUTOK 298
+#define YYMAXUTOK 299
#define YYTRANSLATE(YYX) \
((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
@@ -442,15 +452,15 @@ static const yytype_uint8 yytranslate[] =
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 47, 49, 48, 2, 46, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 52, 44,
- 2, 50, 2, 2, 2, 2, 2, 2, 2, 2,
+ 48, 49, 50, 2, 47, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 53, 45,
+ 2, 51, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 51, 2, 45, 2, 2, 2, 2,
+ 2, 2, 2, 52, 2, 46, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -467,7 +477,7 @@ static const yytype_uint8 yytranslate[] =
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, 31, 32, 33, 34,
- 35, 36, 37, 38, 39, 40, 41, 42, 43
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44
};
#if YYDEBUG
@@ -478,78 +488,77 @@ static const yytype_uint16 yyprhs[] =
0, 0, 3, 5, 8, 9, 12, 13, 18, 19,
23, 25, 27, 29, 31, 34, 37, 41, 42, 44,
46, 50, 55, 56, 58, 60, 63, 65, 67, 69,
- 71, 73, 75, 77, 79, 81, 87, 92, 95, 98,
- 101, 105, 109, 113, 116, 119, 122, 124, 126, 128,
- 130, 132, 134, 136, 138, 140, 142, 144, 147, 148,
- 150, 152, 155, 157, 159, 161, 163, 166, 168, 170,
- 175, 180, 183, 187, 191, 194, 196, 198, 200, 205,
- 210, 213, 217, 221, 224, 226, 230, 231, 233, 235,
- 239, 242, 245, 247, 248, 250, 252, 257, 262, 265,
- 269, 273, 277, 278, 280, 283, 287, 291, 292, 294,
- 296, 299, 303, 306, 307, 309, 311, 315, 318, 321,
- 323, 326, 327, 330, 334, 339, 341, 345, 347, 351,
- 354, 355, 357
+ 71, 73, 75, 77, 79, 81, 86, 88, 91, 94,
+ 97, 101, 105, 109, 112, 115, 118, 120, 122, 124,
+ 126, 128, 130, 132, 134, 136, 138, 140, 143, 144,
+ 146, 148, 151, 153, 155, 157, 159, 162, 164, 166,
+ 171, 176, 179, 183, 187, 190, 192, 194, 196, 201,
+ 206, 209, 213, 217, 220, 222, 226, 227, 229, 231,
+ 235, 238, 241, 243, 244, 246, 248, 253, 258, 261,
+ 265, 269, 273, 274, 276, 279, 283, 287, 288, 290,
+ 292, 295, 299, 302, 303, 305, 307, 311, 314, 317,
+ 319, 322, 323, 326, 330, 335, 337, 341, 343, 347,
+ 350, 351, 353
};
/* YYRHS -- A `-1'-separated list of the rules' RHS. */
static const yytype_int8 yyrhs[] =
{
- 54, 0, -1, 55, -1, 54, 55, -1, -1, 56,
- 57, -1, -1, 12, 23, 58, 60, -1, -1, 23,
- 59, 60, -1, 60, -1, 84, -1, 99, -1, 101,
- -1, 1, 44, -1, 1, 45, -1, 64, 61, 44,
- -1, -1, 62, -1, 63, -1, 62, 46, 63, -1,
- 74, 100, 95, 85, -1, -1, 65, -1, 66, -1,
- 65, 66, -1, 67, -1, 68, -1, 5, -1, 17,
- -1, 21, -1, 11, -1, 14, -1, 69, -1, 73,
- -1, 28, 47, 65, 48, 49, -1, 28, 47, 65,
- 49, -1, 22, 37, -1, 24, 37, -1, 10, 37,
- -1, 22, 37, 87, -1, 24, 37, 87, -1, 10,
- 37, 96, -1, 10, 96, -1, 22, 87, -1, 24,
- 87, -1, 7, -1, 19, -1, 15, -1, 16, -1,
- 20, -1, 25, -1, 13, -1, 9, -1, 26, -1,
- 6, -1, 41, -1, 48, 71, -1, -1, 72, -1,
- 73, -1, 72, 73, -1, 8, -1, 27, -1, 31,
- -1, 18, -1, 70, 74, -1, 75, -1, 37, -1,
- 75, 47, 78, 49, -1, 75, 47, 1, 49, -1,
- 75, 33, -1, 47, 74, 49, -1, 47, 1, 49,
- -1, 70, 76, -1, 77, -1, 37, -1, 41, -1,
- 77, 47, 78, 49, -1, 77, 47, 1, 49, -1,
- 77, 33, -1, 47, 76, 49, -1, 47, 1, 49,
- -1, 79, 36, -1, 79, -1, 80, 46, 36, -1,
- -1, 80, -1, 81, -1, 80, 46, 81, -1, 65,
- 82, -1, 70, 82, -1, 83, -1, -1, 37, -1,
- 41, -1, 83, 47, 78, 49, -1, 83, 47, 1,
- 49, -1, 83, 33, -1, 47, 82, 49, -1, 47,
- 1, 49, -1, 64, 74, 32, -1, -1, 86, -1,
- 50, 34, -1, 51, 88, 45, -1, 51, 1, 45,
- -1, -1, 89, -1, 90, -1, 89, 90, -1, 64,
- 91, 44, -1, 1, 44, -1, -1, 92, -1, 93,
- -1, 92, 46, 93, -1, 76, 95, -1, 37, 94,
- -1, 94, -1, 52, 34, -1, -1, 95, 31, -1,
- 51, 97, 45, -1, 51, 97, 46, 45, -1, 98,
- -1, 97, 46, 98, -1, 37, -1, 37, 50, 34,
- -1, 30, 44, -1, -1, 30, -1, 29, 47, 37,
- 49, 44, -1
+ 55, 0, -1, 56, -1, 55, 56, -1, -1, 57,
+ 58, -1, -1, 12, 23, 59, 61, -1, -1, 23,
+ 60, 61, -1, 61, -1, 85, -1, 100, -1, 102,
+ -1, 1, 45, -1, 1, 46, -1, 65, 62, 45,
+ -1, -1, 63, -1, 64, -1, 63, 47, 64, -1,
+ 75, 101, 96, 86, -1, -1, 66, -1, 67, -1,
+ 66, 67, -1, 68, -1, 69, -1, 5, -1, 17,
+ -1, 21, -1, 11, -1, 14, -1, 70, -1, 74,
+ -1, 28, 48, 82, 49, -1, 32, -1, 22, 38,
+ -1, 24, 38, -1, 10, 38, -1, 22, 38, 88,
+ -1, 24, 38, 88, -1, 10, 38, 97, -1, 10,
+ 97, -1, 22, 88, -1, 24, 88, -1, 7, -1,
+ 19, -1, 15, -1, 16, -1, 20, -1, 25, -1,
+ 13, -1, 9, -1, 26, -1, 6, -1, 42, -1,
+ 50, 72, -1, -1, 73, -1, 74, -1, 73, 74,
+ -1, 8, -1, 27, -1, 31, -1, 18, -1, 71,
+ 75, -1, 76, -1, 38, -1, 76, 48, 79, 49,
+ -1, 76, 48, 1, 49, -1, 76, 34, -1, 48,
+ 75, 49, -1, 48, 1, 49, -1, 71, 77, -1,
+ 78, -1, 38, -1, 42, -1, 78, 48, 79, 49,
+ -1, 78, 48, 1, 49, -1, 78, 34, -1, 48,
+ 77, 49, -1, 48, 1, 49, -1, 80, 37, -1,
+ 80, -1, 81, 47, 37, -1, -1, 81, -1, 82,
+ -1, 81, 47, 82, -1, 66, 83, -1, 71, 83,
+ -1, 84, -1, -1, 38, -1, 42, -1, 84, 48,
+ 79, 49, -1, 84, 48, 1, 49, -1, 84, 34,
+ -1, 48, 83, 49, -1, 48, 1, 49, -1, 65,
+ 75, 33, -1, -1, 87, -1, 51, 35, -1, 52,
+ 89, 46, -1, 52, 1, 46, -1, -1, 90, -1,
+ 91, -1, 90, 91, -1, 65, 92, 45, -1, 1,
+ 45, -1, -1, 93, -1, 94, -1, 93, 47, 94,
+ -1, 77, 96, -1, 38, 95, -1, 95, -1, 53,
+ 35, -1, -1, 96, 31, -1, 52, 98, 46, -1,
+ 52, 98, 47, 46, -1, 99, -1, 98, 47, 99,
+ -1, 38, -1, 38, 51, 35, -1, 30, 45, -1,
+ -1, 30, -1, 29, 48, 38, 49, 45, -1
};
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint16 yyrline[] =
{
- 0, 123, 123, 124, 128, 128, 134, 134, 136, 136,
- 138, 139, 140, 141, 142, 143, 147, 161, 162, 166,
- 174, 187, 193, 194, 198, 199, 203, 209, 213, 214,
- 215, 216, 217, 221, 222, 223, 224, 228, 230, 232,
- 236, 238, 240, 245, 248, 249, 253, 254, 255, 256,
- 257, 258, 259, 260, 261, 262, 263, 267, 272, 273,
- 277, 278, 282, 282, 282, 283, 291, 292, 296, 305,
- 307, 309, 311, 313, 320, 321, 325, 326, 327, 329,
- 331, 333, 335, 340, 341, 342, 346, 347, 351, 352,
- 357, 362, 364, 368, 369, 377, 381, 383, 385, 387,
- 389, 394, 403, 404, 409, 414, 415, 419, 420, 424,
- 425, 429, 431, 436, 437, 441, 442, 446, 447, 448,
- 452, 456, 457, 461, 462, 466, 467, 470, 475, 483,
- 487, 488, 492
+ 0, 124, 124, 125, 129, 129, 135, 135, 137, 137,
+ 139, 140, 141, 142, 143, 144, 148, 162, 163, 167,
+ 175, 188, 194, 195, 199, 200, 204, 210, 214, 215,
+ 216, 217, 218, 222, 223, 224, 225, 229, 231, 233,
+ 237, 239, 241, 246, 249, 250, 254, 255, 256, 257,
+ 258, 259, 260, 261, 262, 263, 264, 268, 273, 274,
+ 278, 279, 283, 283, 283, 284, 292, 293, 297, 306,
+ 308, 310, 312, 314, 321, 322, 326, 327, 328, 330,
+ 332, 334, 336, 341, 342, 343, 347, 348, 352, 353,
+ 358, 363, 365, 369, 370, 378, 382, 384, 386, 388,
+ 390, 395, 404, 405, 410, 415, 416, 420, 421, 425,
+ 426, 430, 432, 437, 438, 442, 443, 447, 448, 449,
+ 453, 457, 458, 462, 463, 467, 468, 471, 476, 484,
+ 488, 489, 493
};
#endif
@@ -565,9 +574,9 @@ static const char *const yytname[] =
"SHORT_KEYW", "SIGNED_KEYW", "STATIC_KEYW", "STRUCT_KEYW",
"TYPEDEF_KEYW", "UNION_KEYW", "UNSIGNED_KEYW", "VOID_KEYW",
"VOLATILE_KEYW", "TYPEOF_KEYW", "EXPORT_SYMBOL_KEYW", "ASM_PHRASE",
- "ATTRIBUTE_PHRASE", "BRACE_PHRASE", "BRACKET_PHRASE",
+ "ATTRIBUTE_PHRASE", "TYPEOF_PHRASE", "BRACE_PHRASE", "BRACKET_PHRASE",
"EXPRESSION_PHRASE", "CHAR", "DOTS", "IDENT", "INT", "REAL", "STRING",
- "TYPE", "OTHER", "FILENAME", "';'", "'}'", "','", "'('", "'*'", "')'",
+ "TYPE", "OTHER", "FILENAME", "';'", "'}'", "','", "'('", "')'", "'*'",
"'='", "'{'", "':'", "$accept", "declaration_seq", "declaration", "$@1",
"declaration1", "$@2", "$@3", "simple_declaration",
"init_declarator_list_opt", "init_declarator_list", "init_declarator",
@@ -584,7 +593,7 @@ static const char *const yytname[] =
"member_declarator_list_opt", "member_declarator_list",
"member_declarator", "member_bitfield_declarator", "attribute_opt",
"enum_body", "enumerator_list", "enumerator", "asm_definition",
- "asm_phrase_opt", "export_definition", 0
+ "asm_phrase_opt", "export_definition", YY_NULL
};
#endif
@@ -597,28 +606,28 @@ static const yytype_uint16 yytoknum[] =
265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
- 295, 296, 297, 298, 59, 125, 44, 40, 42, 41,
- 61, 123, 58
+ 295, 296, 297, 298, 299, 59, 125, 44, 40, 41,
+ 42, 61, 123, 58
};
# endif
/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
static const yytype_uint8 yyr1[] =
{
- 0, 53, 54, 54, 56, 55, 58, 57, 59, 57,
- 57, 57, 57, 57, 57, 57, 60, 61, 61, 62,
- 62, 63, 64, 64, 65, 65, 66, 66, 67, 67,
- 67, 67, 67, 68, 68, 68, 68, 68, 68, 68,
- 68, 68, 68, 68, 68, 68, 69, 69, 69, 69,
- 69, 69, 69, 69, 69, 69, 69, 70, 71, 71,
- 72, 72, 73, 73, 73, 73, 74, 74, 75, 75,
- 75, 75, 75, 75, 76, 76, 77, 77, 77, 77,
- 77, 77, 77, 78, 78, 78, 79, 79, 80, 80,
- 81, 82, 82, 83, 83, 83, 83, 83, 83, 83,
- 83, 84, 85, 85, 86, 87, 87, 88, 88, 89,
- 89, 90, 90, 91, 91, 92, 92, 93, 93, 93,
- 94, 95, 95, 96, 96, 97, 97, 98, 98, 99,
- 100, 100, 101
+ 0, 54, 55, 55, 57, 56, 59, 58, 60, 58,
+ 58, 58, 58, 58, 58, 58, 61, 62, 62, 63,
+ 63, 64, 65, 65, 66, 66, 67, 67, 68, 68,
+ 68, 68, 68, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 71, 72, 72,
+ 73, 73, 74, 74, 74, 74, 75, 75, 76, 76,
+ 76, 76, 76, 76, 77, 77, 78, 78, 78, 78,
+ 78, 78, 78, 79, 79, 79, 80, 80, 81, 81,
+ 82, 83, 83, 84, 84, 84, 84, 84, 84, 84,
+ 84, 85, 86, 86, 87, 88, 88, 89, 89, 90,
+ 90, 91, 91, 92, 92, 93, 93, 94, 94, 94,
+ 95, 96, 96, 97, 97, 98, 98, 99, 99, 100,
+ 101, 101, 102
};
/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
@@ -627,7 +636,7 @@ static const yytype_uint8 yyr2[] =
0, 2, 1, 2, 0, 2, 0, 4, 0, 3,
1, 1, 1, 1, 2, 2, 3, 0, 1, 1,
3, 4, 0, 1, 1, 2, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 5, 4, 2, 2, 2,
+ 1, 1, 1, 1, 1, 4, 1, 2, 2, 2,
3, 3, 3, 2, 2, 2, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2, 0, 1,
1, 2, 1, 1, 1, 1, 2, 1, 1, 4,
@@ -648,68 +657,68 @@ static const yytype_uint8 yydefact[] =
4, 4, 2, 0, 1, 3, 0, 28, 55, 46,
62, 53, 0, 31, 0, 52, 32, 48, 49, 29,
65, 47, 50, 30, 0, 8, 0, 51, 54, 63,
- 0, 0, 0, 64, 56, 5, 10, 17, 23, 24,
- 26, 27, 33, 34, 11, 12, 13, 14, 15, 39,
- 0, 43, 6, 37, 0, 44, 22, 38, 45, 0,
- 0, 129, 68, 0, 58, 0, 18, 19, 0, 130,
- 67, 25, 42, 127, 0, 125, 22, 40, 0, 113,
- 0, 0, 109, 9, 17, 41, 0, 0, 0, 0,
- 57, 59, 60, 16, 0, 66, 131, 101, 121, 71,
- 0, 0, 123, 0, 7, 112, 106, 76, 77, 0,
- 0, 0, 121, 75, 0, 114, 115, 119, 105, 0,
- 110, 130, 0, 36, 0, 73, 72, 61, 20, 102,
- 0, 93, 0, 84, 87, 88, 128, 124, 126, 118,
- 0, 76, 0, 120, 74, 117, 80, 0, 111, 0,
- 35, 132, 122, 0, 21, 103, 70, 94, 56, 0,
- 93, 90, 92, 69, 83, 0, 82, 81, 0, 0,
- 116, 104, 0, 95, 0, 91, 98, 0, 85, 89,
- 79, 78, 100, 99, 0, 0, 97, 96
+ 0, 0, 0, 64, 36, 56, 5, 10, 17, 23,
+ 24, 26, 27, 33, 34, 11, 12, 13, 14, 15,
+ 39, 0, 43, 6, 37, 0, 44, 22, 38, 45,
+ 0, 0, 129, 68, 0, 58, 0, 18, 19, 0,
+ 130, 67, 25, 42, 127, 0, 125, 22, 40, 0,
+ 113, 0, 0, 109, 9, 17, 41, 93, 0, 0,
+ 0, 0, 57, 59, 60, 16, 0, 66, 131, 101,
+ 121, 71, 0, 0, 123, 0, 7, 112, 106, 76,
+ 77, 0, 0, 0, 121, 75, 0, 114, 115, 119,
+ 105, 0, 110, 130, 94, 56, 0, 93, 90, 92,
+ 35, 0, 73, 72, 61, 20, 102, 0, 0, 84,
+ 87, 88, 128, 124, 126, 118, 0, 76, 0, 120,
+ 74, 117, 80, 0, 111, 0, 0, 95, 0, 91,
+ 98, 0, 132, 122, 0, 21, 103, 70, 69, 83,
+ 0, 82, 81, 0, 0, 116, 100, 99, 0, 0,
+ 104, 85, 89, 79, 78, 97, 96
};
/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int16 yydefgoto[] =
{
- -1, 1, 2, 3, 35, 76, 56, 36, 65, 66,
- 67, 79, 38, 39, 40, 41, 42, 68, 90, 91,
- 43, 121, 70, 112, 113, 132, 133, 134, 135, 161,
- 162, 44, 154, 155, 55, 80, 81, 82, 114, 115,
- 116, 117, 129, 51, 74, 75, 45, 98, 46
+ -1, 1, 2, 3, 36, 77, 57, 37, 66, 67,
+ 68, 80, 39, 40, 41, 42, 43, 69, 92, 93,
+ 44, 123, 71, 114, 115, 138, 139, 140, 141, 128,
+ 129, 45, 165, 166, 56, 81, 82, 83, 116, 117,
+ 118, 119, 136, 52, 75, 76, 46, 100, 47
};
/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
STATE-NUM. */
-#define YYPACT_NINF -135
+#define YYPACT_NINF -140
static const yytype_int16 yypact[] =
{
- -135, 20, -135, 321, -135, -135, 30, -135, -135, -135,
- -135, -135, -28, -135, 2, -135, -135, -135, -135, -135,
- -135, -135, -135, -135, -6, -135, 9, -135, -135, -135,
- -5, 15, -17, -135, -135, -135, -135, 18, 491, -135,
- -135, -135, -135, -135, -135, -135, -135, -135, -135, -22,
- 31, -135, -135, 19, 106, -135, 491, 19, -135, 491,
- 50, -135, -135, 11, -3, 51, 57, -135, 18, -14,
- 14, -135, -135, 48, 46, -135, 491, -135, 33, 32,
- 59, 154, -135, -135, 18, -135, 365, 56, 60, 61,
- -135, -3, -135, -135, 18, -135, -135, -135, -135, -135,
- 202, 74, -135, -23, -135, -135, -135, 77, -135, 16,
- 101, 49, -135, 34, 92, 93, -135, -135, -135, 94,
- -135, 110, 95, -135, 97, -135, -135, -135, -135, -20,
- 96, 410, 99, 113, 100, -135, -135, -135, -135, -135,
- 103, -135, 107, -135, -135, 111, -135, 239, -135, 32,
- -135, -135, -135, 123, -135, -135, -135, -135, -135, 3,
- 52, -135, 38, -135, -135, 454, -135, -135, 117, 128,
- -135, -135, 134, -135, 135, -135, -135, 276, -135, -135,
- -135, -135, -135, -135, 137, 138, -135, -135
+ -140, 29, -140, 207, -140, -140, 40, -140, -140, -140,
+ -140, -140, -27, -140, 44, -140, -140, -140, -140, -140,
+ -140, -140, -140, -140, -22, -140, -18, -140, -140, -140,
+ -9, 22, 28, -140, -140, -140, -140, -140, 42, 472,
+ -140, -140, -140, -140, -140, -140, -140, -140, -140, -140,
+ 46, 43, -140, -140, 47, 107, -140, 472, 47, -140,
+ 472, 62, -140, -140, 16, -3, 57, 56, -140, 42,
+ 35, -11, -140, -140, 53, 48, -140, 472, -140, 51,
+ 21, 59, 157, -140, -140, 42, -140, 388, 58, 60,
+ 70, 81, -140, -3, -140, -140, 42, -140, -140, -140,
+ -140, -140, 253, 71, -140, -20, -140, -140, -140, 83,
+ -140, 5, 102, 34, -140, 12, 95, 94, -140, -140,
+ -140, 97, -140, 113, -140, -140, 2, 41, -140, 27,
+ -140, 99, -140, -140, -140, -140, -24, 98, 101, 109,
+ 104, -140, -140, -140, -140, -140, 105, -140, 110, -140,
+ -140, 117, -140, 298, -140, 21, 112, -140, 120, -140,
+ -140, 343, -140, -140, 121, -140, -140, -140, -140, -140,
+ 434, -140, -140, 131, 137, -140, -140, -140, 138, 141,
+ -140, -140, -140, -140, -140, -140, -140
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int16 yypgoto[] =
{
- -135, -135, 187, -135, -135, -135, -135, -50, -135, -135,
- 98, 0, -59, -37, -135, -135, -135, -77, -135, -135,
- -54, -30, -135, -90, -135, -134, -135, -135, 24, -58,
- -135, -135, -135, -135, -18, -135, -135, 109, -135, -135,
- 44, 87, 84, 148, -135, 102, -135, -135, -135
+ -140, -140, 190, -140, -140, -140, -140, -45, -140, -140,
+ 96, 1, -60, -31, -140, -140, -140, -78, -140, -140,
+ -55, -7, -140, -92, -140, -139, -140, -140, -59, -39,
+ -140, -140, -140, -140, -13, -140, -140, 111, -140, -140,
+ 39, 87, 84, 147, -140, 106, -140, -140, -140
};
/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
@@ -718,149 +727,145 @@ static const yytype_int16 yypgoto[] =
#define YYTABLE_NINF -109
static const yytype_int16 yytable[] =
{
- 86, 71, 111, 37, 172, 10, 83, 69, 58, 49,
- 92, 152, 88, 169, 73, 20, 96, 140, 97, 142,
- 4, 144, 137, 50, 29, 52, 104, 61, 33, 50,
- 153, 53, 111, 89, 111, 77, -93, 127, 95, 85,
- 157, 131, 59, 185, 173, 54, 57, 99, 62, 71,
- 159, 64, -93, 141, 160, 62, 84, 108, 63, 64,
- 54, 100, 60, 109, 64, 63, 64, 146, 73, 107,
- 54, 176, 111, 108, 47, 48, 84, 105, 106, 109,
- 64, 147, 160, 160, 110, 177, 141, 87, 131, 157,
- 108, 102, 103, 173, 71, 93, 109, 64, 101, 159,
- 64, 174, 175, 94, 118, 124, 131, 78, 136, 125,
- 126, 7, 8, 9, 10, 11, 12, 13, 131, 15,
- 16, 17, 18, 19, 20, 21, 22, 23, 24, 110,
- 26, 27, 28, 29, 30, 143, 148, 33, 105, 149,
- 96, 151, 152, -22, 150, 156, 165, 34, 163, 164,
- -22, -107, 166, -22, -22, 119, 167, 171, -22, 7,
- 8, 9, 10, 11, 12, 13, 180, 15, 16, 17,
- 18, 19, 20, 21, 22, 23, 24, 181, 26, 27,
- 28, 29, 30, 182, 183, 33, 186, 187, 5, 179,
- 120, -22, 128, 170, 139, 34, 145, 72, -22, -108,
- 0, -22, -22, 130, 0, 138, -22, 7, 8, 9,
- 10, 11, 12, 13, 0, 15, 16, 17, 18, 19,
- 20, 21, 22, 23, 24, 0, 26, 27, 28, 29,
- 30, 0, 0, 33, 0, 0, 0, 0, -86, 0,
- 168, 0, 0, 34, 7, 8, 9, 10, 11, 12,
- 13, -86, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 0, 26, 27, 28, 29, 30, 0, 0,
- 33, 0, 0, 0, 0, -86, 0, 184, 0, 0,
- 34, 7, 8, 9, 10, 11, 12, 13, -86, 15,
- 16, 17, 18, 19, 20, 21, 22, 23, 24, 0,
- 26, 27, 28, 29, 30, 0, 0, 33, 0, 0,
- 0, 0, -86, 0, 0, 0, 0, 34, 0, 0,
- 0, 0, 6, 0, 0, -86, 7, 8, 9, 10,
- 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
- 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
- 31, 32, 33, 0, 0, 0, 0, 0, -22, 0,
- 0, 0, 34, 0, 0, -22, 0, 0, -22, -22,
- 7, 8, 9, 10, 11, 12, 13, 0, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24, 0, 26,
- 27, 28, 29, 30, 0, 0, 33, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 34, 0, 0, 0,
- 0, 0, 0, 122, 123, 7, 8, 9, 10, 11,
- 12, 13, 0, 15, 16, 17, 18, 19, 20, 21,
- 22, 23, 24, 0, 26, 27, 28, 29, 30, 0,
- 0, 33, 0, 0, 0, 0, 0, 157, 0, 0,
- 0, 158, 0, 0, 0, 0, 0, 159, 64, 7,
+ 87, 88, 113, 156, 38, 10, 146, 163, 72, 127,
+ 94, 50, 84, 59, 174, 20, 54, 90, 74, 148,
+ 58, 150, 179, 101, 29, 51, 143, 164, 33, 4,
+ 55, 70, 106, 113, 55, 113, -93, 102, 134, 60,
+ 124, 78, 87, 147, 157, 86, 152, 110, 127, 127,
+ 126, -93, 65, 111, 63, 65, 72, 91, 85, 109,
+ 153, 160, 97, 110, 64, 98, 65, 53, 99, 111,
+ 61, 65, 147, 62, 112, 161, 110, 113, 85, 124,
+ 63, 74, 111, 157, 65, 48, 49, 158, 159, 126,
+ 64, 65, 65, 87, 104, 105, 107, 108, 51, 55,
+ 89, 87, 95, 96, 103, 120, 142, 130, 79, 131,
+ 87, 182, 7, 8, 9, 10, 11, 12, 13, 132,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 133, 26, 27, 28, 29, 30, 112, 149, 33, 34,
+ 154, 155, 107, 98, 162, -22, 169, 167, 163, 35,
+ 168, 170, -22, -107, 171, -22, 180, -22, 121, 172,
+ -22, 176, 7, 8, 9, 10, 11, 12, 13, 177,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 183, 26, 27, 28, 29, 30, 184, 185, 33, 34,
+ 186, 5, 135, 122, 175, -22, 145, 73, 151, 35,
+ 0, 0, -22, -108, 0, -22, 0, -22, 6, 0,
+ -22, 144, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 0, 0, 0, 0, 0, -22, 0, 0, 0, 35,
+ 0, 0, -22, 0, 137, -22, 0, -22, 7, 8,
+ 9, 10, 11, 12, 13, 0, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 0, 26, 27, 28,
+ 29, 30, 0, 0, 33, 34, 0, 0, 0, 0,
+ -86, 0, 0, 0, 0, 35, 0, 0, 0, 173,
+ 0, 0, -86, 7, 8, 9, 10, 11, 12, 13,
+ 0, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 0, 26, 27, 28, 29, 30, 0, 0, 33,
+ 34, 0, 0, 0, 0, -86, 0, 0, 0, 0,
+ 35, 0, 0, 0, 178, 0, 0, -86, 7, 8,
+ 9, 10, 11, 12, 13, 0, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 0, 26, 27, 28,
+ 29, 30, 0, 0, 33, 34, 0, 0, 0, 0,
+ -86, 0, 0, 0, 0, 35, 0, 0, 0, 0,
+ 0, 0, -86, 7, 8, 9, 10, 11, 12, 13,
+ 0, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 0, 26, 27, 28, 29, 30, 0, 0, 33,
+ 34, 0, 0, 0, 0, 0, 124, 0, 0, 0,
+ 125, 0, 0, 0, 0, 0, 126, 0, 65, 7,
8, 9, 10, 11, 12, 13, 0, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 0, 26, 27,
- 28, 29, 30, 0, 0, 33, 0, 0, 0, 0,
- 178, 0, 0, 0, 0, 34, 7, 8, 9, 10,
- 11, 12, 13, 0, 15, 16, 17, 18, 19, 20,
- 21, 22, 23, 24, 0, 26, 27, 28, 29, 30,
- 0, 0, 33, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 34
+ 28, 29, 30, 0, 0, 33, 34, 0, 0, 0,
+ 0, 181, 0, 0, 0, 0, 35, 7, 8, 9,
+ 10, 11, 12, 13, 0, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 0, 26, 27, 28, 29,
+ 30, 0, 0, 33, 34, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 35
};
#define yypact_value_is_default(yystate) \
- ((yystate) == (-135))
+ ((yystate) == (-140))
#define yytable_value_is_error(yytable_value) \
YYID (0)
static const yytype_int16 yycheck[] =
{
- 59, 38, 79, 3, 1, 8, 56, 37, 26, 37,
- 64, 31, 1, 147, 37, 18, 30, 1, 32, 109,
- 0, 111, 45, 51, 27, 23, 76, 44, 31, 51,
- 50, 37, 109, 63, 111, 53, 33, 91, 68, 57,
- 37, 100, 47, 177, 41, 51, 37, 33, 37, 86,
- 47, 48, 49, 37, 131, 37, 56, 41, 47, 48,
- 51, 47, 47, 47, 48, 47, 48, 33, 37, 37,
- 51, 33, 149, 41, 44, 45, 76, 44, 45, 47,
- 48, 47, 159, 160, 52, 47, 37, 37, 147, 37,
- 41, 45, 46, 41, 131, 44, 47, 48, 50, 47,
- 48, 159, 160, 46, 45, 49, 165, 1, 34, 49,
- 49, 5, 6, 7, 8, 9, 10, 11, 177, 13,
- 14, 15, 16, 17, 18, 19, 20, 21, 22, 52,
- 24, 25, 26, 27, 28, 34, 44, 31, 44, 46,
- 30, 44, 31, 37, 49, 49, 46, 41, 49, 36,
- 44, 45, 49, 47, 48, 1, 49, 34, 52, 5,
- 6, 7, 8, 9, 10, 11, 49, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 49, 24, 25,
- 26, 27, 28, 49, 49, 31, 49, 49, 1, 165,
- 81, 37, 94, 149, 107, 41, 112, 49, 44, 45,
- -1, 47, 48, 1, -1, 103, 52, 5, 6, 7,
- 8, 9, 10, 11, -1, 13, 14, 15, 16, 17,
- 18, 19, 20, 21, 22, -1, 24, 25, 26, 27,
- 28, -1, -1, 31, -1, -1, -1, -1, 36, -1,
- 1, -1, -1, 41, 5, 6, 7, 8, 9, 10,
- 11, 49, 13, 14, 15, 16, 17, 18, 19, 20,
- 21, 22, -1, 24, 25, 26, 27, 28, -1, -1,
- 31, -1, -1, -1, -1, 36, -1, 1, -1, -1,
- 41, 5, 6, 7, 8, 9, 10, 11, 49, 13,
- 14, 15, 16, 17, 18, 19, 20, 21, 22, -1,
- 24, 25, 26, 27, 28, -1, -1, 31, -1, -1,
- -1, -1, 36, -1, -1, -1, -1, 41, -1, -1,
- -1, -1, 1, -1, -1, 49, 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, 31, -1, -1, -1, -1, -1, 37, -1,
- -1, -1, 41, -1, -1, 44, -1, -1, 47, 48,
- 5, 6, 7, 8, 9, 10, 11, -1, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, -1, 24,
- 25, 26, 27, 28, -1, -1, 31, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 41, -1, -1, -1,
- -1, -1, -1, 48, 49, 5, 6, 7, 8, 9,
- 10, 11, -1, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, -1, 24, 25, 26, 27, 28, -1,
- -1, 31, -1, -1, -1, -1, -1, 37, -1, -1,
- -1, 41, -1, -1, -1, -1, -1, 47, 48, 5,
+ 60, 60, 80, 1, 3, 8, 1, 31, 39, 87,
+ 65, 38, 57, 26, 153, 18, 38, 1, 38, 111,
+ 38, 113, 161, 34, 27, 52, 46, 51, 31, 0,
+ 52, 38, 77, 111, 52, 113, 34, 48, 93, 48,
+ 38, 54, 102, 38, 42, 58, 34, 42, 126, 127,
+ 48, 49, 50, 48, 38, 50, 87, 64, 57, 38,
+ 48, 34, 69, 42, 48, 30, 50, 23, 33, 48,
+ 48, 50, 38, 45, 53, 48, 42, 155, 77, 38,
+ 38, 38, 48, 42, 50, 45, 46, 126, 127, 48,
+ 48, 50, 50, 153, 46, 47, 45, 46, 52, 52,
+ 38, 161, 45, 47, 51, 46, 35, 49, 1, 49,
+ 170, 170, 5, 6, 7, 8, 9, 10, 11, 49,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 49, 24, 25, 26, 27, 28, 53, 35, 31, 32,
+ 45, 47, 45, 30, 45, 38, 37, 49, 31, 42,
+ 49, 47, 45, 46, 49, 48, 35, 50, 1, 49,
+ 53, 49, 5, 6, 7, 8, 9, 10, 11, 49,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 49, 24, 25, 26, 27, 28, 49, 49, 31, 32,
+ 49, 1, 96, 82, 155, 38, 109, 50, 114, 42,
+ -1, -1, 45, 46, -1, 48, -1, 50, 1, -1,
+ 53, 105, 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, 31, 32,
+ -1, -1, -1, -1, -1, 38, -1, -1, -1, 42,
+ -1, -1, 45, -1, 1, 48, -1, 50, 5, 6,
+ 7, 8, 9, 10, 11, -1, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, -1, 24, 25, 26,
+ 27, 28, -1, -1, 31, 32, -1, -1, -1, -1,
+ 37, -1, -1, -1, -1, 42, -1, -1, -1, 1,
+ -1, -1, 49, 5, 6, 7, 8, 9, 10, 11,
+ -1, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, -1, 24, 25, 26, 27, 28, -1, -1, 31,
+ 32, -1, -1, -1, -1, 37, -1, -1, -1, -1,
+ 42, -1, -1, -1, 1, -1, -1, 49, 5, 6,
+ 7, 8, 9, 10, 11, -1, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, -1, 24, 25, 26,
+ 27, 28, -1, -1, 31, 32, -1, -1, -1, -1,
+ 37, -1, -1, -1, -1, 42, -1, -1, -1, -1,
+ -1, -1, 49, 5, 6, 7, 8, 9, 10, 11,
+ -1, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, -1, 24, 25, 26, 27, 28, -1, -1, 31,
+ 32, -1, -1, -1, -1, -1, 38, -1, -1, -1,
+ 42, -1, -1, -1, -1, -1, 48, -1, 50, 5,
6, 7, 8, 9, 10, 11, -1, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, -1, 24, 25,
- 26, 27, 28, -1, -1, 31, -1, -1, -1, -1,
- 36, -1, -1, -1, -1, 41, 5, 6, 7, 8,
- 9, 10, 11, -1, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, -1, 24, 25, 26, 27, 28,
- -1, -1, 31, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 41
+ 26, 27, 28, -1, -1, 31, 32, -1, -1, -1,
+ -1, 37, -1, -1, -1, -1, 42, 5, 6, 7,
+ 8, 9, 10, 11, -1, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, -1, 24, 25, 26, 27,
+ 28, -1, -1, 31, 32, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 42
};
/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
symbol of state STATE-NUM. */
static const yytype_uint8 yystos[] =
{
- 0, 54, 55, 56, 0, 55, 1, 5, 6, 7,
+ 0, 55, 56, 57, 0, 56, 1, 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, 31, 41, 57, 60, 64, 65, 66,
- 67, 68, 69, 73, 84, 99, 101, 44, 45, 37,
- 51, 96, 23, 37, 51, 87, 59, 37, 87, 47,
- 47, 44, 37, 47, 48, 61, 62, 63, 70, 74,
- 75, 66, 96, 37, 97, 98, 58, 87, 1, 64,
- 88, 89, 90, 60, 64, 87, 65, 37, 1, 74,
- 71, 72, 73, 44, 46, 74, 30, 32, 100, 33,
- 47, 50, 45, 46, 60, 44, 45, 37, 41, 47,
- 52, 70, 76, 77, 91, 92, 93, 94, 45, 1,
- 90, 74, 48, 49, 49, 49, 49, 73, 63, 95,
- 1, 65, 78, 79, 80, 81, 34, 45, 98, 94,
- 1, 37, 76, 34, 76, 95, 33, 47, 44, 46,
- 49, 44, 31, 50, 85, 86, 49, 37, 41, 47,
- 70, 82, 83, 49, 36, 46, 49, 49, 1, 78,
- 93, 34, 1, 41, 82, 82, 33, 47, 36, 81,
- 49, 49, 49, 49, 1, 78, 49, 49
+ 28, 29, 30, 31, 32, 42, 58, 61, 65, 66,
+ 67, 68, 69, 70, 74, 85, 100, 102, 45, 46,
+ 38, 52, 97, 23, 38, 52, 88, 60, 38, 88,
+ 48, 48, 45, 38, 48, 50, 62, 63, 64, 71,
+ 75, 76, 67, 97, 38, 98, 99, 59, 88, 1,
+ 65, 89, 90, 91, 61, 65, 88, 66, 82, 38,
+ 1, 75, 72, 73, 74, 45, 47, 75, 30, 33,
+ 101, 34, 48, 51, 46, 47, 61, 45, 46, 38,
+ 42, 48, 53, 71, 77, 78, 92, 93, 94, 95,
+ 46, 1, 91, 75, 38, 42, 48, 71, 83, 84,
+ 49, 49, 49, 49, 74, 64, 96, 1, 79, 80,
+ 81, 82, 35, 46, 99, 95, 1, 38, 77, 35,
+ 77, 96, 34, 48, 45, 47, 1, 42, 83, 83,
+ 34, 48, 45, 31, 51, 86, 87, 49, 49, 37,
+ 47, 49, 49, 1, 79, 94, 49, 49, 1, 79,
+ 35, 37, 82, 49, 49, 49, 49
};
#define yyerrok (yyerrstatus = 0)
@@ -890,17 +895,18 @@ static const yytype_uint8 yystos[] =
#define YYRECOVERING() (!!yyerrstatus)
-#define YYBACKUP(Token, Value) \
-do \
- if (yychar == YYEMPTY && yylen == 1) \
- { \
- yychar = (Token); \
- yylval = (Value); \
- YYPOPSTACK (1); \
- goto yybackup; \
- } \
- else \
- { \
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
yyerror (YY_("syntax error: cannot back up")); \
YYERROR; \
} \
@@ -995,6 +1001,8 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep)
YYSTYPE const * const yyvaluep;
#endif
{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
if (!yyvaluep)
return;
# ifdef YYPRINT
@@ -1246,12 +1254,12 @@ static int
yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
yytype_int16 *yyssp, int yytoken)
{
- YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+ YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]);
YYSIZE_T yysize = yysize0;
YYSIZE_T yysize1;
enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
/* Internationalized format string. */
- const char *yyformat = 0;
+ const char *yyformat = YY_NULL;
/* Arguments of yyformat. */
char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
/* Number of reported tokens (one for the "unexpected", one per
@@ -1311,7 +1319,7 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
break;
}
yyarg[yycount++] = yytname[yyx];
- yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]);
if (! (yysize <= yysize1
&& yysize1 <= YYSTACK_ALLOC_MAXIMUM))
return 2;
@@ -1463,7 +1471,7 @@ yyparse ()
`yyss': related to states.
`yyvs': related to semantic values.
- Refer to the stacks thru separate pointers, to allow yyoverflow
+ Refer to the stacks through separate pointers, to allow yyoverflow
to reallocate them elsewhere. */
/* The state stack. */
@@ -2346,7 +2354,7 @@ yyabortlab:
yyresult = 1;
goto yyreturn;
-#if !defined(yyoverflow) || YYERROR_VERBOSE
+#if !defined yyoverflow || YYERROR_VERBOSE
/*-------------------------------------------------.
| yyexhaustedlab -- memory exhaustion comes here. |
`-------------------------------------------------*/
diff --git a/scripts/genksyms/parse.tab.h_shipped b/scripts/genksyms/parse.tab.h_shipped
index 93240a3cdecc..a4737dec4532 100644
--- a/scripts/genksyms/parse.tab.h_shipped
+++ b/scripts/genksyms/parse.tab.h_shipped
@@ -1,8 +1,8 @@
-/* A Bison parser, made by GNU Bison 2.5. */
+/* A Bison parser, made by GNU Bison 2.5.1. */
/* Bison interface for Yacc-like parsers in C
- Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+ Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, 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
@@ -66,18 +66,19 @@
EXPORT_SYMBOL_KEYW = 284,
ASM_PHRASE = 285,
ATTRIBUTE_PHRASE = 286,
- BRACE_PHRASE = 287,
- BRACKET_PHRASE = 288,
- EXPRESSION_PHRASE = 289,
- CHAR = 290,
- DOTS = 291,
- IDENT = 292,
- INT = 293,
- REAL = 294,
- STRING = 295,
- TYPE = 296,
- OTHER = 297,
- FILENAME = 298
+ TYPEOF_PHRASE = 287,
+ BRACE_PHRASE = 288,
+ BRACKET_PHRASE = 289,
+ EXPRESSION_PHRASE = 290,
+ CHAR = 291,
+ DOTS = 292,
+ IDENT = 293,
+ INT = 294,
+ REAL = 295,
+ STRING = 296,
+ TYPE = 297,
+ OTHER = 298,
+ FILENAME = 299
};
#endif
diff --git a/scripts/genksyms/parse.y b/scripts/genksyms/parse.y
index 23c39998ad86..b9f4cf202302 100644
--- a/scripts/genksyms/parse.y
+++ b/scripts/genksyms/parse.y
@@ -103,6 +103,7 @@ static void record_compound(struct string_list **keyw,
%token ASM_PHRASE
%token ATTRIBUTE_PHRASE
+%token TYPEOF_PHRASE
%token BRACE_PHRASE
%token BRACKET_PHRASE
%token EXPRESSION_PHRASE
@@ -220,8 +221,8 @@ storage_class_specifier:
type_specifier:
simple_type_specifier
| cvar_qualifier
- | TYPEOF_KEYW '(' decl_specifier_seq '*' ')'
- | TYPEOF_KEYW '(' decl_specifier_seq ')'
+ | TYPEOF_KEYW '(' parameter_declaration ')'
+ | TYPEOF_PHRASE
/* References to s/u/e's defined elsewhere. Rearrange things
so that it is easier to expand the definition fully later. */
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 10085de886fe..1237dd7fb4ca 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -36,13 +36,13 @@ struct sym_entry {
unsigned char *sym;
};
-struct text_range {
- const char *stext, *etext;
+struct addr_range {
+ const char *start_sym, *end_sym;
unsigned long long start, end;
};
static unsigned long long _text;
-static struct text_range text_ranges[] = {
+static struct addr_range text_ranges[] = {
{ "_stext", "_etext" },
{ "_sinittext", "_einittext" },
{ "_stext_l1", "_etext_l1" }, /* Blackfin on-chip L1 inst SRAM */
@@ -51,9 +51,14 @@ static struct text_range text_ranges[] = {
#define text_range_text (&text_ranges[0])
#define text_range_inittext (&text_ranges[1])
+static struct addr_range percpu_range = {
+ "__per_cpu_start", "__per_cpu_end", -1ULL, 0
+};
+
static struct sym_entry *table;
static unsigned int table_size, table_cnt;
static int all_symbols = 0;
+static int absolute_percpu = 0;
static char symbol_prefix_char = '\0';
static unsigned long long kernel_start_addr = 0;
@@ -83,19 +88,20 @@ static inline int is_arm_mapping_symbol(const char *str)
&& (str[2] == '\0' || str[2] == '.');
}
-static int read_symbol_tr(const char *sym, unsigned long long addr)
+static int check_symbol_range(const char *sym, unsigned long long addr,
+ struct addr_range *ranges, int entries)
{
size_t i;
- struct text_range *tr;
+ struct addr_range *ar;
- for (i = 0; i < ARRAY_SIZE(text_ranges); ++i) {
- tr = &text_ranges[i];
+ for (i = 0; i < entries; ++i) {
+ ar = &ranges[i];
- if (strcmp(sym, tr->stext) == 0) {
- tr->start = addr;
+ if (strcmp(sym, ar->start_sym) == 0) {
+ ar->start = addr;
return 0;
- } else if (strcmp(sym, tr->etext) == 0) {
- tr->end = addr;
+ } else if (strcmp(sym, ar->end_sym) == 0) {
+ ar->end = addr;
return 0;
}
}
@@ -130,7 +136,8 @@ static int read_symbol(FILE *in, struct sym_entry *s)
/* Ignore most absolute/undefined (?) symbols. */
if (strcmp(sym, "_text") == 0)
_text = s->addr;
- else if (read_symbol_tr(sym, s->addr) == 0)
+ else if (check_symbol_range(sym, s->addr, text_ranges,
+ ARRAY_SIZE(text_ranges)) == 0)
/* nothing to do */;
else if (toupper(stype) == 'A')
{
@@ -164,18 +171,22 @@ static int read_symbol(FILE *in, struct sym_entry *s)
strcpy((char *)s->sym + 1, str);
s->sym[0] = stype;
+ /* Record if we've found __per_cpu_start/end. */
+ check_symbol_range(sym, s->addr, &percpu_range, 1);
+
return 0;
}
-static int symbol_valid_tr(struct sym_entry *s)
+static int symbol_in_range(struct sym_entry *s, struct addr_range *ranges,
+ int entries)
{
size_t i;
- struct text_range *tr;
+ struct addr_range *ar;
- for (i = 0; i < ARRAY_SIZE(text_ranges); ++i) {
- tr = &text_ranges[i];
+ for (i = 0; i < entries; ++i) {
+ ar = &ranges[i];
- if (s->addr >= tr->start && s->addr <= tr->end)
+ if (s->addr >= ar->start && s->addr <= ar->end)
return 1;
}
@@ -214,7 +225,8 @@ static int symbol_valid(struct sym_entry *s)
/* if --all-symbols is not specified, then symbols outside the text
* and inittext sections are discarded */
if (!all_symbols) {
- if (symbol_valid_tr(s) == 0)
+ if (symbol_in_range(s, text_ranges,
+ ARRAY_SIZE(text_ranges)) == 0)
return 0;
/* Corner case. Discard any symbols with the same value as
* _etext _einittext; they can move between pass 1 and 2 when
@@ -223,9 +235,11 @@ static int symbol_valid(struct sym_entry *s)
* rules.
*/
if ((s->addr == text_range_text->end &&
- strcmp((char *)s->sym + offset, text_range_text->etext)) ||
+ strcmp((char *)s->sym + offset,
+ text_range_text->end_sym)) ||
(s->addr == text_range_inittext->end &&
- strcmp((char *)s->sym + offset, text_range_inittext->etext)))
+ strcmp((char *)s->sym + offset,
+ text_range_inittext->end_sym)))
return 0;
}
@@ -298,6 +312,11 @@ static int expand_symbol(unsigned char *data, int len, char *result)
return total;
}
+static int symbol_absolute(struct sym_entry *s)
+{
+ return toupper(s->sym[0]) == 'A';
+}
+
static void write_src(void)
{
unsigned int i, k, off;
@@ -325,7 +344,7 @@ static void write_src(void)
*/
output_label("kallsyms_addresses");
for (i = 0; i < table_cnt; i++) {
- if (toupper(table[i].sym[0]) != 'A') {
+ if (!symbol_absolute(&table[i])) {
if (_text <= table[i].addr)
printf("\tPTR\t_text + %#llx\n",
table[i].addr - _text);
@@ -646,6 +665,15 @@ static void sort_symbols(void)
qsort(table, table_cnt, sizeof(struct sym_entry), compare_symbols);
}
+static void make_percpus_absolute(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < table_cnt; i++)
+ if (symbol_in_range(&table[i], &percpu_range, 1))
+ table[i].sym[0] = 'A';
+}
+
int main(int argc, char **argv)
{
if (argc >= 2) {
@@ -653,6 +681,8 @@ int main(int argc, char **argv)
for (i = 1; i < argc; i++) {
if(strcmp(argv[i], "--all-symbols") == 0)
all_symbols = 1;
+ else if (strcmp(argv[i], "--absolute-percpu") == 0)
+ absolute_percpu = 1;
else if (strncmp(argv[i], "--symbol-prefix=", 16) == 0) {
char *p = &argv[i][16];
/* skip quote */
@@ -669,6 +699,8 @@ int main(int argc, char **argv)
usage();
read_map(stdin);
+ if (absolute_percpu)
+ make_percpus_absolute();
sort_symbols();
optimize_token_table();
write_src();
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 2dcb37736d84..86a4fe75f453 100644
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -86,6 +86,10 @@ kallsyms()
kallsymopt="${kallsymopt} --page-offset=$CONFIG_PAGE_OFFSET"
fi
+ if [ -n "${CONFIG_X86_64}" ]; then
+ kallsymopt="${kallsymopt} --absolute-percpu"
+ fi
+
local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \
${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}"
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 25f6f5970552..1924990a737f 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -42,7 +42,7 @@ typedef unsigned char __u8;
/* This array collects all instances that use the generic do_table */
struct devtable {
- const char *device_id; /* name of table, __mod_<name>_device_table. */
+ const char *device_id; /* name of table, __mod_<name>__*_device_table. */
unsigned long id_size;
void *function;
};
@@ -146,7 +146,8 @@ static void device_id_check(const char *modname, const char *device_id,
if (size % id_size || size < id_size) {
fatal("%s: sizeof(struct %s_device_id)=%lu is not a modulo "
- "of the size of section __mod_%s_device_table=%lu.\n"
+ "of the size of "
+ "section __mod_%s__<identifier>_device_table=%lu.\n"
"Fix definition of struct %s_device_id "
"in mod_devicetable.h\n",
modname, device_id, id_size, device_id, size, device_id);
@@ -1216,7 +1217,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
{
void *symval;
char *zeros = NULL;
- const char *name;
+ const char *name, *identifier;
unsigned int namelen;
/* We're looking for a section relative symbol */
@@ -1227,7 +1228,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT)
return;
- /* All our symbols are of form <prefix>__mod_XXX_device_table. */
+ /* All our symbols are of form <prefix>__mod_<name>__<identifier>_device_table. */
name = strstr(symname, "__mod_");
if (!name)
return;
@@ -1237,7 +1238,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
return;
if (strcmp(name + namelen - strlen("_device_table"), "_device_table"))
return;
- namelen -= strlen("_device_table");
+ identifier = strstr(name, "__");
+ if (!identifier)
+ return;
+ namelen = identifier - name;
/* Handle all-NULL symbols allocated into .bss */
if (info->sechdrs[get_secindex(info, sym)].sh_type & SHT_NOBITS) {
diff --git a/security/Makefile b/security/Makefile
index a5918e01a4f7..05f1c934d74b 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -16,14 +16,14 @@ obj-$(CONFIG_MMU) += min_addr.o
# Object file lists
obj-$(CONFIG_SECURITY) += security.o capability.o
obj-$(CONFIG_SECURITYFS) += inode.o
-obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
-obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
+obj-$(CONFIG_SECURITY_SELINUX) += selinux/
+obj-$(CONFIG_SECURITY_SMACK) += smack/
obj-$(CONFIG_AUDIT) += lsm_audit.o
-obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o
-obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o
-obj-$(CONFIG_SECURITY_YAMA) += yama/built-in.o
+obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/
+obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
+obj-$(CONFIG_SECURITY_YAMA) += yama/
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
# Object integrity file lists
subdir-$(CONFIG_INTEGRITY) += integrity
-obj-$(CONFIG_INTEGRITY) += integrity/built-in.o
+obj-$(CONFIG_INTEGRITY) += integrity/
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 4257b7e2796b..998100093332 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -751,7 +751,7 @@ module_param_named(enabled, apparmor_enabled, bool, S_IRUGO);
static int __init apparmor_enabled_setup(char *str)
{
unsigned long enabled;
- int error = strict_strtoul(str, 0, &enabled);
+ int error = kstrtoul(str, 0, &enabled);
if (!error)
apparmor_enabled = enabled ? 1 : 0;
return 1;
diff --git a/security/capability.c b/security/capability.c
index 21e2b9cae685..ad0d4de69944 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -116,7 +116,7 @@ static int cap_dentry_init_security(struct dentry *dentry, int mode,
struct qstr *name, void **ctx,
u32 *ctxlen)
{
- return 0;
+ return -EOPNOTSUPP;
}
static int cap_inode_alloc_security(struct inode *inode)
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index d3b6d2cd3a06..8365909f5f8c 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -58,11 +58,9 @@ static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
{
- return css_to_devcgroup(task_css(task, devices_subsys_id));
+ return css_to_devcgroup(task_css(task, devices_cgrp_id));
}
-struct cgroup_subsys devices_subsys;
-
/*
* called under devcgroup_mutex
*/
@@ -498,7 +496,7 @@ static inline bool has_children(struct dev_cgroup *devcgroup)
* parent cgroup has the access you're asking for.
*/
static int devcgroup_update_access(struct dev_cgroup *devcgroup,
- int filetype, const char *buffer)
+ int filetype, char *buffer)
{
const char *b;
char temp[12]; /* 11 + 1 characters needed for a u32 */
@@ -654,7 +652,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
}
static int devcgroup_access_write(struct cgroup_subsys_state *css,
- struct cftype *cft, const char *buffer)
+ struct cftype *cft, char *buffer)
{
int retval;
@@ -684,13 +682,11 @@ static struct cftype dev_cgroup_files[] = {
{ } /* terminate */
};
-struct cgroup_subsys devices_subsys = {
- .name = "devices",
+struct cgroup_subsys devices_cgrp_subsys = {
.css_alloc = devcgroup_css_alloc,
.css_free = devcgroup_css_free,
.css_online = devcgroup_online,
.css_offline = devcgroup_offline,
- .subsys_id = devices_subsys_id,
.base_cftypes = dev_cgroup_files,
};
diff --git a/security/integrity/Makefile b/security/integrity/Makefile
index 0f9cffb1f9ad..0793f4811cb7 100644
--- a/security/integrity/Makefile
+++ b/security/integrity/Makefile
@@ -10,6 +10,6 @@ obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
integrity-y := iint.o
subdir-$(CONFIG_IMA) += ima
-obj-$(CONFIG_IMA) += ima/built-in.o
+obj-$(CONFIG_IMA) += ima/
subdir-$(CONFIG_EVM) += evm
-obj-$(CONFIG_EVM) += evm/built-in.o
+obj-$(CONFIG_EVM) += evm/
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
index fea9749c3756..d35b4915b00d 100644
--- a/security/integrity/evm/Kconfig
+++ b/security/integrity/evm/Kconfig
@@ -1,10 +1,10 @@
config EVM
boolean "EVM support"
- depends on SECURITY && KEYS && (TRUSTED_KEYS=y || TRUSTED_KEYS=n)
+ depends on SECURITY
+ select KEYS
+ select ENCRYPTED_KEYS
select CRYPTO_HMAC
- select CRYPTO_MD5
select CRYPTO_SHA1
- select ENCRYPTED_KEYS
default n
help
EVM protects a file's security extended attributes against
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index 30bd1ec0232e..37c88ddb3cfe 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -32,19 +32,19 @@ extern struct crypto_shash *hash_tfm;
/* List of EVM protected security xattrs */
extern char *evm_config_xattrnames[];
-extern int evm_init_key(void);
-extern int evm_update_evmxattr(struct dentry *dentry,
- const char *req_xattr_name,
- const char *req_xattr_value,
- size_t req_xattr_value_len);
-extern int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
- const char *req_xattr_value,
- size_t req_xattr_value_len, char *digest);
-extern int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
- const char *req_xattr_value,
- size_t req_xattr_value_len, char *digest);
-extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
- char *hmac_val);
-extern int evm_init_secfs(void);
+int evm_init_key(void);
+int evm_update_evmxattr(struct dentry *dentry,
+ const char *req_xattr_name,
+ const char *req_xattr_value,
+ size_t req_xattr_value_len);
+int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
+ const char *req_xattr_value,
+ size_t req_xattr_value_len, char *digest);
+int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
+ const char *req_xattr_value,
+ size_t req_xattr_value_len, char *digest);
+int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
+ char *hmac_val);
+int evm_init_secfs(void);
#endif
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index 3bab89eb21d6..babd8626bf96 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -13,6 +13,8 @@
* Using root's kernel master key (kmk), calculate the HMAC
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/xattr.h>
@@ -103,13 +105,13 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
umode_t mode;
} hmac_misc;
- memset(&hmac_misc, 0, sizeof hmac_misc);
+ memset(&hmac_misc, 0, sizeof(hmac_misc));
hmac_misc.ino = inode->i_ino;
hmac_misc.generation = inode->i_generation;
hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid);
hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
hmac_misc.mode = inode->i_mode;
- crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc);
+ crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
if (evm_hmac_version > 1)
crypto_shash_update(desc, inode->i_sb->s_uuid,
sizeof(inode->i_sb->s_uuid));
@@ -221,7 +223,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
desc = init_desc(EVM_XATTR_HMAC);
if (IS_ERR(desc)) {
- printk(KERN_INFO "init_desc failed\n");
+ pr_info("init_desc failed\n");
return PTR_ERR(desc);
}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 336b3ddfe63f..996092f21b64 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -14,6 +14,8 @@
* evm_inode_removexattr, and evm_verifyxattr
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/audit.h>
@@ -432,7 +434,7 @@ static int __init init_evm(void)
error = evm_init_secfs();
if (error < 0) {
- printk(KERN_INFO "EVM: Error registering secfs\n");
+ pr_info("Error registering secfs\n");
goto err;
}
@@ -449,7 +451,7 @@ static int __init evm_display_config(void)
char **xattrname;
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++)
- printk(KERN_INFO "EVM: %s\n", *xattrname);
+ pr_info("%s\n", *xattrname);
return 0;
}
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index 30f670ad6ac3..cf12a04717d3 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -13,6 +13,8 @@
* - Get the key and enable EVM
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/uaccess.h>
#include <linux/module.h>
#include "evm.h"
@@ -79,9 +81,9 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf,
error = evm_init_key();
if (!error) {
evm_initialized = 1;
- pr_info("EVM: initialized\n");
+ pr_info("initialized\n");
} else
- pr_err("EVM: initialization failed\n");
+ pr_err("initialization failed\n");
return count;
}
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index c49d3f14cbec..a521edf4cbd6 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -151,7 +151,7 @@ static void init_once(void *foo)
{
struct integrity_iint_cache *iint = foo;
- memset(iint, 0, sizeof *iint);
+ memset(iint, 0, sizeof(*iint));
iint->version = 0;
iint->flags = 0UL;
iint->ima_file_status = INTEGRITY_UNKNOWN;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 0356e1d437ca..f79fa8be203c 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -27,7 +27,7 @@
#include "../integrity.h"
enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN,
- IMA_SHOW_ASCII };
+ IMA_SHOW_BINARY_OLD_STRING_FMT, IMA_SHOW_ASCII };
enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
/* digest size for IMA, fits SHA1 or MD5 */
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index c38bbce8c6a6..ba9e4d792dd5 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -92,8 +92,8 @@ int ima_store_template(struct ima_template_entry *entry,
int violation, struct inode *inode,
const unsigned char *filename)
{
- const char *op = "add_template_measure";
- const char *audit_cause = "hashing_error";
+ static const char op[] = "add_template_measure";
+ static const char audit_cause[] = "hashing_error";
char *template_name = entry->template_desc->name;
int result;
struct {
@@ -132,7 +132,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
const char *op, const char *cause)
{
struct ima_template_entry *entry;
- struct inode *inode = file->f_dentry->d_inode;
+ struct inode *inode = file_inode(file);
int violation = 1;
int result;
@@ -160,10 +160,10 @@ err_out:
* @function: calling function (FILE_CHECK, BPRM_CHECK, MMAP_CHECK, MODULE_CHECK)
*
* The policy is defined in terms of keypairs:
- * subj=, obj=, type=, func=, mask=, fsmagic=
+ * subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
- * func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK
- * mask: contains the permission mask
+ * func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK
+ * mask: contains the permission mask
* fsmagic: hex value
*
* Returns IMA_MEASURE, IMA_APPRAISE mask.
@@ -248,7 +248,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
*
* We only get here if the inode has not already been measured,
* but the measurement could already exist:
- * - multiple copies of the same file on either the same or
+ * - multiple copies of the same file on either the same or
* different filesystems.
* - the inode was previously flushed as well as the iint info,
* containing the hashing info.
@@ -260,8 +260,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
struct evm_ima_xattr_data *xattr_value,
int xattr_len)
{
- const char *op = "add_template_measure";
- const char *audit_cause = "ENOMEM";
+ static const char op[] = "add_template_measure";
+ static const char audit_cause[] = "ENOMEM";
int result = -ENOMEM;
struct inode *inode = file_inode(file);
struct ima_template_entry *entry;
@@ -332,5 +332,5 @@ const char *ima_d_path(struct path *path, char **pathbuf)
pathname = NULL;
}
}
- return pathname;
+ return pathname ?: (const char *)path->dentry->d_name.name;
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 734e9468aca0..291bf0f3a46d 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -177,11 +177,11 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
struct evm_ima_xattr_data *xattr_value,
int xattr_len)
{
+ static const char op[] = "appraise_data";
+ char *cause = "unknown";
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
enum integrity_status status = INTEGRITY_UNKNOWN;
- const char *op = "appraise_data";
- char *cause = "unknown";
int rc = xattr_len, hash_start = 0;
if (!ima_appraise)
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index fdf60def52e9..1bde8e627766 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -10,9 +10,11 @@
* the Free Software Foundation, version 2 of the License.
*
* File: ima_crypto.c
- * Calculates md5/sha1 file hash, template hash, boot-aggreate hash
+ * Calculates md5/sha1 file hash, template hash, boot-aggreate hash
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/file.h>
#include <linux/crypto.h>
@@ -85,16 +87,20 @@ static int ima_calc_file_hash_tfm(struct file *file,
if (rc != 0)
return rc;
- rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (!rbuf) {
- rc = -ENOMEM;
+ i_size = i_size_read(file_inode(file));
+
+ if (i_size == 0)
goto out;
- }
+
+ rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!rbuf)
+ return -ENOMEM;
+
if (!(file->f_mode & FMODE_READ)) {
file->f_mode |= FMODE_READ;
read = 1;
}
- i_size = i_size_read(file_inode(file));
+
while (offset < i_size) {
int rbuf_len;
@@ -111,12 +117,12 @@ static int ima_calc_file_hash_tfm(struct file *file,
if (rc)
break;
}
- kfree(rbuf);
- if (!rc)
- rc = crypto_shash_final(&desc.shash, hash->digest);
if (read)
file->f_mode &= ~FMODE_READ;
+ kfree(rbuf);
out:
+ if (!rc)
+ rc = crypto_shash_final(&desc.shash, hash->digest);
return rc;
}
@@ -161,15 +167,22 @@ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data,
return rc;
for (i = 0; i < num_fields; i++) {
+ u8 buffer[IMA_EVENT_NAME_LEN_MAX + 1] = { 0 };
+ u8 *data_to_hash = field_data[i].data;
+ u32 datalen = field_data[i].len;
+
if (strcmp(td->name, IMA_TEMPLATE_IMA_NAME) != 0) {
rc = crypto_shash_update(&desc.shash,
(const u8 *) &field_data[i].len,
sizeof(field_data[i].len));
if (rc)
break;
+ } else if (strcmp(td->fields[i]->field_id, "n") == 0) {
+ memcpy(buffer, data_to_hash, datalen);
+ data_to_hash = buffer;
+ datalen = IMA_EVENT_NAME_LEN_MAX + 1;
}
- rc = crypto_shash_update(&desc.shash, field_data[i].data,
- field_data[i].len);
+ rc = crypto_shash_update(&desc.shash, data_to_hash, datalen);
if (rc)
break;
}
@@ -205,7 +218,7 @@ static void __init ima_pcrread(int idx, u8 *pcr)
return;
if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0)
- pr_err("IMA: Error Communicating to TPM chip\n");
+ pr_err("Error Communicating to TPM chip\n");
}
/*
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index db01125926bd..da92fcc08d15 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -133,14 +133,14 @@ static int ima_measurements_show(struct seq_file *m, void *v)
* PCR used is always the same (config option) in
* little-endian format
*/
- ima_putc(m, &pcr, sizeof pcr);
+ ima_putc(m, &pcr, sizeof(pcr));
/* 2nd: template digest */
ima_putc(m, e->digest, TPM_DIGEST_SIZE);
/* 3rd: template name size */
namelen = strlen(e->template_desc->name);
- ima_putc(m, &namelen, sizeof namelen);
+ ima_putc(m, &namelen, sizeof(namelen));
/* 4th: template name */
ima_putc(m, e->template_desc->name, namelen);
@@ -160,6 +160,8 @@ static int ima_measurements_show(struct seq_file *m, void *v)
if (is_ima_template && strcmp(field->field_id, "d") == 0)
show = IMA_SHOW_BINARY_NO_FIELD_LEN;
+ if (is_ima_template && strcmp(field->field_id, "n") == 0)
+ show = IMA_SHOW_BINARY_OLD_STRING_FMT;
field->field_show(m, show, &e->template_data[i]);
}
return 0;
@@ -290,7 +292,7 @@ static atomic_t policy_opencount = ATOMIC_INIT(1);
/*
* ima_open_policy: sequentialize access to the policy file
*/
-static int ima_open_policy(struct inode * inode, struct file * filp)
+static int ima_open_policy(struct inode *inode, struct file *filp)
{
/* No point in being allowed to open it if you aren't going to write */
if (!(filp->f_flags & O_WRONLY))
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 37122768554a..e8f9d70a465d 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -14,6 +14,9 @@
* File: ima_init.c
* initialization and cleanup functions
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
@@ -42,10 +45,10 @@ int ima_used_chip;
*/
static void __init ima_add_boot_aggregate(void)
{
+ static const char op[] = "add_boot_aggregate";
+ const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry;
struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
- const char *op = "add_boot_aggregate";
- const char *audit_cause = "ENOMEM";
int result = -ENOMEM;
int violation = 0;
struct {
@@ -93,7 +96,7 @@ int __init ima_init(void)
ima_used_chip = 1;
if (!ima_used_chip)
- pr_info("IMA: No TPM chip found, activating TPM-bypass!\n");
+ pr_info("No TPM chip found, activating TPM-bypass!\n");
rc = ima_init_crypto();
if (rc)
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 149ee1119f87..52ac6cf41f88 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -71,15 +71,14 @@ __setup("ima_hash=", hash_setup);
* ima_rdwr_violation_check
*
* Only invalidate the PCR for measured files:
- * - Opening a file for write when already open for read,
+ * - Opening a file for write when already open for read,
* results in a time of measure, time of use (ToMToU) error.
* - Opening a file for read when already open for write,
- * could result in a file measurement error.
+ * could result in a file measurement error.
*
*/
static void ima_rdwr_violation_check(struct file *file)
{
- struct dentry *dentry = file->f_path.dentry;
struct inode *inode = file_inode(file);
fmode_t mode = file->f_mode;
int must_measure;
@@ -111,8 +110,6 @@ out:
return;
pathname = ima_d_path(&file->f_path, &pathbuf);
- if (!pathname || strlen(pathname) > IMA_EVENT_NAME_LEN_MAX)
- pathname = dentry->d_name.name;
if (send_tomtou)
ima_add_violation(file, pathname, "invalid_pcr", "ToMToU");
@@ -220,9 +217,7 @@ static int process_measurement(struct file *file, const char *filename,
if (rc != 0)
goto out_digsig;
- pathname = !filename ? ima_d_path(&file->f_path, &pathbuf) : filename;
- if (!pathname)
- pathname = (const char *)file->f_dentry->d_name.name;
+ pathname = filename ?: ima_d_path(&file->f_path, &pathbuf);
if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname,
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index a9c3d3cd1990..93873a450ff7 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -7,7 +7,7 @@
* the Free Software Foundation, version 2 of the License.
*
* ima_policy.c
- * - initialize default measure policy rules
+ * - initialize default measure policy rules
*
*/
#include <linux/module.h>
@@ -21,8 +21,8 @@
#include "ima.h"
/* flags definitions */
-#define IMA_FUNC 0x0001
-#define IMA_MASK 0x0002
+#define IMA_FUNC 0x0001
+#define IMA_MASK 0x0002
#define IMA_FSMAGIC 0x0004
#define IMA_UID 0x0008
#define IMA_FOWNER 0x0010
@@ -69,35 +69,35 @@ struct ima_rule_entry {
* and running executables.
*/
static struct ima_rule_entry default_rules[] = {
- {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_MEASURE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
- {.action = MEASURE,.func = MMAP_CHECK,.mask = MAY_EXEC,
+ {.action = DONT_MEASURE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = MEASURE, .func = MMAP_CHECK, .mask = MAY_EXEC,
.flags = IMA_FUNC | IMA_MASK},
- {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
+ {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC,
.flags = IMA_FUNC | IMA_MASK},
- {.action = MEASURE,.func = FILE_CHECK,.mask = MAY_READ,.uid = GLOBAL_ROOT_UID,
+ {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, .uid = GLOBAL_ROOT_UID,
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
- {.action = MEASURE,.func = MODULE_CHECK, .flags = IMA_FUNC},
+ {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC},
};
static struct ima_rule_entry default_appraise_rules[] = {
- {.action = DONT_APPRAISE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_APPRAISE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_APPRAISE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_APPRAISE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_APPRAISE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_APPRAISE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_APPRAISE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_APPRAISE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_APPRAISE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
- {.action = DONT_APPRAISE,.fsmagic = CGROUP_SUPER_MAGIC,.flags = IMA_FSMAGIC},
- {.action = APPRAISE,.fowner = GLOBAL_ROOT_UID,.flags = IMA_FOWNER},
+ {.action = DONT_APPRAISE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = RAMFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER},
};
static LIST_HEAD(ima_default_rules);
@@ -122,12 +122,12 @@ static int __init default_appraise_policy_setup(char *str)
}
__setup("ima_appraise_tcb", default_appraise_policy_setup);
-/*
+/*
* Although the IMA policy does not change, the LSM policy can be
* reloaded, leaving the IMA LSM based rules referring to the old,
* stale LSM policy.
*
- * Update the IMA LSM based rules to reflect the reloaded LSM policy.
+ * Update the IMA LSM based rules to reflect the reloaded LSM policy.
* We assume the rules still exist; and BUG_ON() if they don't.
*/
static void ima_lsm_update_rules(void)
@@ -167,9 +167,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
const struct cred *cred = current_cred();
int i;
- if ((rule->flags & IMA_FUNC) && rule->func != func)
+ if ((rule->flags & IMA_FUNC) &&
+ (rule->func != func && func != POST_SETATTR))
return false;
- if ((rule->flags & IMA_MASK) && rule->mask != mask)
+ if ((rule->flags & IMA_MASK) &&
+ (rule->mask != mask && func != POST_SETATTR))
return false;
if ((rule->flags & IMA_FSMAGIC)
&& rule->fsmagic != inode->i_sb->s_magic)
@@ -216,7 +218,7 @@ retry:
retried = 1;
ima_lsm_update_rules();
goto retry;
- }
+ }
if (!rc)
return false;
}
@@ -232,7 +234,7 @@ static int get_subaction(struct ima_rule_entry *rule, int func)
if (!(rule->flags & IMA_FUNC))
return IMA_FILE_APPRAISE;
- switch(func) {
+ switch (func) {
case MMAP_CHECK:
return IMA_MMAP_APPRAISE;
case BPRM_CHECK:
@@ -304,7 +306,7 @@ void __init ima_init_policy(void)
measure_entries = ima_use_tcb ? ARRAY_SIZE(default_rules) : 0;
appraise_entries = ima_use_appraise_tcb ?
ARRAY_SIZE(default_appraise_rules) : 0;
-
+
for (i = 0; i < measure_entries + appraise_entries; i++) {
if (i < measure_entries)
list_add_tail(&default_rules[i].list,
@@ -329,7 +331,7 @@ void __init ima_init_policy(void)
*/
void ima_update_policy(void)
{
- const char *op = "policy_update";
+ static const char op[] = "policy_update";
const char *cause = "already exists";
int result = 1;
int audit_info = 0;
@@ -520,8 +522,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break;
}
- result = strict_strtoul(args[0].from, 16,
- &entry->fsmagic);
+ result = kstrtoul(args[0].from, 16, &entry->fsmagic);
if (!result)
entry->flags |= IMA_FSMAGIC;
break;
@@ -547,7 +548,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break;
}
- result = strict_strtoul(args[0].from, 10, &lnum);
+ result = kstrtoul(args[0].from, 10, &lnum);
if (!result) {
entry->uid = make_kuid(current_user_ns(), (uid_t)lnum);
if (!uid_valid(entry->uid) || (((uid_t)lnum) != lnum))
@@ -564,7 +565,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break;
}
- result = strict_strtoul(args[0].from, 10, &lnum);
+ result = kstrtoul(args[0].from, 10, &lnum);
if (!result) {
entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum);
if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum))
@@ -645,7 +646,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
*/
ssize_t ima_parse_add_rule(char *rule)
{
- const char *op = "update_policy";
+ static const char op[] = "update_policy";
char *p;
struct ima_rule_entry *entry;
ssize_t result, len;
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index d85e99761f4f..552705d5a78d 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -18,6 +18,9 @@
* The measurement list is append-only. No entry is
* ever removed or changed during the boot-cycle.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/rculist.h>
#include <linux/slab.h>
@@ -72,7 +75,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry)
qe = kmalloc(sizeof(*qe), GFP_KERNEL);
if (qe == NULL) {
- pr_err("IMA: OUT OF MEMORY ERROR creating queue entry.\n");
+ pr_err("OUT OF MEMORY ERROR creating queue entry\n");
return -ENOMEM;
}
qe->entry = entry;
@@ -95,8 +98,7 @@ static int ima_pcr_extend(const u8 *hash)
result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
if (result != 0)
- pr_err("IMA: Error Communicating to TPM chip, result: %d\n",
- result);
+ pr_err("Error Communicating to TPM chip, result: %d\n", result);
return result;
}
@@ -115,7 +117,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
mutex_lock(&ima_extend_list_mutex);
if (!violation) {
- memcpy(digest, entry->digest, sizeof digest);
+ memcpy(digest, entry->digest, sizeof(digest));
if (ima_lookup_digest_entry(digest)) {
audit_cause = "hash_exists";
result = -EEXIST;
@@ -131,7 +133,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
}
if (violation) /* invalidate pcr */
- memset(digest, 0xff, sizeof digest);
+ memset(digest, 0xff, sizeof(digest));
tpmresult = ima_pcr_extend(digest);
if (tpmresult != 0) {
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 635695f6a185..a076a967ec47 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -12,6 +12,9 @@
* File: ima_template.c
* Helpers to manage template descriptors.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <crypto/hash_info.h>
#include "ima.h"
@@ -19,20 +22,20 @@
static struct ima_template_desc defined_templates[] = {
{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
- {.name = "ima-ng",.fmt = "d-ng|n-ng"},
- {.name = "ima-sig",.fmt = "d-ng|n-ng|sig"},
+ {.name = "ima-ng", .fmt = "d-ng|n-ng"},
+ {.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
};
static struct ima_template_field supported_fields[] = {
- {.field_id = "d",.field_init = ima_eventdigest_init,
+ {.field_id = "d", .field_init = ima_eventdigest_init,
.field_show = ima_show_template_digest},
- {.field_id = "n",.field_init = ima_eventname_init,
+ {.field_id = "n", .field_init = ima_eventname_init,
.field_show = ima_show_template_string},
- {.field_id = "d-ng",.field_init = ima_eventdigest_ng_init,
+ {.field_id = "d-ng", .field_init = ima_eventdigest_ng_init,
.field_show = ima_show_template_digest_ng},
- {.field_id = "n-ng",.field_init = ima_eventname_ng_init,
+ {.field_id = "n-ng", .field_init = ima_eventname_ng_init,
.field_show = ima_show_template_string},
- {.field_id = "sig",.field_init = ima_eventsig_init,
+ {.field_id = "sig", .field_init = ima_eventsig_init,
.field_show = ima_show_template_sig},
};
@@ -58,7 +61,7 @@ static int __init ima_template_setup(char *str)
*/
if (template_len == 3 && strcmp(str, IMA_TEMPLATE_IMA_NAME) == 0 &&
ima_hash_algo != HASH_ALGO_SHA1 && ima_hash_algo != HASH_ALGO_MD5) {
- pr_err("IMA: template does not support hash alg\n");
+ pr_err("template does not support hash alg\n");
return 1;
}
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 1683bbf289a4..1506f0248572 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -27,7 +27,6 @@ static bool ima_template_hash_algo_allowed(u8 algo)
enum data_formats {
DATA_FMT_DIGEST = 0,
DATA_FMT_DIGEST_WITH_ALGO,
- DATA_FMT_EVENT_NAME,
DATA_FMT_STRING,
DATA_FMT_HEX
};
@@ -37,18 +36,10 @@ static int ima_write_template_field_data(const void *data, const u32 datalen,
struct ima_field_data *field_data)
{
u8 *buf, *buf_ptr;
- u32 buflen;
+ u32 buflen = datalen;
- switch (datafmt) {
- case DATA_FMT_EVENT_NAME:
- buflen = IMA_EVENT_NAME_LEN_MAX + 1;
- break;
- case DATA_FMT_STRING:
+ if (datafmt == DATA_FMT_STRING)
buflen = datalen + 1;
- break;
- default:
- buflen = datalen;
- }
buf = kzalloc(buflen, GFP_KERNEL);
if (!buf)
@@ -63,7 +54,7 @@ static int ima_write_template_field_data(const void *data, const u32 datalen,
* split into multiple template fields (the space is the delimitator
* character for measurements lists in ASCII format).
*/
- if (datafmt == DATA_FMT_EVENT_NAME || datafmt == DATA_FMT_STRING) {
+ if (datafmt == DATA_FMT_STRING) {
for (buf_ptr = buf; buf_ptr - buf < datalen; buf_ptr++)
if (*buf_ptr == ' ')
*buf_ptr = '_';
@@ -109,13 +100,16 @@ static void ima_show_template_data_binary(struct seq_file *m,
enum data_formats datafmt,
struct ima_field_data *field_data)
{
+ u32 len = (show == IMA_SHOW_BINARY_OLD_STRING_FMT) ?
+ strlen(field_data->data) : field_data->len;
+
if (show != IMA_SHOW_BINARY_NO_FIELD_LEN)
- ima_putc(m, &field_data->len, sizeof(u32));
+ ima_putc(m, &len, sizeof(len));
- if (!field_data->len)
+ if (!len)
return;
- ima_putc(m, field_data->data, field_data->len);
+ ima_putc(m, field_data->data, len);
}
static void ima_show_template_field_data(struct seq_file *m,
@@ -129,6 +123,7 @@ static void ima_show_template_field_data(struct seq_file *m,
break;
case IMA_SHOW_BINARY:
case IMA_SHOW_BINARY_NO_FIELD_LEN:
+ case IMA_SHOW_BINARY_OLD_STRING_FMT:
ima_show_template_data_binary(m, show, datafmt, field_data);
break;
default:
@@ -277,8 +272,6 @@ static int ima_eventname_init_common(struct integrity_iint_cache *iint,
{
const char *cur_filename = NULL;
u32 cur_filename_len = 0;
- enum data_formats fmt = size_limit ?
- DATA_FMT_EVENT_NAME : DATA_FMT_STRING;
BUG_ON(filename == NULL && file == NULL);
@@ -301,7 +294,7 @@ static int ima_eventname_init_common(struct integrity_iint_cache *iint,
cur_filename_len = IMA_EVENT_NAME_LEN_MAX;
out:
return ima_write_template_field_data(cur_filename, cur_filename_len,
- fmt, field_data);
+ DATA_FMT_STRING, field_data);
}
/*
diff --git a/security/integrity/integrity_audit.c b/security/integrity/integrity_audit.c
index d7efb30404aa..aab9fa5a8231 100644
--- a/security/integrity/integrity_audit.c
+++ b/security/integrity/integrity_audit.c
@@ -7,7 +7,7 @@
* the Free Software Foundation, version 2 of the License.
*
* File: integrity_audit.c
- * Audit calls for the integrity subsystem
+ * Audit calls for the integrity subsystem
*/
#include <linux/fs.h>
@@ -22,7 +22,7 @@ static int __init integrity_audit_setup(char *str)
{
unsigned long audit;
- if (!strict_strtoul(str, 0, &audit))
+ if (!kstrtoul(str, 0, &audit))
integrity_audit_info = audit ? 1 : 0;
return 1;
}
@@ -33,6 +33,7 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
const char *cause, int result, int audit_info)
{
struct audit_buffer *ab;
+ char name[TASK_COMM_LEN];
if (!integrity_audit_info && audit_info == 1) /* Skip info messages */
return;
@@ -49,7 +50,7 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
audit_log_format(ab, " cause=");
audit_log_string(ab, cause);
audit_log_format(ab, " comm=");
- audit_log_untrustedstring(ab, current->comm);
+ audit_log_untrustedstring(ab, get_task_comm(name, current));
if (fname) {
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, fname);
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 9e1e005c7596..5fe443d120af 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -609,7 +609,7 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
long dlen;
int ret;
- ret = strict_strtol(datalen, 10, &dlen);
+ ret = kstrtol(datalen, 10, &dlen);
if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE)
return ERR_PTR(-EINVAL);
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index e13fcf7636f7..6b804aa4529a 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -753,7 +753,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
return -EINVAL;
break;
case Opt_keyhandle:
- res = strict_strtoul(args[0].from, 16, &handle);
+ res = kstrtoul(args[0].from, 16, &handle);
if (res < 0)
return -EINVAL;
opt->keytype = SEAL_keytype;
@@ -782,7 +782,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
return -EINVAL;
break;
case Opt_pcrlock:
- res = strict_strtoul(args[0].from, 10, &lock);
+ res = kstrtoul(args[0].from, 10, &lock);
if (res < 0)
return -EINVAL;
opt->pcrlock = lock;
@@ -820,7 +820,7 @@ static int datablob_parse(char *datablob, struct trusted_key_payload *p,
c = strsep(&datablob, " \t");
if (!c)
return -EINVAL;
- ret = strict_strtol(c, 10, &keylen);
+ ret = kstrtol(c, 10, &keylen);
if (ret < 0 || keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE)
return -EINVAL;
p->key_len = keylen;
diff --git a/security/security.c b/security/security.c
index 919cad93ac82..8b774f362a3d 100644
--- a/security/security.c
+++ b/security/security.c
@@ -433,11 +433,20 @@ int security_path_link(struct dentry *old_dentry, struct path *new_dir,
}
int security_path_rename(struct path *old_dir, struct dentry *old_dentry,
- struct path *new_dir, struct dentry *new_dentry)
+ struct path *new_dir, struct dentry *new_dentry,
+ unsigned int flags)
{
if (unlikely(IS_PRIVATE(old_dentry->d_inode) ||
(new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode))))
return 0;
+
+ if (flags & RENAME_EXCHANGE) {
+ int err = security_ops->path_rename(new_dir, new_dentry,
+ old_dir, old_dentry);
+ if (err)
+ return err;
+ }
+
return security_ops->path_rename(old_dir, old_dentry, new_dir,
new_dentry);
}
@@ -524,11 +533,20 @@ int security_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
}
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
+ struct inode *new_dir, struct dentry *new_dentry,
+ unsigned int flags)
{
if (unlikely(IS_PRIVATE(old_dentry->d_inode) ||
(new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode))))
return 0;
+
+ if (flags & RENAME_EXCHANGE) {
+ int err = security_ops->inode_rename(new_dir, new_dentry,
+ old_dir, old_dentry);
+ if (err)
+ return err;
+ }
+
return security_ops->inode_rename(old_dir, old_dentry,
new_dir, new_dentry);
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index b332e2cc0954..b4beb77967b1 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -106,7 +106,7 @@ int selinux_enforcing;
static int __init enforcing_setup(char *str)
{
unsigned long enforcing;
- if (!strict_strtoul(str, 0, &enforcing))
+ if (!kstrtoul(str, 0, &enforcing))
selinux_enforcing = enforcing ? 1 : 0;
return 1;
}
@@ -119,7 +119,7 @@ int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
static int __init selinux_enabled_setup(char *str)
{
unsigned long enabled;
- if (!strict_strtoul(str, 0, &enabled))
+ if (!kstrtoul(str, 0, &enabled))
selinux_enabled = enabled ? 1 : 0;
return 1;
}
@@ -1418,15 +1418,33 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
isec->sid = sbsec->sid;
if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) {
- if (opt_dentry) {
- isec->sclass = inode_mode_to_security_class(inode->i_mode);
- rc = selinux_proc_get_sid(opt_dentry,
- isec->sclass,
- &sid);
- if (rc)
- goto out_unlock;
- isec->sid = sid;
- }
+ /* We must have a dentry to determine the label on
+ * procfs inodes */
+ if (opt_dentry)
+ /* Called from d_instantiate or
+ * d_splice_alias. */
+ dentry = dget(opt_dentry);
+ else
+ /* Called from selinux_complete_init, try to
+ * find a dentry. */
+ dentry = d_find_alias(inode);
+ /*
+ * This can be hit on boot when a file is accessed
+ * before the policy is loaded. When we load policy we
+ * may find inodes that have no dentry on the
+ * sbsec->isec_head list. No reason to complain as
+ * these will get fixed up the next time we go through
+ * inode_doinit() with a dentry, before these inodes
+ * could be used again by userspace.
+ */
+ if (!dentry)
+ goto out_unlock;
+ isec->sclass = inode_mode_to_security_class(inode->i_mode);
+ rc = selinux_proc_get_sid(dentry, isec->sclass, &sid);
+ dput(dentry);
+ if (rc)
+ goto out_unlock;
+ isec->sid = sid;
}
break;
}
@@ -3205,24 +3223,20 @@ error:
static int selinux_mmap_addr(unsigned long addr)
{
- int rc = 0;
- u32 sid = current_sid();
+ int rc;
+
+ /* do DAC check on address space usage */
+ rc = cap_mmap_addr(addr);
+ if (rc)
+ return rc;
- /*
- * notice that we are intentionally putting the SELinux check before
- * the secondary cap_file_mmap check. This is such a likely attempt
- * at bad behaviour/exploit that we always want to get the AVC, even
- * if DAC would have also denied the operation.
- */
if (addr < CONFIG_LSM_MMAP_MIN_ADDR) {
+ u32 sid = current_sid();
rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
MEMPROTECT__MMAP_ZERO, NULL);
- if (rc)
- return rc;
}
- /* do DAC check on address space usage */
- return cap_mmap_addr(addr);
+ return rc;
}
static int selinux_mmap_file(struct file *file, unsigned long reqprot,
@@ -3303,6 +3317,9 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
case F_GETLK:
case F_SETLK:
case F_SETLKW:
+ case F_GETLKP:
+ case F_SETLKP:
+ case F_SETLKPW:
#if BITS_PER_LONG == 32
case F_GETLK64:
case F_SETLK64:
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index 9f0584710c85..1450f85b946d 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -46,10 +46,11 @@ static inline void selinux_xfrm_notify_policyload(void)
{
struct net *net;
- atomic_inc(&flow_cache_genid);
rtnl_lock();
- for_each_net(net)
+ for_each_net(net) {
+ atomic_inc(&net->xfrm.flow_cache_genid);
rt_genid_bump_all(net);
+ }
rtnl_unlock();
}
#else
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index d60c0ee66387..c71737f6d1cc 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -54,7 +54,7 @@ unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
static int __init checkreqprot_setup(char *str)
{
unsigned long checkreqprot;
- if (!strict_strtoul(str, 0, &checkreqprot))
+ if (!kstrtoul(str, 0, &checkreqprot))
selinux_checkreqprot = checkreqprot ? 1 : 0;
return 1;
}
diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig
index 49f8437665de..06f4e8aa93ae 100644
--- a/sound/soc/kirkwood/Kconfig
+++ b/sound/soc/kirkwood/Kconfig
@@ -1,6 +1,6 @@
config SND_KIRKWOOD_SOC
tristate "SoC Audio for the Marvell Kirkwood and Dove chips"
- depends on ARCH_KIRKWOOD || ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST
+ depends on ARCH_KIRKWOOD || ARCH_DOVE || ARCH_MVEBU || MACH_KIRKWOOD || COMPILE_TEST
help
Say Y or M if you want to add support for codecs attached to
the Kirkwood I2S interface. You will also need to select the
diff --git a/tools/net/bpf_dbg.c b/tools/net/bpf_dbg.c
index 65dc757f7f7b..bb31813e43dd 100644
--- a/tools/net/bpf_dbg.c
+++ b/tools/net/bpf_dbg.c
@@ -87,9 +87,6 @@
__attribute__ ((format (printf, (pos_fmtstr), (pos_fmtargs))))
#endif
-#define CMD(_name, _func) { .name = _name, .func = _func, }
-#define OP(_op, _name) [_op] = _name
-
enum {
CMD_OK,
CMD_ERR,
@@ -145,32 +142,32 @@ static size_t pcap_map_size = 0;
static char *pcap_ptr_va_start, *pcap_ptr_va_curr;
static const char * const op_table[] = {
- OP(BPF_ST, "st"),
- OP(BPF_STX, "stx"),
- OP(BPF_LD_B, "ldb"),
- OP(BPF_LD_H, "ldh"),
- OP(BPF_LD_W, "ld"),
- OP(BPF_LDX, "ldx"),
- OP(BPF_LDX_B, "ldxb"),
- OP(BPF_JMP_JA, "ja"),
- OP(BPF_JMP_JEQ, "jeq"),
- OP(BPF_JMP_JGT, "jgt"),
- OP(BPF_JMP_JGE, "jge"),
- OP(BPF_JMP_JSET, "jset"),
- OP(BPF_ALU_ADD, "add"),
- OP(BPF_ALU_SUB, "sub"),
- OP(BPF_ALU_MUL, "mul"),
- OP(BPF_ALU_DIV, "div"),
- OP(BPF_ALU_MOD, "mod"),
- OP(BPF_ALU_NEG, "neg"),
- OP(BPF_ALU_AND, "and"),
- OP(BPF_ALU_OR, "or"),
- OP(BPF_ALU_XOR, "xor"),
- OP(BPF_ALU_LSH, "lsh"),
- OP(BPF_ALU_RSH, "rsh"),
- OP(BPF_MISC_TAX, "tax"),
- OP(BPF_MISC_TXA, "txa"),
- OP(BPF_RET, "ret"),
+ [BPF_ST] = "st",
+ [BPF_STX] = "stx",
+ [BPF_LD_B] = "ldb",
+ [BPF_LD_H] = "ldh",
+ [BPF_LD_W] = "ld",
+ [BPF_LDX] = "ldx",
+ [BPF_LDX_B] = "ldxb",
+ [BPF_JMP_JA] = "ja",
+ [BPF_JMP_JEQ] = "jeq",
+ [BPF_JMP_JGT] = "jgt",
+ [BPF_JMP_JGE] = "jge",
+ [BPF_JMP_JSET] = "jset",
+ [BPF_ALU_ADD] = "add",
+ [BPF_ALU_SUB] = "sub",
+ [BPF_ALU_MUL] = "mul",
+ [BPF_ALU_DIV] = "div",
+ [BPF_ALU_MOD] = "mod",
+ [BPF_ALU_NEG] = "neg",
+ [BPF_ALU_AND] = "and",
+ [BPF_ALU_OR] = "or",
+ [BPF_ALU_XOR] = "xor",
+ [BPF_ALU_LSH] = "lsh",
+ [BPF_ALU_RSH] = "rsh",
+ [BPF_MISC_TAX] = "tax",
+ [BPF_MISC_TXA] = "txa",
+ [BPF_RET] = "ret",
};
static __check_format_printf(1, 2) int rl_printf(const char *fmt, ...)
@@ -1127,7 +1124,6 @@ static int cmd_step(char *num)
static int cmd_select(char *num)
{
unsigned int which, i;
- struct pcap_pkthdr *hdr;
bool have_next = true;
if (!pcap_loaded() || strlen(num) == 0)
@@ -1144,7 +1140,7 @@ static int cmd_select(char *num)
for (i = 0; i < which && (have_next = pcap_next_pkt()); i++)
/* noop */;
- if (!have_next || (hdr = pcap_curr_pkt()) == NULL) {
+ if (!have_next || pcap_curr_pkt() == NULL) {
rl_printf("no packet #%u available!\n", which);
pcap_reset_pkt();
return CMD_ERR;
@@ -1177,9 +1173,8 @@ static int cmd_breakpoint(char *subcmd)
static int cmd_run(char *num)
{
static uint32_t pass = 0, fail = 0;
- struct pcap_pkthdr *hdr;
bool has_limit = true;
- int ret, pkts = 0, i = 0;
+ int pkts = 0, i = 0;
if (!bpf_prog_loaded() || !pcap_loaded())
return CMD_ERR;
@@ -1189,10 +1184,10 @@ static int cmd_run(char *num)
has_limit = false;
do {
- hdr = pcap_curr_pkt();
- ret = bpf_run_all(bpf_image, bpf_prog_len,
- (uint8_t *) hdr + sizeof(*hdr),
- hdr->caplen, hdr->len);
+ struct pcap_pkthdr *hdr = pcap_curr_pkt();
+ int ret = bpf_run_all(bpf_image, bpf_prog_len,
+ (uint8_t *) hdr + sizeof(*hdr),
+ hdr->caplen, hdr->len);
if (ret > 0)
pass++;
else if (ret == 0)
@@ -1245,14 +1240,14 @@ static int cmd_quit(char *dontcare)
}
static const struct shell_cmd cmds[] = {
- CMD("load", cmd_load),
- CMD("select", cmd_select),
- CMD("step", cmd_step),
- CMD("run", cmd_run),
- CMD("breakpoint", cmd_breakpoint),
- CMD("disassemble", cmd_disassemble),
- CMD("dump", cmd_dump),
- CMD("quit", cmd_quit),
+ { .name = "load", .func = cmd_load },
+ { .name = "select", .func = cmd_select },
+ { .name = "step", .func = cmd_step },
+ { .name = "run", .func = cmd_run },
+ { .name = "breakpoint", .func = cmd_breakpoint },
+ { .name = "disassemble", .func = cmd_disassemble },
+ { .name = "dump", .func = cmd_dump },
+ { .name = "quit", .func = cmd_quit },
};
static int execf(char *arg)
@@ -1280,7 +1275,6 @@ out:
static char *shell_comp_gen(const char *buf, int state)
{
static int list_index, len;
- const char *name;
if (!state) {
list_index = 0;
@@ -1288,9 +1282,9 @@ static char *shell_comp_gen(const char *buf, int state)
}
for (; list_index < array_size(cmds); ) {
- name = cmds[list_index].name;
- list_index++;
+ const char *name = cmds[list_index].name;
+ list_index++;
if (strncmp(name, buf, len) == 0)
return strdup(name);
}
@@ -1322,16 +1316,9 @@ static void init_shell(FILE *fin, FILE *fout)
{
char file[128];
- memset(file, 0, sizeof(file));
- snprintf(file, sizeof(file) - 1,
- "%s/.bpf_dbg_history", getenv("HOME"));
-
+ snprintf(file, sizeof(file), "%s/.bpf_dbg_history", getenv("HOME"));
read_history(file);
- memset(file, 0, sizeof(file));
- snprintf(file, sizeof(file) - 1,
- "%s/.bpf_dbg_init", getenv("HOME"));
-
rl_instream = fin;
rl_outstream = fout;
@@ -1348,37 +1335,41 @@ static void init_shell(FILE *fin, FILE *fout)
rl_bind_key_in_map('\t', rl_complete, emacs_meta_keymap);
rl_bind_key_in_map('\033', rl_complete, emacs_meta_keymap);
+ snprintf(file, sizeof(file), "%s/.bpf_dbg_init", getenv("HOME"));
rl_read_init_file(file);
+
rl_prep_terminal(0);
rl_set_signals();
signal(SIGINT, intr_shell);
}
-static void exit_shell(void)
+static void exit_shell(FILE *fin, FILE *fout)
{
char file[128];
- memset(file, 0, sizeof(file));
- snprintf(file, sizeof(file) - 1,
- "%s/.bpf_dbg_history", getenv("HOME"));
-
+ snprintf(file, sizeof(file), "%s/.bpf_dbg_history", getenv("HOME"));
write_history(file);
+
clear_history();
rl_deprep_terminal();
try_close_pcap();
+
+ if (fin != stdin)
+ fclose(fin);
+ if (fout != stdout)
+ fclose(fout);
}
static int run_shell_loop(FILE *fin, FILE *fout)
{
char *buf;
- int ret;
init_shell(fin, fout);
while ((buf = readline("> ")) != NULL) {
- ret = execf(buf);
+ int ret = execf(buf);
if (ret == CMD_EX)
break;
if (ret == CMD_OK && strlen(buf) > 0)
@@ -1387,7 +1378,7 @@ static int run_shell_loop(FILE *fin, FILE *fout)
free(buf);
}
- exit_shell();
+ exit_shell(fin, fout);
return 0;
}
diff --git a/tools/perf/config/Makefile.arch b/tools/perf/config/Makefile.arch
index fef8ae922800..4b06719ee984 100644
--- a/tools/perf/config/Makefile.arch
+++ b/tools/perf/config/Makefile.arch
@@ -5,7 +5,8 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
-e s/arm.*/arm/ -e s/sa110/arm/ \
-e s/s390x/s390/ -e s/parisc64/parisc/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
- -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ )
+ -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ \
+ -e s/tile.*/tile/ )
# Additional ARCH settings for x86
ifeq ($(ARCH),i386)
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index e18a8b5e6953..5c11ecad02a9 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -145,6 +145,14 @@
#define CPUINFO_PROC "core ID"
#endif
+#ifdef __tile__
+#define mb() asm volatile ("mf" ::: "memory")
+#define wmb() asm volatile ("mf" ::: "memory")
+#define rmb() asm volatile ("mf" ::: "memory")
+#define cpu_relax() asm volatile ("mfspr zero, PASS" ::: "memory")
+#define CPUINFO_PROC "model name"
+#endif
+
#define barrier() asm volatile ("" ::: "memory")
#ifndef cpu_relax
diff --git a/tools/testing/ktest/examples/kvm.conf b/tools/testing/ktest/examples/kvm.conf
index 831c7c5395f1..fbc134f9ac6e 100644
--- a/tools/testing/ktest/examples/kvm.conf
+++ b/tools/testing/ktest/examples/kvm.conf
@@ -10,6 +10,10 @@ MACHINE = Guest
# Use virsh to read the serial console of the guest
CONSOLE = virsh console ${MACHINE}
+# Use SIGKILL to terminate virsh console. We can't kill virsh console
+# by the default signal, SIGINT.
+CLOSE_CONSOLE_SIGNAL = KILL
+
#*************************************#
# This part is the same as test.conf #
#*************************************#
diff --git a/tools/virtio/linux/kmemleak.h b/tools/virtio/linux/kmemleak.h
new file mode 100644
index 000000000000..c07072270e2f
--- /dev/null
+++ b/tools/virtio/linux/kmemleak.h
@@ -0,0 +1,3 @@
+static inline void kmemleak_ignore(const void *ptr)
+{
+}
diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h
index 844783040703..5a2d1f0f6bc7 100644
--- a/tools/virtio/linux/virtio.h
+++ b/tools/virtio/linux/virtio.h
@@ -63,7 +63,7 @@ int virtqueue_add_inbuf(struct virtqueue *vq,
void *data,
gfp_t gfp);
-void virtqueue_kick(struct virtqueue *vq);
+bool virtqueue_kick(struct virtqueue *vq);
void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len);
@@ -79,7 +79,7 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
struct virtio_device *vdev,
bool weak_barriers,
void *pages,
- void (*notify)(struct virtqueue *vq),
+ bool (*notify)(struct virtqueue *vq),
void (*callback)(struct virtqueue *vq),
const char *name);
void vring_del_virtqueue(struct virtqueue *vq);
diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c
index bdb71a26ae35..00ea679b3826 100644
--- a/tools/virtio/virtio_test.c
+++ b/tools/virtio/virtio_test.c
@@ -172,7 +172,7 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq,
GFP_ATOMIC);
if (likely(r == 0)) {
++started;
- if (unlikely(!virtqueue_kick(vq->vq))
+ if (unlikely(!virtqueue_kick(vq->vq)))
r = -1;
}
} else
diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
index fbe1a48bd629..13f2d19793e3 100644
--- a/virt/kvm/Kconfig
+++ b/virt/kvm/Kconfig
@@ -22,6 +22,10 @@ config KVM_MMIO
config KVM_ASYNC_PF
bool
+# Toggle to switch between direct notification and batch job
+config KVM_ASYNC_PF_SYNC
+ bool
+
config HAVE_KVM_MSI
bool
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index 5081e809821f..22fa819a9b6a 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -277,7 +277,7 @@ int kvm_timer_hyp_init(void)
host_vtimer_irq = ppi;
- err = register_cpu_notifier(&kvm_timer_cpu_nb);
+ err = __register_cpu_notifier(&kvm_timer_cpu_nb);
if (err) {
kvm_err("Cannot register timer CPU notifier\n");
goto out_free;
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
index 8ca405cd7c1a..47b29834a6b6 100644
--- a/virt/kvm/arm/vgic.c
+++ b/virt/kvm/arm/vgic.c
@@ -1496,7 +1496,7 @@ int kvm_vgic_hyp_init(void)
goto out;
}
- ret = register_cpu_notifier(&vgic_cpu_nb);
+ ret = __register_cpu_notifier(&vgic_cpu_nb);
if (ret) {
kvm_err("Cannot register vgic CPU notifier\n");
goto out_free_irq;
diff --git a/virt/kvm/async_pf.c b/virt/kvm/async_pf.c
index 8631d9c14320..10df100c4514 100644
--- a/virt/kvm/async_pf.c
+++ b/virt/kvm/async_pf.c
@@ -28,6 +28,21 @@
#include "async_pf.h"
#include <trace/events/kvm.h>
+static inline void kvm_async_page_present_sync(struct kvm_vcpu *vcpu,
+ struct kvm_async_pf *work)
+{
+#ifdef CONFIG_KVM_ASYNC_PF_SYNC
+ kvm_arch_async_page_present(vcpu, work);
+#endif
+}
+static inline void kvm_async_page_present_async(struct kvm_vcpu *vcpu,
+ struct kvm_async_pf *work)
+{
+#ifndef CONFIG_KVM_ASYNC_PF_SYNC
+ kvm_arch_async_page_present(vcpu, work);
+#endif
+}
+
static struct kmem_cache *async_pf_cache;
int kvm_async_pf_init(void)
@@ -69,6 +84,7 @@ static void async_pf_execute(struct work_struct *work)
down_read(&mm->mmap_sem);
get_user_pages(current, mm, addr, 1, 1, 0, NULL, NULL);
up_read(&mm->mmap_sem);
+ kvm_async_page_present_sync(vcpu, apf);
unuse_mm(mm);
spin_lock(&vcpu->async_pf.lock);
@@ -97,11 +113,16 @@ void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu)
list_entry(vcpu->async_pf.queue.next,
typeof(*work), queue);
list_del(&work->queue);
+
+#ifdef CONFIG_KVM_ASYNC_PF_SYNC
+ flush_work(&work->work);
+#else
if (cancel_work_sync(&work->work)) {
mmdrop(work->mm);
kvm_put_kvm(vcpu->kvm); /* == work->vcpu->kvm */
kmem_cache_free(async_pf_cache, work);
}
+#endif
}
spin_lock(&vcpu->async_pf.lock);
@@ -130,7 +151,7 @@ void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu)
spin_unlock(&vcpu->async_pf.lock);
kvm_arch_async_page_ready(vcpu, work);
- kvm_arch_async_page_present(vcpu, work);
+ kvm_async_page_present_async(vcpu, work);
list_del(&work->queue);
vcpu->async_pf.queued--;
@@ -138,7 +159,7 @@ void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu)
}
}
-int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn,
+int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, unsigned long hva,
struct kvm_arch_async_pf *arch)
{
struct kvm_async_pf *work;
@@ -159,7 +180,7 @@ int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn,
work->wakeup_all = false;
work->vcpu = vcpu;
work->gva = gva;
- work->addr = gfn_to_hva(vcpu->kvm, gfn);
+ work->addr = hva;
work->arch = *arch;
work->mm = current->mm;
atomic_inc(&work->mm->mm_count);
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c
index abe4d6043b36..29c2a04e036e 100644
--- a/virt/kvm/eventfd.c
+++ b/virt/kvm/eventfd.c
@@ -391,19 +391,19 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
lockdep_is_held(&kvm->irqfds.lock));
irqfd_update(kvm, irqfd, irq_rt);
- events = f.file->f_op->poll(f.file, &irqfd->pt);
-
list_add_tail(&irqfd->list, &kvm->irqfds.items);
+ spin_unlock_irq(&kvm->irqfds.lock);
+
/*
* Check if there was an event already pending on the eventfd
* before we registered, and trigger it as if we didn't miss it.
*/
+ events = f.file->f_op->poll(f.file, &irqfd->pt);
+
if (events & POLLIN)
schedule_work(&irqfd->inject);
- spin_unlock_irq(&kvm->irqfds.lock);
-
/*
* do not drop the file until the irqfd is fully initialized, otherwise
* we might race against the POLLHUP
diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c
index ce9ed99ad7dc..d4b601547f1f 100644
--- a/virt/kvm/ioapic.c
+++ b/virt/kvm/ioapic.c
@@ -50,7 +50,7 @@
#else
#define ioapic_debug(fmt, arg...)
#endif
-static int ioapic_deliver(struct kvm_ioapic *vioapic, int irq,
+static int ioapic_service(struct kvm_ioapic *vioapic, int irq,
bool line_status);
static unsigned long ioapic_read_indirect(struct kvm_ioapic *ioapic,
@@ -163,23 +163,67 @@ static bool rtc_irq_check_coalesced(struct kvm_ioapic *ioapic)
return false;
}
-static int ioapic_service(struct kvm_ioapic *ioapic, unsigned int idx,
- bool line_status)
+static int ioapic_set_irq(struct kvm_ioapic *ioapic, unsigned int irq,
+ int irq_level, bool line_status)
{
- union kvm_ioapic_redirect_entry *pent;
- int injected = -1;
+ union kvm_ioapic_redirect_entry entry;
+ u32 mask = 1 << irq;
+ u32 old_irr;
+ int edge, ret;
- pent = &ioapic->redirtbl[idx];
+ entry = ioapic->redirtbl[irq];
+ edge = (entry.fields.trig_mode == IOAPIC_EDGE_TRIG);
- if (!pent->fields.mask) {
- injected = ioapic_deliver(ioapic, idx, line_status);
- if (injected && pent->fields.trig_mode == IOAPIC_LEVEL_TRIG)
- pent->fields.remote_irr = 1;
+ if (!irq_level) {
+ ioapic->irr &= ~mask;
+ ret = 1;
+ goto out;
+ }
+
+ /*
+ * Return 0 for coalesced interrupts; for edge-triggered interrupts,
+ * this only happens if a previous edge has not been delivered due
+ * do masking. For level interrupts, the remote_irr field tells
+ * us if the interrupt is waiting for an EOI.
+ *
+ * RTC is special: it is edge-triggered, but userspace likes to know
+ * if it has been already ack-ed via EOI because coalesced RTC
+ * interrupts lead to time drift in Windows guests. So we track
+ * EOI manually for the RTC interrupt.
+ */
+ if (irq == RTC_GSI && line_status &&
+ rtc_irq_check_coalesced(ioapic)) {
+ ret = 0;
+ goto out;
}
- return injected;
+ old_irr = ioapic->irr;
+ ioapic->irr |= mask;
+ if ((edge && old_irr == ioapic->irr) ||
+ (!edge && entry.fields.remote_irr)) {
+ ret = 0;
+ goto out;
+ }
+
+ ret = ioapic_service(ioapic, irq, line_status);
+
+out:
+ trace_kvm_ioapic_set_irq(entry.bits, irq, ret == 0);
+ return ret;
+}
+
+static void kvm_ioapic_inject_all(struct kvm_ioapic *ioapic, unsigned long irr)
+{
+ u32 idx;
+
+ rtc_irq_eoi_tracking_reset(ioapic);
+ for_each_set_bit(idx, &irr, IOAPIC_NUM_PINS)
+ ioapic_set_irq(ioapic, idx, 1, true);
+
+ kvm_rtc_eoi_tracking_restore_all(ioapic);
}
+
static void update_handled_vectors(struct kvm_ioapic *ioapic)
{
DECLARE_BITMAP(handled_vectors, 256);
@@ -282,12 +326,15 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)
}
}
-static int ioapic_deliver(struct kvm_ioapic *ioapic, int irq, bool line_status)
+static int ioapic_service(struct kvm_ioapic *ioapic, int irq, bool line_status)
{
union kvm_ioapic_redirect_entry *entry = &ioapic->redirtbl[irq];
struct kvm_lapic_irq irqe;
int ret;
+ if (entry->fields.mask)
+ return -1;
+
ioapic_debug("dest=%x dest_mode=%x delivery_mode=%x "
"vector=%x trig_mode=%x\n",
entry->fields.dest_id, entry->fields.dest_mode,
@@ -302,6 +349,9 @@ static int ioapic_deliver(struct kvm_ioapic *ioapic, int irq, bool line_status)
irqe.level = 1;
irqe.shorthand = 0;
+ if (irqe.trig_mode == IOAPIC_EDGE_TRIG)
+ ioapic->irr &= ~(1 << irq);
+
if (irq == RTC_GSI && line_status) {
BUG_ON(ioapic->rtc_status.pending_eoi != 0);
ret = kvm_irq_delivery_to_apic(ioapic->kvm, NULL, &irqe,
@@ -310,45 +360,24 @@ static int ioapic_deliver(struct kvm_ioapic *ioapic, int irq, bool line_status)
} else
ret = kvm_irq_delivery_to_apic(ioapic->kvm, NULL, &irqe, NULL);
+ if (ret && irqe.trig_mode == IOAPIC_LEVEL_TRIG)
+ entry->fields.remote_irr = 1;
+
return ret;
}
int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int irq_source_id,
int level, bool line_status)
{
- u32 old_irr;
- u32 mask = 1 << irq;
- union kvm_ioapic_redirect_entry entry;
int ret, irq_level;
BUG_ON(irq < 0 || irq >= IOAPIC_NUM_PINS);
spin_lock(&ioapic->lock);
- old_irr = ioapic->irr;
irq_level = __kvm_irq_line_state(&ioapic->irq_states[irq],
irq_source_id, level);
- entry = ioapic->redirtbl[irq];
- irq_level ^= entry.fields.polarity;
- if (!irq_level) {
- ioapic->irr &= ~mask;
- ret = 1;
- } else {
- int edge = (entry.fields.trig_mode == IOAPIC_EDGE_TRIG);
+ ret = ioapic_set_irq(ioapic, irq, irq_level, line_status);
- if (irq == RTC_GSI && line_status &&
- rtc_irq_check_coalesced(ioapic)) {
- ret = 0; /* coalesced */
- goto out;
- }
- ioapic->irr |= mask;
- if ((edge && old_irr != ioapic->irr) ||
- (!edge && !entry.fields.remote_irr))
- ret = ioapic_service(ioapic, irq, line_status);
- else
- ret = 0; /* report coalesced interrupt */
- }
-out:
- trace_kvm_ioapic_set_irq(entry.bits, irq, ret == 0);
spin_unlock(&ioapic->lock);
return ret;
@@ -394,7 +423,7 @@ static void __kvm_ioapic_update_eoi(struct kvm_vcpu *vcpu,
ASSERT(ent->fields.trig_mode == IOAPIC_LEVEL_TRIG);
ent->fields.remote_irr = 0;
- if (!ent->fields.mask && (ioapic->irr & (1 << i)))
+ if (ioapic->irr & (1 << i))
ioapic_service(ioapic, i, false);
}
}
@@ -595,9 +624,10 @@ int kvm_set_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state)
spin_lock(&ioapic->lock);
memcpy(ioapic, state, sizeof(struct kvm_ioapic_state));
+ ioapic->irr = 0;
update_handled_vectors(ioapic);
kvm_vcpu_request_scan_ioapic(kvm);
- kvm_rtc_eoi_tracking_restore_all(ioapic);
+ kvm_ioapic_inject_all(ioapic, state->irr);
spin_unlock(&ioapic->lock);
return 0;
}
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index b5ec7fb986f6..56baae8c2f56 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -186,12 +186,9 @@ static bool make_all_cpus_request(struct kvm *kvm, unsigned int req)
void kvm_flush_remote_tlbs(struct kvm *kvm)
{
- long dirty_count = kvm->tlbs_dirty;
-
- smp_mb();
if (make_all_cpus_request(kvm, KVM_REQ_TLB_FLUSH))
++kvm->stat.remote_tlb_flush;
- cmpxchg(&kvm->tlbs_dirty, dirty_count, 0);
+ kvm->tlbs_dirty = false;
}
EXPORT_SYMBOL_GPL(kvm_flush_remote_tlbs);
@@ -1804,7 +1801,7 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *me)
continue;
if (vcpu == me)
continue;
- if (waitqueue_active(&vcpu->wq))
+ if (waitqueue_active(&vcpu->wq) && !kvm_arch_vcpu_runnable(vcpu))
continue;
if (!kvm_vcpu_eligible_for_directed_yield(vcpu))
continue;
@@ -2284,6 +2281,11 @@ static int kvm_ioctl_create_device(struct kvm *kvm,
ops = &kvm_arm_vgic_v2_ops;
break;
#endif
+#ifdef CONFIG_S390
+ case KVM_DEV_TYPE_FLIC:
+ ops = &kvm_flic_ops;
+ break;
+#endif
default:
return -ENODEV;
}
diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c
index b4f9507ae650..ba1a93f935c7 100644
--- a/virt/kvm/vfio.c
+++ b/virt/kvm/vfio.c
@@ -59,6 +59,22 @@ static void kvm_vfio_group_put_external_user(struct vfio_group *vfio_group)
symbol_put(vfio_group_put_external_user);
}
+static bool kvm_vfio_group_is_coherent(struct vfio_group *vfio_group)
+{
+ long (*fn)(struct vfio_group *, unsigned long);
+ long ret;
+
+ fn = symbol_get(vfio_external_check_extension);
+ if (!fn)
+ return false;
+
+ ret = fn(vfio_group, VFIO_DMA_CC_IOMMU);
+
+ symbol_put(vfio_external_check_extension);
+
+ return ret > 0;
+}
+
/*
* Groups can use the same or different IOMMU domains. If the same then
* adding a new group may change the coherency of groups we've previously
@@ -75,13 +91,10 @@ static void kvm_vfio_update_coherency(struct kvm_device *dev)
mutex_lock(&kv->lock);
list_for_each_entry(kvg, &kv->group_list, node) {
- /*
- * TODO: We need an interface to check the coherency of
- * the IOMMU domain this group is using. For now, assume
- * it's always noncoherent.
- */
- noncoherent = true;
- break;
+ if (!kvm_vfio_group_is_coherent(kvg->vfio_group)) {
+ noncoherent = true;
+ break;
+ }
}
if (noncoherent != kv->noncoherent) {